mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 01:38:02 +00:00

* Add bosch_alarm integration * Remove other platforms for now * update some strings not being consistant * fix sentence-casing for strings * remove options flow and versioning * clean up config flow * Add OSI license + tagged releases + ci to bosch-alarm-mode2 * Apply suggestions from code review Co-authored-by: Josef Zweck <josef@zweck.dev> * apply changes from review * apply changes from review * remove options flow * work on fixtures * work on fixtures * fix errors and complete flow * use fixtures for alarm config * Update homeassistant/components/bosch_alarm/manifest.json Co-authored-by: Josef Zweck <josef@zweck.dev> * fix missing type * mock setup entry * remove use of patch in config flow test * Use coordinator for managing panel data * Use coordinator for managing panel data * Coordinator cleanup * remove unnecessary observers * update listeners when error state changes * Update homeassistant/components/bosch_alarm/coordinator.py Co-authored-by: Josef Zweck <josef@zweck.dev> * Update homeassistant/components/bosch_alarm/quality_scale.yaml Co-authored-by: Josef Zweck <josef@zweck.dev> * Update homeassistant/components/bosch_alarm/config_flow.py Co-authored-by: Josef Zweck <josef@zweck.dev> * rename config flow * Update homeassistant/components/bosch_alarm/quality_scale.yaml Co-authored-by: Josef Zweck <josef@zweck.dev> * add missing types * fix quality_scale.yaml * enable strict typing * enable strict typing * Add test for alarm control panel * add more tests * add more tests * Update homeassistant/components/bosch_alarm/coordinator.py Co-authored-by: Josef Zweck <josef@zweck.dev> * Update homeassistant/components/bosch_alarm/coordinator.py Co-authored-by: Josef Zweck <josef@zweck.dev> * Update homeassistant/components/bosch_alarm/alarm_control_panel.py Co-authored-by: Josef Zweck <josef@zweck.dev> * Update homeassistant/components/bosch_alarm/alarm_control_panel.py Co-authored-by: Josef Zweck <josef@zweck.dev> * Update homeassistant/components/bosch_alarm/alarm_control_panel.py Co-authored-by: Josef Zweck <josef@zweck.dev> * Add snapshot test * add snapshot test * add snapshot test * update quality scale * update quality scale * update quality scale * update quality scale * Apply suggestions from code review Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * apply changes from code review * apply changes from code review * apply changes from code review * Apply suggestions from code review Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * apply changes from code review * apply changes from code review * Fix alarm control panel device name * Fix * Fix * Fix * Fix --------- Co-authored-by: Josef Zweck <josef@zweck.dev> Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
166 lines
4.9 KiB
Python
166 lines
4.9 KiB
Python
"""Config flow for Bosch Alarm integration."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import logging
|
|
import ssl
|
|
from typing import Any
|
|
|
|
from bosch_alarm_mode2 import Panel
|
|
import voluptuous as vol
|
|
|
|
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
|
from homeassistant.const import (
|
|
CONF_CODE,
|
|
CONF_HOST,
|
|
CONF_MODEL,
|
|
CONF_PASSWORD,
|
|
CONF_PORT,
|
|
)
|
|
import homeassistant.helpers.config_validation as cv
|
|
|
|
from .const import CONF_INSTALLER_CODE, CONF_USER_CODE, DOMAIN
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
STEP_USER_DATA_SCHEMA = vol.Schema(
|
|
{
|
|
vol.Required(CONF_HOST): str,
|
|
vol.Required(CONF_PORT, default=7700): cv.positive_int,
|
|
}
|
|
)
|
|
|
|
STEP_AUTH_DATA_SCHEMA_SOLUTION = vol.Schema(
|
|
{
|
|
vol.Required(CONF_USER_CODE): str,
|
|
}
|
|
)
|
|
|
|
STEP_AUTH_DATA_SCHEMA_AMAX = vol.Schema(
|
|
{
|
|
vol.Required(CONF_INSTALLER_CODE): str,
|
|
vol.Required(CONF_PASSWORD): str,
|
|
}
|
|
)
|
|
|
|
STEP_AUTH_DATA_SCHEMA_BG = vol.Schema(
|
|
{
|
|
vol.Required(CONF_PASSWORD): str,
|
|
}
|
|
)
|
|
|
|
STEP_INIT_DATA_SCHEMA = vol.Schema({vol.Optional(CONF_CODE): str})
|
|
|
|
|
|
async def try_connect(
|
|
data: dict[str, Any], load_selector: int = 0
|
|
) -> tuple[str, int | None]:
|
|
"""Validate the user input allows us to connect.
|
|
|
|
Data has the keys from STEP_USER_DATA_SCHEMA with values provided by the user.
|
|
"""
|
|
panel = Panel(
|
|
host=data[CONF_HOST],
|
|
port=data[CONF_PORT],
|
|
automation_code=data.get(CONF_PASSWORD),
|
|
installer_or_user_code=data.get(CONF_INSTALLER_CODE, data.get(CONF_USER_CODE)),
|
|
)
|
|
|
|
try:
|
|
await panel.connect(load_selector)
|
|
finally:
|
|
await panel.disconnect()
|
|
|
|
return (panel.model, panel.serial_number)
|
|
|
|
|
|
class BoschAlarmConfigFlow(ConfigFlow, domain=DOMAIN):
|
|
"""Handle a config flow for Bosch Alarm."""
|
|
|
|
def __init__(self) -> None:
|
|
"""Init config flow."""
|
|
|
|
self._data: dict[str, Any] = {}
|
|
|
|
async def async_step_user(
|
|
self, user_input: dict[str, Any] | None = None
|
|
) -> ConfigFlowResult:
|
|
"""Handle the initial step."""
|
|
errors: dict[str, str] = {}
|
|
|
|
if user_input is not None:
|
|
try:
|
|
# Use load_selector = 0 to fetch the panel model without authentication.
|
|
(model, serial) = await try_connect(user_input, 0)
|
|
except (
|
|
OSError,
|
|
ConnectionRefusedError,
|
|
ssl.SSLError,
|
|
asyncio.exceptions.TimeoutError,
|
|
) as e:
|
|
_LOGGER.error("Connection Error: %s", e)
|
|
errors["base"] = "cannot_connect"
|
|
except Exception:
|
|
_LOGGER.exception("Unexpected exception")
|
|
errors["base"] = "unknown"
|
|
else:
|
|
self._data = user_input
|
|
self._data[CONF_MODEL] = model
|
|
return await self.async_step_auth()
|
|
return self.async_show_form(
|
|
step_id="user",
|
|
data_schema=self.add_suggested_values_to_schema(
|
|
STEP_USER_DATA_SCHEMA, user_input
|
|
),
|
|
errors=errors,
|
|
)
|
|
|
|
async def async_step_auth(
|
|
self, user_input: dict[str, Any] | None = None
|
|
) -> ConfigFlowResult:
|
|
"""Handle the auth step."""
|
|
errors: dict[str, str] = {}
|
|
|
|
# Each model variant requires a different authentication flow
|
|
if "Solution" in self._data[CONF_MODEL]:
|
|
schema = STEP_AUTH_DATA_SCHEMA_SOLUTION
|
|
elif "AMAX" in self._data[CONF_MODEL]:
|
|
schema = STEP_AUTH_DATA_SCHEMA_AMAX
|
|
else:
|
|
schema = STEP_AUTH_DATA_SCHEMA_BG
|
|
|
|
if user_input is not None:
|
|
self._data.update(user_input)
|
|
try:
|
|
(model, serial_number) = await try_connect(
|
|
self._data, Panel.LOAD_EXTENDED_INFO
|
|
)
|
|
except (PermissionError, ValueError) as e:
|
|
errors["base"] = "invalid_auth"
|
|
_LOGGER.error("Authentication Error: %s", e)
|
|
except (
|
|
OSError,
|
|
ConnectionRefusedError,
|
|
ssl.SSLError,
|
|
TimeoutError,
|
|
) as e:
|
|
_LOGGER.error("Connection Error: %s", e)
|
|
errors["base"] = "cannot_connect"
|
|
except Exception:
|
|
_LOGGER.exception("Unexpected exception")
|
|
errors["base"] = "unknown"
|
|
else:
|
|
if serial_number:
|
|
await self.async_set_unique_id(str(serial_number))
|
|
self._abort_if_unique_id_configured()
|
|
else:
|
|
self._async_abort_entries_match({CONF_HOST: self._data[CONF_HOST]})
|
|
return self.async_create_entry(title=f"Bosch {model}", data=self._data)
|
|
|
|
return self.async_show_form(
|
|
step_id="auth",
|
|
data_schema=self.add_suggested_values_to_schema(schema, user_input),
|
|
errors=errors,
|
|
)
|