diff --git a/homeassistant/components/light/mysensors.py b/homeassistant/components/light/mysensors.py index a1f01fabf6b..b50446e2f38 100644 --- a/homeassistant/components/light/mysensors.py +++ b/homeassistant/components/light/mysensors.py @@ -1,17 +1,17 @@ """ -homeassistant.components.light.mysensors. - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Support for MySensors lights. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.mysensors.html +https://home-assistant.io/components/light.mysensors/ """ import logging from homeassistant.components.light import ( Light, ATTR_BRIGHTNESS, ATTR_RGB_COLOR) +from homeassistant.util.color import ( + rgb_hex_to_rgb_list) + from homeassistant.const import ( ATTR_BATTERY_LEVEL, STATE_ON, STATE_OFF) @@ -20,6 +20,8 @@ import homeassistant.components.mysensors as mysensors _LOGGER = logging.getLogger(__name__) ATTR_RGB_WHITE = 'rgb_white' +ATTR_VALUE = 'value' +ATTR_VALUE_TYPE = 'value_type' def setup_platform(hass, config, add_devices, discovery_info=None): @@ -38,6 +40,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None): pres.S_LIGHT: [set_req.V_LIGHT], pres.S_DIMMER: [set_req.V_DIMMER], } + device_class_map = { + pres.S_LIGHT: MySensorsLightPlain, + pres.S_DIMMER: MySensorsLightDimmer, + } if float(gateway.version) >= 1.5: # Add V_RGBW when rgb_white is implemented in the frontend map_sv_types.update({ @@ -45,10 +51,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None): }) map_sv_types[pres.S_LIGHT].append(set_req.V_STATUS) map_sv_types[pres.S_DIMMER].append(set_req.V_PERCENTAGE) - + device_class_map.update({ + pres.S_RGB_LIGHT: MySensorsLightRGB, + }) devices = {} gateway.platform_callbacks.append(mysensors.pf_callback_factory( - map_sv_types, devices, add_devices, MySensorsLight)) + map_sv_types, devices, add_devices, device_class_map)) class MySensorsLight(Light): @@ -66,8 +74,8 @@ class MySensorsLight(Light): self.battery_level = 0 self._values = {} self._state = None - self._rgb = None self._brightness = None + self._rgb = None self._white = None @property @@ -108,119 +116,246 @@ class MySensorsLight(Light): device_attr[self.gateway.const.SetReq(value_type).name] = value return device_attr + @property + def available(self): + """Return True if entity is available.""" + return self.value_type in self._values + @property def is_on(self): """True if device is on.""" return self._state - def turn_on(self, **kwargs): - """Turn the device on.""" + def _turn_on_light(self): + """Turn on light child device.""" set_req = self.gateway.const.SetReq - rgb = self._rgb - brightness = self._brightness - white = self._white - if set_req.V_LIGHT in self._values and not self._state: + if not self._state and set_req.V_LIGHT in self._values: self.gateway.set_child_value( self.node_id, self.child_id, set_req.V_LIGHT, 1) - if ATTR_BRIGHTNESS in kwargs and set_req.V_DIMMER in self._values and \ + if self.gateway.optimistic: + # optimistically assume that light has changed state + self._state = True + self.update_ha_state() + + def _turn_on_dimmer(self, **kwargs): + """Turn on dimmer child device.""" + set_req = self.gateway.const.SetReq + brightness = self._brightness + + if ATTR_BRIGHTNESS in kwargs and \ kwargs[ATTR_BRIGHTNESS] != self._brightness: brightness = kwargs[ATTR_BRIGHTNESS] percent = round(100 * brightness / 255) self.gateway.set_child_value( self.node_id, self.child_id, set_req.V_DIMMER, percent) - if float(self.gateway.version) >= 1.5: + if self.gateway.optimistic: + # optimistically assume that light has changed state + self._brightness = brightness + self.update_ha_state() - if ATTR_RGB_WHITE in kwargs and \ - self.value_type in (set_req.V_RGB, set_req.V_RGBW) and \ - kwargs[ATTR_RGB_WHITE] != self._white: - white = kwargs[ATTR_RGB_WHITE] + def _turn_on_rgb_and_w(self, hex_template, **kwargs): + """Turn on RGB or RGBW child device.""" + rgb = self._rgb + white = self._white - if ATTR_RGB_COLOR in kwargs and \ - self.value_type in (set_req.V_RGB, set_req.V_RGBW) and \ - kwargs[ATTR_RGB_COLOR] != self._rgb: - rgb = kwargs[ATTR_RGB_COLOR] - if set_req.V_RGBW == self.value_type: - hex_template = '%02x%02x%02x%02x' - color_list = rgb.append(white) - if set_req.V_RGB == self.value_type: - hex_template = '%02x%02x%02x' - color_list = rgb - hex_color = hex_template % tuple(color_list) - self.gateway.set_child_value( - self.node_id, self.child_id, self.value_type, hex_color) + if ATTR_RGB_WHITE in kwargs and \ + kwargs[ATTR_RGB_WHITE] != self._white: + white = kwargs[ATTR_RGB_WHITE] + + if ATTR_RGB_COLOR in kwargs and \ + kwargs[ATTR_RGB_COLOR] != self._rgb: + rgb = kwargs[ATTR_RGB_COLOR] + if white is not None and hex_template == '%02x%02x%02x%02x': + rgb.append(white) + hex_color = hex_template % tuple(rgb) + self.gateway.set_child_value( + self.node_id, self.child_id, self.value_type, hex_color) if self.gateway.optimistic: # optimistically assume that light has changed state - self._state = True self._rgb = rgb - self._brightness = brightness self._white = white self.update_ha_state() - def turn_off(self, **kwargs): - """Turn the device off.""" + def _turn_off_light(self, value_type=None, value=None): + """Turn off light child device.""" set_req = self.gateway.const.SetReq - v_type = set_req.V_LIGHT - value = 0 - if set_req.V_LIGHT in self._values: - self._values[set_req.V_LIGHT] = STATE_OFF - elif set_req.V_DIMMER in self._values: - v_type = set_req.V_DIMMER - elif float(self.gateway.version) >= 1.5: - if set_req.V_RGB in self._values: - v_type = set_req.V_RGB - value = '000000' - elif set_req.V_RGBW in self._values: - v_type = set_req.V_RGBW - value = '00000000' - self.gateway.set_child_value( - self.node_id, self.child_id, v_type, value) + value_type = ( + set_req.V_LIGHT + if set_req.V_LIGHT in self._values else value_type) + value = 0 if set_req.V_LIGHT in self._values else value + return {ATTR_VALUE_TYPE: value_type, ATTR_VALUE: value} + def _turn_off_dimmer(self, value_type=None, value=None): + """Turn off dimmer child device.""" + set_req = self.gateway.const.SetReq + value_type = ( + set_req.V_DIMMER + if set_req.V_DIMMER in self._values else value_type) + value = 0 if set_req.V_DIMMER in self._values else value + return {ATTR_VALUE_TYPE: value_type, ATTR_VALUE: value} + + def _turn_off_rgb_or_w(self, value_type=None, value=None): + """Turn off RGB or RGBW child device.""" + if float(self.gateway.version) >= 1.5: + set_req = self.gateway.const.SetReq + if self.value_type == set_req.V_RGB: + value = '000000' + elif self.value_type == set_req.V_RGBW: + value = '00000000' + return {ATTR_VALUE_TYPE: self.value_type, ATTR_VALUE: value} + + def _turn_off_main(self, value_type=None, value=None): + """Turn the device off.""" + if value_type is None or value is None: + _LOGGER.warning( + '%s: value_type %s, value = %s, ' + 'None is not valid argument when setting child value' + '', self._name, value_type, value) + return + self.gateway.set_child_value( + self.node_id, self.child_id, value_type, value) if self.gateway.optimistic: # optimistically assume that light has changed state self._state = False self.update_ha_state() - @property - def available(self): - """Return True if entity is available.""" - return self.value_type in self._values + def _update_light(self): + """Update the controller with values from light child.""" + value_type = self.gateway.const.SetReq.V_LIGHT + if value_type in self._values: + self._values[value_type] = ( + STATE_ON if int(self._values[value_type]) == 1 else STATE_OFF) + self._state = self._values[value_type] == STATE_ON - def update(self): + def _update_dimmer(self): + """Update the controller with values from dimmer child.""" + set_req = self.gateway.const.SetReq + value_type = set_req.V_DIMMER + if value_type in self._values: + self._brightness = round(255 * int(self._values[value_type]) / 100) + if self._brightness == 0: + self._state = False + if set_req.V_LIGHT not in self._values: + self._state = self._brightness > 0 + + def _update_rgb_or_w(self): + """Update the controller with values from RGB or RGBW child.""" + set_req = self.gateway.const.SetReq + value = self._values[self.value_type] + color_list = rgb_hex_to_rgb_list(value) + if set_req.V_LIGHT not in self._values and \ + set_req.V_DIMMER not in self._values: + self._state = max(color_list) > 0 + if len(color_list) > 3: + self._white = color_list.pop() + self._rgb = color_list + + def _update_main(self): """Update the controller with the latest value from a sensor.""" node = self.gateway.sensors[self.node_id] child = node.children[self.child_id] - set_req = self.gateway.const.SetReq self.battery_level = node.battery_level for value_type, value in child.values.items(): _LOGGER.debug( - "%s: value_type %s, value = %s", self._name, value_type, value) - if value_type == set_req.V_LIGHT: - self._values[value_type] = ( - STATE_ON if int(value) == 1 else STATE_OFF) - self._state = self._values[value_type] == STATE_ON - else: - self._values[value_type] = value - if value_type == set_req.V_DIMMER: - self._brightness = round( - 255 * int(self._values[value_type]) / 100) - if self._brightness == 0: - self._state = False - if set_req.V_LIGHT not in self._values: - self._state = self._brightness > 0 - if float(self.gateway.version) >= 1.5 and \ - value_type in (set_req.V_RGB, set_req.V_RGBW): - # convert hex color string to rgb(w) integer list - color_list = [int(value[i:i + len(value) // 3], 16) - for i in range(0, - len(value), - len(value) // 3)] - if len(color_list) > 3: - self._white = color_list.pop() - self._rgb = color_list - if set_req.V_LIGHT not in self._values or \ - set_req.V_DIMMER not in self._values: - self._state = max(color_list) > 0 + '%s: value_type %s, value = %s', self._name, value_type, value) + self._values[value_type] = value + + +class MySensorsLightPlain(MySensorsLight): + """Light child class to MySensorsLight.""" + + def turn_on(self, **kwargs): + """Turn the device on.""" + self._turn_on_light() + + def turn_off(self, **kwargs): + """Turn the device off.""" + ret = self._turn_off_light() + self._turn_off_main(value_type=ret[ + ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) + + def update(self): + """Update the controller with the latest value from a sensor.""" + self._update_main() + self._update_light() + + +class MySensorsLightDimmer(MySensorsLight): + """Dimmer child class to MySensorsLight.""" + + def turn_on(self, **kwargs): + """Turn the device on.""" + self._turn_on_light() + self._turn_on_dimmer(**kwargs) + + def turn_off(self, **kwargs): + """Turn the device off.""" + ret = self._turn_off_dimmer() + ret = self._turn_off_light( + value_type=ret[ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) + self._turn_off_main( + value_type=ret[ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) + + def update(self): + """Update the controller with the latest value from a sensor.""" + self._update_main() + self._update_light() + self._update_dimmer() + + +class MySensorsLightRGB(MySensorsLight): + """RGB child class to MySensorsLight.""" + + def turn_on(self, **kwargs): + """Turn the device on.""" + self._turn_on_light() + self._turn_on_dimmer(**kwargs) + self._turn_on_rgb_and_w('%02x%02x%02x', **kwargs) + + def turn_off(self, **kwargs): + """Turn the device off.""" + ret = self._turn_off_rgb_or_w() + ret = self._turn_off_dimmer( + value_type=ret[ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) + ret = self._turn_off_light( + value_type=ret[ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) + self._turn_off_main( + value_type=ret[ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) + + def update(self): + """Update the controller with the latest value from a sensor.""" + self._update_main() + self._update_light() + self._update_dimmer() + self._update_rgb_or_w() + + +class MySensorsLightRGBW(MySensorsLight): + """RGBW child class to MySensorsLight.""" + + def turn_on(self, **kwargs): + """Turn the device on.""" + self._turn_on_light() + self._turn_on_dimmer(**kwargs) + self._turn_on_rgb_and_w('%02x%02x%02x%02x', **kwargs) + + def turn_off(self, **kwargs): + """Turn the device off.""" + ret = self._turn_off_rgb_or_w() + ret = self._turn_off_dimmer( + value_type=ret[ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) + ret = self._turn_off_light( + value_type=ret[ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) + self._turn_off_main( + value_type=ret[ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) + + def update(self): + """Update the controller with the latest value from a sensor.""" + self._update_main() + self._update_light() + self._update_dimmer() + self._update_rgb_or_w() diff --git a/homeassistant/components/mysensors.py b/homeassistant/components/mysensors.py index 4855c838619..30775c4daae 100644 --- a/homeassistant/components/mysensors.py +++ b/homeassistant/components/mysensors.py @@ -146,7 +146,11 @@ def pf_callback_factory(map_sv_types, devices, add_devices, entity_class): continue name = '{} {}.{}'.format( gateway.sensors[node_id].sketch_name, node_id, child.id) - devices[key] = entity_class( + if isinstance(entity_class, dict): + device_class = entity_class[child.type] + else: + device_class = entity_class + devices[key] = device_class( gateway, node_id, child.id, name, value_type) _LOGGER.info('Adding new devices: %s', devices[key]) diff --git a/homeassistant/components/sensor/mysensors.py b/homeassistant/components/sensor/mysensors.py index fcf5248be67..ca4df33462b 100644 --- a/homeassistant/components/sensor/mysensors.py +++ b/homeassistant/components/sensor/mysensors.py @@ -1,7 +1,4 @@ """ -homeassistant.components.sensor.mysensors. - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Support for MySensors sensors. For more details about this platform, please refer to the documentation at diff --git a/homeassistant/components/switch/mysensors.py b/homeassistant/components/switch/mysensors.py index cf24fb3c401..19c8f7112f6 100644 --- a/homeassistant/components/switch/mysensors.py +++ b/homeassistant/components/switch/mysensors.py @@ -1,11 +1,8 @@ """ -homeassistant.components.switch.mysensors. - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Support for MySensors switches. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.mysensors.html +https://home-assistant.io/components/switch.mysensors/ """ import logging diff --git a/homeassistant/util/color.py b/homeassistant/util/color.py index e5b11f2afeb..1806d9058dc 100644 --- a/homeassistant/util/color.py +++ b/homeassistant/util/color.py @@ -1,15 +1,11 @@ -""" -homeassistant.util.color -~~~~~~~~~~~~~~~~~~~~~~~~ -Color util methods. -""" +"""Color util methods.""" # Taken from: http://www.cse.unr.edu/~quiroz/inc/colortransforms.py # License: Code is given as is. Use at your own risk and discretion. # pylint: disable=invalid-name def color_RGB_to_xy(R, G, B): - """ Convert from RGB color to XY color. """ + """Convert from RGB color to XY color.""" if R + G + B == 0: return 0, 0 @@ -50,9 +46,7 @@ def color_RGB_to_xy(R, G, B): # Copyright (c) 2014 Benjamin Knight / MIT License. # pylint: disable=bad-builtin def color_xy_brightness_to_RGB(vX, vY, brightness): - ''' - Convert from XYZ to RGB. - ''' + """Convert from XYZ to RGB.""" brightness /= 255. if brightness == 0: return (0, 0, 0) @@ -88,3 +82,11 @@ def color_xy_brightness_to_RGB(vX, vY, brightness): r, g, b = map(lambda x: int(x * 255), [r, g, b]) return (r, g, b) + + +def rgb_hex_to_rgb_list(hex_string): + """Return an RGB color value list from a hex color string.""" + return [int(hex_string[i:i + len(hex_string) // 3], 16) + for i in range(0, + len(hex_string), + len(hex_string) // 3)] diff --git a/tests/util/test_color.py b/tests/util/test_color.py index 79bbcc01495..f74a074bbe6 100644 --- a/tests/util/test_color.py +++ b/tests/util/test_color.py @@ -1,17 +1,14 @@ -""" -tests.util.test_color -~~~~~~~~~~~~~~~~~~~~~ - -Tests Home Assistant color util methods. -""" +"""Tests Home Assistant color util methods.""" import unittest import homeassistant.util.color as color_util class TestColorUtil(unittest.TestCase): + """Test color util methods.""" + # pylint: disable=invalid-name def test_color_RGB_to_xy(self): - """ Test color_RGB_to_xy. """ + """Test color_RGB_to_xy.""" self.assertEqual((0, 0), color_util.color_RGB_to_xy(0, 0, 0)) self.assertEqual((0.3127159072215825, 0.3290014805066623), color_util.color_RGB_to_xy(255, 255, 255)) @@ -25,7 +22,7 @@ class TestColorUtil(unittest.TestCase): color_util.color_RGB_to_xy(255, 0, 0)) def test_color_xy_brightness_to_RGB(self): - """ Test color_RGB_to_xy. """ + """Test color_RGB_to_xy.""" self.assertEqual((0, 0, 0), color_util.color_xy_brightness_to_RGB(1, 1, 0)) @@ -40,3 +37,23 @@ class TestColorUtil(unittest.TestCase): self.assertEqual((0, 83, 255), color_util.color_xy_brightness_to_RGB(0, 0, 255)) + + def test_rgb_hex_to_rgb_list(self): + """Test rgb_hex_to_rgb_list.""" + self.assertEqual([255, 255, 255], + color_util.rgb_hex_to_rgb_list('ffffff')) + + self.assertEqual([0, 0, 0], + color_util.rgb_hex_to_rgb_list('000000')) + + self.assertEqual([255, 255, 255, 255], + color_util.rgb_hex_to_rgb_list('ffffffff')) + + self.assertEqual([0, 0, 0, 0], + color_util.rgb_hex_to_rgb_list('00000000')) + + self.assertEqual([51, 153, 255], + color_util.rgb_hex_to_rgb_list('3399ff')) + + self.assertEqual([51, 153, 255, 0], + color_util.rgb_hex_to_rgb_list('3399ff00'))