Resolve homekit cover adjustment slowness (#45730)

This commit is contained in:
J. Nick Koston 2021-01-31 10:39:35 -10:00 committed by GitHub
parent 8be357ff4f
commit dac9626112
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 72 additions and 220 deletions

View file

@ -1,7 +1,4 @@
"""Extend the basic Accessory and Bridge functions."""
from datetime import timedelta
from functools import partial, wraps
from inspect import getmodule
import logging
from pyhap.accessory import Accessory, Bridge
@ -37,11 +34,7 @@ from homeassistant.const import (
__version__,
)
from homeassistant.core import Context, callback as ha_callback, split_entity_id
from homeassistant.helpers.event import (
async_track_state_change_event,
track_point_in_utc_time,
)
from homeassistant.util import dt as dt_util
from homeassistant.helpers.event import async_track_state_change_event
from homeassistant.util.decorator import Registry
from .const import (
@ -60,7 +53,6 @@ from .const import (
CONF_LINKED_BATTERY_CHARGING_SENSOR,
CONF_LINKED_BATTERY_SENSOR,
CONF_LOW_BATTERY_THRESHOLD,
DEBOUNCE_TIMEOUT,
DEFAULT_LOW_BATTERY_THRESHOLD,
DEVICE_CLASS_CO,
DEVICE_CLASS_CO2,
@ -98,37 +90,6 @@ SWITCH_TYPES = {
TYPES = Registry()
def debounce(func):
"""Decorate function to debounce callbacks from HomeKit."""
@ha_callback
def call_later_listener(self, *args):
"""Handle call_later callback."""
debounce_params = self.debounce.pop(func.__name__, None)
if debounce_params:
self.hass.async_add_executor_job(func, self, *debounce_params[1:])
@wraps(func)
def wrapper(self, *args):
"""Start async timer."""
debounce_params = self.debounce.pop(func.__name__, None)
if debounce_params:
debounce_params[0]() # remove listener
remove_listener = track_point_in_utc_time(
self.hass,
partial(call_later_listener, self),
dt_util.utcnow() + timedelta(seconds=DEBOUNCE_TIMEOUT),
)
self.debounce[func.__name__] = (remove_listener, *args)
logger.debug(
"%s: Start %s timeout", self.entity_id, func.__name__.replace("set_", "")
)
name = getmodule(func).__name__
logger = logging.getLogger(name)
return wrapper
def get_accessory(hass, driver, state, aid, config):
"""Take state and return an accessory object if supported."""
if not aid:
@ -278,7 +239,6 @@ class HomeAccessory(Accessory):
self.category = category
self.entity_id = entity_id
self.hass = hass
self.debounce = {}
self._subscriptions = []
self._char_battery = None
self._char_charging = None

View file

@ -33,7 +33,7 @@ from homeassistant.const import (
from homeassistant.core import callback
from homeassistant.helpers.event import async_track_state_change_event
from .accessories import TYPES, HomeAccessory, debounce
from .accessories import TYPES, HomeAccessory
from .const import (
ATTR_OBSTRUCTION_DETECTED,
CHAR_CURRENT_DOOR_STATE,
@ -233,7 +233,6 @@ class OpeningDeviceBase(HomeAccessory):
return
self.call_service(DOMAIN, SERVICE_STOP_COVER, {ATTR_ENTITY_ID: self.entity_id})
@debounce
def set_tilt(self, value):
"""Set tilt to value if call came from HomeKit."""
_LOGGER.info("%s: Set tilt to %d", self.entity_id, value)
@ -284,7 +283,6 @@ class OpeningDevice(OpeningDeviceBase, HomeAccessory):
)
self.async_update_state(state)
@debounce
def move_cover(self, value):
"""Move cover to value if call came from HomeKit."""
_LOGGER.debug("%s: Set position to %d", self.entity_id, value)
@ -360,7 +358,6 @@ class WindowCoveringBasic(OpeningDeviceBase, HomeAccessory):
)
self.async_update_state(state)
@debounce
def move_cover(self, value):
"""Move cover to value if call came from HomeKit."""
_LOGGER.debug("%s: Set position to %d", self.entity_id, value)

View file

@ -1,17 +1,9 @@
"""Collection of fixtures and functions for the HomeKit tests."""
from unittest.mock import Mock, patch
from unittest.mock import Mock
EMPTY_8_6_JPEG = b"empty_8_6"
def patch_debounce():
"""Return patch for debounce method."""
return patch(
"homeassistant.components.homekit.accessories.debounce",
lambda f: lambda *args, **kwargs: f(*args, **kwargs),
)
def mock_turbo_jpeg(
first_width=None, second_width=None, first_height=None, second_height=None
):

View file

@ -2,7 +2,6 @@
This includes tests for all mock object types.
"""
from datetime import timedelta
from unittest.mock import Mock, patch
import pytest
@ -11,7 +10,6 @@ from homeassistant.components.homekit.accessories import (
HomeAccessory,
HomeBridge,
HomeDriver,
debounce,
)
from homeassistant.components.homekit.const import (
ATTR_DISPLAY_NAME,
@ -45,41 +43,8 @@ from homeassistant.const import (
__version__,
)
from homeassistant.helpers.event import TRACK_STATE_CHANGE_CALLBACKS
import homeassistant.util.dt as dt_util
from tests.common import async_fire_time_changed, async_mock_service
async def test_debounce(hass):
"""Test add_timeout decorator function."""
def demo_func(*args):
nonlocal arguments, counter
counter += 1
arguments = args
arguments = None
counter = 0
mock = Mock(hass=hass, debounce={})
debounce_demo = debounce(demo_func)
assert debounce_demo.__name__ == "demo_func"
now = dt_util.utcnow()
with patch("homeassistant.util.dt.utcnow", return_value=now):
await hass.async_add_executor_job(debounce_demo, mock, "value")
async_fire_time_changed(hass, now + timedelta(seconds=3))
await hass.async_block_till_done()
assert counter == 1
assert len(arguments) == 2
with patch("homeassistant.util.dt.utcnow", return_value=now):
await hass.async_add_executor_job(debounce_demo, mock, "value")
await hass.async_add_executor_job(debounce_demo, mock, "value")
async_fire_time_changed(hass, now + timedelta(seconds=3))
await hass.async_block_till_done()
assert counter == 2
from tests.common import async_mock_service
async def test_accessory_cancels_track_state_change_on_stop(hass, hk_driver):

View file

@ -67,7 +67,6 @@ from homeassistant.util import json as json_util
from .util import PATH_HOMEKIT, async_init_entry, async_init_integration
from tests.common import MockConfigEntry, mock_device_registry, mock_registry
from tests.components.homekit.common import patch_debounce
IP_ADDRESS = "127.0.0.1"
@ -89,14 +88,6 @@ def entity_reg_fixture(hass):
return mock_registry(hass)
@pytest.fixture(name="debounce_patcher", scope="module")
def debounce_patcher_fixture():
"""Patch debounce method."""
patcher = patch_debounce()
yield patcher.start()
patcher.stop()
async def test_setup_min(hass, mock_zeroconf):
"""Test async_setup with min config options."""
entry = MockConfigEntry(
@ -485,7 +476,7 @@ async def test_homekit_entity_glob_filter(hass, mock_zeroconf):
mock_get_acc.reset_mock()
async def test_homekit_start(hass, hk_driver, device_reg, debounce_patcher):
async def test_homekit_start(hass, hk_driver, device_reg):
"""Test HomeKit start method."""
entry = await async_init_integration(hass)
@ -573,9 +564,7 @@ async def test_homekit_start(hass, hk_driver, device_reg, debounce_patcher):
assert len(device_reg.devices) == 1
async def test_homekit_start_with_a_broken_accessory(
hass, hk_driver, debounce_patcher, mock_zeroconf
):
async def test_homekit_start_with_a_broken_accessory(hass, hk_driver, mock_zeroconf):
"""Test HomeKit start method."""
pin = b"123-45-678"
entry = MockConfigEntry(
@ -754,7 +743,7 @@ async def test_homekit_too_many_accessories(hass, hk_driver, caplog, mock_zeroco
async def test_homekit_finds_linked_batteries(
hass, hk_driver, debounce_patcher, device_reg, entity_reg, mock_zeroconf
hass, hk_driver, device_reg, entity_reg, mock_zeroconf
):
"""Test HomeKit start method."""
entry = await async_init_integration(hass)
@ -840,7 +829,7 @@ async def test_homekit_finds_linked_batteries(
async def test_homekit_async_get_integration_fails(
hass, hk_driver, debounce_patcher, device_reg, entity_reg, mock_zeroconf
hass, hk_driver, device_reg, entity_reg, mock_zeroconf
):
"""Test that we continue if async_get_integration fails."""
entry = await async_init_integration(hass)
@ -1072,7 +1061,7 @@ def _write_data(path: str, data: Dict) -> None:
async def test_homekit_ignored_missing_devices(
hass, hk_driver, debounce_patcher, device_reg, entity_reg, mock_zeroconf
hass, hk_driver, device_reg, entity_reg, mock_zeroconf
):
"""Test HomeKit handles a device in the entity registry but missing from the device registry."""
entry = await async_init_integration(hass)
@ -1153,7 +1142,7 @@ async def test_homekit_ignored_missing_devices(
async def test_homekit_finds_linked_motion_sensors(
hass, hk_driver, debounce_patcher, device_reg, entity_reg, mock_zeroconf
hass, hk_driver, device_reg, entity_reg, mock_zeroconf
):
"""Test HomeKit start method."""
entry = await async_init_integration(hass)
@ -1228,7 +1217,7 @@ async def test_homekit_finds_linked_motion_sensors(
async def test_homekit_finds_linked_humidity_sensors(
hass, hk_driver, debounce_patcher, device_reg, entity_reg, mock_zeroconf
hass, hk_driver, device_reg, entity_reg, mock_zeroconf
):
"""Test HomeKit start method."""
entry = await async_init_integration(hass)
@ -1376,9 +1365,7 @@ def _get_fixtures_base_path():
return os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
async def test_homekit_start_in_accessory_mode(
hass, hk_driver, device_reg, debounce_patcher
):
async def test_homekit_start_in_accessory_mode(hass, hk_driver, device_reg):
"""Test HomeKit start method in accessory mode."""
entry = await async_init_integration(hass)

View file

@ -1,7 +1,4 @@
"""Test different accessory types: Covers."""
from collections import namedtuple
import pytest
from homeassistant.components.cover import (
ATTR_CURRENT_POSITION,
@ -22,6 +19,12 @@ from homeassistant.components.homekit.const import (
HK_DOOR_OPEN,
HK_DOOR_OPENING,
)
from homeassistant.components.homekit.type_covers import (
GarageDoorOpener,
Window,
WindowCovering,
WindowCoveringBasic,
)
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_SUPPORTED_FEATURES,
@ -40,37 +43,15 @@ 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
@pytest.fixture(scope="module")
def cls():
"""Patch debounce decorator during import of type_covers."""
patcher = patch_debounce()
patcher.start()
_import = __import__(
"homeassistant.components.homekit.type_covers",
fromlist=["GarageDoorOpener", "WindowCovering", "WindowCoveringBasic"],
)
patcher_tuple = namedtuple(
"Cls", ["window", "windowcovering", "windowcovering_basic", "garage"]
)
yield patcher_tuple(
window=_import.Window,
windowcovering=_import.WindowCovering,
windowcovering_basic=_import.WindowCoveringBasic,
garage=_import.GarageDoorOpener,
)
patcher.stop()
async def test_garage_door_open_close(hass, hk_driver, cls, events):
async def test_garage_door_open_close(hass, hk_driver, events):
"""Test if accessory and HA are updated accordingly."""
entity_id = "cover.garage_door"
hass.states.async_set(entity_id, None)
await hass.async_block_till_done()
acc = cls.garage(hass, hk_driver, "Garage Door", entity_id, 2, None)
acc = GarageDoorOpener(hass, hk_driver, "Garage Door", entity_id, 2, None)
await acc.run_handler()
await hass.async_block_till_done()
@ -148,13 +129,13 @@ async def test_garage_door_open_close(hass, hk_driver, cls, events):
assert events[-1].data[ATTR_VALUE] is None
async def test_windowcovering_set_cover_position(hass, hk_driver, cls, events):
async def test_windowcovering_set_cover_position(hass, hk_driver, events):
"""Test if accessory and HA are updated accordingly."""
entity_id = "cover.window"
hass.states.async_set(entity_id, None)
await hass.async_block_till_done()
acc = cls.windowcovering(hass, hk_driver, "Cover", entity_id, 2, None)
acc = WindowCovering(hass, hk_driver, "Cover", entity_id, 2, None)
await acc.run_handler()
await hass.async_block_till_done()
@ -218,13 +199,13 @@ async def test_windowcovering_set_cover_position(hass, hk_driver, cls, events):
assert events[-1].data[ATTR_VALUE] == 75
async def test_window_instantiate(hass, hk_driver, cls, events):
async def test_window_instantiate(hass, hk_driver, events):
"""Test if Window accessory is instantiated correctly."""
entity_id = "cover.window"
hass.states.async_set(entity_id, None)
await hass.async_block_till_done()
acc = cls.window(hass, hk_driver, "Window", entity_id, 2, None)
acc = Window(hass, hk_driver, "Window", entity_id, 2, None)
await acc.run_handler()
await hass.async_block_till_done()
@ -235,7 +216,7 @@ async def test_window_instantiate(hass, hk_driver, cls, events):
assert acc.char_target_position.value == 0
async def test_windowcovering_cover_set_tilt(hass, hk_driver, cls, events):
async def test_windowcovering_cover_set_tilt(hass, hk_driver, events):
"""Test if accessory and HA update slat tilt accordingly."""
entity_id = "cover.window"
@ -243,7 +224,7 @@ async def test_windowcovering_cover_set_tilt(hass, hk_driver, cls, events):
entity_id, STATE_UNKNOWN, {ATTR_SUPPORTED_FEATURES: SUPPORT_SET_TILT_POSITION}
)
await hass.async_block_till_done()
acc = cls.windowcovering(hass, hk_driver, "Cover", entity_id, 2, None)
acc = WindowCovering(hass, hk_driver, "Cover", entity_id, 2, None)
await acc.run_handler()
await hass.async_block_till_done()
@ -302,12 +283,12 @@ async def test_windowcovering_cover_set_tilt(hass, hk_driver, cls, events):
assert events[-1].data[ATTR_VALUE] == 75
async def test_windowcovering_open_close(hass, hk_driver, cls, events):
async def test_windowcovering_open_close(hass, hk_driver, events):
"""Test if accessory and HA are updated accordingly."""
entity_id = "cover.window"
hass.states.async_set(entity_id, STATE_UNKNOWN, {ATTR_SUPPORTED_FEATURES: 0})
acc = cls.windowcovering_basic(hass, hk_driver, "Cover", entity_id, 2, None)
acc = WindowCoveringBasic(hass, hk_driver, "Cover", entity_id, 2, None)
await acc.run_handler()
await hass.async_block_till_done()
@ -383,14 +364,14 @@ async def test_windowcovering_open_close(hass, hk_driver, cls, events):
assert events[-1].data[ATTR_VALUE] is None
async def test_windowcovering_open_close_stop(hass, hk_driver, cls, events):
async def test_windowcovering_open_close_stop(hass, hk_driver, events):
"""Test if accessory and HA are updated accordingly."""
entity_id = "cover.window"
hass.states.async_set(
entity_id, STATE_UNKNOWN, {ATTR_SUPPORTED_FEATURES: SUPPORT_STOP}
)
acc = cls.windowcovering_basic(hass, hk_driver, "Cover", entity_id, 2, None)
acc = WindowCoveringBasic(hass, hk_driver, "Cover", entity_id, 2, None)
await acc.run_handler()
await hass.async_block_till_done()
@ -431,7 +412,7 @@ async def test_windowcovering_open_close_stop(hass, hk_driver, cls, events):
async def test_windowcovering_open_close_with_position_and_stop(
hass, hk_driver, cls, events
hass, hk_driver, events
):
"""Test if accessory and HA are updated accordingly."""
entity_id = "cover.stop_window"
@ -441,7 +422,7 @@ async def test_windowcovering_open_close_with_position_and_stop(
STATE_UNKNOWN,
{ATTR_SUPPORTED_FEATURES: SUPPORT_STOP | SUPPORT_SET_POSITION},
)
acc = cls.windowcovering(hass, hk_driver, "Cover", entity_id, 2, None)
acc = WindowCovering(hass, hk_driver, "Cover", entity_id, 2, None)
await acc.run_handler()
await hass.async_block_till_done()
@ -461,7 +442,7 @@ async def test_windowcovering_open_close_with_position_and_stop(
assert events[-1].data[ATTR_VALUE] is None
async def test_windowcovering_basic_restore(hass, hk_driver, cls, events):
async def test_windowcovering_basic_restore(hass, hk_driver, events):
"""Test setting up an entity from state in the event registry."""
hass.state = CoreState.not_running
@ -486,22 +467,20 @@ async def test_windowcovering_basic_restore(hass, hk_driver, cls, events):
hass.bus.async_fire(EVENT_HOMEASSISTANT_START, {})
await hass.async_block_till_done()
acc = cls.windowcovering_basic(hass, hk_driver, "Cover", "cover.simple", 2, None)
acc = WindowCoveringBasic(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.windowcovering_basic(
hass, hk_driver, "Cover", "cover.all_info_set", 2, None
)
acc = WindowCoveringBasic(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_windowcovering_restore(hass, hk_driver, cls, events):
async def test_windowcovering_restore(hass, hk_driver, events):
"""Test setting up an entity from state in the event registry."""
hass.state = CoreState.not_running
@ -526,20 +505,20 @@ async def test_windowcovering_restore(hass, hk_driver, cls, events):
hass.bus.async_fire(EVENT_HOMEASSISTANT_START, {})
await hass.async_block_till_done()
acc = cls.windowcovering(hass, hk_driver, "Cover", "cover.simple", 2, None)
acc = WindowCovering(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.windowcovering(hass, hk_driver, "Cover", "cover.all_info_set", 2, None)
acc = WindowCovering(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_garage_door_with_linked_obstruction_sensor(hass, hk_driver, cls, events):
async def test_garage_door_with_linked_obstruction_sensor(hass, hk_driver, events):
"""Test if accessory and HA are updated accordingly with a linked obstruction sensor."""
linked_obstruction_sensor_entity_id = "binary_sensor.obstruction"
entity_id = "cover.garage_door"
@ -547,7 +526,7 @@ async def test_garage_door_with_linked_obstruction_sensor(hass, hk_driver, cls,
hass.states.async_set(linked_obstruction_sensor_entity_id, STATE_OFF)
hass.states.async_set(entity_id, None)
await hass.async_block_till_done()
acc = cls.garage(
acc = GarageDoorOpener(
hass,
hk_driver,
"Garage Door",

View file

@ -1,8 +1,6 @@
"""Test different accessory types: Fans."""
from collections import namedtuple
from pyhap.const import HAP_REPR_AID, HAP_REPR_CHARS, HAP_REPR_IID, HAP_REPR_VALUE
import pytest
from homeassistant.components.fan import (
ATTR_DIRECTION,
@ -16,6 +14,7 @@ from homeassistant.components.fan import (
SUPPORT_SET_SPEED,
)
from homeassistant.components.homekit.const import ATTR_VALUE
from homeassistant.components.homekit.type_fans import Fan
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_SUPPORTED_FEATURES,
@ -28,27 +27,15 @@ 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
@pytest.fixture(scope="module")
def cls():
"""Patch debounce decorator during import of type_fans."""
patcher = patch_debounce()
patcher.start()
_import = __import__("homeassistant.components.homekit.type_fans", fromlist=["Fan"])
patcher_tuple = namedtuple("Cls", ["fan"])
yield patcher_tuple(fan=_import.Fan)
patcher.stop()
async def test_fan_basic(hass, hk_driver, cls, events):
async def test_fan_basic(hass, hk_driver, events):
"""Test fan with char state."""
entity_id = "fan.demo"
hass.states.async_set(entity_id, STATE_ON, {ATTR_SUPPORTED_FEATURES: 0})
await hass.async_block_till_done()
acc = cls.fan(hass, hk_driver, "Fan", entity_id, 1, None)
acc = Fan(hass, hk_driver, "Fan", entity_id, 1, None)
hk_driver.add_accessory(acc)
assert acc.aid == 1
@ -120,7 +107,7 @@ async def test_fan_basic(hass, hk_driver, cls, events):
assert events[-1].data[ATTR_VALUE] is None
async def test_fan_direction(hass, hk_driver, cls, events):
async def test_fan_direction(hass, hk_driver, events):
"""Test fan with direction."""
entity_id = "fan.demo"
@ -130,7 +117,7 @@ async def test_fan_direction(hass, hk_driver, cls, events):
{ATTR_SUPPORTED_FEATURES: SUPPORT_DIRECTION, ATTR_DIRECTION: DIRECTION_FORWARD},
)
await hass.async_block_till_done()
acc = cls.fan(hass, hk_driver, "Fan", entity_id, 1, None)
acc = Fan(hass, hk_driver, "Fan", entity_id, 1, None)
hk_driver.add_accessory(acc)
assert acc.char_direction.value == 0
@ -188,7 +175,7 @@ async def test_fan_direction(hass, hk_driver, cls, events):
assert events[-1].data[ATTR_VALUE] == DIRECTION_REVERSE
async def test_fan_oscillate(hass, hk_driver, cls, events):
async def test_fan_oscillate(hass, hk_driver, events):
"""Test fan with oscillate."""
entity_id = "fan.demo"
@ -198,7 +185,7 @@ async def test_fan_oscillate(hass, hk_driver, cls, events):
{ATTR_SUPPORTED_FEATURES: SUPPORT_OSCILLATE, ATTR_OSCILLATING: False},
)
await hass.async_block_till_done()
acc = cls.fan(hass, hk_driver, "Fan", entity_id, 1, None)
acc = Fan(hass, hk_driver, "Fan", entity_id, 1, None)
hk_driver.add_accessory(acc)
assert acc.char_swing.value == 0
@ -257,7 +244,7 @@ async def test_fan_oscillate(hass, hk_driver, cls, events):
assert events[-1].data[ATTR_VALUE] is True
async def test_fan_speed(hass, hk_driver, cls, events):
async def test_fan_speed(hass, hk_driver, events):
"""Test fan with speed."""
entity_id = "fan.demo"
@ -270,7 +257,7 @@ async def test_fan_speed(hass, hk_driver, cls, events):
},
)
await hass.async_block_till_done()
acc = cls.fan(hass, hk_driver, "Fan", entity_id, 1, None)
acc = Fan(hass, hk_driver, "Fan", entity_id, 1, None)
hk_driver.add_accessory(acc)
# Initial value can be anything but 0. If it is 0, it might cause HomeKit to set the
@ -336,7 +323,7 @@ async def test_fan_speed(hass, hk_driver, cls, events):
assert acc.char_active.value == 1
async def test_fan_set_all_one_shot(hass, hk_driver, cls, events):
async def test_fan_set_all_one_shot(hass, hk_driver, events):
"""Test fan with speed."""
entity_id = "fan.demo"
@ -353,7 +340,7 @@ async def test_fan_set_all_one_shot(hass, hk_driver, cls, events):
},
)
await hass.async_block_till_done()
acc = cls.fan(hass, hk_driver, "Fan", entity_id, 1, None)
acc = Fan(hass, hk_driver, "Fan", entity_id, 1, None)
hk_driver.add_accessory(acc)
# Initial value can be anything but 0. If it is 0, it might cause HomeKit to set the
@ -529,7 +516,7 @@ async def test_fan_set_all_one_shot(hass, hk_driver, cls, events):
assert len(call_set_direction) == 2
async def test_fan_restore(hass, hk_driver, cls, events):
async def test_fan_restore(hass, hk_driver, events):
"""Test setting up an entity from state in the event registry."""
hass.state = CoreState.not_running
@ -554,14 +541,14 @@ async def test_fan_restore(hass, hk_driver, cls, events):
hass.bus.async_fire(EVENT_HOMEASSISTANT_START, {})
await hass.async_block_till_done()
acc = cls.fan(hass, hk_driver, "Fan", "fan.simple", 2, None)
acc = 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)
acc = 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

View file

@ -1,10 +1,9 @@
"""Test different accessory types: Lights."""
from collections import namedtuple
from pyhap.const import HAP_REPR_AID, HAP_REPR_CHARS, HAP_REPR_IID, HAP_REPR_VALUE
import pytest
from homeassistant.components.homekit.const import ATTR_VALUE
from homeassistant.components.homekit.type_lights import Light
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_BRIGHTNESS_PCT,
@ -28,29 +27,15 @@ 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
@pytest.fixture(scope="module")
def cls():
"""Patch debounce decorator during import of type_lights."""
patcher = patch_debounce()
patcher.start()
_import = __import__(
"homeassistant.components.homekit.type_lights", fromlist=["Light"]
)
patcher_tuple = namedtuple("Cls", ["light"])
yield patcher_tuple(light=_import.Light)
patcher.stop()
async def test_light_basic(hass, hk_driver, cls, events):
async def test_light_basic(hass, hk_driver, events):
"""Test light with char state."""
entity_id = "light.demo"
hass.states.async_set(entity_id, STATE_ON, {ATTR_SUPPORTED_FEATURES: 0})
await hass.async_block_till_done()
acc = cls.light(hass, hk_driver, "Light", entity_id, 1, None)
acc = Light(hass, hk_driver, "Light", entity_id, 1, None)
hk_driver.add_accessory(acc)
assert acc.aid == 1
@ -113,7 +98,7 @@ async def test_light_basic(hass, hk_driver, cls, events):
assert events[-1].data[ATTR_VALUE] == "Set state to 0"
async def test_light_brightness(hass, hk_driver, cls, events):
async def test_light_brightness(hass, hk_driver, events):
"""Test light with brightness."""
entity_id = "light.demo"
@ -123,7 +108,7 @@ async def test_light_brightness(hass, hk_driver, cls, events):
{ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS, ATTR_BRIGHTNESS: 255},
)
await hass.async_block_till_done()
acc = cls.light(hass, hk_driver, "Light", entity_id, 1, None)
acc = Light(hass, hk_driver, "Light", entity_id, 1, None)
hk_driver.add_accessory(acc)
# Initial value can be anything but 0. If it is 0, it might cause HomeKit to set the
@ -231,7 +216,7 @@ async def test_light_brightness(hass, hk_driver, cls, events):
assert acc.char_brightness.value == 1
async def test_light_color_temperature(hass, hk_driver, cls, events):
async def test_light_color_temperature(hass, hk_driver, events):
"""Test light with color temperature."""
entity_id = "light.demo"
@ -241,7 +226,7 @@ async def test_light_color_temperature(hass, hk_driver, cls, events):
{ATTR_SUPPORTED_FEATURES: SUPPORT_COLOR_TEMP, ATTR_COLOR_TEMP: 190},
)
await hass.async_block_till_done()
acc = cls.light(hass, hk_driver, "Light", entity_id, 1, None)
acc = Light(hass, hk_driver, "Light", entity_id, 1, None)
hk_driver.add_accessory(acc)
assert acc.char_color_temperature.value == 190
@ -278,7 +263,7 @@ async def test_light_color_temperature(hass, hk_driver, cls, events):
assert events[-1].data[ATTR_VALUE] == "color temperature at 250"
async def test_light_color_temperature_and_rgb_color(hass, hk_driver, cls, events):
async def test_light_color_temperature_and_rgb_color(hass, hk_driver, events):
"""Test light with color temperature and rgb color not exposing temperature."""
entity_id = "light.demo"
@ -292,7 +277,7 @@ async def test_light_color_temperature_and_rgb_color(hass, hk_driver, cls, event
},
)
await hass.async_block_till_done()
acc = cls.light(hass, hk_driver, "Light", entity_id, 2, None)
acc = Light(hass, hk_driver, "Light", entity_id, 2, None)
assert acc.char_hue.value == 260
assert acc.char_saturation.value == 90
@ -313,7 +298,7 @@ async def test_light_color_temperature_and_rgb_color(hass, hk_driver, cls, event
assert acc.char_saturation.value == 61
async def test_light_rgb_color(hass, hk_driver, cls, events):
async def test_light_rgb_color(hass, hk_driver, events):
"""Test light with rgb_color."""
entity_id = "light.demo"
@ -323,7 +308,7 @@ async def test_light_rgb_color(hass, hk_driver, cls, events):
{ATTR_SUPPORTED_FEATURES: SUPPORT_COLOR, ATTR_HS_COLOR: (260, 90)},
)
await hass.async_block_till_done()
acc = cls.light(hass, hk_driver, "Light", entity_id, 1, None)
acc = Light(hass, hk_driver, "Light", entity_id, 1, None)
hk_driver.add_accessory(acc)
assert acc.char_hue.value == 260
@ -365,7 +350,7 @@ async def test_light_rgb_color(hass, hk_driver, cls, events):
assert events[-1].data[ATTR_VALUE] == "set color at (145, 75)"
async def test_light_restore(hass, hk_driver, cls, events):
async def test_light_restore(hass, hk_driver, events):
"""Test setting up an entity from state in the event registry."""
hass.state = CoreState.not_running
@ -385,20 +370,20 @@ async def test_light_restore(hass, hk_driver, cls, events):
hass.bus.async_fire(EVENT_HOMEASSISTANT_START, {})
await hass.async_block_till_done()
acc = cls.light(hass, hk_driver, "Light", "light.simple", 1, None)
acc = Light(hass, hk_driver, "Light", "light.simple", 1, None)
hk_driver.add_accessory(acc)
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)
acc = 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
async def test_light_set_brightness_and_color(hass, hk_driver, cls, events):
async def test_light_set_brightness_and_color(hass, hk_driver, events):
"""Test light with all chars in one go."""
entity_id = "light.demo"
@ -411,7 +396,7 @@ async def test_light_set_brightness_and_color(hass, hk_driver, cls, events):
},
)
await hass.async_block_till_done()
acc = cls.light(hass, hk_driver, "Light", entity_id, 1, None)
acc = Light(hass, hk_driver, "Light", entity_id, 1, None)
hk_driver.add_accessory(acc)
# Initial value can be anything but 0. If it is 0, it might cause HomeKit to set the
@ -474,7 +459,7 @@ async def test_light_set_brightness_and_color(hass, hk_driver, cls, events):
)
async def test_light_set_brightness_and_color_temp(hass, hk_driver, cls, events):
async def test_light_set_brightness_and_color_temp(hass, hk_driver, events):
"""Test light with all chars in one go."""
entity_id = "light.demo"
@ -487,7 +472,7 @@ async def test_light_set_brightness_and_color_temp(hass, hk_driver, cls, events)
},
)
await hass.async_block_till_done()
acc = cls.light(hass, hk_driver, "Light", entity_id, 1, None)
acc = Light(hass, hk_driver, "Light", entity_id, 1, None)
hk_driver.add_accessory(acc)
# Initial value can be anything but 0. If it is 0, it might cause HomeKit to set the