Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Please Add ONVIF Support. - UniFi Protect 5.0 now Supports Third Party Camera's. #1355

Open
StoneLegion opened this issue Sep 17, 2024 · 82 comments
Labels
enhancement New feature or request

Comments

@StoneLegion
Copy link

Describe the bug

I'm wondering if anyone able to get it to work? It says any camera that uses ONVIF Support, not sure if we emulate or can emulate such a feature?

I have a Dream Machine Pro, and tbh I love to get away from Blue Iris.

Affected Bridge Version

0.0

Bridge type

Docker Run/Compose

Affected Camera(s)

No response

Affected Camera Firmware

No response

docker-compose or config (if applicable)

No response

@StoneLegion StoneLegion added the bug Something isn't working label Sep 17, 2024
@Dudleydogg
Copy link

The current known bug with unifi is you can't authenticate the onvif it fails even if you have correct credentials apparently they are aware but no fix date is posted yet

@StoneLegion
Copy link
Author

The current known bug with unifi is you can't authenticate the onvif it fails even if you have correct credentials apparently they are aware but no fix date is posted yet

I think still Wyze Docker would not work because it does not output or provide a working onvif protocol? Unless I'm not understanding that.

@StoneLegion StoneLegion changed the title UniFi Protect 5.0 now Supports Third Party Camera's. Please Add ONVIF Support. - UniFi Protect 5.0 now Supports Third Party Camera's. Sep 23, 2024
@mrlt8
Copy link
Owner

mrlt8 commented Sep 23, 2024

I have an old local build with some basic support for ONVIF - auto discovery only works on a linux host as it seems to require the host network_mode.

I don't have any ubiquiti gear to test it with, but I can try to clean up some of that code.

@StoneLegion
Copy link
Author

I have an old local build with some basic support for ONVIF - auto discovery only works on a linux host as it seems to require the host network_mode.

I don't have any ubiquiti gear to test it with, but I can try to clean up some of that code.

Thank you, I guess I will have to learn to set it up on Linux hehe. I think for me getting away from the costly Blue Iris would be a really nice deal and I already had a UDM Pro already so this is pretty cool. Thanks for looking into it.

@thereal-BLKMGK
Copy link

BI's new licensing where if you go out of support they stop recording is pushing me to look elsewhere too although I suspect it'll be something closer to Frigate that Unifi although I run some of their hardware (not their NVR). The Wyze-bridge runs GREAT in a container on Linux!

Wish something like this was available for the Ring lights I inherited - grr!

mrlt8 added a commit that referenced this issue Sep 29, 2024
@mrlt8 mrlt8 added enhancement New feature or request and removed bug Something isn't working labels Sep 29, 2024
@mrlt8
Copy link
Owner

mrlt8 commented Sep 29, 2024

I pushed a new onvif image that should have some basic support for ONVIF. I decided to disable to ws-discovery stuff for now, but it should still work without the auto discovery if you point your client to your bridge and work on all platforms even without host mode.

This is still very basic and doesn't support any auth stuff yet, so you may have to set WB_AUTH=false.

@idaband
Copy link

idaband commented Sep 30, 2024

I pushed a new onvif image that should have some basic support for ONVIF. I decided to disable to ws-discovery stuff for now, but it should still work without the auto discovery if you point your client to your bridge and work on all platforms even without host mode.

This is still very basic and doesn't support any auth stuff yet, so you may have to set WB_AUTH=false.

sorry still reading through the code ...did you have a separate port / url that will be used to stream a particular camera ect?

@mrlt8
Copy link
Owner

mrlt8 commented Sep 30, 2024

You should be able to point it to your bridge on port 5000.

@idaband
Copy link

idaband commented Oct 1, 2024

Looking into seeing if its a firewall issue now. Does anyone else out been able to test this too?

https://help.ui.com/hc/en-us/articles/26301104828439-Third-Party-Cameras-in-UniFi-Protect

Preparing a Third-Party Camera

  1. Reference the OEM user manual for instructions on how to set up a specific camera. Generic steps are provided below.
  2. Connect the camera to the network and lookup the camera IP address in the UniFi Network app.
  3. In your browser, navigate to the camera’s local web server.
  4. Log in to the camera with default credentials and configure a username and password.
  5. Check if the camera requires a firmware update. Apply any updates available.
  6. Enable ONVIF in the camera and configure an ONVIF username and password. This is enabled automatically on some cameras and the global username and password are used.
  7. Configure the date, time, and timezone on the camera. Some cameras will fail authentication if the time is not set correctly.
    7a If applicable, ensure the authentication is set to Digest&ws-username token

@StoneLegion
Copy link
Author

As far as I know mrlr8 said discovery is disabled so Unifi is not going pickup anything till that is added.
"I decided to disable to ws-discovery stuff for now"

So I assume, not sure if it's correct, but it should not work on the unifi stuff yet.

@mrlt8
Copy link
Owner

mrlt8 commented Oct 1, 2024

Most of the ONVIF clients that I've been testing have an option to "manually" add a camera.

I can enable the WS discovery, but it will require the host network mode in Docker. AFAIK, the ONVIF auto discovery is only used for finding/adding the camera and is not used after that.

Edit: latest wyze-bridge:onvif image should have support for authentication if WB_AUTH is enabled. The username will be wb and the password will be the api key listed at the bottom of the WebUI.

@StoneLegion
Copy link
Author

Yeah currently the ubiquity one can only find it via discovery no manual adding yet. But this is their very first iteration so they might add more over time.

mrlt8 added a commit that referenced this issue Oct 1, 2024
@mrlt8
Copy link
Owner

mrlt8 commented Oct 1, 2024

Latest onvif image should have a WS_DISCOVERY option:

services:
    wyze-bridge:
        container_name: wyze-bridge
        restart: unless-stopped
        image: mrlt8/wyze-bridge:onvif # onvif image
        network_mode: host # host mode
        environment:
            - WS_DISCOVERY=true # enable discovery
            ....

@Nayruden
Copy link

Nayruden commented Oct 1, 2024

@StoneLegion I've got a Ubiquity UNVR, and you can add cameras manually, though they didn't make it easy to find. See the screenshot below.

github

However, I can't authenticate to the bridge. I tried username of wb and the API key shown at the bottom of the page (same as what I set via WB_API) but Unifi tells me "Invalid Credentials" and I get the following in the logs for the bridge.

[WyzeBridge] Onvif auth failed
[WyzeBridge] 10.42.0.128 - - [01/Oct/2024 21:54:09] "POST /onvif/device_service HTTP/1.1" 200 -
[WyzeBridge] 10.42.0.128 - - [01/Oct/2024 21:54:09] "POST /onvif/device_service HTTP/1.1" 200 -
[WyzeBridge] 10.42.0.128 - - [01/Oct/2024 21:54:09] "POST /onvif/device_service HTTP/1.1" 200 -
[WyzeBridge] 10.42.0.128 - - [01/Oct/2024 21:54:09] "POST /onvif/media_service HTTP/1.1" 200 -
[WyzeBridge] 10.42.0.128 - - [01/Oct/2024 21:54:09] "POST /onvif/media_service HTTP/1.1" 200 -
[WyzeBridge] 10.42.0.128 - - [01/Oct/2024 21:54:09] "POST /onvif/device_service HTTP/1.1" 200 -
[WyzeBridge] 10.42.0.128 - - [01/Oct/2024 21:54:09] "POST /onvif/media_service HTTP/1.1" 200 -
[WyzeBridge] 10.42.0.128 - - [01/Oct/2024 21:54:09] "POST /onvif/media_service HTTP/1.1" 200 -

@mrlt8 I tried reviewing the authentication code for this but nothing stands out to me. Any ideas?

@StoneLegion
Copy link
Author

Wow this is great work and thanks @Nayruden I will have to try this when I have time. Please keep us updated with it.

@Nayruden
Copy link

Nayruden commented Oct 1, 2024

Something is up with the authentication code, I believe. Even with authentication disabled WB_AUTH=False, it's still failing auth.

