Add vad
This commit is contained in:
+322
-20
@@ -94,6 +94,12 @@ def main() -> None:
|
|||||||
|
|
||||||
settings = Settings.load()
|
settings = Settings.load()
|
||||||
|
|
||||||
|
venv_dir = _PROGRAM_DIR / ".venv"
|
||||||
|
if not venv_dir.exists():
|
||||||
|
run_with_gauge(
|
||||||
|
"Installing satellite base...", [[str(_PROGRAM_DIR / "script" / "setup")]]
|
||||||
|
)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
choice = main_menu(settings)
|
choice = main_menu(settings)
|
||||||
|
|
||||||
@@ -107,8 +113,10 @@ def main() -> None:
|
|||||||
set_satellite_name(settings)
|
set_satellite_name(settings)
|
||||||
elif choice == "type":
|
elif choice == "type":
|
||||||
set_satellite_type(settings)
|
set_satellite_type(settings)
|
||||||
elif choice == "services":
|
elif choice == "generate":
|
||||||
pass
|
generate_services(settings)
|
||||||
|
elif choice == "install":
|
||||||
|
install_services(settings)
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -126,7 +134,8 @@ def main_menu(settings: Settings) -> Optional[str]:
|
|||||||
[
|
[
|
||||||
("name", "Set Satellite Name"),
|
("name", "Set Satellite Name"),
|
||||||
("type", "Set Satellite Type"),
|
("type", "Set Satellite Type"),
|
||||||
("services", "Generate Services"),
|
("generate", "Generate Services"),
|
||||||
|
("install", "Install Services"),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -197,8 +206,17 @@ def configure_microphone(settings: Settings) -> None:
|
|||||||
"This will take a while and require a reboot. "
|
"This will take a while and require a reboot. "
|
||||||
"Continue?"
|
"Continue?"
|
||||||
):
|
):
|
||||||
subprocess.check_call(
|
password = paswordbox("sudo password:")
|
||||||
["sudo", str(_PROGRAM_DIR / "etc" / "install-respeaker-drivers.sh")]
|
run_with_gauge(
|
||||||
|
"Installing drivers...",
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"sudo",
|
||||||
|
"-S",
|
||||||
|
str(_PROGRAM_DIR / "etc" / "install-respeaker-drivers.sh"),
|
||||||
|
]
|
||||||
|
],
|
||||||
|
sudo_password=password,
|
||||||
)
|
)
|
||||||
|
|
||||||
msgbox(
|
msgbox(
|
||||||
@@ -360,10 +378,9 @@ def configure_wake_word(settings: Settings) -> None:
|
|||||||
settings.wake_word_system,
|
settings.wake_word_system,
|
||||||
)
|
)
|
||||||
if wake_word_system is not None:
|
if wake_word_system is not None:
|
||||||
settings.wake_word_system = wake_word_system
|
install_wake_word(settings, wake_word_system)
|
||||||
install_wake_word(settings)
|
|
||||||
elif choice == "wake_word":
|
elif choice == "wake_word":
|
||||||
pass
|
select_wake_word(settings)
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -383,10 +400,8 @@ def wake_word_menu(settings: Settings) -> Optional[str]:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def install_wake_word(settings: Settings) -> None:
|
def install_wake_word(settings: Settings, wake_word_system: WakeWordSystem) -> None:
|
||||||
if settings.wake_word_system == WakeWordSystem.OPENWAKEWORD:
|
if wake_word_system == WakeWordSystem.OPENWAKEWORD:
|
||||||
settings.wake_word.setdefault(WakeWordSystem.OPENWAKEWORD, "ok_nabu")
|
|
||||||
|
|
||||||
oww_dir = _LOCAL_DIR / "wyoming-openwakeword"
|
oww_dir = _LOCAL_DIR / "wyoming-openwakeword"
|
||||||
if not oww_dir.exists():
|
if not oww_dir.exists():
|
||||||
success = run_with_gauge(
|
success = run_with_gauge(
|
||||||
@@ -410,17 +425,153 @@ def install_wake_word(settings: Settings) -> None:
|
|||||||
"See local/installer.log for details."
|
"See local/installer.log for details."
|
||||||
)
|
)
|
||||||
|
|
||||||
settings.wake_word_system = None
|
|
||||||
settings.save()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
shutil.rmtree(oww_dir)
|
shutil.rmtree(oww_dir)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
elif settings.wake_word_system == WakeWordSystem.PORCUPINE1:
|
|
||||||
|
return
|
||||||
|
|
||||||
|
settings.wake_word_system = wake_word_system
|
||||||
|
settings.wake_word.setdefault(WakeWordSystem.OPENWAKEWORD, "ok_nabu")
|
||||||
|
settings.save()
|
||||||
|
elif wake_word_system == WakeWordSystem.PORCUPINE1:
|
||||||
|
porcupine1_dir = _LOCAL_DIR / "wyoming-porcupine1"
|
||||||
|
if not porcupine1_dir.exists():
|
||||||
|
success = run_with_gauge(
|
||||||
|
"Installing porcupine1",
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"git",
|
||||||
|
"clone",
|
||||||
|
"https://github.com/rhasspy/wyoming-porcupine1.git",
|
||||||
|
str(porcupine1_dir),
|
||||||
|
],
|
||||||
|
[str(porcupine1_dir / "script" / "setup")],
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
msgbox("porcupine1 installed successfully")
|
||||||
|
else:
|
||||||
|
msgbox(
|
||||||
|
"An error occurred while installing porcupine1. "
|
||||||
|
"See local/installer.log for details."
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
shutil.rmtree(porcupine1_dir)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
settings.wake_word_system = wake_word_system
|
||||||
settings.wake_word.setdefault(WakeWordSystem.PORCUPINE1, "porcupine")
|
settings.wake_word.setdefault(WakeWordSystem.PORCUPINE1, "porcupine")
|
||||||
|
settings.save()
|
||||||
elif settings.wake_word_system == WakeWordSystem.SNOWBOY:
|
elif settings.wake_word_system == WakeWordSystem.SNOWBOY:
|
||||||
|
snowboy_dir = _LOCAL_DIR / "wyoming-snowboy"
|
||||||
|
if not snowboy_dir.exists():
|
||||||
|
success = run_with_gauge(
|
||||||
|
"Installing snowboy",
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"git",
|
||||||
|
"clone",
|
||||||
|
"https://github.com/rhasspy/wyoming-snowboy.git",
|
||||||
|
str(snowboy_dir),
|
||||||
|
],
|
||||||
|
[str(snowboy_dir / "script" / "setup")],
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
msgbox("snowboy installed successfully")
|
||||||
|
else:
|
||||||
|
msgbox(
|
||||||
|
"An error occurred while installing snowboy. "
|
||||||
|
"See local/installer.log for details."
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
shutil.rmtree(snowboy_dir)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
settings.wake_word_system = wake_word_system
|
||||||
settings.wake_word.setdefault(WakeWordSystem.SNOWBOY, "snowboy")
|
settings.wake_word.setdefault(WakeWordSystem.SNOWBOY, "snowboy")
|
||||||
|
settings.save()
|
||||||
|
|
||||||
|
|
||||||
|
def select_wake_word(settings: Settings) -> None:
|
||||||
|
if settings.wake_word_system == WakeWordSystem.OPENWAKEWORD:
|
||||||
|
wake_word = radiolist(
|
||||||
|
"Wake Word:",
|
||||||
|
[
|
||||||
|
("ok_nabu", "ok nabu"),
|
||||||
|
("hey_jarvis", "hey jarvis"),
|
||||||
|
("alexa", "alexa"),
|
||||||
|
("hey_mycroft", "hey mycroft"),
|
||||||
|
("community", "Community Wake Words"),
|
||||||
|
],
|
||||||
|
settings.wake_word.get(WakeWordSystem.OPENWAKEWORD),
|
||||||
|
)
|
||||||
|
|
||||||
|
if wake_word is not None:
|
||||||
|
settings.wake_word[WakeWordSystem.OPENWAKEWORD] = wake_word
|
||||||
|
settings.save()
|
||||||
|
|
||||||
|
elif settings.wake_word_system == WakeWordSystem.PORCUPINE1:
|
||||||
|
wake_word = radiolist(
|
||||||
|
"Wake Word:",
|
||||||
|
[
|
||||||
|
("ok_nabu", "ok nabu"),
|
||||||
|
("hey_jarvis", "hey jarvis"),
|
||||||
|
("alexa", "alexa"),
|
||||||
|
("hey_mycroft", "hey mycroft"),
|
||||||
|
("community", "Community Wake Words"),
|
||||||
|
],
|
||||||
|
settings.wake_word.get(WakeWordSystem.OPENWAKEWORD),
|
||||||
|
)
|
||||||
|
|
||||||
|
if wake_word is not None:
|
||||||
|
settings.wake_word[WakeWordSystem.OPENWAKEWORD] = wake_word
|
||||||
|
settings.save()
|
||||||
|
|
||||||
|
elif settings.wake_word_system == WakeWordSystem.PORCUPINE1:
|
||||||
|
wake_word = radiolist(
|
||||||
|
"Wake Word:",
|
||||||
|
[
|
||||||
|
("porcupine", "porcupine"),
|
||||||
|
],
|
||||||
|
settings.wake_word.get(WakeWordSystem.PORCUPINE1),
|
||||||
|
)
|
||||||
|
|
||||||
|
if wake_word is not None:
|
||||||
|
settings.wake_word[WakeWordSystem.PORCUPINE1] = wake_word
|
||||||
|
settings.save()
|
||||||
|
|
||||||
|
elif settings.wake_word_system == WakeWordSystem.SNOWBOY:
|
||||||
|
wake_word = radiolist(
|
||||||
|
"Wake Word:",
|
||||||
|
[
|
||||||
|
("snowboy", "snowboy"),
|
||||||
|
("jarvis", "jarvis"),
|
||||||
|
("alexa", "alexa"),
|
||||||
|
("smart_mirror", "smart mirror"),
|
||||||
|
("view_glass", "view glass"),
|
||||||
|
("hey_extreme", "hey extreme"),
|
||||||
|
("neoya", "neoya"),
|
||||||
|
("subex", "subex"),
|
||||||
|
],
|
||||||
|
settings.wake_word.get(WakeWordSystem.SNOWBOY),
|
||||||
|
)
|
||||||
|
|
||||||
|
if wake_word is not None:
|
||||||
|
settings.wake_word[WakeWordSystem.SNOWBOY] = wake_word
|
||||||
|
settings.save()
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@@ -445,12 +596,140 @@ def set_satellite_type(settings: Settings) -> None:
|
|||||||
],
|
],
|
||||||
settings.satellite_type,
|
settings.satellite_type,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if satellite_type == SatelliteType.VAD:
|
||||||
|
result = run_with_gauge(
|
||||||
|
"Installing vad...",
|
||||||
|
[
|
||||||
|
pip_install(
|
||||||
|
"-r", str(_PROGRAM_DIR / "requirements_vad.txt")
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
if not result:
|
||||||
|
msgbox(
|
||||||
|
"An error occurred while installed vad. "
|
||||||
|
"See local/installer.log for details."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
if satellite_type is not None:
|
if satellite_type is not None:
|
||||||
settings.satellite_type = satellite_type
|
settings.satellite_type = satellite_type
|
||||||
settings.save()
|
settings.save()
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
# Services
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def generate_services(settings: Settings) -> None:
|
||||||
|
if settings.microphone_device is None:
|
||||||
|
msgbox("Please configure microphone")
|
||||||
|
return
|
||||||
|
|
||||||
|
services_dir = _LOCAL_DIR / "services"
|
||||||
|
services_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
user = subprocess.check_output(["id", "--name", "-u"], text=True).strip()
|
||||||
|
group = subprocess.check_output(["id", "--name", "-g"], text=True).strip()
|
||||||
|
|
||||||
|
satellite_command = [
|
||||||
|
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",
|
||||||
|
]
|
||||||
|
|
||||||
|
if settings.satellite_type == SatelliteType.VAD:
|
||||||
|
satellite_command.append("--vad")
|
||||||
|
|
||||||
|
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)
|
||||||
|
print("", file=service_file)
|
||||||
|
print("[Service]", file=service_file)
|
||||||
|
print("Type=simple", file=service_file)
|
||||||
|
print(f"User={user}", file=service_file)
|
||||||
|
print(f"Group={group}", 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)
|
||||||
|
|
||||||
|
msgbox("Services generated")
|
||||||
|
|
||||||
|
|
||||||
|
def install_services(settings: Settings) -> None:
|
||||||
|
services_dir = _LOCAL_DIR / "services"
|
||||||
|
satellite_service_path = services_dir / "wyoming-satellite.service"
|
||||||
|
|
||||||
|
if not satellite_service_path.exists():
|
||||||
|
msgbox("Please generate services")
|
||||||
|
return
|
||||||
|
|
||||||
|
password = paswordbox("sudo password:")
|
||||||
|
|
||||||
|
run_with_gauge(
|
||||||
|
"Stopping Services...",
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"sudo",
|
||||||
|
"-S",
|
||||||
|
"systemctl",
|
||||||
|
"disable",
|
||||||
|
"--now",
|
||||||
|
f"wyoming-{service}.service",
|
||||||
|
]
|
||||||
|
for service in ("satellite", "openwakeword", "porcupine1", "snowboy")
|
||||||
|
],
|
||||||
|
sudo_password=password,
|
||||||
|
)
|
||||||
|
|
||||||
|
installed_services = ["satellite"]
|
||||||
|
install_commands = [["sudo", "-S", "systemctl", "daemon-reload"]]
|
||||||
|
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", "enable", "--now", service_filename]
|
||||||
|
)
|
||||||
|
|
||||||
|
success = run_with_gauge(
|
||||||
|
"Installing Services...", install_commands, sudo_password=password
|
||||||
|
)
|
||||||
|
if success:
|
||||||
|
msgbox("Successfully installed services")
|
||||||
|
else:
|
||||||
|
msgbox(
|
||||||
|
"An error occurred while installing services. "
|
||||||
|
"See local/installer.log for details."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# whiptail
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def whiptail(*args) -> Optional[str]:
|
def whiptail(*args) -> Optional[str]:
|
||||||
@@ -492,6 +771,10 @@ def inputbox(text: str, init: Optional[str] = None) -> Optional[str]:
|
|||||||
return whiptail("--inputbox", text, HEIGHT, WIDTH, init or "")
|
return whiptail("--inputbox", text, HEIGHT, WIDTH, init or "")
|
||||||
|
|
||||||
|
|
||||||
|
def paswordbox(text: str) -> Optional[str]:
|
||||||
|
return whiptail("--passwordbox", text, HEIGHT, WIDTH)
|
||||||
|
|
||||||
|
|
||||||
def radiolist(
|
def radiolist(
|
||||||
text: str,
|
text: str,
|
||||||
items: List[ItemType],
|
items: List[ItemType],
|
||||||
@@ -602,7 +885,9 @@ def gauge(text: str, seconds: int, parts: int = 20) -> None:
|
|||||||
proc.communicate()
|
proc.communicate()
|
||||||
|
|
||||||
|
|
||||||
def run_with_gauge(text: str, commands: List[List[str]]) -> bool:
|
def run_with_gauge(
|
||||||
|
text: str, commands: List[List[str]], sudo_password: Optional[str] = None
|
||||||
|
) -> bool:
|
||||||
proc = subprocess.Popen(
|
proc = subprocess.Popen(
|
||||||
["whiptail", "--title", TITLE, "--gauge", text, HEIGHT, WIDTH, "0"],
|
["whiptail", "--title", TITLE, "--gauge", text, HEIGHT, WIDTH, "0"],
|
||||||
stdin=subprocess.PIPE,
|
stdin=subprocess.PIPE,
|
||||||
@@ -616,7 +901,7 @@ def run_with_gauge(text: str, commands: List[List[str]]) -> bool:
|
|||||||
|
|
||||||
with ThreadPoolExecutor() as executor:
|
with ThreadPoolExecutor() as executor:
|
||||||
for command in commands:
|
for command in commands:
|
||||||
future = executor.submit(_run_command, command)
|
future = executor.submit(_run_command, command, sudo_password)
|
||||||
while not future.done():
|
while not future.done():
|
||||||
time.sleep(seconds / parts)
|
time.sleep(seconds / parts)
|
||||||
percent += int(100 / parts)
|
percent += int(100 / parts)
|
||||||
@@ -633,15 +918,21 @@ def run_with_gauge(text: str, commands: List[List[str]]) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _run_command(command: List[str]) -> bool:
|
def _run_command(command: List[str], sudo_password: Optional[str] = None) -> bool:
|
||||||
try:
|
try:
|
||||||
|
assert command
|
||||||
|
proc_input: Optional[str] = None
|
||||||
|
if (command[0] == "sudo") and (sudo_password is not None):
|
||||||
|
proc_input = sudo_password
|
||||||
|
|
||||||
proc = subprocess.Popen(
|
proc = subprocess.Popen(
|
||||||
command,
|
command,
|
||||||
|
stdin=subprocess.PIPE,
|
||||||
stdout=subprocess.DEVNULL,
|
stdout=subprocess.DEVNULL,
|
||||||
stderr=subprocess.PIPE,
|
stderr=subprocess.PIPE,
|
||||||
text=True,
|
text=True,
|
||||||
)
|
)
|
||||||
_stdout, stderr = proc.communicate()
|
_stdout, stderr = proc.communicate(proc_input)
|
||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
_LOGGER.error("Error running command: %s", command)
|
_LOGGER.error("Error running command: %s", command)
|
||||||
_LOGGER.error(stderr)
|
_LOGGER.error(stderr)
|
||||||
@@ -653,6 +944,17 @@ def _run_command(command: List[str]) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def pip_install(*args) -> List[str]:
|
||||||
|
return [
|
||||||
|
str(_PROGRAM_DIR / ".venv" / "bin" / "pip3"),
|
||||||
|
"install",
|
||||||
|
"--extra-index-url",
|
||||||
|
"https://www.piwheels.org/simple",
|
||||||
|
"-f",
|
||||||
|
"https://synesthesiam.github.io/prebuilt-apps/",
|
||||||
|
] + list(args)
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user