mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
Add zwave_js.bulk_set_partial_config_parameters service (#48306)
* Add zwave_js.bulk_set_partial_config_parameters service * update to handle command status * add test for awake node * test using a device in service call
This commit is contained in:
parent
114a97bf52
commit
9a75019a65
@ -52,6 +52,7 @@ ATTR_DATA_TYPE = "data_type"
|
||||
|
||||
# service constants
|
||||
SERVICE_SET_CONFIG_PARAMETER = "set_config_parameter"
|
||||
SERVICE_BULK_SET_PARTIAL_CONFIG_PARAMETERS = "bulk_set_partial_config_parameters"
|
||||
|
||||
ATTR_CONFIG_PARAMETER = "parameter"
|
||||
ATTR_CONFIG_PARAMETER_BITMASK = "bitmask"
|
||||
|
@ -6,7 +6,10 @@ import logging
|
||||
import voluptuous as vol
|
||||
from zwave_js_server.const import CommandStatus
|
||||
from zwave_js_server.model.node import Node as ZwaveNode
|
||||
from zwave_js_server.util.node import async_set_config_parameter
|
||||
from zwave_js_server.util.node import (
|
||||
async_bulk_set_partial_config_parameters,
|
||||
async_set_config_parameter,
|
||||
)
|
||||
|
||||
from homeassistant.const import ATTR_DEVICE_ID, ATTR_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant, ServiceCall, callback
|
||||
@ -37,7 +40,13 @@ def parameter_name_does_not_need_bitmask(
|
||||
# Validates that a bitmask is provided in hex form and converts it to decimal
|
||||
# int equivalent since that's what the library uses
|
||||
BITMASK_SCHEMA = vol.All(
|
||||
cv.string, vol.Lower, vol.Match(r"^(0x)?[0-9a-f]+$"), lambda value: int(value, 16)
|
||||
cv.string,
|
||||
vol.Lower,
|
||||
vol.Match(
|
||||
r"^(0x)?[0-9a-f]+$",
|
||||
msg="Must provide an integer (e.g. 255) or a bitmask in hex form (e.g. 0xff)",
|
||||
),
|
||||
lambda value: int(value, 16),
|
||||
)
|
||||
|
||||
|
||||
@ -75,6 +84,30 @@ class ZWaveServices:
|
||||
),
|
||||
)
|
||||
|
||||
self._hass.services.async_register(
|
||||
const.DOMAIN,
|
||||
const.SERVICE_BULK_SET_PARTIAL_CONFIG_PARAMETERS,
|
||||
self.async_bulk_set_partial_config_parameters,
|
||||
schema=vol.All(
|
||||
{
|
||||
vol.Optional(ATTR_DEVICE_ID): vol.All(cv.ensure_list, [cv.string]),
|
||||
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
||||
vol.Required(const.ATTR_CONFIG_PARAMETER): vol.Any(
|
||||
vol.Coerce(int), cv.string
|
||||
),
|
||||
vol.Required(const.ATTR_CONFIG_VALUE): vol.Any(
|
||||
vol.Coerce(int),
|
||||
{
|
||||
vol.Any(vol.Coerce(int), BITMASK_SCHEMA): vol.Any(
|
||||
vol.Coerce(int), cv.string
|
||||
)
|
||||
},
|
||||
),
|
||||
},
|
||||
cv.has_at_least_one_key(ATTR_DEVICE_ID, ATTR_ENTITY_ID),
|
||||
),
|
||||
)
|
||||
|
||||
self._hass.services.async_register(
|
||||
const.DOMAIN,
|
||||
const.SERVICE_REFRESH_VALUE,
|
||||
@ -122,6 +155,41 @@ class ZWaveServices:
|
||||
|
||||
_LOGGER.info(msg, zwave_value, node, new_value)
|
||||
|
||||
async def async_bulk_set_partial_config_parameters(
|
||||
self, service: ServiceCall
|
||||
) -> None:
|
||||
"""Bulk set multiple partial config values on a node."""
|
||||
nodes: set[ZwaveNode] = set()
|
||||
if ATTR_ENTITY_ID in service.data:
|
||||
nodes |= {
|
||||
async_get_node_from_entity_id(self._hass, entity_id)
|
||||
for entity_id in service.data[ATTR_ENTITY_ID]
|
||||
}
|
||||
if ATTR_DEVICE_ID in service.data:
|
||||
nodes |= {
|
||||
async_get_node_from_device_id(self._hass, device_id)
|
||||
for device_id in service.data[ATTR_DEVICE_ID]
|
||||
}
|
||||
property_ = service.data[const.ATTR_CONFIG_PARAMETER]
|
||||
new_value = service.data[const.ATTR_CONFIG_VALUE]
|
||||
|
||||
for node in nodes:
|
||||
cmd_status = await async_bulk_set_partial_config_parameters(
|
||||
node,
|
||||
property_,
|
||||
new_value,
|
||||
)
|
||||
|
||||
if cmd_status == CommandStatus.ACCEPTED:
|
||||
msg = "Bulk set partials for configuration parameter %s on Node %s"
|
||||
else:
|
||||
msg = (
|
||||
"Added command to queue to bulk set partials for configuration "
|
||||
"parameter %s on Node %s"
|
||||
)
|
||||
|
||||
_LOGGER.info(msg, property_, node)
|
||||
|
||||
async def async_poll_value(self, service: ServiceCall) -> None:
|
||||
"""Poll value on a node."""
|
||||
for entity_id in service.data[ATTR_ENTITY_ID]:
|
||||
|
@ -59,11 +59,37 @@ set_config_parameter:
|
||||
example: 5
|
||||
required: true
|
||||
selector:
|
||||
object:
|
||||
text:
|
||||
bitmask:
|
||||
name: Bitmask
|
||||
description: Target a specific bitmask (see the documentation for more information).
|
||||
advanced: true
|
||||
selector:
|
||||
text:
|
||||
|
||||
bulk_set_partial_config_parameters:
|
||||
name: Bulk set partial configuration parameters for a Z-Wave device (Advanced).
|
||||
description: Allow for bulk setting partial parameters. Useful when multiple partial parameters have to be set at the same time.
|
||||
target:
|
||||
entity:
|
||||
integration: zwave_js
|
||||
fields:
|
||||
parameter:
|
||||
name: Parameter
|
||||
description: The id of the configuration parameter you want to configure.
|
||||
example: 9
|
||||
required: true
|
||||
selector:
|
||||
text:
|
||||
value:
|
||||
name: Value
|
||||
description: The new value(s) to set for this configuration parameter. Can either be a raw integer value to represent the bulk change or a mapping where the key is the bitmask (either in hex or integer form) and the value is the new value you want to set for that partial parameter.
|
||||
example:
|
||||
"0x1": 1
|
||||
"0x10": 1
|
||||
"0x20": 1
|
||||
"0x40": 1
|
||||
required: true
|
||||
selector:
|
||||
object:
|
||||
|
||||
|
@ -8,11 +8,15 @@ from homeassistant.components.zwave_js.const import (
|
||||
ATTR_CONFIG_VALUE,
|
||||
ATTR_REFRESH_ALL_VALUES,
|
||||
DOMAIN,
|
||||
SERVICE_BULK_SET_PARTIAL_CONFIG_PARAMETERS,
|
||||
SERVICE_REFRESH_VALUE,
|
||||
SERVICE_SET_CONFIG_PARAMETER,
|
||||
)
|
||||
from homeassistant.const import ATTR_DEVICE_ID, ATTR_ENTITY_ID
|
||||
from homeassistant.helpers.device_registry import async_get as async_get_dev_reg
|
||||
from homeassistant.helpers.device_registry import (
|
||||
async_entries_for_config_entry,
|
||||
async_get as async_get_dev_reg,
|
||||
)
|
||||
from homeassistant.helpers.entity_registry import async_get as async_get_ent_reg
|
||||
|
||||
from .common import AIR_TEMPERATURE_SENSOR, CLIMATE_RADIO_THERMOSTAT_ENTITY
|
||||
@ -343,6 +347,125 @@ async def test_set_config_parameter(hass, client, multisensor_6, integration):
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
|
||||
async def test_bulk_set_config_parameters(hass, client, multisensor_6, integration):
|
||||
"""Test the bulk_set_partial_config_parameters service."""
|
||||
dev_reg = async_get_dev_reg(hass)
|
||||
device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0]
|
||||
# Test setting config parameter by property and property_key
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_BULK_SET_PARTIAL_CONFIG_PARAMETERS,
|
||||
{
|
||||
ATTR_DEVICE_ID: device.id,
|
||||
ATTR_CONFIG_PARAMETER: 102,
|
||||
ATTR_CONFIG_VALUE: 241,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert len(client.async_send_command_no_wait.call_args_list) == 1
|
||||
args = client.async_send_command_no_wait.call_args[0][0]
|
||||
assert args["command"] == "node.set_value"
|
||||
assert args["nodeId"] == 52
|
||||
assert args["valueId"] == {
|
||||
"commandClass": 112,
|
||||
"property": 102,
|
||||
}
|
||||
assert args["value"] == 241
|
||||
|
||||
client.async_send_command_no_wait.reset_mock()
|
||||
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_BULK_SET_PARTIAL_CONFIG_PARAMETERS,
|
||||
{
|
||||
ATTR_ENTITY_ID: AIR_TEMPERATURE_SENSOR,
|
||||
ATTR_CONFIG_PARAMETER: 102,
|
||||
ATTR_CONFIG_VALUE: {
|
||||
1: 1,
|
||||
16: 1,
|
||||
32: 1,
|
||||
64: 1,
|
||||
128: 1,
|
||||
},
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert len(client.async_send_command_no_wait.call_args_list) == 1
|
||||
args = client.async_send_command_no_wait.call_args[0][0]
|
||||
assert args["command"] == "node.set_value"
|
||||
assert args["nodeId"] == 52
|
||||
assert args["valueId"] == {
|
||||
"commandClass": 112,
|
||||
"property": 102,
|
||||
}
|
||||
assert args["value"] == 241
|
||||
|
||||
client.async_send_command_no_wait.reset_mock()
|
||||
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_BULK_SET_PARTIAL_CONFIG_PARAMETERS,
|
||||
{
|
||||
ATTR_ENTITY_ID: AIR_TEMPERATURE_SENSOR,
|
||||
ATTR_CONFIG_PARAMETER: 102,
|
||||
ATTR_CONFIG_VALUE: {
|
||||
"0x1": 1,
|
||||
"0x10": 1,
|
||||
"0x20": 1,
|
||||
"0x40": 1,
|
||||
"0x80": 1,
|
||||
},
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert len(client.async_send_command_no_wait.call_args_list) == 1
|
||||
args = client.async_send_command_no_wait.call_args[0][0]
|
||||
assert args["command"] == "node.set_value"
|
||||
assert args["nodeId"] == 52
|
||||
assert args["valueId"] == {
|
||||
"commandClass": 112,
|
||||
"property": 102,
|
||||
}
|
||||
assert args["value"] == 241
|
||||
|
||||
client.async_send_command_no_wait.reset_mock()
|
||||
|
||||
# Test that when a device is awake, we call async_send_command instead of
|
||||
# async_send_command_no_wait
|
||||
multisensor_6.handle_wake_up(None)
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_BULK_SET_PARTIAL_CONFIG_PARAMETERS,
|
||||
{
|
||||
ATTR_ENTITY_ID: AIR_TEMPERATURE_SENSOR,
|
||||
ATTR_CONFIG_PARAMETER: 102,
|
||||
ATTR_CONFIG_VALUE: {
|
||||
1: 1,
|
||||
16: 1,
|
||||
32: 1,
|
||||
64: 1,
|
||||
128: 1,
|
||||
},
|
||||
},
|
||||
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"] == 52
|
||||
assert args["valueId"] == {
|
||||
"commandClass": 112,
|
||||
"property": 102,
|
||||
}
|
||||
assert args["value"] == 241
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
|
||||
async def test_poll_value(
|
||||
hass, client, climate_radio_thermostat_ct100_plus_different_endpoints, integration
|
||||
):
|
||||
|
Loading…
x
Reference in New Issue
Block a user