Wyze Cam

From Leo's Notes
Last edited on 3 December 2023, at 00:40.
WyzeCam v2 and v3 side by side
WyzeCam v2 and v3 side by side

Wyze Cams are cheap wireless network cameras by Wyze Labs. This page contains notes on both the Wyze Cam v2 and Wyze Cam v3 including how to hack it and enable RTSP.

Wyze Cam v3[edit | edit source]

The Wyze Cam v3 is an IP camera by Wyze Lab and was released in late 2020. It improves on the v2 with better night vision, higher FPS (15fps to 20fps for day / 10fps to 15fps for night), and is weather-proof by being water proof and able to operate between -20°C and 40°C.

I have 2 Wyze Cams outdoors and exposed to the elements for two winters between 2021-2023 and they're still working without any issues. However, the camera that faces west and is in direct sunlight and exposed to the elements appears to have a small fracture in its lens and haziness in its video capture. I replaced the stock lens in 2023 as described in the lens replacement section below.

A large hail storm hammered a Wyze Cam v3 and caused it to glitch into IR mode during the day but was resolved after a power cycle. I suggest 3D printing a cover for the camera and possibly tape up the gap between the lens and the face plate if it's exposed to the elements.

Official RTSP support[edit | edit source]

Real-time Streaming Protocol (RTSP) support exposes the camera's live video feed via the RTSP protocol. This is typically useful if you want to watch your camera (such as with VLC or MPC-HC), use some sort of network video recording (NVR), or use some sort of motion monitoring/recording system (like with Kerberos.io or Frigate). If you have multiple IP Cameras with RTSP support, you can use something like aler9's rtsp-simple-server to 'collect' all the streams onto one server or use a monitoring system like Frigate that can record from multiple sources.

As of 2022, the only official support for RTSP by Wyze Labs is an old beta version of the RTSP firmware (4.61.0.1). Their official statement is this firmware is not supported and recommends against using it due to security concerns. You may still download the RTSP-enabled firmware and flash it. The Wyze App still allows you to enable RTSP with this firmware version installed.

Alternatively, you can enable RTSP using the wz_mini_hacks hack on the latest firmware. Be warned that this method seems to be buggy and results in dropped or corrupted frames periodically. This consequently causes artifacts or smearing with the Frigate recordings and also generates a lot of error messages in the Frigate logs. I have since downgraded all my Wyze Cam v3s back to the RTSP beta firmware with wz_mini_hacks installed (but with wz_mini_hack's RTSP feature turned off).

Hacking the Wyze Cam v3[edit | edit source]

Because the v3 uses the Ingenic T31 chip with secure boot enabled, you cannot load a modified firmware file (demo.bin) on the SD card as you could with the Wyze Cam v2. Rather, you need to rely on other approaches to modifying the firmware. Currently, there are two known options:

  1. Use WyzeHack's OTA update method where your customized firmware is sent instead of the official firmware.
  2. Exploit the fact that uboot attempts to load a file from the SD card called factory_t31_ZMC6tiIDQN which can then be used to sideload hacks.[1] The firmware on the onboard flash memory will remain in tact, making this a very safe and reversible option with the only downside that you need a SD card.

Community firmware hacks[edit | edit source]

These hacks work with the latest official firmware released by Wyze. These methods may be preferable to the beta firmware simply because they're more up to date with known vulnerabilities being patched.

  1. wyze-bridge isn't a firmware hack per-se, but it lets you obtain a RTSP steam from your Wyze Cam. This project achieves this by streaming the video data from the Wyze cloud to a local RTMP server. The downside to this is that it wastes lots of internet bandwidth as the video feed is constantly uploaded (and downloaded if you're recording it locally). See: https://github.com/mrlt8/docker-wyze-bridge
  2. wz_mini_hacks project side-loads firmware changes using a SD card. This does not change the firmware on the flash and is very safe to use and experiment with. This project takes the WyzeHacks work a little further and adds additional features. RTSP and RTMP streaming is supported. Installation is extremely easy. See: https://github.com/gtxaspec/wz_mini_hacks
  3. (Old and unsupported now) Use the WyzeHacks project which pushes a modified firmware by faking the Wyze App. This is technically more involved as you must now spoof your DNS. It may also be slightly more risky as you are modifying the firmware on flash that Wyze did not intend. Using the FiveLeavesLeft modification, you can expose a MPEG video stream via HTTP which can be re-encoded to a RTSP stream using ffmpeg. See: https://github.com/HclX/WyzeHacks

More information on each of these hacks below.

Wyze-bridge[edit | edit source]

Use the wyze-bridge docker stack available at https://github.com/mrlt8/docker-wyze-bridge. This project will communicate with your Wyze cameras and retrieve the video feed through the local LAN and expose it using its own a RTSP server. The docker-wyze-bridge project also exposes a REST API to control your Wyze cameras without needing the Wyze app.

This option may be desirable for Wyze cameras that have no hackable firmware such as the new Wyze Cam v3 Pro.

wz_mini_hacks[edit | edit source]

wz_mini_hacks is a collection of neat hacks that bundles a bunch of features. You may find it at https://github.com/gtxaspec/wz_mini_hacks. wz_mini_hacks side loads hacks to the official firmware using a SD card as part of the camera's boot process. This hack is completely reversible by either removing the SD card or removing the factory image that's loaded (factory_t31_ZMC6tiIDQN), since nothing is changed on the camera's flash memory itself.

This hack should work with a wide range of firmware versions except versions after 4.36.9.139. To install the hack, update the camera to (or older than) firmware version (4.36.9.139) and copy the wz_mini_hacks's SD_ROOT directory to a FAT formatted SD card. Insert the SD card into the camera and power it on. Once the device has done its initial boot process, the hack is installed.

Features that this hack provides include: SSH access, ability to configure the camera out of the box without the Wyze App, possibility of using it in an air-gapped network and without internet access, ability to stream RTSP (though this is glitchy), and more.

RTSP support[edit | edit source]

There are two options to RTSP support under wz_mini_hacks. First is to use the RTSP beta and enable RTSP by adding a config file to /configs. Second is to use the RTSP feature within wz_mini_hacks.

Option 1: RTSP beta[edit | edit source]

You can use wz_mini_hacks with the RTSP beta firmware. RTSP provided by the official beta firmware seems to be more stable and less prone to corrupt or dropped frames. Do so by following these steps:

  1. Load the RTSP beta and update it to the Wyze Cam.
  2. Configure SSH keys and put wz_mini_hacks on a SD card and boot the camera with it.
  3. SSH to the camera and edit /configs/.rtspConfig with the following. Change the username or password as desired.
    [RTSP]
    Switch=1
    Password=wyzecam
    Username=admin
    
  4. Ensure that RTSP is disabled in the wz_mini.conf file on the SD card
  5. Reboot. Your RTSP stream should be visible at rtsp://admin:wyzecam@wyzecam-ip:554/live
Option 2: RTSP server on wz_mini_hacks[edit | edit source]

To get RTSP working, you will then need to edit the wz_mini/wz_mini.conf file and enable the RTSP options. Here's an example configuration with 2 streams active.

RTSP_LOGIN="admin"
RTSP_PASSWORD="wyzecam"
RTSP_PORT="8554"

RTSP_HI_RES_ENABLED="true"
RTSP_HI_RES_ENABLE_AUDIO="true"
RTSP_HI_RES_FPS=""
RTSP_HI_RES_MAX_BITRATE="51200"
RTSP_HI_RES_TARGET_BITRATE="2048"
RTSP_HI_RES_ENC_PARAMETER="8"

RTSP_LOW_RES_ENABLED="false"
RTSP_LOW_RES_ENABLE_AUDIO="false"
RTSP_LOW_RES_FPS="5"
RTSP_LOW_RES_MAX_BITRATE="512"
RTSP_LOW_RES_TARGET_BITRATE="256"
RTSP_LOW_RES_ENC_PARAMETER="2"

You can either enable one or both streams. The high quality stream is set to 1920x1080 and the low quality stream is set to at 640x360. These resolutions cannot be changed. However, you may adjust the bitrate and FPS settings for each stream independently. The low quality stream may be useful for live previews and real-time object detection (though 360p might be too low resolution for object detection).

If you enable only the high quality stream, the RTSP URL will be rtsp://admin:wyzecam@camera-address:8554/unicast. If you enable both streams, they will be at rtsp://admin:wyzecam@camera-address:8554/video1_unicast and rtsp://admin:wyzecam@camera-address:8554/video2_unicast.

The bitrate values here get applied to the underlying hardware encoder. When left unset, the target bitrate is set to 720kb/s and max bitrate set to 960kb/s. You may choose to set these bitrates higher at the cost of more bandwidth.

The ENC_PARAMETER value specifies the bitrate control modes. The wz_mini hack has some notes on the possible values, though I'm unable to find any other notes that support these yet. Values are:

  • 0. FIXQP / CQP - Fixed/Constant Quantization Parameter. Not supported here.
  • 1. CBR - Constant Bitrate. This forces the encoder to always use the target bitrate. Not supported here.
  • 2. VBR - Variable Bitrate, or 2-Pass Average Bitrate.
  • 4. CVBR / VBV - Constrained VBR or Video Buffering Verifier. Ensures bitrates are constrained to a certain maximum.
  • 8. CQ / CRF - Constant Quality / Constant Rate Factor. This gives a constant quality for the entire stream.

The default value when left unset is 8, or constant quality. Which one should you use? For surveillance applications, probably constant quality (8) if bandwidth isn't an issue, or variable bitrate (VBR) if bandwidth is constrained. You can read more about these different rate controls: https://slhck.info/video/2017/03/01/rate-control.html

Under the hood, these values are set by calling the firmware built-in /system/bin/impdbg utility. To check the current encoding values, just run /system/bin/impdbg --enc_info and look for the chnAttr->rcAttr->rcMode and chnAttr->rcAttr->CappedQuality values.

RTMP support[edit | edit source]

If you want the Wyze Cam to also push its video stream to a remote RTMP server, you can use the RTMP_STREAM_ENABLED options. However, this option only supports a few specific services such as YouTube, Twitch, and Facebook (based on the source code on the rtmp-stream.sh script). If you need to push it to a custom RTMP server, you will have to edit this script.

IR LED control and other commands[edit | edit source]

wz_mini_hacks uses the libcallback hack from the atomcam_tools repo. This hack injects a small listener on port 4000 in iCamera which can run some of the internal local_sdk_ functions. An included cmd command will allow you to interact with this hack.

Commands can be found in the commands.c file and include:

  • cmd irled on and cmd irled off to control the IR LEDs
  • aplay audio.wav volume to play a custom audio file. Volume being 1 - 100.
Upgrading from a local server[edit | edit source]

All my cameras are unable to reach the internet. In order to update wz_mini_hacks, I override the hosts file and modify the upgrade script to point to a local web server.

# cp /etc/hosts /tmp/hosts
# echo "10.1.2.11  public.home.steamr.com" >> /tmp/hosts
# mount --bind /tmp/hosts /etc/hosts
# sed -i 's/github.com\/gtxaspec\/wz_mini_hacks\/archive\/refs\/heads/webserver.local/g' /opt/wz_mini/bin/upgrade-run.sh
# /opt/wz_mini/bin/upgrade-run.sh
Issues with wz_mini_hacks[edit | edit source]

RTSP Stream codec: I had issues pushing the RTSP stream to the aler9 rtsp-simple-server using the default ffmpeg options. Because the aler9 RTSP server only accepts H264 and AAC codecs only, and because the Wyze Cam RTSP stream is using H265 and PCM, the audio stream has to be reencoded to AAC using ffmpeg.

The issue however is that when using the default ffmpeg command from the wz_mini_hacks RTMP stream script which uses the AAC HE profile, the aler9 RTSP server will drop the connection, resulting in ffmpeg logging av_interleaved_write_frame(): Broken pipe before quitting. The only way to get this working is to not use the aac_he audio profile. The ffmpeg command that seems to work for me is this:

$ /opt/wz_mini/bin/ffmpeg \
  -rtsp_transport udp -y -i rtsp://admin:password@0.0.0.0:8554/unicast \
  -vcodec copy -f flv \
  -c:a libfdk_aac -afterburner 1 -channels 1 -b:a 128k \
  rtmp://rtsp-server:1935/v3f

Even with this tweak, the ffmpeg stream to the aler9 RTSP server is very unreliable and constantly breaks.

Solution - You can tweak the rtsp-simple-server configuration file to automatically stream from the Wyze Cam directly without needing ffmpeg. Edit the rtsp-simple-server.yml config file and add the following:

camera:
    source: "rtsp://admin:wyzecam@10.1.1.30:8554/unicast"
    sourceProtocol: tcp

Note that I use TCP for this stream. Using UDP causes a lot of smearing to occur on my network. Setting it to TCP seems to fix this for the most part.

SSH not starting: For some reason, the SSH service (dropbear) doesn't start. This seems to only happen when I reboot the device and works again after I go in and delete some empty directories on the root of the SD card. Possibly corrupt FAT causing something to not work which gets fixed when I plug it into my computer?

Power cycling the Wyze Cam when it enters this state one more time seems to fix the issue. Could it be the SD cards I'm using needs a power cycle for it to show up properly to uboot?

Update: This is really starting to look like an issue with this particular SD card. Possibly could be a counterfeit item or the card is just starting to die. I can't replicate this on another camera with a different type of SD card (also 16GB).

Solution: The SD card likely is a counterfeit. It's missing the shiny holographic stripe that my other Kingston cards have. Replacing it seems to fix this particular issue.

