Configuration

Every option is optional. The common case is to change nothing at all.

Defaults

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

MQTT

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.

OptionDefaultDescription
mqtt_hostblankBroker hostname. Blank means use the Supervisor's MQTT service.
mqtt_port1883Broker port.
mqtt_usernameblankBroker username.
mqtt_passwordblankBroker password.
mqtt_sslfalseConnect with TLS.
base_topicbroadlink2mqttRoot of every topic this add-on owns.
discovery_prefixhomeassistantMust match your MQTT integration's discovery prefix.
If you change base_topic after the entities exist, Home Assistant will create a second set. Delete the old device from the MQTT integration afterwards.

Devices

OptionDefaultDescription
auto_discovertrueFind devices by UDP broadcast at startup.
discovery_timeout5Seconds 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.

Give your blaster a DHCP reservation. Entities are keyed by MAC address, so a changed IP will not duplicate anything — but the add-on has to find the device before it can talk to it.

Capture behaviour

You should not need to touch these. The values come from core#177767, where they were tuned against real hardware.

OptionDefaultDescription
capture_window15Seconds to listen after the switch turns on, and after each capture.
capture_limit60Hard cap on one listening session, however many codes arrive.
poll_interval1Seconds between check_data polls.
rearm_interval20Re-arm learning mode this often, ahead of the firmware's own timeout.
always_listenfalseListen continuously instead of in bounded windows. Experimental — see below.
publish_sensorstruePublish temperature/humidity for devices that have them.
log_levelinfoSet 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.

Continuous listening experimental

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.

This is best-effort, not reliable, and the limits are structural. A Broadlink has one IR front end, so it cannot transmit and listen at the same time: a command sent while you press the remote loses one of the two. It is briefly deaf after every capture and every transmission. Its LED stays lit continuously. And a permanently armed session on cheap hardware has not been proven over weeks — this is exactly the uncertainty that stopped core#177767.

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.

Watching whether it is actually working

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:

AttributeWhat it tells you
capturesSignals accepted and published.
noise_discardedCaptures rejected as ambient interference. A high and climbing number means something near the blaster is emitting IR.
duplicates_suppressedRepeat frames from held buttons.
errors / consecutive_errorsDevice failures. Five in a row trips the watchdog.
degradedThe watchdog has backed off to 30 s and the device reads unavailable.
last_captureWhen a real code last arrived.
With continuous listening on, the Learning mode switch becomes a live override: turning it off stops listening until you turn it back on, which is useful during a burst of outgoing commands.

MQTT topics

With base_topic: broadlink2mqtt and a device whose MAC is a1:b2:c3:d4:e5:f6:

TopicDirPayload
broadlink2mqtt/statusoutonline / offline (last will)
…/a1b2c3d4e5f6/availabilityoutonline / offline
…/a1b2c3d4e5f6/emitter/setin{"timings": […], "modulation": 38000, "repeat_count": 0}
…/a1b2c3d4e5f6/receiver/stateout{"timings": […], "modulation": 38000}
…/a1b2c3d4e5f6/learn/setinON / OFF
…/a1b2c3d4e5f6/learn/stateoutON / OFF
…/a1b2c3d4e5f6/code/stateout{"short", "base64", "timings", "modulation"}
…/a1b2c3d4e5f6/sensor/stateout{"temperature": 21.4, "humidity": 48.0}
…/a1b2c3d4e5f6/health/stateout{"status", "captures", "noise_discarded", "errors", …}

Entities go unavailable if either the bridge or the device drops, using availability_mode: all across both topics.

Running outside Home Assistant

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.

On a bridge network, set 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.

docker compose

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