Add RGB light support to ozw (#37636)
This commit is contained in:
parent
93919dea88
commit
a6129467aa
10 changed files with 766 additions and 5 deletions
|
@ -194,6 +194,8 @@ DISCOVERY_SCHEMAS = (
|
||||||
const.DISC_SPECIFIC_DEVICE_CLASS: (
|
const.DISC_SPECIFIC_DEVICE_CLASS: (
|
||||||
const_ozw.SPECIFIC_TYPE_POWER_SWITCH_MULTILEVEL,
|
const_ozw.SPECIFIC_TYPE_POWER_SWITCH_MULTILEVEL,
|
||||||
const_ozw.SPECIFIC_TYPE_SCENE_SWITCH_MULTILEVEL,
|
const_ozw.SPECIFIC_TYPE_SCENE_SWITCH_MULTILEVEL,
|
||||||
|
const_ozw.SPECIFIC_TYPE_COLOR_TUNABLE_BINARY,
|
||||||
|
const_ozw.SPECIFIC_TYPE_COLOR_TUNABLE_MULTILEVEL,
|
||||||
const_ozw.SPECIFIC_TYPE_NOT_USED,
|
const_ozw.SPECIFIC_TYPE_NOT_USED,
|
||||||
),
|
),
|
||||||
const.DISC_VALUES: {
|
const.DISC_VALUES: {
|
||||||
|
|
|
@ -3,20 +3,37 @@ import logging
|
||||||
|
|
||||||
from homeassistant.components.light import (
|
from homeassistant.components.light import (
|
||||||
ATTR_BRIGHTNESS,
|
ATTR_BRIGHTNESS,
|
||||||
|
ATTR_COLOR_TEMP,
|
||||||
|
ATTR_HS_COLOR,
|
||||||
ATTR_TRANSITION,
|
ATTR_TRANSITION,
|
||||||
|
ATTR_WHITE_VALUE,
|
||||||
DOMAIN as LIGHT_DOMAIN,
|
DOMAIN as LIGHT_DOMAIN,
|
||||||
SUPPORT_BRIGHTNESS,
|
SUPPORT_BRIGHTNESS,
|
||||||
|
SUPPORT_COLOR,
|
||||||
|
SUPPORT_COLOR_TEMP,
|
||||||
SUPPORT_TRANSITION,
|
SUPPORT_TRANSITION,
|
||||||
|
SUPPORT_WHITE_VALUE,
|
||||||
LightEntity,
|
LightEntity,
|
||||||
)
|
)
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
|
import homeassistant.util.color as color_util
|
||||||
|
|
||||||
from .const import DATA_UNSUBSCRIBE, DOMAIN
|
from .const import DATA_UNSUBSCRIBE, DOMAIN
|
||||||
from .entity import ZWaveDeviceEntity
|
from .entity import ZWaveDeviceEntity
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
ATTR_VALUE = "Value"
|
||||||
|
COLOR_CHANNEL_WARM_WHITE = 0x01
|
||||||
|
COLOR_CHANNEL_COLD_WHITE = 0x02
|
||||||
|
COLOR_CHANNEL_RED = 0x04
|
||||||
|
COLOR_CHANNEL_GREEN = 0x08
|
||||||
|
COLOR_CHANNEL_BLUE = 0x10
|
||||||
|
TEMP_COLOR_MAX = 500 # mireds (inverted)
|
||||||
|
TEMP_COLOR_MIN = 154
|
||||||
|
TEMP_COLOR_DIFF = TEMP_COLOR_MAX - TEMP_COLOR_MIN
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
"""Set up Z-Wave Light from Config Entry."""
|
"""Set up Z-Wave Light from Config Entry."""
|
||||||
|
@ -24,7 +41,8 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
@callback
|
@callback
|
||||||
def async_add_light(values):
|
def async_add_light(values):
|
||||||
"""Add Z-Wave Light."""
|
"""Add Z-Wave Light."""
|
||||||
light = ZwaveDimmer(values)
|
light = ZwaveLight(values)
|
||||||
|
|
||||||
async_add_entities([light])
|
async_add_entities([light])
|
||||||
|
|
||||||
hass.data[DOMAIN][config_entry.entry_id][DATA_UNSUBSCRIBE].append(
|
hass.data[DOMAIN][config_entry.entry_id][DATA_UNSUBSCRIBE].append(
|
||||||
|
@ -42,12 +60,16 @@ def byte_to_zwave_brightness(value):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
class ZwaveDimmer(ZWaveDeviceEntity, LightEntity):
|
class ZwaveLight(ZWaveDeviceEntity, LightEntity):
|
||||||
"""Representation of a Z-Wave dimmer."""
|
"""Representation of a Z-Wave light."""
|
||||||
|
|
||||||
def __init__(self, values):
|
def __init__(self, values):
|
||||||
"""Initialize the light."""
|
"""Initialize the light."""
|
||||||
super().__init__(values)
|
super().__init__(values)
|
||||||
|
self._color_channels = None
|
||||||
|
self._hs = None
|
||||||
|
self._white = None
|
||||||
|
self._ct = None
|
||||||
self._supported_features = SUPPORT_BRIGHTNESS
|
self._supported_features = SUPPORT_BRIGHTNESS
|
||||||
# make sure that supported features is correctly set
|
# make sure that supported features is correctly set
|
||||||
self.on_value_update()
|
self.on_value_update()
|
||||||
|
@ -55,10 +77,29 @@ class ZwaveDimmer(ZWaveDeviceEntity, LightEntity):
|
||||||
@callback
|
@callback
|
||||||
def on_value_update(self):
|
def on_value_update(self):
|
||||||
"""Call when the underlying value(s) is added or updated."""
|
"""Call when the underlying value(s) is added or updated."""
|
||||||
self._supported_features = SUPPORT_BRIGHTNESS
|
|
||||||
if self.values.dimming_duration is not None:
|
if self.values.dimming_duration is not None:
|
||||||
self._supported_features |= SUPPORT_TRANSITION
|
self._supported_features |= SUPPORT_TRANSITION
|
||||||
|
|
||||||
|
if self.values.color is None and self.values.color_channels is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.values.color is not None:
|
||||||
|
self._supported_features |= SUPPORT_COLOR
|
||||||
|
|
||||||
|
# Support Color Temp if both white channels
|
||||||
|
if (self.values.color_channels.value & COLOR_CHANNEL_WARM_WHITE) and (
|
||||||
|
self.values.color_channels.value & COLOR_CHANNEL_COLD_WHITE
|
||||||
|
):
|
||||||
|
self._supported_features |= SUPPORT_COLOR_TEMP
|
||||||
|
|
||||||
|
# Support White value if only a single white channel
|
||||||
|
if ((self.values.color_channels.value & COLOR_CHANNEL_WARM_WHITE) != 0) ^ (
|
||||||
|
(self.values.color_channels.value & COLOR_CHANNEL_COLD_WHITE) != 0
|
||||||
|
):
|
||||||
|
self._supported_features |= SUPPORT_WHITE_VALUE
|
||||||
|
|
||||||
|
self._calculate_rgb_values()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def brightness(self):
|
def brightness(self):
|
||||||
"""Return the brightness of this light between 0..255.
|
"""Return the brightness of this light between 0..255.
|
||||||
|
@ -81,6 +122,21 @@ class ZwaveDimmer(ZWaveDeviceEntity, LightEntity):
|
||||||
"""Flag supported features."""
|
"""Flag supported features."""
|
||||||
return self._supported_features
|
return self._supported_features
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hs_color(self):
|
||||||
|
"""Return the hs color."""
|
||||||
|
return self._hs
|
||||||
|
|
||||||
|
@property
|
||||||
|
def white_value(self):
|
||||||
|
"""Return the white value of this light between 0..255."""
|
||||||
|
return self._white
|
||||||
|
|
||||||
|
@property
|
||||||
|
def color_temp(self):
|
||||||
|
"""Return the color temperature."""
|
||||||
|
return self._ct
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_set_duration(self, **kwargs):
|
def async_set_duration(self, **kwargs):
|
||||||
"""Set the transition time for the brightness value.
|
"""Set the transition time for the brightness value.
|
||||||
|
@ -118,6 +174,34 @@ class ZwaveDimmer(ZWaveDeviceEntity, LightEntity):
|
||||||
"""Turn the device on."""
|
"""Turn the device on."""
|
||||||
self.async_set_duration(**kwargs)
|
self.async_set_duration(**kwargs)
|
||||||
|
|
||||||
|
rgbw = None
|
||||||
|
white = kwargs.get(ATTR_WHITE_VALUE)
|
||||||
|
hs_color = kwargs.get(ATTR_HS_COLOR)
|
||||||
|
color_temp = kwargs.get(ATTR_COLOR_TEMP)
|
||||||
|
|
||||||
|
if hs_color is not None:
|
||||||
|
rgbw = "#"
|
||||||
|
for colorval in color_util.color_hs_to_RGB(*hs_color):
|
||||||
|
rgbw += f"{colorval:02x}"
|
||||||
|
rgbw += "0000"
|
||||||
|
# white LED must be off in order for color to work
|
||||||
|
|
||||||
|
elif white is not None:
|
||||||
|
if self._color_channels & COLOR_CHANNEL_WARM_WHITE:
|
||||||
|
rgbw = f"#000000{white:02x}00"
|
||||||
|
else:
|
||||||
|
rgbw = f"#00000000{white:02x}"
|
||||||
|
|
||||||
|
elif color_temp is not None:
|
||||||
|
cold = round((TEMP_COLOR_MAX - round(color_temp)) / TEMP_COLOR_DIFF * 255)
|
||||||
|
warm = 255 - cold
|
||||||
|
if warm < 0:
|
||||||
|
warm = 0
|
||||||
|
rgbw = f"#000000{warm:02x}{cold:02x}"
|
||||||
|
|
||||||
|
if rgbw and self.values.color:
|
||||||
|
self.values.color.send_value(rgbw)
|
||||||
|
|
||||||
# Zwave multilevel switches use a range of [0, 99] to control
|
# Zwave multilevel switches use a range of [0, 99] to control
|
||||||
# brightness. Level 255 means to set it to previous value.
|
# brightness. Level 255 means to set it to previous value.
|
||||||
if ATTR_BRIGHTNESS in kwargs:
|
if ATTR_BRIGHTNESS in kwargs:
|
||||||
|
@ -133,3 +217,47 @@ class ZwaveDimmer(ZWaveDeviceEntity, LightEntity):
|
||||||
self.async_set_duration(**kwargs)
|
self.async_set_duration(**kwargs)
|
||||||
|
|
||||||
self.values.primary.send_value(0)
|
self.values.primary.send_value(0)
|
||||||
|
|
||||||
|
def _calculate_rgb_values(self):
|
||||||
|
# Color Channels
|
||||||
|
self._color_channels = self.values.color_channels.data[ATTR_VALUE]
|
||||||
|
|
||||||
|
# Color Data String
|
||||||
|
data = self.values.color.data[ATTR_VALUE]
|
||||||
|
|
||||||
|
# RGB is always present in the openzwave color data string.
|
||||||
|
rgb = [int(data[1:3], 16), int(data[3:5], 16), int(data[5:7], 16)]
|
||||||
|
self._hs = color_util.color_RGB_to_hs(*rgb)
|
||||||
|
|
||||||
|
# Parse remaining color channels. Openzwave appends white channels
|
||||||
|
# that are present.
|
||||||
|
index = 7
|
||||||
|
temp_warm = 0
|
||||||
|
temp_cold = 0
|
||||||
|
|
||||||
|
# Warm white
|
||||||
|
if self._color_channels & COLOR_CHANNEL_WARM_WHITE:
|
||||||
|
self._white = int(data[index : index + 2], 16)
|
||||||
|
temp_warm = self._white
|
||||||
|
|
||||||
|
index += 2
|
||||||
|
|
||||||
|
# Cold white
|
||||||
|
if self._color_channels & COLOR_CHANNEL_COLD_WHITE:
|
||||||
|
self._white = int(data[index : index + 2], 16)
|
||||||
|
temp_cold = self._white
|
||||||
|
|
||||||
|
# Calculate color temps based on white LED status
|
||||||
|
if temp_cold > 0:
|
||||||
|
self._ct = round(TEMP_COLOR_MAX - ((temp_cold / 255) * TEMP_COLOR_DIFF))
|
||||||
|
# Only used if CW channel missing
|
||||||
|
elif temp_warm > 0:
|
||||||
|
self._ct = round(TEMP_COLOR_MAX - temp_warm)
|
||||||
|
|
||||||
|
# If no rgb channels supported, report None.
|
||||||
|
if not (
|
||||||
|
self._color_channels & COLOR_CHANNEL_RED
|
||||||
|
or self._color_channels & COLOR_CHANNEL_GREEN
|
||||||
|
or self._color_channels & COLOR_CHANNEL_BLUE
|
||||||
|
):
|
||||||
|
self._hs = None
|
||||||
|
|
|
@ -27,6 +27,30 @@ def light_data_fixture():
|
||||||
return load_fixture("ozw/light_network_dump.csv")
|
return load_fixture("ozw/light_network_dump.csv")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="light_no_rgb_data", scope="session")
|
||||||
|
def light_no_rgb_data_fixture():
|
||||||
|
"""Load light dimmer MQTT data and return it."""
|
||||||
|
return load_fixture("ozw/light_no_rgb_network_dump.csv")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="light_no_ww_data", scope="session")
|
||||||
|
def light_no_ww_data_fixture():
|
||||||
|
"""Load light dimmer MQTT data and return it."""
|
||||||
|
return load_fixture("ozw/light_no_ww_network_dump.csv")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="light_no_cw_data", scope="session")
|
||||||
|
def light_no_cw_data_fixture():
|
||||||
|
"""Load light dimmer MQTT data and return it."""
|
||||||
|
return load_fixture("ozw/light_no_cw_network_dump.csv")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="light_wc_data", scope="session")
|
||||||
|
def light_wc_only_data_fixture():
|
||||||
|
"""Load light dimmer MQTT data and return it."""
|
||||||
|
return load_fixture("ozw/light_wc_network_dump.csv")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="cover_data", scope="session")
|
@pytest.fixture(name="cover_data", scope="session")
|
||||||
def cover_data_fixture():
|
def cover_data_fixture():
|
||||||
"""Load cover MQTT data and return it."""
|
"""Load cover MQTT data and return it."""
|
||||||
|
@ -87,6 +111,28 @@ async def light_msg_fixture(hass):
|
||||||
return message
|
return message
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="light_no_rgb_msg")
|
||||||
|
async def light_no_rgb_msg_fixture(hass):
|
||||||
|
"""Return a mock MQTT msg with a light actuator message."""
|
||||||
|
light_json = json.loads(
|
||||||
|
await hass.async_add_executor_job(load_fixture, "ozw/light_no_rgb.json")
|
||||||
|
)
|
||||||
|
message = MQTTMessage(topic=light_json["topic"], payload=light_json["payload"])
|
||||||
|
message.encode()
|
||||||
|
return message
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="light_rgb_msg")
|
||||||
|
async def light_rgb_msg_fixture(hass):
|
||||||
|
"""Return a mock MQTT msg with a light actuator message."""
|
||||||
|
light_json = json.loads(
|
||||||
|
await hass.async_add_executor_job(load_fixture, "ozw/light_rgb.json")
|
||||||
|
)
|
||||||
|
message = MQTTMessage(topic=light_json["topic"], payload=light_json["payload"])
|
||||||
|
message.encode()
|
||||||
|
return message
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="switch_msg")
|
@pytest.fixture(name="switch_msg")
|
||||||
async def switch_msg_fixture(hass):
|
async def switch_msg_fixture(hass):
|
||||||
"""Return a mock MQTT msg with a switch actuator message."""
|
"""Return a mock MQTT msg with a switch actuator message."""
|
||||||
|
|
|
@ -4,7 +4,7 @@ from homeassistant.components.ozw.light import byte_to_zwave_brightness
|
||||||
from .common import setup_ozw
|
from .common import setup_ozw
|
||||||
|
|
||||||
|
|
||||||
async def test_light(hass, light_data, light_msg, sent_messages):
|
async def test_light(hass, light_data, light_msg, light_rgb_msg, sent_messages):
|
||||||
"""Test setting up config entry."""
|
"""Test setting up config entry."""
|
||||||
receive_message = await setup_ozw(hass, fixture=light_data)
|
receive_message = await setup_ozw(hass, fixture=light_data)
|
||||||
|
|
||||||
|
@ -149,3 +149,335 @@ async def test_light(hass, light_data, light_msg, sent_messages):
|
||||||
state = hass.states.get("light.led_bulb_6_multi_colour_level")
|
state = hass.states.get("light.led_bulb_6_multi_colour_level")
|
||||||
assert state is not None
|
assert state is not None
|
||||||
assert state.state == "off"
|
assert state.state == "off"
|
||||||
|
|
||||||
|
# Test setting color_name
|
||||||
|
new_color = "blue"
|
||||||
|
await hass.services.async_call(
|
||||||
|
"light",
|
||||||
|
"turn_on",
|
||||||
|
{"entity_id": "light.led_bulb_6_multi_colour_level", "color_name": new_color},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert len(sent_messages) == 9
|
||||||
|
|
||||||
|
msg = sent_messages[-2]
|
||||||
|
assert msg["topic"] == "OpenZWave/1/command/setvalue/"
|
||||||
|
assert msg["payload"] == {"Value": "#0000ff0000", "ValueIDKey": 659341335}
|
||||||
|
|
||||||
|
msg = sent_messages[-1]
|
||||||
|
assert msg["topic"] == "OpenZWave/1/command/setvalue/"
|
||||||
|
assert msg["payload"] == {"Value": 255, "ValueIDKey": 659128337}
|
||||||
|
|
||||||
|
# Feedback on state
|
||||||
|
light_msg.decode()
|
||||||
|
light_msg.payload["Value"] = byte_to_zwave_brightness(255)
|
||||||
|
light_msg.encode()
|
||||||
|
light_rgb_msg.decode()
|
||||||
|
light_rgb_msg.payload["Value"] = "#0000ff0000"
|
||||||
|
light_rgb_msg.encode()
|
||||||
|
receive_message(light_msg)
|
||||||
|
receive_message(light_rgb_msg)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("light.led_bulb_6_multi_colour_level")
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == "on"
|
||||||
|
assert state.attributes["rgb_color"] == (0, 0, 255)
|
||||||
|
|
||||||
|
# Test setting hs_color
|
||||||
|
new_color = [300, 70]
|
||||||
|
await hass.services.async_call(
|
||||||
|
"light",
|
||||||
|
"turn_on",
|
||||||
|
{"entity_id": "light.led_bulb_6_multi_colour_level", "hs_color": new_color},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert len(sent_messages) == 11
|
||||||
|
msg = sent_messages[-1]
|
||||||
|
assert msg["topic"] == "OpenZWave/1/command/setvalue/"
|
||||||
|
assert msg["payload"] == {"Value": 255, "ValueIDKey": 659128337}
|
||||||
|
|
||||||
|
msg = sent_messages[-2]
|
||||||
|
assert msg["topic"] == "OpenZWave/1/command/setvalue/"
|
||||||
|
assert msg["payload"] == {"Value": "#ff4cff0000", "ValueIDKey": 659341335}
|
||||||
|
|
||||||
|
# Feedback on state
|
||||||
|
light_msg.decode()
|
||||||
|
light_msg.payload["Value"] = byte_to_zwave_brightness(255)
|
||||||
|
light_msg.encode()
|
||||||
|
light_rgb_msg.decode()
|
||||||
|
light_rgb_msg.payload["Value"] = "#ff4cff0000"
|
||||||
|
light_rgb_msg.encode()
|
||||||
|
receive_message(light_msg)
|
||||||
|
receive_message(light_rgb_msg)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("light.led_bulb_6_multi_colour_level")
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == "on"
|
||||||
|
assert state.attributes["hs_color"] == (300.0, 70.196)
|
||||||
|
|
||||||
|
# Test setting rgb_color
|
||||||
|
new_color = [255, 154, 0]
|
||||||
|
await hass.services.async_call(
|
||||||
|
"light",
|
||||||
|
"turn_on",
|
||||||
|
{"entity_id": "light.led_bulb_6_multi_colour_level", "rgb_color": new_color},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert len(sent_messages) == 13
|
||||||
|
msg = sent_messages[-1]
|
||||||
|
assert msg["topic"] == "OpenZWave/1/command/setvalue/"
|
||||||
|
assert msg["payload"] == {"Value": 255, "ValueIDKey": 659128337}
|
||||||
|
|
||||||
|
msg = sent_messages[-2]
|
||||||
|
assert msg["topic"] == "OpenZWave/1/command/setvalue/"
|
||||||
|
assert msg["payload"] == {"Value": "#ff99000000", "ValueIDKey": 659341335}
|
||||||
|
|
||||||
|
# Feedback on state
|
||||||
|
light_msg.decode()
|
||||||
|
light_msg.payload["Value"] = byte_to_zwave_brightness(255)
|
||||||
|
light_msg.encode()
|
||||||
|
light_rgb_msg.decode()
|
||||||
|
light_rgb_msg.payload["Value"] = "#ff99000000"
|
||||||
|
light_rgb_msg.encode()
|
||||||
|
receive_message(light_msg)
|
||||||
|
receive_message(light_rgb_msg)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("light.led_bulb_6_multi_colour_level")
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == "on"
|
||||||
|
assert state.attributes["rgb_color"] == (255, 153, 0)
|
||||||
|
|
||||||
|
# Test setting xy_color
|
||||||
|
new_color = [0.52, 0.43]
|
||||||
|
await hass.services.async_call(
|
||||||
|
"light",
|
||||||
|
"turn_on",
|
||||||
|
{"entity_id": "light.led_bulb_6_multi_colour_level", "xy_color": new_color},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert len(sent_messages) == 15
|
||||||
|
msg = sent_messages[-1]
|
||||||
|
assert msg["topic"] == "OpenZWave/1/command/setvalue/"
|
||||||
|
assert msg["payload"] == {"Value": 255, "ValueIDKey": 659128337}
|
||||||
|
|
||||||
|
msg = sent_messages[-2]
|
||||||
|
assert msg["topic"] == "OpenZWave/1/command/setvalue/"
|
||||||
|
assert msg["payload"] == {"Value": "#ffbb370000", "ValueIDKey": 659341335}
|
||||||
|
|
||||||
|
# Feedback on state
|
||||||
|
light_msg.decode()
|
||||||
|
light_msg.payload["Value"] = byte_to_zwave_brightness(255)
|
||||||
|
light_msg.encode()
|
||||||
|
light_rgb_msg.decode()
|
||||||
|
light_rgb_msg.payload["Value"] = "#ffbb370000"
|
||||||
|
light_rgb_msg.encode()
|
||||||
|
receive_message(light_msg)
|
||||||
|
receive_message(light_rgb_msg)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("light.led_bulb_6_multi_colour_level")
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == "on"
|
||||||
|
assert state.attributes["xy_color"] == (0.519, 0.429)
|
||||||
|
|
||||||
|
# Test setting color temp
|
||||||
|
new_color = 465
|
||||||
|
await hass.services.async_call(
|
||||||
|
"light",
|
||||||
|
"turn_on",
|
||||||
|
{"entity_id": "light.led_bulb_6_multi_colour_level", "color_temp": new_color},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert len(sent_messages) == 17
|
||||||
|
msg = sent_messages[-1]
|
||||||
|
assert msg["topic"] == "OpenZWave/1/command/setvalue/"
|
||||||
|
assert msg["payload"] == {"Value": 255, "ValueIDKey": 659128337}
|
||||||
|
|
||||||
|
msg = sent_messages[-2]
|
||||||
|
assert msg["topic"] == "OpenZWave/1/command/setvalue/"
|
||||||
|
assert msg["payload"] == {"Value": "#000000e51a", "ValueIDKey": 659341335}
|
||||||
|
|
||||||
|
# Feedback on state
|
||||||
|
light_msg.decode()
|
||||||
|
light_msg.payload["Value"] = byte_to_zwave_brightness(255)
|
||||||
|
light_msg.encode()
|
||||||
|
light_rgb_msg.decode()
|
||||||
|
light_rgb_msg.payload["Value"] = "#000000e51a"
|
||||||
|
light_rgb_msg.encode()
|
||||||
|
receive_message(light_msg)
|
||||||
|
receive_message(light_rgb_msg)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("light.led_bulb_6_multi_colour_level")
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == "on"
|
||||||
|
assert state.attributes["color_temp"] == 465
|
||||||
|
|
||||||
|
|
||||||
|
async def test_no_rgb_light(hass, light_no_rgb_data, light_no_rgb_msg, sent_messages):
|
||||||
|
"""Test setting up config entry."""
|
||||||
|
receive_message = await setup_ozw(hass, fixture=light_no_rgb_data)
|
||||||
|
|
||||||
|
# Test loaded no RGBW support (dimmer only)
|
||||||
|
state = hass.states.get("light.master_bedroom_l_level")
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == "off"
|
||||||
|
|
||||||
|
# Turn on the light
|
||||||
|
new_brightness = 44
|
||||||
|
await hass.services.async_call(
|
||||||
|
"light",
|
||||||
|
"turn_on",
|
||||||
|
{"entity_id": "light.master_bedroom_l_level", "brightness": new_brightness},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert len(sent_messages) == 1
|
||||||
|
msg = sent_messages[-1]
|
||||||
|
assert msg["topic"] == "OpenZWave/1/command/setvalue/"
|
||||||
|
assert msg["payload"] == {
|
||||||
|
"Value": byte_to_zwave_brightness(new_brightness),
|
||||||
|
"ValueIDKey": 38371345,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Feedback on state
|
||||||
|
|
||||||
|
light_no_rgb_msg.decode()
|
||||||
|
light_no_rgb_msg.payload["Value"] = byte_to_zwave_brightness(new_brightness)
|
||||||
|
light_no_rgb_msg.encode()
|
||||||
|
receive_message(light_no_rgb_msg)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("light.master_bedroom_l_level")
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == "on"
|
||||||
|
assert state.attributes["brightness"] == new_brightness
|
||||||
|
|
||||||
|
|
||||||
|
async def test_no_ww_light(
|
||||||
|
hass, light_no_ww_data, light_msg, light_rgb_msg, sent_messages
|
||||||
|
):
|
||||||
|
"""Test setting up config entry."""
|
||||||
|
receive_message = await setup_ozw(hass, fixture=light_no_ww_data)
|
||||||
|
|
||||||
|
# Test loaded no ww support
|
||||||
|
state = hass.states.get("light.led_bulb_6_multi_colour_level")
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == "off"
|
||||||
|
|
||||||
|
# Turn on the light
|
||||||
|
white_color = 190
|
||||||
|
await hass.services.async_call(
|
||||||
|
"light",
|
||||||
|
"turn_on",
|
||||||
|
{
|
||||||
|
"entity_id": "light.led_bulb_6_multi_colour_level",
|
||||||
|
"white_value": white_color,
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert len(sent_messages) == 2
|
||||||
|
msg = sent_messages[-2]
|
||||||
|
assert msg["topic"] == "OpenZWave/1/command/setvalue/"
|
||||||
|
assert msg["payload"] == {"Value": "#00000000be", "ValueIDKey": 659341335}
|
||||||
|
|
||||||
|
# Feedback on state
|
||||||
|
light_msg.decode()
|
||||||
|
light_msg.payload["Value"] = byte_to_zwave_brightness(255)
|
||||||
|
light_msg.encode()
|
||||||
|
light_rgb_msg.decode()
|
||||||
|
light_rgb_msg.payload["Value"] = "#00000000be"
|
||||||
|
light_rgb_msg.encode()
|
||||||
|
receive_message(light_msg)
|
||||||
|
receive_message(light_rgb_msg)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("light.led_bulb_6_multi_colour_level")
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == "on"
|
||||||
|
assert state.attributes["white_value"] == 190
|
||||||
|
|
||||||
|
|
||||||
|
async def test_no_cw_light(
|
||||||
|
hass, light_no_cw_data, light_msg, light_rgb_msg, sent_messages
|
||||||
|
):
|
||||||
|
"""Test setting up config entry."""
|
||||||
|
receive_message = await setup_ozw(hass, fixture=light_no_cw_data)
|
||||||
|
|
||||||
|
# Test loaded no cw support
|
||||||
|
state = hass.states.get("light.led_bulb_6_multi_colour_level")
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == "off"
|
||||||
|
|
||||||
|
# Turn on the light
|
||||||
|
white_color = 190
|
||||||
|
await hass.services.async_call(
|
||||||
|
"light",
|
||||||
|
"turn_on",
|
||||||
|
{
|
||||||
|
"entity_id": "light.led_bulb_6_multi_colour_level",
|
||||||
|
"white_value": white_color,
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert len(sent_messages) == 2
|
||||||
|
msg = sent_messages[-2]
|
||||||
|
assert msg["topic"] == "OpenZWave/1/command/setvalue/"
|
||||||
|
assert msg["payload"] == {"Value": "#000000be00", "ValueIDKey": 659341335}
|
||||||
|
|
||||||
|
# Feedback on state
|
||||||
|
light_msg.decode()
|
||||||
|
light_msg.payload["Value"] = byte_to_zwave_brightness(255)
|
||||||
|
light_msg.encode()
|
||||||
|
light_rgb_msg.decode()
|
||||||
|
light_rgb_msg.payload["Value"] = "#000000be00"
|
||||||
|
light_rgb_msg.encode()
|
||||||
|
receive_message(light_msg)
|
||||||
|
receive_message(light_rgb_msg)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("light.led_bulb_6_multi_colour_level")
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == "on"
|
||||||
|
assert state.attributes["white_value"] == 190
|
||||||
|
|
||||||
|
|
||||||
|
async def test_wc_light(hass, light_wc_data, light_msg, light_rgb_msg, sent_messages):
|
||||||
|
"""Test setting up config entry."""
|
||||||
|
receive_message = await setup_ozw(hass, fixture=light_wc_data)
|
||||||
|
|
||||||
|
# Test loaded only white LED support
|
||||||
|
state = hass.states.get("light.led_bulb_6_multi_colour_level")
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == "off"
|
||||||
|
|
||||||
|
# Turn on the light
|
||||||
|
new_color = 190
|
||||||
|
await hass.services.async_call(
|
||||||
|
"light",
|
||||||
|
"turn_on",
|
||||||
|
{"entity_id": "light.led_bulb_6_multi_colour_level", "color_temp": new_color},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert len(sent_messages) == 2
|
||||||
|
msg = sent_messages[-2]
|
||||||
|
assert msg["topic"] == "OpenZWave/1/command/setvalue/"
|
||||||
|
assert msg["payload"] == {"Value": "#0000001be4", "ValueIDKey": 659341335}
|
||||||
|
|
||||||
|
# Feedback on state
|
||||||
|
light_msg.decode()
|
||||||
|
light_msg.payload["Value"] = byte_to_zwave_brightness(255)
|
||||||
|
light_msg.encode()
|
||||||
|
light_rgb_msg.decode()
|
||||||
|
light_rgb_msg.payload["Value"] = "#0000001be4"
|
||||||
|
light_rgb_msg.encode()
|
||||||
|
receive_message(light_msg)
|
||||||
|
receive_message(light_rgb_msg)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("light.led_bulb_6_multi_colour_level")
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == "on"
|
||||||
|
assert state.attributes["color_temp"] == 191
|
||||||
|
|
54
tests/fixtures/ozw/light_no_cw_network_dump.csv
vendored
Normal file
54
tests/fixtures/ozw/light_no_cw_network_dump.csv
vendored
Normal file
File diff suppressed because one or more lines are too long
25
tests/fixtures/ozw/light_no_rgb.json
vendored
Normal file
25
tests/fixtures/ozw/light_no_rgb.json
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"topic": "OpenZWave/1/node/2/instance/1/commandclass/38/value/38371345/",
|
||||||
|
"payload": {
|
||||||
|
"Label": "Level",
|
||||||
|
"Value": 0,
|
||||||
|
"Units": "",
|
||||||
|
"Min": 0,
|
||||||
|
"Max": 255,
|
||||||
|
"Type": "Byte",
|
||||||
|
"Instance": 1,
|
||||||
|
"CommandClass": "COMMAND_CLASS_SWITCH_MULTILEVEL",
|
||||||
|
"Index": 0,
|
||||||
|
"Node": 2,
|
||||||
|
"Genre": "User",
|
||||||
|
"Help": "The Current Level of the Device",
|
||||||
|
"ValueIDKey": 38371345,
|
||||||
|
"ReadOnly": false,
|
||||||
|
"WriteOnly": false,
|
||||||
|
"ValueSet": false,
|
||||||
|
"ValuePolled": false,
|
||||||
|
"ChangeVerified": false,
|
||||||
|
"Event": "valueAdded",
|
||||||
|
"TimeStamp": 1579566891
|
||||||
|
}
|
||||||
|
}
|
41
tests/fixtures/ozw/light_no_rgb_network_dump.csv
vendored
Normal file
41
tests/fixtures/ozw/light_no_rgb_network_dump.csv
vendored
Normal file
File diff suppressed because one or more lines are too long
54
tests/fixtures/ozw/light_no_ww_network_dump.csv
vendored
Normal file
54
tests/fixtures/ozw/light_no_ww_network_dump.csv
vendored
Normal file
File diff suppressed because one or more lines are too long
25
tests/fixtures/ozw/light_rgb.json
vendored
Normal file
25
tests/fixtures/ozw/light_rgb.json
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"topic": "OpenZWave/1/node/39/instance/1/commandclass/51/value/659341335/",
|
||||||
|
"payload": {
|
||||||
|
"Label": "Color",
|
||||||
|
"Value": "#000000FF00",
|
||||||
|
"Units": "#RRGGBBWWCW",
|
||||||
|
"Min": 0,
|
||||||
|
"Max": 0,
|
||||||
|
"Type": "String",
|
||||||
|
"Instance": 1,
|
||||||
|
"CommandClass": "COMMAND_CLASS_COLOR",
|
||||||
|
"Index": 0,
|
||||||
|
"Node": 39,
|
||||||
|
"Genre": "User",
|
||||||
|
"Help": "Color (in RGB format)",
|
||||||
|
"ValueIDKey": 659341335,
|
||||||
|
"ReadOnly": false,
|
||||||
|
"WriteOnly": false,
|
||||||
|
"ValueSet": false,
|
||||||
|
"ValuePolled": false,
|
||||||
|
"ChangeVerified": false,
|
||||||
|
"Event": "valueAdded",
|
||||||
|
"TimeStamp": 1579566891
|
||||||
|
}
|
||||||
|
}
|
54
tests/fixtures/ozw/light_wc_network_dump.csv
vendored
Normal file
54
tests/fixtures/ozw/light_wc_network_dump.csv
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue