Home Assistant Unofficial Reference 2024.12.1
config_flow.py
Go to the documentation of this file.
1 """Config flow for the html5 component."""
2 
3 from __future__ import annotations
4 
5 import binascii
6 from typing import Any, cast
7 
8 from cryptography.hazmat.backends import default_backend
9 from cryptography.hazmat.primitives import serialization
10 from cryptography.hazmat.primitives.asymmetric import ec
11 from py_vapid import Vapid
12 from py_vapid.utils import b64urlencode
13 import voluptuous as vol
14 
15 from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
16 from homeassistant.const import CONF_NAME
17 from homeassistant.core import callback
18 
19 from .const import ATTR_VAPID_EMAIL, ATTR_VAPID_PRV_KEY, ATTR_VAPID_PUB_KEY, DOMAIN
20 from .issues import async_create_html5_issue
21 
22 
24  """Generate a VAPID private key."""
25  private_key = ec.generate_private_key(ec.SECP256R1(), default_backend())
26  return b64urlencode(
27  binascii.unhexlify(f"{private_key.private_numbers().private_value:x}".zfill(64))
28  )
29 
30 
31 def vapid_get_public_key(private_key: str) -> str:
32  """Get the VAPID public key from a private key."""
33  vapid = Vapid.from_string(private_key)
34  public_key = cast(ec.EllipticCurvePublicKey, vapid.public_key)
35  return b64urlencode(
36  public_key.public_bytes(
37  serialization.Encoding.X962, serialization.PublicFormat.UncompressedPoint
38  )
39  )
40 
41 
42 class HTML5ConfigFlow(ConfigFlow, domain=DOMAIN):
43  """Handle a config flow for HTML5."""
44 
45  @callback
47  self: HTML5ConfigFlow, data: dict[str, str]
48  ) -> tuple[dict[str, str], ConfigFlowResult | None]:
49  """Create an HTML5 entry."""
50  errors = {}
51  flow_result = None
52 
53  if not data.get(ATTR_VAPID_PRV_KEY):
54  data[ATTR_VAPID_PRV_KEY] = vapid_generate_private_key()
55 
56  # we will always generate the corresponding public key
57  try:
58  data[ATTR_VAPID_PUB_KEY] = vapid_get_public_key(data[ATTR_VAPID_PRV_KEY])
59  except (ValueError, binascii.Error):
60  errors[ATTR_VAPID_PRV_KEY] = "invalid_prv_key"
61 
62  if not errors:
63  config = {
64  ATTR_VAPID_EMAIL: data[ATTR_VAPID_EMAIL],
65  ATTR_VAPID_PRV_KEY: data[ATTR_VAPID_PRV_KEY],
66  ATTR_VAPID_PUB_KEY: data[ATTR_VAPID_PUB_KEY],
67  CONF_NAME: DOMAIN,
68  }
69  flow_result = self.async_create_entryasync_create_entryasync_create_entry(title="HTML5", data=config)
70  return errors, flow_result
71 
72  async def async_step_user(
73  self: HTML5ConfigFlow, user_input: dict[str, Any] | None = None
74  ) -> ConfigFlowResult:
75  """Handle a flow initialized by the user."""
76  errors: dict[str, str] = {}
77  if user_input:
78  errors, flow_result = self._async_create_html5_entry_async_create_html5_entry(user_input)
79  if flow_result:
80  return flow_result
81  else:
82  user_input = {}
83 
84  return self.async_show_formasync_show_formasync_show_form(
85  data_schema=vol.Schema(
86  {
87  vol.Required(
88  ATTR_VAPID_EMAIL, default=user_input.get(ATTR_VAPID_EMAIL, "")
89  ): str,
90  vol.Optional(ATTR_VAPID_PRV_KEY): str,
91  }
92  ),
93  errors=errors,
94  )
95 
96  async def async_step_import(
97  self: HTML5ConfigFlow, import_config: dict
98  ) -> ConfigFlowResult:
99  """Handle config import from yaml."""
100  _, flow_result = self._async_create_html5_entry_async_create_html5_entry(import_config)
101  if not flow_result:
102  async_create_html5_issue(self.hass, False)
103  return self.async_abortasync_abortasync_abort(reason="invalid_config")
104  async_create_html5_issue(self.hass, True)
105  return flow_result
ConfigFlowResult async_step_import(HTML5ConfigFlow self, dict import_config)
Definition: config_flow.py:98
tuple[dict[str, str], ConfigFlowResult|None] _async_create_html5_entry(HTML5ConfigFlow self, dict[str, str] data)
Definition: config_flow.py:48
ConfigFlowResult async_step_user(HTML5ConfigFlow self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:74
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_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)
None async_create_html5_issue(HomeAssistant hass, bool import_success)
Definition: issues.py:19