Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## v0.42.0

- Implement resetting of energy logs

## v0.41.0

- Implement setting of energy logging intervals [#247](https://github.com/plugwise/python-plugwise-usb/pull/247)
Expand Down
23 changes: 21 additions & 2 deletions plugwise_usb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
from collections.abc import Callable, Coroutine
from functools import wraps
import logging
from typing import Any, TypeVar, cast, Final
from typing import Any, Final, TypeVar, cast

from .api import NodeEvent, PlugwiseNode, StickEvent
from .connection import StickController
from .constants import DEFAULT_CONS_INTERVAL, NO_PRODUCTION_INTERVAL
from .exceptions import MessageError, NodeError, StickError, SubscriptionError
from .network import StickNetwork

Expand Down Expand Up @@ -210,6 +211,24 @@
raise NodeError(f"Failed setting accept joining: {exc}") from exc
return True

async def energy_reset_request(self, mac: str) -> bool:
"""Send an energy-reset request to a Node."""
_LOGGER.debug("Resetting energy logs for %s", mac)
try:
await self._network.energy_reset_request(mac)
except (MessageError, NodeError) as exc:
raise NodeError(f"{exc}") from exc

Check warning on line 220 in plugwise_usb/__init__.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/__init__.py#L216-L220

Added lines #L216 - L220 were not covered by tests

# Follow up by an energy-intervals (re)set
if (

Check warning on line 223 in plugwise_usb/__init__.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/__init__.py#L223

Added line #L223 was not covered by tests
result := await self.set_energy_intervals(
mac, DEFAULT_CONS_INTERVAL, NO_PRODUCTION_INTERVAL
)
):
return result

Check warning on line 228 in plugwise_usb/__init__.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/__init__.py#L228

Added line #L228 was not covered by tests

return False

Check warning on line 230 in plugwise_usb/__init__.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/__init__.py#L230

Added line #L230 was not covered by tests

async def set_energy_intervals(
self, mac: str, cons_interval: int, prod_interval: int
) -> bool:
Expand Down Expand Up @@ -295,7 +314,7 @@
"Unable to connect. " +
"Path to USB-Stick is not defined, set port property first"
)

await self._controller.connect_to_stick(
self._port,
)
Expand Down
9 changes: 8 additions & 1 deletion plugwise_usb/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

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

Expand Down Expand Up @@ -99,3 +99,10 @@
8: ("Celsius",),
9: ("Stealth",),
}

# Energy logging intervals
DEFAULT_CONS_INTERVAL: Final[int] = 60
NO_PRODUCTION_INTERVAL: Final[int] = 0

