From 163b86909cd363fe8a5b62fcea540b6b9d3960a2 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 27 Jan 2026 18:02:58 +0100 Subject: [PATCH 01/19] Solve monthly overflow as reported in #399 --- plugwise_usb/nodes/circle.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/circle.py b/plugwise_usb/nodes/circle.py index 1ff06fb92..935f6c2ae 100644 --- a/plugwise_usb/nodes/circle.py +++ b/plugwise_usb/nodes/circle.py @@ -3,6 +3,7 @@ from __future__ import annotations from asyncio import CancelledError, Task, create_task, gather, sleep +import calendar from collections.abc import Awaitable, Callable from dataclasses import replace from datetime import UTC, datetime, timedelta @@ -881,8 +882,13 @@ async def clock_synchronize(self) -> bool: dt_now = datetime.now(tz=UTC) days_diff = (response.day_of_week.value - dt_now.weekday()) % 7 + last_day_of_month = calendar.monthrange(dt_now.year, dt_now.month)[1] + days_to_end_of_month = last_day_of_month - dt_now.day + corrected_day = dt_now.day + days_diff + if (difference := days_diff - days_to_end_of_month) > 0: + corrected_day = difference circle_timestamp: datetime = dt_now.replace( - day=dt_now.day + days_diff, + day=corrected_day, hour=response.time.value.hour, minute=response.time.value.minute, second=response.time.value.second, From c7ffc60df532f664d10e75164abe9f24c4985c75 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 27 Jan 2026 19:26:17 +0100 Subject: [PATCH 02/19] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 827b77ca6..71f7d9766 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Ongoing +- PR [400](https://github.com/plugwise/python-plugwise-usb/pull/400): Fix for Issue [#399](https://github.com/plugwise/python-plugwise-usb/issues/399) - Test/validate for Python 3.14 ## v0.47.1 - 2025-09-27 From 06d41ad427912fb36a8b11d19fddb710faeec673 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 27 Jan 2026 20:10:19 +0100 Subject: [PATCH 03/19] Implement simpler suggestion --- plugwise_usb/nodes/circle.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/plugwise_usb/nodes/circle.py b/plugwise_usb/nodes/circle.py index 935f6c2ae..0f671ded4 100644 --- a/plugwise_usb/nodes/circle.py +++ b/plugwise_usb/nodes/circle.py @@ -3,7 +3,6 @@ from __future__ import annotations from asyncio import CancelledError, Task, create_task, gather, sleep -import calendar from collections.abc import Awaitable, Callable from dataclasses import replace from datetime import UTC, datetime, timedelta @@ -882,13 +881,9 @@ async def clock_synchronize(self) -> bool: dt_now = datetime.now(tz=UTC) days_diff = (response.day_of_week.value - dt_now.weekday()) % 7 - last_day_of_month = calendar.monthrange(dt_now.year, dt_now.month)[1] - days_to_end_of_month = last_day_of_month - dt_now.day - corrected_day = dt_now.day + days_diff - if (difference := days_diff - days_to_end_of_month) > 0: - corrected_day = difference - circle_timestamp: datetime = dt_now.replace( - day=corrected_day, + target_date = dt_now + timedelta(days=days_diff) + circle_timestamp = target_date.replace( + day=target_date.day, hour=response.time.value.hour, minute=response.time.value.minute, second=response.time.value.second, From f1b840274bf49408e5fbdf326a16b8fad7de7985 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 27 Jan 2026 20:29:26 +0100 Subject: [PATCH 04/19] Modulo 7 is not required --- plugwise_usb/nodes/circle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/circle.py b/plugwise_usb/nodes/circle.py index 0f671ded4..46a720d46 100644 --- a/plugwise_usb/nodes/circle.py +++ b/plugwise_usb/nodes/circle.py @@ -880,7 +880,7 @@ async def clock_synchronize(self) -> bool: return False dt_now = datetime.now(tz=UTC) - days_diff = (response.day_of_week.value - dt_now.weekday()) % 7 + days_diff = response.day_of_week.value - dt_now.weekday() target_date = dt_now + timedelta(days=days_diff) circle_timestamp = target_date.replace( day=target_date.day, From dec3280f9f62b5953fede0ce566a1f53c066e880 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 27 Jan 2026 20:32:01 +0100 Subject: [PATCH 05/19] Simplify as suggested --- plugwise_usb/nodes/circle.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugwise_usb/nodes/circle.py b/plugwise_usb/nodes/circle.py index 46a720d46..833c3f8c3 100644 --- a/plugwise_usb/nodes/circle.py +++ b/plugwise_usb/nodes/circle.py @@ -883,7 +883,6 @@ async def clock_synchronize(self) -> bool: days_diff = response.day_of_week.value - dt_now.weekday() target_date = dt_now + timedelta(days=days_diff) circle_timestamp = target_date.replace( - day=target_date.day, hour=response.time.value.hour, minute=response.time.value.minute, second=response.time.value.second, From 2171abd6b6b3f4cf01878e47231e437dc99b1595 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 28 Jan 2026 07:59:54 +0100 Subject: [PATCH 06/19] Add clock-sync testcase to test monthly rollover --- tests/test_usb.py | 55 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/tests/test_usb.py b/tests/test_usb.py index 7de3159ab..93c4bbb36 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -3039,3 +3039,58 @@ def fake_cache_bool(dummy: object, setting: str) -> bool | None: with patch("aiofiles.threadpool.sync_open", return_value=mock_file_stream): await stick.disconnect() await asyncio.sleep(1) + + @freeze_time("2026-01-31 10:30:00", real_asyncio=True) + @pytest.mark.asyncio + async def test_clock_synchronize_month_overflow( + self, monkeypatch: pytest.MonkeyPatch + ) -> None: + """Test clock_synchronize handles month-end date rollover correctly. + + Regression test for issue `#399`: ensures that when the Circle's day_of_week + differs from the current weekday near month-end, the date calculation + doesn't attempt an invalid day value (e.g., Jan 32). + """ + mock_serial = MockSerial(None) + monkeypatch.setattr( + pw_connection_manager, + "create_serial_connection", + mock_serial.mock_connection, + ) + monkeypatch.setattr(pw_sender, "STICK_TIME_OUT", 0.2) + monkeypatch.setattr(pw_requests, "NODE_TIME_OUT", 2.0) + + stick = pw_stick.Stick("test_port", cache_enabled=False) + await stick.connect() + await stick.initialize() + await stick.discover_nodes(load=False) + await self._wait_for_scan(stick) + + # Get a Circle node + circle_node = stick.nodes.get("0098765432101234") + assert circle_node is not None + await circle_node.load() + + # Mock CircleClockGetRequest.send() to return a response where + # day_of_week is Saturday (5) while frozen time is Friday (4), Jan 31 + async def mock_clock_get_send(self): + response = pw_responses.CircleClockResponse() + response.timestamp = dt.now(tz=UTC) + # Set day_of_week to Saturday (5), requiring +1 day from Friday Jan 31 + # Old code: Jan 31 + 1 = day 32 (ValueError) + # New code: Jan 31 + timedelta(days=1) = Feb 1 (correct) + response.day_of_week.value = 5 # Saturday + response.time.value = dt.now(tz=UTC).time() + return response + + monkeypatch.setattr( + pw_requests.CircleClockGetRequest, + "send", + mock_clock_get_send, + ) + + # This should not raise ValueError about invalid day + result = await circle_node.clock_synchronize() + assert result is True + + await stick.disconnect() \ No newline at end of file From 75c87b9c7780c63b3d694dfd8167e6f89dad5419 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 28 Jan 2026 08:18:34 +0100 Subject: [PATCH 07/19] Extend stick_test_data --- tests/stick_test_data.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/stick_test_data.py b/tests/stick_test_data.py index b70ca5239..a1744428b 100644 --- a/tests/stick_test_data.py +++ b/tests/stick_test_data.py @@ -609,6 +609,11 @@ b"000000C1", # Success ack b"0000" + b"00D7" + b"0098765432101234", # msg_id, clock_ack, mac ), + b"\x05\x05\x03\x0300280098765432101234003010053101261F3D\r\n": ( + "Circle+ Realtime set clock at month-end for 0098765432101234", + b"000000C1", # Success ack + b"0000" + b"00D7" + b"0098765432101234", # msg_id, clock_ack, mac + ), b"\x05\x05\x03\x03003E11111111111111111B8A\r\n": ( "clock for 0011111111111111", b"000000C1", # Success ack From 4ae27e8f8ceff01df092bbf83e80df06d41303ce Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 28 Jan 2026 08:22:35 +0100 Subject: [PATCH 08/19] Fix end of file --- tests/test_usb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_usb.py b/tests/test_usb.py index 93c4bbb36..2818d7867 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -3093,4 +3093,4 @@ async def mock_clock_get_send(self): result = await circle_node.clock_synchronize() assert result is True - await stick.disconnect() \ No newline at end of file + await stick.disconnect() From b1dc7331fab331987c90b2ba2c367dc604c6dfe7 Mon Sep 17 00:00:00 2001 From: autoruff Date: Wed, 28 Jan 2026 07:23:14 +0000 Subject: [PATCH 09/19] fixup: fix-399 Python code fixed using Ruff --- tests/stick_test_data.py | 2 +- tests/test_usb.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/stick_test_data.py b/tests/stick_test_data.py index a1744428b..722ea2463 100644 --- a/tests/stick_test_data.py +++ b/tests/stick_test_data.py @@ -613,7 +613,7 @@ "Circle+ Realtime set clock at month-end for 0098765432101234", b"000000C1", # Success ack b"0000" + b"00D7" + b"0098765432101234", # msg_id, clock_ack, mac - ), + ), b"\x05\x05\x03\x03003E11111111111111111B8A\r\n": ( "clock for 0011111111111111", b"000000C1", # Success ack diff --git a/tests/test_usb.py b/tests/test_usb.py index 2818d7867..dd4be8071 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -3046,7 +3046,7 @@ async def test_clock_synchronize_month_overflow( self, monkeypatch: pytest.MonkeyPatch ) -> None: """Test clock_synchronize handles month-end date rollover correctly. - + Regression test for issue `#399`: ensures that when the Circle's day_of_week differs from the current weekday near month-end, the date calculation doesn't attempt an invalid day value (e.g., Jan 32). @@ -3065,12 +3065,12 @@ async def test_clock_synchronize_month_overflow( await stick.initialize() await stick.discover_nodes(load=False) await self._wait_for_scan(stick) - + # Get a Circle node circle_node = stick.nodes.get("0098765432101234") assert circle_node is not None await circle_node.load() - + # Mock CircleClockGetRequest.send() to return a response where # day_of_week is Saturday (5) while frozen time is Friday (4), Jan 31 async def mock_clock_get_send(self): @@ -3082,15 +3082,15 @@ async def mock_clock_get_send(self): response.day_of_week.value = 5 # Saturday response.time.value = dt.now(tz=UTC).time() return response - + monkeypatch.setattr( pw_requests.CircleClockGetRequest, "send", mock_clock_get_send, ) - + # This should not raise ValueError about invalid day result = await circle_node.clock_synchronize() assert result is True - + await stick.disconnect() From d0680291b9232efcdd4fdd0418a4cd267dfc4d38 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 28 Jan 2026 08:27:46 +0100 Subject: [PATCH 10/19] Change to non-async function --- tests/test_usb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_usb.py b/tests/test_usb.py index dd4be8071..a18fa4d0a 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -3073,7 +3073,7 @@ async def test_clock_synchronize_month_overflow( # Mock CircleClockGetRequest.send() to return a response where # day_of_week is Saturday (5) while frozen time is Friday (4), Jan 31 - async def mock_clock_get_send(self): + def mock_clock_get_send(self): response = pw_responses.CircleClockResponse() response.timestamp = dt.now(tz=UTC) # Set day_of_week to Saturday (5), requiring +1 day from Friday Jan 31 From 54de6e2413ad21c1e030a49dd7993e878bb7310b Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 28 Jan 2026 08:34:28 +0100 Subject: [PATCH 11/19] Implement suggested correction --- tests/test_usb.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_usb.py b/tests/test_usb.py index a18fa4d0a..5f50cd8e6 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -3072,14 +3072,14 @@ async def test_clock_synchronize_month_overflow( await circle_node.load() # Mock CircleClockGetRequest.send() to return a response where - # day_of_week is Saturday (5) while frozen time is Friday (4), Jan 31 + # day_of_week is Sunday (6) while frozen time is Saturday (5), Jan 31 def mock_clock_get_send(self): response = pw_responses.CircleClockResponse() response.timestamp = dt.now(tz=UTC) - # Set day_of_week to Saturday (5), requiring +1 day from Friday Jan 31 + # Set day_of_week to Sunday (6), requiring +1 day from Saturday Jan 31 # Old code: Jan 31 + 1 = day 32 (ValueError) # New code: Jan 31 + timedelta(days=1) = Feb 1 (correct) - response.day_of_week.value = 5 # Saturday + response.day_of_week.value = 6 # Sunday response.time.value = dt.now(tz=UTC).time() return response From 5d3a91140e91262bc0d16d41c070c5d45b069be9 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 28 Jan 2026 19:14:18 +0100 Subject: [PATCH 12/19] Make sure to test circle clock-sync --- tests/stick_test_data.py | 6 +++--- tests/test_usb.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/stick_test_data.py b/tests/stick_test_data.py index 722ea2463..1b1a18f39 100644 --- a/tests/stick_test_data.py +++ b/tests/stick_test_data.py @@ -609,10 +609,10 @@ b"000000C1", # Success ack b"0000" + b"00D7" + b"0098765432101234", # msg_id, clock_ack, mac ), - b"\x05\x05\x03\x0300280098765432101234003010053101261F3D\r\n": ( - "Circle+ Realtime set clock at month-end for 0098765432101234", + b"\x05\x05\x03\x0300281111111111111111003010053101261F3D\r\n": ( + "Circle+ Realtime set clock at month-end for 1111111111111111", b"000000C1", # Success ack - b"0000" + b"00D7" + b"0098765432101234", # msg_id, clock_ack, mac + b"0000" + b"00D7" + b"1111111111111111", # msg_id, clock_ack, mac ), b"\x05\x05\x03\x03003E11111111111111111B8A\r\n": ( "clock for 0011111111111111", diff --git a/tests/test_usb.py b/tests/test_usb.py index 5f50cd8e6..377f89a60 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -3067,7 +3067,7 @@ async def test_clock_synchronize_month_overflow( await self._wait_for_scan(stick) # Get a Circle node - circle_node = stick.nodes.get("0098765432101234") + circle_node = stick.nodes.get("1111111111111111") assert circle_node is not None await circle_node.load() From 81a2ec3451b8d454de54cc6e8e43920b53e1910e Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 28 Jan 2026 19:19:25 +0100 Subject: [PATCH 13/19] Try --- tests/test_usb.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/test_usb.py b/tests/test_usb.py index 377f89a60..c8daa3482 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -3073,24 +3073,24 @@ async def test_clock_synchronize_month_overflow( # Mock CircleClockGetRequest.send() to return a response where # day_of_week is Sunday (6) while frozen time is Saturday (5), Jan 31 - def mock_clock_get_send(self): - response = pw_responses.CircleClockResponse() - response.timestamp = dt.now(tz=UTC) + # def mock_clock_get_send(self): + # response = pw_responses.CircleClockResponse() + # response.timestamp = dt.now(tz=UTC) # Set day_of_week to Sunday (6), requiring +1 day from Saturday Jan 31 # Old code: Jan 31 + 1 = day 32 (ValueError) # New code: Jan 31 + timedelta(days=1) = Feb 1 (correct) - response.day_of_week.value = 6 # Sunday - response.time.value = dt.now(tz=UTC).time() - return response + # response.day_of_week.value = 6 # Sunday + # response.time.value = dt.now(tz=UTC).time() + # return response - monkeypatch.setattr( - pw_requests.CircleClockGetRequest, - "send", - mock_clock_get_send, - ) + # monkeypatch.setattr( + # pw_requests.CircleClockGetRequest, + # "send", + # mock_clock_get_send, + # ) # This should not raise ValueError about invalid day - result = await circle_node.clock_synchronize() - assert result is True + # result = await circle_node.clock_synchronize() + # assert result is True await stick.disconnect() From c9293adb064bb39147f29d245eb2fda754bcf68a Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 28 Jan 2026 19:59:24 +0100 Subject: [PATCH 14/19] Simplify testcase, check for occurrence of ValueError --- tests/test_usb.py | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/tests/test_usb.py b/tests/test_usb.py index c8daa3482..b8a6e94fc 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -3067,30 +3067,14 @@ async def test_clock_synchronize_month_overflow( await self._wait_for_scan(stick) # Get a Circle node - circle_node = stick.nodes.get("1111111111111111") - assert circle_node is not None - await circle_node.load() - - # Mock CircleClockGetRequest.send() to return a response where - # day_of_week is Sunday (6) while frozen time is Saturday (5), Jan 31 - # def mock_clock_get_send(self): - # response = pw_responses.CircleClockResponse() - # response.timestamp = dt.now(tz=UTC) - # Set day_of_week to Sunday (6), requiring +1 day from Saturday Jan 31 - # Old code: Jan 31 + 1 = day 32 (ValueError) - # New code: Jan 31 + timedelta(days=1) = Feb 1 (correct) - # response.day_of_week.value = 6 # Sunday - # response.time.value = dt.now(tz=UTC).time() - # return response - - # monkeypatch.setattr( - # pw_requests.CircleClockGetRequest, - # "send", - # mock_clock_get_send, - # ) - - # This should not raise ValueError about invalid day - # result = await circle_node.clock_synchronize() - # assert result is True + circle = stick.nodes.get("1111111111111111") + assert circle is not None + result = True + try: + await circle.load() + except ValueError: + result = False + + assert result await stick.disconnect() From 602797db3dfa7e4af6082d6f6354895568ecb849 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 29 Jan 2026 18:10:04 +0100 Subject: [PATCH 15/19] Circle: line up clock-sync method with CirclePlus --- plugwise_usb/nodes/circle.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/plugwise_usb/nodes/circle.py b/plugwise_usb/nodes/circle.py index 833c3f8c3..d3b39ead2 100644 --- a/plugwise_usb/nodes/circle.py +++ b/plugwise_usb/nodes/circle.py @@ -880,9 +880,26 @@ async def clock_synchronize(self) -> bool: return False dt_now = datetime.now(tz=UTC) - days_diff = response.day_of_week.value - dt_now.weekday() - target_date = dt_now + timedelta(days=days_diff) - circle_timestamp = target_date.replace( + dt_now_date = dt_now.replace(hour=0, minute=0, second=0, microsecond=0) + response_date = datetime( + response.date.value.year, + response.date.value.month, + response.date.value.day, + hour=0, + minute=0, + second=0, + microsecond=0, + tzinfo=UTC, + ) + if dt_now_date != response_date: + _LOGGER.info( + "Sync clock of node %s because time has drifted %s days", + self._mac_in_str, + int(abs((dt_now_date - response_date).days)), + ) + return await self._send_clock_set_req() + + circle_timestamp: datetime = dt_now.replace( hour=response.time.value.hour, minute=response.time.value.minute, second=response.time.value.second, @@ -902,7 +919,10 @@ async def clock_synchronize(self) -> bool: raise NodeError( "Unable to synchronize clock when protocol version is unknown" ) + return await self._send_clock_set_req() + async def _send_clock_set_req(self) -> bool: + """Send CirclePlusRealTimeClockSetRequest.""" set_request = CircleClockSetRequest( self._send, self._mac_in_bytes, From ac4f0d7704c803a4f6da7524249b72cf2b5e52a8 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 29 Jan 2026 18:18:14 +0100 Subject: [PATCH 16/19] Adapt to difference in response format --- plugwise_usb/nodes/circle.py | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/plugwise_usb/nodes/circle.py b/plugwise_usb/nodes/circle.py index d3b39ead2..b57185f6c 100644 --- a/plugwise_usb/nodes/circle.py +++ b/plugwise_usb/nodes/circle.py @@ -880,22 +880,10 @@ async def clock_synchronize(self) -> bool: return False dt_now = datetime.now(tz=UTC) - dt_now_date = dt_now.replace(hour=0, minute=0, second=0, microsecond=0) - response_date = datetime( - response.date.value.year, - response.date.value.month, - response.date.value.day, - hour=0, - minute=0, - second=0, - microsecond=0, - tzinfo=UTC, - ) - if dt_now_date != response_date: + if dt_now.weekday() != response.day_of_week.value: _LOGGER.info( - "Sync clock of node %s because time has drifted %s days", + "Sync clock of node %s because time has drifted more than 1 day", self._mac_in_str, - int(abs((dt_now_date - response_date).days)), ) return await self._send_clock_set_req() From 1464f9dad0057db9892920d5d9ce30b650e63e43 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 29 Jan 2026 18:25:42 +0100 Subject: [PATCH 17/19] Fix docstring --- plugwise_usb/nodes/circle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/circle.py b/plugwise_usb/nodes/circle.py index b57185f6c..f3f0dfe52 100644 --- a/plugwise_usb/nodes/circle.py +++ b/plugwise_usb/nodes/circle.py @@ -910,7 +910,7 @@ async def clock_synchronize(self) -> bool: return await self._send_clock_set_req() async def _send_clock_set_req(self) -> bool: - """Send CirclePlusRealTimeClockSetRequest.""" + """Send CircleClockSetRequest.""" set_request = CircleClockSetRequest( self._send, self._mac_in_bytes, From 1436bc0aa62706c30ec7eec9c71d6b4088940882 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 29 Jan 2026 18:46:33 +0100 Subject: [PATCH 18/19] Fix missing _node_protocols check --- plugwise_usb/nodes/circle.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugwise_usb/nodes/circle.py b/plugwise_usb/nodes/circle.py index f3f0dfe52..c9f99fb97 100644 --- a/plugwise_usb/nodes/circle.py +++ b/plugwise_usb/nodes/circle.py @@ -903,14 +903,15 @@ async def clock_synchronize(self) -> bool: self._mac_in_str, int(abs(clock_offset.total_seconds())), ) + return await self._send_clock_set_req() + + async def _send_clock_set_req(self) -> bool: + """Send CircleClockSetRequest.""" if self._node_protocols is None: raise NodeError( "Unable to synchronize clock when protocol version is unknown" ) - return await self._send_clock_set_req() - async def _send_clock_set_req(self) -> bool: - """Send CircleClockSetRequest.""" set_request = CircleClockSetRequest( self._send, self._mac_in_bytes, From 574a483894ce374ebb2a8ced4a44543bb5565269 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 29 Jan 2026 18:54:50 +0100 Subject: [PATCH 19/19] Bump version to v0.47.2 --- CHANGELOG.md | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71f7d9766..3d7f9cac0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Ongoing +## v0.47.2 - 2026-01-29 - PR [400](https://github.com/plugwise/python-plugwise-usb/pull/400): Fix for Issue [#399](https://github.com/plugwise/python-plugwise-usb/issues/399) - Test/validate for Python 3.14 diff --git a/pyproject.toml b/pyproject.toml index 284719c65..a674759e6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "0.47.1" +version = "0.47.2" license = "MIT" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [