Files
ledgrab/server/src/wled_controller/utils/sound_player.py
T
alexei.dolgolyov 381ee75371
Lint & Test / test (push) Successful in 1m13s
fix: HA light target — brightness source, transition=0, dashboard type label
- Add brightness_value_source_id to HALightOutputTarget model, to_dict,
  from_dict, update_fields, register_with_manager, API response
- Wire value stream in HALightTargetProcessor: acquire/release on
  start/stop, multiply brightness in _update_lights loop
- Fix transition=0 not saving (parseFloat("0") || 0.5 was falsy)
- Fix dashboard showing "Key Colors" for HA targets — now "Home Assistant"
- Fix dashboard FPS showing 0/2 — HA targets show target/target
- Add CSS source subtitle to HA target dashboard cards
2026-03-28 16:03:06 +03:00

66 lines
1.8 KiB
Python

"""Cross-platform asynchronous sound playback for notification alerts.
Uses just_playback (backed by miniaudio) for MP3/WAV/OGG/FLAC playback
with native volume control on all platforms.
A new notification sound cancels any currently playing sound to prevent overlap.
"""
import threading
from pathlib import Path
from wled_controller.utils import get_logger
logger = get_logger(__name__)
_lock = threading.Lock()
_playback = None # lazy-init on first use
def _get_playback():
"""Lazy-init the Playback singleton (import is heavy, defer until needed)."""
global _playback
if _playback is None:
from just_playback import Playback
_playback = Playback()
return _playback
def play_sound_async(file_path: Path, volume: float = 1.0) -> None:
"""Play a sound file (non-blocking).
Supports WAV, MP3, OGG, FLAC. A new call stops any currently playing sound.
just_playback.play() is inherently non-blocking (miniaudio backend thread).
Args:
file_path: Path to the sound file.
volume: Volume level 0.0-1.0.
"""
if not file_path.exists():
logger.warning(f"Sound file not found: {file_path}")
return
volume = max(0.0, min(1.0, volume))
with _lock:
try:
pb = _get_playback()
if pb.active:
pb.stop()
pb.load_file(str(file_path))
pb.set_volume(volume)
pb.play()
except Exception as e:
logger.error(f"Sound playback failed: {e}")
def stop_current_sound() -> None:
"""Stop any currently playing notification sound."""
with _lock:
if _playback is not None and _playback.active:
try:
_playback.stop()
except Exception as e:
logger.debug("Failed to stop playback: %s", e)