From 213a738240b3a0f09ed899ef4cc1d803f03586a1 Mon Sep 17 00:00:00 2001 From: Hugo Dupras Date: Fri, 10 Jun 2016 08:31:36 +0200 Subject: [PATCH] Add Netatmo component and add support for Netatmo Welcome Camera (#2233) * Introducing the Netatmo component As Netatmo is providing several type of device (sensor, camera), a new Netatmo component needs to be created in order to centralize the Netatmo login data. Currently this change only impacts the Netatmo Weather station * Add new Netatmo library This new API will provide access to the Welcome Camera * Basic support for Netatmo Welcome camera This change introduces support for Netatmo Welcome camera. Currently, it will add all detected camera to Home Assistant, camera filtering (similar to the one used for weather station modules) will be added later * Remove useless REQUIREMENTS * Fixes for Netatmo Welcome support * Allow to filter Welcome cameras by name and/or home * Update requirements for Netatmo components * Fix multi-camera support for Welcome * Fix pep8 error/warning * This commit also adds improved logging for bad credentials * Add Throttle decorator for Welcome update function As the update function updates the data for all cameras, we should prevent this function to be called several time during an interval --- .coveragerc | 4 +- homeassistant/components/camera/__init__.py | 3 +- homeassistant/components/camera/netatmo.py | 104 ++++++++++++++++++++ homeassistant/components/netatmo.py | 62 ++++++++++++ homeassistant/components/sensor/__init__.py | 3 +- homeassistant/components/sensor/netatmo.py | 39 ++------ requirements_all.txt | 6 +- 7 files changed, 182 insertions(+), 39 deletions(-) create mode 100644 homeassistant/components/camera/netatmo.py create mode 100644 homeassistant/components/netatmo.py diff --git a/.coveragerc b/.coveragerc index 447e683246b..ca5546cf242 100644 --- a/.coveragerc +++ b/.coveragerc @@ -78,6 +78,9 @@ omit = homeassistant/components/enocean.py homeassistant/components/*/enocean.py + homeassistant/components/netatmo.py + homeassistant/components/*/netatmo.py + homeassistant/components/alarm_control_panel/alarmdotcom.py homeassistant/components/alarm_control_panel/nx584.py homeassistant/components/binary_sensor/arest.py @@ -171,7 +174,6 @@ omit = homeassistant/components/sensor/gtfs.py homeassistant/components/sensor/lastfm.py homeassistant/components/sensor/loopenergy.py - homeassistant/components/sensor/netatmo.py homeassistant/components/sensor/neurio_energy.py homeassistant/components/sensor/nzbget.py homeassistant/components/sensor/onewire.py diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 0be1d4118f7..649d540a44d 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -9,7 +9,7 @@ import logging from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity_component import EntityComponent -from homeassistant.components import bloomsky +from homeassistant.components import bloomsky, netatmo from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa from homeassistant.components.http import HomeAssistantView @@ -21,6 +21,7 @@ ENTITY_ID_FORMAT = DOMAIN + '.{}' # Maps discovered services to their platforms DISCOVERY_PLATFORMS = { bloomsky.DISCOVER_CAMERAS: 'bloomsky', + netatmo.DISCOVER_CAMERAS: 'netatmo', } STATE_RECORDING = 'recording' diff --git a/homeassistant/components/camera/netatmo.py b/homeassistant/components/camera/netatmo.py new file mode 100644 index 00000000000..8e03dc932e9 --- /dev/null +++ b/homeassistant/components/camera/netatmo.py @@ -0,0 +1,104 @@ +""" +Support for the Netatmo Welcome camera. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/camera.netatmo/ +""" +import logging +from datetime import timedelta +import requests +from homeassistant.util import Throttle + +from homeassistant.components.camera import Camera +from homeassistant.loader import get_component + +DEPENDENCIES = ["netatmo"] + +_LOGGER = logging.getLogger(__name__) + +CONF_HOME = 'home' +ATTR_CAMERAS = 'cameras' + +MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=10) + + +# pylint: disable=unused-argument +def setup_platform(hass, config, add_devices_callback, discovery_info=None): + """Setup access to Netatmo Welcome cameras.""" + netatmo = get_component('netatmo') + home = config.get(CONF_HOME, None) + data = WelcomeData(netatmo.NETATMO_AUTH, home) + + for camera_name in data.get_camera_names(): + if ATTR_CAMERAS in config: + if camera_name not in config[ATTR_CAMERAS]: + continue + add_devices_callback([WelcomeCamera(data, camera_name, home)]) + + +class WelcomeCamera(Camera): + """Representation of the images published from Welcome camera.""" + + def __init__(self, data, camera_name, home): + """Setup for access to the BloomSky camera images.""" + super(WelcomeCamera, self).__init__() + self._data = data + self._camera_name = camera_name + if home: + self._name = home + ' / ' + camera_name + else: + self._name = camera_name + self._vpnurl, self._localurl = self._data.welcomedata.cameraUrls( + camera=camera_name + ) + + def camera_image(self): + """Return a still image response from the camera.""" + try: + if self._localurl: + response = requests.get('{0}/live/snapshot_720.jpg'.format( + self._localurl)) + else: + response = requests.get('{0}/live/snapshot_720.jpg'.format( + self._vpnurl)) + except requests.exceptions.RequestException as error: + _LOGGER.error('Welcome VPN url changed: %s', error) + self._data.update() + (self._vpnurl, self._localurl) = \ + self._data.welcomedata.cameraUrls(camera=self._camera_name) + return None + return response.content + + @property + def name(self): + """Return the name of this Netatmo Welcome device.""" + return self._name + + +class WelcomeData(object): + """Get the latest data from NetAtmo.""" + + def __init__(self, auth, home=None): + """Initialize the data object.""" + self.auth = auth + self.welcomedata = None + self.camera_names = [] + self.home = home + + def get_camera_names(self): + """Return all module available on the API as a list.""" + self.update() + if not self.home: + for home in self.welcomedata.cameras.keys(): + for camera in self.welcomedata.cameras[home].values(): + self.camera_names.append(camera['name']) + else: + for camera in self.welcomedata.cameras[self.home].values(): + self.camera_names.append(camera['name']) + return self.camera_names + + @Throttle(MIN_TIME_BETWEEN_UPDATES) + def update(self): + """Call the NetAtmo API to update the data.""" + import lnetatmo + self.welcomedata = lnetatmo.WelcomeData(self.auth) diff --git a/homeassistant/components/netatmo.py b/homeassistant/components/netatmo.py new file mode 100644 index 00000000000..737819fffbb --- /dev/null +++ b/homeassistant/components/netatmo.py @@ -0,0 +1,62 @@ +""" +Support for the Netatmo devices (Weather Station and Welcome camera). + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/netatmo/ +""" +import logging +from urllib.error import HTTPError +from homeassistant.components import discovery +from homeassistant.const import ( + CONF_API_KEY, CONF_PASSWORD, CONF_USERNAME) +from homeassistant.helpers import validate_config + +REQUIREMENTS = [ + 'https://github.com/jabesq/netatmo-api-python/archive/' + 'v0.5.0.zip#lnetatmo==0.5.0'] + +_LOGGER = logging.getLogger(__name__) + +CONF_SECRET_KEY = 'secret_key' + +DOMAIN = "netatmo" +NETATMO_AUTH = None + +_LOGGER = logging.getLogger(__name__) + +DISCOVER_SENSORS = 'netatmo.sensors' +DISCOVER_CAMERAS = 'netatmo.cameras' + + +def setup(hass, config): + """Setup the Netatmo devices.""" + if not validate_config(config, + {DOMAIN: [CONF_API_KEY, + CONF_USERNAME, + CONF_PASSWORD, + CONF_SECRET_KEY]}, + _LOGGER): + return None + + import lnetatmo + + global NETATMO_AUTH + try: + NETATMO_AUTH = lnetatmo.ClientAuth(config[DOMAIN][CONF_API_KEY], + config[DOMAIN][CONF_SECRET_KEY], + config[DOMAIN][CONF_USERNAME], + config[DOMAIN][CONF_PASSWORD], + "read_station read_camera " + "access_camera") + except HTTPError: + _LOGGER.error( + "Connection error " + "Please check your settings for NatAtmo API.") + return False + + for component, discovery_service in ( + ('camera', DISCOVER_CAMERAS), ('sensor', DISCOVER_SENSORS)): + discovery.discover(hass, discovery_service, component=component, + hass_config=config) + + return True diff --git a/homeassistant/components/sensor/__init__.py b/homeassistant/components/sensor/__init__.py index 3c625ae4a85..4cd5c50f22e 100644 --- a/homeassistant/components/sensor/__init__.py +++ b/homeassistant/components/sensor/__init__.py @@ -10,7 +10,7 @@ from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa from homeassistant.components import ( wink, zwave, isy994, verisure, ecobee, tellduslive, mysensors, - bloomsky, vera) + bloomsky, vera, netatmo) DOMAIN = 'sensor' SCAN_INTERVAL = 30 @@ -28,6 +28,7 @@ DISCOVERY_PLATFORMS = { tellduslive.DISCOVER_SENSORS: 'tellduslive', mysensors.DISCOVER_SENSORS: 'mysensors', vera.DISCOVER_SENSORS: 'vera', + netatmo.DISCOVER_SENSORS: 'netatmo', } diff --git a/homeassistant/components/sensor/netatmo.py b/homeassistant/components/sensor/netatmo.py index 184f9fe18a2..22caab1d1fb 100644 --- a/homeassistant/components/sensor/netatmo.py +++ b/homeassistant/components/sensor/netatmo.py @@ -6,18 +6,12 @@ https://home-assistant.io/components/sensor.netatmo/ """ import logging from datetime import timedelta - -from homeassistant.components.sensor import DOMAIN -from homeassistant.const import ( - CONF_API_KEY, CONF_PASSWORD, CONF_USERNAME, TEMP_CELSIUS) -from homeassistant.helpers import validate_config +from homeassistant.const import TEMP_CELSIUS from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle +from homeassistant.loader import get_component -REQUIREMENTS = [ - 'https://github.com/HydrelioxGitHub/netatmo-api-python/archive/' - '43ff238a0122b0939a0dc4e8836b6782913fb6e2.zip' - '#lnetatmo==0.4.0'] +DEPENDENCIES = ["netatmo"] _LOGGER = logging.getLogger(__name__) @@ -32,7 +26,6 @@ SENSOR_TYPES = { 'sum_rain_24': ['sum_rain_24', 'mm', 'mdi:weather-rainy'], } -CONF_SECRET_KEY = 'secret_key' CONF_STATION = 'station' ATTR_MODULE = 'modules' @@ -43,29 +36,9 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=600) def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup the NetAtmo sensor.""" - if not validate_config({DOMAIN: config}, - {DOMAIN: [CONF_API_KEY, - CONF_USERNAME, - CONF_PASSWORD, - CONF_SECRET_KEY]}, - _LOGGER): - return None - - import lnetatmo - - authorization = lnetatmo.ClientAuth(config.get(CONF_API_KEY, None), - config.get(CONF_SECRET_KEY, None), - config.get(CONF_USERNAME, None), - config.get(CONF_PASSWORD, None)) - - if not authorization: - _LOGGER.error( - "Connection error " - "Please check your settings for NatAtmo API.") - return False - - data = NetAtmoData(authorization, config.get(CONF_STATION, None)) + """Setup the available Netatmo weather sensors.""" + netatmo = get_component('netatmo') + data = NetAtmoData(netatmo.NETATMO_AUTH, config.get(CONF_STATION, None)) dev = [] try: diff --git a/requirements_all.txt b/requirements_all.txt index 6c55f638b28..07db2ad3a48 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -94,9 +94,6 @@ hikvision==0.4 # homeassistant.components.sensor.dht # http://github.com/mala-zaba/Adafruit_Python_DHT/archive/4101340de8d2457dd194bca1e8d11cbfc237e919.zip#Adafruit_DHT==1.1.0 -# homeassistant.components.sensor.netatmo -https://github.com/HydrelioxGitHub/netatmo-api-python/archive/43ff238a0122b0939a0dc4e8836b6782913fb6e2.zip#lnetatmo==0.4.0 - # homeassistant.components.switch.dlink https://github.com/LinuxChristian/pyW215/archive/v0.1.1.zip#pyW215==0.1.1 @@ -123,6 +120,9 @@ https://github.com/danieljkemp/onkyo-eiscp/archive/python3.zip#onkyo-eiscp==0.9. # homeassistant.components.device_tracker.fritz # https://github.com/deisi/fritzconnection/archive/b5c14515e1c8e2652b06b6316a7f3913df942841.zip#fritzconnection==0.4.6 +# homeassistant.components.netatmo +https://github.com/jabesq/netatmo-api-python/archive/v0.5.0.zip#lnetatmo==0.5.0 + # homeassistant.components.sensor.sabnzbd https://github.com/jamespcole/home-assistant-nzb-clients/archive/616cad59154092599278661af17e2a9f2cf5e2a9.zip#python-sabnzbd==0.1