# manages home automations { config, lib, pkgs, self, ... }: let cfg = config.my.profiles.home-assistant; hostName = config.networking.hostName; # Python deps for the alexa_media_player custom component. Built against # Home Assistant's own interpreter — the same set buildHomeAssistantComponent # uses — so HA's build-time and runtime manifest-requirement checks pass. haPython = pkgs.home-assistant.python3Packages; # dictor is not in nixpkgs; alexa_media_player needs dictor>=0.1.12,<0.2. # Pure-Python, no runtime deps, legacy setup.py. dictor = haPython.buildPythonPackage { pname = "dictor"; version = "0.1.12"; format = "setuptools"; src = haPython.fetchPypi { pname = "dictor"; version = "0.1.12"; hash = "sha256-bbSDda4eU9ye2EToWzj04/v79qTmC+yjd1Fa0URTuRs="; }; build-system = [ haPython.setuptools ]; doCheck = false; pythonImportsCheck = [ "dictor" ]; }; # nixpkgs ships alexapy 1.29.22; the manifest pins ==1.29.25. Patch bump. # nixpkgs fetches alexapy from GitLab (keatontaylor/alexapy), tag v. alexapy = haPython.alexapy.overridePythonAttrs (old: { version = "1.29.25"; src = pkgs.fetchFromGitLab { owner = "keatontaylor"; repo = "alexapy"; tag = "v1.29.25"; hash = "sha256-P/hvgqZVaBJF5dbmHrDjQMC+pwV3EEhKyFIS5KmhgD4="; }; }); in { options.my.profiles.home-assistant = with lib; { enable = mkEnableOption "Home Automation"; }; config = lib.mkIf cfg.enable { services.matter-server.enable = true; services.home-assistant = { enable = true; openFirewall = true; # HACS-style custom components, packaged declaratively (no HACS runtime). # Config-flow based: add via Settings > Devices & Services after rebuild. customComponents = [ (pkgs.buildHomeAssistantComponent { owner = "Matts-Baps"; domain = "sourdough"; version = "1.1.3"; src = pkgs.fetchFromGitHub { owner = "Matts-Baps"; repo = "ha-sourdough"; rev = "v1.1.3"; hash = "sha256-Uoid/2f6GxZMuE5Keu2VjHPYuOxnYG8hsCD6BYcaTvM="; }; }) (pkgs.buildHomeAssistantComponent { owner = "Alandtse"; domain = "alexa_media"; version = "5.15.7"; src = pkgs.fetchFromGitHub { owner = "Alandtse"; repo = "alexa_media_player"; rev = "v5.15.7"; hash = "sha256-1rcZVSX1xA1Lc4qSu39MOitVEciZFhoPQy2y5+PpoAI="; }; # Every manifest requirement must be importable at a satisfying # version or manifestCheckPhase fails the build. dependencies = [ alexapy dictor haPython.wrapt haPython.packaging ]; }) ]; extraComponents = [ "matter" "mobile_app" "otbr" "thread" "xiaomi_miio" "apple_tv" # Apple TV (pyatv); pair via UI PIN flow "tuya" # Tuya/SmartLife cloud; Unistyle WLAN irrigation computer ]; }; services.home-assistant.config = { homeassistant = { name = "Home - Rechberg"; unit_system = "metric"; internal_url = "http://${hostName}:8123"; external_url = "http://jupiter.solar.internal:8123"; }; mobile_app = { }; automation = "!include automations.yaml"; script = "!include scripts.yaml"; scene = "!include scenes.yaml"; }; # `!include` targets must exist or HA fails at startup. Create them empty # so HA's UI editor can write to them; `f` only acts if the file is absent. systemd.tmpfiles.rules = [ "f /var/lib/hass/automations.yaml 0644 hass hass - []" "f /var/lib/hass/scripts.yaml 0644 hass hass - {}" "f /var/lib/hass/scenes.yaml 0644 hass hass - []" ]; services.openthread-border-router = { enable = true; package = pkgs.unstable.openthread-border-router; openFirewall = true; backboneInterfaces = [ "enp3s0" ]; radio.device = "/dev/serial/by-id/usb-Nabu_Casa_ZBT-2_DCB4D9149C7C-if00"; radio.baudRate = 460800; }; my.homepage.services = [ { group = "Services"; name = "Home Assistant"; description = "Home automation"; href = "http://${hostName}:8123"; icon = "si-homeassistant"; } ]; }; }