Custom Component: Unifi Protect

Does anyone know if there is a way to see how many people are currently watching your camera streams live via Unifi Protect clients/website? If so, that would be a great sensor.

Hi Blake,
I have not come across that when looking through the raw data that we can pull from Unifi Protect. It does not mean that it does not exist, but as you know the API is not documented.
I could imagine it would be hard to do, as the Live Stream can come from many sources. There is the RTSP stream we are using in this component, but there is also the APP/Website itself. But maybe Ubiquiti has found a way to do that, but currently they are not telling us.

1 Like

Thanks for all the hard work on this @briis (and the impressively fast iterative releases).

I spent some time yesterday stripping out my MotionEye bridge between HA and Protect yesterday, dropping the Protect component into my setup via HACS and itā€™s working a treat.

1 Like

Does anyone know if it is somehow possible to improve the delay that are on the rtsp stream.
Currently Iā€™ve added a automation that turns on my Google Nest Hub and shows me an rtsp stream of the camera that got Triggered. Unfortunately the stream is always between 20 and 30 seconds behind, meaning that the trigger gets fired as it should, but the feed shows me an empty driveway or entrance because the person already left :disappointed_relieved:

Hi Yoinkz
I did some testing on this, and even though I run HA on a powerfull NUC I also see this delay. If I stream directly from VLC it still has a delay, just not as big as when streaming through HA.
However, an automation like the below, actually works for me. Even with the delay, it shows me the action that went on. But I donā€™t know if this works for you.

- id: 'cast_when_motion'
  alias: Unifi Protect - Cast on Motion
  description: ''
  trigger:
  - entity_id: binary_sensor.unifiprotect_motion_hallway
    from: 'on'
    platform: state
    to: 'off'
  condition: []
  action:
  - data:
      entity_id: camera.hallway
      media_player: media_player.cast_livingroom
    service: camera.play_stream
  - delay: 00:01:00
  - entity_id: media_player.cast_livingroom
    service: media_player.turn_off
1 Like

I tested streaming this morning and it (subjectively) seems to take the same amount of time as when I had the camera setup as a generic IP camera.

Worth noting that the release notes on the latest Protect (Android) app mention speed of connection improvements are coming with controller 1.13 (not yet released I think) - itā€™s possible that controller may improve things in this scenario.

New Release Version 0.1.0

In this release you will not see a lot of new functionality. There has been a lot of change in the backend of the system as this release introduces a complete new Unifi Protect Server Wrapper. The previous implementation had a lot of calls to the Protect Server, but with this new wrapper, I have drastically reduced those calls, as all data is now stored in a memory dataset, and the wrapper just updates this dataset. With that in place we can now call an update from HASS more often, without overstretching the Protect Server. The update of the dataset is run as an Async event from the main loop. So after this update, you can delete the file protectnvr.py, this has been replaced by unifi_protect_server.py (You can just leave it where it is, but it is no longer used)

  • New Items:

    • Added new Configuration Option minimum_score. (int)(Optional) Minimum Score of Motion Event before motion is triggered. Integer between 0 and 100. Default is 0, and with that value, this option is ignored
  • Changes:

    • A lot of changes to all modules, due to new Unifi Protect Wrapper.

Iā€™m excited to give this a whirl at the weekend as I need additional filtering for some notifications.
Perhaps you can indulge a couple of questions in the meantime:
Is minimum_score per controller or per camera?
Is there a way of returning the Unifi assigned score with a motion event to help with tuning the score?

For now the minimum score is per controller, but if you find it usefull I will try and set it up pr. Camera. I maybe should note that as the API is not documented I found that this number is in the event log, and it seems to be related to ā€˜qualityā€™ of the Motion event. Something like, the higher the number, the closer the object is to the camera. But I will be happy to get your feedback on how this works.

I will add the motion score to the Attributes of the Binary Sensor, then you can pull it from there. On this sensor, the score will be pr. camera. It is not in this release, but I will have it done before the weekend.

I could certainly benefit from that for our particular setup if feasible.

You sir, are a gentleman and a scholar - that would be great but only if it doesnā€™t cause you any hassle.

