Add ElkM1 time and counter services and keypress event (#41867)

pull/41923/head
Glenn Waters 2020-10-15 18:05:07 -04:00 committed by GitHub
parent a0bb8ae5d9
commit 801168f9d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 105 additions and 15 deletions

View File

@ -19,12 +19,16 @@ from homeassistant.const import (
TEMP_FAHRENHEIT,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.typing import ConfigType
import homeassistant.util.dt as dt_util
from .const import (
ATTR_KEY,
ATTR_KEY_NAME,
ATTR_KEYPAD_ID,
BARE_TEMP_CELSIUS,
BARE_TEMP_FAHRENHEIT,
CONF_AREA,
@ -41,6 +45,7 @@ from .const import (
CONF_ZONE,
DOMAIN,
ELK_ELEMENTS,
EVENT_ELKM1_KEYPAD_KEY_PRESSED,
)
SYNC_TIMEOUT = 120
@ -63,6 +68,12 @@ SPEAK_SERVICE_SCHEMA = vol.Schema(
}
)
SET_TIME_SERVICE_SCHEMA = vol.Schema(
{
vol.Optional("prefix", default=""): cv.string,
}
)
def _host_validator(config):
"""Validate that a host is properly configured."""
@ -221,6 +232,23 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
)
elk.connect()
def _element_changed(element, changeset):
keypress = changeset.get("last_keypress")
if keypress is None:
return
hass.bus.async_fire(
EVENT_ELKM1_KEYPAD_KEY_PRESSED,
{
ATTR_KEYPAD_ID: element.index + 1,
ATTR_KEY_NAME: keypress[0],
ATTR_KEY: keypress[1],
},
)
for keypad in elk.keypads: # pylint: disable=no-member
keypad.add_callback(_element_changed)
if not await async_wait_for_elk_to_sync(elk, SYNC_TIMEOUT):
_LOGGER.error(
"Timed out after %d seconds while trying to sync with ElkM1 at %s",
@ -297,21 +325,21 @@ async def async_wait_for_elk_to_sync(elk, timeout):
def _create_elk_services(hass):
def _speak_word_service(service):
def _getelk(service):
prefix = service.data["prefix"]
elk = _find_elk_by_prefix(hass, prefix)
if elk is None:
_LOGGER.error("No elk m1 with prefix for speak_word: '%s'", prefix)
return
elk.panel.speak_word(service.data["number"])
raise HomeAssistantError(f"No ElkM1 with prefix '{prefix}' found")
return elk
def _speak_word_service(service):
_getelk(service).panel.speak_word(service.data["number"])
def _speak_phrase_service(service):
prefix = service.data["prefix"]
elk = _find_elk_by_prefix(hass, prefix)
if elk is None:
_LOGGER.error("No elk m1 with prefix for speak_phrase: '%s'", prefix)
return
elk.panel.speak_phrase(service.data["number"])
_getelk(service).panel.speak_phrase(service.data["number"])
def _set_time_service(service):
_getelk(service).panel.set_time(dt_util.now())
hass.services.async_register(
DOMAIN, "speak_word", _speak_word_service, SPEAK_SERVICE_SCHEMA
@ -319,6 +347,9 @@ def _create_elk_services(hass):
hass.services.async_register(
DOMAIN, "speak_phrase", _speak_phrase_service, SPEAK_SERVICE_SCHEMA
)
hass.services.async_register(
DOMAIN, "set_time", _set_time_service, SET_TIME_SERVICE_SCHEMA
)
def create_elk_entities(elk_data, elk_elements, element_type, class_, entities):

View File

@ -36,10 +36,16 @@ ELK_ELEMENTS = {
CONF_ZONE: Max.ZONES.value,
}
EVENT_ELKM1_KEYPAD_KEY_PRESSED = "elkm1.keypad_key_pressed"
ATTR_KEYPAD_ID = "keypad_id"
ATTR_KEY = "key"
ATTR_KEY_NAME = "key_name"
ATTR_CHANGED_BY_KEYPAD = "changed_by_keypad"
ATTR_CHANGED_BY_ID = "changed_by_id"
ATTR_CHANGED_BY_TIME = "changed_by_time"
ATTR_VALUE = "value"
ELK_USER_CODE_SERVICE_SCHEMA = {
vol.Required(ATTR_CODE): vol.All(vol.Coerce(int), vol.Range(0, 999999))

View File

@ -2,7 +2,7 @@
"domain": "elkm1",
"name": "Elk-M1 Control",
"documentation": "https://www.home-assistant.io/integrations/elkm1",
"requirements": ["elkm1-lib==0.8.2"],
"requirements": ["elkm1-lib==0.8.3"],
"codeowners": ["@gwww", "@bdraco"],
"config_flow": true
}

View File

@ -6,18 +6,25 @@ from elkm1_lib.const import (
ZoneType,
)
from elkm1_lib.util import pretty_const, username
import voluptuous as vol
from homeassistant.const import VOLT
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_platform
from . import ElkAttachedEntity, create_elk_entities
from .const import DOMAIN, ELK_USER_CODE_SERVICE_SCHEMA
from .const import ATTR_VALUE, DOMAIN, ELK_USER_CODE_SERVICE_SCHEMA
SERVICE_SENSOR_COUNTER_REFRESH = "sensor_counter_refresh"
SERVICE_SENSOR_COUNTER_SET = "sensor_counter_set"
SERVICE_SENSOR_ZONE_BYPASS = "sensor_zone_bypass"
SERVICE_SENSOR_ZONE_TRIGGER = "sensor_zone_trigger"
UNDEFINED_TEMPATURE = -40
ELK_SET_COUNTER_SERVICE_SCHEMA = {
vol.Required(ATTR_VALUE): vol.All(vol.Coerce(int), vol.Range(0, 65535))
}
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Create the Elk-M1 sensor platform."""
@ -33,6 +40,16 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
platform = entity_platform.current_platform.get()
platform.async_register_entity_service(
SERVICE_SENSOR_COUNTER_REFRESH,
{},
"async_counter_refresh",
)
platform.async_register_entity_service(
SERVICE_SENSOR_COUNTER_SET,
ELK_SET_COUNTER_SERVICE_SCHEMA,
"async_counter_set",
)
platform.async_register_entity_service(
SERVICE_SENSOR_ZONE_BYPASS,
ELK_USER_CODE_SERVICE_SCHEMA,
@ -63,6 +80,18 @@ class ElkSensor(ElkAttachedEntity):
"""Return the state of the sensor."""
return self._state
async def async_counter_refresh(self):
"""Refresh the value of a counter from the panel."""
if not isinstance(self, ElkCounter):
raise HomeAssistantError("supported only on ElkM1 Counter sensors")
self._element.get()
async def async_counter_set(self, value=None):
"""Set the value of a counter on the panel."""
if not isinstance(self, ElkCounter):
raise HomeAssistantError("supported only on ElkM1 Counter sensors")
self._element.set(value)
async def async_zone_bypass(self, code=None):
"""Bypass zone."""
if not isinstance(self, ElkZone):

View File

@ -70,6 +70,13 @@ alarm_display_message:
description: Up to 16 characters of text (truncated if too long). Default blank.
example: the universe, and everything.
set_time:
description: Set the time for the panel.
fields:
prefix:
description: Prefix for the panel.
example: gatehouse
speak_phrase:
description: Speak a phrase. See list of phrases in ElkM1 ASCII Protocol documentation.
fields:
@ -84,6 +91,23 @@ speak_word:
description: Word number to speak.
example: 142
sensor_counter_refresh:
description: Refresh the value of a counter from the panel.
fields:
entity_id:
description: Name of counter to refresh.
example: "sensor.counting_sheep"
sensor_counter_set:
description: Set the value of a counter on the panel.
fields:
entity_id:
description: Name of counter to set.
example: "sensor.test42"
value:
description: Value to set the counter to.
example: 4242
sensor_zone_bypass:
description: Bypass zone.
fields:

View File

@ -541,7 +541,7 @@ elgato==0.2.0
eliqonline==1.2.2
# homeassistant.components.elkm1
elkm1-lib==0.8.2
elkm1-lib==0.8.3
# homeassistant.components.mobile_app
emoji==0.5.4

View File

@ -281,7 +281,7 @@ eebrightbox==0.0.4
elgato==0.2.0
# homeassistant.components.elkm1
elkm1-lib==0.8.2
elkm1-lib==0.8.3
# homeassistant.components.mobile_app
emoji==0.5.4