Enforce MediaPlayerState in hdmi_cec media player (#78522)

This commit is contained in:
epenet 2022-09-15 17:48:05 +02:00 committed by GitHub
parent 7d11509011
commit b29605060a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 29 additions and 66 deletions

View file

@ -17,11 +17,6 @@ from pycec.const import (
KEY_MUTE_TOGGLE, KEY_MUTE_TOGGLE,
KEY_VOLUME_DOWN, KEY_VOLUME_DOWN,
KEY_VOLUME_UP, KEY_VOLUME_UP,
POWER_OFF,
POWER_ON,
STATUS_PLAY,
STATUS_STILL,
STATUS_STOP,
) )
from pycec.network import HDMINetwork, PhysicalAddress from pycec.network import HDMINetwork, PhysicalAddress
from pycec.tcp import TcpAdapter from pycec.tcp import TcpAdapter
@ -35,12 +30,6 @@ from homeassistant.const import (
CONF_PLATFORM, CONF_PLATFORM,
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_STOP,
STATE_IDLE,
STATE_OFF,
STATE_ON,
STATE_PAUSED,
STATE_PLAYING,
STATE_UNAVAILABLE,
) )
from homeassistant.core import HomeAssistant, ServiceCall, callback from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.helpers import discovery, event from homeassistant.helpers import discovery, event
@ -382,7 +371,6 @@ class CecEntity(Entity):
def __init__(self, device, logical) -> None: def __init__(self, device, logical) -> None:
"""Initialize the device.""" """Initialize the device."""
self._device = device self._device = device
self._state: str | None = None
self._logical_address = logical self._logical_address = logical
self.entity_id = "%s.%d" % (DOMAIN, self._logical_address) self.entity_id = "%s.%d" % (DOMAIN, self._logical_address)
self._set_attr_name() self._set_attr_name()
@ -405,27 +393,9 @@ class CecEntity(Entity):
self._attr_name = f"{self._device.type_name} {self._logical_address} ({self._device.osd_name})" self._attr_name = f"{self._device.type_name} {self._logical_address} ({self._device.osd_name})"
def _hdmi_cec_unavailable(self, callback_event): def _hdmi_cec_unavailable(self, callback_event):
# Change state to unavailable. Without this, entity would remain in self._attr_available = False
# its last state, since the state changes are pushed.
self._state = STATE_UNAVAILABLE
self.schedule_update_ha_state(False) self.schedule_update_ha_state(False)
def update(self):
"""Update device status."""
device = self._device
if device.power_status in [POWER_OFF, 3]:
self._state = STATE_OFF
elif device.status == STATUS_PLAY:
self._state = STATE_PLAYING
elif device.status == STATUS_STOP:
self._state = STATE_IDLE
elif device.status == STATUS_STILL:
self._state = STATE_PAUSED
elif device.power_status in [POWER_ON, 4]:
self._state = STATE_ON
else:
_LOGGER.warning("Unknown state: %d", device.power_status)
async def async_added_to_hass(self): async def async_added_to_hass(self):
"""Register HDMI callbacks after initialization.""" """Register HDMI callbacks after initialization."""
self._device.set_update_callback(self._update) self._device.set_update_callback(self._update)
@ -435,6 +405,7 @@ class CecEntity(Entity):
def _update(self, device=None): def _update(self, device=None):
"""Device status changed, schedule an update.""" """Device status changed, schedule an update."""
self._attr_available = True
self.schedule_update_ha_state(True) self.schedule_update_ha_state(True)
@property @property

View file

@ -89,7 +89,7 @@ class CecPlayerEntity(CecEntity, MediaPlayerEntity):
def turn_on(self) -> None: def turn_on(self) -> None:
"""Turn device on.""" """Turn device on."""
self._device.turn_on() self._device.turn_on()
self._state = MediaPlayerState.ON self._attr_state = MediaPlayerState.ON
def clear_playlist(self) -> None: def clear_playlist(self) -> None:
"""Clear players playlist.""" """Clear players playlist."""
@ -98,12 +98,12 @@ class CecPlayerEntity(CecEntity, MediaPlayerEntity):
def turn_off(self) -> None: def turn_off(self) -> None:
"""Turn device off.""" """Turn device off."""
self._device.turn_off() self._device.turn_off()
self._state = MediaPlayerState.OFF self._attr_state = MediaPlayerState.OFF
def media_stop(self) -> None: def media_stop(self) -> None:
"""Stop playback.""" """Stop playback."""
self.send_keypress(KEY_STOP) self.send_keypress(KEY_STOP)
self._state = MediaPlayerState.IDLE self._attr_state = MediaPlayerState.IDLE
def play_media(self, media_type: str, media_id: str, **kwargs: Any) -> None: def play_media(self, media_type: str, media_id: str, **kwargs: Any) -> None:
"""Not supported.""" """Not supported."""
@ -124,7 +124,7 @@ class CecPlayerEntity(CecEntity, MediaPlayerEntity):
def media_pause(self) -> None: def media_pause(self) -> None:
"""Pause playback.""" """Pause playback."""
self.send_keypress(KEY_PAUSE) self.send_keypress(KEY_PAUSE)
self._state = MediaPlayerState.PAUSED self._attr_state = MediaPlayerState.PAUSED
def select_source(self, source: str) -> None: def select_source(self, source: str) -> None:
"""Not supported.""" """Not supported."""
@ -133,7 +133,7 @@ class CecPlayerEntity(CecEntity, MediaPlayerEntity):
def media_play(self) -> None: def media_play(self) -> None:
"""Start playback.""" """Start playback."""
self.send_keypress(KEY_PLAY) self.send_keypress(KEY_PLAY)
self._state = MediaPlayerState.PLAYING self._attr_state = MediaPlayerState.PLAYING
def volume_up(self) -> None: def volume_up(self) -> None:
"""Increase volume.""" """Increase volume."""
@ -145,25 +145,20 @@ class CecPlayerEntity(CecEntity, MediaPlayerEntity):
_LOGGER.debug("%s: volume down", self._logical_address) _LOGGER.debug("%s: volume down", self._logical_address)
self.send_keypress(KEY_VOLUME_DOWN) self.send_keypress(KEY_VOLUME_DOWN)
@property
def state(self) -> str | None:
"""Cache state of device."""
return self._state
def update(self) -> None: def update(self) -> None:
"""Update device status.""" """Update device status."""
device = self._device device = self._device
if device.power_status in [POWER_OFF, 3]: if device.power_status in [POWER_OFF, 3]:
self._state = MediaPlayerState.OFF self._attr_state = MediaPlayerState.OFF
elif not self.support_pause: elif not self.support_pause:
if device.power_status in [POWER_ON, 4]: if device.power_status in [POWER_ON, 4]:
self._state = MediaPlayerState.ON self._attr_state = MediaPlayerState.ON
elif device.status == STATUS_PLAY: elif device.status == STATUS_PLAY:
self._state = MediaPlayerState.PLAYING self._attr_state = MediaPlayerState.PLAYING
elif device.status == STATUS_STOP: elif device.status == STATUS_STOP:
self._state = MediaPlayerState.IDLE self._attr_state = MediaPlayerState.IDLE
elif device.status == STATUS_STILL: elif device.status == STATUS_STILL:
self._state = MediaPlayerState.PAUSED self._attr_state = MediaPlayerState.PAUSED
else: else:
_LOGGER.warning("Unknown state: %s", device.status) _LOGGER.warning("Unknown state: %s", device.status)

View file

@ -4,8 +4,9 @@ from __future__ import annotations
import logging import logging
from typing import Any from typing import Any
from pycec.const import POWER_OFF, POWER_ON
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN, SwitchEntity from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN, SwitchEntity
from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
@ -44,16 +45,21 @@ class CecSwitchEntity(CecEntity, SwitchEntity):
def turn_on(self, **kwargs: Any) -> None: def turn_on(self, **kwargs: Any) -> None:
"""Turn device on.""" """Turn device on."""
self._device.turn_on() self._device.turn_on()
self._state = STATE_ON self._attr_is_on = True
self.schedule_update_ha_state(force_refresh=False) self.schedule_update_ha_state(force_refresh=False)
def turn_off(self, **kwargs: Any) -> None: def turn_off(self, **kwargs: Any) -> None:
"""Turn device off.""" """Turn device off."""
self._device.turn_off() self._device.turn_off()
self._state = STATE_OFF self._attr_is_on = False
self.schedule_update_ha_state(force_refresh=False) self.schedule_update_ha_state(force_refresh=False)
@property def update(self) -> None:
def is_on(self) -> bool: """Update device status."""
"""Return True if entity is on.""" device = self._device
return self._state == STATE_ON if device.power_status in {POWER_OFF, 3}:
self._attr_is_on = False
elif device.power_status in {POWER_ON, 4}:
self._attr_is_on = True
else:
_LOGGER.warning("Unknown state: %d", device.power_status)

View file

@ -1,15 +1,9 @@
"""Tests for the HDMI-CEC switch platform.""" """Tests for the HDMI-CEC switch platform."""
from pycec.const import POWER_OFF, POWER_ON, STATUS_PLAY, STATUS_STILL, STATUS_STOP
from pycec.network import PhysicalAddress
import pytest import pytest
from homeassistant.components.hdmi_cec import ( from homeassistant.components.hdmi_cec import EVENT_HDMI_CEC_UNAVAILABLE
EVENT_HDMI_CEC_UNAVAILABLE,
POWER_OFF,
POWER_ON,
STATUS_PLAY,
STATUS_STILL,
STATUS_STOP,
PhysicalAddress,
)
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_ID,
@ -20,7 +14,7 @@ from homeassistant.const import (
STATE_UNAVAILABLE, STATE_UNAVAILABLE,
) )
from tests.components.hdmi_cec import MockHDMIDevice from . import MockHDMIDevice
@pytest.mark.parametrize("config", [{}, {"platform": "switch"}]) @pytest.mark.parametrize("config", [{}, {"platform": "switch"}])
@ -236,9 +230,6 @@ async def test_icon(
assert state.attributes["icon"] == expected_icon assert state.attributes["icon"] == expected_icon
@pytest.mark.xfail(
reason="The code only sets the state to unavailable, doesn't set the `_attr_available` to false."
)
async def test_unavailable_status(hass, create_hdmi_network, create_cec_entity): async def test_unavailable_status(hass, create_hdmi_network, create_cec_entity):
"""Test entity goes into unavailable status when expected.""" """Test entity goes into unavailable status when expected."""
hdmi_network = await create_hdmi_network() hdmi_network = await create_hdmi_network()