home-assistant: add light-control

This commit is contained in:
Ingolf Wagner 2020-06-01 04:12:15 +02:00
parent ba39ae9f78
commit d76dd45c67
No known key found for this signature in database
GPG key ID: 76BF5F1928B9618B
8 changed files with 342 additions and 420 deletions

265
assets/light-control.json Normal file
View file

@ -0,0 +1,265 @@
{
"credentials": {
"host": "tcp://localhost:1883",
"user": "homeassistant",
"password": "hallo"
},
"scenes": [
{
"name": "default"
},
{
"name": "outside",
"room_tracking_enabled": false
},
{
"name": "night",
"room_tracking_enabled": false,
"brightness": 25,
"exclude_switches": [
"stat/PAL01/RESULT",
"stat/PAL03/RESULT",
"stat/PAL04/RESULT",
"zigbee2mqtt/light_2"
]
}
],
"sensors": [
{
"topic": "zigbee2mqtt/motion_sensor_2",
"key": "occupancy",
"rooms": [
"bed_room"
],
"invert_state": false,
"delay": {
"secs": 60,
"nanos": 0
}
},
{
"topic": "zigbee2mqtt/motion_sensor_7",
"key": "occupancy",
"rooms": [
"bed_room"
],
"invert_state": false,
"delay": {
"secs": 60,
"nanos": 0
}
},
{
"topic": "zigbee2mqtt/motion_sensor_1",
"key": "occupancy",
"rooms": [
"kitchen_room"
],
"invert_state": false,
"delay": {
"secs": 60,
"nanos": 0
}
},
{
"topic": "zigbee2mqtt/motion_sensor_4",
"key": "occupancy",
"rooms": [
"living_room"
],
"invert_state": false,
"delay": {
"secs": 60,
"nanos": 0
}
},
{
"topic": "zigbee2mqtt/motion_sensor_5",
"key": "occupancy",
"rooms": [
"living_room"
],
"invert_state": false,
"delay": {
"secs": 60,
"nanos": 0
}
},
{
"topic": "zigbee2mqtt/motion_sensor_5",
"key": "occupancy",
"rooms": [
"bath_room"
],
"invert_state": false,
"delay": {
"secs": 60,
"nanos": 0
}
},
{
"topic": "zigbee2mqtt/motion_sensor_8",
"key": "occupancy",
"rooms": [
"bath_room"
],
"invert_state": false,
"delay": {
"secs": 60,
"nanos": 0
}
},
{
"topic": "zigbee2mqtt/door_sensor_2",
"key": "contact",
"rooms": [
"floor_room"
],
"invert_state": true,
"delay": {
"secs": 90,
"nanos": 0
}
},
{
"topic": "zigbee2mqtt/door_sensor_4",
"key": "contact",
"rooms": [
"floor_room"
],
"invert_state": true,
"delay": {
"secs": 90,
"nanos": 0
}
}
],
"switches": [
{
"topic": "zigbee2mqtt/light_1",
"key": "state",
"rooms": [
"floor_room"
],
"command": {
"command": "{\"state\":\"{{state}}\",\"brightness\":{{brightness}}}",
"init_command": null,
"topic": "zigbee2mqtt/light_1/set",
"on": "ON",
"off": "OFF"
}
},
{
"topic": "zigbee2mqtt/light_2",
"key": "state",
"rooms": [
"floor_room"
],
"command": {
"command": "{\"state\":\"{{state}}\",\"brightness\":{{brightness}}}",
"init_command": null,
"topic": "zigbee2mqtt/light_2/set",
"on": "ON",
"off": "OFF"
}
},
{
"topic": "zigbee2mqtt/light_3",
"key": "state",
"rooms": [
"living_room"
],
"command": {
"command": "{\"state\":\"{{state}}\",\"brightness\":{{brightness}}}",
"init_command": null,
"topic": "zigbee2mqtt/light_3/set",
"on": "ON",
"off": "OFF"
}
},
{
"topic": "zigbee2mqtt/light_4",
"key": "state",
"rooms": [
"bath_room"
],
"command": {
"command": "{\"state\":\"{{state}}\",\"brightness\":{{brightness}}}",
"init_command": null,
"topic": "zigbee2mqtt/light_4/set",
"on": "ON",
"off": "OFF"
}
},
{
"topic": "zigbee2mqtt/light_8",
"key": "state",
"rooms": [
"bed_room"
],
"command": {
"command": "{\"state\":\"{{state}}\",\"brightness\":{{brightness}}}",
"init_command": null,
"topic": "zigbee2mqtt/light_8/set",
"on": "ON",
"off": "OFF"
}
},
{
"topic": "stat/PAL01/RESULT",
"key": "POWER",
"rooms": [
"bed_room"
],
"command": {
"command": "{{state}}",
"init_command": "(null)",
"topic": "cmnd/PAL01/POWER",
"on": "ON",
"off": "OFF"
}
},
{
"topic": "stat/PAL03/RESULT",
"key": "POWER",
"rooms": [
"living_room"
],
"command": {
"command": "{{state}}",
"init_command": "(null)",
"topic": "cmnd/PAL03/POWER",
"on": "ON",
"off": "OFF"
}
},
{
"topic": "stat/PAL04/RESULT",
"key": "POWER",
"rooms": [
"bed_room"
],
"command": {
"command": "{{state}}",
"init_command": "(null)",
"topic": "cmnd/PAL04/POWER",
"on": "ON",
"off": "OFF"
}
},
{
"topic": "stat/PAL06/RESULT",
"key": "POWER",
"rooms": [
"kitchen_room"
],
"command": {
"command": "{{state}}",
"init_command": "(null)",
"topic": "cmnd/PAL06/POWER",
"on": "ON",
"off": "OFF"
}
}
]
}

View file

@ -5,6 +5,7 @@ in {
imports = [ imports = [
#./home-assistant/mpd.nix #./home-assistant/mpd.nix
#./home-assistant/timer.nix #./home-assistant/timer.nix
./home-assistant/light-control.nix
./home-assistant/chaospott.nix ./home-assistant/chaospott.nix
./home-assistant/kodi.nix ./home-assistant/kodi.nix
./home-assistant/mqtt.nix ./home-assistant/mqtt.nix
@ -38,397 +39,15 @@ in {
# ------------ # ------------
input_select.situation = { input_select.situation = {
icon = "mdi:brightness-auto"; icon = "mdi:brightness-auto";
options = [ options = [ "default" "night" "outside" ];
# it is dark outside and I want it to be bright inside
"dark"
# it is bright outside, so no need to be bright inside
"bright"
# it is dark ouside, but I don't want it bright inside
"essential"
];
}; };
input_boolean.situation_toggle.icon = "mdi:toggle-switch"; input_boolean.situation_toggle.icon = "mdi:toggle-switch";
# enable presents_tracking automation = [
input_boolean.presents_tracking.icon = "mdi:account";
input_select.room_present = {
icon = "mdi:map-marker";
options = [
"kitchen_room_present"
"bath_room_present"
"living_room_present"
"bed_room_present"
"floor_room_present"
"bath_room_present"
];
};
automation = let
# a help function that flattens even 2 times
flatMap = function: list: lib.flatten (map function (lib.flatten list));
allRooms =
[ "living_room" "floor_room" "bath_room" "bed_room" "kitchen_room" ];
# presents <-> absents : light settings
# -------------------------------------
roomSwitches = let
roomPresents = {
# group of the room that should be turned on
roomGroup,
# group of the room that should be turned off
turnOffGroup ? roomGroup,
# group of the room that that indicates presents in the room
presentsGroup,
# global situation
situation,
# brightness for dimable lights
brightness ? 255 }: [
{
alias =
"presents in ${presentsGroup} -> turn lights on in ${roomGroup} for ${situation}";
trigger = {
platform = "state";
entity_id = "group.${presentsGroup}";
from = "off";
to = "on";
};
condition = {
condition = "template";
value_template =
"{{ states( 'input_select.situation' ) == '${situation}' }}";
};
action = [
{
service = "switch.turn_on";
data.entity_id = "group.${roomGroup}";
}
{
service = "light.turn_on";
data = {
entity_id = "group.${roomGroup}";
brightness = brightness;
};
}
];
}
{
alias =
"absents in ${presentsGroup} -> turn lights off ${turnOffGroup} in ${situation}";
trigger = map (minutes: {
platform = "state";
entity_id = "group.${presentsGroup}";
from = "on";
to = "off";
for.seconds = minutes * 60;
}) [ 2 5 10 15 20 25 30 35 40 45 50 55 ];
condition = {
condition = "and";
conditions = [
{
condition = "template";
value_template =
"{{ states( 'input_select.situation' ) == '${situation}' }}";
}
{
condition = "or";
conditions = [
{
condition = "template";
value_template =
"{{ states( 'input_select.room_present' ) != '${presentsGroup}' }}";
}
{
condition = "template";
value_template =
"{{ states( 'input_boolean.presents_tracking' ) == 'off' }}";
}
];
}
];
};
action = [
{
service = "switch.turn_off";
data.entity_id = "group.${turnOffGroup}";
}
{
service = "light.turn_off";
data.entity_id = "group.${turnOffGroup}";
}
];
}
{
alias =
"finally absents in ${presentsGroup} -> turn lights off ${turnOffGroup} in ${situation}";
trigger = [{
platform = "state";
entity_id = "group.${presentsGroup}";
from = "on";
to = "off";
for.seconds = 60 * 60;
}];
condition = {
condition = "template";
value_template =
"{{ states( 'input_select.situation' ) == '${situation}' }}";
};
action = [
{
service = "switch.turn_off";
data.entity_id = "group.${turnOffGroup}";
}
{
service = "light.turn_off";
data.entity_id = "group.${turnOffGroup}";
}
];
}
];
onOffArguments = flatMap (name: [
{
presentsGroup = "${name}_present";
roomGroup = "${name}_lights";
situation = "dark";
}
{
brightness = 30;
presentsGroup = "${name}_present";
roomGroup = "${name}_essential";
situation = "essential";
turnOffGroup = "${name}_lights";
}
{
presentsGroup = "${name}_present";
roomGroup = "${name}_bright";
turnOffGroup = "${name}_lights";
situation = "bright";
}
]) allRooms;
in (flatMap roomPresents onOffArguments);
roomPresentSwitches = let
allRoomPresentGroups = map (name: "${name}_present") allRooms;
switchPresentsTracking =
# group to determine presents
presentGroup:
let
otherRoomPresentGroups =
(builtins.filter (name: name != presentGroup)
allRoomPresentGroups);
in [
{
alias =
"select ${presentGroup} if all other rooms are longer not present/active";
trigger = {
platform = "state";
entity_id = "group.${presentGroup}";
from = "on";
to = "off";
for.seconds = (2 * 60) - 15;
};
condition = {
condition = "and";
conditions = flatMap (otherRoomPresent: {
condition = "state";
entity_id = "group.${otherRoomPresent}";
state = "off";
for.seconds = 1 * 60;
}) otherRoomPresentGroups;
};
action = [{
service = "input_select.select_option";
data = {
entity_id = "input_select.room_present";
option = presentGroup;
};
}];
}
{
alias =
"select ${presentGroup} if no other room is present/active but ${presentGroup} is";
trigger = {
platform = "time_pattern";
minutes = "/1";
};
condition = {
condition = "and";
conditions = [{
condition = "state";
entity_id = "group.${presentGroup}";
state = "on";
for.seconds = 1 * 60;
}] ++ (flatMap (otherRoomPresent: {
condition = "state";
entity_id = "group.${otherRoomPresent}";
state = "off";
for.seconds = 1 * 60;
}) otherRoomPresentGroups);
};
action = [{
service = "input_select.select_option";
data = {
entity_id = "input_select.room_present";
option = presentGroup;
};
}];
}
{
alias = "select ${presentGroup} when entering the room";
trigger = {
platform = "state";
entity_id = "group.${presentGroup}";
from = "off";
to = "on";
};
action = [{
service = "input_select.select_option";
data = {
entity_id = "input_select.room_present";
option = presentGroup;
};
}];
}
];
in (flatMap switchPresentsTracking allRoomPresentGroups);
# change light scene in rooms when changing situation
# ---------------------------------------------------
situationSwitches = let
situationSwitch = {
# old group to turn off
fromRoomGroup,
# new group to turn on
toRoomGroup,
# group to determine presents
presentsGroup,
# old situation
fromSituation,
# new situation
toSituation,
# brightness for dimable lights
brightness ? 255 }: [
{
alias =
"${fromSituation} -> ${toSituation} for ${fromRoomGroup} -> ${toRoomGroup} on";
trigger = {
platform = "state";
entity_id = "input_select.situation";
from = fromSituation;
to = toSituation;
};
condition = {
condition = "state";
entity_id = "group.${presentsGroup}";
state = "on";
};
action = [
{
service = "switch.turn_off";
data.entity_id = "group.${fromRoomGroup}";
}
{
service = "light.turn_off";
data.entity_id = "group.${fromRoomGroup}";
}
{
service = "switch.turn_on";
data.entity_id = "group.${toRoomGroup}";
}
{
service = "light.turn_on";
data = {
entity_id = "group.${toRoomGroup}";
brightness = brightness;
};
}
];
}
{
alias =
"${fromSituation} -> ${toSituation} for ${fromRoomGroup} -> ${toRoomGroup} off";
trigger = {
platform = "state";
entity_id = "input_select.situation";
from = fromSituation;
to = toSituation;
};
condition = {
condition = "state";
entity_id = "group.${presentsGroup}";
state = "off";
};
action = [
{
service = "switch.turn_off";
data.entity_id = "group.${fromRoomGroup}";
}
{
service = "light.turn_off";
data.entity_id = "group.${fromRoomGroup}";
}
];
}
];
allArguments = flatMap (name: [
# essential <-> dark
{
presentsGroup = "${name}_present";
fromSituation = "dark";
toSituation = "essential";
fromRoomGroup = "${name}_lights";
toRoomGroup = "${name}_essential";
brightness = 30;
}
{
presentsGroup = "${name}_present";
fromSituation = "essential";
toSituation = "dark";
fromRoomGroup = "${name}_essential";
toRoomGroup = "${name}_lights";
}
# bright <-> dark
{
presentsGroup = "${name}_present";
fromSituation = "dark";
toSituation = "bright";
fromRoomGroup = "${name}_lights";
toRoomGroup = "${name}_bright";
}
{
presentsGroup = "${name}_present";
fromSituation = "bright";
toSituation = "dark";
fromRoomGroup = "${name}_bright";
toRoomGroup = "${name}_lights";
}
# bright <-> essential
{
presentsGroup = "${name}_present";
fromSituation = "bright";
toSituation = "essential";
fromRoomGroup = "${name}_bright";
toRoomGroup = "${name}_essential";
brightness = 30;
}
{
presentsGroup = "${name}_present";
fromSituation = "essential";
toSituation = "bright";
fromRoomGroup = "${name}_essential";
toRoomGroup = "${name}_bright";
}
]) allRooms;
in flatMap situationSwitch allArguments;
in situationSwitches ++ roomSwitches ++ roomPresentSwitches ++ [
# control situation with buttons # control situation with buttons
{ {
alias = "set essential"; alias = "set night scene";
trigger = { trigger = {
platform = "state"; platform = "state";
entity_id = "input_boolean.situation_toggle"; entity_id = "input_boolean.situation_toggle";
@ -436,15 +55,15 @@ in {
to = "on"; to = "on";
}; };
action = { action = {
service = "input_select.select_option"; service = "mqtt.publish";
data = { data = {
entity_id = "input_select.situation"; topic = "control/lights/set";
option = "essential"; payload = builtins.toJSON { scene = "night"; };
}; };
}; };
} }
{ {
alias = "unset essential"; alias = "set default scene";
trigger = { trigger = {
platform = "state"; platform = "state";
entity_id = "input_boolean.situation_toggle"; entity_id = "input_boolean.situation_toggle";
@ -452,34 +71,37 @@ in {
to = "off"; to = "off";
}; };
action = { action = {
service = "input_select.select_option"; service = "mqtt.publish";
data = { data = {
entity_id = "input_select.situation"; topic = "control/lights/set";
option = "dark"; payload = builtins.toJSON { scene = "default"; };
}; };
}; };
} }
# trigger outside
{ {
alias = "set essential"; alias = "set outside scene";
trigger = [ trigger = [
{ {
platform = "state"; platform = "state";
entity_id = "group.outside"; # todo : groups are not working right now
entity_id = "binary_sensor.door_sensor_2";
from = "off"; from = "off";
to = "on"; to = "on";
} }
{ {
platform = "state"; platform = "state";
entity_id = "group.outside"; # todo : groups are not working right now
entity_id = "binary_sensor.door_sensor_2";
from = "on"; from = "on";
to = "off"; to = "off";
} }
]; ];
action = { action = {
service = "input_boolean.turn_off"; service = "mqtt.publish";
data.entity_id = "input_boolean.presents_tracking"; data = {
topic = "control/lights/set";
payload = builtins.toJSON { scene = "outside"; };
};
}; };
} }

View file

@ -0,0 +1,14 @@
{ pkgs, lib, config, ... }:
let
lightControlConfig = pkgs.writeText "light-control.json"
(lib.fileContents <assets/light-control.json>);
in {
systemd.services."light-control" = {
wantedBy = [ "multi-user.target" ];
environment = { RUST_LOG = "rust_iot=trace"; };
script = ''
${pkgs.light-control}/bin/rust-iot ${lightControlConfig}
'';
};
}

View file

@ -21,8 +21,8 @@
password = lib.fileContents <secrets/zigbee/password>; password = lib.fileContents <secrets/zigbee/password>;
acl = [ "topic readwrite #" ]; acl = [ "topic readwrite #" ];
}; };
rust-iot = { lightcontrol = {
password = "password" password = "password";
acl = [ "topic readwrite #" ]; acl = [ "topic readwrite #" ];
}; };
}; };

View file

@ -12,20 +12,17 @@ let
"button_1" = { "button_1" = {
id = "0x00158d0002b04f65"; id = "0x00158d0002b04f65";
groups = [ "living_room" ]; groups = [ "living_room" ];
states.single = "input_boolean.presents_tracking"; states.single = "input_boolean.situation_toggle";
states.double = "input_boolean.situation_toggle";
}; };
"button_2" = { "button_2" = {
id = "0x00158d0002b04f09"; id = "0x00158d0002b04f09";
groups = [ "bed_room" ]; groups = [ "bed_room" ];
states.single = "input_boolean.presents_tracking"; states.single = "input_boolean.situation_toggle";
states.double = "input_boolean.situation_toggle";
}; };
"button_3" = { "button_3" = {
id = "0x00158d0002b00e04"; id = "0x00158d0002b00e04";
groups = [ "bed_room" ]; groups = [ "bed_room" ];
states.single = "input_boolean.presents_tracking"; states.single = "input_boolean.situation_toggle";
states.double = "input_boolean.situation_toggle";
}; };
}; };

View file

@ -12,8 +12,7 @@ in {
}; };
}; };
environment.systemPackages = with pkgs; environment.systemPackages = with pkgs; [
[
bitwig-studio bitwig-studio
sononym-crawler sononym-crawler
@ -21,7 +20,6 @@ in {
rustup rustup
jetbrains.clion jetbrains.clion
# python # python
python3Full python3Full
#unstable.pypi2nix #unstable.pypi2nix

View file

@ -40,6 +40,8 @@ in {
landingpage = callPackage ./landingpage { }; landingpage = callPackage ./landingpage { };
light-control = callPackage ./light-control { };
bepasty-client-cli = callPackage ./bepasty-client-cli { }; bepasty-client-cli = callPackage ./bepasty-client-cli { };
emo = callPackage ./emoji { }; emo = callPackage ./emoji { };

View file

@ -0,0 +1,24 @@
{ rustPlatform, fetchgit, stdenv, mosquitto, cmake, openssl, ... }:
rustPlatform.buildRustPackage rec {
name = "light-${version}";
version = "0.1.0";
src = fetchgit {
url = "https://git.ingolf-wagner.de/palo/rust-iot.git";
rev = "180200b6085291834079322fc63c5220cac28e43";
sha256 = "0dwf6dvpgrkpgaba4ayb0z6qjk8wjmp38y5viiq7hbjg5wcbr9qf";
};
cargoSha256 = "1yzkbj36sx4vc8dsgcxahrvn33ci2ad1cpd1shcismw33k63rimh";
#verifyCargoDeps = true;
buildInputs = [ mosquitto openssl cmake ];
meta = with stdenv.lib; {
description = "write me";
homepage = "https://foo";
license = licenses.gpl3;
maintainers = [ maintainers.mrVanDalo ];
platforms = platforms.all;
};
}