diff --git a/custom_components/lghorizon/__init__.py b/custom_components/lghorizon/__init__.py index 098bcb3..7d051c9 100644 --- a/custom_components/lghorizon/__init__.py +++ b/custom_components/lghorizon/__init__.py @@ -1,4 +1,5 @@ """The lghorizon integration.""" + from __future__ import annotations from homeassistant.config_entries import ConfigEntry @@ -13,7 +14,8 @@ CONF_REFRESH_TOKEN, API, COUNTRY_CODES, - CONF_IDENTIFIER + CONF_IDENTIFIER, + CONF_PROFILE_ID, ) from lghorizon import LGHorizonApi @@ -28,8 +30,8 @@ vol.Optional(CONF_COUNTRY_CODE, default="nl"): cv.string, vol.Required(CONF_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string, - vol.Optional(CONF_IDENTIFIER):cv.string, - vol.Optional(CONF_REFRESH_TOKEN):cv.string + vol.Optional(CONF_IDENTIFIER): cv.string, + vol.Optional(CONF_REFRESH_TOKEN): cv.string, } ) }, @@ -45,14 +47,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: refresh_token = None if CONF_REFRESH_TOKEN in entry.data: - refresh_token = entry.data[CONF_REFRESH_TOKEN] - + refresh_token = entry.data[CONF_REFRESH_TOKEN] + api = LGHorizonApi( entry.data[CONF_USERNAME], entry.data[CONF_PASSWORD], COUNTRY_CODES[entry.data[CONF_COUNTRY_CODE]], telenet_identifier, refresh_token, + profile_id=entry.data[CONF_PROFILE_ID], ) await hass.async_add_executor_job(api.connect) hass.data.setdefault(DOMAIN, {}) diff --git a/custom_components/lghorizon/config_flow.py b/custom_components/lghorizon/config_flow.py index 23db868..7d3633a 100644 --- a/custom_components/lghorizon/config_flow.py +++ b/custom_components/lghorizon/config_flow.py @@ -1,4 +1,5 @@ """Config flow for arrisdcx960 integration.""" + from __future__ import annotations import logging @@ -11,98 +12,156 @@ from homeassistant.data_entry_flow import FlowResult from homeassistant.exceptions import HomeAssistantError from homeassistant.const import CONF_USERNAME, CONF_PASSWORD +from homeassistant.helpers.selector import ( + SelectSelectorMode, + SelectOptionDict, + SelectSelector, + SelectSelectorConfig, +) + import homeassistant.helpers.config_validation as cv -from .const import DOMAIN, CONF_COUNTRY_CODE, CONF_REFRESH_TOKEN, COUNTRY_CODES, CONF_IDENTIFIER + from lghorizon import ( LGHorizonApi, LGHorizonApiUnauthorizedError, LGHorizonApiConnectionError, + LGHorizonApiLockedError, + LGHorizonCustomer, ) -_LOGGER = logging.getLogger(__name__) - -STEP_USER_DATA_SCHEMA = vol.Schema( - { - vol.Required(CONF_COUNTRY_CODE, default=list(COUNTRY_CODES.keys())[0]): vol.In( - list(COUNTRY_CODES.keys()) - ), - vol.Required(CONF_USERNAME): cv.string, - vol.Required(CONF_PASSWORD): cv.string, - vol.Optional(CONF_IDENTIFIER):cv.string, - vol.Optional(CONF_REFRESH_TOKEN):cv.string - } +from .const import ( + DOMAIN, + CONF_COUNTRY_CODE, + CONF_REFRESH_TOKEN, + COUNTRY_CODES, + CONF_IDENTIFIER, + CONF_PROFILE_ID, ) -async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, Any]: - """Validate the user input allows us to connect.""" +_LOGGER = logging.getLogger(__name__) - try: - telenet_identifier = None - if CONF_IDENTIFIER in data: - telenet_identifier = data[CONF_IDENTIFIER] - refresh_token = None - if CONF_REFRESH_TOKEN in data: - refresh_token = data[CONF_REFRESH_TOKEN] +class CannotConnect(HomeAssistantError): + """Error to indicate we cannot connect.""" - api = LGHorizonApi( - data[CONF_USERNAME], - data[CONF_PASSWORD], - COUNTRY_CODES[data[CONF_COUNTRY_CODE]], - telenet_identifier, - refresh_token, - ) - await hass.async_add_executor_job(api.connect) - await hass.async_add_executor_job(api.disconnect) - except LGHorizonApiUnauthorizedError: - raise InvalidAuth - except LGHorizonApiConnectionError: - raise CannotConnect - except Exception as ex: - _LOGGER.error(ex) - raise CannotConnect - return {"title": data[CONF_USERNAME]} +class InvalidAuth(HomeAssistantError): + """Error to indicate there is invalid auth.""" + + +class AccountLocked(HomeAssistantError): + """Error to indicate account is locked.""" class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Handle a config flow for lghorizon.""" VERSION = 1 + CONFIG_DATA: dict[str, Any] = {} + customer: LGHorizonCustomer = None async def async_step_user( self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Handle the initial step.""" + + user_schema = vol.Schema( + { + vol.Required( + CONF_COUNTRY_CODE, default=list(COUNTRY_CODES.keys())[0] + ): vol.In(list(COUNTRY_CODES.keys())), + vol.Required(CONF_USERNAME): cv.string, + vol.Required(CONF_PASSWORD): cv.string, + vol.Optional(CONF_IDENTIFIER): cv.string, + vol.Optional(CONF_REFRESH_TOKEN): cv.string, + } + ) + if user_input is None: - return self.async_show_form( - step_id="user", data_schema=STEP_USER_DATA_SCHEMA - ) + return self.async_show_form(step_id="user", data_schema=user_schema) errors = {} try: - info = await validate_input(self.hass, user_input) + await self.validate_input(self.hass, user_input) except CannotConnect: errors["base"] = "cannot_connect" except InvalidAuth: errors["base"] = "invalid_auth" + except AccountLocked: + errors["base"] = "account_locked" except Exception: # pylint: disable=broad-except _LOGGER.exception("Unexpected exception") errors["base"] = "unknown" else: - return self.async_create_entry(title=info["title"], data=user_input) + self.CONFIG_DATA.update(user_input) + profile_step = await self.async_step_profile(user_input=user_input) + return profile_step - return self.async_show_form( - step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors + return await self.async_show_form( + step_id="user", data_schema=user_schema, errors=errors ) + async def validate_input(self, hass: HomeAssistant, data: dict[str, Any]): + """Validate the user input allows us to connect.""" -class CannotConnect(HomeAssistantError): - """Error to indicate we cannot connect.""" - + try: + telenet_identifier = None + if CONF_IDENTIFIER in data: + telenet_identifier = data[CONF_IDENTIFIER] + + refresh_token = None + if CONF_REFRESH_TOKEN in data: + refresh_token = data[CONF_REFRESH_TOKEN] + + api = LGHorizonApi( + data[CONF_USERNAME], + data[CONF_PASSWORD], + COUNTRY_CODES[data[CONF_COUNTRY_CODE]], + telenet_identifier, + refresh_token, + ) + await hass.async_add_executor_job(api.connect) + # store customer for profile extraction + self.customer = api.customer + await hass.async_add_executor_job(api.disconnect) + except LGHorizonApiUnauthorizedError: + raise InvalidAuth + except LGHorizonApiConnectionError: + raise CannotConnect + except LGHorizonApiLockedError: + raise AccountLocked + except Exception as ex: + _LOGGER.error(ex) + raise CannotConnect + + async def async_step_profile( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + profile_selectors = [] + for profile in self.customer.profiles.values(): + profile_selectors.append( + SelectOptionDict(value=profile.profile_id, label=profile.name), + ) + profile_schema = vol.Schema( + { + vol.Required(CONF_PROFILE_ID): SelectSelector( + SelectSelectorConfig( + options=profile_selectors, mode=SelectSelectorMode.DROPDOWN + ), + ), + } + ) -class InvalidAuth(HomeAssistantError): - """Error to indicate there is invalid auth.""" + if ( + user_input is None + or CONF_PROFILE_ID not in user_input + or not user_input[CONF_PROFILE_ID] + ): + return self.async_show_form(step_id="profile", data_schema=profile_schema) + self.CONFIG_DATA.update(user_input) + return self.async_create_entry( + title=self.CONFIG_DATA[CONF_USERNAME], data=self.CONFIG_DATA + ) diff --git a/custom_components/lghorizon/const.py b/custom_components/lghorizon/const.py index 9e8dc55..fd0f6d7 100644 --- a/custom_components/lghorizon/const.py +++ b/custom_components/lghorizon/const.py @@ -6,6 +6,8 @@ CONF_REFRESH_TOKEN = "refresh_token" CONF_REMOTE_KEY = "remote_key" CONF_IDENTIFIER = "identifier" +CONF_PROFILE_ID = "profile_id" + RECORD = "record" REWIND = "rewind" diff --git a/custom_components/lghorizon/manifest.json b/custom_components/lghorizon/manifest.json index 126a6c1..4503822 100644 --- a/custom_components/lghorizon/manifest.json +++ b/custom_components/lghorizon/manifest.json @@ -9,7 +9,7 @@ "iot_class": "cloud_push", "issue_tracker": "https://github.com/Sholofly/lghorizon/issues", "requirements": [ - "lghorizon>=0.7.2" + "lghorizon>=0.7.4" ], - "version": "0.6.3" + "version": "0.6.4" } diff --git a/custom_components/lghorizon/media_player.py b/custom_components/lghorizon/media_player.py index aa309cf..45a0b52 100644 --- a/custom_components/lghorizon/media_player.py +++ b/custom_components/lghorizon/media_player.py @@ -123,7 +123,13 @@ def device_info(self): "model": self._box.model or "unknown", } - def __init__(self, box: LGHorizonBox, api: LGHorizonApi, hass: HomeAssistant, entry: ConfigEntry): + def __init__( + self, + box: LGHorizonBox, + api: LGHorizonApi, + hass: HomeAssistant, + entry: ConfigEntry, + ): """Init the media player.""" self._box = box self.api = api @@ -131,12 +137,6 @@ def __init__(self, box: LGHorizonBox, api: LGHorizonApi, hass: HomeAssistant, en self.entry = entry self.box_id = box.deviceId self.box_name = box.deviceFriendlyName - self._create_channel_map() - - def _create_channel_map(self): - self._channels = {} - for channel in self.api._channels.values(): - self._channels[channel.title] = channel.title async def async_added_to_hass(self): """Use lifecycle hooks.""" @@ -157,9 +157,7 @@ def _save_refresh_token(self): _LOGGER.info("New JWT stored (2): %s", self.api.refresh_token) new_data = {**self.entry.data} new_data[CONF_REFRESH_TOKEN] = self.api.refresh_token - self.hass.config_entries.async_update_entry( - self.entry, data=new_data - ) + self.hass.config_entries.async_update_entry(self.entry, data=new_data) async def async_update(self): """Update the box.""" @@ -256,7 +254,10 @@ def source(self): @property def source_list(self): """Return a list with available sources.""" - return [channel for channel in self._channels.keys()] + channel_list = [] + for channel in self.api.get_display_channels(): + channel_list.append(channel.title) + return channel_list @property def media_duration(self) -> int | None: diff --git a/custom_components/lghorizon/translations/en.json b/custom_components/lghorizon/translations/en.json index e506af0..236b8a7 100644 --- a/custom_components/lghorizon/translations/en.json +++ b/custom_components/lghorizon/translations/en.json @@ -12,11 +12,19 @@ "identifier": "DTV identifier", "refresh_token": "Refresh Token (GB)" } + }, + "profile": { + "title": "LG Horizon - Select profile", + "description":"Select your profile to show it's favorite channels only", + "data": { + "profile_id": "Profile" + } } }, "error": { "cannot_connect": "Can't connect", "invalid_auth": "Invalid credentials", + "account_locked": "Your account is locked. Please wait 10 minutes and try again.", "unknown": "Unknown error" }, "abort": { diff --git a/custom_components/lghorizon/translations/nl.json b/custom_components/lghorizon/translations/nl.json index 6fdf200..db33ac9 100644 --- a/custom_components/lghorizon/translations/nl.json +++ b/custom_components/lghorizon/translations/nl.json @@ -12,11 +12,19 @@ "identifier": "DTV identifier", "refresh_token": "Refresh Token (GB)" } + }, + "profile": { + "title": "LG Horizon - Selecteer profiel", + "description":"Selecteer het profiel waarvan je de favoriete kanalen wilt tonen", + "data": { + "profile_id": "Profiel" + } } }, "error": { "cannot_connect": "Kan niet verbinden", "invalid_auth": "Ongeldige logingegevens", + "account_locked": "Je account is geblokkeerd. Wacht 10 minuten voor je het weer probeert.", "unknown": "Onbekende fout" }, "abort": {