587713944a
GitOrigin-RevId: 6143fc5eeb9c4f00163267708e26191d1e918932
244 lines
6.2 KiB
Nix
244 lines
6.2 KiB
Nix
{ config
|
|
, lib
|
|
, pkgs
|
|
, ...
|
|
}:
|
|
|
|
let
|
|
cfg = config.services.wyoming.satellite;
|
|
|
|
inherit (lib)
|
|
elem
|
|
escapeShellArgs
|
|
getExe
|
|
literalExpression
|
|
mkOption
|
|
mkEnableOption
|
|
mkIf
|
|
mkPackageOption
|
|
optional
|
|
optionals
|
|
types
|
|
;
|
|
|
|
finalPackage = cfg.package.overridePythonAttrs (oldAttrs: {
|
|
propagatedBuildInputs = oldAttrs.propagatedBuildInputs
|
|
# for audio enhancements like auto-gain, noise suppression
|
|
++ cfg.package.optional-dependencies.webrtc
|
|
# vad is currently optional, because it is broken on aarch64-linux
|
|
++ optionals cfg.vad.enable cfg.package.optional-dependencies.silerovad;
|
|
});
|
|
in
|
|
|
|
{
|
|
meta.buildDocsInSandbox = false;
|
|
|
|
options.services.wyoming.satellite = with types; {
|
|
enable = mkEnableOption "Wyoming Satellite";
|
|
|
|
package = mkPackageOption pkgs "wyoming-satellite" { };
|
|
|
|
user = mkOption {
|
|
type = str;
|
|
example = "alice";
|
|
description = ''
|
|
User to run wyoming-satellite under.
|
|
'';
|
|
};
|
|
|
|
group = mkOption {
|
|
type = str;
|
|
default = "users";
|
|
description = ''
|
|
Group to run wyoming-satellite under.
|
|
'';
|
|
};
|
|
|
|
uri = mkOption {
|
|
type = str;
|
|
default = "tcp://0.0.0.0:10700";
|
|
description = ''
|
|
URI where wyoming-satellite will bind its socket.
|
|
'';
|
|
};
|
|
|
|
name = mkOption {
|
|
type = str;
|
|
default = config.networking.hostName;
|
|
defaultText = literalExpression ''
|
|
config.networking.hostName
|
|
'';
|
|
description = ''
|
|
Name of the satellite.
|
|
'';
|
|
};
|
|
|
|
area = mkOption {
|
|
type = nullOr str;
|
|
default = null;
|
|
example = "Kitchen";
|
|
description = ''
|
|
Area to the satellite.
|
|
'';
|
|
};
|
|
|
|
microphone = {
|
|
command = mkOption {
|
|
type = str;
|
|
default = "arecord -r 16000 -c 1 -f S16_LE -t raw";
|
|
description = ''
|
|
Program to run for audio input.
|
|
'';
|
|
};
|
|
|
|
autoGain = mkOption {
|
|
type = ints.between 0 31;
|
|
default = 5;
|
|
example = 15;
|
|
description = ''
|
|
Automatic gain control in dbFS, with 31 being the loudest value. Set to 0 to disable.
|
|
'';
|
|
};
|
|
|
|
noiseSuppression = mkOption {
|
|
type = ints.between 0 4;
|
|
default = 2;
|
|
example = 3;
|
|
description = ''
|
|
Noise suppression level with 4 being the maximum suppression,
|
|
which may cause audio distortion. Set to 0 to disable.
|
|
'';
|
|
};
|
|
};
|
|
|
|
sound = {
|
|
command = mkOption {
|
|
type = nullOr str;
|
|
default = "aplay -r 22050 -c 1 -f S16_LE -t raw";
|
|
description = ''
|
|
Program to run for sound output.
|
|
'';
|
|
};
|
|
};
|
|
|
|
sounds = {
|
|
awake = mkOption {
|
|
type = nullOr path;
|
|
default = null;
|
|
description = ''
|
|
Path to audio file in WAV format to play when wake word is detected.
|
|
'';
|
|
};
|
|
|
|
done = mkOption {
|
|
type = nullOr path;
|
|
default = null;
|
|
description = ''
|
|
Path to audio file in WAV format to play when voice command recording has ended.
|
|
'';
|
|
};
|
|
};
|
|
|
|
vad = {
|
|
enable = mkOption {
|
|
type = bool;
|
|
default = true;
|
|
description = ''
|
|
Whether to enable voice activity detection.
|
|
|
|
Enabling will result in only streaming audio, when speech gets
|
|
detected.
|
|
'';
|
|
};
|
|
};
|
|
|
|
extraArgs = mkOption {
|
|
type = listOf str;
|
|
default = [ ];
|
|
description = ''
|
|
Extra arguments to pass to the executable.
|
|
|
|
Check `wyoming-satellite --help` for possible options.
|
|
'';
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
systemd.services."wyoming-satellite" = {
|
|
description = "Wyoming Satellite";
|
|
after = [
|
|
"network-online.target"
|
|
"sound.target"
|
|
];
|
|
wants = [
|
|
"network-online.target"
|
|
"sound.target"
|
|
];
|
|
wantedBy = [
|
|
"multi-user.target"
|
|
];
|
|
path = with pkgs; [
|
|
alsa-utils
|
|
];
|
|
script = let
|
|
optionalParam = param: argument: optionals (!elem argument [ null 0 false ]) [
|
|
param argument
|
|
];
|
|
in ''
|
|
export XDG_RUNTIME_DIR=/run/user/$UID
|
|
${escapeShellArgs ([
|
|
(getExe finalPackage)
|
|
"--uri" cfg.uri
|
|
"--name" cfg.name
|
|
"--mic-command" cfg.microphone.command
|
|
]
|
|
++ optionalParam "--mic-auto-gain" cfg.microphone.autoGain
|
|
++ optionalParam "--mic-noise-suppression" cfg.microphone.noiseSuppression
|
|
++ optionalParam "--area" cfg.area
|
|
++ optionalParam "--snd-command" cfg.sound.command
|
|
++ optionalParam "--awake-wav" cfg.sounds.awake
|
|
++ optionalParam "--done-wav" cfg.sounds.done
|
|
++ optional cfg.vad.enable "--vad"
|
|
++ cfg.extraArgs)}
|
|
'';
|
|
serviceConfig = {
|
|
User = cfg.user;
|
|
Group = cfg.group;
|
|
# https://github.com/rhasspy/hassio-addons/blob/master/assist_microphone/rootfs/etc/s6-overlay/s6-rc.d/assist_microphone/run
|
|
CapabilityBoundingSet = "";
|
|
DeviceAllow = "";
|
|
DevicePolicy = "closed";
|
|
LockPersonality = true;
|
|
MemoryDenyWriteExecute = false; # onnxruntime/capi/onnxruntime_pybind11_state.so: cannot enable executable stack as shared object requires: Operation not permitted
|
|
PrivateDevices = true;
|
|
PrivateUsers = true;
|
|
ProtectHome = false; # Would deny access to local pulse/pipewire server
|
|
ProtectHostname = true;
|
|
ProtectKernelLogs = true;
|
|
ProtectKernelModules = true;
|
|
ProtectKernelTunables = true;
|
|
ProtectControlGroups = true;
|
|
ProtectProc = "invisible";
|
|
ProcSubset = "all"; # Error in cpuinfo: failed to parse processor information from /proc/cpuinfo
|
|
Restart = "always";
|
|
RestrictAddressFamilies = [
|
|
"AF_INET"
|
|
"AF_INET6"
|
|
"AF_UNIX"
|
|
"AF_NETLINK"
|
|
];
|
|
RestrictNamespaces = true;
|
|
RestrictRealtime = true;
|
|
SupplementaryGroups = [
|
|
"audio"
|
|
];
|
|
SystemCallArchitectures = "native";
|
|
SystemCallFilter = [
|
|
"@system-service"
|
|
"~@privileged"
|
|
];
|
|
UMask = "0077";
|
|
};
|
|
};
|
|
};
|
|
}
|