Custom Component: Unifi Protect

I don’t have this issue. Just last night I updated my UDMP to 1.9.0-10, which basically reboots the whole system, and once it was back up, the Integration just picked up again - which it is also designed to do. Yes I do get some errors in the Log file, but that is understandable as there is no connection to the UDMP.
I am curious to know if other people have the same issue as @areks?

I do not have thus issue.

For me the only way to make it work is restarting HA.

Wow, just love this integration! @briis, not only are you one of the best cyclists in the world you have also done an amazing job on this :wink: Thanks a lot for all your work, this component truly makes a difference for me!

Can I just ask if anyone has any feedback to give on the following:

  1. Image that is saved get’s strange proportions, looks like it is streched out. I have configured my G4 with the code below and use it in an automation to send a snapshot. I only use the smart detection of the camera.

  2. Sometimes it misses to save the image. Example: I went out for a walk, when I left I got a notification but not when I returned, but I was captured in UniFi Protect so the camera triggered. Hard to debug, I know, just wanted to ehar if anyone else has experienced the same.

  3. Has anyone found a good solution in an automation to have a dynamic file name? As it is now with my code the image is overwritten everytime there is a new motion. I would like to write the file with a unique filename adn then, when sending the notification, use the same file name. Probably not too hard to do but haven’t succeeded so far :slight_smile:

  4. Finally, what mode in the automations do you use for queuing the automation jobs for sending notification with an image (single, restart, queued, parallel)?

My code:

- id: '1612642603691'
  alias: KAMERA - G4 Garageuppfarten
  description: Notis med bild
  trigger:
  - entity_id: binary_sensor.motion_g4_garageuppfart
    platform: state
    to: 'on'
  condition: []
  action:
  - delay: '10'
  - service: unifiprotect.save_thumbnail_image
    data:
      entity_id: camera.g4_garageuppfarten
      filename: /config/www/camera_g4_garageuppfarten.png
      image_width: 2688
  - service: notify.mobile_app_pixel_5
    data:
      message: Rörelse på garageuppfarten
      data:
        image: https://xxx.duckdns.org/local/camera_g4_garageuppfarten.png
  mode: single

Thanks, and I do actually cycle :slight_smile:
Let me try and answer your questions:

The snapshot feature has a fixed size, so I recommend you use the camera.snapshot service instead, that will also solve your question 2, as thumbnails are only generated once the motion has completed, so there is a delay and that is why you see the inconsistencies.

You could create a Script that does this, and then in the beginning of the script define a variable with a date_time syntaks, like this: "video_{{ (as_timestamp(now()) | timmestamp_custom('%Y_%m_%d_%H_%M_%S', true)) }}"

And then use that variable later in your script. See more here Scripts - Home Assistant

1 Like

The thumbnail is created by the controller once the event has finished - your automation triggers when motion starts, so longer motion events will outlast your 10 second delay and the thumbnail will not be available.

Still yet to refine my current G4 automation to send smart object detection events when first triggered, but the current state (including dynamic file name for the notification) is below in case it helps.

Notes:

  1. I use the event score and length to provide additional filtering of motion events unless the motion is a smart detection of an object.
  2. I trigger on the end of the motion event to ensure that the thumbnail is available (only seems to 404 when it’s raining). I’m planning to move the person / vehicle smart detections to be triggered on the start of a motion event and captured with a snapshot, but haven’t got round to it yet.
  3. On HA startup I load sensors with local and remote image paths from my secrets.yaml for easy maintenance.
  4. I use the original driveway_motion.jpg to provide a picture entity in my dashboard for the last motion event, and send the copied image with a dynamic file name as the notification. Images are cleaned up with a maintenance automation that runs a command line removal of images over 14 days old.
  5. The camera always records but whether we are notified or not depends on input_boolean.notify_driveway which is set by time of day or through the dashboard.
  6. /lovelace/camera_driveway is a hidden Lovelace view / tab with a panel mode live view picture entity showing the driveway camera.
  7. Notification is sent to the official Android companion app.
