Add zwave_js.set_config_parameter service (#46673)
* create zwave_js.set_config_value service * update docstring * PR comments * make proposed changes * handle providing a label for the new value * fix docstring * use new library function * config param endpoint is always 0 * corresponding changes from upstream PR * bug fixes and add tests * create zwave_js.set_config_value service * update docstring * PR comments * make proposed changes * handle providing a label for the new value * fix docstring * use new library function * config param endpoint is always 0 * corresponding changes from upstream PR * bug fixes and add tests * use lambda to avoid extra function * add services description file * bring back the missing selector * move helper functions to helper file for reuse * allow target selector for automation editor * formatting * fix service schema * update docstrings * raise error in service if call to set value is unsuccessful * Update homeassistant/components/zwave_js/services.yaml Co-authored-by: Franck Nijhof <frenck@frenck.nl> * Update homeassistant/components/zwave_js/services.yaml Co-authored-by: Franck Nijhof <frenck@frenck.nl> * Update homeassistant/components/zwave_js/services.yaml Co-authored-by: Franck Nijhof <frenck@frenck.nl> * Update homeassistant/components/zwave_js/services.yaml Co-authored-by: Franck Nijhof <frenck@frenck.nl> * Update homeassistant/components/zwave_js/services.yaml Co-authored-by: Franck Nijhof <frenck@frenck.nl> * Update homeassistant/components/zwave_js/services.yaml Co-authored-by: Franck Nijhof <frenck@frenck.nl> * remove extra param to vol.Optional * switch to set over list for nodes * switch to set over list for nodes Co-authored-by: Marcel van der Veldt <m.vanderveldt@outlook.com> Co-authored-by: Franck Nijhof <frenck@frenck.nl>
This commit is contained in:
parent
c94968d811
commit
5a3bd30e01
8 changed files with 548 additions and 11 deletions
|
@ -43,7 +43,8 @@ from .const import (
|
|||
ZWAVE_JS_EVENT,
|
||||
)
|
||||
from .discovery import async_discover_values
|
||||
from .entity import get_device_id
|
||||
from .helpers import get_device_id
|
||||
from .services import ZWaveServices
|
||||
|
||||
LOGGER = logging.getLogger(__package__)
|
||||
CONNECT_TIMEOUT = 10
|
||||
|
@ -192,6 +193,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
DATA_UNSUBSCRIBE: unsubscribe_callbacks,
|
||||
}
|
||||
|
||||
services = ZWaveServices(hass)
|
||||
services.async_register()
|
||||
|
||||
# Set up websocket API
|
||||
async_register_api(hass)
|
||||
|
||||
|
|
|
@ -33,4 +33,11 @@ ATTR_PROPERTY_NAME = "property_name"
|
|||
ATTR_PROPERTY_KEY_NAME = "property_key_name"
|
||||
ATTR_PARAMETERS = "parameters"
|
||||
|
||||
# service constants
|
||||
SERVICE_SET_CONFIG_PARAMETER = "set_config_parameter"
|
||||
|
||||
ATTR_CONFIG_PARAMETER = "parameter"
|
||||
ATTR_CONFIG_PARAMETER_BITMASK = "bitmask"
|
||||
ATTR_CONFIG_VALUE = "value"
|
||||
|
||||
ADDON_SLUG = "core_zwave_js"
|
||||
|
|
|
@ -1,30 +1,23 @@
|
|||
"""Generic Z-Wave Entity Class."""
|
||||
|
||||
import logging
|
||||
from typing import List, Optional, Tuple, Union
|
||||
from typing import List, Optional, Union
|
||||
|
||||
from zwave_js_server.client import Client as ZwaveClient
|
||||
from zwave_js_server.model.node import Node as ZwaveNode
|
||||
from zwave_js_server.model.value import Value as ZwaveValue, get_value_id
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
from .const import DOMAIN
|
||||
from .discovery import ZwaveDiscoveryInfo
|
||||
from .helpers import get_device_id
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
EVENT_VALUE_UPDATED = "value updated"
|
||||
|
||||
|
||||
@callback
|
||||
def get_device_id(client: ZwaveClient, node: ZwaveNode) -> Tuple[str, str]:
|
||||
"""Get device registry identifier for Z-Wave node."""
|
||||
return (DOMAIN, f"{client.driver.controller.home_id}-{node.node_id}")
|
||||
|
||||
|
||||
class ZWaveBaseEntity(Entity):
|
||||
"""Generic Entity Class for a Z-Wave Device."""
|
||||
|
||||
|
|
100
homeassistant/components/zwave_js/helpers.py
Normal file
100
homeassistant/components/zwave_js/helpers.py
Normal file
|
@ -0,0 +1,100 @@
|
|||
"""Helper functions for Z-Wave JS integration."""
|
||||
from typing import List, Tuple, cast
|
||||
|
||||
from zwave_js_server.client import Client as ZwaveClient
|
||||
from zwave_js_server.model.node import Node as ZwaveNode
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.device_registry import async_get as async_get_dev_reg
|
||||
from homeassistant.helpers.entity_registry import async_get as async_get_ent_reg
|
||||
|
||||
from .const import DATA_CLIENT, DOMAIN
|
||||
|
||||
|
||||
@callback
|
||||
def get_device_id(client: ZwaveClient, node: ZwaveNode) -> Tuple[str, str]:
|
||||
"""Get device registry identifier for Z-Wave node."""
|
||||
return (DOMAIN, f"{client.driver.controller.home_id}-{node.node_id}")
|
||||
|
||||
|
||||
@callback
|
||||
def get_home_and_node_id_from_device_id(device_id: Tuple[str, str]) -> List[str]:
|
||||
"""
|
||||
Get home ID and node ID for Z-Wave device registry entry.
|
||||
|
||||
Returns [home_id, node_id]
|
||||
"""
|
||||
return device_id[1].split("-")
|
||||
|
||||
|
||||
@callback
|
||||
def async_get_node_from_device_id(hass: HomeAssistant, device_id: str) -> ZwaveNode:
|
||||
"""
|
||||
Get node from a device ID.
|
||||
|
||||
Raises ValueError if device is invalid or node can't be found.
|
||||
"""
|
||||
device_entry = async_get_dev_reg(hass).async_get(device_id)
|
||||
|
||||
if not device_entry:
|
||||
raise ValueError("Device ID is not valid")
|
||||
|
||||
# Use device config entry ID's to validate that this is a valid zwave_js device
|
||||
# and to get the client
|
||||
config_entry_ids = device_entry.config_entries
|
||||
config_entry_id = next(
|
||||
(
|
||||
config_entry_id
|
||||
for config_entry_id in config_entry_ids
|
||||
if cast(
|
||||
ConfigEntry,
|
||||
hass.config_entries.async_get_entry(config_entry_id),
|
||||
).domain
|
||||
== DOMAIN
|
||||
),
|
||||
None,
|
||||
)
|
||||
if config_entry_id is None or config_entry_id not in hass.data[DOMAIN]:
|
||||
raise ValueError("Device is not from an existing zwave_js config entry")
|
||||
|
||||
client = hass.data[DOMAIN][config_entry_id][DATA_CLIENT]
|
||||
|
||||
# Get node ID from device identifier, perform some validation, and then get the
|
||||
# node
|
||||
identifier = next(
|
||||
(
|
||||
get_home_and_node_id_from_device_id(identifier)
|
||||
for identifier in device_entry.identifiers
|
||||
if identifier[0] == DOMAIN
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
node_id = int(identifier[1]) if identifier is not None else None
|
||||
|
||||
if node_id is None or node_id not in client.driver.controller.nodes:
|
||||
raise ValueError("Device node can't be found")
|
||||
|
||||
return client.driver.controller.nodes[node_id]
|
||||
|
||||
|
||||
@callback
|
||||
def async_get_node_from_entity_id(hass: HomeAssistant, entity_id: str) -> ZwaveNode:
|
||||
"""
|
||||
Get node from an entity ID.
|
||||
|
||||
Raises ValueError if entity is invalid.
|
||||
"""
|
||||
entity_entry = async_get_ent_reg(hass).async_get(entity_id)
|
||||
|
||||
if not entity_entry:
|
||||
raise ValueError("Entity ID is not valid")
|
||||
|
||||
if entity_entry.platform != DOMAIN:
|
||||
raise ValueError("Entity is not from zwave_js integration")
|
||||
|
||||
# Assert for mypy, safe because we know that zwave_js entities are always
|
||||
# tied to a device
|
||||
assert entity_entry.device_id
|
||||
return async_get_node_from_device_id(hass, entity_entry.device_id)
|
110
homeassistant/components/zwave_js/services.py
Normal file
110
homeassistant/components/zwave_js/services.py
Normal file
|
@ -0,0 +1,110 @@
|
|||
"""Methods and classes related to executing Z-Wave commands and publishing these to hass."""
|
||||
|
||||
import logging
|
||||
from typing import Dict, Set, Union
|
||||
|
||||
import voluptuous as vol
|
||||
from zwave_js_server.model.node import Node as ZwaveNode
|
||||
from zwave_js_server.util.node import async_set_config_parameter
|
||||
|
||||
from homeassistant.const import ATTR_DEVICE_ID, ATTR_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant, ServiceCall, callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from . import const
|
||||
from .helpers import async_get_node_from_device_id, async_get_node_from_entity_id
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def parameter_name_does_not_need_bitmask(
|
||||
val: Dict[str, Union[int, str]]
|
||||
) -> Dict[str, Union[int, str]]:
|
||||
"""Validate that if a parameter name is provided, bitmask is not as well."""
|
||||
if isinstance(val[const.ATTR_CONFIG_PARAMETER], str) and (
|
||||
val.get(const.ATTR_CONFIG_PARAMETER_BITMASK)
|
||||
):
|
||||
raise vol.Invalid(
|
||||
"Don't include a bitmask when a parameter name is specified",
|
||||
path=[const.ATTR_CONFIG_PARAMETER, const.ATTR_CONFIG_PARAMETER_BITMASK],
|
||||
)
|
||||
return val
|
||||
|
||||
|
||||
# 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)
|
||||
)
|
||||
|
||||
|
||||
class ZWaveServices:
|
||||
"""Class that holds our services (Zwave Commands) that should be published to hass."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant):
|
||||
"""Initialize with hass object."""
|
||||
self._hass = hass
|
||||
|
||||
@callback
|
||||
def async_register(self) -> None:
|
||||
"""Register all our services."""
|
||||
self._hass.services.async_register(
|
||||
const.DOMAIN,
|
||||
const.SERVICE_SET_CONFIG_PARAMETER,
|
||||
self.async_set_config_parameter,
|
||||
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.Optional(const.ATTR_CONFIG_PARAMETER_BITMASK): vol.Any(
|
||||
vol.Coerce(int), BITMASK_SCHEMA
|
||||
),
|
||||
vol.Required(const.ATTR_CONFIG_VALUE): vol.Any(
|
||||
vol.Coerce(int), cv.string
|
||||
),
|
||||
},
|
||||
cv.has_at_least_one_key(ATTR_DEVICE_ID, ATTR_ENTITY_ID),
|
||||
parameter_name_does_not_need_bitmask,
|
||||
),
|
||||
)
|
||||
|
||||
async def async_set_config_parameter(self, service: ServiceCall) -> None:
|
||||
"""Set a config value 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_or_property_name = service.data[const.ATTR_CONFIG_PARAMETER]
|
||||
property_key = service.data.get(const.ATTR_CONFIG_PARAMETER_BITMASK)
|
||||
new_value = service.data[const.ATTR_CONFIG_VALUE]
|
||||
|
||||
for node in nodes:
|
||||
zwave_value = await async_set_config_parameter(
|
||||
node,
|
||||
new_value,
|
||||
property_or_property_name,
|
||||
property_key=property_key,
|
||||
)
|
||||
|
||||
if zwave_value:
|
||||
_LOGGER.info(
|
||||
"Set configuration parameter %s on Node %s with value %s",
|
||||
zwave_value,
|
||||
node,
|
||||
new_value,
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Unable to set configuration parameter on Node {node} with "
|
||||
f"value {new_value}"
|
||||
)
|
|
@ -38,3 +38,31 @@ set_lock_usercode:
|
|||
example: 1234
|
||||
selector:
|
||||
text:
|
||||
|
||||
set_config_parameter:
|
||||
name: Set a Z-Wave device configuration parameter
|
||||
description: Allow for changing configuration parameters of your Z-Wave devices.
|
||||
target:
|
||||
entity:
|
||||
integration: zwave_js
|
||||
fields:
|
||||
parameter:
|
||||
name: Parameter
|
||||
description: The (name or id of the) configuration parameter you want to configure.
|
||||
example: Minimum brightness level
|
||||
required: true
|
||||
selector:
|
||||
text:
|
||||
value:
|
||||
name: Value
|
||||
description: The new value to set for this configuration parameter.
|
||||
example: 5
|
||||
required: true
|
||||
selector:
|
||||
object:
|
||||
bitmask:
|
||||
name: Bitmask
|
||||
description: Target a specific bitmask (see the documentation for more information).
|
||||
advanced: true
|
||||
selector:
|
||||
object:
|
||||
|
|
|
@ -8,7 +8,7 @@ from zwave_js_server.model.node import Node
|
|||
|
||||
from homeassistant.components.hassio.handler import HassioAPIError
|
||||
from homeassistant.components.zwave_js.const import DOMAIN
|
||||
from homeassistant.components.zwave_js.entity import get_device_id
|
||||
from homeassistant.components.zwave_js.helpers import get_device_id
|
||||
from homeassistant.config_entries import (
|
||||
CONN_CLASS_LOCAL_PUSH,
|
||||
ENTRY_STATE_LOADED,
|
||||
|
|
295
tests/components/zwave_js/test_services.py
Normal file
295
tests/components/zwave_js/test_services.py
Normal file
|
@ -0,0 +1,295 @@
|
|||
"""Test the Z-Wave JS services."""
|
||||
import pytest
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.zwave_js.const import (
|
||||
ATTR_CONFIG_PARAMETER,
|
||||
ATTR_CONFIG_PARAMETER_BITMASK,
|
||||
ATTR_CONFIG_VALUE,
|
||||
DOMAIN,
|
||||
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.entity_registry import async_get as async_get_ent_reg
|
||||
|
||||
from .common import AIR_TEMPERATURE_SENSOR
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_set_config_parameter(hass, client, multisensor_6, integration):
|
||||
"""Test the set_config_parameter service."""
|
||||
dev_reg = async_get_dev_reg(hass)
|
||||
ent_reg = async_get_ent_reg(hass)
|
||||
entity_entry = ent_reg.async_get(AIR_TEMPERATURE_SENSOR)
|
||||
|
||||
# Test setting config parameter by property and property_key
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_CONFIG_PARAMETER,
|
||||
{
|
||||
ATTR_ENTITY_ID: AIR_TEMPERATURE_SENSOR,
|
||||
ATTR_CONFIG_PARAMETER: 102,
|
||||
ATTR_CONFIG_PARAMETER_BITMASK: 1,
|
||||
ATTR_CONFIG_VALUE: 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"] == {
|
||||
"commandClassName": "Configuration",
|
||||
"commandClass": 112,
|
||||
"endpoint": 0,
|
||||
"property": 102,
|
||||
"propertyName": "Group 2: Send battery reports",
|
||||
"propertyKey": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": True,
|
||||
"writeable": True,
|
||||
"valueSize": 4,
|
||||
"min": 0,
|
||||
"max": 1,
|
||||
"default": 1,
|
||||
"format": 0,
|
||||
"allowManualEntry": True,
|
||||
"label": "Group 2: Send battery reports",
|
||||
"description": "Include battery information in periodic reports to Group 2",
|
||||
"isFromConfig": True,
|
||||
},
|
||||
"value": 0,
|
||||
}
|
||||
assert args["value"] == 1
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
# Test setting parameter by property name
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_CONFIG_PARAMETER,
|
||||
{
|
||||
ATTR_ENTITY_ID: AIR_TEMPERATURE_SENSOR,
|
||||
ATTR_CONFIG_PARAMETER: "Group 2: Send battery reports",
|
||||
ATTR_CONFIG_VALUE: 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"] == {
|
||||
"commandClassName": "Configuration",
|
||||
"commandClass": 112,
|
||||
"endpoint": 0,
|
||||
"property": 102,
|
||||
"propertyName": "Group 2: Send battery reports",
|
||||
"propertyKey": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": True,
|
||||
"writeable": True,
|
||||
"valueSize": 4,
|
||||
"min": 0,
|
||||
"max": 1,
|
||||
"default": 1,
|
||||
"format": 0,
|
||||
"allowManualEntry": True,
|
||||
"label": "Group 2: Send battery reports",
|
||||
"description": "Include battery information in periodic reports to Group 2",
|
||||
"isFromConfig": True,
|
||||
},
|
||||
"value": 0,
|
||||
}
|
||||
assert args["value"] == 1
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
# Test setting parameter by property name and state label
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_CONFIG_PARAMETER,
|
||||
{
|
||||
ATTR_DEVICE_ID: entity_entry.device_id,
|
||||
ATTR_CONFIG_PARAMETER: "Temperature Threshold (Unit)",
|
||||
ATTR_CONFIG_VALUE: "Fahrenheit",
|
||||
},
|
||||
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"] == {
|
||||
"commandClassName": "Configuration",
|
||||
"commandClass": 112,
|
||||
"endpoint": 0,
|
||||
"property": 41,
|
||||
"propertyName": "Temperature Threshold (Unit)",
|
||||
"propertyKey": 15,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": True,
|
||||
"writeable": True,
|
||||
"valueSize": 3,
|
||||
"min": 1,
|
||||
"max": 2,
|
||||
"default": 1,
|
||||
"format": 0,
|
||||
"allowManualEntry": False,
|
||||
"states": {"1": "Celsius", "2": "Fahrenheit"},
|
||||
"label": "Temperature Threshold (Unit)",
|
||||
"isFromConfig": True,
|
||||
},
|
||||
"value": 0,
|
||||
}
|
||||
assert args["value"] == 2
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
# Test setting parameter by property and bitmask
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_CONFIG_PARAMETER,
|
||||
{
|
||||
ATTR_ENTITY_ID: AIR_TEMPERATURE_SENSOR,
|
||||
ATTR_CONFIG_PARAMETER: 102,
|
||||
ATTR_CONFIG_PARAMETER_BITMASK: "0x01",
|
||||
ATTR_CONFIG_VALUE: 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"] == {
|
||||
"commandClassName": "Configuration",
|
||||
"commandClass": 112,
|
||||
"endpoint": 0,
|
||||
"property": 102,
|
||||
"propertyName": "Group 2: Send battery reports",
|
||||
"propertyKey": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": True,
|
||||
"writeable": True,
|
||||
"valueSize": 4,
|
||||
"min": 0,
|
||||
"max": 1,
|
||||
"default": 1,
|
||||
"format": 0,
|
||||
"allowManualEntry": True,
|
||||
"label": "Group 2: Send battery reports",
|
||||
"description": "Include battery information in periodic reports to Group 2",
|
||||
"isFromConfig": True,
|
||||
},
|
||||
"value": 0,
|
||||
}
|
||||
assert args["value"] == 1
|
||||
|
||||
# Test that an invalid entity ID raises a ValueError
|
||||
with pytest.raises(ValueError):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_CONFIG_PARAMETER,
|
||||
{
|
||||
ATTR_ENTITY_ID: "sensor.fake_entity",
|
||||
ATTR_CONFIG_PARAMETER: "Temperature Threshold (Unit)",
|
||||
ATTR_CONFIG_VALUE: "Fahrenheit",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
# Test that an invalid device ID raises a ValueError
|
||||
with pytest.raises(ValueError):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_CONFIG_PARAMETER,
|
||||
{
|
||||
ATTR_DEVICE_ID: "fake_device_id",
|
||||
ATTR_CONFIG_PARAMETER: "Temperature Threshold (Unit)",
|
||||
ATTR_CONFIG_VALUE: "Fahrenheit",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
# Test that we can't include a bitmask value if parameter is a string
|
||||
with pytest.raises(vol.Invalid):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_CONFIG_PARAMETER,
|
||||
{
|
||||
ATTR_DEVICE_ID: entity_entry.device_id,
|
||||
ATTR_CONFIG_PARAMETER: "Temperature Threshold (Unit)",
|
||||
ATTR_CONFIG_PARAMETER_BITMASK: 1,
|
||||
ATTR_CONFIG_VALUE: "Fahrenheit",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
non_zwave_js_config_entry = MockConfigEntry(entry_id="fake_entry_id")
|
||||
non_zwave_js_config_entry.add_to_hass(hass)
|
||||
non_zwave_js_device = dev_reg.async_get_or_create(
|
||||
config_entry_id=non_zwave_js_config_entry.entry_id,
|
||||
identifiers={("test", "test")},
|
||||
)
|
||||
|
||||
# Test that a non Z-Wave JS device raises a ValueError
|
||||
with pytest.raises(ValueError):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_CONFIG_PARAMETER,
|
||||
{
|
||||
ATTR_DEVICE_ID: non_zwave_js_device.id,
|
||||
ATTR_CONFIG_PARAMETER: "Temperature Threshold (Unit)",
|
||||
ATTR_CONFIG_VALUE: "Fahrenheit",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
zwave_js_device_with_invalid_node_id = dev_reg.async_get_or_create(
|
||||
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):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_CONFIG_PARAMETER,
|
||||
{
|
||||
ATTR_DEVICE_ID: zwave_js_device_with_invalid_node_id.id,
|
||||
ATTR_CONFIG_PARAMETER: "Temperature Threshold (Unit)",
|
||||
ATTR_CONFIG_VALUE: "Fahrenheit",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
non_zwave_js_entity = ent_reg.async_get_or_create(
|
||||
"test",
|
||||
"sensor",
|
||||
"test_sensor",
|
||||
suggested_object_id="test_sensor",
|
||||
config_entry=non_zwave_js_config_entry,
|
||||
)
|
||||
|
||||
# Test that a non Z-Wave JS entity raises a ValueError
|
||||
with pytest.raises(ValueError):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_CONFIG_PARAMETER,
|
||||
{
|
||||
ATTR_ENTITY_ID: non_zwave_js_entity.entity_id,
|
||||
ATTR_CONFIG_PARAMETER: "Temperature Threshold (Unit)",
|
||||
ATTR_CONFIG_VALUE: "Fahrenheit",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
Loading…
Add table
Add a link
Reference in a new issue