mirror of
https://github.com/home-assistant/core.git
synced 2025-07-25 06:07:17 +00:00
Skip polling Sonos audio input sensor when idle (#66271)
This commit is contained in:
parent
c6f3c5da79
commit
4d944e35fd
@ -11,7 +11,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
|||||||
from homeassistant.helpers.entity import EntityCategory
|
from homeassistant.helpers.entity import EntityCategory
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from .const import SONOS_CREATE_AUDIO_FORMAT_SENSOR, SONOS_CREATE_BATTERY
|
from .const import SONOS_CREATE_AUDIO_FORMAT_SENSOR, SONOS_CREATE_BATTERY, SOURCE_TV
|
||||||
from .entity import SonosEntity, SonosPollingEntity
|
from .entity import SonosEntity, SonosPollingEntity
|
||||||
from .helpers import soco_error
|
from .helpers import soco_error
|
||||||
from .speaker import SonosSpeaker
|
from .speaker import SonosSpeaker
|
||||||
@ -94,8 +94,14 @@ class SonosAudioInputFormatSensorEntity(SonosPollingEntity, SensorEntity):
|
|||||||
self._attr_name = f"{self.speaker.zone_name} Audio Input Format"
|
self._attr_name = f"{self.speaker.zone_name} Audio Input Format"
|
||||||
self._attr_native_value = audio_format
|
self._attr_native_value = audio_format
|
||||||
|
|
||||||
@soco_error()
|
|
||||||
def poll_state(self) -> None:
|
def poll_state(self) -> None:
|
||||||
|
"""Poll the state if TV source is active and state has settled."""
|
||||||
|
if self.speaker.media.source_name != SOURCE_TV and self.state == "No input":
|
||||||
|
return
|
||||||
|
self._poll_state()
|
||||||
|
|
||||||
|
@soco_error()
|
||||||
|
def _poll_state(self) -> None:
|
||||||
"""Poll the device for the current state."""
|
"""Poll the device for the current state."""
|
||||||
self._attr_native_value = self.soco.soundbar_audio_input_format
|
self._attr_native_value = self.soco.soundbar_audio_input_format
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ from copy import copy
|
|||||||
from unittest.mock import AsyncMock, MagicMock, Mock, patch
|
from unittest.mock import AsyncMock, MagicMock, Mock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from soco import SoCo
|
||||||
|
|
||||||
from homeassistant.components import ssdp, zeroconf
|
from homeassistant.components import ssdp, zeroconf
|
||||||
from homeassistant.components.media_player import DOMAIN as MP_DOMAIN
|
from homeassistant.components.media_player import DOMAIN as MP_DOMAIN
|
||||||
@ -82,7 +83,9 @@ def config_entry_fixture():
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="soco")
|
@pytest.fixture(name="soco")
|
||||||
def soco_fixture(music_library, speaker_info, battery_info, alarm_clock):
|
def soco_fixture(
|
||||||
|
music_library, speaker_info, current_track_info_empty, battery_info, alarm_clock
|
||||||
|
):
|
||||||
"""Create a mock soco SoCo fixture."""
|
"""Create a mock soco SoCo fixture."""
|
||||||
with patch("homeassistant.components.sonos.SoCo", autospec=True) as mock, patch(
|
with patch("homeassistant.components.sonos.SoCo", autospec=True) as mock, patch(
|
||||||
"socket.gethostbyname", return_value="192.168.42.2"
|
"socket.gethostbyname", return_value="192.168.42.2"
|
||||||
@ -92,6 +95,8 @@ def soco_fixture(music_library, speaker_info, battery_info, alarm_clock):
|
|||||||
mock_soco.uid = "RINCON_test"
|
mock_soco.uid = "RINCON_test"
|
||||||
mock_soco.play_mode = "NORMAL"
|
mock_soco.play_mode = "NORMAL"
|
||||||
mock_soco.music_library = music_library
|
mock_soco.music_library = music_library
|
||||||
|
mock_soco.get_current_track_info.return_value = current_track_info_empty
|
||||||
|
mock_soco.music_source_from_uri = SoCo.music_source_from_uri
|
||||||
mock_soco.get_speaker_info.return_value = speaker_info
|
mock_soco.get_speaker_info.return_value = speaker_info
|
||||||
mock_soco.avTransport = SonosMockService("AVTransport")
|
mock_soco.avTransport = SonosMockService("AVTransport")
|
||||||
mock_soco.renderingControl = SonosMockService("RenderingControl")
|
mock_soco.renderingControl = SonosMockService("RenderingControl")
|
||||||
@ -216,6 +221,22 @@ def speaker_info_fixture():
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="current_track_info_empty")
|
||||||
|
def current_track_info_empty_fixture():
|
||||||
|
"""Create current_track_info_empty fixture."""
|
||||||
|
return {
|
||||||
|
"title": "",
|
||||||
|
"artist": "",
|
||||||
|
"album": "",
|
||||||
|
"album_art": "",
|
||||||
|
"position": "NOT_IMPLEMENTED",
|
||||||
|
"playlist_position": "1",
|
||||||
|
"duration": "NOT_IMPLEMENTED",
|
||||||
|
"uri": "",
|
||||||
|
"metadata": "NOT_IMPLEMENTED",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="battery_info")
|
@pytest.fixture(name="battery_info")
|
||||||
def battery_info_fixture():
|
def battery_info_fixture():
|
||||||
"""Create battery_info fixture."""
|
"""Create battery_info fixture."""
|
||||||
@ -254,6 +275,61 @@ def alarm_event_fixture(soco):
|
|||||||
return SonosMockEvent(soco, soco.alarmClock, variables)
|
return SonosMockEvent(soco, soco.alarmClock, variables)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="no_media_event")
|
||||||
|
def no_media_event_fixture(soco):
|
||||||
|
"""Create no_media_event_fixture."""
|
||||||
|
variables = {
|
||||||
|
"current_crossfade_mode": "0",
|
||||||
|
"current_play_mode": "NORMAL",
|
||||||
|
"current_section": "0",
|
||||||
|
"current_track_uri": "",
|
||||||
|
"enqueued_transport_uri": "",
|
||||||
|
"enqueued_transport_uri_meta_data": "",
|
||||||
|
"transport_state": "STOPPED",
|
||||||
|
}
|
||||||
|
return SonosMockEvent(soco, soco.avTransport, variables)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="tv_event")
|
||||||
|
def tv_event_fixture(soco):
|
||||||
|
"""Create alarm_event fixture."""
|
||||||
|
variables = {
|
||||||
|
"transport_state": "PLAYING",
|
||||||
|
"current_play_mode": "NORMAL",
|
||||||
|
"current_crossfade_mode": "0",
|
||||||
|
"number_of_tracks": "1",
|
||||||
|
"current_track": "1",
|
||||||
|
"current_section": "0",
|
||||||
|
"current_track_uri": f"x-sonos-htastream:{soco.uid}:spdif",
|
||||||
|
"current_track_duration": "",
|
||||||
|
"current_track_meta_data": {
|
||||||
|
"title": " ",
|
||||||
|
"parent_id": "-1",
|
||||||
|
"item_id": "-1",
|
||||||
|
"restricted": True,
|
||||||
|
"resources": [],
|
||||||
|
"desc": None,
|
||||||
|
},
|
||||||
|
"next_track_uri": "",
|
||||||
|
"next_track_meta_data": "",
|
||||||
|
"enqueued_transport_uri": "",
|
||||||
|
"enqueued_transport_uri_meta_data": "",
|
||||||
|
"playback_storage_medium": "NETWORK",
|
||||||
|
"av_transport_uri": f"x-sonos-htastream:{soco.uid}:spdif",
|
||||||
|
"av_transport_uri_meta_data": {
|
||||||
|
"title": soco.uid,
|
||||||
|
"parent_id": "0",
|
||||||
|
"item_id": "spdif-input",
|
||||||
|
"restricted": False,
|
||||||
|
"resources": [],
|
||||||
|
"desc": None,
|
||||||
|
},
|
||||||
|
"current_transport_actions": "Set, Play",
|
||||||
|
"current_valid_play_modes": "",
|
||||||
|
}
|
||||||
|
return SonosMockEvent(soco, soco.avTransport, variables)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def mock_get_source_ip(mock_get_source_ip):
|
def mock_get_source_ip(mock_get_source_ip):
|
||||||
"""Mock network util's async_get_source_ip in all sonos tests."""
|
"""Mock network util's async_get_source_ip in all sonos tests."""
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
"""Tests for the Sonos battery sensor platform."""
|
"""Tests for the Sonos battery sensor platform."""
|
||||||
|
from unittest.mock import PropertyMock
|
||||||
|
|
||||||
from soco.exceptions import NotSupportedException
|
from soco.exceptions import NotSupportedException
|
||||||
|
|
||||||
|
from homeassistant.components.sensor import SCAN_INTERVAL
|
||||||
from homeassistant.components.sonos.binary_sensor import ATTR_BATTERY_POWER_SOURCE
|
from homeassistant.components.sonos.binary_sensor import ATTR_BATTERY_POWER_SOURCE
|
||||||
from homeassistant.const import STATE_OFF, STATE_ON
|
from homeassistant.const import STATE_OFF, STATE_ON
|
||||||
from homeassistant.helpers import entity_registry as ent_reg
|
from homeassistant.helpers import entity_registry as ent_reg
|
||||||
|
from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
|
from tests.common import async_fire_time_changed
|
||||||
|
|
||||||
|
|
||||||
async def test_entity_registry_unsupported(hass, async_setup_sonos, soco):
|
async def test_entity_registry_unsupported(hass, async_setup_sonos, soco):
|
||||||
@ -113,14 +119,46 @@ async def test_device_payload_without_battery_and_ignored_keys(
|
|||||||
assert ignored_payload not in caplog.text
|
assert ignored_payload not in caplog.text
|
||||||
|
|
||||||
|
|
||||||
async def test_audio_input_sensor(hass, async_autosetup_sonos, soco):
|
async def test_audio_input_sensor(
|
||||||
|
hass, async_autosetup_sonos, soco, tv_event, no_media_event
|
||||||
|
):
|
||||||
"""Test audio input sensor."""
|
"""Test audio input sensor."""
|
||||||
entity_registry = ent_reg.async_get(hass)
|
entity_registry = ent_reg.async_get(hass)
|
||||||
|
|
||||||
|
subscription = soco.avTransport.subscribe.return_value
|
||||||
|
sub_callback = subscription.callback
|
||||||
|
sub_callback(tv_event)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
audio_input_sensor = entity_registry.entities["sensor.zone_a_audio_input_format"]
|
audio_input_sensor = entity_registry.entities["sensor.zone_a_audio_input_format"]
|
||||||
audio_input_state = hass.states.get(audio_input_sensor.entity_id)
|
audio_input_state = hass.states.get(audio_input_sensor.entity_id)
|
||||||
assert audio_input_state.state == "Dolby 5.1"
|
assert audio_input_state.state == "Dolby 5.1"
|
||||||
|
|
||||||
|
# Set mocked input format to new value and ensure poll success
|
||||||
|
no_input_mock = PropertyMock(return_value="No input")
|
||||||
|
type(soco).soundbar_audio_input_format = no_input_mock
|
||||||
|
|
||||||
|
async_fire_time_changed(hass, dt_util.utcnow() + SCAN_INTERVAL)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
no_input_mock.assert_called_once()
|
||||||
|
audio_input_state = hass.states.get(audio_input_sensor.entity_id)
|
||||||
|
assert audio_input_state.state == "No input"
|
||||||
|
|
||||||
|
# Ensure state is not polled when source is not TV and state is already "No input"
|
||||||
|
sub_callback(no_media_event)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
unpolled_mock = PropertyMock(return_value="Will not be polled")
|
||||||
|
type(soco).soundbar_audio_input_format = unpolled_mock
|
||||||
|
|
||||||
|
async_fire_time_changed(hass, dt_util.utcnow() + SCAN_INTERVAL)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
unpolled_mock.assert_not_called()
|
||||||
|
audio_input_state = hass.states.get(audio_input_sensor.entity_id)
|
||||||
|
assert audio_input_state.state == "No input"
|
||||||
|
|
||||||
|
|
||||||
async def test_microphone_binary_sensor(
|
async def test_microphone_binary_sensor(
|
||||||
hass, async_autosetup_sonos, soco, device_properties_event
|
hass, async_autosetup_sonos, soco, device_properties_event
|
||||||
|
Loading…
x
Reference in New Issue
Block a user