Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 0 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ language: python
matrix:
fast_finish: true
include:
- python: "2.7"
env: TOXENV=py27
- python: "3.5"
env: TOXENV=py35
- python: "3.6"
env: TOXENV=py36
- python: "3.7"
Expand Down
31 changes: 23 additions & 8 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Python Ring Door Bell
:target: https://pypi.python.org/pypi/ring-doorbell


Python Ring Door Bell is a library written in Python 2.7/3x
Python Ring Door Bell is a library written for Python 3.6+
that exposes the Ring.com devices as Python objects.

*Currently Ring.com does not provide an official API. The results of this project are merely from reverse engineering.*
Expand Down Expand Up @@ -48,6 +48,7 @@ Initializing your Ring object
auth.fetch_token(username, password)
ring = Ring(auth)
ring.update_data()
devices = ring.devices()

pprint(ring.session['profile'])

Expand All @@ -58,17 +59,29 @@ Listing devices linked to your account
.. code-block:: python

# All devices
myring.devices()
devices = ring.devices()
{'chimes': [<RingChime: Downstairs>],
'doorbells': [<RingDoorBell: Front Door>]}
'doorbots': [<RingDoorBell: Front Door>]}

# All doorbells
doorbells = devices['doorbots']
[<RingDoorBell: Front Door>]

# All chimes
chimes = devices['chimes']
[<RingChime: Downstairs>]

# All stickup cams
stickup_cams = devices['stickup_cams']
[<RingStickUpCam: Driveway>]

Playing with the attributes and functions
-----------------------------------------
.. code-block:: python

for dev in list(myring.stickup_cams + myring.chimes + myring.doorbells):
devices = ring.devices()
for dev in list(devices['stickup_cams'] + devices['chimes'] + devices['doorbots']):
dev.update_health_data()
print('Account ID: %s' % dev.account_id)
print('Address: %s' % dev.address)
print('Family: %s' % dev.family)
print('ID: %s' % dev.id)
Expand Down Expand Up @@ -96,7 +109,8 @@ Showing door bell events
------------------------
.. code-block:: python

for doorbell in myring.doorbells:
devices = ring.devices()
for doorbell in devices['doorbots']:

# listing the last 15 events of any kind
for event in doorbell.history(limit=15):
Expand All @@ -114,10 +128,11 @@ Downloading the last video triggered by ding
--------------------------------------------
.. code-block:: python

doorbell = myring.doorbells[0]
devices = ring.devices()
doorbell = devices['doorbots'][0]
doorbell.recording_download(
doorbell.history(limit=100, kind='ding')[0]['id'],
filename='/home/user/last_ding.mp4',
filename='last_ding.mp4',
override=True)


Expand Down
169 changes: 92 additions & 77 deletions scripts/ringcli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
# vim:sw=4:ts=4:et
# Many thanks to @troopermax <https://github.com/troopermax>

import json
import getpass
import argparse
from ring_doorbell import Ring
from pathlib import Path
from ring_doorbell import Ring, Auth
from oauthlib.oauth2 import MissingTokenError


def _header():
Expand All @@ -13,95 +16,106 @@ def _header():


def _bar():
print('---------------------------------')
print("---------------------------------")


def get_username():
try:
username = raw_input("Username: ")
except NameError:
username = input("Username: ")
return username
cache_file = Path("test_token.cache")


def token_updated(token):
cache_file.write_text(json.dumps(token))


def _format_filename(event):
if not isinstance(event, dict):
return

if event['answered']:
answered_status = 'answered'
if event["answered"]:
answered_status = "answered"
else:
answered_status = 'not_answered'
answered_status = "not_answered"

filename = "{}_{}_{}_{}".format(event['created_at'],
event['kind'],
answered_status,
event['id'])
filename = "{}_{}_{}_{}".format(
event["created_at"], event["kind"], answered_status, event["id"]
)

filename = filename.replace(' ', '_').replace(':', '.')+'.mp4'
filename = filename.replace(" ", "_").replace(":", ".") + ".mp4"
return filename


def main():

parser = argparse.ArgumentParser(
description='Ring Doorbell',
epilog='https://github.com/tchellomello/python-ring-doorbell',
formatter_class=argparse.RawDescriptionHelpFormatter)

parser.add_argument('-u',
'--username',
dest='username',
type=str,
help='username for Ring account')

parser.add_argument('-p',
'--password',
type=str,
dest='password',
help='username for Ring account')

parser.add_argument('--count',
action='store_true',
default=False,
help='count the number of videos on your Ring account')

parser.add_argument('--download-all',
action='store_true',
default=False,
help='download all videos on your Ring account')
description="Ring Doorbell",
epilog="https://github.com/tchellomello/python-ring-doorbell",
formatter_class=argparse.RawDescriptionHelpFormatter,
)

parser.add_argument(
"-u", "--username", dest="username", type=str, help="username for Ring account"
)

parser.add_argument(
"-p", "--password", type=str, dest="password", help="username for Ring account"
)

parser.add_argument(
"--count",
action="store_true",
default=False,
help="count the number of videos on your Ring account",
)

parser.add_argument(
"--download-all",
action="store_true",
default=False,
help="download all videos on your Ring account",
)

args = parser.parse_args()
_header()

if not args.username:
args.username = get_username()
# connect to Ring account
if cache_file.is_file():
auth = Auth("RingCLI/0.6", json.loads(cache_file.read_text()), token_updated)
else:
if not args.username:
args.username = input("Username: ")

