Add balance entity for Sonos speakers (#85205)
This commit is contained in:
parent
942a955a77
commit
6013584b7b
4 changed files with 49 additions and 3 deletions
|
@ -19,6 +19,7 @@ from .speaker import SonosSpeaker
|
|||
LEVEL_TYPES = {
|
||||
"audio_delay": (0, 5),
|
||||
"bass": (-10, 10),
|
||||
"balance": (-100, 100),
|
||||
"treble": (-10, 10),
|
||||
"sub_gain": (-15, 15),
|
||||
"surround_level": (-15, 15),
|
||||
|
@ -30,6 +31,40 @@ SocoFeatures = list[tuple[str, tuple[int, int]]]
|
|||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _balance_to_number(state: tuple[int, int]) -> float:
|
||||
"""Represent a balance measure returned by SoCo as a number.
|
||||
|
||||
SoCo returns a pair of volumes, one for the left side and one
|
||||
for the right side. When the two are equal, sound is centered;
|
||||
HA will show that as 0. When the left side is louder, HA will
|
||||
show a negative value, and a positive value means the right
|
||||
side is louder. Maximum absolute value is 100, which means only
|
||||
one side produces sound at all.
|
||||
"""
|
||||
left, right = state
|
||||
return (right - left) * 100 // max(right, left)
|
||||
|
||||
|
||||
def _balance_from_number(value: float) -> tuple[int, int]:
|
||||
"""Convert a balance value from -100 to 100 into SoCo format.
|
||||
|
||||
0 becomes (100, 100), fully enabling both sides. Note that
|
||||
the master volume control is separate, so this does not
|
||||
turn up the speakers to maximum volume. Negative values
|
||||
reduce the volume of the right side, and positive values
|
||||
reduce the volume of the left side. -100 becomes (100, 0),
|
||||
fully muting the right side, and +100 becomes (0, 100),
|
||||
muting the left side.
|
||||
"""
|
||||
left = min(100, 100 - int(value))
|
||||
right = min(100, int(value) + 100)
|
||||
return left, right
|
||||
|
||||
|
||||
LEVEL_TO_NUMBER = {"balance": _balance_to_number}
|
||||
LEVEL_FROM_NUMBER = {"balance": _balance_from_number}
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
|
@ -92,9 +127,11 @@ class SonosLevelEntity(SonosEntity, NumberEntity):
|
|||
@soco_error()
|
||||
def set_native_value(self, value: float) -> None:
|
||||
"""Set a new value."""
|
||||
setattr(self.soco, self.level_type, value)
|
||||
from_number = LEVEL_FROM_NUMBER.get(self.level_type, int)
|
||||
setattr(self.soco, self.level_type, from_number(value))
|
||||
|
||||
@property
|
||||
def native_value(self) -> float:
|
||||
"""Return the current value."""
|
||||
return cast(float, getattr(self.speaker, self.level_type))
|
||||
to_number = LEVEL_TO_NUMBER.get(self.level_type, int)
|
||||
return cast(float, to_number(getattr(self.speaker, self.level_type)))
|
||||
|
|
|
@ -145,6 +145,7 @@ class SonosSpeaker:
|
|||
self.volume: int | None = None
|
||||
self.muted: bool | None = None
|
||||
self.cross_fade: bool | None = None
|
||||
self.balance: tuple[int, int] | None = None
|
||||
self.bass: int | None = None
|
||||
self.treble: int | None = None
|
||||
self.loudness: bool | None = None
|
||||
|
@ -536,7 +537,10 @@ class SonosSpeaker:
|
|||
variables = event.variables
|
||||
|
||||
if "volume" in variables:
|
||||
self.volume = int(variables["volume"]["Master"])
|
||||
volume = variables["volume"]
|
||||
self.volume = int(volume["Master"])
|
||||
if "LF" in volume and "RF" in volume:
|
||||
self.balance = (int(volume["LF"]), int(volume["RF"]))
|
||||
|
||||
if "mute" in variables:
|
||||
self.muted = variables["mute"]["Master"] == "1"
|
||||
|
|
|
@ -112,6 +112,7 @@ def soco_fixture(
|
|||
mock_soco.loudness = True
|
||||
mock_soco.volume = 19
|
||||
mock_soco.audio_delay = 2
|
||||
mock_soco.balance = (61, 100)
|
||||
mock_soco.bass = 1
|
||||
mock_soco.treble = -1
|
||||
mock_soco.mic_enabled = False
|
||||
|
|
|
@ -11,6 +11,10 @@ async def test_number_entities(
|
|||
hass: HomeAssistant, async_autosetup_sonos, soco, entity_registry: er.EntityRegistry
|
||||
) -> None:
|
||||
"""Test number entities."""
|
||||
balance_number = entity_registry.entities["number.zone_a_balance"]
|
||||
balance_state = hass.states.get(balance_number.entity_id)
|
||||
assert balance_state.state == "39"
|
||||
|
||||
bass_number = entity_registry.entities["number.zone_a_bass"]
|
||||
bass_state = hass.states.get(bass_number.entity_id)
|
||||
assert bass_state.state == "1"
|
||||
|
|
Loading…
Add table
Reference in a new issue