From 83c75dee75dc0f5dd26f5eb4041438a0031ec906 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/dnsmasq.nix | 5 +- hosts/iron/services/esphome/default.nix | 70 ++++++++++++ hosts/iron/services/esphome/devices/README.md | 12 +++ .../devices/chinafrickeldeckenleuchte.yaml | 44 ++++++++ .../devices/eingang-deckenleuchte.yaml | 24 +++++ .../devices/led-panel-schreibtisch.yaml | 59 ++++++++++ .../esphome/devices/yeelight-meteorite.yaml | 96 +++++++++++++++++ hosts/iron/services/home-assistant.nix | 102 ++++++++++++++++++ pkgs/default.nix | 3 + .../circadian_lighting.nix | 19 ++++ 13 files changed, 441 insertions(+), 4 deletions(-) create mode 100644 hosts/iron/services/esphome/default.nix create mode 100644 hosts/iron/services/esphome/devices/README.md create mode 100644 hosts/iron/services/esphome/devices/chinafrickeldeckenleuchte.yaml create mode 100644 hosts/iron/services/esphome/devices/eingang-deckenleuchte.yaml create mode 100644 hosts/iron/services/esphome/devices/led-panel-schreibtisch.yaml create mode 100644 hosts/iron/services/esphome/devices/yeelight-meteorite.yaml create mode 100644 hosts/iron/services/home-assistant.nix create mode 100644 pkgs/home-assistant-custom-components/circadian_lighting.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..2b1706b 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:RykNTaLwXD9pvCepexxPv0TY5Hk1c1JQWerehuaTSfYIPJ8/tXWQ8XJWoHs7+wJZCV+7nXY9ZpWDlWdWUqDfKcHnv62BdwnuW5L9baY1PLq4WISpRKsND99euDLuicvbpej5GBwMhxr7XFKVkAlbEpAEj1c+qAv27gEg0tC4Jkn8nl4+qi+f9TeRXv1miijAFgxqZ+C+9nQ2NM6zeMXgD5NS0PqzgP43NhTrC8s0QYOb9Gry78jEbzEfvquFJ+CmqEwoL3NZ/ynQQlO3IUfOwmJlMyhEVmetPs8sMyMIbmHrLlfCKb9KFUjZCJAsJhSq2QgByPB6mHotd4Nu9P/1xkzuy+hQImrlIK7Sz4bo1tp9CM5czRwU6GOmG7haC2UebSSGzhp8cHnhDtMbKbyqDLdTBsn9ilI/lvzNbuvyvygcaos5a8o3nO9MtVrgvUKowIb5wOHxvUrr8fXIAyrLEAdMVNl0UtPlJSBCRe8WFulhqGrgGECzt/seBoRZvzsgwXnSZgbYGTjIA7UR//0J4p877HFQTdEiUVEudK9vp3u8/GoDKThIy3A=,iv:mVL86y99wQ2M8kum2IotrYQbqswcTgcglCQSegoYzFI=,tag:ZhW1eCd6B59eNhipTb/2Mg==,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-12-13T21:51:59Z" + mac: ENC[AES256_GCM,data:EIJ8A537IFnubCKL1QCZbhnsPG1qCDt2jGiYCaOolTvxzuB30xxSpqaUE++9wZopQbKn8MAayaO06zaj4D0ix+CrYokPqpI73/XC+rO33YZMQzb72fbRoy9oPQdb3LSlqNyNP7Gu7/Q/ByfEacLlg+wD+e6mmueHmnmF7wt1lZ8=,iv:vhF/KmogG7jJgl1jflvdlRwuA569xL0DEjjozXrqQRI=,tag:NyYvcbqFWwq89IJ0NeF3oA==,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/dnsmasq.nix b/hosts/iron/services/dnsmasq.nix index 94a148e..3233865 100644 --- a/hosts/iron/services/dnsmasq.nix +++ b/hosts/iron/services/dnsmasq.nix @@ -13,7 +13,10 @@ in ]; interface = "lo"; expand-hosts = true; - domain = "lan.bw.jalr.de"; + domain = [ + "lan.bw.jalr.de,192.168.42.0/24" + "iot.bw.jalr.de,10.20.0.0/22" + ]; dhcp-range = [ "192.168.42.20,192.168.42.254,4h" "10.20.0.20,10.20.3.254,12h" diff --git a/hosts/iron/services/esphome/default.nix b/hosts/iron/services/esphome/default.nix new file mode 100644 index 0000000..03599a9 --- /dev/null +++ b/hosts/iron/services/esphome/default.nix @@ -0,0 +1,70 @@ +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; + /* + package = pkgs.esphome.overrideAttrs (o: o // { + propagatedBuildInputs = (o.propagatedBuildInputs or []) ++ [ + pkgs.gcc + ]; + makeWrapperArgs = [ + # platformio is used in esphomeyaml/platformio_api.py + # esptool is used in esphomeyaml/__main__.py + # git is used in esphomeyaml/writer.py + "--prefix PATH : ${lib.makeBinPath (with pkgs; [ platformio esptool_3 git gcc])}" + "--set ESPHOME_USE_SUBPROCESS ''" + ]; + }); + */ + }; + + systemd.services.esphome.serviceConfig = { + WorkingDirectory = lib.mkForce cfgdir; + ExecStart = lib.mkForce "${cfg.package}/bin/esphome dashboard ${esphomeParams} ${cfgdir}"; + /* + Environment = [ + "NIX_LD=/nix/store/qn3ggz5sf3hkjs2c797xf7nan3amdxmp-glibc-2.38-27/lib/ld-linux-x86-64.so.2" + "NIX_LD_LIBRARY_PATH=/nix/store/myw67gkgayf3s2mniij7zwd79lxy8v0k-gcc-12.3.0-lib/lib" + ]; + */ + }; + + 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/README.md b/hosts/iron/services/esphome/devices/README.md new file mode 100644 index 0000000..4b38621 --- /dev/null +++ b/hosts/iron/services/esphome/devices/README.md @@ -0,0 +1,12 @@ +## Generating passwords + + +Generate home-assistant API key +```sh +dd if=/dev/random bs=32 count=1 | base64 +``` + +Generate OTA secret +```sh +pwgen 14 1 +``` diff --git a/hosts/iron/services/esphome/devices/chinafrickeldeckenleuchte.yaml b/hosts/iron/services/esphome/devices/chinafrickeldeckenleuchte.yaml new file mode 100644 index 0000000..371d9fe --- /dev/null +++ b/hosts/iron/services/esphome/devices/chinafrickeldeckenleuchte.yaml @@ -0,0 +1,44 @@ +esp8266: + board: d1_mini + framework: + version: recommended + +logger: + +bp5758d: + data_pin: GPIO4 + clock_pin: GPIO5 + +output: + - platform: bp5758d + id: output_ch1 + channel: 1 + current: 20 + - platform: bp5758d + id: output_ch2 + channel: 2 + current: 20 + - platform: bp5758d + id: output_ch3 + channel: 3 + current: 20 + - platform: bp5758d + id: output_ch4 + channel: 4 + current: 20 + - platform: bp5758d + id: output_ch5 + channel: 5 + current: 20 + +light: + - platform: rgbww + name: Deckenleuchte + id: ceiling_light + red: output_ch1 + green: output_ch2 + blue: output_ch3 + warm_white: output_ch4 + cold_white: output_ch5 + warm_white_color_temperature: 2700 K + cold_white_color_temperature: 6500 K diff --git a/hosts/iron/services/esphome/devices/eingang-deckenleuchte.yaml b/hosts/iron/services/esphome/devices/eingang-deckenleuchte.yaml new file mode 100644 index 0000000..b752069 --- /dev/null +++ b/hosts/iron/services/esphome/devices/eingang-deckenleuchte.yaml @@ -0,0 +1,24 @@ +<<: !include chinafrickeldeckenleuchte.yaml + +esphome: + name: "eingang-deckenleuchte" + friendly_name: "Eingang Deckenleuchte" + on_boot: + then: + - light.turn_on: + id: ceiling_light + brightness: 50% + color_brightness: 0% + color_temperature: 2700 K + +api: + encryption: + key: !secret apikey_eingang_deckenleuchte + +ota: + password: !secret otapass_eingang_deckenleuchte + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de diff --git a/hosts/iron/services/esphome/devices/led-panel-schreibtisch.yaml b/hosts/iron/services/esphome/devices/led-panel-schreibtisch.yaml new file mode 100644 index 0000000..a58e7e5 --- /dev/null +++ b/hosts/iron/services/esphome/devices/led-panel-schreibtisch.yaml @@ -0,0 +1,59 @@ +esphome: + name: "led-panel-schreibtisch" + friendly_name: "LED Panel Schreibtisch" + on_boot: + then: + - light.turn_on: + id: panel + brightness: 50% + color_brightness: 0% + color_temperature: 2700 K + +api: + encryption: + key: !secret apikey_panel_schreibtisch + +ota: + password: !secret otapass_panel_schreibtisch + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de + +esp32: + board: wemos_d1_uno32 + framework: + type: arduino + version: recommended + platform_version: 5.4.0 + +logger: + +output: + - platform: ledc + pin: GPIO16 + id: output_warm + power_supply: power + frequency: 2kHz + - platform: ledc + pin: GPIO17 + id: output_cold + power_supply: power + frequency: 2kHz + +power_supply: + - id: power + pin: GPIO25 + enable_time: 0s + keep_on_time: 0s + +light: + - platform: cwww + name: "panel" + id: panel + cold_white: output_cold + warm_white: output_warm + cold_white_color_temperature: 6500 K + warm_white_color_temperature: 2700 K + gamma_correct: 0 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..617aae1 --- /dev/null +++ b/hosts/iron/services/esphome/devices/yeelight-meteorite.yaml @@ -0,0 +1,96 @@ +esphome: + name: "yeelight-meteorite" + friendly_name: "Yeelight Meteorite" + on_boot: + then: + - light.turn_on: + id: ceiling_light + brightness: 50% + color_temperature: 2700 K + +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 + domain: .iot.bw.jalr.de + +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: "night light" + id: night_light + output: output_nightlight + gamma_correct: 0 + on_turn_on: + - light.turn_off: ceiling_light + - platform: cwww + 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: "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..a3d1633 --- /dev/null +++ b/hosts/iron/services/home-assistant.nix @@ -0,0 +1,102 @@ +args@{ lib, pkgs, config, custom-utils, ... }: +let + ports = import ../ports.nix args; +in +{ + services.home-assistant = { + enable = true; + lovelaceConfig = { + title = "Home"; + views = [ + { + path = "default_view"; + title = "Home"; + cards = [ + { + type = "entities"; + entities = [ + "switch.circadian_lighting_circadian_lighting" + ]; + } + { + title = "Eingang"; + type = "entities"; + entities = [ + { + entity = "light.eingang_deckenleuchte_deckenleuchte"; + name = "Deckenleuchte"; + } + ]; + } + { + name = "Esstisch"; + type = "entities"; + entities = [ + { + entity = "light.yeelight_meteorite_ambient_light"; + name = "Ambient light"; + } + { + entity = "light.yeelight_meteorite_ceiling_light"; + name = "Ceiling light"; + } + { + entity = "light.yeelight_meteorite_night_light"; + name = "Night light"; + } + ]; + } + ]; + } + ]; + }; + extraComponents = [ + # See https://www.home-assistant.io/integrations + "esphome" + ]; + customComponents = [ + pkgs.home-assistant-custom-components.circadian_lighting + ]; + 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 = { }; + circadian_lighting = { }; + switch = [ + { + platform = "circadian_lighting"; + lights_ct = [ + "light.yeelight_meteorite_ceiling_light" + "light.eingang_deckenleuchte_deckenleuchte" + "light.led_panel_schreibtisch_panel" + ]; + min_brightness = 20; + } + ]; + }; + }; + + 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; + }; + }; +} diff --git a/pkgs/default.nix b/pkgs/default.nix index 5384317..1322ca8 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -29,4 +29,7 @@ in contact = callPackage ./contact-page { }; }; wofi-bluetooth = callPackage ./wofi-bluetooth/wofi-bluetooth.nix { }; + home-assistant-custom-components = prev.recurseIntoAttrs { + circadian_lighting = callPackage ./home-assistant-custom-components/circadian_lighting.nix { }; + }; } diff --git a/pkgs/home-assistant-custom-components/circadian_lighting.nix b/pkgs/home-assistant-custom-components/circadian_lighting.nix new file mode 100644 index 0000000..54316f7 --- /dev/null +++ b/pkgs/home-assistant-custom-components/circadian_lighting.nix @@ -0,0 +1,19 @@ +{ lib +, fetchFromGitHub +, buildHomeAssistantComponent +}: + +buildHomeAssistantComponent rec { + owner = "claytonjn"; + domain = "circadian_lighting"; + version = "2.1.4"; + + src = fetchFromGitHub { + owner = "claytonjn"; + repo = "hass-circadian_lighting"; + rev = "refs/tags/${version}"; + hash = "sha256-F67JP5PzMblWpI4CvrHyutPenzVd9KyKeHcHx8rcezA="; + }; + + dontBuild = true; +}