- id: unifiprotect_driveway_motion_notification
  alias: "Driveway Motion Notification"
  mode: queued
  trigger:
    platform: state
    entity_id: binary_sensor.motion_driveway
    from: "on"
    to: "off"
  condition:
    condition: and
    conditions:
      - condition: template
        value_template: >
           {{ ((is_state('sun.sun', 'above_horizon') and (state_attr('binary_sensor.motion_driveway', 'event_score') | int >= 50)))
             or ((is_state('sun.sun', 'below_horizon') and (state_attr('binary_sensor.motion_driveway', 'event_score') | int >= 50)))
             or (not is_state_attr('binary_sensor.motion_driveway', 'event_object', 'None Identified')) }}
      - condition: template
        value_template: >
           {{ (is_state('sun.sun', 'above_horizon') and (state_attr('binary_sensor.motion_driveway', 'event_length') | int >= 6))
               or (is_state('sun.sun', 'below_horizon') and (state_attr('binary_sensor.motion_driveway', 'event_length') | int >= 12))
               or (not is_state_attr('binary_sensor.motion_driveway', 'event_object', 'None Identified')) }}
  action:
    - variables:
        notify_image: "driveway_motion_{{ now().strftime('%Y%m%d%H%M') }}.jpg"
        motion_type: "{{ 'Motion' if is_state_attr('binary_sensor.motion_driveway', 'event_object', 'None Identified') else (state_attr('binary_sensor.motion_driveway', 'event_object') | capitalize) }}"
        motion_score: "{{ state_attr('binary_sensor.motion_driveway', 'event_score') }}"
        motion_length: >
          {% set time = (state_attr('binary_sensor.motion_driveway', 'event_length') | int) %}
          {% set mins = ((time % 3600) / 60) | int %}
          {% set secs = time - (mins * 60) %}
          {% if time < 60 %}{{ secs }} seconds
          {% else %}{{ mins }} minutes {{ secs }} seconds
          {% endif %}
    - service: input_datetime.set_datetime
      data:
        entity_id: input_datetime.last_driveway_motion
        time: "{{ (as_timestamp(now()) | timestamp_custom('%H:%M:%S', true)) }}"
        date: "{{ (as_timestamp(now()) | timestamp_custom('%Y-%m-%d', true)) }}"
    - choose:
        - conditions:
            # no object type detected, just motion
            - condition: template
              value_template: "{{ not is_state('sensor.motion_recording_driveway', 'never') }}"
            - condition: template 
              value_template: "{{ is_state_attr('binary_sensor.motion_driveway', 'event_object', 'None Identified') }}"
          sequence:
            - delay: "00:00:05"
            - condition: state
              entity_id: binary_sensor.motion_driveway
              state: "off"
            - service: unifiprotect.save_thumbnail_image
              data:
                entity_id: camera.driveway
                filename: "{{ states('sensor.snapshot_local') }}driveway_motion.jpg"
                image_width: 2688
        - conditions:
            # object detected (person, etc) - don't specify image width
            - condition: template
              value_template: "{{ not is_state('sensor.motion_recording_driveway', 'never') }}"
          sequence:
            - delay: "00:00:05"
            - condition: state
              entity_id: binary_sensor.motion_driveway
              state: "off"
            - service: unifiprotect.save_thumbnail_image
              data:
                entity_id: camera.driveway
                filename: "{{ states('sensor.snapshot_local') }}driveway_motion.jpg"
      default:
        - service: camera.snapshot
          data: 
            entity_id: camera.driveway
            filename: "{{ states('sensor.snapshot_local') }}driveway_motion.jpg"
    - service: shell_command.copy_file
      data:
        src_file: "{{ states('sensor.snapshot_local') }}driveway_motion.jpg"  
        dst_file: "{{ states('sensor.snapshot_local') }}{{ notify_image }}"
    - choose:
        - conditions:
            - condition: and 
              conditions:
                - condition: state
                  entity_id: input_boolean.notify_driveway
                  state: "on"
                - condition: state
                  entity_id:
                    - input_boolean.holiday_mode
                    - input_boolean.notify_via_sms
                  state: "off"
          sequence:                         
            - service: notify.all_mobile
              data:
                title: "Driveway Alert"
                message: >
                  {{ motion_type }} at {{ as_timestamp(state_attr('binary_sensor.motion_driveway', 'last_tripped_time')) | timestamp_custom('%H:%M') }}.<br>
                  Score: {{ motion_score }}. <br>
                  Length: {{ motion_length }}.
                data:
                  image: "{{ states('sensor.snapshot_remote') }}{{ notify_image }}"
                  clickAction: "/lovelace/camera_driveway"
                  group: Driveway

1 Like

Super thanks! I updated the code with camera.snapshot and so far so good :slight_smile: Will look into the code for file naming.

