From d76dd45c67b8fa40d332c63ec82593745527c108 Mon Sep 17 00:00:00 2001 From: Ingolf Wagner Date: Mon, 1 Jun 2020 04:12:15 +0200 Subject: [PATCH] home-assistant: add light-control --- assets/light-control.json | 265 +++++++++++ configs/pepe/home-assistant.nix | 420 +----------------- configs/pepe/home-assistant/light-control.nix | 14 + configs/pepe/home-assistant/mqtt.nix | 4 +- .../home-assistant/zigbee2mqtt/buttons.nix | 9 +- configs/workout/packages.nix | 24 +- pkgs/default.nix | 2 + pkgs/light-control/default.nix | 24 + 8 files changed, 342 insertions(+), 420 deletions(-) create mode 100644 assets/light-control.json create mode 100644 configs/pepe/home-assistant/light-control.nix create mode 100644 pkgs/light-control/default.nix diff --git a/assets/light-control.json b/assets/light-control.json new file mode 100644 index 0000000..a1160ef --- /dev/null +++ b/assets/light-control.json @@ -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" + } + } + ] +} diff --git a/configs/pepe/home-assistant.nix b/configs/pepe/home-assistant.nix index 3747b30..aaa1f4d 100644 --- a/configs/pepe/home-assistant.nix +++ b/configs/pepe/home-assistant.nix @@ -5,6 +5,7 @@ in { imports = [ #./home-assistant/mpd.nix #./home-assistant/timer.nix + ./home-assistant/light-control.nix ./home-assistant/chaospott.nix ./home-assistant/kodi.nix ./home-assistant/mqtt.nix @@ -38,397 +39,15 @@ in { # ------------ input_select.situation = { icon = "mdi:brightness-auto"; - options = [ - # 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" - ]; + options = [ "default" "night" "outside" ]; }; input_boolean.situation_toggle.icon = "mdi:toggle-switch"; - # enable presents_tracking - 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 ++ [ + automation = [ # control situation with buttons { - alias = "set essential"; + alias = "set night scene"; trigger = { platform = "state"; entity_id = "input_boolean.situation_toggle"; @@ -436,15 +55,15 @@ in { to = "on"; }; action = { - service = "input_select.select_option"; + service = "mqtt.publish"; data = { - entity_id = "input_select.situation"; - option = "essential"; + topic = "control/lights/set"; + payload = builtins.toJSON { scene = "night"; }; }; }; } { - alias = "unset essential"; + alias = "set default scene"; trigger = { platform = "state"; entity_id = "input_boolean.situation_toggle"; @@ -452,34 +71,37 @@ in { to = "off"; }; action = { - service = "input_select.select_option"; + service = "mqtt.publish"; data = { - entity_id = "input_select.situation"; - option = "dark"; + topic = "control/lights/set"; + payload = builtins.toJSON { scene = "default"; }; }; }; } - - # trigger outside { - alias = "set essential"; + alias = "set outside scene"; trigger = [ { platform = "state"; - entity_id = "group.outside"; + # todo : groups are not working right now + entity_id = "binary_sensor.door_sensor_2"; from = "off"; to = "on"; } { platform = "state"; - entity_id = "group.outside"; + # todo : groups are not working right now + entity_id = "binary_sensor.door_sensor_2"; from = "on"; to = "off"; } ]; action = { - service = "input_boolean.turn_off"; - data.entity_id = "input_boolean.presents_tracking"; + service = "mqtt.publish"; + data = { + topic = "control/lights/set"; + payload = builtins.toJSON { scene = "outside"; }; + }; }; } diff --git a/configs/pepe/home-assistant/light-control.nix b/configs/pepe/home-assistant/light-control.nix new file mode 100644 index 0000000..9a40080 --- /dev/null +++ b/configs/pepe/home-assistant/light-control.nix @@ -0,0 +1,14 @@ +{ pkgs, lib, config, ... }: +let + lightControlConfig = pkgs.writeText "light-control.json" + (lib.fileContents ); + +in { + systemd.services."light-control" = { + wantedBy = [ "multi-user.target" ]; + environment = { RUST_LOG = "rust_iot=trace"; }; + script = '' + ${pkgs.light-control}/bin/rust-iot ${lightControlConfig} + ''; + }; +} diff --git a/configs/pepe/home-assistant/mqtt.nix b/configs/pepe/home-assistant/mqtt.nix index c58a91b..f82584b 100644 --- a/configs/pepe/home-assistant/mqtt.nix +++ b/configs/pepe/home-assistant/mqtt.nix @@ -21,8 +21,8 @@ password = lib.fileContents ; acl = [ "topic readwrite #" ]; }; - rust-iot = { - password = "password" + lightcontrol = { + password = "password"; acl = [ "topic readwrite #" ]; }; }; diff --git a/configs/pepe/home-assistant/zigbee2mqtt/buttons.nix b/configs/pepe/home-assistant/zigbee2mqtt/buttons.nix index 1537691..f445db7 100644 --- a/configs/pepe/home-assistant/zigbee2mqtt/buttons.nix +++ b/configs/pepe/home-assistant/zigbee2mqtt/buttons.nix @@ -12,20 +12,17 @@ let "button_1" = { id = "0x00158d0002b04f65"; groups = [ "living_room" ]; - states.single = "input_boolean.presents_tracking"; - states.double = "input_boolean.situation_toggle"; + states.single = "input_boolean.situation_toggle"; }; "button_2" = { id = "0x00158d0002b04f09"; groups = [ "bed_room" ]; - states.single = "input_boolean.presents_tracking"; - states.double = "input_boolean.situation_toggle"; + states.single = "input_boolean.situation_toggle"; }; "button_3" = { id = "0x00158d0002b00e04"; groups = [ "bed_room" ]; - states.single = "input_boolean.presents_tracking"; - states.double = "input_boolean.situation_toggle"; + states.single = "input_boolean.situation_toggle"; }; }; diff --git a/configs/workout/packages.nix b/configs/workout/packages.nix index 5aaafb3..83adf6e 100644 --- a/configs/workout/packages.nix +++ b/configs/workout/packages.nix @@ -12,22 +12,20 @@ in { }; }; - environment.systemPackages = with pkgs; - [ - bitwig-studio - sononym-crawler + environment.systemPackages = with pkgs; [ + bitwig-studio + sononym-crawler - # rust development environment - rustup - jetbrains.clion + # rust development environment + rustup + jetbrains.clion + # python + python3Full + #unstable.pypi2nix - # python - python3Full - #unstable.pypi2nix + #nur.repos.mic92.nixos-shell - #nur.repos.mic92.nixos-shell - - ]; + ]; } diff --git a/pkgs/default.nix b/pkgs/default.nix index 49a3ae8..c423522 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -40,6 +40,8 @@ in { landingpage = callPackage ./landingpage { }; + light-control = callPackage ./light-control { }; + bepasty-client-cli = callPackage ./bepasty-client-cli { }; emo = callPackage ./emoji { }; diff --git a/pkgs/light-control/default.nix b/pkgs/light-control/default.nix new file mode 100644 index 0000000..c4e6d04 --- /dev/null +++ b/pkgs/light-control/default.nix @@ -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; + }; +}