initial commit
This commit is contained in:
235
custom_components/openhasp/config_flow.py
Normal file
235
custom_components/openhasp/config_flow.py
Normal file
@@ -0,0 +1,235 @@
|
||||
"""Config flow to configure OpenHASP component."""
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
||||
from homeassistant import config_entries, data_entry_flow, exceptions
|
||||
from homeassistant.components.mqtt import valid_subscribe_topic
|
||||
from homeassistant.const import CONF_NAME
|
||||
from homeassistant.core import callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
import voluptuous as vol
|
||||
|
||||
from .const import (
|
||||
CONF_DIMLIGHTS,
|
||||
CONF_HWID,
|
||||
CONF_IDLE_BRIGHTNESS,
|
||||
CONF_INPUT,
|
||||
CONF_LIGHTS,
|
||||
CONF_NODE,
|
||||
CONF_PAGES,
|
||||
CONF_PAGES_PATH,
|
||||
CONF_RELAYS,
|
||||
CONF_TOPIC,
|
||||
DEFAULT_IDLE_BRIGHNESS,
|
||||
DISCOVERED_DIM,
|
||||
DISCOVERED_HWID,
|
||||
DISCOVERED_INPUT,
|
||||
DISCOVERED_LIGHT,
|
||||
DISCOVERED_MANUFACTURER,
|
||||
DISCOVERED_MODEL,
|
||||
DISCOVERED_NODE,
|
||||
DISCOVERED_PAGES,
|
||||
DISCOVERED_POWER,
|
||||
DISCOVERED_URL,
|
||||
DISCOVERED_VERSION,
|
||||
DOMAIN,
|
||||
MAJOR,
|
||||
MINOR,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def validate_jsonl(path):
|
||||
"""Validate that the value is an existing file."""
|
||||
if path is None:
|
||||
raise InvalidJSONL()
|
||||
file_in = os.path.expanduser(str(path))
|
||||
|
||||
if not os.path.isfile(file_in):
|
||||
raise InvalidJSONL("not a file")
|
||||
if not os.access(file_in, os.R_OK):
|
||||
raise InvalidJSONL("file not readable")
|
||||
return file_in
|
||||
|
||||
|
||||
@config_entries.HANDLERS.register(DOMAIN)
|
||||
class OpenHASPFlowHandler(config_entries.ConfigFlow):
|
||||
"""Config flow for OpenHASP component."""
|
||||
|
||||
VERSION = 1
|
||||
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH
|
||||
|
||||
def __init__(self):
|
||||
"""Init OpenHASPFlowHandler."""
|
||||
self._errors = {}
|
||||
self.config_data = {
|
||||
DISCOVERED_MANUFACTURER: "openHASP",
|
||||
DISCOVERED_MODEL: None,
|
||||
CONF_RELAYS: [],
|
||||
}
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
"""Handle a flow initialized by User."""
|
||||
_LOGGER.error("Discovery Only")
|
||||
|
||||
await self.hass.components.mqtt.async_publish(
|
||||
self.hass,
|
||||
"hasp/broadcast/command/discovery",
|
||||
"discovery",
|
||||
qos=0,
|
||||
retain=False,
|
||||
)
|
||||
|
||||
return self.async_abort(reason="discovery_only")
|
||||
|
||||
async def async_step_mqtt(self, discovery_info=None):
|
||||
"""Handle a flow initialized by MQTT discovery."""
|
||||
_discovered = json.loads(discovery_info.payload)
|
||||
_LOGGER.debug("Discovered: %s", _discovered)
|
||||
|
||||
await self.async_set_unique_id(_discovered[DISCOVERED_HWID], raise_on_progress=False)
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
version = _discovered.get(DISCOVERED_VERSION)
|
||||
if version.split(".")[0:2] != [MAJOR, MINOR]:
|
||||
_LOGGER.error(
|
||||
"Version mismatch! Your plate: %s - openHASP Component: %s",
|
||||
version,
|
||||
f"{MAJOR}.{MINOR}.x",
|
||||
)
|
||||
raise data_entry_flow.AbortFlow("mismatch_version")
|
||||
|
||||
self.config_data[DISCOVERED_VERSION] = version
|
||||
|
||||
self.config_data[CONF_HWID] = _discovered[DISCOVERED_HWID]
|
||||
self.config_data[CONF_NODE] = self.config_data[CONF_NAME] = _discovered[
|
||||
DISCOVERED_NODE
|
||||
]
|
||||
self.config_data[
|
||||
CONF_TOPIC
|
||||
] = f"{discovery_info.topic.split('/')[0]}/{self.config_data[CONF_NODE]}"
|
||||
|
||||
self.config_data[DISCOVERED_URL] = _discovered.get(DISCOVERED_URL)
|
||||
self.config_data[DISCOVERED_MANUFACTURER] = _discovered.get(
|
||||
DISCOVERED_MANUFACTURER
|
||||
)
|
||||
self.config_data[DISCOVERED_MODEL] = _discovered.get(DISCOVERED_MODEL)
|
||||
self.config_data[CONF_PAGES] = _discovered.get(DISCOVERED_PAGES)
|
||||
self.config_data[CONF_RELAYS] = _discovered.get(DISCOVERED_POWER)
|
||||
self.config_data[CONF_LIGHTS] = _discovered.get(DISCOVERED_LIGHT)
|
||||
self.config_data[CONF_DIMLIGHTS] = _discovered.get(DISCOVERED_DIM)
|
||||
self.config_data[CONF_INPUT] = _discovered.get(DISCOVERED_INPUT)
|
||||
|
||||
return await self.async_step_personalize()
|
||||
|
||||
async def async_step_personalize(self, user_input=None):
|
||||
"""Handle a flow initialized by the user."""
|
||||
self._errors = {}
|
||||
|
||||
if user_input is not None:
|
||||
self.config_data = {**self.config_data, **user_input}
|
||||
|
||||
if self.config_data[
|
||||
CONF_NAME
|
||||
] not in self.hass.config_entries.async_entries(DOMAIN):
|
||||
# Remove / from base topic
|
||||
if user_input[CONF_TOPIC].endswith("/"):
|
||||
user_input[CONF_TOPIC] = user_input[CONF_TOPIC][:-1]
|
||||
|
||||
try:
|
||||
valid_subscribe_topic(self.config_data[CONF_TOPIC])
|
||||
|
||||
if CONF_PAGES_PATH in user_input:
|
||||
self.config_data[CONF_PAGES_PATH] = validate_jsonl(
|
||||
user_input[CONF_PAGES_PATH]
|
||||
)
|
||||
|
||||
await self.async_set_unique_id(self.config_data[CONF_HWID])
|
||||
|
||||
return self.async_create_entry(
|
||||
title=user_input[CONF_NAME], data=self.config_data
|
||||
)
|
||||
|
||||
except vol.Invalid:
|
||||
return self.async_abort(reason="invalid_discovery_info")
|
||||
|
||||
except InvalidJSONL:
|
||||
self._errors[CONF_PAGES_PATH] = "invalid_jsonl_path"
|
||||
else:
|
||||
self._errors[CONF_NAME] = "name_exists"
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="personalize",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Required(
|
||||
CONF_TOPIC, default=self.config_data.get(CONF_TOPIC, "hasp")
|
||||
): str,
|
||||
vol.Required(
|
||||
CONF_NAME, default=self.config_data.get(CONF_NAME)
|
||||
): str,
|
||||
vol.Optional(
|
||||
CONF_IDLE_BRIGHTNESS, default=DEFAULT_IDLE_BRIGHNESS
|
||||
): vol.All(int, vol.Range(min=0, max=255)),
|
||||
vol.Optional(CONF_PAGES_PATH): str,
|
||||
}
|
||||
),
|
||||
errors=self._errors,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(config_entry):
|
||||
"""Set the OptionsFlowHandler."""
|
||||
return OpenHASPOptionsFlowHandler(config_entry)
|
||||
|
||||
|
||||
class OpenHASPOptionsFlowHandler(config_entries.OptionsFlow):
|
||||
"""ConfigOptions flow for openHASP."""
|
||||
|
||||
def __init__(self, config_entry):
|
||||
"""Initialize openHASP options flow."""
|
||||
self.config_entry = config_entry
|
||||
|
||||
async def async_step_init(self, user_input=None):
|
||||
"""Manage the options."""
|
||||
if user_input is not None:
|
||||
# Actually check path is a file
|
||||
|
||||
try:
|
||||
if len(user_input[CONF_PAGES_PATH]):
|
||||
user_input[CONF_PAGES_PATH] = validate_jsonl(
|
||||
user_input[CONF_PAGES_PATH]
|
||||
)
|
||||
except InvalidJSONL:
|
||||
return self.async_abort(reason="invalid_jsonl_path")
|
||||
|
||||
return self.async_create_entry(title="", data=user_input)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="init",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Optional(
|
||||
CONF_IDLE_BRIGHTNESS,
|
||||
default=self.config_entry.options.get(
|
||||
CONF_IDLE_BRIGHTNESS,
|
||||
self.config_entry.data[CONF_IDLE_BRIGHTNESS],
|
||||
),
|
||||
): vol.All(int, vol.Range(min=0, max=255)),
|
||||
vol.Optional(
|
||||
CONF_PAGES_PATH,
|
||||
default=self.config_entry.options.get(
|
||||
CONF_PAGES_PATH,
|
||||
self.config_entry.data.get(CONF_PAGES_PATH, ""),
|
||||
),
|
||||
): cv.string,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class InvalidJSONL(exceptions.HomeAssistantError):
|
||||
"""Error to indicate we cannot load JSONL."""
|
||||
Reference in New Issue
Block a user