ring-client-api 11.6.0 → 11.7.0-beta.1

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
@@ -115,7 +115,9 @@ camera.getSnapshot() // returns a Promise<Buffer> of the latest snapshot from th
115
115
 
116
116
  Camera also includes the following observables:
117
117
 
118
- - `onNewDing`: this will include the sip info and ding information every time a new ding is created
118
+ - `onNewNotification`: this is triggered any time a new push notification is received
119
+ - `onActiveNotifications`: notifications received within the last minute
120
+ - `onDoorbellPressed`: this will include the sip info and ding information every time a new ding is created
119
121
  - `onActiveDings`: dings created within the last 65 seconds
120
122
  - `onDoorbellPressed`: emits a ding every time the doorbell is pressed
121
123
  - `onMotionDetected`: `true` or `false` based on `onActiveDings` containing a motion ding
package/lib/api.d.ts CHANGED
@@ -33,7 +33,7 @@ export declare class RingApi extends Subscribed {
33
33
  beamBridges: BeamBridge[];
34
34
  onvifCameras: OnvifCameraData[];
35
35
  thirdPartyGarageDoorOpeners: ThirdPartyGarageDoorOpener[];
36
- intercomHandsets: IntercomHandsetAudioData[];
36
+ intercoms: IntercomHandsetAudioData[];
37
37
  unknownDevices: UnknownDevice[];
38
38
  }>;
39
39
  private listenForDeviceUpdates;
package/lib/api.js CHANGED
@@ -15,6 +15,7 @@ const util_1 = require("./util");
15
15
  const ffmpeg_1 = require("./ffmpeg");
16
16
  const subscribed_1 = require("./subscribed");
17
17
  const push_receiver_1 = __importDefault(require("@eneris/push-receiver"));
