Skip to content

Commit

Permalink
Fixed issue where the integration would not set up
Browse files Browse the repository at this point in the history
  • Loading branch information
rainepretorius committed May 26, 2023
1 parent 8a6f498 commit eee215f
Show file tree
Hide file tree
Showing 13 changed files with 250 additions and 258 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ Binary sensors are used for simple alarm panel zones and sensors. The folowing a
3.) Window Sensors (BinarySensorDeviceClass.WINDOW)<br />
4.) Powered by AC (BinarySensorDeviceClass.PLUG)<br />
5.) Powered by Battery (BinarySensorDeviceClass.POWER)<br />
6.) Bypasss states of each zone on the panel. (On=bypassed, Off=active)<br />

# Alarm Control Panel #
There is an alarm control panel for each area enabled on your alarm panel. This allows you to set the state of each area individually. If you have a nemtek electric fence. It is currently coded to enable arming and disarming of the electric fence.<br />
Expand Down
39 changes: 31 additions & 8 deletions custom_components/olarm_sensors/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from .exceptions import DictionaryKeyError
import os
import voluptuous as vol
from homeassistant.exceptions import ConfigEntryNotReady


path = os.path.abspath(__file__).replace("__init__.py", "")
Expand All @@ -44,22 +45,25 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry):
"""
This function handles the setup of the Olarm integration. It creates a coordinator instance, registers services for each zone, and forwards the setup for binary sensors.
"""
# Updating and syncing options and integration data.
await update_listener(hass, config_entry)

# Getting the devices assosiated with the users account.
setup_api = OlarmSetupApi(api_key=config_entry.data[CONF_API_KEY])
devices = await setup_api.get_olarm_devices()
device_len_changed = False
try:
devices = await setup_api.get_olarm_devices()

except:
raise ConfigEntryNotReady(
"Could not connect to the Olarm Api to get the devices linked to your Olarm account"
)

if len(devices) > int(config_entry.data[OLARM_DEVICE_AMOUNT]):
LOGGER.warning(
"The amount of Olarm Devices linked to your profile changed. It was %s and is now %s. Please select the correct devices for this instance under options",
int(config_entry.data[OLARM_DEVICE_AMOUNT]),
len(devices),
)
device_len_changed = True

# Updating and syncing options and integration data.
await update_listener(hass, config_entry)

config_entry.async_on_unload(config_entry.add_update_listener(update_listener))

Expand Down Expand Up @@ -157,8 +161,24 @@ async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry):

async def update_listener(hass: HomeAssistant, config_entry):
"""Handle options update."""
setup_api = OlarmSetupApi(api_key=config_entry.data[CONF_API_KEY])
devices = await setup_api.get_olarm_devices()
try:
if not config_entry.options[CONF_API_KEY] == config_entry.data[CONF_API_KEY]:
data = {**config_entry.data}

data[CONF_API_KEY] = config_entry.options[CONF_API_KEY]

options = {**config_entry.options}

hass.config_entries.async_update_entry(
config_entry, data=data, options=options
)

except (DictionaryKeyError, KeyError):
data = {**config_entry.data}
options = {**config_entry.options}
options[CONF_API_KEY] = data[CONF_API_KEY]

hass.config_entries.async_update_entry(config_entry, data=data, options=options)

try:
if (
Expand Down Expand Up @@ -186,6 +206,9 @@ async def update_listener(hass: HomeAssistant, config_entry):

hass.config_entries.async_update_entry(config_entry, data=data, options=options)

# Getting all the devices.
setup_api = OlarmSetupApi(api_key=config_entry.data[CONF_API_KEY])
devices = await setup_api.get_olarm_devices()
try:
if (
not config_entry.options[CONF_OLARM_DEVICES]
Expand Down
37 changes: 11 additions & 26 deletions custom_components/olarm_sensors/alarm_control_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ async def async_setup_entry(
LOGGER.info("Setting up Alarm Panels for device (%s)", device["deviceName"])
coordinator = hass.data[DOMAIN][device["deviceId"]]

await coordinator.async_get_data()
if datetime.now() - coordinator.last_update > timedelta(seconds=30):
await coordinator.async_get_data()

for sensor in coordinator.panel_state:
LOGGER.info(
Expand Down Expand Up @@ -170,13 +171,21 @@ def available(self):
"""
Whether the entity is available. IE the coordinator updatees successfully.
"""
return self.coordinator.last_update > datetime.now() - timedelta(minutes=2) and self.coordinator.device_online
return (
self.coordinator.last_update > datetime.now() - timedelta(minutes=2)
and self.coordinator.device_online
)

@property
def last_changed(self) -> str | None:
"""Return the last change triggered by."""
return self._last_changed

@property
def should_poll(self):
"""Disable polling."""
return False

@property
def extra_state_attributes(self) -> dict | None:
"""
Expand Down Expand Up @@ -281,30 +290,6 @@ def _handle_coordinator_update(self) -> None:
except ListIndexError:
LOGGER.error("Could not set alarm panel state for %s", self.sensor_name)

try:
self._changed_by = self.coordinator.changed_by[self.area]

except ListIndexError:
LOGGER.debug("Could not set alarm panel changed by")

try:
self._last_changed = self.coordinator.last_changed[self.area]

except ListIndexError:
LOGGER.debug("Could not set alarm panel last changed")

try:
self._last_action = self.coordinator.last_action[self.area]

except ListIndexError:
LOGGER.debug("Could not set alarm panel last action")

try:
self._area_trigger = self.coordinator.area_triggers[self.area - 1]

except ListIndexError:
LOGGER.debug("Could not set alarm panel trigger")

super()._handle_coordinator_update()

def check_code(self, entered_code=None) -> bool:
Expand Down
182 changes: 25 additions & 157 deletions custom_components/olarm_sensors/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ async def async_setup_entry(
# Creating an instance of the DataCoordinator to update the data from Olarm.
coordinator = hass.data[DOMAIN][device["deviceId"]]

# Getting the first setup data from Olarm. eg: Panelstates, and all zones.
await coordinator.async_get_data()
# Getting the first setup data from Olarm. eg: Getting all the zones and their info.
if datetime.now() - coordinator.last_update > timedelta(seconds=30):
await coordinator.async_get_data()

LOGGER.info(
"Adding Olarm Zones Sensors for device (%s)", coordinator.olarm_device_name
"Adding Olarm Zone Sensors for device (%s)", coordinator.olarm_device_name
)

# Looping through the sensors/zones for the panel.
Expand All @@ -54,30 +55,8 @@ async def async_setup_entry(
"Added Olarm Zones Sensors for device (%s)", coordinator.olarm_device_name
)

LOGGER.info(
"Adding Olarm Zones Bypass Sensors for device (%s)",
coordinator.olarm_device_name,
)

for sensor1 in coordinator.bypass_state:
# Creating a bypass sensor for each zone on the alarm panel.
bypass_sensor = OlarmBypassSensor(
coordinator=coordinator,
sensor_name=sensor1["name"],
state=sensor1["state"],
index=sensor1["zone_number"],
last_changed=sensor1["last_changed"],
)

entities.append(bypass_sensor)

LOGGER.info(
"Added Olarm Zone Bypass Sensors for device (%s)",
coordinator.olarm_device_name,
)

async_add_entities(entities)
LOGGER.info("Added Olarm Zone and Bypass Sensors")
LOGGER.info("Added Olarm Zone Sensors")
return True


Expand Down Expand Up @@ -174,12 +153,20 @@ def __init__(
elif self._attr_device_class == BinarySensorDeviceClass.PLUG:
self.sensortypestring = "Device Power Plug Status"

async def async_update(self):
coordinator_update_success = await self.coordinator.update_data()
async def async_update(self) -> bool:
"""
Updates the state of the zone sensor from the coordinator.
Returns:
boolean: Whether tthe update worked.
"""
if datetime.now() - self.coordinator.last_update > timedelta(seconds=30):
# Only update the state from the api if it has been more than 30s since the last update.
await self.coordinator.async_update_sensor_data()

self._attr_is_on = self.coordinator.sensor_data[self.index]["state"] == "on"
self.last_changed = self.coordinator.sensor_data[self.index]["last_changed"]
self.async_write_ha_state()
return coordinator_update_success
return self.coordinator.last_update_success

async def async_added_to_hass(self):
"""
Expand Down Expand Up @@ -273,7 +260,10 @@ def available(self):
"""
Whether the entity is available. IE the coordinator updatees successfully.
"""
return self.coordinator.last_update > datetime.now() - timedelta(minutes=2) and self.coordinator.device_online
return (
self.coordinator.last_update > datetime.now() - timedelta(minutes=2)
and self.coordinator.device_online
)

@property
def state_attributes(self) -> dict | None:
Expand All @@ -283,133 +273,12 @@ def state_attributes(self) -> dict | None:
"last_tripped_time": self.last_changed,
"zone_number": self.index + 1,
"sensor_type": self.sensortypestring,
"coordinator_state": self.coordinator.sensor_data[self.index]['state'],
"boolean_check": self.coordinator.sensor_data[self.index]['state'] == "on"
}

@property
def device_info(self) -> dict:
"""Return device information about this entity."""
return {
"name": f"Olarm Sensors ({self.coordinator.olarm_device_name})",
"manufacturer": "Raine Pretorius",
"model": f"{self.coordinator.olarm_device_make}",
"identifiers": {(DOMAIN, self.coordinator.olarm_device_id)},
"sw_version": VERSION,
"hw_version": f"{self.coordinator.entry.data[CONF_DEVICE_FIRMWARE]}",
}

@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
self._attr_is_on = self.coordinator.sensor_data[self.index]["state"] == "on"
self.last_changed = self.coordinator.sensor_data[self.index]["last_changed"]
self.async_write_ha_state()
return self.coordinator.sensor_data[self.index]["state"] == "on"


class OlarmBypassSensor(BinarySensorEntity):
"""
This class represents a binary sensor entity in Home Assistant for an Olarm security zone's bypass state. It defines the sensor's state and attributes, and provides methods for updating them.
"""

index = 0

def __init__(
self,
coordinator: OlarmCoordinator,
sensor_name: str,
state: str,
index: int,
last_changed,
) -> None:
"""
Creates a sensor for each zone on the alarm panel.
(params):
coordinator (OlarmCoordinator): The Data Update Coordinator.
sensor_name (str): The name of the Sensor on the alarm panel.
state (str): The state of the sensor. (on or off)
index (int): The index in the coordinator's data list of the sensor's state.
"""
self.coordinator = coordinator
self.sensor_name = str(sensor_name) + " Bypass"
self.set_state = state
self._attr_is_on = self.set_state == "on"
self.index = index
self.last_changed = last_changed

async def async_added_to_hass(self):
"""
Writing the state of the sensor to Home Assistant
"""
self.async_on_remove(
self.coordinator.async_add_listener(self.async_write_ha_state)
)

async def async_update(self):
"""Handling the updated data / updating the data."""
coordinator_update_success = await self.coordinator.update_data()
self._attr_is_on = self.coordinator.bypass_state[self.index]["state"] == "on"
self.last_changed = self.coordinator.sensor_data[self.index]["last_changed"]
self.async_write_ha_state()
return coordinator_update_success

@property
def unique_id(self):
"""
The unique id for this entity sothat it can be managed from the ui.
"""
return f"{self.coordinator.olarm_device_id}_{self.sensor_name}".replace(
" ", ""
).lower()

@property
def name(self):
"""
The name of the zone from the ALarm Panel
"""
name = []
name1 = self.sensor_name.replace("_", " ")
for item in str(name1).lower().split(" "):
name.append(str(item).capitalize())

return " ".join(name) + " (" + self.coordinator.olarm_device_name + ")"

@property
def is_on(self):
"""
Whether the sensor/zone is bypassed or not.
"""
self._attr_is_on = self.coordinator.bypass_state[self.index]["state"] == "on"
return self._attr_is_on

@property
def icon(self):
"""
Setting the icon of the entity depending on the state of the zone.
"""
# Zone Bypass
if self.is_on:
return "mdi:shield-home-outline"

else:
return "mdi:shield-home"

@property
def available(self):
"""
Whether the entity is available. IE the coordinator updatees successfully.
"""
return self.coordinator.last_update > datetime.now() - timedelta(minutes=2) and self.coordinator.device_online

@property
def device_state_attributes(self):
"""
The last time the state of the zone/ sensor changed on Olarm's side.
"""
self.last_changed = self.coordinator.bypass_state[self.index]["last_changed"]
return {"last_tripped_time": self.last_changed, "zone_number": self.index + 1}
def should_poll(self):
"""Disable polling."""
return False

@property
def device_info(self) -> dict:
Expand All @@ -426,7 +295,6 @@ def device_info(self) -> dict:
@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
self._attr_is_on = self.coordinator.bypass_state[self.index]["state"] == "on"
self._attr_is_on = self.coordinator.sensor_data[self.index]["state"] == "on"
self.last_changed = self.coordinator.sensor_data[self.index]["last_changed"]
self.async_write_ha_state()

Loading

0 comments on commit eee215f

Please sign in to comment.