From a056e982697e7bbf97b2a3e13929c72814089451 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 3 Mar 2025 08:03:18 +0100 Subject: [PATCH 001/157] Handle negative energy-pulse values --- plugwise_usb/nodes/helpers/counter.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index f894d8244..0b6e0566b 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -261,7 +261,12 @@ def energy(self) -> float | None: return None if self._pulses == 0: return 0.0 - pulses_per_s = self._pulses / float(HOUR_IN_SECONDS) + # Handle both positive and negative pulses values + negative = False + if self._pulses < 0: + negative = True + + pulses_per_s = abs(self._pulses) / float(HOUR_IN_SECONDS) corrected_pulses = HOUR_IN_SECONDS * ( ( ( @@ -276,8 +281,8 @@ def energy(self) -> float | None: + self._calibration.off_tot ) calc_value = corrected_pulses / PULSES_PER_KW_SECOND / HOUR_IN_SECONDS - # Guard for minor negative miscalculations - calc_value = max(calc_value, 0.0) + if negative: + calc_value = -calc_value return calc_value @property From bc58070cb2a55a0f77fd2b96df708a95c8f01e93 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 5 Mar 2025 09:26:16 +0100 Subject: [PATCH 002/157] Extend logging showing production pulses --- plugwise_usb/nodes/helpers/counter.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index 0b6e0566b..91b164d47 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -105,11 +105,8 @@ def add_pulse_stats( self, pulses_consumed: int, pulses_produced: int, timestamp: datetime ) -> None: """Add pulse statistics.""" - _LOGGER.debug( - "add_pulse_stats | consumed=%s, for %s", - str(pulses_consumed), - self._mac, - ) + _LOGGER.debug("add_pulse_stats for %s with timestamp=%s", self._mac, timestamp) + _LOGGER.debug("consumed=%s | produced=%s", pulses_consumed, pulses_produced) self._pulse_collection.update_pulse_counter( pulses_consumed, pulses_produced, timestamp ) From 6e10e939fdaa2b1a37e7895bb7f78b2d13dbc420 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 10 Mar 2025 11:38:03 +0100 Subject: [PATCH 003/157] Add debug-message showing CircleEnergyLog data --- plugwise_usb/nodes/circle.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/circle.py b/plugwise_usb/nodes/circle.py index 7daa30265..7f77c2651 100644 --- a/plugwise_usb/nodes/circle.py +++ b/plugwise_usb/nodes/circle.py @@ -504,7 +504,12 @@ async def energy_log_update(self, address: int | None) -> bool: # energy pulses collected during the previous hour of given timestamp for _slot in range(4, 0, -1): log_timestamp, log_pulses = response.log_data[_slot] - + _LOGGER.debug( + "Energy data from slot=%s: pulses=%s, timestamp=%s", + _slot, + log_pulses, + log_timestamp + ) if log_timestamp is None or log_pulses is None: self._energy_counters.add_empty_log(response.log_address, _slot) elif await self._energy_log_record_update_state( From 944e34c746cbbdaeff8d1d450573dc69e4b08a21 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 23 Mar 2025 10:27:09 +0100 Subject: [PATCH 004/157] Add MAC to CircleEnergy log-message --- plugwise_usb/nodes/circle.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugwise_usb/nodes/circle.py b/plugwise_usb/nodes/circle.py index 7f77c2651..deb7d7817 100644 --- a/plugwise_usb/nodes/circle.py +++ b/plugwise_usb/nodes/circle.py @@ -496,6 +496,7 @@ async def energy_log_update(self, address: int | None) -> bool: ) return False + _LOGGER.debug("EnergyLogs data from %s, address=%s", self._mac_in_str, address) await self._available_update_state(True, response.timestamp) energy_record_update = False From 890bfbce58c82c4d05b5c34901b673bd5aa84fda Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 13 Mar 2025 20:07:32 +0100 Subject: [PATCH 005/157] Add collected_pulses debug-logging --- plugwise_usb/nodes/helpers/pulses.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index a3c0f6511..25d7e7d12 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -196,6 +196,13 @@ def collected_pulses( is_consumption, ) return (None, None) + _LOGGER.debug( + "collected_pulses | pulses=%s | log_pulses=%s | consumption=%s at timestamp=%s", + pulses, + log_pulses, + is_consumption, + timestamp, + ) return (pulses + log_pulses, timestamp) def _collect_pulses_from_logs( From 0b5c55322741b2ca56243c27ef1cf7268b60932e Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 13 Mar 2025 20:08:10 +0100 Subject: [PATCH 006/157] Fix typo in logging --- plugwise_usb/nodes/helpers/counter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index 91b164d47..4726ed320 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -326,5 +326,5 @@ def update( self._pulses = pulses energy = self.energy - _LOGGER.debug("energy=%s or last_update=%s", energy, last_update) + _LOGGER.debug("energy=%s on last_update=%s", energy, last_update) return (energy, last_reset) From f4ad4ab9cc22954e2d5c10a8e3e4f50e7be6be86 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 14 Mar 2025 13:21:26 +0100 Subject: [PATCH 007/157] Extend collected_pulses logging --- plugwise_usb/nodes/helpers/pulses.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 25d7e7d12..d753891c4 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -159,24 +159,28 @@ def collected_pulses( self, from_timestamp: datetime, is_consumption: bool ) -> tuple[int | None, datetime | None]: """Calculate total pulses from given timestamp.""" - - # _LOGGER.debug("collected_pulses | %s | is_cons=%s, from=%s", self._mac, is_consumption, from_timestamp) - + _LOGGER.debug( + "collected_pulses 1 | %s | is_cons=%s, from_timestamp=%s", + self._mac, + is_consumption, + from_timestamp, + ) + _LOGGER.debug("collected_pulses 1a | _log_production=%s", self._log_production) if not is_consumption: if self._log_production is None or not self._log_production: return (None, None) if is_consumption and self._rollover_consumption: - _LOGGER.debug("collected_pulses | %s | _rollover_consumption", self._mac) + _LOGGER.debug("collected_pulses 2 | %s | _rollover_consumption", self._mac) return (None, None) if not is_consumption and self._rollover_production: - _LOGGER.debug("collected_pulses | %s | _rollover_production", self._mac) + _LOGGER.debug("collected_pulses 3 | %s | _rollover_production", self._mac) return (None, None) if ( log_pulses := self._collect_pulses_from_logs(from_timestamp, is_consumption) ) is None: - _LOGGER.debug("collected_pulses | %s | log_pulses:None", self._mac) + _LOGGER.debug("collected_pulses 4 | %s | log_pulses:None", self._mac) return (None, None) pulses: int | None = None @@ -191,13 +195,13 @@ def collected_pulses( if pulses is None: _LOGGER.debug( - "collected_pulses | %s | is_consumption=%s, pulses=None", + "collected_pulses 5 | %s | is_consumption=%s, pulses=None", self._mac, is_consumption, ) return (None, None) _LOGGER.debug( - "collected_pulses | pulses=%s | log_pulses=%s | consumption=%s at timestamp=%s", + "collected_pulses 6 | pulses=%s | log_pulses=%s | consumption=%s at timestamp=%s", pulses, log_pulses, is_consumption, From 2bc498b24e8f3a08ca533b03c5a3a25f039761ba Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 23 Mar 2025 10:35:51 +0100 Subject: [PATCH 008/157] Add_log(): support both cons-/prod-type of logs --- plugwise_usb/nodes/helpers/pulses.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index d753891c4..d3763e10e 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -384,7 +384,11 @@ def add_log( import_only: bool = False, ) -> bool: """Store pulse log.""" - log_record = PulseLogRecord(timestamp, pulses, CONSUMED) + direction = CONSUMED + if self._log_production and pulses < 0: + direction = PRODUCED + + log_record = PulseLogRecord(timestamp, pulses, direction) if not self._add_log_record(address, slot, log_record): if not self._log_exists(address, slot): return False From d585a88fdda249fc8a865fa0205c92bfa6031bb3 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 16 Mar 2025 12:17:42 +0100 Subject: [PATCH 009/157] Add missing await for energy_log_update() --- plugwise_usb/nodes/circle.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugwise_usb/nodes/circle.py b/plugwise_usb/nodes/circle.py index deb7d7817..e08b23ac7 100644 --- a/plugwise_usb/nodes/circle.py +++ b/plugwise_usb/nodes/circle.py @@ -2,7 +2,7 @@ from __future__ import annotations -from asyncio import Task, create_task +from asyncio import Task, create_task, gather from collections.abc import Awaitable, Callable from dataclasses import replace from datetime import UTC, datetime @@ -440,6 +440,7 @@ async def get_missing_energy_logs(self) -> None: self._energy_counters.update() if self._current_log_address is None: return None + if self._energy_counters.log_addresses_missing is None: _LOGGER.debug( "Start with initial energy request for the last 10 log addresses for node %s.", @@ -453,12 +454,13 @@ async def get_missing_energy_logs(self) -> None: log_address, _ = calc_log_address(log_address, 1, -4) total_addresses -= 1 - for task in log_update_tasks: - await task + await gather(*log_update_tasks) if self._cache_enabled: await self._energy_log_records_save_to_cache() + return + if self._energy_counters.log_addresses_missing is not None: _LOGGER.debug("Task created to get missing logs of %s", self._mac_in_str) if ( From 622b5ca719209c6c9e8ada7c1b5ead71cbde01f0 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 17 Mar 2025 12:20:42 +0100 Subject: [PATCH 010/157] Add missing line in _reset_log_references() --- plugwise_usb/nodes/helpers/pulses.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index d3763e10e..99a0d350d 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -624,6 +624,7 @@ def _reset_log_references(self) -> None: if self._last_log_production_timestamp is None: self._last_log_production_timestamp = log_record.timestamp if self._last_log_production_timestamp <= log_record.timestamp: + self._last_log_production_timestamp = log_record.timestamp self._last_log_production_address = address self._last_log_production_slot = slot From e4b109dda8f3062c0242977d433f9315302e9a0e Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 23 Mar 2025 10:40:06 +0100 Subject: [PATCH 011/157] Formatting, remove wrong guarding(?) --- plugwise_usb/nodes/helpers/pulses.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 99a0d350d..924a5f931 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -234,6 +234,7 @@ def _collect_pulses_from_logs( return None if from_timestamp > self._last_log_production_timestamp: return 0 + missing_logs = self._logs_missing(from_timestamp) if missing_logs is None or missing_logs: _LOGGER.debug( @@ -312,6 +313,7 @@ def _update_rollover(self) -> None: if not self._log_production: return + if ( self._last_log_production_timestamp is None or self._next_log_production_timestamp is None @@ -495,6 +497,7 @@ def _update_log_interval(self) -> None: self._log_production, ) return + last_cons_address, last_cons_slot = self._last_log_reference( is_consumption=True ) @@ -513,8 +516,7 @@ def _update_log_interval(self) -> None: delta1.total_seconds() / MINUTE_IN_SECONDS ) break - if not self._log_production: - return + address, slot = calc_log_address(address, slot, -1) if ( self._log_interval_consumption is not None From ae33e721b332493639141779c47062e6f680d8ac Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 18 Mar 2025 08:20:19 +0100 Subject: [PATCH 012/157] Don't update production_log_refs when not required --- plugwise_usb/nodes/helpers/pulses.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 924a5f931..bca4a5f5f 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -700,8 +700,7 @@ def _update_log_references(self, address: int, slot: int) -> None: if is_consumption: self._update_first_consumption_log_reference(address, slot, log_time_stamp) self._update_last_consumption_log_reference(address, slot, log_time_stamp) - else: - # production + elif self._log_production: self._update_first_production_log_reference(address, slot, log_time_stamp) self._update_last_production_log_reference(address, slot, log_time_stamp) From 9dcba8328faa8ef35ce2ff9ede6cd9b6091422f9 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 23 Mar 2025 10:41:28 +0100 Subject: [PATCH 013/157] Formatting --- plugwise_usb/nodes/helpers/pulses.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index bca4a5f5f..787d053aa 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -690,6 +690,7 @@ def _update_log_references(self, address: int, slot: int) -> None: """Update next expected log timestamps.""" if self._logs is None: return + log_time_stamp = self._logs[address][slot].timestamp is_consumption = self._logs[address][slot].is_consumption @@ -812,12 +813,14 @@ def _logs_missing(self, from_timestamp: datetime) -> list[int] | None: log_interval = self._log_interval_consumption elif self._log_interval_production is not None: log_interval = self._log_interval_production + if ( self._log_interval_production is not None and log_interval is not None and self._log_interval_production < log_interval ): log_interval = self._log_interval_production + if log_interval is None: return None @@ -877,7 +880,7 @@ def _missing_addresses_before( if self._log_interval_consumption == 0: pass - if self._log_production is not True: + if not self._log_production: #False expected_timestamp = ( self._logs[address][slot].timestamp - calc_interval_cons ) @@ -935,7 +938,7 @@ def _missing_addresses_after( # Use consumption interval calc_interval_cons = timedelta(minutes=self._log_interval_consumption) - if self._log_production is not True: + if not self._log_production: # False expected_timestamp = ( self._logs[address][slot].timestamp + calc_interval_cons ) From 9c59cbd69b5d0904f9129878e56ed45451161e8c Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 23 Mar 2025 10:43:30 +0100 Subject: [PATCH 014/157] Add guarding for production _energy_statistics --- plugwise_usb/nodes/helpers/counter.py | 28 +++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index 4726ed320..28677c33f 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -173,18 +173,22 @@ def update(self) -> None: self._energy_statistics.week_consumption_reset, ) = self._counters[EnergyType.CONSUMPTION_WEEK].update(self._pulse_collection) - ( - self._energy_statistics.hour_production, - self._energy_statistics.hour_production_reset, - ) = self._counters[EnergyType.PRODUCTION_HOUR].update(self._pulse_collection) - ( - self._energy_statistics.day_production, - self._energy_statistics.day_production_reset, - ) = self._counters[EnergyType.PRODUCTION_DAY].update(self._pulse_collection) - ( - self._energy_statistics.week_production, - self._energy_statistics.week_production_reset, - ) = self._counters[EnergyType.PRODUCTION_WEEK].update(self._pulse_collection) + if self._pulse_collection.production_logging: + self._energy_statistics.log_interval_production = ( + self._pulse_collection.log_interval_production + ) + ( + self._energy_statistics.hour_production, + self._energy_statistics.hour_production_reset, + ) = self._counters[EnergyType.PRODUCTION_HOUR].update(self._pulse_collection) + ( + self._energy_statistics.day_production, + self._energy_statistics.day_production_reset, + ) = self._counters[EnergyType.PRODUCTION_DAY].update(self._pulse_collection) + ( + self._energy_statistics.week_production, + self._energy_statistics.week_production_reset, + ) = self._counters[EnergyType.PRODUCTION_WEEK].update(self._pulse_collection) @property def timestamp(self) -> datetime | None: From 29a9252cab0206e38780035d4dc0f13806fc5fd4 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 18 Mar 2025 09:29:27 +0100 Subject: [PATCH 015/157] Don't update production rollover when no production --- plugwise_usb/nodes/helpers/pulses.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 787d053aa..65d5ef831 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -288,6 +288,7 @@ def _update_rollover(self) -> None: ): # Unable to determine rollover return + if self._pulses_timestamp > self._next_log_consumption_timestamp: self._rollover_consumption = True _LOGGER.debug( @@ -320,6 +321,10 @@ def _update_rollover(self) -> None: ): # Unable to determine rollover return + + if not self._log_production: + return + if self._pulses_timestamp > self._next_log_production_timestamp: self._rollover_production = True _LOGGER.debug( From cef0b6445a4d1991ac26a0e2e78d2d2b2fc4b032 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 22 Mar 2025 16:02:49 +0100 Subject: [PATCH 016/157] Update _update_rollover() docstring --- plugwise_usb/nodes/helpers/pulses.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 65d5ef831..1c3c412f4 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -278,9 +278,15 @@ def update_pulse_counter( self._pulses_production = pulses_produced def _update_rollover(self) -> None: - """Update rollover states. Returns True if rollover is applicable.""" + """Update rollover states. + + When the last found timestamp is outside the interval `_last_log_timestamp` + to `_next_log_timestamp` the pulses should not be counted as part of the + ongoing collection-interval. + """ if self._log_addresses_missing is not None and self._log_addresses_missing: return + if ( self._pulses_timestamp is None or self._last_log_consumption_timestamp is None From 59e76dd27307ba8a532c49050944cc2fc4614379 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 23 Mar 2025 10:53:08 +0100 Subject: [PATCH 017/157] Output production-statistics as positive values --- plugwise_usb/nodes/helpers/pulses.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 1c3c412f4..d8aa75d43 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -207,7 +207,9 @@ def collected_pulses( is_consumption, timestamp, ) - return (pulses + log_pulses, timestamp) + + # Always return positive values of energy_statistics + return (abs(pulses + log_pulses), timestamp) def _collect_pulses_from_logs( self, from_timestamp: datetime, is_consumption: bool From dbe9fd842088cc5a614d47ac630fa32aaf9bcbe9 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 23 Mar 2025 10:56:07 +0100 Subject: [PATCH 018/157] Bump to a55 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8f118df71..1d4f7ef3c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a33" +version = "v0.40.0a55" license = {file = "LICENSE"} description = "Plugwise USB (Stick) module for Python 3." readme = "README.md" From fe96f997b4b400e0fa06e5b9a5c6be7af1e6d41b Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 23 Mar 2025 11:08:23 +0100 Subject: [PATCH 019/157] Change-update _logs_missing logging --- plugwise_usb/nodes/helpers/pulses.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index d8aa75d43..e976eb266 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -803,11 +803,12 @@ def _logs_missing(self, from_timestamp: datetime) -> list[int] | None: if self._logs[address][slot].timestamp <= from_timestamp: break + _LOGGER.debug( + "_logs_missing | %s | missing in range=%s", self._mac, missing + ) + # return missing logs in range first if len(missing) > 0: - _LOGGER.debug( - "_logs_missing | %s | missing in range=%s", self._mac, missing - ) return missing if first_address not in self._logs: @@ -841,6 +842,11 @@ def _logs_missing(self, from_timestamp: datetime) -> list[int] | None: calculated_timestamp = self._logs[first_address][ first_slot ].timestamp - timedelta(minutes=log_interval) + _LOGGER.debug( + "_logs_missing | %s | calculated timestamp=%s", + self._mac, + calculated_timestamp, + ) while from_timestamp < calculated_timestamp: if ( address == self._first_empty_log_address From 026d11c40c57f627f6e32a91a5158dfd1fe22882 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 23 Mar 2025 11:11:43 +0100 Subject: [PATCH 020/157] Improve logging, formatting --- plugwise_usb/nodes/circle.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/circle.py b/plugwise_usb/nodes/circle.py index e08b23ac7..835365a8c 100644 --- a/plugwise_usb/nodes/circle.py +++ b/plugwise_usb/nodes/circle.py @@ -508,7 +508,7 @@ async def energy_log_update(self, address: int | None) -> bool: for _slot in range(4, 0, -1): log_timestamp, log_pulses = response.log_data[_slot] _LOGGER.debug( - "Energy data from slot=%s: pulses=%s, timestamp=%s", + "In slot=%s: pulses=%s, timestamp=%s", _slot, log_pulses, log_timestamp @@ -523,9 +523,11 @@ async def energy_log_update(self, address: int | None) -> bool: import_only=True, ): energy_record_update = True + self._energy_counters.update() if energy_record_update: await self.save_cache() + return True async def _energy_log_records_load_from_cache(self) -> bool: From 142b7f60c712dc495cc0d35c6ccb8c6edcc5bf04 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 23 Mar 2025 11:16:38 +0100 Subject: [PATCH 021/157] Bump to a56 --- pyproject.toml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1d4f7ef3c..c699178c5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,10 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a55" -license = {file = "LICENSE"} -description = "Plugwise USB (Stick) module for Python 3." -readme = "README.md" +version = "v0.40.0a56" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From 9ead2f5484f7622904d237171c20148e065223d3 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 23 Mar 2025 14:16:57 +0100 Subject: [PATCH 022/157] Pulses_production/_produced are negative --- plugwise_usb/nodes/helpers/pulses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index e976eb266..a84edc3cf 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -273,7 +273,7 @@ def update_pulse_counter( self._rollover_consumption = True if ( self._pulses_production is not None - and self._pulses_production > pulses_produced + and self._pulses_production < pulses_produced ): self._rollover_production = True self._pulses_consumption = pulses_consumed From 06ad5668b69f0e2650c540f5ccbf2c950ce2ff0b Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 23 Mar 2025 14:17:27 +0100 Subject: [PATCH 023/157] Bump to a57 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c699178c5..8f6928df7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a56" +version = "v0.40.0a57" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From 2a75755030bf463843eafb616b69aa8ad7d85c04 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 23 Mar 2025 14:41:21 +0100 Subject: [PATCH 024/157] Formatting --- plugwise_usb/nodes/helpers/counter.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index 28677c33f..8e3560e8f 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -260,8 +260,10 @@ def energy(self) -> float | None: """Total energy (in kWh) since last reset.""" if self._pulses is None or self._calibration is None: return None + if self._pulses == 0: return 0.0 + # Handle both positive and negative pulses values negative = False if self._pulses < 0: @@ -284,6 +286,7 @@ def energy(self) -> float | None: calc_value = corrected_pulses / PULSES_PER_KW_SECOND / HOUR_IN_SECONDS if negative: calc_value = -calc_value + return calc_value @property From 8d9b4e46796487a0dad8fd2682a4d2446784080c Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 23 Mar 2025 16:07:41 +0100 Subject: [PATCH 025/157] Formatting --- plugwise_usb/nodes/helpers/pulses.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index a84edc3cf..78e4c9044 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -751,8 +751,10 @@ def _logs_missing(self, from_timestamp: datetime) -> list[int] | None: if self._logs is None: self._log_addresses_missing = None return None + if self.collected_logs < 2: return None + last_address, last_slot = self._last_log_reference() if last_address is None or last_slot is None: _LOGGER.debug( From 6ae788ecec845ee437a4ce50409bd036bddee135 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 23 Mar 2025 16:17:24 +0100 Subject: [PATCH 026/157] Set MAX_LOG_HOURS to 24 --- plugwise_usb/nodes/helpers/pulses.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 78e4c9044..44c41ca51 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -7,14 +7,14 @@ import logging from typing import Final -from ...constants import LOGADDR_MAX, MINUTE_IN_SECONDS, WEEK_IN_HOURS +from ...constants import LOGADDR_MAX, MINUTE_IN_SECONDS, DAY_IN_HOURS from ...exceptions import EnergyError _LOGGER = logging.getLogger(__name__) CONSUMED: Final = True PRODUCED: Final = False -MAX_LOG_HOURS = WEEK_IN_HOURS +MAX_LOG_HOURS = DAY_IN_HOURS def calc_log_address(address: int, slot: int, offset: int) -> tuple[int, int]: From 145983c142f2d9ba110ee5c07c55a4ef1aeed037 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 23 Mar 2025 16:17:50 +0100 Subject: [PATCH 027/157] Bump to a58 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8f6928df7..4411bcd99 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a57" +version = "v0.40.0a58" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From 175dc10b55f659a4b3a0629c5d8fd1829d33e63c Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 23 Mar 2025 19:54:34 +0100 Subject: [PATCH 028/157] Add idea-text to update_pulse_counter() docstring --- plugwise_usb/nodes/helpers/pulses.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 44c41ca51..a21cab129 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -260,7 +260,12 @@ def _collect_pulses_from_logs( def update_pulse_counter( self, pulses_consumed: int, pulses_produced: int, timestamp: datetime ) -> None: - """Update pulse counter.""" + """Update pulse counter. + + Both consumption and production counters reset at the beginning of a new hour. + IDEA: should we treat this event not as a rollover but as a counter reset? + What should be the effect of this reset? + """ self._pulses_timestamp = timestamp self._update_rollover() if not (self._rollover_consumption or self._rollover_production): From 01a8ec0a1f899dfb1191d0616f59451a7f503fc2 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 24 Mar 2025 09:36:49 +0100 Subject: [PATCH 029/157] update_pulse_counter(): add rollover log-debug-messages --- plugwise_usb/nodes/helpers/pulses.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index a21cab129..1fbeec7b2 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -275,12 +275,15 @@ def update_pulse_counter( self._pulses_consumption is not None and self._pulses_consumption > pulses_consumed ): + _LOGGER.debug("update_pulse_counter | rollover consumption") self._rollover_consumption = True if ( self._pulses_production is not None and self._pulses_production < pulses_produced ): + _LOGGER.debug("update_pulse_counter | rollover production") self._rollover_production = True + self._pulses_consumption = pulses_consumed self._pulses_production = pulses_produced From 414684065f6cda29ee923fe5f67395c990cb5707 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 24 Mar 2025 09:50:23 +0100 Subject: [PATCH 030/157] collected_pulses(): don't return None's at rollover Improve logging --- plugwise_usb/nodes/helpers/pulses.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 1fbeec7b2..68ad45e3f 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -160,27 +160,27 @@ def collected_pulses( ) -> tuple[int | None, datetime | None]: """Calculate total pulses from given timestamp.""" _LOGGER.debug( - "collected_pulses 1 | %s | is_cons=%s, from_timestamp=%s", + "collected_pulses | %s | from_timestamp=%s | is_cons=%s | _log_production=%s", self._mac, - is_consumption, from_timestamp, + is_consumption, + self._log_production ) - _LOGGER.debug("collected_pulses 1a | _log_production=%s", self._log_production) if not is_consumption: if self._log_production is None or not self._log_production: return (None, None) - if is_consumption and self._rollover_consumption: - _LOGGER.debug("collected_pulses 2 | %s | _rollover_consumption", self._mac) - return (None, None) - if not is_consumption and self._rollover_production: - _LOGGER.debug("collected_pulses 3 | %s | _rollover_production", self._mac) - return (None, None) + # if is_consumption and self._rollover_consumption: + # _LOGGER.debug("collected_pulses 2 | %s | _rollover_consumption", self._mac) + # return (None, None) + # if not is_consumption and self._rollover_production: + # _LOGGER.debug("collected_pulses 3 | %s | _rollover_production", self._mac) + # return (None, None) if ( log_pulses := self._collect_pulses_from_logs(from_timestamp, is_consumption) ) is None: - _LOGGER.debug("collected_pulses 4 | %s | log_pulses:None", self._mac) + _LOGGER.debug("collected_pulses | %s | log_pulses:None", self._mac) return (None, None) pulses: int | None = None @@ -188,20 +188,20 @@ def collected_pulses( if is_consumption and self._pulses_consumption is not None: pulses = self._pulses_consumption timestamp = self._pulses_timestamp + if not is_consumption and self._pulses_production is not None: pulses = self._pulses_production timestamp = self._pulses_timestamp - # _LOGGER.debug("collected_pulses | %s | pulses=%s", self._mac, pulses) if pulses is None: _LOGGER.debug( - "collected_pulses 5 | %s | is_consumption=%s, pulses=None", + "collected_pulses | %s | is_consumption=%s, pulses=None", self._mac, is_consumption, ) return (None, None) _LOGGER.debug( - "collected_pulses 6 | pulses=%s | log_pulses=%s | consumption=%s at timestamp=%s", + "collected_pulses | pulses=%s | log_pulses=%s | consumption=%s at timestamp=%s", pulses, log_pulses, is_consumption, From f15f11ff2084ffed12f0a2af1c37363fda5bddb7 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 24 Mar 2025 09:55:04 +0100 Subject: [PATCH 031/157] Set to a59 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4411bcd99..6cf978957 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a58" +version = "v0.40.0a59" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From bac0c4e41099e89240383fd5902cfe7f9bcd7f2b Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 24 Mar 2025 09:58:28 +0100 Subject: [PATCH 032/157] Fix related test-assert --- 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 da959c3a4..2020506f8 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -1126,7 +1126,7 @@ def test_pulse_collection_consumption( test_timestamp = fixed_this_hour + td(hours=1, seconds=5) assert tst_consumption.collected_pulses( test_timestamp, is_consumption=True - ) == (None, None) + ) == (45, pulse_update_3) tst_consumption.add_log(100, 2, (fixed_this_hour + td(hours=1)), 2222) assert not tst_consumption.log_rollover assert tst_consumption.collected_pulses( From ce3742aae81a38b9a47e55d5f88bca190a0616bf Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 24 Mar 2025 10:11:44 +0100 Subject: [PATCH 033/157] Fix 2nd related test-assert --- 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 2020506f8..f8074bf91 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -1141,7 +1141,7 @@ def test_pulse_collection_consumption( assert tst_consumption.log_rollover assert tst_consumption.collected_pulses( fixed_this_hour, is_consumption=True - ) == (None, None) + ) == (45+ 2222 + 3333, pulse_update_3) pulse_update_4 = fixed_this_hour + td(hours=2, seconds=10) tst_consumption.update_pulse_counter(321, 0, pulse_update_4) assert not tst_consumption.log_rollover From 719b73769d808be6975f0271df470486bc1ad4e1 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 24 Mar 2025 12:51:46 +0100 Subject: [PATCH 034/157] Remove commented-out code --- plugwise_usb/nodes/helpers/pulses.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 68ad45e3f..e7464f5bb 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -170,13 +170,6 @@ def collected_pulses( if self._log_production is None or not self._log_production: return (None, None) - # if is_consumption and self._rollover_consumption: - # _LOGGER.debug("collected_pulses 2 | %s | _rollover_consumption", self._mac) - # return (None, None) - # if not is_consumption and self._rollover_production: - # _LOGGER.debug("collected_pulses 3 | %s | _rollover_production", self._mac) - # return (None, None) - if ( log_pulses := self._collect_pulses_from_logs(from_timestamp, is_consumption) ) is None: From ad4c29f6d1100e6a9221410700be87b50de74e76 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 24 Mar 2025 13:06:54 +0100 Subject: [PATCH 035/157] Clean up update_pulse_counter() docstring --- plugwise_usb/nodes/helpers/pulses.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index e7464f5bb..f0fb6ffbe 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -255,9 +255,7 @@ def update_pulse_counter( ) -> None: """Update pulse counter. - Both consumption and production counters reset at the beginning of a new hour. - IDEA: should we treat this event not as a rollover but as a counter reset? - What should be the effect of this reset? + Both device consumption and production counters reset after the beginning of a new hour. """ self._pulses_timestamp = timestamp self._update_rollover() From c26857da37761b60a4811efb6f48790081ca2acd Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 24 Mar 2025 13:11:11 +0100 Subject: [PATCH 036/157] Revert back to WEEK_IN_HOURS --- plugwise_usb/nodes/helpers/pulses.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index f0fb6ffbe..fe31ee7da 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -7,14 +7,14 @@ import logging from typing import Final -from ...constants import LOGADDR_MAX, MINUTE_IN_SECONDS, DAY_IN_HOURS +from ...constants import LOGADDR_MAX, MINUTE_IN_SECONDS, WEEK_IN_HOURS from ...exceptions import EnergyError _LOGGER = logging.getLogger(__name__) CONSUMED: Final = True PRODUCED: Final = False -MAX_LOG_HOURS = DAY_IN_HOURS +MAX_LOG_HOURS = WEEK_IN_HOURS def calc_log_address(address: int, slot: int, offset: int) -> tuple[int, int]: From a6a53513ac555eb828abdfae6e109504b33fa499 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 24 Mar 2025 13:27:35 +0100 Subject: [PATCH 037/157] Bump to a60 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6cf978957..9080f3d82 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a59" +version = "v0.40.0a60" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From 6b4aa61ccb7947b9a80b2e7d9963a61984a2e6ac Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 24 Mar 2025 08:12:33 +0100 Subject: [PATCH 038/157] _add_log_record(): formatting --- plugwise_usb/nodes/helpers/pulses.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index fe31ee7da..bee3288b9 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -432,15 +432,19 @@ def _add_log_record( if self._logs is None: self._logs = {address: {slot: log_record}} return True + if self._log_exists(address, slot): return False + # Drop useless log records when we have at least 4 logs if self.collected_logs > 4 and log_record.timestamp < ( datetime.now(tz=UTC) - timedelta(hours=MAX_LOG_HOURS) ): return False + if self._logs.get(address) is None: self._logs[address] = {slot: log_record} + self._logs[address][slot] = log_record if ( address == self._first_empty_log_address @@ -448,12 +452,14 @@ def _add_log_record( ): self._first_empty_log_address = None self._first_empty_log_slot = None + if ( address == self._last_empty_log_address and slot == self._last_empty_log_slot ): self._last_empty_log_address = None self._last_empty_log_slot = None + return True def _update_log_direction( From 5803e5569a6527b9fe412bfaddf5f1a3a8b6d18e Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 24 Mar 2025 18:09:36 +0100 Subject: [PATCH 039/157] Fix typo in comment --- plugwise_usb/nodes/helpers/pulses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index bee3288b9..84ce76cca 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -261,7 +261,7 @@ def update_pulse_counter( self._update_rollover() if not (self._rollover_consumption or self._rollover_production): # No rollover based on time, check rollover based on counter reset - # Required for special cases like nodes which have been power off for several days + # Required for special cases like nodes which have been powered off for several days if ( self._pulses_consumption is not None and self._pulses_consumption > pulses_consumed From d2a6db76c15742b6d8d8bfd3fb246aea2324f08f Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 26 Mar 2025 08:43:47 +0100 Subject: [PATCH 040/157] Formatting --- plugwise_usb/nodes/helpers/pulses.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 84ce76cca..ae39cb23a 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -211,6 +211,7 @@ def _collect_pulses_from_logs( if self._logs is None: _LOGGER.debug("_collect_pulses_from_logs | %s | self._logs=None", self._mac) return None + if is_consumption: if self._last_log_consumption_timestamp is None: _LOGGER.debug( @@ -240,7 +241,6 @@ def _collect_pulses_from_logs( return None log_pulses = 0 - for log_item in self._logs.values(): for slot_item in log_item.values(): if ( @@ -248,6 +248,7 @@ def _collect_pulses_from_logs( and slot_item.timestamp > from_timestamp ): log_pulses += slot_item.pulses + return log_pulses def update_pulse_counter( From d9c2afc50bbeb6dcbb1459c3e0239e5bb712abd5 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 26 Mar 2025 13:50:10 +0100 Subject: [PATCH 041/157] Add pulsecounter_reset booleans, and detection --- plugwise_usb/nodes/helpers/pulses.py | 35 ++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index ae39cb23a..d04952aab 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -83,6 +83,8 @@ def __init__(self, mac: str) -> None: self._first_log_production_slot: int | None = None self._next_log_production_timestamp: datetime | None = None + self._cons_pulsecounter_reset = False + self._prod_pulsecounter_reset = False self._rollover_consumption = False self._rollover_production = False @@ -258,21 +260,34 @@ def update_pulse_counter( Both device consumption and production counters reset after the beginning of a new hour. """ + self._cons_pulsecounter_reset = False + self._prod_pulsecounter_reset = False self._pulses_timestamp = timestamp - self._update_rollover() - if not (self._rollover_consumption or self._rollover_production): # No rollover based on time, check rollover based on counter reset # Required for special cases like nodes which have been powered off for several days - if ( - self._pulses_consumption is not None - and self._pulses_consumption > pulses_consumed - ): + if ( + self._pulses_consumption is not None + and self._pulses_consumption > pulses_consumed + ): + self._cons_pulsecounter_reset = True + + if ( + self._pulses_production is not None + and self._pulses_production < pulses_produced + ): + self._prod_pulsecounter_reset = True + + if consumption_counter_reset or production_counter_reset: + _LOGGER.debug("update_pulse_counter | pulsecounter reset") + self._pulsecounter_reset = True + + self._update_rollover() + if not (self._rollover_consumption or self._rollover_production): + if self._cons_pulsecounter_reset: _LOGGER.debug("update_pulse_counter | rollover consumption") self._rollover_consumption = True - if ( - self._pulses_production is not None - and self._pulses_production < pulses_produced - ): + + if self._prod_pulsecounter_reset: _LOGGER.debug("update_pulse_counter | rollover production") self._rollover_production = True From a8bfbc02c3cd300d77bc2777859cdbb8498803d0 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 27 Mar 2025 08:15:09 +0100 Subject: [PATCH 042/157] Add counter_reset guarding for log_pulses reset --- plugwise_usb/nodes/helpers/pulses.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index d04952aab..ef5364b50 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -221,7 +221,10 @@ def _collect_pulses_from_logs( self._mac, ) return None - if from_timestamp > self._last_log_consumption_timestamp: + if ( + from_timestamp > self._last_log_consumption_timestamp + and self._cons_pulsecounter_reset + ): return 0 else: if self._last_log_production_timestamp is None: @@ -230,7 +233,10 @@ def _collect_pulses_from_logs( self._mac, ) return None - if from_timestamp > self._last_log_production_timestamp: + if ( + from_timestamp > self._last_log_production_timestamp + and self._prod_pulsecounter_reset + ): return 0 missing_logs = self._logs_missing(from_timestamp) From b5b3e19039e518be47d52e8130a2eb35a9b1a5ed Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 27 Mar 2025 08:20:21 +0100 Subject: [PATCH 043/157] Improve --- plugwise_usb/nodes/helpers/pulses.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index ef5364b50..b56babb23 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -269,25 +269,23 @@ def update_pulse_counter( self._cons_pulsecounter_reset = False self._prod_pulsecounter_reset = False self._pulses_timestamp = timestamp - # No rollover based on time, check rollover based on counter reset - # Required for special cases like nodes which have been powered off for several days + self._update_rollover() if ( self._pulses_consumption is not None and self._pulses_consumption > pulses_consumed ): + _LOGGER.debug("update_pulse_counter | consumption pulses reset") self._cons_pulsecounter_reset = True if ( self._pulses_production is not None and self._pulses_production < pulses_produced ): + _LOGGER.debug("update_pulse_counter | production pulses reset") self._prod_pulsecounter_reset = True - if consumption_counter_reset or production_counter_reset: - _LOGGER.debug("update_pulse_counter | pulsecounter reset") - self._pulsecounter_reset = True - - self._update_rollover() + # No rollover based on time, check rollover based on counter reset + # Required for special cases like nodes which have been powered off for several days if not (self._rollover_consumption or self._rollover_production): if self._cons_pulsecounter_reset: _LOGGER.debug("update_pulse_counter | rollover consumption") From 6060f5e286655e38ab496292016d1f9a501a3565 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 27 Mar 2025 08:24:08 +0100 Subject: [PATCH 044/157] Bump to a62 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9080f3d82..ecd032ff2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a60" +version = "v0.40.0a62" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From ae7f823dcf33a35e1ca07552f9ae329eab8364c4 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 27 Mar 2025 14:30:48 +0100 Subject: [PATCH 045/157] Use common _pulsecounter_reset to reset both counters while only one is active --- plugwise_usb/nodes/helpers/pulses.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index b56babb23..d6444f7af 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -83,8 +83,7 @@ def __init__(self, mac: str) -> None: self._first_log_production_slot: int | None = None self._next_log_production_timestamp: datetime | None = None - self._cons_pulsecounter_reset = False - self._prod_pulsecounter_reset = False + self._pulsecounter_reset = False self._rollover_consumption = False self._rollover_production = False @@ -223,7 +222,7 @@ def _collect_pulses_from_logs( return None if ( from_timestamp > self._last_log_consumption_timestamp - and self._cons_pulsecounter_reset + and self._pulsecounter_reset ): return 0 else: @@ -235,7 +234,7 @@ def _collect_pulses_from_logs( return None if ( from_timestamp > self._last_log_production_timestamp - and self._prod_pulsecounter_reset + and self._pulsecounter_reset ): return 0 @@ -266,8 +265,8 @@ def update_pulse_counter( Both device consumption and production counters reset after the beginning of a new hour. """ - self._cons_pulsecounter_reset = False - self._prod_pulsecounter_reset = False + cons_pulsecounter_reset = False + prod_pulsecounter_reset = False self._pulses_timestamp = timestamp self._update_rollover() if ( @@ -275,23 +274,28 @@ def update_pulse_counter( and self._pulses_consumption > pulses_consumed ): _LOGGER.debug("update_pulse_counter | consumption pulses reset") - self._cons_pulsecounter_reset = True + cons_pulsecounter_reset = True if ( self._pulses_production is not None and self._pulses_production < pulses_produced ): _LOGGER.debug("update_pulse_counter | production pulses reset") - self._prod_pulsecounter_reset = True + prod_pulsecounter_reset = True + + if cons_pulsecounter_reset or prod_pulsecounter_reset: + self._pulsecounter_reset = True + else: + self._pulsecounter_reset = False # No rollover based on time, check rollover based on counter reset # Required for special cases like nodes which have been powered off for several days if not (self._rollover_consumption or self._rollover_production): - if self._cons_pulsecounter_reset: + if cons_pulsecounter_reset: _LOGGER.debug("update_pulse_counter | rollover consumption") self._rollover_consumption = True - if self._prod_pulsecounter_reset: + if prod_pulsecounter_reset: _LOGGER.debug("update_pulse_counter | rollover production") self._rollover_production = True From e6d158ee38673376a77ffe87f92e01f7cb7576c1 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 27 Mar 2025 14:46:13 +0100 Subject: [PATCH 046/157] Bump to a63 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ecd032ff2..66832e09c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a62" +version = "v0.40.0a63" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From b08cb086f7e1af96ec8fe89fbaa5cb31b1b11723 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 27 Mar 2025 15:10:37 +0100 Subject: [PATCH 047/157] Set MAX_LOG_HOURS to DAY_IN_HOURS --- plugwise_usb/nodes/helpers/pulses.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index d6444f7af..9c71835f7 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -7,14 +7,14 @@ import logging from typing import Final -from ...constants import LOGADDR_MAX, MINUTE_IN_SECONDS, WEEK_IN_HOURS +from ...constants import LOGADDR_MAX, MINUTE_IN_SECONDS, DAY_IN_HOURS from ...exceptions import EnergyError _LOGGER = logging.getLogger(__name__) CONSUMED: Final = True PRODUCED: Final = False -MAX_LOG_HOURS = WEEK_IN_HOURS +MAX_LOG_HOURS = DAY_IN_HOURS def calc_log_address(address: int, slot: int, offset: int) -> tuple[int, int]: From daada32e8e8138e76b1f7011169f05d549fcc4e5 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 27 Mar 2025 15:13:33 +0100 Subject: [PATCH 048/157] Remove all week-related EnergyTypes --- plugwise_usb/nodes/helpers/counter.py | 30 --------------------------- 1 file changed, 30 deletions(-) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index 8e3560e8f..9611cf4db 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -21,8 +21,6 @@ class EnergyType(Enum): PRODUCTION_HOUR = auto() CONSUMPTION_DAY = auto() PRODUCTION_DAY = auto() - CONSUMPTION_WEEK = auto() - PRODUCTION_WEEK = auto() ENERGY_COUNTERS: Final = ( @@ -30,8 +28,6 @@ class EnergyType(Enum): EnergyType.PRODUCTION_HOUR, EnergyType.CONSUMPTION_DAY, EnergyType.PRODUCTION_DAY, - EnergyType.CONSUMPTION_WEEK, - EnergyType.PRODUCTION_WEEK, ) ENERGY_HOUR_COUNTERS: Final = ( EnergyType.CONSUMPTION_HOUR, @@ -41,20 +37,13 @@ class EnergyType(Enum): EnergyType.CONSUMPTION_DAY, EnergyType.PRODUCTION_DAY, ) -ENERGY_WEEK_COUNTERS: Final = ( - EnergyType.CONSUMPTION_WEEK, - EnergyType.PRODUCTION_WEEK, -) - ENERGY_CONSUMPTION_COUNTERS: Final = ( EnergyType.CONSUMPTION_HOUR, EnergyType.CONSUMPTION_DAY, - EnergyType.CONSUMPTION_WEEK, ) ENERGY_PRODUCTION_COUNTERS: Final = ( EnergyType.PRODUCTION_HOUR, EnergyType.PRODUCTION_DAY, - EnergyType.PRODUCTION_WEEK, ) _LOGGER = logging.getLogger(__name__) @@ -168,11 +157,6 @@ def update(self) -> None: self._energy_statistics.day_consumption, self._energy_statistics.day_consumption_reset, ) = self._counters[EnergyType.CONSUMPTION_DAY].update(self._pulse_collection) - ( - self._energy_statistics.week_consumption, - self._energy_statistics.week_consumption_reset, - ) = self._counters[EnergyType.CONSUMPTION_WEEK].update(self._pulse_collection) - if self._pulse_collection.production_logging: self._energy_statistics.log_interval_production = ( self._pulse_collection.log_interval_production @@ -185,10 +169,6 @@ def update(self) -> None: self._energy_statistics.day_production, self._energy_statistics.day_production_reset, ) = self._counters[EnergyType.PRODUCTION_DAY].update(self._pulse_collection) - ( - self._energy_statistics.week_production, - self._energy_statistics.week_production_reset, - ) = self._counters[EnergyType.PRODUCTION_WEEK].update(self._pulse_collection) @property def timestamp(self) -> datetime | None: @@ -218,8 +198,6 @@ def __init__( self._duration = "hour" if energy_id in ENERGY_DAY_COUNTERS: self._duration = "day" - elif energy_id in ENERGY_WEEK_COUNTERS: - self._duration = "week" self._energy_id: EnergyType = energy_id self._is_consumption = True self._direction = "consumption" @@ -308,14 +286,6 @@ def update( last_reset = last_reset.replace(minute=0, second=0, microsecond=0) elif self._energy_id in ENERGY_DAY_COUNTERS: last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) - elif self._energy_id in ENERGY_WEEK_COUNTERS: - last_reset = last_reset - timedelta(days=last_reset.weekday()) - last_reset = last_reset.replace( - hour=0, - minute=0, - second=0, - microsecond=0, - ) pulses, last_update = pulse_collection.collected_pulses( last_reset, self._is_consumption From b21e015c235f6d82ee7d19b26da86067bacec02a Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 27 Mar 2025 15:16:16 +0100 Subject: [PATCH 049/157] Start cleaning week-related test-asserts --- tests/test_usb.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/test_usb.py b/tests/test_usb.py index f8074bf91..02dd4b906 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -915,14 +915,10 @@ async def fake_get_missing_energy_logs(address: int) -> None: hour_consumption_reset=None, day_consumption=None, day_consumption_reset=None, - week_consumption=None, - week_consumption_reset=None, hour_production=None, hour_production_reset=None, day_production=None, day_production_reset=None, - week_production=None, - week_production_reset=None, ) # energy_update is not complete and should return none utc_now = dt.now(UTC) @@ -936,14 +932,10 @@ async def fake_get_missing_energy_logs(address: int) -> None: hour_consumption_reset=utc_now.replace(minute=0, second=0, microsecond=0), day_consumption=None, day_consumption_reset=None, - week_consumption=None, - week_consumption_reset=None, hour_production=None, hour_production_reset=None, day_production=None, day_production_reset=None, - week_production=None, - week_production_reset=None, ) await stick.disconnect() From b2bb99e0eff247f3270f08a1ba5bc140470b452b Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 27 Mar 2025 15:20:39 +0100 Subject: [PATCH 050/157] Counter.py: clean up imports --- plugwise_usb/nodes/helpers/counter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index 9611cf4db..57c403063 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -2,7 +2,7 @@ from __future__ import annotations -from datetime import datetime, timedelta +from datetime import datetime from enum import Enum, auto import logging from typing import Final From a24cb79f3a5ba6124aa84d9b7699ec6f1c4cf31e Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 27 Mar 2025 19:37:44 +0100 Subject: [PATCH 051/157] Don't use gather --- plugwise_usb/nodes/circle.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/plugwise_usb/nodes/circle.py b/plugwise_usb/nodes/circle.py index 835365a8c..c81e67c51 100644 --- a/plugwise_usb/nodes/circle.py +++ b/plugwise_usb/nodes/circle.py @@ -2,7 +2,7 @@ from __future__ import annotations -from asyncio import Task, create_task, gather +from asyncio import Task, create_task from collections.abc import Awaitable, Callable from dataclasses import replace from datetime import UTC, datetime @@ -448,14 +448,11 @@ async def get_missing_energy_logs(self) -> None: ) total_addresses = 11 log_address = self._current_log_address - log_update_tasks = [] while total_addresses > 0: - log_update_tasks.append(self.energy_log_update(log_address)) + await self.energy_log_update(log_address) log_address, _ = calc_log_address(log_address, 1, -4) total_addresses -= 1 - await gather(*log_update_tasks) - if self._cache_enabled: await self._energy_log_records_save_to_cache() From acbab3c5f604e8a4256855351fcf4e19d8faad20 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 27 Mar 2025 19:45:57 +0100 Subject: [PATCH 052/157] Bump to a64 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 66832e09c..362a0fa3d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a63" +version = "v0.40.0a64" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From 109c50d95a72729abfc05d90809da9d7584f4951 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 28 Mar 2025 09:57:03 +0100 Subject: [PATCH 053/157] _collect_pulses_from_logs(): add debug-logging --- plugwise_usb/nodes/helpers/pulses.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 9c71835f7..03b9a03ff 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -213,6 +213,7 @@ def _collect_pulses_from_logs( _LOGGER.debug("_collect_pulses_from_logs | %s | self._logs=None", self._mac) return None + timestamp: datetime | None = None if is_consumption: if self._last_log_consumption_timestamp is None: _LOGGER.debug( @@ -224,6 +225,8 @@ def _collect_pulses_from_logs( from_timestamp > self._last_log_consumption_timestamp and self._pulsecounter_reset ): + _LOGGER.debug("_collect_pulses_from_logs | resetting log_pulses collection") + timestamp = self._last_log_consumption_timestamp return 0 else: if self._last_log_production_timestamp is None: @@ -236,6 +239,8 @@ def _collect_pulses_from_logs( from_timestamp > self._last_log_production_timestamp and self._pulsecounter_reset ): + _LOGGER.debug("_collect_pulses_from_logs | resetting log_pulses collection") + timestamp = self._last_log_production_timestamp return 0 missing_logs = self._logs_missing(from_timestamp) @@ -256,6 +261,12 @@ def _collect_pulses_from_logs( ): log_pulses += slot_item.pulses + _LOGGER.debug( + "_collect_pulses_from_logs | log_pulses=%s | from %s to %s", + log_pulses, + from_timestamp, + timestamp, + ) return log_pulses def update_pulse_counter( From 474f19d116375670a5fe6e10f3896794c69d1f02 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 28 Mar 2025 09:58:28 +0100 Subject: [PATCH 054/157] Fix typo in comment --- plugwise_usb/nodes/helpers/pulses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 03b9a03ff..f847a9a09 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -202,7 +202,7 @@ def collected_pulses( timestamp, ) - # Always return positive values of energy_statistics + # Always return positive values for energy_statistics return (abs(pulses + log_pulses), timestamp) def _collect_pulses_from_logs( From 14e6f5764d31012e6b00c7e86e0206bc3df3a5ba Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 28 Mar 2025 10:04:29 +0100 Subject: [PATCH 055/157] Bump to a65 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 362a0fa3d..5881d0dcf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a64" +version = "v0.40.0a65" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From 975d8bcaabf35e7321e9dac6530902b2af3b0927 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 28 Mar 2025 10:38:19 +0100 Subject: [PATCH 056/157] Try to not block the event_loop --- plugwise_usb/nodes/circle.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/plugwise_usb/nodes/circle.py b/plugwise_usb/nodes/circle.py index c81e67c51..80b691326 100644 --- a/plugwise_usb/nodes/circle.py +++ b/plugwise_usb/nodes/circle.py @@ -2,7 +2,7 @@ from __future__ import annotations -from asyncio import Task, create_task +from asyncio import Task, create_task, gather from collections.abc import Awaitable, Callable from dataclasses import replace from datetime import UTC, datetime @@ -449,10 +449,13 @@ async def get_missing_energy_logs(self) -> None: total_addresses = 11 log_address = self._current_log_address while total_addresses > 0: - await self.energy_log_update(log_address) + task = create_task(self.energy_log_update(log_address)) + await task log_address, _ = calc_log_address(log_address, 1, -4) total_addresses -= 1 + await gather(*task) + if self._cache_enabled: await self._energy_log_records_save_to_cache() @@ -472,7 +475,10 @@ async def get_missing_energy_logs(self) -> None: missing_addresses = sorted(missing_addresses, reverse=True) for address in missing_addresses: - await self.energy_log_update(address) + task = create_task(self.energy_log_update(address)) + await task + + gather(*task) if self._cache_enabled: await self._energy_log_records_save_to_cache() From fff6bc6d15186a65a4a1085c00201e18770c13de Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 28 Mar 2025 10:47:53 +0100 Subject: [PATCH 057/157] Bump to a66 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 5881d0dcf..ec75200da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a65" +version = "v0.40.0a66" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From 4a9b2b344239c0452e598de1643adba818a86856 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 28 Mar 2025 11:03:52 +0100 Subject: [PATCH 058/157] Revert "Try to not block the event_loop" This reverts commit 975d8bcaabf35e7321e9dac6530902b2af3b0927. --- plugwise_usb/nodes/circle.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/plugwise_usb/nodes/circle.py b/plugwise_usb/nodes/circle.py index 80b691326..c81e67c51 100644 --- a/plugwise_usb/nodes/circle.py +++ b/plugwise_usb/nodes/circle.py @@ -2,7 +2,7 @@ from __future__ import annotations -from asyncio import Task, create_task, gather +from asyncio import Task, create_task from collections.abc import Awaitable, Callable from dataclasses import replace from datetime import UTC, datetime @@ -449,13 +449,10 @@ async def get_missing_energy_logs(self) -> None: total_addresses = 11 log_address = self._current_log_address while total_addresses > 0: - task = create_task(self.energy_log_update(log_address)) - await task + await self.energy_log_update(log_address) log_address, _ = calc_log_address(log_address, 1, -4) total_addresses -= 1 - await gather(*task) - if self._cache_enabled: await self._energy_log_records_save_to_cache() @@ -475,10 +472,7 @@ async def get_missing_energy_logs(self) -> None: missing_addresses = sorted(missing_addresses, reverse=True) for address in missing_addresses: - task = create_task(self.energy_log_update(address)) - await task - - gather(*task) + await self.energy_log_update(address) if self._cache_enabled: await self._energy_log_records_save_to_cache() From 9e896e4a38f2e62548ef22f847941a52362191e8 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 28 Mar 2025 11:07:47 +0100 Subject: [PATCH 059/157] Try something else --- plugwise_usb/nodes/circle.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/circle.py b/plugwise_usb/nodes/circle.py index c81e67c51..0566a6798 100644 --- a/plugwise_usb/nodes/circle.py +++ b/plugwise_usb/nodes/circle.py @@ -2,7 +2,7 @@ from __future__ import annotations -from asyncio import Task, create_task +from asyncio import Task, create_task, sleep from collections.abc import Awaitable, Callable from dataclasses import replace from datetime import UTC, datetime @@ -472,6 +472,7 @@ async def get_missing_energy_logs(self) -> None: missing_addresses = sorted(missing_addresses, reverse=True) for address in missing_addresses: + await sleep(0) await self.energy_log_update(address) if self._cache_enabled: From 59539176ccba1bd78e8618575ee471db52604a76 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 28 Mar 2025 11:12:31 +0100 Subject: [PATCH 060/157] Bump to a67 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ec75200da..42e834dfe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a66" +version = "v0.40.0a67" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From 50ef92759add228153c369a88f569e6cf413d6c2 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 28 Mar 2025 12:13:13 +0100 Subject: [PATCH 061/157] Improve _collect_pulses_from_logs() --- plugwise_usb/nodes/helpers/pulses.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index f847a9a09..b345ea88b 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -221,12 +221,13 @@ def _collect_pulses_from_logs( self._mac, ) return None + + timestamp = self._last_log_consumption_timestamp if ( - from_timestamp > self._last_log_consumption_timestamp + from_timestamp > timestamp and self._pulsecounter_reset ): - _LOGGER.debug("_collect_pulses_from_logs | resetting log_pulses collection") - timestamp = self._last_log_consumption_timestamp + _LOGGER.debug("_collect_pulses_from_logs | resetting log_pulses to 0") return 0 else: if self._last_log_production_timestamp is None: @@ -235,12 +236,13 @@ def _collect_pulses_from_logs( self._mac, ) return None + + timestamp = self._last_log_production_timestamp if ( - from_timestamp > self._last_log_production_timestamp + from_timestamp > timestamp and self._pulsecounter_reset ): - _LOGGER.debug("_collect_pulses_from_logs | resetting log_pulses collection") - timestamp = self._last_log_production_timestamp + _LOGGER.debug("_collect_pulses_from_logs | resetting log_pulses to 0") return 0 missing_logs = self._logs_missing(from_timestamp) @@ -262,8 +264,9 @@ def _collect_pulses_from_logs( log_pulses += slot_item.pulses _LOGGER.debug( - "_collect_pulses_from_logs | log_pulses=%s | from %s to %s", + "_collect_pulses_from_logs | log_pulses=%s | is_consumption=%s | from %s to %s", log_pulses, + is_consumption, from_timestamp, timestamp, ) From d366bea8d5b975a90c4d0555a2135e02d1c75cc4 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 28 Mar 2025 13:10:14 +0100 Subject: [PATCH 062/157] Bump to a68 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 42e834dfe..7e6e21e54 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a67" +version = "v0.40.0a68" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From e43e97edc1d5d8d8006e7457b51d2509f450bc8c Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 28 Mar 2025 14:55:40 +0100 Subject: [PATCH 063/157] Try to avoid using asyncio_sleep() --- plugwise_usb/nodes/circle.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/plugwise_usb/nodes/circle.py b/plugwise_usb/nodes/circle.py index 0566a6798..dde3c5098 100644 --- a/plugwise_usb/nodes/circle.py +++ b/plugwise_usb/nodes/circle.py @@ -2,7 +2,7 @@ from __future__ import annotations -from asyncio import Task, create_task, sleep +from asyncio import Task, create_task from collections.abc import Awaitable, Callable from dataclasses import replace from datetime import UTC, datetime @@ -458,8 +458,7 @@ async def get_missing_energy_logs(self) -> None: return - if self._energy_counters.log_addresses_missing is not None: - _LOGGER.debug("Task created to get missing logs of %s", self._mac_in_str) + _LOGGER.debug("Task created to get missing logs of %s", self._mac_in_str) if ( missing_addresses := self._energy_counters.log_addresses_missing ) is not None: @@ -471,9 +470,12 @@ async def get_missing_energy_logs(self) -> None: ) missing_addresses = sorted(missing_addresses, reverse=True) - for address in missing_addresses: - await sleep(0) - await self.energy_log_update(address) + tasks = [ + create_task(self.energy_log_update(address)) + for address in missing_addresses + ] + for task in tasks: + await task if self._cache_enabled: await self._energy_log_records_save_to_cache() From d5278ce35b1da8b34e398bf627dd40efbae9a24c Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 28 Mar 2025 19:41:55 +0100 Subject: [PATCH 064/157] Bump to a69 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7e6e21e54..e20e6db33 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a68" +version = "v0.40.0a69" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From b3fe6f5cd2f69021df79033e81320c76ad2ffdb6 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 30 Mar 2025 10:29:56 +0200 Subject: [PATCH 065/157] Collect last_hourly_reset timestamps --- plugwise_usb/nodes/helpers/pulses.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index b345ea88b..6a512b598 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -67,6 +67,8 @@ def __init__(self, mac: str) -> None: self._last_empty_log_address: int | None = None self._last_empty_log_slot: int | None = None + self.cons_last_hourly_reset: datetime | None = None + self.prod_last_hourly_reset: datetime | None = None self._last_log_consumption_timestamp: datetime | None = None self._last_log_consumption_address: int | None = None self._last_log_consumption_slot: int | None = None @@ -288,6 +290,7 @@ def update_pulse_counter( and self._pulses_consumption > pulses_consumed ): _LOGGER.debug("update_pulse_counter | consumption pulses reset") + self.cons_last_hourly_reset = timestamp cons_pulsecounter_reset = True if ( @@ -295,6 +298,7 @@ def update_pulse_counter( and self._pulses_production < pulses_produced ): _LOGGER.debug("update_pulse_counter | production pulses reset") + self.prod_last_hourly_reset = timestamp prod_pulsecounter_reset = True if cons_pulsecounter_reset or prod_pulsecounter_reset: From 10693d4583f929fa543b0442eb9ff69f1bca0d78 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 30 Mar 2025 10:46:59 +0200 Subject: [PATCH 066/157] Line up interval reset with device hourly pulsecounter reset --- plugwise_usb/nodes/helpers/counter.py | 14 +++++++++++--- plugwise_usb/nodes/helpers/pulses.py | 8 ++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index 57c403063..4720da0ed 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -206,6 +206,7 @@ def __init__( self._is_consumption = False self._last_reset: datetime | None = None self._last_update: datetime | None = None + self._pulse_collection = PulseCollection(mac) self._pulses: int | None = None @property @@ -282,10 +283,17 @@ def update( ) -> tuple[float | None, datetime | None]: """Get pulse update.""" last_reset = datetime.now(tz=LOCAL_TIMEZONE) + timestamp = self._pulse_collection.hourly_reset_time if self._energy_id in ENERGY_HOUR_COUNTERS: - last_reset = last_reset.replace(minute=0, second=0, microsecond=0) - elif self._energy_id in ENERGY_DAY_COUNTERS: - last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) + if timestamp is not None: + last_reset = timestamp + else: + last_reset = last_reset.replace(minute=0, second=0, microsecond=0) + if self._energy_id in ENERGY_DAY_COUNTERS: + if timestamp is not None: + last_reset = timestamp.replace(hour=0) + else: + last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) pulses, last_update = pulse_collection.collected_pulses( last_reset, self._is_consumption diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 6a512b598..44d216f23 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -106,6 +106,14 @@ def collected_logs(self) -> int: counter += len(self._logs[address]) return counter + @property + def hourly_reset_time(self) -> datetime: + """Provide the device hourly pulse reset time.""" + if timestamp := self.cons_last_hourly_reset is not None: + return timestamp + if timestamp := self.prod_last_hourly_reset is not None: + return timestamp + @property def logs(self) -> dict[int, dict[int, PulseLogRecord]]: """Return currently collected pulse logs in reversed order.""" From 2070561dad8165ff344371a5398ecb395d24f9ff Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 30 Mar 2025 11:08:53 +0200 Subject: [PATCH 067/157] Adapt hourly reset time, add hourly_reset_time test-assert --- tests/test_usb.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_usb.py b/tests/test_usb.py index 02dd4b906..2a6c3fd05 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -1112,9 +1112,10 @@ def test_pulse_collection_consumption( # Test rollover by updating pulses before log record assert not tst_consumption.log_rollover - pulse_update_3 = fixed_this_hour + td(hours=1, seconds=3) + pulse_update_3 = fixed_this_hour + td(hours=1, minutes=1, seconds=3) tst_consumption.update_pulse_counter(45, 0, pulse_update_3) assert tst_consumption.log_rollover + assert tst_consumption.hourly_reset_time == pulse_update_3 test_timestamp = fixed_this_hour + td(hours=1, seconds=5) assert tst_consumption.collected_pulses( test_timestamp, is_consumption=True From c3da099e6b0134726cc4e0a7201ef1763184849e Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 30 Mar 2025 11:15:36 +0200 Subject: [PATCH 068/157] Improve hourly_reset_time property --- plugwise_usb/nodes/helpers/pulses.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 44d216f23..4c135f12a 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -107,12 +107,13 @@ def collected_logs(self) -> int: return counter @property - def hourly_reset_time(self) -> datetime: + def hourly_reset_time(self) -> datetime | None: """Provide the device hourly pulse reset time.""" if timestamp := self.cons_last_hourly_reset is not None: return timestamp if timestamp := self.prod_last_hourly_reset is not None: return timestamp + return None @property def logs(self) -> dict[int, dict[int, PulseLogRecord]]: From ab83196832faa4f90db1691c958d16fd85bff0c7 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 30 Mar 2025 11:20:32 +0200 Subject: [PATCH 069/157] Add debug logging --- plugwise_usb/nodes/helpers/pulses.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 4c135f12a..c1f257bd1 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -300,6 +300,10 @@ def update_pulse_counter( ): _LOGGER.debug("update_pulse_counter | consumption pulses reset") self.cons_last_hourly_reset = timestamp + _LOGGER.debug( + "update_pulse_counter | consumption hourly_reset_time=%s", + self.cons_last_hourly_reset, + ) cons_pulsecounter_reset = True if ( @@ -308,6 +312,10 @@ def update_pulse_counter( ): _LOGGER.debug("update_pulse_counter | production pulses reset") self.prod_last_hourly_reset = timestamp + _LOGGER.debug( + "update_pulse_counter | production hourly_reset_time=%s", + self.prod_last_hourly_reset, + ) prod_pulsecounter_reset = True if cons_pulsecounter_reset or prod_pulsecounter_reset: From 607470a696e7bd7f5eca2b9e283f78fc78b752a5 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 30 Mar 2025 11:25:42 +0200 Subject: [PATCH 070/157] Fix walrus --- plugwise_usb/nodes/helpers/pulses.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index c1f257bd1..2c15678fc 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -109,9 +109,9 @@ def collected_logs(self) -> int: @property def hourly_reset_time(self) -> datetime | None: """Provide the device hourly pulse reset time.""" - if timestamp := self.cons_last_hourly_reset is not None: + if (timestamp := self.cons_last_hourly_reset) is not None: return timestamp - if timestamp := self.prod_last_hourly_reset is not None: + if (timestamp := self.prod_last_hourly_reset) is not None: return timestamp return None From b5403ca2257c5d889922cb1523f6fb5d39b3c299 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 30 Mar 2025 11:27:35 +0200 Subject: [PATCH 071/157] Test: line up 2nd test-time --- 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 2a6c3fd05..70ad1e3c1 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -1116,7 +1116,7 @@ def test_pulse_collection_consumption( tst_consumption.update_pulse_counter(45, 0, pulse_update_3) assert tst_consumption.log_rollover assert tst_consumption.hourly_reset_time == pulse_update_3 - test_timestamp = fixed_this_hour + td(hours=1, seconds=5) + test_timestamp = fixed_this_hour + td(hours=1, minutes=1, seconds=5) assert tst_consumption.collected_pulses( test_timestamp, is_consumption=True ) == (45, pulse_update_3) From 21b162fa667a068df698c71985dc142a5f78f52b Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 30 Mar 2025 11:32:15 +0200 Subject: [PATCH 072/157] Bump to a71 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e20e6db33..5de8433c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a69" +version = "v0.40.0a71" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From 18d8d814205f2156ea502de90322c0337004fa9c Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 30 Mar 2025 17:18:36 +0200 Subject: [PATCH 073/157] Reset log_pulses after pulsecounter resets. --- plugwise_usb/nodes/helpers/pulses.py | 30 ++++++++++++---------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 2c15678fc..730698620 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -182,12 +182,6 @@ def collected_pulses( if self._log_production is None or not self._log_production: return (None, None) - if ( - log_pulses := self._collect_pulses_from_logs(from_timestamp, is_consumption) - ) is None: - _LOGGER.debug("collected_pulses | %s | log_pulses:None", self._mac) - return (None, None) - pulses: int | None = None timestamp: datetime | None = None if is_consumption and self._pulses_consumption is not None: @@ -205,6 +199,13 @@ def collected_pulses( is_consumption, ) return (None, None) + + if ( + log_pulses := self._collect_pulses_from_logs(from_timestamp, is_consumption) + ) is None: + _LOGGER.debug("collected_pulses | %s | log_pulses:None", self._mac) + return (None, None) + _LOGGER.debug( "collected_pulses | pulses=%s | log_pulses=%s | consumption=%s at timestamp=%s", pulses, @@ -236,7 +237,7 @@ def _collect_pulses_from_logs( timestamp = self._last_log_consumption_timestamp if ( from_timestamp > timestamp - and self._pulsecounter_reset + and self._cons_pulsecounter_reset ): _LOGGER.debug("_collect_pulses_from_logs | resetting log_pulses to 0") return 0 @@ -251,7 +252,7 @@ def _collect_pulses_from_logs( timestamp = self._last_log_production_timestamp if ( from_timestamp > timestamp - and self._pulsecounter_reset + and self._prod_pulsecounter_reset ): _LOGGER.debug("_collect_pulses_from_logs | resetting log_pulses to 0") return 0 @@ -290,8 +291,8 @@ def update_pulse_counter( Both device consumption and production counters reset after the beginning of a new hour. """ - cons_pulsecounter_reset = False - prod_pulsecounter_reset = False + self._cons_pulsecounter_reset = False + self._prod_pulsecounter_reset = False self._pulses_timestamp = timestamp self._update_rollover() if ( @@ -304,7 +305,7 @@ def update_pulse_counter( "update_pulse_counter | consumption hourly_reset_time=%s", self.cons_last_hourly_reset, ) - cons_pulsecounter_reset = True + self._cons_pulsecounter_reset = True if ( self._pulses_production is not None @@ -316,12 +317,7 @@ def update_pulse_counter( "update_pulse_counter | production hourly_reset_time=%s", self.prod_last_hourly_reset, ) - prod_pulsecounter_reset = True - - if cons_pulsecounter_reset or prod_pulsecounter_reset: - self._pulsecounter_reset = True - else: - self._pulsecounter_reset = False + self._prod_pulsecounter_reset = True # No rollover based on time, check rollover based on counter reset # Required for special cases like nodes which have been powered off for several days From 6b1ebd0d2c22b00460386a5640d52a0e2898d76c Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 30 Mar 2025 17:20:21 +0200 Subject: [PATCH 074/157] Revert changes in counter.py --- plugwise_usb/nodes/helpers/counter.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index 4720da0ed..e7c585c61 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -283,17 +283,10 @@ def update( ) -> tuple[float | None, datetime | None]: """Get pulse update.""" last_reset = datetime.now(tz=LOCAL_TIMEZONE) - timestamp = self._pulse_collection.hourly_reset_time if self._energy_id in ENERGY_HOUR_COUNTERS: - if timestamp is not None: - last_reset = timestamp - else: - last_reset = last_reset.replace(minute=0, second=0, microsecond=0) + last_reset = last_reset.replace(minute=0, second=0, microsecond=0) if self._energy_id in ENERGY_DAY_COUNTERS: - if timestamp is not None: - last_reset = timestamp.replace(hour=0) - else: - last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) + last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) pulses, last_update = pulse_collection.collected_pulses( last_reset, self._is_consumption From 057ca9d5adea6f76459711356ef92b6d39e9c3bd Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 30 Mar 2025 17:22:43 +0200 Subject: [PATCH 075/157] Fixes --- plugwise_usb/nodes/helpers/pulses.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 730698620..088384944 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -322,11 +322,11 @@ def update_pulse_counter( # No rollover based on time, check rollover based on counter reset # Required for special cases like nodes which have been powered off for several days if not (self._rollover_consumption or self._rollover_production): - if cons_pulsecounter_reset: + if self._cons_pulsecounter_reset: _LOGGER.debug("update_pulse_counter | rollover consumption") self._rollover_consumption = True - if prod_pulsecounter_reset: + if self._prod_pulsecounter_reset: _LOGGER.debug("update_pulse_counter | rollover production") self._rollover_production = True From 7cac11446d1eb7273f2ccc6f6ab537afbc98ea45 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 30 Mar 2025 17:32:46 +0200 Subject: [PATCH 076/157] Add/improve testing --- tests/test_usb.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/test_usb.py b/tests/test_usb.py index 70ad1e3c1..f469242bf 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -1122,25 +1122,28 @@ def test_pulse_collection_consumption( ) == (45, pulse_update_3) tst_consumption.add_log(100, 2, (fixed_this_hour + td(hours=1)), 2222) assert not tst_consumption.log_rollover + pulse_update_4 = fixed_this_hour + td(hours=1, minutes=1, seconds=18) + test_timestamp_2 = fixed_this_hour + td(hours=1, minutes=1, seconds=20) + tst_consumption.update_pulse_counter(145, 0, pulse_update_4) assert tst_consumption.collected_pulses( - test_timestamp, is_consumption=True - ) == (45, pulse_update_3) + test_timestamp_2, is_consumption=True + ) == (145, pulse_update_4) assert tst_consumption.collected_pulses( fixed_this_hour, is_consumption=True - ) == (45 + 2222, pulse_update_3) + ) == (145 + 2222, pulse_update_4) # Test log rollover by updating log first before updating pulses tst_consumption.add_log(100, 3, (fixed_this_hour + td(hours=2)), 3333) assert tst_consumption.log_rollover assert tst_consumption.collected_pulses( fixed_this_hour, is_consumption=True - ) == (45+ 2222 + 3333, pulse_update_3) - pulse_update_4 = fixed_this_hour + td(hours=2, seconds=10) - tst_consumption.update_pulse_counter(321, 0, pulse_update_4) + ) == (145+ 2222 + 3333, pulse_update_4) + pulse_update_5 = fixed_this_hour + td(hours=2, seconds=10) + tst_consumption.update_pulse_counter(321, 0, pulse_update_5) assert not tst_consumption.log_rollover assert tst_consumption.collected_pulses( fixed_this_hour, is_consumption=True - ) == (2222 + 3333 + 321, pulse_update_4) + ) == (2222 + 3333 + 321, pulse_update_5) @freeze_time(dt.now()) def test_pulse_collection_consumption_empty( From 9d468f9a1d2658ca23997191ad5ecc90cc7fac61 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 30 Mar 2025 17:52:05 +0200 Subject: [PATCH 077/157] Bump to a72 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 5de8433c4..3caffb520 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a71" +version = "v0.40.0a72" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From c937c4a8c26e788ab6580b41c133225930129b83 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 30 Mar 2025 17:56:03 +0200 Subject: [PATCH 078/157] Init new selfs --- plugwise_usb/nodes/helpers/pulses.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 088384944..e497fdf31 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -85,7 +85,8 @@ def __init__(self, mac: str) -> None: self._first_log_production_slot: int | None = None self._next_log_production_timestamp: datetime | None = None - self._pulsecounter_reset = False + self._cons_pulsecounter_reset = False + self._prod_pulsecounter_reset = False self._rollover_consumption = False self._rollover_production = False From f970d64abf36a514a2fc25c26a68dbb19ac624fe Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 31 Mar 2025 08:08:11 +0200 Subject: [PATCH 079/157] Add testcase for midnight rollover --- tests/test_usb.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/test_usb.py b/tests/test_usb.py index f469242bf..ca5d4e894 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -1145,6 +1145,21 @@ def test_pulse_collection_consumption( fixed_this_hour, is_consumption=True ) == (2222 + 3333 + 321, pulse_update_5) + # Test next midnight rollover + pulse_update_6 = fixed_this_hour.replace(hour=0, minute=0, second=3) + td(days=1) + assert not tst_consumption.log_rollover + tst_consumption.update_pulse_counter(584, 0, pulse_update_6) + test_timestamp_3 = pulse_update_6 + td(hours=0, minutes=0, seconds=5) + assert tst_consumption.collected_pulses( + test_timestamp_3, is_consumption=True + ) == (14000, pulse_update_6) + pulse_update_7 = fixed_this_hour.replace(hour=0, minute=1, second=3) + tst_consumption.update_pulse_counter(50, 0, pulse_update_7) + test_timestamp_4 = pulse_update_7 + td(hours=0, minutes=1, seconds=5) + assert tst_consumption.collected_pulses( + test_timestamp_3, is_consumption=True + ) == (500, pulse_update_7) + @freeze_time(dt.now()) def test_pulse_collection_consumption_empty( self, monkeypatch: pytest.MonkeyPatch From d643f771a61d1caea7d1bb2709a96e1acb825128 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 31 Mar 2025 11:04:48 +0200 Subject: [PATCH 080/157] Remove double resetting of log_pulses Also happens when slot_item.timestamp > from_timestamp --- plugwise_usb/nodes/helpers/pulses.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index e497fdf31..bd810471a 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -234,14 +234,6 @@ def _collect_pulses_from_logs( self._mac, ) return None - - timestamp = self._last_log_consumption_timestamp - if ( - from_timestamp > timestamp - and self._cons_pulsecounter_reset - ): - _LOGGER.debug("_collect_pulses_from_logs | resetting log_pulses to 0") - return 0 else: if self._last_log_production_timestamp is None: _LOGGER.debug( @@ -250,14 +242,6 @@ def _collect_pulses_from_logs( ) return None - timestamp = self._last_log_production_timestamp - if ( - from_timestamp > timestamp - and self._prod_pulsecounter_reset - ): - _LOGGER.debug("_collect_pulses_from_logs | resetting log_pulses to 0") - return 0 - missing_logs = self._logs_missing(from_timestamp) if missing_logs is None or missing_logs: _LOGGER.debug( From b377de144536b7c37b6e75ed0c2ac3d6be300de9 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 31 Mar 2025 17:07:52 +0200 Subject: [PATCH 081/157] More debug --- plugwise_usb/nodes/helpers/pulses.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index bd810471a..e567b8040 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -234,6 +234,7 @@ def _collect_pulses_from_logs( self._mac, ) return None + _LOGGER.debug("HOI _last_log_consumption_timestamp=%s", self._last_log_consumption_timestamp) else: if self._last_log_production_timestamp is None: _LOGGER.debug( @@ -241,6 +242,7 @@ def _collect_pulses_from_logs( self._mac, ) return None + _LOGGER.debug("HOI _last_log_production_timestamp=%s", self._last_log_production_timestamp) missing_logs = self._logs_missing(from_timestamp) if missing_logs is None or missing_logs: From 3002c2cc2c10197292bbb8818e9f8ae7a8e71aa6 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 31 Mar 2025 17:11:01 +0200 Subject: [PATCH 082/157] Party revert removal --- plugwise_usb/nodes/helpers/pulses.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index e567b8040..8b2d60c22 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -234,6 +234,8 @@ def _collect_pulses_from_logs( self._mac, ) return None + + timestamp = self._last_log_consumption_timestamp _LOGGER.debug("HOI _last_log_consumption_timestamp=%s", self._last_log_consumption_timestamp) else: if self._last_log_production_timestamp is None: @@ -242,6 +244,8 @@ def _collect_pulses_from_logs( self._mac, ) return None + + timestamp = self._last_log_production_timestamp _LOGGER.debug("HOI _last_log_production_timestamp=%s", self._last_log_production_timestamp) missing_logs = self._logs_missing(from_timestamp) From a7b528a4043469f983fa85b802d51f746d6e9fc7 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 31 Mar 2025 17:20:08 +0200 Subject: [PATCH 083/157] Add add_log debugging --- plugwise_usb/nodes/helpers/pulses.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 8b2d60c22..a8c36ce95 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -460,6 +460,14 @@ def add_log( self._update_rollover() if not import_only: self.recalculate_missing_log_addresses() + + _LOGGER.debug( + "add_log | pulses=%s | address=%s | slot= %s |time:%s", + pulses, + address, + slot, + timestamp, + ) return True def recalculate_missing_log_addresses(self) -> None: From c6b9dd52a9344af39ca94cd53fc3194c2f0e5232 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 31 Mar 2025 17:50:11 +0200 Subject: [PATCH 084/157] Try test --- tests/test_usb.py | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/tests/test_usb.py b/tests/test_usb.py index ca5d4e894..4108c9f9a 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -1122,23 +1122,30 @@ def test_pulse_collection_consumption( ) == (45, pulse_update_3) tst_consumption.add_log(100, 2, (fixed_this_hour + td(hours=1)), 2222) assert not tst_consumption.log_rollover + # Test collection of the last full hour + assert tst_consumption.collected_pulses( + fixed_this_hour, is_consumption=True + ) == (45 + 2222, pulse_update_3) pulse_update_4 = fixed_this_hour + td(hours=1, minutes=1, seconds=18) test_timestamp_2 = fixed_this_hour + td(hours=1, minutes=1, seconds=20) tst_consumption.update_pulse_counter(145, 0, pulse_update_4) + # Test collection of the last new hour assert tst_consumption.collected_pulses( test_timestamp_2, is_consumption=True ) == (145, pulse_update_4) - assert tst_consumption.collected_pulses( - fixed_this_hour, is_consumption=True - ) == (145 + 2222, pulse_update_4) + #tst_consumption.add_log(100, 3, (fixed_this_hour + td(hours=2)), 3333) + #assert not tst_consumption.log_rollover + #pulse_update_5 = fixed_this_hour + td(hours=2, minutes=1, seconds=18) + #test_timestamp_3 = fixed_this_hour + td(hours=2, minutes=1, seconds=20) + # Test log rollover by updating log first before updating pulses - tst_consumption.add_log(100, 3, (fixed_this_hour + td(hours=2)), 3333) + tst_consumption.add_log(100, 3, (fixed_this_hour + td(hours=3)), 3333) assert tst_consumption.log_rollover assert tst_consumption.collected_pulses( fixed_this_hour, is_consumption=True - ) == (145+ 2222 + 3333, pulse_update_4) - pulse_update_5 = fixed_this_hour + td(hours=2, seconds=10) + ) == (145 + 2222 + 3333, pulse_update_4) + pulse_update_5 = fixed_this_hour + td(hours=3, seconds=10) tst_consumption.update_pulse_counter(321, 0, pulse_update_5) assert not tst_consumption.log_rollover assert tst_consumption.collected_pulses( @@ -1146,19 +1153,19 @@ def test_pulse_collection_consumption( ) == (2222 + 3333 + 321, pulse_update_5) # Test next midnight rollover - pulse_update_6 = fixed_this_hour.replace(hour=0, minute=0, second=3) + td(days=1) - assert not tst_consumption.log_rollover - tst_consumption.update_pulse_counter(584, 0, pulse_update_6) - test_timestamp_3 = pulse_update_6 + td(hours=0, minutes=0, seconds=5) - assert tst_consumption.collected_pulses( - test_timestamp_3, is_consumption=True - ) == (14000, pulse_update_6) - pulse_update_7 = fixed_this_hour.replace(hour=0, minute=1, second=3) - tst_consumption.update_pulse_counter(50, 0, pulse_update_7) - test_timestamp_4 = pulse_update_7 + td(hours=0, minutes=1, seconds=5) - assert tst_consumption.collected_pulses( - test_timestamp_3, is_consumption=True - ) == (500, pulse_update_7) + #pulse_update_6 = fixed_this_hour.replace(hour=0, minute=0, second=3) + td(days=1) + #assert not tst_consumption.log_rollover + #tst_consumption.update_pulse_counter(584, 0, pulse_update_6) + #test_timestamp_3 = pulse_update_6 + td(hours=0, minutes=0, seconds=5) + #assert tst_consumption.collected_pulses( + # test_timestamp_3, is_consumption=True + #) == (14000, pulse_update_6) + #pulse_update_7 = fixed_this_hour.replace(hour=0, minute=1, second=3) + #tst_consumption.update_pulse_counter(50, 0, pulse_update_7) + #test_timestamp_4 = pulse_update_7 + td(hours=0, minutes=1, seconds=5) + #assert tst_consumption.collected_pulses( + # test_timestamp_3, is_consumption=True + #) == (500, pulse_update_7) @freeze_time(dt.now()) def test_pulse_collection_consumption_empty( From 208806fd10e4e5e65e05a0381e13724d2f1dab25 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 31 Mar 2025 19:01:34 +0200 Subject: [PATCH 085/157] Try update _last_log_consumption_timestamp --- plugwise_usb/nodes/helpers/pulses.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index a8c36ce95..13f582dd3 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -67,8 +67,8 @@ def __init__(self, mac: str) -> None: self._last_empty_log_address: int | None = None self._last_empty_log_slot: int | None = None - self.cons_last_hourly_reset: datetime | None = None - self.prod_last_hourly_reset: datetime | None = None + self._cons_last_hourly_reset: datetime | None = None + self._prod_last_hourly_reset: datetime | None = None self._last_log_consumption_timestamp: datetime | None = None self._last_log_consumption_address: int | None = None self._last_log_consumption_slot: int | None = None @@ -110,9 +110,9 @@ def collected_logs(self) -> int: @property def hourly_reset_time(self) -> datetime | None: """Provide the device hourly pulse reset time.""" - if (timestamp := self.cons_last_hourly_reset) is not None: + if (timestamp := self._cons_last_hourly_reset) is not None: return timestamp - if (timestamp := self.prod_last_hourly_reset) is not None: + if (timestamp := self._prod_last_hourly_reset) is not None: return timestamp return None @@ -291,10 +291,10 @@ def update_pulse_counter( and self._pulses_consumption > pulses_consumed ): _LOGGER.debug("update_pulse_counter | consumption pulses reset") - self.cons_last_hourly_reset = timestamp + self._cons_last_hourly_reset = timestamp _LOGGER.debug( "update_pulse_counter | consumption hourly_reset_time=%s", - self.cons_last_hourly_reset, + self._cons_last_hourly_reset, ) self._cons_pulsecounter_reset = True @@ -765,6 +765,19 @@ def _update_log_references(self, address: int, slot: int) -> None: log_time_stamp = self._logs[address][slot].timestamp is_consumption = self._logs[address][slot].is_consumption + if is_consumption: + if self._cons_last_hourly_reset is not None: + log_time_stamp = log_time_stamp + timedelta( + minutes=self._cons_last_hourly_reset.minute, + seconds=self._cons_last_hourly_reset.second, + microseconds=self._cons_last_hourly_reset.microsecond, + ) + elif self._prod_last_hourly_reset is not None: + log_time_stamp = log_time_stamp + timedelta( + minutes=self._prod_last_hourly_reset.minute, + seconds=self._prod_last_hourly_reset.second, + microseconds=self._prod_last_hourly_reset.microsecond, + ) # Update log references self._update_first_log_reference(address, slot, log_time_stamp, is_consumption) From e832fc79d2b0ae3001b2b3c1725a756b45ffba0e Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 31 Mar 2025 20:46:42 +0200 Subject: [PATCH 086/157] Try test 2 --- tests/test_usb.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/test_usb.py b/tests/test_usb.py index 4108c9f9a..74686d74a 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -1110,10 +1110,16 @@ def test_pulse_collection_consumption( tst_consumption.add_log(94, 1, (fixed_this_hour - td(hours=24)), 1000) assert tst_consumption.collected_logs == 24 + # Test rollover by updating pulses before log record + pulse_update_3 = fixed_this_hour + td(hours=0, minutes=0, seconds=30) + tst_consumption.update_pulse_counter(2500, 0, pulse_update_3) assert not tst_consumption.log_rollover - pulse_update_3 = fixed_this_hour + td(hours=1, minutes=1, seconds=3) - tst_consumption.update_pulse_counter(45, 0, pulse_update_3) + assert tst_consumption.collected_pulses( + test_timestamp, is_consumption=True + ) == (45, pulse_update_3) + pulse_update_4 = fixed_this_hour + td(hours=1, minutes=1, seconds=3) + tst_consumption.update_pulse_counter(45, 0, pulse_update_) assert tst_consumption.log_rollover assert tst_consumption.hourly_reset_time == pulse_update_3 test_timestamp = fixed_this_hour + td(hours=1, minutes=1, seconds=5) @@ -1126,13 +1132,13 @@ def test_pulse_collection_consumption( assert tst_consumption.collected_pulses( fixed_this_hour, is_consumption=True ) == (45 + 2222, pulse_update_3) - pulse_update_4 = fixed_this_hour + td(hours=1, minutes=1, seconds=18) + pulse_update_5 = fixed_this_hour + td(hours=1, minutes=1, seconds=18) test_timestamp_2 = fixed_this_hour + td(hours=1, minutes=1, seconds=20) tst_consumption.update_pulse_counter(145, 0, pulse_update_4) # Test collection of the last new hour assert tst_consumption.collected_pulses( test_timestamp_2, is_consumption=True - ) == (145, pulse_update_4) + ) == (145, pulse_update_5) #tst_consumption.add_log(100, 3, (fixed_this_hour + td(hours=2)), 3333) #assert not tst_consumption.log_rollover #pulse_update_5 = fixed_this_hour + td(hours=2, minutes=1, seconds=18) From 5cda89df35249dcdee5e7533314491788605a41e Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 1 Apr 2025 08:23:27 +0200 Subject: [PATCH 087/157] Try test 3 --- tests/test_usb.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_usb.py b/tests/test_usb.py index 74686d74a..ee23233b8 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -1117,15 +1117,15 @@ def test_pulse_collection_consumption( assert not tst_consumption.log_rollover assert tst_consumption.collected_pulses( test_timestamp, is_consumption=True - ) == (45, pulse_update_3) + ) == (2500 + 1111 + 1000 + 750, pulse_update_3) pulse_update_4 = fixed_this_hour + td(hours=1, minutes=1, seconds=3) - tst_consumption.update_pulse_counter(45, 0, pulse_update_) + tst_consumption.update_pulse_counter(45, 0, pulse_update_4) assert tst_consumption.log_rollover - assert tst_consumption.hourly_reset_time == pulse_update_3 + assert tst_consumption.hourly_reset_time == pulse_update_4 test_timestamp = fixed_this_hour + td(hours=1, minutes=1, seconds=5) assert tst_consumption.collected_pulses( test_timestamp, is_consumption=True - ) == (45, pulse_update_3) + ) == (45, pulse_update_4) tst_consumption.add_log(100, 2, (fixed_this_hour + td(hours=1)), 2222) assert not tst_consumption.log_rollover # Test collection of the last full hour From 8fdf9481d043448c5cf3d9485551889cb756077d Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 1 Apr 2025 08:32:02 +0200 Subject: [PATCH 088/157] More debug --- plugwise_usb/nodes/helpers/pulses.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 13f582dd3..2d79d8de1 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -290,7 +290,11 @@ def update_pulse_counter( self._pulses_consumption is not None and self._pulses_consumption > pulses_consumed ): - _LOGGER.debug("update_pulse_counter | consumption pulses reset") + _LOGGER.debug( + "update_pulse_counter | consumption pulses (%s > %s) reset", + self._pulses_consumption, + pulses_consumed + ) self._cons_last_hourly_reset = timestamp _LOGGER.debug( "update_pulse_counter | consumption hourly_reset_time=%s", @@ -453,6 +457,7 @@ def add_log( if not self._log_exists(address, slot): return False if address != self._last_log_address and slot != self._last_log_slot: + _LOGGER.debug("add_log | address-slot already exists") return False self._update_log_direction(address, slot, timestamp) self._update_log_references(address, slot) From 8213614720b1af8979deee3470f577d0137bfe3d Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 1 Apr 2025 17:05:36 +0200 Subject: [PATCH 089/157] Try test 4 --- tests/test_usb.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/test_usb.py b/tests/test_usb.py index ee23233b8..f47151387 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -1127,14 +1127,14 @@ def test_pulse_collection_consumption( test_timestamp, is_consumption=True ) == (45, pulse_update_4) tst_consumption.add_log(100, 2, (fixed_this_hour + td(hours=1)), 2222) - assert not tst_consumption.log_rollover + assert tst_consumption.log_rollover # Test collection of the last full hour assert tst_consumption.collected_pulses( fixed_this_hour, is_consumption=True - ) == (45 + 2222, pulse_update_3) + ) == (45 + 2222, pulse_update_4) pulse_update_5 = fixed_this_hour + td(hours=1, minutes=1, seconds=18) test_timestamp_2 = fixed_this_hour + td(hours=1, minutes=1, seconds=20) - tst_consumption.update_pulse_counter(145, 0, pulse_update_4) + tst_consumption.update_pulse_counter(145, 0, pulse_update_5) # Test collection of the last new hour assert tst_consumption.collected_pulses( test_timestamp_2, is_consumption=True @@ -1146,17 +1146,17 @@ def test_pulse_collection_consumption( # Test log rollover by updating log first before updating pulses - tst_consumption.add_log(100, 3, (fixed_this_hour + td(hours=3)), 3333) + tst_consumption.add_log(100, 3, (fixed_this_hour + td(hours=2)), 3333) assert tst_consumption.log_rollover assert tst_consumption.collected_pulses( fixed_this_hour, is_consumption=True - ) == (145 + 2222 + 3333, pulse_update_4) - pulse_update_5 = fixed_this_hour + td(hours=3, seconds=10) - tst_consumption.update_pulse_counter(321, 0, pulse_update_5) - assert not tst_consumption.log_rollover + ) == (145 + 2222 + 3333, pulse_update_5) + pulse_update_6 = fixed_this_hour + td(hours=2, seconds=10) + tst_consumption.update_pulse_counter(321, 0, pulse_update_6) + assert tst_consumption.log_rollover assert tst_consumption.collected_pulses( fixed_this_hour, is_consumption=True - ) == (2222 + 3333 + 321, pulse_update_5) + ) == (2222 + 3333 + 321, pulse_update_6) # Test next midnight rollover #pulse_update_6 = fixed_this_hour.replace(hour=0, minute=0, second=3) + td(days=1) From 7d64bfe10048cbc6ee16953d84377a3d39b5185c Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 1 Apr 2025 17:24:01 +0200 Subject: [PATCH 090/157] Extend update_pulse-counter logging --- plugwise_usb/nodes/helpers/pulses.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 2d79d8de1..57eb36dd1 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -290,11 +290,7 @@ def update_pulse_counter( self._pulses_consumption is not None and self._pulses_consumption > pulses_consumed ): - _LOGGER.debug( - "update_pulse_counter | consumption pulses (%s > %s) reset", - self._pulses_consumption, - pulses_consumed - ) + _LOGGER.debug("update_pulse_counter | consumption pulses reset") self._cons_last_hourly_reset = timestamp _LOGGER.debug( "update_pulse_counter | consumption hourly_reset_time=%s", @@ -326,7 +322,9 @@ def update_pulse_counter( self._rollover_production = True self._pulses_consumption = pulses_consumed + _LOGGER.debug("update_pulse_counter | consumption pulses=%s", self._pulses_consumption) self._pulses_production = pulses_produced + _LOGGER.debug("update_pulse_counter | production pulses=%s", self._pulses_production) def _update_rollover(self) -> None: """Update rollover states. From 00f9dfec56f1cc80c036869b3ecfc5efb7bc45e6 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 1 Apr 2025 17:27:07 +0200 Subject: [PATCH 091/157] Clean up test-code --- tests/test_usb.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/tests/test_usb.py b/tests/test_usb.py index f47151387..0d01e22e7 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -1139,11 +1139,6 @@ def test_pulse_collection_consumption( assert tst_consumption.collected_pulses( test_timestamp_2, is_consumption=True ) == (145, pulse_update_5) - #tst_consumption.add_log(100, 3, (fixed_this_hour + td(hours=2)), 3333) - #assert not tst_consumption.log_rollover - #pulse_update_5 = fixed_this_hour + td(hours=2, minutes=1, seconds=18) - #test_timestamp_3 = fixed_this_hour + td(hours=2, minutes=1, seconds=20) - # Test log rollover by updating log first before updating pulses tst_consumption.add_log(100, 3, (fixed_this_hour + td(hours=2)), 3333) @@ -1158,21 +1153,6 @@ def test_pulse_collection_consumption( fixed_this_hour, is_consumption=True ) == (2222 + 3333 + 321, pulse_update_6) - # Test next midnight rollover - #pulse_update_6 = fixed_this_hour.replace(hour=0, minute=0, second=3) + td(days=1) - #assert not tst_consumption.log_rollover - #tst_consumption.update_pulse_counter(584, 0, pulse_update_6) - #test_timestamp_3 = pulse_update_6 + td(hours=0, minutes=0, seconds=5) - #assert tst_consumption.collected_pulses( - # test_timestamp_3, is_consumption=True - #) == (14000, pulse_update_6) - #pulse_update_7 = fixed_this_hour.replace(hour=0, minute=1, second=3) - #tst_consumption.update_pulse_counter(50, 0, pulse_update_7) - #test_timestamp_4 = pulse_update_7 + td(hours=0, minutes=1, seconds=5) - #assert tst_consumption.collected_pulses( - # test_timestamp_3, is_consumption=True - #) == (500, pulse_update_7) - @freeze_time(dt.now()) def test_pulse_collection_consumption_empty( self, monkeypatch: pytest.MonkeyPatch From bc3ae98f6dcd2bdbe3a4512baaf3801ccd781dc2 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 1 Apr 2025 17:31:49 +0200 Subject: [PATCH 092/157] Reorganize updfate_pulse_counter() --- plugwise_usb/nodes/helpers/pulses.py | 49 +++++++++++++--------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 57eb36dd1..b4ec542ce 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -286,29 +286,29 @@ def update_pulse_counter( self._prod_pulsecounter_reset = False self._pulses_timestamp = timestamp self._update_rollover() - if ( - self._pulses_consumption is not None - and self._pulses_consumption > pulses_consumed - ): - _LOGGER.debug("update_pulse_counter | consumption pulses reset") - self._cons_last_hourly_reset = timestamp - _LOGGER.debug( - "update_pulse_counter | consumption hourly_reset_time=%s", - self._cons_last_hourly_reset, - ) - self._cons_pulsecounter_reset = True + if self._pulses_consumption is not None: + self._pulses_consumption = pulses_consumed + _LOGGER.debug("update_pulse_counter | consumption pulses=%s", self._pulses_consumption) + if self._pulses_consumption > pulses_consumed: + self._cons_pulsecounter_reset = True + _LOGGER.debug("update_pulse_counter | consumption pulses reset") + self._cons_last_hourly_reset = timestamp + _LOGGER.debug( + "update_pulse_counter | consumption hourly_reset_time=%s", + self._cons_last_hourly_reset, + ) - if ( - self._pulses_production is not None - and self._pulses_production < pulses_produced - ): - _LOGGER.debug("update_pulse_counter | production pulses reset") - self.prod_last_hourly_reset = timestamp - _LOGGER.debug( - "update_pulse_counter | production hourly_reset_time=%s", - self.prod_last_hourly_reset, - ) - self._prod_pulsecounter_reset = True + if self._pulses_production is not None: + self._pulses_production = pulses_produced + _LOGGER.debug("update_pulse_counter | production pulses=%s", self._pulses_production) + if self._pulses_production < pulses_produced: + self._prod_pulsecounter_reset = True + _LOGGER.debug("update_pulse_counter | production pulses reset") + self.prod_last_hourly_reset = timestamp + _LOGGER.debug( + "update_pulse_counter | production hourly_reset_time=%s", + self.prod_last_hourly_reset, + ) # No rollover based on time, check rollover based on counter reset # Required for special cases like nodes which have been powered off for several days @@ -321,11 +321,6 @@ def update_pulse_counter( _LOGGER.debug("update_pulse_counter | rollover production") self._rollover_production = True - self._pulses_consumption = pulses_consumed - _LOGGER.debug("update_pulse_counter | consumption pulses=%s", self._pulses_consumption) - self._pulses_production = pulses_produced - _LOGGER.debug("update_pulse_counter | production pulses=%s", self._pulses_production) - def _update_rollover(self) -> None: """Update rollover states. From 9c78ba4111f1dea3e82e4d6a98e62ba940f3f95f Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 1 Apr 2025 17:38:59 +0200 Subject: [PATCH 093/157] Revert most --- plugwise_usb/nodes/helpers/pulses.py | 49 +++++++++++++++------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index b4ec542ce..e48b1a60f 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -286,29 +286,29 @@ def update_pulse_counter( self._prod_pulsecounter_reset = False self._pulses_timestamp = timestamp self._update_rollover() - if self._pulses_consumption is not None: - self._pulses_consumption = pulses_consumed - _LOGGER.debug("update_pulse_counter | consumption pulses=%s", self._pulses_consumption) - if self._pulses_consumption > pulses_consumed: - self._cons_pulsecounter_reset = True - _LOGGER.debug("update_pulse_counter | consumption pulses reset") - self._cons_last_hourly_reset = timestamp - _LOGGER.debug( - "update_pulse_counter | consumption hourly_reset_time=%s", - self._cons_last_hourly_reset, - ) + if ( + self._pulses_consumption is not None + and self._pulses_consumption > pulses_consumed + ): + self._cons_pulsecounter_reset = True + _LOGGER.debug("update_pulse_counter | consumption pulses reset") + self._cons_last_hourly_reset = timestamp + _LOGGER.debug( + "update_pulse_counter | consumption hourly_reset_time=%s", + self._cons_last_hourly_reset, + ) - if self._pulses_production is not None: - self._pulses_production = pulses_produced - _LOGGER.debug("update_pulse_counter | production pulses=%s", self._pulses_production) - if self._pulses_production < pulses_produced: - self._prod_pulsecounter_reset = True - _LOGGER.debug("update_pulse_counter | production pulses reset") - self.prod_last_hourly_reset = timestamp - _LOGGER.debug( - "update_pulse_counter | production hourly_reset_time=%s", - self.prod_last_hourly_reset, - ) + if ( + self._pulses_production is not None + and self._pulses_production < pulses_produced + ): + self._prod_pulsecounter_reset = True + _LOGGER.debug("update_pulse_counter | production pulses reset") + self.prod_last_hourly_reset = timestamp + _LOGGER.debug( + "update_pulse_counter | production hourly_reset_time=%s", + self.prod_last_hourly_reset, + ) # No rollover based on time, check rollover based on counter reset # Required for special cases like nodes which have been powered off for several days @@ -321,6 +321,11 @@ def update_pulse_counter( _LOGGER.debug("update_pulse_counter | rollover production") self._rollover_production = True + self._pulses_consumption = pulses_consumed + _LOGGER.debug("update_pulse_counter | consumption pulses=%s", self._pulses_consumption) + self._pulses_production = pulses_produced + _LOGGER.debug("update_pulse_counter | production pulses=%s", self._pulses_production) + def _update_rollover(self) -> None: """Update rollover states. From cb3ae5e455456fe5b618054b3799fc6e4afd38d6 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 1 Apr 2025 17:44:51 +0200 Subject: [PATCH 094/157] Combine loggers --- plugwise_usb/nodes/helpers/pulses.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index e48b1a60f..542018f30 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -322,9 +322,12 @@ def update_pulse_counter( self._rollover_production = True self._pulses_consumption = pulses_consumed - _LOGGER.debug("update_pulse_counter | consumption pulses=%s", self._pulses_consumption) self._pulses_production = pulses_produced - _LOGGER.debug("update_pulse_counter | production pulses=%s", self._pulses_production) + _LOGGER.debug( + "update_pulse_counter | consumption pulses=%s | production pulses=%s", + self._pulses_consumption, + self._pulses_production, + ) def _update_rollover(self) -> None: """Update rollover states. From 2fd82f7645477dd42601458f451af3417907c305 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 1 Apr 2025 17:46:51 +0200 Subject: [PATCH 095/157] Clean up test-debugging --- plugwise_usb/nodes/helpers/pulses.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 542018f30..23b7ca789 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -236,7 +236,6 @@ def _collect_pulses_from_logs( return None timestamp = self._last_log_consumption_timestamp - _LOGGER.debug("HOI _last_log_consumption_timestamp=%s", self._last_log_consumption_timestamp) else: if self._last_log_production_timestamp is None: _LOGGER.debug( @@ -246,7 +245,6 @@ def _collect_pulses_from_logs( return None timestamp = self._last_log_production_timestamp - _LOGGER.debug("HOI _last_log_production_timestamp=%s", self._last_log_production_timestamp) missing_logs = self._logs_missing(from_timestamp) if missing_logs is None or missing_logs: From 04247d184c6d52a0784b44b314c5f6efff1eb07d Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 1 Apr 2025 17:47:26 +0200 Subject: [PATCH 096/157] Bump to a73 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 3caffb520..e2570fcee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a72" +version = "v0.40.0a73" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From 73974beeefe20026f57b84e1c69deb05c1c35b21 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 1 Apr 2025 18:01:30 +0200 Subject: [PATCH 097/157] Pre-commit fixes --- plugwise_usb/nodes/helpers/pulses.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 23b7ca789..6b2826338 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -302,10 +302,10 @@ def update_pulse_counter( ): self._prod_pulsecounter_reset = True _LOGGER.debug("update_pulse_counter | production pulses reset") - self.prod_last_hourly_reset = timestamp + self._prod_last_hourly_reset = timestamp _LOGGER.debug( "update_pulse_counter | production hourly_reset_time=%s", - self.prod_last_hourly_reset, + self._prod_last_hourly_reset, ) # No rollover based on time, check rollover based on counter reset @@ -768,8 +768,7 @@ def _update_log_references(self, address: int, slot: int) -> None: return log_time_stamp = self._logs[address][slot].timestamp - is_consumption = self._logs[address][slot].is_consumption - if is_consumption: + if (is_consumption := self._logs[address][slot].is_consumption): if self._cons_last_hourly_reset is not None: log_time_stamp = log_time_stamp + timedelta( minutes=self._cons_last_hourly_reset.minute, From 3b38975b730f92bcb30085dff6968f32a88d59f3 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 1 Apr 2025 18:53:55 +0200 Subject: [PATCH 098/157] Extend docstring of hourly_reset_time property --- plugwise_usb/nodes/helpers/pulses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 6b2826338..8126c1a0b 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -109,7 +109,7 @@ def collected_logs(self) -> int: @property def hourly_reset_time(self) -> datetime | None: - """Provide the device hourly pulse reset time.""" + """Provide the device hourly pulse reset time, using in testing.""" if (timestamp := self._cons_last_hourly_reset) is not None: return timestamp if (timestamp := self._prod_last_hourly_reset) is not None: From 320b6da832b4842e1d48d667a22a4032c0250dc8 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 1 Apr 2025 19:00:16 +0200 Subject: [PATCH 099/157] Add comment on log_time_stamp changes --- plugwise_usb/nodes/helpers/pulses.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 8126c1a0b..7f5c33454 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -768,6 +768,8 @@ def _update_log_references(self, address: int, slot: int) -> None: return log_time_stamp = self._logs[address][slot].timestamp + # Sync log_time_stamp with the device pulsecounter reset-time + # This syncs the daily reset of energy counters with the corresponding device pulsecounter reset if (is_consumption := self._logs[address][slot].is_consumption): if self._cons_last_hourly_reset is not None: log_time_stamp = log_time_stamp + timedelta( From d724d34915a5da737599828076e8dd043c6bbaeb Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 1 Apr 2025 19:02:01 +0200 Subject: [PATCH 100/157] Shorten log_time_stamp --- plugwise_usb/nodes/helpers/pulses.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 7f5c33454..362df432c 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -767,33 +767,33 @@ def _update_log_references(self, address: int, slot: int) -> None: if self._logs is None: return - log_time_stamp = self._logs[address][slot].timestamp - # Sync log_time_stamp with the device pulsecounter reset-time + log_timestamp = self._logs[address][slot].timestamp + # Sync log_timestamp with the device pulsecounter reset-time # This syncs the daily reset of energy counters with the corresponding device pulsecounter reset if (is_consumption := self._logs[address][slot].is_consumption): if self._cons_last_hourly_reset is not None: - log_time_stamp = log_time_stamp + timedelta( + log_timestamp = log_timestamp + timedelta( minutes=self._cons_last_hourly_reset.minute, seconds=self._cons_last_hourly_reset.second, microseconds=self._cons_last_hourly_reset.microsecond, ) elif self._prod_last_hourly_reset is not None: - log_time_stamp = log_time_stamp + timedelta( + log_timestamp = log_timestamp + timedelta( minutes=self._prod_last_hourly_reset.minute, seconds=self._prod_last_hourly_reset.second, microseconds=self._prod_last_hourly_reset.microsecond, ) # Update log references - self._update_first_log_reference(address, slot, log_time_stamp, is_consumption) - self._update_last_log_reference(address, slot, log_time_stamp, is_consumption) + self._update_first_log_reference(address, slot, log_timestamp, is_consumption) + self._update_last_log_reference(address, slot, log_timestamp, is_consumption) if is_consumption: - self._update_first_consumption_log_reference(address, slot, log_time_stamp) - self._update_last_consumption_log_reference(address, slot, log_time_stamp) + self._update_first_consumption_log_reference(address, slot, log_timestamp) + self._update_last_consumption_log_reference(address, slot, log_timestamp) elif self._log_production: - self._update_first_production_log_reference(address, slot, log_time_stamp) - self._update_last_production_log_reference(address, slot, log_time_stamp) + self._update_first_production_log_reference(address, slot, log_timestamp) + self._update_last_production_log_reference(address, slot, log_timestamp) @property def log_addresses_missing(self) -> list[int] | None: From 75cf08dd281672c1f3c4e39bd769526e0e303097 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 2 Apr 2025 09:30:51 +0200 Subject: [PATCH 101/157] Remove week-related from api --- plugwise_usb/api.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugwise_usb/api.py b/plugwise_usb/api.py index bbbfdaa6c..ce9cdbf38 100644 --- a/plugwise_usb/api.py +++ b/plugwise_usb/api.py @@ -216,14 +216,10 @@ class EnergyStatistics: hour_consumption_reset: datetime | None = None day_consumption: float | None = None day_consumption_reset: datetime | None = None - week_consumption: float | None = None - week_consumption_reset: datetime | None = None hour_production: float | None = None hour_production_reset: datetime | None = None day_production: float | None = None day_production_reset: datetime | None = None - week_production: float | None = None - week_production_reset: datetime | None = None class PlugwiseNode(Protocol): From 9c62ac89a42ecb67e948c311df792ef8efa328a2 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 2 Apr 2025 09:52:34 +0200 Subject: [PATCH 102/157] Delay/sync the from_timestamps as well --- plugwise_usb/nodes/helpers/pulses.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 362df432c..94cd54ed5 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -172,6 +172,23 @@ def collected_pulses( self, from_timestamp: datetime, is_consumption: bool ) -> tuple[int | None, datetime | None]: """Calculate total pulses from given timestamp.""" + + # Sync from_timestamp with the device pulsecounter reset-time + # This syncs the hourly/daily reset of energy counters with the corresponding device pulsecounter reset + if is_consumption: + if self._cons_last_hourly_reset is not None : + from_timestamp = from_timestamp + timedelta( + minutes=self._cons_last_hourly_reset.minute, + seconds=self._cons_last_hourly_reset.second, + microseconds=self._cons_last_hourly_reset.microsecond, + ) + elif self._prod_last_hourly_reset is not None: + from_timestamp = from_timestamp + timedelta( + minutes=self._prod_last_hourly_reset.minute, + seconds=self._prod_last_hourly_reset.second, + microseconds=self._prod_last_hourly_reset.microsecond, + ) + _LOGGER.debug( "collected_pulses | %s | from_timestamp=%s | is_cons=%s | _log_production=%s", self._mac, From d3ea29d7ca9007b2989d39bc93f976066dfb144d Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 2 Apr 2025 10:05:02 +0200 Subject: [PATCH 103/157] Bump to a74 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e2570fcee..5163f67fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a73" +version = "v0.40.0a74" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From 5a390620a49d225285bd0d04755277d85293256a Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 2 Apr 2025 10:33:29 +0200 Subject: [PATCH 104/157] Revert from_timestamp additions, add properties instead --- plugwise_usb/nodes/helpers/pulses.py | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 94cd54ed5..e35204e01 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -107,6 +107,16 @@ def collected_logs(self) -> int: counter += len(self._logs[address]) return counter + @property + def consumption_last_hourly_reset(self) -> datetime | None: + """Consumption last hourly reset.""" + return self._cons_last_hourly_reset + + @property + def production_last_hourly_reset(self) -> datetime | None: + """Production last hourly reset.""" + return self._prod_last_hourly_reset + @property def hourly_reset_time(self) -> datetime | None: """Provide the device hourly pulse reset time, using in testing.""" @@ -172,23 +182,6 @@ def collected_pulses( self, from_timestamp: datetime, is_consumption: bool ) -> tuple[int | None, datetime | None]: """Calculate total pulses from given timestamp.""" - - # Sync from_timestamp with the device pulsecounter reset-time - # This syncs the hourly/daily reset of energy counters with the corresponding device pulsecounter reset - if is_consumption: - if self._cons_last_hourly_reset is not None : - from_timestamp = from_timestamp + timedelta( - minutes=self._cons_last_hourly_reset.minute, - seconds=self._cons_last_hourly_reset.second, - microseconds=self._cons_last_hourly_reset.microsecond, - ) - elif self._prod_last_hourly_reset is not None: - from_timestamp = from_timestamp + timedelta( - minutes=self._prod_last_hourly_reset.minute, - seconds=self._prod_last_hourly_reset.second, - microseconds=self._prod_last_hourly_reset.microsecond, - ) - _LOGGER.debug( "collected_pulses | %s | from_timestamp=%s | is_cons=%s | _log_production=%s", self._mac, From 23897d51135169d5cb3ad19a378f8d0acf285305 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 2 Apr 2025 10:46:37 +0200 Subject: [PATCH 105/157] Sync the daily reset time with the device pulsecounter(s) reset --- plugwise_usb/nodes/helpers/counter.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index e7c585c61..3d0279dac 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -284,9 +284,27 @@ def update( """Get pulse update.""" last_reset = datetime.now(tz=LOCAL_TIMEZONE) if self._energy_id in ENERGY_HOUR_COUNTERS: + # No syncing needed for the hour-counters, they reset when the device pulsecounter(s) reset last_reset = last_reset.replace(minute=0, second=0, microsecond=0) if self._energy_id in ENERGY_DAY_COUNTERS: - last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) + # Sync the daily reset time with the device pulsecounter(s) reset time + if self._is_consumption: + if self._pulse_collection.consumption_last_hourly_reset is not None: + last_reset = last_reset.replace( + hour=0, + minute=self._pulse_collection.consumption_last_hourly_reset.minutes, + second=self._pulse_collection.consumption_last_hourly_reset.seconds, + microsecond=self._pulse_collection.consumption_last_hourly_reset.microseconds, + ) + elif self._pulse_collection.production_last_hourly_reset is not None: + last_reset = last_reset.replace( + hour=0, + minute=self._pulse_collection.production_last_hourly_reset.minutes, + second=self._pulse_collection.production_last_hourly_reset.seconds, + microsecond=self._pulse_collection.production_last_hourly_reset.microseconds, + ) + else: + last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) pulses, last_update = pulse_collection.collected_pulses( last_reset, self._is_consumption From a2203081604cfc712066748304fedaa4d4d6a5c7 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 2 Apr 2025 10:54:14 +0200 Subject: [PATCH 106/157] Fix missing logic --- plugwise_usb/nodes/helpers/counter.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index 3d0279dac..19d26b9b2 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -296,6 +296,8 @@ def update( second=self._pulse_collection.consumption_last_hourly_reset.seconds, microsecond=self._pulse_collection.consumption_last_hourly_reset.microseconds, ) + else: + last_reset = last_reset.replace(minute=0, second=0, microsecond=0) elif self._pulse_collection.production_last_hourly_reset is not None: last_reset = last_reset.replace( hour=0, From b93b778cc822707e57a2c10bcce5400cba354459 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 2 Apr 2025 11:02:59 +0200 Subject: [PATCH 107/157] Fix missing hour-reset --- plugwise_usb/nodes/helpers/counter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index 19d26b9b2..17a3c7e7e 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -297,7 +297,7 @@ def update( microsecond=self._pulse_collection.consumption_last_hourly_reset.microseconds, ) else: - last_reset = last_reset.replace(minute=0, second=0, microsecond=0) + last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) elif self._pulse_collection.production_last_hourly_reset is not None: last_reset = last_reset.replace( hour=0, From 56672f58149d67683aa2cbf7bbbbc0a733fa368d Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 2 Apr 2025 18:53:32 +0200 Subject: [PATCH 108/157] Bump to a75 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 5163f67fa..d8752627a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a74" +version = "v0.40.0a75" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From e37c0cd3e399a9586b618cef30b3fde840525913 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 2 Apr 2025 20:22:05 +0200 Subject: [PATCH 109/157] Consumption/production last_hourly resets are the same --- plugwise_usb/nodes/helpers/pulses.py | 45 ++++++++-------------------- 1 file changed, 12 insertions(+), 33 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index e35204e01..feacb0f36 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -67,8 +67,7 @@ def __init__(self, mac: str) -> None: self._last_empty_log_address: int | None = None self._last_empty_log_slot: int | None = None - self._cons_last_hourly_reset: datetime | None = None - self._prod_last_hourly_reset: datetime | None = None + self._last_hourly_reset: datetime | None = None self._last_log_consumption_timestamp: datetime | None = None self._last_log_consumption_address: int | None = None self._last_log_consumption_slot: int | None = None @@ -108,21 +107,14 @@ def collected_logs(self) -> int: return counter @property - def consumption_last_hourly_reset(self) -> datetime | None: - """Consumption last hourly reset.""" - return self._cons_last_hourly_reset - - @property - def production_last_hourly_reset(self) -> datetime | None: - """Production last hourly reset.""" - return self._prod_last_hourly_reset + def last_hourly_reset(self) -> datetime | None: + """Consumption and production last hourly reset.""" + return self._last_hourly_reset @property def hourly_reset_time(self) -> datetime | None: """Provide the device hourly pulse reset time, using in testing.""" - if (timestamp := self._cons_last_hourly_reset) is not None: - return timestamp - if (timestamp := self._prod_last_hourly_reset) is not None: + if (timestamp := self._last_hourly_reset) is not None: return timestamp return None @@ -300,10 +292,10 @@ def update_pulse_counter( ): self._cons_pulsecounter_reset = True _LOGGER.debug("update_pulse_counter | consumption pulses reset") - self._cons_last_hourly_reset = timestamp + self._last_hourly_reset = timestamp _LOGGER.debug( - "update_pulse_counter | consumption hourly_reset_time=%s", - self._cons_last_hourly_reset, + "update_pulse_counter | hourly_reset_time=%s", + self._last_hourly_reset, ) if ( @@ -311,12 +303,6 @@ def update_pulse_counter( and self._pulses_production < pulses_produced ): self._prod_pulsecounter_reset = True - _LOGGER.debug("update_pulse_counter | production pulses reset") - self._prod_last_hourly_reset = timestamp - _LOGGER.debug( - "update_pulse_counter | production hourly_reset_time=%s", - self._prod_last_hourly_reset, - ) # No rollover based on time, check rollover based on counter reset # Required for special cases like nodes which have been powered off for several days @@ -780,18 +766,11 @@ def _update_log_references(self, address: int, slot: int) -> None: log_timestamp = self._logs[address][slot].timestamp # Sync log_timestamp with the device pulsecounter reset-time # This syncs the daily reset of energy counters with the corresponding device pulsecounter reset - if (is_consumption := self._logs[address][slot].is_consumption): - if self._cons_last_hourly_reset is not None: - log_timestamp = log_timestamp + timedelta( - minutes=self._cons_last_hourly_reset.minute, - seconds=self._cons_last_hourly_reset.second, - microseconds=self._cons_last_hourly_reset.microsecond, - ) - elif self._prod_last_hourly_reset is not None: + if self._last_hourly_reset is not None: log_timestamp = log_timestamp + timedelta( - minutes=self._prod_last_hourly_reset.minute, - seconds=self._prod_last_hourly_reset.second, - microseconds=self._prod_last_hourly_reset.microsecond, + minutes=self._last_hourly_reset.minute, + seconds=self._last_hourly_reset.second, + microseconds=self._last_hourly_reset.microsecond, ) # Update log references From 4093be8d40d1785b2520f9db34bf1424c311c178 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 2 Apr 2025 20:24:30 +0200 Subject: [PATCH 110/157] Adapt code in counter.py --- plugwise_usb/nodes/helpers/counter.py | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index 17a3c7e7e..374f18b2e 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -288,25 +288,14 @@ def update( last_reset = last_reset.replace(minute=0, second=0, microsecond=0) if self._energy_id in ENERGY_DAY_COUNTERS: # Sync the daily reset time with the device pulsecounter(s) reset time - if self._is_consumption: - if self._pulse_collection.consumption_last_hourly_reset is not None: - last_reset = last_reset.replace( - hour=0, - minute=self._pulse_collection.consumption_last_hourly_reset.minutes, - second=self._pulse_collection.consumption_last_hourly_reset.seconds, - microsecond=self._pulse_collection.consumption_last_hourly_reset.microseconds, - ) - else: - last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) - elif self._pulse_collection.production_last_hourly_reset is not None: + last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) + if self._pulse_collection.last_hourly_reset is not None: last_reset = last_reset.replace( hour=0, - minute=self._pulse_collection.production_last_hourly_reset.minutes, - second=self._pulse_collection.production_last_hourly_reset.seconds, - microsecond=self._pulse_collection.production_last_hourly_reset.microseconds, + minute=self._pulse_collection.last_hourly_reset.minutes, + second=self._pulse_collection.last_hourly_reset.seconds, + microsecond=self._pulse_collection.last_hourly_reset.microseconds, ) - else: - last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) pulses, last_update = pulse_collection.collected_pulses( last_reset, self._is_consumption From c99400b085ea320c0da3c2b83d157540d7c6fe92 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 2 Apr 2025 20:27:13 +0200 Subject: [PATCH 111/157] Fix missing line --- plugwise_usb/nodes/helpers/pulses.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index feacb0f36..5bf015b9d 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -764,6 +764,7 @@ def _update_log_references(self, address: int, slot: int) -> None: return log_timestamp = self._logs[address][slot].timestamp + is_consumption = self._logs[address][slot].is_consumption # Sync log_timestamp with the device pulsecounter reset-time # This syncs the daily reset of energy counters with the corresponding device pulsecounter reset if self._last_hourly_reset is not None: From 9cfcd07055bea3c5fb94c4f1b866332a114b1a88 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 3 Apr 2025 08:02:54 +0200 Subject: [PATCH 112/157] Don't use self, use availble input --- plugwise_usb/nodes/helpers/counter.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index 374f18b2e..b44a8acc0 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -206,7 +206,6 @@ def __init__( self._is_consumption = False self._last_reset: datetime | None = None self._last_update: datetime | None = None - self._pulse_collection = PulseCollection(mac) self._pulses: int | None = None @property @@ -289,12 +288,12 @@ def update( if self._energy_id in ENERGY_DAY_COUNTERS: # Sync the daily reset time with the device pulsecounter(s) reset time last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) - if self._pulse_collection.last_hourly_reset is not None: + if pulse_collection.last_hourly_reset is not None: last_reset = last_reset.replace( hour=0, - minute=self._pulse_collection.last_hourly_reset.minutes, - second=self._pulse_collection.last_hourly_reset.seconds, - microsecond=self._pulse_collection.last_hourly_reset.microseconds, + minute=pulse_collection.last_hourly_reset.minutes, + second=pulse_collection.last_hourly_reset.seconds, + microsecond=pulse_collection.last_hourly_reset.microseconds, ) pulses, last_update = pulse_collection.collected_pulses( From 49a06f158463ff50f9d0da784e304b098cc357c9 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 3 Apr 2025 08:07:55 +0200 Subject: [PATCH 113/157] Revert deletion, improve debug-logging --- plugwise_usb/nodes/helpers/pulses.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 5bf015b9d..eac96671a 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -291,10 +291,9 @@ def update_pulse_counter( and self._pulses_consumption > pulses_consumed ): self._cons_pulsecounter_reset = True - _LOGGER.debug("update_pulse_counter | consumption pulses reset") self._last_hourly_reset = timestamp _LOGGER.debug( - "update_pulse_counter | hourly_reset_time=%s", + "update_pulse_counter | consumption pulses reset | hourly_reset_time=%s", self._last_hourly_reset, ) @@ -303,7 +302,10 @@ def update_pulse_counter( and self._pulses_production < pulses_produced ): self._prod_pulsecounter_reset = True - + _LOGGER.debug( + "update_pulse_counter | production pulses reset | hourly_reset_time=%s", + self._last_hourly_reset, + ) # No rollover based on time, check rollover based on counter reset # Required for special cases like nodes which have been powered off for several days if not (self._rollover_consumption or self._rollover_production): From df42a5f87dc0249f7cc0ddfd8f75faca994e94db Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 3 Apr 2025 08:13:49 +0200 Subject: [PATCH 114/157] Bump to a76 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d8752627a..f7c2fbbdd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a75" +version = "v0.40.0a76" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From dad3da3c1f0a8a20b7ff4cde1bc33ad8ed055f5d Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 3 Apr 2025 09:10:42 +0200 Subject: [PATCH 115/157] Fix datetime attributes --- plugwise_usb/nodes/helpers/counter.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index b44a8acc0..dd4ee3451 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -291,9 +291,9 @@ def update( if pulse_collection.last_hourly_reset is not None: last_reset = last_reset.replace( hour=0, - minute=pulse_collection.last_hourly_reset.minutes, - second=pulse_collection.last_hourly_reset.seconds, - microsecond=pulse_collection.last_hourly_reset.microseconds, + minute=pulse_collection.last_hourly_reset.minute, + second=pulse_collection.last_hourly_reset.second, + microsecond=pulse_collection.last_hourly_reset.microsecond, ) pulses, last_update = pulse_collection.collected_pulses( From 657714956888f9c5f292ae27960cb6c04b5c36d4 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 3 Apr 2025 09:11:11 +0200 Subject: [PATCH 116/157] Bump to a77 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f7c2fbbdd..56c9e581f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a76" +version = "v0.40.0a77" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From c10bf4be436d6ea5d4d12f3b8347af8fd4bba9ea Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 3 Apr 2025 17:54:45 +0200 Subject: [PATCH 117/157] Refactor update_rollover() --- plugwise_usb/nodes/helpers/pulses.py | 87 ++++++++++++---------------- 1 file changed, 37 insertions(+), 50 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index eac96671a..f574f2ba2 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -335,72 +335,59 @@ def _update_rollover(self) -> None: if self._log_addresses_missing is not None and self._log_addresses_missing: return + self._rollover_consumption = self._detect_rollover( + self._last_log_consumption_timestamp, + self._next_log_consumption_timestamp, + ) + if self._log_production: + self._rollover_production = self._detect_rollover( + self._last_log_production_timestamp, + self._next_log_production_timestamp, + False, + ) + + def _detect_rollover( + self, + last_log_timestamp: datetime | None, + next_log_timestamp: datetime | None, + is_consumption=True, + ) -> bool: + """Helper function for _update_rollover().""" if ( self._pulses_timestamp is None - or self._last_log_consumption_timestamp is None - or self._next_log_consumption_timestamp is None + or last_log_timestamp is None + or next_log_timestamp is None ): # Unable to determine rollover return - if self._pulses_timestamp > self._next_log_consumption_timestamp: - self._rollover_consumption = True - _LOGGER.debug( - "_update_rollover | %s | set consumption rollover => pulses newer", - self._mac, - ) - elif self._pulses_timestamp < self._last_log_consumption_timestamp: - self._rollover_consumption = True + direction = "consumption" + if not is_consumption: + direction = "production" + + if self._pulses_timestamp > next_log_timestamp: _LOGGER.debug( - "_update_rollover | %s | set consumption rollover => log newer", + "_update_rollover | %s | set %s rollover => pulses newer", self._mac, + direction, ) - elif ( - self._last_log_consumption_timestamp - < self._pulses_timestamp - < self._next_log_consumption_timestamp - ): - if self._rollover_consumption: - _LOGGER.debug("_update_rollover | %s | reset consumption", self._mac) - self._rollover_consumption = False - else: - _LOGGER.debug("_update_rollover | %s | unexpected consumption", self._mac) - - if not self._log_production: - return - - if ( - self._last_log_production_timestamp is None - or self._next_log_production_timestamp is None - ): - # Unable to determine rollover - return - - if not self._log_production: - return + return True - if self._pulses_timestamp > self._next_log_production_timestamp: - self._rollover_production = True + if self._pulses_timestamp < last_log_timestamp: _LOGGER.debug( - "_update_rollover | %s | set production rollover => pulses newer", + "_update_rollover | %s | set %s rollover => log newer", self._mac, + direction, ) - elif self._pulses_timestamp < self._last_log_production_timestamp: - self._rollover_production = True + return True + + if last_log_timestamp < self._pulses_timestamp < next_log_timestamp: _LOGGER.debug( - "_update_rollover | %s | reset production rollover => log newer", + "_update_rollover | %s | reset %s rollover", self._mac, + direction ) - elif ( - self._last_log_production_timestamp - < self._pulses_timestamp - < self._next_log_production_timestamp - ): - if self._rollover_production: - _LOGGER.debug("_update_rollover | %s | reset production", self._mac) - self._rollover_production = False - else: - _LOGGER.debug("_update_rollover | %s | unexpected production", self._mac) + return False def add_empty_log(self, address: int, slot: int) -> None: """Add empty energy log record to mark any start of beginning of energy log collection.""" From aaa1c732bab59ca787db9de6a51b801dfb9e0281 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 3 Apr 2025 20:13:43 +0200 Subject: [PATCH 118/157] _detect_rollover(): full coverage by using <= --- plugwise_usb/nodes/helpers/pulses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index f574f2ba2..3aeb66a11 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -381,7 +381,7 @@ def _detect_rollover( ) return True - if last_log_timestamp < self._pulses_timestamp < next_log_timestamp: + if last_log_timestamp <= self._pulses_timestamp <= next_log_timestamp: _LOGGER.debug( "_update_rollover | %s | reset %s rollover", self._mac, From 77fb8d382c4c7e8fea4aeaf56e329d0838dfc05f Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 3 Apr 2025 20:19:41 +0200 Subject: [PATCH 119/157] Test: add log after pulse counter reset timestamp --- tests/test_usb.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_usb.py b/tests/test_usb.py index 0d01e22e7..13aa9eb72 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -1122,11 +1122,11 @@ def test_pulse_collection_consumption( tst_consumption.update_pulse_counter(45, 0, pulse_update_4) assert tst_consumption.log_rollover assert tst_consumption.hourly_reset_time == pulse_update_4 - test_timestamp = fixed_this_hour + td(hours=1, minutes=1, seconds=5) + test_timestamp = fixed_this_hour + td(hours=1, minutes=1, seconds=4) assert tst_consumption.collected_pulses( test_timestamp, is_consumption=True ) == (45, pulse_update_4) - tst_consumption.add_log(100, 2, (fixed_this_hour + td(hours=1)), 2222) + tst_consumption.add_log(100, 2, (fixed_this_hour + td(hours=1, minutes=1, seconds=5)), 2222) assert tst_consumption.log_rollover # Test collection of the last full hour assert tst_consumption.collected_pulses( From e513141a5b443619943bc2807c0a592254c4bbd6 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 4 Apr 2025 08:12:12 +0200 Subject: [PATCH 120/157] Improve comment --- plugwise_usb/nodes/helpers/pulses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 3aeb66a11..997e51092 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -306,7 +306,7 @@ def update_pulse_counter( "update_pulse_counter | production pulses reset | hourly_reset_time=%s", self._last_hourly_reset, ) - # No rollover based on time, check rollover based on counter reset + # No rollover based on time, set rollover based on counter reset # Required for special cases like nodes which have been powered off for several days if not (self._rollover_consumption or self._rollover_production): if self._cons_pulsecounter_reset: From d3253a9545c23d0e55d619e20d331e0b70b6dba0 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 4 Apr 2025 08:21:23 +0200 Subject: [PATCH 121/157] Add working-comments --- plugwise_usb/nodes/helpers/pulses.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 997e51092..610184780 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -124,7 +124,7 @@ def logs(self) -> dict[int, dict[int, PulseLogRecord]]: if self._logs is None: return {} sorted_log: dict[int, dict[int, PulseLogRecord]] = {} - skip_before = datetime.now(tz=UTC) - timedelta(hours=MAX_LOG_HOURS) + skip_before = datetime.now(tz=UTC) - timedelta(hours=MAX_LOG_HOURS) # Should this timedelta be adapted? sorted_addresses = sorted(self._logs.keys(), reverse=True) for address in sorted_addresses: sorted_slots = sorted(self._logs[address].keys(), reverse=True) @@ -462,7 +462,7 @@ def add_log( def recalculate_missing_log_addresses(self) -> None: """Recalculate missing log addresses.""" self._log_addresses_missing = self._logs_missing( - datetime.now(tz=UTC) - timedelta(hours=MAX_LOG_HOURS) + datetime.now(tz=UTC) - timedelta(hours=MAX_LOG_HOURS) # idem ) def _add_log_record( @@ -481,7 +481,7 @@ def _add_log_record( # Drop useless log records when we have at least 4 logs if self.collected_logs > 4 and log_record.timestamp < ( - datetime.now(tz=UTC) - timedelta(hours=MAX_LOG_HOURS) + datetime.now(tz=UTC) - timedelta(hours=MAX_LOG_HOURS) # idem ): return False From c79a5abc283247ee5cd161c223c94c6bae2f6f3f Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 4 Apr 2025 10:55:37 +0200 Subject: [PATCH 122/157] Remove double occurence --- plugwise_usb/nodes/helpers/counter.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index dd4ee3451..6b3d5b05d 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -146,9 +146,6 @@ def update(self) -> None: self._energy_statistics.log_interval_consumption = ( self._pulse_collection.log_interval_consumption ) - self._energy_statistics.log_interval_production = ( - self._pulse_collection.log_interval_production - ) ( self._energy_statistics.hour_consumption, self._energy_statistics.hour_consumption_reset, From 9d1acff3f2feba1a600771789ce7fd3e425b612c Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 4 Apr 2025 11:22:57 +0200 Subject: [PATCH 123/157] Remove last_hourly_reset Already present as hourly_reset_time --- plugwise_usb/nodes/helpers/counter.py | 8 ++++---- plugwise_usb/nodes/helpers/pulses.py | 7 +------ 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index 6b3d5b05d..0d9d53d81 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -285,12 +285,12 @@ def update( if self._energy_id in ENERGY_DAY_COUNTERS: # Sync the daily reset time with the device pulsecounter(s) reset time last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) - if pulse_collection.last_hourly_reset is not None: + if pulse_collection.hourly_reset_time is not None: last_reset = last_reset.replace( hour=0, - minute=pulse_collection.last_hourly_reset.minute, - second=pulse_collection.last_hourly_reset.second, - microsecond=pulse_collection.last_hourly_reset.microsecond, + minute=pulse_collection.hourly_reset_time.minute, + second=pulse_collection.hourly_reset_time.second, + microsecond=pulse_collection.hourly_reset_time.microsecond, ) pulses, last_update = pulse_collection.collected_pulses( diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 610184780..6ada54eaa 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -106,14 +106,9 @@ def collected_logs(self) -> int: counter += len(self._logs[address]) return counter - @property - def last_hourly_reset(self) -> datetime | None: - """Consumption and production last hourly reset.""" - return self._last_hourly_reset - @property def hourly_reset_time(self) -> datetime | None: - """Provide the device hourly pulse reset time, using in testing.""" + """Provide the device hourly pulse reset time.""" if (timestamp := self._last_hourly_reset) is not None: return timestamp return None From 5b299031db7e59f2d8096774df10352f120ddc48 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 4 Apr 2025 11:43:15 +0200 Subject: [PATCH 124/157] Use the pulse_counter_reset event to update the daily last_reset --- plugwise_usb/nodes/helpers/counter.py | 12 +++--------- plugwise_usb/nodes/helpers/pulses.py | 4 ++++ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index 0d9d53d81..a1e124751 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -283,15 +283,9 @@ def update( # No syncing needed for the hour-counters, they reset when the device pulsecounter(s) reset last_reset = last_reset.replace(minute=0, second=0, microsecond=0) if self._energy_id in ENERGY_DAY_COUNTERS: - # Sync the daily reset time with the device pulsecounter(s) reset time - last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) - if pulse_collection.hourly_reset_time is not None: - last_reset = last_reset.replace( - hour=0, - minute=pulse_collection.hourly_reset_time.minute, - second=pulse_collection.hourly_reset_time.second, - microsecond=pulse_collection.hourly_reset_time.microsecond, - ) + # Postpone the daily reset time change until the device pulsecounter(s) reset + if pulse_collection.pulse_counter_reset: + last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) pulses, last_update = pulse_collection.collected_pulses( last_reset, self._is_consumption diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 6ada54eaa..76c5cd796 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -165,6 +165,10 @@ def last_update(self) -> datetime | None: """Return timestamp of last update.""" return self._pulses_timestamp + def pulse_counter_reset(self) -> bool: + """Return a pulse_counter reset.""" + return self._cons_pulsecounter_reset or self._prod_pulsecounter_reset + def collected_pulses( self, from_timestamp: datetime, is_consumption: bool ) -> tuple[int | None, datetime | None]: From c7404d3e379d9169035092597223d0e5cefe1281 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 4 Apr 2025 11:46:04 +0200 Subject: [PATCH 125/157] Improve comment --- plugwise_usb/nodes/helpers/counter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index a1e124751..2cb1da3d5 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -283,7 +283,7 @@ def update( # No syncing needed for the hour-counters, they reset when the device pulsecounter(s) reset last_reset = last_reset.replace(minute=0, second=0, microsecond=0) if self._energy_id in ENERGY_DAY_COUNTERS: - # Postpone the daily reset time change until the device pulsecounter(s) reset + # Postpone the daily last_reset time-change until a device pulsecounter resets if pulse_collection.pulse_counter_reset: last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) From a25f73c4311894e869a70c378194d4d6481685c1 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 4 Apr 2025 11:51:44 +0200 Subject: [PATCH 126/157] Remove working-comments --- plugwise_usb/nodes/helpers/pulses.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 76c5cd796..ae6e0d68d 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -119,7 +119,7 @@ def logs(self) -> dict[int, dict[int, PulseLogRecord]]: if self._logs is None: return {} sorted_log: dict[int, dict[int, PulseLogRecord]] = {} - skip_before = datetime.now(tz=UTC) - timedelta(hours=MAX_LOG_HOURS) # Should this timedelta be adapted? + skip_before = datetime.now(tz=UTC) - timedelta(hours=MAX_LOG_HOURS) sorted_addresses = sorted(self._logs.keys(), reverse=True) for address in sorted_addresses: sorted_slots = sorted(self._logs[address].keys(), reverse=True) @@ -461,7 +461,7 @@ def add_log( def recalculate_missing_log_addresses(self) -> None: """Recalculate missing log addresses.""" self._log_addresses_missing = self._logs_missing( - datetime.now(tz=UTC) - timedelta(hours=MAX_LOG_HOURS) # idem + datetime.now(tz=UTC) - timedelta(hours=MAX_LOG_HOURS) ) def _add_log_record( @@ -480,7 +480,7 @@ def _add_log_record( # Drop useless log records when we have at least 4 logs if self.collected_logs > 4 and log_record.timestamp < ( - datetime.now(tz=UTC) - timedelta(hours=MAX_LOG_HOURS) # idem + datetime.now(tz=UTC) - timedelta(hours=MAX_LOG_HOURS) ): return False From ee1d2fe22aab4180aed7f47f5c784a2637b79831 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 4 Apr 2025 12:07:52 +0200 Subject: [PATCH 127/157] Bump to a78 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 56c9e581f..c210bd7a8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a77" +version = "v0.40.0a78" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From ca83ca2a8497249a9d5871a8b8ba893e38449414 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 5 Apr 2025 11:39:10 +0200 Subject: [PATCH 128/157] Use sorted logs to collect log_pulses --- plugwise_usb/nodes/helpers/pulses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index ae6e0d68d..27dd8bcb6 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -257,7 +257,7 @@ def _collect_pulses_from_logs( return None log_pulses = 0 - for log_item in self._logs.values(): + for log_item in self.logs.values(): for slot_item in log_item.values(): if ( slot_item.is_consumption == is_consumption From e81fc18c7c2087e654b4fed49b2a5d0a1f5b62b8 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 5 Apr 2025 12:42:09 +0200 Subject: [PATCH 129/157] Update last_reset logic once more --- plugwise_usb/nodes/helpers/counter.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index 2cb1da3d5..fa1ab3cce 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -2,7 +2,7 @@ from __future__ import annotations -from datetime import datetime +from datetime import datetime, timedelta from enum import Enum, auto import logging from typing import Final @@ -280,12 +280,14 @@ def update( """Get pulse update.""" last_reset = datetime.now(tz=LOCAL_TIMEZONE) if self._energy_id in ENERGY_HOUR_COUNTERS: - # No syncing needed for the hour-counters, they reset when the device pulsecounter(s) reset last_reset = last_reset.replace(minute=0, second=0, microsecond=0) if self._energy_id in ENERGY_DAY_COUNTERS: - # Postpone the daily last_reset time-change until a device pulsecounter resets - if pulse_collection.pulse_counter_reset: - last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) + last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) + # Postpone the last_reset time-changes at day-end until a device pulsecounter resets + if last_reset.hour == 0 and not pulse_collection.pulse_counter_reset: + last_reset = (last_reset - timedelta(days=1)).replace( + hour=0, minute=0, second=0, microsecond=0 + ) pulses, last_update = pulse_collection.collected_pulses( last_reset, self._is_consumption From 3bad8cf1e57a0cda431b7315272a5c80b787886d Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 5 Apr 2025 12:51:39 +0200 Subject: [PATCH 130/157] Bump to a79 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c210bd7a8..21889dbe1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a78" +version = "v0.40.0a79" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From fa8b9ff9e9429488027b8ca97846f56147f96731 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 5 Apr 2025 13:26:55 +0200 Subject: [PATCH 131/157] Last_reset: handle state after pulse_counter reset returns back to False --- plugwise_usb/nodes/helpers/counter.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index fa1ab3cce..58b7d746c 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -189,6 +189,7 @@ def __init__( ) -> None: """Initialize energy counter based on energy id.""" self._mac = mac + self._midnight_reset_passed = False if energy_id not in ENERGY_COUNTERS: raise EnergyError(f"Invalid energy id '{energy_id}' for Energy counter") self._calibration: EnergyCalibration | None = None @@ -279,15 +280,22 @@ def update( ) -> tuple[float | None, datetime | None]: """Get pulse update.""" last_reset = datetime.now(tz=LOCAL_TIMEZONE) + if self._midnight_reset_passed and last_reset.hour == 1: + self._midnight_reset_passed = False + if self._energy_id in ENERGY_HOUR_COUNTERS: last_reset = last_reset.replace(minute=0, second=0, microsecond=0) if self._energy_id in ENERGY_DAY_COUNTERS: last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) # Postpone the last_reset time-changes at day-end until a device pulsecounter resets - if last_reset.hour == 0 and not pulse_collection.pulse_counter_reset: + if last_reset.hour == 0 and ( + not pulse_collection.pulse_counter_reset + and not self._midnight_reset_passed + ): last_reset = (last_reset - timedelta(days=1)).replace( hour=0, minute=0, second=0, microsecond=0 ) + self._midnight_reset_passed = True pulses, last_update = pulse_collection.collected_pulses( last_reset, self._is_consumption @@ -307,3 +315,4 @@ def update( energy = self.energy _LOGGER.debug("energy=%s on last_update=%s", energy, last_update) return (energy, last_reset) + From 00c384c11e95206073c81a86b37db687ec2d619e Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 5 Apr 2025 13:30:14 +0200 Subject: [PATCH 132/157] Bump to a80 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 21889dbe1..0143fb721 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a79" +version = "v0.40.0a80" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From 679d021461c2ab5679a152fd86ad76abc8cdefd9 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 4 Apr 2025 13:25:12 +0200 Subject: [PATCH 133/157] Improve refactoring of _update_rollover() --- plugwise_usb/nodes/helpers/pulses.py | 64 ++++++++++++++-------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 27dd8bcb6..c4cc0b7ee 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -335,11 +335,13 @@ def _update_rollover(self) -> None: return self._rollover_consumption = self._detect_rollover( + self._rollover_consumption, self._last_log_consumption_timestamp, self._next_log_consumption_timestamp, ) if self._log_production: self._rollover_production = self._detect_rollover( + self._rollover_production, self._last_log_production_timestamp, self._next_log_production_timestamp, False, @@ -347,46 +349,46 @@ def _update_rollover(self) -> None: def _detect_rollover( self, + rollover: bool, last_log_timestamp: datetime | None, next_log_timestamp: datetime | None, is_consumption=True, ) -> bool: """Helper function for _update_rollover().""" + if ( - self._pulses_timestamp is None - or last_log_timestamp is None - or next_log_timestamp is None + self._pulses_timestamp is not None + and last_log_timestamp is not None + and next_log_timestamp is not None ): - # Unable to determine rollover - return - - direction = "consumption" - if not is_consumption: - direction = "production" - - if self._pulses_timestamp > next_log_timestamp: - _LOGGER.debug( - "_update_rollover | %s | set %s rollover => pulses newer", - self._mac, - direction, - ) - return True + direction = "consumption" + if not is_consumption: + direction = "production" - if self._pulses_timestamp < last_log_timestamp: - _LOGGER.debug( - "_update_rollover | %s | set %s rollover => log newer", - self._mac, - direction, - ) - return True + if self._pulses_timestamp > next_log_timestamp: + _LOGGER.debug( + "_update_rollover | %s | set %s rollover => pulses newer", + self._mac, + direction, + ) + return True - if last_log_timestamp <= self._pulses_timestamp <= next_log_timestamp: - _LOGGER.debug( - "_update_rollover | %s | reset %s rollover", - self._mac, - direction - ) - return False + if self._pulses_timestamp < last_log_timestamp: + _LOGGER.debug( + "_update_rollover | %s | set %s rollover => log newer", + self._mac, + direction, + ) + return True + + if last_log_timestamp <= self._pulses_timestamp <= next_log_timestamp: + if rollover: + _LOGGER.debug( + "_update_rollover | %s | reset %s rollover", + self._mac, + direction + ) + return False def add_empty_log(self, address: int, slot: int) -> None: """Add empty energy log record to mark any start of beginning of energy log collection.""" From 9ca57735da6008e5278e017334c6a951f8003a23 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 6 Apr 2025 13:17:44 +0200 Subject: [PATCH 134/157] _update_log_references(): don't sync to pulse-counter reset The timestamps are set by the device --- plugwise_usb/nodes/helpers/pulses.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index c4cc0b7ee..b56592418 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -755,15 +755,6 @@ def _update_log_references(self, address: int, slot: int) -> None: log_timestamp = self._logs[address][slot].timestamp is_consumption = self._logs[address][slot].is_consumption - # Sync log_timestamp with the device pulsecounter reset-time - # This syncs the daily reset of energy counters with the corresponding device pulsecounter reset - if self._last_hourly_reset is not None: - log_timestamp = log_timestamp + timedelta( - minutes=self._last_hourly_reset.minute, - seconds=self._last_hourly_reset.second, - microseconds=self._last_hourly_reset.microsecond, - ) - # Update log references self._update_first_log_reference(address, slot, log_timestamp, is_consumption) self._update_last_log_reference(address, slot, log_timestamp, is_consumption) From 688a8adc101d68b2b42ac73dc673f46556e65932 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 6 Apr 2025 13:32:37 +0200 Subject: [PATCH 135/157] Correct rollover assert --- 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 13aa9eb72..035e7c1b0 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -1148,7 +1148,7 @@ def test_pulse_collection_consumption( ) == (145 + 2222 + 3333, pulse_update_5) pulse_update_6 = fixed_this_hour + td(hours=2, seconds=10) tst_consumption.update_pulse_counter(321, 0, pulse_update_6) - assert tst_consumption.log_rollover + assert not tst_consumption.log_rollover assert tst_consumption.collected_pulses( fixed_this_hour, is_consumption=True ) == (2222 + 3333 + 321, pulse_update_6) From 1958ed12c6b852f8a1572a10cf5575a9dae9aa99 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 6 Apr 2025 13:39:11 +0200 Subject: [PATCH 136/157] Bump to a81 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 0143fb721..9b2387c79 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a80" +version = "v0.40.0a81" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From 35d51e4669629349786e878c576fa72e07586971 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 6 Apr 2025 14:26:55 +0200 Subject: [PATCH 137/157] Add missing line --- plugwise_usb/nodes/helpers/pulses.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index b56592418..d64b9adc7 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -301,6 +301,7 @@ def update_pulse_counter( and self._pulses_production < pulses_produced ): self._prod_pulsecounter_reset = True + self._last_hourly_reset = timestamp _LOGGER.debug( "update_pulse_counter | production pulses reset | hourly_reset_time=%s", self._last_hourly_reset, From 53348ba33d93d083b74e003e616ab137a8427607 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 7 Apr 2025 08:15:22 +0200 Subject: [PATCH 138/157] counter-update: add debug-logging --- plugwise_usb/nodes/helpers/counter.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index 58b7d746c..9909ecccc 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -288,6 +288,12 @@ def update( if self._energy_id in ENERGY_DAY_COUNTERS: last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) # Postpone the last_reset time-changes at day-end until a device pulsecounter resets + _LOGGER.debug("energycounter_update | last reset hour=%s", last_reset.hour) + _LOGGER.debug( + "energycounter_update | pulse_counter_reset=%s, midnight_reset_passed=%s", + pulse_collection.pulse_counter_reset, + self._midnight_reset_passed, + ) if last_reset.hour == 0 and ( not pulse_collection.pulse_counter_reset and not self._midnight_reset_passed From 458a378eb3d8a7c5a2a539847c21a0e24c7a7f30 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 7 Apr 2025 08:16:12 +0200 Subject: [PATCH 139/157] Bump to a82 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9b2387c79..bc0e718a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a81" +version = "v0.40.0a82" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From f96de0dd6641885c0c2de75bb1b4c8eca1bd7f6b Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 7 Apr 2025 08:35:49 +0200 Subject: [PATCH 140/157] Improve counter update() --- plugwise_usb/nodes/helpers/counter.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index 9909ecccc..89dc17796 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -286,14 +286,14 @@ def update( if self._energy_id in ENERGY_HOUR_COUNTERS: last_reset = last_reset.replace(minute=0, second=0, microsecond=0) if self._energy_id in ENERGY_DAY_COUNTERS: - last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) - # Postpone the last_reset time-changes at day-end until a device pulsecounter resets + reset: bool = pulse_collection.pulse_counter_reset _LOGGER.debug("energycounter_update | last reset hour=%s", last_reset.hour) _LOGGER.debug( "energycounter_update | pulse_counter_reset=%s, midnight_reset_passed=%s", - pulse_collection.pulse_counter_reset, + reset, self._midnight_reset_passed, ) + # Postpone the last_reset time-changes at day-end until a device pulsecounter resets if last_reset.hour == 0 and ( not pulse_collection.pulse_counter_reset and not self._midnight_reset_passed @@ -302,6 +302,8 @@ def update( hour=0, minute=0, second=0, microsecond=0 ) self._midnight_reset_passed = True + else: + last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) pulses, last_update = pulse_collection.collected_pulses( last_reset, self._is_consumption From 5c8db1b728b83323f87243ca82e29e897e4c453d Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 7 Apr 2025 08:36:32 +0200 Subject: [PATCH 141/157] Bump to a83 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index bc0e718a6..cb2c3aa29 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a82" +version = "v0.40.0a83" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From 6e9a5df0f5305588cc393a4f4cc7a5770b80cff2 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 7 Apr 2025 11:44:48 +0200 Subject: [PATCH 142/157] Add missing @property --- plugwise_usb/nodes/helpers/pulses.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index d64b9adc7..f285c4def 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -165,6 +165,7 @@ def last_update(self) -> datetime | None: """Return timestamp of last update.""" return self._pulses_timestamp + @property def pulse_counter_reset(self) -> bool: """Return a pulse_counter reset.""" return self._cons_pulsecounter_reset or self._prod_pulsecounter_reset From d702ab267eb899e34df6ab8fe8388b37156beb27 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 7 Apr 2025 11:49:36 +0200 Subject: [PATCH 143/157] Bump to a84 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index cb2c3aa29..8ed7b8e08 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a83" +version = "v0.40.0a84" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From cf84c6dc5bce2fe8f4042beeca4d23b90e19b1e9 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 7 Apr 2025 19:52:45 +0200 Subject: [PATCH 144/157] Remove hourly-reset related, not used --- plugwise_usb/nodes/helpers/pulses.py | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index f285c4def..2f2bd2dc2 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -106,13 +106,6 @@ def collected_logs(self) -> int: counter += len(self._logs[address]) return counter - @property - def hourly_reset_time(self) -> datetime | None: - """Provide the device hourly pulse reset time.""" - if (timestamp := self._last_hourly_reset) is not None: - return timestamp - return None - @property def logs(self) -> dict[int, dict[int, PulseLogRecord]]: """Return currently collected pulse logs in reversed order.""" @@ -291,22 +284,15 @@ def update_pulse_counter( and self._pulses_consumption > pulses_consumed ): self._cons_pulsecounter_reset = True - self._last_hourly_reset = timestamp - _LOGGER.debug( - "update_pulse_counter | consumption pulses reset | hourly_reset_time=%s", - self._last_hourly_reset, - ) + _LOGGER.debug("update_pulse_counter | consumption pulses reset") if ( self._pulses_production is not None and self._pulses_production < pulses_produced ): self._prod_pulsecounter_reset = True - self._last_hourly_reset = timestamp - _LOGGER.debug( - "update_pulse_counter | production pulses reset | hourly_reset_time=%s", - self._last_hourly_reset, - ) + _LOGGER.debug("update_pulse_counter | production pulses reset") + # No rollover based on time, set rollover based on counter reset # Required for special cases like nodes which have been powered off for several days if not (self._rollover_consumption or self._rollover_production): From 88b008bf8f1c91acdd1c431a26a813a11dc810ad Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 7 Apr 2025 19:54:22 +0200 Subject: [PATCH 145/157] Remove related test-assert --- tests/test_usb.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_usb.py b/tests/test_usb.py index 035e7c1b0..d10ac122f 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -1121,7 +1121,6 @@ def test_pulse_collection_consumption( pulse_update_4 = fixed_this_hour + td(hours=1, minutes=1, seconds=3) tst_consumption.update_pulse_counter(45, 0, pulse_update_4) assert tst_consumption.log_rollover - assert tst_consumption.hourly_reset_time == pulse_update_4 test_timestamp = fixed_this_hour + td(hours=1, minutes=1, seconds=4) assert tst_consumption.collected_pulses( test_timestamp, is_consumption=True From d1ed2525ab753dfb8fe6b74dee7a3f3530f6b9c7 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 7 Apr 2025 20:02:28 +0200 Subject: [PATCH 146/157] Revert debug-logging related changes --- plugwise_usb/nodes/helpers/pulses.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 2f2bd2dc2..2f7665cc6 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -841,12 +841,11 @@ def _logs_missing(self, from_timestamp: datetime) -> list[int] | None: if self._logs[address][slot].timestamp <= from_timestamp: break - _LOGGER.debug( - "_logs_missing | %s | missing in range=%s", self._mac, missing - ) - # return missing logs in range first if len(missing) > 0: + _LOGGER.debug( + "_logs_missing | %s | missing in range=%s", self._mac, missing + ) return missing if first_address not in self._logs: @@ -880,11 +879,6 @@ def _logs_missing(self, from_timestamp: datetime) -> list[int] | None: calculated_timestamp = self._logs[first_address][ first_slot ].timestamp - timedelta(minutes=log_interval) - _LOGGER.debug( - "_logs_missing | %s | calculated timestamp=%s", - self._mac, - calculated_timestamp, - ) while from_timestamp < calculated_timestamp: if ( address == self._first_empty_log_address From 47abbfe19fc3035fe7c07d9c834de503e039e5fd Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 8 Apr 2025 08:09:45 +0200 Subject: [PATCH 147/157] Add extra guarding for setting _midnight_reset_passed to True --- plugwise_usb/nodes/helpers/counter.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index 89dc17796..77f943d4f 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -301,7 +301,8 @@ def update( last_reset = (last_reset - timedelta(days=1)).replace( hour=0, minute=0, second=0, microsecond=0 ) - self._midnight_reset_passed = True + if pulse_collection.pulse_counter_reset: + self._midnight_reset_passed = True else: last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) From be03a87a37103f1dd5b11e259c3d3f1b13e5ea28 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 8 Apr 2025 08:18:30 +0200 Subject: [PATCH 148/157] Bump to a85 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8ed7b8e08..63ef730d8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a84" +version = "v0.40.0a85" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From 179c9721998aa042c7f6d93444e6a3284033c50e Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 8 Apr 2025 20:35:53 +0200 Subject: [PATCH 149/157] Clean up --- plugwise_usb/nodes/helpers/pulses.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugwise_usb/nodes/helpers/pulses.py b/plugwise_usb/nodes/helpers/pulses.py index 2f7665cc6..81de2ca1a 100644 --- a/plugwise_usb/nodes/helpers/pulses.py +++ b/plugwise_usb/nodes/helpers/pulses.py @@ -67,7 +67,6 @@ def __init__(self, mac: str) -> None: self._last_empty_log_address: int | None = None self._last_empty_log_slot: int | None = None - self._last_hourly_reset: datetime | None = None self._last_log_consumption_timestamp: datetime | None = None self._last_log_consumption_address: int | None = None self._last_log_consumption_slot: int | None = None @@ -844,7 +843,7 @@ def _logs_missing(self, from_timestamp: datetime) -> list[int] | None: # return missing logs in range first if len(missing) > 0: _LOGGER.debug( - "_logs_missing | %s | missing in range=%s", self._mac, missing + "_logs_missing | %s | missing in range=%s", self._mac, missing ) return missing @@ -931,7 +930,7 @@ def _missing_addresses_before( if self._log_interval_consumption == 0: pass - if not self._log_production: #False + if not self._log_production: expected_timestamp = ( self._logs[address][slot].timestamp - calc_interval_cons ) @@ -989,7 +988,7 @@ def _missing_addresses_after( # Use consumption interval calc_interval_cons = timedelta(minutes=self._log_interval_consumption) - if not self._log_production: # False + if not self._log_production: expected_timestamp = ( self._logs[address][slot].timestamp + calc_interval_cons ) From ae8266e0070b18ef44e0335e5a282566f93b2a96 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 9 Apr 2025 08:13:07 +0200 Subject: [PATCH 150/157] Move setting _midnight_reset_passed --- plugwise_usb/nodes/helpers/counter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index 77f943d4f..dcc094e02 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -301,10 +301,10 @@ def update( last_reset = (last_reset - timedelta(days=1)).replace( hour=0, minute=0, second=0, microsecond=0 ) - if pulse_collection.pulse_counter_reset: - self._midnight_reset_passed = True else: last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) + if pulse_collection.pulse_counter_reset: + self._midnight_reset_passed = True pulses, last_update = pulse_collection.collected_pulses( last_reset, self._is_consumption From b50ba2b31d30b0462014b078bdcf9cc90676b3d9 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 9 Apr 2025 08:13:31 +0200 Subject: [PATCH 151/157] Bump to a86 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 63ef730d8..12235d405 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a85" +version = "v0.40.0a86" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From 612752603573de5d89419d29054d93b037f0ca52 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 10 Apr 2025 08:13:38 +0200 Subject: [PATCH 152/157] Improve midnight reset delaying code further --- plugwise_usb/nodes/helpers/counter.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index dcc094e02..cae4a1c4e 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -280,9 +280,6 @@ def update( ) -> tuple[float | None, datetime | None]: """Get pulse update.""" last_reset = datetime.now(tz=LOCAL_TIMEZONE) - if self._midnight_reset_passed and last_reset.hour == 1: - self._midnight_reset_passed = False - if self._energy_id in ENERGY_HOUR_COUNTERS: last_reset = last_reset.replace(minute=0, second=0, microsecond=0) if self._energy_id in ENERGY_DAY_COUNTERS: @@ -303,8 +300,10 @@ def update( ) else: last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) - if pulse_collection.pulse_counter_reset: + if last_reset.hour == 0 and pulse_collection.pulse_counter_reset: self._midnight_reset_passed = True + if last_reset.hour == 1 and self._midnight_reset_passed: + self._midnight_reset_passed = False pulses, last_update = pulse_collection.collected_pulses( last_reset, self._is_consumption From ca546caf93bc972ec4d917549d9153f5d0868ac5 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 10 Apr 2025 08:17:46 +0200 Subject: [PATCH 153/157] Bump to a87 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 12235d405..8280ece0e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a86" +version = "v0.40.0a87" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From f5a0a33e53a604bb1e6c05141b223f0b522d7239 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 11 Apr 2025 08:11:49 +0200 Subject: [PATCH 154/157] Test last_reset.hour before setting hour to 0 --- plugwise_usb/nodes/helpers/counter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index cae4a1c4e..02d8050fe 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -299,11 +299,11 @@ def update( hour=0, minute=0, second=0, microsecond=0 ) else: - last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) if last_reset.hour == 0 and pulse_collection.pulse_counter_reset: self._midnight_reset_passed = True if last_reset.hour == 1 and self._midnight_reset_passed: self._midnight_reset_passed = False + last_reset = last_reset.replace(hour=0, minute=0, second=0, microsecond=0) pulses, last_update = pulse_collection.collected_pulses( last_reset, self._is_consumption From 9b21241bb7f42b02630f95884b7475427d3d8740 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 11 Apr 2025 08:13:09 +0200 Subject: [PATCH 155/157] Bump to a88 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8280ece0e..43b965fcf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a87" +version = "v0.40.0a88" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable", From 2aa72063ebb16e2898a75d155adcd247cecf5fbd Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 12 Apr 2025 07:19:02 +0200 Subject: [PATCH 156/157] Remove debug-logging for testing --- plugwise_usb/nodes/helpers/counter.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/plugwise_usb/nodes/helpers/counter.py b/plugwise_usb/nodes/helpers/counter.py index 02d8050fe..a8d08a8ea 100644 --- a/plugwise_usb/nodes/helpers/counter.py +++ b/plugwise_usb/nodes/helpers/counter.py @@ -283,13 +283,6 @@ def update( if self._energy_id in ENERGY_HOUR_COUNTERS: last_reset = last_reset.replace(minute=0, second=0, microsecond=0) if self._energy_id in ENERGY_DAY_COUNTERS: - reset: bool = pulse_collection.pulse_counter_reset - _LOGGER.debug("energycounter_update | last reset hour=%s", last_reset.hour) - _LOGGER.debug( - "energycounter_update | pulse_counter_reset=%s, midnight_reset_passed=%s", - reset, - self._midnight_reset_passed, - ) # Postpone the last_reset time-changes at day-end until a device pulsecounter resets if last_reset.hour == 0 and ( not pulse_collection.pulse_counter_reset From 7836e01be3b25df94492d3d230fe9a311161ac93 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 12 Apr 2025 07:19:38 +0200 Subject: [PATCH 157/157] Bump to a89 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 43b965fcf..7467082cd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a88" +version = "v0.40.0a89" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ "Development Status :: 5 - Production/Stable",