Skip to content

Commit 91c9d1b

Browse files
authored
Merge pull request #204 from plugwise/stick-mac-fw
Collect Stick NodeInfo
2 parents 6eb50d9 + 62ed325 commit 91c9d1b

File tree

8 files changed

+102
-38
lines changed

8 files changed

+102
-38
lines changed

plugwise_usb/__init__.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,16 @@ def joined_nodes(self) -> int | None:
117117
return None
118118
return len(self._network.registry) + 1
119119

120+
@property
121+
def firmware(self) -> str:
122+
"""Firmware of USB-Stick."""
123+
return self._controller.firmware_stick
124+
125+
@property
126+
def hardware(self) -> str:
127+
"""Hardware of USB-Stick."""
128+
return self._controller.hardware_stick
129+
120130
@property
121131
def mac_stick(self) -> str:
122132
"""MAC address of USB-Stick. Raises StickError is connection is missing."""
@@ -127,6 +137,11 @@ def mac_coordinator(self) -> str:
127137
"""MAC address of the network coordinator (Circle+). Raises StickError is connection is missing."""
128138
return self._controller.mac_coordinator
129139

140+
@property
141+
def name(self) -> str:
142+
"""Return name of Stick."""
143+
return self._controller.stick_name
144+
130145
@property
131146
def network_discovered(self) -> bool:
132147
"""Indicate if discovery of network is active. Raises StickError is connection is missing."""

plugwise_usb/connection/__init__.py

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,21 @@
77
from typing import Any
88

99
from ..api import StickEvent
10+
from ..constants import UTF8
1011
from ..exceptions import NodeError, StickError
11-
from ..messages.requests import PlugwiseRequest, StickInitRequest
12-
from ..messages.responses import PlugwiseResponse, StickInitResponse
12+
from ..helpers.util import version_to_model
13+
from ..messages.requests import (
14+
NodeInfoRequest,
15+
NodePingRequest,
16+
PlugwiseRequest,
17+
StickInitRequest,
18+
)
19+
from ..messages.responses import (
20+
NodeInfoResponse,
21+
NodePingResponse,
22+
PlugwiseResponse,
23+
StickInitResponse,
24+
)
1325
from .manager import StickConnectionManager
1426
from .queue import StickQueue
1527

@@ -26,10 +38,13 @@ def __init__(self) -> None:
2638
self._unsubscribe_stick_event: Callable[[], None] | None = None
2739
self._init_sequence_id: bytes | None = None
2840
self._is_initialized = False
41+
self._fw_stick: str | None = None
42+
self._hw_stick: str | None = None
2943
self._mac_stick: str | None = None
3044
self._mac_nc: str | None = None
3145
self._network_id: int | None = None
3246
self._network_online = False
47+
self.stick_name: str | None = None
3348

3449
@property
3550
def is_initialized(self) -> bool:
@@ -43,6 +58,16 @@ def is_connected(self) -> bool:
4358
"""Return connection state from connection manager."""
4459
return self._manager.is_connected
4560

61+
@property
62+
def firmware_stick(self) -> str | None:
63+
"""Firmware version of the Stick."""
64+
return self._fw_stick
65+
66+
@property
67+
def hardware_stick(self) -> str | None:
68+
"""Hardware version of the Stick."""
69+
return self._hw_stick
70+
4671
@property
4772
def mac_stick(self) -> str:
4873
"""MAC address of USB-Stick. Raises StickError when not connected."""
@@ -160,16 +185,50 @@ async def initialize_stick(self) -> None:
160185
+ f"' {self._manager.serial_path}'"
161186
)
162187
self._mac_stick = init_response.mac_decoded
188+
self.stick_name = f"Stick {self._mac_stick[-5:]}"
163189
self._network_online = init_response.network_online
164190

165191
# Replace first 2 characters by 00 for mac of circle+ node
166192
self._mac_nc = init_response.mac_network_controller
167193
self._network_id = init_response.network_id
168194
self._is_initialized = True
169195

196+
# Add Stick NodeInfoRequest
197+
node_info, _ = await self.get_node_details(self._mac_stick, ping_first=False)
198+
if node_info is not None:
199+
self._fw_stick = node_info.firmware
200+
hardware, _ = version_to_model(node_info.hardware)
201+
self._hw_stick = hardware
202+
170203
if not self._network_online:
171204
raise StickError("Zigbee network connection to Circle+ is down.")
172205

206+
async def get_node_details(
207+
self, mac: str, ping_first: bool
208+
) -> tuple[NodeInfoResponse | None, NodePingResponse | None]:
209+
"""Return node discovery type."""
210+
ping_response: NodePingResponse | None = None
211+
if ping_first:
212+
# Define ping request with one retry
213+
ping_request = NodePingRequest(
214+
self.send, bytes(mac, UTF8), retries=1
215+
)
216+
try:
217+
ping_response = await ping_request.send(suppress_node_errors=True)
218+
except StickError:
219+
return (None, None)
220+
if ping_response is None:
221+
return (None, None)
222+
223+
info_request = NodeInfoRequest(
224+
self.send, bytes(mac, UTF8), retries=1
225+
)
226+
try:
227+
info_response = await info_request.send()
228+
except StickError:
229+
return (None, None)
230+
return (info_response, ping_response)
231+
173232
async def send(
174233
self, request: PlugwiseRequest, suppress_node_errors: bool = True
175234
) -> PlugwiseResponse | None:

plugwise_usb/messages/properties.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ def __init__(self, year: int = 0, month: int = 1, minutes: int = 0) -> None:
227227

