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
parent
3cfcffc6f2
commit
c7affa75d4
|
@ -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
|
||||||
|
|
|
@ -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
|
@ -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()
|
||||||
|
|
Loading…
Reference in New Issue