hass-core/homeassistant/components/mysensors/light.py
petewill d1b4bd22ce Add MySensors ACK (#26894)
* Add MySensors ACK

The addition of the ACK will ask sensors to respond to commands sent to them which will update the MySensors device in Home Assistant.
 Currently, if a default MySensors sketch is used the device will update but Home Assistant does not reflect the update and custom code has to be added to tell Home Assistant the command was received.  With the ACK set to 1 in the message all this is taken care of by MySensors.

* Run black
2019-09-25 23:20:02 +02:00

243 lines
7.9 KiB
Python

"""Support for MySensors lights."""
from homeassistant.components import mysensors
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_HS_COLOR,
ATTR_WHITE_VALUE,
DOMAIN,
SUPPORT_BRIGHTNESS,
SUPPORT_COLOR,
SUPPORT_WHITE_VALUE,
Light,
)
from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.util.color import rgb_hex_to_rgb_list
import homeassistant.util.color as color_util
SUPPORT_MYSENSORS_RGBW = SUPPORT_COLOR | SUPPORT_WHITE_VALUE
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the mysensors platform for lights."""
device_class_map = {
"S_DIMMER": MySensorsLightDimmer,
"S_RGB_LIGHT": MySensorsLightRGB,
"S_RGBW_LIGHT": MySensorsLightRGBW,
}
mysensors.setup_mysensors_platform(
hass,
DOMAIN,
discovery_info,
device_class_map,
async_add_entities=async_add_entities,
)
class MySensorsLight(mysensors.device.MySensorsEntity, Light):
"""Representation of a MySensors Light child node."""
def __init__(self, *args):
"""Initialize a MySensors Light."""
super().__init__(*args)
self._state = None
self._brightness = None
self._hs = None
self._white = None
@property
def brightness(self):
"""Return the brightness of this light between 0..255."""
return self._brightness
@property
def hs_color(self):
"""Return the hs color value [int, int]."""
return self._hs
@property
def white_value(self):
"""Return the white value of this light between 0..255."""
return self._white
@property
def assumed_state(self):
"""Return true if unable to access real state of entity."""
return self.gateway.optimistic
@property
def is_on(self):
"""Return true if device is on."""
return self._state
def _turn_on_light(self):
"""Turn on light child device."""
set_req = self.gateway.const.SetReq
if self._state:
return
self.gateway.set_child_value(
self.node_id, self.child_id, set_req.V_LIGHT, 1, ack=1
)
if self.gateway.optimistic:
# optimistically assume that light has changed state
self._state = True
self._values[set_req.V_LIGHT] = STATE_ON
def _turn_on_dimmer(self, **kwargs):
"""Turn on dimmer child device."""
set_req = self.gateway.const.SetReq
brightness = self._brightness
if (
ATTR_BRIGHTNESS not in kwargs
or kwargs[ATTR_BRIGHTNESS] == self._brightness
or set_req.V_DIMMER not in self._values
):
return
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, ack=1
)
if self.gateway.optimistic:
# optimistically assume that light has changed state
self._brightness = brightness
self._values[set_req.V_DIMMER] = percent
def _turn_on_rgb_and_w(self, hex_template, **kwargs):
"""Turn on RGB or RGBW child device."""
rgb = list(color_util.color_hs_to_RGB(*self._hs))
white = self._white
hex_color = self._values.get(self.value_type)
hs_color = kwargs.get(ATTR_HS_COLOR)
if hs_color is not None:
new_rgb = color_util.color_hs_to_RGB(*hs_color)
else:
new_rgb = None
new_white = kwargs.get(ATTR_WHITE_VALUE)
if new_rgb is None and new_white is None:
return
if new_rgb is not None:
rgb = list(new_rgb)
if hex_template == "%02x%02x%02x%02x":
if new_white is not None:
rgb.append(new_white)
else:
rgb.append(white)
hex_color = hex_template % tuple(rgb)
if len(rgb) > 3:
white = rgb.pop()
self.gateway.set_child_value(
self.node_id, self.child_id, self.value_type, hex_color, ack=1
)
if self.gateway.optimistic:
# optimistically assume that light has changed state
self._hs = color_util.color_RGB_to_hs(*rgb)
self._white = white
self._values[self.value_type] = hex_color
async def async_turn_off(self, **kwargs):
"""Turn the device off."""
value_type = self.gateway.const.SetReq.V_LIGHT
self.gateway.set_child_value(self.node_id, self.child_id, value_type, 0, ack=1)
if self.gateway.optimistic:
# optimistically assume that light has changed state
self._state = False
self._values[value_type] = STATE_OFF
self.async_schedule_update_ha_state()
def _async_update_light(self):
"""Update the controller with values from light child."""
value_type = self.gateway.const.SetReq.V_LIGHT
self._state = self._values[value_type] == STATE_ON
def _async_update_dimmer(self):
"""Update the controller with values from dimmer child."""
value_type = self.gateway.const.SetReq.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
def _async_update_rgb_or_w(self):
"""Update the controller with values from RGB or RGBW child."""
value = self._values[self.value_type]
color_list = rgb_hex_to_rgb_list(value)
if len(color_list) > 3:
self._white = color_list.pop()
self._hs = color_util.color_RGB_to_hs(*color_list)
class MySensorsLightDimmer(MySensorsLight):
"""Dimmer child class to MySensorsLight."""
@property
def supported_features(self):
"""Flag supported features."""
return SUPPORT_BRIGHTNESS
async def async_turn_on(self, **kwargs):
"""Turn the device on."""
self._turn_on_light()
self._turn_on_dimmer(**kwargs)
if self.gateway.optimistic:
self.async_schedule_update_ha_state()
async def async_update(self):
"""Update the controller with the latest value from a sensor."""
await super().async_update()
self._async_update_light()
self._async_update_dimmer()
class MySensorsLightRGB(MySensorsLight):
"""RGB child class to MySensorsLight."""
@property
def supported_features(self):
"""Flag supported features."""
set_req = self.gateway.const.SetReq
if set_req.V_DIMMER in self._values:
return SUPPORT_BRIGHTNESS | SUPPORT_COLOR
return SUPPORT_COLOR
async def async_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)
if self.gateway.optimistic:
self.async_schedule_update_ha_state()
async def async_update(self):
"""Update the controller with the latest value from a sensor."""
await super().async_update()
self._async_update_light()
self._async_update_dimmer()
self._async_update_rgb_or_w()
class MySensorsLightRGBW(MySensorsLightRGB):
"""RGBW child class to MySensorsLightRGB."""
# pylint: disable=too-many-ancestors
@property
def supported_features(self):
"""Flag supported features."""
set_req = self.gateway.const.SetReq
if set_req.V_DIMMER in self._values:
return SUPPORT_BRIGHTNESS | SUPPORT_MYSENSORS_RGBW
return SUPPORT_MYSENSORS_RGBW
async def async_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)
if self.gateway.optimistic:
self.async_schedule_update_ha_state()