From efa0def1fb1823b58e74b111311f20e6d47c37c7 Mon Sep 17 00:00:00 2001 From: Steve Gombos <3118886+steve-gombos@users.noreply.github.com> Date: Sat, 18 Jan 2020 14:19:06 -0500 Subject: [PATCH 01/13] Removed python 2.7 from tox. Updated readme and test.py examples --- README.rst | 13 ++++++------- test.py | 7 +++---- tox.ini | 2 +- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/README.rst b/README.rst index d547c1cb..ec161e71 100644 --- a/README.rst +++ b/README.rst @@ -58,17 +58,16 @@ Listing devices linked to your account .. code-block:: python # All devices - myring.devices() + ring.devices() {'chimes': [], - 'doorbells': []} + 'doorbots': []} Playing with the attributes and functions ----------------------------------------- .. code-block:: python - for dev in list(myring.stickup_cams + myring.chimes + myring.doorbells): + 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) @@ -96,7 +95,7 @@ Showing door bell events ------------------------ .. code-block:: python - for doorbell in myring.doorbells: + for doorbell in devices['doorbots']: # listing the last 15 events of any kind for event in doorbell.history(limit=15): @@ -114,10 +113,10 @@ Downloading the last video triggered by ding -------------------------------------------- .. code-block:: python - doorbell = myring.doorbells[0] + 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) diff --git a/test.py b/test.py index f96f604c..72726347 100644 --- a/test.py +++ b/test.py @@ -1,6 +1,5 @@ import json from pathlib import Path -from pprint import pprint from ring_doorbell import Ring, Auth from oauthlib.oauth2 import MissingTokenError @@ -28,7 +27,7 @@ def main(): else: username = input("Username: ") password = input("Password: ") - auth = Auth(None, token_updated) + auth = Auth("HomeAssistant/0.105.0dev0", None, token_updated) try: auth.fetch_token(username, password) except MissingTokenError: @@ -36,10 +35,10 @@ def main(): ring = Ring(auth) ring.update_data() - + devices = ring.devices() print(f"Hello {ring.session['profile']['first_name']}") print() - pprint(ring.devices_data) + print(devices) if __name__ == "__main__": diff --git a/tox.ini b/tox.ini index 2f672d83..8cfbeb67 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27, py35, py36, py37, lint +envlist = py35, py36, py37, lint skip_missing_interpreters = True [testenv] From bbcfb1fe7244f58a098fd859026edb6580f73378 Mon Sep 17 00:00:00 2001 From: Steve Gombos <3118886+steve-gombos@users.noreply.github.com> Date: Sat, 18 Jan 2020 14:27:50 -0500 Subject: [PATCH 02/13] Removed another python 2.7 reference --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index ec161e71..91aaaa69 100644 --- a/README.rst +++ b/README.rst @@ -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 in Python 3x 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.* From 1d500dcfff34eb332cf8ba3dbe6b67e7510f6ac6 Mon Sep 17 00:00:00 2001 From: Steve Gombos <3118886+steve-gombos@users.noreply.github.com> Date: Sat, 18 Jan 2020 14:34:13 -0500 Subject: [PATCH 03/13] Removed python 2.7 from travis --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 524a67c1..df5e369e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,6 @@ language: python matrix: fast_finish: true include: - - python: "2.7" - env: TOXENV=py27 - python: "3.5" env: TOXENV=py35 - python: "3.6" From 89b269ee9aac60ff10be1e36cbc23e20bcb308c0 Mon Sep 17 00:00:00 2001 From: Steve Gombos <3118886+steve-gombos@users.noreply.github.com> Date: Sat, 18 Jan 2020 14:49:37 -0500 Subject: [PATCH 04/13] Added pathlib to requirements_test --- requirements_tests.txt | 1 + test.py | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/requirements_tests.txt b/requirements_tests.txt index 634c6f84..114f312c 100644 --- a/requirements_tests.txt +++ b/requirements_tests.txt @@ -6,3 +6,4 @@ pytest pytest-cov requests_mock tox +pathlib diff --git a/test.py b/test.py index 72726347..86ec5b6e 100644 --- a/test.py +++ b/test.py @@ -36,8 +36,6 @@ def main(): ring = Ring(auth) ring.update_data() devices = ring.devices() - print(f"Hello {ring.session['profile']['first_name']}") - print() print(devices) From 4512c40278920855f1195335e3865c53eaf17da3 Mon Sep 17 00:00:00 2001 From: Steve Gombos <3118886+steve-gombos@users.noreply.github.com> Date: Sat, 18 Jan 2020 15:46:21 -0500 Subject: [PATCH 05/13] Removed python 3.5 --- .travis.yml | 2 -- setup.py | 2 -- tox.ini | 2 +- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index df5e369e..209b5dd1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,6 @@ language: python matrix: fast_finish: true include: - - python: "3.5" - env: TOXENV=py35 - python: "3.6" env: TOXENV=py36 - python: "3.7" diff --git a/setup.py b/setup.py index d35d6ed8..40ec591b 100644 --- a/setup.py +++ b/setup.py @@ -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', diff --git a/tox.ini b/tox.ini index 8cfbeb67..dcfaec63 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py35, py36, py37, lint +envlist = py36, py37, lint skip_missing_interpreters = True [testenv] From 36b65a6e8e5d5749360366b95ae8a34871111297 Mon Sep 17 00:00:00 2001 From: Steve Gombos <3118886+steve-gombos@users.noreply.github.com> Date: Sat, 18 Jan 2020 22:47:21 -0500 Subject: [PATCH 06/13] Additional readme updates --- README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rst b/README.rst index 91aaaa69..07ce4065 100644 --- a/README.rst +++ b/README.rst @@ -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']) @@ -66,6 +67,7 @@ Playing with the attributes and functions ----------------------------------------- .. code-block:: python + devices = ring.devices() for dev in list(devices['stickup_cams'] + devices['chimes'] + devices['doorbots']): dev.update_health_data() print('Address: %s' % dev.address) @@ -95,6 +97,7 @@ Showing door bell events ------------------------ .. code-block:: python + devices = ring.devices() for doorbell in devices['doorbots']: # listing the last 15 events of any kind @@ -113,6 +116,7 @@ Downloading the last video triggered by ding -------------------------------------------- .. code-block:: python + devices = ring.devices() doorbell = devices['doorbots'][0] doorbell.recording_download( doorbell.history(limit=100, kind='ding')[0]['id'], From f700881c27466c3f34e82b0555f411b3e46c1c12 Mon Sep 17 00:00:00 2001 From: steve-gombos <3118886+steve-gombos@users.noreply.github.com> Date: Sun, 19 Jan 2020 01:24:16 -0500 Subject: [PATCH 07/13] Update README.rst Co-Authored-By: Paulus Schoutsen --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 07ce4065..b7fb9dab 100644 --- a/README.rst +++ b/README.rst @@ -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 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.* From 6fa5968be7136b2a968cc5adf135b9e621bbfbd5 Mon Sep 17 00:00:00 2001 From: steve-gombos <3118886+steve-gombos@users.noreply.github.com> Date: Sun, 19 Jan 2020 01:24:32 -0500 Subject: [PATCH 08/13] Update test.py Co-Authored-By: Paulus Schoutsen --- test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.py b/test.py index 86ec5b6e..d0b050c3 100644 --- a/test.py +++ b/test.py @@ -27,7 +27,7 @@ def main(): else: username = input("Username: ") password = input("Password: ") - auth = Auth("HomeAssistant/0.105.0dev0", None, token_updated) + auth = Auth("MyProject/1.0", None, token_updated) try: auth.fetch_token(username, password) except MissingTokenError: From 73b836f3fbd980058664ac1160bc887af00d1128 Mon Sep 17 00:00:00 2001 From: Steve Gombos <3118886+steve-gombos@users.noreply.github.com> Date: Sun, 19 Jan 2020 01:33:17 -0500 Subject: [PATCH 09/13] Updated user agent --- test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.py b/test.py index d0b050c3..c245e851 100644 --- a/test.py +++ b/test.py @@ -20,7 +20,7 @@ def otp_callback(): def main(): if cache_file.is_file(): auth = Auth( - "HomeAssistant/0.105.0dev0", + "MyProject/1.0", json.loads(cache_file.read_text()), token_updated, ) From bc2585283af54405af32fe916de225bfd728684a Mon Sep 17 00:00:00 2001 From: Steve Gombos <3118886+steve-gombos@users.noreply.github.com> Date: Sun, 19 Jan 2020 01:37:23 -0500 Subject: [PATCH 10/13] Black error --- test.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/test.py b/test.py index c245e851..30c6ec63 100644 --- a/test.py +++ b/test.py @@ -19,11 +19,7 @@ def otp_callback(): def main(): if cache_file.is_file(): - auth = Auth( - "MyProject/1.0", - 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: ") From 118197cddb5b924de475ca39293ff58eb5060ef1 Mon Sep 17 00:00:00 2001 From: Steve Gombos <3118886+steve-gombos@users.noreply.github.com> Date: Sun, 19 Jan 2020 02:11:04 -0500 Subject: [PATCH 11/13] Removed pathlib dep --- requirements_tests.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements_tests.txt b/requirements_tests.txt index 114f312c..634c6f84 100644 --- a/requirements_tests.txt +++ b/requirements_tests.txt @@ -6,4 +6,3 @@ pytest pytest-cov requests_mock tox -pathlib From f878f924dab286917e78750976110a4fdebca11b Mon Sep 17 00:00:00 2001 From: Steve Gombos <3118886+steve-gombos@users.noreply.github.com> Date: Sun, 19 Jan 2020 10:09:25 -0500 Subject: [PATCH 12/13] Updated ringcli script to use new auth implementation --- scripts/ringcli.py | 169 ++++++++++++++++++++++++--------------------- test.py | 5 +- tox.ini | 4 +- 3 files changed, 97 insertions(+), 81 deletions(-) diff --git a/scripts/ringcli.py b/scripts/ringcli.py index a2826726..2bb2b8ec 100755 --- a/scripts/ringcli.py +++ b/scripts/ringcli.py @@ -2,9 +2,12 @@ # vim:sw=4:ts=4:et # Many thanks to @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(): @@ -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)) @@ -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() diff --git a/test.py b/test.py index 30c6ec63..0275bc26 100644 --- a/test.py +++ b/test.py @@ -1,4 +1,5 @@ import json +import getpass from pathlib import Path from ring_doorbell import Ring, Auth @@ -19,10 +20,10 @@ def otp_callback(): def main(): if cache_file.is_file(): - auth = Auth("MyProject/1.0", 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: ") + password = getpass.getpass("Password: ") auth = Auth("MyProject/1.0", None, token_updated) try: auth.fetch_token(username, password) diff --git a/tox.ini b/tox.ini index dcfaec63..116c6353 100644 --- a/tox.ini +++ b/tox.ini @@ -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 From c470e5e77b25d570e614dc739e43a8b63de1c0c6 Mon Sep 17 00:00:00 2001 From: Steve Gombos <3118886+steve-gombos@users.noreply.github.com> Date: Mon, 20 Jan 2020 10:29:00 -0500 Subject: [PATCH 13/13] Added examples for getting all specific devices --- README.rst | 14 +++++++++++++- test.py | 9 +++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index b7fb9dab..0d98d380 100644 --- a/README.rst +++ b/README.rst @@ -59,10 +59,22 @@ Listing devices linked to your account .. code-block:: python # All devices - ring.devices() + devices = ring.devices() {'chimes': [], 'doorbots': []} + # All doorbells + doorbells = devices['doorbots'] + [] + + # All chimes + chimes = devices['chimes'] + [] + + # All stickup cams + stickup_cams = devices['stickup_cams'] + [] + Playing with the attributes and functions ----------------------------------------- .. code-block:: python diff --git a/test.py b/test.py index 0275bc26..0c68aa5e 100644 --- a/test.py +++ b/test.py @@ -32,9 +32,18 @@ def main(): ring = Ring(auth) ring.update_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__": main()