Skip to content
This repository has been archived by the owner on Jul 7, 2022. It is now read-only.

Commit

Permalink
new version of config_entry with migration and fix for #45
Browse files Browse the repository at this point in the history
  • Loading branch information
eavanvalkenburg committed May 8, 2021
1 parent bbe80ec commit 5845a53
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 81 deletions.
19 changes: 18 additions & 1 deletion custom_components/sia/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
"""The sia integration."""
import asyncio
import logging

from homeassistant.components.alarm_control_panel import (
DOMAIN as ALARM_CONTROL_PANEL_DOMAIN,
)
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PORT
from homeassistant.const import CONF_PORT, CONF_PROTOCOL
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady

Expand All @@ -16,6 +17,8 @@

PLATFORMS = [ALARM_CONTROL_PANEL_DOMAIN, BINARY_SENSOR_DOMAIN, SENSOR_DOMAIN]

_LOGGER = logging.getLogger(__name__)


async def async_setup(hass: HomeAssistant, config: dict):
"""Set up the sia component."""
Expand Down Expand Up @@ -57,3 +60,17 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
hub: SIAHub = hass.data[DOMAIN].pop(entry.entry_id)
await hub.async_shutdown()
return unload_ok


async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry):
"""Migrate old entry."""
_LOGGER.debug("Migrating from version %s", config_entry.version)

if config_entry.version == 1:
data = config_entry.data.copy()
data[CONF_PROTOCOL] = "TCP"
config_entry.version = 2
hass.config_entries.async_update_entry(config_entry, data=data)

_LOGGER.info("Migration to version %s successful", config_entry.version)
return True
137 changes: 66 additions & 71 deletions custom_components/sia/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
"""Config flow for sia integration."""
import logging

import voluptuous as vol
from homeassistant import config_entries, exceptions
from homeassistant.const import CONF_PORT
from homeassistant.data_entry_flow import AbortFlow
from pysiaalarm import (
InvalidAccountFormatError,
InvalidAccountLengthError,
InvalidKeyFormatError,
InvalidKeyLengthError,
SIAAccount,
)
import voluptuous as vol

from homeassistant import config_entries, exceptions
from homeassistant.const import CONF_PORT, CONF_PROTOCOL
from homeassistant.data_entry_flow import AbortFlow

from .const import (
CONF_ACCOUNT,
CONF_ACCOUNTS,
CONF_ADDITIONAL_ACCOUNTS,
CONF_ENCRYPTION_KEY,
CONF_IGNORE_TIMESTAMPS,
CONF_PING_INTERVAL,
CONF_ZONES,
DOMAIN,
Expand All @@ -29,10 +31,12 @@
HUB_SCHEMA = vol.Schema(
{
vol.Required(CONF_PORT): int,
vol.Optional(CONF_PROTOCOL, default="TCP"): vol.In(["TCP", "UDP"]),
vol.Required(CONF_ACCOUNT): str,
vol.Optional(CONF_ENCRYPTION_KEY): str,
vol.Required(CONF_PING_INTERVAL, default=1): int,
vol.Required(CONF_ZONES, default=1): int,
vol.Required(CONF_IGNORE_TIMESTAMPS, default=False): bool,
vol.Optional(CONF_ADDITIONAL_ACCOUNTS, default=False): bool,
}
)
Expand All @@ -43,6 +47,7 @@
vol.Optional(CONF_ENCRYPTION_KEY): str,
vol.Required(CONF_PING_INTERVAL, default=1): int,
vol.Required(CONF_ZONES, default=1): int,
vol.Required(CONF_IGNORE_TIMESTAMPS, default=False): bool,
vol.Optional(CONF_ADDITIONAL_ACCOUNTS, default=False): bool,
}
)
Expand All @@ -55,105 +60,95 @@ def validate_input(data: dict):
try:
ping = int(data[CONF_PING_INTERVAL])
assert 1 <= ping <= 1440
except AssertionError:
raise InvalidPing
except AssertionError as invalid_ping:
raise InvalidPing from invalid_ping
try:
zones = int(data[CONF_ZONES])
assert zones > 0
except AssertionError:
raise InvalidZones
except AssertionError as invalid_zone:
raise InvalidZones from invalid_zone


class SIAConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for sia."""

VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH
data = None
VERSION = 2

def __init__(self):
"""Initialize the config flow."""
self._data = {}

async def async_step_add_account(self, user_input: dict = None):
"""Handle the additional accounts steps."""
errors = {}
if user_input is not None:
try:
validate_input(user_input)
self.update_data(user_input)
if user_input[CONF_ADDITIONAL_ACCOUNTS]:
return await self.async_step_add_account()
except InvalidKeyFormatError:
errors["base"] = "invalid_key_format"
except InvalidKeyLengthError:
errors["base"] = "invalid_key_length"
except InvalidAccountFormatError:
errors["base"] = "invalid_account_format"
except InvalidAccountLengthError:
errors["base"] = "invalid_account_length"
except InvalidPing:
errors["base"] = "invalid_ping"
except InvalidZones:
errors["base"] = "invalid_zones"

return self.async_show_form(
step_id="user",
data_schema=ACCOUNT_SCHEMA,
errors=errors,
)
if user_input is None:
return self.async_show_form(
step_id="user",
data_schema=ACCOUNT_SCHEMA,
errors={},
)

async def async_step_user(self, user_input: dict = None):
"""Handle the initial step."""
if user_input is None:
return self.async_show_form(
step_id="user", data_schema=HUB_SCHEMA, errors={}
)
errors = {}
if user_input is not None:
try:
validate_input(user_input)
await self.async_set_unique_id(f"{DOMAIN}_{self.data[CONF_PORT]}")
self._abort_if_unique_id_configured()
self.update_data(user_input)
if not user_input[CONF_ADDITIONAL_ACCOUNTS]:
return self.async_create_entry(
title=f"SIA Alarm on port {self.data[CONF_PORT]}",
data=self.data,
)
return await self.async_step_add_account()
except InvalidKeyFormatError:
errors["base"] = "invalid_key_format"
except InvalidKeyLengthError:
errors["base"] = "invalid_key_length"
except InvalidAccountFormatError:
errors["base"] = "invalid_account_format"
except InvalidAccountLengthError:
errors["base"] = "invalid_account_length"
except InvalidPing:
errors["base"] = "invalid_ping"
except InvalidZones:
errors["base"] = "invalid_zones"
except AbortFlow:
return self.async_abort(reason="already_configured")
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"

return self.async_show_form(
step_id="user", data_schema=HUB_SCHEMA, errors=errors
try:
validate_input(user_input)
except InvalidKeyFormatError:
errors["base"] = "invalid_key_format"
except InvalidKeyLengthError:
errors["base"] = "invalid_key_length"
except InvalidAccountFormatError:
errors["base"] = "invalid_account_format"
except InvalidAccountLengthError:
errors["base"] = "invalid_account_length"
except InvalidPing:
errors["base"] = "invalid_ping"
except InvalidZones:
errors["base"] = "invalid_zones"
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
if errors:
return self.async_show_form(
step_id="user", data_schema=HUB_SCHEMA, errors=errors
)
self.update_data(user_input)
await self.async_set_unique_id(f"{DOMAIN}_{self._data[CONF_PORT]}")
try:
self._abort_if_unique_id_configured()
except AbortFlow:
return self.async_abort(reason="already_configured")

if user_input[CONF_ADDITIONAL_ACCOUNTS]:
return await self.async_step_add_account()
return self.async_create_entry(
title=f"SIA Alarm on port {self._data[CONF_PORT]}",
data=self._data,
)

def update_data(self, user_input):
"""Parse the user_input and store in self.data."""
if not self.data:
self.data = {
if not self._data:
self._data = {
CONF_PORT: user_input[CONF_PORT],
CONF_PROTOCOL: user_input[CONF_PROTOCOL],
CONF_ACCOUNTS: [
{
CONF_ACCOUNT: user_input[CONF_ACCOUNT],
CONF_ENCRYPTION_KEY: user_input.get(CONF_ENCRYPTION_KEY),
CONF_PING_INTERVAL: user_input[CONF_PING_INTERVAL],
CONF_ZONES: user_input[CONF_ZONES],
CONF_IGNORE_TIMESTAMPS: user_input[CONF_IGNORE_TIMESTAMPS],
}
],
}
else:
add_data = user_input.copy()
add_data.pop(CONF_ADDITIONAL_ACCOUNTS)
self.data[CONF_ACCOUNTS].append(add_data)
self._data[CONF_ACCOUNTS].append(add_data)


class InvalidPing(exceptions.HomeAssistantError):
Expand Down
1 change: 1 addition & 0 deletions custom_components/sia/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
CONF_PING_INTERVAL = "ping_interval"
CONF_ENCRYPTION_KEY = "encryption_key"
CONF_ZONES = "zones"
CONF_IGNORE_TIMESTAMPS = "ignore_timestamps"

DOMAIN = "sia"
DATA_UPDATED = f"{DOMAIN}_data_updated"
Expand Down
5 changes: 3 additions & 2 deletions custom_components/sia/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/sia",
"requirements": [
"pysiaalarm==3.0.0b6"
"pysiaalarm==3.0.0b12"
],
"codeowners": [
"@eavanvalkenburg"
],
"version": "0.5.0b5"
"version": "0.5.0b6",
"iot_class": "local_push"
}
26 changes: 19 additions & 7 deletions custom_components/sia/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,27 @@
"step": {
"user": {
"data": {
"name": "Name",
"port": "Port",
"account": "Account",
"port": "[%key:common::config_flow::data::port%]",
"protocol": "Protocol",
"account": "Account ID",
"encryption_key": "Encryption Key",
"ping_interval": "Ping Interval (min)",
"zones": "Number of zones for the account",
"additional_account": "Add more accounts?"
"ignore_timestamps": "Ignore the timestamp check",
"additional_account": "Additional accounts"
},
"title": "Create a connection for SIA DC-09 based alarm systems."
"title": "Create a connection for SIA based alarm systems."
},
"additional_account": {
"data": {
"account": "[%key:component::sia::config::step::user::data::account%]",
"encryption_key": "[%key:component::sia::config::step::user::data::encryption_key%]",
"ping_interval": "[%key:component::sia::config::step::user::data::ping_interval%]",
"zones": "[%key:component::sia::config::step::user::data::zones%]",
"ignore_timestamps": "[%key:component::sia::config::step::user::data::ignore_timestamps%]",
"additional_account": "[%key:component::sia::config::step::user::data::additional_account%]"
},
"title": "Add another account to the current port."
}
},
"error": {
Expand All @@ -22,10 +34,10 @@
"invalid_account_length": "The account is not the right length, it has to be between 3 and 16 characters.",
"invalid_ping": "The ping interval needs to be between 1 and 1440 minutes.",
"invalid_zones": "There needs to be at least 1 zone.",
"unknown": "Unexpected error"
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"abort": {
"already_configured": "This SIA Port is already used, please select another or recreate the existing with an extra account."
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
}
}
}
12 changes: 12 additions & 0 deletions custom_components/sia/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,21 @@
"encryption_key": "Encryption Key",
"ping_interval": "Ping Interval (min)",
"zones": "Number of zones for the account",
"ignore_timestamps": "Ignore the timestamp check",
"additional_account": "Add more accounts?"
},
"title": "Create a connection for SIA DC-09 based alarm systems."
},
"additional_account": {
"data": {
"account": "Account",
"encryption_key": "Encryption Key",
"ping_interval": "Ping Interval (min)",
"zones": "Number of zones for the account",
"ignore_timestamps": "Ignore the timestamp check",
"additional_account": "Add more accounts?"
},
"title": "Add another account to the current port."
}
}
},
Expand Down

0 comments on commit 5845a53

Please sign in to comment.