Skip to content

Instantly share code, notes, and snippets.

@randellhodges
Last active December 22, 2020 20:15
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save randellhodges/96a0cd9bafa03fb90ada49040d65f1b0 to your computer and use it in GitHub Desktop.
Save randellhodges/96a0cd9bafa03fb90ada49040d65f1b0 to your computer and use it in GitHub Desktop.
In the directory your configuration.yaml file is location, create a folder called custom_components and put group_globber.py there. Then inside your configuration.yaml file, add a section like so:
group_globber:
groups:
group.group_emby:
- media_player.emby*
You should probably already have the group, in this case "group_emby" already created somewhere. Then you can add a list of wildcards that'll match entities and put them in that group.
Note: I have not tried modifying the built in "all" groups and I have always had the group I want to add things to already created.
import asyncio
import fnmatch
import re
import logging
import voluptuous as vol
from homeassistant.helpers import config_validation as cv
from homeassistant.const import (
EVENT_STATE_CHANGED)
from homeassistant.components import (
group)
import homeassistant.core as ha
DOMAIN = 'group_globber'
_LOGGER = logging.getLogger(__name__)
CONF_GROUPS = 'groups'
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_GROUPS): vol.Schema({
str: vol.Schema(vol.All(cv.ensure_list, [cv.string]))
})
})
}, extra=vol.ALLOW_EXTRA)
@asyncio.coroutine
def async_setup(hass, config):
#lock = threading.Lock()
config = config.get(DOMAIN)
groups = config.get(CONF_GROUPS)
# We are going to keep track of what we see, so we don't have to check the groups
# on every state update.
seen = set()
for key, value in groups.items():
groups[key] = [re.compile(fnmatch.translate(x)) for x in value]
@ha.callback
def _handle_state_change(event):
entity_id = event.data.get('entity_id')
domain, object_id = ha.split_entity_id(entity_id)
if domain == 'group':
# Newly created group won't have an old_state
is_new = event.data.get('old_state') is None
if is_new:
#_LOGGER.debug("Group %s created", entity_id)
group_id = entity_id
expressions = groups.get(group_id)
if expressions:
# We have to get all the items and then see if they match. Seems
# like it could be rather expensive if we had a lot of groups created (reloaded).
# Maybe I'm micro-optimizing...
for entity_id in hass.states.async_entity_ids():
seen.add(entity_id)
if any(x.match(entity_id) for x in expressions):
_LOGGER.debug("Adding %s to group %s (reload)", entity_id, group_id)
group.set_group(hass, object_id, add=[entity_id])
else:
# Check that we have a state. If it isn't there, that means it is a
# probably being removed
new_state = event.data.get('new_state')
if new_state:
if not entity_id in seen:
seen.add(entity_id)
for group_id, expressions in groups.items():
if any(x.match(entity_id) for x in expressions):
_LOGGER.debug("Adding %s to group %s (state_change)", entity_id, group_id)
_, object_id = ha.split_entity_id(group_id)
group.set_group(hass, object_id, add=[entity_id])
return
# If we get here, that means we didn't have a new_state, so that means it was removed
if entity_id in seen:
seen.remove(entity_id)
hass.bus.async_listen(EVENT_STATE_CHANGED, _handle_state_change)
return True
@crazybadger
Copy link

This sounds like just what I was looking for - a way to group all emby entities - but I'm struggling to get it working.

I've created the sub-folder called custom_components and created the group_globber.py file with the code above.

I've added the group_globber: code to the bottom of my configuration.yaml file and I've created a group_emby in my group.yaml file which looks like this:

group_emby:
name: emby
view: yes
entities:
-media_player.emby*

But I'm getting invalid config on my group. What am I doing wrong?

@Joshfindit
Copy link

@crazybadger: Just got this working as follows:

configuration.yaml

...
group_globber:
  groups:
    group.all_media_players:
      - media_player.*
...
group: !include groups.yaml

groups.yaml

  all_media_players:
    name: 'All Media Players'
    entities:
      - media_player.xboxone_2

Note: I added a single already-existing media player, because I suspected home assistant might be reluctant to create an empty group. I did not test this, however.

@Joshfindit
Copy link

@randellhodges
Thank you, this grouped them without issue.

To go further; how would one create an automation based on any media player playing or paused?
From what I can see, group state seems to be reflective of all entities within the group (all_lights only has a state of on when all the lights are on).

I'd like to trigger lights off when any single media player (or combination of media players) turns on.

@bbrendon
Copy link

I think this broke in 0.80? No?

@madrover
Copy link

I think so... I'm now at 0.84.6. I can see the following error in the HA log.

2018-12-31 00:41:01 ERROR (MainThread) [homeassistant.core] Error doing job: Exception in callback async_setup.<locals>._handle_state_change(<Event state_...binihomelocal>) at /opt/homeassistant/.homeassistant/custom_components/group_globber.py:45
Traceback (most recent call last):
  File "/usr/lib/python3.5/asyncio/events.py", line 126, in _run
    self._callback(*self._args)
  File "/opt/homeassistant/.homeassistant/custom_components/group_globber.py", line 84, in _handle_state_change
    group.set_group(hass, object_id, add=[entity_id])
AttributeError: module 'homeassistant.components.group' has no attribute 'set_group'

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