mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 20:57:21 +00:00
Handle unknown state in HomeKit (#107039)
This commit is contained in:
parent
40e1bab0ac
commit
ea4143154b
@ -36,6 +36,7 @@ from homeassistant.const import (
|
||||
PERCENTAGE,
|
||||
STATE_ON,
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
UnitOfTemperature,
|
||||
__version__,
|
||||
)
|
||||
@ -506,7 +507,7 @@ class HomeAccessory(Accessory): # type: ignore[misc]
|
||||
_LOGGER.debug("New_state: %s", new_state)
|
||||
# HomeKit handles unavailable state via the available property
|
||||
# so we should not propagate it here
|
||||
if new_state is None or new_state.state == STATE_UNAVAILABLE:
|
||||
if new_state is None or new_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
|
||||
return
|
||||
battery_state = None
|
||||
battery_charging_state = None
|
||||
|
@ -54,6 +54,8 @@ from homeassistant.const import (
|
||||
ATTR_SUPPORTED_FEATURES,
|
||||
ATTR_TEMPERATURE,
|
||||
PERCENTAGE,
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
UnitOfTemperature,
|
||||
)
|
||||
from homeassistant.core import State, callback
|
||||
@ -167,7 +169,9 @@ HEAT_COOL_DEADBAND = 5
|
||||
|
||||
def _hk_hvac_mode_from_state(state: State) -> int | None:
|
||||
"""Return the equivalent HomeKit HVAC mode for a given state."""
|
||||
if not (hvac_mode := try_parse_enum(HVACMode, state.state)):
|
||||
if (current_state := state.state) in (STATE_UNKNOWN, STATE_UNAVAILABLE):
|
||||
return None
|
||||
if not (hvac_mode := try_parse_enum(HVACMode, current_state)):
|
||||
_LOGGER.error(
|
||||
"%s: Received invalid HVAC mode: %s", state.entity_id, state.state
|
||||
)
|
||||
|
@ -368,22 +368,22 @@ async def test_windowcovering_cover_set_tilt(
|
||||
assert acc.char_current_tilt.value == 0
|
||||
assert acc.char_target_tilt.value == 0
|
||||
|
||||
hass.states.async_set(entity_id, STATE_UNKNOWN, {ATTR_CURRENT_TILT_POSITION: None})
|
||||
hass.states.async_set(entity_id, STATE_CLOSING, {ATTR_CURRENT_TILT_POSITION: None})
|
||||
await hass.async_block_till_done()
|
||||
assert acc.char_current_tilt.value == 0
|
||||
assert acc.char_target_tilt.value == 0
|
||||
|
||||
hass.states.async_set(entity_id, STATE_UNKNOWN, {ATTR_CURRENT_TILT_POSITION: 100})
|
||||
hass.states.async_set(entity_id, STATE_CLOSING, {ATTR_CURRENT_TILT_POSITION: 100})
|
||||
await hass.async_block_till_done()
|
||||
assert acc.char_current_tilt.value == 90
|
||||
assert acc.char_target_tilt.value == 90
|
||||
|
||||
hass.states.async_set(entity_id, STATE_UNKNOWN, {ATTR_CURRENT_TILT_POSITION: 50})
|
||||
hass.states.async_set(entity_id, STATE_CLOSING, {ATTR_CURRENT_TILT_POSITION: 50})
|
||||
await hass.async_block_till_done()
|
||||
assert acc.char_current_tilt.value == 0
|
||||
assert acc.char_target_tilt.value == 0
|
||||
|
||||
hass.states.async_set(entity_id, STATE_UNKNOWN, {ATTR_CURRENT_TILT_POSITION: 0})
|
||||
hass.states.async_set(entity_id, STATE_CLOSING, {ATTR_CURRENT_TILT_POSITION: 0})
|
||||
await hass.async_block_till_done()
|
||||
assert acc.char_current_tilt.value == -90
|
||||
assert acc.char_target_tilt.value == -90
|
||||
|
@ -66,14 +66,14 @@ async def test_lock_unlock(hass: HomeAssistant, hk_driver, events) -> None:
|
||||
|
||||
hass.states.async_set(entity_id, STATE_UNKNOWN)
|
||||
await hass.async_block_till_done()
|
||||
assert acc.char_current_state.value == 3
|
||||
assert acc.char_current_state.value == 2
|
||||
assert acc.char_target_state.value == 0
|
||||
|
||||
# Unavailable should keep last state
|
||||
# but set the accessory to not available
|
||||
hass.states.async_set(entity_id, STATE_UNAVAILABLE)
|
||||
await hass.async_block_till_done()
|
||||
assert acc.char_current_state.value == 3
|
||||
assert acc.char_current_state.value == 2
|
||||
assert acc.char_target_state.value == 0
|
||||
assert acc.available is False
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
"""Test different accessory types: Thermostats."""
|
||||
from unittest.mock import patch
|
||||
|
||||
from pyhap.characteristic import Characteristic
|
||||
from pyhap.const import HAP_REPR_AID, HAP_REPR_CHARS, HAP_REPR_IID, HAP_REPR_VALUE
|
||||
import pytest
|
||||
|
||||
@ -68,6 +69,8 @@ from homeassistant.const import (
|
||||
ATTR_TEMPERATURE,
|
||||
CONF_TEMPERATURE_UNIT,
|
||||
EVENT_HOMEASSISTANT_START,
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
UnitOfTemperature,
|
||||
)
|
||||
from homeassistant.core import CoreState, HomeAssistant
|
||||
@ -2446,3 +2449,144 @@ async def test_thermostat_with_supported_features_target_temp_but_fan_mode_set(
|
||||
|
||||
assert acc.ordered_fan_speeds == []
|
||||
assert not acc.fan_chars
|
||||
|
||||
|
||||
async def test_thermostat_handles_unknown_state(
|
||||
hass: HomeAssistant, hk_driver, events
|
||||
) -> None:
|
||||
"""Test a thermostat can handle unknown state."""
|
||||
entity_id = "climate.test"
|
||||
attrs = {
|
||||
ATTR_SUPPORTED_FEATURES: ClimateEntityFeature.TARGET_TEMPERATURE,
|
||||
ATTR_MIN_TEMP: 44.6,
|
||||
ATTR_MAX_TEMP: 95,
|
||||
ATTR_PRESET_MODES: ["home", "away"],
|
||||
ATTR_TEMPERATURE: 67,
|
||||
ATTR_TARGET_TEMP_HIGH: None,
|
||||
ATTR_TARGET_TEMP_LOW: None,
|
||||
ATTR_FAN_MODE: FAN_AUTO,
|
||||
ATTR_FAN_MODES: None,
|
||||
ATTR_HVAC_ACTION: HVACAction.IDLE,
|
||||
ATTR_PRESET_MODE: "home",
|
||||
ATTR_FRIENDLY_NAME: "Rec Room",
|
||||
ATTR_HVAC_MODES: [
|
||||
HVACMode.OFF,
|
||||
HVACMode.HEAT,
|
||||
],
|
||||
}
|
||||
|
||||
call_set_hvac_mode = async_mock_service(hass, DOMAIN_CLIMATE, "set_hvac_mode")
|
||||
hass.states.async_set(
|
||||
entity_id,
|
||||
HVACMode.OFF,
|
||||
attrs,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
acc = Thermostat(hass, hk_driver, "Climate", entity_id, 1, None)
|
||||
hk_driver.add_accessory(acc)
|
||||
|
||||
await acc.run()
|
||||
await hass.async_block_till_done()
|
||||
heat_cool_char: Characteristic = acc.char_target_heat_cool
|
||||
|
||||
assert heat_cool_char.value == HC_HEAT_COOL_OFF
|
||||
assert acc.available is True
|
||||
hass.states.async_set(
|
||||
entity_id,
|
||||
STATE_UNKNOWN,
|
||||
attrs,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert heat_cool_char.value == HC_HEAT_COOL_OFF
|
||||
assert acc.available is True
|
||||
|
||||
hass.states.async_set(
|
||||
entity_id,
|
||||
HVACMode.OFF,
|
||||
attrs,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert heat_cool_char.value == HC_HEAT_COOL_OFF
|
||||
assert acc.available is True
|
||||
|
||||
hass.states.async_set(
|
||||
entity_id,
|
||||
STATE_UNAVAILABLE,
|
||||
attrs,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert heat_cool_char.value == HC_HEAT_COOL_OFF
|
||||
assert acc.available is False
|
||||
|
||||
hass.states.async_set(
|
||||
entity_id,
|
||||
HVACMode.OFF,
|
||||
attrs,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert heat_cool_char.value == HC_HEAT_COOL_OFF
|
||||
assert acc.available is True
|
||||
hass.states.async_set(
|
||||
entity_id,
|
||||
STATE_UNAVAILABLE,
|
||||
attrs,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert heat_cool_char.value == HC_HEAT_COOL_OFF
|
||||
assert acc.available is False
|
||||
|
||||
hk_driver.set_characteristics(
|
||||
{
|
||||
HAP_REPR_CHARS: [
|
||||
{
|
||||
HAP_REPR_AID: acc.aid,
|
||||
HAP_REPR_IID: heat_cool_char.to_HAP()[HAP_REPR_IID],
|
||||
HAP_REPR_VALUE: HC_HEAT_COOL_HEAT,
|
||||
}
|
||||
]
|
||||
},
|
||||
"mock_addr",
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert heat_cool_char.value == HC_HEAT_COOL_HEAT
|
||||
assert acc.available is False
|
||||
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] == HVACMode.HEAT
|
||||
|
||||
hass.states.async_set(
|
||||
entity_id,
|
||||
STATE_UNKNOWN,
|
||||
attrs,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert heat_cool_char.value == HC_HEAT_COOL_HEAT
|
||||
assert acc.available is True
|
||||
|
||||
hk_driver.set_characteristics(
|
||||
{
|
||||
HAP_REPR_CHARS: [
|
||||
{
|
||||
HAP_REPR_AID: acc.aid,
|
||||
HAP_REPR_IID: heat_cool_char.to_HAP()[HAP_REPR_IID],
|
||||
HAP_REPR_VALUE: HC_HEAT_COOL_HEAT,
|
||||
}
|
||||
]
|
||||
},
|
||||
"mock_addr",
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert heat_cool_char.value == HC_HEAT_COOL_HEAT
|
||||
assert acc.available is True
|
||||
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] == HVACMode.HEAT
|
||||
|
Loading…
x
Reference in New Issue
Block a user