From 2ceb4d2d1eba05ab685c914618bf6bf8f3ec7300 Mon Sep 17 00:00:00 2001 From: emontnemery Date: Mon, 15 Oct 2018 15:35:33 +0200 Subject: [PATCH] Refactor RFLink component (#17402) * Start refactor of RFLink component * alias _id not added correctly Aliases for sensor not added correctly And some debug traces. * Update rflink.py * Cleanup, fix review comments * Call event handlers directly when processing initial event * Use hass.async_create_task when adding discovered device * Review comments * Review comments --- homeassistant/components/cover/rflink.py | 36 ++----- homeassistant/components/light/rflink.py | 66 ++++--------- homeassistant/components/rflink.py | 112 ++++++++++++++++------ homeassistant/components/sensor/rflink.py | 82 +++++++++------- homeassistant/components/switch/rflink.py | 56 ++++------- 5 files changed, 168 insertions(+), 184 deletions(-) diff --git a/homeassistant/components/cover/rflink.py b/homeassistant/components/cover/rflink.py index 41a4c2af045..353cccc7d4f 100644 --- a/homeassistant/components/cover/rflink.py +++ b/homeassistant/components/cover/rflink.py @@ -9,8 +9,9 @@ import logging import voluptuous as vol from homeassistant.components.rflink import ( - DATA_ENTITY_GROUP_LOOKUP, DATA_ENTITY_LOOKUP, - DEVICE_DEFAULTS_SCHEMA, EVENT_KEY_COMMAND, RflinkCommand) + CONF_ALIASES, CONF_DEVICE_DEFAULTS, CONF_DEVICES, CONF_FIRE_EVENT, + CONF_GROUP, CONF_GROUP_ALIASES, CONF_NOGROUP_ALIASES, + CONF_SIGNAL_REPETITIONS, DEVICE_DEFAULTS_SCHEMA, RflinkCommand) from homeassistant.components.cover import ( CoverDevice, PLATFORM_SCHEMA) import homeassistant.helpers.config_validation as cv @@ -22,19 +23,6 @@ DEPENDENCIES = ['rflink'] _LOGGER = logging.getLogger(__name__) -CONF_ALIASES = 'aliases' -CONF_GROUP_ALIASES = 'group_aliases' -CONF_GROUP = 'group' -CONF_NOGROUP_ALIASES = 'nogroup_aliases' -CONF_DEVICE_DEFAULTS = 'device_defaults' -CONF_DEVICES = 'devices' -CONF_AUTOMATIC_ADD = 'automatic_add' -CONF_FIRE_EVENT = 'fire_event' -CONF_IGNORE_DEVICES = 'ignore_devices' -CONF_RECONNECT_INTERVAL = 'reconnect_interval' -CONF_SIGNAL_REPETITIONS = 'signal_repetitions' -CONF_WAIT_FOR_ACK = 'wait_for_ack' - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_DEVICE_DEFAULTS, default=DEVICE_DEFAULTS_SCHEMA({})): DEVICE_DEFAULTS_SCHEMA, @@ -55,33 +43,21 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ }) -def devices_from_config(domain_config, hass=None): +def devices_from_config(domain_config): """Parse configuration and add Rflink cover devices.""" devices = [] for device_id, config in domain_config[CONF_DEVICES].items(): device_config = dict(domain_config[CONF_DEVICE_DEFAULTS], **config) - device = RflinkCover(device_id, hass, **device_config) + device = RflinkCover(device_id, **device_config) devices.append(device) - # Register entity (and aliases) to listen to incoming rflink events - # Device id and normal aliases respond to normal and group command - hass.data[DATA_ENTITY_LOOKUP][ - EVENT_KEY_COMMAND][device_id].append(device) - if config[CONF_GROUP]: - hass.data[DATA_ENTITY_GROUP_LOOKUP][ - EVENT_KEY_COMMAND][device_id].append(device) - for _id in config[CONF_ALIASES]: - hass.data[DATA_ENTITY_LOOKUP][ - EVENT_KEY_COMMAND][_id].append(device) - hass.data[DATA_ENTITY_GROUP_LOOKUP][ - EVENT_KEY_COMMAND][_id].append(device) return devices async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Rflink cover platform.""" - async_add_entities(devices_from_config(config, hass)) + async_add_entities(devices_from_config(config)) class RflinkCover(RflinkCommand, CoverDevice): diff --git a/homeassistant/components/light/rflink.py b/homeassistant/components/light/rflink.py index d9f9dd589ec..885239a51c3 100644 --- a/homeassistant/components/light/rflink.py +++ b/homeassistant/components/light/rflink.py @@ -7,18 +7,16 @@ https://home-assistant.io/components/light.rflink/ import logging from homeassistant.components.light import ( - ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light) + ATTR_BRIGHTNESS, PLATFORM_SCHEMA, SUPPORT_BRIGHTNESS, Light) from homeassistant.components.rflink import ( CONF_ALIASES, CONF_ALIASSES, CONF_AUTOMATIC_ADD, CONF_DEVICE_DEFAULTS, CONF_DEVICES, CONF_FIRE_EVENT, CONF_GROUP, CONF_GROUP_ALIASES, CONF_GROUP_ALIASSES, CONF_IGNORE_DEVICES, CONF_NOGROUP_ALIASES, CONF_NOGROUP_ALIASSES, CONF_SIGNAL_REPETITIONS, DATA_DEVICE_REGISTER, - DATA_ENTITY_GROUP_LOOKUP, DATA_ENTITY_LOOKUP, DEVICE_DEFAULTS_SCHEMA, - DOMAIN, EVENT_KEY_COMMAND, EVENT_KEY_ID, SwitchableRflinkDevice, cv, + DEVICE_DEFAULTS_SCHEMA, + EVENT_KEY_COMMAND, EVENT_KEY_ID, SwitchableRflinkDevice, cv, remove_deprecated, vol) -from homeassistant.const import ( - CONF_NAME, CONF_PLATFORM, CONF_TYPE, STATE_UNKNOWN) -from homeassistant.helpers.deprecation import get_deprecated +from homeassistant.const import (CONF_NAME, CONF_TYPE) DEPENDENCIES = ['rflink'] @@ -29,14 +27,13 @@ TYPE_SWITCHABLE = 'switchable' TYPE_HYBRID = 'hybrid' TYPE_TOGGLE = 'toggle' -PLATFORM_SCHEMA = vol.Schema({ - vol.Required(CONF_PLATFORM): DOMAIN, +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_IGNORE_DEVICES): vol.All(cv.ensure_list, [cv.string]), vol.Optional(CONF_DEVICE_DEFAULTS, default=DEVICE_DEFAULTS_SCHEMA({})): - DEVICE_DEFAULTS_SCHEMA, + DEVICE_DEFAULTS_SCHEMA, vol.Optional(CONF_AUTOMATIC_ADD, default=True): cv.boolean, - vol.Optional(CONF_DEVICES, default={}): vol.Schema({ - cv.string: { + vol.Optional(CONF_DEVICES, default={}): { + cv.string: vol.Schema({ vol.Optional(CONF_NAME): cv.string, vol.Optional(CONF_TYPE): vol.Any(TYPE_DIMMABLE, TYPE_SWITCHABLE, @@ -57,9 +54,9 @@ PLATFORM_SCHEMA = vol.Schema({ vol.All(cv.ensure_list, [cv.string]), vol.Optional(CONF_NOGROUP_ALIASSES): vol.All(cv.ensure_list, [cv.string]), - }, - }), -}) + }) + }, +}, extra=vol.ALLOW_EXTRA) def entity_type_for_device_id(device_id): @@ -98,7 +95,7 @@ def entity_class_for_type(entity_type): return entity_device_mapping.get(entity_type, RflinkLight) -def devices_from_config(domain_config, hass=None): +def devices_from_config(domain_config): """Parse configuration and add Rflink light devices.""" devices = [] for device_id, config in domain_config[CONF_DEVICES].items(): @@ -124,40 +121,16 @@ def devices_from_config(domain_config, hass=None): "repetitions. Please set 'dimmable' or 'switchable' " "type explicitly in configuration", device_id) - device = entity_class(device_id, hass, **device_config) + device = entity_class(device_id, **device_config) devices.append(device) - # Register entity (and aliases) to listen to incoming rflink events - - # Device id and normal aliases respond to normal and group command - hass.data[DATA_ENTITY_LOOKUP][ - EVENT_KEY_COMMAND][device_id].append(device) - if config[CONF_GROUP]: - hass.data[DATA_ENTITY_GROUP_LOOKUP][ - EVENT_KEY_COMMAND][device_id].append(device) - for _id in get_deprecated(config, CONF_ALIASES, CONF_ALIASSES): - hass.data[DATA_ENTITY_LOOKUP][ - EVENT_KEY_COMMAND][_id].append(device) - hass.data[DATA_ENTITY_GROUP_LOOKUP][ - EVENT_KEY_COMMAND][_id].append(device) - # group_aliases only respond to group commands - for _id in get_deprecated( - config, CONF_GROUP_ALIASES, CONF_GROUP_ALIASSES): - hass.data[DATA_ENTITY_GROUP_LOOKUP][ - EVENT_KEY_COMMAND][_id].append(device) - # nogroup_aliases only respond to normal commands - for _id in get_deprecated( - config, CONF_NOGROUP_ALIASES, CONF_NOGROUP_ALIASSES): - hass.data[DATA_ENTITY_LOOKUP][ - EVENT_KEY_COMMAND][_id].append(device) - return devices async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Rflink light platform.""" - async_add_entities(devices_from_config(config, hass)) + async_add_entities(devices_from_config(config)) async def add_new_device(event): """Check if device is known, otherwise add to list of known devices.""" @@ -167,16 +140,9 @@ async def async_setup_platform(hass, config, async_add_entities, entity_class = entity_class_for_type(entity_type) device_config = config[CONF_DEVICE_DEFAULTS] - device = entity_class(device_id, hass, **device_config) + device = entity_class(device_id, initial_event=event, **device_config) async_add_entities([device]) - # Register entity to listen to incoming Rflink events - hass.data[DATA_ENTITY_LOOKUP][ - EVENT_KEY_COMMAND][device_id].append(device) - - # Schedule task to process event after entity is created - hass.async_add_job(device.handle_event, event) - if config[CONF_AUTOMATIC_ADD]: hass.data[DATA_DEVICE_REGISTER][EVENT_KEY_COMMAND] = add_new_device @@ -277,7 +243,7 @@ class ToggleRflinkLight(SwitchableRflinkDevice, Light): if command == 'on': # if the state is unknown or false, it gets set as true # if the state is true, it gets set as false - self._state = self._state in [STATE_UNKNOWN, False] + self._state = self._state in [None, False] async def async_turn_on(self, **kwargs): """Turn the device on.""" diff --git a/homeassistant/components/rflink.py b/homeassistant/components/rflink.py index a8aeca273d6..b75a14968cd 100644 --- a/homeassistant/components/rflink.py +++ b/homeassistant/components/rflink.py @@ -6,7 +6,6 @@ https://home-assistant.io/components/rflink/ """ import asyncio from collections import defaultdict -import functools as ft import logging import async_timeout @@ -14,7 +13,7 @@ import voluptuous as vol from homeassistant.const import ( ATTR_ENTITY_ID, CONF_COMMAND, CONF_HOST, CONF_PORT, - EVENT_HOMEASSISTANT_STOP, STATE_UNKNOWN) + EVENT_HOMEASSISTANT_STOP) from homeassistant.core import CoreState, callback from homeassistant.exceptions import HomeAssistantError import homeassistant.helpers.config_validation as cv @@ -68,6 +67,9 @@ DOMAIN = 'rflink' SERVICE_SEND_COMMAND = 'send_command' SIGNAL_AVAILABILITY = 'rflink_device_available' +SIGNAL_HANDLE_EVENT = 'rflink_handle_event_{}' + +TMP_ENTITY = 'tmp.{}' DEVICE_DEFAULTS_SCHEMA = vol.Schema({ vol.Optional(CONF_FIRE_EVENT, default=False): cv.boolean, @@ -153,28 +155,38 @@ async def async_setup(hass, config): return # Lookup entities who registered this device id as device id or alias - event_id = event.get('id', None) + event_id = event.get(EVENT_KEY_ID, None) is_group_event = (event_type == EVENT_KEY_COMMAND and event[EVENT_KEY_COMMAND] in RFLINK_GROUP_COMMANDS) if is_group_event: - entities = hass.data[DATA_ENTITY_GROUP_LOOKUP][event_type].get( + entity_ids = hass.data[DATA_ENTITY_GROUP_LOOKUP][event_type].get( event_id, []) else: - entities = hass.data[DATA_ENTITY_LOOKUP][event_type][event_id] + entity_ids = hass.data[DATA_ENTITY_LOOKUP][event_type][event_id] - if entities: + _LOGGER.debug('entity_ids: %s', entity_ids) + if entity_ids: # Propagate event to every entity matching the device id - for entity in entities: - _LOGGER.debug('passing event to %s', entities) - entity.handle_event(event) - else: - _LOGGER.debug('device_id not known, adding new device') - + for entity in entity_ids: + _LOGGER.debug('passing event to %s', entity) + async_dispatcher_send(hass, + SIGNAL_HANDLE_EVENT.format(entity), + event) + elif not is_group_event: # If device is not yet known, register with platform (if loaded) if event_type in hass.data[DATA_DEVICE_REGISTER]: - hass.async_run_job( - hass.data[DATA_DEVICE_REGISTER][event_type], event) + _LOGGER.debug('device_id not known, adding new device') + # Add bogus event_id first to avoid race if we get another + # event before the device is created + # Any additional events recevied before the device has been + # created will thus be ignored. + hass.data[DATA_ENTITY_LOOKUP][event_type][ + event_id].append(TMP_ENTITY.format(event_id)) + hass.async_create_task( + hass.data[DATA_DEVICE_REGISTER][event_type](event)) + else: + _LOGGER.debug('device_id not known and automatic add disabled') # When connecting to tcp host instead of serial port (optional) host = config[DOMAIN].get(CONF_HOST) @@ -192,7 +204,7 @@ async def async_setup(hass, config): # If HA is not stopping, initiate new connection if hass.state != CoreState.stopping: _LOGGER.warning('disconnected from Rflink, reconnecting') - hass.async_add_job(connect) + hass.async_create_task(connect()) async def connect(): """Set up connection and hook it into HA for reconnect/shutdown.""" @@ -242,7 +254,7 @@ async def async_setup(hass, config): _LOGGER.info('Connected to Rflink') - hass.async_add_job(connect) + hass.async_create_task(connect()) return True @@ -253,26 +265,31 @@ class RflinkDevice(Entity): """ platform = None - _state = STATE_UNKNOWN + _state = None _available = True - def __init__(self, device_id, hass, name=None, aliases=None, group=True, - group_aliases=None, nogroup_aliases=None, fire_event=False, + def __init__(self, device_id, initial_event=None, name=None, aliases=None, + group=True, group_aliases=None, nogroup_aliases=None, + fire_event=False, signal_repetitions=DEFAULT_SIGNAL_REPETITIONS): """Initialize the device.""" - self.hass = hass - # Rflink specific attributes for every component type + self._initial_event = initial_event self._device_id = device_id if name: self._name = name else: self._name = device_id + self._aliases = aliases + self._group = group + self._group_aliases = group_aliases + self._nogroup_aliases = nogroup_aliases self._should_fire_event = fire_event self._signal_repetitions = signal_repetitions - def handle_event(self, event): + @callback + def handle_event_callback(self, event): """Handle incoming event for device type.""" # Call platform specific event handler self._handle_event(event) @@ -283,7 +300,7 @@ class RflinkDevice(Entity): # Put command onto bus for user to subscribe to if self._should_fire_event and identify_event_type( event) == EVENT_KEY_COMMAND: - self.hass.bus.fire(EVENT_BUTTON_PRESSED, { + self.hass.bus.async_fire(EVENT_BUTTON_PRESSED, { ATTR_ENTITY_ID: self.entity_id, ATTR_STATE: event[EVENT_KEY_COMMAND], }) @@ -314,7 +331,7 @@ class RflinkDevice(Entity): @property def assumed_state(self): """Assume device state until first device event sets state.""" - return self._state is STATE_UNKNOWN + return self._state is None @property def available(self): @@ -322,15 +339,52 @@ class RflinkDevice(Entity): return self._available @callback - def set_availability(self, availability): + def _availability_callback(self, availability): """Update availability state.""" self._available = availability self.async_schedule_update_ha_state() async def async_added_to_hass(self): """Register update callback.""" + # Remove temporary bogus entity_id if added + tmp_entity = TMP_ENTITY.format(self._device_id) + if tmp_entity in self.hass.data[DATA_ENTITY_LOOKUP][ + EVENT_KEY_SENSOR][self._device_id]: + self.hass.data[DATA_ENTITY_LOOKUP][ + EVENT_KEY_SENSOR][self._device_id].remove(tmp_entity) + + # Register id and aliases + self.hass.data[DATA_ENTITY_LOOKUP][ + EVENT_KEY_COMMAND][self._device_id].append(self.entity_id) + if self._group: + self.hass.data[DATA_ENTITY_GROUP_LOOKUP][ + EVENT_KEY_COMMAND][self._device_id].append(self.entity_id) + # aliases respond to both normal and group commands (allon/alloff) + if self._aliases: + for _id in self._aliases: + self.hass.data[DATA_ENTITY_LOOKUP][ + EVENT_KEY_COMMAND][_id].append(self.entity_id) + self.hass.data[DATA_ENTITY_GROUP_LOOKUP][ + EVENT_KEY_COMMAND][_id].append(self.entity_id) + # group_aliases only respond to group commands (allon/alloff) + if self._group_aliases: + for _id in self._group_aliases: + self.hass.data[DATA_ENTITY_GROUP_LOOKUP][ + EVENT_KEY_COMMAND][_id].append(self.entity_id) + # nogroup_aliases only respond to normal commands + if self._nogroup_aliases: + for _id in self._nogroup_aliases: + self.hass.data[DATA_ENTITY_LOOKUP][ + EVENT_KEY_COMMAND][_id].append(self.entity_id) async_dispatcher_connect(self.hass, SIGNAL_AVAILABILITY, - self.set_availability) + self._availability_callback) + async_dispatcher_connect(self.hass, + SIGNAL_HANDLE_EVENT.format(self.entity_id), + self.handle_event_callback) + + # Process the initial event now that the entity is created + if self._initial_event: + self.handle_event_callback(self._initial_event) class RflinkCommand(RflinkDevice): @@ -388,7 +442,7 @@ class RflinkCommand(RflinkDevice): cmd = 'on' # if the state is unknown or false, it gets set as true # if the state is true, it gets set as false - self._state = self._state in [STATE_UNKNOWN, False] + self._state = self._state in [None, False] # Cover options for RFlink elif command == 'close_cover': @@ -439,8 +493,8 @@ class RflinkCommand(RflinkDevice): # Rflink protocol/transport handles asynchronous writing of buffer # to serial/tcp device. Does not wait for command send # confirmation. - self.hass.async_add_job(ft.partial( - self._protocol.send_command, self._device_id, cmd)) + self.hass.async_create_task(self._protocol.send_command( + self._device_id, cmd)) if repetitions > 1: self._repetition_task = self.hass.async_create_task( diff --git a/homeassistant/components/sensor/rflink.py b/homeassistant/components/sensor/rflink.py index c33c99c5828..a401eebeec6 100644 --- a/homeassistant/components/sensor/rflink.py +++ b/homeassistant/components/sensor/rflink.py @@ -4,16 +4,18 @@ Support for Rflink sensors. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/light.rflink/ """ -from functools import partial import logging from homeassistant.components.rflink import ( CONF_ALIASES, CONF_ALIASSES, CONF_AUTOMATIC_ADD, CONF_DEVICES, - DATA_DEVICE_REGISTER, DATA_ENTITY_LOOKUP, DOMAIN, EVENT_KEY_ID, - EVENT_KEY_SENSOR, EVENT_KEY_UNIT, RflinkDevice, cv, remove_deprecated, vol) + DATA_DEVICE_REGISTER, DATA_ENTITY_LOOKUP, EVENT_KEY_ID, + EVENT_KEY_SENSOR, EVENT_KEY_UNIT, RflinkDevice, cv, remove_deprecated, vol, + SIGNAL_AVAILABILITY, SIGNAL_HANDLE_EVENT, TMP_ENTITY) +from homeassistant.components.sensor import ( + PLATFORM_SCHEMA) from homeassistant.const import ( - ATTR_UNIT_OF_MEASUREMENT, CONF_NAME, CONF_PLATFORM, - CONF_UNIT_OF_MEASUREMENT) + ATTR_UNIT_OF_MEASUREMENT, CONF_NAME, CONF_UNIT_OF_MEASUREMENT) +from homeassistant.helpers.dispatcher import (async_dispatcher_connect) DEPENDENCIES = ['rflink'] @@ -27,11 +29,10 @@ SENSOR_ICONS = { CONF_SENSOR_TYPE = 'sensor_type' -PLATFORM_SCHEMA = vol.Schema({ - vol.Required(CONF_PLATFORM): DOMAIN, +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_AUTOMATIC_ADD, default=True): cv.boolean, - vol.Optional(CONF_DEVICES, default={}): vol.Schema({ - cv.string: { + vol.Optional(CONF_DEVICES, default={}): { + cv.string: vol.Schema({ vol.Optional(CONF_NAME): cv.string, vol.Required(CONF_SENSOR_TYPE): cv.string, vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string, @@ -40,9 +41,9 @@ PLATFORM_SCHEMA = vol.Schema({ # deprecated config options vol.Optional(CONF_ALIASSES): vol.All(cv.ensure_list, [cv.string]), - }, - }), -}) + }) + }, +}, extra=vol.ALLOW_EXTRA) def lookup_unit_for_sensor_type(sensor_type): @@ -56,7 +57,7 @@ def lookup_unit_for_sensor_type(sensor_type): return UNITS.get(field_abbrev.get(sensor_type)) -def devices_from_config(domain_config, hass=None): +def devices_from_config(domain_config): """Parse configuration and add Rflink sensor devices.""" devices = [] for device_id, config in domain_config[CONF_DEVICES].items(): @@ -64,41 +65,26 @@ def devices_from_config(domain_config, hass=None): config[ATTR_UNIT_OF_MEASUREMENT] = lookup_unit_for_sensor_type( config[CONF_SENSOR_TYPE]) remove_deprecated(config) - device = RflinkSensor(device_id, hass, **config) + device = RflinkSensor(device_id, **config) devices.append(device) - # Register entity (and aliases) to listen to incoming rflink events - hass.data[DATA_ENTITY_LOOKUP][ - EVENT_KEY_SENSOR][device_id].append(device) - aliases = config.get(CONF_ALIASES) - if aliases: - for _id in aliases: - hass.data[DATA_ENTITY_LOOKUP][ - EVENT_KEY_SENSOR][_id].append(device) return devices async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Rflink platform.""" - async_add_entities(devices_from_config(config, hass)) + async_add_entities(devices_from_config(config)) async def add_new_device(event): """Check if device is known, otherwise create device entity.""" device_id = event[EVENT_KEY_ID] - rflinksensor = partial(RflinkSensor, device_id, hass) - device = rflinksensor(event[EVENT_KEY_SENSOR], event[EVENT_KEY_UNIT]) + device = RflinkSensor(device_id, event[EVENT_KEY_SENSOR], + event[EVENT_KEY_UNIT], initial_event=event) # Add device entity async_add_entities([device]) - # Register entity to listen to incoming rflink events - hass.data[DATA_ENTITY_LOOKUP][ - EVENT_KEY_SENSOR][device_id].append(device) - - # Schedule task to process event after entity is created - hass.async_add_job(device.handle_event, event) - if config[CONF_AUTOMATIC_ADD]: hass.data[DATA_DEVICE_REGISTER][EVENT_KEY_SENSOR] = add_new_device @@ -106,17 +92,43 @@ async def async_setup_platform(hass, config, async_add_entities, class RflinkSensor(RflinkDevice): """Representation of a Rflink sensor.""" - def __init__(self, device_id, hass, sensor_type, unit_of_measurement, - **kwargs): + def __init__(self, device_id, sensor_type, unit_of_measurement, + initial_event=None, **kwargs): """Handle sensor specific args and super init.""" self._sensor_type = sensor_type self._unit_of_measurement = unit_of_measurement - super().__init__(device_id, hass, **kwargs) + super().__init__(device_id, initial_event=initial_event, **kwargs) def _handle_event(self, event): """Domain specific event handler.""" self._state = event['value'] + async def async_added_to_hass(self): + """Register update callback.""" + # Remove temporary bogus entity_id if added + tmp_entity = TMP_ENTITY.format(self._device_id) + if tmp_entity in self.hass.data[DATA_ENTITY_LOOKUP][ + EVENT_KEY_SENSOR][self._device_id]: + self.hass.data[DATA_ENTITY_LOOKUP][ + EVENT_KEY_SENSOR][self._device_id].remove(tmp_entity) + + # Register id and aliases + self.hass.data[DATA_ENTITY_LOOKUP][ + EVENT_KEY_SENSOR][self._device_id].append(self.entity_id) + if self._aliases: + for _id in self._aliases: + self.hass.data[DATA_ENTITY_LOOKUP][ + EVENT_KEY_SENSOR][_id].append(self.entity_id) + async_dispatcher_connect(self.hass, SIGNAL_AVAILABILITY, + self._availability_callback) + async_dispatcher_connect(self.hass, + SIGNAL_HANDLE_EVENT.format(self.entity_id), + self.handle_event_callback) + + # Process the initial event now that the entity is created + if self._initial_event: + self.handle_event_callback(self._initial_event) + @property def unit_of_measurement(self): """Return measurement unit.""" diff --git a/homeassistant/components/switch/rflink.py b/homeassistant/components/switch/rflink.py index 2bbe3e3f03d..1f217b1c39c 100644 --- a/homeassistant/components/switch/rflink.py +++ b/homeassistant/components/switch/rflink.py @@ -10,23 +10,22 @@ from homeassistant.components.rflink import ( CONF_ALIASES, CONF_ALIASSES, CONF_DEVICE_DEFAULTS, CONF_DEVICES, CONF_FIRE_EVENT, CONF_GROUP, CONF_GROUP_ALIASES, CONF_GROUP_ALIASSES, CONF_NOGROUP_ALIASES, CONF_NOGROUP_ALIASSES, CONF_SIGNAL_REPETITIONS, - DATA_ENTITY_GROUP_LOOKUP, DATA_ENTITY_LOOKUP, DEVICE_DEFAULTS_SCHEMA, - DOMAIN, EVENT_KEY_COMMAND, SwitchableRflinkDevice, cv, remove_deprecated, - vol) -from homeassistant.components.switch import SwitchDevice -from homeassistant.const import CONF_NAME, CONF_PLATFORM -from homeassistant.helpers.deprecation import get_deprecated + DEVICE_DEFAULTS_SCHEMA, SwitchableRflinkDevice, cv, + remove_deprecated, vol) +from homeassistant.components.switch import ( + PLATFORM_SCHEMA, SwitchDevice) + +from homeassistant.const import CONF_NAME DEPENDENCIES = ['rflink'] _LOGGER = logging.getLogger(__name__) -PLATFORM_SCHEMA = vol.Schema({ - vol.Required(CONF_PLATFORM): DOMAIN, +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_DEVICE_DEFAULTS, default=DEVICE_DEFAULTS_SCHEMA({})): - DEVICE_DEFAULTS_SCHEMA, - vol.Optional(CONF_DEVICES, default={}): vol.Schema({ - cv.string: { + DEVICE_DEFAULTS_SCHEMA, + vol.Optional(CONF_DEVICES, default={}): { + cv.string: vol.Schema({ vol.Optional(CONF_NAME): cv.string, vol.Optional(CONF_ALIASES, default=[]): vol.All(cv.ensure_list, [cv.string]), @@ -44,50 +43,27 @@ PLATFORM_SCHEMA = vol.Schema({ vol.All(cv.ensure_list, [cv.string]), vol.Optional(CONF_NOGROUP_ALIASSES): vol.All(cv.ensure_list, [cv.string]), - }, - }), -}) + }) + }, +}, extra=vol.ALLOW_EXTRA) -def devices_from_config(domain_config, hass=None): +def devices_from_config(domain_config): """Parse configuration and add Rflink switch devices.""" devices = [] for device_id, config in domain_config[CONF_DEVICES].items(): device_config = dict(domain_config[CONF_DEVICE_DEFAULTS], **config) remove_deprecated(device_config) - device = RflinkSwitch(device_id, hass, **device_config) + device = RflinkSwitch(device_id, **device_config) devices.append(device) - # Register entity (and aliases) to listen to incoming rflink events - # Device id and normal aliases respond to normal and group command - hass.data[DATA_ENTITY_LOOKUP][ - EVENT_KEY_COMMAND][device_id].append(device) - if config[CONF_GROUP]: - hass.data[DATA_ENTITY_GROUP_LOOKUP][ - EVENT_KEY_COMMAND][device_id].append(device) - for _id in get_deprecated(config, CONF_ALIASES, CONF_ALIASSES): - hass.data[DATA_ENTITY_LOOKUP][ - EVENT_KEY_COMMAND][_id].append(device) - hass.data[DATA_ENTITY_GROUP_LOOKUP][ - EVENT_KEY_COMMAND][_id].append(device) - # group_aliases only respond to group commands - for _id in get_deprecated( - config, CONF_GROUP_ALIASES, CONF_GROUP_ALIASSES): - hass.data[DATA_ENTITY_GROUP_LOOKUP][ - EVENT_KEY_COMMAND][_id].append(device) - # nogroup_aliases only respond to normal commands - for _id in get_deprecated( - config, CONF_NOGROUP_ALIASES, CONF_NOGROUP_ALIASSES): - hass.data[DATA_ENTITY_LOOKUP][ - EVENT_KEY_COMMAND][_id].append(device) - return devices async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Rflink platform.""" - async_add_entities(devices_from_config(config, hass)) + async_add_entities(devices_from_config(config)) class RflinkSwitch(SwitchableRflinkDevice, SwitchDevice):