aler9 rtsp-simple-server gives 'ERR: Content-Length exceeds 131072 (it's 1666223)': Using rtsp-simple-server v.0.20.0 to relay a RTSP stream from the Wyze Cam, I am getting 'ERR: Content-Length exceeds 131072 (it's 1666223)' with the video stream stalling out. This seemed to happen occasionally after enabling audio with the RTSP stream in wz_mini.conf.

Workaround: Restarting the Wyze Cam seemed to fix the issue. I have a container watching the logs and calling reboot when it sees these errors.

WyzeHacks[edit | edit source]

Possibly unsupported or not working
WyzeHacks hasn't been updated for over a year and Wyze Labs has recently required newer firmware versions to be used by the cameras. As a result, this method is likely not viable anymore. See the wz_mini_hacks option below as an alternative.

Thanks to the work by the authors of the WyzeHacks project (https://github.com/HclX/WyzeHacks) and its modified version by FiveLeavesLeft with the streaming support available (https://github.com/FiveLeavesLeft/WyzeHacks), you can stream video data directly from the camera without needing to download the data from the cloud as is the case with the wyze-bridge option. The hack sets up a h264 video stream on the Wyze cam (the file utility reports the data as a JVT NAL sequence) that need to be converted using ffmpeg and sent to the RTSP server.

To get this working:

  1. Load FiveLeavesLeft's version of WyzeHacks onto the Wyze Cam. More information on installing the hack is below. This method requires your Wyze credentials and works over the air without needing physical access to the device or the SD card. More in the WyzeHacks section below.
  2. Start a local RTMP server. Easiest thing to do is just run the RTSP server container with the following docker-compose stack. Create a docker-compose.yml file with the following contents. Replace the IP addresses as required. Add/remove the ffmpeg conversion containers as required. Make sure you are publishing to a unique path on the RTSP server.
    version: '3'
    
    services:
      rtsp-server:
        container_name: rtsp-server
        image: aler9/rtsp-simple-server
        environment:
          - RTSP_PROTOCOLS=tcp
        ports:
          - "10.1.1.Y:1935:1935"
          - "10.1.1.Y:8554:8554"
          - "10.1.1.Y:8888:8888"
    
      wyzecam-v2:
        image: registry.steamr.com/docker/alpine-ffmpeg:latest
        command: ["sh", "-c", "wget -q -O - 10.1.3.X:12345/ | ffmpeg -f h264 -i - -tune zerolatency -vcodec copy -f flv rtmp://rtsp-server:1935/v2"]
        restart: "always"
    
      wyzecam-v3:
        image: registry.steamr.com/docker/alpine-ffmpeg:latest
        command: ["sh", "-c", "wget -q -O - 10.1.3.X:12345/ | ffmpeg -f h264 -i - -tune zerolatency -vcodec copy -f flv rtmp://rtsp-server:1935/v3"]
        restart: "always"
    
    Place the contents in a docker-compose.yml file and then start up the stack with docker-compose up -d. This should bring up both the RTSP server and the ffmpeg conversions for each camera.
  3. If things are working right, you should be able to access the video feed at rtsp://10.1.1.Y:8554/camera

I've used this hack on 4 Wyze Cam v3 devices and it works without any issues. The only limiting issue is that there is no audio with these streams since the video frames are retrieved using wget from an external server. Furthermore, there's no way to load this hack on newer units.

Remote access[edit | edit source]

Depending on which firmware hack you installed, you can connect to the Wyze Cam using either telnet or SSH.

The default username and password set on the Wyze Cam v3 is:

  • Username: root
  • Password: WYom2020 or, on newer firmware versions: WYom20200 (passwd hash is aVG8.5PMEOfnQ)

If you are using wz_mini_hacks, you must SSH keys. The Add your SSH public key into the SD_ROOT/wz_mini/etc/ssh/authorized_keys file.

Wyze Cam v3 hardware hacks[edit | edit source]

Lens replacement[edit | edit source]

The Wyze Cam v3 that I have installed outside that is exposed to direct sunlight and the elements recently started to exhibit haziness after about 2 years. The haziness seems to be from a combination of internal cracking in the lens as well as possible moisture ingress within the lens unit. I tried to blow some hot air at the lens in hopes of having the moisture evaporate out but it had little effect. This appears to be a common occurrence as a quick web search turns up a few discussions about foggy lenses.

Wyze Cam v3 with a 160 FOV lens replacement
Wyze Cam v3 with a 160 FOV lens replacement. Trim the internal plastic standoffs to make the lens flush.

There doesn't seem to be any information on what specific lens is used in the Wyze Cam v3 and there doesn't appear to be a direct replacement that can be purchased. The Wyze Cam v3 is advertised as having a 130 degrees field of view with a f/1.6 aperture and the lens itself has a diameter of ~12mm and is ~16.7mm long. Further research shows that these types of camera lenses are quite common in CCTV applications and usually comes with a M12 thread which matches the diameter of the lens installed in the Wyze Cam v3.

I was able to find a cheap ($4 CAD) M12 lens with 150 degrees field of view and a f/2.5 aperture from Amazon. I wasn't able to find anything with a 130 FoV at a reasonable price and I wanted a cheap fix. After replacing the lens and adjusting it to focus, I had to trim the plastic standoff that held the camera PCB from the main board in order to make the lens flush with the face plate.

Here's the comparison between the damaged stock lens and the replacement lens during the day:

Wyze Cam v3 Lens Comparison
Wyze Cam v3 Lens Comparison

The night vision is significantly worse. All the color appears to have been completely lost. The dark summer sky which appears azure with the stock lens now appears dark green like a bad rendition of a Matrix scene.

Wyze Cam v3 Lens Comparison at night
Wyze Cam v3 Lens Comparison at night

Wi-Fi antenna replacement[edit | edit source]

The stock internal Wi-Fi antenna on the Wyze Cam v3 is a cheap stamped metal antenna which isn't great and seems to have poor reception. For cameras installed outside, the Wyze Cam seems to constantly struggle to keep a stable connection.

Since I was swapping out the camera lens anyway, I also went ahead and swapped out the stamped metal antenna with a internal PCB antenna. There're plenty of space within the camera housing, so I placed the PCB antenna behind where the speaker is.

Wyze Cam v3 antenna swap
Wyze Cam v3 antenna swap

Official firmware notes[edit | edit source]

Here are some notes on Wyze Cam v3 official firmware.

Download older firmware files[edit | edit source]

You can download the firmware files from their servers at https://download.wyzecam.com/firmware/v3/. There is no directory listing, so you'll have to guess the filenames based on the firmware version numbers. The firmware versions are available from their release notes posted on their website at https://support.wyze.com/hc/en-us/articles/360024852172-Release-Notes-Firmware.

I've collected most official firmware files in my GitHub repo at https://github.com/kohrar/Wyze-Firmwares.

Version Notes Download URL
4.61.0.1 RTSP beta. /config/app.ver reports 4.61.0.3 https://download.wyzecam.com/firmware/rtsp/demo_v3_RTSP_4.61.0.1.zip
4.36.0.125 Nov 2020, Initial firmware https://download.wyzecam.com/firmware/v3/demo_wcv3_4.36.0.125.bin.zip
4.36.0.228 https://download.wyzecam.com/firmware/v3/demo_wcv3_4.36.0.228.bin.zip
4.36.0.248 https://download.wyzecam.com/firmware/v3/demo_wcv3_4.36.0.248.bin.zip
4.36.0.280 Mar 2021 https://download.wyzecam.com/firmware/v3/demo_wcv3_4.36.0.280.bin.zip
4.36.1.4 May 2021 https://download.wyzecam.com/firmware/v3/demo_wcv3_4.36.1.4.bin.zip
4.36.2.5 Jun 2021, last to not require internet https://download.wyzecam.com/firmware/v3/demo_wcv3_4.36.2.5.bin.zip
4.36.3.19 Aug 2021 https://download.wyzecam.com/firmware/v3/demo_wcv3_4.36.3.19.zip
4.36.6.17 Nov 2021 https://download.wyzecam.com/firmware/v3/demo_wcv3_4.36.6.17.zip
4.36.7.22 Nov 2021 https://download.wyzecam.com/firmware/v3/demo_wcv3_4.36.7.22.zip
4.36.8.15 Dec 2021 https://download.wyzecam.com/firmware/v3/demo_wcv3_4.36.8.15.zip
4.36.8.26 Feb 2022 https://download.wyzecam.com/firmware/v3/demo_wcv3_4.36.8.26.zip
4.36.8.32 Feb 2022 https://download.wyzecam.com/firmware/v3/demo_wcv3_4.36.8.32.zip
4.36.9.131 Apr 2022 https://download.wyzecam.com/firmware/v3/demo_wcv3_4.36.9.131.zip
4.36.9.139 May 2022 https://download.wyzecam.com/firmware/v3/demo_wcv3_4.36.9.139.zip
Others See: https://github.com/kohrar/Wyze-Firmwares

How to upgrade / downgrade firmware[edit | edit source]

To upgrade or revert back to a specific firmware on the Wyze Cam, simply extract the .zip file into the root of a SD card. For the Wyze Cam v3, the firmware file should be named demo_wcv3.bin. Make sure that you remove or rename factory_t31_ZMC6tiIDQN if it exists since this will highjack the boot process and prevent it from entering the firmware update stage.

Insert the SD card into the camera and then power it on while holding the setup button until both blue + red (appearing purple) LEDs are on.[2] The flashing process should take about a minute, though it will likely depend on the speed of your SD card.

As of January 2022, Wyze has restricted the ability to use certain older firmware versions[3] and is forcing users to use an updated Wyze app. I'm unsure what the minimum Wyze Cam version is, though I was able to roll back to 4.36.2.5 without issue.

RTSP Beta firmware[edit | edit source]

The RTSP beta firmware is configured using /configs/.rtspConfig which contains the following configuration:

[RTSP]
Switch=1
Password=rtsp-password
Username=rtsp-username

With that file defined, restart the camera or the iCamera process and connect to RTSP at: rtsp://username:password@wyze-camera/live. The RTSP server is on the default port 554.

Side loading hacks[edit | edit source]

The firmware files for the v3 are signed and verified using ed25519. The signature is placed at the last 64 bytes of the demo.bin file and is checked by uboot. The files on flash don't appear to be verified which is why WyzeHack's OTA firmware update persists.

Fortunately for us, uboot also checks for a file called factory_t31_ZMC6tiIDQN on the SD card and attempts to boot that as the kernel image without any signature verification. You can build a custom image using the instructions at https://github.com/gtxaspec/wz_mini_hacks/blob/master/documentation/developer_environment.md.

Setup a Wyze Cam without the Wyze App or internet[edit | edit source]

You can indeed set up a Wyze Cam v3 out of the box without the Wyze App and use it as a normal RTSP-enabled IP Camera. This can be accomplished using the wz_mini_hacks firmware hack on an SD card and some tweaks to get the initial setup complete.

With a clean Wyze Cam v3, upgrade the firmware to the latest version:

  1. Unbox the Wyze Cam v3
  2. Place the latest firmware file on a SD card (I used 4.36.9.139) and insert it to the Wyze Cam
  3. Hold the SETUP button and power on the device. Hold the button down until you see both blue and red lights on
  4. After the upgrade is done, the camera will reboot and begin flashing the red LED. This red LED probably means the device isn't set up and is awaiting you to do something. Power off the camera.

Next, set up wz_mini_hacks and enable the self-hosted mode:

  1. Insert the SD card and copy the contents of SD_ROOT from the wz_mini_hacks repo to the SD card.
  2. Edit wz_mini/wz_mini.conf and set ENABLE_SELFHOSTED_MODE to true. This is needed so that we can just scan a QR code.
  3. Ensure that your SSH keys are set in wz_mini/etc/ssh/authorized_keys. We will need to SSH in once the camera comes up to finish up the setup.
  4. Power on the camera. You should hear "WZ Mini initializing" message.

After wz_mini_hacks is ready, you can then set up the WiFi connection with a QR code. If you need to generate this QR code without the Wyze App, see the WiFi QR code section below.

  1. Press the SETUP button once. You should hear "Ready to connect"
  2. Present it with a QR code with your WiFi settings.
  3. The camera should join your wireless network and you should hear a message saying it has connected.
  4. You should be able to SSH into the camera at this point.

If you are using the ENABLE_SELFHOSTED_MODE option with wz_mini_hacks, then you are all set to go. If you are using some other hacks, you will need to SSH/telnet in and edit /configs/.user_config so that bindOK=0 is changed to bindOK=1 so that the camera connects to your wireless network automatically when it starts up again in the future.

WiFi QR code[edit | edit source]

The initial WiFi configuration is sent to the Wyze Cam using a QR code. This QR code can be generated from the Wyze App without needing to connect the camera to their cloud (Just go through the process of adding a new Camera, enter your WiFi settings, then take a screenshot of the generated QR code for future use).

The QR code generated for the Wyze Cam v3 is a URL encoded string containing 6 variables. The string itself will look like this: b=68c7ebd2-07c6-4c&s=ZGQtd3J0&p=NgcDWVcmOzlZUysDFhs2CgoxOyMbChYGEA==&t=-6&r=USA&ty=1.

Here are each value in a bit more detail:

  1. b=[a-z0-9-]{8}-[a-z0-9-]{4}-[a-z0-9-]{2} - A randomly generated alphanumeric string in 3 sections. 8 characters, dash, 4 characters, dash, 2 characters. I'm not sure if this is actually used anywhere by the camera.
  2. s=base64(SSID) - The WiFi SSID in base64
  3. p=base64(xor(password, key)) - The base64 encoded and XOR encrypted password. The key here is Wfb86GZX82JbwzWkkPZBzkwgqtpswmlqqytjfrfxjzccjsyyaylbbdmzspxymdwz.
  4. t=-6 - Your timezone.
  5. r=USA - The country or region? I'm from Canada but the Wyze app still generated it as USA.
  6. ty=1 - Thank-you? I'll take it.

There's a handy dandy tool at https://codepen.io/ril3y/full/gXyzmO/ which can generate the QR code for you in the browser without needing the Wyze App.

Running without internet[edit | edit source]

Newer versions of the firmware (4.36.2.5 or later?) requires a constant internet connection to function. On these newer firmware versions, the internal iCamera process will reboot the camera when it's unable to contact the Wyze's servers. The reboot happens within about a minute or so and basically renders the device unusable at this state. The reboot is triggered by iCamera, possibly at a kernel or hardware watchdog level because locking down the reboot command isn't sufficient and killing iCamera eventually triggers a reboot as well. Making the Wyze domains resolve to another server is also ineffective as it also triggers a reboot.

The old firmware versions (4.36.0.x ~ 4.36.2.5 including the RTSP beta firmware 4.61.0.1) does not have the issue and I have had no issues using the Wyze Cam on these older versions after placing it on a non-routable network which effectively blocks all internet traffic to the camera. However, the iCamera process will attempt to restart the wireless connection periodically which results in 4-5 second disconnects every minute or so. The camera is functional even with these short disconnections, but it will cause stuttering and dropped frames.

Fixing the WiFi dropouts in iCamera[edit | edit source]

The iCamera program appears to bring the WiFi interface down and up when it is unable to reach some servers including Wyze's API server and Google. After 3 retries, the process will bounce the WiFi interface, kill and restart the DHCP client and WPA supplicant processes, and rewrite the hosts and resolv.conf files. It is this bouncing that causes the brief 4-5 second disconnects.

Fortunately, tracing down where exactly in the binary that triggers this behavior within the iCamera binary is made easy with the verbose iCamera debug messages (with source filenames and line numbers!) combined with some strace outputs I collected from the process during these dropout periods. There is a function that handles the network setup which sets up the WiFi interface, obtains an IP from DHCP, then attempts to check whether a domain works (DN something in the logs). When the domain check fails 3 times, it calls a few other functions to restart the network setup process over again.

The dropouts can be 'fixed' by overwriting the domain check with no-ops and ensuring that the retry loop never exits by no-opping out the decrement or store instructions. This will make iCamera retry the domain check rather than bouncing the network interface after 3 tries. There are other jobs that spawn via threadpool messages such as to validate the network (net-valid) and to uploading reboot log (upload-rebootlog). There are other calls to functions that send messages to a dongle (probably some other Wyze peripheral?). All these can be no-op'ed and be bypassed.

Here's the patch script that should work for 4.61.0.1 (RTSP beta), 4.36.9.139 (latest official firmware for the Wyze Cam v3 as of this writing) as well as 4.9.8.1002 for the Wyze Cam v2. Alternatively, use wz_mini_hacks which now includes my patch and define ENABLE_SELFHOST_MODE=true in wz_mini.conf which will trigger this script at boot time.

#!/opt/wz_mini/bin/bash
# Description: Applies the iCamera binary patch to prevent iCamera from restarting the wireless
#   network when it is unable to reach the internet or Wyze's cloud. This should only be used in
#   self-hosted environments as it may break Wyze App functionality.
# Author: Leo Leung <leo@steamr.com>
# Last modified: September 2022
#

set -e
PATH=$PATH:/opt/wz_mini/tmp/.bin

# Firmware version
Version=""

function main() {
	# Handle remove / apply commands or print the usage message.
	if [[ "$1" == "remove" ]] ; then
		remove_patch
		exit
	fi

	if [[ "$1" == "apply" ]] ; then
		apply_patch
		exit
	fi

	echo "Usage: $0 [apply|remove]"
	echo "  Applies the iCamera patch to make it work nice without Wyze Cloud connectivity"
}

function determine_version() {
	# Verify the iCamera version is supported
	MD5Sum=$(md5sum $1 | awk '{print $1}')

	if [[ "$MD5Sum" == "04b90d6d77be72a4dd8c18da4b31946a" ]] ; then
		echo "4.61.0.1"
	elif [[ "$MD5Sum" == "b1c96d966226d76db86c96ecdfdd79e9" ]] ; then
		echo "4.36.9.139"
	elif [[ "$MD5Sum" == "b187239d1881a97d4598798a2035c0f3" ]] ; then
		# v2 camera firmware
		echo "4.9.8.1002"
	else
		echo "Error: Unknown iCamera version with md5sum $MD5Sum"
		exit 1
	fi
}

function apply_patch() {
	# Check to see if the patched version is installed and is up to date
	if [ -f /opt/wz_mini/usr/bin/iCamera.patched ] ; then
		# Check the build date. (this check may be brittle?)
		OriginalDate=$(strings /system/bin/iCamera | grep "Build date" -A 1 | tail -n 1)
		PatchedDate=$(strings /opt/wz_mini/usr/bin/iCamera.patched | grep "Build date" -A 1 | tail -n 1)

		if [[ "$OriginalDate" == "$PatchedDate" ]] ; then
			echo "Patch already applied to current iCamera version."
			exit 0
		fi

		echo "Patched iCamera binary differs in build date. ($OriginalDate vs $PatchedDate)."
		echo "Patch is now reapplying."
	fi

	# Ensure our version works. This exits if it is unsupported.
	Version=$(determine_version /system/bin/iCamera)

	# Working in /tmp
	cd /tmp

	# Make a copy to patch
	cp /system/bin/iCamera iCamera

	# For the T20/v2 cameras, we also have to patch the libwyzeUtils.so library
	[ -f /opt/wz_mini/tmp/.T20 ] && cp /system/lib/libwyzeUtils.so libwyzeUtils.so

	# Apply our patches.
	patch_out_calls_to_test_cloud_url
	patch_out_jobs_after_connect
	patch_out_network_reset_to_idle
	patch_out_code_test_enable
	# v2 specific
	patch_wzutil_testconnectbyurl_skip_check
	patch_v2_led_connect_led

	echo -e "\n\nPatching done."
	md5sum iCamera
	[ -f /opt/wz_mini/tmp/.T20 ] && md5sum libwyzeUtils.so


	# Place it on the SD card and modify the iCamera script to use it.
	cp iCamera  /opt/wz_mini/usr/bin/iCamera.patched
	sed -i 's/\/system\/bin\/iCamera/\/opt\/wz_mini\/usr\/bin\/iCamera.patched/' /opt/wz_mini/usr/bin/iCamera

	# the v2 patched library should be copied to /opt/wz_mini/lib
	if [ -f /opt/wz_mini/tmp/.T20 ]; then
		cp libwyzeUtils.so /opt/wz_mini/lib/libwyzeUtils.so

		# Fix the LD_PRELOAD to use this patched version first.
		# The T20 has 'libcallback_t20.so:libtinyalsa.so.2.0.0'
		sed -i "s/LD_PRELOAD='libcallback_t20.so:libtinyalsa.so.2.0.0'/LD_PRELOAD='\/opt\/wz_mini\/lib\/libwyzeUtils.so:libcallback_t20.so:libtinyalsa.so.2.0.0'/" /opt/wz_mini/usr/bin/iCamera
	fi

	echo "Installed."
}

function remove_patch() {
	echo "Reverting iCamera patch."
	
	# Remove patched iCamera
	if [ -f /opt/wz_mini/usr/bin/iCamera.patched ] ; then
		rm -v /opt/wz_mini/usr/bin/iCamera.patched
	fi

	# Remove patched libwyzeUtils
	if [ -f /opt/wz_mini/lib/libwyzeUtils.so ] ; then
		rm -v /opt/wz_mini/lib/libwyzeUtils.so
	fi

	# Ensure iCamera shim script points to /system/bin/iCamera
	if grep -q iCamera.patched /opt/wz_mini/usr/bin/iCamera ; then
		sed -i 's/\/opt\/wz_mini\/usr\/bin\/iCamera.patched/\/system\/bin\/iCamera/' /opt/wz_mini/usr/bin/iCamera
	fi

	# If the libwyzeUtils is referenced for the t20, remove it
	if grep -q libwyzeUtils.so:libcallback_t20.so /opt/wz_mini/usr/bin/iCamera ; then
		sed -i "s/LD_PRELOAD='\/opt\/wz_mini\/lib\/libwyzeUtils.so:libcallback_t20.so:libtinyalsa.so.2.0.0'/LD_PRELOAD='libcallback_t20.so:libtinyalsa.so.2.0.0'/" /opt/wz_mini/usr/bin/iCamera
	fi

	echo "Removed."
}


# Patch out the calls to test cloud url, which calls the DN check function with NOPs
#  This isn't strictly necessary, though it will cause iCamera to constantly retry the tests and spam the iCamera outputs with messages like
#    DN:854]err: (getaddrinfo) fail:-2(Name or service not known), (domain: www.google.com
#  so we'll comment these calls here to make it hush up
function patch_out_calls_to_test_cloud_url() {
	[[ "$Version" == "4.61.0.1" ]] && Address="0x603b0 0x602d4"
	[[ "$Version" == "4.36.9.139" ]] && Address="0x89938 0x89858"
	[[ "$Version" == "4.9.8.1002" ]] && return; # Not in v2

	echo -e "\n\n====> Calling ${FUNCNAME[0]}\n"

	for i in  $Address ; do
		echo -e "\nOriginal at $i"
		dd if=iCamera bs=1 count=4 skip=$(($i)) 2>/dev/null | xxd

		echo "Patched"
		printf '\x00\x00\x00\x00' | dd conv=notrunc of=iCamera bs=1 seek=$(($i)) 2> /dev/null
		dd if=iCamera bs=1 count=4 skip=$(($i)) 2>/dev/null | xxd
	done
}


# Patch out the threadpool_add_job calls for net-valid and upload-rebootlog, and dongle send calls with NOPs
function patch_out_jobs_after_connect () {
	[[ "$Version" == "4.61.0.1" ]]   && Address="$(seq 0x070d0 4 0x07114)"
	[[ "$Version" == "4.36.9.139" ]] && Address="$(seq 0x7b184 4 0x7b1cc)"
	[[ "$Version" == "4.9.8.1002" ]] && Address="$(seq 0x089c8 4 0x08a30)"

	echo -e "\n\n====> Calling ${FUNCNAME[0]}\n"

	# Everything up until the last branch instruction
	for i in $Address ; do
		echo -e "\nOriginal at $i"
		dd if=iCamera bs=1 count=4 skip=$(($i)) 2>/dev/null | xxd

		echo "Patched"
		printf '\x00\x00\x00\x00' | dd conv=notrunc of=iCamera bs=1 seek=$(($i)) 2> /dev/null
		dd if=iCamera bs=1 count=4 skip=$(($i)) 2>/dev/null | xxd
	done
}

# When our calls to DN check cloud url fails, we run into the code that calls funky_network_function(0). We don't want that. So noop all this out
# All the code that sets DAT_005e4d54=0, prints debug message, and calls funky_network_func(0) is nooped out here.
# This fixes the network from going back to the idle state and bouncing everything
#  The call to print debug message could probably be left intact... we don't actually prevent 005e4d50=0  though..
function patch_out_network_reset_to_idle () {
	[[ "$Version" == "4.61.0.1" ]]   && Address="$(seq 0x6041c 4 0x6045c)"
	[[ "$Version" == "4.36.9.139" ]] && Address="$(seq 0x899a4 4 0x899e4)"
	[[ "$Version" == "4.9.8.1002" ]] && return; # this is in the libwyzeUtils.so library, I think. Can't find similar code

	echo -e "\n\n====> Calling ${FUNCNAME[0]}\n"

	for i in $Address ; do
		echo -e "\nOriginal at $i"
		dd if=iCamera bs=1 count=4 skip=$(($i)) 2>/dev/null | xxd

		echo "Patched"
		# noop
		printf '\x00\x00\x00\x00' | dd conv=notrunc of=iCamera bs=1 seek=$(($i)) 2> /dev/null
		dd if=iCamera bs=1 count=4 skip=$(($i)) 2>/dev/null | xxd
	done
}


# Patch out code_test trigger with NOPs
# For some reason on 4.36.9.139, iCamera starts the code_test section which just constantly lists /tmp over and over every 10 seconds.
# I don't know why this is getting triggered, so I'm going to patch this out from being called.
function patch_out_code_test_enable() {
	[[ "$Version" == "4.61.0.1" ]]   && return;  # No need to do this as it doesn't seem to be a problem
	[[ "$Version" == "4.36.9.139" ]] && Address="0x7dfcc"
	[[ "$Version" == "4.9.8.1002" ]] && return;  # not in the v2 firmware, I think. 

	echo -e "\n\n====> Calling ${FUNCNAME[0]}\n"

	for i in $Address ; do
		echo -e "\nOriginal at $i"
		dd if=iCamera bs=1 count=4 skip=$(($i)) 2>/dev/null | xxd

		echo "Patched"
		# noop
		printf '\x00\x00\x00\x00' | dd conv=notrunc of=iCamera bs=1 seek=$(($i)) 2> /dev/null
		dd if=iCamera bs=1 count=4 skip=$(($i)) 2>/dev/null | xxd
	done
}

# Patch libwyzeUtils.so so that the testconnectbyurl function always returns true, regardless of whether
# the cloud is available or not.
function patch_wzutil_testconnectbyurl_skip_check() {
	# For the v2 firmware using the libwyzeUtils.so library only.
	[ ! -f /opt/wz_mini/tmp/.T20 ]   && return  # Only on the v2
	[[ "$Version" == "4.61.0.1" ]]   && return
	[[ "$Version" == "4.36.9.139" ]] && return
	[[ "$Version" != "4.9.8.1002" ]] && return  # Only supports 4.9.8.1002

	echo -e "\n\n====> Calling ${FUNCNAME[0]}\n"

	# BEQ $4 $0 0x5E branches to the 'URL is null' section of the code which returns -1
	# We want to go there and return 0 always instead. So let's blez (always true on an unsigned int)
	# and patch the -1 to 0

	i="0x203d0" # should have content: 5e 00 80 10, (BEQ $4 $0 0x5E)
	echo -e "\nOriginal at $i"
	dd if=libwyzeUtils.so bs=1 count=4 skip=$(($i)) 2>/dev/null | xxd

	echo "Patched"  # patch with 5e 00 81 04  (BLEZ $4 0x5E)
	printf '\x5e\x00\x81\x04' | dd conv=notrunc of=libwyzeUtils.so bs=1 seek=$(($i)) 2> /dev/null
	dd if=libwyzeUtils.so bs=1 count=4 skip=$(($i)) 2>/dev/null | xxd

	# At 3056c, we load -1 to s0, which is our return code. fix this to 0
	i="0x2056c" # should have content: ff ff 10 24
	echo -e "\nOriginal at $i"
	dd if=libwyzeUtils.so bs=1 count=4 skip=$(($i)) 2>/dev/null | xxd

	echo "Patched" # patch with 00 00 10 24, should load s0 to 0 before returning
	printf '\x00\x00\x10\x24' | dd conv=notrunc of=libwyzeUtils.so bs=1 seek=$(($i)) 2> /dev/null
	dd if=libwyzeUtils.so bs=1 count=4 skip=$(($i)) 2>/dev/null | xxd
}

# Patch LED state in iCamera binary so LED stays off after connecting
# Applies only to the v2
function patch_v2_led_connect_led () {
	# Applies only to this particular firmware in the v2.
	[[ "$Version" != "4.9.8.1002" ]] && return

	echo -e "\n\n====> Calling ${FUNCNAME[0]}\n"

	# Use the big NOP space from patch_out_jobs_after_connect to call led_ctrl_run_action_by_state(5)

	i="0x089c8"
	# li 5 a0
	printf '\x05\x00\x04\x24' | dd conv=notrunc of=iCamera bs=1 seek=$(($i)) 2> /dev/null
	dd if=iCamera bs=1 count=4 skip=$(($i)) 2>/dev/null | xxd


	i="0x089cc"
	# jal led_ctrl_run_action_by_state
	printf '\xcc\xc4\x10\x0c' | dd conv=notrunc of=iCamera bs=1 seek=$(($i)) 2> /dev/null
	dd if=iCamera bs=1 count=4 skip=$(($i)) 2>/dev/null | xxd
}


main "$@"

This patch isn't quite complete however. I notice that there are DNS lookups to api.wyzecam.com is still being made every 5-7 seconds. Since this is on an isolated network, it might not really matter.

Configuring iCamera without internet[edit | edit source]

Here are some tips for those that are attempting to run their Wyze Cam on an isolate network or on a network that blocks internet access to these cameras:

  • The Wyze firmware sets the clock using /system/bin/timesync which includes hard coded NTP servers. If these server's hostnames resolve to a local NTP server, it seems like timesync will update the hardware clock when I run it manually.
  • Alternative ways to set the clock is to use the date command (have something telnet/ssh in periodically to set the clock) or use wz_mini_hacks which include the ntpd busybox applet that can be used to sync the clock through your own NTP server.
  • If you need to change a camera setting that's controlled by the Wyze App, the /configs/.user_configs file might be of interest to you (more about this below).

Camera configuration[edit | edit source]

The local camera configuration is stored in /configs/.user_config. The file itself looks something like this:

[NET]
bindOk=1
p2pid=####################
r_encr=################

[SETTING]
night_cut_thr=1
nightVision=3
night_led_ex=2
NIGHT_LED_flag=2
verSwitch=2
horSwitch=2
indicator=1
watermark_flag=2
recordType=3
drawBoxSwitch=2
bitRate=120
timezone=-6

[CAMERA_INFO]
TZ_STR=MST+0700MDT+0600,M3.2.0/02:00:00,M11.1.0/02:00:00

The [NET] section contains bindOk which should always be set to 1. Setting this to 0 will force the Wyze Cam to read a QR code before connecting to a wireless network. The p2pid and r_encr can be left empty if you never intend to have the camera talk to the cloud.

The [SETTING] section contains configuration keys and are described below. This is an incomplete list and was what I found by looking at the /configs/.user_configs file after changing each setting in the Wyze App. You should be able to change the values in this file and restart iCamera for it to take effect.

key description values
nightVision Enable IR 1 = on, 2 = off, 3 = auto
night_led_ex IR type? 1 = near, 2 = far
night_cut_thr IR threshold 1 = 'dusk mode', 2 = 'dark mode'
indicator Camera LED status lights 1 = on, 2 = off
verSwitch Flip vertically 1 = no flip, 2 = flip
horSwitch Flip horizontally 1 = no flip, 2 = flip
osdSwitch Show timestamp 1 = show, 2 = hide
watermark_flag Show wyze logo 1 = show, 2 = hide
AST Audio recording 1 = on, 2 = off
recordType Record to SD card 1 = continuous, 2 = events only, 3 = no recording
MASwitch Record when motion detected 1 = enable, 2 = disable
AASwitch Record when audio detected 1 = enable, 2 = disable
SASwitch Record when smoke alarm detected 1 = enable, 2 = disable
CASwitch Record when CO alarm detected 1 = enable, 2 = disable
AMALevel Sound detection sensitivity 2 through 254, higher is more sensitive
MMALevel Motion detection sensitivity 2 through 254, higher is more sensitive
drawBoxSwitch Draw the green boxes around objects 1 = enable, 2 = disable

There are other setting keys that can be found in the iCamera startup logs. See below:

<Reading> /configs/.user_config
[user_config.c:373][USER SET] [indicator    ] [2]
[user_config.c:373][USER SET] [nightVision  ] [3]
[user_config.c:373][USER SET] [bitRate      ] [120]
[user_config.c:373][USER SET] [res          ] [1]
[user_config.c:373][USER SET] [fps          ] [20]
[user_config.c:373][USER SET] [horSwitch    ] [2]
[user_config.c:373][USER SET] [verSwitch    ] [2]
[user_config.c:373][USER SET] [osdSwitch    ] [1]
[user_config.c:373][USER SET] [logSd        ] [1]
[user_config.c:373][USER SET] [logUdisk     ] [1]
[user_config.c:373][USER SET] [telntSwitch  ] [2]
[user_config.c:373][USER SET] [recordType   ] [3]
[user_config.c:373][USER SET] [MASwitch     ] [2]
[user_config.c:373][USER SET] [MALevel      ] [5]
[user_config.c:373][USER SET] [AASwitch     ] [2]
[user_config.c:373][USER SET] [AALevel      ] [5]
[user_config.c:373][USER SET] [SASwitch     ] [2]
[user_config.c:373][USER SET] [CASwitch     ] [2]
[user_config.c:373][USER SET] [TBStart      ] [0]
[user_config.c:373][USER SET] [TBDuration   ] [1440]
[user_config.c:373][USER SET] [drawBoxSwitch] [2]
[user_config.c:360]timezone value -6 read from config file
[timesync_utility.c:160]TZ file is set with UTC-06:00[user_config.c:373][USER SET] [timezone     ] [-6]
[user_config.c:373][USER SET] [MMALevel     ] [100]
[user_config.c:373][USER SET] [AMALevel     ] [100]
[user_config.c:373][USER SET] [MAT          ] [0]
[user_config.c:373][USER SET] [AST          ] [1]
[user_config.c:373][USER SET] [motionTrack  ] [2]
[user_config.c:373][USER SET] [motionCruisin] [2]
[user_config.c:373][USER SET] [AASX         ] [25]
[user_config.c:373][USER SET] [AASY         ] [25]
[user_config.c:373][USER SET] [AALX         ] [50]
[user_config.c:373][USER SET] [AALY         ] [50]
[user_config.c:373][USER SET] [motionbackvalue] [1]
[user_config.c:373][USER SET] [slide_x      ] [0]
[user_config.c:373][USER SET] [slide_y      ] [0]
[user_config.c:373][USER SET] [preset_x     ] [0]
[user_config.c:373][USER SET] [preset_y     ] [0]
[user_config.c:373][USER SET] [ubootflag    ] [0]
[user_config.c:373][USER SET] [exposureModel] [0]
[user_config.c:373][USER SET] [limit_max_x  ] [0]
[user_config.c:373][USER SET] [limit_min_x  ] [0]
[user_config.c:373][USER SET] [limit_max_y  ] [0]
[user_config.c:373][USER SET] [limit_min_y  ] [0]
[user_config.c:373][USER SET] [motor_limit_flag] [0]
[user_config.c:373][USER SET] [resolution_select_flag] [1]
[user_config.c:373][USER SET] [low_bitrate_num] [30]
[user_config.c:373][USER SET] [watermark_flag] [2]
[user_config.c:373][USER SET] [speaker_vol  ] [50]
[user_config.c:373][USER SET] [pir_alarm_switch] [0]
[user_config.c:373][USER SET] [NIGHT_LED_flag] [2]
[user_config.c:373][USER SET] [sdcard_exist ] [0]
[user_config.c:373][USER SET] [autoRecordNum] [0]
[user_config.c:373][USER SET] [autoRecordMem] [0]
[user_config.c:373][USER SET] [dongle_led   ] [1]
[user_config.c:373][USER SET] [alarmDate    ] [255]
[user_config.c:373][USER SET] [alarmInterval] [300]
[user_config.c:373][USER SET] [SDEVICE_ACTION] [0]
[user_config.c:373][USER SET] [voice_switch ] [2]
[user_config.c:373][USER SET] [voice_scene  ] [1]
[user_config.c:373][USER SET] [auto_clarity ] [2]
[user_config.c:373][USER SET] [cmc_upload   ] [1]
[user_config.c:373][USER SET] [p2p_log      ] [2]
[user_config.c:373][USER SET] [night_led_ex ] [2]
[user_config.c:373][USER SET] [night_cut_thr] [1]
[user_config.c:373][USER SET] [slightEnable ] [1]
[user_config.c:373][USER SET] [slightMode   ] [2]
[user_config.c:373][USER SET] [slightLowLight] [2]
[user_config.c:373][USER SET] [slightBright ] [255]
[user_config.c:373][USER SET] [hms_enable   ] [0]
[user_config.c:373][USER SET] [hms_state    ] [0]
[user_config.c:373][USER SET] [slightPreemptSw] [0]
[user_config.c:320]timezone string set to [MST+0700MDT+0600,M3.2.0/02:00:00,M11.1.0/02:00:00]

Note that the TZ_STR value is not what libc would expect (See: Timezone). You'll have to convert the TZ value (such as MST7MDT,M3.2.0,M11.1.0 to MST7:00MDT6:00,M3.2.0/02:00:00,M11.1.0/02:00:00).

For my use case in America/Edmonton, I set: TZ_STR=MST+0700MDT+0600,M3.2.0/02:00:00,M11.1.0/02:00:00

iCamera Logs[edit | edit source]

The firmware uses dmon to daemonize/restart processes and dslog to do the logging. The logging facility that's used can be access using logread. Run logread to get all the application logs including iCamera.

Alternatively, you can restart iCamera in the foreground to see its messages in real time. This can be done by running kill -9 $(pgrep iCamera) and then immediately calling /usr/bin/iCamera. There is a hardware watchdog that needs to be satisfied or else the hardware will automatically reset. As a result, you'll have to make sure you start iCamera within 10 or so seconds.

For the RTSP firmware, you'll have to also kill the liveMediaServer for iCamera to come up properly. Run: kill -9 $(pgrep iCamera.patch) ; kill -9 $(pgrep liveMediaServer)

For the wz_mini_hacks, start iCamera with: LD_PRELOAD=libsetunbuf.so /opt/wz_mini/usr/bin/iCamera -- dslog --priority DEBUG --facility USER iCamera

Some observations about the v3[edit | edit source]

After getting telnet access, here are some stuff about the v3 which might be of interest.

[root@WyzeCam-ED6A:~]# cat /proc/cpuinfo
system type             : Swan
machine                 : Unknown
processor               : 0
cpu model               : Ingenic Xburst V0.0  FPU V0.0
BogoMIPS                : 1391.00
wait instruction        : yes
microsecond timers      : no
tlb_entries             : 32
extra interrupt vector  : yes
hardware watchpoint     : yes, count: 1, address/irw mask: [0x0fff]
isa                     : mips32r1
ASEs implemented        :
shadow register sets    : 1
kscratch registers      : 7
core                    : 0
VCED exceptions         : not available
VCEI exceptions         : not available

Hardware                : isvp
Serial                  : 00000000 00000000 00000000 00000000

## MTD partitions hard coded to cmdline
[root@WyzeCam-ED6A:~]# cat /proc/cmdline
console=ttyS1,115200n8 mem=96M@0x0 rmem=32M@0x6000000 init=/linuxrc rootfstype=squashfs root=/dev/mtdblock2 rw mtdparts=jz_sfc:256K(boot),1984K(kernel),3904K(rootfs),3904K(app),1984K(kback),3904K(aback),384K(cfg),64K(para)

The firmware version can be found in /system/bin/app.ver:

[root@WyzeCam-7419:system]# cat /system/bin/app.ver
[VER]
appver=4.36.0.280

MTD Partitions[edit | edit source]

There is a 16MB flash. The MTD partitions are coded into the cmdline: mtdparts=jz_sfc:256K(boot),1984K(kernel),3904K(rootfs),3904K(app),1984K(kback),3904K(aback),384K(cfg),64K(para)

On startup, we see the following kernel message.

# dmesg
[    0.433992] 8 cmdlinepart partitions found on MTD device jz_sfc
[    0.440116] Creating 8 MTD partitions on "jz_sfc":
[    0.445056] 0x000000000000-0x000000040000 : "boot"
[    0.450404] 0x000000040000-0x000000230000 : "kernel"
[    0.455910] 0x000000230000-0x000000600000 : "rootfs"
[    0.461366] 0x000000600000-0x0000009d0000 : "app"
[    0.466624] 0x0000009d0000-0x000000bc0000 : "kback"
[    0.472007] 0x000000bc0000-0x000000f90000 : "aback"
[    0.477436] 0x000000f90000-0x000000ff0000 : "cfg"
[    0.482645] 0x000000ff0000-0x000001000000 : "para"
  • cfg on mtdblock6 is mounted rw as /configs.
  • app on mtdblock3 is mounted ro as /system.
  • rootfs on mtdblock2 is mounted ro as /.

Wyze Cam v2[edit | edit source]

Firmware[edit | edit source]

Official firmware[edit | edit source]

You can download the firmware files from their servers at https://download.wyzecam.com/firmware/v2/. There is no directory listing, so you'll have to guess the filenames based on the firmware version numbers. The firmware versions are available from their release notes posted on their website at https://support.wyze.com/hc/en-us/articles/360024852172-Release-Notes-Firmware.

I've collected most official firmware files in my GitHub repo at https://github.com/kohrar/Wyze-Firmwares.

Version Notes Download URL
4.28.4.49 RTSP beta https://download.wyzecam.com/firmware/rtsp/demo_v2_rtsp_4.28.4.49.bin.zip
4.9.6.156 Jul 2020 https://download.wyzecam.com/firmware/v2/demo_4.9.6.156.bin.zip
4.95.115 Aug 2020 https://download.wyzecam.com/firmware/v2/demo_4.9.5.115.bin.zip
4.9.8.860 Jan 2022 https://download.wyzecam.com/firmware/v2/demo_4.9.8.860.zip
4.9.8.1002 Mar 2022 https://download.wyzecam.com/firmware/v2/demo_4.9.8.1002.zip
Others See: https://github.com/kohrar/Wyze-Firmwares

Hacked firmware[edit | edit source]

There are two firmwares available to the v2:

  • Dafang Hacks (https://github.com/EliasKotlyar/Xiaomi-Dafang-Hacks) - this is a hack that is loaded with the stock firmware and adds additional features. You may also use the WyzeHacks on the Wyze Cam v2 which allows enabling telnet access on the stock firmware.
  • OpenMiko (https://github.com/openmiko/openmiko) - A drop in replacement for the stock firmware and allows you to use the v2 without needing Wyze's cloud services.
  • wz_mini_hacks (https://github.com/gtxaspec/wz_mini_hacks) - wz_mini_hacks also works on the Wyze Cam v2 as well as the Wyze Cam v3. It has mostly the same features. To install, run their image generation script, then copy the demo.bin and the SD_ROOT files onto a SD card. Flash the firmware and enjoy.

How to upgrade / downgrade firmware[edit | edit source]

To upgrade or revert back to a specific firmware on the Wyze Cam, simply extract the .zip file into the root of a SD card. For the Wyze Cam v2, the firmware file should be named demo.bin. Insert the SD card into the camera and then power it on while holding the setup button until both blue + amber LEDs are on.[2] The flashing process should take about a minute, though it will likely depend on the speed of your SD card.

This process should also be used for any hacked firmware that generates a demo.bin file.

Firmware settings[edit | edit source]

Like the v3, you can configure the camera settings without the Wyze App by editing an ini file. The configuration file for the v2 is /configs/.parameters (not /configs/.user_settings like on the v3).

From the iCamera log messages, you can see the following keys that can be set on the v2 firmware. Refer to the v3 camera configuration table above for more information for some of these keys.

<Reading> params from /configs/.parameters
[USERSET] [indicator    ] [1]
[USERSET] [nightVision  ] [3]
[USERSET] [bitRate      ] [100]
[USERSET] [res          ] [1]
[USERSET] [fps          ] [15]
[USERSET] [horSwitch    ] [1]
[USERSET] [verSwitch    ] [1]
[USERSET] [osdSwitch    ] [1]
[USERSET] [logSd        ] [1]
[USERSET] [logUdisk     ] [1]
[USERSET] [telntSwitch  ] [2]
[USERSET] [recordType   ] [1]
[USERSET] [MASwitch     ] [2]
[USERSET] [MALevel      ] [5]
[USERSET] [AASwitch     ] [0]
[USERSET] [AALevel      ] [5]
[USERSET] [SASwitch     ] [0]
[USERSET] [CASwitch     ] [0]
[USERSET] [TBStart      ] [0]
[USERSET] [TBDuration   ] [1440]
[USERSET] [drawBoxSwitch] [1]
[USERSET] [timezone     ] [0]
[USERSET] [MMALevel     ] [125]
[USERSET] [AMALevel     ] [125]
[USERSET] [MAT          ] [0]
[USERSET] [AST          ] [1]
[USERSET] [motionTrack  ] [2]
[USERSET] [motionCruisin] [2]
[USERSET] [AASX         ] [25]
[USERSET] [AASY         ] [25]
[USERSET] [AALX         ] [50]
[USERSET] [AALY         ] [50]
[USERSET] [motionbackvalue] [1]
[USERSET] [slide_x      ] [0]
[USERSET] [slide_y      ] [0]
[USERSET] [preset_x     ] [0]
[USERSET] [preset_y     ] [0]
[USERSET] [ubootflag    ] [0]
[USERSET] [exposureModel] [0]
[USERSET] [limit_max_x  ] [0]
[USERSET] [limit_min_x  ] [0]
[USERSET] [limit_max_y  ] [0]
[USERSET] [limit_min_y  ] [0]
[USERSET] [motor_limit_flag] [0]
[USERSET] [resolution_select_flag] [1]
[USERSET] [low_bitrate_num] [30]
[USERSET] [watermark_flag] [1]
[USERSET] [speaker_vol  ] [50]
[USERSET] [pir_alarm_switch] [0]
[USERSET] [NIGHT_LED_flag] [1]
[USERSET] [sdcard_exist ] [0]
[USERSET] [autoRecordNum] [0]
[USERSET] [autoRecordMem] [0]
[USERSET] [alarmDate    ] [255]
[USERSET] [SDEVICE_ACTION] [0]
[USERSET] [hms_enable   ] [0]
[USERSET] [hms_state    ] [0]
[USERSET] [CamPlus      ] [0]
[USERSET] [EdgeAI       ] [0]
[USERSET] [ObjectDetection] [0]
[USERSET] [FaceRecognition] [0]
[USERSET] [EventInsight ] [0]
[USERSET] [SoundDetection] [0]

RTSP streaming[edit | edit source]

There is an old beta firmware released by Wyze Labs with RTSP streaming support built in. However, this has not been updated and is deprecated.

For RTSP support, consider using one of the hacked firmwares listed above. Enabling RTSP with wz_mini_hacks is the same as on the Wyze Cam v3 which is covered in detail above.

Telnet access[edit | edit source]

The default username and password set on the Wyze Cam v2 is:

  • Username: root
  • Password: ismart12

If you are using wz_mini_hacks, you must use SSH using the public key defined in the authorized_keys file on the SD card.

Dafang Hacks[edit | edit source]

Clone the git repository at https://github.com/EliasKotlyar/Xiaomi-Dafang-Hacks. Format a new SD card with FAT32 and copy everything from firmware_mod to the root of the SD card. Edit config/wpa_supplicant.conf to look something like this:

# For more configuration option please see:
# https://w1.fi/cgit/hostap/plain/wpa_supplicant/wpa_supplicant.conf

ctrl_interface=/var/run/wpa_supplicant
ctrl_interface_group=0
ap_scan=1

network={
        ssid="wifi-network-ssid"
        # Uncomment to connect to Hidden SSIDs
        #scan_ssid=1
        key_mgmt=WPA-PSK
        pairwise=CCMP TKIP
        group=CCMP TKIP WEP104 WEP40
        psk="wpa-passphrase"
        priority=2
}

The default username and password is root and ismart12.

NFS notes[edit | edit source]

NFS can be mounted with

# mount -t nfs -o port=2049,nolock,proto=tcp nas:/data/public /mnt

A JPEG can be captured with

# /system/sdcard/bin/getimage > output.jpg

A quick and dirty way of just capturing pictures every couple seconds would be to create /system/sdcard/config/userscripts/startup/record.sh with

#!/bin/sh

while true ; do

        while true ; do
                if mount | grep -q nfs ; then
                        break
                fi

                echo "Attempting to mount /mnt"
                mount -t nfs -o port=2049,nolock,proto=tcp nas:/data/public /mnt
        done


        Dir="/mnt/wyzecam02/snapshots/`date +%Y%m/%d`"
        if [ ! -d $Dir ] ; then
                mkdir -p $Dir
        fi

        /system/sdcard/bin/getimage > $Dir/`date +%H%M%S`.jpg

        sleep 2
done &


Hacks[edit | edit source]

Applying the WyzeHacks hack[edit | edit source]

This option no longer works without spoofing DNS
Wyze Labs has restricted the payload URL and you can no longer arbitrarily pass in a random payload. As a result, this method won't work.

You may try to work around the problem by spoofing the DNS for s3-us-west-2.amazonaws.com and try updating the firmware using this URL: http://s3-us-west-2.amazonaws.com/wuv2/upgrade/WLPP1/firmware/1.2.0.80a.bin

But your mileage may vary in the future as Wyze continues to change the way their API works.

This issue is tracked at https://github.com/HclX/WyzeHacks/issues/132

WyzeHacks contains a series of scripts that push an upload payload via the SD card or over the air that enables additional features including telnet access. To get WyzeHacks working, you will need a Linux computer with Python on the same subnet as the Wyze Cams.

What I did to get the Wyze Cam v3 (and later v2) running telnet was:

  1. Clone the git repository as well as the WyzeUpdater repo
  2. Run build.sh on the root directory. This should generate a new release under the release directory.
  3. Go into the latest release directory cd ./release/release_0_5_06/. Execute the wyze_updater.py script.
  4. If prompted, enter your Wyze credentials. If you get an invalid token error, remove the existing token by running rm ~/.wyze_token.
  5. Any cameras associated with your account should be visible. Update the desired camera.
  6. You will see the script wait and print lots of ...'s. You can exit out of it by hitting Ctrl-C since the script does nothing past this point. Note that the update is automatic past this point. The Wyze Cam should come back online automatically. For the v3, the update took a few minutes. For the v2, it was nearly instantaneous.

The console output should look something like this:

$ ./remote_install.sh
Found local config file, including into firmware update archive...
Upgrade/config.inc
Defaulting to user installation because normal site-packages is not writeable
Requirement already satisfied: requests in /usr/lib/python3.9/site-packages (2.25.1)
Requirement already satisfied: idna<3,>=2.5 in /usr/lib/python3.9/site-packages (from requests) (2.10)
Requirement already satisfied: urllib3<1.27,>=1.21.1 in /usr/lib/python3.9/site-packages (from requests) (1.25.10)
Requirement already satisfied: chardet<5,>=3.0.2 in /usr/lib/python3.9/site-packages (from requests) (4.0.0)
INFO:root:Trying saved credentials from /home/leo/.wyze_token.
INFO:root:Checking device, mac=7C78B23AF941

Device type:      Camera (WYZE_CAKP2JFUS)
Device name:      v3b
Firmware version: 4.36.0.280
IP Address:       10.1.3.252
Pushing firmware to this device? [y/N]:y

INFO:root:Serving firmware file './firmware.bin' as 'http://s3-us-west-2.amazonaws.com/wuv2/upgrade/WLPP1/firmware/1.2.0.80a.bin', md5=7cde34dd8669101e0a93ae962109d0be
INFO:root:Checking device, mac=A4DA222C2591

Press Ctrl+C when all the updates are done...
.......^C
INFO:root:Stopping http server...

Troubleshooting[edit | edit source]

Broken pipe while updating[edit | edit source]

Initially, I had issues with the script where it kept on getting a Broken Pipe exception and then bailing out. This somehow solved itself after moving the laptop to another room, so it might be an issue with the network.

DNS spoofing[edit | edit source]

Recently, Wyze Labs have changed their API such that you can no longer set an arbitrary firmware URL. Attempting to do so will result in a RuntimeError: Request failed, error 3005:UnauthorizedOperation error. To work around this, you will need to:

  1. Spoof s3-us-west-2.amazonaws.com to a local web server hosting your wyze hacks firmware file
  2. Serve the firmware from this web server, so that the URL http://s3-us-west-2.amazonaws.com/wuv2/upgrade/WLPP1/firmware/1.2.0.80a.bin works
  3. Edit the wyze_updater.py script so that the URL is set to this specific URL by adding the following line:
url = build_url(args.addr, args.ssl, args.port)
url = "http://s3-us-west-2.amazonaws.com/wuv2/upgrade/WLPP1/firmware/1.2.0.80a.bin"

Ensure that the MD5 checksum the wyze_updater.py script shows matches the MD5 of the file that's being served. If it mismatches, the update will fail and the camera will simply reboot without any changes.

See Also[edit | edit source]