diff --git a/bellows/ezsp/__init__.py b/bellows/ezsp/__init__.py index 7166847e..e7e0b88b 100644 --- a/bellows/ezsp/__init__.py +++ b/bellows/ezsp/__init__.py @@ -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, + ) + ) diff --git a/bellows/ezsp/xncp.py b/bellows/ezsp/xncp.py index 2cfc50d4..44dcac95 100644 --- a/bellows/ezsp/xncp.py +++ b/bellows/ezsp/xncp.py @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/tests/test_xncp.py b/tests/test_xncp.py index 74a099d3..720f514f 100644 --- a/tests/test_xncp.py +++ b/tests/test_xncp.py @@ -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() + ) + ]