[WyzeBridge] [AUTH] WB_AUTH=False
[WyzeBridge] 🔍 Could not find local cache for 'cameras'
[WyzeBridge] ☁️ Fetching 'cameras' from the Wyze API...
[WyzeBridge] [API] Fetched [6] cameras
[WyzeBridge] 💾 Saving 'cameras' to local cache...
<SNIP>
[WyzeBridge] [MTX] starting MediaMTX 1.9.0
[WyzeBridge] 🎬 6 streams enabled
[WyzeBridge] 10.42.0.128 - - [01/Oct/2024 22:37:54] "POST /onvif/device_service HTTP/1.1" 200 -
[WyzeBridge] Onvif auth failed

@idaband
Copy link

idaband commented Oct 2, 2024

I agree. Something with auth if failing. I went to auth = false and turned off network host and then i could log in.
(although my network host is not working as expected. I'm getting a 65 subnet instead of 1 and i'm not using any mask )

I did want to say THANK YOU THANK YOU for your help working this. If it doesn't happen any time soon or at all ..its ok. It's just going to be pretty crazy fun if it does end up working.

@mrlt8
Copy link
Owner

mrlt8 commented Oct 2, 2024

I made some changes to disable validating the authentication headers when WB_AUTH is set to false since some clients will still send security related headers.

Still not sure why authentication is failing when WB_AUTH is enabled though...

@idaband
Copy link

idaband commented Oct 2, 2024

which tag? i see three? or should we just pull the code and build the image locally?

mrlt8 added a commit that referenced this issue Oct 2, 2024
@mrlt8
Copy link
Owner

mrlt8 commented Oct 2, 2024

All the onvif related stuff should be in the onvif tag or you can build the image from the onvif branch locally.

The latest onvif image should show the 92a38e9 SHA on startup like this: 🚀 DOCKER-WYZE-BRIDGE ... 92a38e9

@Nayruden
Copy link

Nayruden commented Oct 2, 2024

With WB_AUTH=True now I see...

[WyzeBridge] [ONVIF] Auth failed for action='GetSystemDateAndTime' with creds=None
[WyzeBridge] 10.42.0.128 - - [02/Oct/2024 11:10:03] "POST /onvif/device_service HTTP/1.1" 200 -
[WyzeBridge] 10.42.0.128 - - [02/Oct/2024 11:10:03] "POST /onvif/device_service HTTP/1.1" 200 -
[WyzeBridge] 10.42.0.128 - - [02/Oct/2024 11:10:03] "POST /onvif/device_service HTTP/1.1" 200 -
[WyzeBridge] 10.42.0.128 - - [02/Oct/2024 11:10:03] "POST /onvif/media_service HTTP/1.1" 200 -
[WyzeBridge] 10.42.0.128 - - [02/Oct/2024 11:10:03] "POST /onvif/media_service HTTP/1.1" 200 -
[WyzeBridge] 10.42.0.128 - - [02/Oct/2024 11:10:03] "POST /onvif/device_service HTTP/1.1" 200 -
[WyzeBridge] 10.42.0.128 - - [02/Oct/2024 11:10:03] "POST /onvif/media_service HTTP/1.1" 200 -
[WyzeBridge] 10.42.0.128 - - [02/Oct/2024 11:10:03] "POST /onvif/media_service HTTP/1.1" 200 -

It still fails with "authentication failed" with WB_AUTH=False though. @mrlt8 send me an email at [email protected] and I'll send you a pcap. I think that it's never sending creds with GetSystemDateAndTime but otherwise the creds are working and it's failing on something else.

@Nayruden
Copy link

Nayruden commented Oct 4, 2024

Nope -- doesn't matter what the bitrate is set to. It seems like UNVR will always take the first stream as "high quality" and the second as "low quality".

@Nayruden
Copy link

Nayruden commented Oct 4, 2024

Awesome! I've got scrubbing working now. I have SUBSTREAM=True and SUB_QUALITY=SD30 along with the following ugly hack replacement function in onvif.py. I wasn't sure what type of collection/iterable streams was and I didn't want to debug/trace it so I took a very brute force approach. 🤣

def get_profiles(streams):
    resp = """<trt:GetProfilesResponse>
            """
    resp_high = ""
    resp_low = ""
    for stream in streams:
        bitrate = 200 if "-sub" not in stream else 100
        resp_add = f"""<trt:Profiles token="{stream}" fixed="true">
                <tt:Name>{stream}</tt:Name>
                <tt:VideoSourceConfiguration token="VideoSourceConfig_1">
                    <tt:SourceToken>VideoSource_1</tt:SourceToken>
                    <tt:Bounds x="0" y="0" width="1920" height="1080"/>
                </tt:VideoSourceConfiguration>
                <tt:VideoEncoderConfiguration token="VideoEncoderConfig_1">
                    <tt:Encoding>H264</tt:Encoding>
                    <tt:Resolution>
                        <tt:Width>1920</tt:Width>
                        <tt:Height>1080</tt:Height>
                    </tt:Resolution>
                    <tt:RateControl>
                        <tt:BitrateLimit>{bitrate}</tt:BitrateLimit>
                    </tt:RateControl>
                </tt:VideoEncoderConfiguration>
            </trt:Profiles>
            """
        if "-sub" in stream:
            resp_low += resp_add
        else:
            resp_high += resp_add
    return resp + resp_high + resp_low + "</trt:GetProfilesResponse>"

@StoneLegion
Copy link
Author

It's been a week since the last reply. Is it running solid and well for yo guys? Worth migrating from Blue Iris to it or is there some stuff that needs to be fixed either UBNT side of things on the Hack?

@bdog4life
Copy link

I would like to test this, How would i Enable ONVIF in a Portainer config? Or is that not supported?

@Nayruden
Copy link

It's been a week since the last reply. Is it running solid and well for yo guys? Worth migrating from Blue Iris to it or is there some stuff that needs to be fixed either UBNT side of things on the Hack?

It's been working rock-solid for me! I've got four Wyze cameras streaming to UNVR now and they work just like the official Unifi cameras, except there's no motion detection.

@jpriganc
Copy link

@mrlt8 Unfortunately, you are right -- it does not accept a path. It only accepts an IP or hostname with a port. The way I got around this was that I added a bunch of alias domains on my network (EG kitchen.cam.local, hallway.cam.local, etc) that all point to the IP address of the Wyze bridge. When I added these different domains to the Protect, I used FILTER_NAMES on the Wyze bridge side so that it would only present a profile of my choice to the UNVR during that initial negotiation. Obviously not ideal but it's been working 100% since the initial registration and I am very happy with it so far.

So, as far as the initial registration, our only options are to vary the hostname / IP or the port as idaband suggested. I suspect most people would find the hostname registration straightforward enough (can do it from their router or PiHole, etc). In order to ease registration with that, maybe we could have a drop down selector on the Wyze bridge admin UI for which camera it will present to ONVIF? The camera adoption flow would then be as follows....

  • User sets up hostname for camera 1 -- cam-1.my.house (points to IP for Wyze bridge)
  • User selects "cam 1" in the Wyze bridge web UI ONVIF drop down
  • User adopts camera in their NVR, pointing it at cam-1.my.house:5000
  • User sets up hostname for camera 2 -- cam-2.my.house
  • User selects "cam 2" in the Wyze bridge web UI ONVIF drop down
  • User adopts camera in their NVR, pointing it at cam-2.my.house:5000

@Nayruden - sorry if this is a dumb question, but where did you put the "Filter_Names" option? Is that in the Docker settings or in the onvif.py file? I am running this on UnRAID, using the pre-built package. Also, I can't find the onvif.py file anywhere to modify. I need to keep looking for that. I have been recording one camera for the last week, but it is bringing in two different streams for that one camera.

@Nayruden
Copy link

FILTER_NAMES is an env var -- https://github.com/mrlt8/docker-wyze-bridge/wiki/Camera-Filtering

onvif.py needs to be added via a Dockerfile build or by a volume mount into the container. Here are instructions on how to do both of those options -- https://chatgpt.com/share/670e8461-4458-8010-a0bb-a9fea35e0cf1 . The container path is /app/wyzebridge/onvif.py

@MNewman
Copy link

MNewman commented Oct 16, 2024

@Nayruden I follow the idea of having multiple hostnames pointing to the same IP, but I'm missing how we get wyze-bridge to present the single filtered camera on each host name. Are you running multiple copies of wyze bridge, each filtered to a single camera?

@Nayruden
Copy link

@Nayruden I follow the idea of having multiple hostnames pointing to the same IP, but I'm missing how we get wyze-bridge to present the single filtered camera on each host name. Are you running multiple copies of wyze bridge, each filtered to a single camera?

No, I'm only running one copy. ONVIF is a one-time setup process. ONVIF hands back the RTSP stream URI and the NVR only ever cares about that URI that was handed back going forward. The wyze-bridge hands back RTSP streams that include the name of the camera and it doesn't matter what ONVIF passes to other clients in the future since it only ever grabs the data once. EG, see #1355 (comment) for how it stores the camera data after getting it from ONVIF -- it has the RTSP stream saved and that's all it ever cares about going forward.

At least, that's how UNVR works. I'm assuming others would be the same.

@idaband
Copy link

idaband commented Oct 16, 2024

i have 8 cameras...i'm getting ready to use a new PI for all the work. @Nayruden does the onvif image have hardware acceleration in it?

@Nayruden
Copy link

i have 8 cameras...i'm getting ready to use a new PI for all the work. @Nayruden does the onvif image have hardware acceleration in it?

Unless you're doing something unusual, my understanding is that wyze-bridge does a stream copy (no additional processing with CPU or GPU required).

@Toei79
Copy link

Toei79 commented Oct 28, 2024

wish know how work this , understanding its for cameras with onvif type? i have a few brands, use other method to able to play on synology surveillance, but its possible do on bridge great. understanding need inject onvif.py and how its on the chapgpt chat file with the info about my cameras? thats a python file ?

image
image

it gives a link address but after you shutdown camera or open app it gives a new address. no fully sure about this exactly but do some like that , no checked again just now

it gives something like this , rtsp://192.168.2.208:554/live/1jxxxxxjzaxa_p0_UFDWJJDBIHUE , then change last letters ramdomly

@Toei79
Copy link

Toei79 commented Nov 6, 2024

ok i figure out this lol . i did in the past cant remember before. one above give a different token? on the link ,

then go2rtc its able to get a permanent link

onvif://email:[email protected]:80?subtype=SubProfileToken

go2rtc discover camera with some data, and generate this one above, and you use to get m3u8 links there

so how i can use any of this for use onvif? i have already running onvif docker with this
version: '3'

services:

wyze-bridge:
container_name: wyze-bridge
image: mrlt8/wyze-bridge:onvif # onvif image
network_mode: host # Use host network mode
restart: unless-stopped # Restart policy
environment:
- WS_DISCOVERY=true # Enable ONVIF discovery
# Add additional environment variables as needed
# - WYZE_EMAIL=email
# - WYZE_PASSWORD=pass
# - RTSP_PORT=8554

but not discovers any camera onvif.

@TylerDIREC
Copy link

Hey! Wondering if this has been getting further attention. From what I am seeing I am unable to add multiple cameras using the DNS and filtering. I've also tried multiple instances of the bridge with filtering and protect will only add 1 camera. I think it's recognizing the IP yet I may be wrong. Also wondering if WS_Discovery should be working and if so does it work when the Bridge Host and Cameras are on separate vLans. Thanks :)

@daxiang28
Copy link

daxiang28 commented Dec 10, 2024

@mrlt8 Unfortunately, you are right -- it does not accept a path. It only accepts an IP or hostname with a port. The way I got around this was that I added a bunch of alias domains on my network (EG kitchen.cam.local, hallway.cam.local, etc) that all point to the IP address of the Wyze bridge. When I added these different domains to the Protect, I used FILTER_NAMES on the Wyze bridge side so that it would only present a profile of my choice to the UNVR during that initial negotiation. Obviously not ideal but it's been working 100% since the initial registration and I am very happy with it so far.

So, as far as the initial registration, our only options are to vary the hostname / IP or the port as idaband suggested. I suspect most people would find the hostname registration straightforward enough (can do it from their router or PiHole, etc). In order to ease registration with that, maybe we could have a drop down selector on the Wyze bridge admin UI for which camera it will present to ONVIF? The camera adoption flow would then be as follows....

  • User sets up hostname for camera 1 -- cam-1.my.house (points to IP for Wyze bridge)
  • User selects "cam 1" in the Wyze bridge web UI ONVIF drop down
  • User adopts camera in their NVR, pointing it at cam-1.my.house:5000
  • User sets up hostname for camera 2 -- cam-2.my.house
  • User selects "cam 2" in the Wyze bridge web UI ONVIF drop down
  • User adopts camera in their NVR, pointing it at cam-2.my.house:5000

The hostnames are different so the NVR treats them as separate devices. Here's an example of how UNVR sees the devices at the end of this. (I modified the SOAP return so the snapshots would work in these examples)

Cam 1

{
  "device": {
    "manufacturer": "Wyze Bridge",
    "model": "onvif Build [X86_64]",
    "firmwareVersion": "v2.10.3",
    "serialNumber": 0,
    "hardwareId": "X86_64 ONVIF BUILD [2024-10-02t15:44:22.474z] 44c1fc6"
  },
  "snapshotUri": "http://wbadmin:[email protected]:5000/snapshot/cam1.jpg",
  "streams": [
    {
      "resolution": {
        "width": 1920,
        "height": 1080
      },
      "encoding": "H264",
      "bitrate": 5000,
      "uri": "rtsp://wb:[email protected]:8554/cam1"
    }
  ],
  "camera": {},
  "profiles": [
    {
      "$": {
        "token": "cam1",
        "fixed": true
      },
      "name": "cam1",
      "videoSourceConfiguration": {
        "$": {
          "token": "VideoSourceConfig_1"
        },
        "sourceToken": "VideoSource_1",
        "bounds": {
          "$": {
            "x": 0,
            "y": 0,
            "width": 1920,
            "height": 1080
          }
        }
      },
      "videoEncoderConfiguration": {
        "$": {
          "token": "VideoEncoderConfig_1"
        },
        "encoding": "H264",
        "resolution": {
          "width": 1920,
          "height": 1080
        },
        "rateControl": {
          "bitrateLimit": 5000
        }
      }
    }
  ]
}

Cam2

{
  "device": {
    "manufacturer": "Wyze Bridge",
    "model": "onvif Build [X86_64]",
    "firmwareVersion": "v2.10.3",
    "serialNumber": 0,
    "hardwareId": "X86_64 ONVIF BUILD [2024-10-02t15:44:22.474z] 44c1fc6"
  },
  "snapshotUri": "http://wbadmin:[email protected]:5000/snapshot/cam2.jpg",
  "streams": [
    {
      "resolution": {
        "width": 1920,
        "height": 1080
      },
      "encoding": "H264",
      "bitrate": 5000,
      "uri": "rtsp://wb:[email protected]:8554/cam2"
    }
  ],
  "camera": {},
  "profiles": [
    {
      "$": {
        "token": "cam2",
        "fixed": true
      },
      "name": "cam2",
      "videoSourceConfiguration": {
        "$": {
          "token": "VideoSourceConfig_1"
        },
        "sourceToken": "VideoSource_1",
        "bounds": {
          "$": {
            "x": 0,
            "y": 0,
            "width": 1920,
            "height": 1080
          }
        }
      },
      "videoEncoderConfiguration": {
        "$": {
          "token": "VideoEncoderConfig_1"
        },
        "encoding": "H264",
        "resolution": {
          "width": 1920,
          "height": 1080
        },
        "rateControl": {
          "bitrateLimit": 5000
        }
      }
    }
  ]
}

Since the NVR remembers the full path to the correct device after the initial registration, it doesn't matter if Wyze bridge is advertising another camera on ONVIF later.

@Nayruden Nayruden I'm wondering if anything has changed in Protect. When I try to add a new camera with a new hostname (but pointing to the same IP:Port, It just updates the first one as if it fully resolves the dns to the IP and sees them as the same device.

@Nayruden
Copy link

@daxiang28 I'm running the latest version of everything and it's still working great for me. But I haven't added any additional cameras since I first set it all up. It would be odd if the Protect is trying to align devices by IP though, since the major reason of using DNS is so that the underlying IP can change later.

@wr1williams
Copy link

wr1williams commented Dec 10, 2024

@mrlt8 Unfortunately, you are right -- it does not accept a path. It only accepts an IP or hostname with a port. The way I got around this was that I added a bunch of alias domains on my network (EG kitchen.cam.local, hallway.cam.local, etc) that all point to the IP address of the Wyze bridge. When I added these different domains to the Protect, I used FILTER_NAMES on the Wyze bridge side so that it would only present a profile of my choice to the UNVR during that initial negotiation. Obviously not ideal but it's been working 100% since the initial registration and I am very happy with it so far.
So, as far as the initial registration, our only options are to vary the hostname / IP or the port as idaband suggested. I suspect most people would find the hostname registration straightforward enough (can do it from their router or PiHole, etc). In order to ease registration with that, maybe we could have a drop down selector on the Wyze bridge admin UI for which camera it will present to ONVIF? The camera adoption flow would then be as follows....

  • User sets up hostname for camera 1 -- cam-1.my.house (points to IP for Wyze bridge)
  • User selects "cam 1" in the Wyze bridge web UI ONVIF drop down
  • User adopts camera in their NVR, pointing it at cam-1.my.house:5000
  • User sets up hostname for camera 2 -- cam-2.my.house
  • User selects "cam 2" in the Wyze bridge web UI ONVIF drop down
  • User adopts camera in their NVR, pointing it at cam-2.my.house:5000

The hostnames are different so the NVR treats them as separate devices. Here's an example of how UNVR sees the devices at the end of this. (I modified the SOAP return so the snapshots would work in these examples)
Cam 1

{
  "device": {
    "manufacturer": "Wyze Bridge",
    "model": "onvif Build [X86_64]",
    "firmwareVersion": "v2.10.3",
    "serialNumber": 0,
    "hardwareId": "X86_64 ONVIF BUILD [2024-10-02t15:44:22.474z] 44c1fc6"
  },
  "snapshotUri": "http://wbadmin:[email protected]:5000/snapshot/cam1.jpg",
  "streams": [
    {
      "resolution": {
        "width": 1920,
        "height": 1080
      },
      "encoding": "H264",
      "bitrate": 5000,
      "uri": "rtsp://wb:[email protected]:8554/cam1"
    }
  ],
  "camera": {},
  "profiles": [
    {
      "$": {
        "token": "cam1",
        "fixed": true
      },
      "name": "cam1",
      "videoSourceConfiguration": {
        "$": {
          "token": "VideoSourceConfig_1"
        },
        "sourceToken": "VideoSource_1",
        "bounds": {
          "$": {
            "x": 0,
            "y": 0,
            "width": 1920,
            "height": 1080
          }
        }
      },
      "videoEncoderConfiguration": {
        "$": {
          "token": "VideoEncoderConfig_1"
        },
        "encoding": "H264",
        "resolution": {
          "width": 1920,
          "height": 1080
        },
        "rateControl": {
          "bitrateLimit": 5000
        }
      }
    }
  ]
}

Cam2

{
  "device": {
    "manufacturer": "Wyze Bridge",
    "model": "onvif Build [X86_64]",
    "firmwareVersion": "v2.10.3",
    "serialNumber": 0,
    "hardwareId": "X86_64 ONVIF BUILD [2024-10-02t15:44:22.474z] 44c1fc6"
  },
  "snapshotUri": "http://wbadmin:[email protected]:5000/snapshot/cam2.jpg",
  "streams": [
    {
      "resolution": {
        "width": 1920,
        "height": 1080
      },
      "encoding": "H264",
      "bitrate": 5000,
      "uri": "rtsp://wb:[email protected]:8554/cam2"
    }
  ],
  "camera": {},
  "profiles": [
    {
      "$": {
        "token": "cam2",
        "fixed": true
      },
      "name": "cam2",
      "videoSourceConfiguration": {
        "$": {
          "token": "VideoSourceConfig_1"
        },
        "sourceToken": "VideoSource_1",
        "bounds": {
          "$": {
            "x": 0,
            "y": 0,
            "width": 1920,
            "height": 1080
          }
        }
      },
      "videoEncoderConfiguration": {
        "$": {
          "token": "VideoEncoderConfig_1"
        },
        "encoding": "H264",
        "resolution": {
          "width": 1920,
          "height": 1080
        },
        "rateControl": {
          "bitrateLimit": 5000
        }
      }
    }
  ]
}

Since the NVR remembers the full path to the correct device after the initial registration, it doesn't matter if Wyze bridge is advertising another camera on ONVIF later.

@Nayruden Nayruden I'm wondering if anything has changed in Protect. When I try to add a new camera with a new hostname (but pointing to the same IP:Port, It just updates the first one as if it fully resolves the dns to the IP and sees them as the same device.

@Nayruden I was actually just about to post about this when I saw @daxiang28 already had. I've been working on getting this fully up and running since last night and ran into the same issue daxiang did. I was able to use the FILTER_NAMES tag you mentioned to isolate one of my two cams within wyze-bridge and adopted it using one of my wyze-bridge FQDNs in the Unifi portal, then swapped out the name filter to show only my second cam and mapped it by FQDN as well, but rather than a second camera appearing, it just updated the hostname listed in the "IP address" column of the first cam I added.

@MNewman
Copy link

MNewman commented Dec 10, 2024

Finally set up a local DNS server to try this technique and am also running into Protect just overwriting the settings of the previous camera when I add a new one with a different hostname.

Dumping the system migration data from Protect, in the camera definition JSON we can see a few extra values that the NVR is storing. You can see before and after adding a new camera it keeps the MAC address the same and also an 'ID' field down by the RTSP stream locations. My guess is it's clobbering the old camera based on matching mac address.

  {
    "mac": "90B11C8BC575",
    "host": "kitchen.cam",
    "connectionHost": null,
    "type": "Wyze Bridge onvif-hw Build [X86_64]",
    "name": "Back Porch",
    "upSince": null,
    "lastSeen": 1733857753333,
    "connectedSince": null,
    "lastDisconnect": null,
    "authToken": null,
    "hardwareRevision": null,
    "firmwareVersion": null,
    "firmwareBuild": null,
    "isUpdating": false,
    "isAdopting": false,
    "isAdopted": true,
    "isAdoptedByOther": false,
    "isProvisioned": true,
    "isSshEnabled": false,
    "fingerprint": null,
    "password": null,
    "guid": null,
    "anonymousDeviceId": null,
    "consoleId": null,
    "needForceAdopt": false,
    "lastMotion": null,
    "micVolume": 80,
    "isMicEnabled": true,
    "hdrMode": false,
    "videoMode": "default",
    "isProbingForWifi": false,
    "apMac": null,
    "apRssi": null,
    "elementInfo": null,
    "chimeDuration": 0,
    "lastRing": null,
    "activePatrolSlot": null,
    "useGlobal": false,
    "downScaleMode": 0,
    "isExtenderInstalledEver": false,
    "userConfiguredAp": false,
    "videoCodec": "h264",
    "videoCodecState": 0,
    "isThirdPartyCamera": true,
    "isPairedWithAiPort": false,
    "channels": [
      {
        "id": 0,
        "videoId": "video1",
        "name": "High",
        "enabled": true,
        "isRtspEnabled": false,
        "rtspAlias": null,
        "width": 1920,
        "height": 1080,
        "fps": null,
        "bitrate": 5000000,
        "minBitrate": null,
        "maxBitrate": null,
        "minClientAdaptiveBitRate": null,
        "minMotionAdaptiveBitRate": null,
        "fpsValues": [
          null
        ],
        "idrInterval": 5,
        "autoFps": false,
        "autoBitrate": false
      },
      {
        "id": 1,
        "videoId": "video2",
        "name": "Medium",
        "enabled": true,
        "isRtspEnabled": false,
        "rtspAlias": null,
        "width": 1920,
        "height": 1080,
        "fps": null,
        "bitrate": 5000000,
        "minBitrate": null,
        "maxBitrate": null,
        "minClientAdaptiveBitRate": null,
        "minMotionAdaptiveBitRate": null,
        "fpsValues": [
          null
        ],
        "idrInterval": 5,
        "autoFps": false,
        "autoBitrate": false
      },
      {
        "id": 2,
        "videoId": "video3",
        "name": "Low",
        "enabled": true,
        "isRtspEnabled": false,
        "rtspAlias": null,
        "width": 1920,
        "height": 1080,
        "fps": null,
        "bitrate": 5000000,
        "minBitrate": null,
        "maxBitrate": null,
        "minClientAdaptiveBitRate": null,
        "minMotionAdaptiveBitRate": null,
        "fpsValues": [
          null
        ],
        "idrInterval": 5,
        "autoFps": false,
        "autoBitrate": false
      }
    ],
    "ispSettings": {
      "aeMode": "auto",
      "irLedMode": "auto",
      "irLedLevel": 255,
      "wdr": 1,
      "icrSensitivity": 0,
      "icrSwitchMode": "sensitivity",
      "icrCustomValue": 2,
      "brightness": 50,
      "contrast": 50,
      "hue": 50,
      "saturation": 50,
      "sharpness": 50,
      "denoise": 50,
      "isColorNightVisionEnabled": false,
      "spotlightDuration": 15,
      "isFlippedVertical": false,
      "isFlippedHorizontal": false,
      "isAutoRotateEnabled": true,
      "isLdcEnabled": true,
      "is3dnrEnabled": true,
      "isExternalIrEnabled": false,
      "isAggressiveAntiFlickerEnabled": false,
      "isPauseMotionEnabled": false,
      "dZoomCenterX": 50,
      "dZoomCenterY": 50,
      "dZoomScale": 0,
      "dZoomStreamId": 4,
      "focusPosition": 0,
      "touchFocusX": null,
      "touchFocusY": null,
      "zoomPosition": 0,
      "mountPosition": null,
      "hdrMode": "normal"
    },
    "audioSettings": {
      "style": [
        "nature"
      ]
    },
    "talkbackSettings": {
      "typeFmt": "aac",
      "typeIn": "serverudp",
      "bindAddr": "0.0.0.0",
      "bindPort": 7004,
      "filterAddr": null,
      "filterPort": null,
      "channels": 1,
      "samplingRate": 22050,
      "bitsPerSample": 16,
      "quality": 100
    },
    "osdSettings": {
      "isNameEnabled": false,
      "isDateEnabled": false,
      "isLogoEnabled": true,
      "isDebugEnabled": false
    },
    "ledSettings": {
      "isEnabled": true,
      "blinkRate": 0
    },
    "speakerSettings": {
      "isEnabled": true,
      "areSystemSoundsEnabled": false,
      "volume": 80,
      "ringVolume": 80
    },
    "recordingSettings": {
      "prePaddingSecs": 2,
      "postPaddingSecs": 2,
      "smartDetectPrePaddingSecs": 2,
      "smartDetectPostPaddingSecs": 2,
      "minMotionEventTrigger": 1000,
      "endMotionEventDelay": 3000,
      "suppressIlluminationSurge": false,
      "_recordingEventOnly": "detections",
      "mode": "always",
      "_inScheduleMode": "always",
      "inScheduleMode": "always",
      "_outScheduleMode": "never",
      "outScheduleMode": "never",
      "geofencing": "off",
      "retentionDurationLQMs": null,
      "motionAlgorithm": "stable",
      "enableMotionDetection": true
    },
    "smartDetectSettings": {
      "objectTypes": [],
      "autoTrackingObjectTypes": [],
      "autoTrackingWithZoom": true,
      "audioTypes": [],
      "detectionRange": {
        "max": null,
        "min": null
      }
    },
    "recordingSchedulesV2": [],
    "motionZones": [],
    "privacyZones": [],
    "smartDetectZones": [],
    "smartDetectLines": [],
    "smartDetectLoiterZones": [],
    "stats": {
      "rxBytes": 0,
      "txBytes": 0,
      "wifi": {
        "channel": null,
        "frequency": null,
        "linkSpeedMbps": null,
        "signalQuality": 50,
        "signalStrength": 0
      },
      "video": {
        "recordingStart": 1729085024550,
        "recordingEnd": 1733856430350,
        "recordingStartLQ": 1729085025467,
        "recordingEndLQ": 1733090330000,
        "timelapseStart": 1729085025534,
        "timelapseEnd": 1733089997800,
        "timelapseStartLQ": 1729085025534,
        "timelapseEndLQ": 1733088812800
      },
      "lenses": []
    },
    "featureFlags": {
      "canAdjustIrLedLevel": false,
      "canMagicZoom": false,
      "canOpticalZoom": false,
      "canTouchFocus": false,
      "hasAccelerometer": false,
      "hasVerticalFlip": true,
      "hasAec": false,
      "hasBluetooth": false,
      "hasChime": false,
      "hasExternalIr": false,
      "hasIcrSensitivity": true,
      "hasInfrared": true,
      "hasLdc": false,
      "hasLedIr": false,
      "hasLedStatus": false,
      "hasLineIn": false,
      "hasMic": false,
      "hasPrivacyMask": true,
      "hasRtc": false,
      "hasSdCard": false,
      "hasSpeaker": false,
      "hasWifi": false,
      "hasHdr": false,
      "hasAutoICROnly": false,
      "videoModes": [],
      "videoModeMaxFps": [],
      "hasMotionZones": true,
      "hasLcdScreen": false,
      "hasFingerprintSensor": false,
      "mountPositions": [],
      "videoSourceCount": 1,
      "smartDetectTypes": [],
      "smartDetectAudioTypes": [],
      "supportDoorAccessConfig": false,
      "supportNfc": false,
      "supportLpDetectionWithoutVehicle": false,
      "lensType": null,
      "lensModel": null,
      "motionAlgorithms": [],
      "hasSquareEventThumbnail": false,
      "hasPackageCamera": false,
      "audio": [],
      "audioCodecs": [],
      "videoCodecs": [],
      "audioStyle": [],
      "isDoorbell": false,
      "isPtz": false,
      "hasColorLcdScreen": false,
      "hasLiveviewTracking": false,
      "hasLineCrossing": false,
      "hasLineCrossingCounting": false,
      "hasFlash": false,
      "flashRange": null,
      "hasLuxCheck": false,
      "presetTour": false,
      "hasEdgeRecording": false,
      "privacyMaskCapability": {
        "maxMasks": null,
        "rectangleOnly": false
      },
      "focus": {
        "steps": {
          "max": null,
          "min": null,
          "step": null
        },
        "degrees": {
          "max": null,
          "min": null,
          "step": null
        }
      },
      "pan": {
        "steps": {
          "max": null,
          "min": null,
          "step": null
        },
        "degrees": {
          "max": null,
          "min": null,
          "step": null
        }
      },
      "tilt": {
        "steps": {
          "max": null,
          "min": null,
          "step": null
        },
        "degrees": {
          "max": null,
          "min": null,
          "step": null
        }
      },
      "zoom": {
        "ratio": 22,
        "steps": {
          "max": null,
          "min": null,
          "step": null
        },
        "degrees": {
          "max": null,
          "min": null,
          "step": null
        }
      },
      "hotplug": {
        "audio": null,
        "video": null,
        "standaloneAdoption": false,
        "extender": {
          "isAttached": null,
          "hasFlash": null,
          "flashRange": null,
          "hasIR": null,
          "hasRadar": null,
          "radarRangeMax": null,
          "radarRangeMin": null
        }
      }
    },
    "lcdMessage": {},
    "streamSharing": {
      "enabled": false,
      "token": null,
      "shareLink": null,
      "expires": null,
      "sharedByUserId": null,
      "sharedByUser": null,
      "maxStreams": null
    },
    "homekitSettings": {
      "talkbackSettingsActive": false,
      "streamInProgress": false,
      "microphoneMuted": false,
      "speakerMuted": false
    },
    "shortcuts": [],
    "thirdPartyCameraInfo": {
      "port": "5000",
      "username": "wb",
      "password": "cam",
      "rtspUrl": "rtsp://kitchen.cam:8554/kitchen-cam-sub",
      "rtspUrlLQ": "rtsp://kitchen.cam:8554/kitchen-cam",
      "snapshotUrl": "http://kitchen.cam:5000/snapshot/kitchen-cam-sub.jpg?api=cam"
    },
    "fingerprintSettings": {
      "enable": false,
      "enablePrintLatency": false,
      "mode": "identify",
      "reportFingerTouch": false,
      "reportCaptureComplete": false
    },
    "nfcSettings": {
      "enableNfc": false,
      "supportThirdPartyCard": false
    },
    "id": "670fbe3b01fe2003e4000b38"
  },
`

`  {
    "mac": "90B11C8BC575",
    "host": "backporch.cam",
    "connectionHost": null,
    "type": "Wyze Bridge onvif-hw Build [X86_64]",
    "name": "Back Porch",
    "upSince": null,
    "lastSeen": 1733857611108,
    "connectedSince": null,
    "lastDisconnect": null,
    "authToken": null,
    "hardwareRevision": null,
    "firmwareVersion": null,
    "firmwareBuild": null,
    "isUpdating": false,
    "isAdopting": false,
    "isAdopted": true,
    "isAdoptedByOther": false,
    "isProvisioned": true,
    "isSshEnabled": false,
    "fingerprint": null,
    "password": null,
    "guid": null,
    "anonymousDeviceId": null,
    "consoleId": null,
    "needForceAdopt": false,
    "lastMotion": null,
    "micVolume": 80,
    "isMicEnabled": true,
    "hdrMode": false,
    "videoMode": "default",
    "isProbingForWifi": false,
    "apMac": null,
    "apRssi": null,
    "elementInfo": null,
    "chimeDuration": 0,
    "lastRing": null,
    "activePatrolSlot": null,
    "useGlobal": false,
    "downScaleMode": 0,
    "isExtenderInstalledEver": false,
    "userConfiguredAp": false,
    "videoCodec": "h264",
    "videoCodecState": 0,
    "isThirdPartyCamera": true,
    "isPairedWithAiPort": false,
    "channels": [
      {
        "id": 0,
        "videoId": "video1",
        "name": "High",
        "enabled": true,
        "isRtspEnabled": false,
        "rtspAlias": null,
        "width": 1920,
        "height": 1080,
        "fps": null,
        "bitrate": 5000000,
        "minBitrate": null,
        "maxBitrate": null,
        "minClientAdaptiveBitRate": null,
        "minMotionAdaptiveBitRate": null,
        "fpsValues": [
          null
        ],
        "idrInterval": 5,
        "autoFps": false,
        "autoBitrate": false
      },
      {
        "id": 1,
        "videoId": "video2",
        "name": "Medium",
        "enabled": true,
        "isRtspEnabled": false,
        "rtspAlias": null,
        "width": 1920,
        "height": 1080,
        "fps": null,
        "bitrate": 5000000,
        "minBitrate": null,
        "maxBitrate": null,
        "minClientAdaptiveBitRate": null,
        "minMotionAdaptiveBitRate": null,
        "fpsValues": [
          null
        ],
        "idrInterval": 5,
        "autoFps": false,
        "autoBitrate": false
      },
      {
        "id": 2,
        "videoId": "video3",
        "name": "Low",
        "enabled": true,
        "isRtspEnabled": false,
        "rtspAlias": null,
        "width": 1920,
        "height": 1080,
        "fps": null,
        "bitrate": 5000000,
        "minBitrate": null,
        "maxBitrate": null,
        "minClientAdaptiveBitRate": null,
        "minMotionAdaptiveBitRate": null,
        "fpsValues": [
          null
        ],
        "idrInterval": 5,
        "autoFps": false,
        "autoBitrate": false
      }
    ],
    "ispSettings": {
      "aeMode": "auto",
      "irLedMode": "auto",
      "irLedLevel": 255,
      "wdr": 1,
      "icrSensitivity": 0,
      "icrSwitchMode": "sensitivity",
      "icrCustomValue": 2,
      "brightness": 50,
      "contrast": 50,
      "hue": 50,
      "saturation": 50,
      "sharpness": 50,
      "denoise": 50,
      "isColorNightVisionEnabled": false,
      "spotlightDuration": 15,
      "isFlippedVertical": false,
      "isFlippedHorizontal": false,
      "isAutoRotateEnabled": true,
      "isLdcEnabled": true,
      "is3dnrEnabled": true,
      "isExternalIrEnabled": false,
      "isAggressiveAntiFlickerEnabled": false,
      "isPauseMotionEnabled": false,
      "dZoomCenterX": 50,
      "dZoomCenterY": 50,
      "dZoomScale": 0,
      "dZoomStreamId": 4,
      "focusPosition": 0,
      "touchFocusX": null,
      "touchFocusY": null,
      "zoomPosition": 0,
      "mountPosition": null,
      "hdrMode": "normal"
    },
    "audioSettings": {
      "style": [
        "nature"
      ]
    },
    "talkbackSettings": {
      "typeFmt": "aac",
      "typeIn": "serverudp",
      "bindAddr": "0.0.0.0",
      "bindPort": 7004,
      "filterAddr": null,
      "filterPort": null,
      "channels": 1,
      "samplingRate": 22050,
      "bitsPerSample": 16,
      "quality": 100
    },
    "osdSettings": {
      "isNameEnabled": false,
      "isDateEnabled": false,
      "isLogoEnabled": true,
      "isDebugEnabled": false
    },
    "ledSettings": {
      "isEnabled": true,
      "blinkRate": 0
    },
    "speakerSettings": {
      "isEnabled": true,
      "areSystemSoundsEnabled": false,
      "volume": 80,
      "ringVolume": 80
    },
    "recordingSettings": {
      "prePaddingSecs": 2,
      "postPaddingSecs": 2,
      "smartDetectPrePaddingSecs": 2,
      "smartDetectPostPaddingSecs": 2,
      "minMotionEventTrigger": 1000,
      "endMotionEventDelay": 3000,
      "suppressIlluminationSurge": false,
      "_recordingEventOnly": "detections",
      "mode": "always",
      "_inScheduleMode": "always",
      "inScheduleMode": "always",
      "_outScheduleMode": "never",
      "outScheduleMode": "never",
      "geofencing": "off",
      "retentionDurationLQMs": null,
      "motionAlgorithm": "stable",
      "enableMotionDetection": true
    },
    "smartDetectSettings": {
      "objectTypes": [],
      "autoTrackingObjectTypes": [],
      "autoTrackingWithZoom": true,
      "audioTypes": [],
      "detectionRange": {
        "max": null,
        "min": null
      }
    },
    "recordingSchedulesV2": [],
    "motionZones": [],
    "privacyZones": [],
    "smartDetectZones": [],
    "smartDetectLines": [],
    "smartDetectLoiterZones": [],
    "stats": {
      "rxBytes": 0,
      "txBytes": 0,
      "wifi": {
        "channel": null,
        "frequency": null,
        "linkSpeedMbps": null,
        "signalQuality": 50,
        "signalStrength": 0
      },
      "video": {
        "recordingStart": 1729085024550,
        "recordingEnd": 1733856430350,
        "recordingStartLQ": 1729085025467,
        "recordingEndLQ": 1733090330000,
        "timelapseStart": 1729085025534,
        "timelapseEnd": 1733089997800,
        "timelapseStartLQ": 1729085025534,
        "timelapseEndLQ": 1733088812800
      },
      "lenses": []
    },
    "featureFlags": {
      "canAdjustIrLedLevel": false,
      "canMagicZoom": false,
      "canOpticalZoom": false,
      "canTouchFocus": false,
      "hasAccelerometer": false,
      "hasVerticalFlip": true,
      "hasAec": false,
      "hasBluetooth": false,
      "hasChime": false,
      "hasExternalIr": false,
      "hasIcrSensitivity": true,
      "hasInfrared": true,
      "hasLdc": false,
      "hasLedIr": false,
      "hasLedStatus": false,
      "hasLineIn": false,
      "hasMic": false,
      "hasPrivacyMask": true,
      "hasRtc": false,
      "hasSdCard": false,
      "hasSpeaker": false,
      "hasWifi": false,
      "hasHdr": false,
      "hasAutoICROnly": false,
      "videoModes": [],
      "videoModeMaxFps": [],
      "hasMotionZones": true,
      "hasLcdScreen": false,
      "hasFingerprintSensor": false,
      "mountPositions": [],
      "videoSourceCount": 1,
      "smartDetectTypes": [],
      "smartDetectAudioTypes": [],
      "supportDoorAccessConfig": false,
      "supportNfc": false,
      "supportLpDetectionWithoutVehicle": false,
      "lensType": null,
      "lensModel": null,
      "motionAlgorithms": [],
      "hasSquareEventThumbnail": false,
      "hasPackageCamera": false,
      "audio": [],
      "audioCodecs": [],
      "videoCodecs": [],
      "audioStyle": [],
      "isDoorbell": false,
      "isPtz": false,
      "hasColorLcdScreen": false,
      "hasLiveviewTracking": false,
      "hasLineCrossing": false,
      "hasLineCrossingCounting": false,
      "hasFlash": false,
      "flashRange": null,
      "hasLuxCheck": false,
      "presetTour": false,
      "hasEdgeRecording": false,
      "privacyMaskCapability": {
        "maxMasks": null,
        "rectangleOnly": false
      },
      "focus": {
        "steps": {
          "max": null,
          "min": null,
          "step": null
        },
        "degrees": {
          "max": null,
          "min": null,
          "step": null
        }
      },
      "pan": {
        "steps": {
          "max": null,
          "min": null,
          "step": null
        },
        "degrees": {
          "max": null,
          "min": null,
          "step": null
        }
      },
      "tilt": {
        "steps": {
          "max": null,
          "min": null,
          "step": null
        },
        "degrees": {
          "max": null,
          "min": null,
          "step": null
        }
      },
      "zoom": {
        "ratio": 22,
        "steps": {
          "max": null,
          "min": null,
          "step": null
        },
        "degrees": {
          "max": null,
          "min": null,
          "step": null
        }
      },
      "hotplug": {
        "audio": null,
        "video": null,
        "standaloneAdoption": false,
        "extender": {
          "isAttached": null,
          "hasFlash": null,
          "flashRange": null,
          "hasIR": null,
          "hasRadar": null,
          "radarRangeMax": null,
          "radarRangeMin": null
        }
      }
    },
    "lcdMessage": {},
    "streamSharing": {
      "enabled": false,
      "token": null,
      "shareLink": null,
      "expires": null,
      "sharedByUserId": null,
      "sharedByUser": null,
      "maxStreams": null
    },
    "homekitSettings": {
      "talkbackSettingsActive": false,
      "streamInProgress": false,
      "microphoneMuted": false,
      "speakerMuted": false
    },
    "shortcuts": [],
    "thirdPartyCameraInfo": {
      "port": "5000",
      "username": "wb",
      "password": "cam",
      "rtspUrl": "rtsp://backporch.cam:8554/back-porch-sub",
      "rtspUrlLQ": "rtsp://backporch.cam:8554/back-porch",
      "snapshotUrl": "http://backporch.cam:5000/snapshot/back-porch-sub.jpg?api=cam"
    },
    "fingerprintSettings": {
      "enable": false,
      "enablePrintLatency": false,
      "mode": "identify",
      "reportFingerTouch": false,
      "reportCaptureComplete": false
    },
    "nfcSettings": {
      "enableNfc": false,
      "supportThirdPartyCard": false
    },
    "id": "670fbe3b01fe2003e4000b38"
  }

