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)]