{ config, lib, pkgs, ... }: with lib; let hostname = "wormhole0"; primary-ip = "10.0.0.3"; state-dir = "/state"; zigbee2mqtt-statedir = "${state-dir}/services/zigbee2mqtt"; home-assistant-port = 8123; zigbee2mqtt-user = config.systemd.services.zigbee2mqtt.serviceConfig.User; zigbee2mqtt-passwd-file = pkgs.lib.passwd.stablerandom-passwd-file "zigbee2mqtt-passwd" config.instance.build-seed; teslaMateMqttPasswdFile = pkgs.lib.passwd.stablerandom-passwd-file "tesla-mate-mqtt-passwd" config.instance.build-seed; nitenMqttPasswdFile = pkgs.lib.passwd.stablerandom-passwd-file "niten-mqtt-passwd" config.instance.build-seed; nodeRedPasswdFile = pkgs.lib.passwd.stablerandom-passwd-file "node-red-mqtt-passwd" config.instance.build-seed; teslaMatePort = 4400; teslaGraphPort = 4401; nodeRedPort = 1880; host-secrets = config.fudo.secrets.host-secrets.${hostname}; host-passwds = config.fudo.secrets.files.service-passwords.${hostname}; in { imports = [ (import ./wormhole0/home-assistant.nix { homeAssistantImage = "ghcr.io/home-assistant/home-assistant:2023.9"; nodeRedImage = "nodered/node-red:3.1.0-14"; inherit nodeRedPort; stateDirectory = "/state/services/arion-home-assistant"; }) ]; config = { boot.kernel.sysctl = { "net.ipv4.ip_forward" = true; }; networking = { hostName = hostname; firewall = { enable = false; }; defaultGateway = { address = "10.0.0.1"; interface = "intif0"; }; nameservers = [ "10.0.0.1" ]; interfaces = { intif0 = { useDHCP = false; ipv4 = { addresses = [{ address = primary-ip; prefixLength = 16; }]; }; }; wormif0.useDHCP = true; wlp2s0.useDHCP = false; }; dhcpcd.extraConfig = concatStringsSep "\n" [ "nogateway" ]; }; fudo.services.mqtt = { enable = true; state-directory = "${state-dir}/services/mosquitto"; private = { enable = true; users = { zigbee2mqtt = { password-file = zigbee2mqtt-passwd-file; acl = [ "readwrite #" ]; }; home-assistant = { password-file = host-passwds.mosquitto-home-assistant; acl = [ "readwrite #" ]; }; tesla-mate = { password-file = teslaMateMqttPasswdFile; acl = [ "readwrite teslamate/cars/#" ]; }; niten = { password-file = nitenMqttPasswdFile; acl = [ "readwrite #" ]; }; node-red = { password-file = nodeRedPasswdFile; acl = [ "readwrite #" ]; }; }; }; }; systemd = { services = { wormhole-route = { wantedBy = [ "multi-user.target" ]; after = [ "network-online.target" ]; serviceConfig = { ExecStart = "${pkgs.iproute2}/bin/ip route add 192.168.86.0/24 dev wormif0"; ExecStop = "${pkgs.iproute2}/bin/ip route del 192.168.86.0/24 dev wormif0"; RemainAfterExit = true; }; }; }; tmpfiles.rules = [ "L /root/.gnupg - - - - ${state-dir}/user/root/gnupg" "L /root/.ssh/id_rsa - - - - ${state-dir}/user/root/ssh/id_rsa" "L /root/.ssh/id_rsa.pub - - - - ${state-dir}/user/root/ssh/id_rsa.pub" "L /root/.ssh/known_hosts - - - - ${state-dir}/user/root/ssh/known_hosts" "L /etc/adjtime - - - - ${state-dir}/etc/adjtime" "d /state/services 0711 root root - -" "d ${zigbee2mqtt-statedir} 0700 ${zigbee2mqtt-user} - - -" ]; }; users.groups = let zigbee2mqtt-user = config.systemd.services.zigbee2mqtt.serviceConfig.User; in { dialout.members = [ zigbee2mqtt-user ]; }; services = { openssh.hostKeys = [ { path = "${state-dir}/ssh/ssh_host_rsa_key"; type = "rsa"; bits = 4096; } { path = "${state-dir}/ssh/ssh_host_ed25519_key"; type = "ed25519"; } ]; nginx = { enable = true; recommendedOptimisation = true; recommendedProxySettings = true; recommendedGzipSettings = true; virtualHosts = { "home-assist.sea.fudo.org" = { locations."/" = { proxyPass = "http://localhost:${toString home-assistant-port}"; proxyWebsockets = true; }; }; "node-red.sea.fudo.org" = { locations."/" = { proxyPass = "http://localhost:${toString nodeRedPort}"; proxyWebsockets = true; }; }; "tesla-mate.sea.fudo.org" = { locations."/" = { proxyPass = "http://localhost:${toString teslaMatePort}"; proxyWebsockets = true; }; }; "tesla-graph.sea.fudo.org" = { locations."/" = { proxyPass = "http://localhost:${toString teslaGraphPort}"; proxyWebsockets = true; }; }; }; }; # mosquitto = { # enable = true; # dataDir = mosquitto-statedir; # listeners = [{ # settings.allow_anonymous = false; # port = 1883; # address = "0.0.0.0"; # users = { # zigbee2mqtt = { # passwordFile = # host-secrets.mosquitto-zigbee2mqtt-passwd.target-file; # acl = [ "readwrite #" ]; # }; # home-assistant = { # passwordFile = # host-secrets.mosquitto-home-assistant-passwd.target-file; # acl = [ "readwrite #" ]; # }; # niten = { # passwordFile = host-secrets.mosquitto-niten-passwd.target-file; # acl = [ "readwrite #" ]; # }; # # xiaoxuan = { # # passwordFile = host-secrets.mosquitto-xiaoxuan-passwd.target-file; # # acl = [ "readwrite #" ]; # # }; # # wallfly = { # # passwordFile = host-secrets.mosquitto-wallfly-passwd.target-file; # # acl = [ "readwrite homeassistant/binary_sensor/#" ]; # # }; # }; # }]; # }; zigbee2mqtt = { enable = true; dataDir = zigbee2mqtt-statedir; package = pkgs.pkgsUnstable.zigbee2mqtt; settings = { homeassistant = true; permit_join = true; serial.port = "/dev/ttyUSB0"; mqtt = let mqttHost = config.fudo.services.mqtt.mqtt-hostname; mqttPort = config.fudo.services.mqtt.private.port; in { server = "mqtt://${mqttHost}:${toString mqttPort}"; user = "zigbee2mqtt"; password = readFile zigbee2mqtt-passwd-file; # TODO: could make a yaml file containing password # described https://www.zigbee2mqtt.io/guide/configuration/mqtt.html#server-connection # Weird, though. }; advanced.log_level = "debug"; }; }; avahi = { enable = true; reflector = true; interfaces = [ "intif0" "worm0" ]; }; teslaMateContainer = { enable = true; mqtt = { host = config.fudo.services.mqtt.mqtt-hostname; port = config.fudo.services.mqtt.private.port; user = "tesla-mate"; password = readFile teslaMateMqttPasswdFile; }; port = teslaMatePort; grafana-port = teslaGraphPort; state-directory = "/state/services/tesla-mate"; }; }; virtualisation = { docker = { enable = false; #enableOnBoot = true; #autoPrune.enable = true; }; podman = { enable = true; dockerSocket.enable = true; autoPrune.enable = true; }; arion.backend = "podman-socket"; # oci-containers = { # backend = "podman"; # containers = { # home-assistant = { # image = "homeassistant/home-assistant:stable"; # autoStart = true; # environment.TZ = config.time.timeZone; # #ports = [ "${toString home-assistant-port}:8123" ]; # volumes = [ "/state/services/home-assistant:/config" ]; # extraOptions = [ "--network=host" ]; # }; # }; # }; }; security.sudo.extraConfig = '' # Due to rollback, sudo will lecture after every reboot Defaults lecture = never ''; }; }