diff --git a/homeassistant/components/sonos/const.py b/homeassistant/components/sonos/const.py index 4b636b3e0f6..523ac9f561b 100644 --- a/homeassistant/components/sonos/const.py +++ b/homeassistant/components/sonos/const.py @@ -144,6 +144,7 @@ PLAYABLE_MEDIA_TYPES = [ SONOS_CHECK_ACTIVITY = "sonos_check_activity" SONOS_CREATE_ALARM = "sonos_create_alarm" +SONOS_CREATE_AUDIO_FORMAT_SENSOR = "sonos_create_audio_format_sensor" SONOS_CREATE_BATTERY = "sonos_create_battery" SONOS_CREATE_SWITCHES = "sonos_create_switches" SONOS_CREATE_LEVELS = "sonos_create_levels" diff --git a/homeassistant/components/sonos/sensor.py b/homeassistant/components/sonos/sensor.py index 599e5434fb4..7c9235cf4af 100644 --- a/homeassistant/components/sonos/sensor.py +++ b/homeassistant/components/sonos/sensor.py @@ -7,9 +7,10 @@ from homeassistant.const import ( ENTITY_CATEGORY_DIAGNOSTIC, PERCENTAGE, ) +from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect -from .const import SONOS_CREATE_BATTERY +from .const import SONOS_CREATE_AUDIO_FORMAT_SENSOR, SONOS_CREATE_BATTERY from .entity import SonosEntity from .speaker import SonosSpeaker @@ -17,12 +18,27 @@ from .speaker import SonosSpeaker async def async_setup_entry(hass, config_entry, async_add_entities): """Set up Sonos from a config entry.""" - async def _async_create_entity(speaker: SonosSpeaker) -> None: + @callback + def _async_create_audio_format_entity( + speaker: SonosSpeaker, audio_format: str + ) -> None: + entity = SonosAudioInputFormatSensorEntity(speaker, audio_format) + async_add_entities([entity]) + + @callback + def _async_create_battery_sensor(speaker: SonosSpeaker) -> None: entity = SonosBatteryEntity(speaker) async_add_entities([entity]) config_entry.async_on_unload( - async_dispatcher_connect(hass, SONOS_CREATE_BATTERY, _async_create_entity) + async_dispatcher_connect( + hass, SONOS_CREATE_AUDIO_FORMAT_SENSOR, _async_create_audio_format_entity + ) + ) + config_entry.async_on_unload( + async_dispatcher_connect( + hass, SONOS_CREATE_BATTERY, _async_create_battery_sensor + ) ) @@ -64,3 +80,25 @@ class SonosBatteryEntity(SonosEntity, SensorEntity): def available(self) -> bool: """Return whether this device is available.""" return self.speaker.available and self.speaker.power_source + + +class SonosAudioInputFormatSensorEntity(SonosEntity, SensorEntity): + """Representation of a Sonos audio import format sensor entity.""" + + _attr_entity_category = ENTITY_CATEGORY_DIAGNOSTIC + _attr_icon = "mdi:import" + _attr_should_poll = True + + def __init__(self, speaker: SonosSpeaker, audio_format: str) -> None: + """Initialize the audio input format sensor.""" + super().__init__(speaker) + self._attr_unique_id = f"{self.soco.uid}-audio-format" + self._attr_name = f"{self.speaker.zone_name} Audio Input Format" + self._attr_native_value = audio_format + + def update(self) -> None: + """Poll the device for the current state.""" + self._attr_native_value = self.soco.soundbar_audio_input_format + + async def _async_poll(self) -> None: + """Provide a stub for required ABC method.""" diff --git a/homeassistant/components/sonos/speaker.py b/homeassistant/components/sonos/speaker.py index 4838da69c50..05787a354f0 100644 --- a/homeassistant/components/sonos/speaker.py +++ b/homeassistant/components/sonos/speaker.py @@ -46,6 +46,7 @@ from .const import ( SCAN_INTERVAL, SONOS_CHECK_ACTIVITY, SONOS_CREATE_ALARM, + SONOS_CREATE_AUDIO_FORMAT_SENSOR, SONOS_CREATE_BATTERY, SONOS_CREATE_LEVELS, SONOS_CREATE_MEDIA_PLAYER, @@ -240,6 +241,11 @@ class SonosSpeaker: dispatcher_send(self.hass, SONOS_CREATE_LEVELS, self) + if audio_format := self.soco.soundbar_audio_input_format: + dispatcher_send( + self.hass, SONOS_CREATE_AUDIO_FORMAT_SENSOR, self, audio_format + ) + if battery_info := fetch_battery_info_or_none(self.soco): self.battery_info = battery_info # Battery events can be infrequent, polling is still necessary diff --git a/tests/components/sonos/conftest.py b/tests/components/sonos/conftest.py index a17f07c3c35..f7f8d67589f 100644 --- a/tests/components/sonos/conftest.py +++ b/tests/components/sonos/conftest.py @@ -74,6 +74,7 @@ def soco_fixture(music_library, speaker_info, battery_info, alarm_clock): mock_soco.treble = -1 mock_soco.sub_enabled = False mock_soco.surround_enabled = True + mock_soco.soundbar_audio_input_format = "Dolby 5.1" mock_soco.get_battery_info.return_value = battery_info mock_soco.all_zones = [mock_soco] yield mock_soco diff --git a/tests/components/sonos/test_sensor.py b/tests/components/sonos/test_sensor.py index 18cd87ca9be..a45d587cc08 100644 --- a/tests/components/sonos/test_sensor.py +++ b/tests/components/sonos/test_sensor.py @@ -123,3 +123,14 @@ async def test_device_payload_without_battery_and_ignored_keys( await hass.async_block_till_done() assert ignored_payload not in caplog.text + + +async def test_audio_input_sensor(hass, config_entry, config, soco): + """Test sonos device with battery state.""" + await setup_platform(hass, config_entry, config) + + entity_registry = await hass.helpers.entity_registry.async_get_registry() + + audio_input_sensor = entity_registry.entities["sensor.zone_a_audio_input_format"] + audio_input_state = hass.states.get(audio_input_sensor.entity_id) + assert audio_input_state.state == "Dolby 5.1"