228228
def deserialize(self, val: bytes) -> None:
229229
"""Convert data into datetime based on timestamp with offset to Y2k."""
230-
if val == b"FFFFFFFF":
230+
if val in (b"FFFFFFFF", b"00000000"):
231231
self._value = None
232232
else:
233233
CompositeType.deserialize(self, val)
@@ -389,6 +389,9 @@ def serialize(self) -> bytes:
389389

390390
def deserialize(self, val: bytes) -> None:
391391
"""Convert data into integer value based on log address formatted data."""
392+
if val == b"00000000":
393+
self._value = int(0)
394+
return
392395
Int.deserialize(self, val)
393396
self._value = (self.value - LOGADDR_OFFSET) // 32
394397

plugwise_usb/messages/responses.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ def _parse_params(self, response: bytes) -> bytes:
205205
my_val = response[: len(param)]
206206
param.deserialize(my_val)
207207
response = response[len(my_val) :]
208+
208209
return response
209210

210211
def __len__(self) -> int:

plugwise_usb/network/__init__.py

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,7 @@
1515
from ..constants import UTF8
1616
from ..exceptions import CacheError, MessageError, NodeError, StickError, StickTimeout
1717
from ..helpers.util import validate_mac
18-
from ..messages.requests import (
19-
CirclePlusAllowJoiningRequest,
20-
NodeInfoRequest,
21-
NodePingRequest,
22-
)
18+
from ..messages.requests import CirclePlusAllowJoiningRequest, NodePingRequest
2319
from ..messages.responses import (
2420
NODE_AWAKE_RESPONSE_ID,
2521
NODE_JOIN_ID,
@@ -289,7 +285,7 @@ def _unsubscribe_to_protocol_events(self) -> None:
289285
self._unsubscribe_stick_event = None
290286

291287
# endregion
292-
288+
293289
# region - Coordinator
294290
async def discover_network_coordinator(self, load: bool = False) -> bool:
295291
"""Discover the Zigbee network coordinator (Circle+/Stealth+)."""
@@ -365,32 +361,6 @@ def _create_node_object(
365361
self._nodes[mac].cache_folder_create = self._cache_folder_create
366362
self._nodes[mac].cache_enabled = True
367363

368-
async def get_node_details(
369-
self, mac: str, ping_first: bool
370-
) -> tuple[NodeInfoResponse | None, NodePingResponse | None]:
371-
"""Return node discovery type."""
372-
ping_response: NodePingResponse | None = None
373-
if ping_first:
374-
# Define ping request with one retry
375-
ping_request = NodePingRequest(
376-
self._controller.send, bytes(mac, UTF8), retries=1
377-
)
378-
try:
379-
ping_response = await ping_request.send(suppress_node_errors=True)
380-
except StickError:
381-
return (None, None)
382-
if ping_response is None:
383-
return (None, None)
384-
385-
info_request = NodeInfoRequest(
386-
self._controller.send, bytes(mac, UTF8), retries=1
387-
)
388-
try:
389-
info_response = await info_request.send()
390-
except StickError:
391-
return (None, None)
392-
return (info_response, ping_response)
393-
394364
async def _discover_battery_powered_node(
395365
self,
396366
address: int,
@@ -432,7 +402,7 @@ async def _discover_node(
432402

433403
# Node type is unknown, so we need to discover it first
434404
_LOGGER.debug("Starting the discovery of node %s", mac)
435-
node_info, node_ping = await self.get_node_details(mac, ping_first)
405+
node_info, node_ping = await self._controller.get_node_details(mac, ping_first)
436406
if node_info is None:
437407
return False
438408
self._create_node_object(mac, address, node_info.node_type)

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.40.0a29"
7+
version = "v0.40.0a31"
88
license = {file = "LICENSE"}
99
description = "Plugwise USB (Stick) module for Python 3."
1010
readme = "README.md"

tests/stick_test_data.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,20 @@
3636
+ b"01" # network_is_online
3737
+ b"0098765432101234" # circle_plus_mac
3838
+ b"4321" # network_id
39-
+ b"00", # unknown2
39+
+ b"FF", # unknown2
40+
),
41+
b"\x05\x05\x03\x0300230123456789012345A0EC\r\n": (
42+
"Node Info of stick 0123456789012345",
43+
b"000000C1", # Success ack
44+
b"0024" # msg_id
45+
+ b"0123456789012345" # mac
46+
+ b"00000000" # datetime
47+
+ b"00000000" # log address 0
48+
+ b"00" # relay
49+
+ b"80" # hz
50+
+ b"653907008512" # hw_ver
51+
+ b"4E0843A9" # fw_ver
52+
+ b"00", # node_type (Stick)
4053
),
4154
b"\x05\x05\x03\x03002300987654321012341AE2\r\n": (
4255
"Node Info of network controller 0098765432101234",

tests/test_usb.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,10 @@ async def test_stick_connect(self, monkeypatch: pytest.MonkeyPatch) -> None:
459459
assert await self.test_connected
460460
await stick.initialize()
461461
assert stick.mac_stick == "0123456789012345"
462+
assert stick.name == "Stick 12345"
462463
assert stick.mac_coordinator == "0098765432101234"
464+
assert stick.firmware == dt(2011, 6, 27, 8, 47, 37, tzinfo=UTC)
465+
assert stick.hardware == "070085"
463466
assert not stick.network_discovered
464467
assert stick.network_state
465468
assert stick.network_id == 17185

0 commit comments

Comments
 (0)