Auto recreate HomeKit TVs when the sources are out of sync (#53208)

pull/53265/head
J. Nick Koston 2021-07-20 18:46:39 -10:00 committed by GitHub
parent 2a65c5f93c
commit f20602e11d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 51 additions and 6 deletions

View File

@ -58,12 +58,14 @@ from .const import (
CONF_LOW_BATTERY_THRESHOLD, CONF_LOW_BATTERY_THRESHOLD,
DEFAULT_LOW_BATTERY_THRESHOLD, DEFAULT_LOW_BATTERY_THRESHOLD,
DEVICE_CLASS_PM25, DEVICE_CLASS_PM25,
DOMAIN,
EVENT_HOMEKIT_CHANGED, EVENT_HOMEKIT_CHANGED,
HK_CHARGING, HK_CHARGING,
HK_NOT_CHARGABLE, HK_NOT_CHARGABLE,
HK_NOT_CHARGING, HK_NOT_CHARGING,
MANUFACTURER, MANUFACTURER,
SERV_BATTERY_SERVICE, SERV_BATTERY_SERVICE,
SERVICE_HOMEKIT_RESET_ACCESSORY,
TYPE_FAUCET, TYPE_FAUCET,
TYPE_OUTLET, TYPE_OUTLET,
TYPE_SHOWER, TYPE_SHOWER,
@ -454,6 +456,17 @@ class HomeAccessory(Accessory):
) )
) )
@ha_callback
def async_reset(self):
"""Reset and recreate an accessory."""
self.hass.async_create_task(
self.hass.services.async_call(
DOMAIN,
SERVICE_HOMEKIT_RESET_ACCESSORY,
{ATTR_ENTITY_ID: self.entity_id},
)
)
@ha_callback @ha_callback
def async_stop(self): def async_stop(self):
"""Cancel any subscriptions when the bridge is stopped.""" """Cancel any subscriptions when the bridge is stopped."""

View File

@ -87,6 +87,7 @@ class RemoteInputSelectAccessory(HomeAccessory):
features = state.attributes.get(ATTR_SUPPORTED_FEATURES, 0) features = state.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
self.source_key = source_key self.source_key = source_key
self.source_list_key = source_list_key
self.sources = [] self.sources = []
self.support_select_source = False self.support_select_source = False
if features & required_feature: if features & required_feature:
@ -152,11 +153,24 @@ class RemoteInputSelectAccessory(HomeAccessory):
index = self.sources.index(source_name) index = self.sources.index(source_name)
if self.char_input_source.value != index: if self.char_input_source.value != index:
self.char_input_source.set_value(index) self.char_input_source.set_value(index)
elif hk_state: return
_LOGGER.warning(
"%s: Sources out of sync. Restart Home Assistant", possible_sources = new_state.attributes.get(self.source_list_key, [])
if source_name in possible_sources:
_LOGGER.debug(
"%s: Sources out of sync. Rebuilding Accessory",
self.entity_id, self.entity_id,
) )
# Sources are out of sync, recreate the accessory
self.async_reset()
return
_LOGGER.debug(
"%s: Source %s does not exist the source list: %s",
self.entity_id,
source_name,
possible_sources,
)
if self.char_input_source.value != 0: if self.char_input_source.value != 0:
self.char_input_source.set_value(0) self.char_input_source.set_value(0)

View File

@ -242,7 +242,7 @@ async def test_media_player_television(hass, hk_driver, events, caplog):
hass.states.async_set(entity_id, STATE_ON, {ATTR_INPUT_SOURCE: "HDMI 5"}) hass.states.async_set(entity_id, STATE_ON, {ATTR_INPUT_SOURCE: "HDMI 5"})
await hass.async_block_till_done() await hass.async_block_till_done()
assert acc.char_input_source.value == 0 assert acc.char_input_source.value == 0
assert caplog.records[-2].levelname == "WARNING" assert caplog.records[-2].levelname == "DEBUG"
# Set from HomeKit # Set from HomeKit
call_turn_on = async_mock_service(hass, DOMAIN, "turn_on") call_turn_on = async_mock_service(hass, DOMAIN, "turn_on")

View File

@ -3,8 +3,10 @@
from homeassistant.components.homekit.const import ( from homeassistant.components.homekit.const import (
ATTR_KEY_NAME, ATTR_KEY_NAME,
ATTR_VALUE, ATTR_VALUE,
DOMAIN as HOMEKIT_DOMAIN,
EVENT_HOMEKIT_TV_REMOTE_KEY_PRESSED, EVENT_HOMEKIT_TV_REMOTE_KEY_PRESSED,
KEY_ARROW_RIGHT, KEY_ARROW_RIGHT,
SERVICE_HOMEKIT_RESET_ACCESSORY,
) )
from homeassistant.components.homekit.type_remotes import ActivityRemote from homeassistant.components.homekit.type_remotes import ActivityRemote
from homeassistant.components.remote import ( from homeassistant.components.remote import (
@ -146,3 +148,19 @@ async def test_activity_remote(hass, hk_driver, events, caplog):
assert len(events) == 1 assert len(events) == 1
assert events[0].data[ATTR_KEY_NAME] == KEY_ARROW_RIGHT assert events[0].data[ATTR_KEY_NAME] == KEY_ARROW_RIGHT
call_reset_accessory = async_mock_service(
hass, HOMEKIT_DOMAIN, SERVICE_HOMEKIT_RESET_ACCESSORY
)
# A wild source appears - The accessory should rebuild itself
hass.states.async_set(
entity_id,
STATE_ON,
{
ATTR_SUPPORTED_FEATURES: SUPPORT_ACTIVITY,
ATTR_CURRENT_ACTIVITY: "Amazon TV",
ATTR_ACTIVITY_LIST: ["TV", "Apple TV", "Amazon TV"],
},
)
await hass.async_block_till_done()
assert call_reset_accessory[0].data[ATTR_ENTITY_ID] == entity_id