Try to fix crashes after Hue refactoring (#11270)

* Try to fix crashes after Hue refactoring

Refs #11183

* Fix usage of dispatcher_send via helper.

* Address review feedback.
This commit is contained in:
Andrea Campi 2018-01-10 07:05:04 +00:00 committed by Martin Hjelmare
parent 3cba09c6f6
commit 4dda842b16
3 changed files with 202 additions and 130 deletions

View File

@ -152,6 +152,7 @@ class HueBridge(object):
allow_in_emulated_hue=True, allow_hue_groups=True): allow_in_emulated_hue=True, allow_hue_groups=True):
"""Initialize the system.""" """Initialize the system."""
self.host = host self.host = host
self.bridge_id = socket.gethostbyname(host)
self.hass = hass self.hass = hass
self.filename = filename self.filename = filename
self.allow_unreachable = allow_unreachable self.allow_unreachable = allow_unreachable
@ -165,7 +166,7 @@ class HueBridge(object):
self.configured = False self.configured = False
self.config_request_id = None self.config_request_id = None
hass.data[DOMAIN][socket.gethostbyname(host)] = self hass.data[DOMAIN][self.bridge_id] = self
def setup(self): def setup(self):
"""Set up a phue bridge based on host parameter.""" """Set up a phue bridge based on host parameter."""
@ -196,7 +197,7 @@ class HueBridge(object):
discovery.load_platform( discovery.load_platform(
self.hass, 'light', DOMAIN, self.hass, 'light', DOMAIN,
{'bridge_id': socket.gethostbyname(self.host)}) {'bridge_id': self.bridge_id})
# create a service for calling run_scene directly on the bridge, # create a service for calling run_scene directly on the bridge,
# used to simplify automation rules. # used to simplify automation rules.

View File

@ -4,6 +4,7 @@ This component provides light support for the Philips Hue system.
For more details about this platform, please refer to the documentation at For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/light.hue/ https://home-assistant.io/components/light.hue/
""" """
import asyncio
from datetime import timedelta from datetime import timedelta
import logging import logging
import random import random
@ -14,9 +15,6 @@ import voluptuous as vol
import homeassistant.components.hue as hue import homeassistant.components.hue as hue
import homeassistant.util as util
from homeassistant.util import yaml
import homeassistant.util.color as color_util
from homeassistant.components.light import ( from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_EFFECT, ATTR_FLASH, ATTR_RGB_COLOR, ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_EFFECT, ATTR_FLASH, ATTR_RGB_COLOR,
ATTR_TRANSITION, ATTR_XY_COLOR, EFFECT_COLORLOOP, EFFECT_RANDOM, ATTR_TRANSITION, ATTR_XY_COLOR, EFFECT_COLORLOOP, EFFECT_RANDOM,
@ -24,8 +22,10 @@ from homeassistant.components.light import (
SUPPORT_EFFECT, SUPPORT_FLASH, SUPPORT_RGB_COLOR, SUPPORT_TRANSITION, SUPPORT_EFFECT, SUPPORT_FLASH, SUPPORT_RGB_COLOR, SUPPORT_TRANSITION,
SUPPORT_XY_COLOR, Light, PLATFORM_SCHEMA) SUPPORT_XY_COLOR, Light, PLATFORM_SCHEMA)
from homeassistant.const import CONF_FILENAME, CONF_HOST, DEVICE_DEFAULT_NAME from homeassistant.const import CONF_FILENAME, CONF_HOST, DEVICE_DEFAULT_NAME
from homeassistant.components.emulated_hue import ATTR_EMULATED_HUE_HIDDEN
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
import homeassistant.util as util
from homeassistant.util import yaml
import homeassistant.util.color as color_util
DEPENDENCIES = ['hue'] DEPENDENCIES = ['hue']
@ -49,6 +49,7 @@ SUPPORT_HUE = {
'Color temperature light': SUPPORT_HUE_COLOR_TEMP 'Color temperature light': SUPPORT_HUE_COLOR_TEMP
} }
ATTR_EMULATED_HUE_HIDDEN = 'emulated_hue_hidden'
ATTR_IS_HUE_GROUP = 'is_hue_group' ATTR_IS_HUE_GROUP = 'is_hue_group'
# Legacy configuration, will be removed in 0.60 # Legacy configuration, will be removed in 0.60
@ -83,6 +84,8 @@ This configuration is deprecated, please check the
information. information.
""" """
SIGNAL_CALLBACK = 'hue_light_callback_{}_{}'
def setup_platform(hass, config, add_devices, discovery_info=None): def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Hue lights.""" """Set up the Hue lights."""
@ -163,7 +166,10 @@ def process_lights(hass, api, bridge, update_lights_cb):
new_lights.append(bridge.lights[light_id]) new_lights.append(bridge.lights[light_id])
else: else:
bridge.lights[light_id].info = info bridge.lights[light_id].info = info
bridge.lights[light_id].schedule_update_ha_state() hass.helpers.dispatcher.dispatcher_send(
SIGNAL_CALLBACK.format(
bridge.bridge_id,
bridge.lights[light_id].light_id))
return new_lights return new_lights
@ -193,7 +199,10 @@ def process_groups(hass, api, bridge, update_lights_cb):
new_lights.append(bridge.lightgroups[lightgroup_id]) new_lights.append(bridge.lightgroups[lightgroup_id])
else: else:
bridge.lightgroups[lightgroup_id].info = info bridge.lightgroups[lightgroup_id].info = info
bridge.lightgroups[lightgroup_id].schedule_update_ha_state() hass.helpers.dispatcher.dispatcher_send(
SIGNAL_CALLBACK.format(
bridge.bridge_id,
bridge.lightgroups[lightgroup_id].light_id))
return new_lights return new_lights
@ -366,3 +375,11 @@ class HueLight(Light):
if self.is_group: if self.is_group:
attributes[ATTR_IS_HUE_GROUP] = self.is_group attributes[ATTR_IS_HUE_GROUP] = self.is_group
return attributes return attributes
@asyncio.coroutine
def async_added_to_hass(self):
"""Register update callback."""
dev_id = self.bridge.bridge_id, self.light_id
self.hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_CALLBACK.format(*dev_id),
self.async_schedule_update_ha_state)

View File

@ -12,6 +12,8 @@ from tests.common import get_test_home_assistant, MockDependency
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
HUE_LIGHT_NS = 'homeassistant.components.light.hue.'
class TestSetup(unittest.TestCase): class TestSetup(unittest.TestCase):
"""Test the Hue light platform.""" """Test the Hue light platform."""
@ -29,11 +31,10 @@ class TestSetup(unittest.TestCase):
def setup_mocks_for_update_lights(self): def setup_mocks_for_update_lights(self):
"""Set up all mocks for update_lights tests.""" """Set up all mocks for update_lights tests."""
self.mock_bridge = MagicMock() self.mock_bridge = MagicMock()
self.mock_bridge.bridge_id = 'bridge-id'
self.mock_bridge.allow_hue_groups = False self.mock_bridge.allow_hue_groups = False
self.mock_api = MagicMock() self.mock_api = MagicMock()
self.mock_bridge.get_api.return_value = self.mock_api self.mock_bridge.get_api.return_value = self.mock_api
self.mock_lights = []
self.mock_groups = []
self.mock_add_devices = MagicMock() self.mock_add_devices = MagicMock()
def setup_mocks_for_process_lights(self): def setup_mocks_for_process_lights(self):
@ -56,6 +57,7 @@ class TestSetup(unittest.TestCase):
def create_mock_bridge(self, host, allow_hue_groups=True): def create_mock_bridge(self, host, allow_hue_groups=True):
"""Return a mock HueBridge with reasonable defaults.""" """Return a mock HueBridge with reasonable defaults."""
mock_bridge = MagicMock() mock_bridge = MagicMock()
mock_bridge.bridge_id = 'bridge-id'
mock_bridge.host = host mock_bridge.host = host
mock_bridge.allow_hue_groups = allow_hue_groups mock_bridge.allow_hue_groups = allow_hue_groups
mock_bridge.lights = {} mock_bridge.lights = {}
@ -72,6 +74,14 @@ class TestSetup(unittest.TestCase):
return mock_bridge_lights return mock_bridge_lights
def build_mock_light(self, bridge, light_id, name):
"""Return a mock HueLight."""
light = MagicMock()
light.bridge = bridge
light.light_id = light_id
light.name = name
return light
def test_setup_platform_no_discovery_info(self): def test_setup_platform_no_discovery_info(self):
"""Test setup_platform without discovery info.""" """Test setup_platform without discovery info."""
self.hass.data[hue.DOMAIN] = {} self.hass.data[hue.DOMAIN] = {}
@ -96,8 +106,8 @@ class TestSetup(unittest.TestCase):
self.hass.data[hue.DOMAIN] = {'10.0.0.1': mock_bridge} self.hass.data[hue.DOMAIN] = {'10.0.0.1': mock_bridge}
mock_add_devices = MagicMock() mock_add_devices = MagicMock()
with patch('homeassistant.components.light.hue.' + with patch(HUE_LIGHT_NS + 'unthrottled_update_lights') \
'unthrottled_update_lights') as mock_update_lights: as mock_update_lights:
hue_light.setup_platform( hue_light.setup_platform(
self.hass, {}, mock_add_devices, self.hass, {}, mock_add_devices,
{'bridge_id': '10.0.0.1'}) {'bridge_id': '10.0.0.1'})
@ -114,8 +124,8 @@ class TestSetup(unittest.TestCase):
} }
mock_add_devices = MagicMock() mock_add_devices = MagicMock()
with patch('homeassistant.components.light.hue.' + with patch(HUE_LIGHT_NS + 'unthrottled_update_lights') \
'unthrottled_update_lights') as mock_update_lights: as mock_update_lights:
hue_light.setup_platform( hue_light.setup_platform(
self.hass, {}, mock_add_devices, self.hass, {}, mock_add_devices,
{'bridge_id': '10.0.0.1'}) {'bridge_id': '10.0.0.1'})
@ -133,83 +143,105 @@ class TestSetup(unittest.TestCase):
"""Test the update_lights function when no lights are found.""" """Test the update_lights function when no lights are found."""
self.setup_mocks_for_update_lights() self.setup_mocks_for_update_lights()
with patch('homeassistant.components.light.hue.process_lights', with patch(HUE_LIGHT_NS + 'process_lights', return_value=[]) \
return_value=[]) as mock_process_lights: as mock_process_lights:
with patch('homeassistant.components.light.hue.process_groups', with patch(HUE_LIGHT_NS + 'process_groups', return_value=[]) \
return_value=self.mock_groups) \
as mock_process_groups: as mock_process_groups:
hue_light.unthrottled_update_lights( with patch.object(self.hass.helpers.dispatcher,
self.hass, self.mock_bridge, self.mock_add_devices) 'dispatcher_send') as dispatcher_send:
hue_light.unthrottled_update_lights(
self.hass, self.mock_bridge, self.mock_add_devices)
mock_process_lights.assert_called_once_with( mock_process_lights.assert_called_once_with(
self.hass, self.mock_api, self.mock_bridge, mock.ANY) self.hass, self.mock_api, self.mock_bridge, mock.ANY)
mock_process_groups.assert_not_called() mock_process_groups.assert_not_called()
self.mock_add_devices.assert_not_called() self.mock_add_devices.assert_not_called()
dispatcher_send.assert_not_called()
@MockDependency('phue') @MockDependency('phue')
def test_update_lights_with_some_lights(self, mock_phue): def test_update_lights_with_some_lights(self, mock_phue):
"""Test the update_lights function with some lights.""" """Test the update_lights function with some lights."""
self.setup_mocks_for_update_lights() self.setup_mocks_for_update_lights()
self.mock_lights = ['some', 'light'] mock_lights = [
self.build_mock_light(self.mock_bridge, 42, 'some'),
self.build_mock_light(self.mock_bridge, 84, 'light'),
]
with patch('homeassistant.components.light.hue.process_lights', with patch(HUE_LIGHT_NS + 'process_lights',
return_value=self.mock_lights) as mock_process_lights: return_value=mock_lights) as mock_process_lights:
with patch('homeassistant.components.light.hue.process_groups', with patch(HUE_LIGHT_NS + 'process_groups', return_value=[]) \
return_value=self.mock_groups) \
as mock_process_groups: as mock_process_groups:
hue_light.unthrottled_update_lights( with patch.object(self.hass.helpers.dispatcher,
self.hass, self.mock_bridge, self.mock_add_devices) 'dispatcher_send') as dispatcher_send:
hue_light.unthrottled_update_lights(
self.hass, self.mock_bridge, self.mock_add_devices)
mock_process_lights.assert_called_once_with( mock_process_lights.assert_called_once_with(
self.hass, self.mock_api, self.mock_bridge, mock.ANY) self.hass, self.mock_api, self.mock_bridge, mock.ANY)
mock_process_groups.assert_not_called() mock_process_groups.assert_not_called()
self.mock_add_devices.assert_called_once_with( self.mock_add_devices.assert_called_once_with(
self.mock_lights) mock_lights)
dispatcher_send.assert_not_called()
@MockDependency('phue') @MockDependency('phue')
def test_update_lights_no_groups(self, mock_phue): def test_update_lights_no_groups(self, mock_phue):
"""Test the update_lights function when no groups are found.""" """Test the update_lights function when no groups are found."""
self.setup_mocks_for_update_lights() self.setup_mocks_for_update_lights()
self.mock_bridge.allow_hue_groups = True self.mock_bridge.allow_hue_groups = True
self.mock_lights = ['some', 'light'] mock_lights = [
self.build_mock_light(self.mock_bridge, 42, 'some'),
self.build_mock_light(self.mock_bridge, 84, 'light'),
]
with patch('homeassistant.components.light.hue.process_lights', with patch(HUE_LIGHT_NS + 'process_lights',
return_value=self.mock_lights) as mock_process_lights: return_value=mock_lights) as mock_process_lights:
with patch('homeassistant.components.light.hue.process_groups', with patch(HUE_LIGHT_NS + 'process_groups', return_value=[]) \
return_value=self.mock_groups) \
as mock_process_groups: as mock_process_groups:
hue_light.unthrottled_update_lights( with patch.object(self.hass.helpers.dispatcher,
self.hass, self.mock_bridge, self.mock_add_devices) 'dispatcher_send') as dispatcher_send:
hue_light.unthrottled_update_lights(
self.hass, self.mock_bridge, self.mock_add_devices)
mock_process_lights.assert_called_once_with( mock_process_lights.assert_called_once_with(
self.hass, self.mock_api, self.mock_bridge, mock.ANY) self.hass, self.mock_api, self.mock_bridge, mock.ANY)
mock_process_groups.assert_called_once_with( mock_process_groups.assert_called_once_with(
self.hass, self.mock_api, self.mock_bridge, mock.ANY) self.hass, self.mock_api, self.mock_bridge, mock.ANY)
self.mock_add_devices.assert_called_once_with( self.mock_add_devices.assert_called_once_with(
self.mock_lights) mock_lights)
dispatcher_send.assert_not_called()
@MockDependency('phue') @MockDependency('phue')
def test_update_lights_with_lights_and_groups(self, mock_phue): def test_update_lights_with_lights_and_groups(self, mock_phue):
"""Test the update_lights function with both lights and groups.""" """Test the update_lights function with both lights and groups."""
self.setup_mocks_for_update_lights() self.setup_mocks_for_update_lights()
self.mock_bridge.allow_hue_groups = True self.mock_bridge.allow_hue_groups = True
self.mock_lights = ['some', 'light'] mock_lights = [
self.mock_groups = ['and', 'groups'] self.build_mock_light(self.mock_bridge, 42, 'some'),
self.build_mock_light(self.mock_bridge, 84, 'light'),
]
mock_groups = [
self.build_mock_light(self.mock_bridge, 15, 'and'),
self.build_mock_light(self.mock_bridge, 72, 'groups'),
]
with patch('homeassistant.components.light.hue.process_lights', with patch(HUE_LIGHT_NS + 'process_lights',
return_value=self.mock_lights) as mock_process_lights: return_value=mock_lights) as mock_process_lights:
with patch('homeassistant.components.light.hue.process_groups', with patch(HUE_LIGHT_NS + 'process_groups',
return_value=self.mock_groups) \ return_value=mock_groups) as mock_process_groups:
as mock_process_groups: with patch.object(self.hass.helpers.dispatcher,
hue_light.unthrottled_update_lights( 'dispatcher_send') as dispatcher_send:
self.hass, self.mock_bridge, self.mock_add_devices) hue_light.unthrottled_update_lights(
self.hass, self.mock_bridge, self.mock_add_devices)
mock_process_lights.assert_called_once_with( mock_process_lights.assert_called_once_with(
self.hass, self.mock_api, self.mock_bridge, mock.ANY) self.hass, self.mock_api, self.mock_bridge, mock.ANY)
mock_process_groups.assert_called_once_with( mock_process_groups.assert_called_once_with(
self.hass, self.mock_api, self.mock_bridge, mock.ANY) self.hass, self.mock_api, self.mock_bridge, mock.ANY)
self.mock_add_devices.assert_called_once_with( # note that mock_lights has been modified in place and
self.mock_lights) # now contains both lights and groups
self.mock_add_devices.assert_called_once_with(
mock_lights)
dispatcher_send.assert_not_called()
@MockDependency('phue') @MockDependency('phue')
def test_update_lights_with_two_bridges(self, mock_phue): def test_update_lights_with_two_bridges(self, mock_phue):
@ -288,36 +320,42 @@ class TestSetup(unittest.TestCase):
"""Test the process_lights function when bridge returns no lights.""" """Test the process_lights function when bridge returns no lights."""
self.setup_mocks_for_process_lights() self.setup_mocks_for_process_lights()
ret = hue_light.process_lights( with patch.object(self.hass.helpers.dispatcher, 'dispatcher_send') \
self.hass, self.mock_api, self.mock_bridge, None) as mock_dispatcher_send:
ret = hue_light.process_lights(
self.hass, self.mock_api, self.mock_bridge, None)
self.assertEquals([], ret) self.assertEquals([], ret)
self.assertEquals(self.mock_bridge.lights, {}) mock_dispatcher_send.assert_not_called()
self.assertEquals(self.mock_bridge.lights, {})
@patch('homeassistant.components.light.hue.HueLight') @patch(HUE_LIGHT_NS + 'HueLight')
def test_process_lights_some_lights(self, mock_hue_light): def test_process_lights_some_lights(self, mock_hue_light):
"""Test the process_lights function with multiple groups.""" """Test the process_lights function with multiple groups."""
self.setup_mocks_for_process_lights() self.setup_mocks_for_process_lights()
self.mock_api.get.return_value = { self.mock_api.get.return_value = {
1: {'state': 'on'}, 2: {'state': 'off'}} 1: {'state': 'on'}, 2: {'state': 'off'}}
ret = hue_light.process_lights( with patch.object(self.hass.helpers.dispatcher, 'dispatcher_send') \
self.hass, self.mock_api, self.mock_bridge, None) as mock_dispatcher_send:
ret = hue_light.process_lights(
self.hass, self.mock_api, self.mock_bridge, None)
self.assertEquals(len(ret), 2) self.assertEquals(len(ret), 2)
mock_hue_light.assert_has_calls([ mock_hue_light.assert_has_calls([
call( call(
1, {'state': 'on'}, self.mock_bridge, mock.ANY, 1, {'state': 'on'}, self.mock_bridge, mock.ANY,
self.mock_bridge.allow_unreachable, self.mock_bridge.allow_unreachable,
self.mock_bridge.allow_in_emulated_hue), self.mock_bridge.allow_in_emulated_hue),
call( call(
2, {'state': 'off'}, self.mock_bridge, mock.ANY, 2, {'state': 'off'}, self.mock_bridge, mock.ANY,
self.mock_bridge.allow_unreachable, self.mock_bridge.allow_unreachable,
self.mock_bridge.allow_in_emulated_hue), self.mock_bridge.allow_in_emulated_hue),
]) ])
self.assertEquals(len(self.mock_bridge.lights), 2) mock_dispatcher_send.assert_not_called()
self.assertEquals(len(self.mock_bridge.lights), 2)
@patch('homeassistant.components.light.hue.HueLight') @patch(HUE_LIGHT_NS + 'HueLight')
def test_process_lights_new_light(self, mock_hue_light): def test_process_lights_new_light(self, mock_hue_light):
""" """
Test the process_lights function with new groups. Test the process_lights function with new groups.
@ -327,21 +365,24 @@ class TestSetup(unittest.TestCase):
self.setup_mocks_for_process_lights() self.setup_mocks_for_process_lights()
self.mock_api.get.return_value = { self.mock_api.get.return_value = {
1: {'state': 'on'}, 2: {'state': 'off'}} 1: {'state': 'on'}, 2: {'state': 'off'}}
self.mock_bridge.lights = {1: MagicMock()} self.mock_bridge.lights = {
1: self.build_mock_light(self.mock_bridge, 1, 'foo')}
ret = hue_light.process_lights( with patch.object(self.hass.helpers.dispatcher, 'dispatcher_send') \
self.hass, self.mock_api, self.mock_bridge, None) as mock_dispatcher_send:
ret = hue_light.process_lights(
self.hass, self.mock_api, self.mock_bridge, None)
self.assertEquals(len(ret), 1) self.assertEquals(len(ret), 1)
mock_hue_light.assert_has_calls([ mock_hue_light.assert_has_calls([
call( call(
2, {'state': 'off'}, self.mock_bridge, mock.ANY, 2, {'state': 'off'}, self.mock_bridge, mock.ANY,
self.mock_bridge.allow_unreachable, self.mock_bridge.allow_unreachable,
self.mock_bridge.allow_in_emulated_hue), self.mock_bridge.allow_in_emulated_hue),
]) ])
self.assertEquals(len(self.mock_bridge.lights), 2) mock_dispatcher_send.assert_called_once_with(
self.mock_bridge.lights[1]\ 'hue_light_callback_bridge-id_1')
.schedule_update_ha_state.assert_called_once_with() self.assertEquals(len(self.mock_bridge.lights), 2)
def test_process_groups_api_error(self): def test_process_groups_api_error(self):
"""Test the process_groups function when the bridge errors out.""" """Test the process_groups function when the bridge errors out."""
@ -359,36 +400,42 @@ class TestSetup(unittest.TestCase):
self.setup_mocks_for_process_groups() self.setup_mocks_for_process_groups()
self.mock_bridge.get_group.return_value = {'name': 'Group 0'} self.mock_bridge.get_group.return_value = {'name': 'Group 0'}
ret = hue_light.process_groups( with patch.object(self.hass.helpers.dispatcher, 'dispatcher_send') \
self.hass, self.mock_api, self.mock_bridge, None) as mock_dispatcher_send:
ret = hue_light.process_groups(
self.hass, self.mock_api, self.mock_bridge, None)
self.assertEquals([], ret) self.assertEquals([], ret)
self.assertEquals(self.mock_bridge.lightgroups, {}) mock_dispatcher_send.assert_not_called()
self.assertEquals(self.mock_bridge.lightgroups, {})
@patch('homeassistant.components.light.hue.HueLight') @patch(HUE_LIGHT_NS + 'HueLight')
def test_process_groups_some_groups(self, mock_hue_light): def test_process_groups_some_groups(self, mock_hue_light):
"""Test the process_groups function with multiple groups.""" """Test the process_groups function with multiple groups."""
self.setup_mocks_for_process_groups() self.setup_mocks_for_process_groups()
self.mock_api.get.return_value = { self.mock_api.get.return_value = {
1: {'state': 'on'}, 2: {'state': 'off'}} 1: {'state': 'on'}, 2: {'state': 'off'}}
ret = hue_light.process_groups( with patch.object(self.hass.helpers.dispatcher, 'dispatcher_send') \
self.hass, self.mock_api, self.mock_bridge, None) as mock_dispatcher_send:
ret = hue_light.process_groups(
self.hass, self.mock_api, self.mock_bridge, None)
self.assertEquals(len(ret), 2) self.assertEquals(len(ret), 2)
mock_hue_light.assert_has_calls([ mock_hue_light.assert_has_calls([
call( call(
1, {'state': 'on'}, self.mock_bridge, mock.ANY, 1, {'state': 'on'}, self.mock_bridge, mock.ANY,
self.mock_bridge.allow_unreachable, self.mock_bridge.allow_unreachable,
self.mock_bridge.allow_in_emulated_hue, True), self.mock_bridge.allow_in_emulated_hue, True),
call( call(
2, {'state': 'off'}, self.mock_bridge, mock.ANY, 2, {'state': 'off'}, self.mock_bridge, mock.ANY,
self.mock_bridge.allow_unreachable, self.mock_bridge.allow_unreachable,
self.mock_bridge.allow_in_emulated_hue, True), self.mock_bridge.allow_in_emulated_hue, True),
]) ])
self.assertEquals(len(self.mock_bridge.lightgroups), 2) mock_dispatcher_send.assert_not_called()
self.assertEquals(len(self.mock_bridge.lightgroups), 2)
@patch('homeassistant.components.light.hue.HueLight') @patch(HUE_LIGHT_NS + 'HueLight')
def test_process_groups_new_group(self, mock_hue_light): def test_process_groups_new_group(self, mock_hue_light):
""" """
Test the process_groups function with new groups. Test the process_groups function with new groups.
@ -398,21 +445,24 @@ class TestSetup(unittest.TestCase):
self.setup_mocks_for_process_groups() self.setup_mocks_for_process_groups()
self.mock_api.get.return_value = { self.mock_api.get.return_value = {
1: {'state': 'on'}, 2: {'state': 'off'}} 1: {'state': 'on'}, 2: {'state': 'off'}}
self.mock_bridge.lightgroups = {1: MagicMock()} self.mock_bridge.lightgroups = {
1: self.build_mock_light(self.mock_bridge, 1, 'foo')}
ret = hue_light.process_groups( with patch.object(self.hass.helpers.dispatcher, 'dispatcher_send') \
self.hass, self.mock_api, self.mock_bridge, None) as mock_dispatcher_send:
ret = hue_light.process_groups(
self.hass, self.mock_api, self.mock_bridge, None)
self.assertEquals(len(ret), 1) self.assertEquals(len(ret), 1)
mock_hue_light.assert_has_calls([ mock_hue_light.assert_has_calls([
call( call(
2, {'state': 'off'}, self.mock_bridge, mock.ANY, 2, {'state': 'off'}, self.mock_bridge, mock.ANY,
self.mock_bridge.allow_unreachable, self.mock_bridge.allow_unreachable,
self.mock_bridge.allow_in_emulated_hue, True), self.mock_bridge.allow_in_emulated_hue, True),
]) ])
self.assertEquals(len(self.mock_bridge.lightgroups), 2) mock_dispatcher_send.assert_called_once_with(
self.mock_bridge.lightgroups[1]\ 'hue_light_callback_bridge-id_1')
.schedule_update_ha_state.assert_called_once_with() self.assertEquals(len(self.mock_bridge.lightgroups), 2)
class TestHueLight(unittest.TestCase): class TestHueLight(unittest.TestCase):
@ -440,6 +490,10 @@ class TestHueLight(unittest.TestCase):
def buildLight( def buildLight(
self, light_id=None, info=None, update_lights=None, is_group=None): self, light_id=None, info=None, update_lights=None, is_group=None):
"""Helper to build a HueLight object with minimal fuss.""" """Helper to build a HueLight object with minimal fuss."""
if 'state' not in info:
on_key = 'any_on' if is_group is not None else 'on'
info['state'] = {on_key: False}
return hue_light.HueLight( return hue_light.HueLight(
light_id if light_id is not None else self.light_id, light_id if light_id is not None else self.light_id,
info if info is not None else self.mock_info, info if info is not None else self.mock_info,