1
0
mirror of https://github.com/home-assistant/core.git synced 2025-12-24 21:06:19 +00:00

Support multiple Hue bridges with lights of the same id (#11259)

* Improve support for multiple Hue bridges with lights that have the same id.

The old code pre-refactoring kept a per-bridge list of lights in a closure; my refactoring moved that to hass.data, which is convenient but caused them to conflict with each other.

Fixes #11183

* Update test_hue.py
This commit is contained in:
Andrea Campi
2017-12-24 00:12:54 +00:00
committed by Pascal Vizeli
parent 8683d75aa1
commit 8c303bf48c
3 changed files with 113 additions and 63 deletions

View File

@@ -36,27 +36,45 @@ class TestSetup(unittest.TestCase):
self.mock_lights = []
self.mock_groups = []
self.mock_add_devices = MagicMock()
hue_light.setup_data(self.hass)
def setup_mocks_for_process_lights(self):
"""Set up all mocks for process_lights tests."""
self.mock_bridge = MagicMock()
self.mock_bridge = self.create_mock_bridge('host')
self.mock_api = MagicMock()
self.mock_api.get.return_value = {}
self.mock_bridge.get_api.return_value = self.mock_api
self.mock_bridge_type = MagicMock()
hue_light.setup_data(self.hass)
def setup_mocks_for_process_groups(self):
"""Set up all mocks for process_groups tests."""
self.mock_bridge = MagicMock()
self.mock_bridge = self.create_mock_bridge('host')
self.mock_bridge.get_group.return_value = {
'name': 'Group 0', 'state': {'any_on': True}}
self.mock_api = MagicMock()
self.mock_api.get.return_value = {}
self.mock_bridge.get_api.return_value = self.mock_api
self.mock_bridge_type = MagicMock()
hue_light.setup_data(self.hass)
def create_mock_bridge(self, host, allow_hue_groups=True):
"""Return a mock HueBridge with reasonable defaults."""
mock_bridge = MagicMock()
mock_bridge.host = host
mock_bridge.allow_hue_groups = allow_hue_groups
mock_bridge.lights = {}
mock_bridge.lightgroups = {}
return mock_bridge
def create_mock_lights(self, lights):
"""Return a dict suitable for mocking api.get('lights')."""
mock_bridge_lights = lights
for light_id, info in mock_bridge_lights.items():
if 'state' not in info:
info['state'] = {'on': False}
return mock_bridge_lights
def test_setup_platform_no_discovery_info(self):
"""Test setup_platform without discovery info."""
@@ -211,6 +229,70 @@ class TestSetup(unittest.TestCase):
self.mock_add_devices.assert_called_once_with(
self.mock_lights)
@MockDependency('phue')
def test_update_lights_with_two_bridges(self, mock_phue):
"""Test the update_lights function with two bridges."""
self.setup_mocks_for_update_lights()
mock_bridge_one = self.create_mock_bridge('one', False)
mock_bridge_one_lights = self.create_mock_lights(
{1: {'name': 'b1l1'}, 2: {'name': 'b1l2'}})
mock_bridge_two = self.create_mock_bridge('two', False)
mock_bridge_two_lights = self.create_mock_lights(
{1: {'name': 'b2l1'}, 3: {'name': 'b2l3'}})
with patch('homeassistant.components.light.hue.get_bridge_type',
return_value=self.mock_bridge_type):
with patch('homeassistant.components.light.hue.HueLight.'
'schedule_update_ha_state'):
mock_api = MagicMock()
mock_api.get.return_value = mock_bridge_one_lights
with patch.object(mock_bridge_one, 'get_api',
return_value=mock_api):
hue_light.unthrottled_update_lights(
self.hass, mock_bridge_one, self.mock_add_devices)
mock_api = MagicMock()
mock_api.get.return_value = mock_bridge_two_lights
with patch.object(mock_bridge_two, 'get_api',
return_value=mock_api):
hue_light.unthrottled_update_lights(
self.hass, mock_bridge_two, self.mock_add_devices)
self.assertEquals(sorted(mock_bridge_one.lights.keys()), [1, 2])
self.assertEquals(sorted(mock_bridge_two.lights.keys()), [1, 3])
self.assertEquals(len(self.mock_add_devices.mock_calls), 2)
# first call
name, args, kwargs = self.mock_add_devices.mock_calls[0]
self.assertEquals(len(args), 1)
self.assertEquals(len(kwargs), 0)
# one argument, a list of lights in bridge one; each of them is an
# object of type HueLight so we can't straight up compare them
lights = args[0]
self.assertEquals(
lights[0].unique_id,
'{}.b1l1.Light.1'.format(hue_light.HueLight))
self.assertEquals(
lights[1].unique_id,
'{}.b1l2.Light.2'.format(hue_light.HueLight))
# second call works the same
name, args, kwargs = self.mock_add_devices.mock_calls[1]
self.assertEquals(len(args), 1)
self.assertEquals(len(kwargs), 0)
lights = args[0]
self.assertEquals(
lights[0].unique_id,
'{}.b2l1.Light.1'.format(hue_light.HueLight))
self.assertEquals(
lights[1].unique_id,
'{}.b2l3.Light.3'.format(hue_light.HueLight))
def test_process_lights_api_error(self):
"""Test the process_lights function when the bridge errors out."""
self.setup_mocks_for_process_lights()
@@ -221,9 +303,7 @@ class TestSetup(unittest.TestCase):
None)
self.assertEquals([], ret)
self.assertEquals(
{},
self.hass.data[hue_light.DATA_KEY][hue_light.DATA_LIGHTS])
self.assertEquals(self.mock_bridge.lights, {})
def test_process_lights_no_lights(self):
"""Test the process_lights function when bridge returns no lights."""
@@ -234,9 +314,7 @@ class TestSetup(unittest.TestCase):
None)
self.assertEquals([], ret)
self.assertEquals(
{},
self.hass.data[hue_light.DATA_KEY][hue_light.DATA_LIGHTS])
self.assertEquals(self.mock_bridge.lights, {})
@patch('homeassistant.components.light.hue.HueLight')
def test_process_lights_some_lights(self, mock_hue_light):
@@ -260,9 +338,7 @@ class TestSetup(unittest.TestCase):
self.mock_bridge_type, self.mock_bridge.allow_unreachable,
self.mock_bridge.allow_in_emulated_hue),
])
self.assertEquals(
len(self.hass.data[hue_light.DATA_KEY][hue_light.DATA_LIGHTS]),
2)
self.assertEquals(len(self.mock_bridge.lights), 2)
@patch('homeassistant.components.light.hue.HueLight')
def test_process_lights_new_light(self, mock_hue_light):
@@ -274,8 +350,7 @@ class TestSetup(unittest.TestCase):
self.setup_mocks_for_process_lights()
self.mock_api.get.return_value = {
1: {'state': 'on'}, 2: {'state': 'off'}}
self.hass.data[
hue_light.DATA_KEY][hue_light.DATA_LIGHTS][1] = MagicMock()
self.mock_bridge.lights = {1: MagicMock()}
ret = hue_light.process_lights(
self.hass, self.mock_api, self.mock_bridge, self.mock_bridge_type,
@@ -288,11 +363,9 @@ class TestSetup(unittest.TestCase):
self.mock_bridge_type, self.mock_bridge.allow_unreachable,
self.mock_bridge.allow_in_emulated_hue),
])
self.hass.data[hue_light.DATA_KEY][hue_light.DATA_LIGHTS][
1].schedule_update_ha_state.assert_called_once_with()
self.assertEquals(
len(self.hass.data[hue_light.DATA_KEY][hue_light.DATA_LIGHTS]),
2)
self.assertEquals(len(self.mock_bridge.lights), 2)
self.mock_bridge.lights[1]\
.schedule_update_ha_state.assert_called_once_with()
def test_process_groups_api_error(self):
"""Test the process_groups function when the bridge errors out."""
@@ -304,9 +377,7 @@ class TestSetup(unittest.TestCase):
None)
self.assertEquals([], ret)
self.assertEquals(
{},
self.hass.data[hue_light.DATA_KEY][hue_light.DATA_LIGHTGROUPS])
self.assertEquals(self.mock_bridge.lightgroups, {})
def test_process_groups_no_state(self):
"""Test the process_groups function when bridge returns no status."""
@@ -318,9 +389,7 @@ class TestSetup(unittest.TestCase):
None)
self.assertEquals([], ret)
self.assertEquals(
{},
self.hass.data[hue_light.DATA_KEY][hue_light.DATA_LIGHTGROUPS])
self.assertEquals(self.mock_bridge.lightgroups, {})
@patch('homeassistant.components.light.hue.HueLight')
def test_process_groups_some_groups(self, mock_hue_light):
@@ -344,10 +413,7 @@ class TestSetup(unittest.TestCase):
self.mock_bridge_type, self.mock_bridge.allow_unreachable,
self.mock_bridge.allow_in_emulated_hue, True),
])
self.assertEquals(
len(self.hass.data[
hue_light.DATA_KEY][hue_light.DATA_LIGHTGROUPS]),
2)
self.assertEquals(len(self.mock_bridge.lightgroups), 2)
@patch('homeassistant.components.light.hue.HueLight')
def test_process_groups_new_group(self, mock_hue_light):
@@ -359,8 +425,7 @@ class TestSetup(unittest.TestCase):
self.setup_mocks_for_process_groups()
self.mock_api.get.return_value = {
1: {'state': 'on'}, 2: {'state': 'off'}}
self.hass.data[
hue_light.DATA_KEY][hue_light.DATA_LIGHTGROUPS][1] = MagicMock()
self.mock_bridge.lightgroups = {1: MagicMock()}
ret = hue_light.process_groups(
self.hass, self.mock_api, self.mock_bridge, self.mock_bridge_type,
@@ -373,12 +438,9 @@ class TestSetup(unittest.TestCase):
self.mock_bridge_type, self.mock_bridge.allow_unreachable,
self.mock_bridge.allow_in_emulated_hue, True),
])
self.hass.data[hue_light.DATA_KEY][hue_light.DATA_LIGHTGROUPS][
1].schedule_update_ha_state.assert_called_once_with()
self.assertEquals(
len(self.hass.data[
hue_light.DATA_KEY][hue_light.DATA_LIGHTGROUPS]),
2)
self.assertEquals(len(self.mock_bridge.lightgroups), 2)
self.mock_bridge.lightgroups[1]\
.schedule_update_ha_state.assert_called_once_with()
class TestHueLight(unittest.TestCase):