ring-client-api 13.2.1 → 14.0.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/lib/api.d.ts +10 -8
- package/lib/api.js +57 -64
- package/lib/device-data.js +6 -9
- package/lib/ffmpeg.js +2 -6
- package/lib/index.d.ts +7 -7
- package/lib/index.js +7 -23
- package/lib/location.d.ts +32 -31
- package/lib/location.js +67 -72
- package/lib/refresh-token.js +6 -11
- package/lib/rest-client.d.ts +4 -4
- package/lib/rest-client.js +40 -51
- package/lib/ring-auth-cli.js +2 -4
- package/lib/ring-camera.d.ts +26 -23
- package/lib/ring-camera.js +64 -61
- package/lib/ring-chime.d.ts +7 -7
- package/lib/ring-chime.js +14 -18
- package/lib/ring-device-data-cli.js +2 -4
- package/lib/ring-device.d.ts +7 -7
- package/lib/ring-device.js +16 -20
- package/lib/ring-intercom.d.ts +8 -8
- package/lib/ring-intercom.js +23 -27
- package/lib/ring-types.d.ts +142 -133
- package/lib/ring-types.js +137 -153
- package/lib/streaming/peer-connection.d.ts +5 -3
- package/lib/streaming/peer-connection.js +25 -30
- package/lib/streaming/simple-webrtc-session.d.ts +3 -3
- package/lib/streaming/simple-webrtc-session.js +3 -7
- package/lib/streaming/streaming-messages.d.ts +6 -5
- package/lib/streaming/streaming-messages.js +6 -9
- package/lib/streaming/streaming-session.d.ts +5 -5
- package/lib/streaming/streaming-session.js +34 -38
- package/lib/streaming/webrtc-connection.d.ts +5 -5
- package/lib/streaming/webrtc-connection.js +39 -43
- package/lib/subscribed.d.ts +1 -1
- package/lib/subscribed.js +1 -5
- package/lib/test/rest-client.spec.js +44 -45
- package/lib/test/ring-camera.spec.js +12 -13
- package/lib/util.js +33 -54
- package/package.json +17 -19
package/lib/ring-camera.d.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { RingCameraKind } from './ring-types.ts';
|
|
2
|
+
import { type CameraData, type CameraDeviceSettingsData, type CameraEventOptions, type CameraEventResponse, type CameraHealth, type HistoryOptions, type PeriodicFootageResponse, type PushNotificationDingV2, type VideoSearchResponse, type OnvifCameraData, type PushNotification } from './ring-types.ts';
|
|
3
|
+
import type { RingRestClient } from './rest-client.ts';
|
|
3
4
|
import { BehaviorSubject, Subject } from 'rxjs';
|
|
4
|
-
import { DeepPartial } from './util';
|
|
5
|
-
import { Subscribed } from './subscribed';
|
|
6
|
-
import { StreamingConnectionOptions } from './streaming/webrtc-connection';
|
|
7
|
-
import { FfmpegOptions
|
|
8
|
-
import {
|
|
5
|
+
import { type DeepPartial } from './util.ts';
|
|
6
|
+
import { Subscribed } from './subscribed.ts';
|
|
7
|
+
import type { StreamingConnectionOptions } from './streaming/webrtc-connection.ts';
|
|
8
|
+
import type { FfmpegOptions } from './streaming/streaming-session.ts';
|
|
9
|
+
import { StreamingSession } from './streaming/streaming-session.ts';
|
|
10
|
+
import { SimpleWebRtcSession } from './streaming/simple-webrtc-session.ts';
|
|
9
11
|
export type AnyCameraData = CameraData | OnvifCameraData;
|
|
10
12
|
export declare function getBatteryLevel(data: Pick<CameraData, 'battery_life' | 'battery_life_2'> & {
|
|
11
13
|
health?: Partial<CameraData['health']>;
|
|
@@ -15,12 +17,8 @@ export declare function getSearchQueryString(options: CameraEventOptions | (Hist
|
|
|
15
17
|
})): string;
|
|
16
18
|
export declare function cleanSnapshotUuid(uuid?: string | null): string | null | undefined;
|
|
17
19
|
export declare class RingCamera extends Subscribed {
|
|
18
|
-
private initialData;
|
|
19
|
-
isDoorbot: boolean;
|
|
20
|
-
private restClient;
|
|
21
|
-
private avoidSnapshotBatteryDrain;
|
|
22
20
|
id: number;
|
|
23
|
-
deviceType:
|
|
21
|
+
deviceType: "onvif_camera" | Omit<RingCameraKind, "onvif_camera">;
|
|
24
22
|
model: string;
|
|
25
23
|
onData: BehaviorSubject<AnyCameraData>;
|
|
26
24
|
hasLight: boolean;
|
|
@@ -33,6 +31,10 @@ export declare class RingCamera extends Subscribed {
|
|
|
33
31
|
onMotionStarted: import("rxjs").Observable<null>;
|
|
34
32
|
onBatteryLevel: import("rxjs").Observable<number | null>;
|
|
35
33
|
onInHomeDoorbellStatus: import("rxjs").Observable<boolean>;
|
|
34
|
+
private initialData;
|
|
35
|
+
isDoorbot: boolean;
|
|
36
|
+
private restClient;
|
|
37
|
+
private avoidSnapshotBatteryDrain;
|
|
36
38
|
constructor(initialData: AnyCameraData, isDoorbot: boolean, restClient: RingRestClient, avoidSnapshotBatteryDrain: boolean);
|
|
37
39
|
updateData(update: AnyCameraData): void;
|
|
38
40
|
requestUpdate(): void;
|
|
@@ -46,6 +48,7 @@ export declare class RingCamera extends Subscribed {
|
|
|
46
48
|
get hasLowBattery(): boolean;
|
|
47
49
|
get isCharging(): boolean;
|
|
48
50
|
get operatingOnBattery(): boolean;
|
|
51
|
+
get canTakeSnapshotWhileRecording(): boolean;
|
|
49
52
|
get isOffline(): boolean;
|
|
50
53
|
get isRingEdgeEnabled(): boolean;
|
|
51
54
|
get hasInHomeDoorbell(): boolean;
|
|
@@ -54,24 +57,24 @@ export declare class RingCamera extends Subscribed {
|
|
|
54
57
|
setLight(on: boolean): Promise<boolean>;
|
|
55
58
|
setSiren(on: boolean): Promise<boolean>;
|
|
56
59
|
setSettings(settings: DeepPartial<CameraData['settings']>): Promise<void>;
|
|
57
|
-
setDeviceSettings(settings: DeepPartial<CameraDeviceSettingsData>): Promise<CameraDeviceSettingsData & import("./rest-client").ExtendedResponse>;
|
|
58
|
-
getDeviceSettings(): Promise<CameraDeviceSettingsData & import("./rest-client").ExtendedResponse>;
|
|
60
|
+
setDeviceSettings(settings: DeepPartial<CameraDeviceSettingsData>): Promise<CameraDeviceSettingsData & import("./rest-client.ts").ExtendedResponse>;
|
|
61
|
+
getDeviceSettings(): Promise<CameraDeviceSettingsData & import("./rest-client.ts").ExtendedResponse>;
|
|
59
62
|
setInHomeDoorbell(enable: boolean): Promise<boolean>;
|
|
60
63
|
getHealth(): Promise<CameraHealth>;
|
|
61
64
|
private createStreamingConnection;
|
|
62
65
|
startLiveCall(options?: StreamingConnectionOptions): Promise<StreamingSession>;
|
|
63
66
|
private removeDingById;
|
|
64
67
|
processPushNotification(notification: PushNotification): void;
|
|
65
|
-
getEvents(options?: CameraEventOptions): Promise<CameraEventResponse & import("./rest-client").ExtendedResponse>;
|
|
68
|
+
getEvents(options?: CameraEventOptions): Promise<CameraEventResponse & import("./rest-client.ts").ExtendedResponse>;
|
|
66
69
|
videoSearch({ dateFrom, dateTo, order }?: {
|
|
67
70
|
dateFrom: number;
|
|
68
71
|
dateTo: number;
|
|
69
72
|
order?: string | undefined;
|
|
70
|
-
}): Promise<VideoSearchResponse & import("./rest-client").ExtendedResponse>;
|
|
73
|
+
}): Promise<VideoSearchResponse & import("./rest-client.ts").ExtendedResponse>;
|
|
71
74
|
getPeriodicalFootage({ startAtMs, endAtMs }?: {
|
|
72
75
|
startAtMs: number;
|
|
73
76
|
endAtMs: number;
|
|
74
|
-
}): Promise<PeriodicFootageResponse & import("./rest-client").ExtendedResponse>;
|
|
77
|
+
}): Promise<PeriodicFootageResponse & import("./rest-client.ts").ExtendedResponse>;
|
|
75
78
|
getRecordingUrl(dingIdStr: string, { transcoded }?: {
|
|
76
79
|
transcoded?: boolean | undefined;
|
|
77
80
|
}): Promise<string>;
|
|
@@ -94,8 +97,8 @@ export declare class RingCamera extends Subscribed {
|
|
|
94
97
|
maxWaitMs?: number;
|
|
95
98
|
force?: boolean;
|
|
96
99
|
uuid?: string;
|
|
97
|
-
}): Promise<Buffer<ArrayBufferLike> & import("./rest-client").ExtendedResponse>;
|
|
98
|
-
getSnapshotByUuid(uuid: string): Promise<Buffer<ArrayBufferLike> & import("./rest-client").ExtendedResponse>;
|
|
100
|
+
}): Promise<Buffer<ArrayBufferLike> & import("./rest-client.ts").ExtendedResponse>;
|
|
101
|
+
getSnapshotByUuid(uuid: string): Promise<Buffer<ArrayBufferLike> & import("./rest-client.ts").ExtendedResponse>;
|
|
99
102
|
recordToFile(outputPath: string, duration?: number): Promise<void>;
|
|
100
103
|
streamVideo(ffmpegOptions: FfmpegOptions): Promise<StreamingSession>;
|
|
101
104
|
/**
|
|
@@ -105,9 +108,9 @@ export declare class RingCamera extends Subscribed {
|
|
|
105
108
|
* enabled as soon as the stream starts, which can drain the battery more quickly.
|
|
106
109
|
*/
|
|
107
110
|
createSimpleWebRtcSession(): SimpleWebRtcSession;
|
|
108
|
-
subscribeToDingEvents(): Promise<void & import("./rest-client").ExtendedResponse>;
|
|
109
|
-
unsubscribeFromDingEvents(): Promise<void & import("./rest-client").ExtendedResponse>;
|
|
110
|
-
subscribeToMotionEvents(): Promise<void & import("./rest-client").ExtendedResponse>;
|
|
111
|
-
unsubscribeFromMotionEvents(): Promise<void & import("./rest-client").ExtendedResponse>;
|
|
111
|
+
subscribeToDingEvents(): Promise<void & import("./rest-client.ts").ExtendedResponse>;
|
|
112
|
+
unsubscribeFromDingEvents(): Promise<void & import("./rest-client.ts").ExtendedResponse>;
|
|
113
|
+
subscribeToMotionEvents(): Promise<void & import("./rest-client.ts").ExtendedResponse>;
|
|
114
|
+
unsubscribeFromMotionEvents(): Promise<void & import("./rest-client.ts").ExtendedResponse>;
|
|
112
115
|
disconnect(): void;
|
|
113
116
|
}
|
package/lib/ring-camera.js
CHANGED
|
@@ -1,18 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const util_1 = require("./util");
|
|
12
|
-
const subscribed_1 = require("./subscribed");
|
|
13
|
-
const webrtc_connection_1 = require("./streaming/webrtc-connection");
|
|
14
|
-
const streaming_session_1 = require("./streaming/streaming-session");
|
|
15
|
-
const simple_webrtc_session_1 = require("./streaming/simple-webrtc-session");
|
|
1
|
+
import { RingCameraKind } from "./ring-types.js";
|
|
2
|
+
import { DoorbellType, PushNotificationAction, RingCameraModel, } from "./ring-types.js";
|
|
3
|
+
import { appApi, clientApi, deviceApi } from "./rest-client.js";
|
|
4
|
+
import { BehaviorSubject, firstValueFrom, ReplaySubject, Subject } from 'rxjs';
|
|
5
|
+
import { distinctUntilChanged, filter, map, share, startWith, throttleTime, } from 'rxjs/operators';
|
|
6
|
+
import { buildSearchString, delay, logDebug, logError, } from "./util.js";
|
|
7
|
+
import { Subscribed } from "./subscribed.js";
|
|
8
|
+
import { WebrtcConnection } from "./streaming/webrtc-connection.js";
|
|
9
|
+
import { StreamingSession } from "./streaming/streaming-session.js";
|
|
10
|
+
import { SimpleWebRtcSession } from "./streaming/simple-webrtc-session.js";
|
|
16
11
|
const maxSnapshotRefreshSeconds = 15, fullDayMs = 24 * 60 * 60 * 1000;
|
|
17
12
|
function parseBatteryLife(batteryLife) {
|
|
18
13
|
if (batteryLife === null || batteryLife === undefined) {
|
|
@@ -32,7 +27,7 @@ function getStartOfToday() {
|
|
|
32
27
|
function getEndOfToday() {
|
|
33
28
|
return getStartOfToday() + fullDayMs - 1;
|
|
34
29
|
}
|
|
35
|
-
function getBatteryLevel(data) {
|
|
30
|
+
export function getBatteryLevel(data) {
|
|
36
31
|
const levels = [
|
|
37
32
|
parseBatteryLife(data.battery_life),
|
|
38
33
|
parseBatteryLife(data.battery_life_2),
|
|
@@ -46,7 +41,7 @@ function getBatteryLevel(data) {
|
|
|
46
41
|
}
|
|
47
42
|
return Math.min(...levels);
|
|
48
43
|
}
|
|
49
|
-
function getSearchQueryString(options) {
|
|
44
|
+
export function getSearchQueryString(options) {
|
|
50
45
|
const queryString = Object.entries(options)
|
|
51
46
|
.map(([key, value]) => {
|
|
52
47
|
if (value === undefined) {
|
|
@@ -61,38 +56,44 @@ function getSearchQueryString(options) {
|
|
|
61
56
|
.join('&');
|
|
62
57
|
return queryString.length ? `?${queryString}` : '';
|
|
63
58
|
}
|
|
64
|
-
function cleanSnapshotUuid(uuid) {
|
|
59
|
+
export function cleanSnapshotUuid(uuid) {
|
|
65
60
|
if (!uuid) {
|
|
66
61
|
return uuid;
|
|
67
62
|
}
|
|
68
63
|
return uuid.replace(/:.*$/, '');
|
|
69
64
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
65
|
+
const wiredModelsWithNoSnapshotDuringRecording = new Set([
|
|
66
|
+
RingCameraKind.doorbell_graham_cracker,
|
|
67
|
+
]), enabledDoorbellTypes = new Set([
|
|
68
|
+
DoorbellType.Mechanical,
|
|
69
|
+
DoorbellType.Digital,
|
|
70
|
+
]);
|
|
71
|
+
export class RingCamera extends Subscribed {
|
|
75
72
|
id;
|
|
76
73
|
deviceType;
|
|
77
74
|
model;
|
|
78
75
|
onData;
|
|
79
76
|
hasLight;
|
|
80
77
|
hasSiren;
|
|
81
|
-
onRequestUpdate = new
|
|
82
|
-
onNewNotification = new
|
|
83
|
-
onActiveNotifications = new
|
|
84
|
-
onDoorbellPressed = this.onNewNotification.pipe(
|
|
85
|
-
onMotionDetected = this.onActiveNotifications.pipe(
|
|
86
|
-
|
|
87
|
-
connector: () => new
|
|
78
|
+
onRequestUpdate = new Subject();
|
|
79
|
+
onNewNotification = new Subject();
|
|
80
|
+
onActiveNotifications = new BehaviorSubject([]);
|
|
81
|
+
onDoorbellPressed = this.onNewNotification.pipe(filter((notification) => notification.android_config.category === PushNotificationAction.Ding), share());
|
|
82
|
+
onMotionDetected = this.onActiveNotifications.pipe(map((notifications) => notifications.some((notification) => notification.android_config.category ===
|
|
83
|
+
PushNotificationAction.Motion)), distinctUntilChanged(), share({
|
|
84
|
+
connector: () => new ReplaySubject(1),
|
|
88
85
|
resetOnError: false,
|
|
89
86
|
resetOnComplete: false,
|
|
90
87
|
resetOnRefCountZero: false,
|
|
91
88
|
}));
|
|
92
|
-
onMotionStarted = this.onMotionDetected.pipe(
|
|
93
|
-
|
|
89
|
+
onMotionStarted = this.onMotionDetected.pipe(filter((currentlyDetected) => currentlyDetected), map(() => null), // no value needed, event is what matters
|
|
90
|
+
share());
|
|
94
91
|
onBatteryLevel;
|
|
95
92
|
onInHomeDoorbellStatus;
|
|
93
|
+
initialData;
|
|
94
|
+
isDoorbot;
|
|
95
|
+
restClient;
|
|
96
|
+
avoidSnapshotBatteryDrain;
|
|
96
97
|
constructor(initialData, isDoorbot, restClient, avoidSnapshotBatteryDrain) {
|
|
97
98
|
super();
|
|
98
99
|
this.initialData = initialData;
|
|
@@ -102,34 +103,34 @@ class RingCamera extends subscribed_1.Subscribed {
|
|
|
102
103
|
this.id = this.initialData.id;
|
|
103
104
|
this.deviceType = this.initialData.kind;
|
|
104
105
|
this.model =
|
|
105
|
-
|
|
106
|
+
RingCameraModel[this.initialData.kind] ||
|
|
106
107
|
'Unknown Model';
|
|
107
|
-
this.onData = new
|
|
108
|
+
this.onData = new BehaviorSubject(this.initialData);
|
|
108
109
|
this.hasLight = this.initialData.led_status !== undefined;
|
|
109
110
|
this.hasSiren = this.initialData.siren_status !== undefined;
|
|
110
|
-
this.onBatteryLevel = this.onData.pipe(
|
|
111
|
+
this.onBatteryLevel = this.onData.pipe(map((data) => {
|
|
111
112
|
if (!('battery_life' in data)) {
|
|
112
113
|
return null;
|
|
113
114
|
}
|
|
114
115
|
return getBatteryLevel(data);
|
|
115
|
-
}),
|
|
116
|
-
this.onInHomeDoorbellStatus = this.onData.pipe(
|
|
116
|
+
}), distinctUntilChanged());
|
|
117
|
+
this.onInHomeDoorbellStatus = this.onData.pipe(map(({ settings: { chime_settings } }) => {
|
|
117
118
|
return Boolean(chime_settings?.enable);
|
|
118
|
-
}),
|
|
119
|
+
}), distinctUntilChanged());
|
|
119
120
|
this.addSubscriptions(this.restClient.onSession
|
|
120
|
-
.pipe(
|
|
121
|
+
.pipe(startWith(undefined), throttleTime(1000)) // Force this to run immediately, but don't double run if a session is created due to these api calls
|
|
121
122
|
.subscribe(() => {
|
|
122
123
|
this.subscribeToDingEvents().catch((e) => {
|
|
123
|
-
|
|
124
|
+
logError('Failed to subscribe ' +
|
|
124
125
|
initialData.description +
|
|
125
126
|
' to ding events');
|
|
126
|
-
|
|
127
|
+
logError(e);
|
|
127
128
|
});
|
|
128
129
|
this.subscribeToMotionEvents().catch((e) => {
|
|
129
|
-
|
|
130
|
+
logError('Failed to subscribe ' +
|
|
130
131
|
initialData.description +
|
|
131
132
|
' to motion events');
|
|
132
|
-
|
|
133
|
+
logError(e);
|
|
133
134
|
});
|
|
134
135
|
}));
|
|
135
136
|
}
|
|
@@ -177,6 +178,10 @@ class RingCamera extends subscribed_1.Subscribed {
|
|
|
177
178
|
get operatingOnBattery() {
|
|
178
179
|
return this.hasBattery && this.data.settings.power_mode !== 'wired';
|
|
179
180
|
}
|
|
181
|
+
get canTakeSnapshotWhileRecording() {
|
|
182
|
+
return (!this.operatingOnBattery &&
|
|
183
|
+
!wiredModelsWithNoSnapshotDuringRecording.has(this.data.kind));
|
|
184
|
+
}
|
|
180
185
|
get isOffline() {
|
|
181
186
|
return this.data.alerts.connection === 'offline';
|
|
182
187
|
}
|
|
@@ -186,14 +191,13 @@ class RingCamera extends subscribed_1.Subscribed {
|
|
|
186
191
|
get hasInHomeDoorbell() {
|
|
187
192
|
const { chime_settings } = this.data.settings;
|
|
188
193
|
return (this.isDoorbot &&
|
|
189
|
-
Boolean(chime_settings &&
|
|
190
|
-
[ring_types_1.DoorbellType.Mechanical, ring_types_1.DoorbellType.Digital].includes(chime_settings.type)));
|
|
194
|
+
Boolean(chime_settings && enabledDoorbellTypes.has(chime_settings.type)));
|
|
191
195
|
}
|
|
192
196
|
doorbotUrl(path = '') {
|
|
193
|
-
return
|
|
197
|
+
return clientApi(`doorbots/${this.id}/${path}`);
|
|
194
198
|
}
|
|
195
199
|
deviceUrl(path = '') {
|
|
196
|
-
return
|
|
200
|
+
return deviceApi(`devices/${this.id}/${path}`);
|
|
197
201
|
}
|
|
198
202
|
async setLight(on) {
|
|
199
203
|
if (!this.hasLight) {
|
|
@@ -263,16 +267,16 @@ class RingCamera extends subscribed_1.Subscribed {
|
|
|
263
267
|
const response = await this.restClient
|
|
264
268
|
.request({
|
|
265
269
|
method: 'POST',
|
|
266
|
-
url:
|
|
270
|
+
url: appApi('clap/ticket/request/signalsocket'),
|
|
267
271
|
})
|
|
268
272
|
.catch((e) => {
|
|
269
273
|
throw e;
|
|
270
274
|
});
|
|
271
|
-
return new
|
|
275
|
+
return new WebrtcConnection(response.ticket, this, options);
|
|
272
276
|
}
|
|
273
277
|
async startLiveCall(options = {}) {
|
|
274
278
|
const connection = await this.createStreamingConnection(options);
|
|
275
|
-
return new
|
|
279
|
+
return new StreamingSession(this, connection);
|
|
276
280
|
}
|
|
277
281
|
removeDingById(idToRemove) {
|
|
278
282
|
const allActiveDings = this.activeNotifications, otherDings = allActiveDings.filter(({ data }) => data.event.ding.id !== idToRemove);
|
|
@@ -296,7 +300,7 @@ class RingCamera extends subscribed_1.Subscribed {
|
|
|
296
300
|
}
|
|
297
301
|
getEvents(options = {}) {
|
|
298
302
|
return this.restClient.request({
|
|
299
|
-
url:
|
|
303
|
+
url: clientApi(`locations/${this.data.location_id}/devices/${this.id}/events${getSearchQueryString(options)}`),
|
|
300
304
|
});
|
|
301
305
|
}
|
|
302
306
|
videoSearch({ dateFrom, dateTo, order = 'asc' } = {
|
|
@@ -304,7 +308,7 @@ class RingCamera extends subscribed_1.Subscribed {
|
|
|
304
308
|
dateTo: getEndOfToday(),
|
|
305
309
|
}) {
|
|
306
310
|
return this.restClient.request({
|
|
307
|
-
url:
|
|
311
|
+
url: clientApi(`video_search/history?doorbot_id=${this.id}&date_from=${dateFrom}&date_to=${dateTo}&order=${order}&api_version=11&includes%5B%5D=pva`),
|
|
308
312
|
});
|
|
309
313
|
}
|
|
310
314
|
getPeriodicalFootage({ startAtMs, endAtMs } = {
|
|
@@ -318,7 +322,7 @@ class RingCamera extends subscribed_1.Subscribed {
|
|
|
318
322
|
}
|
|
319
323
|
async getRecordingUrl(dingIdStr, { transcoded = false } = {}) {
|
|
320
324
|
const path = transcoded ? 'recording' : 'share/play', response = await this.restClient.request({
|
|
321
|
-
url:
|
|
325
|
+
url: clientApi(`dings/${dingIdStr}/${path}?disable_redirect=true`),
|
|
322
326
|
});
|
|
323
327
|
return response.url;
|
|
324
328
|
}
|
|
@@ -355,7 +359,7 @@ class RingCamera extends subscribed_1.Subscribed {
|
|
|
355
359
|
return true;
|
|
356
360
|
}
|
|
357
361
|
if (this.hasSnapshotWithinLifetime) {
|
|
358
|
-
|
|
362
|
+
logDebug(`Snapshot for ${this.name} is still within its life time (${this.currentTimestampAge / 1000}s old)`);
|
|
359
363
|
return true;
|
|
360
364
|
}
|
|
361
365
|
if (!this.avoidSnapshotBatteryDrain || !this.operatingOnBattery) {
|
|
@@ -377,8 +381,8 @@ class RingCamera extends subscribed_1.Subscribed {
|
|
|
377
381
|
afterMs: this.lastSnapshotTimestamp,
|
|
378
382
|
force: true,
|
|
379
383
|
}),
|
|
380
|
-
|
|
381
|
-
const extraMessageForBatteryCam = this.
|
|
384
|
+
delay(maxSnapshotRefreshSeconds * 1000).then(() => {
|
|
385
|
+
const extraMessageForBatteryCam = !this.canTakeSnapshotWhileRecording
|
|
382
386
|
? '. This is normal behavior since this camera is unable to capture snapshots while streaming'
|
|
383
387
|
: '';
|
|
384
388
|
throw new Error(`Snapshot for ${this.name} (${this.deviceType} - ${this.model}) failed to refresh after ${maxSnapshotRefreshSeconds} seconds${extraMessageForBatteryCam}`);
|
|
@@ -397,7 +401,7 @@ class RingCamera extends subscribed_1.Subscribed {
|
|
|
397
401
|
}
|
|
398
402
|
async getNextSnapshot({ afterMs, maxWaitMs, force, uuid, }) {
|
|
399
403
|
const response = await this.restClient.request({
|
|
400
|
-
url: `https://app-snaps.ring.com/snapshots/next/${this.id}${
|
|
404
|
+
url: `https://app-snaps.ring.com/snapshots/next/${this.id}${buildSearchString({
|
|
401
405
|
'after-ms': afterMs,
|
|
402
406
|
'max-wait-ms': maxWaitMs,
|
|
403
407
|
extras: force ? 'force' : undefined,
|
|
@@ -415,7 +419,7 @@ class RingCamera extends subscribed_1.Subscribed {
|
|
|
415
419
|
}
|
|
416
420
|
getSnapshotByUuid(uuid) {
|
|
417
421
|
return this.restClient.request({
|
|
418
|
-
url:
|
|
422
|
+
url: clientApi('snapshots/uuid?uuid=' + cleanSnapshotUuid(uuid)),
|
|
419
423
|
responseType: 'buffer',
|
|
420
424
|
headers: {
|
|
421
425
|
accept: 'image/jpeg',
|
|
@@ -426,7 +430,7 @@ class RingCamera extends subscribed_1.Subscribed {
|
|
|
426
430
|
const liveCall = await this.streamVideo({
|
|
427
431
|
output: ['-t', duration.toString(), outputPath],
|
|
428
432
|
});
|
|
429
|
-
await
|
|
433
|
+
await firstValueFrom(liveCall.onCallEnded);
|
|
430
434
|
}
|
|
431
435
|
async streamVideo(ffmpegOptions) {
|
|
432
436
|
const liveCall = await this.startLiveCall();
|
|
@@ -440,7 +444,7 @@ class RingCamera extends subscribed_1.Subscribed {
|
|
|
440
444
|
* enabled as soon as the stream starts, which can drain the battery more quickly.
|
|
441
445
|
*/
|
|
442
446
|
createSimpleWebRtcSession() {
|
|
443
|
-
return new
|
|
447
|
+
return new SimpleWebRtcSession(this, this.restClient);
|
|
444
448
|
}
|
|
445
449
|
subscribeToDingEvents() {
|
|
446
450
|
return this.restClient.request({
|
|
@@ -470,4 +474,3 @@ class RingCamera extends subscribed_1.Subscribed {
|
|
|
470
474
|
this.unsubscribe();
|
|
471
475
|
}
|
|
472
476
|
}
|
|
473
|
-
exports.RingCamera = RingCamera;
|
package/lib/ring-chime.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { ChimeData, ChimeUpdate, ChimeSoundKind, RingtoneOptions, ChimeHealth } from './ring-types';
|
|
2
|
-
import { RingRestClient } from './rest-client';
|
|
1
|
+
import type { ChimeData, ChimeUpdate, ChimeSoundKind, RingtoneOptions, ChimeHealth } from './ring-types.ts';
|
|
2
|
+
import type { RingRestClient } from './rest-client.ts';
|
|
3
3
|
import { BehaviorSubject, Subject } from 'rxjs';
|
|
4
4
|
export declare class RingChime {
|
|
5
|
-
private initialData;
|
|
6
|
-
private restClient;
|
|
7
5
|
id: number;
|
|
8
|
-
deviceType: import("./ring-types").ChimeKind;
|
|
6
|
+
deviceType: import("./ring-types.ts").ChimeKind;
|
|
9
7
|
model: string;
|
|
10
8
|
onData: BehaviorSubject<ChimeData>;
|
|
11
9
|
onRequestUpdate: Subject<unknown>;
|
|
10
|
+
private initialData;
|
|
11
|
+
private restClient;
|
|
12
12
|
constructor(initialData: ChimeData, restClient: RingRestClient);
|
|
13
13
|
updateData(update: ChimeData): void;
|
|
14
14
|
requestUpdate(): void;
|
|
@@ -16,7 +16,7 @@ export declare class RingChime {
|
|
|
16
16
|
get name(): string;
|
|
17
17
|
get description(): string;
|
|
18
18
|
get volume(): number;
|
|
19
|
-
getRingtones(): Promise<RingtoneOptions & import("./rest-client").ExtendedResponse>;
|
|
19
|
+
getRingtones(): Promise<RingtoneOptions & import("./rest-client.ts").ExtendedResponse>;
|
|
20
20
|
getRingtoneByDescription(description: string, kind: ChimeSoundKind): Promise<{
|
|
21
21
|
user_id: string;
|
|
22
22
|
id: string;
|
|
@@ -27,7 +27,7 @@ export declare class RingChime {
|
|
|
27
27
|
available: string;
|
|
28
28
|
}>;
|
|
29
29
|
chimeUrl(path?: string): string;
|
|
30
|
-
playSound(kind: ChimeSoundKind): Promise<void & import("./rest-client").ExtendedResponse>;
|
|
30
|
+
playSound(kind: ChimeSoundKind): Promise<void & import("./rest-client.ts").ExtendedResponse>;
|
|
31
31
|
snooze(time: number): Promise<void>;
|
|
32
32
|
clearSnooze(): Promise<void>;
|
|
33
33
|
updateChime(update: ChimeUpdate): Promise<boolean>;
|
package/lib/ring-chime.js
CHANGED
|
@@ -1,30 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const ring_types_1 = require("./ring-types");
|
|
5
|
-
const rest_client_1 = require("./rest-client");
|
|
6
|
-
const rxjs_1 = require("rxjs");
|
|
1
|
+
import { ChimeModel } from "./ring-types.js";
|
|
2
|
+
import { clientApi } from "./rest-client.js";
|
|
3
|
+
import { BehaviorSubject, Subject } from 'rxjs';
|
|
7
4
|
const settingsWhichRequireReboot = [
|
|
8
5
|
'ding_audio_id',
|
|
9
6
|
'ding_audio_user_id',
|
|
10
7
|
'motion_audio_id',
|
|
11
8
|
'motion_audio_user_id',
|
|
12
9
|
];
|
|
13
|
-
class RingChime {
|
|
14
|
-
initialData;
|
|
15
|
-
restClient;
|
|
10
|
+
export class RingChime {
|
|
16
11
|
id;
|
|
17
12
|
deviceType;
|
|
18
13
|
model;
|
|
19
14
|
onData;
|
|
20
|
-
onRequestUpdate = new
|
|
15
|
+
onRequestUpdate = new Subject();
|
|
16
|
+
initialData;
|
|
17
|
+
restClient;
|
|
21
18
|
constructor(initialData, restClient) {
|
|
22
19
|
this.initialData = initialData;
|
|
23
20
|
this.restClient = restClient;
|
|
24
|
-
this.id =
|
|
25
|
-
this.deviceType =
|
|
26
|
-
this.model =
|
|
27
|
-
this.onData = new
|
|
21
|
+
this.id = initialData.id;
|
|
22
|
+
this.deviceType = initialData.kind;
|
|
23
|
+
this.model = ChimeModel[this.deviceType] || 'Chime';
|
|
24
|
+
this.onData = new BehaviorSubject(this.initialData);
|
|
28
25
|
}
|
|
29
26
|
updateData(update) {
|
|
30
27
|
this.onData.next(update);
|
|
@@ -46,7 +43,7 @@ class RingChime {
|
|
|
46
43
|
}
|
|
47
44
|
getRingtones() {
|
|
48
45
|
return this.restClient.request({
|
|
49
|
-
url:
|
|
46
|
+
url: clientApi('ringtones'),
|
|
50
47
|
});
|
|
51
48
|
}
|
|
52
49
|
async getRingtoneByDescription(description, kind) {
|
|
@@ -59,7 +56,7 @@ class RingChime {
|
|
|
59
56
|
return requestedRingtone;
|
|
60
57
|
}
|
|
61
58
|
chimeUrl(path = '') {
|
|
62
|
-
return
|
|
59
|
+
return clientApi(`chimes/${this.id}/${path}`);
|
|
63
60
|
}
|
|
64
61
|
playSound(kind) {
|
|
65
62
|
return this.restClient.request({
|
|
@@ -106,9 +103,8 @@ class RingChime {
|
|
|
106
103
|
}
|
|
107
104
|
async getHealth() {
|
|
108
105
|
const response = await this.restClient.request({
|
|
109
|
-
url:
|
|
106
|
+
url: clientApi(`chimes/${this.id}/health`),
|
|
110
107
|
});
|
|
111
108
|
return response.device_health;
|
|
112
109
|
}
|
|
113
110
|
}
|
|
114
|
-
exports.RingChime = RingChime;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const device_data_1 = require("./device-data");
|
|
5
|
-
(0, device_data_1.logDeviceData)().catch((e) => {
|
|
2
|
+
import { logDeviceData } from "./device-data.js";
|
|
3
|
+
logDeviceData().catch((e) => {
|
|
6
4
|
// eslint-disable-next-line no-console
|
|
7
5
|
console.error(e);
|
|
8
6
|
process.exit(1);
|
package/lib/ring-device.d.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { BehaviorSubject } from 'rxjs';
|
|
2
|
-
import { RingDeviceData } from './ring-types';
|
|
3
|
-
import { Location } from './location';
|
|
4
|
-
import { Subscribed } from './subscribed';
|
|
2
|
+
import type { RingDeviceData } from './ring-types.ts';
|
|
3
|
+
import type { Location } from './location.ts';
|
|
4
|
+
import { Subscribed } from './subscribed.ts';
|
|
5
5
|
export declare class RingDevice extends Subscribed {
|
|
6
|
-
private initialData;
|
|
7
|
-
location: Location;
|
|
8
|
-
assetId: string;
|
|
9
6
|
onData: BehaviorSubject<RingDeviceData>;
|
|
10
7
|
zid: string;
|
|
11
8
|
id: string;
|
|
12
|
-
deviceType: import("./ring-types").RingDeviceType;
|
|
9
|
+
deviceType: import("./ring-types.ts").RingDeviceType;
|
|
13
10
|
categoryId: number;
|
|
14
11
|
onComponentDevices: import("rxjs").Observable<RingDevice[]>;
|
|
12
|
+
private initialData;
|
|
13
|
+
location: Location;
|
|
14
|
+
assetId: string;
|
|
15
15
|
constructor(initialData: RingDeviceData, location: Location, assetId: string);
|
|
16
16
|
updateData(update: Partial<RingDeviceData>): void;
|
|
17
17
|
get data(): RingDeviceData;
|
package/lib/ring-device.js
CHANGED
|
@@ -1,34 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const subscribed_1 = require("./subscribed");
|
|
8
|
-
const util_1 = require("./util");
|
|
9
|
-
class RingDevice extends subscribed_1.Subscribed {
|
|
10
|
-
initialData;
|
|
11
|
-
location;
|
|
12
|
-
assetId;
|
|
1
|
+
import { BehaviorSubject } from 'rxjs';
|
|
2
|
+
import { deviceTypesWithVolume } from "./ring-types.js";
|
|
3
|
+
import { filter, map } from 'rxjs/operators';
|
|
4
|
+
import { Subscribed } from "./subscribed.js";
|
|
5
|
+
import { logError } from "./util.js";
|
|
6
|
+
export class RingDevice extends Subscribed {
|
|
13
7
|
onData;
|
|
14
8
|
zid;
|
|
15
9
|
id;
|
|
16
10
|
deviceType;
|
|
17
11
|
categoryId;
|
|
18
12
|
onComponentDevices;
|
|
13
|
+
initialData;
|
|
14
|
+
location;
|
|
15
|
+
assetId;
|
|
19
16
|
constructor(initialData, location, assetId) {
|
|
20
17
|
super();
|
|
21
18
|
this.initialData = initialData;
|
|
22
19
|
this.location = location;
|
|
23
20
|
this.assetId = assetId;
|
|
24
|
-
this.onData = new
|
|
25
|
-
this.zid =
|
|
21
|
+
this.onData = new BehaviorSubject(initialData);
|
|
22
|
+
this.zid = initialData.zid;
|
|
26
23
|
this.id = this.zid;
|
|
27
24
|
this.deviceType = this.initialData.deviceType;
|
|
28
25
|
this.categoryId = this.initialData.categoryId;
|
|
29
|
-
this.onComponentDevices = this.location.onDevices.pipe(
|
|
26
|
+
this.onComponentDevices = this.location.onDevices.pipe(map((devices) => devices.filter(({ data }) => data.parentZid === this.id)));
|
|
30
27
|
this.addSubscriptions(location.onDeviceDataUpdate
|
|
31
|
-
.pipe(
|
|
28
|
+
.pipe(filter((update) => update.zid === this.zid))
|
|
32
29
|
.subscribe((update) => this.updateData(update)));
|
|
33
30
|
}
|
|
34
31
|
updateData(update) {
|
|
@@ -41,7 +38,7 @@ class RingDevice extends subscribed_1.Subscribed {
|
|
|
41
38
|
return this.data.name;
|
|
42
39
|
}
|
|
43
40
|
get supportsVolume() {
|
|
44
|
-
return (
|
|
41
|
+
return (deviceTypesWithVolume.includes(this.data.deviceType) &&
|
|
45
42
|
this.data.volume !== undefined);
|
|
46
43
|
}
|
|
47
44
|
setVolume(volume) {
|
|
@@ -49,7 +46,7 @@ class RingDevice extends subscribed_1.Subscribed {
|
|
|
49
46
|
throw new Error('Volume must be between 0 and 1');
|
|
50
47
|
}
|
|
51
48
|
if (!this.supportsVolume) {
|
|
52
|
-
throw new Error(`Volume can only be set on ${
|
|
49
|
+
throw new Error(`Volume can only be set on ${deviceTypesWithVolume.join(', ')}`);
|
|
53
50
|
}
|
|
54
51
|
return this.setInfo({ device: { v1: { volume } } });
|
|
55
52
|
}
|
|
@@ -76,7 +73,7 @@ class RingDevice extends subscribed_1.Subscribed {
|
|
|
76
73
|
},
|
|
77
74
|
],
|
|
78
75
|
},
|
|
79
|
-
}).catch(
|
|
76
|
+
}).catch(logError);
|
|
80
77
|
}
|
|
81
78
|
toString() {
|
|
82
79
|
return this.toJSON();
|
|
@@ -90,4 +87,3 @@ class RingDevice extends subscribed_1.Subscribed {
|
|
|
90
87
|
this.unsubscribe();
|
|
91
88
|
}
|
|
92
89
|
}
|
|
93
|
-
exports.RingDevice = RingDevice;
|