Skip to content
Get started

RS485 Frame Button

Each rs485_frame button entity is a shortcut for sending one pre-configured frame onto the bus. Pressing the button queues that frame, and the hub sends it at the next TX gate window. The bytes are built at compile time, so nothing is encoded when the button is pressed. On buses where the target device treats incoming frames as commands (like pressing a key on a physical panel), a button entity gives you direct control from Home Assistant. It requires the RS485 Frame hub to be configured, and supports three construction forms:

  • value: — encodes one or more values into the on-wire payload, back-to-back, using the command_format: block of the button or the hub. Requires one of the two to be set. value_element_bytes sets how many bytes each value is written as (1, 2, 3, or 4), and a value that does not fit is rejected. A list is shorthand for protocols whose frame carries more than one value in a single payload, without repeating the preamble/postamble per value.
  • frame_type: + payload: — bypasses command_format: entirely. The supplied frame type and payload bytes are sent as-is after DLE-framing, byte-stuffing, and CRC. Use this for hubs with no command_format:, probe frames, or any custom frame layout. See also the rs485_frame.send_frame action for invoking raw frames from scripts or computed-payload contexts.
  • value: + command_format: — encodes the value(s) using a button-level command_format: block instead of the hub’s. Use this when one button needs a different encoding than the hub default (different preamble, element size, or postamble), or when the hub has no command_format: set and you want the value: shorthand on a single button without adding a hub-level encoder.

The value: form and the frame_type: + payload: form are mutually exclusive. command_format: on the button can only be combined with value:, not with frame_type: / payload:.

A button press queues a frame on the hub, which transmits it during the next TX gate window. What happens after depends on your bus: the remote device may toggle a state, execute an action, or send back an acknowledgement — the ESP cannot know the outcome until it reads a subsequent response frame. That is why each button entity models a single outbound frame event rather than a stateful on/off switch.

On Hayward AquaLogic pool controllers, for example, this maps directly to pressing a key on the physical panel: the controller receives the frame (typically within its 100 ms keep-alive cycle) and decides whether to change state; the LED status frame that follows reflects the result.

Enable dump_frames: true and sniffer_only: true on the hub, then press a physical button on your controller and watch the ESP log. Each validated frame is printed in hex as wire bytes: DLE/STX, the frame type and payload, the CRC (1 or 2 bytes depending on crc.type), then DLE/ETX, with byte-stuffing still applied. Frames longer than 32 bytes span several log lines, and the closing DLE/ETX can be split across two of them; join them first.

To get the bytes a button sends, drop the leading DLE/STX and the trailing DLE/ETX, undo the byte-stuffing (with escape_byte, DLE followed by the marker byte is one DLE; with double, two DLEs are one DLE), then drop the CRC bytes from the end. The CRC is stuffed too, so remove it only after unstuffing. What is left starts with the frame type. Use it as the raw frame_type: + payload: form, or, if the same layout repeats across buttons, as command_format: on the hub: put the frame type and the fixed bytes after it in preamble, and the part that changes between buttons in value:.

Known value: entries for Hayward AquaLogic, confirmed from live bus captures, are in the rs485_frame-examples repository. AUX outputs are controller-wiring-dependent — rename and verify against your physical panel.

  • rs485_frame_id (Required, ID): The ID of the rs485_frame hub.

Provide exactly one of the following forms:

  • value (Required, hex uint32 or list of up to 8 hex uint32): Value(s) to encode, e.g. 0x00010000 or [0x00010000, 0x00020000]. Each value is written as value_element_bytes bytes, back-to-back between the preamble and postamble of the command_format: in use (the button’s, otherwise the hub’s); a value that does not fit is rejected at config time. Also rejected when neither the button nor the hub has a command_format: — use the raw form or add one.

  • frame_type + payload (Required together when not using value:):

    • frame_type (list of hex bytes): Frame type prefix, up to 8 bytes (usually 2). Frame type and payload together must be at least 2 bytes. Same convention as gate.frame_type and on_frame.frame_type elsewhere in the component.
    • payload (list of hex bytes): The payload bytes that follow the frame type. Caller-composed; the hub adds framing and CRC.

    Whichever form is used, the button’s resolved on-wire frame (frame_type: + payload:, or command_format:’s preamble + encoded value(s) + postamble) must fit within the referenced hub’s max_frame_length: (see the hub config) — rejected at config time otherwise.

  • value_element_bytes (Optional, integer): Overrides only the element byte width for this button, using 1, 2, 3, or 4. Inherits the hub’s preamble, endian, and postamble. Requires the hub to have command_format: set. Cannot be combined with a per-button command_format: or with frame_type: / payload:.

  • command_format (Optional): Overrides the hub’s command_format: for this button only. Accepts the same sub-keys as command_format: on the hub: preamble, value_element_bytes, endian, postamble. When present, the value: form is accepted even if the hub has no command_format: set. Cannot be combined with frame_type: / payload:.

All other options from Button.

button:
- platform: rs485_frame
rs485_frame_id: pool
name: "Pool Lights"
# Confirmed from bus capture: the first word is the press, the second the release (a quick press).
value: [ 0x00010000, 0x00010000 ]
- platform: rs485_frame
rs485_frame_id: pool
name: "Filter"
value: [ 0x80000000, 0x80000000 ]
- platform: rs485_frame
rs485_frame_id: pool
name: "Pool/Spa Mode"
value: [ 0x40000000, 0x40000000 ]

Raw Form (Custom Devices, No command_format:)

Section titled “Raw Form (Custom Devices, No command_format:)”
button:
- platform: rs485_frame
rs485_frame_id: my_device
name: "Open Door"
frame_type: [0x00, 0xA1]
payload: [0x01, 0x00, 0x55]
- platform: rs485_frame
rs485_frame_id: my_device
name: "Close Door"
frame_type: [0x00, 0xA1]
payload: [0x01, 0x00, 0xAA]

Use when a single button needs a different encoding than the hub default, or when the hub has no command_format: set (for example, a Hayward AquaLogic setup where most buttons use the wired-remote preamble but one must use the wireless preamble):

button:
- platform: rs485_frame
rs485_frame_id: pool
name: "System Off"
value: 0x08000000
command_format:
preamble: [0x00, 0x83, 0x01]
value_element_bytes: 4
endian: big
postamble: [0x00]

Read the resulting state from a response frame on the bus — decode the relevant frame type in on_frame: on the hub and push to template binary sensors. See the Basic usage example for the general pattern. For a single status bit decoded from a Hayward AquaLogic LED-mask frame:

rs485_frame:
id: pool
on_frame:
- frame_type: [0x01, 0x02]
then:
- lambda: |-
if (payload.size() < 6) return;
uint32_t mask = uint32_t(payload[2]) | (uint32_t(payload[3]) << 8) |
(uint32_t(payload[4]) << 16) | (uint32_t(payload[5]) << 24);
id(led_filter).publish_state(bool(mask & (1UL << 5)));
binary_sensor:
- platform: template
id: led_filter
name: "Filter"