homebridge-ring 9.22.0-beta.0 → 9.24.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.
Files changed (44) hide show
  1. package/CHANGELOG.md +43 -2
  2. package/README.md +9 -1
  3. package/config.schema.json +11 -0
  4. package/lib/api/api.d.ts +1 -0
  5. package/lib/api/api.js +4 -2
  6. package/lib/api/location.js +11 -7
  7. package/lib/api/rest-client.d.ts +2 -0
  8. package/lib/api/rest-client.js +11 -4
  9. package/lib/api/ring-camera.d.ts +3 -2
  10. package/lib/api/ring-camera.js +25 -19
  11. package/lib/api/ring-chime.d.ts +1 -1
  12. package/lib/api/ring-chime.js +1 -1
  13. package/lib/api/ring-device.js +2 -1
  14. package/lib/api/ring-types.d.ts +2 -1
  15. package/lib/api/ring-types.js +1 -0
  16. package/lib/api/rtp-utils.js +4 -2
  17. package/lib/api/sip-call.js +1 -1
  18. package/lib/api/sip-session.js +3 -3
  19. package/lib/api/sip.d.ts +1 -0
  20. package/lib/api/sip.js +1 -0
  21. package/lib/api/util.js +5 -2
  22. package/lib/homebridge/base-accessory.js +1 -1
  23. package/lib/homebridge/base-device-accessory.js +1 -3
  24. package/lib/homebridge/camera-source.d.ts +1 -1
  25. package/lib/homebridge/camera-source.js +44 -25
  26. package/lib/homebridge/camera.js +1 -1
  27. package/lib/homebridge/config.d.ts +1 -0
  28. package/lib/homebridge/homebridge-ui/public/asset-manifest.json +10 -10
  29. package/lib/homebridge/homebridge-ui/public/index.html +1 -1
  30. package/lib/homebridge/homebridge-ui/public/static/js/2.fe3feb44.chunk.js +3 -0
  31. package/lib/homebridge/homebridge-ui/public/static/js/{2.01174b6e.chunk.js.LICENSE.txt → 2.fe3feb44.chunk.js.LICENSE.txt} +0 -0
  32. package/lib/homebridge/homebridge-ui/public/static/js/2.fe3feb44.chunk.js.map +1 -0
  33. package/lib/homebridge/homebridge-ui/public/static/js/main.adeecae1.chunk.js +2 -0
  34. package/lib/homebridge/homebridge-ui/public/static/js/main.adeecae1.chunk.js.map +1 -0
  35. package/lib/homebridge/homebridge-ui/public/static/js/{runtime-main.a9f07fed.js → runtime-main.401a8c6e.js} +1 -1
  36. package/lib/homebridge/homebridge-ui/public/static/js/runtime-main.401a8c6e.js.map +1 -0
  37. package/lib/homebridge/location-mode-switch.js +1 -1
  38. package/lib/homebridge/ring-platform.js +10 -4
  39. package/package.json +25 -22
  40. package/lib/homebridge/homebridge-ui/public/static/js/2.01174b6e.chunk.js +0 -3
  41. package/lib/homebridge/homebridge-ui/public/static/js/2.01174b6e.chunk.js.map +0 -1
  42. package/lib/homebridge/homebridge-ui/public/static/js/main.5f123226.chunk.js +0 -2
  43. package/lib/homebridge/homebridge-ui/public/static/js/main.5f123226.chunk.js.map +0 -1
  44. package/lib/homebridge/homebridge-ui/public/static/js/runtime-main.a9f07fed.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -2,12 +2,53 @@
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
- ## [9.22.0-beta.0](https://github.com/dgreif/ring/compare/v9.21.2...v9.22.0-beta.0) (2021-09-26)
5
+ ## [9.24.0](https://github.com/dgreif/ring/compare/v9.23.0...v9.24.0) (2022-01-22)
6
6
 
7
7
 
8
8
  ### Features
9
9
 
