Documentation / Tutorials

Tutorial: your panel and Home Assistant over MQTT

A touchscreen on the wall that shows what the house is doing and switches a light, talking to Home Assistant through an MQTT broker: what a broker is, how to set one up, and a panel built from it one step at a time.

The finished panel in Preview, reading the house.
The finished panel in Preview, reading the house.

MQTT is how a lot of home kit talks: sensors, smart plugs, Zigbee2MQTT, Tasmota, ESPHome, and Home Assistant itself. This tutorial explains how the pieces fit together, sets up the broker that sits in the middle, and then builds a panel on an LCDWIKI ES3C35P that reads six values from the house, switches the porch light, and shows up in Home Assistant as a device of its own.

Every step below was carried out in the designer to take its picture. The values come from a real grab of a pretend house: a small program that publishes lights, heating, solar and a doorbell to a broker, with Home Assistant watching it. The design that came out of the steps was generated and compiled for the board: 1.39 MB of firmware.

What is free and what is Pro. Connecting to a broker, listening to topics, publishing to them and importing a grab are free on every plan. Announcing the panel's own values to Home Assistant (Part 7) is Pro. Leave Part 7 out and the rest of the panel generates on any plan; the export dialog says so before anything is charged (step 25).

Run on a board. The same MQTT and Home Assistant set-up has run on a Waveshare ESP32-P4 4.3 board against our test house: it joined the broker, turned up in Home Assistant as a device by itself, and was controlled from there both ways. The ES3C35P build in this tutorial has been compiled, not yet run.

What you need

Part 1: what a broker is

A broker is a post office for small messages. Nothing on MQTT talks to anything else directly: every device connects to the broker, and the broker passes messages on.

Everything that connects is a client, and they are all equal: your sensors, Home Assistant, the panel, and MQTT Wizard when it grabs. Home Assistant is not the broker. It is one more client, which listens to what devices say and publishes the commands you give it.

So the panel points at the broker, never at Home Assistant's web page. If Home Assistant is at http://192.168.1.20:8123 and the broker runs on the same machine (the usual way, below), the panel's broker is 192.168.1.20, port 1883. Port 8123 is Home Assistant's website; the board does not speak to it.

Part 2: setting up the broker

If you run Home Assistant OS

  1. In Home Assistant open Settings > Add-ons > Add-on store, find Mosquitto broker, press Install and then Start. Mosquitto is the broker; the add-on runs it on the same machine as Home Assistant, on port 1883.
  2. Home Assistant finds it by itself: Settings > Devices & services offers the MQTT integration. Press Configure and accept.
  3. Make a login for the panel. The add-on lets in Home Assistant's own users, so the simplest is Settings > People > Users > Add user (turn on advanced mode in your profile if you do not see Users), called something like panel, with a password of its own. Give each device its own login, not yours.

If you run Home Assistant in Docker

Home Assistant Container has no add-ons, so the broker is a container of its own beside it, from the eclipse-mosquitto image. Give it a mosquitto.conf that listens on the network and asks for a password:

listener 1883
allow_anonymous false
password_file /mosquitto/config/passwd
persistence true
persistence_location /mosquitto/data/

Make the password file with mosquitto_passwd -c /mosquitto/config/passwd panel inside the container (leave out -c for the second user and after, or it starts the file again), make one for Home Assistant too, and restart the container. Then add the MQTT integration in Home Assistant by hand, with the machine's address, port 1883 and Home Assistant's login. Publish port 1883 from the container, or the board will not reach it.

One broker. Home Assistant's MQTT integration connects to one broker. If you already have another (a Zigbee2MQTT box with its own, say), do not point the panel at a second one and hope: either use that broker for everything, or join the two with a bridge (Part 8).

Part 3: a project, and the broker

1 Start a project for Arduino

Open the designer. On a first visit the setup wizard is already there; otherwise choose File > New project. For What are you building for? choose Arduino (C++) and press Next.

The wizard's first question: what the project is for.
The wizard's first question: what the project is for.

2 Choose the board

Choose LCDWIKI 3.5" ESP32-S3 (ES3C35P) and press Next. Leave Rotation at 0 degrees and press Next; keep LVGL 9 and press Next.

The board list, with the ES3C35P chosen.
The board list, with the ES3C35P chosen.

3 Name it and start empty

Type House panel for the project name, choose Start from an empty screen, and press Create the project.

A name, an empty screen, and Create the project.
A name, an empty screen, and Create the project.
The designer with the new project: an empty 320 x 480 screen.
The designer with the new project: an empty 320 x 480 screen.

