Fixes & Tests

This commit is contained in:
Otto Winter
2018-02-23 10:44:32 +01:00
parent 9753268e2a
commit 1eab314b1c
3 changed files with 417 additions and 30 deletions

View File

@@ -206,8 +206,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 [
@@ -216,7 +217,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

@@ -18,13 +18,15 @@ from homeassistant.components import light
from homeassistant.const import (STATE_OFF, STATE_ON, SERVICE_TURN_ON,
SERVICE_TURN_OFF, ATTR_ENTITY_ID, CONF_NAME,
CONF_ENTITIES, STATE_UNAVAILABLE,
STATE_UNKNOWN, EVENT_HOMEASSISTANT_START)
STATE_UNKNOWN, 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)
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)
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
@@ -47,7 +49,7 @@ async def async_setup_platform(hass: HomeAssistantType, config: ConfigType,
async_add_devices, discovery_info=None) -> None:
"""Initialize light.group platform."""
async_add_devices(
[GroupLight(hass, config.get(CONF_NAME), config[CONF_ENTITIES])])
[GroupLight(hass, config.get(CONF_NAME), config[CONF_ENTITIES])], True)
class GroupLight(light.Light):
@@ -70,26 +72,24 @@ class GroupLight(light.Light):
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"""
"""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)
@callback
def async_homeassistant_start(event):
"""Track child state changes on startup."""
async_track_state_change(self.hass, self._entity_ids,
async_state_changed_listener)
self._async_unsub_state_changed = async_track_state_change(
self.hass, self._entity_ids, async_state_changed_listener)
self.async_schedule_update_ha_state(True)
self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START,
async_homeassistant_start)
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:
@@ -168,7 +168,7 @@ class GroupLight(light.Light):
payload = deepcopy(kwargs)
payload[ATTR_ENTITY_ID] = entity_id
tasks.append(self.hass.services.async_call(
'light', service, payload, blocking=True))
light.DOMAIN, service, payload, blocking=True))
if tasks:
await asyncio.wait(tasks, loop=self.hass.loop)
@@ -188,35 +188,40 @@ class GroupLight(light.Light):
self._state = _determine_on_off_state(states)
self._brightness = _reduce_attribute(on_states, 'brightness')
self._brightness = _reduce_attribute(on_states, ATTR_BRIGHTNESS)
self._xy_color = _reduce_attribute(
on_states, 'xy_color', reduce=_average_tuple)
on_states, ATTR_XY_COLOR, reduce=_average_tuple)
self._rgb_color = _reduce_attribute(
on_states, 'rgb_color', reduce=_average_tuple)
on_states, ATTR_RGB_COLOR, reduce=_average_tuple)
if self._rgb_color is not None:
self._rgb_color = tuple(map(int, self._rgb_color))
self._white_value = _reduce_attribute(on_states, 'white_value')
self._color_temp = _reduce_attribute(on_states, 'color_temp')
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, 'min_mireds', default=154, reduce=min)
states, ATTR_MIN_MIREDS, default=154, reduce=min)
self._max_mireds = _reduce_attribute(
states, 'max_mireds', default=500, reduce=max)
states, ATTR_MAX_MIREDS, default=500, reduce=max)
self._effect_list = None
all_effect_lists = list(_find_state_attributes(states, 'effect_list'))
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(states, 'effect'))
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, 'supported_features'):
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
@@ -239,9 +244,9 @@ def _find_state_attributes(states: List[State],
yield value
def _average(*args):
def _average_int(*args):
"""Return the average of the supplied values."""
return sum(args) / len(args)
return int(sum(args) / len(args))
def _average_tuple(*args):
@@ -252,7 +257,7 @@ def _average_tuple(*args):
def _reduce_attribute(states: List[State],
key: str,
default: Optional[Any] = None,
reduce: Callable[..., Any] = _average) -> Any:
reduce: Callable[..., Any] = _average_int) -> Any:
"""Find the first attribute matching key from states.
If none are found, return default.
@@ -262,6 +267,9 @@ def _reduce_attribute(states: List[State],
if not attrs:
return default
if len(attrs) == 1:
return attrs[0]
return reduce(*attrs)

View File

@@ -0,0 +1,371 @@
"""The tests for the Group Light platform."""
from homeassistant.components import light
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', 'unknown')
hass.states.async_set('light.test2', 'unknown')
await hass.async_block_till_done()
assert hass.states.get('light.group_light').state == 'unknown'
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)