Use HA uuid as client_id in BraviaTV (#79618)

* Use uuid as clientid/nickname

* Fixes after rebase

* Move gen_instance_ids() to utils

* Store client_id and nickname in config_entry

* Update tests

* Clean names

* Rename consts
This commit is contained in:
Maciej Bieniek 2022-10-05 08:24:52 +00:00 committed by GitHub
parent 33bdc67a61
commit 9dd9147343
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 59 additions and 20 deletions

View File

@ -7,11 +7,11 @@ from aiohttp import CookieJar
from pybravia import BraviaTV from pybravia import BraviaTV
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_PIN, Platform from homeassistant.const import CONF_HOST, CONF_MAC, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_create_clientsession from homeassistant.helpers.aiohttp_client import async_create_clientsession
from .const import CONF_IGNORED_SOURCES, CONF_USE_PSK, DOMAIN from .const import CONF_IGNORED_SOURCES, DOMAIN
from .coordinator import BraviaTVCoordinator from .coordinator import BraviaTVCoordinator
PLATFORMS: Final[list[Platform]] = [ PLATFORMS: Final[list[Platform]] = [
@ -25,8 +25,6 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
"""Set up a config entry.""" """Set up a config entry."""
host = config_entry.data[CONF_HOST] host = config_entry.data[CONF_HOST]
mac = config_entry.data[CONF_MAC] mac = config_entry.data[CONF_MAC]
pin = config_entry.data[CONF_PIN]
use_psk = config_entry.data.get(CONF_USE_PSK, False)
ignored_sources = config_entry.options.get(CONF_IGNORED_SOURCES, []) ignored_sources = config_entry.options.get(CONF_IGNORED_SOURCES, [])
session = async_create_clientsession( session = async_create_clientsession(
@ -36,8 +34,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
coordinator = BraviaTVCoordinator( coordinator = BraviaTVCoordinator(
hass=hass, hass=hass,
client=client, client=client,
pin=pin, config=config_entry.data,
use_psk=use_psk,
ignored_sources=ignored_sources, ignored_sources=ignored_sources,
) )
config_entry.async_on_unload(config_entry.add_update_listener(update_listener)) config_entry.async_on_unload(config_entry.add_update_listener(update_listener))

View File

@ -15,6 +15,7 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_NAME, CONF_PIN from homeassistant.const import CONF_HOST, CONF_MAC, CONF_NAME, CONF_PIN
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.data_entry_flow import FlowResult from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers import instance_id
from homeassistant.helpers.aiohttp_client import async_create_clientsession from homeassistant.helpers.aiohttp_client import async_create_clientsession
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.util.network import is_host_valid from homeassistant.util.network import is_host_valid
@ -24,11 +25,12 @@ from .const import (
ATTR_CID, ATTR_CID,
ATTR_MAC, ATTR_MAC,
ATTR_MODEL, ATTR_MODEL,
CLIENTID_PREFIX, CONF_CLIENT_ID,
CONF_IGNORED_SOURCES, CONF_IGNORED_SOURCES,
CONF_NICKNAME,
CONF_USE_PSK, CONF_USE_PSK,
DOMAIN, DOMAIN,
NICKNAME, NICKNAME_PREFIX,
) )
@ -42,6 +44,8 @@ class BraviaTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
self.client: BraviaTV | None = None self.client: BraviaTV | None = None
self.device_config: dict[str, Any] = {} self.device_config: dict[str, Any] = {}
self.entry: ConfigEntry | None = None self.entry: ConfigEntry | None = None
self.client_id: str = ""
self.nickname: str = ""
@staticmethod @staticmethod
@callback @callback
@ -68,8 +72,10 @@ class BraviaTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
if use_psk: if use_psk:
await self.client.connect(psk=pin) await self.client.connect(psk=pin)
else: else:
self.device_config[CONF_CLIENT_ID] = self.client_id
self.device_config[CONF_NICKNAME] = self.nickname
await self.client.connect( await self.client.connect(
pin=pin, clientid=CLIENTID_PREFIX, nickname=NICKNAME pin=pin, clientid=self.client_id, nickname=self.nickname
) )
await self.client.set_wol_mode(True) await self.client.set_wol_mode(True)
@ -110,6 +116,7 @@ class BraviaTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
) -> FlowResult: ) -> FlowResult:
"""Authorize Bravia TV device.""" """Authorize Bravia TV device."""
errors: dict[str, str] = {} errors: dict[str, str] = {}
self.client_id, self.nickname = await self.gen_instance_ids()
if user_input is not None: if user_input is not None:
self.device_config[CONF_PIN] = user_input[CONF_PIN] self.device_config[CONF_PIN] = user_input[CONF_PIN]
@ -126,7 +133,7 @@ class BraviaTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
assert self.client assert self.client
try: try:
await self.client.pair(CLIENTID_PREFIX, NICKNAME) await self.client.pair(self.client_id, self.nickname)
except BraviaTVError: except BraviaTVError:
return self.async_abort(reason="no_ip_control") return self.async_abort(reason="no_ip_control")
@ -190,6 +197,7 @@ class BraviaTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
) -> FlowResult: ) -> FlowResult:
"""Dialog that informs the user that reauth is required.""" """Dialog that informs the user that reauth is required."""
self.create_client() self.create_client()
client_id, nickname = await self.gen_instance_ids()
assert self.client is not None assert self.client is not None
assert self.entry is not None assert self.entry is not None
@ -201,8 +209,10 @@ class BraviaTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
if use_psk: if use_psk:
await self.client.connect(psk=pin) await self.client.connect(psk=pin)
else: else:
self.device_config[CONF_CLIENT_ID] = client_id
self.device_config[CONF_NICKNAME] = nickname
await self.client.connect( await self.client.connect(
pin=pin, clientid=CLIENTID_PREFIX, nickname=NICKNAME pin=pin, clientid=client_id, nickname=nickname
) )
await self.client.set_wol_mode(True) await self.client.set_wol_mode(True)
except BraviaTVError: except BraviaTVError:
@ -215,7 +225,7 @@ class BraviaTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
return self.async_abort(reason="reauth_successful") return self.async_abort(reason="reauth_successful")
try: try:
await self.client.pair(CLIENTID_PREFIX, NICKNAME) await self.client.pair(client_id, nickname)
except BraviaTVError: except BraviaTVError:
return self.async_abort(reason="reauth_unsuccessful") return self.async_abort(reason="reauth_unsuccessful")
@ -229,6 +239,11 @@ class BraviaTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
), ),
) )
async def gen_instance_ids(self) -> tuple[str, str]:
"""Generate client_id and nickname."""
uuid = await instance_id.async_get(self.hass)
return uuid, f"{NICKNAME_PREFIX} {uuid[:6]}"
class BraviaTVOptionsFlowHandler(config_entries.OptionsFlow): class BraviaTVOptionsFlowHandler(config_entries.OptionsFlow):
"""Config flow options for Bravia TV.""" """Config flow options for Bravia TV."""