4 Open the MQTT window

On the right, open the Data tab and its MQTT section. It is one line while MQTT is off. Press Configure MQTT.

Data, MQTT: Off, with Configure MQTT and Import.
Data, MQTT: Off, with Configure MQTT and Import.

The window has five tabs: Broker (where to connect), Topics (what the panel listens to), Publishing (what it says), Home Assistant and Import.

The MQTT window, on its Broker tab.
The MQTT window, on its Broker tab.

5 Point it at the broker

Tick Connect. Type the broker's address under Broker (the house here is at 192.168.1.163; yours is the Home Assistant machine's address with the add-on), leave Port at 1883 and TLS off, and type the User and Password you made in Part 2.

Connect on, the broker's address, and the panel's own login.
Connect on, the broker's address, and the panel's own login.

Use the address as numbers. A name ending .local, such as homeassistant.local, works on your computer and not on the board, which does not look such names up. Your router can usually be told to keep the Home Assistant machine on the same address.

The password is kept in the design and in the firmware, and taken out of any copy you publish to the gallery. Client id can stay empty: each board then makes its own from its network address.

Part 4: grabbing the topics

The designer cannot see your broker: it runs in a browser, on our server, and your broker is on your own network with a password. So instead of typing every topic in, you grab them. MQTT Wizard runs on a computer on the same network, connects to the broker with a login you give it, listens to everything for a few seconds, and saves what it heard to a file: every topic, the last thing said on it, whether it was retained, and every device that announced itself to Home Assistant. Your password is not written to the file.

6 Load the grab

Open the window's Import tab and press Choose a grab file, or drop the file on it.

Import, before a grab: Choose a grab file.
Import, before a grab: Choose a grab file.

On the left are the topics as a tree, as they are named. On the right are the Home Assistant devices the grab heard announced, each entity with the topics it is read from (listen) and the topics it takes commands on (publish, with the commands it understands).

The grab loaded: 65 topics, and the House simulation's 24 entities.
The grab loaded: 65 topics, and the House simulation's 24 entities.

The grab is kept with the design, so you can come back and tick more later without grabbing again. Forget it takes it off. The tree opens a level at a time with the + beside each name; each row shows the last payload heard, its kind, kept for a retained topic, and how many times it was heard.

house, light, porch opened: the porch light's state and the request beside it.
house, light, porch opened: the porch light's state and the request beside it.

7 Tick what the panel needs

In Home Assistant devices, tick:

  • Porch light: listen on house/light/porch/state and publish on house/light/porch/set.
  • Outside temperature, Solar power and Battery: each one's listen.
  • Living room heating: the listen for its current temperature, house/heating/living/temperature.

Ticking the topic on either side ticks the same thing: it is one topic.

The porch light's state ticked to listen to, and its command to publish.
The porch light's state ticked to listen to, and its command to publish.

8 Import

Leave Make variables too and Make publish actions for commands ticked, and press Import.

Five topics and one command ticked, and room for 24.
Five topics and one command ticked, and room for 24.
  • Make variables too gives each topic a variable that holds what it last said. Widgets are bound to variables, not to topics.
  • Make publish actions for commands turns each command into a ready-made publish: Porch light ON and Porch light OFF. Nothing is put on a widget; they wait to be picked (step 18).

A design listens to at most 24 topics for each screen: 24 on one screen, 48 on two. If you tick more than there is room for, Import says how many to untick, and changes nothing.

Imported 5 topics, 5 variables and 2 ready-made publishes.
Imported 5 topics, 5 variables and 2 ready-made publishes.

All of it is one change: one Undo takes every topic, variable and ready-made publish back.

Part 5: topics and JSON

9 Look at the topics

Open the Topics tab. Each row is one value the panel listens to: its Name, the Topic, its Type, a Sample payload (filled in from the grab), what that sample Reads as, and the Variable that holds it.

The five imported topics, each with its variable.
The five imported topics, each with its variable.

The sample is what the designer and Preview show, since the designer never connects to your broker. The board reads the real thing.

10 Read one value out of a JSON payload

Not every device says one value per topic. Zigbee2MQTT, for one, puts a whole sensor in one message: {"temperature":21.4,"humidity":48,"battery":92,"linkquality":120} on zigbee2mqtt/hall_sensor. The house in this tutorial has no such sensor, so this step types one in, as you would for a topic the grab did not hear.

  1. Press + Topic.
  2. Name: hall_temperature. Topic: zigbee2mqtt/hall_sensor. Type: Number.
  3. Path into JSON: temperature. That is the key to read. Deeper values are reached with dots and brackets: update.state, readings[0].value.
  4. Sample payload: paste the message above. Reads shows 21.4.
  5. Press + Variable on the row.
