diff --git a/homeassistant/components/google/__init__.py b/homeassistant/components/google/__init__.py index 2b7aeadc0ba..3c3d6577e6c 100644 --- a/homeassistant/components/google/__init__.py +++ b/homeassistant/components/google/__init__.py @@ -14,7 +14,6 @@ from gcal_sync.model import DateOrDatetime, Event import voluptuous as vol import yaml -from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_DEVICE_ID, CONF_ENTITIES, @@ -34,8 +33,6 @@ from homeassistant.helpers.entity import generate_entity_id from .api import ApiAuthImpl, get_feature_access from .const import ( - DATA_SERVICE, - DATA_STORE, DOMAIN, EVENT_DESCRIPTION, EVENT_END_DATE, @@ -50,7 +47,7 @@ from .const import ( EVENT_TYPES_CONF, FeatureAccess, ) -from .store import LocalCalendarStore +from .store import GoogleConfigEntry, GoogleRuntimeData, LocalCalendarStore _LOGGER = logging.getLogger(__name__) @@ -139,11 +136,8 @@ ADD_EVENT_SERVICE_SCHEMA = vol.All( ) -async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: +async def async_setup_entry(hass: HomeAssistant, entry: GoogleConfigEntry) -> bool: """Set up Google from a config entry.""" - hass.data.setdefault(DOMAIN, {}) - hass.data[DOMAIN][entry.entry_id] = {} - # Validate google_calendars.yaml (if present) as soon as possible to return # helpful error messages. try: @@ -181,9 +175,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: calendar_service = GoogleCalendarService( ApiAuthImpl(async_get_clientsession(hass), session) ) - hass.data[DOMAIN][entry.entry_id][DATA_SERVICE] = calendar_service - hass.data[DOMAIN][entry.entry_id][DATA_STORE] = LocalCalendarStore( - hass, entry.entry_id + entry.runtime_data = GoogleRuntimeData( + service=calendar_service, + store=LocalCalendarStore(hass, entry.entry_id), ) if entry.unique_id is None: @@ -207,27 +201,25 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return True -def async_entry_has_scopes(entry: ConfigEntry) -> bool: +def async_entry_has_scopes(entry: GoogleConfigEntry) -> bool: """Verify that the config entry desired scope is present in the oauth token.""" access = get_feature_access(entry) token_scopes = entry.data.get("token", {}).get("scope", []) return access.scope in token_scopes -async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: +async def async_unload_entry(hass: HomeAssistant, entry: GoogleConfigEntry) -> bool: """Unload a config entry.""" - if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): - hass.data[DOMAIN].pop(entry.entry_id) - return unload_ok + return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) -async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: +async def async_reload_entry(hass: HomeAssistant, entry: GoogleConfigEntry) -> None: """Reload config entry if the access options change.""" if not async_entry_has_scopes(entry): await hass.config_entries.async_reload(entry.entry_id) -async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: +async def async_remove_entry(hass: HomeAssistant, entry: GoogleConfigEntry) -> None: """Handle removal of a local storage.""" store = LocalCalendarStore(hass, entry.entry_id) await store.async_remove() diff --git a/homeassistant/components/google/api.py b/homeassistant/components/google/api.py index 194c2a0b4a5..efbbec73017 100644 --- a/homeassistant/components/google/api.py +++ b/homeassistant/components/google/api.py @@ -17,7 +17,6 @@ from oauth2client.client import ( ) from homeassistant.components.application_credentials import AuthImplementation -from homeassistant.config_entries import ConfigEntry from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback from homeassistant.helpers import config_entry_oauth2_flow from homeassistant.helpers.event import ( @@ -27,6 +26,7 @@ from homeassistant.helpers.event import ( from homeassistant.util import dt as dt_util from .const import CONF_CALENDAR_ACCESS, DEFAULT_FEATURE_ACCESS, FeatureAccess +from .store import GoogleConfigEntry _LOGGER = logging.getLogger(__name__) @@ -155,7 +155,7 @@ class DeviceFlow: self._listener() -def get_feature_access(config_entry: ConfigEntry) -> FeatureAccess: +def get_feature_access(config_entry: GoogleConfigEntry) -> FeatureAccess: """Return the desired calendar feature access.""" if config_entry.options and CONF_CALENDAR_ACCESS in config_entry.options: return FeatureAccess[config_entry.options[CONF_CALENDAR_ACCESS]] diff --git a/homeassistant/components/google/calendar.py b/homeassistant/components/google/calendar.py index a62d2bf1d6b..6fef46395e8 100644 --- a/homeassistant/components/google/calendar.py +++ b/homeassistant/components/google/calendar.py @@ -37,7 +37,6 @@ from homeassistant.components.calendar import ( extract_offset, is_offset_reached, ) -from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_DEVICE_ID, CONF_ENTITIES, CONF_NAME, CONF_OFFSET from homeassistant.core import HomeAssistant, ServiceCall from homeassistant.exceptions import HomeAssistantError, PlatformNotReady @@ -52,7 +51,6 @@ from . import ( CONF_SEARCH, CONF_TRACK, DEFAULT_CONF_OFFSET, - DOMAIN, YAML_DEVICES, get_calendar_info, load_config, @@ -60,8 +58,6 @@ from . import ( ) from .api import get_feature_access from .const import ( - DATA_SERVICE, - DATA_STORE, EVENT_END_DATE, EVENT_END_DATETIME, EVENT_IN, @@ -72,6 +68,7 @@ from .const import ( FeatureAccess, ) from .coordinator import CalendarQueryUpdateCoordinator, CalendarSyncUpdateCoordinator +from .store import GoogleConfigEntry _LOGGER = logging.getLogger(__name__) @@ -109,7 +106,7 @@ class GoogleCalendarEntityDescription(CalendarEntityDescription): def _get_entity_descriptions( hass: HomeAssistant, - config_entry: ConfigEntry, + config_entry: GoogleConfigEntry, calendar_item: Calendar, calendar_info: Mapping[str, Any], ) -> list[GoogleCalendarEntityDescription]: @@ -202,12 +199,12 @@ def _get_entity_descriptions( async def async_setup_entry( hass: HomeAssistant, - config_entry: ConfigEntry, + config_entry: GoogleConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up the google calendar platform.""" - calendar_service = hass.data[DOMAIN][config_entry.entry_id][DATA_SERVICE] - store = hass.data[DOMAIN][config_entry.entry_id][DATA_STORE] + calendar_service = config_entry.runtime_data.service + store = config_entry.runtime_data.store try: result = await calendar_service.async_list_calendars() except ApiException as err: diff --git a/homeassistant/components/google/config_flow.py b/homeassistant/components/google/config_flow.py index add75f5e95b..15b9ed1c0d8 100644 --- a/homeassistant/components/google/config_flow.py +++ b/homeassistant/components/google/config_flow.py @@ -11,12 +11,7 @@ from gcal_sync.api import GoogleCalendarService from gcal_sync.exceptions import ApiException, ApiForbiddenException import voluptuous as vol -from homeassistant.config_entries import ( - SOURCE_REAUTH, - ConfigEntry, - ConfigFlowResult, - OptionsFlow, -) +from homeassistant.config_entries import SOURCE_REAUTH, ConfigFlowResult, OptionsFlow from homeassistant.core import callback from homeassistant.helpers import config_entry_oauth2_flow from homeassistant.helpers.aiohttp_client import async_get_clientsession @@ -38,6 +33,7 @@ from .const import ( CredentialType, FeatureAccess, ) +from .store import GoogleConfigEntry _LOGGER = logging.getLogger(__name__) @@ -240,7 +236,7 @@ class OAuth2FlowHandler( @staticmethod @callback def async_get_options_flow( - config_entry: ConfigEntry, + config_entry: GoogleConfigEntry, ) -> OptionsFlow: """Create an options flow.""" return OptionsFlowHandler() diff --git a/homeassistant/components/google/const.py b/homeassistant/components/google/const.py index 1e0b2fc910b..6613668cf91 100644 --- a/homeassistant/components/google/const.py +++ b/homeassistant/components/google/const.py @@ -9,9 +9,7 @@ DOMAIN = "google" CONF_CALENDAR_ACCESS = "calendar_access" CONF_CREDENTIAL_TYPE = "credential_type" DATA_CALENDARS = "calendars" -DATA_SERVICE = "service" DATA_CONFIG = "config" -DATA_STORE = "store" class FeatureAccess(Enum): diff --git a/homeassistant/components/google/coordinator.py b/homeassistant/components/google/coordinator.py index 4a8a3d9f167..9f51c60b069 100644 --- a/homeassistant/components/google/coordinator.py +++ b/homeassistant/components/google/coordinator.py @@ -14,12 +14,13 @@ from gcal_sync.sync import CalendarEventSyncManager from gcal_sync.timeline import Timeline from ical.iter import SortableItemValue -from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.util import dt as dt_util +from .store import GoogleConfigEntry + _LOGGER = logging.getLogger(__name__) MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=15) @@ -47,12 +48,12 @@ def _truncate_timeline(timeline: Timeline, max_events: int) -> Timeline: class CalendarSyncUpdateCoordinator(DataUpdateCoordinator[Timeline]): """Coordinator for calendar RPC calls that use an efficient sync.""" - config_entry: ConfigEntry + config_entry: GoogleConfigEntry def __init__( self, hass: HomeAssistant, - config_entry: ConfigEntry, + config_entry: GoogleConfigEntry, sync: CalendarEventSyncManager, name: str, ) -> None: @@ -108,12 +109,12 @@ class CalendarQueryUpdateCoordinator(DataUpdateCoordinator[list[Event]]): for limitations in the calendar API for supporting search. """ - config_entry: ConfigEntry + config_entry: GoogleConfigEntry def __init__( self, hass: HomeAssistant, - config_entry: ConfigEntry, + config_entry: GoogleConfigEntry, calendar_service: GoogleCalendarService, name: str, calendar_id: str, diff --git a/homeassistant/components/google/diagnostics.py b/homeassistant/components/google/diagnostics.py index 1a6f498b4cd..6dc6e321a23 100644 --- a/homeassistant/components/google/diagnostics.py +++ b/homeassistant/components/google/diagnostics.py @@ -4,11 +4,10 @@ import datetime from typing import Any from homeassistant.components.diagnostics import async_redact_data -from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.util import dt as dt_util -from .const import DATA_STORE, DOMAIN +from .store import GoogleConfigEntry TO_REDACT = { "id", @@ -40,7 +39,7 @@ def redact_store(data: dict[str, Any]) -> dict[str, Any]: async def async_get_config_entry_diagnostics( - hass: HomeAssistant, config_entry: ConfigEntry + hass: HomeAssistant, config_entry: GoogleConfigEntry ) -> dict[str, Any]: """Return diagnostics for a config entry.""" payload: dict[str, Any] = { @@ -49,7 +48,7 @@ async def async_get_config_entry_diagnostics( "system_timezone": str(datetime.datetime.now().astimezone().tzinfo), } - store = hass.data[DOMAIN][config_entry.entry_id][DATA_STORE] - data = await store.async_load() - payload["store"] = redact_store(data) + store = config_entry.runtime_data.store + if data := await store.async_load(): + payload["store"] = redact_store(data) return payload diff --git a/homeassistant/components/google/quality_scale.yaml b/homeassistant/components/google/quality_scale.yaml index 9ef6abdba90..43c86c54e28 100644 --- a/homeassistant/components/google/quality_scale.yaml +++ b/homeassistant/components/google/quality_scale.yaml @@ -40,11 +40,7 @@ rules: to increase functionality such as checking for the specific contents of a unique id assigned to a config entry. docs-actions: done - runtime-data: - status: todo - comment: | - The integration stores config entry data in `hass.data` and should be - updated to use `runtime_data`. + runtime-data: done # Silver log-when-unavailable: done diff --git a/homeassistant/components/google/store.py b/homeassistant/components/google/store.py index c4d9e4c3e9c..4936a86f384 100644 --- a/homeassistant/components/google/store.py +++ b/homeassistant/components/google/store.py @@ -2,11 +2,14 @@ from __future__ import annotations +from dataclasses import dataclass import logging from typing import Any +from gcal_sync.api import GoogleCalendarService from gcal_sync.store import CalendarStore +from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.storage import Store @@ -19,6 +22,16 @@ STORAGE_VERSION = 1 # Buffer writes every few minutes (plus guaranteed to be written at shutdown) STORAGE_SAVE_DELAY_SECONDS = 120 +type GoogleConfigEntry = ConfigEntry[GoogleRuntimeData] + + +@dataclass +class GoogleRuntimeData: + """Google runtime data.""" + + service: GoogleCalendarService + store: LocalCalendarStore + class LocalCalendarStore(CalendarStore): """Storage for local persistence of calendar and event data."""