Add zwave_js light support for HSM-200 V1 (#67089)

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Raman Gupta 2022-02-23 14:18:42 -05:00 committed by GitHub
parent 9906717e33
commit 9fe61f9e7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 968 additions and 21 deletions

View File

@ -16,6 +16,7 @@ from zwave_js_server.const import (
from zwave_js_server.const.command_class.barrier_operator import (
SIGNALING_STATE_PROPERTY,
)
from zwave_js_server.const.command_class.color_switch import CURRENT_COLOR_PROPERTY
from zwave_js_server.const.command_class.humidity_control import (
HUMIDITY_CONTROL_MODE_PROPERTY,
)
@ -125,9 +126,9 @@ class ZWaveValueDiscoverySchema(DataclassMustHaveAtLeastOne):
# [optional] the value's property name must match ANY of these values
property_name: set[str] | None = None
# [optional] the value's property key must match ANY of these values
property_key: set[str | int] | None = None
property_key: set[str | int | None] | None = None
# [optional] the value's property key name must match ANY of these values
property_key_name: set[str] | None = None
property_key_name: set[str | None] | None = None
# [optional] the value's metadata_type must match ANY of these values
type: set[str] | None = None
@ -180,8 +181,8 @@ class ZWaveDiscoverySchema:
def get_config_parameter_discovery_schema(
property_: set[str | int] | None = None,
property_name: set[str] | None = None,
property_key: set[str | int] | None = None,
property_key_name: set[str] | None = None,
property_key: set[str | int | None] | None = None,
property_key_name: set[str | None] | None = None,
**kwargs: Any,
) -> ZWaveDiscoverySchema:
"""
@ -462,6 +463,20 @@ DISCOVERY_SCHEMAS = [
},
),
),
# HomeSeer HSM-200 v1
ZWaveDiscoverySchema(
platform="light",
hint="black_is_off",
manufacturer_id={0x001E},
product_id={0x0001},
product_type={0x0004},
primary_value=ZWaveValueDiscoverySchema(
command_class={CommandClass.SWITCH_COLOR},
property={CURRENT_COLOR_PROPERTY},
property_key={None},
),
absent_values=[SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA],
),
# ====== START OF CONFIG PARAMETER SPECIFIC MAPPING SCHEMAS =======
# Door lock mode config parameter. Functionality equivalent to Notification CC
# list sensors.

View File

@ -74,8 +74,10 @@ async def async_setup_entry(
def async_add_light(info: ZwaveDiscoveryInfo) -> None:
"""Add Z-Wave Light."""
light = ZwaveLight(config_entry, client, info)
async_add_entities([light])
if info.platform_hint == "black_is_off":
async_add_entities([ZwaveBlackIsOffLight(config_entry, client, info)])
else:
async_add_entities([ZwaveLight(config_entry, client, info)])
config_entry.async_on_unload(
async_dispatcher_connect(
@ -127,7 +129,9 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
# get additional (optional) values and set features
self._target_brightness = self.get_zwave_value(
TARGET_VALUE_PROPERTY, add_to_watched_value_ids=False
TARGET_VALUE_PROPERTY,
CommandClass.SWITCH_MULTILEVEL,
add_to_watched_value_ids=False,
)
self._target_color = self.get_zwave_value(
TARGET_COLOR_PROPERTY,
@ -167,14 +171,14 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
self._calculate_color_values()
@property
def brightness(self) -> int:
def brightness(self) -> int | None:
"""Return the brightness of this light between 0..255.
Z-Wave multilevel switches use a range of [0, 99] to control brightness.
"""
if self.info.primary_value.value is not None:
return round((self.info.primary_value.value / 99) * 255)
return 0
if self.info.primary_value.value is None:
return None
return round((self.info.primary_value.value / 99) * 255)
@property
def color_mode(self) -> str | None:
@ -182,9 +186,12 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
return self._color_mode
@property
def is_on(self) -> bool:
def is_on(self) -> bool | None:
"""Return true if device is on (brightness above 0)."""
return self.brightness > 0
brightness = self.brightness
if brightness is None:
return None
return brightness > 0
@property
def hs_color(self) -> tuple[float, float] | None:
@ -318,6 +325,9 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
self, brightness: int | None, transition: float | None = None
) -> None:
"""Set new brightness to light."""
# If we have no target brightness value, there is nothing to do
if not self._target_brightness:
return
if brightness is None:
# Level 255 means to set it to previous value.
zwave_brightness = 255
@ -426,3 +436,77 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
self._rgbw_color = (red, green, blue, white)
# Light supports rgbw, set color mode to rgbw
self._color_mode = COLOR_MODE_RGBW
class ZwaveBlackIsOffLight(ZwaveLight):
"""
Representation of a Z-Wave light where setting the color to black turns it off.
Currently only supports lights with RGB, no color temperature, and no white channels.
"""
def __init__(
self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo
) -> None:
"""Initialize the light."""
super().__init__(config_entry, client, info)
self._last_color: dict[str, int] | None = None
self._supported_color_modes.discard(COLOR_MODE_BRIGHTNESS)
@property
def brightness(self) -> int:
"""Return the brightness of this light between 0..255."""
return 255
@property
def is_on(self) -> bool | None:
"""Return true if device is on (brightness above 0)."""
if self.info.primary_value.value is None:
return None
return any(value != 0 for value in self.info.primary_value.value.values())
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the device on."""
await super().async_turn_on(**kwargs)
if (
kwargs.get(ATTR_RGBW_COLOR) is not None
or kwargs.get(ATTR_COLOR_TEMP) is not None
or kwargs.get(ATTR_HS_COLOR) is not None
):
return
transition = kwargs.get(ATTR_TRANSITION)
# turn on light to last color if known, otherwise set to white
if self._last_color is not None:
await self._async_set_colors(
{
ColorComponent.RED: self._last_color["red"],
ColorComponent.GREEN: self._last_color["green"],
ColorComponent.BLUE: self._last_color["blue"],
},
transition,
)
else:
await self._async_set_colors(
{
ColorComponent.RED: 255,
ColorComponent.GREEN: 255,
ColorComponent.BLUE: 255,
},
transition,
)
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the light off."""
self._last_color = self.info.primary_value.value
await self._async_set_colors(
{
ColorComponent.RED: 0,
ColorComponent.GREEN: 0,
ColorComponent.BLUE: 0,
},
kwargs.get(ATTR_TRANSITION),
)
await self._async_set_brightness(0, kwargs.get(ATTR_TRANSITION))

View File

@ -497,6 +497,12 @@ def zp3111_state_fixture():
return json.loads(load_fixture("zwave_js/zp3111-5_state.json"))
@pytest.fixture(name="express_controls_ezmultipli_state", scope="session")
def light_express_controls_ezmultipli_state_fixture():
"""Load the Express Controls EZMultiPli node state fixture data."""
return json.loads(load_fixture("zwave_js/express_controls_ezmultipli_state.json"))
@pytest.fixture(name="client")
def mock_client_fixture(controller_state, version_state, log_config_state):
"""Mock a client."""
@ -981,3 +987,11 @@ def zp3111_fixture(client, zp3111_state):
node = Node(client, copy.deepcopy(zp3111_state))
client.driver.controller.nodes[node.node_id] = node
return node
@pytest.fixture(name="express_controls_ezmultipli")
def express_controls_ezmultipli_fixture(client, express_controls_ezmultipli_state):
"""Mock a Express Controls EZMultiPli node."""
node = Node(client, copy.deepcopy(express_controls_ezmultipli_state))
client.driver.controller.nodes[node.node_id] = node
return node

View File

@ -0,0 +1,673 @@
{
"nodeId": 96,
"index": 0,
"installerIcon": 3079,
"userIcon": 3079,
"status": 4,
"ready": true,
"isListening": true,
"isRouting": true,
"isSecure": false,
"manufacturerId": 30,
"productId": 1,
"productType": 4,
"firmwareVersion": "1.8",
"zwavePlusVersion": 1,
"name": "HSM200",
"location": "Basement",
"deviceConfig": {
"filename": "/data/db/devices/0x001e/ezmultipli.json",
"isEmbedded": true,
"manufacturer": "Express Controls",
"manufacturerId": 30,
"label": "EZMultiPli",
"description": "Multi Sensor",
"devices": [
{
"productType": 4,
"productId": 1
}
],
"firmwareVersion": {
"min": "0.0",
"max": "255.255"
},
"associations": {},
"paramInformation": {
"_map": {}
}
},
"label": "EZMultiPli",
"interviewAttempts": 1,
"endpoints": [
{
"nodeId": 96,
"index": 0,
"installerIcon": 3079,
"userIcon": 3079,
"deviceClass": {
"basic": {
"key": 4,
"label": "Routing Slave"
},
"generic": {
"key": 7,
"label": "Notification Sensor"
},
"specific": {
"key": 1,
"label": "Notification Sensor"
},
"mandatorySupportedCCs": [],
"mandatoryControlledCCs": []
}
}
],
"values": [
{
"endpoint": 0,
"commandClass": 113,
"commandClassName": "Notification",
"property": "Home Security",
"propertyKey": "Motion sensor status",
"propertyName": "Home Security",
"propertyKeyName": "Motion sensor status",
"ccVersion": 3,
"metadata": {
"type": "number",
"readable": true,
"writeable": false,
"label": "Motion sensor status",
"ccSpecific": {
"notificationType": 7
},
"min": 0,
"max": 255,
"states": {
"0": "idle",
"7": "Motion detection (location provided)"
}
},
"value": 0
},
{
"endpoint": 0,
"commandClass": 49,
"commandClassName": "Multilevel Sensor",
"property": "Air temperature",
"propertyName": "Air temperature",
"ccVersion": 6,
"metadata": {
"type": "number",
"readable": true,
"writeable": false,
"label": "Air temperature",
"ccSpecific": {
"sensorType": 1,
"scale": 0
},
"unit": "\u00b0C"
},
"value": 16.8
},
{
"endpoint": 0,
"commandClass": 49,
"commandClassName": "Multilevel Sensor",
"property": "Illuminance",
"propertyName": "Illuminance",
"ccVersion": 6,
"metadata": {
"type": "number",
"readable": true,
"writeable": false,
"label": "Illuminance",
"ccSpecific": {
"sensorType": 3,
"scale": 0
},
"unit": "%"
},
"value": 61
},
{
"endpoint": 0,
"commandClass": 51,
"commandClassName": "Color Switch",
"property": "duration",
"propertyName": "duration",
"ccVersion": 1,
"metadata": {
"type": "duration",
"readable": true,
"writeable": true,
"label": "Remaining duration"
}
},
{
"endpoint": 0,
"commandClass": 51,
"commandClassName": "Color Switch",
"property": "currentColor",
"propertyKey": 2,
"propertyName": "currentColor",
"propertyKeyName": "Red",
"ccVersion": 1,
"metadata": {
"type": "number",
"readable": true,
"writeable": false,
"description": "The current value of the Red color.",
"label": "Current value (Red)",
"min": 0,
"max": 255
},
"value": 0
},
{
"endpoint": 0,
"commandClass": 51,
"commandClassName": "Color Switch",
"property": "currentColor",
"propertyKey": 3,
"propertyName": "currentColor",
"propertyKeyName": "Green",
"ccVersion": 1,
"metadata": {
"type": "number",
"readable": true,
"writeable": false,
"description": "The current value of the Green color.",
"label": "Current value (Green)",
"min": 0,
"max": 255
},
"value": 255
},
{
"endpoint": 0,
"commandClass": 51,
"commandClassName": "Color Switch",
"property": "currentColor",
"propertyKey": 4,
"propertyName": "currentColor",
"propertyKeyName": "Blue",
"ccVersion": 1,
"metadata": {
"type": "number",
"readable": true,
"writeable": false,
"description": "The current value of the Blue color.",
"label": "Current value (Blue)",
"min": 0,
"max": 255
},
"value": 0
},
{
"endpoint": 0,
"commandClass": 51,
"commandClassName": "Color Switch",
"property": "currentColor",
"propertyName": "currentColor",
"ccVersion": 1,
"metadata": {
"type": "any",
"readable": true,
"writeable": false,
"label": "Current Color"
},
"value": {
"red": 0,
"green": 255,
"blue": 0
}
},
{
"endpoint": 0,
"commandClass": 51,
"commandClassName": "Color Switch",
"property": "hexColor",
"propertyName": "hexColor",
"ccVersion": 1,
"metadata": {
"type": "color",
"readable": true,
"writeable": true,
"label": "RGB Color",
"minLength": 6,
"maxLength": 7
},
"value": "00ff00"
},
{
"endpoint": 0,
"commandClass": 51,
"commandClassName": "Color Switch",
"property": "targetColor",
"propertyKey": 2,
"propertyName": "targetColor",
"propertyKeyName": "Red",
"ccVersion": 1,
"metadata": {
"type": "number",
"readable": true,
"writeable": true,
"description": "The target value of the Red color.",
"label": "Target value (Red)",
"min": 0,
"max": 255
}
},
{
"endpoint": 0,
"commandClass": 51,
"commandClassName": "Color Switch",
"property": "targetColor",
"propertyKey": 3,
"propertyName": "targetColor",
"propertyKeyName": "Green",
"ccVersion": 1,
"metadata": {
"type": "number",
"readable": true,
"writeable": true,
"description": "The target value of the Green color.",
"label": "Target value (Green)",
"min": 0,
"max": 255
}
},
{
"endpoint": 0,
"commandClass": 51,
"commandClassName": "Color Switch",
"property": "targetColor",
"propertyKey": 4,
"propertyName": "targetColor",
"propertyKeyName": "Blue",
"ccVersion": 1,
"metadata": {
"type": "number",
"readable": true,
"writeable": true,
"description": "The target value of the Blue color.",
"label": "Target value (Blue)",
"min": 0,
"max": 255
}
},
{
"endpoint": 0,
"commandClass": 51,
"commandClassName": "Color Switch",
"property": "targetColor",
"propertyName": "targetColor",
"ccVersion": 1,
"metadata": {
"type": "any",
"readable": true,
"writeable": true,
"label": "Target Color",
"valueChangeOptions": ["transitionDuration"]
}
},
{
"endpoint": 0,
"commandClass": 114,
"commandClassName": "Manufacturer Specific",
"property": "manufacturerId",
"propertyName": "manufacturerId",
"ccVersion": 2,
"metadata": {
"type": "number",
"readable": true,
"writeable": false,
"label": "Manufacturer ID",
"min": 0,
"max": 65535
},
"value": 30
},
{
"endpoint": 0,
"commandClass": 114,
"commandClassName": "Manufacturer Specific",
"property": "productType",
"propertyName": "productType",
"ccVersion": 2,
"metadata": {
"type": "number",
"readable": true,
"writeable": false,
"label": "Product type",
"min": 0,
"max": 65535
},
"value": 4
},
{
"endpoint": 0,
"commandClass": 114,
"commandClassName": "Manufacturer Specific",
"property": "productId",
"propertyName": "productId",
"ccVersion": 2,
"metadata": {
"type": "number",
"readable": true,
"writeable": false,
"label": "Product ID",
"min": 0,
"max": 65535
},
"value": 1
},
{
"endpoint": 0,
"commandClass": 134,
"commandClassName": "Version",
"property": "libraryType",
"propertyName": "libraryType",
"ccVersion": 2,
"metadata": {
"type": "number",
"readable": true,
"writeable": false,
"label": "Library type",
"states": {
"0": "Unknown",
"1": "Static Controller",
"2": "Controller",
"3": "Enhanced Slave",
"4": "Slave",
"5": "Installer",
"6": "Routing Slave",
"7": "Bridge Controller",
"8": "Device under Test",
"9": "N/A",
"10": "AV Remote",
"11": "AV Device"
}
},
"value": 3
},
{
"endpoint": 0,
"commandClass": 134,
"commandClassName": "Version",
"property": "protocolVersion",
"propertyName": "protocolVersion",
"ccVersion": 2,
"metadata": {
"type": "string",
"readable": true,
"writeable": false,
"label": "Z-Wave protocol version"
},
"value": "4.5"
},
{
"endpoint": 0,
"commandClass": 134,
"commandClassName": "Version",
"property": "firmwareVersions",
"propertyName": "firmwareVersions",
"ccVersion": 2,
"metadata": {
"type": "string[]",
"readable": true,
"writeable": false,
"label": "Z-Wave chip firmware versions"
},
"value": ["1.8"]
},
{
"endpoint": 0,
"commandClass": 134,
"commandClassName": "Version",
"property": "hardwareVersion",
"propertyName": "hardwareVersion",
"ccVersion": 2,
"metadata": {
"type": "number",
"readable": true,
"writeable": false,
"label": "Z-Wave chip hardware version"
},
"value": 2
},
{
"endpoint": 0,
"commandClass": 112,
"commandClassName": "Configuration",
"property": 1,
"propertyName": "OnTime",
"ccVersion": 2,
"metadata": {
"type": "number",
"readable": true,
"writeable": true,
"label": "OnTime",
"default": 10,
"min": 0,
"max": 127,
"unit": "Minutes",
"valueSize": 1,
"format": 0,
"allowManualEntry": true,
"isFromConfig": true
},
"value": 10
},
{
"endpoint": 0,
"commandClass": 112,
"commandClassName": "Configuration",
"property": 2,
"propertyName": "OnLevel",
"ccVersion": 2,
"metadata": {
"type": "number",
"readable": true,
"writeable": true,
"description": "Allowable range: 0-99, 255",
"label": "OnLevel",
"default": 255,
"min": 0,
"max": 255,
"valueSize": 1,
"format": 1,
"allowManualEntry": true,
"isFromConfig": true
},
"value": 255
},
{
"endpoint": 0,
"commandClass": 112,
"commandClassName": "Configuration",
"property": 3,
"propertyName": "LiteMin",
"ccVersion": 2,
"metadata": {
"type": "number",
"readable": true,
"writeable": true,
"label": "LiteMin",
"default": 60,
"min": 0,
"max": 127,
"unit": "Minutes",
"valueSize": 1,
"format": 0,
"allowManualEntry": true,
"isFromConfig": true
},
"value": 60
},
{
"endpoint": 0,
"commandClass": 112,
"commandClassName": "Configuration",
"property": 4,
"propertyName": "TempMin",
"ccVersion": 2,
"metadata": {
"type": "number",
"readable": true,
"writeable": true,
"description": "A Temperature report is sent to the controller every TempMin minutes.",
"label": "TempMin",
"default": 60,
"min": 0,
"max": 127,
"unit": "Minutes",
"valueSize": 1,
"format": 0,
"allowManualEntry": true,
"isFromConfig": true
},
"value": 60
},
{
"endpoint": 0,
"commandClass": 112,
"commandClassName": "Configuration",
"property": 5,
"propertyName": "TempAdj",
"ccVersion": 2,
"metadata": {
"type": "number",
"readable": true,
"writeable": true,
"label": "TempAdj",
"default": 0,
"min": -128,
"max": 127,
"valueSize": 1,
"format": 0,
"allowManualEntry": true,
"isFromConfig": true
},
"value": -40
}
],
"isFrequentListening": false,
"maxDataRate": 100000,
"supportedDataRates": [40000, 100000],
"protocolVersion": 3,
"supportsBeaming": true,
"supportsSecurity": false,
"nodeType": 1,
"zwavePlusNodeType": 0,
"zwavePlusRoleType": 5,
"deviceClass": {
"basic": {
"key": 4,
"label": "Routing Slave"
},
"generic": {
"key": 7,
"label": "Notification Sensor"
},
"specific": {
"key": 1,
"label": "Notification Sensor"
},
"mandatorySupportedCCs": [],
"mandatoryControlledCCs": []
},
"commandClasses": [
{
"id": 94,
"name": "Z-Wave Plus Info",
"version": 2,
"isSecure": false
},
{
"id": 113,
"name": "Notification",
"version": 3,
"isSecure": false
},
{
"id": 49,
"name": "Multilevel Sensor",
"version": 6,
"isSecure": false
},
{
"id": 51,
"name": "Color Switch",
"version": 1,
"isSecure": false
},
{
"id": 114,
"name": "Manufacturer Specific",
"version": 2,
"isSecure": false
},
{
"id": 134,
"name": "Version",
"version": 2,
"isSecure": false
},
{
"id": 89,
"name": "Association Group Information",
"version": 1,
"isSecure": false
},
{
"id": 133,
"name": "Association",
"version": 2,
"isSecure": false
},
{
"id": 112,
"name": "Configuration",
"version": 2,
"isSecure": false
},
{
"id": 119,
"name": "Node Naming and Location",
"version": 1,
"isSecure": false
},
{
"id": 90,
"name": "Device Reset Locally",
"version": 1,
"isSecure": false
},
{
"id": 122,
"name": "Firmware Update Meta Data",
"version": 2,
"isSecure": false
},
{
"id": 115,
"name": "Powerlevel",
"version": 1,
"isSecure": false
}
],
"interviewStage": "Complete",
"deviceDatabaseUrl": "https://devices.zwave-js.io/?jumpTo=0x001e:0x0004:0x0001:1.8",
"statistics": {
"commandsTX": 147,
"commandsRX": 322,
"commandsDroppedRX": 0,
"commandsDroppedTX": 3,
"timeoutResponse": 0
},
"highestSecurityClass": -1,
"isControllerNode": false,
"keepAwake": false
}

View File

@ -13,9 +13,17 @@ from homeassistant.components.light import (
ATTR_RGBW_COLOR,
ATTR_SUPPORTED_COLOR_MODES,
ATTR_TRANSITION,
DOMAIN as LIGHT_DOMAIN,
SUPPORT_TRANSITION,
)
from homeassistant.const import ATTR_SUPPORTED_FEATURES, STATE_OFF, STATE_ON
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_SUPPORTED_FEATURES,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
STATE_OFF,
STATE_ON,
)
from .common import (
AEON_SMART_SWITCH_LIGHT_ENTITY,
@ -24,6 +32,8 @@ from .common import (
ZEN_31_ENTITY,
)
HSM200_V1_ENTITY = "light.hsm200"
async def test_light(hass, client, bulb_6_multi_color, integration):
"""Test the light entity."""
@ -62,7 +72,6 @@ async def test_light(hass, client, bulb_6_multi_color, integration):
"type": "number",
"readable": True,
"writeable": True,
"label": "Target value",
"valueChangeOptions": ["transitionDuration"],
},
}
@ -95,7 +104,6 @@ async def test_light(hass, client, bulb_6_multi_color, integration):
"type": "number",
"readable": True,
"writeable": True,
"label": "Target value",
"valueChangeOptions": ["transitionDuration"],
},
}
@ -168,7 +176,6 @@ async def test_light(hass, client, bulb_6_multi_color, integration):
"type": "number",
"readable": True,
"writeable": True,
"label": "Target value",
"valueChangeOptions": ["transitionDuration"],
},
}
@ -206,7 +213,6 @@ async def test_light(hass, client, bulb_6_multi_color, integration):
"type": "number",
"readable": True,
"writeable": True,
"label": "Target value",
"valueChangeOptions": ["transitionDuration"],
},
}
@ -444,7 +450,6 @@ async def test_light(hass, client, bulb_6_multi_color, integration):
"type": "number",
"readable": True,
"writeable": True,
"label": "Target value",
"valueChangeOptions": ["transitionDuration"],
},
}
@ -469,7 +474,6 @@ async def test_optional_light(hass, client, aeon_smart_switch_6, integration):
async def test_rgbw_light(hass, client, zen_31, integration):
"""Test the light entity."""
zen_31
state = hass.states.get(ZEN_31_ENTITY)
assert state
@ -523,7 +527,6 @@ async def test_rgbw_light(hass, client, zen_31, integration):
"type": "number",
"readable": True,
"writeable": True,
"label": "Target value",
"valueChangeOptions": ["transitionDuration"],
},
"value": 59,
@ -542,3 +545,161 @@ async def test_light_none_color_value(hass, light_color_null_values, integration
assert state.state == STATE_ON
assert state.attributes[ATTR_SUPPORTED_FEATURES] == SUPPORT_TRANSITION
assert state.attributes[ATTR_SUPPORTED_COLOR_MODES] == ["hs"]
async def test_black_is_off(hass, client, express_controls_ezmultipli, integration):
"""Test the black is off light entity."""
node = express_controls_ezmultipli
state = hass.states.get(HSM200_V1_ENTITY)
assert state.state == STATE_ON
# Attempt to turn on the light and ensure it defaults to white
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: HSM200_V1_ENTITY},
blocking=True,
)
assert len(client.async_send_command.call_args_list) == 1
args = client.async_send_command.call_args_list[0][0][0]
assert args["command"] == "node.set_value"
assert args["nodeId"] == node.node_id
assert args["valueId"] == {
"commandClassName": "Color Switch",
"commandClass": 51,
"endpoint": 0,
"property": "targetColor",
"propertyName": "targetColor",
"ccVersion": 1,
"metadata": {
"label": "Target Color",
"type": "any",
"readable": True,
"writeable": True,
"valueChangeOptions": ["transitionDuration"],
},
}
assert args["value"] == {"red": 255, "green": 255, "blue": 255}
client.async_send_command.reset_mock()
# Force the light to turn off
event = Event(
type="value updated",
data={
"source": "node",
"event": "value updated",
"nodeId": node.node_id,
"args": {
"commandClassName": "Color Switch",
"commandClass": 51,
"endpoint": 0,
"property": "currentColor",
"newValue": {
"red": 0,
"green": 0,
"blue": 0,
},
"prevValue": {
"red": 0,
"green": 255,
"blue": 0,
},
"propertyName": "currentColor",
},
},
)
node.receive_event(event)
await hass.async_block_till_done()
state = hass.states.get(HSM200_V1_ENTITY)
assert state.state == STATE_OFF
# Force the light to turn on
event = Event(
type="value updated",
data={
"source": "node",
"event": "value updated",
"nodeId": node.node_id,
"args": {
"commandClassName": "Color Switch",
"commandClass": 51,
"endpoint": 0,
"property": "currentColor",
"newValue": {
"red": 0,
"green": 255,
"blue": 0,
},
"prevValue": {
"red": 0,
"green": 0,
"blue": 0,
},
"propertyName": "currentColor",
},
},
)
node.receive_event(event)
await hass.async_block_till_done()
state = hass.states.get(HSM200_V1_ENTITY)
assert state.state == STATE_ON
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: HSM200_V1_ENTITY},
blocking=True,
)
assert len(client.async_send_command.call_args_list) == 1
args = client.async_send_command.call_args_list[0][0][0]
assert args["command"] == "node.set_value"
assert args["nodeId"] == node.node_id
assert args["valueId"] == {
"commandClassName": "Color Switch",
"commandClass": 51,
"endpoint": 0,
"property": "targetColor",
"propertyName": "targetColor",
"ccVersion": 1,
"metadata": {
"label": "Target Color",
"type": "any",
"readable": True,
"writeable": True,
"valueChangeOptions": ["transitionDuration"],
},
}
assert args["value"] == {"red": 0, "green": 0, "blue": 0}
client.async_send_command.reset_mock()
# Assert that the last color is restored
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: HSM200_V1_ENTITY},
blocking=True,
)
assert len(client.async_send_command.call_args_list) == 1
args = client.async_send_command.call_args_list[0][0][0]
assert args["command"] == "node.set_value"
assert args["nodeId"] == node.node_id
assert args["valueId"] == {
"commandClassName": "Color Switch",
"commandClass": 51,
"endpoint": 0,
"property": "targetColor",
"propertyName": "targetColor",
"ccVersion": 1,
"metadata": {
"label": "Target Color",
"type": "any",
"readable": True,
"writeable": True,
"valueChangeOptions": ["transitionDuration"],
},
}
assert args["value"] == {"red": 0, "green": 255, "blue": 0}
client.async_send_command.reset_mock()