Save preferences to JSON file
This commit is contained in:
@@ -16,3 +16,4 @@ venv/
|
|||||||
*.egg-info/
|
*.egg-info/
|
||||||
|
|
||||||
dist/
|
dist/
|
||||||
|
preferences.json
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
- Save active wake words to preferences JSON file
|
||||||
|
- Refactor main into separate files
|
||||||
|
|
||||||
## 1.0.0
|
## 1.0.0
|
||||||
|
|
||||||
- Initial release
|
- Initial release
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
@@ -12,7 +11,7 @@ from typing import Dict
|
|||||||
import sounddevice as sd
|
import sounddevice as sd
|
||||||
|
|
||||||
from .microwakeword import MicroWakeWord
|
from .microwakeword import MicroWakeWord
|
||||||
from .models import AvailableWakeWord, ServerState
|
from .models import AvailableWakeWord, Preferences, ServerState
|
||||||
from .mpv_player import MpvMediaPlayer
|
from .mpv_player import MpvMediaPlayer
|
||||||
from .satellite import VoiceSatelliteProtocol
|
from .satellite import VoiceSatelliteProtocol
|
||||||
from .util import get_mac, is_arm
|
from .util import get_mac, is_arm
|
||||||
@@ -57,6 +56,9 @@ async def main() -> None:
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--timer-finished-sound", default=str(_SOUNDS_DIR / "timer_finished.flac")
|
"--timer-finished-sound", default=str(_SOUNDS_DIR / "timer_finished.flac")
|
||||||
)
|
)
|
||||||
|
#
|
||||||
|
parser.add_argument("--preferences-file", default=_REPO_DIR / "preferences.json")
|
||||||
|
#
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--host",
|
"--host",
|
||||||
default="0.0.0.0",
|
default="0.0.0.0",
|
||||||
@@ -93,11 +95,28 @@ async def main() -> None:
|
|||||||
|
|
||||||
_LOGGER.debug("Available wake words: %s", list(sorted(available_wake_words.keys())))
|
_LOGGER.debug("Available wake words: %s", list(sorted(available_wake_words.keys())))
|
||||||
|
|
||||||
|
# Load preferences
|
||||||
|
preferences_path = Path(args.preferences_file)
|
||||||
|
if preferences_path.exists():
|
||||||
|
_LOGGER.debug("Loading preferences: %s", preferences_path)
|
||||||
|
with open(preferences_path, "r", encoding="utf-8") as preferences_file:
|
||||||
|
preferences_dict = json.load(preferences_file)
|
||||||
|
preferences = Preferences(**preferences_dict)
|
||||||
|
else:
|
||||||
|
preferences = Preferences()
|
||||||
|
|
||||||
libtensorflowlite_c_path = _LIB_DIR / "libtensorflowlite_c.so"
|
libtensorflowlite_c_path = _LIB_DIR / "libtensorflowlite_c.so"
|
||||||
_LOGGER.debug("libtensorflowlite_c path: %s", libtensorflowlite_c_path)
|
_LOGGER.debug("libtensorflowlite_c path: %s", libtensorflowlite_c_path)
|
||||||
|
|
||||||
# Load wake/stop models
|
# Load wake/stop models
|
||||||
wake_config_path = wake_word_dir / f"{args.wake_model}.json"
|
wake_config_path = wake_word_dir / f"{args.wake_model}.json"
|
||||||
|
if preferences.active_wake_words:
|
||||||
|
wake_word_id = preferences.active_wake_words[0]
|
||||||
|
maybe_wake_config_path = wake_word_dir / f"{wake_word_id}.json"
|
||||||
|
if maybe_wake_config_path.exists():
|
||||||
|
# Override with last set
|
||||||
|
wake_config_path = maybe_wake_config_path
|
||||||
|
|
||||||
_LOGGER.debug("Loading wake model: %s", wake_config_path)
|
_LOGGER.debug("Loading wake model: %s", wake_config_path)
|
||||||
wake_model = MicroWakeWord.from_config(wake_config_path, libtensorflowlite_c_path)
|
wake_model = MicroWakeWord.from_config(wake_config_path, libtensorflowlite_c_path)
|
||||||
|
|
||||||
@@ -117,6 +136,8 @@ async def main() -> None:
|
|||||||
tts_player=MpvMediaPlayer(device=args.audio_output_device),
|
tts_player=MpvMediaPlayer(device=args.audio_output_device),
|
||||||
wakeup_sound=args.wakeup_sound,
|
wakeup_sound=args.wakeup_sound,
|
||||||
timer_finished_sound=args.timer_finished_sound,
|
timer_finished_sound=args.timer_finished_sound,
|
||||||
|
preferences=preferences,
|
||||||
|
preferences_path=preferences_path,
|
||||||
)
|
)
|
||||||
|
|
||||||
process_audio_thread = threading.Thread(
|
process_audio_thread = threading.Thread(
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
"""Shared models."""
|
"""Shared models."""
|
||||||
|
|
||||||
from dataclasses import dataclass
|
import json
|
||||||
|
import logging
|
||||||
|
from dataclasses import asdict, dataclass, field
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
from typing import TYPE_CHECKING, Dict, List, Optional
|
from typing import TYPE_CHECKING, Dict, List, Optional
|
||||||
@@ -11,6 +13,8 @@ if TYPE_CHECKING:
|
|||||||
from .mpv_player import MpvMediaPlayer
|
from .mpv_player import MpvMediaPlayer
|
||||||
from .satellite import VoiceSatelliteProtocol
|
from .satellite import VoiceSatelliteProtocol
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class AvailableWakeWord:
|
class AvailableWakeWord:
|
||||||
@@ -20,6 +24,11 @@ class AvailableWakeWord:
|
|||||||
config_path: Path
|
config_path: Path
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Preferences:
|
||||||
|
active_wake_words: List[str] = field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ServerState:
|
class ServerState:
|
||||||
name: str
|
name: str
|
||||||
@@ -33,5 +42,16 @@ class ServerState:
|
|||||||
tts_player: "MpvMediaPlayer"
|
tts_player: "MpvMediaPlayer"
|
||||||
wakeup_sound: str
|
wakeup_sound: str
|
||||||
timer_finished_sound: str
|
timer_finished_sound: str
|
||||||
|
preferences: Preferences
|
||||||
|
preferences_path: Path
|
||||||
media_player_entity: "Optional[MediaPlayerEntity]" = None
|
media_player_entity: "Optional[MediaPlayerEntity]" = None
|
||||||
satellite: "Optional[VoiceSatelliteProtocol]" = None
|
satellite: "Optional[VoiceSatelliteProtocol]" = None
|
||||||
|
|
||||||
|
def save_preferences(self) -> None:
|
||||||
|
"""Save preferences as JSON."""
|
||||||
|
_LOGGER.debug("Saving preferences: %s", self.preferences_path)
|
||||||
|
self.preferences_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
with open(self.preferences_path, "w", encoding="utf-8") as preferences_file:
|
||||||
|
json.dump(
|
||||||
|
asdict(self.preferences), preferences_file, ensure_ascii=False, indent=4
|
||||||
|
)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
from collections.abc import Iterable
|
from collections.abc import Iterable
|
||||||
from typing import Dict, Optional
|
from typing import Dict, Optional, Set
|
||||||
|
|
||||||
# pylint: disable=no-name-in-module
|
# pylint: disable=no-name-in-module
|
||||||
from aioesphomeapi.api_pb2 import ( # type: ignore[attr-defined]
|
from aioesphomeapi.api_pb2 import ( # type: ignore[attr-defined]
|
||||||
@@ -181,9 +181,12 @@ class VoiceSatelliteProtocol(APIServer):
|
|||||||
_LOGGER.info("Connected to Home Assistant")
|
_LOGGER.info("Connected to Home Assistant")
|
||||||
elif isinstance(msg, VoiceAssistantSetConfiguration):
|
elif isinstance(msg, VoiceAssistantSetConfiguration):
|
||||||
# TODO: support multiple wake words
|
# TODO: support multiple wake words
|
||||||
|
active_wake_words: Set[str] = set()
|
||||||
|
|
||||||
for wake_word_id in msg.active_wake_words:
|
for wake_word_id in msg.active_wake_words:
|
||||||
if wake_word_id == self.state.wake_word.id:
|
if wake_word_id == self.state.wake_word.id:
|
||||||
# Already active
|
# Already active
|
||||||
|
active_wake_words.add(wake_word_id)
|
||||||
break
|
break
|
||||||
|
|
||||||
model_info = self.state.available_wake_words.get(wake_word_id)
|
model_info = self.state.available_wake_words.get(wake_word_id)
|
||||||
@@ -197,8 +200,12 @@ class VoiceSatelliteProtocol(APIServer):
|
|||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER.info("Wake word set: %s", self.state.wake_word.wake_word)
|
_LOGGER.info("Wake word set: %s", self.state.wake_word.wake_word)
|
||||||
|
active_wake_words.add(wake_word_id)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
self.state.preferences.active_wake_words = list(active_wake_words)
|
||||||
|
self.state.save_preferences()
|
||||||
|
|
||||||
def handle_audio(self, audio_chunk: bytes) -> None:
|
def handle_audio(self, audio_chunk: bytes) -> None:
|
||||||
|
|
||||||
if not self._is_streaming_audio:
|
if not self._is_streaming_audio:
|
||||||
|
|||||||
Reference in New Issue
Block a user