Air conditioning is quite uncommon in many parts of Europe, my apartment is on the southwest side of the building and I have big windows. It’s great to have a bright and friendly room, but that also means it can become quite hot during the summer. Being a geek and interested in monitoring, having an ordinary thermometer wasn’t enough for me.

I didn’t just want to know the current temperature right now, but also to be able to compare different spots and have some more historical data to play around with (e.g. finding the coolest room to work at a certain time, the best time for venting a room without letting too much heat in, etc).

So I bought three Shelly H&T humidity and temperature sensors. There may be cheaper sensors out there (the Shellys are around 25€ incl. VAT), but they come with some nice benefits:

Having the size of a golf ball, they also don’t look too shabby in your room.

Shelly H&T sensor (outside and inside view)

So my plan was to use these sensors to collect and analyze data over a longer period of time. Since I already run a local Prometheus instance, all I needed was to get the data into it.

Setting up the Shelly H&T sensors

  1. After inserting the battery into the sensor, if the red LED is flashing slowly, the device is in Access Point mode. Change the wireless connection to the Shelly H&T Wi-Fi (shellyht-<alphanumeric ID>), then enter http://192.168.33.1 in the browser to get to the configuration UI.

  2. Add your Wi-Fi name and password under Internet & Security > Wi-Fi Mode - Client. Only 2.4GHz networks seem to work here.

  3. Switch back to your own Wi-Fi, the router should have assigned the sensor a new IP address. Use it to access the configuration UI again.

  4. I highly recommend going to Settings > Firmware Update first and get the latest firmware release. (The one shipped with my sensor dated back to 2018.) Make sure you disable any JavaScript blockers in the browser during the firmware update.

Screenshot of Shelly H&T web UI

Tips

To conserve battery power as much as possible, the sensor switches into sleep mode after a short while (LED is off). This also turns off the Wi-Fi connection, which can be a bit annoying during setup. To wake the sensor up again, press the button next to the battery until the LED starts flashing again.

Before you place the sensors in your home, I'd recommend leaving them lying next to each other for a couple of hours. Because of production variances, a sensor value can be a bit off sometimes. For Shelly H&T sensors, you can adjust this on the web UI under Sensor Settings > Temperature offset and Humidity offset.

Setting up an MQTT broker

Next, we need an MQTT broker to send the sensor data to. I went for Mosquitto, as it works well out of the box and comes with a Docker image. My mosquitto.conf file consists only of two lines:

persistence true
persistence_location /mosquitto/data

Then start the Docker container:

docker run -d \
-p 1883:1883 -p 9001:9001 \
-v mosquitto.conf:/mosquitto/config/mosquitto.conf:ro \
-v mosquitto_data:/mosquitto/data \
--name mosquitto \
eclipse-mosquitto

Now we can configure the sensor to send its data to the broker. You can either use the configuration UI or send a request directly to the API endpoint:

curl http://<Shelly IP address>/settings?
mqtt_id=shellyht-livingroom&
mqtt_enable=true&
mqtt_server=<Mosquitto IP address>:1883&
mqtt_retain=true

By default, the mqtt_id is identical to the Wi-Fi name. To make it easier to distinguish the sensors, I’d recommend a custom name.

And as I don’t plan to use the Shelly Cloud service, I disabled it as well:

curl http://<Shelly IP address>/settings/cloud/?enabled=0

To test the MQTT setup, I can recommend MQTT Explorer, a client that provides a structured overview of MQTT topics:

Screenshot of MQTT explorer showing a tree view of existing topics and its values

Getting MQTT data into Prometheus

I tried various MQTT exporters but all had some issues. In the end I took the most promising one and modified it to suit my needs. You can find the source code on GitHub as well as a Docker image.

Again, we need a bit of configuration first. The connection to the MQTT broker:

mqtt:
host: "<Mosquitto IP address>"

And what kind of Prometheus metrics we want to create. The Shelly H&T sensors publish three different values under the shellies/<sensor name>/sensor topic:

metrics:
- name: "shelly_temperature_celsius"
help: "Shelly H&T temperature"
type: "gauge"
topic: "shellies/+/sensor/temperature"

- name: "shelly_humidity_percent"
help: "Shelly H&T humidity"
type: "gauge"
topic: "shellies/+/sensor/humidity"

- name: "shelly_battery_percent"
help: "Shelly H&T battery"
type: "gauge"
topic: "shellies/+/sensor/battery"

If you have worked with Prometheus before, this should look familiar: You need to define an individual name/help message and the type of Prometheus metric (here: “gauge”).

To make querying in Prometheus a bit more comfortable, I’m adding two extra labels:

label_configs:

# `location` removes the `shellyht-` prefix from the message topic,
# e.g. `location="livingroom"``
- source_labels: ["__msg_topic__"]
regex: ".*shellyht-([^/]+).*"
target_label: "location"
replacement: '\1'
action: "replace"

# `location_type` is "outside" by default ...
# (The mqtt_exporter requires a `source_labels` field,
# even if it wouldn't be necessary in Prometheus)
- source_labels: ["location"]
target_label: "location_type"
replacement: "outside"

# .. unless the location name ends with "room"
- source_labels: ["location"]
regex: "\\w+room"
target_label: "location_type"
replacement: "inside"
action: "replace"

The complete config mqtt_exporter.yaml:

mqtt:
host: "<Mosquitto IP address>"

metrics:
- name: "shelly_temperature_celsius"
help: "Shelly H&T temperature"
type: "gauge"
topic: "shellies/+/sensor/temperature"
label_configs:
- source_labels: ["__msg_topic__"]
regex: ".*shellyht-([^/]+).*"
target_label: "location"
replacement: '\1'
action: "replace"
- source_labels: ["location"]
target_label: "location_type"
replacement: "outside"
- source_labels: ["location"]
regex: "\\w+room"
target_label: "location_type"
replacement: "inside"
action: "replace"

- name: "shelly_humidity_percent"
help: "Shelly H&T humidity"
type: "gauge"
topic: "shellies/+/sensor/humidity"
label_configs:
- source_labels: ["__msg_topic__"]
regex: ".*shellyht-([^/]+).*"
target_label: "location"
replacement: '\1'
action: "replace"
- source_labels: ["location"]
target_label: "location_type"
replacement: "outside"
- source_labels: ["location"]
regex: "\\w+room"
target_label: "location_type"
replacement: "inside"
action: "replace"

- name: "shelly_battery_percent"
help: "Shelly H&T battery"
type: "gauge"
topic: "shellies/+/sensor/battery"
label_configs:
- source_labels: ["__msg_topic__"]
regex: ".*shellyht-([^/]+).*"
target_label: "location"
replacement: '\1'
action: "replace"
- source_labels: ["location"]
target_label: "location_type"
replacement: "outside"
- source_labels: ["location"]
regex: "\\w+room"
target_label: "location_type"
replacement: "inside"
action: "replace"

Run the Docker container:

docker run -d \
-v "$(pwd)/mqtt_exporter.yaml:/conf/mqtt_exporter.yaml:ro" \
-p "9344:9344" \
--name mqtt_exporter \
fhemberger/mqtt_exporter

You should be able now to call the /metrics endpoint of the exporter now:

curl http://<mqtt_exporter IP>:9344/metrics

Once you add the MQTT exporter to the Prometheus config, you can visualize the data in Grafana:

Grafana dashboard showing temperature and humidity collected from the sensors