Prepare for new aiohomekit lifecycle API (#66340)
This commit is contained in:
parent
2f220b27d4
commit
0daf20c0cc
11 changed files with 79 additions and 28 deletions
|
@ -94,6 +94,7 @@ homeassistant.components.homekit_controller.const
|
|||
homeassistant.components.homekit_controller.lock
|
||||
homeassistant.components.homekit_controller.select
|
||||
homeassistant.components.homekit_controller.storage
|
||||
homeassistant.components.homekit_controller.utils
|
||||
homeassistant.components.homewizard.*
|
||||
homeassistant.components.http.*
|
||||
homeassistant.components.huawei_lte.*
|
||||
|
|
|
@ -14,7 +14,6 @@ from aiohomekit.model.characteristics import (
|
|||
)
|
||||
from aiohomekit.model.services import Service, ServicesTypes
|
||||
|
||||
from homeassistant.components import zeroconf
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import Event, HomeAssistant
|
||||
|
@ -24,8 +23,9 @@ from homeassistant.helpers.typing import ConfigType
|
|||
|
||||
from .config_flow import normalize_hkid
|
||||
from .connection import HKDevice, valid_serial_number
|
||||
from .const import CONTROLLER, ENTITY_MAP, KNOWN_DEVICES, TRIGGERS
|
||||
from .const import ENTITY_MAP, KNOWN_DEVICES, TRIGGERS
|
||||
from .storage import EntityMapStorage
|
||||
from .utils import async_get_controller
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -208,10 +208,8 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||
map_storage = hass.data[ENTITY_MAP] = EntityMapStorage(hass)
|
||||
await map_storage.async_initialize()
|
||||
|
||||
async_zeroconf_instance = await zeroconf.async_get_async_instance(hass)
|
||||
hass.data[CONTROLLER] = aiohomekit.Controller(
|
||||
async_zeroconf_instance=async_zeroconf_instance
|
||||
)
|
||||
await async_get_controller(hass)
|
||||
|
||||
hass.data[KNOWN_DEVICES] = {}
|
||||
hass.data[TRIGGERS] = {}
|
||||
|
||||
|
@ -246,10 +244,10 @@ async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
|||
# Remove cached type data from .storage/homekit_controller-entity-map
|
||||
hass.data[ENTITY_MAP].async_delete_map(hkid)
|
||||
|
||||
controller = await async_get_controller(hass)
|
||||
|
||||
# Remove the pairing on the device, making the device discoverable again.
|
||||
# Don't reuse any objects in hass.data as they are already unloaded
|
||||
async_zeroconf_instance = await zeroconf.async_get_async_instance(hass)
|
||||
controller = aiohomekit.Controller(async_zeroconf_instance=async_zeroconf_instance)
|
||||
controller.load_pairing(hkid, dict(entry.data))
|
||||
try:
|
||||
await controller.remove_pairing(hkid)
|
||||
|
|
|
@ -20,6 +20,7 @@ from homeassistant.helpers.device_registry import (
|
|||
)
|
||||
|
||||
from .const import DOMAIN, KNOWN_DEVICES
|
||||
from .utils import async_get_controller
|
||||
|
||||
HOMEKIT_DIR = ".homekit"
|
||||
HOMEKIT_BRIDGE_DOMAIN = "homekit"
|
||||
|
@ -104,10 +105,7 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
|
||||
async def _async_setup_controller(self):
|
||||
"""Create the controller."""
|
||||
async_zeroconf_instance = await zeroconf.async_get_async_instance(self.hass)
|
||||
self.controller = aiohomekit.Controller(
|
||||
async_zeroconf_instance=async_zeroconf_instance
|
||||
)
|
||||
self.controller = await async_get_controller(self.hass)
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
"""Handle a flow start."""
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"name": "HomeKit Controller",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/homekit_controller",
|
||||
"requirements": ["aiohomekit==0.7.5"],
|
||||
"requirements": ["aiohomekit==0.7.7"],
|
||||
"zeroconf": ["_hap._tcp.local."],
|
||||
"after_dependencies": ["zeroconf"],
|
||||
"codeowners": ["@Jc2k", "@bdraco"],
|
||||
|
|
42
homeassistant/components/homekit_controller/utils.py
Normal file
42
homeassistant/components/homekit_controller/utils.py
Normal file
|
@ -0,0 +1,42 @@
|
|||
"""Helper functions for the homekit_controller component."""
|
||||
from typing import cast
|
||||
|
||||
from aiohomekit import Controller
|
||||
|
||||
from homeassistant.components import zeroconf
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import Event, HomeAssistant
|
||||
|
||||
from .const import CONTROLLER
|
||||
|
||||
|
||||
async def async_get_controller(hass: HomeAssistant) -> Controller:
|
||||
"""Get or create an aiohomekit Controller instance."""
|
||||
if existing := hass.data.get(CONTROLLER):
|
||||
return cast(Controller, existing)
|
||||
|
||||
async_zeroconf_instance = await zeroconf.async_get_async_instance(hass)
|
||||
|
||||
# In theory another call to async_get_controller could have run while we were
|
||||
# trying to get the zeroconf instance. So we check again to make sure we
|
||||
# don't leak a Controller instance here.
|
||||
if existing := hass.data.get(CONTROLLER):
|
||||
return cast(Controller, existing)
|
||||
|
||||
controller = Controller(async_zeroconf_instance=async_zeroconf_instance)
|
||||
|
||||
hass.data[CONTROLLER] = controller
|
||||
|
||||
async def _async_stop_homekit_controller(event: Event) -> None:
|
||||
# Pop first so that in theory another controller /could/ start
|
||||
# While this one was shutting down
|
||||
hass.data.pop(CONTROLLER, None)
|
||||
await controller.async_stop()
|
||||
|
||||
# Right now _async_stop_homekit_controller is only called on HA exiting
|
||||
# So we don't have to worry about leaking a callback here.
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_stop_homekit_controller)
|
||||
|
||||
await controller.async_start()
|
||||
|
||||
return controller
|
11
mypy.ini
11
mypy.ini
|
@ -851,6 +851,17 @@ no_implicit_optional = true
|
|||
warn_return_any = true
|
||||
warn_unreachable = true
|
||||
|
||||
[mypy-homeassistant.components.homekit_controller.utils]
|
||||
check_untyped_defs = true
|
||||
disallow_incomplete_defs = true
|
||||
disallow_subclassing_any = true
|
||||
disallow_untyped_calls = true
|
||||
disallow_untyped_decorators = true
|
||||
disallow_untyped_defs = true
|
||||
no_implicit_optional = true
|
||||
warn_return_any = true
|
||||
warn_unreachable = true
|
||||
|
||||
[mypy-homeassistant.components.homewizard.*]
|
||||
check_untyped_defs = true
|
||||
disallow_incomplete_defs = true
|
||||
|
|
|
@ -184,7 +184,7 @@ aioguardian==2021.11.0
|
|||
aioharmony==0.2.9
|
||||
|
||||
# homeassistant.components.homekit_controller
|
||||
aiohomekit==0.7.5
|
||||
aiohomekit==0.7.7
|
||||
|
||||
# homeassistant.components.emulated_hue
|
||||
# homeassistant.components.http
|
||||
|
|
|
@ -134,7 +134,7 @@ aioguardian==2021.11.0
|
|||
aioharmony==0.2.9
|
||||
|
||||
# homeassistant.components.homekit_controller
|
||||
aiohomekit==0.7.5
|
||||
aiohomekit==0.7.7
|
||||
|
||||
# homeassistant.components.emulated_hue
|
||||
# homeassistant.components.http
|
||||
|
|
|
@ -174,7 +174,9 @@ async def setup_platform(hass):
|
|||
"""Load the platform but with a fake Controller API."""
|
||||
config = {"discovery": {}}
|
||||
|
||||
with mock.patch("aiohomekit.Controller") as controller:
|
||||
with mock.patch(
|
||||
"homeassistant.components.homekit_controller.utils.Controller"
|
||||
) as controller:
|
||||
fake_controller = controller.return_value = FakeController()
|
||||
await async_setup_component(hass, DOMAIN, config)
|
||||
|
||||
|
|
|
@ -27,7 +27,10 @@ def utcnow(request):
|
|||
def controller(hass):
|
||||
"""Replace aiohomekit.Controller with an instance of aiohomekit.testing.FakeController."""
|
||||
instance = FakeController()
|
||||
with unittest.mock.patch("aiohomekit.Controller", return_value=instance):
|
||||
with unittest.mock.patch(
|
||||
"homeassistant.components.homekit_controller.utils.Controller",
|
||||
return_value=instance,
|
||||
):
|
||||
yield instance
|
||||
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ from unittest.mock import patch
|
|||
|
||||
from aiohomekit.model.characteristics import CharacteristicsTypes
|
||||
from aiohomekit.model.services import ServicesTypes
|
||||
from aiohomekit.testing import FakeController
|
||||
|
||||
from homeassistant.components.homekit_controller.const import ENTITY_MAP
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||
|
@ -35,19 +34,16 @@ async def test_unload_on_stop(hass, utcnow):
|
|||
async def test_async_remove_entry(hass: HomeAssistant):
|
||||
"""Test unpairing a component."""
|
||||
helper = await setup_test_component(hass, create_motion_sensor_service)
|
||||
controller = helper.pairing.controller
|
||||
|
||||
hkid = "00:00:00:00:00:00"
|
||||
|
||||
with patch("aiohomekit.Controller") as controller_cls:
|
||||
# Setup a fake controller with 1 pairing
|
||||
controller = controller_cls.return_value = FakeController()
|
||||
await controller.add_paired_device([helper.accessory], hkid)
|
||||
assert len(controller.pairings) == 1
|
||||
assert len(controller.pairings) == 1
|
||||
|
||||
assert hkid in hass.data[ENTITY_MAP].storage_data
|
||||
assert hkid in hass.data[ENTITY_MAP].storage_data
|
||||
|
||||
# Remove it via config entry and number of pairings should go down
|
||||
await helper.config_entry.async_remove(hass)
|
||||
assert len(controller.pairings) == 0
|
||||
# Remove it via config entry and number of pairings should go down
|
||||
await helper.config_entry.async_remove(hass)
|
||||
assert len(controller.pairings) == 0
|
||||
|
||||
assert hkid not in hass.data[ENTITY_MAP].storage_data
|
||||
assert hkid not in hass.data[ENTITY_MAP].storage_data
|
||||
|
|
Loading…
Add table
Reference in a new issue