View File

@ -8,9 +8,11 @@ ATTR_MAC: Final = "macAddr"
ATTR_MANUFACTURER: Final = "Sony" ATTR_MANUFACTURER: Final = "Sony"
ATTR_MODEL: Final = "model" ATTR_MODEL: Final = "model"
CONF_CLIENT_ID: Final = "client_id"
CONF_IGNORED_SOURCES: Final = "ignored_sources" CONF_IGNORED_SOURCES: Final = "ignored_sources"
CONF_NICKNAME: Final = "nickname"
CONF_USE_PSK: Final = "use_psk" CONF_USE_PSK: Final = "use_psk"
CLIENTID_PREFIX: Final = "HomeAssistant"
DOMAIN: Final = "braviatv" DOMAIN: Final = "braviatv"
NICKNAME: Final = "Home Assistant" LEGACY_CLIENT_ID: Final = "HomeAssistant"
NICKNAME_PREFIX: Final = "Home Assistant"

View File

@ -5,6 +5,7 @@ from collections.abc import Awaitable, Callable, Coroutine, Iterable
from datetime import timedelta from datetime import timedelta
from functools import wraps from functools import wraps
import logging import logging
from types import MappingProxyType
from typing import Any, Final, TypeVar from typing import Any, Final, TypeVar
from pybravia import ( from pybravia import (
@ -19,12 +20,20 @@ from pybravia import (
from typing_extensions import Concatenate, ParamSpec from typing_extensions import Concatenate, ParamSpec
from homeassistant.components.media_player import MediaType from homeassistant.components.media_player import MediaType
from homeassistant.const import CONF_PIN
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.debounce import Debouncer from homeassistant.helpers.debounce import Debouncer
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import CLIENTID_PREFIX, DOMAIN, NICKNAME from .const import (
CONF_CLIENT_ID,
CONF_NICKNAME,
CONF_USE_PSK,
DOMAIN,
LEGACY_CLIENT_ID,
NICKNAME_PREFIX,
)
_BraviaTVCoordinatorT = TypeVar("_BraviaTVCoordinatorT", bound="BraviaTVCoordinator") _BraviaTVCoordinatorT = TypeVar("_BraviaTVCoordinatorT", bound="BraviaTVCoordinator")
_P = ParamSpec("_P") _P = ParamSpec("_P")
@ -61,15 +70,16 @@ class BraviaTVCoordinator(DataUpdateCoordinator[None]):
self, self,
hass: HomeAssistant, hass: HomeAssistant,
client: BraviaTV, client: BraviaTV,
pin: str, config: MappingProxyType[str, Any],
use_psk: bool,
ignored_sources: list[str], ignored_sources: list[str],
) -> None: ) -> None:
"""Initialize Bravia TV Client.""" """Initialize Bravia TV Client."""
self.client = client self.client = client
self.pin = pin self.pin = config[CONF_PIN]
self.use_psk = use_psk self.use_psk = config.get(CONF_USE_PSK, False)
self.client_id = config.get(CONF_CLIENT_ID, LEGACY_CLIENT_ID)
self.nickname = config.get(CONF_NICKNAME, NICKNAME_PREFIX)
self.ignored_sources = ignored_sources self.ignored_sources = ignored_sources
self.source: str | None = None self.source: str | None = None
self.source_list: list[str] = [] self.source_list: list[str] = []
@ -119,7 +129,7 @@ class BraviaTVCoordinator(DataUpdateCoordinator[None]):
await self.client.connect(psk=self.pin) await self.client.connect(psk=self.pin)
else: else:
await self.client.connect( await self.client.connect(
pin=self.pin, clientid=CLIENTID_PREFIX, nickname=NICKNAME pin=self.pin, clientid=self.client_id, nickname=self.nickname
) )
self.connected = True self.connected = True

View File

@ -12,12 +12,16 @@ import pytest
from homeassistant import data_entry_flow from homeassistant import data_entry_flow
from homeassistant.components import ssdp from homeassistant.components import ssdp
from homeassistant.components.braviatv.const import ( from homeassistant.components.braviatv.const import (
CONF_CLIENT_ID,
CONF_IGNORED_SOURCES, CONF_IGNORED_SOURCES,
CONF_NICKNAME,
CONF_USE_PSK, CONF_USE_PSK,
DOMAIN, DOMAIN,
NICKNAME_PREFIX,
) )
from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_SSDP, SOURCE_USER from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_SSDP, SOURCE_USER
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_PIN from homeassistant.const import CONF_HOST, CONF_MAC, CONF_PIN
from homeassistant.helpers import instance_id
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
@ -93,6 +97,7 @@ async def test_show_form(hass):
async def test_ssdp_discovery(hass): async def test_ssdp_discovery(hass):
"""Test that the device is discovered.""" """Test that the device is discovered."""
uuid = await instance_id.async_get(hass)
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": SOURCE_SSDP}, context={"source": SOURCE_SSDP},
@ -129,6 +134,8 @@ async def test_ssdp_discovery(hass):
CONF_PIN: "1234", CONF_PIN: "1234",
CONF_USE_PSK: False, CONF_USE_PSK: False,
CONF_MAC: "AA:BB:CC:DD:EE:FF", CONF_MAC: "AA:BB:CC:DD:EE:FF",
CONF_CLIENT_ID: uuid,
CONF_NICKNAME: f"{NICKNAME_PREFIX} {uuid[:6]}",
} }
@ -270,6 +277,8 @@ async def test_duplicate_error(hass):
async def test_create_entry(hass): async def test_create_entry(hass):
"""Test that the user step works.""" """Test that the user step works."""
uuid = await instance_id.async_get(hass)
with patch("pybravia.BraviaTV.connect"), patch("pybravia.BraviaTV.pair"), patch( with patch("pybravia.BraviaTV.connect"), patch("pybravia.BraviaTV.pair"), patch(
"pybravia.BraviaTV.set_wol_mode" "pybravia.BraviaTV.set_wol_mode"
), patch( ), patch(
@ -297,11 +306,15 @@ async def test_create_entry(hass):
CONF_PIN: "1234", CONF_PIN: "1234",
CONF_USE_PSK: False, CONF_USE_PSK: False,
CONF_MAC: "AA:BB:CC:DD:EE:FF", CONF_MAC: "AA:BB:CC:DD:EE:FF",
CONF_CLIENT_ID: uuid,
CONF_NICKNAME: f"{NICKNAME_PREFIX} {uuid[:6]}",
} }
async def test_create_entry_with_ipv6_address(hass): async def test_create_entry_with_ipv6_address(hass):
"""Test that the user step works with device IPv6 address.""" """Test that the user step works with device IPv6 address."""
uuid = await instance_id.async_get(hass)
with patch("pybravia.BraviaTV.connect"), patch("pybravia.BraviaTV.pair"), patch( with patch("pybravia.BraviaTV.connect"), patch("pybravia.BraviaTV.pair"), patch(
"pybravia.BraviaTV.set_wol_mode" "pybravia.BraviaTV.set_wol_mode"
), patch( ), patch(
@ -331,6 +344,8 @@ async def test_create_entry_with_ipv6_address(hass):
CONF_PIN: "1234", CONF_PIN: "1234",
CONF_USE_PSK: False, CONF_USE_PSK: False,
CONF_MAC: "AA:BB:CC:DD:EE:FF", CONF_MAC: "AA:BB:CC:DD:EE:FF",
CONF_CLIENT_ID: uuid,
CONF_NICKNAME: f"{NICKNAME_PREFIX} {uuid[:6]}",
} }