Update __main__.py

Extended Preferences loading to initialize and backfill a new alarm_duration_seconds field (default 0).

Added a set_alarm_duration event handler to MicMuteHandler which:

validates and normalizes the duration value,

updates state.preferences.alarm_duration_seconds,

persists the updated preferences to disk.

Kept the existing “single writer” pattern (MicMuteHandler) for preferences/state updates.
This commit is contained in:
imonlinux
2025-11-29 14:37:18 -08:00
committed by GitHub
parent 5931370c63
commit b43b036198
+62 -8
View File
@@ -48,7 +48,7 @@ class MediaPlayers:
tts: MpvMediaPlayer
# -----------------------------------------------------------------------------
# Mic Mute Handler
# Mic Mute / Preferences Handler
# -----------------------------------------------------------------------------
class MicMuteHandler(EventHandler):
@@ -98,6 +98,45 @@ class MicMuteHandler(EventHandler):
self.state.preferences.num_leds = num_leds
self.state.save_preferences()
@subscribe
def set_alarm_duration(self, data: dict):
"""
Event handler to save alarm_duration_seconds to preferences.
Expected payload from MQTT controller:
{ "alarm_duration_seconds": <int> }
Semantics:
0 -> infinite alarm (only stop via Stop wake word / wake word logic)
>0 -> auto-stop alarm after N seconds (plus Stop wake word support)
"""
duration = data.get("alarm_duration_seconds")
if duration is None:
return
try:
duration = int(duration)
except (TypeError, ValueError):
_LOGGER.warning(
"Invalid alarm_duration_seconds value received: %r", duration
)
return
if duration < 0:
_LOGGER.warning(
"Negative alarm_duration_seconds (%d) is not allowed; ignoring",
duration,
)
return
current = getattr(self.state.preferences, "alarm_duration_seconds", 0)
if current != duration:
_LOGGER.debug(
"Updating alarm_duration_seconds: %s -> %s", current, duration
)
self.state.preferences.alarm_duration_seconds = duration
self.state.save_preferences()
# -----------------------------------------------------------------------------
# Main Application
# -----------------------------------------------------------------------------
@@ -166,7 +205,10 @@ def _init_basics() -> Tuple[Config, asyncio.AbstractEventLoop, EventBus]:
for idx, mic in enumerate(sc.all_microphones(include_loopback=False)):
print(f"[{idx}]", mic.name)
except Exception as e:
_LOGGER.error("Error listing input devices (ensure audio backend is working): %s", e)
_LOGGER.error(
"Error listing input devices (ensure audio backend is working): %s",
e,
)
sys.exit(0)
if args.list_output_devices:
@@ -210,7 +252,13 @@ def _load_preferences(config: Config) -> Preferences:
else:
preferences = Preferences()
preferences.num_leds = getattr(preferences, 'num_leds', config.led.num_leds)
# Backwards-compatible defaults / migrations
preferences.num_leds = getattr(preferences, "num_leds", config.led.num_leds)
# New: default alarm_duration_seconds, 0 = infinite until Stop/wake word
preferences.alarm_duration_seconds = getattr(
preferences, "alarm_duration_seconds", 0
)
return preferences
def _get_microphone(config: Config):
@@ -294,17 +342,21 @@ def _load_wake_words(config: Config, preferences: Preferences) -> WakeWordData:
return WakeWordData(available, models, active, stop_model)
def _init_media_players(loop: asyncio.AbstractEventLoop, config: Config, preferences: Preferences) -> MediaPlayers:
def _init_media_players(
loop: asyncio.AbstractEventLoop,
config: Config,
preferences: Preferences,
) -> MediaPlayers:
"""Initializes the music and TTS media players."""
music_player = MpvMediaPlayer(
loop=loop,
device=config.audio.output_device,
initial_volume=preferences.volume_level
initial_volume=preferences.volume_level,
)
tts_player = MpvMediaPlayer(
loop=loop,
device=config.audio.output_device,
initial_volume=preferences.volume_level
initial_volume=preferences.volume_level,
)
return MediaPlayers(music=music_player, tts=tts_player)
@@ -377,13 +429,15 @@ async def _run_server(state: ServerState, config: Config):
server = await state.loop.create_server(
lambda: VoiceSatelliteProtocol(state),
host=config.esphome.host,
port=config.esphome.port
port=config.esphome.port,
)
discovery = HomeAssistantZeroconf(port=config.esphome.port, name=config.app.name)
await discovery.register_server()
async with server:
_LOGGER.info("Server started (host=%s, port=%s)", config.esphome.host, config.esphome.port)
_LOGGER.info(
"Server started (host=%s, port=%s)", config.esphome.host, config.esphome.port
)
await server.serve_forever()