Add device info to Panasonic Viera (#41028)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
72289b8171
commit
d1041efedf
6 changed files with 250 additions and 10 deletions
|
@ -14,7 +14,9 @@ import homeassistant.helpers.config_validation as cv
|
|||
from homeassistant.helpers.script import Script
|
||||
|
||||
from .const import (
|
||||
ATTR_DEVICE_INFO,
|
||||
ATTR_REMOTE,
|
||||
ATTR_UDN,
|
||||
CONF_APP_ID,
|
||||
CONF_ENCRYPTION_KEY,
|
||||
CONF_ON_ACTION,
|
||||
|
@ -86,6 +88,22 @@ async def async_setup_entry(hass, config_entry):
|
|||
|
||||
panasonic_viera_data[config_entry.entry_id] = {ATTR_REMOTE: remote}
|
||||
|
||||
# Add device_info to older config entries
|
||||
if ATTR_DEVICE_INFO not in config or config[ATTR_DEVICE_INFO] is None:
|
||||
device_info = await remote.async_get_device_info()
|
||||
unique_id = config_entry.unique_id
|
||||
if device_info is None:
|
||||
_LOGGER.error(
|
||||
"Couldn't gather device info. Please restart Home Assistant with your TV turned on and connected to your network."
|
||||
)
|
||||
else:
|
||||
unique_id = device_info[ATTR_UDN]
|
||||
hass.config_entries.async_update_entry(
|
||||
config_entry,
|
||||
unique_id=unique_id,
|
||||
data={**config, ATTR_DEVICE_INFO: device_info},
|
||||
)
|
||||
|
||||
for component in PLATFORMS:
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_forward_entry_setup(config_entry, component)
|
||||
|
@ -223,6 +241,12 @@ class Remote:
|
|||
_LOGGER.debug("Play media: %s (%s)", media_id, media_type)
|
||||
await self._handle_errors(self._control.open_webpage, media_id)
|
||||
|
||||
async def async_get_device_info(self):
|
||||
"""Return device info."""
|
||||
if self._control is None:
|
||||
return None
|
||||
return await self._handle_errors(self._control.get_device_info)
|
||||
|
||||
async def _handle_errors(self, func, *args):
|
||||
"""Handle errors from func, set available and reconnect if needed."""
|
||||
try:
|
||||
|
|
|
@ -10,6 +10,9 @@ from homeassistant import config_entries
|
|||
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PIN, CONF_PORT
|
||||
|
||||
from .const import ( # pylint: disable=unused-import
|
||||
ATTR_DEVICE_INFO,
|
||||
ATTR_FRIENDLY_NAME,
|
||||
ATTR_UDN,
|
||||
CONF_APP_ID,
|
||||
CONF_ENCRYPTION_KEY,
|
||||
CONF_ON_ACTION,
|
||||
|
@ -35,6 +38,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
CONF_NAME: None,
|
||||
CONF_PORT: None,
|
||||
CONF_ON_ACTION: None,
|
||||
ATTR_DEVICE_INFO: None,
|
||||
}
|
||||
|
||||
self._remote = None
|
||||
|
@ -49,6 +53,10 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
self._remote = await self.hass.async_add_executor_job(
|
||||
partial(RemoteControl, self._data[CONF_HOST], self._data[CONF_PORT])
|
||||
)
|
||||
|
||||
self._data[ATTR_DEVICE_INFO] = await self.hass.async_add_executor_job(
|
||||
self._remote.get_device_info
|
||||
)
|
||||
except (TimeoutError, URLError, SOAPError, OSError) as err:
|
||||
_LOGGER.error("Could not establish remote connection: %s", err)
|
||||
errors["base"] = "cannot_connect"
|
||||
|
@ -57,6 +65,14 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
return self.async_abort(reason="unknown")
|
||||
|
||||
if "base" not in errors:
|
||||
await self.async_set_unique_id(self._data[ATTR_DEVICE_INFO][ATTR_UDN])
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
if self._data[CONF_NAME] == DEFAULT_NAME:
|
||||
self._data[CONF_NAME] = self._data[ATTR_DEVICE_INFO][
|
||||
ATTR_FRIENDLY_NAME
|
||||
].replace("_", " ")
|
||||
|
||||
if self._remote.type == TV_TYPE_ENCRYPTED:
|
||||
return await self.async_step_pairing()
|
||||
|
||||
|
|
|
@ -12,4 +12,10 @@ DEFAULT_PORT = 55000
|
|||
|
||||
ATTR_REMOTE = "remote"
|
||||
|
||||
ATTR_DEVICE_INFO = "device_info"
|
||||
ATTR_FRIENDLY_NAME = "friendlyName"
|
||||
ATTR_MANUFACTURER = "manufacturer"
|
||||
ATTR_MODEL_NUMBER = "modelNumber"
|
||||
ATTR_UDN = "UDN"
|
||||
|
||||
ERROR_INVALID_PIN_CODE = "invalid_pin_code"
|
||||
|
|
|
@ -20,7 +20,14 @@ from homeassistant.components.media_player.const import (
|
|||
)
|
||||
from homeassistant.const import CONF_NAME
|
||||
|
||||
from .const import ATTR_REMOTE, DOMAIN
|
||||
from .const import (
|
||||
ATTR_DEVICE_INFO,
|
||||
ATTR_MANUFACTURER,
|
||||
ATTR_MODEL_NUMBER,
|
||||
ATTR_REMOTE,
|
||||
ATTR_UDN,
|
||||
DOMAIN,
|
||||
)
|
||||
|
||||
SUPPORT_VIERATV = (
|
||||
SUPPORT_PAUSE
|
||||
|
@ -46,24 +53,39 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||
|
||||
remote = hass.data[DOMAIN][config_entry.entry_id][ATTR_REMOTE]
|
||||
name = config[CONF_NAME]
|
||||
device_info = config[ATTR_DEVICE_INFO]
|
||||
|
||||
tv_device = PanasonicVieraTVEntity(remote, name)
|
||||
tv_device = PanasonicVieraTVEntity(remote, name, device_info)
|
||||
async_add_entities([tv_device])
|
||||
|
||||
|
||||
class PanasonicVieraTVEntity(MediaPlayerEntity):
|
||||
"""Representation of a Panasonic Viera TV."""
|
||||
|
||||
def __init__(self, remote, name, uuid=None):
|
||||
def __init__(self, remote, name, device_info):
|
||||
"""Initialize the entity."""
|
||||
self._remote = remote
|
||||
self._name = name
|
||||
self._uuid = uuid
|
||||
self._device_info = device_info
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
def unique_id(self) -> str:
|
||||
"""Return the unique ID of the device."""
|
||||
return self._uuid
|
||||
if self._device_info is not None:
|
||||
return self._device_info[ATTR_UDN]
|
||||
return None
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
"""Return device specific attributes."""
|
||||
if self._device_info is None:
|
||||
return None
|
||||
return {
|
||||
"name": self._name,
|
||||
"identifiers": {(DOMAIN, self._device_info[ATTR_UDN])},
|
||||
"manufacturer": self._device_info[ATTR_MANUFACTURER],
|
||||
"model": self._device_info[ATTR_MODEL_NUMBER],
|
||||
}
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
|
|
@ -4,6 +4,11 @@ import pytest
|
|||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.panasonic_viera.const import (
|
||||
ATTR_DEVICE_INFO,
|
||||
ATTR_FRIENDLY_NAME,
|
||||
ATTR_MANUFACTURER,
|
||||
ATTR_MODEL_NUMBER,
|
||||
ATTR_UDN,
|
||||
CONF_APP_ID,
|
||||
CONF_ENCRYPTION_KEY,
|
||||
CONF_ON_ACTION,
|
||||
|
@ -36,6 +41,10 @@ def get_mock_remote(
|
|||
encrypted=False,
|
||||
app_id=None,
|
||||
encryption_key=None,
|
||||
name=DEFAULT_NAME,
|
||||
manufacturer="mock-manufacturer",
|
||||
model_number="mock-model-number",
|
||||
unique_id="mock-unique-id",
|
||||
):
|
||||
"""Return a mock remote."""
|
||||
mock_remote = Mock()
|
||||
|
@ -58,6 +67,16 @@ def get_mock_remote(
|
|||
|
||||
mock_remote.authorize_pin_code = authorize_pin_code
|
||||
|
||||
def get_device_info():
|
||||
return {
|
||||
ATTR_FRIENDLY_NAME: name,
|
||||
ATTR_MANUFACTURER: manufacturer,
|
||||
ATTR_MODEL_NUMBER: model_number,
|
||||
ATTR_UDN: unique_id,
|
||||
}
|
||||
|
||||
mock_remote.get_device_info = get_device_info
|
||||
|
||||
return mock_remote
|
||||
|
||||
|
||||
|
@ -89,6 +108,12 @@ async def test_flow_non_encrypted(hass):
|
|||
CONF_NAME: DEFAULT_NAME,
|
||||
CONF_PORT: DEFAULT_PORT,
|
||||
CONF_ON_ACTION: None,
|
||||
ATTR_DEVICE_INFO: {
|
||||
ATTR_FRIENDLY_NAME: DEFAULT_NAME,
|
||||
ATTR_MANUFACTURER: "mock-manufacturer",
|
||||
ATTR_MODEL_NUMBER: "mock-model-number",
|
||||
ATTR_UDN: "mock-unique-id",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
@ -181,6 +206,12 @@ async def test_flow_encrypted_valid_pin_code(hass):
|
|||
CONF_ON_ACTION: None,
|
||||
CONF_APP_ID: "test-app-id",
|
||||
CONF_ENCRYPTION_KEY: "test-encryption-key",
|
||||
ATTR_DEVICE_INFO: {
|
||||
ATTR_FRIENDLY_NAME: DEFAULT_NAME,
|
||||
ATTR_MANUFACTURER: "mock-manufacturer",
|
||||
ATTR_MODEL_NUMBER: "mock-model-number",
|
||||
ATTR_UDN: "mock-unique-id",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
@ -359,6 +390,12 @@ async def test_imported_flow_non_encrypted(hass):
|
|||
CONF_NAME: DEFAULT_NAME,
|
||||
CONF_PORT: DEFAULT_PORT,
|
||||
CONF_ON_ACTION: "test-on-action",
|
||||
ATTR_DEVICE_INFO: {
|
||||
ATTR_FRIENDLY_NAME: DEFAULT_NAME,
|
||||
ATTR_MANUFACTURER: "mock-manufacturer",
|
||||
ATTR_MODEL_NUMBER: "mock-model-number",
|
||||
ATTR_UDN: "mock-unique-id",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
@ -403,6 +440,12 @@ async def test_imported_flow_encrypted_valid_pin_code(hass):
|
|||
CONF_ON_ACTION: "test-on-action",
|
||||
CONF_APP_ID: "test-app-id",
|
||||
CONF_ENCRYPTION_KEY: "test-encryption-key",
|
||||
ATTR_DEVICE_INFO: {
|
||||
ATTR_FRIENDLY_NAME: DEFAULT_NAME,
|
||||
ATTR_MANUFACTURER: "mock-manufacturer",
|
||||
ATTR_MODEL_NUMBER: "mock-model-number",
|
||||
ATTR_UDN: "mock-unique-id",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
"""Test the Panasonic Viera setup process."""
|
||||
from homeassistant.components.panasonic_viera.const import (
|
||||
ATTR_DEVICE_INFO,
|
||||
ATTR_FRIENDLY_NAME,
|
||||
ATTR_MANUFACTURER,
|
||||
ATTR_MODEL_NUMBER,
|
||||
ATTR_UDN,
|
||||
CONF_APP_ID,
|
||||
CONF_ENCRYPTION_KEY,
|
||||
CONF_ON_ACTION,
|
||||
|
@ -26,8 +31,15 @@ MOCK_ENCRYPTION_DATA = {
|
|||
CONF_ENCRYPTION_KEY: "mock-encryption-key",
|
||||
}
|
||||
|
||||
MOCK_DEVICE_INFO = {
|
||||
ATTR_FRIENDLY_NAME: DEFAULT_NAME,
|
||||
ATTR_MANUFACTURER: "mock-manufacturer",
|
||||
ATTR_MODEL_NUMBER: "mock-model-number",
|
||||
ATTR_UDN: "mock-unique-id",
|
||||
}
|
||||
|
||||
def get_mock_remote():
|
||||
|
||||
def get_mock_remote(device_info=MOCK_DEVICE_INFO):
|
||||
"""Return a mock remote."""
|
||||
mock_remote = Mock()
|
||||
|
||||
|
@ -36,6 +48,11 @@ def get_mock_remote():
|
|||
|
||||
mock_remote.async_create_remote_control = async_create_remote_control
|
||||
|
||||
async def async_get_device_info():
|
||||
return device_info
|
||||
|
||||
mock_remote.async_get_device_info = async_get_device_info
|
||||
|
||||
return mock_remote
|
||||
|
||||
|
||||
|
@ -43,8 +60,8 @@ async def test_setup_entry_encrypted(hass):
|
|||
"""Test setup with encrypted config entry."""
|
||||
mock_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id=MOCK_CONFIG_DATA[CONF_HOST],
|
||||
data={**MOCK_CONFIG_DATA, **MOCK_ENCRYPTION_DATA},
|
||||
unique_id=MOCK_DEVICE_INFO[ATTR_UDN],
|
||||
data={**MOCK_CONFIG_DATA, **MOCK_ENCRYPTION_DATA, **MOCK_DEVICE_INFO},
|
||||
)
|
||||
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
@ -64,8 +81,89 @@ async def test_setup_entry_encrypted(hass):
|
|||
assert state.name == DEFAULT_NAME
|
||||
|
||||
|
||||
async def test_setup_entry_encrypted_missing_device_info(hass):
|
||||
"""Test setup with encrypted config entry and missing device info."""
|
||||
mock_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id=MOCK_CONFIG_DATA[CONF_HOST],
|
||||
data={**MOCK_CONFIG_DATA, **MOCK_ENCRYPTION_DATA},
|
||||
)
|
||||
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
mock_remote = get_mock_remote()
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.panasonic_viera.Remote",
|
||||
return_value=mock_remote,
|
||||
):
|
||||
await hass.config_entries.async_setup(mock_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_entry.data[ATTR_DEVICE_INFO] == MOCK_DEVICE_INFO
|
||||
assert mock_entry.unique_id == MOCK_DEVICE_INFO[ATTR_UDN]
|
||||
|
||||
state = hass.states.get("media_player.panasonic_viera_tv")
|
||||
|
||||
assert state
|
||||
assert state.name == DEFAULT_NAME
|
||||
|
||||
|
||||
async def test_setup_entry_encrypted_missing_device_info_none(hass):
|
||||
"""Test setup with encrypted config entry and device info set to None."""
|
||||
mock_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id=MOCK_CONFIG_DATA[CONF_HOST],
|
||||
data={**MOCK_CONFIG_DATA, **MOCK_ENCRYPTION_DATA},
|
||||
)
|
||||
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
mock_remote = get_mock_remote(device_info=None)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.panasonic_viera.Remote",
|
||||
return_value=mock_remote,
|
||||
):
|
||||
await hass.config_entries.async_setup(mock_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_entry.data[ATTR_DEVICE_INFO] is None
|
||||
assert mock_entry.unique_id == MOCK_CONFIG_DATA[CONF_HOST]
|
||||
|
||||
state = hass.states.get("media_player.panasonic_viera_tv")
|
||||
|
||||
assert state
|
||||
assert state.name == DEFAULT_NAME
|
||||
|
||||
|
||||
async def test_setup_entry_unencrypted(hass):
|
||||
"""Test setup with unencrypted config entry."""
|
||||
mock_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id=MOCK_DEVICE_INFO[ATTR_UDN],
|
||||
data={**MOCK_CONFIG_DATA, **MOCK_DEVICE_INFO},
|
||||
)
|
||||
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
mock_remote = get_mock_remote()
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.panasonic_viera.Remote",
|
||||
return_value=mock_remote,
|
||||
):
|
||||
await hass.config_entries.async_setup(mock_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("media_player.panasonic_viera_tv")
|
||||
|
||||
assert state
|
||||
assert state.name == DEFAULT_NAME
|
||||
|
||||
|
||||
async def test_setup_entry_unencrypted_missing_device_info(hass):
|
||||
"""Test setup with unencrypted config entry and missing device info."""
|
||||
mock_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id=MOCK_CONFIG_DATA[CONF_HOST],
|
||||
|
@ -83,6 +181,37 @@ async def test_setup_entry_unencrypted(hass):
|
|||
await hass.config_entries.async_setup(mock_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_entry.data[ATTR_DEVICE_INFO] == MOCK_DEVICE_INFO
|
||||
assert mock_entry.unique_id == MOCK_DEVICE_INFO[ATTR_UDN]
|
||||
|
||||
state = hass.states.get("media_player.panasonic_viera_tv")
|
||||
|
||||
assert state
|
||||
assert state.name == DEFAULT_NAME
|
||||
|
||||
|
||||
async def test_setup_entry_unencrypted_missing_device_info_none(hass):
|
||||
"""Test setup with unencrypted config entry and device info set to None."""
|
||||
mock_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id=MOCK_CONFIG_DATA[CONF_HOST],
|
||||
data=MOCK_CONFIG_DATA,
|
||||
)
|
||||
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
mock_remote = get_mock_remote(device_info=None)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.panasonic_viera.Remote",
|
||||
return_value=mock_remote,
|
||||
):
|
||||
await hass.config_entries.async_setup(mock_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_entry.data[ATTR_DEVICE_INFO] is None
|
||||
assert mock_entry.unique_id == MOCK_CONFIG_DATA[CONF_HOST]
|
||||
|
||||
state = hass.states.get("media_player.panasonic_viera_tv")
|
||||
|
||||
assert state
|
||||
|
@ -106,7 +235,7 @@ async def test_setup_config_flow_initiated(hass):
|
|||
async def test_setup_unload_entry(hass):
|
||||
"""Test if config entry is unloaded."""
|
||||
mock_entry = MockConfigEntry(
|
||||
domain=DOMAIN, unique_id=MOCK_CONFIG_DATA[CONF_HOST], data=MOCK_CONFIG_DATA
|
||||
domain=DOMAIN, unique_id=MOCK_DEVICE_INFO[ATTR_UDN], data=MOCK_CONFIG_DATA
|
||||
)
|
||||
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue