I made some progress, now I have something I find reliable with ZM and HA. It’s far from perfect but I get notifications when needed.
I’m using the number of events added to a template sensor, derive a trend binary-sensor from this value, and trigger the notification when it turns on, but with conditions to prevent too many notifications (only one in 10mn time) and false positive because of fast light change :
The template sensor :
- platform: template
sensors:
alarmes:
friendly_name : 'Mouvements ZM'
unit_of_measurement: 'Events'
value_template: "{{ ((states.sensor.cam1_events.state | int) + (states.sensor.cam2_events.state | int) + ((states.sensor.camext_events.state | int) * 0.66) + ((states.sensor.cam2ext_events.state | int) * 0.75) + ((states.sensor.cam3_events.state | int)) *1.5) | round(2) }}"
Notice that you can weight differently the cameras, I chose to limit the influence of the exterior cams, but add importance to the latest that is the basement, so no external interference except when the garage door opens !
Then the trend sensor, you’ll have to play a bit with the parameters to achieve what you expect.
In my case, I don’t want the sensor to turn on when there is no significant motion, I have a dog at home, doesn’t move that much when nobody’s home but he’s big enough to be detected. Not only min_gradient is important, but also limiting both sample number and sample duration makes the sensor forget about older events (whether they’ve been notified or not) :
- platform: trend
sensors:
alerte_mouvement:
# 3 events/minute
entity_id: sensor.alarmes
sample_duration: 600
min_gradient: 0.05
device_class: moving
max_samples: 10
And last, the notification automation, won’t send the mail if the trend is too much. I sometimes have the camera report lots of very short events, dozen per minute, depending on the light condition. First “condition” filters that out. I’d rather have a “max_gradient” option to the trend sensor.
- alias: 'A_ Alerte ZM'
trigger:
platform: state
entity_id: binary_sensor.alerte_mouvement
to: 'on'
condition:
- condition: template
value_template: "{{ (states.binary_sensor.alerte_mouvement.attributes['gradient'] | float) < 1.0 }}"
- condition: state
entity_id: binary_sensor.somebody_home
state: 'off'
- condition: template
value_template: "{{ (as_timestamp(now()) - (as_timestamp(states.automation.a__alerte_zm.attributes['last_triggered']) | int)) > 600 }}"
action:
service: notify.alerte
data_template:
title: 'ZM ALARM'
message: "Il y a {{ states.sensor.alarmes.state }} mouvements."
Note the parenthesis in the last condition that offer an ugly workaround for a never triggered automation : if “last triggered” is “None”, as_timestamp won’t fail but returns ‘None’, then the int cast will turn it into a convenient 0…
Of course, this comes after tuning ZoneMinder itself, for instance using the blend % parameter to have short events (but still significant…)