Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Deprecate auto target all for services and introduce entity_id: * #19006

Merged
merged 2 commits into from
Dec 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion homeassistant/components/alarm_control_panel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
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,
})

Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/automation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,11 @@ def _platform_validator(config):
})

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,
})

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/camera/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
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({
Expand Down
18 changes: 9 additions & 9 deletions homeassistant/components/climate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,15 @@
_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(
Expand All @@ -110,28 +110,28 @@
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,
})

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/counter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
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({
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/cover/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
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({
Expand Down
12 changes: 6 additions & 6 deletions homeassistant/components/fan/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,30 +61,30 @@
} # 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

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/group/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
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
})

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/image_processing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
})

SERVICE_SCAN_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids,
})


Expand Down
6 changes: 3 additions & 3 deletions homeassistant/components/light/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
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,
Expand All @@ -115,13 +115,13 @@
})

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,
})

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/lock/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
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,
})

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/media_player/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@

# 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({
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/remote/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
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({
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/scene/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def _platform_validator(config):
), 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,
})


Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/switch/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
}

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__)
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/vacuum/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
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({
Expand Down
8 changes: 4 additions & 4 deletions homeassistant/components/water_heater/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,22 +57,22 @@
_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,
})

Expand Down
3 changes: 3 additions & 0 deletions homeassistant/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
# 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'

Expand Down
9 changes: 8 additions & 1 deletion homeassistant/helpers/config_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
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
Expand Down Expand Up @@ -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:
Expand Down
12 changes: 10 additions & 2 deletions homeassistant/helpers/entity_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
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
Expand Down Expand Up @@ -161,7 +161,15 @@ def async_extract_from_service(self, service, expand_group=True):

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))
Expand Down
9 changes: 7 additions & 2 deletions homeassistant/helpers/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
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
Expand Down Expand Up @@ -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.
Expand Down
13 changes: 13 additions & 0 deletions tests/helpers/test_config_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Loading