diff --git a/modules/nixos/services/home-assistant/default.nix b/modules/nixos/services/home-assistant/default.nix index 7f31e36..0c8f89c 100644 --- a/modules/nixos/services/home-assistant/default.nix +++ b/modules/nixos/services/home-assistant/default.nix @@ -131,6 +131,13 @@ in name: file: "C ${cfg.path}/config/${name} 0644 root root - ${file}" ) seed; + assertions = [ + { + assertion = !cfg.radio.udevRule || !(lib.hasPrefix "/dev/tty" cfg.radio.device); + message = "services.home-assistant.radio.device is a kernel-owned node, so the udev rule would symlink it onto itself. Set radio.udevRule = false, or point device at a name of your own."; + } + ]; + # HA does its own mDNS discovery on the host network. networking.firewall.allowedUDPPorts = [ 5353 ]; @@ -138,9 +145,17 @@ in # for thread mode rather than a rescue tool. environment.systemPackages = [ pkgs.python3Packages.universal-silabs-flasher ]; - services.udev.extraRules = lib.mkIf cfg.radio.udevRule '' - SUBSYSTEM=="tty", ATTRS{idVendor}=="303a", ATTRS{idProduct}=="831a", ENV{ID_USB_INTERFACE_NUM}=="00", SYMLINK+="${lib.removePrefix "/dev/" cfg.radio.device}", GROUP="dialout", MODE="0660" - ''; + # Two independent matches, whichever the stick answers to. A rule that + # matches nothing is silently a no-op, hence the belt and braces. + services.udev.extraRules = + let + link = lib.removePrefix "/dev/" cfg.radio.device; + tail = ''SYMLINK+="${link}", GROUP="dialout", MODE="0660"''; + in + lib.mkIf cfg.radio.udevRule '' + SUBSYSTEM=="tty", ATTRS{idVendor}=="303a", ATTRS{idProduct}=="831a", ${tail} + SUBSYSTEM=="tty", ATTRS{manufacturer}=="Nabu Casa", ATTRS{product}=="ZBT-2", ${tail} + ''; virtualisation.oci-containers.containers.home-assistant = { image = "ghcr.io/home-assistant/home-assistant:${cfg.version}"; @@ -224,6 +239,10 @@ in cfg.otbr.webPort ]; + # Docker skips --device validation for privileged containers, so without + # this a missing radio only shows up as otbr-agent crash-looping. + systemd.services.docker-otbr.unitConfig.ConditionPathExists = otbrDevice; + virtualisation.oci-containers.containers.otbr = { image = "ghcr.io/ownbee/hass-otbr-docker:${cfg.otbr.version}"; diff --git a/systems/x86_64-nixos/absolutesolver/default.nix b/systems/x86_64-nixos/absolutesolver/default.nix index 4bc9344..6ef4f1c 100644 --- a/systems/x86_64-nixos/absolutesolver/default.nix +++ b/systems/x86_64-nixos/absolutesolver/default.nix @@ -65,6 +65,10 @@ home-assistant = { enable = true; path = "/mnt/storage/active/docker/volumes/home-assistant"; + radio = { + device = "/dev/ttyACM0"; + udevRule = false; + }; }; }; };