emulated_hue: allow customization within emulated_hue configuration (#11981)

* emulated_hue: add entities configuration

* emulated_hue: update tests to include new entities attribute
This commit is contained in:
Jack Wilsdon 2018-01-29 07:40:00 +00:00 committed by Paulus Schoutsen
parent 766875f702
commit 5426e5c875
3 changed files with 69 additions and 30 deletions

View File

@ -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:

View File

@ -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'

View File

@ -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(