Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Source routing fixes #626

Merged
merged 8 commits into from
Jul 23, 2024
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
4 changes: 4 additions & 0 deletions bellows/ezsp/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,3 +239,7 @@ async def send_broadcast(
data: bytes,
) -> tuple[t.sl_Status, t.uint8_t]:
raise NotImplementedError

@abc.abstractmethod
async def set_source_route(self, nwk: t.NWK, relays: list[t.NWK]) -> t.sl_Status:
raise NotImplementedError
4 changes: 4 additions & 0 deletions bellows/ezsp/v4/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,7 @@ async def send_broadcast(
)

return t.sl_Status.from_ember_status(status), sequence

async def set_source_route(self, nwk: t.NWK, relays: list[t.NWK]) -> t.sl_Status:
(res,) = await self.setSourceRoute(destination=nwk, relayList=relays)
return t.sl_Status.from_ember_status(res)
4 changes: 4 additions & 0 deletions bellows/ezsp/v9/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,7 @@ async def write_child_data(self, children: dict[t.EUI64, t.NWK]) -> None:
timeout=0,
),
)

async def set_source_route(self, nwk: t.NWK, relays: list[t.NWK]) -> t.sl_Status:
# While the command may succeed, it does absolutely nothing
return t.sl_Status.OK
21 changes: 7 additions & 14 deletions bellows/zigbee/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -671,12 +671,6 @@ async def _reset_mfg_id(self, mfg_id: int) -> None:
await asyncio.sleep(MFG_ID_RESET_DELAY)
await self._ezsp.setManufacturerCode(code=DEFAULT_MFG_ID)

async def _set_source_route(
self, nwk: zigpy.types.NWK, relays: list[zigpy.types.NWK]
) -> bool:
(res,) = await self._ezsp.setSourceRoute(destination=nwk, relayList=relays)
return t.sl_Status.from_ember_status(res) == t.sl_Status.OK

async def energy_scan(
self, channels: t.Channels, duration_exp: int, count: int
) -> dict[int, float]:
Expand Down Expand Up @@ -743,6 +737,9 @@ async def send_packet(self, packet: zigpy.types.ZigbeePacket) -> None:

if not self.config[zigpy.config.CONF_SOURCE_ROUTING]:
aps_frame.options |= t.EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY
else:
# Source routing uses address discovery to discover routes
aps_frame.options |= t.EmberApsOption.APS_OPTION_ENABLE_ADDRESS_DISCOVERY

async with self._limit_concurrency():
message_tag = self.get_sequence()
Expand All @@ -756,14 +753,10 @@ async def send_packet(self, packet: zigpy.types.ZigbeePacket) -> None:
remoteEui64=device.ieee, extendedTimeout=True
)

if (
packet.source_route is not None
and not await self._set_source_route(
packet.dst.address, packet.source_route
)
):
aps_frame.options |= (
t.EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY
if packet.source_route is not None:
await self._ezsp.set_source_route(
nwk=packet.dst.address,
relays=packet.source_route,
)

status, _ = await self._ezsp.send_unicast(
Expand Down
19 changes: 11 additions & 8 deletions tests/test_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -831,20 +831,23 @@ async def test_send_packet_unicast_ieee_no_fallback(app, packet, caplog):
assert app._ezsp.send_unicast.call_count == 0


async def test_send_packet_unicast_source_route_ezsp7(make_app, packet):
async def test_send_packet_unicast_source_route(make_app, packet):
app = make_app({zigpy.config.CONF_SOURCE_ROUTING: True})
app._ezsp.ezsp_version = 7
app._ezsp.setSourceRoute = AsyncMock(return_value=(t.EmberStatus.SUCCESS,))
app._ezsp.set_source_route = AsyncMock(return_value=(t.sl_Status.OK,))

packet.source_route = [0x0001, 0x0002]
await _test_send_packet_unicast(
app, packet, options=(t.EmberApsOption.APS_OPTION_RETRY)
app,
packet,
options=(
t.EmberApsOption.APS_OPTION_RETRY
| t.EmberApsOption.APS_OPTION_ENABLE_ADDRESS_DISCOVERY
),
)

assert app._ezsp.setSourceRoute.await_count == 1
app._ezsp.setSourceRoute.assert_called_once_with(
destination=packet.dst.address,
relayList=[0x0001, 0x0002],
app._ezsp.set_source_route.assert_called_once_with(
nwk=packet.dst.address,
relays=[0x0001, 0x0002],
)


Expand Down
11 changes: 11 additions & 0 deletions tests/test_ezsp_v4.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,3 +337,14 @@ async def test_send_broadcast(ezsp_f) -> None:
messageContents=b"hello",
)
]


async def test_source_route(ezsp_f) -> None:
ezsp_f.setSourceRoute = AsyncMock(return_value=(t.EmberStatus.SUCCESS,))

status = await ezsp_f.set_source_route(nwk=0x1234, relays=[0x5678, 0xABCD])
assert status == t.sl_Status.OK

assert ezsp_f.setSourceRoute.mock_calls == [
call(destination=0x1234, relayList=[0x5678, 0xABCD])
]
10 changes: 10 additions & 0 deletions tests/test_ezsp_v9.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,13 @@ async def test_write_child_data(ezsp_f) -> None:
),
),
]


async def test_source_route(ezsp_f) -> None:
ezsp_f.setSourceRoute = AsyncMock(return_value=(t.EmberStatus.SUCCESS,))

status = await ezsp_f.set_source_route(nwk=0x1234, relays=[0x5678, 0xABCD])
assert status == t.sl_Status.OK

# Nothing happens
assert ezsp_f.setSourceRoute.mock_calls == []
Loading