Cleanup BLE-only controls when migrating HomeKit BLE device to Thread (#110334)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
c890c1aeee
commit
cf849664ba
4 changed files with 39 additions and 84 deletions
|
@ -5,6 +5,7 @@ characteristics that don't map to a Home Assistant feature.
|
|||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
|
||||
|
@ -32,6 +33,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||
class HomeKitButtonEntityDescription(ButtonEntityDescription):
|
||||
"""Describes Homekit button."""
|
||||
|
||||
probe: Callable[[Characteristic], bool] | None = None
|
||||
write_value: int | str | None = None
|
||||
|
||||
|
||||
|
@ -71,13 +73,19 @@ async def async_setup_entry(
|
|||
|
||||
@callback
|
||||
def async_add_characteristic(char: Characteristic) -> bool:
|
||||
entities: list[HomeKitButton | HomeKitEcobeeClearHoldButton] = []
|
||||
entities: list[CharacteristicEntity] = []
|
||||
info = {"aid": char.service.accessory.aid, "iid": char.service.iid}
|
||||
|
||||
if description := BUTTON_ENTITIES.get(char.type):
|
||||
entities.append(HomeKitButton(conn, info, char, description))
|
||||
elif entity_type := BUTTON_ENTITY_CLASSES.get(char.type):
|
||||
entities.append(entity_type(conn, info, char))
|
||||
elif char.type == CharacteristicsTypes.THREAD_CONTROL_POINT:
|
||||
if not conn.is_unprovisioned_thread_device:
|
||||
return False
|
||||
entities.append(
|
||||
HomeKitProvisionPreferredThreadCredentials(conn, info, char)
|
||||
)
|
||||
else:
|
||||
return False
|
||||
|
||||
|
@ -92,7 +100,11 @@ async def async_setup_entry(
|
|||
conn.add_char_factory(async_add_characteristic)
|
||||
|
||||
|
||||
class HomeKitButton(CharacteristicEntity, ButtonEntity):
|
||||
class BaseHomeKitButton(CharacteristicEntity, ButtonEntity):
|
||||
"""Base class for all HomeKit buttons."""
|
||||
|
||||
|
||||
class HomeKitButton(BaseHomeKitButton):
|
||||
"""Representation of a Button control on a homekit accessory."""
|
||||
|
||||
entity_description: HomeKitButtonEntityDescription
|
||||
|
@ -126,7 +138,7 @@ class HomeKitButton(CharacteristicEntity, ButtonEntity):
|
|||
await self.async_put_characteristics({key: val})
|
||||
|
||||
|
||||
class HomeKitEcobeeClearHoldButton(CharacteristicEntity, ButtonEntity):
|
||||
class HomeKitEcobeeClearHoldButton(BaseHomeKitButton):
|
||||
"""Representation of a Button control for Ecobee clear hold request."""
|
||||
|
||||
def get_characteristic_types(self) -> list[str]:
|
||||
|
@ -155,7 +167,7 @@ class HomeKitEcobeeClearHoldButton(CharacteristicEntity, ButtonEntity):
|
|||
await self.async_put_characteristics({key: val})
|
||||
|
||||
|
||||
class HomeKitProvisionPreferredThreadCredentials(CharacteristicEntity, ButtonEntity):
|
||||
class HomeKitProvisionPreferredThreadCredentials(BaseHomeKitButton):
|
||||
"""A button users can press to migrate their HomeKit BLE device to Thread."""
|
||||
|
||||
_attr_entity_category = EntityCategory.CONFIG
|
||||
|
@ -179,5 +191,4 @@ class HomeKitProvisionPreferredThreadCredentials(CharacteristicEntity, ButtonEnt
|
|||
|
||||
BUTTON_ENTITY_CLASSES: dict[str, type] = {
|
||||
CharacteristicsTypes.VENDOR_ECOBEE_CLEAR_HOLD: HomeKitEcobeeClearHoldButton,
|
||||
CharacteristicsTypes.THREAD_CONTROL_POINT: HomeKitProvisionPreferredThreadCredentials,
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ from aiohomekit.exceptions import (
|
|||
EncryptionError,
|
||||
)
|
||||
from aiohomekit.model import Accessories, Accessory, Transport
|
||||
from aiohomekit.model.characteristics import Characteristic
|
||||
from aiohomekit.model.characteristics import Characteristic, CharacteristicsTypes
|
||||
from aiohomekit.model.services import Service, ServicesTypes
|
||||
|
||||
from homeassistant.components.thread.dataset_store import async_get_preferred_dataset
|
||||
|
@ -544,6 +544,10 @@ class HKDevice:
|
|||
current_unique_id.add((accessory.aid, service.iid, None))
|
||||
|
||||
for char in service.characteristics:
|
||||
if self.pairing.transport != Transport.BLE:
|
||||
if char.type == CharacteristicsTypes.THREAD_CONTROL_POINT:
|
||||
continue
|
||||
|
||||
current_unique_id.add(
|
||||
(
|
||||
accessory.aid,
|
||||
|
|
|
@ -70,44 +70,6 @@
|
|||
'state': 'unknown',
|
||||
}),
|
||||
}),
|
||||
dict({
|
||||
'entry': dict({
|
||||
'aliases': list([
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': 'TestData',
|
||||
'device_class': None,
|
||||
'disabled_by': None,
|
||||
'domain': 'button',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'button.airversa_ap2_1808_provision_preferred_thread_credentials',
|
||||
'has_entity_name': False,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'labels': list([
|
||||
]),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Airversa AP2 1808 Provision Preferred Thread Credentials',
|
||||
'platform': 'homekit_controller',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00:00:00:00:00:00_1_112_119',
|
||||
'unit_of_measurement': None,
|
||||
}),
|
||||
'state': dict({
|
||||
'attributes': dict({
|
||||
'friendly_name': 'Airversa AP2 1808 Provision Preferred Thread Credentials',
|
||||
}),
|
||||
'entity_id': 'button.airversa_ap2_1808_provision_preferred_thread_credentials',
|
||||
'state': 'unknown',
|
||||
}),
|
||||
}),
|
||||
dict({
|
||||
'entry': dict({
|
||||
'aliases': list([
|
||||
|
@ -14200,44 +14162,6 @@
|
|||
'state': 'unknown',
|
||||
}),
|
||||
}),
|
||||
dict({
|
||||
'entry': dict({
|
||||
'aliases': list([
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': 'TestData',
|
||||
'device_class': None,
|
||||
'disabled_by': None,
|
||||
'domain': 'button',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'button.nanoleaf_strip_3b32_provision_preferred_thread_credentials',
|
||||
'has_entity_name': False,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'labels': list([
|
||||
]),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Nanoleaf Strip 3B32 Provision Preferred Thread Credentials',
|
||||
'platform': 'homekit_controller',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00:00:00:00:00:00_1_31_119',
|
||||
'unit_of_measurement': None,
|
||||
}),
|
||||
'state': dict({
|
||||
'attributes': dict({
|
||||
'friendly_name': 'Nanoleaf Strip 3B32 Provision Preferred Thread Credentials',
|
||||
}),
|
||||
'entity_id': 'button.nanoleaf_strip_3b32_provision_preferred_thread_credentials',
|
||||
'state': 'unknown',
|
||||
}),
|
||||
}),
|
||||
dict({
|
||||
'entry': dict({
|
||||
'aliases': list([
|
||||
|
|
|
@ -14,7 +14,7 @@ from homeassistant.components.homekit_controller.const import (
|
|||
from homeassistant.components.thread import async_add_dataset, dataset_store
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
|
||||
from .common import setup_accessories_from_file, setup_platform, setup_test_accessories
|
||||
|
||||
|
@ -216,7 +216,9 @@ async def test_thread_provision_no_creds(hass: HomeAssistant) -> None:
|
|||
)
|
||||
|
||||
|
||||
async def test_thread_provision(hass: HomeAssistant) -> None:
|
||||
async def test_thread_provision(
|
||||
hass: HomeAssistant, entity_registry: er.EntityRegistry
|
||||
) -> None:
|
||||
"""Test that a when a thread provision works the config entry is updated."""
|
||||
await async_add_dataset(
|
||||
hass,
|
||||
|
@ -256,6 +258,13 @@ async def test_thread_provision(hass: HomeAssistant) -> None:
|
|||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(
|
||||
"button.nanoleaf_strip_3b32_provision_preferred_thread_credentials"
|
||||
)
|
||||
assert entity_registry.async_get(
|
||||
"button.nanoleaf_strip_3b32_provision_preferred_thread_credentials"
|
||||
)
|
||||
|
||||
await hass.services.async_call(
|
||||
"button",
|
||||
"press",
|
||||
|
@ -267,6 +276,13 @@ async def test_thread_provision(hass: HomeAssistant) -> None:
|
|||
|
||||
assert config_entry.data["Connection"] == "CoAP"
|
||||
|
||||
assert not hass.states.get(
|
||||
"button.nanoleaf_strip_3b32_provision_preferred_thread_credentials"
|
||||
)
|
||||
assert not entity_registry.async_get(
|
||||
"button.nanoleaf_strip_3b32_provision_preferred_thread_credentials"
|
||||
)
|
||||
|
||||
|
||||
async def test_thread_provision_migration_failed(hass: HomeAssistant) -> None:
|
||||
"""Test that when a device 'migrates' but doesn't show up in CoAP, we remain in BLE mode."""
|
||||
|
|
Loading…
Add table
Reference in a new issue