diff --git a/configs/pepe/home-assistant/heater-control.nix b/configs/pepe/home-assistant/heater-control.nix index 56afb3b..99dd39d 100644 --- a/configs/pepe/home-assistant/heater-control.nix +++ b/configs/pepe/home-assistant/heater-control.nix @@ -10,7 +10,8 @@ script = let myPython = pkgs.python3.withPackages (ps: with ps; [ paho-mqtt ]); in '' - ${myPython}/bin/python ${} + cd ${} + ${myPython}/bin/python heater.py ''; }; } diff --git a/mqtt/.gitignore b/mqtt/.gitignore index f5e96db..f425bf4 100644 --- a/mqtt/.gitignore +++ b/mqtt/.gitignore @@ -1 +1,2 @@ -venv \ No newline at end of file +venv +**/__pycache__ \ No newline at end of file diff --git a/mqtt/heater.py b/mqtt/heater.py index 65541ab..afcacfe 100644 --- a/mqtt/heater.py +++ b/mqtt/heater.py @@ -1,11 +1,21 @@ import time + import paho.mqtt.client as mqtt import json import threading # initial scene +from heater.modules.heater import Heater +from heater.modules.watcher import Watcher + scene = "default" +watcher = Watcher({ + "office1": Heater(topic="zigbee2mqtt/heater1", set_topic="zigbee2mqtt/heater1/set"), + "office2": Heater(topic="zigbee2mqtt/heater2", set_topic="zigbee2mqtt/heater2/set"), + "bedroom": Heater(topic="zigbee2mqtt/heater3", set_topic="zigbee2mqtt/heater3/set"), +}) + # The callback for when the client receives a CONNACK response from the server. def on_connect(client, _userdata, _flags, rc): @@ -16,16 +26,23 @@ def on_connect(client, _userdata, _flags, rc): # Subscribing in on_connect() means that if we lose the connection and # reconnect then subscriptions will be renewed. client.subscribe("control/lights/set") + for topic in watcher.get_topics(): + client.subscribe(topic) + watcher.pull_values(client) # The callback for when a PUBLISH message is received from the server. def on_message(client, _userdata, msg): global scene (topic, payload) = parse_message(msg) - print("set scene %s -> %s" % (scene, payload['scene'])) - scene = payload['scene'] - publish_heater_depending_on_scene(client) - # print(topic + " " + payload['scene']) + if topic == "control/lights/set": + print("set scene %s -> %s" % (scene, payload['scene'])) + scene = payload['scene'] + update_scene(client) + else: + print("got %s" % topic) + watcher.update_actual_heating_point_for_topic(topic, payload) + #watcher.publish(client) def parse_message(msg): @@ -34,43 +51,39 @@ def parse_message(msg): return msg.topic, payload -mqttClient = mqtt.Client() -mqttClient.on_connect = on_connect -mqttClient.on_message = on_message -mqttClient.username_pw_set("homeassistant", password="password") - -mqttClient.connect("pepe.private", 1883, 60) - - -def publish_heater(client, temperature=10): - payload = { - "system_mode": "auto", - "current_heating_setpoint": str(temperature), - "eurotronic_host_flags": {"window_open": True} - } - client.publish("zigbee2mqtt/heater1/set", json.dumps(payload)) - client.publish("zigbee2mqtt/heater2/set", json.dumps(payload)) - client.publish("zigbee2mqtt/heater3/set", json.dumps(payload)) - - -def publish_heater_depending_on_scene(client): +def update_scene(client): if scene == "night": - publish_heater(client, 12) + watcher.update("office1", 10) + watcher.update("office2", 10) + watcher.update("bedroom", 13) elif scene == "default": - publish_heater(client, 26) + watcher.update("office1", 23) + watcher.update("office2", 23) + watcher.update("bedroom", 18) elif scene == "outside": - publish_heater(client, 10) + watcher.update("office1", 10) + watcher.update("office2", 10) + watcher.update("bedroom", 13) else: - publish_heater(client, 10) + watcher.update("office1", 10) + watcher.update("office2", 10) + watcher.update("bedroom", 13) + + watcher.publish(client) def loop_thread(client): while True: - publish_heater_depending_on_scene(client) - time.sleep(300) + watcher.publish(client) + time.sleep(120) if __name__ == "__main__": + mqttClient = mqtt.Client() + mqttClient.on_connect = on_connect + mqttClient.on_message = on_message + mqttClient.username_pw_set("homeassistant", password="password") + mqttClient.connect("pepe.private", 1883, 60) # Blocking call that processes network traffic, dispatches callbacks and # handles reconnecting. # Other loop*() functions are available that give a threaded interface and a diff --git a/mqtt/heater/__init__.py b/mqtt/heater/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mqtt/heater/modules/__init__.py b/mqtt/heater/modules/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mqtt/heater/modules/heater.py b/mqtt/heater/modules/heater.py new file mode 100644 index 0000000..f020354 --- /dev/null +++ b/mqtt/heater/modules/heater.py @@ -0,0 +1,39 @@ +import json + + +class Heater: + def __init__(self, topic, set_topic): + self.not_initialized_yet = True + self.wanted_temperature = 15 + self.actual_temperature = 15 + self.set_topic = set_topic + self.topic = topic + + def payload(self): + payload = { + "system_mode": "auto", + "current_heating_setpoint": str(self.wanted_temperature), + "eurotronic_host_flags": {"window_open": True} + } + return json.dumps(payload) + + def needs_publish(self): + if self.not_initialized_yet: + return True + else: + return self.wanted_temperature != self.actual_temperature + + def update_actual_heating_point(self, payload): + heating_setpoint = int(payload["current_heating_setpoint"]) + if self.not_initialized_yet: + self.not_initialized_yet = False + self.wanted_temperature = heating_setpoint + print("%s: update wanted temperature %d" % (self.topic, self.actual_temperature)) + self.actual_temperature = heating_setpoint + print("%s: update actual temperature %d" % (self.topic, self.actual_temperature)) + + def topic_and_payload_for_query(self): + payload = { + "current_heating_setpoint": "" + } + return ("%s/get" % self.topic), json.dumps(payload) diff --git a/mqtt/heater/modules/watcher.py b/mqtt/heater/modules/watcher.py new file mode 100644 index 0000000..4892207 --- /dev/null +++ b/mqtt/heater/modules/watcher.py @@ -0,0 +1,34 @@ +import time +from typing import List, Dict + +from heater.modules.heater import Heater + + +class Watcher: + + def __init__(self, heater: Dict[str, Heater]): + self.heater = heater + + def publish(self, client): + for heater in self.heater.values(): + if heater.needs_publish(): + client.publish(heater.set_topic, heater.payload()) + time.sleep(2) + + def update(self, name, temperature): + heater: Heater = self.heater.get(name) + heater.wanted_temperature = temperature + + def get_topics(self): + return [heater.topic for heater in self.heater.values()] + + def update_actual_heating_point_for_topic(self, topic, payload): + for heater in self.heater.values(): + if heater.topic == topic: + heater.update_actual_heating_point(payload) + return + + def pull_values(self, client): + for heater in self.heater.values(): + topic, payload = heater.topic_and_payload_for_query() + client.publish(topic, payload)