Entity service (#15991)

* Add entity service helper

* Use entity service helper

* Context
This commit is contained in:
Paulus Schoutsen 2018-08-16 09:50:11 +02:00 committed by GitHub
parent e52ba87af1
commit b682e48e12
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 276 additions and 387 deletions

View file

@ -26,20 +26,6 @@ ATTR_CHANGED_BY = 'changed_by'
ENTITY_ID_FORMAT = DOMAIN + '.{}' ENTITY_ID_FORMAT = DOMAIN + '.{}'
SERVICE_TO_METHOD = {
SERVICE_ALARM_DISARM: 'alarm_disarm',
SERVICE_ALARM_ARM_HOME: 'alarm_arm_home',
SERVICE_ALARM_ARM_AWAY: 'alarm_arm_away',
SERVICE_ALARM_ARM_NIGHT: 'alarm_arm_night',
SERVICE_ALARM_ARM_CUSTOM_BYPASS: 'alarm_arm_custom_bypass',
SERVICE_ALARM_TRIGGER: 'alarm_trigger'
}
ATTR_TO_PROPERTY = [
ATTR_CODE,
ATTR_CODE_FORMAT
]
ALARM_SERVICE_SCHEMA = vol.Schema({ ALARM_SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_CODE): cv.string, vol.Optional(ATTR_CODE): cv.string,
@ -126,30 +112,30 @@ def async_setup(hass, config):
yield from component.async_setup(config) yield from component.async_setup(config)
@asyncio.coroutine component.async_register_entity_service(
def async_alarm_service_handler(service): SERVICE_ALARM_DISARM, ALARM_SERVICE_SCHEMA,
"""Map services to methods on Alarm.""" 'async_alarm_disarm'
target_alarms = component.async_extract_from_service(service) )
component.async_register_entity_service(
code = service.data.get(ATTR_CODE) SERVICE_ALARM_ARM_HOME, ALARM_SERVICE_SCHEMA,
'async_alarm_arm_home'
method = "async_{}".format(SERVICE_TO_METHOD[service.service]) )
component.async_register_entity_service(
update_tasks = [] SERVICE_ALARM_ARM_AWAY, ALARM_SERVICE_SCHEMA,
for alarm in target_alarms: 'async_alarm_arm_away'
yield from getattr(alarm, method)(code) )
component.async_register_entity_service(
if not alarm.should_poll: SERVICE_ALARM_ARM_NIGHT, ALARM_SERVICE_SCHEMA,
continue 'async_alarm_arm_night'
update_tasks.append(alarm.async_update_ha_state(True)) )
component.async_register_entity_service(
if update_tasks: SERVICE_ALARM_ARM_CUSTOM_BYPASS, ALARM_SERVICE_SCHEMA,
yield from asyncio.wait(update_tasks, loop=hass.loop) 'async_alarm_arm_custom_bypass'
)
for service in SERVICE_TO_METHOD: component.async_register_entity_service(
hass.services.async_register( SERVICE_ALARM_TRIGGER, ALARM_SERVICE_SCHEMA,
DOMAIN, service, async_alarm_service_handler, 'async_alarm_trigger'
schema=ALARM_SERVICE_SCHEMA) )
return True return True

View file

@ -4,7 +4,6 @@ Component to count within automations.
For more details about this component, please refer to the documentation For more details about this component, please refer to the documentation
at https://home-assistant.io/components/counter/ at https://home-assistant.io/components/counter/
""" """
import asyncio
import logging import logging
import voluptuous as vol import voluptuous as vol
@ -114,27 +113,15 @@ async def async_setup(hass, config):
if not entities: if not entities:
return False return False
async def async_handler_service(service): component.async_register_entity_service(
"""Handle a call to the counter services.""" SERVICE_INCREMENT, SERVICE_SCHEMA,
target_counters = component.async_extract_from_service(service) 'async_increment')
component.async_register_entity_service(
if service.service == SERVICE_INCREMENT: SERVICE_DECREMENT, SERVICE_SCHEMA,
attr = 'async_increment' 'async_decrement')
elif service.service == SERVICE_DECREMENT: component.async_register_entity_service(
attr = 'async_decrement' SERVICE_RESET, SERVICE_SCHEMA,
elif service.service == SERVICE_RESET: 'async_reset')
attr = 'async_reset'
tasks = [getattr(counter, attr)() for counter in target_counters]
if tasks:
await asyncio.wait(tasks, loop=hass.loop)
hass.services.async_register(
DOMAIN, SERVICE_INCREMENT, async_handler_service)
hass.services.async_register(
DOMAIN, SERVICE_DECREMENT, async_handler_service)
hass.services.async_register(
DOMAIN, SERVICE_RESET, async_handler_service)
await component.async_add_entities(entities) await component.async_add_entities(entities)
return True return True

View file

@ -4,7 +4,6 @@ Support for Cover devices.
For more details about this platform, please refer to the documentation at For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/cover/ https://home-assistant.io/components/cover/
""" """
import asyncio
from datetime import timedelta from datetime import timedelta
import functools as ft import functools as ft
import logging import logging
@ -73,21 +72,6 @@ COVER_SET_COVER_TILT_POSITION_SCHEMA = COVER_SERVICE_SCHEMA.extend({
vol.All(vol.Coerce(int), vol.Range(min=0, max=100)), vol.All(vol.Coerce(int), vol.Range(min=0, max=100)),
}) })
SERVICE_TO_METHOD = {
SERVICE_OPEN_COVER: {'method': 'async_open_cover'},
SERVICE_CLOSE_COVER: {'method': 'async_close_cover'},
SERVICE_SET_COVER_POSITION: {
'method': 'async_set_cover_position',
'schema': COVER_SET_COVER_POSITION_SCHEMA},
SERVICE_STOP_COVER: {'method': 'async_stop_cover'},
SERVICE_OPEN_COVER_TILT: {'method': 'async_open_cover_tilt'},
SERVICE_CLOSE_COVER_TILT: {'method': 'async_close_cover_tilt'},
SERVICE_STOP_COVER_TILT: {'method': 'async_stop_cover_tilt'},
SERVICE_SET_COVER_TILT_POSITION: {
'method': 'async_set_cover_tilt_position',
'schema': COVER_SET_COVER_TILT_POSITION_SCHEMA},
}
@bind_hass @bind_hass
def is_closed(hass, entity_id=None): def is_closed(hass, entity_id=None):
@ -161,30 +145,46 @@ async def async_setup(hass, config):
await component.async_setup(config) await component.async_setup(config)
async def async_handle_cover_service(service): component.async_register_entity_service(
"""Handle calls to the cover services.""" SERVICE_OPEN_COVER, COVER_SERVICE_SCHEMA,
covers = component.async_extract_from_service(service) 'async_open_cover'
method = SERVICE_TO_METHOD.get(service.service) )
params = service.data.copy()
params.pop(ATTR_ENTITY_ID, None)
# call method component.async_register_entity_service(
update_tasks = [] SERVICE_CLOSE_COVER, COVER_SERVICE_SCHEMA,
for cover in covers: 'async_close_cover'
await getattr(cover, method['method'])(**params) )
if not cover.should_poll:
continue
update_tasks.append(cover.async_update_ha_state(True))
if update_tasks: component.async_register_entity_service(
await asyncio.wait(update_tasks, loop=hass.loop) SERVICE_SET_COVER_POSITION, COVER_SET_COVER_POSITION_SCHEMA,
'async_set_cover_position'
)
component.async_register_entity_service(
SERVICE_STOP_COVER, COVER_SERVICE_SCHEMA,
'async_stop_cover'
)
component.async_register_entity_service(
SERVICE_OPEN_COVER_TILT, COVER_SERVICE_SCHEMA,
'async_open_cover_tilt'
)
component.async_register_entity_service(
SERVICE_CLOSE_COVER_TILT, COVER_SERVICE_SCHEMA,
'async_close_cover_tilt'
)
component.async_register_entity_service(
SERVICE_STOP_COVER_TILT, COVER_SERVICE_SCHEMA,
'async_stop_cover_tilt'
)
component.async_register_entity_service(
SERVICE_SET_COVER_TILT_POSITION, COVER_SET_COVER_TILT_POSITION_SCHEMA,
'async_set_cover_tilt_position'
)
for service_name in SERVICE_TO_METHOD:
schema = SERVICE_TO_METHOD[service_name].get(
'schema', COVER_SERVICE_SCHEMA)
hass.services.async_register(
DOMAIN, service_name, async_handle_cover_service,
schema=schema)
hass.helpers.intent.async_register(intent.ServiceIntentHandler( hass.helpers.intent.async_register(intent.ServiceIntentHandler(
INTENT_OPEN_COVER, DOMAIN, SERVICE_OPEN_COVER, INTENT_OPEN_COVER, DOMAIN, SERVICE_OPEN_COVER,
"Opened {}")) "Opened {}"))

