Refactor HomeKit to allow supported features/device class to change (#101719)

This commit is contained in:
J. Nick Koston 2023-10-10 06:20:25 -10:00 committed by GitHub
parent f166e1cc1a
commit 7b4b8e7516
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 662 additions and 452 deletions

View file

@ -36,7 +36,13 @@ from homeassistant.components.homekit.const import (
)
from homeassistant.components.homekit.type_triggers import DeviceTriggerAccessory
from homeassistant.components.homekit.util import get_persist_fullpath_for_entry_id
from homeassistant.components.light import (
ATTR_COLOR_MODE,
ATTR_SUPPORTED_COLOR_MODES,
ColorMode,
)
from homeassistant.components.sensor import SensorDeviceClass
from homeassistant.components.switch import SwitchDeviceClass
from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_ZEROCONF
from homeassistant.const import (
ATTR_DEVICE_CLASS,
@ -532,7 +538,7 @@ async def test_homekit_remove_accessory(
acc_mock.stop = AsyncMock()
homekit.bridge.accessories = {6: acc_mock}
acc = await homekit.async_remove_bridge_accessory(6)
acc = homekit.async_remove_bridge_accessory(6)
assert acc is acc_mock
assert len(homekit.bridge.accessories) == 0
@ -876,6 +882,7 @@ async def test_homekit_stop(hass: HomeAssistant) -> None:
# Test if driver is started
homekit.status = STATUS_RUNNING
homekit._cancel_reload_dispatcher = lambda: None
await homekit.async_stop()
await hass.async_block_till_done()
assert homekit.driver.async_stop.called is True
@ -919,6 +926,120 @@ async def test_homekit_reset_accessories(
await homekit.async_stop()
async def test_homekit_reload_accessory_can_change_class(
hass: HomeAssistant, mock_async_zeroconf: None, mock_hap
) -> None:
"""Test reloading a HomeKit Accessory in brdige mode.
This test ensure when device class changes the HomeKit class changes.
"""
entry = MockConfigEntry(
domain=DOMAIN, data={CONF_NAME: "mock_name", CONF_PORT: 12345}
)
entity_id = "switch.outlet"
hass.states.async_set(entity_id, "on", {ATTR_DEVICE_CLASS: None})
homekit = _mock_homekit(hass, entry, HOMEKIT_MODE_BRIDGE)
with patch(f"{PATH_HOMEKIT}.HomeKit", return_value=homekit):
await async_init_entry(hass, entry)
bridge: HomeBridge = homekit.driver.accessory
await bridge.run()
switch_accessory = next(iter(bridge.accessories.values()))
assert type(switch_accessory).__name__ == "Switch"
await hass.async_block_till_done()
assert homekit.status == STATUS_RUNNING
homekit.driver.aio_stop_event = MagicMock()
hass.states.async_set(
entity_id, "off", {ATTR_DEVICE_CLASS: SwitchDeviceClass.OUTLET}
)
await hass.async_block_till_done()
await hass.async_block_till_done()
outlet_accessory = next(iter(bridge.accessories.values()))
assert type(outlet_accessory).__name__ == "Outlet"
await homekit.async_stop()
async def test_homekit_reload_accessory_in_accessory_mode(
hass: HomeAssistant, mock_async_zeroconf: None, mock_hap
) -> None:
"""Test reloading a HomeKit Accessory in accessory mode.
This test ensure a device class changes can change the class of
the accessory.
"""
entry = MockConfigEntry(
domain=DOMAIN, data={CONF_NAME: "mock_name", CONF_PORT: 12345}
)
entity_id = "switch.outlet"
hass.states.async_set(entity_id, "on", {ATTR_DEVICE_CLASS: None})
homekit = _mock_homekit(hass, entry, HOMEKIT_MODE_ACCESSORY)
with patch(f"{PATH_HOMEKIT}.HomeKit", return_value=homekit):
await async_init_entry(hass, entry)
primary_accessory = homekit.driver.accessory
await primary_accessory.run()
assert type(primary_accessory).__name__ == "Switch"
await hass.async_block_till_done()
assert homekit.status == STATUS_RUNNING
homekit.driver.aio_stop_event = MagicMock()
hass.states.async_set(
entity_id, "off", {ATTR_DEVICE_CLASS: SwitchDeviceClass.OUTLET}
)
await hass.async_block_till_done()
await hass.async_block_till_done()
primary_accessory = homekit.driver.accessory
assert type(primary_accessory).__name__ == "Outlet"
await homekit.async_stop()
async def test_homekit_reload_accessory_same_class(
hass: HomeAssistant, mock_async_zeroconf: None, mock_hap
) -> None:
"""Test reloading a HomeKit Accessory in bridge mode.
The class of the accessory remains the same.
"""
entry = MockConfigEntry(
domain=DOMAIN, data={CONF_NAME: "mock_name", CONF_PORT: 12345}
)
entity_id = "light.color"
hass.states.async_set(
entity_id,
"on",
{ATTR_SUPPORTED_COLOR_MODES: [ColorMode.HS], ATTR_COLOR_MODE: ColorMode.HS},
)
homekit = _mock_homekit(hass, entry, HOMEKIT_MODE_BRIDGE)
with patch(f"{PATH_HOMEKIT}.HomeKit", return_value=homekit):
await async_init_entry(hass, entry)
bridge: HomeBridge = homekit.driver.accessory
await bridge.run()
light_accessory_color = next(iter(bridge.accessories.values()))
assert not hasattr(light_accessory_color, "char_color_temp")
await hass.async_block_till_done()
assert homekit.status == STATUS_RUNNING
homekit.driver.aio_stop_event = MagicMock()
hass.states.async_set(
entity_id,
"on",
{
ATTR_SUPPORTED_COLOR_MODES: [ColorMode.HS, ColorMode.COLOR_TEMP],
ATTR_COLOR_MODE: ColorMode.COLOR_TEMP,
},
)
await hass.async_block_till_done()
await hass.async_block_till_done()
light_accessory_color_and_temp = next(iter(bridge.accessories.values()))
assert hasattr(light_accessory_color_and_temp, "char_color_temp")
await homekit.async_stop()
async def test_homekit_unpair(
hass: HomeAssistant, device_registry: dr.DeviceRegistry, mock_async_zeroconf: None
) -> None:
@ -1076,8 +1197,8 @@ async def test_homekit_reset_accessories_not_supported(
with patch(f"{PATH_HOMEKIT}.HomeKit", return_value=homekit), patch(
"pyhap.accessory.Bridge.add_accessory"
) as mock_add_accessory, patch(
"pyhap.accessory_driver.AccessoryDriver.config_changed"
) as hk_driver_config_changed, patch(
"pyhap.accessory_driver.AccessoryDriver.async_update_advertisement"
) as hk_driver_async_update_advertisement, patch(
"pyhap.accessory_driver.AccessoryDriver.async_start"
), patch.object(
homekit_base, "_HOMEKIT_CONFIG_UPDATE_TIME", 0
@ -1101,7 +1222,7 @@ async def test_homekit_reset_accessories_not_supported(
)
await hass.async_block_till_done()
assert hk_driver_config_changed.call_count == 2
assert hk_driver_async_update_advertisement.call_count == 1
assert not mock_add_accessory.called
assert len(homekit.bridge.accessories) == 0
homekit.status = STATUS_STOPPED
@ -1165,22 +1286,25 @@ async def test_homekit_reset_accessories_not_bridged(
with patch(f"{PATH_HOMEKIT}.HomeKit", return_value=homekit), patch(
"pyhap.accessory.Bridge.add_accessory"
) as mock_add_accessory, patch(
"pyhap.accessory_driver.AccessoryDriver.config_changed"
) as hk_driver_config_changed, patch(
"pyhap.accessory_driver.AccessoryDriver.async_update_advertisement"
) as hk_driver_async_update_advertisement, patch(
"pyhap.accessory_driver.AccessoryDriver.async_start"
), patch.object(
homekit_base, "_HOMEKIT_CONFIG_UPDATE_TIME", 0
):
await async_init_entry(hass, entry)
assert hk_driver_async_update_advertisement.call_count == 0
acc_mock = MagicMock()
acc_mock.entity_id = entity_id
acc_mock.stop = AsyncMock()
acc_mock.to_HAP = lambda: {}
aid = homekit.aid_storage.get_or_allocate_aid_for_entity_id(entity_id)
homekit.bridge.accessories = {aid: acc_mock}
homekit.status = STATUS_RUNNING
homekit.driver.aio_stop_event = MagicMock()
assert hk_driver_async_update_advertisement.call_count == 0
await hass.services.async_call(
DOMAIN,
@ -1190,7 +1314,7 @@ async def test_homekit_reset_accessories_not_bridged(
)
await hass.async_block_till_done()
assert hk_driver_config_changed.call_count == 0
assert hk_driver_async_update_advertisement.call_count == 0
assert not mock_add_accessory.called
homekit.status = STATUS_STOPPED
@ -1208,8 +1332,8 @@ async def test_homekit_reset_single_accessory(
homekit = _mock_homekit(hass, entry, HOMEKIT_MODE_ACCESSORY)
with patch(f"{PATH_HOMEKIT}.HomeKit", return_value=homekit), patch(
"pyhap.accessory_driver.AccessoryDriver.config_changed"
) as hk_driver_config_changed, patch(
"pyhap.accessory_driver.AccessoryDriver.async_update_advertisement"
) as hk_driver_async_update_advertisement, patch(
"pyhap.accessory_driver.AccessoryDriver.async_start"
), patch(
f"{PATH_HOMEKIT}.accessories.HomeAccessory.run"
@ -1226,7 +1350,7 @@ async def test_homekit_reset_single_accessory(
)
await hass.async_block_till_done()
assert mock_run.called
assert hk_driver_config_changed.call_count == 1
assert hk_driver_async_update_advertisement.call_count == 1
homekit.status = STATUS_READY
await homekit.async_stop()

View file

@ -129,7 +129,14 @@ async def test_fan_direction(hass: HomeAssistant, hk_driver, events) -> None:
await hass.async_block_till_done()
assert acc.char_direction.value == 0
hass.states.async_set(entity_id, STATE_ON, {ATTR_DIRECTION: DIRECTION_REVERSE})
hass.states.async_set(
entity_id,
STATE_ON,
{
ATTR_SUPPORTED_FEATURES: FanEntityFeature.DIRECTION,
ATTR_DIRECTION: DIRECTION_REVERSE,
},
)
await hass.async_block_till_done()
assert acc.char_direction.value == 1
@ -197,7 +204,11 @@ async def test_fan_oscillate(hass: HomeAssistant, hk_driver, events) -> None:
await hass.async_block_till_done()
assert acc.char_swing.value == 0
hass.states.async_set(entity_id, STATE_ON, {ATTR_OSCILLATING: True})
hass.states.async_set(
entity_id,
STATE_ON,
{ATTR_SUPPORTED_FEATURES: FanEntityFeature.OSCILLATE, ATTR_OSCILLATING: True},
)
await hass.async_block_till_done()
assert acc.char_swing.value == 1
@ -272,7 +283,15 @@ async def test_fan_speed(hass: HomeAssistant, hk_driver, events) -> None:
await acc.run()
await hass.async_block_till_done()
hass.states.async_set(entity_id, STATE_ON, {ATTR_PERCENTAGE: 100})
hass.states.async_set(
entity_id,
STATE_ON,
{
ATTR_PERCENTAGE_STEP: 25,
ATTR_SUPPORTED_FEATURES: FanEntityFeature.SET_SPEED,
ATTR_PERCENTAGE: 100,
},
)
await hass.async_block_till_done()
assert acc.char_speed.value == 100
@ -306,7 +325,15 @@ async def test_fan_speed(hass: HomeAssistant, hk_driver, events) -> None:
assert events[-1].data[ATTR_VALUE] == 42
# Verify speed is preserved from off to on
hass.states.async_set(entity_id, STATE_OFF, {ATTR_PERCENTAGE: 42})
hass.states.async_set(
entity_id,
STATE_OFF,
{
ATTR_PERCENTAGE_STEP: 25,
ATTR_SUPPORTED_FEATURES: FanEntityFeature.SET_SPEED,
ATTR_PERCENTAGE: 42,
},
)
await hass.async_block_till_done()
assert acc.char_speed.value == 50
assert acc.char_active.value == 0

View file

@ -48,7 +48,9 @@ async def test_humidifier(hass: HomeAssistant, hk_driver, events) -> None:
"""Test if humidifier accessory and HA are updated accordingly."""
entity_id = "humidifier.test"
hass.states.async_set(entity_id, STATE_OFF)
hass.states.async_set(
entity_id, STATE_OFF, {ATTR_DEVICE_CLASS: HumidifierDeviceClass.HUMIDIFIER}
)
await hass.async_block_till_done()
acc = HumidifierDehumidifier(
hass, hk_driver, "HumidifierDehumidifier", entity_id, 1, None
@ -77,7 +79,7 @@ async def test_humidifier(hass: HomeAssistant, hk_driver, events) -> None:
hass.states.async_set(
entity_id,
STATE_ON,
{ATTR_HUMIDITY: 47},
{ATTR_HUMIDITY: 47, ATTR_DEVICE_CLASS: HumidifierDeviceClass.HUMIDIFIER},
)
await hass.async_block_till_done()
assert acc.char_target_humidity.value == 47.0
@ -158,7 +160,7 @@ async def test_dehumidifier(hass: HomeAssistant, hk_driver, events) -> None:
hass.states.async_set(
entity_id,
STATE_ON,
{ATTR_HUMIDITY: 30},
{ATTR_HUMIDITY: 30, ATTR_DEVICE_CLASS: HumidifierDeviceClass.DEHUMIDIFIER},
)
await hass.async_block_till_done()
assert acc.char_target_humidity.value == 30.0
@ -169,7 +171,7 @@ async def test_dehumidifier(hass: HomeAssistant, hk_driver, events) -> None:
hass.states.async_set(
entity_id,
STATE_OFF,
{ATTR_HUMIDITY: 42},
{ATTR_HUMIDITY: 42, ATTR_DEVICE_CLASS: HumidifierDeviceClass.DEHUMIDIFIER},
)
await hass.async_block_till_done()
assert acc.char_target_humidity.value == 42.0

View file

@ -122,7 +122,8 @@ async def test_light_basic(hass: HomeAssistant, hk_driver, events) -> None:
@pytest.mark.parametrize(
"supported_color_modes", [["brightness"], ["hs"], ["color_temp"]]
"supported_color_modes",
[[ColorMode.BRIGHTNESS], [ColorMode.HS], [ColorMode.COLOR_TEMP]],
)
async def test_light_brightness(
hass: HomeAssistant, hk_driver, events, supported_color_modes
@ -149,7 +150,11 @@ async def test_light_brightness(
await hass.async_block_till_done()
assert acc.char_brightness.value == 100
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 102})
hass.states.async_set(
entity_id,
STATE_ON,
{ATTR_SUPPORTED_COLOR_MODES: supported_color_modes, ATTR_BRIGHTNESS: 102},
)
await hass.async_block_till_done()
assert acc.char_brightness.value == 40
@ -222,24 +227,48 @@ async def test_light_brightness(
# 0 is a special case for homekit, see "Handle Brightness"
# in update_state
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 0})
hass.states.async_set(
entity_id,
STATE_ON,
{ATTR_SUPPORTED_COLOR_MODES: supported_color_modes, ATTR_BRIGHTNESS: 0},
)
await hass.async_block_till_done()
assert acc.char_brightness.value == 1
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 255})
hass.states.async_set(
entity_id,
STATE_ON,
{ATTR_SUPPORTED_COLOR_MODES: supported_color_modes, ATTR_BRIGHTNESS: 255},
)
await hass.async_block_till_done()
assert acc.char_brightness.value == 100
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 0})
hass.states.async_set(
entity_id,
STATE_ON,
{ATTR_SUPPORTED_COLOR_MODES: supported_color_modes, ATTR_BRIGHTNESS: 0},
)
await hass.async_block_till_done()
assert acc.char_brightness.value == 1
# Ensure floats are handled
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 55.66})
hass.states.async_set(
entity_id,
STATE_ON,
{ATTR_SUPPORTED_COLOR_MODES: supported_color_modes, ATTR_BRIGHTNESS: 55.66},
)
await hass.async_block_till_done()
assert acc.char_brightness.value == 22
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 108.4})
hass.states.async_set(
entity_id,
STATE_ON,
{ATTR_SUPPORTED_COLOR_MODES: supported_color_modes, ATTR_BRIGHTNESS: 108.4},
)
await hass.async_block_till_done()
assert acc.char_brightness.value == 43
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 0.0})
hass.states.async_set(
entity_id,
STATE_ON,
{ATTR_SUPPORTED_COLOR_MODES: supported_color_modes, ATTR_BRIGHTNESS: 0.0},
)
await hass.async_block_till_done()
assert acc.char_brightness.value == 1
@ -490,7 +519,9 @@ async def test_light_color_temperature_and_rgb_color(
assert acc.char_saturation.value == 100
@pytest.mark.parametrize("supported_color_modes", [["hs"], ["rgb"], ["xy"]])
@pytest.mark.parametrize(
"supported_color_modes", [[ColorMode.HS], [ColorMode.RGB], [ColorMode.XY]]
)
async def test_light_rgb_color(
hass: HomeAssistant, hk_driver, events, supported_color_modes
) -> None:
@ -1221,7 +1252,7 @@ async def test_light_set_brightness_and_color(
entity_id,
STATE_ON,
{
ATTR_SUPPORTED_COLOR_MODES: ["hs"],
ATTR_SUPPORTED_COLOR_MODES: [ColorMode.HS],
ATTR_BRIGHTNESS: 255,
},
)
@ -1241,11 +1272,19 @@ async def test_light_set_brightness_and_color(
await hass.async_block_till_done()
assert acc.char_brightness.value == 100
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 102})
hass.states.async_set(
entity_id,
STATE_ON,
{ATTR_SUPPORTED_COLOR_MODES: [ColorMode.HS], ATTR_BRIGHTNESS: 102},
)
await hass.async_block_till_done()
assert acc.char_brightness.value == 40
hass.states.async_set(entity_id, STATE_ON, {ATTR_HS_COLOR: (4.5, 9.2)})
hass.states.async_set(
entity_id,
STATE_ON,
{ATTR_SUPPORTED_COLOR_MODES: [ColorMode.HS], ATTR_HS_COLOR: (4.5, 9.2)},
)
await hass.async_block_till_done()
assert acc.char_hue.value == 4
assert acc.char_saturation.value == 9
@ -1297,7 +1336,7 @@ async def test_light_min_max_mireds(hass: HomeAssistant, hk_driver, events) -> N
entity_id,
STATE_ON,
{
ATTR_SUPPORTED_COLOR_MODES: ["color_temp"],
ATTR_SUPPORTED_COLOR_MODES: [ColorMode.COLOR_TEMP],
ATTR_BRIGHTNESS: 255,
ATTR_MAX_MIREDS: 500.5,
ATTR_MIN_MIREDS: 100.5,
@ -1319,7 +1358,7 @@ async def test_light_set_brightness_and_color_temp(
entity_id,
STATE_ON,
{
ATTR_SUPPORTED_COLOR_MODES: ["color_temp"],
ATTR_SUPPORTED_COLOR_MODES: [ColorMode.COLOR_TEMP],
ATTR_BRIGHTNESS: 255,
},
)
@ -1338,11 +1377,22 @@ async def test_light_set_brightness_and_color_temp(
await hass.async_block_till_done()
assert acc.char_brightness.value == 100
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 102})
hass.states.async_set(
entity_id,
STATE_ON,
{ATTR_SUPPORTED_COLOR_MODES: [ColorMode.COLOR_TEMP], ATTR_BRIGHTNESS: 102},
)
await hass.async_block_till_done()
assert acc.char_brightness.value == 40
hass.states.async_set(entity_id, STATE_ON, {ATTR_COLOR_TEMP_KELVIN: (4461)})
hass.states.async_set(
entity_id,
STATE_ON,
{
ATTR_SUPPORTED_COLOR_MODES: [ColorMode.COLOR_TEMP],
ATTR_COLOR_TEMP_KELVIN: (4461),
},
)
await hass.async_block_till_done()
assert acc.char_color_temp.value == 224

