From d25104b8a1db176278ca972879314a9ddd3d2497 Mon Sep 17 00:00:00 2001 From: Mitja Horvat Date: Tue, 9 Jun 2026 09:36:10 +0200 Subject: [PATCH] Make checking parallel (faster). --- devices.py | 101 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 68 insertions(+), 33 deletions(-) diff --git a/devices.py b/devices.py index a1427a4..eb5ada7 100644 --- a/devices.py +++ b/devices.py @@ -3,6 +3,7 @@ import json import subprocess import threading +from concurrent.futures import ThreadPoolExecutor, as_completed from datetime import datetime, timedelta from pathlib import Path @@ -274,17 +275,28 @@ def _budget_tick(): if not is_curfew_allowed(): return + # Check all devices in parallel + online_status = {} + with ThreadPoolExecutor(max_workers=3) as executor: + futures = { + executor.submit(dev_info["check"]): dev_id + for dev_id, dev_info in DEVICES.items() + } + for future in as_completed(futures): + dev_id = futures[future] + result = future.result() + if isinstance(result, dict): + online_status[dev_id] = result.get("online", False) + else: + online_status[dev_id] = bool(result) + + # Process results sequentially (budget operations need to be thread-safe) with _timer_lock: - for dev_id, dev_info in DEVICES.items(): + for dev_id, online in online_status.items(): + dev_info = DEVICES[dev_id] state = _load_state() current_budget = state.get(dev_id, {}).get("budget", BUDGET_S) - is_online = dev_info["check"]() - if isinstance(is_online, dict): - online = is_online.get("online", False) - else: - online = bool(is_online) - # Expired budget + device back online โ†’ shut it down if current_budget <= 0 and online: dev_info["turnoff"]() @@ -342,35 +354,58 @@ def shutdown_all() -> list[dict]: return actions +def _check_device(dev_id: str, dev_info: dict) -> dict: + """Check a single device and return its status dict.""" + is_online = dev_info["check"]() + if isinstance(is_online, dict): + online = is_online.get("online", False) + detail = is_online.get("detail", "") + else: + online = bool(is_online) + detail = "Online" if online else "Offline" + + state = _load_state() + budget_seconds = state.get(dev_id, {}).get("budget", BUDGET_S) + budget_minutes = _budget_to_minutes(budget_seconds) + + # Budget warning + if budget_minutes <= 0: + icon = "๐Ÿ”ด" + elif budget_minutes < 5: + icon = "๐ŸŸ " + else: + icon = dev_info["icon"] + + return { + "icon": icon, + "title": dev_info["name"], + "detail": f"{detail} ยท {budget_minutes} min left", + "online": online, + } + + def status_all() -> list[dict]: """Get current status + budget for all devices. Returns ordered list.""" curfew = curfew_status() - actions = [] - for dev_id, dev_info in DEVICES.items(): - state = _load_state() - budget_seconds = state.get(dev_id, {}).get("budget", BUDGET_S) - is_online = dev_info["check"]() - if isinstance(is_online, dict): - online = is_online.get("online", False) - detail = is_online.get("detail", "") - else: - online = bool(is_online) - detail = "Online" if online else "Offline" + # Check all devices in parallel + results = {} + with ThreadPoolExecutor(max_workers=3) as executor: + futures = { + executor.submit(_check_device, dev_id, dev_info): dev_id + for dev_id, dev_info in DEVICES.items() + } + for future in as_completed(futures): + dev_id = futures[future] + results[dev_id] = future.result() - budget_minutes = _budget_to_minutes(budget_seconds) - - # Budget warning - if budget_minutes <= 0: - icon = "๐Ÿ”ด" - elif budget_minutes < 5: - icon = "๐ŸŸ " - else: - icon = dev_info["icon"] - - actions.append({ - "icon": icon, - "title": dev_info["name"], - "detail": f"{detail} ยท {budget_minutes} min left", - }) + # Return in ordered list (DEVICES preserves insertion order) + actions = [ + { + "icon": results[dev_id]["icon"], + "title": results[dev_id]["title"], + "detail": results[dev_id]["detail"], + } + for dev_id in DEVICES + ] return actions, curfew