Hoe ik mijn Nefit Easy thermostaat bedien met Home Assistant

Door balk op dinsdag 25 september 2018 20:29 - Reacties (3)
Categorie: -, Views: 6.882

Zie deze post.

Ik wilde onze Nefit Easy kunnen besturen met Home Assistant. Maar ik kon geen component vinden die dat mogelijk kon maken. (er is er intussen wel een).
Globale stappen
1) Ik heb de python Nefit Easy HTTP Server draaien. Die opent een http server die je kan aanspreken met
2) Node Red om elke zoveel seconden te informeren wat de status is van de Nefit Easy thermostaat: ingestelde temperatuur, operatie modus en nog wat zaken. De Node Red flow stopt dat in een MQTT topic dat door HASS wordt uitgelezen
3) Door middel van de Hass Climate MQTT HVAC component heb ik een thermostaat in HASS die de status uitleest van bovenstaande MQTT topics
4) Ik heb een AppDaemon script dat reageert op een MQTT topic om de temperatuur in te stellen. Dit script roept 3 endpoints van de Nefit Easy HTTP server aan om de temperatuur in te stellen. Vervolgens pollt Node Red weer de HTTP server en wordt de nieuwe ingestelde temperatuur weer aan HASS doorgegeven.
Nefit Easy Server
Dit pakket praat met de Nefit cloud, waarmee ook de app op mijn telefoon en natuurlijk de thermostaat zelf communiceert.
Node Red flow:
Vanuit Node Red wordt elke zoveel seconden de status opgevraagd aan het pyhton script hierboven. De resultaten worden in enkele MQTT telegrammen gestopt voor verdere verwerking.

code:
1
[{"id":"41151e3f.3383e","type":"tab","label":"Nefit"},{"id":"c03c0e01.75cb3","type":"http request","z":"41151e3f.3383e","name":"","method":"GET","ret":"txt","url":"http://192.168.0.9:8124/bridge/ecus/rrc/uiStatus","tls":"","x":538.5,"y":146,"wires":[["cdb1fbde.fd35d8","5d90adf3.160804","28ec33bd.11d44c","feecaa72.399178","d6366363.501d","ae42c889.77b0c8","15cc82c4.7d3c8d"]]},{"id":"85019b50.4bca78","type":"inject","z":"41151e3f.3383e","name":"","topic":"","payload":"","payloadType":"date","repeat":"10","crontab":"","once":false,"onceDelay":"","x":310,"y":140,"wires":[["c03c0e01.75cb3"]]},{"id":"cdb1fbde.fd35d8","type":"function","z":"41151e3f.3383e","name":"BAI","func":"var data = JSON.parse(msg.payload);\nmsg.payload = data.value.BAI;\n\n//if (msg === \"No\" or msg === \"CH\" or msg === \"HW\") {\n    return msg;\n//}","outputs":1,"noerr":0,"x":742.5,"y":146,"wires":[["b8c9c4e2.56d2b8","db7e1433.7a2d98"]]},{"id":"b8c9c4e2.56d2b8","type":"debug","z":"41151e3f.3383e","name":"","active":false,"console":"false","complete":"false","x":1099.8332824707031,"y":116.00000667572021,"wires":[]},{"id":"15cc82c4.7d3c8d","type":"debug","z":"41151e3f.3383e","name":"","active":false,"console":"false","complete":"false","x":871.5,"y":107,"wires":[]},{"id":"5d90adf3.160804","type":"json","z":"41151e3f.3383e","name":"","x":712.5,"y":108,"wires":[["15cc82c4.7d3c8d"]]},{"id":"28ec33bd.11d44c","type":"function","z":"41151e3f.3383e","name":"IHT (measured Temp)","func":"var data = JSON.parse(msg.payload);\nmsg.payload = data.value.IHT;\n\nreturn msg;","outputs":1,"noerr":0,"x":792.5,"y":376,"wires":[["e912d7a.8d6a428"]]},{"id":"ebe95643.b53498","type":"mqtt out","z":"41151e3f.3383e","name":"","topic":"woonkamer/woonkamer/thermostaat/gemetentemperatuur","qos":"","retain":"true","broker":"716d90a0.a6a12","x":1300,"y":380,"wires":[]},{"id":"dd1ecb0a.adafd8","type":"mqtt out","z":"41151e3f.3383e","name":"","topic":"woonkamer/woonkamer/thermostaat/boilerstatus","qos":"","retain":"true","broker":"716d90a0.a6a12","x":1113.5,"y":154,"wires":[]},{"id":"bbb2fc01.b582d","type":"http request","z":"41151e3f.3383e","name":"","method":"GET","ret":"txt","url":"http://192.168.0.9:8124/bridge/heatingCircuits/hc1/actualSupplyTemperature","tls":"","x":538,"y":771.6666259765625,"wires":[["a17b3b28.bec538","811e023b.a9537"]]},{"id":"95de828.7c3578","type":"inject","z":"41151e3f.3383e","name":"","topic":"","payload":"","payloadType":"date","repeat":"60","crontab":"","once":false,"onceDelay":"","x":329.6666603088379,"y":770.3333015441895,"wires":[["bbb2fc01.b582d"]]},{"id":"a17b3b28.bec538","type":"function","z":"41151e3f.3383e","name":"supplytemp","func":"var data = JSON.parse(msg.payload);\nmsg.payload = data.value;\n\nreturn msg;","outputs":1,"noerr":0,"x":762,"y":771.6666259765625,"wires":[["47cbd323.b8a28c","bb7239da.99e638"]]},{"id":"47cbd323.b8a28c","type":"debug","z":"41151e3f.3383e","name":"","active":false,"console":"false","complete":"false","x":791,"y":871.6666259765625,"wires":[]},{"id":"811e023b.a9537","type":"debug","z":"41151e3f.3383e","name":"","active":false,"console":"false","complete":"false","x":910,"y":640,"wires":[]},{"id":"2320da2a.17c066","type":"json","z":"41151e3f.3383e","name":"","x":712,"y":733.6666259765625,"wires":[[]]},{"id":"85524341.08a56","type":"mqtt out","z":"41151e3f.3383e","name":"","topic":"woonkamer/woonkamer/thermostaat/supplytemp","qos":"","retain":"true","broker":"716d90a0.a6a12","x":1350,"y":780,"wires":[]},{"id":"feecaa72.399178","type":"function","z":"41151e3f.3383e","name":"TSP (Temp Setpoint)","func":"var data = JSON.parse(msg.payload);\nmsg.payload = data.value.TSP;\n\nreturn msg;","outputs":1,"noerr":0,"x":623.75,"y":471.25,"wires":[["d0484361.073b2","480a92c5.6b298c"]]},{"id":"d27d50d0.31f34","type":"mqtt out","z":"41151e3f.3383e","name":"","topic":"woonkamer/woonkamer/thermostaat/tempsetpoint","qos":"","retain":"true","broker":"716d90a0.a6a12","x":1170,"y":460,"wires":[]},{"id":"d6366363.501d","type":"function","z":"41151e3f.3383e","name":"CPM (Clock Program)","func":"var data = JSON.parse(msg.payload);\nmsg.payload = data.value.CPM;\n\nreturn msg;","outputs":1,"noerr":0,"x":624.4443969726562,"y":555.5555419921875,"wires":[["f87a3188.ba761"]]},{"id":"358c21bc.ffdfbe","type":"mqtt out","z":"41151e3f.3383e","name":"","topic":"woonkamer/woonkamer/thermostaat/clockprogram","qos":"","retain":"true","broker":"716d90a0.a6a12","x":1190,"y":560,"wires":[]},{"id":"5d6fee11.49984","type":"http request","z":"41151e3f.3383e","name":"","method":"GET","ret":"txt","url":"http://192.168.0.9:8124/api/program","tls":"","x":406.6666564941406,"y":1009.9999389648438,"wires":[["c70461a6.4a46a","3b923a34.936356"]]},{"id":"4bb1ea37.1d2924","type":"inject","z":"41151e3f.3383e","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":"","x":188.33331680297852,"y":1008.6666145324707,"wires":[["5d6fee11.49984"]]},{"id":"c70461a6.4a46a","type":"function","z":"41151e3f.3383e","name":"Active Program","func":"var data = JSON.parse(msg.payload);\nmsg.payload = data.active;\n\nreturn msg;","outputs":1,"noerr":0,"x":640.6666564941406,"y":1009.9999389648438,"wires":[["a842484d.822288","8cf65bdb.a0a548"]]},{"id":"a842484d.822288","type":"debug","z":"41151e3f.3383e","name":"","active":false,"console":"false","complete":"false","x":659.6666564941406,"y":1109.9999389648438,"wires":[]},{"id":"3b923a34.936356","type":"debug","z":"41151e3f.3383e","name":"","active":false,"console":"false","complete":"false","x":730,"y":920,"wires":[]},{"id":"3a9e078d.00b1c8","type":"json","z":"41151e3f.3383e","name":"","x":580.6666564941406,"y":971.9999389648438,"wires":[[]]},{"id":"8cf65bdb.a0a548","type":"mqtt out","z":"41151e3f.3383e","name":"","topic":"woonkamer/woonkamer/thermostaat/activeprogram","qos":"","retain":"true","broker":"716d90a0.a6a12","x":991.6666564941406,"y":1017.9999389648438,"wires":[]},{"id":"ae42c889.77b0c8","type":"function","z":"41151e3f.3383e","name":"BAI","func":"var data = JSON.parse(msg.payload);\nmsg.payload = data.value.BAI;\nif (data.value.BAI === \"No\") {\n    msg.payload = 0;\n} else if (data.value.BAI === \"CH\") {\n    msg.payload = 1;\n} else if (data.value.BAI === \"HW\") {\n    msg.payload = 2;\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":758,"y":257,"wires":[["af3e35bd.9c8f58"]]},{"id":"af3e35bd.9c8f58","type":"mqtt out","z":"41151e3f.3383e","name":"","topic":"woonkamer/woonkamer/thermostaat/boilerstatus_num","qos":"","retain":"true","broker":"716d90a0.a6a12","x":1200,"y":260,"wires":[]},{"id":"d0484361.073b2","type":"debug","z":"41151e3f.3383e","name":"","active":false,"console":"false","complete":"false","x":1115.3332824707031,"y":227.00000667572021,"wires":[]},{"id":"db7e1433.7a2d98","type":"filter","z":"41151e3f.3383e","name":"","property":"payload","propertyType":"msg","asArray":false,"itemProperty":"payload","itemPropertyType":"item","rules":[{"t":"eq","v":"CH","vt":"str","output":1},{"t":"eq","v":"No","vt":"str","output":1},{"t":"eq","v":"HW","vt":"str","output":1}],"checkall":"true","outputs":1,"x":850,"y":200,"wires":[["dd1ecb0a.adafd8"]]},{"id":"abbc08dd.ddc508","type":"filter","z":"41151e3f.3383e","name":"","property":"payload","propertyType":"msg","asArray":false,"itemProperty":"payload","itemPropertyType":"item","rules":[{"t":"eq","v":"CH","vt":"str","output":1},{"t":"eq","v":"No","vt":"str","output":1},{"t":"eq","v":"HW","vt":"str","output":1}],"checkall":"true","outputs":1,"x":880,"y":260,"wires":[[]]},{"id":"e912d7a.8d6a428","type":"filter","z":"41151e3f.3383e","name":"","property":"payload","propertyType":"msg","asArray":false,"itemProperty":"payload","itemPropertyType":"item","rules":[{"t":"nnull","output":1}],"checkall":"true","outputs":1,"x":980,"y":380,"wires":[["ebe95643.b53498"]]},{"id":"480a92c5.6b298c","type":"filter","z":"41151e3f.3383e","name":"","property":"payload","propertyType":"msg","asArray":false,"itemProperty":"payload","itemPropertyType":"item","rules":[{"t":"nnull","output":1}],"checkall":"true","outputs":1,"x":840,"y":480,"wires":[["d27d50d0.31f34"]]},{"id":"f87a3188.ba761","type":"filter","z":"41151e3f.3383e","name":"","property":"payload","propertyType":"msg","asArray":false,"itemProperty":"payload","itemPropertyType":"item","rules":[{"t":"nnull","output":1}],"checkall":"true","outputs":1,"x":860,"y":560,"wires":[["358c21bc.ffdfbe"]]},{"id":"bb7239da.99e638","type":"filter","z":"41151e3f.3383e","name":"","property":"payload","propertyType":"msg","asArray":false,"itemProperty":"payload","itemPropertyType":"item","rules":[{"t":"nnull","output":1}],"checkall":"true","outputs":1,"x":1000,"y":780,"wires":[["85524341.08a56"]]},{"id":"716d90a0.a6a12","type":"mqtt-broker","z":"","name":"","broker":"192.168.0.9","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]

Home Assistant code:
In Home Assistant heb ik een MQTT Climate Component geactiveerd. Die ontvangt de MQTT topics die Node Red produceert, en verstuurt ook het nieuwe set point voor de thermostaat.
https://tweakers.net/ext/f/u8TmJ16qvOf1eGjX1RAF4rhT/full.png

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
climate:
- platform: mqtt
  name: Woonkamer
  current_temperature_topic: "woonkamer/woonkamer/thermostaat/gemetentemperatuur"
  temperature_command_topic: "woonkamer/woonkamer/thermostaat/temperature/set"
  temperature_state_topic: "woonkamer/woonkamer/thermostaat/tempsetpoint2"
  mode_state_topic: "woonkamer/woonkamer/thermostaat/boilerstatus"
  modes:
    - "No"
    - "HW"
    - "CH"
  min_temp: 10
  max_temp: 25
  intial: 15
  retain: True


Ik heb ook een paar sensoren aangemaakt om met de volgende stap te praten.

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
sensors:
- platform: mqtt
  name: 'wk_boilerstatus'
  state_topic: "woonkamer/woonkamer/thermostaat/boilerstatus"
- platform: mqtt
  name: 'wk_thermostaat_pv'
  unit_of_measurement: '°C'
  state_topic: "woonkamer/woonkamer/thermostaat/gemetentemperatuur"
- platform: mqtt
  name: 'wk_thermostaat_hass_sp'
  unit_of_measurement: '°C'
  state_topic: "woonkamer/woonkamer/thermostaat/temperature/set"
- platform: mqtt
  name: 'thermostaat_activeprogram'
  state_topic: "woonkamer/woonkamer/thermostaat/activeprogram"
- platform: mqtt
  name: 'thermostaat_tempsetpoint_raw'
  state_topic: "woonkamer/woonkamer/thermostaat/tempsetpoint"

Appdaemon script 1
De Nefit Easy Server spuugt soms een 'undefined' uit en die moet ik er uit filteren. Dat gebeurt met dit AppDaemon script:

Python:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import appdaemon.plugins.hass.hassapi as hass

class thermostaat_raw(hass.Hass):
    def initialize(self):

        self.listen_state(self.inputhandler, "sensor.thermostaat_tempsetpoint_raw")


    def inputhandler(self, entity, attribute, old, new, kwargs):
        reading = self.get_state("sensor.thermostaat_tempsetpoint_raw")
        #self.log(reading)
        if reading != "undefined":
            try:
                temp_sp_thermostaat = float(reading)
                self.log("float: " + reading )
                
                self.call_service("mqtt/publish", topic="woonkamer/woonkamer/thermostaat/tempsetpoint2", payload=temp_sp_thermostaat, retain=True)
                

            except:
                self.log("error in: reading" + reading)

Appdaemon Script 2
Dit script zet een nieuw (in hass ingesteld) set point door naar de thermostaat. Het roept drie endpoints aan, zoals beschreven in dit bericht op github.

Python:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import appdaemon.plugins.hass.hassapi as hass

import requests

class thermostaat_mqtt(hass.Hass):
    def initialize(self):
        self.listen_state(self.program_enable, "input_boolean.nefit_programma",new="on")
        self.listen_state(self.inputhandler, "sensor.wk_thermostaat_hass_sp")


    def inputhandler(self, entity, attribute, old, new, kwargs):
        temp_sp_hass = float(self.get_state("sensor.wk_thermostaat_hass_sp"))

        headers = {'Content-type': 'application/json'}
        myurl = "http://192.168.0.9:8124"
        command1 = "/bridge/heatingCircuits/hc1/temperatureRoomManual"
        command2 = "/bridge/heatingCircuits/hc1/manualTempOverride/status"
        command3 = "/bridge/heatingCircuits/hc1/manualTempOverride/temperature"

        body1 = temp_sp_hass
        body2 = "on"
        body3 = temp_sp_hass
        self.log("new setpoint & command:")
        self.log(body1)
        self.log(body2)


        r1 = requests.post(myurl+command1, verify=False, json={"value": body1}, headers = headers)
        self.log(r1.status_code)
        self.log(r1.text)

        r2 = requests.post(myurl+command2, verify=False, json={"value": body2}, headers = headers)
        self.log(r2.status_code)
        self.log(r2.text)

        r3 = requests.post(myurl+command3, verify=False, json={"value": body3}, headers = headers)
        self.log(r3.status_code)
        self.log(r3.text)

        self.call_service("input_boolean/turn_off", entity_id="input_boolean.nefit_programma")





    def program_enable(self, entity, attribute, old, new, kwargs):

        headers = {'Content-type': 'application/json'}
        myurl = "http://192.168.0.9:8124"
        command2 = "/bridge/heatingCircuits/hc1/manualTempOverride/status"

        body2 = "off"


        r2 = requests.post(myurl+command2, verify=False, json={"value": body2}, headers = headers)
        self.log(r2.status_code)
        self.log(r2.text)