Limit available heat/cool modes for HomeKit thermostats (#28586)

* Limit available heat/cool modes for HomeKit thermostats.
The Home app only shows appropriate modes (heat/cool/auto) for the device. Depending on the climate integration, disabling the auto start might be needed.

* Include improved mapping for HVAC modes in tests
This commit is contained in:
David K 2019-12-06 14:07:45 +01:00 committed by Pascal Vizeli
parent 27530be46f
commit c5f4872aea
2 changed files with 115 additions and 29 deletions

View file

@ -24,6 +24,8 @@ from homeassistant.components.climate.const import (
HVAC_MODE_HEAT,
HVAC_MODE_HEAT_COOL,
HVAC_MODE_OFF,
HVAC_MODE_FAN_ONLY,
HVAC_MODE_AUTO,
)
from homeassistant.components.homekit.const import (
ATTR_VALUE,
@ -64,7 +66,20 @@ async def test_thermostat(hass, hk_driver, cls, events):
"""Test if accessory and HA are updated accordingly."""
entity_id = "climate.test"
hass.states.async_set(entity_id, HVAC_MODE_OFF)
hass.states.async_set(
entity_id,
HVAC_MODE_OFF,
{
ATTR_HVAC_MODES: [
HVAC_MODE_HEAT,
HVAC_MODE_HEAT_COOL,
HVAC_MODE_FAN_ONLY,
HVAC_MODE_COOL,
HVAC_MODE_OFF,
HVAC_MODE_AUTO,
],
},
)
await hass.async_block_till_done()
acc = cls.thermostat(hass, hk_driver, "Climate", entity_id, 2, None)
await hass.async_add_job(acc.run)
@ -120,7 +135,7 @@ async def test_thermostat(hass, hk_driver, cls, events):
hass.states.async_set(
entity_id,
HVAC_MODE_COOL,
HVAC_MODE_FAN_ONLY,
{
ATTR_TEMPERATURE: 20.0,
ATTR_CURRENT_TEMPERATURE: 25.0,
@ -164,9 +179,8 @@ async def test_thermostat(hass, hk_driver, cls, events):
hass.states.async_set(
entity_id,
HVAC_MODE_HEAT_COOL,
HVAC_MODE_AUTO,
{
ATTR_HVAC_MODES: [HVAC_MODE_HEAT, HVAC_MODE_COOL],
ATTR_TEMPERATURE: 22.0,
ATTR_CURRENT_TEMPERATURE: 18.0,
ATTR_HVAC_ACTION: CURRENT_HVAC_HEAT,
@ -183,7 +197,6 @@ async def test_thermostat(hass, hk_driver, cls, events):
entity_id,
HVAC_MODE_HEAT_COOL,
{
ATTR_HVAC_MODES: [HVAC_MODE_HEAT, HVAC_MODE_COOL],
ATTR_TEMPERATURE: 22.0,
ATTR_CURRENT_TEMPERATURE: 25.0,
ATTR_HVAC_ACTION: CURRENT_HVAC_COOL,
@ -198,9 +211,8 @@ async def test_thermostat(hass, hk_driver, cls, events):
hass.states.async_set(
entity_id,
HVAC_MODE_HEAT_COOL,
HVAC_MODE_AUTO,
{
ATTR_HVAC_MODES: [HVAC_MODE_HEAT, HVAC_MODE_COOL],
ATTR_TEMPERATURE: 22.0,
ATTR_CURRENT_TEMPERATURE: 22.0,
ATTR_HVAC_ACTION: CURRENT_HVAC_IDLE,
@ -226,14 +238,23 @@ async def test_thermostat(hass, hk_driver, cls, events):
assert len(events) == 1
assert events[-1].data[ATTR_VALUE] == "19.0°C"
await hass.async_add_job(acc.char_target_heat_cool.client_update_value, 1)
await hass.async_add_job(acc.char_target_heat_cool.client_update_value, 2)
await hass.async_block_till_done()
assert call_set_hvac_mode
assert call_set_hvac_mode[0].data[ATTR_ENTITY_ID] == entity_id
assert call_set_hvac_mode[0].data[ATTR_HVAC_MODE] == HVAC_MODE_HEAT
assert acc.char_target_heat_cool.value == 1
assert call_set_hvac_mode[0].data[ATTR_HVAC_MODE] == HVAC_MODE_COOL
assert acc.char_target_heat_cool.value == 2
assert len(events) == 2
assert events[-1].data[ATTR_VALUE] == HVAC_MODE_HEAT
assert events[-1].data[ATTR_VALUE] == HVAC_MODE_COOL
await hass.async_add_job(acc.char_target_heat_cool.client_update_value, 3)
await hass.async_block_till_done()
assert call_set_hvac_mode
assert call_set_hvac_mode[1].data[ATTR_ENTITY_ID] == entity_id
assert call_set_hvac_mode[1].data[ATTR_HVAC_MODE] == HVAC_MODE_AUTO
assert acc.char_target_heat_cool.value == 3
assert len(events) == 3
assert events[-1].data[ATTR_VALUE] == HVAC_MODE_AUTO
async def test_thermostat_auto(hass, hk_driver, cls, events):
@ -261,7 +282,6 @@ async def test_thermostat_auto(hass, hk_driver, cls, events):
entity_id,
HVAC_MODE_HEAT_COOL,
{
ATTR_HVAC_MODE: HVAC_MODE_HEAT_COOL,
ATTR_TARGET_TEMP_HIGH: 22.0,
ATTR_TARGET_TEMP_LOW: 20.0,
ATTR_CURRENT_TEMPERATURE: 18.0,
@ -278,9 +298,8 @@ async def test_thermostat_auto(hass, hk_driver, cls, events):
hass.states.async_set(
entity_id,
HVAC_MODE_HEAT_COOL,
HVAC_MODE_COOL,
{
ATTR_HVAC_MODE: HVAC_MODE_HEAT_COOL,
ATTR_TARGET_TEMP_HIGH: 23.0,
ATTR_TARGET_TEMP_LOW: 19.0,
ATTR_CURRENT_TEMPERATURE: 24.0,
@ -291,15 +310,14 @@ async def test_thermostat_auto(hass, hk_driver, cls, events):
assert acc.char_heating_thresh_temp.value == 19.0
assert acc.char_cooling_thresh_temp.value == 23.0
assert acc.char_current_heat_cool.value == 2
assert acc.char_target_heat_cool.value == 3
assert acc.char_target_heat_cool.value == 2
assert acc.char_current_temp.value == 24.0
assert acc.char_display_units.value == 0
hass.states.async_set(
entity_id,
HVAC_MODE_HEAT_COOL,
HVAC_MODE_AUTO,
{
ATTR_HVAC_MODE: HVAC_MODE_HEAT_COOL,
ATTR_TARGET_TEMP_HIGH: 23.0,
ATTR_TARGET_TEMP_LOW: 19.0,
ATTR_CURRENT_TEMPERATURE: 21.0,
@ -346,7 +364,6 @@ async def test_thermostat_power_state(hass, hk_driver, cls, events):
HVAC_MODE_HEAT,
{
ATTR_SUPPORTED_FEATURES: 4096,
ATTR_HVAC_MODE: HVAC_MODE_HEAT,
ATTR_TEMPERATURE: 23.0,
ATTR_CURRENT_TEMPERATURE: 18.0,
ATTR_HVAC_ACTION: CURRENT_HVAC_HEAT,
@ -364,7 +381,6 @@ async def test_thermostat_power_state(hass, hk_driver, cls, events):
entity_id,
HVAC_MODE_OFF,
{
ATTR_HVAC_MODE: HVAC_MODE_HEAT,
ATTR_TEMPERATURE: 23.0,
ATTR_CURRENT_TEMPERATURE: 18.0,
ATTR_HVAC_ACTION: CURRENT_HVAC_IDLE,
@ -378,7 +394,6 @@ async def test_thermostat_power_state(hass, hk_driver, cls, events):
entity_id,
HVAC_MODE_OFF,
{
ATTR_HVAC_MODE: HVAC_MODE_OFF,
ATTR_TEMPERATURE: 23.0,
ATTR_CURRENT_TEMPERATURE: 18.0,
ATTR_HVAC_ACTION: CURRENT_HVAC_IDLE,
@ -423,7 +438,6 @@ async def test_thermostat_fahrenheit(hass, hk_driver, cls, events):
entity_id,
HVAC_MODE_HEAT_COOL,
{
ATTR_HVAC_MODE: HVAC_MODE_HEAT_COOL,
ATTR_TARGET_TEMP_HIGH: 75.2,
ATTR_TARGET_TEMP_LOW: 68.1,
ATTR_TEMPERATURE: 71.6,
@ -503,6 +517,34 @@ async def test_thermostat_temperature_step_whole(hass, hk_driver, cls):
assert acc.char_target_temp.properties[PROP_MIN_STEP] == 1.0
async def test_thermostat_hvac_modes(hass, hk_driver, cls):
"""Test if unsupported HVAC modes are deactivated in HomeKit."""
entity_id = "climate.test"
hass.states.async_set(
entity_id, HVAC_MODE_OFF, {ATTR_HVAC_MODES: [HVAC_MODE_HEAT, HVAC_MODE_OFF]}
)
await hass.async_block_till_done()
acc = cls.thermostat(hass, hk_driver, "Climate", entity_id, 2, None)
await hass.async_add_job(acc.run)
await hass.async_block_till_done()
with pytest.raises(ValueError):
await hass.async_add_job(acc.char_target_heat_cool.set_value, 3)
await hass.async_block_till_done()
assert acc.char_target_heat_cool.value == 0
await hass.async_add_job(acc.char_target_heat_cool.set_value, 1)
await hass.async_block_till_done()
assert acc.char_target_heat_cool.value == 1
with pytest.raises(ValueError):
await hass.async_add_job(acc.char_target_heat_cool.set_value, 2)
await hass.async_block_till_done()
assert acc.char_target_heat_cool.value == 1
async def test_water_heater(hass, hk_driver, cls, events):
"""Test if accessory and HA are updated accordingly."""
entity_id = "water_heater.test"
@ -571,7 +613,8 @@ async def test_water_heater(hass, hk_driver, cls, events):
await hass.async_block_till_done()
assert acc.char_target_heat_cool.value == 1
await hass.async_add_job(acc.char_target_heat_cool.client_update_value, 3)
with pytest.raises(ValueError):
await hass.async_add_job(acc.char_target_heat_cool.set_value, 3)
await hass.async_block_till_done()
assert acc.char_target_heat_cool.value == 1