mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 04:07:08 +00:00
Avoid unnecessary cast state updates (#13770)
* Avoid unnecessary cast state updates * Add test * Fixed bad syntax * Fixed imports * Fixed test
This commit is contained in:
parent
99ded8a0a6
commit
80a3220b88
@ -288,7 +288,8 @@ class CastDevice(MediaPlayerDevice):
|
|||||||
self._chromecast = None # type: Optional[pychromecast.Chromecast]
|
self._chromecast = None # type: Optional[pychromecast.Chromecast]
|
||||||
self.cast_status = None
|
self.cast_status = None
|
||||||
self.media_status = None
|
self.media_status = None
|
||||||
self.media_status_received = None
|
self.media_status_position = None
|
||||||
|
self.media_status_position_received = None
|
||||||
self._available = False # type: bool
|
self._available = False # type: bool
|
||||||
self._status_listener = None # type: Optional[CastStatusListener]
|
self._status_listener = None # type: Optional[CastStatusListener]
|
||||||
|
|
||||||
@ -361,7 +362,8 @@ class CastDevice(MediaPlayerDevice):
|
|||||||
self._chromecast = None
|
self._chromecast = None
|
||||||
self.cast_status = None
|
self.cast_status = None
|
||||||
self.media_status = None
|
self.media_status = None
|
||||||
self.media_status_received = None
|
self.media_status_position = None
|
||||||
|
self.media_status_position_received = None
|
||||||
self._status_listener.invalidate()
|
self._status_listener.invalidate()
|
||||||
self._status_listener = None
|
self._status_listener = None
|
||||||
|
|
||||||
@ -388,8 +390,36 @@ class CastDevice(MediaPlayerDevice):
|
|||||||
|
|
||||||
def new_media_status(self, media_status):
|
def new_media_status(self, media_status):
|
||||||
"""Handle updates of the media status."""
|
"""Handle updates of the media status."""
|
||||||
|
# Only use media position for playing/paused,
|
||||||
|
# and for normal playback rate
|
||||||
|
if (media_status is None or
|
||||||
|
abs(media_status.playback_rate - 1) > 0.01 or
|
||||||
|
not (media_status.player_is_playing or
|
||||||
|
media_status.player_is_paused)):
|
||||||
|
self.media_status_position = None
|
||||||
|
self.media_status_position_received = None
|
||||||
|
else:
|
||||||
|
# Avoid unnecessary state attribute updates if player_state and
|
||||||
|
# calculated position stay the same
|
||||||
|
now = dt_util.utcnow()
|
||||||
|
do_update = \
|
||||||
|
(self.media_status is None or
|
||||||
|
self.media_status_position is None or
|
||||||
|
self.media_status.player_state != media_status.player_state)
|
||||||
|
if not do_update:
|
||||||
|
if media_status.player_is_playing:
|
||||||
|
elapsed = now - self.media_status_position_received
|
||||||
|
do_update = abs(media_status.current_time -
|
||||||
|
(self.media_status_position +
|
||||||
|
elapsed.total_seconds())) > 1
|
||||||
|
else:
|
||||||
|
do_update = \
|
||||||
|
self.media_status_position != media_status.current_time
|
||||||
|
if do_update:
|
||||||
|
self.media_status_position = media_status.current_time
|
||||||
|
self.media_status_position_received = now
|
||||||
|
|
||||||
self.media_status = media_status
|
self.media_status = media_status
|
||||||
self.media_status_received = dt_util.utcnow()
|
|
||||||
self.schedule_update_ha_state()
|
self.schedule_update_ha_state()
|
||||||
|
|
||||||
def new_connection_status(self, connection_status):
|
def new_connection_status(self, connection_status):
|
||||||
@ -595,13 +625,7 @@ class CastDevice(MediaPlayerDevice):
|
|||||||
@property
|
@property
|
||||||
def media_position(self):
|
def media_position(self):
|
||||||
"""Position of current playing media in seconds."""
|
"""Position of current playing media in seconds."""
|
||||||
if self.media_status is None or \
|
return self.media_status_position
|
||||||
not (self.media_status.player_is_playing or
|
|
||||||
self.media_status.player_is_paused or
|
|
||||||
self.media_status.player_is_idle):
|
|
||||||
return None
|
|
||||||
|
|
||||||
return self.media_status.current_time
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def media_position_updated_at(self):
|
def media_position_updated_at(self):
|
||||||
@ -609,7 +633,7 @@ class CastDevice(MediaPlayerDevice):
|
|||||||
|
|
||||||
Returns value from homeassistant.util.dt.utcnow().
|
Returns value from homeassistant.util.dt.utcnow().
|
||||||
"""
|
"""
|
||||||
return self.media_status_received
|
return self.media_status_position_received
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self) -> Optional[str]:
|
def unique_id(self) -> Optional[str]:
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
"""The tests for the Cast Media player platform."""
|
"""The tests for the Cast Media player platform."""
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import datetime as dt
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from unittest.mock import patch, MagicMock, Mock
|
from unittest.mock import patch, MagicMock, Mock
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
@ -14,7 +15,8 @@ from homeassistant.components.media_player.cast import ChromecastInfo
|
|||||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect, \
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect, \
|
||||||
async_dispatcher_send
|
async_dispatcher_send
|
||||||
from homeassistant.components.media_player import cast
|
from homeassistant.components.media_player import cast, \
|
||||||
|
ATTR_MEDIA_POSITION, ATTR_MEDIA_POSITION_UPDATED_AT
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
|
|
||||||
@ -286,6 +288,8 @@ async def test_entity_media_states(hass: HomeAssistantType):
|
|||||||
assert entity.unique_id == full_info.uuid
|
assert entity.unique_id == full_info.uuid
|
||||||
|
|
||||||
media_status = MagicMock(images=None)
|
media_status = MagicMock(images=None)
|
||||||
|
media_status.current_time = 0
|
||||||
|
media_status.playback_rate = 1
|
||||||
media_status.player_is_playing = True
|
media_status.player_is_playing = True
|
||||||
entity.new_media_status(media_status)
|
entity.new_media_status(media_status)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
@ -320,6 +324,85 @@ async def test_entity_media_states(hass: HomeAssistantType):
|
|||||||
assert state.state == 'unknown'
|
assert state.state == 'unknown'
|
||||||
|
|
||||||
|
|
||||||
|
async def test_entity_media_position(hass: HomeAssistantType):
|
||||||
|
"""Test various entity media states."""
|
||||||
|
info = get_fake_chromecast_info()
|
||||||
|
full_info = attr.evolve(info, model_name='google home',
|
||||||
|
friendly_name='Speaker', uuid=FakeUUID)
|
||||||
|
|
||||||
|
with patch('pychromecast.dial.get_device_status',
|
||||||
|
return_value=full_info):
|
||||||
|
chromecast, entity = await async_setup_media_player_cast(hass, info)
|
||||||
|
|
||||||
|
media_status = MagicMock(images=None)
|
||||||
|
media_status.current_time = 10
|
||||||
|
media_status.playback_rate = 1
|
||||||
|
media_status.player_is_playing = True
|
||||||
|
media_status.player_is_paused = False
|
||||||
|
media_status.player_is_idle = False
|
||||||
|
now = dt.datetime.now(dt.timezone.utc)
|
||||||
|
with patch('homeassistant.util.dt.utcnow', return_value=now):
|
||||||
|
entity.new_media_status(media_status)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
state = hass.states.get('media_player.speaker')
|
||||||
|
assert state.attributes[ATTR_MEDIA_POSITION] == 10
|
||||||
|
assert state.attributes[ATTR_MEDIA_POSITION_UPDATED_AT] == now
|
||||||
|
|
||||||
|
media_status.current_time = 15
|
||||||
|
now_plus_5 = now + dt.timedelta(seconds=5)
|
||||||
|
with patch('homeassistant.util.dt.utcnow', return_value=now_plus_5):
|
||||||
|
entity.new_media_status(media_status)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
state = hass.states.get('media_player.speaker')
|
||||||
|
assert state.attributes[ATTR_MEDIA_POSITION] == 10
|
||||||
|
assert state.attributes[ATTR_MEDIA_POSITION_UPDATED_AT] == now
|
||||||
|
|
||||||
|
media_status.current_time = 20
|
||||||
|
with patch('homeassistant.util.dt.utcnow', return_value=now_plus_5):
|
||||||
|
entity.new_media_status(media_status)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
state = hass.states.get('media_player.speaker')
|
||||||
|
assert state.attributes[ATTR_MEDIA_POSITION] == 20
|
||||||
|
assert state.attributes[ATTR_MEDIA_POSITION_UPDATED_AT] == now_plus_5
|
||||||
|
|
||||||
|
media_status.current_time = 25
|
||||||
|
now_plus_10 = now + dt.timedelta(seconds=10)
|
||||||
|
media_status.player_is_playing = False
|
||||||
|
media_status.player_is_paused = True
|
||||||
|
with patch('homeassistant.util.dt.utcnow', return_value=now_plus_10):
|
||||||
|
entity.new_media_status(media_status)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
state = hass.states.get('media_player.speaker')
|
||||||
|
assert state.attributes[ATTR_MEDIA_POSITION] == 25
|
||||||
|
assert state.attributes[ATTR_MEDIA_POSITION_UPDATED_AT] == now_plus_10
|
||||||
|
|
||||||
|
now_plus_15 = now + dt.timedelta(seconds=15)
|
||||||
|
with patch('homeassistant.util.dt.utcnow', return_value=now_plus_15):
|
||||||
|
entity.new_media_status(media_status)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
state = hass.states.get('media_player.speaker')
|
||||||
|
assert state.attributes[ATTR_MEDIA_POSITION] == 25
|
||||||
|
assert state.attributes[ATTR_MEDIA_POSITION_UPDATED_AT] == now_plus_10
|
||||||
|
|
||||||
|
media_status.current_time = 30
|
||||||
|
now_plus_20 = now + dt.timedelta(seconds=20)
|
||||||
|
with patch('homeassistant.util.dt.utcnow', return_value=now_plus_20):
|
||||||
|
entity.new_media_status(media_status)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
state = hass.states.get('media_player.speaker')
|
||||||
|
assert state.attributes[ATTR_MEDIA_POSITION] == 30
|
||||||
|
assert state.attributes[ATTR_MEDIA_POSITION_UPDATED_AT] == now_plus_20
|
||||||
|
|
||||||
|
media_status.player_is_paused = False
|
||||||
|
media_status.player_is_idle = True
|
||||||
|
with patch('homeassistant.util.dt.utcnow', return_value=now_plus_20):
|
||||||
|
entity.new_media_status(media_status)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
state = hass.states.get('media_player.speaker')
|
||||||
|
assert ATTR_MEDIA_POSITION not in state.attributes
|
||||||
|
assert ATTR_MEDIA_POSITION_UPDATED_AT not in state.attributes
|
||||||
|
|
||||||
|
|
||||||
async def test_switched_host(hass: HomeAssistantType):
|
async def test_switched_host(hass: HomeAssistantType):
|
||||||
"""Test cast device listens for changed hosts and disconnects old cast."""
|
"""Test cast device listens for changed hosts and disconnects old cast."""
|
||||||
info = get_fake_chromecast_info()
|
info = get_fake_chromecast_info()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user