88import zigpy .application
99import zigpy .config
1010import zigpy .device
11- from zigpy .types import EUI64 , Channels
11+ from zigpy .types import EUI64 , Channels , KeyData
1212import zigpy .zdo .types as zdo_t
1313
1414from zigpy_deconz import types as t
@@ -41,6 +41,7 @@ def api():
4141 return_value = deconz_api .DeviceState (deconz_api .NetworkState .CONNECTED )
4242 )
4343 api .write_parameter = AsyncMock ()
44+ api .firmware_version = deconz_api .FirmwareVersion (0x26580700 )
4445
4546 # So the protocol version is effectively infinite
4647 api ._protocol_version .__ge__ .return_value = True
@@ -112,7 +113,7 @@ def addr_nwk_and_ieee(nwk, ieee):
112113 return addr
113114
114115
115- @patch ("zigpy_deconz.zigbee.application.CHANGE_NETWORK_WAIT " , 0.001 )
116+ @patch ("zigpy_deconz.zigbee.application.CHANGE_NETWORK_POLL_TIME " , 0.001 )
116117@pytest .mark .parametrize (
117118 "proto_ver, target_state, returned_state" ,
118119 [
@@ -200,16 +201,12 @@ async def test_connect_failure(app):
200201
201202
202203async def test_disconnect (app ):
203- reset_watchdog_task = app ._reset_watchdog_task = MagicMock ()
204204 api_close = app ._api .close = MagicMock ()
205205
206206 await app .disconnect ()
207207
208208 assert app ._api is None
209- assert app ._reset_watchdog_task is None
210-
211209 assert api_close .call_count == 1
212- assert reset_watchdog_task .cancel .call_count == 1
213210
214211
215212async def test_disconnect_no_api (app ):
@@ -224,19 +221,34 @@ async def test_disconnect_close_error(app):
224221 await app .disconnect ()
225222
226223
227- async def test_permit_with_key_not_implemented (app ):
228- with pytest .raises (NotImplementedError ):
229- await app .permit_with_key (node = MagicMock (), code = b"abcdef" )
224+ async def test_permit_with_link_key (app ):
225+ app ._api .write_parameter = AsyncMock ()
226+ app .permit = AsyncMock ()
227+
228+ await app .permit_with_link_key (
229+ node = t .EUI64 .convert ("00:11:22:33:44:55:66:77" ),
230+ link_key = KeyData .convert ("aa:bb:cc:dd:aa:bb:cc:dd:aa:bb:cc:dd:aa:bb:cc:dd" ),
231+ )
232+
233+ assert app ._api .write_parameter .mock_calls == [
234+ mock .call (
235+ deconz_api .NetworkParameter .link_key ,
236+ deconz_api .LinkKey (
237+ ieee = t .EUI64 .convert ("00:11:22:33:44:55:66:77" ),
238+ key = KeyData .convert ("aa:bb:cc:dd:aa:bb:cc:dd:aa:bb:cc:dd:aa:bb:cc:dd" ),
239+ ),
240+ )
241+ ]
242+
243+ assert app .permit .mock_calls == [mock .call (mock .ANY )]
230244
231245
232246async def test_deconz_dev_add_to_group (app , nwk , device_path ):
233247 group = MagicMock ()
234248 app ._groups = MagicMock ()
235249 app ._groups .add_group .return_value = group
236250
237- deconz = application .DeconzDevice (
238- deconz_api .FirmwareVersion (0 ), device_path , app , sentinel .ieee , nwk
239- )
251+ deconz = application .DeconzDevice ("Conbee II" , app , sentinel .ieee , nwk )
240252 deconz .endpoints = {
241253 0 : sentinel .zdo ,
242254 1 : sentinel .ep1 ,
@@ -254,9 +266,7 @@ async def test_deconz_dev_add_to_group(app, nwk, device_path):
254266async def test_deconz_dev_remove_from_group (app , nwk , device_path ):
255267 group = MagicMock ()
256268 app .groups [sentinel .grp_id ] = group
257- deconz = application .DeconzDevice (
258- deconz_api .FirmwareVersion (0 ), device_path , app , sentinel .ieee , nwk
259- )
269+ deconz = application .DeconzDevice ("Conbee II" , app , sentinel .ieee , nwk )
260270 deconz .endpoints = {
261271 0 : sentinel .zdo ,
262272 1 : sentinel .ep1 ,
@@ -268,38 +278,16 @@ async def test_deconz_dev_remove_from_group(app, nwk, device_path):
268278
269279
270280def test_deconz_props (nwk , device_path ):
271- deconz = application .DeconzDevice (
272- deconz_api .FirmwareVersion (0 ), device_path , app , sentinel .ieee , nwk
273- )
281+ deconz = application .DeconzDevice ("Conbee II" , app , sentinel .ieee , nwk )
274282 assert deconz .manufacturer is not None
275283 assert deconz .model is not None
276284
277285
278- @pytest .mark .parametrize (
279- "name, firmware_version, device_path" ,
280- [
281- ("ConBee" , deconz_api .FirmwareVersion (0x00000500 ), "/dev/ttyUSB0" ),
282- ("ConBee II" , deconz_api .FirmwareVersion (0x00000700 ), "/dev/ttyUSB0" ),
283- ("RaspBee" , deconz_api .FirmwareVersion (0x00000500 ), "/dev/ttyS0" ),
284- ("RaspBee II" , deconz_api .FirmwareVersion (0x00000700 ), "/dev/ttyS0" ),
285- ("RaspBee" , deconz_api .FirmwareVersion (0x00000500 ), "/dev/ttyAMA0" ),
286- ("RaspBee II" , deconz_api .FirmwareVersion (0x00000700 ), "/dev/ttyAMA0" ),
287- ],
288- )
289- def test_deconz_name (nwk , name , firmware_version , device_path ):
290- deconz = application .DeconzDevice (
291- firmware_version , device_path , app , sentinel .ieee , nwk
292- )
293- assert deconz .model == name
294-
295-
296286async def test_deconz_new (app , nwk , device_path , monkeypatch ):
297287 mock_init = AsyncMock ()
298288 monkeypatch .setattr (zigpy .device .Device , "_initialize" , mock_init )
299289
300- deconz = await application .DeconzDevice .new (
301- app , sentinel .ieee , nwk , deconz_api .FirmwareVersion (0 ), device_path
302- )
290+ deconz = await application .DeconzDevice .new (app , sentinel .ieee , nwk , "Conbee II" )
303291 assert isinstance (deconz , application .DeconzDevice )
304292 assert mock_init .call_count == 1
305293 mock_init .reset_mock ()
@@ -311,9 +299,7 @@ async def test_deconz_new(app, nwk, device_path, monkeypatch):
311299 22 : MagicMock (),
312300 }
313301 app .devices [sentinel .ieee ] = mock_dev
314- deconz = await application .DeconzDevice .new (
315- app , sentinel .ieee , nwk , deconz_api .FirmwareVersion (0 ), device_path
316- )
302+ deconz = await application .DeconzDevice .new (app , sentinel .ieee , nwk , "Conbee II" )
317303 assert isinstance (deconz , application .DeconzDevice )
318304 assert mock_init .call_count == 0
319305
@@ -346,18 +332,21 @@ def test_tx_confirm_unexpcted(app, caplog):
346332
347333async def test_reset_watchdog (app ):
348334 """Test watchdog."""
349- with patch .object (app ._api , "write_parameter" ) as mock_api :
350- dog = asyncio .create_task (app ._reset_watchdog ())
351- await asyncio .sleep (0.3 )
352- dog .cancel ()
353- assert mock_api .call_count == 1
335+ app ._api .protocol_version = application .PROTO_VER_WATCHDOG
336+ app ._api .get_device_state = AsyncMock ()
337+ app ._api .write_parameter = AsyncMock ()
354338
355- with patch .object (app ._api , "write_parameter" ) as mock_api :
356- mock_api .side_effect = zigpy_deconz .exception .CommandError
357- dog = asyncio .create_task (app ._reset_watchdog ())
358- await asyncio .sleep (0.3 )
359- dog .cancel ()
360- assert mock_api .call_count == 1
339+ await app ._watchdog_feed ()
340+ assert len (app ._api .get_device_state .mock_calls ) == 1
341+ assert len (app ._api .write_parameter .mock_calls ) == 1
342+
343+ app ._api .protocol_version = application .PROTO_VER_WATCHDOG - 1
344+ app ._api .get_device_state .reset_mock ()
345+ app ._api .write_parameter .reset_mock ()
346+
347+ await app ._watchdog_feed ()
348+ assert len (app ._api .get_device_state .mock_calls ) == 1
349+ assert len (app ._api .write_parameter .mock_calls ) == 0
361350
362351
363352async def test_force_remove (app ):
@@ -426,11 +415,8 @@ async def test_delayed_scan():
426415 app .topology .scan .assert_called_once_with (devices = [coord ])
427416
428417
429- @patch ("zigpy_deconz.zigbee.application.CHANGE_NETWORK_WAIT" , 0.001 )
430- @pytest .mark .parametrize ("support_watchdog" , [False , True ])
431- async def test_change_network_state (app , support_watchdog ):
432- app ._reset_watchdog_task = MagicMock ()
433-
418+ @patch ("zigpy_deconz.zigbee.application.CHANGE_NETWORK_POLL_TIME" , 0.001 )
419+ async def test_change_network_state (app ):
434420 app ._api .get_device_state = AsyncMock (
435421 side_effect = [
436422 deconz_api .DeviceState (deconz_api .NetworkState .OFFLINE ),
@@ -439,25 +425,11 @@ async def test_change_network_state(app, support_watchdog):
439425 ]
440426 )
441427
442- if support_watchdog :
443- app ._api ._protocol_version = application .PROTO_VER_WATCHDOG
444- app ._api .protocol_version = application .PROTO_VER_WATCHDOG
445- else :
446- app ._api ._protocol_version = application .PROTO_VER_WATCHDOG - 1
447- app ._api .protocol_version = application .PROTO_VER_WATCHDOG - 1
448-
449- old_watchdog_task = app ._reset_watchdog_task
450- cancel_mock = app ._reset_watchdog_task .cancel = MagicMock ()
428+ app ._api ._protocol_version = application .PROTO_VER_WATCHDOG
429+ app ._api .protocol_version = application .PROTO_VER_WATCHDOG
451430
452431 await app ._change_network_state (deconz_api .NetworkState .CONNECTED , timeout = 0.01 )
453432
454- if support_watchdog :
455- assert cancel_mock .call_count == 1
456- assert app ._reset_watchdog_task is not old_watchdog_task
457- else :
458- assert cancel_mock .call_count == 0
459- assert app ._reset_watchdog_task is old_watchdog_task
460-
461433
462434ENDPOINT = zdo_t .SimpleDescriptor (
463435 endpoint = None ,
@@ -552,43 +524,14 @@ async def read_param(param_id, index):
552524 )
553525
554526
555- @patch ("zigpy_deconz.zigbee.application.asyncio.sleep" , new_callable = AsyncMock )
556- @patch (
557- "zigpy_deconz.zigbee.application.ControllerApplication.initialize" ,
558- side_effect = [RuntimeError (), None ],
559- )
560- @patch (
561- "zigpy_deconz.zigbee.application.ControllerApplication.connect" ,
562- side_effect = [RuntimeError (), None , None ],
563- )
564- async def test_reconnect (mock_connect , mock_initialize , mock_sleep , app ):
565- assert app ._reconnect_task is None
566- app .connection_lost (RuntimeError ())
567-
568- assert app ._reconnect_task is not None
569- await app ._reconnect_task
570-
571- assert mock_connect .call_count == 3
572- assert mock_initialize .call_count == 2
573-
574-
575- async def test_disconnect_during_reconnect (app ):
576- assert app ._reconnect_task is None
577- app .connection_lost (RuntimeError ())
578- await asyncio .sleep (0 )
579- await app .disconnect ()
580-
581- assert app ._reconnect_task is None
582-
583-
584527async def test_reset_network_info (app ):
585528 app .form_network = AsyncMock ()
586529 await app .reset_network_info ()
587530
588531 app .form_network .assert_called_once ()
589532
590533
591- async def test_energy_scan (app ):
534+ async def test_energy_scan_conbee_2 (app ):
592535 with mock .patch .object (
593536 zigpy .application .ControllerApplication ,
594537 "energy_scan" ,
@@ -601,6 +544,40 @@ async def test_energy_scan(app):
601544 assert results == {c : c * 3 for c in Channels .ALL_CHANNELS }
602545
603546
547+ async def test_energy_scan_conbee_3 (app ):
548+ app ._api .firmware_version = deconz_api .FirmwareVersion (0x26580900 )
549+
550+ type(app )._device = AsyncMock ()
551+
552+ app ._device .zdo .Mgmt_NWK_Update_req = AsyncMock (
553+ side_effect = zigpy .exceptions .DeliveryError ("error" )
554+ )
555+
556+ with pytest .raises (zigpy .exceptions .DeliveryError ):
557+ await app .energy_scan (channels = Channels .ALL_CHANNELS , duration_exp = 0 , count = 1 )
558+
559+ app ._device .zdo .Mgmt_NWK_Update_req = AsyncMock (
560+ side_effect = [
561+ asyncio .TimeoutError (),
562+ list (
563+ {
564+ "Status" : zdo_t .Status .SUCCESS ,
565+ "ScannedChannels" : Channels .ALL_CHANNELS ,
566+ "TotalTransmissions" : 0 ,
567+ "TransmissionFailures" : 0 ,
568+ "EnergyValues" : [i for i in range (11 , 26 + 1 )],
569+ }.values ()
570+ ),
571+ ]
572+ )
573+
574+ results = await app .energy_scan (
575+ channels = Channels .ALL_CHANNELS , duration_exp = 0 , count = 1
576+ )
577+
578+ assert results == {c : c for c in Channels .ALL_CHANNELS }
579+
580+
604581async def test_channel_migration (app ):
605582 app ._api .write_parameter = AsyncMock ()
606583 app ._change_network_state = AsyncMock ()
0 commit comments