Skip to content

Commit 5ac5738

Browse files
authored
Refactor reading/writing network parameters. (#79)
* Network parameteres specific types. * List types class. Refactor EUI64 to use new List classes. * Refactor read_parameter()/write_parameter(). Some parameters (like Network Key) require an extra argument eg index. Modify readin/writing parameters schema. * Update tests. * Update dependencies.
1 parent 67a9a0c commit 5ac5738

File tree

7 files changed

+113
-44
lines changed

7 files changed

+113
-44
lines changed

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
packages=find_packages(exclude=['*.tests']),
1616
install_requires=[
1717
'pyserial-asyncio',
18-
'zigpy-homeassistant>=0.9.0',
18+
'zigpy-homeassistant>=0.10.0',
1919
],
2020
tests_require=[
2121
'pytest',

tests/test_api.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -305,12 +305,12 @@ async def test_read_parameter(api):
305305

306306
r = await api.read_parameter(deconz_api.NetworkParameter.nwk_panid)
307307
assert api._command.call_count == 1
308-
assert r == 0x55aa
308+
assert r[0] == 0x55aa
309309

310310
api._command.reset_mock()
311311
r = await api.read_parameter(0x05)
312312
assert api._command.call_count == 1
313-
assert r == 0x55aa
313+
assert r[0] == 0x55aa
314314

315315
with pytest.raises(KeyError):
316316
await api.read_parameter('unknown_param')
@@ -365,7 +365,7 @@ async def test_write_parameter(api):
365365
async def test_version(protocol_ver, firmware_version, flags, api):
366366
api.read_parameter = mock.MagicMock()
367367
api.read_parameter.side_effect = asyncio.coroutine(
368-
mock.MagicMock(return_value=protocol_ver))
368+
mock.MagicMock(return_value=[protocol_ver]))
369369
api._command = mock.MagicMock()
370370
api._command.side_effect = asyncio.coroutine(
371371
mock.MagicMock(return_value=[firmware_version]))

tests/test_application.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ async def test_startup(protocol_ver, watchdog_cc, app, monkeypatch, version=0):
195195

196196
async def _version():
197197
app._api._proto_ver = protocol_ver
198-
return version
198+
return [version]
199199

200200
app._reset_watchdog = mock.MagicMock(
201201
side_effect=asyncio.coroutine(mock.MagicMock()))
@@ -204,7 +204,8 @@ async def _version():
204204
app._api._command = mock.MagicMock(
205205
side_effect=asyncio.coroutine(mock.MagicMock()))
206206
app._api.read_parameter = mock.MagicMock(
207-
side_effect=asyncio.coroutine(mock.MagicMock()))
207+
side_effect=asyncio.coroutine(
208+
mock.MagicMock(return_value=[[0]])))
208209
app._api.version = mock.MagicMock(
209210
side_effect=_version)
210211
app._api.write_parameter = mock.MagicMock(

tests/test_types.py

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,14 @@ def test_deconz_address_ieee():
3535
assert rest == extra
3636
assert addr.address_mode == t.ADDRESS_MODE.IEEE
3737
assert addr.address_mode == 3
38-
assert addr.address == [0xbe, 0xef, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x55]
38+
assert addr.address[0] == 0x55
39+
assert addr.address[1] == 0xaa
40+
assert addr.address[2] == 0xbb
41+
assert addr.address[3] == 0xcc
42+
assert addr.address[4] == 0xdd
43+
assert addr.address[5] == 0xee
44+
assert addr.address[6] == 0xef
45+
assert addr.address[7] == 0xbe
3946

4047
assert addr.serialize() == data
4148

@@ -48,7 +55,33 @@ def test_deconz_address_nwk_and_ieee():
4855
assert rest == extra
4956
assert addr.address_mode == t.ADDRESS_MODE.NWK_AND_IEEE
5057
assert addr.address_mode == 4
51-
assert addr.ieee == [0xbe, 0xef, 0xee, 0xdd, 0xcc, 0xbb, 0x99, 0x88]
58+
assert addr.ieee[0] == 0x88
59+
assert addr.ieee[1] == 0x99
60+
assert addr.ieee[2] == 0xbb
61+
assert addr.ieee[3] == 0xcc
62+
assert addr.ieee[4] == 0xdd
63+
assert addr.ieee[5] == 0xee
64+
assert addr.ieee[6] == 0xef
65+
assert addr.ieee[7] == 0xbe
5266
assert addr.address == 0xaa55
5367

5468
assert addr.serialize() == data
69+
70+
71+
def test_pan_id():
72+
t.PanId()
73+
74+
75+
def test_extended_pan_id():
76+
t.ExtendedPanId()
77+
78+
79+
def test_key():
80+
data = b'\x31\x39\x63\x32\x30\x65\x61\x63\x36\x36\x32\x63\x61\x38\x30\x35'
81+
extra = b'extra data'
82+
83+
key, rest = t.Key.deserialize(data + extra)
84+
assert rest == extra
85+
assert key == [49, 57, 99, 50, 48, 101, 97, 99, 54, 54, 50, 99, 97, 56, 48, 53]
86+
87+
assert key.serialize() == data

zigpy_deconz/api.py

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class Command(t.uint8_t, enum.Enum):
3838
),
3939
Command.change_network_state: (t.uint8_t, ),
4040
Command.device_state: (t.uint8_t, t.uint8_t, t.uint8_t),
41-
Command.read_parameter: (t.uint16_t, t.uint8_t),
41+
Command.read_parameter: (t.uint16_t, t.uint8_t, t.Bytes),
4242
Command.version: (),
4343
Command.write_parameter: (t.uint16_t, t.uint8_t, t.Bytes),
4444
}
@@ -88,21 +88,21 @@ class NetworkParameter(t.uint8_t, enum.Enum):
8888

8989

9090
NETWORK_PARAMETER_SCHEMA = {
91-
NetworkParameter.mac_address: t.EUI64,
92-
NetworkParameter.nwk_panid: t.uint16_t,
93-
NetworkParameter.nwk_address: t.uint16_t,
94-
NetworkParameter.nwk_extended_panid: t.uint64_t,
95-
NetworkParameter.aps_designed_coordinator: t.uint8_t,
96-
NetworkParameter.channel_mask: t.uint32_t,
97-
NetworkParameter.aps_extended_panid: t.uint64_t,
98-
NetworkParameter.trust_center_address: t.uint64_t,
99-
NetworkParameter.security_mode: t.uint8_t,
100-
NetworkParameter.network_key: t.uint8_t,
101-
NetworkParameter.current_channel: t.uint8_t,
102-
NetworkParameter.permit_join: t.uint8_t,
103-
NetworkParameter.protocol_version: t.uint16_t,
104-
NetworkParameter.nwk_update_id: t.uint8_t,
105-
NetworkParameter.watchdog_ttl: t.uint32_t,
91+
NetworkParameter.mac_address: (t.EUI64, ),
92+
NetworkParameter.nwk_panid: (t.PanId, ),
93+
NetworkParameter.nwk_address: (t.NWK, ),
94+
NetworkParameter.nwk_extended_panid: (t.ExtendedPanId, ),
95+
NetworkParameter.aps_designed_coordinator: (t.uint8_t, ),
96+
NetworkParameter.channel_mask: (t.uint32_t, ),
97+
NetworkParameter.aps_extended_panid: (t.ExtendedPanId, ),
98+
NetworkParameter.trust_center_address: (t.EUI64, ),
99+
NetworkParameter.security_mode: (t.uint8_t, ),
100+
NetworkParameter.network_key: (t.uint8_t, t.Key, ),
101+
NetworkParameter.current_channel: (t.uint8_t, ),
102+
NetworkParameter.permit_join: (t.uint8_t, ),
103+
NetworkParameter.protocol_version: (t.uint16_t, ),
104+
NetworkParameter.nwk_update_id: (t.uint8_t, ),
105+
NetworkParameter.watchdog_ttl: (t.uint32_t, ),
106106
}
107107

108108

@@ -231,7 +231,7 @@ def change_network_state(self, state):
231231
def _handle_change_network_state(self, data):
232232
LOGGER.debug("Change network state response: %s", NetworkState(data[0]).name)
233233

234-
async def read_parameter(self, id_):
234+
async def read_parameter(self, id_, *args):
235235
try:
236236
if isinstance(id_, str):
237237
param = NetworkParameter[id_]
@@ -240,15 +240,16 @@ async def read_parameter(self, id_):
240240
except (KeyError, ValueError):
241241
raise KeyError("Unknown parameter id: %s" % (id_, ))
242242

243-
r = await self._command(Command.read_parameter, 1, param)
244-
data = NETWORK_PARAMETER_SCHEMA[param].deserialize(r[2])[0]
243+
data = t.serialize(args, NETWORK_PARAMETER_SCHEMA[param])
244+
r = await self._command(Command.read_parameter, 1 + len(data), param, data)
245+
data = t.deserialize(r[2], NETWORK_PARAMETER_SCHEMA[param])[0]
245246
LOGGER.debug("Read parameter %s response: %s", param.name, data)
246247
return data
247248

248249
def _handle_read_parameter(self, data):
249250
pass
250251

251-
def write_parameter(self, id_, value):
252+
def write_parameter(self, id_, *args):
252253
try:
253254
if isinstance(id_, str):
254255
param = NetworkParameter[id_]
@@ -257,7 +258,7 @@ def write_parameter(self, id_, value):
257258
except (KeyError, ValueError):
258259
raise KeyError("Unknown parameter id: %s write request" % (id_,))
259260

260-
v = NETWORK_PARAMETER_SCHEMA[param](value).serialize()
261+
v = t.serialize(args, NETWORK_PARAMETER_SCHEMA[param])
261262
length = len(v) + 1
262263
return self._command(Command.write_parameter, length, param, v)
263264

@@ -271,7 +272,7 @@ def _handle_write_parameter(self, data):
271272
LOGGER.debug("Write parameter %s: SUCCESS", param.name)
272273

273274
async def version(self):
274-
self._proto_ver = await self[NetworkParameter.protocol_version]
275+
self._proto_ver, = await self[NetworkParameter.protocol_version]
275276
version = await self._command(Command.version)
276277
if self.protocol_version >= MIN_PROTO_VERSION and \
277278
(version[0] & 0x0000FF00) == 0x00000500:

zigpy_deconz/types.py

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -159,38 +159,67 @@ def __repr__(self):
159159
return r
160160

161161

162-
class EUI64(list):
162+
class List(list):
163+
_length = None
164+
_itemtype = None
165+
163166
def serialize(self):
164-
assert len(self) == 8
165-
return b''.join([i.serialize() for i in self[::-1]])
167+
assert self._length is None or len(self) == self._length
168+
return b"".join([self._itemtype(i).serialize() for i in self])
169+
170+
@classmethod
171+
def deserialize(cls, data):
172+
assert cls._itemtype is not None
173+
r = cls()
174+
while data:
175+
item, data = cls._itemtype.deserialize(data)
176+
r.append(item)
177+
return r, data
178+
179+
180+
class FixedList(List):
181+
_length = None
182+
_itemtype = None
166183

167184
@classmethod
168185
def deserialize(cls, data):
169-
r = []
170-
for i in range(8):
171-
item, data = uint8_t.deserialize(data)
186+
assert cls._itemtype is not None
187+
r = cls()
188+
for i in range(cls._length):
189+
item, data = cls._itemtype.deserialize(data)
172190
r.append(item)
173-
return cls(r[::-1]), data
191+
return r, data
192+
193+
194+
class EUI64(FixedList):
195+
_length = 8
196+
_itemtype = uint8_t
174197

175198
def __repr__(self):
176-
return ':'.join('%02x' % i for i in self)
199+
return ':'.join('%02x' % i for i in self[::-1])
177200

178201
def __hash__(self):
179202
return hash(repr(self))
180203

181204

182205
class HexRepr:
183-
_hex_len = 2
184-
185206
def __repr__(self):
186-
return ('0x{:0' + str(self._hex_len) + 'x}').format(self)
207+
return ('0x{:0' + str(self._size * 2) + 'x}').format(self)
187208

188209
def __str__(self):
189-
return ('0x{:0' + str(self._hex_len) + 'x}').format(self)
210+
return ('0x{:0' + str(self._size * 2) + 'x}').format(self)
190211

191212

192213
class NWK(HexRepr, uint16_t):
193-
_hex_len = 4
214+
pass
215+
216+
217+
class PanId(HexRepr, uint16_t):
218+
pass
219+
220+
221+
class ExtendedPanId(EUI64):
222+
pass
194223

195224

196225
class DeconzAddress(Struct):
@@ -245,3 +274,8 @@ def deserialize(cls, data):
245274
e, data = uint8_t.deserialize(data)
246275
setattr(r, cls._fields[2][0], e)
247276
return r, data
277+
278+
279+
class Key(FixedList):
280+
_itemtype = uint8_t
281+
_length = 16

zigpy_deconz/zigbee/application.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ async def startup(self, auto_form=False):
4444
"""Perform a complete application startup"""
4545
self.version = await self._api.version()
4646
await self._api.device_state()
47-
ieee = await self._api[NetworkParameter.mac_address]
47+
ieee, = await self._api[NetworkParameter.mac_address]
4848
self._ieee = zigpy.types.EUI64(ieee)
4949
await self._api[NetworkParameter.nwk_panid]
5050
await self._api[NetworkParameter.nwk_address]

0 commit comments

Comments
 (0)