Skip to content

Commit f7276eb

Browse files
authored
Merge pull request #252 from plugwise/energy-reset
Implement resetting of energylogs
2 parents 4983462 + 0202797 commit f7276eb

File tree

7 files changed

+81
-10
lines changed

7 files changed

+81
-10
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## v0.42.0
4+
5+
- Implement resetting of energy logs
6+
37
## v0.41.0
48

59
- Implement setting of energy logging intervals [#247](https://github.com/plugwise/python-plugwise-usb/pull/247)

plugwise_usb/__init__.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@
1010
from collections.abc import Callable, Coroutine
1111
from functools import wraps
1212
import logging
13-
from typing import Any, TypeVar, cast, Final
13+
from typing import Any, Final, TypeVar, cast
1414

1515
from .api import NodeEvent, PlugwiseNode, StickEvent
1616
from .connection import StickController
17+
from .constants import DEFAULT_CONS_INTERVAL, NO_PRODUCTION_INTERVAL
1718
from .exceptions import MessageError, NodeError, StickError, SubscriptionError
1819
from .network import StickNetwork
1920

@@ -210,6 +211,24 @@ async def set_accept_join_request(self, state: bool) -> bool:
210211
raise NodeError(f"Failed setting accept joining: {exc}") from exc
211212
return True
212213

214+
async def energy_reset_request(self, mac: str) -> bool:
215+
"""Send an energy-reset request to a Node."""
216+
_LOGGER.debug("Resetting energy logs for %s", mac)
217+
try:
218+
await self._network.energy_reset_request(mac)
219+
except (MessageError, NodeError) as exc:
220+
raise NodeError(f"{exc}") from exc
221+
222+
# Follow up by an energy-intervals (re)set
223+
if (
224+
result := await self.set_energy_intervals(
225+
mac, DEFAULT_CONS_INTERVAL, NO_PRODUCTION_INTERVAL
226+
)
227+
):
228+
return result
229+
230+
return False
231+
213232
async def set_energy_intervals(
214233
self, mac: str, cons_interval: int, prod_interval: int
215234
) -> bool:
@@ -295,7 +314,7 @@ async def connect(self, port: str | None = None) -> None:
295314
"Unable to connect. " +
296315
"Path to USB-Stick is not defined, set port property first"
297316
)
298-
317+
299318
await self._controller.connect_to_stick(
300319
self._port,
301320
)

plugwise_usb/constants.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

3232
# Max timeout in seconds
3333
# Stick responds with timeout messages within 10s.
34-
STICK_TIME_OUT: Final = 11
34+
STICK_TIME_OUT: Final = 11
3535
# In bigger networks a response from a Node could take up a while, so lets use 15 seconds.
3636
NODE_TIME_OUT: Final = 15
3737

@@ -99,3 +99,10 @@
9999
8: ("Celsius",),
100100
9: ("Stealth",),
101101
}
102+
103+
# Energy logging intervals
104+
DEFAULT_CONS_INTERVAL: Final[int] = 60
105+
NO_PRODUCTION_INTERVAL: Final[int] = 0
106+
107+
# Energy Node types
108+
ENERGY_NODE_TYPES: tuple[int] = (1, 2, 9)