@Nayruden
Copy link

Nayruden commented Dec 10, 2024

My docker-wyze-bridge is running on a different subnet than my UNVR, so perhaps that's the difference? It wouldn't be able to capture the MAC since it's not L2 adjacent.

EDIT: Just to give more information...

UNVR is at 192.168.51.0/24
Cams are at 192.168.51.0/24 (Wouldn't matter since UNVR is not connecting to them directly)
docker-wyze-bridge is at 192.168.52.0/24

@MNewman
Copy link

MNewman commented Dec 10, 2024

@Nayruden Perhaps export your NVR settings via Settings->System->Migration->Download File and look at the cameras.json file to see if it's missing a MAC header? Also to see if the "id" field at the end of each camera definition is different.

@Nayruden
Copy link

Nayruden commented Dec 11, 2024

@MNewman is 90B11C8BC575 the actual MAC of the IP serving up your Wyze Bridge? For me, they're just random values, but they are unique for each camera...

Snippets of two cameras below...

  {
    "mac": "A6294009C422",
    "host": "cam1.wyze.my.house",
    "connectionHost": null,
    "type": "Wyze Bridge onvif Build [X86_64]",
    "name": "cam1"
}
  {
    "mac": "86171EC071C3",
    "host": "cam2.wyze.my.house",
    "connectionHost": null,
    "type": "Wyze Bridge onvif Build [X86_64]",
    "name": "cam2"
}

I double checked the neighbor list on the UNVR and confirmed that for me at least, the MACs listed above do not map to any device known by the UNVR.

@MNewman
Copy link

MNewman commented Dec 11, 2024 via email

@Nayruden
Copy link

Indeed that is the actual MAC of the IP hosting the wyze-bridge. Can Docker be configured to run the bridge on a separate virtual nic that obfuscates the mac address? Or would that be more a function of the actual network configuration? At present it's flat, no vlans, single subnet.

On Wed, Dec 11, 2024 at 8:02 AM Brett Smith @.> wrote: @MNewman https://github.com/MNewman is 90B11C8BC575 the actual MAC of the IP serving up your Wyze Bridge? For me, they're just random values, but they are unique for each camera... Snippets of two cameras below... { "mac": "A6294009C422", "host": "cam1.wyze.my.house", "connectionHost": null, "type": "Wyze Bridge onvif Build [X86_64]", "name": "cam1" } { "mac": "86171EC071C3", "host": "cam2.wyze.my.house", "connectionHost": null, "type": "Wyze Bridge onvif Build [X86_64]", "name": "cam2" } — Reply to this email directly, view it on GitHub <#1355 (comment)>, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABIIXR3VRBQ73JTZCXET4ET2FAZU7AVCNFSM6AAAAABOKQQ6DCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDKMZVHEZTAOBTHA . You are receiving this because you were mentioned.Message ID: @.>

If your network equipment supports VLANs, you can attach a docker container to a different VLAN by creating a sub-interface and then attaching the container to that interface (not as hard as it sounds as long as your network equipment supports VLAN tagging).

You could also try changing the MAC on your Docker machine's physical interface after you add each camera -- that would work as long as the UNVR never verifies existing MACs again.

@wr1williams
Copy link

Indeed that is the actual MAC of the IP hosting the wyze-bridge. Can Docker be configured to run the bridge on a separate virtual nic that obfuscates the mac address? Or would that be more a function of the actual network configuration? At present it's flat, no vlans, single subnet.

On Wed, Dec 11, 2024 at 8:02 AM Brett Smith @.> wrote: @MNewman https://github.com/MNewman is 90B11C8BC575 the actual MAC of the IP serving up your Wyze Bridge? For me, they're just random values, but they are unique for each camera... Snippets of two cameras below... { "mac": "A6294009C422", "host": "cam1.wyze.my.house", "connectionHost": null, "type": "Wyze Bridge onvif Build [X86_64]", "name": "cam1" } { "mac": "86171EC071C3", "host": "cam2.wyze.my.house", "connectionHost": null, "type": "Wyze Bridge onvif Build [X86_64]", "name": "cam2" } — Reply to this email directly, view it on GitHub <#1355 (comment)>, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABIIXR3VRBQ73JTZCXET4ET2FAZU7AVCNFSM6AAAAABOKQQ6DCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDKMZVHEZTAOBTHA . You are receiving this because you were mentioned.Message ID: _@**.**_>

If your network equipment supports VLANs, you can attach a docker container to a different VLAN by creating a sub-interface and then attaching the container to that interface (not as hard as it sounds as long as your network equipment supports VLAN tagging).

You could also try changing the MAC on your Docker machine's physical interface after you add each camera -- that would work as long as the UNVR never verifies existing MACs again.

I just tried your VLAN method (created new VLAN, tagged the container to route traffic through it and left the rest of the server alone on the main network) and still ran into the same issue since the MAC address showing for the cams is still the real physical address of the server that the container is running on. Did I misconfigure something on the VLAN in the Unifi console so that it's not obfuscating the MAC address?

@julioviegas
Copy link

Any chance of having this awesome branch merged into main? I cannot wait to use this from homeassistant addon setup. Otherwise, any possible way to fetch this branch instead of main when using homeassistant addon?

@daxiang28
Copy link

daxiang28 commented Dec 29, 2024

Stumbled on this but have yet to try it. Anybody try this with Protect?

https://www.reddit.com/r/wyzecam/s/xl39l6JXv8

@cra21k
Copy link

cra21k commented Dec 30, 2024

@mrlt8 @Nayruden

I tried configuring the onvif build with Frigate for PTZ control and auto tracking. Looks like the current onvif build does not list PTZ configuration.

I know we already have access to set a custom value for the camera Pan and tilt function using mqtt or rest api. Is this something that is being considered.

If not I'll try adding in the config in my local build and try setting up basic Pan tilt config.

@daxiang28
Copy link

I was able to get this to work on my Synology (via their native Container Manager app) using a Docker compose. The trick was the macvlan. Additionally, I did setup a custom DNS on my network for each camera that routed back to the IP address that was set with the ipv4_address.

version: "3.8"
services:
  wyze-bridge:
    image: mrlt8/wyze-bridge:onvif
    container_name: wyze-bridge
    environment:
      - PATH=/usr/local/bin:/usr/local/sb
      - LANG=C.UTF-8
      - GPG_KEY=#not sure what this is for
      - PYTHON_VERSION=3.12.7
      - PYTHONUNBUFFERED=1
      - FLASK_APP=frontend
      - BUILD=onvif
      - WYZE_EMAIL=########
      - WYZE_PASSWORD=#######
      - WB_AUTH=false
      - WS_DISCOVERY=true
      - FILTER_NAMES=Garage # Change this when adding another camera
    ports:
      - "8554:8554" # RTSP
      - "8888:8888" # HTTP API
      - "5050:5000" # Web UI and also the port that needs to be added to address of the camera in Unifi Protect
    mac_address: "02:42:ac:11:00:03"  # Change this when adding another camera
    volumes:
      - /volume2/docker/wyze_bridge:/config 
    restart: unless-stopped
    networks:
      custom_macvlan:
        ipv4_address: 192.168.1.85    

networks:
  custom_macvlan:
    driver: macvlan
    driver_opts:
      parent: eth0  # Replace with your Synology's network interface
    ipam:
      config:
        - subnet: 192.168.1.0/24  # Match your network
          gateway: 192.168.1.1    # Your router's gateway

@Jahnkeanater
Copy link

@daxiang28 I installed Thingino on a Wyze v2. I think there is a bug in protect that causes the camera to go offline every 10 seconds. The RTSP stream was stable though.

@iceman3k
Copy link

iceman3k commented Jan 8, 2025

I was able to get this to work on my Synology (via their native Container Manager app) using a Docker compose. The trick was the macvlan. Additionally, I did setup a custom DNS on my network for each camera that routed back to the IP address that was set with the ipv4_address.

version: "3.8"
services:
  wyze-bridge:
    image: mrlt8/wyze-bridge:onvif
    container_name: wyze-bridge
    environment:
      - PATH=/usr/local/bin:/usr/local/sb
      - LANG=C.UTF-8
      - GPG_KEY=#not sure what this is for
      - PYTHON_VERSION=3.12.7
      - PYTHONUNBUFFERED=1
      - FLASK_APP=frontend
      - BUILD=onvif
      - WYZE_EMAIL=########
      - WYZE_PASSWORD=#######
      - WB_AUTH=false
      - WS_DISCOVERY=true
      - FILTER_NAMES=Garage # Change this when adding another camera
    ports:
      - "8554:8554" # RTSP
      - "8888:8888" # HTTP API
      - "5050:5000" # Web UI and also the port that needs to be added to address of the camera in Unifi Protect
    mac_address: "02:42:ac:11:00:03"  # Change this when adding another camera
    volumes:
      - /volume2/docker/wyze_bridge:/config 
    restart: unless-stopped
    networks:
      custom_macvlan:
        ipv4_address: 192.168.1.85    

networks:
  custom_macvlan:
    driver: macvlan
    driver_opts:
      parent: eth0  # Replace with your Synology's network interface
    ipam:
      config:
        - subnet: 192.168.1.0/24  # Match your network
          gateway: 192.168.1.1    # Your router's gateway

Using this on a Virtualbox Ubuntu VM.
Why are you setting path? Why are you adding python versions in there? Are you creating new containers for each bridge and camera?

What credentials are you using to sign in via onvif? I am using Unifi protect.

I have not had to create a DNS entry yet, could you please provide an example of what you added for a DNS entry? When I use your macvlan method, I get onvif discovery popup on the unifi protect and a log saying [ONVIF] WS-Discovery enabled for 192.168.5.251:5000, however, I am unable to log in to the web interface. However, when I set network mode as host, I can log into the web interface, but onvif discovery disappears and shows in the logs as "[ONVIF] WS-Discovery enabled for 127.0.1.1:5000"

Update:

I was able to get this working for 1 camera. I did NOT create a DNS entry. I cannot figure out how to get this working for 2. The ports conflict for 127.0.1.1:5000 when trying to run multiple containers, I don't know where to change the port for that address or where wyze_bridge is pulling that IP from.

Perhaps use a IPvLAN instead of a macvlan? What was your reason one way or another?

I also do not have persistent data, even if I set up a volume with full 777 permissions. Some advice would be appreciated. I have to add the wyze credentials every time the container is restarted. Figured out the hard code of credentials in docker compose.

This is the config I used to get 1 Wyze Pan v3 working in Unifi Protect over ONVIF. I have another VM running docker on Hyper-V running this for another Pan v3.

Any chance for audio support support over ONVIF?

services:
  wyze-bridge:
    image: mrlt8/wyze-bridge:onvif
    container_name: wyze-bridge_kitchencam
    environment:
#      - PATH=/usr/local/bin:/usr/local/sb
#      - LANG=C.UTF-8
#      - GPG_KEY=#not sure what this is for
#      - PYTHON_VERSION=3.10.12
#      - PYTHONUNBUFFERED=1
#      - FLASK_APP=frontend
#      - BUILD=onvif
      - WYZE_EMAIL=***Your Info***
      - WYZE_PASSWORD=***Your Info***
      - WB_IP=192.168.5.250
      - WS_DISCOVERY=true # Keep true to announce Onvif discovery (if somehow this gets fixed to show host IP instead of 127.0.1.1 in network_mode: host)
      - FILTER_NAMES=KitchenCam # Change this when adding another camera
      # Onvif auth will be: [IP: "ip":5000] [User: wb] [Password: API Key at bottom of dashboard page].
      # WebUI and Stream authentication:
      - WB_AUTH=true # Set to 'true' to enable onvif login
      - WB_USERNAME=***Your Info*** # Whatever username you want. Onvif user will be 'wb'
      - WB_PASSWORD=***Your Info*** # Whatever password you want for web portal login. Onvif password will be 'API Key' at the bottom of the main page
    ports:
      - 1935:1935 # RTMP
      - 8554:8554 # RTSP
      - 8888:8888 # HLS
      - 8889:8889 #WebRTC
      - 8189:8189/udp # WebRTC/ICE
#      - 5000:5000 # WEB-UI
      - 5050:5000 # Web UI and also the port that needs to be added to address of the camera in Unifi Protect
#    mac_address: 02:42:ac:11:00:03  # Change this when adding another camera. NEEDED to only show 1 camera per Onvif
    restart: unless-stopped
    network_mode: host
#    networks:
#      custom_macvlan:
#        ipv4_address: 192.168.5.251

#networks:
#  custom_macvlan:
#    driver: macvlan
#    driver_opts:
#     parent: enp0s3  # Replace with your Synology's network interface
#    ipam:
#      config:
#        - subnet: 192.168.5.0/24  # Match your network
#          gateway: 192.168.5.1    # Your router's gateway

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests