In questo post vediamo come costruire una dashboard lovelace mediante la quale controllare il riscaldamento intelligente della nostra casa. Il punto di partenza è che abbiamo un certo numero di caloriferi dotati di dispositivi smart per la regolazione della temperatura, ad esempio le testine termostatiche Tado che abbiamo visto nel post dedicato e che abbiamo automazioni che agiscono sulle valvole in base allo stato delle finestre nelle stanze riscaldate, come raccontato nel post dedicato

Punto di partenza: caloriferi integrati in HA

Supponiamo dunque di essere in grado di:

  • agire su ciascun calorifero accendere, spegnere o per settare la temperatura desiderata
  • leggere (in tutte o alcune stanze) la temperatura mediante sensori esterni
  • intervenire sulle valvole per correggere errori mediante impostazione degli offset
  • conoscere lo stato di apertura delle finestre in prossimità dei caloriferi

Cosa vogliamo fare: la dahsboard lovelace

Il nostro obiettivo è riuscire a gestire tutto quanto concerne il riscaldamento della nostra casa da un unico punto di accesso, una pagina della nostro HA, ovvero una dashboard lovelace dedicata.

Va detto che esistono numerose card, sia native sia custom, per la gestione delle entità climate, ma ognuna di queste utilizza in modo più o meno avanzato le informazioni della sola entità di riferimento.

L’esigenza a cui stiamo rispondendo qui è quella di usare una card generica, la diffusissima button-card, per mostrare tutte le info inerenti il riscaldamento di una stanza, non solo quelle dell’entità climate.

Costruiamo un template base con button-card

Sfruttando la potenza e la flessibilità della button-card ed in particolare il meccanismo di template, riusciamo a creare il nostro modello di card che poi andremo ad utilizzare per tutti i dispositivi che vogliamo controllare nella nostra dashboard.

Partiamo con definire le proprietà base card: nascondiamo le aree native state e label, mentre mostriamo name ed icon, ai quali aggiungeremo appositi custom_field per ospitare le icone rappresentative dello stato dei sensori che ci interessano. Chiameremo il nostro template tado:

tado:
  show_state: false
  show_label: false
  show_name: true
  show_icon: true

Impostiamo il comportamento di base della card: un tap in qualsiasi suo punto aprirà il popup nativo con le informazioni sulla entità di riferimento:

  # nodo figlio di primo livello
  tap_action:
    action: more-info

Icone dinamiche

Lavoriamo sull’icona: possiamo personalizzare la card utlizzando le icone che più ci aggradano tra le migliaia di icone disponibili legandole ai possibili stati dell’entità climate, che nel caso dei dispositivi Tado sono auto, heat e off:

  # nodo figlio di primo livello
  icon: >
    [[[
      if (entity.state=="heat") 
        return "mdi:fire"
      else if (entity.state=="auto") 
        return "mdi:clock-outline"
      else
        return "mdi:power"
    ]]]    

Variabili

La card ci conente di definire variabili, da valorizzare opportunamente quando la istanziamo nella dashboard lovelace, per realizzare eventuali logiche o aspetti grafici personalizzati. Nel nostro caso, abbiamo bisogno di due variabili:

  • window_sensor è il nome del sensore di aapertura finestra
  • device_name è il nome del device tado, visibile sull’app oppure su my.tado.com, che ci permetterà di accedere ai sensori creati automaticamente dall’integrazione, in particolare allo stato della batteria e del segnale wifi.
  # nodo figlio di primo livello
  variables:
    window_sensor: ""
    device_name: ""

Personalizziamo lo stile

Con le personalizzazioni dello stile, possiamo dare alla nostra card l’aspetto che vogliamo e decidere graficamente come si adatterà ai vari stati operativi dell’entità climate collegata.

Le dimensioni Particolare attenzione va prestata alle dimensioni della card e di ogni sua componente. Le misure in px nel nostro esempio sono calcolate considerando:

  • layout che si desidera attribuire alla dashboard
  • numero di card da disporre orizzontalmente/verticalmente
  • risoluzione dettagliata del dispositivo su cui si visualizza la dashboard
  • puoi visualizzare i dettagli del tuo video a questo link
  • come riferimento prendere la dimensione netta della finestra indicata

