Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
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
44 changes: 25 additions & 19 deletions homeassistant/components/alarm_control_panel/arlo.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -36,21 +38,20 @@
})


@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:
if not arlo.base_stations:
return

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):
Expand All @@ -68,24 +69,29 @@ def icon(self):
"""Return icon."""
return ICON

@asyncio.coroutine

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.

We're now using Python 3.5 async syntax, ie async def.

def async_added_to_hass(self):
"""Register callbacks."""
async_dispatcher_connect(
self.hass, SIGNAL_UPDATE_ARLO, self._update_callback)

def _update_callback(self):

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.

Decorate this with @callback imported from core.py.

"""Call update method."""
self.schedule_update_ha_state(True)

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.

Use self.async_schedule_update_ha_state.


@property
def state(self):
"""Return the state of the device."""
return self._state

def update(self):
"""Update the state of the device."""
# 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
_LOGGER.info("Updating Arlo Alarm Control Panel %s", self.name)

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.

Max debug.

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):
Expand Down Expand Up @@ -125,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
47 changes: 44 additions & 3 deletions homeassistant/components/arlo.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@
https://home-assistant.io/components/arlo/
"""
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 dispatcher_send

REQUIREMENTS = ['pyarlo==0.1.2']
REQUIREMENTS = ['pyarlo==0.1.4']

_LOGGER = logging.getLogger(__name__)

Expand All @@ -25,10 +29,16 @@
NOTIFICATION_ID = 'arlo_notification'
NOTIFICATION_TITLE = 'Arlo Component Setup'

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)

Expand All @@ -38,14 +48,24 @@ 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

arlo = PyArlo(username, password, preload=False)
if not arlo.is_connected:
return False
hass.data[DATA_ARLO] = arlo

# assign refresh period to base station thread
try:
if arlo.base_stations:
arlo_base_station = arlo.base_stations[0]

@MartinHjelmare MartinHjelmare Jun 5, 2018

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.

This is better:

arlo_base_station = next((
    station for station in arlo.base_stations), None)
if arlo_base_station is None:
    return False

arlo_base_station.refresh_rate = scan_interval.total_seconds()
except (AttributeError, IndexError):

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 would there be an attribute error?

The index error is solved by using my suggestion above.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, that is not needed anymore. Good refactoring!! Thanks

return False

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(
Expand All @@ -55,4 +75,25 @@ def setup(hass, config):
title=NOTIFICATION_TITLE,
notification_id=NOTIFICATION_ID)
return False

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,
update_base_station=True)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

continuation line over-indented for visual indent

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


class ArloHub(object):
"""Representation of the base Arlo hub component."""

def __init__(self, hub):
"""Initialize the entity."""
self.hub = hub

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 store the hub on another instance if it's just that attribute and no methods needed?

39 changes: 22 additions & 17 deletions homeassistant/components/camera/arlo.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,20 @@
"""
import asyncio
import logging
from datetime import timedelta

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

_LOGGER = logging.getLogger(__name__)

SCAN_INTERVAL = timedelta(seconds=90)

ARLO_MODE_ARMED = 'armed'
ARLO_MODE_DISARMED = 'disarmed'

Expand All @@ -44,14 +43,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:

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.

This can't happen.

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.

I don't think this can happen?

return False

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.

Only return.


Expand All @@ -74,21 +72,32 @@ 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

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.

Update all coroutines to use new syntax.

def async_added_to_hass(self):
"""Register callbacks."""
async_dispatcher_connect(
self.hass, SIGNAL_UPDATE_ARLO, self._update_callback)

def _update_callback(self):

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.

Update this like my previous comment. I haven't commented on all the places that needs the same changes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, my bad.

"""Call update method."""
self.schedule_update_ha_state(True)

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.


@asyncio.coroutine
def handle_async_mjpeg_stream(self, request):
"""Generate an HTTP MJPEG stream from the camera."""
from haffmpeg import CameraMjpeg
video = self._camera.last_video
if not video:
error_msg = \
'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

stream = CameraMjpeg(self._ffmpeg.binary, loop=self.hass.loop)
Expand Down Expand Up @@ -135,7 +144,7 @@ def brand(self):
@property
def should_poll(self):
"""Camera should poll periodically."""
return True
return False

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.

This is the default for cameras.


@property
def motion_detection_enabled(self):
Expand All @@ -145,7 +154,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

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 change to dict.get. We know that the key will be in the dict.


# Some Arlo cameras does not have base station
# So check if there is base station detected first
Expand All @@ -164,7 +173,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()
Loading