Add zwave_js.multicast_set_value service (#51115)
* Add zwave_js.multicast_set_value service * comment * Add test for multiple config entries validation * additional validation test * brevity * wrap schema in vol.Schema * Update homeassistant/components/zwave_js/services.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * do node transform and multicast validation in schema validation * move poll value entity validation into schema validation, pass helper functions dev and ent reg instead of retrieving it every time * make validators nested functions since they don't neeed to be externally accessible * Update homeassistant/components/zwave_js/services.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Remove errant ALLOW_EXTRA Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
93ada0a675
commit
ca8d09e5e1
6 changed files with 432 additions and 69 deletions
|
@ -1,9 +1,12 @@
|
|||
"""Test the Z-Wave JS services."""
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
import voluptuous as vol
|
||||
from zwave_js_server.exceptions import SetValueFailed
|
||||
|
||||
from homeassistant.components.zwave_js.const import (
|
||||
ATTR_BROADCAST,
|
||||
ATTR_COMMAND_CLASS,
|
||||
ATTR_CONFIG_PARAMETER,
|
||||
ATTR_CONFIG_PARAMETER_BITMASK,
|
||||
|
@ -14,6 +17,7 @@ from homeassistant.components.zwave_js.const import (
|
|||
ATTR_WAIT_FOR_RESULT,
|
||||
DOMAIN,
|
||||
SERVICE_BULK_SET_PARTIAL_CONFIG_PARAMETERS,
|
||||
SERVICE_MULTICAST_SET_VALUE,
|
||||
SERVICE_REFRESH_VALUE,
|
||||
SERVICE_SET_CONFIG_PARAMETER,
|
||||
SERVICE_SET_VALUE,
|
||||
|
@ -212,8 +216,8 @@ async def test_set_config_parameter(hass, client, multisensor_6, integration):
|
|||
}
|
||||
assert args["value"] == 1
|
||||
|
||||
# Test that an invalid entity ID raises a ValueError
|
||||
with pytest.raises(ValueError):
|
||||
# Test that an invalid entity ID raises a MultipleInvalid
|
||||
with pytest.raises(vol.MultipleInvalid):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_CONFIG_PARAMETER,
|
||||
|
@ -225,8 +229,8 @@ async def test_set_config_parameter(hass, client, multisensor_6, integration):
|
|||
blocking=True,
|
||||
)
|
||||
|
||||
# Test that an invalid device ID raises a ValueError
|
||||
with pytest.raises(ValueError):
|
||||
# Test that an invalid device ID raises a MultipleInvalid
|
||||
with pytest.raises(vol.MultipleInvalid):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_CONFIG_PARAMETER,
|
||||
|
@ -259,8 +263,8 @@ async def test_set_config_parameter(hass, client, multisensor_6, integration):
|
|||
identifiers={("test", "test")},
|
||||
)
|
||||
|
||||
# Test that a non Z-Wave JS device raises a ValueError
|
||||
with pytest.raises(ValueError):
|
||||
# Test that a non Z-Wave JS device raises a MultipleInvalid
|
||||
with pytest.raises(vol.MultipleInvalid):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_CONFIG_PARAMETER,
|
||||
|
@ -276,8 +280,8 @@ async def test_set_config_parameter(hass, client, multisensor_6, integration):
|
|||
config_entry_id=integration.entry_id, identifiers={(DOMAIN, "500-500")}
|
||||
)
|
||||
|
||||
# Test that a Z-Wave JS device with an invalid node ID raises a ValueError
|
||||
with pytest.raises(ValueError):
|
||||
# Test that a Z-Wave JS device with an invalid node ID raises a MultipleInvalid
|
||||
with pytest.raises(vol.MultipleInvalid):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_CONFIG_PARAMETER,
|
||||
|
@ -297,8 +301,8 @@ async def test_set_config_parameter(hass, client, multisensor_6, integration):
|
|||
config_entry=non_zwave_js_config_entry,
|
||||
)
|
||||
|
||||
# Test that a non Z-Wave JS entity raises a ValueError
|
||||
with pytest.raises(ValueError):
|
||||
# Test that a non Z-Wave JS entity raises a MultipleInvalid
|
||||
with pytest.raises(vol.MultipleInvalid):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_CONFIG_PARAMETER,
|
||||
|
@ -530,8 +534,8 @@ async def test_poll_value(
|
|||
)
|
||||
assert len(client.async_send_command.call_args_list) == 8
|
||||
|
||||
# Test polling against an invalid entity raises ValueError
|
||||
with pytest.raises(ValueError):
|
||||
# Test polling against an invalid entity raises MultipleInvalid
|
||||
with pytest.raises(vol.MultipleInvalid):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_REFRESH_VALUE,
|
||||
|
@ -634,3 +638,155 @@ async def test_set_value(hass, client, climate_danfoss_lc_13, integration):
|
|||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
||||
async def test_multicast_set_value(
|
||||
hass,
|
||||
client,
|
||||
climate_danfoss_lc_13,
|
||||
climate_radio_thermostat_ct100_plus_different_endpoints,
|
||||
integration,
|
||||
):
|
||||
"""Test multicast_set_value service."""
|
||||
# Test successful multicast call
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_MULTICAST_SET_VALUE,
|
||||
{
|
||||
ATTR_ENTITY_ID: [
|
||||
CLIMATE_DANFOSS_LC13_ENTITY,
|
||||
CLIMATE_RADIO_THERMOSTAT_ENTITY,
|
||||
],
|
||||
ATTR_COMMAND_CLASS: 117,
|
||||
ATTR_PROPERTY: "local",
|
||||
ATTR_VALUE: 2,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 1
|
||||
args = client.async_send_command.call_args[0][0]
|
||||
assert args["command"] == "multicast_group.set_value"
|
||||
assert args["nodeIDs"] == [
|
||||
climate_radio_thermostat_ct100_plus_different_endpoints.node_id,
|
||||
climate_danfoss_lc_13.node_id,
|
||||
]
|
||||
assert args["valueId"] == {
|
||||
"commandClass": 117,
|
||||
"property": "local",
|
||||
}
|
||||
assert args["value"] == 2
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
# Test successful broadcast call
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_MULTICAST_SET_VALUE,
|
||||
{
|
||||
ATTR_BROADCAST: True,
|
||||
ATTR_COMMAND_CLASS: 117,
|
||||
ATTR_PROPERTY: "local",
|
||||
ATTR_VALUE: 2,
|
||||
},
|
||||
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": 117,
|
||||
"property": "local",
|
||||
}
|
||||
assert args["value"] == 2
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
# Test sending one node without broadcast fails
|
||||
with pytest.raises(vol.Invalid):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_MULTICAST_SET_VALUE,
|
||||
{
|
||||
ATTR_ENTITY_ID: CLIMATE_DANFOSS_LC13_ENTITY,
|
||||
ATTR_COMMAND_CLASS: 117,
|
||||
ATTR_PROPERTY: "local",
|
||||
ATTR_VALUE: 2,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
# Test no device, entity, or broadcast flag raises error
|
||||
with pytest.raises(vol.Invalid):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_MULTICAST_SET_VALUE,
|
||||
{
|
||||
ATTR_COMMAND_CLASS: 117,
|
||||
ATTR_PROPERTY: "local",
|
||||
ATTR_VALUE: 2,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
# Test that when a command fails we raise an exception
|
||||
client.async_send_command.return_value = {"success": False}
|
||||
|
||||
with pytest.raises(SetValueFailed):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_MULTICAST_SET_VALUE,
|
||||
{
|
||||
ATTR_ENTITY_ID: [
|
||||
CLIMATE_DANFOSS_LC13_ENTITY,
|
||||
CLIMATE_RADIO_THERMOSTAT_ENTITY,
|
||||
],
|
||||
ATTR_COMMAND_CLASS: 117,
|
||||
ATTR_PROPERTY: "local",
|
||||
ATTR_VALUE: 2,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
# Create a fake node with a different home ID from a real node and patch it into
|
||||
# return of helper function to check the validation for two nodes having different
|
||||
# home IDs
|
||||
diff_network_node = MagicMock()
|
||||
diff_network_node.client.driver.controller.home_id.return_value = "diff_home_id"
|
||||
|
||||
with pytest.raises(vol.MultipleInvalid), patch(
|
||||
"homeassistant.components.zwave_js.services.async_get_node_from_device_id",
|
||||
return_value=diff_network_node,
|
||||
):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_MULTICAST_SET_VALUE,
|
||||
{
|
||||
ATTR_ENTITY_ID: [
|
||||
CLIMATE_DANFOSS_LC13_ENTITY,
|
||||
],
|
||||
ATTR_DEVICE_ID: "fake_device_id",
|
||||
ATTR_COMMAND_CLASS: 117,
|
||||
ATTR_PROPERTY: "local",
|
||||
ATTR_VALUE: 2,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
# Test that when there are multiple zwave_js config entries, service will fail
|
||||
# without devices or entities
|
||||
new_entry = MockConfigEntry(domain=DOMAIN)
|
||||
new_entry.add_to_hass(hass)
|
||||
with pytest.raises(vol.Invalid):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_MULTICAST_SET_VALUE,
|
||||
{
|
||||
ATTR_BROADCAST: True,
|
||||
ATTR_COMMAND_CLASS: 117,
|
||||
ATTR_PROPERTY: "local",
|
||||
ATTR_VALUE: 2,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue