From 8ea0a8d40b2471166bf419b3dbed4b7efbcb4bd9 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 13 Dec 2018 10:07:59 +0100 Subject: [PATCH] RFC: Deprecate auto target all for services and introduce entity_id: * (#19006) * Deprecate auto target all * Match on word 'all' --- .../alarm_control_panel/__init__.py | 2 +- .../components/automation/__init__.py | 4 +-- homeassistant/components/camera/__init__.py | 2 +- homeassistant/components/climate/__init__.py | 18 +++++----- homeassistant/components/counter/__init__.py | 2 +- homeassistant/components/cover/__init__.py | 2 +- homeassistant/components/fan/__init__.py | 12 +++---- homeassistant/components/group/__init__.py | 2 +- .../components/image_processing/__init__.py | 2 +- homeassistant/components/light/__init__.py | 6 ++-- homeassistant/components/lock/__init__.py | 2 +- .../components/media_player/__init__.py | 2 +- homeassistant/components/remote/__init__.py | 2 +- homeassistant/components/scene/__init__.py | 2 +- homeassistant/components/switch/__init__.py | 2 +- homeassistant/components/vacuum/__init__.py | 2 +- .../components/water_heater/__init__.py | 8 ++--- homeassistant/const.py | 3 ++ homeassistant/helpers/config_validation.py | 9 ++++- homeassistant/helpers/entity_component.py | 12 +++++-- homeassistant/helpers/service.py | 9 +++-- tests/helpers/test_config_validation.py | 13 +++++++ tests/helpers/test_entity_component.py | 34 +++++++++++++++++++ tests/helpers/test_service.py | 32 +++++++++++++++++ 24 files changed, 143 insertions(+), 41 deletions(-) diff --git a/homeassistant/components/alarm_control_panel/__init__.py b/homeassistant/components/alarm_control_panel/__init__.py index a42e6e880b5..ad8520118b4 100644 --- a/homeassistant/components/alarm_control_panel/__init__.py +++ b/homeassistant/components/alarm_control_panel/__init__.py @@ -25,7 +25,7 @@ ATTR_CHANGED_BY = 'changed_by' ENTITY_ID_FORMAT = DOMAIN + '.{}' ALARM_SERVICE_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids, vol.Optional(ATTR_CODE): cv.string, }) diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index 4a2df399e0a..d062376f2a8 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -94,11 +94,11 @@ PLATFORM_SCHEMA = vol.Schema({ }) SERVICE_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids, }) TRIGGER_SERVICE_SCHEMA = vol.Schema({ - vol.Required(ATTR_ENTITY_ID): cv.entity_ids, + vol.Required(ATTR_ENTITY_ID): cv.comp_entity_ids, vol.Optional(ATTR_VARIABLES, default={}): dict, }) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 0463b172d7a..653d0315ad4 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -61,7 +61,7 @@ FALLBACK_STREAM_INTERVAL = 1 # seconds MIN_STREAM_INTERVAL = 0.5 # seconds CAMERA_SERVICE_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids, }) CAMERA_SERVICE_SNAPSHOT = CAMERA_SERVICE_SCHEMA.extend({ diff --git a/homeassistant/components/climate/__init__.py b/homeassistant/components/climate/__init__.py index 4b73e24fb41..d116a885319 100644 --- a/homeassistant/components/climate/__init__.py +++ b/homeassistant/components/climate/__init__.py @@ -92,15 +92,15 @@ CONVERTIBLE_ATTRIBUTE = [ _LOGGER = logging.getLogger(__name__) ON_OFF_SERVICE_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids, }) SET_AWAY_MODE_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids, vol.Required(ATTR_AWAY_MODE): cv.boolean, }) SET_AUX_HEAT_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids, vol.Required(ATTR_AUX_HEAT): cv.boolean, }) SET_TEMPERATURE_SCHEMA = vol.Schema(vol.All( @@ -110,28 +110,28 @@ SET_TEMPERATURE_SCHEMA = vol.Schema(vol.All( vol.Exclusive(ATTR_TEMPERATURE, 'temperature'): vol.Coerce(float), vol.Inclusive(ATTR_TARGET_TEMP_HIGH, 'temperature'): vol.Coerce(float), vol.Inclusive(ATTR_TARGET_TEMP_LOW, 'temperature'): vol.Coerce(float), - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids, vol.Optional(ATTR_OPERATION_MODE): cv.string, } )) SET_FAN_MODE_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids, vol.Required(ATTR_FAN_MODE): cv.string, }) SET_HOLD_MODE_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids, vol.Required(ATTR_HOLD_MODE): cv.string, }) SET_OPERATION_MODE_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids, vol.Required(ATTR_OPERATION_MODE): cv.string, }) SET_HUMIDITY_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids, vol.Required(ATTR_HUMIDITY): vol.Coerce(float), }) SET_SWING_MODE_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids, vol.Required(ATTR_SWING_MODE): cv.string, }) diff --git a/homeassistant/components/counter/__init__.py b/homeassistant/components/counter/__init__.py index 228870489a2..cd3a29df2b6 100644 --- a/homeassistant/components/counter/__init__.py +++ b/homeassistant/components/counter/__init__.py @@ -33,7 +33,7 @@ SERVICE_INCREMENT = 'increment' SERVICE_RESET = 'reset' SERVICE_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids, }) CONFIG_SCHEMA = vol.Schema({ diff --git a/homeassistant/components/cover/__init__.py b/homeassistant/components/cover/__init__.py index ec11b139f6b..ef8fcc42302 100644 --- a/homeassistant/components/cover/__init__.py +++ b/homeassistant/components/cover/__init__.py @@ -60,7 +60,7 @@ INTENT_OPEN_COVER = 'HassOpenCover' INTENT_CLOSE_COVER = 'HassCloseCover' COVER_SERVICE_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids, }) COVER_SET_COVER_POSITION_SCHEMA = COVER_SERVICE_SCHEMA.extend({ diff --git a/homeassistant/components/fan/__init__.py b/homeassistant/components/fan/__init__.py index 36b075747e0..a54d52f4b12 100644 --- a/homeassistant/components/fan/__init__.py +++ b/homeassistant/components/fan/__init__.py @@ -61,30 +61,30 @@ PROP_TO_ATTR = { } # type: dict FAN_SET_SPEED_SCHEMA = vol.Schema({ - vol.Required(ATTR_ENTITY_ID): cv.entity_ids, + vol.Required(ATTR_ENTITY_ID): cv.comp_entity_ids, vol.Required(ATTR_SPEED): cv.string }) # type: dict FAN_TURN_ON_SCHEMA = vol.Schema({ - vol.Required(ATTR_ENTITY_ID): cv.entity_ids, + vol.Required(ATTR_ENTITY_ID): cv.comp_entity_ids, vol.Optional(ATTR_SPEED): cv.string }) # type: dict FAN_TURN_OFF_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids + vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids }) # type: dict FAN_OSCILLATE_SCHEMA = vol.Schema({ - vol.Required(ATTR_ENTITY_ID): cv.entity_ids, + vol.Required(ATTR_ENTITY_ID): cv.comp_entity_ids, vol.Required(ATTR_OSCILLATING): cv.boolean }) # type: dict FAN_TOGGLE_SCHEMA = vol.Schema({ - vol.Required(ATTR_ENTITY_ID): cv.entity_ids + vol.Required(ATTR_ENTITY_ID): cv.comp_entity_ids }) FAN_SET_DIRECTION_SCHEMA = vol.Schema({ - vol.Required(ATTR_ENTITY_ID): cv.entity_ids, + vol.Required(ATTR_ENTITY_ID): cv.comp_entity_ids, vol.Optional(ATTR_DIRECTION): cv.string }) # type: dict diff --git a/homeassistant/components/group/__init__.py b/homeassistant/components/group/__init__.py index 15a3816c559..b6dcd65fc2c 100644 --- a/homeassistant/components/group/__init__.py +++ b/homeassistant/components/group/__init__.py @@ -49,7 +49,7 @@ SERVICE_REMOVE = 'remove' CONTROL_TYPES = vol.In(['hidden', None]) SET_VISIBILITY_SERVICE_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids, vol.Required(ATTR_VISIBLE): cv.boolean }) diff --git a/homeassistant/components/image_processing/__init__.py b/homeassistant/components/image_processing/__init__.py index 72a4a8155e2..b2cbb2b2391 100644 --- a/homeassistant/components/image_processing/__init__.py +++ b/homeassistant/components/image_processing/__init__.py @@ -62,7 +62,7 @@ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ }) SERVICE_SCAN_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids, }) diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index 41dbbcd6d0c..aa12e562515 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -88,7 +88,7 @@ VALID_BRIGHTNESS = vol.All(vol.Coerce(int), vol.Clamp(min=0, max=255)) VALID_BRIGHTNESS_PCT = vol.All(vol.Coerce(float), vol.Range(min=0, max=100)) LIGHT_TURN_ON_SCHEMA = vol.Schema({ - ATTR_ENTITY_ID: cv.entity_ids, + ATTR_ENTITY_ID: cv.comp_entity_ids, vol.Exclusive(ATTR_PROFILE, COLOR_GROUP): cv.string, ATTR_TRANSITION: VALID_TRANSITION, ATTR_BRIGHTNESS: VALID_BRIGHTNESS, @@ -115,13 +115,13 @@ LIGHT_TURN_ON_SCHEMA = vol.Schema({ }) LIGHT_TURN_OFF_SCHEMA = vol.Schema({ - ATTR_ENTITY_ID: cv.entity_ids, + ATTR_ENTITY_ID: cv.comp_entity_ids, ATTR_TRANSITION: VALID_TRANSITION, ATTR_FLASH: vol.In([FLASH_SHORT, FLASH_LONG]), }) LIGHT_TOGGLE_SCHEMA = vol.Schema({ - ATTR_ENTITY_ID: cv.entity_ids, + ATTR_ENTITY_ID: cv.comp_entity_ids, ATTR_TRANSITION: VALID_TRANSITION, }) diff --git a/homeassistant/components/lock/__init__.py b/homeassistant/components/lock/__init__.py index 22923602dc2..72e87f763d2 100644 --- a/homeassistant/components/lock/__init__.py +++ b/homeassistant/components/lock/__init__.py @@ -34,7 +34,7 @@ GROUP_NAME_ALL_LOCKS = 'all locks' MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) LOCK_SERVICE_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids, vol.Optional(ATTR_CODE): cv.string, }) diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index 8530a01d3e6..cd109cce7d3 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -117,7 +117,7 @@ SUPPORT_SELECT_SOUND_MODE = 65536 # Service call validation schemas MEDIA_PLAYER_SCHEMA = vol.Schema({ - ATTR_ENTITY_ID: cv.entity_ids, + ATTR_ENTITY_ID: cv.comp_entity_ids, }) MEDIA_PLAYER_SET_VOLUME_SCHEMA = MEDIA_PLAYER_SCHEMA.extend({ diff --git a/homeassistant/components/remote/__init__.py b/homeassistant/components/remote/__init__.py index 4fc491e57e8..162cb41d92e 100644 --- a/homeassistant/components/remote/__init__.py +++ b/homeassistant/components/remote/__init__.py @@ -46,7 +46,7 @@ DEFAULT_NUM_REPEATS = 1 DEFAULT_DELAY_SECS = 0.4 REMOTE_SERVICE_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids, }) REMOTE_SERVICE_ACTIVITY_SCHEMA = REMOTE_SERVICE_SCHEMA.extend({ diff --git a/homeassistant/components/scene/__init__.py b/homeassistant/components/scene/__init__.py index 2bcb1c8e16d..b3ab5228875 100644 --- a/homeassistant/components/scene/__init__.py +++ b/homeassistant/components/scene/__init__.py @@ -56,7 +56,7 @@ PLATFORM_SCHEMA = vol.Schema( ), extra=vol.ALLOW_EXTRA) SCENE_SERVICE_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Required(ATTR_ENTITY_ID): cv.entity_ids, }) diff --git a/homeassistant/components/switch/__init__.py b/homeassistant/components/switch/__init__.py index 1adabe4b57e..513ebbcb5ea 100644 --- a/homeassistant/components/switch/__init__.py +++ b/homeassistant/components/switch/__init__.py @@ -39,7 +39,7 @@ PROP_TO_ATTR = { } SWITCH_SERVICE_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids, }) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/vacuum/__init__.py b/homeassistant/components/vacuum/__init__.py index 212e6bd648f..4799e945be0 100644 --- a/homeassistant/components/vacuum/__init__.py +++ b/homeassistant/components/vacuum/__init__.py @@ -49,7 +49,7 @@ SERVICE_PAUSE = 'pause' SERVICE_STOP = 'stop' VACUUM_SERVICE_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids, }) VACUUM_SET_FAN_SPEED_SERVICE_SCHEMA = VACUUM_SERVICE_SCHEMA.extend({ diff --git a/homeassistant/components/water_heater/__init__.py b/homeassistant/components/water_heater/__init__.py index 92dbebc4421..fee2846e8d5 100644 --- a/homeassistant/components/water_heater/__init__.py +++ b/homeassistant/components/water_heater/__init__.py @@ -57,22 +57,22 @@ CONVERTIBLE_ATTRIBUTE = [ _LOGGER = logging.getLogger(__name__) ON_OFF_SERVICE_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids, }) SET_AWAY_MODE_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids, vol.Required(ATTR_AWAY_MODE): cv.boolean, }) SET_TEMPERATURE_SCHEMA = vol.Schema(vol.All( { vol.Required(ATTR_TEMPERATURE, 'temperature'): vol.Coerce(float), - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids, vol.Optional(ATTR_OPERATION_MODE): cv.string, } )) SET_OPERATION_MODE_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids, vol.Required(ATTR_OPERATION_MODE): cv.string, }) diff --git a/homeassistant/const.py b/homeassistant/const.py index 3f24da30a0a..a03b5fe52bc 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -13,6 +13,9 @@ PLATFORM_FORMAT = '{}.{}' # Can be used to specify a catch all when registering state or event listeners. MATCH_ALL = '*' +# Entity target all constant +ENTITY_MATCH_ALL = 'all' + # If no name is specified DEVICE_DEFAULT_NAME = 'Unnamed Device' diff --git a/homeassistant/helpers/config_validation.py b/homeassistant/helpers/config_validation.py index 5c49a1b50e1..c14f4e4fadb 100644 --- a/homeassistant/helpers/config_validation.py +++ b/homeassistant/helpers/config_validation.py @@ -15,7 +15,8 @@ from homeassistant.const import ( CONF_PLATFORM, CONF_SCAN_INTERVAL, TEMP_CELSIUS, TEMP_FAHRENHEIT, CONF_ALIAS, CONF_ENTITY_ID, CONF_VALUE_TEMPLATE, WEEKDAYS, CONF_CONDITION, CONF_BELOW, CONF_ABOVE, CONF_TIMEOUT, SUN_EVENT_SUNSET, - SUN_EVENT_SUNRISE, CONF_UNIT_SYSTEM_IMPERIAL, CONF_UNIT_SYSTEM_METRIC) + SUN_EVENT_SUNRISE, CONF_UNIT_SYSTEM_IMPERIAL, CONF_UNIT_SYSTEM_METRIC, + ENTITY_MATCH_ALL) from homeassistant.core import valid_entity_id, split_entity_id from homeassistant.exceptions import TemplateError import homeassistant.util.dt as dt_util @@ -161,6 +162,12 @@ def entity_ids(value: Union[str, Sequence]) -> Sequence[str]: return [entity_id(ent_id) for ent_id in value] +comp_entity_ids = vol.Any( + vol.All(vol.Lower, ENTITY_MATCH_ALL), + entity_ids +) + + def entity_domain(domain: str): """Validate that entity belong to domain.""" def validate(value: Any) -> str: diff --git a/homeassistant/helpers/entity_component.py b/homeassistant/helpers/entity_component.py index 982c92510a9..ce876991097 100644 --- a/homeassistant/helpers/entity_component.py +++ b/homeassistant/helpers/entity_component.py @@ -7,7 +7,7 @@ import logging from homeassistant import config as conf_util from homeassistant.setup import async_prepare_setup_platform from homeassistant.const import ( - ATTR_ENTITY_ID, CONF_SCAN_INTERVAL, CONF_ENTITY_NAMESPACE) + ATTR_ENTITY_ID, CONF_SCAN_INTERVAL, CONF_ENTITY_NAMESPACE, MATCH_ALL) from homeassistant.core import callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import config_per_platform, discovery @@ -161,7 +161,15 @@ class EntityComponent: This method must be run in the event loop. """ - if ATTR_ENTITY_ID not in service.data: + data_ent_id = service.data.get(ATTR_ENTITY_ID) + + if data_ent_id in (None, MATCH_ALL): + if data_ent_id is None: + self.logger.warning( + 'Not passing an entity ID to a service to target all ' + 'entities is deprecated. Update your call to %s.%s to be ' + 'instead: entity_id: "*"', service.domain, service.service) + return [entity for entity in self.entities if entity.available] entity_ids = set(extract_entity_ids(self.hass, service, expand_group)) diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index e8068f57286..f51d0f8b248 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -6,7 +6,7 @@ from os import path import voluptuous as vol from homeassistant.auth.permissions.const import POLICY_CONTROL -from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.const import ATTR_ENTITY_ID, ENTITY_MATCH_ALL import homeassistant.core as ha from homeassistant.exceptions import TemplateError, Unauthorized, UnknownUser from homeassistant.helpers import template @@ -197,7 +197,12 @@ async def entity_service_call(hass, platforms, func, call): entity_perms = None # Are we trying to target all entities - target_all_entities = ATTR_ENTITY_ID not in call.data + if ATTR_ENTITY_ID in call.data: + target_all_entities = call.data[ATTR_ENTITY_ID] == ENTITY_MATCH_ALL + else: + _LOGGER.warning('Not passing an entity ID to a service to target all ' + 'entities is deprecated. Use instead: entity_id: "*"') + target_all_entities = True if not target_all_entities: # A set of entities we're trying to target. diff --git a/tests/helpers/test_config_validation.py b/tests/helpers/test_config_validation.py index cfd84dbc3b3..412882f0a01 100644 --- a/tests/helpers/test_config_validation.py +++ b/tests/helpers/test_config_validation.py @@ -584,3 +584,16 @@ def test_is_regex(): valid_re = ".*" schema(valid_re) + + +def test_comp_entity_ids(): + """Test config validation for component entity IDs.""" + schema = vol.Schema(cv.comp_entity_ids) + + for valid in ('ALL', 'all', 'AlL', 'light.kitchen', ['light.kitchen'], + ['light.kitchen', 'light.ceiling'], []): + schema(valid) + + for invalid in (['light.kitchen', 'not-entity-id'], '*', ''): + with pytest.raises(vol.Invalid): + schema(invalid) diff --git a/tests/helpers/test_entity_component.py b/tests/helpers/test_entity_component.py index 7562a38d268..8f54f0ee5bc 100644 --- a/tests/helpers/test_entity_component.py +++ b/tests/helpers/test_entity_component.py @@ -452,3 +452,37 @@ async def test_set_service_race(hass): await hass.async_block_till_done() assert not exception + + +async def test_extract_all_omit_entity_id(hass, caplog): + """Test extract all with None and *.""" + component = EntityComponent(_LOGGER, DOMAIN, hass) + await component.async_add_entities([ + MockEntity(name='test_1'), + MockEntity(name='test_2'), + ]) + + call = ha.ServiceCall('test', 'service') + + assert ['test_domain.test_1', 'test_domain.test_2'] == \ + sorted(ent.entity_id for ent in + component.async_extract_from_service(call)) + assert ('Not passing an entity ID to a service to target all entities is ' + 'deprecated') in caplog.text + + +async def test_extract_all_use_match_all(hass, caplog): + """Test extract all with None and *.""" + component = EntityComponent(_LOGGER, DOMAIN, hass) + await component.async_add_entities([ + MockEntity(name='test_1'), + MockEntity(name='test_2'), + ]) + + call = ha.ServiceCall('test', 'service', {'entity_id': '*'}) + + assert ['test_domain.test_1', 'test_domain.test_2'] == \ + sorted(ent.entity_id for ent in + component.async_extract_from_service(call)) + assert ('Not passing an entity ID to a service to target all entities is ' + 'deprecated') not in caplog.text diff --git a/tests/helpers/test_service.py b/tests/helpers/test_service.py index 8fca7df69c1..35e89fc5218 100644 --- a/tests/helpers/test_service.py +++ b/tests/helpers/test_service.py @@ -306,3 +306,35 @@ async def test_call_no_context_target_specific( assert len(mock_service_platform_call.mock_calls) == 1 entities = mock_service_platform_call.mock_calls[0][1][2] assert entities == [mock_entities['light.kitchen']] + + +async def test_call_with_match_all(hass, mock_service_platform_call, + mock_entities, caplog): + """Check we only target allowed entities if targetting all.""" + await service.entity_service_call(hass, [ + Mock(entities=mock_entities) + ], Mock(), ha.ServiceCall('test_domain', 'test_service', { + 'entity_id': 'all' + })) + + assert len(mock_service_platform_call.mock_calls) == 1 + entities = mock_service_platform_call.mock_calls[0][1][2] + assert entities == [ + mock_entities['light.kitchen'], mock_entities['light.living_room']] + assert ('Not passing an entity ID to a service to target ' + 'all entities is deprecated') not in caplog.text + + +async def test_call_with_omit_entity_id(hass, mock_service_platform_call, + mock_entities, caplog): + """Check we only target allowed entities if targetting all.""" + await service.entity_service_call(hass, [ + Mock(entities=mock_entities) + ], Mock(), ha.ServiceCall('test_domain', 'test_service')) + + assert len(mock_service_platform_call.mock_calls) == 1 + entities = mock_service_platform_call.mock_calls[0][1][2] + assert entities == [ + mock_entities['light.kitchen'], mock_entities['light.living_room']] + assert ('Not passing an entity ID to a service to target ' + 'all entities is deprecated') in caplog.text