View file

@ -4,7 +4,6 @@ Component to keep track of user controlled booleans for within automation.
For more details about this component, please refer to the documentation For more details about this component, please refer to the documentation
at https://home-assistant.io/components/input_boolean/ at https://home-assistant.io/components/input_boolean/
""" """
import asyncio
import logging import logging
import voluptuous as vol import voluptuous as vol
@ -84,30 +83,20 @@ async def async_setup(hass, config):
if not entities: if not entities:
return False return False
async def async_handler_service(service): component.async_register_entity_service(
"""Handle a calls to the input boolean services.""" SERVICE_TURN_ON, SERVICE_SCHEMA,
target_inputs = component.async_extract_from_service(service) 'async_turn_on'
)
if service.service == SERVICE_TURN_ON: component.async_register_entity_service(
attr = 'async_turn_on' SERVICE_TURN_OFF, SERVICE_SCHEMA,
elif service.service == SERVICE_TURN_OFF: 'async_turn_off'
attr = 'async_turn_off' )
else:
attr = 'async_toggle'
tasks = [getattr(input_b, attr)() for input_b in target_inputs] component.async_register_entity_service(
if tasks: SERVICE_TOGGLE, SERVICE_SCHEMA,
await asyncio.wait(tasks, loop=hass.loop) 'async_toggle'
)
hass.services.async_register(
DOMAIN, SERVICE_TURN_OFF, async_handler_service,
schema=SERVICE_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_TURN_ON, async_handler_service,
schema=SERVICE_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_TOGGLE, async_handler_service,
schema=SERVICE_SCHEMA)
await component.async_add_entities(entities) await component.async_add_entities(entities)
return True return True

