Convert rachio to cloudhooks (#33724)

* Convert rachio to cloudhooks

* add cloud to after_dependencies
pull/33726/head
J. Nick Koston 2020-04-05 17:47:27 -05:00 committed by GitHub
parent 2e6108365e
commit e3e2e817e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 65 additions and 27 deletions

View File

@ -13,14 +13,18 @@ from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import config_validation as cv
from .const import (
CONF_CUSTOM_URL,
CONF_CLOUDHOOK_URL,
CONF_MANUAL_RUN_MINS,
CONF_WEBHOOK_ID,
DEFAULT_MANUAL_RUN_MINS,
DOMAIN,
RACHIO_API_EXCEPTIONS,
)
from .device import RachioPerson
from .webhooks import WEBHOOK_PATH, RachioWebhookView
from .webhooks import (
async_get_or_create_registered_webhook_id_and_url,
async_register_webhook,
)
_LOGGER = logging.getLogger(__name__)
@ -31,7 +35,6 @@ 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,
@ -76,6 +79,12 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
return unload_ok
async def async_remove_entry(hass, entry):
"""Remove a rachio config entry."""
if CONF_CLOUDHOOK_URL in entry.data:
await hass.components.cloud.async_delete_cloudhook(entry.data[CONF_WEBHOOK_ID])
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Set up the Rachio config entry."""
@ -93,11 +102,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
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}"
webhook_id, webhook_url = await async_get_or_create_registered_webhook_id_and_url(
hass, entry
)
rachio.webhook_url = webhook_url
person = RachioPerson(rachio, entry)
@ -118,9 +127,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
# 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))
async_register_webhook(hass, webhook_id, entry.entry_id)
for component in SUPPORTED_DOMAINS:
hass.async_create_task(

View File

@ -58,3 +58,6 @@ SIGNAL_RACHIO_UPDATE = f"{DOMAIN}_update"
SIGNAL_RACHIO_CONTROLLER_UPDATE = f"{SIGNAL_RACHIO_UPDATE}_controller"
SIGNAL_RACHIO_ZONE_UPDATE = f"{SIGNAL_RACHIO_UPDATE}_zone"
SIGNAL_RACHIO_SCHEDULE_UPDATE = f"{SIGNAL_RACHIO_UPDATE}_schedule"
CONF_WEBHOOK_ID = "webhook_id"
CONF_CLOUDHOOK_URL = "cloudhook_url"

View File

@ -4,6 +4,7 @@
"documentation": "https://www.home-assistant.io/integrations/rachio",
"requirements": ["rachiopy==0.1.3"],
"dependencies": ["http"],
"after_dependencies": ["cloud"],
"codeowners": ["@bdraco"],
"config_flow": true,
"homekit": {

View File

@ -4,11 +4,13 @@ import logging
from aiohttp import web
from homeassistant.components.http import HomeAssistantView
from homeassistant.const import URL_API
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_send
from .const import (
CONF_CLOUDHOOK_URL,
CONF_WEBHOOK_ID,
DOMAIN,
KEY_EXTERNAL_ID,
KEY_TYPE,
@ -68,28 +70,17 @@ SIGNAL_MAP = {
_LOGGER = logging.getLogger(__name__)
class RachioWebhookView(HomeAssistantView):
"""Provide a page for the server to call."""
@callback
def async_register_webhook(hass, webhook_id, entry_id):
"""Register a webhook."""
requires_auth = False # Handled separately
def __init__(self, entry_id, webhook_url):
"""Initialize the instance of the view."""
self._entry_id = entry_id
self.url = webhook_url
self.name = webhook_url[1:].replace("/", ":")
_LOGGER.debug(
"Initialize webhook at url: %s, with name %s", self.url, self.name
)
async def post(self, request) -> web.Response:
async def _async_handle_rachio_webhook(hass, webhook_id, request):
"""Handle webhook calls from the server."""
hass = request.app["hass"]
data = await request.json()
try:
auth = data.get(KEY_EXTERNAL_ID, "").split(":")[1]
assert auth == hass.data[DOMAIN][self._entry_id].rachio.webhook_auth
assert auth == hass.data[DOMAIN][entry_id].rachio.webhook_auth
except (AssertionError, IndexError):
return web.Response(status=web.HTTPForbidden.status_code)
@ -98,3 +89,39 @@ class RachioWebhookView(HomeAssistantView):
async_dispatcher_send(hass, SIGNAL_MAP[update_type], data)
return web.Response(status=web.HTTPNoContent.status_code)
hass.components.webhook.async_register(
DOMAIN, "Rachio", webhook_id, _async_handle_rachio_webhook
)
async def async_get_or_create_registered_webhook_id_and_url(hass, entry):
"""Generate webhook ID."""
config = entry.data.copy()
updated_config = False
webhook_url = None
webhook_id = config.get(CONF_WEBHOOK_ID)
if not webhook_id:
webhook_id = hass.components.webhook.async_generate_id()
config[CONF_WEBHOOK_ID] = webhook_id
updated_config = True
if hass.components.cloud.async_active_subscription():
cloudhook_url = config.get(CONF_CLOUDHOOK_URL)
if not cloudhook_url:
cloudhook_url = await hass.components.cloud.async_create_cloudhook(
webhook_id
)
config[CONF_CLOUDHOOK_URL] = cloudhook_url
updated_config = True
webhook_url = cloudhook_url
if not webhook_url:
webhook_url = hass.components.webhook.async_generate_url(webhook_id)
if updated_config:
hass.config_entries.async_update_entry(entry, data=config)
return webhook_id, webhook_url