Fix autorestore logic and protocol badge per device type

Autorestore fixes:
- Snapshot WLED state before connect() mutates it (lor, AudioReactive)
- Gate restore on auto_shutdown setting (was unconditional)
- Remove misleading auto_restore capability from serial provider
- Default auto_shutdown to false for all new devices

Protocol badge fixes:
- Show correct protocol per device type (OpenRGB SDK, MQTT, WebSocket)
- Was showing "Serial" for all non-WLED devices

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-09 10:04:40 +03:00
parent 96bd3bd0f0
commit 8061c26bef
9 changed files with 44 additions and 12 deletions

View File

@@ -29,8 +29,7 @@ class SerialDeviceProvider(LEDDeviceProvider):
# power_control: can blank LEDs by sending all-black pixels
# brightness_control: software brightness (multiplies pixel values before sending)
# health_check: serial port availability probe
# auto_restore: blank LEDs when targets stop
return {"manual_led_count", "power_control", "brightness_control", "health_check", "auto_restore"}
return {"manual_led_count", "power_control", "brightness_control", "health_check"}
async def check_health(self, url: str, http_client, prev_health=None) -> DeviceHealth:
# Generic serial port health check — enumerate COM ports

View File

@@ -82,6 +82,7 @@ class WLEDClient(LEDClient):
self._client: Optional[httpx.AsyncClient] = None
self._ddp_client: Optional[DDPClient] = None
self._connected = False
self._pre_connect_state: Optional[dict] = None
async def connect(self) -> bool:
"""Establish connection to WLED device.
@@ -107,6 +108,9 @@ class WLEDClient(LEDClient):
)
self.use_ddp = True
# Snapshot device state BEFORE any mutations (for auto-restore)
self._pre_connect_state = await self.snapshot_device_state()
# Create DDP client if needed
if self.use_ddp:
self._ddp_client = DDPClient(self.host, rgbw=False)
@@ -465,7 +469,14 @@ class WLEDClient(LEDClient):
# ===== LEDClient abstraction methods =====
async def snapshot_device_state(self) -> Optional[dict]:
"""Snapshot WLED state before streaming (on, lor, AudioReactive)."""
"""Snapshot WLED state (on, lor, AudioReactive).
If connect() already captured a pre-mutation snapshot, returns that
instead of the current (potentially already-modified) state.
"""
# Return pre-connect snapshot if available (captured before DDP setup)
if hasattr(self, '_pre_connect_state') and self._pre_connect_state is not None:
return self._pre_connect_state
if not self._client:
return None
try:

View File

@@ -165,6 +165,7 @@ class ProcessorManager:
send_latency_ms=send_latency_ms,
rgbw=rgbw,
zone_mode=ds.zone_mode,
auto_shutdown=ds.auto_shutdown,
)
# ===== EVENT SYSTEM (state change notifications) =====

View File

@@ -75,6 +75,7 @@ class DeviceInfo:
send_latency_ms: int = 0
rgbw: bool = False
zone_mode: str = "combined"
auto_shutdown: bool = False
@dataclass

View File

@@ -197,9 +197,11 @@ class WledTargetProcessor(TargetProcessor):
self._task = None
await asyncio.sleep(0.05)
# Restore device state
# Restore device state (only if auto_shutdown is enabled)
if self._led_client and self._device_state_before:
await self._led_client.restore_device_state(self._device_state_before)
device_info = self._ctx.get_device_info(self._device_id)
if device_info and device_info.auto_shutdown:
await self._led_client.restore_device_state(self._device_state_before)
self._device_state_before = None
# Close LED connection