1
mirror of https://github.com/home-assistant/core synced 2024-07-30 21:18:57 +02:00

Do not select all entities when omitting entity ID in service call (#29178)

* Do not select all entities when omitting entity ID

* Address comments Matthew

* Require either area_id or entity_id

* Fix tests

* Fix test
This commit is contained in:
Paulus Schoutsen 2019-12-02 16:23:12 -08:00 committed by GitHub
parent 9587afc5ce
commit 02d9ed5e36
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 538 additions and 626 deletions

View File

@ -17,7 +17,7 @@ from homeassistant.const import (
)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import ( # noqa: F401
ENTITY_SERVICE_SCHEMA,
make_entity_service_schema,
PLATFORM_SCHEMA,
PLATFORM_SCHEMA_BASE,
)
@ -41,9 +41,7 @@ ATTR_CODE_ARM_REQUIRED = "code_arm_required"
ENTITY_ID_FORMAT = DOMAIN + ".{}"
ALARM_SERVICE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{vol.Optional(ATTR_CODE): cv.string}
)
ALARM_SERVICE_SCHEMA = make_entity_service_schema({vol.Optional(ATTR_CODE): cv.string})
async def async_setup(hass, config):

View File

@ -24,7 +24,7 @@ from homeassistant.core import Context, CoreState, HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import condition, extract_domain_configs, script
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import ENTITY_SERVICE_SCHEMA
from homeassistant.helpers.config_validation import make_entity_service_schema
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.restore_state import RestoreEntity
@ -106,7 +106,7 @@ PLATFORM_SCHEMA = vol.Schema(
}
)
TRIGGER_SERVICE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
TRIGGER_SERVICE_SCHEMA = make_entity_service_schema(
{vol.Optional(ATTR_VARIABLES, default={}): dict}
)
@ -184,12 +184,18 @@ async def async_setup(hass, config):
)
hass.services.async_register(
DOMAIN, SERVICE_TOGGLE, toggle_service_handler, schema=ENTITY_SERVICE_SCHEMA
DOMAIN,
SERVICE_TOGGLE,
toggle_service_handler,
schema=make_entity_service_schema({}),
)
for service in (SERVICE_TURN_ON, SERVICE_TURN_OFF):
hass.services.async_register(
DOMAIN, service, turn_onoff_service_handler, schema=ENTITY_SERVICE_SCHEMA
DOMAIN,
service,
turn_onoff_service_handler,
schema=make_entity_service_schema({}),
)
return True

View File

@ -19,7 +19,7 @@ from homeassistant.const import (
)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import ( # noqa: F401
ENTITY_SERVICE_SCHEMA,
make_entity_service_schema,
PLATFORM_SCHEMA,
PLATFORM_SCHEMA_BASE,
)
@ -84,38 +84,19 @@ CONVERTIBLE_ATTRIBUTE = [ATTR_TEMPERATURE, ATTR_TARGET_TEMP_LOW, ATTR_TARGET_TEM
_LOGGER = logging.getLogger(__name__)
SET_AUX_HEAT_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{vol.Required(ATTR_AUX_HEAT): cv.boolean}
)
SET_TEMPERATURE_SCHEMA = vol.Schema(
vol.All(
cv.has_at_least_one_key(
ATTR_TEMPERATURE, ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW
),
ENTITY_SERVICE_SCHEMA.extend(
{
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_HVAC_MODE): vol.In(HVAC_MODES),
}
),
)
)
SET_FAN_MODE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{vol.Required(ATTR_FAN_MODE): cv.string}
)
SET_PRESET_MODE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{vol.Required(ATTR_PRESET_MODE): cv.string}
)
SET_HVAC_MODE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{vol.Required(ATTR_HVAC_MODE): vol.In(HVAC_MODES)}
)
SET_HUMIDITY_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{vol.Required(ATTR_HUMIDITY): vol.Coerce(float)}
)
SET_SWING_MODE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{vol.Required(ATTR_SWING_MODE): cv.string}
SET_TEMPERATURE_SCHEMA = vol.All(
cv.has_at_least_one_key(
ATTR_TEMPERATURE, ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW
),
make_entity_service_schema(
{
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_HVAC_MODE): vol.In(HVAC_MODES),
}
),
)
@ -126,32 +107,40 @@ async def async_setup(hass: HomeAssistantType, config: ConfigType) -> bool:
)
await component.async_setup(config)
component.async_register_entity_service(SERVICE_TURN_ON, {}, "async_turn_on")
component.async_register_entity_service(SERVICE_TURN_OFF, {}, "async_turn_off")
component.async_register_entity_service(
SERVICE_TURN_ON, ENTITY_SERVICE_SCHEMA, "async_turn_on"
SERVICE_SET_HVAC_MODE,
{vol.Required(ATTR_HVAC_MODE): vol.In(HVAC_MODES)},
"async_set_hvac_mode",
)
component.async_register_entity_service(
SERVICE_TURN_OFF, ENTITY_SERVICE_SCHEMA, "async_turn_off"
SERVICE_SET_PRESET_MODE,
{vol.Required(ATTR_PRESET_MODE): cv.string},
"async_set_preset_mode",
)
component.async_register_entity_service(
SERVICE_SET_HVAC_MODE, SET_HVAC_MODE_SCHEMA, "async_set_hvac_mode"
SERVICE_SET_AUX_HEAT,
{vol.Required(ATTR_AUX_HEAT): cv.boolean},
async_service_aux_heat,
)
component.async_register_entity_service(
SERVICE_SET_PRESET_MODE, SET_PRESET_MODE_SCHEMA, "async_set_preset_mode"
SERVICE_SET_TEMPERATURE, SET_TEMPERATURE_SCHEMA, async_service_temperature_set,
)
component.async_register_entity_service(
SERVICE_SET_AUX_HEAT, SET_AUX_HEAT_SCHEMA, async_service_aux_heat
SERVICE_SET_HUMIDITY,
{vol.Required(ATTR_HUMIDITY): vol.Coerce(float)},
"async_set_humidity",
)
component.async_register_entity_service(
SERVICE_SET_TEMPERATURE, SET_TEMPERATURE_SCHEMA, async_service_temperature_set
SERVICE_SET_FAN_MODE,
{vol.Required(ATTR_FAN_MODE): cv.string},
"async_set_fan_mode",
)
component.async_register_entity_service(
SERVICE_SET_HUMIDITY, SET_HUMIDITY_SCHEMA, "async_set_humidity"
)
component.async_register_entity_service(
SERVICE_SET_FAN_MODE, SET_FAN_MODE_SCHEMA, "async_set_fan_mode"
)
component.async_register_entity_service(
SERVICE_SET_SWING_MODE, SET_SWING_MODE_SCHEMA, "async_set_swing_mode"
SERVICE_SET_SWING_MODE,
{vol.Required(ATTR_SWING_MODE): cv.string},
"async_set_swing_mode",
)
return True

View File

@ -6,7 +6,6 @@ import voluptuous as vol
from homeassistant.const import CONF_ICON, CONF_NAME, CONF_MAXIMUM, CONF_MINIMUM
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import ENTITY_SERVICE_SCHEMA
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.restore_state import RestoreEntity
@ -33,15 +32,6 @@ SERVICE_INCREMENT = "increment"
SERVICE_RESET = "reset"
SERVICE_CONFIGURE = "configure"
SERVICE_SCHEMA_CONFIGURE = ENTITY_SERVICE_SCHEMA.extend(
{
vol.Optional(ATTR_MINIMUM): vol.Any(None, vol.Coerce(int)),
vol.Optional(ATTR_MAXIMUM): vol.Any(None, vol.Coerce(int)),
vol.Optional(ATTR_STEP): cv.positive_int,
vol.Optional(ATTR_INITIAL): cv.positive_int,
vol.Optional(VALUE): cv.positive_int,
}
)
CONFIG_SCHEMA = vol.Schema(
{
@ -95,17 +85,19 @@ async def async_setup(hass, config):
if not entities:
return False
component.async_register_entity_service(SERVICE_INCREMENT, {}, "async_increment")
component.async_register_entity_service(SERVICE_DECREMENT, {}, "async_decrement")
component.async_register_entity_service(SERVICE_RESET, {}, "async_reset")
component.async_register_entity_service(
SERVICE_INCREMENT, ENTITY_SERVICE_SCHEMA, "async_increment"
)
component.async_register_entity_service(
SERVICE_DECREMENT, ENTITY_SERVICE_SCHEMA, "async_decrement"
)
component.async_register_entity_service(
SERVICE_RESET, ENTITY_SERVICE_SCHEMA, "async_reset"
)
component.async_register_entity_service(
SERVICE_CONFIGURE, SERVICE_SCHEMA_CONFIGURE, "async_configure"
SERVICE_CONFIGURE,
{
vol.Optional(ATTR_MINIMUM): vol.Any(None, vol.Coerce(int)),
vol.Optional(ATTR_MAXIMUM): vol.Any(None, vol.Coerce(int)),
vol.Optional(ATTR_STEP): cv.positive_int,
vol.Optional(ATTR_INITIAL): cv.positive_int,
vol.Optional(VALUE): cv.positive_int,
},
"async_configure",
)
await component.async_add_entities(entities)

View File

@ -13,7 +13,6 @@ from homeassistant.helpers.config_validation import ( # noqa: F401
PLATFORM_SCHEMA,
PLATFORM_SCHEMA_BASE,
)
from homeassistant.helpers.config_validation import ENTITY_SERVICE_SCHEMA
from homeassistant.components import group
from homeassistant.const import (
SERVICE_OPEN_COVER,
@ -83,18 +82,6 @@ ATTR_POSITION = "position"
ATTR_TILT_POSITION = "tilt_position"
COVER_SET_COVER_POSITION_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{vol.Required(ATTR_POSITION): vol.All(vol.Coerce(int), vol.Range(min=0, max=100))}
)
COVER_SET_COVER_TILT_POSITION_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{
vol.Required(ATTR_TILT_POSITION): vol.All(
vol.Coerce(int), vol.Range(min=0, max=100)
)
}
)
@bind_hass
def is_closed(hass, entity_id=None):
@ -111,48 +98,50 @@ async def async_setup(hass, config):
await component.async_setup(config)
component.async_register_entity_service(
SERVICE_OPEN_COVER, ENTITY_SERVICE_SCHEMA, "async_open_cover"
)
component.async_register_entity_service(SERVICE_OPEN_COVER, {}, "async_open_cover")
component.async_register_entity_service(
SERVICE_CLOSE_COVER, ENTITY_SERVICE_SCHEMA, "async_close_cover"
SERVICE_CLOSE_COVER, {}, "async_close_cover"
)
component.async_register_entity_service(
SERVICE_SET_COVER_POSITION,
COVER_SET_COVER_POSITION_SCHEMA,
{
vol.Required(ATTR_POSITION): vol.All(
vol.Coerce(int), vol.Range(min=0, max=100)
)
},
"async_set_cover_position",
)
component.async_register_entity_service(SERVICE_STOP_COVER, {}, "async_stop_cover")
component.async_register_entity_service(SERVICE_TOGGLE, {}, "async_toggle")
component.async_register_entity_service(
SERVICE_STOP_COVER, ENTITY_SERVICE_SCHEMA, "async_stop_cover"
SERVICE_OPEN_COVER_TILT, {}, "async_open_cover_tilt"
)
component.async_register_entity_service(
SERVICE_TOGGLE, ENTITY_SERVICE_SCHEMA, "async_toggle"
SERVICE_CLOSE_COVER_TILT, {}, "async_close_cover_tilt"
)
component.async_register_entity_service(
SERVICE_OPEN_COVER_TILT, ENTITY_SERVICE_SCHEMA, "async_open_cover_tilt"
)
component.async_register_entity_service(
SERVICE_CLOSE_COVER_TILT, ENTITY_SERVICE_SCHEMA, "async_close_cover_tilt"
)
component.async_register_entity_service(
SERVICE_STOP_COVER_TILT, ENTITY_SERVICE_SCHEMA, "async_stop_cover_tilt"
SERVICE_STOP_COVER_TILT, {}, "async_stop_cover_tilt"
)
component.async_register_entity_service(
SERVICE_SET_COVER_TILT_POSITION,
COVER_SET_COVER_TILT_POSITION_SCHEMA,
{
vol.Required(ATTR_TILT_POSITION): vol.All(
vol.Coerce(int), vol.Range(min=0, max=100)
)
},
"async_set_cover_tilt_position",
)
component.async_register_entity_service(
SERVICE_TOGGLE_COVER_TILT, ENTITY_SERVICE_SCHEMA, "async_toggle_tilt"
SERVICE_TOGGLE_COVER_TILT, {}, "async_toggle_tilt"
)
return True

View File

@ -12,7 +12,6 @@ from homeassistant.loader import bind_hass
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.config_validation import ( # noqa: F401
ENTITY_SERVICE_SCHEMA,
PLATFORM_SCHEMA,
PLATFORM_SCHEMA_BASE,
)
@ -57,20 +56,6 @@ PROP_TO_ATTR = {
"current_direction": ATTR_DIRECTION,
}
FAN_SET_SPEED_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{vol.Required(ATTR_SPEED): cv.string}
)
FAN_TURN_ON_SCHEMA = ENTITY_SERVICE_SCHEMA.extend({vol.Optional(ATTR_SPEED): cv.string})
FAN_OSCILLATE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{vol.Required(ATTR_OSCILLATING): cv.boolean}
)
FAN_SET_DIRECTION_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{vol.Optional(ATTR_DIRECTION): cv.string}
)
@bind_hass
def is_on(hass, entity_id: Optional[str] = None) -> bool:
@ -89,22 +74,22 @@ async def async_setup(hass, config: dict):
await component.async_setup(config)
component.async_register_entity_service(
SERVICE_TURN_ON, FAN_TURN_ON_SCHEMA, "async_turn_on"
SERVICE_TURN_ON, {vol.Optional(ATTR_SPEED): cv.string}, "async_turn_on"
)
component.async_register_entity_service(SERVICE_TURN_OFF, {}, "async_turn_off")
component.async_register_entity_service(SERVICE_TOGGLE, {}, "async_toggle")
component.async_register_entity_service(
SERVICE_SET_SPEED, {vol.Required(ATTR_SPEED): cv.string}, "async_set_speed"
)
component.async_register_entity_service(
SERVICE_TURN_OFF, ENTITY_SERVICE_SCHEMA, "async_turn_off"
SERVICE_OSCILLATE,
{vol.Required(ATTR_OSCILLATING): cv.boolean},
"async_oscillate",
)
component.async_register_entity_service(
SERVICE_TOGGLE, ENTITY_SERVICE_SCHEMA, "async_toggle"
)
component.async_register_entity_service(
SERVICE_SET_SPEED, FAN_SET_SPEED_SCHEMA, "async_set_speed"
)
component.async_register_entity_service(
SERVICE_OSCILLATE, FAN_OSCILLATE_SCHEMA, "async_oscillate"
)
component.async_register_entity_service(
SERVICE_SET_DIRECTION, FAN_SET_DIRECTION_SCHEMA, "async_set_direction"
SERVICE_SET_DIRECTION,
{vol.Optional(ATTR_DIRECTION): cv.string},
"async_set_direction",
)
return True

View File

@ -32,7 +32,7 @@ from homeassistant.helpers.entity import Entity, async_generate_entity_id
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.event import async_track_state_change
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import ENTITY_SERVICE_SCHEMA
from homeassistant.helpers.config_validation import make_entity_service_schema
from homeassistant.helpers.typing import HomeAssistantType
@ -63,28 +63,6 @@ SERVICE_REMOVE = "remove"
CONTROL_TYPES = vol.In(["hidden", None])
SET_VISIBILITY_SERVICE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{vol.Required(ATTR_VISIBLE): cv.boolean}
)
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.Optional(ATTR_ALL): 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__)
@ -227,7 +205,7 @@ async def async_setup(hass, config):
await component.async_add_entities(auto)
hass.services.async_register(
DOMAIN, SERVICE_RELOAD, reload_service_handler, schema=RELOAD_SERVICE_SCHEMA
DOMAIN, SERVICE_RELOAD, reload_service_handler, schema=vol.Schema({})
)
service_lock = asyncio.Lock()
@ -319,11 +297,29 @@ async def async_setup(hass, config):
await component.async_remove_entity(entity_id)
hass.services.async_register(
DOMAIN, SERVICE_SET, locked_service_handler, schema=SET_SERVICE_SCHEMA
DOMAIN,
SERVICE_SET,
locked_service_handler,
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.Optional(ATTR_ALL): cv.boolean,
vol.Exclusive(ATTR_ENTITIES, "entities"): cv.entity_ids,
vol.Exclusive(ATTR_ADD_ENTITIES, "entities"): cv.entity_ids,
}
),
)
hass.services.async_register(
DOMAIN, SERVICE_REMOVE, groups_service_handler, schema=REMOVE_SERVICE_SCHEMA
DOMAIN,
SERVICE_REMOVE,
groups_service_handler,
schema=vol.Schema({vol.Required(ATTR_OBJECT_ID): cv.slug}),
)
async def visibility_service_handler(service):
@ -344,7 +340,7 @@ async def async_setup(hass, config):
DOMAIN,
SERVICE_SET_VISIBILITY,
visibility_service_handler,
schema=SET_VISIBILITY_SERVICE_SCHEMA,
schema=make_entity_service_schema({vol.Required(ATTR_VISIBLE): cv.boolean}),
)
return True

View File

@ -11,7 +11,7 @@ from homeassistant.const import ATTR_ENTITY_ID, ATTR_NAME, CONF_ENTITY_ID, CONF_
from homeassistant.core import callback
from homeassistant.exceptions import HomeAssistantError
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import ENTITY_SERVICE_SCHEMA
from homeassistant.helpers.config_validation import make_entity_service_schema
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.util.async_ import run_callback_threadsafe
@ -124,7 +124,7 @@ async def async_setup(hass, config):
await asyncio.wait(update_tasks)
hass.services.async_register(
DOMAIN, SERVICE_SCAN, async_scan_service, schema=ENTITY_SERVICE_SCHEMA
DOMAIN, SERVICE_SCAN, async_scan_service, schema=make_entity_service_schema({})
)
return True

View File

@ -13,7 +13,6 @@ from homeassistant.const import (
)
from homeassistant.loader import bind_hass
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import ENTITY_SERVICE_SCHEMA
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.restore_state import RestoreEntity
@ -68,17 +67,11 @@ async def async_setup(hass, config):
if not entities:
return False
component.async_register_entity_service(
SERVICE_TURN_ON, ENTITY_SERVICE_SCHEMA, "async_turn_on"
)
component.async_register_entity_service(SERVICE_TURN_ON, {}, "async_turn_on")
component.async_register_entity_service(
SERVICE_TURN_OFF, ENTITY_SERVICE_SCHEMA, "async_turn_off"
)
component.async_register_entity_service(SERVICE_TURN_OFF, {}, "async_turn_off")
component.async_register_entity_service(
SERVICE_TOGGLE, ENTITY_SERVICE_SCHEMA, "async_toggle"
)
component.async_register_entity_service(SERVICE_TOGGLE, {}, "async_toggle")
await component.async_add_entities(entities)
return True

View File

@ -6,7 +6,6 @@ import voluptuous as vol
from homeassistant.const import ATTR_DATE, ATTR_TIME, CONF_ICON, CONF_NAME
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import ENTITY_SERVICE_SCHEMA
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.util import dt as dt_util
@ -27,14 +26,6 @@ ATTR_DATETIME = "datetime"
SERVICE_SET_DATETIME = "set_datetime"
SERVICE_SET_DATETIME_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{
vol.Optional(ATTR_DATE): cv.date,
vol.Optional(ATTR_TIME): cv.time,
vol.Optional(ATTR_DATETIME): cv.datetime,
}
)
def has_date_or_time(conf):
"""Check at least date or time is true."""
@ -108,7 +99,13 @@ async def async_setup(hass, config):
entity.async_set_datetime(date, time)
component.async_register_entity_service(
SERVICE_SET_DATETIME, SERVICE_SET_DATETIME_SCHEMA, async_set_datetime_service
SERVICE_SET_DATETIME,
{
vol.Optional(ATTR_DATE): cv.date,
vol.Optional(ATTR_TIME): cv.time,
vol.Optional(ATTR_DATETIME): cv.datetime,
},
async_set_datetime_service,
)
await component.async_add_entities(entities)

View File

@ -4,7 +4,6 @@ import logging
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import ENTITY_SERVICE_SCHEMA
from homeassistant.const import (
ATTR_UNIT_OF_MEASUREMENT,
ATTR_MODE,
@ -38,10 +37,6 @@ SERVICE_SET_VALUE = "set_value"
SERVICE_INCREMENT = "increment"
SERVICE_DECREMENT = "decrement"
SERVICE_SET_VALUE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{vol.Required(ATTR_VALUE): vol.Coerce(float)}
)
def _cv_input_number(cfg):
"""Configure validation helper for input number (voluptuous)."""
@ -110,16 +105,14 @@ async def async_setup(hass, config):
return False
component.async_register_entity_service(
SERVICE_SET_VALUE, SERVICE_SET_VALUE_SCHEMA, "async_set_value"
SERVICE_SET_VALUE,
{vol.Required(ATTR_VALUE): vol.Coerce(float)},
"async_set_value",
)
component.async_register_entity_service(
SERVICE_INCREMENT, ENTITY_SERVICE_SCHEMA, "async_increment"
)
component.async_register_entity_service(SERVICE_INCREMENT, {}, "async_increment")
component.async_register_entity_service(
SERVICE_DECREMENT, ENTITY_SERVICE_SCHEMA, "async_decrement"
)
component.async_register_entity_service(SERVICE_DECREMENT, {}, "async_decrement")
await component.async_add_entities(entities)
return True

View File

@ -5,7 +5,6 @@ import voluptuous as vol
from homeassistant.const import CONF_ICON, CONF_NAME
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import ENTITY_SERVICE_SCHEMA
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.restore_state import RestoreEntity
@ -22,9 +21,6 @@ ATTR_OPTIONS = "options"
SERVICE_SELECT_OPTION = "select_option"
SERVICE_SELECT_OPTION_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{vol.Required(ATTR_OPTION): cv.string}
)
SERVICE_SELECT_NEXT = "select_next"
@ -32,14 +28,6 @@ SERVICE_SELECT_PREVIOUS = "select_previous"
SERVICE_SET_OPTIONS = "set_options"
SERVICE_SET_OPTIONS_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{
vol.Required(ATTR_OPTIONS): vol.All(
cv.ensure_list, vol.Length(min=1), [cv.string]
)
}
)
def _cv_input_select(cfg):
"""Configure validation helper for input select (voluptuous)."""
@ -92,23 +80,27 @@ async def async_setup(hass, config):
return False
component.async_register_entity_service(
SERVICE_SELECT_OPTION, SERVICE_SELECT_OPTION_SCHEMA, "async_select_option"
SERVICE_SELECT_OPTION,
{vol.Required(ATTR_OPTION): cv.string},
"async_select_option",
)
component.async_register_entity_service(
SERVICE_SELECT_NEXT,
ENTITY_SERVICE_SCHEMA,
lambda entity, call: entity.async_offset_index(1),
SERVICE_SELECT_NEXT, {}, lambda entity, call: entity.async_offset_index(1),
)
component.async_register_entity_service(
SERVICE_SELECT_PREVIOUS,
ENTITY_SERVICE_SCHEMA,
lambda entity, call: entity.async_offset_index(-1),
SERVICE_SELECT_PREVIOUS, {}, lambda entity, call: entity.async_offset_index(-1),
)
component.async_register_entity_service(
SERVICE_SET_OPTIONS, SERVICE_SET_OPTIONS_SCHEMA, "async_set_options"
SERVICE_SET_OPTIONS,
{
vol.Required(ATTR_OPTIONS): vol.All(
cv.ensure_list, vol.Length(min=1), [cv.string]
)
},
"async_set_options",
)
await component.async_add_entities(entities)

View File

@ -4,7 +4,6 @@ import logging
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import ENTITY_SERVICE_SCHEMA
from homeassistant.const import (
ATTR_UNIT_OF_MEASUREMENT,
ATTR_MODE,
@ -34,10 +33,6 @@ ATTR_PATTERN = "pattern"
SERVICE_SET_VALUE = "set_value"
SERVICE_SET_VALUE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{vol.Required(ATTR_VALUE): cv.string}
)
def _cv_input_text(cfg):
"""Configure validation helper for input box (voluptuous)."""
@ -111,7 +106,7 @@ async def async_setup(hass, config):
return False
component.async_register_entity_service(
SERVICE_SET_VALUE, SERVICE_SET_VALUE_SCHEMA, "async_set_value"
SERVICE_SET_VALUE, {vol.Required(ATTR_VALUE): cv.string}, "async_set_value"
)
await component.async_add_entities(entities)

View File

@ -66,8 +66,9 @@ ATTR_INFRARED = "infrared"
ATTR_ZONES = "zones"
ATTR_POWER = "power"
LIFX_SET_STATE_SCHEMA = LIGHT_TURN_ON_SCHEMA.extend(
LIFX_SET_STATE_SCHEMA = cv.make_entity_service_schema(
{
**LIGHT_TURN_ON_SCHEMA,
ATTR_INFRARED: vol.All(vol.Coerce(int), vol.Clamp(min=0, max=255)),
ATTR_ZONES: vol.All(cv.ensure_list, [cv.positive_int]),
ATTR_POWER: cv.boolean,
@ -369,16 +370,11 @@ class LIFXManager:
async def async_service_to_entities(self, service):
"""Return the known entities that a service call mentions."""
entity_ids = await async_extract_entity_ids(self.hass, service)
if entity_ids:
entities = [
entity
for entity in self.entities.values()
if entity.entity_id in entity_ids
]
else:
entities = list(self.entities.values())
return entities
return [
entity
for entity in self.entities.values()
if entity.entity_id in entity_ids
]
@callback
def register(self, bulb):

View File

@ -22,7 +22,7 @@ import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import ( # noqa: F401
PLATFORM_SCHEMA,
PLATFORM_SCHEMA_BASE,
ENTITY_SERVICE_SCHEMA,
make_entity_service_schema,
)
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.helpers.entity_component import EntityComponent
@ -93,49 +93,37 @@ VALID_TRANSITION = vol.All(vol.Coerce(float), vol.Clamp(min=0, max=6553))
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 = ENTITY_SERVICE_SCHEMA.extend(
{
vol.Exclusive(ATTR_PROFILE, COLOR_GROUP): cv.string,
ATTR_TRANSITION: VALID_TRANSITION,
ATTR_BRIGHTNESS: VALID_BRIGHTNESS,
ATTR_BRIGHTNESS_PCT: VALID_BRIGHTNESS_PCT,
vol.Exclusive(ATTR_COLOR_NAME, COLOR_GROUP): cv.string,
vol.Exclusive(ATTR_RGB_COLOR, COLOR_GROUP): vol.All(
vol.ExactSequence((cv.byte, cv.byte, cv.byte)), vol.Coerce(tuple)
),
vol.Exclusive(ATTR_XY_COLOR, COLOR_GROUP): vol.All(
vol.ExactSequence((cv.small_float, cv.small_float)), vol.Coerce(tuple)
),
vol.Exclusive(ATTR_HS_COLOR, COLOR_GROUP): vol.All(
vol.ExactSequence(
(
vol.All(vol.Coerce(float), vol.Range(min=0, max=360)),
vol.All(vol.Coerce(float), vol.Range(min=0, max=100)),
)
),
vol.Coerce(tuple),
),
vol.Exclusive(ATTR_COLOR_TEMP, COLOR_GROUP): vol.All(
vol.Coerce(int), vol.Range(min=1)
),
vol.Exclusive(ATTR_KELVIN, COLOR_GROUP): vol.All(
vol.Coerce(int), vol.Range(min=0)
),
ATTR_WHITE_VALUE: vol.All(vol.Coerce(int), vol.Range(min=0, max=255)),
ATTR_FLASH: vol.In([FLASH_SHORT, FLASH_LONG]),
ATTR_EFFECT: cv.string,
}
)
LIGHT_TURN_OFF_SCHEMA = {
LIGHT_TURN_ON_SCHEMA = {
vol.Exclusive(ATTR_PROFILE, COLOR_GROUP): cv.string,
ATTR_TRANSITION: VALID_TRANSITION,
ATTR_BRIGHTNESS: VALID_BRIGHTNESS,
ATTR_BRIGHTNESS_PCT: VALID_BRIGHTNESS_PCT,
vol.Exclusive(ATTR_COLOR_NAME, COLOR_GROUP): cv.string,
vol.Exclusive(ATTR_RGB_COLOR, COLOR_GROUP): vol.All(
vol.ExactSequence((cv.byte, cv.byte, cv.byte)), vol.Coerce(tuple)
),
vol.Exclusive(ATTR_XY_COLOR, COLOR_GROUP): vol.All(
vol.ExactSequence((cv.small_float, cv.small_float)), vol.Coerce(tuple)
),
vol.Exclusive(ATTR_HS_COLOR, COLOR_GROUP): vol.All(
vol.ExactSequence(
(
vol.All(vol.Coerce(float), vol.Range(min=0, max=360)),
vol.All(vol.Coerce(float), vol.Range(min=0, max=100)),
)
),
vol.Coerce(tuple),
),
vol.Exclusive(ATTR_COLOR_TEMP, COLOR_GROUP): vol.All(
vol.Coerce(int), vol.Range(min=1)
),
vol.Exclusive(ATTR_KELVIN, COLOR_GROUP): vol.All(vol.Coerce(int), vol.Range(min=0)),
ATTR_WHITE_VALUE: vol.All(vol.Coerce(int), vol.Range(min=0, max=255)),
ATTR_FLASH: vol.In([FLASH_SHORT, FLASH_LONG]),
ATTR_EFFECT: cv.string,
}
LIGHT_TOGGLE_SCHEMA = LIGHT_TURN_ON_SCHEMA
PROFILE_SCHEMA = vol.Schema(
vol.ExactSequence((str, cv.small_float, cv.small_float, cv.byte))
)
@ -268,15 +256,20 @@ async def async_setup(hass, config):
DOMAIN,
SERVICE_TURN_ON,
async_handle_light_on_service,
schema=LIGHT_TURN_ON_SCHEMA,
schema=cv.make_entity_service_schema(LIGHT_TURN_ON_SCHEMA),
)
component.async_register_entity_service(
SERVICE_TURN_OFF, LIGHT_TURN_OFF_SCHEMA, "async_turn_off"
SERVICE_TURN_OFF,
{
ATTR_TRANSITION: VALID_TRANSITION,
ATTR_FLASH: vol.In([FLASH_SHORT, FLASH_LONG]),
},
"async_turn_off",
)
component.async_register_entity_service(
SERVICE_TOGGLE, LIGHT_TOGGLE_SCHEMA, "async_toggle"
SERVICE_TOGGLE, LIGHT_TURN_ON_SCHEMA, "async_toggle"
)
return True

View File

@ -9,7 +9,7 @@ from homeassistant.loader import bind_hass
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.config_validation import ( # noqa: F401
ENTITY_SERVICE_SCHEMA,
make_entity_service_schema,
PLATFORM_SCHEMA,
PLATFORM_SCHEMA_BASE,
)
@ -40,7 +40,7 @@ GROUP_NAME_ALL_LOCKS = "all locks"
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
LOCK_SERVICE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend({vol.Optional(ATTR_CODE): cv.string})
LOCK_SERVICE_SCHEMA = make_entity_service_schema({vol.Optional(ATTR_CODE): cv.string})
# Bitfield of features supported by the lock entity
SUPPORT_OPEN = 1

View File

@ -2,6 +2,8 @@
import logging
import voluptuous as vol
from youtube_dl import YoutubeDL
from youtube_dl.utils import DownloadError, ExtractorError
from homeassistant.components.media_player import MEDIA_PLAYER_PLAY_MEDIA_SCHEMA
from homeassistant.components.media_player.const import (
@ -44,7 +46,10 @@ def setup(hass, config):
MediaExtractor(hass, config[DOMAIN], call.data).extract_and_send()
hass.services.register(
DOMAIN, SERVICE_PLAY_MEDIA, play_media, schema=MEDIA_PLAYER_PLAY_MEDIA_SCHEMA
DOMAIN,
SERVICE_PLAY_MEDIA,
play_media,
schema=cv.make_entity_service_schema(MEDIA_PLAYER_PLAY_MEDIA_SCHEMA),
)
return True
@ -98,9 +103,6 @@ class MediaExtractor:
def get_stream_selector(self):
"""Return format selector for the media URL."""
from youtube_dl import YoutubeDL
from youtube_dl.utils import DownloadError, ExtractorError
ydl = YoutubeDL({"quiet": True, "logger": _LOGGER})
try:

View File

@ -40,7 +40,6 @@ from homeassistant.const import (
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import ( # noqa: F401
ENTITY_SERVICE_SCHEMA,
PLATFORM_SCHEMA,
PLATFORM_SCHEMA_BASE,
)
@ -123,42 +122,12 @@ DEVICE_CLASSES = [DEVICE_CLASS_TV, DEVICE_CLASS_SPEAKER]
DEVICE_CLASSES_SCHEMA = vol.All(vol.Lower, vol.In(DEVICE_CLASSES))
# Service call validation schemas
MEDIA_PLAYER_SET_VOLUME_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{vol.Required(ATTR_MEDIA_VOLUME_LEVEL): cv.small_float}
)
MEDIA_PLAYER_MUTE_VOLUME_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{vol.Required(ATTR_MEDIA_VOLUME_MUTED): cv.boolean}
)
MEDIA_PLAYER_MEDIA_SEEK_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{
vol.Required(ATTR_MEDIA_SEEK_POSITION): vol.All(
vol.Coerce(float), vol.Range(min=0)
)
}
)
MEDIA_PLAYER_SELECT_SOURCE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{vol.Required(ATTR_INPUT_SOURCE): cv.string}
)
MEDIA_PLAYER_SELECT_SOUND_MODE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{vol.Required(ATTR_SOUND_MODE): cv.string}
)
MEDIA_PLAYER_PLAY_MEDIA_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{
vol.Required(ATTR_MEDIA_CONTENT_TYPE): cv.string,
vol.Required(ATTR_MEDIA_CONTENT_ID): cv.string,
vol.Optional(ATTR_MEDIA_ENQUEUE): cv.boolean,
}
)
MEDIA_PLAYER_SET_SHUFFLE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{vol.Required(ATTR_MEDIA_SHUFFLE): cv.boolean}
)
MEDIA_PLAYER_PLAY_MEDIA_SCHEMA = {
vol.Required(ATTR_MEDIA_CONTENT_TYPE): cv.string,
vol.Required(ATTR_MEDIA_CONTENT_ID): cv.string,
vol.Optional(ATTR_MEDIA_ENQUEUE): cv.boolean,
}
ATTR_TO_PROPERTY = [
ATTR_MEDIA_VOLUME_LEVEL,
@ -223,65 +192,56 @@ async def async_setup(hass, config):
await component.async_setup(config)
component.async_register_entity_service(
SERVICE_TURN_ON, ENTITY_SERVICE_SCHEMA, "async_turn_on", [SUPPORT_TURN_ON]
SERVICE_TURN_ON, {}, "async_turn_on", [SUPPORT_TURN_ON]
)
component.async_register_entity_service(
SERVICE_TURN_OFF, ENTITY_SERVICE_SCHEMA, "async_turn_off", [SUPPORT_TURN_OFF]
SERVICE_TURN_OFF, {}, "async_turn_off", [SUPPORT_TURN_OFF]
)
component.async_register_entity_service(
SERVICE_TOGGLE,
ENTITY_SERVICE_SCHEMA,
"async_toggle",
[SUPPORT_TURN_OFF | SUPPORT_TURN_ON],
SERVICE_TOGGLE, {}, "async_toggle", [SUPPORT_TURN_OFF | SUPPORT_TURN_ON],
)
component.async_register_entity_service(
SERVICE_VOLUME_UP,
ENTITY_SERVICE_SCHEMA,
{},
"async_volume_up",
[SUPPORT_VOLUME_SET, SUPPORT_VOLUME_STEP],
)
component.async_register_entity_service(
SERVICE_VOLUME_DOWN,
ENTITY_SERVICE_SCHEMA,
{},
"async_volume_down",
[SUPPORT_VOLUME_SET, SUPPORT_VOLUME_STEP],
)
component.async_register_entity_service(
SERVICE_MEDIA_PLAY_PAUSE,
ENTITY_SERVICE_SCHEMA,
{},
"async_media_play_pause",
[SUPPORT_PLAY | SUPPORT_PAUSE],
)
component.async_register_entity_service(
SERVICE_MEDIA_PLAY, ENTITY_SERVICE_SCHEMA, "async_media_play", [SUPPORT_PLAY]
SERVICE_MEDIA_PLAY, {}, "async_media_play", [SUPPORT_PLAY]
)
component.async_register_entity_service(
SERVICE_MEDIA_PAUSE, ENTITY_SERVICE_SCHEMA, "async_media_pause", [SUPPORT_PAUSE]
SERVICE_MEDIA_PAUSE, {}, "async_media_pause", [SUPPORT_PAUSE]
)
component.async_register_entity_service(
SERVICE_MEDIA_STOP, ENTITY_SERVICE_SCHEMA, "async_media_stop", [SUPPORT_STOP]
SERVICE_MEDIA_STOP, {}, "async_media_stop", [SUPPORT_STOP]
)
component.async_register_entity_service(
SERVICE_MEDIA_NEXT_TRACK,
ENTITY_SERVICE_SCHEMA,
"async_media_next_track",
[SUPPORT_NEXT_TRACK],
SERVICE_MEDIA_NEXT_TRACK, {}, "async_media_next_track", [SUPPORT_NEXT_TRACK],
)
component.async_register_entity_service(
SERVICE_MEDIA_PREVIOUS_TRACK,
ENTITY_SERVICE_SCHEMA,
{},
"async_media_previous_track",
[SUPPORT_PREVIOUS_TRACK],
)
component.async_register_entity_service(
SERVICE_CLEAR_PLAYLIST,
ENTITY_SERVICE_SCHEMA,
"async_clear_playlist",
[SUPPORT_CLEAR_PLAYLIST],
SERVICE_CLEAR_PLAYLIST, {}, "async_clear_playlist", [SUPPORT_CLEAR_PLAYLIST],
)
component.async_register_entity_service(
SERVICE_VOLUME_SET,
MEDIA_PLAYER_SET_VOLUME_SCHEMA,
{vol.Required(ATTR_MEDIA_VOLUME_LEVEL): cv.small_float},
lambda entity, call: entity.async_set_volume_level(
volume=call.data[ATTR_MEDIA_VOLUME_LEVEL]
),
@ -289,7 +249,7 @@ async def async_setup(hass, config):
)
component.async_register_entity_service(
SERVICE_VOLUME_MUTE,
MEDIA_PLAYER_MUTE_VOLUME_SCHEMA,
{vol.Required(ATTR_MEDIA_VOLUME_MUTED): cv.boolean},
lambda entity, call: entity.async_mute_volume(
mute=call.data[ATTR_MEDIA_VOLUME_MUTED]
),
@ -297,7 +257,11 @@ async def async_setup(hass, config):
)
component.async_register_entity_service(
SERVICE_MEDIA_SEEK,
MEDIA_PLAYER_MEDIA_SEEK_SCHEMA,
{
vol.Required(ATTR_MEDIA_SEEK_POSITION): vol.All(
vol.Coerce(float), vol.Range(min=0)
)
},
lambda entity, call: entity.async_media_seek(
position=call.data[ATTR_MEDIA_SEEK_POSITION]
),
@ -305,13 +269,13 @@ async def async_setup(hass, config):
)
component.async_register_entity_service(
SERVICE_SELECT_SOURCE,
MEDIA_PLAYER_SELECT_SOURCE_SCHEMA,
{vol.Required(ATTR_INPUT_SOURCE): cv.string},
"async_select_source",
[SUPPORT_SELECT_SOURCE],
)
component.async_register_entity_service(
SERVICE_SELECT_SOUND_MODE,
MEDIA_PLAYER_SELECT_SOUND_MODE_SCHEMA,
{vol.Required(ATTR_SOUND_MODE): cv.string},
"async_select_sound_mode",
[SUPPORT_SELECT_SOUND_MODE],
)
@ -327,7 +291,7 @@ async def async_setup(hass, config):
)
component.async_register_entity_service(
SERVICE_SHUFFLE_SET,
MEDIA_PLAYER_SET_SHUFFLE_SCHEMA,
{vol.Required(ATTR_MEDIA_SHUFFLE): cv.boolean},
"async_set_shuffle",
[SUPPORT_SHUFFLE_SET],
)

View File

@ -17,7 +17,7 @@ from homeassistant.const import (
)
from homeassistant.components import group
from homeassistant.helpers.config_validation import ( # noqa: F401
ENTITY_SERVICE_SCHEMA,
make_entity_service_schema,
PLATFORM_SCHEMA,
PLATFORM_SCHEMA_BASE,
)
@ -56,29 +56,10 @@ DEFAULT_HOLD_SECS = 0
SUPPORT_LEARN_COMMAND = 1
REMOTE_SERVICE_ACTIVITY_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
REMOTE_SERVICE_ACTIVITY_SCHEMA = make_entity_service_schema(
{vol.Optional(ATTR_ACTIVITY): cv.string}
)
REMOTE_SERVICE_SEND_COMMAND_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{
vol.Required(ATTR_COMMAND): vol.All(cv.ensure_list, [cv.string]),
vol.Optional(ATTR_DEVICE): cv.string,
vol.Optional(ATTR_NUM_REPEATS, default=DEFAULT_NUM_REPEATS): cv.positive_int,
vol.Optional(ATTR_DELAY_SECS): vol.Coerce(float),
vol.Optional(ATTR_HOLD_SECS, default=DEFAULT_HOLD_SECS): vol.Coerce(float),
}
)
REMOTE_SERVICE_LEARN_COMMAND_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{
vol.Optional(ATTR_DEVICE): cv.string,
vol.Optional(ATTR_COMMAND): vol.All(cv.ensure_list, [cv.string]),
vol.Optional(ATTR_ALTERNATIVE): cv.boolean,
vol.Optional(ATTR_TIMEOUT): cv.positive_int,
}
)
@bind_hass
def is_on(hass, entity_id=None):
@ -107,12 +88,27 @@ async def async_setup(hass, config):
)
component.async_register_entity_service(
SERVICE_SEND_COMMAND, REMOTE_SERVICE_SEND_COMMAND_SCHEMA, "async_send_command"
SERVICE_SEND_COMMAND,
{
vol.Required(ATTR_COMMAND): vol.All(cv.ensure_list, [cv.string]),
vol.Optional(ATTR_DEVICE): cv.string,
vol.Optional(
ATTR_NUM_REPEATS, default=DEFAULT_NUM_REPEATS
): cv.positive_int,
vol.Optional(ATTR_DELAY_SECS): vol.Coerce(float),
vol.Optional(ATTR_HOLD_SECS, default=DEFAULT_HOLD_SECS): vol.Coerce(float),
},
"async_send_command",
)
component.async_register_entity_service(
SERVICE_LEARN_COMMAND,
REMOTE_SERVICE_LEARN_COMMAND_SCHEMA,
{
vol.Optional(ATTR_DEVICE): cv.string,
vol.Optional(ATTR_COMMAND): vol.All(cv.ensure_list, [cv.string]),
vol.Optional(ATTR_ALTERNATIVE): cv.boolean,
vol.Optional(ATTR_TIMEOUT): cv.positive_int,
},
"async_learn_command",
)

View File

@ -19,7 +19,7 @@ from homeassistant.loader import bind_hass
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.helpers.entity_component import EntityComponent
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import ENTITY_SERVICE_SCHEMA
from homeassistant.helpers.config_validation import make_entity_service_schema
from homeassistant.helpers.service import async_set_service_schema
from homeassistant.helpers.script import Script
@ -60,7 +60,7 @@ CONFIG_SCHEMA = vol.Schema(
)
SCRIPT_SERVICE_SCHEMA = vol.Schema(dict)
SCRIPT_TURN_ONOFF_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
SCRIPT_TURN_ONOFF_SCHEMA = make_entity_service_schema(
{vol.Optional(ATTR_VARIABLES): dict}
)
RELOAD_SERVICE_SCHEMA = vol.Schema({})

View File

@ -6,7 +6,6 @@ import voluptuous as vol
from homeassistant.const import CONF_ICON, CONF_NAME
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import ENTITY_SERVICE_SCHEMA
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.event import async_track_point_in_utc_time
from homeassistant.helpers.restore_state import RestoreEntity
@ -37,10 +36,6 @@ SERVICE_PAUSE = "pause"
SERVICE_CANCEL = "cancel"
SERVICE_FINISH = "finish"
SERVICE_SCHEMA_DURATION = ENTITY_SERVICE_SCHEMA.extend(
{vol.Optional(ATTR_DURATION, default=timedelta(DEFAULT_DURATION)): cv.time_period}
)
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: cv.schema_with_slug_keys(
@ -80,17 +75,17 @@ async def async_setup(hass, config):
return False
component.async_register_entity_service(
SERVICE_START, SERVICE_SCHEMA_DURATION, "async_start"
)
component.async_register_entity_service(
SERVICE_PAUSE, ENTITY_SERVICE_SCHEMA, "async_pause"
)
component.async_register_entity_service(
SERVICE_CANCEL, ENTITY_SERVICE_SCHEMA, "async_cancel"
)
component.async_register_entity_service(
SERVICE_FINISH, ENTITY_SERVICE_SCHEMA, "async_finish"
SERVICE_START,
{
vol.Optional(
ATTR_DURATION, default=timedelta(DEFAULT_DURATION)
): cv.time_period
},
"async_start",
)
component.async_register_entity_service(SERVICE_PAUSE, {}, "async_pause")
component.async_register_entity_service(SERVICE_CANCEL, {}, "async_cancel")
component.async_register_entity_service(SERVICE_FINISH, {}, "async_finish")
await component.async_add_entities(entities)
return True

View File

@ -7,7 +7,6 @@ import voluptuous as vol
from homeassistant.const import CONF_NAME
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers import discovery
from homeassistant.helpers.config_validation import ENTITY_SERVICE_SCHEMA
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.restore_state import RestoreEntity
@ -39,11 +38,8 @@ ATTR_TARIFFS = "tariffs"
DEFAULT_OFFSET = timedelta(hours=0)
SERVICE_SELECT_TARIFF_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{vol.Required(ATTR_TARIFF): cv.string}
)
METER_CONFIG_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
METER_CONFIG_SCHEMA = vol.Schema(
{
vol.Required(CONF_SOURCE_SENSOR): cv.entity_id,
vol.Optional(CONF_NAME): cv.string,
@ -110,16 +106,16 @@ async def async_setup(hass, config):
register_services = True
if register_services:
component.async_register_entity_service(SERVICE_RESET, {}, "async_reset_meters")
component.async_register_entity_service(
SERVICE_RESET, ENTITY_SERVICE_SCHEMA, "async_reset_meters"
SERVICE_SELECT_TARIFF,
{vol.Required(ATTR_TARIFF): cv.string},
"async_select_tariff",
)
component.async_register_entity_service(
SERVICE_SELECT_TARIFF, SERVICE_SELECT_TARIFF_SCHEMA, "async_select_tariff"
)
component.async_register_entity_service(
SERVICE_SELECT_NEXT_TARIFF, ENTITY_SERVICE_SCHEMA, "async_next_tariff"
SERVICE_SELECT_NEXT_TARIFF, {}, "async_next_tariff"
)
return True

View File

@ -19,7 +19,7 @@ from homeassistant.const import ( # noqa: F401 # STATE_PAUSED/IDLE are API
from homeassistant.loader import bind_hass
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import ( # noqa: F401
ENTITY_SERVICE_SCHEMA,
make_entity_service_schema,
PLATFORM_SCHEMA,
PLATFORM_SCHEMA_BASE,
)
@ -55,16 +55,6 @@ SERVICE_START = "start"
SERVICE_PAUSE = "pause"
SERVICE_STOP = "stop"
VACUUM_SET_FAN_SPEED_SERVICE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{vol.Required(ATTR_FAN_SPEED): cv.string}
)
VACUUM_SEND_COMMAND_SERVICE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
{
vol.Required(ATTR_COMMAND): cv.string,
vol.Optional(ATTR_PARAMS): vol.Any(dict, cv.ensure_list),
}
)
STATE_CLEANING = "cleaning"
STATE_DOCKED = "docked"
@ -106,43 +96,32 @@ async def async_setup(hass, config):
await component.async_setup(config)
component.async_register_entity_service(SERVICE_TURN_ON, {}, "async_turn_on")
component.async_register_entity_service(SERVICE_TURN_OFF, {}, "async_turn_off")
component.async_register_entity_service(SERVICE_TOGGLE, {}, "async_toggle")
component.async_register_entity_service(
SERVICE_TURN_ON, ENTITY_SERVICE_SCHEMA, "async_turn_on"
SERVICE_START_PAUSE, {}, "async_start_pause"
)
component.async_register_entity_service(SERVICE_START, {}, "async_start")
component.async_register_entity_service(SERVICE_PAUSE, {}, "async_pause")
component.async_register_entity_service(
SERVICE_TURN_OFF, ENTITY_SERVICE_SCHEMA, "async_turn_off"
)
component.async_register_entity_service(
SERVICE_TOGGLE, ENTITY_SERVICE_SCHEMA, "async_toggle"
)
component.async_register_entity_service(
SERVICE_START_PAUSE, ENTITY_SERVICE_SCHEMA, "async_start_pause"
)
component.async_register_entity_service(
SERVICE_START, ENTITY_SERVICE_SCHEMA, "async_start"
)
component.async_register_entity_service(
SERVICE_PAUSE, ENTITY_SERVICE_SCHEMA, "async_pause"
)
component.async_register_entity_service(
SERVICE_RETURN_TO_BASE, ENTITY_SERVICE_SCHEMA, "async_return_to_base"
)
component.async_register_entity_service(
SERVICE_CLEAN_SPOT, ENTITY_SERVICE_SCHEMA, "async_clean_spot"
)
component.async_register_entity_service(
SERVICE_LOCATE, ENTITY_SERVICE_SCHEMA, "async_locate"
)
component.async_register_entity_service(
SERVICE_STOP, ENTITY_SERVICE_SCHEMA, "async_stop"
SERVICE_RETURN_TO_BASE, {}, "async_return_to_base"
)
component.async_register_entity_service(SERVICE_CLEAN_SPOT, {}, "async_clean_spot")
component.async_register_entity_service(SERVICE_LOCATE, {}, "async_locate")
component.async_register_entity_service(SERVICE_STOP, {}, "async_stop")
component.async_register_entity_service(
SERVICE_SET_FAN_SPEED,
VACUUM_SET_FAN_SPEED_SERVICE_SCHEMA,
{vol.Required(ATTR_FAN_SPEED): cv.string},
"async_set_fan_speed",
)
component.async_register_entity_service(
SERVICE_SEND_COMMAND, VACUUM_SEND_COMMAND_SERVICE_SCHEMA, "async_send_command"
SERVICE_SEND_COMMAND,
{
vol.Required(ATTR_COMMAND): cv.string,
vol.Optional(ATTR_PARAMS): vol.Any(dict, cv.ensure_list),
},
"async_send_command",
)
return True

View File

@ -25,7 +25,7 @@ from homeassistant.const import (
from homeassistant.core import callback
from homeassistant.helpers import discovery
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import ENTITY_SERVICE_SCHEMA
from homeassistant.helpers.config_validation import make_entity_service_schema
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.event import track_time_interval
@ -131,11 +131,11 @@ CONFIG_SCHEMA = vol.Schema(
extra=vol.ALLOW_EXTRA,
)
RENAME_DEVICE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
RENAME_DEVICE_SCHEMA = make_entity_service_schema(
{vol.Required(ATTR_NAME): cv.string}, extra=vol.ALLOW_EXTRA
)
DELETE_DEVICE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA)
DELETE_DEVICE_SCHEMA = make_entity_service_schema({}, extra=vol.ALLOW_EXTRA)
SET_PAIRING_MODE_SCHEMA = vol.Schema(
{
@ -146,31 +146,31 @@ SET_PAIRING_MODE_SCHEMA = vol.Schema(
extra=vol.ALLOW_EXTRA,
)
SET_VOLUME_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
SET_VOLUME_SCHEMA = make_entity_service_schema(
{vol.Required(ATTR_VOLUME): vol.In(VOLUMES)}
)
SET_SIREN_TONE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
SET_SIREN_TONE_SCHEMA = make_entity_service_schema(
{vol.Required(ATTR_TONE): vol.In(TONES)}
)
SET_CHIME_MODE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
SET_CHIME_MODE_SCHEMA = make_entity_service_schema(
{vol.Required(ATTR_TONE): vol.In(CHIME_TONES)}
)
SET_AUTO_SHUTOFF_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
SET_AUTO_SHUTOFF_SCHEMA = make_entity_service_schema(
{vol.Required(ATTR_AUTO_SHUTOFF): vol.In(AUTO_SHUTOFF_TIMES)}
)
SET_STROBE_ENABLED_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
SET_STROBE_ENABLED_SCHEMA = make_entity_service_schema(
{vol.Required(ATTR_ENABLED): cv.boolean}
)
ENABLED_SIREN_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
ENABLED_SIREN_SCHEMA = make_entity_service_schema(
{vol.Required(ATTR_ENABLED): cv.boolean}
)
DIAL_CONFIG_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
DIAL_CONFIG_SCHEMA = make_entity_service_schema(
{
vol.Optional(ATTR_MIN_VALUE): vol.Coerce(int),
vol.Optional(ATTR_MAX_VALUE): vol.Coerce(int),
@ -182,7 +182,7 @@ DIAL_CONFIG_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
}
)
DIAL_STATE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
DIAL_STATE_SCHEMA = make_entity_service_schema(
{
vol.Required(ATTR_VALUE): vol.Coerce(int),
vol.Optional(ATTR_LABELS): cv.ensure_list(cv.string),

View File

@ -715,12 +715,23 @@ PLATFORM_SCHEMA = vol.Schema(
PLATFORM_SCHEMA_BASE = PLATFORM_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA)
ENTITY_SERVICE_SCHEMA = vol.Schema(
{
vol.Optional(ATTR_ENTITY_ID): comp_entity_ids,
vol.Optional(ATTR_AREA_ID): vol.All(ensure_list, [str]),
}
)
def make_entity_service_schema(
schema: dict, *, extra: int = vol.PREVENT_EXTRA
) -> vol.All:
"""Create an entity service schema."""
return vol.All(
vol.Schema(
{
**schema,
vol.Optional(ATTR_ENTITY_ID): comp_entity_ids,
vol.Optional(ATTR_AREA_ID): vol.All(ensure_list, [str]),
},
extra=extra,
),
has_at_least_one_key(ATTR_ENTITY_ID, ATTR_AREA_ID),
)
EVENT_SCHEMA = vol.Schema(
{

View File

@ -15,7 +15,7 @@ from homeassistant.const import (
from homeassistant.core import callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_per_platform, discovery
from homeassistant.helpers.config_validation import ENTITY_SERVICE_SCHEMA
from homeassistant.helpers.config_validation import make_entity_service_schema
from homeassistant.helpers.service import async_extract_entity_ids
from homeassistant.loader import bind_hass, async_get_integration
from homeassistant.util import slugify
@ -173,24 +173,16 @@ class EntityComponent:
async def async_extract_from_service(self, service, expand_group=True):
"""Extract all known and available entities from a service call.
Will return all entities if no entities specified in call.
Will return an empty list if entities specified but unknown.
This method must be run in the event loop.
"""
data_ent_id = service.data.get(ATTR_ENTITY_ID)
if data_ent_id in (None, ENTITY_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: %s",
service.domain,
service.service,
ENTITY_MATCH_ALL,
)
if data_ent_id is None:
return []
if data_ent_id == ENTITY_MATCH_ALL:
return [entity for entity in self.entities if entity.available]
entity_ids = await async_extract_entity_ids(self.hass, service, expand_group)
@ -204,7 +196,7 @@ class EntityComponent:
def async_register_entity_service(self, name, schema, func, required_features=None):
"""Register an entity service."""
if isinstance(schema, dict):
schema = ENTITY_SERVICE_SCHEMA.extend(schema)
schema = make_entity_service_schema(schema)
async def handle_service(call):
"""Handle the service."""

View File

@ -260,19 +260,7 @@ async def entity_service_call(
else:
entity_perms = None
# Are we trying to target all entities
if ATTR_ENTITY_ID in call.data:
target_all_entities = call.data[ATTR_ENTITY_ID] == ENTITY_MATCH_ALL
else:
# Remove the service_name parameter along with this warning
_LOGGER.warning(
"Not passing an entity ID to a service to target all "
"entities is deprecated. Update your call to %s to be "
"instead: entity_id: %s",
service_name,
ENTITY_MATCH_ALL,
)
target_all_entities = True
target_all_entities = call.data.get(ATTR_ENTITY_ID) == ENTITY_MATCH_ALL
if not target_all_entities:
# A set of entities we're trying to target.

View File

@ -13,11 +13,12 @@ from homeassistant.const import (
SERVICE_ALARM_ARM_AWAY,
SERVICE_ALARM_ARM_NIGHT,
SERVICE_ALARM_ARM_CUSTOM_BYPASS,
ENTITY_MATCH_ALL,
)
from homeassistant.loader import bind_hass
async def async_alarm_disarm(hass, code=None, entity_id=None):
async def async_alarm_disarm(hass, code=None, entity_id=ENTITY_MATCH_ALL):
"""Send the alarm the command for disarm."""
data = {}
if code:
@ -29,7 +30,7 @@ async def async_alarm_disarm(hass, code=None, entity_id=None):
@bind_hass
def alarm_disarm(hass, code=None, entity_id=None):
def alarm_disarm(hass, code=None, entity_id=ENTITY_MATCH_ALL):
"""Send the alarm the command for disarm."""
data = {}
if code:
@ -40,7 +41,7 @@ def alarm_disarm(hass, code=None, entity_id=None):
hass.services.call(DOMAIN, SERVICE_ALARM_DISARM, data)
async def async_alarm_arm_home(hass, code=None, entity_id=None):
async def async_alarm_arm_home(hass, code=None, entity_id=ENTITY_MATCH_ALL):
"""Send the alarm the command for disarm."""
data = {}
if code:
@ -52,7 +53,7 @@ async def async_alarm_arm_home(hass, code=None, entity_id=None):
@bind_hass
def alarm_arm_home(hass, code=None, entity_id=None):
def alarm_arm_home(hass, code=None, entity_id=ENTITY_MATCH_ALL):
"""Send the alarm the command for arm home."""
data = {}
if code:
@ -63,7 +64,7 @@ def alarm_arm_home(hass, code=None, entity_id=None):
hass.services.call(DOMAIN, SERVICE_ALARM_ARM_HOME, data)
async def async_alarm_arm_away(hass, code=None, entity_id=None):
async def async_alarm_arm_away(hass, code=None, entity_id=ENTITY_MATCH_ALL):
"""Send the alarm the command for disarm."""
data = {}
if code:
@ -75,7 +76,7 @@ async def async_alarm_arm_away(hass, code=None, entity_id=None):
@bind_hass
def alarm_arm_away(hass, code=None, entity_id=None):
def alarm_arm_away(hass, code=None, entity_id=ENTITY_MATCH_ALL):
"""Send the alarm the command for arm away."""
data = {}
if code:
@ -86,7 +87,7 @@ def alarm_arm_away(hass, code=None, entity_id=None):
hass.services.call(DOMAIN, SERVICE_ALARM_ARM_AWAY, data)
async def async_alarm_arm_night(hass, code=None, entity_id=None):
async def async_alarm_arm_night(hass, code=None, entity_id=ENTITY_MATCH_ALL):
"""Send the alarm the command for disarm."""
data = {}
if code:
@ -98,7 +99,7 @@ async def async_alarm_arm_night(hass, code=None, entity_id=None):
@bind_hass
def alarm_arm_night(hass, code=None, entity_id=None):
def alarm_arm_night(hass, code=None, entity_id=ENTITY_MATCH_ALL):
"""Send the alarm the command for arm night."""
data = {}
if code:
@ -109,7 +110,7 @@ def alarm_arm_night(hass, code=None, entity_id=None):
hass.services.call(DOMAIN, SERVICE_ALARM_ARM_NIGHT, data)
async def async_alarm_trigger(hass, code=None, entity_id=None):
async def async_alarm_trigger(hass, code=None, entity_id=ENTITY_MATCH_ALL):
"""Send the alarm the command for disarm."""
data = {}
if code:
@ -121,7 +122,7 @@ async def async_alarm_trigger(hass, code=None, entity_id=None):
@bind_hass
def alarm_trigger(hass, code=None, entity_id=None):
def alarm_trigger(hass, code=None, entity_id=ENTITY_MATCH_ALL):
"""Send the alarm the command for trigger."""
data = {}
if code:
@ -132,7 +133,7 @@ def alarm_trigger(hass, code=None, entity_id=None):
hass.services.call(DOMAIN, SERVICE_ALARM_TRIGGER, data)
async def async_alarm_arm_custom_bypass(hass, code=None, entity_id=None):
async def async_alarm_arm_custom_bypass(hass, code=None, entity_id=ENTITY_MATCH_ALL):
"""Send the alarm the command for disarm."""
data = {}
if code:
@ -146,7 +147,7 @@ async def async_alarm_arm_custom_bypass(hass, code=None, entity_id=None):
@bind_hass
def alarm_arm_custom_bypass(hass, code=None, entity_id=None):
def alarm_arm_custom_bypass(hass, code=None, entity_id=ENTITY_MATCH_ALL):
"""Send the alarm the command for arm custom bypass."""
data = {}
if code:

View File

@ -10,33 +10,34 @@ from homeassistant.const import (
SERVICE_TURN_OFF,
SERVICE_TOGGLE,
SERVICE_RELOAD,
ENTITY_MATCH_ALL,
)
from homeassistant.loader import bind_hass
@bind_hass
async def async_turn_on(hass, entity_id=None):
async def async_turn_on(hass, entity_id=ENTITY_MATCH_ALL):
"""Turn on specified automation or all."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
await hass.services.async_call(DOMAIN, SERVICE_TURN_ON, data)
@bind_hass
async def async_turn_off(hass, entity_id=None):
async def async_turn_off(hass, entity_id=ENTITY_MATCH_ALL):
"""Turn off specified automation or all."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
await hass.services.async_call(DOMAIN, SERVICE_TURN_OFF, data)
@bind_hass
async def async_toggle(hass, entity_id=None):
async def async_toggle(hass, entity_id=ENTITY_MATCH_ALL):
"""Toggle specified automation or all."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
await hass.services.async_call(DOMAIN, SERVICE_TOGGLE, data)
@bind_hass
async def async_trigger(hass, entity_id=None):
async def async_trigger(hass, entity_id=ENTITY_MATCH_ALL):
"""Trigger specified automation or all."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
await hass.services.async_call(DOMAIN, SERVICE_TRIGGER, data)

View File

@ -13,20 +13,25 @@ from homeassistant.components.camera.const import (
DATA_CAMERA_PREFS,
PREF_PRELOAD_STREAM,
)
from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON
from homeassistant.const import (
ATTR_ENTITY_ID,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
ENTITY_MATCH_ALL,
)
from homeassistant.core import callback
from homeassistant.loader import bind_hass
@bind_hass
async def async_turn_off(hass, entity_id=None):
async def async_turn_off(hass, entity_id=ENTITY_MATCH_ALL):
"""Turn off camera."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
await hass.services.async_call(DOMAIN, SERVICE_TURN_OFF, data)
@bind_hass
async def async_turn_on(hass, entity_id=None):
async def async_turn_on(hass, entity_id=ENTITY_MATCH_ALL):
"""Turn on camera, and set operation mode."""
data = {}
if entity_id is not None:
@ -36,7 +41,7 @@ async def async_turn_on(hass, entity_id=None):
@bind_hass
def enable_motion_detection(hass, entity_id=None):
def enable_motion_detection(hass, entity_id=ENTITY_MATCH_ALL):
"""Enable Motion Detection."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
hass.async_add_job(hass.services.async_call(DOMAIN, SERVICE_ENABLE_MOTION, data))
@ -44,7 +49,7 @@ def enable_motion_detection(hass, entity_id=None):
@bind_hass
@callback
def async_snapshot(hass, filename, entity_id=None):
def async_snapshot(hass, filename, entity_id=ENTITY_MATCH_ALL):
"""Make a snapshot from a camera."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
data[ATTR_FILENAME] = filename

View File

@ -27,11 +27,12 @@ from homeassistant.const import (
ATTR_TEMPERATURE,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
ENTITY_MATCH_ALL,
)
from homeassistant.loader import bind_hass
async def async_set_preset_mode(hass, preset_mode, entity_id=None):
async def async_set_preset_mode(hass, preset_mode, entity_id=ENTITY_MATCH_ALL):
"""Set new preset mode."""
data = {ATTR_PRESET_MODE: preset_mode}
@ -42,7 +43,7 @@ async def async_set_preset_mode(hass, preset_mode, entity_id=None):
@bind_hass
def set_preset_mode(hass, preset_mode, entity_id=None):
def set_preset_mode(hass, preset_mode, entity_id=ENTITY_MATCH_ALL):
"""Set new preset mode."""
data = {ATTR_PRESET_MODE: preset_mode}
@ -52,7 +53,7 @@ def set_preset_mode(hass, preset_mode, entity_id=None):
hass.services.call(DOMAIN, SERVICE_SET_PRESET_MODE, data)
async def async_set_aux_heat(hass, aux_heat, entity_id=None):
async def async_set_aux_heat(hass, aux_heat, entity_id=ENTITY_MATCH_ALL):
"""Turn all or specified climate devices auxiliary heater on."""
data = {ATTR_AUX_HEAT: aux_heat}
@ -63,7 +64,7 @@ async def async_set_aux_heat(hass, aux_heat, entity_id=None):
@bind_hass
def set_aux_heat(hass, aux_heat, entity_id=None):
def set_aux_heat(hass, aux_heat, entity_id=ENTITY_MATCH_ALL):
"""Turn all or specified climate devices auxiliary heater on."""
data = {ATTR_AUX_HEAT: aux_heat}
@ -76,7 +77,7 @@ def set_aux_heat(hass, aux_heat, entity_id=None):
async def async_set_temperature(
hass,
temperature=None,
entity_id=None,
entity_id=ENTITY_MATCH_ALL,
target_temp_high=None,
target_temp_low=None,
hvac_mode=None,
@ -103,7 +104,7 @@ async def async_set_temperature(
def set_temperature(
hass,
temperature=None,
entity_id=None,
entity_id=ENTITY_MATCH_ALL,
target_temp_high=None,
target_temp_low=None,
hvac_mode=None,
@ -124,7 +125,7 @@ def set_temperature(
hass.services.call(DOMAIN, SERVICE_SET_TEMPERATURE, kwargs)
async def async_set_humidity(hass, humidity, entity_id=None):
async def async_set_humidity(hass, humidity, entity_id=ENTITY_MATCH_ALL):
"""Set new target humidity."""
data = {ATTR_HUMIDITY: humidity}
@ -135,7 +136,7 @@ async def async_set_humidity(hass, humidity, entity_id=None):
@bind_hass
def set_humidity(hass, humidity, entity_id=None):
def set_humidity(hass, humidity, entity_id=ENTITY_MATCH_ALL):
"""Set new target humidity."""
data = {ATTR_HUMIDITY: humidity}
@ -145,7 +146,7 @@ def set_humidity(hass, humidity, entity_id=None):
hass.services.call(DOMAIN, SERVICE_SET_HUMIDITY, data)
async def async_set_fan_mode(hass, fan, entity_id=None):
async def async_set_fan_mode(hass, fan, entity_id=ENTITY_MATCH_ALL):
"""Set all or specified climate devices fan mode on."""
data = {ATTR_FAN_MODE: fan}
@ -156,7 +157,7 @@ async def async_set_fan_mode(hass, fan, entity_id=None):
@bind_hass
def set_fan_mode(hass, fan, entity_id=None):
def set_fan_mode(hass, fan, entity_id=ENTITY_MATCH_ALL):
"""Set all or specified climate devices fan mode on."""
data = {ATTR_FAN_MODE: fan}
@ -166,7 +167,7 @@ def set_fan_mode(hass, fan, entity_id=None):
hass.services.call(DOMAIN, SERVICE_SET_FAN_MODE, data)
async def async_set_hvac_mode(hass, hvac_mode, entity_id=None):
async def async_set_hvac_mode(hass, hvac_mode, entity_id=ENTITY_MATCH_ALL):
"""Set new target operation mode."""
data = {ATTR_HVAC_MODE: hvac_mode}
@ -177,7 +178,7 @@ async def async_set_hvac_mode(hass, hvac_mode, entity_id=None):
@bind_hass
def set_operation_mode(hass, hvac_mode, entity_id=None):
def set_operation_mode(hass, hvac_mode, entity_id=ENTITY_MATCH_ALL):
"""Set new target operation mode."""
data = {ATTR_HVAC_MODE: hvac_mode}
@ -187,7 +188,7 @@ def set_operation_mode(hass, hvac_mode, entity_id=None):
hass.services.call(DOMAIN, SERVICE_SET_HVAC_MODE, data)
async def async_set_swing_mode(hass, swing_mode, entity_id=None):
async def async_set_swing_mode(hass, swing_mode, entity_id=ENTITY_MATCH_ALL):
"""Set new target swing mode."""
data = {ATTR_SWING_MODE: swing_mode}
@ -198,7 +199,7 @@ async def async_set_swing_mode(hass, swing_mode, entity_id=None):
@bind_hass
def set_swing_mode(hass, swing_mode, entity_id=None):
def set_swing_mode(hass, swing_mode, entity_id=ENTITY_MATCH_ALL):
"""Set new target swing mode."""
data = {ATTR_SWING_MODE: swing_mode}
@ -208,7 +209,7 @@ def set_swing_mode(hass, swing_mode, entity_id=None):
hass.services.call(DOMAIN, SERVICE_SET_SWING_MODE, data)
async def async_turn_on(hass, entity_id=None):
async def async_turn_on(hass, entity_id=ENTITY_MATCH_ALL):
"""Turn on device."""
data = {}
@ -218,7 +219,7 @@ async def async_turn_on(hass, entity_id=None):
await hass.services.async_call(DOMAIN, SERVICE_TURN_ON, data, blocking=True)
async def async_turn_off(hass, entity_id=None):
async def async_turn_off(hass, entity_id=ENTITY_MATCH_ALL):
"""Turn off device."""
data = {}

View File

@ -62,10 +62,14 @@ async def test_turn_off(hass):
async def test_turn_off_without_entity_id(hass):
"""Test light turn off all lights."""
await hass.services.async_call("light", "turn_on", {}, blocking=True)
await hass.services.async_call(
"light", "turn_on", {"entity_id": "all"}, blocking=True
)
assert light.is_on(hass, ENTITY_LIGHT)
await hass.services.async_call("light", "turn_off", {}, blocking=True)
await hass.services.async_call(
"light", "turn_off", {"entity_id": "all"}, blocking=True
)
assert not light.is_on(hass, ENTITY_LIGHT)

View File

@ -12,10 +12,15 @@ from homeassistant.components.fan import (
SERVICE_SET_DIRECTION,
SERVICE_SET_SPEED,
)
from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF
from homeassistant.const import (
ATTR_ENTITY_ID,
SERVICE_TURN_ON,
SERVICE_TURN_OFF,
ENTITY_MATCH_ALL,
)
async def async_turn_on(hass, entity_id: str = None, speed: str = None) -> None:
async def async_turn_on(hass, entity_id=ENTITY_MATCH_ALL, speed: str = None) -> None:
"""Turn all or specified fan on."""
data = {
key: value
@ -26,7 +31,7 @@ async def async_turn_on(hass, entity_id: str = None, speed: str = None) -> None:
await hass.services.async_call(DOMAIN, SERVICE_TURN_ON, data, blocking=True)
async def async_turn_off(hass, entity_id: str = None) -> None:
async def async_turn_off(hass, entity_id=ENTITY_MATCH_ALL) -> None:
"""Turn all or specified fan off."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
@ -34,7 +39,7 @@ async def async_turn_off(hass, entity_id: str = None) -> None:
async def async_oscillate(
hass, entity_id: str = None, should_oscillate: bool = True
hass, entity_id=ENTITY_MATCH_ALL, should_oscillate: bool = True
) -> None:
"""Set oscillation on all or specified fan."""
data = {
@ -49,7 +54,7 @@ async def async_oscillate(
await hass.services.async_call(DOMAIN, SERVICE_OSCILLATE, data, blocking=True)
async def async_set_speed(hass, entity_id: str = None, speed: str = None) -> None:
async def async_set_speed(hass, entity_id=ENTITY_MATCH_ALL, speed: str = None) -> None:
"""Set speed for all or specified fan."""
data = {
key: value
@ -61,7 +66,7 @@ async def async_set_speed(hass, entity_id: str = None, speed: str = None) -> Non
async def async_set_direction(
hass, entity_id: str = None, direction: str = None
hass, entity_id=ENTITY_MATCH_ALL, direction: str = None
) -> None:
"""Set direction for all or specified fan."""
data = {

View File

@ -4,20 +4,20 @@ All containing methods are legacy helpers that should not be used by new
components. Instead call the service directly.
"""
from homeassistant.components.image_processing import DOMAIN, SERVICE_SCAN
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.const import ATTR_ENTITY_ID, ENTITY_MATCH_ALL
from homeassistant.core import callback
from homeassistant.loader import bind_hass
@bind_hass
def scan(hass, entity_id=None):
def scan(hass, entity_id=ENTITY_MATCH_ALL):
"""Force process of all cameras or given entity."""
hass.add_job(async_scan, hass, entity_id)
@callback
@bind_hass
def async_scan(hass, entity_id=None):
def async_scan(hass, entity_id=ENTITY_MATCH_ALL):
"""Force process of all cameras or given entity."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
hass.async_add_job(hass.services.async_call(DOMAIN, SERVICE_SCAN, data))

View File

@ -24,6 +24,7 @@ from homeassistant.const import (
SERVICE_TOGGLE,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
ENTITY_MATCH_ALL,
)
from homeassistant.loader import bind_hass
@ -31,7 +32,7 @@ from homeassistant.loader import bind_hass
@bind_hass
def turn_on(
hass,
entity_id=None,
entity_id=ENTITY_MATCH_ALL,
transition=None,
brightness=None,
brightness_pct=None,
@ -69,7 +70,7 @@ def turn_on(
async def async_turn_on(
hass,
entity_id=None,
entity_id=ENTITY_MATCH_ALL,
transition=None,
brightness=None,
brightness_pct=None,
@ -110,12 +111,12 @@ async def async_turn_on(
@bind_hass
def turn_off(hass, entity_id=None, transition=None):
def turn_off(hass, entity_id=ENTITY_MATCH_ALL, transition=None):
"""Turn all or specified light off."""
hass.add_job(async_turn_off, hass, entity_id, transition)
async def async_turn_off(hass, entity_id=None, transition=None):
async def async_turn_off(hass, entity_id=ENTITY_MATCH_ALL, transition=None):
"""Turn all or specified light off."""
data = {
key: value
@ -127,12 +128,12 @@ async def async_turn_off(hass, entity_id=None, transition=None):
@bind_hass
def toggle(hass, entity_id=None, transition=None):
def toggle(hass, entity_id=ENTITY_MATCH_ALL, transition=None):
"""Toggle all or specified light."""
hass.add_job(async_toggle, hass, entity_id, transition)
async def async_toggle(hass, entity_id=None, transition=None):
async def async_toggle(hass, entity_id=ENTITY_MATCH_ALL, transition=None):
"""Toggle all or specified light."""
data = {
key: value

View File

@ -10,12 +10,13 @@ from homeassistant.const import (
SERVICE_LOCK,
SERVICE_UNLOCK,
SERVICE_OPEN,
ENTITY_MATCH_ALL,
)
from homeassistant.loader import bind_hass
@bind_hass
def lock(hass, entity_id=None, code=None):
def lock(hass, entity_id=ENTITY_MATCH_ALL, code=None):
"""Lock all or specified locks."""
data = {}
if code:
@ -26,7 +27,7 @@ def lock(hass, entity_id=None, code=None):
hass.services.call(DOMAIN, SERVICE_LOCK, data)
async def async_lock(hass, entity_id=None, code=None):
async def async_lock(hass, entity_id=ENTITY_MATCH_ALL, code=None):
"""Lock all or specified locks."""
data = {}
if code:
@ -38,7 +39,7 @@ async def async_lock(hass, entity_id=None, code=None):
@bind_hass
def unlock(hass, entity_id=None, code=None):
def unlock(hass, entity_id=ENTITY_MATCH_ALL, code=None):
"""Unlock all or specified locks."""
data = {}
if code:
@ -49,7 +50,7 @@ def unlock(hass, entity_id=None, code=None):
hass.services.call(DOMAIN, SERVICE_UNLOCK, data)
async def async_unlock(hass, entity_id=None, code=None):
async def async_unlock(hass, entity_id=ENTITY_MATCH_ALL, code=None):
"""Lock all or specified locks."""
data = {}
if code:
@ -61,7 +62,7 @@ async def async_unlock(hass, entity_id=None, code=None):
@bind_hass
def open_lock(hass, entity_id=None, code=None):
def open_lock(hass, entity_id=ENTITY_MATCH_ALL, code=None):
"""Open all or specified locks."""
data = {}
if code:
@ -72,7 +73,7 @@ def open_lock(hass, entity_id=None, code=None):
hass.services.call(DOMAIN, SERVICE_OPEN, data)
async def async_open_lock(hass, entity_id=None, code=None):
async def async_open_lock(hass, entity_id=ENTITY_MATCH_ALL, code=None):
"""Lock all or specified locks."""
data = {}
if code:

View File

@ -32,47 +32,48 @@ from homeassistant.const import (
SERVICE_VOLUME_MUTE,
SERVICE_VOLUME_SET,
SERVICE_VOLUME_UP,
ENTITY_MATCH_ALL,
)
from homeassistant.loader import bind_hass
@bind_hass
def turn_on(hass, entity_id=None):
def turn_on(hass, entity_id=ENTITY_MATCH_ALL):
"""Turn on specified media player or all."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_TURN_ON, data)
@bind_hass
def turn_off(hass, entity_id=None):
def turn_off(hass, entity_id=ENTITY_MATCH_ALL):
"""Turn off specified media player or all."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_TURN_OFF, data)
@bind_hass
def toggle(hass, entity_id=None):
def toggle(hass, entity_id=ENTITY_MATCH_ALL):
"""Toggle specified media player or all."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_TOGGLE, data)
@bind_hass
def volume_up(hass, entity_id=None):
def volume_up(hass, entity_id=ENTITY_MATCH_ALL):
"""Send the media player the command for volume up."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_VOLUME_UP, data)
@bind_hass
def volume_down(hass, entity_id=None):
def volume_down(hass, entity_id=ENTITY_MATCH_ALL):
"""Send the media player the command for volume down."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_VOLUME_DOWN, data)
@bind_hass
def mute_volume(hass, mute, entity_id=None):
def mute_volume(hass, mute, entity_id=ENTITY_MATCH_ALL):
"""Send the media player the command for muting the volume."""
data = {ATTR_MEDIA_VOLUME_MUTED: mute}
@ -83,7 +84,7 @@ def mute_volume(hass, mute, entity_id=None):
@bind_hass
def set_volume_level(hass, volume, entity_id=None):
def set_volume_level(hass, volume, entity_id=ENTITY_MATCH_ALL):
"""Send the media player the command for setting the volume."""
data = {ATTR_MEDIA_VOLUME_LEVEL: volume}
@ -94,49 +95,49 @@ def set_volume_level(hass, volume, entity_id=None):
@bind_hass
def media_play_pause(hass, entity_id=None):
def media_play_pause(hass, entity_id=ENTITY_MATCH_ALL):
"""Send the media player the command for play/pause."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_MEDIA_PLAY_PAUSE, data)
@bind_hass
def media_play(hass, entity_id=None):
def media_play(hass, entity_id=ENTITY_MATCH_ALL):
"""Send the media player the command for play/pause."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_MEDIA_PLAY, data)
@bind_hass
def media_pause(hass, entity_id=None):
def media_pause(hass, entity_id=ENTITY_MATCH_ALL):
"""Send the media player the command for pause."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_MEDIA_PAUSE, data)
@bind_hass
def media_stop(hass, entity_id=None):
def media_stop(hass, entity_id=ENTITY_MATCH_ALL):
"""Send the media player the command for stop."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_MEDIA_STOP, data)
@bind_hass
def media_next_track(hass, entity_id=None):
def media_next_track(hass, entity_id=ENTITY_MATCH_ALL):
"""Send the media player the command for next track."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_MEDIA_NEXT_TRACK, data)
@bind_hass
def media_previous_track(hass, entity_id=None):
def media_previous_track(hass, entity_id=ENTITY_MATCH_ALL):
"""Send the media player the command for prev track."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_MEDIA_PREVIOUS_TRACK, data)
@bind_hass
def media_seek(hass, position, entity_id=None):
def media_seek(hass, position, entity_id=ENTITY_MATCH_ALL):
"""Send the media player the command to seek in current playing media."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
data[ATTR_MEDIA_SEEK_POSITION] = position
@ -144,7 +145,7 @@ def media_seek(hass, position, entity_id=None):
@bind_hass
def play_media(hass, media_type, media_id, entity_id=None, enqueue=None):
def play_media(hass, media_type, media_id, entity_id=ENTITY_MATCH_ALL, enqueue=None):
"""Send the media player the command for playing media."""
data = {ATTR_MEDIA_CONTENT_TYPE: media_type, ATTR_MEDIA_CONTENT_ID: media_id}
@ -158,7 +159,7 @@ def play_media(hass, media_type, media_id, entity_id=None, enqueue=None):
@bind_hass
def select_source(hass, source, entity_id=None):
def select_source(hass, source, entity_id=ENTITY_MATCH_ALL):
"""Send the media player the command to select input source."""
data = {ATTR_INPUT_SOURCE: source}
@ -169,7 +170,7 @@ def select_source(hass, source, entity_id=None):
@bind_hass
def clear_playlist(hass, entity_id=None):
def clear_playlist(hass, entity_id=ENTITY_MATCH_ALL):
"""Send the media player the command for clear playlist."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_CLEAR_PLAYLIST, data)

View File

@ -28,6 +28,7 @@ from homeassistant.const import (
CONF_PLATFORM,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
ENTITY_MATCH_ALL,
)
from homeassistant.setup import async_setup_component
@ -75,29 +76,41 @@ async def test_all_commands(hass, mqtt_mock):
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
await hass.services.async_call(DOMAIN, SERVICE_START, blocking=True)
await hass.services.async_call(
DOMAIN, SERVICE_START, {"entity_id": ENTITY_MATCH_ALL}, blocking=True
)
mqtt_mock.async_publish.assert_called_once_with(COMMAND_TOPIC, "start", 0, False)
mqtt_mock.async_publish.reset_mock()
await hass.services.async_call(DOMAIN, SERVICE_STOP, blocking=True)
await hass.services.async_call(
DOMAIN, SERVICE_STOP, {"entity_id": ENTITY_MATCH_ALL}, blocking=True
)
mqtt_mock.async_publish.assert_called_once_with(COMMAND_TOPIC, "stop", 0, False)
mqtt_mock.async_publish.reset_mock()
await hass.services.async_call(DOMAIN, SERVICE_PAUSE, blocking=True)
await hass.services.async_call(
DOMAIN, SERVICE_PAUSE, {"entity_id": ENTITY_MATCH_ALL}, blocking=True
)
mqtt_mock.async_publish.assert_called_once_with(COMMAND_TOPIC, "pause", 0, False)
mqtt_mock.async_publish.reset_mock()
await hass.services.async_call(DOMAIN, SERVICE_LOCATE, blocking=True)
await hass.services.async_call(
DOMAIN, SERVICE_LOCATE, {"entity_id": ENTITY_MATCH_ALL}, blocking=True
)
mqtt_mock.async_publish.assert_called_once_with(COMMAND_TOPIC, "locate", 0, False)
mqtt_mock.async_publish.reset_mock()
await hass.services.async_call(DOMAIN, SERVICE_CLEAN_SPOT, blocking=True)
await hass.services.async_call(
DOMAIN, SERVICE_CLEAN_SPOT, {"entity_id": ENTITY_MATCH_ALL}, blocking=True
)
mqtt_mock.async_publish.assert_called_once_with(
COMMAND_TOPIC, "clean_spot", 0, False
)
mqtt_mock.async_publish.reset_mock()
await hass.services.async_call(DOMAIN, SERVICE_RETURN_TO_BASE, blocking=True)
await hass.services.async_call(
DOMAIN, SERVICE_RETURN_TO_BASE, {"entity_id": ENTITY_MATCH_ALL}, blocking=True
)
mqtt_mock.async_publish.assert_called_once_with(
COMMAND_TOPIC, "return_to_base", 0, False
)
@ -134,27 +147,39 @@ async def test_commands_without_supported_features(hass, mqtt_mock):
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
await hass.services.async_call(DOMAIN, SERVICE_START, blocking=True)
await hass.services.async_call(
DOMAIN, SERVICE_START, {"entity_id": ENTITY_MATCH_ALL}, blocking=True
)
mqtt_mock.async_publish.assert_not_called()
mqtt_mock.async_publish.reset_mock()
await hass.services.async_call(DOMAIN, SERVICE_PAUSE, blocking=True)
await hass.services.async_call(
DOMAIN, SERVICE_PAUSE, {"entity_id": ENTITY_MATCH_ALL}, blocking=True
)
mqtt_mock.async_publish.assert_not_called()
mqtt_mock.async_publish.reset_mock()
await hass.services.async_call(DOMAIN, SERVICE_STOP, blocking=True)
await hass.services.async_call(
DOMAIN, SERVICE_STOP, {"entity_id": ENTITY_MATCH_ALL}, blocking=True
)
mqtt_mock.async_publish.assert_not_called()
mqtt_mock.async_publish.reset_mock()
await hass.services.async_call(DOMAIN, SERVICE_RETURN_TO_BASE, blocking=True)
await hass.services.async_call(
DOMAIN, SERVICE_RETURN_TO_BASE, {"entity_id": ENTITY_MATCH_ALL}, blocking=True
)
mqtt_mock.async_publish.assert_not_called()
mqtt_mock.async_publish.reset_mock()
await hass.services.async_call(DOMAIN, SERVICE_LOCATE, blocking=True)
await hass.services.async_call(
DOMAIN, SERVICE_LOCATE, {"entity_id": ENTITY_MATCH_ALL}, blocking=True
)
mqtt_mock.async_publish.assert_not_called()
mqtt_mock.async_publish.reset_mock()
await hass.services.async_call(DOMAIN, SERVICE_CLEAN_SPOT, blocking=True)
await hass.services.async_call(
DOMAIN, SERVICE_CLEAN_SPOT, {"entity_id": ENTITY_MATCH_ALL}, blocking=True
)
mqtt_mock.async_publish.assert_not_called()
mqtt_mock.async_publish.reset_mock()

View File

@ -15,12 +15,17 @@ from homeassistant.components.remote import (
SERVICE_LEARN_COMMAND,
SERVICE_SEND_COMMAND,
)
from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON
from homeassistant.const import (
ATTR_ENTITY_ID,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
ENTITY_MATCH_ALL,
)
from homeassistant.loader import bind_hass
@bind_hass
def turn_on(hass, activity=None, entity_id=None):
def turn_on(hass, activity=None, entity_id=ENTITY_MATCH_ALL):
"""Turn all or specified remote on."""
data = {
key: value
@ -31,7 +36,7 @@ def turn_on(hass, activity=None, entity_id=None):
@bind_hass
def turn_off(hass, activity=None, entity_id=None):
def turn_off(hass, activity=None, entity_id=ENTITY_MATCH_ALL):
"""Turn all or specified remote off."""
data = {}
if activity:
@ -45,7 +50,12 @@ def turn_off(hass, activity=None, entity_id=None):
@bind_hass
def send_command(
hass, command, entity_id=None, device=None, num_repeats=None, delay_secs=None
hass,
command,
entity_id=ENTITY_MATCH_ALL,
device=None,
num_repeats=None,
delay_secs=None,
):
"""Send a command to a device."""
data = {ATTR_COMMAND: command}
@ -66,7 +76,12 @@ def send_command(
@bind_hass
def learn_command(
hass, entity_id=None, device=None, command=None, alternative=None, timeout=None
hass,
entity_id=ENTITY_MATCH_ALL,
device=None,
command=None,
alternative=None,
timeout=None,
):
"""Learn a command from a device."""
data = {}

View File

@ -4,12 +4,12 @@ All containing methods are legacy helpers that should not be used by new
components. Instead call the service directly.
"""
from homeassistant.components.scene import DOMAIN
from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_ON
from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_ON, ENTITY_MATCH_ALL
from homeassistant.loader import bind_hass
@bind_hass
def activate(hass, entity_id=None):
def activate(hass, entity_id=ENTITY_MATCH_ALL):
"""Activate a scene."""
data = {}

View File

@ -551,7 +551,9 @@ async def test_set_turn_off(hass, air_conditioner):
await setup_platform(hass, CLIMATE_DOMAIN, devices=[air_conditioner])
state = hass.states.get("climate.air_conditioner")
assert state.state == HVAC_MODE_HEAT_COOL
await hass.services.async_call(CLIMATE_DOMAIN, SERVICE_TURN_OFF, blocking=True)
await hass.services.async_call(
CLIMATE_DOMAIN, SERVICE_TURN_OFF, {"entity_id": "all"}, blocking=True
)
state = hass.states.get("climate.air_conditioner")
assert state.state == HVAC_MODE_OFF
@ -562,7 +564,9 @@ async def test_set_turn_on(hass, air_conditioner):
await setup_platform(hass, CLIMATE_DOMAIN, devices=[air_conditioner])
state = hass.states.get("climate.air_conditioner")
assert state.state == HVAC_MODE_OFF
await hass.services.async_call(CLIMATE_DOMAIN, SERVICE_TURN_ON, blocking=True)
await hass.services.async_call(
CLIMATE_DOMAIN, SERVICE_TURN_ON, {"entity_id": "all"}, blocking=True
)
state = hass.states.get("climate.air_conditioner")
assert state.state == HVAC_MODE_HEAT_COOL

View File

@ -114,7 +114,10 @@ async def test_set_cover_position(hass, device_factory):
await setup_platform(hass, COVER_DOMAIN, devices=[device])
# Act
await hass.services.async_call(
COVER_DOMAIN, SERVICE_SET_COVER_POSITION, {ATTR_POSITION: 50}, blocking=True
COVER_DOMAIN,
SERVICE_SET_COVER_POSITION,
{ATTR_POSITION: 50, "entity_id": "all"},
blocking=True,
)
state = hass.states.get("cover.shade")
@ -136,7 +139,10 @@ async def test_set_cover_position_unsupported(hass, device_factory):
await setup_platform(hass, COVER_DOMAIN, devices=[device])
# Act
await hass.services.async_call(
COVER_DOMAIN, SERVICE_SET_COVER_POSITION, {ATTR_POSITION: 50}, blocking=True
COVER_DOMAIN,
SERVICE_SET_COVER_POSITION,
{"entity_id": "all", ATTR_POSITION: 50},
blocking=True,
)
state = hass.states.get("cover.shade")

View File

@ -4,29 +4,34 @@ All containing methods are legacy helpers that should not be used by new
components. Instead call the service directly.
"""
from homeassistant.components.switch import DOMAIN
from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON
from homeassistant.const import (
ATTR_ENTITY_ID,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
ENTITY_MATCH_ALL,
)
from homeassistant.loader import bind_hass
@bind_hass
def turn_on(hass, entity_id=None):
def turn_on(hass, entity_id=ENTITY_MATCH_ALL):
"""Turn all or specified switch on."""
hass.add_job(async_turn_on, hass, entity_id)
async def async_turn_on(hass, entity_id=None):
async def async_turn_on(hass, entity_id=ENTITY_MATCH_ALL):
"""Turn all or specified switch on."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
await hass.services.async_call(DOMAIN, SERVICE_TURN_ON, data, blocking=True)
@bind_hass
def turn_off(hass, entity_id=None):
def turn_off(hass, entity_id=ENTITY_MATCH_ALL):
"""Turn all or specified switch off."""
hass.add_job(async_turn_off, hass, entity_id)
async def async_turn_off(hass, entity_id=None):
async def async_turn_off(hass, entity_id=ENTITY_MATCH_ALL):
"""Turn all or specified switch off."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
await hass.services.async_call(DOMAIN, SERVICE_TURN_OFF, data, blocking=True)

View File

@ -23,137 +23,138 @@ from homeassistant.const import (
SERVICE_TOGGLE,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
ENTITY_MATCH_ALL,
)
from homeassistant.loader import bind_hass
@bind_hass
def turn_on(hass, entity_id=None):
def turn_on(hass, entity_id=ENTITY_MATCH_ALL):
"""Turn all or specified vacuum on."""
hass.add_job(async_turn_on, hass, entity_id)
async def async_turn_on(hass, entity_id=None):
async def async_turn_on(hass, entity_id=ENTITY_MATCH_ALL):
"""Turn all or specified vacuum on."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
await hass.services.async_call(DOMAIN, SERVICE_TURN_ON, data, blocking=True)
@bind_hass
def turn_off(hass, entity_id=None):
def turn_off(hass, entity_id=ENTITY_MATCH_ALL):
"""Turn all or specified vacuum off."""
hass.add_job(async_turn_off, hass, entity_id)
async def async_turn_off(hass, entity_id=None):
async def async_turn_off(hass, entity_id=ENTITY_MATCH_ALL):
"""Turn all or specified vacuum off."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
await hass.services.async_call(DOMAIN, SERVICE_TURN_OFF, data, blocking=True)
@bind_hass
def toggle(hass, entity_id=None):
def toggle(hass, entity_id=ENTITY_MATCH_ALL):
"""Toggle all or specified vacuum."""
hass.add_job(async_toggle, hass, entity_id)
async def async_toggle(hass, entity_id=None):
async def async_toggle(hass, entity_id=ENTITY_MATCH_ALL):
"""Toggle all or specified vacuum."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
await hass.services.async_call(DOMAIN, SERVICE_TOGGLE, data, blocking=True)
@bind_hass
def locate(hass, entity_id=None):
def locate(hass, entity_id=ENTITY_MATCH_ALL):
"""Locate all or specified vacuum."""
hass.add_job(async_locate, hass, entity_id)
async def async_locate(hass, entity_id=None):
async def async_locate(hass, entity_id=ENTITY_MATCH_ALL):
"""Locate all or specified vacuum."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
await hass.services.async_call(DOMAIN, SERVICE_LOCATE, data, blocking=True)
@bind_hass
def clean_spot(hass, entity_id=None):
def clean_spot(hass, entity_id=ENTITY_MATCH_ALL):
"""Tell all or specified vacuum to perform a spot clean-up."""
hass.add_job(async_clean_spot, hass, entity_id)
async def async_clean_spot(hass, entity_id=None):
async def async_clean_spot(hass, entity_id=ENTITY_MATCH_ALL):
"""Tell all or specified vacuum to perform a spot clean-up."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
await hass.services.async_call(DOMAIN, SERVICE_CLEAN_SPOT, data, blocking=True)
@bind_hass
def return_to_base(hass, entity_id=None):
def return_to_base(hass, entity_id=ENTITY_MATCH_ALL):
"""Tell all or specified vacuum to return to base."""
hass.add_job(async_return_to_base, hass, entity_id)
async def async_return_to_base(hass, entity_id=None):
async def async_return_to_base(hass, entity_id=ENTITY_MATCH_ALL):
"""Tell all or specified vacuum to return to base."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
await hass.services.async_call(DOMAIN, SERVICE_RETURN_TO_BASE, data, blocking=True)
@bind_hass
def start_pause(hass, entity_id=None):
def start_pause(hass, entity_id=ENTITY_MATCH_ALL):
"""Tell all or specified vacuum to start or pause the current task."""
hass.add_job(async_start_pause, hass, entity_id)
async def async_start_pause(hass, entity_id=None):
async def async_start_pause(hass, entity_id=ENTITY_MATCH_ALL):
"""Tell all or specified vacuum to start or pause the current task."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
await hass.services.async_call(DOMAIN, SERVICE_START_PAUSE, data, blocking=True)
@bind_hass
def start(hass, entity_id=None):
def start(hass, entity_id=ENTITY_MATCH_ALL):
"""Tell all or specified vacuum to start or resume the current task."""
hass.add_job(async_start, hass, entity_id)
async def async_start(hass, entity_id=None):
async def async_start(hass, entity_id=ENTITY_MATCH_ALL):
"""Tell all or specified vacuum to start or resume the current task."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
await hass.services.async_call(DOMAIN, SERVICE_START, data, blocking=True)
@bind_hass
def pause(hass, entity_id=None):
def pause(hass, entity_id=ENTITY_MATCH_ALL):
"""Tell all or the specified vacuum to pause the current task."""
hass.add_job(async_pause, hass, entity_id)
async def async_pause(hass, entity_id=None):
async def async_pause(hass, entity_id=ENTITY_MATCH_ALL):
"""Tell all or the specified vacuum to pause the current task."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
await hass.services.async_call(DOMAIN, SERVICE_PAUSE, data, blocking=True)
@bind_hass
def stop(hass, entity_id=None):
def stop(hass, entity_id=ENTITY_MATCH_ALL):
"""Stop all or specified vacuum."""
hass.add_job(async_stop, hass, entity_id)
async def async_stop(hass, entity_id=None):
async def async_stop(hass, entity_id=ENTITY_MATCH_ALL):
"""Stop all or specified vacuum."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
await hass.services.async_call(DOMAIN, SERVICE_STOP, data, blocking=True)
@bind_hass
def set_fan_speed(hass, fan_speed, entity_id=None):
def set_fan_speed(hass, fan_speed, entity_id=ENTITY_MATCH_ALL):
"""Set fan speed for all or specified vacuum."""
hass.add_job(async_set_fan_speed, hass, fan_speed, entity_id)
async def async_set_fan_speed(hass, fan_speed, entity_id=None):
async def async_set_fan_speed(hass, fan_speed, entity_id=ENTITY_MATCH_ALL):
"""Set fan speed for all or specified vacuum."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
data[ATTR_FAN_SPEED] = fan_speed
@ -161,12 +162,12 @@ async def async_set_fan_speed(hass, fan_speed, entity_id=None):
@bind_hass
def send_command(hass, command, params=None, entity_id=None):
def send_command(hass, command, params=None, entity_id=ENTITY_MATCH_ALL):
"""Send command to all or specified vacuum."""
hass.add_job(async_send_command, hass, command, params, entity_id)
async def async_send_command(hass, command, params=None, entity_id=None):
async def async_send_command(hass, command, params=None, entity_id=ENTITY_MATCH_ALL):
"""Send command to all or specified vacuum."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
data[ATTR_COMMAND] = command

View File

@ -12,12 +12,12 @@ from homeassistant.components.water_heater import (
SERVICE_SET_TEMPERATURE,
SERVICE_SET_OPERATION_MODE,
)
from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE
from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE, ENTITY_MATCH_ALL
from homeassistant.loader import bind_hass
@bind_hass
def set_away_mode(hass, away_mode, entity_id=None):
def set_away_mode(hass, away_mode, entity_id=ENTITY_MATCH_ALL):
"""Turn all or specified water_heater devices away mode on."""
data = {ATTR_AWAY_MODE: away_mode}
@ -28,7 +28,9 @@ def set_away_mode(hass, away_mode, entity_id=None):
@bind_hass
def set_temperature(hass, temperature=None, entity_id=None, operation_mode=None):
def set_temperature(
hass, temperature=None, entity_id=ENTITY_MATCH_ALL, operation_mode=None
):
"""Set new target temperature."""
kwargs = {
key: value
@ -44,7 +46,7 @@ def set_temperature(hass, temperature=None, entity_id=None, operation_mode=None)
@bind_hass
def set_operation_mode(hass, operation_mode, entity_id=None):
def set_operation_mode(hass, operation_mode, entity_id=ENTITY_MATCH_ALL):
"""Set new target operation mode."""
data = {ATTR_OPERATION_MODE: operation_mode}

View File

@ -9,6 +9,7 @@ import asynctest
import pytest
import homeassistant.core as ha
from homeassistant.const import ENTITY_MATCH_ALL
from homeassistant.exceptions import PlatformNotReady
from homeassistant.components import group
from homeassistant.helpers.entity_component import EntityComponent
@ -194,7 +195,7 @@ async def test_extract_from_service_available_device(hass):
]
)
call_1 = ha.ServiceCall("test", "service")
call_1 = ha.ServiceCall("test", "service", data={"entity_id": ENTITY_MATCH_ALL})
assert ["test_domain.test_1", "test_domain.test_3"] == sorted(
ent.entity_id for ent in (await component.async_extract_from_service(call_1))
@ -250,7 +251,7 @@ async def test_platform_not_ready(hass):
assert "test_domain.mod1" in hass.config.components
async def test_extract_from_service_returns_all_if_no_entity_id(hass):
async def test_extract_from_service_fails_if_no_entity_id(hass):
"""Test the extraction of everything from service."""
component = EntityComponent(_LOGGER, DOMAIN, hass)
await component.async_add_entities(
@ -259,7 +260,7 @@ async def test_extract_from_service_returns_all_if_no_entity_id(hass):
call = ha.ServiceCall("test", "service")
assert ["test_domain.test_1", "test_domain.test_2"] == sorted(
assert [] == sorted(
ent.entity_id for ent in (await component.async_extract_from_service(call))
)
@ -445,12 +446,9 @@ async def test_extract_all_omit_entity_id(hass, caplog):
call = ha.ServiceCall("test", "service")
assert ["test_domain.test_1", "test_domain.test_2"] == sorted(
assert [] == sorted(
ent.entity_id for ent in await 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):

View File

@ -11,7 +11,7 @@ import pytest
# To prevent circular import when running just this file
import homeassistant.components # noqa: F401
from homeassistant import core as ha, exceptions
from homeassistant.const import STATE_ON, STATE_OFF, ATTR_ENTITY_ID
from homeassistant.const import STATE_ON, STATE_OFF, ATTR_ENTITY_ID, ENTITY_MATCH_ALL
from homeassistant.setup import async_setup_component
import homeassistant.helpers.config_validation as cv
from homeassistant.auth.permissions import PolicyPermissions
@ -334,7 +334,10 @@ async def test_call_context_target_all(hass, mock_service_platform_call, mock_en
[Mock(entities=mock_entities)],
Mock(),
ha.ServiceCall(
"test_domain", "test_service", context=ha.Context(user_id="mock-id")
"test_domain",
"test_service",
data={"entity_id": ENTITY_MATCH_ALL},
context=ha.Context(user_id="mock-id"),
),
)
@ -407,7 +410,9 @@ async def test_call_no_context_target_all(
hass,
[Mock(entities=mock_entities)],
Mock(),
ha.ServiceCall("test_domain", "test_service"),
ha.ServiceCall(
"test_domain", "test_service", data={"entity_id": ENTITY_MATCH_ALL}
),
)
assert len(mock_service_platform_call.mock_calls) == 1
@ -458,9 +463,9 @@ async def test_call_with_match_all(
async def test_call_with_omit_entity_id(
hass, mock_service_platform_call, mock_entities, caplog
hass, mock_service_platform_call, mock_entities
):
"""Check we only target allowed entities if targetting all."""
"""Check service call if we do not pass an entity ID."""
await service.entity_service_call(
hass,
[Mock(entities=mock_entities)],
@ -470,13 +475,7 @@ async def test_call_with_omit_entity_id(
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
assert entities == []
async def test_register_admin_service(hass, hass_read_only_user, hass_admin_user):