Un elemento che colpisce anche uno sguardo distratto è senza dubbio il colore. Per questo motivo, la nostra card assumerà colori differenti di bordo e icona a seconda dello state (da non confondere con hvac_action che useremo più avanti) in cui si trova l’entità:

  • heating: bordo e icona colore arancio
  • auto: bordo e icona bianco
  • off: bordo e icona grigio scuro (#4D4D4D)
  # nodo figlio di primo livello
  styles:
    card:     
      - height: 170px
      - width: 170px
      - border-radius: 50%
      - border: >
          [[[
            if (entity.state=="off")
              return "3px solid #4d4d4d"
            if (entity.attributes.hvac_action=="heating")
              return "3px solid orange"
            else
              return "3px solid white" 
          ]]]                  
    label: [ padding-bottom: 20px, font-size: 12px ]
    name: [ font-size: 16px, padding-top: 10px ]
    icon:
      - background: black
      - border-radius: 50%
      - width: 36px
      - height: 36px
      - color: >
          [[[
            if (entity.attributes.hvac_action=="heating")
              return "orange"
            else if (entity.state=="off")
              return "#4d4d4d"                 
            else
              return "white" 
          ]]]          

Layout a griglia

Siamo ad un punto fondamentale: il layout della card. In queste poche righe andiamo a definire come si compone la nostra card, ovvero righe, colonne e relative dimensioni. Nel nostro esempio abbiamo 5 righe ed 1 sola colonna:


Nella figura precedente, a causa delle ridotte dimensioni delle icone, alcune aree risultano leggermente sovrapposte: questo è gestito mediante le proprietà di stile padding dell’area stessa.

    # nodo figlio ddel campo styles          
    grid:
      - grid-template-areas: '"n" "device_info" "target" "temp" "i"'
      - grid-template-rows: 20px 10px 20px 50px 60px
      - grid-template-columns: 168px
    custom_fields:
      device_info: [ padding-top: 13px ]
      target: [ padding-top: 20px, font-size: 14px ]
      temp: [ padding-top: 5px, font-family: sans-serif, font-size: 36px, font-weight: bold, fill: white]      

Campi personalizzati

Continuiamo la costruzione del template andando a definire il contenuto delle aree. A questo punto entrano in gioco le variabili che abbiamo definito qualche step indietro. Utilizzando la sintassi javascript-like disponibile nella configurazione della card, possiamo semplicemente accedere al valore di una variabile con la forma variables.nome_variabile.

Nel caso del device_name ricordiamo che con questa variabile vogliamo accedere al valore dei sensori creati automaticamente dall’integrazione Tado. Possiamo recuperarli madiante la solita pagina di Strumenti per sviluppatori:


Nella variabile device_info salveremo soltanto il nome del device, ovvero la parte VA0123456789: concatenando questa con _battery_state prima e con _connection_state poi, otterremo i valori dei sensori binari che ci interessano:

  • stato batteria: states['binary_sensor.' + variables.device_name + '_battery_state'].state
  • stato segnale wifi: states['binary_sensor.' + variables.device_name + '_connection_state'].state.

Per ciascuna di queste informazioni usiamo poi una specifica icona, che sarà normalmente spenta (nero) oppure accesa (rosso) nel caso il relativo sensore sia off.

  # nodo figlio di primo livello
  custom_fields:
    device_info: >
      [[[
        var c="black"
        var i="mdi:wifi"
        if (variables.device_name && states['binary_sensor.' + variables.device_name + '_connection_state'].state == 'off') { 
            c="red"
            i="mdi:wifi-alert"
          }
        var return_wifi='<ha-icon icon="'+i+'" style="width:25px; height:15px; color:'+c+'"></ha-icon>'
        c="black"
        i="mdi:battery-outline"
        if (variables.device_name && states['binary_sensor.' + variables.device_name + '_battery_state'].state == 'on') { 
            c="red"
            i="mdi:battery-alert-variant-outline"
          }
        var return_batt='<ha-icon icon="'+i+'" style="width:25px; height:15px; color:'+c+'"></ha-icon>'
        c="black"
        if (variables.window_sensor && states[variables.window_sensor].state == 'on') { 
            c="red"
          }
        var return_wind='<ha-icon icon="mdi:window-open-variant" style="width:25px; height:15px; color:'+c+'"></ha-icon>' 
        return (return_wifi + return_wind + return_batt)
      ]]]      

Concludiamo definendo le informazioni mostrate negli altri due campi personalizzati. Particolare attenzione al campo target per cui prenderemo in considerazione non solo lo stato dell’entità di riferimento, ma anche il valore dell’attributo hvac_action. Quest’ultimo indica, a prescindere dall’impostazione del device (auto o manuale) l’azione effettiva corrente: riscaldamento, inattivo o spento. Valutando congiuntamente queste due informazioni andiamo a compilare il campo:

    # nodo figlio del campo custom_fields         
    target: >
        [[[ 
          if (entity.attributes.hvac_action=="heating") 
            return "Riscaldamento a " + entity.attributes.temperature
          else if (entity.state=="off")
            return "Spento"     
          else if (entity.state=="auto" && entity.attributes.current_temperature==entity.attributes.temperature)
            return "Impostato a OFF"                
          else
            return "Impostato a " + entity.attributes.temperature
        ]]]        
    temp: >
        [[[ return entity.attributes.current_temperature ]]]

Il campo temp contiene sempicemente la temperatura corrente, che ricaviamo da un attributo specifico dell’entità di riferimento.

Template derivato per dispositivi con offset

La card ci consente di costruire dei template derivandone un altro già definito: questo è quello che noi faremo per specializzare il template che abbiamo costruito in precedenza in modo da mostrare anche eventuali informazioni relative alla gestione dell’offset. Nella definizione del template derivato è sufficiente definire soltanto le parti modificate o aggiunte.


Nella griglia della card aggiungiamo l’area ofs nella quale andiamo a mostrare le informazioni relative. Con l’aggiunta di questaa nuova area è necessario modificare le dimensioni delle righe e la posizione dell’icona, dovendo mantenere invariata la dimensione della card.

  tado_with_offset:
    template: ['tado']
    variables:
      offset_sensor: ""
      temp_sensor: ""
    custom_fields:
      ofs: >
        [[[ 
          if (variables.offset_sensor && variables.temp_sensor) {
            return (states[variables.temp_sensor].state 
            + " | offset: " + states[variables.offset_sensor].state)
          }
        ]]]              
    styles:
      icon: [ margin-bottom: 10px ]
      grid:
        - grid-template-areas: '"n" "device_info" "target" "temp" "ofs" "i"'
        - grid-template-rows: 20px 10px 20px 50px 10px 50px    
      custom_fields:       
        ofs: [padding-bottom: 5px, font-family: sans-serif, font-size: 12px, fill: white]   

Il valore della temperatura misurata dal sensore esterno viene passato alla card mediante apposita variabile temp_sensor, mentre il valore corrente di offset viene passato mediante la variabile offset_sensor. Tutto il resto rimane come ereditato dal template padre.

Istanziamo la card

Non ci resta che utilizzare i nostri template per istanziare le card e posizionarle a piacimento nella dashboard, ad esempio in una pila orizzontale:

- type: horizontal-stack
  cards:
  # esempio senza offset
  - type: 'custom:button-card'
    entity: climate.cucinotto
    name: Cucinotto
    template: tado
    variables:
      device_name: "va0123456789"
      window_sensor: "binary_sensor.door_window_sensor_abc123456789"  
  # esempio con offset
  - type: 'custom:button-card'
    entity: climate.cameretta
    name: Cameretta
    template: tado_with_offset
    variables:
      device_name: "va1236549870"
      window_sensor: "binary_sensor.door_window_sensor_def123456789"
      offset_sensor: "sensor.cameretta_offset_celsius"
      temp_sensor: "sensor.temperature_158d000224cd0d"     

Il risultato è molto gradevole, ricco di informazioni ma al contempo leggero alla vista e di semplice lettura. Il grande valore aggiunto di questo approccio è quello di riuscire a conentrare in un’unica card tante informazioni utili alla gestione intelligente del riscaldamento, cosa non possibile con le card native o custom disponibili.


Enjoy!