Deprecate HEOS sign_in and sign_out actions (#134616)

This commit is contained in:
Andrew Sayre 2025-01-04 17:13:46 -06:00 committed by GitHub
parent 11d80065ef
commit f68c16586d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 80 additions and 18 deletions

View File

@ -27,10 +27,12 @@ from homeassistant.const import (
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError
from homeassistant.helpers import device_registry as dr, entity_registry as er
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import (
async_dispatcher_connect,
async_dispatcher_send,
)
from homeassistant.helpers.typing import ConfigType
from homeassistant.util import Throttle
from . import services
@ -46,6 +48,8 @@ PLATFORMS = [Platform.MEDIA_PLAYER]
MIN_UPDATE_SOURCES = timedelta(seconds=1)
CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)
_LOGGER = logging.getLogger(__name__)
@ -62,6 +66,12 @@ class HeosRuntimeData:
type HeosConfigEntry = ConfigEntry[HeosRuntimeData]
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the HEOS component."""
services.register(hass)
return True
async def async_setup_entry(hass: HomeAssistant, entry: HeosConfigEntry) -> bool:
"""Initialize config entry which represents the HEOS controller."""
# For backwards compat
@ -141,7 +151,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: HeosConfigEntry) -> bool
controller_manager, group_manager, source_manager, players
)
services.register(hass, controller)
group_manager.connect_update()
entry.async_on_unload(group_manager.disconnect_update)
@ -153,9 +162,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: HeosConfigEntry) -> bool
async def async_unload_entry(hass: HomeAssistant, entry: HeosConfigEntry) -> bool:
"""Unload a config entry."""
await entry.runtime_data.controller_manager.disconnect()
services.remove(hass)
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)

View File

@ -1,8 +1,6 @@
rules:
# Bronze
action-setup:
status: todo
comment: Future enhancement to move custom actions for login/out into an options flow.
action-setup: done
appropriate-polling:
status: done
comment: Integration is a local push integration

View File

@ -1,13 +1,14 @@
"""Services for the HEOS integration."""
import functools
import logging
from pyheos import CommandFailedError, Heos, HeosError, const
import voluptuous as vol
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.helpers import config_validation as cv
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv, issue_registry as ir
from .const import (
ATTR_PASSWORD,
@ -26,30 +27,50 @@ HEOS_SIGN_IN_SCHEMA = vol.Schema(
HEOS_SIGN_OUT_SCHEMA = vol.Schema({})
def register(hass: HomeAssistant, controller: Heos):
def register(hass: HomeAssistant):
"""Register HEOS services."""
hass.services.async_register(
DOMAIN,
SERVICE_SIGN_IN,
functools.partial(_sign_in_handler, controller),
_sign_in_handler,
schema=HEOS_SIGN_IN_SCHEMA,
)
hass.services.async_register(
DOMAIN,
SERVICE_SIGN_OUT,
functools.partial(_sign_out_handler, controller),
_sign_out_handler,
schema=HEOS_SIGN_OUT_SCHEMA,
)
def remove(hass: HomeAssistant):
"""Unregister HEOS services."""
hass.services.async_remove(DOMAIN, SERVICE_SIGN_IN)
hass.services.async_remove(DOMAIN, SERVICE_SIGN_OUT)
def _get_controller(hass: HomeAssistant) -> Heos:
"""Get the HEOS controller instance."""
_LOGGER.warning(
"Actions 'heos.sign_in' and 'heos.sign_out' are deprecated and will be removed in the 2025.8.0 release"
)
ir.async_create_issue(
hass,
DOMAIN,
"sign_in_out_deprecated",
breaks_in_ha_version="2025.8.0",
is_fixable=False,
severity=ir.IssueSeverity.WARNING,
translation_key="sign_in_out_deprecated",
)
entry = hass.config_entries.async_entry_for_domain_unique_id(DOMAIN, DOMAIN)
if not entry or not entry.state == ConfigEntryState.LOADED:
raise HomeAssistantError(
translation_domain=DOMAIN, translation_key="integration_not_loaded"
)
return entry.runtime_data.controller_manager.controller
async def _sign_in_handler(controller: Heos, service: ServiceCall) -> None:
async def _sign_in_handler(service: ServiceCall) -> None:
"""Sign in to the HEOS account."""
controller = _get_controller(service.hass)
if controller.connection_state != const.STATE_CONNECTED:
_LOGGER.error("Unable to sign in because HEOS is not connected")
return
@ -63,8 +84,10 @@ async def _sign_in_handler(controller: Heos, service: ServiceCall) -> None:
_LOGGER.error("Unable to sign in: %s", err)
async def _sign_out_handler(controller: Heos, service: ServiceCall) -> None:
async def _sign_out_handler(service: ServiceCall) -> None:
"""Sign out of the HEOS account."""
controller = _get_controller(service.hass)
if controller.connection_state != const.STATE_CONNECTED:
_LOGGER.error("Unable to sign out because HEOS is not connected")
return

View File

@ -89,5 +89,16 @@
"name": "Sign out",
"description": "Signs out of the HEOS account."
}
},
"exceptions": {
"integration_not_loaded": {
"message": "The HEOS integration is not loaded"
}
},
"issues": {
"sign_in_out_deprecated": {
"title": "HEOS Actions Deprecated",
"description": "Actions 'heos.sign_in' and 'heos.sign_out' are deprecated and will be removed in the 2025.8.0 release. Enter your HEOS Account credentials in the configuration options and the integration will manage authentication automatically."
}
}
}

View File

@ -11,6 +11,7 @@ from homeassistant.components.heos.const import (
SERVICE_SIGN_OUT,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry
@ -91,6 +92,20 @@ async def test_sign_in_unknown_error(
assert "Unable to sign in" in caplog.text
async def test_sign_in_not_loaded_raises(hass: HomeAssistant, config_entry) -> None:
"""Test the sign-in service when entry not loaded raises exception."""
await setup_component(hass, config_entry)
await hass.config_entries.async_unload(config_entry.entry_id)
with pytest.raises(HomeAssistantError, match="The HEOS integration is not loaded"):
await hass.services.async_call(
DOMAIN,
SERVICE_SIGN_IN,
{ATTR_USERNAME: "test@test.com", ATTR_PASSWORD: "password"},
blocking=True,
)
async def test_sign_out(hass: HomeAssistant, config_entry, controller) -> None:
"""Test the sign-out service."""
await setup_component(hass, config_entry)
@ -113,6 +128,15 @@ async def test_sign_out_not_connected(
assert "Unable to sign out because HEOS is not connected" in caplog.text
async def test_sign_out_not_loaded_raises(hass: HomeAssistant, config_entry) -> None:
"""Test the sign-out service when entry not loaded raises exception."""
await setup_component(hass, config_entry)
await hass.config_entries.async_unload(config_entry.entry_id)
with pytest.raises(HomeAssistantError, match="The HEOS integration is not loaded"):
await hass.services.async_call(DOMAIN, SERVICE_SIGN_OUT, {}, blocking=True)
async def test_sign_out_unknown_error(
hass: HomeAssistant, config_entry, controller, caplog: pytest.LogCaptureFixture
) -> None: