Skip to content

Commit

Permalink
Merge pull request #181 from garnser/new_api
Browse files Browse the repository at this point in the history
Overhaul of API
  • Loading branch information
gjohansson-ST authored Nov 4, 2024
2 parents c297127 + 7f43abc commit 482af03
Show file tree
Hide file tree
Showing 14 changed files with 1,149 additions and 1,253 deletions.
96 changes: 15 additions & 81 deletions custom_components/sector/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,104 +2,38 @@
from __future__ import annotations

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_CODE, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr

from .const import (
CONF_CODE_FORMAT,
CONF_TEMP,
CONF_USERID,
DOMAIN,
LOGGER,
PLATFORMS,
UPDATE_INTERVAL,
)
from .const import DOMAIN, PLATFORMS
from .coordinator import SectorDataUpdateCoordinator


async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Migrate old entry."""
LOGGER.debug("Migrating from version %s", entry.version)

if entry.version == 1:
entry.version = 2

if entry.version == 2:
username = (
entry.data[CONF_USERNAME]
if entry.data.get(CONF_USERNAME)
else entry.data[CONF_USERID]
)
new_data2 = {
CONF_USERNAME: username,
CONF_PASSWORD: entry.data[CONF_PASSWORD],
CONF_TEMP: entry.data[CONF_TEMP],
}
new_options2 = {
CONF_CODE_FORMAT: entry.options.get(CONF_CODE_FORMAT, 6),
}
if success := hass.config_entries.async_update_entry(
entry,
data=new_data2,
options=new_options2,
title=username,
unique_id=username,
):
entry.version = 3
LOGGER.info("Migration to version %s successful", entry.version)
return success
return False


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Sector Alarm as config entry."""

"""Set up Sector Alarm from a config entry."""
coordinator = SectorDataUpdateCoordinator(hass, entry)
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator

await coordinator.async_config_entry_first_refresh()
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator

entry.async_on_unload(entry.add_update_listener(async_update_listener))

for key in coordinator.data:
device_registry = dr.async_get(hass)
device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
identifiers={(DOMAIN, f"sa_hub_{key}")},
manufacturer="Sector Alarm",
name="Sector Hub",
model="Hub",
sw_version="master",
)

if entry.options.get(CONF_CODE):
new_options = entry.options.copy()
new_options.pop(CONF_CODE)
hass.config_entries.async_update_entry(entry, options=new_options)
if entry.options.get(UPDATE_INTERVAL):
new_options = entry.options.copy()
new_options.pop(UPDATE_INTERVAL)
hass.config_entries.async_update_entry(entry, options=new_options)
if not entry.options.get(CONF_CODE_FORMAT):
new_options = entry.options.copy()
new_options[CONF_CODE_FORMAT] = 6
hass.config_entries.async_update_entry(entry, options=new_options)

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
# Schedule the platform setups to avoid blocking the event loop
hass.async_create_task(
hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
)

return True


async def async_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Update when config_entry options update."""
"""Handle options update."""
await hass.config_entries.async_reload(entry.entry_id)


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""

if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok
return False
"""Unload a Sector Alarm config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
if unload_ok:
coordinator: SectorDataUpdateCoordinator = hass.data[DOMAIN].pop(entry.entry_id)
# await coordinator.api.logout()
await coordinator.api.close()
return unload_ok
169 changes: 70 additions & 99 deletions custom_components/sector/alarm_control_panel.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
"""Adds Alarm Panel for Sector integration."""
"""Alarm Control Panel for Sector Alarm integration."""
from __future__ import annotations

import logging

from homeassistant.components.alarm_control_panel import (
AlarmControlPanelEntity,
AlarmControlPanelEntityFeature,
CodeFormat,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
Expand All @@ -13,126 +14,96 @@
STATE_ALARM_DISARMED,
STATE_ALARM_PENDING,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .const import DOMAIN
from .coordinator import SectorDataUpdateCoordinator

_LOGGER = logging.getLogger(__name__)

ALARM_STATE_TO_HA_STATE = {
3: STATE_ALARM_ARMED_AWAY,
2: STATE_ALARM_ARMED_HOME,
1: STATE_ALARM_DISARMED,
0: STATE_ALARM_PENDING,
}


async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up alarm panel from config entry."""

hass: HomeAssistant, entry: ConfigEntry, async_add_entities
):
"""Set up the Sector Alarm control panel."""
coordinator: SectorDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
async_add_entities([SectorAlarmPanel(coordinator, key) for key in coordinator.data])
async_add_entities([SectorAlarmControlPanel(coordinator)])


class SectorAlarmPanel(
CoordinatorEntity[SectorDataUpdateCoordinator], AlarmControlPanelEntity
):
"""Sector Alarm Panel."""
class SectorAlarmControlPanel(CoordinatorEntity, AlarmControlPanelEntity):
"""Representation of the Sector Alarm control panel."""

_attr_has_entity_name = True
_attr_supported_features = (
AlarmControlPanelEntityFeature.ARM_AWAY
| AlarmControlPanelEntityFeature.ARM_HOME
)

def __init__(
self,
coordinator: SectorDataUpdateCoordinator,
panel_id: str,
) -> None:
"""Initialize the Alarm panel."""
def __init__(self, coordinator: SectorDataUpdateCoordinator) -> None:
"""Initialize the control panel."""
super().__init__(coordinator)
self._panel_id = panel_id
self._displayname: str = self.coordinator.data[panel_id]["name"]
self._attr_unique_id = f"sa_panel_{panel_id}"
self._attr_changed_by = self.coordinator.data[panel_id]["changed_by"]
self._attr_supported_features = (
AlarmControlPanelEntityFeature.ARM_HOME
| AlarmControlPanelEntityFeature.ARM_AWAY
)
self._attr_code_arm_required = True
self._attr_code_format = CodeFormat.NUMBER
self._attr_state = ALARM_STATE_TO_HA_STATE[
self.coordinator.data[panel_id]["alarmstatus"]
]
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, f"sa_panel_{panel_id}")},
name=f"Sector Alarmpanel {panel_id}",
manufacturer="Sector Alarm",
model="Alarmpanel",
sw_version="master",
via_device=(DOMAIN, f"sa_hub_{panel_id}"),
)
panel_status = coordinator.data.get("panel_status", {})
self._serial_no = panel_status.get("SerialNo") or coordinator.entry.data.get("panel_id")
self._attr_unique_id = f"{self._serial_no}_alarm_panel"
self._attr_name = "Sector Alarm Panel"
_LOGGER.debug(f"Initialized alarm control panel with unique_id: {self._attr_unique_id}")

@property
def extra_state_attributes(self) -> dict:
"""Additional states for alarm panel."""
return {
"display_name": self._displayname,
}
def state(self):
"""Return the state of the device."""
status = self.coordinator.data.get("panel_status", {})
if status.get("IsOnline", False) is False:
return "offline"
# Get the status code from the panel data
status_code = status.get("Status", 0)
# Map the status code to the appropriate Home Assistant state
mapped_state = ALARM_STATE_TO_HA_STATE.get(status_code, STATE_ALARM_PENDING)
_LOGGER.debug(f"Alarm status_code: {status_code}, Mapped state: {mapped_state}")
return mapped_state

async def async_alarm_arm_away(self, code=None):
"""Send arm away command."""
success = await self.coordinator.api.arm_system("total")
if success:
await self.coordinator.async_request_refresh()

async def async_alarm_arm_home(self, code=None):
"""Send arm home command."""
success = await self.coordinator.api.arm_system("partial")
if success:
await self.coordinator.async_request_refresh()

async def async_alarm_disarm(self, code=None):
"""Send disarm command."""
success = await self.coordinator.api.disarm_system()
if success:
await self.coordinator.async_request_refresh()

async def async_alarm_arm_home(self, code: str | None = None) -> None:
"""Arm alarm home."""
command = "partial"
if code and len(code) == self.coordinator.data[self._panel_id]["codelength"]:
await self.coordinator.triggeralarm(
command, code=code, panel_id=self._panel_id
)
self._attr_state = STATE_ALARM_ARMED_HOME
if self.coordinator.logname:
self._attr_changed_by = self.coordinator.logname
self.async_write_ha_state()
return
raise HomeAssistantError("No code provided or incorrect length")

async def async_alarm_disarm(self, code: str | None = None) -> None:
"""Arm alarm off."""
command = "disarm"
if code and len(code) == self.coordinator.data[self._panel_id]["codelength"]:
await self.coordinator.triggeralarm(
command, code=code, panel_id=self._panel_id
)
self._attr_state = STATE_ALARM_DISARMED
if self.coordinator.logname:
self._attr_changed_by = self.coordinator.logname
self.async_write_ha_state()
return
raise HomeAssistantError("No code provided or incorrect length")

async def async_alarm_arm_away(self, code: str | None = None) -> None:
"""Arm alarm away."""
command = "full"
if code and len(code) == self.coordinator.data[self._panel_id]["codelength"]:
await self.coordinator.triggeralarm(
command, code=code, panel_id=self._panel_id
)
self._attr_state = STATE_ALARM_ARMED_AWAY
if self.coordinator.logname:
self._attr_changed_by = self.coordinator.logname
self.async_write_ha_state()
return
raise HomeAssistantError("No code provided or incorrect length")

@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
self._attr_changed_by = self.coordinator.data[self._panel_id].get("changed_by")
if alarm_state := self.coordinator.data[self._panel_id].get("alarmstatus"):
self._attr_state = ALARM_STATE_TO_HA_STATE[alarm_state]
super()._handle_coordinator_update()
@property
def device_info(self) -> DeviceInfo:
"""Return device info."""
return DeviceInfo(
identifiers={(DOMAIN, self._serial_no)},
name="Sector Alarm Panel",
manufacturer="Sector Alarm",
model="Alarm Panel",
)

@property
def available(self) -> bool:
"""Return entity available."""
"""Return entity availability."""
return True

@property
def extra_state_attributes(self):
"""Return the state attributes."""
return {
"serial_number": self._serial_no,
}
Loading

0 comments on commit 482af03

Please sign in to comment.