diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index a99113b6f6f..09f0e286755 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -29,6 +29,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.components.frontend import register_built_in_panel DOMAIN = 'automation' +DEPENDENCIES = ['group'] ENTITY_ID_FORMAT = DOMAIN + '.{}' GROUP_NAME_ALL_AUTOMATIONS = 'all automations' diff --git a/homeassistant/components/cover/__init__.py b/homeassistant/components/cover/__init__.py index f913d126c4a..d4e7d4b0db6 100644 --- a/homeassistant/components/cover/__init__.py +++ b/homeassistant/components/cover/__init__.py @@ -27,6 +27,7 @@ from homeassistant.const import ( _LOGGER = logging.getLogger(__name__) DOMAIN = 'cover' +DEPENDENCIES = ['group'] SCAN_INTERVAL = timedelta(seconds=15) GROUP_NAME_ALL_COVERS = 'all covers' diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index 7ef5b19487e..017bb723ee5 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -27,6 +27,7 @@ from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.restore_state import async_get_last_state from homeassistant.helpers.typing import GPSType, ConfigType, HomeAssistantType import homeassistant.helpers.config_validation as cv +from homeassistant.loader import get_component import homeassistant.util as util from homeassistant.util.async import run_coroutine_threadsafe import homeassistant.util.dt as dt_util @@ -41,7 +42,7 @@ from homeassistant.const import ( _LOGGER = logging.getLogger(__name__) DOMAIN = 'device_tracker' -DEPENDENCIES = ['zone'] +DEPENDENCIES = ['zone', 'group'] GROUP_NAME_ALL_DEVICES = 'all devices' ENTITY_ID_ALL_DEVICES = group.ENTITY_ID_FORMAT.format('all_devices') @@ -175,7 +176,7 @@ def async_setup(hass: HomeAssistantType, config: ConfigType): if setup_tasks: yield from asyncio.wait(setup_tasks, loop=hass.loop) - yield from tracker.async_setup_group() + tracker.async_setup_group() @callback def async_device_tracker_discovered(service, info): @@ -228,7 +229,7 @@ class DeviceTracker(object): self.mac_to_dev = {dev.mac: dev for dev in devices if dev.mac} self.consider_home = consider_home self.track_new = track_new - self.group = None # type: group.Group + self.group = None self._is_updating = asyncio.Lock(loop=hass.loop) for dev in devices: @@ -302,9 +303,10 @@ class DeviceTracker(object): }) # During init, we ignore the group - if self.group is not None: - yield from self.group.async_update_tracked_entity_ids( - list(self.group.tracking) + [device.entity_id]) + if self.group and self.track_new: + self.group.async_set_group( + self.hass, util.slugify(GROUP_NAME_ALL_DEVICES), visible=False, + name=GROUP_NAME_ALL_DEVICES, add=[device.entity_id]) # lookup mac vendor string to be stored in config yield from device.set_vendor_for_mac() @@ -326,16 +328,19 @@ class DeviceTracker(object): update_config, self.hass.config.path(YAML_DEVICES), dev_id, device) - @asyncio.coroutine + @callback def async_setup_group(self): """Initialize group for all tracked devices. - This method is a coroutine. + This method must be run in the event loop. """ - entity_ids = (dev.entity_id for dev in self.devices.values() - if dev.track) - self.group = yield from group.Group.async_create_group( - self.hass, GROUP_NAME_ALL_DEVICES, entity_ids, False) + entity_ids = [dev.entity_id for dev in self.devices.values() + if dev.track] + + self.group = get_component('group') + self.group.async_set_group( + self.hass, util.slugify(GROUP_NAME_ALL_DEVICES), visible=False, + name=GROUP_NAME_ALL_DEVICES, entity_ids=entity_ids) @callback def async_update_stale(self, now: dt_util.dt.datetime): diff --git a/homeassistant/components/fan/__init__.py b/homeassistant/components/fan/__init__.py index f2ade535426..4642017ce32 100644 --- a/homeassistant/components/fan/__init__.py +++ b/homeassistant/components/fan/__init__.py @@ -25,7 +25,7 @@ import homeassistant.helpers.config_validation as cv _LOGGER = logging.getLogger(__name__) DOMAIN = 'fan' - +DEPENDENCIES = ['group'] SCAN_INTERVAL = timedelta(seconds=30) GROUP_NAME_ALL_FANS = 'all fans' diff --git a/homeassistant/components/group.py b/homeassistant/components/group.py index f207a9b8b62..7c992a277f8 100644 --- a/homeassistant/components/group.py +++ b/homeassistant/components/group.py @@ -30,13 +30,23 @@ CONF_ENTITIES = 'entities' CONF_VIEW = 'view' CONF_CONTROL = 'control' +ATTR_ADD_ENTITIES = 'add_entities' ATTR_AUTO = 'auto' +ATTR_CONTROL = 'control' +ATTR_ENTITIES = 'entities' +ATTR_ICON = 'icon' +ATTR_NAME = 'name' +ATTR_OBJECT_ID = 'object_id' ATTR_ORDER = 'order' ATTR_VIEW = 'view' ATTR_VISIBLE = 'visible' -ATTR_CONTROL = 'control' SERVICE_SET_VISIBILITY = 'set_visibility' +SERVICE_SET = 'set' +SERVICE_REMOVE = 'remove' + +CONTROL_TYPES = vol.In(['hidden', None]) + SET_VISIBILITY_SERVICE_SCHEMA = vol.Schema({ vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, vol.Required(ATTR_VISIBLE): cv.boolean @@ -44,6 +54,21 @@ SET_VISIBILITY_SERVICE_SCHEMA = vol.Schema({ RELOAD_SERVICE_SCHEMA = vol.Schema({}) +SET_SERVICE_SCHEMA = vol.Schema({ + vol.Required(ATTR_OBJECT_ID): cv.slug, + vol.Optional(ATTR_NAME): cv.string, + vol.Optional(ATTR_VIEW): cv.boolean, + vol.Optional(ATTR_ICON): cv.string, + vol.Optional(ATTR_CONTROL): CONTROL_TYPES, + vol.Optional(ATTR_VISIBLE): cv.boolean, + vol.Exclusive(ATTR_ENTITIES, 'entities'): cv.entity_ids, + vol.Exclusive(ATTR_ADD_ENTITIES, 'entities'): cv.entity_ids, +}) + +REMOVE_SERVICE_SCHEMA = vol.Schema({ + vol.Required(ATTR_OBJECT_ID): cv.slug, +}) + _LOGGER = logging.getLogger(__name__) @@ -60,7 +85,7 @@ GROUP_SCHEMA = vol.Schema({ CONF_VIEW: cv.boolean, CONF_NAME: cv.string, CONF_ICON: cv.icon, - CONF_CONTROL: cv.string, + CONF_CONTROL: CONTROL_TYPES, }) CONFIG_SCHEMA = vol.Schema({ @@ -99,10 +124,10 @@ def reload(hass): hass.add_job(async_reload, hass) -@asyncio.coroutine +@callback def async_reload(hass): """Reload the automation from config.""" - yield from hass.services.async_call(DOMAIN, SERVICE_RELOAD) + hass.async_add_job(hass.services.async_call(DOMAIN, SERVICE_RELOAD)) def set_visibility(hass, entity_id=None, visible=True): @@ -111,6 +136,46 @@ def set_visibility(hass, entity_id=None, visible=True): hass.services.call(DOMAIN, SERVICE_SET_VISIBILITY, data) +def set_group(hass, object_id, name=None, entity_ids=None, visible=None, + icon=None, view=None, control=None, add=None): + """Create a new user group.""" + hass.add_job( + async_set_group, hass, object_id, name, entity_ids, visible, icon, + view, control, add) + + +@callback +def async_set_group(hass, object_id, name=None, entity_ids=None, visible=None, + icon=None, view=None, control=None, add=None): + """Create a new user group.""" + data = { + key: value for key, value in [ + (ATTR_OBJECT_ID, object_id), + (ATTR_NAME, name), + (ATTR_ENTITIES, entity_ids), + (ATTR_VISIBLE, visible), + (ATTR_ICON, icon), + (ATTR_VIEW, view), + (ATTR_CONTROL, control), + (ATTR_ADD_ENTITIES, add), + ] if value is not None + } + + hass.async_add_job(hass.services.async_call(DOMAIN, SERVICE_SET, data)) + + +def remove(hass, name): + """Remove a user group.""" + hass.add_job(async_remove, hass, name) + + +@callback +def async_remove(hass, object_id): + """Remove a user group.""" + data = {ATTR_OBJECT_ID: object_id} + hass.async_add_job(hass.services.async_call(DOMAIN, SERVICE_REMOVE, data)) + + def expand_entity_ids(hass, entity_ids): """Return entity_ids with group entity ids replaced by their members. @@ -170,6 +235,7 @@ def get_entity_ids(hass, entity_id, domain_filter=None): def async_setup(hass, config): """Set up all groups found definded in the configuration.""" component = EntityComponent(_LOGGER, DOMAIN, hass) + service_groups = {} yield from _async_process_config(hass, config, component) @@ -179,29 +245,116 @@ def async_setup(hass, config): ) @asyncio.coroutine - def reload_service_handler(service_call): + def reload_service_handler(service): """Remove all groups and load new ones from config.""" conf = yield from component.async_prepare_reload() if conf is None: return yield from _async_process_config(hass, conf, component) + hass.services.async_register( + DOMAIN, SERVICE_RELOAD, reload_service_handler, + descriptions[DOMAIN][SERVICE_RELOAD], schema=RELOAD_SERVICE_SCHEMA) + + @asyncio.coroutine + def groups_service_handler(service): + """Handle dynamic group service functions.""" + object_id = service.data[ATTR_OBJECT_ID] + + # new group + if service.service == SERVICE_SET and object_id not in service_groups: + entity_ids = service.data.get(ATTR_ENTITIES) or \ + service.data.get(ATTR_ADD_ENTITIES) or None + + extra_arg = {attr: service.data[attr] for attr in ( + ATTR_VISIBLE, ATTR_ICON, ATTR_VIEW, ATTR_CONTROL + ) if service.data.get(attr) is not None} + + new_group = yield from Group.async_create_group( + hass, service.data.get(ATTR_NAME, object_id), + object_id=object_id, + entity_ids=entity_ids, + user_defined=False, + **extra_arg + ) + + service_groups[object_id] = new_group + return + + # update group + if service.service == SERVICE_SET: + group = service_groups[object_id] + need_update = False + + if ATTR_ADD_ENTITIES in service.data: + delta = service.data[ATTR_ADD_ENTITIES] + entity_ids = set(group.tracking) | set(delta) + yield from group.async_update_tracked_entity_ids(entity_ids) + + if ATTR_ENTITIES in service.data: + entity_ids = service.data[ATTR_ENTITIES] + yield from group.async_update_tracked_entity_ids(entity_ids) + + if ATTR_NAME in service.data: + group.name = service.data[ATTR_NAME] + need_update = True + + if ATTR_VISIBLE in service.data: + group.visible = service.data[ATTR_VISIBLE] + need_update = True + + if ATTR_ICON in service.data: + group.icon = service.data[ATTR_ICON] + need_update = True + + if ATTR_CONTROL in service.data: + group.control = service.data[ATTR_CONTROL] + need_update = True + + if ATTR_VIEW in service.data: + group.view = service.data[ATTR_VIEW] + need_update = True + + if need_update: + yield from group.async_update_ha_state() + + return + + # remove group + if service.service == SERVICE_REMOVE: + if object_id not in service_groups: + _LOGGER.warning("Group '%s' not exists!", object_id) + return + + del_group = service_groups.pop(object_id) + yield from del_group.async_stop() + + hass.services.async_register( + DOMAIN, SERVICE_SET, groups_service_handler, + descriptions[DOMAIN][SERVICE_SET], schema=SET_SERVICE_SCHEMA) + + hass.services.async_register( + DOMAIN, SERVICE_REMOVE, groups_service_handler, + descriptions[DOMAIN][SERVICE_REMOVE], schema=REMOVE_SERVICE_SCHEMA) + @asyncio.coroutine def visibility_service_handler(service): """Change visibility of a group.""" visible = service.data.get(ATTR_VISIBLE) - tasks = [group.async_set_visible(visible) for group - in component.async_extract_from_service(service, - expand_group=False)] - yield from asyncio.wait(tasks, loop=hass.loop) + + tasks = [] + for group in component.async_extract_from_service(service, + expand_group=False): + group.visible = visible + tasks.append(group.async_update_ha_state()) + + if tasks: + yield from asyncio.wait(tasks, loop=hass.loop) hass.services.async_register( DOMAIN, SERVICE_SET_VISIBILITY, visibility_service_handler, descriptions[DOMAIN][SERVICE_SET_VISIBILITY], schema=SET_VISIBILITY_SERVICE_SCHEMA) - hass.services.async_register( - DOMAIN, SERVICE_RELOAD, reload_service_handler, - descriptions[DOMAIN][SERVICE_RELOAD], schema=RELOAD_SERVICE_SCHEMA) return True @@ -231,8 +384,8 @@ def _async_process_config(hass, config, component): class Group(Entity): """Track a group of entity ids.""" - def __init__(self, hass, name, order=None, user_defined=True, icon=None, - view=False, control=None): + def __init__(self, hass, name, order=None, visible=True, icon=None, + view=False, control=None, user_defined=True): """Initialize a group. This Object has factory function for creation. @@ -240,31 +393,33 @@ class Group(Entity): self.hass = hass self._name = name self._state = STATE_UNKNOWN - self._user_defined = user_defined - self._order = order self._icon = icon - self._view = view + self.view = view self.tracking = [] self.group_on = None self.group_off = None + self.visible = visible + self.control = control + self._user_defined = user_defined + self._order = order self._assumed_state = False self._async_unsub_state_changed = None - self._visible = True - self._control = control @staticmethod def create_group(hass, name, entity_ids=None, user_defined=True, - icon=None, view=False, control=None, object_id=None): + visible=True, icon=None, view=False, control=None, + object_id=None): """Initialize a group.""" return run_coroutine_threadsafe( - Group.async_create_group(hass, name, entity_ids, user_defined, - icon, view, control, object_id), + Group.async_create_group( + hass, name, entity_ids, user_defined, visible, icon, view, + control, object_id), hass.loop).result() @staticmethod @asyncio.coroutine def async_create_group(hass, name, entity_ids=None, user_defined=True, - icon=None, view=False, control=None, + visible=True, icon=None, view=False, control=None, object_id=None): """Initialize a group. @@ -273,8 +428,9 @@ class Group(Entity): group = Group( hass, name, order=len(hass.states.async_entity_ids(DOMAIN)), - user_defined=user_defined, icon=icon, view=view, - control=control) + visible=visible, icon=icon, view=view, control=control, + user_defined=user_defined + ) group.entity_id = async_generate_entity_id( ENTITY_ID_FORMAT, object_id or name, hass=hass) @@ -297,6 +453,11 @@ class Group(Entity): """Return the name of the group.""" return self._name + @name.setter + def name(self, value): + """Set Group name.""" + self._name = value + @property def state(self): """Return the state of the group.""" @@ -307,19 +468,16 @@ class Group(Entity): """Return the icon of the group.""" return self._icon - @asyncio.coroutine - def async_set_visible(self, visible): - """Change visibility of the group.""" - if self._visible != visible: - self._visible = visible - yield from self.async_update_ha_state() + @icon.setter + def icon(self, value): + """Set Icon for group.""" + self._icon = value @property def hidden(self): """If group should be hidden or not.""" - # Visibility from set_visibility service overrides - if self._visible: - return not self._user_defined or self._view + if self.visible and not self.view: + return False return True @property @@ -331,10 +489,10 @@ class Group(Entity): } if not self._user_defined: data[ATTR_AUTO] = True - if self._view: + if self.view: data[ATTR_VIEW] = True - if self._control: - data[ATTR_CONTROL] = self._control + if self.control: + data[ATTR_CONTROL] = self.control return data @property diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index 30a05215698..1dbd07f9439 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -26,6 +26,7 @@ from homeassistant.helpers.restore_state import async_restore_state import homeassistant.util.color as color_util DOMAIN = "light" +DEPENDENCIES = ['group'] SCAN_INTERVAL = timedelta(seconds=30) GROUP_NAME_ALL_LIGHTS = 'all lights' diff --git a/homeassistant/components/lock/__init__.py b/homeassistant/components/lock/__init__.py index 27af8d28764..369f0c93b85 100644 --- a/homeassistant/components/lock/__init__.py +++ b/homeassistant/components/lock/__init__.py @@ -25,6 +25,8 @@ from homeassistant.components import group ATTR_CHANGED_BY = 'changed_by' DOMAIN = 'lock' +DEPENDENCIES = ['group'] +SCAN_INTERVAL = timedelta(seconds=30) ENTITY_ID_ALL_LOCKS = group.ENTITY_ID_FORMAT.format('all_locks') ENTITY_ID_FORMAT = DOMAIN + '.{}' @@ -33,8 +35,6 @@ GROUP_NAME_ALL_LOCKS = 'all locks' MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) -SCAN_INTERVAL = timedelta(seconds=30) - LOCK_SERVICE_SCHEMA = vol.Schema({ vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, vol.Optional(ATTR_CODE): cv.string, diff --git a/homeassistant/components/remote/__init__.py b/homeassistant/components/remote/__init__.py index 40a419eaa58..4e00a053cf9 100755 --- a/homeassistant/components/remote/__init__.py +++ b/homeassistant/components/remote/__init__.py @@ -30,6 +30,8 @@ ATTR_NUM_REPEATS = 'num_repeats' ATTR_DELAY_SECS = 'delay_secs' DOMAIN = 'remote' +DEPENDENCIES = ['group'] +SCAN_INTERVAL = timedelta(seconds=30) ENTITY_ID_ALL_REMOTES = group.ENTITY_ID_FORMAT.format('all_remotes') ENTITY_ID_FORMAT = DOMAIN + '.{}' @@ -38,7 +40,6 @@ GROUP_NAME_ALL_REMOTES = 'all remotes' MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) -SCAN_INTERVAL = timedelta(seconds=30) SERVICE_SEND_COMMAND = 'send_command' SERVICE_SYNC = 'sync' diff --git a/homeassistant/components/script.py b/homeassistant/components/script.py index 6d2982dd262..996cef10d77 100644 --- a/homeassistant/components/script.py +++ b/homeassistant/components/script.py @@ -24,6 +24,9 @@ from homeassistant.helpers.script import Script _LOGGER = logging.getLogger(__name__) +DOMAIN = 'script' +DEPENDENCIES = ['group'] + ATTR_CAN_CANCEL = 'can_cancel' ATTR_LAST_ACTION = 'last_action' ATTR_LAST_TRIGGERED = 'last_triggered' @@ -31,8 +34,6 @@ ATTR_VARIABLES = 'variables' CONF_SEQUENCE = 'sequence' -DOMAIN = 'script' - ENTITY_ID_FORMAT = DOMAIN + '.{}' GROUP_NAME_ALL_SCRIPTS = 'all scripts' diff --git a/homeassistant/components/services.yaml b/homeassistant/components/services.yaml index 8c211a190c2..94b7002093f 100644 --- a/homeassistant/components/services.yaml +++ b/homeassistant/components/services.yaml @@ -42,7 +42,6 @@ foursquare: group: reload: description: "Reload group configuration." - fields: set_visibility: description: Hide or show a group @@ -56,6 +55,50 @@ group: description: True if group should be shown or False if it should be hidden. example: True + set: + description: Create/Update a user group + + fields: + object_id: + description: Group id and part of entity id + example: 'test_group' + + name: + description: Name of group + example: 'My test group' + + view: + description: Boolean for if the group is a view + example: True + + icon: + description: Name of icon for the group + example: 'mdi:camera' + + control: + description: Value for control the group control + example: 'hidden' + + visible: + description: If the group is visible on UI + example: True + + entities: + description: List of all members in the group. Not compatible with 'delta' + example: domain.entity_id1, domain.entity_id2 + + add_entities: + description: List of members they will change on group listening. + example: domain.entity_id1, domain.entity_id2 + + remove: + description: Remove a user group + + fields: + object_id: + description: Group id and part of entity id + example: 'test_group' + persistent_notification: create: description: Show a notification in the frontend diff --git a/homeassistant/components/switch/__init__.py b/homeassistant/components/switch/__init__.py index 0af5dcf4a5c..2af79a54313 100644 --- a/homeassistant/components/switch/__init__.py +++ b/homeassistant/components/switch/__init__.py @@ -23,6 +23,7 @@ from homeassistant.const import ( from homeassistant.components import group DOMAIN = 'switch' +DEPENDENCIES = ['group'] SCAN_INTERVAL = timedelta(seconds=30) GROUP_NAME_ALL_SWITCHES = 'all switches' diff --git a/homeassistant/helpers/entity_component.py b/homeassistant/helpers/entity_component.py index 08acfe66851..f7cf23b21fd 100644 --- a/homeassistant/helpers/entity_component.py +++ b/homeassistant/helpers/entity_component.py @@ -14,6 +14,7 @@ from homeassistant.helpers import config_per_platform, discovery from homeassistant.helpers.entity import async_generate_entity_id from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.service import extract_entity_ids +from homeassistant.util import slugify from homeassistant.util.async import ( run_callback_threadsafe, run_coroutine_threadsafe) @@ -37,8 +38,6 @@ class EntityComponent(object): self.group_name = group_name self.entities = {} - self.group = None - self.config = None self._platforms = { @@ -232,21 +231,19 @@ class EntityComponent(object): run_callback_threadsafe( self.hass.loop, self.async_update_group).result() - @asyncio.coroutine + @callback def async_update_group(self): """Set up and/or update component group. This method must be run in the event loop. """ - if self.group is None and self.group_name is not None: + if self.group_name is not None: + ids = sorted(self.entities, key=lambda x: self.entities[x].name) group = get_component('group') - self.group = yield from group.Group.async_create_group( - self.hass, self.group_name, - sorted(self.entities, key=lambda x: self.entities[x].name), - user_defined=False) - elif self.group is not None: - yield from self.group.async_update_tracked_entity_ids( - sorted(self.entities, key=lambda x: self.entities[x].name)) + group.async_set_group( + self.hass, slugify(self.group_name), name=self.group_name, + visible=False, entity_ids=ids + ) def reset(self): """Remove entities and reset the entity component to initial values.""" @@ -270,9 +267,9 @@ class EntityComponent(object): self.entities = {} self.config = None - if self.group is not None: - yield from self.group.async_stop() - self.group = None + if self.group_name is not None: + group = get_component('group') + group.async_remove(self.hass, slugify(self.group_name)) def prepare_reload(self): """Prepare reloading this entity component.""" @@ -375,7 +372,7 @@ class EntityPlatform(object): tasks = [async_process_entity(entity) for entity in new_entities] yield from asyncio.wait(tasks, loop=self.component.hass.loop) - yield from self.component.async_update_group() + self.component.async_update_group() if self._async_unsub_polling is not None or \ not any(entity.should_poll for entity diff --git a/tests/components/automation/test_init.py b/tests/components/automation/test_init.py index f67c572ae75..dfef66bf30e 100644 --- a/tests/components/automation/test_init.py +++ b/tests/components/automation/test_init.py @@ -32,7 +32,7 @@ class TestAutomation(unittest.TestCase): def test_service_data_not_a_dict(self): """Test service data not dict.""" - with assert_setup_component(0): + with assert_setup_component(0, automation.DOMAIN): assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { @@ -390,23 +390,7 @@ class TestAutomation(unittest.TestCase): self.hass.block_till_done() assert automation.is_on(self.hass, entity_id) - @patch('homeassistant.config.load_yaml_config_file', autospec=True, - return_value={ - automation.DOMAIN: { - 'alias': 'bye', - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event2', - }, - 'action': { - 'service': 'test.automation', - 'data_template': { - 'event': '{{ trigger.event.event_type }}' - } - } - } - }) - def test_reload_config_service(self, mock_load_yaml): + def test_reload_config_service(self): """Test the reload config service.""" assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { @@ -435,10 +419,25 @@ class TestAutomation(unittest.TestCase): assert len(self.calls) == 1 assert self.calls[0].data.get('event') == 'test_event' - automation.reload(self.hass) - self.hass.block_till_done() - # De-flake ?! - self.hass.block_till_done() + with patch('homeassistant.config.load_yaml_config_file', autospec=True, + return_value={ + automation.DOMAIN: { + 'alias': 'bye', + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event2', + }, + 'action': { + 'service': 'test.automation', + 'data_template': { + 'event': '{{ trigger.event.event_type }}' + } + } + }}): + automation.reload(self.hass) + self.hass.block_till_done() + # De-flake ?! + self.hass.block_till_done() assert self.hass.states.get('automation.hello') is None assert self.hass.states.get('automation.bye') is not None @@ -455,11 +454,9 @@ class TestAutomation(unittest.TestCase): assert len(self.calls) == 2 assert self.calls[1].data.get('event') == 'test_event2' - @patch('homeassistant.config.load_yaml_config_file', autospec=True, - return_value={automation.DOMAIN: 'not valid'}) - def test_reload_config_when_invalid_config(self, mock_load_yaml): + def test_reload_config_when_invalid_config(self): """Test the reload config service handling invalid config.""" - with assert_setup_component(1): + with assert_setup_component(1, automation.DOMAIN): assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'alias': 'hello', @@ -483,8 +480,10 @@ class TestAutomation(unittest.TestCase): assert len(self.calls) == 1 assert self.calls[0].data.get('event') == 'test_event' - automation.reload(self.hass) - self.hass.block_till_done() + with patch('homeassistant.config.load_yaml_config_file', autospec=True, + return_value={automation.DOMAIN: 'not valid'}): + automation.reload(self.hass) + self.hass.block_till_done() assert self.hass.states.get('automation.hello') is None diff --git a/tests/components/device_tracker/test_asuswrt.py b/tests/components/device_tracker/test_asuswrt.py index 0de5ac67a30..b507bfea7c9 100644 --- a/tests/components/device_tracker/test_asuswrt.py +++ b/tests/components/device_tracker/test_asuswrt.py @@ -57,7 +57,7 @@ class TestComponentsDeviceTrackerASUSWRT(unittest.TestCase): def test_password_or_pub_key_required(self): \ # pylint: disable=invalid-name """Test creating an AsusWRT scanner without a pass or pubkey.""" - with assert_setup_component(0): + with assert_setup_component(0, DOMAIN): assert setup_component( self.hass, DOMAIN, {DOMAIN: { CONF_PLATFORM: 'asuswrt', @@ -82,7 +82,7 @@ class TestComponentsDeviceTrackerASUSWRT(unittest.TestCase): } } - with assert_setup_component(1): + with assert_setup_component(1, DOMAIN): assert setup_component(self.hass, DOMAIN, conf_dict) conf_dict[DOMAIN][CONF_MODE] = 'router' @@ -108,7 +108,7 @@ class TestComponentsDeviceTrackerASUSWRT(unittest.TestCase): } } - with assert_setup_component(1): + with assert_setup_component(1, DOMAIN): assert setup_component(self.hass, DOMAIN, conf_dict) conf_dict[DOMAIN][CONF_MODE] = 'router' @@ -192,7 +192,7 @@ class TestComponentsDeviceTrackerASUSWRT(unittest.TestCase): update_mock.start() self.addCleanup(update_mock.stop) - with assert_setup_component(0): + with assert_setup_component(0, DOMAIN): assert setup_component(self.hass, DOMAIN, {DOMAIN: conf_dict}) ssh.login.assert_not_called() @@ -264,7 +264,7 @@ class TestComponentsDeviceTrackerASUSWRT(unittest.TestCase): update_mock.start() self.addCleanup(update_mock.stop) - with assert_setup_component(0): + with assert_setup_component(0, DOMAIN): assert setup_component(self.hass, DOMAIN, {DOMAIN: conf_dict}) telnet.login.assert_not_called() diff --git a/tests/components/device_tracker/test_ddwrt.py b/tests/components/device_tracker/test_ddwrt.py index d55bc4e2ae1..c66029b5fca 100644 --- a/tests/components/device_tracker/test_ddwrt.py +++ b/tests/components/device_tracker/test_ddwrt.py @@ -57,7 +57,7 @@ class TestDdwrt(unittest.TestCase): mock_request.register_uri( 'GET', r'http://%s/Status_Wireless.live.asp' % TEST_HOST, status_code=401) - with assert_setup_component(1): + with assert_setup_component(1, DOMAIN): assert setup_component( self.hass, DOMAIN, {DOMAIN: { CONF_PLATFORM: 'ddwrt', @@ -77,7 +77,7 @@ class TestDdwrt(unittest.TestCase): mock_request.register_uri( 'GET', r'http://%s/Status_Wireless.live.asp' % TEST_HOST, status_code=444) - with assert_setup_component(1): + with assert_setup_component(1, DOMAIN): assert setup_component( self.hass, DOMAIN, {DOMAIN: { CONF_PLATFORM: 'ddwrt', @@ -95,7 +95,7 @@ class TestDdwrt(unittest.TestCase): 'ddwrt.DdWrtDeviceScanner.get_ddwrt_data', return_value=None) def test_no_response(self, data_mock, error_mock): """Create a Ddwrt scanner with no response in init, should fail.""" - with assert_setup_component(1): + with assert_setup_component(1, DOMAIN): assert setup_component( self.hass, DOMAIN, {DOMAIN: { CONF_PLATFORM: 'ddwrt', @@ -112,7 +112,7 @@ class TestDdwrt(unittest.TestCase): @mock.patch('homeassistant.components.device_tracker.ddwrt._LOGGER.error') def test_get_timeout(self, mock_error, mock_request): """Test get Ddwrt data with request time out.""" - with assert_setup_component(1): + with assert_setup_component(1, DOMAIN): assert setup_component( self.hass, DOMAIN, {DOMAIN: { CONF_PLATFORM: 'ddwrt', @@ -140,7 +140,7 @@ class TestDdwrt(unittest.TestCase): 'GET', r'http://%s/Status_Lan.live.asp' % TEST_HOST, text=load_fixture('Ddwrt_Status_Lan.txt')) - with assert_setup_component(1): + with assert_setup_component(1, DOMAIN): assert setup_component( self.hass, DOMAIN, {DOMAIN: { CONF_PLATFORM: 'ddwrt', @@ -169,7 +169,7 @@ class TestDdwrt(unittest.TestCase): mock_request.register_uri( 'GET', r'http://%s/Status_Lan.live.asp' % TEST_HOST, text=None) - with assert_setup_component(1): + with assert_setup_component(1, DOMAIN): assert setup_component( self.hass, DOMAIN, {DOMAIN: { CONF_PLATFORM: 'ddwrt', @@ -198,7 +198,7 @@ class TestDdwrt(unittest.TestCase): text=load_fixture('Ddwrt_Status_Lan.txt'). replace('dhcp_leases', 'missing')) - with assert_setup_component(1): + with assert_setup_component(1, DOMAIN): assert setup_component( self.hass, DOMAIN, {DOMAIN: { CONF_PLATFORM: 'ddwrt', @@ -229,7 +229,7 @@ class TestDdwrt(unittest.TestCase): 'GET', r'http://%s/Status_Lan.live.asp' % TEST_HOST, text=load_fixture('Ddwrt_Status_Lan.txt')) - with assert_setup_component(1): + with assert_setup_component(1, DOMAIN): assert setup_component( self.hass, DOMAIN, {DOMAIN: { CONF_PLATFORM: 'ddwrt', @@ -249,7 +249,7 @@ class TestDdwrt(unittest.TestCase): 'GET', r'http://%s/Status_Lan.live.asp' % TEST_HOST, text=load_fixture('Ddwrt_Status_Lan.txt')) - with assert_setup_component(1): + with assert_setup_component(1, DOMAIN): assert setup_component( self.hass, DOMAIN, {DOMAIN: { CONF_PLATFORM: 'ddwrt', diff --git a/tests/components/device_tracker/test_init.py b/tests/components/device_tracker/test_init.py index 968800ceafa..92cb84cba2f 100644 --- a/tests/components/device_tracker/test_init.py +++ b/tests/components/device_tracker/test_init.py @@ -429,6 +429,7 @@ class TestComponentsDeviceTracker(unittest.TestCase): with assert_setup_component(1, device_tracker.DOMAIN): assert setup_component(self.hass, device_tracker.DOMAIN, TEST_PLATFORM) + self.hass.block_till_done() state = self.hass.states.get(device_tracker.ENTITY_ID_ALL_DEVICES) self.assertIsNotNone(state) diff --git a/tests/components/device_tracker/test_upc_connect.py b/tests/components/device_tracker/test_upc_connect.py index 87e84c000d0..dea53b16559 100644 --- a/tests/components/device_tracker/test_upc_connect.py +++ b/tests/components/device_tracker/test_upc_connect.py @@ -58,7 +58,7 @@ class TestUPCConnect(object): content=b'successful' ) - with assert_setup_component(1): + with assert_setup_component(1, DOMAIN): assert setup_component( self.hass, DOMAIN, {DOMAIN: { CONF_PLATFORM: 'upc_connect', @@ -84,7 +84,7 @@ class TestUPCConnect(object): status=404 ) - with assert_setup_component(1): + with assert_setup_component(1, DOMAIN): assert setup_component( self.hass, DOMAIN, {DOMAIN: { CONF_PLATFORM: 'upc_connect', @@ -114,7 +114,7 @@ class TestUPCConnect(object): exc=asyncio.TimeoutError() ) - with assert_setup_component(1): + with assert_setup_component(1, DOMAIN): assert setup_component( self.hass, DOMAIN, {DOMAIN: { CONF_PLATFORM: 'upc_connect', @@ -143,7 +143,7 @@ class TestUPCConnect(object): content=b'successful', ) - with assert_setup_component(1): + with assert_setup_component(1, DOMAIN): assert setup_component( self.hass, DOMAIN, {DOMAIN: { CONF_PLATFORM: 'upc_connect', diff --git a/tests/components/light/test_mqtt.py b/tests/components/light/test_mqtt.py index 41df5f0d48c..d3e56794712 100644 --- a/tests/components/light/test_mqtt.py +++ b/tests/components/light/test_mqtt.py @@ -151,7 +151,7 @@ class TestLightMQTT(unittest.TestCase): def test_fail_setup_if_no_command_topic(self): """Test if command fails with command topic.""" - with assert_setup_component(0): + with assert_setup_component(0, light.DOMAIN): assert setup_component(self.hass, light.DOMAIN, { light.DOMAIN: { 'platform': 'mqtt', @@ -163,7 +163,7 @@ class TestLightMQTT(unittest.TestCase): def test_no_color_brightness_color_temp_white_xy_if_no_topics(self): \ # pylint: disable=invalid-name """Test if there is no color and brightness if no topic.""" - with assert_setup_component(1): + with assert_setup_component(1, light.DOMAIN): assert setup_component(self.hass, light.DOMAIN, { light.DOMAIN: { 'platform': 'mqtt', @@ -217,7 +217,7 @@ class TestLightMQTT(unittest.TestCase): 'payload_off': 0 }} - with assert_setup_component(1): + with assert_setup_component(1, light.DOMAIN): assert setup_component(self.hass, light.DOMAIN, config) state = self.hass.states.get('light.test') @@ -301,7 +301,7 @@ class TestLightMQTT(unittest.TestCase): def test_brightness_controlling_scale(self): """Test the brightness controlling scale.""" - with assert_setup_component(1): + with assert_setup_component(1, light.DOMAIN): assert setup_component(self.hass, light.DOMAIN, { light.DOMAIN: { 'platform': 'mqtt', @@ -348,7 +348,7 @@ class TestLightMQTT(unittest.TestCase): def test_white_value_controlling_scale(self): """Test the white_value controlling scale.""" - with assert_setup_component(1): + with assert_setup_component(1, light.DOMAIN): assert setup_component(self.hass, light.DOMAIN, { light.DOMAIN: { 'platform': 'mqtt', @@ -416,7 +416,7 @@ class TestLightMQTT(unittest.TestCase): 'xy_value_template': '{{ value_json.hello | join(",") }}', }} - with assert_setup_component(1): + with assert_setup_component(1, light.DOMAIN): assert setup_component(self.hass, light.DOMAIN, config) state = self.hass.states.get('light.test') @@ -467,7 +467,7 @@ class TestLightMQTT(unittest.TestCase): 'payload_off': 'off' }} - with assert_setup_component(1): + with assert_setup_component(1, light.DOMAIN): assert setup_component(self.hass, light.DOMAIN, config) state = self.hass.states.get('light.test') @@ -522,7 +522,7 @@ class TestLightMQTT(unittest.TestCase): 'state_topic': 'test_light_rgb/status', }} - with assert_setup_component(1): + with assert_setup_component(1, light.DOMAIN): assert setup_component(self.hass, light.DOMAIN, config) state = self.hass.states.get('light.test') @@ -546,7 +546,7 @@ class TestLightMQTT(unittest.TestCase): 'state_topic': 'test_light_rgb/status' }} - with assert_setup_component(1): + with assert_setup_component(1, light.DOMAIN): assert setup_component(self.hass, light.DOMAIN, config) state = self.hass.states.get('light.test') @@ -570,7 +570,7 @@ class TestLightMQTT(unittest.TestCase): 'state_topic': 'test_light_rgb/status' }} - with assert_setup_component(1): + with assert_setup_component(1, light.DOMAIN): assert setup_component(self.hass, light.DOMAIN, config) state = self.hass.states.get('light.test') @@ -594,7 +594,7 @@ class TestLightMQTT(unittest.TestCase): 'state_topic': 'test_light_rgb/status', }} - with assert_setup_component(1): + with assert_setup_component(1, light.DOMAIN): assert setup_component(self.hass, light.DOMAIN, config) state = self.hass.states.get('light.test') @@ -618,7 +618,7 @@ class TestLightMQTT(unittest.TestCase): 'state_topic': 'test_light_rgb/status', }} - with assert_setup_component(1): + with assert_setup_component(1, light.DOMAIN): assert setup_component(self.hass, light.DOMAIN, config) state = self.hass.states.get('light.test') diff --git a/tests/components/light/test_mqtt_json.py b/tests/components/light/test_mqtt_json.py index 172eb8ca178..d3cbf2bda00 100755 --- a/tests/components/light/test_mqtt_json.py +++ b/tests/components/light/test_mqtt_json.py @@ -104,7 +104,7 @@ class TestLightMQTTJSON(unittest.TestCase): def test_fail_setup_if_no_command_topic(self): \ # pylint: disable=invalid-name """Test if setup fails with no command topic.""" - with assert_setup_component(0): + with assert_setup_component(0, light.DOMAIN): assert setup_component(self.hass, light.DOMAIN, { light.DOMAIN: { 'platform': 'mqtt_json', diff --git a/tests/components/light/test_mqtt_template.py b/tests/components/light/test_mqtt_template.py index 3c11d11326d..99a91f8f6cc 100755 --- a/tests/components/light/test_mqtt_template.py +++ b/tests/components/light/test_mqtt_template.py @@ -51,7 +51,7 @@ class TestLightMQTTTemplate(unittest.TestCase): def test_setup_fails(self): \ # pylint: disable=invalid-name """Test that setup fails with missing required configuration items.""" - with assert_setup_component(0): + with assert_setup_component(0, light.DOMAIN): assert setup_component(self.hass, light.DOMAIN, { light.DOMAIN: { 'platform': 'mqtt_template', @@ -63,7 +63,7 @@ class TestLightMQTTTemplate(unittest.TestCase): def test_state_change_via_topic(self): \ # pylint: disable=invalid-name """Test state change via topic.""" - with assert_setup_component(1): + with assert_setup_component(1, light.DOMAIN): assert setup_component(self.hass, light.DOMAIN, { light.DOMAIN: { 'platform': 'mqtt_template', @@ -103,7 +103,7 @@ class TestLightMQTTTemplate(unittest.TestCase): def test_state_brightness_color_effect_temp_white_change_via_topic(self): \ # pylint: disable=invalid-name """Test state, bri, color, effect, color temp, white val change.""" - with assert_setup_component(1): + with assert_setup_component(1, light.DOMAIN): assert setup_component(self.hass, light.DOMAIN, { light.DOMAIN: { 'platform': 'mqtt_template', @@ -205,7 +205,7 @@ class TestLightMQTTTemplate(unittest.TestCase): def test_optimistic(self): \ # pylint: disable=invalid-name """Test optimistic mode.""" - with assert_setup_component(1): + with assert_setup_component(1, light.DOMAIN): assert setup_component(self.hass, light.DOMAIN, { light.DOMAIN: { 'platform': 'mqtt_template', @@ -278,7 +278,7 @@ class TestLightMQTTTemplate(unittest.TestCase): def test_flash(self): \ # pylint: disable=invalid-name """Test flash.""" - with assert_setup_component(1): + with assert_setup_component(1, light.DOMAIN): assert setup_component(self.hass, light.DOMAIN, { light.DOMAIN: { 'platform': 'mqtt_template', @@ -321,7 +321,7 @@ class TestLightMQTTTemplate(unittest.TestCase): def test_transition(self): """Test for transition time being sent when included.""" - with assert_setup_component(1): + with assert_setup_component(1, light.DOMAIN): assert setup_component(self.hass, light.DOMAIN, { light.DOMAIN: { 'platform': 'mqtt_template', @@ -364,7 +364,7 @@ class TestLightMQTTTemplate(unittest.TestCase): def test_invalid_values(self): \ # pylint: disable=invalid-name """Test that invalid values are ignored.""" - with assert_setup_component(1): + with assert_setup_component(1, light.DOMAIN): assert setup_component(self.hass, light.DOMAIN, { light.DOMAIN: { 'platform': 'mqtt_template', diff --git a/tests/components/light/test_template.py b/tests/components/light/test_template.py index 4abee754547..6564d66299b 100644 --- a/tests/components/light/test_template.py +++ b/tests/components/light/test_template.py @@ -39,7 +39,7 @@ class TestTemplateLight: def test_template_state_text(self): """"Test the state text of a template.""" - with assert_setup_component(1): + with assert_setup_component(1, 'light'): assert setup.setup_component(self.hass, 'light', { 'light': { 'platform': 'template', @@ -84,7 +84,7 @@ class TestTemplateLight: def test_template_state_boolean_on(self): """Test the setting of the state with boolean on.""" - with assert_setup_component(1): + with assert_setup_component(1, 'light'): assert setup.setup_component(self.hass, 'light', { 'light': { 'platform': 'template', @@ -119,7 +119,7 @@ class TestTemplateLight: def test_template_state_boolean_off(self): """Test the setting of the state with off.""" - with assert_setup_component(1): + with assert_setup_component(1, 'light'): assert setup.setup_component(self.hass, 'light', { 'light': { 'platform': 'template', @@ -154,7 +154,7 @@ class TestTemplateLight: def test_template_syntax_error(self): """Test templating syntax error.""" - with assert_setup_component(0): + with assert_setup_component(0, 'light'): assert setup.setup_component(self.hass, 'light', { 'light': { 'platform': 'template', @@ -188,7 +188,7 @@ class TestTemplateLight: def test_invalid_name_does_not_create(self): """Test invalid name.""" - with assert_setup_component(0): + with assert_setup_component(0, 'light'): assert setup.setup_component(self.hass, 'light', { 'light': { 'platform': 'template', @@ -222,7 +222,7 @@ class TestTemplateLight: def test_invalid_light_does_not_create(self): """Test invalid light.""" - with assert_setup_component(0): + with assert_setup_component(0, 'light'): assert setup.setup_component(self.hass, 'light', { 'light': { 'platform': 'template', @@ -239,7 +239,7 @@ class TestTemplateLight: def test_no_lights_does_not_create(self): """Test if there are no lights no creation.""" - with assert_setup_component(0): + with assert_setup_component(0, 'light'): assert setup.setup_component(self.hass, 'light', { 'light': { 'platform': 'template' @@ -253,7 +253,7 @@ class TestTemplateLight: def test_missing_template_does_create(self): """Test missing template.""" - with assert_setup_component(1): + with assert_setup_component(1, 'light'): assert setup.setup_component(self.hass, 'light', { 'light': { 'platform': 'template', @@ -286,7 +286,7 @@ class TestTemplateLight: def test_missing_on_does_not_create(self): """Test missing on.""" - with assert_setup_component(0): + with assert_setup_component(0, 'light'): assert setup.setup_component(self.hass, 'light', { 'light': { 'platform': 'template', @@ -316,7 +316,7 @@ class TestTemplateLight: def test_missing_off_does_not_create(self): """Test missing off.""" - with assert_setup_component(0): + with assert_setup_component(0, 'light'): assert setup.setup_component(self.hass, 'light', { 'light': { 'platform': 'template', @@ -553,7 +553,7 @@ class TestTemplateLight: def test_level_template(self): """Test the template for the level.""" - with assert_setup_component(1): + with assert_setup_component(1, 'light'): assert setup.setup_component(self.hass, 'light', { 'light': { 'platform': 'template', diff --git a/tests/components/switch/test_template.py b/tests/components/switch/test_template.py index e14eea01189..0ef3d505e5a 100644 --- a/tests/components/switch/test_template.py +++ b/tests/components/switch/test_template.py @@ -36,7 +36,7 @@ class TestTemplateSwitch: def test_template_state_text(self): """"Test the state text of a template.""" - with assert_setup_component(1): + with assert_setup_component(1, 'switch'): assert setup.setup_component(self.hass, 'switch', { 'switch': { 'platform': 'template', @@ -74,7 +74,7 @@ class TestTemplateSwitch: def test_template_state_boolean_on(self): """Test the setting of the state with boolean on.""" - with assert_setup_component(1): + with assert_setup_component(1, 'switch'): assert setup.setup_component(self.hass, 'switch', { 'switch': { 'platform': 'template', @@ -103,7 +103,7 @@ class TestTemplateSwitch: def test_template_state_boolean_off(self): """Test the setting of the state with off.""" - with assert_setup_component(1): + with assert_setup_component(1, 'switch'): assert setup.setup_component(self.hass, 'switch', { 'switch': { 'platform': 'template', @@ -132,7 +132,7 @@ class TestTemplateSwitch: def test_icon_template(self): """Test icon template.""" - with assert_setup_component(1): + with assert_setup_component(1, 'switch'): assert setup.setup_component(self.hass, 'switch', { 'switch': { 'platform': 'template', @@ -171,7 +171,7 @@ class TestTemplateSwitch: def test_template_syntax_error(self): """Test templating syntax error.""" - with assert_setup_component(0): + with assert_setup_component(0, 'switch'): assert setup.setup_component(self.hass, 'switch', { 'switch': { 'platform': 'template', @@ -199,7 +199,7 @@ class TestTemplateSwitch: def test_invalid_name_does_not_create(self): """Test invalid name.""" - with assert_setup_component(0): + with assert_setup_component(0, 'switch'): assert setup.setup_component(self.hass, 'switch', { 'switch': { 'platform': 'template', @@ -227,7 +227,7 @@ class TestTemplateSwitch: def test_invalid_switch_does_not_create(self): """Test invalid switch.""" - with assert_setup_component(0): + with assert_setup_component(0, 'switch'): assert setup.setup_component(self.hass, 'switch', { 'switch': { 'platform': 'template', @@ -244,7 +244,7 @@ class TestTemplateSwitch: def test_no_switches_does_not_create(self): """Test if there are no switches no creation.""" - with assert_setup_component(0): + with assert_setup_component(0, 'switch'): assert setup.setup_component(self.hass, 'switch', { 'switch': { 'platform': 'template' @@ -258,7 +258,7 @@ class TestTemplateSwitch: def test_missing_template_does_not_create(self): """Test missing template.""" - with assert_setup_component(0): + with assert_setup_component(0, 'switch'): assert setup.setup_component(self.hass, 'switch', { 'switch': { 'platform': 'template', @@ -286,7 +286,7 @@ class TestTemplateSwitch: def test_missing_on_does_not_create(self): """Test missing on.""" - with assert_setup_component(0): + with assert_setup_component(0, 'switch'): assert setup.setup_component(self.hass, 'switch', { 'switch': { 'platform': 'template', @@ -314,7 +314,7 @@ class TestTemplateSwitch: def test_missing_off_does_not_create(self): """Test missing off.""" - with assert_setup_component(0): + with assert_setup_component(0, 'switch'): assert setup.setup_component(self.hass, 'switch', { 'switch': { 'platform': 'template', diff --git a/tests/components/test_group.py b/tests/components/test_group.py index af1cadc2466..c9c413c30c1 100644 --- a/tests/components/test_group.py +++ b/tests/components/test_group.py @@ -1,16 +1,17 @@ """The tests for the Group components.""" # pylint: disable=protected-access +import asyncio from collections import OrderedDict import unittest from unittest.mock import patch -from homeassistant.setup import setup_component +from homeassistant.setup import setup_component, async_setup_component from homeassistant.const import ( STATE_ON, STATE_OFF, STATE_HOME, STATE_UNKNOWN, ATTR_ICON, ATTR_HIDDEN, - ATTR_ASSUMED_STATE, STATE_NOT_HOME, ) + ATTR_ASSUMED_STATE, STATE_NOT_HOME) import homeassistant.components.group as group -from tests.common import get_test_home_assistant +from tests.common import get_test_home_assistant, assert_setup_component class TestComponentsGroup(unittest.TestCase): @@ -380,3 +381,67 @@ class TestComponentsGroup(unittest.TestCase): self.hass.block_till_done() group_state = self.hass.states.get(group_entity_id) self.assertIsNone(group_state.attributes.get(ATTR_HIDDEN)) + + +@asyncio.coroutine +def test_service_group_services(hass): + """Check if service are available.""" + with assert_setup_component(0, 'group'): + yield from async_setup_component(hass, 'group', { + 'group': {} + }) + + assert hass.services.has_service('group', group.SERVICE_SET) + assert hass.services.has_service('group', group.SERVICE_REMOVE) + + +@asyncio.coroutine +def test_service_group_set_group_remove_group(hass): + """Check if service are available.""" + with assert_setup_component(0, 'group'): + yield from async_setup_component(hass, 'group', { + 'group': {} + }) + + group.async_set_group(hass, 'user_test_group', name="Test") + yield from hass.async_block_till_done() + + group_state = hass.states.get('group.user_test_group') + assert group_state + assert group_state.attributes[group.ATTR_AUTO] + assert group_state.attributes['friendly_name'] == "Test" + + group.async_set_group( + hass, 'user_test_group', view=True, visible=False, + entity_ids=['test.entity_bla1']) + yield from hass.async_block_till_done() + + group_state = hass.states.get('group.user_test_group') + assert group_state + assert group_state.attributes[group.ATTR_VIEW] + assert group_state.attributes[group.ATTR_AUTO] + assert group_state.attributes['hidden'] + assert group_state.attributes['friendly_name'] == "Test" + assert list(group_state.attributes['entity_id']) == ['test.entity_bla1'] + + group.async_set_group( + hass, 'user_test_group', icon="mdi:camera", name="Test2", + control="hidden", add=['test.entity_id2']) + yield from hass.async_block_till_done() + + group_state = hass.states.get('group.user_test_group') + assert group_state + assert group_state.attributes[group.ATTR_VIEW] + assert group_state.attributes[group.ATTR_AUTO] + assert group_state.attributes['hidden'] + assert group_state.attributes['friendly_name'] == "Test2" + assert group_state.attributes['icon'] == "mdi:camera" + assert group_state.attributes[group.ATTR_CONTROL] == "hidden" + assert sorted(list(group_state.attributes['entity_id'])) == sorted([ + 'test.entity_bla1', 'test.entity_id2']) + + group.async_remove(hass, 'user_test_group') + yield from hass.async_block_till_done() + + group_state = hass.states.get('group.user_test_group') + assert group_state is None diff --git a/tests/components/test_script.py b/tests/components/test_script.py index e333e6dd482..fcb0047c135 100644 --- a/tests/components/test_script.py +++ b/tests/components/test_script.py @@ -7,7 +7,7 @@ from homeassistant.core import callback from homeassistant.setup import setup_component from homeassistant.components import script -from tests.common import get_test_home_assistant, mock_component +from tests.common import get_test_home_assistant ENTITY_ID = 'script.test' @@ -20,7 +20,6 @@ class TestScriptComponent(unittest.TestCase): def setUp(self): """Setup things to be run when tests are started.""" self.hass = get_test_home_assistant() - mock_component(self.hass, 'group') # pylint: disable=invalid-name def tearDown(self): diff --git a/tests/helpers/test_entity_component.py b/tests/helpers/test_entity_component.py index 566306d7fe7..530e2662083 100644 --- a/tests/helpers/test_entity_component.py +++ b/tests/helpers/test_entity_component.py @@ -14,6 +14,7 @@ from homeassistant.helpers.entity import Entity, generate_entity_id from homeassistant.helpers.entity_component import ( EntityComponent, DEFAULT_SCAN_INTERVAL, SLOW_SETUP_WARNING) from homeassistant.helpers import entity_component +from homeassistant.setup import setup_component from homeassistant.helpers import discovery import homeassistant.util.dt as dt_util @@ -76,6 +77,7 @@ class TestHelpersEntityComponent(unittest.TestCase): def test_setting_up_group(self): """Setup the setting of a group.""" + setup_component(self.hass, 'group', {'group': {}}) component = EntityComponent(_LOGGER, DOMAIN, self.hass, group_name='everyone') diff --git a/tests/scripts/test_check_config.py b/tests/scripts/test_check_config.py index e27bb99fa59..5ad4ec8cdb3 100644 --- a/tests/scripts/test_check_config.py +++ b/tests/scripts/test_check_config.py @@ -68,7 +68,7 @@ class TestCheckConfig(unittest.TestCase): res = check_config.check(get_test_config_dir('light.yaml')) change_yaml_files(res) self.assertDictEqual({ - 'components': {'light': [{'platform': 'demo'}]}, + 'components': {'light': [{'platform': 'demo'}], 'group': None}, 'except': {}, 'secret_cache': {}, 'secrets': {}, @@ -110,7 +110,8 @@ class TestCheckConfig(unittest.TestCase): 'discovery_prefix': 'homeassistant', 'tls_version': 'auto', }, - 'light': []}, + 'light': [], + 'group': None}, res['components'] ) self.assertDictEqual( @@ -142,7 +143,7 @@ class TestCheckConfig(unittest.TestCase): res = check_config.check(get_test_config_dir('badplatform.yaml')) change_yaml_files(res) - assert res['components'] == {'light': []} + assert res['components'] == {'light': [], 'group': None} assert res['except'] == { check_config.ERROR_STR: [ 'Platform not found: light.beer', diff --git a/tests/test_setup.py b/tests/test_setup.py index 9d29961da10..291dfdd741f 100644 --- a/tests/test_setup.py +++ b/tests/test_setup.py @@ -296,7 +296,7 @@ class TestSetup: MockPlatform(platform_schema=platform_schema, setup_platform=mock_setup)) - with assert_setup_component(0): + with assert_setup_component(0, 'switch'): assert setup.setup_component(self.hass, 'switch', { 'switch': { 'platform': 'platform_a',