Wow! Your code was like finding a big safe full of money, you get super excited but not sure what to do with it :grin: So much useful stuff in there but it’s gonna take some time for me to digest it :slight_smile: I had no clue you can get all kind of information from the motion triggering. Is there a list somewhere with the variables that can be used?

Thank you so much for sharing!

I tend to use the homeassistant Developer Tools > State page to review entities and attributes and always build and test my template syntax in Developer Tools > Template. I do (still) build my automations in flat yaml files rather than using the user interface tools though as I find it easier to do things that way.

1 Like

Smart, I’ll be better at looking there.

Can I ask you (or someone else) how you deal with privacy and the images that are created? As it is now, they are stored in config/www and can be accessed by anyone. Is there a way to secure them and make them unreachable (I guess outside of www but still make them show up in the notification?

Hmm - can’t say I’d ever realised that they could be accessed on the internal http link without authenticating via HA first but based on a quick test they can (if someone knows the full image path).
Looks like this as come up in discussion not too long ago: What the heck: config/www == local? - #25 by tom_l with the suggestion of using the new /media path for storage (I’ve not tried this).

I may consider moving mine down the road but I’ve a lot of items on my todo list at the moment - moving from a core venv install to core in docker, migrating from zwave to zwavejs.
Changing image storage location on my install is likely to be a Saturday grepping session when I’ve got docker stuff done…

If you get anything working, please share.

A workaround would be to instead of showing a static image, a live stream would show in the notification. Would be so much useful, but I’m not having any luck getting it up and running.

@gr4z, I noticed that you struggeled to get the live stream in a notification, did you ever get that to work?

Edit:
I got the /media folder to work when sending notifications. This way the images aren’t accessible if someone has the direct link. A bit more secure. This page helped me get it to work:

1 Like

This probably explains the issue I am experiencing. I seem to get outdated images, and even after removing the files from the expected storage location and re-triggering the motion event, I get an old image. This was very confusing to me until reading this post.

I’m using the Home Assistant app on iOS, and need to figure out how to clear the cache for it. I am especially interested in doing it programmatically as part of my Node-Red flow that pops up a camera snapshot.

The easiest way to resolve image caching is to append a timestamp to the end of the captured image, making it unique. I work in YAML so set a variable with the image name as a first action in the automation and then reference that variable for the other actions.
I use a nightly cron job to clean images older than 14 days from the storage folder.

That link proved very useful - I’ve moved my notification images to a sub-directory of /media after testing it out as part of my prep to move to docker.
As part of the move I also tidied up my automations to use a folder_watcher to automatically update the last motion event image for the dashboard which removed the need for a file copy in the camera automation.
Just the venv->docker and zwave->zwavejs move to go now (all tested, just need a window to migrate with the time to bugfix if needed).

Hello all, clean install of HA and working very well, however im not able to find Unifi protect when i search for it under integrations. Any idea?

Have you installed the integration files using HACs or another method per https://github.com/briis/unifiprotect#installation?
The integration is not yet one of the core integrations included with a installation of HA.

Ah thank you :slight_smile:

Thanks so much for this! Your component is why I dug in and started using Home Assistant (as of yesterday) I have a newb question if you don’t mind. Everything seems to be working great except I notice a good 10 second delay on the feed with this method. Is there any way to improve on that?

alias: Doorbell Nest Display
description: ''
trigger:
  - platform: state
    entity_id: binary_sensor.motion_front_door
    to: 'on'
condition: []
action:
  - data:
      media_player: media_player.kitchen_display
    service: camera.play_stream
    entity_id: camera.front_door
  - delay:
      hours: 0
      minutes: 0
      seconds: 20
      milliseconds: 0
  - service: media_player.turn_off
    data: {}
    entity_id: media_player.kitchen_display
mode: single

Welcome to Home Assistant - hope you will enjoy it.

The Protect integration uses the stream component built-in to Home Assistant, and that gives a 10-20 seconds delay in the stream. I assume it is due to conversion of the RTSP stream.
If you don’t need the Audio, you can disable all the RTSP streams in the Protect APP, and then restart Home Assistant. This will improve the video delay, but you loose the audio.

Thanks! It seems that the same automation doesn’t work after turning off the streams:

Logger: homeassistant.components.stream.worker
Source: components/stream/worker.py:83
Integration: Stream (documentation, issues)
First occurred: 8:11:13 AM (4 occurrences)
Last logged: 8:12:47 AM

Error opening stream rtsp://192.168.1.1:7447/MyFWLhUVZ9qhSXId