Home Assistant - Recuperare informazioni aggiuntive da Netatmo (termostato)

 


In questa guida cerco di spiegare passo-passo (diciamo per principianti) come recuperare le informazioni aggiuntive fornite dalle API di Netatmo, che l’integrazione ufficiale purtroppo non espone. Le informazioni che interessavano a me (e che vedremo in questo esempio) sono l’orario di fine override (nel caso si imposti una temperatura manualmente) e la schedulazione settimanale. Come sempre cercherò di scrivere un articolo come piace a me: inserirò le informazioni necessarie senza dilungarmi troppo, senza continue ripetizioni (in stile SEO) e andando al punto, ma inserendo le informazioni che è necessario sapere (per capire cosa si sta facendo).

Come prima cosa, naturalmente, dovete avere un’installazione di Home Assistant funzionante e correttamente configurata. Inoltre bisogna chiaramente avere un termostato Netatmo già installato e funzionante con l'app Netatmo.

1. Configurazione tipo (HW/SW)

Nel mio caso mi trovo nella seguente situazione (cioè, se avete la mia stessa configurazione e seguite la guida, vi garantisco che funziona):

  • Raspberry Pi 4 – 8 GB
  • Home Assistant 2022.11.5
    • Supervisor 2022.11.2
    • Operating System 9.3
    • NodeRed 13.5.3

2. Prerequisiti

 Come già detto dovete avere:

  • Installazione di Home Assistant (qualunque tipologia) funzionante
  • Node-Red installato, configurato e funzionante (non obbligatorio, io l'ho usato perché per me è più comodo, voi potete anche convertire la logica usando le automazioni di HA)

3. Configurazione dell'account Netatmo

La prima cosa da fare per poter interrogare le WebAPI di Netatmo è quella di creare le chiavi per poterci interfacciare. Per fare ciò andiamo sul sito Dev di Netatmo (Netatmo Connect | Energy API Documentation) e colleghiamoci inserendo le credenziali create in fase di registrazione sull'App.
Una volta autenticati, è necessario creare la nostra "App", cioè una coppia di chiavi che ci permetteranno di interrogare le API dal nostro sistema. Quindi è sufficiente portarsi con il mouse sul nome dlel'account in alto a destra e cliccare sulla voce "My Apps" nel menu a tendina che compare sotto:

Se non avete ancora creato nessuna "App", vi si aprirà direttamente il form per inserire le informazioni (altrimenti cliccate sul bottone "Create" in alto a destra):

Inseriamo tutti i campi obbligatori e spuntiamo la casella in fondo. Fatto questo si abiliterà il bottone "Salva" in fondo alla pagina. Clicchiamoci sopra

Terminato il salvataggio si aprirà una sezione chiamata "App Technical Parameters", che contiene 2 dati fondamentali: "client ID" e "client secret". Segniamoceli da qualche parte, perché ci serviranno in seguito.


Passiamo al prossimo step: eseguire l'autenticazione.

4. Autenticazione

Per capire come deve essere eseguita l'autenticazione, basta andare sulla pagina dedicata nella documentazione di Netatmo. Clicchiamo quindi sul menu "Documentation" e poi su "Authentication"


In questa pagina possiamo sostanzialmente leggere che Netatmo utilizza OAuth 2 per l'autenticazione, e sono i seguenti:
  • Authorization code: è il metodo standard, si chiama la WebAPI apposita indicando i dati (ID e secret) e un url per la risposta. Una volta convalidati i dati passati verrà invocato l'url di risposta. Noi NON utilizzeremo questo metodo.
  • Client credentials: questo metodo è utilizzabile SOLO per scopi personali, è più "comodo" per i nostri scopi. Si fa una chiamata passando una serie di dati e lui risponde con 2 token (authentication e refresh token). Noi utilizzeremo QUESTO metodo
Leggendo le istruzioni nella pagina viene spiegato che il funzionamento di questi metodi funziona così:
  1. Per prima cosa bisogna chiamare il metodo "password", passandogli tutte le informazioni richieste
  2. Se abbiamo inserito le informazioni corrette, il metodo ritorna 3 parametri: "access_token", "expires_in" e "refresh_token".
    1. access_token è il token da utilizzare per fare le chiamate alle API
    2. expires_in è il tempo (in secondi) di validità di access_token
    3. refresh_token è la chiave da utilizzare per "rinnovare" l'access_token
  3. Quando access_token scade, bisogna chiamare il metodo "refresh_token" per ottenere un nuovo access_token
Ora che abbiamo capito il funzionamento e che abbiamo sotto mano degli esempi di chiamate (nella pagina della documentazione) possiamo passare ad implementare il codice per ottenere e aggiornare questi dati.

5. Ottenere e aggiornare i Token

Per recuperare e aggiornare i token io ho utilizzato NodeRed. Perché? Semplicemente perché per me era il metodo più comodo e immediato per fare i test ed eseguire il debug e manipolare i dati. In questa guida spiego il metodo che ho usato io, se non lo volete utilizzare potete tranquillamente usare le automazioni di HomeAssistant e adattarne la logica.

Come prima cosa dobbiamo crearci 3 "aiutanti" (Helpers) di tipo testuale, in cui andare a mettere i nostri 3 valori (i 2 token e la scadenza). Apriamo quindi l'interfaccia web di HA e andiamo su "Impostazioni"

Selezioniamo "Dispositivi e servizi"

Nel menu superiore clicchiamo su "Aiutanti"


E infine sul bottone in basso a destra "CREA AIUTANTE"



Nella lista che si apre scegliamo "Testo"


A questo punto si apre una finestra dove dobbiamo solo impostare il nome e la lunghezza massima (di default 100) a 255 (giusto per sicurezza), poi cliccare su "CREA".
Io ho creato gli aiutanti con questi 3 nomi: "NetatmoToken", "NetatmoTokenExpiration" e "NetatmoRefreshToken". Voi potete anche usare nomi diversi, ma ricordatevi di modificarli in tutti i punti

Finito di fare ciò, ci troverete le 3 entità appena create con i relativi ID.

Adesso possiamo iniziare a popolare queste nuove entità, scrivendoci dentro i dati che otteniamo da Netatmo. Per fare ciò avviamo NodeRede andando in "Impostazioni"


Poi su "Componenti aggiuntivi"


Identifichiamo e lanciamo "Node-RED"

Quindi apriamo l'interfaccia web di NodeRed

Quando l'interfaccia si è cariata, apriamo il tasto di menu di NodeRed in alto a destra


E poi clicchiamo su "Import"


Nella finestra che sia apre incolliamo il seguente testo:

[{"id":"3843908b20b546be","type":"tab","label":"GetNetatmoToken","disabled":true,"info":"","env":[]},{"id":"028b51485b704c5b","type":"debug","z":"3843908b20b546be","name":"debug 2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1080,"y":100,"wires":[]},{"id":"b671f19e61752b57","type":"bigtimer","z":"3843908b20b546be","outtopic":"","outpayload1":"","outpayload2":"","name":"Big Timer","comment":"","lat":0,"lon":0,"starttime":"0","endtime":"1439","starttime2":0,"endtime2":0,"startoff":"0","endoff":0,"startoff2":0,"endoff2":0,"offs":0,"outtext1":"","outtext2":"","timeout":1440,"sun":true,"mon":true,"tue":true,"wed":true,"thu":true,"fri":true,"sat":true,"jan":true,"feb":true,"mar":true,"apr":true,"may":true,"jun":true,"jul":true,"aug":true,"sep":true,"oct":true,"nov":true,"dec":true,"day1":0,"month1":0,"day2":0,"month2":0,"day3":0,"month3":0,"day4":0,"month4":0,"day5":0,"month5":0,"day6":0,"month6":0,"day7":0,"month7":0,"day8":0,"month8":0,"day9":0,"month9":0,"day10":0,"month10":0,"day11":0,"month11":0,"day12":0,"month12":0,"d1":0,"w1":0,"d2":0,"w2":0,"d3":0,"w3":0,"d4":0,"w4":0,"d5":0,"w5":0,"d6":0,"w6":0,"xday1":0,"xmonth1":0,"xday2":0,"xmonth2":0,"xday3":0,"xmonth3":0,"xday4":0,"xmonth4":0,"xday5":0,"xmonth5":0,"xday6":0,"xmonth6":0,"xday7":0,"xmonth7":0,"xday8":0,"xmonth8":0,"xday9":0,"xmonth9":0,"xday10":0,"xmonth10":0,"xday11":0,"xmonth11":0,"xday12":0,"xmonth12":0,"xd1":0,"xw1":0,"xd2":0,"xw2":0,"xd3":0,"xw3":0,"xd4":0,"xw4":0,"xd5":0,"xw5":0,"xd6":0,"xw6":0,"suspend":false,"random":false,"randon1":false,"randoff1":false,"randon2":false,"randoff2":false,"repeat":true,"atstart":true,"odd":false,"even":false,"x":100,"y":80,"wires":[[],["57f3cdd19472aabd"],[]]},{"id":"38381b2a565db1d7","type":"http request","z":"3843908b20b546be","name":"NetatmoRequestToken","method":"POST","ret":"obj","paytoqs":"ignore","url":"https://api.netatmo.com/oauth2/token","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[{"keyType":"Content-Type","keyValue":"","valueType":"other","valueValue":"application/x-www-form-urlencoded;charset=UTF-8"}],"x":860,"y":220,"wires":[["028b51485b704c5b","94209b6f667e6475","c19dd44366ac1da6","79e3ea5426a00d40"]]},{"id":"f3abdc833f5cdaea","type":"inject","z":"3843908b20b546be","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"grant_type=password&client_id=myclid&client_secret=myclscr&username=pippo%40gmail.com&password=123456&scope=read_station read_thermostat","payloadType":"str","x":70,"y":400,"wires":[["57f3cdd19472aabd"]]},{"id":"94209b6f667e6475","type":"api-call-service","z":"3843908b20b546be","name":"Set Expiration Value","server":"72e5b6ee.b05ef8","version":5,"debugenabled":false,"domain":"input_text","service":"set_value","areaId":[],"deviceId":[],"entityId":["input_text.netatmotokenexpiration"],"data":"{\t    \"value\": $sum([$toMillis($now()), (payload.expires_in*1000)])\t}\t","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1160,"y":240,"wires":[[]]},{"id":"c19dd44366ac1da6","type":"api-call-service","z":"3843908b20b546be","name":"Set Token Value","server":"72e5b6ee.b05ef8","version":5,"debugenabled":false,"domain":"input_text","service":"set_value","areaId":[],"deviceId":[],"entityId":["input_text.netatmotoken"],"data":"{\t    \"value\": payload.access_token\t}\t","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1140,"y":320,"wires":[[]]},{"id":"79e3ea5426a00d40","type":"api-call-service","z":"3843908b20b546be","name":"Set Refresh Token Value","server":"72e5b6ee.b05ef8","version":5,"debugenabled":false,"domain":"input_text","service":"set_value","areaId":[],"deviceId":[],"entityId":["input_text.netatmorefreshtoken"],"data":"{\t    \"value\": payload.refresh_token\t}\t","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1170,"y":400,"wires":[[]]},{"id":"57f3cdd19472aabd","type":"api-current-state","z":"3843908b20b546be","name":"Scadenza Token","server":"72e5b6ee.b05ef8","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"input_text.netatmotokenexpiration","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":200,"y":180,"wires":[["adbafc5de0d009f5"]]},{"id":"adbafc5de0d009f5","type":"switch","z":"3843908b20b546be","name":"E' scaduto?","property":"$number(payload)\t","propertyType":"jsonata","rules":[{"t":"lt","v":"$toMillis($now())","vt":"jsonata"},{"t":"else"}],"checkall":"false","repair":false,"outputs":2,"x":310,"y":280,"wires":[["174f5e8e454f768a"],["62a76bf1ee1cf107"]]},{"id":"d642fbb091c24342","type":"debug","z":"3843908b20b546be","name":"debug 4","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":760,"y":480,"wires":[]},{"id":"fff28ce24e7eadd6","type":"function","z":"3843908b20b546be","name":"PayloadNewToken","func":"var msg = {\n    payload: \"grant_type=password&client_id=myclid&client_secret=myclscr&username=pippo%40gmail.com&password=123456&scope=read_station read_thermostat\"\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":530,"y":160,"wires":[["38381b2a565db1d7"]]},{"id":"62a76bf1ee1cf107","type":"debug","z":"3843908b20b546be","name":"debug 5","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":380,"y":440,"wires":[]},{"id":"174f5e8e454f768a","type":"api-current-state","z":"3843908b20b546be","name":"Refresh Token","server":"72e5b6ee.b05ef8","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"input_text.netatmorefreshtoken","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":520,"y":240,"wires":[["f49a7e9979fdc551"]]},{"id":"79b4c45909fdf9a5","type":"inject","z":"3843908b20b546be","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"grant_type=password&client_id=myclid&client_secret=myclscr&username=pippo%40gmail.com&password=123456&scope=read_station read_thermostat","payloadType":"str","x":370,"y":60,"wires":[["fff28ce24e7eadd6"]]},{"id":"f49a7e9979fdc551","type":"function","z":"3843908b20b546be","name":"PayloadRefreshToken","func":"var pl = \"grant_type=refresh_token&refresh_token=\";\npl = pl + msg.payload.replace('|', '%7C');\npl = pl + \"&client_id=myclid&client_secret=myclscr\";\nvar msg = {\n    payload: pl\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":680,"y":340,"wires":[["d642fbb091c24342","38381b2a565db1d7"]]},{"id":"72e5b6ee.b05ef8","type":"server","name":"Home Assistant","addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"","connectionDelay":false,"cacheJson":false,"heartbeat":false,"heartbeatInterval":"","statusSeparator":"","enableGlobalContextStore":false}]


Poi selezioniamo "New flow" e clicchiamo su "Import"


Se non viene selezionato di default, apriamo il nuovo Flow appena importato, che si presenterà così (è tratteggiato perché è disabilitato):


Arrivati a questo punto, dobbiamo andare ad inserire i dati del nostro account nelle chiamate che vengono fatte.
Facciamo doppio click sul nodo chiamato "PayloadNewToken"

Nella sezione che si aprirà sulla destra, dobbiamo andare a sostituire alcuni parti del contenuto di "payload". In particolare il contenuto è il seguente:
"grant_type=password&client_id=myclid&client_secret=myclscr&username=pippo%40gmail.com&password=123456&scope=read_station read_thermostat"

Dovete andare a sostituire:
  • myclid con il vostro "client ID" ottenuto al punto 3.
  • myclscr con il vostro "client secret" ottenuto sempre al punto 3
  • pippo%40gmail.com con la mail che avete utilizzato per la registrazione a Netatmo.
    ATTENZIONE: il valore deve essere URLENCODED (codificato per url). Potete usare un tool online (come ad esempio questo URL Encode and Decode - Online (urlencoder.org)) per ottenere il valore codificato
  • 123456 con la vostra password dell'account Netatmo.
    ATTENZIONE: il valore deve essere URLENCODED (codificato per url). Potete usare un tool online (come ad esempio questo URL Encode and Decode - Online (urlencoder.org)) per ottenere il valore codificato
Fatto ciò clicchiamo sul bottone "Done"



Facciamo ora doppio click sul nodo "PayloadRefreshToken"

Nella sezione che si aprirà sulla destra, dobbiamo andare a sostituire alcuni parti del contenuto della terza riga. In particolare il contenuto è il seguente:
&client_id=myclid&client_secret=myclscr

Dobbiamo andare a sostituire:
  • myclid con il vostro "client ID" ottenuto al punto 3.
  • myclscr con il vostro "client secret" ottenuto sempre al punto 3
Fatto questo clicchiamo sul bottone "Done"


Abbiamo finito la configurazione, ora andiamo ad abilitare il Flow cliccando sul bottone che compare di fianco al nome del Flow, se ci passiamo su con il mouse (nel riquadro in alto a destra).

Vi accorgerete che il Flow diventa attivo perché non sarà più in grigino tratteggiato, ma diventerà di colore pieno.
Clicchiamo sul bottone "Deploy" in alto a destra per "committare" le nostre modifiche su NodeRed.


Ora verifichiamo di aver fatto tutto giusto. Visualizziamo la finestra di "debug" cliccando sull'apposito bottone in alto a destra.


In questo momento potrebbero esserci degli errori. Per il momento ignorateli.
Clicchiamo la "linguetta" del nodo "Inject" collegato al nodo "PayloadNewToken"


Ora, nel giro di alcuni secondi, dovremmo vedere comparire dei messaggi relativi alla risposta del Webservice di autenticazione di Netatmo. In caso positivo dovrebbe esserci scritto (tra le altre cose) qualcosa del tipo:
{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "expires_in":10800,
  "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
}



In caso negativo ci sarà indicato un errore. Questo significa che avete sbagliato qualcosa nella configurazione del payload, ricontrollate i dati che avete messo e riprovate.

Se è andato tutto a buon fine, possiamo tornare nella pagina delle entità di HA a verificare che ci sia scritto dentro qualcosa.


Bene, ora abbiamo il token che ci serve per poter chiedere i dati.

Da qui in poi è una spiegazione di cosa fa il Flow di NodeRed, se non vi interessa potete passare direttamente al prossimo punto.

Spiegazione del Flow

Se vi interessa capire quale logica usa il Flow di NodeRed che vi ho fatto aggiungere, questa è la spiegazione sommaria.
Iniziamo con il dire che ci sono 2 tipi di nodi che non sono funzionali al flusso di per se, ma sono più che altro per la manutenzione e il debug, sono appunto i nodi "Inject" e "Debug".
Inject è un nodo che permette di mandare manualmente un input al nodo a cui è collegato. Nel Flow in oggetto ce ne sono 2:
  • il primo è collegato a "PayloadNewToken" e serve la prima volta per "lanciare il comando" di ottenimento token.
    Dopo la prima volta non dovrebbe più servire, ma potrebbe anche tornare utile nel caso di problemi ai server di Netatmo, oppure se lasciassimo spento HA per molto tempo (non è chiaro se questo token venga cancellato dopo un certo periodo di tempo, la documentazione non lo specifica)
  • il secondo è collegato a "Scadenza Token" e serve per "forzare" manualmente il controllo sulla scadenza
Debug, invece, come dice il nome stesso è una tipologia di nodo che permette il debug. Serve per stampare il payload che gli arriva in ingresso nella finestra "debug", permettendo così di controllare se i dati passati sono quelli attesi.

Fatta questa premessa, passiamo alla logica effettiva. Tutto comincia con il nodo "Big Timer". Questo nodo è un timer altamente configurabile, che nel nostro caso è impostato semplicemente per funzionare tutti i giorni, "accendendosi" alle 00:00 e "spegnendosi" alle 23:59.
Il nodo in questione ha 3 possibili output (uscite):
  • Il primo manda un segnale ogni volta che il timer si accende o si spegne
  • Il secondo manda un segnale ogni minuto in cui il timer rimane acceso
  • Il terzo serve per messaggi TTS o per debug
Nel nostro caso utilizziamo il secondo output (un segnale ogni minuto), in modo da controllare se il token è scaduto e ha bisogno di essere aggiornato. Sicuramente qualcuno di voi potrebbe obiettare che il controllo "in polling" (cioè continuare a controllare se il token è scaduto, invece di impostare un timer che scateni un evento al momento della scadenza effettiva) sia meno efficiente e meno "bello". E' vero, il polling è meno bello e meno ottimizzato, ma è più "robusto". Con il polling non avrete problemi in caso di riavvio di HA o di NodeRed a cavallo della scadenza, non avrete problemi in caso di irraggiungibilità temporanea dei WS di Netatmo o di problemi sulla vostra connessione, in caso vogliate modificare manualmente i valori, ecc.
Insomma, è meno bello, meno efficiente, ma garantisce maggiore tranquillità e minore necessità di "tenere monitorato" il flusso stesso.
Come dicevo, il Big Timer ogni minuto manda un segnale al nodo "Scadenza Token". Quest'ultimo si occupa di recuperare lo stato dell'entità "input_text.netatmotokenexpiration" (cioè recupera la data/ora di scadenza del token) e inviarlo al nodo successivo per il controllo.
A questo punto il nodo "E' scaduto?" riceve in ingresso dal nodo "Scadenza Token" la data/ora di scadenza (in formato epoch in millisecondi) e la confronta con la data/ora attuale. Questo nodo ha 2 output: se il token è scaduto (cioè se la data/ora ricevuta è < della data/ora attuale) manda un messaggio sul primo output, altrimenti lo manda sul secondo output.
Quindi, finché il token non scade, il nodo "E' Scaduto?" continuerà a mandare messaggi sul secondo output (non collegato a nessun nodo funzionale). Appena rileva che il Token è scaduto, invece, viene mandato un segnale al nodo "Refresh Token".
"Refresh Token" non fa altro che recuperare lo stato dell'entità "input_text.netatmorefreshtoken" (cioè il Refresh Token effettivo) e passarlo al nodo successivo.
A questo punto il nodo "PayloadRefreshToken" riceve dal nodo "Refresh Token" il Token da usare per fare appunto il "refresh", e compone la richiesta da mandare al WS di Netatmo per ottenere una nuova coppia di Access Token e Refresh Token, oltre ad una nuova data di scadenza.
La richiesta così composta viene mandata dal nodo "PayloadRefreshToken" al nodo "NetatmoRequestToken", che non fa altro che prendere quello che gli viene mandato e inviarlo all'URL impostato (https://api.netatmo.com/oauth2/token) con i vari Header necessari.
Quando "NetatmoRequestToken" riceve la risposta, fa la decodifica del jsonp e passa il contenuto ai 4 nodi a valle: "debug 2", "Set Expiration Value", "Set Token Value" e "Set Refresh Token Value". Lo scopo dei 4 nodi è:
  • "debug 2" scrive il risultato nella console di debug, ci server per controllare se va tutto bene e vedere eventuali errori
  • "Set Expiration Value" scrive in "input_text.netatmotokenexpiration" la data/ora di scadenza appena ricevuta
  • "Set Token Value" scrive in "input_text.netatmotoken" il valore di Access Token appena ricevuto
  • "Set Refresh Token Value" scrive in "input_text.netatmorefreshtoken" il valore di Refresh Token appena ricevuto
In questo modo si conclude il "giro" di aggiornamento del Token, fino al prossimo "segnale" di Big Timer.

6. I WebService "Energia" di Netatmo

Passiamo dunque ad analizzare i WS di Netatmo per capire come prendere le informazioni che ci servono. Torniamo quindi sulla pagina web dedicata: Netatmo Connect | Energy API Documentation e verifichiamo di essere autenticati.

Se non siamo autenticati, basta fare Login con i dati di accesso all'app Netatmo Energia.
Ora spostiamoci in basso fino a trovare la sezione dedicata alla documentazione degli endpoint. In particolare in questo momento ci interessa l'endpoint /homesdata, che ci fornisce informazioni generali sulla configurazione della nostra casa (o delle nostre case, se ne abbiamo impostata più di una), sulla schedulazione del termostato (che vedremo più avanti) e, soprattutto, l'ID della casa che ci server per l'altro endpoint.
Clicchiamo quindi su "/homesdata" per espandere il nodo e visualizzare i dettagli.

Qui possiamo vedere una (molto scarna) sorta di documentazione e di esempio per il metodo in questione. Ma, cosa più importante, POSSIAMO PROVARLO.
Quindi proviamolo subito cliccando sul bottone "Try It Out".

E poi su "Execute /Homesdata".

Sotto si popolerà una sezione con il risultato della chiamata, e le informazioni della/e nostra/e casa/e.
Per il momento ci interessa solo l'ID della casa, segnamocelo (ci serve per le altre chiamate) e torneremo su questo metodo più tardi.

Scendiamo fino all'endpoint "/homestatus" e clicchiamoci sopra per espandere il nodo e vedere le informazioni.

Clicchiamo sul bottone "Try it out"

Inseriamo il campo "home_id" (il valore che abbiamo visto nella chiamata precedente), quindi clicchiamo su "Execute /homestatus"

E vedremo quindi la risposta con i dati relativi al nostro termostato.
Nota: vi consiglio di fare un po' di prove modificando delle impostazioni per vedere tutti i parametri che possono comparire. Ad esempio "therm_setpoint_start_time" e "therm_setpoint_end_time" sono presenti solo se sul termostato viene impostata una temperatura manuale.


Nel mio caso ho una sola stanza configurata per il riscaldamento di casa (ho solo 1 termostato, e nessuna valvola termostatica aggiuntiva), se avete più stanze configurate, vedrete più "rooms" nella risposta.
Sopra questa sezione c'è quello che ci serve per poter fare la chiamata ed ottenere i dati che vediamo (l'esempio usa curl):



Passiamo quindi alla creazione dei sensori REST.

7. I sensori REST

Per creare i sensori dobbiamo aggiungere del codice YAML alla configurazione di HA. Se avete abilitato la cartella "packages" vi consiglio (per avere una maggiore "pulizia" del codice) di creare lì dentro un nuovo file per i sensori REST, altrimenti potete scrivere direttamente dentro al file "configuration.yaml".
Do per scontato che abbiate un editor per fare ciò (Fileditor o Filebrowser) e che sappiate di cosa parlo. Se non lo sapete o non avete ancora l'editor installato, fatevi un giro su Google e vedrete che ci sono tantissime guide che spiegano come farlo.
Andiamo quindi ad incollare il seguente codice YAML nel file:
rest:
####################################################
#                                                  #
#                     NETATMO                      #
#                                                  #
####################################################
  - resource: https://api.netatmo.com/api/homestatus?home_id=home_id
    scan_interval: 600
    timeout: 60
    headers:
      Content-Type: application/json
      Authorization: >
        Bearer {{ states("input_text.netatmotoken") }}
    sensor:
      - name: NetatmoT Next Period
        value_template: "{{ value_json.body.home.rooms[0].therm_setpoint_end_time }}"
      - name: NetatmoT Start Period
        value_template: "{{ value_json.body.home.rooms[0].therm_setpoint_start_time }}"
      - name: Netatmo Sala Raggiungibile
        value_template: "{{ value_json.body.home.rooms[0].reachable }}"
      - name: Netatmo Sala Usa Antic.
        value_template: "{{ value_json.body.home.rooms[0].anticipating }}"
      - name: Netatmo Sala Perc. Apertura Valvole
        value_template: "{{ value_json.body.home.rooms[0].heating_power_request }}"
      - name: Netatmo Sala Finestra Aperta
        value_template: "{{ value_json.body.home.rooms[0].open_window }}"
        
Dove:
  • home_id è il codice alfanumerico che identifica la vostra casa (quello che avete messo prima nella chiamata di esempio sul sito di Netatmo)
  • "scan_interval: 600" indica che andiamo a richiedere i dati ogni 10 minuti (600 secondi)
Nel mio caso, come dicevo, ho una sola stanza (e quindi prendo "rooms[0]"), se avete più stanze e dovete aggiungere sensori da un'altra stanza, potete tranquillamente prendere quella giusta ("rooms[1]", "rooms[2]", ecc.).
Nella sezione "sensor" sono andato ad includere tutti i parametri che sono presenti nella "room" del json di Netatmo, mentre invece ho tralasciato "modules", poiché tutte le informazioni lì presenti sono già esposte dall'integrazione ufficiale.

Andiamo quindi nella pagina "Strumenti per sviluppatori"

Clicchiamo su "VERIFICA CONFIGURAZIONE" per accertarci che ciò che abbiamo aggiunto sia corretto.

Poi, nella sezione sottostante ("Ricarica configurazione YAML") cerchiamo la voce "ENTITA' REST E SERVIZI DI NOTIFICA REST" e clicchiamoci sopra.


Spostiamoci quindi nel tab "STATI"

Inseriamo il filtro del nome entità per visualizzare solo quelle che abbiamo appena creato (ad esempio "sensor.netatmo_sala" nel mio caso) e verifichiamo che tutto funzioni correttamente.

Se però il termostato non è su una temperatura manuale ed andiamo a visualizzare il valore di "sensor.netatmot_next_period" (ad esempio), ci accorgeremo che l'entità non ha nessun valore, poiché tale attributo non viene restituito dal WS di Netatmo.


Per me questa cosa è piuttosto fastidiosa, perché mi interessa poter vedere nella mia interfaccia di HA l'orario in cui la temperatura è stata impostata su quella attualmente visualizzata (manualmente o da schedulazione) e l'orario fino a cui questa temperatura verrà mantenuta.

Per fortuna un modo c'è!
Vediamo come fare.

8. I sensori Command Line

Come dicevo pocanzi, c'è un modo per sapere da che ore e fino a quando resterà impostata la temperatura attuale.
Se infatti andiamo ad analizzare la risposta della chiamata "/homesdata" che abbiamo visto nella sezione precedente, vediamo che, tra i dati forniti, c'è anche la configurazione della pianificazione settimanale.

Questa "timetable" è composta da tanti blocchi, ognuno dei quali è formato da:
  • "zone_id" che indica la temperatura da tenere nella fascia oraria (a me non interessava andarla a recuperare, ma se a voi serve, più in basso sono definite tutte le "zones")
  • "m_offset" che indica l'orario (in minuti dalle 00:00 del lunedì) di inizio della fascia oraria
Qui abbiamo un primo problema: non vogliamo dover aspettare l'aggiornamento del sensore REST per aggiornare gli orari, e allo stesso modo è inutile continuare a chiedere gli orari aggiornati, quando la schedulazione rimane principalmente statica (normalmente non la cambiamo in continuazione, soprattutto per il giorno stesso).
Questo primo problema si può risolvere creando un sensore che ha come valore tutto il contenuto di "timetable".
Sorge però un secondo problema: i sensori REST hanno il limite di 255 caratteri massimi, e con la nostra timetable sforiamo DI SICURO questo limite.

Ecco che quindi siamo costretti ad utilizzare un workaround per aggirare il problema (e per fortuna che si può!): utilizziamo un command line sensor che invia una chiamata "curl" e salva la risposta in un sensore!

Come prima, andiamo ad aggiungere questo codice YAML al file configuration.yaml (o mettiamolo in un file YAML nella cartella "packages"):

sensor:
    - platform: command_line
      name: netatmo_schedules
      scan_interval: 3600
      command: >
         echo "{\"events\":" $(
         curl 
         -s 
         -H 'Authorization: Bearer {{ states('input_text.netatmotoken') }}'
         'https://api.netatmo.com/api/homesdata'
         ) "}" 
      value_template: > 
         {{ value_json.events | length }}
      json_attributes:
         - events
In questo modo, ogni ora (3600 secondi) viene inserito come valore del sensore "netatmo_schedules" il contenuto completo della chiamata "/homesdata".

Torniamo quindi su "Strumenti per sviluppatori"

Facciamo di nuovo la "VERIFICA CONFIGURAZIONE"

Nella sezione "Ricarica configurazione YAML" cerchiamo e clicchiamo "ENTITA' DI RIGA DI COMANDO"


Di nuovo andiamo sul tab "STATI"

Filtriamo per "netatmo_schedules" e dovremmo vedere gli attributi completi del nostro sensore


Ci siamo quasi, ora non rimane che creare un sensore template che metta insieme queste informazioni!

9. I sensori Template

Arrivati fin qui, non ci resta che creare i nostri 2 sensori finali, uno con l'ora di inizio e l'altro con l'ora di fine.

Aggiungiamo questo codice YAML alla nostra configurazione (di nuovo, al file configuration.yaml o in un file della cartella "packages"):

sensor:
  - platform: template
    sensors:
    #Netatmo (termostato) - Mantiene temperatura fino a
      netatmot_held_until:
        friendly_name: "Temp. Fino alle"
        value_template: >
            {% set manual = states('sensor.netatmot_next_period') %} 
            {% set epochnow = as_timestamp(now()) %}
            {% set weekminutenow = now().weekday() * 1440 + now().hour*60 + now().minute %} 
            {% set monday = now().timestamp() - now().weekday() *86400 - now().hour * 3600 - now().minute * 60 %}
            {% set found = namespace(h_bool=False) %} 
            {% set minutesfound = namespace(found=0) %} 
            {% set getschedule = false %}
            
            {% if manual | string == '' %}
              {% set getschedule = true %}
            {% elif manual is undefined %}
              {% set getschedule = true %}
            {% elif manual | string == 'unknown' %}
              {% set getschedule = true %}
            {% elif manual | float < epochnow %}
              {% set getschedule = true %}
            {% endif %}
            {% if getschedule == true %}
              {%  for i in states.sensor.netatmo_schedules.attributes.events.body.homes[0].schedules[0].timetable if found.h_bool == false %}
                {% if weekminutenow < i.m_offset %}
                  {% set found.h_bool = true %}
                  {% set minutesfound.found = i.m_offset %}
                {% endif %}
              {% endfor %}
            {% else %}
              {{ manual | int | timestamp_custom('%H:%M') }}
            {% endif %}
            
            {% if found.h_bool %}
              {{ (monday | int + minutesfound.found*60) | int | timestamp_custom('%H:%M') }}
            {% endif %}
        icon_template: "mdi:clock"
    #Netatmo (termostato) - Inizio schedulazione
      netatmot_start_sched:
        friendly_name: "Schedulazione iniz. alle"
        value_template: >
            {% set manual = states('sensor.netatmot_start_period') %} 
            {% set epochnow = as_timestamp(now()) %}
            {% set weekminutenow = now().weekday() * 1440 + now().hour*60 + now().minute %} 
            {% set monday = now().timestamp() - now().weekday() *86400 - now().hour * 3600 - now().minute * 60 %}
            {% set found = namespace(h_bool=False) %} 
            {% set minutesfound = namespace(found=0) %} 
            {% set getschedule = false %}
            
            {% if manual | string == '' %}
              {% set getschedule = true %}
            {% elif manual is undefined %}
              {% set getschedule = true %}
            {% elif manual | string == 'unknown' %}
              {% set getschedule = true %}
            {% endif %}
            {% if getschedule == true %}
              {%  for i in states.sensor.netatmo_schedules.attributes.events.body.homes[0].schedules[0].timetable if found.h_bool == false %}
                {% if weekminutenow > i.m_offset %}
                  {% set minutesfound.found = i.m_offset %}
                {% else %}
                  {% set found.h_bool = true %}
                {% endif %}
              {% endfor %}
            {% else %}
              {{ manual | int | timestamp_custom('%H:%M') }}
            {% endif %}
            
            {% if found.h_bool %}
              {{ (monday | int + minutesfound.found*60) | int | timestamp_custom('%H:%M') }}
            {% endif %}
        icon_template: "mdi:clock"


Ora andiamo di nuovo in "Strumenti per sviluppatori"

Verifichiamo la configurazione


Nella sezione "Ricarica configurazione YAML" troviamo e clicchiamo "ENTITA' MODELLO"

Poi andiamo sul tab "STATI"


E inseriamo nel filtro "netatmot_next_period". Dovremmo vedere una cosa del genere:


FINE!
Ci siamo riusciti, ora possiamo usare le nostre entità comodamente dalla plancia:
















Commenti

Post popolari in questo blog

Creare un sensore weather (previsioni del tempo) da Meteo & Radar in Home Assistant

Installare Frigate su Raspberry Pi con HAssOS

Creare un sensore del prezzo del gas (PSBIL_BUY) in Home Assistant

Ricevere la posizione GPS quando si parcheggia l'automobile (con Home Assistant)

Creare MQTT Server (broker) in Home Assistant (HASSIO)

Esporre UPS da NAS Synology verso Home Assistant (NUT)

Installare Portainer su NAS Synology

Tasmota (via OTA) su BlitzWolf BW-SHP10 (e su Tuya ESP in generale)

JC vs Khaby Lame