[PROJECT] Driver for Unifi Protect Controllers

Thanks! I have revised the code on my side to do some more checks before that. That section of code was basically because some websocket data lacks an ID I was used to... so I made a workaround but apparently even then it sometimes does not pass all the data needed.

Either way... letting it "stew" on my hub for a few hours to see if the errors happen anymore (I was able to see it on mine) before I publish the update.

Updated Version(s):

  • UnifiProtectAPI.groovy = 0.2.27

Change(s):

  • Additional null checking being performed on websocket data. Thanks @mjarends for letting me know about the errors occurring. I no longer get any on my Hubitat but if anyone else still sees them after updating, let me know.

If I add a new camera, how do I import it into Hubitat?

It should automatically add new devices. You can run the GetProtectInfo or Refresh to trigger it manually.

If it does not add it, there should be messaging in the log (an error) about why not.

If you do not spot anything, set the logging to Trace, run the GetProtectInfo command, and send me a message with the logged response from that.

What type of camera have you added by the way?

@snell First of all thanks for this project and the work you have put into this!

I just started testing with a G4 instant and was trying to get the correct smartDetecTypes (e.g. person) to show up.

I noticed that the data received from the websocket can have two types of "modelKey" in the actionPaylad: event or camera. If the modelKey is camera than the "id" record matches the camera, however if it's an event the id record is different and it's actually the "recordId" that matches.

As originally coded "modelKey" with "event" would result in a "null" Device and not get parsed properly under the "smartDetecTypes" key.

I'm not a groovy expert, but this is the part of the code that I changed to make it work for me.

def parse( String description ){
    def Data = DecodeWebSocket( description )
    if( Data != null ){
        Logging( "WebSocket Data = ${ Data }", 4 )
        def modelKey = Data.actionPacket.actionPayload.modelKey
        def EventID
        def TempID
        if (modelKey == "event") {
            TempID = Data.actionPacket.actionPayload.recordId
            if( TempID.indexOf( "-" ) != -1 ){
                TempID = Data.actionPacket.actionPayload.recordId.split( "-" )
                EventID = TempID[ 0 ]
                TempID = TempID[ 1 ]
            } else {
                TempID = Data.actionPacket.actionPayload.recordId
                EventID = TempID
            }
        } else {
            TempID = Data.actionPacket.actionPayload.id    
            if( TempID.indexOf( "-" ) != -1 ){
                TempID = Data.actionPacket.actionPayload.id.split( "-" )
                EventID = TempID[ 0 ]
                TempID = TempID[ 1 ]
            } else {
                TempID = Data.actionPacket.actionPayload.id
                EventID = TempID
            }
        } 
       def Device
.....

@blocklanders:
I think you must be looking at a fairly old one or something based off of an older version of one of my drivers (since I have never seen someone use the Logging method like I do, so this is likely from one of mine regardless). My parse is not structured like that in the current (0.2.27) version published.

In the current one, modelKey is part of DecodeWebSocket (~line 570 to 580) and those are pretty close to @tomw's as well (since I use his WebSocket methods with tweaks for additional devices and such).

I'm actually looking at 0.2.27. The code in my previous post is the change I made to make it work

Below is the part of your code in 0.2.27, that I modified. Here TempID is always set with Data.actionPacket.actionPayload.id. If modelKey is event, instead of camera, TempID should be set to Data.actionPacket.actionPayload.recordId, otherwise Data.actionPacket.actionPayload.id provides the wrong Camera ID (at least for me).

Anyways, I'm not sure if this is only specific to me (running protect on UDMPRO SE with a G4 Instant) but I'm now able to receive the smartDetectTypes correctly. Just wanted to pass it along.

def parse( String description ){
    def Data = DecodeWebSocket( description )
    if( Data != null ){
        Logging( "WebSocket Data = ${ Data }", 4 )
        def TempID = Data.actionPacket.actionPayload.id
        def EventID
        if( TempID.indexOf( "-" ) != -1 ){
            TempID = Data.actionPacket.actionPayload.id.split( "-" )
            EventID = TempID[ 0 ]
            TempID = TempID[ 1 ]
        } else {
            TempID = Data.actionPacket.actionPayload.id
            EventID = TempID
        }
        def Device

Ok, that makes more sense... but I will have to check it out. This area has been revised a few times due to distinctions between events, cameras, and lights (yes, for some reason the floodlight has it's own modelKey).

This is a real problem spot for me because I do not own any cameras that can do Smart Detection. I only have a G3 Instant, Floodlight, and Sensor... Oh well.

I put your code in place on mine and will see how it works for tonight and part of tomorrow. If I do not see any bugs crop up with my weird devices I will publish it. Thanks for letting me know!

1 Like

For any future changes, if you need me to do some testing for smart detection with my G4 Instant, don't hesitate.

1 Like

Hey folks, I'm brand new to the platform and home automation. I have gotten so far as to install the drivers etc and add some cameras as "devices" in Hubitat. Now what? lol

Sorry, I'm new and dont really understand how the integration works as it pertains to Unifi Protect!

Thanks in advance for the guidance!

It is really just for some basic monitoring and control. For example, you can trigger a picture from the camera and have that on your dashboard. It also allows for the sensors to be read and used. For example, my G3 Instant can let me know if there is motion and it also lets me know if it is light or dark in the area. If I wanted, I could put a Rule to do something if one of those two changed.

It also can deal with some of the other devices like the Multi-Sensor or the Floodlight. In the case of the Floodlight it can also turn it on/off and control the brightness.

Of course, there are two different Unifi Protect drivers available. Mine and @tomw's. They are very similar in capabilities but do have some differences.

Updated Version(s):

  • UnifiProtectAPI.groovy = 0.2.29

Change(s):

  • Change to how the CSRF value is received when a login attempt is made. In version 3.2.8 of the Unifi OS they changed the header's case... Yes, something as simple as changing it from X-CSRF-Token to X-Csrf-Token broke it. So the device was not actually capturing the latest CSRF so actions could no longer work. I was recoding it when @tomw provided a better method of changing it's case and comparing it to that. So now, even if they change it to x-CsRf-ToKeN (or any combination thereof) it will still work.
  • Some additional returned data fields were already in my working version so those were included as well.
1 Like

I was trying to write a rule using the button device type for the doorbell and I wasn't able to get the trigger event to work in Rule Machine when watching for button 1 pushed events. I was also not seeing the events in the logs. Instead I used the "Button Presses" custom variable to trigger when my doorbell is ringing.

I wanted to check if this was expected and/or if anyone else is experiencing this.

I'm running the following:
Hubitat OS: 2.3.7.139
Hubitat Protect driver: 0.2.29
Doorbell driver: 0.1.7

Also I am seeing the following errors post upgrade to Hubitat OS 2.3.7.XXX. These were not present prior to this release. In the screen captures below, device 1344 is the Hubitat Protect parent. Events were streaming correctly before the error started to occur.

The following is coming from the UDM pro device:

The following is coming from the Doorbell device:

The following is coming from a Camera device:

@mjarends:

  1. If the Button Presses is incrementing, then the pushed event should be just before it (it is the event right before). I even made it a "forced" event that should be sent every time rather than checking if it matches the current value. If you set the doorbell child to Trace logging (not the parent device in this case) it should show up in the logs. It would have the same value though (button 1 has been pushed) so I do not think Rules that involve "changed" or such would work (since the value never changes). I do not have a doorbell myself though so I cannot check it, but happy to try to help figure this out. Can you take a screenshot of your Rule?

  2. I am not sure what is going on with the errors... in the past, Hubitat's "triggers" for usage warnings were easily hit with the Unifi API drivers (this one and the Network one) because of how much data the API sends back that needs to be processed and such. BUT... ProcessEvent is an INCREDIBLY simple function I use on ALL of my drivers and has barely changed in years. It deals with a single event (and whether it triggers the sendEvent or not). So there is no way it, itself, would be causing the hub load. The only thing I can think is if the device is being sent a ton of events and it is bogging down trying to handle them? The line numbers do not make sense either as they do not "line up" with ProcessEvent for the API, Child, Bridge, or Camera drivers (in case any of those were being used). The Doorbell one (line 245) does match up... to when it is actually posting the sendEvent.
    I am not sure what could trigger this and I am not seeing it on my system (but maybe mine has far less stuff and do not have a doorbell, although I do have a camera, sensor, and floodlight). Maybe there is a lot of WebSocket activity happening? I will ask the Hubitat team if there was something that changed that might trigger this (and what it really means).

EDIT:

  • One thing I did see in other posts (in the Beta area) for 2.3.7 was to try restarting the hub if excessive hub load shows up.
  • You could also try checking the memory/cpu usage to see how your hub is doing.

What does the Log - Device stats page look like? Can you post a screen snapshot of the top 10 (more of possible) devices and the header right above the devices list?

This is the screen capture I grabbed when the issue was occurring. The top two devices are related to snell's Hubitat Protect device. I also have tomw's version of the Hubitat Protect running which is the Unifi Protect Controller instance that shows up in the list.

After reporting this I did remove snell's version of Hubitat Protect and recreate it. I haven't seen the LimitExceededException since.

  1. I do see the button 1 pushed event getting logged but I do not see a corresponding event in the Event log (see first screen capture). Here's the test rule I have showing my trigger event (see 2nd screen capture). When I use the Button Presses attribute the rule does work.

  1. Not sure if the two events are related but I did use the Take command to grab an image at roughly the same time this issue occurred.

Also to give some additional context on the steps I took before reporting the issue. I performed each of the steps below and after each I saw the error persist. After posting this I did recreate the devices and I no longer see the errors.

  • Rebooted
  • Stopped and pulled the plug
  • Performed a soft reset/reboot

I also want to note that I am experiencing another issue with the driver where the devices fail to record events until I update their preferences. This issue was occurring before the Hubitat OS update and seems to be happening after a reboot. I can see the parent Hubitat Protect device receiving web socket events and logging them, but I do not see the events being created for the respective devices until I update a device's preferences.

You mean you need to Save Preferences on the individual child devices before they work? I have NEVER seen that. I am not sure how that would even happen because the parent device is handling all the work for when data is received. Is there a particular type of event that is not being saved or is it all?

Can you tell me what versions of Unifi OS and Protect you are using? Not sure that will help me figure it out at all but...

On a different point, you were running mine and @tomw's at the same time... Could there be any chance that might be a factor? I do not run @tomw's on mine obviously... but it seems like it would be problematic to have two drivers trying to get all the same data and trying to receive the WebSocket info...

My observation is that UniFi OS and the various applications (Protect, Network) allow multiple simultaneous websocket connections. So I wouldn't expect that to be directly related to missing events - it wouldn't be the case that only one connection was receiving the raw events for example.

Now, processing everything twice could potentially contribute to elevated HE hub load. Whether or not that could be related to any specific issue would require some more investigation.

1 Like