at 23.11-beta 6.5 kB view raw
1{ config 2, lib 3, pkgs 4, ... 5}: 6 7let 8 cfg = config.services.homeassistant-satellite; 9 10 inherit (lib) 11 escapeShellArg 12 escapeShellArgs 13 mkOption 14 mdDoc 15 mkEnableOption 16 mkIf 17 mkPackageOptionMD 18 types 19 ; 20 21 inherit (builtins) 22 toString 23 ; 24 25 # override the package with the relevant vad dependencies 26 package = cfg.package.overridePythonAttrs (oldAttrs: { 27 propagatedBuildInputs = oldAttrs.propagatedBuildInputs 28 ++ lib.optional (cfg.vad == "webrtcvad") cfg.package.optional-dependencies.webrtc 29 ++ lib.optional (cfg.vad == "silero") cfg.package.optional-dependencies.silerovad 30 ++ lib.optional (cfg.pulseaudio.enable) cfg.package.optional-dependencies.pulseaudio; 31 }); 32 33in 34 35{ 36 meta.buildDocsInSandbox = false; 37 38 options.services.homeassistant-satellite = with types; { 39 enable = mkEnableOption (mdDoc "Home Assistant Satellite"); 40 41 package = mkPackageOptionMD pkgs "homeassistant-satellite" { }; 42 43 user = mkOption { 44 type = str; 45 example = "alice"; 46 description = mdDoc '' 47 User to run homeassistant-satellite under. 48 ''; 49 }; 50 51 group = mkOption { 52 type = str; 53 default = "users"; 54 description = mdDoc '' 55 Group to run homeassistant-satellite under. 56 ''; 57 }; 58 59 host = mkOption { 60 type = str; 61 example = "home-assistant.local"; 62 description = mdDoc '' 63 Hostname on which your Home Assistant instance can be reached. 64 ''; 65 }; 66 67 port = mkOption { 68 type = port; 69 example = 8123; 70 description = mdDoc '' 71 Port on which your Home Assistance can be reached. 72 ''; 73 apply = toString; 74 }; 75 76 protocol = mkOption { 77 type = enum [ "http" "https" ]; 78 default = "http"; 79 example = "https"; 80 description = mdDoc '' 81 The transport protocol used to connect to Home Assistant. 82 ''; 83 }; 84 85 tokenFile = mkOption { 86 type = path; 87 example = "/run/keys/hass-token"; 88 description = mdDoc '' 89 Path to a file containing a long-lived access token for your Home Assistant instance. 90 ''; 91 apply = escapeShellArg; 92 }; 93 94 sounds = { 95 awake = mkOption { 96 type = nullOr str; 97 default = null; 98 description = mdDoc '' 99 Audio file to play when the wake word is detected. 100 ''; 101 }; 102 103 done = mkOption { 104 type = nullOr str; 105 default = null; 106 description = mdDoc '' 107 Audio file to play when the voice command is done. 108 ''; 109 }; 110 }; 111 112 vad = mkOption { 113 type = enum [ "disabled" "webrtcvad" "silero" ]; 114 default = "disabled"; 115 example = "silero"; 116 description = mdDoc '' 117 Voice activity detection model. With `disabled` sound will be transmitted continously. 118 ''; 119 }; 120 121 pulseaudio = { 122 enable = mkEnableOption "recording/playback via PulseAudio or PipeWire"; 123 124 socket = mkOption { 125 type = nullOr str; 126 default = null; 127 example = "/run/user/1000/pulse/native"; 128 description = mdDoc '' 129 Path or hostname to connect with the PulseAudio server. 130 ''; 131 }; 132 133 duckingVolume = mkOption { 134 type = nullOr float; 135 default = null; 136 example = 0.4; 137 description = mdDoc '' 138 Reduce output volume (between 0 and 1) to this percentage value while recording. 139 ''; 140 }; 141 142 echoCancellation = mkEnableOption "acoustic echo cancellation"; 143 }; 144 145 extraArgs = mkOption { 146 type = listOf str; 147 default = [ ]; 148 description = mdDoc '' 149 Extra arguments to pass to the commandline. 150 ''; 151 apply = escapeShellArgs; 152 }; 153 }; 154 155 config = mkIf cfg.enable { 156 systemd.services."homeassistant-satellite" = { 157 description = "Home Assistant Satellite"; 158 after = [ 159 "network-online.target" 160 ]; 161 wants = [ 162 "network-online.target" 163 ]; 164 wantedBy = [ 165 "multi-user.target" 166 ]; 167 path = with pkgs; [ 168 ffmpeg-headless 169 ] ++ lib.optionals (!cfg.pulseaudio.enable) [ 170 alsa-utils 171 ]; 172 serviceConfig = { 173 User = cfg.user; 174 Group = cfg.group; 175 # https://github.com/rhasspy/hassio-addons/blob/master/assist_microphone/rootfs/etc/s6-overlay/s6-rc.d/assist_microphone/run 176 ExecStart = '' 177 ${package}/bin/homeassistant-satellite \ 178 --host ${cfg.host} \ 179 --port ${cfg.port} \ 180 --protocol ${cfg.protocol} \ 181 --token-file ${cfg.tokenFile} \ 182 --vad ${cfg.vad} \ 183 ${lib.optionalString cfg.pulseaudio.enable "--pulseaudio"}${lib.optionalString (cfg.pulseaudio.socket != null) "=${cfg.pulseaudio.socket}"} \ 184 ${lib.optionalString (cfg.pulseaudio.enable && cfg.pulseaudio.duckingVolume != null) "--ducking-volume=${toString cfg.pulseaudio.duckingVolume}"} \ 185 ${lib.optionalString (cfg.pulseaudio.enable && cfg.pulseaudio.echoCancellation) "--echo-cancel"} \ 186 ${lib.optionalString (cfg.sounds.awake != null) "--awake-sound=${toString cfg.sounds.awake}"} \ 187 ${lib.optionalString (cfg.sounds.done != null) "--done-sound=${toString cfg.sounds.done}"} \ 188 ${cfg.extraArgs} 189 ''; 190 CapabilityBoundingSet = ""; 191 DeviceAllow = ""; 192 DevicePolicy = "closed"; 193 LockPersonality = true; 194 MemoryDenyWriteExecute = false; # onnxruntime/capi/onnxruntime_pybind11_state.so: cannot enable executable stack as shared object requires: Operation not permitted 195 PrivateDevices = true; 196 PrivateUsers = true; 197 ProtectHome = false; # Would deny access to local pulse/pipewire server 198 ProtectHostname = true; 199 ProtectKernelLogs = true; 200 ProtectKernelModules = true; 201 ProtectKernelTunables = true; 202 ProtectControlGroups = true; 203 ProtectProc = "invisible"; 204 ProcSubset = "all"; # Error in cpuinfo: failed to parse processor information from /proc/cpuinfo 205 Restart = "always"; 206 RestrictAddressFamilies = [ 207 "AF_INET" 208 "AF_INET6" 209 "AF_UNIX" 210 ]; 211 RestrictNamespaces = true; 212 RestrictRealtime = true; 213 SupplementaryGroups = [ 214 "audio" 215 ]; 216 SystemCallArchitectures = "native"; 217 SystemCallFilter = [ 218 "@system-service" 219 "~@privileged" 220 ]; 221 UMask = "0077"; 222 }; 223 }; 224 }; 225}