ring-client-api 13.2.1-beta.1 → 14.0.0-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/lib/api.d.ts +9 -7
- package/lib/api.js +56 -63
- 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 +22 -21
- package/lib/location.js +61 -65
- package/lib/refresh-token.js +6 -10
- package/lib/rest-client.d.ts +3 -3
- package/lib/rest-client.js +39 -50
- package/lib/ring-auth-cli.js +2 -4
- package/lib/ring-camera.d.ts +21 -18
- package/lib/ring-camera.js +57 -56
- package/lib/ring-chime.d.ts +5 -5
- package/lib/ring-chime.js +10 -14
- package/lib/ring-device-data-cli.js +2 -4
- package/lib/ring-device.d.ts +4 -4
- package/lib/ring-device.js +12 -16
- package/lib/ring-intercom.d.ts +6 -6
- package/lib/ring-intercom.js +19 -23
- package/lib/ring-types.d.ts +2 -0
- package/lib/ring-types.js +24 -24
- 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 +2 -2
- package/lib/streaming/simple-webrtc-session.js +3 -7
- package/lib/streaming/streaming-messages.js +1 -2
- package/lib/streaming/streaming-session.d.ts +3 -3
- package/lib/streaming/streaming-session.js +34 -38
- package/lib/streaming/webrtc-connection.d.ts +4 -4
- package/lib/streaming/webrtc-connection.js +38 -42
- package/lib/subscribed.d.ts +1 -1
- package/lib/subscribed.js +1 -5
- package/lib/util.js +33 -54
- package/package.json +14 -16
- package/lib/test/rest-client.spec.d.ts +0 -1
- package/lib/test/rest-client.spec.js +0 -242
- package/lib/test/ring-camera.spec.d.ts +0 -1
- package/lib/test/ring-camera.spec.js +0 -37
package/lib/ring-intercom.js
CHANGED
|
@@ -1,33 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const ring_camera_1 = require("./ring-camera");
|
|
9
|
-
const util_1 = require("./util");
|
|
10
|
-
class RingIntercom {
|
|
1
|
+
import { PushNotificationAction } from "./ring-types.js";
|
|
2
|
+
import { clientApi, commandsApi } from "./rest-client.js";
|
|
3
|
+
import { BehaviorSubject, Subject } from 'rxjs';
|
|
4
|
+
import { distinctUntilChanged, map } from 'rxjs/operators';
|
|
5
|
+
import { getBatteryLevel } from "./ring-camera.js";
|
|
6
|
+
import { logError } from "./util.js";
|
|
7
|
+
export class RingIntercom {
|
|
11
8
|
initialData;
|
|
12
9
|
restClient;
|
|
13
10
|
id;
|
|
14
11
|
deviceType;
|
|
15
12
|
onData;
|
|
16
|
-
onRequestUpdate = new
|
|
13
|
+
onRequestUpdate = new Subject();
|
|
17
14
|
onBatteryLevel;
|
|
18
|
-
onDing = new
|
|
19
|
-
onUnlocked = new
|
|
15
|
+
onDing = new Subject();
|
|
16
|
+
onUnlocked = new Subject();
|
|
20
17
|
constructor(initialData, restClient) {
|
|
21
18
|
this.initialData = initialData;
|
|
22
19
|
this.restClient = restClient;
|
|
23
20
|
this.id = this.initialData.id;
|
|
24
21
|
this.deviceType = this.initialData.kind;
|
|
25
|
-
this.onData = new
|
|
26
|
-
this.onBatteryLevel = this.onData.pipe(
|
|
22
|
+
this.onData = new BehaviorSubject(this.initialData);
|
|
23
|
+
this.onBatteryLevel = this.onData.pipe(map((data) => getBatteryLevel(data)), distinctUntilChanged());
|
|
27
24
|
if (!initialData.subscribed) {
|
|
28
25
|
this.subscribeToDingEvents().catch((e) => {
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
logError('Failed to subscribe ' + initialData.description + ' to ding events');
|
|
27
|
+
logError(e);
|
|
31
28
|
});
|
|
32
29
|
}
|
|
33
30
|
}
|
|
@@ -47,12 +44,12 @@ class RingIntercom {
|
|
|
47
44
|
return this.data.alerts.connection === 'offline';
|
|
48
45
|
}
|
|
49
46
|
get batteryLevel() {
|
|
50
|
-
return
|
|
47
|
+
return getBatteryLevel(this.data);
|
|
51
48
|
}
|
|
52
49
|
unlock() {
|
|
53
50
|
return this.restClient.request({
|
|
54
51
|
method: 'PUT',
|
|
55
|
-
url:
|
|
52
|
+
url: commandsApi(`devices/${this.id}/device_rpc`),
|
|
56
53
|
json: {
|
|
57
54
|
command_name: 'device_rpc',
|
|
58
55
|
request: {
|
|
@@ -67,7 +64,7 @@ class RingIntercom {
|
|
|
67
64
|
});
|
|
68
65
|
}
|
|
69
66
|
doorbotUrl(path = '') {
|
|
70
|
-
return
|
|
67
|
+
return clientApi(`doorbots/${this.id}/${path}`);
|
|
71
68
|
}
|
|
72
69
|
subscribeToDingEvents() {
|
|
73
70
|
return this.restClient.request({
|
|
@@ -84,13 +81,12 @@ class RingIntercom {
|
|
|
84
81
|
processPushNotification(notification) {
|
|
85
82
|
if ('android_config' in notification &&
|
|
86
83
|
notification.android_config.category ===
|
|
87
|
-
|
|
84
|
+
PushNotificationAction.IntercomDing) {
|
|
88
85
|
this.onDing.next();
|
|
89
86
|
}
|
|
90
87
|
else if ('gcmData' in notification.data &&
|
|
91
|
-
notification.data.gcmData.action ===
|
|
88
|
+
notification.data.gcmData.action === PushNotificationAction.IntercomUnlock) {
|
|
92
89
|
this.onUnlocked.next();
|
|
93
90
|
}
|
|
94
91
|
}
|
|
95
92
|
}
|
|
96
|
-
exports.RingIntercom = RingIntercom;
|
package/lib/ring-types.d.ts
CHANGED
|
@@ -94,6 +94,8 @@ export declare enum RingCameraKind {
|
|
|
94
94
|
floodlight_pro = "floodlight_pro",
|
|
95
95
|
cocoa_camera = "cocoa_camera",// appears to be used for all next gen stickup cams (wired/battery/solar)
|
|
96
96
|
cocoa_doorbell = "cocoa_doorbell",
|
|
97
|
+
cocoa_doorbell_v2 = "cocoa_doorbell_v2",
|
|
98
|
+
cocoa_doorbell_v3 = "cocoa_doorbell_v3",
|
|
97
99
|
cocoa_floodlight = "cocoa_floodlight",
|
|
98
100
|
cocoa_spotlight = "cocoa_spotlight",// used for the Spotlight Cam Plus (potentially other Spotlight models)
|
|
99
101
|
stickup_cam_mini = "stickup_cam_mini",
|
package/lib/ring-types.js
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.disabledLocationModes = exports.DispatchSignalType = exports.PushNotificationAction = exports.NotificationDetectionType = exports.DoorbellType = exports.ChimeModel = exports.deviceTypesWithVolume = exports.allAlarmStates = exports.RingCameraModel = exports.RingCameraKind = exports.RingDeviceCategory = exports.RingDeviceType = void 0;
|
|
4
|
-
exports.isWebSocketSupportedAsset = isWebSocketSupportedAsset;
|
|
5
1
|
// eslint-disable-next-line no-shadow
|
|
6
|
-
var RingDeviceType;
|
|
2
|
+
export var RingDeviceType;
|
|
7
3
|
(function (RingDeviceType) {
|
|
8
4
|
RingDeviceType["BaseStation"] = "hub.redsky";
|
|
9
5
|
RingDeviceType["BaseStationPro"] = "hub.kili";
|
|
@@ -49,9 +45,9 @@ var RingDeviceType;
|
|
|
49
45
|
RingDeviceType["ThirdPartyGarageDoorOpener"] = "third_party_gdo";
|
|
50
46
|
RingDeviceType["IntercomHandsetAudio"] = "intercom_handset_audio";
|
|
51
47
|
RingDeviceType["WaterValve"] = "valve.water";
|
|
52
|
-
})(RingDeviceType || (
|
|
48
|
+
})(RingDeviceType || (RingDeviceType = {}));
|
|
53
49
|
// eslint-disable-next-line no-shadow
|
|
54
|
-
var RingDeviceCategory;
|
|
50
|
+
export var RingDeviceCategory;
|
|
55
51
|
(function (RingDeviceCategory) {
|
|
56
52
|
RingDeviceCategory[RingDeviceCategory["Outlets"] = 1] = "Outlets";
|
|
57
53
|
RingDeviceCategory[RingDeviceCategory["Lights"] = 2] = "Lights";
|
|
@@ -71,9 +67,9 @@ var RingDeviceCategory;
|
|
|
71
67
|
RingDeviceCategory[RingDeviceCategory["Sirens"] = 34] = "Sirens";
|
|
72
68
|
RingDeviceCategory[RingDeviceCategory["PanicButtons"] = 35] = "PanicButtons";
|
|
73
69
|
RingDeviceCategory[RingDeviceCategory["WaterValves"] = 37] = "WaterValves";
|
|
74
|
-
})(RingDeviceCategory || (
|
|
70
|
+
})(RingDeviceCategory || (RingDeviceCategory = {}));
|
|
75
71
|
// eslint-disable-next-line no-shadow
|
|
76
|
-
var RingCameraKind;
|
|
72
|
+
export var RingCameraKind;
|
|
77
73
|
(function (RingCameraKind) {
|
|
78
74
|
RingCameraKind["doorbot"] = "doorbot";
|
|
79
75
|
RingCameraKind["doorbell"] = "doorbell";
|
|
@@ -103,13 +99,15 @@ var RingCameraKind;
|
|
|
103
99
|
RingCameraKind["floodlight_pro"] = "floodlight_pro";
|
|
104
100
|
RingCameraKind["cocoa_camera"] = "cocoa_camera";
|
|
105
101
|
RingCameraKind["cocoa_doorbell"] = "cocoa_doorbell";
|
|
102
|
+
RingCameraKind["cocoa_doorbell_v2"] = "cocoa_doorbell_v2";
|
|
103
|
+
RingCameraKind["cocoa_doorbell_v3"] = "cocoa_doorbell_v3";
|
|
106
104
|
RingCameraKind["cocoa_floodlight"] = "cocoa_floodlight";
|
|
107
105
|
RingCameraKind["cocoa_spotlight"] = "cocoa_spotlight";
|
|
108
106
|
RingCameraKind["stickup_cam_mini"] = "stickup_cam_mini";
|
|
109
107
|
RingCameraKind["onvif_camera"] = "onvif_camera";
|
|
110
|
-
})(RingCameraKind || (
|
|
108
|
+
})(RingCameraKind || (RingCameraKind = {}));
|
|
111
109
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
112
|
-
|
|
110
|
+
export const RingCameraModel = {
|
|
113
111
|
doorbot: 'Doorbell',
|
|
114
112
|
doorbell: 'Doorbell',
|
|
115
113
|
doorbell_v3: 'Doorbell',
|
|
@@ -138,12 +136,14 @@ exports.RingCameraModel = {
|
|
|
138
136
|
floodlight_pro: 'Floodlight Pro',
|
|
139
137
|
cocoa_camera: 'Stick Up Cam',
|
|
140
138
|
cocoa_doorbell: 'Doorbell Gen 2',
|
|
139
|
+
cocoa_doorbell_v2: 'Battery Doorbell Plus',
|
|
140
|
+
cocoa_doorbell_v3: 'Battery Doorbell Pro',
|
|
141
141
|
cocoa_floodlight: 'Floodlight Cam Plus',
|
|
142
142
|
cocoa_spotlight: 'Spotlight Cam Plus',
|
|
143
143
|
stickup_cam_mini: 'Indoor Cam',
|
|
144
144
|
onvif_camera: 'ONVIF Camera',
|
|
145
145
|
};
|
|
146
|
-
|
|
146
|
+
export const allAlarmStates = [
|
|
147
147
|
'burglar-alarm',
|
|
148
148
|
'entry-delay',
|
|
149
149
|
'fire-alarm',
|
|
@@ -154,26 +154,26 @@ exports.allAlarmStates = [
|
|
|
154
154
|
'burglar-accelerated-alarm',
|
|
155
155
|
'fire-accelerated-alarm',
|
|
156
156
|
];
|
|
157
|
-
|
|
157
|
+
export const deviceTypesWithVolume = [
|
|
158
158
|
RingDeviceType.BaseStation,
|
|
159
159
|
RingDeviceType.Keypad,
|
|
160
160
|
];
|
|
161
161
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
162
|
-
|
|
162
|
+
export const ChimeModel = {
|
|
163
163
|
chime: 'Chime',
|
|
164
164
|
chime_pro: 'Chime Pro',
|
|
165
165
|
chime_v2: 'Chime v2',
|
|
166
166
|
chime_pro_v2: 'Chime Pro v2',
|
|
167
167
|
};
|
|
168
168
|
// eslint-disable-next-line no-shadow
|
|
169
|
-
var DoorbellType;
|
|
169
|
+
export var DoorbellType;
|
|
170
170
|
(function (DoorbellType) {
|
|
171
171
|
DoorbellType[DoorbellType["Mechanical"] = 0] = "Mechanical";
|
|
172
172
|
DoorbellType[DoorbellType["Digital"] = 1] = "Digital";
|
|
173
173
|
DoorbellType[DoorbellType["None"] = 2] = "None";
|
|
174
|
-
})(DoorbellType || (
|
|
174
|
+
})(DoorbellType || (DoorbellType = {}));
|
|
175
175
|
// eslint-disable-next-line no-shadow
|
|
176
|
-
var NotificationDetectionType;
|
|
176
|
+
export var NotificationDetectionType;
|
|
177
177
|
(function (NotificationDetectionType) {
|
|
178
178
|
// Note, this list may not be complete
|
|
179
179
|
NotificationDetectionType["Human"] = "human";
|
|
@@ -182,9 +182,9 @@ var NotificationDetectionType;
|
|
|
182
182
|
NotificationDetectionType["OtherMotion"] = "other_motion";
|
|
183
183
|
NotificationDetectionType["NotAvailable"] = "null";
|
|
184
184
|
NotificationDetectionType["StreamBroken"] = "stream_broken";
|
|
185
|
-
})(NotificationDetectionType || (
|
|
185
|
+
})(NotificationDetectionType || (NotificationDetectionType = {}));
|
|
186
186
|
// eslint-disable-next-line no-shadow
|
|
187
|
-
var PushNotificationAction;
|
|
187
|
+
export var PushNotificationAction;
|
|
188
188
|
(function (PushNotificationAction) {
|
|
189
189
|
PushNotificationAction["Ding"] = "com.ring.pn.live-event.ding";
|
|
190
190
|
PushNotificationAction["Motion"] = "com.ring.pn.live-event.motion";
|
|
@@ -194,14 +194,14 @@ var PushNotificationAction;
|
|
|
194
194
|
PushNotificationAction["AlarmModeSome"] = "com.ring.push.HANDLE_NEW_SECURITY_PANEL_MODE_SOME_NOTICE";
|
|
195
195
|
PushNotificationAction["AlarmSoundSiren"] = "com.ring.push.HANDLE_NEW_USER_SOUND_SIREN";
|
|
196
196
|
PushNotificationAction["AlarmSilenceSiren"] = "com.ring.push.HANDLE_NEW_NON_ALARM_SIREN_SILENCED";
|
|
197
|
-
})(PushNotificationAction || (
|
|
197
|
+
})(PushNotificationAction || (PushNotificationAction = {}));
|
|
198
198
|
// eslint-disable-next-line no-shadow
|
|
199
|
-
var DispatchSignalType;
|
|
199
|
+
export var DispatchSignalType;
|
|
200
200
|
(function (DispatchSignalType) {
|
|
201
201
|
DispatchSignalType["Burglar"] = "user-verified-burglar-xa";
|
|
202
202
|
DispatchSignalType["Fire"] = "user-verified-fire-xa";
|
|
203
|
-
})(DispatchSignalType || (
|
|
204
|
-
|
|
205
|
-
function isWebSocketSupportedAsset({ kind }) {
|
|
203
|
+
})(DispatchSignalType || (DispatchSignalType = {}));
|
|
204
|
+
export const disabledLocationModes = ['disabled', 'unset'];
|
|
205
|
+
export function isWebSocketSupportedAsset({ kind }) {
|
|
206
206
|
return kind.startsWith('base_station') || kind.startsWith('beams_bridge');
|
|
207
207
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import type { RTCIceCandidate, RtpPacket } from 'werift';
|
|
2
|
+
import { type ConnectionState, MediaStreamTrack, type RtcpPacket } from 'werift';
|
|
3
|
+
import type { Observable } from 'rxjs';
|
|
4
|
+
import { ReplaySubject, Subject } from 'rxjs';
|
|
5
|
+
import { Subscribed } from '../subscribed.ts';
|
|
4
6
|
export interface BasicPeerConnection {
|
|
5
7
|
createOffer(): Promise<{
|
|
6
8
|
sdp: string;
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const werift_1 = require("werift");
|
|
6
|
-
const rxjs_1 = require("rxjs");
|
|
7
|
-
const util_1 = require("../util");
|
|
8
|
-
const subscribed_1 = require("../subscribed");
|
|
1
|
+
import { MediaStreamTrack, RTCPeerConnection, RTCRtpCodecParameters, } from 'werift';
|
|
2
|
+
import { interval, merge, ReplaySubject, Subject } from 'rxjs';
|
|
3
|
+
import { logDebug, logError, logInfo } from "../util.js";
|
|
4
|
+
import { Subscribed } from "../subscribed.js";
|
|
9
5
|
const ringIceServers = [
|
|
10
6
|
'stun:stun.kinesisvideo.us-east-1.amazonaws.com:443',
|
|
11
7
|
'stun:stun.kinesisvideo.us-east-2.amazonaws.com:443',
|
|
@@ -16,27 +12,27 @@ const ringIceServers = [
|
|
|
16
12
|
'stun:stun3.l.google.com:19302',
|
|
17
13
|
'stun:stun4.l.google.com:19302',
|
|
18
14
|
];
|
|
19
|
-
class WeriftPeerConnection extends
|
|
15
|
+
export class WeriftPeerConnection extends Subscribed {
|
|
20
16
|
pc;
|
|
21
|
-
onAudioRtp = new
|
|
22
|
-
onAudioRtcp = new
|
|
23
|
-
onVideoRtp = new
|
|
24
|
-
onVideoRtcp = new
|
|
25
|
-
onIceCandidate = new
|
|
26
|
-
onConnectionState = new
|
|
27
|
-
returnAudioTrack = new
|
|
28
|
-
onRequestKeyFrame = new
|
|
17
|
+
onAudioRtp = new Subject();
|
|
18
|
+
onAudioRtcp = new Subject();
|
|
19
|
+
onVideoRtp = new Subject();
|
|
20
|
+
onVideoRtcp = new Subject();
|
|
21
|
+
onIceCandidate = new Subject();
|
|
22
|
+
onConnectionState = new ReplaySubject(1);
|
|
23
|
+
returnAudioTrack = new MediaStreamTrack({ kind: 'audio' });
|
|
24
|
+
onRequestKeyFrame = new Subject();
|
|
29
25
|
constructor() {
|
|
30
26
|
super();
|
|
31
|
-
const pc = (this.pc = new
|
|
27
|
+
const pc = (this.pc = new RTCPeerConnection({
|
|
32
28
|
codecs: {
|
|
33
29
|
audio: [
|
|
34
|
-
new
|
|
30
|
+
new RTCRtpCodecParameters({
|
|
35
31
|
mimeType: 'audio/opus',
|
|
36
32
|
clockRate: 48000,
|
|
37
33
|
channels: 2,
|
|
38
34
|
}),
|
|
39
|
-
new
|
|
35
|
+
new RTCRtpCodecParameters({
|
|
40
36
|
mimeType: 'audio/PCMU',
|
|
41
37
|
clockRate: 8000,
|
|
42
38
|
channels: 1,
|
|
@@ -44,7 +40,7 @@ class WeriftPeerConnection extends subscribed_1.Subscribed {
|
|
|
44
40
|
}),
|
|
45
41
|
],
|
|
46
42
|
video: [
|
|
47
|
-
new
|
|
43
|
+
new RTCRtpCodecParameters({
|
|
48
44
|
mimeType: 'video/H264',
|
|
49
45
|
clockRate: 90000,
|
|
50
46
|
rtcpFeedback: [
|
|
@@ -56,7 +52,7 @@ class WeriftPeerConnection extends subscribed_1.Subscribed {
|
|
|
56
52
|
],
|
|
57
53
|
parameters: 'packetization-mode=1;profile-level-id=640029;level-asymmetry-allowed=1',
|
|
58
54
|
}),
|
|
59
|
-
new
|
|
55
|
+
new RTCRtpCodecParameters({
|
|
60
56
|
mimeType: 'video/rtx',
|
|
61
57
|
clockRate: 90000,
|
|
62
58
|
}),
|
|
@@ -78,7 +74,7 @@ class WeriftPeerConnection extends subscribed_1.Subscribed {
|
|
|
78
74
|
this.onAudioRtcp.next(rtcp);
|
|
79
75
|
});
|
|
80
76
|
track.onReceiveRtp.once(() => {
|
|
81
|
-
|
|
77
|
+
logDebug('received first audio packet');
|
|
82
78
|
});
|
|
83
79
|
});
|
|
84
80
|
videoTransceiver.onTrack.subscribe((track) => {
|
|
@@ -89,11 +85,11 @@ class WeriftPeerConnection extends subscribed_1.Subscribed {
|
|
|
89
85
|
this.onVideoRtcp.next(rtcp);
|
|
90
86
|
});
|
|
91
87
|
track.onReceiveRtp.once(() => {
|
|
92
|
-
|
|
93
|
-
this.addSubscriptions(
|
|
88
|
+
logDebug('received first video packet');
|
|
89
|
+
this.addSubscriptions(merge(this.onRequestKeyFrame, interval(4000)).subscribe(() => {
|
|
94
90
|
videoTransceiver.receiver
|
|
95
91
|
.sendRtcpPLI(track.ssrc)
|
|
96
|
-
.catch((e) =>
|
|
92
|
+
.catch((e) => logError(e));
|
|
97
93
|
}));
|
|
98
94
|
this.requestKeyFrame();
|
|
99
95
|
});
|
|
@@ -104,13 +100,13 @@ class WeriftPeerConnection extends subscribed_1.Subscribed {
|
|
|
104
100
|
}
|
|
105
101
|
});
|
|
106
102
|
pc.iceConnectionStateChange.subscribe(() => {
|
|
107
|
-
|
|
103
|
+
logInfo(`iceConnectionStateChange: ${pc.iceConnectionState}`);
|
|
108
104
|
if (pc.iceConnectionState === 'closed') {
|
|
109
105
|
this.onConnectionState.next('closed');
|
|
110
106
|
}
|
|
111
107
|
});
|
|
112
108
|
pc.connectionStateChange.subscribe(() => {
|
|
113
|
-
|
|
109
|
+
logInfo(`connectionStateChange: ${pc.connectionState}`);
|
|
114
110
|
this.onConnectionState.next(pc.connectionState);
|
|
115
111
|
});
|
|
116
112
|
}
|
|
@@ -129,8 +125,7 @@ class WeriftPeerConnection extends subscribed_1.Subscribed {
|
|
|
129
125
|
this.onRequestKeyFrame.next();
|
|
130
126
|
}
|
|
131
127
|
close() {
|
|
132
|
-
this.pc.close().catch(
|
|
128
|
+
this.pc.close().catch(logError);
|
|
133
129
|
this.unsubscribe();
|
|
134
130
|
}
|
|
135
131
|
}
|
|
136
|
-
exports.WeriftPeerConnection = WeriftPeerConnection;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { RingCamera } from '../ring-camera';
|
|
2
|
-
import { RingRestClient } from '../rest-client';
|
|
1
|
+
import type { RingCamera } from '../ring-camera.ts';
|
|
2
|
+
import type { RingRestClient } from '../rest-client.ts';
|
|
3
3
|
export declare class SimpleWebRtcSession {
|
|
4
4
|
private readonly camera;
|
|
5
5
|
private restClient;
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SimpleWebRtcSession = void 0;
|
|
4
|
-
const util_1 = require("../util");
|
|
1
|
+
import { generateUuid } from "../util.js";
|
|
5
2
|
function liveViewUrl(path) {
|
|
6
3
|
return `https://api.ring.com/integrations/v1/liveview/${path}`;
|
|
7
4
|
}
|
|
8
|
-
class SimpleWebRtcSession {
|
|
5
|
+
export class SimpleWebRtcSession {
|
|
9
6
|
camera;
|
|
10
7
|
restClient;
|
|
11
|
-
sessionId =
|
|
8
|
+
sessionId = generateUuid();
|
|
12
9
|
constructor(camera, restClient) {
|
|
13
10
|
this.camera = camera;
|
|
14
11
|
this.restClient = restClient;
|
|
@@ -47,4 +44,3 @@ class SimpleWebRtcSession {
|
|
|
47
44
|
});
|
|
48
45
|
}
|
|
49
46
|
}
|
|
50
|
-
exports.SimpleWebRtcSession = SimpleWebRtcSession;
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
1
|
// eslint-disable-next-line no-shadow
|
|
4
2
|
var CloseReasonCode;
|
|
5
3
|
(function (CloseReasonCode) {
|
|
@@ -11,3 +9,4 @@ var CloseReasonCode;
|
|
|
11
9
|
// reason: { code: 6, text: 'Timeout waiting for ping' }
|
|
12
10
|
CloseReasonCode[CloseReasonCode["Timeout"] = 6] = "Timeout";
|
|
13
11
|
})(CloseReasonCode || (CloseReasonCode = {}));
|
|
12
|
+
export {};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { RtpPacket } from 'werift';
|
|
2
2
|
import { ReplaySubject, Subject } from 'rxjs';
|
|
3
|
-
import { WebrtcConnection } from './webrtc-connection';
|
|
4
|
-
import { RingCamera } from '../ring-camera';
|
|
5
|
-
import { Subscribed } from '../subscribed';
|
|
3
|
+
import type { WebrtcConnection } from './webrtc-connection.ts';
|
|
4
|
+
import type { RingCamera } from '../ring-camera.ts';
|
|
5
|
+
import { Subscribed } from '../subscribed.ts';
|
|
6
6
|
type SpawnInput = string | number;
|
|
7
7
|
export interface FfmpegOptions {
|
|
8
8
|
input?: SpawnInput[];
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const util_1 = require("../util");
|
|
9
|
-
const operators_1 = require("rxjs/operators");
|
|
10
|
-
const subscribed_1 = require("../subscribed");
|
|
1
|
+
import { RtpPacket } from 'werift';
|
|
2
|
+
import { FfmpegProcess, reservePorts, RtpSplitter, } from '@homebridge/camera-utils';
|
|
3
|
+
import { firstValueFrom, ReplaySubject, Subject } from 'rxjs';
|
|
4
|
+
import { getFfmpegPath } from "../ffmpeg.js";
|
|
5
|
+
import { logDebug, logError } from "../util.js";
|
|
6
|
+
import { concatMap, map, mergeWith, take } from 'rxjs/operators';
|
|
7
|
+
import { Subscribed } from "../subscribed.js";
|
|
11
8
|
function getCleanSdp(sdp, includeVideo) {
|
|
12
9
|
return sdp
|
|
13
10
|
.split('\nm=')
|
|
@@ -16,16 +13,16 @@ function getCleanSdp(sdp, includeVideo) {
|
|
|
16
13
|
.filter((section) => includeVideo || !section.startsWith('m=video'))
|
|
17
14
|
.join('\n');
|
|
18
15
|
}
|
|
19
|
-
class StreamingSession extends
|
|
16
|
+
export class StreamingSession extends Subscribed {
|
|
20
17
|
camera;
|
|
21
18
|
connection;
|
|
22
|
-
onCallEnded = new
|
|
23
|
-
onUsingOpus = new
|
|
24
|
-
onVideoRtp = new
|
|
25
|
-
onAudioRtp = new
|
|
26
|
-
audioSplitter = new
|
|
27
|
-
videoSplitter = new
|
|
28
|
-
returnAudioSplitter = new
|
|
19
|
+
onCallEnded = new ReplaySubject(1);
|
|
20
|
+
onUsingOpus = new ReplaySubject(1);
|
|
21
|
+
onVideoRtp = new Subject();
|
|
22
|
+
onAudioRtp = new Subject();
|
|
23
|
+
audioSplitter = new RtpSplitter();
|
|
24
|
+
videoSplitter = new RtpSplitter();
|
|
25
|
+
returnAudioSplitter = new RtpSplitter();
|
|
29
26
|
constructor(camera, connection) {
|
|
30
27
|
super();
|
|
31
28
|
this.camera = camera;
|
|
@@ -53,11 +50,11 @@ class StreamingSession extends subscribed_1.Subscribed {
|
|
|
53
50
|
this.connection.activateCameraSpeaker();
|
|
54
51
|
}
|
|
55
52
|
async reservePort(bufferPorts = 0) {
|
|
56
|
-
const ports = await
|
|
53
|
+
const ports = await reservePorts({ count: bufferPorts + 1 });
|
|
57
54
|
return ports[0];
|
|
58
55
|
}
|
|
59
56
|
get isUsingOpus() {
|
|
60
|
-
return
|
|
57
|
+
return firstValueFrom(this.onUsingOpus.pipe(mergeWith(this.connection.onError.pipe(map((e) => {
|
|
61
58
|
throw e;
|
|
62
59
|
})))));
|
|
63
60
|
}
|
|
@@ -66,11 +63,11 @@ class StreamingSession extends subscribed_1.Subscribed {
|
|
|
66
63
|
return;
|
|
67
64
|
}
|
|
68
65
|
const videoPort = await this.reservePort(1), audioPort = await this.reservePort(1), transcodeVideoStream = ffmpegOptions.video !== false, ringSdp = await Promise.race([
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
firstValueFrom(this.connection.onCallAnswered),
|
|
67
|
+
firstValueFrom(this.onCallEnded),
|
|
71
68
|
]);
|
|
72
69
|
if (!ringSdp) {
|
|
73
|
-
|
|
70
|
+
logDebug('Call ended before answered');
|
|
74
71
|
return;
|
|
75
72
|
}
|
|
76
73
|
const usingOpus = await this.isUsingOpus, ffmpegInputArguments = [
|
|
@@ -86,21 +83,21 @@ class StreamingSession extends subscribed_1.Subscribed {
|
|
|
86
83
|
'pipe:',
|
|
87
84
|
], inputSdp = getCleanSdp(ringSdp, transcodeVideoStream)
|
|
88
85
|
.replace(/m=audio \d+/, `m=audio ${audioPort}`)
|
|
89
|
-
.replace(/m=video \d+/, `m=video ${videoPort}`), ff = new
|
|
86
|
+
.replace(/m=video \d+/, `m=video ${videoPort}`), ff = new FfmpegProcess({
|
|
90
87
|
ffmpegArgs: ffmpegInputArguments.concat(...(ffmpegOptions.audio || ['-acodec', 'aac']), ...(transcodeVideoStream
|
|
91
88
|
? ffmpegOptions.video || ['-vcodec', 'copy']
|
|
92
89
|
: []), ...(ffmpegOptions.output || [])),
|
|
93
|
-
ffmpegPath:
|
|
90
|
+
ffmpegPath: getFfmpegPath(),
|
|
94
91
|
stdoutCallback: ffmpegOptions.stdoutCallback,
|
|
95
92
|
exitCallback: () => this.callEnded(),
|
|
96
93
|
logLabel: `From Ring (${this.camera.name})`,
|
|
97
94
|
logger: {
|
|
98
|
-
error:
|
|
99
|
-
info:
|
|
95
|
+
error: logError,
|
|
96
|
+
info: logDebug,
|
|
100
97
|
},
|
|
101
98
|
});
|
|
102
99
|
this.addSubscriptions(this.onAudioRtp
|
|
103
|
-
.pipe(
|
|
100
|
+
.pipe(concatMap((rtp) => {
|
|
104
101
|
return this.audioSplitter.send(rtp.serialize(), {
|
|
105
102
|
port: audioPort,
|
|
106
103
|
});
|
|
@@ -108,14 +105,14 @@ class StreamingSession extends subscribed_1.Subscribed {
|
|
|
108
105
|
.subscribe());
|
|
109
106
|
if (transcodeVideoStream) {
|
|
110
107
|
this.addSubscriptions(this.onVideoRtp
|
|
111
|
-
.pipe(
|
|
108
|
+
.pipe(concatMap((rtp) => {
|
|
112
109
|
return this.videoSplitter.send(rtp.serialize(), {
|
|
113
110
|
port: videoPort,
|
|
114
111
|
});
|
|
115
112
|
}))
|
|
116
113
|
.subscribe());
|
|
117
114
|
}
|
|
118
|
-
this.onCallEnded.pipe(
|
|
115
|
+
this.onCallEnded.pipe(take(1)).subscribe(() => ff.stop());
|
|
119
116
|
ff.writeStdin(inputSdp);
|
|
120
117
|
// Request a key frame now that ffmpeg is ready to receive
|
|
121
118
|
this.requestKeyFrame();
|
|
@@ -124,11 +121,11 @@ class StreamingSession extends subscribed_1.Subscribed {
|
|
|
124
121
|
if (this.hasEnded) {
|
|
125
122
|
return;
|
|
126
123
|
}
|
|
127
|
-
const audioOutForwarder = new
|
|
128
|
-
const rtp =
|
|
124
|
+
const audioOutForwarder = new RtpSplitter(({ message }) => {
|
|
125
|
+
const rtp = RtpPacket.deSerialize(message);
|
|
129
126
|
this.connection.sendAudioPacket(rtp);
|
|
130
127
|
return null;
|
|
131
|
-
}), usingOpus = await this.isUsingOpus, ff = new
|
|
128
|
+
}), usingOpus = await this.isUsingOpus, ff = new FfmpegProcess({
|
|
132
129
|
ffmpegArgs: [
|
|
133
130
|
'-hide_banner',
|
|
134
131
|
'-protocol_whitelist',
|
|
@@ -146,15 +143,15 @@ class StreamingSession extends subscribed_1.Subscribed {
|
|
|
146
143
|
'rtp',
|
|
147
144
|
`rtp://127.0.0.1:${await audioOutForwarder.portPromise}`,
|
|
148
145
|
],
|
|
149
|
-
ffmpegPath:
|
|
146
|
+
ffmpegPath: getFfmpegPath(),
|
|
150
147
|
exitCallback: () => this.callEnded(),
|
|
151
148
|
logLabel: `Return Audio (${this.camera.name})`,
|
|
152
149
|
logger: {
|
|
153
|
-
error:
|
|
154
|
-
info:
|
|
150
|
+
error: logError,
|
|
151
|
+
info: logDebug,
|
|
155
152
|
},
|
|
156
153
|
});
|
|
157
|
-
this.onCallEnded.pipe(
|
|
154
|
+
this.onCallEnded.pipe(take(1)).subscribe(() => ff.stop());
|
|
158
155
|
}
|
|
159
156
|
hasEnded = false;
|
|
160
157
|
callEnded() {
|
|
@@ -182,4 +179,3 @@ class StreamingSession extends subscribed_1.Subscribed {
|
|
|
182
179
|
this.connection.requestKeyFrame();
|
|
183
180
|
}
|
|
184
181
|
}
|
|
185
|
-
exports.StreamingSession = StreamingSession;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ReplaySubject, Subject } from 'rxjs';
|
|
2
|
-
import { RingCamera } from '../ring-camera';
|
|
3
|
-
import { BasicPeerConnection } from './peer-connection';
|
|
4
|
-
import { Subscribed } from '../subscribed';
|
|
5
|
-
import { RtpPacket } from 'werift';
|
|
2
|
+
import type { RingCamera } from '../ring-camera.ts';
|
|
3
|
+
import { type BasicPeerConnection } from './peer-connection.ts';
|
|
4
|
+
import { Subscribed } from '../subscribed.ts';
|
|
5
|
+
import type { RtpPacket } from 'werift';
|
|
6
6
|
export interface StreamingConnectionOptions {
|
|
7
7
|
createPeerConnection?: () => BasicPeerConnection;
|
|
8
8
|
}
|