Add webhook support for Netatmo Cameras (#20755)
Add webhook support for Netatmo Cameraspull/21206/head
parent
33b8dbe73a
commit
847711ddc9
|
@ -1,26 +1,64 @@
|
|||
"""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
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
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.
|
Loading…
Reference in New Issue