Heat pump with dynamic colors and lables using picture elements card

Intro:
This thread is describing how I did set up a picture elements card to visualize the heating system in my house with dynamically changing colors corresponding to temperatures and changing lables corresponding to the current heater activity.

5 Likes

Demo:
How the card looks like and changes over time during a sometimes sunny morning after a cold night beginning of April in southern Germany.
HeizungGIF

2 Likes

Description heating system:
The heating system is composed of a geothermal heat pump with a ‘internet service gateway’ that offers a local intranet page and is accessible with modbus. Additionally the heat pump gateway is interacting with the energy management of the photovoltaics system. If there is excess solar power available then the EMI status will change to high and the heat pump is increasing the temperatures in the water tank to 55°C for hot water and 50°C for heating water.

Additionally there are 2 solar thermal panels on the roof only heating the hot water and not the heating water.

Description sensors:
The sensors were integrated here with an Arduino based 1Wire to KNX bridge. In total there are 15 1Wire temperature sensors installed for the hot water tank (4 sensors), the heating water tank (4 sensors), the floor heating before the mixer (2 sensors) and after the mixer (2 sensors), the solar thermal panel (2 sensors), and the cold water supply from the grid (1 sensor). The sensor placement on the pipes is not perfect yet but sufficient for this demo here.

The temperatures stated in between the heat pump and the water tanks are collected with the HA modbus integration reading values from the heatpump gateway on my LAN. Same applies for the heat pump parameters (power, operating mode and state of the enegy management interface).

The temperatures of the geothermal heat exchanger are collected in HA with scraping them from the local heat pump gateway intranet page since not accessible with modbus.

Picture Elements Card Configuration:

type: picture-elements
image: /local/pictures/heizung.png
style: |
  ha-card {
    {% set tww1 = (states('sensor.ti_ww1') | float(0) -10) %}
    {% set tww2 = (states('sensor.ti_ww2') | float(0) -10) %}
    {% set tww3 = (states('sensor.ti_ww3') | float(0) -10) %}
    {% set tww4 = (states('sensor.ti_ww4') | float(0) -10) %}
    {% set tpuffer1 = (states('sensor.ti_puffer1') | float(0) -10) %}
    {% set tpuffer2 = (states('sensor.ti_puffer2') | float(0) -10) %}
    {% set tpuffer3 = (states('sensor.ti_puffer3') | float(0) -10) %}
    {% set tpuffer4 = (states('sensor.ti_puffer4') | float(0) -10) %}
    {% set tsolarvor = (states('sensor.ti_solarvor') | float(0) -10) %}
    {% set tsolarrueck = (states('sensor.ti_solarrueck') | float(0) -10) %}
    {% set betrieb = states('sensor.wp2501') | float(0) %}
    --my-state-color-ww1: rgb({{255/50*tww1}}, 0, {{255/50*(50-tww1)}});
    --my-state-color-ww2: rgb({{255/50*tww2}}, 0, {{255/50*(50-tww2)}});
    --my-state-color-ww3: rgb({{255/50*tww3}}, 0, {{255/50*(50-tww3)}});
    --my-state-color-ww4: rgb({{255/50*tww4}}, 0, {{255/50*(50-tww4)}});   
    --my-state-color-puffer1: rgb({{255/50*tpuffer1}}, 0, {{255/50*(50-tpuffer1)}});
    --my-state-color-puffer2: rgb({{255/50*tpuffer2}}, 0, {{255/50*(50-tpuffer2)}});
    --my-state-color-puffer3: rgb({{255/50*tpuffer3}}, 0, {{255/50*(50-tpuffer3)}});
    --my-state-color-puffer4: rgb({{255/50*tpuffer4}}, 0, {{255/50*(50-tpuffer4)}});              
    --my-state-color-solarvor: rgb({{255/50*tsolarvor}}, 0, {{255/50*(50-tsolarvor)}});
    --my-state-color-solarrueck: rgb({{255/50*tsolarrueck}}, 0, {{255/50*(50-tsolarrueck)}});              
    {% if states('sensor.wp2501') | float(0) == 2 %}
      --my-state-color-vorlaufww: transparent;
      --my-state-color-ruecklaufww: transparent;
      --my-state-color-vorlaufheizung: transparent;
      --my-state-color-ruecklaufheizung: transparent;
    {% elif states('sensor.wp2501') | float(0) == 83 %}
      --my-state-color-vorlaufww: transparent;
      --my-state-color-ruecklaufww: transparent;
      --my-state-color-vorlaufheizung: red;
      --my-state-color-ruecklaufheizung: blue;  
    {% elif states('sensor.wp2501') | float(0) == 99%} 
      --my-state-color-vorlaufww: red;
      --my-state-color-ruecklaufww: blue;
      --my-state-color-vorlaufheizung: transparent;
      --my-state-color-ruecklaufheizung: transparent; 
    {% else %} 
      --my-state-color-vorlaufww: transparent;
      --my-state-color-ruecklaufww: transparent;
      --my-state-color-vorlaufheizung: transparent;
      --my-state-color-ruecklaufheizung: transparent;      
    {% endif %}
  }
