First working version

This commit is contained in:
Michael Hansen
2024-01-14 22:19:01 -06:00
parent 316427948e
commit 8063f5b773
12 changed files with 740 additions and 439 deletions
+17 -212
View File
@@ -1,34 +1,15 @@
"""Command-line installer."""
import array
import math
import logging
import json
import shlex
import subprocess
import shutil
import sys
import time
from collections.abc import Sequence
from concurrent.futures import ThreadPoolExecutor, Future
from enum import Enum
from dataclasses import dataclass, fields, asdict, field
from pathlib import Path
from typing import Any, Dict, List, Tuple, Optional, Union
from typing import List, Optional
from .const import (
Settings,
PROGRAM_DIR,
LOCAL_DIR,
SatelliteType,
SERVICES_DIR,
WakeWordSystem,
)
from .whiptail import run_with_gauge, menu, ItemType, msgbox, error, passwordbox
from .satellite import configure_satellite
from .microphone import configure_microphone
from .speakers import configure_speakers
from .const import LOCAL_DIR, PROGRAM_DIR, SatelliteType, Settings
from .drivers import install_drivers
from .microphone import configure_microphone
from .satellite import configure_satellite
from .services import generate_services, install_services, stop_services
from .speakers import configure_speakers
from .wake_word import configure_wake_word
from .whiptail import ItemType, error, menu, msgbox, passwordbox, run_with_gauge
_LOGGER = logging.getLogger()
@@ -41,12 +22,6 @@ def main() -> None:
settings = Settings.load()
venv_dir = PROGRAM_DIR / ".venv"
if not venv_dir.exists():
run_with_gauge(
"Installing satellite base...", [[str(PROGRAM_DIR / "script" / "setup")]]
)
choice: Optional[str] = None
while True:
choice = main_menu(choice)
@@ -99,151 +74,17 @@ def pip_install(*args) -> List[str]:
] + list(args)
def stop_services(password: str) -> None:
stop_commands = []
for service in ("satellite", "openwakeword", "porcupine1", "snowboy"):
service_filename = f"wyoming-{service}.service"
service_path = Path("/etc/systemd/system") / service_filename
if not service_path.exists():
continue
stop_commands.append(["sudo", "-S", "systemctl", "stop", service_filename])
stop_commands.append(["sudo", "-S", "systemctl", "disable", service_filename])
run_with_gauge("Stopping Services...", stop_commands, sudo_password=password)
def generate_services(settings: Settings) -> None:
SERVICES_DIR.mkdir(parents=True, exist_ok=True)
user = subprocess.check_output(["id", "--name", "-u"], text=True).strip()
satellite_command: List[str] = [
str(PROGRAM_DIR / "script" / "run"),
"--name",
settings.satellite_name,
"--uri",
"tcp://0.0.0.0:10700",
"--mic-command",
f"arecord -D {settings.microphone_device} -q -r 16000 -c 1 -f S16_LE -t raw",
]
satellite_requires: List[str] = []
if settings.sound_device is not None:
# Audio output
satellite_command.extend(
[
"--snd-command",
f"aplay -D {settings.sound_device} -q -r 22050 -c 1 -f S16_LE -t raw",
]
)
for sound_name in settings.feedback_sounds:
# Try local/sounds first
sound_path = LOCAL_DIR / "sounds" / f"{sound_name}.wav"
if not sound_path.exists():
sound_path = PROGRAM_DIR / "sounds" / f"{sound_name}.wav"
satellite_command.extend([f"--{sound_name}-wav", str(sound_path)])
if settings.satellite_type == SatelliteType.VAD:
# Voice activity detect
satellite_command.append("--vad")
elif settings.satellite_type == SatelliteType.WAKE:
# Local wake word detection
assert settings.wake_word_system is not None, "Wake word system not set"
wake_word = settings.wake_word.get(settings.wake_word_system)
assert wake_word, "No wake word set"
wake_word_service = "wyoming-" + str(settings.wake_word_system).lower()
wake_word_dir = LOCAL_DIR / wake_word_service
wake_word_command = [
str(wake_word_dir / "script" / "run"),
"--uri",
"tcp://127.0.0.1:10400",
]
if settings.wake_word_system in (
WakeWordSystem.OPENWAKEWORD,
WakeWordSystem.SNOWBOY,
):
wake_word_command.extend(
[
"--custom-model-dir",
str(
LOCAL_DIR
/ "custom-wake-words"
/ WakeWordSystem(settings.wake_word_system).value
),
]
)
if settings.debug_enabled:
wake_word_command.append("--debug")
wake_word_command_str = shlex.join(wake_word_command)
with open(
SERVICES_DIR / f"{wake_word_service}.service", "w", encoding="utf-8"
) as service_file:
print("[Unit]", file=service_file)
print(f"Description={settings.wake_word_system}", file=service_file)
print("", file=service_file)
print("[Service]", file=service_file)
print("Type=simple", file=service_file)
print(f"User={user}", file=service_file)
print(f"ExecStart={wake_word_command_str}", file=service_file)
print(f"WorkingDirectory={wake_word_dir}", file=service_file)
print("Restart=always", file=service_file)
print("RestartSec=1", file=service_file)
print("", file=service_file)
print("[Install]", file=service_file)
print("WantedBy=default.target", file=service_file)
satellite_command.extend(
[
"--wake-uri",
"tcp://127.0.0.1:10400",
"--wake-word-name",
wake_word,
]
)
satellite_requires.append(f"{wake_word_service}.service")
if settings.debug_enabled:
satellite_command.extend(
["--debug", "--debug-recording-dir", str(LOCAL_DIR / "debug-recording")]
)
satellite_command_str = shlex.join(satellite_command)
with open(
SERVICES_DIR / "wyoming-satellite.service", "w", encoding="utf-8"
) as service_file:
print("[Unit]", file=service_file)
print("Description=Wyoming Satellite", file=service_file)
print("Wants=network-online.target", file=service_file)
print("After=network-online.target", file=service_file)
for requires in satellite_requires:
print(f"Requires={requires}", file=service_file)
print("", file=service_file)
print("[Service]", file=service_file)
print("Type=simple", file=service_file)
print(f"User={user}", file=service_file)
print(f"ExecStart={satellite_command_str}", file=service_file)
print(f"WorkingDirectory={PROGRAM_DIR}", file=service_file)
print("Restart=always", file=service_file)
print("RestartSec=1", file=service_file)
print("", file=service_file)
print("[Install]", file=service_file)
print("WantedBy=default.target", file=service_file)
def apply_settings(settings: Settings) -> None:
if settings.microphone_device is None:
if settings.mic.device is None:
msgbox("Please configure microphone")
return
if (settings.satellite.type == SatelliteType.WAKE) and (
settings.wake.system is None
):
msgbox("Please set wake word system")
return
# Satellite venv
venv_dir = PROGRAM_DIR / ".venv"
if not venv_dir.exists():
@@ -255,7 +96,7 @@ def apply_settings(settings: Settings) -> None:
return
# silero (vad)
if settings.satellite_type == SatelliteType.VAD:
if settings.satellite.type == SatelliteType.VAD:
result = run_with_gauge(
"Installing vad...",
[pip_install("-r", str(PROGRAM_DIR / "requirements_vad.txt"))],
@@ -265,7 +106,7 @@ def apply_settings(settings: Settings) -> None:
return
# webrtc (audio enhancements)
if (settings.noise_suppression_level > 0) or (settings.auto_gain > 0):
if (settings.mic.noise_suppression > 0) or (settings.mic.auto_gain > 0):
result = run_with_gauge(
"Installing audio enhancements...",
[
@@ -285,43 +126,7 @@ def apply_settings(settings: Settings) -> None:
return
stop_services(password)
# Install and run
installed_services = ["satellite"]
if settings.satellite_type == SatelliteType.WAKE:
assert settings.wake_word_system is not None, "No wake word system"
installed_services.append(
WakeWordSystem(settings.wake_word_system).value.lower()
)
install_commands = []
for service in installed_services:
service_filename = f"wyoming-{service}.service"
install_commands.append(
[
"sudo",
"-S",
"cp",
str(SERVICES_DIR / service_filename),
"/etc/systemd/system/",
]
)
install_commands.append(["sudo", "-S", "systemctl", "daemon-reload"])
# Copy first, then enable and start
for service in installed_services:
install_commands.append(["sudo", "-S", "systemctl", "enable", service_filename])
install_commands.append(["sudo", "-S", "systemctl", "start", "wyoming-satellite.service"])
success = run_with_gauge(
"Installing Services...", install_commands, sudo_password=password
)
if success:
msgbox("Successfully installed services")
else:
error("installing services")
install_services(settings, password)
# -----------------------------------------------------------------------------