diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index bc3c17e41da..8c490754f40 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -338,10 +338,9 @@ class AutomationEntity(ToggleEntity): yield from self.async_update_ha_state() @asyncio.coroutine - def async_remove(self): - """Remove automation from HASS.""" + def async_will_remove_from_hass(self): + """Remove listeners when removing automation from HASS.""" yield from self.async_turn_off() - yield from super().async_remove() @asyncio.coroutine def async_enable(self): diff --git a/homeassistant/components/binary_sensor/flic.py b/homeassistant/components/binary_sensor/flic.py index 51fffae5cc0..170f1818a0e 100644 --- a/homeassistant/components/binary_sensor/flic.py +++ b/homeassistant/components/binary_sensor/flic.py @@ -238,6 +238,5 @@ class FlicButton(BinarySensorDevice): import pyflic if connection_status == pyflic.ConnectionStatus.Disconnected: - _LOGGER.info("Button (%s) disconnected. Reason: %s", - self.address, disconnect_reason) - self.remove() + _LOGGER.warning("Button (%s) disconnected. Reason: %s", + self.address, disconnect_reason) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 6839c2c3b9c..1bb88050b2f 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -124,15 +124,15 @@ def async_setup(hass, config): """Set up the camera component.""" component = EntityComponent(_LOGGER, DOMAIN, hass, SCAN_INTERVAL) - hass.http.register_view(CameraImageView(component.entities)) - hass.http.register_view(CameraMjpegStream(component.entities)) + hass.http.register_view(CameraImageView(component)) + hass.http.register_view(CameraMjpegStream(component)) yield from component.async_setup(config) @callback def update_tokens(time): """Update tokens of the entities.""" - for entity in component.entities.values(): + for entity in component.entities: entity.async_update_token() hass.async_add_job(entity.async_update_ha_state()) @@ -358,14 +358,14 @@ class CameraView(HomeAssistantView): requires_auth = False - def __init__(self, entities): + def __init__(self, component): """Initialize a basic camera view.""" - self.entities = entities + self.component = component @asyncio.coroutine def get(self, request, entity_id): """Start a GET request.""" - camera = self.entities.get(entity_id) + camera = self.component.get_entity(entity_id) if camera is None: status = 404 if request[KEY_AUTHENTICATED] else 401 diff --git a/homeassistant/components/group/__init__.py b/homeassistant/components/group/__init__.py index 8b1e05e3122..a8529f18b69 100644 --- a/homeassistant/components/group/__init__.py +++ b/homeassistant/components/group/__init__.py @@ -42,8 +42,6 @@ ATTR_ORDER = 'order' ATTR_VIEW = 'view' ATTR_VISIBLE = 'visible' -DATA_ALL_GROUPS = 'data_all_groups' - SERVICE_SET_VISIBILITY = 'set_visibility' SERVICE_SET = 'set' SERVICE_REMOVE = 'remove' @@ -250,8 +248,10 @@ def get_entity_ids(hass, entity_id, domain_filter=None): @asyncio.coroutine def async_setup(hass, config): """Set up all groups found definded in the configuration.""" - component = EntityComponent(_LOGGER, DOMAIN, hass) - hass.data[DATA_ALL_GROUPS] = {} + component = hass.data.get(DOMAIN) + + if component is None: + component = hass.data[DOMAIN] = EntityComponent(_LOGGER, DOMAIN, hass) yield from _async_process_config(hass, config, component) @@ -271,10 +271,11 @@ def async_setup(hass, config): def groups_service_handler(service): """Handle dynamic group service functions.""" object_id = service.data[ATTR_OBJECT_ID] - service_groups = hass.data[DATA_ALL_GROUPS] + entity_id = ENTITY_ID_FORMAT.format(object_id) + group = component.get_entity(entity_id) # new group - if service.service == SERVICE_SET and object_id not in service_groups: + if service.service == SERVICE_SET and group is None: entity_ids = service.data.get(ATTR_ENTITIES) or \ service.data.get(ATTR_ADD_ENTITIES) or None @@ -289,12 +290,15 @@ def async_setup(hass, config): user_defined=False, **extra_arg ) + return + if group is None: + _LOGGER.warning("%s:Group '%s' doesn't exist!", + service.service, object_id) return # update group if service.service == SERVICE_SET: - group = service_groups[object_id] need_update = False if ATTR_ADD_ENTITIES in service.data: @@ -333,12 +337,7 @@ def async_setup(hass, config): # remove group if service.service == SERVICE_REMOVE: - if object_id not in service_groups: - _LOGGER.warning("Group '%s' doesn't exist!", object_id) - return - - del_group = service_groups.pop(object_id) - yield from del_group.async_stop() + yield from component.async_remove_entity(entity_id) hass.services.async_register( DOMAIN, SERVICE_SET, groups_service_handler, @@ -395,7 +394,7 @@ class Group(Entity): """Track a group of entity ids.""" def __init__(self, hass, name, order=None, visible=True, icon=None, - view=False, control=None, user_defined=True): + view=False, control=None, user_defined=True, entity_ids=None): """Initialize a group. This Object has factory function for creation. @@ -405,7 +404,10 @@ class Group(Entity): self._state = STATE_UNKNOWN self._icon = icon self.view = view - self.tracking = [] + if entity_ids: + self.tracking = tuple(ent_id.lower() for ent_id in entity_ids) + else: + self.tracking = tuple() self.group_on = None self.group_off = None self.visible = visible @@ -439,23 +441,21 @@ class Group(Entity): hass, name, order=len(hass.states.async_entity_ids(DOMAIN)), visible=visible, icon=icon, view=view, control=control, - user_defined=user_defined + user_defined=user_defined, entity_ids=entity_ids ) group.entity_id = async_generate_entity_id( ENTITY_ID_FORMAT, object_id or name, hass=hass) - # run other async stuff - if entity_ids is not None: - yield from group.async_update_tracked_entity_ids(entity_ids) - else: - yield from group.async_update_ha_state(True) - # If called before the platform async_setup is called (test cases) - if DATA_ALL_GROUPS not in hass.data: - hass.data[DATA_ALL_GROUPS] = {} + component = hass.data.get(DOMAIN) + + if component is None: + component = hass.data[DOMAIN] = \ + EntityComponent(_LOGGER, DOMAIN, hass) + + yield from component.async_add_entities([group], True) - hass.data[DATA_ALL_GROUPS][object_id] = group return group @property @@ -534,10 +534,6 @@ class Group(Entity): yield from self.async_update_ha_state(True) self.async_start() - def start(self): - """Start tracking members.""" - self.hass.add_job(self.async_start) - @callback def async_start(self): """Start tracking members. @@ -549,17 +545,15 @@ class Group(Entity): self.hass, self.tracking, self._async_state_changed_listener ) - def stop(self): - """Unregister the group from Home Assistant.""" - run_coroutine_threadsafe(self.async_stop(), self.hass.loop).result() - @asyncio.coroutine def async_stop(self): """Unregister the group from Home Assistant. This method must be run in the event loop. """ - yield from self.async_remove() + if self._async_unsub_state_changed: + self._async_unsub_state_changed() + self._async_unsub_state_changed = None @asyncio.coroutine def async_update(self): @@ -567,17 +561,19 @@ class Group(Entity): self._state = STATE_UNKNOWN self._async_update_group_state() - def async_remove(self): - """Remove group from HASS. + @asyncio.coroutine + def async_added_to_hass(self): + """Callback when added to HASS.""" + if self.tracking: + self.async_start() - This method must be run in the event loop and returns a coroutine. - """ + @asyncio.coroutine + def async_will_remove_from_hass(self): + """Callback when removed from HASS.""" if self._async_unsub_state_changed: self._async_unsub_state_changed() self._async_unsub_state_changed = None - return super().async_remove() - @asyncio.coroutine def _async_state_changed_listener(self, entity_id, old_state, new_state): """Respond to a member state changing. diff --git a/homeassistant/components/mailbox/__init__.py b/homeassistant/components/mailbox/__init__.py index 4f999649a44..a1e68555649 100644 --- a/homeassistant/components/mailbox/__init__.py +++ b/homeassistant/components/mailbox/__init__.py @@ -82,7 +82,7 @@ def async_setup(hass, config): mailbox_entity = MailboxEntity(hass, mailbox) component = EntityComponent( logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL) - yield from component.async_add_entity(mailbox_entity) + yield from component.async_add_entities([mailbox_entity]) setup_tasks = [async_setup_platform(p_type, p_config) for p_type, p_config in config_per_platform(config, DOMAIN)] diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index 44e6810fd5d..91bcb4d8af0 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -366,7 +366,7 @@ def async_setup(hass, config): component = EntityComponent( logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL) - hass.http.register_view(MediaPlayerImageView(component.entities)) + hass.http.register_view(MediaPlayerImageView(component)) yield from component.async_setup(config) @@ -929,14 +929,14 @@ class MediaPlayerImageView(HomeAssistantView): url = '/api/media_player_proxy/{entity_id}' name = 'api:media_player:image' - def __init__(self, entities): + def __init__(self, component): """Initialize a media player view.""" - self.entities = entities + self.component = component @asyncio.coroutine def get(self, request, entity_id): """Start a get request.""" - player = self.entities.get(entity_id) + player = self.component.get_entity(entity_id) if player is None: status = 404 if request[KEY_AUTHENTICATED] else 401 return web.Response(status=status) diff --git a/homeassistant/components/microsoft_face.py b/homeassistant/components/microsoft_face.py index 829c1124363..e61ed05ce10 100644 --- a/homeassistant/components/microsoft_face.py +++ b/homeassistant/components/microsoft_face.py @@ -161,7 +161,7 @@ def async_setup(hass, config): face.store.pop(g_id) entity = entities.pop(g_id) - yield from entity.async_remove() + hass.states.async_remove(entity.entity_id) except HomeAssistantError as err: _LOGGER.error("Can't delete group '%s' with error: %s", g_id, err) diff --git a/homeassistant/components/remember_the_milk/__init__.py b/homeassistant/components/remember_the_milk/__init__.py index 581380e3667..98cd937de3c 100644 --- a/homeassistant/components/remember_the_milk/__init__.py +++ b/homeassistant/components/remember_the_milk/__init__.py @@ -86,7 +86,7 @@ def _create_instance(hass, account_name, api_key, shared_secret, token, stored_rtm_config, component): entity = RememberTheMilk(account_name, api_key, shared_secret, token, stored_rtm_config) - component.add_entity(entity) + component.add_entities([entity]) hass.services.register( DOMAIN, '{}_create_task'.format(account_name), entity.create_task, schema=SERVICE_SCHEMA_CREATE_TASK) diff --git a/homeassistant/components/script.py b/homeassistant/components/script.py index 7be8bd8175e..a45f8ba8930 100644 --- a/homeassistant/components/script.py +++ b/homeassistant/components/script.py @@ -156,7 +156,7 @@ def _async_process_config(hass, config, component): def service_handler(service): """Execute a service call to script.