From 172c75baa8199bba33e1994c4d028a8d4e05dc5a Mon Sep 17 00:00:00 2001 From: Jakob Lechner Date: Thu, 30 Nov 2023 10:13:47 +0000 Subject: [PATCH] Add home-assistant --- hosts/iron/ports.nix | 2 + hosts/iron/secrets.yaml | 7 +- hosts/iron/services/default.nix | 2 + hosts/iron/services/esphome/default.nix | 50 +++++++ .../esphome/devices/yeelight-meteorite.yaml | 89 ++++++++++++ hosts/iron/services/home-assistant.nix | 132 ++++++++++++++++++ 6 files changed, 279 insertions(+), 3 deletions(-) create mode 100644 hosts/iron/services/esphome/default.nix create mode 100644 hosts/iron/services/esphome/devices/yeelight-meteorite.yaml create mode 100644 hosts/iron/services/home-assistant.nix diff --git a/hosts/iron/ports.nix b/hosts/iron/ports.nix index 4eac1eb..b9fe77b 100644 --- a/hosts/iron/ports.nix +++ b/hosts/iron/ports.nix @@ -1,6 +1,8 @@ { lib, custom-utils, ... }: custom-utils.validatePortAttrset { + esphome.tcp = 6052; + home-assistant.tcp = 8123; jellyfin.tcp = 8096; matrix-synapse.tcp = 8008; navidrome.tcp = 4533; diff --git a/hosts/iron/secrets.yaml b/hosts/iron/secrets.yaml index c0cad33..1ac3813 100644 --- a/hosts/iron/secrets.yaml +++ b/hosts/iron/secrets.yaml @@ -9,6 +9,7 @@ dkim-keys: radicale-htpasswd: ENC[AES256_GCM,data:Q0WnleP9I4xozsL/H+5oV3Ag7khfalV40A6ub+DA07U8UKna3/ju533RmjWOnETzSNa6XK140nfCcfGZCiqGyF9tfuuXcKFu+j4=,iv:87PSvHyKF7QUQZmEuxM+IT0VKSGnS0MjoUmCqJ+6tzI=,tag:yrP3TgxE8aSZf0MrCF9dsQ==,type:str] synapse-turn-shared-secret: ENC[AES256_GCM,data:Q1XRds3Zud1kYkvD6s9WUzP+kNDNsxB5SHd6oCAaLCHhHhYENSAYTZOF+rGjCPNyKFL0e/A=,iv:zScRQrz+pXHNUh/BGOaV+TVnDR3wu1Z/UO1zXarKwtA=,tag:ckpVziE+yb0FjctcT7tAkg==,type:str] rmfakecloud: ENC[AES256_GCM,data:ktKBKb6cRv1VF8tRvXIpxIy9hPinVPKK05mgvYzz18PEdcrCLpldm5xf7ffHtY5XzDOAMXDCiz6x4xyv7071frrF0spOEPnIzVhxwG8H2Ck=,iv:qJdHjv0RziAs4G9UGeRwGQ4GE5kaObJWpIYWpRKhr9c=,tag:PXgvU1hZK/gvWGyFJaHekg==,type:str] +esphome: ENC[AES256_GCM,data:BvQ5vUP2i/OtuKIWfZyHpZFjYK8E9AG0t1R6p7z4wbcyKmJLnvIvqwj5jgVZWfMKbnkX1fh3RJJtgW98tMkW8HhUGjapKsMsroHvnUNfGIDm04NKWI+Fql6EH0vlQAnNZ7kHDAOPMjCJSGBAKg9H7jagbJCXSZByP0l6ducippyFpmx9qsMETsjG4xFqG4/QsgjlkI2kF3rgvpYxCYc8exrHlIc6Ci9BPzC8SVjz8WQd1Mvq9N0elcVifWcr,iv:Tm3Uiod6rL92RNyds+dOs4kN6nNRZvfXNbB4CwXK0lY=,tag:QJ0OKxaBaXN7/LzsmkSn1A==,type:str] sops: kms: [] gcp_kms: [] @@ -24,8 +25,8 @@ sops: TjdZRldhSzVtMkVoTzY1NjdGbCswRVUK0pi+8UuLqRmytcR2ikxOAM02iccl8P1y ixv0PKPLd+vQ23QeeQy/TfoGx16XttaDUnUrPLZR3TUKtAcld8+m6w== -----END AGE ENCRYPTED FILE----- - lastmodified: "2023-11-15T22:26:56Z" - mac: ENC[AES256_GCM,data:cFb5BwiT6WIAuPmYD0wQZLy+DyS7+uZk81aNWDV8stnOdBEBJoiG+jVG585u1Cl3cp2d2TWTlg5lYJSIbk0Rxp3/typg+C31VxTKffTf6iS3r93mAUVsrN51QPn8DIJ7G6Rrf3JCNI+nTJ4LKH472QMd7da6atYb1TmFmLEmWrs=,iv:sqqQz38aThr4M8nxHu/VdvCkbQOZYUzJAtVvnBjNNsU=,tag:HiXOZBjznGrgKo6MjH51FQ==,type:str] + lastmodified: "2023-11-30T00:55:33Z" + mac: ENC[AES256_GCM,data:90gY/9S+ci/8EQphHnxpJ2t38L6Ii3oSwfYsGkKQdkUC7lsGrUmmHS7SGy9wVKOhkiQBQGgQLRqEnXE8c/fmXaQ8kg4wA7LFhnEGykyVZ1tQqYsLaXr+HIlFZG6APahYX5geKUokc3L0k/Mxe0cT9XGTxkD8EYdKgFnZCISJ1WY=,iv:axggMJ0cYCuXT2FTjJRUntgUafO7bwh7aq9btdU+Vyc=,tag:fW9PAKwVcZSR1akRgXr1tA==,type:str] pgp: - created_at: "2023-05-02T19:30:42Z" enc: | @@ -39,4 +40,4 @@ sops: -----END PGP MESSAGE----- fp: 66FB54F6081375106EEBF651A222365EB448F934 unencrypted_suffix: _unencrypted - version: 3.7.3 + version: 3.8.1 diff --git a/hosts/iron/services/default.nix b/hosts/iron/services/default.nix index 1fb2cfc..1e4c1dd 100644 --- a/hosts/iron/services/default.nix +++ b/hosts/iron/services/default.nix @@ -2,6 +2,8 @@ imports = [ ./dnsmasq.nix ./dyndns.nix + ./home-assistant.nix + ./esphome ./jellyfin.nix ./mail.nix ./matrix diff --git a/hosts/iron/services/esphome/default.nix b/hosts/iron/services/esphome/default.nix new file mode 100644 index 0000000..34f83c2 --- /dev/null +++ b/hosts/iron/services/esphome/default.nix @@ -0,0 +1,50 @@ +args@{ lib, pkgs, config, custom-utils, ... }: +let + ports = import ../../ports.nix args; + cfg = config.services.esphome; + stateDir = "/var/lib/esphome"; + devices = [ + ./yeelight-meteorite.yaml + ]; + cfgdir = pkgs.stdenvNoCC.mkDerivation { + name = "esphome-config"; + src = ./devices; + dontBuild = true; + installPhase = '' + mkdir $out + cp -r * $out + ln -snf "${config.sops.secrets.esphome.path}" "$out/secrets.yaml" + ln -snf "${stateDir}/.esphome" "$out/.esphome" + ln -snf "${stateDir}/.gitignore" "$out/.gitignore" + ''; + }; + esphomeParams = + if cfg.enableUnixSocket + then "--socket /run/esphome/esphome.sock" + else "--address ${cfg.address} --port ${toString cfg.port}"; +in +{ + sops.secrets.esphome = { + sopsFile = ../../secrets.yaml; + owner = "esphome"; + group = "esphome"; + mode = "0400"; + }; + + services.esphome = { + enable = true; + address = "127.0.0.1"; + port = ports.esphome.tcp; + }; + + systemd.services.esphome.serviceConfig = { + WorkingDirectory = lib.mkForce cfgdir; + ExecStart = lib.mkForce "${cfg.package}/bin/esphome dashboard ${esphomeParams} ${cfgdir}"; + }; + + system.activationScripts.esphome-custom.text = '' + mkdir -p "${stateDir}/.esphome" + touch "${stateDir}/.gitignore" + chown esphome:esphome "${stateDir}/.esphome" "${stateDir}/.gitignore" + ''; +} diff --git a/hosts/iron/services/esphome/devices/yeelight-meteorite.yaml b/hosts/iron/services/esphome/devices/yeelight-meteorite.yaml new file mode 100644 index 0000000..e8d14d5 --- /dev/null +++ b/hosts/iron/services/esphome/devices/yeelight-meteorite.yaml @@ -0,0 +1,89 @@ +esphome: + name: "yeelight-meteorite" + friendly_name: "Yeelight Meteorite" + +esp32: + board: esp32dev + framework: + type: esp-idf + sdkconfig_options: + CONFIG_FREERTOS_UNICORE: y + advanced: + ignore_efuse_mac_crc: true + +logger: + +api: + encryption: + key: !secret apikey_yeelight_meteorite + +ota: + password: !secret otapass_yeelight_meteorite + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + +output: + - platform: ledc + pin: GPIO19 + id: output_warm + power_supply: power +# min_power: 0.13 + max_power: 0.82 + - platform: ledc + pin: GPIO21 + id: output_cold + power_supply: power +# min_power: 0.13 + max_power: 0.82 + + - platform: ledc + pin: GPIO23 + id: output_nightlight + power_supply: power + + - platform: ledc + pin: GPIO33 + id: output_red + power_supply: power + - platform: ledc + pin: GPIO26 + id: output_green + power_supply: power + - platform: ledc + pin: GPIO27 + id: output_blue + power_supply: power + +power_supply: + - id: power + pin: GPIO22 + enable_time: 0s + keep_on_time: 0s + +light: + - platform: monochromatic + name: "${name} night light" + id: night_light + output: output_nightlight + gamma_correct: 0 + on_turn_on: + - light.turn_off: ceiling_light + - platform: cwww + name: "${name} ceiling light" + id: ceiling_light + cold_white: output_cold + warm_white: output_warm + cold_white_color_temperature: 6500 K + warm_white_color_temperature: 2700 K + constant_brightness: true + gamma_correct: 0 + on_turn_on: + - light.turn_off: night_light + - platform: rgb + name: "${name} ambient light" + red: output_red + green: output_green + blue: output_blue + gamma_correct: 0 diff --git a/hosts/iron/services/home-assistant.nix b/hosts/iron/services/home-assistant.nix new file mode 100644 index 0000000..3bcf765 --- /dev/null +++ b/hosts/iron/services/home-assistant.nix @@ -0,0 +1,132 @@ +args@{ lib, pkgs, config, custom-utils, ... }: +let + ports = import ../ports.nix args; +in +{ + services.home-assistant = { + enable = true; + lovelaceConfig = { }; + extraComponents = [ + # See https://www.home-assistant.io/integrations + "esphome" + ]; + lovelaceConfigWritable = false; + configWritable = false; + config = { + http = { + server_host = [ "127.0.0.1" ]; + server_port = ports.home-assistant.tcp; + use_x_forwarded_for = true; + trusted_proxies = [ "127.0.0.1" ]; + }; + homeassistant = { + unit_system = "metric"; + time_zone = "Europe/Berlin"; + temperature_unit = "C"; + longitude = config.location.longitude; + latitude = config.location.latitude; + }; + default_config = { }; + "automation solar_color_temperature" = { + variables = { + target_lights = "!input target_lights"; + target_on_lights = "'{{states.light | selectattr(''state'',''eq'',''on'') | selectattr(''attributes.supported_color_modes'',''match'',\".*color_temp.*\") | map(attribute=''entity_id'') | select(\"in\",target_lights) | list }}'"; + min_color_temp = "!input min_color_temp"; + max_color_temp = "!input max_color_temp"; + color_temp = "{{ [([(((4791.67 - 3290.66/(1 + 0.222 * ([([0,state_attr(''sun.sun'', ''elevation'')]|max),90]|min**0.81))))|int),max_color_temp]|max),min_color_temp]|min}}"; + }; + trigger = [ + { + platform = "state"; + entity_id = "!input target_lights"; + to = "on"; + id = "single_light_on"; + } + { + platform = "state"; + entity_id = [ "sun.sun" ]; + attribute = "elevation"; + id = "elevation_change"; + } + { + platform = "time_pattern"; + minutes = "5"; + id = "every_5_min"; + } + ]; + condition = [ + { + condition = "template"; + value_template = "{{ (states.light | selectattr(''state'',''eq'',''on'') | map(attribute=''entity_id'') | list)!= [] }}"; + } + ]; + action = [ + { + choose = [ + { + conditions = [ + { + condition = "trigger"; + id = "single_light_on"; + } + ]; + sequence = [ + { + service = "light.turn_on"; + data.kelvin = "{{color_temp}}"; + target.entity_id = "{{trigger.entity_id}}"; + } + { + delay.milliseconds = 500; + } + { + choose.default = "!input brightness_action"; + } + ]; + } + { + conditions = [ + { + condition = "or"; + conditions = [ + { + condition = "trigger"; + id = "every_5_min"; + } + { + condition = "trigger"; + id = "elevation_change"; + } + ]; + } + ]; + sequence = [ + { + service = "light.turn_on"; + data = { + kelvin = "{{color_temp}}"; + transition = 5; + }; + target.entity_id = "{{target_on_lights}}"; + } + ]; + } + ]; + } + ]; + mode = "parallel"; + }; + }; + }; + + services.nginx.virtualHosts."hass.jalr.de" = { + enableACME = true; + forceSSL = true; + kTLS = true; + locations."/" = { + proxyPass = "http://127.0.0.1:${toString ports.home-assistant.tcp}/"; + recommendedProxySettings = true; + proxyWebsockets = true; + }; + }; +}