Add service to change visibility of a group (#3998)
parent
3f6a5564ad
commit
33e46b484f
|
@ -33,6 +33,13 @@ CONF_VIEW = 'view'
|
|||
ATTR_AUTO = 'auto'
|
||||
ATTR_ORDER = 'order'
|
||||
ATTR_VIEW = 'view'
|
||||
ATTR_VISIBLE = 'visible'
|
||||
|
||||
SERVICE_SET_VISIBILITY = 'set_visibility'
|
||||
SET_VISIBILITY_SERVICE_SCHEMA = vol.Schema({
|
||||
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
||||
vol.Required(ATTR_VISIBLE): cv.boolean
|
||||
})
|
||||
|
||||
SERVICE_RELOAD = 'reload'
|
||||
RELOAD_SERVICE_SCHEMA = vol.Schema({})
|
||||
|
@ -89,6 +96,12 @@ def reload(hass):
|
|||
hass.services.call(DOMAIN, SERVICE_RELOAD)
|
||||
|
||||
|
||||
def set_visibility(hass, entity_id=None, visible=True):
|
||||
"""Hide or shows a group."""
|
||||
data = {ATTR_ENTITY_ID: entity_id, ATTR_VISIBLE: visible}
|
||||
hass.services.call(DOMAIN, SERVICE_SET_VISIBILITY, data)
|
||||
|
||||
|
||||
def expand_entity_ids(hass, entity_ids):
|
||||
"""Return entity_ids with group entity ids replaced by their members.
|
||||
|
||||
|
@ -164,6 +177,18 @@ def async_setup(hass, config):
|
|||
return
|
||||
hass.loop.create_task(_async_process_config(hass, conf, component))
|
||||
|
||||
@callback
|
||||
def visibility_service_handler(service):
|
||||
"""Change visibility of a group."""
|
||||
visible = service.data.get(ATTR_VISIBLE)
|
||||
for group in component.async_extract_from_service(
|
||||
service, expand_group=False):
|
||||
group.async_set_visible(visible)
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SET_VISIBILITY, visibility_service_handler,
|
||||
descriptions[DOMAIN][SERVICE_SET_VISIBILITY],
|
||||
schema=SET_VISIBILITY_SERVICE_SCHEMA)
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_RELOAD, reload_service_handler,
|
||||
descriptions[DOMAIN][SERVICE_RELOAD], schema=RELOAD_SERVICE_SCHEMA)
|
||||
|
@ -212,6 +237,7 @@ class Group(Entity):
|
|||
self.group_off = None
|
||||
self._assumed_state = False
|
||||
self._async_unsub_state_changed = None
|
||||
self._visible = True
|
||||
|
||||
@staticmethod
|
||||
# pylint: disable=too-many-arguments
|
||||
|
@ -268,10 +294,20 @@ class Group(Entity):
|
|||
"""Return the icon of the group."""
|
||||
return self._icon
|
||||
|
||||
@callback
|
||||
def async_set_visible(self, visible):
|
||||
"""Change visibility of the group."""
|
||||
if self._visible != visible:
|
||||
self._visible = visible
|
||||
self.hass.loop.create_task(self.async_update_ha_state())
|
||||
|
||||
@property
|
||||
def hidden(self):
|
||||
"""If group should be hidden or not."""
|
||||
return not self._user_defined or self._view
|
||||
# Visibility from set_visibility service overrides
|
||||
if self._visible:
|
||||
return not self._user_defined or self._view
|
||||
return True
|
||||
|
||||
@property
|
||||
def state_attributes(self):
|
||||
|
|
|
@ -44,6 +44,18 @@ group:
|
|||
description: "Reload group configuration."
|
||||
fields:
|
||||
|
||||
set_visibility:
|
||||
description: Hide or show a group
|
||||
|
||||
fields:
|
||||
entity_id:
|
||||
description: Name(s) of entities to set value
|
||||
example: 'group.travel'
|
||||
|
||||
visible:
|
||||
description: True if group should be shown or False if it should be hidden.
|
||||
example: True
|
||||
|
||||
persistent_notification:
|
||||
create:
|
||||
description: Show a notification in the frontend
|
||||
|
|
|
@ -86,17 +86,18 @@ class EntityComponent(object):
|
|||
discovery.async_listen_platform(
|
||||
self.hass, self.domain, component_platform_discovered)
|
||||
|
||||
def extract_from_service(self, service):
|
||||
def extract_from_service(self, service, expand_group=True):
|
||||
"""Extract all known entities from a service call.
|
||||
|
||||
Will return all entities if no entities specified in call.
|
||||
Will return an empty list if entities specified but unknown.
|
||||
"""
|
||||
return run_callback_threadsafe(
|
||||
self.hass.loop, self.async_extract_from_service, service
|
||||
self.hass.loop, self.async_extract_from_service, service,
|
||||
expand_group
|
||||
).result()
|
||||
|
||||
def async_extract_from_service(self, service):
|
||||
def async_extract_from_service(self, service, expand_group=True):
|
||||
"""Extract all known entities from a service call.
|
||||
|
||||
Will return all entities if no entities specified in call.
|
||||
|
@ -108,7 +109,7 @@ class EntityComponent(object):
|
|||
return list(self.entities.values())
|
||||
|
||||
return [self.entities[entity_id] for entity_id
|
||||
in extract_entity_ids(self.hass, service)
|
||||
in extract_entity_ids(self.hass, service, expand_group)
|
||||
if entity_id in self.entities]
|
||||
|
||||
@asyncio.coroutine
|
||||
|
|
|
@ -94,7 +94,7 @@ def async_call_from_config(hass, config, blocking=False, variables=None,
|
|||
domain, service_name, service_data, blocking)
|
||||
|
||||
|
||||
def extract_entity_ids(hass, service_call):
|
||||
def extract_entity_ids(hass, service_call, expand_group=True):
|
||||
"""Helper method to extract a list of entity ids from a service call.
|
||||
|
||||
Will convert group entity ids to the entity ids it represents.
|
||||
|
@ -109,7 +109,17 @@ def extract_entity_ids(hass, service_call):
|
|||
# Entity ID attr can be a list or a string
|
||||
service_ent_id = service_call.data[ATTR_ENTITY_ID]
|
||||
|
||||
if isinstance(service_ent_id, str):
|
||||
return group.expand_entity_ids(hass, [service_ent_id])
|
||||
if expand_group:
|
||||
|
||||
return [ent_id for ent_id in group.expand_entity_ids(hass, service_ent_id)]
|
||||
if isinstance(service_ent_id, str):
|
||||
return group.expand_entity_ids(hass, [service_ent_id])
|
||||
|
||||
return [ent_id for ent_id in
|
||||
group.expand_entity_ids(hass, service_ent_id)]
|
||||
|
||||
else:
|
||||
|
||||
if isinstance(service_ent_id, str):
|
||||
return [service_ent_id]
|
||||
|
||||
return service_ent_id
|
||||
|
|
|
@ -352,3 +352,23 @@ class TestComponentsGroup(unittest.TestCase):
|
|||
assert self.hass.states.entity_ids() == ['group.light']
|
||||
grp.stop()
|
||||
assert self.hass.states.entity_ids() == []
|
||||
|
||||
def test_changing_group_visibility(self):
|
||||
"""Test that a group can be hidden and shown."""
|
||||
setup_component(self.hass, 'group', {
|
||||
'group': {
|
||||
'test_group': 'hello.world,sensor.happy'
|
||||
}
|
||||
})
|
||||
|
||||
group_entity_id = group.ENTITY_ID_FORMAT.format('test_group')
|
||||
|
||||
# Hide the group
|
||||
group.set_visibility(self.hass, group_entity_id, False)
|
||||
group_state = self.hass.states.get(group_entity_id)
|
||||
self.assertTrue(group_state.attributes.get(ATTR_HIDDEN))
|
||||
|
||||
# Show it again
|
||||
group.set_visibility(self.hass, group_entity_id, True)
|
||||
group_state = self.hass.states.get(group_entity_id)
|
||||
self.assertIsNone(group_state.attributes.get(ATTR_HIDDEN))
|
||||
|
|
|
@ -7,6 +7,7 @@ from unittest.mock import patch, Mock
|
|||
|
||||
import homeassistant.core as ha
|
||||
import homeassistant.loader as loader
|
||||
from homeassistant.components import group
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers import discovery
|
||||
|
@ -205,6 +206,20 @@ class TestHelpersEntityComponent(unittest.TestCase):
|
|||
assert ['test_domain.test_2'] == \
|
||||
[ent.entity_id for ent in component.extract_from_service(call)]
|
||||
|
||||
def test_extract_from_service_no_group_expand(self):
|
||||
"""Test not expanding a group."""
|
||||
component = EntityComponent(_LOGGER, DOMAIN, self.hass)
|
||||
test_group = group.Group.create_group(
|
||||
self.hass, 'test_group', ['light.Ceiling', 'light.Kitchen'])
|
||||
component.add_entities([test_group])
|
||||
|
||||
call = ha.ServiceCall('test', 'service', {
|
||||
'entity_id': ['group.test_group']
|
||||
})
|
||||
|
||||
extracted = component.extract_from_service(call, expand_group=False)
|
||||
self.assertEqual([test_group], extracted)
|
||||
|
||||
def test_setup_loads_platforms(self):
|
||||
"""Test the loading of the platforms."""
|
||||
component_setup = Mock(return_value=True)
|
||||
|
|
|
@ -153,3 +153,6 @@ class TestServiceHelpers(unittest.TestCase):
|
|||
|
||||
self.assertEqual(['light.ceiling', 'light.kitchen'],
|
||||
service.extract_entity_ids(self.hass, call))
|
||||
|
||||
self.assertEqual(['group.test'], service.extract_entity_ids(
|
||||
self.hass, call, expand_group=False))
|
||||
|
|
Loading…
Reference in New Issue