diff --git a/.coveragerc b/.coveragerc index cb57afd317e..ab9d40a1896 100644 --- a/.coveragerc +++ b/.coveragerc @@ -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 @@ -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 diff --git a/homeassistant/components/alarm_control_panel/egardia.py b/homeassistant/components/alarm_control_panel/egardia.py index e9f08cd4fed..64e165f6b16 100644 --- a/homeassistant/components/alarm_control_panel/egardia.py +++ b/homeassistant/components/alarm_control_panel/egardia.py @@ -4,130 +4,65 @@ Interfaces with Egardia/Woonveilig alarm control panel. 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): @@ -156,31 +91,20 @@ class EgardiaAlarm(alarm.AlarmControlPanel): 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") diff --git a/homeassistant/components/binary_sensor/egardia.py b/homeassistant/components/binary_sensor/egardia.py new file mode 100644 index 00000000000..ab88de9d3c9 --- /dev/null +++ b/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 diff --git a/homeassistant/components/egardia.py b/homeassistant/components/egardia.py new file mode 100644 index 00000000000..2cfc44a407b --- /dev/null +++ b/homeassistant/components/egardia.py @@ -0,0 +1,123 @@ +""" +Interfaces with Egardia/Woonveilig alarm control panel. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/egardia/ +""" +import logging + +import requests +import voluptuous as vol + +from homeassistant.helpers import discovery +import homeassistant.helpers.config_validation as cv +from homeassistant.const import ( + CONF_PORT, CONF_HOST, CONF_PASSWORD, CONF_USERNAME, CONF_NAME, + EVENT_HOMEASSISTANT_STOP) + +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' +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' +EGARDIA_SERVER = 'egardia_server' +EGARDIA_DEVICE = 'egardiadevice' +EGARDIA_NAME = 'egardianame' +EGARDIA_REPORT_SERVER_ENABLED = 'egardia_rs_enabled' +EGARDIA_REPORT_SERVER_CODES = 'egardia_rs_codes' +NOTIFICATION_ID = 'egardia_notification' +NOTIFICATION_TITLE = 'Egardia' +ATTR_DISCOVER_DEVICES = 'egardia_sensor' + +SERVER_CODE_SCHEMA = vol.Schema({ + vol.Optional('arm'): vol.All(cv.ensure_list_csv, [cv.string]), + vol.Optional('disarm'): vol.All(cv.ensure_list_csv, [cv.string]), + vol.Optional('armhome'): vol.All(cv.ensure_list_csv, [cv.string]), + vol.Optional('triggered'): vol.All(cv.ensure_list_csv, [cv.string]), + vol.Optional('ignore'): vol.All(cv.ensure_list_csv, [cv.string]) +}) + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: vol.Schema({ + 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, default={}): SERVER_CODE_SCHEMA, + 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, + }), +}, extra=vol.ALLOW_EXTRA) + + +def setup(hass, config): + """Set up the Egardia platform.""" + from pythonegardia import egardiadevice + from pythonegardia import egardiaserver + conf = config[DOMAIN] + username = conf.get(CONF_USERNAME) + password = conf.get(CONF_PASSWORD) + host = conf.get(CONF_HOST) + port = conf.get(CONF_PORT) + version = conf.get(CONF_VERSION) + rs_enabled = conf.get(CONF_REPORT_SERVER_ENABLED) + rs_port = conf.get(CONF_REPORT_SERVER_PORT) + try: + device = hass.data[EGARDIA_DEVICE] = egardiadevice.EgardiaDevice( + host, port, username, password, '', version) + except requests.exceptions.RequestException: + _LOGGER.error("An error occurred accessing your Egardia device. " + + "Please check config.") + return False + except egardiadevice.UnauthorizedError: + _LOGGER.error("Unable to authorize. Wrong password or username.") + return False + # Set up the egardia server if enabled + if rs_enabled: + _LOGGER.debug("Setting up EgardiaServer") + try: + if EGARDIA_SERVER 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[EGARDIA_SERVER] = server + server.start() + + def handle_stop_event(event): + """Callback function for HA stop event.""" + server.stop() + + # listen to home assistant stop event + hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, handle_stop_event) + + except IOError: + _LOGGER.error("Binding error occurred while starting " + + "EgardiaServer.") + return False + + discovery.load_platform(hass, 'alarm_control_panel', DOMAIN, + discovered=conf, hass_config=config) + + # get the sensors from the device and add those + sensors = device.getsensors() + discovery.load_platform(hass, 'binary_sensor', DOMAIN, + {ATTR_DISCOVER_DEVICES: sensors}, config) + + return True diff --git a/requirements_all.txt b/requirements_all.txt index 8bbf005c0d7..50026ee72d1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -988,8 +988,9 @@ python_opendata_transport==0.0.3 # homeassistant.components.zwave python_openzwave==0.4.3 +# homeassistant.components.egardia # homeassistant.components.alarm_control_panel.egardia -pythonegardia==1.0.26 +pythonegardia==1.0.38 # homeassistant.components.sensor.whois pythonwhois==2.4.3