Support unpairing homekit accessories from homekit_controller (#65065)
parent
7415513352
commit
735edd83fc
|
@ -2,6 +2,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import aiohomekit
|
import aiohomekit
|
||||||
|
@ -26,6 +27,8 @@ from .connection import HKDevice, valid_serial_number
|
||||||
from .const import CONTROLLER, ENTITY_MAP, KNOWN_DEVICES, TRIGGERS
|
from .const import CONTROLLER, ENTITY_MAP, KNOWN_DEVICES, TRIGGERS
|
||||||
from .storage import EntityMapStorage
|
from .storage import EntityMapStorage
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def escape_characteristic_name(char_name):
|
def escape_characteristic_name(char_name):
|
||||||
"""Escape any dash or dots in a characteristics name."""
|
"""Escape any dash or dots in a characteristics name."""
|
||||||
|
@ -248,4 +251,21 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||||
"""Cleanup caches before removing config entry."""
|
"""Cleanup caches before removing config entry."""
|
||||||
hkid = entry.data["AccessoryPairingID"]
|
hkid = entry.data["AccessoryPairingID"]
|
||||||
|
|
||||||
|
# Remove cached type data from .storage/homekit_controller-entity-map
|
||||||
hass.data[ENTITY_MAP].async_delete_map(hkid)
|
hass.data[ENTITY_MAP].async_delete_map(hkid)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
except aiohomekit.AccessoryDisconnectedError:
|
||||||
|
_LOGGER.warning(
|
||||||
|
"Accessory %s was removed from HomeAssistant but was not reachable "
|
||||||
|
"to properly unpair. It may need resetting before you can use it with "
|
||||||
|
"HomeKit again",
|
||||||
|
entry.title,
|
||||||
|
)
|
||||||
|
|
|
@ -4,8 +4,11 @@ from unittest.mock import patch
|
||||||
|
|
||||||
from aiohomekit.model.characteristics import CharacteristicsTypes
|
from aiohomekit.model.characteristics import CharacteristicsTypes
|
||||||
from aiohomekit.model.services import ServicesTypes
|
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
|
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from tests.components.homekit_controller.common import setup_test_component
|
from tests.components.homekit_controller.common import setup_test_component
|
||||||
|
|
||||||
|
@ -27,3 +30,24 @@ async def test_unload_on_stop(hass, utcnow):
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert async_unlock_mock.called
|
assert async_unlock_mock.called
|
||||||
|
|
||||||
|
|
||||||
|
async def test_async_remove_entry(hass: HomeAssistant):
|
||||||
|
"""Test unpairing a component."""
|
||||||
|
helper = await setup_test_component(hass, create_motion_sensor_service)
|
||||||
|
|
||||||
|
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 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
|
||||||
|
|
||||||
|
assert hkid not in hass.data[ENTITY_MAP].storage_data
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
from aiohomekit.model.characteristics import CharacteristicsTypes
|
from aiohomekit.model.characteristics import CharacteristicsTypes
|
||||||
from aiohomekit.model.services import ServicesTypes
|
from aiohomekit.model.services import ServicesTypes
|
||||||
|
|
||||||
from homeassistant import config_entries
|
|
||||||
from homeassistant.components.homekit_controller import async_remove_entry
|
|
||||||
from homeassistant.components.homekit_controller.const import ENTITY_MAP
|
from homeassistant.components.homekit_controller.const import ENTITY_MAP
|
||||||
|
|
||||||
from tests.common import flush_store
|
from tests.common import flush_store
|
||||||
|
@ -79,26 +77,3 @@ async def test_storage_is_updated_on_add(hass, hass_storage, utcnow):
|
||||||
# Is saved out to store?
|
# Is saved out to store?
|
||||||
await flush_store(entity_map.store)
|
await flush_store(entity_map.store)
|
||||||
assert hkid in hass_storage[ENTITY_MAP]["data"]["pairings"]
|
assert hkid in hass_storage[ENTITY_MAP]["data"]["pairings"]
|
||||||
|
|
||||||
|
|
||||||
async def test_storage_is_removed_on_config_entry_removal(hass, utcnow):
|
|
||||||
"""Test entity map storage is cleaned up on config entry removal."""
|
|
||||||
await setup_test_component(hass, create_lightbulb_service)
|
|
||||||
|
|
||||||
hkid = "00:00:00:00:00:00"
|
|
||||||
|
|
||||||
pairing_data = {"AccessoryPairingID": hkid}
|
|
||||||
|
|
||||||
entry = config_entries.ConfigEntry(
|
|
||||||
1,
|
|
||||||
"homekit_controller",
|
|
||||||
"TestData",
|
|
||||||
pairing_data,
|
|
||||||
"test",
|
|
||||||
)
|
|
||||||
|
|
||||||
assert hkid in hass.data[ENTITY_MAP].storage_data
|
|
||||||
|
|
||||||
await async_remove_entry(hass, entry)
|
|
||||||
|
|
||||||
assert hkid not in hass.data[ENTITY_MAP].storage_data
|
|
||||||
|
|
Loading…
Reference in New Issue