At home, I have Home Assistant installed, which I use to monitor several sensors — such as door opening, motion, temperature, and humidity sensors — and also to control many lights throughout the house. In one of the rooms, which I use as a workshop and where I store items such as paints and solvents, I also have an air quality sensor that measures different environmental variables.
Although I can check these measurements from the Home Assistant dashboard on a computer or on my phone, I thought it would be much more practical to have a dedicated, always-visible panel that would let me check the room conditions at a glance, at any time.
For this type of application, I thought an ePaper or EPD — Electronic Paper Display — would be the perfect choice for the panel I had in mind. This type of display is ideal for showing information that does not change too frequently. It has very low power consumption, keeps the image visible even without power thanks to its bistable behavior, and offers excellent readability even in bright environments.
The panel also takes advantage of a very useful feature of Home Assistant: it provides different mechanisms that allow other boards and devices to interact with the information from the sensors it monitors. Using this feature, the panel can request values from the air quality sensor and display them on the ePaper screen.
In the following sections, I will show you how to use this Home Assistant functionality and how to build an information panel based on the Seeed Studio EE05 board, programmed with Seeed_GFX in Arduino.
Measuring Air QualityIndoor environmental conditions in a home, school, or workplace directly affect the health, well-being, and comfort of the people who spend several hours there.
There are different types of air quality meters, with varying levels of quality and accuracy, that measure different parameters related to air quality.
In my case, the meter I have installed is not a professional-grade device and does not provide high-precision measurements. On the contrary, it is a low-cost device, but it is still useful as a general reference for evaluating the air quality of the room.
Although these values should not be interpreted as professional measurements, they do provide a useful reference for the general state of the environment. In this way, the sensor helps detect changes, identify when it is convenient to ventilate the room, and make better decisions to maintain a healthier and more comfortable space.
Connecting to Home AssistantThe air quality sensor I have installed is compatible with Tuya (also known as Smart Life in some countries) and integrates with Home Assistant through the Tuya integration, using my home WiFi network. In this way, the sensor sends its measurements to the Tuya platform, and Home Assistant can include them as entities within the system.
In Home Assistant, the sensor has five main associated entities, which correspond to the values measured or calculated by the device. Their names or identifiers are:
sensor.calidad_de_aire_temperature
sensor.calidad_de_aire_humidity
sensor.calidad_de_aire_carbon_dioxide
sensor.calidad_de_aire_formaldehyde
sensor.calidad_de_aire_volatile_organic_compounds“calidad de aire” means “air quality” in Spanish. It is the original name of the sensor in my Home Assistant installation.
The values of these entities can be viewed from the Home Assistant dashboard, where each one is displayed as an individual card. This is a view of my custom dashboard, which shows the air quality data and the lighting status of that room — my workshop.
This is the usual way to view Home Assistant entity values, but how can we display them on an external device, such as a panel with an ePaper screen?
Each of these mechanisms has its own characteristics, advantages, and disadvantages. For this project, we will use the first one: the REST API, because besides being simple to use, it is also extremely powerful.
REST APIA REST API is a technology used in web application development that allows access to a site for exchanging information. It uses the HTTP protocol and some of its methods — such as GET or POST — to request or send information.
To access the REST API, we must use a specific URL or address called an endpoint. By checking the Home Assistant documentation, we can find the corresponding endpoint:
http://IP_ADDRESS:8123/api/Where IP_ADDRESS is the same IP address used to access the main dashboard of the Home Assistant installation.
HTTP requests can be sent to this endpoint using methods such as GET — for example, to read a sensor value — POST, to set the value of an entity, and DELETE, to delete an entity.
To access the main Home Assistant dashboard from a browser, you can use the address:http://homeassistant.local:8123as long as the local network can resolve that name. In some cases, especially from computers or devies connected through WiFi, that address may not work. In that case, you should use the IP address of the device where Home Assistant is installed, for example:http://192.168.100.142:8123
In all cases, the information sent and received is in JSON format. In addition, to restrict access to Home Assistant, each HTTP request must include an Authorization: Bearer TOKEN header, where the token is a unique identifier that identifies us as an authorized user.
The token — called a Long-Lived Access Token — must be created from the user profile page in the Home Assistant administration panel: http://IP_ADDRESS:8123/profile/security
Now let's take a closer look at how to make GET requests to Home Assistant and what format it uses to return the response.
If we make a GET request to the URL:
http://IP_ADDRESS:8123/api/states/<entity_id>Home Assistant will return a JSON object with the state information of the entity named <entity_id>.
This can be tested easily from the command line using curl. For example, if the entity is the temperature value measured by the sensor, sensor.calidad_de_aire_temperature, the curl command to request its value has this format on Windows:
curl -H "Authorization: Bearer --token--" -H "Content-Type: application/json" http://192.168.100.142:8123/api/states/sensor.calidad_de_aire_temperatureWhere --token-- is the long-lived access token generated from the administration panel, as explained earlier.
This is the response returned by Home Assistant:
In a more readable format, the response looks like this:
{
"entity_id": "sensor.calidad_de_aire_temperature",
"state": "20.2",
"attributes": {
"state_class": "measurement",
"unit_of_measurement": "°C",
"device_class": "temperature",
"friendly_name": "Calidad de aire Temperature"
},
"last_changed": "2026-05-29T23:15:39.702639+00:00",
"last_reported": "2026-05-29T23:15:39.702639+00:00",
"last_updated": "2026-05-29T23:15:39.702639+00:00",
"context": {
"id": "01KSV0B71PNQ42C4HZ22FTA4YG",
"parent_id": null,
"user_id": null
}
}Inside this JSON object, there is a lot of information. For this particular project, we are only interested in the state field, which contains the measured parameter value and is what we will display on the ePaper screen.
Although it is possible to get the state of all entities with a single GET request, the JSON response can be too large if there are many sensors and devices connected. In this project, I chose to send five requests, one for each entity associated with the air quality sensor.The ePaper Panel
Now that we have seen how to retrieve entity values from Home Assistant, let’s get to work and build the ePaper display panel. First, we will look at the components used in the project, and then we will review the software that makes everything work together.
The ePaper Display
For this project, I chose a 4.26-inch monochrome ePaper display from Seeed Studio. One of the main reasons was its excellent 800 × 480 pixel resolution, which makes it possible to display information with great sharpness and contrast. In addition, its low power consumption and fast refresh time make it an ideal choice for information panels, e-readers, and other portable devices.
The EE05 Board
To control the 4.26-inch display, I will use an EE05 controller board, also from Seeed Studio. The EE05 is an ePaper display driver board with features similar to the EE04, but in a more compact form factor. It is based on the powerful XIAO ESP32-S3 Plus module and integrates WiFi and Bluetooth connectivity, a battery connector, a charging management circuit, and a 24-pin interface for compatible ePaper displays.
In addition to controlling the display, this same board will also be responsible for connecting to Home Assistant and retrieving the values measured by the air quality sensor. Taking advantage of the WiFi connectivity and processing power of the XIAO ESP32-S3 Plus, the EE05 will send the necessary requests to Home Assistant, obtain the state of the five corresponding entities, and then display that data on the connected ePaper screen.
The Assembled Panel
Below are two images of the panel. The first one shows it before assembly, and the second one shows it while testing the first enclosure prototype.
The panel program is developed in Arduino using the Seeed_GFX library, a library created by Seeed Studio and optimized to support its ePaper displays and controller boards, such as the EE05.
The complete code has been added to the article. Now, let’s analyze its different sections in more detail:
The includes
At the beginning, we have the #include directives, which are used to load the different libraries: WiFi for the WiFi connection, TFT_eSPI (Seeed_GFX) to control the ePaper display, HTTPClient to send requests to the server, ArduinoJson to handle the server responses, and image, which is the file that contains the definition of the image used as the panel background.
#include <WiFi.h>
#include "TFT_eSPI.h"
#include <HTTPClient.h>
#include <ArduinoJson.h> // Used to parse JSON
#include <esp_sleep.h> // For deep sleep
#include "image.h" // Background imageThe Background Image
The panel background image was created as a 16-level grayscale BMP, like the one shown below.
Then I converted this BMP into a header file using the SenseCraft HMI dithering tool. With this tool, I first reduced the number of colors to 1 bpp (bit per pixel) by applying the Floyd-Steinberg dithering algorithm, and then generated the header file containing the image definition.
This is a fragment of the generated header file:
The Driver
Although it does not appear explicitly in the #include directives, the Seeed_GFX library requires us to create a driver.h file with the definitions of the ePaper display and the controller board being used. For this, I used the online tool provided by Seeed Studio:
Then I copied the generated code into a new file named driver.h:
#define BOARD_SCREEN_COMBO 506 // 4.26 inch monochrome ePaper Screen (SSD1677)
#define USE_XIAO_EPAPER_DISPLAY_BOARD_EE05If you want to learn more about using monochrome and color images on EPD displays, check out this other project.
Declarations
In this section, the epaper object is created and associated with the display. Then, several constants are defined: the username and password of the WiFi network to be used, the panel refresh time — that is, how often it connects to Home Assistant to update the data — and the token required for authentication.
EPaper epaper;
// WiFi network name and password
const char* ssid = "--your SSID--";
const char* password = "--your Password--";
// Refresh time (minutes)
const int RefreshTime = 15;
// Token provided by Home Assistant
const char* Token = "PASTE_YOUR_HOME_ASSISTANT_LONG_LIVED_ACCESS_TOKEN_HERE";Functions
Next, the code defines several functions.
Perhaps the most important function in the program is getHAState(), since it is responsible for querying the Home Assistant REST API to obtain the state of a specific entity.
The function receives the entity name as a parameter, for example sensor.calidad_de_aire_temperature, and uses that information to build the corresponding endpoint URL. It then checks whether the board is connected to the WiFi network and sends an HTTP GET request to Home Assistant.
For the request to be accepted, it includes the access token through the Authorization header. If Home Assistant responds correctly, the function receives a JSON object, parses it, and looks for the state field, which contains the current value of the entity.
Finally, it returns that value as a String. If an error occurs, such as a missing WiFi connection, an HTTP request error, or a problem parsing the JSON, the function returns "--".
// Function to get states from Home Assistant
String getHAState(String entity_id) {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("Error! WiFi not connected");
return "--";
}
WiFiClient client;
HTTPClient http;
String url = "http://192.168.100.142:8123/api/states/" + entity_id;
Serial.println();
Serial.print("Requesting: ");
Serial.println(url);
http.setTimeout(5000); // 5 seconds
if (!http.begin(client, url)) {
Serial.println("Error! http.begin() failed");
return "--";
}
http.addHeader("Authorization", String("Bearer ") + Token);
http.addHeader("Content-Type", "application/json");
int httpCode = http.GET();
Serial.print("HTTP code: ");
Serial.println(httpCode);
if (httpCode != HTTP_CODE_OK) {
String errBody = http.getString();
Serial.println("Error body:");
Serial.println(errBody.substring(0, 200));
http.end();
return "--";
}
String payload = http.getString();
Serial.print("Payload length: ");
Serial.println(payload.length());
http.end();
JsonDocument doc;
DeserializationError error = deserializeJson(doc, payload);
if (error) {
Serial.print("JSON parse error: ");
Serial.println(error.c_str());
return "--";
}
String value = doc["state"].as<String>();
Serial.print("State value: ");
Serial.println(value);
return value;
}Preparations
In this part of the code, the setup() function begins. Here, some initial preparations are made: the serial console is initialized for debugging, the board connects to the WiFi network, and the ePaper display is prepared.
Several display parameters are configured, and the background image is loaded into the driver memory, although it is not shown yet.
void setup() {
Serial.begin(115200);
Serial.println ("\nStarting..");
if (!connectWiFi(30000)) {
Serial.println("WiFi did not connect. Going to sleep for 5 min.");
sleepSeconds(300);
return;
}
Serial.println("WiFi connected!");
// Configure and initialize display
epaper.begin();
epaper.fillScreen(TFT_WHITE); // Clear screen
epaper.setTextFont (6);
epaper.setRotation(0);
epaper.setTextDatum(MC_DATUM); // Set centered alignment
epaper.setTextSize (1);
// Load background image
epaper.drawBitmap(0, 0, background, 800, 480, TFT_BLACK);Reading Values and Displaying Them
In this final block, five requests are sent to the REST API to retrieve the values of the five entities associated with the air quality sensor, using the getHAState() function defined earlier.
Then, these values are loaded onto the screen. The epaper.update() method performs a full refresh, displaying both the background image and the entity values.
// Access Home Assistant REST API
String temp = getHAState("sensor.calidad_de_aire_temperature");
String hum = getHAState("sensor.calidad_de_aire_humidity");
String co2 = getHAState("sensor.calidad_de_aire_carbon_dioxide");
String hcho = getHAState("sensor.calidad_de_aire_formaldehyde");
String voc = getHAState("sensor.calidad_de_aire_volatile_organic_compounds");
epaper.drawString (temp, 90, 290);
epaper.drawString (hum, 244, 290 );
epaper.drawNumber (co2.toInt(), 398, 290);
epaper.drawString (hcho, 556, 290 );
epaper.drawString (voc, 710, 290 );
epaper.update ();Going to Sleep…
Once the screen refresh is complete, the EE05’s ESP32-S3 is put into low-power mode for a period defined by the RefreshTime value, in minutes. Thanks to the bistable nature of ePaper, the information remains unchanged until the next update, with minimal power consumption.
The 10-second delay is useful while working on the program, because it allows you to regain control of the board from the IDE before it enters low-power mode.
And then it goes to sleep.
Serial.println ("Going to sleep...");
// Delay to allow access from the IDE if necessary.
delay (10000);
sleepSeconds (RefreshTime * 60);That’s it. The loop() function does nothing.
In this project, we built a simple but very useful panel to view, at a glance, the values measured by an air quality sensor integrated with Home Assistant. Based on this data, it is possible to get a quick reference of the general condition of a room and detect when it may be necessary to ventilate or improve its conditions.
Throughout the article, we first looked at which parameters the sensor measures and how these values appear inside Home Assistant as independent entities. Then, we reviewed some of the available options for accessing this information from external devices, focusing especially on the use of the REST API, sending HTTP GET requests to the corresponding endpoints and using an authorization token to securely access the data.
We also analyzed the criteria used to select the hardware for this project. The ePaper display is ideal for this type of information panel, as it offers excellent readability, low power consumption, and the ability to keep the image visible even without power. At this point, the EE05 board plays a central role: it not only acts as the controller for the 4.26-inch ePaper display, but also, thanks to the integrated XIAO ESP32-S3 Plus module, provides WiFi connectivity, processing power, battery management, and a compact form factor that is especially practical for developing this type of application.
Finally, we reviewed in detail the software developed in Arduino with Seeed_GFX, from the WiFi connection and the query to Home Assistant, to the processing of the JSON response, the extraction of the state field, the screen update, and the use of low-power mode.
The result is a dedicated, autonomous, and easy-to-check panel that combines the possibilities of Home Assistant with the advantages of ePaper displays and the practicality of the EE05 board. In addition to solving a specific need, this project shows a simple way to create physical interfaces for visualizing smart home information without always depending on a computer or a phone.
I hope you enjoyed this project and learned something new. If you did, please share it.
If you have any questions or suggestions, feel free to leave them in the comment section below.
For more information and projects, you can check out my blog and social media.
See you next time! 🚀








Comments