perf: parallelize the TURN OFF button via shared _shutdown_devices

shutdown_all() was the only remaining sequential shutdown path (TV, then
Gabi, then Gaja — 10+s with SSH timeouts). It now uses the same parallel
_shutdown_devices() as the budget tick, which gained a {dev_id: result}
return value so the button response keeps per-device details and
registry ordering. Per-device failures are isolated instead of being
silently swallowed.
This commit is contained in:
2026-09-03 21:47:24 +02:00
parent cee6162431
commit 3ce17f2146

View File

@ -278,12 +278,25 @@ def curfew_status() -> dict:
} }
def _shutdown_devices(device_ids: list[str]): def _shutdown_devices(device_ids: list[str]) -> dict[str, dict | None]:
"""Shut down a list of devices in parallel.""" """Shut down a list of devices in parallel.
Returns {dev_id: turnoff result or None on failure}.
"""
results: dict[str, dict | None] = {}
if not device_ids: if not device_ids:
return return results
with ThreadPoolExecutor(max_workers=len(device_ids)) as executor: with ThreadPoolExecutor(max_workers=len(device_ids)) as executor:
list(executor.submit(DEVICES[dev_id]["turnoff"]) for dev_id in device_ids) futures = {
executor.submit(DEVICES[dev_id]["turnoff"]): dev_id
for dev_id in device_ids
}
for future, dev_id in futures.items():
try:
results[dev_id] = future.result()
except Exception:
results[dev_id] = None
return results
# ── Budget timer (runs every 10s in background) ────────────────────── # ── Budget timer (runs every 10s in background) ──────────────────────
@ -415,10 +428,14 @@ def stop_timer():
# ── Orchestrator ────────────────────────────────────────────────────── # ── Orchestrator ──────────────────────────────────────────────────────
def shutdown_all() -> list[dict]: def shutdown_all() -> list[dict]:
"""Indiscriminately turn off all devices. Returns ordered list of action dicts.""" """Indiscriminately turn off all devices in parallel.
Returns ordered list of action dicts (registry order).
"""
results = _shutdown_devices(list(DEVICES))
actions = [] actions = []
for dev_id, dev_info in DEVICES.items(): for dev_id, dev_info in DEVICES.items():
result = dev_info["turnoff"]() result = results.get(dev_id)
if result: if result:
budget_minutes = _budget_to_minutes(_get_budget(dev_id)) budget_minutes = _budget_to_minutes(_get_budget(dev_id))
actions.append({ actions.append({