Unverified Commit 847711dd authored by Daniel Perna's avatar Daniel Perna Committed by GitHub

Add webhook support for Netatmo Cameras (#20755)

Add webhook support for Netatmo Cameras
parent 33b8dbe7
"""Support for the Netatmo devices."""
import logging
import json
from datetime import timedelta
from urllib.error import HTTPError
import voluptuous as vol
from homeassistant.const import (
CONF_API_KEY, CONF_PASSWORD, CONF_USERNAME, CONF_DISCOVERY)
CONF_API_KEY, CONF_PASSWORD, CONF_USERNAME, CONF_DISCOVERY, CONF_URL,
EVENT_HOMEASSISTANT_STOP)
from homeassistant.helpers import discovery
import homeassistant.helpers.config_validation as cv
from homeassistant.util import Throttle
REQUIREMENTS = ['pyatmo==1.8']
DEPENDENCIES = ['webhook']
_LOGGER = logging.getLogger(__name__)
CONF_SECRET_KEY = 'secret_key'
CONF_WEBHOOKS = 'webhooks'
DOMAIN = 'netatmo'
SERVICE_ADDWEBHOOK = 'addwebhook'
SERVICE_DROPWEBHOOK = 'dropwebhook'
NETATMO_AUTH = None
NETATMO_WEBHOOK_URL = None
NETATMO_PERSONS = {}
DEFAULT_PERSON = 'Unknown'
DEFAULT_DISCOVERY = True
DEFAULT_WEBHOOKS = False
EVENT_PERSON = 'person'
EVENT_MOVEMENT = 'movement'
EVENT_HUMAN = 'human'
EVENT_ANIMAL = 'animal'
EVENT_VEHICLE = 'vehicle'
EVENT_BUS_PERSON = 'netatmo_person'
EVENT_BUS_MOVEMENT = 'netatmo_movement'
EVENT_BUS_HUMAN = 'netatmo_human'
EVENT_BUS_ANIMAL = 'netatmo_animal'
EVENT_BUS_VEHICLE = 'netatmo_vehicle'
EVENT_BUS_OTHER = 'netatmo_other'
ATTR_ID = 'id'
ATTR_PSEUDO = 'pseudo'
ATTR_NAME = 'name'
ATTR_EVENT_TYPE = 'event_type'
ATTR_MESSAGE = 'message'
ATTR_CAMERA_ID = 'camera_id'
ATTR_HOME_NAME = 'home_name'
ATTR_PERSONS = 'persons'
ATTR_IS_KNOWN = 'is_known'
ATTR_FACE_URL = 'face_url'
ATTR_SNAPSHOT_URL = 'snapshot_url'
ATTR_VIGNETTE_URL = 'vignette_url'
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=10)
MIN_TIME_BETWEEN_EVENT_UPDATES = timedelta(seconds=10)
......@@ -31,16 +69,23 @@ CONFIG_SCHEMA = vol.Schema({
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_SECRET_KEY): cv.string,
vol.Required(CONF_USERNAME): cv.string,
vol.Optional(CONF_WEBHOOKS, default=DEFAULT_WEBHOOKS): cv.boolean,
vol.Optional(CONF_DISCOVERY, default=DEFAULT_DISCOVERY): cv.boolean,
})
}, extra=vol.ALLOW_EXTRA)
SCHEMA_SERVICE_ADDWEBHOOK = vol.Schema({
vol.Optional(CONF_URL): cv.string,
})
SCHEMA_SERVICE_DROPWEBHOOK = vol.Schema({})
def setup(hass, config):
"""Set up the Netatmo devices."""
import pyatmo
global NETATMO_AUTH
global NETATMO_AUTH, NETATMO_WEBHOOK_URL
try:
NETATMO_AUTH = pyatmo.ClientAuth(
config[DOMAIN][CONF_API_KEY], config[DOMAIN][CONF_SECRET_KEY],
......@@ -56,9 +101,88 @@ def setup(hass, config):
for component in 'camera', 'sensor', 'binary_sensor', 'climate':
discovery.load_platform(hass, component, DOMAIN, {}, config)
if config[DOMAIN][CONF_WEBHOOKS]:
webhook_id = hass.components.webhook.async_generate_id()
NETATMO_WEBHOOK_URL = hass.components.webhook.async_generate_url(
webhook_id)
hass.components.webhook.async_register(
DOMAIN, 'Netatmo', webhook_id, handle_webhook)
NETATMO_AUTH.addwebhook(NETATMO_WEBHOOK_URL)
hass.bus.listen_once(
EVENT_HOMEASSISTANT_STOP, dropwebhook)
def _service_addwebhook(service):
"""Service to (re)add webhooks during runtime."""
url = service.data.get(CONF_URL)
if url is None:
url = NETATMO_WEBHOOK_URL
_LOGGER.info("Adding webhook for URL: %s", url)
NETATMO_AUTH.addwebhook(url)
hass.services.register(
DOMAIN, SERVICE_ADDWEBHOOK, _service_addwebhook,
schema=SCHEMA_SERVICE_ADDWEBHOOK)
def _service_dropwebhook(service):
"""Service to drop webhooks during runtime."""
_LOGGER.info("Dropping webhook")
NETATMO_AUTH.dropwebhook()
hass.services.register(
DOMAIN, SERVICE_DROPWEBHOOK, _service_dropwebhook,
schema=SCHEMA_SERVICE_DROPWEBHOOK)
return True
def dropwebhook(hass):
"""Drop the webhook subscription."""
NETATMO_AUTH.dropwebhook()
async def handle_webhook(hass, webhook_id, request):
"""Handle webhook callback."""
body = await request.text()
try:
data = json.loads(body) if body else {}
except ValueError:
return None
_LOGGER.debug("Got webhook data: %s", data)
published_data = {
ATTR_EVENT_TYPE: data.get(ATTR_EVENT_TYPE),
ATTR_HOME_NAME: data.get(ATTR_HOME_NAME),
ATTR_CAMERA_ID: data.get(ATTR_CAMERA_ID),
ATTR_MESSAGE: data.get(ATTR_MESSAGE)
}
if data.get(ATTR_EVENT_TYPE) == EVENT_PERSON:
for person in data[ATTR_PERSONS]:
published_data[ATTR_ID] = person.get(ATTR_ID)
published_data[ATTR_NAME] = NETATMO_PERSONS.get(
published_data[ATTR_ID], DEFAULT_PERSON)
published_data[ATTR_IS_KNOWN] = person.get(ATTR_IS_KNOWN)
published_data[ATTR_FACE_URL] = person.get(ATTR_FACE_URL)
hass.bus.async_fire(EVENT_BUS_PERSON, published_data)
elif data.get(ATTR_EVENT_TYPE) == EVENT_MOVEMENT:
published_data[ATTR_VIGNETTE_URL] = data.get(ATTR_VIGNETTE_URL)
published_data[ATTR_SNAPSHOT_URL] = data.get(ATTR_SNAPSHOT_URL)
hass.bus.async_fire(EVENT_BUS_MOVEMENT, published_data)
elif data.get(ATTR_EVENT_TYPE) == EVENT_HUMAN:
published_data[ATTR_VIGNETTE_URL] = data.get(ATTR_VIGNETTE_URL)
published_data[ATTR_SNAPSHOT_URL] = data.get(ATTR_SNAPSHOT_URL)
hass.bus.async_fire(EVENT_BUS_HUMAN, published_data)
elif data.get(ATTR_EVENT_TYPE) == EVENT_ANIMAL:
published_data[ATTR_VIGNETTE_URL] = data.get(ATTR_VIGNETTE_URL)
published_data[ATTR_SNAPSHOT_URL] = data.get(ATTR_SNAPSHOT_URL)
hass.bus.async_fire(EVENT_BUS_ANIMAL, published_data)
elif data.get(ATTR_EVENT_TYPE) == EVENT_VEHICLE:
hass.bus.async_fire(EVENT_BUS_VEHICLE, published_data)
published_data[ATTR_VIGNETTE_URL] = data.get(ATTR_VIGNETTE_URL)
published_data[ATTR_SNAPSHOT_URL] = data.get(ATTR_SNAPSHOT_URL)
else:
hass.bus.async_fire(EVENT_BUS_OTHER, data)
class CameraData:
"""Get the latest data from Netatmo."""
......@@ -101,6 +225,12 @@ class CameraData:
home=home, cid=cid)
return self.camera_type
def get_persons(self):
"""Gather person data for webhooks."""
global NETATMO_PERSONS
for person_id, person_data in self.camera_data.persons.items():
NETATMO_PERSONS[person_id] = person_data.get(ATTR_PSEUDO)
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self):
"""Call the Netatmo API to update the data."""
......
......@@ -40,6 +40,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
continue
add_entities([NetatmoCamera(data, camera_name, home,
camera_type, verify_ssl)])
data.get_persons()
except pyatmo.NoDevice:
return None
......
addwebhook:
description: Add webhook during runtime (e.g. if it has been banned).
fields:
url:
description: URL for which to add the webhook.
example: https://yourdomain.com:443/api/webhook/webhook_id
dropwebhook:
description: Drop active webhooks.
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment