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]):
"""Shut down a list of devices in parallel."""
def _shutdown_devices(device_ids: list[str]) -> dict[str, dict | None]:
"""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:
return
return results
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) ──────────────────────
@ -415,10 +428,14 @@ def stop_timer():
# ── Orchestrator ──────────────────────────────────────────────────────
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 = []
for dev_id, dev_info in DEVICES.items():
result = dev_info["turnoff"]()
result = results.get(dev_id)
if result:
budget_minutes = _budget_to_minutes(_get_budget(dev_id))
actions.append({