One topic, one key out of it: reads 21.4.
One topic, one key out of it: reads 21.4.

With a grab, a JSON payload's keys are offered in the tree to tick one by one, and this is done for you. To read the humidity as well, add a second row with the same topic and the path humidity. Close the window.

Part 6: the panel

11 Lay out the screen

Give the screen a dark background (#10151c) and drop on these, from Widgets:

  • A Label Our house at the top, in Roboto Bold 28.
  • Four Labels under it: Outside, Living, Hall and Solar.
  • A Bar called Battery, 0 to 100, and a Label under it, BatteryText.
  • A Label Porch light with a Switch beside it, PorchState, and two Buttons under them, PorchOn and PorchOff, reading On and Off.
  • A Label Quiet mode with a Switch, Quiet, and at the bottom a Label called Message, in yellow.
The screen laid out, before anything is bound.
The screen laid out, before anything is bound.

12 Bind the labels to the variables

Select Outside and, in the Inspector's Data bindings, press +: From a variable, Variable outside_temperature, Drives Text, Prefix Outside , Format %.1f, Postfix °C.

Outside: the outside temperature, one decimal place, with words round it.
Outside: the outside temperature, one decimal place, with words round it.

Do the same for the others:

  • Living: living_room_heating, prefix Living room , %.1f, °C. (Import names a variable after the Home Assistant entity, here Living room heating; rename it in the Data tab if you like.)
  • Hall: hall_temperature, prefix Hall , %.1f, °C.
  • Solar: solar_power, prefix Solar , %.0f, W.
  • Battery (the bar): battery, Drives Value.
  • BatteryText: battery, prefix Battery , %.0f, %.

13 Show the porch light's state

Select PorchState and bind it: Variable porch_light, Drives Checked, Direction Variable -> widget. The switch now shows the light as the house reports it.

PorchState follows porch_light, one way.
PorchState follows porch_light, one way.

One way on purpose. The house says the light's state on one topic and takes commands on another, and a well-behaved device works that way: you ask on .../set, and the state topic changes once the light has done it. The buttons in the next step ask.

14 Make the On button switch the light on

Select PorchOn. Under Events press + Add event, leave Clicked, and choose the action Publish to MQTT. The card offers Ready-made: choose Porch light ON. It fills in the Topic, house/light/porch/set, and the Payload, ON.

Clicked, Publish to MQTT, and the ready-made Porch light ON.
Clicked, Publish to MQTT, and the ready-made Porch light ON.

Do the same on PorchOff with Porch light OFF.

Without a grab, type the topic and payload yourself: they are whatever the device's documentation says it takes. A payload can carry a variable in braces: {"brightness": {level}}.

15 Give the board a way onto Wi-Fi

MQTT goes over the network, and the board has to join one. The designer says so under Problems until it can: MQTT needs a network, and nothing in this design joins one.

  1. Rename the screen Home. Add a second screen, Network, and drop a WiFi Setup on it, filling the screen. Set its Go to when joined and Go to on Back to Home, and leave Reconnect on boot ticked.
  2. Back on Home, add a small Button at the top right reading Wi-Fi, with the event Clicked, Change Screen, Network.
The Network screen: a WiFi Setup, shown with made-up networks.
The Network screen: a WiFi Setup, shown with made-up networks.
The Wi-Fi key opens it.
The Wi-Fi key opens it.

The first time, press Wi-Fi on the board and join your network. It keeps the network and rejoins it each time it starts, and connects to the broker as soon as it has.

Part 7: the panel as a device in Home Assistant

So far the panel listens to the house and asks for the porch light. This part goes the other way: values that belong to the panel, made visible and settable in Home Assistant, with no YAML. The panel announces them by MQTT discovery, the way Zigbee2MQTT and ESPHome devices announce themselves.

16 Two variables of the panel's own

On the Data tab add two variables: quiet, On or off, and message, Text, starting as Hello from the panel. Bind the Quiet switch's Checked to quiet, Both ways, and the Message label's Text to message.

The Quiet switch and the quiet variable, both ways.
The Quiet switch and the quiet variable, both ways.

17 Announce them

Open the MQTT window again and its Home Assistant tab. Tick Announce, and type a Device name: Hall panel.

The Home Assistant tab before anything is announced.
The Home Assistant tab before anything is announced.

Press + Variable to announce twice. In the first row choose quiet, a Switch, named Quiet mode; in the second message, a Text, named Panel message.

quiet as a switch and message as a text, each with its name in Home Assistant.
quiet as a switch and message as a text, each with its name in Home Assistant.

What each kind means there:

  • A Sensor or Binary sensor is only read: a number, or on and off.
  • A Switch, a Number or a Text can also be set from Home Assistant. The panel listens on the entity's own /set topic and puts what arrives into the variable, so the switch on the screen moves, or the message changes.
  • Give a Sensor a Unit and a Device class (°C and temperature) and Home Assistant draws it as a temperature, with a history graph.

18 What it says, and that it is there

The Publishing tab lists everything in the design that publishes, with what sets it off, and the ready-made publishes Import made. Select goes to the widget.

The two buttons, and the two ready-made publishes.
The two buttons, and the two ready-made publishes.

On the Broker tab, Status topic can stay empty: a design that announces anything is given one of its own. The panel says online there when it connects, and the broker says offline for it when it drops off, so Home Assistant greys its entities out while the panel is unplugged.

The Status topic, left for the panel to choose.
The Status topic, left for the panel to choose.

Close the window. The Data tab's MQTT line now sums it up.

The broker, six topics, and two values announced to Home Assistant.
The broker, six topics, and two values announced to Home Assistant.

Once the board connects, the device should appear in Home Assistant under Settings > Devices & services > MQTT as Hall panel, with Quiet mode and Panel message. Nothing is announced until then, so the device is not there while the board is off or before its first connection. If Home Assistant restarts, the panel announces itself again.

Part 8: try it, and build it

19 Check it

Open Problems at the bottom: no errors and no warnings.

The finished Home screen.
The finished Home screen.

20 Preview

Press Preview. Every label reads its topic's sample payload, so you see the panel as it will look with real values. Press On: the Console says what the board would publish, and that the designer does not send it.

Preview: the house's values, and On's message in the Console.
Preview: the house's values, and On's message in the Console.
"ON" on house/light/porch/set - the board says it, the designer does not.
"ON" on house/light/porch/set - the board says it, the designer does not.

21 Generate the code

Press Export code. The dialog says which parts are Pro: here, Home Assistant discovery on Quiet mode and Panel message. Without Part 7 the design has no Pro part.

Export: Home Assistant discovery is the Pro part of this design.
Export: Home Assistant discovery is the Pro part of this design.

Generate, download, and build and upload it as Flashing and configuration describes. The MQTT client is the one in the ESP32's own core, so there is no library to install.

22 On the board

Press Wi-Fi, join your network, and the panel goes back to Home. Within a few seconds of joining it should connect to the broker: the retained values arrive at once and the labels fill in, and On and Off switch the porch light. To see the connection on the panel, the Broker tab's Connected to the broker has a + Variable you can bind a label or an LED to.

Part 9: devices that are not on MQTT, and more than one broker

A Hue bulb, a Nest, anything Home Assistant knows

Many devices reach Home Assistant some other way (Wi-Fi integrations, Zigbee through ZHA, cloud services) and say nothing on the broker, so the panel cannot hear them. Home Assistant can put them on the broker for you with MQTT Statestream, in configuration.yaml:

mqtt_statestream:
  base_topic: homeassistant_states
  include:
    entities:
      - light.kitchen
      - sensor.living_room_temperature

After a restart Home Assistant publishes each entity's state, retained, on homeassistant_states/light/kitchen/state and so on: on and off for a light, the number for a sensor. Grab again and they are there to import.

Statestream only speaks. To switch the kitchen light from the panel, give a button a Publish to MQTT on a topic of your own, panel/kitchen/set with ON or OFF, and an automation in Home Assistant that does it:

automation:
  - alias: Panel switches the kitchen light
    triggers:
      - trigger: mqtt
        topic: panel/kitchen/set
    actions:
      - action: "light.turn_{{ 'on' if trigger.payload == 'ON' else 'off' }}"
        target:
          entity_id: light.kitchen

The same can be made in the automation editor: trigger MQTT, action Light: Turn on.

Several brokers

The panel connects to one broker, and so does Home Assistant. If some of your kit talks to another broker, join that one into yours with a bridge: a few lines in the main broker's Mosquitto configuration that copy chosen topics both ways.

connection garage
address 192.168.1.50:1883
remote_username bridge
remote_password a-password
topic zigbee2mqtt/# both 0

Point the panel at the broker everything is bridged into. With the add-on, the lines go in a file in /share/mosquitto/ and the add-on's Customize option turns that folder on; see the add-on's own documentation.

When it does not work

Where next