From 82d5688ef7cb2441fa3ac228d508ffab79f5c05a Mon Sep 17 00:00:00 2001 From: Malte Brandy Date: Thu, 23 Dec 2021 18:02:52 +0100 Subject: [PATCH] Add home-assistant functions --- nixos/roles/home-assistant-local/default.nix | 80 ++++++++++++++------ nixos/roles/home-assistant-local/jinja.nix | 12 +++ nixos/roles/home-assistant-local/lib.nix | 68 +++++++++++++++++ 3 files changed, 135 insertions(+), 25 deletions(-) create mode 100644 nixos/roles/home-assistant-local/jinja.nix create mode 100644 nixos/roles/home-assistant-local/lib.nix diff --git a/nixos/roles/home-assistant-local/default.nix b/nixos/roles/home-assistant-local/default.nix index 6cbf5099..a682b518 100644 --- a/nixos/roles/home-assistant-local/default.nix +++ b/nixos/roles/home-assistant-local/default.nix @@ -1,5 +1,37 @@ -{ pkgs, ... }: +{ pkgs, lib, ... }: let + haLib = import ./lib.nix lib; + inherit (haLib) modules util cards conditions triggers; + modes = + let + empty = { + icon = util.mkIcon "account-off"; + title = "Leer"; + }; + heat = { + icon = util.mkIcon "radiator"; + title = "Heizen"; + }; + active = { + icon = util.mkIcon "account"; + title = "Aktiv"; + }; + force-active = { + icon = util.mkIcon "lightbulb-on"; + title = "Alles An"; + }; + vacation = { + icon = util.mkIcon "airplane"; + title = "Urlaub"; + }; + in + { + flat = { + title = "Wohnung"; + name = "flat"; + options = { inherit active vacation; }; + }; + }; inherit (import ../../../nix/sources.nix) nixos-unstable; homeAssistantDir = "/disk/persist/home-assistant"; in @@ -10,6 +42,7 @@ in ]; imports = [ + (modules.mkModeSwitcher modes.flat { }) "${nixos-unstable}/nixos/modules/services/misc/home-assistant.nix" ./hexa-cards.nix ]; @@ -39,16 +72,6 @@ in }; }; homeassistant = pkgs.privateValue { } "homeassistant-home"; - logger = { - default = "info"; - logs = { - "homeassistant.core" = "debug"; - "homeassistant.components.zha" = "debug"; - zigpy = "debug"; - zigpy_znp = "debug"; - zhaquirks = "debug"; - }; - }; frontend.themes.ourdefault = { primary-color = "#858EFF"; }; @@ -99,17 +122,19 @@ in } { alias = "Thermostatsteuerung Schlafzimmer"; - trigger = [ - { platform = "state"; entity_id = "input_number.target_temperature_schlafzimmer"; } - { platform = "state"; entity_id = "sensor.schlafzimmer_temperature"; } - { platform = "state"; entity_id = "binary_sensor.schlafzimmerfenster"; } - { platform = "state"; entity_id = "climate.schlafzimmer"; } + trigger = with triggers; [ + (stateTrigger "input_number.target_temperature_schlafzimmer") + (stateTrigger "sensor.schlafzimmer_temperature") + (stateTrigger "binary_sensor.schlafzimmerfenster") + (stateTrigger "climate.schlafzimmer") + (modeSwitchTrigger modes.flat) ]; action = [{ choose = [{ conditions = [ { condition = "numeric_state"; entity_id = "sensor.schlafzimmer_temperature"; below = "input_number.target_temperature_schlafzimmer"; } { condition = "state"; entity_id = "binary_sensor.schlafzimmerfenster"; state = "off"; } + (conditions.modeIs modes.flat "active") ]; sequence = { service = "climate.set_temperature"; @@ -125,17 +150,19 @@ in } { alias = "Thermostatsteuerung Küche"; - trigger = [ + trigger = with triggers; [ { platform = "state"; entity_id = "input_number.target_temperature_kueche"; } { platform = "state"; entity_id = "sensor.kueche_temperature"; } { platform = "state"; entity_id = "binary_sensor.kuechenfenster"; } { platform = "state"; entity_id = "climate.kueche"; } + (modeSwitchTrigger modes.flat) ]; action = [{ choose = [{ conditions = [ { condition = "numeric_state"; entity_id = "sensor.kueche_temperature"; below = "input_number.target_temperature_kueche"; } { condition = "state"; entity_id = "binary_sensor.kuechenfenster"; state = "off"; } + (conditions.modeIs modes.flat "active") ]; sequence = { service = "climate.set_temperature"; @@ -151,14 +178,16 @@ in } { alias = "Thermostatsteuerung Wohnzimmer"; - trigger = [ + trigger = with triggers; [ { platform = "state"; entity_id = "input_number.target_temperature_wohnzimmer"; } { platform = "state"; entity_id = "binary_sensor.wohnzimmerfenster"; } { platform = "state"; entity_id = "climate.wohnzimmer"; } + (modeSwitchTrigger modes.flat) ]; action = [{ choose = [{ - conditions = [{ condition = "state"; entity_id = "binary_sensor.wohnzimmerfenster"; state = "off"; }]; + conditions = [{ condition = "state"; entity_id = "binary_sensor.wohnzimmerfenster"; state = "off"; } + (conditions.modeIs modes.flat "active")]; sequence = { service = "climate.set_temperature"; target.area_id = "wohnzimmer"; @@ -179,7 +208,7 @@ in target.entity_id = "input_number.target_temperature_kueche"; data.value = '' {% if is_state('input_select.scene_kueche', 'empty') %} - 17 + 18 {% else %} 21 {% endif %} @@ -194,7 +223,7 @@ in target.entity_id = "input_number.target_temperature_wohnzimmer"; data.value = '' {% if is_state('input_select.scene_wohnzimmer', 'empty') %} - 17 + 18 {% else %} 24 {% endif %} @@ -209,7 +238,7 @@ in target.entity_id = "input_number.target_temperature_schlafzimmer"; data.value = '' {% if is_state('input_select.scene_schlafzimmer', 'empty') %} - 17 + 18 {% else %} 21 {% endif %} @@ -245,19 +274,19 @@ in { alias = "Schlafzimmer vorheizen"; trigger = [{ platform = "time"; at = "19:00:00"; } { platform = "time"; at = "04:00:00"; }]; - condition = [{ condition = "state"; entity_id = "input_select.scene_schlafzimmer"; state = "empty"; }]; + condition = [{ condition = "state"; entity_id = "input_select.scene_schlafzimmer"; state = "empty"; } (conditions.modeIs modes.flat "active")]; action = [{ service = "input_select.select_option"; data.option = "heat"; entity_id = "input_select.scene_schlafzimmer"; }]; } { alias = "Schlafzimmer nachts kühl"; trigger = [{ platform = "time"; at = "01:00:00"; }]; - condition = [{ condition = "state"; entity_id = "input_select.scene_schlafzimmer"; state = "heat"; }]; + condition = [{ condition = "state"; entity_id = "input_select.scene_schlafzimmer"; state = "heat"; } (conditions.modeIs modes.flat "active")]; action = [{ service = "input_select.select_option"; data.option = "empty"; entity_id = "input_select.scene_schlafzimmer"; }]; } { alias = "Morgens Licht an"; trigger = [{ platform = "time"; at = "08:00:00"; }]; - condition = [{ condition = "state"; entity_id = "input_select.scene_schlafzimmer"; state = "heat"; }]; + condition = [{ condition = "state"; entity_id = "input_select.scene_schlafzimmer"; state = "heat"; } (conditions.modeIs modes.flat "active")]; action = [{ service = "input_select.select_option"; data.option = "active"; entity_id = "input_select.scene_schlafzimmer"; }]; } # Warnung für offene Fenster oder Türen @@ -662,6 +691,7 @@ in flurstack = { type = "vertical-stack"; cards = [ + (cards.modeSwitcher modes.flat) { type = "custom:mini-graph-card"; entities = [ diff --git a/nixos/roles/home-assistant-local/jinja.nix b/nixos/roles/home-assistant-local/jinja.nix new file mode 100644 index 00000000..d6a3edb1 --- /dev/null +++ b/nixos/roles/home-assistant-local/jinja.nix @@ -0,0 +1,12 @@ +lib: rec { + case = default: attrs: '' + {% if ${lib.concatStringsSep "\n{% elseif " (lib.mapAttrsToList (condition: result: "${condition} %}\n ${result}") attrs)} + {% else %} + ${default} + {% endif %} + ''; + if' = condition: ifTrue: ifFalse: case ifFalse { ${condition} = ifTrue; }; + or = lhs: rhs: "(${lhs} or ${rhs})"; + and = lhs: rhs: "(${lhs} and ${rhs})"; + isState = entity: state: "is_state('${entity}','${state}')"; +} diff --git a/nixos/roles/home-assistant-local/lib.nix b/nixos/roles/home-assistant-local/lib.nix new file mode 100644 index 00000000..3c14383d --- /dev/null +++ b/nixos/roles/home-assistant-local/lib.nix @@ -0,0 +1,68 @@ +lib: +let + # name refers to an internal name + # title refers to a human readable name + # mode = { name, title, options = { : { title , icon } } } +in +rec { + jinja = import ./jinja.nix lib; + tap_actions = { + setMode = mode: option: { + action = "call-service"; + service = "input_select.select_option"; + service_data = { entity_id = util.modeSelectEntity mode; inherit option; }; + }; + }; + util = rec { + mkIcon = name: "mdi:${name}"; + mkMode = name: options: { inherit name options; }; + modeSelectEntity = mode: "input_select.${modeSelectName mode}"; + modeSelectName = mode: "mode_${mode.name}"; + modeBinarySensorName = mode: option: "${modeSelectName mode}_is_${option}"; + modeBinarySensorEntity = mode: option: "binary_sensor.${modeBinarySensorName mode option}"; + }; + triggers = rec { + stateTrigger = entity: { platform = "state"; entity_id = entity; }; + modeSwitchTrigger = mode: stateTrigger (util.modeSelectEntity mode); + }; + conditions = { + modeIs = mode: option: { condition = "state"; entity_id = util.modeSelectEntity mode; state = option; }; + }; + modules = rec { + mkHAConfig = attrs: { + services.home-assistant.config = attrs; + }; + mkModeSwitcher = mode: attrs: { ... }: + let + options = builtins.attrNames mode.options; + in + mkHAConfig { + input_select.${util.modeSelectName mode} = { inherit options; } // attrs; + template = builtins.map (templates.binarySensorForMode mode) options; + }; + }; + cards = { + modeSwitcher = mode: + let + mkEntity = optionName: option: + { + entity = util.modeBinarySensorEntity mode optionName; + name = option.title; + inherit (option) icon; + tap_action = tap_actions.setMode mode optionName; + }; + in + { + type = "glance"; + inherit (mode) title; + columns = builtins.length (builtins.attrNames mode.options); + show_state = false; + entities = lib.mapAttrsToList mkEntity mode.options; + }; + }; + templates = rec { + binarySensor = state: attrs: { binary_sensor = [ ({ inherit state; } // attrs) ]; }; + binarySensorFromCondition = condition: binarySensor (jinja.if' condition "1" "0"); + binarySensorForMode = mode: option: binarySensorFromCondition (jinja.isState (util.modeSelectEntity mode) option) { name = util.modeBinarySensorName mode option; }; + }; +}