Skip to content

Commit

Permalink
Include a can_write_custom_eui64 stack-specific key in the backup (#…
Browse files Browse the repository at this point in the history
…469)

* Include a `can_write_custom_eui64` stack-specific key in the backup

* Fix failing unit tests
  • Loading branch information
puddly authored Jul 24, 2022
1 parent d73b2f0 commit 7dc68b0
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 1 deletion.
6 changes: 6 additions & 0 deletions bellows/ezsp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,12 @@ async def get_board_info(self) -> Tuple[str, str, str]:
version = "unknown stack version"
return tokens[0], tokens[1], version

async def can_write_custom_eui64(self) -> bool:
"""Checks if the write-once custom EUI64 token has been written."""
(custom_eui_64,) = await self.getMfgToken(t.EzspMfgTokenId.MFG_CUSTOM_EUI_64)

return custom_eui_64 == b"\xFF" * 8

def add_callback(self, cb):
id_ = hash(cb)
while id_ in self._callbacks:
Expand Down
10 changes: 9 additions & 1 deletion bellows/zigbee/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ async def load_network_info(self, *, load_devices=False) -> None:
tc_link_key.partner_ieee = self.state.node_info.ieee

brd_manuf, brd_name, version = await self._get_board_info()
can_write_custom_eui64 = await ezsp.can_write_custom_eui64()

self.state.network_info = zigpy.state.NetworkInfo(
source=f"bellows@{bellows.__version__}",
Expand All @@ -285,6 +286,7 @@ async def load_network_info(self, *, load_devices=False) -> None:
"board": brd_name,
"version": version,
"stack_version": ezsp.ezsp_version,
"can_write_custom_eui64": can_write_custom_eui64,
}
},
)
Expand Down Expand Up @@ -351,6 +353,7 @@ async def write_network_info(
except bellows.exception.EzspError:
pass

can_write_custom_eui64 = await ezsp.can_write_custom_eui64()
stack_specific = network_info.stack_specific.get("ezsp", {})
(current_eui64,) = await ezsp.getEui64()

Expand All @@ -362,7 +365,12 @@ async def write_network_info(
"i_understand_i_can_update_eui64_only_once_and_i_still_want_to_do_it"
)

if should_update_eui64:
if should_update_eui64 and not can_write_custom_eui64:
LOGGER.error(
"Current node's IEEE address has already been written once. It"
" cannot be written again without fully erasing the chip with JTAG."
)
elif should_update_eui64:
new_ncp_eui64 = t.EmberEUI64(node_info.ieee)
(status,) = await ezsp.setMfgToken(
t.EzspMfgTokenId.MFG_CUSTOM_EUI_64, new_ncp_eui64.serialize()
Expand Down
1 change: 1 addition & 0 deletions tests/test_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ async def mock_leave(*args, **kwargs):
ezsp_mock.setConfigurationValue = AsyncMock(return_value=t.EmberStatus.SUCCESS)
ezsp_mock.networkInit = AsyncMock(return_value=[init])
ezsp_mock.getNetworkParameters = AsyncMock(return_value=[0, nwk_type, nwk_params])
ezsp_mock.can_write_custom_eui64 = AsyncMock(return_value=True)

if board_info:
ezsp_mock.get_board_info = AsyncMock(
Expand Down
32 changes: 32 additions & 0 deletions tests/test_application_network_state.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import logging

import pytest
import zigpy.state
import zigpy.types as zigpy_t
Expand Down Expand Up @@ -79,6 +81,7 @@ def _mock_app_for_load(app):
ezsp = app._ezsp

app._ensure_network_running = AsyncMock()
ezsp.can_write_custom_eui64 = AsyncMock(return_value=True)
ezsp.getNetworkParameters = AsyncMock(
return_value=[
t.EmberStatus.SUCCESS,
Expand Down Expand Up @@ -364,6 +367,7 @@ def _mock_app_for_write(app, network_info, node_info, ezsp_ver=None):
ezsp.formNetwork = AsyncMock(return_value=[t.EmberStatus.SUCCESS])
ezsp.setValue = AsyncMock(return_value=[t.EmberStatus.SUCCESS])
ezsp.setMfgToken = AsyncMock(return_value=[t.EmberStatus.SUCCESS])
ezsp.can_write_custom_eui64 = AsyncMock(return_value=True)


async def test_write_network_info_failed_leave1(app, network_info, node_info):
Expand Down Expand Up @@ -415,6 +419,34 @@ async def test_write_network_info_write_new_eui64(app, network_info, node_info):
)


async def test_write_network_info_write_new_eui64_failure(
caplog, app, network_info, node_info
):
_mock_app_for_write(app, network_info, node_info)

app._ezsp.can_write_custom_eui64.return_value = False

# Differs from what is in `node_info`
app._ezsp.getEui64.return_value = [t.EmberEUI64.convert("AA:AA:AA:AA:AA:AA:AA:AA")]

await app.write_network_info(
network_info=network_info.replace(
stack_specific={
"ezsp": {
"i_understand_i_can_update_eui64_only_once_and_i_still_want_to_do_it": True,
**network_info.stack_specific["ezsp"],
}
}
),
node_info=node_info,
)

assert "cannot be written" in caplog.text

# The EUI64 is not written
app._ezsp.setMfgToken.assert_not_called()


async def test_write_network_info_dont_write_new_eui64(app, network_info, node_info):
_mock_app_for_write(app, network_info, node_info)

Expand Down
13 changes: 13 additions & 0 deletions tests/test_ezsp.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,3 +436,16 @@ async def _mock_cmd(*args, **kwargs):
cmd_mock.side_effect = _mock_cmd
(status,) = await ezsp_f.leaveNetwork(timeout=0.01)
assert status == t.EmberStatus.NETWORK_DOWN


@pytest.mark.parametrize(
"value, expected_result",
[(b"\xFF" * 8, True), (bytes.fromhex("0846b8a11c004b1200"), False)],
)
async def test_can_write_custom_eui64(ezsp_f, value, expected_result):
ezsp_f.getMfgToken = AsyncMock(return_value=[value])

result = await ezsp_f.can_write_custom_eui64()
assert result == expected_result

ezsp_f.getMfgToken.assert_called_once_with(t.EzspMfgTokenId.MFG_CUSTOM_EUI_64)

0 comments on commit 7dc68b0

Please sign in to comment.