introduced fyrtur-control

This commit is contained in:
Ingolf Wagner 2021-03-17 09:09:44 +01:00
parent 0e1f0572cf
commit 4003c41c3d
No known key found for this signature in database
GPG key ID: 76BF5F1928B9618B
5 changed files with 205 additions and 287 deletions

View file

@ -54,22 +54,18 @@ in {
# all scenes
input_select.scene = {
icon = "mdi:brightness-auto";
options = [ "default" "night" "outside" "cooking" ];
options = [ "up-bright" "up-dark" "half" "down" "night" "outside" ];
};
# scenes controlled by buttons
input_select.scene_button = {
icon = "mdi:brightness-auto";
options = [ "default" "night" ];
options = [ "up-dark" "night" ];
};
input_boolean.situation_toggle.icon = "mdi:toggle-switch";
input_boolean.printer_toggle.icon = "mdi:toggle-switch";
input_boolean.windows_up.icon = "mdi:toggle-switch";
# heater scenes
#input_select.heater_state.options = [ "off" "on1" "on2" "on3" ];
#input_select.heater_state_memory.options = [ "off" "on1" "on2" "on3" ];
automation = [
{
@ -152,28 +148,7 @@ in {
entity_id = "input_select.scene";
option = "outside";
};
}
#{
# service = "input_select.select_option";
# data_template = {
# entity_id = "input_select.heater_state_memory";
# option = ''
# {% if not is_state("input_select.heater_state", "off") %}
# {{ states('input_select.heater_state') }}
# {%- else -%}
# {{ states('input_select.heater_state_memory') }}
# {%- endif %}
# '';
# };
#}
#{
# service = "input_select.select_option";
# data = {
# entity_id = "input_select.heater_state";
# option = "off";
# };
#}
];
}];
}
{
@ -202,155 +177,11 @@ in {
service = "input_select.select_option";
data = {
entity_id = "input_select.scene";
option = "default";
option = "up-dark";
};
}
#{
# service = "input_select.select_option";
# data_template = {
# entity_id = "input_select.heater_state";
# option = "{{ states('input_select.heater_state_memory') }}";
# };
#}
];
}
# window roles
{
alias = "windows state = down in the evening";
trigger = [{
platform = "sun";
event = "sunset";
offset = "+00:01:00"; # 10 min after sunset
}];
action = [{
service = "input_boolean.turn_off";
data.entity_id = "input_boolean.windows_up";
}];
}
{
alias = "windows state = up in the morning";
trigger = [{
platform = "time";
at = "08:30:00";
}];
action = [{
service = "input_boolean.turn_on";
data.entity_id = "input_boolean.windows_up";
}];
}
{
alias = "handle windows up state";
trigger = [
#{
# platform = "time_pattern";
# minutes = "/5"; # every 5 minutes
#}
{
platform = "state";
entity_id = "input_boolean.windows_up";
}
];
action = [{
service = "script.turn_on";
data_template.entity_id = ''
{% if is_state('input_boolean.windows_up','on') -%}
script.fyrtur_up
{%- else -%}
script.fyrtur_down
{%- endif %}
'';
}];
}
# heater
#{
# alias = "heater state = on1 in the morning";
# trigger = [{
# platform = "time";
# at = "09:00:00";
# }];
# action = [
# {
# service = "input_select.select_option";
# data = {
# entity_id = "input_select.heater_state";
# option = "on1";
# };
# }
# {
# service = "input_select.select_option";
# data = {
# entity_id = "input_select.heater_state_memory";
# option = "on1";
# };
# }
# ];
#}
#{
# alias = "heater state = on2 in the early evening";
# trigger = [{
# platform = "time";
# at = "22:30:00";
# }];
# action = [
# {
# service = "input_select.select_option";
# data = {
# entity_id = "input_select.heater_state";
# option = "on2";
# };
# }
# {
# service = "input_select.select_option";
# data = {
# entity_id = "input_select.heater_state_memory";
# option = "on2";
# };
# }
# ];
#}
#{
# alias = "heater state = off in the evening";
# trigger = [{
# platform = "time";
# at = "23:30:00";
# }];
# action = [
# {
# service = "input_select.select_option";
# data = {
# entity_id = "input_select.heater_state";
# option = "off";
# };
# }
# {
# service = "input_select.select_option";
# data = {
# entity_id = "input_select.heater_state_memory";
# option = "off";
# };
# }
# ];
#}
#{
# alias = "handle heater state";
# trigger = [
# {
# platform = "time_pattern";
# minutes = "/10"; # every 5 minutes
# }
# {
# platform = "state";
# entity_id = "input_select.heater_state";
# }
# ];
# action = [{
# service = "script.turn_on";
# data_template.entity_id =
# "script.heater_{{ states('input_select.heater_state') }}";
# }];
#}
];
group = let
@ -405,111 +236,6 @@ in {
};
script = let
# delay in seconds
delay = 2;
heater_on = heater: temperatur: {
service = "mqtt.publish";
data_template = {
topic = "zigbee2mqtt/${heater}/set"; # office
payload_template = builtins.toJSON {
system_mode = "auto";
current_heating_setpoint = toString temperatur;
eurotronic_host_flags.window_open = false;
};
};
};
#heater_off = heater: {
# service = "mqtt.publish";
# data_template = {
# topic = "zigbee2mqtt/${heater}/set"; # office
# payload_template = ''{"system_mode":"off"}'';
# };
#};
hot = 23;
cold = 14;
fyrtur_command = device: position: {
service = "mqtt.publish";
data_template = {
topic = "zigbee2mqtt/${device}/set";
payload_template = ''{"position":"${toString position}"}'';
};
};
in {
#heater_off = {
# sequence = [
# (heater_on "heater1" 5) # office
# { delay = delay; }
# (heater_on "heater2" 5) # office
# { delay = delay; }
# (heater_on "heater3" 5) # bed room
# { delay = delay; }
# (heater_on "heater4" 5) # storage room
# ];
#};
#heater_on1 = {
# sequence = [
# (heater_on "heater1" hot) # office
# { delay = delay; }
# (heater_on "heater2" hot) # office
# { delay = delay; }
# (heater_on "heater3" cold) # bed room
# { delay = delay; }
# (heater_on "heater4" cold) # storage room
# ];
#};
#heater_on2 = {
# sequence = [
# (heater_on "heater1" hot) # office
# { delay = delay; }
# (heater_on "heater2" hot) # office
# { delay = delay; }
# (heater_on "heater3" hot) # bed room
# { delay = delay; }
# (heater_on "heater4" cold) # storage room
# ];
#};
#heater_on3 = {
# sequence = [
# (heater_on "heater1" cold) # office
# { delay = delay; }
# (heater_on "heater2" cold) # office
# { delay = delay; }
# (heater_on "heater3" hot) # bed room
# { delay = delay; }
# (heater_on "heater4" cold) # storage room
# ];
#};
fyrtur_up = {
sequence = [
(fyrtur_command "fyrtur1" 100)
{ delay = delay; }
(fyrtur_command "fyrtur2" 100)
{ delay = delay; }
(fyrtur_command "fyrtur3" 100)
{ delay = delay; }
(fyrtur_command "fyrtur4" 100)
];
};
fyrtur_down = {
sequence = [
(fyrtur_command "fyrtur1" 16)
{ delay = delay; }
(fyrtur_command "fyrtur2" 16)
{ delay = delay; }
(fyrtur_command "fyrtur3" 22)
{ delay = delay; }
(fyrtur_command "fyrtur4" 22)
];
};
};
};

View file

@ -14,4 +14,19 @@
${myPython}/bin/python heater.py
'';
};
users.users.fyrtur-control = { };
systemd.services.fyrtur-control = {
enable = true;
wantedBy = [ "multi-user.target" ];
serviceConfig = { User = "fyrtur-control"; };
script =
let myPython = pkgs.python3.withPackages (ps: with ps; [ paho-mqtt ]);
in ''
cd ${<mqtt>}
${myPython}/bin/python fyrtur.py
'';
};
}

View file

@ -10,9 +10,40 @@
};
scenes = [
{
name = "default";
ignored_sensors =
[ "zigbee2mqtt/door_sensor_5" "zigbee2mqtt/door_sensor_1" ];
name = "up-dark";
ignored_sensors = [
"zigbee2mqtt/door_sensor_1"
"zigbee2mqtt/door_sensor_4"
"zigbee2mqtt/door_sensor_5"
];
}
{
name = "half";
ignored_sensors = [
"zigbee2mqtt/door_sensor_1"
"zigbee2mqtt/door_sensor_4"
"zigbee2mqtt/door_sensor_5"
];
}
{
name = "down";
ignored_sensors = [
"zigbee2mqtt/door_sensor_1"
"zigbee2mqtt/door_sensor_4"
"zigbee2mqtt/door_sensor_5"
];
}
{
name = "up-bright";
disabled_switches = [
"zigbee2mqtt/led_1"
"zigbee2mqtt/led_2"
"zigbee2mqtt/light_2"
"zigbee2mqtt/light_4"
"zigbee2mqtt/light_5"
"zigbee2mqtt/light_7"
];
ignored_sensors = [ "zigbee2mqtt/door_sensor_4" ];
}
{
name = "outside";

150
mqtt/fyrtur.py Normal file
View file

@ -0,0 +1,150 @@
import time
from enum import Enum
from typing import Dict
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 = "up-dark"
class Position(Enum):
UP = 1
DOWN = 2
HALF = 3
class Fyrtur:
def __init__(self, topic, set_topic, top, bottom):
self.topic = topic
self.set_topic = set_topic
self.top = top
self.bottom = bottom
self.current_position = 100
self.wanted_position = 100
def update_position(self, payload):
self.current_position = payload["position"]
def needs_publish(self):
return self.wanted_position != self.current_position
def payload(self):
payload = {"position": self.wanted_position}
return json.dumps(payload)
class FyrturWatcher:
def __init__(self, fyrturs: Dict[str, Fyrtur]):
self.fyrturs = fyrturs
def get_topics(self):
return [fyrtur.topic for fyrtur in self.fyrturs.values()]
def update_position(self, topic, payload):
for fyrtur in self.fyrturs.values():
if fyrtur.topic == topic:
fyrtur.update_position(payload)
return
def update(self, name, position : Position):
fyrtur: Fyrtur = self.fyrturs.get(name)
if position == Position.UP:
fyrtur.wanted_position = fyrtur.top
elif position == Position.DOWN:
fyrtur.wanted_position = fyrtur.bottom
elif position == Position.HALF:
fyrtur.wanted_position = round((fyrtur.top - fyrtur.bottom) / 2)
def publish(self, client):
for fyrtur in self.fyrturs.values():
if fyrtur.needs_publish():
client.publish(fyrtur.set_topic, fyrtur.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),
})
# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, _userdata, _flags, rc):
print("Connected with result code " + str(rc))
threading.Thread(target=loop_thread, args=(client,), daemon=True).start()
# 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)
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_position(topic, payload)
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
def update_scene(client):
if scene in ["night", "down"]:
watcher.update("office1", Position.DOWN)
watcher.update("office2", Position.DOWN)
watcher.update("bedroom", Position.DOWN)
elif scene in ["default", "up-bright", "up-dark"]:
watcher.update("office1", Position.UP)
watcher.update("office2", Position.UP)
watcher.update("bedroom", Position.UP)
elif scene in ["half"]:
watcher.update("office1", Position.HALF)
watcher.update("office2", Position.HALF)
watcher.update("bedroom", Position.HALF)
else:
watcher.update("office1", Position.DOWN)
watcher.update("office2", Position.DOWN)
watcher.update("bedroom", Position.DOWN)
watcher.publish(client)
def loop_thread(client):
while True:
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
# manual interface.
mqttClient.loop_forever()

View file

@ -52,18 +52,14 @@ def parse_message(msg):
def update_scene(client):
if scene == "night":
if scene in ["night", "outside"]:
watcher.update("office1", 10)
watcher.update("office2", 10)
watcher.update("bedroom", 13)
elif scene == "default":
elif scene in ["default", "up-bright", "up-dark", "half", "down"]:
watcher.update("office1", 23)
watcher.update("office2", 23)
watcher.update("bedroom", 18)
elif scene == "outside":
watcher.update("office1", 10)
watcher.update("office2", 10)
watcher.update("bedroom", 13)
else:
watcher.update("office1", 10)
watcher.update("office2", 10)