Skip to content

Commit

Permalink
Merge pull request #3 from unii-security/bypass_input
Browse files Browse the repository at this point in the history
Add bypass inputs and arm sections
  • Loading branch information
rrooggiieerr authored Jul 5, 2024
2 parents 9ac93a0 + 8b4f9d9 commit 3a1694f
Show file tree
Hide file tree
Showing 10 changed files with 775 additions and 160 deletions.
17 changes: 12 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,14 @@ This integration is still in **beta** and **subject to change**. We're looking f
## Features

- Status inputs (clear, open, tamper, masking)
- Status sections (armed, disarmed, alarm)
- (Un)bypassing inputs
- Status sections (armed, disarmed, alarm, exit timer, entry timer)
- Connection status UNii panel

Section in alarm and fast input status update are only available for firmware version 2.17.x and above

Extra features (arming/disarming, (un)bypassing, outputs and event handling) are added shortly.

## Update
After installing the latest version of the integration the previous version must be removed and readded to fix encryption key issues.

## Hardware

Tested with the UNii 32, 128 and 512. No additional UNii license needed.
Expand Down Expand Up @@ -69,7 +67,7 @@ Or follow these instructions:
`config/custom_components/` directory of your Home Assistant installation
- Restart Home Assistant

## Adding a new Alphatronics UNii to Home Assistant
## Adding a new Alphatronics UNii to Home Assistant

If your UNii is on the same network as your Home Assistant server and is assigned an IP address using DHCP your UNii will most probably be automatically discovered by the integration.

Expand All @@ -82,6 +80,15 @@ In case your UNii is not automatically discovered follow these instructions:

A new UNii integration and device will now be added to your Integrations view.

## Write access to the UNii

By default the UNii integration is set to read only mode, only the status of your Unii will be displayed.
To enable write access and be able to (un)bypass inputs set an user code in the UNii integration options by going to the UNii device and clicking **Configure**. By removing the user code the UNii integration is back in read only mode.

It is recommended to use a dedicated user in your UNii which is assigned only those permissions that are needed for your scenarios.

---

All UNii trademarks, logos and brand names are registered and the property of Alphatronics BV

[hacs]: https://hacs.xyz/
65 changes: 60 additions & 5 deletions custom_components/unii/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,20 @@
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from unii import UNii, UNiiCommand, UNiiData, UNiiEncryptionError, UNiiLocal

from .const import CONF_SHARED_KEY, CONF_TYPE_LOCAL, DOMAIN
from .const import CONF_SHARED_KEY, CONF_TYPE_LOCAL, CONF_USER_CODE, DOMAIN

_LOGGER = logging.getLogger(__name__)

PLATFORMS: list[Platform] = [
Platform.ALARM_CONTROL_PANEL,
# Platform.ALARM_CONTROL_PANEL,
Platform.BINARY_SENSOR,
Platform.SENSOR,
# Platform.SWITCH,
Platform.SWITCH,
# Platform.SELECT,
# Platform.NUMBER,
]
# These platforms need to be reloaded when switching between read-only and writable mode.
RW_PLATFORMS: list[Platform] = [Platform.SWITCH]


class UNiiCoordinator(DataUpdateCoordinator):
Expand All @@ -42,7 +44,13 @@ class UNiiCoordinator(DataUpdateCoordinator):
unii: UNii = None
device_info: DeviceInfo = None

def __init__(self, hass: HomeAssistant, unii: UNii, config_entry: ConfigEntry):
def __init__(
self,
hass: HomeAssistant,
unii: UNii,
config_entry: ConfigEntry,
user_code: str | None = None,
):
"""Initialize Alphatronics UNii Data Update Coordinator."""

super().__init__(
Expand All @@ -55,6 +63,7 @@ def __init__(self, hass: HomeAssistant, unii: UNii, config_entry: ConfigEntry):
self.unii = unii
self.config_entry = config_entry
self.config_entry_id = config_entry.entry_id
self.user_code = user_code

identifiers = {(DOMAIN, config_entry.entry_id)}
connections = set()
Expand Down Expand Up @@ -97,6 +106,26 @@ def __init__(self, hass: HomeAssistant, unii: UNii, config_entry: ConfigEntry):

self.unii.add_event_occurred_callback(self.event_occurred_callback)

async def set_user_code(self, user_code: str):
# If the configuration changes between read-only and writable the device needs to be
# reloaded to create/disable entities
reload = False
if (self.user_code is None) ^ (user_code is None):
reload = True

self.user_code = user_code

if reload:
await self.hass.config_entries.async_unload_platforms(
self.config_entry, RW_PLATFORMS
)
await self.hass.config_entries.async_forward_entry_setups(
self.config_entry, RW_PLATFORMS
)

def can_write(self) -> bool:
return self.user_code is not None

async def async_disconnect(self):
"""
Disconnect from UNii.
Expand Down Expand Up @@ -140,6 +169,18 @@ def event_occurred_callback(self, command: UNiiCommand, data: UNiiData):
async def _async_update_data(self):
"""Fetch data from Alphatronics UNii."""

async def bypass_input(self, number: int) -> bool:
return await self.unii.bypass_input(number, self.user_code)

async def unbypass_input(self, number: int) -> bool:
return await self.unii.unbypass_input(number, self.user_code)

async def arm_section(self, number: int) -> bool:
return await self.unii.arm_section(number, self.user_code)

async def disarm_section(self, number: int) -> bool:
return await self.unii.disarm_section(number, self.user_code)


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Alphatronics UNii from a config entry."""
Expand Down Expand Up @@ -177,7 +218,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
)

# Setup coordinator
coordinator = UNiiCoordinator(hass, unii, entry)
user_code = entry.options.get(CONF_USER_CODE)
coordinator = UNiiCoordinator(hass, unii, entry, user_code)

# Fetch initial data so we have data when entities subscribe
await coordinator.async_config_entry_first_refresh()
Expand All @@ -186,6 +228,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

entry.async_on_unload(entry.add_update_listener(update_listener))

await coordinator.async_request_refresh()

return True
Expand All @@ -199,4 +243,15 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
hass.data[DOMAIN].pop(entry.entry_id)

# Wait a bit for the UNii to accept new connections after an integration reload.
await asyncio.sleep(1)

return unload_ok


async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Handle options update."""
_LOGGER.debug("Configuration options updated")
coordinator: UNiiCoordinator = hass.data[DOMAIN][entry.entry_id]

await coordinator.set_user_code(entry.options.get(CONF_USER_CODE))
Loading

0 comments on commit 3a1694f

Please sign in to comment.