Skip to content

Commit 1c9cff9

Browse files
author
Frank
committed
Apply ruff and cleanup code
1 parent 196b47b commit 1c9cff9

29 files changed

+2904
-1840
lines changed

plugwise_usb/__init__.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@
77
from __future__ import annotations
88

99
from asyncio import get_running_loop
10-
from collections.abc import Awaitable, Callable
10+
from collections.abc import Callable, Coroutine
1111
from functools import wraps
1212
import logging
1313
from typing import Any, TypeVar, cast
1414

1515
from .api import NodeEvent, StickEvent
1616
from .connection import StickController
17-
from .exceptions import StickError
17+
from .exceptions import StickError, SubscriptionError
1818
from .network import StickNetwork
1919
from .nodes import PlugwiseNode
2020

@@ -198,7 +198,7 @@ async def clear_cache(self) -> None:
198198

199199
def subscribe_to_stick_events(
200200
self,
201-
stick_event_callback: Callable[[StickEvent], Awaitable[None]],
201+
stick_event_callback: Callable[[StickEvent], Coroutine[Any, Any, None]],
202202
events: tuple[StickEvent],
203203
) -> Callable[[], None]:
204204
"""Subscribe callback when specified StickEvent occurs.
@@ -213,13 +213,15 @@ def subscribe_to_stick_events(
213213
@raise_not_initialized
214214
def subscribe_to_node_events(
215215
self,
216-
node_event_callback: Callable[[NodeEvent, str], Awaitable[None]],
217-
events: tuple[NodeEvent],
216+
node_event_callback: Callable[[NodeEvent, str], Coroutine[Any, Any, None]],
217+
events: tuple[NodeEvent, ...],
218218
) -> Callable[[], None]:
219219
"""Subscribe callback to be called when specific NodeEvent occurs.
220220
221221
Returns the function to be called to unsubscribe later.
222222
"""
223+
if self._network is None:
224+
raise SubscriptionError("Unable to subscribe to node events without network connection initialized")
223225
return self._network.subscribe_to_node_events(
224226
node_event_callback,
225227
events,

plugwise_usb/api.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ class StickEvent(Enum):
1515
NETWORK_ONLINE = auto()
1616

1717

18+
class MotionSensitivity(Enum):
19+
"""Motion sensitivity levels for Scan devices."""
20+
21+
HIGH = auto()
22+
MEDIUM = auto()
23+
OFF = auto()
24+
25+
1826
class NodeEvent(Enum):
1927
"""Plugwise Node events for callback subscription."""
2028

@@ -46,6 +54,7 @@ class NodeFeature(str, Enum):
4654
"""USB Stick Node feature."""
4755

4856
AVAILABLE = "available"
57+
BATTERY = "battery"
4958
ENERGY = "energy"
5059
HUMIDITY = "humidity"
5160
INFO = "info"
@@ -66,6 +75,28 @@ class NodeFeature(str, Enum):
6675
)
6776

6877

78+
@dataclass
79+
class BatteryConfig:
80+
"""Battery related configuration settings."""
81+
82+
# Duration in minutes the node synchronize its clock
83+
clock_interval: int | None = None
84+
85+
# Enable/disable clock sync
86+
clock_sync: bool | None = None
87+
88+
# Minimal interval in minutes the node will wake up
89+
# and able to receive (maintenance) commands
90+
maintenance_interval: int | None = None
91+
92+
# Duration in seconds the SED will be awake for receiving commands
93+
stay_active: int | None = None
94+
95+
# Duration in minutes the SED will be in sleeping mode
96+
# and not able to respond any command
97+
sleep_for: int | None = None
98+
99+
69100
@dataclass
70101
class NodeInfo:
71102
"""Node hardware information."""
@@ -116,6 +147,8 @@ class MotionState:
116147

117148
motion: bool | None = None
118149
timestamp: datetime | None = None
150+
reset_timer: int | None = None
151+
daylight_mode: bool | None = None
119152

120153

121154
@dataclass

plugwise_usb/connection/__init__.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
from __future__ import annotations
44

5-
from collections.abc import Awaitable, Callable
6-
from concurrent import futures
5+
from collections.abc import Awaitable, Callable, Coroutine
76
import logging
7+
from typing import Any
88

99
from ..api import StickEvent
1010
from ..exceptions import NodeError, StickError
@@ -26,7 +26,6 @@ def __init__(self) -> None:
2626
self._unsubscribe_stick_event: Callable[[], None] | None = None
2727

2828
self._init_sequence_id: bytes | None = None
29-
self._init_future: futures.Future | None = None
3029

3130
self._is_initialized = False
3231
self._mac_stick: str | None = None
@@ -115,7 +114,7 @@ async def connect_to_stick(self, serial_path: str) -> None:
115114
def subscribe_to_stick_events(
116115
self,
117116
stick_event_callback: Callable[[StickEvent], Awaitable[None]],
118-
events: tuple[StickEvent],
117+
events: tuple[StickEvent, ...],
119118
) -> Callable[[], None]:
120119
"""Subscribe callback when specified StickEvent occurs.
121120
@@ -130,7 +129,7 @@ def subscribe_to_stick_events(
130129

131130
def subscribe_to_node_responses(
132131
self,
133-
node_response_callback: Callable[[PlugwiseResponse], Awaitable[None]],
132+
node_response_callback: Callable[[PlugwiseResponse], Coroutine[Any, Any, bool]],
134133
mac: bytes | None = None,
135134
message_ids: tuple[bytes] | None = None,
136135
) -> Callable[[], None]:
@@ -165,15 +164,20 @@ async def initialize_stick(self) -> None:
165164
raise StickError("Cannot initialize, queue manager not running")
166165

167166
try:
168-
init_response: StickInitResponse = await self._queue.submit(
169-
StickInitRequest()
170-
)
167+
request = StickInitRequest(self.send)
168+
init_response: StickInitResponse | None = await request.send()
171169
except StickError as err:
172170
raise StickError(
173171
"No response from USB-Stick to initialization request." +
174172
" Validate USB-stick is connected to port " +
175173
f"' {self._manager.serial_path}'"
176174
) from err
175+
if init_response is None:
176+
raise StickError(
177+
"No response from USB-Stick to initialization request." +
178+
" Validate USB-stick is connected to port " +
179+
f"' {self._manager.serial_path}'"
180+
)
177181
self._mac_stick = init_response.mac_decoded
178182
self._network_online = init_response.network_online
179183

plugwise_usb/connection/manager.py

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from __future__ import annotations
44

55
from asyncio import Future, gather, get_event_loop, wait_for
6-
from collections.abc import Awaitable, Callable
6+
from collections.abc import Awaitable, Callable, Coroutine
77
import logging
88
from typing import Any
99

@@ -32,18 +32,26 @@ def __init__(self) -> None:
3232
self._connected: bool = False
3333
self._stick_event_subscribers: dict[
3434
Callable[[], None],
35-
tuple[Callable[[StickEvent], Awaitable[None]], StickEvent | None]
35+
tuple[Callable[[StickEvent], Awaitable[None]], tuple[StickEvent, ...]],
3636
] = {}
3737
self._unsubscribe_stick_events: Callable[[], None] | None = None
3838

3939
@property
4040
def reduce_receive_logging(self) -> bool:
4141
"""Return if logging must reduced."""
42+
if self._receiver is None:
43+
raise StickError(
44+
"Unable to return log settings when connection is not active."
45+
)
4246
return self._receiver.reduce_logging
4347

4448
@reduce_receive_logging.setter
4549
def reduce_receive_logging(self, state: bool) -> None:
4650
"""Reduce logging of unhandled received messages."""
51+
if self._receiver is None:
52+
raise StickError(
53+
"Unable to set log settings when connection is not active."
54+
)
4755
self._receiver.reduce_logging = state
4856

4957
@property
@@ -62,14 +70,12 @@ def is_connected(self) -> bool:
6270

6371
def _subscribe_to_stick_events(self) -> None:
6472
"""Subscribe to handle stick events by manager."""
65-
if not self.is_connected:
73+
if not self.is_connected or self._receiver is None:
6674
raise StickError("Unable to subscribe to events")
6775
if self._unsubscribe_stick_events is None:
68-
self._unsubscribe_stick_events = (
69-
self._receiver.subscribe_to_stick_events(
70-
self._handle_stick_event,
71-
(StickEvent.CONNECTED, StickEvent.DISCONNECTED)
72-
)
76+
self._unsubscribe_stick_events = self._receiver.subscribe_to_stick_events(
77+
self._handle_stick_event,
78+
(StickEvent.CONNECTED, StickEvent.DISCONNECTED),
7379
)
7480

7581
async def _handle_stick_event(
@@ -79,47 +85,47 @@ async def _handle_stick_event(
7985
"""Call callback for stick event subscribers."""
8086
if len(self._stick_event_subscribers) == 0:
8187
return
82-
callback_list: list[Callable] = []
83-
for callback, filtered_events in list(
84-
self._stick_event_subscribers.values()
85-
):
86-
if event in filtered_events:
88+
callback_list: list[Awaitable[None]] = []
89+
for callback, stick_events in self._stick_event_subscribers.values():
90+
if event in stick_events:
8791
callback_list.append(callback(event))
8892
if len(callback_list) > 0:
8993
await gather(*callback_list)
9094

9195
def subscribe_to_stick_events(
9296
self,
9397
stick_event_callback: Callable[[StickEvent], Awaitable[None]],
94-
events: tuple[StickEvent],
98+
events: tuple[StickEvent, ...],
9599
) -> Callable[[], None]:
96100
"""Subscribe callback when specified StickEvent occurs.
97101
98102
Returns the function to be called to unsubscribe later.
99103
"""
104+
100105
def remove_subscription() -> None:
101106
"""Remove stick event subscription."""
102107
self._stick_event_subscribers.pop(remove_subscription)
103-
self._stick_event_subscribers[remove_subscription] = (stick_event_callback, events)
108+
109+
self._stick_event_subscribers[remove_subscription] = (
110+
stick_event_callback,
111+
events,
112+
)
104113
return remove_subscription
105114

106115
def subscribe_to_stick_replies(
107116
self,
108-
callback: Callable[
109-
[StickResponse], Awaitable[None]
110-
],
117+
callback: Callable[[StickResponse], Coroutine[Any, Any, None]],
111118
) -> Callable[[], None]:
112119
"""Subscribe to response messages from stick."""
113120
if self._receiver is None or not self._receiver.is_connected:
114121
raise StickError(
115-
"Unable to subscribe to stick response when receiver " +
116-
"is not loaded"
122+
"Unable to subscribe to stick response when receiver " + "is not loaded"
117123
)
118124
return self._receiver.subscribe_to_stick_responses(callback)
119125

120126
def subscribe_to_node_responses(
121127
self,
122-
node_response_callback: Callable[[PlugwiseResponse], Awaitable[None]],
128+
node_response_callback: Callable[[PlugwiseResponse], Coroutine[Any, Any, bool]],
123129
mac: bytes | None = None,
124130
message_ids: tuple[bytes] | None = None,
125131
) -> Callable[[], None]:
@@ -129,21 +135,18 @@ def subscribe_to_node_responses(
129135
"""
130136
if self._receiver is None or not self._receiver.is_connected:
131137
raise StickError(
132-
"Unable to subscribe to node response when receiver " +
133-
"is not loaded"
138+
"Unable to subscribe to node response when receiver " + "is not loaded"
134139
)
135140
return self._receiver.subscribe_to_node_responses(
136141
node_response_callback, mac, message_ids
137142
)
138143

139-
async def setup_connection_to_stick(
140-
self, serial_path: str
141-
) -> None:
144+
async def setup_connection_to_stick(self, serial_path: str) -> None:
142145
"""Create serial connection to USB-stick."""
143146
if self._connected:
144147
raise StickError("Cannot setup connection, already connected")
145148
loop = get_event_loop()
146-
connected_future: Future[Any] = Future()
149+
connected_future: Future[bool] = Future()
147150
self._receiver = StickReceiver(connected_future)
148151
self._port = serial_path
149152

@@ -187,14 +190,14 @@ async def write_to_stick(self, request: PlugwiseRequest) -> None:
187190
_LOGGER.debug("Write to USB-stick: %s", request)
188191
if not request.resend:
189192
raise StickError(
190-
f"Failed to send {request.__class__.__name__} " +
191-
f"to node {request.mac_decoded}, maximum number " +
192-
f"of retries ({request.max_retries}) has been reached"
193+
f"Failed to send {request.__class__.__name__} "
194+
+ f"to node {request.mac_decoded}, maximum number "
195+
+ f"of retries ({request.max_retries}) has been reached"
193196
)
194197
if self._sender is None:
195198
raise StickError(
196-
f"Failed to send {request.__class__.__name__}" +
197-
"because USB-Stick connection is not setup"
199+
f"Failed to send {request.__class__.__name__}"
200+
+ "because USB-Stick connection is not setup"
198201
)
199202
await self._sender.write_request_to_port(request)
200203

plugwise_usb/connection/queue.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88

99
from ..api import StickEvent
1010
from ..exceptions import NodeTimeout, StickError, StickTimeout
11-
from ..messages.requests import NodePingRequest, PlugwiseRequest, Priority
11+
from ..messages import Priority
12+
from ..messages.requests import NodePingRequest, PlugwiseCancelRequest, PlugwiseRequest
1213
from ..messages.responses import PlugwiseResponse
1314
from .manager import StickConnectionManager
1415

@@ -31,7 +32,7 @@ def __init__(self) -> None:
3132
self._stick: StickConnectionManager | None = None
3233
self._loop = get_running_loop()
3334
self._submit_queue: PriorityQueue[PlugwiseRequest] = PriorityQueue()
34-
self._submit_worker_task: Task | None = None
35+
self._submit_worker_task: Task[None] | None = None
3536
self._unsubscribe_connection_events: Callable[[], None] | None = None
3637
self._running = False
3738

@@ -71,8 +72,7 @@ async def stop(self) -> None:
7172
self._unsubscribe_connection_events()
7273
self._running = False
7374
if self._submit_worker_task is not None and not self._submit_worker_task.done():
74-
cancel_request = PlugwiseRequest(b"0000", None)
75-
cancel_request.priority = Priority.CANCEL
75+
cancel_request = PlugwiseCancelRequest()
7676
await self._submit_queue.put(cancel_request)
7777
await self._submit_worker_task
7878
self._submit_worker_task = None
@@ -97,10 +97,10 @@ async def submit(
9797
if isinstance(request, NodePingRequest):
9898
# For ping requests it is expected to receive timeouts, so lower log level
9999
_LOGGER.debug("%s, cancel because timeout is expected for NodePingRequests", e)
100-
elif request.resend:
100+
if request.resend:
101101
_LOGGER.info("%s, retrying", e)
102102
else:
103-
_LOGGER.warning("%s, cancel request", e)
103+
_LOGGER.warning("%s, cancel request", e) # type: ignore[unreachable]
104104
except StickError as exception:
105105
_LOGGER.error(exception)
106106
raise StickError(
@@ -134,7 +134,7 @@ async def _add_request_to_queue(self, request: PlugwiseRequest) -> None:
134134
async def _send_queue_worker(self) -> None:
135135
"""Send messages from queue at the order of priority."""
136136
_LOGGER.debug("Send_queue_worker started")
137-
while self._running:
137+
while self._running and self._stick is not None:
138138
request = await self._submit_queue.get()
139139
_LOGGER.debug("Send from send queue %s", request)
140140
if request.priority == Priority.CANCEL:

0 commit comments

Comments
 (0)