Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
110 commits
Select commit Hold shift + click to select a range
90720f0
Handle negative energy-pulse values
bouwew Mar 3, 2025
081b4ca
Bump to a34
bouwew Mar 3, 2025
2738abd
Extend logging showing production pulses
bouwew Mar 5, 2025
27bd8ca
Change negative _pulses handling
bouwew Mar 10, 2025
1857bae
Add debug-message showing CircleEnergyLog data
bouwew Mar 10, 2025
3f4e8bc
Bump to a35
bouwew Mar 10, 2025
c9ad42a
Improve CircleEnergyLogsResponse logging
bouwew Mar 10, 2025
e9dea6a
Improve add_pulse_stats logging
bouwew Mar 10, 2025
0bd1bd7
Bump to a36
bouwew Mar 10, 2025
4883df8
Add MAC to CircleEnergy log-message
bouwew Mar 12, 2025
91f7d46
Add collected_pulses debug-logging
bouwew Mar 13, 2025
07ce535
Fix typo in logging
bouwew Mar 13, 2025
fa7efa4
Bump to a37
bouwew Mar 13, 2025
56d9149
Extend collected_pulses logging
bouwew Mar 14, 2025
e6be79b
Add add_log debug-logging, improve on previous logging-commit
bouwew Mar 14, 2025
001a95f
Bump to a38
bouwew Mar 14, 2025
1a20950
Disable _update_log_direction(), not needed
bouwew Mar 14, 2025
57ad893
Force _log_production to True
bouwew Mar 14, 2025
3b61971
Full test-output
bouwew Mar 14, 2025
204c641
Disable most production_logging test-asserts
bouwew Mar 14, 2025
bbbbdd4
Disable
bouwew Mar 14, 2025
90f207e
Disable more test-asserts
bouwew Mar 14, 2025
a004df6
Bump to a39
bouwew Mar 14, 2025
32a527e
Production pulses are negative
bouwew Mar 14, 2025
4cd9ecd
Bump to a40
bouwew Mar 14, 2025
57ed0c3
Add missing await for energy_log_update()
bouwew Mar 16, 2025
24c0136
Bump to a44
bouwew Mar 16, 2025
f5a7a56
2nd try fixing missing await task
bouwew Mar 16, 2025
4aa51f1
Bump to a45
bouwew Mar 16, 2025
4973852
Fix ident
bouwew Mar 16, 2025
117adcc
Back to normal test-output
bouwew Mar 16, 2025
65fcd47
Indicate PRODUCERS, use to set _log_production
bouwew Mar 17, 2025
61d71f4
Improvements, formatting
bouwew Mar 17, 2025
69dd717
Add extra guarding: ignore negative production-pulses when only consu…
bouwew Mar 17, 2025
7bfbbf5
Add extra rollover logging
bouwew Mar 17, 2025
d7f1497
Fix typo
bouwew Mar 17, 2025
f813388
Update relevant test-asserts
bouwew Mar 17, 2025
a9fa2c3
Bump to a47
bouwew Mar 17, 2025
8a8dcfd
Fix typos
bouwew Mar 17, 2025
ef65066
Update logging positions
bouwew Mar 17, 2025
1108aa4
Fix another mistake
bouwew Mar 17, 2025
54f684e
Bump to a48
bouwew Mar 17, 2025
8a1fac9
Add missing line in _reset_log_references()
bouwew Mar 17, 2025
4d6372f
Bump to a49
bouwew Mar 17, 2025
36004b3
Limit retrieving missing logs to energy_update()
bouwew Mar 17, 2025
8dd6c68
Bump to a50
bouwew Mar 17, 2025
290ec83
Revert
bouwew Mar 17, 2025
9e8b506
Bump to a51
bouwew Mar 17, 2025
fa3adb0
Formatting, remove wrong guarding(?)
bouwew Mar 18, 2025
fac1ea9
Don't update production_log_refs when not required
bouwew Mar 18, 2025
f2f5990
Disable week statistics, don't update production-statistics when no p…
bouwew Mar 18, 2025
5f18045
Formatting
bouwew Mar 18, 2025
18a2db9
Fix typo
bouwew Mar 18, 2025
a76e5dd
Bump to a52
bouwew Mar 18, 2025
6502f1e
Hide not used import
bouwew Mar 18, 2025
71b36c6
Use production_logging property
bouwew Mar 18, 2025
693478b
Don't update production rollover when no production
bouwew Mar 18, 2025
821b6db
Formatting
bouwew Mar 18, 2025
a4e01d7
_log_production is never None, remove incorrect _update_log_direction…
bouwew Mar 18, 2025
251574e
Simplify code based on no separate slots for consumption-production
bouwew Mar 18, 2025
382b49f
Combine last-first-next_log inits
bouwew Mar 18, 2025
a764a1c
Update _update_log_interval()
bouwew Mar 18, 2025
c8bfd57
Remove _update_last_cons/prod_log_reference()
bouwew Mar 18, 2025
bb221fe
Update _reset_log_references()
bouwew Mar 18, 2025
907e0d5
Remove _update_first_cons/prod_log_reference()
bouwew Mar 18, 2025
0ca8f7f
Clean up removed function references
bouwew Mar 18, 2025
3e52307
Use a single log_interval
bouwew Mar 18, 2025
f7840ca
Add notes for rollovers
bouwew Mar 18, 2025
d648c64
Update counter.py for changes in pulses.py
bouwew Mar 18, 2025
1b53978
Clean-up
bouwew Mar 18, 2025
5543ba8
Fix typo
bouwew Mar 18, 2025
9d9c28b
Fixes for various pylint issues
bouwew Mar 18, 2025
da5b6fd
Test: update relevant parameters
bouwew Mar 18, 2025
81e60a3
Update api.py - log_interval
bouwew Mar 18, 2025
3daf616
Make sure to reset rollovers
bouwew Mar 18, 2025
101b1df
Move rollover assert to after collected_pulses()
bouwew Mar 18, 2025
d402c3f
Revert some changes
bouwew Mar 18, 2025
cacfbe3
Try
bouwew Mar 18, 2025
cb0dafd
Bring _update_rollover() back?
bouwew Mar 19, 2025
8404617
Add _cons/_prod_counter_reset vars, update/add comments, enable code
bouwew Mar 19, 2025
ff2add5
Remove old copy of update_pulse_counter()
bouwew Mar 19, 2025
d2f6471
Improve logic
bouwew Mar 19, 2025
c7b45e4
Finish up _update_rollover()
bouwew Mar 19, 2025
7b15e36
Fix ident
bouwew Mar 19, 2025
9c4fefb
Fixes
bouwew Mar 19, 2025
5ff20fc
Re-add _updatye_rollover() in update_pulse_counter()
bouwew Mar 20, 2025
d06e6e3
Add missed line
bouwew Mar 20, 2025
017920c
Re-add cons/prod_counter_reset reinits
bouwew Mar 20, 2025
88acd6a
Revert back to async-test-order
bouwew Mar 21, 2025
75f219e
Revert back to old logic
bouwew Mar 21, 2025
37dfe48
Cleanup test
bouwew Mar 21, 2025
b988fe6
Try test
bouwew Mar 21, 2025
345431e
Add 0098765432101234 to PRODUCERS
bouwew Mar 21, 2025
ff24908
Fix production asserts
bouwew Mar 21, 2025
6d80a00
Try
bouwew Mar 21, 2025
165c428
Disable missing
bouwew Mar 21, 2025
32cd079
Disable more
bouwew Mar 21, 2025
7b6fcf8
Disable test_log_address_rollover()
bouwew Mar 21, 2025
b30a5e9
Enable first production-asserts
bouwew Mar 21, 2025
213fbd3
Don't use double timeslots
bouwew Mar 21, 2025
763dd55
Enable disabled production asserts
bouwew Mar 21, 2025
e2d3b4e
Fixes
bouwew Mar 21, 2025
6852081
Improve test_log_address_rollover
bouwew Mar 21, 2025
e3ddeb6
Output production-statistics as positive values
bouwew Mar 21, 2025
5edf15b
And adapt related test-asserts
bouwew Mar 21, 2025
891f2aa
Correct
bouwew Mar 21, 2025
6bc5142
Remove test-debugging
bouwew Mar 21, 2025
b711a58
Bump to a53
bouwew Mar 21, 2025
524db1c
Lower MAX_LOG_HOURS
bouwew Mar 22, 2025
3bf7581
Bump to a54
bouwew Mar 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions plugwise_usb/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,7 @@ class MotionConfig:
class EnergyStatistics:
"""Energy statistics collection."""

