Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 73 additions & 2 deletions homeassistant/components/netatmo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,48 @@
https://home-assistant.io/components/netatmo/
"""
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,
EVENT_HOMEASSISTANT_STOP)
from homeassistant.helpers import discovery
import homeassistant.helpers.config_validation as cv
from homeassistant.util import Throttle

REQUIREMENTS = ['pyatmo==1.4']
REQUIREMENTS = ['pyatmo==1.8']
DEPENDENCIES = ['webhook']

_LOGGER = logging.getLogger(__name__)

CONF_SECRET_KEY = 'secret_key'
CONF_WEBHOOK_URL = 'webhook_url'

DOMAIN = 'netatmo'

NETATMO_AUTH = None
NETATMO_PERSONS = {}
DEFAULT_PERSON = 'Unknown'
DEFAULT_DISCOVERY = True

EVENT_RECEIVED = 'netatmo_webhook_received'
EVENT_PERSON = 'person'
EVENT_MOVEMENT = 'movement'

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'

MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=10)
MIN_TIME_BETWEEN_EVENT_UPDATES = timedelta(seconds=10)

Expand All @@ -36,6 +56,7 @@
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_SECRET_KEY): cv.string,
vol.Required(CONF_USERNAME): cv.string,
vol.Optional(CONF_WEBHOOK_URL): cv.string,
Comment thread
danielperna84 marked this conversation as resolved.
Outdated
vol.Optional(CONF_DISCOVERY, default=DEFAULT_DISCOVERY): cv.boolean,
})
}, extra=vol.ALLOW_EXTRA)
Expand All @@ -61,9 +82,53 @@ def setup(hass, config):
for component in 'camera', 'sensor', 'binary_sensor', 'climate':
discovery.load_platform(hass, component, DOMAIN, {}, config)

if config[DOMAIN][CONF_WEBHOOK_URL]:
webhook_url = config[DOMAIN].get(CONF_WEBHOOK_URL)
webhook_id = webhook_url.split('/')[-1]
hass.components.webhook.async_register(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above.

DOMAIN, 'Netatmo', webhook_id, handle_webhook)
NETATMO_AUTH.addwebhook(webhook_url)
hass.bus.listen_once(
EVENT_HOMEASSISTANT_STOP, dropwebhook)

return True


def dropwebhook(hass):
"""Drop the webhook subscription."""
NETATMO_AUTH.dropwebhook()

async def handle_webhook(hass, webhook_id, request):
Comment thread
danielperna84 marked this conversation as resolved.
"""Handle webhook callback."""
body = await request.text()
try:
data = json.loads(body) if body else {}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use:

data = await request.json()

except ValueError:
return None

published_data = {}
if isinstance(data, dict):
published_data['webhook_id'] = webhook_id
if data.get(ATTR_EVENT_TYPE) == EVENT_PERSON:
published_data[ATTR_EVENT_TYPE] = EVENT_PERSON
published_data[ATTR_HOME_NAME] = data.get(ATTR_HOME_NAME)
published_data[ATTR_CAMERA_ID] = data.get(ATTR_CAMERA_ID)
published_data[ATTR_MESSAGE] = data.get(ATTR_MESSAGE)
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)
_LOGGER.debug("webhook data: %s", published_data)
hass.bus.async_fire(EVENT_RECEIVED, published_data)
elif data.get(ATTR_EVENT_TYPE) == EVENT_MOVEMENT:
published_data[ATTR_EVENT_TYPE] = EVENT_MOVEMENT
published_data[ATTR_HOME_NAME] = data.get(ATTR_HOME_NAME)
published_data[ATTR_CAMERA_ID] = data.get(ATTR_CAMERA_ID)
_LOGGER.debug("webhook data: %s", published_data)
hass.bus.async_fire(EVENT_RECEIVED, published_data)


class CameraData:
"""Get the latest data from Netatmo."""

Expand Down Expand Up @@ -106,6 +171,12 @@ def get_camera_type(self, camera=None, home=None, cid=None):
home=home, cid=cid)
return self.camera_type

def get_persons(self):
"""Gather person data for webhooks."""
global NETATMO_PERSONS

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use global.

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."""
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/netatmo/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,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

Expand Down
2 changes: 1 addition & 1 deletion requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -918,7 +918,7 @@ pyalarmdotcom==0.3.2
pyarlo==0.2.3

# homeassistant.components.netatmo
pyatmo==1.4
pyatmo==1.8

# homeassistant.components.apple_tv
pyatv==0.3.12
Expand Down