Iā€™ll certainly provide any feedback I can.

For context, our setup is as follows:

  1. G3 Flex inside garage to rear of property - any motion event (unless my SO or I are in the garage or entering / exiting) is a notification. Iā€™m filtering out expected motion events with a combination of Unifi device_tracker attributes based on the garage WiFi access point MAC and whether the door open / close automations have been triggered).
  2. G3 bullet mounted to the house overlooking the property frontage - this is the more problematic one as I need to capture any motion event in high quality (we regularly have people drive into parts of the property, cause damage and attempt to get away without paying for it :roll_eyes:).
    This camera generates a lot of motion events from shadows, passing vehicles and foot traffic that I would like to try and filter out using the motion score.
    In the past I have requested that Unifi provide notifications configurable at a more granular level based on motion zones, so your minimum_score addition is a massive plus for us.
    We both work full time, so Iā€™m planning on incrementing the minimum_score gradually based on triggered notifications, and will pass the score through in the notification during my fine-tuning. This feature is a massive plus for us as Unifi have ignored my feature request for notifications configured to a granular level using the motion zones.

Is there a way of attaching the snapshot picture to a notification ?

I have the following automation but it would be great if a picture could be sent with it.

#Notify When Motion Detected at Front Door
- alias: 'Front Door Motion Notification'
  initial_state: true
  trigger:
    platform: state
    entity_id: binary_sensor.unifiprotect_motion_front_door
    from: 'off'
    to: 'on'
  condition:
    - condition: template # only notify once every 5 minutes at most
      value_template: "{{ ( as_timestamp(now()) - as_timestamp(state_attr('automation.front_door_motion_notification', 'last_triggered')) |int(0) ) > 1800 }}"
  action:
    - service: notify.all
      data:
        title: "Home Assistant"
        data:
          type: announce  
        message: 'Motion Detected at Front Door'
    - service: script.audio_notify
      data_template:
        tts_msg: "I think somoene is at the Front Door"

Thanks in advance

Hi Andrew,
Something like the below will attach a snapshot, and send a notify with that snapshot. I am sure you can customize to your specific needs:

- id: '1579603059080'
  alias: Send snapshot on motion
  description: ''
  trigger:
  - entity_id: binary_sensor.unifiprotect_motion_front_door
    platform: state
    to: 'on'
  condition: []
  action:
  - data:
      filename: /config/www/snapshot.jpg
    entity_id: camera.front_door
    service: camera.snapshot
  - delay: 00:00:01
  - data:
      data:
        attachment:
          content-type: jpg
          hide-thumbnail: false
          url: http://<ip_address>:8123/local/snapshot.jpg
      message: The Camera has detected motion at the front door
      title: Someone is at the door
    service: notify.mobile_app_iphone
2 Likes

Version 0.1.1

Minor update

  • New:
    • Added a new attribute to each binary_sensor, called motion_score. This displayes the score of the last motion recording.

With this release the actual motion_score pr. motion event is now visible. As said it is added as an Attribute to each of the binary sensors. You could create a Template Sensor like the below to have it visible on the Frontend for testing.

  - platform: template
    sensors:
      motion_score_office:
        friendly_name: Motion Score Office
        value_template: '{{ state_attr("binary_sensor.unifiprotect_motion_office", "motion_score") }}'

Different cameras might need different scores to work correct, so I will start working on a way to implement that. But in the meantime, please share if this makes any difference in the quality of detection and triggering automations.
Please note This is just one metric, most of the quality of motion detection, should be done in Unifi Protect, using Zones and sensitivity.

1 Like

Awesome, thank you.

Duly Noted.

So if I add a non-default score item to my configuration, e.g.:

unifiprotect:
  host: !secret unifi_host
  username: !secret unifi_usr
  password: !secret unifi_pwd
  minimum_score: 1

Then I can append the returned score value to my motion notifications to fine tune, e.g.

- id: unifiprotect_driveway_motion_notification
  alias: 'Driveway Motion Detected'
  hide_entity: True
  trigger:
    platform: state
    entity_id: binary_sensor.unifiprotect_motion_driveway
    from: 'off'
    to: 'on'
  condition:
    condition: and
    conditions:
      - condition: state
        entity_id: input_boolean.driveway_notify
        state: 'on'
      - condition: state
        entity_id: input_boolean.holiday_mode
        state: 'off'
      - condition: state
        entity_id: input_boolean.notify_via_sms
        state: 'off'
  action:
    - service: camera.snapshot
      data_template:
        entity_id: camera.driveway
        filename: >
          {% if (states.automation.driveway_motion_detected.attributes.last_triggered is defined) %}
             {{ states("sensor.snapshot_local") }}driveway_{{ now().strftime("%Y%m%d%H%M") }}.jpg
          {% else %}
             {{ states("sensor.snapshot_local") }}driveway_{{ as_timestamp(states.automation.driveway_motion_detected.attributes.last_triggered) | timestamp_custom("%Y%m%d%H%M%S%f") }}.jpg
          {% endif %}
    - service: notify.all_mobile
      data_template:
        title: "Driveway Alert"
        message: 'Movement detected on the driveway at {{ now().strftime("%H:%M") }}. [Score: {{ state_attr("binary_sensor.unifiprotect_motion_driveway", "motion_score")]'
        data:
          android:
            notification:
              image: >
                {% if (states.automation.driveway_motion_detected.attributes.last_triggered is defined) %}
                   {{ states("sensor.snapshot_remote") }}driveway_{{ now().strftime("%Y%m%d%H%M") }}.jpg
                {% else %}
                   {{ states("sensor.snapshot_remote") }}driveway_{{ as_timestamp(states.automation.driveway_motion_detected.attributes.last_triggered) | timestamp_custom("%Y%m%d%H%M%S%f") }}.jpg
                {% endif %}

Nice! - Excited to give it a whirl.

Yes, but technically, you donā€™t need to add the minimum_score. The score the Unifi Protect returns is between 0 and 100. I have designed the integration such that if you enter a minimum_score > 0 then the system will only trigger the binary_sensor if there is motion AND the score is > than the minimum score.

By NOT setting the minimum_score, the binary_sensor will always return motion, BUT then you can now check the motion_score attribute in your automation, and then only perform the Action if that is above a certain value. That actually gives you a possibility to set different scores pr. Camera - I did not even think about that before :smile:

So you could check that state_attr("binary_sensor.unifiprotect_motion_driveway", "motion_score") is greather than a specific value in your Condition and only proceed if that is True

Does this make sense?

Ah yes, thank you.

My longer term plan would be to only fire the automation for > X minimum score per camera.
Iā€™m still thinking out the easiest way to find the appropriate level (particularly for the driveway) as the majority of excessive notifications occur during the working week.
Return the score in the notification will let me figure out the scoring by comparing notification against recorded footage via the cloud controller connection.

In your Automation, you could use something like the below as a Condition, to filter out anything that has a motion_score smaller than a specific value. Experiment with the value to filter out unwanted triggers.

  - condition: template
    value_template: '{{ state_attr("binary_sensor.unifiprotect_motion_driveway", "motion_score")
      > 50 }}'
1 Like

Right got the motion sensor working, it sends the notifications and on my phone the HA IOS app attaches a picture which is pretty cool.

Only problem now is it sends an email as i would expect but the email doesnā€™t have the picture attached, either inline or as an attachment, its just the text.

If i can figure out how to get the picture in the email notification its job done !

#Notify When Motion Detected at Front Door
- alias: 'Front Door Motion Notification'
  initial_state: true
  trigger:
    platform: state
    entity_id: binary_sensor.unifiprotect_motion_front_door
    from: 'off'
    to: 'on'
  condition:
    - condition: template # only notify once every 2 minutes at most
      value_template: "{{ ( as_timestamp(now()) - as_timestamp(state_attr('automation.front_door_motion_notification', 'last_triggered')) |int(0) ) > 120 }}"
  action:
    - service: camera.snapshot
      data:
        entity_id: camera.front_door
        filename: /config/www/snapshot.jpg
    - delay: 00:00:01
    - service: notify.all
      data:
        title: "Home Assistant"
        data:
          type: announce
          icon: mdi:bell
          attachment:
            content-type: jpg
            hide-thumbnail: false
            url: https://<<ipaddress>>/local/snapshot.jpg
        message: 'Someone is at the Front Door'
    - service: script.audio_notify
      data_template:
        tts_msg: "I think someone is at the Front Door"

It is not clear from your automation how you send email, but if you look at the bottom of the documentation for the SMTP Integration, there is an example on how to attach an image.

Iā€™ve been monitoring the minimum score since Friday evening and now have some (preliminary) feedback.

Iā€™ve been focusing primarily on filtering notifications from the driveway camera as this (through necessity) triggers more easily based on fleeting motion events that Iā€™m not really interested in being notified about (but do require recordings of).

For context, a crude representation of the configured motion zones is below, showing the sensitivity set in each motion zone in the Unifi camera configuration and the position of the security lighting and camera.

Layout

Seeing the score per motion event notification has enabled me to match it to the recorded camera footage in the controller. Based on this, I believe that the score returned is a calculated value based on speed of motion and the size of object (i.e. the number of pixels changing in the image).
Some examples:

  • Person moving right to left along path during daylight hours footage of 6 seconds = 55 score.
  • Car pulling away from edge of road left to right during daylight hours footage of 5 seconds = 80 score.
  • 2 people entering driveway during daylight hours from lhs heading towards camera over 30 seconds = 78
  • Person running right to left along path during daylight hours footage of 5 secodns = 80 score

For the moment Iā€™ve now implemented a minimum score of 60 for the driveway notifications, although I may need to factor in sun.elevation as after dusk our (very sensitive) security lighting comes into play, as do passing headlights.

For completeness, my automation is below.
Note that this delivers notifications to android mobiles via a notification group and using the following elements:

  • input_boolean.drivewa_notify: toggled based on sunrise / time or sunset with a UI override.
  • input_boolean.holiday_mode: centralised flag to control system behaviour when away
  • input_boolean.notify_via_sms: automatic ā€˜offlineā€™ limiting and routing of notifications
  • sensor.snapshot_local: configuration item holding internal network image path
  • sensor.snapshot_remote: configuration item holding nabu casa image path
# movement on driveway signalled via unifiprotect - filtered by motion score attribute
- id: unifiprotect_driveway_motion_notification
  alias: "Driveway Motion Detected"
  hide_entity: True
  trigger:
    platform: state
    entity_id: binary_sensor.unifiprotect_motion_driveway
    from: "off"
    to: "on"
  condition:
    condition: and
    conditions:
      - condition: state
        entity_id: input_boolean.driveway_notify
        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: "{{ state_attr('binary_sensor.unifiprotect_motion_driveway', 'motion_score') | int >= 60 }}"
  action:
    - service: camera.snapshot
      data_template:
        entity_id: camera.driveway
        filename: >
          {% if (states.automation.driveway_motion_detected.attributes.last_triggered is defined) %}
             {{ states('sensor.snapshot_local') }}driveway_{{ now().strftime('%Y%m%d%H%M') }}.jpg
          {% else %}
             {{ states('sensor.snapshot_local') }}driveway_{{ as_timestamp(states.automation.driveway_motion_detected.attributes.last_triggered) | timestamp_custom('%Y%m%d%H%M%S%f') }}.jpg
          {% endif %}
    - service: notify.all_mobile
      data_template:
        title: "Driveway Alert"
        message: "Movement detected on the driveway at {{ now().strftime('%H:%M') }}. [ {{state_attr('binary_sensor.unifiprotect_motion_driveway', 'motion_score') }}]"
        data:
          android:
            notification:
              image: >
                {% if (states.automation.driveway_motion_detected.attributes.last_triggered is defined) %}
                   {{ states('sensor.snapshot_remote') }}driveway_{{ now().strftime('%Y%m%d%H%M') }}.jpg
                {% else %}
                   {{ states('sensor.snapshot_remote') }}driveway_{{ as_timestamp(states.automation.driveway_motion_detected.attributes.last_triggered) | timestamp_custom('%Y%m%d%H%M%S%f') }}.jpg
                {% endif %}
1 Like