Elk types 4 (#70305)

This commit is contained in:
Glenn Waters 2022-04-19 22:45:45 -04:00 committed by GitHub
parent a1f33a093c
commit 1492ea1fe3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 74 additions and 43 deletions

View file

@ -91,6 +91,7 @@ homeassistant.components.dunehd.*
homeassistant.components.efergy.*
homeassistant.components.elgato.*
homeassistant.components.elkm1.__init__
homeassistant.components.elkm1.alarm_control_panel
homeassistant.components.elkm1.climate
homeassistant.components.elkm1.discovery
homeassistant.components.elkm1.light

View file

@ -486,7 +486,7 @@ class ElkEntity(Entity):
"""Is the entity available to be updated."""
return self._elk.is_connected()
def initial_attrs(self) -> dict[str, int]:
def initial_attrs(self) -> dict[str, Any]:
"""Return the underlying element's attributes as a dict."""
attrs = {}
attrs["index"] = self._element.index + 1

View file

@ -1,7 +1,13 @@
"""Each ElkM1 area will be created as a separate alarm_control_panel."""
from __future__ import annotations
from typing import Any
from elkm1_lib.areas import Area
from elkm1_lib.const import AlarmState, ArmedStatus, ArmLevel, ArmUpState
from elkm1_lib.elements import Element
from elkm1_lib.elk import Elk
from elkm1_lib.keypads import Keypad
import voluptuous as vol
from homeassistant.components.alarm_control_panel import (
@ -107,18 +113,19 @@ class ElkArea(ElkAttachedEntity, AlarmControlPanelEntity, RestoreEntity):
| AlarmControlPanelEntityFeature.ARM_AWAY
| AlarmControlPanelEntityFeature.ARM_NIGHT
)
_element: Area
def __init__(self, element, elk, elk_data):
def __init__(self, element: Element, elk: Elk, elk_data: dict[str, Any]) -> None:
"""Initialize Area as Alarm Control Panel."""
super().__init__(element, elk, elk_data)
self._elk = elk
self._changed_by_keypad = None
self._changed_by_time = None
self._changed_by_id = None
self._changed_by = None
self._state = None
self._changed_by_keypad: str | None = None
self._changed_by_time: str | None = None
self._changed_by_id: int | None = None
self._changed_by: str | None = None
self._state: str | None = None
async def async_added_to_hass(self):
async def async_added_to_hass(self) -> None:
"""Register callback for ElkM1 changes."""
await super().async_added_to_hass()
if len(self._elk.areas.elements) == 1:
@ -139,7 +146,8 @@ class ElkArea(ElkAttachedEntity, AlarmControlPanelEntity, RestoreEntity):
if ATTR_CHANGED_BY in last_state.attributes:
self._changed_by = last_state.attributes[ATTR_CHANGED_BY]
def _watch_keypad(self, keypad, changeset):
def _watch_keypad(self, keypad: Element, changeset: dict[str, Any]) -> None:
assert isinstance(keypad, Keypad)
if keypad.area != self._element.index:
return
if changeset.get("last_user") is not None:
@ -149,30 +157,30 @@ class ElkArea(ElkAttachedEntity, AlarmControlPanelEntity, RestoreEntity):
self._changed_by = self._elk.users.username(keypad.last_user)
self.async_write_ha_state()
def _watch_area(self, area, changeset):
def _watch_area(self, area: Element, changeset: dict[str, Any]) -> None:
if not (last_log := changeset.get("last_log")):
return
# user_number only set for arm/disarm logs
if not last_log.get("user_number"):
if (user_number := last_log.get("user_number")) is None:
return
self._changed_by_keypad = None
self._changed_by_id = last_log["user_number"]
self._changed_by = self._elk.users.username(self._changed_by_id - 1)
self._changed_by_id = user_number
self._changed_by = self._elk.users.username(user_number - 1)
self._changed_by_time = last_log["timestamp"]
self.async_write_ha_state()
@property
def code_format(self):
def code_format(self) -> CodeFormat | None:
"""Return the alarm code format."""
return CodeFormat.NUMBER
@property
def state(self):
def state(self) -> str | None:
"""Return the state of the element."""
return self._state
@property
def extra_state_attributes(self):
def extra_state_attributes(self) -> dict[str, Any]:
"""Attributes of the area."""
attrs = self.initial_attrs()
elmt = self._element
@ -191,11 +199,11 @@ class ElkArea(ElkAttachedEntity, AlarmControlPanelEntity, RestoreEntity):
return attrs
@property
def changed_by(self):
def changed_by(self) -> str | None:
"""Last change triggered by."""
return self._changed_by
def _element_changed(self, element, changeset):
def _element_changed(self, element: Element, changeset: dict[str, Any]) -> None:
elk_state_to_hass_state = {
ArmedStatus.DISARMED.value: STATE_ALARM_DISARMED,
ArmedStatus.ARMED_AWAY.value: STATE_ALARM_ARMED_AWAY,
@ -208,57 +216,68 @@ class ElkArea(ElkAttachedEntity, AlarmControlPanelEntity, RestoreEntity):
if self._element.alarm_state is None:
self._state = None
elif self._area_is_in_alarm_state():
elif self._element.alarm_state >= AlarmState.FIRE_ALARM.value:
# Area is in alarm state
self._state = STATE_ALARM_TRIGGERED
elif self._entry_exit_timer_is_running():
self._state = (
STATE_ALARM_ARMING if self._element.is_exit else STATE_ALARM_PENDING
)
else:
elif self._element.armed_status is not None:
self._state = elk_state_to_hass_state[self._element.armed_status]
else:
self._state = None
def _entry_exit_timer_is_running(self):
def _entry_exit_timer_is_running(self) -> bool:
return self._element.timer1 > 0 or self._element.timer2 > 0
def _area_is_in_alarm_state(self):
return self._element.alarm_state >= AlarmState.FIRE_ALARM.value
async def async_alarm_disarm(self, code=None):
async def async_alarm_disarm(self, code: str | None = None) -> None:
"""Send disarm command."""
self._element.disarm(int(code))
if code is not None:
self._element.disarm(int(code))
async def async_alarm_arm_home(self, code=None):
async def async_alarm_arm_home(self, code: str | None = None) -> None:
"""Send arm home command."""
self._element.arm(ArmLevel.ARMED_STAY.value, int(code))
if code is not None:
self._element.arm(ArmLevel.ARMED_STAY.value, int(code))
async def async_alarm_arm_away(self, code=None):
async def async_alarm_arm_away(self, code: str | None = None) -> None:
"""Send arm away command."""
self._element.arm(ArmLevel.ARMED_AWAY.value, int(code))
if code is not None:
self._element.arm(ArmLevel.ARMED_AWAY.value, int(code))
async def async_alarm_arm_night(self, code=None):
async def async_alarm_arm_night(self, code: str | None = None) -> None:
"""Send arm night command."""
self._element.arm(ArmLevel.ARMED_NIGHT.value, int(code))
if code is not None:
self._element.arm(ArmLevel.ARMED_NIGHT.value, int(code))
async def async_alarm_arm_home_instant(self, code=None):
async def async_alarm_arm_home_instant(self, code: str | None = None) -> None:
"""Send arm stay instant command."""
self._element.arm(ArmLevel.ARMED_STAY_INSTANT.value, int(code))
if code is not None:
self._element.arm(ArmLevel.ARMED_STAY_INSTANT.value, int(code))
async def async_alarm_arm_night_instant(self, code=None):
async def async_alarm_arm_night_instant(self, code: str | None = None) -> None:
"""Send arm night instant command."""
self._element.arm(ArmLevel.ARMED_NIGHT_INSTANT.value, int(code))
if code is not None:
self._element.arm(ArmLevel.ARMED_NIGHT_INSTANT.value, int(code))
async def async_alarm_arm_vacation(self, code=None):
async def async_alarm_arm_vacation(self, code: str | None = None) -> None:
"""Send arm vacation command."""
self._element.arm(ArmLevel.ARMED_VACATION.value, int(code))
if code is not None:
self._element.arm(ArmLevel.ARMED_VACATION.value, int(code))
async def async_display_message(self, clear, beep, timeout, line1, line2):
async def async_display_message(
self, clear: int, beep: bool, timeout: int, line1: str, line2: str
) -> None:
"""Display a message on all keypads for the area."""
self._element.display_message(clear, beep, timeout, line1, line2)
async def async_bypass(self, code=None):
async def async_bypass(self, code: str | None = None) -> None:
"""Bypass all zones in area."""
self._element.bypass(code)
if code is not None:
self._element.bypass(int(code))
async def async_clear_bypass(self, code=None):
async def async_clear_bypass(self, code: str | None = None) -> None:
"""Clear bypass for all zones in area."""
self._element.clear_bypass(code)
if code is not None:
self._element.clear_bypass(int(code))

View file

@ -803,6 +803,17 @@ no_implicit_optional = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.elkm1.alarm_control_panel]
check_untyped_defs = true
disallow_incomplete_defs = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_decorators = true
disallow_untyped_defs = true
no_implicit_optional = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.elkm1.climate]
check_untyped_defs = true
disallow_incomplete_defs = true