Config flow for elkm1 (#33297)

* Config flow for elkm1

* As entity ids can now be changed, the “alarm_control_panel”
attribute “changed_by_entity_id” is now “changed_by_keypad”
and will show the name of the Elk keypad instead of the entity id.

* An auto configure mode has been introduced which avoids the
need to setup the complex include and exclude filters.  This
functionality still exists when configuring from yaml for power
users who want more control over which entities elkm1 generates.

* restore _has_all_unique_prefixes

* preserve legacy behavior of creating alarm_control_panels that have no linked keypads when auto_configure is False

* unroll loop
This commit is contained in:
J. Nick Koston 2020-03-27 15:38:35 -05:00 committed by GitHub
parent c629e7dc0e
commit 18a4829314
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 823 additions and 214 deletions

View file

@ -1,4 +1,6 @@
"""Each ElkM1 area will be created as a separate alarm_control_panel."""
import logging
from elkm1_lib.const import AlarmState, ArmedStatus, ArmLevel, ArmUpState
import voluptuous as vol
@ -22,24 +24,18 @@ from homeassistant.const import (
STATE_ALARM_PENDING,
STATE_ALARM_TRIGGERED,
)
from homeassistant.helpers import entity_platform
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import (
async_dispatcher_connect,
async_dispatcher_send,
)
from . import (
DOMAIN,
SERVICE_ALARM_ARM_HOME_INSTANT,
SERVICE_ALARM_ARM_NIGHT_INSTANT,
SERVICE_ALARM_ARM_VACATION,
SERVICE_ALARM_DISPLAY_MESSAGE,
ElkEntity,
ElkAttachedEntity,
create_elk_entities,
)
SIGNAL_ARM_ENTITY = "elkm1_arm"
SIGNAL_DISPLAY_MESSAGE = "elkm1_display_message"
from .const import DOMAIN
ELK_ALARM_SERVICE_SCHEMA = vol.Schema(
{
@ -61,69 +57,57 @@ DISPLAY_MESSAGE_SERVICE_SCHEMA = vol.Schema(
}
)
_LOGGER = logging.getLogger(__name__)
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the ElkM1 alarm platform."""
if discovery_info is None:
return
elk_datas = hass.data[DOMAIN]
elk_data = hass.data[DOMAIN][config_entry.entry_id]
entities = []
for elk_data in elk_datas.values():
elk = elk_data["elk"]
entities = create_elk_entities(elk_data, elk.areas, "area", ElkArea, entities)
elk = elk_data["elk"]
areas_with_keypad = set()
for keypad in elk.keypads:
areas_with_keypad.add(keypad.area)
areas = []
for area in elk.areas:
if area.index in areas_with_keypad or elk_data["auto_configure"] is False:
areas.append(area)
create_elk_entities(elk_data, areas, "area", ElkArea, entities)
async_add_entities(entities, True)
def _dispatch(signal, entity_ids, *args):
for entity_id in entity_ids:
async_dispatcher_send(hass, f"{signal}_{entity_id}", *args)
platform = entity_platform.current_platform.get()
def _arm_service(service):
entity_ids = service.data.get(ATTR_ENTITY_ID, [])
arm_level = _arm_services().get(service.service)
args = (arm_level, service.data.get(ATTR_CODE))
_dispatch(SIGNAL_ARM_ENTITY, entity_ids, *args)
for service in _arm_services():
hass.services.async_register(
DOMAIN, service, _arm_service, ELK_ALARM_SERVICE_SCHEMA
)
def _display_message_service(service):
entity_ids = service.data.get(ATTR_ENTITY_ID, [])
data = service.data
args = (
data["clear"],
data["beep"],
data["timeout"],
data["line1"],
data["line2"],
)
_dispatch(SIGNAL_DISPLAY_MESSAGE, entity_ids, *args)
hass.services.async_register(
DOMAIN,
platform.async_register_entity_service(
SERVICE_ALARM_ARM_VACATION,
ELK_ALARM_SERVICE_SCHEMA,
"async_alarm_arm_vacation",
)
platform.async_register_entity_service(
SERVICE_ALARM_ARM_HOME_INSTANT,
ELK_ALARM_SERVICE_SCHEMA,
"async_alarm_arm_home_instant",
)
platform.async_register_entity_service(
SERVICE_ALARM_ARM_NIGHT_INSTANT,
ELK_ALARM_SERVICE_SCHEMA,
"async_alarm_arm_night_instant",
)
platform.async_register_entity_service(
SERVICE_ALARM_DISPLAY_MESSAGE,
_display_message_service,
DISPLAY_MESSAGE_SERVICE_SCHEMA,
"async_display_message",
)
def _arm_services():
return {
SERVICE_ALARM_ARM_VACATION: ArmLevel.ARMED_VACATION.value,
SERVICE_ALARM_ARM_HOME_INSTANT: ArmLevel.ARMED_STAY_INSTANT.value,
SERVICE_ALARM_ARM_NIGHT_INSTANT: ArmLevel.ARMED_NIGHT_INSTANT.value,
}
class ElkArea(ElkEntity, AlarmControlPanel):
class ElkArea(ElkAttachedEntity, AlarmControlPanel):
"""Representation of an Area / Partition within the ElkM1 alarm panel."""
def __init__(self, element, elk, elk_data):
"""Initialize Area as Alarm Control Panel."""
super().__init__(element, elk, elk_data)
self._changed_by_entity_id = ""
self._changed_by_keypad = None
self._state = None
async def async_added_to_hass(self):
@ -131,23 +115,13 @@ class ElkArea(ElkEntity, AlarmControlPanel):
await super().async_added_to_hass()
for keypad in self._elk.keypads:
keypad.add_callback(self._watch_keypad)
async_dispatcher_connect(
self.hass, f"{SIGNAL_ARM_ENTITY}_{self.entity_id}", self._arm_service
)
async_dispatcher_connect(
self.hass,
f"{SIGNAL_DISPLAY_MESSAGE}_{self.entity_id}",
self._display_message,
)
def _watch_keypad(self, keypad, changeset):
if keypad.area != self._element.index:
return
if changeset.get("last_user") is not None:
self._changed_by_entity_id = self.hass.data[DOMAIN][self._prefix][
"keypads"
].get(keypad.index, "")
self.async_schedule_update_ha_state(True)
self._changed_by_keypad = keypad.name
self.async_write_ha_state()
@property
def code_format(self):
@ -178,7 +152,7 @@ class ElkArea(ElkEntity, AlarmControlPanel):
attrs["arm_up_state"] = ArmUpState(elmt.arm_up_state).name.lower()
if elmt.alarm_state is not None:
attrs["alarm_state"] = AlarmState(elmt.alarm_state).name.lower()
attrs["changed_by_entity_id"] = self._changed_by_entity_id
attrs["changed_by_keypad"] = self._changed_by_keypad
return attrs
def _element_changed(self, element, changeset):
@ -225,9 +199,18 @@ class ElkArea(ElkEntity, AlarmControlPanel):
"""Send arm night command."""
self._element.arm(ArmLevel.ARMED_NIGHT.value, int(code))
async def _arm_service(self, arm_level, code):
self._element.arm(arm_level, code)
async def async_alarm_arm_home_instant(self, code=None):
"""Send arm stay instant command."""
self._element.arm(ArmLevel.ARMED_STAY_INSTANT.value, int(code))
async def _display_message(self, clear, beep, timeout, line1, line2):
async def async_alarm_arm_night_instant(self, code=None):
"""Send arm night instant command."""
self._element.arm(ArmLevel.ARMED_NIGHT_INSTANT.value, int(code))
async def async_alarm_arm_vacation(self, code=None):
"""Send arm vacation command."""
self._element.arm(ArmLevel.ARMED_VACATION.value, int(code))
async def async_display_message(self, clear, beep, timeout, line1, line2):
"""Display a message on all keypads for the area."""
self._element.display_message(clear, beep, timeout, line1, line2)