From 03423cc3a9f2fc4cb1a5ba00c0d2cc042fc3b649 Mon Sep 17 00:00:00 2001 From: MartinHjelmare Date: Sat, 13 Feb 2016 18:32:13 +0100 Subject: [PATCH 1/3] Refactor mysensors light * Add a light entity class per V_LIGHT, V_DIMMER, V_RGB and V_RGBW. Make these classes inherit each other up to MySensorsLight class. * Map the entity classes to their S_TYPE in a dict. * Check if an entity class map or just an entity class have been passed to pf_callback_factory before using the entity_class variable in homeassistant/components/mysensors.py. * Add rgb_hex_to_list function in homeassistant/util/color.py. --- homeassistant/components/light/mysensors.py | 323 ++++++++++++++------ homeassistant/components/mysensors.py | 6 +- homeassistant/util/color.py | 17 +- 3 files changed, 244 insertions(+), 102 deletions(-) diff --git a/homeassistant/components/light/mysensors.py b/homeassistant/components/light/mysensors.py index a1f01fabf6b..f4e2d1a497f 100644 --- a/homeassistant/components/light/mysensors.py +++ b/homeassistant/components/light/mysensors.py @@ -12,6 +12,9 @@ import logging from homeassistant.components.light import ( Light, ATTR_BRIGHTNESS, ATTR_RGB_COLOR) +from homeassistant.util.color import ( + rgb_hex_to_list) + from homeassistant.const import ( ATTR_BATTERY_LEVEL, STATE_ON, STATE_OFF) @@ -38,6 +41,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: MySensorsLightLight, + 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 +52,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,9 +75,6 @@ class MySensorsLight(Light): self.battery_level = 0 self._values = {} self._state = None - self._rgb = None - self._brightness = None - self._white = None @property def should_poll(self): @@ -80,21 +86,6 @@ class MySensorsLight(Light): """The name of this entity.""" return self._name - @property - def brightness(self): - """Brightness of this light between 0..255.""" - return self._brightness - - @property - def rgb_color(self): - """RGB color value [int, int, int].""" - return self._rgb - - @property - def rgb_white(self): # not implemented in the frontend yet - """White value in RGBW, value between 0..255.""" - return self._white - @property def device_state_attributes(self): """Return device specific state attributes.""" @@ -108,6 +99,11 @@ 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.""" @@ -115,112 +111,247 @@ class MySensorsLight(Light): def turn_on(self, **kwargs): """Turn the device on.""" - set_req = self.gateway.const.SetReq - rgb = self._rgb - brightness = self._brightness - white = self._white + if self.gateway.optimistic: + # optimistically assume that light has changed state + self.update_ha_state() - if set_req.V_LIGHT in self._values and not self._state: + def turn_off(self, **kwargs): + """Turn the device off.""" + value_type = kwargs.get('value_type') + value = kwargs.get('value') + if value_type is not None and value is not None: + self.gateway.set_child_value( + self.node_id, self.child_id, value_type, value) + else: + _LOGGER.warning( + '%s: value_type %s, value = %s, ' + 'None is not valid argument when setting child value' + '', self._name, value_type, value) + if self.gateway.optimistic: + # optimistically assume that light has changed state + self._state = False + self.update_ha_state() + + def update(self): + """Update the controller with the latest value from a sensor.""" + node = self.gateway.sensors[self.node_id] + child = node.children[self.child_id] + 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) + self._values[value_type] = value + + +class MySensorsLightLight(MySensorsLight): + """Light child class to MySensorsLight.""" + + def __init__(self, *args): + """Setup instance attributes.""" + super().__init__(*args) + + def turn_on(self, **kwargs): + """Turn the device on.""" + set_req = self.gateway.const.SetReq + + if not self._state: 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 + super().turn_on(**kwargs) + + def turn_off(self, **kwargs): + """Turn the device off.""" + set_req = self.gateway.const.SetReq + value_type = kwargs.get('value_type') + value = kwargs.get('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 + super().turn_off(value_type=value_type, value=value) + + def update(self): + """Update the controller with the latest value from a sensor.""" + super().update() + 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 + + +class MySensorsLightDimmer(MySensorsLightLight): + """Dimmer child class to MySensorsLight.""" + + def __init__(self, *args): + """Setup instance attributes.""" + self._brightness = None + super().__init__(*args) + + @property + def brightness(self): + """Brightness of this light between 0..255.""" + return self._brightness + + def turn_on(self, **kwargs): + """Turn the device on.""" + 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 self.gateway.optimistic: + # optimistically assume that light has changed state + self._brightness = brightness + super().turn_on(**kwargs) + + def turn_off(self, **kwargs): + """Turn the device off.""" + set_req = self.gateway.const.SetReq + value_type = kwargs.get('value_type') + value = kwargs.get('value') + 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 + super().turn_off(value_type=value_type, value=value) + + def update(self): + """Update the controller with the latest value from a sensor.""" + super().update() + 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 + + +class MySensorsLightRGB(MySensorsLightDimmer): + """RGB child class to MySensorsLight.""" + + def __init__(self, *args): + """Setup instance attributes.""" + self._rgb = None + super().__init__(*args) + + @property + def rgb_color(self): + """RGB color value [int, int, int].""" + return self._rgb + + def turn_on(self, **kwargs): + """Turn the device on.""" + rgb = self._rgb + if ATTR_RGB_COLOR in kwargs and kwargs[ATTR_RGB_COLOR] != self._rgb: + rgb = kwargs[ATTR_RGB_COLOR] + hex_color = '%02x%02x%02x' % 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._rgb = rgb + super().turn_on(**kwargs) + + def turn_off(self, **kwargs): + """Turn the device off.""" + value_type = None + value = None + if float(self.gateway.version) >= 1.5: + value_type = self.gateway.const.SetReq.V_RGB + value = '000000' + super().turn_off(value_type=value_type, value=value) + + def update(self): + """Update the controller with the latest value from a sensor.""" + super().update() + set_req = self.gateway.const.SetReq + if float(self.gateway.version) >= 1.5 and \ + set_req.V_RGB in self._values: + value = self._values[set_req.V_RGB] + self._rgb = rgb_hex_to_list(value) + if set_req.V_LIGHT not in self._values and \ + set_req.V_DIMMER not in self._values: + self._state = max(self._rgb) > 0 + + +class MySensorsLightRGBW(MySensorsLightDimmer): + """RGBW child class to MySensorsLight.""" + + def __init__(self, *args): + """Setup instance attributes.""" + self._rgb = None + self._white = None + super().__init__(*args) + + @property + def rgb_color(self): + """RGB color value [int, int, int].""" + return self._rgb + + @property + def rgb_white(self): # not implemented in the frontend yet + """White value in RGBW, value between 0..255.""" + return self._white + + def turn_on(self, **kwargs): + """Turn the device on.""" + rgb = self._rgb + white = self._white + if float(self.gateway.version) >= 1.5: 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] 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) + if white is not None: + rgb.append(white) + hex_color = '%02x%02x%02x%02x' % 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() + super().turn_on(**kwargs) def turn_off(self, **kwargs): """Turn the device off.""" - 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) - - 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 + value_type = None + value = None + if float(self.gateway.version) >= 1.5: + value_type = self.gateway.const.SetReq.V_RGBW + value = '00000000' + super().turn_off(value_type=value_type, value=value) def update(self): """Update the controller with the latest value from a sensor.""" - node = self.gateway.sensors[self.node_id] - child = node.children[self.child_id] + super().update() 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 + if float(self.gateway.version) >= 1.5 and \ + set_req.V_RGBW in self._values: + value = self._values[set_req.V_RGBW] + color_list = rgb_hex_to_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 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/util/color.py b/homeassistant/util/color.py index e5b11f2afeb..db4475b7087 100644 --- a/homeassistant/util/color.py +++ b/homeassistant/util/color.py @@ -1,5 +1,6 @@ """ -homeassistant.util.color +homeassistant.util.color. + ~~~~~~~~~~~~~~~~~~~~~~~~ Color util methods. """ @@ -9,7 +10,7 @@ Color util methods. # 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 +51,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 +87,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_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)] From 6e8c79d5310396987cf39d65ea40279abb799754 Mon Sep 17 00:00:00 2001 From: MartinHjelmare Date: Mon, 15 Feb 2016 03:47:51 +0100 Subject: [PATCH 2/3] Change refactor structure * Make a flatter one level inheritance, with MySensorsLight as parent with four children, one per light type. * Break out helper methods. One per plain light, dimmer and RGB/RGBW children and per update, turn_on and turn_off, nine in total. Put these in the parent. * Call the helper methods as needed from the child methods update, turn_on and turn_off. * Change name of MySensorsLightLight to MySensorsLightPlain. * Fix module docstrings according to pep257. * Change name of color util method from rgb_hex_to_list to rgb_hex_to_rgb_list. * Add unit tests for rgb_hex_to_rgb_list. --- homeassistant/components/light/mysensors.py | 391 ++++++++++--------- homeassistant/components/sensor/mysensors.py | 3 - homeassistant/components/switch/mysensors.py | 5 +- homeassistant/util/color.py | 9 +- tests/util/test_color.py | 33 +- 5 files changed, 237 insertions(+), 204 deletions(-) diff --git a/homeassistant/components/light/mysensors.py b/homeassistant/components/light/mysensors.py index f4e2d1a497f..5cc8a4648d5 100644 --- a/homeassistant/components/light/mysensors.py +++ b/homeassistant/components/light/mysensors.py @@ -1,11 +1,8 @@ """ -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 @@ -13,7 +10,7 @@ from homeassistant.components.light import ( Light, ATTR_BRIGHTNESS, ATTR_RGB_COLOR) from homeassistant.util.color import ( - rgb_hex_to_list) + rgb_hex_to_rgb_list) from homeassistant.const import ( ATTR_BATTERY_LEVEL, @@ -23,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): @@ -42,7 +41,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): pres.S_DIMMER: [set_req.V_DIMMER], } device_class_map = { - pres.S_LIGHT: MySensorsLightLight, + pres.S_LIGHT: MySensorsLightPlain, pres.S_DIMMER: MySensorsLightDimmer, } if float(gateway.version) >= 1.5: @@ -75,6 +74,9 @@ class MySensorsLight(Light): self.battery_level = 0 self._values = {} self._state = None + self._brightness = None + self._rgb = None + self._white = None @property def should_poll(self): @@ -86,6 +88,21 @@ class MySensorsLight(Light): """The name of this entity.""" return self._name + @property + def brightness(self): + """Brightness of this light between 0..255.""" + return self._brightness + + @property + def rgb_color(self): + """RGB color value [int, int, int].""" + return self._rgb + + @property + def rgb_white(self): # not implemented in the frontend yet + """White value in RGBW, value between 0..255.""" + return self._white + @property def device_state_attributes(self): """Return device specific state attributes.""" @@ -109,49 +126,8 @@ class MySensorsLight(Light): """True if device is on.""" return self._state - def turn_on(self, **kwargs): - """Turn the device on.""" - if self.gateway.optimistic: - # optimistically assume that light has changed state - self.update_ha_state() - - def turn_off(self, **kwargs): - """Turn the device off.""" - value_type = kwargs.get('value_type') - value = kwargs.get('value') - if value_type is not None and value is not None: - self.gateway.set_child_value( - self.node_id, self.child_id, value_type, value) - else: - _LOGGER.warning( - '%s: value_type %s, value = %s, ' - 'None is not valid argument when setting child value' - '', self._name, value_type, value) - if self.gateway.optimistic: - # optimistically assume that light has changed state - self._state = False - self.update_ha_state() - - def update(self): - """Update the controller with the latest value from a sensor.""" - node = self.gateway.sensors[self.node_id] - child = node.children[self.child_id] - 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) - self._values[value_type] = value - - -class MySensorsLightLight(MySensorsLight): - """Light child class to MySensorsLight.""" - - def __init__(self, *args): - """Setup instance attributes.""" - super().__init__(*args) - - 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 if not self._state: @@ -161,44 +137,9 @@ class MySensorsLightLight(MySensorsLight): if self.gateway.optimistic: # optimistically assume that light has changed state self._state = True - super().turn_on(**kwargs) - def turn_off(self, **kwargs): - """Turn the device off.""" - set_req = self.gateway.const.SetReq - value_type = kwargs.get('value_type') - value = kwargs.get('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 - super().turn_off(value_type=value_type, value=value) - - def update(self): - """Update the controller with the latest value from a sensor.""" - super().update() - 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 - - -class MySensorsLightDimmer(MySensorsLightLight): - """Dimmer child class to MySensorsLight.""" - - def __init__(self, *args): - """Setup instance attributes.""" - self._brightness = None - super().__init__(*args) - - @property - def brightness(self): - """Brightness of this light between 0..255.""" - return self._brightness - - def turn_on(self, **kwargs): - """Turn the device on.""" + def _turn_on_dimmer(self, **kwargs): + """Turn on dimmer child device.""" set_req = self.gateway.const.SetReq brightness = self._brightness @@ -212,22 +153,89 @@ class MySensorsLightDimmer(MySensorsLightLight): if self.gateway.optimistic: # optimistically assume that light has changed state self._brightness = brightness - super().turn_on(**kwargs) - def turn_off(self, **kwargs): - """Turn the device off.""" + 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_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._rgb = rgb + self._white = white + + def _turn_on(self): + """Turn the device on.""" + if self.gateway.optimistic: + # optimistically assume that light has changed state + self.update_ha_state() + + def _turn_off_light(self, value_type=None, value=None): + """Turn off light child device.""" + set_req = self.gateway.const.SetReq + 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 = kwargs.get('value_type') - value = kwargs.get('value') 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 - super().turn_off(value_type=value_type, value=value) + return {ATTR_VALUE_TYPE: value_type, ATTR_VALUE: value} - def update(self): - """Update the controller with the latest value from a sensor.""" - super().update() + 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(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() + + 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_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: @@ -237,121 +245,140 @@ class MySensorsLightDimmer(MySensorsLightLight): 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 -class MySensorsLightRGB(MySensorsLightDimmer): + def _update(self): + """Update the controller with the latest value from a sensor.""" + node = self.gateway.sensors[self.node_id] + child = node.children[self.child_id] + 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) + self._values[value_type] = value + + +class MySensorsLightPlain(MySensorsLight): + """Light child class to MySensorsLight.""" + + def __init__(self, *args): + """Setup instance attributes.""" + super().__init__(*args) + + def turn_on(self, **kwargs): + """Turn the device on.""" + super()._turn_on_light() + super()._turn_on() + + def turn_off(self, **kwargs): + """Turn the device off.""" + ret = super()._turn_off_light() + super()._turn_off(value_type=ret[ + ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) + + def update(self): + """Update the controller with the latest value from a sensor.""" + super()._update() + super()._update_light() + + +class MySensorsLightDimmer(MySensorsLight): + """Dimmer child class to MySensorsLight.""" + + def __init__(self, *args): + """Setup instance attributes.""" + super().__init__(*args) + + def turn_on(self, **kwargs): + """Turn the device on.""" + super()._turn_on_light() + super()._turn_on_dimmer(**kwargs) + super()._turn_on() + + def turn_off(self, **kwargs): + """Turn the device off.""" + ret = super()._turn_off_dimmer() + ret = super()._turn_off_light( + value_type=ret[ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) + super()._turn_off( + value_type=ret[ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) + + def update(self): + """Update the controller with the latest value from a sensor.""" + super()._update() + super()._update_light() + super()._update_dimmer() + + +class MySensorsLightRGB(MySensorsLight): """RGB child class to MySensorsLight.""" def __init__(self, *args): """Setup instance attributes.""" - self._rgb = None super().__init__(*args) - @property - def rgb_color(self): - """RGB color value [int, int, int].""" - return self._rgb - def turn_on(self, **kwargs): """Turn the device on.""" - rgb = self._rgb - if ATTR_RGB_COLOR in kwargs and kwargs[ATTR_RGB_COLOR] != self._rgb: - rgb = kwargs[ATTR_RGB_COLOR] - hex_color = '%02x%02x%02x' % 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._rgb = rgb - super().turn_on(**kwargs) + super()._turn_on_light() + super()._turn_on_dimmer(**kwargs) + super()._turn_on_rgb_and_w('%02x%02x%02x', **kwargs) + super()._turn_on() def turn_off(self, **kwargs): """Turn the device off.""" - value_type = None - value = None - if float(self.gateway.version) >= 1.5: - value_type = self.gateway.const.SetReq.V_RGB - value = '000000' - super().turn_off(value_type=value_type, value=value) + ret = super()._turn_off_rgb_or_w() + ret = super()._turn_off_dimmer( + value_type=ret[ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) + ret = super()._turn_off_light( + value_type=ret[ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) + super()._turn_off( + value_type=ret[ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) def update(self): """Update the controller with the latest value from a sensor.""" - super().update() - set_req = self.gateway.const.SetReq - if float(self.gateway.version) >= 1.5 and \ - set_req.V_RGB in self._values: - value = self._values[set_req.V_RGB] - self._rgb = rgb_hex_to_list(value) - if set_req.V_LIGHT not in self._values and \ - set_req.V_DIMMER not in self._values: - self._state = max(self._rgb) > 0 + super()._update() + super()._update_light() + super()._update_dimmer() + super()._update_rgb_or_w() -class MySensorsLightRGBW(MySensorsLightDimmer): +class MySensorsLightRGBW(MySensorsLight): """RGBW child class to MySensorsLight.""" def __init__(self, *args): """Setup instance attributes.""" - self._rgb = None - self._white = None super().__init__(*args) - @property - def rgb_color(self): - """RGB color value [int, int, int].""" - return self._rgb - - @property - def rgb_white(self): # not implemented in the frontend yet - """White value in RGBW, value between 0..255.""" - return self._white - def turn_on(self, **kwargs): """Turn the device on.""" - rgb = self._rgb - white = self._white - - if float(self.gateway.version) >= 1.5: - - 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: - rgb.append(white) - hex_color = '%02x%02x%02x%02x' % 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._rgb = rgb - self._white = white - super().turn_on(**kwargs) + super()._turn_on_light() + super()._turn_on_dimmer(**kwargs) + super()._turn_on_rgb_and_w('%02x%02x%02x%02x', **kwargs) + super()._turn_on() def turn_off(self, **kwargs): """Turn the device off.""" - value_type = None - value = None - if float(self.gateway.version) >= 1.5: - value_type = self.gateway.const.SetReq.V_RGBW - value = '00000000' - super().turn_off(value_type=value_type, value=value) + ret = super()._turn_off_rgb_or_w() + ret = super()._turn_off_dimmer( + value_type=ret[ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) + ret = super()._turn_off_light( + value_type=ret[ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) + super()._turn_off( + value_type=ret[ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) def update(self): """Update the controller with the latest value from a sensor.""" - super().update() - set_req = self.gateway.const.SetReq - if float(self.gateway.version) >= 1.5 and \ - set_req.V_RGBW in self._values: - value = self._values[set_req.V_RGBW] - color_list = rgb_hex_to_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 + super()._update() + super()._update_light() + super()._update_dimmer() + super()._update_rgb_or_w() 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 db4475b7087..1806d9058dc 100644 --- a/homeassistant/util/color.py +++ b/homeassistant/util/color.py @@ -1,9 +1,4 @@ -""" -homeassistant.util.color. - -~~~~~~~~~~~~~~~~~~~~~~~~ -Color util methods. -""" +"""Color util methods.""" # Taken from: http://www.cse.unr.edu/~quiroz/inc/colortransforms.py @@ -89,7 +84,7 @@ def color_xy_brightness_to_RGB(vX, vY, brightness): return (r, g, b) -def rgb_hex_to_list(hex_string): +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, 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')) From 9ac53b502ffec1b1c4befb1b63fc0d254e96ee42 Mon Sep 17 00:00:00 2001 From: MartinHjelmare Date: Thu, 18 Feb 2016 01:28:03 +0100 Subject: [PATCH 3/3] Clean up and fix * Add check if V_LIGHT is in values before sending message in _turn_on_light. * Replace super calls with self. * Remove not needed init method in child classes. * Remove turn_on method in parent class and add update_ha_state to _turn_on_light, _turn_on_dimmer and _turn_on_rgb_or_w. --- homeassistant/components/light/mysensors.py | 103 ++++++++------------ 1 file changed, 40 insertions(+), 63 deletions(-) diff --git a/homeassistant/components/light/mysensors.py b/homeassistant/components/light/mysensors.py index 5cc8a4648d5..b50446e2f38 100644 --- a/homeassistant/components/light/mysensors.py +++ b/homeassistant/components/light/mysensors.py @@ -130,13 +130,14 @@ class MySensorsLight(Light): """Turn on light child device.""" set_req = self.gateway.const.SetReq - if 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 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.""" @@ -153,6 +154,7 @@ class MySensorsLight(Light): if self.gateway.optimistic: # optimistically assume that light has changed state self._brightness = brightness + self.update_ha_state() def _turn_on_rgb_and_w(self, hex_template, **kwargs): """Turn on RGB or RGBW child device.""" @@ -176,11 +178,6 @@ class MySensorsLight(Light): # optimistically assume that light has changed state self._rgb = rgb self._white = white - - def _turn_on(self): - """Turn the device on.""" - if self.gateway.optimistic: - # optimistically assume that light has changed state self.update_ha_state() def _turn_off_light(self, value_type=None, value=None): @@ -211,7 +208,7 @@ class MySensorsLight(Light): value = '00000000' return {ATTR_VALUE_TYPE: self.value_type, ATTR_VALUE: value} - def _turn_off(self, value_type=None, value=None): + 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( @@ -257,7 +254,7 @@ class MySensorsLight(Light): self._white = color_list.pop() self._rgb = color_list - def _update(self): + 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] @@ -271,114 +268,94 @@ class MySensorsLight(Light): class MySensorsLightPlain(MySensorsLight): """Light child class to MySensorsLight.""" - def __init__(self, *args): - """Setup instance attributes.""" - super().__init__(*args) - def turn_on(self, **kwargs): """Turn the device on.""" - super()._turn_on_light() - super()._turn_on() + self._turn_on_light() def turn_off(self, **kwargs): """Turn the device off.""" - ret = super()._turn_off_light() - super()._turn_off(value_type=ret[ + 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.""" - super()._update() - super()._update_light() + self._update_main() + self._update_light() class MySensorsLightDimmer(MySensorsLight): """Dimmer child class to MySensorsLight.""" - def __init__(self, *args): - """Setup instance attributes.""" - super().__init__(*args) - def turn_on(self, **kwargs): """Turn the device on.""" - super()._turn_on_light() - super()._turn_on_dimmer(**kwargs) - super()._turn_on() + self._turn_on_light() + self._turn_on_dimmer(**kwargs) def turn_off(self, **kwargs): """Turn the device off.""" - ret = super()._turn_off_dimmer() - ret = super()._turn_off_light( + ret = self._turn_off_dimmer() + ret = self._turn_off_light( value_type=ret[ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) - super()._turn_off( + 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.""" - super()._update() - super()._update_light() - super()._update_dimmer() + self._update_main() + self._update_light() + self._update_dimmer() class MySensorsLightRGB(MySensorsLight): """RGB child class to MySensorsLight.""" - def __init__(self, *args): - """Setup instance attributes.""" - super().__init__(*args) - def turn_on(self, **kwargs): """Turn the device on.""" - super()._turn_on_light() - super()._turn_on_dimmer(**kwargs) - super()._turn_on_rgb_and_w('%02x%02x%02x', **kwargs) - super()._turn_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 = super()._turn_off_rgb_or_w() - ret = super()._turn_off_dimmer( + ret = self._turn_off_rgb_or_w() + ret = self._turn_off_dimmer( value_type=ret[ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) - ret = super()._turn_off_light( + ret = self._turn_off_light( value_type=ret[ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) - super()._turn_off( + 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.""" - super()._update() - super()._update_light() - super()._update_dimmer() - super()._update_rgb_or_w() + self._update_main() + self._update_light() + self._update_dimmer() + self._update_rgb_or_w() class MySensorsLightRGBW(MySensorsLight): """RGBW child class to MySensorsLight.""" - def __init__(self, *args): - """Setup instance attributes.""" - super().__init__(*args) - def turn_on(self, **kwargs): """Turn the device on.""" - super()._turn_on_light() - super()._turn_on_dimmer(**kwargs) - super()._turn_on_rgb_and_w('%02x%02x%02x%02x', **kwargs) - super()._turn_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 = super()._turn_off_rgb_or_w() - ret = super()._turn_off_dimmer( + ret = self._turn_off_rgb_or_w() + ret = self._turn_off_dimmer( value_type=ret[ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) - ret = super()._turn_off_light( + ret = self._turn_off_light( value_type=ret[ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) - super()._turn_off( + 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.""" - super()._update() - super()._update_light() - super()._update_dimmer() - super()._update_rgb_or_w() + self._update_main() + self._update_light() + self._update_dimmer() + self._update_rgb_or_w()