Reworked entity id extraction from service calls
This commit is contained in:
parent
49e6386794
commit
2890f2d6cc
5 changed files with 116 additions and 66 deletions
|
@ -20,7 +20,10 @@ import importlib
|
|||
import homeassistant as ha
|
||||
import homeassistant.util as util
|
||||
|
||||
# String that contains an entity id or a comma seperated list of entity ids
|
||||
ATTR_ENTITY_ID = 'entity_id'
|
||||
|
||||
# String with a friendly name for the entity
|
||||
ATTR_FRIENDLY_NAME = "friendly_name"
|
||||
|
||||
STATE_ON = 'on'
|
||||
|
@ -38,7 +41,29 @@ SERVICE_MEDIA_PLAY_PAUSE = "media_play_pause"
|
|||
SERVICE_MEDIA_NEXT_TRACK = "media_next_track"
|
||||
SERVICE_MEDIA_PREV_TRACK = "media_prev_track"
|
||||
|
||||
_LOADED_MOD = {}
|
||||
_LOADED_COMP = {}
|
||||
|
||||
|
||||
def _get_component(component):
|
||||
""" Returns requested component. Imports it if necessary. """
|
||||
|
||||
comps = _LOADED_COMP
|
||||
|
||||
# See if we have the module locally cached, else import it
|
||||
try:
|
||||
return comps[component]
|
||||
|
||||
except KeyError:
|
||||
# If comps[component] does not exist, import module
|
||||
try:
|
||||
comps[component] = importlib.import_module(
|
||||
'homeassistant.components.'+component)
|
||||
|
||||
except ImportError:
|
||||
# If we got a bogus component the input will fail
|
||||
comps[component] = None
|
||||
|
||||
return comps[component]
|
||||
|
||||
|
||||
def is_on(statemachine, entity_id=None):
|
||||
|
@ -46,26 +71,10 @@ def is_on(statemachine, entity_id=None):
|
|||
If there is no entity id given we will check all. """
|
||||
entity_ids = [entity_id] if entity_id else statemachine.entity_ids
|
||||
|
||||
# Save reference locally because those lookups are way faster
|
||||
modules = _LOADED_MOD
|
||||
|
||||
for entity_id in entity_ids:
|
||||
domain = util.split_entity_id(entity_id)[0]
|
||||
|
||||
# See if we have the module locally cached, else import it
|
||||
# Does this code look chaotic? Yes!
|
||||
try:
|
||||
module = modules[domain]
|
||||
|
||||
except KeyError:
|
||||
# If modules[domain] does not exist, import module
|
||||
try:
|
||||
module = modules[domain] = importlib.import_module(
|
||||
'homeassistant.components.'+domain)
|
||||
|
||||
except ImportError:
|
||||
# If we got a bogus domain the input will fail
|
||||
module = modules[domain] = None
|
||||
module = _get_component(domain)
|
||||
|
||||
try:
|
||||
if module.is_on(statemachine, entity_id):
|
||||
|
@ -108,6 +117,45 @@ def turn_off(bus, entity_id=None):
|
|||
pass
|
||||
|
||||
|
||||
def extract_entity_ids(statemachine, service):
|
||||
"""
|
||||
Helper method to extract a list of entity ids from a service call.
|
||||
Will convert group entity ids to the entity ids it represents.
|
||||
"""
|
||||
entity_ids = []
|
||||
|
||||
if service.data and ATTR_ENTITY_ID in service.data:
|
||||
group = _get_component('group')
|
||||
|
||||
# Entity ID attr can be a list or a string
|
||||
service_ent_id = service.data[ATTR_ENTITY_ID]
|
||||
if isinstance(service_ent_id, list):
|
||||
ent_ids = service_ent_id
|
||||
else:
|
||||
ent_ids = [service_ent_id]
|
||||
|
||||
for entity_id in ent_ids:
|
||||
try:
|
||||
# If entity_id points at a group, expand it
|
||||
domain, _ = util.split_entity_id(entity_id)
|
||||
|
||||
if domain == group.DOMAIN:
|
||||
entity_ids.extend(
|
||||
ent_id for ent_id
|
||||
in group.get_entity_ids(statemachine, entity_id)
|
||||
if ent_id not in entity_ids)
|
||||
|
||||
else:
|
||||
if entity_id not in entity_ids:
|
||||
entity_ids.append(entity_id)
|
||||
|
||||
except AttributeError:
|
||||
# Raised by util.split_entity_id if entity_id is not a string
|
||||
pass
|
||||
|
||||
return entity_ids
|
||||
|
||||
|
||||
def setup(bus):
|
||||
""" Setup general services related to homeassistant. """
|
||||
|
||||
|
|
|
@ -172,13 +172,14 @@ def setup(bus, statemachine):
|
|||
|
||||
def _service_to_entities(service):
|
||||
""" Helper method to get entities from service. """
|
||||
entity_id = service.data.get(components.ATTR_ENTITY_ID)
|
||||
entity_ids = components.extract_entity_ids(statemachine, service)
|
||||
|
||||
if entity_id:
|
||||
cast = casts.get(entity_id)
|
||||
if entity_ids:
|
||||
for entity_id in entity_ids:
|
||||
cast = casts.get(entity_id)
|
||||
|
||||
if cast:
|
||||
yield entity_id, cast
|
||||
if cast:
|
||||
yield entity_id, cast
|
||||
|
||||
else:
|
||||
for item in casts.items():
|
||||
|
|
|
@ -8,17 +8,20 @@ Provides functionality to group devices that can be turned on or off.
|
|||
import logging
|
||||
|
||||
import homeassistant as ha
|
||||
import homeassistant.components as components
|
||||
from homeassistant.components import (STATE_ON, STATE_OFF,
|
||||
STATE_HOME, STATE_NOT_HOME,
|
||||
SERVICE_TURN_ON, SERVICE_TURN_OFF,
|
||||
turn_on as comp_turn_on,
|
||||
turn_off as comp_turn_off,
|
||||
ATTR_ENTITY_ID)
|
||||
|
||||
DOMAIN = "group"
|
||||
|
||||
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
||||
|
||||
STATE_ATTR_ENTITY_IDS = "entity_ids"
|
||||
|
||||
_GROUP_TYPES = {
|
||||
"on_off": (components.STATE_ON, components.STATE_OFF),
|
||||
"home_not_home": (components.STATE_HOME, components.STATE_NOT_HOME)
|
||||
"on_off": (STATE_ON, STATE_OFF),
|
||||
"home_not_home": (STATE_HOME, STATE_NOT_HOME)
|
||||
}
|
||||
|
||||
|
||||
|
@ -51,7 +54,7 @@ def get_entity_ids(statemachine, entity_id):
|
|||
""" Get the entity ids that make up this group. """
|
||||
try:
|
||||
return \
|
||||
statemachine.get_state(entity_id).attributes[STATE_ATTR_ENTITY_IDS]
|
||||
statemachine.get_state(entity_id).attributes[ATTR_ENTITY_ID]
|
||||
except (AttributeError, KeyError):
|
||||
# AttributeError if state did not exist
|
||||
# KeyError if key did not exist in attributes
|
||||
|
@ -111,7 +114,7 @@ def setup(bus, statemachine, name, entity_ids):
|
|||
return False
|
||||
|
||||
group_entity_id = ENTITY_ID_FORMAT.format(name)
|
||||
state_attr = {STATE_ATTR_ENTITY_IDS: entity_ids}
|
||||
state_attr = {ATTR_ENTITY_ID: entity_ids}
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def update_group_state(entity_id, old_state, new_state):
|
||||
|
@ -141,28 +144,28 @@ def setup(bus, statemachine, name, entity_ids):
|
|||
# group.setup is called to setup each group. Only the first time will we
|
||||
# register a turn_on and turn_off method for groups.
|
||||
|
||||
if not bus.has_service(DOMAIN, components.SERVICE_TURN_ON):
|
||||
if not bus.has_service(DOMAIN, SERVICE_TURN_ON):
|
||||
def turn_group_on_service(service):
|
||||
""" Call components.turn_on for each entity_id from this group. """
|
||||
for entity_id in get_entity_ids(statemachine,
|
||||
service.data.get(
|
||||
components.ATTR_ENTITY_ID)):
|
||||
ATTR_ENTITY_ID)):
|
||||
|
||||
components.turn_on(bus, entity_id)
|
||||
comp_turn_on(bus, entity_id)
|
||||
|
||||
bus.register_service(DOMAIN, components.SERVICE_TURN_ON,
|
||||
bus.register_service(DOMAIN, SERVICE_TURN_ON,
|
||||
turn_group_on_service)
|
||||
|
||||
if not bus.has_service(DOMAIN, components.SERVICE_TURN_OFF):
|
||||
if not bus.has_service(DOMAIN, SERVICE_TURN_OFF):
|
||||
def turn_group_off_service(service):
|
||||
""" Call components.turn_off for each entity_id in this group. """
|
||||
for entity_id in get_entity_ids(statemachine,
|
||||
service.data.get(
|
||||
components.ATTR_ENTITY_ID)):
|
||||
ATTR_ENTITY_ID)):
|
||||
|
||||
components.turn_off(bus, entity_id)
|
||||
comp_turn_off(bus, entity_id)
|
||||
|
||||
bus.register_service(DOMAIN, components.SERVICE_TURN_OFF,
|
||||
bus.register_service(DOMAIN, SERVICE_TURN_OFF,
|
||||
turn_group_off_service)
|
||||
|
||||
statemachine.set_state(group_entity_id, group_state, state_attr)
|
||||
|
|
|
@ -12,7 +12,8 @@ from collections import namedtuple
|
|||
|
||||
import homeassistant as ha
|
||||
import homeassistant.util as util
|
||||
from homeassistant.components import (group, STATE_ON, STATE_OFF,
|
||||
from homeassistant.components import (group, extract_entity_ids,
|
||||
STATE_ON, STATE_OFF,
|
||||
SERVICE_TURN_ON, SERVICE_TURN_OFF,
|
||||
ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME)
|
||||
|
||||
|
@ -157,15 +158,18 @@ def setup(bus, statemachine, light_control):
|
|||
# Get and validate data
|
||||
dat = service.data
|
||||
|
||||
if ATTR_ENTITY_ID in dat:
|
||||
light_id = ent_to_light.get(dat[ATTR_ENTITY_ID])
|
||||
else:
|
||||
light_id = None
|
||||
# Convert the entity ids to valid light ids
|
||||
light_ids = [ent_to_light[entity_id] for entity_id
|
||||
in extract_entity_ids(statemachine, service)
|
||||
if entity_id in ent_to_light]
|
||||
|
||||
if not light_ids:
|
||||
light_ids = ent_to_light.values()
|
||||
|
||||
transition = util.dict_get_convert(dat, ATTR_TRANSITION, int, None)
|
||||
|
||||
if service.service == SERVICE_TURN_OFF:
|
||||
light_control.turn_light_off(light_id, transition)
|
||||
light_control.turn_light_off(light_ids, transition)
|
||||
|
||||
else:
|
||||
# Processing extra data for turn light on request
|
||||
|
@ -201,13 +205,14 @@ def setup(bus, statemachine, light_control):
|
|||
except (TypeError, ValueError):
|
||||
# TypeError if color has no len
|
||||
# ValueError if not all values convertable to int
|
||||
color = None
|
||||
pass
|
||||
|
||||
light_control.turn_light_on(light_id, transition, bright, color)
|
||||
light_control.turn_light_on(light_ids, transition, bright, color)
|
||||
|
||||
# Update state of lights touched
|
||||
if light_id:
|
||||
update_light_state(light_id)
|
||||
# Update state of lights touched. If there was only 1 light selected
|
||||
# then just update that light else update all
|
||||
if len(light_ids) == 1:
|
||||
update_light_state(light_ids[0])
|
||||
else:
|
||||
update_lights_state(None, True)
|
||||
|
||||
|
@ -329,11 +334,8 @@ class HueLightControl(object):
|
|||
|
||||
return states
|
||||
|
||||
def turn_light_on(self, light_id, transition, brightness, xy_color):
|
||||
def turn_light_on(self, light_ids, transition, brightness, xy_color):
|
||||
""" Turn the specified or all lights on. """
|
||||
if light_id is None:
|
||||
light_id = self._lights.keys()
|
||||
|
||||
command = {'on': True}
|
||||
|
||||
if transition is not None:
|
||||
|
@ -347,13 +349,10 @@ class HueLightControl(object):
|
|||
if xy_color:
|
||||
command['xy'] = xy_color
|
||||
|
||||
self._bridge.set_light(light_id, command)
|
||||
self._bridge.set_light(light_ids, command)
|
||||
|
||||
def turn_light_off(self, light_id, transition):
|
||||
def turn_light_off(self, light_ids, transition):
|
||||
""" Turn the specified or all lights off. """
|
||||
if light_id is None:
|
||||
light_id = self._lights.keys()
|
||||
|
||||
command = {'on': False}
|
||||
|
||||
if transition is not None:
|
||||
|
@ -361,4 +360,4 @@ class HueLightControl(object):
|
|||
# 900 seconds.
|
||||
command['transitiontime'] = min(9000, transition * 10)
|
||||
|
||||
self._bridge.set_light(light_id, command)
|
||||
self._bridge.set_light(light_ids, command)
|
||||
|
|
|
@ -7,7 +7,8 @@ from datetime import datetime, timedelta
|
|||
|
||||
import homeassistant as ha
|
||||
import homeassistant.util as util
|
||||
from homeassistant.components import (group, STATE_ON, STATE_OFF,
|
||||
from homeassistant.components import (group, extract_entity_ids,
|
||||
STATE_ON, STATE_OFF,
|
||||
SERVICE_TURN_ON, SERVICE_TURN_OFF,
|
||||
ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME)
|
||||
DOMAIN = 'wemo'
|
||||
|
@ -118,13 +119,11 @@ def setup(bus, statemachine):
|
|||
|
||||
def _handle_wemo_service(service):
|
||||
""" Handles calls to the WeMo service. """
|
||||
dat = service.data
|
||||
devices = [ent_to_dev[entity_id] for entity_id
|
||||
in extract_entity_ids(statemachine, service)
|
||||
if entity_id in ent_to_dev]
|
||||
|
||||
if ATTR_ENTITY_ID in dat:
|
||||
device = ent_to_dev.get(dat[ATTR_ENTITY_ID])
|
||||
|
||||
devices = [device] if device is not None else []
|
||||
else:
|
||||
if not devices:
|
||||
devices = ent_to_dev.values()
|
||||
|
||||
for device in devices:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue