From b29605060a74c441550708ccf4ace4b697f66ae6 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 15 Sep 2022 17:48:05 +0200 Subject: [PATCH] Enforce MediaPlayerState in hdmi_cec media player (#78522) --- homeassistant/components/hdmi_cec/__init__.py | 33 ++----------------- .../components/hdmi_cec/media_player.py | 25 ++++++-------- homeassistant/components/hdmi_cec/switch.py | 20 +++++++---- tests/components/hdmi_cec/test_switch.py | 17 +++------- 4 files changed, 29 insertions(+), 66 deletions(-) diff --git a/homeassistant/components/hdmi_cec/__init__.py b/homeassistant/components/hdmi_cec/__init__.py index fa64b3dac41..05d5ab57841 100644 --- a/homeassistant/components/hdmi_cec/__init__.py +++ b/homeassistant/components/hdmi_cec/__init__.py @@ -17,11 +17,6 @@ from pycec.const import ( KEY_MUTE_TOGGLE, KEY_VOLUME_DOWN, KEY_VOLUME_UP, - POWER_OFF, - POWER_ON, - STATUS_PLAY, - STATUS_STILL, - STATUS_STOP, ) from pycec.network import HDMINetwork, PhysicalAddress from pycec.tcp import TcpAdapter @@ -35,12 +30,6 @@ from homeassistant.const import ( CONF_PLATFORM, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, - STATE_IDLE, - STATE_OFF, - STATE_ON, - STATE_PAUSED, - STATE_PLAYING, - STATE_UNAVAILABLE, ) from homeassistant.core import HomeAssistant, ServiceCall, callback from homeassistant.helpers import discovery, event @@ -382,7 +371,6 @@ class CecEntity(Entity): def __init__(self, device, logical) -> None: """Initialize the device.""" self._device = device - self._state: str | None = None self._logical_address = logical self.entity_id = "%s.%d" % (DOMAIN, self._logical_address) 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})" def _hdmi_cec_unavailable(self, callback_event): - # Change state to unavailable. Without this, entity would remain in - # its last state, since the state changes are pushed. - self._state = STATE_UNAVAILABLE + self._attr_available = 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): """Register HDMI callbacks after initialization.""" self._device.set_update_callback(self._update) @@ -435,6 +405,7 @@ class CecEntity(Entity): def _update(self, device=None): """Device status changed, schedule an update.""" + self._attr_available = True self.schedule_update_ha_state(True) @property diff --git a/homeassistant/components/hdmi_cec/media_player.py b/homeassistant/components/hdmi_cec/media_player.py index 203ce85a6b2..cfe73ff7c40 100644 --- a/homeassistant/components/hdmi_cec/media_player.py +++ b/homeassistant/components/hdmi_cec/media_player.py @@ -89,7 +89,7 @@ class CecPlayerEntity(CecEntity, MediaPlayerEntity): def turn_on(self) -> None: """Turn device on.""" self._device.turn_on() - self._state = MediaPlayerState.ON + self._attr_state = MediaPlayerState.ON def clear_playlist(self) -> None: """Clear players playlist.""" @@ -98,12 +98,12 @@ class CecPlayerEntity(CecEntity, MediaPlayerEntity): def turn_off(self) -> None: """Turn device off.""" self._device.turn_off() - self._state = MediaPlayerState.OFF + self._attr_state = MediaPlayerState.OFF def media_stop(self) -> None: """Stop playback.""" 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: """Not supported.""" @@ -124,7 +124,7 @@ class CecPlayerEntity(CecEntity, MediaPlayerEntity): def media_pause(self) -> None: """Pause playback.""" self.send_keypress(KEY_PAUSE) - self._state = MediaPlayerState.PAUSED + self._attr_state = MediaPlayerState.PAUSED def select_source(self, source: str) -> None: """Not supported.""" @@ -133,7 +133,7 @@ class CecPlayerEntity(CecEntity, MediaPlayerEntity): def media_play(self) -> None: """Start playback.""" self.send_keypress(KEY_PLAY) - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING def volume_up(self) -> None: """Increase volume.""" @@ -145,25 +145,20 @@ class CecPlayerEntity(CecEntity, MediaPlayerEntity): _LOGGER.debug("%s: volume down", self._logical_address) self.send_keypress(KEY_VOLUME_DOWN) - @property - def state(self) -> str | None: - """Cache state of device.""" - return self._state - def update(self) -> None: """Update device status.""" device = self._device if device.power_status in [POWER_OFF, 3]: - self._state = MediaPlayerState.OFF + self._attr_state = MediaPlayerState.OFF elif not self.support_pause: if device.power_status in [POWER_ON, 4]: - self._state = MediaPlayerState.ON + self._attr_state = MediaPlayerState.ON elif device.status == STATUS_PLAY: - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING elif device.status == STATUS_STOP: - self._state = MediaPlayerState.IDLE + self._attr_state = MediaPlayerState.IDLE elif device.status == STATUS_STILL: - self._state = MediaPlayerState.PAUSED + self._attr_state = MediaPlayerState.PAUSED else: _LOGGER.warning("Unknown state: %s", device.status) diff --git a/homeassistant/components/hdmi_cec/switch.py b/homeassistant/components/hdmi_cec/switch.py index b44e5ce5c64..a554594a219 100644 --- a/homeassistant/components/hdmi_cec/switch.py +++ b/homeassistant/components/hdmi_cec/switch.py @@ -4,8 +4,9 @@ from __future__ import annotations import logging from typing import Any +from pycec.const import POWER_OFF, POWER_ON + 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.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -44,16 +45,21 @@ class CecSwitchEntity(CecEntity, SwitchEntity): def turn_on(self, **kwargs: Any) -> None: """Turn device on.""" self._device.turn_on() - self._state = STATE_ON + self._attr_is_on = True self.schedule_update_ha_state(force_refresh=False) def turn_off(self, **kwargs: Any) -> None: """Turn device off.""" self._device.turn_off() - self._state = STATE_OFF + self._attr_is_on = False self.schedule_update_ha_state(force_refresh=False) - @property - def is_on(self) -> bool: - """Return True if entity is on.""" - return self._state == STATE_ON + def update(self) -> None: + """Update device status.""" + device = self._device + 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) diff --git a/tests/components/hdmi_cec/test_switch.py b/tests/components/hdmi_cec/test_switch.py index 61e1e03e4a6..999037967dd 100644 --- a/tests/components/hdmi_cec/test_switch.py +++ b/tests/components/hdmi_cec/test_switch.py @@ -1,15 +1,9 @@ """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 -from homeassistant.components.hdmi_cec import ( - EVENT_HDMI_CEC_UNAVAILABLE, - POWER_OFF, - POWER_ON, - STATUS_PLAY, - STATUS_STILL, - STATUS_STOP, - PhysicalAddress, -) +from homeassistant.components.hdmi_cec import EVENT_HDMI_CEC_UNAVAILABLE from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN from homeassistant.const import ( ATTR_ENTITY_ID, @@ -20,7 +14,7 @@ from homeassistant.const import ( STATE_UNAVAILABLE, ) -from tests.components.hdmi_cec import MockHDMIDevice +from . import MockHDMIDevice @pytest.mark.parametrize("config", [{}, {"platform": "switch"}]) @@ -236,9 +230,6 @@ async def test_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): """Test entity goes into unavailable status when expected.""" hdmi_network = await create_hdmi_network()