Animation None option, FPS min 1, serial COM lifecycle fixes

- Replace animation Enable checkbox with None option in effect selector;
  show effect description tooltip; disable speed slider when None selected
- Allow target FPS range 1-90 (was 10-90) across UI and backend validation
- Scope serial COM connections to target lifetime (no idle caching);
  use temporary connections for power-off/test mode
- Fix serial black frame on stop: flush after write, delay after task
  cancel to prevent race with in-flight thread pool write

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-21 04:33:56 +03:00
parent 8a0730d91b
commit ee52e2d98f
15 changed files with 126 additions and 71 deletions

View File

@@ -105,9 +105,22 @@ class AdalightClient(LEDClient):
try:
black = np.zeros((self._led_count, 3), dtype=np.uint8)
frame = self._build_frame(black, brightness=255)
logger.info(
f"Adalight sending black frame: {self._port} "
f"({self._led_count} LEDs, {len(frame)} bytes)"
)
await asyncio.to_thread(self._serial.write, frame)
await asyncio.to_thread(self._serial.flush)
logger.info(f"Adalight black frame sent and flushed: {self._port}")
except Exception as e:
logger.debug(f"Failed to send black frame on close: {e}")
logger.warning(f"Failed to send black frame on close: {e}")
else:
logger.warning(
f"Adalight close skipped black frame: port={self._port} "
f"connected={self._connected} serial={self._serial is not None} "
f"is_open={self._serial.is_open if self._serial else 'N/A'} "
f"led_count={self._led_count}"
)
self._connected = False
if self._serial and self._serial.is_open:
try: