2020-01-06 14:00:01 +00:00
|
|
|
"""Support for Sure Petcare cat/pet flaps."""
|
|
|
|
import logging
|
2020-02-09 16:46:00 +00:00
|
|
|
from typing import Any, Dict, List
|
2020-01-06 14:00:01 +00:00
|
|
|
|
|
|
|
from surepy import (
|
|
|
|
SurePetcare,
|
|
|
|
SurePetcareAuthenticationError,
|
|
|
|
SurePetcareError,
|
2020-02-09 16:46:00 +00:00
|
|
|
SureProductID,
|
2020-01-06 14:00:01 +00:00
|
|
|
)
|
|
|
|
import voluptuous as vol
|
|
|
|
|
|
|
|
from homeassistant.const import (
|
|
|
|
CONF_ID,
|
|
|
|
CONF_PASSWORD,
|
|
|
|
CONF_SCAN_INTERVAL,
|
|
|
|
CONF_TYPE,
|
|
|
|
CONF_USERNAME,
|
|
|
|
)
|
|
|
|
from homeassistant.helpers import config_validation as cv
|
|
|
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
|
|
|
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
|
|
|
from homeassistant.helpers.event import async_track_time_interval
|
|
|
|
|
|
|
|
from .const import (
|
2020-02-09 16:46:00 +00:00
|
|
|
CONF_FEEDERS,
|
2020-01-06 14:00:01 +00:00
|
|
|
CONF_FLAPS,
|
2020-02-09 16:46:00 +00:00
|
|
|
CONF_PARENT,
|
2020-01-06 14:00:01 +00:00
|
|
|
CONF_PETS,
|
2020-02-09 16:46:00 +00:00
|
|
|
CONF_PRODUCT_ID,
|
2020-01-06 14:00:01 +00:00
|
|
|
DATA_SURE_PETCARE,
|
|
|
|
DEFAULT_SCAN_INTERVAL,
|
|
|
|
DOMAIN,
|
|
|
|
SPC,
|
|
|
|
TOPIC_UPDATE,
|
|
|
|
)
|
|
|
|
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
CONFIG_SCHEMA = vol.Schema(
|
|
|
|
{
|
|
|
|
DOMAIN: vol.Schema(
|
|
|
|
{
|
|
|
|
vol.Required(CONF_USERNAME): cv.string,
|
|
|
|
vol.Required(CONF_PASSWORD): cv.string,
|
2020-02-09 16:46:00 +00:00
|
|
|
vol.Optional(CONF_FEEDERS, default=[]): vol.All(
|
|
|
|
cv.ensure_list, [cv.positive_int]
|
|
|
|
),
|
|
|
|
vol.Optional(CONF_FLAPS, default=[]): vol.All(
|
|
|
|
cv.ensure_list, [cv.positive_int]
|
|
|
|
),
|
|
|
|
vol.Optional(CONF_PETS): vol.All(cv.ensure_list, [cv.positive_int]),
|
2020-01-06 14:00:01 +00:00
|
|
|
vol.Optional(
|
|
|
|
CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL
|
|
|
|
): cv.time_period,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
},
|
|
|
|
extra=vol.ALLOW_EXTRA,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2020-02-09 16:46:00 +00:00
|
|
|
async def async_setup(hass, config) -> bool:
|
2020-01-06 14:00:01 +00:00
|
|
|
"""Initialize the Sure Petcare component."""
|
|
|
|
conf = config[DOMAIN]
|
|
|
|
|
|
|
|
# update interval
|
|
|
|
scan_interval = conf[CONF_SCAN_INTERVAL]
|
|
|
|
|
|
|
|
# shared data
|
|
|
|
hass.data[DOMAIN] = hass.data[DATA_SURE_PETCARE] = {}
|
|
|
|
|
|
|
|
# sure petcare api connection
|
|
|
|
try:
|
|
|
|
surepy = SurePetcare(
|
|
|
|
conf[CONF_USERNAME],
|
|
|
|
conf[CONF_PASSWORD],
|
|
|
|
hass.loop,
|
|
|
|
async_get_clientsession(hass),
|
|
|
|
)
|
2020-02-09 16:46:00 +00:00
|
|
|
await surepy.get_data()
|
2020-01-06 14:00:01 +00:00
|
|
|
except SurePetcareAuthenticationError:
|
|
|
|
_LOGGER.error("Unable to connect to surepetcare.io: Wrong credentials!")
|
|
|
|
return False
|
|
|
|
except SurePetcareError as error:
|
|
|
|
_LOGGER.error("Unable to connect to surepetcare.io: Wrong %s!", error)
|
|
|
|
return False
|
|
|
|
|
2020-02-09 16:46:00 +00:00
|
|
|
# add feeders
|
2020-01-06 14:00:01 +00:00
|
|
|
things = [
|
2020-02-09 16:46:00 +00:00
|
|
|
{CONF_ID: feeder, CONF_TYPE: SureProductID.FEEDER}
|
|
|
|
for feeder in conf[CONF_FEEDERS]
|
2020-01-06 14:00:01 +00:00
|
|
|
]
|
|
|
|
|
2020-02-09 16:46:00 +00:00
|
|
|
# add flaps (don't differentiate between CAT and PET for now)
|
2020-01-06 14:00:01 +00:00
|
|
|
things.extend(
|
|
|
|
[
|
2020-02-09 16:46:00 +00:00
|
|
|
{CONF_ID: flap, CONF_TYPE: SureProductID.PET_FLAP}
|
|
|
|
for flap in conf[CONF_FLAPS]
|
2020-01-06 14:00:01 +00:00
|
|
|
]
|
|
|
|
)
|
|
|
|
|
2020-02-09 16:46:00 +00:00
|
|
|
# discover hubs the flaps/feeders are connected to
|
|
|
|
for device in things.copy():
|
|
|
|
device_data = await surepy.device(device[CONF_ID])
|
|
|
|
if (
|
|
|
|
CONF_PARENT in device_data
|
|
|
|
and device_data[CONF_PARENT][CONF_PRODUCT_ID] == SureProductID.HUB
|
|
|
|
and device_data[CONF_PARENT][CONF_ID] not in things
|
|
|
|
):
|
|
|
|
things.append(
|
|
|
|
{
|
|
|
|
CONF_ID: device_data[CONF_PARENT][CONF_ID],
|
|
|
|
CONF_TYPE: SureProductID.HUB,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
# add pets
|
|
|
|
things.extend(
|
|
|
|
[{CONF_ID: pet, CONF_TYPE: SureProductID.PET} for pet in conf[CONF_PETS]]
|
2020-01-06 14:00:01 +00:00
|
|
|
)
|
|
|
|
|
2020-02-09 16:46:00 +00:00
|
|
|
_LOGGER.debug("Devices and Pets to setup: %s", things)
|
|
|
|
|
|
|
|
spc = hass.data[DATA_SURE_PETCARE][SPC] = SurePetcareAPI(hass, surepy, things)
|
|
|
|
|
2020-01-06 14:00:01 +00:00
|
|
|
# initial update
|
|
|
|
await spc.async_update()
|
|
|
|
|
|
|
|
async_track_time_interval(hass, spc.async_update, scan_interval)
|
|
|
|
|
|
|
|
# load platforms
|
|
|
|
hass.async_create_task(
|
|
|
|
hass.helpers.discovery.async_load_platform("binary_sensor", DOMAIN, {}, config)
|
|
|
|
)
|
|
|
|
hass.async_create_task(
|
|
|
|
hass.helpers.discovery.async_load_platform("sensor", DOMAIN, {}, config)
|
|
|
|
)
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
class SurePetcareAPI:
|
|
|
|
"""Define a generic Sure Petcare object."""
|
|
|
|
|
2020-02-09 16:46:00 +00:00
|
|
|
def __init__(self, hass, surepy: SurePetcare, ids: List[Dict[str, Any]]) -> None:
|
2020-01-06 14:00:01 +00:00
|
|
|
"""Initialize the Sure Petcare object."""
|
|
|
|
self.hass = hass
|
|
|
|
self.surepy = surepy
|
|
|
|
self.ids = ids
|
2020-02-09 16:46:00 +00:00
|
|
|
self.states: Dict[str, Any] = {}
|
2020-01-06 14:00:01 +00:00
|
|
|
|
2020-02-09 16:46:00 +00:00
|
|
|
async def async_update(self, arg: Any = None) -> None:
|
2020-01-06 14:00:01 +00:00
|
|
|
"""Refresh Sure Petcare data."""
|
2020-02-09 16:46:00 +00:00
|
|
|
|
|
|
|
await self.surepy.get_data()
|
|
|
|
|
2020-01-06 14:00:01 +00:00
|
|
|
for thing in self.ids:
|
|
|
|
sure_id = thing[CONF_ID]
|
|
|
|
sure_type = thing[CONF_TYPE]
|
|
|
|
|
|
|
|
try:
|
|
|
|
type_state = self.states.setdefault(sure_type, {})
|
|
|
|
|
2020-02-09 16:46:00 +00:00
|
|
|
if sure_type in [
|
|
|
|
SureProductID.CAT_FLAP,
|
|
|
|
SureProductID.PET_FLAP,
|
|
|
|
SureProductID.FEEDER,
|
|
|
|
SureProductID.HUB,
|
|
|
|
]:
|
|
|
|
type_state[sure_id] = await self.surepy.device(sure_id)
|
|
|
|
elif sure_type == SureProductID.PET:
|
|
|
|
type_state[sure_id] = await self.surepy.pet(sure_id)
|
2020-01-06 14:00:01 +00:00
|
|
|
|
|
|
|
except SurePetcareError as error:
|
|
|
|
_LOGGER.error("Unable to retrieve data from surepetcare.io: %s", error)
|
|
|
|
|
|
|
|
async_dispatcher_send(self.hass, TOPIC_UPDATE)
|