Persist emulated hue IDs (#5435)

This commit is contained in:
Paulus Schoutsen 2017-01-19 21:27:10 -08:00 committed by GitHub
parent dbcad34b47
commit 887a33c7d1
3 changed files with 63 additions and 16 deletions

View File

@ -5,6 +5,7 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/emulated_hue/
"""
import asyncio
import json
import logging
import voluptuous as vol
@ -24,6 +25,8 @@ DOMAIN = 'emulated_hue'
_LOGGER = logging.getLogger(__name__)
NUMBERS_FILE = 'emulated_hue_ids.json'
CONF_HOST_IP = 'host_ip'
CONF_LISTEN_PORT = 'listen_port'
CONF_UPNP_BIND_MULTICAST = 'upnp_bind_multicast'
@ -63,7 +66,7 @@ ATTR_EMULATED_HUE = 'emulated_hue'
def setup(hass, yaml_config):
"""Activate the emulated_hue component."""
config = Config(yaml_config.get(DOMAIN, {}))
config = Config(hass, yaml_config.get(DOMAIN, {}))
server = HomeAssistantWSGI(
hass,
@ -112,10 +115,11 @@ def setup(hass, yaml_config):
class Config(object):
"""Holds configuration variables for the emulated hue bridge."""
def __init__(self, conf):
def __init__(self, hass, conf):
"""Initialize the instance."""
self.hass = hass
self.type = conf.get(CONF_TYPE)
self.numbers = {}
self.numbers = None
self.cached_states = {}
# Get the IP address that will be passed to the Echo during discovery
@ -165,6 +169,9 @@ class Config(object):
if self.type == TYPE_ALEXA:
return entity_id
if self.numbers is None:
self.numbers = self._load_numbers_json()
# Google Home
for number, ent_id in self.numbers.items():
if entity_id == ent_id:
@ -172,6 +179,7 @@ class Config(object):
number = str(len(self.numbers) + 1)
self.numbers[number] = entity_id
self._save_numbers_json()
return number
def number_to_entity_id(self, number):
@ -179,6 +187,9 @@ class Config(object):
if self.type == TYPE_ALEXA:
return number
if self.numbers is None:
self.numbers = self._load_numbers_json()
# Google Home
assert isinstance(number, str)
return self.numbers.get(number)
@ -205,3 +216,26 @@ class Config(object):
domain_exposed_by_default and explicit_expose is not False
return is_default_exposed or explicit_expose
def _load_numbers_json(self):
"""Helper method to load numbers json."""
try:
with open(self.hass.config.path(NUMBERS_FILE),
encoding='utf-8') as fil:
return json.loads(fil.read())
except (OSError, ValueError) as err:
# OSError if file not found or unaccessible/no permissions
# ValueError if could not parse JSON
if not isinstance(err, FileNotFoundError):
_LOGGER.warning('Failed to open %s: %s', NUMBERS_FILE, err)
return {}
def _save_numbers_json(self):
"""Helper method to save numbers json."""
try:
with open(self.hass.config.path(NUMBERS_FILE), 'w',
encoding='utf-8') as fil:
fil.write(json.dumps(self.numbers))
except OSError as err:
# OSError if file write permissions
_LOGGER.warning('Failed to write %s: %s', NUMBERS_FILE, err)

View File

@ -106,7 +106,7 @@ def hass_hue(loop, hass):
def hue_client(loop, hass_hue, test_client):
"""Create web client for emulated hue api."""
web_app = mock_http_component_app(hass_hue)
config = Config({'type': 'alexa'})
config = Config(None, {'type': 'alexa'})
HueUsernameView().register(web_app.router)
HueAllLightsStateView(config).register(web_app.router)

View File

@ -1,31 +1,44 @@
"""Test the Emulated Hue component."""
from unittest.mock import patch
import json
from unittest.mock import patch, Mock, mock_open
from homeassistant.components.emulated_hue import Config, _LOGGER
def test_config_google_home_entity_id_to_number():
"""Test config adheres to the type."""
conf = Config({
conf = Config(Mock(), {
'type': 'google_home'
})
number = conf.entity_id_to_number('light.test')
assert number == '1'
mop = mock_open(read_data=json.dumps({'1': 'light.test2'}))
handle = mop()
number = conf.entity_id_to_number('light.test')
assert number == '1'
with patch('homeassistant.components.emulated_hue.open', mop, create=True):
number = conf.entity_id_to_number('light.test')
assert number == '2'
assert handle.write.call_count == 1
assert json.loads(handle.write.mock_calls[0][1][0]) == {
'1': 'light.test2',
'2': 'light.test',
}
number = conf.entity_id_to_number('light.test2')
assert number == '2'
number = conf.entity_id_to_number('light.test')
assert number == '2'
assert handle.write.call_count == 1
entity_id = conf.number_to_entity_id('1')
assert entity_id == 'light.test'
number = conf.entity_id_to_number('light.test2')
assert number == '1'
assert handle.write.call_count == 1
entity_id = conf.number_to_entity_id('1')
assert entity_id == 'light.test2'
def test_config_alexa_entity_id_to_number():
"""Test config adheres to the type."""
conf = Config({
conf = Config(None, {
'type': 'alexa'
})
@ -45,7 +58,7 @@ def test_config_alexa_entity_id_to_number():
def test_warning_config_google_home_listen_port():
"""Test we warn when non-default port is used for Google Home."""
with patch.object(_LOGGER, 'warning') as mock_warn:
Config({
Config(None, {
'type': 'google_home',
'host_ip': '123.123.123.123',
'listen_port': 8300