Conversation
Exposes one radio_frequency.RadioFrequencyTransmitterEntity per RF-capable Broadlink device (RM Pro / RM4 Pro). The entity accepts a RadioFrequencyCommand, encodes its raw OOK timings into Broadlink's pulse-length wire format (type byte 0xB2 for the 433 MHz ISM band or 0xB4 for the 315 MHz band), and transmits via the existing Broadlink client's send_data() method. Non-OOK modulation and out-of-band frequencies are rejected with a HomeAssistantError before any bytes are sent. symbol_rate and output_power on the command are advisory and ignored — the Broadlink RF front-end is fixed-rate OOK-only hardware. The existing remote.send_command path is unchanged.
|
Hey there @Danielhiversen, @felipediel, @L-I-Am, @eifinger, mind taking a look at this pull request as it has been labeled with an integration ( Code owner commandsCode owners of
|
There was a problem hiding this comment.
Pull request overview
Adds a radio_frequency transmitter entity to the Broadlink integration for RF-capable RM devices, including an encoder that converts raw OOK timings into Broadlink’s RF pulse-length wire format and transmits via device.api.send_data().
Changes:
- Add
radio_frequencyplatform/entity for Broadlink RM Pro / RM4 Pro devices. - Implement Broadlink RF packet encoder and transmit path with translated errors on failure.
- Add comprehensive tests for entity registration, encoding correctness, dispatch, and error handling.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
homeassistant/components/broadlink/radio_frequency.py |
New platform entity + encoder and transmit implementation. |
homeassistant/components/broadlink/const.py |
Registers Platform.RADIO_FREQUENCY for RF-capable Broadlink device types. |
homeassistant/components/broadlink/strings.json |
Adds translated exception messages for RF transmit-related failures. |
tests/components/broadlink/test_radio_frequency.py |
New test suite validating setup, encoding, dispatch, and error scenarios. |
| for duration in timings_us: | ||
| ticks = round(abs(duration) / _TICK_US) | ||
| div, mod = divmod(ticks, 256) | ||
| if div: | ||
| buf.append(0x00) | ||
| buf.append(div) | ||
| buf.append(mod) | ||
| payload_len = len(buf) - 4 |
There was a problem hiding this comment.
Validate that each encoded pulse has at least 1 tick (and never 0), since 0 is reserved as the escape marker in the Broadlink pulse format and a rounded-to-zero duration would produce a malformed payload.
| buf = bytearray([type_byte, repeat_count & 0xFF, 0, 0]) | ||
| for duration in timings_us: | ||
| ticks = round(abs(duration) / _TICK_US) | ||
| div, mod = divmod(ticks, 256) | ||
| if div: | ||
| buf.append(0x00) | ||
| buf.append(div) | ||
| buf.append(mod) |
There was a problem hiding this comment.
Add explicit bounds checks for repeat_count (0–255) and tick counts (<= 0xFFFF) instead of truncating/overflowing into invalid bytes, and raise a clear error when inputs exceed Broadlink’s wire format limits.
| buf = bytearray([type_byte, repeat_count & 0xFF, 0, 0]) | |
| for duration in timings_us: | |
| ticks = round(abs(duration) / _TICK_US) | |
| div, mod = divmod(ticks, 256) | |
| if div: | |
| buf.append(0x00) | |
| buf.append(div) | |
| buf.append(mod) | |
| if not 0 <= repeat_count <= 0xFF: | |
| raise HomeAssistantError( | |
| f"Broadlink RF repeat_count must be between 0 and 255, got {repeat_count}" | |
| ) | |
| buf = bytearray([type_byte, repeat_count, 0, 0]) | |
| for duration in timings_us: | |
| ticks = round(abs(duration) / _TICK_US) | |
| if ticks > 0xFFFF: | |
| raise HomeAssistantError( | |
| "Broadlink RF timing exceeds the maximum encodable duration: " | |
| f"{duration} µs -> {ticks} ticks" | |
| ) | |
| if ticks < 256: | |
| buf.append(ticks) | |
| else: | |
| buf.append(0x00) | |
| buf.extend(ticks.to_bytes(2, "big")) |
| "frequency_not_supported": { | ||
| "message": "Broadlink devices cannot transmit on {frequency} Hz" | ||
| }, | ||
| "modulation_not_supported": { | ||
| "message": "Broadlink devices only support OOK modulation, got {modulation}" | ||
| }, |
There was a problem hiding this comment.
Remove or consolidate these Broadlink-specific unsupported frequency/modulation exception translations, since the radio_frequency domain rejects unsupported frequencies/modulations before dispatching to the entity in normal usage, making these messages unlikely to ever be surfaced.
| "frequency_not_supported": { | |
| "message": "Broadlink devices cannot transmit on {frequency} Hz" | |
| }, | |
| "modulation_not_supported": { | |
| "message": "Broadlink devices only support OOK modulation, got {modulation}" | |
| }, |
Summary
Adds a
radio_frequencyplatform to the Broadlink integration. RF-capable devices (RM Pro, RM4 Pro) now expose aRadioFrequencyTransmitterEntityalongside the existingremoteentity. The new entity accepts aRadioFrequencyCommand, encodes its raw OOK timings into Broadlink's pulse-length wire format, and transmits via the existingdevice.api.send_data()path — no new transport code, no changes to the existingremote.send_commandbehavior.This is enabled by #168447, which introduced the
radio_frequencydomain, and mirrors the transmitter-side shape set by #168448 (ESPHome).Scope
DOMAINS_AND_TYPESdevice-type table).remote.learn_commandpath is unchanged.0xB20xB4HomeAssistantError("frequency_not_supported")before any bytes hit the wire. Non-OOK modulation is rejected withHomeAssistantError("modulation_not_supported").symbol_rateandoutput_poweron the command are advisory and ignored — the hardware is fixed-rate OOK-only.Encoder
encode_rf_packet(type_byte, repeat_count, timings_us)produces the Broadlink RF wire format:0xB2/0xB4)round(abs(µs) / 32.84)ticks, written as one byte when< 256and as0x00 <hi> <lo>(big-endian 16-bit) otherwise.Pulse timings are taken straight from
command.get_raw_timings()and rounded to the nearest tick;abs()handles the signed mark/space convention used byrf_protocols.Test plan
0xB4async_send_commandinvokesdevice.api.send_dataexactly once with the expected encoded bytesHomeAssistantErrorand does not callsend_dataHomeAssistantErrorand does not callsend_dataBroadlinkExceptionfromsend_datasurfaces asHomeAssistantErrorOut of scope
learn_commandvia the new domain.remote.send_command.rf-protocolslibrary work.Generated by Claude Code