Skip to content

Instantly share code, notes, and snippets.

@jjensn
Forked from hordurk/grouped_light.py
Last active March 6, 2018 19:47
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jjensn/83dd60d96330bbf66e58dfae336187bf to your computer and use it in GitHub Desktop.
Save jjensn/83dd60d96330bbf66e58dfae336187bf to your computer and use it in GitHub Desktop.
Grouped light platform for Home Assistant
import logging
# Import the device class from the component that you want to support
from homeassistant.components import light
from homeassistant.const import (STATE_OFF, STATE_ON, SERVICE_TURN_ON,
SERVICE_TURN_OFF, ATTR_ENTITY_ID)
from homeassistant.components.light import (SUPPORT_BRIGHTNESS,
SUPPORT_RGB_COLOR,
SUPPORT_COLOR_TEMP,
SUPPORT_TRANSITION)
CONF_NAME = 'name'
CONF_ENTITIES = 'entities' #
_LOGGER = logging.getLogger(__name__)
SUPPORT_GROUP_LIGHT = (SUPPORT_BRIGHTNESS | SUPPORT_RGB_COLOR |
SUPPORT_COLOR_TEMP | SUPPORT_TRANSITION)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Initialize grouped light platform."""
name = config.get(CONF_NAME)
entity_ids = config.get(CONF_ENTITIES)
if name is None or entity_ids is None or len(entity_ids) == 0:
_LOGGER.error('Invalid config. Excepted %s and %s', CONF_NAME, CONF_ENTITIES)
return False
add_devices([GroupedLight(hass, name, entity_ids)])
class GroupedLight(light.Light):
"""Represents an Grouped Light in Home Assistant."""
def __init__(self, hass, name, entity_ids):
"""Initialize a Grouped Light."""
self.hass = hass
self._name = name
self._entity_ids = entity_ids
@property
def name(self):
return self._name
@property
def brightness(self):
"""Brightness of the light group"""
brightness = 0
for state in self._light_states():
if not 'brightness' in state.attributes:
return None
brightness += state.attributes.get('brightness')
brightness = brightness / float(len(self._entity_ids))
return brightness
@property
def color_temp(self):
"""Return the CT color value."""
for state in self._light_states():
if not 'color_temp' in state.attributes:
return None
return state.attributes.get('color_temp')
@property
def xy_color(self):
"""Return the XY color value."""
for state in self._light_states():
if not 'xy_color' in state.attributes:
return None
#return the first value we get since merging color values does not make sense
return state.attributes.get('xy_color')
@property
def rgb_color(self):
"""Return the RGB color value."""
for state in self._light_states():
if not 'rgb_color' in state.attributes:
return None
#return the first value we get since merging color values does not make sense
return state.attributes.get('rgb_color')
@property
def is_on(self):
"""If light is on."""
for state in self._light_states():
if state.state == STATE_ON:
return True
return False
@property
def supported_features(self):
"""Flag supported features."""
return SUPPORT_GROUP_LIGHT
def _light_states(self):
"""The states that the group is tracking."""
states = []
for entity_id in self._entity_ids:
state = self.hass.states.get(entity_id)
if state is not None:
states.append(state)
return states
def turn_on(self, **kwargs):
"""Forward the turn_on command to all lights in the group"""
for entity_id in self._entity_ids:
kwargs[ATTR_ENTITY_ID] = entity_id
self.hass.services.call('light', SERVICE_TURN_ON, kwargs, blocking=True)
def turn_off(self, **kwargs):
"""Forward the turn_off command to all lights in the group"""
for entity_id in self._entity_ids:
kwargs[ATTR_ENTITY_ID] = entity_id
self.hass.services.call('light', SERVICE_TURN_OFF, kwargs, blocking=True)
@jjensn
Copy link
Author

jjensn commented Oct 25, 2016

Updated to add support for RGB bulbs

@jjensn
Copy link
Author

jjensn commented Jan 11, 2017

Updated to support attributes (now can be controlled by alexa)

@timdonovanuk
Copy link

How do you use this (in configuration.yaml)?

@rickybrent
Copy link

Thanks, this works great. :)

@timdonovanuk: just in case someone else has the same question later:

light:
  - platform: grouped_light
    name: "Test Grouped Light"
    entities:
      - light.first_light_name
      - light.second_light_name

@TehRobot
Copy link

TehRobot commented Jan 9, 2018

This is the best. 👍

Make sure you copy the 'grouped_light.py' into /custom_components/light or you will be getting all kinds of errors.

@Gronis
Copy link

Gronis commented Jan 28, 2018

Great work with this custom component. Works very well. Shame it is not part of home-assistant by default.

Is it possible to add an event callback/listener so that if all the lights in the group are turned off, the grouped_light will change its state to off (like how the normal group component works)? Right now, if I turn off the individual lights instead of the grouped_light, the grouped_light for those individual lights are still considered to be "on".

@Gronis
Copy link

Gronis commented Jan 29, 2018

Here is a version which updates when the other entities change. I also changed the way some properties are computed since the old version assumed all entities were in sync (or so it seamed)

import asyncio
import logging

# Import the device class from the component that you want to support
from homeassistant.core import callback
from homeassistant.components import light
from homeassistant.const import (STATE_OFF, STATE_ON, SERVICE_TURN_ON,
                                 SERVICE_TURN_OFF, ATTR_ENTITY_ID)
from homeassistant.components.light import (SUPPORT_BRIGHTNESS,
                                            SUPPORT_RGB_COLOR,
                                            SUPPORT_COLOR_TEMP,
                                            SUPPORT_TRANSITION)
from homeassistant.helpers.event import async_track_state_change

CONF_NAME = 'name'
CONF_ENTITIES = 'entities' #

_LOGGER = logging.getLogger(__name__)

SUPPORT_GROUP_LIGHT = (SUPPORT_BRIGHTNESS | SUPPORT_RGB_COLOR |
                       SUPPORT_COLOR_TEMP | SUPPORT_TRANSITION)

@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
    """Initialize grouped light platform."""
    name = config.get(CONF_NAME)
    entity_ids = config.get(CONF_ENTITIES)

    if entity_ids is None:
        _LOGGER.error('No entity_ids')
        return False

    if name is None or entity_ids is None or len(entity_ids) == 0:
        _LOGGER.error('Invalid config. Excepted %s and %s', CONF_NAME, CONF_ENTITIES)
        return False

    lights = [GroupedLight(hass, name, entity_ids)]

    async_add_devices(lights)
    return True


class GroupedLight(light.Light):
    """Represents an Grouped Light in Home Assistant."""

    def __init__(self, hass, name, entity_ids):
        """Initialize a Grouped Light."""
        self.hass = hass
        self._name = name
        self._entity_ids = entity_ids
        self._tracking = tuple(ent_id.lower() for ent_id in entity_ids)

        #@callback
        def async_state_changed_listener(entity_id, old_state, new_state):
            """Respond to a member state changing."""
            self.hass.async_add_job(self.async_update_ha_state, True)

        async_track_state_change(
            self.hass, self._tracking, async_state_changed_listener
        )

    @property
    def name(self):
        return self._name

    @property
    def brightness(self):
        """Brightness of the light group"""
        brightness = 0
        count = 0
        for state in self._light_states():
            if 'brightness' in state.attributes and state.state == STATE_ON:
                brightness += state.attributes.get('brightness')
                count += 1
        brightness = brightness / float(count or 1)
        return brightness

    @property
    def color_temp(self):
        """Return the CT color value."""
        for state in self._light_states():
            if 'color_temp' in state.attributes and state.state == STATE_ON:
             return state.attributes.get('color_temp')
        return None

    @property
    def xy_color(self):
        """Return the XY color value."""
        for state in self._light_states():
            if 'xy_color' in state.attributes and state.state == STATE_ON:
                return state.attributes.get('xy_color')
        return None

    @property
    def rgb_color(self):
        """Return the RGB color value."""
        for state in self._light_states():
            if 'rgb_color' in state.attributes and state.state == STATE_ON:
                return state.attributes.get('rgb_color')
        return None

    @property
    def is_on(self):
        """If light is on."""
        for state in self._light_states():
            if state.state == STATE_ON:
                return True
        return False

    @property
    def supported_features(self):
        """Flag supported features."""
        return SUPPORT_GROUP_LIGHT

    def _light_states(self):
        """The states that the group is tracking."""
        states = []

        for entity_id in self._entity_ids:
            state = self.hass.states.get(entity_id)
            if state is not None:
                states.append(state)

        return states

    def turn_on(self, **kwargs):
        """Forward the turn_on command to all lights in the group"""
        for entity_id in self._entity_ids:
            kwargs[ATTR_ENTITY_ID] = entity_id
            self.hass.services.call('light', SERVICE_TURN_ON, kwargs, blocking=True)

    def turn_off(self, **kwargs):
        """Forward the turn_off command to all lights in the group"""
        for entity_id in self._entity_ids:
            kwargs[ATTR_ENTITY_ID] = entity_id
            self.hass.services.call('light', SERVICE_TURN_OFF, kwargs, blocking=True)

@jjensn
Copy link
Author

jjensn commented Feb 9, 2018

Wow I had no idea people were still using this

I have created a repo and will take everyone's PR requests:

https://github.com/jjensn/home-assistant-grouped-lights

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment