Custom Component: Unifi Protect

Hi @briis,
I’m sorry but I didn’t upgraded yet since last time so I’m very late in terms of both HA & UniFi Protect custom component upgrades :frowning:

  • My current version of HA is : 0.108.9
  • My current version of UniFi Protect is : 0.1.7

Can you please confirm that there is no risk for me to upgrade to the latest versions ? I use your excellent custom component to be able to see my baby girl in her bedroom through HA, and I don’t want to break something.
Is my upgrade plan correct:

  • Replace all the files in the unifiprotect directory - version 0.3.4
  • Restart HA
  • Upgrade HA to version 0.110.1

Thank you!

0.108.9 is a long time ago :rofl: but 0.3.4 of unifiprotect should work with that version. The process you describe is correct.
Hope it all works out for you.
/B

1 Like

Upgraded succesfully to 0.3.4 and HA 0.110.1 and still have access to my G3 Flex streams ! Thanks again for your work @briis ! :slight_smile:

1 Like

Have you figured this out yet? I’m having the same issue when accessing streams through the Home app.

Hi @briis,

I’ve upgraded to HA 0.110.2 (just now on .3) from 0.109.6 with 0.3.4 of the component installed via HACs.

Since upgrading HA I’ve noticed that the event_score attribute does not seem to be refreshed in the same way as before.

I was using the event_score to filter events to reduce notifications from passers by with a condition.

- id: unifiprotect_driveway_daytime_motion_notification
  alias: "Driveway Daytime Motion Detected"
  trigger:
    platform: state
    entity_id: binary_sensor.unifiprotect_motion_driveway
    from: "off"
    to: "on"
    for:
      seconds: 5
  condition:
    condition: and
    conditions:
      - condition: state
        entity_id: input_boolean.notify_driveway
        state: "on"
      - condition: state
        entity_id: input_boolean.holiday_mode
        state: "off"
      - condition: state
        entity_id: input_boolean.notify_via_sms
        state: "off"
      - condition: template
        value_template: "{{ ((is_state('sun.sun', 'above_horizon') and (state_attr('binary_sensor.unifiprotect_motion_driveway', 'event_score') | int >= 50))) }}"
  action:
    - service: notify.all_mobile
      data_template:
        title: "Driveway Alert"
        message: "Motion detected at {{ now().strftime('%H:%M') }}. Day Score: {{ state_attr('binary_sensor.unifiprotect_motion_driveway', 'event_score') }}"
        data:
          image: "/api/camera_proxy/camera.driveway"
          actions:
            - action: "URI"
              title: "View Live Feed"
              uri: "/lovelace/camera_driveway"

This automation suddenly stopped notifying following the HA upgrade despite qualifying events, so I reduced the event_score threshold in the condition to 0.

This then allowed the automation to trigger and execute successfully, however the notification supplies an event_score of 0. When I check the binary_sensor state attributes in Developer Tools I can see a valid event_score after the fact. My guess is that the event_score is being updated on completion of the motion event (i.e. after my automation has fired).

Is this a side-effect of the processing changes for 0.110.x, or have I missed something here?

I’d appreciate any guidance you (or anyone else) can provide.

Hey @briis

Just confirmed this on that beta version from yesterday too. event_score is null on all my cameras, EXCEPT the doorbell motion sensor has a timestamp in it:
event_score: 2020-05-25 14:28:51

Hope that helps? Thanks!

1 Like

Edit: I’m actually gonna nuke this post since I’m on a different version than you, and I’m not sure how yours is written.

I did send it to Briis, so he can update it as needed!

A few Unifi Protect firmware releases ago, the event_score started to behave differently than before the updates. When I first did this, the event_score was set when the motion event had finished, but now it is set somewhere in the beginning of the event and then set back to zero when the event is done.

So where I previously could just fetch the score when the event ended, I now need to get it in the absolute right moment, before it returns to zero. The way the event log is read is by polling it every 2 seconds, so now I might actually miss when there is a value in here.

I will flag this as an outstanding issue, and do some testing to see if I can get back to a reliable method of fetching the value.

1 Like

Hey @briis

It looks like the “event” itself stores the score (as “score”). And I believe that’s how it’s pulling now if I’m not mistaken? This is a recent event from Postman:

    {
        "type": "motion",
        "start": 1590522958426,
        "end": 1590522975664,
        "camera": "1234",
        **"score": 91,**
        "id": "1234",
        "metadata": {
            "objectType": null,
            "objectCoords": null,
            "objectConfidence": null
        },
        "modelKey": "event",
        "partition": null,
        "thumbnail": "1234",
        "heatmap": "1234"
    },

So I think it’s working as expected with the pull request I just sent over :slight_smile: My event_score attribute is still filled in from the last event. So maybe Ubiquiti changed it back?

Thanks!

1 Like

You are absolutely right @thegame3202 - My post above was absolute rubbish, it is the complete opposite. (I had to many changes done over the last few weeks).

Back in mid April it was changed in the Unifi Protect firmware, so that the score was stored when THE EVENT ENDED where before that time it came right away, and as such it could be used to only trigger motion when we were above a certain threshold. That option I had to remove in V0.16, as it did not work anymore. So sorry for the confusion.

@TazUk: Maybe you could change your automation so that it first triggers when the motion sensor goes from on to off. Then you should have access to the motion score attribute?

I did consider this when I was fiddling around with it, however I’m interested in obtaining notifications about significant movement whilst that movement is going on - e.g. someone calling at the house (rather than walking past). I’ve been using a combination of length of ongoing motion and scoring to achieve this and provide a snapshot and an action link to a camera live view (hidden view on dashboard using a picture-entity which seems to lag behind real-time by 15 seconds, which I gather is the card rather than the stream) as part of the notification. Essentially I’m aiming to put a better layer of intelligence over the top of the native Protect featureset.

Triggering on completion of the motion event would:

  • require much more in terms of calculation to determine the length of a motion event
  • potentially miss the actual event with the camera proxy api (perhaps I could use the thumbnail image in place of the api - if that’s available immediately at the end of the event).

I think I’m going to have to look again at how I currently handle notifications to meet my use case but I’m currently lacking ideas.

And I will see if there is anything I can do with the event stream to give you this data earlier in the process.

If feasible that would be awesome, but mine is a rather odd use case I’m sure so perhaps I need to take a step back and try something different. It’ll give me something to do during my week of annual leave during the UK lockdown.

1 Like

Messing with alternative options at the moment.

Unfortunately the thumbnail is not available at the point motion ends, giving a 404 response to the service call.

2020-05-28 12:50:26 ERROR (MainThread) [homeassistant.components.automation] Driveway Daytime Motion Ended Detected: Error executing script. Unexpected error for call_service at pos 1: Thumbnail Request failed: 404 - Reason: Not Found
Traceback (most recent call last):
  File "/srv/homeassistant/lib/python3.7/site-packages/homeassistant/helpers/script.py", line 153, in _async_step
    self, f"_async_{cv.determine_script_action(self._action)}_step"
  File "/srv/homeassistant/lib/python3.7/site-packages/homeassistant/helpers/script.py", line 623, in _async_call_service_step
    *self._prep_call_service_step(), blocking=True, context=self._context
  File "/srv/homeassistant/lib/python3.7/site-packages/homeassistant/core.py", line 1253, in async_call
    task.result()
  File "/srv/homeassistant/lib/python3.7/site-packages/homeassistant/core.py", line 1288, in _execute_service
    await handler.func(service_call)
  File "/home/homeassistant/.homeassistant/custom_components/unifiprotect/__init__.py", line 160, in async_save_thumbnail
    await async_handle_save_thumbnail_service(hass, call)
  File "/home/homeassistant/.homeassistant/custom_components/unifiprotect/__init__.py", line 258, in async_handle_save_thumbnail_service
    await _write_thumbnail(camera_id, filename, image_width)
  File "/home/homeassistant/.homeassistant/custom_components/unifiprotect/__init__.py", line 244, in _write_thumbnail
    camera_id, image_width
  File "/home/homeassistant/.homeassistant/custom_components/unifiprotect/unifi_protect_server.py", line 362, in get_thumbnail
    f"Thumbnail Request failed: {response.status} - Reason: {response.reason}"
