Skip to content

Commit 317aca0

Browse files
authored
Merge pull request #232 from plugwise/join-services
Auto-joining, replace device-add/-remove services
2 parents da5f919 + 59b8742 commit 317aca0

File tree

23 files changed

+246
-242
lines changed

23 files changed

+246
-242
lines changed

.github/workflows/verify.yml

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
name: Latest commit
55

66
env:
7-
CACHE_VERSION: 22
7+
CACHE_VERSION: 24
88
DEFAULT_PYTHON: "3.13"
99
PRE_COMMIT_HOME: ~/.cache/pre-commit
1010

@@ -22,7 +22,7 @@ jobs:
2222
name: Prepare
2323
steps:
2424
- name: Check out committed code
25-
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
25+
uses: actions/checkout@v4
2626
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
2727
id: python
2828
uses: actions/setup-python@v5
@@ -71,7 +71,7 @@ jobs:
7171
needs: prepare
7272
steps:
7373
- name: Check out committed code
74-
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
74+
uses: actions/checkout@v4
7575
with:
7676
persist-credentials: false
7777
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
@@ -124,7 +124,7 @@ jobs:
124124
- dependencies_check
125125
steps:
126126
- name: Check out committed code
127-
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
127+
uses: actions/checkout@v4
128128
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
129129
id: python
130130
uses: actions/setup-python@v5
@@ -175,7 +175,7 @@ jobs:
175175
python-version: ["3.13"]
176176
steps:
177177
- name: Check out committed code
178-
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
178+
uses: actions/checkout@v4
179179
- name: Set up Python ${{ matrix.python-version }}
180180
id: python
181181
uses: actions/setup-python@v5
@@ -215,7 +215,7 @@ jobs:
215215

216216
steps:
217217
- name: Check out committed code
218-
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
218+
uses: actions/checkout@v4
219219
- name: Set up Python ${{ matrix.python-version }}
220220
id: python
221221
uses: actions/setup-python@v5
@@ -253,7 +253,7 @@ jobs:
253253
needs: pytest
254254
steps:
255255
- name: Check out committed code
256-
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
256+
uses: actions/checkout@v4
257257
with:
258258
persist-credentials: false
259259
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
@@ -293,7 +293,7 @@ jobs:
293293
runs-on: ubuntu-latest
294294
steps:
295295
- name: Check out committed code
296-
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
296+
uses: actions/checkout@v4
297297
- name: Run ShellCheck
298298
uses: ludeeus/action-shellcheck@master
299299

@@ -303,7 +303,7 @@ jobs:
303303
name: Dependency
304304
steps:
305305
- name: Check out committed code
306-
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
306+
uses: actions/checkout@v4
307307
- name: Run dependency checker
308308
run: scripts/dependencies_check.sh debug
309309

@@ -313,7 +313,7 @@ jobs:
313313
needs: pytest
314314
steps:
315315
- name: Check out committed code
316-
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
316+
uses: actions/checkout@v4
317317
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
318318
id: python
319319
uses: actions/setup-python@v5
@@ -358,7 +358,7 @@ jobs:
358358
needs: [coverage, mypy]
359359
steps:
360360
- name: Check out committed code
361-
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
361+
uses: actions/checkout@v4
362362
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
363363
id: python
364364
uses: actions/setup-python@v5
@@ -401,7 +401,7 @@ jobs:
401401
needs: coverage
402402
steps:
403403
- name: Check out committed code
404-
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
404+
uses: actions/checkout@v4
405405
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
406406
id: python
407407
uses: actions/setup-python@v5

CODEOWNERS

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,11 @@
33

44
# Specific files
55
setup.cfg @plugwise/plugwise-usb
6-
setup.py @plugwise/plugwise-usb
76
pyproject.toml @plugwise/plugwise-usb
87
requirements*.txt @plugwise/plugwise-usb
98

109
# Main code
11-
/plugwise/ @plugwise/plugwise-usb
12-
/userdata/ @plugwise/plugwise-usb
10+
/plugwise_usb/ @plugwise/plugwise-usb
1311

1412
# Tests and development support
1513
/tests/ @plugwise/plugwise-usb

plugwise_usb/__init__.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

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

2020
FuncT = TypeVar("FuncT", bound=Callable[..., Any])
@@ -190,20 +190,27 @@ def accept_join_request(self) -> bool | None:
190190
return None
191191
return self._network.accept_join_request
192192

