{ pkgs, config, lib, ... }: let unstablePkgs = import { }; in { imports = [ #./home-assistant/mpd.nix #./home-assistant/timer.nix ./home-assistant/chaospott.nix ./home-assistant/kodi.nix ./home-assistant/mqtt.nix ./home-assistant/sonoff.nix ./home-assistant/stocks.nix ./home-assistant/weather.nix ./home-assistant/workday.nix ./home-assistant/zigbee2mqtt.nix ]; services.homeAssistantConfig = { # turn on to edit GUI # lovelace = {}; homeassistant = { latitude = 51.444847; longitude = 6.967006; elevation = 116; auth_providers = [{ type = "trusted_networks"; trusted_networks = [ config.module.cluster.services.tinc."private".networkSubnet ]; }]; }; prometheus.namespace = "hass"; # manual state # ------------ 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" ]; }; 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" "outside" ]; }; 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) ++ [ "outside" ]; 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) ++ (switchPresentsTracking "outside"); # 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 { alias = "set essential"; trigger = { platform = "state"; entity_id = "input_boolean.situation_toggle"; from = "off"; to = "on"; }; action = { service = "input_select.select_option"; data = { entity_id = "input_select.situation"; option = "essential"; }; }; } { alias = "unset essential"; trigger = { platform = "state"; entity_id = "input_boolean.situation_toggle"; from = "on"; to = "off"; }; action = { service = "input_select.select_option"; data = { entity_id = "input_select.situation"; option = "dark"; }; }; } ]; group = let create_room = { name, description }: { "${name}" = { name = "${description}"; view = false; control = "hidden"; entities = [ ]; }; "${name}_lights" = { name = "${description} Beleuchtung"; view = false; entities = [ ]; }; "${name}_present" = { control = "hidden"; name = "${description} Anwesend"; view = false; entities = [ ]; }; "${name}_essential" = { control = "hidden"; name = "${description} Minimale Beleuchtung"; view = false; entities = [ ]; }; "${name}_bright" = { control = "hidden"; name = "${description} Tages Beleuchtung"; view = false; entities = [ ]; }; "view_${name}" = { name = description; view = true; entities = [ "group.${name}" "group.${name}_bright" "group.${name}_essential" "group.${name}_lights" "group.${name}_present" ]; }; }; create_rooms = rooms: lib.foldr (a: b: a // b) { } (map create_room rooms); # rooms # ----- in (create_rooms [ { name = "floor_room"; description = "Flur"; } { name = "bed_room"; description = "Schlafzimmer"; } { name = "living_room"; description = "Wohnzimmer"; } { name = "kitchen_room"; description = "Küche"; } { name = "bath_room"; description = "Klo"; } ]) // { view_rooms = { name = "Räume"; view = true; entities = [ "group.today" "group.floor_room" "group.bed_room" "group.living_room" "group.kitchen_room" "group.bath_room" "group.outside" ]; }; all_room_presents = { name = "Alle Anwesenheits Raum Sensoren"; entities = [ "group.floor_room_present" "group.bed_room_present" "group.living_room_present" "group.kitchen_room_present" "group.bath_room_present" "group.outside" ]; }; outside = { control = "hidden"; name = "Draußen"; view = false; entities = [ ]; }; # overview # -------- all_sensors = { name = "Alle Sensoren"; control = "hidden"; }; today = { control = "hidden"; name = "Today"; view = false; entities = [ "input_select.situation" "input_select.room_present" "input_boolean.presents_tracking" ]; }; view_overview = { name = "Übersicht"; view = true; entities = [ "group.today" ]; }; # other stuff # ----------- tv = { name = "TV"; view = false; }; all_lights = { name = "Alle Lampen"; view = false; }; unknown = { control = "hidden"; name = "Not Used"; view = false; entities = [ ]; }; }; script.turn_all_off.sequence = [ ]; script.turn_all_lights_off.sequence = [ ]; }; services.home-assistant = { enable = true; package = unstablePkgs.home-assistant.override { python3 = unstablePkgs.python37; extraPackages = python: [ # todo : check which is still needed python.netdisco python.xmltodict python.mpd2 # for mqtt python.hbmqtt python.paho-mqtt # needed for platform workday #(python.buildPythonPackage rec { # pname = "holidays"; # version = "0.9.10"; # src = python.fetchPypi { # inherit pname version; # sha256 = # "9f06d143eb708e8732230260636938f2f57114e94defd8fa2082408e0d422d6f"; # }; # doCheck = false; # buildInputs = [ pkgs.dateutils ]; # propagatedBuildInputs = [ python."python-dateutil" python."six" ]; # meta = with pkgs.stdenv.lib; { # homepage = "https://github.com/dr-prodigy/python-holidays"; # license = licenses.mit; # description = "Generate and work with holidays in Python"; # maintainers = with maintainers; [ mrVanDalo ]; # }; #}) ]; }; }; # host nginx setup services.nginx = { enable = true; recommendedProxySettings = true; virtualHosts = { "iot.pepe.private" = { serverAliases = [ "hass.pepe.private" "home.pepe.private" ]; locations."/" = { proxyPass = "http://pepe.private:8123"; }; }; }; }; }