Block repeated on while auto-off is active

This commit is contained in:
2026-08-03 01:37:14 +02:00
parent 4ea737024b
commit 6b2bc3853a
8 changed files with 204 additions and 6 deletions
+27 -4
View File
@@ -350,7 +350,7 @@ class Action:
if not key:
return self.name
prefix = self.cfg.get("camera_name") or self.cfg.get("frigate_name") or ""
prefix = self.cfg.get("camera_display_name") or self.cfg.get("camera_name") or self.cfg.get("frigate_name") or ""
return text_for(normalized, key, target=prefix).strip()
def command_list(self):
@@ -807,12 +807,14 @@ class CameraControlBridge:
urls = self.camera_candidate_urls(camera_name, camera_cfg, streams)
endpoints = self.camera_endpoints_from_urls(urls)
kinds = self.kinds_from_urls(urls)
camera_display_name = self.camera_display_name(camera_name, camera_cfg)
for host in endpoints:
inventory.append(
{
"frigate": name,
"frigate_url": base_url,
"camera": camera_name,
"camera_display_name": camera_display_name,
"host": host,
"kinds": kinds,
}
@@ -820,6 +822,15 @@ class CameraControlBridge:
LOG.info("discovered %d camera host mapping(s) from Frigate", len(inventory))
return inventory
@staticmethod
def camera_display_name(camera_name, camera_cfg):
# Frigate camera keys remain the API identifier. friendly_name is only
# used for human-facing labels when present.
friendly_name = camera_cfg.get("friendly_name")
if friendly_name:
return str(friendly_name)
return camera_name
def load_frigate_config_with_retry(self, frigate, base_url):
discovery = self.config.get("discovery", {})
wait = bool(discovery.get("wait_for_frigate", True))
@@ -1081,6 +1092,7 @@ class CameraControlBridge:
"name": text_for(lang, "action.manual_white", target=item["camera"]),
"name_key": "action.manual_white",
"camera_name": item["camera"],
"camera_display_name": item.get("camera_display_name", item["camera"]),
"platform": "switch",
"kind": "hikvision_colorvu",
"host": host,
@@ -1089,7 +1101,7 @@ class CameraControlBridge:
"snapshot": self.snapshot_config(item),
"device": {
"identifiers": [f"camera_{host.replace('.', '_')}"],
"name": item["camera"],
"name": item.get("camera_display_name", item["camera"]),
"manufacturer": "Hikvision",
"model": model,
"configuration_url": item["frigate_url"],
@@ -1120,6 +1132,7 @@ class CameraControlBridge:
"name": text_for(lang, "action.red_blue", target=item["camera"]),
"name_key": "action.red_blue",
"camera_name": item["camera"],
"camera_display_name": item.get("camera_display_name", item["camera"]),
"platform": "switch",
"kind": "dahua_coaxial_io",
"host": host,
@@ -1138,6 +1151,7 @@ class CameraControlBridge:
"name": text_for(lang, "action.siren", target=item["camera"]),
"name_key": "action.siren",
"camera_name": item["camera"],
"camera_display_name": item.get("camera_display_name", item["camera"]),
"platform": "switch",
"kind": "dahua_coaxial_io",
"host": host,
@@ -1182,7 +1196,7 @@ class CameraControlBridge:
def discovered_device(item, manufacturer, model):
return {
"identifiers": [f"camera_{item['host'].replace('.', '_')}"],
"name": item["camera"],
"name": item.get("camera_display_name", item["camera"]),
"manufacturer": manufacturer,
"model": model,
"configuration_url": item["frigate_url"],
@@ -1192,6 +1206,7 @@ class CameraControlBridge:
config = {
"frigate_url": item["frigate_url"],
"camera": item["camera"],
"camera_display_name": item.get("camera_display_name", item["camera"]),
}
defaults = self.config.get("defaults", {})
if defaults.get("frigate_public_url"):
@@ -1413,6 +1428,8 @@ class CameraControlBridge:
action = self.action_by_id(action_id)
if action is None:
raise RuntimeError(f"unknown action {action_id}")
if command == "on" and self.auto_off_running(action):
raise RuntimeError("action is already active until automatic turn-off")
result = action.run(command, bridge=self)
if action.platform == "switch":
if command == "on":
@@ -1428,6 +1445,10 @@ class CameraControlBridge:
LOG.warning("action %s %s worked, but MQTT publish failed: %s", action_id, command, exc)
return result
@staticmethod
def auto_off_running(action):
return bool(getattr(action, "auto_off_at", None))
def schedule_auto_off(self, action):
seconds = int(action.auto_off_seconds or 0)
if seconds <= 0:
@@ -1723,7 +1744,9 @@ class CameraControlBridge:
if parsed.path.startswith("/snapshot/"):
action_id = urllib.parse.unquote(parsed.path.rsplit("/", 1)[-1]).removesuffix(".jpg")
try:
live = bool(bridge.config.get("defaults", {}).get("snapshot_live_on_gui", True))
values = urllib.parse.parse_qs(parsed.query)
refresh = values.get("refresh", ["0"])[0].lower() in ("1", "true", "yes", "on")
live = refresh or bool(bridge.config.get("defaults", {}).get("snapshot_live_on_gui", True))
snapshot = bridge.snapshot_for_action(action_id, live=live)
except Exception as exc:
LOG.info("snapshot request failed for %s: %s", action_id, exc)