Skip to content

Commit

Permalink
Fix HomeKit media players when entity has duplicate sources (#83890)
Browse files Browse the repository at this point in the history
fixes #83852 fixes #83698
  • Loading branch information
bdraco authored Dec 13, 2022
1 parent b9753a9 commit 692a732
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 3 deletions.
21 changes: 18 additions & 3 deletions homeassistant/components/homekit/type_remotes.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
SERVICE_TURN_ON,
STATE_ON,
)
from homeassistant.core import callback
from homeassistant.core import State, callback

from .accessories import TYPES, HomeAccessory
from .const import (
Expand Down Expand Up @@ -96,7 +96,7 @@ def __init__(
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)",
Expand Down Expand Up @@ -143,6 +143,21 @@ def __init__(
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."""
Expand All @@ -169,7 +184,7 @@ def _async_update_input_state(self, hk_state, new_state):
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:
Expand Down
45 changes: 45 additions & 0 deletions tests/components/homekit/test_type_media_players.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 692a732

Please sign in to comment.