193-
@accept_join_request.setter
194-
def accept_join_request(self, state: bool) -> None:
193+
async def set_accept_join_request(self, state: bool) -> bool:
195194
"""Configure join request setting."""
196195
if not self._controller.is_connected:
197196
raise StickError(
198197
"Cannot accept joining node"
199198
+ " without an active USB-Stick connection."
200199
)
200+
201201
if self._network is None or not self._network.is_running:
202202
raise StickError(
203203
"Cannot accept joining node"
204204
+ "without node discovery be activated. Call discover() first."
205205
)
206-
self._network.accept_join_request = state
206+
207+
# Observation: joining is only temporarily possible after a HA (re)start or
208+
# Integration reload, force the setting when used otherwise
209+
try:
210+
await self._network.allow_join_requests(state)
211+
except (MessageError, NodeError) as exc:
212+
raise NodeError(f"Failed setting accept joining: {exc}") from exc
213+
return True
207214

208215
async def clear_cache(self) -> None:
209216
"""Clear current cache."""
@@ -347,15 +354,22 @@ async def register_node(self, mac: str) -> bool:
347354
"""Add node to plugwise network."""
348355
if self._network is None:
349356
return False
350-
return await self._network.register_node(mac)
357+
358+
try:
359+
return await self._network.register_node(mac)
360+
except NodeError as exc:
361+
raise NodeError(f"Unable to add Node ({mac}): {exc}") from exc
351362

352363
@raise_not_connected
353364
@raise_not_initialized
354365
async def unregister_node(self, mac: str) -> None:
355366
"""Remove node to plugwise network."""
356367
if self._network is None:
357368
return
358-
await self._network.unregister_node(mac)
369+
try:
370+
await self._network.unregister_node(mac)
371+
except MessageError as exc:
372+
raise NodeError(f"Unable to remove Node ({mac}): {exc}") from exc
359373

360374
async def disconnect(self) -> None:
361375
"""Disconnect from USB-Stick."""

plugwise_usb/connection/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ async def get_node_details(
214214
self.send, bytes(mac, UTF8), retries=1
215215
)
216216
try:
217-
ping_response = await ping_request.send(suppress_node_errors=True)
217+
ping_response = await ping_request.send()
218218
except StickError:
219219
return (None, None)
220220
if ping_response is None:
@@ -230,7 +230,9 @@ async def get_node_details(
230230
return (info_response, ping_response)
231231

232232
async def send(
233-
self, request: PlugwiseRequest, suppress_node_errors: bool = True
233+
self,
234+
request: PlugwiseRequest,
235+
suppress_node_errors=True,
234236
) -> PlugwiseResponse | None:
235237
"""Submit request to queue and return response."""
236238
if not suppress_node_errors:

plugwise_usb/connection/queue.py

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -75,46 +75,53 @@ async def stop(self) -> None:
7575
_LOGGER.debug("queue stopped")
7676

7777
async def submit(self, request: PlugwiseRequest) -> PlugwiseResponse | None:
78-
"""Add request to queue and return the response of node. Raises an error when something fails."""
78+
"""Add request to queue and return the received node-response when applicable.
79+
80+
Raises an error when something fails.
81+
"""
7982
if request.waiting_for_response:
8083
raise MessageError(
8184
f"Cannot send message {request} which is currently waiting for response."
8285
)
8386

84-
while request.resend and not request.waiting_for_response:
87+
while request.resend:
8588
_LOGGER.debug("submit | start (%s) %s", request.retries_left, request)
8689
if not self._running or self._stick is None:
8790
raise StickError(
8891
f"Cannot send message {request.__class__.__name__} for"
8992
+ f"{request.mac_decoded} because queue manager is stopped"
9093
)
94+
9195
await self._add_request_to_queue(request)
96+
if request.no_response:
97+
return None
98+
9299
try:
93100
response: PlugwiseResponse = await request.response_future()
94101
return response
95-
except (NodeTimeout, StickTimeout) as e:
102+
except (NodeTimeout, StickTimeout) as exc:
96103
if isinstance(request, NodePingRequest):
97104
# For ping requests it is expected to receive timeouts, so lower log level
98105
_LOGGER.debug(
99-
"%s, cancel because timeout is expected for NodePingRequests", e
106+
"%s, cancel because timeout is expected for NodePingRequests", exc
100107
)
101108
elif request.resend:
102-
_LOGGER.debug("%s, retrying", e)
109+
_LOGGER.debug("%s, retrying", exc)
103110
else:
104-
_LOGGER.warning("%s, cancel request", e) # type: ignore[unreachable]
105-
except StickError as exception:
106-
_LOGGER.error(exception)
111+
_LOGGER.warning("%s, cancel request", exc) # type: ignore[unreachable]
112+
except StickError as exc:
113+
_LOGGER.error(exc)
107114
self._stick.correct_received_messages(1)
108115
raise StickError(
109116
f"No response received for {request.__class__.__name__} "
110117
+ f"to {request.mac_decoded}"
111-
) from exception
112-
except BaseException as exception:
118+
) from exc
119+
except BaseException as exc:
113120
self._stick.correct_received_messages(1)
114121
raise StickError(
115122
f"No response received for {request.__class__.__name__} "
116123
+ f"to {request.mac_decoded}"
117-
) from exception
124+
) from exc
118125

