mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Fix webostv live TV source missing when configuring sources (#65243)
This commit is contained in:
parent
8e71e2e8ee
commit
6e4c281e15
@ -24,6 +24,7 @@ from homeassistant.helpers.typing import ConfigType
|
|||||||
|
|
||||||
from . import async_control_connect
|
from . import async_control_connect
|
||||||
from .const import CONF_SOURCES, DEFAULT_NAME, DOMAIN, WEBOSTV_EXCEPTIONS
|
from .const import CONF_SOURCES, DEFAULT_NAME, DOMAIN, WEBOSTV_EXCEPTIONS
|
||||||
|
from .helpers import async_get_sources
|
||||||
|
|
||||||
DATA_SCHEMA = vol.Schema(
|
DATA_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
@ -198,20 +199,3 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="init", data_schema=options_schema, errors=errors
|
step_id="init", data_schema=options_schema, errors=errors
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def async_get_sources(host: str, key: str) -> list[str]:
|
|
||||||
"""Construct sources list."""
|
|
||||||
try:
|
|
||||||
client = await async_control_connect(host, key)
|
|
||||||
except WEBOSTV_EXCEPTIONS:
|
|
||||||
return []
|
|
||||||
|
|
||||||
return list(
|
|
||||||
dict.fromkeys( # Preserve order when filtering duplicates
|
|
||||||
[
|
|
||||||
*(app["title"] for app in client.apps.values()),
|
|
||||||
*(app["label"] for app in client.inputs.values()),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
@ -6,8 +6,8 @@ from homeassistant.core import HomeAssistant, callback
|
|||||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||||
from homeassistant.helpers.device_registry import DeviceEntry
|
from homeassistant.helpers.device_registry import DeviceEntry
|
||||||
|
|
||||||
from . import WebOsClientWrapper
|
from . import WebOsClientWrapper, async_control_connect
|
||||||
from .const import DATA_CONFIG_ENTRY, DOMAIN
|
from .const import DATA_CONFIG_ENTRY, DOMAIN, LIVE_TV_APP_ID, WEBOSTV_EXCEPTIONS
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
@ -81,3 +81,29 @@ def async_get_client_wrapper_by_device_entry(
|
|||||||
)
|
)
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
async def async_get_sources(host: str, key: str) -> list[str]:
|
||||||
|
"""Construct sources list."""
|
||||||
|
try:
|
||||||
|
client = await async_control_connect(host, key)
|
||||||
|
except WEBOSTV_EXCEPTIONS:
|
||||||
|
return []
|
||||||
|
|
||||||
|
sources = []
|
||||||
|
found_live_tv = False
|
||||||
|
for app in client.apps.values():
|
||||||
|
sources.append(app["title"])
|
||||||
|
if app["id"] == LIVE_TV_APP_ID:
|
||||||
|
found_live_tv = True
|
||||||
|
|
||||||
|
for source in client.inputs.values():
|
||||||
|
sources.append(source["label"])
|
||||||
|
if source["appId"] == LIVE_TV_APP_ID:
|
||||||
|
found_live_tv = True
|
||||||
|
|
||||||
|
if not found_live_tv:
|
||||||
|
sources.append("Live TV")
|
||||||
|
|
||||||
|
# Preserve order when filtering duplicates
|
||||||
|
return list(dict.fromkeys(sources))
|
||||||
|
@ -11,27 +11,10 @@ from homeassistant.const import CONF_CLIENT_SECRET, CONF_HOST
|
|||||||
from homeassistant.helpers import entity_registry
|
from homeassistant.helpers import entity_registry
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
|
from .const import CLIENT_KEY, FAKE_UUID, HOST, MOCK_CLIENT_KEYS, TV_NAME
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
FAKE_UUID = "some-fake-uuid"
|
|
||||||
TV_NAME = "fake_webos"
|
|
||||||
ENTITY_ID = f"{MP_DOMAIN}.{TV_NAME}"
|
|
||||||
HOST = "1.2.3.4"
|
|
||||||
CLIENT_KEY = "some-secret"
|
|
||||||
MOCK_CLIENT_KEYS = {HOST: CLIENT_KEY}
|
|
||||||
MOCK_JSON = '{"1.2.3.4": "some-secret"}'
|
|
||||||
|
|
||||||
CHANNEL_1 = {
|
|
||||||
"channelNumber": "1",
|
|
||||||
"channelName": "Channel 1",
|
|
||||||
"channelId": "ch1id",
|
|
||||||
}
|
|
||||||
CHANNEL_2 = {
|
|
||||||
"channelNumber": "20",
|
|
||||||
"channelName": "Channel Name 2",
|
|
||||||
"channelId": "ch2id",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def setup_webostv(hass, unique_id=FAKE_UUID):
|
async def setup_webostv(hass, unique_id=FAKE_UUID):
|
||||||
"""Initialize webostv and media_player for tests."""
|
"""Initialize webostv and media_player for tests."""
|
||||||
|
@ -6,7 +6,7 @@ import pytest
|
|||||||
from homeassistant.components.webostv.const import LIVE_TV_APP_ID
|
from homeassistant.components.webostv.const import LIVE_TV_APP_ID
|
||||||
from homeassistant.helpers import entity_registry
|
from homeassistant.helpers import entity_registry
|
||||||
|
|
||||||
from . import CHANNEL_1, CHANNEL_2, CLIENT_KEY, FAKE_UUID
|
from .const import CHANNEL_1, CHANNEL_2, CLIENT_KEY, FAKE_UUID, MOCK_APPS, MOCK_INPUTS
|
||||||
|
|
||||||
from tests.common import async_mock_service
|
from tests.common import async_mock_service
|
||||||
|
|
||||||
@ -28,18 +28,8 @@ def client_fixture():
|
|||||||
client.software_info = {"major_ver": "major", "minor_ver": "minor"}
|
client.software_info = {"major_ver": "major", "minor_ver": "minor"}
|
||||||
client.system_info = {"modelName": "TVFAKE"}
|
client.system_info = {"modelName": "TVFAKE"}
|
||||||
client.client_key = CLIENT_KEY
|
client.client_key = CLIENT_KEY
|
||||||
client.apps = {
|
client.apps = MOCK_APPS
|
||||||
LIVE_TV_APP_ID: {
|
client.inputs = MOCK_INPUTS
|
||||||
"title": "Live TV",
|
|
||||||
"id": LIVE_TV_APP_ID,
|
|
||||||
"largeIcon": "large-icon",
|
|
||||||
"icon": "icon",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
client.inputs = {
|
|
||||||
"in1": {"label": "Input01", "id": "in1", "appId": "app0"},
|
|
||||||
"in2": {"label": "Input02", "id": "in2", "appId": "app1"},
|
|
||||||
}
|
|
||||||
client.current_app_id = LIVE_TV_APP_ID
|
client.current_app_id = LIVE_TV_APP_ID
|
||||||
|
|
||||||
client.channels = [CHANNEL_1, CHANNEL_2]
|
client.channels = [CHANNEL_1, CHANNEL_2]
|
||||||
|
36
tests/components/webostv/const.py
Normal file
36
tests/components/webostv/const.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
"""Constants for LG webOS Smart TV tests."""
|
||||||
|
from homeassistant.components.media_player import DOMAIN as MP_DOMAIN
|
||||||
|
from homeassistant.components.webostv.const import LIVE_TV_APP_ID
|
||||||
|
|
||||||
|
FAKE_UUID = "some-fake-uuid"
|
||||||
|
TV_NAME = "fake_webos"
|
||||||
|
ENTITY_ID = f"{MP_DOMAIN}.{TV_NAME}"
|
||||||
|
HOST = "1.2.3.4"
|
||||||
|
CLIENT_KEY = "some-secret"
|
||||||
|
MOCK_CLIENT_KEYS = {HOST: CLIENT_KEY}
|
||||||
|
MOCK_JSON = '{"1.2.3.4": "some-secret"}'
|
||||||
|
|
||||||
|
CHANNEL_1 = {
|
||||||
|
"channelNumber": "1",
|
||||||
|
"channelName": "Channel 1",
|
||||||
|
"channelId": "ch1id",
|
||||||
|
}
|
||||||
|
CHANNEL_2 = {
|
||||||
|
"channelNumber": "20",
|
||||||
|
"channelName": "Channel Name 2",
|
||||||
|
"channelId": "ch2id",
|
||||||
|
}
|
||||||
|
|
||||||
|
MOCK_APPS = {
|
||||||
|
LIVE_TV_APP_ID: {
|
||||||
|
"title": "Live TV",
|
||||||
|
"id": LIVE_TV_APP_ID,
|
||||||
|
"largeIcon": "large-icon",
|
||||||
|
"icon": "icon",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
MOCK_INPUTS = {
|
||||||
|
"in1": {"label": "Input01", "id": "in1", "appId": "app0"},
|
||||||
|
"in2": {"label": "Input02", "id": "in2", "appId": "app1"},
|
||||||
|
}
|
@ -7,7 +7,7 @@ import pytest
|
|||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.components import ssdp
|
from homeassistant.components import ssdp
|
||||||
from homeassistant.components.webostv.const import CONF_SOURCES, DOMAIN
|
from homeassistant.components.webostv.const import CONF_SOURCES, DOMAIN, LIVE_TV_APP_ID
|
||||||
from homeassistant.config_entries import SOURCE_SSDP
|
from homeassistant.config_entries import SOURCE_SSDP
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_CLIENT_SECRET,
|
CONF_CLIENT_SECRET,
|
||||||
@ -24,7 +24,8 @@ from homeassistant.data_entry_flow import (
|
|||||||
RESULT_TYPE_FORM,
|
RESULT_TYPE_FORM,
|
||||||
)
|
)
|
||||||
|
|
||||||
from . import CLIENT_KEY, FAKE_UUID, HOST, TV_NAME, setup_webostv
|
from . import setup_webostv
|
||||||
|
from .const import CLIENT_KEY, FAKE_UUID, HOST, MOCK_APPS, MOCK_INPUTS, TV_NAME
|
||||||
|
|
||||||
MOCK_YAML_CONFIG = {
|
MOCK_YAML_CONFIG = {
|
||||||
CONF_HOST: HOST,
|
CONF_HOST: HOST,
|
||||||
@ -149,8 +150,27 @@ async def test_form(hass, client):
|
|||||||
assert result["title"] == TV_NAME
|
assert result["title"] == TV_NAME
|
||||||
|
|
||||||
|
|
||||||
async def test_options_flow(hass, client):
|
@pytest.mark.parametrize(
|
||||||
"""Test options config flow."""
|
"apps, inputs",
|
||||||
|
[
|
||||||
|
# Live TV in apps (default)
|
||||||
|
(MOCK_APPS, MOCK_INPUTS),
|
||||||
|
# Live TV in inputs
|
||||||
|
(
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
**MOCK_INPUTS,
|
||||||
|
"livetv": {"label": "Live TV", "id": "livetv", "appId": LIVE_TV_APP_ID},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
# Live TV not found
|
||||||
|
({}, MOCK_INPUTS),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_options_flow_live_tv_in_apps(hass, client, apps, inputs):
|
||||||
|
"""Test options config flow Live TV found in apps."""
|
||||||
|
client.apps = apps
|
||||||
|
client.inputs = inputs
|
||||||
entry = await setup_webostv(hass)
|
entry = await setup_webostv(hass)
|
||||||
|
|
||||||
result = await hass.config_entries.options.async_init(entry.entry_id)
|
result = await hass.config_entries.options.async_init(entry.entry_id)
|
||||||
@ -161,20 +181,24 @@ async def test_options_flow(hass, client):
|
|||||||
|
|
||||||
result2 = await hass.config_entries.options.async_configure(
|
result2 = await hass.config_entries.options.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
user_input={CONF_SOURCES: ["Input01", "Input02"]},
|
user_input={CONF_SOURCES: ["Live TV", "Input01", "Input02"]},
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert result2["type"] == RESULT_TYPE_CREATE_ENTRY
|
assert result2["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||||
assert result2["data"][CONF_SOURCES] == ["Input01", "Input02"]
|
assert result2["data"][CONF_SOURCES] == ["Live TV", "Input01", "Input02"]
|
||||||
|
|
||||||
|
|
||||||
|
async def test_options_flow_cannot_retrieve(hass, client):
|
||||||
|
"""Test options config flow cannot retrieve sources."""
|
||||||
|
entry = await setup_webostv(hass)
|
||||||
|
|
||||||
client.connect = Mock(side_effect=ConnectionRefusedError())
|
client.connect = Mock(side_effect=ConnectionRefusedError())
|
||||||
result3 = await hass.config_entries.options.async_init(entry.entry_id)
|
result = await hass.config_entries.options.async_init(entry.entry_id)
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert result3["type"] == RESULT_TYPE_FORM
|
assert result["type"] == RESULT_TYPE_FORM
|
||||||
assert result3["errors"] == {"base": "cannot_retrieve"}
|
assert result["errors"] == {"base": "cannot_retrieve"}
|
||||||
|
|
||||||
|
|
||||||
async def test_form_cannot_connect(hass, client):
|
async def test_form_cannot_connect(hass, client):
|
||||||
|
@ -11,7 +11,8 @@ from homeassistant.exceptions import HomeAssistantError
|
|||||||
from homeassistant.helpers.device_registry import async_get as get_dev_reg
|
from homeassistant.helpers.device_registry import async_get as get_dev_reg
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from . import ENTITY_ID, FAKE_UUID, setup_webostv
|
from . import setup_webostv
|
||||||
|
from .const import ENTITY_ID, FAKE_UUID
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, async_get_device_automations
|
from tests.common import MockConfigEntry, async_get_device_automations
|
||||||
|
|
||||||
|
@ -8,11 +8,11 @@ from homeassistant.components.media_player import DOMAIN as MP_DOMAIN
|
|||||||
from homeassistant.components.webostv import DOMAIN
|
from homeassistant.components.webostv import DOMAIN
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
MOCK_JSON,
|
|
||||||
create_memory_sqlite_engine,
|
create_memory_sqlite_engine,
|
||||||
is_entity_unique_id_updated,
|
is_entity_unique_id_updated,
|
||||||
setup_legacy_component,
|
setup_legacy_component,
|
||||||
)
|
)
|
||||||
|
from .const import MOCK_JSON
|
||||||
|
|
||||||
|
|
||||||
async def test_missing_keys_file_abort(hass, client, caplog):
|
async def test_missing_keys_file_abort(hass, client, caplog):
|
||||||
|
@ -64,7 +64,8 @@ from homeassistant.helpers import device_registry
|
|||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
from homeassistant.util import dt
|
from homeassistant.util import dt
|
||||||
|
|
||||||
from . import CHANNEL_2, ENTITY_ID, TV_NAME, setup_webostv
|
from . import setup_webostv
|
||||||
|
from .const import CHANNEL_2, ENTITY_ID, TV_NAME
|
||||||
|
|
||||||
from tests.common import async_fire_time_changed
|
from tests.common import async_fire_time_changed
|
||||||
|
|
||||||
|
@ -9,7 +9,8 @@ from homeassistant.components.webostv import DOMAIN
|
|||||||
from homeassistant.const import CONF_ICON, CONF_SERVICE_DATA
|
from homeassistant.const import CONF_ICON, CONF_SERVICE_DATA
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from . import TV_NAME, setup_webostv
|
from . import setup_webostv
|
||||||
|
from .const import TV_NAME
|
||||||
|
|
||||||
ICON_PATH = "/some/path"
|
ICON_PATH = "/some/path"
|
||||||
MESSAGE = "one, two, testing, testing"
|
MESSAGE = "one, two, testing, testing"
|
||||||
|
@ -7,7 +7,8 @@ from homeassistant.const import SERVICE_RELOAD
|
|||||||
from homeassistant.helpers.device_registry import async_get as get_dev_reg
|
from homeassistant.helpers.device_registry import async_get as get_dev_reg
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from . import ENTITY_ID, FAKE_UUID, setup_webostv
|
from . import setup_webostv
|
||||||
|
from .const import ENTITY_ID, FAKE_UUID
|
||||||
|
|
||||||
from tests.common import MockEntity, MockEntityPlatform
|
from tests.common import MockEntity, MockEntityPlatform
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user