Skip to content

Commit f89ed4a

Browse files
committed
expose logaddress and create set-function to update logaddress
1 parent 0698cce commit f89ed4a

File tree

5 files changed

+113
-23
lines changed

5 files changed

+113
-23
lines changed

plugwise_usb/api.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ class EnergyStatistics:
260260
hour_production_reset: datetime | None = None
261261
day_production: float | None = None
262262
day_production_reset: datetime | None = None
263+
current_logaddress: int | None = None
263264

264265

265266
@dataclass(frozen=True)

plugwise_usb/messages/requests.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,7 @@ def __init__(
738738
mac: bytes,
739739
dt: datetime,
740740
protocol_version: float,
741-
reset: bool = False,
741+
reset: int | bool = False,
742742
) -> None:
743743
"""Initialize CircleLogDataRequest message object."""
744744
super().__init__(send_fn, mac)
@@ -754,7 +754,14 @@ def __init__(
754754
this_date = DateTime(dt.year, dt.month, month_minutes)
755755
this_time = Time(dt.hour, dt.minute, dt.second)
756756
day_of_week = Int(dt.weekday(), 2)
757-
if reset:
757+
if isinstance(reset, int):
758+
self._args += [
759+
this_date,
760+
LogAddr(reset, 8, False),
761+
this_time,
762+
day_of_week,
763+
]
764+
elif reset:
758765
self._args += [
759766
this_date,
760767
LogAddr(0, 8, False),

plugwise_usb/nodes/circle.py

Lines changed: 90 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from ..constants import (
2828
DAY_IN_HOURS,
2929
DEFAULT_CONS_INTERVAL,
30+
LOGADDR_MAX,
3031
MAX_TIME_DRIFT,
3132
MINIMAL_POWER_UPDATE,
3233
NO_PRODUCTION_INTERVAL,
@@ -1030,26 +1031,11 @@ async def node_info_update(
10301031
node_info = await node_request.send()
10311032

10321033
if node_info is None:
1034+
_LOGGER.debug("No response for node_info_update() for %s", self.mac)
1035+
await self._available_update_state(False)
10331036
return None
10341037

10351038
await super().node_info_update(node_info)
1036-
await self._relay_update_state(
1037-
node_info.relay_state, timestamp=node_info.timestamp
1038-
)
1039-
if self._current_log_address is not None and (
1040-
self._current_log_address > node_info.current_logaddress_pointer
1041-
or self._current_log_address == 1
1042-
):
1043-
# Rollover of log address
1044-
_LOGGER.debug(
1045-
"Rollover log address from %s into %s for node %s",
1046-
self._current_log_address,
1047-
node_info.current_logaddress_pointer,
1048-
self._mac_in_str,
1049-
)
1050-
1051-
if self._current_log_address != node_info.current_logaddress_pointer:
1052-
self._current_log_address = node_info.current_logaddress_pointer
10531039

10541040
return self._node_info
10551041

@@ -1059,14 +1045,29 @@ async def update_node_details(
10591045
) -> bool:
10601046
"""Process new node info and return true if all fields are updated."""
10611047
if node_info.relay_state is not None:
1062-
self._relay_state = replace(
1063-
self._relay_state,
1064-
state=node_info.relay_state,
1065-
timestamp=node_info.timestamp,
1048+
await self._relay_update_state(
1049+
node_info.relay_state, timestamp=node_info.timestamp
1050+
)
1051+
1052+
if (
1053+
node_info.current_logaddress_pointer is not None
1054+
and self._current_log_address is not None
1055+
and (
1056+
self._current_log_address < node_info.current_logaddress_pointer
1057+
or self._current_log_address == 1
1058+
)
1059+
):
1060+
# Rollover of log address
1061+
_LOGGER.debug(
1062+
"Rollover log address from %s into %s for node %s",
1063+
self._current_log_address,
1064+
node_info.current_logaddress_pointer,
1065+
self._mac_in_str,
10661066
)
10671067

10681068
if node_info.current_logaddress_pointer is not None:
10691069
self._current_log_address = node_info.current_logaddress_pointer
1070+
self._energy_counters.set_current_logaddres(self._current_log_address)
10701071

10711072
return await super().update_node_details(node_info)
10721073

@@ -1363,3 +1364,71 @@ async def energy_reset_request(self) -> None:
13631364
"Node info update after energy-reset successful for %s",
13641365
self._mac_in_str,
13651366
)
1367+
1368+
async def energy_logaddr_setrequest(self, logaddr: int) -> None:
1369+
"""Set the logaddress to a specific value."""
1370+
if self._node_protocols is None:
1371+
raise NodeError("Unable to energy-reset when protocol version is unknown")
1372+
1373+
if logaddr < 1 or logaddr >= LOGADDR_MAX:
1374+
raise ValueError("Set logaddress out of range for {self._mac_in_str}")
1375+
request = CircleClockSetRequest(
1376+
self._send,
1377+
self._mac_in_bytes,
1378+
datetime.now(tz=UTC),
1379+
self._node_protocols.max,
1380+
logaddr,
1381+
)
1382+
if (response := await request.send()) is None:
1383+
raise NodeError(f"Logaddress set for {self._mac_in_str} failed")
1384+
1385+
if response.ack_id != NodeResponseType.CLOCK_ACCEPTED:
1386+
raise MessageError(
1387+
f"Unexpected NodeResponseType {response.ack_id!r} received as response to CircleClockSetRequest"
1388+
)
1389+
1390+
_LOGGER.warning("Logaddress set for Node %s successful", self._mac_in_str)
1391+
1392+
# Follow up by an energy-intervals (re)set
1393+
interval_request = CircleMeasureIntervalRequest(
1394+
self._send,
1395+
self._mac_in_bytes,
1396+
DEFAULT_CONS_INTERVAL,
1397+
NO_PRODUCTION_INTERVAL,
1398+
)
1399+
if (interval_response := await interval_request.send()) is None:
1400+
raise NodeError("No response for CircleMeasureIntervalRequest")
1401+
1402+
if (
1403+
interval_response.response_type
1404+
!= NodeResponseType.POWER_LOG_INTERVAL_ACCEPTED
1405+
):
1406+
raise MessageError(
1407+
f"Unknown NodeResponseType '{interval_response.response_type.name}' received"
1408+
)
1409+
_LOGGER.warning("Resetting energy intervals to default (= consumption only)")
1410+
1411+
# Clear the cached energy_collection
1412+
if self._cache_enabled:
1413+
self._set_cache(CACHE_ENERGY_COLLECTION, "")
1414+
_LOGGER.warning(
1415+
"Energy-collection cache cleared successfully, updating cache for %s",
1416+
self._mac_in_str,
1417+
)
1418+
await self.save_cache()
1419+
1420+
# Clear PulseCollection._logs
1421+
self._energy_counters.reset_pulse_collection()
1422+
_LOGGER.warning("Resetting pulse-collection")
1423+
1424+
# Request a NodeInfo update
1425+
if await self.node_info_update() is None:
1426+
_LOGGER.warning(
1427+
"Node info update failed after energy-reset for %s",
1428+
self._mac_in_str,
1429+
)
1430+
else:
1431+
_LOGGER.warning(
1432+
"Node info update after energy-reset successful for %s",
1433+
self._mac_in_str,
1434+
)

plugwise_usb/nodes/helpers/counter.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ def __init__(self, mac: str) -> None:
5757
self._mac = mac
5858
self._calibration: EnergyCalibration | None = None
5959
self._counters: dict[EnergyType, EnergyCounter] = {}
60+
self._current_logaddress: int | None = None
6061
for energy_type in ENERGY_COUNTERS:
6162
self._counters[energy_type] = EnergyCounter(energy_type, mac)
6263
self._pulse_collection = PulseCollection(mac)
@@ -67,6 +68,10 @@ def collected_logs(self) -> int:
6768
"""Total collected logs."""
6869
return self._pulse_collection.collected_logs
6970

71+
def set_current_logaddres(self, address: int) -> None:
72+
"""Update current logaddress value."""
73+
self._current_logaddress = address
74+
7075
def add_empty_log(self, address: int, slot: int) -> None:
7176
"""Add empty energy log record to mark any start of beginning of energy log collection."""
7277
self._pulse_collection.add_empty_log(address, slot)
@@ -127,6 +132,11 @@ def log_addresses_missing(self) -> list[int] | None:
127132
"""Return list of addresses of energy logs."""
128133
return self._pulse_collection.log_addresses_missing
129134

135+
@property
136+
def current_logaddress(self) -> int | None:
137+
"""Return current registered logaddress value."""
138+
return self._current_logaddress
139+
130140
@property
131141
def log_rollover(self) -> bool:
132142
"""Indicate if new log is required due to rollover."""
@@ -149,6 +159,7 @@ def update(self) -> None:
149159
self._pulse_collection.recalculate_missing_log_addresses()
150160
if self._calibration is None:
151161
return
162+
self._energy_statistics.current_logaddress = self._current_logaddress
152163
self._energy_statistics.log_interval_consumption = (
153164
self._pulse_collection.log_interval_consumption
154165
)

tests/test_usb.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -947,6 +947,7 @@ async def fake_get_missing_energy_logs(address: int) -> None:
947947
hour_production_reset=None,
948948
day_production=None,
949949
day_production_reset=None,
950+
current_logaddress=20,
950951
)
951952
# energy_update is not complete and should return none
952953
utc_now = dt.now(UTC)
@@ -964,6 +965,7 @@ async def fake_get_missing_energy_logs(address: int) -> None:
964965
hour_production_reset=None,
965966
day_production=None,
966967
day_production_reset=None,
968+
current_logaddress=20,
967969
)
968970
await stick.disconnect()
969971

0 commit comments

Comments
 (0)