156 lines
4.7 KiB
Python
156 lines
4.7 KiB
Python
"""Config flow for Elk-M1 Control integration."""
|
|
import asyncio
|
|
import logging
|
|
from urllib.parse import urlparse
|
|
|
|
import elkm1_lib as elkm1
|
|
import voluptuous as vol
|
|
|
|
from homeassistant import config_entries, exceptions
|
|
from homeassistant.const import (
|
|
CONF_ADDRESS,
|
|
CONF_HOST,
|
|
CONF_PASSWORD,
|
|
CONF_PREFIX,
|
|
CONF_PROTOCOL,
|
|
CONF_TEMPERATURE_UNIT,
|
|
CONF_USERNAME,
|
|
TEMP_CELSIUS,
|
|
TEMP_FAHRENHEIT,
|
|
)
|
|
from homeassistant.util import slugify
|
|
|
|
from . import async_wait_for_elk_to_sync
|
|
from .const import CONF_AUTO_CONFIGURE, DOMAIN
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
PROTOCOL_MAP = {
|
|
"secure": "elks://",
|
|
"TLS 1.2": "elksv1_2://",
|
|
"non-secure": "elk://",
|
|
"serial": "serial://",
|
|
}
|
|
|
|
DATA_SCHEMA = vol.Schema(
|
|
{
|
|
vol.Required(CONF_PROTOCOL, default="secure"): vol.In(
|
|
["secure", "TLS 1.2", "non-secure", "serial"]
|
|
),
|
|
vol.Required(CONF_ADDRESS): str,
|
|
vol.Optional(CONF_USERNAME, default=""): str,
|
|
vol.Optional(CONF_PASSWORD, default=""): str,
|
|
vol.Optional(CONF_PREFIX, default=""): str,
|
|
vol.Optional(CONF_TEMPERATURE_UNIT, default=TEMP_FAHRENHEIT): vol.In(
|
|
[TEMP_FAHRENHEIT, TEMP_CELSIUS]
|
|
),
|
|
}
|
|
)
|
|
|
|
VALIDATE_TIMEOUT = 35
|
|
|
|
|
|
async def validate_input(data):
|
|
"""Validate the user input allows us to connect.
|
|
|
|
Data has the keys from DATA_SCHEMA with values provided by the user.
|
|
"""
|
|
userid = data.get(CONF_USERNAME)
|
|
password = data.get(CONF_PASSWORD)
|
|
|
|
prefix = data[CONF_PREFIX]
|
|
url = _make_url_from_data(data)
|
|
requires_password = url.startswith("elks://") or url.startswith("elksv1_2")
|
|
|
|
if requires_password and (not userid or not password):
|
|
raise InvalidAuth
|
|
|
|
elk = elkm1.Elk(
|
|
{"url": url, "userid": userid, "password": password, "element_list": ["panel"]}
|
|
)
|
|
elk.connect()
|
|
|
|
if not await async_wait_for_elk_to_sync(elk, VALIDATE_TIMEOUT, url):
|
|
raise InvalidAuth
|
|
|
|
device_name = data[CONF_PREFIX] if data[CONF_PREFIX] else "ElkM1"
|
|
# Return info that you want to store in the config entry.
|
|
return {"title": device_name, CONF_HOST: url, CONF_PREFIX: slugify(prefix)}
|
|
|
|
|
|
def _make_url_from_data(data):
|
|
if host := data.get(CONF_HOST):
|
|
return host
|
|
|
|
protocol = PROTOCOL_MAP[data[CONF_PROTOCOL]]
|
|
address = data[CONF_ADDRESS]
|
|
return f"{protocol}{address}"
|
|
|
|
|
|
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|
"""Handle a config flow for Elk-M1 Control."""
|
|
|
|
VERSION = 1
|
|
|
|
def __init__(self):
|
|
"""Initialize the elkm1 config flow."""
|
|
self.importing = False
|
|
|
|
async def async_step_user(self, user_input=None):
|
|
"""Handle the initial step."""
|
|
errors = {}
|
|
if user_input is not None:
|
|
if self._url_already_configured(_make_url_from_data(user_input)):
|
|
return self.async_abort(reason="address_already_configured")
|
|
|
|
try:
|
|
info = await validate_input(user_input)
|
|
|
|
except asyncio.TimeoutError:
|
|
errors["base"] = "cannot_connect"
|
|
except InvalidAuth:
|
|
errors["base"] = "invalid_auth"
|
|
except Exception: # pylint: disable=broad-except
|
|
_LOGGER.exception("Unexpected exception")
|
|
errors["base"] = "unknown"
|
|
|
|
if "base" not in errors:
|
|
await self.async_set_unique_id(user_input[CONF_PREFIX])
|
|
self._abort_if_unique_id_configured()
|
|
|
|
if self.importing:
|
|
return self.async_create_entry(title=info["title"], data=user_input)
|
|
|
|
return self.async_create_entry(
|
|
title=info["title"],
|
|
data={
|
|
CONF_HOST: info[CONF_HOST],
|
|
CONF_USERNAME: user_input[CONF_USERNAME],
|
|
CONF_PASSWORD: user_input[CONF_PASSWORD],
|
|
CONF_AUTO_CONFIGURE: True,
|
|
CONF_TEMPERATURE_UNIT: user_input[CONF_TEMPERATURE_UNIT],
|
|
CONF_PREFIX: info[CONF_PREFIX],
|
|
},
|
|
)
|
|
|
|
return self.async_show_form(
|
|
step_id="user", data_schema=DATA_SCHEMA, errors=errors
|
|
)
|
|
|
|
async def async_step_import(self, user_input):
|
|
"""Handle import."""
|
|
self.importing = True
|
|
return await self.async_step_user(user_input)
|
|
|
|
def _url_already_configured(self, url):
|
|
"""See if we already have a elkm1 matching user input configured."""
|
|
existing_hosts = {
|
|
urlparse(entry.data[CONF_HOST]).hostname
|
|
for entry in self._async_current_entries()
|
|
}
|
|
return urlparse(url).hostname in existing_hosts
|
|
|
|
|
|
class InvalidAuth(exceptions.HomeAssistantError):
|
|
"""Error to indicate there is invalid auth."""
|