/
config_flow.py
115 lines (90 loc) · 3.62 KB
/
config_flow.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
"""Config flow to configure Blink."""
import logging
from blinkpy.blinkpy import Blink
import voluptuous as vol
from homeassistant import config_entries, core, exceptions
from homeassistant.const import (
CONF_PASSWORD,
CONF_PIN,
CONF_SCAN_INTERVAL,
CONF_USERNAME,
)
from .const import DEFAULT_OFFSET, DEFAULT_SCAN_INTERVAL, DEVICE_ID, DOMAIN
_LOGGER = logging.getLogger(__name__)
async def validate_input(hass: core.HomeAssistant, blink):
"""Validate the user input allows us to connect."""
response = await hass.async_add_executor_job(blink.get_auth_token)
if not response:
raise InvalidAuth
if blink.key_required:
raise Require2FA
return blink.login_response
class BlinkConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a Blink config flow."""
VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
def __init__(self):
"""Initialize the blink flow."""
self.blink = None
self.data = {
CONF_USERNAME: "",
CONF_PASSWORD: "",
CONF_SCAN_INTERVAL: DEFAULT_SCAN_INTERVAL,
"login_response": None,
}
async def async_step_user(self, user_input=None):
"""Handle a flow initiated by the user."""
errors = {}
if user_input is not None:
self.data[CONF_USERNAME] = user_input["username"]
self.data[CONF_PASSWORD] = user_input["password"]
await self.async_set_unique_id(self.data[CONF_USERNAME])
if CONF_SCAN_INTERVAL in user_input:
self.data[CONF_SCAN_INTERVAL] = user_input["scan_interval"]
self.blink = Blink(
username=self.data[CONF_USERNAME],
password=self.data[CONF_PASSWORD],
motion_interval=DEFAULT_OFFSET,
legacy_subdomain=False,
no_prompt=True,
device_id=DEVICE_ID,
)
try:
response = await validate_input(self.hass, self.blink)
self.data["login_response"] = response
return self.async_create_entry(title=DOMAIN, data=self.data,)
except Require2FA:
return await self.async_step_2fa()
except InvalidAuth:
errors["base"] = "invalid_auth"
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
data_schema = {
vol.Required("username"): str,
vol.Required("password"): str,
}
return self.async_show_form(
step_id="user", data_schema=vol.Schema(data_schema), errors=errors,
)
async def async_step_2fa(self, user_input=None):
"""Handle 2FA step."""
if user_input is not None:
pin = user_input.get(CONF_PIN)
if await self.hass.async_add_executor_job(
self.blink.login_handler.send_auth_key, self.blink, pin
):
return await self.async_step_user(user_input=self.data)
return self.async_show_form(
step_id="2fa",
data_schema=vol.Schema(
{vol.Optional("pin"): vol.All(str, vol.Length(min=1))}
),
)
async def async_step_import(self, import_data):
"""Import blink config from configuration.yaml."""
return await self.async_step_user(import_data)
class Require2FA(exceptions.HomeAssistantError):
"""Error to indicate we require 2FA."""
class InvalidAuth(exceptions.HomeAssistantError):
"""Error to indicate there is invalid auth."""