Skip to content
Permalink
Browse files
Deprecate google calendar configuration.yaml (#72288)
* Deprecate google calendar configuration.yaml

* Remove unused translations

* Enable strict type checking and address pr feedback

* Move default hass.data init to `async_setup`
  • Loading branch information
allenporter committed May 22, 2022
1 parent 9c3f949 commit e6ffae8bd3570c61ce3b8d9f15a3d3b678537650
Showing 10 changed files with 267 additions and 47 deletions.
@@ -101,6 +101,7 @@ homeassistant.components.geo_location.*
homeassistant.components.geocaching.*
homeassistant.components.gios.*
homeassistant.components.goalzero.*
homeassistant.components.google.*
homeassistant.components.greeneye_monitor.*
homeassistant.components.group.*
homeassistant.components.guardian.*
@@ -97,23 +97,27 @@


CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
vol.Required(CONF_CLIENT_ID): cv.string,
vol.Required(CONF_CLIENT_SECRET): cv.string,
vol.Optional(CONF_TRACK_NEW, default=True): cv.boolean,
vol.Optional(CONF_CALENDAR_ACCESS, default="read_write"): cv.enum(
FeatureAccess
),
}
)
},
vol.All(
cv.deprecated(DOMAIN),
{
DOMAIN: vol.Schema(
{
vol.Required(CONF_CLIENT_ID): cv.string,
vol.Required(CONF_CLIENT_SECRET): cv.string,
vol.Optional(CONF_TRACK_NEW, default=True): cv.boolean,
vol.Optional(CONF_CALENDAR_ACCESS, default="read_write"): cv.enum(
FeatureAccess
),
}
)
},
),
extra=vol.ALLOW_EXTRA,
)

