Formalize supported_features as entity property (#5794)

* Formalize supported_features as entity property

* Remove extra emulated_hue conditions

* Generate log message in executor
This commit is contained in:
Adam Mills 2017-02-07 23:42:45 -05:00 committed by Paulus Schoutsen
parent 4fa4d7347f
commit ecfe8e0a9a
49 changed files with 193 additions and 165 deletions

View File

@ -8,14 +8,13 @@ from homeassistant import core
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON, SERVICE_VOLUME_SET, ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON, SERVICE_VOLUME_SET,
SERVICE_OPEN_COVER, SERVICE_CLOSE_COVER, STATE_ON, STATE_OFF, SERVICE_OPEN_COVER, SERVICE_CLOSE_COVER, STATE_ON, STATE_OFF,
HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, ATTR_SUPPORTED_FEATURES,
) )
from homeassistant.components.light import ( from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_SUPPORTED_FEATURES, SUPPORT_BRIGHTNESS ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS
) )
from homeassistant.components.media_player import ( from homeassistant.components.media_player import (
ATTR_MEDIA_VOLUME_LEVEL, ATTR_SUPPORTED_MEDIA_COMMANDS, ATTR_MEDIA_VOLUME_LEVEL, SUPPORT_VOLUME_SET,
SUPPORT_VOLUME_SET,
) )
from homeassistant.components.fan import ( from homeassistant.components.fan import (
ATTR_SPEED, SUPPORT_SET_SPEED, SPEED_OFF, SPEED_LOW, ATTR_SPEED, SUPPORT_SET_SPEED, SPEED_OFF, SPEED_LOW,
@ -178,11 +177,10 @@ class HueOneLightChangeView(HomeAssistantView):
# Make sure the entity actually supports brightness # Make sure the entity actually supports brightness
entity_features = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0) entity_features = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
if (entity_features & if entity.domain == "light":
SUPPORT_BRIGHTNESS & if entity_features & SUPPORT_BRIGHTNESS:
(entity.domain == "light")) == SUPPORT_BRIGHTNESS: if brightness is not None:
if brightness is not None: data[ATTR_BRIGHTNESS] = brightness
data[ATTR_BRIGHTNESS] = brightness
# If the requested entity is a script add some variables # If the requested entity is a script add some variables
elif entity.domain == "script": elif entity.domain == "script":
@ -195,9 +193,7 @@ class HueOneLightChangeView(HomeAssistantView):
# If the requested entity is a media player, convert to volume # If the requested entity is a media player, convert to volume
elif entity.domain == "media_player": elif entity.domain == "media_player":
media_commands = entity.attributes.get( if entity_features & SUPPORT_VOLUME_SET:
ATTR_SUPPORTED_MEDIA_COMMANDS, 0)
if media_commands & SUPPORT_VOLUME_SET == SUPPORT_VOLUME_SET:
if brightness is not None: if brightness is not None:
turn_on_needed = True turn_on_needed = True
domain = entity.domain domain = entity.domain
@ -215,9 +211,7 @@ class HueOneLightChangeView(HomeAssistantView):
# If the requested entity is a fan, convert to speed # If the requested entity is a fan, convert to speed
elif entity.domain == "fan": elif entity.domain == "fan":
functions = entity.attributes.get( if entity_features & SUPPORT_SET_SPEED:
ATTR_SUPPORTED_FEATURES, 0)
if (functions & SUPPORT_SET_SPEED) == SUPPORT_SET_SPEED:
if brightness is not None: if brightness is not None:
domain = entity.domain domain = entity.domain
# Convert 0-100 to a fan speed # Convert 0-100 to a fan speed
@ -288,9 +282,10 @@ def parse_hue_api_put_light_body(request_json, entity):
# Make sure the entity actually supports brightness # Make sure the entity actually supports brightness
entity_features = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0) entity_features = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
if (entity_features & SUPPORT_BRIGHTNESS) == SUPPORT_BRIGHTNESS: if entity.domain == "light":
report_brightness = True if entity_features & SUPPORT_BRIGHTNESS:
result = (brightness > 0) report_brightness = True
result = (brightness > 0)
elif (entity.domain == "script" or elif (entity.domain == "script" or
entity.domain == "media_player" or entity.domain == "media_player" or
@ -316,8 +311,9 @@ def get_entity_state(config, entity):
# Make sure the entity actually supports brightness # Make sure the entity actually supports brightness
entity_features = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0) entity_features = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
if (entity_features & SUPPORT_BRIGHTNESS) == SUPPORT_BRIGHTNESS: if entity.domain == "light":
pass if entity_features & SUPPORT_BRIGHTNESS:
pass
elif entity.domain == "media_player": elif entity.domain == "media_player":
level = entity.attributes.get( level = entity.attributes.get(

View File

@ -34,7 +34,6 @@ ENTITY_ID_ALL_FANS = group.ENTITY_ID_FORMAT.format(GROUP_NAME_ALL_FANS)
ENTITY_ID_FORMAT = DOMAIN + '.{}' ENTITY_ID_FORMAT = DOMAIN + '.{}'
# Bitfield of features supported by the fan entity # Bitfield of features supported by the fan entity
ATTR_SUPPORTED_FEATURES = 'supported_features'
SUPPORT_SET_SPEED = 1 SUPPORT_SET_SPEED = 1
SUPPORT_OSCILLATE = 2 SUPPORT_OSCILLATE = 2
SUPPORT_DIRECTION = 4 SUPPORT_DIRECTION = 4
@ -60,7 +59,6 @@ PROP_TO_ATTR = {
'speed': ATTR_SPEED, 'speed': ATTR_SPEED,
'speed_list': ATTR_SPEED_LIST, 'speed_list': ATTR_SPEED_LIST,
'oscillating': ATTR_OSCILLATING, 'oscillating': ATTR_OSCILLATING,
'supported_features': ATTR_SUPPORTED_FEATURES,
'direction': ATTR_DIRECTION, 'direction': ATTR_DIRECTION,
} # type: dict } # type: dict

View File

@ -35,7 +35,6 @@ ENTITY_ID_ALL_LIGHTS = group.ENTITY_ID_FORMAT.format('all_lights')
ENTITY_ID_FORMAT = DOMAIN + ".{}" ENTITY_ID_FORMAT = DOMAIN + ".{}"
# Bitfield of features supported by the light entity # Bitfield of features supported by the light entity
ATTR_SUPPORTED_FEATURES = 'supported_features'
SUPPORT_BRIGHTNESS = 1 SUPPORT_BRIGHTNESS = 1
SUPPORT_COLOR_TEMP = 2 SUPPORT_COLOR_TEMP = 2
SUPPORT_EFFECT = 4 SUPPORT_EFFECT = 4
@ -85,7 +84,6 @@ PROP_TO_ATTR = {
'white_value': ATTR_WHITE_VALUE, 'white_value': ATTR_WHITE_VALUE,
'effect_list': ATTR_EFFECT_LIST, 'effect_list': ATTR_EFFECT_LIST,
'effect': ATTR_EFFECT, 'effect': ATTR_EFFECT,
'supported_features': ATTR_SUPPORTED_FEATURES,
} }
# Service call validation schemas # Service call validation schemas
@ -364,8 +362,6 @@ class Light(ToggleEntity):
data[ATTR_RGB_COLOR] = color_util.color_xy_brightness_to_RGB( data[ATTR_RGB_COLOR] = color_util.color_xy_brightness_to_RGB(
data[ATTR_XY_COLOR][0], data[ATTR_XY_COLOR][1], data[ATTR_XY_COLOR][0], data[ATTR_XY_COLOR][1],
data[ATTR_BRIGHTNESS]) data[ATTR_BRIGHTNESS])
else:
data[ATTR_SUPPORTED_FEATURES] = self.supported_features
return data return data

View File

@ -77,7 +77,6 @@ ATTR_MEDIA_CHANNEL = 'media_channel'
ATTR_MEDIA_PLAYLIST = 'media_playlist' ATTR_MEDIA_PLAYLIST = 'media_playlist'
ATTR_APP_ID = 'app_id' ATTR_APP_ID = 'app_id'
ATTR_APP_NAME = 'app_name' ATTR_APP_NAME = 'app_name'
ATTR_SUPPORTED_MEDIA_COMMANDS = 'supported_media_commands'
ATTR_INPUT_SOURCE = 'source' ATTR_INPUT_SOURCE = 'source'
ATTR_INPUT_SOURCE_LIST = 'source_list' ATTR_INPUT_SOURCE_LIST = 'source_list'
ATTR_MEDIA_ENQUEUE = 'enqueue' ATTR_MEDIA_ENQUEUE = 'enqueue'
@ -183,7 +182,6 @@ ATTR_TO_PROPERTY = [
ATTR_MEDIA_PLAYLIST, ATTR_MEDIA_PLAYLIST,
ATTR_APP_ID, ATTR_APP_ID,
ATTR_APP_NAME, ATTR_APP_NAME,
ATTR_SUPPORTED_MEDIA_COMMANDS,
ATTR_INPUT_SOURCE, ATTR_INPUT_SOURCE,
ATTR_INPUT_SOURCE_LIST, ATTR_INPUT_SOURCE_LIST,
] ]
@ -523,7 +521,39 @@ class MediaPlayerDevice(Entity):
@property @property
def supported_media_commands(self): def supported_media_commands(self):
"""Flag media commands that are supported.""" """Flag media commands that are supported.
DEPRECATED: Included for temporary custom platform compatibility.
"""
return None
@property
def supported_features(self):
"""Flag media player features that are supported."""
# Begin temporary transition logic
if self.supported_media_commands is not None:
# If this platform is still using supported_media_commands, issue
# a logger warning once with instructions on how to fix it.
if not getattr(self, '_supported_features_warned', False):
def show_warning():
"""Show a deprecation warning in the log for this class."""
import inspect
_LOGGER.warning(
"supported_media_commands is deprecated. Please "
"rename supported_media_commands to "
"supported_features in '%s' to ensure future support.",
inspect.getfile(self.__class__))
# This is a temporary attribute. We don't want to pollute
# __init__ so it can be easily removed.
# pylint: disable=attribute-defined-outside-init
self._supported_features_warned = True
self.hass.add_job(show_warning)
# Return the old property
return self.supported_media_commands
# End temporary transition logic
return 0 return 0
def turn_on(self): def turn_on(self):
@ -686,57 +716,57 @@ class MediaPlayerDevice(Entity):
@property @property
def support_play(self): def support_play(self):
"""Boolean if play is supported.""" """Boolean if play is supported."""
return bool(self.supported_media_commands & SUPPORT_PLAY) return bool(self.supported_features & SUPPORT_PLAY)
@property @property
def support_pause(self): def support_pause(self):
"""Boolean if pause is supported.""" """Boolean if pause is supported."""
return bool(self.supported_media_commands & SUPPORT_PAUSE) return bool(self.supported_features & SUPPORT_PAUSE)
@property @property
def support_stop(self): def support_stop(self):
"""Boolean if stop is supported.""" """Boolean if stop is supported."""
return bool(self.supported_media_commands & SUPPORT_STOP) return bool(self.supported_features & SUPPORT_STOP)
@property @property
def support_seek(self): def support_seek(self):
"""Boolean if seek is supported.""" """Boolean if seek is supported."""
return bool(self.supported_media_commands & SUPPORT_SEEK) return bool(self.supported_features & SUPPORT_SEEK)
@property @property
def support_volume_set(self): def support_volume_set(self):
"""Boolean if setting volume is supported.""" """Boolean if setting volume is supported."""
return bool(self.supported_media_commands & SUPPORT_VOLUME_SET) return bool(self.supported_features & SUPPORT_VOLUME_SET)
@property @property
def support_volume_mute(self): def support_volume_mute(self):
"""Boolean if muting volume is supported.""" """Boolean if muting volume is supported."""
return bool(self.supported_media_commands & SUPPORT_VOLUME_MUTE) return bool(self.supported_features & SUPPORT_VOLUME_MUTE)
@property @property
def support_previous_track(self): def support_previous_track(self):
"""Boolean if previous track command supported.""" """Boolean if previous track command supported."""
return bool(self.supported_media_commands & SUPPORT_PREVIOUS_TRACK) return bool(self.supported_features & SUPPORT_PREVIOUS_TRACK)
@property @property
def support_next_track(self): def support_next_track(self):
"""Boolean if next track command supported.""" """Boolean if next track command supported."""
return bool(self.supported_media_commands & SUPPORT_NEXT_TRACK) return bool(self.supported_features & SUPPORT_NEXT_TRACK)
@property @property
def support_play_media(self): def support_play_media(self):
"""Boolean if play media command supported.""" """Boolean if play media command supported."""
return bool(self.supported_media_commands & SUPPORT_PLAY_MEDIA) return bool(self.supported_features & SUPPORT_PLAY_MEDIA)
@property @property
def support_select_source(self): def support_select_source(self):
"""Boolean if select source command supported.""" """Boolean if select source command supported."""
return bool(self.supported_media_commands & SUPPORT_SELECT_SOURCE) return bool(self.supported_features & SUPPORT_SELECT_SOURCE)
@property @property
def support_clear_playlist(self): def support_clear_playlist(self):
"""Boolean if clear playlist command supported.""" """Boolean if clear playlist command supported."""
return bool(self.supported_media_commands & SUPPORT_CLEAR_PLAYLIST) return bool(self.supported_features & SUPPORT_CLEAR_PLAYLIST)
def toggle(self): def toggle(self):
"""Toggle the power on the media player.""" """Toggle the power on the media player."""
@ -821,14 +851,12 @@ class MediaPlayerDevice(Entity):
def state_attributes(self): def state_attributes(self):
"""Return the state attributes.""" """Return the state attributes."""
if self.state == STATE_OFF: if self.state == STATE_OFF:
state_attr = { return None
ATTR_SUPPORTED_MEDIA_COMMANDS: self.supported_media_commands,
} state_attr = {
else: attr: getattr(self, attr) for attr
state_attr = { in ATTR_TO_PROPERTY if getattr(self, attr) is not None
attr: getattr(self, attr) for attr }
in ATTR_TO_PROPERTY if getattr(self, attr) is not None
}
return state_attr return state_attr

View File

@ -79,8 +79,8 @@ class AnthemAVR(MediaPlayerDevice):
return getattr(self.avr.protocol, propname, dval) return getattr(self.avr.protocol, propname, dval)
@property @property
def supported_media_commands(self): def supported_features(self):
"""Return flag of media commands that are supported.""" """Flag media player features that are supported."""
return SUPPORT_ANTHEMAV return SUPPORT_ANTHEMAV
@property @property

View File

@ -184,8 +184,8 @@ class AppleTvDevice(MediaPlayerDevice):
return title if title else "No title" return title if title else "No title"
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
if self._playing is not None: if self._playing is not None:
if self.state != STATE_IDLE: if self.state != STATE_IDLE:
return SUPPORT_PAUSE | SUPPORT_PLAY | \ return SUPPORT_PAUSE | SUPPORT_PLAY | \

View File

@ -191,8 +191,8 @@ class SharpAquosTVDevice(MediaPlayerDevice):
return self._muted return self._muted
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return SUPPORT_SHARPTV return SUPPORT_SHARPTV
@_retry @_retry

View File

@ -316,8 +316,8 @@ class BraviaTVDevice(MediaPlayerDevice):
return self._muted return self._muted
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return SUPPORT_BRAVIA return SUPPORT_BRAVIA
@property @property

View File

@ -226,8 +226,8 @@ class CastDevice(MediaPlayerDevice):
return self.cast.app_display_name return self.cast.app_display_name
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return SUPPORT_CAST return SUPPORT_CAST
@property @property

View File

@ -151,8 +151,8 @@ class CmusDevice(MediaPlayerDevice):
return int(volume)/100 return int(volume)/100
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return SUPPORT_CMUS return SUPPORT_CMUS
def turn_off(self): def turn_off(self):

View File

@ -155,8 +155,8 @@ class DemoYoutubePlayer(AbstractDemoPlayer):
return "YouTube" return "YouTube"
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return YOUTUBE_PLAYER_SUPPORT return YOUTUBE_PLAYER_SUPPORT
@property @property
@ -269,8 +269,8 @@ class DemoMusicPlayer(AbstractDemoPlayer):
return self._cur_track + 1 return self._cur_track + 1
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
support = MUSIC_PLAYER_SUPPORT support = MUSIC_PLAYER_SUPPORT
if self._cur_track > 0: if self._cur_track > 0:
@ -364,8 +364,8 @@ class DemoTVShowPlayer(AbstractDemoPlayer):
return self._source return self._source
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
support = NETFLIX_PLAYER_SUPPORT support = NETFLIX_PLAYER_SUPPORT
if self._cur_episode > 1: if self._cur_episode > 1:

View File

@ -193,8 +193,8 @@ class DenonDevice(MediaPlayerDevice):
return self._mediainfo return self._mediainfo
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
if self._mediasource in MEDIA_MODES.values(): if self._mediasource in MEDIA_MODES.values():
return SUPPORT_DENON | SUPPORT_MEDIA_MODES return SUPPORT_DENON | SUPPORT_MEDIA_MODES
else: else:

View File

@ -167,8 +167,8 @@ class DenonDevice(MediaPlayerDevice):
return self._source_list return self._source_list
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
if self._current_source in self._receiver.netaudio_func_list: if self._current_source in self._receiver.netaudio_func_list:
return SUPPORT_DENON | SUPPORT_MEDIA_MODES return SUPPORT_DENON | SUPPORT_MEDIA_MODES
else: else:

View File

@ -133,8 +133,8 @@ class DirecTvDevice(MediaPlayerDevice):
return None return None
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return SUPPORT_DTV return SUPPORT_DTV
@property @property

View File

@ -97,8 +97,8 @@ class DuneHDPlayerEntity(MediaPlayerDevice):
return list(self._sources.keys()) return list(self._sources.keys())
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return DUNEHD_PLAYER_SUPPORT return DUNEHD_PLAYER_SUPPORT
def volume_up(self): def volume_up(self):

View File

@ -309,8 +309,8 @@ class EmbyClient(MediaPlayerDevice):
return self.now_playing_item['IndexNumber'] return self.now_playing_item['IndexNumber']
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
if self.supports_remote_control: if self.supports_remote_control:
return SUPPORT_EMBY return SUPPORT_EMBY
else: else:

View File

@ -122,8 +122,8 @@ class FireTVDevice(MediaPlayerDevice):
return True return True
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return SUPPORT_FIRETV return SUPPORT_FIRETV
@property @property

View File

@ -295,8 +295,8 @@ class GPMDP(MediaPlayerDevice):
return self._name return self._name
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return SUPPORT_GPMDP return SUPPORT_GPMDP
def media_next_track(self): def media_next_track(self):

View File

@ -158,8 +158,8 @@ class CecPlayerDevice(CecDevice, MediaPlayerDevice):
self.schedule_update_ha_state() self.schedule_update_ha_state()
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag media commands that are supported.""" """Flag media player features that are supported."""
from pycec.const import TYPE_RECORDER, TYPE_PLAYBACK, TYPE_TUNER, \ from pycec.const import TYPE_RECORDER, TYPE_PLAYBACK, TYPE_TUNER, \
TYPE_AUDIO TYPE_AUDIO
if self.type_id == TYPE_RECORDER or self.type == TYPE_PLAYBACK: if self.type_id == TYPE_RECORDER or self.type == TYPE_PLAYBACK:

View File

@ -306,8 +306,8 @@ class ItunesDevice(MediaPlayerDevice):
return self.current_playlist return self.current_playlist
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return SUPPORT_ITUNES return SUPPORT_ITUNES
def set_volume_level(self, volume): def set_volume_level(self, volume):
@ -425,8 +425,8 @@ class AirPlayDevice(MediaPlayerDevice):
return MEDIA_TYPE_MUSIC return MEDIA_TYPE_MUSIC
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return SUPPORT_AIRPLAY return SUPPORT_AIRPLAY
def set_volume_level(self, volume): def set_volume_level(self, volume):

View File

@ -228,14 +228,14 @@ class KodiDevice(MediaPlayerDevice):
self._item.get('label', self._item.get('file', 'unknown'))) self._item.get('label', self._item.get('file', 'unknown')))
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
supported_media_commands = SUPPORT_KODI supported_features = SUPPORT_KODI
if self._turn_off_action in TURN_OFF_ACTION: if self._turn_off_action in TURN_OFF_ACTION:
supported_media_commands |= SUPPORT_TURN_OFF supported_features |= SUPPORT_TURN_OFF
return supported_media_commands return supported_features
@asyncio.coroutine @asyncio.coroutine
def async_turn_off(self): def async_turn_off(self):

View File

@ -163,8 +163,8 @@ class LgTVDevice(MediaPlayerDevice):
return self._program_name return self._program_name
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return SUPPORT_LGTV return SUPPORT_LGTV
@property @property

View File

@ -133,8 +133,8 @@ class LiveboxPlayTvDevice(MediaPlayerDevice):
self._current_program) self._current_program)
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return SUPPORT_LIVEBOXPLAYTV return SUPPORT_LIVEBOXPLAYTV
def refresh_channel_list(self): def refresh_channel_list(self):

View File

@ -126,8 +126,8 @@ class MpcHcDevice(MediaPlayerDevice):
int(duration[2]) int(duration[2])
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return SUPPORT_MPCHC return SUPPORT_MPCHC
def volume_up(self): def volume_up(self):

View File

@ -184,8 +184,8 @@ class MpdDevice(MediaPlayerDevice):
return int(self.status['volume'])/100 return int(self.status['volume'])/100
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return SUPPORT_MPD return SUPPORT_MPD
@property @property

View File

@ -136,8 +136,8 @@ class NAD(MediaPlayerDevice):
return self._mute return self._mute
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return SUPPORT_NAD return SUPPORT_NAD
def turn_off(self): def turn_off(self):

View File

@ -150,8 +150,8 @@ class OnkyoDevice(MediaPlayerDevice):
return self._muted return self._muted
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return SUPPORT_ONKYO return SUPPORT_ONKYO
@property @property

View File

@ -123,8 +123,8 @@ class PanasonicVieraTVDevice(MediaPlayerDevice):
return self._muted return self._muted
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
if self._mac: if self._mac:
return SUPPORT_VIERATV | SUPPORT_TURN_ON return SUPPORT_VIERATV | SUPPORT_TURN_ON
return SUPPORT_VIERATV return SUPPORT_VIERATV

View File

@ -158,8 +158,8 @@ class PandoraMediaPlayer(MediaPlayerDevice):
self.schedule_update_ha_state() self.schedule_update_ha_state()
@property @property
def supported_media_commands(self): def supported_features(self):
"""Show what this supports.""" """Flag media player features that are supported."""
return PANDORA_SUPPORT return PANDORA_SUPPORT
@property @property

View File

@ -86,8 +86,8 @@ class PhilipsTV(MediaPlayerDevice):
return True return True
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
if self._watching_tv: if self._watching_tv:
return SUPPORT_PHILIPS_JS_TV return SUPPORT_PHILIPS_JS_TV
else: else:

View File

@ -176,8 +176,8 @@ class PioneerDevice(MediaPlayerDevice):
return self._muted return self._muted
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return SUPPORT_PIONEER return SUPPORT_PIONEER
@property @property

View File

@ -334,8 +334,8 @@ class PlexClient(MediaPlayerDevice):
return self._convert_na_to_none(self.session.index) return self._convert_na_to_none(self.session.index)
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return SUPPORT_PLEX return SUPPORT_PLEX
def set_volume_level(self, volume): def set_volume_level(self, volume):

View File

@ -142,8 +142,8 @@ class RokuDevice(MediaPlayerDevice):
return STATE_UNKNOWN return STATE_UNKNOWN
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return SUPPORT_ROKU return SUPPORT_ROKU
@property @property

View File

@ -95,8 +95,8 @@ class RussoundRNETDevice(MediaPlayerDevice):
return self._state return self._state
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return SUPPORT_RUSSOUND return SUPPORT_RUSSOUND
@property @property

View File

@ -156,8 +156,8 @@ class SamsungTVDevice(MediaPlayerDevice):
return self._muted return self._muted
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return SUPPORT_SAMSUNGTV return SUPPORT_SAMSUNGTV
def turn_off(self): def turn_off(self):

View File

@ -72,8 +72,8 @@ class SnapcastDevice(MediaPlayerDevice):
return self._client.muted return self._client.muted
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return SUPPORT_SNAPCAST return SUPPORT_SNAPCAST
@property @property

View File

@ -761,10 +761,10 @@ class SonosDevice(MediaPlayerDevice):
return self._media_title return self._media_title
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
if self._coordinator: if self._coordinator:
return self._coordinator.supported_media_commands return self._coordinator.supported_features
supported = SUPPORT_SONOS supported = SUPPORT_SONOS

View File

@ -287,8 +287,8 @@ class SoundTouchDevice(MediaPlayerDevice):
return self._volume.muted return self._volume.muted
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return SUPPORT_SOUNDTOUCH return SUPPORT_SOUNDTOUCH
def turn_off(self): def turn_off(self):

View File

@ -302,8 +302,8 @@ class SqueezeBoxDevice(MediaPlayerDevice):
return self._status['album'] return self._status['album']
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return SUPPORT_SQUEEZEBOX return SUPPORT_SQUEEZEBOX
def async_turn_off(self): def async_turn_off(self):

View File

@ -17,7 +17,7 @@ from homeassistant.components.media_player import (
ATTR_MEDIA_PLAYLIST, ATTR_MEDIA_SEASON, ATTR_MEDIA_SEEK_POSITION, ATTR_MEDIA_PLAYLIST, ATTR_MEDIA_SEASON, ATTR_MEDIA_SEEK_POSITION,
ATTR_MEDIA_SERIES_TITLE, ATTR_MEDIA_TITLE, ATTR_MEDIA_TRACK, ATTR_MEDIA_SERIES_TITLE, ATTR_MEDIA_TITLE, ATTR_MEDIA_TRACK,
ATTR_MEDIA_VOLUME_LEVEL, ATTR_MEDIA_VOLUME_MUTED, ATTR_INPUT_SOURCE_LIST, ATTR_MEDIA_VOLUME_LEVEL, ATTR_MEDIA_VOLUME_MUTED, ATTR_INPUT_SOURCE_LIST,
ATTR_SUPPORTED_MEDIA_COMMANDS, ATTR_MEDIA_POSITION, ATTR_MEDIA_POSITION,
ATTR_MEDIA_POSITION_UPDATED_AT, DOMAIN, SERVICE_PLAY_MEDIA, ATTR_MEDIA_POSITION_UPDATED_AT, DOMAIN, SERVICE_PLAY_MEDIA,
SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET,
SUPPORT_VOLUME_STEP, SUPPORT_SELECT_SOURCE, SUPPORT_CLEAR_PLAYLIST, SUPPORT_VOLUME_STEP, SUPPORT_SELECT_SOURCE, SUPPORT_CLEAR_PLAYLIST,
@ -29,7 +29,7 @@ from homeassistant.const import (
SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_MEDIA_SEEK, SERVICE_TURN_OFF, SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_MEDIA_SEEK, SERVICE_TURN_OFF,
SERVICE_TURN_ON, SERVICE_VOLUME_DOWN, SERVICE_VOLUME_MUTE, SERVICE_TURN_ON, SERVICE_VOLUME_DOWN, SERVICE_VOLUME_MUTE,
SERVICE_VOLUME_SET, SERVICE_VOLUME_UP, STATE_IDLE, STATE_OFF, STATE_ON, SERVICE_VOLUME_SET, SERVICE_VOLUME_UP, STATE_IDLE, STATE_OFF, STATE_ON,
SERVICE_MEDIA_STOP) SERVICE_MEDIA_STOP, ATTR_SUPPORTED_FEATURES)
from homeassistant.helpers.event import async_track_state_change from homeassistant.helpers.event import async_track_state_change
from homeassistant.helpers.service import async_call_from_config from homeassistant.helpers.service import async_call_from_config
@ -357,9 +357,9 @@ class UniversalMediaPlayer(MediaPlayerDevice):
return self._override_or_child_attr(ATTR_INPUT_SOURCE_LIST) return self._override_or_child_attr(ATTR_INPUT_SOURCE_LIST)
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag media commands that are supported.""" """Flag media player features that are supported."""
flags = self._child_attr(ATTR_SUPPORTED_MEDIA_COMMANDS) or 0 flags = self._child_attr(ATTR_SUPPORTED_FEATURES) or 0
if SERVICE_TURN_ON in self._cmds: if SERVICE_TURN_ON in self._cmds:
flags |= SUPPORT_TURN_ON flags |= SUPPORT_TURN_ON

View File

@ -97,8 +97,8 @@ class VlcDevice(MediaPlayerDevice):
return self._muted return self._muted
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
return SUPPORT_VLC return SUPPORT_VLC
@property @property

View File

@ -244,8 +244,8 @@ class LgWebOSDevice(MediaPlayerDevice):
return None return None
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
if self._mac: if self._mac:
return SUPPORT_WEBOSTV | SUPPORT_TURN_ON return SUPPORT_WEBOSTV | SUPPORT_TURN_ON
return SUPPORT_WEBOSTV return SUPPORT_WEBOSTV

View File

@ -181,9 +181,9 @@ class YamahaDevice(MediaPlayerDevice):
return self._source_list return self._source_list
@property @property
def supported_media_commands(self): def supported_features(self):
"""Flag of media commands that are supported.""" """Flag media player features that are supported."""
supported_commands = SUPPORT_YAMAHA supported_features = SUPPORT_YAMAHA
supports = self._playback_support supports = self._playback_support
mapping = {'play': (SUPPORT_PLAY | SUPPORT_PLAY_MEDIA), mapping = {'play': (SUPPORT_PLAY | SUPPORT_PLAY_MEDIA),
@ -193,8 +193,8 @@ class YamahaDevice(MediaPlayerDevice):
'skip_r': SUPPORT_PREVIOUS_TRACK} 'skip_r': SUPPORT_PREVIOUS_TRACK}
for attr, feature in mapping.items(): for attr, feature in mapping.items():
if getattr(supports, attr, False): if getattr(supports, attr, False):
supported_commands |= feature supported_features |= feature
return supported_commands return supported_features
def turn_off(self): def turn_off(self):
"""Turn off media player.""" """Turn off media player."""

View File

@ -286,6 +286,9 @@ ATTR_STATE = 'state'
ATTR_OPTION = 'option' ATTR_OPTION = 'option'
# Bitfield of supported component features for the entity
ATTR_SUPPORTED_FEATURES = 'supported_features'
# #### SERVICES #### # #### SERVICES ####
SERVICE_HOMEASSISTANT_STOP = 'stop' SERVICE_HOMEASSISTANT_STOP = 'stop'
SERVICE_HOMEASSISTANT_RESTART = 'restart' SERVICE_HOMEASSISTANT_RESTART = 'restart'

View File

@ -10,7 +10,7 @@ from homeassistant.const import (
ATTR_ASSUMED_STATE, ATTR_FRIENDLY_NAME, ATTR_HIDDEN, ATTR_ICON, ATTR_ASSUMED_STATE, ATTR_FRIENDLY_NAME, ATTR_HIDDEN, ATTR_ICON,
ATTR_UNIT_OF_MEASUREMENT, DEVICE_DEFAULT_NAME, STATE_OFF, STATE_ON, ATTR_UNIT_OF_MEASUREMENT, DEVICE_DEFAULT_NAME, STATE_OFF, STATE_ON,
STATE_UNAVAILABLE, STATE_UNKNOWN, TEMP_CELSIUS, TEMP_FAHRENHEIT, STATE_UNAVAILABLE, STATE_UNKNOWN, TEMP_CELSIUS, TEMP_FAHRENHEIT,
ATTR_ENTITY_PICTURE) ATTR_ENTITY_PICTURE, ATTR_SUPPORTED_FEATURES)
from homeassistant.core import HomeAssistant, DOMAIN as CORE_DOMAIN from homeassistant.core import HomeAssistant, DOMAIN as CORE_DOMAIN
from homeassistant.exceptions import NoEntitySpecifiedError from homeassistant.exceptions import NoEntitySpecifiedError
from homeassistant.util import ensure_unique_string, slugify from homeassistant.util import ensure_unique_string, slugify
@ -148,6 +148,11 @@ class Entity(object):
""" """
return False return False
@property
def supported_features(self) -> int:
"""Flag supported features."""
return None
def update(self): def update(self):
"""Retrieve latest state. """Retrieve latest state.
@ -231,6 +236,8 @@ class Entity(object):
self._attr_setter('entity_picture', str, ATTR_ENTITY_PICTURE, attr) self._attr_setter('entity_picture', str, ATTR_ENTITY_PICTURE, attr)
self._attr_setter('hidden', bool, ATTR_HIDDEN, attr) self._attr_setter('hidden', bool, ATTR_HIDDEN, attr)
self._attr_setter('assumed_state', bool, ATTR_ASSUMED_STATE, attr) self._attr_setter('assumed_state', bool, ATTR_ASSUMED_STATE, attr)
self._attr_setter('supported_features', int, ATTR_SUPPORTED_FEATURES,
attr)
end = timer() end = timer()

View File

@ -301,7 +301,7 @@ def test_put_light_state_fan(hass_hue, hue_client):
blocking=True) blocking=True)
# Emulated hue converts 0-100% to 0-255. # Emulated hue converts 0-100% to 0-255.
level = 23 level = 43
brightness = round(level * 255 / 100) brightness = round(level * 255 / 100)
fan_result = yield from perform_put_light_state( fan_result = yield from perform_put_light_state(

View File

@ -155,28 +155,28 @@ class TestDemoMediaPlayer(unittest.TestCase):
state = self.hass.states.get(entity_id) state = self.hass.states.get(entity_id)
assert 1 == state.attributes.get('media_track') assert 1 == state.attributes.get('media_track')
assert 0 == (mp.SUPPORT_PREVIOUS_TRACK & assert 0 == (mp.SUPPORT_PREVIOUS_TRACK &
state.attributes.get('supported_media_commands')) state.attributes.get('supported_features'))
mp.media_next_track(self.hass, entity_id) mp.media_next_track(self.hass, entity_id)
self.hass.block_till_done() self.hass.block_till_done()
state = self.hass.states.get(entity_id) state = self.hass.states.get(entity_id)
assert 2 == state.attributes.get('media_track') assert 2 == state.attributes.get('media_track')
assert 0 < (mp.SUPPORT_PREVIOUS_TRACK & assert 0 < (mp.SUPPORT_PREVIOUS_TRACK &
state.attributes.get('supported_media_commands')) state.attributes.get('supported_features'))
mp.media_next_track(self.hass, entity_id) mp.media_next_track(self.hass, entity_id)
self.hass.block_till_done() self.hass.block_till_done()
state = self.hass.states.get(entity_id) state = self.hass.states.get(entity_id)
assert 3 == state.attributes.get('media_track') assert 3 == state.attributes.get('media_track')
assert 0 < (mp.SUPPORT_PREVIOUS_TRACK & assert 0 < (mp.SUPPORT_PREVIOUS_TRACK &
state.attributes.get('supported_media_commands')) state.attributes.get('supported_features'))
mp.media_previous_track(self.hass, entity_id) mp.media_previous_track(self.hass, entity_id)
self.hass.block_till_done() self.hass.block_till_done()
state = self.hass.states.get(entity_id) state = self.hass.states.get(entity_id)
assert 2 == state.attributes.get('media_track') assert 2 == state.attributes.get('media_track')
assert 0 < (mp.SUPPORT_PREVIOUS_TRACK & assert 0 < (mp.SUPPORT_PREVIOUS_TRACK &
state.attributes.get('supported_media_commands')) state.attributes.get('supported_features'))
assert setup_component( assert setup_component(
self.hass, mp.DOMAIN, self.hass, mp.DOMAIN,
@ -185,21 +185,21 @@ class TestDemoMediaPlayer(unittest.TestCase):
state = self.hass.states.get(ent_id) state = self.hass.states.get(ent_id)
assert 1 == state.attributes.get('media_episode') assert 1 == state.attributes.get('media_episode')
assert 0 == (mp.SUPPORT_PREVIOUS_TRACK & assert 0 == (mp.SUPPORT_PREVIOUS_TRACK &
state.attributes.get('supported_media_commands')) state.attributes.get('supported_features'))
mp.media_next_track(self.hass, ent_id) mp.media_next_track(self.hass, ent_id)
self.hass.block_till_done() self.hass.block_till_done()
state = self.hass.states.get(ent_id) state = self.hass.states.get(ent_id)
assert 2 == state.attributes.get('media_episode') assert 2 == state.attributes.get('media_episode')
assert 0 < (mp.SUPPORT_PREVIOUS_TRACK & assert 0 < (mp.SUPPORT_PREVIOUS_TRACK &
state.attributes.get('supported_media_commands')) state.attributes.get('supported_features'))
mp.media_previous_track(self.hass, ent_id) mp.media_previous_track(self.hass, ent_id)
self.hass.block_till_done() self.hass.block_till_done()
state = self.hass.states.get(ent_id) state = self.hass.states.get(ent_id)
assert 1 == state.attributes.get('media_episode') assert 1 == state.attributes.get('media_episode')
assert 0 == (mp.SUPPORT_PREVIOUS_TRACK & assert 0 == (mp.SUPPORT_PREVIOUS_TRACK &
state.attributes.get('supported_media_commands')) state.attributes.get('supported_features'))
@patch('homeassistant.components.media_player.demo.DemoYoutubePlayer.' @patch('homeassistant.components.media_player.demo.DemoYoutubePlayer.'
'media_seek', autospec=True) 'media_seek', autospec=True)
@ -211,21 +211,21 @@ class TestDemoMediaPlayer(unittest.TestCase):
ent_id = 'media_player.living_room' ent_id = 'media_player.living_room'
state = self.hass.states.get(ent_id) state = self.hass.states.get(ent_id)
assert 0 < (mp.SUPPORT_PLAY_MEDIA & assert 0 < (mp.SUPPORT_PLAY_MEDIA &
state.attributes.get('supported_media_commands')) state.attributes.get('supported_features'))
assert state.attributes.get('media_content_id') is not None assert state.attributes.get('media_content_id') is not None
mp.play_media(self.hass, None, 'some_id', ent_id) mp.play_media(self.hass, None, 'some_id', ent_id)
self.hass.block_till_done() self.hass.block_till_done()
state = self.hass.states.get(ent_id) state = self.hass.states.get(ent_id)
assert 0 < (mp.SUPPORT_PLAY_MEDIA & assert 0 < (mp.SUPPORT_PLAY_MEDIA &
state.attributes.get('supported_media_commands')) state.attributes.get('supported_features'))
assert not 'some_id' == state.attributes.get('media_content_id') assert not 'some_id' == state.attributes.get('media_content_id')
mp.play_media(self.hass, 'youtube', 'some_id', ent_id) mp.play_media(self.hass, 'youtube', 'some_id', ent_id)
self.hass.block_till_done() self.hass.block_till_done()
state = self.hass.states.get(ent_id) state = self.hass.states.get(ent_id)
assert 0 < (mp.SUPPORT_PLAY_MEDIA & assert 0 < (mp.SUPPORT_PLAY_MEDIA &
state.attributes.get('supported_media_commands')) state.attributes.get('supported_features'))
assert 'some_id' == state.attributes.get('media_content_id') assert 'some_id' == state.attributes.get('media_content_id')
assert not mock_seek.called assert not mock_seek.called

View File

@ -318,7 +318,7 @@ class TestSoundtouchMediaPlayer(unittest.TestCase):
default_component(), default_component(),
mock.MagicMock()) mock.MagicMock())
self.assertEqual(mocked_sountouch_device.call_count, 1) self.assertEqual(mocked_sountouch_device.call_count, 1)
self.assertEqual(soundtouch.DEVICES[0].supported_media_commands, 17853) self.assertEqual(soundtouch.DEVICES[0].supported_features, 17853)
@mock.patch('libsoundtouch.device.SoundTouchDevice.power_off') @mock.patch('libsoundtouch.device.SoundTouchDevice.power_off')
@mock.patch('libsoundtouch.device.SoundTouchDevice.volume') @mock.patch('libsoundtouch.device.SoundTouchDevice.volume')

View File

@ -27,7 +27,7 @@ class MockMediaPlayer(media_player.MediaPlayerDevice):
self._volume_level = 0 self._volume_level = 0
self._is_volume_muted = False self._is_volume_muted = False
self._media_title = None self._media_title = None
self._supported_media_commands = 0 self._supported_features = 0
self._source = None self._source = None
self._tracks = 12 self._tracks = 12
self._media_image_url = None self._media_image_url = None
@ -91,9 +91,9 @@ class MockMediaPlayer(media_player.MediaPlayerDevice):
return self._is_volume_muted return self._is_volume_muted
@property @property
def supported_media_commands(self): def supported_features(self):
"""Supported media commands flag.""" """Flag media player features that are supported."""
return self._supported_media_commands return self._supported_features
@property @property
def media_image_url(self): def media_image_url(self):
@ -502,7 +502,7 @@ class TestMediaPlayer(unittest.TestCase):
self.hass.states.set(self.mock_mute_switch_id, STATE_ON) self.hass.states.set(self.mock_mute_switch_id, STATE_ON)
self.assertTrue(ump.is_volume_muted) self.assertTrue(ump.is_volume_muted)
def test_supported_media_commands_children_only(self): def test_supported_features_children_only(self):
"""Test supported media commands with only children.""" """Test supported media commands with only children."""
config = self.config_children_only config = self.config_children_only
universal.validate_config(config) universal.validate_config(config)
@ -511,15 +511,15 @@ class TestMediaPlayer(unittest.TestCase):
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config['name']) ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config['name'])
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.assertEqual(0, ump.supported_media_commands) self.assertEqual(0, ump.supported_features)
self.mock_mp_1._supported_media_commands = 512 self.mock_mp_1._supported_features = 512
self.mock_mp_1._state = STATE_PLAYING self.mock_mp_1._state = STATE_PLAYING
self.mock_mp_1.update_ha_state() self.mock_mp_1.update_ha_state()
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.assertEqual(512, ump.supported_media_commands) self.assertEqual(512, ump.supported_features)
def test_supported_media_commands_children_and_cmds(self): def test_supported_features_children_and_cmds(self):
"""Test supported media commands with children and attrs.""" """Test supported media commands with children and attrs."""
config = self.config_children_and_attr config = self.config_children_and_attr
universal.validate_config(config) universal.validate_config(config)
@ -543,7 +543,7 @@ class TestMediaPlayer(unittest.TestCase):
| universal.SUPPORT_VOLUME_STEP | universal.SUPPORT_VOLUME_MUTE \ | universal.SUPPORT_VOLUME_STEP | universal.SUPPORT_VOLUME_MUTE \
| universal.SUPPORT_SELECT_SOURCE | universal.SUPPORT_SELECT_SOURCE
self.assertEqual(check_flags, ump.supported_media_commands) self.assertEqual(check_flags, ump.supported_features)
def test_service_call_no_active_child(self): def test_service_call_no_active_child(self):
"""Test a service call to children with no active child.""" """Test a service call to children with no active child."""