18
+ const ring_intercom_1 = require("./ring-intercom");
18
19
  class RingApi extends subscribed_1.Subscribed {
19
20
  constructor(options) {
20
21
  super();
@@ -34,14 +35,14 @@ class RingApi extends subscribed_1.Subscribed {
34
35
  }
35
36
  }
36
37
  async fetchRingDevices() {
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
+ 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 = [], intercoms = [], thirdPartyGarageDoorOpeners = [], unknownDevices = [];
38
39
  otherDevices.forEach((device) => {
39
40
  switch (device.kind) {
40
41
  case ring_types_1.RingDeviceType.OnvifCamera:
41
42
  onvifCameras.push(device);
42
43
  break;
43
44
  case ring_types_1.RingDeviceType.IntercomHandsetAudio:
44
- intercomHandsets.push(device);
45
+ intercoms.push(device);
45
46
  break;
46
47
  case ring_types_1.RingDeviceType.ThirdPartyGarageDoorOpener:
47
48
  thirdPartyGarageDoorOpeners.push(device);
@@ -66,16 +67,16 @@ class RingApi extends subscribed_1.Subscribed {
66
67
  beamBridges,
67
68
  onvifCameras,
68
69
  thirdPartyGarageDoorOpeners,
69
- intercomHandsets,
70
+ intercoms,
70
71
  unknownDevices,
71
72
  };
72
73
  }
73
- listenForDeviceUpdates(cameras, chimes) {
74
+ listenForDeviceUpdates(cameras, chimes, intercoms) {
74
75
  const { cameraStatusPollingSeconds } = this.options;
75
76
  if (!cameraStatusPollingSeconds) {
76
77
  return;
77
78
  }
78
- const onCamerasRequestUpdate = (0, rxjs_1.merge)(...cameras.map((camera) => camera.onRequestUpdate)), onChimesRequestUpdate = (0, rxjs_1.merge)(...chimes.map((chime) => chime.onRequestUpdate)), onUpdateReceived = new rxjs_1.Subject(), onPollForStatusUpdate = cameraStatusPollingSeconds
79
+ const devices = [...cameras, ...chimes, ...intercoms], onDeviceRequestUpdate = (0, rxjs_1.merge)(...devices.map((device) => device.onRequestUpdate)), onUpdateReceived = new rxjs_1.Subject(), onPollForStatusUpdate = cameraStatusPollingSeconds
79
80
  ? onUpdateReceived.pipe((0, operators_1.debounceTime)(cameraStatusPollingSeconds * 1000))
80
81
  : rxjs_1.EMPTY, camerasById = cameras.reduce((byId, camera) => {
81
82
  byId[camera.id] = camera;
@@ -83,11 +84,14 @@ class RingApi extends subscribed_1.Subscribed {
83
84
  }, {}), chimesById = chimes.reduce((byId, chime) => {
84
85
  byId[chime.id] = chime;
85
86
  return byId;
87
+ }, {}), intercomsById = intercoms.reduce((byId, intercom) => {
88
+ byId[intercom.id] = intercom;
89
+ return byId;
86
90
  }, {});
87
91
  if (!cameras.length && !chimes.length) {
88
92
  return;
89
93
  }
90
- this.addSubscriptions((0, rxjs_1.merge)(onCamerasRequestUpdate, onChimesRequestUpdate, onPollForStatusUpdate)
94
+ this.addSubscriptions((0, rxjs_1.merge)(onDeviceRequestUpdate, onPollForStatusUpdate)
91
95
  .pipe((0, operators_1.throttleTime)(500), (0, operators_1.switchMap)(() => this.fetchRingDevices().catch(() => null)))
92
96
  .subscribe((response) => {
93
97
  onUpdateReceived.next(null);
@@ -106,6 +110,12 @@ class RingApi extends subscribed_1.Subscribed {
106
110
  chime.updateData(data);
107
111
  }
108
112
  });
113
+ response.intercoms.forEach((data) => {
114
+ const intercom = intercomsById[data.id];
115
+ if (intercom) {
116
+ intercom.updateData(data);
117
+ }
118
+ });
109
119
  }));
110
120
  if (cameraStatusPollingSeconds) {
111
121
  onUpdateReceived.next(null); // kick off polling
@@ -142,6 +152,7 @@ class RingApi extends subscribed_1.Subscribed {
142
152
  });
143
153
  pushReceiver.onNotification(({ message }) => {
144
154
  const dataJson = message.data?.gcmData;
155
+ (0, util_1.logInfo)(`Received push notification: ${dataJson}`);
145
156
  try {
146
157
  const notification = JSON.parse(dataJson);
147
158
  if (!notification.ding) {
@@ -175,19 +186,19 @@ class RingApi extends subscribed_1.Subscribed {
175
186
  });
176
187
  }
177
188
  async fetchAndBuildLocations() {
178
- const rawLocations = await this.fetchRawLocations(), { authorizedDoorbots, chimes, doorbots, allCameras, baseStations, beamBridges, } = await this.fetchRingDevices(), locationIdsWithHubs = [...baseStations, ...beamBridges].map((x) => x.location_id), cameras = allCameras.map((data) => new ring_camera_1.RingCamera(data, doorbots.includes(data) ||
189
+ const rawLocations = await this.fetchRawLocations(), { authorizedDoorbots, chimes, doorbots, allCameras, baseStations, beamBridges, intercoms, } = await this.fetchRingDevices(), locationIdsWithHubs = [...baseStations, ...beamBridges].map((x) => x.location_id), cameras = allCameras.map((data) => new ring_camera_1.RingCamera(data, doorbots.includes(data) ||
179
190
  authorizedDoorbots.includes(data) ||
180
- data.kind.startsWith('doorbell'), this.restClient, this.options.avoidSnapshotBatteryDrain || false)), ringChimes = chimes.map((data) => new ring_chime_1.RingChime(data, this.restClient)), locations = rawLocations
191
+ data.kind.startsWith('doorbell'), this.restClient, this.options.avoidSnapshotBatteryDrain || false)), ringChimes = chimes.map((data) => new ring_chime_1.RingChime(data, this.restClient)), ringIntercoms = intercoms.map((data) => new ring_intercom_1.RingIntercom(data, this.restClient)), locations = rawLocations
181
192
  .filter((location) => {
182
193
  return (!Array.isArray(this.options.locationIds) ||
183
194
  this.options.locationIds.includes(location.location_id));
184
195
  })
185
- .map((location) => new location_1.Location(location, cameras.filter((x) => x.data.location_id === location.location_id), ringChimes.filter((x) => x.data.location_id === location.location_id), {
196
+ .map((location) => new location_1.Location(location, cameras.filter((x) => x.data.location_id === location.location_id), ringChimes.filter((x) => x.data.location_id === location.location_id), ringIntercoms.filter((x) => x.data.location_id === location.location_id), {
186
197
  hasHubs: locationIdsWithHubs.includes(location.location_id),
187
198
  hasAlarmBaseStation: baseStations.some((station) => station.location_id === location.location_id),
188
199
  locationModePollingSeconds: this.options.locationModePollingSeconds,
189
200
  }, this.restClient));
190
- this.listenForDeviceUpdates(cameras, ringChimes);
201
+ this.listenForDeviceUpdates(cameras, ringChimes, ringIntercoms);
191
202
  this.registerPushReceiver(cameras).catch((e) => {
192
203
  (0, util_1.logError)(e);
193
204
  });
package/lib/index.d.ts CHANGED
@@ -3,4 +3,5 @@ export * from './location';
3
3
  export * from './ring-camera';
4
4
  export * from './ring-chime';
5
5
  export * from './ring-device';
6
+ export * from './ring-intercom';
6
7
  export * from './ring-types';
package/lib/index.js CHANGED
@@ -19,4 +19,5 @@ __exportStar(require("./location"), exports);
19
19
  __exportStar(require("./ring-camera"), exports);
20
20
  __exportStar(require("./ring-chime"), exports);
21
21
  __exportStar(require("./ring-device"), exports);
22
+ __exportStar(require("./ring-intercom"), exports);
22
23
  __exportStar(require("./ring-types"), exports);
package/lib/location.d.ts CHANGED
@@ -5,11 +5,13 @@ import { RingRestClient } from './rest-client';
5
5
  import { RingCamera } from './ring-camera';
6
6
  import { RingChime } from './ring-chime';
7
7
  import { RingDevice } from './ring-device';
8
+ import { RingIntercom } from './ring-intercom';
8
9
  import { Subscribed } from './subscribed';
9
10
  export declare class Location extends Subscribed {
10
11
  readonly locationDetails: UserLocation;
11
12
  readonly cameras: RingCamera[];
12
13
  readonly chimes: RingChime[];
14
+ readonly intercoms: RingIntercom[];
13
15
  readonly options: {
14
16
  hasHubs: boolean;
15
17
  hasAlarmBaseStation: boolean;
@@ -35,7 +37,7 @@ export declare class Location extends Subscribed {
35
37
  offlineAssets: string[];
36
38
  hasHubs: boolean;
37
39
  hasAlarmBaseStation: boolean;
38
- constructor(locationDetails: UserLocation, cameras: RingCamera[], chimes: RingChime[], options: {
40
+ constructor(locationDetails: UserLocation, cameras: RingCamera[], chimes: RingChime[], intercoms: RingIntercom[], options: {
39
41
  hasHubs: boolean;
40
42
  hasAlarmBaseStation: boolean;
41
43
  locationModePollingSeconds?: number;
package/lib/location.js CHANGED
@@ -15,11 +15,12 @@ function flattenDeviceData(data) {
15
15
  return Object.assign({}, data.general && data.general.v2, data.device && data.device.v1);
16
16
  }
17
17
  class Location extends subscribed_1.Subscribed {
18
- constructor(locationDetails, cameras, chimes, options, restClient) {
18
+ constructor(locationDetails, cameras, chimes, intercoms, options, restClient) {
19
19
  super();
20
20
  this.locationDetails = locationDetails;
21
21
  this.cameras = cameras;
22
22
  this.chimes = chimes;
23
+ this.intercoms = intercoms;
23
24
  this.options = options;
24
25
  this.restClient = restClient;
25
26
  this.seq = 1;
@@ -3,6 +3,7 @@ import { AuthTokenResponse, SessionResponse } from './ring-types';
3
3
  import { ReplaySubject } from 'rxjs';
4
4
  export declare function clientApi(path: string): string;
5
5
  export declare function deviceApi(path: string): string;
6
+ export declare function commandsApi(path: string): string;
6
7
  export declare function appApi(path: string): string;
7
8
  export interface ExtendedResponse {
8
9
  responseTimestamp: number;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.RingRestClient = exports.appApi = exports.deviceApi = exports.clientApi = void 0;
6
+ exports.RingRestClient = exports.appApi = exports.commandsApi = exports.deviceApi = exports.clientApi = void 0;
7
7
  const got_1 = __importDefault(require("got"));
8
8
  const util_1 = require("./util");
9
9
  const rxjs_1 = require("rxjs");
@@ -18,7 +18,7 @@ const defaultRequestOptions = {
18
18
  7061: 'ASSET_CELL_BACKUP',
19
19
  7062: 'UPDATING',
20
20
  7063: 'MAINTENANCE',
21
- }, clientApiBaseUrl = 'https://api.ring.com/clients_api/', deviceApiBaseUrl = 'https://api.ring.com/devices/v1/', appApiBaseUrl = 'https://app.ring.com/api/v1/', apiVersion = 11;
21
+ }, clientApiBaseUrl = 'https://api.ring.com/clients_api/', deviceApiBaseUrl = 'https://api.ring.com/devices/v1/', commandsApiBaseUrl = 'https://api.ring.com/commands/v1/', appApiBaseUrl = 'https://app.ring.com/api/v1/', apiVersion = 11;
22
22
  function clientApi(path) {
23
23
  return clientApiBaseUrl + path;
24
24
  }
@@ -27,6 +27,10 @@ function deviceApi(path) {
27
27
  return deviceApiBaseUrl + path;
28
28
  }
29
29
  exports.deviceApi = deviceApi;
30
+ function commandsApi(path) {
31
+ return commandsApiBaseUrl + path;
32
+ }
33
+ exports.commandsApi = commandsApi;
30
34
  function appApi(path) {
31
35
  return appApiBaseUrl + path;
32
36
  }
@@ -0,0 +1,20 @@
1
+ import { IntercomHandsetAudioData } from './ring-types';
2
+ import { RingRestClient } from './rest-client';
3
+ import { BehaviorSubject, Subject } from 'rxjs';
4
+ export declare class RingIntercom {
5
+ private initialData;
6
+ private restClient;
7
+ id: number;
8
+ deviceType: import("./ring-types").RingDeviceType.IntercomHandsetAudio;
9
+ onData: BehaviorSubject<IntercomHandsetAudioData>;
10
+ onRequestUpdate: Subject<unknown>;
11
+ onBatteryLevel: import("rxjs").Observable<number | null>;
12
+ constructor(initialData: IntercomHandsetAudioData, restClient: RingRestClient);
13
+ updateData(update: IntercomHandsetAudioData): void;
14
+ requestUpdate(): void;
15
+ get data(): IntercomHandsetAudioData;
16
+ get name(): string;
17
+ get isOffline(): boolean;
18
+ get batteryLevel(): number | null;
19
+ unlock(): Promise<import("./rest-client").ExtendedResponse>;
20
+ }
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RingIntercom = void 0;
4
+ const rest_client_1 = require("./rest-client");
5
+ const rxjs_1 = require("rxjs");
6
+ const operators_1 = require("rxjs/operators");
7
+ const ring_camera_1 = require("./ring-camera");
8
+ class RingIntercom {
9
+ constructor(initialData, restClient) {
10
+ this.initialData = initialData;
11
+ this.restClient = restClient;
12
+ this.onRequestUpdate = new rxjs_1.Subject();
13
+ this.id = this.initialData.id;
14
+ this.deviceType = this.initialData.kind;
15
+ this.onData = new rxjs_1.BehaviorSubject(this.initialData);
16
+ this.onBatteryLevel = this.onData.pipe((0, operators_1.map)((data) => (0, ring_camera_1.getBatteryLevel)(data)), (0, operators_1.distinctUntilChanged)());
17
+ }
18
+ updateData(update) {
19
+ this.onData.next(update);
20
+ }
21
+ requestUpdate() {
22
+ this.onRequestUpdate.next(null);
23
+ }
24
+ get data() {
25
+ return this.onData.getValue();
26
+ }
27
+ get name() {
28
+ return this.data.description;
29
+ }
30
+ get isOffline() {
31
+ return this.data.alerts.connection === 'offline';
32
+ }
33
+ get batteryLevel() {
34
+ return (0, ring_camera_1.getBatteryLevel)(this.data);
35
+ }
36
+ unlock() {
37
+ return this.restClient.request({
38
+ method: 'PUT',
39
+ url: (0, rest_client_1.commandsApi)(`devices/${this.id}/device_rpc`),
40
+ json: {
41
+ command_name: 'device_rpc',
42
+ request: {
43
+ jsonrpc: '2.0',
44
+ method: 'unlock_door',
45
+ params: {
46
+ door_id: this.id,
47
+ user_id: 0,
48
+ },
49
+ },
50
+ },
51
+ });
52
+ }
53
+ }
54
+ exports.RingIntercom = RingIntercom;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ring-client-api",
3
- "version": "11.6.0",
3
+ "version": "11.7.0-beta.1",
4
4
  "description": "Unofficial API for Ring doorbells, cameras, security alarm system and smart lighting",
5
5
  "main": "lib/index.js",
6
6
  "exports": {