Convert val to str when needed while calling zwave_js.set_value (#57216)

This commit is contained in:
Raman Gupta 2021-10-07 16:22:33 -04:00 committed by GitHub
parent 33b8130002
commit 3476b430db
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 130 additions and 14 deletions

View file

@ -408,7 +408,7 @@ class ZWaveServices:
async def async_set_value(self, service: ServiceCall) -> None:
"""Set a value on a node."""
# pylint: disable=no-self-use
nodes = service.data[const.ATTR_NODES]
nodes: set[ZwaveNode] = service.data[const.ATTR_NODES]
command_class = service.data[const.ATTR_COMMAND_CLASS]
property_ = service.data[const.ATTR_PROPERTY]
property_key = service.data.get(const.ATTR_PROPERTY_KEY)
@ -418,15 +418,27 @@ class ZWaveServices:
options = service.data.get(const.ATTR_OPTIONS)
for node in nodes:
value_id = get_value_id(
node,
command_class,
property_,
endpoint=endpoint,
property_key=property_key,
)
# If value has a string type but the new value is not a string, we need to
# convert it to one. We use new variable `new_value_` to convert the data
# so we can preserve the original `new_value` for every node.
if (
value_id in node.values
and node.values[value_id].metadata.type == "string"
and not isinstance(new_value, str)
):
new_value_ = str(new_value)
else:
new_value_ = new_value
success = await node.async_set_value(
get_value_id(
node,
command_class,
property_,
endpoint=endpoint,
property_key=property_key,
),
new_value,
value_id,
new_value_,
options=options,
wait_for_result=wait_for_result,
)
@ -452,11 +464,16 @@ class ZWaveServices:
await self.async_set_value(service)
return
command_class = service.data[const.ATTR_COMMAND_CLASS]
property_ = service.data[const.ATTR_PROPERTY]
property_key = service.data.get(const.ATTR_PROPERTY_KEY)
endpoint = service.data.get(const.ATTR_ENDPOINT)
value = {
"commandClass": service.data[const.ATTR_COMMAND_CLASS],
"property": service.data[const.ATTR_PROPERTY],
"propertyKey": service.data.get(const.ATTR_PROPERTY_KEY),
"endpoint": service.data.get(const.ATTR_ENDPOINT),
"commandClass": command_class,
"property": property_,
"propertyKey": property_key,
"endpoint": endpoint,
}
new_value = service.data[const.ATTR_VALUE]
@ -464,12 +481,30 @@ class ZWaveServices:
# schema validation and can use that to get the client, otherwise we can just
# get the client from the node.
client: ZwaveClient = None
first_node = next((node for node in nodes), None)
first_node: ZwaveNode = next((node for node in nodes), None)
if first_node:
client = first_node.client
else:
entry_id = self._hass.config_entries.async_entries(const.DOMAIN)[0].entry_id
client = self._hass.data[const.DOMAIN][entry_id][const.DATA_CLIENT]
first_node = next(
node
for node in client.driver.controller.nodes.values()
if get_value_id(node, command_class, property_, endpoint, property_key)
in node.values
)
# If value has a string type but the new value is not a string, we need to
# convert it to one
value_id = get_value_id(
first_node, command_class, property_, endpoint, property_key
)
if (
value_id in first_node.values
and first_node.values[value_id].metadata.type == "string"
and not isinstance(new_value, str)
):
new_value = str(new_value)
success = await async_multicast_set_value(
client=client,

View file

@ -43,6 +43,7 @@ from .common import (
CLIMATE_DANFOSS_LC13_ENTITY,
CLIMATE_EUROTRONICS_SPIRIT_Z_ENTITY,
CLIMATE_RADIO_THERMOSTAT_ENTITY,
SCHLAGE_BE469_LOCK_ENTITY,
)
from tests.common import MockConfigEntry
@ -1021,6 +1022,51 @@ async def test_set_value(hass, client, climate_danfoss_lc_13, integration):
)
async def test_set_value_string(
hass, client, climate_danfoss_lc_13, lock_schlage_be469, integration
):
"""Test set_value service converts number to string when needed."""
client.async_send_command.return_value = {"success": True}
# Test that number gets converted to a string when needed
await hass.services.async_call(
DOMAIN,
SERVICE_SET_VALUE,
{
ATTR_ENTITY_ID: SCHLAGE_BE469_LOCK_ENTITY,
ATTR_COMMAND_CLASS: 99,
ATTR_PROPERTY: "userCode",
ATTR_PROPERTY_KEY: 1,
ATTR_VALUE: 12345,
},
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"] == lock_schlage_be469.node_id
assert args["valueId"] == {
"commandClassName": "User Code",
"commandClass": 99,
"endpoint": 0,
"property": "userCode",
"propertyName": "userCode",
"propertyKey": 1,
"propertyKeyName": "1",
"metadata": {
"type": "string",
"readable": True,
"writeable": True,
"minLength": 4,
"maxLength": 10,
"label": "User Code (1)",
},
"value": "**********",
}
assert args["value"] == "12345"
async def test_set_value_options(hass, client, aeon_smart_switch_6, integration):
"""Test set_value service with options."""
await hass.services.async_call(
@ -1381,6 +1427,41 @@ async def test_multicast_set_value_options(
client.async_send_command.reset_mock()
async def test_multicast_set_value_string(
hass,
client,
lock_id_lock_as_id150,
lock_schlage_be469,
integration,
):
"""Test multicast_set_value service converts number to string when needed."""
client.async_send_command.return_value = {"success": True}
# Test that number gets converted to a string when needed
await hass.services.async_call(
DOMAIN,
SERVICE_MULTICAST_SET_VALUE,
{
ATTR_BROADCAST: True,
ATTR_COMMAND_CLASS: 99,
ATTR_PROPERTY: "userCode",
ATTR_PROPERTY_KEY: 1,
ATTR_VALUE: 12345,
},
blocking=True,
)
assert len(client.async_send_command.call_args_list) == 1
args = client.async_send_command.call_args[0][0]
assert args["command"] == "broadcast_node.set_value"
assert args["valueId"] == {
"commandClass": 99,
"property": "userCode",
"propertyKey": 1,
}
assert args["value"] == "12345"
async def test_ping(
hass,
client,