Allow hdmi_cec to recover from lost connection to adapter without restart (#40714)

* Only update CecDevice state when there is new data

* Replace CecDevice with CecEntity

* Support for losing and reconnecting to pycec TcpAdapter

* Register listener in async_added_to_hass

* Rename hdmi_cec watchdog

* Only update CecDevice state when there is new data

* Fix flake8 docstring error

* Fix linter error

* Bump pycec version to 0.5.0

* Bump pycec version to 0.5.1

* Fixe merge mistake

Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
Raj Laud 2021-03-18 07:07:35 -05:00 committed by GitHub
parent 25a13d1554
commit 99f9f8dec0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 30 additions and 5 deletions

View File

@ -1,6 +1,6 @@
"""Support for HDMI CEC.""" """Support for HDMI CEC."""
from collections import defaultdict from collections import defaultdict
from functools import reduce from functools import partial, reduce
import logging import logging
import multiprocessing import multiprocessing
@ -38,9 +38,10 @@ from homeassistant.const import (
STATE_ON, STATE_ON,
STATE_PAUSED, STATE_PAUSED,
STATE_PLAYING, STATE_PLAYING,
STATE_UNAVAILABLE,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import discovery from homeassistant.helpers import discovery, event
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
@ -162,6 +163,9 @@ CONFIG_SCHEMA = vol.Schema(
extra=vol.ALLOW_EXTRA, extra=vol.ALLOW_EXTRA,
) )
WATCHDOG_INTERVAL = 120
EVENT_HDMI_CEC_UNAVAILABLE = "hdmi_cec_unavailable"
def pad_physical_address(addr): def pad_physical_address(addr):
"""Right-pad a physical address.""" """Right-pad a physical address."""
@ -210,6 +214,18 @@ def setup(hass: HomeAssistant, base_config):
adapter = CecAdapter(name=display_name[:12], activate_source=False) adapter = CecAdapter(name=display_name[:12], activate_source=False)
hdmi_network = HDMINetwork(adapter, loop=loop) hdmi_network = HDMINetwork(adapter, loop=loop)
def _adapter_watchdog(now=None):
_LOGGER.debug("Reached _adapter_watchdog")
event.async_call_later(hass, WATCHDOG_INTERVAL, _adapter_watchdog)
if not adapter.initialized:
_LOGGER.info("Adapter not initialized. Trying to restart.")
hass.bus.fire(EVENT_HDMI_CEC_UNAVAILABLE)
adapter.init()
hdmi_network.set_initialized_callback(
partial(event.async_call_later, hass, WATCHDOG_INTERVAL, _adapter_watchdog)
)
def _volume(call): def _volume(call):
"""Increase/decrease volume and mute/unmute system.""" """Increase/decrease volume and mute/unmute system."""
mute_key_mapping = { mute_key_mapping = {
@ -327,7 +343,7 @@ def setup(hass: HomeAssistant, base_config):
def _shutdown(call): def _shutdown(call):
hdmi_network.stop() hdmi_network.stop()
def _start_cec(event): def _start_cec(callback_event):
"""Register services and start HDMI network to watch for devices.""" """Register services and start HDMI network to watch for devices."""
hass.services.register( hass.services.register(
DOMAIN, SERVICE_SEND_COMMAND, _tx, SERVICE_SEND_COMMAND_SCHEMA DOMAIN, SERVICE_SEND_COMMAND, _tx, SERVICE_SEND_COMMAND_SCHEMA
@ -364,6 +380,12 @@ class CecEntity(Entity):
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)
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.schedule_update_ha_state(False)
def update(self): def update(self):
"""Update device status.""" """Update device status."""
device = self._device device = self._device
@ -383,6 +405,9 @@ class CecEntity(Entity):
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)
self.hass.bus.async_listen(
EVENT_HDMI_CEC_UNAVAILABLE, self._hdmi_cec_unavailable
)
def _update(self, device=None): def _update(self, device=None):
"""Device status changed, schedule an update.""" """Device status changed, schedule an update."""

View File

@ -2,6 +2,6 @@
"domain": "hdmi_cec", "domain": "hdmi_cec",
"name": "HDMI-CEC", "name": "HDMI-CEC",
"documentation": "https://www.home-assistant.io/integrations/hdmi_cec", "documentation": "https://www.home-assistant.io/integrations/hdmi_cec",
"requirements": ["pyCEC==0.4.14"], "requirements": ["pyCEC==0.5.1"],
"codeowners": [] "codeowners": []
} }

View File

@ -1216,7 +1216,7 @@ py-zabbix==1.1.7
py17track==2.2.2 py17track==2.2.2
# homeassistant.components.hdmi_cec # homeassistant.components.hdmi_cec
pyCEC==0.4.14 pyCEC==0.5.1
# homeassistant.components.control4 # homeassistant.components.control4
pyControl4==0.0.6 pyControl4==0.0.6