Use API to discover Hue if no bridges specified (#11909)

* Use API to discover Hue if no bridges specified

* hide config file
pull/11833/head^2
Paulus Schoutsen 2018-01-25 05:55:14 -08:00 committed by Pascal Vizeli
parent 9123dfce6d
commit 3d9ff372fc
3 changed files with 58 additions and 28 deletions

View File

@ -9,6 +9,7 @@ import logging
import os
import socket
import requests
import voluptuous as vol
from homeassistant.components.discovery import SERVICE_HUE
@ -22,6 +23,7 @@ _LOGGER = logging.getLogger(__name__)
DOMAIN = "hue"
SERVICE_HUE_SCENE = "hue_activate_scene"
API_NUPNP = 'https://www.meethue.com/api/nupnp'
CONF_BRIDGES = "bridges"
@ -49,7 +51,7 @@ BRIDGE_CONFIG_SCHEMA = vol.Schema([{
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Optional(CONF_BRIDGES, default=[]): BRIDGE_CONFIG_SCHEMA,
vol.Optional(CONF_BRIDGES): BRIDGE_CONFIG_SCHEMA,
}),
}, extra=vol.ALLOW_EXTRA)
@ -69,9 +71,9 @@ Press the button on the bridge to register Philips Hue with Home Assistant.
def setup(hass, config):
"""Set up the Hue platform."""
config = config.get(DOMAIN)
if config is None:
config = {}
conf = config.get(DOMAIN)
if conf is None:
conf = {}
if DOMAIN not in hass.data:
hass.data[DOMAIN] = {}
@ -82,7 +84,21 @@ def setup(hass, config):
lambda service, discovery_info:
bridge_discovered(hass, service, discovery_info))
bridges = config.get(CONF_BRIDGES, [])
# User has configured bridges
if CONF_BRIDGES in conf:
bridges = conf[CONF_BRIDGES]
# Component is part of config but no bridges specified, discover.
elif DOMAIN in config:
# discover from nupnp
hosts = requests.get(API_NUPNP).json()
bridges = [{
CONF_HOST: entry['internalipaddress'],
CONF_FILENAME: '.hue_{}.conf'.format(entry['id']),
} for entry in hosts]
else:
# Component not specified in config, we're loaded via discovery
bridges = []
for bridge in bridges:
filename = bridge.get(CONF_FILENAME)
allow_unreachable = bridge.get(CONF_ALLOW_UNREACHABLE)

View File

@ -542,10 +542,8 @@ class MockDependency:
self.root = root
self.submodules = args
def __call__(self, func):
"""Apply decorator."""
from unittest.mock import MagicMock, patch
def __enter__(self):
"""Start mocking."""
def resolve(mock, path):
"""Resolve a mock."""
if not path:
@ -553,16 +551,27 @@ class MockDependency:
return resolve(getattr(mock, path[0]), path[1:])
base = MagicMock()
to_mock = {
"{}.{}".format(self.root, tom): resolve(base, tom.split('.'))
for tom in self.submodules
}
to_mock[self.root] = base
self.patcher = patch.dict('sys.modules', to_mock)
self.patcher.start()
return base
def __exit__(self, *exc):
"""Stop mocking."""
self.patcher.stop()
return False
def __call__(self, func):
"""Apply decorator."""
def run_mocked(*args, **kwargs):
"""Run with mocked dependencies."""
base = MagicMock()
to_mock = {
"{}.{}".format(self.root, tom): resolve(base, tom.split('.'))
for tom in self.submodules
}
to_mock[self.root] = base
with patch.dict('sys.modules', to_mock):
with self as base:
args = list(args) + [base]
func(*args, **kwargs)

View File

@ -1,12 +1,12 @@
"""Generic Philips Hue component tests."""
import asyncio
import logging
import unittest
from unittest.mock import call, MagicMock, patch
from homeassistant.components import configurator, hue
from homeassistant.const import CONF_FILENAME, CONF_HOST
from homeassistant.setup import setup_component
from homeassistant.setup import setup_component, async_setup_component
from tests.common import (
assert_setup_component, get_test_home_assistant, get_test_config_dir,
@ -38,15 +38,6 @@ class TestSetup(unittest.TestCase):
mock_phue.Bridge.assert_not_called()
self.assertEquals({}, self.hass.data[hue.DOMAIN])
@MockDependency('phue')
def test_setup_no_host(self, mock_phue):
"""No host specified in any way."""
with assert_setup_component(1):
self.assertTrue(setup_component(
self.hass, hue.DOMAIN, {hue.DOMAIN: {}}))
mock_phue.Bridge.assert_not_called()
self.assertEquals({}, self.hass.data[hue.DOMAIN])
@MockDependency('phue')
def test_setup_with_host(self, mock_phue):
"""Host specified in the config file."""
@ -400,3 +391,17 @@ class TestHueBridge(unittest.TestCase):
{hue.ATTR_GROUP_NAME: 'group', hue.ATTR_SCENE_NAME: 'scene'},
blocking=True)
bridge.bridge.run_scene.assert_called_once_with('group', 'scene')
@asyncio.coroutine
def test_setup_no_host(hass, requests_mock):
"""No host specified in any way."""
requests_mock.get(hue.API_NUPNP, json=[])
with MockDependency('phue') as mock_phue:
result = yield from async_setup_component(
hass, hue.DOMAIN, {hue.DOMAIN: {}})
assert result
mock_phue.Bridge.assert_not_called()
assert hass.data[hue.DOMAIN] == {}