Solar thermal pool heating control

Hello openHab community.

I’ve created a visualization of my combined pool and home heating system and thought I might as well share it with you. My widget development skills are more than limited so my appologies for the probably bad/inefficient code.
Looking forward to your thoughts, suggestions and ideas!

Here’s the initial version (of my work in progress):


(solar thermal water being sent to pool heat exchanger, pool pump running)


(solar thermal water being sent to house heating system, pool pump not running)

Here’s my setup (excerpt):

  • raspberry pi 5b 8GB openHab Server

On roof:

  • solar thermal collectors
  • photovoltaic solar energy system (17kWp)

In pool house:

  • heat exchanger (for pool water/solar thermal system)
  • Pool pump (approx 600W) and sand filter system
  • Shelly EM (to measure pool pump and pool light power consumption)
  • MySensors node in pool house with relays for
    • pool pump
    • pool lights
      and ds18b20 sensors for measuring temperature of
    • pool water flowing from pool to pump
    • pool water flowing from pump back to pool
    • solar thermal water flowing in to pool heat exchanger from switching valve (in basement)
    • solar thermal water flowing back from pool heat exchanger back to switching valve (in basement)

In basement (HVAC-room):

  • Two Growatt inverters and 17kWh battery (connected to openhab via modbus serial)
  • European (German) style house heating system (with hot water tank)
  • electric switching valve (for switching solar thermal hot water flow to pool heat exchanger or house heating system)
  • DIY MySensors node (in basement HVAC-room) with
    • relay for electric switching valve
      and ds18b20 sensors for measuring temperature of
    • solar thermal water arriving from roof at switching valve (in basement)
    • solar thermal water flowing from switching valve (in basement) back to roof
    • solar thermal water flowing from switching valve (in basement) to pool heat exchanger
    • solar thermal water returning from pool heat exchanger at switching valve (in basement)
    • solar thermal water flowing switching valve to house heating system (heat exchanger in hot water tank)
    • solar thermal water returning from house heating system (heat exchanger in hot water tank) to switching valve

On ground floor:

  • DIY MySensors MQTT Ethernet gateway

In garden:

  • inground pool 7m x 3m x 1,7m (23ft x 10ft x 5.6ft)

Summary of the most relevant rules:

  • run pool pump for 4h/day
  • run pool pump only when solar energy production exceeds house load consumption by at least 1kw
  • if not enough excess solar energy production make sure pool pump ran min 4h/day at 7pm each day
  • automatically switch valve to pool heat exchanger when solar thermal water temp exceeds predifined threshold and pool temperature below target and difference bt. pool temp and thermal temp exceeds defined threshold
  • automatically run pool pump when switching valve switched to pool heat exchanger (to protect pool pipes from melting)
  • run pool pump in pool cleaning mode

Here’s the widget code:

uid: Pool_v01
tags: []
props:
  parameters:
    - context: item
      label: Item Umschaltventil
      name: itemVentil
      required: true
      type: TEXT
    - context: item
      label: Item Solarwasser-Zulauf-Temperatur
      name: itemSolarZulaufTemp
      required: true
      type: TEXT
    - context: item
      label: Item Solarwasser-Rücklauf-Temperatur
      name: itemSolarRuecklaufTemp
      required: true
      type: TEXT
    - context: item
      label: Item Solarwasser-Pool-Zulauf-Temperatur (Keller)
      name: itemSolarPoolZulaufKellerTemp
      required: true
      type: TEXT
    - context: item
      label: Item Solarwasser-Rücklauf-Temperatur (Keller)
      name: itemSolarPoolRuecklaufKellerTemp
      required: true
      type: TEXT
    - context: item
      label: Item Poolpumpe-Zulauf-Temperatur
      name: itemPoolpumpeZulaufTemp
      required: true
      type: TEXT
    - context: item
      label: Item Poolpumpe-Rücklauf-Temperatur
      name: itemPoolpumpeRuecklaufTemp
      required: true
      type: TEXT
    - context: item
      label: Item Poolpumpe-Stromverbrauch
      name: itemPoolpumpeWatt
      required: true
      type: TEXT
    - context: item
      label: Item Solarwasser-Pool-Zulauf-Temperatur
      name: itemSolarPoolZulaufTemp
      required: true
      type: TEXT
    - context: item
      label: Item Solarwasser-Pool-Rücklauf-Temperatur
      name: itemSolarPoolRuecklaufTemp
      required: true
      type: TEXT
    - context: item
      label: Item Kessel-Zulauf-Temperatur
      name: itemSolarKesselZulaufTemp
      required: true
      type: TEXT
    - context: item
      label: Item Kessel-Rücklauf-Temperatur
      name: itemSolarKesselRuecklaufTemp
      required: true
      type: TEXT
    - context: item
      label: Item Automatik
      name: itemAutomatik
      required: true
      type: TEXT
    - context: item
      label: Item Modus
      name: itemModus
      required: true
      type: TEXT
    - default: light
      description: Farbschema
      label: Farbschema
      name: colortheme
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Mar 25, 2025, 4:43:11 PM
component: f7-card
config:
  footer: '="Modus: " + items[props.itemModus].state + ";  Automatik: " +
    items[props.itemAutomatik].state'
  style:
    background: '=(items[props.itemVentil].state == "ON") ?
      "url(/static/Solar_heating_pool4.svg)" :
      "url(/static/Solar_heating_boiler4.svg)"'
    background-position: center
    background-size: cover
    color: '=(props.colortheme == "dark") ? "white" : "black"'
    height: 400px
    width: 750px
  title: Solarthermie-Heizung
slots:
  content:
    - component: f7-row
      slots:
        default:
          - component: f7-block
            config:
              style:
                align-items: center
                display: flex
                flex-direction: row
                height: 0px
                justify-content: center
                margin-top: 130px
                margin-left: 203px
                width: 100%
            slots:
              default:
                - component: f7-col
                  config:
                    style:
                      align-items: center
                      display: flex
                      flex-direction: column
                  slots:
                    default:
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            display: flex
                            flex-direction: row
                            height: 50px
                            justify-content: center
                            margin-top: 0
                            width: 375px
                        slots:
                          default:
                            - component: Label
                              config:
                                style:
                                  color: '=(props.colortheme == "dark") ? "white" : "black"'
                                  font-size: 14px
                                  margin-left: 275px
                                  margin-top: 0px
                                  text-align: center
                                  width: 80px
                                text: =items[props.itemSolarZulaufTemp].displayState ||
                                  items[props.itemSolarZulaufTemp].state
                                visible: true
                - component: f7-col
                  config:
                    style:
                      align-items: center
                      display: flex
                      flex-direction: column
                  slots:
                    default:
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            display: flex
                            flex-direction: row
                            height: 0px
                            justify-content: center
                            margin-top: -0px
                            width: 375px
                        slots:
                          default:
                            - component: Label
                              config:
                                style:
                                  color: '=(props.colortheme == "dark") ? "white" : "black"'
                                  font-size: 14px
                                  margin-left: -275px
                                  margin-top: 0px
                                  text-align: left
                                  width: 80px
                                text: =items[props.itemSolarRuecklaufTemp].displayState ||
                                  items[props.itemSolarRuecklaufTemp].state
    - component: f7-row
      slots:
        default:
          - component: f7-block
            config:
              style:
                align-items: center
                display: flex
                flex-direction: row
                height: 0px
                justify-content: center
                margin-top: 34px
                margin-left: 270px
                width: 100%
            slots:
              default:
                - component: f7-col
                  config:
                    style:
                      align-items: center
                      display: flex
                      flex-direction: column
                  slots:
                    default:
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            display: flex
                            flex-direction: row
                            height: 0px
                            justify-content: center
                            margin-left: -0px
                            margin-top: 0px
                            width: 160px
                        slots:
                          default:
                            - component: Label
                              config:
                                style:
                                  color: '=(props.colortheme == "dark") ? "white" : "black"'
                                  font-size: 14px
                                  margin-left: 45px
                                  margin-top: 0px
                                  text-align: left
                                  width: 160px
                                text: =items[props.itemPoolpumpeRuecklaufTemp].displayState ||
                                  items[props.itemPoolpumpeRuecklaufTemp].state
                - component: f7-col
                  config:
                    style:
                      align-items: center
                      display: flex
                      flex-direction: column
                  slots:
                    default:
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            display: flex
                            flex-direction: row
                            height: 0px
                            justify-content: center
                            margin-left: -0px
                            margin-top: 0px
                            width: 160px
                        slots:
                          default:
                            - component: Label
                              config:
                                style:
                                  color: '=(props.colortheme == "dark") ? "white" : "black"'
                                  font-size: 14px
                                  margin-left: -55px
                                  margin-top: 0px
                                  text-align: center
                                  width: 160px
                                text: =items[props.itemSolarPoolZulaufKellerTemp].displayState ||
                                  items[props.itemSolarPoolZulaufKellerTemp].state
                - component: f7-col
                  config:
                    style:
                      align-items: center
                      display: flex
                      flex-direction: column
                  slots:
                    default:
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            display: flex
                            flex-direction: row
                            height: 0px
                            justify-content: center
                            margin-left: 0px
                            margin-top: 0px
                            width: 160px
                        slots:
                          default:
                            - component: Label
                              config:
                                style:
                                  color: '=(props.colortheme == "dark") ? "white" : "black"'
                                  font-size: 14px
                                  margin-left: -20px
                                  margin-top: 0px
                                  text-align: left
                                  width: 160px
                                text: =items[props.itemSolarKesselRuecklaufTemp].displayState ||
                                  items[props.itemSolarKesselRuecklaufTemp].state
    - component: f7-row
      slots:
        default:
          - component: f7-block
            config:
              style:
                align-items: center
                display: flex
                flex-direction: row
                height: 0px
                justify-content: center
                margin-top: 42px
                margin-left: 230px
                width: 100%
            slots:
              default:
                - component: f7-col
                  config:
                    style:
                      align-items: center
                      display: flex
                      flex-direction: column
                  slots:
                    default:
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            display: flex
                            flex-direction: row
                            height: 0px
                            justify-content: center
                            margin-left: -0px
                            margin-top: 0px
                            width: 160px
                        slots:
                          default:
                            - component: Label
                              config:
                                style:
                                  color: '=(props.colortheme == "dark") ? "white" : "black"'
                                  font-size: 14px
                                  margin-left: 0px
                                  margin-top: 0px
                                  text-align: left
                                  width: 160px
                                text: =items[props.itemSolarPoolZulaufTemp].displayState ||
                                  items[props.itemSolarPoolZulaufTemp].state
                - component: f7-col
                  config:
                    style:
                      align-items: center
                      display: flex
                      flex-direction: column
                  slots:
                    default:
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            display: flex
                            flex-direction: row
                            height: 0px
                            justify-content: center
                            margin-left: -35px
                            margin-top: 0px
                            width: 160px
                        slots:
                          default:
                            - component: Label
                              config:
                                style:
                                  color: '=(props.colortheme == "dark") ? "white" : "black"'
                                  font-size: 14px
                                  margin-left: 0px
                                  margin-top: 0px
                                  text-align: left
                                  width: 160px
                                text: =items[props.itemSolarPoolRuecklaufTemp].displayState ||
                                  items[props.itemSolarPoolRuecklaufTemp].state
                - component: f7-col
                  config:
                    style:
                      align-items: center
                      display: flex
                      flex-direction: column
                  slots:
                    default:
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            display: flex
                            flex-direction: row
                            height: 0px
                            justify-content: center
                            margin-left: -0px
                            margin-top: 0px
                            width: 160px
                        slots:
                          default:
                            - component: Label
                              config:
                                style:
                                  color: '=(props.colortheme == "dark") ? "white" : "black"'
                                  font-size: 14px
                                  margin-left: -226px
                                  margin-top: 0px
                                  text-align: center
                                  width: 160px
                                text: =items[props.itemSolarPoolRuecklaufKellerTemp].displayState ||
                                  items[props.itemSolarPoolRuecklaufKellerTemp].state
                - component: f7-col
                  config:
                    style:
                      align-items: center
                      display: flex
                      flex-direction: column
                  slots:
                    default:
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            display: flex
                            flex-direction: row
                            height: 0px
                            justify-content: center
                            margin-left: 0px
                            margin-top: 0px
                            width: 160px
                        slots:
                          default:
                            - component: Label
                              config:
                                style:
                                  color: '=(props.colortheme == "dark") ? "white" : "black"'
                                  font-size: 14px
                                  margin-left: -200px
                                  margin-top: 0px
                                  text-align: left
                                  width: 160px
                                text: =items[props.itemSolarKesselZulaufTemp].displayState ||
                                  items[props.itemSolarKesselZulaufTemp].state
    - component: f7-row
      slots:
        default:
          - component: f7-block
            config:
              style:
                align-items: center
                display: flex
                flex-direction: row
                height: 0px
                justify-content: center
                margin-top: 107px
                margin-left: 150px
                width: 100%
            slots:
              default:
                - component: f7-col
                  config:
                    style:
                      align-items: center
                      display: flex
                      flex-direction: column
                  slots:
                    default:
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            display: flex
                            flex-direction: row
                            height: 0px
                            justify-content: center
                            margin-left: -0px
                            margin-top: 0px
                            width: 160px
                        slots:
                          default:
                            - component: Label
                              config:
                                style:
                                  color: '=(props.colortheme == "dark") ? "white" : "black"'
                                  font-size: 14px
                                  margin-left: 20px
                                  margin-top: 0px
                                  text-align: left
                                  width: 160px
                                text: =items[props.itemPoolpumpeZulaufTemp].displayState ||
                                  items[props.itemPoolpumpeZulaufTemp].state
                - component: f7-col
                  config:
                    style:
                      align-items: center
                      display: flex
                      flex-direction: column
                  slots:
                    default:
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            display: flex
                            flex-direction: row
                            height: 0px
                            justify-content: center
                            margin-left: -0px
                            margin-top: 0px
                            width: 160px
                        slots:
                          default:
                            - component: Label
                              config:
                                style:
                                  color: '=(props.colortheme == "dark") ? "white" : "black"'
                                  font-size: 14px
                                  margin-left: 0px
                                  margin-top: 0px
                                  text-align: left
                                  width: 160px
                                text: =items[props.itemPoolpumpeWatt].displayState ||
                                  items[props.itemPoolpumpeWatt].state

The things, items and rules are very extensive - in case you’re interested in those I’ll happily attach them as files on request.

Looking forward to your feedback (and especially suggestions for optimizating the widget code)

Kind regards,
Ralph…

3 Likes

Hey Ralph, looks great. And i think you Invest hours, days or weeks to get this result.

Can you please attach the items, things and rules?

Thx for your great Work

Hi Manuel.

It’s rather complex, I tried to remove most of the stuff not related to the solar thermal system, one or two things or items might still be missing - feel free to ask in case you need more information/items/things etc.

Here are the files:
html_Solar_heating_boiler6
html_Solar_heating_pool4
Debug.items.txt (354 Bytes)
Poolsteuerung.items.txt (9.0 KB)
Solarsteuerung.items.txt (4.6 KB)
Pool.rules.txt (35.6 KB)
Solar.rules.txt (5.7 KB)
Mqtt.things.txt (10.1 KB)
EnergyMeters.items.txt (5.7 KB)

1 Like