Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Elk-M1 sensor platform #17342

Merged
merged 8 commits into from
Oct 19, 2018
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion homeassistant/components/elkm1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,13 @@

_LOGGER = logging.getLogger(__name__)

SUPPORTED_DOMAINS = ['alarm_control_panel', 'light', 'scene', 'switch']
SUPPORTED_DOMAINS = ['alarm_control_panel', 'light', 'scene', 'sensor',
'switch']

SPEAK_SERVICE_SCHEMA = vol.Schema({
vol.Required('number'):
vol.All(vol.Coerce(int), vol.Range(min=0, max=999))
})


def _host_validator(config):
Expand Down Expand Up @@ -135,6 +141,8 @@ def _included(ranges, set_to, values):
'password': conf[CONF_PASSWORD]})
elk.connect()

_create_elk_services(hass, elk)

hass.data[DOMAIN] = {'elk': elk, 'config': config, 'keypads': {}}
for component in SUPPORTED_DOMAINS:
hass.async_create_task(
Expand All @@ -143,6 +151,19 @@ def _included(ranges, set_to, values):
return True


def _create_elk_services(hass, elk):
def _speak_word_service(service):
elk.panel.speak_word(service.data.get('number'))

def _speak_phrase_service(service):
elk.panel.speak_phrase(service.data.get('number'))

hass.services.async_register(
DOMAIN, 'speak_word', _speak_word_service, SPEAK_SERVICE_SCHEMA)
hass.services.async_register(
DOMAIN, 'speak_phrase', _speak_phrase_service, SPEAK_SERVICE_SCHEMA)


def create_elk_entities(hass, elk_elements, element_type, class_, entities):
"""Create the ElkM1 devices of a particular class."""
elk_data = hass.data[DOMAIN]
Expand Down
13 changes: 13 additions & 0 deletions homeassistant/components/elkm1/services.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
elkm1:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this key and unindent the following lines.

sensor_speak_word:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not the correct service name. Remove the sensor prefix.

description: Speak a word. See list of words in ElkM1 ASCII Protocol documentation.
fields:
number:
description: Word number to speak.
example: 142
speak_phrase:
description: Speak a phrase. See list of phrases in ElkM1 ASCII Protocol documentation.
fields:
number:
description: Phrase number to speak.
example: 42
228 changes: 228 additions & 0 deletions homeassistant/components/sensor/elkm1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
"""
Support for control of ElkM1 sensors.

For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.elkm1/
"""

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove blank line.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still left.

from homeassistant.components.elkm1 import (
DOMAIN as ELK_DOMAIN, create_elk_entities, ElkEntity)

DEPENDENCIES = [ELK_DOMAIN]


async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
"""Create the Elk-M1 sensor platform."""
if discovery_info is None:
return

elk = hass.data[ELK_DOMAIN]['elk']
entities = create_elk_entities(
hass, elk.counters, 'counter', ElkCounter, [])
entities = create_elk_entities(
hass, elk.keypads, 'keypad', ElkKeypad, entities)
entities = create_elk_entities(
hass, [elk.panel], 'panel', ElkPanel, entities)
entities = create_elk_entities(
hass, elk.settings, 'setting', ElkSetting, entities)
entities = create_elk_entities(
hass, elk.zones, 'zone', ElkZone, entities)
async_add_entities(entities, True)


def temperature_to_state(temperature, undefined_temperature):
"""Convert temperature to a state."""
return temperature if temperature > undefined_temperature else None


class ElkSensor(ElkEntity):
"""Base representation of Elk-M1 sensor."""

def __init__(self, element, elk, elk_data):
"""Initialize the base of all Elk sensors."""
super().__init__(element, elk, elk_data)
self._state = None

@property
def state(self):
"""Return the state of the sensor."""
return self._state


class ElkCounter(ElkSensor):
"""Representation of an Elk-M1 Counter."""

@property
def icon(self):
"""Icon to use in the frontend."""
return 'mdi:numeric'

def _element_changed(self, element, changeset):
self._state = self._element.value


class ElkKeypad(ElkSensor):
"""Representation of an Elk-M1 Keypad."""

@property
def temperature_unit(self):
"""Return the temperature unit."""
return self._temperature_unit

@property
def unit_of_measurement(self):
"""Return the unit of measurement."""
return self._temperature_unit

@property
def icon(self):
"""Icon to use in the frontend."""
return 'mdi:thermometer-lines'

@property
def device_state_attributes(self):
"""Attributes of the sensor."""
from elkm1_lib.util import username

attrs = self.initial_attrs()
attrs['area'] = self._element.area + 1
attrs['temperature'] = self._element.temperature
attrs['last_user_time'] = self._element.last_user_time.isoformat()
attrs['last_user'] = self._element.last_user + 1
attrs['code'] = self._element.code
attrs['last_user_name'] = username(self._elk, self._element.last_user)
return attrs

def _element_changed(self, element, changeset):
self._state = temperature_to_state(self._element.temperature, -40)

async def async_added_to_hass(self):
"""Register callback for ElkM1 changes and update entity state."""
await super().async_added_to_hass()
self.hass.data[ELK_DOMAIN]['keypads'][
self._element.index] = self.entity_id


class ElkPanel(ElkSensor):
"""Representation of an Elk-M1 Panel."""

@property
def icon(self):
"""Icon to use in the frontend."""
return "mdi:home"

@property
def device_state_attributes(self):
"""Attributes of the sensor."""
attrs = self.initial_attrs()
attrs['system_trouble_status'] = self._element.system_trouble_status
return attrs

def _element_changed(self, element, changeset):
if self._elk.is_connected():
self._state = 'Paused' if self._element.remote_programming_status \
else 'Connected'
else:
self._state = 'Disconnected'


class ElkSetting(ElkSensor):
"""Representation of an Elk-M1 Setting."""

@property
def icon(self):
"""Icon to use in the frontend."""
return 'mdi:numeric'

def _element_changed(self, element, changeset):
self._state = self._element.value

@property
def device_state_attributes(self):
"""Attributes of the sensor."""
from elkm1_lib.const import SettingFormat
attrs = self.initial_attrs()
attrs['value_format'] = SettingFormat(
self._element.value_format).name.lower()
return attrs


class ElkZone(ElkSensor):
"""Representation of an Elk-M1 Zone."""

@property
def icon(self):
"""Icon to use in the frontend."""
from elkm1_lib.const import ZoneType
zone_icons = {
ZoneType.FIRE_ALARM.value: 'fire',
ZoneType.FIRE_VERIFIED.value: 'fire',
ZoneType.FIRE_SUPERVISORY.value: 'fire',
ZoneType.KEYFOB.value: 'key',
ZoneType.NON_ALARM.value: 'alarm-off',
ZoneType.MEDICAL_ALARM.value: 'medical-bag',
ZoneType.POLICE_ALARM.value: 'alarm-light',
ZoneType.POLICE_NO_INDICATION.value: 'alarm-light',
ZoneType.KEY_MOMENTARY_ARM_DISARM.value: 'power',
ZoneType.KEY_MOMENTARY_ARM_AWAY.value: 'power',
ZoneType.KEY_MOMENTARY_ARM_STAY.value: 'power',
ZoneType.KEY_MOMENTARY_DISARM.value: 'power',
ZoneType.KEY_ON_OFF.value: 'toggle-switch',
ZoneType.MUTE_AUDIBLES.value: 'volume-mute',
ZoneType.POWER_SUPERVISORY.value: 'power-plug',
ZoneType.TEMPERATURE.value: 'thermometer-lines',
ZoneType.ANALOG_ZONE.value: 'speedometer',
ZoneType.PHONE_KEY.value: 'phone-classic',
ZoneType.INTERCOM_KEY.value: 'deskphone'
}
return 'mdi:{}'.format(
zone_icons.get(self._element.definition, 'alarm-bell'))

@property
def device_state_attributes(self):
"""Attributes of the sensor."""
from elkm1_lib.const import (
ZoneLogicalStatus, ZonePhysicalStatus, ZoneType)

attrs = self.initial_attrs()
attrs['physical_status'] = ZonePhysicalStatus(
self._element.physical_status).name.lower()
attrs['logical_status'] = ZoneLogicalStatus(
self._element.logical_status).name.lower()
attrs['definition'] = ZoneType(
self._element.definition).name.lower()
attrs['area'] = self._element.area + 1
attrs['bypassed'] = self._element.bypassed
attrs['triggered_alarm'] = self._element.triggered_alarm
return attrs

@property
def temperature_unit(self):
"""Return the temperature unit."""
from elkm1_lib.const import ZoneType
if self._element.definition == ZoneType.TEMPERATURE.value:
return self._temperature_unit
return None

@property
def unit_of_measurement(self):
"""Return the unit of measurement."""
from elkm1_lib.const import ZoneType
if self._element.definition == ZoneType.TEMPERATURE.value:
return self._temperature_unit
if self._element.definition == ZoneType.ANALOG_ZONE.value:
return 'V'
return None

def _element_changed(self, element, changeset):
from elkm1_lib.const import ZoneLogicalStatus, ZoneType
from elkm1_lib.util import pretty_const

if self._element.definition == ZoneType.TEMPERATURE.value:
self._state = temperature_to_state(self._element.temperature, -60)
elif self._element.definition == ZoneType.ANALOG_ZONE.value:
self._state = self._element.voltage
else:
self._state = pretty_const(ZoneLogicalStatus(
MartinHjelmare marked this conversation as resolved.
Show resolved Hide resolved
self._element.logical_status).name)