diff --git a/bellows/ezsp/__init__.py b/bellows/ezsp/__init__.py index 1aa308bd..7dd0adfa 100644 --- a/bellows/ezsp/__init__.py +++ b/bellows/ezsp/__init__.py @@ -916,3 +916,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, + ) + ) diff --git a/bellows/ezsp/xncp.py b/bellows/ezsp/xncp.py index 1048e967..9e80a01e 100644 --- a/bellows/ezsp/xncp.py +++ b/bellows/ezsp/xncp.py @@ -47,6 +47,7 @@ class XncpCommandId(t.enum16): GET_ROUTE_TABLE_ENTRY_REQ = 0x0007 GET_TX_POWER_INFO_REQ = 0x0008 SEND_UNICAST_REQ = 0x0009 + SET_LED_STATE_REQ = 0x0F00 GET_SUPPORTED_FEATURES_RSP = GET_SUPPORTED_FEATURES_REQ | 0x8000 SET_SOURCE_ROUTE_RSP = SET_SOURCE_ROUTE_REQ | 0x8000 @@ -58,6 +59,7 @@ class XncpCommandId(t.enum16): GET_ROUTE_TABLE_ENTRY_RSP = GET_ROUTE_TABLE_ENTRY_REQ | 0x8000 GET_TX_POWER_INFO_RSP = GET_TX_POWER_INFO_REQ | 0x8000 SEND_UNICAST_RSP = SEND_UNICAST_REQ | 0x8000 + SET_LED_STATE_RSP = SET_LED_STATE_REQ | 0x8000 UNKNOWN = 0xFFFF @@ -153,6 +155,9 @@ class FirmwareFeatures(t.bitmap32): # single command COMBINED_SEND = 1 << 8 + # The firmware exposes adapter LED control + LED_CONTROL = 1 << 31 + class XncpCommandPayload(t.Struct): pass @@ -300,6 +305,18 @@ class SendUnicastRsp(XncpCommandPayload): sequence: t.uint8_t +@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 diff --git a/tests/test_xncp.py b/tests/test_xncp.py index 451f5e8e..f9bd09d7 100644 --- a/tests/test_xncp.py +++ b/tests/test_xncp.py @@ -348,3 +348,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() + ) + ]