ring-client-api 11.4.0-beta.4 → 11.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,7 +9,7 @@ This is an unofficial TypeScript api for [Ring Doorbells](https://shop.ring.com/
9
9
  the [Ring Alarm System](https://shop.ring.com/pages/security-system),
10
10
  [Ring Smart Lighting](https://shop.ring.com/pages/smart-lighting),
11
11
  and third party devices that connect to the Ring Alarm System.
12
- Built to support the [homebridge-ring Plugin](./homebridge)
12
+ Built to support the [homebridge-ring Plugin](../homebridge-ring)
13
13
 
14
14
  ## Installation
15
15
 
package/lib/api.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { RefreshTokenAuth, RingRestClient, SessionOptions } from './rest-client';
2
2
  import { Location } from './location';
3
- import { BaseStation, BeamBridge, CameraData, ChimeData, ProfileResponse, UserLocation } from './ring-types';
4
- import { RingCamera } from './ring-camera';
3
+ import { BaseStation, BeamBridge, CameraData, ChimeData, IntercomHandsetAudioData, OnvifCameraData, ProfileResponse, ThirdPartyGarageDoorOpener, UnknownDevice, UserLocation } from './ring-types';
4
+ import { AnyCameraData, RingCamera } from './ring-camera';
5
5
  import { Subscribed } from './subscribed';
6
6
  export interface RingApiOptions extends SessionOptions {
7
7
  locationIds?: string[];
@@ -28,9 +28,13 @@ export declare class RingApi extends Subscribed {
28
28
  chimes: ChimeData[];
29
29
  authorizedDoorbots: CameraData[];
30
30
  stickupCams: CameraData[];
31
- allCameras: CameraData[];
31
+ allCameras: AnyCameraData[];
32
32
  baseStations: BaseStation[];
33
33
  beamBridges: BeamBridge[];
34
+ onvifCameras: OnvifCameraData[];
35
+ thirdPartyGarageDoorOpeners: ThirdPartyGarageDoorOpener[];
36
+ intercomHandsets: IntercomHandsetAudioData[];
37
+ unknownDevices: UnknownDevice[];
34
38
  }>;
35
39
  private listenForDeviceUpdates;
36
40
  registerPushReceiver(cameras: RingCamera[]): Promise<void>;
package/lib/api.js CHANGED
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.RingApi = void 0;
7
7
  const rest_client_1 = require("./rest-client");
8
8
  const location_1 = require("./location");
9
+ const ring_types_1 = require("./ring-types");
9
10
  const ring_camera_1 = require("./ring-camera");
10
11
  const ring_chime_1 = require("./ring-chime");
11
12
  const rxjs_1 = require("rxjs");
@@ -33,15 +34,40 @@ class RingApi extends subscribed_1.Subscribed {
33
34
  }
34
35
  }
35
36
  async fetchRingDevices() {
36
- const { doorbots, chimes, authorized_doorbots: authorizedDoorbots, stickup_cams: stickupCams, base_stations: baseStations, beams_bridges: beamBridges, } = await this.restClient.request({ url: (0, rest_client_1.clientApi)('ring_devices') });
37
+ const { doorbots, chimes, authorized_doorbots: authorizedDoorbots, stickup_cams: stickupCams, base_stations: baseStations, beams_bridges: beamBridges, other: otherDevices, } = await this.restClient.request({ url: (0, rest_client_1.clientApi)('ring_devices') }), onvifCameras = [], intercomHandsets = [], thirdPartyGarageDoorOpeners = [], unknownDevices = [];
38
+ otherDevices.forEach((device) => {
39
+ switch (device.kind) {
40
+ case ring_types_1.RingDeviceType.OnvifCamera:
41
+ onvifCameras.push(device);
42
+ break;
43
+ case ring_types_1.RingDeviceType.IntercomHandsetAudio:
44
+ intercomHandsets.push(device);
45
+ break;
46
+ case ring_types_1.RingDeviceType.ThirdPartyGarageDoorOpener:
47
+ thirdPartyGarageDoorOpeners.push(device);
48
+ break;
49
+ default:
50
+ unknownDevices.push(device);
51
+ break;
52
+ }
53
+ });
37
54
  return {
38
55
  doorbots,
39
56
  chimes,
40
57
  authorizedDoorbots,
41
58
  stickupCams,
42
- allCameras: doorbots.concat(stickupCams, authorizedDoorbots),
59
+ allCameras: [
60
+ ...doorbots,
61
+ ...stickupCams,
62
+ ...authorizedDoorbots,
63
+ ...onvifCameras,
64
+ ],
43
65
  baseStations,
44
66
  beamBridges,
67
+ onvifCameras,
68
+ thirdPartyGarageDoorOpeners,
69
+ intercomHandsets,
70
+ unknownDevices,
45
71
  };
46
72
  }
47
73
  listenForDeviceUpdates(cameras, chimes) {
File without changes
@@ -1,5 +1,5 @@
1
1
  /// <reference types="node" />
2
- import { CameraData, CameraDeviceSettingsData, CameraEventOptions, CameraEventResponse, CameraHealth, HistoryOptions, PeriodicFootageResponse, PushNotification, VideoSearchResponse } from './ring-types';
2
+ import { CameraData, CameraDeviceSettingsData, CameraEventOptions, CameraEventResponse, CameraHealth, HistoryOptions, PeriodicFootageResponse, PushNotification, VideoSearchResponse, OnvifCameraData, RingCameraKind } from './ring-types';
3
3
  import { RingRestClient } from './rest-client';
4
4
  import { BehaviorSubject, Subject } from 'rxjs';
5
5
  import { DeepPartial } from './util';
@@ -7,6 +7,7 @@ import { Subscribed } from './subscribed';
7
7
  import { FfmpegOptions, StreamingSession } from './streaming/streaming-session';
8
8
  import { StreamingConnectionOptions } from './streaming/streaming-connection-base';
9
9
  import { SimpleWebRtcSession } from './streaming/simple-webrtc-session';
10
+ export declare type AnyCameraData = CameraData | OnvifCameraData;
10
11
  export declare function getBatteryLevel(data: Pick<CameraData, 'battery_life' | 'battery_life_2'> & {
11
12
  health?: Partial<CameraData['health']>;
12
13
  }): number | null;
@@ -19,9 +20,9 @@ export declare class RingCamera extends Subscribed {
19
20
  private restClient;
20
21
  private avoidSnapshotBatteryDrain;
21
22
  id: number;
22
- deviceType: import("./ring-types").RingCameraKind;
23
+ deviceType: RingCameraKind.onvif_camera | Omit<RingCameraKind, RingCameraKind.onvif_camera>;
23
24
  model: string;
24
- onData: BehaviorSubject<CameraData>;
25
+ onData: BehaviorSubject<AnyCameraData>;
25
26
  hasLight: boolean;
26
27
  hasSiren: boolean;
27
28
  onRequestUpdate: Subject<unknown>;
@@ -32,10 +33,10 @@ export declare class RingCamera extends Subscribed {
32
33
  onMotionStarted: import("rxjs").Observable<null>;
33
34
  onBatteryLevel: import("rxjs").Observable<number | null>;
34
35
  onInHomeDoorbellStatus: import("rxjs").Observable<boolean>;
35
- constructor(initialData: CameraData, isDoorbot: boolean, restClient: RingRestClient, avoidSnapshotBatteryDrain: boolean);
36
- updateData(update: CameraData): void;
36
+ constructor(initialData: AnyCameraData, isDoorbot: boolean, restClient: RingRestClient, avoidSnapshotBatteryDrain: boolean);
37
+ updateData(update: AnyCameraData): void;
37
38
  requestUpdate(): void;
38
- get data(): CameraData;
39
+ get data(): AnyCameraData;
39
40
  get name(): string;
40
41
  get activeNotifications(): PushNotification[];
41
42
  get latestNotification(): PushNotification | undefined;
@@ -77,11 +77,18 @@ class RingCamera extends subscribed_1.Subscribed {
77
77
  this.fetchingSnapshot = false;
78
78
  this.id = this.initialData.id;
79
79
  this.deviceType = this.initialData.kind;
80
- this.model = ring_types_1.RingCameraModel[this.initialData.kind] || 'Unknown Model';
80
+ this.model =
81
+ ring_types_1.RingCameraModel[this.initialData.kind] ||
82
+ 'Unknown Model';
81
83
  this.onData = new rxjs_1.BehaviorSubject(this.initialData);
82
84
  this.hasLight = this.initialData.led_status !== undefined;
83
85
  this.hasSiren = this.initialData.siren_status !== undefined;
84
- this.onBatteryLevel = this.onData.pipe((0, operators_1.map)(getBatteryLevel), (0, operators_1.distinctUntilChanged)());
86
+ this.onBatteryLevel = this.onData.pipe((0, operators_1.map)((data) => {
87
+ if (!('battery_level' in data)) {
88
+ return null;
89
+ }
90
+ return getBatteryLevel(data);
91
+ }), (0, operators_1.distinctUntilChanged)());
85
92
  this.onInHomeDoorbellStatus = this.onData.pipe((0, operators_1.map)(({ settings: { chime_settings } }) => {
86
93
  return Boolean(chime_settings?.enable);
87
94
  }), (0, operators_1.distinctUntilChanged)());
@@ -118,6 +125,9 @@ class RingCamera extends subscribed_1.Subscribed {
118
125
  return notifications[notifications.length - 1];
119
126
  }
120
127
  get batteryLevel() {
128
+ if (!('battery_level' in this.data)) {
129
+ return null;
130
+ }
121
131
  return getBatteryLevel(this.data);
122
132
  }
123
133
  get hasBattery() {
@@ -127,7 +137,10 @@ class RingCamera extends subscribed_1.Subscribed {
127
137
  return this.data.alerts.battery === 'low';
128
138
  }
129
139
  get isCharging() {
130
- return this.initialData.external_connection;
140
+ if (!('external_connection' in this.data)) {
141
+ return false;
142
+ }
143
+ return this.data.external_connection;
131
144
  }
132
145
  get operatingOnBattery() {
133
146
  return this.hasBattery && this.data.settings.power_mode !== 'wired';
File without changes
@@ -39,7 +39,10 @@ export declare enum RingDeviceType {
39
39
  ZWaveAdapter = "adapter.zwave",
40
40
  ZWaveExtender = "range-extender.zwave",
41
41
  PanicButton = "security-panic",
42
- UnknownZWave = "unknown.zwave"
42
+ UnknownZWave = "unknown.zwave",
43
+ OnvifCamera = "onvif_camera",
44
+ ThirdPartyGarageDoorOpener = "third_party_gdo",
45
+ IntercomHandsetAudio = "intercom_handset_audio"
43
46
  }
44
47
  export declare enum RingDeviceCategory {
45
48
  Outlets = 1,
@@ -88,7 +91,8 @@ export declare enum RingCameraKind {
88
91
  cocoa_camera = "cocoa_camera",
89
92
  cocoa_doorbell = "cocoa_doorbell",
90
93
  cocoa_floodlight = "cocoa_floodlight",
91
- stickup_cam_mini = "stickup_cam_mini"
94
+ stickup_cam_mini = "stickup_cam_mini",
95
+ onvif_camera = "onvif_camera"
92
96
  }
93
97
  export declare const RingCameraModel: {
94
98
  readonly [P in RingCameraKind]: string;
@@ -329,23 +333,34 @@ export declare enum DoorbellType {
329
333
  Digital = 1,
330
334
  None = 2
331
335
  }
332
- export interface CameraData {
333
- id: number;
336
+ export interface BaseCameraData {
337
+ alerts: {
338
+ connection: 'online' | 'offline' | string;
339
+ battery?: 'low' | string;
340
+ ota_status?: 'timeout' | string;
341
+ };
342
+ created_at: string;
343
+ deactivated_at: null | string;
334
344
  description: string;
335
345
  device_id: string;
336
- time_zone: string;
337
- subscribed: boolean;
338
- subscribed_motions: boolean;
339
- battery_life: number | string | null;
340
- battery_life_2?: number | string | null;
341
- battery_voltage?: number;
342
- battery_voltage_2?: number;
343
- external_connection: boolean;
344
- firmware_version: Firmware;
345
- kind: RingCameraKind;
346
- latitude: number;
347
- longitude: number;
348
- address: string;
346
+ features: {
347
+ motions_enabled: boolean;
348
+ show_recordings: boolean;
349
+ advanced_motion_enabled: boolean;
350
+ people_only_enabled: boolean;
351
+ shadow_correction_enabled: boolean;
352
+ motion_message_enabled: boolean;
353
+ night_vision_enabled: boolean;
354
+ };
355
+ id: number;
356
+ is_sidewalk_gateway: boolean;
357
+ location_id: string;
358
+ motion_snooze: null | {
359
+ scheduled: boolean;
360
+ };
361
+ night_mode_status: 'unknown' | 'true' | 'false';
362
+ owned: boolean;
363
+ ring_net_id: null;
349
364
  settings: {
350
365
  enable_vod: boolean | 1;
351
366
  motion_zones: {
@@ -397,26 +412,9 @@ export interface CameraData {
397
412
  ring_media_server_enabled: boolean;
398
413
  };
399
414
  };
400
- features: {
401
- motions_enabled: boolean;
402
- show_recordings: boolean;
403
- advanced_motion_enabled: boolean;
404
- people_only_enabled: boolean;
405
- shadow_correction_enabled: boolean;
406
- motion_message_enabled: boolean;
407
- night_vision_enabled: boolean;
408
- };
409
- owned: boolean;
410
- alerts: {
411
- connection: 'online' | 'offline' | string;
412
- battery?: 'low' | string;
413
- };
414
- motion_snooze: {
415
- scheduled: boolean;
416
- };
417
- stolen: boolean;
418
- location_id: string;
419
- ring_id: null;
415
+ subscribed: boolean;
416
+ subscribed_motions: boolean;
417
+ time_zone: string;
420
418
  motion_detection_enabled?: boolean;
421
419
  camera_location_indoor?: boolean;
422
420
  facing_window?: boolean;
@@ -428,7 +426,6 @@ export interface CameraData {
428
426
  last_name: string;
429
427
  };
430
428
  led_status?: 'on' | 'off';
431
- night_mode_status: 'true' | 'false';
432
429
  ring_cam_light_installed?: 'true' | 'false';
433
430
  ring_cam_setup_flow?: 'floodlight';
434
431
  siren_status?: {
@@ -476,6 +473,144 @@ export interface CameraData {
476
473
  ptz_connected?: 'penguin';
477
474
  };
478
475
  }
476
+ export interface CameraData extends BaseCameraData {
477
+ kind: Omit<RingCameraKind, RingCameraKind.onvif_camera>;
478
+ address: string;
479
+ battery_life: number | string | null;
480
+ battery_life_2?: number | string | null;
481
+ battery_voltage?: number;
482
+ battery_voltage_2?: number;
483
+ external_connection: boolean;
484
+ firmware_version: Firmware;
485
+ latitude: number;
486
+ longitude: number;
487
+ ring_id: null;
488
+ stolen: boolean;
489
+ }
490
+ export interface OnvifCameraData extends BaseCameraData {
491
+ kind: RingCameraKind.onvif_camera;
492
+ metadata: {
493
+ legacy_fw_migrated: boolean;
494
+ imported_from_amazon: boolean;
495
+ is_sidewalk_gateway: boolean;
496
+ third_party_manufacturer: string;
497
+ third_party_model: string;
498
+ third_party_dsn: string;
499
+ third_party_properties: {
500
+ amzn_dsn: string;
501
+ uuid: string;
502
+ };
503
+ };
504
+ owner_id: number;
505
+ updated_at: string;
506
+ }
507
+ export interface ThirdPartyGarageDoorOpener {
508
+ id: number;
509
+ kind: RingDeviceType.ThirdPartyGarageDoorOpener;
510
+ description: string;
511
+ location_id: string;
512
+ owner_id: number;
513
+ hardware_id: string;
514
+ created_at: string;
515
+ updated_at: string;
516
+ role: 'owner' | string;
517
+ metadata: {
518
+ is_sidewalk_gateway: boolean;
519
+ third_party_manufacturer: string;
520
+ third_party_model: string;
521
+ third_party_properties: {
522
+ key_access_point_associated: 'true' | 'false';
523
+ };
524
+ integration_type: 'Key by Amazon' | string;
525
+ };
526
+ ring_net_id: null;
527
+ is_sidewalk_gateway: boolean;
528
+ }
529
+ export interface IntercomHandsetAudioData {
530
+ id: number;
531
+ description: string;
532
+ device_id: string;
533
+ kind: RingDeviceType.IntercomHandsetAudio;
534
+ function: {
535
+ name: null;
536
+ };
537
+ settings: {
538
+ show_recordings: boolean;
539
+ recording_ttl: number;
540
+ recording_enabled: boolean;
541
+ keep_alive: null;
542
+ chime_settings: {
543
+ type: number;
544
+ enable: boolean;
545
+ duration: number;
546
+ };
547
+ intercom_settings: {
548
+ predecessor: string;
549
+ config: string;
550
+ ring_to_open: boolean;
551
+ intercom_type: 'DF' | string;
552
+ unlock_mode: number;
553
+ replication: number;
554
+ };
555
+ keep_alive_auto: number;
556
+ doorbell_volume: number;
557
+ enable_chime: number;
558
+ theft_alarm_enable: number;
559
+ use_cached_domain: number;
560
+ use_server_ip: number;
561
+ server_domain: 'fw.ring.com' | string;
562
+ server_ip: null;
563
+ enable_log: number;
564
+ forced_keep_alive: null;
565
+ mic_volume: number;
566
+ voice_volume: number;
567
+ };
568
+ features: {
569
+ motion_zone_recommendation: boolean;
570
+ motions_enabled: boolean;
571
+ show_recordings: boolean;
572
+ show_vod_settings: boolean;
573
+ rich_notifications_eligible: boolean;
574
+ show_24x7_lite: boolean;
575
+ show_offline_motion_events: boolean;
576
+ cfes_eligible: boolean;
577
+ sheila_camera_eligible: null | boolean;
578
+ sheila_camera_processing_eligible: null | boolean;
579
+ chime_auto_detect_capable: boolean;
580
+ };
581
+ owned: boolean;
582
+ owner: {
583
+ id: number;
584
+ first_name: string;
585
+ last_name: string;
586
+ email: string;
587
+ };
588
+ alerts: {
589
+ connection: 'online' | 'offline' | string;
590
+ ota_status?: 'no_ota' | string;
591
+ };
592
+ firmware_version: 'Up to Date' | string;
593
+ location_id: string;
594
+ time_zone: string;
595
+ created_at: string;
596
+ ring_net_id: null;
597
+ is_sidewalk_gateway: boolean;
598
+ subscribed: boolean;
599
+ deactivated_at: null | string;
600
+ battery_life: string;
601
+ metadata: {
602
+ ethernet: boolean;
603
+ legacy_fw_migrated: boolean;
604
+ imported_from_amazon: boolean;
605
+ is_sidewalk_gateway: boolean;
606
+ key_access_point_associated: boolean;
607
+ };
608
+ }
609
+ export interface UnknownDevice {
610
+ id: number;
611
+ kind: unknown;
612
+ description: string;
613
+ }
479
614
  export interface CvDetectionType {
480
615
  enabled: boolean;
481
616
  mode: string;
package/lib/ring-types.js CHANGED
@@ -44,6 +44,9 @@ var RingDeviceType;
44
44
  RingDeviceType["ZWaveExtender"] = "range-extender.zwave";
45
45
  RingDeviceType["PanicButton"] = "security-panic";
46
46
  RingDeviceType["UnknownZWave"] = "unknown.zwave";
47
+ RingDeviceType["OnvifCamera"] = "onvif_camera";
48
+ RingDeviceType["ThirdPartyGarageDoorOpener"] = "third_party_gdo";
49
+ RingDeviceType["IntercomHandsetAudio"] = "intercom_handset_audio";
47
50
  })(RingDeviceType = exports.RingDeviceType || (exports.RingDeviceType = {}));
48
51
  // eslint-disable-next-line no-shadow
49
52
  var RingDeviceCategory;
@@ -97,6 +100,7 @@ var RingCameraKind;
97
100
  RingCameraKind["cocoa_doorbell"] = "cocoa_doorbell";
98
101
  RingCameraKind["cocoa_floodlight"] = "cocoa_floodlight";
99
102
  RingCameraKind["stickup_cam_mini"] = "stickup_cam_mini";
103
+ RingCameraKind["onvif_camera"] = "onvif_camera";
100
104
  })(RingCameraKind = exports.RingCameraKind || (exports.RingCameraKind = {}));
101
105
  exports.RingCameraModel = {
102
106
  doorbot: 'Doorbell',
@@ -127,6 +131,7 @@ exports.RingCameraModel = {
127
131
  cocoa_doorbell: 'Doorbell Gen 2',
128
132
  cocoa_floodlight: 'Floodlight Cam Plus',
129
133
  stickup_cam_mini: 'Indoor Cam',
134
+ onvif_camera: 'ONVIF Camera',
130
135
  };
131
136
  exports.allAlarmStates = [
132
137
  'burglar-alarm',
@@ -104,6 +104,13 @@ class RingEdgeConnection extends streaming_connection_base_1.StreamingConnection
104
104
  return;
105
105
  case 'pong':
106
106
  return;
107
+ case 'notification':
108
+ const { text } = message.body;
109
+ if (text === 'PeerConnectionState::kConnecting' ||
110
+ text === 'PeerConnectionState::kConnected') {
111
+ return;
112
+ }
113
+ break;
107
114
  case 'close':
108
115
  (0, util_1.logError)('Video stream closed');
109
116
  (0, util_1.logError)(message.body);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ring-client-api",
3
- "version": "11.4.0-beta.4",
3
+ "version": "11.5.0",
4
4
  "description": "Unofficial API for Ring doorbells, cameras, security alarm system and smart lighting",
5
5
  "main": "lib/index.js",
6
6
  "exports": {
@@ -10,7 +10,7 @@
10
10
  "./rest-client": "./lib/rest-client.js"
11
11
  },
12
12
  "scripts": {
13
- "build": "rm -rf lib && tsc --declaration --excludeDirectories test",
13
+ "build": "rm -rf lib && tsc --declaration && chmod +x ./lib/ring-*-cli.js",
14
14
  "lint": "eslint . --ext .ts",
15
15
  "test": "jest",
16
16
  "test:watch": "jest --watch"
@@ -1 +0,0 @@
1
- export {};
@@ -1,25 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const index_1 = require("../index");
4
- describe('Ring Camera', () => {
5
- describe('battery level', () => {
6
- it('should handle string battery life', () => {
7
- expect((0, index_1.getBatteryLevel)({
8
- battery_life: '49',
9
- })).toEqual(49);
10
- });
11
- it('should handle null battery life', () => {
12
- expect((0, index_1.getBatteryLevel)({ battery_life: null })).toEqual(null);
13
- });
14
- it('should handle right battery only', () => {
15
- expect((0, index_1.getBatteryLevel)({ battery_life: null, battery_life_2: 24 })).toEqual(24);
16
- });
17
- it('should handle left battery only', () => {
18
- expect((0, index_1.getBatteryLevel)({ battery_life: 76, battery_life_2: null })).toEqual(76);
19
- });
20
- it('should handle dual batteries', () => {
21
- expect((0, index_1.getBatteryLevel)({ battery_life: '92', battery_life_2: 84 })).toEqual(84);
22
- expect((0, index_1.getBatteryLevel)({ battery_life: '92', battery_life_2: 100 })).toEqual(92);
23
- });
24
- });
25
- });