View file

@ -82,19 +82,6 @@ CONFIG_SCHEMA = vol.Schema({
}, required=True, extra=vol.ALLOW_EXTRA) }, required=True, extra=vol.ALLOW_EXTRA)
SERVICE_TO_METHOD = {
SERVICE_SET_VALUE: {
'method': 'async_set_value',
'schema': SERVICE_SET_VALUE_SCHEMA},
SERVICE_INCREMENT: {
'method': 'async_increment',
'schema': SERVICE_DEFAULT_SCHEMA},
SERVICE_DECREMENT: {
'method': 'async_decrement',
'schema': SERVICE_DEFAULT_SCHEMA},
}
@bind_hass @bind_hass
def set_value(hass, entity_id, value): def set_value(hass, entity_id, value):
"""Set input_number to value.""" """Set input_number to value."""
@ -144,28 +131,20 @@ def async_setup(hass, config):
if not entities: if not entities:
return False return False
@asyncio.coroutine component.async_register_entity_service(
def async_handle_service(service): SERVICE_SET_VALUE, SERVICE_SET_VALUE_SCHEMA,
"""Handle calls to input_number services.""" 'async_set_value'
target_inputs = component.async_extract_from_service(service) )
method = SERVICE_TO_METHOD.get(service.service)
params = service.data.copy()
params.pop(ATTR_ENTITY_ID, None)
# call method component.async_register_entity_service(
update_tasks = [] SERVICE_INCREMENT, SERVICE_DEFAULT_SCHEMA,
for target_input in target_inputs: 'async_increment'
yield from getattr(target_input, method['method'])(**params) )
if not target_input.should_poll:
continue
update_tasks.append(target_input.async_update_ha_state(True))
if update_tasks: component.async_register_entity_service(
yield from asyncio.wait(update_tasks, loop=hass.loop) SERVICE_DECREMENT, SERVICE_DEFAULT_SCHEMA,
'async_decrement'
for service, data in SERVICE_TO_METHOD.items(): )
hass.services.async_register(
DOMAIN, service, async_handle_service, schema=data['schema'])
yield from component.async_add_entities(entities) yield from component.async_add_entities(entities)
return True return True

View file

@ -129,61 +129,25 @@ def async_setup(hass, config):
if not entities: if not entities:
return False return False
@asyncio.coroutine component.async_register_entity_service(
def async_select_option_service(call): SERVICE_SELECT_OPTION, SERVICE_SELECT_OPTION_SCHEMA,
"""Handle a calls to the input select option service.""" 'async_select_option'
target_inputs = component.async_extract_from_service(call) )
tasks = [input_select.async_select_option(call.data[ATTR_OPTION]) component.async_register_entity_service(
for input_select in target_inputs] SERVICE_SELECT_NEXT, SERVICE_SELECT_NEXT_SCHEMA,
if tasks: lambda entity, call: entity.async_offset_index(1)
yield from asyncio.wait(tasks, loop=hass.loop) )
hass.services.async_register( component.async_register_entity_service(
DOMAIN, SERVICE_SELECT_OPTION, async_select_option_service, SERVICE_SELECT_PREVIOUS, SERVICE_SELECT_PREVIOUS_SCHEMA,
schema=SERVICE_SELECT_OPTION_SCHEMA) lambda entity, call: entity.async_offset_index(-1)
)
@asyncio.coroutine component.async_register_entity_service(
def async_select_next_service(call): SERVICE_SET_OPTIONS, SERVICE_SET_OPTIONS_SCHEMA,
"""Handle a calls to the input select next service.""" 'async_set_options'
target_inputs = component.async_extract_from_service(call) )
tasks = [input_select.async_offset_index(1)
for input_select in target_inputs]
if tasks:
yield from asyncio.wait(tasks, loop=hass.loop)
hass.services.async_register(
DOMAIN, SERVICE_SELECT_NEXT, async_select_next_service,
schema=SERVICE_SELECT_NEXT_SCHEMA)
@asyncio.coroutine
def async_select_previous_service(call):
"""Handle a calls to the input select previous service."""
target_inputs = component.async_extract_from_service(call)
tasks = [input_select.async_offset_index(-1)
for input_select in target_inputs]
if tasks:
yield from asyncio.wait(tasks, loop=hass.loop)
hass.services.async_register(
DOMAIN, SERVICE_SELECT_PREVIOUS, async_select_previous_service,
schema=SERVICE_SELECT_PREVIOUS_SCHEMA)
@asyncio.coroutine
def async_set_options_service(call):
"""Handle a calls to the set options service."""
target_inputs = component.async_extract_from_service(call)
tasks = [input_select.async_set_options(call.data[ATTR_OPTIONS])
for input_select in target_inputs]
if tasks:
yield from asyncio.wait(tasks, loop=hass.loop)
hass.services.async_register(
DOMAIN, SERVICE_SET_OPTIONS, async_set_options_service,
schema=SERVICE_SET_OPTIONS_SCHEMA)
yield from component.async_add_entities(entities) yield from component.async_add_entities(entities)
return True return True

View file

@ -107,19 +107,10 @@ def async_setup(hass, config):
if not entities: if not entities:
return False return False
@asyncio.coroutine component.async_register_entity_service(
def async_set_value_service(call): SERVICE_SET_VALUE, SERVICE_SET_VALUE_SCHEMA,
"""Handle a calls to the input box services.""" 'async_set_value'
target_inputs = component.async_extract_from_service(call) )
tasks = [input_text.async_set_value(call.data[ATTR_VALUE])
for input_text in target_inputs]
if tasks:
yield from asyncio.wait(tasks, loop=hass.loop)
hass.services.async_register(
DOMAIN, SERVICE_SET_VALUE, async_set_value_service,
schema=SERVICE_SET_VALUE_SCHEMA)
yield from component.async_add_entities(entities) yield from component.async_add_entities(entities)
return True return True

View file

@ -101,38 +101,18 @@ def async_setup(hass, config):
yield from component.async_setup(config) yield from component.async_setup(config)
@asyncio.coroutine component.async_register_entity_service(
def async_handle_lock_service(service): SERVICE_UNLOCK, LOCK_SERVICE_SCHEMA,
"""Handle calls to the lock services.""" 'async_unlock'
target_locks = component.async_extract_from_service(service) )
component.async_register_entity_service(
code = service.data.get(ATTR_CODE) SERVICE_LOCK, LOCK_SERVICE_SCHEMA,
'async_lock'
update_tasks = [] )
for entity in target_locks: component.async_register_entity_service(
if service.service == SERVICE_LOCK: SERVICE_OPEN, LOCK_SERVICE_SCHEMA,
yield from entity.async_lock(code=code) 'async_open'
elif service.service == SERVICE_OPEN: )
yield from entity.async_open(code=code)
else:
yield from entity.async_unlock(code=code)
if not entity.should_poll:
continue
update_tasks.append(entity.async_update_ha_state(True))
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)
hass.services.async_register(
DOMAIN, SERVICE_UNLOCK, async_handle_lock_service,
schema=LOCK_SERVICE_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_LOCK, async_handle_lock_service,
schema=LOCK_SERVICE_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_OPEN, async_handle_lock_service,
schema=LOCK_SERVICE_SCHEMA)
return True return True

