mirror of
https://github.com/esphome/esphome.git
synced 2026-11-10 23:25:58 +00:00
[speaker][i2s_audio][es8156] Allow a list of audio DACs on one speaker (#21260)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
26ba2cc234
commit
ae127f7db8
9 files changed
+285
-28
No files matched your search
@@ -0,0 +1,82 @@
|
||||
"""Tests for the es8156 bits_per_sample check on the speakers that use it."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome.components.es8156.audio_dac import FINAL_VALIDATE_SCHEMA
|
||||
from esphome.config import Config
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_AUDIO_DAC,
|
||||
CONF_BITS_PER_SAMPLE,
|
||||
CONF_ID,
|
||||
PlatformFramework,
|
||||
)
|
||||
from esphome.core import ID
|
||||
from tests.component_tests.types import SetCoreConfigCallable
|
||||
|
||||
ES8156_ID = "es"
|
||||
|
||||
|
||||
def _dac_id(name: str) -> ID:
|
||||
return ID(name, is_declaration=False, type="audio_dac::AudioDac")
|
||||
|
||||
|
||||
def _full_config(audio_dac: ID | list[ID], bits_per_sample: int) -> Config:
|
||||
"""A full config with one speaker using the given audio_dac value."""
|
||||
full = Config()
|
||||
full["speaker"] = [
|
||||
{CONF_AUDIO_DAC: audio_dac, CONF_BITS_PER_SAMPLE: bits_per_sample}
|
||||
]
|
||||
return full
|
||||
|
||||
|
||||
def _final_validate() -> None:
|
||||
FINAL_VALIDATE_SCHEMA({CONF_ID: ID(ES8156_ID, is_declaration=True)})
|
||||
|
||||
|
||||
def test_single_audio_dac_over_24_bits_is_rejected(
|
||||
set_core_config: SetCoreConfigCallable,
|
||||
) -> None:
|
||||
set_core_config(
|
||||
PlatformFramework.ESP32_IDF, full_config=_full_config(_dac_id(ES8156_ID), 32)
|
||||
)
|
||||
|
||||
with pytest.raises(cv.Invalid, match="ES8156 does not support more than 24 bits"):
|
||||
_final_validate()
|
||||
|
||||
|
||||
def test_audio_dac_list_over_24_bits_is_rejected(
|
||||
set_core_config: SetCoreConfigCallable,
|
||||
) -> None:
|
||||
"""The check also finds the es8156 inside a list of audio DACs."""
|
||||
set_core_config(
|
||||
PlatformFramework.ESP32_IDF,
|
||||
full_config=_full_config([_dac_id("other"), _dac_id(ES8156_ID)], 32),
|
||||
)
|
||||
|
||||
with pytest.raises(cv.Invalid, match="ES8156 does not support more than 24 bits"):
|
||||
_final_validate()
|
||||
|
||||
|
||||
def test_audio_dac_list_at_16_bits_passes(
|
||||
set_core_config: SetCoreConfigCallable,
|
||||
) -> None:
|
||||
set_core_config(
|
||||
PlatformFramework.ESP32_IDF,
|
||||
full_config=_full_config([_dac_id("other"), _dac_id(ES8156_ID)], 16),
|
||||
)
|
||||
|
||||
_final_validate()
|
||||
|
||||
|
||||
def test_other_audio_dac_over_24_bits_passes(
|
||||
set_core_config: SetCoreConfigCallable,
|
||||
) -> None:
|
||||
"""A speaker that does not use this es8156 is not checked."""
|
||||
set_core_config(
|
||||
PlatformFramework.ESP32_IDF, full_config=_full_config(_dac_id("other"), 32)
|
||||
)
|
||||
|
||||
_final_validate()
|
||||
@@ -0,0 +1,57 @@
|
||||
"""Tests for the speaker audio_dac option."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome.components.speaker import SPEAKER_SCHEMA
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_AUDIO_DAC, PlatformFramework
|
||||
from tests.component_tests.types import SetCoreConfigCallable
|
||||
|
||||
|
||||
def test_speaker_empty_audio_dac_uses_the_only_dac(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
) -> None:
|
||||
"""An empty audio_dac: still picks the only audio DAC in the config."""
|
||||
main_cpp = generate_main(
|
||||
"tests/component_tests/speaker/test_speaker_audio_dac_empty.yaml"
|
||||
)
|
||||
|
||||
assert "speaker_audio_dacs[] PROGMEM = {only_dac};" in main_cpp
|
||||
assert "speaker_empty_audio_dac->set_audio_dacs(speaker_audio_dacs, 1);" in main_cpp
|
||||
|
||||
|
||||
def test_speaker_audio_dac_list_single_and_absent(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
) -> None:
|
||||
"""A list feeds every DAC, a single id one DAC, and no audio_dac sets nothing."""
|
||||
main_cpp = generate_main(
|
||||
"tests/component_tests/speaker/test_speaker_audio_dac_forms.yaml"
|
||||
)
|
||||
|
||||
assert "speaker_audio_dacs[] PROGMEM = {dac_a, dac_b};" in main_cpp
|
||||
assert "speaker_list->set_audio_dacs(speaker_audio_dacs, 2);" in main_cpp
|
||||
assert "speaker_audio_dacs_2[] PROGMEM = {dac_a};" in main_cpp
|
||||
assert "source_single->set_audio_dacs(speaker_audio_dacs_2, 1);" in main_cpp
|
||||
assert "source_absent->set_audio_dac" not in main_cpp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("audio_dac", "message"),
|
||||
[
|
||||
(["dac_a", "dac_a"], "Each audio_dac can only be listed once"),
|
||||
([], "length of value must be at least 1"),
|
||||
],
|
||||
)
|
||||
def test_speaker_audio_dac_list_rejections(
|
||||
set_core_config: SetCoreConfigCallable, audio_dac: list[str], message: str
|
||||
) -> None:
|
||||
"""A repeated DAC and an empty list are rejected with the list's own message, not use_id's."""
|
||||
set_core_config(PlatformFramework.ESP32_IDF)
|
||||
|
||||
with pytest.raises(cv.Invalid, match=message):
|
||||
SPEAKER_SCHEMA({CONF_AUDIO_DAC: audio_dac})
|
||||
@@ -0,0 +1,26 @@
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
|
||||
i2c:
|
||||
id: i2c_bus
|
||||
sda: 21
|
||||
scl: 22
|
||||
|
||||
i2s_audio:
|
||||
- id: i2s_audio_bus
|
||||
i2s_lrclk_pin: 25
|
||||
i2s_bclk_pin: 26
|
||||
|
||||
audio_dac:
|
||||
- platform: aic3204
|
||||
id: only_dac
|
||||
|
||||
speaker:
|
||||
- platform: i2s_audio
|
||||
id: speaker_empty_audio_dac
|
||||
dac_type: external
|
||||
i2s_dout_pin: 27
|
||||
audio_dac:
|
||||
@@ -0,0 +1,35 @@
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
|
||||
i2c:
|
||||
id: i2c_bus
|
||||
sda: 21
|
||||
scl: 22
|
||||
|
||||
i2s_audio:
|
||||
- id: i2s_audio_bus
|
||||
i2s_lrclk_pin: 25
|
||||
i2s_bclk_pin: 26
|
||||
|
||||
audio_dac:
|
||||
- platform: aic3204
|
||||
id: dac_a
|
||||
- platform: aic3204
|
||||
id: dac_b
|
||||
address: 0x19
|
||||
|
||||
speaker:
|
||||
- platform: i2s_audio
|
||||
id: speaker_list
|
||||
dac_type: external
|
||||
i2s_dout_pin: 27
|
||||
audio_dac: [dac_a, dac_b]
|
||||
- platform: mixer
|
||||
output_speaker: speaker_list
|
||||
source_speakers:
|
||||
- id: source_single
|
||||
audio_dac: dac_a
|
||||
- id: source_absent
|
||||
Reference in new issue
Block a user