diff --git a/devices/cluster/modules/caddy.nix b/devices/cluster/modules/caddy.nix index b16a01c8..fdeea4b6 100644 --- a/devices/cluster/modules/caddy.nix +++ b/devices/cluster/modules/caddy.nix @@ -35,6 +35,7 @@ in { clusterIP = config.services.pcsd.virtualIps.caddy-vip.ip; nosIP = "10.0.0.121"; serviviIP = "10.0.0.249"; + homieIP = "100.64.0.10"; tlsConf = '' tls { @@ -53,6 +54,7 @@ in { // (builtins.removeAttrs extraConf ["extraConfig"]); in { # Public + "Home-Assistant" = mkPublicReverseProxy "homie" "${homieIP}:8123" {}; "Vaultwarden" = mkPublicReverseProxy "vault" "${nosIP}:8781" {}; "Hauk" = mkPublicReverseProxy "hauk" "${nosIP}:3003" {}; "Headscale" = mkPublicReverseProxy "headscale" "${clusterIP}:8085" {}; diff --git a/devices/homie/default.nix b/devices/homie/default.nix index 27f0b27b..23bdf9aa 100644 --- a/devices/homie/default.nix +++ b/devices/homie/default.nix @@ -11,6 +11,8 @@ in { imports = [ ./hardware-configuration.nix + ./modules/home-assistant.nix + self.nixosModules.kmscon self.nixosModules.server ]; diff --git a/devices/homie/modules/home-assistant.nix b/devices/homie/modules/home-assistant.nix new file mode 100644 index 00000000..2bbcbcfe --- /dev/null +++ b/devices/homie/modules/home-assistant.nix @@ -0,0 +1,81 @@ +{ + self, + wakewords-src, + ... +}: { + imports = [self.nixosModules.wyoming-plus]; + + services = { + home-assistant = { + enable = true; + + extraComponents = [ + "esphome" + "holiday" + "met" + "spotify" + "upnp" + "wyoming" + "yamaha_musiccast" + ]; + + config = { + http = { + server_host = "0.0.0.0"; + trusted_proxies = ["100.64.0.8" "100.64.0.9"]; + use_x_forwarded_for = true; + }; + + # Wanted defaults + assist_pipeline = {}; + config = {}; + conversation = {}; + dhcp = {}; + history = {}; + image_upload = {}; + logbook = {}; + mobile_app = {}; + my = {}; + sun = {}; + automation = {}; + zeroconf = {}; + }; + }; + + esphome = { + enable = true; + address = "localhost"; + port = 6052; + }; + + # Needs manual setting in GUI + wyoming = { + piper.servers."en" = { + enable = true; + uri = "tcp://127.0.0.1:10200"; + + # see https://github.com/rhasspy/rhasspy3/blob/master/programs/tts/piper/script/download.py + voice = "en-us-ryan-low"; + speaker = 0; + }; + + faster-whisper.servers."en" = { + enable = true; + uri = "tcp://127.0.0.1:10300"; + + # see https://github.com/rhasspy/rhasspy3/blob/master/programs/asr/faster-whisper/script/download.py + model = "small-int8"; + language = "en"; + device = "cpu"; + }; + + openwakeword-docker = { + enable = true; + uri = "127.0.0.1:10400"; + + customModelsDirectories = ["${wakewords-src}/en/yo_homie"]; + preloadModels = ["yo_homie"]; + }; + }; + }; +} diff --git a/flake.lock b/flake.lock index 1a4d2080..55c03168 100644 Binary files a/flake.lock and b/flake.lock differ diff --git a/flake.nix b/flake.nix index 1682b16c..32289e76 100644 Binary files a/flake.nix and b/flake.nix differ diff --git a/inputs.nix b/inputs.nix index 77bd647d..7bd38a81 100644 --- a/inputs.nix +++ b/inputs.nix @@ -170,6 +170,13 @@ let }; srcs = [ + # Home-assistant + { + name = "wakewords-src"; + owner = "fwartner"; + repo = "home-assistant-wakewords-collection"; + } + # Nvim plugins { name = "vimplugin-easytables-src"; diff --git a/nixosModules/default.nix b/nixosModules/default.nix index 1d87a787..10bc1101 100644 --- a/nixosModules/default.nix +++ b/nixosModules/default.nix @@ -7,4 +7,5 @@ self: { nvidia = import ./nvidia; plymouth = import ./plymouth; server = import ./server; + wyoming-plus = import ./wyoming-plus self; } diff --git a/nixosModules/wyoming-plus/default.nix b/nixosModules/wyoming-plus/default.nix new file mode 100644 index 00000000..b860c478 --- /dev/null +++ b/nixosModules/wyoming-plus/default.nix @@ -0,0 +1,153 @@ +self: { + config, + lib, + pkgs, + ... +}: let + inherit + (lib) + attrNames + escapeShellArgs + flatten + filterAttrs + listToAttrs + map + mkEnableOption + mkForce + mkIf + mkOption + nameValuePair + optionals + splitString + types + ; + + flatMap = f: list: flatten (map f list); + + cfg = config.services.wyoming.openwakeword-docker; +in { + imports = [self.nixosModules.docker]; + + options.services.wyoming.openwakeword-docker = { + enable = mkEnableOption "Wyoming openWakeWord server"; + + image = mkOption { + type = types.package; + default = pkgs.dockerTools.pullImage { + imageName = "docker.io/rhasspy/wyoming-openwakeword"; + imageDigest = "sha256:88df83cfdaa5a0dd068f79662d06b81479ec7b59a4bea59751ff5d6f68bad24a"; + sha256 = "1c2yhrhhj1wpd5bcc3zaz1gv8mw8dw5m76cjf42nhf2sgwp2hsjl"; + finalImageName = "docker.io/rhasspy/wyoming-openwakeword"; + finalImageTag = "latest"; + }; + description = '' + The image that docker will use. + ''; + }; + + uri = mkOption { + type = types.str; + default = "0.0.0.0:10400"; + example = "192.0.2.1:5000"; + description = '' + URI to bind the wyoming server to. + ''; + }; + + customModelsDirectories = mkOption { + type = types.listOf types.path; + default = []; + description = '' + Paths to directories with custom wake word models (*.tflite model files). + ''; + }; + + preloadModels = mkOption { + type = with types; listOf str; + default = [ + "ok_nabu" + ]; + example = [ + # wyoming_openwakeword/models/*.tflite + "alexa" + "hey_jarvis" + "hey_mycroft" + "hey_rhasspy" + "ok_nabu" + ]; + description = '' + List of wake word models to preload after startup. + ''; + }; + + threshold = mkOption { + type = types.float; + default = 0.5; + description = '' + Activation threshold (0-1), where higher means fewer activations. + + See trigger level for the relationship between activations and + wake word detections. + ''; + apply = toString; + }; + + triggerLevel = mkOption { + type = types.int; + default = 1; + description = '' + Number of activations before a detection is registered. + + A higher trigger level means fewer detections. + ''; + apply = toString; + }; + + extraArgs = mkOption { + type = with types; listOf str; + default = []; + description = '' + Extra arguments to pass to the server commandline. + ''; + apply = escapeShellArgs; + }; + }; + + config = { + systemd.services = let + unitNames = attrNames ( + filterAttrs (_: v: v.device == "cpu") config.services.wyoming.faster-whisper.servers + ); + in + listToAttrs (map (x: + nameValuePair "wyoming-faster-whisper-${x}" { + serviceConfig.ProcSubset = mkForce "all"; + }) + unitNames); + + khepri = mkIf cfg.enable { + compositions."openwakeword" = { + networks.default = {}; + + services."openwakeword" = { + image = cfg.image; + restart = "always"; + networks = ["default"]; + + volumes = map (dir: "${toString dir}:${toString dir}") cfg.customModelsDirectories; + cmd = + (flatMap (model: ["--preload-model" model]) cfg.preloadModels) + ++ (flatMap (dir: ["--custom-model-dir" (toString dir)]) cfg.customModelsDirectories) + ++ ["--threshold" cfg.threshold] + ++ ["--trigger-level" cfg.triggerLevel] + ++ optionals (cfg.extraArgs != "") (splitString " " cfg.extraArgs); + + ports = ["${cfg.uri}:10400"]; + }; + }; + }; + }; + + # For accurate stack trace + _file = ./default.nix; +}