More entity service (#15998)
* Camera use entity service * Convert climate services * Convert light * Convert media player * Migrate fanpull/16000/head
parent
e4d41fe313
commit
1ff1639cef
|
@ -189,74 +189,26 @@ async def async_setup(hass, config):
|
|||
hass.helpers.event.async_track_time_interval(
|
||||
update_tokens, TOKEN_CHANGE_INTERVAL)
|
||||
|
||||
async def async_handle_camera_service(service):
|
||||
"""Handle calls to the camera services."""
|
||||
target_cameras = component.async_extract_from_service(service)
|
||||
|
||||
update_tasks = []
|
||||
for camera in target_cameras:
|
||||
if service.service == SERVICE_ENABLE_MOTION:
|
||||
await camera.async_enable_motion_detection()
|
||||
elif service.service == SERVICE_DISABLE_MOTION:
|
||||
await camera.async_disable_motion_detection()
|
||||
elif service.service == SERVICE_TURN_OFF and \
|
||||
camera.supported_features & SUPPORT_ON_OFF:
|
||||
await camera.async_turn_off()
|
||||
elif service.service == SERVICE_TURN_ON and \
|
||||
camera.supported_features & SUPPORT_ON_OFF:
|
||||
await camera.async_turn_on()
|
||||
|
||||
if not camera.should_poll:
|
||||
continue
|
||||
update_tasks.append(camera.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
await asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
||||
async def async_handle_snapshot_service(service):
|
||||
"""Handle snapshot services calls."""
|
||||
target_cameras = component.async_extract_from_service(service)
|
||||
filename = service.data[ATTR_FILENAME]
|
||||
filename.hass = hass
|
||||
|
||||
for camera in target_cameras:
|
||||
snapshot_file = filename.async_render(
|
||||
variables={ATTR_ENTITY_ID: camera})
|
||||
|
||||
# check if we allow to access to that file
|
||||
if not hass.config.is_allowed_path(snapshot_file):
|
||||
_LOGGER.error(
|
||||
"Can't write %s, no access to path!", snapshot_file)
|
||||
continue
|
||||
|
||||
image = await camera.async_camera_image()
|
||||
|
||||
def _write_image(to_file, image_data):
|
||||
"""Executor helper to write image."""
|
||||
with open(to_file, 'wb') as img_file:
|
||||
img_file.write(image_data)
|
||||
|
||||
try:
|
||||
await hass.async_add_job(
|
||||
_write_image, snapshot_file, image)
|
||||
except OSError as err:
|
||||
_LOGGER.error("Can't write image to file: %s", err)
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_TURN_OFF, async_handle_camera_service,
|
||||
schema=CAMERA_SERVICE_SCHEMA)
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_TURN_ON, async_handle_camera_service,
|
||||
schema=CAMERA_SERVICE_SCHEMA)
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_ENABLE_MOTION, async_handle_camera_service,
|
||||
schema=CAMERA_SERVICE_SCHEMA)
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_DISABLE_MOTION, async_handle_camera_service,
|
||||
schema=CAMERA_SERVICE_SCHEMA)
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SNAPSHOT, async_handle_snapshot_service,
|
||||
schema=CAMERA_SERVICE_SNAPSHOT)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_ENABLE_MOTION, CAMERA_SERVICE_SCHEMA,
|
||||
'async_enable_motion_detection'
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_DISABLE_MOTION, CAMERA_SERVICE_SCHEMA,
|
||||
'async_disable_motion_detection'
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_TURN_OFF, CAMERA_SERVICE_SCHEMA,
|
||||
'async_turn_off'
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_TURN_ON, CAMERA_SERVICE_SCHEMA,
|
||||
'async_turn_on'
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_SNAPSHOT, CAMERA_SERVICE_SNAPSHOT,
|
||||
async_handle_snapshot_service
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -553,3 +505,32 @@ def websocket_camera_thumbnail(hass, connection, msg):
|
|||
msg['id'], 'image_fetch_failed', 'Unable to fetch image'))
|
||||
|
||||
hass.async_add_job(send_camera_still())
|
||||
|
||||
|
||||
async def async_handle_snapshot_service(camera, service):
|
||||
"""Handle snapshot services calls."""
|
||||
hass = camera.hass
|
||||
filename = service.data[ATTR_FILENAME]
|
||||
filename.hass = hass
|
||||
|
||||
snapshot_file = filename.async_render(
|
||||
variables={ATTR_ENTITY_ID: camera})
|
||||
|
||||
# check if we allow to access to that file
|
||||
if not hass.config.is_allowed_path(snapshot_file):
|
||||
_LOGGER.error(
|
||||
"Can't write %s, no access to path!", snapshot_file)
|
||||
return
|
||||
|
||||
image = await camera.async_camera_image()
|
||||
|
||||
def _write_image(to_file, image_data):
|
||||
"""Executor helper to write image."""
|
||||
with open(to_file, 'wb') as img_file:
|
||||
img_file.write(image_data)
|
||||
|
||||
try:
|
||||
await hass.async_add_executor_job(
|
||||
_write_image, snapshot_file, image)
|
||||
except OSError as err:
|
||||
_LOGGER.error("Can't write image to file: %s", err)
|
||||
|
|
|
@ -4,7 +4,6 @@ Provides functionality to interact with climate devices.
|
|||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/climate/
|
||||
"""
|
||||
import asyncio
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
import functools as ft
|
||||
|
@ -250,209 +249,46 @@ async def async_setup(hass, config):
|
|||
EntityComponent(_LOGGER, DOMAIN, hass, SCAN_INTERVAL)
|
||||
await component.async_setup(config)
|
||||
|
||||
async def async_away_mode_set_service(service):
|
||||
"""Set away mode on target climate devices."""
|
||||
target_climate = component.async_extract_from_service(service)
|
||||
|
||||
away_mode = service.data.get(ATTR_AWAY_MODE)
|
||||
|
||||
update_tasks = []
|
||||
for climate in target_climate:
|
||||
if away_mode:
|
||||
await climate.async_turn_away_mode_on()
|
||||
else:
|
||||
await climate.async_turn_away_mode_off()
|
||||
|
||||
if not climate.should_poll:
|
||||
continue
|
||||
update_tasks.append(climate.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
await asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SET_AWAY_MODE, async_away_mode_set_service,
|
||||
schema=SET_AWAY_MODE_SCHEMA)
|
||||
|
||||
async def async_hold_mode_set_service(service):
|
||||
"""Set hold mode on target climate devices."""
|
||||
target_climate = component.async_extract_from_service(service)
|
||||
|
||||
hold_mode = service.data.get(ATTR_HOLD_MODE)
|
||||
|
||||
update_tasks = []
|
||||
for climate in target_climate:
|
||||
await climate.async_set_hold_mode(hold_mode)
|
||||
|
||||
if not climate.should_poll:
|
||||
continue
|
||||
update_tasks.append(climate.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
await asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SET_HOLD_MODE, async_hold_mode_set_service,
|
||||
schema=SET_HOLD_MODE_SCHEMA)
|
||||
|
||||
async def async_aux_heat_set_service(service):
|
||||
"""Set auxiliary heater on target climate devices."""
|
||||
target_climate = component.async_extract_from_service(service)
|
||||
|
||||
aux_heat = service.data.get(ATTR_AUX_HEAT)
|
||||
|
||||
update_tasks = []
|
||||
for climate in target_climate:
|
||||
if aux_heat:
|
||||
await climate.async_turn_aux_heat_on()
|
||||
else:
|
||||
await climate.async_turn_aux_heat_off()
|
||||
|
||||
if not climate.should_poll:
|
||||
continue
|
||||
update_tasks.append(climate.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
await asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SET_AUX_HEAT, async_aux_heat_set_service,
|
||||
schema=SET_AUX_HEAT_SCHEMA)
|
||||
|
||||
async def async_temperature_set_service(service):
|
||||
"""Set temperature on the target climate devices."""
|
||||
target_climate = component.async_extract_from_service(service)
|
||||
|
||||
update_tasks = []
|
||||
for climate in target_climate:
|
||||
kwargs = {}
|
||||
for value, temp in service.data.items():
|
||||
if value in CONVERTIBLE_ATTRIBUTE:
|
||||
kwargs[value] = convert_temperature(
|
||||
temp,
|
||||
hass.config.units.temperature_unit,
|
||||
climate.temperature_unit
|
||||
)
|
||||
else:
|
||||
kwargs[value] = temp
|
||||
|
||||
await climate.async_set_temperature(**kwargs)
|
||||
|
||||
if not climate.should_poll:
|
||||
continue
|
||||
update_tasks.append(climate.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
await asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SET_TEMPERATURE, async_temperature_set_service,
|
||||
schema=SET_TEMPERATURE_SCHEMA)
|
||||
|
||||
async def async_humidity_set_service(service):
|
||||
"""Set humidity on the target climate devices."""
|
||||
target_climate = component.async_extract_from_service(service)
|
||||
|
||||
humidity = service.data.get(ATTR_HUMIDITY)
|
||||
|
||||
update_tasks = []
|
||||
for climate in target_climate:
|
||||
await climate.async_set_humidity(humidity)
|
||||
if not climate.should_poll:
|
||||
continue
|
||||
update_tasks.append(climate.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
await asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SET_HUMIDITY, async_humidity_set_service,
|
||||
schema=SET_HUMIDITY_SCHEMA)
|
||||
|
||||
async def async_fan_mode_set_service(service):
|
||||
"""Set fan mode on target climate devices."""
|
||||
target_climate = component.async_extract_from_service(service)
|
||||
|
||||
fan = service.data.get(ATTR_FAN_MODE)
|
||||
|
||||
update_tasks = []
|
||||
for climate in target_climate:
|
||||
await climate.async_set_fan_mode(fan)
|
||||
if not climate.should_poll:
|
||||
continue
|
||||
update_tasks.append(climate.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
await asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SET_FAN_MODE, async_fan_mode_set_service,
|
||||
schema=SET_FAN_MODE_SCHEMA)
|
||||
|
||||
async def async_operation_set_service(service):
|
||||
"""Set operating mode on the target climate devices."""
|
||||
target_climate = component.async_extract_from_service(service)
|
||||
|
||||
operation_mode = service.data.get(ATTR_OPERATION_MODE)
|
||||
|
||||
update_tasks = []
|
||||
for climate in target_climate:
|
||||
await climate.async_set_operation_mode(operation_mode)
|
||||
if not climate.should_poll:
|
||||
continue
|
||||
update_tasks.append(climate.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
await asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SET_OPERATION_MODE, async_operation_set_service,
|
||||
schema=SET_OPERATION_MODE_SCHEMA)
|
||||
|
||||
async def async_swing_set_service(service):
|
||||
"""Set swing mode on the target climate devices."""
|
||||
target_climate = component.async_extract_from_service(service)
|
||||
|
||||
swing_mode = service.data.get(ATTR_SWING_MODE)
|
||||
|
||||
update_tasks = []
|
||||
for climate in target_climate:
|
||||
await climate.async_set_swing_mode(swing_mode)
|
||||
if not climate.should_poll:
|
||||
continue
|
||||
update_tasks.append(climate.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
await asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SET_SWING_MODE, async_swing_set_service,
|
||||
schema=SET_SWING_MODE_SCHEMA)
|
||||
|
||||
async def async_on_off_service(service):
|
||||
"""Handle on/off calls."""
|
||||
target_climate = component.async_extract_from_service(service)
|
||||
|
||||
update_tasks = []
|
||||
for climate in target_climate:
|
||||
if service.service == SERVICE_TURN_ON:
|
||||
await climate.async_turn_on()
|
||||
elif service.service == SERVICE_TURN_OFF:
|
||||
await climate.async_turn_off()
|
||||
|
||||
if not climate.should_poll:
|
||||
continue
|
||||
update_tasks.append(climate.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
await asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_TURN_OFF, async_on_off_service,
|
||||
schema=ON_OFF_SERVICE_SCHEMA)
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_TURN_ON, async_on_off_service,
|
||||
schema=ON_OFF_SERVICE_SCHEMA)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_SET_AWAY_MODE, SET_AWAY_MODE_SCHEMA,
|
||||
async_service_away_mode
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_SET_HOLD_MODE, SET_HOLD_MODE_SCHEMA,
|
||||
'async_set_hold_mode'
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_SET_AUX_HEAT, SET_AUX_HEAT_SCHEMA,
|
||||
async_service_aux_heat
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_SET_TEMPERATURE, SET_TEMPERATURE_SCHEMA,
|
||||
async_service_temperature_set
|
||||
)
|
||||
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_OPERATION_MODE, SET_OPERATION_MODE_SCHEMA,
|
||||
'async_set_operation_mode'
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_SET_SWING_MODE, SET_SWING_MODE_SCHEMA,
|
||||
'async_set_swing_mode'
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_TURN_OFF, ON_OFF_SERVICE_SCHEMA,
|
||||
'async_turn_off'
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_TURN_ON, ON_OFF_SERVICE_SCHEMA,
|
||||
'async_turn_on'
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -812,3 +648,37 @@ class ClimateDevice(Entity):
|
|||
def max_humidity(self):
|
||||
"""Return the maximum humidity."""
|
||||
return DEFAULT_MAX_HUMIDITY
|
||||
|
||||
|
||||
async def async_service_away_mode(entity, service):
|
||||
"""Handle away mode service."""
|
||||
if service.data[ATTR_AWAY_MODE]:
|
||||
await entity.async_turn_away_mode_on()
|
||||
else:
|
||||
await entity.async_turn_away_mode_off()
|
||||
|
||||
|
||||
async def async_service_aux_heat(entity, service):
|
||||
"""Handle aux heat service."""
|
||||
if service.data[ATTR_AUX_HEAT]:
|
||||
await entity.async_turn_aux_heat_on()
|
||||
else:
|
||||
await entity.async_turn_aux_heat_off()
|
||||
|
||||
|
||||
async def async_service_temperature_set(entity, service):
|
||||
"""Handle set temperature service."""
|
||||
hass = entity.hass
|
||||
kwargs = {}
|
||||
|
||||
for value, temp in service.data.items():
|
||||
if value in CONVERTIBLE_ATTRIBUTE:
|
||||
kwargs[value] = convert_temperature(
|
||||
temp,
|
||||
hass.config.units.temperature_unit,
|
||||
entity.temperature_unit
|
||||
)
|
||||
else:
|
||||
kwargs[value] = temp
|
||||
|
||||
await entity.async_set_temperature(**kwargs)
|
||||
|
|
|
@ -89,33 +89,6 @@ FAN_SET_DIRECTION_SCHEMA = vol.Schema({
|
|||
vol.Optional(ATTR_DIRECTION): cv.string
|
||||
}) # type: dict
|
||||
|
||||
SERVICE_TO_METHOD = {
|
||||
SERVICE_TURN_ON: {
|
||||
'method': 'async_turn_on',
|
||||
'schema': FAN_TURN_ON_SCHEMA,
|
||||
},
|
||||
SERVICE_TURN_OFF: {
|
||||
'method': 'async_turn_off',
|
||||
'schema': FAN_TURN_OFF_SCHEMA,
|
||||
},
|
||||
SERVICE_TOGGLE: {
|
||||
'method': 'async_toggle',
|
||||
'schema': FAN_TOGGLE_SCHEMA,
|
||||
},
|
||||
SERVICE_SET_SPEED: {
|
||||
'method': 'async_set_speed',
|
||||
'schema': FAN_SET_SPEED_SCHEMA,
|
||||
},
|
||||
SERVICE_OSCILLATE: {
|
||||
'method': 'async_oscillate',
|
||||
'schema': FAN_OSCILLATE_SCHEMA,
|
||||
},
|
||||
SERVICE_SET_DIRECTION: {
|
||||
'method': 'async_set_direction',
|
||||
'schema': FAN_SET_DIRECTION_SCHEMA,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@bind_hass
|
||||
def is_on(hass, entity_id: str = None) -> bool:
|
||||
|
@ -204,30 +177,30 @@ def async_setup(hass, config: dict):
|
|||
|
||||
yield from component.async_setup(config)
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_handle_fan_service(service):
|
||||
"""Handle service call for fans."""
|
||||
method = SERVICE_TO_METHOD.get(service.service)
|
||||
params = service.data.copy()
|
||||
|
||||
# Convert the entity ids to valid fan ids
|
||||
target_fans = component.async_extract_from_service(service)
|
||||
params.pop(ATTR_ENTITY_ID, None)
|
||||
|
||||
update_tasks = []
|
||||
for fan in target_fans:
|
||||
yield from getattr(fan, method['method'])(**params)
|
||||
if not fan.should_poll:
|
||||
continue
|
||||
update_tasks.append(fan.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
yield from asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
||||
for service_name in SERVICE_TO_METHOD:
|
||||
schema = SERVICE_TO_METHOD[service_name].get('schema')
|
||||
hass.services.async_register(
|
||||
DOMAIN, service_name, async_handle_fan_service, schema=schema)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_TURN_ON, FAN_TURN_ON_SCHEMA,
|
||||
'async_turn_on'
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_TURN_OFF, FAN_TURN_OFF_SCHEMA,
|
||||
'async_turn_off'
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_TOGGLE, FAN_TOGGLE_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'
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
|
|
@ -332,8 +332,8 @@ async def async_setup(hass, config):
|
|||
if not profiles_valid:
|
||||
return False
|
||||
|
||||
async def async_handle_light_service(service):
|
||||
"""Handle a turn light on or off service call."""
|
||||
async def async_handle_light_on_service(service):
|
||||
"""Handle a turn light on service call."""
|
||||
# Get the validated data
|
||||
params = service.data.copy()
|
||||
|
||||
|
@ -345,17 +345,12 @@ async def async_setup(hass, config):
|
|||
|
||||
update_tasks = []
|
||||
for light in target_lights:
|
||||
if service.service == SERVICE_TURN_ON:
|
||||
pars = params
|
||||
if not pars:
|
||||
pars = params.copy()
|
||||
pars[ATTR_PROFILE] = Profiles.get_default(light.entity_id)
|
||||
preprocess_turn_on_alternatives(pars)
|
||||
await light.async_turn_on(**pars)
|
||||
elif service.service == SERVICE_TURN_OFF:
|
||||
await light.async_turn_off(**params)
|
||||
else:
|
||||
await light.async_toggle(**params)
|
||||
pars = params
|
||||
if not pars:
|
||||
pars = params.copy()
|
||||
pars[ATTR_PROFILE] = Profiles.get_default(light.entity_id)
|
||||
preprocess_turn_on_alternatives(pars)
|
||||
await light.async_turn_on(**pars)
|
||||
|
||||
if not light.should_poll:
|
||||
continue
|
||||
|
@ -368,16 +363,18 @@ async def async_setup(hass, config):
|
|||
|
||||
# Listen for light on and light off service calls.
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_TURN_ON, async_handle_light_service,
|
||||
DOMAIN, SERVICE_TURN_ON, async_handle_light_on_service,
|
||||
schema=LIGHT_TURN_ON_SCHEMA)
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_TURN_OFF, async_handle_light_service,
|
||||
schema=LIGHT_TURN_OFF_SCHEMA)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_TURN_OFF, LIGHT_TURN_OFF_SCHEMA,
|
||||
'async_turn_off'
|
||||
)
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_TOGGLE, async_handle_light_service,
|
||||
schema=LIGHT_TOGGLE_SCHEMA)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_TOGGLE, LIGHT_TOGGLE_SCHEMA,
|
||||
'async_toggle'
|
||||
)
|
||||
|
||||
hass.helpers.intent.async_register(SetIntentHandler())
|
||||
|
||||
|
|
|
@ -151,42 +151,6 @@ MEDIA_PLAYER_SET_SHUFFLE_SCHEMA = MEDIA_PLAYER_SCHEMA.extend({
|
|||
vol.Required(ATTR_MEDIA_SHUFFLE): cv.boolean,
|
||||
})
|
||||
|
||||
SERVICE_TO_METHOD = {
|
||||
SERVICE_TURN_ON: {'method': 'async_turn_on'},
|
||||
SERVICE_TURN_OFF: {'method': 'async_turn_off'},
|
||||
SERVICE_TOGGLE: {'method': 'async_toggle'},
|
||||
SERVICE_VOLUME_UP: {'method': 'async_volume_up'},
|
||||
SERVICE_VOLUME_DOWN: {'method': 'async_volume_down'},
|
||||
SERVICE_MEDIA_PLAY_PAUSE: {'method': 'async_media_play_pause'},
|
||||
SERVICE_MEDIA_PLAY: {'method': 'async_media_play'},
|
||||
SERVICE_MEDIA_PAUSE: {'method': 'async_media_pause'},
|
||||
SERVICE_MEDIA_STOP: {'method': 'async_media_stop'},
|
||||
SERVICE_MEDIA_NEXT_TRACK: {'method': 'async_media_next_track'},
|
||||
SERVICE_MEDIA_PREVIOUS_TRACK: {'method': 'async_media_previous_track'},
|
||||
SERVICE_CLEAR_PLAYLIST: {'method': 'async_clear_playlist'},
|
||||
SERVICE_VOLUME_SET: {
|
||||
'method': 'async_set_volume_level',
|
||||
'schema': MEDIA_PLAYER_SET_VOLUME_SCHEMA},
|
||||
SERVICE_VOLUME_MUTE: {
|
||||
'method': 'async_mute_volume',
|
||||
'schema': MEDIA_PLAYER_MUTE_VOLUME_SCHEMA},
|
||||
SERVICE_MEDIA_SEEK: {
|
||||
'method': 'async_media_seek',
|
||||
'schema': MEDIA_PLAYER_MEDIA_SEEK_SCHEMA},
|
||||
SERVICE_SELECT_SOURCE: {
|
||||
'method': 'async_select_source',
|
||||
'schema': MEDIA_PLAYER_SELECT_SOURCE_SCHEMA},
|
||||
SERVICE_SELECT_SOUND_MODE: {
|
||||
'method': 'async_select_sound_mode',
|
||||
'schema': MEDIA_PLAYER_SELECT_SOUND_MODE_SCHEMA},
|
||||
SERVICE_PLAY_MEDIA: {
|
||||
'method': 'async_play_media',
|
||||
'schema': MEDIA_PLAYER_PLAY_MEDIA_SCHEMA},
|
||||
SERVICE_SHUFFLE_SET: {
|
||||
'method': 'async_set_shuffle',
|
||||
'schema': MEDIA_PLAYER_SET_SHUFFLE_SCHEMA},
|
||||
}
|
||||
|
||||
ATTR_TO_PROPERTY = [
|
||||
ATTR_MEDIA_VOLUME_LEVEL,
|
||||
ATTR_MEDIA_VOLUME_MUTED,
|
||||
|
@ -409,50 +373,89 @@ async def async_setup(hass, config):
|
|||
|
||||
await component.async_setup(config)
|
||||
|
||||
async def async_service_handler(service):
|
||||
"""Map services to methods on MediaPlayerDevice."""
|
||||
method = SERVICE_TO_METHOD.get(service.service)
|
||||
if not method:
|
||||
return
|
||||
|
||||
params = {}
|
||||
if service.service == SERVICE_VOLUME_SET:
|
||||
params['volume'] = service.data.get(ATTR_MEDIA_VOLUME_LEVEL)
|
||||
elif service.service == SERVICE_VOLUME_MUTE:
|
||||
params['mute'] = service.data.get(ATTR_MEDIA_VOLUME_MUTED)
|
||||
elif service.service == SERVICE_MEDIA_SEEK:
|
||||
params['position'] = service.data.get(ATTR_MEDIA_SEEK_POSITION)
|
||||
elif service.service == SERVICE_SELECT_SOURCE:
|
||||
params['source'] = service.data.get(ATTR_INPUT_SOURCE)
|
||||
elif service.service == SERVICE_SELECT_SOUND_MODE:
|
||||
params['sound_mode'] = service.data.get(ATTR_SOUND_MODE)
|
||||
elif service.service == SERVICE_PLAY_MEDIA:
|
||||
params['media_type'] = \
|
||||
service.data.get(ATTR_MEDIA_CONTENT_TYPE)
|
||||
params['media_id'] = service.data.get(ATTR_MEDIA_CONTENT_ID)
|
||||
params[ATTR_MEDIA_ENQUEUE] = \
|
||||
service.data.get(ATTR_MEDIA_ENQUEUE)
|
||||
elif service.service == SERVICE_SHUFFLE_SET:
|
||||
params[ATTR_MEDIA_SHUFFLE] = \
|
||||
service.data.get(ATTR_MEDIA_SHUFFLE)
|
||||
target_players = component.async_extract_from_service(service)
|
||||
|
||||
update_tasks = []
|
||||
for player in target_players:
|
||||
await getattr(player, method['method'])(**params)
|
||||
if not player.should_poll:
|
||||
continue
|
||||
update_tasks.append(player.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
await asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
||||
for service in SERVICE_TO_METHOD:
|
||||
schema = SERVICE_TO_METHOD[service].get(
|
||||
'schema', MEDIA_PLAYER_SCHEMA)
|
||||
hass.services.async_register(
|
||||
DOMAIN, service, async_service_handler,
|
||||
schema=schema)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_TURN_ON, MEDIA_PLAYER_SCHEMA,
|
||||
'async_turn_on'
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_TURN_OFF, MEDIA_PLAYER_SCHEMA,
|
||||
'async_turn_off'
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_TOGGLE, MEDIA_PLAYER_SCHEMA,
|
||||
'async_toggle'
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_VOLUME_UP, MEDIA_PLAYER_SCHEMA,
|
||||
'async_volume_up'
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_VOLUME_DOWN, MEDIA_PLAYER_SCHEMA,
|
||||
'async_volume_down'
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_MEDIA_PLAY_PAUSE, MEDIA_PLAYER_SCHEMA,
|
||||
'async_media_play_pause'
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_MEDIA_PLAY, MEDIA_PLAYER_SCHEMA,
|
||||
'async_media_play'
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_MEDIA_PAUSE, MEDIA_PLAYER_SCHEMA,
|
||||
'async_media_pause'
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_MEDIA_STOP, MEDIA_PLAYER_SCHEMA,
|
||||
'async_media_stop'
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_MEDIA_NEXT_TRACK, MEDIA_PLAYER_SCHEMA,
|
||||
'async_media_next_track'
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_MEDIA_PREVIOUS_TRACK, MEDIA_PLAYER_SCHEMA,
|
||||
'async_media_previous_track'
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_CLEAR_PLAYLIST, MEDIA_PLAYER_SCHEMA,
|
||||
'async_clear_playlist'
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_VOLUME_SET, MEDIA_PLAYER_SET_VOLUME_SCHEMA,
|
||||
lambda entity, call: entity.async_set_volume_level(
|
||||
volume=call.data[ATTR_MEDIA_VOLUME_LEVEL])
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_VOLUME_MUTE, MEDIA_PLAYER_MUTE_VOLUME_SCHEMA,
|
||||
lambda entity, call: entity.async_mute_volume(
|
||||
mute=call.data[ATTR_MEDIA_VOLUME_MUTED])
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_MEDIA_SEEK, MEDIA_PLAYER_MEDIA_SEEK_SCHEMA,
|
||||
lambda entity, call: entity.async_media_seek(
|
||||
position=call.data[ATTR_MEDIA_SEEK_POSITION])
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_SELECT_SOURCE, MEDIA_PLAYER_SELECT_SOURCE_SCHEMA,
|
||||
'async_select_source'
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_SELECT_SOUND_MODE, MEDIA_PLAYER_SELECT_SOUND_MODE_SCHEMA,
|
||||
'async_select_sound_mode'
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_PLAY_MEDIA, MEDIA_PLAYER_PLAY_MEDIA_SCHEMA,
|
||||
lambda entity, call: entity.async_play_media(
|
||||
media_type=call.data[ATTR_MEDIA_CONTENT_TYPE],
|
||||
media_id=call.data[ATTR_MEDIA_CONTENT_ID],
|
||||
enqueue=call.data.get(ATTR_MEDIA_ENQUEUE)
|
||||
)
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_SHUFFLE_SET, MEDIA_PLAYER_SET_SHUFFLE_SCHEMA,
|
||||
'async_set_shuffle'
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
"""The tests for local file camera component."""
|
||||
from unittest.mock import mock_open, patch, PropertyMock
|
||||
from unittest.mock import mock_open, patch
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -67,19 +67,6 @@ async def test_turn_off_invalid_camera(hass, demo_camera):
|
|||
assert demo_camera.state == STATE_STREAMING
|
||||
|
||||
|
||||
async def test_turn_off_unsupport_camera(hass, demo_camera):
|
||||
"""Turn off unsupported camera should quietly fail."""
|
||||
assert demo_camera.state == STATE_STREAMING
|
||||
with patch('homeassistant.components.camera.demo.DemoCamera'
|
||||
'.supported_features', new_callable=PropertyMock) as m:
|
||||
m.return_value = 0
|
||||
|
||||
await camera.async_turn_off(hass, demo_camera.entity_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert demo_camera.state == STATE_STREAMING
|
||||
|
||||
|
||||
async def test_motion_detection(hass):
|
||||
"""Test motion detection services."""
|
||||
# Setup platform
|
||||
|
|
|
@ -356,11 +356,10 @@ def test_signal_repetitions_cancelling(hass, monkeypatch):
|
|||
|
||||
yield from hass.async_block_till_done()
|
||||
|
||||
print(protocol.send_command_ack.call_args_list)
|
||||
assert protocol.send_command_ack.call_args_list[0][0][1] == 'off'
|
||||
assert protocol.send_command_ack.call_args_list[1][0][1] == 'on'
|
||||
assert protocol.send_command_ack.call_args_list[2][0][1] == 'on'
|
||||
assert protocol.send_command_ack.call_args_list[3][0][1] == 'on'
|
||||
assert protocol.send_command_ack.call_args_list[0][0][1] == 'on'
|
||||
assert protocol.send_command_ack.call_args_list[1][0][1] == 'off'
|
||||
assert protocol.send_command_ack.call_args_list[2][0][1] == 'off'
|
||||
assert protocol.send_command_ack.call_args_list[3][0][1] == 'off'
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
|
|
Loading…
Reference in New Issue