From 91bc39b2840a7c0eddcfc68f50b1b2d89da70f85 Mon Sep 17 00:00:00 2001 From: Erwin Douna Date: Mon, 6 Jul 2026 13:18:34 +0200 Subject: [PATCH] Move Sure Petcare services to async_setup (#175475) --- .../components/surepetcare/__init__.py | 62 +++----------- .../components/surepetcare/services.py | 85 +++++++++++++++++++ 2 files changed, 97 insertions(+), 50 deletions(-) create mode 100644 homeassistant/components/surepetcare/services.py diff --git a/homeassistant/components/surepetcare/__init__.py b/homeassistant/components/surepetcare/__init__.py index 01d21e1be58e..e1d7f163843a 100644 --- a/homeassistant/components/surepetcare/__init__.py +++ b/homeassistant/components/surepetcare/__init__.py @@ -3,30 +3,31 @@ from datetime import timedelta import logging -from surepy.enums import Location from surepy.exceptions import SurePetcareAuthenticationError, SurePetcareError -import voluptuous as vol -from homeassistant.const import ATTR_LOCATION, Platform +from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.typing import ConfigType -from .const import ( - ATTR_FLAP_ID, - ATTR_LOCK_STATE, - ATTR_PET_NAME, - DOMAIN, - SERVICE_SET_LOCK_STATE, - SERVICE_SET_PET_LOCATION, -) +from .const import DOMAIN from .coordinator import SurePetcareConfigEntry, SurePetcareDataCoordinator +from .services import async_setup_services _LOGGER = logging.getLogger(__name__) PLATFORMS = [Platform.BINARY_SENSOR, Platform.LOCK, Platform.SENSOR] SCAN_INTERVAL = timedelta(minutes=3) +CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN) + + +async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: + """Set up Sure Petcare services.""" + async_setup_services(hass) + return True + async def async_setup_entry(hass: HomeAssistant, entry: SurePetcareConfigEntry) -> bool: """Set up Sure Petcare from a config entry.""" @@ -43,45 +44,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: SurePetcareConfigEntry) entry.runtime_data = coordinator await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) - lock_state_service_schema = vol.Schema( - { - vol.Required(ATTR_FLAP_ID): vol.All( - cv.positive_int, vol.In(coordinator.data.keys()) - ), - vol.Required(ATTR_LOCK_STATE): vol.All( - cv.string, - vol.Lower, - vol.In(coordinator.lock_states_callbacks.keys()), - ), - } - ) - # pylint: disable-next=home-assistant-service-registered-in-setup-entry - hass.services.async_register( - DOMAIN, - SERVICE_SET_LOCK_STATE, - coordinator.handle_set_lock_state, - schema=lock_state_service_schema, - ) - - set_pet_location_schema = vol.Schema( - { - vol.Required(ATTR_PET_NAME): vol.In(coordinator.get_pets().keys()), - vol.Required(ATTR_LOCATION): vol.In( - [ - Location.INSIDE.name.title(), - Location.OUTSIDE.name.title(), - ] - ), - } - ) - # pylint: disable-next=home-assistant-service-registered-in-setup-entry - hass.services.async_register( - DOMAIN, - SERVICE_SET_PET_LOCATION, - coordinator.handle_set_pet_location, - schema=set_pet_location_schema, - ) - return True diff --git a/homeassistant/components/surepetcare/services.py b/homeassistant/components/surepetcare/services.py new file mode 100644 index 000000000000..74ec28a91aa9 --- /dev/null +++ b/homeassistant/components/surepetcare/services.py @@ -0,0 +1,85 @@ +"""Support for Sure Petcare services.""" + +from surepy.enums import Location +import voluptuous as vol + +from homeassistant.const import ATTR_LOCATION +from homeassistant.core import HomeAssistant, ServiceCall, callback +from homeassistant.exceptions import ServiceValidationError +from homeassistant.helpers import config_validation as cv, service + +from .const import ( + ATTR_FLAP_ID, + ATTR_LOCK_STATE, + ATTR_PET_NAME, + DOMAIN, + SERVICE_SET_LOCK_STATE, + SERVICE_SET_PET_LOCATION, +) +from .coordinator import SurePetcareConfigEntry + + +@callback +def async_setup_services(hass: HomeAssistant) -> None: + """Register Sure Petcare services.""" + + async def handle_set_lock_state(call: ServiceCall) -> None: + """Set lock state for a flap.""" + entry: SurePetcareConfigEntry = service.async_get_config_entry( + hass, DOMAIN, None + ) + coordinator = entry.runtime_data + flap_id = call.data[ATTR_FLAP_ID] + if flap_id not in coordinator.data: + raise ServiceValidationError(f"Unknown Sure Petcare flap ID: {flap_id}") + await coordinator.handle_set_lock_state(call) + + async def handle_set_pet_location(call: ServiceCall) -> None: + """Set pet location.""" + entry: SurePetcareConfigEntry = service.async_get_config_entry( + hass, DOMAIN, None + ) + coordinator = entry.runtime_data + pet_name = call.data[ATTR_PET_NAME] + if pet_name not in coordinator.get_pets(): + raise ServiceValidationError(f"Unknown Sure Petcare pet: {pet_name}") + await coordinator.handle_set_pet_location(call) + + hass.services.async_register( + DOMAIN, + SERVICE_SET_LOCK_STATE, + handle_set_lock_state, + schema=vol.Schema( + { + vol.Required(ATTR_FLAP_ID): cv.positive_int, + vol.Required(ATTR_LOCK_STATE): vol.All( + cv.string, + vol.Lower, + vol.In( + [ + "unlocked", + "locked_in", + "locked_out", + "locked_all", + ] + ), + ), + } + ), + ) + hass.services.async_register( + DOMAIN, + SERVICE_SET_PET_LOCATION, + handle_set_pet_location, + schema=vol.Schema( + { + vol.Required(ATTR_PET_NAME): cv.string, + vol.Required(ATTR_LOCATION): vol.In( + [ + Location.INSIDE.name.title(), + Location.OUTSIDE.name.title(), + ] + ), + } + ), + )