diff --git a/infra/compose.yaml b/infra/compose.yaml index fb9c205..d552cf0 100644 --- a/infra/compose.yaml +++ b/infra/compose.yaml @@ -73,7 +73,7 @@ services: PLUG_TOPIC: "zigbee2mqtt/Sideboard Lamp/set" POLL_INTERVAL: "30" MAX_AGE_SECONDS: "180" - FLASH_INTERVAL_SECONDS: "3" + FLASH_INTERVAL_SECONDS: "2" FLASH_DURATION_SECONDS: "15" TZ: America/Los_Angeles diff --git a/infra/doorbell-listener/doorbell-listener.py b/infra/doorbell-listener/doorbell-listener.py index 82cda53..f7530c2 100644 --- a/infra/doorbell-listener/doorbell-listener.py +++ b/infra/doorbell-listener/doorbell-listener.py @@ -28,6 +28,7 @@ Env vars (all optional): NTFY_TOPICS comma-separated override, default "ALERT_klubhaus_topic_test,ALERT_klubhaus_topic" """ import json +import math import os import sys import threading @@ -48,12 +49,21 @@ POLL_INTERVAL = int(os.environ.get("POLL_INTERVAL", "30")) # Drop messages older than this many seconds (replay protection after restart). MAX_AGE_SECONDS = int(os.environ.get("MAX_AGE_SECONDS", "180")) # On a fresh alert, flash the plug: TOGGLE every FLASH_INTERVAL_SECONDS, -# repeated so the total runtime is FLASH_DURATION_SECONDS. With defaults -# (3s interval, 15s duration) that's 5 toggles, ending opposite of start. -FLASH_INTERVAL_SECONDS = float(os.environ.get("FLASH_INTERVAL_SECONDS", "3")) +# repeated so the total runtime is FLASH_DURATION_SECONDS. Count is rounded +# UP to the nearest even integer so the lamp ends in the state it started +# in (an even number of toggles returns to the original state). +FLASH_INTERVAL_SECONDS = float(os.environ.get("FLASH_INTERVAL_SECONDS", "2")) FLASH_DURATION_SECONDS = float(os.environ.get("FLASH_DURATION_SECONDS", "15")) +def flash_count() -> int: + """Number of toggles per alert. Always >= 2 and even.""" + n = max(2, math.ceil(FLASH_DURATION_SECONDS / FLASH_INTERVAL_SECONDS)) + if n % 2: + n += 1 + return n + + def log(msg: str) -> None: print(msg, flush=True) @@ -107,7 +117,7 @@ def poll_topic(client: mqtt.Client, topic: str, last_id: str) -> str: newest_id = last_id now = time.time() - flash_count = max(1, int(FLASH_DURATION_SECONDS / FLASH_INTERVAL_SECONDS)) + n_flashes = flash_count() for line in r.iter_lines(decode_unicode=True): if not line: continue @@ -136,7 +146,7 @@ def poll_topic(client: mqtt.Client, topic: str, last_id: str) -> str: continue log(f"[{topic}] alert: id={msg_id} age={age}s title={title!r} message={message!r}") - start_flash(client, flash_count, FLASH_INTERVAL_SECONDS) + start_flash(client, n_flashes, FLASH_INTERVAL_SECONDS) return newest_id @@ -148,8 +158,9 @@ def main() -> None: log(f" plug topic: {PLUG_TOPIC}") log(f" poll interval: {POLL_INTERVAL}s") log(f" max age: {MAX_AGE_SECONDS}s") - log(f" flash: {FLASH_DURATION_SECONDS / FLASH_INTERVAL_SECONDS:.0f} toggles " - f"@ {FLASH_INTERVAL_SECONDS}s ({FLASH_DURATION_SECONDS}s total)") + n_flashes = flash_count() + log(f" flash: {n_flashes} toggles @ {FLASH_INTERVAL_SECONDS}s " + f"({n_flashes * FLASH_INTERVAL_SECONDS}s total, even -> ends at start)") log(f" state dir: {STATE_DIR}") client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2) diff --git a/infra/zigbee2mqtt/configuration.yaml b/infra/zigbee2mqtt/configuration.yaml index 922af93..ebc8bf8 100644 --- a/infra/zigbee2mqtt/configuration.yaml +++ b/infra/zigbee2mqtt/configuration.yaml @@ -9,6 +9,34 @@ serial: rtscts: false advanced: log_level: debug + network_key: + - 146 + - 30 + - 124 + - 1 + - 70 + - 32 + - 235 + - 196 + - 27 + - 113 + - 122 + - 230 + - 7 + - 177 + - 134 + - 45 + pan_id: 3926 + ext_pan_id: + - 225 + - 105 + - 42 + - 213 + - 37 + - 25 + - 93 + - 235 + frontend: enabled: true port: 8080