feat(hass): use fork of wyoming-oww
All checks were successful
Discord / discord commits (push) Has been skipped
All checks were successful
Discord / discord commits (push) Has been skipped
This commit is contained in:
parent
dd4226e10f
commit
de8b7fd2bb
5 changed files with 144 additions and 150 deletions
|
@ -16,21 +16,6 @@
|
||||||
"wyoming"
|
"wyoming"
|
||||||
];
|
];
|
||||||
|
|
||||||
customComponents = builtins.attrValues {
|
|
||||||
# Switched to HASS Ollama integration
|
|
||||||
# inherit (self.legacyPackages.${pkgs.system}.hass-components) home-llm;
|
|
||||||
# Home-llm prompt:
|
|
||||||
/*
|
|
||||||
You are 'Homie', a helpful AI Assistant that controls the devices in a house. Complete the following task as instructed.
|
|
||||||
|
|
||||||
The current time and date is {{ (as_timestamp(now()) | timestamp_custom("%I:%M %p on %A %B %d, %Y", "EST")) }}.
|
|
||||||
|
|
||||||
Services: {{ formatted_tools }}
|
|
||||||
Devices:
|
|
||||||
{{ formatted_devices }}
|
|
||||||
*/
|
|
||||||
};
|
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
assist_pipeline = {};
|
assist_pipeline = {};
|
||||||
conversation = {};
|
conversation = {};
|
||||||
|
@ -48,12 +33,13 @@
|
||||||
speaker = 0;
|
speaker = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
openwakeword-docker = {
|
openwakeword = {
|
||||||
enable = true;
|
enable = true;
|
||||||
uri = "127.0.0.1:10400";
|
uri = "tcp://127.0.0.1:10400";
|
||||||
|
|
||||||
|
threshold = 0.8;
|
||||||
|
|
||||||
customModelsDirectories = ["${wakewords-src}/en/yo_homie"];
|
customModelsDirectories = ["${wakewords-src}/en/yo_homie"];
|
||||||
preloadModels = ["yo_homie"];
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,157 +7,69 @@
|
||||||
inherit
|
inherit
|
||||||
(lib)
|
(lib)
|
||||||
attrNames
|
attrNames
|
||||||
escapeShellArgs
|
concatMapStringsSep
|
||||||
flatten
|
concatStringsSep
|
||||||
filterAttrs
|
filterAttrs
|
||||||
hasAttr
|
|
||||||
listToAttrs
|
listToAttrs
|
||||||
map
|
map
|
||||||
mkEnableOption
|
|
||||||
mkForce
|
mkForce
|
||||||
mkIf
|
mkIf
|
||||||
mkOption
|
mkOverride
|
||||||
nameValuePair
|
nameValuePair
|
||||||
optionals
|
optionalString
|
||||||
splitString
|
|
||||||
types
|
|
||||||
;
|
;
|
||||||
|
|
||||||
flatMap = f: list: flatten (map f list);
|
cfg = config.services.wyoming;
|
||||||
|
|
||||||
cfg = config.services.wyoming.openwakeword-docker;
|
|
||||||
in {
|
in {
|
||||||
options.services.wyoming.openwakeword-docker = {
|
config = let
|
||||||
enable = mkEnableOption "Wyoming openWakeWord server";
|
forkedPkg = import ./pkgs/wyoming-openwakeword.nix pkgs;
|
||||||
|
|
||||||
image = mkOption {
|
whisperUnitNames = attrNames (
|
||||||
type = types.package;
|
filterAttrs (_: v: v.device == "cpu") cfg.faster-whisper.servers
|
||||||
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 = {
|
|
||||||
assertions = [
|
|
||||||
{
|
|
||||||
assertion =
|
|
||||||
(cfg.enable
|
|
||||||
&& hasAttr "khepri" config
|
|
||||||
&& hasAttr "rwDataDir" config.khepri)
|
|
||||||
|| !cfg.enable;
|
|
||||||
message = ''
|
|
||||||
The module `docker` from this same flake is needed to use
|
|
||||||
this openwakeword implementation.
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
systemd.services = let
|
|
||||||
unitNames = attrNames (
|
|
||||||
filterAttrs (_: v: v.device == "cpu") config.services.wyoming.faster-whisper.servers
|
|
||||||
);
|
);
|
||||||
in
|
in {
|
||||||
listToAttrs (map (x:
|
systemd.services =
|
||||||
|
# https://github.com/felschr/nixos-config/blob/6a0f0bf76e3ae80c1e180ba6f6c7fd3b8e91d2d3/services/home-assistant/wyoming.nix#L29
|
||||||
|
mkIf (cfg.faster-whisper.servers != {})
|
||||||
|
(listToAttrs (map (x:
|
||||||
nameValuePair "wyoming-faster-whisper-${x}" {
|
nameValuePair "wyoming-faster-whisper-${x}" {
|
||||||
serviceConfig.ProcSubset = mkForce "all";
|
serviceConfig.ProcSubset = mkForce "all";
|
||||||
})
|
})
|
||||||
unitNames);
|
whisperUnitNames))
|
||||||
|
#
|
||||||
|
# openWakeWord
|
||||||
|
// mkIf (cfg.openwakeword.enable) {
|
||||||
|
wyoming-openwakeword.serviceConfig = {
|
||||||
|
MemoryDenyWriteExecute = mkForce (cfg.openwakeword.package != forkedPkg);
|
||||||
|
|
||||||
khepri = mkIf cfg.enable {
|
# changes according to https://github.com/rhasspy/wyoming-openwakeword/pull/27
|
||||||
compositions."openwakeword" = {
|
ExecStart = mkForce (concatStringsSep " " [
|
||||||
networks.default = {};
|
"${cfg.openwakeword.package}/bin/wyoming-openwakeword"
|
||||||
|
|
||||||
services."openwakeword" = {
|
"--uri ${cfg.openwakeword.uri}"
|
||||||
image = cfg.image;
|
"--threshold ${cfg.openwakeword.threshold}"
|
||||||
restart = "always";
|
|
||||||
networks = ["default"];
|
|
||||||
|
|
||||||
volumes = map (dir: "${toString dir}:${toString dir}") cfg.customModelsDirectories;
|
(concatMapStringsSep " "
|
||||||
cmd =
|
(dir: "--custom-model-dir ${toString dir}")
|
||||||
(flatMap (model: ["--preload-model" model]) cfg.preloadModels)
|
cfg.openwakeword.customModelsDirectories)
|
||||||
++ (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"];
|
# removed option https://github.com/rhasspy/wyoming-openwakeword/pull/27#issuecomment-2211822998
|
||||||
|
(optionalString
|
||||||
|
(cfg.openwakeword.package != forkedPkg)
|
||||||
|
(concatMapStringsSep " " (model: "--preload-model ${model}") cfg.openwakeword.preloadModels))
|
||||||
|
|
||||||
|
# removed option since preloading was removed
|
||||||
|
(optionalString
|
||||||
|
(cfg.openwakeword.package != forkedPkg)
|
||||||
|
"--trigger-level ${cfg.openwakeword.triggerLevel}")
|
||||||
|
|
||||||
|
"${cfg.openwakeword.extraArgs}"
|
||||||
|
]);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
services.wyoming.openwakeword = mkIf (cfg.openwakeword.enable) {
|
||||||
|
package = mkOverride 900 forkedPkg;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
43
nixosModules/wyoming-plus/pkgs/openwakeword.nix
Normal file
43
nixosModules/wyoming-plus/pkgs/openwakeword.nix
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
{
|
||||||
|
fetchFromGitHub,
|
||||||
|
fetchpatch,
|
||||||
|
python3Packages,
|
||||||
|
speexdsp-ns,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
python3Packages.buildPythonApplication rec {
|
||||||
|
pname = "openwakeword";
|
||||||
|
version = "0.6.0";
|
||||||
|
pyproject = true;
|
||||||
|
|
||||||
|
src = fetchFromGitHub {
|
||||||
|
owner = "dscripka";
|
||||||
|
repo = "openWakeWord";
|
||||||
|
rev = "v${version}";
|
||||||
|
hash = "sha256-QsXV9REAHdP0Y0fVZuU+Gt9+gcPMB60bc3DOMDYuaDM=";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Patch upstream to enable use of full tensorflow dep
|
||||||
|
pythonRemoveDeps = ["tflite-runtime"];
|
||||||
|
patches = [
|
||||||
|
(fetchpatch {
|
||||||
|
url = "https://github.com/dscripka/openWakeWord/pull/178/commits/99cd87e8898348255e864540e43bab17ce0576d6.patch";
|
||||||
|
hash = "sha256-xveMBZTcYfT8LKiiStqYjjOdUOM/v4taQzSewo97Bfc=";
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
nativeBuildInputs = with python3Packages; [
|
||||||
|
setuptools
|
||||||
|
];
|
||||||
|
|
||||||
|
propagatedBuildInputs =
|
||||||
|
(with python3Packages; [
|
||||||
|
onnxruntime
|
||||||
|
tensorflow-bin
|
||||||
|
tqdm
|
||||||
|
scipy
|
||||||
|
scikit-learn
|
||||||
|
requests
|
||||||
|
])
|
||||||
|
++ [speexdsp-ns];
|
||||||
|
}
|
21
nixosModules/wyoming-plus/pkgs/speexdsp-ns.nix
Normal file
21
nixosModules/wyoming-plus/pkgs/speexdsp-ns.nix
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
fetchFromGitHub,
|
||||||
|
python3Packages,
|
||||||
|
speexdsp,
|
||||||
|
swig,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
python3Packages.buildPythonApplication {
|
||||||
|
pname = "speexdsp-ns";
|
||||||
|
version = "0.1.2";
|
||||||
|
|
||||||
|
src = fetchFromGitHub {
|
||||||
|
owner = "TeaPoly";
|
||||||
|
repo = "speexdsp-ns-python";
|
||||||
|
rev = "8af784a230e23f4eeaa4a58111774ad0864b1f0b";
|
||||||
|
hash = "sha256-9IGhHZBlDYfGygB+fAdEDp7qeIEOWBsiLZAUFTVBxG0=";
|
||||||
|
};
|
||||||
|
|
||||||
|
nativeBuildInputs = [swig];
|
||||||
|
propagatedBuildInputs = [speexdsp];
|
||||||
|
}
|
32
nixosModules/wyoming-plus/pkgs/wyoming-openwakeword.nix
Normal file
32
nixosModules/wyoming-plus/pkgs/wyoming-openwakeword.nix
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
This package uses a `wyoming-openwakeword` fork that makes use of
|
||||||
|
the upstream `openwakeword` instead of a fork: https://github.com/rhasspy/wyoming-openwakeword/pull/27
|
||||||
|
|
||||||
|
It also enforces the python version to 3.11, because tensorflow
|
||||||
|
cannot be used with 3.12 yet.
|
||||||
|
*/
|
||||||
|
pkgs: let
|
||||||
|
pyPkgs = pkgs.python311Packages;
|
||||||
|
|
||||||
|
speexdsp-ns = pkgs.callPackage ./speexdsp-ns.nix {
|
||||||
|
python3Packages = pyPkgs;
|
||||||
|
};
|
||||||
|
|
||||||
|
openwakeword = pkgs.callPackage ./openwakeword.nix {
|
||||||
|
inherit speexdsp-ns;
|
||||||
|
python3Packages = pyPkgs;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
(pkgs.wyoming-openwakeword.override {
|
||||||
|
python3Packages = pyPkgs;
|
||||||
|
})
|
||||||
|
.overrideAttrs (o: {
|
||||||
|
src = pkgs.fetchFromGitHub {
|
||||||
|
owner = "rhasspy";
|
||||||
|
repo = "wyoming-openwakeword";
|
||||||
|
rev = "synesthesiam-20240627-openwakeword";
|
||||||
|
hash = "sha256-69oR2LHiUfx8j39nWp7XhG5xTvmOoPCLjSlH1CFvavo=";
|
||||||
|
};
|
||||||
|
|
||||||
|
propagatedBuildInputs = [openwakeword pyPkgs.wyoming];
|
||||||
|
})
|
Loading…
Reference in a new issue