core/homeassistant/components/rachio/__init__.py

131 lines
3.9 KiB
Python

"""Integration with the Rachio Iro sprinkler system controller."""
import asyncio
import logging
import secrets
from rachiopy import Rachio
import voluptuous as vol
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
from homeassistant.const import CONF_API_KEY
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import config_validation as cv
from .const import (
CONF_CUSTOM_URL,
CONF_MANUAL_RUN_MINS,
DEFAULT_MANUAL_RUN_MINS,
DOMAIN,
RACHIO_API_EXCEPTIONS,
)
from .device import RachioPerson
from .webhooks import WEBHOOK_PATH, RachioWebhookView
_LOGGER = logging.getLogger(__name__)
SUPPORTED_DOMAINS = ["switch", "binary_sensor"]
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
vol.Required(CONF_API_KEY): cv.string,
vol.Optional(CONF_CUSTOM_URL): cv.string,
vol.Optional(
CONF_MANUAL_RUN_MINS, default=DEFAULT_MANUAL_RUN_MINS
): cv.positive_int,
}
)
},
extra=vol.ALLOW_EXTRA,
)
async def async_setup(hass: HomeAssistant, config: dict):
"""Set up the rachio component from YAML."""
conf = config.get(DOMAIN)
hass.data.setdefault(DOMAIN, {})
if not conf:
return True
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_IMPORT}, data=conf
)
)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Unload a config entry."""
unload_ok = all(
await asyncio.gather(
*[
hass.config_entries.async_forward_entry_unload(entry, component)
for component in SUPPORTED_DOMAINS
]
)
)
if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Set up the Rachio config entry."""
config = entry.data
options = entry.options
# CONF_MANUAL_RUN_MINS can only come from a yaml import
if not options.get(CONF_MANUAL_RUN_MINS) and config.get(CONF_MANUAL_RUN_MINS):
options_copy = options.copy()
options_copy[CONF_MANUAL_RUN_MINS] = config[CONF_MANUAL_RUN_MINS]
hass.config_entries.async_update_entry(options=options_copy)
# Configure API
api_key = config[CONF_API_KEY]
rachio = Rachio(api_key)
# Get the URL of this server
custom_url = config.get(CONF_CUSTOM_URL)
hass_url = hass.config.api.base_url if custom_url is None else custom_url
rachio.webhook_auth = secrets.token_hex()
webhook_url_path = f"{WEBHOOK_PATH}-{entry.entry_id}"
rachio.webhook_url = f"{hass_url}{webhook_url_path}"
person = RachioPerson(rachio, entry)
# Get the API user
try:
await hass.async_add_executor_job(person.setup, hass)
# Yes we really do get all these exceptions (hopefully rachiopy switches to requests)
# and there is not a reasonable timeout here so it can block for a long time
except RACHIO_API_EXCEPTIONS as error:
_LOGGER.error("Could not reach the Rachio API: %s", error)
raise ConfigEntryNotReady
# Check for Rachio controller devices
if not person.controllers:
_LOGGER.error("No Rachio devices found in account %s", person.username)
return False
_LOGGER.info("%d Rachio device(s) found", len(person.controllers))
# Enable component
hass.data[DOMAIN][entry.entry_id] = person
# Listen for incoming webhook connections after the data is there
hass.http.register_view(RachioWebhookView(entry.entry_id, webhook_url_path))
for component in SUPPORTED_DOMAINS:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, component)
)
return True