-
-
Notifications
You must be signed in to change notification settings - Fork 39
Listen for preset changes #1931
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,6 +4,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import asyncio | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import socket | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import time | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from dataclasses import dataclass | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from typing import TYPE_CHECKING, Any, Self | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -30,6 +31,12 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from .const import LiveDataOverride | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @dataclass | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class _PresetsVersion: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| presets_modified_timestamp: int | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| boot_time: int | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @dataclass | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class WLED: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Main class for handling connections with WLED.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -41,6 +48,7 @@ class WLED: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _client: aiohttp.ClientWebSocketResponse | None = None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _close_session: bool = False | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _device: Device | None = None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _presets_version: _PresetsVersion | None = None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @property | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def connected(self) -> bool: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -119,7 +127,21 @@ async def listen(self, callback: Callable[[Device], None]) -> None: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if message.type == aiohttp.WSMsgType.TEXT: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message_data = message.json() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| presets_changed, new_presets_version = self._check_presets_version( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message_data | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if presets_changed: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not (presets := await self.request("/presets.json")): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| err_msg = ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| f"WLED device at {self.host} returned an empty API" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| " response on presets update", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise WLEDConnectionError(WLEDEmptyResponseError(err_msg)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message_data["presets"] = presets | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| device = self._device.update_from_dict(data=message_data) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._presets_version = new_presets_version | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| callback(device) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if message.type in ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -256,19 +278,22 @@ async def update(self) -> Device: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise WLEDEmptyResponseError(msg) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not (presets := await self.request("/presets.json")): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| msg = ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| f"WLED device at {self.host} returned an empty API" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| " response on presets update", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise WLEDEmptyResponseError(msg) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data["presets"] = presets | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| presets_changed, new_presets_version = self._check_presets_version(data) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if presets_changed: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not (presets := await self.request("/presets.json")): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| msg = ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| f"WLED device at {self.host} returned an empty API" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| " response on presets update", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise WLEDEmptyResponseError(msg) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data["presets"] = presets | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not self._device: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._device = Device.from_dict(data) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._device.update_from_dict(data) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._presets_version = new_presets_version | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+281
to
+296
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix error message construction. The error message at lines 284-288 has the same trailing comma issue as in the 🔎 Proposed fix presets_changed, new_presets_version = self._check_presets_version(data)
if presets_changed:
if not (presets := await self.request("/presets.json")):
- msg = (
- f"WLED device at {self.host} returned an empty API"
- " response on presets update",
- )
+ msg = (
+ f"WLED device at {self.host} returned an empty API"
+ " response on presets update"
+ )
raise WLEDEmptyResponseError(msg)📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return self._device | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def master( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -703,6 +728,59 @@ async def __aexit__(self, *_exc_info: object) -> None: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await self.close() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def _check_presets_version( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self, device_data: Any | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) -> tuple[bool, _PresetsVersion | None]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Check if the presets have possibly been changed since last check. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Returns | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ------- | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Tuple where first element denotes if presets have possibly changed, and | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| the second element the new version tuple. If version cannot be parsed, the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| version is None. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # pylint: disable=too-many-boolean-expressions | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| not isinstance(device_data, dict) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| or not (info := device_data.get("info")) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| or not (uptime := info.get("uptime")) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| or not (fs := info.get("fs")) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| or not (pmt := fs.get("pmt")) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return (True, None) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| presets_modified_timestamp = int(pmt) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| uptime_seconds = int(uptime) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| current_time_seconds = int(time.time()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| boot_time_approx = current_time_seconds - uptime_seconds | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| new_version = _PresetsVersion( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| presets_modified_timestamp, int(boot_time_approx) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Presets are the same if the presets last modified timestamp has not been | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # modified. Since the last modified timestamp is only stored in memory, it | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # will be reset to 0 on device restart, and we might miss an update. Detect | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # device restarts by tracking the boot time. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Since we are approximating the time, allow 2 seconds changes for rounding | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # errors and network delay. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # We could get a better approximation of the boot time by using the device | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # time from info.time. The device time is however unreliable, especially | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # around boot. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| changed = ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._presets_version is None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| or self._presets_version.presets_modified_timestamp | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| != new_version.presets_modified_timestamp | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| or abs(self._presets_version.boot_time - new_version.boot_time) > 2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except ValueError: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # In case of a parse failure, assume presets might have changed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return (True, None) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return (changed, new_version) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @dataclass | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class WLEDReleases: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix error message construction.
The error message at lines 136-140 has a trailing comma that creates a tuple instead of a string:
This results in
err_msgbeing a tuple("message",)rather than a string, which is then incorrectly passed toWLEDEmptyResponseError.🔎 Proposed fix
if presets_changed: if not (presets := await self.request("/presets.json")): - err_msg = ( - f"WLED device at {self.host} returned an empty API" - " response on presets update", - ) + err_msg = ( + f"WLED device at {self.host} returned an empty API" + " response on presets update" + ) raise WLEDConnectionError(WLEDEmptyResponseError(err_msg))🤖 Prompt for AI Agents