This commit is contained in:
Otto Winter
2018-02-20 20:56:50 +01:00
parent dd32be2ce3
commit 33dfd92984

View File

@@ -7,7 +7,7 @@ https://home-assistant.io/components/light.group/
import asyncio import asyncio
import logging import logging
import itertools import itertools
from typing import List, Tuple, Optional, Iterator, Any from typing import List, Tuple, Optional, Iterator, Any, Callable
from collections import Counter from collections import Counter
from copy import deepcopy from copy import deepcopy
@@ -75,8 +75,8 @@ class GroupLight(light.Light):
"""Register callbacks""" """Register callbacks"""
@callback @callback
def async_state_changed_listener(self, entity_id: str, def async_state_changed_listener(entity_id: str, old_state: State,
old_state: State, new_state: State): new_state: State):
"""Handle child updates.""" """Handle child updates."""
self.async_schedule_update_ha_state(True) self.async_schedule_update_ha_state(True)
@@ -168,7 +168,7 @@ class GroupLight(light.Light):
payload = deepcopy(kwargs) payload = deepcopy(kwargs)
payload[ATTR_ENTITY_ID] = entity_id payload[ATTR_ENTITY_ID] = entity_id
tasks.append(self.hass.services.async_call( tasks.append(self.hass.services.async_call(
'light', SERVICE_TURN_OFF, payload, blocking=True)) 'light', service, payload, blocking=True))
if tasks: if tasks:
await asyncio.wait(tasks, loop=self.hass.loop) await asyncio.wait(tasks, loop=self.hass.loop)
@@ -184,20 +184,22 @@ class GroupLight(light.Light):
async def async_update(self): async def async_update(self):
"""Query all members and determine the group state.""" """Query all members and determine the group state."""
states = self._child_states() states = self._child_states()
on_states = [state for state in states if state.state == STATE_ON]
self._state = _determine_on_off_state(states) self._state = _determine_on_off_state(states)
self._brightness = _reduce_attribute(states, 'brightness') self._brightness = _reduce_attribute(on_states, 'brightness')
self._xy_color = _reduce_attribute(states, 'xy_color') self._xy_color = _reduce_attribute(
self._rgb_color = _reduce_attribute(states, 'rgb_color') on_states, 'xy_color', reduce=_average_tuple)
self._color_temp = _reduce_attribute(states, 'color_temp') self._rgb_color = _reduce_attribute(
on_states, 'rgb_color', reduce=_average_tuple)
self._white_value = _reduce_attribute(on_states, 'white_value')
self._color_temp = _reduce_attribute(on_states, 'color_temp')
self._min_mireds = _reduce_attribute( self._min_mireds = _reduce_attribute(
states, 'min_mireds', default=154, force_on=False) states, 'min_mireds', default=154, reduce=min)
self._max_mireds = _reduce_attribute( self._max_mireds = _reduce_attribute(
states, 'max_mireds', default=500, force_on=False) states, 'max_mireds', default=500, reduce=max)
self._white_value = _reduce_attribute(states, 'white_value')
all_effect_lists = list( all_effect_lists = list(_find_state_attributes(states, 'effect_list'))
_find_state_attributes(states, 'effect_list', force_on=False))
self._effect_list = None self._effect_list = None
if all_effect_lists: if all_effect_lists:
# Merge all effects from all effect_lists with a union merge. # Merge all effects from all effect_lists with a union merge.
@@ -206,14 +208,13 @@ class GroupLight(light.Light):
all_effects = list(_find_state_attributes(states, 'effect')) all_effects = list(_find_state_attributes(states, 'effect'))
self._effect = None self._effect = None
if all_effects: if all_effects:
flat_effects = list(itertools.chain(*all_effect_lists)) flat_effects = list(itertools.chain(all_effects))
# Report the most common effect. # Report the most common effect.
effects_count = Counter(flat_effects) effects_count = Counter(flat_effects)
self._effect = effects_count.most_common(1)[0][0] self._effect = effects_count.most_common(1)[0][0]
self._supported_features = 0 self._supported_features = 0
for support in _find_state_attributes( for support in _find_state_attributes(states, 'supported_features'):
states, 'supported_features', force_on=False):
# Merge supported features by emulating support for every feature # Merge supported features by emulating support for every feature
# we find. # we find.
self._supported_features |= support self._supported_features |= support
@@ -228,27 +229,37 @@ class GroupLight(light.Light):
def _find_state_attributes(states: List[State], def _find_state_attributes(states: List[State],
key: str, key: str) -> Iterator[Any]:
force_on: bool = True) -> Iterator[Any]: """Find attributes with matching key from states."""
"""Find attributes with matching key from states.
Only return attributes of enabled lights when force_on is True.
"""
for state in states: for state in states:
assume_on = (not force_on) or state.state == STATE_ON if key in state.attributes:
if assume_on and key in state.attributes:
yield state.attributes.get(key) yield state.attributes.get(key)
def _average(*args):
"""Return the average of the supplied values."""
return sum(args) / len(args)
def _average_tuple(*args):
"""Return the average values along the columns of the supplied values."""
return tuple(sum(l) / len(l) for l in zip(*args))
def _reduce_attribute(states: List[State], def _reduce_attribute(states: List[State],
key: str, key: str,
default: Optional[Any] = None, default: Optional[Any] = None,
force_on: bool = True) -> Any: reduce: Callable[..., Any] = _average) -> Any:
"""Find the first attribute matching key from states. """Find the first attribute matching key from states.
If none are found, return default. If none are found, return default.
""" """
return next(_find_state_attributes(states, key, force_on), default) attrs = list(_find_state_attributes(states, key))
if not attrs:
return default
return reduce(*attrs)
def _determine_on_off_state(states: List[State]) -> str: def _determine_on_off_state(states: List[State]) -> str: