From e2ea83676112ef4f5b6c971169d28e5e8b1926a6 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Thu, 5 Mar 2026 12:54:32 -0600 Subject: [PATCH] add MQTT and dispatcher integration for profiles - Subscribe to frigate/profile/set MQTT topic - Publish profile/state and profiles/available on connect - Add _on_profile_command handler to dispatcher - Broadcast active profile state on WebSocket connect --- frigate/comms/dispatcher.py | 21 +++++++++++++++++++++ frigate/comms/mqtt.py | 22 ++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/frigate/comms/dispatcher.py b/frigate/comms/dispatcher.py index 490a829dc..ad512e6ab 100644 --- a/frigate/comms/dispatcher.py +++ b/frigate/comms/dispatcher.py @@ -91,7 +91,9 @@ class Dispatcher: } self._global_settings_handlers: dict[str, Callable] = { "notifications": self._on_global_notification_command, + "profile": self._on_profile_command, } + self.profile_manager = None for comm in self.comms: comm.subscribe(self._receive) @@ -298,6 +300,11 @@ class Dispatcher: ) self.publish("birdseye_layout", json.dumps(self.birdseye_layout.copy())) self.publish("audio_detections", json.dumps(audio_detections)) + self.publish( + "profile/state", + self.config.active_profile or "none", + retain=True, + ) def handle_notification_test() -> None: self.publish("notification_test", "Test notification") @@ -556,6 +563,20 @@ class Dispatcher: ) self.publish("notifications/state", payload, retain=True) + def _on_profile_command(self, payload: str) -> None: + """Callback for profile/set topic.""" + if self.profile_manager is None: + logger.error("Profile manager not initialized") + return + + profile_name = payload.strip() if payload.strip() not in ("", "none", "None") else None + err = self.profile_manager.activate_profile(profile_name) + if err: + logger.error("Failed to activate profile: %s", err) + return + + self.publish("profile/state", payload.strip() or "none", 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 diff --git a/frigate/comms/mqtt.py b/frigate/comms/mqtt.py index 9279b4388..8b62f78b5 100644 --- a/frigate/comms/mqtt.py +++ b/frigate/comms/mqtt.py @@ -1,3 +1,4 @@ +import json import logging import threading from typing import Any, Callable @@ -163,6 +164,22 @@ class MqttClient(Communicator): retain=True, ) + self.publish( + "profile/state", + self.config.active_profile or "none", + retain=True, + ) + available_profiles: list[str] = [] + for camera in self.config.cameras.values(): + for profile_name in camera.profiles: + if profile_name not in available_profiles: + available_profiles.append(profile_name) + self.publish( + "profiles/available", + json.dumps(sorted(available_profiles)), + retain=True, + ) + self.publish("available", "online", retain=True) def on_mqtt_command( @@ -289,6 +306,11 @@ class MqttClient(Communicator): self.on_mqtt_command, ) + self.client.message_callback_add( + f"{self.mqtt_config.topic_prefix}/profile/set", + self.on_mqtt_command, + ) + self.client.message_callback_add( f"{self.mqtt_config.topic_prefix}/onConnect", self.on_mqtt_command )