Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions tests/application/test_startup.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,3 +313,42 @@ async def test_concurrency_auto_config(device, concurrency, make_application):
await app.start_network()

assert app._concurrent_requests_semaphore.max_concurrency == concurrency


@pytest.mark.parametrize(
("code_revision", "expected"),
[
(None, False),
(20190608, False),
(20221226, False),
(20230506, False),
(20230507, True),
(20250321, True),
],
)
async def test_child_aging_by_build(code_revision, expected, make_application):
app, znp_server = make_application(FormedLaunchpadCC26X2R1)

znp_server.reply_to(
c.SYS.Version.Req(),
responses=c.SYS.Version.Rsp(
TransportRev=2,
ProductId=1,
MajorRel=2,
MinorRel=7,
MaintRel=1,
CodeRevision=code_revision,
BootloaderBuildType=None,
BootloaderRevision=None,
),
override=True,
)

await app.startup(auto_form=False)

value = await app._znp.nvram.osal_read(
OsalNvIds.NWK_CHILD_AGE_ENABLE, item_type=t.Bool
)
assert value == expected

await app.shutdown()
23 changes: 16 additions & 7 deletions zigpy_znp/zigbee/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
DATA_CONFIRM_TIMEOUT = 8
EXTENDED_DATA_CONFIRM_TIMEOUT = 30
DEVICE_JOIN_MAX_DELAY = 5
# First Koenkk Z-Stack 3.x.0 build with child aging enabled (and Xiaomi exempted)
CHILD_AGING_MIN_BUILD_ID = 20230507

LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -139,6 +141,9 @@ async def start_network(self, *, read_only=False):
if self.state.node_info == zigpy.state.NodeInfo():
await self.load_network_info()

# Needed by the stack settings below
self._version_rsp = await self._znp.request(c.SYS.Version.Req())

if not read_only:
await self._znp.migrate_nvram()
await self._write_stack_settings()
Expand All @@ -150,8 +155,6 @@ async def start_network(self, *, read_only=False):

await self._znp.start_network()

self._version_rsp = await self._znp.request(c.SYS.Version.Req())

# The CC2531 running Z-Stack Home 1.2 overrides the LED setting if it is changed
# before the coordinator has started.
if self.znp_config[conf.CONF_LED_MODE] is not None:
Expand Down Expand Up @@ -624,7 +627,13 @@ async def _write_stack_settings(self) -> bool:
OsalNvIds.CONCENTRATOR_DISCOVERY: t.uint8_t(120),
OsalNvIds.CONCENTRATOR_RC: t.Bool(True),
OsalNvIds.SRC_RTG_EXPIRY_TIME: t.uint8_t(255),
OsalNvIds.NWK_CHILD_AGE_ENABLE: t.Bool(False),
# Koenkk's builds enable child aging from 20230507 on and skip the leave
# for Xiaomi devices, which do not implement it. Older builds have no such
# exception, so aging stays off there. The value is written either way
# because an earlier zigpy-znp may have left `False` in NVRAM.
OsalNvIds.NWK_CHILD_AGE_ENABLE: t.Bool(
self._zstack_build_id >= CHILD_AGING_MIN_BUILD_ID
),
# Default is 20 in Z-Stack 3.0.1, 30 in Z-Stack 3/4
OsalNvIds.BCAST_DELIVERY_TIME: t.uint8_t(30),
# We want to receive all ZDO callbacks to proxy them back to zigpy
Expand Down Expand Up @@ -936,10 +945,10 @@ async def send_packet(self, packet: zigpy.types.ZigbeePacket) -> None:
except InvalidCommandResponse as e:
last_error = e

# Child aging is disabled so if a child switches parents from
# the coordinator to another router, we will not be able to
# re-discover a route to it. We have to manually drop the child
# to do this.
# Child aging is off on older builds, so if a child switches
# parents from the coordinator to another router, we will not be
# able to re-discover a route to it. We have to manually drop
# the child to do this.
if (
e.response.Status == t.Status.MAC_TRANSACTION_EXPIRED
and device is not None
Expand Down
Loading