# Energy Node types
ENERGY_NODE_TYPES: tuple[int] = (1, 2, 9)
3 changes: 1 addition & 2 deletions plugwise_usb/messages/requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from ..constants import (
DAY_IN_MINUTES,
HOUR_IN_MINUTES,
LOGADDR_OFFSET,
MAX_RETRIES,
MESSAGE_FOOTER,
MESSAGE_HEADER,
Expand Down Expand Up @@ -765,7 +764,7 @@ def __init__(
if reset:
self._args += [
this_date,
LogAddr(LOGADDR_OFFSET, 8, False),
LogAddr(0, 8, False),
this_time,
day_of_week,
]
Expand Down
42 changes: 38 additions & 4 deletions plugwise_usb/network/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,19 @@

from asyncio import Task, create_task, gather, sleep
from collections.abc import Callable, Coroutine
from datetime import datetime, timedelta
from datetime import UTC, datetime, timedelta
import logging
from typing import Any

from ..api import NodeEvent, NodeType, PlugwiseNode, StickEvent
from ..connection import StickController
from ..constants import UTF8
from ..constants import ENERGY_NODE_TYPES, UTF8
from ..exceptions import CacheError, MessageError, NodeError, StickError, StickTimeout
from ..helpers.util import validate_mac
from ..messages.requests import (
CirclePlusAllowJoiningRequest,
CircleClockSetRequest,
CircleMeasureIntervalRequest,
CirclePlusAllowJoiningRequest,
NodePingRequest,
)
from ..messages.responses import (
Expand Down Expand Up @@ -541,6 +543,25 @@
_LOGGER.debug("Sent AllowJoiningRequest to Circle+ with state=%s", state)
self.accept_join_request = state

async def energy_reset_request(self, mac: str) -> None:
"""Send an energy-reset to a Node."""
self._validate_energy_node(mac)
node_protocols = self._nodes[mac].node_protocols
request = CircleClockSetRequest(

Check warning on line 550 in plugwise_usb/network/__init__.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/network/__init__.py#L548-L550

Added lines #L548 - L550 were not covered by tests
self._controller.send,
bytes(mac, UTF8),
datetime.now(tz=UTC),
node_protocols.max,
True,
)
if (response := await request.send()) is None:
raise NodeError(f"Energy-reset for {mac} failed")

Check warning on line 558 in plugwise_usb/network/__init__.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/network/__init__.py#L557-L558

Added lines #L557 - L558 were not covered by tests

if response.ack_id != NodeResponseType.CLOCK_ACCEPTED:
raise MessageError(

Check warning on line 561 in plugwise_usb/network/__init__.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/network/__init__.py#L560-L561

Added lines #L560 - L561 were not covered by tests
f"Unexpected NodeResponseType {response.ack_id!r} received as response to CircleClockSetRequest"
)

async def set_energy_intervals(
self, mac: str, consumption: int, production: int
) -> None:
Expand All @@ -549,7 +570,7 @@
Default: consumption = 60, production = 0.
For logging energy in both directions set both to 60.
"""
# Validate input parameters
self._validate_energy_node(mac)

Check warning on line 573 in plugwise_usb/network/__init__.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/network/__init__.py#L573

Added line #L573 was not covered by tests
if consumption <= 0:
raise ValueError("Consumption interval must be positive")
if production < 0:
Expand All @@ -569,6 +590,19 @@
f"Unknown NodeResponseType '{response.response_type.name}' received"
)

def _validate_energy_node(self, mac: str) -> None:
"""Validate node for energy operations."""
if not validate_mac(mac):
raise NodeError(f"MAC '{mac}' invalid")

Check warning on line 596 in plugwise_usb/network/__init__.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/network/__init__.py#L595-L596

Added lines #L595 - L596 were not covered by tests

if mac not in self._nodes:
raise NodeError(f"Node {mac} not present in network")

Check warning on line 599 in plugwise_usb/network/__init__.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/network/__init__.py#L598-L599

Added lines #L598 - L599 were not covered by tests

if self._nodes[mac].node_info.node_type.value not in ENERGY_NODE_TYPES:
raise NodeError(

Check warning on line 602 in plugwise_usb/network/__init__.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/network/__init__.py#L601-L602

Added lines #L601 - L602 were not covered by tests
f"Energy operations not supported for {self._nodes[mac].node_info.node_type.name}"
)

def subscribe_to_node_events(
self,
node_event_callback: Callable[[NodeEvent, str], Coroutine[Any, Any, None]],
Expand Down
8 changes: 8 additions & 0 deletions plugwise_usb/nodes/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@
self._last_seen,
)

@property
def node_protocols(self) -> SupportedVersions | None:
"""Return the node_protocols for the Node."""
if self._node_protocols is None:
return None

Check warning on line 107 in plugwise_usb/nodes/node.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/nodes/node.py#L106-L107

Added lines #L106 - L107 were not covered by tests

return self._node_protocols

Check warning on line 109 in plugwise_usb/nodes/node.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/nodes/node.py#L109

Added line #L109 was not covered by tests

@property
@raise_not_loaded
def battery_config(self) -> BatteryConfig:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "plugwise_usb"
version = "v0.41.0"
version = "0.42.0"
license = "MIT"
keywords = ["home", "automation", "plugwise", "module", "usb"]
classifiers = [
Expand Down
Loading