at master 4.1 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 utils, 6 ... 7}: 8 9let 10 cfg = config.services.wyoming.openwakeword; 11 12 inherit (lib) 13 concatMap 14 mkOption 15 mkEnableOption 16 mkIf 17 mkPackageOption 18 types 19 ; 20 21 inherit (builtins) 22 toString 23 ; 24 25 inherit (utils) 26 escapeSystemdExecArgs 27 ; 28in 29 30{ 31 options.services.wyoming.openwakeword = with types; { 32 enable = mkEnableOption "Wyoming openWakeWord server"; 33 34 package = mkPackageOption pkgs "wyoming-openwakeword" { }; 35 36 uri = mkOption { 37 type = strMatching "^(tcp|unix)://.*$"; 38 default = "tcp://0.0.0.0:10400"; 39 example = "tcp://192.0.2.1:5000"; 40 description = '' 41 URI to bind the wyoming server to. 42 ''; 43 }; 44 45 customModelsDirectories = mkOption { 46 type = listOf types.path; 47 default = [ ]; 48 description = '' 49 Paths to directories with custom wake word models (*.tflite model files). 50 ''; 51 }; 52 53 preloadModels = mkOption { 54 type = listOf str; 55 default = [ 56 "ok_nabu" 57 ]; 58 example = [ 59 # wyoming_openwakeword/models/*.tflite 60 "alexa" 61 "hey_jarvis" 62 "hey_mycroft" 63 "hey_rhasspy" 64 "ok_nabu" 65 ]; 66 description = '' 67 List of wake word models to preload after startup. 68 ''; 69 }; 70 71 threshold = mkOption { 72 type = numbers.between 0.0 1.0; 73 default = 0.5; 74 description = '' 75 Activation threshold (0.0-1.0), where higher means fewer activations. 76 77 See trigger level for the relationship between activations and 78 wake word detections. 79 ''; 80 apply = toString; 81 }; 82 83 triggerLevel = mkOption { 84 type = ints.unsigned; 85 default = 1; 86 description = '' 87 Number of activations before a detection is registered. 88 89 A higher trigger level means fewer detections. 90 ''; 91 apply = toString; 92 }; 93 94 extraArgs = mkOption { 95 type = listOf str; 96 default = [ ]; 97 description = '' 98 Extra arguments to pass to the server commandline. 99 ''; 100 }; 101 }; 102 103 config = mkIf cfg.enable { 104 systemd.services."wyoming-openwakeword" = { 105 description = "Wyoming openWakeWord server"; 106 wants = [ 107 "network-online.target" 108 ]; 109 after = [ 110 "network-online.target" 111 ]; 112 wantedBy = [ 113 "multi-user.target" 114 ]; 115 serviceConfig = { 116 DynamicUser = true; 117 User = "wyoming-openwakeword"; 118 # https://github.com/home-assistant/addons/blob/master/openwakeword/rootfs/etc/s6-overlay/s6-rc.d/openwakeword/run 119 ExecStart = escapeSystemdExecArgs ( 120 [ 121 (lib.getExe cfg.package) 122 "--uri" 123 cfg.uri 124 "--threshold" 125 cfg.threshold 126 "--trigger-level" 127 cfg.triggerLevel 128 ] 129 ++ (concatMap (model: [ 130 "--preload-model" 131 model 132 ]) cfg.preloadModels) 133 ++ (concatMap (dir: [ 134 "--custom-model-dir" 135 (toString dir) 136 ]) cfg.customModelsDirectories) 137 ++ cfg.extraArgs 138 ); 139 CapabilityBoundingSet = ""; 140 DeviceAllow = ""; 141 DevicePolicy = "closed"; 142 LockPersonality = true; 143 MemoryDenyWriteExecute = true; 144 PrivateDevices = true; 145 PrivateUsers = true; 146 ProtectHome = true; 147 ProtectHostname = true; 148 ProtectKernelLogs = true; 149 ProtectKernelModules = true; 150 ProtectKernelTunables = true; 151 ProtectControlGroups = true; 152 ProtectProc = "invisible"; 153 ProcSubset = "all"; # reads /proc/cpuinfo 154 RestrictAddressFamilies = [ 155 "AF_INET" 156 "AF_INET6" 157 "AF_UNIX" 158 ]; 159 RestrictNamespaces = true; 160 RestrictRealtime = true; 161 RuntimeDirectory = "wyoming-openwakeword"; 162 SystemCallArchitectures = "native"; 163 SystemCallFilter = [ 164 "@system-service" 165 "~@privileged" 166 ]; 167 UMask = "0077"; 168 }; 169 }; 170 }; 171}