From 5426e5c875b73cb2d89769199ec80774344d0b10 Mon Sep 17 00:00:00 2001 From: Jack Wilsdon Date: Mon, 29 Jan 2018 07:40:00 +0000 Subject: [PATCH] emulated_hue: allow customization within emulated_hue configuration (#11981) * emulated_hue: add entities configuration * emulated_hue: update tests to include new entities attribute --- .../components/emulated_hue/__init__.py | 29 +++++++++- .../components/emulated_hue/hue_api.py | 13 ++--- tests/components/emulated_hue/test_hue_api.py | 57 ++++++++++++------- 3 files changed, 69 insertions(+), 30 deletions(-) diff --git a/homeassistant/components/emulated_hue/__init__.py b/homeassistant/components/emulated_hue/__init__.py index b2206f80766..9fba21b81dc 100644 --- a/homeassistant/components/emulated_hue/__init__.py +++ b/homeassistant/components/emulated_hue/__init__.py @@ -39,6 +39,9 @@ CONF_OFF_MAPS_TO_ON_DOMAINS = 'off_maps_to_on_domains' CONF_EXPOSE_BY_DEFAULT = 'expose_by_default' CONF_EXPOSED_DOMAINS = 'exposed_domains' CONF_TYPE = 'type' +CONF_ENTITIES = 'entities' +CONF_ENTITY_NAME = 'name' +CONF_ENTITY_HIDDEN = 'hidden' TYPE_ALEXA = 'alexa' TYPE_GOOGLE = 'google_home' @@ -52,6 +55,11 @@ DEFAULT_EXPOSED_DOMAINS = [ ] DEFAULT_TYPE = TYPE_GOOGLE +CONFIG_ENTITY_SCHEMA = vol.Schema({ + vol.Optional(CONF_ENTITY_NAME): cv.string, + vol.Optional(CONF_ENTITY_HIDDEN): cv.boolean +}) + CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ vol.Optional(CONF_HOST_IP): cv.string, @@ -63,11 +71,14 @@ CONFIG_SCHEMA = vol.Schema({ vol.Optional(CONF_EXPOSE_BY_DEFAULT): cv.boolean, vol.Optional(CONF_EXPOSED_DOMAINS): cv.ensure_list, vol.Optional(CONF_TYPE, default=DEFAULT_TYPE): - vol.Any(TYPE_ALEXA, TYPE_GOOGLE) + vol.Any(TYPE_ALEXA, TYPE_GOOGLE), + vol.Optional(CONF_ENTITIES): + vol.Schema({cv.entity_id: CONFIG_ENTITY_SCHEMA}) }) }, extra=vol.ALLOW_EXTRA) ATTR_EMULATED_HUE = 'emulated_hue' +ATTR_EMULATED_HUE_NAME = 'emulated_hue_name' ATTR_EMULATED_HUE_HIDDEN = 'emulated_hue_hidden' @@ -183,6 +194,8 @@ class Config(object): self.advertise_port = conf.get( CONF_ADVERTISE_PORT) or self.listen_port + self.entities = conf.get(CONF_ENTITIES, {}) + def entity_id_to_number(self, entity_id): """Get a unique number for the entity id.""" if self.type == TYPE_ALEXA: @@ -215,6 +228,14 @@ class Config(object): assert isinstance(number, str) return self.numbers.get(number) + def get_entity_name(self, entity): + """Get the name of an entity.""" + if entity.entity_id in self.entities and \ + CONF_ENTITY_NAME in self.entities[entity.entity_id]: + return self.entities[entity.entity_id][CONF_ENTITY_NAME] + + return entity.attributes.get(ATTR_EMULATED_HUE_NAME, entity.name) + def is_entity_exposed(self, entity): """Determine if an entity should be exposed on the emulated bridge. @@ -227,6 +248,12 @@ class Config(object): domain = entity.domain.lower() explicit_expose = entity.attributes.get(ATTR_EMULATED_HUE, None) explicit_hidden = entity.attributes.get(ATTR_EMULATED_HUE_HIDDEN, None) + + if entity.entity_id in self.entities and \ + CONF_ENTITY_HIDDEN in self.entities[entity.entity_id]: + explicit_hidden = \ + self.entities[entity.entity_id][CONF_ENTITY_HIDDEN] + if explicit_expose is True or explicit_hidden is False: expose = True elif explicit_expose is False or explicit_hidden is True: diff --git a/homeassistant/components/emulated_hue/hue_api.py b/homeassistant/components/emulated_hue/hue_api.py index 7b98ca7deaa..5d97ef3cea4 100644 --- a/homeassistant/components/emulated_hue/hue_api.py +++ b/homeassistant/components/emulated_hue/hue_api.py @@ -24,9 +24,6 @@ from homeassistant.components.http import HomeAssistantView _LOGGER = logging.getLogger(__name__) -ATTR_EMULATED_HUE = 'emulated_hue' -ATTR_EMULATED_HUE_NAME = 'emulated_hue_name' - HUE_API_STATE_ON = 'on' HUE_API_STATE_BRI = 'bri' @@ -77,7 +74,7 @@ class HueAllLightsStateView(HomeAssistantView): number = self.config.entity_id_to_number(entity.entity_id) json_response[number] = entity_to_json( - entity, state, brightness) + self.config, entity, state, brightness) return self.json(json_response) @@ -110,7 +107,7 @@ class HueOneLightStateView(HomeAssistantView): state, brightness = get_entity_state(self.config, entity) - json_response = entity_to_json(entity, state, brightness) + json_response = entity_to_json(self.config, entity, state, brightness) return self.json(json_response) @@ -344,10 +341,8 @@ def get_entity_state(config, entity): return (final_state, final_brightness) -def entity_to_json(entity, is_on=None, brightness=None): +def entity_to_json(config, entity, is_on=None, brightness=None): """Convert an entity to its Hue bridge JSON representation.""" - name = entity.attributes.get(ATTR_EMULATED_HUE_NAME, entity.name) - return { 'state': { @@ -356,7 +351,7 @@ def entity_to_json(entity, is_on=None, brightness=None): 'reachable': True }, 'type': 'Dimmable light', - 'name': name, + 'name': config.get_entity_name(entity), 'modelid': 'HASS123', 'uniqueid': entity.entity_id, 'swversion': '123' diff --git a/tests/components/emulated_hue/test_hue_api.py b/tests/components/emulated_hue/test_hue_api.py index 383b4f7165d..af07da547b7 100644 --- a/tests/components/emulated_hue/test_hue_api.py +++ b/tests/components/emulated_hue/test_hue_api.py @@ -121,7 +121,14 @@ def hass_hue(loop, hass): def hue_client(loop, hass_hue, test_client): """Create web client for emulated hue api.""" web_app = hass_hue.http.app - config = Config(None, {'type': 'alexa'}) + config = Config(None, { + emulated_hue.CONF_TYPE: emulated_hue.TYPE_ALEXA, + emulated_hue.CONF_ENTITIES: { + 'light.bed_light': { + emulated_hue.CONF_ENTITY_HIDDEN: True + } + } + }) HueUsernameView().register(web_app.router) HueAllLightsStateView(config).register(web_app.router) @@ -145,7 +152,7 @@ def test_discover_lights(hue_client): # Make sure the lights we added to the config are there assert 'light.ceiling_lights' in devices - assert 'light.bed_light' in devices + assert 'light.bed_light' not in devices assert 'script.set_kitchen_light' in devices assert 'light.kitchen_lights' not in devices assert 'media_player.living_room' in devices @@ -186,19 +193,23 @@ def test_get_light_state(hass_hue, hue_client): assert result_json['light.ceiling_lights']['state'][HUE_API_STATE_BRI] == \ 127 - # Turn bedroom light off + # Turn office light off yield from hass_hue.services.async_call( light.DOMAIN, const.SERVICE_TURN_OFF, { - const.ATTR_ENTITY_ID: 'light.bed_light' + const.ATTR_ENTITY_ID: 'light.ceiling_lights' }, blocking=True) - bedroom_json = yield from perform_get_light_state( - hue_client, 'light.bed_light', 200) + office_json = yield from perform_get_light_state( + hue_client, 'light.ceiling_lights', 200) - assert bedroom_json['state'][HUE_API_STATE_ON] is False - assert bedroom_json['state'][HUE_API_STATE_BRI] == 0 + assert office_json['state'][HUE_API_STATE_ON] is False + assert office_json['state'][HUE_API_STATE_BRI] == 0 + + # Make sure bedroom light isn't accessible + yield from perform_get_light_state( + hue_client, 'light.bed_light', 404) # Make sure kitchen light isn't accessible yield from perform_get_light_state( @@ -213,29 +224,35 @@ def test_put_light_state(hass_hue, hue_client): # Turn the bedroom light on first yield from hass_hue.services.async_call( light.DOMAIN, const.SERVICE_TURN_ON, - {const.ATTR_ENTITY_ID: 'light.bed_light', + {const.ATTR_ENTITY_ID: 'light.ceiling_lights', light.ATTR_BRIGHTNESS: 153}, blocking=True) - bed_light = hass_hue.states.get('light.bed_light') - assert bed_light.state == STATE_ON - assert bed_light.attributes[light.ATTR_BRIGHTNESS] == 153 + ceiling_lights = hass_hue.states.get('light.ceiling_lights') + assert ceiling_lights.state == STATE_ON + assert ceiling_lights.attributes[light.ATTR_BRIGHTNESS] == 153 # Go through the API to turn it off - bedroom_result = yield from perform_put_light_state( + ceiling_result = yield from perform_put_light_state( hass_hue, hue_client, - 'light.bed_light', False) + 'light.ceiling_lights', False) - bedroom_result_json = yield from bedroom_result.json() + ceiling_result_json = yield from ceiling_result.json() - assert bedroom_result.status == 200 - assert 'application/json' in bedroom_result.headers['content-type'] + assert ceiling_result.status == 200 + assert 'application/json' in ceiling_result.headers['content-type'] - assert len(bedroom_result_json) == 1 + assert len(ceiling_result_json) == 1 # Check to make sure the state changed - bed_light = hass_hue.states.get('light.bed_light') - assert bed_light.state == STATE_OFF + ceiling_lights = hass_hue.states.get('light.ceiling_lights') + assert ceiling_lights.state == STATE_OFF + + # Make sure we can't change the bedroom light state + bedroom_result = yield from perform_put_light_state( + hass_hue, hue_client, + 'light.bed_light', True) + assert bedroom_result.status == 404 # Make sure we can't change the kitchen light state kitchen_result = yield from perform_put_light_state(