Add support for multilevel switch CC select entities (#56656)
* Add support for multilevel switch CC select entities * Use state names from docs and include more device identifiers from device DB * black * pylint * type fix * Add failure scenario test * Update homeassistant/components/zwave_js/select.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> Co-authored-by: Martin Hjelmare <marhje52@gmail.com>pull/55924/head
parent
2326e3ed94
commit
8716aa011a
|
@ -291,7 +291,7 @@ DISCOVERY_SCHEMAS = [
|
|||
type={"number"},
|
||||
),
|
||||
data_template=DynamicCurrentTempClimateDataTemplate(
|
||||
{
|
||||
lookup_table={
|
||||
# Internal Sensor
|
||||
"A": ZwaveValueID(
|
||||
THERMOSTAT_CURRENT_TEMP_PROPERTY,
|
||||
|
@ -321,7 +321,7 @@ DISCOVERY_SCHEMAS = [
|
|||
endpoint=4,
|
||||
),
|
||||
},
|
||||
ZwaveValueID(2, CommandClass.CONFIGURATION, endpoint=0),
|
||||
dependent_value=ZwaveValueID(2, CommandClass.CONFIGURATION, endpoint=0),
|
||||
),
|
||||
),
|
||||
# Heatit Z-TRM2fx
|
||||
|
@ -338,7 +338,7 @@ DISCOVERY_SCHEMAS = [
|
|||
type={"number"},
|
||||
),
|
||||
data_template=DynamicCurrentTempClimateDataTemplate(
|
||||
{
|
||||
lookup_table={
|
||||
# External Sensor
|
||||
"A2": ZwaveValueID(
|
||||
THERMOSTAT_CURRENT_TEMP_PROPERTY,
|
||||
|
@ -357,7 +357,24 @@ DISCOVERY_SCHEMAS = [
|
|||
endpoint=3,
|
||||
),
|
||||
},
|
||||
ZwaveValueID(2, CommandClass.CONFIGURATION, endpoint=0),
|
||||
dependent_value=ZwaveValueID(2, CommandClass.CONFIGURATION, endpoint=0),
|
||||
),
|
||||
),
|
||||
# FortrezZ SSA1/SSA2
|
||||
ZWaveDiscoverySchema(
|
||||
platform="select",
|
||||
hint="multilevel_switch",
|
||||
manufacturer_id={0x0084},
|
||||
product_id={0x0107, 0x0108, 0x010B, 0x0205},
|
||||
product_type={0x0311, 0x0313, 0x0341, 0x0343},
|
||||
primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
|
||||
data_template=BaseDiscoverySchemaDataTemplate(
|
||||
{
|
||||
0: "Off",
|
||||
33: "Strobe ONLY",
|
||||
66: "Siren ONLY",
|
||||
99: "Siren & Strobe FULL Alarm",
|
||||
},
|
||||
),
|
||||
),
|
||||
# ====== START OF CONFIG PARAMETER SPECIFIC MAPPING SCHEMAS =======
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Iterable
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any
|
||||
|
||||
from zwave_js_server.const import CommandClass
|
||||
|
@ -92,9 +92,12 @@ class ZwaveValueID:
|
|||
property_key: str | int | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class BaseDiscoverySchemaDataTemplate:
|
||||
"""Base class for discovery schema data templates."""
|
||||
|
||||
static_data: Any | None = None
|
||||
|
||||
def resolve_data(self, value: ZwaveValue) -> Any:
|
||||
"""
|
||||
Resolve helper class data for a discovered value.
|
||||
|
@ -141,11 +144,13 @@ class BaseDiscoverySchemaDataTemplate:
|
|||
class DynamicCurrentTempClimateDataTemplate(BaseDiscoverySchemaDataTemplate):
|
||||
"""Data template class for Z-Wave JS Climate entities with dynamic current temps."""
|
||||
|
||||
lookup_table: dict[str | int, ZwaveValueID]
|
||||
dependent_value: ZwaveValueID
|
||||
lookup_table: dict[str | int, ZwaveValueID] = field(default_factory=dict)
|
||||
dependent_value: ZwaveValueID | None = None
|
||||
|
||||
def resolve_data(self, value: ZwaveValue) -> dict[str, Any]:
|
||||
"""Resolve helper class data for a discovered value."""
|
||||
if not self.lookup_table or not self.dependent_value:
|
||||
raise ValueError("Invalid discovery data template")
|
||||
data: dict[str, Any] = {
|
||||
"lookup_table": {},
|
||||
"dependent_value": self._get_value_from_id(
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
"""Support for Z-Wave controls using the select platform."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Dict, cast
|
||||
|
||||
from zwave_js_server.client import Client as ZwaveClient
|
||||
from zwave_js_server.const import CommandClass
|
||||
from zwave_js_server.const.command_class.sound_switch import ToneID
|
||||
|
@ -30,6 +32,10 @@ async def async_setup_entry(
|
|||
entities: list[ZWaveBaseEntity] = []
|
||||
if info.platform_hint == "Default tone":
|
||||
entities.append(ZwaveDefaultToneSelectEntity(config_entry, client, info))
|
||||
elif info.platform_hint == "multilevel_switch":
|
||||
entities.append(
|
||||
ZwaveMultilevelSwitchSelectEntity(config_entry, client, info)
|
||||
)
|
||||
else:
|
||||
entities.append(ZwaveSelectEntity(config_entry, client, info))
|
||||
async_add_entities(entities)
|
||||
|
@ -126,3 +132,37 @@ class ZwaveDefaultToneSelectEntity(ZWaveBaseEntity, SelectEntity):
|
|||
if val == option
|
||||
)
|
||||
await self.info.node.async_set_value(self.info.primary_value, int(key))
|
||||
|
||||
|
||||
class ZwaveMultilevelSwitchSelectEntity(ZWaveBaseEntity, SelectEntity):
|
||||
"""Representation of a Z-Wave Multilevel Switch CC select entity."""
|
||||
|
||||
def __init__(
|
||||
self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo
|
||||
) -> None:
|
||||
"""Initialize a ZwaveSelectEntity entity."""
|
||||
super().__init__(config_entry, client, info)
|
||||
self._target_value = self.get_zwave_value("targetValue")
|
||||
assert self.info.platform_data_template
|
||||
self._lookup_map = cast(
|
||||
Dict[int, str], self.info.platform_data_template.static_data
|
||||
)
|
||||
|
||||
# Entity class attributes
|
||||
self._attr_options = list(self._lookup_map.values())
|
||||
|
||||
@property
|
||||
def current_option(self) -> str | None:
|
||||
"""Return the selected entity option to represent the entity state."""
|
||||
if self.info.primary_value.value is None:
|
||||
return None
|
||||
return str(
|
||||
self._lookup_map.get(
|
||||
int(self.info.primary_value.value), self.info.primary_value.value
|
||||
)
|
||||
)
|
||||
|
||||
async def async_select_option(self, option: str) -> None:
|
||||
"""Change the selected option."""
|
||||
key = next(key for key, val in self._lookup_map.items() if val == option)
|
||||
await self.info.node.async_set_value(self._target_value, int(key))
|
||||
|
|
|
@ -455,6 +455,12 @@ def lock_popp_electric_strike_lock_control_state_fixture():
|
|||
)
|
||||
|
||||
|
||||
@pytest.fixture(name="fortrezz_ssa1_siren_state", scope="session")
|
||||
def fortrezz_ssa1_siren_state_fixture():
|
||||
"""Load the fortrezz ssa1 siren node state fixture data."""
|
||||
return json.loads(load_fixture("zwave_js/fortrezz_ssa1_siren_state.json"))
|
||||
|
||||
|
||||
@pytest.fixture(name="client")
|
||||
def mock_client_fixture(controller_state, version_state, log_config_state):
|
||||
"""Mock a client."""
|
||||
|
@ -859,6 +865,14 @@ def lock_popp_electric_strike_lock_control_fixture(
|
|||
return node
|
||||
|
||||
|
||||
@pytest.fixture(name="fortrezz_ssa1_siren")
|
||||
def fortrezz_ssa1_siren_fixture(client, fortrezz_ssa1_siren_state):
|
||||
"""Mock a fortrezz ssa1 siren node."""
|
||||
node = Node(client, copy.deepcopy(fortrezz_ssa1_siren_state))
|
||||
client.driver.controller.nodes[node.node_id] = node
|
||||
return node
|
||||
|
||||
|
||||
@pytest.fixture(name="firmware_file")
|
||||
def firmware_file_fixture():
|
||||
"""Return mock firmware file stream."""
|
||||
|
|
|
@ -6,6 +6,9 @@ from homeassistant.components.zwave_js.discovery import (
|
|||
ZWaveDiscoverySchema,
|
||||
ZWaveValueDiscoverySchema,
|
||||
)
|
||||
from homeassistant.components.zwave_js.discovery_data_template import (
|
||||
DynamicCurrentTempClimateDataTemplate,
|
||||
)
|
||||
|
||||
|
||||
async def test_iblinds_v2(hass, client, iblinds_v2, integration):
|
||||
|
@ -76,3 +79,12 @@ async def test_firmware_version_range_exception(hass):
|
|||
ZWaveValueDiscoverySchema(command_class=1),
|
||||
firmware_version_range=FirmwareVersionRange(),
|
||||
)
|
||||
|
||||
|
||||
async def test_dynamic_climate_data_discovery_template_failure(hass, multisensor_6):
|
||||
"""Test that initing a DynamicCurrentTempClimateDataTemplate with no data raises."""
|
||||
node = multisensor_6
|
||||
with pytest.raises(ValueError):
|
||||
DynamicCurrentTempClimateDataTemplate().resolve_data(
|
||||
node.values[f"{node.node_id}-49-0-Ultraviolet"]
|
||||
)
|
||||
|
|
|
@ -5,6 +5,7 @@ from homeassistant.const import STATE_UNKNOWN
|
|||
|
||||
DEFAULT_TONE_SELECT_ENTITY = "select.indoor_siren_6_default_tone_2"
|
||||
PROTECTION_SELECT_ENTITY = "select.family_room_combo_local_protection_state"
|
||||
MULTILEVEL_SWITCH_SELECT_ENTITY = "select.front_door_siren"
|
||||
|
||||
|
||||
async def test_default_tone_select(hass, client, aeotec_zw164_siren, integration):
|
||||
|
@ -199,3 +200,75 @@ async def test_protection_select(hass, client, inovelli_lzw36, integration):
|
|||
|
||||
state = hass.states.get(PROTECTION_SELECT_ENTITY)
|
||||
assert state.state == STATE_UNKNOWN
|
||||
|
||||
|
||||
async def test_multilevel_switch_select(hass, client, fortrezz_ssa1_siren, integration):
|
||||
"""Test Multilevel Switch CC based select entity."""
|
||||
node = fortrezz_ssa1_siren
|
||||
state = hass.states.get(MULTILEVEL_SWITCH_SELECT_ENTITY)
|
||||
|
||||
assert state
|
||||
assert state.state == "Off"
|
||||
attr = state.attributes
|
||||
assert attr["options"] == [
|
||||
"Off",
|
||||
"Strobe ONLY",
|
||||
"Siren ONLY",
|
||||
"Siren & Strobe FULL Alarm",
|
||||
]
|
||||
|
||||
# Test select option with string value
|
||||
await hass.services.async_call(
|
||||
"select",
|
||||
"select_option",
|
||||
{"entity_id": MULTILEVEL_SWITCH_SELECT_ENTITY, "option": "Strobe ONLY"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 1
|
||||
args = client.async_send_command.call_args[0][0]
|
||||
assert args["command"] == "node.set_value"
|
||||
assert args["nodeId"] == node.node_id
|
||||
assert args["valueId"] == {
|
||||
"endpoint": 0,
|
||||
"commandClass": 38,
|
||||
"commandClassName": "Multilevel Switch",
|
||||
"property": "targetValue",
|
||||
"propertyName": "targetValue",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": True,
|
||||
"writeable": True,
|
||||
"label": "Target value",
|
||||
"valueChangeOptions": ["transitionDuration"],
|
||||
"min": 0,
|
||||
"max": 99,
|
||||
},
|
||||
}
|
||||
assert args["value"] == 33
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
# Test value update from value updated event
|
||||
event = Event(
|
||||
type="value updated",
|
||||
data={
|
||||
"source": "node",
|
||||
"event": "value updated",
|
||||
"nodeId": node.node_id,
|
||||
"args": {
|
||||
"commandClassName": "Multilevel Switch",
|
||||
"commandClass": 38,
|
||||
"endpoint": 0,
|
||||
"property": "currentValue",
|
||||
"newValue": 33,
|
||||
"prevValue": 0,
|
||||
"propertyName": "currentValue",
|
||||
},
|
||||
},
|
||||
)
|
||||
node.receive_event(event)
|
||||
|
||||
state = hass.states.get(MULTILEVEL_SWITCH_SELECT_ENTITY)
|
||||
assert state.state == "Strobe ONLY"
|
||||
|
|
|
@ -0,0 +1,350 @@
|
|||
{
|
||||
"nodeId": 80,
|
||||
"index": 0,
|
||||
"status": 4,
|
||||
"ready": true,
|
||||
"isListening": true,
|
||||
"isRouting": true,
|
||||
"isSecure": false,
|
||||
"manufacturerId": 132,
|
||||
"productId": 267,
|
||||
"productType": 787,
|
||||
"firmwareVersion": "1.11",
|
||||
"name": "Front Door Siren",
|
||||
"location": "Outside",
|
||||
"deviceConfig": {
|
||||
"filename": "/data/db/devices/0x0084/ssa1_ssa2.json",
|
||||
"isEmbedded": true,
|
||||
"manufacturer": "FortrezZ LLC",
|
||||
"manufacturerId": 132,
|
||||
"label": "SSA1/SSA2",
|
||||
"description": "Siren and Strobe Alarm",
|
||||
"devices": [
|
||||
{
|
||||
"productType": 785,
|
||||
"productId": 267
|
||||
},
|
||||
{
|
||||
"productType": 787,
|
||||
"productId": 264
|
||||
},
|
||||
{
|
||||
"productType": 787,
|
||||
"productId": 267
|
||||
}
|
||||
],
|
||||
"firmwareVersion": {
|
||||
"min": "0.0",
|
||||
"max": "255.255"
|
||||
},
|
||||
"paramInformation": {
|
||||
"_map": {}
|
||||
}
|
||||
},
|
||||
"label": "SSA1/SSA2",
|
||||
"interviewAttempts": 1,
|
||||
"endpoints": [
|
||||
{
|
||||
"nodeId": 80,
|
||||
"index": 0,
|
||||
"deviceClass": {
|
||||
"basic": {
|
||||
"key": 4,
|
||||
"label": "Routing Slave"
|
||||
},
|
||||
"generic": {
|
||||
"key": 17,
|
||||
"label": "Multilevel Switch"
|
||||
},
|
||||
"specific": {
|
||||
"key": 0,
|
||||
"label": "Unused"
|
||||
},
|
||||
"mandatorySupportedCCs": [32, 38],
|
||||
"mandatoryControlledCCs": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"values": [
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 38,
|
||||
"commandClassName": "Multilevel Switch",
|
||||
"property": "targetValue",
|
||||
"propertyName": "targetValue",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"label": "Target value",
|
||||
"valueChangeOptions": ["transitionDuration"],
|
||||
"min": 0,
|
||||
"max": 99
|
||||
}
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 38,
|
||||
"commandClassName": "Multilevel Switch",
|
||||
"property": "duration",
|
||||
"propertyName": "duration",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "duration",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"label": "Transition duration"
|
||||
}
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 38,
|
||||
"commandClassName": "Multilevel Switch",
|
||||
"property": "currentValue",
|
||||
"propertyName": "currentValue",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Current value",
|
||||
"min": 0,
|
||||
"max": 99
|
||||
},
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 38,
|
||||
"commandClassName": "Multilevel Switch",
|
||||
"property": "Up",
|
||||
"propertyName": "Up",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "boolean",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"label": "Perform a level change (Up)",
|
||||
"ccSpecific": {
|
||||
"switchType": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 38,
|
||||
"commandClassName": "Multilevel Switch",
|
||||
"property": "Down",
|
||||
"propertyName": "Down",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "boolean",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"label": "Perform a level change (Down)",
|
||||
"ccSpecific": {
|
||||
"switchType": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 114,
|
||||
"commandClassName": "Manufacturer Specific",
|
||||
"property": "manufacturerId",
|
||||
"propertyName": "manufacturerId",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Manufacturer ID",
|
||||
"min": 0,
|
||||
"max": 65535
|
||||
},
|
||||
"value": 132
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 114,
|
||||
"commandClassName": "Manufacturer Specific",
|
||||
"property": "productType",
|
||||
"propertyName": "productType",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Product type",
|
||||
"min": 0,
|
||||
"max": 65535
|
||||
},
|
||||
"value": 787
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 114,
|
||||
"commandClassName": "Manufacturer Specific",
|
||||
"property": "productId",
|
||||
"propertyName": "productId",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Product ID",
|
||||
"min": 0,
|
||||
"max": 65535
|
||||
},
|
||||
"value": 267
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 134,
|
||||
"commandClassName": "Version",
|
||||
"property": "libraryType",
|
||||
"propertyName": "libraryType",
|
||||
"ccVersion": 1,
|
||||
"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": 6
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 134,
|
||||
"commandClassName": "Version",
|
||||
"property": "protocolVersion",
|
||||
"propertyName": "protocolVersion",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "string",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Z-Wave protocol version"
|
||||
},
|
||||
"value": "2.97"
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 134,
|
||||
"commandClassName": "Version",
|
||||
"property": "firmwareVersions",
|
||||
"propertyName": "firmwareVersions",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "string[]",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Z-Wave chip firmware versions"
|
||||
},
|
||||
"value": ["1.11"]
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 112,
|
||||
"commandClassName": "Configuration",
|
||||
"property": 1,
|
||||
"propertyName": "Delay before accept of Basic Set Off",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"description": "Delay, from the time the siren-strobe turns on",
|
||||
"label": "Delay before accept of Basic Set Off",
|
||||
"default": 0,
|
||||
"min": 0,
|
||||
"max": 255,
|
||||
"unit": "Seconds",
|
||||
"valueSize": 1,
|
||||
"format": 1,
|
||||
"allowManualEntry": true,
|
||||
"isFromConfig": true
|
||||
},
|
||||
"value": 0
|
||||
}
|
||||
],
|
||||
"isFrequentListening": false,
|
||||
"maxDataRate": 40000,
|
||||
"supportedDataRates": [40000],
|
||||
"protocolVersion": 3,
|
||||
"supportsBeaming": true,
|
||||
"supportsSecurity": false,
|
||||
"nodeType": 1,
|
||||
"deviceClass": {
|
||||
"basic": {
|
||||
"key": 4,
|
||||
"label": "Routing Slave"
|
||||
},
|
||||
"generic": {
|
||||
"key": 17,
|
||||
"label": "Multilevel Switch"
|
||||
},
|
||||
"specific": {
|
||||
"key": 0,
|
||||
"label": "Unused"
|
||||
},
|
||||
"mandatorySupportedCCs": [32, 38],
|
||||
"mandatoryControlledCCs": []
|
||||
},
|
||||
"commandClasses": [
|
||||
{
|
||||
"id": 38,
|
||||
"name": "Multilevel Switch",
|
||||
"version": 1,
|
||||
"isSecure": false
|
||||
},
|
||||
{
|
||||
"id": 114,
|
||||
"name": "Manufacturer Specific",
|
||||
"version": 1,
|
||||
"isSecure": false
|
||||
},
|
||||
{
|
||||
"id": 134,
|
||||
"name": "Version",
|
||||
"version": 1,
|
||||
"isSecure": false
|
||||
},
|
||||
{
|
||||
"id": 113,
|
||||
"name": "Notification",
|
||||
"version": 2,
|
||||
"isSecure": false
|
||||
},
|
||||
{
|
||||
"id": 112,
|
||||
"name": "Configuration",
|
||||
"version": 1,
|
||||
"isSecure": false
|
||||
}
|
||||
],
|
||||
"interviewStage": "Complete",
|
||||
"deviceDatabaseUrl": "https://devices.zwave-js.io/?jumpTo=0x0084:0x0313:0x010b:1.11",
|
||||
"statistics": {
|
||||
"commandsTX": 12,
|
||||
"commandsRX": 64,
|
||||
"commandsDroppedRX": 0,
|
||||
"commandsDroppedTX": 0,
|
||||
"timeoutResponse": 2
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue