Every option is optional. The common case is to change nothing at all.
mqtt_host: ""
mqtt_port: 1883
mqtt_username: ""
mqtt_password: ""
mqtt_ssl: false
base_topic: broadlink2mqtt
discovery_prefix: homeassistant
auto_discover: true
discovery_timeout: 5
devices: []
capture_window: 15
capture_limit: 60
poll_interval: 1
rearm_interval: 20
publish_sensors: true
log_level: info
Leave mqtt_host blank and the add-on asks the Supervisor for the broker you
already configured. Fill these in only to point at an external broker.
| Option | Default | Description |
|---|---|---|
mqtt_host | blank | Broker hostname. Blank means use the Supervisor's MQTT service. |
mqtt_port | 1883 | Broker port. |
mqtt_username | blank | Broker username. |
mqtt_password | blank | Broker password. |
mqtt_ssl | false | Connect with TLS. |
base_topic | broadlink2mqtt | Root of every topic this add-on owns. |
discovery_prefix | homeassistant | Must match your MQTT integration's discovery prefix. |
base_topic after the entities exist, Home Assistant will create
a second set. Delete the old device from the MQTT integration afterwards.
| Option | Default | Description |
|---|---|---|
auto_discover | true | Find devices by UDP broadcast at startup. |
discovery_timeout | 5 | Seconds to wait for discovery replies. |
devices | [] | Devices to add by IP address. |
Discovery is a UDP broadcast to 255.255.255.255:80, which does not cross VLANs
or subnets. If your blaster sits on another segment, list it explicitly:
auto_discover: false
devices:
- host: 192.168.1.50
name: Living Room Blaster
- host: 192.168.1.51
name is optional and only sets the device name shown in Home Assistant.
Explicit entries and discovered devices are merged, so you can leave
auto_discover on and still pin one device by IP.
You should not need to touch these. The values come from core#177767, where they were tuned against real hardware.
| Option | Default | Description |
|---|---|---|
capture_window | 15 | Seconds to listen after the switch turns on, and after each capture. |
capture_limit | 60 | Hard cap on one listening session, however many codes arrive. |
poll_interval | 1 | Seconds between check_data polls. |
rearm_interval | 20 | Re-arm learning mode this often, ahead of the firmware's own timeout. |
always_listen | false | Listen continuously instead of in bounded windows. Experimental — see below. |
publish_sensors | true | Publish temperature/humidity for devices that have them. |
log_level | info | Set to debug when reporting a problem. |
capture_limit exists on purpose. Learning mode keeps the device's receiver
and LED active and blocks it from transmitting, so an unbounded session would leave your
blaster permanently unable to send. Raise it if you need to, but do not remove the idea.
Setting always_listen: true keeps the receiver armed permanently, so pressing
a physical remote keeps Home Assistant in sync rather than only working during a learning
window. The polling cost is modest — measured on an RM4 mini at 46 ms median per poll,
no errors over 90 seconds, under 5% of wall-clock in device I/O.
For dependable always-on reception, a dedicated ESPHome IR receiver (an ESP plus a TSOP38238, about the price of a coffee) genuinely listens continuously, pushes events instead of being polled, has no LED and no transmit conflict. The strongest setup is both: ESPHome for receive, your Broadlink for transmit. This option exists for people who only own a Broadlink.
Because it is best-effort, the add-on publishes a Capture health
diagnostic entity rather than leaving you to infer trouble from a flaky automation. Its
state is listening, idle or degraded, with counters
in its attributes:
| Attribute | What it tells you |
|---|---|
captures | Signals accepted and published. |
noise_discarded | Captures rejected as ambient interference. A high and climbing number means something near the blaster is emitting IR. |
duplicates_suppressed | Repeat frames from held buttons. |
errors / consecutive_errors | Device failures. Five in a row trips the watchdog. |
degraded | The watchdog has backed off to 30 s and the device reads unavailable. |
last_capture | When a real code last arrived. |
With base_topic: broadlink2mqtt and a device whose MAC is a1:b2:c3:d4:e5:f6:
| Topic | Dir | Payload |
|---|---|---|
broadlink2mqtt/status | out | online / offline (last will) |
…/a1b2c3d4e5f6/availability | out | online / offline |
…/a1b2c3d4e5f6/emitter/set | in | {"timings": […], "modulation": 38000, "repeat_count": 0} |
…/a1b2c3d4e5f6/receiver/state | out | {"timings": […], "modulation": 38000} |
…/a1b2c3d4e5f6/learn/set | in | ON / OFF |
…/a1b2c3d4e5f6/learn/state | out | ON / OFF |
…/a1b2c3d4e5f6/code/state | out | {"short", "base64", "timings", "modulation"} |
…/a1b2c3d4e5f6/sensor/state | out | {"temperature": 21.4, "humidity": 48.0} |
…/a1b2c3d4e5f6/health/state | out | {"status", "captures", "noise_discarded", "errors", …} |
Entities go unavailable if either the bridge or the device drops, using
availability_mode: all across both topics.
Without the Supervisor, every option above is available as an environment variable of the
same name in upper case — MQTT_HOST, MQTT_PORT,
MQTT_USERNAME, MQTT_PASSWORD, MQTT_SSL,
BASE_TOPIC, DISCOVERY_PREFIX, AUTO_DISCOVER,
DISCOVERY_TIMEOUT, CAPTURE_WINDOW, CAPTURE_LIMIT,
POLL_INTERVAL, REARM_INTERVAL, PUBLISH_SENSORS and
LOG_LEVEL. The exception is devices, which becomes
DEVICES as a comma-separated list of IP addresses.
AUTO_DISCOVER=false and pin the blaster with
DEVICES. Broadcast discovery needs host networking, but talking to a known
IP is plain unicast and works through Docker's NAT — which is usually the easier setup.
services:
broadlink2mqtt:
image: ghcr.io/pranjal-joshi/broadlink2mqtt:1.1.0-beta.1
container_name: broadlink2mqtt
restart: unless-stopped
environment:
- MQTT_HOST=mosquitto
- MQTT_USERNAME=addons
- MQTT_PASSWORD=${MQTT_PASSWORD:?set it in .env}
- AUTO_DISCOVER=false
- DEVICES=192.168.1.50
- LOG_LEVEL=debug
networks:
- ha_network
depends_on:
- mosquitto