From 7348a1fd0c9c11c3ca1a75f0c1f2e186609ec2de Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 25 Jul 2024 04:06:55 -0500 Subject: [PATCH] Convert homekit to use entry.runtime_data (#122533) --- homeassistant/components/homekit/__init__.py | 34 +++++++++++-------- .../components/homekit/diagnostics.py | 9 ++--- homeassistant/components/homekit/models.py | 4 +++ homeassistant/components/homekit/util.py | 5 +-- tests/components/homekit/test_homekit.py | 6 +++- tests/components/homekit/test_util.py | 7 +++- 6 files changed, 40 insertions(+), 25 deletions(-) diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index 36372a5d16f..3f633c2ec59 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -127,7 +127,7 @@ from .const import ( SIGNAL_RELOAD_ENTITIES, ) from .iidmanager import AccessoryIIDStorage -from .models import HomeKitEntryData +from .models import HomeKitConfigEntry, HomeKitEntryData from .type_triggers import DeviceTriggerAccessory from .util import ( accessory_friendly_name, @@ -223,8 +223,12 @@ UNPAIR_SERVICE_SCHEMA = vol.All( def _async_all_homekit_instances(hass: HomeAssistant) -> list[HomeKit]: """All active HomeKit instances.""" - domain_data: dict[str, HomeKitEntryData] = hass.data[DOMAIN] - return [data.homekit for data in domain_data.values()] + hk_data: HomeKitEntryData | None + return [ + hk_data.homekit + for entry in hass.config_entries.async_entries(DOMAIN) + if (hk_data := getattr(entry, "runtime_data", None)) + ] def _async_get_imported_entries_indices( @@ -246,7 +250,6 @@ def _async_get_imported_entries_indices( async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the HomeKit from yaml.""" - hass.data[DOMAIN] = {} hass.data[PERSIST_LOCK_DATA] = asyncio.Lock() # Initialize the loader before loading entries to ensure @@ -316,7 +319,7 @@ def _async_update_config_entry_from_yaml( return True -async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: +async def async_setup_entry(hass: HomeAssistant, entry: HomeKitConfigEntry) -> bool: """Set up HomeKit from a config entry.""" _async_import_options_from_data_if_missing(hass, entry) @@ -372,7 +375,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: entry_data = HomeKitEntryData( homekit=homekit, pairing_qr=None, pairing_qr_secret=None ) - hass.data[DOMAIN][entry.entry_id] = entry_data + entry.runtime_data = entry_data async def _async_start_homekit(hass: HomeAssistant) -> None: await homekit.async_start() @@ -382,17 +385,19 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return True -async def _async_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None: +async def _async_update_listener( + hass: HomeAssistant, entry: HomeKitConfigEntry +) -> None: """Handle options update.""" if entry.source == SOURCE_IMPORT: return await hass.config_entries.async_reload(entry.entry_id) -async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: +async def async_unload_entry(hass: HomeAssistant, entry: HomeKitConfigEntry) -> bool: """Unload a config entry.""" async_dismiss_setup_message(hass, entry.entry_id) - entry_data: HomeKitEntryData = hass.data[DOMAIN][entry.entry_id] + entry_data = entry.runtime_data homekit = entry_data.homekit if homekit.status == STATUS_RUNNING: @@ -409,12 +414,10 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: await asyncio.sleep(PORT_CLEANUP_CHECK_INTERVAL_SECS) - hass.data[DOMAIN].pop(entry.entry_id) - return True -async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: +async def async_remove_entry(hass: HomeAssistant, entry: HomeKitConfigEntry) -> None: """Remove a config entry.""" await hass.async_add_executor_job( remove_state_files_for_entry_id, hass, entry.entry_id @@ -423,7 +426,7 @@ async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: @callback def _async_import_options_from_data_if_missing( - hass: HomeAssistant, entry: ConfigEntry + hass: HomeAssistant, entry: HomeKitConfigEntry ) -> None: options = deepcopy(dict(entry.options)) data = deepcopy(dict(entry.data)) @@ -1198,9 +1201,10 @@ class HomeKitPairingQRView(HomeAssistantView): raise Unauthorized entry_id, secret = request.query_string.split("-") hass = request.app[KEY_HASS] - domain_data: dict[str, HomeKitEntryData] = hass.data[DOMAIN] + entry_data: HomeKitEntryData | None if ( - not (entry_data := domain_data.get(entry_id)) + not (entry := hass.config_entries.async_get_entry(entry_id)) + or not (entry_data := getattr(entry, "runtime_data", None)) or not secret or not entry_data.pairing_qr_secret or secret != entry_data.pairing_qr_secret diff --git a/homeassistant/components/homekit/diagnostics.py b/homeassistant/components/homekit/diagnostics.py index f31dd268b26..eb062735ad0 100644 --- a/homeassistant/components/homekit/diagnostics.py +++ b/homeassistant/components/homekit/diagnostics.py @@ -8,22 +8,19 @@ from pyhap.accessory_driver import AccessoryDriver from pyhap.state import State from homeassistant.components.diagnostics import async_redact_data -from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from .accessories import HomeAccessory, HomeBridge -from .const import DOMAIN -from .models import HomeKitEntryData +from .models import HomeKitConfigEntry TO_REDACT = {"access_token", "entity_picture"} async def async_get_config_entry_diagnostics( - hass: HomeAssistant, entry: ConfigEntry + hass: HomeAssistant, entry: HomeKitConfigEntry ) -> dict[str, Any]: """Return diagnostics for a config entry.""" - entry_data: HomeKitEntryData = hass.data[DOMAIN][entry.entry_id] - homekit = entry_data.homekit + homekit = entry.runtime_data.homekit data: dict[str, Any] = { "status": homekit.status, "config-entry": { diff --git a/homeassistant/components/homekit/models.py b/homeassistant/components/homekit/models.py index f3fa8b7504c..9b647928fdd 100644 --- a/homeassistant/components/homekit/models.py +++ b/homeassistant/components/homekit/models.py @@ -5,9 +5,13 @@ from __future__ import annotations from dataclasses import dataclass from typing import TYPE_CHECKING +from homeassistant.config_entries import ConfigEntry + if TYPE_CHECKING: from . import HomeKit +type HomeKitConfigEntry = ConfigEntry[HomeKitEntryData] + @dataclass class HomeKitEntryData: diff --git a/homeassistant/components/homekit/util.py b/homeassistant/components/homekit/util.py index d521fd6db0c..a4566efaa35 100644 --- a/homeassistant/components/homekit/util.py +++ b/homeassistant/components/homekit/util.py @@ -106,7 +106,7 @@ from .const import ( VIDEO_CODEC_H264_V4L2M2M, VIDEO_CODEC_LIBX264, ) -from .models import HomeKitEntryData +from .models import HomeKitConfigEntry _LOGGER = logging.getLogger(__name__) @@ -366,7 +366,8 @@ def async_show_setup_message( url.svg(buffer, scale=5, module_color="#000", background="#FFF") pairing_secret = secrets.token_hex(32) - entry_data: HomeKitEntryData = hass.data[DOMAIN][entry_id] + entry = cast(HomeKitConfigEntry, hass.config_entries.async_get_entry(entry_id)) + entry_data = entry.runtime_data entry_data.pairing_qr = buffer.getvalue() entry_data.pairing_qr_secret = pairing_secret diff --git a/tests/components/homekit/test_homekit.py b/tests/components/homekit/test_homekit.py index 9653acdfabb..93458724c5e 100644 --- a/tests/components/homekit/test_homekit.py +++ b/tests/components/homekit/test_homekit.py @@ -1843,7 +1843,11 @@ async def test_homekit_uses_system_zeroconf(hass: HomeAssistant, hk_driver) -> N entry.add_to_hass(hass) assert await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - entry_data: HomeKitEntryData = hass.data[DOMAIN][entry.entry_id] + # New tests should not access runtime data. + # Do not use this pattern for new tests. + entry_data: HomeKitEntryData = hass.config_entries.async_get_entry( + entry.entry_id + ).runtime_data assert entry_data.homekit.driver.advertiser == system_async_zc assert await hass.config_entries.async_unload(entry.entry_id) await hass.async_block_till_done() diff --git a/tests/components/homekit/test_util.py b/tests/components/homekit/test_util.py index ff6ee0c6aa8..4939511166f 100644 --- a/tests/components/homekit/test_util.py +++ b/tests/components/homekit/test_util.py @@ -257,7 +257,12 @@ async def test_async_show_setup_msg(hass: HomeAssistant, hk_driver) -> None: hass, entry.entry_id, "bridge_name", pincode, "X-HM://0" ) await hass.async_block_till_done() - entry_data: HomeKitEntryData = hass.data[DOMAIN][entry.entry_id] + + # New tests should not access runtime data. + # Do not use this pattern for new tests. + entry_data: HomeKitEntryData = hass.config_entries.async_get_entry( + entry.entry_id + ).runtime_data assert entry_data.pairing_qr_secret assert entry_data.pairing_qr