View file

@ -56,11 +56,12 @@ async def test_media_player_set_state(hass: HomeAssistant, hk_driver, events) ->
}
}
entity_id = "media_player.test"
base_attrs = {ATTR_SUPPORTED_FEATURES: 20873, ATTR_MEDIA_VOLUME_MUTED: False}
hass.states.async_set(
entity_id,
None,
{ATTR_SUPPORTED_FEATURES: 20873, ATTR_MEDIA_VOLUME_MUTED: False},
base_attrs,
)
await hass.async_block_till_done()
acc = MediaPlayer(hass, hk_driver, "MediaPlayer", entity_id, 2, config)
@ -75,33 +76,35 @@ async def test_media_player_set_state(hass: HomeAssistant, hk_driver, events) ->
assert acc.chars[FEATURE_PLAY_STOP].value is False
assert acc.chars[FEATURE_TOGGLE_MUTE].value is False
hass.states.async_set(entity_id, STATE_ON, {ATTR_MEDIA_VOLUME_MUTED: True})
hass.states.async_set(
entity_id, STATE_ON, {**base_attrs, ATTR_MEDIA_VOLUME_MUTED: True}
)
await hass.async_block_till_done()
assert acc.chars[FEATURE_ON_OFF].value is True
assert acc.chars[FEATURE_TOGGLE_MUTE].value is True
hass.states.async_set(entity_id, STATE_OFF)
hass.states.async_set(entity_id, STATE_OFF, base_attrs)
await hass.async_block_till_done()
assert acc.chars[FEATURE_ON_OFF].value is False
hass.states.async_set(entity_id, STATE_ON)
hass.states.async_set(entity_id, STATE_ON, base_attrs)
await hass.async_block_till_done()
assert acc.chars[FEATURE_ON_OFF].value is True
hass.states.async_set(entity_id, STATE_STANDBY)
hass.states.async_set(entity_id, STATE_STANDBY, base_attrs)
await hass.async_block_till_done()
assert acc.chars[FEATURE_ON_OFF].value is False
hass.states.async_set(entity_id, STATE_PLAYING)
hass.states.async_set(entity_id, STATE_PLAYING, base_attrs)
await hass.async_block_till_done()
assert acc.chars[FEATURE_PLAY_PAUSE].value is True
assert acc.chars[FEATURE_PLAY_STOP].value is True
hass.states.async_set(entity_id, STATE_PAUSED)
hass.states.async_set(entity_id, STATE_PAUSED, base_attrs)
await hass.async_block_till_done()
assert acc.chars[FEATURE_PLAY_PAUSE].value is False
hass.states.async_set(entity_id, STATE_IDLE)
hass.states.async_set(entity_id, STATE_IDLE, base_attrs)
await hass.async_block_till_done()
assert acc.chars[FEATURE_PLAY_STOP].value is False
@ -180,15 +183,16 @@ async def test_media_player_television(
# Supports 'select_source', 'volume_step', 'turn_on', 'turn_off',
# 'volume_mute', 'volume_set', 'pause'
base_attrs = {
ATTR_DEVICE_CLASS: MediaPlayerDeviceClass.TV,
ATTR_SUPPORTED_FEATURES: 3469,
ATTR_MEDIA_VOLUME_MUTED: False,
ATTR_INPUT_SOURCE_LIST: ["HDMI 1", "HDMI 2", "HDMI 3", "HDMI 4"],
}
hass.states.async_set(
entity_id,
None,
{
ATTR_DEVICE_CLASS: MediaPlayerDeviceClass.TV,
ATTR_SUPPORTED_FEATURES: 3469,
ATTR_MEDIA_VOLUME_MUTED: False,
ATTR_INPUT_SOURCE_LIST: ["HDMI 1", "HDMI 2", "HDMI 3", "HDMI 4"],
},
base_attrs,
)
await hass.async_block_till_done()
acc = TelevisionMediaPlayer(hass, hk_driver, "MediaPlayer", entity_id, 2, None)
@ -203,32 +207,40 @@ async def test_media_player_television(
assert acc.char_input_source.value == 0
assert acc.char_mute.value is False
hass.states.async_set(entity_id, STATE_ON, {ATTR_MEDIA_VOLUME_MUTED: True})
hass.states.async_set(
entity_id, STATE_ON, {**base_attrs, ATTR_MEDIA_VOLUME_MUTED: True}
)
await hass.async_block_till_done()
assert acc.char_active.value == 1
assert acc.char_mute.value is True
hass.states.async_set(entity_id, STATE_OFF)
hass.states.async_set(entity_id, STATE_OFF, base_attrs)
await hass.async_block_till_done()
assert acc.char_active.value == 0
hass.states.async_set(entity_id, STATE_ON)
hass.states.async_set(entity_id, STATE_ON, base_attrs)
await hass.async_block_till_done()
assert acc.char_active.value == 1
hass.states.async_set(entity_id, STATE_STANDBY)
hass.states.async_set(entity_id, STATE_STANDBY, base_attrs)
await hass.async_block_till_done()
assert acc.char_active.value == 0
hass.states.async_set(entity_id, STATE_ON, {ATTR_INPUT_SOURCE: "HDMI 2"})
hass.states.async_set(
entity_id, STATE_ON, {**base_attrs, ATTR_INPUT_SOURCE: "HDMI 2"}
)
await hass.async_block_till_done()
assert acc.char_input_source.value == 1
hass.states.async_set(entity_id, STATE_ON, {ATTR_INPUT_SOURCE: "HDMI 3"})
hass.states.async_set(
entity_id, STATE_ON, {**base_attrs, ATTR_INPUT_SOURCE: "HDMI 3"}
)
await hass.async_block_till_done()
assert acc.char_input_source.value == 2
hass.states.async_set(entity_id, STATE_ON, {ATTR_INPUT_SOURCE: "HDMI 5"})
hass.states.async_set(
entity_id, STATE_ON, {**base_attrs, ATTR_INPUT_SOURCE: "HDMI 5"}
)
await hass.async_block_till_done()
assert acc.char_input_source.value == 0
assert caplog.records[-2].levelname == "DEBUG"
@ -358,12 +370,15 @@ async def test_media_player_television_basic(
) -> None:
"""Test if basic television accessory and HA are updated accordingly."""
entity_id = "media_player.television"
base_attrs = {
ATTR_DEVICE_CLASS: MediaPlayerDeviceClass.TV,
ATTR_SUPPORTED_FEATURES: 384,
}
# Supports turn_on', 'turn_off'
hass.states.async_set(
entity_id,
None,
{ATTR_DEVICE_CLASS: MediaPlayerDeviceClass.TV, ATTR_SUPPORTED_FEATURES: 384},
base_attrs,
)
await hass.async_block_till_done()
acc = TelevisionMediaPlayer(hass, hk_driver, "MediaPlayer", entity_id, 2, None)
@ -374,15 +389,19 @@ async def test_media_player_television_basic(
assert acc.chars_speaker == []
assert acc.support_select_source is False
hass.states.async_set(entity_id, STATE_ON, {ATTR_MEDIA_VOLUME_MUTED: True})
hass.states.async_set(
entity_id, STATE_ON, {**base_attrs, ATTR_MEDIA_VOLUME_MUTED: True}
)
await hass.async_block_till_done()
assert acc.char_active.value == 1
hass.states.async_set(entity_id, STATE_OFF)
hass.states.async_set(entity_id, STATE_OFF, base_attrs)
await hass.async_block_till_done()
assert acc.char_active.value == 0
hass.states.async_set(entity_id, STATE_ON, {ATTR_INPUT_SOURCE: "HDMI 3"})
hass.states.async_set(
entity_id, STATE_ON, {**base_attrs, ATTR_INPUT_SOURCE: "HDMI 3"}
)
await hass.async_block_till_done()
assert acc.char_active.value == 1

View file

@ -1,13 +1,14 @@
"""Test different accessory types: Remotes."""
from unittest.mock import patch
import pytest
from homeassistant.components.homekit.accessories import HomeDriver
from homeassistant.components.homekit.const import (
ATTR_KEY_NAME,
ATTR_VALUE,
DOMAIN as HOMEKIT_DOMAIN,
EVENT_HOMEKIT_TV_REMOTE_KEY_PRESSED,
KEY_ARROW_RIGHT,
SERVICE_HOMEKIT_RESET_ACCESSORY,
)
from homeassistant.components.homekit.type_remotes import ActivityRemote
from homeassistant.components.remote import (
@ -30,18 +31,19 @@ from tests.common import async_mock_service
async def test_activity_remote(
hass: HomeAssistant, hk_driver, events, caplog: pytest.LogCaptureFixture
hass: HomeAssistant, hk_driver: HomeDriver, events, caplog: pytest.LogCaptureFixture
) -> None:
"""Test if remote accessory and HA are updated accordingly."""
entity_id = "remote.harmony"
base_attrs = {
ATTR_SUPPORTED_FEATURES: RemoteEntityFeature.ACTIVITY,
ATTR_CURRENT_ACTIVITY: "Apple TV",
ATTR_ACTIVITY_LIST: ["TV", "Apple TV"],
}
hass.states.async_set(
entity_id,
None,
{
ATTR_SUPPORTED_FEATURES: RemoteEntityFeature.ACTIVITY,
ATTR_CURRENT_ACTIVITY: "Apple TV",
ATTR_ACTIVITY_LIST: ["TV", "Apple TV"],
},
base_attrs,
)
await hass.async_block_till_done()
acc = ActivityRemote(hass, hk_driver, "ActivityRemote", entity_id, 2, None)
@ -58,47 +60,31 @@ async def test_activity_remote(
hass.states.async_set(
entity_id,
STATE_ON,
{
ATTR_SUPPORTED_FEATURES: RemoteEntityFeature.ACTIVITY,
ATTR_CURRENT_ACTIVITY: "Apple TV",
ATTR_ACTIVITY_LIST: ["TV", "Apple TV"],
},
base_attrs,
)
await hass.async_block_till_done()
assert acc.char_active.value == 1
hass.states.async_set(entity_id, STATE_OFF)
hass.states.async_set(entity_id, STATE_OFF, base_attrs)
await hass.async_block_till_done()
assert acc.char_active.value == 0
hass.states.async_set(entity_id, STATE_ON)
hass.states.async_set(entity_id, STATE_ON, base_attrs)
await hass.async_block_till_done()
assert acc.char_active.value == 1
hass.states.async_set(entity_id, STATE_STANDBY)
hass.states.async_set(entity_id, STATE_STANDBY, base_attrs)
await hass.async_block_till_done()
assert acc.char_active.value == 0
hass.states.async_set(
entity_id,
STATE_ON,
{
ATTR_SUPPORTED_FEATURES: RemoteEntityFeature.ACTIVITY,
ATTR_CURRENT_ACTIVITY: "TV",
ATTR_ACTIVITY_LIST: ["TV", "Apple TV"],
},
entity_id, STATE_ON, {**base_attrs, ATTR_CURRENT_ACTIVITY: "TV"}
)
await hass.async_block_till_done()
assert acc.char_input_source.value == 0
hass.states.async_set(
entity_id,
STATE_ON,
{
ATTR_SUPPORTED_FEATURES: RemoteEntityFeature.ACTIVITY,
ATTR_CURRENT_ACTIVITY: "Apple TV",
ATTR_ACTIVITY_LIST: ["TV", "Apple TV"],
},
entity_id, STATE_ON, {**base_attrs, ATTR_CURRENT_ACTIVITY: "Apple TV"}
)
await hass.async_block_till_done()
assert acc.char_input_source.value == 1
@ -154,21 +140,19 @@ async def test_activity_remote(
assert len(events) == 1
assert events[0].data[ATTR_KEY_NAME] == KEY_ARROW_RIGHT
call_reset_accessory = async_mock_service(
hass, HOMEKIT_DOMAIN, SERVICE_HOMEKIT_RESET_ACCESSORY
)
# A wild source appears - The accessory should rebuild itself
hass.states.async_set(
entity_id,
STATE_ON,
{
ATTR_SUPPORTED_FEATURES: RemoteEntityFeature.ACTIVITY,
ATTR_CURRENT_ACTIVITY: "Amazon TV",
ATTR_ACTIVITY_LIST: ["TV", "Apple TV", "Amazon TV"],
},
)
await hass.async_block_till_done()
assert call_reset_accessory[0].data[ATTR_ENTITY_ID] == entity_id
# A wild source appears - The accessory should reload itself
with patch.object(acc, "async_reload") as mock_reload:
hass.states.async_set(
entity_id,
STATE_ON,
{
**base_attrs,
ATTR_CURRENT_ACTIVITY: "Amazon TV",
ATTR_ACTIVITY_LIST: ["TV", "Apple TV", "Amazon TV"],
},
)
await hass.async_block_till_done()
assert mock_reload.called
async def test_activity_remote_bad_names(

View file

@ -1,4 +1,6 @@
"""Test different accessory types: Sensors."""
from unittest.mock import patch
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
from homeassistant.components.homekit import get_accessory
from homeassistant.components.homekit.const import (
@ -71,11 +73,13 @@ async def test_temperature(hass: HomeAssistant, hk_driver) -> None:
await hass.async_block_till_done()
assert acc.char_temp.value == 0
hass.states.async_set(
entity_id, "75.2", {ATTR_UNIT_OF_MEASUREMENT: UnitOfTemperature.FAHRENHEIT}
)
await hass.async_block_till_done()
assert acc.char_temp.value == 24
# The UOM changes, the accessory should reload itself
with patch.object(acc, "async_reload") as mock_reload:
hass.states.async_set(
entity_id, "75.2", {ATTR_UNIT_OF_MEASUREMENT: UnitOfTemperature.FAHRENHEIT}
)
await hass.async_block_till_done()
assert mock_reload.called
async def test_humidity(hass: HomeAssistant, hk_driver) -> None:

View file

@ -79,21 +79,22 @@ from tests.common import async_mock_service
async def test_thermostat(hass: HomeAssistant, hk_driver, events) -> None:
"""Test if accessory and HA are updated accordingly."""
entity_id = "climate.test"
base_attrs = {
ATTR_SUPPORTED_FEATURES: ClimateEntityFeature.TARGET_TEMPERATURE,
ATTR_HVAC_MODES: [
HVACMode.HEAT,
HVACMode.HEAT_COOL,
HVACMode.FAN_ONLY,
HVACMode.COOL,
HVACMode.OFF,
HVACMode.AUTO,
],
}
hass.states.async_set(
entity_id,
HVACMode.OFF,
{
ATTR_SUPPORTED_FEATURES: ClimateEntityFeature.TARGET_TEMPERATURE,
ATTR_HVAC_MODES: [
HVACMode.HEAT,
HVACMode.HEAT_COOL,
HVACMode.FAN_ONLY,
HVACMode.COOL,
HVACMode.OFF,
HVACMode.AUTO,
],
},
base_attrs,
)
await hass.async_block_till_done()
acc = Thermostat(hass, hk_driver, "Climate", entity_id, 1, None)
@ -124,17 +125,10 @@ async def test_thermostat(hass: HomeAssistant, hk_driver, events) -> None:
entity_id,
HVACMode.HEAT,
{
**base_attrs,
ATTR_TEMPERATURE: 22.2,
ATTR_CURRENT_TEMPERATURE: 17.8,
ATTR_HVAC_ACTION: HVACAction.HEATING,
ATTR_HVAC_MODES: [
HVACMode.HEAT,
HVACMode.HEAT_COOL,
HVACMode.FAN_ONLY,
HVACMode.COOL,
HVACMode.OFF,
HVACMode.AUTO,
],
},
)
await hass.async_block_till_done()
@ -148,17 +142,10 @@ async def test_thermostat(hass: HomeAssistant, hk_driver, events) -> None:
entity_id,
HVACMode.HEAT,
{
**base_attrs,
ATTR_TEMPERATURE: 22.0,
ATTR_CURRENT_TEMPERATURE: 23.0,
ATTR_HVAC_ACTION: HVACAction.IDLE,
ATTR_HVAC_MODES: [
HVACMode.HEAT,
HVACMode.HEAT_COOL,
HVACMode.FAN_ONLY,
HVACMode.COOL,
HVACMode.OFF,
HVACMode.AUTO,
],
},
)
await hass.async_block_till_done()
@ -172,17 +159,10 @@ async def test_thermostat(hass: HomeAssistant, hk_driver, events) -> None:
entity_id,
HVACMode.FAN_ONLY,
{
**base_attrs,
ATTR_TEMPERATURE: 20.0,
ATTR_CURRENT_TEMPERATURE: 25.0,
ATTR_HVAC_ACTION: HVACAction.COOLING,
ATTR_HVAC_MODES: [
HVACMode.HEAT,
HVACMode.HEAT_COOL,
HVACMode.FAN_ONLY,
HVACMode.COOL,
HVACMode.OFF,
HVACMode.AUTO,
],
},
)
await hass.async_block_till_done()
@ -196,6 +176,7 @@ async def test_thermostat(hass: HomeAssistant, hk_driver, events) -> None:
entity_id,
HVACMode.COOL,
{
**base_attrs,
ATTR_TEMPERATURE: 20.0,
ATTR_CURRENT_TEMPERATURE: 19.0,
ATTR_HVAC_ACTION: HVACAction.IDLE,
@ -211,7 +192,7 @@ async def test_thermostat(hass: HomeAssistant, hk_driver, events) -> None:
hass.states.async_set(
entity_id,
HVACMode.OFF,
{ATTR_TEMPERATURE: 22.0, ATTR_CURRENT_TEMPERATURE: 18.0},
{**base_attrs, ATTR_TEMPERATURE: 22.0, ATTR_CURRENT_TEMPERATURE: 18.0},
)
await hass.async_block_till_done()
assert acc.char_target_temp.value == 22.0
@ -224,17 +205,10 @@ async def test_thermostat(hass: HomeAssistant, hk_driver, events) -> None:
entity_id,
HVACMode.AUTO,
{
**base_attrs,
ATTR_TEMPERATURE: 22.0,
ATTR_CURRENT_TEMPERATURE: 18.0,
ATTR_HVAC_ACTION: HVACAction.HEATING,
ATTR_HVAC_MODES: [
HVACMode.HEAT,
HVACMode.HEAT_COOL,
HVACMode.FAN_ONLY,
HVACMode.COOL,
HVACMode.OFF,
HVACMode.AUTO,
],
},
)
await hass.async_block_till_done()
@ -248,17 +222,10 @@ async def test_thermostat(hass: HomeAssistant, hk_driver, events) -> None:
entity_id,
HVACMode.HEAT_COOL,
{
**base_attrs,
ATTR_TEMPERATURE: 22.0,
ATTR_CURRENT_TEMPERATURE: 25.0,
ATTR_HVAC_ACTION: HVACAction.COOLING,
ATTR_HVAC_MODES: [
HVACMode.HEAT,
HVACMode.HEAT_COOL,
HVACMode.FAN_ONLY,
HVACMode.COOL,
HVACMode.OFF,
HVACMode.AUTO,
],
},
)
await hass.async_block_till_done()
@ -272,17 +239,10 @@ async def test_thermostat(hass: HomeAssistant, hk_driver, events) -> None:
entity_id,
HVACMode.AUTO,
{
**base_attrs,
ATTR_TEMPERATURE: 22.0,
ATTR_CURRENT_TEMPERATURE: 22.0,
ATTR_HVAC_ACTION: HVACAction.IDLE,
ATTR_HVAC_MODES: [
HVACMode.HEAT,
HVACMode.HEAT_COOL,
HVACMode.FAN_ONLY,
HVACMode.COOL,
HVACMode.OFF,
HVACMode.AUTO,
],
},
)
await hass.async_block_till_done()
@ -296,17 +256,10 @@ async def test_thermostat(hass: HomeAssistant, hk_driver, events) -> None:
entity_id,
HVACMode.FAN_ONLY,
{
**base_attrs,
ATTR_TEMPERATURE: 22.0,
ATTR_CURRENT_TEMPERATURE: 22.0,
ATTR_HVAC_ACTION: HVACAction.FAN,
ATTR_HVAC_MODES: [
HVACMode.HEAT,
HVACMode.HEAT_COOL,
HVACMode.FAN_ONLY,
HVACMode.COOL,
HVACMode.OFF,
HVACMode.AUTO,
],
},
)
await hass.async_block_till_done()
@ -320,7 +273,7 @@ async def test_thermostat(hass: HomeAssistant, hk_driver, events) -> None:
entity_id,
HVACMode.DRY,
{
ATTR_SUPPORTED_FEATURES: ClimateEntityFeature.TARGET_TEMPERATURE,
**base_attrs,
ATTR_TEMPERATURE: 22.0,
ATTR_CURRENT_TEMPERATURE: 22.0,
ATTR_HVAC_ACTION: HVACAction.DRYING,
@ -419,23 +372,23 @@ async def test_thermostat(hass: HomeAssistant, hk_driver, events) -> None:
async def test_thermostat_auto(hass: HomeAssistant, hk_driver, events) -> None:
"""Test if accessory and HA are updated accordingly."""
entity_id = "climate.test"
base_attrs = {
ATTR_SUPPORTED_FEATURES: ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
ATTR_HVAC_MODES: [
HVACMode.HEAT,
HVACMode.HEAT_COOL,
HVACMode.FAN_ONLY,
HVACMode.COOL,
HVACMode.OFF,
HVACMode.AUTO,
],
}
# support_auto = True
hass.states.async_set(
entity_id,
HVACMode.OFF,
{
ATTR_SUPPORTED_FEATURES: ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
ATTR_HVAC_MODES: [
HVACMode.HEAT,
HVACMode.HEAT_COOL,
HVACMode.FAN_ONLY,
HVACMode.COOL,
HVACMode.OFF,
HVACMode.AUTO,
],
},
base_attrs,
)
await hass.async_block_till_done()
acc = Thermostat(hass, hk_driver, "Climate", entity_id, 1, None)
@ -458,18 +411,11 @@ async def test_thermostat_auto(hass: HomeAssistant, hk_driver, events) -> None:
entity_id,
HVACMode.HEAT_COOL,
{
**base_attrs,
ATTR_TARGET_TEMP_HIGH: 22.0,
ATTR_TARGET_TEMP_LOW: 20.0,
ATTR_CURRENT_TEMPERATURE: 18.0,
ATTR_HVAC_ACTION: HVACAction.HEATING,
ATTR_HVAC_MODES: [
HVACMode.HEAT,
HVACMode.HEAT_COOL,
HVACMode.FAN_ONLY,
HVACMode.COOL,
HVACMode.OFF,
HVACMode.AUTO,
],
},
)
await hass.async_block_till_done()
@ -484,18 +430,11 @@ async def test_thermostat_auto(hass: HomeAssistant, hk_driver, events) -> None:
entity_id,
HVACMode.COOL,
{
**base_attrs,
ATTR_TARGET_TEMP_HIGH: 23.0,
ATTR_TARGET_TEMP_LOW: 19.0,
ATTR_CURRENT_TEMPERATURE: 24.0,
ATTR_HVAC_ACTION: HVACAction.COOLING,
ATTR_HVAC_MODES: [
HVACMode.HEAT,
HVACMode.HEAT_COOL,
HVACMode.FAN_ONLY,
HVACMode.COOL,
HVACMode.OFF,
HVACMode.AUTO,
],
},
)
await hass.async_block_till_done()
@ -510,18 +449,11 @@ async def test_thermostat_auto(hass: HomeAssistant, hk_driver, events) -> None:
entity_id,
HVACMode.AUTO,
{
**base_attrs,
ATTR_TARGET_TEMP_HIGH: 23.0,
ATTR_TARGET_TEMP_LOW: 19.0,
ATTR_CURRENT_TEMPERATURE: 21.0,
ATTR_HVAC_ACTION: HVACAction.IDLE,
ATTR_HVAC_MODES: [
HVACMode.HEAT,
HVACMode.HEAT_COOL,
HVACMode.FAN_ONLY,
HVACMode.COOL,
HVACMode.OFF,
HVACMode.AUTO,
],
},
)
await hass.async_block_till_done()
@ -575,23 +507,23 @@ async def test_thermostat_mode_and_temp_change(
) -> None:
"""Test if accessory where the mode and temp change in the same call."""
entity_id = "climate.test"
base_attrs = {
ATTR_SUPPORTED_FEATURES: ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
ATTR_HVAC_MODES: [
HVACMode.HEAT,
HVACMode.HEAT_COOL,
HVACMode.FAN_ONLY,
HVACMode.COOL,
HVACMode.OFF,
HVACMode.AUTO,
],
}
# support_auto = True
hass.states.async_set(
entity_id,
HVACMode.OFF,
{
ATTR_SUPPORTED_FEATURES: ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
ATTR_HVAC_MODES: [
HVACMode.HEAT,
HVACMode.HEAT_COOL,
HVACMode.FAN_ONLY,
HVACMode.COOL,
HVACMode.OFF,
HVACMode.AUTO,
],
},
base_attrs,
)
await hass.async_block_till_done()
acc = Thermostat(hass, hk_driver, "Climate", entity_id, 1, None)
@ -614,18 +546,11 @@ async def test_thermostat_mode_and_temp_change(
entity_id,
HVACMode.COOL,
{
**base_attrs,
ATTR_TARGET_TEMP_HIGH: 23.0,
ATTR_TARGET_TEMP_LOW: 19.0,
ATTR_CURRENT_TEMPERATURE: 21.0,
ATTR_HVAC_ACTION: HVACAction.COOLING,
ATTR_HVAC_MODES: [
HVACMode.HEAT,
HVACMode.HEAT_COOL,
HVACMode.FAN_ONLY,
HVACMode.COOL,
HVACMode.OFF,
HVACMode.AUTO,
],
},
)
await hass.async_block_till_done()
@ -688,9 +613,9 @@ async def test_thermostat_mode_and_temp_change(
async def test_thermostat_humidity(hass: HomeAssistant, hk_driver, events) -> None:
"""Test if accessory and HA are updated accordingly with humidity."""
entity_id = "climate.test"
base_attrs = {ATTR_SUPPORTED_FEATURES: 4}
# support_auto = True
hass.states.async_set(entity_id, HVACMode.OFF, {ATTR_SUPPORTED_FEATURES: 4})
hass.states.async_set(entity_id, HVACMode.OFF, base_attrs)
await hass.async_block_till_done()
acc = Thermostat(hass, hk_driver, "Climate", entity_id, 1, None)
hk_driver.add_accessory(acc)
@ -704,14 +629,18 @@ async def test_thermostat_humidity(hass: HomeAssistant, hk_driver, events) -> No
assert acc.char_target_humidity.properties[PROP_MIN_VALUE] == DEFAULT_MIN_HUMIDITY
hass.states.async_set(
entity_id, HVACMode.HEAT_COOL, {ATTR_HUMIDITY: 65, ATTR_CURRENT_HUMIDITY: 40}
entity_id,
HVACMode.HEAT_COOL,
{**base_attrs, ATTR_HUMIDITY: 65, ATTR_CURRENT_HUMIDITY: 40},
)
await hass.async_block_till_done()
assert acc.char_current_humidity.value == 40
assert acc.char_target_humidity.value == 65
hass.states.async_set(
entity_id, HVACMode.COOL, {ATTR_HUMIDITY: 35, ATTR_CURRENT_HUMIDITY: 70}
entity_id,
HVACMode.COOL,
{**base_attrs, ATTR_HUMIDITY: 35, ATTR_CURRENT_HUMIDITY: 70},
)
await hass.async_block_till_done()
assert acc.char_current_humidity.value == 70
@ -772,24 +701,24 @@ async def test_thermostat_humidity_with_target_humidity(
async def test_thermostat_power_state(hass: HomeAssistant, hk_driver, events) -> None:
"""Test if accessory and HA are updated accordingly."""
entity_id = "climate.test"
base_attrs = {
ATTR_SUPPORTED_FEATURES: 4096,
ATTR_TEMPERATURE: 23.0,
ATTR_CURRENT_TEMPERATURE: 18.0,
ATTR_HVAC_ACTION: HVACAction.HEATING,
ATTR_HVAC_MODES: [
HVACMode.HEAT_COOL,
HVACMode.COOL,
HVACMode.AUTO,
HVACMode.HEAT,
HVACMode.OFF,
],
}
# SUPPORT_ON_OFF = True
hass.states.async_set(
entity_id,
HVACMode.HEAT,
{
ATTR_SUPPORTED_FEATURES: 4096,
ATTR_TEMPERATURE: 23.0,
ATTR_CURRENT_TEMPERATURE: 18.0,
ATTR_HVAC_ACTION: HVACAction.HEATING,
ATTR_HVAC_MODES: [
HVACMode.HEAT_COOL,
HVACMode.COOL,
HVACMode.AUTO,
HVACMode.HEAT,
HVACMode.OFF,
],
},
base_attrs,
)
await hass.async_block_till_done()
acc = Thermostat(hass, hk_driver, "Climate", entity_id, 1, None)
@ -805,16 +734,10 @@ async def test_thermostat_power_state(hass: HomeAssistant, hk_driver, events) ->
entity_id,
HVACMode.OFF,
{
**base_attrs,
ATTR_TEMPERATURE: 23.0,
ATTR_CURRENT_TEMPERATURE: 18.0,
ATTR_HVAC_ACTION: HVACAction.IDLE,
ATTR_HVAC_MODES: [
HVACMode.HEAT_COOL,
HVACMode.COOL,
HVACMode.AUTO,
HVACMode.HEAT,
HVACMode.OFF,
],
},
)
await hass.async_block_till_done()
@ -825,16 +748,10 @@ async def test_thermostat_power_state(hass: HomeAssistant, hk_driver, events) ->
entity_id,
HVACMode.OFF,
{
**base_attrs,
ATTR_TEMPERATURE: 23.0,
ATTR_CURRENT_TEMPERATURE: 18.0,
ATTR_HVAC_ACTION: HVACAction.IDLE,
ATTR_HVAC_MODES: [
HVACMode.HEAT_COOL,
HVACMode.COOL,
HVACMode.AUTO,
HVACMode.HEAT,
HVACMode.OFF,
],
},
)
await hass.async_block_till_done()
@ -1566,12 +1483,15 @@ async def test_thermostat_without_target_temp_only_range(
) -> None:
"""Test a thermostat that only supports a range."""
entity_id = "climate.test"
base_attrs = {
ATTR_SUPPORTED_FEATURES: ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
}
# support_auto = True
hass.states.async_set(
entity_id,
HVACMode.OFF,
{ATTR_SUPPORTED_FEATURES: ClimateEntityFeature.TARGET_TEMPERATURE_RANGE},
base_attrs,
)
await hass.async_block_till_done()
acc = Thermostat(hass, hk_driver, "Climate", entity_id, 1, None)
@ -1594,19 +1514,11 @@ async def test_thermostat_without_target_temp_only_range(
entity_id,
HVACMode.HEAT_COOL,
{
**base_attrs,
ATTR_TARGET_TEMP_HIGH: 22.0,
ATTR_TARGET_TEMP_LOW: 20.0,
ATTR_CURRENT_TEMPERATURE: 18.0,
ATTR_HVAC_ACTION: HVACAction.HEATING,
ATTR_SUPPORTED_FEATURES: ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
ATTR_HVAC_MODES: [
HVACMode.HEAT,
HVACMode.HEAT_COOL,
HVACMode.FAN_ONLY,
HVACMode.COOL,
HVACMode.OFF,
HVACMode.AUTO,
],
},
)
await hass.async_block_till_done()
@ -1621,19 +1533,11 @@ async def test_thermostat_without_target_temp_only_range(
entity_id,
HVACMode.COOL,
{
**base_attrs,
ATTR_TARGET_TEMP_HIGH: 23.0,
ATTR_TARGET_TEMP_LOW: 19.0,
ATTR_CURRENT_TEMPERATURE: 24.0,
ATTR_HVAC_ACTION: HVACAction.COOLING,
ATTR_SUPPORTED_FEATURES: ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
ATTR_HVAC_MODES: [
HVACMode.HEAT,
HVACMode.HEAT_COOL,
HVACMode.FAN_ONLY,
HVACMode.COOL,
HVACMode.OFF,
HVACMode.AUTO,
],
},
)
await hass.async_block_till_done()
@ -1648,19 +1552,11 @@ async def test_thermostat_without_target_temp_only_range(
entity_id,
HVACMode.COOL,
{
**base_attrs,
ATTR_TARGET_TEMP_HIGH: 23.0,
ATTR_TARGET_TEMP_LOW: 19.0,
ATTR_CURRENT_TEMPERATURE: 21.0,
ATTR_HVAC_ACTION: HVACAction.IDLE,
ATTR_SUPPORTED_FEATURES: ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
ATTR_HVAC_MODES: [
HVACMode.HEAT,
HVACMode.HEAT_COOL,
HVACMode.FAN_ONLY,
HVACMode.COOL,
HVACMode.OFF,
HVACMode.AUTO,
],
},
)
await hass.async_block_till_done()
@ -1925,16 +1821,17 @@ async def test_thermostat_with_no_modes_when_we_first_see(
) -> None:
"""Test if a thermostat that is not ready when we first see it."""
entity_id = "climate.test"
base_attrs = {
ATTR_SUPPORTED_FEATURES: ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
ATTR_HVAC_MODES: [],
}
# support_auto = True
hass.states.async_set(
entity_id,
HVACMode.OFF,
{
ATTR_SUPPORTED_FEATURES: ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
ATTR_HVAC_MODES: [],
},
base_attrs,
)
await hass.async_block_till_done()
acc = Thermostat(hass, hk_driver, "Climate", entity_id, 1, None)
@ -1955,24 +1852,22 @@ async def test_thermostat_with_no_modes_when_we_first_see(
assert acc.char_target_heat_cool.value == 0
hass.states.async_set(
entity_id,
HVACMode.HEAT_COOL,
{
ATTR_TARGET_TEMP_HIGH: 22.0,
ATTR_TARGET_TEMP_LOW: 20.0,
ATTR_CURRENT_TEMPERATURE: 18.0,
ATTR_HVAC_ACTION: HVACAction.HEATING,
ATTR_HVAC_MODES: [HVACMode.HEAT_COOL, HVACMode.OFF, HVACMode.AUTO],
},
)
await hass.async_block_till_done()
assert acc.char_heating_thresh_temp.value == 20.0
assert acc.char_cooling_thresh_temp.value == 22.0
assert acc.char_current_heat_cool.value == 1
assert acc.char_target_heat_cool.value == 3
assert acc.char_current_temp.value == 18.0
assert acc.char_display_units.value == 0
# Verify reload on modes changed out from under us
with patch.object(acc, "async_reload") as mock_reload:
hass.states.async_set(
entity_id,
HVACMode.HEAT_COOL,
{
**base_attrs,
ATTR_TARGET_TEMP_HIGH: 22.0,
ATTR_TARGET_TEMP_LOW: 20.0,
ATTR_CURRENT_TEMPERATURE: 18.0,
ATTR_HVAC_ACTION: HVACAction.HEATING,
ATTR_HVAC_MODES: [HVACMode.HEAT_COOL, HVACMode.OFF, HVACMode.AUTO],
},
)
await hass.async_block_till_done()
assert mock_reload.called
async def test_thermostat_with_no_off_after_recheck(
@ -1981,15 +1876,16 @@ async def test_thermostat_with_no_off_after_recheck(
"""Test if a thermostat that is not ready when we first see it that actually does not have off."""
entity_id = "climate.test"
base_attrs = {
ATTR_SUPPORTED_FEATURES: ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
ATTR_HVAC_MODES: [],
}
# support_auto = True
hass.states.async_set(
entity_id,
HVACMode.COOL,
{
ATTR_SUPPORTED_FEATURES: ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
ATTR_HVAC_MODES: [],
},
base_attrs,
)
await hass.async_block_till_done()
acc = Thermostat(hass, hk_driver, "Climate", entity_id, 1, None)
@ -2010,24 +1906,22 @@ async def test_thermostat_with_no_off_after_recheck(
assert acc.char_target_heat_cool.value == 2
hass.states.async_set(
entity_id,
HVACMode.HEAT_COOL,
{
ATTR_TARGET_TEMP_HIGH: 22.0,
ATTR_TARGET_TEMP_LOW: 20.0,
ATTR_CURRENT_TEMPERATURE: 18.0,
ATTR_HVAC_ACTION: HVACAction.HEATING,
ATTR_HVAC_MODES: [HVACMode.HEAT_COOL, HVACMode.AUTO],
},
)
await hass.async_block_till_done()
assert acc.char_heating_thresh_temp.value == 20.0
assert acc.char_cooling_thresh_temp.value == 22.0
assert acc.char_current_heat_cool.value == 1
assert acc.char_target_heat_cool.value == 3
assert acc.char_current_temp.value == 18.0
assert acc.char_display_units.value == 0
# Verify reload when modes change out from under us
with patch.object(acc, "async_reload") as mock_reload:
hass.states.async_set(
entity_id,
HVACMode.HEAT_COOL,
{
**base_attrs,
ATTR_TARGET_TEMP_HIGH: 22.0,
ATTR_TARGET_TEMP_LOW: 20.0,
ATTR_CURRENT_TEMPERATURE: 18.0,
ATTR_HVAC_ACTION: HVACAction.HEATING,
ATTR_HVAC_MODES: [HVACMode.HEAT_COOL, HVACMode.AUTO],
},
)
await hass.async_block_till_done()
assert mock_reload.called
async def test_thermostat_with_temp_clamps(
@ -2035,17 +1929,17 @@ async def test_thermostat_with_temp_clamps(
) -> None:
"""Test that tempatures are clamped to valid values to prevent homekit crash."""
entity_id = "climate.test"
base_attrs = {
ATTR_SUPPORTED_FEATURES: ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
ATTR_HVAC_MODES: [HVACMode.HEAT_COOL, HVACMode.AUTO],
ATTR_MAX_TEMP: 50,
ATTR_MIN_TEMP: 100,
}
hass.states.async_set(
entity_id,
HVACMode.COOL,
{
ATTR_SUPPORTED_FEATURES: ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
ATTR_HVAC_MODES: [],
ATTR_MAX_TEMP: 50,
ATTR_MIN_TEMP: 100,
},
base_attrs,
)
await hass.async_block_till_done()
acc = Thermostat(hass, hk_driver, "Climate", entity_id, 1, None)
@ -2064,17 +1958,17 @@ async def test_thermostat_with_temp_clamps(
assert acc.char_heating_thresh_temp.properties[PROP_MIN_VALUE] == 100
assert acc.char_heating_thresh_temp.properties[PROP_MIN_STEP] == 0.1
assert acc.char_target_heat_cool.value == 2
assert acc.char_target_heat_cool.value == 3
hass.states.async_set(
entity_id,
HVACMode.HEAT_COOL,
{
**base_attrs,
ATTR_TARGET_TEMP_HIGH: 822.0,
ATTR_TARGET_TEMP_LOW: 20.0,
ATTR_CURRENT_TEMPERATURE: 9918.0,
ATTR_HVAC_ACTION: HVACAction.HEATING,
ATTR_HVAC_MODES: [HVACMode.HEAT_COOL, HVACMode.AUTO],
},
)
await hass.async_block_till_done()

View file

@ -71,3 +71,4 @@ async def test_programmable_switch_button_fires_on_trigger(
char = acc.get_characteristic(call.args[0]["aid"], call.args[0]["iid"])
assert char.display_name == CHAR_PROGRAMMABLE_SWITCH_EVENT
await acc.stop()
await hass.async_block_till_done()