diff --git a/homeassistant/components/binary_sensor/netatmo.py b/homeassistant/components/binary_sensor/netatmo.py index 94ef0faaad0..4ef29b9e5f5 100644 --- a/homeassistant/components/binary_sensor/netatmo.py +++ b/homeassistant/components/binary_sensor/netatmo.py @@ -1,19 +1,19 @@ """ Support for the Netatmo binary sensors. -The binary sensors based on events seen by the NetatmoCamera +The binary sensors based on events seen by the Netatmo cameras. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.netatmo/ +https://home-assistant.io/components/binary_sensor.netatmo/. """ import logging import voluptuous as vol from homeassistant.components.binary_sensor import ( BinarySensorDevice, PLATFORM_SCHEMA) -from homeassistant.components.netatmo import WelcomeData +from homeassistant.components.netatmo import CameraData from homeassistant.loader import get_component -from homeassistant.const import CONF_MONITORED_CONDITIONS, CONF_TIMEOUT +from homeassistant.const import CONF_TIMEOUT, CONF_OFFSET from homeassistant.helpers import config_validation as cv DEPENDENCIES = ["netatmo"] @@ -22,24 +22,37 @@ _LOGGER = logging.getLogger(__name__) # These are the available sensors mapped to binary_sensor class -SENSOR_TYPES = { - "Someone known": 'occupancy', - "Someone unknown": 'motion', - "Motion": 'motion', +WELCOME_SENSOR_TYPES = { + "Someone known": "motion", + "Someone unknown": "motion", + "Motion": "motion", "Tag Vibration": 'vibration', - "Tag Open": 'opening', + "Tag Open": 'opening' +} +PRESENCE_SENSOR_TYPES = { + "Outdoor motion": "motion", + "Outdoor human": "motion", + "Outdoor animal": "motion", + "Outdoor vehicle": "motion" } CONF_HOME = 'home' CONF_CAMERAS = 'cameras' +CONF_WELCOME_SENSORS = 'welcome_sensors' +CONF_PRESENCE_SENSORS = 'presence_sensors' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_HOME): cv.string, vol.Optional(CONF_TIMEOUT): cv.positive_int, + vol.Optional(CONF_OFFSET): cv.positive_int, vol.Optional(CONF_CAMERAS, default=[]): vol.All(cv.ensure_list, [cv.string]), - vol.Optional(CONF_MONITORED_CONDITIONS, default=SENSOR_TYPES.keys()): - vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]), + vol.Optional( + CONF_WELCOME_SENSORS, default=WELCOME_SENSOR_TYPES.keys()): + vol.All(cv.ensure_list, [vol.In(WELCOME_SENSOR_TYPES)]), + vol.Optional( + CONF_PRESENCE_SENSORS, default=PRESENCE_SENSOR_TYPES.keys()): + vol.All(cv.ensure_list, [vol.In(PRESENCE_SENSOR_TYPES)]), }) @@ -49,48 +62,68 @@ def setup_platform(hass, config, add_devices, discovery_info=None): netatmo = get_component('netatmo') home = config.get(CONF_HOME, None) timeout = config.get(CONF_TIMEOUT, 15) + offset = config.get(CONF_OFFSET, 90) module_name = None import lnetatmo try: - data = WelcomeData(netatmo.NETATMO_AUTH, home) + data = CameraData(netatmo.NETATMO_AUTH, home) if data.get_camera_names() == []: return None except lnetatmo.NoDevice: return None - sensors = config.get(CONF_MONITORED_CONDITIONS, SENSOR_TYPES) + welcome_sensors = config.get( + CONF_WELCOME_SENSORS, WELCOME_SENSOR_TYPES) + presence_sensors = config.get( + CONF_PRESENCE_SENSORS, PRESENCE_SENSOR_TYPES) for camera_name in data.get_camera_names(): - if CONF_CAMERAS in config: - if config[CONF_CAMERAS] != [] and \ - camera_name not in config[CONF_CAMERAS]: - continue - for variable in sensors: - if variable in ('Tag Vibration', 'Tag Open'): - continue - add_devices([WelcomeBinarySensor(data, camera_name, module_name, - home, timeout, variable)]) + camera_type = data.get_camera_type(camera=camera_name, home=home) + if camera_type == "NACamera": + if CONF_CAMERAS in config: + if config[CONF_CAMERAS] != [] and \ + camera_name not in config[CONF_CAMERAS]: + continue + for variable in welcome_sensors: + add_devices([NetatmoBinarySensor(data, camera_name, + module_name, home, timeout, + offset, camera_type, + variable)]) + if camera_type == "NOC": + if CONF_CAMERAS in config: + if config[CONF_CAMERAS] != [] and \ + camera_name not in config[CONF_CAMERAS]: + continue + for variable in presence_sensors: + add_devices([NetatmoBinarySensor(data, camera_name, + module_name, home, timeout, + offset, camera_type, + variable)]) for module_name in data.get_module_names(camera_name): - for variable in sensors: + for variable in welcome_sensors: if variable in ('Tag Vibration', 'Tag Open'): - add_devices([WelcomeBinarySensor(data, camera_name, + add_devices([NetatmoBinarySensor(data, camera_name, module_name, home, - timeout, variable)]) + timeout, offset, + camera_type, + variable)]) -class WelcomeBinarySensor(BinarySensorDevice): - """Represent a single binary sensor in a Netatmo Welcome device.""" +class NetatmoBinarySensor(BinarySensorDevice): + """Represent a single binary sensor in a Netatmo Camera device.""" - def __init__(self, data, camera_name, module_name, home, timeout, sensor): + def __init__(self, data, camera_name, module_name, home, + timeout, offset, camera_type, sensor): """Setup for access to the Netatmo camera events.""" self._data = data self._camera_name = camera_name self._module_name = module_name self._home = home self._timeout = timeout + self._offset = offset if home: self._name = home + ' / ' + camera_name else: @@ -99,10 +132,11 @@ class WelcomeBinarySensor(BinarySensorDevice): self._name += ' / ' + module_name self._sensor_name = sensor self._name += ' ' + sensor - camera_id = data.welcomedata.cameraByName(camera=camera_name, + camera_id = data.camera_data.cameraByName(camera=camera_name, home=home)['id'] - self._unique_id = "Welcome_binary_sensor {0} - {1}".format(self._name, + self._unique_id = "Netatmo_binary_sensor {0} - {1}".format(self._name, camera_id) + self._cameratype = camera_type self.update() @property @@ -118,7 +152,12 @@ class WelcomeBinarySensor(BinarySensorDevice): @property def sensor_class(self): """Return the class of this sensor, from SENSOR_CLASSES.""" - return SENSOR_TYPES.get(self._sensor_name) + if self._cameratype == "NACamera": + return WELCOME_SENSOR_TYPES.get(self._sensor_name) + elif self._cameratype == "NOC": + return PRESENCE_SENSOR_TYPES.get(self._sensor_name) + else: + return None @property def is_on(self): @@ -130,30 +169,54 @@ class WelcomeBinarySensor(BinarySensorDevice): self._data.update() self._data.update_event() - if self._sensor_name == "Someone known": - self._state =\ - self._data.welcomedata.someoneKnownSeen(self._home, + if self._cameratype == "NACamera": + if self._sensor_name == "Someone known": + self._state =\ + self._data.camera_data.someoneKnownSeen(self._home, self._camera_name, self._timeout*60) - elif self._sensor_name == "Someone unknown": - self._state =\ - self._data.welcomedata.someoneUnknownSeen(self._home, + elif self._sensor_name == "Someone unknown": + self._state =\ + self._data.camera_data.someoneUnknownSeen( + self._home, self._camera_name, self._timeout*60) + elif self._sensor_name == "Motion": + self._state =\ + self._data.camera_data.motionDetected(self._home, self._camera_name, self._timeout*60) - elif self._sensor_name == "Motion": - self._state =\ - self._data.welcomedata.motionDetected(self._home, - self._camera_name, - self._timeout*60) + else: + return None + elif self._cameratype == "NOC": + if self._sensor_name == "Outdoor motion": + self._state =\ + self._data.camera_data.outdoormotionDetected( + self._home, self._camera_name, self._offset) + elif self._sensor_name == "Outdoor human": + self._state =\ + self._data.camera_data.humanDetected(self._home, + self._camera_name, + self._offset) + elif self._sensor_name == "Outdoor animal": + self._state =\ + self._data.camera_data.animalDetected(self._home, + self._camera_name, + self._offset) + elif self._sensor_name == "Outdoor vehicle": + self._state =\ + self._data.camera_data.carDetected(self._home, + self._camera_name, + self._offset) + else: + return None elif self._sensor_name == "Tag Vibration": self._state =\ - self._data.welcomedata.moduleMotionDetected(self._home, + self._data.camera_data.moduleMotionDetected(self._home, self._module_name, self._camera_name, self._timeout*60) elif self._sensor_name == "Tag Open": self._state =\ - self._data.welcomedata.moduleOpened(self._home, + self._data.camera_data.moduleOpened(self._home, self._module_name, self._camera_name) else: diff --git a/homeassistant/components/camera/netatmo.py b/homeassistant/components/camera/netatmo.py index 47808de02b9..563de206dea 100644 --- a/homeassistant/components/camera/netatmo.py +++ b/homeassistant/components/camera/netatmo.py @@ -1,15 +1,15 @@ """ -Support for the Netatmo Welcome camera. +Support for the Netatmo cameras. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/camera.netatmo/ +https://home-assistant.io/components/camera.netatmo/. """ import logging import requests import voluptuous as vol -from homeassistant.components.netatmo import WelcomeData +from homeassistant.components.netatmo import CameraData from homeassistant.components.camera import (Camera, PLATFORM_SCHEMA) from homeassistant.loader import get_component from homeassistant.helpers import config_validation as cv @@ -30,41 +30,43 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ # pylint: disable=unused-argument def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup access to Netatmo Welcome cameras.""" + """Setup access to Netatmo cameras.""" netatmo = get_component('netatmo') home = config.get(CONF_HOME) import lnetatmo try: - data = WelcomeData(netatmo.NETATMO_AUTH, home) + data = CameraData(netatmo.NETATMO_AUTH, home) for camera_name in data.get_camera_names(): + camera_type = data.get_camera_type(camera=camera_name, home=home) if CONF_CAMERAS in config: if config[CONF_CAMERAS] != [] and \ camera_name not in config[CONF_CAMERAS]: continue - add_devices([WelcomeCamera(data, camera_name, home)]) + add_devices([NetatmoCamera(data, camera_name, home, camera_type)]) except lnetatmo.NoDevice: return None -class WelcomeCamera(Camera): - """Representation of the images published from Welcome camera.""" +class NetatmoCamera(Camera): + """Representation of the images published from a Netatmo camera.""" - def __init__(self, data, camera_name, home): + def __init__(self, data, camera_name, home, camera_type): """Setup for access to the Netatmo camera images.""" - super(WelcomeCamera, self).__init__() + super(NetatmoCamera, self).__init__() self._data = data self._camera_name = camera_name if home: self._name = home + ' / ' + camera_name else: self._name = camera_name - camera_id = data.welcomedata.cameraByName(camera=camera_name, + camera_id = data.camera_data.cameraByName(camera=camera_name, home=home)['id'] self._unique_id = "Welcome_camera {0} - {1}".format(self._name, camera_id) - self._vpnurl, self._localurl = self._data.welcomedata.cameraUrls( + self._vpnurl, self._localurl = self._data.camera_data.cameraUrls( camera=camera_name ) + self._cameratype = camera_type def camera_image(self): """Return a still image response from the camera.""" @@ -79,15 +81,30 @@ class WelcomeCamera(Camera): _LOGGER.error('Welcome VPN url changed: %s', error) self._data.update() (self._vpnurl, self._localurl) = \ - self._data.welcomedata.cameraUrls(camera=self._camera_name) + self._data.camera_data.cameraUrls(camera=self._camera_name) return None return response.content @property def name(self): - """Return the name of this Netatmo Welcome device.""" + """Return the name of this Netatmo camera device.""" return self._name + @property + def brand(self): + """Camera brand.""" + return "Netatmo" + + @property + def model(self): + """Camera model.""" + if self._cameratype == "NOC": + return "Presence" + elif self._cameratype == "NACamera": + return "Welcome" + else: + return None + @property def unique_id(self): """Return the unique ID for this sensor.""" diff --git a/homeassistant/components/netatmo.py b/homeassistant/components/netatmo.py index b4ebbc1d460..ea691ac94f4 100644 --- a/homeassistant/components/netatmo.py +++ b/homeassistant/components/netatmo.py @@ -18,7 +18,7 @@ from homeassistant.util import Throttle REQUIREMENTS = [ 'https://github.com/jabesq/netatmo-api-python/archive/' - 'v0.8.1.zip#lnetatmo==0.8.1'] + 'v0.9.0.zip#lnetatmo==0.9.0'] _LOGGER = logging.getLogger(__name__) @@ -53,7 +53,8 @@ def setup(hass, config): config[DOMAIN][CONF_API_KEY], config[DOMAIN][CONF_SECRET_KEY], config[DOMAIN][CONF_USERNAME], config[DOMAIN][CONF_PASSWORD], 'read_station read_camera access_camera ' - 'read_thermostat write_thermostat') + 'read_thermostat write_thermostat ' + 'read_presence access_presence') except HTTPError: _LOGGER.error("Unable to connect to Netatmo API") return False @@ -65,27 +66,28 @@ def setup(hass, config): return True -class WelcomeData(object): +class CameraData(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_data = None self.camera_names = [] self.module_names = [] self.home = home + self.camera_type = None def get_camera_names(self): """Return all camera available on the API as a list.""" self.camera_names = [] self.update() if not self.home: - for home in self.welcomedata.cameras: - for camera in self.welcomedata.cameras[home].values(): + for home in self.camera_data.cameras: + for camera in self.camera_data.cameras[home].values(): self.camera_names.append(camera['name']) else: - for camera in self.welcomedata.cameras[self.home].values(): + for camera in self.camera_data.cameras[self.home].values(): self.camera_names.append(camera['name']) return self.camera_names @@ -93,20 +95,27 @@ class WelcomeData(object): """Return all module available on the API as a list.""" self.module_names = [] self.update() - cam_id = self.welcomedata.cameraByName(camera=camera_name, + cam_id = self.camera_data.cameraByName(camera=camera_name, home=self.home)['id'] - for module in self.welcomedata.modules.values(): + for module in self.camera_data.modules.values(): if cam_id == module['cam_id']: self.module_names.append(module['name']) return self.module_names + def get_camera_type(self, camera=None, home=None, cid=None): + """Return all module available on the API as a list.""" + for camera_name in self.camera_names: + self.camera_type = self.camera_data.cameraType(camera_name) + return self.camera_type + @Throttle(MIN_TIME_BETWEEN_UPDATES) def update(self): """Call the Netatmo API to update the data.""" import lnetatmo - self.welcomedata = lnetatmo.WelcomeData(self.auth, size=100) + self.camera_data = lnetatmo.CameraData(self.auth, size=100) @Throttle(MIN_TIME_BETWEEN_EVENT_UPDATES) def update_event(self): - """Call the Netatmo API to update the list of events.""" - self.welcomedata.updateEvent(home=self.home) + """Call the Netatmo API to update the events.""" + self.camera_data.updateEvent( + home=self.home, cameratype=self.camera_type) diff --git a/requirements_all.txt b/requirements_all.txt index 7edbb4bcc70..31613054a52 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -214,7 +214,7 @@ https://github.com/danieljkemp/onkyo-eiscp/archive/python3.zip#onkyo-eiscp==0.9. # https://github.com/deisi/fritzconnection/archive/b5c14515e1c8e2652b06b6316a7f3913df942841.zip#fritzconnection==0.4.6 # homeassistant.components.netatmo -https://github.com/jabesq/netatmo-api-python/archive/v0.8.1.zip#lnetatmo==0.8.1 +https://github.com/jabesq/netatmo-api-python/archive/v0.9.0.zip#lnetatmo==0.9.0 # homeassistant.components.neato https://github.com/jabesq/pybotvac/archive/v0.0.1.zip#pybotvac==0.0.1