plugwise_usb/messages/requests.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from ..constants import (
1313
DAY_IN_MINUTES,
1414
HOUR_IN_MINUTES,
15-
LOGADDR_OFFSET,
1615
MAX_RETRIES,
1716
MESSAGE_FOOTER,
1817
MESSAGE_HEADER,
@@ -765,7 +764,7 @@ def __init__(
765764
if reset:
766765
self._args += [
767766
this_date,
768-
LogAddr(LOGADDR_OFFSET, 8, False),
767+
LogAddr(0, 8, False),
769768
this_time,
770769
day_of_week,
771770
]

plugwise_usb/network/__init__.py

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,19 @@
66

77
from asyncio import Task, create_task, gather, sleep
88
from collections.abc import Callable, Coroutine
9-
from datetime import datetime, timedelta
9+
from datetime import UTC, datetime, timedelta
1010
import logging
1111
from typing import Any
1212

1313
from ..api import NodeEvent, NodeType, PlugwiseNode, StickEvent
1414
from ..connection import StickController
15-
from ..constants import UTF8
15+
from ..constants import ENERGY_NODE_TYPES, UTF8
1616
from ..exceptions import CacheError, MessageError, NodeError, StickError, StickTimeout
17+
from ..helpers.util import validate_mac
1718
from ..messages.requests import (
18-
CirclePlusAllowJoiningRequest,
19+
CircleClockSetRequest,
1920
CircleMeasureIntervalRequest,
21+
CirclePlusAllowJoiningRequest,
2022
NodePingRequest,
2123
)
2224
from ..messages.responses import (
@@ -541,6 +543,25 @@ async def allow_join_requests(self, state: bool) -> None:
541543
_LOGGER.debug("Sent AllowJoiningRequest to Circle+ with state=%s", state)
542544
self.accept_join_request = state
543545

546+
async def energy_reset_request(self, mac: str) -> None:
547+
"""Send an energy-reset to a Node."""
548+
self._validate_energy_node(mac)
549+
node_protocols = self._nodes[mac].node_protocols
550+
request = CircleClockSetRequest(
551+
self._controller.send,
552+
bytes(mac, UTF8),
553+
datetime.now(tz=UTC),
554+
node_protocols.max,
555+
True,
556+
)
557+
if (response := await request.send()) is None:
558+
raise NodeError(f"Energy-reset for {mac} failed")
559+
560+
if response.ack_id != NodeResponseType.CLOCK_ACCEPTED:
561+
raise MessageError(
562+
f"Unexpected NodeResponseType {response.ack_id!r} received as response to CircleClockSetRequest"
563+
)
564+
544565
async def set_energy_intervals(
545566
self, mac: str, consumption: int, production: int
546567
) -> None:
@@ -549,7 +570,7 @@ async def set_energy_intervals(
549570
Default: consumption = 60, production = 0.
550571
For logging energy in both directions set both to 60.
551572
"""
552-
# Validate input parameters
573+
self._validate_energy_node(mac)
553574
if consumption <= 0:
554575
raise ValueError("Consumption interval must be positive")
555576
if production < 0:
@@ -569,6 +590,19 @@ async def set_energy_intervals(
569590
f"Unknown NodeResponseType '{response.response_type.name}' received"
570591
)
571592

593+
def _validate_energy_node(self, mac: str) -> None:
594+
"""Validate node for energy operations."""
595+
if not validate_mac(mac):
596+
raise NodeError(f"MAC '{mac}' invalid")
597+
598+
if mac not in self._nodes:
599+
raise NodeError(f"Node {mac} not present in network")
600+
601+
if self._nodes[mac].node_info.node_type.value not in ENERGY_NODE_TYPES:
602+
raise NodeError(
603+
f"Energy operations not supported for {self._nodes[mac].node_info.node_type.name}"
604+
)
605+
572606
def subscribe_to_node_events(
573607
self,
574608
node_event_callback: Callable[[NodeEvent, str], Coroutine[Any, Any, None]],

plugwise_usb/nodes/node.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,14 @@ def available_state(self) -> AvailableState:
100100
self._last_seen,
101101
)
102102

103+
@property
104+
def node_protocols(self) -> SupportedVersions | None:
105+
"""Return the node_protocols for the Node."""
106+
if self._node_protocols is None:
107+
return None
108+
109+
return self._node_protocols
110+
103111
@property
104112
@raise_not_loaded
105113
def battery_config(self) -> BatteryConfig:

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "plugwise_usb"
7-
version = "v0.41.0"
7+
version = "0.42.0"
88
license = "MIT"
99
keywords = ["home", "automation", "plugwise", "module", "usb"]
1010
classifiers = [

0 commit comments

Comments
 (0)