mirror of
https://github.com/home-assistant/core.git
synced 2026-08-24 10:13:52 -05:00
Report an available source for LG soundbars (#178799)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
co-authored by
Joost Lekkerkerker
parent
ae288ad94d
commit
1bb7e60aeb
@@ -17,6 +17,22 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
EQUIVALENT_FUNCTIONS = (
|
||||
("Optical/HDMI ARC", "E-ARC", "ARC", "LG Optical", "Optical", "Optical2"),
|
||||
("HDMI", "HDMI2", "HDMI3"),
|
||||
("USB", "USB2"),
|
||||
("Bluetooth", "Portable"),
|
||||
("Wi-Fi", "Chromecast", "Spotify"),
|
||||
)
|
||||
|
||||
|
||||
def _offered_equivalent(function: str, offered: list[int]) -> str | None:
|
||||
"""Return an offered function from the same group as the given one."""
|
||||
group = next((names for names in EQUIVALENT_FUNCTIONS if function in names), ())
|
||||
return next(
|
||||
(name for name in group if temescal.functions.index(name) in offered), None
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
@@ -240,7 +256,10 @@ class LGDevice(MediaPlayerEntity):
|
||||
"""Return the current input source."""
|
||||
if self._function == -1 or self._function >= len(temescal.functions):
|
||||
return None
|
||||
return temescal.functions[self._function]
|
||||
function = temescal.functions[self._function]
|
||||
if self._function in self._functions:
|
||||
return function
|
||||
return _offered_equivalent(function, self._functions) or function
|
||||
|
||||
@property
|
||||
@override
|
||||
|
||||
@@ -1 +1,24 @@
|
||||
"""Tests for the lg_soundbar component."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from typing import Any
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def setup_integration(hass: HomeAssistant, config_entry: MockConfigEntry) -> None:
|
||||
"""Set up the component."""
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
def find_update_callback(
|
||||
mock: MagicMock,
|
||||
) -> Callable[[dict[str, Any]], None]:
|
||||
"""Return the callback registered with the temescal device."""
|
||||
return mock.call_args.kwargs["callback"]
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
"""Common fixtures for the lg_soundbar tests."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.lg_soundbar.const import DEFAULT_PORT, DOMAIN
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_config_entry() -> MockConfigEntry:
|
||||
"""Return the default mocked config entry."""
|
||||
return MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="LG Soundbar",
|
||||
data={CONF_HOST: "127.0.0.1", CONF_PORT: DEFAULT_PORT},
|
||||
unique_id="uuid",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_temescal() -> Generator[MagicMock]:
|
||||
"""Mock the temescal library.
|
||||
|
||||
Only the device constructor is mocked so that the real ``functions`` and
|
||||
``equalisers`` lookup tables remain available to the media player.
|
||||
"""
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.lg_soundbar.media_player.temescal.temescal",
|
||||
autospec=True,
|
||||
) as mock_temescal,
|
||||
patch(
|
||||
"homeassistant.components.lg_soundbar.test_connect",
|
||||
return_value={"name": "LG Soundbar", "uuid": "uuid"},
|
||||
),
|
||||
):
|
||||
yield mock_temescal
|
||||
@@ -0,0 +1,124 @@
|
||||
"""Test the lg_soundbar media player."""
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import temescal
|
||||
|
||||
from homeassistant.components.media_player import ATTR_INPUT_SOURCE_LIST
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import find_update_callback, setup_integration
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
ENTITY_ID = "media_player.127_0_0_1"
|
||||
|
||||
AVAILABLE_FUNCTIONS = [
|
||||
temescal.functions.index("Wi-Fi"),
|
||||
temescal.functions.index("Bluetooth"),
|
||||
temescal.functions.index("Optical/HDMI ARC"),
|
||||
temescal.functions.index("HDMI"),
|
||||
temescal.functions.index("USB2"),
|
||||
]
|
||||
|
||||
|
||||
def send_func_view_info(callback: MagicMock, current_function: str) -> None:
|
||||
"""Report the given function as the current one via the callback."""
|
||||
callback(
|
||||
{
|
||||
"msg": "FUNC_VIEW_INFO",
|
||||
"data": {
|
||||
"i_curr_func": temescal.functions.index(current_function),
|
||||
"ai_func_list": AVAILABLE_FUNCTIONS,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def test_source_reported_as_is_when_available(
|
||||
hass: HomeAssistant,
|
||||
mock_temescal: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test that a function offered by the device is reported unchanged."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
send_func_view_info(find_update_callback(mock_temescal), "Bluetooth")
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(ENTITY_ID).attributes["source"] == "Bluetooth"
|
||||
|
||||
|
||||
async def test_source_falls_back_to_available_equivalent(
|
||||
hass: HomeAssistant,
|
||||
mock_temescal: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test that a function missing from the list falls back to an equivalent one.
|
||||
|
||||
Soundbars report states such as E-ARC or HDMI3 that they never offer in
|
||||
ai_func_list, which leaves the current source outside of source_list.
|
||||
"""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
callback = find_update_callback(mock_temescal)
|
||||
|
||||
send_func_view_info(callback, "E-ARC")
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(ENTITY_ID).attributes["source"] == "Optical/HDMI ARC"
|
||||
|
||||
send_func_view_info(callback, "ARC")
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(ENTITY_ID).attributes["source"] == "Optical/HDMI ARC"
|
||||
|
||||
send_func_view_info(callback, "HDMI3")
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(ENTITY_ID).attributes["source"] == "HDMI"
|
||||
|
||||
send_func_view_info(callback, "USB")
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(ENTITY_ID).attributes["source"] == "USB2"
|
||||
|
||||
|
||||
async def test_source_kept_when_no_equivalent_is_available(
|
||||
hass: HomeAssistant,
|
||||
mock_temescal: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test that a function without an available equivalent is reported as is."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
send_func_view_info(find_update_callback(mock_temescal), "Aux")
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(ENTITY_ID).attributes["source"] == "Aux"
|
||||
|
||||
|
||||
async def test_source_list_only_contains_offered_functions(
|
||||
hass: HomeAssistant,
|
||||
mock_temescal: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test that the source list is not extended by the fallback."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
send_func_view_info(find_update_callback(mock_temescal), "E-ARC")
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(ENTITY_ID).attributes[ATTR_INPUT_SOURCE_LIST] == [
|
||||
"Bluetooth",
|
||||
"HDMI",
|
||||
"Optical/HDMI ARC",
|
||||
"USB2",
|
||||
"Wi-Fi",
|
||||
]
|
||||
|
||||
|
||||
async def test_source_unknown_before_any_response(
|
||||
hass: HomeAssistant,
|
||||
mock_temescal: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test that no source is reported before the device answers."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
assert "source" not in hass.states.get(ENTITY_ID).attributes
|
||||
Reference in New Issue
Block a user