At home I have a few Shelly H&T sensors. These sensors provide me with a regular update on the temperature and humidity in a room. They look pretty nice and are tiny, so they can be placed just about anywhere without drawing too much attention. They currently sell in a white/blue and a black/orange variant for about €24, with an optional USB-powered base replacing the CR123A battery sold for €5.
I have one of these on each floor for general temperature monitoring.

Recently, I came accross a video by Intermit.Tech (Quindor ) on setting up temperature en humidity monitoring using cheap $4 Xiaomi sensors. These sensors are also pretty tiny, but come with a LCD-screen displaying the sensor data. Their sensor is also pre-calibrated and the devices are optimized for running on battery power.
Because of their small size, and more importantly their small price, they’re ideal for per-room temperature monitoring.

Xiaomi LYWSD03MMC temperature and humidity sensor
Xiaomi LYWSD03MMC sensor

They send their data over Bluetooth Low-Energy (BLE) since they’re supposed to be used with a companion app. That’s where the ESP32 board comes in. We use this board (with BLE chip) to capture the data and import it using ESPHome.

Sadly enough, more recent models of the Xiaomi sensors come with a new firmware that encrypts the data send over bluetooth. Luckily Aaron Christophel came up with a nice way of hacking the firmware on these sensors. Not only does his hack provide a method of retrieving the bluetooth encryption key (allowing us to decrypt the data) but he even wrote his own firmware. Using Aaron’s ATC_MiThermometer firmware, you can also control the update interval, allowing you to choose sensor update frequency over battery life.

ATC MiThermometer by Aaron Christophel
ATC MiThermometer

In this blogpost I’ll explain how to flash the Xiaomi sensors with the Aaron’s custom firmware and how to get the sensor data into Home Assistant using an ESP32 and ESPHome.

Parts needed (shopping list)

For this tutorial, you’ll need:

Flashing the Xiaomi sensor

Note: To make identifying the correct Xiaomi device easier, make sure the others are out of bluetooth range or are turned off.
You may also want to label each sensor after it’s been flashed.

Note2: Flashing the custom firmware isn’t strictly necessary to get the sensor data imported into Home Assistant.
If you don’t feel like flashing the sensor, follow the guide below until step 5 and skip the rest of the flashing.

The flashing will be done from the browser a phone or tablet, since you need to connect to the sensor over bluetooth.
Use the default browser of the device or the Chrome browser. If it doesn’t work with one device, try it again on another.
I had issues trying this on a OnePlus and Firefox. It succeeded using a Samsung Galaxy Tab and its default browser.

  1. Get the ATC_MiThermometer.bin binary from Aaron’s GitHub and save it to your phone/tablet.

  2. Still on the GitHub page, at the top of the Readme documentation, you’ll find a link to the Web Flasher tool also written by Aaron. Open this link in the default/Chrome browser of your phone/tablet.

  3. On the Web Flasher page, check the Hide unsupported checkbox and click Connect.

  4. From the popup that should now have appeared, select the sensor you want to flash. The name should match the device ID from the shopping list above (e.g., LYWSD03MMC).
    Once connected, the “Temp/Humi” field – located about halfway the webpage – should be displaying sensor readings. At the bottom of the page, the logs should also show the device having connected.

  5. Press the Do activation button.
    A Mi Token and Mi Bind Key should appear a bit below the “Temp/Humi” field of the previous step.
    Note these down (or copy them into an email, Google Keep note, …) in a way that you can easily get to them from your computer/laptop.

    This token and key are what you need to import the Xiaomi sensor into Home Assistant without flashing the custom firmware. If you don’t want to flash the Xiaomi sensor with the custom firmware, stop here and continue with the next part.

  6. Below the “Do activation” button, there’s a Choose File button.
    Press this button and select the ATC_MiThermometer.bin you downloaded earlier.
    Make sure to select the correct file here, or you may/will brick the sensor!

  7. Press the Start Flashing button.
    You’ll see that status of the flashing appearing below the button. Don’t use the phone for now, it’ll only take a minute or so.

  8. Once the update is succesful, press the Connect button again and look for the flashed sensor.
    The device should now appear as ATC_xxxxxx. The logs at the bottom of the page should show you that you’ve connected to the device.

  9. Using the newly acquired features, we can now change some settings on the device.
    Note that the changes don’t always go through to the device, so press each button like 5 times to make sure it was set succesfully.

    1. set the Advertising Type to Mi Like
    2. set the Advertising Interval to your liking. Shorter interval means shorter battery life but less detailed measurements.

Flashing the ESP32

If you got a brand new ESP32 board, you’ll need to flash it once over USB to get it setup to work with ESPHome. Once this flashing is done, you’ll be able to push any future updates Over-The-Air (OTA).

ESPHome

We’ll be writing our code for the ESP32 in ESPHome . So make sure you’ve installed the ESPHome Add-on in Home Assistant.
The ESPHome website has a tutorial on installing this add-on in Home Assistant.

Flashing bluetooth discovery firmware

  1. In ESPHome, create a new device by clicking the big + button.
    The details you enter here don’t matter too much. Just make sure you give it a reasonable name (this will also be used to name the config file). E.g., livingroom_temp

  2. Now edit the device and overwrite the config with the following code. Change the back to what you originally named the device.
    You can enter the WiFi credentials directly in the config, or make use of the secrets file (see below).

    substitutions:
        esphome_name: livingroom_temp
        sensor_name: Living room
       
    esphome:
        name: ${esphome_name}
        platform: ESP32
        board: mhetesp32devkit
       
    wifi:
        ssid: !secret wifi_ssid
        password: !secret wifi_pass
       
    api:
        reboot_timeout: 60min
        password: !secret esphome_api_pass
       
    # Sync time with HA
    time:
        - platform: homeassistant
       
    # Optional
    #web_server:
    #	port: 80
       
    ota:
        password: !secret esphome_ota_pass
       
    logger:
       
    # Enable Bluetooth scanning for this ESP32
    esp32_ble_tracker:
    

    Save and Close the editor.

  3. Now click the 3 dots in the upper right corner of your device and click Compile.
    This will compile the code into a firmware binary that we can download to our computer.

  4. While the code is compiling, download and install the ESPHome-Flasher for your system. This software will be used for the initial flashing over USB. Future updates will be done Over-The-Air (OTA).

  5. We’ll now flash the compiled firmware to the ESP32.

    1. Connect the ESP32 using a micro-USB cable to your computer
    2. Open ESPHome-Flasher
    3. Click the refresh button at the top-right
    4. Select the COM port your ESP32 is connected to
    5. Browse to the firmware we’ve just compiled and download to our computer
    6. Click Flash ESP

    If the flashing software is unable to put the ESP32 in flash mode, hold down the Boot button on the ESP32 while it’s trying to flash.
    Once the flashing has started, you can let go of the Boot button.

  6. Once it’s done flashing, the ESP32 will connect to your WiFi network and you’ll be able to control the ESP32 Over-The-Air using ESPHome.

Flashing sensor firmware

You can now disconnect the ESP32 and install it where it will be able to receive the bluetooth data of your sensors. Power it using a phone charger, like the one in the Shopping List above.

  1. Back in the ESPHome dashboard, you can now click the LOGS button to view the output of the ESP32 board.
    In the past you’d see broadcast messages containing temperature and humidity information, including the device’s MAC address. Nowadays you’ll need to look for lines Found device XX:XX:XX:XX:XX:XX (...) Name: 'xxxxxxxxxx'. You’ll want to note down the MAC address of the ATC device (if flashed) or with the original name (e.g., LYWSD03MMC).
    BLE_tracker showing discovered devices
    BLE_tracker output
  2. We’ll now edit the device’s code again and add the sensor entities for the Xiaomi sensor(s) we’ll be monitoring.
    Open the config editor and paste the following code below the code we already had:
    switch:
        - platform: gpio
          name: "${esphome_name} - Onboard LED"
          pin: 2
          inverted: True
    
        - platform: restart
          name: "${esphome_name} - Restart"
          id: restart_switch
    
    sensor:
        - platform: uptime
          name: "${esphome_name} - Uptime Sensor"
    
        - platform: wifi_signal
          name: "${esphome_name} - WiFi Signal"
          update_interval: 60s
    
        - platform: xiaomi_lywsd03mmc
          mac_address: XX:XX:XX:XX:XX:XX # Replace with MAC Address of sensor
          bindkey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # Replace with bind key, not necessary with custom firmware
          temperature:
            name: "${sensor_name} - Temperature"
          humidity:
            name: "${sensor_name} - Humidity"
          battery_level:
            name: "${sensor_name} - Battery Level"
           
        # Add extra sensors by copying the 'platform: xiaomi_lywsd03mmc' block above.
        # Change MAC address, bindkey and sensor names
    
  3. Press the Upload bottom at the bottom right of the editor.
    This will save, compile and upload the new firmware to the ESP32 (OTA).

Storing WiFi creds and OTA/API passwords in secret file

Just like with Home Assistant, ESPHome has a secrets.yaml in which you can store stuff like WiFi credentials.
This allows you to keep secret data away from your config files and also adds consistency accross your devices.

To make things even easier, you can reference your Home Assistant secrets.yaml from the ESPHome’s one, allowing you to keep everything in one central location.

Create a file /config/esphome/secrets.yaml with the following content:

<<: !include ../secrets.yaml

Now in /config/secrets.yaml you can add your WiFi credentials etc.

wifi_ssid: "MyVerySecretWiFiNetwork"
wifi_pass: "MyverySecretWiFiPassword"
esphome_ota_pass: "SecretEsphomeOtaPass"
esphome_api_pass: "SecretEsphomeApiPass"

You can now reference these secrets from your config using e.g. ssid: !secret wifi_ssid.

Adding the sensors in Home Assistant

Adding the sensors to Home Assistant is really easy. If the sensor for some reason doesn’t show up yet in the guide below, give Home Assistant a reboot and wait a few minutes.

  1. In Home Assistant, go to Configuration > Integrations.
  2. If the ESP32 isn’t automatically discovered yet by Home Assistant, click the big + button to add a new integration.
    Choose ESPHome from the list.
  3. Enter the IP address or hostname of your ESP32 board, keep the port in its default value.
  4. Enter your API and/or OTA password.
  5. Add the device to a room if you wish to do so.

Within the Configuration page you should now see an entry for your device (livingroom_temp) under ESPHome.
Clicking this device will show you the different sensors for the temperature, humidity and battery level. You should also have a switch to turn on the onboard LED (useful to identify the device) and to reboot it. You may also have a sensor for the WiFi signal strength and device uptime.

It may take a while before sensor data will appear for the device.
Since the sensors are battery powered, it may take until a certain temperature change (or the next update interval).

ESPHome entities in Home Assistant
ESPHome entities in Home Assistant
Temperature readings
Temperature readings

Full ESP32 configuration

The complete configuration file looks like this:

substitutions:
	esphome_name: livingroom_temp
	sensor_name: Living room

esphome:
	name: ${esphome_name}
	platform: ESP32
	board: mhetesp32devkit

wifi:
	ssid: !secret wifi_ssid
	password: !secret wifi_pass

api:
	reboot_timeout: 60min
	password: !secret esphome_api_pass

# Sync time with HA
time:
	- platform: homeassistant

# Optional
#web_server:
#	port: 80

ota:
	password: !secret esphome_ota_pass

logger:

# Enable Bluetooth scanning for this ESP32
esp32_ble_tracker:

switch:
	- platform: gpio
	  name: "${esphome_name} - Onboard LED"
	  pin: 2
	  inverted: True

	- platform: restart
	  name: "${esphome_name} - Restart"
	  id: restart_switch

sensor:
	- platform: uptime
	  name: "${esphome_name} - Uptime Sensor"

	- platform: wifi_signal
	  name: "${esphome_name} - WiFi Signal"
	  update_interval: 60s

	- platform: xiaomi_lywsd03mmc
	  mac_address: XX:XX:XX:XX:XX:XX # Replace with MAC Address of sensor
	  bindkey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # Replace with bind key, not necessary with custom firmware
	  temperature:
	    name: "${sensor_name} - Temperature"
	  humidity:
	    name: "${sensor_name} - Humidity"
	  battery_level:
	    name: "${sensor_name} - Battery Level"
		
	# Add extra sensors by copying the 'platform: xiaomi_lywsd03mmc' block above.
	# Change MAC address, bindkey and sensor names