Send mqtt message when motion is detected (#3152)

* Send mqtt message when motion is detected

* Use object processing instead of passing mqtt client around

* Cleanup

* Formatting

* add comment

* Make off delay configurable.

* Handle updating each camera based on config off delay

* Formatting

* Update docker-compose.yml

* Fix processing issue

* Update mqtt docs

* Update main config docs

* Make sure multiple True values aren't published for the same motion

* Make sure multiple True values aren't published for the same motion

* Update payload to fit existing HA standard values

* Update docs to fit new values

* Update docs

* Update motion topic

* Use datetime.datetime and remove unused imports

* Cast to int

* Clarify motion detector behavior in docs

* Fix typo

Co-authored-by: Blake Blackshear <blakeb@blakeshome.com>
This commit is contained in:
Nicolas Mowen 2022-05-15 06:03:33 -06:00 committed by GitHub
parent 90bff605fa
commit de244d6873
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 45 additions and 8 deletions

View File

@ -246,6 +246,8 @@ motion:
# Enables dynamic contrast improvement. This should help improve night detections at the cost of making motion detection more sensitive
# for daytime.
improve_contrast: False
# Optional: Delay when updating camera motion through MQTT from ON -> OFF (default: shown below).
mqtt_off_delay: 30
# Optional: Record configuration
# NOTE: Can be overridden at the camera level

View File

@ -123,6 +123,12 @@ Topic with current state of snapshots for a camera. Published values are `ON` an
Topic to turn motion detection for a camera on and off. Expected values are `ON` and `OFF`.
NOTE: Turning off motion detection will fail if detection is not disabled.
### `frigate/<camera_name>/motion`
Whether camera_name is currently detecting motion. Expected values are `ON` and `OFF`.
NOTE: After motion is initially detected, `ON` will be set until no motion has
been detected for `mqtt_off_delay` seconds (30 by default).
### `frigate/<camera_name>/motion/state`
Topic with current state of motion detection for a camera. Published values are `ON` and `OFF`.

View File

@ -134,6 +134,10 @@ class MotionConfig(FrigateBaseModel):
mask: Union[str, List[str]] = Field(
default="", title="Coordinates polygon for the motion mask."
)
mqtt_off_delay: int = Field(
default=30,
title="Delay for updating MQTT with no motion detected.",
)
class RuntimeMotionConfig(MotionConfig):

View File

@ -1,29 +1,24 @@
import base64
import copy
import datetime
import hashlib
import itertools
import json
import logging
import os
import queue
import threading
import time
from collections import Counter, defaultdict
from statistics import mean, median
from statistics import median
from typing import Callable
import cv2
import numpy as np
from frigate.config import CameraConfig, SnapshotsConfig, RecordConfig, FrigateConfig
from frigate.const import CACHE_DIR, CLIPS_DIR, RECORD_DIR
from frigate.const import CLIPS_DIR
from frigate.util import (
SharedMemoryFrameManager,
calculate_region,
draw_box_with_label,
draw_timestamp,
load_labels,
)
logger = logging.getLogger(__name__)
@ -652,6 +647,7 @@ class TrackedObjectProcessor(threading.Thread):
self.stop_event = stop_event
self.camera_states: dict[str, CameraState] = {}
self.frame_manager = SharedMemoryFrameManager()
self.last_motion_updates: dict[str, int] = {}
def start(camera, obj: TrackedObject, current_frame_time):
self.event_queue.put(("start", camera, obj.to_dict()))
@ -844,6 +840,33 @@ class TrackedObjectProcessor(threading.Thread):
return True
def should_mqtt_motion(self, camera, motion_boxes):
# publish if motion is currently being detected
if motion_boxes:
# only send True if motion hasn't been detected recently
if self.last_motion_updates.get(camera, 0) == 0:
self.client.publish(
f"{self.topic_prefix}/{camera}/motion",
"ON",
retain=False,
)
# always updated latest motion
self.last_motion_updates[camera] = int(datetime.datetime.now().timestamp())
elif not motion_boxes and self.last_motion_updates.get(camera, 0) != 0:
mqtt_delay = self.config.cameras[camera].motion.mqtt_off_delay
now = int(datetime.datetime.now().timestamp())
# If no motion, make sure the off_delay has passed
if now - self.last_motion_updates.get(camera, 0) >= mqtt_delay:
self.client.publish(
f"{self.topic_prefix}/{camera}/motion",
"OFF",
retain=False,
)
# reset the last_motion so redundant `off` commands aren't sent
self.last_motion_updates[camera] = 0
def get_best(self, camera, label):
# TODO: need a lock here
camera_state = self.camera_states[camera]
@ -879,6 +902,8 @@ class TrackedObjectProcessor(threading.Thread):
frame_time, current_tracked_objects, motion_boxes, regions
)
self.should_mqtt_motion(camera, motion_boxes)
tracked_objects = [
o.to_dict() for o in camera_state.tracked_objects.values()
]