add voice option to home-assistant
This commit is contained in:
parent
2a18c0b28f
commit
710428c5a0
5 changed files with 272 additions and 13 deletions
|
@ -35,11 +35,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1714076141,
|
"lastModified": 1714253743,
|
||||||
"narHash": "sha256-Drmja/f5MRHZCskS6mvzFqxEaZMeciScCTFxWVLqWEY=",
|
"narHash": "sha256-mdTQw2XlariysyScCv2tTE45QSU9v/ezLcHJ22f0Nxc=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "7bb2ccd8cdc44c91edba16c48d2c8f331fb3d856",
|
"rev": "58a1abdbae3217ca6b702f03d3b35125d88a2994",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
|
@ -111,6 +111,7 @@
|
||||||
// (vm_test "audiobookshelf" ./test/vm/audiobookshelf.nix)
|
// (vm_test "audiobookshelf" ./test/vm/audiobookshelf.nix)
|
||||||
// (vm_test "authelia" ./test/vm/authelia.nix)
|
// (vm_test "authelia" ./test/vm/authelia.nix)
|
||||||
// (vm_test "grocy" ./test/vm/grocy.nix)
|
// (vm_test "grocy" ./test/vm/grocy.nix)
|
||||||
|
// (vm_test "home-assistant" ./test/vm/home-assistant.nix)
|
||||||
// (vm_test "jellyfin" ./test/vm/jellyfin.nix)
|
// (vm_test "jellyfin" ./test/vm/jellyfin.nix)
|
||||||
// (vm_test "ldap" ./test/vm/ldap.nix)
|
// (vm_test "ldap" ./test/vm/ldap.nix)
|
||||||
// (vm_test "lib" ./test/vm/lib.nix)
|
// (vm_test "lib" ./test/vm/lib.nix)
|
||||||
|
|
|
@ -29,12 +29,11 @@ rec {
|
||||||
mkdir -p $(dirname ${templatePath})
|
mkdir -p $(dirname ${templatePath})
|
||||||
ln -fs ${file} ${templatePath}
|
ln -fs ${file} ${templatePath}
|
||||||
rm -f ${resultPath}
|
rm -f ${resultPath}
|
||||||
if [ -z "${sedPatterns}" ]; then
|
'' + (if sedPatterns == "" then ''
|
||||||
cat ${templatePath} > ${resultPath}
|
cat ${templatePath} > ${resultPath}
|
||||||
else
|
'' else ''
|
||||||
${pkgs.gnused}/bin/sed ${sedPatterns} ${templatePath} > ${resultPath}
|
${pkgs.gnused}/bin/sed ${sedPatterns} ${templatePath} > ${resultPath}
|
||||||
fi
|
'');
|
||||||
'';
|
|
||||||
|
|
||||||
secretFileType = lib.types.submodule {
|
secretFileType = lib.types.submodule {
|
||||||
options = {
|
options = {
|
||||||
|
|
|
@ -136,6 +136,42 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
voice = lib.mkOption {
|
||||||
|
description = "Options related to voice service.";
|
||||||
|
default = {};
|
||||||
|
type = lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
speech-to-text = lib.mkOption {
|
||||||
|
description = ''
|
||||||
|
Wyoming piper servers.
|
||||||
|
|
||||||
|
https://search.nixos.org/options?channel=23.11&from=0&size=50&sort=relevance&type=packages&query=services.wyoming.piper.servers
|
||||||
|
'';
|
||||||
|
type = lib.types.attrsOf lib.types.anything;
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
text-to-speech = lib.mkOption {
|
||||||
|
description = ''
|
||||||
|
Wyoming faster-whisper servers.
|
||||||
|
|
||||||
|
https://search.nixos.org/options?channel=23.11&from=0&size=50&sort=relevance&type=packages&query=services.wyoming.faster-whisper.servers
|
||||||
|
'';
|
||||||
|
type = lib.types.attrsOf lib.types.anything;
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
wakeword = lib.mkOption {
|
||||||
|
description = ''
|
||||||
|
Wyoming open wakework servers.
|
||||||
|
|
||||||
|
https://search.nixos.org/options?channel=23.11&from=0&size=50&sort=relevance&type=packages&query=services.wyoming.openwakeword
|
||||||
|
'';
|
||||||
|
type = lib.types.anything;
|
||||||
|
default = { enable = false; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
backupCfg = lib.mkOption {
|
backupCfg = lib.mkOption {
|
||||||
type = lib.types.anything;
|
type = lib.types.anything;
|
||||||
description = "Backup configuration for home-assistant";
|
description = "Backup configuration for home-assistant";
|
||||||
|
@ -258,6 +294,10 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
services.wyoming.piper.servers = cfg.voice.text-to-speech;
|
||||||
|
services.wyoming.faster-whisper.servers = cfg.voice.speech-to-text;
|
||||||
|
services.wyoming.openwakeword = cfg.voice.wakeword;
|
||||||
|
|
||||||
services.nginx.virtualHosts."${fqdn}" = {
|
services.nginx.virtualHosts."${fqdn}" = {
|
||||||
http2 = true;
|
http2 = true;
|
||||||
|
|
||||||
|
@ -274,7 +314,7 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.home-assistant.preStart = lib.mkIf cfg.ldap.enable (
|
systemd.services.home-assistant.preStart = lib.mkIf cfg.enable (
|
||||||
let
|
let
|
||||||
onboarding = pkgs.writeText "onboarding" ''
|
onboarding = pkgs.writeText "onboarding" ''
|
||||||
{
|
{
|
||||||
|
@ -289,16 +329,16 @@ in
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'';
|
'';
|
||||||
storage = "${config.services.home-assistant.configDir}";
|
configDir = "${config.services.home-assistant.configDir}";
|
||||||
file = "${storage}/.storage/onboarding";
|
file = "${configDir}/.storage/onboarding";
|
||||||
in
|
in
|
||||||
''
|
''
|
||||||
if ! -f ${file}; then
|
if ! [ -f ${file} ]; then
|
||||||
mkdir -p ${storage} && cp ${onboarding} ${file}
|
mkdir -p ${configDir}/.storage && cp ${onboarding} ${file}
|
||||||
fi
|
fi
|
||||||
'' + shblib.replaceSecrets {
|
'' + shblib.replaceSecrets {
|
||||||
userConfig = cfg.config;
|
userConfig = cfg.config;
|
||||||
resultPath = "${config.services.home-assistant.configDir}/secrets.yaml";
|
resultPath = "${configDir}/secrets.yaml";
|
||||||
generator = name: value: lib.generators.toYAML {} value;
|
generator = name: value: lib.generators.toYAML {} value;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
219
test/vm/home-assistant.nix
Normal file
219
test/vm/home-assistant.nix
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
{ pkgs, lib, ... }:
|
||||||
|
let
|
||||||
|
pkgs' = pkgs;
|
||||||
|
|
||||||
|
commonTestScript = { nodes, extraPorts ? [], ... }:
|
||||||
|
let
|
||||||
|
hasSSL = !(isNull nodes.server.shb.home-assistant.ssl);
|
||||||
|
fqdn = if hasSSL then "https://ha.example.com" else "http://ha.example.com";
|
||||||
|
in
|
||||||
|
''
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import pathlib
|
||||||
|
|
||||||
|
start_all()
|
||||||
|
server.wait_for_unit("home-assistant.service")
|
||||||
|
server.wait_for_open_port(${toString nodes.server.services.home-assistant.config.http.server_port})
|
||||||
|
''
|
||||||
|
+ lib.concatMapStringsSep "\n" (port: ''
|
||||||
|
server.wait_for_open_port(${toString port})
|
||||||
|
'') extraPorts
|
||||||
|
+ ''
|
||||||
|
|
||||||
|
if ${if hasSSL then "True" else "False"}:
|
||||||
|
server.copy_from_vm("/etc/ssl/certs/ca-certificates.crt")
|
||||||
|
client.succeed("rm -r /etc/ssl/certs")
|
||||||
|
client.copy_from_host(str(pathlib.Path(os.environ.get("out", os.getcwd())) / "ca-certificates.crt"), "/etc/ssl/certs/ca-certificates.crt")
|
||||||
|
|
||||||
|
def curl(target, format, endpoint, succeed=True):
|
||||||
|
return json.loads(target.succeed(
|
||||||
|
"curl --fail-with-body --silent --show-error --output /dev/null --location"
|
||||||
|
+ " --connect-to ha.example.com:443:server:443"
|
||||||
|
+ " --connect-to ha.example.com:80:server:80"
|
||||||
|
+ f" --write-out '{format}'"
|
||||||
|
+ " " + endpoint
|
||||||
|
))
|
||||||
|
|
||||||
|
print(server.succeed("cat /var/lib/hass/configuration.yaml"))
|
||||||
|
print(server.succeed("systemctl cat home-assistant"))
|
||||||
|
print(server.succeed("cat ''$(systemctl cat home-assistant | grep ExecStartPre | cut -d= -f2)"))
|
||||||
|
print(server.succeed("cat /var/lib/hass/secrets.yaml.template"))
|
||||||
|
print(server.succeed("cat /var/lib/hass/secrets.yaml"))
|
||||||
|
|
||||||
|
with subtest("access"):
|
||||||
|
response = curl(client, """{"code":%{response_code}}""", "${fqdn}")
|
||||||
|
|
||||||
|
if response['code'] != 200:
|
||||||
|
raise Exception(f"Code is {response['code']}")
|
||||||
|
'';
|
||||||
|
|
||||||
|
modules = {
|
||||||
|
base = {
|
||||||
|
imports = [
|
||||||
|
(pkgs'.path + "/nixos/modules/profiles/headless.nix")
|
||||||
|
(pkgs'.path + "/nixos/modules/profiles/qemu-guest.nix")
|
||||||
|
../../modules/blocks/nginx.nix
|
||||||
|
../../modules/services/home-assistant.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
# Nginx port.
|
||||||
|
networking.firewall.allowedTCPPorts = [ 80 ];
|
||||||
|
# VM needs a bit more memory than default.
|
||||||
|
# virtualisation.memorySize = 4096;
|
||||||
|
};
|
||||||
|
|
||||||
|
basic = {
|
||||||
|
imports = [
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
shb.backup = lib.mkOption { type = lib.types.anything; };
|
||||||
|
shb.authelia = lib.mkOption { type = lib.types.anything; };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
shb.home-assistant = {
|
||||||
|
enable = true;
|
||||||
|
domain = "example.com";
|
||||||
|
subdomain = "ha";
|
||||||
|
|
||||||
|
config = {
|
||||||
|
name = "SHB Test";
|
||||||
|
country = "BE"; # https://en.wikipedia.org/wiki/ISO_3166-1
|
||||||
|
latitude = "0";
|
||||||
|
longitude = "0";
|
||||||
|
time_zone.source = pkgs.writeText "timeZoneSecret" "UTC"; # https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
||||||
|
unit_system = "metric";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
ldap = { config, ... }: {
|
||||||
|
imports = [
|
||||||
|
../../modules/blocks/ldap.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
shb.ldap = {
|
||||||
|
enable = true;
|
||||||
|
domain = "example.com";
|
||||||
|
subdomain = "ldap";
|
||||||
|
ldapPort = 3890;
|
||||||
|
webUIListenPort = 17170;
|
||||||
|
dcdomain = "dc=example,dc=com";
|
||||||
|
ldapUserPasswordFile = pkgs.writeText "ldapUserPassword" "ldapUserPassword";
|
||||||
|
jwtSecretFile = pkgs.writeText "jwtSecret" "jwtSecret";
|
||||||
|
};
|
||||||
|
|
||||||
|
shb.home-assistant = {
|
||||||
|
ldap = {
|
||||||
|
enable = true;
|
||||||
|
host = "127.0.0.1";
|
||||||
|
port = config.shb.ldap.ldapPort;
|
||||||
|
userGroup = "homeassistant_user";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
voice = {
|
||||||
|
shb.home-assistant.voice.text-to-speech = {
|
||||||
|
"fr" = {
|
||||||
|
enable = true;
|
||||||
|
voice = "fr-siwis-medium";
|
||||||
|
uri = "tcp://0.0.0.0:10200";
|
||||||
|
speaker = 0;
|
||||||
|
};
|
||||||
|
"en" = {
|
||||||
|
enable = true;
|
||||||
|
voice = "en_GB-alba-medium";
|
||||||
|
uri = "tcp://0.0.0.0:10201";
|
||||||
|
speaker = 0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
shb.home-assistant.voice.speech-to-text = {
|
||||||
|
"tiny-fr" = {
|
||||||
|
enable = true;
|
||||||
|
model = "base-int8";
|
||||||
|
language = "fr";
|
||||||
|
uri = "tcp://0.0.0.0:10300";
|
||||||
|
device = "cpu";
|
||||||
|
};
|
||||||
|
"tiny-en" = {
|
||||||
|
enable = true;
|
||||||
|
model = "base-int8";
|
||||||
|
language = "en";
|
||||||
|
uri = "tcp://0.0.0.0:10301";
|
||||||
|
device = "cpu";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
shb.home-assistant.voice.wakeword = {
|
||||||
|
enable = true;
|
||||||
|
uri = "tcp://127.0.0.1:10400";
|
||||||
|
preloadModels = [
|
||||||
|
"alexa"
|
||||||
|
"hey_jarvis"
|
||||||
|
"hey_mycroft"
|
||||||
|
"hey_rhasspy"
|
||||||
|
"ok_nabu"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
basic = pkgs.testers.runNixOSTest {
|
||||||
|
name = "home-assistant-basic";
|
||||||
|
|
||||||
|
nodes.server = { config, pkgs, ... }: {
|
||||||
|
imports = [
|
||||||
|
modules.base
|
||||||
|
modules.basic
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
nodes.client = {};
|
||||||
|
|
||||||
|
testScript = commonTestScript;
|
||||||
|
};
|
||||||
|
|
||||||
|
ldap = pkgs.testers.runNixOSTest {
|
||||||
|
name = "home-assistant-ldap";
|
||||||
|
|
||||||
|
nodes.server = { config, pkgs, ... }: {
|
||||||
|
imports = [
|
||||||
|
modules.base
|
||||||
|
modules.basic
|
||||||
|
modules.ldap
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
nodes.client = {};
|
||||||
|
|
||||||
|
testScript = commonTestScript;
|
||||||
|
};
|
||||||
|
|
||||||
|
voice = pkgs.testers.runNixOSTest {
|
||||||
|
name = "home-assistant-basic";
|
||||||
|
|
||||||
|
nodes.server = { config, pkgs, ... }: {
|
||||||
|
imports = [
|
||||||
|
modules.base
|
||||||
|
modules.basic
|
||||||
|
modules.voice
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
nodes.client = {};
|
||||||
|
|
||||||
|
testScript = { nodes, ... }: commonTestScript {
|
||||||
|
inherit nodes;
|
||||||
|
extraPorts = [
|
||||||
|
10200
|
||||||
|
10201
|
||||||
|
# 10300
|
||||||
|
# 10301
|
||||||
|
10400
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in a new issue