Introducing Ring Door Bell Camera (including StickUp cameras) and WiFi sensors (#9962)
* Extended Ring DoorBell to support camera playback and wifi sensors * Bump python-ringdoorbell to version 0.1.6 * Support to camera playback via ffmpeg * Extended ringdoorbell sensors to report WiFi attributes * Extended unittests * Makes lint happy * Added support to stickup cameras and fixed logic * Fixed unittests for stickup cameras * Makes lint happy * Refactored attributions and removed extra refresh method.
This commit is contained in:
parent
222cc4c393
commit
51a65ee8e9
12 changed files with 399 additions and 19 deletions
|
@ -272,6 +272,7 @@ omit =
|
|||
homeassistant/components/camera/mjpeg.py
|
||||
homeassistant/components/camera/rpi_camera.py
|
||||
homeassistant/components/camera/onvif.py
|
||||
homeassistant/components/camera/ring.py
|
||||
homeassistant/components/camera/synology.py
|
||||
homeassistant/components/camera/yi.py
|
||||
homeassistant/components/climate/eq3btsmart.py
|
||||
|
|
|
@ -11,7 +11,7 @@ import voluptuous as vol
|
|||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from homeassistant.components.ring import (
|
||||
CONF_ATTRIBUTION, DEFAULT_ENTITY_NAMESPACE)
|
||||
CONF_ATTRIBUTION, DEFAULT_ENTITY_NAMESPACE, DATA_RING)
|
||||
|
||||
from homeassistant.const import (
|
||||
ATTR_ATTRIBUTION, CONF_ENTITY_NAMESPACE, CONF_MONITORED_CONDITIONS)
|
||||
|
@ -27,21 +27,21 @@ SCAN_INTERVAL = timedelta(seconds=5)
|
|||
|
||||
# Sensor types: Name, category, device_class
|
||||
SENSOR_TYPES = {
|
||||
'ding': ['Ding', ['doorbell'], 'occupancy'],
|
||||
'motion': ['Motion', ['doorbell'], 'motion'],
|
||||
'ding': ['Ding', ['doorbell', 'stickup_cams'], 'occupancy'],
|
||||
'motion': ['Motion', ['doorbell', 'stickup_cams'], 'motion'],
|
||||
}
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_ENTITY_NAMESPACE, default=DEFAULT_ENTITY_NAMESPACE):
|
||||
cv.string,
|
||||
vol.Required(CONF_MONITORED_CONDITIONS, default=[]):
|
||||
vol.Required(CONF_MONITORED_CONDITIONS, default=list(SENSOR_TYPES)):
|
||||
vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]),
|
||||
})
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Set up a sensor for a Ring device."""
|
||||
ring = hass.data.get('ring')
|
||||
ring = hass.data[DATA_RING]
|
||||
|
||||
sensors = []
|
||||
for sensor_type in config.get(CONF_MONITORED_CONDITIONS):
|
||||
|
@ -50,6 +50,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||
sensors.append(RingBinarySensor(hass,
|
||||
device,
|
||||
sensor_type))
|
||||
|
||||
for device in ring.stickup_cams:
|
||||
if 'stickup_cams' in SENSOR_TYPES[sensor_type][1]:
|
||||
sensors.append(RingBinarySensor(hass,
|
||||
device,
|
||||
sensor_type))
|
||||
add_devices(sensors, True)
|
||||
return True
|
||||
|
||||
|
|
141
homeassistant/components/camera/ring.py
Normal file
141
homeassistant/components/camera/ring.py
Normal file
|
@ -0,0 +1,141 @@
|
|||
"""
|
||||
This component provides support to the Ring Door Bell camera.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/camera.ring/
|
||||
"""
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.components.ring import DATA_RING, CONF_ATTRIBUTION
|
||||
from homeassistant.components.camera import Camera, PLATFORM_SCHEMA
|
||||
from homeassistant.components.ffmpeg import DATA_FFMPEG
|
||||
from homeassistant.const import ATTR_ATTRIBUTION, CONF_SCAN_INTERVAL
|
||||
from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments'
|
||||
|
||||
DEPENDENCIES = ['ring', 'ffmpeg']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SCAN_INTERVAL = timedelta(seconds=90)
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_FFMPEG_ARGUMENTS): cv.string,
|
||||
vol.Optional(CONF_SCAN_INTERVAL, default=SCAN_INTERVAL):
|
||||
cv.time_period,
|
||||
})
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
||||
"""Set up a Ring Door Bell and StickUp Camera."""
|
||||
ring = hass.data[DATA_RING]
|
||||
|
||||
cams = []
|
||||
for camera in ring.doorbells:
|
||||
cams.append(RingCam(hass, camera, config))
|
||||
|
||||
for camera in ring.stickup_cams:
|
||||
cams.append(RingCam(hass, camera, config))
|
||||
|
||||
async_add_devices(cams, True)
|
||||
return True
|
||||
|
||||
|
||||
class RingCam(Camera):
|
||||
"""An implementation of a Ring Door Bell camera."""
|
||||
|
||||
def __init__(self, hass, camera, device_info):
|
||||
"""Initialize a Ring Door Bell camera."""
|
||||
super(RingCam, self).__init__()
|
||||
self._camera = camera
|
||||
self._hass = hass
|
||||
self._name = self._camera.name
|
||||
self._ffmpeg = hass.data[DATA_FFMPEG]
|
||||
self._ffmpeg_arguments = device_info.get(CONF_FFMPEG_ARGUMENTS)
|
||||
self._last_video_id = self._camera.last_recording_id
|
||||
self._video_url = self._camera.recording_url(self._last_video_id)
|
||||
self._expires_at = None
|
||||
self._utcnow = None
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of this camera."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes."""
|
||||
return {
|
||||
ATTR_ATTRIBUTION: CONF_ATTRIBUTION,
|
||||
'device_id': self._camera.id,
|
||||
'firmware': self._camera.firmware,
|
||||
'kind': self._camera.kind,
|
||||
'timezone': self._camera.timezone,
|
||||
'type': self._camera.family,
|
||||
'video_url': self._video_url,
|
||||
'video_id': self._last_video_id
|
||||
}
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_camera_image(self):
|
||||
"""Return a still image response from the camera."""
|
||||
from haffmpeg import ImageFrame, IMAGE_JPEG
|
||||
ffmpeg = ImageFrame(self._ffmpeg.binary, loop=self.hass.loop)
|
||||
|
||||
if self._video_url is None:
|
||||
return
|
||||
|
||||
image = yield from asyncio.shield(ffmpeg.get_image(
|
||||
self._video_url, output_format=IMAGE_JPEG,
|
||||
extra_cmd=self._ffmpeg_arguments), loop=self.hass.loop)
|
||||
return image
|
||||
|
||||
@asyncio.coroutine
|
||||
def handle_async_mjpeg_stream(self, request):
|
||||
"""Generate an HTTP MJPEG stream from the camera."""
|
||||
from haffmpeg import CameraMjpeg
|
||||
|
||||
if self._video_url is None:
|
||||
return
|
||||
|
||||
stream = CameraMjpeg(self._ffmpeg.binary, loop=self.hass.loop)
|
||||
yield from stream.open_camera(
|
||||
self._video_url, extra_cmd=self._ffmpeg_arguments)
|
||||
|
||||
yield from async_aiohttp_proxy_stream(
|
||||
self.hass, request, stream,
|
||||
'multipart/x-mixed-replace;boundary=ffserver')
|
||||
yield from stream.close()
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""Update the image periodically."""
|
||||
return True
|
||||
|
||||
def update(self):
|
||||
"""Update camera entity and refresh attributes."""
|
||||
# extract the video expiration from URL
|
||||
x_amz_expires = int(self._video_url.split('&')[0].split('=')[-1])
|
||||
x_amz_date = self._video_url.split('&')[1].split('=')[-1]
|
||||
|
||||
self._utcnow = dt_util.utcnow()
|
||||
self._expires_at = \
|
||||
timedelta(seconds=x_amz_expires) + \
|
||||
dt_util.as_utc(datetime.strptime(x_amz_date, "%Y%m%dT%H%M%SZ"))
|
||||
|
||||
if self._last_video_id != self._camera.last_recording_id:
|
||||
_LOGGER.debug("Updated Ring DoorBell last_video_id")
|
||||
self._last_video_id = self._camera.last_recording_id
|
||||
|
||||
if self._utcnow >= self._expires_at:
|
||||
_LOGGER.debug("Updated Ring DoorBell video_url")
|
||||
self._video_url = self._camera.recording_url(self._last_video_id)
|
|
@ -12,7 +12,7 @@ from homeassistant.const import CONF_USERNAME, CONF_PASSWORD
|
|||
|
||||
from requests.exceptions import HTTPError, ConnectTimeout
|
||||
|
||||
REQUIREMENTS = ['ring_doorbell==0.1.4']
|
||||
REQUIREMENTS = ['ring_doorbell==0.1.6']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -21,6 +21,7 @@ CONF_ATTRIBUTION = "Data provided by Ring.com"
|
|||
NOTIFICATION_ID = 'ring_notification'
|
||||
NOTIFICATION_TITLE = 'Ring Sensor Setup'
|
||||
|
||||
DATA_RING = 'ring'
|
||||
DOMAIN = 'ring'
|
||||
DEFAULT_CACHEDB = '.ring_cache.pickle'
|
||||
DEFAULT_ENTITY_NAMESPACE = 'ring'
|
||||
|
|
|
@ -11,7 +11,7 @@ import voluptuous as vol
|
|||
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.components.ring import (
|
||||
CONF_ATTRIBUTION, DEFAULT_ENTITY_NAMESPACE)
|
||||
CONF_ATTRIBUTION, DEFAULT_ENTITY_NAMESPACE, DATA_RING)
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||
from homeassistant.const import (
|
||||
CONF_ENTITY_NAMESPACE, CONF_MONITORED_CONDITIONS,
|
||||
|
@ -27,24 +27,43 @@ SCAN_INTERVAL = timedelta(seconds=30)
|
|||
|
||||
# Sensor types: Name, category, units, icon, kind
|
||||
SENSOR_TYPES = {
|
||||
'battery': ['Battery', ['doorbell'], '%', 'battery-50', None],
|
||||
'last_activity': ['Last Activity', ['doorbell'], None, 'history', None],
|
||||
'last_ding': ['Last Ding', ['doorbell'], None, 'history', 'ding'],
|
||||
'last_motion': ['Last Motion', ['doorbell'], None, 'history', 'motion'],
|
||||
'volume': ['Volume', ['chime', 'doorbell'], None, 'bell-ring', None],
|
||||
'battery': [
|
||||
'Battery', ['doorbell', 'stickup_cams'], '%', 'battery-50', None],
|
||||
|
||||
'last_activity': [
|
||||
'Last Activity', ['doorbell', 'stickup_cams'], None, 'history', None],
|
||||
|
||||
'last_ding': [
|
||||
'Last Ding', ['doorbell', 'stickup_cams'], None, 'history', 'ding'],
|
||||
|
||||
'last_motion': [
|
||||
'Last Motion', ['doorbell', 'stickup_cams'], None,
|
||||
'history', 'motion'],
|
||||
|
||||
'volume': [
|
||||
'Volume', ['chime', 'doorbell', 'stickup_cams'], None,
|
||||
'bell-ring', None],
|
||||
|
||||
'wifi_signal_category': [
|
||||
'WiFi Signal Category', ['chime', 'doorbell', 'stickup_cams'], None,
|
||||
'wifi', None],
|
||||
|
||||
'wifi_signal_strength': [
|
||||
'WiFi Signal Strength', ['chime', 'doorbell', 'stickup_cams'], 'dBm',
|
||||
'wifi', None],
|
||||
}
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_ENTITY_NAMESPACE, default=DEFAULT_ENTITY_NAMESPACE):
|
||||
cv.string,
|
||||
vol.Required(CONF_MONITORED_CONDITIONS, default=[]):
|
||||
vol.Required(CONF_MONITORED_CONDITIONS, default=list(SENSOR_TYPES)):
|
||||
vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]),
|
||||
})
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Set up a sensor for a Ring device."""
|
||||
ring = hass.data.get('ring')
|
||||
ring = hass.data[DATA_RING]
|
||||
|
||||
sensors = []
|
||||
for sensor_type in config.get(CONF_MONITORED_CONDITIONS):
|
||||
|
@ -56,6 +75,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||
if 'doorbell' in SENSOR_TYPES[sensor_type][1]:
|
||||
sensors.append(RingSensor(hass, device, sensor_type))
|
||||
|
||||
for device in ring.stickup_cams:
|
||||
if 'stickup_cams' in SENSOR_TYPES[sensor_type][1]:
|
||||
sensors.append(RingSensor(hass, device, sensor_type))
|
||||
|
||||
add_devices(sensors, True)
|
||||
return True
|
||||
|
||||
|
@ -97,6 +120,7 @@ class RingSensor(Entity):
|
|||
attrs['kind'] = self._data.kind
|
||||
attrs['timezone'] = self._data.timezone
|
||||
attrs['type'] = self._data.family
|
||||
attrs['wifi_name'] = self._data.wifi_name
|
||||
|
||||
if self._extra and self._sensor_type.startswith('last_'):
|
||||
attrs['created_at'] = self._extra['created_at']
|
||||
|
@ -132,10 +156,18 @@ class RingSensor(Entity):
|
|||
self._state = self._data.battery_life
|
||||
|
||||
if self._sensor_type.startswith('last_'):
|
||||
history = self._data.history(timezone=self._tz,
|
||||
kind=self._kind)
|
||||
history = self._data.history(limit=5,
|
||||
timezone=self._tz,
|
||||
kind=self._kind,
|
||||
enforce_limit=True)
|
||||
if history:
|
||||
self._extra = history[0]
|
||||
created_at = self._extra['created_at']
|
||||
self._state = '{0:0>2}:{1:0>2}'.format(
|
||||
created_at.hour, created_at.minute)
|
||||
|
||||
if self._sensor_type == 'wifi_signal_category':
|
||||
self._state = self._data.wifi_signal_category
|
||||
|
||||
if self._sensor_type == 'wifi_signal_strength':
|
||||
self._state = self._data.wifi_signal_strength
|
||||
|
|
|
@ -900,7 +900,7 @@ restrictedpython==4.0a3
|
|||
rflink==0.0.34
|
||||
|
||||
# homeassistant.components.ring
|
||||
ring_doorbell==0.1.4
|
||||
ring_doorbell==0.1.6
|
||||
|
||||
# homeassistant.components.notify.rocketchat
|
||||
rocketchat-API==0.6.1
|
||||
|
|
|
@ -130,7 +130,7 @@ restrictedpython==4.0a3
|
|||
rflink==0.0.34
|
||||
|
||||
# homeassistant.components.ring
|
||||
ring_doorbell==0.1.4
|
||||
ring_doorbell==0.1.6
|
||||
|
||||
# homeassistant.components.media_player.yamaha
|
||||
rxv==0.5.1
|
||||
|
|
|
@ -50,6 +50,8 @@ class TestRingBinarySensorSetup(unittest.TestCase):
|
|||
text=load_fixture('ring_devices.json'))
|
||||
mock.get('https://api.ring.com/clients_api/dings/active',
|
||||
text=load_fixture('ring_ding_active.json'))
|
||||
mock.get('https://api.ring.com/clients_api/doorbots/987652/health',
|
||||
text=load_fixture('ring_doorboot_health_attrs.json'))
|
||||
|
||||
base_ring.setup(self.hass, VALID_CONFIG)
|
||||
ring.setup_platform(self.hass,
|
||||
|
|
|
@ -38,7 +38,9 @@ class TestRingSensorSetup(unittest.TestCase):
|
|||
'last_activity',
|
||||
'last_ding',
|
||||
'last_motion',
|
||||
'volume']
|
||||
'volume',
|
||||
'wifi_signal_category',
|
||||
'wifi_signal_strength']
|
||||
}
|
||||
|
||||
def tearDown(self):
|
||||
|
@ -55,6 +57,10 @@ class TestRingSensorSetup(unittest.TestCase):
|
|||
text=load_fixture('ring_devices.json'))
|
||||
mock.get('https://api.ring.com/clients_api/doorbots/987652/history',
|
||||
text=load_fixture('ring_doorbots.json'))
|
||||
mock.get('https://api.ring.com/clients_api/doorbots/987652/health',
|
||||
text=load_fixture('ring_doorboot_health_attrs.json'))
|
||||
mock.get('https://api.ring.com/clients_api/chimes/999999/health',
|
||||
text=load_fixture('ring_chime_health_attrs.json'))
|
||||
base_ring.setup(self.hass, VALID_CONFIG)
|
||||
ring.setup_platform(self.hass,
|
||||
self.config,
|
||||
|
@ -63,6 +69,12 @@ class TestRingSensorSetup(unittest.TestCase):
|
|||
|
||||
for device in self.DEVICES:
|
||||
device.update()
|
||||
if device.name == 'Front Battery':
|
||||
self.assertEqual(80, device.state)
|
||||
self.assertEqual('hp_cam_v1',
|
||||
device.device_state_attributes['kind'])
|
||||
self.assertEqual('stickup_cams',
|
||||
device.device_state_attributes['type'])
|
||||
if device.name == 'Front Door Battery':
|
||||
self.assertEqual(100, device.state)
|
||||
self.assertEqual('lpd_v1',
|
||||
|
@ -73,6 +85,8 @@ class TestRingSensorSetup(unittest.TestCase):
|
|||
self.assertEqual(2, device.state)
|
||||
self.assertEqual('1.2.3',
|
||||
device.device_state_attributes['firmware'])
|
||||
self.assertEqual('ring_mock_wifi',
|
||||
device.device_state_attributes['wifi_name'])
|
||||
self.assertEqual('mdi:bell-ring', device.icon)
|
||||
self.assertEqual('chimes',
|
||||
device.device_state_attributes['type'])
|
||||
|
@ -81,6 +95,15 @@ class TestRingSensorSetup(unittest.TestCase):
|
|||
self.assertEqual('America/New_York',
|
||||
device.device_state_attributes['timezone'])
|
||||
|
||||
if device.name == 'Downstairs WiFi Signal Strength':
|
||||
self.assertEqual(-39, device.state)
|
||||
|
||||
if device.name == 'Front Door WiFi Signal Category':
|
||||
self.assertEqual('good', device.state)
|
||||
|
||||
if device.name == 'Front Door WiFi Signal Strength':
|
||||
self.assertEqual(-58, device.state)
|
||||
|
||||
self.assertIsNone(device.entity_picture)
|
||||
self.assertEqual(ATTRIBUTION,
|
||||
device.device_state_attributes['attribution'])
|
||||
|
|
18
tests/fixtures/ring_chime_health_attrs.json
vendored
Normal file
18
tests/fixtures/ring_chime_health_attrs.json
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"device_health": {
|
||||
"average_signal_category": "good",
|
||||
"average_signal_strength": -39,
|
||||
"battery_percentage": 100,
|
||||
"battery_percentage_category": null,
|
||||
"battery_voltage": null,
|
||||
"battery_voltage_category": null,
|
||||
"firmware": "1.2.3",
|
||||
"firmware_out_of_date": false,
|
||||
"id": 999999,
|
||||
"latest_signal_category": "good",
|
||||
"latest_signal_strength": -39,
|
||||
"updated_at": "2017-09-30T07:05:03Z",
|
||||
"wifi_is_ring_network": false,
|
||||
"wifi_name": "ring_mock_wifi"
|
||||
}
|
||||
}
|
138
tests/fixtures/ring_devices.json
vendored
138
tests/fixtures/ring_devices.json
vendored
|
@ -75,5 +75,143 @@
|
|||
"high"]},
|
||||
"subscribed": true,
|
||||
"subscribed_motions": true,
|
||||
"time_zone": "America/New_York"}],
|
||||
"stickup_cams": [
|
||||
{
|
||||
"address": "123 Main St",
|
||||
"alerts": {"connection": "online"},
|
||||
"battery_life": 80,
|
||||
"description": "Front",
|
||||
"device_id": "aacdef123",
|
||||
"external_connection": false,
|
||||
"features": {
|
||||
"advanced_motion_enabled": false,
|
||||
"motion_message_enabled": false,
|
||||
"motions_enabled": true,
|
||||
"night_vision_enabled": false,
|
||||
"people_only_enabled": false,
|
||||
"shadow_correction_enabled": false,
|
||||
"show_recordings": true},
|
||||
"firmware_version": "1.9.3",
|
||||
"id": 987652,
|
||||
"kind": "hp_cam_v1",
|
||||
"latitude": 12.000000,
|
||||
"led_status": "off",
|
||||
"location_id": null,
|
||||
"longitude": -70.12345,
|
||||
"motion_snooze": {"scheduled": true},
|
||||
"night_mode_status": "false",
|
||||
"owned": true,
|
||||
"owner": {
|
||||
"email": "foo@bar.org",
|
||||
"first_name": "Foo",
|
||||
"id": 999999,
|
||||
"last_name": "Bar"},
|
||||
"ring_cam_light_installed": "false",
|
||||
"ring_id": null,
|
||||
"settings": {
|
||||
"chime_settings": {
|
||||
"duration": 10,
|
||||
"enable": true,
|
||||
"type": 0},
|
||||
"doorbell_volume": 11,
|
||||
"enable_vod": true,
|
||||
"floodlight_settings": {
|
||||
"duration": 30,
|
||||
"priority": 0},
|
||||
"light_schedule_settings": {
|
||||
"end_hour": 0,
|
||||
"end_minute": 0,
|
||||
"start_hour": 0,
|
||||
"start_minute": 0},
|
||||
"live_view_preset_profile": "highest",
|
||||
"live_view_presets": [
|
||||
"low",
|
||||
"middle",
|
||||
"high",
|
||||
"highest"],
|
||||
"motion_announcement": false,
|
||||
"motion_snooze_preset_profile": "low",
|
||||
"motion_snooze_presets": [
|
||||
"none",
|
||||
"low",
|
||||
"medium",
|
||||
"high"],
|
||||
"motion_zones": {
|
||||
"active_motion_filter": 1,
|
||||
"advanced_object_settings": {
|
||||
"human_detection_confidence": {
|
||||
"day": 0.7,
|
||||
"night": 0.7},
|
||||
"motion_zone_overlap": {
|
||||
"day": 0.1,
|
||||
"night": 0.2},
|
||||
"object_size_maximum": {
|
||||
"day": 0.8,
|
||||
"night": 0.8},
|
||||
"object_size_minimum": {
|
||||
"day": 0.03,
|
||||
"night": 0.05},
|
||||
"object_time_overlap": {
|
||||
"day": 0.1,
|
||||
"night": 0.6}
|
||||
},
|
||||
"enable_audio": false,
|
||||
"pir_settings": {
|
||||
"sensitivity1": 1,
|
||||
"sensitivity2": 1,
|
||||
"sensitivity3": 1,
|
||||
"zone_mask": 6},
|
||||
"sensitivity": 5,
|
||||
"zone1": {
|
||||
"name": "Zone 1",
|
||||
"state": 2,
|
||||
"vertex1": {"x": 0.0, "y": 0.0},
|
||||
"vertex2": {"x": 0.0, "y": 0.0},
|
||||
"vertex3": {"x": 0.0, "y": 0.0},
|
||||
"vertex4": {"x": 0.0, "y": 0.0},
|
||||
"vertex5": {"x": 0.0, "y": 0.0},
|
||||
"vertex6": {"x": 0.0, "y": 0.0},
|
||||
"vertex7": {"x": 0.0, "y": 0.0},
|
||||
"vertex8": {"x": 0.0, "y": 0.0}},
|
||||
"zone2": {
|
||||
"name": "Zone 2",
|
||||
"state": 2,
|
||||
"vertex1": {"x": 0.0, "y": 0.0},
|
||||
"vertex2": {"x": 0.0, "y": 0.0},
|
||||
"vertex3": {"x": 0.0, "y": 0.0},
|
||||
"vertex4": {"x": 0.0, "y": 0.0},
|
||||
"vertex5": {"x": 0.0, "y": 0.0},
|
||||
"vertex6": {"x": 0.0, "y": 0.0},
|
||||
"vertex7": {"x": 0.0, "y": 0.0},
|
||||
"vertex8": {"x": 0.0, "y": 0.0}},
|
||||
"zone3": {
|
||||
"name": "Zone 3",
|
||||
"state": 2,
|
||||
"vertex1": {"x": 0.0, "y": 0.0},
|
||||
"vertex2": {"x": 0.0, "y": 0.0},
|
||||
"vertex3": {"x": 0.0, "y": 0.0},
|
||||
"vertex4": {"x": 0.0, "y": 0.0},
|
||||
"vertex5": {"x": 0.0, "y": 0.0},
|
||||
"vertex6": {"x": 0.0, "y": 0.0},
|
||||
"vertex7": {"x": 0.0, "y": 0.0},
|
||||
"vertex8": {"x": 0.0, "y": 0.0}}},
|
||||
"pir_motion_zones": [0, 1, 1],
|
||||
"pir_settings": {
|
||||
"sensitivity1": 1,
|
||||
"sensitivity2": 1,
|
||||
"sensitivity3": 1,
|
||||
"zone_mask": 6},
|
||||
"stream_setting": 0,
|
||||
"video_settings": {
|
||||
"ae_level": 0,
|
||||
"birton": null,
|
||||
"brightness": 0,
|
||||
"contrast": 64,
|
||||
"saturation": 80}},
|
||||
"siren_status": {"seconds_remaining": 0},
|
||||
"stolen": false,
|
||||
"subscribed": true,
|
||||
"subscribed_motions": true,
|
||||
"time_zone": "America/New_York"}]
|
||||
}
|
||||
|
|
18
tests/fixtures/ring_doorboot_health_attrs.json
vendored
Normal file
18
tests/fixtures/ring_doorboot_health_attrs.json
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"device_health": {
|
||||
"average_signal_category": "good",
|
||||
"average_signal_strength": -39,
|
||||
"battery_percentage": 100,
|
||||
"battery_percentage_category": null,
|
||||
"battery_voltage": null,
|
||||
"battery_voltage_category": null,
|
||||
"firmware": "1.9.2",
|
||||
"firmware_out_of_date": false,
|
||||
"id": 987652,
|
||||
"latest_signal_category": "good",
|
||||
"latest_signal_strength": -58,
|
||||
"updated_at": "2017-09-30T07:05:03Z",
|
||||
"wifi_is_ring_network": false,
|
||||
"wifi_name": "ring_mock_wifi"
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue