ring-client-api 10.0.0 → 11.0.2-beta.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/CHANGELOG.md CHANGED
@@ -2,8 +2,72 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [11.0.2-beta.0](https://github.com/dgreif/ring/compare/v11.0.1...v11.0.2-beta.0) (2022-05-21)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * handle pcmu audio streams for incoming audio ([06f6ff1](https://github.com/dgreif/ring/commit/06f6ff10ce7c8f2b05b1a12b8f244daa200fa9ab)), closes [#930](https://github.com/dgreif/ring/issues/930)
11
+
12
+ ### [11.0.1](https://github.com/dgreif/ring/compare/v11.0.0...v11.0.1) (2022-05-17)
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * update webrtc package ([63d003c](https://github.com/dgreif/ring/commit/63d003c1517c158663d230e6daf911c7fc765c55)), closes [#930](https://github.com/dgreif/ring/issues/930)
18
+
19
+ ## [11.0.0](https://github.com/dgreif/ring/compare/v10.0.0...v11.0.0) (2022-05-08)
20
+
21
+
22
+ ### ⚠ BREAKING CHANGES
23
+
24
+ * Node 12/14 are no longer supported, please update to node 16. A dependency added in v10 did not support node 12/14, which is why I'm officially dropping support now.
25
+
26
+ ### Bug Fixes
27
+
28
+ * officially drop node 12 & 14 support ([3d65c77](https://github.com/dgreif/ring/commit/3d65c77fab9399f29d4fb15d0e87dfe60f3db40f)), closes [#926](https://github.com/dgreif/ring/issues/926)
29
+
5
30
  ## [10.0.0](https://github.com/dgreif/ring/compare/v10.0.0-beta.8...v10.0.0) (2022-05-08)
6
31
 
32
+ ### Features
33
+
34
+ * support ring edge camera streaming ([b30d99f](https://github.com/dgreif/ring/commit/b30d99f9ffaa1fabc1d09b830cf8ba0e69fe84a0))
35
+ * receive dings from push notifications ([c9f130f](https://github.com/dgreif/ring/commit/c9f130f0f1f21b6db2454d611641ef7c29c7e620))
36
+ * **homebridge:** faster snapshots from camera notifications ([7fc821b](https://github.com/dgreif/ring/commit/7fc821b71013c36314d6d428b8152e631ea4b497))
37
+ * add `cocoa_floodlight` ([c7a7e9c](https://github.com/dgreif/ring/commit/c7a7e9cabf45ec6579a7d9a1033fa01930923243)), closes [#893](https://github.com/dgreif/ring/issues/893)
38
+ * **api:** simple webrtc session ([bac7a49](https://github.com/dgreif/ring/commit/bac7a49178bb5a22914e53fda91c0fdd522b4a74))
39
+ * **api:** allow custom peer connections for streaming ([3e9e784](https://github.com/dgreif/ring/commit/3e9e78458afe2849a8c414a6b50eec8718bf77cc))
40
+ * **api:** convert ding handling to use notifications ([5b62318](https://github.com/dgreif/ring/commit/5b62318b6a71bcc224a2b29cbd6e21162fc9891e))
41
+ * **api:** update notification types ([15d3e7e](https://github.com/dgreif/ring/commit/15d3e7e40744e2c3ed48140fa94fbc0c6f9d4088))
42
+
43
+ ### Bug Fixes
44
+
45
+ * **homebridge:** add `name` option to schema so that it won't be removed by config-ui ([af6755a](https://github.com/dgreif/ring/commit/af6755aa09cf4aaad0963499ad0d1afd0beeab96)), closes [#870](https://github.com/dgreif/ring/issues/870)
46
+ * include device name when setting push token ([2466a25](https://github.com/dgreif/ring/commit/2466a2519341771d8c79557b5a61c339f31eddf6)), closes [#920](https://github.com/dgreif/ring/issues/920)
47
+ * npm audit fix ([c1fe605](https://github.com/dgreif/ring/commit/c1fe6051e46dac92b00bb2fd1202746fd576b15a))
48
+ * remove doorbell press simulation options ([e1d1225](https://github.com/dgreif/ring/commit/e1d12257b2acbdafdc6995a53d9052822c8a3460))
49
+ * update dependencies ([4b25595](https://github.com/dgreif/ring/commit/4b255950bcfbf94486192d55cfc90d966cd79d24))
50
+ * **cameras:** wait to activate session until connected ([9a49389](https://github.com/dgreif/ring/commit/9a49389aa434cc5df4cfb4ba039bad0f2a30f756))
51
+ * handle push notification for unknown camera ([cc8fc2a](https://github.com/dgreif/ring/commit/cc8fc2a6377084515d3b169a85644b2a2b13235e))
52
+ * hide ECONNRESET errors from push receiver ([1196d96](https://github.com/dgreif/ring/commit/1196d96e67e26952f18dfbe75103419a7261bd1e))
53
+ * update dependencies ([4b97fb3](https://github.com/dgreif/ring/commit/4b97fb33623248147997944bab3de4ea3c2e5ee2))
54
+ * update uuid namespace to force new client sessions ([bc3e943](https://github.com/dgreif/ring/commit/bc3e9432c41dda085845f72f332dcf480c92475d))
55
+ * separate streaming setup for ring edge vs regular cameras ([6281cf9](https://github.com/dgreif/ring/commit/6281cf966de12dcea8acc6516cd9079634734dec))
56
+ * **api:** allow snapshot to be force-updated based on options ([fa0212a](https://github.com/dgreif/ring/commit/fa0212ab539cc268f8000c5b9ea39a00799cb8d7))
57
+ * drop support for node 10 ([509d556](https://github.com/dgreif/ring/commit/509d55670b3dd4efee1be968618f6bc6b142ae2b))
58
+ * **homebridge:** increase minimum homebridge version to 1.4.0 ([d5be74a](https://github.com/dgreif/ring/commit/d5be74aae45a3e353bcaba4fc42fa25c6b2fa37e))
59
+ * remove sip calling in favor of live calls ([7aeae37](https://github.com/dgreif/ring/commit/7aeae37e6e93b68747e14983cea9c0a9229faec6))
60
+ * use Buffer.from ([2b28d90](https://github.com/dgreif/ring/commit/2b28d9028fc7f2c2e11b11cf3b27cc03f097eab2))
61
+
62
+ ### ⚠ BREAKING CHANGES
63
+
64
+ * The `sendDoorbellMotionNotificationsToTv` and `sendCameraMotionNotificationsToTv` options have been removed. These options were originally inteded as a workaround for the fact that tvOS would only automatically start streaming for doorbell presses and not for motion events. tvOS 15 now supports this features as an option in the UI, so we no longer need this option at the plugin level.
65
+ * **api:** All ding-related functionality will now use the `PushNotification` type, and the `ActiveDing` type has been removed
66
+ * **api:** The `cameraDingsPollingSeconds` option has been removed. Ding events will now be received via push notifications (no setup required)
67
+ * **api:** The `treatKnockAsDing` option has been removed. Knocks should now register as "dings" automatically, though I have not been able to test this functionality
68
+ * `ring-client-api` only (homebridge not impacted) - All streaming will now be done using the new Ring api via WebRTC + WebSockets. For those using `camera.streamVideo`, you should not notice any difference. Anyone directly interacting with sip sessions, you will need to migrate to the new `LiveCall` api. It's fairly similar, and should be more reliable long term. A number of other sip-specific methods have been removed from the `RingCamera` api as well.
69
+ * Node 10 is no longer officially supported. It may still work, but could break without warning in the future.
70
+ * **homebridge:** The minimum supported homebridge version for this plugin is now 1.4.0
7
71
 
8
72
  ### Bug Fixes
9
73
 
package/lib/api/api.js CHANGED
@@ -1,13 +1,4 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
12
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
4
  };
@@ -41,19 +32,17 @@ class RingApi extends subscribed_1.Subscribed {
41
32
  (0, ffmpeg_1.setFfmpegPath)(ffmpegPath);
42
33
  }
43
34
  }
44
- fetchRingDevices() {
45
- return __awaiter(this, void 0, void 0, function* () {
46
- const { doorbots, chimes, authorized_doorbots: authorizedDoorbots, stickup_cams: stickupCams, base_stations: baseStations, beams_bridges: beamBridges, } = yield this.restClient.request({ url: (0, rest_client_1.clientApi)('ring_devices') });
47
- return {
48
- doorbots,
49
- chimes,
50
- authorizedDoorbots,
51
- stickupCams,
52
- allCameras: doorbots.concat(stickupCams, authorizedDoorbots),
53
- baseStations,
54
- beamBridges,
55
- };
56
- });
35
+ 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
+ return {
38
+ doorbots,
39
+ chimes,
40
+ authorizedDoorbots,
41
+ stickupCams,
42
+ allCameras: doorbots.concat(stickupCams, authorizedDoorbots),
43
+ baseStations,
44
+ beamBridges,
45
+ };
57
46
  }
58
47
  listenForDeviceUpdates(cameras, chimes) {
59
48
  const { cameraStatusPollingSeconds } = this.options;
@@ -96,91 +85,87 @@ class RingApi extends subscribed_1.Subscribed {
96
85
  onUpdateReceived.next(null); // kick off polling
97
86
  }
98
87
  }
99
- registerPushReceiver(cameras) {
100
- return __awaiter(this, void 0, void 0, function* () {
101
- const pushReceiver = new push_receiver_1.default({
102
- logLevel: 'NONE',
103
- senderId: '876313859327', // for Ring android app. 703521446232 for ring-site
104
- }), camerasById = cameras.reduce((byId, camera) => {
105
- byId[camera.id] = camera;
106
- return byId;
107
- }, {});
108
- pushReceiver.onCredentialsChanged(({ newCredentials: { fcm: { token }, }, }) => __awaiter(this, void 0, void 0, function* () {
109
- try {
110
- yield this.restClient.request({
111
- url: (0, rest_client_1.clientApi)('device'),
112
- method: 'PATCH',
113
- json: {
114
- device: {
115
- metadata: Object.assign(Object.assign({}, this.restClient.baseSessionMetadata), { pn_service: 'fcm' }),
116
- os: 'android',
117
- push_notification_token: token,
88
+ async registerPushReceiver(cameras) {
89
+ const pushReceiver = new push_receiver_1.default({
90
+ logLevel: 'NONE',
91
+ senderId: '876313859327', // for Ring android app. 703521446232 for ring-site
92
+ }), camerasById = cameras.reduce((byId, camera) => {
93
+ byId[camera.id] = camera;
94
+ return byId;
95
+ }, {});
96
+ pushReceiver.onCredentialsChanged(async ({ newCredentials: { fcm: { token }, }, }) => {
97
+ try {
98
+ await this.restClient.request({
99
+ url: (0, rest_client_1.clientApi)('device'),
100
+ method: 'PATCH',
101
+ json: {
102
+ device: {
103
+ metadata: {
104
+ ...this.restClient.baseSessionMetadata,
105
+ pn_service: 'fcm',
118
106
  },
107
+ os: 'android',
108
+ push_notification_token: token,
119
109
  },
120
- });
121
- }
122
- catch (e) {
123
- (0, util_1.logError)(e);
124
- }
125
- }));
126
- pushReceiver.onNotification(({ message }) => {
127
- var _a, _b;
128
- const dataJson = (_a = message.data) === null || _a === void 0 ? void 0 : _a.gcmData;
129
- try {
130
- const notification = JSON.parse(dataJson);
131
- if (!notification.ding) {
132
- // We may get notification that are not for a ding/motion, which can be ignored
133
- return;
134
- }
135
- (_b = camerasById[notification.ding.doorbot_id]) === null || _b === void 0 ? void 0 : _b.processPushNotification(notification);
136
- }
137
- catch (e) {
138
- (0, util_1.logError)(e);
139
- }
140
- });
141
- try {
142
- yield pushReceiver.connect();
110
+ },
111
+ });
143
112
  }
144
113
  catch (e) {
145
- (0, util_1.logError)('Failed to connect push notification receiver');
146
114
  (0, util_1.logError)(e);
147
115
  }
148
116
  });
149
- }
150
- fetchRawLocations() {
151
- return __awaiter(this, void 0, void 0, function* () {
152
- const { user_locations: rawLocations } = yield this.restClient.request({ url: (0, rest_client_1.deviceApi)('locations') });
153
- if (!rawLocations) {
154
- throw new Error('The Ring account which you used to generate a refresh token does not have any associated locations. Please use an account that has access to at least one location.');
117
+ pushReceiver.onNotification(({ message }) => {
118
+ const dataJson = message.data?.gcmData;
119
+ try {
120
+ const notification = JSON.parse(dataJson);
121
+ if (!notification.ding) {
122
+ // We may get notification that are not for a ding/motion, which can be ignored
123
+ return;
124
+ }
125
+ camerasById[notification.ding.doorbot_id]?.processPushNotification(notification);
126
+ }
127
+ catch (e) {
128
+ (0, util_1.logError)(e);
155
129
  }
156
- return rawLocations;
157
130
  });
131
+ try {
132
+ await pushReceiver.connect();
133
+ }
134
+ catch (e) {
135
+ (0, util_1.logError)('Failed to connect push notification receiver');
136
+ (0, util_1.logError)(e);
137
+ }
138
+ }
139
+ async fetchRawLocations() {
140
+ const { user_locations: rawLocations } = await this.restClient.request({ url: (0, rest_client_1.deviceApi)('locations') });
141
+ if (!rawLocations) {
142
+ throw new Error('The Ring account which you used to generate a refresh token does not have any associated locations. Please use an account that has access to at least one location.');
143
+ }
144
+ return rawLocations;
158
145
  }
159
146
  fetchAmazonKeyLocks() {
160
147
  return this.restClient.request({
161
148
  url: 'https://api.ring.com/integrations/amazonkey/v2/devices/lock_associations',
162
149
  });
163
150
  }
164
- fetchAndBuildLocations() {
165
- return __awaiter(this, void 0, void 0, function* () {
166
- const rawLocations = yield this.fetchRawLocations(), { authorizedDoorbots, chimes, doorbots, allCameras, baseStations, beamBridges, } = yield this.fetchRingDevices(), locationIdsWithHubs = [...baseStations, ...beamBridges].map((x) => x.location_id), cameras = allCameras.map((data) => new ring_camera_1.RingCamera(data, doorbots.includes(data) ||
167
- authorizedDoorbots.includes(data) ||
168
- data.kind.startsWith('doorbell'), this.restClient, this.options.avoidSnapshotBatteryDrain || false)), ringChimes = chimes.map((data) => new ring_chime_1.RingChime(data, this.restClient)), locations = rawLocations
169
- .filter((location) => {
170
- return (!Array.isArray(this.options.locationIds) ||
171
- this.options.locationIds.includes(location.location_id));
172
- })
173
- .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), {
174
- hasHubs: locationIdsWithHubs.includes(location.location_id),
175
- hasAlarmBaseStation: baseStations.some((station) => station.location_id === location.location_id),
176
- locationModePollingSeconds: this.options.locationModePollingSeconds,
177
- }, this.restClient));
178
- this.listenForDeviceUpdates(cameras, ringChimes);
179
- this.registerPushReceiver(cameras).catch((e) => {
180
- (0, util_1.logError)(e);
181
- });
182
- return locations;
151
+ async fetchAndBuildLocations() {
152
+ 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) ||
153
+ authorizedDoorbots.includes(data) ||
154
+ data.kind.startsWith('doorbell'), this.restClient, this.options.avoidSnapshotBatteryDrain || false)), ringChimes = chimes.map((data) => new ring_chime_1.RingChime(data, this.restClient)), locations = rawLocations
155
+ .filter((location) => {
156
+ return (!Array.isArray(this.options.locationIds) ||
157
+ this.options.locationIds.includes(location.location_id));
158
+ })
159
+ .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), {
160
+ hasHubs: locationIdsWithHubs.includes(location.location_id),
161
+ hasAlarmBaseStation: baseStations.some((station) => station.location_id === location.location_id),
162
+ locationModePollingSeconds: this.options.locationModePollingSeconds,
163
+ }, this.restClient));
164
+ this.listenForDeviceUpdates(cameras, ringChimes);
165
+ this.registerPushReceiver(cameras).catch((e) => {
166
+ (0, util_1.logError)(e);
183
167
  });
168
+ return locations;
184
169
  }
185
170
  getLocations() {
186
171
  if (!this.locationsPromise) {
@@ -188,11 +173,9 @@ class RingApi extends subscribed_1.Subscribed {
188
173
  }
189
174
  return this.locationsPromise;
190
175
  }
191
- getCameras() {
192
- return __awaiter(this, void 0, void 0, function* () {
193
- const locations = yield this.getLocations();
194
- return locations.reduce((cameras, location) => [...cameras, ...location.cameras], []);
195
- });
176
+ async getCameras() {
177
+ const locations = await this.getLocations();
178
+ return locations.reduce((cameras, location) => [...cameras, ...location.cameras], []);
196
179
  }
197
180
  getProfile() {
198
181
  return this.restClient.request({
@@ -1,13 +1,4 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
2
  Object.defineProperty(exports, "__esModule", { value: true });
12
3
  exports.logDeviceData = void 0;
13
4
  /* eslint-disable no-console */
@@ -59,28 +50,26 @@ function stripSensitiveFields(input) {
59
50
  }
60
51
  }
61
52
  }
62
- function logDeviceData() {
63
- return __awaiter(this, void 0, void 0, function* () {
64
- console.log('This CLI will log data from you Ring Account to help debug issues and discovering new device types.');
65
- console.log('The logged data is anonymized and should not compromise your account in any way.');
66
- const refreshToken = yield (0, refresh_token_1.acquireRefreshToken)(), ringApi = new api_1.RingApi({ refreshToken });
67
- console.log('Successfully logged in. Fetching devices...');
68
- const locations = yield ringApi.getLocations(), amazonKeyLocks = yield ringApi.fetchAmazonKeyLocks(), locationsWithDevices = yield (0, util_1.mapAsync)(locations, (location) => __awaiter(this, void 0, void 0, function* () {
69
- const devices = yield location.getDevices();
70
- return {
71
- name: location.name,
72
- cameras: location.cameras.map((camera) => camera.data),
73
- chimes: location.chimes.map((chime) => chime.data),
74
- devices: devices.map((device) => device.data),
75
- };
76
- })), results = {
77
- locations: locationsWithDevices,
78
- amazonKeyLocks,
53
+ async function logDeviceData() {
54
+ console.log('This CLI will log data from you Ring Account to help debug issues and discovering new device types.');
55
+ console.log('The logged data is anonymized and should not compromise your account in any way.');
56
+ const refreshToken = await (0, refresh_token_1.acquireRefreshToken)(), ringApi = new api_1.RingApi({ refreshToken });
57
+ console.log('Successfully logged in. Fetching devices...');
58
+ const locations = await ringApi.getLocations(), amazonKeyLocks = await ringApi.fetchAmazonKeyLocks(), locationsWithDevices = await (0, util_1.mapAsync)(locations, async (location) => {
59
+ const devices = await location.getDevices();
60
+ return {
61
+ name: location.name,
62
+ cameras: location.cameras.map((camera) => camera.data),
63
+ chimes: location.chimes.map((chime) => chime.data),
64
+ devices: devices.map((device) => device.data),
79
65
  };
80
- stripSensitiveFields(results);
81
- console.log('\nPlease copy and paste everything AFTER THIS LINE:\n\n');
82
- console.log(JSON.stringify(results));
83
- process.exit(0);
84
- });
66
+ }), results = {
67
+ locations: locationsWithDevices,
68
+ amazonKeyLocks,
69
+ };
70
+ stripSensitiveFields(results);
71
+ console.log('\nPlease copy and paste everything AFTER THIS LINE:\n\n');
72
+ console.log(JSON.stringify(results));
73
+ process.exit(0);
85
74
  }
86
75
  exports.logDeviceData = logDeviceData;