From d6ac61a3a4e5a648288492befd6d190479854935 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Mon, 28 May 2018 03:55:20 -0400 Subject: [PATCH 01/17] start arlo refactoring --- homeassistant/components/arlo.py | 34 +++++++++++++++++++++-- homeassistant/components/sensor/arlo.py | 37 +++++++++++++++---------- 2 files changed, 55 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/arlo.py b/homeassistant/components/arlo.py index 7e51ec8c045e17..4842f6bec543a8 100644 --- a/homeassistant/components/arlo.py +++ b/homeassistant/components/arlo.py @@ -4,13 +4,19 @@ For more details about this component, please refer to the documentation at https://home-assistant.io/components/arlo/ """ +import asyncio import logging +from datetime import timedelta import voluptuous as vol from requests.exceptions import HTTPError, ConnectTimeout from homeassistant.helpers import config_validation as cv -from homeassistant.const import CONF_USERNAME, CONF_PASSWORD +from homeassistant.const import ( + CONF_USERNAME, CONF_PASSWORD, CONF_SCAN_INTERVAL) +from homeassistant.helpers.event import track_time_interval +from homeassistant.helpers.dispatcher import ( + async_dispatcher_connect, dispatcher_send) REQUIREMENTS = ['pyarlo==0.1.2'] @@ -25,10 +31,17 @@ NOTIFICATION_ID = 'arlo_notification' NOTIFICATION_TITLE = 'Arlo Component Setup' +#SCAN_INTERVAL = timedelta(minutes=5) +SCAN_INTERVAL = timedelta(seconds=60) + +SIGNAL_UPDATE_ARLO = "arlo_update" + CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ vol.Required(CONF_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string, + vol.Optional(CONF_SCAN_INTERVAL, default=SCAN_INTERVAL): + cv.time_period, }), }, extra=vol.ALLOW_EXTRA) @@ -38,6 +51,7 @@ def setup(hass, config): conf = config[DOMAIN] username = conf.get(CONF_USERNAME) password = conf.get(CONF_PASSWORD) + scan_interval = conf.get(CONF_SCAN_INTERVAL) try: from pyarlo import PyArlo @@ -45,7 +59,7 @@ def setup(hass, config): arlo = PyArlo(username, password, preload=False) if not arlo.is_connected: return False - hass.data[DATA_ARLO] = arlo + hass.data[DATA_ARLO] = ArloHub(arlo) except (ConnectTimeout, HTTPError) as ex: _LOGGER.error("Unable to connect to Netgear Arlo: %s", str(ex)) hass.components.persistent_notification.create( @@ -55,4 +69,20 @@ def setup(hass, config): title=NOTIFICATION_TITLE, notification_id=NOTIFICATION_ID) return False + + def hub_refresh(event_time): + """Call ArloHub to refresh information.""" + _LOGGER.debug("Updating Arlo Hub component") + hass.data[DATA_ARLO].hub.update() + dispatcher_send(hass, SIGNAL_UPDATE_ARLO) + + track_time_interval(hass, hub_refresh, scan_interval) return True + + +class ArloHub(object): + """Representation of the base Arlo hub component.""" + + def __init__(self, hub): + """Initialize the entity.""" + self.hub = hub diff --git a/homeassistant/components/sensor/arlo.py b/homeassistant/components/sensor/arlo.py index 97b7ac2290940d..c98e013fbae270 100644 --- a/homeassistant/components/sensor/arlo.py +++ b/homeassistant/components/sensor/arlo.py @@ -12,9 +12,11 @@ import homeassistant.helpers.config_validation as cv from homeassistant.components.arlo import ( - CONF_ATTRIBUTION, DEFAULT_BRAND, DATA_ARLO) + CONF_ATTRIBUTION, DEFAULT_BRAND, DATA_ARLO, SIGNAL_UPDATE_ARLO) from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import (ATTR_ATTRIBUTION, CONF_MONITORED_CONDITIONS) +from homeassistant.helpers.dispatcher import ( + async_dispatcher_connect, dispatcher_send) from homeassistant.helpers.entity import Entity from homeassistant.helpers.icon import icon_for_battery_level @@ -22,8 +24,6 @@ DEPENDENCIES = ['arlo'] -SCAN_INTERVAL = timedelta(seconds=90) - # sensor_type [ description, unit, icon ] SENSOR_TYPES = { 'last_capture': ['Last', None, 'run-fast'], @@ -39,10 +39,9 @@ }) -@asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def setup_platform(hass, config, add_devices, discovery_info=None): """Set up an Arlo IP sensor.""" - arlo = hass.data.get(DATA_ARLO) + arlo = hass.data.get(DATA_ARLO).hub if not arlo: return False @@ -50,24 +49,23 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): for sensor_type in config.get(CONF_MONITORED_CONDITIONS): if sensor_type == 'total_cameras': sensors.append(ArloSensor( - hass, SENSOR_TYPES[sensor_type][0], arlo, sensor_type)) + SENSOR_TYPES[sensor_type][0], arlo, sensor_type)) else: for camera in arlo.cameras: name = '{0} {1}'.format( SENSOR_TYPES[sensor_type][0], camera.name) - sensors.append(ArloSensor(hass, name, camera, sensor_type)) + sensors.append(ArloSensor(name, camera, sensor_type)) - async_add_devices(sensors, True) + add_devices(sensors, True) + return True class ArloSensor(Entity): """An implementation of a Netgear Arlo IP sensor.""" - def __init__(self, hass, name, device, sensor_type): + def __init__(self, name, device, sensor_type): """Initialize an Arlo sensor.""" - super().__init__() self._name = name - self._hass = hass self._data = device self._sensor_type = sensor_type self._state = None @@ -78,6 +76,16 @@ def name(self): """Return the name of this camera.""" return self._name + @asyncio.coroutine + def async_added_to_hass(self): + """Register callbacks.""" + async_dispatcher_connect( + self.hass, SIGNAL_UPDATE_ARLO, self._update_callback) + + def _update_callback(self): + """Call update method.""" + self.schedule_update_ha_state(True) + @property def state(self): """Return the state of the sensor.""" @@ -98,6 +106,7 @@ def unit_of_measurement(self): def update(self): """Get the latest data and updates the state.""" + _LOGGER.debug("Updating Arlo sensor component") try: base_station = self._data.base_station except (AttributeError, IndexError): @@ -106,9 +115,9 @@ def update(self): if not base_station: return - base_station.refresh_rate = SCAN_INTERVAL.total_seconds() + #base_station.refresh_rate = SCAN_INTERVAL.total_seconds() - self._data.update() + #self._data.update() if self._sensor_type == 'total_cameras': self._state = len(self._data.cameras) From ec1442f1a9fef13ce4f7f3cc5242c1a5e39c3052 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Tue, 29 May 2018 03:19:02 -0400 Subject: [PATCH 02/17] Refactored Arlo Hub to avoid uncessary and duplicated GETs to Arlo API --- homeassistant/components/arlo.py | 21 +++++++++++++++++---- homeassistant/components/sensor/arlo.py | 16 ++-------------- requirements_all.txt | 2 +- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/arlo.py b/homeassistant/components/arlo.py index 4842f6bec543a8..6e4017d54b3a9f 100644 --- a/homeassistant/components/arlo.py +++ b/homeassistant/components/arlo.py @@ -17,8 +17,9 @@ from homeassistant.helpers.event import track_time_interval from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, dispatcher_send) +from homeassistant.util.dt import now -REQUIREMENTS = ['pyarlo==0.1.2'] +REQUIREMENTS = ['pyarlo==0.1.3'] _LOGGER = logging.getLogger(__name__) @@ -31,8 +32,7 @@ NOTIFICATION_ID = 'arlo_notification' NOTIFICATION_TITLE = 'Arlo Component Setup' -#SCAN_INTERVAL = timedelta(minutes=5) -SCAN_INTERVAL = timedelta(seconds=60) +SCAN_INTERVAL = timedelta(minutes=5) SIGNAL_UPDATE_ARLO = "arlo_update" @@ -59,6 +59,15 @@ def setup(hass, config): arlo = PyArlo(username, password, preload=False) if not arlo.is_connected: return False + + # assign refresh period to base station thread + try: + if arlo.base_stations: + arlo_base_station = arlo.base_stations[0] + arlo_base_station.refresh_rate = scan_interval + except (AttributeError, IndexError): + return False + hass.data[DATA_ARLO] = ArloHub(arlo) except (ConnectTimeout, HTTPError) as ex: _LOGGER.error("Unable to connect to Netgear Arlo: %s", str(ex)) @@ -73,9 +82,13 @@ def setup(hass, config): def hub_refresh(event_time): """Call ArloHub to refresh information.""" _LOGGER.debug("Updating Arlo Hub component") - hass.data[DATA_ARLO].hub.update() + hass.data[DATA_ARLO].hub.update(update_cameras=True) dispatcher_send(hass, SIGNAL_UPDATE_ARLO) + # register service + hass.services.register(DOMAIN, 'update', hub_refresh) + + # register scan interval for ArloHub track_time_interval(hass, hub_refresh, scan_interval) return True diff --git a/homeassistant/components/sensor/arlo.py b/homeassistant/components/sensor/arlo.py index c98e013fbae270..a0cc5b0107fd50 100644 --- a/homeassistant/components/sensor/arlo.py +++ b/homeassistant/components/sensor/arlo.py @@ -106,19 +106,7 @@ def unit_of_measurement(self): def update(self): """Get the latest data and updates the state.""" - _LOGGER.debug("Updating Arlo sensor component") - try: - base_station = self._data.base_station - except (AttributeError, IndexError): - return - - if not base_station: - return - - #base_station.refresh_rate = SCAN_INTERVAL.total_seconds() - - #self._data.update() - + _LOGGER.debug("Updating Arlo sensor %s", self.name) if self._sensor_type == 'total_cameras': self._state = len(self._data.cameras) @@ -127,7 +115,7 @@ def update(self): elif self._sensor_type == 'last_capture': try: - video = self._data.videos()[0] + video = self._data.last_video self._state = video.created_at_pretty("%m-%d-%Y %H:%M:%S") except (AttributeError, IndexError): self._state = None diff --git a/requirements_all.txt b/requirements_all.txt index 9e7d73b053b390..d8c2dbbbb21eec 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -725,7 +725,7 @@ pyairvisual==1.0.0 pyalarmdotcom==0.3.2 # homeassistant.components.arlo -pyarlo==0.1.2 +pyarlo==0.1.3 # homeassistant.components.notify.xmpp pyasn1-modules==0.1.5 From 3f3387af006aa75294ab9ac34fac8e8926eaedde Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Thu, 31 May 2018 03:24:45 -0400 Subject: [PATCH 03/17] Refactored Arlo camera component to avoid duplicate queries --- homeassistant/components/arlo.py | 5 ++-- homeassistant/components/camera/arlo.py | 36 ++++++++++++++----------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/arlo.py b/homeassistant/components/arlo.py index 6e4017d54b3a9f..9886d8853ede20 100644 --- a/homeassistant/components/arlo.py +++ b/homeassistant/components/arlo.py @@ -32,7 +32,7 @@ NOTIFICATION_ID = 'arlo_notification' NOTIFICATION_TITLE = 'Arlo Component Setup' -SCAN_INTERVAL = timedelta(minutes=5) +SCAN_INTERVAL = timedelta(seconds=60) SIGNAL_UPDATE_ARLO = "arlo_update" @@ -82,7 +82,8 @@ def setup(hass, config): def hub_refresh(event_time): """Call ArloHub to refresh information.""" _LOGGER.debug("Updating Arlo Hub component") - hass.data[DATA_ARLO].hub.update(update_cameras=True) + hass.data[DATA_ARLO].hub.update(update_cameras=True, + update_base_station=True) dispatcher_send(hass, SIGNAL_UPDATE_ARLO) # register service diff --git a/homeassistant/components/camera/arlo.py b/homeassistant/components/camera/arlo.py index f3e70c2bdd74c9..bed744ff6d1827 100644 --- a/homeassistant/components/camera/arlo.py +++ b/homeassistant/components/camera/arlo.py @@ -11,16 +11,17 @@ import voluptuous as vol import homeassistant.helpers.config_validation as cv -from homeassistant.components.arlo import DEFAULT_BRAND, DATA_ARLO +from homeassistant.components.arlo import ( + DEFAULT_BRAND, DATA_ARLO, SIGNAL_UPDATE_ARLO) from homeassistant.components.camera import Camera, PLATFORM_SCHEMA from homeassistant.components.ffmpeg import DATA_FFMPEG from homeassistant.const import ATTR_BATTERY_LEVEL from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream +from homeassistant.helpers.dispatcher import ( + async_dispatcher_connect, dispatcher_send) _LOGGER = logging.getLogger(__name__) -SCAN_INTERVAL = timedelta(seconds=90) - ARLO_MODE_ARMED = 'armed' ARLO_MODE_DISARMED = 'disarmed' @@ -44,14 +45,13 @@ } PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_FFMPEG_ARGUMENTS): - cv.string, + vol.Optional(CONF_FFMPEG_ARGUMENTS): cv.string, }) def setup_platform(hass, config, add_devices, discovery_info=None): """Set up an Arlo IP Camera.""" - arlo = hass.data.get(DATA_ARLO) + arlo = hass.data.get(DATA_ARLO).hub if not arlo: return False @@ -74,14 +74,21 @@ def __init__(self, hass, camera, device_info): self._ffmpeg = hass.data[DATA_FFMPEG] self._ffmpeg_arguments = device_info.get(CONF_FFMPEG_ARGUMENTS) self._last_refresh = None - if self._camera.base_station: - self._camera.base_station.refresh_rate = \ - SCAN_INTERVAL.total_seconds() self.attrs = {} def camera_image(self): """Return a still image response from the camera.""" - return self._camera.last_image + return self._camera.last_image_from_cache + + @asyncio.coroutine + def async_added_to_hass(self): + """Register callbacks.""" + async_dispatcher_connect( + self.hass, SIGNAL_UPDATE_ARLO, self._update_callback) + + def _update_callback(self): + """Call update method.""" + self.schedule_update_ha_state(True) @asyncio.coroutine def handle_async_mjpeg_stream(self, request): @@ -89,6 +96,7 @@ def handle_async_mjpeg_stream(self, request): from haffmpeg import CameraMjpeg video = self._camera.last_video if not video: + _LOGGER.error("Could not find video for Arlo %s" % self.name) return stream = CameraMjpeg(self._ffmpeg.binary, loop=self.hass.loop) @@ -135,7 +143,7 @@ def brand(self): @property def should_poll(self): """Camera should poll periodically.""" - return True + return False @property def motion_detection_enabled(self): @@ -145,7 +153,7 @@ def motion_detection_enabled(self): def set_base_station_mode(self, mode): """Set the mode in the base station.""" # Get the list of base stations identified by library - base_stations = self.hass.data[DATA_ARLO].base_stations + base_stations = self.hass.data.get(DATA_ARLO).hub.base_stations # Some Arlo cameras does not have base station # So check if there is base station detected first @@ -164,7 +172,3 @@ def disable_motion_detection(self): """Disable the motion detection in base station (Disarm).""" self._motion_status = False self.set_base_station_mode(ARLO_MODE_DISARMED) - - def update(self): - """Add an attribute-update task to the executor pool.""" - self._camera.update() From e0a252f1d92c98817d77cb76622ef3e41def7b75 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Thu, 31 May 2018 03:40:02 -0400 Subject: [PATCH 04/17] Added debug and error messages when video is not found --- homeassistant/components/camera/arlo.py | 5 ++++- homeassistant/components/sensor/arlo.py | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/camera/arlo.py b/homeassistant/components/camera/arlo.py index bed744ff6d1827..c40799881cf0d3 100644 --- a/homeassistant/components/camera/arlo.py +++ b/homeassistant/components/camera/arlo.py @@ -96,7 +96,10 @@ def handle_async_mjpeg_stream(self, request): from haffmpeg import CameraMjpeg video = self._camera.last_video if not video: - _LOGGER.error("Could not find video for Arlo %s" % self.name) + error_msg = \ + 'Video not found for {}. Is it older than 180 days?'.format( + self.name) + _LOGGER.error(error_msg) return stream = CameraMjpeg(self._ffmpeg.binary, loop=self.hass.loop) diff --git a/homeassistant/components/sensor/arlo.py b/homeassistant/components/sensor/arlo.py index a0cc5b0107fd50..5913c0443ca08b 100644 --- a/homeassistant/components/sensor/arlo.py +++ b/homeassistant/components/sensor/arlo.py @@ -118,6 +118,10 @@ def update(self): video = self._data.last_video self._state = video.created_at_pretty("%m-%d-%Y %H:%M:%S") except (AttributeError, IndexError): + error_msg = \ + 'Video not found for {}. Is it older than 180 days?'.format( + self.name) + _LOGGER.debug(error_msg) self._state = None elif self._sensor_type == 'battery_level': From 0cc50799b13885077c4630d10e34649f08c5ba49 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Thu, 31 May 2018 03:46:10 -0400 Subject: [PATCH 05/17] Transformed Arlo Control Panel to Sync --- homeassistant/components/alarm_control_panel/arlo.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/alarm_control_panel/arlo.py b/homeassistant/components/alarm_control_panel/arlo.py index 333bde9ee36a75..71fadd8668a18d 100644 --- a/homeassistant/components/alarm_control_panel/arlo.py +++ b/homeassistant/components/alarm_control_panel/arlo.py @@ -36,10 +36,9 @@ }) -@asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the Arlo Alarm Control Panels.""" - data = hass.data[DATA_ARLO] + arlo = hass.data[DATA_ARLO].hub if not data.base_stations: return @@ -47,10 +46,10 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): home_mode_name = config.get(CONF_HOME_MODE_NAME) away_mode_name = config.get(CONF_AWAY_MODE_NAME) base_stations = [] - for base_station in data.base_stations: + for base_station in arlo.base_stations: base_stations.append(ArloBaseStation(base_station, home_mode_name, away_mode_name)) - async_add_devices(base_stations, True) + add_devices(base_stations, True) class ArloBaseStation(AlarmControlPanel): From e437a0c853b78ef45c58d0c7c699bcb8cfefffb1 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Thu, 31 May 2018 03:56:57 -0400 Subject: [PATCH 06/17] Makes linter happy --- homeassistant/components/alarm_control_panel/arlo.py | 2 +- homeassistant/components/arlo.py | 7 ++----- homeassistant/components/camera/arlo.py | 4 +--- homeassistant/components/sensor/arlo.py | 6 ++---- 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/alarm_control_panel/arlo.py b/homeassistant/components/alarm_control_panel/arlo.py index 71fadd8668a18d..a0806eebbeaa99 100644 --- a/homeassistant/components/alarm_control_panel/arlo.py +++ b/homeassistant/components/alarm_control_panel/arlo.py @@ -40,7 +40,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the Arlo Alarm Control Panels.""" arlo = hass.data[DATA_ARLO].hub - if not data.base_stations: + if not arlo.base_stations: return home_mode_name = config.get(CONF_HOME_MODE_NAME) diff --git a/homeassistant/components/arlo.py b/homeassistant/components/arlo.py index 9886d8853ede20..984da28dd9c0e8 100644 --- a/homeassistant/components/arlo.py +++ b/homeassistant/components/arlo.py @@ -4,7 +4,6 @@ For more details about this component, please refer to the documentation at https://home-assistant.io/components/arlo/ """ -import asyncio import logging from datetime import timedelta @@ -15,9 +14,7 @@ from homeassistant.const import ( CONF_USERNAME, CONF_PASSWORD, CONF_SCAN_INTERVAL) from homeassistant.helpers.event import track_time_interval -from homeassistant.helpers.dispatcher import ( - async_dispatcher_connect, dispatcher_send) -from homeassistant.util.dt import now +from homeassistant.helpers.dispatcher import dispatcher_send REQUIREMENTS = ['pyarlo==0.1.3'] @@ -81,7 +78,7 @@ def setup(hass, config): def hub_refresh(event_time): """Call ArloHub to refresh information.""" - _LOGGER.debug("Updating Arlo Hub component") + _LOGGER.info("Updating Arlo Hub component") hass.data[DATA_ARLO].hub.update(update_cameras=True, update_base_station=True) dispatcher_send(hass, SIGNAL_UPDATE_ARLO) diff --git a/homeassistant/components/camera/arlo.py b/homeassistant/components/camera/arlo.py index c40799881cf0d3..8422bba76b4e62 100644 --- a/homeassistant/components/camera/arlo.py +++ b/homeassistant/components/camera/arlo.py @@ -6,7 +6,6 @@ """ import asyncio import logging -from datetime import timedelta import voluptuous as vol @@ -17,8 +16,7 @@ from homeassistant.components.ffmpeg import DATA_FFMPEG from homeassistant.const import ATTR_BATTERY_LEVEL from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream -from homeassistant.helpers.dispatcher import ( - async_dispatcher_connect, dispatcher_send) +from homeassistant.helpers.dispatcher import async_dispatcher_connect _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/sensor/arlo.py b/homeassistant/components/sensor/arlo.py index 5913c0443ca08b..b77bbe29595ad9 100644 --- a/homeassistant/components/sensor/arlo.py +++ b/homeassistant/components/sensor/arlo.py @@ -6,7 +6,6 @@ """ import asyncio import logging -from datetime import timedelta import voluptuous as vol @@ -15,8 +14,7 @@ CONF_ATTRIBUTION, DEFAULT_BRAND, DATA_ARLO, SIGNAL_UPDATE_ARLO) from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import (ATTR_ATTRIBUTION, CONF_MONITORED_CONDITIONS) -from homeassistant.helpers.dispatcher import ( - async_dispatcher_connect, dispatcher_send) +from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity from homeassistant.helpers.icon import icon_for_battery_level @@ -119,7 +117,7 @@ def update(self): self._state = video.created_at_pretty("%m-%d-%Y %H:%M:%S") except (AttributeError, IndexError): error_msg = \ - 'Video not found for {}. Is it older than 180 days?'.format( + 'Video not found for {}. Older than 180 days?'.format( self.name) _LOGGER.debug(error_msg) self._state = None From 1cbd2f832261699e41e19d5c09a56201d6b9c430 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Sat, 2 Jun 2018 02:56:29 -0400 Subject: [PATCH 07/17] Uses total_seconds() for scan_interval --- homeassistant/components/arlo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/arlo.py b/homeassistant/components/arlo.py index 984da28dd9c0e8..133effa6aafa5c 100644 --- a/homeassistant/components/arlo.py +++ b/homeassistant/components/arlo.py @@ -48,7 +48,7 @@ def setup(hass, config): conf = config[DOMAIN] username = conf.get(CONF_USERNAME) password = conf.get(CONF_PASSWORD) - scan_interval = conf.get(CONF_SCAN_INTERVAL) + scan_interval = conf.get(CONF_SCAN_INTERVAL).total_seconds() try: from pyarlo import PyArlo From 63ae2015dc2d6f783e48a39be853aab8c2281c21 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Sat, 2 Jun 2018 04:06:08 -0400 Subject: [PATCH 08/17] Added callback and fixed scan_interval issue --- .../components/alarm_control_panel/arlo.py | 15 ++++++++++++++- homeassistant/components/arlo.py | 4 ++-- homeassistant/components/camera/arlo.py | 4 ++-- homeassistant/components/sensor/arlo.py | 4 ++-- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/alarm_control_panel/arlo.py b/homeassistant/components/alarm_control_panel/arlo.py index a0806eebbeaa99..dc5a2d18e83cd2 100644 --- a/homeassistant/components/alarm_control_panel/arlo.py +++ b/homeassistant/components/alarm_control_panel/arlo.py @@ -10,9 +10,11 @@ import voluptuous as vol import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.components.alarm_control_panel import ( AlarmControlPanel, PLATFORM_SCHEMA) -from homeassistant.components.arlo import (DATA_ARLO, CONF_ATTRIBUTION) +from homeassistant.components.arlo import ( + DATA_ARLO, CONF_ATTRIBUTION, SIGNAL_UPDATE_ARLO) from homeassistant.const import ( ATTR_ATTRIBUTION, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED) @@ -67,6 +69,16 @@ def icon(self): """Return icon.""" return ICON + @asyncio.coroutine + def async_added_to_hass(self): + """Register callbacks.""" + async_dispatcher_connect( + self.hass, SIGNAL_UPDATE_ARLO, self._update_callback) + + def _update_callback(self): + """Call update method.""" + self.schedule_update_ha_state(True) + @property def state(self): """Return the state of the device.""" @@ -74,6 +86,7 @@ def state(self): def update(self): """Update the state of the device.""" + _LOGGER.info("Updating Arlo Alarm Control Panel %s" % self.name) # PyArlo sometimes returns None for mode. So retry 3 times before # returning None. num_retries = 3 diff --git a/homeassistant/components/arlo.py b/homeassistant/components/arlo.py index 133effa6aafa5c..aabbedfb52217c 100644 --- a/homeassistant/components/arlo.py +++ b/homeassistant/components/arlo.py @@ -48,7 +48,7 @@ def setup(hass, config): conf = config[DOMAIN] username = conf.get(CONF_USERNAME) password = conf.get(CONF_PASSWORD) - scan_interval = conf.get(CONF_SCAN_INTERVAL).total_seconds() + scan_interval = conf.get(CONF_SCAN_INTERVAL) try: from pyarlo import PyArlo @@ -61,7 +61,7 @@ def setup(hass, config): try: if arlo.base_stations: arlo_base_station = arlo.base_stations[0] - arlo_base_station.refresh_rate = scan_interval + arlo_base_station.refresh_rate = scan_interval.total_seconds() except (AttributeError, IndexError): return False diff --git a/homeassistant/components/camera/arlo.py b/homeassistant/components/camera/arlo.py index 8422bba76b4e62..824163fe43e68f 100644 --- a/homeassistant/components/camera/arlo.py +++ b/homeassistant/components/camera/arlo.py @@ -95,8 +95,8 @@ def handle_async_mjpeg_stream(self, request): video = self._camera.last_video if not video: error_msg = \ - 'Video not found for {}. Is it older than 180 days?'.format( - self.name) + 'Video not found for {0}. Is it older than {1} days?'.format( + self.name, self._camera.min_days_vdo_cache) _LOGGER.error(error_msg) return diff --git a/homeassistant/components/sensor/arlo.py b/homeassistant/components/sensor/arlo.py index b77bbe29595ad9..6544b25122c2e1 100644 --- a/homeassistant/components/sensor/arlo.py +++ b/homeassistant/components/sensor/arlo.py @@ -117,8 +117,8 @@ def update(self): self._state = video.created_at_pretty("%m-%d-%Y %H:%M:%S") except (AttributeError, IndexError): error_msg = \ - 'Video not found for {}. Older than 180 days?'.format( - self.name) + 'Video not found for {0}. Older than {1} days?'.format( + self.name, self._data.min_days_vdo_cache) _LOGGER.debug(error_msg) self._state = None From 66a791d3cb650ca857fb4ed12b2f373b63728bd1 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Mon, 4 Jun 2018 23:43:51 -0400 Subject: [PATCH 09/17] Disable multiple tries and supported custom modes set in Arlo --- .../components/alarm_control_panel/arlo.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/alarm_control_panel/arlo.py b/homeassistant/components/alarm_control_panel/arlo.py index dc5a2d18e83cd2..143e6fddc2d3ce 100644 --- a/homeassistant/components/alarm_control_panel/arlo.py +++ b/homeassistant/components/alarm_control_panel/arlo.py @@ -87,17 +87,11 @@ def state(self): def update(self): """Update the state of the device.""" _LOGGER.info("Updating Arlo Alarm Control Panel %s" % self.name) - # PyArlo sometimes returns None for mode. So retry 3 times before - # returning None. - num_retries = 3 - i = 0 - while i < num_retries: - mode = self._base_station.mode - if mode: - self._state = self._get_state_from_mode(mode) - return - i += 1 - self._state = None + mode = self._base_station.mode + if mode: + self._state = self._get_state_from_mode(mode) + else: + self._state = None @asyncio.coroutine def async_alarm_disarm(self, code=None): @@ -137,4 +131,4 @@ def _get_state_from_mode(self, mode): return STATE_ALARM_ARMED_HOME elif mode == self._away_mode_name: return STATE_ALARM_ARMED_AWAY - return None + return mode From cae961694d043d4762d8ea51cf8109872364fe30 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Tue, 5 Jun 2018 00:34:02 -0400 Subject: [PATCH 10/17] Bump PyArlo version to 0.1.4 --- homeassistant/components/arlo.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/arlo.py b/homeassistant/components/arlo.py index aabbedfb52217c..51af1215f7f66f 100644 --- a/homeassistant/components/arlo.py +++ b/homeassistant/components/arlo.py @@ -16,7 +16,7 @@ from homeassistant.helpers.event import track_time_interval from homeassistant.helpers.dispatcher import dispatcher_send -REQUIREMENTS = ['pyarlo==0.1.3'] +REQUIREMENTS = ['pyarlo==0.1.4'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index d8c2dbbbb21eec..5e181215dd40c2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -725,7 +725,7 @@ pyairvisual==1.0.0 pyalarmdotcom==0.3.2 # homeassistant.components.arlo -pyarlo==0.1.3 +pyarlo==0.1.4 # homeassistant.components.notify.xmpp pyasn1-modules==0.1.5 From da74c936aab3cc4e4b9407fb38cb37cb12828875 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Tue, 5 Jun 2018 01:28:47 -0400 Subject: [PATCH 11/17] Makes lint happy --- homeassistant/components/alarm_control_panel/arlo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/alarm_control_panel/arlo.py b/homeassistant/components/alarm_control_panel/arlo.py index 143e6fddc2d3ce..c8bdd99c5550c3 100644 --- a/homeassistant/components/alarm_control_panel/arlo.py +++ b/homeassistant/components/alarm_control_panel/arlo.py @@ -86,7 +86,7 @@ def state(self): def update(self): """Update the state of the device.""" - _LOGGER.info("Updating Arlo Alarm Control Panel %s" % self.name) + _LOGGER.info("Updating Arlo Alarm Control Panel %s", self.name) mode = self._base_station.mode if mode: self._state = self._get_state_from_mode(mode) From 377a9b7782652a882dd41655e0ece8d1e7f3eacf Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Wed, 6 Jun 2018 10:17:39 -0400 Subject: [PATCH 12/17] Removed ArloHub object and added some tweaks --- .../components/alarm_control_panel/arlo.py | 11 +++++----- homeassistant/components/arlo.py | 21 +++++++------------ homeassistant/components/camera/arlo.py | 9 ++------ homeassistant/components/sensor/arlo.py | 2 +- 4 files changed, 16 insertions(+), 27 deletions(-) diff --git a/homeassistant/components/alarm_control_panel/arlo.py b/homeassistant/components/alarm_control_panel/arlo.py index c8bdd99c5550c3..303a8614179021 100644 --- a/homeassistant/components/alarm_control_panel/arlo.py +++ b/homeassistant/components/alarm_control_panel/arlo.py @@ -10,6 +10,7 @@ import voluptuous as vol import homeassistant.helpers.config_validation as cv +from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.components.alarm_control_panel import ( AlarmControlPanel, PLATFORM_SCHEMA) @@ -40,7 +41,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the Arlo Alarm Control Panels.""" - arlo = hass.data[DATA_ARLO].hub + arlo = hass.data[DATA_ARLO] if not arlo.base_stations: return @@ -69,15 +70,15 @@ def icon(self): """Return icon.""" return ICON - @asyncio.coroutine - def async_added_to_hass(self): + async def async_added_to_hass(self): """Register callbacks.""" async_dispatcher_connect( self.hass, SIGNAL_UPDATE_ARLO, self._update_callback) + @callback def _update_callback(self): """Call update method.""" - self.schedule_update_ha_state(True) + self.async_schedule_update_ha_state() @property def state(self): @@ -86,7 +87,7 @@ def state(self): def update(self): """Update the state of the device.""" - _LOGGER.info("Updating Arlo Alarm Control Panel %s", self.name) + _LOGGER.debug("Updating Arlo Alarm Control Panel %s", self.name) mode = self._base_station.mode if mode: self._state = self._get_state_from_mode(mode) diff --git a/homeassistant/components/arlo.py b/homeassistant/components/arlo.py index 51af1215f7f66f..06416a02645a19 100644 --- a/homeassistant/components/arlo.py +++ b/homeassistant/components/arlo.py @@ -58,14 +58,15 @@ def setup(hass, config): return False # assign refresh period to base station thread - try: - if arlo.base_stations: - arlo_base_station = arlo.base_stations[0] - arlo_base_station.refresh_rate = scan_interval.total_seconds() - except (AttributeError, IndexError): + arlo_base_station = next(( + station for station in arlo.base_stations), None) + + if arlo_base_station is None: return False - hass.data[DATA_ARLO] = ArloHub(arlo) + arlo_base_station.refresh_rate = scan_interval.total_seconds() + hass.data[DATA_ARLO] = arlo + except (ConnectTimeout, HTTPError) as ex: _LOGGER.error("Unable to connect to Netgear Arlo: %s", str(ex)) hass.components.persistent_notification.create( @@ -89,11 +90,3 @@ def hub_refresh(event_time): # register scan interval for ArloHub track_time_interval(hass, hub_refresh, scan_interval) return True - - -class ArloHub(object): - """Representation of the base Arlo hub component.""" - - def __init__(self, hub): - """Initialize the entity.""" - self.hub = hub diff --git a/homeassistant/components/camera/arlo.py b/homeassistant/components/camera/arlo.py index 824163fe43e68f..afa8b1938a6c3a 100644 --- a/homeassistant/components/camera/arlo.py +++ b/homeassistant/components/camera/arlo.py @@ -49,7 +49,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): """Set up an Arlo IP Camera.""" - arlo = hass.data.get(DATA_ARLO).hub + arlo = hass.data[DATA_ARLO] if not arlo: return False @@ -141,11 +141,6 @@ def brand(self): """Return the camera brand.""" return DEFAULT_BRAND - @property - def should_poll(self): - """Camera should poll periodically.""" - return False - @property def motion_detection_enabled(self): """Return the camera motion detection status.""" @@ -154,7 +149,7 @@ def motion_detection_enabled(self): def set_base_station_mode(self, mode): """Set the mode in the base station.""" # Get the list of base stations identified by library - base_stations = self.hass.data.get(DATA_ARLO).hub.base_stations + base_stations = self.hass.data[DATA_ARLO].base_stations # Some Arlo cameras does not have base station # So check if there is base station detected first diff --git a/homeassistant/components/sensor/arlo.py b/homeassistant/components/sensor/arlo.py index 6544b25122c2e1..ecf41b8624fa36 100644 --- a/homeassistant/components/sensor/arlo.py +++ b/homeassistant/components/sensor/arlo.py @@ -39,7 +39,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): """Set up an Arlo IP sensor.""" - arlo = hass.data.get(DATA_ARLO).hub + arlo = hass.data.get(DATA_ARLO) if not arlo: return False From b0c3d03ca8d5bfe3abe8804a5af150aabfc48720 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Wed, 6 Jun 2018 10:50:59 -0400 Subject: [PATCH 13/17] Fixed hub_refresh method --- homeassistant/components/arlo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/arlo.py b/homeassistant/components/arlo.py index 06416a02645a19..1b97a46548ee0b 100644 --- a/homeassistant/components/arlo.py +++ b/homeassistant/components/arlo.py @@ -80,7 +80,7 @@ def setup(hass, config): def hub_refresh(event_time): """Call ArloHub to refresh information.""" _LOGGER.info("Updating Arlo Hub component") - hass.data[DATA_ARLO].hub.update(update_cameras=True, + hass.data[DATA_ARLO].update(update_cameras=True, update_base_station=True) dispatcher_send(hass, SIGNAL_UPDATE_ARLO) From a83658c46f9bee105235ecc856be99a890e22884 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Wed, 6 Jun 2018 11:26:08 -0400 Subject: [PATCH 14/17] Makes lint happy --- homeassistant/components/arlo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/arlo.py b/homeassistant/components/arlo.py index 1b97a46548ee0b..92d5240cf99971 100644 --- a/homeassistant/components/arlo.py +++ b/homeassistant/components/arlo.py @@ -81,7 +81,7 @@ def hub_refresh(event_time): """Call ArloHub to refresh information.""" _LOGGER.info("Updating Arlo Hub component") hass.data[DATA_ARLO].update(update_cameras=True, - update_base_station=True) + update_base_station=True) dispatcher_send(hass, SIGNAL_UPDATE_ARLO) # register service From 6a0a9c8c3444570a11fa9463b0abac3f5802fd40 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Fri, 8 Jun 2018 10:35:10 -0400 Subject: [PATCH 15/17] Ajusted async syntax and added callbacks decorators --- .../components/alarm_control_panel/arlo.py | 12 ++++-------- homeassistant/components/camera/arlo.py | 17 ++++++++--------- homeassistant/components/sensor/arlo.py | 8 ++++---- 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/homeassistant/components/alarm_control_panel/arlo.py b/homeassistant/components/alarm_control_panel/arlo.py index 303a8614179021..20887157cb4dff 100644 --- a/homeassistant/components/alarm_control_panel/arlo.py +++ b/homeassistant/components/alarm_control_panel/arlo.py @@ -4,7 +4,6 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/alarm_control_panel.arlo/ """ -import asyncio import logging import voluptuous as vol @@ -78,7 +77,7 @@ async def async_added_to_hass(self): @callback def _update_callback(self): """Call update method.""" - self.async_schedule_update_ha_state() + self.async_schedule_update_ha_state(True) @property def state(self): @@ -94,18 +93,15 @@ def update(self): else: self._state = None - @asyncio.coroutine - def async_alarm_disarm(self, code=None): + async def async_alarm_disarm(self, code=None): """Send disarm command.""" self._base_station.mode = DISARMED - @asyncio.coroutine - def async_alarm_arm_away(self, code=None): + async def async_alarm_arm_away(self, code=None): """Send arm away command. Uses custom mode.""" self._base_station.mode = self._away_mode_name - @asyncio.coroutine - def async_alarm_arm_home(self, code=None): + async def async_alarm_arm_home(self, code=None): """Send arm home command. Uses custom mode.""" self._base_station.mode = self._home_mode_name diff --git a/homeassistant/components/camera/arlo.py b/homeassistant/components/camera/arlo.py index afa8b1938a6c3a..29b456da1a8392 100644 --- a/homeassistant/components/camera/arlo.py +++ b/homeassistant/components/camera/arlo.py @@ -4,11 +4,11 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/camera.arlo/ """ -import asyncio import logging import voluptuous as vol +from homeassistant.core import callback import homeassistant.helpers.config_validation as cv from homeassistant.components.arlo import ( DEFAULT_BRAND, DATA_ARLO, SIGNAL_UPDATE_ARLO) @@ -78,18 +78,17 @@ def camera_image(self): """Return a still image response from the camera.""" return self._camera.last_image_from_cache - @asyncio.coroutine - def async_added_to_hass(self): + async def async_added_to_hass(self): """Register callbacks.""" async_dispatcher_connect( self.hass, SIGNAL_UPDATE_ARLO, self._update_callback) + @callback def _update_callback(self): """Call update method.""" - self.schedule_update_ha_state(True) + self.async_schedule_update_ha_state(True) - @asyncio.coroutine - def handle_async_mjpeg_stream(self, request): + async def handle_async_mjpeg_stream(self, request): """Generate an HTTP MJPEG stream from the camera.""" from haffmpeg import CameraMjpeg video = self._camera.last_video @@ -101,13 +100,13 @@ def handle_async_mjpeg_stream(self, request): return stream = CameraMjpeg(self._ffmpeg.binary, loop=self.hass.loop) - yield from stream.open_camera( + await stream.open_camera( video.video_url, extra_cmd=self._ffmpeg_arguments) - yield from async_aiohttp_proxy_stream( + await async_aiohttp_proxy_stream( self.hass, request, stream, 'multipart/x-mixed-replace;boundary=ffserver') - yield from stream.close() + await stream.close() @property def name(self): diff --git a/homeassistant/components/sensor/arlo.py b/homeassistant/components/sensor/arlo.py index ecf41b8624fa36..a0b3c605eeed88 100644 --- a/homeassistant/components/sensor/arlo.py +++ b/homeassistant/components/sensor/arlo.py @@ -4,11 +4,11 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.arlo/ """ -import asyncio import logging import voluptuous as vol +from homeassistant.core import callback import homeassistant.helpers.config_validation as cv from homeassistant.components.arlo import ( CONF_ATTRIBUTION, DEFAULT_BRAND, DATA_ARLO, SIGNAL_UPDATE_ARLO) @@ -74,15 +74,15 @@ def name(self): """Return the name of this camera.""" return self._name - @asyncio.coroutine - def async_added_to_hass(self): + async def async_added_to_hass(self): """Register callbacks.""" async_dispatcher_connect( self.hass, SIGNAL_UPDATE_ARLO, self._update_callback) + @callback def _update_callback(self): """Call update method.""" - self.schedule_update_ha_state(True) + self.async_schedule_update_ha_state(True) @property def state(self): From db2d2eecf2b62477d47ff3a4e7b920c01c7aee95 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Mon, 11 Jun 2018 00:02:49 -0400 Subject: [PATCH 16/17] Bump PyArlo version to 0.1.6 to include some enhacements --- homeassistant/components/arlo.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/arlo.py b/homeassistant/components/arlo.py index 92d5240cf99971..206ea4005e6bae 100644 --- a/homeassistant/components/arlo.py +++ b/homeassistant/components/arlo.py @@ -16,7 +16,7 @@ from homeassistant.helpers.event import track_time_interval from homeassistant.helpers.dispatcher import dispatcher_send -REQUIREMENTS = ['pyarlo==0.1.4'] +REQUIREMENTS = ['pyarlo==0.1.6'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 5e181215dd40c2..e398eb169f8f95 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -725,7 +725,7 @@ pyairvisual==1.0.0 pyalarmdotcom==0.3.2 # homeassistant.components.arlo -pyarlo==0.1.4 +pyarlo==0.1.6 # homeassistant.components.notify.xmpp pyasn1-modules==0.1.5 From 1f2f03b0122b9ffbf16729745bd399a7f9a1f181 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Mon, 11 Jun 2018 22:34:27 -0400 Subject: [PATCH 17/17] Refined code --- homeassistant/components/camera/arlo.py | 6 ++---- homeassistant/components/sensor/arlo.py | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/camera/arlo.py b/homeassistant/components/camera/arlo.py index 29b456da1a8392..1a98ade55183ea 100644 --- a/homeassistant/components/camera/arlo.py +++ b/homeassistant/components/camera/arlo.py @@ -50,14 +50,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None): """Set up an Arlo IP Camera.""" arlo = hass.data[DATA_ARLO] - if not arlo: - return False cameras = [] for camera in arlo.cameras: cameras.append(ArloCam(hass, camera, config)) - add_devices(cameras, True) + add_devices(cameras) class ArloCam(Camera): @@ -86,7 +84,7 @@ async def async_added_to_hass(self): @callback def _update_callback(self): """Call update method.""" - self.async_schedule_update_ha_state(True) + self.async_schedule_update_ha_state() async def handle_async_mjpeg_stream(self, request): """Generate an HTTP MJPEG stream from the camera.""" diff --git a/homeassistant/components/sensor/arlo.py b/homeassistant/components/sensor/arlo.py index a0b3c605eeed88..18029691dc7bae 100644 --- a/homeassistant/components/sensor/arlo.py +++ b/homeassistant/components/sensor/arlo.py @@ -55,7 +55,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sensors.append(ArloSensor(name, camera, sensor_type)) add_devices(sensors, True) - return True class ArloSensor(Entity):