OpenTherm Gateway Through MQTT (instructions and samples)

Today I configured my OpenTherm Gateway to publish data to MQTT where the messages are received, processes and persisted in influx. This way I can see what my current conditions are for my central heating system and analyse the historic data through Grafana.

I do not use the OTGW to control my central heating system. It is there for monitoring. A regular Honeywell Evohome does OpenTherm Modulation and has control over zone valves etc.

This solution is based on OpenTherm Gateway with Raspberry Pi Zero W directly connected through an USB-TTL cable. This solution does NOT use a NodeMCU / Network shield on OTGW

This solution does NOT use the OpenHAB OpenTherm Gateway Binding. It instead uses the standard MQTT Binding in OpenHAB to integrate the OpenTherm Gateway.

Enjoy and let me know if you like the solution.

Kind regards, Bas

A) Get an OpenTherm Gateway
Buy it or compose it yourself (https://www.nodo-shop.nl/nl/opentherm-gateway/188-opentherm-gateway.html) incl. the power supply and soldered it according to the manual (https://www.nodo-shop.nl/nl/index.php?controller=attachment&id_attachment=47).

B) Install a Raspberry PI Zero W and run it headless (https://www.raspberrypi.org/documentation/configuration/wireless/headless.md)

C) Connect all the things
Connect the OTGW and RPI through TTL-USB (https://www.electrodragon.com/product/usb-ttl-serial-ch340-board/) and connect DuPont Jumper Cable connecting GND, RTX, TRX. Read the manual for the OTGW for images illustrating how it’s done. You need a connector cable to connect the TTL-USB to the RPI Zero (Adapter Cable Micro USB 2.0 Female to Micro B Male Converter Adaptor Cable).

D) Build and Run OTMonitor
Retreive the latest OTMonitor from github (https://github.com/basmeerman/otmonitor) build, configure and run it according to the readme and documentation listed in the repository.

  • Note to configure the correct USB device in the config file, otherwise no output/debug information is printed to the logs.
  • Make sure to create the otmonitor.conf file containing the configuration (the MQTT broker information).
  • Install otmonitor as a system service/daemon (see https://github.com/basmeerman/otmonitor readme)

E) Now in OpenHAB: Create things in .thing file:

    Thing topic openthermgateway "OpenThermGateway" {
    Channels:
        // powermode			{mode		string}
        // thermostat  		{connected	boolean}
        // error			{code		byte}
        Type number : error "Error Code" [ stateTopic="events/central_heating/otmonitor/error", transformationPattern="JSONPATH:$.value"]
        // flame			{on		boolean}
        Type switch : flame "Flame" [ stateTopic="events/central_heating/otmonitor/flame", transformationPattern="JSONPATH:$.value", on="true", off="false"]
        // hotwater			{on		boolean}
        Type switch : hotwater "Hotwater" [ stateTopic="events/central_heating/otmonitor/hotwater", transformationPattern="JSONPATH:$.value", on="true", off="false"]
        // centralheating		{on		boolean}
        Type switch : centralheating "Central Heating" [ stateTopic="events/central_heating/otmonitor/centralheating", transformationPattern="JSONPATH:$.value", on="true", off="false"]
        // fault			{on		boolean}
        Type switch : fault "Fault" [ stateTopic="events/central_heating/otmonitor/fault", transformationPattern="JSONPATH:$.value", on="true", off="false"]
        // diagnostic			{on		boolean}
        // cooling			{on		boolean}
        // centralheating2		{on		boolean}
        // electricity			{on		boolean}
        // roomtemperature		{temp		float}
        Type number : roomtemperature "Room Temperature" [ stateTopic="events/central_heating/otmonitor/roomtemperature", transformationPattern="JSONPATH:$.value"]
        // roomtemperature2		{temp		float}
        // outsidetemperature		{temp		float}
        // setpoint			{temp		float}
        Type number : roomsetpoint "Room Setpoint" [ stateTopic="events/central_heating/otmonitor/setpoint", transformationPattern="JSONPATH:$.value"]
        // setpoint2			{temp		float}
        // controlsetpoint		{temp		float}
        Type number : controlsetpoint "Control Setpoint" [ stateTopic="events/central_heating/otmonitor/controlsetpoint", transformationPattern="JSONPATH:$.value"]
        // controlsetpoint2		{temp		float}
        // dhwenable			{on		boolean}
        Type switch : dhwenable "Hotwater Enabled" [ stateTopic="events/central_heating/otmonitor/dhwenable", transformationPattern="JSONPATH:$.value", on="true", off="false"]
        // dhwsetpoint			{temp		float}
        Type number : dhwsetpoint "Hotwater Setpoint Temperature" [ stateTopic="events/central_heating/otmonitor/dhwsetpoint", transformationPattern="JSONPATH:$.value"]
        // dhwtemperature		{temp		float}
        //* Niet in MQTT Gezien!
        Type number : dhwtemperature "Hotwater Temperature" [ stateTopic="events/central_heating/otmonitor/dhwtemperature", transformationPattern="JSONPATH:$.value"]
        // dhwtemperature2		{temp		float}
        // chenable			{on		boolean}
        Type switch : chenable "Heating Enabled" [ stateTopic="events/central_heating/otmonitor/chenable", transformationPattern="JSONPATH:$.value", on="true", off="false"]
        // chsetpoint			{temp		float}
        Type number : chsetpoint "Heating Setpoint" [ stateTopic="events/central_heating/otmonitor/chsetpoint", transformationPattern="JSONPATH:$.value"]
        // modulation			{level		float}
        // maxmodulation		{level		float}	
        Type number : maxmodulation "Maximum Modulation" [ stateTopic="events/central_heating/otmonitor/maxmodulation", transformationPattern="JSONPATH:$.value"]
        // boilerwatertemperature	{temp		float}
        Type number : boilerwatertemperature "Boilerwater Temperature" [ stateTopic="events/central_heating/otmonitor/boilerwatertemperature", transformationPattern="JSONPATH:$.value"]
        // boilerwatertemperature2	{temp		float}
        // returnwatertemperature	{temp		float}
        // chwaterpresure		{bar		float}
        Type number : chwaterpressure "Heating Water Pressure" [ stateTopic="events/central_heating/otmonitor/chwaterpresure", transformationPattern="JSONPATH:$.value"]
        // remoteoverrideroomsetpoint	{temp		float}
        // chwaterdeltat		{temp		float}
        // chburnerstarts		{count		unsigned}
        Type number : chburnerstarts "Heating Burner Starts" [ stateTopic="events/central_heating/otmonitor/chburnerstarts", transformationPattern="JSONPATH:$.value"]
        // chpumpstarts		{count		unsigned}
        // dhwpumpstarts 		{count		unsigned}
        // dhwburnerstarts 		{count		unsigned}
        // chburnerhours 		{count		unsigned}
        Type number : chburnerhours "Heating Burner Hours" [ stateTopic="events/central_heating/otmonitor/chburnerhours", transformationPattern="JSONPATH:$.value"]
        // chpumphours 		{count		unsigned}
        // dhwpumphours 		{count		unsigned}
        // dhwburnerhours 		{count		unsigned}
        Type number : dhwburnerhours "Hotwater Burner Hours" [ stateTopic="events/central_heating/otmonitor/dhwburnerhours", transformationPattern="JSONPATH:$.value"]
    }

F) Create items in .item file:

// OpenTherm Gateway
Number ErrorCodeCentralHeating "CV Error Code" <switch> (GClimate) {channel="mqtt:topic:broker:openthermgateway:error"}
Switch SwitchFlameCentralHeating "CV Brander" <switch> (GClimate) {channel="mqtt:topic:broker:openthermgateway:flame"}
Switch SwitchHotwaterCentralHeating "CV Warmwater" <switch> (GClimate) {channel="mqtt:topic:broker:openthermgateway:hotwater"}
Switch SwitchHeatingCentralHeating "CV Verwarming" <switch> (GClimate) {channel="mqtt:topic:broker:openthermgateway:centralheating"}
Switch SwitchFaultCentralHeating "CV Fout" <switch> (GClimate) {channel="mqtt:topic:broker:openthermgateway:fault"}
Number TemperatureRoomCentralHeating "CV Temperatuur" <temperature> (GClimate) {channel="mqtt:topic:broker:openthermgateway:roomtemperature"}
Number TemperatureSetpointRoomCentralHeating "CV Setpoint" <heating> (GClimate) {channel="mqtt:topic:broker:openthermgateway:roomsetpoint"}
Number TemperatureControlSetpointCentralHeating "Controle Setpoint" <heating> (GClimate) {channel="mqtt:topic:broker:openthermgateway:controlsetpoint"}
Switch SwitchHotwaterEnabledCentralHeating "CV Warmwater Bedrijf Enabled" <switch> (GClimate) {channel="mqtt:topic:broker:openthermgateway:dhwenable"}
Number TemperatureHotwaterSetpointCentralHeating "CV Warmwater Setpoint" <heating> (GClimate) {channel="mqtt:topic:broker:openthermgateway:dhwsetpoint"}
Number TemperatureHotwaterCentralHeating "CV Warmwater Temperatuur" <temperature> (GClimate) {channel="mqtt:topic:broker:openthermgateway:dhwtemperature"}
Switch SwitchCentralHeatingEnabledCentralHeating "CV Bedrijf Enabled" <switch> (GClimate) {channel="mqtt:topic:broker:openthermgateway:chenable"}
Number TemperatureCentralHeatingSetpointCentralHeating "CV Setpoint" <heating> (GClimate) {channel="mqtt:topic:broker:openthermgateway:chsetpoint"}
Number PercentageMaximumModulationCentralHeating "CV Maximale Modulatie" <incline> (GClimate) {channel="mqtt:topic:broker:openthermgateway:maxmodulation"}
Number TemperatureBoilerWaterCentralHeating "CV Boiler Temperatuur" <temperature> (GClimate) {channel="mqtt:topic:broker:openthermgateway:boilerwatertemperature"}
Number PressureWaterCentralHeating "CV Warmwater Druk" <pressure> (GClimate) {channel="mqtt:topic:broker:openthermgateway:chwaterpressure"}
Number NumberBurnerStartsCentralHeating "CV Brander Starts" <line> (GClimate) {channel="mqtt:topic:broker:openthermgateway:chburnerstarts"}
Number HoursBurnerCentralHeating "CV Bedrijf Brander Uren" <time> (GClimate) {channel="mqtt:topic:broker:openthermgateway:chburnerhours"}
Number HoursHotwaterBurnerCentralHeating "CV Warmwater Berijf Brander Uren" <time> (GClimate) {channel="mqtt:topic:broker:openthermgateway:dhwburnerhours"}

G) Create a small sitemap to demo the controls

sitemap centralheating label="CentralHeating" icon="heating"
{
  Frame label="Central Heating" {
    Group item=GClimate label="Climaat Beheersing" icon="heating" 
  }
}

Troubleshooting)

  • If things do not work directly trace back your steps diligently, the solution contains a lot of steps.
  • You can find a lot of info on the links provided in the article and on the website.
  • Please note you need to compile the OTMonitor on the RPI in order to receive the latest MQTT implementation and receive more attributes than in previous versions.
  • It’s a good idea to first test the OTGW hardware with for example a Windows/Linux desktop so you know the hardware is working. Then proceed to connecting and configuring the Raspberry PI and finish the solution.
  • Since not all central heaters/boilers are created equal, they communicate a different set of data attributes over OpenTherm. Therefor the sample does not have a complete scenario. I happen to own an Intergas 30/36HRE and it’s is not publishing a lot of details. You can subscribe to your MQTT broker with a wildcard subscription in order to see the complete list of messages OTMonitor publishes (or you need to read the TCL code :-))

That’s all. It was a fun project to do.

3 Likes

Hi @basm,

Nice article, but just out of curiosity… what is the reason you went with MQTT instead of using the OpenTherm Gateway binding?

Regards,
Arjen

I use openHAB as an integration abstraction layer, abstracting physical representation to items. with nodered as a rule engine.

I tend to minimize using niche bindings (bindings that aren’t used by a majority of the population) to be more independent of the lifecycle Management of the binding owner. This because I don’t have the time to maintain these myself.

Hope this helps you.

Kind regards,

Bas

Hi @basm, thank you for your reply.

The OpenTherm Gateway binding is currently still in test phase, but actively used, proven very stable and on the edge of being included as standard binding into openHAB.

Anyway, the reason I asked was mostly because in case you would experience any issues with the binding, or if you were missing any features, I would like to know so that I can further improve the binding.

Regards,
Arjen

Hi ajren,

Once i’ve upgraded to 2.5 I’ll give the binding a chance. Thanks for the reachout.

Bas