Welcome to python-ring-doorbell’s documentation!
Python Ring Door Bell
Python Ring Door Bell is a library written for Python 3.8+ that exposes the Ring.com devices as Python objects.
There is also a command line interface that is work in progress. Contributors welcome.
Currently Ring.com does not provide an official API. The results of this project are merely from reverse engineering.
Documentation: http://python-ring-doorbell.readthedocs.io/
Installation
# Installing from PyPi
$ pip install ring_doorbell
# Installing latest development
$ pip install \
git+https://github.com/tchellomello/python-ring-doorbell@master
Event Listener
If you want the ring api to listen for push events from ring.com for dings and motion you will need to install with the listen extra:
$ pip install ring_doorbell[listen]
The api will then start listening for push events after you have first called update_dings() or update_data() but only if there is a running asyncio event loop (which there will be if using the CLI)
Using the CLI
The CLI is work in progress and currently has the following commands:
Show your devices:
$ ring-doorbell
Or:
$ ring-doorbell show
List your device names (with device kind):
$ ring-doorbell list
Either count or download your vidoes or both:
$ ring-doorbell videos --count --download-all
Enable disable motion detection:
$ ring-doorbell motion-detection --device-name "DEVICENAME" --on $ ring-doorbell motion-detection --device-name "DEVICENAME" --off
Listen for push notifications like the ones sent to your phone:
$ ring-doorbell listen
List your ring groups:
$ ring-doorbell groups
Show your ding history:
$ ring-doorbell history --device-name "Front Door"
Show your currently active dings:
$ ring-doorbell dings
Query a ring api url directly:
$ ring-doorbell raw-query --url /clients_api/dings/active
Run
ring-doorbell --help
orring-doorbell videos --help
for full options
Initializing your Ring object
import json
import getpass
from pathlib import Path
from pprint import pprint
from ring_doorbell import Ring, Auth
from oauthlib.oauth2 import MissingTokenError
cache_file = Path("test_token.cache")
def token_updated(token):
cache_file.write_text(json.dumps(token))
def otp_callback():
auth_code = input("2FA code: ")
return auth_code
def main():
if cache_file.is_file():
auth = Auth("MyProject/1.0", json.loads(cache_file.read_text()), token_updated)
else:
username = input("Username: ")
password = getpass.getpass("Password: ")
auth = Auth("MyProject/1.0", None, token_updated)
try:
auth.fetch_token(username, password)
except MissingTokenError:
auth.fetch_token(username, password, otp_callback())
ring = Ring(auth)
ring.update_data()
devices = ring.devices()
pprint(devices)
if __name__ == "__main__":
main()
Listing devices linked to your account
# All devices
devices = ring.devices()
{'chimes': [<RingChime: Downstairs>],
'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
devices = ring.devices()
for dev in list(devices['stickup_cams'] + devices['chimes'] + devices['doorbots']):
dev.update_health_data()
print('Address: %s' % dev.address)
print('Family: %s' % dev.family)
print('ID: %s' % dev.id)
print('Name: %s' % dev.name)
print('Timezone: %s' % dev.timezone)
print('Wifi Name: %s' % dev.wifi_name)
print('Wifi RSSI: %s' % dev.wifi_signal_strength)
# setting dev volume
print('Volume: %s' % dev.volume)
dev.volume = 5
print('Volume: %s' % dev.volume)
# play dev test shound
if dev.family == 'chimes':
dev.test_sound(kind = 'ding')
dev.test_sound(kind = 'motion')
# turn on lights on floodlight cam
if dev.family == 'stickup_cams' and dev.lights:
dev.lights = 'on'
Showing door bell events
devices = ring.devices()
for doorbell in devices['doorbots']:
# listing the last 15 events of any kind
for event in doorbell.history(limit=15):
print('ID: %s' % event['id'])
print('Kind: %s' % event['kind'])
print('Answered: %s' % event['answered'])
print('When: %s' % event['created_at'])
print('--' * 50)
# get a event list only the triggered by motion
events = doorbell.history(kind='motion')
Downloading the last video triggered by a ding or motion event
devices = ring.devices()
doorbell = devices['doorbots'][0]
doorbell.recording_download(
doorbell.history(limit=100, kind='ding')[0]['id'],
filename='last_ding.mp4',
override=True)
Displaying the last video capture URL
print(doorbell.recording_url(doorbell.last_recording_id))
'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'
Controlling a Light Group
groups = ring.groups()
group = groups['the-group-you-want']
print(group.lights)
# Prints True if lights are on, False if off
# Turn on lights indefinitely
group.lights = True
# Turn off lights
group.lights = False
# Turn on lights for 30 seconds
group.lights = (True, 30)
How to contribute
See our Contributing Page.
Credits && Thanks
This project was inspired and based on https://github.com/jeroenmoors/php-ring-api. Many thanks @jeroenmoors.
A guy named MadBagger at Prism19 for his initial research (http://www.prism19.com/doorbot/second-pass-and-comm-reversing/)
The creators of mitmproxy (https://mitmproxy.org/) great http and https traffic inspector
@mfussenegger for his post on mitmproxy and virtualbox https://zignar.net/2015/12/31/sniffing-vbox-traffic-mitmproxy/
To the project http://www.android-x86.org/ which allowed me to install Android on KVM.
Many thanks to Carles Pina I Estany <carles@pina.cat> for creating the python-ring-doorbell Debian Package (https://tracker.debian.org/pkg/python-ring-doorbell).
Contributing
Contributions are welcome and very appreciated!! Keep in mind that every little contribution helps, don’t matter what.
Types of Contributions
Report Bugs
Report bugs at https://github.com/tchellomello/python-ring-doorbell/issues
If you are reporting a bug, please include:
Ring product and firmware version
Steps to reproduce the issue
Anything you judge interesting for the troubleshooting
Fix Bugs
Look through the GitHub issues for bugs. Anything tagged with “bug” and “help wanted” is open to whoever wants to implement it.
Implement Features
Look through the GitHub issues for features. Anything tagged with “enhancement” and “help wanted” is open to whoever wants to implement it.
Documentation
Documentation is always good. So please feel free to add any documentation you think will help our users.
Request Features
File an issue at https://github.com/tchellomello/python-ring-doorbell/issues.
Get Started!
Ready to contribute? Here’s how to set up python-ring-doorbell for local development.
Fork the python-ring-doorbell repo on GitHub.
Clone your fork locally:
$ cd YOURDIRECTORYFORTHECODE $ git clone git@github.com:YOUR_GITHUB_USERNAME/python-ring-doorbell.git
We are using poetry for dependency management.
If you dont have poetry installed you can install it with:
$ curl -sSL https://install.python-poetry.org | python3 -
This installs Poetry in a virtual environment to isolate it from the rest of your system. Then to install python-ring-doorbell:
$ poetry install
Poetry will create a virtual environment for you and install all the requirements
If you want to be able to build the docs (not necessary unless you are working on the doc generation):
$ poetry install --extras docs
Create a branch for local development:
$ git checkout -b NAME-OF-YOUR-BUGFIX-OR-FEATURE
Now you can make your changes locally.
We are using tox for testing and linting:
$ poetry run tox -r
Commit your changes and push your branch to GitHub:
$ git add . $ git commit -m "Your detailed description of your changes." $ git push origin NAME-OF-YOUR-BUGFIX-OR-FEATURE
Submit a pull request through the GitHub website.
Thank you!!
Additional Notes
Poetry
Dependencies
Poetry is very useful at managing virtual environments and ensuring that dependencies all match up for you. It manages this with the use of the poetry.lock file which contains all the exact versions to be installed. This means that if you add any dependecies you should do it via:
$ poetry add pypi_project_name
rather than pip. This will update pyproject.toml and poetry.lock accordingly. If you install something in the virtual environment directly via pip you will need to run:
$ poetry lock --no-update
to resync the lock file but without updating all the other requirements to latest versions. To uninstall a dependency:
$ poetry remove pypi_project_name
finally if you want to add a dependency for development only:
$ poetry add --group dev pypi_project_name
Environments
Poetry creates a virtual environment for the project and you can activate the virtual environment with:
$ poetry shell
To exit the shell type exit
rather than deactivate.
However you don’t need to activate the virtual environment and you can run any command without activating it by:
$ poetry run SOME_COMMAND
It is possible to manage all this from within a virtual environment you create yourself but that requires installing poetry into the same virtual environment and this can potentially cause poetry to uninstall some of its own dependencies in certain situations. Hence the recommendation to install poetry into a seperate virtual environment of its via the install script above or pipx.
See poetry documentation for more info
Documentation
To build the docs install with the docs extra:
$ poetry install --extras docs
Then generate a Github access token (no permissions are needed) and export it as follows:
$ export CHANGELOG_GITHUB_TOKEN="«your-40-digit-github-token»"
Then build:
$ make -C html
You can add the token to your shell profile to avoid having to export it each time. (e.g., .env, ~/.bash_profile, ~/.bashrc, ~/.zshrc, etc)
Changelog
0.8.7
Released on 2024-02-06 - GitHub - PyPI
Highlights
- Support for Ring Intercoms. Many thanks to @rautsch & @andrew-rinato for initial PRs and special thanks to @cosimomeli for getting this over the line!
What's Changed
- Remove exec permissions of ring_doorbell/cli.py by @cpina in #323
- Use coveralls github action by @sdb9696 in #332
- Fix coverage over-reporting by uploading xml report by @sdb9696 in #333
- Updated Intercom Support (2024) by @cosimomeli in #330
- Make changelog autogenerated as part of CI by @sdb9696 in #335
- Migrate to ruff by @sdb9696 in #336
- Fix changelog link by @sdb9696 in #337
- Upgrade CI poetry version to 1.7.1 by @sdb9696 in #338
- Bump version to 0.8.7.dev0 by @sdb9696 in #339
- Add history to has_capability check by @sdb9696 in #342
- Bump cryptography from 41.0.5 to 41.0.6 by @dependabot in #313
- Bump jinja2 from 3.1.2 to 3.1.3 by @dependabot in #327
- Bump version to 0.8.7 by @sdb9696 in #344
New Contributors
- @cpina made their first contribution in #323
- @cosimomeli made their first contribution in #330
- @dependabot made their first contribution in #313
Full Changelog: 0.8.6...0.8.7
0.8.7.dev0
Released on 2024-02-01 - GitHub - PyPI
This is a development release to facilitate testing ring intercoms.
Highlights
- Support for Ring Intercoms. Many thanks to @rautsch & @andrew-rinato for initial PRs and special thanks to @cosimomeli for getting this over the line!
What's Changed
- Remove exec permissions of ring_doorbell/cli.py by @cpina in #323
- Use coveralls github action by @sdb9696 in #332
- Fix coverage over-reporting by uploading xml report by @sdb9696 in #333
- Updated Intercom Support (2024) by @cosimomeli in #330
- Make changelog autogenerated as part of CI by @sdb9696 in #335
- Migrate to ruff by @sdb9696 in #336
- Fix changelog link by @sdb9696 in #337
- Upgrade CI poetry version to 1.7.1 by @sdb9696 in #338
- Bump version to 0.8.7.dev0 by @sdb9696 in #339
New Contributors
- @cpina made their first contribution in #323
- @cosimomeli made their first contribution in #330
Full Changelog: 0.8.6...0.8.7.dev0
0.8.6
Released on 2024-01-25 - GitHub - PyPI
What's Changed
Breaking change to the listen subpackage api to allow the listener be configurable.
0.8.5
Released on 2023-12-21 - GitHub - PyPI
What's Changed
Full Changelog: 0.8.4...0.8.5
0.8.4
Released on 2023-12-12 - GitHub - PyPI
What's Changed
Full Changelog: 0.8.3...0.8.4
0.8.3
Released on 2023-11-27 - GitHub - PyPI
What's Changed
- fix typo in the documentation by @listentothefrog in #284
- Fix auth when token invalid & rename device_id parameters by @sdb9696 in #311
- Bump version to 0.8.3 by @sdb9696 in #312
New Contributors
- @listentothefrog made their first contribution in #284
Full Changelog: 0.8.2...0.8.3
0.8.2
Released on 2023-11-24 - GitHub - PyPI
What's Changed
0.8.1
Released on 2023-11-15 - GitHub - PyPI
What's Changed
Full Changelog: 0.8.0...0.8.1
0.8.0
Released on 2023-11-08 - GitHub - PyPI
What's Changed
This is a breaking change as consumers who currently handle oauth exceptions directly will need to handle the new exceptions instead
Full Changelog: 0.7.7...0.8.0
0.7.7
Released on 2023-10-31 - GitHub - PyPI
What's Changed
Full Changelog: 0.7.6...0.7.7
0.7.6
Released on 2023-10-25 - GitHub - PyPI
What's Changed
Full Changelog: 0.7.5...0.7.6
0.7.5
Released on 2023-10-25 - GitHub - PyPI
What's Changed
- Add tests for cli and fix issues with videos by @sdb9696 in #290
- Add motion detection cli command and improve formatting of show command by @sdb9696 in #292
- Add cli commands: devices, groups, dings and history by @sdb9696 in #293
- Add event listener for getting realtime dings by @sdb9696 in #296
Full Changelog: 0.7.4...0.7.5
0.7.4
Released on 2023-09-27 - GitHub - PyPI
What's Changed
0.7.3
Released on 2023-09-11 - GitHub - PyPI
What's Changed
0.7.2
Released on 2021-12-18 - GitHub - PyPI
What's Changed
0.7.1
Released on 2021-08-26 - GitHub - PyPI
What's Changed
0.7.0
Released on 2021-02-05 - GitHub - PyPI
New features
- #231 Support for light groups (and thus Transformers indirectly) (thanks @decompil3d!)
Fixes
- #196 Fix snapshot functionality (thanks @dshokouhi!)
- #225 Fix live stream functionality (thanks @JoeDaddy7105!)
- #228 Avoid multiple clients in list by maintaining consistent hardware ID (thanks @riptidewave93!)
- #185 Return
None
instead of0
for battery level when a device is not battery powered (thanks @balloob!) - #218 Fix snapshot again and add download option (thanks @kvntng17!)
Misc
- #224 Fix build failures (thanks @JoeDaddy7105!)
- #233 Move to GitHub Actions (thanks @decompil3d!)
0.6.2
Released on 2020-11-21 - GitHub - PyPI
- Unpin reqs even more c3e98c9
0.6.1
Released on 2020-09-28 - GitHub - PyPI
Relax requirements version pinning - 59ae9b1
0.6.0
Released on 2020-01-14 - GitHub - PyPI
Major breaking change
Ring APIs offer 1 endpoint with all device info. 1 with all health for doorbells etc. The API used to make a request from each device to the "all device" endpoint and fetch its own data.
With the new approach we now just fetch the data once and each device will fetch that data. This significantly reduces the number of requests.
See updated test.py on usage.
Changes:
- Pass a user agent to the auth class to identify your project (at request from Ring)
- For most updates, just call
ring.update_all()
. If you want health data (wifi stuff), calldevice.update_health_data()
on each device - Renamed
device.id
->device.device_id
,device.account_id
->device.id
to follow API naming. - Call
ring.update_all()
at least once before querying for devices - Querying devices now is a function
ring.devices()
instead of propertyring.devices
- Removed
ring.chimes
,ring.doorbells
,ring.stickup_cams
- Cleaned up tests with pytest fixtures
- Run Black on code to silence hound.
0.5.0
Released on 2020-01-12 - GitHub - PyPI
Breaking Change
The Auth
class no longer takes an otp_callback
but now takes an otp_code
. It raises MissingTokenError
if otp_code
is required. See the updated example. This prevents duplicate SMS messages. Thanks to @steve-gombos
Timeout has been increased from 5 to 10 seconds to give requests a bit more time (by @cyberjunky)
0.4.0
Released on 2020-01-11 - GitHub - PyPI
Major breaking change.
This release is a major breaking change to clean up the auth and follow proper OAuth2. Big thanks to @steve-gombos for this.
All authentication is now done inside Auth
. The first time you need username, password and optionally an 2-factor auth callback function. After that you have a token and that can be used.
The old cache file is no longer in use and can be removed.
Example usage (also available as test.py
):
import json
from pathlib import Path
from ring_doorbell import Ring, Auth
cache_file = Path('test_token.cache')
def token_updated(token):
cache_file.write_text(json.dumps(token))
def otp_callback():
auth_code = input("2FA code: ")
return auth_code
def main():
if cache_file.is_file():
auth = Auth(json.loads(cache_file.read_text()), token_updated)
else:
username = input("Username: ")
password = input("Password: ")
auth = Auth(None, token_updated)
auth.fetch_token(username, password, otp_callback)
ring = Ring(auth)
print(ring.devices)
if __name__ == '__main__':
main()
Version 0.2.9
Released on 2020-01-03 - GitHub - PyPI
- Fixed Compatibility with Python 2 (old-school typing syntax in docstrings); fix for OAuth.SCOPE - @ZachBenz #163
- Implemented timeouts for HTTP requests methods - @tchellomello #165
- Use auth expires_in to refresh oauth tokens. - @jeromelaban #167
- Fixed logic and simplified module imports - @tchellomello #168
Version 0.2.8
Released on 2019-12-27 - GitHub - PyPI
Quick fix to make sure the requests-oauthlib
gets installed. Made requirements.txt
and setup.py
consistent. @tchellomello - #158
Version 0.2.6
Released on 2019-12-27 - GitHub - PyPI
This release includes a properly OAuth2 handle implemented by @steve-gombos. Many thanks for all involved to make this happen!
Fix for Issue #146 #149 - @ZachBenz
Fix R1705: Unnecessary elif after return (no-else-return) #151 - @xernaj
OAuth Fixes #152 - @steve-gombos
Version 0.2.5
Released on 2019-12-20 - GitHub - PyPI
@dshokouhi - Add a couple of device kinds #137
@xernaj - Fix/oauth fail due to blocked user agent #143
Many thanks for your efforts and help!!
Version 0.2.3
Released on 2019-03-05 - GitHub - PyPI
- Add support for downloading snapshot from doorbell #108 - @MorganBulkeley
- support of externally powered new stickup cam #109 - @steveww
- Fixed pylint and test errors #115 - @tchellomello
- Support for device model name property and has capability method #116 - @jsetton
Many thanks to @MorganBulkeley @steveww @jsetton
You guys rock!!
Version 0.2.2
Released on 2018-10-29 - GitHub - PyPI
Many thanks for your first contribution @evanjd!!
Version 0.2.1
Released on 2018-06-15 - GitHub - PyPI
- Updated Ring authentication method to oauth base
Many thanks to @davglass for reporting this issue and for @rbrtio, @vickyg3, @cathcartd, and @dshokouhi for testing the fix.
Version 0.2.0
Released on 2018-05-16 - GitHub - PyPI
Changelog:
@andrewkress - only save token to disk if reuse session is true #81
@tchellomello - Getting TypeError #86
Many thanks to @andrewkress for your contribution!
Version 0.1.9
Released on 2017-11-29 - GitHub - PyPI
- Added generic method update() on the top level Ring method that refreshes the attributes for all linked devices. #75
Version 0.1.8
Released on 2017-11-22 - GitHub - PyPI
- Added the ability to check if account has the Ring subscription active since it is now enforced by Ring in order to use the methods recording_download() and recording_url() #71
Many thanks to @arsaboo for your help on testing this.
Version 0.1.7
Released on 2017-11-14 - GitHub - PyPI
- Fixes some bugs and enhancements
- Add ability to inform attribute older_than to the history() method. Thanks to @troopermax #63
- Introduced ring_cli.py script
scripts/ringcli.py -u foo -p bar --count
---------------------------------
Ring CLI
---------------------------------
Counting videos linked on your Ring account.
This may take some time....
Total videos: 384
Ding triggered: 32
Motion triggered: 340
On-Demand triggered: 12
======================
---------------------------------
Ring CLI
---------------------------------
scripts/ringcli.py -u foo -p bar --count
1/384 Downloading 2017-11-14_00.57.16+00.00_motion_answered_64880679462719.mp4
2/384 Downloading 2017-11-13_21.32.23+00.00_motion_not_answered_64880151491.mp4
======================
scripts/ringcli.py --help
usage: ringcli.py [-h] [-u USERNAME] [-p PASSWORD] [--count] [--download-all]
Ring Doorbell
optional arguments:
-h, --help show this help message and exit
-u USERNAME, --username USERNAME
username for Ring account
-p PASSWORD, --password PASSWORD
username for Ring account
--count count the number of videos on your Ring account
--download-all download all videos on your Ring account
https://github.com/tchellomello/python-ring-doorbell
Version 0.1.6
Released on 2017-10-19 - GitHub - PyPI
- Introduces support to floodlight lights and siren support. Many thanks to @jsetton for this addition
Version 0.1.5
Released on 2017-10-17 - GitHub - PyPI
- Added support to stickup and floodlight cameras. @jlippold
- Allow
history()
to return exact number of events of a given kind - Code refactored
- Added support to report wifi status. Thanks to @keeth
- Added support to play test sounds @vickyg3
Many thanks to the community and special thanks to @keeth @vickyg3 @jlippold
v0.1.4
v0.1.3
v0.1.2
Released on 2017-03-20 - GitHub - PyPI
v0.1.2
0.1.1
0.1.0
Released on 2017-02-25 - GitHub - PyPI
BREAK CHANGES
The code was refactored to allow to manipulate the objects in a better way.
In [1]: from ring_doorbell import Ring
In [2]: myring = Ring('user@email.com', 'password')
In [3]: myring.devices
Out[3]:
{'chimes': [<RingChime: Downstairs>],
'doorbells': [<RingDoorBell: Front Door>]}
In [4]: myring.chimes
Out[4]: [<RingChime: Downstairs>]
In [5]: myring.doorbells
Out[5]: [<RingDoorBell: Front Door>]
In [6]: mychime = myring.chimes[0]
In [7]: mychime.
mychime.account_id mychime.firmware mychime.linked_tree mychime.subscribed_motions
mychime.address mychime.id mychime.longitude mychime.timezone
mychime.debug mychime.kind mychime.name mychime.update
mychime.family mychime.latitude mychime.subscribed mychime.volume
In [7]: mychime.volume
Out[7]: 5
#updating volume
In [8]: mychime.volume = 200
Must be within the 0-10.
In [9]: mychime.volume = 4
In [10]: mychime.volume
Out[10]: 4
# DoorBells
In [11]: mydoorbell = myring.doorbells[0]
In [12]: mydoorbell.
mydoorbell.account_id mydoorbell.kind
mydoorbell.address mydoorbell.last_recording_id
mydoorbell.battery_life mydoorbell.latitude
mydoorbell.check_activity mydoorbell.live_streaming_json
mydoorbell.debug mydoorbell.longitude
mydoorbell.existing_doorbell_type mydoorbell.name
mydoorbell.existing_doorbell_type_duration mydoorbell.recording_download
mydoorbell.existing_doorbell_type_enabled mydoorbell.recording_url
mydoorbell.family mydoorbell.timezone
mydoorbell.firmware mydoorbell.update
mydoorbell.history mydoorbell.volume
mydoorbell.id
In [12]: mydoorbell.last_recording_id
Out[12]: 2222222221
In [14]: mydoorbell.existing_doorbell_type
Out[14]: 'Mechanical'
In [15]: mydoorbell.existing_doorbell_type_enabled
Out[15]: True
In [16]: mydoorbell.existing_doorbell_type_enabled = False
In [17]: mydoorbell.existing_doorbell_type_enabled
Out[17]: False
0.0.4
Released on 2017-02-15 - GitHub - PyPI
- Allow to filter history per doorbell or globally.