View file

@ -134,42 +134,25 @@ def async_setup(hass, config):
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, GROUP_NAME_ALL_REMOTES) _LOGGER, DOMAIN, hass, SCAN_INTERVAL, GROUP_NAME_ALL_REMOTES)
yield from component.async_setup(config) yield from component.async_setup(config)
@asyncio.coroutine component.async_register_entity_service(
def async_handle_remote_service(service): SERVICE_TURN_OFF, REMOTE_SERVICE_ACTIVITY_SCHEMA,
"""Handle calls to the remote services.""" 'async_turn_off'
target_remotes = component.async_extract_from_service(service) )
kwargs = service.data.copy()
update_tasks = [] component.async_register_entity_service(
for remote in target_remotes: SERVICE_TURN_ON, REMOTE_SERVICE_ACTIVITY_SCHEMA,
if service.service == SERVICE_TURN_ON: 'async_turn_on'
yield from remote.async_turn_on(**kwargs) )
elif service.service == SERVICE_TOGGLE:
yield from remote.async_toggle(**kwargs)
elif service.service == SERVICE_SEND_COMMAND:
yield from remote.async_send_command(**kwargs)
else:
yield from remote.async_turn_off(**kwargs)
if not remote.should_poll: component.async_register_entity_service(
continue SERVICE_TOGGLE, REMOTE_SERVICE_ACTIVITY_SCHEMA,
update_tasks.append(remote.async_update_ha_state(True)) 'async_toggle'
)
if update_tasks: component.async_register_entity_service(
yield from asyncio.wait(update_tasks, loop=hass.loop) SERVICE_SEND_COMMAND, REMOTE_SERVICE_SEND_COMMAND_SCHEMA,
'async_send_command'
hass.services.async_register( )
DOMAIN, SERVICE_TURN_OFF, async_handle_remote_service,
schema=REMOTE_SERVICE_ACTIVITY_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_TURN_ON, async_handle_remote_service,
schema=REMOTE_SERVICE_ACTIVITY_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_TOGGLE, async_handle_remote_service,
schema=REMOTE_SERVICE_ACTIVITY_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_SEND_COMMAND, async_handle_remote_service,
schema=REMOTE_SERVICE_SEND_COMMAND_SCHEMA)
return True return True

View file

@ -4,7 +4,6 @@ Component to interface with various switches that can be controlled remotely.
For more details about this component, please refer to the documentation For more details about this component, please refer to the documentation
at https://home-assistant.io/components/switch/ at https://home-assistant.io/components/switch/
""" """
import asyncio
from datetime import timedelta from datetime import timedelta
import logging import logging
@ -99,36 +98,20 @@ async def async_setup(hass, config):
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, GROUP_NAME_ALL_SWITCHES) _LOGGER, DOMAIN, hass, SCAN_INTERVAL, GROUP_NAME_ALL_SWITCHES)
await component.async_setup(config) await component.async_setup(config)
async def async_handle_switch_service(service): component.async_register_entity_service(
"""Handle calls to the switch services.""" SERVICE_TURN_OFF, SWITCH_SERVICE_SCHEMA,
target_switches = component.async_extract_from_service(service) 'async_turn_off'
)
update_tasks = [] component.async_register_entity_service(
for switch in target_switches: SERVICE_TURN_ON, SWITCH_SERVICE_SCHEMA,
if service.service == SERVICE_TURN_ON: 'async_turn_on'
await switch.async_turn_on() )
elif service.service == SERVICE_TOGGLE:
await switch.async_toggle()
else:
await switch.async_turn_off()
if not switch.should_poll: component.async_register_entity_service(
continue SERVICE_TOGGLE, SWITCH_SERVICE_SCHEMA,
update_tasks.append( 'async_toggle'
switch.async_update_ha_state(True, service.context)) )
if update_tasks:
await asyncio.wait(update_tasks, loop=hass.loop)
hass.services.async_register(
DOMAIN, SERVICE_TURN_OFF, async_handle_switch_service,
schema=SWITCH_SERVICE_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_TURN_ON, async_handle_switch_service,
schema=SWITCH_SERVICE_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_TOGGLE, async_handle_switch_service,
schema=SWITCH_SERVICE_SCHEMA)
return True return True

View file

@ -4,7 +4,6 @@ Timer component.
For more details about this component, please refer to the documentation For more details about this component, please refer to the documentation
at https://home-assistant.io/components/timer/ at https://home-assistant.io/components/timer/
""" """
import asyncio
import logging import logging
from datetime import timedelta from datetime import timedelta
@ -141,39 +140,18 @@ async def async_setup(hass, config):
if not entities: if not entities:
return False return False
async def async_handler_service(service): component.async_register_entity_service(
"""Handle a call to the timer services.""" SERVICE_START, SERVICE_SCHEMA_DURATION,
target_timers = component.async_extract_from_service(service) 'async_start')
component.async_register_entity_service(
attr = None SERVICE_PAUSE, SERVICE_SCHEMA,
if service.service == SERVICE_PAUSE: 'async_pause')
attr = 'async_pause' component.async_register_entity_service(
elif service.service == SERVICE_CANCEL: SERVICE_CANCEL, SERVICE_SCHEMA,
attr = 'async_cancel' 'async_cancel')
elif service.service == SERVICE_FINISH: component.async_register_entity_service(
attr = 'async_finish' SERVICE_FINISH, SERVICE_SCHEMA,
'async_finish')
tasks = [getattr(timer, attr)() for timer in target_timers if attr]
if service.service == SERVICE_START:
for timer in target_timers:
tasks.append(
timer.async_start(service.data.get(ATTR_DURATION))
)
if tasks:
await asyncio.wait(tasks, loop=hass.loop)
hass.services.async_register(
DOMAIN, SERVICE_START, async_handler_service,
schema=SERVICE_SCHEMA_DURATION)
hass.services.async_register(
DOMAIN, SERVICE_PAUSE, async_handler_service,
schema=SERVICE_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_CANCEL, async_handler_service,
schema=SERVICE_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_FINISH, async_handler_service,
schema=SERVICE_SCHEMA)
await component.async_add_entities(entities) await component.async_add_entities(entities)
return True return True

View file

