zigpy-deconz 0.25.2__tar.gz → 0.25.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {zigpy_deconz-0.25.2/zigpy_deconz.egg-info → zigpy_deconz-0.25.4}/PKG-INFO +1 -1
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/tests/test_application.py +12 -2
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/tests/test_network_state.py +111 -6
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/zigpy_deconz/zigbee/application.py +55 -10
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4/zigpy_deconz.egg-info}/PKG-INFO +1 -1
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/COPYING +0 -0
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/LICENSE +0 -0
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/README.md +0 -0
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/pyproject.toml +0 -0
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/setup.cfg +0 -0
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/setup.py +0 -0
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/tests/test_api.py +0 -0
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/tests/test_exception.py +0 -0
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/tests/test_send_receive.py +0 -0
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/tests/test_types.py +0 -0
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/tests/test_uart.py +0 -0
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/tests/test_utils.py +0 -0
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/zigpy_deconz/__init__.py +0 -0
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/zigpy_deconz/api.py +0 -0
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/zigpy_deconz/config.py +0 -0
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/zigpy_deconz/exception.py +0 -0
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/zigpy_deconz/types.py +0 -0
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/zigpy_deconz/uart.py +0 -0
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/zigpy_deconz/utils.py +0 -0
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/zigpy_deconz/zigbee/__init__.py +0 -0
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/zigpy_deconz.egg-info/SOURCES.txt +0 -0
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/zigpy_deconz.egg-info/dependency_links.txt +0 -0
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/zigpy_deconz.egg-info/requires.txt +0 -0
- {zigpy_deconz-0.25.2 → zigpy_deconz-0.25.4}/zigpy_deconz.egg-info/top_level.txt +0 -0
|
@@ -553,10 +553,20 @@ async def test_add_endpoint_no_unnecessary_writes(app):
|
|
|
553
553
|
|
|
554
554
|
|
|
555
555
|
async def test_reset_network_info(app):
|
|
556
|
-
app.
|
|
556
|
+
app.write_network_info = AsyncMock()
|
|
557
557
|
await app.reset_network_info()
|
|
558
558
|
|
|
559
|
-
app.
|
|
559
|
+
assert len(app.write_network_info.mock_calls) == 1
|
|
560
|
+
network_info = app.write_network_info.mock_calls[0].kwargs["network_info"]
|
|
561
|
+
node_info = app.write_network_info.mock_calls[0].kwargs["node_info"]
|
|
562
|
+
|
|
563
|
+
# Verify invalid network settings are written
|
|
564
|
+
assert network_info.pan_id == 0xFFFF
|
|
565
|
+
assert network_info.extended_pan_id == zigpy.types.EUI64.convert(
|
|
566
|
+
"FF:FF:FF:FF:FF:FF:FF:FF"
|
|
567
|
+
)
|
|
568
|
+
assert network_info.channel is None
|
|
569
|
+
assert node_info.nwk == 0xFFFF
|
|
560
570
|
|
|
561
571
|
|
|
562
572
|
async def test_energy_scan_conbee_2(app):
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"""Test `load_network_info` and `write_network_info` methods."""
|
|
2
2
|
|
|
3
3
|
import importlib.metadata
|
|
4
|
+
from unittest.mock import ANY, call
|
|
4
5
|
|
|
5
6
|
import pytest
|
|
6
|
-
from zigpy.exceptions import
|
|
7
|
+
from zigpy.exceptions import CannotWriteNetworkSettings, NetworkNotFormed
|
|
7
8
|
import zigpy.state as app_state
|
|
8
9
|
import zigpy.types as t
|
|
9
10
|
import zigpy.zdo.types as zdo_t
|
|
@@ -11,6 +12,7 @@ import zigpy.zdo.types as zdo_t
|
|
|
11
12
|
import zigpy_deconz
|
|
12
13
|
import zigpy_deconz.api
|
|
13
14
|
import zigpy_deconz.exception
|
|
15
|
+
import zigpy_deconz.types
|
|
14
16
|
import zigpy_deconz.zigbee.application as application
|
|
15
17
|
|
|
16
18
|
from tests.async_mock import AsyncMock, patch
|
|
@@ -70,28 +72,55 @@ def network_info(node_info):
|
|
|
70
72
|
@patch.object(application, "CHANGE_NETWORK_POLL_TIME", 0.001)
|
|
71
73
|
@patch.object(application, "CHANGE_NETWORK_STATE_DELAY", 0.001)
|
|
72
74
|
@pytest.mark.parametrize(
|
|
73
|
-
|
|
75
|
+
(
|
|
76
|
+
"channel_mask",
|
|
77
|
+
"channel",
|
|
78
|
+
"security_level",
|
|
79
|
+
"fw_supports_fc",
|
|
80
|
+
"logical_type",
|
|
81
|
+
"tx_counter",
|
|
82
|
+
"should_error",
|
|
83
|
+
),
|
|
74
84
|
[
|
|
85
|
+
# FW supports frame counter
|
|
75
86
|
(
|
|
76
87
|
t.Channels.from_channel_list([15]),
|
|
77
88
|
15,
|
|
78
89
|
0,
|
|
79
90
|
True,
|
|
80
91
|
zdo_t.LogicalType.Coordinator,
|
|
92
|
+
39009277,
|
|
93
|
+
False,
|
|
94
|
+
),
|
|
95
|
+
# FW doesn't support but we're writing blank settings (tx_counter == 0)
|
|
96
|
+
(
|
|
97
|
+
t.Channels.from_channel_list([15]),
|
|
98
|
+
15,
|
|
99
|
+
5,
|
|
100
|
+
False,
|
|
101
|
+
zdo_t.LogicalType.Coordinator,
|
|
102
|
+
0,
|
|
103
|
+
False,
|
|
81
104
|
),
|
|
105
|
+
# FW doesn't support and we're writing real settings (should error)
|
|
82
106
|
(
|
|
83
107
|
t.Channels.from_channel_list([15]),
|
|
84
108
|
15,
|
|
85
109
|
0,
|
|
86
110
|
False,
|
|
87
111
|
zdo_t.LogicalType.Coordinator,
|
|
112
|
+
39009277,
|
|
113
|
+
True,
|
|
88
114
|
),
|
|
115
|
+
# Additional test cases with FW support
|
|
89
116
|
(
|
|
90
117
|
t.Channels.from_channel_list([15, 20]),
|
|
91
118
|
15,
|
|
92
119
|
5,
|
|
93
120
|
True,
|
|
94
121
|
zdo_t.LogicalType.Coordinator,
|
|
122
|
+
39009277,
|
|
123
|
+
False,
|
|
95
124
|
),
|
|
96
125
|
(
|
|
97
126
|
t.Channels.from_channel_list([15, 20, 25]),
|
|
@@ -99,8 +128,18 @@ def network_info(node_info):
|
|
|
99
128
|
5,
|
|
100
129
|
True,
|
|
101
130
|
zdo_t.LogicalType.Router,
|
|
131
|
+
39009277,
|
|
132
|
+
False,
|
|
133
|
+
),
|
|
134
|
+
(
|
|
135
|
+
None,
|
|
136
|
+
15,
|
|
137
|
+
5,
|
|
138
|
+
True,
|
|
139
|
+
zdo_t.LogicalType.Coordinator,
|
|
140
|
+
39009277,
|
|
141
|
+
False,
|
|
102
142
|
),
|
|
103
|
-
(None, 15, 5, True, zdo_t.LogicalType.Coordinator),
|
|
104
143
|
],
|
|
105
144
|
)
|
|
106
145
|
async def test_write_network_info(
|
|
@@ -112,6 +151,8 @@ async def test_write_network_info(
|
|
|
112
151
|
security_level,
|
|
113
152
|
fw_supports_fc,
|
|
114
153
|
logical_type,
|
|
154
|
+
tx_counter,
|
|
155
|
+
should_error,
|
|
115
156
|
):
|
|
116
157
|
"""Test that network info is correctly written."""
|
|
117
158
|
|
|
@@ -137,13 +178,14 @@ async def test_write_network_info(
|
|
|
137
178
|
channel=channel,
|
|
138
179
|
channel_mask=channel_mask,
|
|
139
180
|
security_level=security_level,
|
|
181
|
+
network_key=network_info.network_key.replace(tx_counter=tx_counter),
|
|
140
182
|
)
|
|
141
183
|
|
|
142
184
|
node_info = node_info.replace(logical_type=logical_type)
|
|
143
185
|
|
|
144
|
-
if
|
|
186
|
+
if should_error:
|
|
145
187
|
with pytest.raises(
|
|
146
|
-
|
|
188
|
+
CannotWriteNetworkSettings,
|
|
147
189
|
match=(
|
|
148
190
|
"Please upgrade your adapter firmware. Firmware version 0x26580700 does"
|
|
149
191
|
" not support writing the network key frame counter, which is required"
|
|
@@ -166,7 +208,9 @@ async def test_write_network_info(
|
|
|
166
208
|
for call in app._api.write_parameter.await_args_list
|
|
167
209
|
}
|
|
168
210
|
|
|
169
|
-
|
|
211
|
+
# Only check frame counter if firmware supports it
|
|
212
|
+
if fw_supports_fc:
|
|
213
|
+
assert params["nwk_frame_counter"] == (network_info.network_key.tx_counter,)
|
|
170
214
|
|
|
171
215
|
if node_info.logical_type == zdo_t.LogicalType.Coordinator:
|
|
172
216
|
assert params["aps_designed_coordinator"] == (1,)
|
|
@@ -328,3 +372,64 @@ async def test_load_network_info(
|
|
|
328
372
|
assert app.state.network_info == network_info
|
|
329
373
|
|
|
330
374
|
assert app.state.node_info == node_info.replace(**node_state_changes)
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
@patch.object(application, "CHANGE_NETWORK_POLL_TIME", 0.001)
|
|
378
|
+
@patch.object(application, "CHANGE_NETWORK_STATE_DELAY", 0.001)
|
|
379
|
+
async def test_form_network_fast_without_frame_counter_support(app): # noqa: F811
|
|
380
|
+
"""Test that form_network(fast=True) works when FW doesn't support frame counter."""
|
|
381
|
+
|
|
382
|
+
async def write_parameter(param, *args):
|
|
383
|
+
if param == zigpy_deconz.api.NetworkParameter.nwk_frame_counter:
|
|
384
|
+
raise zigpy_deconz.exception.CommandError(
|
|
385
|
+
"Command is unsupported",
|
|
386
|
+
status=zigpy_deconz.api.Status.UNSUPPORTED,
|
|
387
|
+
command=None,
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
app._change_network_state = AsyncMock()
|
|
391
|
+
app._api.write_parameter = AsyncMock(side_effect=write_parameter)
|
|
392
|
+
app.backups = AsyncMock()
|
|
393
|
+
app.backups.restore_backup = AsyncMock()
|
|
394
|
+
|
|
395
|
+
# This should not raise an error because fast=True sets form_quickly
|
|
396
|
+
await app.form_network(fast=True)
|
|
397
|
+
|
|
398
|
+
# Verify that restore_backup was called with create_new=False (due to fast=True)
|
|
399
|
+
assert app.backups.restore_backup.mock_calls == [
|
|
400
|
+
call(backup=ANY, counter_increment=0, allow_incomplete=True, create_new=False)
|
|
401
|
+
]
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
@patch.object(application, "CHANGE_NETWORK_POLL_TIME", 0.001)
|
|
405
|
+
@patch.object(application, "CHANGE_NETWORK_STATE_DELAY", 0.001)
|
|
406
|
+
async def test_reset_network_info_without_frame_counter_support(app): # noqa: F811
|
|
407
|
+
"""Test that reset_network_info works even when FW doesn't support frame counter."""
|
|
408
|
+
|
|
409
|
+
async def write_parameter(param, *args):
|
|
410
|
+
if param == zigpy_deconz.api.NetworkParameter.nwk_frame_counter:
|
|
411
|
+
raise zigpy_deconz.exception.CommandError(
|
|
412
|
+
"Command is unsupported",
|
|
413
|
+
status=zigpy_deconz.api.Status.UNSUPPORTED,
|
|
414
|
+
command=None,
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
app._change_network_state = AsyncMock()
|
|
418
|
+
app._api.write_parameter = AsyncMock(side_effect=write_parameter)
|
|
419
|
+
app._api.read_parameter = AsyncMock(
|
|
420
|
+
return_value=t.EUI64.convert("00:11:22:33:44:55:66:77")
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
# Should not raise an error despite frame counter not being supported
|
|
424
|
+
await app.reset_network_info()
|
|
425
|
+
|
|
426
|
+
# Verify that write_parameter was called (including the frame counter attempt)
|
|
427
|
+
assert any(
|
|
428
|
+
mock_call.args[0] == zigpy_deconz.api.NetworkParameter.nwk_frame_counter
|
|
429
|
+
for mock_call in app._api.write_parameter.mock_calls
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
# Verify network state changes were awaited
|
|
433
|
+
assert app._change_network_state.mock_calls == [
|
|
434
|
+
call(zigpy_deconz.api.NetworkState.OFFLINE)
|
|
435
|
+
]
|
|
@@ -180,7 +180,29 @@ class ControllerApplication(zigpy.application.ControllerApplication):
|
|
|
180
180
|
|
|
181
181
|
async def reset_network_info(self):
|
|
182
182
|
# TODO: There does not appear to be a way to factory reset a Conbee
|
|
183
|
-
await self.
|
|
183
|
+
await self.write_network_info(
|
|
184
|
+
network_info=zigpy.state.NetworkInfo(
|
|
185
|
+
pan_id=0xFFFF,
|
|
186
|
+
extended_pan_id=zigpy.types.EUI64.convert("FF:FF:FF:FF:FF:FF:FF:FF"),
|
|
187
|
+
channel=None,
|
|
188
|
+
channel_mask=zigpy.types.Channels(0),
|
|
189
|
+
nwk_update_id=0,
|
|
190
|
+
network_key=zigpy.state.Key(
|
|
191
|
+
key=zigpy.types.KeyData.convert(
|
|
192
|
+
"FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF"
|
|
193
|
+
)
|
|
194
|
+
),
|
|
195
|
+
tc_link_key=zigpy.state.Key(
|
|
196
|
+
key=zigpy.types.KeyData.convert(b"ZigBeeAlliance09".hex())
|
|
197
|
+
),
|
|
198
|
+
security_level=0x05,
|
|
199
|
+
),
|
|
200
|
+
node_info=zigpy.state.NodeInfo(
|
|
201
|
+
logical_type=zdo_t.LogicalType.Coordinator,
|
|
202
|
+
ieee=zigpy.types.EUI64.UNKNOWN,
|
|
203
|
+
nwk=0xFFFF,
|
|
204
|
+
),
|
|
205
|
+
)
|
|
184
206
|
|
|
185
207
|
async def write_network_info(self, *, network_info, node_info):
|
|
186
208
|
try:
|
|
@@ -189,12 +211,19 @@ class ControllerApplication(zigpy.application.ControllerApplication):
|
|
|
189
211
|
)
|
|
190
212
|
except zigpy_deconz.exception.CommandError as ex:
|
|
191
213
|
assert ex.status == Status.UNSUPPORTED
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
214
|
+
|
|
215
|
+
# If we are resetting the adapter or forming a brand new network, we can
|
|
216
|
+
# skip this check
|
|
217
|
+
if not (
|
|
218
|
+
network_info.stack_specific.get("form_quickly", False)
|
|
219
|
+
or network_info.network_key.tx_counter == 0
|
|
220
|
+
):
|
|
221
|
+
fw_version = f"{int(self._api.firmware_version):#010x}"
|
|
222
|
+
raise zigpy.exceptions.CannotWriteNetworkSettings(
|
|
223
|
+
f"Please upgrade your adapter firmware. Firmware version"
|
|
224
|
+
f" {fw_version} does not support writing the network key frame"
|
|
225
|
+
f" counter, which is required for migration to succeed."
|
|
226
|
+
)
|
|
198
227
|
|
|
199
228
|
if node_info.logical_type == zdo_t.LogicalType.Coordinator:
|
|
200
229
|
await self._api.write_parameter(
|
|
@@ -284,6 +313,14 @@ class ControllerApplication(zigpy.application.ControllerApplication):
|
|
|
284
313
|
# Note: Changed network configuration parameters become only affective after
|
|
285
314
|
# sending a Leave Network Request followed by a Create or Join Network Request
|
|
286
315
|
await self._change_network_state(NetworkState.OFFLINE)
|
|
316
|
+
|
|
317
|
+
if (
|
|
318
|
+
network_info.pan_id == 0xFFFF
|
|
319
|
+
or network_info.channel_mask == zigpy.types.Channels(0)
|
|
320
|
+
):
|
|
321
|
+
# Network is being reset, it will never enter the CONNECTED state
|
|
322
|
+
return
|
|
323
|
+
|
|
287
324
|
await asyncio.sleep(CHANGE_NETWORK_STATE_DELAY)
|
|
288
325
|
await self._change_network_state(NetworkState.CONNECTED)
|
|
289
326
|
|
|
@@ -334,7 +371,7 @@ class ControllerApplication(zigpy.application.ControllerApplication):
|
|
|
334
371
|
NetworkParameter.aps_extended_panid
|
|
335
372
|
)
|
|
336
373
|
|
|
337
|
-
if network_info.extended_pan_id == zigpy.types.
|
|
374
|
+
if network_info.extended_pan_id == zigpy.types.ExtendedPanId.convert(
|
|
338
375
|
"00:00:00:00:00:00:00:00"
|
|
339
376
|
):
|
|
340
377
|
network_info.extended_pan_id = await self._api.read_parameter(
|
|
@@ -351,8 +388,16 @@ class ControllerApplication(zigpy.application.ControllerApplication):
|
|
|
351
388
|
NetworkParameter.nwk_update_id
|
|
352
389
|
)
|
|
353
390
|
|
|
354
|
-
if
|
|
355
|
-
|
|
391
|
+
if (
|
|
392
|
+
node_info.nwk == 0xFFFF
|
|
393
|
+
or network_info.pan_id == 0xFFFF
|
|
394
|
+
or (
|
|
395
|
+
network_info.extended_pan_id
|
|
396
|
+
== zigpy.types.ExtendedPanId.convert("FF:FF:FF:FF:FF:FF:FF:FF")
|
|
397
|
+
)
|
|
398
|
+
or network_info.channel == 0
|
|
399
|
+
):
|
|
400
|
+
raise NetworkNotFormed("Network is not formed")
|
|
356
401
|
|
|
357
402
|
indexed_key = await self._api.read_parameter(NetworkParameter.network_key, 0)
|
|
358
403
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|