Add light.group platform (#12229)

* Add grouped_light platform

* 📝 Fix Lint issues

* 🎨 Reformat code with yapf

* A Few changes

*  Python 3.5 magic

* Improvements

Included the comments from #11323

* Fixes

* Updates

* Fixes & Tests

* Fix bad-whitespace

* Domain Config Validation

... by rebasing onto #12592

* Style changes & Improvements

* Lint

* Changes according to Review Comments

* Use blocking light.async_turn_*

* Revert "Use blocking light.async_turn_*"

This reverts commit 9e83198552af9347aede9efb547f91793275cc5f.

* Update service calls and state reporting

* Add group service call tests

* Remove unused constant.
This commit is contained in:
Otto Winter 2018-03-02 02:14:26 +01:00 committed by Paulus Schoutsen
parent 168e1f0e2d
commit 03970764d8
4 changed files with 720 additions and 4 deletions

View File

@ -409,6 +409,7 @@ omit =
homeassistant/components/light/decora_wifi.py
homeassistant/components/light/flux_led.py
homeassistant/components/light/greenwave.py
homeassistant/components/light/group.py
homeassistant/components/light/hue.py
homeassistant/components/light/hyperion.py
homeassistant/components/light/iglo.py

View File

@ -12,7 +12,8 @@ import os
import voluptuous as vol
from homeassistant.components import group
from homeassistant.components.group import \
ENTITY_ID_FORMAT as GROUP_ENTITY_ID_FORMAT
from homeassistant.const import (
ATTR_ENTITY_ID, SERVICE_TOGGLE, SERVICE_TURN_OFF, SERVICE_TURN_ON,
STATE_ON)
@ -30,7 +31,7 @@ DEPENDENCIES = ['group']
SCAN_INTERVAL = timedelta(seconds=30)
GROUP_NAME_ALL_LIGHTS = 'all lights'
ENTITY_ID_ALL_LIGHTS = group.ENTITY_ID_FORMAT.format('all_lights')
ENTITY_ID_ALL_LIGHTS = GROUP_ENTITY_ID_FORMAT.format('all_lights')
ENTITY_ID_FORMAT = DOMAIN + '.{}'
@ -209,8 +210,9 @@ def async_turn_off(hass, entity_id=None, transition=None):
DOMAIN, SERVICE_TURN_OFF, data))
@callback
@bind_hass
def toggle(hass, entity_id=None, transition=None):
def async_toggle(hass, entity_id=None, transition=None):
"""Toggle all or specified light."""
data = {
key: value for key, value in [
@ -219,7 +221,14 @@ def toggle(hass, entity_id=None, transition=None):
] if value is not None
}
hass.services.call(DOMAIN, SERVICE_TOGGLE, data)
hass.async_add_job(hass.services.async_call(
DOMAIN, SERVICE_TOGGLE, data))
@bind_hass
def toggle(hass, entity_id=None, transition=None):
"""Toggle all or specified light."""
hass.add_job(async_toggle, hass, entity_id, transition)
def preprocess_turn_on_alternatives(params):

View File

