Welcome to python-ring-doorbell’s documentation!

Python Ring Door Bell

PyPI Version Build Status Coverage Documentation Status Py Versions

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:

  1. Show your devices:

    $ ring-doorbell
    

    Or:

    $ ring-doorbell show
    
  2. List your device names (with device kind):

    $ ring-doorbell list
    
  3. Either count or download your vidoes or both:

    $ ring-doorbell videos --count --download-all
    
  4. Enable disable motion detection:

    $ ring-doorbell motion-detection --device-name "DEVICENAME" --on
    $ ring-doorbell motion-detection --device-name "DEVICENAME" --off
    
  5. Listen for push notifications like the ones sent to your phone:

    $ ring-doorbell listen
    
  6. List your ring groups:

    $ ring-doorbell groups
    
  7. Show your ding history:

    $ ring-doorbell history --device-name "Front Door"
    
  8. Show your currently active dings:

    $ ring-doorbell dings
    
  9. Query a ring api url directly:

    $ ring-doorbell raw-query --url /clients_api/dings/active
    
  10. Run ring-doorbell --help or ring-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

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.

  1. Fork the python-ring-doorbell repo on GitHub.

  2. Clone your fork locally:

    $ cd YOURDIRECTORYFORTHECODE
    $ git clone git@github.com:YOUR_GITHUB_USERNAME/python-ring-doorbell.git
    
  3. 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
    
  4. Create a branch for local development:

    $ git checkout -b NAME-OF-YOUR-BUGFIX-OR-FEATURE
    

    Now you can make your changes locally.

  5. We are using tox for testing and linting:

    $ poetry run tox -r
    
  6. 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
    
  7. 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

What's Changed

New Contributors

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

What's Changed

New Contributors

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

  • Fix history timeformat and bump to 0.8.5 by @sdb9696 in #320

Full Changelog: 0.8.4...0.8.5

0.8.4

Released on 2023-12-12 - GitHub - PyPI

What's Changed

  • Add Spotlight Cam Pro and enable motion detection for Stickup Cam by @sdb9696 in #316

Full Changelog: 0.8.3...0.8.4

0.8.3

Released on 2023-11-27 - GitHub - PyPI

What's Changed

New Contributors

Full Changelog: 0.8.2...0.8.3

0.8.2

Released on 2023-11-24 - GitHub - PyPI

What's Changed

  • Add ring devices and bump version to 0.8.2 (#310) @sdb9696
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

  • Add custom exceptions and encapsulate oauth error handling by @sdb9696 in #304

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

  • Improve stability and capabilities of realtime event listener by @sdb9696 in #300

Full Changelog: 0.7.6...0.7.7

0.7.6

Released on 2023-10-25 - GitHub - PyPI

What's Changed

  • Fix anyio dependency preventing ha install by @sdb9696 in #298

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

  • Fix and update cli (#288) @sdb9696
  • Update to pyproject.toml, poetry, and update docs to use yaml config (#287) @sdb9696
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

  • Recognize cocoa_floodlight as a floodlight kind (#255) @mwren
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 of 0 for battery level when a device is not battery powered (thanks @balloob!)
  • #218 Fix snapshot again and add download option (thanks @kvntng17!)

Misc

0.6.2

Released on 2020-11-21 - GitHub - PyPI

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), call device.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 property ring.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

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

Many thanks to @MorganBulkeley @steveww @jsetton
You guys rock!!

Version 0.2.2

Released on 2018-10-29 - GitHub - PyPI

  • Support for Spotlight Battery cameras with multiple battery bays #106 (@evanjd)

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

Released on 2017-04-30 - GitHub - PyPI

v0.1.3

Released on 2017-03-31 - GitHub - PyPI

v0.1.2

Released on 2017-03-20 - GitHub - PyPI

v0.1.2

0.1.1

Released on 2017-03-09 - GitHub - PyPI

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.
0.0.3

Released on 2017-02-15 - GitHub - PyPI

0.0.2

Released on 2017-02-15 - GitHub - PyPI

0.0.1

Released on 2017-02-15 - GitHub - PyPI