log_interval_consumption: int | None = None
log_interval_production: int | None = None
log_interval: int | None = None
hour_consumption: float | None = None
hour_consumption_reset: datetime | None = None
day_consumption: float | None = None
Expand Down
22 changes: 18 additions & 4 deletions plugwise_usb/nodes/circle.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -326,6 +326,7 @@
self.name,
)
return None

# request node info update every 30 minutes.
elif not self.skip_update(self._node_info, 1800):
if await self.node_info_update() is None:
Expand Down Expand Up @@ -419,6 +420,7 @@
"Skip creating task to update energy logs for node %s",
self._mac_in_str,
)

if (
self._initialization_delay_expired is not None
and datetime.now(tz=UTC) < self._initialization_delay_expired
Expand All @@ -432,6 +434,7 @@
"Unable to return energy statistics for %s, collecting required data...",
self.name,
)

return None

async def get_missing_energy_logs(self) -> None:
Expand All @@ -440,6 +443,7 @@
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.",
Expand All @@ -453,12 +457,13 @@
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)

Check warning on line 460 in plugwise_usb/nodes/circle.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/nodes/circle.py#L460

Added line #L460 was not covered by tests

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 (
Expand Down Expand Up @@ -496,6 +501,7 @@
)
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

Expand All @@ -504,7 +510,12 @@
# 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(
"In 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(
Expand Down Expand Up @@ -561,14 +572,17 @@
# Create task to retrieve remaining (missing) logs
if self._energy_counters.log_addresses_missing is None:
return False

