An unofficial, friendly Javascript API for ring doorbells, cameras, etc.
- Happy in node or browsers
- Promised-based
- Glosses over ring's API weirdness
- Hides http polling behind an event-driven interface
Requires a JS runtime that supports ES6 async/await or else traspilation
const RingApi = require( 'ring-api' );
// note that RingApi returns a promise - the promise resolves when you are authenticated/
// authorised and have a session ready to start interacting with your ring deviecs. This
// promise will reject if for some reason you are not able to log in
const ringApi = await RingApi( {
// note - that the email and password can also be given by setting the RING_USER
// and RING_PASSWORD environment variables. For example if you want to keep
// passwords out of your source code
email: '[email protected]',
password: 'password you use on ring.com',
// the user agent parameter has been removed since Ring have started rejecting
// unrecognised agent strings
//userAgent: 'any string',
// OPTIONAL: if true, ring-api will poll behind the scenes.
// Listening for events only works if this is on.
// True by default.
poll: true,
// OPTIONAL
// Set this if you need to run in a browser behind a proxy, for example
// to get around x-origin request restrictions. Ring don't have CORS headers.
// once set, all requests will be made relative to this value
// default is 'https://api.ring.com/clients_api'
// If running in node, you almost certainly want to leave this out
serverRoot: 'http://example.com'
} );
const logActivity = activity => console.log( 'there is a activity', activity );
ringApi.events.on('activity', logActivity);
The event will be fired on rings and motion detected. To distinguish between then, use the activity.kind property.
Where the activity object looks like:
{
kind: 'motion', // 'motion' or 'ring',
// note - id will be a string - Javascript Number can't do large integers
id: '6500907085284961754',
id_str: '6500907085284961754', // same as id
state: 'ringing',
protocol: 'sip',
doorbot_id: 3861978, // id of the device that is ringing
doorbot_description: 'Back garden',
device_kind: 'hp_cam_v1',
motion: true,
snapshot_url: '', // seems to always be blank
expires_in: 175,
now: Date, // js Date object
optimization_level: 1,
// various sip-related fields for the video:
sip_server_ip: '...',
sip_server_port: 15063,
sip_server_tls: true,
sip_session_id: '...',
sip_from: '...',
sip_to: '..',
audio_jitter_buffer_ms: 300,
video_jitter_buffer_ms: 300,
sip_endpoints: null,
sip_token: 'long hex token',
sip_ding_id: '6500907085284961754', // seems to always be the same as the id
}
// returns a promise
ringApi.devices();
Where the promise will resolve to an object like:
{
all: [ /* all your devices in one array */ ],
doorbells: [ /* array of doorbells */ ]
authorizedDoorbells: [], // other people's doorbells you are authorised for
chimes: [ /* array of chimes */ ],
cameras: [ /* array of cameras, floodlight cams, spotlight cams etc */ ] ],
baseStations: [] // presumably if you have a chime pro with the wifi hotspot built in
}
const prompt = require('node-ask').prompt;
async function lightsOnAndOff() {
const devices = await ringApi.devices();
console.log( `turning on all lights` );
// note that .lightOn() returns a promise
// with the magic of promises we can turn them all on asynchronously
await Promise.all( devices.cameras.map( c => c.lightOn() ) );
await prompt( 'all your lights are now on, hit return to turn them off' );
await Promise.all( devices.cameras.map( c => c.lightOff() ) );
console.log( 'they\'re all off again!');
};
lightsOnAndOff();
async function logMyRingHistory() {
const history = await ringApi.history();
const firstVideoUrl = await history[0].videoUrl();
console.log( 'latest video is at', firstVideoUrl );
const allRecentVideos = Promise.all( history.map( h => h.videoUrl() ) );
console.log( 'list of all recent videos:', await allRecentVideos );
};
So far this only works so far as getting the SIP details of the stream. To get this, can do:
// returns a promise that will resolve to the livestream SIP information:
devices.doorbells[0].liveStream();
async function printHealth( device ) {
const strength = (await device.health()).latest_signal_strength;
console.log( `${device.description} wifi strength is ${strength}` );
}
// asynchronously print the health of the first of each kind of device,
// without worrying about the order they are printed in:
const devices = await ringApi.devices();
printHealth( devices.doorbells[0] );
printHealth( devices.chimes[0] );
printHealth( devices.cameras[0] );
To get extended debugging info, since ring-api uses https://www.npmjs.com/package/debug you
can set the DEBUG
environment variable to ring-api
(or add ring-api to it if it is already set)
For example, to run the example script from bash with debugging you might do:
env DEBUG="$DEBUG ring-api" ./examples/example-script
- The doorbot source for getting me started
- This python API
- mitm proxy