Have you ever bought a sensor that looked fancy, put it in your drawer and kept it there for ages? At least for me I can say I keep buying sensors in the internet without thinking about use cases, setup or integration beforehand, so they end up in a drawer until some day "when I have time".
But what if there was an easy and re-usable approach to integrate any sensor in Home Assistant? - Actually, there is!
With MicroPython and Home Assistant's MQTT Discovery integrating sensors in your smart home and hence using them productively is easier than ever before!
I know, I have used this approach for some projects already, for example in the AI baby cry detector and the office presence sensor.
In this article I am providing a step-by-step guide for integration of any MicroPython-compatible sensor into Home Assistant.
What you needFirst, hardware of course, a MicroPython-compatible microcontroller and some sensors... Air quality, temperature, humidity, light,...you name it!
Here's a selection of hardware I had laying around for this project:
- Analog light sensor with photoresistor
- Infineon PASCO2V15 - 5V version of Infineon's CO2 sensor
- Adafruit SHT30 - Humidity and Temperature sensor
- Infineon DPS368 Shield/Kit2Go - Atmospheric pressure sensor
- PSOC™ 6 Prototyping Kit - MicroPython-compatible microcontroller
- ...note that these are just examples, any sensor which can be read out using MicroPython can also be integrated into Home Assistant with this approach.
Second, software! MicroPython and the uhome module, which helps us integrating our sensors in Home Assistant.
- Get started with MicroPython on PSOC
- uhome module (installation & usage instructions below)
First, we will write some code to read out the sensor's values and use the uhome module to let these values appear in Home Assistant.
Every sensor's value will become an "entity" in our Home Assistant instance, and every entity will be assigned to a "device". In this article, the device will be the microcontroller board we are using. One MCU could also define multiple devices, but for this simple sensor integration use case this would go too far...
Prerequisite: Home Assistant & MQTT BrokerThis article focuses on integration of sensors into an existing Home Assistant instance, you need to have the following ready to continue: A MQTT broker, a Home Assistant instance with the MQTT integration and of course a Wi-Fi network.
Here some useful links to get everything working:
- Home Assistant installation: https://www.home-assistant.io/installation/
- MQTT broker & integration: https://www.home-assistant.io/integrations/mqtt/
That's it! If you have Home Assistant, a MQTT broker and the MQTT integration running, you're ready to continue.
Getting Started - WiFi Connection & Sensor DriversBefore we can look into the Home Assistant integration part we need to make sure to have all sensor drivers and a WiFi connection in place. Let's start by connecting to our WiFi network:
import network
import wifi_secrets, mqtt_secrets
sta = network.WLAN(network.STA_IF)
sta.connect(wifi_secrets.ssid, wifi_secrets.psk)
Note: I am using "secret" files for passing WiFi credentials and other personal information (like the MQTT credentials later on). Feel free to replace these variables by hardcoded credentials in your code or make use of the attached "*_secrets.py" files and modify them.
Now we can download all required sensor drivers (of course this step will vary depending on the sensors you selected):
# mip module ("mip installs packages")
import mip
# Adafruit SHT30
mip.install('github:ederjc/micropython-sht30/sht30.py')
import sht30
# Infineon PAS CO2
mip.install("https://raw.githubusercontent.com/jaenrig-ifx/micropython-lib/mpy-lib/pasco2-sensor-module/micropython/drivers/sensor/pasco2/pasco2.py")
import pasco2
# Infineon DPS368
mip.install("github:seanwagoner/MiPy/DPS.py")
import DPS
MQTT & uhomeNow we're ready to install the MQTT client (umqtt) and Home Assistant Discovery (uhome) module:
# umqtt
mip.install('umqtt.simple')
import umqtt.simple
# uhome
mip.install('github:ederjc/uhome/uhome/uhome.py')
import uhome
Ready to integrateNow we can make use of the uhome module, which helps us creating & handling the configuration messages for Home Assistant's MQTT Auto Discovery. If you want to learn more about Auto Discovery, have a look into this article, where I show how to manually set everything up without using the uhome module.
Entities & Devices in Home AssistantTo get started, we first need to understand the difference between entities and devices in Home Assistant. A device is usually a physical device, while an entity can be a sensor value, button, etc. of this physical device. Therefore, a device can have multiple entities. In this project, the device will be the PSOC™ 6 Prototyping Kit, while entities will be the measurement values of the connected sensors and diagnostic values, like the WiFi signal strength, current IP address, etc. You can read up more about Home Assistant's device registry here.
Setup with uhomeTo setup our device using the uhome module, we can simply create a new object of the device class:
device = uhome.Device('MicroPython Sensornode', mf='Infineon', mdl='PSOC 6')
This device needs to know how to communicate, so we need to create a MQTT client object using the umqtt module and pass this object to the device.connect() method:
mqttc = umqtt.simple.MQTTClient(device.id, mqtt_secrets.broker, user=mqtt_secrets.user, password=mqtt_secrets.password, keepalive=60)
device.connect(mqttc)
Now we can create a uhome.Sensor
object for each measurement value:
temperature = uhome.Sensor(device, 'Temperature', device_class="temperature", unit_of_measurement='°C')
humidity = uhome.Sensor(device, 'Relative Humidity', device_class="humidity", unit_of_measurement='%')
co2 = uhome.Sensor(device, 'CO2', device_class="carbon_dioxide", unit_of_measurement='ppm')
atmospheric_pressure = uhome.Sensor(device, 'Atmospheric Pressure', device_class="pressure", unit_of_measurement='Pa')
brightness = uhome.Sensor(device, 'Brightness', device_class="illuminance", unit_of_measurement='lx')
You'll need to adapt this part according to the sensors you're using.
When we're interested in diagnostic entities like network information etc. we can also add these:
signal_strength = uhome.Sensor(device, 'Signal Strength', device_class="signal_strength", unit_of_measurement='dBm', entity_category="diagnostic")
wifi_ch = uhome.Sensor(device, 'WiFi Channel', device_class="enum", entity_category="diagnostic")
reset_cause = uhome.Sensor(device, 'Last Reset Cause', device_class="enum", entity_category="diagnostic")
ip_addr = uhome.Sensor(device, 'IP Address', device_class="enum", entity_category="diagnostic")
More information on Home Assistant MQTT sensors:
- https://www.home-assistant.io/integrations/sensor.mqtt/
- https://www.home-assistant.io/integrations/sensor#device-class
Now that we have added the entities we need to "report" it to Home Assistant by sending a configuration message. In the uhome module this can be done for all entities at once by calling this command:
device.discover_all()
Yes, it's that easy! The device and entity already show up in Home Assistant now :)
As you can see, the sensor entities show up, but the measurement values are not available, yet. Let's change that by reporting the values to Home Assistant.
Publishing Sensor ValuesIn order to make the sensor values available for Home Assistant we need to connect and set up the different sensors first:
Sensor setup:
import machine
# I2C interface for PAS CO2, SHT30, DPS368
i2c = machine.I2C(scl='P6_0', sda='P6_1', freq=400000)
# I2C sensors
sht = sht30.SHT30(i2c, i2c_address=sht30.ALTERNATE_I2C_ADDRESS)
dps = DPS.DPS(i2c)
pasco2 = pasco2.PASCO2(i2c, measInterval=60)
pasco2.initialize()
# Light sensor on ADC
light_adc = machine.ADC('P10_3')
After the sensors are set up we're ready to read out the measurement values and publish them to Home Assistant. For this, I'll set up some callback functions because I want to trigger the sensor updates from interval timers later on:
import time
def publishDiagnostics(tmr=None):
"""
Helper function to publish variable diagnostic values.
"""
signal_strength.publish(f'{sta.status('rssi'):.0f}')
wifi_ch.publish(f'{sta.config('channel')}')
reset_cause.publish(f'{machine.reset_cause()}')
ip_addr.publish(f'{sta.ifconfig()[0]}')
diagnostics_tmr = machine.Timer(0, mode=machine.Timer.PERIODIC, period=30000, call back=publishDiagnostics)
def publishSHT(tmr=None):
'''
Callback function for retrieving
brightness value from ADC and publishing
to Home Assistant.
'''
temp, hum = sht.measure()
print(f'SHT30:\tTemperature:\t{temp} °C')
print(f'SHT30:\tHumidity:\t{hum} %RH')
temperature.publish(f'{temp:.1f}')
humidity.publish(f'{hum:.0f}')
def publishCO2(tmr=None):
'''
Callback function for retrieving
CO2 measurement and publishing to Home Assistant.
'''
for i in range(10):
co2ppm = pasco2.get_co2_value()
if co2ppm != -1:
co2.publish(f'{co2ppm:.0f}')
print(f'PASCO2:\tCO2 value:\t{co2ppm} ppm')
break
time.sleep(1)
def publishDPS(tmr=None):
'''
Callback function for retrieving
pressure measurement and publishing
to Home Assistant.
'''
p = dps.measurePressureOnce()
atmospheric_pressure.publish(f'{p:.2f}')
print(f'DPS368:\tPressure:\t{p} Pa')
def publishLight(tmr=None):
'''
Callback function for retrieving
brightness value from ADC and publishing
to Home Assistant.
This value is not actually in lux but the raw
value from ADC.
'''
light_value = 32767 - light_adc.read_u16()
print(f'Light:\tAnalog raw:\t{light_value}')
brightness.publish(f'{light_value}')
Note that all these callback functions take one optional argument. This is because the timer object will be passed as an argument by the timer itself. We do not make further use of this argument, but need to allow the function to take it.
If we now call all of these functions all entities will show up in Home Assistant with the correct measurement values. However, I am not calling the functions directly but adding them as callback functions to interval timers. With this approach I can control the publish interval for each of the sensors while the main loop stays clean:
diagnostics_tmr = machine.Timer(0, mode=machine.Timer.PERIODIC, period=30000, callback=publishDiagnostics)
sht_tmr = machine.Timer(1, mode=machine.Timer.PERIODIC, period=32000, callback=publishSHT)
dps_tmr = machine.Timer(2, mode=machine.Timer.PERIODIC, period=3000, callback=publishDPS)
co2_tmr = machine.Timer(3, mode=machine.Timer.PERIODIC, period=60000, callback=publishCO2)
light_tmr = machine.Timer(4, mode=machine.Timer.PERIODIC, period=19000, callback=publishLight)
Finally, we need to add a main loop, so the program keeps running and uhome can take care of MQTT messages, pings, etc. in the background:
while True:
if not sta.isconnected(): sta = sta.connect(wifi_secrets.ssid, wifi_secrets.psk)
device.loop() # Handle all device specific tasks (mandatory).
To be on the safe side I am also checking for the WiFi connection in each loop iteration and reconnecting if it's lost.
Have a look at the attachments to see the full code in one file.
When we execute the code on the device the sensors now show up in Home Assistant with measurement values:
Let me know in the comments which sensors you're gonna integrate into Home Assistant :)
Comments
Please log in or sign up to comment.