_SINGLE_CALSEARCH_CONFIG = vol.All(
cv.deprecated(CONF_MAX_RESULTS),
cv.deprecated(CONF_TRACK),
vol.Schema(
{
vol.Required(CONF_NAME): cv.string,
@@ -160,6 +164,9 @@

async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Google component."""
if DOMAIN not in config:
return True

conf = config.get(DOMAIN, {})
hass.data[DOMAIN] = {DATA_CONFIG: conf}

@@ -189,11 +196,22 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
},
)
)

_LOGGER.warning(
"Configuration of Google Calendar in YAML in configuration.yaml is "
"is deprecated and will be removed in a future release; Your existing "
"OAuth Application Credentials and other settings have been imported "
"into the UI automatically and can be safely removed from your "
"configuration.yaml file"
)

return True


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Google from a config entry."""
hass.data.setdefault(DOMAIN, {})
async_upgrade_entry(hass, entry)
implementation = (
await config_entry_oauth2_flow.async_get_config_entry_implementation(
hass, entry
@@ -216,8 +234,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
except aiohttp.ClientError as err:
raise ConfigEntryNotReady from err

access = get_feature_access(hass)
if access.scope not in session.token.get("scope", []):
access = FeatureAccess[entry.options[CONF_CALENDAR_ACCESS]]
token_scopes = session.token.get("scope", [])
if access.scope not in token_scopes:
_LOGGER.debug("Scope '%s' not in scopes '%s'", access.scope, token_scopes)
raise ConfigEntryAuthFailed(
"Required scopes are not available, reauth required"
)
@@ -226,25 +246,53 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
)
hass.data[DOMAIN][DATA_SERVICE] = calendar_service

track_new = hass.data[DOMAIN][DATA_CONFIG].get(CONF_TRACK_NEW, True)
await async_setup_services(hass, track_new, calendar_service)
await async_setup_services(hass, calendar_service)
# Only expose the add event service if we have the correct permissions
if access is FeatureAccess.read_write:
await async_setup_add_event_service(hass, calendar_service)

hass.config_entries.async_setup_platforms(entry, PLATFORMS)

# Reload entry when options are updated
entry.async_on_unload(entry.add_update_listener(async_reload_entry))

return True


def async_upgrade_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Upgrade the config entry if needed."""
if DATA_CONFIG not in hass.data[DOMAIN] and entry.options:
return

options = (
entry.options
if entry.options
else {
CONF_CALENDAR_ACCESS: get_feature_access(hass).name,
}
)
disable_new_entities = (
not hass.data[DOMAIN].get(DATA_CONFIG, {}).get(CONF_TRACK_NEW, True)
)
hass.config_entries.async_update_entry(
entry,
options=options,
pref_disable_new_entities=disable_new_entities,
)


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)


async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Reload the config entry when it changed."""
await hass.config_entries.async_reload(entry.entry_id)


async def async_setup_services(
hass: HomeAssistant,
track_new: bool,
calendar_service: GoogleCalendarService,
) -> None:
"""Set up the service listeners."""
@@ -256,10 +304,7 @@ async def async_setup_services(
async def _found_calendar(calendar_item: Calendar) -> None:
calendar = get_calendar_info(
hass,
{
**calendar_item.dict(exclude_unset=True),
CONF_TRACK: track_new,
},
calendar_item.dict(exclude_unset=True),
)
calendar_id = calendar_item.id
# Populate the yaml file with all discovered calendars
@@ -363,7 +408,6 @@ def get_calendar_info(
CONF_CAL_ID: calendar["id"],
CONF_ENTITIES: [
{
CONF_TRACK: calendar["track"],
CONF_NAME: calendar["summary"],
CONF_DEVICE_ID: generate_entity_id(
"{}", calendar["summary"], hass=hass
@@ -6,7 +6,7 @@
import datetime
import logging
import time
from typing import Any
from typing import Any, cast

import aiohttp
from gcal_sync.auth import AbstractAuth
@@ -76,12 +76,12 @@ def __init__(
@property
def verification_url(self) -> str:
"""Return the verification url that the user should visit to enter the code."""
return self._device_flow_info.verification_url
return self._device_flow_info.verification_url # type: ignore[no-any-return]

@property
def user_code(self) -> str:
"""Return the code that the user should enter at the verification url."""
return self._device_flow_info.user_code
return self._device_flow_info.user_code # type: ignore[no-any-return]

async def start_exchange_task(
self, finished_cb: Callable[[Credentials | None], Awaitable[None]]
@@ -131,10 +131,13 @@ def get_feature_access(hass: HomeAssistant) -> FeatureAccess:
"""Return the desired calendar feature access."""
# This may be called during config entry setup without integration setup running when there
# is no google entry in configuration.yaml
return (
hass.data.get(DOMAIN, {})
.get(DATA_CONFIG, {})
.get(CONF_CALENDAR_ACCESS, DEFAULT_FEATURE_ACCESS)
return cast(
FeatureAccess,
(
hass.data.get(DOMAIN, {})
.get(DATA_CONFIG, {})
.get(CONF_CALENDAR_ACCESS, DEFAULT_FEATURE_ACCESS)
),
)


@@ -157,7 +160,7 @@ async def async_create_device_flow(
return DeviceFlow(hass, oauth_flow, device_flow_info)


class ApiAuthImpl(AbstractAuth):
class ApiAuthImpl(AbstractAuth): # type: ignore[misc]
"""Authentication implementation for google calendar api library."""

def __init__(
@@ -172,10 +175,10 @@ def __init__(
async def async_get_access_token(self) -> str:
"""Return a valid access token."""
await self._session.async_ensure_token_valid()
return self._session.token["access_token"]
return cast(str, self._session.token["access_token"])


class AccessTokenAuthImpl(AbstractAuth):
class AccessTokenAuthImpl(AbstractAuth): # type: ignore[misc]
"""Authentication implementation used during config flow, without refresh.
This exists to allow the config flow to use the API before it has fully
@@ -1,4 +1,5 @@
"""Support for Google Calendar Search binary sensors."""

from __future__ import annotations

import copy
@@ -89,14 +90,25 @@ def _async_setup_entities(
) -> None:
calendar_service = hass.data[DOMAIN][DATA_SERVICE]
entities = []
num_entities = len(disc_info[CONF_ENTITIES])
for data in disc_info[CONF_ENTITIES]:
if not data[CONF_TRACK]:
continue
entity_id = generate_entity_id(
ENTITY_ID_FORMAT, data[CONF_DEVICE_ID], hass=hass
)
entity_enabled = data.get(CONF_TRACK, True)
entity_name = data[CONF_DEVICE_ID]
entity_id = generate_entity_id(ENTITY_ID_FORMAT, entity_name, hass=hass)
calendar_id = disc_info[CONF_CAL_ID]
if num_entities > 1:
# The google_calendars.yaml file lets users add multiple entities for
# the same calendar id and needs additional disambiguation
unique_id = f"{calendar_id}-{entity_name}"
else:
unique_id = calendar_id
entity = GoogleCalendarEntity(
calendar_service, disc_info[CONF_CAL_ID], data, entity_id
calendar_service,
disc_info[CONF_CAL_ID],
data,
entity_id,
unique_id,
entity_enabled,
)
entities.append(entity)

@@ -112,6 +124,8 @@ def __init__(
calendar_id: str,
data: dict[str, Any],
entity_id: str,
unique_id: str,
entity_enabled: bool,
) -> None:
"""Create the Calendar event device."""
self._calendar_service = calendar_service
@@ -123,6 +137,8 @@ def __init__(
self._offset = data.get(CONF_OFFSET, DEFAULT_CONF_OFFSET)
self._offset_value: timedelta | None = None
self.entity_id = entity_id
self._attr_unique_id = unique_id
self._attr_entity_registry_enabled_default = entity_enabled

@property
def extra_state_attributes(self) -> dict[str, bool]:
@@ -152,7 +168,7 @@ def _event_filter(self, event: Event) -> bool:
"""Return True if the event is visible."""
if self._ignore_availability:
return True
return event.transparency == OPAQUE
return event.transparency == OPAQUE # type: ignore[no-any-return]

async def async_get_events(
self, hass: HomeAssistant, start_date: datetime, end_date: datetime

0 comments on commit e6ffae8

Please sign in to comment.