if len(self._energy_counters.log_addresses_missing) > 0:
if self._retrieve_energy_logs_task is not None:
if not self._retrieve_energy_logs_task.done():
await self._retrieve_energy_logs_task

self._retrieve_energy_logs_task = create_task(
self.get_missing_energy_logs()
)
return False

return True

async def _energy_log_records_save_to_cache(self) -> None:
Expand Down
102 changes: 55 additions & 47 deletions plugwise_usb/nodes/helpers/counter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from __future__ import annotations

from datetime import datetime, timedelta
from datetime import datetime #, timedelta
from enum import Enum, auto
import logging
from typing import Final
Expand Down Expand Up @@ -30,8 +30,8 @@
EnergyType.PRODUCTION_HOUR,
EnergyType.CONSUMPTION_DAY,
EnergyType.PRODUCTION_DAY,
EnergyType.CONSUMPTION_WEEK,
EnergyType.PRODUCTION_WEEK,
# EnergyType.CONSUMPTION_WEEK,
# EnergyType.PRODUCTION_WEEK,
)
ENERGY_HOUR_COUNTERS: Final = (
EnergyType.CONSUMPTION_HOUR,
Expand All @@ -49,12 +49,12 @@
ENERGY_CONSUMPTION_COUNTERS: Final = (
EnergyType.CONSUMPTION_HOUR,
EnergyType.CONSUMPTION_DAY,
EnergyType.CONSUMPTION_WEEK,
# EnergyType.CONSUMPTION_WEEK,
)
ENERGY_PRODUCTION_COUNTERS: Final = (
EnergyType.PRODUCTION_HOUR,
EnergyType.PRODUCTION_DAY,
EnergyType.PRODUCTION_WEEK,
# EnergyType.PRODUCTION_WEEK,
)

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -105,11 +105,8 @@
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
)
Expand All @@ -123,12 +120,12 @@
@property
def consumption_interval(self) -> int | None:
"""Measurement interval for energy consumption."""
return self._pulse_collection.log_interval_consumption
return self._pulse_collection.log_interval

Check warning on line 123 in plugwise_usb/nodes/helpers/counter.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/nodes/helpers/counter.py#L123

Added line #L123 was not covered by tests

@property
def production_interval(self) -> int | None:
"""Measurement interval for energy production."""
return self._pulse_collection.log_interval_production
return self._pulse_collection.log_interval

Check warning on line 128 in plugwise_usb/nodes/helpers/counter.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/nodes/helpers/counter.py#L128

Added line #L128 was not covered by tests

@property
def log_addresses_missing(self) -> list[int] | None:
Expand Down Expand Up @@ -157,11 +154,9 @@
self._pulse_collection.recalculate_missing_log_addresses()
if self._calibration is None:
return
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.log_interval = (
self._pulse_collection.log_interval
)
(
self._energy_statistics.hour_consumption,
Expand All @@ -171,23 +166,24 @@
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)
# (
# self._energy_statistics.week_consumption,
# 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.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:
Expand All @@ -213,18 +209,21 @@
self._mac = mac
if energy_id not in ENERGY_COUNTERS:
raise EnergyError(f"Invalid energy id '{energy_id}' for Energy counter")

self._calibration: EnergyCalibration | None = None
self._duration = "hour"
if energy_id in ENERGY_DAY_COUNTERS:
self._duration = "day"
elif energy_id in ENERGY_WEEK_COUNTERS:
self._duration = "week"
#elif energy_id in ENERGY_WEEK_COUNTERS:
# self._duration = "week"

self._energy_id: EnergyType = energy_id
self._is_consumption = True
self._direction = "consumption"
if self._energy_id in ENERGY_PRODUCTION_COUNTERS:
self._direction = "production"
self._is_consumption = False

self._last_reset: datetime | None = None
self._last_update: datetime | None = None
self._pulses: int | None = None
Expand Down Expand Up @@ -259,9 +258,16 @@
"""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
pulses_per_s = self._pulses / float(HOUR_IN_SECONDS)

# Handle both positive and negative pulses values
negative = False
if self._pulses < 0:
negative = True

Check warning on line 268 in plugwise_usb/nodes/helpers/counter.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/nodes/helpers/counter.py#L268

Added line #L268 was not covered by tests

pulses_per_s = abs(self._pulses) / float(HOUR_IN_SECONDS)
corrected_pulses = HOUR_IN_SECONDS * (
(
(
Expand All @@ -276,8 +282,9 @@
+ 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

Check warning on line 286 in plugwise_usb/nodes/helpers/counter.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/nodes/helpers/counter.py#L286

Added line #L286 was not covered by tests

return calc_value

@property
Expand All @@ -299,14 +306,14 @@
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,
)
# 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
Expand All @@ -319,10 +326,11 @@
)
if pulses is None or last_update is None:
return (None, None)

self._last_update = last_update
self._last_reset = last_reset
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)
Loading
Loading