Home Assistant Unofficial Reference 2024.12.1
config_flow.py
Go to the documentation of this file.
1 """Config flow for Panasonic Viera TV integration."""
2 
3 from functools import partial
4 import logging
5 from typing import Any
6 from urllib.error import URLError
7 
8 from panasonic_viera import TV_TYPE_ENCRYPTED, RemoteControl, SOAPError
9 import voluptuous as vol
10 
11 from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
12 from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PIN, CONF_PORT
13 
14 from .const import (
15  ATTR_DEVICE_INFO,
16  ATTR_FRIENDLY_NAME,
17  ATTR_UDN,
18  CONF_APP_ID,
19  CONF_ENCRYPTION_KEY,
20  CONF_ON_ACTION,
21  DEFAULT_NAME,
22  DEFAULT_PORT,
23  DOMAIN,
24  ERROR_INVALID_PIN_CODE,
25 )
26 
27 _LOGGER = logging.getLogger(__name__)
28 
29 
30 class PanasonicVieraConfigFlow(ConfigFlow, domain=DOMAIN):
31  """Config flow for Panasonic Viera."""
32 
33  VERSION = 1
34 
35  def __init__(self) -> None:
36  """Initialize the Panasonic Viera config flow."""
37  self._data_data: dict[str, Any] = {
38  CONF_HOST: None,
39  CONF_NAME: None,
40  CONF_PORT: None,
41  CONF_ON_ACTION: None,
42  ATTR_DEVICE_INFO: None,
43  }
44 
45  self._remote_remote: RemoteControl | None = None
46 
47  async def async_step_user(
48  self, user_input: dict[str, Any] | None = None
49  ) -> ConfigFlowResult:
50  """Handle the initial step."""
51  errors: dict[str, str] = {}
52 
53  if user_input is not None:
54  await self.async_load_dataasync_load_data(user_input)
55  try:
56  self._remote_remote = await self.hass.async_add_executor_job(
57  partial(RemoteControl, self._data_data[CONF_HOST], self._data_data[CONF_PORT])
58  )
59  assert self._remote_remote is not None
60  self._data_data[ATTR_DEVICE_INFO] = await self.hass.async_add_executor_job(
61  self._remote_remote.get_device_info
62  )
63  except (URLError, SOAPError, OSError) as err:
64  _LOGGER.error("Could not establish remote connection: %s", err)
65  errors["base"] = "cannot_connect"
66  except Exception:
67  _LOGGER.exception("An unknown error occurred")
68  return self.async_abortasync_abortasync_abort(reason="unknown")
69  else:
70  await self.async_set_unique_idasync_set_unique_id(self._data_data[ATTR_DEVICE_INFO][ATTR_UDN])
71  self._abort_if_unique_id_configured_abort_if_unique_id_configured()
72 
73  if self._data_data[CONF_NAME] == DEFAULT_NAME:
74  self._data_data[CONF_NAME] = self._data_data[ATTR_DEVICE_INFO][
75  ATTR_FRIENDLY_NAME
76  ].replace("_", " ")
77 
78  if self._remote_remote.type == TV_TYPE_ENCRYPTED:
79  return await self.async_step_pairingasync_step_pairing()
80 
81  return self.async_create_entryasync_create_entryasync_create_entry(
82  title=self._data_data[CONF_NAME],
83  data=self._data_data,
84  )
85 
86  return self.async_show_formasync_show_formasync_show_form(
87  step_id="user",
88  data_schema=vol.Schema(
89  {
90  vol.Required(
91  CONF_HOST,
92  default=self._data_data[CONF_HOST]
93  if self._data_data[CONF_HOST] is not None
94  else "",
95  ): str,
96  vol.Optional(
97  CONF_NAME,
98  default=self._data_data[CONF_NAME]
99  if self._data_data[CONF_NAME] is not None
100  else DEFAULT_NAME,
101  ): str,
102  }
103  ),
104  errors=errors,
105  )
106 
108  self, user_input: dict[str, Any] | None = None
109  ) -> ConfigFlowResult:
110  """Handle the pairing step."""
111  errors: dict[str, str] = {}
112  assert self._remote_remote is not None
113 
114  if user_input is not None:
115  pin = user_input[CONF_PIN]
116  try:
117  await self.hass.async_add_executor_job(
118  partial(self._remote_remote.authorize_pin_code, pincode=pin)
119  )
120  except SOAPError as err:
121  _LOGGER.error("Invalid PIN code: %s", err)
122  errors["base"] = ERROR_INVALID_PIN_CODE
123  except (URLError, OSError) as err:
124  _LOGGER.error("The remote connection was lost: %s", err)
125  return self.async_abortasync_abortasync_abort(reason="cannot_connect")
126  except Exception:
127  _LOGGER.exception("Unknown error")
128  return self.async_abortasync_abortasync_abort(reason="unknown")
129 
130  if "base" not in errors:
131  encryption_data = {
132  CONF_APP_ID: self._remote_remote.app_id,
133  CONF_ENCRYPTION_KEY: self._remote_remote.enc_key,
134  }
135 
136  self._data_data = {**self._data_data, **encryption_data}
137 
138  return self.async_create_entryasync_create_entryasync_create_entry(
139  title=self._data_data[CONF_NAME],
140  data=self._data_data,
141  )
142 
143  try:
144  await self.hass.async_add_executor_job(
145  partial(self._remote_remote.request_pin_code, name="Home Assistant")
146  )
147  except (URLError, SOAPError, OSError) as err:
148  _LOGGER.error("The remote connection was lost: %s", err)
149  return self.async_abortasync_abortasync_abort(reason="cannot_connect")
150  except Exception:
151  _LOGGER.exception("Unknown error")
152  return self.async_abortasync_abortasync_abort(reason="unknown")
153 
154  return self.async_show_formasync_show_formasync_show_form(
155  step_id="pairing",
156  data_schema=vol.Schema({vol.Required(CONF_PIN): str}),
157  errors=errors,
158  )
159 
160  async def async_step_import(self, import_data: dict[str, Any]) -> ConfigFlowResult:
161  """Import a config entry from configuration.yaml."""
162  return await self.async_step_userasync_step_userasync_step_user(user_input=import_data)
163 
164  async def async_load_data(self, config: dict[str, Any]) -> None:
165  """Load the data."""
166  self._data_data = config
167 
168  self._data_data[CONF_PORT] = self._data_data.get(CONF_PORT, DEFAULT_PORT)
169  self._data_data[CONF_ON_ACTION] = self._data_data.get(CONF_ON_ACTION)
170 
171  await self.async_set_unique_idasync_set_unique_id(self._data_data[CONF_HOST])
172  self._abort_if_unique_id_configured_abort_if_unique_id_configured()
ConfigFlowResult async_step_pairing(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:109
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:49
ConfigFlowResult async_step_import(self, dict[str, Any] import_data)
Definition: config_flow.py:160
None _abort_if_unique_id_configured(self, dict[str, Any]|None updates=None, bool reload_on_update=True, *str error="already_configured")
ConfigEntry|None async_set_unique_id(self, str|None unique_id=None, *bool raise_on_progress=True)
ConfigFlowResult async_create_entry(self, *str title, Mapping[str, Any] data, str|None description=None, Mapping[str, str]|None description_placeholders=None, Mapping[str, Any]|None options=None)
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_abort(self, *str reason, Mapping[str, str]|None description_placeholders=None)
ConfigFlowResult async_show_form(self, *str|None step_id=None, vol.Schema|None data_schema=None, dict[str, str]|None errors=None, Mapping[str, str]|None description_placeholders=None, bool|None last_step=None, str|None preview=None)
_FlowResultT async_show_form(self, *str|None step_id=None, vol.Schema|None data_schema=None, dict[str, str]|None errors=None, Mapping[str, str]|None description_placeholders=None, bool|None last_step=None, str|None preview=None)
_FlowResultT async_create_entry(self, *str|None title=None, Mapping[str, Any] data, str|None description=None, Mapping[str, str]|None description_placeholders=None)
_FlowResultT async_abort(self, *str reason, Mapping[str, str]|None description_placeholders=None)
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88