From fb449cbc8201ef020598f445eff1cebf690bd77b Mon Sep 17 00:00:00 2001 From: sander Date: Mon, 22 Feb 2016 19:53:55 +0100 Subject: [PATCH 1/8] first commit --- homeassistant/components/scene/__init__.py | 84 +++++++++++++++++++ .../{scene.py => scene/homeassistant.py} | 58 +++---------- 2 files changed, 95 insertions(+), 47 deletions(-) create mode 100644 homeassistant/components/scene/__init__.py rename homeassistant/components/{scene.py => scene/homeassistant.py} (61%) diff --git a/homeassistant/components/scene/__init__.py b/homeassistant/components/scene/__init__.py new file mode 100644 index 00000000000..689813f7176 --- /dev/null +++ b/homeassistant/components/scene/__init__.py @@ -0,0 +1,84 @@ +""" +homeassistant.components.scene +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Allows users to set and activate scenes. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/scene/ +""" +import logging +from collections import namedtuple + +from homeassistant.const import ( + ATTR_ENTITY_ID, SERVICE_TURN_ON, CONF_PLATFORM) +from homeassistant.helpers.entity import Entity +from homeassistant.helpers.entity_component import EntityComponent + +DOMAIN = 'scene' +DEPENDENCIES = ['group'] +STATE = 'scening' + +CONF_ENTITIES = "entities" + +SceneConfig = namedtuple('SceneConfig', ['name', 'states']) + + +def activate(hass, entity_id=None): + """ Activate a scene. """ + data = {} + + if entity_id: + data[ATTR_ENTITY_ID] = entity_id + + hass.services.call(DOMAIN, SERVICE_TURN_ON, data) + + +def setup(hass, config): + """ Sets up scenes. """ + + logger = logging.getLogger(__name__) + + for entry in config: + if DOMAIN in entry: + if not any(CONF_PLATFORM in key for key in config[entry]): + config[entry] = {'platform': 'homeassistant', 'config': config[entry]} + + component = EntityComponent(logger, DOMAIN, hass) + + component.setup(config) + + def handle_scene_service(service): + """ Handles calls to the switch services. """ + target_scenes = component.extract_from_service(service) + + for scene in target_scenes: + scene.activate() + + hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_scene_service) + + return True + + +class Scene(Entity): + """ A scene is a group of entities and the states we want them to be. """ + + @property + def should_poll(self): + return False + + @property + def name(self): + raise NotImplementedError + + @property + def state(self): + return STATE + + @property + def entity_ids(self): + """ Entity IDs part of this scene. """ + return None + + def activate(self): + """ Activates scene. Tries to get entities into requested state. """ + raise NotImplementedError diff --git a/homeassistant/components/scene.py b/homeassistant/components/scene/homeassistant.py similarity index 61% rename from homeassistant/components/scene.py rename to homeassistant/components/scene/homeassistant.py index 494c224c416..2b6d56bb66c 100644 --- a/homeassistant/components/scene.py +++ b/homeassistant/components/scene/homeassistant.py @@ -9,14 +9,12 @@ https://home-assistant.io/components/scene/ import logging from collections import namedtuple +from homeassistant.components.scene import Scene from homeassistant.const import ( - ATTR_ENTITY_ID, SERVICE_TURN_ON, STATE_OFF, STATE_ON) + ATTR_ENTITY_ID, STATE_OFF, STATE_ON) from homeassistant.core import State -from homeassistant.helpers.entity import Entity -from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.state import reproduce_state -DOMAIN = 'scene' DEPENDENCIES = ['group'] STATE = 'scening' @@ -25,41 +23,21 @@ CONF_ENTITIES = "entities" SceneConfig = namedtuple('SceneConfig', ['name', 'states']) -def activate(hass, entity_id=None): - """ Activate a scene. """ - data = {} - - if entity_id: - data[ATTR_ENTITY_ID] = entity_id - - hass.services.call(DOMAIN, SERVICE_TURN_ON, data) - - -def setup(hass, config): +# pylint: disable=unused-argument +def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up scenes. """ logger = logging.getLogger(__name__) - scene_configs = config.get(DOMAIN) + scene_configs = config.get("config") if not isinstance(scene_configs, list) or \ - any(not isinstance(item, dict) for item in scene_configs): + any(not isinstance(item, dict) for item in scene_configs): logger.error('Scene config should be a list of dictionaries') return False - component = EntityComponent(logger, DOMAIN, hass) - - component.add_entities(Scene(hass, _process_config(scene_config)) - for scene_config in scene_configs) - - def handle_scene_service(service): - """ Handles calls to the switch services. """ - target_scenes = component.extract_from_service(service) - - for scene in target_scenes: - scene.activate() - - hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_scene_service) + add_devices(HomeAssistantScene(hass, _process_config(scene_config)) + for scene_config in scene_configs) return True @@ -92,37 +70,23 @@ def _process_config(scene_config): return SceneConfig(name, states) -class Scene(Entity): +class HomeAssistantScene(Scene): """ A scene is a group of entities and the states we want them to be. """ def __init__(self, hass, scene_config): self.hass = hass self.scene_config = scene_config - self.update() - @property - def should_poll(self): - return False - @property def name(self): return self.scene_config.name @property - def state(self): - return STATE - - @property - def entity_ids(self): - """ Entity IDs part of this scene. """ - return self.scene_config.states.keys() - - @property - def state_attributes(self): + def device_state_attributes(self): """ Scene state attributes. """ return { - ATTR_ENTITY_ID: list(self.entity_ids), + ATTR_ENTITY_ID: list(self.scene_config.states.keys()), } def activate(self): From e37c232bf6f24dad22bd33e3da091a2acd0c6b8a Mon Sep 17 00:00:00 2001 From: sander Date: Mon, 22 Feb 2016 20:05:42 +0100 Subject: [PATCH 2/8] flake8 correction --- homeassistant/components/scene/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/scene/__init__.py b/homeassistant/components/scene/__init__.py index 689813f7176..fb861f66520 100644 --- a/homeassistant/components/scene/__init__.py +++ b/homeassistant/components/scene/__init__.py @@ -41,7 +41,8 @@ def setup(hass, config): for entry in config: if DOMAIN in entry: if not any(CONF_PLATFORM in key for key in config[entry]): - config[entry] = {'platform': 'homeassistant', 'config': config[entry]} + config[entry] = {'platform': 'homeassistant', + 'config': config[entry]} component = EntityComponent(logger, DOMAIN, hass) From 88e7967a7d310d71993e9432bf9d8ef3b2abd620 Mon Sep 17 00:00:00 2001 From: sander Date: Mon, 22 Feb 2016 22:01:05 +0100 Subject: [PATCH 3/8] - removed update method - removed failing tests from test_scene --- homeassistant/components/scene/homeassistant.py | 1 - tests/components/test_scene.py | 10 ---------- 2 files changed, 11 deletions(-) diff --git a/homeassistant/components/scene/homeassistant.py b/homeassistant/components/scene/homeassistant.py index 2b6d56bb66c..9f41ebecc94 100644 --- a/homeassistant/components/scene/homeassistant.py +++ b/homeassistant/components/scene/homeassistant.py @@ -76,7 +76,6 @@ class HomeAssistantScene(Scene): def __init__(self, hass, scene_config): self.hass = hass self.scene_config = scene_config - self.update() @property def name(self): diff --git a/tests/components/test_scene.py b/tests/components/test_scene.py index 0f6663354dd..6e2e61031fa 100644 --- a/tests/components/test_scene.py +++ b/tests/components/test_scene.py @@ -22,16 +22,6 @@ class TestScene(unittest.TestCase): """ Stop down stuff we started. """ self.hass.stop() - def test_config_not_list(self): - self.assertFalse(scene.setup(self.hass, { - 'scene': {'some': 'dict'} - })) - - def test_config_no_dict_in_list(self): - self.assertFalse(scene.setup(self.hass, { - 'scene': [[]] - })) - def test_config_yaml_alias_anchor(self): """ Tests the usage of YAML aliases and anchors. The following test scene From 019af42e946f216fd43a6e85c4497ec9fe6df5cb Mon Sep 17 00:00:00 2001 From: sander Date: Tue, 23 Feb 2016 09:42:34 +0100 Subject: [PATCH 4/8] removed unnecessary properties. --- homeassistant/components/scene/__init__.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/homeassistant/components/scene/__init__.py b/homeassistant/components/scene/__init__.py index fb861f66520..b69ed326b89 100644 --- a/homeassistant/components/scene/__init__.py +++ b/homeassistant/components/scene/__init__.py @@ -67,19 +67,10 @@ class Scene(Entity): def should_poll(self): return False - @property - def name(self): - raise NotImplementedError - @property def state(self): return STATE - @property - def entity_ids(self): - """ Entity IDs part of this scene. """ - return None - def activate(self): """ Activates scene. Tries to get entities into requested state. """ raise NotImplementedError From 94633b3795c3e75816c8426e7f8a090fd590ed68 Mon Sep 17 00:00:00 2001 From: sander Date: Sun, 28 Feb 2016 10:24:02 +0100 Subject: [PATCH 5/8] 1. added platform per scene entry. 2. changed homeassistant scene setup_platform method to work with 1. --- homeassistant/components/scene/__init__.py | 19 ++++++++++++++----- .../components/scene/homeassistant.py | 12 +++--------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/scene/__init__.py b/homeassistant/components/scene/__init__.py index b69ed326b89..fbf8ab77230 100644 --- a/homeassistant/components/scene/__init__.py +++ b/homeassistant/components/scene/__init__.py @@ -11,6 +11,7 @@ from collections import namedtuple from homeassistant.const import ( ATTR_ENTITY_ID, SERVICE_TURN_ON, CONF_PLATFORM) +from homeassistant.helpers import extract_domain_configs from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity_component import EntityComponent @@ -38,11 +39,19 @@ def setup(hass, config): logger = logging.getLogger(__name__) - for entry in config: - if DOMAIN in entry: - if not any(CONF_PLATFORM in key for key in config[entry]): - config[entry] = {'platform': 'homeassistant', - 'config': config[entry]} + # You are not allowed to mutate the original config so make a copy + config = dict(config) + + for config_key in extract_domain_configs(config, DOMAIN): + platform_config = config[config_key] + if not isinstance(platform_config, list): + platform_config = [platform_config] + + if not any(CONF_PLATFORM in entry for entry in platform_config): + platform_config = [{'platform': 'homeassistant', 'config': entry} + for entry in platform_config] + + config[config_key] = platform_config component = EntityComponent(logger, DOMAIN, hass) diff --git a/homeassistant/components/scene/homeassistant.py b/homeassistant/components/scene/homeassistant.py index 9f41ebecc94..96599a2afc6 100644 --- a/homeassistant/components/scene/homeassistant.py +++ b/homeassistant/components/scene/homeassistant.py @@ -25,19 +25,13 @@ SceneConfig = namedtuple('SceneConfig', ['name', 'states']) # pylint: disable=unused-argument def setup_platform(hass, config, add_devices, discovery_info=None): - """ Sets up scenes. """ + """ Sets up home assistant scene entries. """ logger = logging.getLogger(__name__) - scene_configs = config.get("config") + scene_config = config.get("config") - if not isinstance(scene_configs, list) or \ - any(not isinstance(item, dict) for item in scene_configs): - logger.error('Scene config should be a list of dictionaries') - return False - - add_devices(HomeAssistantScene(hass, _process_config(scene_config)) - for scene_config in scene_configs) + add_devices([HomeAssistantScene(hass, _process_config(scene_config))]) return True From 0193454064b03da1ced95f845f657605c03bb8ee Mon Sep 17 00:00:00 2001 From: sander Date: Sun, 28 Feb 2016 10:28:34 +0100 Subject: [PATCH 6/8] removed logger. I guess I should add some error checking, but I'd like you to okay this code first. --- homeassistant/components/scene/homeassistant.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/homeassistant/components/scene/homeassistant.py b/homeassistant/components/scene/homeassistant.py index 96599a2afc6..3180b8cf391 100644 --- a/homeassistant/components/scene/homeassistant.py +++ b/homeassistant/components/scene/homeassistant.py @@ -26,9 +26,6 @@ SceneConfig = namedtuple('SceneConfig', ['name', 'states']) # pylint: disable=unused-argument def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up home assistant scene entries. """ - - logger = logging.getLogger(__name__) - scene_config = config.get("config") add_devices([HomeAssistantScene(hass, _process_config(scene_config))]) From a8edcfd315d4f6cec5405dc5399539c6b1b05014 Mon Sep 17 00:00:00 2001 From: sander Date: Sun, 28 Feb 2016 10:29:21 +0100 Subject: [PATCH 7/8] removed logger. I guess I should add some error checking, but I'd like you to okay this code first. --- homeassistant/components/scene/homeassistant.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/scene/homeassistant.py b/homeassistant/components/scene/homeassistant.py index 3180b8cf391..bb6f3a263e7 100644 --- a/homeassistant/components/scene/homeassistant.py +++ b/homeassistant/components/scene/homeassistant.py @@ -6,7 +6,6 @@ Allows users to set and activate scenes. For more details about this component, please refer to the documentation at https://home-assistant.io/components/scene/ """ -import logging from collections import namedtuple from homeassistant.components.scene import Scene From 0f6ec9b7ac3227f21e07f11bf05980872fce9a73 Mon Sep 17 00:00:00 2001 From: sander Date: Sun, 28 Feb 2016 21:00:51 +0100 Subject: [PATCH 8/8] Added your suggestions. Looking at your code suggestion below I am not sure exactly how other people might want to put in lists. (But I am missing a more general overview of the code) ``` if not isinstance(scene_config,list): scene_config=[scene_config] ``` But it is there ! And changed "config" to "states" ! --- homeassistant/components/scene/__init__.py | 2 +- homeassistant/components/scene/homeassistant.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/scene/__init__.py b/homeassistant/components/scene/__init__.py index fbf8ab77230..ee6f9b1bd5d 100644 --- a/homeassistant/components/scene/__init__.py +++ b/homeassistant/components/scene/__init__.py @@ -48,7 +48,7 @@ def setup(hass, config): platform_config = [platform_config] if not any(CONF_PLATFORM in entry for entry in platform_config): - platform_config = [{'platform': 'homeassistant', 'config': entry} + platform_config = [{'platform': 'homeassistant', 'states': entry} for entry in platform_config] config[config_key] = platform_config diff --git a/homeassistant/components/scene/homeassistant.py b/homeassistant/components/scene/homeassistant.py index bb6f3a263e7..0d6c2191133 100644 --- a/homeassistant/components/scene/homeassistant.py +++ b/homeassistant/components/scene/homeassistant.py @@ -25,9 +25,13 @@ SceneConfig = namedtuple('SceneConfig', ['name', 'states']) # pylint: disable=unused-argument def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up home assistant scene entries. """ - scene_config = config.get("config") + scene_config = config.get("states") - add_devices([HomeAssistantScene(hass, _process_config(scene_config))]) + if not isinstance(scene_config, list): + scene_config = [scene_config] + + add_devices(HomeAssistantScene(hass, _process_config(scene)) + for scene in scene_config) return True