Custom Component: Unifi Protect

Great feedback Taz. I am moving to a new house in 3 weeks, and first there I will get my Cameras in their final positions, so I look forward to take some of your learnings and implement them.
/B

It’s the least I can do after all your efforts on this component.

Hopefully I’ll be able to refine my filters further, and I’ll update if I do.

1 Like

Version 0.1.2

This will most likely be the last release for a few weeks, as I am moving to a new house - I have tons of new devices that I will be installing - looking forward to that :rofl:

Plans for the future is to add code so that this component can be installed from the Integrations page, thus avoiding editing of configuration.yaml. The work has startet, but the next few weeks I will be busy with work, and preparing the move.

If you find any errors, I will try to fix them ASAP.

What is changed in V0.1.2:

  • New: Added a new Switch component. If enabled the system will create a switch for each camera, that can enable or disable the motion recording. You can already do this by running the services camera.enable_motion_detection and camera.disable_motion_detection but this gives a convenient way to do it from the Frontend. See the Github README.md for setup instructions.

  • Change: Cleaned up some unused code

2 Likes

Thanks for keeping up the good work :slight_smile:

Go’ flytning og pøj pøj med det nye hus (danish: I wish you a good move and the best of luck with your new house :+1:)

1 Like

Thank you for this. I am now using it in my HA setup with Protect.

Good luck with the house move - and thanks for the hard work.

Part #2 of my refining of driveway camera notifications.

To cope with passing foot traffic on the path / shadows from cars during daytime, and headlights at night all triggering my notifications I’ve:

  • added some basic tracking of the length of the motion events using an input_datetime
  • tightened up the motion_score filtering in my automations

This lets me only notify users based on a high motion score and / or length of motion event (I have a stealthy postman who only scores 40 when delivering to the house but takes over 10 seconds to do so, making him an ideal test scenario :smile:).
Hopefully the automations are self-explanatory.

# record start of driveway motion 
- id: unifiprotect_driveway_motion_started
  alias: "Driveway Motion Started"
  trigger: 
    platform: state
    entity_id: binary_sensor.unifiprotect_motion_driveway
    from: "off"
    to: "on"
  condition: []
  action:
    - service: input_datetime.set_datetime
      data_template:
        entity_id: input_datetime.driveway_motion_started
        time: "{{ (as_timestamp(now()) | timestamp_custom('%H:%M:%S', true)) }}"
        date: "{{ (as_timestamp(now()) | timestamp_custom('%Y-%m-%d', true)) }}"

# movement on driveway signalled via unifiprotect - filtered by motion score attribute
# daylight only, minimum score 60
- 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: "{{ ((is_state('sun.sun', 'above_horizon') and (state_attr('binary_sensor.unifiprotect_motion_driveway', 'motion_score') | int >= 60))) }}"
  action:
    - service: camera.snapshot
      data_template:
        entity_id: camera.driveway
        filename: >
          {% if state_attr('automation.driveway_motion_detected', 'last_triggered') != None %}
             {{ states('sensor.snapshot_local') }}driveway_{{ now().strftime('%Y%m%d%H%M') }}.jpg
          {% else %}
             {{ states('sensor.snapshot_local') }}driveway_{{ as_timestamp(state_attr('automation.driveway_motion_detected', 'last_triggered')) | timestamp_custom('%Y%m%d%H%M%S%f') }}.jpg
          {% endif %}
    - service: notify.all_mobile
      data_template:
        title: "Driveway Alert"
        message: "Motion detected at {{ now().strftime('%H:%M') }}. Score: {{ state_attr('binary_sensor.unifiprotect_motion_driveway', 'motion_score') }}"
        data:
          android:
            notification:
              image: >
                {% if state_attr('automation.driveway_motion_detected', 'last_triggered') != None %}
                   {{ states('sensor.snapshot_remote') }}driveway_{{ now().strftime('%Y%m%d%H%M') }}.jpg
                {% else %}
                   {{ states('sensor.snapshot_remote') }}driveway_{{ as_timestamp(state_attr('automation.driveway_motion_detected', 'last_triggered')) | timestamp_custom('%Y%m%d%H%M%S%f') }}.jpg
                {% endif %}

