Add Home Assistant discovery icons
This commit is contained in:
@@ -14,6 +14,8 @@ through Home Assistant MQTT Discovery plus an emergency HTTP fallback GUI.
|
||||
can update MQTT Discovery without a container restart.
|
||||
- On startup it runs a safe `all-off` by default, sets switch entities to `OFF`,
|
||||
and publishes retained MQTT state.
|
||||
- Home Assistant MQTT Discovery payloads include semantic MDI icons for all-off,
|
||||
siren, red-blue/strobe, and white-light entities.
|
||||
- Alarms and lights have default auto-off timers: siren 180s, red-blue/strobe
|
||||
300s, white light 600s. The GUI timer is rendered from a backend timestamp,
|
||||
so page refreshes do not reset it.
|
||||
|
||||
@@ -19,6 +19,12 @@ VERSION = "1.0"
|
||||
DEFAULT_CONFIG = "/etc/frigate-camera-control-bridge.json"
|
||||
LOG = logging.getLogger("frigate-camera-control-bridge")
|
||||
STREAM_URL_RE = re.compile(r"(?:rtsp|rtsps|http|https)://[^\s'\"<>]+", re.IGNORECASE)
|
||||
HOME_ASSISTANT_ACTION_ICONS = {
|
||||
"action.siren": "mdi:bullhorn",
|
||||
"action.red_blue": "mdi:alarm-light",
|
||||
"action.manual_white": "mdi:lightbulb-on",
|
||||
"action.all_off": "mdi:power",
|
||||
}
|
||||
|
||||
from .i18n import DEFAULT_LANGUAGE, available_languages, normalize_language, text_for
|
||||
from .ui import read_static_file, render_page
|
||||
@@ -1401,6 +1407,9 @@ class CameraControlBridge:
|
||||
},
|
||||
"qos": 0,
|
||||
}
|
||||
icon = self.home_assistant_icon(action)
|
||||
if icon:
|
||||
payload["icon"] = icon
|
||||
if action.platform == "switch":
|
||||
payload.update(
|
||||
{
|
||||
@@ -1424,6 +1433,20 @@ class CameraControlBridge:
|
||||
)
|
||||
return payload
|
||||
|
||||
@staticmethod
|
||||
def home_assistant_icon(action):
|
||||
# Home Assistant MQTT discovery accepts MDI icons on switches and
|
||||
# buttons. Keep icons semantic and overrideable by static action config.
|
||||
configured = action.cfg.get("icon")
|
||||
if configured:
|
||||
return configured
|
||||
name_key = action.cfg.get("name_key")
|
||||
if name_key in HOME_ASSISTANT_ACTION_ICONS:
|
||||
return HOME_ASSISTANT_ACTION_ICONS[name_key]
|
||||
if action.kind == "all_off":
|
||||
return HOME_ASSISTANT_ACTION_ICONS["action.all_off"]
|
||||
return None
|
||||
|
||||
def execute_action(self, action_id, command, publish=True):
|
||||
action = self.action_by_id(action_id)
|
||||
if action is None:
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import unittest
|
||||
|
||||
from frigate_camera_control_bridge.app import Action, CameraControlBridge
|
||||
|
||||
|
||||
class HomeAssistantDiscoveryTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.bridge = object.__new__(CameraControlBridge)
|
||||
self.bridge.config = {"mqtt": {"base_topic": "test/camera_control"}}
|
||||
self.bridge.default_language = "en"
|
||||
|
||||
def payload_for(self, cfg):
|
||||
base_cfg = {
|
||||
"id": "example_action",
|
||||
"name": "Example action",
|
||||
"platform": "switch",
|
||||
"kind": "dahua_coaxial_io",
|
||||
"device": {"identifiers": ["camera_example"], "name": "Example camera"},
|
||||
}
|
||||
base_cfg.update(cfg)
|
||||
action = Action(base_cfg, http_client=None)
|
||||
return self.bridge.discovery_payload(action, "test/camera_control/status")
|
||||
|
||||
def test_switch_discovery_includes_semantic_icons(self):
|
||||
cases = {
|
||||
"action.siren": "mdi:bullhorn",
|
||||
"action.red_blue": "mdi:alarm-light",
|
||||
"action.manual_white": "mdi:lightbulb-on",
|
||||
}
|
||||
|
||||
for name_key, icon in cases.items():
|
||||
with self.subTest(name_key=name_key):
|
||||
payload = self.payload_for({"name_key": name_key})
|
||||
|
||||
self.assertEqual(payload["icon"], icon)
|
||||
|
||||
def test_all_off_button_discovery_includes_power_icon(self):
|
||||
payload = self.payload_for(
|
||||
{
|
||||
"platform": "button",
|
||||
"kind": "all_off",
|
||||
"name_key": "action.all_off",
|
||||
}
|
||||
)
|
||||
|
||||
self.assertEqual(payload["icon"], "mdi:power")
|
||||
|
||||
def test_configured_icon_overrides_default_icon(self):
|
||||
payload = self.payload_for({"name_key": "action.siren", "icon": "mdi:test-tube"})
|
||||
|
||||
self.assertEqual(payload["icon"], "mdi:test-tube")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user