elements:
  - type: state-label
    entity: sensor.ti_puffer1
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 29.2%
      left: 27.8%
      color: transparent
      background: >-
        linear-gradient(to bottom, var(--my-state-color-ww1) 0%,
        var(--my-state-color-ww2) 100%)
  - type: state-label
    entity: sensor.ti_puffer1
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 43.6%
      left: 27.8%
      color: transparent
      background: >-
        linear-gradient(to bottom, var(--my-state-color-ww2) 0%,
        var(--my-state-color-ww3) 100%)
  - type: state-label
    entity: sensor.ti_puffer1
    style:
      width: 12.4%
      height: 14.3%
      position: absolute
      transform: translate(0,0)
      top: 58%
      left: 27.8%
      color: transparent
      background: >-
        linear-gradient(to bottom, var(--my-state-color-ww3) 0%,
        var(--my-state-color-ww4) 100%)   
  - type: state-label
    entity: sensor.ti_puffer1
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 29.2%
      left: 75.5%
      color: transparent
      background: >-
        linear-gradient(to bottom, var(--my-state-color-puffer1) 0%,
        var(--my-state-color-puffer2) 100%)
  - type: state-label
    entity: sensor.ti_puffer1
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 43.6%
      left: 75.5%
      color: transparent
      background: >-
        linear-gradient(to bottom, var(--my-state-color-puffer2) 0%,
        var(--my-state-color-puffer3) 100%)
  - type: state-label
    entity: sensor.ti_puffer1
    style:
      width: 12.4%
      height: 14.3%
      position: absolute
      transform: translate(0,0)
      top: 58%
      left: 75.5%
      color: transparent
      background: >-
        linear-gradient(to bottom, var(--my-state-color-puffer3) 0%,
        var(--my-state-color-puffer4) 100%)
  - type: state-label
    entity: sensor.ti_puffer1
    style:
      width: 2.3%
      height: 41.2%
      position: absolute
      transform: rotate(-0.338turn)
      top: '-3.0%'
      left: 13.6%
      color: transparent
      background: >-
        linear-gradient(to bottom, var(--my-state-color-solarrueck) 0%,
        var(--my-state-color-solarvor) 100%)
  - type: state-label
    entity: sensor.ti_ww1
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 27%
      left: 28%
      font-size: 12px
      color: black
  - type: state-label
    entity: sensor.ti_ww2
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 39%
      left: 28%
      font-size: 12px
      color: black
  - type: state-label
    entity: sensor.ti_ww3
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 51%
      left: 28%
      font-size: 12px
      color: black
  - type: state-label
    entity: sensor.ti_ww4
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 63%
      left: 28%
      font-size: 12px
      color: black
  - type: state-label
    entity: sensor.ti_puffer1
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 27%
      left: 75%
      font-size: 12px
      color: black
  - type: state-label
    entity: sensor.ti_puffer2
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 39%
      left: 75%
      font-size: 12px
      color: black
  - type: state-label
    entity: sensor.ti_puffer3
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 51%
      left: 75%
      font-size: 12px
      color: black
  - type: state-label
    entity: sensor.ti_puffer4
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 63%
      left: 75%
      font-size: 12px
      color: black
  - type: state-label
    entity: sensor.ti_solarvor
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 48%
      left: 12%
      font-size: 12px
      color: red
  - type: state-label
    entity: sensor.ti_solarrueck
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 55%
      left: 12%
      font-size: 12px
      color: blue
  - type: state-label
    entity: sensor.ti_fbhvor
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 16%
      left: 64.5%
      font-size: 12px
      color: red
  - type: state-label
    entity: sensor.ti_fbhrueck
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: '-1%'
      left: 64.5%
      font-size: 12px
      color: blue
  - type: state-label
    entity: sensor.ti_heizvor
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 16%
      left: 85%
      font-size: 12px
      color: red
  - type: state-label
    entity: sensor.ti_heizrueck
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: '-1%'
      left: 85%
      font-size: 12px
      color: blue
  - type: state-label
    entity: sensor.ti_kw
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 69%
      left: 12%
      font-size: 12px
      color: blue
  - type: state-label
    entity: sensor.wp_sole_vorlauf
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 74%
      left: 62%
      font-size: 12px
      color: red
  - type: state-label
    entity: sensor.wp_sole_ruecklauf
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 74%
      left: 40%
      font-size: 12px
      color: blue
  - type: state-label
    entity: sensor.wp542
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 43%
      left: 39.2%
      font-size: 12px
      color: var(--my-state-color-ruecklaufww)
  - type: state-label
    entity: sensor.wp543
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 27%
      left: 39.2%
      font-size: 12px
      color: var(--my-state-color-vorlaufww)
  - type: state-label
    entity: sensor.wp542
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 66%
      left: 64%
      font-size: 12px
      color: var(--my-state-color-ruecklaufheizung)
  - type: state-label
    entity: sensor.wp543
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 36%
      left: 64%
      font-size: 12px
      color: var(--my-state-color-vorlaufheizung)
  - type: state-label
    entity: sensor.blank
    prefix: 'Leistung: '
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 30%
      left: 49%
      font-size: 10px
      font-weight: bold
      color: black
  - type: state-label
    entity: sensor.wp_leistungsaufnahme
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 35%
      left: 49%
      font-size: 10px
      color: black
  - type: state-label
    entity: sensor.blank
    prefix: 'Betriebsart: '
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 45%
      left: 49%
      font-size: 10px
      font-weight: bold
      color: black
  - type: state-label
    entity: sensor.betriebsart_heizung
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 50%
      left: 49%
      font-size: 10px
      color: black
  - type: state-label
    entity: sensor.blank
    prefix: 'EMI Status: '
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 60%
      left: 49%
      font-size: 10px
      font-weight: bold
      color: black
  - type: state-label
    entity: sensor.wp_energiemanagement_status
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 65%
      left: 49%
      font-size: 10px
      color: black
  - type: state-badge
    entity: sensor.energie_warmwasser_prozent
    prefix: 'Warmwasser '
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 54%
      left: 41.5%
      '--ha-label-badge-title-font-size': 0px
      font-size: 8px
      color: transparent
  - type: state-badge
    entity: sensor.energie_heizung_prozent
    prefix: 'Heizung '
    title: asd
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 54%
      left: 89%
      '--ha-label-badge-title-font-size': 0px
      font-size: 8px

Explanations on Card Configuration 1: temperature dynamic colors
the colors of the water tanks and the solar thermal panal are changing with temperature - 60°C being red and 10°C being blue. The corresponding temperature range, delta temperature, is thus 50°C.

The color of the current temperature is calculated basically in 2 steps:

  1. the actual temperature from the sensor is reduced by 10; this sets 10°C to 0 as the minimum temperature and sets 60°C to 50 as the maximum temperature (delta temperature)
  2. the recalibrated temperature (step 1) is now used for scaling the rgb value (255)
    For red: 255 * recalibrated temperature / 50 → 0°C gives you 0 and 60°C gives you 255
    For blue: 255 * (50 - recalibrated temperature) / 50 → 0°C gives you 255 and 60°C gives you 0

The color of the tanks and the solar thermal is the set up as a gradient in between the color associated with 2 sensor readings: e.g. the color gradient in between sensor ww1 and ww2, i.e.

background: >-
  linear-gradient(to bottom, var(--my-state-color-ww1) 0%, var(--my-state-color-ww2) 100%)

Explanations on Card Configuration 2: state dynamic lables
the lables in between the heat pump and the tanks show up dynamically depending on the state of the heat pump. if the heat pump is heating hot water then the values show up on the left side of the heatpump. If the heat pump is warming up the heating water tank then the values are showing up on the right side of the tank.

This feature is realized here with defining text colors for all 4 sensors and changing the colors conditionally depending on the heat pump operating mode. For each operating mode some sensor values are not showing up by setting the text color to transparent.

The background image:
the image of the hydraulic system was generated with a freeware called ‘TAPPS2’ and the postprocessed with some image editing software to get it to the right size and crop away some parts of the image.

Open items / ongoing activities:
the display on a large screen is currently ~1/3 of the screen wide. The panel mode has been briefly tested but some of the sensor values were misaligned. Currently I did not invest additional time into getting the panel mode / view running.

If you have some hint on how to get panel mode up and running without too much effort then please let me know.

Looks great, and I really want to use this for a project for a hot water cylinder. To determinte how much hot water I have left.

But… Im useless at this and really stuck. I’m just using a transparent image of a hot water tank and playing around with your shared code. But i can’t get the sensors or image to show like yours. I’m more than likely doing something bad here. Im just using sensors i have currently and plan to get proper temp sensors and install Shelly 1PM to control my hot water tank, plus have 5 temp sensors on there.

What am i doing wrong here? Can you help? I just want the colours to show like yours and i replaced the sensors with my own. Thought it would work but no colours show with the temperatures



type: picture-elements
image: /local/test.png
style: |
  ha-card {
    {% set tww1 = (states('sensor.0x00124b0029209ca1_temperature') | float(0) -10) %}
    {% set tww2 = (states('sensor.spare_room_inside_temperature') | float(0) -10) %}
    {% set tww3 = (states('sensor.my_bedroom_inside_temperature') | float(0) -10) %}
    {% set tww4 = (states('sensor.my_bedroom_outside_temperature') | float(0) -10) %}
    --my-state-color-ww1: rgb({{255/50*tww1}}, 0, {{255/50*(50-tww1)}});
    --my-state-color-ww2: rgb({{255/50*tww2}}, 0, {{255/50*(50-tww2)}});
    --my-state-color-ww3: rgb({{255/50*tww3}}, 0, {{255/50*(50-tww3)}});
    --my-state-color-ww4: rgb({{255/50*tww4}}, 0, {{255/50*(50-tww4)}});   
  }
elements:
  - type: state-label
    entity: sensor.0x00124b0029209ca1_temperature
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 29.2%
      left: 27.8%
      color: transparent
      background: >-
        linear-gradient(to bottom, var(--my-state-color-ww1) 0%,
        var(--my-state-color-ww2) 100%)
  - type: state-label
    entity: sensor.my_bedroom_inside_temperature
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 43.6%
      left: 27.8%
      color: transparent
      background: >-
        linear-gradient(to bottom, var(--my-state-color-ww2) 0%,
        var(--my-state-color-ww3) 100%)
  - type: state-label
    entity: sensor.my_bedroom_outside_temperature
    style:
      width: 12.4%
      height: 14.3%
      position: absolute
      transform: translate(0,0)
      top: 58%
      left: 27.8%
      color: transparent
      background: >-
        linear-gradient(to bottom, var(--my-state-color-ww3) 0%,
        var(--my-state-color-ww4) 100%)   
  - type: state-label
    entity: sensor.0x00124b0029209ca1_temperature
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 27%
      left: 28%
      font-size: 12px
      color: black
  - type: state-label
    entity: sensor.0x00124b0029209ca1_temperature
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 39%
      left: 28%
      font-size: 12px
      color: black
  - type: state-label
    entity: sensor.my_bedroom_inside_temperature
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 51%
      left: 28%
      font-size: 12px
      color: black
  - type: state-label
    entity: sensor.my_bedroom_outside_temperature
    style:
      width: 12.4%
      height: 14.4%
      position: absolute
      transform: translate(0,0)
      top: 63%
      left: 28%
      font-size: 12px
      color: black

You need to install card mod :slight_smile:

I did a similar visualization but I have multiple transparent PNGs that are enabled or disabled based on the state. The colors don’t change, but the circuits that aren’t active go gray and the fan heat exchanger (top) has the width of the lines adjust to reflect the fan speed. This is the net result:

2 Likes

@dbs Looks also great. Could you share your configuration for this?

Thanks

@MichaelST01 here you are!
The “blank.png” is the image that is displayed if the device is off (it’s a PNGs that is the same size but all transparent). The “base3_all_off.png” is the graphic with all elements off (gray). I created the images by drawing the whole thing in PowerPoint, then copying each slide as a PDF to Apple’s Preview, then saving it as a PNG. I then used Preview to delete the regions that should be transparent and re-saved them.
-David

type: vertical-stack
cards:
  - type: picture-elements
    image: /local/heat_graphics2/base3_all_off.png
    theme: light
    elements:
      - entity: binary_sensor.heatpump_pump_cold_circuit
        type: image
        image: /local/heat_graphics2/blank.png
        state_image:
          'on': /local/heat_graphics2/base3_brine_on.png
        style:
          left: 0%
          top: 0%
          transform: none
          width: 100%
      - entity: binary_sensor.heatpump_compressor
        type: image
        image: /local/heat_graphics2/blank.png
        state_image:
          'on': /local/heat_graphics2/base3_compressor_on.png
        style:
          left: 0%
          top: 0%
          transform: none
          width: 100%
      - entity: sensor.heatpump_status_direct
        type: image
        image: /local/heat_graphics2/blank.png
        state_image:
          Idle: /local/heat_graphics2/blank.png
          Water Heat Pumping: /local/heat_graphics2/base3_water_heat_on.png
          Floor Heat Pumping: /local/heat_graphics2/base3_floor_heat_on.png
        style:
          left: 0%
          top: 0%
          transform: none
          width: 100%
      - entity: sensor.ftx_supply_fan_mode
        type: image
        image: /local/heat_graphics2/blank.png
        state_image:
          Low: /local/heat_graphics2/base3_ftx_supply_low.png
          Normal: /local/heat_graphics2/base3_ftx_supply_normal.png
          High: /local/heat_graphics2/base3_ftx_supply_high.png
        style:
          left: 0%
          top: 0%
          transform: none
          width: 100%
      - entity: sensor.ftx_extract_fan_mode
        type: image
        image: /local/heat_graphics2/blank.png
        state_image:
          Low: /local/heat_graphics2/base3_ftx_extract_low.png
          Normal: /local/heat_graphics2/base3_ftx_extract_normal.png
          High: /local/heat_graphics2/base3_ftx_extract_high.png
        style:
          left: 0%
          top: 0%
          transform: none
          width: 100%
      - entity: binary_sensor.heatpump_pump_radiator
        type: image
        image: /local/heat_graphics2/blank.png
        state_image:
          'on': /local/heat_graphics2/base3_floor_on.png
        style:
          left: 0%
          top: 0%
          transform: none
          width: 100%
      - entity: sensor.ftx_heater_on
        type: image
        image: /local/heat_graphics2/blank.png
        state_image:
          'on': /local/heat_graphics2/base3_ftx_electric_on.png
          'off': /local/heat_graphics2/blank.png
        style:
          left: 0%
          top: 0%
          transform: none
          width: 100%
      - entity: sensor.heatpump_heater_on
        type: image
        image: /local/heat_graphics2/blank.png
        state_image:
          'on': /local/heat_graphics2/base3_heat_pump_electric_on.png
          'off': /local/heat_graphics2/blank.png
        style:
          left: 0%
          top: 0%
          transform: none
          width: 100%
      - entity: binary_sensor.heatpump_alarm
        type: image
        image: /local/heat_graphics2/blank.png
        state_image:
          'on': /local/heat_graphics2/base3_heatpump_warning.png
        style:
          left: 0%
          top: 0%
          transform: none
          width: 100%
      - entity: binary_sensor.ftx_alarm
        type: image
        image: /local/heat_graphics2/blank.png
        state_image:
          'on': /local/heat_graphics2/base3_ftx_warning.png
        style:
          left: 0%
          top: 0%
          transform: none
          width: 100%
      - entity: climate.heatpump_room_temp_setpoint
        attribute: current_temperature
        style:
          left: 2%
          top: 50.5%
          transform: translate(0%, -50%)
          font-size: 1.5vw
          text-decoration: underline
          color: black
        type: state-label
        prefix: 'Setpoint: '
        suffix: °C
      - entity: sensor.heatpump_status_direct
        type: state-label
        style:
          right: 46.5%
          top: 81%
          transform: translate(0%, -50%)
          font-size: 1vw
          color: black
      - entity: sensor.heatpump_brine_out_condenser
        style:
          right: 86%
          top: 58.8%
          font-size: 1.5vw
          transform: translate(0%,-50%)
          color: black
        type: state-label
      - entity: sensor.heatpump_brine_in_evaporator
        style:
          right: 86%
          top: 70%
          font-size: 1.5vw
          transform: translate(0%,-50%)
          color: black
        type: state-label
      - entity: sensor.heatpump_radiator_forward
        style:
          left: 83.5%
          top: 63.5%
          transform: translate(0%,-50%)
          font-size: 1.5vw
          color: black
        type: state-label
      - entity: sensor.heatpump_heating_setpoint
        style:
          left: 83.5%
          top: 67.5%
          transform: translate(0%,-50%)
          font-size: 1vw
          color: black
        prefix: 'Target: '
        type: state-label
      - entity: sensor.heatpump_heat_carrier_forwrd
        style:
          right: 45.7%
          top: 58.8%
          font-size: 1.5vw
          transform: translate(0%,-50%)
          color: black
        type: state-label
      - entity: sensor.heatpump_heat_carrier_return
        style:
          right: 45.7%
          top: 70%
          font-size: 1.5vw
          transform: translate(0%,-50%)
          color: black
        type: state-label
      - entity: sensor.heatpump_warm_water_1_top
        style:
          left: 73.8%
          top: 81.6%
          font-size: 1.5vw
          color: black
        type: state-label
      - entity: sensor.heatpump_hot_gas
        style:
          right: 68%
          top: 58.8%
          font-size: 1.5vw
          transform: translate(0%,-50%)
          color: black
        type: state-label
      - entity: sensor.heat_pump_outdoor_temperature_without_offset
        style:
          left: 7%
          top: 81%
          transform: translate(0%, -50%)
          font-size: 1vw
          color: black
        type: state-label
        prefix: 'Outside: '
      - entity: sensor.heatpump_add_heat_status
        style:
          left: 42%
          bottom: 45%
          font-size: 1.5vw
          transform: translate(-50%,50%)
          color: black
        type: state-label
      - entity: sensor.ftx_humidity_filtered
        suffix: RH
        style:
          left: 79.5%
          top: 26.5%
          transform: translate(0%,-50%)
          font-size: 1.5vw
          color: black
        type: state-label
      - entity: sensor.ftx_extract_temperature_filtered
        style:
          left: 79.5%
          top: 30%
          transform: translate(0%,-50%)
          font-size: 1.5vw
          color: black
        type: state-label
      - entity: sensor.ftx_supply_temperature
        style:
          right: 36%
          top: 30%
          transform: translate(50%,-50%)
          font-size: 1.5vw
          color: black
        type: state-label
      - entity: sensor.ftx_exhaust_temperature
        style:
          right: 94%
          top: 12.5%
          font-size: 1.5vw
          transform: translate(50%,-50%)
          color: black
        type: state-label
      - entity: sensor.ftx_outdoor_temperature
        style:
          right: 94%
          top: 22.5%
          font-size: 1.5vw
          transform: translate(50%,-50%)
          color: black
        type: state-label
      - entity: sensor.ftx_heater_active
        type: state-label
        style:
          left: 44%
          top: 30%
          font-size: 1.5vw
          transform: translate(-50%,-50%)
          color: black
      - entity: sensor.ftx_heat_exchanger_active
        type: state-label
        style:
          left: 22%
          top: 18%
          font-size: 1.5vw
          transform: translate(-50%,-50%)
          color: black
      - entity: sensor.ftx_extract_efficiency
        type: state-label
        style:
          left: 28%
          top: 26.5%
          font-size: 1vw
          transform: translate(-50%,-50%)
          color: black
        suffix: ' eff'
      - entity: sensor.ftx_exhaust_efficiency
        type: state-label
        style:
          left: 28%
          top: 8.5%
          font-size: 1vw
          transform: translate(-50%,-50%)
          color: black
        suffix: ' eff'
      - entity: sensor.ftx_supply_fan_speed_percent
        type: state-label
        style:
          left: 36.5%
          top: 30%
          font-size: 1.5vw
          transform: translate(-50%,-50%)
          color: black
      - entity: sensor.ftx_extract_fan_speed_percent
        type: state-label
        style:
          left: 36.5%
          top: 5.7%
          font-size: 1.5vw
          transform: translate(-50%,-50%)
          color: black
      - entity: sensor.kitchen_fan_status
        type: state-label
        style:
          left: 92.5%
          top: 54%
          transform: translate(0%,-50%)
          font-size: 1.5vw
          color: black
      - entity: sensor.bathroom_humidity
        type: state-label
        suffix: RH
        style:
          left: 88.5%
          top: 40%
          transform: translate(0%,-50%)
          font-size: 1.5vw
          color: black
      - entity: sensor.bathroom_temperature
        type: state-label
        style:
          left: 88.5%
          top: 43.5%
          transform: translate(0%,-50%)
          font-size: 1.5vw
          color: black
      - entity: sensor.upstairs_humidity
        type: state-label
        suffix: RH
        style:
          left: 78%
          top: 42%
          font-size: 1.5vw
          color: black
      - entity: sensor.upstairs_temperature
        type: state-label
        style:
          left: 69.5%
          top: 42%
          font-size: 1.5vw
          color: black
      - entity: sensor.downstairs_humidity
        type: state-label
        suffix: RH
        style:
          left: 78%
          top: 54%
          font-size: 1.5vw
          color: black
      - entity: sensor.downstairs_temperature
        type: state-label
        style:
          left: 69.5%
          top: 54%
          font-size: 1.5vw
          color: black
      - entity: sensor.behind_house_temperature
        type: state-label
        style:
          right: 32.0%
          top: 54%
          font-size: 1.5vw
          color: black
      - entity: sensor.behind_house_luminance
        type: state-label
        prefix: ☀️
        style:
          right: 0.5%
          top: 2%
          font-size: 1.5vw
          color: black
      - entity: input_number.ftx_temperature_setpoint
        style:
          left: 19%
          top: 4%
          transform: translate(0%, -50%)
          font-size: 1.5vw
          text-decoration: underline
          color: black
        type: state-label
        prefix: 'Setpoint: '
      - entity: input_select.ftx_active_user_mode
        type: state-label
        style:
          left: 19%
          top: 34.4%
          transform: translate(0%, -50%)
          font-size: 1.5vw
          text-decoration: underline
          color: black
        prefix: 'Mode: '
      - entity: input_select.ftx_manual_fan_speed
        type: state-label
        style:
          left: 36.5%
          top: 34.4%
          transform: translate(-50%, -50%)
          font-size: 1.5vw
          text-decoration: underline
        prefix: 'Fans: '
      - entity: switch.ftx_eco_mode_enabled
        type: state-label
        style:
          left: 44%
          top: 34.4%
          transform: translate(-50%, -50%)
          font-size: 1.5vw
          text-decoration: underline
          color: black
        prefix: 'Eco: '

Note that I added “color:black” so that if a device is in dark mode the text is still visible on the white background.
-David