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 +64 -0
- package/lib/api/api.js +79 -96
- package/lib/api/device-data.js +20 -31
- package/lib/api/location.js +125 -157
- package/lib/api/refresh-token.js +23 -36
- package/lib/api/rest-client.js +159 -165
- package/lib/api/ring-camera.js +121 -157
- package/lib/api/ring-chime.js +32 -51
- package/lib/api/ring-device.js +4 -1
- package/lib/api/streaming/peer-connection.d.ts +3 -3
- package/lib/api/streaming/peer-connection.js +18 -27
- package/lib/api/streaming/ring-edge-connection.js +56 -65
- package/lib/api/streaming/simple-webrtc-session.js +27 -42
- package/lib/api/streaming/streaming-connection-base.d.ts +1 -1
- package/lib/api/streaming/streaming-connection-base.js +12 -25
- package/lib/api/streaming/streaming-session.d.ts +2 -1
- package/lib/api/streaming/streaming-session.js +60 -74
- package/lib/api/streaming/webrtc-connection.js +22 -30
- package/lib/api/util.js +26 -39
- package/package.json +21 -21
package/lib/api/location.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
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
3
|
exports.Location = void 0;
|
|
13
4
|
const socket_io_client_1 = require("socket.io-client");
|
|
@@ -108,61 +99,59 @@ class Location extends subscribed_1.Subscribed {
|
|
|
108
99
|
get name() {
|
|
109
100
|
return this.locationDetails.name;
|
|
110
101
|
}
|
|
111
|
-
createConnection() {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
102
|
+
async createConnection() {
|
|
103
|
+
if (this.disconnected) {
|
|
104
|
+
return Promise.resolve({ disconnected: true });
|
|
105
|
+
}
|
|
106
|
+
(0, util_1.logInfo)('Creating location socket.io connection - ' + this.name);
|
|
107
|
+
if (process.version.startsWith('v15.')) {
|
|
108
|
+
(0, util_1.logError)('Node 15 is not currently supported by the Ring client. Please install the latest Node 14 instead. May not be able to fetch devices from Ring Alarm and Smart Lighting Hubs on this version of node.');
|
|
109
|
+
}
|
|
110
|
+
const { assets, ticket, host } = await this.restClient.request({
|
|
111
|
+
url: (0, rest_client_1.appApi)('clap/tickets?locationID=' + this.id),
|
|
112
|
+
}), supportedAssets = assets.filter(ring_types_1.isWebSocketSupportedAsset);
|
|
113
|
+
this.assets = supportedAssets;
|
|
114
|
+
this.receivedAssetDeviceLists.length = 0;
|
|
115
|
+
this.offlineAssets.length = 0;
|
|
116
|
+
if (!supportedAssets.length) {
|
|
117
|
+
const errorMessage = `No assets (alarm hubs or beam bridges) found for location ${this.name} - ${this.id}`;
|
|
118
|
+
(0, util_1.logError)(errorMessage);
|
|
119
|
+
throw new Error(errorMessage);
|
|
120
|
+
}
|
|
121
|
+
const connection = (0, socket_io_client_1.connect)(`wss://${host}/?authcode=${ticket}&ack=false&EIO=3`, { transports: ['websocket'] }), reconnect = () => {
|
|
122
|
+
if (this.reconnecting && this.connectionPromise) {
|
|
123
|
+
return this.connectionPromise;
|
|
115
124
|
}
|
|
116
|
-
|
|
117
|
-
if (
|
|
118
|
-
(0, util_1.
|
|
125
|
+
this.onConnected.next(false);
|
|
126
|
+
if (!this.disconnected) {
|
|
127
|
+
(0, util_1.logInfo)('Reconnecting location socket.io connection');
|
|
119
128
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
129
|
+
this.reconnecting = true;
|
|
130
|
+
connection.close();
|
|
131
|
+
return (this.connectionPromise = (0, util_1.delay)(1000).then(() => {
|
|
132
|
+
return this.createConnection();
|
|
133
|
+
}));
|
|
134
|
+
};
|
|
135
|
+
this.reconnecting = false;
|
|
136
|
+
connection.on('DataUpdate', (message) => {
|
|
137
|
+
if (message.datatype === 'HubDisconnectionEventType') {
|
|
138
|
+
(0, util_1.logInfo)('Location connection told to reconnect');
|
|
139
|
+
return reconnect();
|
|
130
140
|
}
|
|
131
|
-
|
|
132
|
-
if (this.reconnecting && this.connectionPromise) {
|
|
133
|
-
return this.connectionPromise;
|
|
134
|
-
}
|
|
135
|
-
this.onConnected.next(false);
|
|
136
|
-
if (!this.disconnected) {
|
|
137
|
-
(0, util_1.logInfo)('Reconnecting location socket.io connection');
|
|
138
|
-
}
|
|
139
|
-
this.reconnecting = true;
|
|
140
|
-
connection.close();
|
|
141
|
-
return (this.connectionPromise = (0, util_1.delay)(1000).then(() => {
|
|
142
|
-
return this.createConnection();
|
|
143
|
-
}));
|
|
144
|
-
};
|
|
145
|
-
this.reconnecting = false;
|
|
146
|
-
connection.on('DataUpdate', (message) => {
|
|
147
|
-
if (message.datatype === 'HubDisconnectionEventType') {
|
|
148
|
-
(0, util_1.logInfo)('Location connection told to reconnect');
|
|
149
|
-
return reconnect();
|
|
150
|
-
}
|
|
151
|
-
this.onDataUpdate.next(message);
|
|
152
|
-
});
|
|
153
|
-
connection.on('message', (message) => this.onMessage.next(message));
|
|
154
|
-
connection.on('error', reconnect);
|
|
155
|
-
connection.on('disconnect', reconnect);
|
|
156
|
-
return new Promise((resolve, reject) => {
|
|
157
|
-
connection.once('connect', () => {
|
|
158
|
-
resolve(connection);
|
|
159
|
-
this.onConnected.next(true);
|
|
160
|
-
(0, util_1.logInfo)('Ring connected to socket.io server');
|
|
161
|
-
assets.forEach((asset) => this.requestList(deviceListMessageType, asset.uuid));
|
|
162
|
-
});
|
|
163
|
-
connection.once('error', reject);
|
|
164
|
-
}).catch(reconnect);
|
|
141
|
+
this.onDataUpdate.next(message);
|
|
165
142
|
});
|
|
143
|
+
connection.on('message', (message) => this.onMessage.next(message));
|
|
144
|
+
connection.on('error', reconnect);
|
|
145
|
+
connection.on('disconnect', reconnect);
|
|
146
|
+
return new Promise((resolve, reject) => {
|
|
147
|
+
connection.once('connect', () => {
|
|
148
|
+
resolve(connection);
|
|
149
|
+
this.onConnected.next(true);
|
|
150
|
+
(0, util_1.logInfo)('Ring connected to socket.io server');
|
|
151
|
+
assets.forEach((asset) => this.requestList(deviceListMessageType, asset.uuid));
|
|
152
|
+
});
|
|
153
|
+
connection.once('error', reject);
|
|
154
|
+
}).catch(reconnect);
|
|
166
155
|
}
|
|
167
156
|
getConnection() {
|
|
168
157
|
if (!this.hasHubs) {
|
|
@@ -173,37 +162,29 @@ class Location extends subscribed_1.Subscribed {
|
|
|
173
162
|
}
|
|
174
163
|
return (this.connectionPromise = this.createConnection());
|
|
175
164
|
}
|
|
176
|
-
sendMessage(message) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
connection.emit('message', message);
|
|
181
|
-
});
|
|
165
|
+
async sendMessage(message) {
|
|
166
|
+
const connection = await this.getConnection();
|
|
167
|
+
message.seq = this.seq++;
|
|
168
|
+
connection.emit('message', message);
|
|
182
169
|
}
|
|
183
|
-
sendCommandToSecurityPanel(commandType, data) {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
securityPanel.sendCommand(commandType, data);
|
|
187
|
-
});
|
|
170
|
+
async sendCommandToSecurityPanel(commandType, data) {
|
|
171
|
+
const securityPanel = await this.getSecurityPanel();
|
|
172
|
+
securityPanel.sendCommand(commandType, data);
|
|
188
173
|
}
|
|
189
|
-
setAlarmMode(alarmMode, bypassSensorZids) {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
bypass: bypassSensorZids,
|
|
195
|
-
});
|
|
196
|
-
const updatedData = yield updatedDataPromise;
|
|
197
|
-
if (updatedData.mode !== alarmMode) {
|
|
198
|
-
throw new Error(`Failed to set alarm mode to "${alarmMode}". Sensors may require bypass, which can only be done in the Ring app.`);
|
|
199
|
-
}
|
|
174
|
+
async setAlarmMode(alarmMode, bypassSensorZids) {
|
|
175
|
+
const securityPanel = await this.getSecurityPanel(), updatedDataPromise = (0, rxjs_1.firstValueFrom)(securityPanel.onData.pipe((0, operators_1.skip)(1)));
|
|
176
|
+
await this.sendCommandToSecurityPanel('security-panel.switch-mode', {
|
|
177
|
+
mode: alarmMode,
|
|
178
|
+
bypass: bypassSensorZids,
|
|
200
179
|
});
|
|
180
|
+
const updatedData = await updatedDataPromise;
|
|
181
|
+
if (updatedData.mode !== alarmMode) {
|
|
182
|
+
throw new Error(`Failed to set alarm mode to "${alarmMode}". Sensors may require bypass, which can only be done in the Ring app.`);
|
|
183
|
+
}
|
|
201
184
|
}
|
|
202
|
-
getAlarmMode() {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
return securityPanel.data.mode;
|
|
206
|
-
});
|
|
185
|
+
async getAlarmMode() {
|
|
186
|
+
const securityPanel = await this.getSecurityPanel();
|
|
187
|
+
return securityPanel.data.mode;
|
|
207
188
|
}
|
|
208
189
|
soundSiren() {
|
|
209
190
|
return this.sendCommandToSecurityPanel('security-panel.sound-siren');
|
|
@@ -229,39 +210,33 @@ class Location extends subscribed_1.Subscribed {
|
|
|
229
210
|
requestList(listType, assetId) {
|
|
230
211
|
return this.sendMessage({ msg: listType, dst: assetId });
|
|
231
212
|
}
|
|
232
|
-
getList(listType, assetId) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
return this.getNextMessageOfType(listType, assetId);
|
|
236
|
-
});
|
|
213
|
+
async getList(listType, assetId) {
|
|
214
|
+
await this.requestList(listType, assetId);
|
|
215
|
+
return this.getNextMessageOfType(listType, assetId);
|
|
237
216
|
}
|
|
238
|
-
getDevices() {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
return (0, rxjs_1.firstValueFrom)(this.onDevices);
|
|
247
|
-
});
|
|
217
|
+
async getDevices() {
|
|
218
|
+
if (!this.hasHubs) {
|
|
219
|
+
return Promise.resolve([]);
|
|
220
|
+
}
|
|
221
|
+
if (!this.connectionPromise) {
|
|
222
|
+
await this.getConnection();
|
|
223
|
+
}
|
|
224
|
+
return (0, rxjs_1.firstValueFrom)(this.onDevices);
|
|
248
225
|
}
|
|
249
226
|
getRoomList(assetId) {
|
|
250
227
|
return this.getList('RoomGetList', assetId);
|
|
251
228
|
}
|
|
252
|
-
getSecurityPanel() {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
return device.data.deviceType === ring_types_1.RingDeviceType.SecurityPanel;
|
|
259
|
-
});
|
|
260
|
-
if (!securityPanel) {
|
|
261
|
-
throw new Error(`Could not find a security panel for location ${this.name} - ${this.id}`);
|
|
262
|
-
}
|
|
263
|
-
return (this.securityPanel = securityPanel);
|
|
229
|
+
async getSecurityPanel() {
|
|
230
|
+
if (this.securityPanel) {
|
|
231
|
+
return this.securityPanel;
|
|
232
|
+
}
|
|
233
|
+
const devices = await this.getDevices(), securityPanel = devices.find((device) => {
|
|
234
|
+
return device.data.deviceType === ring_types_1.RingDeviceType.SecurityPanel;
|
|
264
235
|
});
|
|
236
|
+
if (!securityPanel) {
|
|
237
|
+
throw new Error(`Could not find a security panel for location ${this.name} - ${this.id}`);
|
|
238
|
+
}
|
|
239
|
+
return (this.securityPanel = securityPanel);
|
|
265
240
|
}
|
|
266
241
|
disarm() {
|
|
267
242
|
return this.setAlarmMode('none');
|
|
@@ -275,7 +250,10 @@ class Location extends subscribed_1.Subscribed {
|
|
|
275
250
|
getHistory(options = {}) {
|
|
276
251
|
options.maxLevel = options.maxLevel || 50;
|
|
277
252
|
return this.restClient.request({
|
|
278
|
-
url: (0, rest_client_1.appApi)(`rs/history${(0, ring_camera_1.getSearchQueryString)(
|
|
253
|
+
url: (0, rest_client_1.appApi)(`rs/history${(0, ring_camera_1.getSearchQueryString)({
|
|
254
|
+
accountId: this.id,
|
|
255
|
+
...options,
|
|
256
|
+
})}`),
|
|
279
257
|
});
|
|
280
258
|
}
|
|
281
259
|
getCameraEvents(options = {}) {
|
|
@@ -310,46 +288,38 @@ class Location extends subscribed_1.Subscribed {
|
|
|
310
288
|
triggerFireAlarm() {
|
|
311
289
|
return this.triggerAlarm(ring_types_1.DispatchSignalType.Fire);
|
|
312
290
|
}
|
|
313
|
-
getLocationMode() {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
url: (0, rest_client_1.appApi)(`mode/location/${this.id}`),
|
|
319
|
-
});
|
|
320
|
-
this.onLocationMode.next(response.mode);
|
|
321
|
-
return response;
|
|
291
|
+
async getLocationMode() {
|
|
292
|
+
this.onLocationModeRequested.next(null);
|
|
293
|
+
const response = await this.restClient.request({
|
|
294
|
+
method: 'GET',
|
|
295
|
+
url: (0, rest_client_1.appApi)(`mode/location/${this.id}`),
|
|
322
296
|
});
|
|
297
|
+
this.onLocationMode.next(response.mode);
|
|
298
|
+
return response;
|
|
323
299
|
}
|
|
324
|
-
setLocationMode(mode) {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
json: { mode },
|
|
330
|
-
});
|
|
331
|
-
this.onLocationMode.next(response.mode);
|
|
332
|
-
return response;
|
|
300
|
+
async setLocationMode(mode) {
|
|
301
|
+
const response = await this.restClient.request({
|
|
302
|
+
method: 'POST',
|
|
303
|
+
url: (0, rest_client_1.appApi)(`mode/location/${this.id}`),
|
|
304
|
+
json: { mode },
|
|
333
305
|
});
|
|
306
|
+
this.onLocationMode.next(response.mode);
|
|
307
|
+
return response;
|
|
334
308
|
}
|
|
335
|
-
disableLocationModes() {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
url: (0, rest_client_1.appApi)(`mode/location/${this.id}/settings`),
|
|
340
|
-
});
|
|
341
|
-
this.onLocationMode.next('disabled');
|
|
309
|
+
async disableLocationModes() {
|
|
310
|
+
await this.restClient.request({
|
|
311
|
+
method: 'DELETE',
|
|
312
|
+
url: (0, rest_client_1.appApi)(`mode/location/${this.id}/settings`),
|
|
342
313
|
});
|
|
314
|
+
this.onLocationMode.next('disabled');
|
|
343
315
|
}
|
|
344
|
-
enableLocationModes() {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
url: (0, rest_client_1.appApi)(`mode/location/${this.id}/settings/setup`),
|
|
349
|
-
});
|
|
350
|
-
yield this.getLocationMode();
|
|
351
|
-
return response;
|
|
316
|
+
async enableLocationModes() {
|
|
317
|
+
const response = await this.restClient.request({
|
|
318
|
+
method: 'POST',
|
|
319
|
+
url: (0, rest_client_1.appApi)(`mode/location/${this.id}/settings/setup`),
|
|
352
320
|
});
|
|
321
|
+
await this.getLocationMode();
|
|
322
|
+
return response;
|
|
353
323
|
}
|
|
354
324
|
getLocationModeSettings() {
|
|
355
325
|
return this.restClient.request({
|
|
@@ -377,15 +347,13 @@ class Location extends subscribed_1.Subscribed {
|
|
|
377
347
|
json: { sharedUsersEnabled },
|
|
378
348
|
});
|
|
379
349
|
}
|
|
380
|
-
supportsLocationModeSwitching() {
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
return !readOnly && !ring_types_1.disabledLocationModes.includes(mode);
|
|
388
|
-
});
|
|
350
|
+
async supportsLocationModeSwitching() {
|
|
351
|
+
if (this.hasAlarmBaseStation || !this.cameras.length) {
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
const modeResponse = await this.getLocationMode(), { mode, readOnly } = modeResponse;
|
|
355
|
+
(0, util_1.logDebug)('Location Mode: ' + JSON.stringify(modeResponse));
|
|
356
|
+
return !readOnly && !ring_types_1.disabledLocationModes.includes(mode);
|
|
389
357
|
}
|
|
390
358
|
disconnect() {
|
|
391
359
|
this.disconnected = true;
|
package/lib/api/refresh-token.js
CHANGED
|
@@ -1,48 +1,35 @@
|
|
|
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.logRefreshToken = exports.acquireRefreshToken = void 0;
|
|
13
4
|
/* eslint-disable no-console */
|
|
14
5
|
const rest_client_1 = require("./rest-client");
|
|
15
6
|
const util_1 = require("./util");
|
|
16
|
-
function acquireRefreshToken() {
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
process.exit(1);
|
|
34
|
-
});
|
|
35
|
-
return auth.refresh_token;
|
|
7
|
+
async function acquireRefreshToken() {
|
|
8
|
+
const email = await (0, util_1.requestInput)('Email: '), password = await (0, util_1.requestInput)('Password: '), restClient = new rest_client_1.RingRestClient({ email, password }), getAuthWith2fa = async () => {
|
|
9
|
+
const code = await (0, util_1.requestInput)('2fa Code: ');
|
|
10
|
+
try {
|
|
11
|
+
return await restClient.getAuth(code);
|
|
12
|
+
}
|
|
13
|
+
catch (_) {
|
|
14
|
+
console.log('Incorrect 2fa code. Please try again.');
|
|
15
|
+
return getAuthWith2fa();
|
|
16
|
+
}
|
|
17
|
+
}, auth = await restClient.getCurrentAuth().catch((e) => {
|
|
18
|
+
if (restClient.promptFor2fa) {
|
|
19
|
+
console.log(restClient.promptFor2fa);
|
|
20
|
+
return getAuthWith2fa();
|
|
21
|
+
}
|
|
22
|
+
console.error(e);
|
|
23
|
+
process.exit(1);
|
|
36
24
|
});
|
|
25
|
+
return auth.refresh_token;
|
|
37
26
|
}
|
|
38
27
|
exports.acquireRefreshToken = acquireRefreshToken;
|
|
39
|
-
function logRefreshToken() {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
console.log(`"refreshToken": "${refreshToken}"`);
|
|
45
|
-
});
|
|
28
|
+
async function logRefreshToken() {
|
|
29
|
+
console.log('This CLI will provide you with a refresh token which you can use to configure ring-client-api and homebridge-ring.');
|
|
30
|
+
const refreshToken = await acquireRefreshToken();
|
|
31
|
+
console.log('\nSuccessfully logged in to Ring. Please add the following to your config:\n');
|
|
32
|
+
console.log(`"refreshToken": "${refreshToken}"`);
|
|
46
33
|
}
|
|
47
34
|
exports.logRefreshToken = logRefreshToken;
|
|
48
35
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|