Skip to content

Commit cb666b5

Browse files
authored
Refactor reading/writing network parameters. (#73)
* Refactor "Network Parameter" as an enum. * Refactor Network Parameter read/write operations. Read Network Parameter via __getitem__ Write Network Parameter via __setitem__ * Deserialize read_parameter() result. * Update tests.
1 parent 5fe5f68 commit cb666b5

File tree

4 files changed

+153
-43
lines changed

4 files changed

+153
-43
lines changed

tests/test_api.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,3 +289,64 @@ async def test_aps_data_request_busy(api, monkeypatch):
289289
with pytest.raises(zigpy_deconz.exception.CommandError):
290290
await api.aps_data_request(*params)
291291
assert mock_cmd.call_count == 4
292+
293+
294+
def test_handle_read_parameter(api):
295+
api._handle_read_parameter(mock.sentinel.data)
296+
297+
298+
@pytest.mark.asyncio
299+
async def test_read_parameter(api):
300+
api._command = mock.MagicMock()
301+
api._command.side_effect = asyncio.coroutine(
302+
mock.MagicMock(return_value=(mock.sentinel.len,
303+
mock.sentinel.param_id,
304+
b'\xaa\x55')))
305+
306+
r = await api.read_parameter(deconz_api.NetworkParameter.nwk_panid)
307+
assert api._command.call_count == 1
308+
assert r == 0x55aa
309+
310+
api._command.reset_mock()
311+
r = await api.read_parameter(0x05)
312+
assert api._command.call_count == 1
313+
assert r == 0x55aa
314+
315+
with pytest.raises(KeyError):
316+
await api.read_parameter('unknown_param')
317+
318+
unk_param = 0xff
319+
assert unk_param not in list(deconz_api.NetworkParameter)
320+
with pytest.raises(KeyError):
321+
await api.read_parameter(unk_param)
322+
323+
324+
def test_handle_write_parameter(api):
325+
param_id = 0x05
326+
api._handle_write_parameter([mock.sentinel.len, param_id])
327+
328+
unk_param = 0xff
329+
assert unk_param not in list(deconz_api.NetworkParameter)
330+
api._handle_write_parameter([mock.sentinel.len, unk_param])
331+
332+
333+
@pytest.mark.asyncio
334+
async def test_write_parameter(api):
335+
api._command = mock.MagicMock()
336+
api._command.side_effect = asyncio.coroutine(
337+
mock.MagicMock())
338+
339+
await api.write_parameter(deconz_api.NetworkParameter.nwk_panid, 0x55aa)
340+
assert api._command.call_count == 1
341+
342+
api._command.reset_mock()
343+
await api.write_parameter(0x05, 0x55aa)
344+
assert api._command.call_count == 1
345+
346+
with pytest.raises(KeyError):
347+
await api.write_parameter('unknown_param', 0x55aa)
348+
349+
unk_param = 0xff
350+
assert unk_param not in list(deconz_api.NetworkParameter)
351+
with pytest.raises(KeyError):
352+
await api.write_parameter(unk_param, 0x55aa)

tests/test_application.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,12 @@ async def _version():
166166
side_effect=asyncio.coroutine(mock.MagicMock()))
167167
app._api._command = mock.MagicMock(
168168
side_effect=asyncio.coroutine(mock.MagicMock()))
169+
app._api.read_parameter = mock.MagicMock(
170+
side_effect=asyncio.coroutine(mock.MagicMock()))
169171
app._api.version = mock.MagicMock(
170172
side_effect=_version)
173+
app._api.write_parameter = mock.MagicMock(
174+
side_effect=asyncio.coroutine(mock.MagicMock()))
171175

172176
new_mock = mock.MagicMock(side_effect=asyncio.coroutine(mock.MagicMock()))
173177
monkeypatch.setattr(application.ConBeeDevice, 'new', new_mock)

zigpy_deconz/api.py

Lines changed: 72 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -67,25 +67,42 @@ class Command(t.uint8_t, enum.Enum):
6767
Command.zigbee_green_power: ((t.LVBytes, ), False),
6868
}
6969

