diff --git a/test/util/test_node.py b/test/util/test_node.py index 9ac656018..416423819 100644 --- a/test/util/test_node.py +++ b/test/util/test_node.py @@ -174,6 +174,30 @@ async def test_bulk_set_partial_config_parameters(multisensor_6, uuid4, mock_com "messageId": uuid4, } + # Use property key names instead of bitmasks for dict key + cmd_status = await async_bulk_set_partial_config_parameters( + node, + 101, + { + "Group 1: Send humidity reports": 1, + "Group 1: Send temperature reports": 1, + "Group 1: Send ultraviolet reports": 1, + "Group 1: Send battery reports": 1, + }, + ) + assert cmd_status == CommandStatus.QUEUED + assert len(ack_commands) == 4 + assert ack_commands[3] == { + "command": "node.set_value", + "nodeId": node.node_id, + "valueId": { + "commandClass": CommandClass.CONFIGURATION.value, + "property": 101, + }, + "value": 241, + "messageId": uuid4, + } + # Use an invalid property with pytest.raises(NotFoundError): await async_bulk_set_partial_config_parameters(node, 999, 99) @@ -184,6 +208,12 @@ async def test_bulk_set_partial_config_parameters(multisensor_6, uuid4, mock_com node, 101, {128: 1, 64: 1, 32: 1, 16: 1, 2: 1} ) + # use an invalid property name + with pytest.raises(NotFoundError): + await async_bulk_set_partial_config_parameters( + node, 101, {"Invalid property name": 1} + ) + # Try to bulkset a property that isn't broken into partials with a dictionary with pytest.raises(ValueTypeError): await async_bulk_set_partial_config_parameters(node, 252, {1: 1}) diff --git a/zwave_js_server/util/node.py b/zwave_js_server/util/node.py index 96de00f87..83653c7da 100644 --- a/zwave_js_server/util/node.py +++ b/zwave_js_server/util/node.py @@ -81,7 +81,7 @@ async def async_set_config_parameter( async def async_bulk_set_partial_config_parameters( node: Node, property_: int, - new_value: Union[int, Dict[int, Union[int, str]]], + new_value: Union[int, Dict[Union[int, str], Union[int, str]]], ) -> CommandStatus: """Bulk set partial configuration values on this node.""" config_values = node.get_configuration_values() @@ -222,33 +222,57 @@ def _get_int_from_partials_dict( node: Node, partial_param_values: Dict[str, ConfigurationValue], property_: int, - new_value: Dict[int, Union[int, str]], + new_value: Dict[Union[int, str], Union[int, str]], ) -> int: """Take an input dict for a set of partial values and compute the raw int value.""" int_value = 0 + provided_partial_values = [] # For each property key provided, we bit shift the partial value using the # property_key - for property_key, partial_value in new_value.items(): - value_id = get_value_id( - node, CommandClass.CONFIGURATION, property_, property_key=property_key - ) - if value_id not in partial_param_values: - raise NotFoundError( - f"Bitmask {property_key} ({hex(property_key)}) not found for " - f"parameter {property_}" + for property_key_or_name, partial_value in new_value.items(): + # If the dict key is a property key, we can generate the value ID to find the + # partial value + if isinstance(property_key_or_name, int): + value_id = get_value_id( + node, + CommandClass.CONFIGURATION, + property_, + property_key=property_key_or_name, ) - partial_value = _validate_and_transform_new_value( - partial_param_values[value_id], partial_value - ) - int_value += partial_value << partial_param_bit_shift(property_key) + if value_id not in partial_param_values: + raise NotFoundError( + f"Bitmask {property_key_or_name} ({hex(property_key_or_name)}) " + f"not found for parameter {property_}" + ) + zwave_value = partial_param_values[value_id] + # If the dict key is a property name, we have to find the value from the list + # of partial param values + else: + try: + zwave_value = next( + value + for value in partial_param_values.values() + if value.property_name == property_key_or_name + ) + except StopIteration: + raise NotFoundError( + f"Partial parameter with label '{property_key_or_name}'" + f"not found for parameter {property_}" + ) from None + + provided_partial_values.append(zwave_value) + partial_value = _validate_and_transform_new_value(zwave_value, partial_value) + bit_shift = partial_param_bit_shift(cast(int, zwave_value.property_key)) + int_value += partial_value << bit_shift # To set partial parameters in bulk, we also have to include cached values for # property keys that haven't been specified - for property_value in partial_param_values.values(): - if property_value.property_key not in new_value: - int_value += cast(int, property_value.value) << partial_param_bit_shift( - cast(int, property_value.property_key) - ) + missing_values = set(partial_param_values.values()) - set(provided_partial_values) + int_value += sum( + cast(int, property_value.value) + << partial_param_bit_shift(cast(int, property_value.property_key)) + for property_value in missing_values + ) return int_value