if not args.password:
args.password = getpass.getpass("Password: ")

if not args.password:
args.password = getpass.getpass("Password: ")
auth = Auth("RingCLI/0.6", None, token_updated)
try:
auth.fetch_token(args.username, args.password)
except MissingTokenError:
auth.fetch_token(args.username, args.password, input("2FA Code: "))

# connect to Ring account
myring = Ring(args.username, args.password)
doorbell = myring.doorbells[0]
ring = Ring(auth)
ring.update_data()
devices = ring.devices()
doorbell = devices["doorbots"][0]

_bar()

if args.count:
print("\tCounting videos linked on your Ring account.\n" +
"\tThis may take some time....\n")
print(
"\tCounting videos linked on your Ring account.\n"
+ "\tThis may take some time....\n"
)

events = []
counter = 0
history = doorbell.history(limit=100)
while (len(history) > 0):
while len(history) > 0:
events += history
counter += len(history)
history = doorbell.history(older_than=history[-1]['id'])
history = doorbell.history(older_than=history[-1]["id"])

motion = len([m['kind'] for m in events if m['kind'] == 'motion'])
ding = len([m['kind'] for m in events if m['kind'] == 'ding'])
on_demand = \
len([m['kind'] for m in events if m['kind'] == 'on_demand'])
motion = len([m["kind"] for m in events if m["kind"] == "motion"])
ding = len([m["kind"] for m in events if m["kind"] == "ding"])
on_demand = len([m["kind"] for m in events if m["kind"] == "on_demand"])

print("\tTotal videos: {}".format(counter))
print("\tDing triggered: {}".format(ding))
Expand All @@ -111,43 +125,44 @@ def main():
# already have all events in memory
if args.download_all:
counter = 0
print("\tDownloading all videos linked on your Ring account.\n" +
"\tThis may take some time....\n")
print(
"\tDownloading all videos linked on your Ring account.\n"
+ "\tThis may take some time....\n"
)

for event in events:
counter += 1
filename = _format_filename(event)
print("\t{}/{} Downloading {}".format(counter,
len(events),
filename))
print("\t{}/{} Downloading {}".format(counter, len(events), filename))

doorbell.recording_download(event['id'],
filename=filename,
override=False)
doorbell.recording_download(
event["id"], filename=filename, override=False
)

if args.download_all and not args.count:
print("\tDownloading all videos linked on your Ring account.\n" +
"\tThis may take some time....\n")
print(
"\tDownloading all videos linked on your Ring account.\n"
+ "\tThis may take some time....\n"
)
history = doorbell.history(limit=100)

while (len(history) > 0):
print("\tProcessing and downloading the next" +
" videos".format(len(history)))
while len(history) > 0:
print(
"\tProcessing and downloading the next" + " videos".format(len(history))
)

counter = 0
for event in history:
counter += 1
filename = _format_filename(event)
print("\t{}/{} Downloading {}".format(counter,
len(history),
filename))
print("\t{}/{} Downloading {}".format(counter, len(history), filename))

doorbell.recording_download(event['id'],
filename=filename,
override=False)
doorbell.recording_download(
event["id"], filename=filename, override=False
)

history = doorbell.history(limit=100, older_than=history[-1]['id'])
history = doorbell.history(limit=100, older_than=history[-1]["id"])


if __name__ == '__main__':
if __name__ == "__main__":
main()
2 changes: 0 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ def readme():
'GNU Lesser General Public License v3 or later (LGPLv3+)',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Topic :: Home Automation',
Expand Down
25 changes: 14 additions & 11 deletions test.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import json
import getpass
from pathlib import Path
from pprint import pprint

from ring_doorbell import Ring, Auth
from oauthlib.oauth2 import MissingTokenError
Expand All @@ -20,15 +20,11 @@ def otp_callback():

def main():
if cache_file.is_file():
auth = Auth(
"HomeAssistant/0.105.0dev0",
json.loads(cache_file.read_text()),
token_updated,
)
auth = Auth("MyProject/1.0", json.loads(cache_file.read_text()), token_updated)
else:
username = input("Username: ")
password = input("Password: ")
auth = Auth(None, token_updated)
password = getpass.getpass("Password: ")
auth = Auth("MyProject/1.0", None, token_updated)
try:
auth.fetch_token(username, password)
except MissingTokenError:
Expand All @@ -37,9 +33,16 @@ def main():
ring = Ring(auth)
ring.update_data()

print(f"Hello {ring.session['profile']['first_name']}")
print()
pprint(ring.devices_data)
devices = ring.devices()
print(devices)

doorbells = devices["doorbots"]
chimes = devices["chimes"]
stickup_cams = devices["stickup_cams"]

print(doorbells)
print(chimes)
print(stickup_cams)


if __name__ == "__main__":
Expand Down
6 changes: 3 additions & 3 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tox]
envlist = py27, py35, py36, py37, lint
envlist = py36, py37, lint
skip_missing_interpreters = True

[testenv]
Expand All @@ -17,6 +17,6 @@ deps =
ignore_errors = True
commands =
pip3 install black
flake8 ring_doorbell tests test.py
flake8 ring_doorbell tests test.py scripts
pylint ring_doorbell
black --check ring_doorbell tests test.py
black --check ring_doorbell tests test.py scripts