70-
NETWORK_PARAMETER = {
71-
'mac_address': (0x01, t.uint64_t),
72-
'nwk_panid': (0x05, t.uint16_t),
73-
'nwk_address': (0x07, t.uint16_t),
74-
'nwk_extended_panid': (0x08, t.uint64_t),
75-
'aps_designed_coordinator': (0x09, t.uint8_t),
76-
'channel_mask': (0x0A, t.uint32_t),
77-
'aps_extended_panid': (0x0B, t.uint64_t),
78-
'trust_center_address': (0x0E, t.uint64_t),
79-
'security_mode': (0x10, t.uint8_t),
80-
'network_key': (0x18, t.uint8_t),
81-
'current_channel': (0x1C, t.uint8_t),
82-
'permit_join': (0x21, t.uint8_t),
83-
'protocol_version': (0x22, t.uint16_t),
84-
'nwk_update_id': (0x24, t.uint8_t),
85-
'watchdog_ttl': (0x26, t.uint32_t),
86-
}
8770

88-
NETWORK_PARAMETER_BY_ID = {v[0]: (k, v[1]) for k, v in NETWORK_PARAMETER.items()}
71+
class NetworkParameter(t.uint8_t, enum.Enum):
72+
mac_address = 0x01
73+
nwk_panid = 0x05
74+
nwk_address = 0x07
75+
nwk_extended_panid = 0x08
76+
aps_designed_coordinator = 0x09
77+
channel_mask = 0x0A
78+
aps_extended_panid = 0x0B
79+
trust_center_address = 0x0E
80+
security_mode = 0x10
81+
network_key = 0x18
82+
current_channel = 0x1C
83+
permit_join = 0x21
84+
protocol_version = 0x22
85+
nwk_update_id = 0x24
86+
watchdog_ttl = 0x26
87+
88+
89+
NETWORK_PARAMETER_SCHEMA = {
90+
NetworkParameter.mac_address: t.EUI64,
91+
NetworkParameter.nwk_panid: t.uint16_t,
92+
NetworkParameter.nwk_address: t.uint16_t,
93+
NetworkParameter.nwk_extended_panid: t.uint64_t,
94+
NetworkParameter.aps_designed_coordinator: t.uint8_t,
95+
NetworkParameter.channel_mask: t.uint32_t,
96+
NetworkParameter.aps_extended_panid: t.uint64_t,
97+
NetworkParameter.trust_center_address: t.uint64_t,
98+
NetworkParameter.security_mode: t.uint8_t,
99+
NetworkParameter.network_key: t.uint8_t,
100+
NetworkParameter.current_channel: t.uint8_t,
101+
NetworkParameter.permit_join: t.uint8_t,
102+
NetworkParameter.protocol_version: t.uint16_t,
103+
NetworkParameter.nwk_update_id: t.uint8_t,
104+
NetworkParameter.watchdog_ttl: t.uint32_t,
105+
}
89106

90107

91108
class Status(t.uint8_t, enum.Enum):
@@ -206,19 +223,44 @@ def change_network_state(self, state):
206223
def _handle_change_network_state(self, data):
207224
LOGGER.debug("Change network state response: %s", NetworkState(data[0]).name)
208225

209-
def read_parameter(self, id_):
210-
return self._command(Command.read_parameter, 1, id_)
226+
async def read_parameter(self, id_):
227+
try:
228+
if isinstance(id_, str):
229+
param = NetworkParameter[id_]
230+
else:
231+
param = NetworkParameter(id_)
232+
except (KeyError, ValueError):
233+
raise KeyError("Unknown parameter id: %s" % (id_, ))
234+
235+
r = await self._command(Command.read_parameter, 1, param)
236+
data = NETWORK_PARAMETER_SCHEMA[param].deserialize(r[2])[0]
237+
LOGGER.debug("Read parameter %s response: %s", param.name, data)
238+
return data
211239

212240
def _handle_read_parameter(self, data):
213-
LOGGER.debug("Read parameter %s response: %s", NETWORK_PARAMETER_BY_ID[data[1]][0], data[2])
241+
pass
214242

215243
def write_parameter(self, id_, value):
216-
v = NETWORK_PARAMETER_BY_ID[id_][1](value).serialize()
244+
try:
245+
if isinstance(id_, str):
246+
param = NetworkParameter[id_]
247+
else:
248+
param = NetworkParameter(id_)
249+
except (KeyError, ValueError):
250+
raise KeyError("Unknown parameter id: %s write request" % (id_,))
251+
252+
v = NETWORK_PARAMETER_SCHEMA[param](value).serialize()
217253
length = len(v) + 1
218-
return self._command(Command.write_parameter, length, id_, v)
254+
return self._command(Command.write_parameter, length, param, v)
219255

220256
def _handle_write_parameter(self, data):
221-
LOGGER.debug("Write parameter %s: SUCCESS", NETWORK_PARAMETER_BY_ID[data[1]][0])
257+
try:
258+
param = NetworkParameter(data[1])
259+
except ValueError:
260+
LOGGER.error("Received unknown network param id '%s' response",
261+
data[1])
262+
return
263+
LOGGER.debug("Write parameter %s: SUCCESS", param.name)
222264

