Add a repair issue for Yale Home users using the August integration (#124895)

The Yale Home brand will stop working with the August integration very
soon. Users must migrate to the Yale integration to avoid an interruption in service.
pull/124880/head
J. Nick Koston 2024-08-29 21:35:19 -10:00 committed by Bram Kragten
parent ee9e3fe27b
commit 8ab8f7a740
5 changed files with 73 additions and 7 deletions

View File

@ -6,15 +6,16 @@ from pathlib import Path
from typing import cast from typing import cast
from aiohttp import ClientResponseError from aiohttp import ClientResponseError
from yalexs.const import Brand
from yalexs.exceptions import AugustApiAIOHTTPError from yalexs.exceptions import AugustApiAIOHTTPError
from yalexs.manager.exceptions import CannotConnect, InvalidAuth, RequireValidation from yalexs.manager.exceptions import CannotConnect, InvalidAuth, RequireValidation
from yalexs.manager.gateway import Config as YaleXSConfig from yalexs.manager.gateway import Config as YaleXSConfig
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EVENT_HOMEASSISTANT_STOP from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import device_registry as dr from homeassistant.helpers import device_registry as dr, issue_registry as ir
from .const import DOMAIN, PLATFORMS from .const import DOMAIN, PLATFORMS
from .data import AugustData from .data import AugustData
@ -24,6 +25,26 @@ from .util import async_create_august_clientsession
type AugustConfigEntry = ConfigEntry[AugustData] type AugustConfigEntry = ConfigEntry[AugustData]
@callback
def _async_create_yale_brand_migration_issue(
hass: HomeAssistant, entry: AugustConfigEntry
) -> None:
"""Create an issue for a brand migration."""
ir.async_create_issue(
hass,
DOMAIN,
"yale_brand_migration",
breaks_in_ha_version="2024.9",
learn_more_url="https://www.home-assistant.io/integrations/yale",
translation_key="yale_brand_migration",
is_fixable=False,
severity=ir.IssueSeverity.CRITICAL,
translation_placeholders={
"migrate_url": "https://my.home-assistant.io/redirect/config_flow_start?domain=yale"
},
)
async def async_setup_entry(hass: HomeAssistant, entry: AugustConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: AugustConfigEntry) -> bool:
"""Set up August from a config entry.""" """Set up August from a config entry."""
session = async_create_august_clientsession(hass) session = async_create_august_clientsession(hass)
@ -40,6 +61,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: AugustConfigEntry) -> bo
return True return True
async def async_remove_entry(hass: HomeAssistant, entry: AugustConfigEntry) -> None:
"""Remove an August config entry."""
ir.async_delete_issue(hass, DOMAIN, "yale_brand_migration")
async def async_unload_entry(hass: HomeAssistant, entry: AugustConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, entry: AugustConfigEntry) -> bool:
"""Unload a config entry.""" """Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
@ -51,6 +77,8 @@ async def async_setup_august(
"""Set up the August component.""" """Set up the August component."""
config = cast(YaleXSConfig, entry.data) config = cast(YaleXSConfig, entry.data)
await august_gateway.async_setup(config) await august_gateway.async_setup(config)
if august_gateway.api.brand == Brand.YALE_HOME:
_async_create_yale_brand_migration_issue(hass, entry)
await august_gateway.async_authenticate() await august_gateway.async_authenticate()
await august_gateway.async_refresh_access_token_if_needed() await august_gateway.async_refresh_access_token_if_needed()
data = entry.runtime_data = AugustData(hass, august_gateway) data = entry.runtime_data = AugustData(hass, august_gateway)

View File

@ -9,7 +9,7 @@ from typing import Any
import aiohttp import aiohttp
import voluptuous as vol import voluptuous as vol
from yalexs.authenticator_common import ValidationResult from yalexs.authenticator_common import ValidationResult
from yalexs.const import BRANDS_WITHOUT_OAUTH, DEFAULT_BRAND from yalexs.const import BRANDS_WITHOUT_OAUTH, DEFAULT_BRAND, Brand
from yalexs.manager.exceptions import CannotConnect, InvalidAuth, RequireValidation from yalexs.manager.exceptions import CannotConnect, InvalidAuth, RequireValidation
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
@ -28,6 +28,12 @@ from .const import (
from .gateway import AugustGateway from .gateway import AugustGateway
from .util import async_create_august_clientsession from .util import async_create_august_clientsession
# The Yale Home Brand is not supported by the August integration
# anymore and should migrate to the Yale integration
AVAILABLE_BRANDS = BRANDS_WITHOUT_OAUTH.copy()
del AVAILABLE_BRANDS[Brand.YALE_HOME]
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -118,7 +124,7 @@ class AugustConfigFlow(ConfigFlow, domain=DOMAIN):
vol.Required( vol.Required(
CONF_BRAND, CONF_BRAND,
default=self._user_auth_details.get(CONF_BRAND, DEFAULT_BRAND), default=self._user_auth_details.get(CONF_BRAND, DEFAULT_BRAND),
): vol.In(BRANDS_WITHOUT_OAUTH), ): vol.In(AVAILABLE_BRANDS),
vol.Required( vol.Required(
CONF_LOGIN_METHOD, CONF_LOGIN_METHOD,
default=self._user_auth_details.get( default=self._user_auth_details.get(

View File

@ -1,4 +1,10 @@
{ {
"issues": {
"yale_brand_migration": {
"title": "Yale Home has a new integration",
"description": "Add the [Yale integration]({migrate_url}), and remove the August integration as soon as possible to avoid an interruption in service. The Yale Home brand will stop working with the August integration soon and will be removed in a future release."
}
},
"config": { "config": {
"error": { "error": {
"unhandled": "Unhandled error: {error}", "unhandled": "Unhandled error: {error}",

View File

@ -385,7 +385,7 @@ async def test_switching_brands(hass: HomeAssistant) -> None:
result2 = await hass.config_entries.flow.async_configure( result2 = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{ {
CONF_BRAND: "yale_home", CONF_BRAND: "yale_access",
CONF_LOGIN_METHOD: "email", CONF_LOGIN_METHOD: "email",
CONF_USERNAME: "my@email.tld", CONF_USERNAME: "my@email.tld",
CONF_PASSWORD: "test-password", CONF_PASSWORD: "test-password",
@ -396,4 +396,4 @@ async def test_switching_brands(hass: HomeAssistant) -> None:
assert result2["type"] is FlowResultType.ABORT assert result2["type"] is FlowResultType.ABORT
assert result2["reason"] == "reauth_successful" assert result2["reason"] == "reauth_successful"
assert len(mock_setup_entry.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1
assert entry.data[CONF_BRAND] == "yale_home" assert entry.data[CONF_BRAND] == "yale_access"

View File

@ -5,6 +5,7 @@ from unittest.mock import Mock, patch
from aiohttp import ClientResponseError from aiohttp import ClientResponseError
import pytest import pytest
from yalexs.authenticator_common import AuthenticationState from yalexs.authenticator_common import AuthenticationState
from yalexs.const import Brand
from yalexs.exceptions import AugustApiAIOHTTPError from yalexs.exceptions import AugustApiAIOHTTPError
from homeassistant.components.august.const import DOMAIN from homeassistant.components.august.const import DOMAIN
@ -20,7 +21,11 @@ from homeassistant.const import (
) )
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, entity_registry as er from homeassistant.helpers import (
device_registry as dr,
entity_registry as er,
issue_registry as ir,
)
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from .mocks import ( from .mocks import (
@ -420,3 +425,24 @@ async def test_device_remove_devices(
) )
response = await client.remove_device(dead_device_entry.id, config_entry.entry_id) response = await client.remove_device(dead_device_entry.id, config_entry.entry_id)
assert response["success"] assert response["success"]
async def test_brand_migration_issue(hass: HomeAssistant) -> None:
"""Test creating and removing the brand migration issue."""
august_operative_lock = await _mock_operative_august_lock_detail(hass)
config_entry = await _create_august_with_devices(
hass, [august_operative_lock], brand=Brand.YALE_HOME
)
assert config_entry.state is ConfigEntryState.LOADED
issue_reg = ir.async_get(hass)
issue_entry = issue_reg.async_get_issue(DOMAIN, "yale_brand_migration")
assert issue_entry
assert issue_entry.severity == ir.IssueSeverity.CRITICAL
assert issue_entry.translation_placeholders == {
"migrate_url": "https://my.home-assistant.io/redirect/config_flow_start?domain=yale"
}
await hass.config_entries.async_remove(config_entry.entry_id)
assert not issue_reg.async_get_issue(DOMAIN, "yale_brand_migration")