Visualizing temperatures and heating cycles

During these last days, the weather went crazy in Madrid. A great snowstorm was followed by a great freeze. It was hard for our heater to catch up. But also, a perfect scenario for learning more about what is going on with temperatures inside and outside the house!

Since the beginning, when I started with home automation and my thermostat, I had it clear that I wanted to store the data and get insights from it.

Besides, I could read about rlkoshak‘s great graphics in his How to customize a Rule tutorial, I had the way forward. In fact, rlkoshak pointed to a Grafana + InfluxDB tutorial, which I used as reference. In any case, I found some issues I wanted to share.

InfluxDB as OpenHAB persistence

InfluxDB is an opensource time series database. I found that configuring OpenHAB for storing item values is pretty straightforward!

First, you need to install the InfluxDB extension

Then, you just have to add one file to the /etc/openhab2/persistence/influxdb.persist with the items you want to store, and its frequency. For instance, I stored the items related with house temperatures, the heater working, and the thermostat temperature setpoint.

Strategies {
  everyMinute : "0 * * * * ?"
  everyHour : "0 0 * * * ?"
  everyDay : "0 0 0 * * ?"
}
Items {
  HeatingState, Temperature, OutsideTemperature, Humidity, OutsideHumidity, TemperatureSetpoint : strategy = everyChange, everyHour
}

Finally, you should define the users for database access. Besides the admin user, with full privileges, you should add a read write user for OpenHAB and a read only user for Grafana. All this setup is covered in the tutorial.

Grafana graphics

The next step, which is also covered in the tutorial, is about connecting Grafana with InfluxDB. You must use datasources for that. Then you can start building graphics.

However, I had a major problem: I wanted a graphic with temperatures and heating state, but InfluxQL, the default query language, does not support value conversion. I needed the HeatingState value to be mapped from ON / OFF to 1 / 0, so it could be drawn with the temperatures.

I could achieve this creating a new number item in OpenHAB. But I though it was not OpenHAB’s responsibility to change the way it stores the data, but Grafana‘s to convert the data so it is able to draw it.

So I adopted the Flux approach: besides InfluxQL, InfluxDB supports a more advanced language that can be used to query the database and prepare the data, supporting data transformations.

Flux plugin is already distributed with Grafana, but I needed to upgrade InfluxDB to 1.8, where the Flux API is supported.

Organizations and tokens are a bit misleading as well. You don’t need an organization at all, and the token is the form user:password. Totally unexpected.

With this configuration, I could build a Flux query where I could transform the string values ON / OFF into integers. It also transforms the fields names for a better label in the graph, and groups the values by time intervals.

from(bucket: v.defaultBucket)
  |> range(start: v.timeRangeStart, stop:v.timeRangeStop)
  |> filter(fn: (r) =>
    r._measurement == "HeatingState" and
    r._field == "value"
  )
  |> map(fn: (r) => ({r with _value:if r._value == "ON" then 1 else 0 }))
  |> fill(column: "_value", usePrevious: true)
  |> set(key: "_field", value: "")
  |> set(key: "_measurement", value: "Calefacción")

I used some more tweaks to draw the HeatingState in red as temperature background.

You can see the result! I was just in time to catch the greatest snowstorm in Madrid in the last 60 years, followed by the great freeze! You can see how outside temperatures established for two days during the snowfall, and then dropped to below zero. Luckily the temperatures inside our home stayed safe!

Temperatures before, during and after the great 2021 Madrid snowstorm

Open Bedroom Thermometer: Integration with the thermostat

My main goal of setting an extra thermometer in the bedroom was to integrate it with my open thermostat, placed in the living room. My house is not big, but when we go to bed, we are not in the living room anymore. We want it to watch for the temperature in our bedroom.

Of course, I wanted to automatize this switch as well. Every night, when we go to sleep, the thermostat switches the tracking of the temperatures from the living room thermometer, to the bedroom thermometer. When we wake up, it switches back to the living room thermometer, the one in the thermostat.

One of the great things about using open hardware and software is that you can customize everything. I could change and adapt both the UI and the logic of the thermostat to incorporate the new temperature readings

UI integration

HestiaPi UI is just Javascript (Vue.js) and HTML, running on Kweb, a very light browser for Raspberry Pi. I redesigned the interface to accommodate the new figures. I also added the buttons to switch manually the thermometer that drives the thermostat behavior, from the thermostat sensor to the bedroom thermometer and back. HestiaPi UI communicates over MQTT, the same way than ANAVI Thermometer, so it was pretty straightforward to copy the same logic.

You can find the code in the anavi-thermometer branch of my fork of hestia-touch-one-ui.

Modified interface of HestiaPi including ANAVI Thermometer readings

Backend integration

HestiaPi logic is built on top on OpenHAB. Despite OpenHAB is a bit heavy for running in a Raspberry Pi Zero, it provides HestiaPi with all the power of smart home capabilities.

Backend integration involves new OpenHAB things, channels, items and rules, so it deserves its own post: ANAVI Thermometer – HestiaPi integration

ANAVI Thermometer things and channels in Hestia Pi’s OpenHAB

I feel grateful for having found these two great open projects and being able to play with them, learn many things and having a feeling of accomplishment after customizing my own setup!

This is the last post on these ANAVI Thermometer series, but not the last from Hestia Pi… More hacking to come!

ANAVI Thermometer – HestiaPi integration

This integration works with HestiaPi Touch ONE v1.2

These changes need the HestiaPi Touch UI to be updated with my anavi-thermometer branch.

Add-ons

ANAVI Thermometer publishes values in JSON. They are easy parsed in OpenHAB with the JSONPath transformation add-on. We will need to install it first

Things and items

ANAVI Thermometer

We create a separate Generic MQTT Thing for setting the channels related to ANAVI Thermometer values

Generic MQTT Thing configuration

Then we create appropriate channels for ANAVI Thermometer’s temperature and humidity. They will probably be pushed to workgroup/<your id>/air/temperature and workgroup/<your id>/air/humidity MQTT paths. We use JSONPath for extracting actual values.

ANAVI Thermometer temperature channel
ANAVI Thermometer humidity channel
ANAVI Thermometer thing with channels

Finally, we need to link channels to their corresponding items. You can create them from the link channel, clicking at the arrows on the right

ANAVI Thermometer temperature item
ANAVI Thermometer humidity item

Thermometer Switch

We create an extra MQTT thing, Thermometer Switch, that will be used for selecting which temperature the thermostat is following; based on this thing, HestiaPi will act on changes of the temperature sensor from HestiaPi or the one from ANAVI Thermometer

Thermometer Switch thing

We need to create a new channel for this new thing. We will be reading its state from hestia/local/stat/thermometerswitch MQTT topic

Thermometer Switch channel

And finally, we create an item for this channel

Thermometer Switch channel with its linked item

Hestia Local Sensor Readings

A side effect of the Process Sensor Changes rule modification below is that the original MQTT channels used by HestiaPi to publish local sensor readings hestia/local/temperature and hestia/local/humidity will now publish ANAVI’s readings when Thermometer Switch has Anavi value. We will need to add two extra MQTT things and items to keep those local sensor readings. Former channels become current values the thermostat is considering for taking the decisions about turning on or off.

We add LocalTemp and LocalHumi MQTT channels pointing to hestia/local/localtemperature and hestia/local/localhumidity

LocalTemp channel
LocalHumi channel

And we link these things to their corresponding items LocalTemp and LocalHumi

LocalTemp item
LocalHumi items

Rules

Initialization rule

We will need to add the initial value for Thermometer Switch, that will default to HestiaPi’s value My. This value is not very descriptive, but will match HestiaPi’s item names and will make things easier in the rules below.

initState("ThermometerSwitch", "My");

Process Sensor Changes rule

Process Sensor Changes is the rule that updates temperature, humidity and pressure proxies. It also decides whether to update temperature based on hysteresis changes.

We add two new triggers so the rule is ran also whether AnaviTemp or AnaviHumi change

AnaviTemp trigger for Process Sensor Changes rule
AnaviHumi trigger for Process Sensor Changes rule

However, we need an extra condition: we set the constraint that the sensor that changed has to match the value of ThermometerSwitch, that is, only update values from HestiaPi’s sensor when My is selected in ThermometerSwitch, and only update values from ANAVI’s sensors when Anavi is selected

event.itemName.indexOf(items["ThermometerSwitch"]) == 0 || event.itemName == "MyPressure"

Finally, we slightly modify the rule to get the value from the relevant sensor, that can have My or Anavi prefix now

var OPENHAB_CONF = Java.type('java.lang.System').getenv('OPENHAB_CONF');
load(OPENHAB_CONF + '/automation/lib/hestia/utils.js');
load(OPENHAB_CONF + '/automation/lib/hestia/defaults.js');

var device;
var logName = "sensor";

logDebug(logName, "Changed "+event.itemName);

if (event.itemName == 'MyPressure') {
  device = 'Pressure'
} else {
  device = event.itemName.replace(items["ThermometerSwitch"], "");
}

// Update the proxy
events.sendCommand("My"+device+"Proxy", newState);

// Verifying the newState can be parsed is already checked in the but only if…
var newReading = parseFloat(newState.toString());

var prev = items["Previous"+device+"Reading"].floatValue();
if(prev == NaN) prev = 0;

var hyst = (device == "Temp") ? DEFAULTS.get("Temp"+items["TempUnit"]+"_DIFF") : DEFAULTS.get(device+"_DIFF");

logDebug(logName, "Processing " + device + " with value " + newState + " and prevState " + prev + " and delta " + delta);

var delta = Math.abs(newReading - prev);
if(delta > hyst) {
  logDebug(logName, "Updating Previous"+device+"Reading with " + newState);
  events.postUpdate("Previous"+device+"Reading", newState);
}
else {
  logDebug(logName, "Ignoring " + device + " sensor reading, change is too small");
}
Modified Process Sensor Changes rule

Process Local Sensor Changes rule

Finally, we add an extra rule to publish HestiaPi’s sensor values to the extra MQTT things LocalTemp and LocalHumi that we created before

They will be triggered when MyTemp or MyHumi change

var OPENHAB_CONF = Java.type('java.lang.System').getenv('OPENHAB_CONF');
load(OPENHAB_CONF + '/automation/lib/hestia/utils.js');
load(OPENHAB_CONF + '/automation/lib/hestia/defaults.js');

var logName = "localsensor";

logDebug(logName, "Changed "+event.itemName);

var device = event.itemName.replace("My", "");

// Update the proxy
events.sendCommand("Local"+device, newState);