Skip to content
This repository has been archived by the owner on Aug 15, 2023. It is now read-only.

Commit

Permalink
Security code while arming and disarming is added
Browse files Browse the repository at this point in the history
  • Loading branch information
gunkutzeybek committed Jul 29, 2022
1 parent f2c0ca2 commit 4c20e09
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 13 deletions.
43 changes: 38 additions & 5 deletions custom_components/hikvision_axpro/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,20 @@

from homeassistant.components.alarm_control_panel import SCAN_INTERVAL
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD, Platform
from homeassistant.const import (
ATTR_CODE_FORMAT,
CONF_ENABLED,
CONF_HOST,
CONF_USERNAME,
CONF_PASSWORD,
CONF_CODE,
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

from .const import DATA_COORDINATOR, DOMAIN
from .const import DATA_COORDINATOR, DOMAIN, USE_CODE_ARMING

PLATFORMS: list[Platform] = [Platform.ALARM_CONTROL_PANEL]
_LOGGER = logging.getLogger(__name__)
Expand All @@ -23,6 +31,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
host = entry.data[CONF_HOST]
username = entry.data[CONF_USERNAME]
password = entry.data[CONF_PASSWORD]
use_code = entry.data[CONF_ENABLED]
code_format = entry.data[ATTR_CODE_FORMAT]
code = entry.data[CONF_CODE]
use_code_arming = entry.data[USE_CODE_ARMING]
axpro = hikaxpro.HikAxPro(host, username, password)

try:
Expand All @@ -31,7 +43,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
except (asyncio.TimeoutError, ConnectionError) as ex:
raise ConfigEntryNotReady from ex

coordinator = HikAxProDataUpdateCoordinator(hass, axpro, mac)
coordinator = HikAxProDataUpdateCoordinator(
hass,
axpro,
mac,
use_code,
code_format,
use_code_arming,
code,
)
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = {DATA_COORDINATOR: coordinator}

Expand All @@ -51,11 +71,24 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
class HikAxProDataUpdateCoordinator(DataUpdateCoordinator):
"""Class to manage fetching ax pro data."""

def __init__(self, hass, axpro, mac):
def __init__(
self,
hass,
axpro,
mac,
use_code,
code_format,
use_code_arming,
code,
):
self.axpro = axpro
self.state = None
self.host = axpro.host
self.mac = mac
self.use_code = use_code
self.code_format = code_format
self.use_code_arming = use_code_arming
self.code = code

super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=SCAN_INTERVAL)

Expand Down Expand Up @@ -96,4 +129,4 @@ async def async_disarm(self):

if is_success:
await self._async_update_data()
await self.async_request_refresh()
await self.async_request_refresh()
35 changes: 35 additions & 0 deletions custom_components/hikvision_axpro/alarm_control_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.components.alarm_control_panel import (
AlarmControlPanelEntity,
AlarmControlPanelEntityFeature,
CodeFormat,
)

from .const import DATA_COORDINATOR, DOMAIN
Expand Down Expand Up @@ -58,14 +60,47 @@ def state(self):
"""Return the state of the device."""
return self.coordinator.state

@property
def code_format(self) -> CodeFormat | None:
"""Return the code format."""
return self.__get_code_format(self.coordinator.code_format)

def __get_code_format(self, code_format_str) -> CodeFormat:
"""Returns CodeFormat according to the given code fomrat string."""
code_format: CodeFormat = None

if not self.coordinator.use_code:
code_format = None
elif code_format_str == "NUMBER":
code_format = CodeFormat.NUMBER
elif code_format_str == "TEXT":
code_format = CodeFormat.TEXT

return code_format

async def async_alarm_disarm(self, code=None):
"""Send disarm command."""
if self.coordinator.use_code:
if not self.__is_code_valid(code):
return

await self.coordinator.async_disarm()

async def async_alarm_arm_home(self, code=None):
"""Send arm home command."""
if self.coordinator.use_code and self.coordinator.use_code_arming:
if not self.__is_code_valid(code):
return

await self.coordinator.async_arm_home()

async def async_alarm_arm_away(self, code=None):
"""Send arm away command."""
if self.coordinator.use_code and self.coordinator.use_code_arming:
if not self.__is_code_valid(code):
return

await self.coordinator.async_arm_away()

def __is_code_valid(self, code):
return code == self.coordinator.code
44 changes: 40 additions & 4 deletions custom_components/hikvision_axpro/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,16 @@
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResult
from homeassistant.exceptions import HomeAssistantError
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.const import (
CONF_CODE,
CONF_ENABLED,
ATTR_CODE_FORMAT,
CONF_HOST,
CONF_USERNAME,
CONF_PASSWORD,
)

from .const import DOMAIN
from .const import DOMAIN, USE_CODE_ARMING

_LOGGER = logging.getLogger(__name__)

Expand All @@ -21,6 +28,10 @@
vol.Required(CONF_HOST): str,
vol.Required(CONF_USERNAME): str,
vol.Required(CONF_PASSWORD): str,
vol.Required(CONF_ENABLED, default=False): bool,
vol.Optional(ATTR_CODE_FORMAT, default="NUMBER"): vol.In(["TEXT", "NUMBER"]),
vol.Optional(CONF_CODE, default=""): str,
vol.Optional(USE_CODE_ARMING, default=False): bool,
}
)

Expand All @@ -39,7 +50,7 @@ def __init__(

async def authenticate(self) -> bool:
"""Check the provided credentials by connecting to ax pro."""
is_connect_success = self.axpro.connect()
is_connect_success = await self.hass.async_add_executor_job(self.axpro.connect)
return is_connect_success


Expand All @@ -49,9 +60,22 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str,
Data has the keys from STEP_USER_DATA_SCHEMA with values provided by the user.
"""

if data[CONF_ENABLED]:
if data[ATTR_CODE_FORMAT] is None or (
data[ATTR_CODE_FORMAT] != "NUMBER" and data[ATTR_CODE_FORMAT] != "TEXT"
):
raise InvalidCodeFormat

if (
data[CONF_CODE] is None
or data[CONF_CODE] == ""
or (data[ATTR_CODE_FORMAT] == "NUMBER" and not str.isdigit(data[CONF_CODE]))
):
raise InvalidCode

hub = AxProHub(data[CONF_HOST], data[CONF_USERNAME], data[CONF_PASSWORD], hass)

if not await hass.async_add_executor_job(hub.authenticate):
if not await hub.authenticate():
raise InvalidAuth

return {"title": f"Hikvision_axpro_{data['host']}"}
Expand All @@ -77,6 +101,10 @@ async def async_step_user(self, user_input=None) -> FlowResult:
errors["base"] = "cannot_connect"
except InvalidAuth:
errors["base"] = "invalid_auth"
except InvalidCodeFormat:
errors["base"] = "invalid_code_format"
except InvalidCode:
errors["base"] = "invalid_code"
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
Expand All @@ -94,3 +122,11 @@ class CannotConnect(HomeAssistantError):

class InvalidAuth(HomeAssistantError):
"""Error to indicate there is invalid auth."""


class InvalidCodeFormat(HomeAssistantError):
"""Error to indicate code format is wrong."""


class InvalidCode(HomeAssistantError):
"""Error to indicate the code is in wrong format"""
9 changes: 7 additions & 2 deletions custom_components/hikvision_axpro/const.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
"""Constants for the hikvision_axpro integration."""

DOMAIN = "hikvision_axpro"
from typing import Final

DATA_COORDINATOR = "hikaxpro"

DOMAIN: Final = "hikvision_axpro"

DATA_COORDINATOR: Final = "hikaxpro"

USE_CODE_ARMING: Final = "use_code_arming"
2 changes: 1 addition & 1 deletion custom_components/hikvision_axpro/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@
"@gunkutzeybek"
],
"iot_class": "local_polling",
"version": "0.0.1"
"version": "0.3.0"
}
28 changes: 28 additions & 0 deletions custom_components/hikvision_axpro/strings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"config": {
"step": {
"user": {
"data": {
"host": "[%key:common::config_flow::data::host%]",
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]",
"enabled": "[%key:common::config_flow::data::enabled%]",
"code_format": "[%key:common::config_flow::data::code_format%]",
"use_code_arming": "[%key:common::config_flow::data::use_code_arming%]",
"code": "[%key:common::config_flow::data::code%]"
}
}
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"invalid_code": "[%key:common::config_flow::error::invalid_code%]",
"invalid_code_format": "[%key:common::config_flow::error::invalid_code_format%]",
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
}
}
}

8 changes: 7 additions & 1 deletion custom_components/hikvision_axpro/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,20 @@
"error": {
"cannot_connect": "Failed to connect",
"invalid_auth": "Invalid authentication",
"invalid_code": "Inavlid code",
"invalid_code_format": "Invalid code format. Code format can take only NUMBER or TEXT as value.",
"unknown": "Unexpected error"
},
"step": {
"user": {
"data": {
"host": "Host",
"password": "Password",
"username": "Username"
"username": "Username",
"enabled": "Use code to disarm",
"code_format": "Code format (TEXT or NUMBER)",
"use_code_arming": "Use code for arming",
"code": "Code"
}
}
}
Expand Down

0 comments on commit 4c20e09

Please sign in to comment.