# long movement with lower score on driveway signalled via unifiprotect - filtered by motion score attribute and length
# minimum length 10 seconds (+/- 3 seconds scan time) and score >= 10 (but less than 60 reporting)
# daylight: score >= 10 and < 60 (ie. not already reported when trigger 'on' 
# night: score >= 50 (no night trigger due to security lights being triggered)
- id: unifiprotect_driveway_long_motion_notification
  alias: "Driveway Long Motion Detected"
  hide_entity: True
  trigger:
    platform: state
    entity_id: binary_sensor.unifiprotect_motion_driveway
    from: "on"
    to: "off"
  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: "{{ ((is_state('sun.sun', 'above_horizon') and (state_attr('binary_sensor.unifiprotect_motion_driveway', 'motion_score') | int < 60))) or (is_state('sun.sun', 'below_horizon')) }}"
      - condition: template
        value_template: >
          {{ ((is_state('sun.sun', 'above_horizon') and (state_attr('binary_sensor.unifiprotect_motion_driveway', 'motion_score') | int >= 10)) or 
              (is_state('sun.sun', 'below_horizon') and (state_attr('binary_sensor.unifiprotect_motion_driveway', 'motion_score') | int >= 50))) }}
      - condition: template
        value_template: "{{ (as_timestamp(now()) -  as_timestamp(states('input_datetime.driveway_motion_started')) | int >= 10) }}"
  action:
    - service: unifiprotect.save_thumbnail_image
      data_template:
        entity_id: camera.driveway
        filename: >
          {% if state_attr('automation.driveway_long_motion_detected', 'last_triggered') != None %}
             {{ states('sensor.snapshot_local') }}driveway_{{ now().strftime('%Y%m%d%H%M') }}.jpg
          {% else %}
             {{ states('sensor.snapshot_local') }}driveway_{{ as_timestamp(state_attr('automation.driveway_long_motion_detected', 'last_triggered')) | timestamp_custom('%Y%m%d%H%M%S%f') }}.jpg
          {% endif %}
    - service: notify.all_mobile
      data_template:
        title: "Driveway Alert"
        message: >
           Motion detected at {{ as_timestamp(states('input_datetime.driveway_motion_started')) | timestamp_custom('%H:%M') }}. 
           Score: {{ state_attr('binary_sensor.unifiprotect_motion_driveway', 'motion_score') }}, Length: {{ (as_timestamp(now()) -  as_timestamp(states('input_datetime.driveway_motion_started'))) | int }}s
        data:
          android:
            notification:
              image: >
                {% if state_attr('automation.driveway_long_motion_detected', 'last_triggered') != None %}
                   {{ states('sensor.snapshot_remote') }}driveway_{{ now().strftime('%Y%m%d%H%M') }}.jpg
                {% else %}
                   {{ states('sensor.snapshot_remote') }}driveway_{{ as_timestamp(state_attr('automation.driveway_long_motion_detected' ,'last_triggered')) | timestamp_custom('%Y%m%d%H%M%S%f') }}.jpg
                {% endif %}
1 Like

is it normal that it’s off sync? if i wave my arms in front my camera it can take up to 15 sec before it shows up on my HA

If you read previous entries in this thread, this seems to be normal when using stream. I don’t believe this is specific to this component. I am not sure how really to optimize this for now. I believe the delay you see is dependent on the HW Home Assistent is running on, nut even with the powerfull platform I run on, I see this delay when using an RTSP stream - just not 15 sec.

New Release V0.1.3

Between putting everything in to boxes and preparing for the move, I found a little time to make some changes.
As per discussion on Github, I made some changes to the component. The prime purpose of this, is to make it possible to set both recording modes (Always and Motion) from Home Assistant. Please see below for how this is done. Note that there is a breaking change on the Switch component, where there is a name change on the previous switch.
Ideally I would have made a Dropdown box, from where you could select the recording mode (Like the input_select component), but at the moment I cannot figure out how to do that, so I made two Switches instead.

  • Breaking Change The Switch component has changed due to implementation of a new Switch. The previous switch name switch.unifiprotect_recording_[camera Name] does no longer exist. It has been replaced by two new switches switch.unifiprotect_record_motion_[Camera Name] and switch.unifiprotect_record_always_[Camera Name].
    The first sets recording mode to motion and the second sets recording mode to always. They are mutually exclusive, so turning on one, will turn off the other.
  • New: Added new Service unifiprotect.set_recording_mode. When called, this service sets the recording mode for the chosen camera to the specified recording_mode. The value can be always, motion or never. The mode always, will let the camera record all the time, motion is the usual motion recording and never disables all recording.
  • Change: Cleaned up some unused code
1 Like

Mmm I have a script, which I run to turn recording to ‘always’. Only fires the first camera, camera.garden

sequence:
  service: unifiprotect.set_recording_mode
  data:
    entity_id: 
      - camera.garden
      - camera.front
      - camera.driveway
    recording_mode: always

Have I done something wrong?

1 Like

You are close. In order to perform the same action on several entities, you need to create a call to the service pr camera. So you script should look like:

sequence:
  - data:
      recording_mode: always
    entity_id: camera.garden
    service: unifiprotect.set_recording_mode
  - data:
      recording_mode: always
    entity_id: camera.front
    service: unifiprotect.set_recording_mode
  - data:
      recording_mode: always
    entity_id: camera.driveway
    service: unifiprotect.set_recording_mode

I think I have seen a smarter way to do that, but I can’t remember right now. But the above works.

1 Like

Awesome component, I was able to remove all my generic camara streams and just use this. Running all the latest versions when I attempt to add the new switch option I get the following error when checking the config. When I disable the switch configuration the config validates fine.

Platform error switch.unifiprotect - cannot import name ‘TYPE_RECORD_ALLWAYS’ from ‘custom_components.unifiprotect’ (/config/custom_components/unifiprotect/init.py)

Hi Jared,
Glad to hear that it is usefull. With regards to the error you see, can you tell me how you installed it? Manually or via HACS?
I was just checking my own installation, with the latest V0.1.3, and here I don’t get that error.

If you installed via HACS, could you try to remove it, and then install again. Just to make sure you don’t have something old in cache. Remember to restart Home Assistant.

Let me know how this goes.

Installed via HACS.

I uninstalled and reinstalled and everything works now, should have tried the old ‘reboot’ prior to posting :slight_smile:

Thanks again, good luck with the move!

1 Like

Hi @briis, I’m getting the following exception when I try to run this custom component via HACS on homeassistant 105.0 as a container:

Error during setup of component unifiprotect
Traceback (most recent call last):
File “/usr/src/homeassistant/homeassistant/setup.py”, line 174, in _async_setup_component
component.setup, hass, processed_config # type: ignore
File “/usr/local/lib/python3.7/concurrent/futures/thread.py”, line 57, in run
result = self.fn(*self.args, **self.kwargs)
File “/config/custom_components/unifiprotect/init.py”, line 111, in setup
host, port, username, password, use_ssl, minimum_score
File “/config/custom_components/unifiprotect/unifi_protect_server.py”, line 44, in init
self._api_auth_bearer_token = self._get_api_auth_bearer_token()
File “/config/custom_components/unifiprotect/unifi_protect_server.py”, line 74, in _get_api_auth_bearer_token
raise NvrError(“Request failed: %s” % response.status)
AttributeError: ‘Response’ object has no attribute ‘status’

Any idea on why this is happening?

I’m using a UDM Pro to run UniFi Protect, so I wonder if there’s something weird with how Protect runs on there. My Protect version is 1.14.7

The UDMP has introduced the new UnifiOS operating system, and that uses a new Authentication which this component does not support. As UDMP is currently only shipped in the US, and I am based in Europe I have no access to this, so it is not possible to directly support it.
BUT the good news is that @Silvenga has made a proxy that you can use to get authentication working. Please have a look here for setup instructions

1 Like

Any suggestions for reducing the amount of notifications you get with fog? I have the usual problem with UniFi Protect, it was foggy overnight and I was swamped with 300+ notifications. I guess I could put a condition on it not being foggy :slight_smile: but has anyone got any cooler suggestions?

Thanks

Here is my code already (stolen from some code above)

#Notify When Motion Detected on Driveway
alias: 'Driveway Motion Notification'
initial_state: true
trigger:
  - platform: state
    entity_id: binary_sensor.unifiprotect_motion_driveway
    from: 'off'
    to: 'on'
condition:
  condition: and
  conditions:
    - condition: template # only notify once every 2 minute(s) at most
      value_template: "{{ ( as_timestamp(now()) - as_timestamp(state_attr('automation.driveway_motion_notification', 'last_triggered')) |int(0) ) > 120 }}"
    - condition: template # notify differently with sun is up or down
      value_template: >
        {{ ((is_state('sun.sun', 'above_horizon') and (state_attr('binary_sensor.unifiprotect_motion_driveway', 'motion_score') | int >= 60)) or 
            (is_state('sun.sun', 'below_horizon') and (state_attr('binary_sensor.unifiprotect_motion_driveway', 'motion_score') | int >= 40))) }}
    - condition: or
      conditions:
        - condition: state #check someone is at home
          entity_id: input_boolean.family_home
          state: 'off'
        - condition: state # check bedtime
          entity_id: input_boolean.bedtime
          state: 'on'
action:
  - service: camera.snapshot
    data:
      entity_id: camera.driveway
      filename: /config/tmp/driveway.jpg
  - delay: 00:00:05
  - service: notify.telegram
    data:
      message: "Driveway motion detected at {{ now().strftime('%H:%M') }}. Score: {{ state_attr('binary_sensor.unifiprotect_motion_driveway', 'motion_score') }}"
  - service: notify.telegram
    data:
      title: Send Unifi image
      message: "Unifi motion detected"
      data:
        photo:
          - file: /config/tmp/driveway.jpg
#            caption: Driveway motion detected

I might not have the best solution, but here is at least what I did.

I was having so many issues setting the motion detection on these cameras, especially because it is not possible to filter on objects (not to my knowledge). So I got these Xiaomi sensors, which are placed pretty close to the cameras. When both Sensor and Camera is Triggered it then fires a Notification. That has brought down the number of Notifications dramatically. I have tested it, since I record always and it actually seems throughout a day, I only get Notifications when there actually is movement by a car / person.

Finnaly I have tweaken my automation, so it starts a timer and then it asks for a picture every 20th second, while the motion sensors are still active. When the motion detectors switches to off, the timers stops and reset.

Maybe it doesn’t make sense, but at least it works very well for me :slight_smile:

Saw this and immediately had to install it! My Unifi Protect system now feels as though part of my automation system! No motion in the basement, turn off the lights and wall heater! No need to buy more Aqara Motion sensors or dig my heels into the Frigate/Coral setup. Dude … @briis … let me know how I can buy you a coffee! Thank you

1 Like