Skip to content

Commit 5ca0073

Browse files
committed
Merge branch 'dev'
2 parents cbc643e + 5c7afcc commit 5ca0073

File tree

8 files changed

+322
-112
lines changed

8 files changed

+322
-112
lines changed

.coveragerc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
[run]
2+
source = ring_doorbell
3+
14
[report]
25
omit =
36
*/python?.?/*

README.md

Lines changed: 0 additions & 87 deletions
This file was deleted.

README.rst

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
=====================
2+
Python Ring Door Bell
3+
=====================
4+
5+
.. image:: https://badge.fury.io/py/ring-doorbell.svg
6+
:target: https://badge.fury.io/py/ring-doorbell
7+
8+
.. image:: https://travis-ci.org/tchellomello/python-ring-doorbell.svg?branch=master
9+
:target: https://travis-ci.org/tchellomello/python-ring-doorbell
10+
11+
.. image:: https://coveralls.io/repos/github/tchellomello/python-ring-doorbell/badge.svg
12+
:target: https://coveralls.io/github/tchellomello/python-ring-doorbell
13+
14+
.. image:: https://img.shields.io/pypi/pyversions/ring-doorbell.svg
15+
:target: https://pypi.python.org/pypi/ring-doorbell
16+
17+
18+
Python Ring Door Bell is a library written in Python 2.7/3x
19+
that exposes the Ring.com devices as Python objects.
20+
21+
*Currently Ring.com does not provide an official API. The results of this project are merely from reverse engineering.*
22+
23+
24+
Installation
25+
------------
26+
27+
.. code-block:: bash
28+
29+
# Installing from PyPi
30+
$ pip install ring_doorbell #python 2.7
31+
$ pip3 install ring_doorbell #python 3.x
32+
33+
# Installing latest development
34+
$ pip3 install \
35+
git+https://github.com/tchellomello/python-ring-doorbell@dev
36+
37+
38+
Initializing your Ring object
39+
-----------------------------
40+
41+
.. code-block:: python
42+
43+
from ring_doorbell import Ring
44+
myring = Ring('foo@bar', 'secret')
45+
46+
myring.is_connected
47+
True
48+
49+
myring.has_subscription
50+
True
51+
52+
53+
Listing devices linked to your account
54+
------------------------------------------
55+
56+
.. code-block:: python
57+
58+
# All devices
59+
myring.devices
60+
{'chimes': [<RingChime: Downstairs>],
61+
'doorbells': [<RingDoorBell: Front Door>]}
62+
63+
# All chimes
64+
myring.chimes
65+
[<RingChime: Downstairs>]
66+
67+
# All door bells
68+
myring.doorbells
69+
[<RingDoorBell: Front Door>]
70+
71+
Playing with the attributes
72+
--------------------------------
73+
.. code-block:: python
74+
75+
for dev in list(myring.chimes + myring.doorbells):
76+
77+
# refresh data
78+
dev.update()
79+
80+
print('Account ID: %s' % dev.account_id)
81+
print('Address: %s' % dev.address)
82+
print('Family: %s' % dev.family)
83+
print('ID: %s' % dev.id)
84+
print('Name: %s' % dev.name)
85+
print('Timezone: %s' % dev.timezone)
86+
87+
# setting dev volume
88+
print('Volume: %s' % dev.volume)
89+
dev.volume = 5
90+
print('Volume: %s' % dev.volume)
91+
92+
# play dev test shound
93+
if dev.family == 'chimes'
94+
dev.test_sound
95+
96+
97+
Showing door bell events
98+
------------------------
99+
.. code-block:: python
100+
101+
for doorbell in myring.doorbells:
102+
103+
# listing the last 15 events of any kind
104+
for event in doorbell.history(limit=15):
105+
print('ID: %s' % event['id'])
106+
print('Kind: %s' % event['kind'])
107+
print('Answered: %s' % event['answered'])
108+
print('When: %s' % event['created_at'])
109+
print('--' * 50)
110+
111+
# get a event list only the triggered by motion
112+
events = doorbell.history(kind='motion')
113+
114+
115+
Downloading the last video triggered by ding
116+
-------------------------------------------
117+
.. code-block:: python
118+
119+
doorbell = myring.doorbells[0]
120+
doorbell.recording_download(
121+
doorbell.history(limit=100, kind='ding')[0]['id'],
122+
filename='/home/user/last_ding.mp4',
123+
override=True)
124+
125+
126+
Displaying the last video capture URL
127+
-------------------------------------------
128+
.. code-block:: python
129+
130+
print(doorbell.recording_url(doorbell.last_recording_id))
131+
'https://ring-transcoded-videos.s3.amazonaws.com/99999999.mp4?X-Amz-Expires=3600&X-Amz-Date=20170313T232537Z&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=TOKEN_SECRET/us-east-1/s3/aws4_request&X-Amz-SignedHeaders=host&X-Amz-Signature=secret'
132+
133+
Credits && Thanks
134+
-----------------
135+
136+
* This project was inspired and based on https://github.com/jeroenmoors/php-ring-api. Many thanks @jeroenmoors.
137+
* A guy named MadBagger at Prism19 for his initial research (http://www.prism19.com/doorbot/second-pass-and-comm-reversing/)
138+
* The creators of mitmproxy (https://mitmproxy.org/) great http and https traffic inspector
139+
* @mfussenegger for his post on mitmproxy and virtualbox https://zignar.net/2015/12/31/sniffing-vbox-traffic-mitmproxy/
140+
* To the project http://www.android-x86.org/ which allowed me to install Android on KVM.

ring_doorbell/__init__.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
import requests
1414
import pytz
1515

16-
from ring_doorbell.utils import _locator, _save_cache, _read_cache
16+
from ring_doorbell.utils import (
17+
_locator, _clean_cache, _save_cache, _read_cache)
1718
from ring_doorbell.const import (
1819
API_VERSION, API_URI, CHIMES_ENDPOINT, CHIME_VOL_MIN, CHIME_VOL_MAX,
1920
DEVICES_ENDPOINT, DOORBELLS_ENDPOINT, DOORBELL_VOL_MIN, DOORBELL_VOL_MAX,
@@ -176,6 +177,11 @@ def __devices(self, device_type):
176177
for member in list((obj['description'] for obj in req)):
177178
lst.append(RingDoorBell(self, member))
178179

180+
# get shared doorbells, however device is read-only
181+
req = self.query(url).get('authorized_doorbots')
182+
for member in list((obj['description'] for obj in req)):
183+
lst.append(RingDoorBell(self, member, shared=True))
184+
179185
except AttributeError:
180186
pass
181187
return lst
@@ -237,10 +243,13 @@ def _update_alert(self):
237243
_save_cache(None, self._alert_cache)
238244

239245
def _get_attrs(self):
240-
"""Return chime attributes."""
246+
"""Return attributes."""
241247
url = API_URI + DEVICES_ENDPOINT
242248
try:
243-
lst = self._ring.query(url).get(self.family)
249+
if self.family == 'doorbots' and self.shared:
250+
lst = self._ring.query(url).get('authorized_doorbots')
251+
else:
252+
lst = self._ring.query(url).get(self.family)
244253
index = _locator(lst, 'description', self.name)
245254
if index == NOT_FOUND:
246255
return None
@@ -343,11 +352,12 @@ def test_sound(self):
343352
class RingDoorBell(RingGeneric):
344353
"""Implementation for Ring Doorbell."""
345354

346-
def __init__(self, ring, name):
355+
def __init__(self, ring, name, shared=False):
347356
"""Initilize Ring doorbell object."""
348357
super(RingDoorBell, self).__init__()
349358
self._attrs = None
350359
self._ring = ring
360+
self.shared = shared
351361
self.debug = self._ring.debug
352362
self.family = 'doorbots'
353363
self.name = name
@@ -366,14 +376,15 @@ def check_alerts(self, cache=None):
366376
# save alerts attributes to an external pickle file
367377
# when multiple resources are checking for alerts
368378
if cache:
379+
_clean_cache(cache)
369380
self._alert_cache = cache
370381

371382
url = API_URI + DINGS_ENDPOINT
372383
self.update()
373384

374385
try:
375386
resp = self._ring.query(url)[0]
376-
except IndexError:
387+
except (IndexError, TypeError):
377388
return None
378389

379390
if resp:
@@ -517,7 +528,10 @@ def history(self, limit=30, timezone=None, kind=None):
517528
@property
518529
def last_recording_id(self):
519530
"""Return the last recording ID."""
520-
return self.history(limit=1)[0]['id']
531+
try:
532+
return self.history(limit=1)[0]['id']
533+
except (IndexError, TypeError):
534+
return None
521535

522536
@property
523537
def live_streaming_json(self):
@@ -526,7 +540,10 @@ def live_streaming_json(self):
526540
req = self._ring.query((url), method='POST', raw=True)
527541
if req.status_code == 204:
528542
url = API_URI + DINGS_ENDPOINT
529-
return self._ring.query(url)[0]
543+
try:
544+
return self._ring.query(url)[0]
545+
except (IndexError, TypeError):
546+
pass
530547
return None
531548

532549
def recording_download(self, recording_id, filename=None, override=False):

ring_doorbell/utils.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ def _locator(lst, key, value):
1919
return NOT_FOUND
2020

2121

22+
def _clean_cache(filename):
23+
"""Remove filename if pickle version mismatch."""
24+
try:
25+
if os.path.isfile(filename):
26+
_read_cache(filename)
27+
except ValueError:
28+
os.remove(filename)
29+
return True
30+
31+
2232
def _save_cache(data, filename):
2333
"""Dump data into a pickle file."""
2434
try:
@@ -36,4 +46,3 @@ def _read_cache(filename):
3646
return pickle.load(open(filename, 'rb'))
3747
except:
3848
raise
39-
return None

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
setup(
66
name='ring_doorbell',
77
packages=['ring_doorbell'],
8-
version='0.1.1',
8+
version='0.1.2',
99
description='A Python library to communicate with Ring' +
1010
' Door Bell (https://ring.com/)',
1111
author='Marcelo Moreira de Mello',
@@ -28,7 +28,7 @@
2828
'Operating System :: OS Independent',
2929
'Programming Language :: Python',
3030
'Programming Language :: Python :: 2.7',
31-
'Programming Language :: Python :: 3',
31+
'Programming Language :: Python :: 3.5',
3232
'Topic :: Home Automation',
3333
'Topic :: Software Development :: Libraries :: Python Modules'
3434
],

0 commit comments

Comments
 (0)