From a1c36815497a47ee6644ff67409751e3b9802eab Mon Sep 17 00:00:00 2001 From: Malte Brandy Date: Sun, 9 Jan 2022 23:32:32 +0100 Subject: [PATCH] Take down home.maralorn.de --- nixos/machines/fluffy/configuration.nix | 2 +- nixos/machines/hera/configuration.nix | 1 - nixos/roles/home-assistant-local/default.nix | 783 ---------------- .../roles/home-assistant-local/hexa-cards.nix | 35 - .../home-assistant-local/warnwetter.patch | 481 ---------- nixos/roles/home-assistant/default.nix | 853 ++++++++++++++++-- nixos/roles/home-assistant/hexa-cards.nix | 4 +- .../jinja.nix | 0 .../lib.nix | 0 nixos/roles/monitoring/probes.nix | 6 +- nixos/roles/monitoring/prometheus.nix | 9 - nixos/roles/monitoring/rules.yml | 7 - 12 files changed, 779 insertions(+), 1402 deletions(-) delete mode 100644 nixos/roles/home-assistant-local/default.nix delete mode 100644 nixos/roles/home-assistant-local/hexa-cards.nix delete mode 100644 nixos/roles/home-assistant-local/warnwetter.patch rename nixos/roles/{home-assistant-local => home-assistant}/jinja.nix (100%) rename nixos/roles/{home-assistant-local => home-assistant}/lib.nix (100%) diff --git a/nixos/machines/fluffy/configuration.nix b/nixos/machines/fluffy/configuration.nix index 6902f164..d22e277b 100644 --- a/nixos/machines/fluffy/configuration.nix +++ b/nixos/machines/fluffy/configuration.nix @@ -14,7 +14,7 @@ in ../../roles/fonts.nix ../../roles/earlyoom.nix ../../roles/standalone - ../../roles/home-assistant-local + ../../roles/home-assistant ]; fileSystems = diff --git a/nixos/machines/hera/configuration.nix b/nixos/machines/hera/configuration.nix index 85ed565f..87e3dd21 100644 --- a/nixos/machines/hera/configuration.nix +++ b/nixos/machines/hera/configuration.nix @@ -25,7 +25,6 @@ in ../../roles/kassandra-server.nix ../../roles/foundryvtt.nix ../../roles/mailman.nix - ../../roles/home-assistant ../../roles/firefox-sync.nix ../../roles/goatcounter.nix ./web.nix diff --git a/nixos/roles/home-assistant-local/default.nix b/nixos/roles/home-assistant-local/default.nix deleted file mode 100644 index 5f4e7bc8..00000000 --- a/nixos/roles/home-assistant-local/default.nix +++ /dev/null @@ -1,783 +0,0 @@ -{ pkgs, lib, ... }: -let - haLib = import ./lib.nix lib; - inherit (haLib) modules util cards conditions triggers jinja actions tap_actions; - 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 = "Wohnungsmodus"; - name = "flat"; - options = { inherit active vacation; }; - }; - wohnzimmer = { - title = "Wohnzimmermodus"; - name = "wohnzimmer"; - options = { inherit empty heat active force_active; }; - }; - kueche = { - title = "Küchenmodus"; - name = "kueche"; - options = { inherit empty active; }; - }; - schlafzimmer = { - title = "Schlafzimmermodus"; - name = "schlafzimmer"; - options = { inherit empty heat active force_active; }; - }; - }; - fenster = map (name: "binary_sensor.${name}") - [ - "kuechenfenster" - "wohnzimmerfenster" - "schlafzimmerfenster" - "wohnungstuer" - ]; - batteries = map (name: "sensor.${name}") [ - "wohnzimmerfenster_battery" - "thermostat_kueche_battery" - "thermostat_schlafzimmer_battery" - "thermostat_wohnzimmer_battery" - "klimasensor_bad_battery" - "klimasensor_kueche_battery" - "klimasensor_schlafzimmer_battery" - "kuechenfenster_battery" - "pegasus_battery_level" - "schlafzimmerfenster_battery" - "wohnungstuer_battery" - ]; - inherit (import ../../../nix/sources.nix) nixos-unstable; - homeAssistantDir = "/disk/persist/home-assistant"; -in -{ - - disabledModules = [ - "services/misc/home-assistant.nix" - ]; - - imports = [ - (modules.mkModeSwitcher modes.wohnzimmer { }) - (modules.mkModeSwitcher modes.kueche { }) - (modules.mkModeSwitcher modes.schlafzimmer { }) - (modules.mkModeSwitcher modes.flat { }) - "${nixos-unstable}/nixos/modules/services/misc/home-assistant.nix" - ./hexa-cards.nix - ]; - systemd.tmpfiles.rules = [ - "d ${homeAssistantDir} - - - - -" - ]; - - services = { - home-assistant = { - enable = true; - package = pkgs.home-assistant.overrideAttrs ( - oldAttrs: { - doInstallCheck = false; - patches = (oldAttrs.patches or [ ]) ++ [ ./warnwetter.patch ]; - } - ); - configDir = homeAssistantDir; - config = { - shopping_list = { }; - matrix = { - homeserver = "https://matrix.maralorn.de"; - username = "@marabot:maralorn.de"; - password = pkgs.privateValue "" "matrix/marabot-pw"; - }; - notify = [{ platform = "matrix"; default_room = "#fluffy:maralorn.de"; }]; - group = { - wohnzimmer_lights = { - name = "Lichter Wohnzimmer"; - entities = [ "switch.blaue_lichterkette" "switch.lichterkette_schrank" "switch.lichterkette_fernseher" ]; - }; - schlafzimmer_lights = { - name = "Lichter Schlafzimmer"; - entities = [ "switch.lichterkette_schlafzimmer" "switch.weihnachtsstern_schlafzimmer" ]; - }; - }; - homeassistant = pkgs.privateValue { } "homeassistant-home"; - frontend.themes.ourdefault = { - primary-color = "#858EFF"; - }; - automation = [ - { - alias = "Set theme at startup'"; - trigger = { platform = "homeassistant"; event = "start"; }; - action = { service = "frontend.set_theme"; data.name = "ourdefault"; }; - } - { - alias = "Entfeuchtersteuerung Schlafzimmer"; - trigger = [ - { platform = "state"; entity_id = "sensor.schlafzimmer_humidity"; } - { platform = "state"; entity_id = "binary_sensor.schlafzimmerfenster"; } - ]; - action = [{ - choose = [ - { - conditions = [{ - condition = "or"; - conditions = [ - { condition = "numeric_state"; entity_id = "sensor.schlafzimmer_humidity"; below = 65; } - { condition = "state"; entity_id = "binary_sensor.schlafzimmerfenster"; state = "on"; } - ]; - }]; - sequence = { service = "switch.turn_off"; target.entity_id = "switch.luftentfeuchter"; }; - } - { - conditions = [{ condition = "numeric_state"; entity_id = "sensor.schlafzimmer_humidity"; above = 66; }]; - sequence = { service = "switch.turn_on"; target.entity_id = "switch.luftentfeuchter"; }; - } - ]; - }]; - } - { - alias = "Lüftungssteuerung Bad"; - trigger = [{ platform = "state"; entity_id = "sensor.bad_humidity"; }]; - action = [{ - choose = [{ - conditions = [{ condition = "numeric_state"; entity_id = "sensor.bad_humidity"; above = 66; }]; - sequence = { service = "switch.turn_on"; target.entity_id = "switch.lueftung_bad"; }; - } - { - conditions = [{ condition = "numeric_state"; entity_id = "sensor.bad_humidity"; below = 64; }]; - sequence = { service = "switch.turn_off"; target.entity_id = "switch.lueftung_bad"; }; - }]; - }]; - } - { - alias = "Thermostatsteuerung 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"; - target.area_id = "schlafzimmer"; - data = { temperature = 30; hvac_mode = "heat"; }; - }; - }]; - default = { - service = "climate.turn_off"; - target.area_id = "schlafzimmer"; - }; - }]; - } - { - alias = "Thermostatsteuerung Küche"; - trigger = with triggers; [ - (stateTrigger "input_number.target_temperature_kueche") - (stateTrigger "sensor.kueche_temperature") - (stateTrigger "binary_sensor.kuechenfenster") - (stateTrigger "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"; - target.area_id = "kuche"; - data = { temperature = 30; hvac_mode = "heat"; }; - }; - }]; - default = { - service = "climate.turn_off"; - target.area_id = "kuche"; - }; - }]; - } - { - alias = "Thermostatsteuerung Wohnzimmer"; - trigger = with triggers; [ - (stateTrigger "input_number.target_temperature_wohnzimmer") - (stateTrigger "binary_sensor.wohnzimmerfenster") - (stateTrigger "climate.wohnzimmer") - (modeSwitchTrigger modes.flat) - ]; - action = [{ - choose = [{ - conditions = [{ condition = "state"; entity_id = "binary_sensor.wohnzimmerfenster"; state = "off"; } - (conditions.modeIs modes.flat "active")]; - sequence = { - service = "climate.set_temperature"; - target.area_id = "wohnzimmer"; - data = { temperature = "{{ states('input_number.target_temperature_wohnzimmer') | int }}"; hvac_mode = "heat"; }; - }; - }]; - default = { - service = "climate.turn_off"; - target.area_id = "wohnzimmer"; - }; - }]; - } - { - alias = "Küchentemperatur"; - trigger = [ (triggers.modeSwitchTrigger modes.kueche) ]; - action = [{ - service = "input_number.set_value"; - target.entity_id = "input_number.target_temperature_kueche"; - data.value = jinja.if' (jinja.isState (util.modeSelectEntity modes.kueche) "empty") "18" "20.5"; - }]; - } - { - alias = "Wohnzimmertemperatur"; - trigger = [ (triggers.modeSwitchTrigger modes.wohnzimmer) ]; - action = [{ - service = "input_number.set_value"; - target.entity_id = "input_number.target_temperature_wohnzimmer"; - data.value = jinja.if' (jinja.isState (util.modeSelectEntity modes.wohnzimmer) "empty") "18" "24"; - }]; - } - { - alias = "Schlafzimmertemperatur"; - trigger = [ (triggers.modeSwitchTrigger modes.schlafzimmer) ]; - action = [{ - service = "input_number.set_value"; - target.entity_id = "input_number.target_temperature_schlafzimmer"; - data.value = jinja.if' (jinja.isState (util.modeSelectEntity modes.schlafzimmer) "empty") "18" "20.5"; - }]; - } - { - alias = "Wohnzimmerlichter"; - trigger = with triggers; [ - (modeSwitchTrigger modes.wohnzimmer) - (stateTrigger "sun.sun") - ]; - action = [{ - service = jinja.if' - (jinja.or - (jinja.isState (util.modeSelectEntity modes.wohnzimmer) "force_active") - (jinja.and - (jinja.isState (util.modeSelectEntity modes.wohnzimmer) "active") - "state_attr('sun.sun', 'elevation') < 6")) - "homeassistant.turn_on" - "homeassistant.turn_off"; - target.entity_id = "group.wohnzimmer_lights"; - }]; - } - { - alias = "Schlafzimmerlichter"; - trigger = with triggers; [ - (modeSwitchTrigger modes.schlafzimmer) - (stateTrigger "sun.sun") - ]; - action = [{ - service = jinja.if' - (jinja.or - (jinja.isState (util.modeSelectEntity modes.schlafzimmer) "force_active") - (jinja.and - (jinja.isState (util.modeSelectEntity modes.schlafzimmer) "active") - "state_attr('sun.sun', 'elevation') < 6")) - "homeassistant.turn_on" - "homeassistant.turn_off"; - target.entity_id = "group.schlafzimmer_lights"; - }]; - } - { - alias = "Schlafzimmer vorheizen"; - trigger = [{ platform = "time"; at = "19:00:00"; } { platform = "time"; at = "04:00:00"; }]; - condition = [ - (conditions.modeIs modes.schlafzimmer "empty") - (conditions.modeIs modes.flat "active") - ]; - action = [ (actions.setMode modes.schlafzimmer "heat") ]; - } - { - alias = "Schlafzimmer nachts kühl"; - trigger = [{ platform = "time"; at = "01:00:00"; }]; - condition = [ - (conditions.modeIs modes.schlafzimmer "heat") - (conditions.modeIs modes.flat "active") - ]; - action = [ (actions.setMode modes.schlafzimmer "empty") ]; - } - { - alias = "Morgens Licht an"; - trigger = [{ platform = "time"; at = "07:00:00"; }]; - condition = [ - (conditions.modeIs modes.schlafzimmer "heat") - (conditions.modeIs modes.flat "active") - ]; - action = [ (actions.setMode modes.schlafzimmer "force_active") ]; - } - { - alias = "Warnung bei niedrigem Akkustand"; - trigger = map - (limit: { - platform = "numeric_state"; - below = toString limit; - entity_id = batteries; - }) [ 25 20 15 10 5 4 3 2 1 ]; - action = [ (actions.notify "{{ trigger.to_state.name }} ist {{ trigger.to_state.value }}%.") ]; - } - # Warnungen für hohe Luftfeuchtigkeit - ] ++ (map - (minutes: - { - alias = "Warnung bei ${minutes} Minuten offenem Fenster oder offener Tür"; - trigger = map (name: triggers.stateTrigger name // { to = "on"; for = "00:${minutes}:00"; }) fenster; - action = [ (actions.notify "{{ trigger.to_state.name }} ist seit mehr als ${minutes} Minuten offen.") ]; - }) - (map toString [ 10 20 30 40 50 60 ])); - history = { }; - image = { }; - sun = { }; - logbook = { }; - config = { }; - mobile_app = { }; - recorder = { }; - ssdp = { }; - template = [ - { sensor = [{ state = "{% if is_state('switch.luftentfeuchter', 'on') %}1{% else %}0{% endif %}"; name = "Luftentfeuchter"; }]; } - { sensor = [{ state = "{% if is_state('switch.lueftung_bad', 'on') %}1{% else %}0{% endif %}"; name = "Lüftung"; }]; } - { sensor = [{ state = "{% if is_state('binary_sensor.schlafzimmerfenster', 'on') %}1{% else %}0{% endif %}"; name = "Schlafzimmerfenster"; }]; } - { sensor = [{ state = "{% if is_state('binary_sensor.wohnzimmerfenster', 'on') %}1{% else %}0{% endif %}"; name = "Balkontür"; }]; } - { sensor = [{ state = "{% if is_state('binary_sensor.kuechenfenster', 'on') %}1{% else %}0{% endif %}"; name = "Küchenfenster"; }]; } - { sensor = [{ state = "{% if is_state('binary_sensor.wohnungstuer', 'on') %}1{% else %}0{% endif %}"; name = "Wohnungstür"; }]; } - { sensor = [{ state = "{% if is_state('climate.schlafzimmer', 'heat') %}1{% else %}0{% endif %}"; name = "Schlafzimmerheizung"; }]; } - { sensor = [{ state = "{% if is_state('climate.wohnzimmer', 'heat') %}1{% else %}0{% endif %}"; name = "Wohnzimmerheizung"; }]; } - { sensor = [{ state = "{% if is_state('climate.kueche', 'heat') %}1{% else %}0{% endif %}"; name = "Küchenheizung"; }]; } - { sensor = [{ state = "{{ state_attr('climate.wohnzimmer', 'current_temperature') }}"; name = "Temperatur Wohnzimmer"; }]; } - ]; - input_number = { - target_temperature_schlafzimmer = { - name = "Zieltemperatur Schlafzimmer"; - unit_of_measurement = "°C"; - min = "17"; - max = "25"; - step = "0.25"; - }; - target_temperature_wohnzimmer = { - name = "Zieltemperatur Wohnzimmer"; - unit_of_measurement = "°C"; - min = "17"; - max = "25"; - step = "0.25"; - }; - target_temperature_kueche = { - name = "Zieltemperatur Küche"; - unit_of_measurement = "°C"; - min = "17"; - max = "25"; - step = "0.25"; - }; - }; - system_health = { }; - zha = { - device_config = { - "00:12:4b:00:24:c0:ff:52-1".type = "switch"; # Lüftung Bad - "00:12:4b:00:24:c1:00:45-1".type = "switch"; # Blaue Lichterkette - "00:12:4b:00:24:c1:00:1b-1".type = "switch"; # Luftentfeuchter Schlafzimmer - "00:12:4b:00:24:c1:00:05-1".type = "switch"; # Lichterkette Fernseher - "00:12:4b:00:24:c0:ff:16-1".type = "switch"; # Lichterkette Schrank - "00:12:4b:00:24:c0:ff:a8-1".type = "switch"; # Lichterkette Schlafzimmer - "00:12:4b:00:24:c0:ff:ad-1".type = "switch"; # Weihnachtsstern Schlafzimmer - }; - }; - ipp = { }; - brother = { }; - sensor = [ - { - platform = "rmvtransport"; - next_departure = [ - { - station = "3024634"; - } - ]; - } - ]; - weather = [ - { - platform = "warnwetter"; - name = "DWD Darmstadt"; - station_id = "L886"; - } - ]; - http = { - use_x_forwarded_for = true; - trusted_proxies = [ "::1" ]; - }; - prometheus = { - namespace = "hass"; - }; - }; - lovelaceConfig = - let - alertbadges = [ - { - type = "entity-filter"; - entities = fenster; - state_filter = [ "on" ]; - } - { - type = "entity-filter"; - entities = batteries; - state_filter = [{ value = 25; operator = "<"; }]; - } - ]; - badges = - let - badge = mode: - { - type = "state-label"; - entity = util.modeSelectEntity mode; - name = mode.title; - tap_action = tap_actions.cycleMode mode; - }; - in - [ - (badge modes.wohnzimmer) - (badge modes.kueche) - (badge modes.schlafzimmer) - ] ++ alertbadges; - envstack = - { - type = "vertical-stack"; - cards = [ - { - type = "custom:sun-card"; - } - { - type = "weather-forecast"; - entity = "weather.dwd_darmstadt"; - } - { - type = "picture"; - image = "https://www.dwd.de/DWD/wetter/radar/radfilm_hes_akt.gif"; - } - { - type = "custom:rmv-card"; - entity = "sensor.darmstadt_schulstrasse"; - } - ]; - }; - wohnzimmerstack = - { - type = "vertical-stack"; - cards = [ - (cards.modeSwitcher modes.wohnzimmer) - { - type = "custom:mini-graph-card"; - entities = [ - { entity = "sensor.temperatur_wohnzimmer"; name = "Temperatur"; show_fill = false; } - { entity = "input_number.target_temperature_wohnzimmer"; name = "Zieltemperatur"; show_fill = false; } - { entity = "sensor.wohnzimmerheizung"; name = "Heizung"; y_axis = "secondary"; show_fill = true; show_points = false; show_line = false; smoothing = false; } - ]; - show = { - labels = true; - labels_secondary = "hover"; - }; - lower_bound_secondary = 0; - upper_bound_secondary = 1; - hours_to_show = 24; - update_interval = 30; - line_width = 2; - hour24 = true; - decimals = 1; - points_per_hour = 3; - state_map = [ - { value = 0; label = "Aus"; } - { value = 1; label = "An"; } - ]; - } - { - type = "logbook"; - entities = [ (util.modeSelectEntity modes.wohnzimmer) "binary_sensor.wohnzimmerfenster" "switch.lichterkette_fernseher" "switch.lichterkette_schrank" "switch.blaue_lichterkette" ]; - } - ]; - }; - kuechenstack = { - type = "vertical-stack"; - cards = [ - (cards.modeSwitcher modes.kueche) - { - type = "custom:mini-graph-card"; - entities = [ - { entity = "sensor.kueche_humidity"; name = "Luftfeuchtigkeit"; show_fill = false; state_adaptive_color = true; } - { entity = "sensor.kuchenfenster"; name = "Fenster"; color = "#ff0000"; y_axis = "secondary"; show_fill = true; show_points = false; show_line = false; smoothing = false; } - ]; - color_thresholds = [{ value = 0; color = "#009933"; } { value = 64; color = "#ffbf00"; } { value = 66; color = "#ff0000"; }]; - color_thresholds_transition = "hard"; - show = { - labels = true; - labels_secondary = "hover"; - }; - lower_bound_secondary = 0; - upper_bound_secondary = 1; - hour24 = true; - decimals = 1; - points_per_hour = 3; - hours_to_show = 24; - update_interval = 30; - line_width = 2; - state_map = [ - { value = 0; label = "Zu"; } - { value = 1; label = "Auf"; } - ]; - } - { - type = "custom:mini-graph-card"; - entities = [ - { entity = "sensor.kueche_temperature"; name = "Temperatur"; show_fill = false; } - { entity = "input_number.target_temperature_kueche"; name = "Zieltemperatur"; show_fill = false; } - { entity = "sensor.kuchenheizung"; name = "Heizung"; y_axis = "secondary"; show_fill = true; show_points = false; show_line = false; smoothing = false; } - ]; - show = { - labels = true; - labels_secondary = "hover"; - }; - lower_bound_secondary = 0; - upper_bound_secondary = 1; - hours_to_show = 24; - update_interval = 30; - line_width = 2; - hour24 = true; - decimals = 1; - points_per_hour = 3; - state_map = [ - { value = 0; label = "Aus"; } - { value = 1; label = "An"; } - ]; - } - { - type = "logbook"; - entities = [ (util.modeSelectEntity modes.kueche) "climate.kueche" "binary_sensor.kuechenfenster" ]; - } - ]; - }; - schlafzimmerstack = - { - type = "vertical-stack"; - cards = [ - (cards.modeSwitcher modes.schlafzimmer) - { - type = "custom:mini-graph-card"; - entities = [ - { entity = "sensor.schlafzimmer_humidity"; name = "Luftfeuchtigkeit"; show_fill = false; state_adaptive_color = true; } - { entity = "sensor.luftentfeuchter"; name = "Entfeuchter"; color = "#0000ff"; y_axis = "secondary"; show_fill = true; show_points = false; show_line = false; smoothing = false; } - { entity = "sensor.schlafzimmerfenster"; name = "Fenster"; color = "#ff0000"; y_axis = "secondary"; show_fill = true; show_points = false; show_line = false; smoothing = false; } - ]; - color_thresholds = [{ value = 0; color = "#009933"; } { value = 64; color = "#ffbf00"; } { value = 66; color = "#ff0000"; }]; - color_thresholds_transition = "hard"; - show = { - labels = true; - labels_secondary = "hover"; - }; - lower_bound_secondary = 0; - upper_bound_secondary = 1; - hour24 = true; - decimals = 1; - points_per_hour = 3; - hours_to_show = 24; - update_interval = 30; - line_width = 2; - state_map = [ - { value = 0; label = "Aus/Zu"; } - { value = 1; label = "An/Auf"; } - ]; - } - { - type = "custom:mini-graph-card"; - entities = [ - { entity = "sensor.schlafzimmer_temperature"; name = "Temperatur"; show_fill = false; } - { entity = "input_number.target_temperature_schlafzimmer"; name = "Zieltemperatur"; show_fill = false; } - { entity = "sensor.schlafzimmerheizung"; name = "Heizung"; y_axis = "secondary"; show_fill = true; show_points = false; show_line = false; smoothing = false; } - ]; - show = { - labels = true; - labels_secondary = "hover"; - }; - lower_bound_secondary = 0; - upper_bound_secondary = 1; - hours_to_show = 24; - update_interval = 30; - line_width = 2; - hour24 = true; - decimals = 1; - points_per_hour = 3; - state_map = [ - { value = 0; label = "Aus"; } - { value = 1; label = "An"; } - ]; - } - { - type = "logbook"; - entities = [ (util.modeSelectEntity modes.schlafzimmer) "switch.weihnachtsstern_schlafzimmer" "switch.luftentfeuchter" "climate.schlafzimmer" "binary_sensor.schlafzimmerfenster" ]; - } - ]; - }; - badstack = - { - type = "vertical-stack"; - cards = [ - { - type = "glance"; - title = "Bad"; - columns = 4; - show_state = false; - entities = [ ]; - } - { - type = "custom:mini-graph-card"; - entities = [ - { entity = "sensor.bad_humidity"; name = "Luftfeuchtigkeit"; show_fill = false; state_adaptive_color = true; } - { entity = "sensor.luftung"; name = "Lüftung"; color = "#0000ff"; y_axis = "secondary"; show_fill = true; show_points = false; show_line = false; smoothing = false; } - ]; - color_thresholds = [{ value = 0; color = "#009933"; } { value = 64; color = "#ffbf00"; } { value = 66; color = "#ff0000"; }]; - color_thresholds_transition = "hard"; - show = { - labels = true; - labels_secondary = "hover"; - }; - lower_bound_secondary = 0; - upper_bound_secondary = 1; - hour24 = true; - decimals = 1; - points_per_hour = 3; - hours_to_show = 24; - update_interval = 30; - line_width = 2; - state_map = [ - { value = 0; label = "Aus"; } - { value = 1; label = "An"; } - ]; - } - { - type = "custom:mini-graph-card"; - entities = [ - { entity = "sensor.bad_temperature"; name = "Temperatur"; show_fill = false; } - ]; - show = { - labels = true; - labels_secondary = "hover"; - }; - lower_bound_secondary = 0; - upper_bound_secondary = 1; - hours_to_show = 24; - update_interval = 30; - line_width = 2; - hour24 = true; - decimals = 1; - points_per_hour = 3; - state_map = [ - { value = 0; label = "Aus"; } - { value = 1; label = "An"; } - ]; - } - { - type = "logbook"; - entities = [ "switch.lueftung_bad" ]; - } - ]; - }; - flurstack = { - type = "vertical-stack"; - cards = [ - (cards.modeSwitcher modes.flat) - { - type = "custom:mini-graph-card"; - entities = [ - { entity = "sensor.wohnungstur"; name = "Wohnungstür"; color = "#ff0000"; show_fill = true; aggregate_func = "max"; smoothing = false; } - ]; - show = { - labels = true; - }; - lower_bound = 0; - upper_bound = 1; - hour24 = true; - decimals = 1; - points_per_hour = 3; - hours_to_show = 24; - update_interval = 30; - line_width = 2; - state_map = [ - { value = 0; label = "Zu"; } - { value = 1; label = "Auf"; } - ]; - } - { - type = "logbook"; - entities = [ "binary_sensor.wohnungstuer" ]; - } - ]; - }; - in - { - views = [ - { - icon = "mdi:switch"; - badges = alertbadges; - cards = [ - (cards.modeSwitcher modes.wohnzimmer) - (cards.modeSwitcher modes.kueche) - (cards.modeSwitcher modes.schlafzimmer) - (cards.modeSwitcher modes.flat) - ]; - } - { icon = "mdi:sofa"; inherit badges; cards = [ wohnzimmerstack ]; } - { icon = "mdi:countertop"; inherit badges; cards = [ kuechenstack ]; } - { icon = "mdi:bed-king"; inherit badges; cards = [ schlafzimmerstack ]; } - { icon = "mdi:shower-head"; inherit badges; cards = [ badstack ]; } - { icon = "mdi:door-closed"; inherit badges; cards = [ flurstack ]; } - { icon = "mdi:city"; inherit badges; cards = [ envstack ]; } - { icon = "mdi:floor-plan"; badges = alertbadges; cards = [ wohnzimmerstack kuechenstack schlafzimmerstack badstack ]; } - ]; - }; - }; - nginx = { - enable = true; - virtualHosts = { - "home.lo.m-0.eu" = { - serverAliases = [ "home.vpn.m-0.eu" ]; - extraConfig = "proxy_buffering off;"; - locations."/" = { - proxyPass = "http://[::1]:8123"; - proxyWebsockets = true; - }; - locations."/custom/" = { - alias = "/run/hass/"; - }; - }; - "fluffy.lo.m-0.eu" = { - default = true; - locations."/".extraConfig = "return 301 http://home.lo.m-0.eu$request_uri;"; - }; - }; - }; - }; -} diff --git a/nixos/roles/home-assistant-local/hexa-cards.nix b/nixos/roles/home-assistant-local/hexa-cards.nix deleted file mode 100644 index fd53b787..00000000 --- a/nixos/roles/home-assistant-local/hexa-cards.nix +++ /dev/null @@ -1,35 +0,0 @@ -{ pkgs, ... }: -let - nur = import pkgs.sources.hexa-nur-packages { }; - - mkLovelaceModule = name: { - url = "custom/${name}.js?${nur.hassLovelaceModules."${name}".version}"; - type = "module"; - }; -in -{ - systemd.tmpfiles.rules = [ - # Lovelace Cards - "d /run/hass 0700 nginx nginx" - "L+ /run/hass/mini-graph-card.js - - - - ${nur.hassLovelaceModules.mini-graph-card}/mini-graph-card-bundle.js" - "L+ /run/hass/mini-media-player.js - - - - ${nur.hassLovelaceModules.mini-media-player}/mini-media-player-bundle.js" - "L+ /run/hass/multiple-entity-row.js - - - - ${nur.hassLovelaceModules.multiple-entity-row}/multiple-entity-row.js" - "L+ /run/hass/sun-card.js - - - - ${nur.hassLovelaceModules.sun-card}/sun-card.js" - "L+ /run/hass/slider-button-card.js - - - - ${nur.hassLovelaceModules.slider-button-card}/slider-button-card.js" - "L+ /run/hass/rmv-card.js - - - - ${nur.hassLovelaceModules.rmv-card}/rmv-card.js" - "L+ /run/hass/weather-card-chart.js - - - - ${nur.hassLovelaceModules.weather-card-chart}/weather-card-chart.js" - ]; - - services.home-assistant.config.lovelace = { - resources = [ - (mkLovelaceModule "mini-graph-card") - (mkLovelaceModule "mini-media-player") - (mkLovelaceModule "multiple-entity-row") - (mkLovelaceModule "rmv-card") - (mkLovelaceModule "weather-card-chart") - (mkLovelaceModule "sun-card") - (mkLovelaceModule "slider-button-card") - ]; - }; -} - diff --git a/nixos/roles/home-assistant-local/warnwetter.patch b/nixos/roles/home-assistant-local/warnwetter.patch deleted file mode 100644 index b8387863..00000000 --- a/nixos/roles/home-assistant-local/warnwetter.patch +++ /dev/null @@ -1,481 +0,0 @@ -From 92f85569b2ec8beabfb7ea6aea462fba5e5af39d Mon Sep 17 00:00:00 2001 -From: Martin Weinelt -Date: Sat, 20 Mar 2021 17:33:14 +0100 -Subject: [PATCH] warnwetter: init - ---- - CODEOWNERS | 1 + - .../components/warnwetter/__init__.py | 1 + - .../components/warnwetter/manifest.json | 7 + - homeassistant/components/warnwetter/sensor.py | 302 ++++++++++++++++++ - .../components/warnwetter/weather.py | 115 +++++++ - 5 files changed, 426 insertions(+) - create mode 100644 homeassistant/components/warnwetter/__init__.py - create mode 100644 homeassistant/components/warnwetter/manifest.json - create mode 100644 homeassistant/components/warnwetter/sensor.py - create mode 100644 homeassistant/components/warnwetter/weather.py - -diff --git a/CODEOWNERS b/CODEOWNERS -index 0e069f94e7..596beebec8 100644 ---- a/CODEOWNERS -+++ b/CODEOWNERS -@@ -525,6 +525,7 @@ homeassistant/components/vlc_telnet/* @rodripf @dmcc - homeassistant/components/volkszaehler/* @fabaff - homeassistant/components/volumio/* @OnFreund - homeassistant/components/waqi/* @andrey-git -+homeassistant/components/warnwetter/* @mtdcr - homeassistant/components/watson_tts/* @rutkai - homeassistant/components/weather/* @fabaff - homeassistant/components/webostv/* @bendavid -diff --git a/homeassistant/components/warnwetter/__init__.py b/homeassistant/components/warnwetter/__init__.py -new file mode 100644 -index 0000000000..538fc5a2dc ---- /dev/null -+++ b/homeassistant/components/warnwetter/__init__.py -@@ -0,0 +1 @@ -+"""The warnwetter component.""" -diff --git a/homeassistant/components/warnwetter/manifest.json b/homeassistant/components/warnwetter/manifest.json -new file mode 100644 -index 0000000000..cf37e4824e ---- /dev/null -+++ b/homeassistant/components/warnwetter/manifest.json -@@ -0,0 +1,7 @@ -+{ -+ "domain": "warnwetter", -+ "name": "DWD WarnWetter API", -+ "documentation": "https://www.home-assistant.io/integrations/warnwetter", -+ "dependencies": ["weather"], -+ "codeowners": ["@mtdcr"] -+} -diff --git a/homeassistant/components/warnwetter/sensor.py b/homeassistant/components/warnwetter/sensor.py -new file mode 100644 -index 0000000000..65bbe2bdce ---- /dev/null -+++ b/homeassistant/components/warnwetter/sensor.py -@@ -0,0 +1,302 @@ -+"""Sensor platform for data from WarnWetter.""" -+from datetime import datetime, timedelta -+import json -+import logging -+from typing import Any, Dict, Optional, Union -+ -+from aiohttp.hdrs import USER_AGENT -+import pytz -+import requests -+import voluptuous as vol -+ -+from homeassistant.components.weather import ( -+ ATTR_FORECAST_CONDITION, -+ ATTR_FORECAST_PRECIPITATION, -+ ATTR_FORECAST_TEMP, -+ ATTR_FORECAST_TEMP_LOW, -+ ATTR_FORECAST_TIME, -+ ATTR_FORECAST_WIND_BEARING, -+ ATTR_FORECAST_WIND_SPEED, -+) -+from homeassistant.const import ( -+ ATTR_ATTRIBUTION, -+ ATTR_ICON, -+ ATTR_TEMPERATURE, -+ ATTR_TIME, -+ CONF_MONITORED_CONDITIONS, -+ CONF_NAME, -+ DEGREE, -+ DEVICE_CLASS_HUMIDITY, -+ DEVICE_CLASS_PRESSURE, -+ DEVICE_CLASS_TEMPERATURE, -+ DEVICE_CLASS_TIMESTAMP, -+ PERCENTAGE, -+ PRESSURE_HPA, -+ SPEED_KILOMETERS_PER_HOUR, -+ TEMP_CELSIUS, -+ TIME_HOURS, -+ TIME_MINUTES, -+ __version__, -+) -+import homeassistant.helpers.config_validation as cv -+from homeassistant.helpers.entity import Entity -+from homeassistant.util import Throttle -+ -+_LOGGER = logging.getLogger(__name__) -+ -+ATTR_STATION = "station" -+ATTR_UPDATED = "updated" -+ATTRIBUTION = "Data provided by WarnWetter" -+ -+CONF_STATION_ID = "station_id" -+ -+MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=10) -+MIN_TIME_BETWEEN_FORECAST_UPDATES = timedelta(minutes=30) -+ -+ATTR_DEWPOINT = "dewpoint" -+ATTR_HUMIDITY = "humidity" -+ATTR_MAXWIND = "maxwind" -+ATTR_MEANWIND = "meanwind" -+ATTR_PRECIPITATION = "precipitation" -+ATTR_PRECIPITATION3H = "precipitation3h" -+ATTR_PRESSURE = "pressure" -+ATTR_SUNSHINE = "sunshine" -+ATTR_TOTALSNOW = "totalsnow" -+ATTR_WINDDIRECTION = "winddirection" -+ -+SENSOR_TYPES = { -+ ATTR_DEWPOINT: ("Dew Point", DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, 10), -+ ATTR_HUMIDITY: ("Humidity", DEVICE_CLASS_HUMIDITY, PERCENTAGE, 10), -+ ATTR_ICON: ("Icon", None, None, 1), -+ ATTR_MAXWIND: ("Top Wind Speed", None, SPEED_KILOMETERS_PER_HOUR, 10), -+ ATTR_MEANWIND: ("Wind Speed", None, SPEED_KILOMETERS_PER_HOUR, 10), -+ ATTR_PRECIPITATION: ("Precipitation", None, f"mm/{TIME_HOURS}", 10), -+ ATTR_PRECIPITATION3H: ( -+ f"Precipitation 3{TIME_HOURS}", -+ None, -+ f"mm/3{TIME_HOURS}", -+ 10, -+ ), -+ ATTR_PRESSURE: ("Pressure", DEVICE_CLASS_PRESSURE, PRESSURE_HPA, 10), -+ ATTR_SUNSHINE: ("Sun Last Hour", None, TIME_MINUTES, 10), -+ ATTR_TEMPERATURE: ("Temperature", DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, 10), -+ ATTR_TIME: ("Update Time", DEVICE_CLASS_TIMESTAMP, None, 1000), -+ ATTR_TOTALSNOW: ("Snow", None, "cm", 10), -+ ATTR_WINDDIRECTION: ("Wind Bearing", None, DEGREE, 10), -+} -+ -+CONDITION_CLASSES = { -+ "cloudy": {4}, -+ "fog": {5, 6}, -+ "hail": {17, 24, 25}, -+ "lightning": {26}, -+ "lightning-rainy": {27, 28, 29, 30}, -+ "partlycloudy": {2, 3}, -+ "pouring": {18, 19}, -+ "rainy": {7, 8, 9, 10, 11}, -+ "snowy": {14, 15, 16, 22, 23}, -+ "snowy-rainy": {12, 13, 20, 21}, -+ "sunny": {1}, -+ "windy": {31}, -+} -+ -+PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend( -+ { -+ vol.Required(CONF_MONITORED_CONDITIONS, default=["temperature"]): vol.All( -+ cv.ensure_list, [vol.In(SENSOR_TYPES)] -+ ), -+ vol.Required(CONF_STATION_ID): cv.string, -+ vol.Required(CONF_NAME): cv.string, -+ } -+) -+ -+ -+def setup_platform(hass, config, add_entities, discovery_info=None): -+ """Set up the WarnWetter sensor platform.""" -+ name = config[CONF_NAME] -+ probe = WarnWetterMeasurements(config[CONF_STATION_ID]) -+ -+ add_entities( -+ [ -+ WarnWetterSensor(probe, variable, name) -+ for variable in config[CONF_MONITORED_CONDITIONS] -+ ], -+ True, -+ ) -+ -+ -+class WarnWetterSensor(Entity): -+ """Implementation of a WarnWetter sensor.""" -+ -+ def __init__(self, probe, variable, name): -+ """Initialize the sensor.""" -+ self.probe = probe -+ self.client_name = name -+ self.variable = variable -+ -+ @property -+ def unique_id(self) -> Optional[str]: -+ """Return a unique ID.""" -+ return f"{self.probe.station_id}:{self.variable}" -+ -+ @property -+ def name(self) -> Optional[str]: -+ """Return the name of the entity.""" -+ return f"{self.client_name} {SENSOR_TYPES[self.variable][0]}" -+ -+ @property -+ def state(self) -> Union[None, str, int, float]: -+ """Return the state of the entity.""" -+ return self.probe.data.get(self.variable) -+ -+ @property -+ def device_state_attributes(self) -> Optional[Dict[str, Any]]: -+ """Return device specific state attributes.""" -+ return { -+ ATTR_ATTRIBUTION: ATTRIBUTION, -+ ATTR_STATION: self.probe.station_id, -+ ATTR_UPDATED: self.probe.last_update.isoformat(), -+ } -+ -+ @property -+ def device_class(self) -> Optional[str]: -+ """Return the class of this device, from component DEVICE_CLASSES.""" -+ return SENSOR_TYPES[self.variable][1] -+ -+ @property -+ def unit_of_measurement(self) -> Optional[str]: -+ """Return the unit of measurement of this entity, if any.""" -+ return SENSOR_TYPES[self.variable][2] -+ -+ def update(self): -+ """Delegate update to probe.""" -+ self.probe.update() -+ -+ -+class WarnWetterClient: -+ """Base class for static json data.""" -+ -+ API_URL = "https://s3.eu-central-1.amazonaws.com/app-prod-static.warnwetter.de/v16/%s_%s.json" -+ API_HEADERS = {USER_AGENT: f"home-assistant.warnwetter/ {__version__}"} -+ -+ def __init__(self, resource, station_id, interval_h): -+ """Initialize WarnWetterClient.""" -+ self._station_id = station_id -+ self._api_url = self.API_URL % (resource, station_id) -+ self._interval_h = interval_h -+ self.data = {} -+ -+ @property -+ def station_id(self): -+ """Return station ID.""" -+ return self._station_id -+ -+ @property -+ def last_update(self): -+ """Return the timestamp of the most recent data.""" -+ time = self.data.get("time") -+ if time is not None: -+ return datetime.fromtimestamp(int(time)).replace( -+ tzinfo=pytz.timezone("Europe/Berlin") -+ ) -+ -+ def _current_observations(self): -+ """Fetch the latest JSON data.""" -+ try: -+ response = requests.get(self._api_url, headers=self.API_HEADERS, timeout=15) -+ response.raise_for_status() -+ return json.loads(response.content) -+ except requests.exceptions.HTTPError: -+ _LOGGER.error("While fetching data") -+ -+ def _fetch_json(self): -+ """Get the latest data from WarnWetter.""" -+ if self.last_update and ( -+ self.last_update + timedelta(hours=self._interval_h) -+ > datetime.utcnow().replace(tzinfo=pytz.utc) -+ ): -+ return None -+ -+ return self._current_observations() -+ -+ @classmethod -+ def _icons_to_condition(cls, icons): -+ """Convert icon numbers to conditions (first match).""" -+ for icon in icons: -+ for key, val in CONDITION_CLASSES.items(): -+ if icon in val: -+ return key -+ -+ -+class WarnWetterForecast(WarnWetterClient): -+ """A class consuming forecast data.""" -+ -+ def __init__(self, station_id): -+ """Initialize WarnWetterForecast.""" -+ super().__init__("forecast_mosmix", station_id, 4) -+ -+ @Throttle(MIN_TIME_BETWEEN_FORECAST_UPDATES) -+ def update(self): -+ """Update state.""" -+ src = self._fetch_json() -+ if not src: -+ return -+ -+ data = { -+ "forecast": [], -+ } -+ for key, value in src.items(): -+ if key == "days" and isinstance(value, list): -+ for day in value: -+ data["forecast"].append( -+ { -+ ATTR_FORECAST_TIME: day.get("dayDate"), -+ ATTR_FORECAST_TEMP_LOW: day.get("temperatureMin") / 10.0, -+ ATTR_FORECAST_TEMP: day.get("temperatureMax") / 10.0, -+ ATTR_FORECAST_CONDITION: self._condition(day), -+ ATTR_FORECAST_PRECIPITATION: day.get("precipitation") -+ / 10.0, -+ ATTR_FORECAST_WIND_SPEED: day.get("windSpeed") / 10.0, -+ ATTR_FORECAST_WIND_BEARING: day.get("windDirection") / 10.0, -+ # ATTR_FORECAST_WIND_GUST: day.get('windGust') / 10.0, -+ # ATTR_FORECAST_SUNSHINE: day.get('sunshine') / 10.0, -+ } -+ ) -+ -+ elif key == "forecast" and isinstance(value, dict): -+ data["time"] = value.get("start", 0) // 1000 -+ -+ self.data = data -+ -+ @classmethod -+ def _condition(cls, day): -+ keys = ("icon", "icon1", "icon2") -+ icons = filter(None, (day.get(icon) for icon in keys)) -+ return cls._icons_to_condition(icons) -+ -+ -+class WarnWetterMeasurements(WarnWetterClient): -+ """A class consuming measured data.""" -+ -+ def __init__(self, station_id): -+ """Initialize WarnWetterMeasurements.""" -+ super().__init__("current_measurement", station_id, 1) -+ -+ @Throttle(MIN_TIME_BETWEEN_UPDATES) -+ def update(self): -+ """Update state.""" -+ src = self._fetch_json() -+ if not src: -+ return -+ -+ data = {} -+ for key, value in src.items(): -+ spec = SENSOR_TYPES.get(key) -+ if spec: -+ if isinstance(value, int) and value != 32767: -+ if key == "icon": -+ data[key] = self._icons_to_condition([value]) -+ else: -+ data[key] = value / spec[3] -+ -+ self.data = data -diff --git a/homeassistant/components/warnwetter/weather.py b/homeassistant/components/warnwetter/weather.py -new file mode 100644 -index 0000000000..f00155c2bf ---- /dev/null -+++ b/homeassistant/components/warnwetter/weather.py -@@ -0,0 +1,115 @@ -+"""Weather platform for data from WarnWetter.""" -+import logging -+from typing import Optional -+ -+import voluptuous as vol -+ -+from homeassistant.components.weather import ( -+ ATTR_FORECAST_CONDITION, -+ PLATFORM_SCHEMA, -+ WeatherEntity, -+) -+from homeassistant.const import ATTR_ICON, ATTR_TEMPERATURE, CONF_NAME, TEMP_CELSIUS -+from homeassistant.helpers import config_validation as cv -+ -+# Reuse data and API logic from the sensor implementation -+from .sensor import ( -+ ATTR_HUMIDITY, -+ ATTR_MEANWIND, -+ ATTR_PRESSURE, -+ ATTR_WINDDIRECTION, -+ ATTRIBUTION, -+ CONF_STATION_ID, -+ WarnWetterForecast, -+ WarnWetterMeasurements, -+) -+ -+_LOGGER = logging.getLogger(__name__) -+ -+PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( -+ {vol.Required(CONF_NAME): cv.string, vol.Required(CONF_STATION_ID): cv.string} -+) -+ -+ -+def setup_platform(hass, config, add_entities, discovery_info=None): -+ """Set up the WarnWetter weather platform.""" -+ mprobe = WarnWetterMeasurements(config[CONF_STATION_ID]) -+ fprobe = WarnWetterForecast(config[CONF_STATION_ID]) -+ entity = WarnWetterWeather(mprobe, fprobe, config[CONF_NAME]) -+ add_entities([entity], True) -+ -+ -+class WarnWetterWeather(WeatherEntity): -+ """Representation of a weather condition.""" -+ -+ def __init__(self, mprobe, fprobe, stationname): -+ """Initialize WarnWetterWeather.""" -+ self.mprobe = mprobe -+ self.fprobe = fprobe -+ self.stationname = stationname -+ -+ @property -+ def unique_id(self) -> Optional[str]: -+ """Return a unique ID.""" -+ return self.mprobe.station_id -+ -+ @property -+ def name(self) -> Optional[str]: -+ """Return the name of the entity.""" -+ return self.stationname -+ -+ @property -+ def temperature(self): -+ """Return the platform temperature.""" -+ return self.mprobe.data.get(ATTR_TEMPERATURE) -+ -+ @property -+ def temperature_unit(self): -+ """Return the unit of measurement.""" -+ return TEMP_CELSIUS -+ -+ @property -+ def pressure(self): -+ """Return the pressure.""" -+ return self.mprobe.data.get(ATTR_PRESSURE) -+ -+ @property -+ def humidity(self): -+ """Return the humidity.""" -+ return self.mprobe.data.get(ATTR_HUMIDITY) -+ -+ @property -+ def wind_speed(self): -+ """Return the wind speed.""" -+ return self.mprobe.data.get(ATTR_MEANWIND) -+ -+ @property -+ def wind_bearing(self): -+ """Return the wind bearing.""" -+ return self.mprobe.data.get(ATTR_WINDDIRECTION) -+ -+ @property -+ def attribution(self): -+ """Return the attribution.""" -+ return ATTRIBUTION -+ -+ @property -+ def forecast(self): -+ """Return the forecast.""" -+ return self.fprobe.data.get("forecast") -+ -+ @property -+ def condition(self): -+ """Return the current condition.""" -+ icon = self.mprobe.data.get(ATTR_ICON) -+ if icon: -+ return icon -+ -+ forecast = self.forecast -+ if forecast: -+ return forecast[0].get(ATTR_FORECAST_CONDITION) -+ -+ def update(self): -+ """Update state.""" -+ self.mprobe.update() -+ self.fprobe.update() --- -2.30.1 - diff --git a/nixos/roles/home-assistant/default.nix b/nixos/roles/home-assistant/default.nix index 470a9b02..5f4e7bc8 100644 --- a/nixos/roles/home-assistant/default.nix +++ b/nixos/roles/home-assistant/default.nix @@ -1,92 +1,783 @@ -{ pkgs, ... }: -{ - imports = [ ./hexa-cards.nix ]; - - services.home-assistant = { - enable = true; - package = pkgs.home-assistant.overrideAttrs ( - oldAttrs: { - doInstallCheck = false; - patches = (oldAttrs.patches or [ ]) ++ [ ./warnwetter.patch ]; - } - ); - config = { - default_config = { }; - sensor = [ - { - platform = "rmvtransport"; - next_departure = [ - { - station = "3024634"; - } - ]; - } - ]; - weather = [ - { - platform = "warnwetter"; - name = "DWD Darmstadt"; - station_id = "L886"; - } - ]; - http = { - use_x_forwarded_for = true; - trusted_proxies = [ "::1" ]; +{ pkgs, lib, ... }: +let + haLib = import ./lib.nix lib; + inherit (haLib) modules util cards conditions triggers jinja actions tap_actions; + modes = + let + empty = { + icon = util.mkIcon "account-off"; + title = "Leer"; }; - prometheus = { - namespace = "hass"; + 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 = "Wohnungsmodus"; + name = "flat"; + options = { inherit active vacation; }; + }; + wohnzimmer = { + title = "Wohnzimmermodus"; + name = "wohnzimmer"; + options = { inherit empty heat active force_active; }; + }; + kueche = { + title = "Küchenmodus"; + name = "kueche"; + options = { inherit empty active; }; + }; + schlafzimmer = { + title = "Schlafzimmermodus"; + name = "schlafzimmer"; + options = { inherit empty heat active force_active; }; }; }; - lovelaceConfig = { - views = [ + fenster = map (name: "binary_sensor.${name}") + [ + "kuechenfenster" + "wohnzimmerfenster" + "schlafzimmerfenster" + "wohnungstuer" + ]; + batteries = map (name: "sensor.${name}") [ + "wohnzimmerfenster_battery" + "thermostat_kueche_battery" + "thermostat_schlafzimmer_battery" + "thermostat_wohnzimmer_battery" + "klimasensor_bad_battery" + "klimasensor_kueche_battery" + "klimasensor_schlafzimmer_battery" + "kuechenfenster_battery" + "pegasus_battery_level" + "schlafzimmerfenster_battery" + "wohnungstuer_battery" + ]; + inherit (import ../../../nix/sources.nix) nixos-unstable; + homeAssistantDir = "/disk/persist/home-assistant"; +in +{ + + disabledModules = [ + "services/misc/home-assistant.nix" + ]; + + imports = [ + (modules.mkModeSwitcher modes.wohnzimmer { }) + (modules.mkModeSwitcher modes.kueche { }) + (modules.mkModeSwitcher modes.schlafzimmer { }) + (modules.mkModeSwitcher modes.flat { }) + "${nixos-unstable}/nixos/modules/services/misc/home-assistant.nix" + ./hexa-cards.nix + ]; + systemd.tmpfiles.rules = [ + "d ${homeAssistantDir} - - - - -" + ]; + + services = { + home-assistant = { + enable = true; + package = pkgs.home-assistant.overrideAttrs ( + oldAttrs: { + doInstallCheck = false; + patches = (oldAttrs.patches or [ ]) ++ [ ./warnwetter.patch ]; + } + ); + configDir = homeAssistantDir; + config = { + shopping_list = { }; + matrix = { + homeserver = "https://matrix.maralorn.de"; + username = "@marabot:maralorn.de"; + password = pkgs.privateValue "" "matrix/marabot-pw"; + }; + notify = [{ platform = "matrix"; default_room = "#fluffy:maralorn.de"; }]; + group = { + wohnzimmer_lights = { + name = "Lichter Wohnzimmer"; + entities = [ "switch.blaue_lichterkette" "switch.lichterkette_schrank" "switch.lichterkette_fernseher" ]; + }; + schlafzimmer_lights = { + name = "Lichter Schlafzimmer"; + entities = [ "switch.lichterkette_schlafzimmer" "switch.weihnachtsstern_schlafzimmer" ]; + }; + }; + homeassistant = pkgs.privateValue { } "homeassistant-home"; + frontend.themes.ourdefault = { + primary-color = "#858EFF"; + }; + automation = [ + { + alias = "Set theme at startup'"; + trigger = { platform = "homeassistant"; event = "start"; }; + action = { service = "frontend.set_theme"; data.name = "ourdefault"; }; + } + { + alias = "Entfeuchtersteuerung Schlafzimmer"; + trigger = [ + { platform = "state"; entity_id = "sensor.schlafzimmer_humidity"; } + { platform = "state"; entity_id = "binary_sensor.schlafzimmerfenster"; } + ]; + action = [{ + choose = [ + { + conditions = [{ + condition = "or"; + conditions = [ + { condition = "numeric_state"; entity_id = "sensor.schlafzimmer_humidity"; below = 65; } + { condition = "state"; entity_id = "binary_sensor.schlafzimmerfenster"; state = "on"; } + ]; + }]; + sequence = { service = "switch.turn_off"; target.entity_id = "switch.luftentfeuchter"; }; + } + { + conditions = [{ condition = "numeric_state"; entity_id = "sensor.schlafzimmer_humidity"; above = 66; }]; + sequence = { service = "switch.turn_on"; target.entity_id = "switch.luftentfeuchter"; }; + } + ]; + }]; + } + { + alias = "Lüftungssteuerung Bad"; + trigger = [{ platform = "state"; entity_id = "sensor.bad_humidity"; }]; + action = [{ + choose = [{ + conditions = [{ condition = "numeric_state"; entity_id = "sensor.bad_humidity"; above = 66; }]; + sequence = { service = "switch.turn_on"; target.entity_id = "switch.lueftung_bad"; }; + } + { + conditions = [{ condition = "numeric_state"; entity_id = "sensor.bad_humidity"; below = 64; }]; + sequence = { service = "switch.turn_off"; target.entity_id = "switch.lueftung_bad"; }; + }]; + }]; + } + { + alias = "Thermostatsteuerung 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"; + target.area_id = "schlafzimmer"; + data = { temperature = 30; hvac_mode = "heat"; }; + }; + }]; + default = { + service = "climate.turn_off"; + target.area_id = "schlafzimmer"; + }; + }]; + } + { + alias = "Thermostatsteuerung Küche"; + trigger = with triggers; [ + (stateTrigger "input_number.target_temperature_kueche") + (stateTrigger "sensor.kueche_temperature") + (stateTrigger "binary_sensor.kuechenfenster") + (stateTrigger "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"; + target.area_id = "kuche"; + data = { temperature = 30; hvac_mode = "heat"; }; + }; + }]; + default = { + service = "climate.turn_off"; + target.area_id = "kuche"; + }; + }]; + } + { + alias = "Thermostatsteuerung Wohnzimmer"; + trigger = with triggers; [ + (stateTrigger "input_number.target_temperature_wohnzimmer") + (stateTrigger "binary_sensor.wohnzimmerfenster") + (stateTrigger "climate.wohnzimmer") + (modeSwitchTrigger modes.flat) + ]; + action = [{ + choose = [{ + conditions = [{ condition = "state"; entity_id = "binary_sensor.wohnzimmerfenster"; state = "off"; } + (conditions.modeIs modes.flat "active")]; + sequence = { + service = "climate.set_temperature"; + target.area_id = "wohnzimmer"; + data = { temperature = "{{ states('input_number.target_temperature_wohnzimmer') | int }}"; hvac_mode = "heat"; }; + }; + }]; + default = { + service = "climate.turn_off"; + target.area_id = "wohnzimmer"; + }; + }]; + } + { + alias = "Küchentemperatur"; + trigger = [ (triggers.modeSwitchTrigger modes.kueche) ]; + action = [{ + service = "input_number.set_value"; + target.entity_id = "input_number.target_temperature_kueche"; + data.value = jinja.if' (jinja.isState (util.modeSelectEntity modes.kueche) "empty") "18" "20.5"; + }]; + } + { + alias = "Wohnzimmertemperatur"; + trigger = [ (triggers.modeSwitchTrigger modes.wohnzimmer) ]; + action = [{ + service = "input_number.set_value"; + target.entity_id = "input_number.target_temperature_wohnzimmer"; + data.value = jinja.if' (jinja.isState (util.modeSelectEntity modes.wohnzimmer) "empty") "18" "24"; + }]; + } + { + alias = "Schlafzimmertemperatur"; + trigger = [ (triggers.modeSwitchTrigger modes.schlafzimmer) ]; + action = [{ + service = "input_number.set_value"; + target.entity_id = "input_number.target_temperature_schlafzimmer"; + data.value = jinja.if' (jinja.isState (util.modeSelectEntity modes.schlafzimmer) "empty") "18" "20.5"; + }]; + } + { + alias = "Wohnzimmerlichter"; + trigger = with triggers; [ + (modeSwitchTrigger modes.wohnzimmer) + (stateTrigger "sun.sun") + ]; + action = [{ + service = jinja.if' + (jinja.or + (jinja.isState (util.modeSelectEntity modes.wohnzimmer) "force_active") + (jinja.and + (jinja.isState (util.modeSelectEntity modes.wohnzimmer) "active") + "state_attr('sun.sun', 'elevation') < 6")) + "homeassistant.turn_on" + "homeassistant.turn_off"; + target.entity_id = "group.wohnzimmer_lights"; + }]; + } + { + alias = "Schlafzimmerlichter"; + trigger = with triggers; [ + (modeSwitchTrigger modes.schlafzimmer) + (stateTrigger "sun.sun") + ]; + action = [{ + service = jinja.if' + (jinja.or + (jinja.isState (util.modeSelectEntity modes.schlafzimmer) "force_active") + (jinja.and + (jinja.isState (util.modeSelectEntity modes.schlafzimmer) "active") + "state_attr('sun.sun', 'elevation') < 6")) + "homeassistant.turn_on" + "homeassistant.turn_off"; + target.entity_id = "group.schlafzimmer_lights"; + }]; + } + { + alias = "Schlafzimmer vorheizen"; + trigger = [{ platform = "time"; at = "19:00:00"; } { platform = "time"; at = "04:00:00"; }]; + condition = [ + (conditions.modeIs modes.schlafzimmer "empty") + (conditions.modeIs modes.flat "active") + ]; + action = [ (actions.setMode modes.schlafzimmer "heat") ]; + } + { + alias = "Schlafzimmer nachts kühl"; + trigger = [{ platform = "time"; at = "01:00:00"; }]; + condition = [ + (conditions.modeIs modes.schlafzimmer "heat") + (conditions.modeIs modes.flat "active") + ]; + action = [ (actions.setMode modes.schlafzimmer "empty") ]; + } + { + alias = "Morgens Licht an"; + trigger = [{ platform = "time"; at = "07:00:00"; }]; + condition = [ + (conditions.modeIs modes.schlafzimmer "heat") + (conditions.modeIs modes.flat "active") + ]; + action = [ (actions.setMode modes.schlafzimmer "force_active") ]; + } + { + alias = "Warnung bei niedrigem Akkustand"; + trigger = map + (limit: { + platform = "numeric_state"; + below = toString limit; + entity_id = batteries; + }) [ 25 20 15 10 5 4 3 2 1 ]; + action = [ (actions.notify "{{ trigger.to_state.name }} ist {{ trigger.to_state.value }}%.") ]; + } + # Warnungen für hohe Luftfeuchtigkeit + ] ++ (map + (minutes: + { + alias = "Warnung bei ${minutes} Minuten offenem Fenster oder offener Tür"; + trigger = map (name: triggers.stateTrigger name // { to = "on"; for = "00:${minutes}:00"; }) fenster; + action = [ (actions.notify "{{ trigger.to_state.name }} ist seit mehr als ${minutes} Minuten offen.") ]; + }) + (map toString [ 10 20 30 40 50 60 ])); + history = { }; + image = { }; + sun = { }; + logbook = { }; + config = { }; + mobile_app = { }; + recorder = { }; + ssdp = { }; + template = [ + { sensor = [{ state = "{% if is_state('switch.luftentfeuchter', 'on') %}1{% else %}0{% endif %}"; name = "Luftentfeuchter"; }]; } + { sensor = [{ state = "{% if is_state('switch.lueftung_bad', 'on') %}1{% else %}0{% endif %}"; name = "Lüftung"; }]; } + { sensor = [{ state = "{% if is_state('binary_sensor.schlafzimmerfenster', 'on') %}1{% else %}0{% endif %}"; name = "Schlafzimmerfenster"; }]; } + { sensor = [{ state = "{% if is_state('binary_sensor.wohnzimmerfenster', 'on') %}1{% else %}0{% endif %}"; name = "Balkontür"; }]; } + { sensor = [{ state = "{% if is_state('binary_sensor.kuechenfenster', 'on') %}1{% else %}0{% endif %}"; name = "Küchenfenster"; }]; } + { sensor = [{ state = "{% if is_state('binary_sensor.wohnungstuer', 'on') %}1{% else %}0{% endif %}"; name = "Wohnungstür"; }]; } + { sensor = [{ state = "{% if is_state('climate.schlafzimmer', 'heat') %}1{% else %}0{% endif %}"; name = "Schlafzimmerheizung"; }]; } + { sensor = [{ state = "{% if is_state('climate.wohnzimmer', 'heat') %}1{% else %}0{% endif %}"; name = "Wohnzimmerheizung"; }]; } + { sensor = [{ state = "{% if is_state('climate.kueche', 'heat') %}1{% else %}0{% endif %}"; name = "Küchenheizung"; }]; } + { sensor = [{ state = "{{ state_attr('climate.wohnzimmer', 'current_temperature') }}"; name = "Temperatur Wohnzimmer"; }]; } + ]; + input_number = { + target_temperature_schlafzimmer = { + name = "Zieltemperatur Schlafzimmer"; + unit_of_measurement = "°C"; + min = "17"; + max = "25"; + step = "0.25"; + }; + target_temperature_wohnzimmer = { + name = "Zieltemperatur Wohnzimmer"; + unit_of_measurement = "°C"; + min = "17"; + max = "25"; + step = "0.25"; + }; + target_temperature_kueche = { + name = "Zieltemperatur Küche"; + unit_of_measurement = "°C"; + min = "17"; + max = "25"; + step = "0.25"; + }; + }; + system_health = { }; + zha = { + device_config = { + "00:12:4b:00:24:c0:ff:52-1".type = "switch"; # Lüftung Bad + "00:12:4b:00:24:c1:00:45-1".type = "switch"; # Blaue Lichterkette + "00:12:4b:00:24:c1:00:1b-1".type = "switch"; # Luftentfeuchter Schlafzimmer + "00:12:4b:00:24:c1:00:05-1".type = "switch"; # Lichterkette Fernseher + "00:12:4b:00:24:c0:ff:16-1".type = "switch"; # Lichterkette Schrank + "00:12:4b:00:24:c0:ff:a8-1".type = "switch"; # Lichterkette Schlafzimmer + "00:12:4b:00:24:c0:ff:ad-1".type = "switch"; # Weihnachtsstern Schlafzimmer + }; + }; + ipp = { }; + brother = { }; + sensor = [ + { + platform = "rmvtransport"; + next_departure = [ + { + station = "3024634"; + } + ]; + } + ]; + weather = [ + { + platform = "warnwetter"; + name = "DWD Darmstadt"; + station_id = "L886"; + } + ]; + http = { + use_x_forwarded_for = true; + trusted_proxies = [ "::1" ]; + }; + prometheus = { + namespace = "hass"; + }; + }; + lovelaceConfig = + let + alertbadges = [ + { + type = "entity-filter"; + entities = fenster; + state_filter = [ "on" ]; + } + { + type = "entity-filter"; + entities = batteries; + state_filter = [{ value = 25; operator = "<"; }]; + } + ]; + badges = + let + badge = mode: + { + type = "state-label"; + entity = util.modeSelectEntity mode; + name = mode.title; + tap_action = tap_actions.cycleMode mode; + }; + in + [ + (badge modes.wohnzimmer) + (badge modes.kueche) + (badge modes.schlafzimmer) + ] ++ alertbadges; + envstack = + { + type = "vertical-stack"; + cards = [ + { + type = "custom:sun-card"; + } + { + type = "weather-forecast"; + entity = "weather.dwd_darmstadt"; + } + { + type = "picture"; + image = "https://www.dwd.de/DWD/wetter/radar/radfilm_hes_akt.gif"; + } + { + type = "custom:rmv-card"; + entity = "sensor.darmstadt_schulstrasse"; + } + ]; + }; + wohnzimmerstack = + { + type = "vertical-stack"; + cards = [ + (cards.modeSwitcher modes.wohnzimmer) + { + type = "custom:mini-graph-card"; + entities = [ + { entity = "sensor.temperatur_wohnzimmer"; name = "Temperatur"; show_fill = false; } + { entity = "input_number.target_temperature_wohnzimmer"; name = "Zieltemperatur"; show_fill = false; } + { entity = "sensor.wohnzimmerheizung"; name = "Heizung"; y_axis = "secondary"; show_fill = true; show_points = false; show_line = false; smoothing = false; } + ]; + show = { + labels = true; + labels_secondary = "hover"; + }; + lower_bound_secondary = 0; + upper_bound_secondary = 1; + hours_to_show = 24; + update_interval = 30; + line_width = 2; + hour24 = true; + decimals = 1; + points_per_hour = 3; + state_map = [ + { value = 0; label = "Aus"; } + { value = 1; label = "An"; } + ]; + } + { + type = "logbook"; + entities = [ (util.modeSelectEntity modes.wohnzimmer) "binary_sensor.wohnzimmerfenster" "switch.lichterkette_fernseher" "switch.lichterkette_schrank" "switch.blaue_lichterkette" ]; + } + ]; + }; + kuechenstack = { + type = "vertical-stack"; + cards = [ + (cards.modeSwitcher modes.kueche) + { + type = "custom:mini-graph-card"; + entities = [ + { entity = "sensor.kueche_humidity"; name = "Luftfeuchtigkeit"; show_fill = false; state_adaptive_color = true; } + { entity = "sensor.kuchenfenster"; name = "Fenster"; color = "#ff0000"; y_axis = "secondary"; show_fill = true; show_points = false; show_line = false; smoothing = false; } + ]; + color_thresholds = [{ value = 0; color = "#009933"; } { value = 64; color = "#ffbf00"; } { value = 66; color = "#ff0000"; }]; + color_thresholds_transition = "hard"; + show = { + labels = true; + labels_secondary = "hover"; + }; + lower_bound_secondary = 0; + upper_bound_secondary = 1; + hour24 = true; + decimals = 1; + points_per_hour = 3; + hours_to_show = 24; + update_interval = 30; + line_width = 2; + state_map = [ + { value = 0; label = "Zu"; } + { value = 1; label = "Auf"; } + ]; + } + { + type = "custom:mini-graph-card"; + entities = [ + { entity = "sensor.kueche_temperature"; name = "Temperatur"; show_fill = false; } + { entity = "input_number.target_temperature_kueche"; name = "Zieltemperatur"; show_fill = false; } + { entity = "sensor.kuchenheizung"; name = "Heizung"; y_axis = "secondary"; show_fill = true; show_points = false; show_line = false; smoothing = false; } + ]; + show = { + labels = true; + labels_secondary = "hover"; + }; + lower_bound_secondary = 0; + upper_bound_secondary = 1; + hours_to_show = 24; + update_interval = 30; + line_width = 2; + hour24 = true; + decimals = 1; + points_per_hour = 3; + state_map = [ + { value = 0; label = "Aus"; } + { value = 1; label = "An"; } + ]; + } + { + type = "logbook"; + entities = [ (util.modeSelectEntity modes.kueche) "climate.kueche" "binary_sensor.kuechenfenster" ]; + } + ]; + }; + schlafzimmerstack = + { + type = "vertical-stack"; + cards = [ + (cards.modeSwitcher modes.schlafzimmer) + { + type = "custom:mini-graph-card"; + entities = [ + { entity = "sensor.schlafzimmer_humidity"; name = "Luftfeuchtigkeit"; show_fill = false; state_adaptive_color = true; } + { entity = "sensor.luftentfeuchter"; name = "Entfeuchter"; color = "#0000ff"; y_axis = "secondary"; show_fill = true; show_points = false; show_line = false; smoothing = false; } + { entity = "sensor.schlafzimmerfenster"; name = "Fenster"; color = "#ff0000"; y_axis = "secondary"; show_fill = true; show_points = false; show_line = false; smoothing = false; } + ]; + color_thresholds = [{ value = 0; color = "#009933"; } { value = 64; color = "#ffbf00"; } { value = 66; color = "#ff0000"; }]; + color_thresholds_transition = "hard"; + show = { + labels = true; + labels_secondary = "hover"; + }; + lower_bound_secondary = 0; + upper_bound_secondary = 1; + hour24 = true; + decimals = 1; + points_per_hour = 3; + hours_to_show = 24; + update_interval = 30; + line_width = 2; + state_map = [ + { value = 0; label = "Aus/Zu"; } + { value = 1; label = "An/Auf"; } + ]; + } + { + type = "custom:mini-graph-card"; + entities = [ + { entity = "sensor.schlafzimmer_temperature"; name = "Temperatur"; show_fill = false; } + { entity = "input_number.target_temperature_schlafzimmer"; name = "Zieltemperatur"; show_fill = false; } + { entity = "sensor.schlafzimmerheizung"; name = "Heizung"; y_axis = "secondary"; show_fill = true; show_points = false; show_line = false; smoothing = false; } + ]; + show = { + labels = true; + labels_secondary = "hover"; + }; + lower_bound_secondary = 0; + upper_bound_secondary = 1; + hours_to_show = 24; + update_interval = 30; + line_width = 2; + hour24 = true; + decimals = 1; + points_per_hour = 3; + state_map = [ + { value = 0; label = "Aus"; } + { value = 1; label = "An"; } + ]; + } + { + type = "logbook"; + entities = [ (util.modeSelectEntity modes.schlafzimmer) "switch.weihnachtsstern_schlafzimmer" "switch.luftentfeuchter" "climate.schlafzimmer" "binary_sensor.schlafzimmerfenster" ]; + } + ]; + }; + badstack = + { + type = "vertical-stack"; + cards = [ + { + type = "glance"; + title = "Bad"; + columns = 4; + show_state = false; + entities = [ ]; + } + { + type = "custom:mini-graph-card"; + entities = [ + { entity = "sensor.bad_humidity"; name = "Luftfeuchtigkeit"; show_fill = false; state_adaptive_color = true; } + { entity = "sensor.luftung"; name = "Lüftung"; color = "#0000ff"; y_axis = "secondary"; show_fill = true; show_points = false; show_line = false; smoothing = false; } + ]; + color_thresholds = [{ value = 0; color = "#009933"; } { value = 64; color = "#ffbf00"; } { value = 66; color = "#ff0000"; }]; + color_thresholds_transition = "hard"; + show = { + labels = true; + labels_secondary = "hover"; + }; + lower_bound_secondary = 0; + upper_bound_secondary = 1; + hour24 = true; + decimals = 1; + points_per_hour = 3; + hours_to_show = 24; + update_interval = 30; + line_width = 2; + state_map = [ + { value = 0; label = "Aus"; } + { value = 1; label = "An"; } + ]; + } + { + type = "custom:mini-graph-card"; + entities = [ + { entity = "sensor.bad_temperature"; name = "Temperatur"; show_fill = false; } + ]; + show = { + labels = true; + labels_secondary = "hover"; + }; + lower_bound_secondary = 0; + upper_bound_secondary = 1; + hours_to_show = 24; + update_interval = 30; + line_width = 2; + hour24 = true; + decimals = 1; + points_per_hour = 3; + state_map = [ + { value = 0; label = "Aus"; } + { value = 1; label = "An"; } + ]; + } + { + type = "logbook"; + entities = [ "switch.lueftung_bad" ]; + } + ]; + }; + flurstack = { + type = "vertical-stack"; + cards = [ + (cards.modeSwitcher modes.flat) + { + type = "custom:mini-graph-card"; + entities = [ + { entity = "sensor.wohnungstur"; name = "Wohnungstür"; color = "#ff0000"; show_fill = true; aggregate_func = "max"; smoothing = false; } + ]; + show = { + labels = true; + }; + lower_bound = 0; + upper_bound = 1; + hour24 = true; + decimals = 1; + points_per_hour = 3; + hours_to_show = 24; + update_interval = 30; + line_width = 2; + state_map = [ + { value = 0; label = "Zu"; } + { value = 1; label = "Auf"; } + ]; + } + { + type = "logbook"; + entities = [ "binary_sensor.wohnungstuer" ]; + } + ]; + }; + in { - cards = [ + views = [ { - type = "weather-forecast"; - entity = "weather.dwd_darmstadt"; - } - { - type = "custom:sun-card"; - } - { - type = "picture"; - image = "https://www.dwd.de/DWD/wetter/radar/radfilm_hes_akt.gif"; - } - { - type = "history-graph"; - entities = [ - "sensor.pegasus_battery_level" - "sensor.pegasus_battery_state" - "sensor.kalliope_battery_level" - "sensor.kalliope_battery_state" + icon = "mdi:switch"; + badges = alertbadges; + cards = [ + (cards.modeSwitcher modes.wohnzimmer) + (cards.modeSwitcher modes.kueche) + (cards.modeSwitcher modes.schlafzimmer) + (cards.modeSwitcher modes.flat) ]; } - { - type = "custom:rmv-card"; - entity = "sensor.darmstadt_schulstrasse"; - } + { icon = "mdi:sofa"; inherit badges; cards = [ wohnzimmerstack ]; } + { icon = "mdi:countertop"; inherit badges; cards = [ kuechenstack ]; } + { icon = "mdi:bed-king"; inherit badges; cards = [ schlafzimmerstack ]; } + { icon = "mdi:shower-head"; inherit badges; cards = [ badstack ]; } + { icon = "mdi:door-closed"; inherit badges; cards = [ flurstack ]; } + { icon = "mdi:city"; inherit badges; cards = [ envstack ]; } + { icon = "mdi:floor-plan"; badges = alertbadges; cards = [ wohnzimmerstack kuechenstack schlafzimmerstack badstack ]; } ]; - } - ]; + }; + }; + nginx = { + enable = true; + virtualHosts = { + "home.lo.m-0.eu" = { + serverAliases = [ "home.vpn.m-0.eu" ]; + extraConfig = "proxy_buffering off;"; + locations."/" = { + proxyPass = "http://[::1]:8123"; + proxyWebsockets = true; + }; + locations."/custom/" = { + alias = "/run/hass/"; + }; + }; + "fluffy.lo.m-0.eu" = { + default = true; + locations."/".extraConfig = "return 301 http://home.lo.m-0.eu$request_uri;"; + }; + }; }; }; - - services.nginx = { - virtualHosts."home.maralorn.de" = { - enableACME = true; - forceSSL = true; - locations."/" = { - proxyPass = "http://[::1]:8123"; - proxyWebsockets = true; - }; - locations."/custom/" = { - alias = "/run/hass/"; - }; - extraConfig = '' - proxy_buffering off; - ''; - - }; - }; - } diff --git a/nixos/roles/home-assistant/hexa-cards.nix b/nixos/roles/home-assistant/hexa-cards.nix index ac69eff4..fd53b787 100644 --- a/nixos/roles/home-assistant/hexa-cards.nix +++ b/nixos/roles/home-assistant/hexa-cards.nix @@ -15,7 +15,7 @@ in "L+ /run/hass/mini-media-player.js - - - - ${nur.hassLovelaceModules.mini-media-player}/mini-media-player-bundle.js" "L+ /run/hass/multiple-entity-row.js - - - - ${nur.hassLovelaceModules.multiple-entity-row}/multiple-entity-row.js" "L+ /run/hass/sun-card.js - - - - ${nur.hassLovelaceModules.sun-card}/sun-card.js" - "L+ /run/hass/swipe-navigation.js - - - - ${nur.hassLovelaceModules.swipe-navigation}/swipe-navigation.js" + "L+ /run/hass/slider-button-card.js - - - - ${nur.hassLovelaceModules.slider-button-card}/slider-button-card.js" "L+ /run/hass/rmv-card.js - - - - ${nur.hassLovelaceModules.rmv-card}/rmv-card.js" "L+ /run/hass/weather-card-chart.js - - - - ${nur.hassLovelaceModules.weather-card-chart}/weather-card-chart.js" ]; @@ -28,7 +28,7 @@ in (mkLovelaceModule "rmv-card") (mkLovelaceModule "weather-card-chart") (mkLovelaceModule "sun-card") - (mkLovelaceModule "swipe-navigation") + (mkLovelaceModule "slider-button-card") ]; }; } diff --git a/nixos/roles/home-assistant-local/jinja.nix b/nixos/roles/home-assistant/jinja.nix similarity index 100% rename from nixos/roles/home-assistant-local/jinja.nix rename to nixos/roles/home-assistant/jinja.nix diff --git a/nixos/roles/home-assistant-local/lib.nix b/nixos/roles/home-assistant/lib.nix similarity index 100% rename from nixos/roles/home-assistant-local/lib.nix rename to nixos/roles/home-assistant/lib.nix diff --git a/nixos/roles/monitoring/probes.nix b/nixos/roles/monitoring/probes.nix index 6b712ab3..4d74006b 100644 --- a/nixos/roles/monitoring/probes.nix +++ b/nixos/roles/monitoring/probes.nix @@ -43,7 +43,10 @@ in "bach.vocalensemble-darmstadt.de:25" "hera.m-0.eu:25" ]) - (makeProbe "http" [ "hera.m-0.eu:80" ]) + (makeProbe "http" [ + "hera.m-0.eu:80" + "home.vpn.m-0.eu" + ]) (makeProbe "https" [ "https://hera.m-0.eu" @@ -58,7 +61,6 @@ in "https://element.maralorn.de" "https://firefox-sync.maralorn.de" "https://fdroid.maralorn.de/index-v1.json" - "https://home.maralorn.de" "https://matrix.maralorn.de" "https://monitoring.maralorn.de" "https://rpg.maralorn.de" diff --git a/nixos/roles/monitoring/prometheus.nix b/nixos/roles/monitoring/prometheus.nix index 2248ede2..e0e51f46 100644 --- a/nixos/roles/monitoring/prometheus.nix +++ b/nixos/roles/monitoring/prometheus.nix @@ -15,15 +15,6 @@ let alert_type = "infrastructure"; in [ - { - job_name = "home-assistant"; - metrics_path = "/api/prometheus"; - bearer_token = pkgs.privateValue "" "home-assistant"; - scheme = "https"; - static_configs = [{ - targets = [ "home.maralorn.de" ]; - }]; - } ( let name = "matrix-synapse"; in diff --git a/nixos/roles/monitoring/rules.yml b/nixos/roles/monitoring/rules.yml index 1659d971..6a78b779 100644 --- a/nixos/roles/monitoring/rules.yml +++ b/nixos/roles/monitoring/rules.yml @@ -50,10 +50,3 @@ groups: severity: warning annotations: description: "mail queue {{ $labels.queue }} of {{ $labels.name }} has accumulated a waiting time of {{ $value | humanizeDuration }}." - - alert: battery_low - expr: label_replace(hass_battery_percent, "device", "$1", "entity", "sensor.(.*)_battery_level") < 25 and on(device) label_replace(hass_binary_sensor_state{entity=~"binary_sensor.*_is_charging"}, "device", "$1", "entity", "binary_sensor.(.*)_is_charging") and on(entity) hass_entity_available == 1 - for: 5m - labels: - severity: warning - annotations: - description: "battery on {{ $labels.device }} is at {{ $value }} while discharging."