223265
def version(self):
224266
return self._command(Command.version)
@@ -317,3 +359,9 @@ def _handle_device_state_value(self, value):
317359
if DeviceState.APSDE_DATA_CONFIRM in flags and not self._data_confirm:
318360
self._data_confirm = True
319361
asyncio.ensure_future(self._aps_data_confirm())
362+
363+
def __getitem__(self, key):
364+
return self.read_parameter(key)
365+
366+
def __setitem__(self, key, value):
367+
return asyncio.ensure_future(self.write_parameter(key, value))

zigpy_deconz/zigbee/application.py

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import zigpy.util
1111
import zigpy_deconz.exception
1212
from zigpy_deconz import types as t
13-
from zigpy_deconz.api import NETWORK_PARAMETER, NetworkState
13+
from zigpy_deconz.api import NetworkParameter, NetworkState
1414

1515
LOGGER = logging.getLogger(__name__)
1616

@@ -32,7 +32,7 @@ def __init__(self, api, database_file=None):
3232

3333
async def _reset_watchdog(self):
3434
while True:
35-
await self._api.write_parameter(NETWORK_PARAMETER['watchdog_ttl'][0], 3600)
35+
await self._api.write_parameter(NetworkParameter.watchdog_ttl, 3600)
3636
await asyncio.sleep(1200)
3737

3838
async def shutdown(self):
@@ -44,19 +44,19 @@ async def startup(self, auto_form=False):
4444
r = await self._api.version()
4545
self.version = r[0]
4646
await self._api.device_state()
47-
r = await self._api.read_parameter(NETWORK_PARAMETER['mac_address'][0])
48-
self._ieee = zigpy.types.EUI64([zigpy.types.uint8_t(r[2][i]) for i in range(7, -1, -1)])
49-
await self._api.read_parameter(NETWORK_PARAMETER['nwk_panid'][0])
50-
await self._api.read_parameter(NETWORK_PARAMETER['nwk_address'][0])
51-
await self._api.read_parameter(NETWORK_PARAMETER['nwk_extended_panid'][0])
52-
await self._api.read_parameter(NETWORK_PARAMETER['channel_mask'][0])
53-
await self._api.read_parameter(NETWORK_PARAMETER['aps_extended_panid'][0])
54-
await self._api.read_parameter(NETWORK_PARAMETER['trust_center_address'][0])
55-
await self._api.read_parameter(NETWORK_PARAMETER['security_mode'][0])
56-
await self._api.read_parameter(NETWORK_PARAMETER['current_channel'][0])
57-
await self._api.read_parameter(NETWORK_PARAMETER['protocol_version'][0])
58-
await self._api.read_parameter(NETWORK_PARAMETER['nwk_update_id'][0])
59-
await self._api.write_parameter(NETWORK_PARAMETER['aps_designed_coordinator'][0], 1)
47+
ieee = await self._api[NetworkParameter.mac_address]
48+
self._ieee = zigpy.types.EUI64(ieee)
49+
await self._api[NetworkParameter.nwk_panid]
50+
await self._api[NetworkParameter.nwk_address]
51+
await self._api[NetworkParameter.nwk_extended_panid]
52+
await self._api[NetworkParameter.channel_mask]
53+
await self._api[NetworkParameter.aps_extended_panid]
54+
await self._api[NetworkParameter.trust_center_address]
55+
await self._api[NetworkParameter.security_mode]
56+
await self._api[NetworkParameter.current_channel]
57+
await self._api[NetworkParameter.protocol_version]
58+
await self._api[NetworkParameter.nwk_update_id]
59+
self._api[NetworkParameter.aps_designed_coordinator] = 1
6060

6161
if self.version > 0x261f0500:
6262
asyncio.ensure_future(self._reset_watchdog())
@@ -152,10 +152,7 @@ async def broadcast(self, profile, cluster, src_ep, dst_ep, grpid, radius,
152152

153153
async def permit_ncp(self, time_s=60):
154154
assert 0 <= time_s <= 254
155-
await self._api.write_parameter(
156-
NETWORK_PARAMETER['permit_join'][0],
157-
time_s
158-
)
155+
await self._api.write_parameter(NetworkParameter.permit_join, time_s)
159156

160157
def handle_rx(self, src_addr, src_ep, dst_ep, profile_id, cluster_id, data, lqi, rssi):
161158
# intercept ZDO device announce frames

0 commit comments

Comments
 (0)