Building Custom Sensors for Home Assistant with Python

Introduction
Home Assistant has revolutionized the way we think about home automation, providing an open-source platform that integrates seamlessly with thousands of devices and services. But what happens when you want to monitor something that doesn't have a pre-built integration? This is where custom sensors come in.
In this comprehensive guide, I'll walk you through the process of building custom sensors for Home Assistant using Python, ESP32 microcontrollers, and MicroPython. By the end, you'll have the knowledge to extend your smart home's capabilities beyond off-the-shelf solutions.
Table of Contents
Prerequisites
Before we dive into building custom sensors, make sure you have the following:
Hardware Requirements
- ESP32 development board (ESP8266 works too, but with fewer features)
- DHT22 or BME280 temperature/humidity sensor
- Breadboard and jumper wires
- MicroUSB cable for programming
- Optional: 3D printed enclosure for permanent installation
Software Requirements
- Home Assistant running on your network
- MQTT broker (I recommend Mosquitto, which can be installed as a Home Assistant add-on)
- Thonny IDE or esptool for flashing MicroPython
- MicroPython firmware for ESP32
If you're new to some of these concepts, don't worry. I'll provide step-by-step instructions to get everything set up.
Hardware Setup
Let's start by connecting our temperature and humidity sensor to the ESP32. For this tutorial, I'll be using the DHT22 sensor, but the process is similar for BME280 or other I2C sensors.
DHT22 Connections:
DHT22 Pin | ESP32 Pin
------------------
VCC | 3.3V
GND | GND
DATA | GPIO 4
For a more reliable connection, add a 10kΩ pull-up resistor between the DATA and VCC pins. This helps ensure clean signal transmission.
Pro Tip
When building sensors for permanent installation, soldering the connections rather than using a breadboard will significantly improve reliability and reduce the chance of loose connections over time.
Once your hardware is connected, we need to flash MicroPython to the ESP32.
Programming with MicroPython
MicroPython is a lean and efficient implementation of Python 3 that's optimized for microcontrollers. It allows us to write clean, readable code while still maintaining good performance.
Flashing MicroPython
First, download the latest MicroPython firmware for ESP32 from the official website. Then, use esptool to flash it:
# Install esptool if you haven't already
pip install esptool
# Erase the flash memory first
esptool.py --port /dev/ttyUSB0 erase_flash
# Flash the MicroPython firmware
esptool.py --port /dev/ttyUSB0 --baud 460800 write_flash -z 0x1000 esp32-20220117-v1.18.bin
Replace /dev/ttyUSB0
with the appropriate port for your system and esp32-20220117-v1.18.bin
with the name of the firmware file you downloaded.
Sensor Code
Now, let's write the MicroPython code that will read from our sensor and publish the data to our MQTT broker. Create a file called main.py
on your ESP32:
import time
import dht
import machine
import network
import json
from umqtt.simple import MQTTClient
# Configuration
WIFI_SSID = "YourWifiName"
WIFI_PASSWORD = "YourWifiPassword"
MQTT_BROKER = "192.168.1.xxx" # IP address of your MQTT broker
MQTT_PORT = 1883
MQTT_USER = "your_mqtt_username"
MQTT_PASSWORD = "your_mqtt_password"
MQTT_CLIENT_ID = "esp32_sensor_1"
MQTT_TOPIC = "home/sensors/living_room"
SENSOR_PIN = 4
READING_INTERVAL = 60 # Seconds between readings
# Initialize sensor
sensor = dht.DHT22(machine.Pin(SENSOR_PIN))
# Connect to WiFi
def connect_wifi():
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if not wlan.isconnected():
print('Connecting to WiFi...')
wlan.connect(WIFI_SSID, WIFI_PASSWORD)
while not wlan.isconnected():
pass
print('Network configuration:', wlan.ifconfig())
# Read sensor data
def read_sensor():
try:
sensor.measure()
temp = sensor.temperature()
hum = sensor.humidity()
return {
'temperature': round(temp, 1),
'humidity': round(hum, 1),
'timestamp': time.time()
}
except Exception as e:
print('Error reading sensor:', e)
return None
# Publish to MQTT
def publish_reading(data):
if data is None:
return
try:
client = MQTTClient(MQTT_CLIENT_ID, MQTT_BROKER, MQTT_PORT,
MQTT_USER, MQTT_PASSWORD)
client.connect()
# Convert data to JSON string
json_data = json.dumps(data)
client.publish(MQTT_TOPIC, json_data, retain=True)
# Also publish to individual topics for easier Home Assistant integration
client.publish(f"{MQTT_TOPIC}/temperature", str(data['temperature']), retain=True)
client.publish(f"{MQTT_TOPIC}/humidity", str(data['humidity']), retain=True)
client.disconnect()
print('Published:', json_data)
except Exception as e:
print('Error publishing to MQTT:', e)
# Main loop
def main():
connect_wifi()
while True:
data = read_sensor()
publish_reading(data)
time.sleep(READING_INTERVAL)
# Start the program
if __name__ == "__main__":
main()
This code does the following:
- Connects to your Wi-Fi network
- Reads temperature and humidity from the DHT22 sensor
- Formats the data as JSON
- Publishes it to your MQTT broker
- Waits for the specified interval before reading again
Important Security Note
In a production environment, never hardcode credentials in your code. Instead, use a separate config file or environment variables.
MQTT Integration
MQTT (Message Queuing Telemetry Transport) is a lightweight messaging protocol that's perfect for IoT applications. It follows a publish-subscribe model where our sensors publish data to specific topics, and Home Assistant subscribes to those topics to receive the data.
MQTT Broker Setup
If you haven't already set up an MQTT broker, the easiest way is to install the Mosquitto add-on in Home Assistant:
- Go to your Home Assistant dashboard
- Navigate to Settings → Add-ons → Add-on Store
- Search for "Mosquitto broker" and install it
- Configure a username and password in the add-on config
- Start the add-on
Make note of your Home Assistant's IP address, as this will be the MQTT broker address you'll use in your ESP32 code.
MQTT Topic Structure
Organizing your MQTT topics in a logical hierarchy makes it easier to manage multiple sensors. I recommend a structure like this:
home/sensors/[location]/temperature
home/sensors/[location]/humidity
For example, home/sensors/living_room/temperature
would contain the current temperature reading from the living room sensor.
Home Assistant Configuration
Now that our ESP32 is publishing data to MQTT, we need to configure Home Assistant to use this data.
MQTT Integration
First, make sure the MQTT integration is enabled in Home Assistant:
- Go to Settings → Devices & Services
- Click Add Integration and search for "MQTT"
- Enter the broker details (typically localhost:1883 if using the Mosquitto add-on)
- Enter the username and password you configured for the MQTT broker
Sensor Configuration
Next, we'll create sensors in Home Assistant that subscribe to our MQTT topics. Add the following to your configuration.yaml
file:
# MQTT Sensors
sensor:
- platform: mqtt
name: "Living Room Temperature"
state_topic: "home/sensors/living_room/temperature"
unit_of_measurement: "°C"
device_class: temperature
value_template: "{{ value | float }}"
- platform: mqtt
name: "Living Room Humidity"
state_topic: "home/sensors/living_room/humidity"
unit_of_measurement: "%"
device_class: humidity
value_template: "{{ value | float }}"
After saving the configuration, restart Home Assistant to apply the changes.
Creating Dashboard Cards
Finally, let's add these sensors to our Home Assistant dashboard:
- Go to Overview and edit your dashboard
- Click + Add Card
- Select Entities card type
- Add both your temperature and humidity sensors
- Customize the card title and appearance as desired
For a more visual representation, you might prefer using a Gauge card for temperature and humidity individually.
Advanced Features and Customizations
Once you have the basic sensor setup working, there are several ways to enhance and extend your system:
Adding Battery Monitoring
If your sensor is battery-powered, you can add battery level monitoring:
# Add to your main.py
ADC_PIN = 35 # GPIO pin connected to battery monitoring circuit
BATTERY_MAX = 4.2 # Maximum battery voltage
BATTERY_MIN = 3.0 # Minimum battery voltage
def read_battery_level():
# ESP32 ADC reference voltage is 3.3V and resolution is 12-bit (0-4095)
adc = machine.ADC(machine.Pin(ADC_PIN))
adc.atten(machine.ADC.ATTN_11DB) # Full range: 0-3.3V
# Read 10 samples and average
readings = [adc.read() for _ in range(10)]
avg_reading = sum(readings) / len(readings)
# Convert ADC reading to voltage
voltage = avg_reading / 4095 * 3.3
# If using a voltage divider, apply the conversion factor
# voltage = voltage * 2 # example for a 1:1 voltage divider
# Calculate percentage
percentage = 100 * (voltage - BATTERY_MIN) / (BATTERY_MAX - BATTERY_MIN)
percentage = max(0, min(100, percentage)) # Clamp between 0-100%
return round(percentage, 1)
Then publish this value to MQTT along with your other sensor readings.
Deep Sleep for Battery Saving
For battery-powered sensors, implementing deep sleep significantly extends battery life:
import esp32
# Instead of time.sleep() in the main loop
def main():
connect_wifi()
while True:
data = read_sensor()
data['battery'] = read_battery_level()
publish_reading(data)
# Go to deep sleep instead of using time.sleep
print(f"Going to deep sleep for {READING_INTERVAL} seconds")
esp32.wake_on_timer(READING_INTERVAL * 1000) # Time in milliseconds
esp32.deep_sleep()
Multiple Sensors on One ESP32
You can connect multiple sensors to a single ESP32 by assigning different GPIO pins and creating additional sensor objects in your code.
OTA Updates
For sensors placed in hard-to-reach locations, implementing Over-The-Air (OTA) updates is invaluable. MicroPython has libraries for this, but it's beyond the scope of this tutorial.
Troubleshooting Common Issues
Sensor Not Showing in Home Assistant
Possible causes:
- MQTT topics in Home Assistant don't match what the ESP32 is publishing
- ESP32 not connected to WiFi or MQTT broker
- MQTT broker credentials incorrect
Solution: Check the MQTT logs in Home Assistant to see if messages are being received. Use an MQTT client like MQTT Explorer to monitor topics and messages directly.
Inconsistent or Incorrect Readings
Possible causes:
- Sensor placement near heat sources or in direct sunlight
- Poor wiring connections
- Sensor quality issues
Solution: Place sensors away from heat sources, ensure all connections are secure, and consider using a higher-quality sensor like BME280 instead of DHT22 for more accurate readings.
ESP32 Crashes or Resets Frequently
Possible causes:
- Power supply issues
- Memory leaks in code
- Hardware issues
Solution: Use a stable power supply, implement error handling and watchdog timers in your code, and check for hardware defects.
Conclusion
Building custom sensors for Home Assistant opens up endless possibilities for home automation and monitoring. Whether you're tracking environmental conditions, monitoring energy usage, or creating specialized sensors for unique needs, the combination of ESP32, MicroPython, MQTT, and Home Assistant provides a powerful, flexible foundation.
The approach outlined in this tutorial is just the beginning. As you become more comfortable with these technologies, you can expand your system to include more sensors, more complex data processing, and integrations with other systems.
Remember, the key to reliable sensor networks is thoughtful planning, clean code, and careful attention to power management and connectivity issues.
Happy building! If you create any interesting sensors using this guide, I'd love to hear about them.
Comments (0)