diff --git a/homeassistant/components/surepetcare/__init__.py b/homeassistant/components/surepetcare/__init__.py index 59b91dabb2a..1efcaa6b5c1 100644 --- a/homeassistant/components/surepetcare/__init__.py +++ b/homeassistant/components/surepetcare/__init__.py @@ -5,7 +5,7 @@ from datetime import timedelta import logging from surepy import Surepy, SurepyEntity -from surepy.enums import LockState +from surepy.enums import EntityType, Location, LockState from surepy.exceptions import SurePetcareAuthenticationError, SurePetcareError import voluptuous as vol @@ -20,17 +20,21 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant from homeassistant.helpers import config_validation as cv from homeassistant.helpers.aiohttp_client import async_get_clientsession +from homeassistant.helpers.service import ServiceCall from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from .const import ( ATTR_FLAP_ID, + ATTR_LOCATION, ATTR_LOCK_STATE, + ATTR_PET_NAME, CONF_FEEDERS, CONF_FLAPS, CONF_PETS, DOMAIN, SERVICE_SET_LOCK_STATE, + SERVICE_SET_PET_LOCATION, SURE_API_TIMEOUT, ) @@ -91,12 +95,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.data.setdefault(DOMAIN, {}) try: - surepy = Surepy( - entry.data[CONF_USERNAME], - entry.data[CONF_PASSWORD], - auth_token=entry.data[CONF_TOKEN], - api_timeout=SURE_API_TIMEOUT, - session=async_get_clientsession(hass), + hass.data[DOMAIN][entry.entry_id] = coordinator = SurePetcareDataCoordinator( + entry, + hass, ) except SurePetcareAuthenticationError: _LOGGER.error("Unable to connect to surepetcare.io: Wrong credentials!") @@ -105,40 +106,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: _LOGGER.error("Unable to connect to surepetcare.io: Wrong %s!", error) return False - async def _update_method() -> dict[int, SurepyEntity]: - """Get the latest data from Sure Petcare.""" - try: - return await surepy.get_entities(refresh=True) - except SurePetcareError as err: - raise UpdateFailed(f"Unable to fetch data: {err}") from err - - coordinator = DataUpdateCoordinator( - hass, - _LOGGER, - name=DOMAIN, - update_method=_update_method, - update_interval=SCAN_INTERVAL, - ) - - hass.data[DOMAIN][entry.entry_id] = coordinator await coordinator.async_config_entry_first_refresh() hass.config_entries.async_setup_platforms(entry, PLATFORMS) - lock_states = { - LockState.UNLOCKED.name.lower(): surepy.sac.unlock, - LockState.LOCKED_IN.name.lower(): surepy.sac.lock_in, - LockState.LOCKED_OUT.name.lower(): surepy.sac.lock_out, - LockState.LOCKED_ALL.name.lower(): surepy.sac.lock, - } - - async def handle_set_lock_state(call): - """Call when setting the lock state.""" - flap_id = call.data[ATTR_FLAP_ID] - state = call.data[ATTR_LOCK_STATE] - await lock_states[state](flap_id) - await coordinator.async_request_refresh() - lock_state_service_schema = vol.Schema( { vol.Required(ATTR_FLAP_ID): vol.All( @@ -147,18 +118,35 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: vol.Required(ATTR_LOCK_STATE): vol.All( cv.string, vol.Lower, - vol.In(lock_states.keys()), + vol.In(coordinator.lock_states.keys()), ), } ) - hass.services.async_register( DOMAIN, SERVICE_SET_LOCK_STATE, - handle_set_lock_state, + coordinator.handle_set_lock_state, schema=lock_state_service_schema, ) + set_pet_location_schema = vol.Schema( + { + vol.Optional(ATTR_PET_NAME): vol.In(coordinator.get_pets().values()), + vol.Required(ATTR_LOCATION): vol.In( + [ + Location.INSIDE.name.title(), + Location.OUTSIDE.name.title(), + ] + ), + } + ) + hass.services.async_register( + DOMAIN, + SERVICE_SET_PET_LOCATION, + coordinator.handle_set_pet_location, + schema=set_pet_location_schema, + ) + return True @@ -169,3 +157,64 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.data[DOMAIN].pop(entry.entry_id) return unload_ok + + +class SurePetcareDataCoordinator(DataUpdateCoordinator): + """Handle Surepetcare data.""" + + def __init__(self, entry: ConfigEntry, hass: HomeAssistant) -> None: + """Initialize the data handler.""" + self.surepy = Surepy( + entry.data[CONF_USERNAME], + entry.data[CONF_PASSWORD], + auth_token=entry.data[CONF_TOKEN], + api_timeout=SURE_API_TIMEOUT, + session=async_get_clientsession(hass), + ) + self.lock_states = { + LockState.UNLOCKED.name.lower(): self.surepy.sac.unlock, + LockState.LOCKED_IN.name.lower(): self.surepy.sac.lock_in, + LockState.LOCKED_OUT.name.lower(): self.surepy.sac.lock_out, + LockState.LOCKED_ALL.name.lower(): self.surepy.sac.lock, + } + super().__init__( + hass, + _LOGGER, + name=DOMAIN, + update_interval=SCAN_INTERVAL, + ) + + async def _async_update_data(self) -> dict[int, SurepyEntity]: + """Get the latest data from Sure Petcare.""" + try: + return await self.surepy.get_entities(refresh=True) + except SurePetcareError as err: + raise UpdateFailed(f"Unable to fetch data: {err}") from err + + async def handle_set_lock_state(self, call: ServiceCall) -> None: + """Call when setting the lock state.""" + flap_id = call.data[ATTR_FLAP_ID] + state = call.data[ATTR_LOCK_STATE] + await self.lock_states[state](flap_id) + await self.async_request_refresh() + + def get_pets(self) -> dict[int, str]: + """Get pets.""" + names = {} + for surepy_entity in self.data.values(): + if surepy_entity.type == EntityType.PET and surepy_entity.name: + names[surepy_entity.id] = surepy_entity.name + return names + + async def handle_set_pet_location(self, call: ServiceCall) -> None: + """Call when setting the pet location.""" + pet_name = call.data[ATTR_PET_NAME] + location = call.data[ATTR_LOCATION] + for device_id, device_name in self.get_pets().items(): + if pet_name == device_name: + await self.surepy.sac.set_pet_location( + device_id, Location[location.upper()] + ) + await self.async_request_refresh() + return + _LOGGER.error("Unknown pet %s", pet_name) diff --git a/homeassistant/components/surepetcare/const.py b/homeassistant/components/surepetcare/const.py index 6349ebe14a8..6617137b026 100644 --- a/homeassistant/components/surepetcare/const.py +++ b/homeassistant/components/surepetcare/const.py @@ -13,7 +13,10 @@ SURE_BATT_VOLTAGE_FULL = 1.6 # voltage SURE_BATT_VOLTAGE_LOW = 1.25 # voltage SURE_BATT_VOLTAGE_DIFF = SURE_BATT_VOLTAGE_FULL - SURE_BATT_VOLTAGE_LOW -# lock state service +# state service SERVICE_SET_LOCK_STATE = "set_lock_state" +SERVICE_SET_PET_LOCATION = "set_pet_location" ATTR_FLAP_ID = "flap_id" +ATTR_LOCATION = "location" ATTR_LOCK_STATE = "lock_state" +ATTR_PET_NAME = "pet_name" diff --git a/homeassistant/components/surepetcare/services.yaml b/homeassistant/components/surepetcare/services.yaml index 77887a18b87..fc352aeb6ab 100644 --- a/homeassistant/components/surepetcare/services.yaml +++ b/homeassistant/components/surepetcare/services.yaml @@ -20,3 +20,23 @@ set_lock_state: - 'locked_in' - 'locked_out' - 'unlocked' + +set_pet_location: + name: Set pet location + description: Set pet location + fields: + pet_name: + description: Name of pet + example: My_cat + required: true + selector: + text: + location: + description: Pet location (Inside or Outside) + example: inside + required: true + selector: + select: + options: + - 'Inside' + - 'Outside'