Remap homekit auto to home assistant heat_cool (#33701)
Home Assistant auto mode is described as "The device is set to a schedule, learned behavior, AI." HomeKit Accessory Protocol expects "heating or cooling to maintain temperature within the heating and cooling threshold of the target temperature" Since HomeKit is expecting to set temperatures in this mode, mapping homekit state 3 ("Auto") to Home Assistant HVAC_MODE_HEAT_COOL is more inline with how Home Assistant defines HVAC_MODE_HEAT_COOL as "The device supports heating/cooling to a range"pull/33721/head
parent
0793b5ac62
commit
171c1b20f7
|
@ -27,6 +27,7 @@ from homeassistant.components.climate.const import (
|
|||
DOMAIN as DOMAIN_CLIMATE,
|
||||
HVAC_MODE_AUTO,
|
||||
HVAC_MODE_COOL,
|
||||
HVAC_MODE_DRY,
|
||||
HVAC_MODE_FAN_ONLY,
|
||||
HVAC_MODE_HEAT,
|
||||
HVAC_MODE_HEAT_COOL,
|
||||
|
@ -150,15 +151,25 @@ class Thermostat(HomeAccessory):
|
|||
HVAC_MODE_OFF,
|
||||
)
|
||||
|
||||
# determine available modes for this entity, prefer AUTO over HEAT_COOL and COOL over FAN_ONLY
|
||||
# Determine available modes for this entity,
|
||||
# Prefer HEAT_COOL over AUTO and COOL over FAN_ONLY, DRY
|
||||
#
|
||||
# HEAT_COOL is preferred over auto because HomeKit Accessory Protocol describes
|
||||
# heating or cooling comes on to maintain a target temp which is closest to
|
||||
# the Home Assistant spec
|
||||
#
|
||||
# HVAC_MODE_HEAT_COOL: The device supports heating/cooling to a range
|
||||
self.hc_homekit_to_hass = {
|
||||
c: s
|
||||
for s, c in HC_HASS_TO_HOMEKIT.items()
|
||||
if (
|
||||
s in hc_modes
|
||||
and not (
|
||||
(s == HVAC_MODE_HEAT_COOL and HVAC_MODE_AUTO in hc_modes)
|
||||
or (s == HVAC_MODE_FAN_ONLY and HVAC_MODE_COOL in hc_modes)
|
||||
(s == HVAC_MODE_AUTO and HVAC_MODE_HEAT_COOL in hc_modes)
|
||||
or (
|
||||
s in (HVAC_MODE_DRY, HVAC_MODE_FAN_ONLY)
|
||||
and HVAC_MODE_COOL in hc_modes
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -294,10 +294,10 @@ async def test_thermostat(hass, hk_driver, cls, events):
|
|||
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 call_set_hvac_mode[1].data[ATTR_HVAC_MODE] == HVAC_MODE_HEAT_COOL
|
||||
assert acc.char_target_heat_cool.value == 3
|
||||
assert len(events) == 3
|
||||
assert events[-1].data[ATTR_VALUE] == HVAC_MODE_AUTO
|
||||
assert events[-1].data[ATTR_VALUE] == HVAC_MODE_HEAT_COOL
|
||||
|
||||
|
||||
async def test_thermostat_auto(hass, hk_driver, cls, events):
|
||||
|
@ -660,6 +660,9 @@ async def test_thermostat_hvac_modes(hass, hk_driver, cls):
|
|||
acc = cls.thermostat(hass, hk_driver, "Climate", entity_id, 2, None)
|
||||
await hass.async_add_job(acc.run)
|
||||
await hass.async_block_till_done()
|
||||
hap = acc.char_target_heat_cool.to_HAP()
|
||||
assert hap["valid-values"] == [0, 1]
|
||||
assert acc.char_target_heat_cool.value == 0
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
await hass.async_add_job(acc.char_target_heat_cool.set_value, 3)
|
||||
|
@ -676,6 +679,124 @@ async def test_thermostat_hvac_modes(hass, hk_driver, cls):
|
|||
assert acc.char_target_heat_cool.value == 1
|
||||
|
||||
|
||||
async def test_thermostat_hvac_modes_with_auto_heat_cool(hass, hk_driver, cls):
|
||||
"""Test we get heat cool over auto."""
|
||||
entity_id = "climate.test"
|
||||
|
||||
hass.states.async_set(
|
||||
entity_id,
|
||||
HVAC_MODE_HEAT_COOL,
|
||||
{
|
||||
ATTR_HVAC_MODES: [
|
||||
HVAC_MODE_HEAT_COOL,
|
||||
HVAC_MODE_AUTO,
|
||||
HVAC_MODE_HEAT,
|
||||
HVAC_MODE_OFF,
|
||||
]
|
||||
},
|
||||
)
|
||||
call_set_hvac_mode = async_mock_service(hass, DOMAIN_CLIMATE, "set_hvac_mode")
|
||||
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()
|
||||
hap = acc.char_target_heat_cool.to_HAP()
|
||||
assert hap["valid-values"] == [0, 1, 3]
|
||||
assert acc.char_target_heat_cool.value == 3
|
||||
|
||||
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 == 3
|
||||
|
||||
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
|
||||
|
||||
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[0].data[ATTR_ENTITY_ID] == entity_id
|
||||
assert call_set_hvac_mode[0].data[ATTR_HVAC_MODE] == HVAC_MODE_HEAT_COOL
|
||||
assert acc.char_target_heat_cool.value == 3
|
||||
|
||||
|
||||
async def test_thermostat_hvac_modes_with_auto_no_heat_cool(hass, hk_driver, cls):
|
||||
"""Test we get auto when there is no heat cool."""
|
||||
entity_id = "climate.test"
|
||||
|
||||
hass.states.async_set(
|
||||
entity_id,
|
||||
HVAC_MODE_HEAT_COOL,
|
||||
{ATTR_HVAC_MODES: [HVAC_MODE_AUTO, HVAC_MODE_HEAT, HVAC_MODE_OFF]},
|
||||
)
|
||||
call_set_hvac_mode = async_mock_service(hass, DOMAIN_CLIMATE, "set_hvac_mode")
|
||||
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()
|
||||
hap = acc.char_target_heat_cool.to_HAP()
|
||||
assert hap["valid-values"] == [0, 1, 3]
|
||||
assert acc.char_target_heat_cool.value == 3
|
||||
|
||||
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 == 3
|
||||
|
||||
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
|
||||
|
||||
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[0].data[ATTR_ENTITY_ID] == entity_id
|
||||
assert call_set_hvac_mode[0].data[ATTR_HVAC_MODE] == HVAC_MODE_AUTO
|
||||
assert acc.char_target_heat_cool.value == 3
|
||||
|
||||
|
||||
async def test_thermostat_hvac_modes_with_auto_only(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_AUTO, {ATTR_HVAC_MODES: [HVAC_MODE_AUTO, 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()
|
||||
hap = acc.char_target_heat_cool.to_HAP()
|
||||
assert hap["valid-values"] == [0, 3]
|
||||
assert acc.char_target_heat_cool.value == 3
|
||||
|
||||
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 == 3
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
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 == 3
|
||||
|
||||
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 == 3
|
||||
|
||||
|
||||
async def test_water_heater(hass, hk_driver, cls, events):
|
||||
"""Test if accessory and HA are updated accordingly."""
|
||||
entity_id = "water_heater.test"
|
||||
|
|
Loading…
Reference in New Issue