From 1492ea1fe311eca5b19ee7237258f6dffaa9e1c7 Mon Sep 17 00:00:00 2001 From: Glenn Waters Date: Tue, 19 Apr 2022 22:45:45 -0400 Subject: [PATCH] Elk types 4 (#70305) --- .strict-typing | 1 + homeassistant/components/elkm1/__init__.py | 2 +- .../components/elkm1/alarm_control_panel.py | 103 +++++++++++------- mypy.ini | 11 ++ 4 files changed, 74 insertions(+), 43 deletions(-) diff --git a/.strict-typing b/.strict-typing index 81994fd02c3..df61a23d17b 100644 --- a/.strict-typing +++ b/.strict-typing @@ -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 diff --git a/homeassistant/components/elkm1/__init__.py b/homeassistant/components/elkm1/__init__.py index 5d12690ffc0..22dd50499d6 100644 --- a/homeassistant/components/elkm1/__init__.py +++ b/homeassistant/components/elkm1/__init__.py @@ -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 diff --git a/homeassistant/components/elkm1/alarm_control_panel.py b/homeassistant/components/elkm1/alarm_control_panel.py index eb0653b5984..49aa6f68a09 100644 --- a/homeassistant/components/elkm1/alarm_control_panel.py +++ b/homeassistant/components/elkm1/alarm_control_panel.py @@ -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)) diff --git a/mypy.ini b/mypy.ini index 063f57c6856..49ae1883d37 100644 --- a/mypy.ini +++ b/mypy.ini @@ -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