Skip to content
Draft
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
12 changes: 12 additions & 0 deletions bellows/ezsp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -854,3 +854,15 @@ async def xncp_get_tx_power_info(self, country_code: str) -> GetTxPowerInfoRsp:
"""Get maximum and recommended TX power for a country (ISO 3166-1 alpha-2)."""
code = country_code.upper().encode("ascii")
return await self.send_xncp_frame(xncp.GetTxPowerInfoReq(country_code=code))

async def xncp_set_led_state(
self, red: t.uint8_t, green: t.uint8_t, blue: t.uint8_t
) -> None:
"""Set the adapter LED color."""
await self.send_xncp_frame(
xncp.SetLedStateReq(
red=red,
green=green,
blue=blue,
)
)
17 changes: 17 additions & 0 deletions bellows/ezsp/xncp.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class XncpCommandId(t.enum16):
SET_ROUTE_TABLE_ENTRY_REQ = 0x0006
GET_ROUTE_TABLE_ENTRY_REQ = 0x0007
GET_TX_POWER_INFO_REQ = 0x0008
SET_LED_STATE_REQ = 0x0F00

GET_SUPPORTED_FEATURES_RSP = GET_SUPPORTED_FEATURES_REQ | 0x8000
SET_SOURCE_ROUTE_RSP = SET_SOURCE_ROUTE_REQ | 0x8000
Expand All @@ -53,6 +54,7 @@ class XncpCommandId(t.enum16):
SET_ROUTE_TABLE_ENTRY_RSP = SET_ROUTE_TABLE_ENTRY_REQ | 0x8000
GET_ROUTE_TABLE_ENTRY_RSP = GET_ROUTE_TABLE_ENTRY_REQ | 0x8000
GET_TX_POWER_INFO_RSP = GET_TX_POWER_INFO_REQ | 0x8000
SET_LED_STATE_RSP = SET_LED_STATE_REQ | 0x8000

UNKNOWN = 0xFFFF

Expand Down Expand Up @@ -123,6 +125,9 @@ class FirmwareFeatures(t.bitmap32):
# Recommended and maximum TX power can be queried by country code
TX_POWER_INFO = 1 << 7

# The firmware exposes adapter LED control
LED_CONTROL = 1 << 31


class XncpCommandPayload(t.Struct):
pass
Expand Down Expand Up @@ -233,6 +238,18 @@ class GetTxPowerInfoRsp(XncpCommandPayload):
max_power_dbm: t.int8s


@register_command(XncpCommandId.SET_LED_STATE_REQ)
class SetLedStateReq(XncpCommandPayload):
red: t.uint8_t
green: t.uint8_t
blue: t.uint8_t


@register_command(XncpCommandId.SET_LED_STATE_RSP)
class SetLedStateRsp(XncpCommandPayload):
pass


@register_command(XncpCommandId.UNKNOWN)
class Unknown(XncpCommandPayload):
pass
20 changes: 20 additions & 0 deletions tests/test_xncp.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,3 +314,23 @@ async def test_xncp_get_tx_power_info(ezsp_f: EZSP) -> None:
).serialize()
)
]


async def test_xncp_set_led_state(ezsp_f: EZSP) -> None:
"""Test XNCP set_led_state."""
ezsp_f._mock_commands["customFrame"] = customFrame = AsyncMock(
return_value=[
t.EmberStatus.SUCCESS,
xncp.XncpCommand.from_payload(xncp.SetLedStateRsp()).serialize(),
]
)

await ezsp_f.xncp_set_led_state(red=1, green=2, blue=3)

assert customFrame.mock_calls == [
call(
xncp.XncpCommand.from_payload(
xncp.SetLedStateReq(red=1, green=2, blue=3)
).serialize()
)
]
Loading