Z-wave would drop the floating point by calling int() instead of round() which would result in the brightness being off by one in many cases.
464 lines
15 KiB
Python
464 lines
15 KiB
Python
"""Test Z-Wave lights."""
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from homeassistant.components import zwave
|
|
from homeassistant.components.light import (
|
|
ATTR_BRIGHTNESS,
|
|
ATTR_COLOR_TEMP,
|
|
ATTR_HS_COLOR,
|
|
ATTR_TRANSITION,
|
|
ATTR_WHITE_VALUE,
|
|
SUPPORT_BRIGHTNESS,
|
|
SUPPORT_COLOR,
|
|
SUPPORT_COLOR_TEMP,
|
|
SUPPORT_TRANSITION,
|
|
SUPPORT_WHITE_VALUE,
|
|
)
|
|
from homeassistant.components.zwave import const, light
|
|
|
|
from tests.mock.zwave import MockEntityValues, MockNode, MockValue, value_changed
|
|
|
|
|
|
class MockLightValues(MockEntityValues):
|
|
"""Mock Z-Wave light values."""
|
|
|
|
def __init__(self, **kwargs):
|
|
"""Initialize the mock zwave values."""
|
|
self.dimming_duration = None
|
|
self.color = None
|
|
self.color_channels = None
|
|
super().__init__(**kwargs)
|
|
|
|
|
|
def test_get_device_detects_dimmer(mock_openzwave):
|
|
"""Test get_device returns a normal dimmer."""
|
|
node = MockNode()
|
|
value = MockValue(data=0, node=node)
|
|
values = MockLightValues(primary=value)
|
|
|
|
device = light.get_device(node=node, values=values, node_config={})
|
|
assert isinstance(device, light.ZwaveDimmer)
|
|
assert device.supported_features == SUPPORT_BRIGHTNESS
|
|
|
|
|
|
def test_get_device_detects_colorlight(mock_openzwave):
|
|
"""Test get_device returns a color light."""
|
|
node = MockNode(command_classes=[const.COMMAND_CLASS_SWITCH_COLOR])
|
|
value = MockValue(data=0, node=node)
|
|
values = MockLightValues(primary=value)
|
|
|
|
device = light.get_device(node=node, values=values, node_config={})
|
|
assert isinstance(device, light.ZwaveColorLight)
|
|
assert device.supported_features == SUPPORT_BRIGHTNESS | SUPPORT_COLOR
|
|
|
|
|
|
def test_get_device_detects_zw098(mock_openzwave):
|
|
"""Test get_device returns a zw098 color light."""
|
|
node = MockNode(
|
|
manufacturer_id="0086",
|
|
product_id="0062",
|
|
command_classes=[const.COMMAND_CLASS_SWITCH_COLOR],
|
|
)
|
|
value = MockValue(data=0, node=node)
|
|
values = MockLightValues(primary=value)
|
|
device = light.get_device(node=node, values=values, node_config={})
|
|
assert isinstance(device, light.ZwaveColorLight)
|
|
assert device.supported_features == (
|
|
SUPPORT_BRIGHTNESS | SUPPORT_COLOR | SUPPORT_COLOR_TEMP
|
|
)
|
|
|
|
|
|
def test_get_device_detects_rgbw_light(mock_openzwave):
|
|
"""Test get_device returns a color light."""
|
|
node = MockNode(command_classes=[const.COMMAND_CLASS_SWITCH_COLOR])
|
|
value = MockValue(data=0, node=node)
|
|
color = MockValue(data="#0000000000", node=node)
|
|
color_channels = MockValue(data=0x1D, node=node)
|
|
values = MockLightValues(primary=value, color=color, color_channels=color_channels)
|
|
|
|
device = light.get_device(node=node, values=values, node_config={})
|
|
device.value_added()
|
|
assert isinstance(device, light.ZwaveColorLight)
|
|
assert device.supported_features == (
|
|
SUPPORT_BRIGHTNESS | SUPPORT_COLOR | SUPPORT_WHITE_VALUE
|
|
)
|
|
|
|
|
|
def test_dimmer_turn_on(mock_openzwave):
|
|
"""Test turning on a dimmable Z-Wave light."""
|
|
node = MockNode()
|
|
value = MockValue(data=0, node=node)
|
|
values = MockLightValues(primary=value)
|
|
device = light.get_device(node=node, values=values, node_config={})
|
|
|
|
device.turn_on()
|
|
|
|
assert node.set_dimmer.called
|
|
value_id, brightness = node.set_dimmer.mock_calls[0][1]
|
|
assert value_id == value.value_id
|
|
assert brightness == 255
|
|
|
|
node.reset_mock()
|
|
|
|
device.turn_on(**{ATTR_BRIGHTNESS: 224})
|
|
|
|
assert node.set_dimmer.called
|
|
value_id, brightness = node.set_dimmer.mock_calls[0][1]
|
|
|
|
assert value_id == value.value_id
|
|
assert brightness == 87 # round(224 / 255 * 99)
|
|
|
|
node.reset_mock()
|
|
|
|
device.turn_on(**{ATTR_BRIGHTNESS: 120})
|
|
|
|
assert node.set_dimmer.called
|
|
value_id, brightness = node.set_dimmer.mock_calls[0][1]
|
|
|
|
assert value_id == value.value_id
|
|
assert brightness == 47 # round(120 / 255 * 99)
|
|
|
|
with patch.object(light, "_LOGGER", MagicMock()) as mock_logger:
|
|
device.turn_on(**{ATTR_TRANSITION: 35})
|
|
assert mock_logger.debug.called
|
|
assert node.set_dimmer.called
|
|
msg, entity_id = mock_logger.debug.mock_calls[0][1]
|
|
assert entity_id == device.entity_id
|
|
|
|
|
|
def test_dimmer_min_brightness(mock_openzwave):
|
|
"""Test turning on a dimmable Z-Wave light to its minimum brightness."""
|
|
node = MockNode()
|
|
value = MockValue(data=0, node=node)
|
|
values = MockLightValues(primary=value)
|
|
device = light.get_device(node=node, values=values, node_config={})
|
|
|
|
assert not device.is_on
|
|
|
|
device.turn_on(**{ATTR_BRIGHTNESS: 1})
|
|
|
|
assert device.is_on
|
|
assert device.brightness == 1
|
|
|
|
device.turn_on(**{ATTR_BRIGHTNESS: 0})
|
|
|
|
assert device.is_on
|
|
assert device.brightness == 0
|
|
|
|
|
|
def test_dimmer_transitions(mock_openzwave):
|
|
"""Test dimming transition on a dimmable Z-Wave light."""
|
|
node = MockNode()
|
|
value = MockValue(data=0, node=node)
|
|
duration = MockValue(data=0, node=node)
|
|
values = MockLightValues(primary=value, dimming_duration=duration)
|
|
device = light.get_device(node=node, values=values, node_config={})
|
|
assert device.supported_features == SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION
|
|
|
|
# Test turn_on
|
|
# Factory Default
|
|
device.turn_on()
|
|
assert duration.data == 0xFF
|
|
|
|
# Seconds transition
|
|
device.turn_on(**{ATTR_TRANSITION: 45})
|
|
assert duration.data == 45
|
|
|
|
# Minutes transition
|
|
device.turn_on(**{ATTR_TRANSITION: 245})
|
|
assert duration.data == 0x83
|
|
|
|
# Clipped transition
|
|
device.turn_on(**{ATTR_TRANSITION: 10000})
|
|
assert duration.data == 0xFE
|
|
|
|
# Test turn_off
|
|
# Factory Default
|
|
device.turn_off()
|
|
assert duration.data == 0xFF
|
|
|
|
# Seconds transition
|
|
device.turn_off(**{ATTR_TRANSITION: 45})
|
|
assert duration.data == 45
|
|
|
|
# Minutes transition
|
|
device.turn_off(**{ATTR_TRANSITION: 245})
|
|
assert duration.data == 0x83
|
|
|
|
# Clipped transition
|
|
device.turn_off(**{ATTR_TRANSITION: 10000})
|
|
assert duration.data == 0xFE
|
|
|
|
|
|
def test_dimmer_turn_off(mock_openzwave):
|
|
"""Test turning off a dimmable Z-Wave light."""
|
|
node = MockNode()
|
|
value = MockValue(data=46, node=node)
|
|
values = MockLightValues(primary=value)
|
|
device = light.get_device(node=node, values=values, node_config={})
|
|
|
|
device.turn_off()
|
|
|
|
assert node.set_dimmer.called
|
|
value_id, brightness = node.set_dimmer.mock_calls[0][1]
|
|
assert value_id == value.value_id
|
|
assert brightness == 0
|
|
|
|
|
|
def test_dimmer_value_changed(mock_openzwave):
|
|
"""Test value changed for dimmer lights."""
|
|
node = MockNode()
|
|
value = MockValue(data=0, node=node)
|
|
values = MockLightValues(primary=value)
|
|
device = light.get_device(node=node, values=values, node_config={})
|
|
|
|
assert not device.is_on
|
|
|
|
value.data = 46
|
|
value_changed(value)
|
|
|
|
assert device.is_on
|
|
assert device.brightness == 118
|
|
|
|
|
|
def test_dimmer_refresh_value(mock_openzwave):
|
|
"""Test value changed for dimmer lights."""
|
|
node = MockNode()
|
|
value = MockValue(data=0, node=node)
|
|
values = MockLightValues(primary=value)
|
|
device = light.get_device(
|
|
node=node,
|
|
values=values,
|
|
node_config={zwave.CONF_REFRESH_VALUE: True, zwave.CONF_REFRESH_DELAY: 5},
|
|
)
|
|
|
|
assert not device.is_on
|
|
|
|
with patch.object(light, "Timer", MagicMock()) as mock_timer:
|
|
value.data = 46
|
|
value_changed(value)
|
|
|
|
assert not device.is_on
|
|
assert mock_timer.called
|
|
assert len(mock_timer.mock_calls) == 2
|
|
timeout, callback = mock_timer.mock_calls[0][1][:2]
|
|
assert timeout == 5
|
|
assert mock_timer().start.called
|
|
assert len(mock_timer().start.mock_calls) == 1
|
|
|
|
with patch.object(light, "Timer", MagicMock()) as mock_timer_2:
|
|
value_changed(value)
|
|
assert not device.is_on
|
|
assert mock_timer().cancel.called
|
|
assert len(mock_timer_2.mock_calls) == 2
|
|
timeout, callback = mock_timer_2.mock_calls[0][1][:2]
|
|
assert timeout == 5
|
|
assert mock_timer_2().start.called
|
|
assert len(mock_timer_2().start.mock_calls) == 1
|
|
|
|
callback()
|
|
assert device.is_on
|
|
assert device.brightness == 118
|
|
|
|
|
|
def test_set_hs_color(mock_openzwave):
|
|
"""Test setting zwave light color."""
|
|
node = MockNode(command_classes=[const.COMMAND_CLASS_SWITCH_COLOR])
|
|
value = MockValue(data=0, node=node)
|
|
color = MockValue(data="#0000000000", node=node)
|
|
# Supports RGB only
|
|
color_channels = MockValue(data=0x1C, node=node)
|
|
values = MockLightValues(primary=value, color=color, color_channels=color_channels)
|
|
device = light.get_device(node=node, values=values, node_config={})
|
|
|
|
assert color.data == "#0000000000"
|
|
|
|
device.turn_on(**{ATTR_HS_COLOR: (30, 50)})
|
|
|
|
assert color.data == "#ffbf7f0000"
|
|
|
|
|
|
def test_set_white_value(mock_openzwave):
|
|
"""Test setting zwave light color."""
|
|
node = MockNode(command_classes=[const.COMMAND_CLASS_SWITCH_COLOR])
|
|
value = MockValue(data=0, node=node)
|
|
color = MockValue(data="#0000000000", node=node)
|
|
# Supports RGBW
|
|
color_channels = MockValue(data=0x1D, node=node)
|
|
values = MockLightValues(primary=value, color=color, color_channels=color_channels)
|
|
device = light.get_device(node=node, values=values, node_config={})
|
|
|
|
assert color.data == "#0000000000"
|
|
|
|
device.turn_on(**{ATTR_WHITE_VALUE: 200})
|
|
|
|
assert color.data == "#ffffffc800"
|
|
|
|
|
|
def test_disable_white_if_set_color(mock_openzwave):
|
|
"""
|
|
Test that _white is set to 0 if turn_on with ATTR_HS_COLOR.
|
|
|
|
See Issue #13930 - many RGBW ZWave bulbs will only activate the RGB LED to
|
|
produce color if _white is set to zero.
|
|
"""
|
|
node = MockNode(command_classes=[const.COMMAND_CLASS_SWITCH_COLOR])
|
|
value = MockValue(data=0, node=node)
|
|
color = MockValue(data="#0000000000", node=node)
|
|
# Supports RGB only
|
|
color_channels = MockValue(data=0x1C, node=node)
|
|
values = MockLightValues(primary=value, color=color, color_channels=color_channels)
|
|
device = light.get_device(node=node, values=values, node_config={})
|
|
device._white = 234
|
|
|
|
assert color.data == "#0000000000"
|
|
assert device.white_value == 234
|
|
|
|
device.turn_on(**{ATTR_HS_COLOR: (30, 50)})
|
|
|
|
assert device.white_value == 0
|
|
assert color.data == "#ffbf7f0000"
|
|
|
|
|
|
def test_zw098_set_color_temp(mock_openzwave):
|
|
"""Test setting zwave light color."""
|
|
node = MockNode(
|
|
manufacturer_id="0086",
|
|
product_id="0062",
|
|
command_classes=[const.COMMAND_CLASS_SWITCH_COLOR],
|
|
)
|
|
value = MockValue(data=0, node=node)
|
|
color = MockValue(data="#0000000000", node=node)
|
|
# Supports RGB, warm white, cold white
|
|
color_channels = MockValue(data=0x1F, node=node)
|
|
values = MockLightValues(primary=value, color=color, color_channels=color_channels)
|
|
device = light.get_device(node=node, values=values, node_config={})
|
|
|
|
assert color.data == "#0000000000"
|
|
|
|
device.turn_on(**{ATTR_COLOR_TEMP: 200})
|
|
|
|
assert color.data == "#00000000ff"
|
|
|
|
device.turn_on(**{ATTR_COLOR_TEMP: 400})
|
|
|
|
assert color.data == "#000000ff00"
|
|
|
|
|
|
def test_rgb_not_supported(mock_openzwave):
|
|
"""Test value changed for rgb lights."""
|
|
node = MockNode(command_classes=[const.COMMAND_CLASS_SWITCH_COLOR])
|
|
value = MockValue(data=0, node=node)
|
|
color = MockValue(data="#0000000000", node=node)
|
|
# Supports color temperature only
|
|
color_channels = MockValue(data=0x01, node=node)
|
|
values = MockLightValues(primary=value, color=color, color_channels=color_channels)
|
|
device = light.get_device(node=node, values=values, node_config={})
|
|
|
|
assert device.hs_color is None
|
|
|
|
|
|
def test_no_color_value(mock_openzwave):
|
|
"""Test value changed for rgb lights."""
|
|
node = MockNode(command_classes=[const.COMMAND_CLASS_SWITCH_COLOR])
|
|
value = MockValue(data=0, node=node)
|
|
values = MockLightValues(primary=value)
|
|
device = light.get_device(node=node, values=values, node_config={})
|
|
|
|
assert device.hs_color is None
|
|
|
|
|
|
def test_no_color_channels_value(mock_openzwave):
|
|
"""Test value changed for rgb lights."""
|
|
node = MockNode(command_classes=[const.COMMAND_CLASS_SWITCH_COLOR])
|
|
value = MockValue(data=0, node=node)
|
|
color = MockValue(data="#0000000000", node=node)
|
|
values = MockLightValues(primary=value, color=color)
|
|
device = light.get_device(node=node, values=values, node_config={})
|
|
|
|
assert device.hs_color is None
|
|
|
|
|
|
def test_rgb_value_changed(mock_openzwave):
|
|
"""Test value changed for rgb lights."""
|
|
node = MockNode(command_classes=[const.COMMAND_CLASS_SWITCH_COLOR])
|
|
value = MockValue(data=0, node=node)
|
|
color = MockValue(data="#0000000000", node=node)
|
|
# Supports RGB only
|
|
color_channels = MockValue(data=0x1C, node=node)
|
|
values = MockLightValues(primary=value, color=color, color_channels=color_channels)
|
|
device = light.get_device(node=node, values=values, node_config={})
|
|
|
|
assert device.hs_color == (0, 0)
|
|
|
|
color.data = "#ffbf800000"
|
|
value_changed(color)
|
|
|
|
assert device.hs_color == (29.764, 49.804)
|
|
|
|
|
|
def test_rgbww_value_changed(mock_openzwave):
|
|
"""Test value changed for rgb lights."""
|
|
node = MockNode(command_classes=[const.COMMAND_CLASS_SWITCH_COLOR])
|
|
value = MockValue(data=0, node=node)
|
|
color = MockValue(data="#0000000000", node=node)
|
|
# Supports RGB, Warm White
|
|
color_channels = MockValue(data=0x1D, node=node)
|
|
values = MockLightValues(primary=value, color=color, color_channels=color_channels)
|
|
device = light.get_device(node=node, values=values, node_config={})
|
|
|
|
assert device.hs_color == (0, 0)
|
|
assert device.white_value == 0
|
|
|
|
color.data = "#c86400c800"
|
|
value_changed(color)
|
|
|
|
assert device.hs_color == (30, 100)
|
|
assert device.white_value == 200
|
|
|
|
|
|
def test_rgbcw_value_changed(mock_openzwave):
|
|
"""Test value changed for rgb lights."""
|
|
node = MockNode(command_classes=[const.COMMAND_CLASS_SWITCH_COLOR])
|
|
value = MockValue(data=0, node=node)
|
|
color = MockValue(data="#0000000000", node=node)
|
|
# Supports RGB, Cold White
|
|
color_channels = MockValue(data=0x1E, node=node)
|
|
values = MockLightValues(primary=value, color=color, color_channels=color_channels)
|
|
device = light.get_device(node=node, values=values, node_config={})
|
|
|
|
assert device.hs_color == (0, 0)
|
|
assert device.white_value == 0
|
|
|
|
color.data = "#c86400c800"
|
|
value_changed(color)
|
|
|
|
assert device.hs_color == (30, 100)
|
|
assert device.white_value == 200
|
|
|
|
|
|
def test_ct_value_changed(mock_openzwave):
|
|
"""Test value changed for zw098 lights."""
|
|
node = MockNode(
|
|
manufacturer_id="0086",
|
|
product_id="0062",
|
|
command_classes=[const.COMMAND_CLASS_SWITCH_COLOR],
|
|
)
|
|
value = MockValue(data=0, node=node)
|
|
color = MockValue(data="#0000000000", node=node)
|
|
# Supports RGB, Cold White
|
|
color_channels = MockValue(data=0x1F, node=node)
|
|
values = MockLightValues(primary=value, color=color, color_channels=color_channels)
|
|
device = light.get_device(node=node, values=values, node_config={})
|
|
|
|
assert device.color_temp == light.TEMP_MID_HASS
|
|
|
|
color.data = "#000000ff00"
|
|
value_changed(color)
|
|
|
|
assert device.color_temp == light.TEMP_WARM_HASS
|
|
|
|
color.data = "#00000000ff"
|
|
value_changed(color)
|
|
|
|
assert device.color_temp == light.TEMP_COLD_HASS
|