mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-03-22 00:17:13 +01:00
Make notifications toggleable via MQTT (#13657)
* Add ability to toggle mqtt state from MQTT / ws * Listen to notification config updates * Add docs for notifications
This commit is contained in:
parent
8db9824842
commit
07d1692f2b
@ -154,6 +154,14 @@ Message published for each changed review item. The first message is published w
|
||||
|
||||
Same data available at `/api/stats` published at a configurable interval.
|
||||
|
||||
### `frigate/notifications/set`
|
||||
|
||||
Topic to turn notifications on and off. Expected values are `ON` and `OFF`.
|
||||
|
||||
### `frigate/notifications/state`
|
||||
|
||||
Topic with current state of notifications. Published values are `ON` and `OFF`.
|
||||
|
||||
## Frigate Camera Topics
|
||||
|
||||
### `frigate/<camera_name>/<object_name>`
|
||||
|
@ -406,7 +406,7 @@ class FrigateApp:
|
||||
if self.config.mqtt.enabled:
|
||||
comms.append(MqttClient(self.config))
|
||||
|
||||
if self.config.notifications.enabled:
|
||||
if self.config.notifications.enabled_in_config:
|
||||
comms.append(WebPushClient(self.config))
|
||||
|
||||
comms.append(WebSocketClient(self.config))
|
||||
|
@ -75,6 +75,9 @@ class Dispatcher:
|
||||
"birdseye": self._on_birdseye_command,
|
||||
"birdseye_mode": self._on_birdseye_mode_command,
|
||||
}
|
||||
self._global_settings_handlers: dict[str, Callable] = {
|
||||
"notifications": self._on_notification_command,
|
||||
}
|
||||
|
||||
for comm in self.comms:
|
||||
comm.subscribe(self._receive)
|
||||
@ -86,9 +89,13 @@ class Dispatcher:
|
||||
if topic.endswith("set"):
|
||||
try:
|
||||
# example /cam_name/detect/set payload=ON|OFF
|
||||
camera_name = topic.split("/")[-3]
|
||||
command = topic.split("/")[-2]
|
||||
self._camera_settings_handlers[command](camera_name, payload)
|
||||
if topic.count("/") == 2:
|
||||
camera_name = topic.split("/")[-3]
|
||||
command = topic.split("/")[-2]
|
||||
self._camera_settings_handlers[command](camera_name, payload)
|
||||
elif topic.count("/") == 1:
|
||||
command = topic.split("/")[-2]
|
||||
self._global_settings_handlers[command](payload)
|
||||
except IndexError:
|
||||
logger.error(f"Received invalid set command: {topic}")
|
||||
return
|
||||
@ -282,6 +289,18 @@ class Dispatcher:
|
||||
self.config_updater.publish(f"config/motion/{camera_name}", motion_settings)
|
||||
self.publish(f"{camera_name}/motion_threshold/state", payload, retain=True)
|
||||
|
||||
def _on_notification_command(self, payload: str) -> None:
|
||||
"""Callback for notification topic."""
|
||||
if payload != "ON" and payload != "OFF":
|
||||
f"Received unsupported value for notification: {payload}"
|
||||
return
|
||||
|
||||
notification_settings = self.config.notifications
|
||||
logger.info(f"Setting notifications: {payload}")
|
||||
notification_settings.enabled = payload == "ON" # type: ignore[union-attr]
|
||||
self.config_updater.publish("config/notifications", notification_settings)
|
||||
self.publish("notifications/state", payload, retain=True)
|
||||
|
||||
def _on_audio_command(self, camera_name: str, payload: str) -> None:
|
||||
"""Callback for audio topic."""
|
||||
audio_settings = self.config.cameras[camera_name].audio
|
||||
|
@ -105,6 +105,13 @@ class MqttClient(Communicator): # type: ignore[misc]
|
||||
retain=True,
|
||||
)
|
||||
|
||||
if self.config.notifications.enabled_in_config:
|
||||
self.publish(
|
||||
"notifications/state",
|
||||
"ON" if self.config.notifications.enabled else "OFF",
|
||||
retain=True,
|
||||
)
|
||||
|
||||
self.publish("available", "online", retain=True)
|
||||
|
||||
def on_mqtt_command(
|
||||
@ -209,6 +216,12 @@ class MqttClient(Communicator): # type: ignore[misc]
|
||||
self.on_mqtt_command,
|
||||
)
|
||||
|
||||
if self.config.notifications.enabled_in_config:
|
||||
self.client.message_callback_add(
|
||||
f"{self.mqtt_config.topic_prefix}/notifications/set",
|
||||
self.on_mqtt_command,
|
||||
)
|
||||
|
||||
self.client.message_callback_add(
|
||||
f"{self.mqtt_config.topic_prefix}/restart", self.on_mqtt_command
|
||||
)
|
||||
|
@ -9,6 +9,7 @@ from typing import Any, Callable
|
||||
from py_vapid import Vapid01
|
||||
from pywebpush import WebPusher
|
||||
|
||||
from frigate.comms.config_updater import ConfigSubscriber
|
||||
from frigate.comms.dispatcher import Communicator
|
||||
from frigate.config import FrigateConfig
|
||||
from frigate.const import CONFIG_DIR
|
||||
@ -41,6 +42,9 @@ class WebPushClient(Communicator): # type: ignore[misc]
|
||||
for sub in user["notification_tokens"]:
|
||||
self.web_pushers[user["username"]].append(WebPusher(sub))
|
||||
|
||||
# notification config updater
|
||||
self.config_subscriber = ConfigSubscriber("config/notifications")
|
||||
|
||||
def subscribe(self, receiver: Callable) -> None:
|
||||
"""Wrapper for allowing dispatcher to subscribe."""
|
||||
pass
|
||||
@ -101,6 +105,15 @@ class WebPushClient(Communicator): # type: ignore[misc]
|
||||
|
||||
def publish(self, topic: str, payload: Any, retain: bool = False) -> None:
|
||||
"""Wrapper for publishing when client is in valid state."""
|
||||
# check for updated notification config
|
||||
_, updated_notif_config = self.config_subscriber.check_for_update()
|
||||
|
||||
if updated_notif_config:
|
||||
self.config.notifications = updated_notif_config
|
||||
|
||||
if not self.config.notifications.enabled:
|
||||
return
|
||||
|
||||
if topic == "reviews":
|
||||
self.send_alert(json.loads(payload))
|
||||
|
||||
|
@ -172,6 +172,9 @@ class AuthConfig(FrigateBaseModel):
|
||||
class NotificationConfig(FrigateBaseModel):
|
||||
enabled: bool = Field(default=False, title="Enable notifications")
|
||||
email: Optional[str] = Field(default=None, title="Email required for push.")
|
||||
enabled_in_config: Optional[bool] = Field(
|
||||
default=None, title="Keep track of original state of notifications."
|
||||
)
|
||||
|
||||
|
||||
class StatsConfig(FrigateBaseModel):
|
||||
@ -1459,6 +1462,9 @@ class FrigateConfig(FrigateBaseModel):
|
||||
config.mqtt.user = config.mqtt.user.format(**FRIGATE_ENV_VARS)
|
||||
config.mqtt.password = config.mqtt.password.format(**FRIGATE_ENV_VARS)
|
||||
|
||||
# set notifications state
|
||||
config.notifications.enabled_in_config = config.notifications.enabled
|
||||
|
||||
# GenAI substitution
|
||||
if config.genai.api_key:
|
||||
config.genai.api_key = config.genai.api_key.format(**FRIGATE_ENV_VARS)
|
||||
|
Loading…
Reference in New Issue
Block a user