10
- * support unknown zwave switch ([45ee375](https://github.com/dgreif/ring/commit/45ee37508436bb84b64c7816f228620333934f25)), closes [#775](https://github.com/dgreif/ring/issues/775)
10
+ * support glassbreak sensor ([98271f9](https://github.com/dgreif/ring/commit/98271f91f13aaae051fccaab7165cd17f2a2377c))
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * allow process to exit when RingApi.disconnect() is called ([#851](https://github.com/dgreif/ring/issues/851)) ([8ef25ac](https://github.com/dgreif/ring/commit/8ef25acca66c533873b7fd43c7c899e34b4a7617))
16
+ * avoid error when ring terminates video stream ([7717d5e](https://github.com/dgreif/ring/commit/7717d5eaa40afed3d34f0e8e6eea1402e21afef7)), closes [#853](https://github.com/dgreif/ring/issues/853)
17
+ * update dependencies ([c80d052](https://github.com/dgreif/ring/commit/c80d052f9eaf4c0a50a495166177daa40299ffa7))
18
+
19
+ ## [9.23.0](https://github.com/dgreif/ring/compare/v9.22.1...v9.23.0) (2021-10-17)
20
+
21
+
22
+ ### Features
23
+
24
+ * **homebridge:** config option to hide camera lights ([58be17c](https://github.com/dgreif/ring/commit/58be17cfee524bfb158566e59d60438e9b6f0f5d)), closes [#802](https://github.com/dgreif/ring/issues/802)
25
+ * **homebridge:** option to treat "knock" as a ding ([7ece8b2](https://github.com/dgreif/ring/commit/7ece8b25314ef403a6c2bab6a28476a3b7229507)), closes [#514](https://github.com/dgreif/ring/issues/514)
26
+
27
+
28
+ ### Bug Fixes
29
+
30
+ * update dependencies ([b685e27](https://github.com/dgreif/ring/commit/b685e27e952a94635a277848821ebfa640d50ee2))
31
+
32
+ ### [9.22.1](https://github.com/dgreif/ring/compare/v9.22.0...v9.22.1) (2021-10-13)
33
+
34
+
35
+ ### Bug Fixes
36
+
37
+ * **homebridge:** prevent login attempt for null refresh tokens ([#791](https://github.com/dgreif/ring/issues/791)) ([bbc9db5](https://github.com/dgreif/ring/commit/bbc9db5578f9b3ed2af49256c8581fef5af8e134))
38
+
39
+ ## [9.22.0](https://github.com/dgreif/ring/compare/v9.21.2...v9.22.0) (2021-10-02)
40
+
41
+
42
+ ### Features
43
+
44
+ * support unknown zwave switch ([9a0a0b9](https://github.com/dgreif/ring/commit/9a0a0b9a3518e2ab9ec732d61fc1102a4e3b9458)), closes [#775](https://github.com/dgreif/ring/issues/775)
45
+
46
+
47
+ ### Bug Fixes
48
+
49
+ * handle promise rejections ([2df5591](https://github.com/dgreif/ring/commit/2df559128fbaeda7de6a64fa7029c86a08b26633))
50
+ * **homebridge:** handle camera stream ending before it is active ([4a0edc0](https://github.com/dgreif/ring/commit/4a0edc031fa6093ec0fa873f04479efa98307bc1)), closes [#762](https://github.com/dgreif/ring/issues/762)
51
+ * update dependencies ([24d5fde](https://github.com/dgreif/ring/commit/24d5fde5f456fa9bb3191c0dc8fa2b483a7ee310))
11
52
 
12
53
  ### [9.21.2](https://github.com/dgreif/ring/compare/v9.21.1...v9.21.2) (2021-09-08)
13
54
 
package/README.md CHANGED
@@ -57,6 +57,7 @@ Only include an optional parameter if you actually need it. Default behavior wi
57
57
  "hideDeviceIds": ["477e4800-fcde-4493-969b-d1a06f683102", "5aaed7a7-06df-4f18-b3af-291c89854d60"],
58
58
  "hideLightGroups": true,
59
59
  "hideDoorbellSwitch": true,
60
+ "hideCameraLight": true,
60
61
  "hideCameraMotionSensor": true,
61
62
  "sendCameraMotionNotificationsToTv": true,
62
63
  "sendDoorbellMotionNotificationsToTv": true,
@@ -65,6 +66,7 @@ Only include an optional parameter if you actually need it. Default behavior wi
65
66
  "hideAlarmSirenSwitch": true,
66
67
  "hideUnsupportedServices": true,
67
68
  "avoidSnapshotBatteryDrain": true,
69
+ "treatKnockAsDing": true,
68
70
  "cameraStatusPollingSeconds": 20,
69
71
  "cameraDingsPollingSeconds": 2,
70
72
  "locationModePollingSeconds": 20,
@@ -79,6 +81,7 @@ Option | Default | Explanation
79
81
  `hideDeviceIds` | `[]` | Allows you to hide specific devices by an array of ids. The id for each device is logged when homebridge starts.
80
82
  `hideLightGroups` | `false` | Ring smart lighting allows you to create lighting groups within the Ring app. These groups are convenient for detecting motion in an area of your yard and turning on/off all lights in the group. However, you may wish to group the lights differently in HomeKit and ignore the groups you have configured in Ring. If this option is `true`, your Ring groups (and their associated motion sensor) will be ignored and will not show up in HomeKit.
81
83
  `hideDoorbellSwitch` | `false` | If you have a Ring video doorbell, you will see a Programmable Switch associated with it. This switch can be used to perform actions on when the doorbell is pressed using "Single Press" actions. If you do not care to perform actions when the doorbell is pressed, you can hide the Programmable Switch by setting this option to `true`. You will still be able to receive _notifications_ from the doorbell even if the Programmable Switch is hidden (notifications can be configured in the settings for the doorbell camera in the Home app)
84
+ `hideCameraLight` | `false` | If `true`, hides the light for Ring cameras in HomeKit.
82
85
  `hideCameraMotionSensor` | `false` | If `true`, hides the motion sensor for Ring cameras in HomeKit.
83
86
  `sendCameraMotionNotificationsToTv` | `false` | If `true`, creates a 'Doorbell' accessory triggered by non-doorbell camera motion alerts for tvOS 14 notification support.
84
87
  `sendDoorbellMotionNotificationsToTv` | `false` | If `true`, motion events on doorbell cameras will trigger a doorbell press event for tvOS 14 notification support.
@@ -89,6 +92,7 @@ Option | Default | Explanation
89
92
  `showPanicButtons` | `false` | Creates a new `Panic Buttons` device in HomeKit with `Burglar Alarm` and `Fire Alarm` switches. **Use these at your own risk. I do not guarantee functionality in case of emergency, nor do I take responsibility for any false alarms**. These function just like the SOS sliders in the Ring app.
90
93
  `nightModeBypassFor` | no Night Mode | Allows you to use Night mode to "Bypass and Arm" a Ring Alarm. Can be set to `all` for Away or `some` for Home. If set and Night mode is activated from HomeKit, any open contact sensors will automatically be bypassed.
91
94
  `avoidSnapshotBatteryDrain` | false | Causes snapshots for battery cameras to be fetched at a minimum 10 minute interval to avoid draining the battery.
95
+ `treatKnockAsDing` | false | When someone knocks on your door, it will be recognized as a doorbell press in HomeKit. Limited to select camera models (Peephole Cam).
92
96
  `cameraStatusPollingSeconds` | `20` | How frequently to poll for updates to your cameras and chimes (in seconds). Information like light/siren/volume/snooze status do not update in real time and need to be requested periodically.
93
97
  `cameraDingsPollingSeconds` | `2` | How frequently to poll for new events from your cameras. These include motion and doorbell presses.
94
98
  `locationModePollingSeconds` | `20` | How frequently to poll for location mode updates (in seconds). This is only useful if you are using location modes to control camera settings and want to keep an up-to-date reference of the current mode for each location. Polling is automatically disabled for locations equipped with a Ring Alarm. Will hide Location Mode switch if set to `0`
@@ -161,6 +165,10 @@ If you are having issues with your cameras in the Home app, please see the [Came
161
165
  * Detect water leak
162
166
  * Tamper status
163
167
  * Battery status
168
+ * Glassbreak Sensor (**untested**)
169
+ * Detect glass breaking
170
+ * Tamper status
171
+ * Battery status
164
172
  * Ring Smart Lights (Motion Detector, Flood/Path/Step/Spot Lights, Bulbs, Transformer, Outdoor Smart Plug, Mailbox Sensor)
165
173
  * On/Off
166
174
  * Brightness Level
@@ -173,7 +181,7 @@ If you are having issues with your cameras in the Home app, please see the [Came
173
181
  * Fans
174
182
  * On/Off
175
183
  * Speed
176
- * Lights/Switches/Outlets
184
+ * Lights/Switches/Outlets/Unknown (some brands of blinds, etc.)
177
185
  * On/Off
178
186
  * Brightness Level (if applicable)
179
187
  * Hue/Sat/Color Temp are _possible_, but currently not supported.
@@ -31,6 +31,10 @@
31
31
  "type": "boolean",
32
32
  "description": "Hides the Programmable Switch for Ring doorbells. This switch is only useful for triggering an automation when the doorbell is pressed, so most users can safely hide it"
33
33
  },
34
+ "hideCameraLight": {
35
+ "title": "Hide Camera Light",
36
+ "type": "boolean"
37
+ },
34
38
  "hideCameraMotionSensor": {
35
39
  "title": "Hide Camera Motion Sensors",
36
40
  "type": "boolean"
@@ -82,6 +86,11 @@
82
86
  "type": "boolean",
83
87
  "description": "Causes snapshots for battery cameras to be fetched at a minimum 10 minute interval to avoid draining the battery. This is useful if have the Home app open constantly, causing frequent snapshot requests."
84
88
  },
89
+ "treatKnockAsDing": {
90
+ "title": "Treat Knock as Ding",
91
+ "type": "boolean",
92
+ "description": "When someone knocks on your door, it will be recognized as a doorbell press in HomeKit. Limited to select camera models (Peephole Cam)"
93
+ },
85
94
  "sendCameraMotionNotificationsToTv": {
86
95
  "title": "Create a 'Doorbell' accessory triggered by non-doorbell camera motion alerts for tvOS 14 notification support",
87
96
  "type": "boolean"
@@ -161,6 +170,7 @@
161
170
  "alarmOnEntryDelay",
162
171
  "hideLightGroups",
163
172
  "hideDoorbellSwitch",
173
+ "hideCameraLight",
164
174
  "hideCameraMotionSensor",
165
175
  "hideCameraSirenSwitch",
166
176
  "hideInHomeDoorbellSwitch",
@@ -169,6 +179,7 @@
169
179
  "showPanicButtons",
170
180
  "nightModeBypassFor",
171
181
  "avoidSnapshotBatteryDrain",
182
+ "treatKnockAsDing",
172
183
  "sendCameraMotionNotificationsToTv",
173
184
  "sendDoorbellMotionNotificationsToTv",
174
185
  "beamDurationSeconds",
package/lib/api/api.d.ts CHANGED
@@ -9,6 +9,7 @@ export interface RingApiOptions extends SessionOptions {
9
9
  cameraDingsPollingSeconds?: number;
10
10
  locationModePollingSeconds?: number;
11
11
  avoidSnapshotBatteryDrain?: boolean;
12
+ treatKnockAsDing?: boolean;
12
13
  debug?: boolean;
13
14
  ffmpegPath?: string;
14
15
  externalPorts?: {
package/lib/api/api.js CHANGED
@@ -24,7 +24,8 @@ class RingApi extends subscribed_1.Subscribed {
24
24
  super();
25
25
  this.options = options;
26
26
  this.restClient = new rest_client_1.RingRestClient(this.options);
27
- this.onRefreshTokenUpdated = this.restClient.onRefreshTokenUpdated.asObservable();
27
+ this.onRefreshTokenUpdated =
28
+ this.restClient.onRefreshTokenUpdated.asObservable();
28
29
  if (options.debug) {
29
30
  (0, util_1.enableDebug)();
30
31
  }
@@ -128,7 +129,7 @@ class RingApi extends subscribed_1.Subscribed {
128
129
  return __awaiter(this, void 0, void 0, function* () {
129
130
  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) ||
130
131
  authorizedDoorbots.includes(data) ||
131
- data.kind.startsWith('doorbell'), this.restClient, this.options.avoidSnapshotBatteryDrain || false)), ringChimes = chimes.map((data) => new ring_chime_1.RingChime(data, this.restClient)), locations = rawLocations
132
+ data.kind.startsWith('doorbell'), this.restClient, this.options.avoidSnapshotBatteryDrain || false, this.options.treatKnockAsDing || false)), ringChimes = chimes.map((data) => new ring_chime_1.RingChime(data, this.restClient)), locations = rawLocations
132
133
  .filter((location) => {
133
134
  return (!Array.isArray(this.options.locationIds) ||
134
135
  this.options.locationIds.includes(location.location_id));
@@ -169,6 +170,7 @@ class RingApi extends subscribed_1.Subscribed {
169
170
  .catch((e) => {
170
171
  (0, util_1.logError)(e);
171
172
  });
173
+ this.restClient.clearTimeouts();
172
174
  }
173
175
  }
174
176
  exports.RingApi = RingApi;
@@ -96,7 +96,7 @@ class Location extends subscribed_1.Subscribed {
96
96
  this.addSubscriptions((0, rxjs_1.merge)(this.onLocationModeRequested, this.onLocationMode)
97
97
  .pipe((0, operators_1.debounceTime)(options.locationModePollingSeconds * 1000))
98
98
  .subscribe(() => this.getLocationMode()));
99
- void this.getLocationMode();
99
+ this.getLocationMode().catch(util_1.logError);
100
100
  }
101
101
  }
102
102
  get id() {
@@ -188,7 +188,7 @@ class Location extends subscribed_1.Subscribed {
188
188
  }
189
189
  setAlarmMode(alarmMode, bypassSensorZids) {
190
190
  return __awaiter(this, void 0, void 0, function* () {
191
- const securityPanel = yield this.getSecurityPanel(), updatedDataPromise = (0, rxjs_1.lastValueFrom)(securityPanel.onData.pipe((0, operators_1.skip)(1), (0, operators_1.take)(1)));
191
+ const securityPanel = yield this.getSecurityPanel(), updatedDataPromise = (0, rxjs_1.firstValueFrom)(securityPanel.onData.pipe((0, operators_1.skip)(1)));
192
192
  yield this.sendCommandToSecurityPanel('security-panel.switch-mode', {
193
193
  mode: alarmMode,
194
194
  bypass: bypassSensorZids,
@@ -224,7 +224,7 @@ class Location extends subscribed_1.Subscribed {
224
224
  });
225
225
  }
226
226
  getNextMessageOfType(type, src) {
227
- return (0, rxjs_1.lastValueFrom)(this.onMessage.pipe((0, operators_1.filter)((m) => m.msg === type && m.src === src), (0, operators_1.map)((m) => m.body), (0, operators_1.take)(1)));
227
+ return (0, rxjs_1.firstValueFrom)(this.onMessage.pipe((0, operators_1.filter)((m) => m.msg === type && m.src === src), (0, operators_1.map)((m) => m.body)));
228
228
  }
229
229
  requestList(listType, assetId) {
230
230
  return this.sendMessage({ msg: listType, dst: assetId });
@@ -243,7 +243,7 @@ class Location extends subscribed_1.Subscribed {
243
243
  if (!this.connectionPromise) {
244
244
  yield this.getConnection();
245
245
  }
246
- return (0, rxjs_1.lastValueFrom)(this.onDevices.pipe((0, operators_1.take)(1)));
246
+ return (0, rxjs_1.firstValueFrom)(this.onDevices);
247
247
  });
248
248
  }
249
249
  getRoomList(assetId) {
@@ -391,11 +391,15 @@ class Location extends subscribed_1.Subscribed {
391
391
  this.disconnected = true;
392
392
  this.unsubscribe();
393
393
  this.cameras.forEach((camera) => camera.disconnect());
394
- this.getDevices().then((devices) => {
394
+ this.getDevices()
395
+ .then((devices) => {
395
396
  devices.forEach((device) => device.disconnect());
396
- });
397
+ })
398
+ .catch(util_1.logError);
397
399
  if (this.connectionPromise) {
398
- this.connectionPromise.then((connection) => connection.close());
400
+ this.connectionPromise
401
+ .then((connection) => connection.close())
402
+ .catch(util_1.logError);
399
403
  }
400
404
  }
401
405
  }
@@ -24,6 +24,7 @@ export declare class RingRestClient {
24
24
  refreshToken: string | undefined;
25
25
  private hardwareIdPromise;
26
26
  private _authPromise;
27
+ private timeouts;
27
28
  private clearPreviousAuth;
28
29
  private get authPromise();
29
30
  private sessionPromise?;
@@ -44,4 +45,5 @@ export declare class RingRestClient {
44
45
  url: string;
45
46
  }): Promise<T & ExtendedResponse>;
46
47
  getCurrentAuth(): Promise<AuthTokenResponse>;
48
+ clearTimeouts(): void;
47
49
  }
@@ -66,12 +66,15 @@ function requestWithRetry(requestOptions) {
66
66
  class RingRestClient {
67
67
  constructor(authOptions) {
68
68
  this.authOptions = authOptions;
69
- // prettier-ignore
70
- this.refreshToken = ('refreshToken' in this.authOptions ? this.authOptions.refreshToken : undefined);
71
- this.hardwareIdPromise = (0, util_1.getHardwareId)(this.authOptions.systemId);
69
+ this.timeouts = [];
72
70
  this.sessionPromise = undefined;
73
71
  this.using2fa = false;
74
72
  this.onRefreshTokenUpdated = new rxjs_1.ReplaySubject(1);
73
+ this.refreshToken =
74
+ 'refreshToken' in this.authOptions
75
+ ? this.authOptions.refreshToken
76
+ : undefined;
77
+ this.hardwareIdPromise = (0, util_1.getHardwareId)(this.authOptions.systemId);
75
78
  }
76
79
  clearPreviousAuth() {
77
80
  this._authPromise = undefined;
@@ -83,11 +86,12 @@ class RingRestClient {
83
86
  authPromise
84
87
  .then(({ expires_in }) => {
85
88
  // clear the existing auth promise 1 minute before it expires
86
- setTimeout(() => {
89
+ const timeout = setTimeout(() => {
87
90
  if (this._authPromise === authPromise) {
88
91
  this.clearPreviousAuth();
89
92
  }
90
93
  }, ((expires_in || 3600) - 60) * 1000);
94
+ this.timeouts.push(timeout);
91
95
  })
92
96
  .catch(() => {
93
97
  // ignore these errors here, they should be handled by the function making a rest request
@@ -288,5 +292,8 @@ class RingRestClient {
288
292
  getCurrentAuth() {
289
293
  return this.authPromise;
290
294
  }
295
+ clearTimeouts() {
296
+ this.timeouts.forEach(clearTimeout);
297
+ }
291
298
  }
292
299
  exports.RingRestClient = RingRestClient;
@@ -16,6 +16,7 @@ export declare class RingCamera extends Subscribed {
16
16
  isDoorbot: boolean;
17
17
  private restClient;
18
18
  private avoidSnapshotBatteryDrain;
19
+ private treatKnockAsDing;
19
20
  id: number;
20
21
  deviceType: import("./ring-types").RingCameraKind;
21
22
  model: string;
@@ -32,7 +33,7 @@ export declare class RingCamera extends Subscribed {
32
33
  onMotionStarted: import("rxjs").Observable<null>;
33
34
  onBatteryLevel: import("rxjs").Observable<number | null>;
34
35
  onInHomeDoorbellStatus: import("rxjs").Observable<boolean>;
35
- constructor(initialData: CameraData, isDoorbot: boolean, restClient: RingRestClient, avoidSnapshotBatteryDrain: boolean);
36
+ constructor(initialData: CameraData, isDoorbot: boolean, restClient: RingRestClient, avoidSnapshotBatteryDrain: boolean, treatKnockAsDing: boolean);
36
37
  updateData(update: CameraData): void;
37
38
  requestUpdate(): void;
38
39
  get data(): CameraData;
@@ -85,7 +86,7 @@ export declare class RingCamera extends Subscribed {
85
86
  private checkIfSnapshotsAreBlocked;
86
87
  private requestSnapshotUpdate;
87
88
  private refreshSnapshot;
88
- getSnapshot(): Promise<Buffer>;
89
+ getSnapshot(): Promise<Buffer | undefined>;
89
90
  getSipOptions(): Promise<SipOptions>;
90
91
  getUpdatedSipOptions(expiredDingId: string): Promise<SipOptions>;
91
92
  createSipSession(options?: {
@@ -66,37 +66,40 @@ function getSearchQueryString(options) {
66
66
  }
67
67
  exports.getSearchQueryString = getSearchQueryString;
68
68
  class RingCamera extends subscribed_1.Subscribed {
69
- constructor(initialData, isDoorbot, restClient, avoidSnapshotBatteryDrain) {
69
+ constructor(initialData, isDoorbot, restClient, avoidSnapshotBatteryDrain, treatKnockAsDing) {
70
70
  super();
71
71
  this.initialData = initialData;
72
72
  this.isDoorbot = isDoorbot;
73
73
  this.restClient = restClient;
74
74
  this.avoidSnapshotBatteryDrain = avoidSnapshotBatteryDrain;
75
- this.id = this.initialData.id;
76
- this.deviceType = this.initialData.kind;
77
- this.model = ring_types_1.RingCameraModel[this.initialData.kind] || 'Unknown Model';
78
- this.onData = new rxjs_1.BehaviorSubject(this.initialData);
79
- this.hasLight = this.initialData.led_status !== undefined;
80
- this.hasSiren = this.initialData.siren_status !== undefined;
81
- this.hasBattery = (0, ring_types_1.isBatteryCameraKind)(this.deviceType) ||
82
- (typeof this.initialData.battery_life === 'string' &&
83
- this.batteryLevel !== null &&
84
- this.batteryLevel < 100 &&
85
- this.batteryLevel >= 0);
75
+ this.treatKnockAsDing = treatKnockAsDing;
86
76
  this.onRequestUpdate = new rxjs_1.Subject();
87
77
  this.onRequestActiveDings = new rxjs_1.Subject();
88
78
  this.onNewDing = new rxjs_1.Subject();
89
79
  this.onActiveDings = new rxjs_1.BehaviorSubject([]);
90
- this.onDoorbellPressed = this.onNewDing.pipe((0, operators_1.filter)((ding) => ding.kind === 'ding'), (0, operators_1.share)());
80
+ this.onDoorbellPressed = this.onNewDing.pipe((0, operators_1.filter)((ding) => ding.kind === 'ding' ||
81
+ (this.treatKnockAsDing && ding.kind === 'door_activity')), (0, operators_1.share)());
91
82
  this.onMotionDetected = this.onActiveDings.pipe((0, operators_1.map)((dings) => dings.some((ding) => ding.motion || ding.kind === 'motion')), (0, operators_1.distinctUntilChanged)(), (0, operators_1.publishReplay)(1), (0, operators_1.refCount)());
92
83
  this.onMotionStarted = this.onMotionDetected.pipe((0, operators_1.filter)((currentlyDetected) => currentlyDetected), (0, operators_1.mapTo)(null), // no value needed, event is what matters
93
84
  (0, operators_1.share)());
85
+ this.expiredDingIds = [];
86
+ this.lastSnapshotTimestampLocal = 0;
87
+ this.id = this.initialData.id;
88
+ this.deviceType = this.initialData.kind;
89
+ this.model = ring_types_1.RingCameraModel[this.initialData.kind] || 'Unknown Model';
90
+ this.onData = new rxjs_1.BehaviorSubject(this.initialData);
91
+ this.hasLight = this.initialData.led_status !== undefined;
92
+ this.hasSiren = this.initialData.siren_status !== undefined;
93
+ this.hasBattery =
94
+ (0, ring_types_1.isBatteryCameraKind)(this.deviceType) ||
95
+ (typeof this.initialData.battery_life === 'string' &&
96
+ this.batteryLevel !== null &&
97
+ this.batteryLevel < 100 &&
98
+ this.batteryLevel >= 0);
94
99
  this.onBatteryLevel = this.onData.pipe((0, operators_1.map)(getBatteryLevel), (0, operators_1.distinctUntilChanged)());
95
100
  this.onInHomeDoorbellStatus = this.onData.pipe((0, operators_1.map)(({ settings: { chime_settings } }) => {
96
101
  return Boolean(chime_settings === null || chime_settings === void 0 ? void 0 : chime_settings.enable);
97
102
  }), (0, operators_1.distinctUntilChanged)());
98
- this.expiredDingIds = [];
99
- this.lastSnapshotTimestampLocal = 0;
100
103
  if (!initialData.subscribed) {
101
104
  this.subscribeToDingEvents().catch((e) => {
102
105
  (0, util_1.logError)('Failed to subscribe ' + initialData.description + ' to ding events');
@@ -250,7 +253,7 @@ class RingCamera extends subscribed_1.Subscribed {
250
253
  }
251
254
  getSipConnectionDetails() {
252
255
  return __awaiter(this, void 0, void 0, function* () {
253
- const vodPromise = (0, rxjs_1.lastValueFrom)(this.onNewDing.pipe((0, operators_1.take)(1))), videoOnDemandDing = yield this.startVideoOnDemand();
256
+ const vodPromise = (0, rxjs_1.firstValueFrom)(this.onNewDing), videoOnDemandDing = yield this.startVideoOnDemand();
254
257
  if (videoOnDemandDing && 'sip_from' in videoOnDemandDing) {
255
258
  // wired cams return a ding from live_view so we don't need to wait
256
259
  return videoOnDemandDing;
@@ -407,10 +410,13 @@ class RingCamera extends subscribed_1.Subscribed {
407
410
  url: (0, rest_client_1.clientApi)(`snapshots/image/${this.id}`),
408
411
  responseType: 'buffer',
409
412
  });
410
- this.lastSnapshotPromise.catch(() => {
413
+ try {
414
+ yield this.lastSnapshotPromise;
415
+ }
416
+ catch (_) {
411
417
  // snapshot request failed, don't use it again
412
418
  this.lastSnapshotPromise = undefined;
413
- });
419
+ }
414
420
  return this.lastSnapshotPromise;
415
421
  });
416
422
  }
@@ -458,7 +464,7 @@ class RingCamera extends subscribed_1.Subscribed {
458
464
  const sipSession = yield this.streamVideo({
459
465
  output: ['-t', duration.toString(), outputPath],
460
466
  });
461
- yield (0, rxjs_1.lastValueFrom)(sipSession.onCallEnded.pipe((0, operators_1.take)(1)));
467
+ yield (0, rxjs_1.firstValueFrom)(sipSession.onCallEnded);
462
468
  });
463
469
  }
464
470
  streamVideo(ffmpegOptions) {
@@ -5,7 +5,7 @@ export declare class RingChime {
5
5
  private initialData;
6
6
  private restClient;
7
7
  id: number;
8
- deviceType: "chime" | "chime_pro" | "chime_v2" | "chime_pro_v2";
8
+ deviceType: import("./ring-types").ChimeKind;
9
9
  model: string;
10
10
  onData: BehaviorSubject<ChimeData>;
11
11
  onRequestUpdate: Subject<unknown>;
@@ -23,11 +23,11 @@ class RingChime {
23
23
  constructor(initialData, restClient) {
24
24
  this.initialData = initialData;
25
25
  this.restClient = restClient;
26
+ this.onRequestUpdate = new rxjs_1.Subject();
26
27
  this.id = this.initialData.id;
27
28
  this.deviceType = this.initialData.kind;
28
29
  this.model = ring_types_1.ChimeModel[this.deviceType] || 'Chime';
29
30
  this.onData = new rxjs_1.BehaviorSubject(this.initialData);
30
- this.onRequestUpdate = new rxjs_1.Subject();
31
31
  }
32
32
  updateData(update) {
33
33
  this.onData.next(update);
@@ -5,6 +5,7 @@ const rxjs_1 = require("rxjs");
5
5
  const ring_types_1 = require("./ring-types");
6
6
  const operators_1 = require("rxjs/operators");
7
7
  const subscribed_1 = require("./subscribed");
8
+ const util_1 = require("./util");
8
9
  class RingDevice extends subscribed_1.Subscribed {
9
10
  constructor(initialData, location, assetId) {
10
11
  super();
@@ -63,7 +64,7 @@ class RingDevice extends subscribed_1.Subscribed {
63
64
  },
64
65
  ],
65
66
  },
66
- });
67
+ }).catch(util_1.logError);
67
68
  }
68
69
  toString() {
69
70
  return this.toJSON();
@@ -10,6 +10,7 @@ export declare enum RingDeviceType {
10
10
  TemperatureSensor = "sensor.temperature",
11
11
  WaterSensor = "sensor.water",
12
12
  TiltSensor = "sensor.tilt",
13
+ GlassbreakSensor = "sensor.glassbreak",
13
14
  RangeExtender = "range-extender.zwave",
14
15
  ZigbeeAdapter = "adapter.zigbee",
15
16
  AccessCodeVault = "access-code.vault",
@@ -214,7 +215,7 @@ export interface BeamBridge {
214
215
  role: string;
215
216
  updated_at: string;
216
217
  }
217
- declare type ChimeKind = 'chime' | 'chime_pro' | 'chime_v2' | 'chime_pro_v2';
218
+ export declare type ChimeKind = 'chime' | 'chime_pro' | 'chime_v2' | 'chime_pro_v2';
218
219
  export declare const ChimeModel: {
219
220
  readonly [P in ChimeKind]: string;
220
221
  };
@@ -14,6 +14,7 @@ var RingDeviceType;
14
14
  RingDeviceType["TemperatureSensor"] = "sensor.temperature";
15
15
  RingDeviceType["WaterSensor"] = "sensor.water";
16
16
  RingDeviceType["TiltSensor"] = "sensor.tilt";
17
+ RingDeviceType["GlassbreakSensor"] = "sensor.glassbreak";
17
18
  RingDeviceType["RangeExtender"] = "range-extender.zwave";
18
19
  RingDeviceType["ZigbeeAdapter"] = "adapter.zigbee";
19
20
  RingDeviceType["AccessCodeVault"] = "access-code.vault";
@@ -28,8 +28,10 @@ function sendStunBindingRequest({ rtpDescription, rtpSplitter, rtcpSplitter, loc
28
28
  else {
29
29
  // ICE not supported. Fire and forget the stun request for RTP and RTCP
30
30
  const encodedMessage = stun.encode(message);
31
- rtpSplitter.send(encodedMessage, { address, port });
32
- rtcpSplitter.send(encodedMessage, { address, port: rtcpPort });
31
+ rtpSplitter.send(encodedMessage, { address, port }).catch(util_1.logError);
32
+ rtcpSplitter
33
+ .send(encodedMessage, { address, port: rtcpPort })
34
+ .catch(util_1.logError);
33
35
  }
34
36
  }
35
37
  exports.sendStunBindingRequest = sendStunBindingRequest;
@@ -166,7 +166,7 @@ class SipCall {
166
166
  ackWithInfo(seq) {
167
167
  return __awaiter(this, void 0, void 0, function* () {
168
168
  // Don't wait for ack, it won't ever come back.
169
- void this.request({
169
+ this.request({
170
170
  method: 'ACK',
171
171
  seq, // The ACK must have the original sequence number.
172
172
  }).catch(rxjs_1.noop);
@@ -32,8 +32,8 @@ class SipSession extends subscribed_1.Subscribed {
32
32
  this.hasStarted = false;
33
33
  this.hasCallEnded = false;
34
34
  this.onCallEndedSubject = new rxjs_1.ReplaySubject(1);
35
- this.sipCall = this.createSipCall(this.sipOptions);
36
35
  this.onCallEnded = this.onCallEndedSubject.asObservable();
36
+ this.sipCall = this.createSipCall(this.sipOptions);
37
37
  }
38
38
  createSipCall(sipOptions) {
39
39
  if (this.sipCall) {
@@ -159,7 +159,7 @@ class SipSession extends subscribed_1.Subscribed {
159
159
  return null;
160
160
  }
161
161
  if (!haveReceivedStreamPacket) {
162
- void this.sipCall.requestKeyFrame();
162
+ this.sipCall.requestKeyFrame().catch(rxjs_1.noop);
163
163
  haveReceivedStreamPacket = true;
164
164
  }
165
165
  return {
@@ -196,7 +196,7 @@ class SipSession extends subscribed_1.Subscribed {
196
196
  }
197
197
  this.hasCallEnded = true;
198
198
  if (sendBye) {
199
- this.sipCall.sendBye();
199
+ this.sipCall.sendBye().catch(util_1.logError);
200
200
  }
201
201
  // clean up
202
202
  this.onCallEndedSubject.next(null);
package/lib/api/sip.d.ts CHANGED
@@ -19,6 +19,7 @@ export function create(options: any, callback: any): {
19
19
  isFlowUri: (uri: any) => boolean;
20
20
  hostname: () => any;
21
21
  destroy: () => void;
22
+ makeResponse: typeof makeResponse;
22
23
  };
23
24
  export function start(options: any, callback: any): void;
24
25
  export function parseAOR(data: any): any;
package/lib/api/sip.js CHANGED
@@ -1300,6 +1300,7 @@ exports.create = function (options, callback) {
1300
1300
  transaction.destroy();
1301
1301
  transport.destroy();
1302
1302
  },
1303
+ makeResponse,
1303
1304
  };
1304
1305
  };
1305
1306
  exports.start = function (options, callback) {
package/lib/api/util.js CHANGED
@@ -8,14 +8,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
11
14
  Object.defineProperty(exports, "__esModule", { value: true });
12
15
  exports.randomString = exports.randomInteger = exports.mapAsync = exports.stringify = exports.requestInput = exports.getHardwareId = exports.generateUuid = exports.enableDebug = exports.useLogger = exports.logError = exports.logInfo = exports.logDebug = exports.delay = void 0;
13
- const debug = require("debug");
16
+ const debug_1 = __importDefault(require("debug"));
14
17
  const colors_1 = require("colors");
15
18
  const readline_1 = require("readline");
16
19
  const uuid_1 = require("uuid");
17
20
  const systeminformation_1 = require("systeminformation");
18
- const debugLogger = debug('ring'), uuidNamespace = 'e53ffdc0-e91d-4ce1-bec2-df939d94739c';
21
+ const debugLogger = (0, debug_1.default)('ring'), uuidNamespace = 'e53ffdc0-e91d-4ce1-bec2-df939d94739c';
19
22
  let logger = {
20
23
  logInfo(message) {
21
24
  debugLogger(message);
@@ -56,7 +56,7 @@ class BaseAccessory {
56
56
  // allow GET once a value is cached
57
57
  characteristic.on("get" /* GET */, (callback) => __awaiter(this, void 0, void 0, function* () {
58
58
  try {
59
- const value = yield (0, rxjs_1.lastValueFrom)(onCachedValue.pipe((0, operators_1.take)(1)));
59
+ const value = yield (0, rxjs_1.firstValueFrom)(onCachedValue);
60
60
  callback(null, value);
61
61
  requestUpdate();
62
62
  }
@@ -68,9 +68,7 @@ class BaseDeviceAccessory extends base_data_accessory_1.BaseDataAccessory {
68
68
  getValue: (data) => {
69
69
  return data.volume ? data.volume * 100 : 0;
70
70
  },
71
- setValue: (volume) => {
72
- device.setVolume(volume / 100);
73
- },
71
+ setValue: (volume) => device.setVolume(volume / 100),
74
72
  });
75
73
  }
76
74
  if (hasBatteryStatus(initialData)) {
@@ -7,7 +7,7 @@ export declare class CameraSource implements CameraStreamingDelegate {
7
7
  private cachedSnapshot?;
8
8
  constructor(ringCamera: RingCamera);
9
9
  private previousLoadSnapshotPromise?;
10
- loadSnapshot(): Promise<any> | undefined;
10
+ loadSnapshot(): Promise<any>;
11
11
  private loadAndCacheSnapshot;
12
12
  private getCurrentSnapshot;
13
13
  handleSnapshotRequest(request: SnapshotRequest, callback: SnapshotRequestCallback): Promise<void>;