Integrating ChirpStack with Home Assistant
ChirpStack does not talk to Home Assistant directly. There is no native integration, no one-click setup, no magic. What ChirpStack does is publish sensor data to MQTT, and Home Assistant can consume MQTT, so the two systems connect through a broker in the middle. That is the whole architecture. Everything else is just the details of making it work cleanly.
If you have not read the MQTT article in this series, now is a good time. The short version: MQTT is a lightweight publish/subscribe messaging protocol. ChirpStack publishes uplink data as JSON over MQTT, and to receive data from a device you subscribe to its MQTT topic. Home Assistant subscribes to those topics and turns the payloads into sensor entities. The broker sits between them, routing messages.
The two approaches
There is a decision to make before you start, and it matters more than it looks.
Option one: shared broker. ChirpStack and Home Assistant both point at the same MQTT broker. ChirpStack publishes uplinks there, Home Assistant reads them directly. Simple, no duplication, fewer moving parts.
Option two: separate brokers with a bridge. ChirpStack has its own broker, Home Assistant runs Mosquitto as an add-on with its own broker. You configure a Mosquitto bridge between them so messages flow from one to the other.
The shared broker approach is cleaner if you are running ChirpStack on a separate server, which in this homelab setup means running on February, pointing both ChirpStack and Home Assistant at the same external Mosquitto instance. That is the setup I am using.
If you want to run ChirpStack directly inside Home Assistant, there is a community add-on at databang-io/chirpstack-ha-addon that wires everything together internally. This article covers the external server approach.
ChirpStack MQTT configuration
ChirpStack’s MQTT integration is configured in chirpstack.toml. The relevant section:
[integration]
enabled = ["mqtt"]
[integration.mqtt]
server = "tcp://your-mqtt-broker:1883"
username = "chirpstack"
password = "your-password"
json = true
The json = true line matters. Without it, ChirpStack sends Protobuf-encoded binary payloads. With it, everything comes through as JSON, which Home Assistant can parse directly. Turn it on.
The default event topic is application/APPLICATION_ID/device/DEV_EUI/event/EVENT, where the APPLICATION_ID is retrievable from the API or the web interface, and is not the same as the AppEUI or JoinEUI. For Home Assistant sensors you care about the up event.
Restart ChirpStack after editing the config and verify it is publishing:
mosquitto_sub -h your-mqtt-broker -u user -P password \
-t "application/#" -v
Trigger an uplink from a sensor and you should see JSON in the terminal. If you see nothing, ChirpStack is not connecting to the broker. If you see binary noise, JSON mode is not enabled.
Payload codecs
This is the part that trips most people up, and it is worth understanding before you touch Home Assistant.
Most commercial LoRaWAN sensors transmit binary-encoded payloads to keep packet sizes small. A temperature reading does not arrive as {"temperature": 21.5}. It arrives as a handful of bytes that encode that number according to a scheme defined by the manufacturer.
ChirpStack handles decoding through JavaScript codec functions in the device profile. If the codec is configured correctly, the uplink payload arriving at MQTT will contain a decoded object field. If the codec is missing or wrong, you get the raw base64 data field and nothing readable. Go to Device Profiles, find the profile for your device, and open the Codec tab.
Dragino, Milesight, Seeed, and most other manufacturers publish their decoder functions in their documentation. Paste the function in, save, and trigger another uplink. The MQTT payload should now include an object field with readable values.
Here is what a decoded uplink looks like for a basic temperature and humidity sensor:
{
"deviceInfo": {
"deviceName": "garden-sensor",
"devEui": "0102030405060708"
},
"object": {
"temperature": 18.4,
"humidity": 72.1,
"battery": 3.1
},
"time": "2026-05-08T09:14:22Z"
}
That is what Home Assistant is going to be parsing. Check what your actual payloads look like before writing any configuration, because the structure varies by device.
Home Assistant configuration
With decoded payloads arriving at the broker, the Home Assistant side is relatively straightforward. Make sure the MQTT integration is installed and connected to the same broker, then define sensors in configuration.yaml:
mqtt:
sensor:
- name: "Garden Temperature"
state_topic: "application/YOUR_APP_ID/device/YOUR_DEV_EUI/event/up"
value_template: "{{ value_json.object.temperature }}"
unit_of_measurement: "°C"
device_class: temperature
unique_id: "garden_sensor_temperature"
- name: "Garden Humidity"
state_topic: "application/YOUR_APP_ID/device/YOUR_DEV_EUI/event/up"
value_template: "{{ value_json.object.humidity }}"
unit_of_measurement: "%"
device_class: humidity
unique_id: "garden_sensor_humidity"
- name: "Garden Battery"
state_topic: "application/YOUR_APP_ID/device/YOUR_DEV_EUI/event/up"
value_template: "{{ value_json.object.battery }}"
unit_of_measurement: "V"
device_class: voltage
unique_id: "garden_sensor_battery"
Replace YOUR_APP_ID with the Application ID from the ChirpStack interface, and YOUR_DEV_EUI with the device EUI in lowercase hex. The value_template uses Jinja2 to extract values from the JSON payload. If your codec produces a different structure, adjust accordingly. This is where checking the actual MQTT payload first saves a lot of frustration.
After adding the configuration, restart Home Assistant and the sensors should appear under Settings > Devices & Services > MQTT.
The HACS integration route
There is a community integration called Chirp that glues together HA MQTT and ChirpStack. It retrieves device information from ChirpStack’s gRPC API and exposes it to Home Assistant’s MQTT discovery service, with detailed configuration stored as a ChirpStack codec extension.
It is more elegant than manual YAML for large deployments. It is also more complex to set up and requires the codec to be structured in a specific way. For a small homelab deployment with a handful of sensors, manual YAML is less overhead and easier to debug. For anything beyond that, Chirp is worth looking at.
What breaks and why
A few things that will go wrong, and how to recognise them:
No data in Home Assistant, messages visible in MQTT Explorer. The state_topic does not match what ChirpStack is publishing. Double-check the Application ID and DevEUI, and confirm the topic structure matches exactly. MQTT topics are case-sensitive.
Data visible as raw data field, no object. The codec is missing or broken. Fix it in ChirpStack before touching Home Assistant configuration.
Sensors appear but show unavailable. The value_template path does not match the actual JSON structure. Subscribe to the topic with mosquitto_sub and check the exact field names in the payload.
Sensors update intermittently or not at all. LoRaWAN devices uplink infrequently by design. A sensor configured to send every 15 minutes will produce Home Assistant state updates every 15 minutes and not more often. This is normal.
On keeping things in one place
The appeal of getting ChirpStack data into Home Assistant is that it makes LoRaWAN sensors first-class citizens in the same automation environment as everything else. A temperature sensor in the garden can trigger the same automations as a Zigbee contact sensor on the back door. The data ends up in the same history graphs. The alerts go through the same notification stack.
That is the argument for doing this rather than routing everything through Grafana and treating them as separate systems. Grafana is better for visualisation and long-term trend analysis. Home Assistant is better for automation and integration. The sensor data should eventually end up in both, and MQTT makes that straightforward: ChirpStack publishes once, multiple systems subscribe.
The setup described here is the starting point. Once the sensors are appearing as Home Assistant entities, the interesting work of actually using them begins.