Fix HomeKit with entity registry restoration where supported_features is a non-None falsey (#30657)
* Fix homekit with #30094 * Fix test
This commit is contained in:
parent
d6512c8a9f
commit
669c89e8c0
7 changed files with 300 additions and 4 deletions
|
@ -502,13 +502,13 @@ def async_setup_entity_restore(
|
|||
|
||||
attrs: Dict[str, Any] = {ATTR_RESTORED: True}
|
||||
|
||||
if entry.capabilities:
|
||||
if entry.capabilities is not None:
|
||||
attrs.update(entry.capabilities)
|
||||
|
||||
if entry.supported_features:
|
||||
if entry.supported_features is not None:
|
||||
attrs[ATTR_SUPPORTED_FEATURES] = entry.supported_features
|
||||
|
||||
if entry.device_class:
|
||||
if entry.device_class is not None:
|
||||
attrs[ATTR_DEVICE_CLASS] = entry.device_class
|
||||
|
||||
states.async_set(entry.entity_id, STATE_UNAVAILABLE, attrs)
|
||||
|
|
|
@ -13,6 +13,7 @@ from homeassistant.components.homekit.const import ATTR_VALUE
|
|||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_SUPPORTED_FEATURES,
|
||||
EVENT_HOMEASSISTANT_START,
|
||||
STATE_CLOSED,
|
||||
STATE_CLOSING,
|
||||
STATE_OPEN,
|
||||
|
@ -20,6 +21,8 @@ from homeassistant.const import (
|
|||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
)
|
||||
from homeassistant.core import CoreState
|
||||
from homeassistant.helpers import entity_registry
|
||||
|
||||
from tests.common import async_mock_service
|
||||
from tests.components.homekit.common import patch_debounce
|
||||
|
@ -308,3 +311,73 @@ async def test_window_open_close_stop(hass, hk_driver, cls, events):
|
|||
assert acc.char_position_state.value == 2
|
||||
assert len(events) == 3
|
||||
assert events[-1].data[ATTR_VALUE] is None
|
||||
|
||||
|
||||
async def test_window_basic_restore(hass, hk_driver, cls, events):
|
||||
"""Test setting up an entity from state in the event registry."""
|
||||
hass.state = CoreState.not_running
|
||||
|
||||
registry = await entity_registry.async_get_registry(hass)
|
||||
|
||||
registry.async_get_or_create(
|
||||
"cover", "generic", "1234", suggested_object_id="simple",
|
||||
)
|
||||
registry.async_get_or_create(
|
||||
"cover",
|
||||
"generic",
|
||||
"9012",
|
||||
suggested_object_id="all_info_set",
|
||||
capabilities={},
|
||||
supported_features=SUPPORT_STOP,
|
||||
device_class="mock-device-class",
|
||||
)
|
||||
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
acc = cls.window_basic(hass, hk_driver, "Cover", "cover.simple", 2, None)
|
||||
assert acc.category == 14
|
||||
assert acc.char_current_position is not None
|
||||
assert acc.char_target_position is not None
|
||||
assert acc.char_position_state is not None
|
||||
|
||||
acc = cls.window_basic(hass, hk_driver, "Cover", "cover.all_info_set", 2, None)
|
||||
assert acc.category == 14
|
||||
assert acc.char_current_position is not None
|
||||
assert acc.char_target_position is not None
|
||||
assert acc.char_position_state is not None
|
||||
|
||||
|
||||
async def test_window_restore(hass, hk_driver, cls, events):
|
||||
"""Test setting up an entity from state in the event registry."""
|
||||
hass.state = CoreState.not_running
|
||||
|
||||
registry = await entity_registry.async_get_registry(hass)
|
||||
|
||||
registry.async_get_or_create(
|
||||
"cover", "generic", "1234", suggested_object_id="simple",
|
||||
)
|
||||
registry.async_get_or_create(
|
||||
"cover",
|
||||
"generic",
|
||||
"9012",
|
||||
suggested_object_id="all_info_set",
|
||||
capabilities={},
|
||||
supported_features=SUPPORT_STOP,
|
||||
device_class="mock-device-class",
|
||||
)
|
||||
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
acc = cls.window(hass, hk_driver, "Cover", "cover.simple", 2, None)
|
||||
assert acc.category == 14
|
||||
assert acc.char_current_position is not None
|
||||
assert acc.char_target_position is not None
|
||||
assert acc.char_position_state is not None
|
||||
|
||||
acc = cls.window(hass, hk_driver, "Cover", "cover.all_info_set", 2, None)
|
||||
assert acc.category == 14
|
||||
assert acc.char_current_position is not None
|
||||
assert acc.char_target_position is not None
|
||||
assert acc.char_position_state is not None
|
||||
|
|
|
@ -24,10 +24,13 @@ from homeassistant.components.homekit.util import HomeKitSpeedMapping
|
|||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_SUPPORTED_FEATURES,
|
||||
EVENT_HOMEASSISTANT_START,
|
||||
STATE_OFF,
|
||||
STATE_ON,
|
||||
STATE_UNKNOWN,
|
||||
)
|
||||
from homeassistant.core import CoreState
|
||||
from homeassistant.helpers import entity_registry
|
||||
|
||||
from tests.common import async_mock_service
|
||||
from tests.components.homekit.common import patch_debounce
|
||||
|
@ -226,3 +229,40 @@ async def test_fan_speed(hass, hk_driver, cls, events):
|
|||
assert call_set_speed[0].data[ATTR_SPEED] == "ludicrous"
|
||||
assert len(events) == 1
|
||||
assert events[-1].data[ATTR_VALUE] == "ludicrous"
|
||||
|
||||
|
||||
async def test_fan_restore(hass, hk_driver, cls, events):
|
||||
"""Test setting up an entity from state in the event registry."""
|
||||
hass.state = CoreState.not_running
|
||||
|
||||
registry = await entity_registry.async_get_registry(hass)
|
||||
|
||||
registry.async_get_or_create(
|
||||
"fan", "generic", "1234", suggested_object_id="simple",
|
||||
)
|
||||
registry.async_get_or_create(
|
||||
"fan",
|
||||
"generic",
|
||||
"9012",
|
||||
suggested_object_id="all_info_set",
|
||||
capabilities={"speed_list": ["off", "low", "medium", "high"]},
|
||||
supported_features=SUPPORT_SET_SPEED | SUPPORT_OSCILLATE | SUPPORT_DIRECTION,
|
||||
device_class="mock-device-class",
|
||||
)
|
||||
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
acc = cls.fan(hass, hk_driver, "Fan", "fan.simple", 2, None)
|
||||
assert acc.category == 3
|
||||
assert acc.char_active is not None
|
||||
assert acc.char_direction is None
|
||||
assert acc.char_speed is None
|
||||
assert acc.char_swing is None
|
||||
|
||||
acc = cls.fan(hass, hk_driver, "Fan", "fan.all_info_set", 2, None)
|
||||
assert acc.category == 3
|
||||
assert acc.char_active is not None
|
||||
assert acc.char_direction is not None
|
||||
assert acc.char_speed is not None
|
||||
assert acc.char_swing is not None
|
||||
|
|
|
@ -17,10 +17,13 @@ from homeassistant.components.light import (
|
|||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_SUPPORTED_FEATURES,
|
||||
EVENT_HOMEASSISTANT_START,
|
||||
STATE_OFF,
|
||||
STATE_ON,
|
||||
STATE_UNKNOWN,
|
||||
)
|
||||
from homeassistant.core import CoreState
|
||||
from homeassistant.helpers import entity_registry
|
||||
|
||||
from tests.common import async_mock_service
|
||||
from tests.components.homekit.common import patch_debounce
|
||||
|
@ -205,3 +208,36 @@ async def test_light_rgb_color(hass, hk_driver, cls, events):
|
|||
assert call_turn_on[0].data[ATTR_HS_COLOR] == (145, 75)
|
||||
assert len(events) == 1
|
||||
assert events[-1].data[ATTR_VALUE] == "set color at (145, 75)"
|
||||
|
||||
|
||||
async def test_light_restore(hass, hk_driver, cls, events):
|
||||
"""Test setting up an entity from state in the event registry."""
|
||||
hass.state = CoreState.not_running
|
||||
|
||||
registry = await entity_registry.async_get_registry(hass)
|
||||
|
||||
registry.async_get_or_create(
|
||||
"light", "hue", "1234", suggested_object_id="simple",
|
||||
)
|
||||
registry.async_get_or_create(
|
||||
"light",
|
||||
"hue",
|
||||
"9012",
|
||||
suggested_object_id="all_info_set",
|
||||
capabilities={"max": 100},
|
||||
supported_features=5,
|
||||
device_class="mock-device-class",
|
||||
)
|
||||
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
acc = cls.light(hass, hk_driver, "Light", "light.simple", 2, None)
|
||||
assert acc.category == 5 # Lightbulb
|
||||
assert acc.chars == []
|
||||
assert acc.char_on.value == 0
|
||||
|
||||
acc = cls.light(hass, hk_driver, "Light", "light.all_info_set", 2, None)
|
||||
assert acc.category == 5 # Lightbulb
|
||||
assert acc.chars == ["Brightness"]
|
||||
assert acc.char_on.value == 0
|
||||
|
|
|
@ -24,12 +24,15 @@ from homeassistant.const import (
|
|||
ATTR_DEVICE_CLASS,
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_SUPPORTED_FEATURES,
|
||||
EVENT_HOMEASSISTANT_START,
|
||||
STATE_IDLE,
|
||||
STATE_OFF,
|
||||
STATE_ON,
|
||||
STATE_PAUSED,
|
||||
STATE_PLAYING,
|
||||
)
|
||||
from homeassistant.core import CoreState
|
||||
from homeassistant.helpers import entity_registry
|
||||
|
||||
from tests.common import async_mock_service
|
||||
|
||||
|
@ -336,3 +339,56 @@ async def test_media_player_television_basic(hass, hk_driver, events, caplog):
|
|||
assert acc.char_active.value == 1
|
||||
|
||||
assert not caplog.messages or "Error" not in caplog.messages[-1]
|
||||
|
||||
|
||||
async def test_tv_restore(hass, hk_driver, events):
|
||||
"""Test setting up an entity from state in the event registry."""
|
||||
hass.state = CoreState.not_running
|
||||
|
||||
registry = await entity_registry.async_get_registry(hass)
|
||||
|
||||
registry.async_get_or_create(
|
||||
"media_player",
|
||||
"generic",
|
||||
"1234",
|
||||
suggested_object_id="simple",
|
||||
device_class=DEVICE_CLASS_TV,
|
||||
)
|
||||
registry.async_get_or_create(
|
||||
"media_player",
|
||||
"generic",
|
||||
"9012",
|
||||
suggested_object_id="all_info_set",
|
||||
capabilities={
|
||||
ATTR_INPUT_SOURCE_LIST: ["HDMI 1", "HDMI 2", "HDMI 3", "HDMI 4"],
|
||||
},
|
||||
supported_features=3469,
|
||||
device_class=DEVICE_CLASS_TV,
|
||||
)
|
||||
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
acc = TelevisionMediaPlayer(
|
||||
hass, hk_driver, "MediaPlayer", "media_player.simple", 2, None
|
||||
)
|
||||
assert acc.category == 31
|
||||
assert acc.chars_tv == []
|
||||
assert acc.chars_speaker == []
|
||||
assert acc.support_select_source is False
|
||||
assert not hasattr(acc, "char_input_source")
|
||||
|
||||
acc = TelevisionMediaPlayer(
|
||||
hass, hk_driver, "MediaPlayer", "media_player.all_info_set", 2, None
|
||||
)
|
||||
assert acc.category == 31
|
||||
assert acc.chars_tv == ["RemoteKey"]
|
||||
assert acc.chars_speaker == [
|
||||
"Name",
|
||||
"Active",
|
||||
"VolumeControlType",
|
||||
"VolumeSelector",
|
||||
"Volume",
|
||||
]
|
||||
assert acc.support_select_source is True
|
||||
assert acc.char_input_source is not None
|
||||
|
|
|
@ -41,8 +41,11 @@ from homeassistant.const import (
|
|||
ATTR_SUPPORTED_FEATURES,
|
||||
ATTR_TEMPERATURE,
|
||||
CONF_TEMPERATURE_UNIT,
|
||||
EVENT_HOMEASSISTANT_START,
|
||||
TEMP_FAHRENHEIT,
|
||||
)
|
||||
from homeassistant.core import CoreState
|
||||
from homeassistant.helpers import entity_registry
|
||||
|
||||
from tests.common import async_mock_service
|
||||
from tests.components.homekit.common import patch_debounce
|
||||
|
@ -517,6 +520,51 @@ 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_restore(hass, hk_driver, cls, events):
|
||||
"""Test setting up an entity from state in the event registry."""
|
||||
hass.state = CoreState.not_running
|
||||
|
||||
registry = await entity_registry.async_get_registry(hass)
|
||||
|
||||
registry.async_get_or_create(
|
||||
"climate", "generic", "1234", suggested_object_id="simple",
|
||||
)
|
||||
registry.async_get_or_create(
|
||||
"climate",
|
||||
"generic",
|
||||
"9012",
|
||||
suggested_object_id="all_info_set",
|
||||
capabilities={
|
||||
ATTR_MIN_TEMP: 60,
|
||||
ATTR_MAX_TEMP: 70,
|
||||
ATTR_HVAC_MODES: [HVAC_MODE_HEAT_COOL, HVAC_MODE_OFF],
|
||||
},
|
||||
supported_features=0,
|
||||
device_class="mock-device-class",
|
||||
)
|
||||
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
acc = cls.thermostat(hass, hk_driver, "Climate", "climate.simple", 2, None)
|
||||
assert acc.category == 9
|
||||
assert acc.get_temperature_range() == (7, 35)
|
||||
assert set(acc.char_target_heat_cool.properties["ValidValues"].keys()) == {
|
||||
"cool",
|
||||
"heat",
|
||||
"heat_cool",
|
||||
"off",
|
||||
}
|
||||
|
||||
acc = cls.thermostat(hass, hk_driver, "Climate", "climate.all_info_set", 2, None)
|
||||
assert acc.category == 9
|
||||
assert acc.get_temperature_range() == (60.0, 70.0)
|
||||
assert set(acc.char_target_heat_cool.properties["ValidValues"].keys()) == {
|
||||
"heat_cool",
|
||||
"off",
|
||||
}
|
||||
|
||||
|
||||
async def test_thermostat_hvac_modes(hass, hk_driver, cls):
|
||||
"""Test if unsupported HVAC modes are deactivated in HomeKit."""
|
||||
entity_id = "climate.test"
|
||||
|
@ -671,3 +719,46 @@ async def test_water_heater_get_temperature_range(hass, hk_driver, cls):
|
|||
)
|
||||
await hass.async_block_till_done()
|
||||
assert acc.get_temperature_range() == (15.5, 21.0)
|
||||
|
||||
|
||||
async def test_water_heater_restore(hass, hk_driver, cls, events):
|
||||
"""Test setting up an entity from state in the event registry."""
|
||||
hass.state = CoreState.not_running
|
||||
|
||||
registry = await entity_registry.async_get_registry(hass)
|
||||
|
||||
registry.async_get_or_create(
|
||||
"water_heater", "generic", "1234", suggested_object_id="simple",
|
||||
)
|
||||
registry.async_get_or_create(
|
||||
"water_heater",
|
||||
"generic",
|
||||
"9012",
|
||||
suggested_object_id="all_info_set",
|
||||
capabilities={ATTR_MIN_TEMP: 60, ATTR_MAX_TEMP: 70},
|
||||
supported_features=0,
|
||||
device_class="mock-device-class",
|
||||
)
|
||||
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
acc = cls.thermostat(hass, hk_driver, "WaterHeater", "water_heater.simple", 2, None)
|
||||
assert acc.category == 9
|
||||
assert acc.get_temperature_range() == (7, 35)
|
||||
assert set(acc.char_current_heat_cool.properties["ValidValues"].keys()) == {
|
||||
"Cool",
|
||||
"Heat",
|
||||
"Off",
|
||||
}
|
||||
|
||||
acc = cls.thermostat(
|
||||
hass, hk_driver, "WaterHeater", "water_heater.all_info_set", 2, None
|
||||
)
|
||||
assert acc.category == 9
|
||||
assert acc.get_temperature_range() == (60.0, 70.0)
|
||||
assert set(acc.char_current_heat_cool.properties["ValidValues"].keys()) == {
|
||||
"Cool",
|
||||
"Heat",
|
||||
"Off",
|
||||
}
|
||||
|
|
|
@ -511,7 +511,7 @@ async def test_restore_states(hass):
|
|||
simple = hass.states.get("light.simple")
|
||||
assert simple is not None
|
||||
assert simple.state == STATE_UNAVAILABLE
|
||||
assert simple.attributes == {"restored": True}
|
||||
assert simple.attributes == {"restored": True, "supported_features": 0}
|
||||
|
||||
disabled = hass.states.get("light.disabled")
|
||||
assert disabled is None
|
||||
|
|
Loading…
Add table
Reference in a new issue