@ -0,0 +1,289 @@
"""
This component allows several lights to be grouped into one light.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/light.group/
"""
import logging
import itertools
from typing import List, Tuple, Optional, Iterator, Any, Callable
from collections import Counter
import voluptuous as vol
from homeassistant.core import State, callback
from homeassistant.components import light
from homeassistant.const import (STATE_ON, ATTR_ENTITY_ID, CONF_NAME,
CONF_ENTITIES, STATE_UNAVAILABLE,
ATTR_SUPPORTED_FEATURES)
from homeassistant.helpers.event import async_track_state_change
from homeassistant.helpers.typing import HomeAssistantType, ConfigType
from homeassistant.components.light import (
SUPPORT_BRIGHTNESS, SUPPORT_RGB_COLOR, SUPPORT_COLOR_TEMP,
SUPPORT_TRANSITION, SUPPORT_EFFECT, SUPPORT_FLASH, SUPPORT_XY_COLOR,
SUPPORT_WHITE_VALUE, PLATFORM_SCHEMA, ATTR_BRIGHTNESS, ATTR_XY_COLOR,
ATTR_RGB_COLOR, ATTR_WHITE_VALUE, ATTR_COLOR_TEMP, ATTR_MIN_MIREDS,
ATTR_MAX_MIREDS, ATTR_EFFECT_LIST, ATTR_EFFECT, ATTR_FLASH,
ATTR_TRANSITION)
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
DEFAULT_NAME = 'Group Light'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Required(CONF_ENTITIES): cv.entities_domain('light')
})
SUPPORT_GROUP_LIGHT = (SUPPORT_BRIGHTNESS | SUPPORT_COLOR_TEMP | SUPPORT_EFFECT
| SUPPORT_FLASH | SUPPORT_RGB_COLOR | SUPPORT_TRANSITION
| SUPPORT_XY_COLOR | SUPPORT_WHITE_VALUE)
async def async_setup_platform(hass: HomeAssistantType, config: ConfigType,
async_add_devices, discovery_info=None) -> None:
"""Initialize light.group platform."""
async_add_devices([GroupLight(config.get(CONF_NAME),
config[CONF_ENTITIES])])
class GroupLight(light.Light):
"""Representation of a group light."""
def __init__(self, name: str, entity_ids: List[str]) -> None:
"""Initialize a group light."""
self._name = name # type: str
self._entity_ids = entity_ids # type: List[str]
self._is_on = False # type: bool
self._available = False # type: bool
self._brightness = None # type: Optional[int]
self._xy_color = None # type: Optional[Tuple[float, float]]
self._rgb_color = None # type: Optional[Tuple[int, int, int]]
self._color_temp = None # type: Optional[int]
self._min_mireds = 154 # type: Optional[int]
self._max_mireds = 500 # type: Optional[int]
self._white_value = None # type: Optional[int]
self._effect_list = None # type: Optional[List[str]]
self._effect = None # type: Optional[str]
self._supported_features = 0 # type: int
self._async_unsub_state_changed = None
async def async_added_to_hass(self) -> None:
"""Register callbacks."""
@callback
def async_state_changed_listener(entity_id: str, old_state: State,
new_state: State):
"""Handle child updates."""
self.async_schedule_update_ha_state(True)
self._async_unsub_state_changed = async_track_state_change(
self.hass, self._entity_ids, async_state_changed_listener)
async def async_will_remove_from_hass(self):
"""Callback when removed from HASS."""
if self._async_unsub_state_changed:
self._async_unsub_state_changed()
self._async_unsub_state_changed = None
@property
def name(self) -> str:
"""Return the name of the entity."""
return self._name
@property
def is_on(self) -> bool:
"""Return the on/off state of the light."""
return self._is_on
@property
def available(self) -> bool:
"""Return whether the light is available."""
return self._available
@property
def brightness(self) -> Optional[int]:
"""Return the brightness of this light between 0..255."""
return self._brightness
@property
def xy_color(self) -> Optional[Tuple[float, float]]:
"""Return the XY color value [float, float]."""
return self._xy_color
@property
def rgb_color(self) -> Optional[Tuple[int, int, int]]:
"""Return the RGB color value [int, int, int]."""
return self._rgb_color
@property
def color_temp(self) -> Optional[int]:
"""Return the CT color value in mireds."""
return self._color_temp
@property
def min_mireds(self) -> Optional[int]:
"""Return the coldest color_temp that this light supports."""
return self._min_mireds
@property
def max_mireds(self) -> Optional[int]:
"""Return the warmest color_temp that this light supports."""
return self._max_mireds
@property
def white_value(self) -> Optional[int]:
"""Return the white value of this light between 0..255."""
return self._white_value
@property
def effect_list(self) -> Optional[List[str]]:
"""Return the list of supported effects."""
return self._effect_list
@property
def effect(self) -> Optional[str]:
"""Return the current effect."""
return self._effect
@property
def supported_features(self) -> int:
"""Flag supported features."""
return self._supported_features
@property
def should_poll(self) -> bool:
"""No polling needed for a group light."""
return False
async def async_turn_on(self, **kwargs):
"""Forward the turn_on command to all lights in the group."""
data = {ATTR_ENTITY_ID: self._entity_ids}
if ATTR_BRIGHTNESS in kwargs:
data[ATTR_BRIGHTNESS] = kwargs[ATTR_BRIGHTNESS]
if ATTR_XY_COLOR in kwargs:
data[ATTR_XY_COLOR] = kwargs[ATTR_XY_COLOR]
if ATTR_RGB_COLOR in kwargs:
data[ATTR_RGB_COLOR] = kwargs[ATTR_RGB_COLOR]
if ATTR_COLOR_TEMP in kwargs:
data[ATTR_COLOR_TEMP] = kwargs[ATTR_COLOR_TEMP]
if ATTR_WHITE_VALUE in kwargs:
data[ATTR_WHITE_VALUE] = kwargs[ATTR_WHITE_VALUE]
if ATTR_EFFECT in kwargs:
data[ATTR_EFFECT] = kwargs[ATTR_EFFECT]
if ATTR_TRANSITION in kwargs:
data[ATTR_TRANSITION] = kwargs[ATTR_TRANSITION]
if ATTR_FLASH in kwargs:
data[ATTR_FLASH] = kwargs[ATTR_FLASH]
await self.hass.services.async_call(
light.DOMAIN, light.SERVICE_TURN_ON, data, blocking=True)
async def async_turn_off(self, **kwargs):
"""Forward the turn_off command to all lights in the group."""
data = {ATTR_ENTITY_ID: self._entity_ids}
if ATTR_TRANSITION in kwargs:
data[ATTR_TRANSITION] = kwargs[ATTR_TRANSITION]
await self.hass.services.async_call(
light.DOMAIN, light.SERVICE_TURN_OFF, data, blocking=True)
async def async_update(self):
"""Query all members and determine the group state."""
all_states = [self.hass.states.get(x) for x in self._entity_ids]
states = list(filter(None, all_states))
on_states = [state for state in states if state.state == STATE_ON]
self._is_on = len(on_states) > 0
self._available = any(state.state != STATE_UNAVAILABLE
for state in states)
self._brightness = _reduce_attribute(on_states, ATTR_BRIGHTNESS)
self._xy_color = _reduce_attribute(
on_states, ATTR_XY_COLOR, reduce=_mean_tuple)
self._rgb_color = _reduce_attribute(
on_states, ATTR_RGB_COLOR, reduce=_mean_tuple)
if self._rgb_color is not None:
self._rgb_color = tuple(map(int, self._rgb_color))
self._white_value = _reduce_attribute(on_states, ATTR_WHITE_VALUE)
self._color_temp = _reduce_attribute(on_states, ATTR_COLOR_TEMP)
self._min_mireds = _reduce_attribute(
states, ATTR_MIN_MIREDS, default=154, reduce=min)
self._max_mireds = _reduce_attribute(
states, ATTR_MAX_MIREDS, default=500, reduce=max)
self._effect_list = None
all_effect_lists = list(
_find_state_attributes(states, ATTR_EFFECT_LIST))
if all_effect_lists:
# Merge all effects from all effect_lists with a union merge.
self._effect_list = list(set().union(*all_effect_lists))
self._effect = None
all_effects = list(_find_state_attributes(on_states, ATTR_EFFECT))
if all_effects:
# Report the most common effect.
effects_count = Counter(itertools.chain(all_effects))
self._effect = effects_count.most_common(1)[0][0]
self._supported_features = 0
for support in _find_state_attributes(states, ATTR_SUPPORTED_FEATURES):
# Merge supported features by emulating support for every feature
# we find.
self._supported_features |= support
# Bitwise-and the supported features with the GroupedLight's features
# so that we don't break in the future when a new feature is added.
self._supported_features &= SUPPORT_GROUP_LIGHT
def _find_state_attributes(states: List[State],
key: str) -> Iterator[Any]:
"""Find attributes with matching key from states."""
for state in states:
value = state.attributes.get(key)
if value is not None:
yield value
def _mean_int(*args):
"""Return the mean of the supplied values."""
return int(sum(args) / len(args))
def _mean_tuple(*args):
"""Return the mean values along the columns of the supplied values."""
return tuple(sum(l) / len(l) for l in zip(*args))
# https://github.com/PyCQA/pylint/issues/1831
# pylint: disable=bad-whitespace
def _reduce_attribute(states: List[State],
key: str,
default: Optional[Any] = None,
reduce: Callable[..., Any] = _mean_int) -> Any:
"""Find the first attribute matching key from states.
If none are found, return default.
"""
attrs = list(_find_state_attributes(states, key))
if not attrs:
return default
if len(attrs) == 1:
return attrs[0]
return reduce(*attrs)

View File

@ -0,0 +1,417 @@
"""The tests for the Group Light platform."""
from unittest.mock import MagicMock
import asynctest
from homeassistant.components import light
from homeassistant.components.light import group
from homeassistant.setup import async_setup_component
async def test_default_state(hass):
"""Test light group default state."""
await async_setup_component(hass, 'light', {'light': {
'platform': 'group', 'entities': [], 'name': 'Bedroom Group'
}})
await hass.async_block_till_done()
state = hass.states.get('light.bedroom_group')
assert state is not None
assert state.state == 'unavailable'
assert state.attributes['supported_features'] == 0
assert state.attributes.get('brightness') is None
assert state.attributes.get('rgb_color') is None
assert state.attributes.get('xy_color') is None
assert state.attributes.get('color_temp') is None
assert state.attributes.get('white_value') is None
assert state.attributes.get('effect_list') is None
assert state.attributes.get('effect') is None
async def test_state_reporting(hass):
"""Test the state reporting."""
await async_setup_component(hass, 'light', {'light': {
'platform': 'group', 'entities': ['light.test1', 'light.test2']
}})
hass.states.async_set('light.test1', 'on')
hass.states.async_set('light.test2', 'unavailable')
await hass.async_block_till_done()
assert hass.states.get('light.group_light').state == 'on'
hass.states.async_set('light.test1', 'on')
hass.states.async_set('light.test2', 'off')
await hass.async_block_till_done()
assert hass.states.get('light.group_light').state == 'on'
hass.states.async_set('light.test1', 'off')
hass.states.async_set('light.test2', 'off')
await hass.async_block_till_done()
assert hass.states.get('light.group_light').state == 'off'
hass.states.async_set('light.test1', 'unavailable')
hass.states.async_set('light.test2', 'unavailable')
await hass.async_block_till_done()
assert hass.states.get('light.group_light').state == 'unavailable'
async def test_brightness(hass):
"""Test brightness reporting."""
await async_setup_component(hass, 'light', {'light': {
'platform': 'group', 'entities': ['light.test1', 'light.test2']
}})
hass.states.async_set('light.test1', 'on',
{'brightness': 255, 'supported_features': 1})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.state == 'on'
assert state.attributes['supported_features'] == 1
assert state.attributes['brightness'] == 255
hass.states.async_set('light.test2', 'on',
{'brightness': 100, 'supported_features': 1})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.state == 'on'
assert state.attributes['brightness'] == 177
hass.states.async_set('light.test1', 'off',
{'brightness': 255, 'supported_features': 1})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.state == 'on'
assert state.attributes['supported_features'] == 1
assert state.attributes['brightness'] == 100
async def test_xy_color(hass):
"""Test XY reporting."""
await async_setup_component(hass, 'light', {'light': {
'platform': 'group', 'entities': ['light.test1', 'light.test2']
}})
hass.states.async_set('light.test1', 'on',
{'xy_color': (1.0, 1.0), 'supported_features': 64})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.state == 'on'
assert state.attributes['supported_features'] == 64
assert state.attributes['xy_color'] == (1.0, 1.0)
hass.states.async_set('light.test2', 'on',
{'xy_color': (0.5, 0.5), 'supported_features': 64})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.state == 'on'
assert state.attributes['xy_color'] == (0.75, 0.75)
hass.states.async_set('light.test1', 'off',
{'xy_color': (1.0, 1.0), 'supported_features': 64})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.state == 'on'
assert state.attributes['xy_color'] == (0.5, 0.5)
async def test_rgb_color(hass):
"""Test RGB reporting."""
await async_setup_component(hass, 'light', {'light': {
'platform': 'group', 'entities': ['light.test1', 'light.test2']
}})
hass.states.async_set('light.test1', 'on',
{'rgb_color': (255, 0, 0), 'supported_features': 16})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.state == 'on'
assert state.attributes['supported_features'] == 16
assert state.attributes['rgb_color'] == (255, 0, 0)
hass.states.async_set('light.test2', 'on',
{'rgb_color': (255, 255, 255),
'supported_features': 16})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.attributes['rgb_color'] == (255, 127, 127)
hass.states.async_set('light.test1', 'off',
{'rgb_color': (255, 0, 0), 'supported_features': 16})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.attributes['rgb_color'] == (255, 255, 255)
async def test_white_value(hass):
"""Test white value reporting."""
await async_setup_component(hass, 'light', {'light': {
'platform': 'group', 'entities': ['light.test1', 'light.test2']
}})
hass.states.async_set('light.test1', 'on',
{'white_value': 255, 'supported_features': 128})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.attributes['white_value'] == 255
hass.states.async_set('light.test2', 'on',
{'white_value': 100, 'supported_features': 128})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.attributes['white_value'] == 177
hass.states.async_set('light.test1', 'off',
{'white_value': 255, 'supported_features': 128})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.attributes['white_value'] == 100
async def test_color_temp(hass):
"""Test color temp reporting."""
await async_setup_component(hass, 'light', {'light': {
'platform': 'group', 'entities': ['light.test1', 'light.test2']
}})
hass.states.async_set('light.test1', 'on',
{'color_temp': 2, 'supported_features': 2})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.attributes['color_temp'] == 2
hass.states.async_set('light.test2', 'on',
{'color_temp': 1000, 'supported_features': 2})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.attributes['color_temp'] == 501
hass.states.async_set('light.test1', 'off',
{'color_temp': 2, 'supported_features': 2})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.attributes['color_temp'] == 1000
async def test_min_max_mireds(hass):
"""Test min/max mireds reporting."""
await async_setup_component(hass, 'light', {'light': {
'platform': 'group', 'entities': ['light.test1', 'light.test2']
}})
hass.states.async_set('light.test1', 'on',
{'min_mireds': 2, 'max_mireds': 5,
'supported_features': 2})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.attributes['min_mireds'] == 2
assert state.attributes['max_mireds'] == 5
hass.states.async_set('light.test2', 'on',
{'min_mireds': 7, 'max_mireds': 1234567890,
'supported_features': 2})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.attributes['min_mireds'] == 2
assert state.attributes['max_mireds'] == 1234567890
hass.states.async_set('light.test1', 'off',
{'min_mireds': 1, 'max_mireds': 2,
'supported_features': 2})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.attributes['min_mireds'] == 1
assert state.attributes['max_mireds'] == 1234567890
async def test_effect_list(hass):
"""Test effect_list reporting."""
await async_setup_component(hass, 'light', {'light': {
'platform': 'group', 'entities': ['light.test1', 'light.test2']
}})
hass.states.async_set('light.test1', 'on',
{'effect_list': ['None', 'Random', 'Colorloop']})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert set(state.attributes['effect_list']) == {
'None', 'Random', 'Colorloop'}
hass.states.async_set('light.test2', 'on',
{'effect_list': ['None', 'Random', 'Rainbow']})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert set(state.attributes['effect_list']) == {
'None', 'Random', 'Colorloop', 'Rainbow'}
hass.states.async_set('light.test1', 'off',
{'effect_list': ['None', 'Colorloop', 'Seven']})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert set(state.attributes['effect_list']) == {
'None', 'Random', 'Colorloop', 'Seven', 'Rainbow'}
async def test_effect(hass):
"""Test effect reporting."""
await async_setup_component(hass, 'light', {'light': {
'platform': 'group', 'entities': ['light.test1', 'light.test2',
'light.test3']
}})
hass.states.async_set('light.test1', 'on',
{'effect': 'None', 'supported_features': 2})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.attributes['effect'] == 'None'
hass.states.async_set('light.test2', 'on',
{'effect': 'None', 'supported_features': 2})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.attributes['effect'] == 'None'
hass.states.async_set('light.test3', 'on',
{'effect': 'Random', 'supported_features': 2})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.attributes['effect'] == 'None'
hass.states.async_set('light.test1', 'off',
{'effect': 'None', 'supported_features': 2})
hass.states.async_set('light.test2', 'off',
{'effect': 'None', 'supported_features': 2})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.attributes['effect'] == 'Random'
async def test_supported_features(hass):
"""Test supported features reporting."""
await async_setup_component(hass, 'light', {'light': {
'platform': 'group', 'entities': ['light.test1', 'light.test2']
}})
hass.states.async_set('light.test1', 'on',
{'supported_features': 0})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.attributes['supported_features'] == 0
hass.states.async_set('light.test2', 'on',
{'supported_features': 2})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.attributes['supported_features'] == 2
hass.states.async_set('light.test1', 'off',
{'supported_features': 41})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.attributes['supported_features'] == 43
hass.states.async_set('light.test2', 'off',
{'supported_features': 256})
await hass.async_block_till_done()
state = hass.states.get('light.group_light')
assert state.attributes['supported_features'] == 41
async def test_service_calls(hass):
"""Test service calls."""
await async_setup_component(hass, 'light', {'light': [
{'platform': 'demo'},
{'platform': 'group', 'entities': ['light.bed_light',
'light.ceiling_lights',
'light.kitchen_lights']}
]})
await hass.async_block_till_done()
assert hass.states.get('light.group_light').state == 'on'
light.async_toggle(hass, 'light.group_light')
await hass.async_block_till_done()
assert hass.states.get('light.bed_light').state == 'off'
assert hass.states.get('light.ceiling_lights').state == 'off'
assert hass.states.get('light.kitchen_lights').state == 'off'
light.async_turn_on(hass, 'light.group_light')
await hass.async_block_till_done()
assert hass.states.get('light.bed_light').state == 'on'
assert hass.states.get('light.ceiling_lights').state == 'on'
assert hass.states.get('light.kitchen_lights').state == 'on'
light.async_turn_off(hass, 'light.group_light')
await hass.async_block_till_done()
assert hass.states.get('light.bed_light').state == 'off'
assert hass.states.get('light.ceiling_lights').state == 'off'
assert hass.states.get('light.kitchen_lights').state == 'off'
light.async_turn_on(hass, 'light.group_light', brightness=128,
effect='Random', rgb_color=(42, 255, 255))
await hass.async_block_till_done()
state = hass.states.get('light.bed_light')
assert state.state == 'on'
assert state.attributes['brightness'] == 128
assert state.attributes['effect'] == 'Random'
assert state.attributes['rgb_color'] == (42, 255, 255)
state = hass.states.get('light.ceiling_lights')
assert state.state == 'on'
assert state.attributes['brightness'] == 128
assert state.attributes['effect'] == 'Random'
assert state.attributes['rgb_color'] == (42, 255, 255)
state = hass.states.get('light.kitchen_lights')
assert state.state == 'on'
assert state.attributes['brightness'] == 128
assert state.attributes['effect'] == 'Random'
assert state.attributes['rgb_color'] == (42, 255, 255)
async def test_invalid_service_calls(hass):
"""Test invalid service call arguments get discarded."""
add_devices = MagicMock()
await group.async_setup_platform(hass, {
'entities': ['light.test1', 'light.test2']
}, add_devices)
assert add_devices.call_count == 1
grouped_light = add_devices.call_args[0][0][0]
grouped_light.hass = hass
with asynctest.patch.object(hass.services, 'async_call') as mock_call:
await grouped_light.async_turn_on(brightness=150, four_oh_four='404')
data = {
'entity_id': ['light.test1', 'light.test2'],
'brightness': 150
}
mock_call.assert_called_once_with('light', 'turn_on', data,
blocking=True)
mock_call.reset_mock()
await grouped_light.async_turn_off(transition=4, four_oh_four='404')
data = {
'entity_id': ['light.test1', 'light.test2'],
'transition': 4
}
mock_call.assert_called_once_with('light', 'turn_off', data,
blocking=True)
mock_call.reset_mock()
data = {
'brightness': 150,
'xy_color': (0.5, 0.42),
'rgb_color': (80, 120, 50),
'color_temp': 1234,
'white_value': 1,
'effect': 'Sunshine',
'transition': 4,
'flash': 'long'
}
await grouped_light.async_turn_on(**data)
data['entity_id'] = ['light.test1', 'light.test2']
mock_call.assert_called_once_with('light', 'turn_on', data,
blocking=True)