119126
return None
120127

@@ -132,7 +139,7 @@ async def _send_queue_worker(self) -> None:
132139
_LOGGER.debug("Send_queue_worker started")
133140
while self._running and self._stick is not None:
134141
request = await self._submit_queue.get()
135-
_LOGGER.debug("Send from send queue %s", request)
142+
_LOGGER.debug("Sending from send queue %s", request)
136143
if request.priority == Priority.CANCEL:
137144
self._submit_queue.task_done()
138145
return

plugwise_usb/connection/receiver.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ async def _put_message_in_queue(
271271
_LOGGER.debug("Add response to queue: %s", response)
272272
await self._message_queue.put(response)
273273
if self._message_worker_task is None or self._message_worker_task.done():
274+
_LOGGER.debug("Queue: start new worker-task")
274275
self._message_worker_task = self._loop.create_task(
275276
self._message_queue_worker(),
276277
name="Plugwise message receiver queue worker",
@@ -281,6 +282,7 @@ async def _message_queue_worker(self) -> None:
281282
_LOGGER.debug("Message queue worker started")
282283
while self.is_connected:
283284
response: PlugwiseResponse = await self._message_queue.get()
285+
_LOGGER.debug("Priority: %s", response.priority)
284286
if response.priority == Priority.CANCEL:
285287
self._message_queue.task_done()
286288
return
@@ -511,5 +513,4 @@ async def _notify_node_response_subscribers(
511513
name=f"Postpone subscription task for {node_response.seq_id!r} retry {node_response.retries}",
512514
)
513515

514-
515516
# endregion

plugwise_usb/connection/sender.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,15 @@ async def write_request_to_port(self, request: PlugwiseRequest) -> None:
7575
self._stick_response = self._loop.create_future()
7676

7777
request.add_send_attempt()
78-
_LOGGER.info("Send %s", request)
78+
_LOGGER.info("Sending %s", request)
7979

8080
# Write message to serial port buffer
8181
serialized_data = request.serialize()
8282
_LOGGER.debug("write_request_to_port | Write %s to port as %s", request, serialized_data)
8383
self._transport.write(serialized_data)
84-
request.start_response_timeout()
84+
# Don't timeout when no response expected
85+
if not request.no_response:
86+
request.start_response_timeout()
8587

8688
# Wait for USB stick to accept request
8789
try:
@@ -118,7 +120,7 @@ async def write_request_to_port(self, request: PlugwiseRequest) -> None:
118120
self._receiver.subscribe_to_stick_responses,
119121
self._receiver.subscribe_to_node_responses,
120122
)
121-
_LOGGER.debug("write_request_to_port | request has subscribed : %s", request)
123+
_LOGGER.debug("write_request_to_port | request has subscribed : %s", request)
122124
elif response.response_type == StickResponseType.TIMEOUT:
123125
_LOGGER.warning(
124126
"USB-Stick directly responded with communication timeout for %s",

plugwise_usb/constants.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,11 @@
3030
MESSAGE_HEADER: Final = b"\x05\x05\x03\x03"
3131

3232
# Max timeout in seconds
33-
STICK_TIME_OUT: Final = 11 # Stick responds with timeout messages within 10s.
34-
NODE_TIME_OUT: Final = 15 # In bigger networks a response from a node could take up a while, so lets use 15 seconds.
33+
# Stick responds with timeout messages within 10s.
34+
STICK_TIME_OUT: Final = 11
35+
# In bigger networks a response from a Node could take up a while, so lets use 15 seconds.
36+
NODE_TIME_OUT: Final = 15
37+
3538
MAX_RETRIES: Final = 3
3639
SUPPRESS_INITIALIZATION_WARNINGS: Final = 10 # Minutes to suppress (expected) communication warning messages after initialization
3740

plugwise_usb/messages/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from ..helpers.util import crc_fun
1212

1313

14-
class Priority(int, Enum):
14+
class Priority(Enum):
1515
"""Message priority levels for USB-stick message requests."""
1616

1717
CANCEL = 0

0 commit comments

Comments
 (0)