From ef925ad0a93ba9e3739bd49dddd07c346987b949 Mon Sep 17 00:00:00 2001 From: "alexei.dolgolyov" Date: Thu, 19 Feb 2026 02:26:57 +0300 Subject: [PATCH] Fix Adalight power toggle using cached idle client and tracked state Serial devices now route power on/off through the cached idle client instead of opening a new serial connection (which caused PermissionError). Adds tracked power_on state to DeviceState since Adalight has no hardware power query. Co-Authored-By: Claude Opus 4.6 --- .../src/wled_controller/api/routes/devices.py | 28 +++++++++++++++---- .../core/processing/processor_manager.py | 2 ++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/server/src/wled_controller/api/routes/devices.py b/server/src/wled_controller/api/routes/devices.py index 1179928..443993b 100644 --- a/server/src/wled_controller/api/routes/devices.py +++ b/server/src/wled_controller/api/routes/devices.py @@ -401,6 +401,7 @@ async def get_device_power( device_id: str, _auth: AuthRequired, store: DeviceStore = Depends(get_device_store), + manager: ProcessorManager = Depends(get_processor_manager), ): """Get current power state from the device.""" device = store.get_device(device_id) @@ -410,6 +411,11 @@ async def get_device_power( raise HTTPException(status_code=400, detail=f"Power control is not supported for {device.device_type} devices") try: + # Serial devices: use tracked state (no hardware query available) + ds = manager._devices.get(device_id) + if device.device_type in ("adalight", "ambiled") and ds: + return {"on": ds.power_on} + provider = get_provider(device.device_type) on = await provider.get_power(device.url) return {"on": on} @@ -424,6 +430,7 @@ async def set_device_power( body: dict, _auth: AuthRequired, store: DeviceStore = Depends(get_device_store), + manager: ProcessorManager = Depends(get_processor_manager), ): """Turn device on or off.""" device = store.get_device(device_id) @@ -437,11 +444,22 @@ async def set_device_power( raise HTTPException(status_code=400, detail="'on' must be a boolean") try: - provider = get_provider(device.device_type) - await provider.set_power( - device.url, on, - led_count=device.led_count, baud_rate=device.baud_rate, - ) + # For serial devices, use the cached idle client to avoid port conflicts + ds = manager._devices.get(device_id) + if device.device_type in ("adalight", "ambiled") and ds: + if on: + # Restore idle state (static color or stay dark) + if ds.static_color is not None: + await manager.send_static_color(device_id, ds.static_color) + else: + await manager._send_clear_pixels(device_id) + ds.power_on = on + else: + provider = get_provider(device.device_type) + await provider.set_power( + device.url, on, + led_count=device.led_count, baud_rate=device.baud_rate, + ) return {"on": on} except Exception as e: logger.error(f"Failed to set power for {device_id}: {e}") diff --git a/server/src/wled_controller/core/processing/processor_manager.py b/server/src/wled_controller/core/processing/processor_manager.py index 78391bd..8a44983 100644 --- a/server/src/wled_controller/core/processing/processor_manager.py +++ b/server/src/wled_controller/core/processing/processor_manager.py @@ -55,6 +55,8 @@ class DeviceState: # Calibration test mode (works independently of target processing) test_mode_active: bool = False test_mode_edges: Dict[str, Tuple[int, int, int]] = field(default_factory=dict) + # Tracked power state for serial devices (no hardware query) + power_on: bool = True class ProcessorManager: