-
-
Notifications
You must be signed in to change notification settings - Fork 37.8k
Camera services arm disarm including Netgear Arlo #7961
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
705205b
1ef7055
8669c7f
e6deae2
eb11fd8
70808e5
c4a91c9
e2c0113
c1a8382
2775c0e
4b1f36f
49438bb
7f8e914
53dc772
890d7f1
7e477a2
285d9b4
2e1208b
fe2d68e
282487d
a4e5cc1
60aaa52
07b941c
96f0ee7
d304f2b
baab606
27d9c28
5c95020
33362c9
6c97791
488a71e
19c3d62
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,20 +12,25 @@ | |
| import logging | ||
| import hashlib | ||
| from random import SystemRandom | ||
| import os | ||
|
|
||
| import aiohttp | ||
| from aiohttp import web | ||
| import async_timeout | ||
| import voluptuous as vol | ||
|
|
||
| from homeassistant.core import callback | ||
| from homeassistant.const import ATTR_ENTITY_PICTURE | ||
| from homeassistant.config import load_yaml_config_file | ||
| from homeassistant.exceptions import HomeAssistantError | ||
| from homeassistant.helpers.aiohttp_client import async_get_clientsession | ||
| from homeassistant.helpers.entity import Entity | ||
| from homeassistant.helpers.entity_component import EntityComponent | ||
| from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa | ||
| from homeassistant.components.http import HomeAssistantView, KEY_AUTHENTICATED | ||
| from homeassistant.helpers.event import async_track_time_interval | ||
| import homeassistant.helpers.config_validation as cv | ||
| from homeassistant.const import (SERVICE_ARM, SERVICE_DISARM, ATTR_ENTITY_ID) | ||
|
|
||
| _LOGGER = logging.getLogger(__name__) | ||
|
|
||
|
|
@@ -43,6 +48,33 @@ | |
| TOKEN_CHANGE_INTERVAL = timedelta(minutes=5) | ||
| _RND = SystemRandom() | ||
|
|
||
| CAMERA_SERVICE_SCHEMA = vol.Schema({ | ||
| vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, | ||
| }) | ||
|
|
||
|
|
||
| def arm(hass, entity_id=None): | ||
| """Arm all""" | ||
| hass.add_job(async_arm, hass, entity_id) | ||
|
|
||
|
|
||
| @callback | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. expected 2 blank lines, found 1 |
||
| def async_arm(hass, entity_id=None): | ||
| """Arm all the cameras""" | ||
| data = {ATTR_ENTITY_ID: entity_id} if entity_id else None | ||
| hass.async_add_job(hass.services.async_call(DOMAIN, SERVICE_ARM, data)) | ||
|
|
||
| def disarm(hass, entity_id=None): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. expected 2 blank lines, found 1 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. expected 2 blank lines, found 1 |
||
| """Disarm all""" | ||
| hass.add_job(async_disarm, hass, entity_id) | ||
|
|
||
|
|
||
| @callback | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. expected 2 blank lines, found 1 |
||
| def async_disarm(hass, entity_id=None): | ||
| """Disarm all the cameras""" | ||
| data = {ATTR_ENTITY_ID: entity_id} if entity_id else None | ||
| hass.async_add_job(hass.services.async_call(DOMAIN, SERVICE_DISARM, data)) | ||
|
|
||
|
|
||
| @asyncio.coroutine | ||
| def async_get_image(hass, entity_id, timeout=10): | ||
|
|
@@ -92,6 +124,47 @@ def update_tokens(time): | |
| hass.async_add_job(entity.async_update_ha_state()) | ||
|
|
||
| async_track_time_interval(hass, update_tokens, TOKEN_CHANGE_INTERVAL) | ||
|
|
||
| @asyncio.coroutine | ||
| def async_handle_camera_service(service): | ||
| """Handle calls to the camera services.""" | ||
| target_cameras = component.async_extract_from_service(service) | ||
|
|
||
| for camera in target_cameras: | ||
| try: | ||
| if service.service == SERVICE_ARM: | ||
| yield from camera.async_arm() | ||
| elif service.service == SERVICE_DISARM: | ||
| yield from camera.async_disarm() | ||
| except AttributeError as e: | ||
| pass | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should not just let this pass. We should also not try to update a camera that didn't had an attribute.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can check if the camera has that attribute using |
||
|
|
||
| update_tasks = [] | ||
| for camera in target_cameras: | ||
| if not camera.should_poll: | ||
| continue | ||
|
|
||
| update_coro = hass.async_add_job( | ||
| camera.async_update_ha_state(True)) | ||
| if hasattr(camera, 'async_update'): | ||
| update_tasks.append(update_coro) | ||
| else: | ||
| yield from update_coro | ||
|
|
||
| if update_tasks: | ||
| yield from asyncio.wait(update_tasks, loop=hass.loop) | ||
|
|
||
| descriptions = yield from hass.async_add_job( | ||
| load_yaml_config_file, os.path.join( | ||
| os.path.dirname(__file__), 'services.yaml')) | ||
|
|
||
| hass.services.async_register( | ||
| DOMAIN, SERVICE_ARM, async_handle_camera_service, | ||
| descriptions.get(SERVICE_ARM), schema=CAMERA_SERVICE_SCHEMA) | ||
| hass.services.async_register( | ||
| DOMAIN, SERVICE_DISARM, async_handle_camera_service, | ||
| descriptions.get(SERVICE_DISARM), schema=CAMERA_SERVICE_SCHEMA) | ||
|
|
||
| return True | ||
|
|
||
|
|
||
|
|
@@ -124,6 +197,16 @@ def brand(self): | |
| """Return the camera brand.""" | ||
| return None | ||
|
|
||
| @property | ||
| def status(self): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why add something new if we already have |
||
| """Return the camera status.""" | ||
| try: | ||
| status = self._status | ||
| except AttributeError as e: | ||
| status = None | ||
|
|
||
| return status | ||
|
|
||
| @property | ||
| def model(self): | ||
| """Return the camera model.""" | ||
|
|
@@ -212,6 +295,9 @@ def state_attributes(self): | |
| if self.brand: | ||
| attr['brand'] = self.brand | ||
|
|
||
| if self.status: | ||
| attr['status'] = self.status | ||
|
|
||
| return attr | ||
|
|
||
| @callback | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,7 +27,6 @@ | |
| cv.string, | ||
| }) | ||
|
|
||
|
|
||
| @asyncio.coroutine | ||
| def async_setup_platform(hass, config, async_add_devices, discovery_info=None): | ||
| """Set up an Arlo IP Camera.""" | ||
|
|
@@ -40,6 +39,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): | |
| cameras.append(ArloCam(hass, camera, config)) | ||
|
|
||
| async_add_devices(cameras, True) | ||
|
|
||
| return True | ||
|
|
||
|
|
||
|
|
@@ -49,9 +49,11 @@ class ArloCam(Camera): | |
| def __init__(self, hass, camera, device_info): | ||
| """Initialize an Arlo camera.""" | ||
| super().__init__() | ||
|
|
||
| self._parent = hass | ||
| self._camera = camera | ||
| self._base_stn = hass.data['arlo'].base_stations[0] | ||
| self._name = self._camera.name | ||
| self._status = "Disarmed" | ||
| self._ffmpeg = hass.data[DATA_FFMPEG] | ||
| self._ffmpeg_arguments = device_info.get(CONF_FFMPEG_ARGUMENTS) | ||
|
|
||
|
|
@@ -90,3 +92,22 @@ def model(self): | |
| def brand(self): | ||
| """Camera brand.""" | ||
| return DEFAULT_BRAND | ||
|
|
||
| @property | ||
| def status(self): | ||
| """Camera Status.""" | ||
| return self._status | ||
|
|
||
| @asyncio.coroutine | ||
| def async_arm(self): | ||
| """Camera arm.""" | ||
| self._base_stn.mode = "armed" | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are not allowed to do I/O inside async methods.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there any example as to how to handle I/O, if we cannot do it inside the async methods ? |
||
| self._status = "Armed" | ||
| self.hass.async_add_job(self.async_update_ha_state()) | ||
|
|
||
| @asyncio.coroutine | ||
| def async_disarm(self): | ||
| """Camera disarm.""" | ||
| self._base_stn.mode = "disarmed" | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there any example as to how to handle I/O, if we cannot do it inside the async methods ?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. def set_mode(mode):
self._base_stn.mode = mode
yield from hass.async_add_job(set_mode, "disarmed") |
||
| self._status = "Disarmed" | ||
| self.hass.async_add_job(self.async_update_ha_state()) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same, remove this. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| # Describes the format for available camera services | ||
|
|
||
| arm: | ||
| description: Arm a camera with motion detection | ||
|
|
||
| fields: | ||
| entity_id: | ||
| description: Name(s) of entities to arm. | ||
| example: 'camera.living_room_camera' | ||
|
|
||
| disarm: | ||
| description: Disarm a camera disabling motion detection | ||
|
|
||
| fields: | ||
| entity_id: | ||
| description: Name(s) of entities to turn off | ||
| example: 'camera.living_room_camera' |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -402,3 +402,18 @@ eight_sleep: | |
| duration: | ||
| description: Duration to heat at the target level in seconds. | ||
| example: 3600 | ||
|
|
||
| camera: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please remove these, as they are already present in
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
| arm: | ||
| description: Arm a camera with motion detection | ||
| fields: | ||
| entity_id: | ||
| description: Name(s) of entities to arm. | ||
| example: 'camera.living_room_camera' | ||
|
|
||
| disarm: | ||
| description: Disarm a camera disabling motion detection | ||
| fields: | ||
| entity_id: | ||
| description: Name(s) of entities to turn off | ||
| example: 'camera.living_room_camera' | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expected 2 blank lines, found 1