Add RGB light support to ozw (#37636)

This commit is contained in:
Chris 2020-07-16 18:10:36 -07:00 committed by GitHub
parent 93919dea88
commit a6129467aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 766 additions and 5 deletions

View file

@ -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: {

View file

@ -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

View file

@ -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."""

View file

@ -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

File diff suppressed because one or more lines are too long

25
tests/fixtures/ozw/light_no_rgb.json vendored Normal file
View 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
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

25
tests/fixtures/ozw/light_rgb.json vendored Normal file
View 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
}
}

File diff suppressed because one or more lines are too long