custom_components.unifiprotect.unifi_protect_server.NvrError: Thumbnail Request failed: 404 - Reason: Not Found

So currently I can either continue as is without the scoring filter, or grab an image after the motion event finishes without the reason for the motion shown. I could (I suppose) delay the service call for a few seconds, but that would rather defeat the purpose.

If anyone has any better ideas I’m all ears - test automation below for reference.

- id: unifiprotect_driveway_daytime_motion_ended_notification
  alias: "Driveway Daytime Motion Ended Detected"
  trigger:
    platform: state
    entity_id: binary_sensor.unifiprotect_motion_driveway
    from: "on"
    to: "off"
  condition:
    condition: and
    conditions:
      - condition: state
        entity_id: input_boolean.notify_driveway
        state: "on"
      - condition: state
        entity_id: input_boolean.holiday_mode
        state: "off"
      - condition: state
        entity_id: input_boolean.notify_via_sms
        state: "off"
      - condition: template
        value_template: >
           {{ ((is_state('sun.sun', 'above_horizon') and (state_attr('binary_sensor.unifiprotect_motion_driveway', 'event_score') | int >= 50))) 
                or ((is_state('sun.sun', 'below_horizon') and (state_attr('binary_sensor.unifiprotect_motion_driveway', 'event_score') | int >= 50))) }}
      - condition: template
        value_template: >
           {{ (is_state('sun.sun', 'above_horizon') and (as_timestamp(now()) - as_timestamp(state_attr('camera.driveway', 'last_tripped_time')) | int >= 6))
               or (is_state('sun.sun', 'below_horizon') and (as_timestamp(now()) - as_timestamp(state_attr('camera.driveway', 'last_tripped_time')) | int >= 12)) }}
  action:
    - service: unifiprotect.save_thumbnail_image
      data_template:
        entity_id: camera.driveway
        filename: "{{ states('sensor.snapshot_local') }}driveway_lastmotion.jpg"
    - service: notify.mobile_app_turk
      data_template:
        title: "Driveway Alert"
        message: >
               Driveway camera motioned detected at {{ as_timestamp(state_attr('camera.driveway', 'last_tripped_time')) | timestamp_custom('%H:%M') }}.{{ '\n' -}}
               Score: {{ state_attr('binary_sensor.unifiprotect_motion_driveway', 'event_score') }}.{{ '\n' -}}
               {% set time = (as_timestamp(now()) - as_timestamp(state_attr('camera.driveway', 'last_tripped_time'))) | int  %}
               {% set mins = ((time % 3600) / 60) | int %}
               {% set secs = time - (mins * 60) %}
               {% if time < 60 %}Length: {{ secs }}s 
               {% else %}Length: {{ mins }}m {{ secs }}s
               {% endif %}
        data:
          image: "{{ states('sensor.snapshot_remote') }}driveway_lastmotion.jpg"
          actions:
            - action: "URI"
              title: "View Live Feed"
              uri: "/lovelace/camera_driveway"

I’ve found that with a small delay after the binary_sensor returns to ‘off’ I’m able to retrieve the thumbnail for the last motion event successfully (so far at least).

I’m leveraging the ‘last_tripped_time’ in my automation and attempting to calculate the length of the event, which is complicated both by the automation’s deliberate delay and the potential delay in parsing the logs by the component.

With that in mind, a quick question for @briis - is it feasible to return a ‘last_tripped_length’ or similar as part of the camera entity’s state attributes?
If this could be returned it would give a more accurate picture of the last captured event, and (selfishly) I could use it as a simpler condition to ignore the odd blips caused by sudden light changes, passing cars, etc. It would also let me simplify the automation and remove the rudimentary length calculation block in the message section.

A note for anyone looking to reuse below: the image_width is scaled for Pixel 3 phones and the source camera is a UVC-G3-AF and may require tuning for other phones and / or cameras.

- id: unifiprotect_driveway_daytime_motion_ended_notification
  alias: "Driveway Daytime Motion Ended Detected"
  trigger:
    platform: state
    entity_id: binary_sensor.unifiprotect_motion_driveway
    from: "on"
    to: "off"
  condition:
    condition: and
    conditions:
      - condition: state
        entity_id: input_boolean.notify_driveway
        state: "on"
      - condition: state
        entity_id: input_boolean.holiday_mode
        state: "off"
      - condition: state
        entity_id: input_boolean.notify_via_sms
        state: "off"
      - condition: template
        value_template: >
           {{ ((is_state('sun.sun', 'above_horizon') and (state_attr('binary_sensor.unifiprotect_motion_driveway', 'event_score') | int >= 50)))
                or ((is_state('sun.sun', 'below_horizon') and (state_attr('binary_sensor.unifiprotect_motion_driveway', 'event_score') | int >= 50))) }}
      - condition: template
        value_template: >
           {{ (is_state('sun.sun', 'above_horizon') and (as_timestamp(now()) - as_timestamp(state_attr('camera.driveway', 'last_tripped_time')) | int >= 7))
               or (is_state('sun.sun', 'below_horizon') and (as_timestamp(now()) - as_timestamp(state_attr('camera.driveway', 'last_tripped_time')) | int >= 12)) }}
  action:
    - delay: "00:00:03"
    - service: unifiprotect.save_thumbnail_image
      data_template:
        entity_id: camera.driveway
        filename: "{{ states('sensor.snapshot_local') }}driveway_motion.jpg"
        image_width: 1920
    - service: notify.all_mobile
      data_template:
        title: "Driveway Alert"
        message: >
               Driveway camera motioned detected at {{ as_timestamp(state_attr('camera.driveway', 'last_tripped_time')) | timestamp_custom('%H:%M') }}.{{ '\n' -}}
               Score: {{ state_attr('binary_sensor.unifiprotect_motion_driveway', 'event_score') }}.{{ '\n' -}}
               {% set time = ((as_timestamp(now()) - as_timestamp(state_attr('camera.driveway', 'last_tripped_time'))) | int) - 3 %}
               {% set mins = ((time % 3600) / 60) | int %}
               {% set secs = time - (mins * 60) %}
               {% if time < 60 %}Length: {{ secs }}s
               {% else %}Length: {{ mins }}m {{ secs }}s
               {% endif %}
        data:
          image: "{{ states('sensor.snapshot_remote') }}driveway_motion.jpg"
          actions:
            - action: "URI"
              title: "View Live Feed"
              uri: "/lovelace/camera_driveway"

EDIT:

Just to correct myself, it seems that even a (now) 5 second delay may be insufficient to account for Protect’s creation of the last motion thumbnail.
I’m seeing occasional 404 errors in my logs and (with my current automation) being sent the previous motion event.
I’ll have to come at this from a different angle I think.

Hey @briis, my cams stopped working with a recent update, so I’m just getting caught back up on this thread.

I saw you could now add via the settings gui, so I tried that, and it did work. But it created all my cameras with a _2, because the existing entities still existed (stupid me).

I uninstalled via hacs, removed the folder in custom_config, and took out the various references to protect in my switches, sensors, binary sensors, and cameras portions of my config and rebooted.

Then I went back to hacs and found the newest beta2 version, and installed that and rebooted. But unifi protect does not show up in my settings / integrations gui, do you know why? Do you recommend I install manually? Am I correct to assume that all the manual entries in configuration.yaml will no longer be required with your new flow?

Thanks!

Hi @VinistoisR
Just to be sure.

Then try to add the the Integration from the Integrations Page.

And YES, no references to unifiprotect in your yaml files.

Huzzah, that worked fine. That’s a much easier setup! Thanks!

I am glad it worked out for you.
Within the next week, this will be the official release that will go in to HACS. I just need to iron out some minor things, and then it will be released.

1 Like

No problem, now you can delete most of your instructions, and hopefully most of your tech support along with it right??

I had some automations that relied on the binary sensors and had to take the unifiprotect_ portion out of the sensor names for them to work again.