Fix HomeKit media players when entity has duplicate sources (#83890)

fixes #83852 fixes #83698
This commit is contained in:
J. Nick Koston 2022-12-12 16:29:06 -10:00 committed by GitHub
parent b9753a9f92
commit 692a732555
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 63 additions and 3 deletions

View File

@ -18,7 +18,7 @@ from homeassistant.const import (
SERVICE_TURN_ON,
STATE_ON,
)
from homeassistant.core import callback
from homeassistant.core import State, callback
from .accessories import TYPES, HomeAccessory
from .const import (
@ -96,7 +96,7 @@ class RemoteInputSelectAccessory(HomeAccessory, ABC):
self.sources = []
self.support_select_source = False
if features & required_feature:
sources = state.attributes.get(source_list_key, [])
sources = self._get_ordered_source_list_from_state(state)
if len(sources) > MAXIMUM_SOURCES:
_LOGGER.warning(
"%s: Reached maximum number of sources (%s)",
@ -143,6 +143,21 @@ class RemoteInputSelectAccessory(HomeAccessory, ABC):
serv_input.configure_char(CHAR_CURRENT_VISIBILITY_STATE, value=False)
_LOGGER.debug("%s: Added source %s", self.entity_id, source)
def _get_ordered_source_list_from_state(self, state: State) -> list[str]:
"""Return ordered source list while preserving order with duplicates removed.
Some integrations have duplicate sources in the source list
which will make the source list conflict as HomeKit requires
unique source names.
"""
seen = set()
sources: list[str] = []
for source in state.attributes.get(self.source_list_key, []):
if source not in seen:
sources.append(source)
seen.add(source)
return sources
@abstractmethod
def set_on_off(self, value):
"""Move switch state to value if call came from HomeKit."""
@ -169,7 +184,7 @@ class RemoteInputSelectAccessory(HomeAccessory, ABC):
self.char_input_source.set_value(index)
return
possible_sources = new_state.attributes.get(self.source_list_key, [])
possible_sources = self._get_ordered_source_list_from_state(new_state)
if source in possible_sources:
index = possible_sources.index(source)
if index >= MAXIMUM_SOURCES:

View File

@ -512,3 +512,48 @@ async def test_media_player_television_max_sources(hass, hk_driver, events, capl
)
await hass.async_block_till_done()
assert acc.char_input_source.value == 0
async def test_media_player_television_duplicate_sources(
hass, hk_driver, events, caplog
):
"""Test if television accessory with duplicate sources."""
entity_id = "media_player.television"
sources = ["MUSIC", "HDMI", "SCREEN MIRRORING", "HDMI", "MUSIC"]
hass.states.async_set(
entity_id,
None,
{
ATTR_DEVICE_CLASS: MediaPlayerDeviceClass.TV,
ATTR_SUPPORTED_FEATURES: 3469,
ATTR_MEDIA_VOLUME_MUTED: False,
ATTR_INPUT_SOURCE: "HDMI",
ATTR_INPUT_SOURCE_LIST: sources,
},
)
await hass.async_block_till_done()
acc = TelevisionMediaPlayer(hass, hk_driver, "MediaPlayer", entity_id, 2, None)
await acc.run()
await hass.async_block_till_done()
assert acc.aid == 2
assert acc.category == 31 # Television
assert acc.char_active.value == 0
assert acc.char_remote_key.value == 0
assert acc.char_input_source.value == 1
assert acc.char_mute.value is False
hass.states.async_set(
entity_id,
None,
{
ATTR_DEVICE_CLASS: MediaPlayerDeviceClass.TV,
ATTR_SUPPORTED_FEATURES: 3469,
ATTR_MEDIA_VOLUME_MUTED: False,
ATTR_INPUT_SOURCE: "MUSIC",
ATTR_INPUT_SOURCE_LIST: sources,
},
)
await hass.async_block_till_done()
assert acc.char_input_source.value == 0