mirror of
https://github.com/home-assistant/core.git
synced 2025-07-16 17:57:11 +00:00
Fix zwave_js device actions (#63769)
This commit is contained in:
parent
b5bb692fe4
commit
7b3e5fdf9d
@ -14,7 +14,14 @@ from zwave_js_server.util.command_class.meter import get_meter_type
|
|||||||
|
|
||||||
from homeassistant.components.lock import DOMAIN as LOCK_DOMAIN
|
from homeassistant.components.lock import DOMAIN as LOCK_DOMAIN
|
||||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||||
from homeassistant.const import CONF_DEVICE_ID, CONF_DOMAIN, CONF_ENTITY_ID, CONF_TYPE
|
from homeassistant.const import (
|
||||||
|
ATTR_DEVICE_ID,
|
||||||
|
ATTR_DOMAIN,
|
||||||
|
CONF_DEVICE_ID,
|
||||||
|
CONF_DOMAIN,
|
||||||
|
CONF_ENTITY_ID,
|
||||||
|
CONF_TYPE,
|
||||||
|
)
|
||||||
from homeassistant.core import Context, HomeAssistant
|
from homeassistant.core import Context, HomeAssistant
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers import entity_registry
|
from homeassistant.helpers import entity_registry
|
||||||
@ -227,7 +234,22 @@ async def async_call_action_from_config(
|
|||||||
if action_type not in ACTION_TYPES:
|
if action_type not in ACTION_TYPES:
|
||||||
raise HomeAssistantError(f"Unhandled action type {action_type}")
|
raise HomeAssistantError(f"Unhandled action type {action_type}")
|
||||||
|
|
||||||
service_data = {k: v for k, v in config.items() if v not in (None, "")}
|
# Don't include domain, subtype or any null/empty values in the service call
|
||||||
|
service_data = {
|
||||||
|
k: v
|
||||||
|
for k, v in config.items()
|
||||||
|
if k not in (ATTR_DOMAIN, CONF_SUBTYPE) and v not in (None, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
# Entity services (including refresh value which is a fake entity service) expects
|
||||||
|
# just an entity ID
|
||||||
|
if action_type in (
|
||||||
|
SERVICE_REFRESH_VALUE,
|
||||||
|
SERVICE_SET_LOCK_USERCODE,
|
||||||
|
SERVICE_CLEAR_LOCK_USERCODE,
|
||||||
|
SERVICE_RESET_METER,
|
||||||
|
):
|
||||||
|
service_data.pop(ATTR_DEVICE_ID)
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
DOMAIN, service, service_data, blocking=True, context=context
|
DOMAIN, service, service_data, blocking=True, context=context
|
||||||
)
|
)
|
||||||
@ -283,7 +305,10 @@ async def async_get_action_capabilities(
|
|||||||
"extra_fields": vol.Schema(
|
"extra_fields": vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Required(ATTR_COMMAND_CLASS): vol.In(
|
vol.Required(ATTR_COMMAND_CLASS): vol.In(
|
||||||
{cc.value: cc.name for cc in CommandClass}
|
{
|
||||||
|
CommandClass(cc.id).value: cc.name
|
||||||
|
for cc in sorted(node.command_classes, key=lambda cc: cc.name) # type: ignore[no-any-return]
|
||||||
|
}
|
||||||
),
|
),
|
||||||
vol.Required(ATTR_PROPERTY): cv.string,
|
vol.Required(ATTR_PROPERTY): cv.string,
|
||||||
vol.Optional(ATTR_PROPERTY_KEY): cv.string,
|
vol.Optional(ATTR_PROPERTY_KEY): cv.string,
|
||||||
|
@ -57,7 +57,128 @@
|
|||||||
},
|
},
|
||||||
{ "nodeId": 13, "index": 2 }
|
{ "nodeId": 13, "index": 2 }
|
||||||
],
|
],
|
||||||
"commandClasses": [],
|
"commandClasses": [
|
||||||
|
{
|
||||||
|
"id": 49,
|
||||||
|
"name": "Multilevel Sensor",
|
||||||
|
"version": 5,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 64,
|
||||||
|
"name": "Thermostat Mode",
|
||||||
|
"version": 2,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 66,
|
||||||
|
"name": "Thermostat Operating State",
|
||||||
|
"version": 2,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 67,
|
||||||
|
"name": "Thermostat Setpoint",
|
||||||
|
"version": 2,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 68,
|
||||||
|
"name": "Thermostat Fan Mode",
|
||||||
|
"version": 1,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 69,
|
||||||
|
"name": "Thermostat Fan State",
|
||||||
|
"version": 1,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 89,
|
||||||
|
"name": "Association Group Information",
|
||||||
|
"version": 1,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 90,
|
||||||
|
"name": "Device Reset Locally",
|
||||||
|
"version": 1,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 94,
|
||||||
|
"name": "Z-Wave Plus Info",
|
||||||
|
"version": 2,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 96,
|
||||||
|
"name": "Multi Channel",
|
||||||
|
"version": 4,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 112,
|
||||||
|
"name": "Configuration",
|
||||||
|
"version": 1,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 114,
|
||||||
|
"name": "Manufacturer Specific",
|
||||||
|
"version": 2,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 115,
|
||||||
|
"name": "Powerlevel",
|
||||||
|
"version": 1,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 122,
|
||||||
|
"name": "Firmware Update Meta Data",
|
||||||
|
"version": 3,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 128,
|
||||||
|
"name": "Battery",
|
||||||
|
"version": 1,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 129,
|
||||||
|
"name": "Clock",
|
||||||
|
"version": 1,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 133,
|
||||||
|
"name": "Association",
|
||||||
|
"version": 2,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 134,
|
||||||
|
"name": "Version",
|
||||||
|
"version": 2,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 135,
|
||||||
|
"name": "Indicator",
|
||||||
|
"version": 1,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 142,
|
||||||
|
"name": "Multi Channel Association",
|
||||||
|
"version": 3,
|
||||||
|
"isSecure": false
|
||||||
|
}
|
||||||
|
],
|
||||||
"values": [
|
"values": [
|
||||||
{
|
{
|
||||||
"commandClassName": "Manufacturer Specific",
|
"commandClassName": "Manufacturer Specific",
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
"""The tests for Z-Wave JS device actions."""
|
"""The tests for Z-Wave JS device actions."""
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import voluptuous_serialize
|
import voluptuous_serialize
|
||||||
from zwave_js_server.client import Client
|
from zwave_js_server.client import Client
|
||||||
@ -15,7 +17,7 @@ from homeassistant.exceptions import HomeAssistantError
|
|||||||
from homeassistant.helpers import config_validation as cv, device_registry
|
from homeassistant.helpers import config_validation as cv, device_registry
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from tests.common import async_get_device_automations, async_mock_service
|
from tests.common import async_get_device_automations
|
||||||
|
|
||||||
|
|
||||||
async def test_get_actions(
|
async def test_get_actions(
|
||||||
@ -92,8 +94,130 @@ async def test_get_actions_meter(
|
|||||||
assert len(filtered_actions) > 0
|
assert len(filtered_actions) > 0
|
||||||
|
|
||||||
|
|
||||||
async def test_action(hass: HomeAssistant) -> None:
|
async def test_actions(
|
||||||
"""Test for turn_on and turn_off actions."""
|
hass: HomeAssistant,
|
||||||
|
client: Client,
|
||||||
|
climate_radio_thermostat_ct100_plus: Node,
|
||||||
|
integration: ConfigEntry,
|
||||||
|
) -> None:
|
||||||
|
"""Test actions."""
|
||||||
|
node = climate_radio_thermostat_ct100_plus
|
||||||
|
device_id = get_device_id(client, node)
|
||||||
|
dev_reg = device_registry.async_get(hass)
|
||||||
|
device = dev_reg.async_get_device({device_id})
|
||||||
|
assert device
|
||||||
|
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass,
|
||||||
|
automation.DOMAIN,
|
||||||
|
{
|
||||||
|
automation.DOMAIN: [
|
||||||
|
{
|
||||||
|
"trigger": {
|
||||||
|
"platform": "event",
|
||||||
|
"event_type": "test_event_refresh_value",
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"domain": DOMAIN,
|
||||||
|
"type": "refresh_value",
|
||||||
|
"device_id": device.id,
|
||||||
|
"entity_id": "climate.z_wave_thermostat",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": {
|
||||||
|
"platform": "event",
|
||||||
|
"event_type": "test_event_ping",
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"domain": DOMAIN,
|
||||||
|
"type": "ping",
|
||||||
|
"device_id": device.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": {
|
||||||
|
"platform": "event",
|
||||||
|
"event_type": "test_event_set_value",
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"domain": DOMAIN,
|
||||||
|
"type": "set_value",
|
||||||
|
"device_id": device.id,
|
||||||
|
"command_class": 112,
|
||||||
|
"property": 1,
|
||||||
|
"value": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": {
|
||||||
|
"platform": "event",
|
||||||
|
"event_type": "test_event_set_config_parameter",
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"domain": DOMAIN,
|
||||||
|
"type": "set_config_parameter",
|
||||||
|
"device_id": device.id,
|
||||||
|
"parameter": 1,
|
||||||
|
"bitmask": None,
|
||||||
|
"subtype": "2-112-0-3 (Beeper)",
|
||||||
|
"value": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch("zwave_js_server.model.node.Node.async_poll_value") as mock_call:
|
||||||
|
hass.bus.async_fire("test_event_refresh_value")
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
mock_call.assert_called_once()
|
||||||
|
args = mock_call.call_args_list[0][0]
|
||||||
|
assert len(args) == 1
|
||||||
|
assert args[0].value_id == "13-64-1-mode"
|
||||||
|
|
||||||
|
with patch("zwave_js_server.model.node.Node.async_ping") as mock_call:
|
||||||
|
hass.bus.async_fire("test_event_ping")
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
mock_call.assert_called_once()
|
||||||
|
args = mock_call.call_args_list[0][0]
|
||||||
|
assert len(args) == 0
|
||||||
|
|
||||||
|
with patch("zwave_js_server.model.node.Node.async_set_value") as mock_call:
|
||||||
|
hass.bus.async_fire("test_event_set_value")
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
mock_call.assert_called_once()
|
||||||
|
args = mock_call.call_args_list[0][0]
|
||||||
|
assert len(args) == 2
|
||||||
|
assert args[0] == "13-112-0-1"
|
||||||
|
assert args[1] == 1
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.zwave_js.services.async_set_config_parameter"
|
||||||
|
) as mock_call:
|
||||||
|
hass.bus.async_fire("test_event_set_config_parameter")
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
mock_call.assert_called_once()
|
||||||
|
args = mock_call.call_args_list[0][0]
|
||||||
|
assert len(args) == 3
|
||||||
|
assert args[0].node_id == 13
|
||||||
|
assert args[1] == 1
|
||||||
|
assert args[2] == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_lock_actions(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
client: Client,
|
||||||
|
lock_schlage_be469: Node,
|
||||||
|
integration: ConfigEntry,
|
||||||
|
) -> None:
|
||||||
|
"""Test actions for locks."""
|
||||||
|
node = lock_schlage_be469
|
||||||
|
device_id = get_device_id(client, node)
|
||||||
|
dev_reg = device_registry.async_get(hass)
|
||||||
|
device = dev_reg.async_get_device({device_id})
|
||||||
|
assert device
|
||||||
|
|
||||||
assert await async_setup_component(
|
assert await async_setup_component(
|
||||||
hass,
|
hass,
|
||||||
automation.DOMAIN,
|
automation.DOMAIN,
|
||||||
@ -107,7 +231,7 @@ async def test_action(hass: HomeAssistant) -> None:
|
|||||||
"action": {
|
"action": {
|
||||||
"domain": DOMAIN,
|
"domain": DOMAIN,
|
||||||
"type": "clear_lock_usercode",
|
"type": "clear_lock_usercode",
|
||||||
"device_id": "fake",
|
"device_id": device.id,
|
||||||
"entity_id": "lock.touchscreen_deadbolt",
|
"entity_id": "lock.touchscreen_deadbolt",
|
||||||
"code_slot": 1,
|
"code_slot": 1,
|
||||||
},
|
},
|
||||||
@ -120,97 +244,80 @@ async def test_action(hass: HomeAssistant) -> None:
|
|||||||
"action": {
|
"action": {
|
||||||
"domain": DOMAIN,
|
"domain": DOMAIN,
|
||||||
"type": "set_lock_usercode",
|
"type": "set_lock_usercode",
|
||||||
"device_id": "fake",
|
"device_id": device.id,
|
||||||
"entity_id": "lock.touchscreen_deadbolt",
|
"entity_id": "lock.touchscreen_deadbolt",
|
||||||
"code_slot": 1,
|
"code_slot": 1,
|
||||||
"usercode": "1234",
|
"usercode": "1234",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"trigger": {
|
|
||||||
"platform": "event",
|
|
||||||
"event_type": "test_event_refresh_value",
|
|
||||||
},
|
|
||||||
"action": {
|
|
||||||
"domain": DOMAIN,
|
|
||||||
"type": "refresh_value",
|
|
||||||
"device_id": "fake",
|
|
||||||
"entity_id": "lock.touchscreen_deadbolt",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"trigger": {
|
|
||||||
"platform": "event",
|
|
||||||
"event_type": "test_event_ping",
|
|
||||||
},
|
|
||||||
"action": {
|
|
||||||
"domain": DOMAIN,
|
|
||||||
"type": "ping",
|
|
||||||
"device_id": "fake",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"trigger": {
|
|
||||||
"platform": "event",
|
|
||||||
"event_type": "test_event_set_value",
|
|
||||||
},
|
|
||||||
"action": {
|
|
||||||
"domain": DOMAIN,
|
|
||||||
"type": "set_value",
|
|
||||||
"device_id": "fake",
|
|
||||||
"command_class": 112,
|
|
||||||
"property": "test",
|
|
||||||
"value": 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"trigger": {
|
|
||||||
"platform": "event",
|
|
||||||
"event_type": "test_event_set_config_parameter",
|
|
||||||
},
|
|
||||||
"action": {
|
|
||||||
"domain": DOMAIN,
|
|
||||||
"type": "set_config_parameter",
|
|
||||||
"device_id": "fake",
|
|
||||||
"parameter": 3,
|
|
||||||
"bitmask": None,
|
|
||||||
"subtype": "2-112-0-3 (Beeper)",
|
|
||||||
"value": 255,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
clear_lock_usercode = async_mock_service(hass, "zwave_js", "clear_lock_usercode")
|
with patch("homeassistant.components.zwave_js.lock.clear_usercode") as mock_call:
|
||||||
hass.bus.async_fire("test_event_clear_lock_usercode")
|
hass.bus.async_fire("test_event_clear_lock_usercode")
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(clear_lock_usercode) == 1
|
mock_call.assert_called_once()
|
||||||
|
args = mock_call.call_args_list[0][0]
|
||||||
|
assert len(args) == 2
|
||||||
|
assert args[0].node_id == node.node_id
|
||||||
|
assert args[1] == 1
|
||||||
|
|
||||||
set_lock_usercode = async_mock_service(hass, "zwave_js", "set_lock_usercode")
|
with patch("homeassistant.components.zwave_js.lock.set_usercode") as mock_call:
|
||||||
hass.bus.async_fire("test_event_set_lock_usercode")
|
hass.bus.async_fire("test_event_set_lock_usercode")
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(set_lock_usercode) == 1
|
mock_call.assert_called_once()
|
||||||
|
args = mock_call.call_args_list[0][0]
|
||||||
|
assert len(args) == 3
|
||||||
|
assert args[0].node_id == node.node_id
|
||||||
|
assert args[1] == 1
|
||||||
|
assert args[2] == "1234"
|
||||||
|
|
||||||
refresh_value = async_mock_service(hass, "zwave_js", "refresh_value")
|
|
||||||
hass.bus.async_fire("test_event_refresh_value")
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
assert len(refresh_value) == 1
|
|
||||||
|
|
||||||
ping = async_mock_service(hass, "zwave_js", "ping")
|
async def test_reset_meter_action(
|
||||||
hass.bus.async_fire("test_event_ping")
|
hass: HomeAssistant,
|
||||||
await hass.async_block_till_done()
|
client: Client,
|
||||||
assert len(ping) == 1
|
aeon_smart_switch_6: Node,
|
||||||
|
integration: ConfigEntry,
|
||||||
|
) -> None:
|
||||||
|
"""Test reset_meter action."""
|
||||||
|
node = aeon_smart_switch_6
|
||||||
|
device_id = get_device_id(client, node)
|
||||||
|
dev_reg = device_registry.async_get(hass)
|
||||||
|
device = dev_reg.async_get_device({device_id})
|
||||||
|
assert device
|
||||||
|
|
||||||
set_value = async_mock_service(hass, "zwave_js", "set_value")
|
assert await async_setup_component(
|
||||||
hass.bus.async_fire("test_event_set_value")
|
hass,
|
||||||
await hass.async_block_till_done()
|
automation.DOMAIN,
|
||||||
assert len(set_value) == 1
|
{
|
||||||
|
automation.DOMAIN: [
|
||||||
|
{
|
||||||
|
"trigger": {
|
||||||
|
"platform": "event",
|
||||||
|
"event_type": "test_event_reset_meter",
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"domain": DOMAIN,
|
||||||
|
"type": "reset_meter",
|
||||||
|
"device_id": device.id,
|
||||||
|
"entity_id": "sensor.smart_switch_6_electric_consumed_kwh",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
set_config_parameter = async_mock_service(hass, "zwave_js", "set_config_parameter")
|
with patch(
|
||||||
hass.bus.async_fire("test_event_set_config_parameter")
|
"zwave_js_server.model.endpoint.Endpoint.async_invoke_cc_api"
|
||||||
await hass.async_block_till_done()
|
) as mock_call:
|
||||||
assert len(set_config_parameter) == 1
|
hass.bus.async_fire("test_event_reset_meter")
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
mock_call.assert_called_once()
|
||||||
|
args = mock_call.call_args_list[0][0]
|
||||||
|
assert len(args) == 2
|
||||||
|
assert args[0] == CommandClass.METER
|
||||||
|
assert args[1] == "reset"
|
||||||
|
|
||||||
|
|
||||||
async def test_get_action_capabilities(
|
async def test_get_action_capabilities(
|
||||||
@ -266,7 +373,28 @@ async def test_get_action_capabilities(
|
|||||||
)
|
)
|
||||||
assert capabilities and "extra_fields" in capabilities
|
assert capabilities and "extra_fields" in capabilities
|
||||||
|
|
||||||
cc_options = [(cc.value, cc.name) for cc in CommandClass]
|
cc_options = [
|
||||||
|
(133, "Association"),
|
||||||
|
(89, "Association Group Information"),
|
||||||
|
(128, "Battery"),
|
||||||
|
(129, "Clock"),
|
||||||
|
(112, "Configuration"),
|
||||||
|
(90, "Device Reset Locally"),
|
||||||
|
(122, "Firmware Update Meta Data"),
|
||||||
|
(135, "Indicator"),
|
||||||
|
(114, "Manufacturer Specific"),
|
||||||
|
(96, "Multi Channel"),
|
||||||
|
(142, "Multi Channel Association"),
|
||||||
|
(49, "Multilevel Sensor"),
|
||||||
|
(115, "Powerlevel"),
|
||||||
|
(68, "Thermostat Fan Mode"),
|
||||||
|
(69, "Thermostat Fan State"),
|
||||||
|
(64, "Thermostat Mode"),
|
||||||
|
(66, "Thermostat Operating State"),
|
||||||
|
(67, "Thermostat Setpoint"),
|
||||||
|
(134, "Version"),
|
||||||
|
(94, "Z-Wave Plus Info"),
|
||||||
|
]
|
||||||
|
|
||||||
assert voluptuous_serialize.convert(
|
assert voluptuous_serialize.convert(
|
||||||
capabilities["extra_fields"], custom_serializer=cv.custom_serializer
|
capabilities["extra_fields"], custom_serializer=cv.custom_serializer
|
||||||
|
Loading…
x
Reference in New Issue
Block a user