My underfloor heating (hot water) was previously controlled with Busch-Jäger bimetal thermostats. That went well, but there was no option for night setback or absence mode. As a SmartHome freak, I want to control everything from a tablet. Several experiments with finished electronic thermostats followed, with MAX! and HomeMatic, but nothing was really satisfying - sometimes very expensive. For a long time I have been using Shellies for the light switches in the house, which are connected via WiFi and fit into a flush-mounted box. These are inexpensive, reliable and can be integrated into OpenHAB via MQTT. I do not use the Shelly HT because it is a standalone device with battery operation and therefore only very rarely sends temperature updates to OpenHAB. Since Allterco has offered a temperature sensor add-on for the Shelly 1, I've toyed with the idea of using this duo for the thermostat. |
Functionality
The Shelly 1 with the add-on runs as a two-point controller via the "Temperature Actions". This also works if the WLAN or OpenHAB should not work. The switching thresholds are given to the Shelly 1 via HTTP commands from OpenHAB. MQTT does not work for these parameters, possibly once in a later firmware.
The Shelly returns all measured values and the relay position via MQTT to OpenHAB so that they can be displayed there in the sitemap. The relay controls my hot water valves in the heating distributor.
There are a few global virtual switches for all thermostatic valves:
- Heating on / off (summer / winter)
Day / night switching via CRON. Lower temperatures are used at night.
Hysteresis: this is the distance in ° C between switching the thermostats on and off. Since the Shelly only measures to an accuracy of 0.1 ° C, at least 0.2 ° C should be set
Unfortunately, my underfloor heating pump is not electronically controlled, which is why it used to run all winter. The pump has got its own Shelly 1, which is controlled by the group of all thermostatic valves. If at least one valve is switched on, the pump also runs. The pump is only switched off when all valves are off.
Hardware
You have to buy
- 1x Shelly 1, 10,90€
- 1x Temperatursensor-Addon, 6,90€
- 1x DS18B20-Sensor in TO92-Gehäuse, 1,20€
So the total cost of a thermostat is € 19.
On the left the Shelly 1 and on the right the temperature sensor add-on. The add-on is plugged into the programming socket of the Shelly. | |
The temperature sensor DS18B20 in the TO92 housing |
3D printed housing
There is no suitable thermostat housing from Busch Jäger, so I designed one myself - it consists of the wall plate, the Shelly adapter and the cover for the sensor. You can download it from Thingiverse: https://www.thingiverse.com/thing:4579411
Above the round Shelly adapter, below the wall plate and the cover. |
Software for the Shelly
The Shelly 1 is equipped with the current Shelly firmware 1.8.0.
When setting up via the web interface, you have to enter the access data of your own WLAN and the MQTT server in the Shelly. The MQTT server Mosquitto is located with OpenHAB on the Raspberry Pi.
Montage
The Shelly adapter is attached to the wall plate with M3x12 screws. The screw in the middle is a M3x16 for screwing the cover. | |||||||||||||
The M3 nuts are pressed into the PLA housing with a soldering iron. It only takes a few seconds. | |||||||||||||
The Shelly 1 is now pressed into the adapter from above until it sits on the screw connection. The adapter is a few millimeters away from the wall plate. This reduces heat transfer from the Shelly to the wall plate behind which the sensor is located. | |||||||||||||
The temperature sensor add-on is now plugged into the programming socket of the Shelly. The three wires are fed through the hole in the wall plate. | |||||||||||||
The three wires are led out on the front. The Busch Jäger frame fits very well on the wall plate. |
|||||||||||||
The wires are shortened a little and then soldered to the DS18B20. Shrink tubing prevents a short circuit. The assignment:
|
|||||||||||||
It is essential to switch off the fuse and check whether the wires are voltage-free! After removing the old thermostat, the 230V wires are now connected to the Shelly. |
|||||||||||||
After tightening and checking the clamps, the add-on is attached. | |||||||||||||
The module is pushed into the flush-mounted box and the wall plate is fastened with two screws. | |||||||||||||
The soldered-on temperature sensor is clamped into the groove in the cover. If needed, secure with some hot glue. | |||||||||||||
Now you put the Busch Jäger frame on and screw the cover with the central screw. Finished! Now you can switch the fuse on again and test whether the Shelly 1 can be reached via the web interface. |
OpenHAB Things
Each thermostat is its own thing with two channels each: temperature and relay. Creates a file things/mqtt.things with the following content:
//---------------------------------------------------------------------------------- // Shelly 1 mit Temperature Addon for WZ-Thermostat Thing mqtt:topic:HZ_WZ_FloorThermostat "Th-HZ-WZ-FloorThermostat" { Channels: Type number : chTemp "Temperatur" [ stateTopic="shellies/shelly1-A4CF12F3E44D/ext_temperature/0" ] Type switch : chRelais "Ventilausgang" [ stateTopic="shellies/shelly1-A4CF12F3E44D/relay/0", on="on", off="off", commandTopic="shellies/shelly1-A4CF12F3E44D/relay/0/command", on="on", off="off" ] }
Den MQTT-Pfad zum Shelly findet man am Besten mit dem "MQTT-Explorer" (Windows) heraus:
You can download it from the Windows Store for Windows 10.
OpenHAB Items
For each thing (= thermostat) two items must be created. Creates a file items/mqtt.items with the following content:
// shelly1-A4CF12F3E44D Number It_WZ_ActTemperature "Wohnzimmer Temperatur [%.1f °C]" (gPersistMapDB, Thermostat) { channel="mqtt:topic:HZ_WZ_FloorThermostat:chTemp", alexa="TemperatureSensor.temperature" } Switch It_WZ_RelaisThermostat "Wohnzimmer Thermostatventil [MAP(onoff.map):%s]" (gPersistMapDB, gHeatingValves) { channel="mqtt:topic:HZ_WZ_FloorThermostat:chRelais" }
In addition, virtual items are required for the temperatures and day / night or summer / winter settings:
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Fußbodenheizung mit Shelly 1 & Temperatur-Addon: // Globale Werte: Group:Switch:OR(ON, OFF) gHeatingValves "Heizventile [MAP(onoff.map):%s]" (Home) Number vIt_HZ_Hysterese "Fußbodenheizung Hysterese [%.1f °C]" (gPersistMapDB) Switch vIt_HZ_OnOff "Fußbodenheizung [MAP(onoff.map):%s]" (gPersistMapDB) Switch vIt_HZ_DayNight "Fußbodenheizung Programm [MAP(heating.map):%s]" (gPersistMapDB) Group Thermostat "Wohnzimmer" {alexa="Endpoint.Thermostat"} // Thermostat GF_LivingRoom: Number vIt_HZ_WZ_SollTag_upper "Wohnzimmer Solltemperatur Tag [%.1f °C]" (gPersistMapDB, Thermostat) {alexa="ThermostatController.upperSetpoint"} Number vIt_HZ_WZ_SollTag_lower "Wohnzimmer Solltemperatur Tag Start [%.1f °C]" (gPersistMapDB) Number vIt_HZ_WZ_SollNacht_upper "Wohnzimmer Solltemperatur Nacht [%.1f °C]" (gPersistMapDB) Number vIt_HZ_WZ_SollNacht_lower "Wohnzimmer Solltemperatur Nacht Start [%.1f °C]" (gPersistMapDB)
OpenHAB Rules
Creates a rules/heating.rules file with the following content:
//================================================================================== // Global Thermostat Rules: //---------------------------------------------------------------------------------- // Nightly temperature reduction for all thermostats: // http://www.cronmaker.com/;jsessionid=node01fm60c8kkb5fb1uiiavit2knbt5438.node0?0 // Night reduction starting everyday at 19:30 rule "FloorHeating_NightReduction" when Time cron "0 30 19 1/1 * ? *" then vIt_HZ_DayNight.sendCommand( 'OFF' ) logInfo( "FloorHeating_NightReduction", "Rule FloorHeating_NightReduction ...") end //---------------------------------------------------------------------------------- // Daily temperature operation for all thermostats: // http://www.cronmaker.com/;jsessionid=node01fm60c8kkb5fb1uiiavit2knbt5438.node0?0 // Day operation starting everyday at 07:00 rule "FloorHeating_DayOperation" when Time cron "0 0 7 1/1 * ? *" then vIt_HZ_DayNight.sendCommand( 'ON' ) logInfo( "FloorHeating_DayOperation", "Rule FloorHeating_DayOperation ...") end //---------------------------------------------------------------------------------- // Floor Heating Pump: shellies/shelly1-A4CF12F48853 rule "FL_FloorHeatingPump" when Item gHeatingValves received update then if(gHeatingValves.state==ON){ // Switch ON=pump running It_FL_Pump.sendCommand( "ON" ) logInfo( "FL_FloorHeatingPump", "Heating ON" ) } else { // Switch OFF=pump not running It_FL_Pump.sendCommand( "OFF" ) logInfo( "FL_FloorHeatingPump", "Heating OFF" ) } end //================================================================================== // Thermostat Wohnzimmer: //---------------------------------------------------------------------------------- // Calculate lower DAY setpoint if hysterese or upper setpoint have changed rule "WZ_Thermostat_LowerSetpointDay" when Item vIt_HZ_Hysterese changed or Item vIt_HZ_WZ_SollTag_upper changed then var Double UpperSetpoint = ( vIt_HZ_WZ_SollTag_upper.state as Number ).doubleValue() var Double Hysterese = ( vIt_HZ_Hysterese.state as Number ).doubleValue() var Double LowerSetpoint = UpperSetpoint - Hysterese vIt_HZ_WZ_SollTag_lower.sendCommand( LowerSetpoint ) logInfo( "WZ_Thermostat_LowerSetpointDay", "Rule WZ_Thermostat_LowerSetpointDay: " + "TagLower= " + LowerSetpoint ) end //---------------------------------------------------------------------------------- // Calculate lower NIGHT setpoint if hysterese or upper setpoint have changed rule "WZ_Thermostat_LowerSetpointNight" when Item vIt_HZ_Hysterese changed or Item vIt_HZ_WZ_SollNacht_upper changed then var Double UpperSetpoint = ( vIt_HZ_WZ_SollNacht_upper.state as Number ).doubleValue() var Double Hysterese = ( vIt_HZ_Hysterese.state as Number ).doubleValue() var Double LowerSetpoint = UpperSetpoint - Hysterese vIt_HZ_WZ_SollNacht_lower.sendCommand( LowerSetpoint ) logInfo( "WZ_Thermostat_LowerSetpointNight", "Rule WZ_Thermostat_LowerSetpointNight: " + " NachtLower= " + LowerSetpoint ) end //---------------------------------------------------------------------------------- // Send new values to the Shelly via HTTP if one of the values has been switched or // The switch DAY/NIGHT has changed rule "WZ_Thermostat_SetShelly" when Item vIt_HZ_WZ_SollTag_upper changed or Item vIt_HZ_WZ_SollTag_lower changed or Item vIt_HZ_WZ_SollNacht_upper changed or Item vIt_HZ_WZ_SollNacht_lower changed or Item vIt_HZ_DayNight changed then Thread::sleep(200) var Double UpperSetpoint var Double LowerSetpoint if(vIt_HZ_DayNight.state==ON){ // Switch ON=Day setpoints UpperSetpoint = ( vIt_HZ_WZ_SollTag_upper.state as Number ).doubleValue() LowerSetpoint = ( vIt_HZ_WZ_SollTag_lower.state as Number ).doubleValue() } else { // Switch OFF=Night setpoints UpperSetpoint = ( vIt_HZ_WZ_SollNacht_upper.state as Number ).doubleValue() LowerSetpoint = ( vIt_HZ_WZ_SollNacht_lower.state as Number ).doubleValue() } // Send http to Shelly: var String httpString = "" httpString = "http://10.2.10.56/settings/ext_temperature/0?overtemp_threshold_tC=" + String::format("%.2f",UpperSetpoint) sendHttpGetRequest(httpString) logInfo( "WZ_Thermostat_SetShelly", "Rule WZ_Thermostat_SetShelly: " + httpString ) httpString = "http://10.2.10.56/settings/ext_temperature/0?undertemp_threshold_tC=" + String::format("%.2f",LowerSetpoint) sendHttpGetRequest(httpString) logInfo( "WZ_Thermostat_SetShelly", "Rule WZ_Thermostat_SetShelly: " + httpString ) end //----------------------------------------------------------------------------------EVERY Thermostat // Switch Shelly ON/OFF for SUMMER/WINTER switch rule "WZ_Thermostat_HeatingOnOff" when Item vIt_HZ_OnOff changed then var String httpString = "" if(vIt_HZ_OnOff.state==ON){ // Switch ON=heating running httpString = "http://10.2.10.56/settings/ext_temperature/0?overtemp_act=relay_off" sendHttpGetRequest(httpString) logInfo( "WZ_Thermostat_HeatingOnOff", "Heating ON: " + httpString ) httpString = "http://10.2.10.56/settings/ext_temperature/0?undertemp_act=relay_on" sendHttpGetRequest(httpString) logInfo( "WZ_Thermostat_HeatingOnOff", "Heating ON: " + httpString ) } else { // Switch OFF=heating switched off httpString = "http://10.2.10.56/settings/ext_temperature/0?overtemp_act=disabled" sendHttpGetRequest(httpString) logInfo( "WZ_Thermostat_HeatingOnOff", "Heating ON: " + httpString ) httpString = "http://10.2.10.56/settings/ext_temperature/0?undertemp_act=disabled" sendHttpGetRequest(httpString) It_WZ_RelaisThermostat.sendCommand( OFF ) logInfo( "WZ_Thermostat_HeatingOnOff", "Heating ON: " + httpString ) } end
OpenHAB Map
Creates a file transform/onoff.map with the following content:
ON=EIN on=EIN On=EIN 1=EIN OFF=AUS off=AUS Off=AUS 0=AUS NULL=undefiniert UNDEF=undefiniert -=undefiniert
OpenHAB Sitemap
Creates a file sitemaps/heating.sitemap with the following content:
sitemap heating label="Fußbodenheizung" { Frame label="Globale Einstellung Fußbodenheizung" { Setpoint item=vIt_HZ_Hysterese minValue=0.1 maxValue=4.0 step=0.1 Switch item=vIt_HZ_OnOff Switch item=vIt_HZ_DayNight Default item=gHeatingValves Default item=It_FL_Pump } Frame label="Heizung Wohnzimmer" { Text item=It_WZ_ActTemperature Switch item=It_WZ_RelaisThermostat Setpoint item=vIt_HZ_WZ_SollTag_upper minValue=4.5 maxValue=35 step=0.5 Text item=vIt_HZ_WZ_SollTag_lower Setpoint item=vIt_HZ_WZ_SollNacht_upper minValue=4.5 maxValue=35 step=0.5 Text item=vIt_HZ_WZ_SollNacht_lower } }
The result in the browser:
The icons used for valves and pumps are not included in OpenHAB, you have to use your own.
Conclusion and outlook
The project with its five thermostats kept me busy for a few days, especially the design and 3D printing. After installation and configuration, it works flawlessly. Now only winter has to come - that is the hard practical test. I'm currently trying to control the temperatures via Alexa, but that's another topic.
What could be improved?
- Use separate times for each individual thermostat
Absence of the house residents for lowering also during the day
Use Alexa for the settings