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:
@@ -48,7 +48,7 @@ class MediaPlayers:
|
|||||||
tts: MpvMediaPlayer
|
tts: MpvMediaPlayer
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Mic Mute Handler
|
# Mic Mute / Preferences Handler
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
class MicMuteHandler(EventHandler):
|
class MicMuteHandler(EventHandler):
|
||||||
@@ -98,6 +98,45 @@ class MicMuteHandler(EventHandler):
|
|||||||
self.state.preferences.num_leds = num_leds
|
self.state.preferences.num_leds = num_leds
|
||||||
self.state.save_preferences()
|
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
|
# 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)):
|
for idx, mic in enumerate(sc.all_microphones(include_loopback=False)):
|
||||||
print(f"[{idx}]", mic.name)
|
print(f"[{idx}]", mic.name)
|
||||||
except Exception as e:
|
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)
|
sys.exit(0)
|
||||||
|
|
||||||
if args.list_output_devices:
|
if args.list_output_devices:
|
||||||
@@ -210,7 +252,13 @@ def _load_preferences(config: Config) -> Preferences:
|
|||||||
else:
|
else:
|
||||||
preferences = Preferences()
|
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
|
return preferences
|
||||||
|
|
||||||
def _get_microphone(config: Config):
|
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)
|
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."""
|
"""Initializes the music and TTS media players."""
|
||||||
music_player = MpvMediaPlayer(
|
music_player = MpvMediaPlayer(
|
||||||
loop=loop,
|
loop=loop,
|
||||||
device=config.audio.output_device,
|
device=config.audio.output_device,
|
||||||
initial_volume=preferences.volume_level
|
initial_volume=preferences.volume_level,
|
||||||
)
|
)
|
||||||
tts_player = MpvMediaPlayer(
|
tts_player = MpvMediaPlayer(
|
||||||
loop=loop,
|
loop=loop,
|
||||||
device=config.audio.output_device,
|
device=config.audio.output_device,
|
||||||
initial_volume=preferences.volume_level
|
initial_volume=preferences.volume_level,
|
||||||
)
|
)
|
||||||
return MediaPlayers(music=music_player, tts=tts_player)
|
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(
|
server = await state.loop.create_server(
|
||||||
lambda: VoiceSatelliteProtocol(state),
|
lambda: VoiceSatelliteProtocol(state),
|
||||||
host=config.esphome.host,
|
host=config.esphome.host,
|
||||||
port=config.esphome.port
|
port=config.esphome.port,
|
||||||
)
|
)
|
||||||
discovery = HomeAssistantZeroconf(port=config.esphome.port, name=config.app.name)
|
discovery = HomeAssistantZeroconf(port=config.esphome.port, name=config.app.name)
|
||||||
await discovery.register_server()
|
await discovery.register_server()
|
||||||
|
|
||||||
async with 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()
|
await server.serve_forever()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user