381ee75371
Lint & Test / test (push) Successful in 1m13s
- 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
66 lines
1.8 KiB
Python
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)
|