@ -62,23 +62,6 @@ VACUUM_SEND_COMMAND_SERVICE_SCHEMA = VACUUM_SERVICE_SCHEMA.extend({
vol.Optional(ATTR_PARAMS): vol.Any(dict, cv.ensure_list), vol.Optional(ATTR_PARAMS): vol.Any(dict, cv.ensure_list),
}) })
SERVICE_TO_METHOD = {
SERVICE_TURN_ON: {'method': 'async_turn_on'},
SERVICE_TURN_OFF: {'method': 'async_turn_off'},
SERVICE_TOGGLE: {'method': 'async_toggle'},
SERVICE_START_PAUSE: {'method': 'async_start_pause'},
SERVICE_START: {'method': 'async_start'},
SERVICE_PAUSE: {'method': 'async_pause'},
SERVICE_RETURN_TO_BASE: {'method': 'async_return_to_base'},
SERVICE_CLEAN_SPOT: {'method': 'async_clean_spot'},
SERVICE_LOCATE: {'method': 'async_locate'},
SERVICE_STOP: {'method': 'async_stop'},
SERVICE_SET_FAN_SPEED: {'method': 'async_set_fan_speed',
'schema': VACUUM_SET_FAN_SPEED_SERVICE_SCHEMA},
SERVICE_SEND_COMMAND: {'method': 'async_send_command',
'schema': VACUUM_SEND_COMMAND_SERVICE_SCHEMA},
}
STATE_CLEANING = 'cleaning' STATE_CLEANING = 'cleaning'
STATE_DOCKED = 'docked' STATE_DOCKED = 'docked'
STATE_IDLE = STATE_IDLE STATE_IDLE = STATE_IDLE
@ -207,30 +190,54 @@ def async_setup(hass, config):
yield from component.async_setup(config) yield from component.async_setup(config)
@asyncio.coroutine component.async_register_entity_service(
def async_handle_vacuum_service(service): SERVICE_TURN_ON, VACUUM_SERVICE_SCHEMA,
"""Map services to methods on VacuumDevice.""" 'async_turn_on'
method = SERVICE_TO_METHOD.get(service.service) )
target_vacuums = component.async_extract_from_service(service) component.async_register_entity_service(
params = service.data.copy() SERVICE_TURN_OFF, VACUUM_SERVICE_SCHEMA,
params.pop(ATTR_ENTITY_ID, None) 'async_turn_off'
)
update_tasks = [] component.async_register_entity_service(
for vacuum in target_vacuums: SERVICE_TOGGLE, VACUUM_SERVICE_SCHEMA,
yield from getattr(vacuum, method['method'])(**params) 'async_toggle'
if not vacuum.should_poll: )
continue component.async_register_entity_service(
update_tasks.append(vacuum.async_update_ha_state(True)) SERVICE_START_PAUSE, VACUUM_SERVICE_SCHEMA,
'async_start_pause'
if update_tasks: )
yield from asyncio.wait(update_tasks, loop=hass.loop) component.async_register_entity_service(
SERVICE_START, VACUUM_SERVICE_SCHEMA,
for service in SERVICE_TO_METHOD: 'async_start'
schema = SERVICE_TO_METHOD[service].get( )
'schema', VACUUM_SERVICE_SCHEMA) component.async_register_entity_service(
hass.services.async_register( SERVICE_PAUSE, VACUUM_SERVICE_SCHEMA,
DOMAIN, service, async_handle_vacuum_service, 'async_pause'
schema=schema) )
component.async_register_entity_service(
SERVICE_RETURN_TO_BASE, VACUUM_SERVICE_SCHEMA,
'async_return_to_base'
)
component.async_register_entity_service(
SERVICE_CLEAN_SPOT, VACUUM_SERVICE_SCHEMA,
'async_clean_spot'
)
component.async_register_entity_service(
SERVICE_LOCATE, VACUUM_SERVICE_SCHEMA,
'async_locate'
)
component.async_register_entity_service(
SERVICE_STOP, VACUUM_SERVICE_SCHEMA,
'async_stop'
)
component.async_register_entity_service(
SERVICE_SET_FAN_SPEED, VACUUM_SET_FAN_SPEED_SERVICE_SCHEMA,
'async_set_fan_speed'
)
component.async_register_entity_service(
SERVICE_SEND_COMMAND, VACUUM_SEND_COMMAND_SERVICE_SCHEMA,
'async_send_command'
)
return True return True

View file

@ -142,6 +142,18 @@ class EntityComponent:
return [entity for entity in self.entities return [entity for entity in self.entities
if entity.available and entity.entity_id in entity_ids] if entity.available and entity.entity_id in entity_ids]
@callback
def async_register_entity_service(self, name, schema, func):
"""Register an entity service."""
async def handle_service(call):
"""Handle the service."""
await self.hass.helpers.service.entity_service_call(
self._platforms.values(), func, call
)
self.hass.services.async_register(
self.domain, name, handle_service, schema)
async def _async_setup_platform(self, platform_type, platform_config, async def _async_setup_platform(self, platform_type, platform_config,
discovery_info=None): discovery_info=None):
"""Set up a platform for this component.""" """Set up a platform for this component."""

View file

@ -1,4 +1,5 @@
"""Service calling related helpers.""" """Service calling related helpers."""
import asyncio
import logging import logging
from os import path from os import path
@ -178,3 +179,52 @@ async def async_get_all_descriptions(hass):
descriptions[domain][service] = description descriptions[domain][service] = description
return descriptions return descriptions
@bind_hass
async def entity_service_call(hass, platforms, func, call):
"""Handle an entity service call.
Calls all platforms simultaneously.
"""
tasks = []
all_entities = ATTR_ENTITY_ID not in call.data
if not all_entities:
entity_ids = set(
extract_entity_ids(hass, call, True))
if isinstance(func, str):
data = {key: val for key, val in call.data.items()
if key != ATTR_ENTITY_ID}
else:
data = call
tasks = [
_handle_service_platform_call(func, data, [
entity for entity in platform.entities.values()
if all_entities or entity.entity_id in entity_ids
], call.context) for platform in platforms
]
if tasks:
await asyncio.wait(tasks)
async def _handle_service_platform_call(func, data, entities, context):
"""Handle a function call."""
tasks = []
for entity in entities:
if not entity.available:
continue
if isinstance(func, str):
await getattr(entity, func)(**data)
else:
await func(entity, data)
if entity.should_poll:
tasks.append(entity.async_update_ha_state(True, context))
if tasks:
await asyncio.wait(tasks)