From b84f4bb54dbbd2a804c360d717f799f00526c4f5 Mon Sep 17 00:00:00 2001 From: Martin Weinelt Date: Fri, 29 Mar 2024 03:02:33 +0100 Subject: nixos/wyoming: move into home-automation category --- nixos/modules/module-list.nix | 6 +- .../services/audio/wyoming/faster-whisper.nix | 182 --------------------- .../services/audio/wyoming/openwakeword.nix | 163 ------------------ nixos/modules/services/audio/wyoming/piper.nix | 175 -------------------- .../home-automation/wyoming/faster-whisper.nix | 182 +++++++++++++++++++++ .../home-automation/wyoming/openwakeword.nix | 163 ++++++++++++++++++ .../services/home-automation/wyoming/piper.nix | 175 ++++++++++++++++++++ 7 files changed, 523 insertions(+), 523 deletions(-) delete mode 100644 nixos/modules/services/audio/wyoming/faster-whisper.nix delete mode 100644 nixos/modules/services/audio/wyoming/openwakeword.nix delete mode 100644 nixos/modules/services/audio/wyoming/piper.nix create mode 100644 nixos/modules/services/home-automation/wyoming/faster-whisper.nix create mode 100644 nixos/modules/services/home-automation/wyoming/openwakeword.nix create mode 100644 nixos/modules/services/home-automation/wyoming/piper.nix diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 0022e6ff79081..1ddf536d3224e 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -362,9 +362,6 @@ ./services/audio/spotifyd.nix ./services/audio/squeezelite.nix ./services/audio/tts.nix - ./services/audio/wyoming/faster-whisper.nix - ./services/audio/wyoming/openwakeword.nix - ./services/audio/wyoming/piper.nix ./services/audio/ympd.nix ./services/backup/automysqlbackup.nix ./services/backup/bacula.nix @@ -588,6 +585,9 @@ ./services/home-automation/govee2mqtt.nix ./services/home-automation/home-assistant.nix ./services/home-automation/matter-server.nix + ./services/home-automation/wyoming/faster-whisper.nix + ./services/home-automation/wyoming/openwakeword.nix + ./services/home-automation/wyoming/piper.nix ./services/home-automation/wyoming/satellite.nix ./services/home-automation/zigbee2mqtt.nix ./services/home-automation/zwave-js.nix diff --git a/nixos/modules/services/audio/wyoming/faster-whisper.nix b/nixos/modules/services/audio/wyoming/faster-whisper.nix deleted file mode 100644 index 0c36e8c9ab059..0000000000000 --- a/nixos/modules/services/audio/wyoming/faster-whisper.nix +++ /dev/null @@ -1,182 +0,0 @@ -{ config -, lib -, pkgs -, ... -}: - -let - cfg = config.services.wyoming.faster-whisper; - - inherit (lib) - escapeShellArgs - mkOption - mdDoc - mkEnableOption - mkPackageOption - types - ; - - inherit (builtins) - toString - ; - -in - -{ - options.services.wyoming.faster-whisper = with types; { - package = mkPackageOption pkgs "wyoming-faster-whisper" { }; - - servers = mkOption { - default = {}; - description = mdDoc '' - Attribute set of faster-whisper instances to spawn. - ''; - type = types.attrsOf (types.submodule ( - { ... }: { - options = { - enable = mkEnableOption (mdDoc "Wyoming faster-whisper server"); - - model = mkOption { - type = str; - default = "tiny-int8"; - example = "Systran/faster-distil-whisper-small.en"; - description = mdDoc '' - Name of the voice model to use. - - Check the [2.0.0 release notes](https://github.com/rhasspy/wyoming-faster-whisper/releases/tag/v2.0.0) for possible values. - ''; - }; - - uri = mkOption { - type = strMatching "^(tcp|unix)://.*$"; - example = "tcp://0.0.0.0:10300"; - description = mdDoc '' - URI to bind the wyoming server to. - ''; - }; - - device = mkOption { - # https://opennmt.net/CTranslate2/python/ctranslate2.models.Whisper.html# - type = types.enum [ - "cpu" - "cuda" - "auto" - ]; - default = "cpu"; - description = mdDoc '' - Determines the platform faster-whisper is run on. CPU works everywhere, CUDA requires a compatible NVIDIA GPU. - ''; - }; - - language = mkOption { - type = enum [ - # https://github.com/home-assistant/addons/blob/master/whisper/config.yaml#L20 - "auto" "af" "am" "ar" "as" "az" "ba" "be" "bg" "bn" "bo" "br" "bs" "ca" "cs" "cy" "da" "de" "el" "en" "es" "et" "eu" "fa" "fi" "fo" "fr" "gl" "gu" "ha" "haw" "he" "hi" "hr" "ht" "hu" "hy" "id" "is" "it" "ja" "jw" "ka" "kk" "km" "kn" "ko" "la" "lb" "ln" "lo" "lt" "lv" "mg" "mi" "mk" "ml" "mn" "mr" "ms" "mt" "my" "ne" "nl" "nn" "no" "oc" "pa" "pl" "ps" "pt" "ro" "ru" "sa" "sd" "si" "sk" "sl" "sn" "so" "sq" "sr" "su" "sv" "sw" "ta" "te" "tg" "th" "tk" "tl" "tr" "tt" "uk" "ur" "uz" "vi" "yi" "yo" "zh" - ]; - example = "en"; - description = mdDoc '' - The language used to to parse words and sentences. - ''; - }; - - beamSize = mkOption { - type = ints.unsigned; - default = 1; - example = 5; - description = mdDoc '' - The number of beams to use in beam search. - ''; - apply = toString; - }; - - extraArgs = mkOption { - type = listOf str; - default = [ ]; - description = mdDoc '' - Extra arguments to pass to the server commandline. - ''; - apply = escapeShellArgs; - }; - }; - } - )); - }; - }; - - config = let - inherit (lib) - mapAttrs' - mkIf - nameValuePair - ; - in mkIf (cfg.servers != {}) { - systemd.services = mapAttrs' (server: options: - nameValuePair "wyoming-faster-whisper-${server}" { - inherit (options) enable; - description = "Wyoming faster-whisper server instance ${server}"; - after = [ - "network-online.target" - ]; - wantedBy = [ - "multi-user.target" - ]; - serviceConfig = { - DynamicUser = true; - User = "wyoming-faster-whisper"; - StateDirectory = "wyoming/faster-whisper"; - # https://github.com/home-assistant/addons/blob/master/whisper/rootfs/etc/s6-overlay/s6-rc.d/whisper/run - ExecStart = '' - ${cfg.package}/bin/wyoming-faster-whisper \ - --data-dir $STATE_DIRECTORY \ - --download-dir $STATE_DIRECTORY \ - --uri ${options.uri} \ - --device ${options.device} \ - --model ${options.model} \ - --language ${options.language} \ - --beam-size ${options.beamSize} ${options.extraArgs} - ''; - CapabilityBoundingSet = ""; - DeviceAllow = if builtins.elem options.device [ "cuda" "auto" ] then [ - # https://docs.nvidia.com/dgx/pdf/dgx-os-5-user-guide.pdf - # CUDA not working? Check DeviceAllow and PrivateDevices first! - "/dev/nvidia0" - "/dev/nvidia1" - "/dev/nvidia2" - "/dev/nvidia3" - "/dev/nvidia4" - "/dev/nvidia-caps/nvidia-cap1" - "/dev/nvidia-caps/nvidia-cap2" - "/dev/nvidiactl" - "/dev/nvidia-modeset" - "/dev/nvidia-uvm" - "/dev/nvidia-uvm-tools" - ] else ""; - DevicePolicy = "closed"; - LockPersonality = true; - MemoryDenyWriteExecute = true; - PrivateUsers = true; - ProtectHome = true; - ProtectHostname = true; - ProtectKernelLogs = true; - ProtectKernelModules = true; - ProtectKernelTunables = true; - ProtectControlGroups = true; - ProtectProc = "invisible"; - ProcSubset = "pid"; - RestrictAddressFamilies = [ - "AF_INET" - "AF_INET6" - "AF_UNIX" - ]; - RestrictNamespaces = true; - RestrictRealtime = true; - SystemCallArchitectures = "native"; - SystemCallFilter = [ - "@system-service" - "~@privileged" - ]; - UMask = "0077"; - }; - }) cfg.servers; - }; -} diff --git a/nixos/modules/services/audio/wyoming/openwakeword.nix b/nixos/modules/services/audio/wyoming/openwakeword.nix deleted file mode 100644 index 252f70be2baa4..0000000000000 --- a/nixos/modules/services/audio/wyoming/openwakeword.nix +++ /dev/null @@ -1,163 +0,0 @@ -{ config -, lib -, pkgs -, ... -}: - -let - cfg = config.services.wyoming.openwakeword; - - inherit (lib) - concatStringsSep - concatMapStringsSep - escapeShellArgs - mkOption - mdDoc - mkEnableOption - mkIf - mkPackageOption - mkRemovedOptionModule - types - ; - - inherit (builtins) - toString - ; - -in - -{ - imports = [ - (mkRemovedOptionModule [ "services" "wyoming" "openwakeword" "models" ] "Configuring models has been removed, they are now dynamically discovered and loaded at runtime") - ]; - - meta.buildDocsInSandbox = false; - - options.services.wyoming.openwakeword = with types; { - enable = mkEnableOption (mdDoc "Wyoming openWakeWord server"); - - package = mkPackageOption pkgs "wyoming-openwakeword" { }; - - uri = mkOption { - type = strMatching "^(tcp|unix)://.*$"; - default = "tcp://0.0.0.0:10400"; - example = "tcp://192.0.2.1:5000"; - description = mdDoc '' - URI to bind the wyoming server to. - ''; - }; - - customModelsDirectories = mkOption { - type = listOf types.path; - default = []; - description = lib.mdDoc '' - Paths to directories with custom wake word models (*.tflite model files). - ''; - }; - - preloadModels = mkOption { - type = listOf str; - default = [ - "ok_nabu" - ]; - example = [ - # wyoming_openwakeword/models/*.tflite - "alexa" - "hey_jarvis" - "hey_mycroft" - "hey_rhasspy" - "ok_nabu" - ]; - description = mdDoc '' - List of wake word models to preload after startup. - ''; - }; - - threshold = mkOption { - type = float; - default = 0.5; - description = mdDoc '' - 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 = int; - default = 1; - description = mdDoc '' - Number of activations before a detection is registered. - - A higher trigger level means fewer detections. - ''; - apply = toString; - }; - - extraArgs = mkOption { - type = listOf str; - default = [ ]; - description = mdDoc '' - Extra arguments to pass to the server commandline. - ''; - apply = escapeShellArgs; - }; - }; - - config = mkIf cfg.enable { - systemd.services."wyoming-openwakeword" = { - description = "Wyoming openWakeWord server"; - after = [ - "network-online.target" - ]; - wantedBy = [ - "multi-user.target" - ]; - serviceConfig = { - DynamicUser = true; - User = "wyoming-openwakeword"; - # https://github.com/home-assistant/addons/blob/master/openwakeword/rootfs/etc/s6-overlay/s6-rc.d/openwakeword/run - ExecStart = concatStringsSep " " [ - "${cfg.package}/bin/wyoming-openwakeword" - "--uri ${cfg.uri}" - (concatMapStringsSep " " (model: "--preload-model ${model}") cfg.preloadModels) - (concatMapStringsSep " " (dir: "--custom-model-dir ${toString dir}") cfg.customModelsDirectories) - "--threshold ${cfg.threshold}" - "--trigger-level ${cfg.triggerLevel}" - "${cfg.extraArgs}" - ]; - CapabilityBoundingSet = ""; - DeviceAllow = ""; - DevicePolicy = "closed"; - LockPersonality = true; - MemoryDenyWriteExecute = true; - PrivateDevices = true; - PrivateUsers = true; - ProtectHome = true; - ProtectHostname = true; - ProtectKernelLogs = true; - ProtectKernelModules = true; - ProtectKernelTunables = true; - ProtectControlGroups = true; - ProtectProc = "invisible"; - ProcSubset = "all"; # reads /proc/cpuinfo - RestrictAddressFamilies = [ - "AF_INET" - "AF_INET6" - "AF_UNIX" - ]; - RestrictNamespaces = true; - RestrictRealtime = true; - RuntimeDirectory = "wyoming-openwakeword"; - SystemCallArchitectures = "native"; - SystemCallFilter = [ - "@system-service" - "~@privileged" - ]; - UMask = "0077"; - }; - }; - }; -} diff --git a/nixos/modules/services/audio/wyoming/piper.nix b/nixos/modules/services/audio/wyoming/piper.nix deleted file mode 100644 index 2828fdf078921..0000000000000 --- a/nixos/modules/services/audio/wyoming/piper.nix +++ /dev/null @@ -1,175 +0,0 @@ -{ config -, lib -, pkgs -, ... -}: - -let - cfg = config.services.wyoming.piper; - - inherit (lib) - escapeShellArgs - mkOption - mdDoc - mkEnableOption - mkPackageOption - types - ; - - inherit (builtins) - toString - ; - -in - -{ - meta.buildDocsInSandbox = false; - - options.services.wyoming.piper = with types; { - package = mkPackageOption pkgs "wyoming-piper" { }; - - servers = mkOption { - default = {}; - description = mdDoc '' - Attribute set of piper instances to spawn. - ''; - type = types.attrsOf (types.submodule ( - { ... }: { - options = { - enable = mkEnableOption (mdDoc "Wyoming Piper server"); - - piper = mkPackageOption pkgs "piper-tts" { }; - - voice = mkOption { - type = str; - example = "en-us-ryan-medium"; - description = mdDoc '' - Name of the voice model to use. See the following website for samples: - https://rhasspy.github.io/piper-samples/ - ''; - }; - - uri = mkOption { - type = strMatching "^(tcp|unix)://.*$"; - example = "tcp://0.0.0.0:10200"; - description = mdDoc '' - URI to bind the wyoming server to. - ''; - }; - - speaker = mkOption { - type = ints.unsigned; - default = 0; - description = mdDoc '' - ID of a specific speaker in a multi-speaker model. - ''; - apply = toString; - }; - - noiseScale = mkOption { - type = float; - default = 0.667; - description = mdDoc '' - Generator noise value. - ''; - apply = toString; - }; - - noiseWidth = mkOption { - type = float; - default = 0.333; - description = mdDoc '' - Phoneme width noise value. - ''; - apply = toString; - }; - - lengthScale = mkOption { - type = float; - default = 1.0; - description = mdDoc '' - Phoneme length value. - ''; - apply = toString; - }; - - extraArgs = mkOption { - type = listOf str; - default = [ ]; - description = mdDoc '' - Extra arguments to pass to the server commandline. - ''; - apply = escapeShellArgs; - }; - }; - } - )); - }; - }; - - config = let - inherit (lib) - mapAttrs' - mkIf - nameValuePair - ; - in mkIf (cfg.servers != {}) { - systemd.services = mapAttrs' (server: options: - nameValuePair "wyoming-piper-${server}" { - inherit (options) enable; - description = "Wyoming Piper server instance ${server}"; - after = [ - "network-online.target" - ]; - wantedBy = [ - "multi-user.target" - ]; - serviceConfig = { - DynamicUser = true; - User = "wyoming-piper"; - StateDirectory = "wyoming/piper"; - # https://github.com/home-assistant/addons/blob/master/piper/rootfs/etc/s6-overlay/s6-rc.d/piper/run - ExecStart = '' - ${cfg.package}/bin/wyoming-piper \ - --data-dir $STATE_DIRECTORY \ - --download-dir $STATE_DIRECTORY \ - --uri ${options.uri} \ - --piper ${options.piper}/bin/piper \ - --voice ${options.voice} \ - --speaker ${options.speaker} \ - --length-scale ${options.lengthScale} \ - --noise-scale ${options.noiseScale} \ - --noise-w ${options.noiseWidth} ${options.extraArgs} - ''; - CapabilityBoundingSet = ""; - DeviceAllow = ""; - DevicePolicy = "closed"; - LockPersonality = true; - MemoryDenyWriteExecute = true; - PrivateDevices = true; - PrivateUsers = true; - ProtectHome = true; - ProtectHostname = true; - ProtectKernelLogs = true; - ProtectKernelModules = true; - ProtectKernelTunables = true; - ProtectControlGroups = true; - ProtectProc = "invisible"; - ProcSubset = "pid"; - RestrictAddressFamilies = [ - "AF_INET" - "AF_INET6" - "AF_UNIX" - ]; - RestrictNamespaces = true; - RestrictRealtime = true; - SystemCallArchitectures = "native"; - SystemCallFilter = [ - "@system-service" - "~@privileged" - ]; - UMask = "0077"; - }; - }) cfg.servers; - }; -} diff --git a/nixos/modules/services/home-automation/wyoming/faster-whisper.nix b/nixos/modules/services/home-automation/wyoming/faster-whisper.nix new file mode 100644 index 0000000000000..0c36e8c9ab059 --- /dev/null +++ b/nixos/modules/services/home-automation/wyoming/faster-whisper.nix @@ -0,0 +1,182 @@ +{ config +, lib +, pkgs +, ... +}: + +let + cfg = config.services.wyoming.faster-whisper; + + inherit (lib) + escapeShellArgs + mkOption + mdDoc + mkEnableOption + mkPackageOption + types + ; + + inherit (builtins) + toString + ; + +in + +{ + options.services.wyoming.faster-whisper = with types; { + package = mkPackageOption pkgs "wyoming-faster-whisper" { }; + + servers = mkOption { + default = {}; + description = mdDoc '' + Attribute set of faster-whisper instances to spawn. + ''; + type = types.attrsOf (types.submodule ( + { ... }: { + options = { + enable = mkEnableOption (mdDoc "Wyoming faster-whisper server"); + + model = mkOption { + type = str; + default = "tiny-int8"; + example = "Systran/faster-distil-whisper-small.en"; + description = mdDoc '' + Name of the voice model to use. + + Check the [2.0.0 release notes](https://github.com/rhasspy/wyoming-faster-whisper/releases/tag/v2.0.0) for possible values. + ''; + }; + + uri = mkOption { + type = strMatching "^(tcp|unix)://.*$"; + example = "tcp://0.0.0.0:10300"; + description = mdDoc '' + URI to bind the wyoming server to. + ''; + }; + + device = mkOption { + # https://opennmt.net/CTranslate2/python/ctranslate2.models.Whisper.html# + type = types.enum [ + "cpu" + "cuda" + "auto" + ]; + default = "cpu"; + description = mdDoc '' + Determines the platform faster-whisper is run on. CPU works everywhere, CUDA requires a compatible NVIDIA GPU. + ''; + }; + + language = mkOption { + type = enum [ + # https://github.com/home-assistant/addons/blob/master/whisper/config.yaml#L20 + "auto" "af" "am" "ar" "as" "az" "ba" "be" "bg" "bn" "bo" "br" "bs" "ca" "cs" "cy" "da" "de" "el" "en" "es" "et" "eu" "fa" "fi" "fo" "fr" "gl" "gu" "ha" "haw" "he" "hi" "hr" "ht" "hu" "hy" "id" "is" "it" "ja" "jw" "ka" "kk" "km" "kn" "ko" "la" "lb" "ln" "lo" "lt" "lv" "mg" "mi" "mk" "ml" "mn" "mr" "ms" "mt" "my" "ne" "nl" "nn" "no" "oc" "pa" "pl" "ps" "pt" "ro" "ru" "sa" "sd" "si" "sk" "sl" "sn" "so" "sq" "sr" "su" "sv" "sw" "ta" "te" "tg" "th" "tk" "tl" "tr" "tt" "uk" "ur" "uz" "vi" "yi" "yo" "zh" + ]; + example = "en"; + description = mdDoc '' + The language used to to parse words and sentences. + ''; + }; + + beamSize = mkOption { + type = ints.unsigned; + default = 1; + example = 5; + description = mdDoc '' + The number of beams to use in beam search. + ''; + apply = toString; + }; + + extraArgs = mkOption { + type = listOf str; + default = [ ]; + description = mdDoc '' + Extra arguments to pass to the server commandline. + ''; + apply = escapeShellArgs; + }; + }; + } + )); + }; + }; + + config = let + inherit (lib) + mapAttrs' + mkIf + nameValuePair + ; + in mkIf (cfg.servers != {}) { + systemd.services = mapAttrs' (server: options: + nameValuePair "wyoming-faster-whisper-${server}" { + inherit (options) enable; + description = "Wyoming faster-whisper server instance ${server}"; + after = [ + "network-online.target" + ]; + wantedBy = [ + "multi-user.target" + ]; + serviceConfig = { + DynamicUser = true; + User = "wyoming-faster-whisper"; + StateDirectory = "wyoming/faster-whisper"; + # https://github.com/home-assistant/addons/blob/master/whisper/rootfs/etc/s6-overlay/s6-rc.d/whisper/run + ExecStart = '' + ${cfg.package}/bin/wyoming-faster-whisper \ + --data-dir $STATE_DIRECTORY \ + --download-dir $STATE_DIRECTORY \ + --uri ${options.uri} \ + --device ${options.device} \ + --model ${options.model} \ + --language ${options.language} \ + --beam-size ${options.beamSize} ${options.extraArgs} + ''; + CapabilityBoundingSet = ""; + DeviceAllow = if builtins.elem options.device [ "cuda" "auto" ] then [ + # https://docs.nvidia.com/dgx/pdf/dgx-os-5-user-guide.pdf + # CUDA not working? Check DeviceAllow and PrivateDevices first! + "/dev/nvidia0" + "/dev/nvidia1" + "/dev/nvidia2" + "/dev/nvidia3" + "/dev/nvidia4" + "/dev/nvidia-caps/nvidia-cap1" + "/dev/nvidia-caps/nvidia-cap2" + "/dev/nvidiactl" + "/dev/nvidia-modeset" + "/dev/nvidia-uvm" + "/dev/nvidia-uvm-tools" + ] else ""; + DevicePolicy = "closed"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + PrivateUsers = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectControlGroups = true; + ProtectProc = "invisible"; + ProcSubset = "pid"; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_UNIX" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "~@privileged" + ]; + UMask = "0077"; + }; + }) cfg.servers; + }; +} diff --git a/nixos/modules/services/home-automation/wyoming/openwakeword.nix b/nixos/modules/services/home-automation/wyoming/openwakeword.nix new file mode 100644 index 0000000000000..252f70be2baa4 --- /dev/null +++ b/nixos/modules/services/home-automation/wyoming/openwakeword.nix @@ -0,0 +1,163 @@ +{ config +, lib +, pkgs +, ... +}: + +let + cfg = config.services.wyoming.openwakeword; + + inherit (lib) + concatStringsSep + concatMapStringsSep + escapeShellArgs + mkOption + mdDoc + mkEnableOption + mkIf + mkPackageOption + mkRemovedOptionModule + types + ; + + inherit (builtins) + toString + ; + +in + +{ + imports = [ + (mkRemovedOptionModule [ "services" "wyoming" "openwakeword" "models" ] "Configuring models has been removed, they are now dynamically discovered and loaded at runtime") + ]; + + meta.buildDocsInSandbox = false; + + options.services.wyoming.openwakeword = with types; { + enable = mkEnableOption (mdDoc "Wyoming openWakeWord server"); + + package = mkPackageOption pkgs "wyoming-openwakeword" { }; + + uri = mkOption { + type = strMatching "^(tcp|unix)://.*$"; + default = "tcp://0.0.0.0:10400"; + example = "tcp://192.0.2.1:5000"; + description = mdDoc '' + URI to bind the wyoming server to. + ''; + }; + + customModelsDirectories = mkOption { + type = listOf types.path; + default = []; + description = lib.mdDoc '' + Paths to directories with custom wake word models (*.tflite model files). + ''; + }; + + preloadModels = mkOption { + type = listOf str; + default = [ + "ok_nabu" + ]; + example = [ + # wyoming_openwakeword/models/*.tflite + "alexa" + "hey_jarvis" + "hey_mycroft" + "hey_rhasspy" + "ok_nabu" + ]; + description = mdDoc '' + List of wake word models to preload after startup. + ''; + }; + + threshold = mkOption { + type = float; + default = 0.5; + description = mdDoc '' + 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 = int; + default = 1; + description = mdDoc '' + Number of activations before a detection is registered. + + A higher trigger level means fewer detections. + ''; + apply = toString; + }; + + extraArgs = mkOption { + type = listOf str; + default = [ ]; + description = mdDoc '' + Extra arguments to pass to the server commandline. + ''; + apply = escapeShellArgs; + }; + }; + + config = mkIf cfg.enable { + systemd.services."wyoming-openwakeword" = { + description = "Wyoming openWakeWord server"; + after = [ + "network-online.target" + ]; + wantedBy = [ + "multi-user.target" + ]; + serviceConfig = { + DynamicUser = true; + User = "wyoming-openwakeword"; + # https://github.com/home-assistant/addons/blob/master/openwakeword/rootfs/etc/s6-overlay/s6-rc.d/openwakeword/run + ExecStart = concatStringsSep " " [ + "${cfg.package}/bin/wyoming-openwakeword" + "--uri ${cfg.uri}" + (concatMapStringsSep " " (model: "--preload-model ${model}") cfg.preloadModels) + (concatMapStringsSep " " (dir: "--custom-model-dir ${toString dir}") cfg.customModelsDirectories) + "--threshold ${cfg.threshold}" + "--trigger-level ${cfg.triggerLevel}" + "${cfg.extraArgs}" + ]; + CapabilityBoundingSet = ""; + DeviceAllow = ""; + DevicePolicy = "closed"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + PrivateDevices = true; + PrivateUsers = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectControlGroups = true; + ProtectProc = "invisible"; + ProcSubset = "all"; # reads /proc/cpuinfo + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_UNIX" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RuntimeDirectory = "wyoming-openwakeword"; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "~@privileged" + ]; + UMask = "0077"; + }; + }; + }; +} diff --git a/nixos/modules/services/home-automation/wyoming/piper.nix b/nixos/modules/services/home-automation/wyoming/piper.nix new file mode 100644 index 0000000000000..2828fdf078921 --- /dev/null +++ b/nixos/modules/services/home-automation/wyoming/piper.nix @@ -0,0 +1,175 @@ +{ config +, lib +, pkgs +, ... +}: + +let + cfg = config.services.wyoming.piper; + + inherit (lib) + escapeShellArgs + mkOption + mdDoc + mkEnableOption + mkPackageOption + types + ; + + inherit (builtins) + toString + ; + +in + +{ + meta.buildDocsInSandbox = false; + + options.services.wyoming.piper = with types; { + package = mkPackageOption pkgs "wyoming-piper" { }; + + servers = mkOption { + default = {}; + description = mdDoc '' + Attribute set of piper instances to spawn. + ''; + type = types.attrsOf (types.submodule ( + { ... }: { + options = { + enable = mkEnableOption (mdDoc "Wyoming Piper server"); + + piper = mkPackageOption pkgs "piper-tts" { }; + + voice = mkOption { + type = str; + example = "en-us-ryan-medium"; + description = mdDoc '' + Name of the voice model to use. See the following website for samples: + https://rhasspy.github.io/piper-samples/ + ''; + }; + + uri = mkOption { + type = strMatching "^(tcp|unix)://.*$"; + example = "tcp://0.0.0.0:10200"; + description = mdDoc '' + URI to bind the wyoming server to. + ''; + }; + + speaker = mkOption { + type = ints.unsigned; + default = 0; + description = mdDoc '' + ID of a specific speaker in a multi-speaker model. + ''; + apply = toString; + }; + + noiseScale = mkOption { + type = float; + default = 0.667; + description = mdDoc '' + Generator noise value. + ''; + apply = toString; + }; + + noiseWidth = mkOption { + type = float; + default = 0.333; + description = mdDoc '' + Phoneme width noise value. + ''; + apply = toString; + }; + + lengthScale = mkOption { + type = float; + default = 1.0; + description = mdDoc '' + Phoneme length value. + ''; + apply = toString; + }; + + extraArgs = mkOption { + type = listOf str; + default = [ ]; + description = mdDoc '' + Extra arguments to pass to the server commandline. + ''; + apply = escapeShellArgs; + }; + }; + } + )); + }; + }; + + config = let + inherit (lib) + mapAttrs' + mkIf + nameValuePair + ; + in mkIf (cfg.servers != {}) { + systemd.services = mapAttrs' (server: options: + nameValuePair "wyoming-piper-${server}" { + inherit (options) enable; + description = "Wyoming Piper server instance ${server}"; + after = [ + "network-online.target" + ]; + wantedBy = [ + "multi-user.target" + ]; + serviceConfig = { + DynamicUser = true; + User = "wyoming-piper"; + StateDirectory = "wyoming/piper"; + # https://github.com/home-assistant/addons/blob/master/piper/rootfs/etc/s6-overlay/s6-rc.d/piper/run + ExecStart = '' + ${cfg.package}/bin/wyoming-piper \ + --data-dir $STATE_DIRECTORY \ + --download-dir $STATE_DIRECTORY \ + --uri ${options.uri} \ + --piper ${options.piper}/bin/piper \ + --voice ${options.voice} \ + --speaker ${options.speaker} \ + --length-scale ${options.lengthScale} \ + --noise-scale ${options.noiseScale} \ + --noise-w ${options.noiseWidth} ${options.extraArgs} + ''; + CapabilityBoundingSet = ""; + DeviceAllow = ""; + DevicePolicy = "closed"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + PrivateDevices = true; + PrivateUsers = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectControlGroups = true; + ProtectProc = "invisible"; + ProcSubset = "pid"; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_UNIX" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "~@privileged" + ]; + UMask = "0077"; + }; + }) cfg.servers; + }; +} -- cgit 1.4.1