diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index ea8900d8fbf..54099600287 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -227,11 +227,17 @@ def setup(hass, config): if not lights: lights = list(ent_to_light.values()) + params = {} + transition = util.convert(dat.get(ATTR_TRANSITION), int) + if transition is not None: + params[ATTR_TRANSITION] = transition + if service.service == SERVICE_TURN_OFF: for light in lights: - light.turn_off(transition=transition) + # pylint: disable=star-args + light.turn_off(**params) else: # Processing extra data for turn light on request @@ -242,20 +248,22 @@ def setup(hass, config): profile = profiles.get(dat.get(ATTR_PROFILE)) if profile: - *color, bright = profile - else: - color, bright = None, None + # *color, bright = profile + *params[ATTR_XY_COLOR], params[ATTR_BRIGHTNESS] = profile if ATTR_BRIGHTNESS in dat: - bright = util.convert(dat.get(ATTR_BRIGHTNESS), int) + # We pass in the old value as the default parameter if parsing + # of the new one goes wrong. + params[ATTR_BRIGHTNESS] = util.convert( + dat.get(ATTR_BRIGHTNESS), int, params.get(ATTR_BRIGHTNESS)) if ATTR_XY_COLOR in dat: try: # xy_color should be a list containing 2 floats - xy_color = dat.get(ATTR_XY_COLOR) + xycolor = dat.get(ATTR_XY_COLOR) - if len(xy_color) == 2: - color = [float(val) for val in xy_color] + if len(xycolor) == 2: + params[ATTR_XY_COLOR] = [float(val) for val in xycolor] except (TypeError, ValueError): # TypeError if xy_color is not iterable @@ -268,9 +276,10 @@ def setup(hass, config): rgb_color = dat.get(ATTR_RGB_COLOR) if len(rgb_color) == 3: - color = util.color_RGB_to_xy(int(rgb_color[0]), - int(rgb_color[1]), - int(rgb_color[2])) + params[ATTR_XY_COLOR] = \ + util.color_RGB_to_xy(int(rgb_color[0]), + int(rgb_color[1]), + int(rgb_color[2])) except (TypeError, ValueError): # TypeError if rgb_color is not iterable @@ -278,8 +287,8 @@ def setup(hass, config): pass for light in lights: - light.turn_on(transition=transition, brightness=bright, - xy_color=color) + # pylint: disable=star-args + light.turn_on(**params) for light in lights: light.update_ha_state(hass, True) diff --git a/homeassistant/components/light/hue.py b/homeassistant/components/light/hue.py index 8deb4111e4f..fb1e8a42ff5 100644 --- a/homeassistant/components/light/hue.py +++ b/homeassistant/components/light/hue.py @@ -98,16 +98,16 @@ class HueLight(ToggleDevice): """ Turn the specified or all lights on. """ command = {'on': True} - if kwargs.get(ATTR_TRANSITION) is not None: + if ATTR_TRANSITION in kwargs: # Transition time is in 1/10th seconds and cannot exceed # 900 seconds. - command['transitiontime'] = min(9000, kwargs['transition'] * 10) + command['transitiontime'] = min(9000, kwargs[ATTR_TRANSITION] * 10) - if kwargs.get(ATTR_BRIGHTNESS) is not None: - command['bri'] = kwargs['brightness'] + if ATTR_BRIGHTNESS in kwargs: + command['bri'] = kwargs[ATTR_BRIGHTNESS] - if kwargs.get(ATTR_XY_COLOR) is not None: - command['xy'] = kwargs['xy_color'] + if ATTR_XY_COLOR in kwargs: + command['xy'] = kwargs[ATTR_XY_COLOR] self.bridge.set_light(self.light_id, command) @@ -115,10 +115,10 @@ class HueLight(ToggleDevice): """ Turn the specified or all lights off. """ command = {'on': False} - if kwargs.get('transition') is not None: + if ATTR_TRANSITION in kwargs: # Transition time is in 1/10th seconds and cannot exceed # 900 seconds. - command['transitiontime'] = min(9000, kwargs['transition'] * 10) + command['transitiontime'] = min(9000, kwargs[ATTR_TRANSITION] * 10) self.bridge.set_light(self.light_id, command) diff --git a/test/mock_toggledevice_platform.py b/test/mock_toggledevice_platform.py index 6488e8f8b9c..f864301ade4 100644 --- a/test/mock_toggledevice_platform.py +++ b/test/mock_toggledevice_platform.py @@ -3,6 +3,8 @@ test.mock.switch_platform ~~~~~~~~~~~~~~~~~~~~~~~~~ Provides a mock switch platform. + +Call init before using it in your tests to ensure clean test data. """ import homeassistant.components as components @@ -12,46 +14,51 @@ class MockToggleDevice(components.ToggleDevice): def __init__(self, name, state): self.name = name self.state = state + self.calls = [] def get_name(self): """ Returns the name of the device if any. """ + self.calls.append(('get_name', {})) return self.name def turn_on(self, **kwargs): """ Turn the device on. """ + self.calls.append(('turn_on', kwargs)) self.state = components.STATE_ON def turn_off(self, **kwargs): """ Turn the device off. """ + self.calls.append(('turn_off', kwargs)) self.state = components.STATE_OFF def is_on(self): """ True if device is on. """ + self.calls.append(('is_on', {})) return self.state == components.STATE_ON - def get_state_attributes(self): - """ Returns optional state attributes. """ - return {} + def last_call(self, method=None): + if method is None: + return self.calls[-1] + else: + return next(call for call in reversed(self.calls) + if call[0] == method) + +DEVICES = [] -FAKE_NO_DEVICES = False +def init(empty=False): + """ (re-)initalizes the platform with devices. """ + global DEVICES -DEVICES = [ - MockToggleDevice('AC', components.STATE_ON), - MockToggleDevice('AC', components.STATE_OFF), - MockToggleDevice(None, components.STATE_OFF) -] - - -def fake_no_switches(do_fake): - """ Set the platform to act as if it has no devices. """ - global FAKE_NO_DEVICES - - FAKE_NO_DEVICES = do_fake + DEVICES = [] if empty else [ + MockToggleDevice('AC', components.STATE_ON), + MockToggleDevice('AC', components.STATE_OFF), + MockToggleDevice(None, components.STATE_OFF) + ] def get_switches(hass, config): """ Returns mock devices. """ - return [] if FAKE_NO_DEVICES else DEVICES + return DEVICES get_lights = get_switches diff --git a/test/test_component_light.py b/test/test_component_light.py new file mode 100644 index 00000000000..ec0d032d35e --- /dev/null +++ b/test/test_component_light.py @@ -0,0 +1,228 @@ +""" +test.test_component_switch +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Tests switch component. +""" +# pylint: disable=too-many-public-methods,protected-access +import unittest + +import homeassistant as ha +import homeassistant.loader as loader +import homeassistant.util as util +import homeassistant.components as components +import homeassistant.components.light as light + +import mock_toggledevice_platform + +from helper import mock_service + + +class TestLight(unittest.TestCase): + """ Test the switch module. """ + + def setUp(self): # pylint: disable=invalid-name + self.hass = ha.HomeAssistant() + loader.prepare(self.hass) + loader.set_component('light.test', mock_toggledevice_platform) + + def tearDown(self): # pylint: disable=invalid-name + """ Stop down stuff we started. """ + self.hass._pool.stop() + + def test_methods(self): + """ Test if methods call the services as expected. """ + # Test is_on + self.hass.states.set('light.test', components.STATE_ON) + self.assertTrue(light.is_on(self.hass, 'light.test')) + + self.hass.states.set('light.test', components.STATE_OFF) + self.assertFalse(light.is_on(self.hass, 'light.test')) + + self.hass.states.set(light.ENTITY_ID_ALL_LIGHTS, components.STATE_ON) + self.assertTrue(light.is_on(self.hass)) + + self.hass.states.set(light.ENTITY_ID_ALL_LIGHTS, components.STATE_OFF) + self.assertFalse(light.is_on(self.hass)) + + # Test turn_on + turn_on_calls = mock_service( + self.hass, light.DOMAIN, components.SERVICE_TURN_ON) + + light.turn_on( + self.hass, + entity_id='entity_id_val', + transition='transition_val', + brightness='brightness_val', + rgb_color='rgb_color_val', + xy_color='xy_color_val', + profile='profile_val') + + self.hass._pool.block_till_done() + + self.assertEqual(1, len(turn_on_calls)) + call = turn_on_calls[-1] + + self.assertEqual(light.DOMAIN, call.domain) + self.assertEqual(components.SERVICE_TURN_ON, call.service) + self.assertEqual('entity_id_val', call.data[components.ATTR_ENTITY_ID]) + self.assertEqual('transition_val', call.data[light.ATTR_TRANSITION]) + self.assertEqual('brightness_val', call.data[light.ATTR_BRIGHTNESS]) + self.assertEqual('rgb_color_val', call.data[light.ATTR_RGB_COLOR]) + self.assertEqual('xy_color_val', call.data[light.ATTR_XY_COLOR]) + self.assertEqual('profile_val', call.data[light.ATTR_PROFILE]) + + # Test turn_off + turn_off_calls = mock_service( + self.hass, light.DOMAIN, components.SERVICE_TURN_OFF) + + light.turn_off( + self.hass, entity_id='entity_id_val', transition='transition_val') + + self.hass._pool.block_till_done() + + self.assertEqual(1, len(turn_off_calls)) + call = turn_off_calls[-1] + + self.assertEqual(light.DOMAIN, call.domain) + self.assertEqual(components.SERVICE_TURN_OFF, call.service) + self.assertEqual('entity_id_val', call.data[components.ATTR_ENTITY_ID]) + self.assertEqual('transition_val', call.data[light.ATTR_TRANSITION]) + + def test_services(self): + """ Test the provided services. """ + mock_toggledevice_platform.init() + self.assertTrue( + light.setup(self.hass, {light.DOMAIN: {ha.CONF_TYPE: 'test'}})) + + dev1, dev2, dev3 = mock_toggledevice_platform.get_lights(None, None) + + # Test init + self.assertTrue(light.is_on(self.hass, dev1.entity_id)) + self.assertFalse(light.is_on(self.hass, dev2.entity_id)) + self.assertFalse(light.is_on(self.hass, dev3.entity_id)) + + # Test basic turn_on, turn_off services + light.turn_off(self.hass, entity_id=dev1.entity_id) + light.turn_on(self.hass, entity_id=dev2.entity_id) + + self.hass._pool.block_till_done() + + self.assertFalse(light.is_on(self.hass, dev1.entity_id)) + self.assertTrue(light.is_on(self.hass, dev2.entity_id)) + + # turn on all lights + light.turn_on(self.hass) + + self.hass._pool.block_till_done() + + self.assertTrue(light.is_on(self.hass, dev1.entity_id)) + self.assertTrue(light.is_on(self.hass, dev2.entity_id)) + self.assertTrue(light.is_on(self.hass, dev3.entity_id)) + + # turn off all lights + light.turn_off(self.hass) + + self.hass._pool.block_till_done() + + self.assertFalse(light.is_on(self.hass, dev1.entity_id)) + self.assertFalse(light.is_on(self.hass, dev2.entity_id)) + self.assertFalse(light.is_on(self.hass, dev3.entity_id)) + + # Ensure all attributes process correctly + light.turn_on(self.hass, dev1.entity_id, + transition=10, brightness=20) + light.turn_on( + self.hass, dev2.entity_id, rgb_color=[255, 255, 255]) + light.turn_on(self.hass, dev3.entity_id, xy_color=[.4, .6]) + + self.hass._pool.block_till_done() + + method, data = dev1.last_call('turn_on') + self.assertEqual( + {light.ATTR_TRANSITION: 10, + light.ATTR_BRIGHTNESS: 20}, + data) + + method, data = dev2.last_call('turn_on') + self.assertEqual( + {light.ATTR_XY_COLOR: util.color_RGB_to_xy(255, 255, 255)}, + data) + + method, data = dev3.last_call('turn_on') + self.assertEqual({light.ATTR_XY_COLOR: [.4, .6]}, data) + + # One of the light profiles + prof_name, prof_x, prof_y, prof_bri = 'relax', 0.5119, 0.4147, 144 + + # Test light profiles + light.turn_on(self.hass, dev1.entity_id, profile=prof_name) + # Specify a profile and attributes to overwrite it + light.turn_on( + self.hass, dev2.entity_id, + profile=prof_name, brightness=100, xy_color=[.4, .6]) + + self.hass._pool.block_till_done() + + method, data = dev1.last_call('turn_on') + self.assertEqual( + {light.ATTR_BRIGHTNESS: prof_bri, + light.ATTR_XY_COLOR: [prof_x, prof_y]}, + data) + + method, data = dev2.last_call('turn_on') + self.assertEqual( + {light.ATTR_BRIGHTNESS: 100, + light.ATTR_XY_COLOR: [.4, .6]}, + data) + + # Test shitty data + light.turn_on(self.hass, dev1.entity_id, profile="nonexisting") + light.turn_on(self.hass, dev2.entity_id, xy_color="bla-di-bla") + light.turn_on(self.hass, dev3.entity_id, rgb_color=[255, None, 2]) + + self.hass._pool.block_till_done() + + method, data = dev1.last_call('turn_on') + self.assertEqual({}, data) + + method, data = dev2.last_call('turn_on') + self.assertEqual({}, data) + + method, data = dev3.last_call('turn_on') + self.assertEqual({}, data) + + # faulty attributes should not overwrite profile data + light.turn_on( + self.hass, dev1.entity_id, + profile=prof_name, brightness='bright', rgb_color='yellowish') + + self.hass._pool.block_till_done() + + method, data = dev1.last_call('turn_on') + self.assertEqual( + {light.ATTR_BRIGHTNESS: prof_bri, + light.ATTR_XY_COLOR: [prof_x, prof_y]}, + data) + + def test_setup(self): + """ Test the setup method. """ + # Bogus config + self.assertFalse(light.setup(self.hass, {})) + + self.assertFalse(light.setup(self.hass, {light.DOMAIN: {}})) + + # Test with non-existing component + self.assertFalse(light.setup( + self.hass, {light.DOMAIN: {ha.CONF_TYPE: 'nonexisting'}} + )) + + # Test if light component returns 0 lightes + mock_toggledevice_platform.init(True) + + self.assertEqual( + [], mock_toggledevice_platform.get_lights(None, None)) + + self.assertFalse(light.setup( + self.hass, {light.DOMAIN: {ha.CONF_TYPE: 'test'}} + )) diff --git a/test/test_component_switch.py b/test/test_component_switch.py index 6432369e7ed..ca207972720 100644 --- a/test/test_component_switch.py +++ b/test/test_component_switch.py @@ -23,6 +23,7 @@ class TestSwitch(unittest.TestCase): loader.prepare(self.hass) loader.set_component('switch.test', mock_toggledevice_platform) + mock_toggledevice_platform.init() self.assertTrue(switch.setup( self.hass, {switch.DOMAIN: {ha.CONF_TYPE: 'test'}} )) @@ -92,7 +93,7 @@ class TestSwitch(unittest.TestCase): )) # Test if switch component returns 0 switches - mock_toggledevice_platform.fake_no_switches(True) + mock_toggledevice_platform.init(True) self.assertEqual( [], mock_toggledevice_platform.get_switches(None, None)) @@ -100,5 +101,3 @@ class TestSwitch(unittest.TestCase): self.assertFalse(switch.setup( self.hass, {switch.DOMAIN: {ha.CONF_TYPE: 'test'}} )) - - mock_toggledevice_platform.fake_no_switches(False)