Fix temperature setting for multi-setpoint z-wave device (#102395)

* Fix temperature setting for multi-setpoint z-wave device

* Add missing fixture file

* Apply suggestions from code review

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
pull/102408/head
kpine 2023-10-20 10:57:00 -07:00 committed by GitHub
parent 3cfcffc6f2
commit c7affa75d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 4720 additions and 3 deletions

View File

@ -259,9 +259,11 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity):
def _current_mode_setpoint_enums(self) -> list[ThermostatSetpointType]: def _current_mode_setpoint_enums(self) -> list[ThermostatSetpointType]:
"""Return the list of enums that are relevant to the current thermostat mode.""" """Return the list of enums that are relevant to the current thermostat mode."""
if self._current_mode is None or self._current_mode.value is None: if self._current_mode is None or self._current_mode.value is None:
# Thermostat(valve) with no support for setting a mode # Thermostat with no support for setting a mode is just a setpoint
# is considered heating-only if self.info.primary_value.property_key is None:
return [ThermostatSetpointType.HEATING] return []
return [ThermostatSetpointType(int(self.info.primary_value.property_key))]
return THERMOSTAT_MODE_SETPOINT_MAP.get(int(self._current_mode.value), []) return THERMOSTAT_MODE_SETPOINT_MAP.get(int(self._current_mode.value), [])
@property @property

View File

@ -662,6 +662,12 @@ def logic_group_zdb5100_state_fixture():
return json.loads(load_fixture("zwave_js/logic_group_zdb5100_state.json")) return json.loads(load_fixture("zwave_js/logic_group_zdb5100_state.json"))
@pytest.fixture(name="climate_intermatic_pe653_state", scope="session")
def climate_intermatic_pe653_state_fixture():
"""Load Intermatic PE653 Pool Control node state fixture data."""
return json.loads(load_fixture("zwave_js/climate_intermatic_pe653_state.json"))
# model fixtures # model fixtures
@ -1290,3 +1296,11 @@ def logic_group_zdb5100_fixture(client, logic_group_zdb5100_state):
node = Node(client, copy.deepcopy(logic_group_zdb5100_state)) node = Node(client, copy.deepcopy(logic_group_zdb5100_state))
client.driver.controller.nodes[node.node_id] = node client.driver.controller.nodes[node.node_id] = node
return node return node
@pytest.fixture(name="climate_intermatic_pe653")
def climate_intermatic_pe653_fixture(client, climate_intermatic_pe653_state):
"""Mock an Intermatic PE653 node."""
node = Node(client, copy.deepcopy(climate_intermatic_pe653_state))
client.driver.controller.nodes[node.node_id] = node
return node

File diff suppressed because it is too large Load Diff

View File

@ -792,3 +792,196 @@ async def test_thermostat_raise_repair_issue_and_warning_when_setting_fan_preset
"Dry and Fan preset modes are deprecated and will be removed in Home Assistant 2024.2. Please use the corresponding Dry and Fan HVAC modes instead" "Dry and Fan preset modes are deprecated and will be removed in Home Assistant 2024.2. Please use the corresponding Dry and Fan HVAC modes instead"
in caplog.text in caplog.text
) )
async def test_multi_setpoint_thermostat(
hass: HomeAssistant, client, climate_intermatic_pe653, integration
) -> None:
"""Test a thermostat with multiple setpoints."""
node = climate_intermatic_pe653
heating_entity_id = "climate.pool_control_2"
heating = hass.states.get(heating_entity_id)
assert heating
assert heating.state == HVACMode.HEAT
assert heating.attributes[ATTR_TEMPERATURE] == 3.9
assert heating.attributes[ATTR_HVAC_MODES] == [HVACMode.HEAT]
assert (
heating.attributes[ATTR_SUPPORTED_FEATURES]
== ClimateEntityFeature.TARGET_TEMPERATURE
)
furnace_entity_id = "climate.pool_control"
furnace = hass.states.get(furnace_entity_id)
assert furnace
assert furnace.state == HVACMode.HEAT
assert furnace.attributes[ATTR_TEMPERATURE] == 15.6
assert furnace.attributes[ATTR_HVAC_MODES] == [HVACMode.HEAT]
assert (
furnace.attributes[ATTR_SUPPORTED_FEATURES]
== ClimateEntityFeature.TARGET_TEMPERATURE
)
client.async_send_command_no_wait.reset_mock()
# Test setting temperature of heating setpoint
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{
ATTR_ENTITY_ID: heating_entity_id,
ATTR_TEMPERATURE: 20.0,
},
blocking=True,
)
# Test setting temperature of furnace setpoint
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{
ATTR_ENTITY_ID: furnace_entity_id,
ATTR_TEMPERATURE: 2.0,
},
blocking=True,
)
# Test setting illegal mode raises an error
with pytest.raises(ValueError):
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
{
ATTR_ENTITY_ID: heating_entity_id,
ATTR_HVAC_MODE: HVACMode.COOL,
},
blocking=True,
)
with pytest.raises(ValueError):
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
{
ATTR_ENTITY_ID: furnace_entity_id,
ATTR_HVAC_MODE: HVACMode.COOL,
},
blocking=True,
)
# this is a no-op since there's no mode
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
{
ATTR_ENTITY_ID: heating_entity_id,
ATTR_HVAC_MODE: HVACMode.HEAT,
},
blocking=True,
)
# this is a no-op since there's no mode
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
{
ATTR_ENTITY_ID: furnace_entity_id,
ATTR_HVAC_MODE: HVACMode.HEAT,
},
blocking=True,
)
assert len(client.async_send_command.call_args_list) == 2
args = client.async_send_command.call_args_list[0][0][0]
assert args["command"] == "node.set_value"
assert args["nodeId"] == 19
assert args["valueId"] == {
"endpoint": 1,
"commandClass": 67,
"property": "setpoint",
"propertyKey": 1,
}
assert args["value"] == 68.0
args = client.async_send_command.call_args_list[1][0][0]
assert args["command"] == "node.set_value"
assert args["nodeId"] == 19
assert args["valueId"] == {
"endpoint": 0,
"commandClass": 67,
"property": "setpoint",
"propertyKey": 7,
}
assert args["value"] == 35.6
client.async_send_command.reset_mock()
# Test heating setpoint value update from value updated event
event = Event(
type="value updated",
data={
"source": "node",
"event": "value updated",
"nodeId": 19,
"args": {
"commandClassName": "Thermostat Setpoint",
"commandClass": 67,
"endpoint": 1,
"property": "setpoint",
"propertyKey": 1,
"propertyKeyName": "Heating",
"propertyName": "setpoint",
"newValue": 23,
"prevValue": 21.5,
},
},
)
node.receive_event(event)
state = hass.states.get(heating_entity_id)
assert state
assert state.state == HVACMode.HEAT
assert state.attributes[ATTR_TEMPERATURE] == -5
# furnace not changed
state = hass.states.get(furnace_entity_id)
assert state
assert state.state == HVACMode.HEAT
assert state.attributes[ATTR_TEMPERATURE] == 15.6
client.async_send_command.reset_mock()
# Test furnace setpoint value update from value updated event
event = Event(
type="value updated",
data={
"source": "node",
"event": "value updated",
"nodeId": 19,
"args": {
"commandClassName": "Thermostat Setpoint",
"commandClass": 67,
"endpoint": 0,
"property": "setpoint",
"propertyKey": 7,
"propertyKeyName": "Furnace",
"propertyName": "setpoint",
"newValue": 68,
"prevValue": 21.5,
},
},
)
node.receive_event(event)
# heating not changed
state = hass.states.get(heating_entity_id)
assert state
assert state.state == HVACMode.HEAT
assert state.attributes[ATTR_TEMPERATURE] == -5
state = hass.states.get(furnace_entity_id)
assert state
assert state.state == HVACMode.HEAT
assert state.attributes[ATTR_TEMPERATURE] == 20
client.async_send_command.reset_mock()