From cf61334f5e62a4ee5668fbe1d3a65c4ea15f90c9 Mon Sep 17 00:00:00 2001 From: Ingolf Wagner Date: Sat, 20 Mar 2021 13:28:40 +0100 Subject: [PATCH] heater fiddeling around with stuff --- configs/pepe/home-assistant.nix | 2 +- .../{heater-control.nix => iot-control.nix} | 0 configs/pepe/home-assistant/zigbee2mqtt.nix | 11 +- .../home-assistant/zigbee2mqtt/fyrtur.nix | 8 +- .../home-assistant/zigbee2mqtt/heater.nix | 12 +- .../home-assistant/zigbee2mqtt/service.nix | 118 ++++++++++++++++++ configs/sternchen/packages.nix | 1 - mqtt/fyrtur.py | 19 ++- mqtt/heater.py | 49 ++++---- 9 files changed, 171 insertions(+), 49 deletions(-) rename configs/pepe/home-assistant/{heater-control.nix => iot-control.nix} (100%) create mode 100644 configs/pepe/home-assistant/zigbee2mqtt/service.nix diff --git a/configs/pepe/home-assistant.nix b/configs/pepe/home-assistant.nix index 0276fcf..c3ee8a3 100644 --- a/configs/pepe/home-assistant.nix +++ b/configs/pepe/home-assistant.nix @@ -6,7 +6,7 @@ in { #./home-assistant/mpd.nix #./home-assistant/timer.nix ./home-assistant/light-control.nix - ./home-assistant/heater-control.nix + ./home-assistant/iot-control.nix ./home-assistant/chaospott.nix ./home-assistant/kodi.nix ./home-assistant/mqtt.nix diff --git a/configs/pepe/home-assistant/heater-control.nix b/configs/pepe/home-assistant/iot-control.nix similarity index 100% rename from configs/pepe/home-assistant/heater-control.nix rename to configs/pepe/home-assistant/iot-control.nix diff --git a/configs/pepe/home-assistant/zigbee2mqtt.nix b/configs/pepe/home-assistant/zigbee2mqtt.nix index b900e87..e96b528 100644 --- a/configs/pepe/home-assistant/zigbee2mqtt.nix +++ b/configs/pepe/home-assistant/zigbee2mqtt.nix @@ -6,6 +6,7 @@ let unstable = import { }; in { imports = [ ./mqtt.nix + ./zigbee2mqtt/service.nix ./zigbee2mqtt/buttons.nix ./zigbee2mqtt/configurationHelper.nix ./zigbee2mqtt/doors.nix @@ -18,7 +19,7 @@ in { ./zigbee2mqtt/temperatur.nix ]; - services.zigbee2mqtt = { + custom.services.zigbee2mqtt = { enable = true; #package = pkgs.own_zigbee2mqtt; #package = unstable.zigbee2mqtt; @@ -77,9 +78,9 @@ in { }; }; - systemd.services.zigbee2mqtt.environment = { - ZIGBEE2MQTT_DATA = "/var/lib/zigbee2mqtt"; - }; + #systemd.services.zigbee2mqtt.environment = { + # ZIGBEE2MQTT_DATA = "/var/lib/zigbee2mqtt"; + #}; services.nginx = { enable = true; @@ -89,7 +90,7 @@ in { serverAliases = [ "zigbee.pepe.private" ]; locations."/" = { proxyPass = "http://localhost:${ - toString config.services.zigbee2mqtt.config.frontend.port + toString config.custom.services.zigbee2mqtt.config.frontend.port }"; proxyWebsockets = true; }; diff --git a/configs/pepe/home-assistant/zigbee2mqtt/fyrtur.nix b/configs/pepe/home-assistant/zigbee2mqtt/fyrtur.nix index dbac7e8..6001e5d 100644 --- a/configs/pepe/home-assistant/zigbee2mqtt/fyrtur.nix +++ b/configs/pepe/home-assistant/zigbee2mqtt/fyrtur.nix @@ -3,10 +3,10 @@ let # https://www.zigbee2mqtt.io/devices/E1757.html allDevices = { - "fyrtur1" = { id = "0x680ae2fffe64fa40"; }; # office - "fyrtur2" = { id = "0x680ae2fffe6e9f41"; }; # bed room - "fyrtur3" = { id = "0x680ae2fffe8f6411"; }; # (retoure) - "fyrtur4" = { id = "0x680ae2fffe91d234"; }; # office (close to kitchen) + "office_fyrtur_1" = { id = "0x680ae2fffe64fa40"; }; + "office_fyrtur_2" = { id = "0x680ae2fffe91d234"; }; + "bedroom_fyrtur_1" = { id = "0x680ae2fffe6e9f41"; }; + "broken_fyrtur_1" = { id = "0x680ae2fffe8f6411"; }; }; # -t "zigbee2mqtt/fyrtur1/set" -m '{"position":100}' diff --git a/configs/pepe/home-assistant/zigbee2mqtt/heater.nix b/configs/pepe/home-assistant/zigbee2mqtt/heater.nix index 417a8e3..6166667 100644 --- a/configs/pepe/home-assistant/zigbee2mqtt/heater.nix +++ b/configs/pepe/home-assistant/zigbee2mqtt/heater.nix @@ -3,10 +3,10 @@ let # https://www.zigbee2mqtt.io/devices/SPZB0001.html allDevices = { - "heater1" = { id = "0x00158d00032f5ee4"; }; # office - "heater2" = { id = "0x00158d00032f5f9f"; }; # office (kitchen) - "heater3" = { id = "0x00158d00032f6d1e"; }; # bed room - "heater4" = { id = "0x00158d00032f604d"; }; # abstell raum + "office_heater_1" = { id = "0x00158d00032f5ee4"; }; # office + "office_heater_2" = { id = "0x00158d00032f5f9f"; }; # office (kitchen) + "bedroom_heater_1" = { id = "0x00158d00032f6d1e"; }; # bed room + "storage_heater_1" = { id = "0x00158d00032f604d"; }; # abstell raum }; # -t "zigbee2mqtt/heater3/set" -m '{"system_mode":"auto","current_heating_setpoint":23}' @@ -27,9 +27,9 @@ in { "battery_low" "eurotronic_host_flags" "eurotronic_system_mode" - "occupied_heating_setpoint" + #"occupied_heating_setpoint" #"pi_heating_demand" - "unoccupied_heating_setpoint" + #"unoccupied_heating_setpoint" ]; }; }) allDevices; diff --git a/configs/pepe/home-assistant/zigbee2mqtt/service.nix b/configs/pepe/home-assistant/zigbee2mqtt/service.nix new file mode 100644 index 0000000..a6fd536 --- /dev/null +++ b/configs/pepe/home-assistant/zigbee2mqtt/service.nix @@ -0,0 +1,118 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.custom.services.zigbee2mqtt; + + configJSON = pkgs.writeText "configuration.json" (builtins.toJSON + (recursiveUpdate cfg.config config.services.zigbee2mqtt.config)); + configFile = + pkgs.runCommand "configuration.yaml" { preferLocalBuild = true; } '' + ${pkgs.remarshal}/bin/json2yaml -i ${configJSON} -o $out + ''; + + # the default config contains all required settings, + # so the service starts up without crashing. + defaultConfig = { + homeassistant = false; + permit_join = false; + mqtt = { + base_topic = "zigbee2mqtt"; + server = "mqtt://localhost:1883"; + }; + serial.port = "/dev/ttyACM0"; + # put device configuration into separate file because configuration.yaml + # is copied from the store on startup + devices = "devices.yaml"; + }; +in { + options.custom.services.zigbee2mqtt = { + enable = mkEnableOption "enable zigbee2mqtt service"; + + package = mkOption { + description = "ignored"; + default = pkgs.zigbee2mqtt.override { dataDir = cfg.dataDir; }; + defaultText = "pkgs.zigbee2mqtt"; + type = types.package; + }; + + dataDir = mkOption { + description = "Zigbee2mqtt data directory"; + default = "/var/lib/zigbee2mqtt"; + type = types.path; + }; + + config = mkOption { + default = { }; + type = with types; nullOr attrs; + example = literalExample '' + { + homeassistant = config.services.home-assistant.enable; + permit_join = true; + serial = { + port = "/dev/ttyACM1"; + }; + } + ''; + description = '' + Your configuration.yaml as a Nix attribute set. + ''; + }; + }; + + config = mkIf (cfg.enable) { + + virtualisation.oci-containers.containers.zigbee2mqtt = { + image = "koenkk/zigbee2mqtt:1.18.1"; + volumes = [ + "${cfg.dataDir}:/app/data" + #"/run/udev:/run/udev:ro" + ]; + extraOptions = [ + "--device=${cfg.config.serial.port}" # /dev/ttyUSB0 + "--network=host" + #"--privileged=true" + ]; + environment = { TZ = "Europe/Amsterdam"; }; + #ports = [ "127.0.0.1:${toString frontPort}:80" ]; + }; + + # create config before staring container + systemd.services.docker-zigbee2mqtt = { + preStart = '' + cp --no-preserve=mode ${configFile} "${cfg.dataDir}/configuration.yaml" + ''; + }; + + #systemd.services.zigbee2mqtt = { + # description = "Zigbee2mqtt Service"; + # wantedBy = [ "multi-user.target" ]; + # after = [ "network.target" ]; + # environment.ZIGBEE2MQTT_DATA = cfg.dataDir; + # serviceConfig = { + # ExecStart = "${cfg.package}/bin/zigbee2mqtt"; + # User = "zigbee2mqtt"; + # WorkingDirectory = cfg.dataDir; + # Restart = "on-failure"; + # ProtectSystem = "strict"; + # ReadWritePaths = cfg.dataDir; + # PrivateTmp = true; + # RemoveIPC = true; + # }; + # preStart = '' + # cp --no-preserve=mode ${configFile} "${cfg.dataDir}/configuration.yaml" + # ''; + #}; + + #users.users.zigbee2mqtt = { + # home = cfg.dataDir; + # createHome = true; + # group = "zigbee2mqtt"; + # extraGroups = [ "dialout" ]; + # uid = config.ids.uids.zigbee2mqtt; + #}; + + #users.groups.zigbee2mqtt.gid = config.ids.gids.zigbee2mqtt; + }; +} diff --git a/configs/sternchen/packages.nix b/configs/sternchen/packages.nix index 6b86858..cf8210b 100644 --- a/configs/sternchen/packages.nix +++ b/configs/sternchen/packages.nix @@ -25,7 +25,6 @@ in { gwenview skanlite - #(tor-browser-bundle-bin.overrideAttrs (old: { # #version = "10.0.13"; # src = pkgs.fetchurl { diff --git a/mqtt/fyrtur.py b/mqtt/fyrtur.py index df4f8f1..3eaabe5 100644 --- a/mqtt/fyrtur.py +++ b/mqtt/fyrtur.py @@ -17,9 +17,8 @@ class Position(Enum): class Fyrtur: - def __init__(self, topic, set_topic, top, bottom): + def __init__(self, topic, top, bottom): self.topic = topic - self.set_topic = set_topic self.top = top self.bottom = bottom self.current_position = 100 @@ -31,9 +30,9 @@ class Fyrtur: def needs_publish(self): return self.wanted_position != self.current_position - def payload(self): + def topic_and_payload_for_set(self): payload = {"position": self.wanted_position} - return json.dumps(payload) + return ("%s/set" % self.topic), json.dumps(payload) class FyrturWatcher: @@ -61,14 +60,15 @@ class FyrturWatcher: def publish(self, client): for fyrtur in self.fyrturs.values(): if fyrtur.needs_publish(): - client.publish(fyrtur.set_topic, fyrtur.payload()) + topic, payload = fyrtur.topic_and_payload_for_set() + client.publish(topic, payload) time.sleep(2) watcher = FyrturWatcher({ - "office1": Fyrtur(topic="zigbee2mqtt/fyrtur1", set_topic="zigbee2mqtt/fyrtur1/set", top=100, bottom=16), - "office2": Fyrtur(topic="zigbee2mqtt/fyrtur4", set_topic="zigbee2mqtt/fyrtur4/set", top=100, bottom=22), - "bedroom": Fyrtur(topic="zigbee2mqtt/fyrtur2", set_topic="zigbee2mqtt/fyrtur2/set", top=100, bottom=16), + "office1": Fyrtur(topic="zigbee2mqtt/office_fyrtur_1", top=100, bottom=16), + "office2": Fyrtur(topic="zigbee2mqtt/office_fyrtur_2", top=100, bottom=22), + "bedroom": Fyrtur(topic="zigbee2mqtt/bedroom_fyrtur_1", top=100, bottom=16), }) @@ -83,7 +83,6 @@ def on_connect(client, _userdata, _flags, rc): 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. @@ -110,7 +109,7 @@ def update_scene(client): watcher.update("office1", Position.DOWN) watcher.update("office2", Position.DOWN) watcher.update("bedroom", Position.DOWN) - elif scene in ["default", "up-bright", "up-dark"]: + elif scene in ["default", "up-bright", "up-dark" "outside"]: watcher.update("office1", Position.UP) watcher.update("office2", Position.UP) watcher.update("bedroom", Position.UP) diff --git a/mqtt/heater.py b/mqtt/heater.py index 255e048..a830e22 100644 --- a/mqtt/heater.py +++ b/mqtt/heater.py @@ -6,23 +6,12 @@ from typing import Dict class Heater: - def __init__(self, topic, set_topic): + def __init__(self, topic): self.not_initialized_yet = True - self.wanted_temperature = 15 - self.actual_temperature = 15 - self.set_topic = set_topic + self.wanted_temperature = 10 + self.actual_temperature = 10 self.topic = topic - def payload(self): - payload = { - "system_mode": "auto", - "current_heating_setpoint": self.wanted_temperature, - #"occupied_heating_setpoint": self.wanted_temperature, - #"unoccupied_heating_setpoint": self.wanted_temperature, - "eurotronic_host_flags": {"window_open": True} - } - return json.dumps(payload) - def needs_publish(self): if self.not_initialized_yet: return True @@ -40,10 +29,25 @@ class Heater: def topic_and_payload_for_query(self): payload = { - "current_heating_setpoint": "" + "current_heating_setpoint": "", + "occupied_heating_setpoint": "", + "unoccupied_heating_setpoint": "", + "local_temperature": "", + #"pi_heating_demand": "", + #"system_mode": "", } return ("%s/get" % self.topic), json.dumps(payload) + def topic_and_payload_for_set(self): + payload = { + "system_mode": "auto", + #"current_heating_setpoint": str(self.wanted_temperature), + "occupied_heating_setpoint": str(self.wanted_temperature), + "unoccupied_heating_setpoint": str(self.wanted_temperature), + "eurotronic_host_flags": {"window_open": True} + } + return ("%s/set" % self.topic), json.dumps(payload) + class Watcher: @@ -53,7 +57,8 @@ class Watcher: def publish(self, client): for heater in self.heater.values(): if heater.needs_publish(): - client.publish(heater.set_topic, heater.payload()) + topic, payload = heater.topic_and_payload_for_set() + client.publish(topic, payload) time.sleep(2) def update(self, name, temperature): @@ -78,9 +83,9 @@ class 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"), + "office1": Heater(topic="zigbee2mqtt/office_heater_1"), + "office2": Heater(topic="zigbee2mqtt/office_heater_2"), + "bedroom": Heater(topic="zigbee2mqtt/bedroom_heater_1"), }) @@ -109,7 +114,6 @@ def on_message(client, _userdata, msg): else: print("got %s" % topic) watcher.update_actual_heating_point_for_topic(topic, payload) - # watcher.publish(client) def parse_message(msg): @@ -122,7 +126,7 @@ def update_scene(client): if scene in ["night", "outside"]: watcher.update("office1", 10) watcher.update("office2", 10) - watcher.update("bedroom", 13) + watcher.update("bedroom", 10) elif scene in ["default", "up-bright", "up-dark", "half", "down"]: watcher.update("office1", 26) watcher.update("office2", 26) @@ -130,7 +134,7 @@ def update_scene(client): else: watcher.update("office1", 10) watcher.update("office2", 10) - watcher.update("bedroom", 13) + watcher.update("bedroom", 10) watcher.publish(client) @@ -138,6 +142,7 @@ def update_scene(client): def loop_thread(client): while True: watcher.publish(client) + watcher.pull_values(client) time.sleep(120)