Skip to content

Commit

Permalink
Egardia redesign - generic component and sensor support (#11994)
Browse files Browse the repository at this point in the history
* Egardia redesign - generic component and sensor support

* Updating .coveragerc and requirements_all

* Fixing linting errors

* Fixing linting errors (again)

* Fixing linting errors

* Responding to review

* Responding to review.

* Updating requirements_all.txt

* Responding to review.

* Responding to review

* Removing unnessesary logging line.

* Responding to review

* Responding to review.

* Fixing copying mistake.

* Responding to review.

* Improving validation.

* Updating package requirement to .38

* Fixing syntax error.

* Updating requirements_all.txt

* Fixing bug handling alarm status.

* Updating requirements_all.txt

* Updating requirements_all.txt

* Changing parsing of configuration.

* Changing code lookup.

* Fixing linting error.
  • Loading branch information
jeroenterheerdt authored and MartinHjelmare committed Mar 2, 2018
1 parent 25c4c9b commit 7a979e9
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 117 deletions.
4 changes: 3 additions & 1 deletion .coveragerc
Expand Up @@ -85,6 +85,9 @@ omit =
homeassistant/components/ecobee.py
homeassistant/components/*/ecobee.py

homeassistant/components/egardia.py
homeassistant/components/*/egardia.py

homeassistant/components/enocean.py
homeassistant/components/*/enocean.py

Expand Down Expand Up @@ -305,7 +308,6 @@ omit =
homeassistant/components/alarm_control_panel/alarmdotcom.py
homeassistant/components/alarm_control_panel/canary.py
homeassistant/components/alarm_control_panel/concord232.py
homeassistant/components/alarm_control_panel/egardia.py
homeassistant/components/alarm_control_panel/ialarm.py
homeassistant/components/alarm_control_panel/manual_mqtt.py
homeassistant/components/alarm_control_panel/nx584.py
Expand Down
154 changes: 39 additions & 115 deletions homeassistant/components/alarm_control_panel/egardia.py
Expand Up @@ -4,130 +4,65 @@
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.egardia/
"""
import asyncio
import logging

import requests
import voluptuous as vol

import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components.alarm_control_panel import PLATFORM_SCHEMA
from homeassistant.const import (
CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_USERNAME,
EVENT_HOMEASSISTANT_STOP, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME,
STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED, STATE_UNKNOWN)
import homeassistant.exceptions as exc
import homeassistant.helpers.config_validation as cv

REQUIREMENTS = ['pythonegardia==1.0.26']
STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_AWAY, STATE_ALARM_TRIGGERED)
from homeassistant.components.egardia import (
EGARDIA_DEVICE, EGARDIA_SERVER,
REPORT_SERVER_CODES_IGNORE, CONF_REPORT_SERVER_CODES,
CONF_REPORT_SERVER_ENABLED, CONF_REPORT_SERVER_PORT
)
REQUIREMENTS = ['pythonegardia==1.0.38']

_LOGGER = logging.getLogger(__name__)

CONF_REPORT_SERVER_CODES = 'report_server_codes'
CONF_REPORT_SERVER_ENABLED = 'report_server_enabled'
CONF_REPORT_SERVER_PORT = 'report_server_port'
CONF_REPORT_SERVER_CODES_IGNORE = 'ignore'
CONF_VERSION = 'version'

DEFAULT_NAME = 'Egardia'
DEFAULT_PORT = 80
DEFAULT_REPORT_SERVER_ENABLED = False
DEFAULT_REPORT_SERVER_PORT = 52010
DEFAULT_VERSION = 'GATE-01'
DOMAIN = 'egardia'
D_EGARDIASRV = 'egardiaserver'

NOTIFICATION_ID = 'egardia_notification'
NOTIFICATION_TITLE = 'Egardia'

STATES = {
'ARM': STATE_ALARM_ARMED_AWAY,
'DAY HOME': STATE_ALARM_ARMED_HOME,
'DISARM': STATE_ALARM_DISARMED,
'HOME': STATE_ALARM_ARMED_HOME,
'TRIGGERED': STATE_ALARM_TRIGGERED,
'UNKNOWN': STATE_UNKNOWN,
'ARMHOME': STATE_ALARM_ARMED_HOME,
'TRIGGERED': STATE_ALARM_TRIGGERED
}

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_USERNAME): cv.string,
vol.Optional(CONF_VERSION, default=DEFAULT_VERSION): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_REPORT_SERVER_CODES): vol.All(cv.ensure_list),
vol.Optional(CONF_REPORT_SERVER_ENABLED,
default=DEFAULT_REPORT_SERVER_ENABLED): cv.boolean,
vol.Optional(CONF_REPORT_SERVER_PORT, default=DEFAULT_REPORT_SERVER_PORT):
cv.port,
})


def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Egardia platform."""
from pythonegardia import egardiadevice
from pythonegardia import egardiaserver

name = config.get(CONF_NAME)
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
host = config.get(CONF_HOST)
port = config.get(CONF_PORT)
rs_enabled = config.get(CONF_REPORT_SERVER_ENABLED)
rs_port = config.get(CONF_REPORT_SERVER_PORT)
rs_codes = config.get(CONF_REPORT_SERVER_CODES)
version = config.get(CONF_VERSION)

try:
egardiasystem = egardiadevice.EgardiaDevice(
host, port, username, password, '', version)
except requests.exceptions.RequestException:
raise exc.PlatformNotReady()
except egardiadevice.UnauthorizedError:
_LOGGER.error("Unable to authorize. Wrong password or username")
return

eg_dev = EgardiaAlarm(
name, egardiasystem, rs_enabled, rs_codes)

if rs_enabled:
# Set up the egardia server
_LOGGER.info("Setting up EgardiaServer")
try:
if D_EGARDIASRV not in hass.data:
server = egardiaserver.EgardiaServer('', rs_port)
bound = server.bind()
if not bound:
raise IOError(
"Binding error occurred while starting EgardiaServer")
hass.data[D_EGARDIASRV] = server
server.start()
except IOError:
return
hass.data[D_EGARDIASRV].register_callback(eg_dev.handle_status_event)

def handle_stop_event(event):
"""Call function for Home Assistant stop event."""
hass.data[D_EGARDIASRV].stop()

hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, handle_stop_event)

add_devices([eg_dev], True)
device = EgardiaAlarm(
discovery_info['name'],
hass.data[EGARDIA_DEVICE],
discovery_info[CONF_REPORT_SERVER_ENABLED],
discovery_info.get(CONF_REPORT_SERVER_CODES),
discovery_info[CONF_REPORT_SERVER_PORT])
# add egardia alarm device
add_devices([device], True)


class EgardiaAlarm(alarm.AlarmControlPanel):
"""Representation of a Egardia alarm."""

def __init__(self, name, egardiasystem, rs_enabled=False, rs_codes=None):
def __init__(self, name, egardiasystem,
rs_enabled=False, rs_codes=None, rs_port=52010):
"""Initialize the Egardia alarm."""
self._name = name
self._egardiasystem = egardiasystem
self._status = None
self._rs_enabled = rs_enabled
if rs_codes is not None:
self._rs_codes = rs_codes[0]
else:
self._rs_codes = rs_codes
self._rs_codes = rs_codes
self._rs_port = rs_port

@asyncio.coroutine
def async_added_to_hass(self):
"""Add Egardiaserver callback if enabled."""
if self._rs_enabled:
_LOGGER.debug("Registering callback to Egardiaserver")
self.hass.data[EGARDIA_SERVER].register_callback(
self.handle_status_event)

@property
def name(self):
Expand Down Expand Up @@ -156,31 +91,20 @@ def handle_status_event(self, event):

def lookupstatusfromcode(self, statuscode):
"""Look at the rs_codes and returns the status from the code."""
status = 'UNKNOWN'
if self._rs_codes is not None:
statuscode = str(statuscode).strip()
for i in self._rs_codes:
val = str(self._rs_codes[i]).strip()
if ',' in val:
splitted = val.split(',')
for code in splitted:
code = str(code).strip()
if statuscode == code:
status = i.upper()
break
elif statuscode == val:
status = i.upper()
break
status = next((
status_group.upper() for status_group, codes
in self._rs_codes.items() for code in codes
if statuscode == code), 'UNKNOWN')
return status

def parsestatus(self, status):
"""Parse the status."""
_LOGGER.debug("Parsing status %s", status)
# Ignore the statuscode if it is IGNORE
if status.lower().strip() != CONF_REPORT_SERVER_CODES_IGNORE:
_LOGGER.debug("Not ignoring status")
newstatus = ([v for k, v in STATES.items()
if status.upper() == k][0])
if status.lower().strip() != REPORT_SERVER_CODES_IGNORE:
_LOGGER.debug("Not ignoring status %s", status)
newstatus = STATES.get(status.upper())
_LOGGER.debug("newstatus %s", newstatus)
self._status = newstatus
else:
_LOGGER.error("Ignoring status")
Expand Down
78 changes: 78 additions & 0 deletions homeassistant/components/binary_sensor/egardia.py
@@ -0,0 +1,78 @@
"""
Interfaces with Egardia/Woonveilig alarm control panel.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.egardia/
"""
import asyncio
import logging

from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.const import STATE_ON, STATE_OFF
from homeassistant.components.egardia import (
EGARDIA_DEVICE, ATTR_DISCOVER_DEVICES)
_LOGGER = logging.getLogger(__name__)

EGARDIA_TYPE_TO_DEVICE_CLASS = {'IR Sensor': 'motion',
'Door Contact': 'opening',
'IR': 'motion'}


@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Initialize the platform."""
if (discovery_info is None or
discovery_info[ATTR_DISCOVER_DEVICES] is None):
return

disc_info = discovery_info[ATTR_DISCOVER_DEVICES]
# multiple devices here!
async_add_devices(
(
EgardiaBinarySensor(
sensor_id=disc_info[sensor]['id'],
name=disc_info[sensor]['name'],
egardia_system=hass.data[EGARDIA_DEVICE],
device_class=EGARDIA_TYPE_TO_DEVICE_CLASS.get(
disc_info[sensor]['type'], None)
)
for sensor in disc_info
), True)


class EgardiaBinarySensor(BinarySensorDevice):
"""Represents a sensor based on an Egardia sensor (IR, Door Contact)."""

def __init__(self, sensor_id, name, egardia_system, device_class):
"""Initialize the sensor device."""
self._id = sensor_id
self._name = name
self._state = None
self._device_class = device_class
self._egardia_system = egardia_system

def update(self):
"""Update the status."""
egardia_input = self._egardia_system.getsensorstate(self._id)
self._state = STATE_ON if egardia_input else STATE_OFF

@property
def name(self):
"""The name of the device."""
return self._name

@property
def is_on(self):
"""Whether the device is switched on."""
return self._state == STATE_ON

@property
def hidden(self):
"""Whether the device is hidden by default."""
# these type of sensors are probably mainly used for automations
return True

@property
def device_class(self):
"""The device class."""
return self._device_class

0 comments on commit 7a979e9

Please sign in to comment.