mirror of
https://github.com/home-assistant/core.git
synced 2025-07-25 22:27:07 +00:00
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
@ -5,6 +5,7 @@ characteristics that don't map to a Home Assistant feature.
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Callable
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -32,6 +33,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
class HomeKitButtonEntityDescription(ButtonEntityDescription):
|
class HomeKitButtonEntityDescription(ButtonEntityDescription):
|
||||||
"""Describes Homekit button."""
|
"""Describes Homekit button."""
|
||||||
|
|
||||||
|
probe: Callable[[Characteristic], bool] | None = None
|
||||||
write_value: int | str | None = None
|
write_value: int | str | None = None
|
||||||
|
|
||||||
|
|
||||||
@ -71,13 +73,19 @@ async def async_setup_entry(
|
|||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_add_characteristic(char: Characteristic) -> bool:
|
def async_add_characteristic(char: Characteristic) -> bool:
|
||||||
entities: list[HomeKitButton | HomeKitEcobeeClearHoldButton] = []
|
entities: list[CharacteristicEntity] = []
|
||||||
info = {"aid": char.service.accessory.aid, "iid": char.service.iid}
|
info = {"aid": char.service.accessory.aid, "iid": char.service.iid}
|
||||||
|
|
||||||
if description := BUTTON_ENTITIES.get(char.type):
|
if description := BUTTON_ENTITIES.get(char.type):
|
||||||
entities.append(HomeKitButton(conn, info, char, description))
|
entities.append(HomeKitButton(conn, info, char, description))
|
||||||
elif entity_type := BUTTON_ENTITY_CLASSES.get(char.type):
|
elif entity_type := BUTTON_ENTITY_CLASSES.get(char.type):
|
||||||
entities.append(entity_type(conn, info, char))
|
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:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -92,7 +100,11 @@ async def async_setup_entry(
|
|||||||
conn.add_char_factory(async_add_characteristic)
|
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."""
|
"""Representation of a Button control on a homekit accessory."""
|
||||||
|
|
||||||
entity_description: HomeKitButtonEntityDescription
|
entity_description: HomeKitButtonEntityDescription
|
||||||
@ -126,7 +138,7 @@ class HomeKitButton(CharacteristicEntity, ButtonEntity):
|
|||||||
await self.async_put_characteristics({key: val})
|
await self.async_put_characteristics({key: val})
|
||||||
|
|
||||||
|
|
||||||
class HomeKitEcobeeClearHoldButton(CharacteristicEntity, ButtonEntity):
|
class HomeKitEcobeeClearHoldButton(BaseHomeKitButton):
|
||||||
"""Representation of a Button control for Ecobee clear hold request."""
|
"""Representation of a Button control for Ecobee clear hold request."""
|
||||||
|
|
||||||
def get_characteristic_types(self) -> list[str]:
|
def get_characteristic_types(self) -> list[str]:
|
||||||
@ -155,7 +167,7 @@ class HomeKitEcobeeClearHoldButton(CharacteristicEntity, ButtonEntity):
|
|||||||
await self.async_put_characteristics({key: val})
|
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."""
|
"""A button users can press to migrate their HomeKit BLE device to Thread."""
|
||||||
|
|
||||||
_attr_entity_category = EntityCategory.CONFIG
|
_attr_entity_category = EntityCategory.CONFIG
|
||||||
@ -179,5 +191,4 @@ class HomeKitProvisionPreferredThreadCredentials(CharacteristicEntity, ButtonEnt
|
|||||||
|
|
||||||
BUTTON_ENTITY_CLASSES: dict[str, type] = {
|
BUTTON_ENTITY_CLASSES: dict[str, type] = {
|
||||||
CharacteristicsTypes.VENDOR_ECOBEE_CLEAR_HOLD: HomeKitEcobeeClearHoldButton,
|
CharacteristicsTypes.VENDOR_ECOBEE_CLEAR_HOLD: HomeKitEcobeeClearHoldButton,
|
||||||
CharacteristicsTypes.THREAD_CONTROL_POINT: HomeKitProvisionPreferredThreadCredentials,
|
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ from aiohomekit.exceptions import (
|
|||||||
EncryptionError,
|
EncryptionError,
|
||||||
)
|
)
|
||||||
from aiohomekit.model import Accessories, Accessory, Transport
|
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 aiohomekit.model.services import Service, ServicesTypes
|
||||||
|
|
||||||
from homeassistant.components.thread.dataset_store import async_get_preferred_dataset
|
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))
|
current_unique_id.add((accessory.aid, service.iid, None))
|
||||||
|
|
||||||
for char in service.characteristics:
|
for char in service.characteristics:
|
||||||
|
if self.pairing.transport != Transport.BLE:
|
||||||
|
if char.type == CharacteristicsTypes.THREAD_CONTROL_POINT:
|
||||||
|
continue
|
||||||
|
|
||||||
current_unique_id.add(
|
current_unique_id.add(
|
||||||
(
|
(
|
||||||
accessory.aid,
|
accessory.aid,
|
||||||
|
@ -70,44 +70,6 @@
|
|||||||
'state': 'unknown',
|
'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({
|
dict({
|
||||||
'entry': dict({
|
'entry': dict({
|
||||||
'aliases': list([
|
'aliases': list([
|
||||||
@ -14200,44 +14162,6 @@
|
|||||||
'state': 'unknown',
|
'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({
|
dict({
|
||||||
'entry': dict({
|
'entry': dict({
|
||||||
'aliases': list([
|
'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.components.thread import async_add_dataset, dataset_store
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
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
|
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."""
|
"""Test that a when a thread provision works the config entry is updated."""
|
||||||
await async_add_dataset(
|
await async_add_dataset(
|
||||||
hass,
|
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.config_entries.async_setup(config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
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(
|
await hass.services.async_call(
|
||||||
"button",
|
"button",
|
||||||
"press",
|
"press",
|
||||||
@ -267,6 +276,13 @@ async def test_thread_provision(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
assert config_entry.data["Connection"] == "CoAP"
|
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:
|
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."""
|
"""Test that when a device 'migrates' but doesn't show up in CoAP, we remain in BLE mode."""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user