nixos-config/mqtt/heater.py

171 lines
5.4 KiB
Python
Raw Normal View History

2021-03-16 22:00:45 +01:00
import json
2021-03-18 13:15:51 +01:00
import paho.mqtt.client as mqtt
2021-03-16 22:00:45 +01:00
import threading
2021-03-18 13:15:51 +01:00
import time
from typing import Dict
class Heater:
2021-03-20 13:28:40 +01:00
def __init__(self, topic):
2021-03-18 13:15:51 +01:00
self.not_initialized_yet = True
self.wanted_temperature = 14
self.actual_temperature = 14
2021-03-18 13:15:51 +01:00
self.topic = topic
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
2021-03-26 02:46:05 +01:00
print(
"%s: update wanted temperature %d"
% (self.topic, self.actual_temperature)
)
2021-03-18 13:15:51 +01:00
self.actual_temperature = heating_setpoint
2021-03-26 02:46:05 +01:00
print(
"%s: update actual temperature %d" % (self.topic, self.actual_temperature)
)
2021-03-18 13:15:51 +01:00
def topic_and_payload_for_query(self):
payload = {
2021-03-20 13:28:40 +01:00
"current_heating_setpoint": "",
"occupied_heating_setpoint": "",
"unoccupied_heating_setpoint": "",
"local_temperature": "",
2021-03-26 02:46:05 +01:00
# "pi_heating_demand": "",
# "system_mode": "",
2021-03-18 13:15:51 +01:00
}
return ("%s/get" % self.topic), json.dumps(payload)
2021-03-20 13:28:40 +01:00
def topic_and_payload_for_set(self):
payload = {
"system_mode": "auto",
2021-03-26 02:46:05 +01:00
# "current_heating_setpoint": str(self.wanted_temperature),
2021-03-20 13:28:40 +01:00
"occupied_heating_setpoint": str(self.wanted_temperature),
"unoccupied_heating_setpoint": str(self.wanted_temperature),
2021-03-26 02:46:05 +01:00
"eurotronic_host_flags": {"window_open": True},
2021-03-20 13:28:40 +01:00
}
return ("%s/set" % self.topic), json.dumps(payload)
2021-03-18 13:15:51 +01:00
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():
2021-03-20 13:28:40 +01:00
topic, payload = heater.topic_and_payload_for_set()
client.publish(topic, payload)
2021-03-18 13:15:51 +01:00
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)
2021-03-16 21:09:22 +01:00
2021-03-17 07:34:55 +01:00
2021-03-16 22:00:45 +01:00
scene = "default"
2021-03-16 21:09:22 +01:00
2021-03-26 02:46:05 +01:00
watcher = Watcher(
{
"office1": Heater(topic="zigbee2mqtt/office_heater_1"),
"office2": Heater(topic="zigbee2mqtt/office_heater_2"),
"bedroom": Heater(topic="zigbee2mqtt/bedroom_heater_1"),
"storage": Heater(topic="zigbee2mqtt/storage_heater_1"),
}
)
2021-03-17 07:34:55 +01:00
2021-03-16 21:09:22 +01:00
# The callback for when the client receives a CONNACK response from the server.
2021-03-16 22:00:45 +01:00
def on_connect(client, _userdata, _flags, rc):
2021-03-16 21:09:22 +01:00
print("Connected with result code " + str(rc))
2021-03-16 22:00:45 +01:00
threading.Thread(target=loop_thread, args=(client,), daemon=True).start()
2021-03-16 21:09:22 +01:00
# Subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
client.subscribe("control/lights/set")
2021-03-17 07:34:55 +01:00
for topic in watcher.get_topics():
client.subscribe(topic)
watcher.pull_values(client)
2021-03-16 21:09:22 +01:00
# The callback for when a PUBLISH message is received from the server.
2021-03-16 22:00:45 +01:00
def on_message(client, _userdata, msg):
global scene
(topic, payload) = parse_message(msg)
2021-03-17 07:34:55 +01:00
if topic == "control/lights/set":
2021-03-26 02:46:05 +01:00
print("set scene %s -> %s" % (scene, payload["scene"]))
scene = payload["scene"]
2021-03-17 07:34:55 +01:00
update_scene(client)
else:
print("got %s" % topic)
watcher.update_actual_heating_point_for_topic(topic, payload)
2021-03-16 22:00:45 +01:00
def parse_message(msg):
m_decode = str(msg.payload.decode("utf-8", "ignore"))
payload = json.loads(m_decode) # decode json data
return msg.topic, payload
2021-03-17 07:34:55 +01:00
def update_scene(client):
2021-03-17 09:09:44 +01:00
if scene in ["night", "outside"]:
watcher.update("office1", 14)
watcher.update("office2", 14)
watcher.update("bedroom", 14)
2021-03-23 07:37:21 +01:00
watcher.update("storage", 14)
2021-03-17 09:09:44 +01:00
elif scene in ["default", "up-bright", "up-dark", "half", "down"]:
2021-03-23 07:37:21 +01:00
watcher.update("office1", 25)
watcher.update("office2", 25)
watcher.update("bedroom", 18)
watcher.update("storage", 18)
2021-03-16 22:00:45 +01:00
else:
watcher.update("office1", 14)
watcher.update("office2", 14)
watcher.update("bedroom", 14)
2021-03-23 07:37:21 +01:00
watcher.update("storage", 14)
2021-03-17 07:34:55 +01:00
watcher.publish(client)
2021-03-16 21:09:22 +01:00
2021-03-16 22:00:45 +01:00
def loop_thread(client):
while True:
2021-03-17 07:34:55 +01:00
watcher.publish(client)
2021-03-20 13:28:40 +01:00
watcher.pull_values(client)
2021-03-17 07:34:55 +01:00
time.sleep(120)
2021-03-16 21:09:22 +01:00
2021-03-16 22:00:45 +01:00
if __name__ == "__main__":
2021-03-17 07:34:55 +01:00
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)
2021-03-18 13:15:51 +01:00
2021-03-16 22:00:45 +01:00
# Blocking call that processes network traffic, dispatches callbacks and
# handles reconnecting.
# Other loop*() functions are available that give a threaded interface and a
# manual interface.
mqttClient.loop_forever()