Skip to content

Commit 5f5e1dd

Browse files
authored
Update write_ieee to use the new bellows API (#87)
* Update `write_ieee` to use the new bellows API * Try probing EZSP first * Adjust logging * Add a unit test * Adjust README to include `--force` flag * Re-run pre-commit
1 parent 98fbe9f commit 5f5e1dd

File tree

4 files changed

+67
-21
lines changed

4 files changed

+67
-21
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,6 @@ $ universal-silabs-flasher \
7474
The IEEE address can also be specified without colons: `--ieee 003c84fffe92bb2c`.
7575

7676
If the current device's IEEE address already matches the provided one, the command will not write it unnecessarily.
77+
Depending on firmware version, writing the IEEE address can be a **permanent** operation. If this is the case,
78+
you will need to upgrade the firmware on your adapter to a more recent release of EmberZNet or perform the one-time
79+
write with `--force`.

tests/test_flasher.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import asyncio
2+
from unittest.mock import call, patch
3+
4+
import zigpy.types as t
5+
6+
from universal_silabs_flasher.common import Version
7+
from universal_silabs_flasher.flasher import Flasher, ProbeResult
8+
9+
10+
async def test_write_emberznet_eui64():
11+
flasher = Flasher(device="/dev/ttyMOCK")
12+
13+
with (
14+
patch.object(
15+
flasher, "probe_gecko_bootloader", side_effect=asyncio.TimeoutError
16+
),
17+
patch.object(
18+
flasher,
19+
"probe_ezsp",
20+
return_value=ProbeResult(
21+
version=Version("7.4.4.0 build 0"),
22+
continue_probing=False,
23+
baudrate=115200,
24+
),
25+
),
26+
patch.object(flasher, "_connect_ezsp") as mock_connect_ezsp,
27+
):
28+
ezsp = mock_connect_ezsp.return_value.__aenter__.return_value
29+
30+
ezsp.getEui64.return_value = (t.EUI64.convert("00:11:22:33:44:55:66:77"),)
31+
ezsp.write_custom_eui64.return_value = None
32+
33+
await flasher.write_emberznet_eui64(
34+
new_ieee=t.EUI64.convert("11:22:33:44:55:66:77:88"), force=True
35+
)
36+
37+
assert ezsp.write_custom_eui64.mock_calls == [
38+
call(ieee=t.EUI64.convert("11:22:33:44:55:66:77:88"), burn_into_userdata=True)
39+
]

universal_silabs_flasher/flash.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import typing
1212
import urllib.parse
1313

14-
import bellows.types
1514
import click
1615
import coloredlogs
1716
import zigpy.ota.validators
@@ -248,12 +247,11 @@ async def probe(ctx: click.Context) -> None:
248247
@main.command()
249248
@click.pass_context
250249
@click.option("--ieee", required=True, type=zigpy.types.EUI64.convert)
250+
@click.option("--force", default=False, type=bool)
251251
@click_coroutine
252-
async def write_ieee(ctx: click.Context, ieee: zigpy.types.EUI64) -> None:
253-
new_eui64 = bellows.types.EmberEUI64(ieee)
254-
252+
async def write_ieee(ctx: click.Context, ieee: zigpy.types.EUI64, force: bool) -> None:
255253
try:
256-
await ctx.obj["flasher"].write_emberznet_eui64(new_eui64)
254+
await ctx.obj["flasher"].write_emberznet_eui64(ieee, force=force)
257255
except (ValueError, RuntimeError) as e:
258256
raise click.ClickException(str(e)) from e
259257

universal_silabs_flasher/flasher.py

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import bellows.ezsp
1010
import bellows.types
1111
from zigpy.serial import SerialProtocol
12+
import zigpy.types
1213

1314
from .common import (
1415
PROBE_TIMEOUT,
@@ -162,10 +163,18 @@ async def probe_spinel(self, baudrate: int) -> ProbeResult:
162163
async def probe_app_type(
163164
self,
164165
types: typing.Iterable[ApplicationType] | None = None,
166+
try_first: tuple[ApplicationType, ...] = (),
165167
) -> None:
166168
if types is None:
167169
types = self._probe_methods
168170

171+
# fmt: off
172+
types = (
173+
[m for m in types if m in try_first]
174+
+ [m for m in types if m not in try_first]
175+
)
176+
# fmt: on
177+
169178
# Reset into bootloader
170179
if self._reset_target:
171180
await self.enter_bootloader_reset(self._reset_target)
@@ -205,6 +214,8 @@ async def probe_app_type(
205214
except asyncio.TimeoutError:
206215
continue
207216

217+
_LOGGER.debug("Probe result: %s", result)
218+
208219
# Keep track of the bootloader version for later
209220
if probe_method == ApplicationType.GECKO_BOOTLOADER:
210221
_LOGGER.info("Detected bootloader version %s", result.version)
@@ -307,30 +318,25 @@ async def dump_emberznet_config(self) -> None:
307318
continue
308319
print(f"{config.name}={v[1]}")
309320

310-
async def write_emberznet_eui64(self, new_eui64: bellows.types.EUI64) -> bool:
311-
await self.probe_app_type()
321+
async def write_emberznet_eui64(
322+
self, new_ieee: zigpy.types.EUI64, force: bool = False
323+
) -> bool:
324+
await self.probe_app_type(
325+
try_first=[ApplicationType.GECKO_BOOTLOADER, ApplicationType.EZSP]
326+
)
312327

313328
if self.app_type != ApplicationType.EZSP:
314329
raise RuntimeError(f"Device is not running EmberZNet: {self.app_type}")
315330

316331
async with self._connect_ezsp(self.app_baudrate) as ezsp:
317-
(current_eui64,) = await ezsp.getEui64()
318-
_LOGGER.info("Current device IEEE: %s", current_eui64)
332+
(current_ieee,) = await ezsp.getEui64()
333+
_LOGGER.info("Current device IEEE: %s", current_ieee)
319334

320-
if current_eui64 == new_eui64:
335+
if current_ieee == new_ieee:
321336
_LOGGER.info("Device IEEE address already matches, not overwriting")
322337
return False
323338

324-
if not await ezsp.can_write_custom_eui64():
325-
raise ValueError(
326-
"IEEE address has already been written, it cannot be written again"
327-
)
328-
329-
(status,) = await ezsp.setMfgToken(
330-
bellows.types.EzspMfgTokenId.MFG_CUSTOM_EUI_64, new_eui64.serialize()
331-
)
332-
333-
if status != bellows.types.EmberStatus.SUCCESS:
334-
raise RuntimeError(f"Failed to write IEEE address: {status}")
339+
await ezsp.write_custom_eui64(ieee=new_ieee, burn_into_userdata=force)
340+
_LOGGER.info("Wrote new device IEEE: %s", new_ieee)
335341

336342
return True

0 commit comments

Comments
 (0)