homebridge-ring 14.3.0 → 15.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/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # homebridge-ring
2
2
 
3
+ ## 15.0.0-beta.0
4
+
5
+ ### Major Changes
6
+
7
+ - [#1846](https://github.com/dgreif/ring/pull/1846) [`2a6371d`](https://github.com/dgreif/ring/commit/2a6371d0890e35a80bf97fb1e1ccfa2a7c283b4e) Thanks [@dgreif](https://github.com/dgreif)! - Drop Node.js 20 support and upgrade undici to 8.x. Requires Node.js 22.19.0+ (22.x), 24.x, or 26.x; earlier 22.x releases are no longer supported.
8
+
9
+ ### Patch Changes
10
+
11
+ - [#1892](https://github.com/dgreif/ring/pull/1892) [`8c9b637`](https://github.com/dgreif/ring/commit/8c9b63740f9d1bcd30a12922325eb58ac68e4cf9) Thanks [@dgreif](https://github.com/dgreif)! - Prevent failed volume, brightness, dimmer, and fan-speed writes from crashing Homebridge. Log failures and restore the last reported Ring level without overwriting newer requests.
12
+
13
+ - [#1886](https://github.com/dgreif/ring/pull/1886) [`78ae341`](https://github.com/dgreif/ring/commit/78ae341e990aa18517402a4e3809a51d3692678d) Thanks [@dgreif](https://github.com/dgreif)! - Never overwrite the stored refresh token when Ring's oauth response has no `refresh_token`. Previously, an undecodable response (for example Node.js 26 with an older undici) saved a token without `rt`, which caused permanent `invalid_grant` errors until a new token was generated
14
+
15
+ - [#1871](https://github.com/dgreif/ring/pull/1871) [`c72026d`](https://github.com/dgreif/ring/commit/c72026d72c7b9ccb9ef45b4f64c6affd86d85df2) Thanks [@dgreif](https://github.com/dgreif)! - Restore the reported location mode when a HomeKit mode change fails, without overwriting newer requests. Preserve pending Home mode targets until Ring confirms them.
16
+
17
+ - [#1746](https://github.com/dgreif/ring/pull/1746) [`25c6ec6`](https://github.com/dgreif/ring/commit/25c6ec63a9826f42f3f4269d0c2aad6f1e0e4185) Thanks [@kzaky](https://github.com/kzaky)! - Redact the 2FA code from the homebridge-ui server log on token exchange. Codes are short-lived and single-use, but logs are frequently pasted into bug reports, so removing the value from the log is a small defensive improvement.
18
+
19
+ - [#1838](https://github.com/dgreif/ring/pull/1838) [`c78c8b3`](https://github.com/dgreif/ring/commit/c78c8b3fadb0650d210d1220d2724309aec02611) Thanks [@dgreif](https://github.com/dgreif)! - Updated dependencies
20
+
21
+ - [#1840](https://github.com/dgreif/ring/pull/1840) [`5e4afd4`](https://github.com/dgreif/ring/commit/5e4afd43e88802e62bfc8acab743433d9bfaabfa) Thanks [@dgreif](https://github.com/dgreif)! - Bump werift to 0.26.0 (clears `ip` audit) and accept partial ICE candidates for Ring signaling
22
+ - Updated dependencies [[`e368e26`](https://github.com/dgreif/ring/commit/e368e26f789f4ca2093ac015dcec4bc9ce181d62), [`003c0d0`](https://github.com/dgreif/ring/commit/003c0d0740cedd3c393ce86baf628d7e5bc26c86), [`98f0cb3`](https://github.com/dgreif/ring/commit/98f0cb3f87473c168f4cb369ae26d486030e059e), [`bb781fe`](https://github.com/dgreif/ring/commit/bb781fe5ea8616c568a6f7d8e05a0d15fd472104), [`78ae341`](https://github.com/dgreif/ring/commit/78ae341e990aa18517402a4e3809a51d3692678d), [`2a6371d`](https://github.com/dgreif/ring/commit/2a6371d0890e35a80bf97fb1e1ccfa2a7c283b4e), [`c78c8b3`](https://github.com/dgreif/ring/commit/c78c8b3fadb0650d210d1220d2724309aec02611), [`5e4afd4`](https://github.com/dgreif/ring/commit/5e4afd43e88802e62bfc8acab743433d9bfaabfa)]:
23
+ - ring-client-api@15.0.0-beta.0
24
+
3
25
  ## 14.3.0
4
26
 
5
27
  ### Minor Changes
@@ -1,7 +1,7 @@
1
- import { hap } from "./hap.js";
1
+ import { hap } from './hap.js';
2
2
  import { shareReplay, take } from 'rxjs/operators';
3
3
  import { firstValueFrom } from 'rxjs';
4
- import { debug } from "./config.js";
4
+ import { debug } from './config.js';
5
5
  import { logError, logInfo } from 'ring-client-api/util';
6
6
  function isServiceInstance(serviceType) {
7
7
  return typeof serviceType === 'object';
@@ -1,10 +1,10 @@
1
- import { BaseAccessory } from "./base-accessory.js";
2
- import { Subject } from 'rxjs';
1
+ import { BaseAccessory } from './base-accessory.js';
2
+ import { merge, Subject } from 'rxjs';
3
3
  import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators';
4
4
  import { logError } from 'ring-client-api/util';
5
5
  export class BaseDataAccessory extends BaseAccessory {
6
6
  registerCharacteristic({ characteristicType, serviceType, getValue, setValue, setValueDebounceTime = 0, name, requestUpdate, serviceSubType, }) {
7
- const service = this.getService(serviceType, name, serviceSubType), characteristic = service.getCharacteristic(characteristicType), { device } = this;
7
+ const service = this.getService(serviceType, name, serviceSubType), characteristic = service.getCharacteristic(characteristicType), { device } = this, onValue = new Subject();
8
8
  if (requestUpdate) {
9
9
  // Only register for GET if an async request should be made to get an updated value
10
10
  characteristic.on("get" /* CharacteristicEventTypes.GET */, (callback) => {
@@ -21,10 +21,17 @@ export class BaseDataAccessory extends BaseAccessory {
21
21
  if (setValue && setValueDebounceTime) {
22
22
  const onValueToSet = new Subject();
23
23
  characteristic.on("set" /* CharacteristicEventTypes.SET */, (newValue, callback) => {
24
+ onValue.next(newValue);
24
25
  onValueToSet.next(newValue);
25
26
  callback();
26
27
  });
27
- onValueToSet.pipe(debounceTime(setValueDebounceTime)).subscribe(setValue);
28
+ onValueToSet
29
+ .pipe(debounceTime(setValueDebounceTime))
30
+ .subscribe((value) => {
31
+ Promise.resolve()
32
+ .then(() => setValue(value))
33
+ .catch(logError);
34
+ });
28
35
  }
29
36
  else if (setValue) {
30
37
  characteristic.on("set" /* CharacteristicEventTypes.SET */, (newValue, callback) => {
@@ -32,14 +39,14 @@ export class BaseDataAccessory extends BaseAccessory {
32
39
  callback();
33
40
  });
34
41
  }
35
- ;
36
- this.device.onData
37
- .pipe(map(getValue), distinctUntilChanged())
42
+ merge(this.device.onData.pipe(map(getValue)), onValue)
43
+ .pipe(distinctUntilChanged())
38
44
  .subscribe((value) => characteristic.updateValue(value));
45
+ return (value) => onValue.next(value);
39
46
  }
40
47
  registerLevelCharacteristic({ characteristicType, serviceType, getValue, setValue, requestUpdate, }) {
41
- let targetLevel;
42
- this.registerCharacteristic({
48
+ let targetLevel, levelRequestId = 0;
49
+ const updateValue = this.registerCharacteristic({
43
50
  characteristicType,
44
51
  serviceType,
45
52
  getValue: (data) => {
@@ -49,9 +56,19 @@ export class BaseDataAccessory extends BaseAccessory {
49
56
  }
50
57
  return targetLevel === undefined ? newLevel : targetLevel;
51
58
  },
52
- setValue: (level) => {
59
+ setValue: async (level) => {
60
+ const requestId = ++levelRequestId;
53
61
  targetLevel = level;
54
- setValue(level);
62
+ try {
63
+ await setValue(level);
64
+ }
65
+ catch (e) {
66
+ if (requestId === levelRequestId) {
67
+ targetLevel = undefined;
68
+ updateValue(getValue(this.device.data));
69
+ }
70
+ throw e;
71
+ }
55
72
  },
56
73
  setValueDebounceTime: 500,
57
74
  requestUpdate,
@@ -1,5 +1,5 @@
1
- import { hap } from "./hap.js";
2
- import { BaseDataAccessory } from "./base-data-accessory.js";
1
+ import { hap } from './hap.js';
2
+ import { BaseDataAccessory } from './base-data-accessory.js';
3
3
  function getBatteryLevel({ batteryLevel, batteryStatus }) {
4
4
  if (batteryLevel !== undefined) {
5
5
  return batteryLevel;
package/lib/beam.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { RingDeviceType } from 'ring-client-api';
2
- import { hap } from "./hap.js";
3
- import { BaseDeviceAccessory } from "./base-device-accessory.js";
2
+ import { hap } from './hap.js';
3
+ import { BaseDeviceAccessory } from './base-device-accessory.js';
4
4
  import { logInfo } from 'ring-client-api/util';
5
5
  export class Beam extends BaseDeviceAccessory {
6
6
  isLightGroup;
@@ -1,5 +1,5 @@
1
- import { BaseDeviceAccessory } from "./base-device-accessory.js";
2
- import { hap } from "./hap.js";
1
+ import { BaseDeviceAccessory } from './base-device-accessory.js';
2
+ import { hap } from './hap.js';
3
3
  import { logInfo } from 'ring-client-api/util';
4
4
  export class BrightnessOnly extends BaseDeviceAccessory {
5
5
  device;
@@ -1,4 +1,4 @@
1
- import { hap } from "./hap.js";
1
+ import { hap } from './hap.js';
2
2
  import { generateSrtpOptions, ReturnAudioTranscoder, RtpSplitter, } from '@homebridge/camera-utils';
3
3
  import { logDebug, logError, logInfo } from 'ring-client-api/util';
4
4
  import { debounceTime, delay, take } from 'rxjs/operators';
package/lib/camera.js CHANGED
@@ -1,8 +1,8 @@
1
- import { hap } from "./hap.js";
2
- import { BaseDataAccessory } from "./base-data-accessory.js";
1
+ import { hap } from './hap.js';
2
+ import { BaseDataAccessory } from './base-data-accessory.js';
3
3
  import { filter, map, switchMap, throttleTime } from 'rxjs/operators';
4
- import { CameraSource } from "./camera-source.js";
5
- import { TargetValueTimer } from "./target-value-timer.js";
4
+ import { CameraSource } from './camera-source.js';
5
+ import { TargetValueTimer } from './target-value-timer.js';
6
6
  import { delay, logError, logInfo } from 'ring-client-api/util';
7
7
  import { firstValueFrom } from 'rxjs';
8
8
  export class Camera extends BaseDataAccessory {
package/lib/chime.js CHANGED
@@ -1,5 +1,5 @@
1
- import { hap } from "./hap.js";
2
- import { BaseDataAccessory } from "./base-data-accessory.js";
1
+ import { hap } from './hap.js';
2
+ import { BaseDataAccessory } from './base-data-accessory.js';
3
3
  import { logInfo } from 'ring-client-api/util';
4
4
  const minutesFor24Hours = 24 * 60;
5
5
  export class Chime extends BaseDataAccessory {
package/lib/co-alarm.js CHANGED
@@ -1,5 +1,5 @@
1
- import { BaseDeviceAccessory } from "./base-device-accessory.js";
2
- import { hap } from "./hap.js";
1
+ import { BaseDeviceAccessory } from './base-device-accessory.js';
2
+ import { hap } from './hap.js';
3
3
  export class CoAlarm extends BaseDeviceAccessory {
4
4
  device;
5
5
  accessory;
@@ -1,5 +1,5 @@
1
- import { BaseDeviceAccessory } from "./base-device-accessory.js";
2
- import { hap } from "./hap.js";
1
+ import { BaseDeviceAccessory } from './base-device-accessory.js';
2
+ import { hap } from './hap.js';
3
3
  export class ContactSensor extends BaseDeviceAccessory {
4
4
  device;
5
5
  accessory;
package/lib/fan.js CHANGED
@@ -1,5 +1,5 @@
1
- import { BaseDeviceAccessory } from "./base-device-accessory.js";
2
- import { hap } from "./hap.js";
1
+ import { BaseDeviceAccessory } from './base-device-accessory.js';
2
+ import { hap } from './hap.js';
3
3
  import { logInfo } from 'ring-client-api/util';
4
4
  export class Fan extends BaseDeviceAccessory {
5
5
  device;
@@ -1,5 +1,5 @@
1
- import { BaseDeviceAccessory } from "./base-device-accessory.js";
2
- import { hap } from "./hap.js";
1
+ import { BaseDeviceAccessory } from './base-device-accessory.js';
2
+ import { hap } from './hap.js';
3
3
  import { distinctUntilChanged, filter, map } from 'rxjs/operators';
4
4
  import { logInfo } from 'ring-client-api/util';
5
5
  export class FloodFreezeSensor extends BaseDeviceAccessory {
@@ -1,5 +1,5 @@
1
- import { BaseDeviceAccessory } from "./base-device-accessory.js";
2
- import { hap } from "./hap.js";
1
+ import { BaseDeviceAccessory } from './base-device-accessory.js';
2
+ import { hap } from './hap.js';
3
3
  import { distinctUntilChanged, filter, map } from 'rxjs/operators';
4
4
  import { logInfo } from 'ring-client-api/util';
5
5
  export class FreezeSensor extends BaseDeviceAccessory {
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable no-console */
2
2
  import { HomebridgePluginUiServer, RequestError, } from '@homebridge/plugin-ui-utils';
3
- import { RingRestClient } from 'ring-client-api/rest-client';
4
- import { controlCenterDisplayName, getSystemId } from "../config.js";
3
+ import { InvalidAuthResponseError, RingRestClient, } from 'ring-client-api/rest-client';
4
+ import { controlCenterDisplayName, getSystemId } from '../config.js';
5
5
  class PluginUiServer extends HomebridgePluginUiServer {
6
6
  restClient;
7
7
  constructor() {
@@ -36,12 +36,16 @@ class PluginUiServer extends HomebridgePluginUiServer {
36
36
  generateToken = async ({ email, password, code }) => {
37
37
  // use the existing restClient to avoid sending a token again
38
38
  this.restClient = this.restClient || new RingRestClient({ email, password });
39
- console.log(`Getting token for ${email} with code ${code}`);
39
+ console.log(`Getting token for ${email}`);
40
40
  try {
41
41
  const authResponse = await this.restClient.getAuth(code);
42
42
  return { refreshToken: authResponse.refresh_token };
43
43
  }
44
44
  catch (e) {
45
+ if (e instanceof InvalidAuthResponseError) {
46
+ console.error(e);
47
+ throw new RequestError(e.message, e);
48
+ }
45
49
  console.error('Incorrect 2fa Code');
46
50
  throw new RequestError('Please check the code and try again', e);
47
51
  }
package/lib/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { platformName, pluginName, RingPlatform } from "./ring-platform.js";
2
- import { setHap } from "./hap.js";
1
+ import { platformName, pluginName, RingPlatform } from './ring-platform.js';
2
+ import { setHap } from './hap.js';
3
3
  export default function (homebridge) {
4
4
  setHap(homebridge.hap);
5
5
  homebridge.registerPlatform(pluginName, platformName, RingPlatform, true);
package/lib/intercom.js CHANGED
@@ -1,5 +1,5 @@
1
- import { hap } from "./hap.js";
2
- import { BaseDataAccessory } from "./base-data-accessory.js";
1
+ import { hap } from './hap.js';
2
+ import { BaseDataAccessory } from './base-data-accessory.js';
3
3
  import { logError, logInfo } from 'ring-client-api/util';
4
4
  import { map, throttleTime } from 'rxjs/operators';
5
5
  export class Intercom extends BaseDataAccessory {
@@ -1,7 +1,7 @@
1
1
  import { distinctUntilChanged } from 'rxjs/operators';
2
- import { hap } from "./hap.js";
2
+ import { hap } from './hap.js';
3
3
  import { logError, logInfo } from 'ring-client-api/util';
4
- import { BaseAccessory } from "./base-accessory.js";
4
+ import { BaseAccessory } from './base-accessory.js';
5
5
  import { firstValueFrom, of } from 'rxjs';
6
6
  function getStateFromMode(mode) {
7
7
  const { Characteristic: { SecuritySystemCurrentState: State }, } = hap;
@@ -18,6 +18,7 @@ function getStateFromMode(mode) {
18
18
  }
19
19
  export class LocationModeSwitch extends BaseAccessory {
20
20
  targetState;
21
+ modeRequestId = 0;
21
22
  device;
22
23
  location;
23
24
  accessory;
@@ -36,7 +37,7 @@ export class LocationModeSwitch extends BaseAccessory {
36
37
  if (state === this.targetState) {
37
38
  this.targetState = undefined;
38
39
  }
39
- if (!this.targetState) {
40
+ if (this.targetState === undefined) {
40
41
  targetState.updateValue(state);
41
42
  }
42
43
  currentState.updateValue(state);
@@ -58,28 +59,53 @@ export class LocationModeSwitch extends BaseAccessory {
58
59
  : await getCurrentState());
59
60
  });
60
61
  targetState.on("set" /* CharacteristicEventTypes.SET */, async (state, callback) => {
61
- const { Characteristic: { SecuritySystemTargetState: State }, } = hap;
62
+ const { Characteristic: { SecuritySystemTargetState: State }, } = hap, requestId = ++this.modeRequestId;
62
63
  callback();
63
- if (state === State.NIGHT_ARM) {
64
- state = State.STAY_ARM;
65
- // Ring doesn't have night mode, so switch over to stay mode
66
- setTimeout(() => targetState.updateValue(state), 100);
64
+ try {
65
+ const current = await getCurrentState();
66
+ if (requestId !== this.modeRequestId) {
67
+ return;
68
+ }
69
+ if (state === State.NIGHT_ARM) {
70
+ state = State.STAY_ARM;
71
+ // Ring doesn't have night mode, so switch over to stay mode
72
+ setTimeout(() => {
73
+ if (requestId === this.modeRequestId) {
74
+ targetState.updateValue(state);
75
+ }
76
+ }, 100);
77
+ }
78
+ if (state === current) {
79
+ this.targetState = undefined;
80
+ return;
81
+ }
82
+ this.targetState = state;
83
+ if (state === State.AWAY_ARM) {
84
+ logInfo(`Setting ${this.location.name} Mode to away`);
85
+ await this.location.setLocationMode('away');
86
+ }
87
+ else if (state === State.DISARM) {
88
+ logInfo(`Setting ${this.location.name} Mode to disarmed`);
89
+ await this.location.setLocationMode('disarmed');
90
+ }
91
+ else {
92
+ logInfo(`Setting ${this.location.name} Mode to home`);
93
+ await this.location.setLocationMode('home');
94
+ }
67
95
  }
68
- if (state === (await getCurrentState())) {
69
- this.targetState = undefined;
70
- return;
71
- }
72
- this.targetState = state;
73
- if (state === State.AWAY_ARM) {
74
- logInfo(`Setting ${this.location.name} Mode to away`);
75
- return this.location.setLocationMode('away');
76
- }
77
- else if (state === State.DISARM) {
78
- logInfo(`Setting ${this.location.name} Mode to disarmed`);
79
- return this.location.setLocationMode('disarmed');
96
+ catch (e) {
97
+ logError('Failed to set location mode for ' + location.name);
98
+ logError(e);
99
+ if (requestId !== this.modeRequestId) {
100
+ return;
101
+ }
102
+ const current = await getCurrentState();
103
+ if (requestId === this.modeRequestId) {
104
+ this.targetState = undefined;
105
+ this.modeRequestId++;
106
+ targetState.updateValue(current);
107
+ }
80
108
  }
81
- logInfo(`Setting ${this.location.name} Mode to home`);
82
- return this.location.setLocationMode('home');
83
109
  });
84
110
  targetState.setProps({
85
111
  validValues: [
package/lib/lock.js CHANGED
@@ -1,6 +1,6 @@
1
- import { BaseDeviceAccessory } from "./base-device-accessory.js";
1
+ import { BaseDeviceAccessory } from './base-device-accessory.js';
2
2
  import { distinctUntilChanged } from 'rxjs/operators';
3
- import { hap } from "./hap.js";
3
+ import { hap } from './hap.js';
4
4
  function getCurrentState({ locked }) {
5
5
  const { Characteristic: { LockCurrentState: State }, } = hap;
6
6
  switch (locked) {
@@ -1,5 +1,5 @@
1
- import { BaseDeviceAccessory } from "./base-device-accessory.js";
2
- import { hap } from "./hap.js";
1
+ import { BaseDeviceAccessory } from './base-device-accessory.js';
2
+ import { hap } from './hap.js';
3
3
  export class MotionSensor extends BaseDeviceAccessory {
4
4
  device;
5
5
  accessory;
@@ -1,5 +1,5 @@
1
- import { BaseDeviceAccessory } from "./base-device-accessory.js";
2
- import { hap } from "./hap.js";
1
+ import { BaseDeviceAccessory } from './base-device-accessory.js';
2
+ import { hap } from './hap.js';
3
3
  import { logInfo } from 'ring-client-api/util';
4
4
  export class MultiLevelSwitch extends BaseDeviceAccessory {
5
5
  device;
package/lib/outlet.js CHANGED
@@ -1,5 +1,5 @@
1
- import { BaseDeviceAccessory } from "./base-device-accessory.js";
2
- import { hap } from "./hap.js";
1
+ import { BaseDeviceAccessory } from './base-device-accessory.js';
2
+ import { hap } from './hap.js';
3
3
  import { logInfo } from 'ring-client-api/util';
4
4
  export class Outlet extends BaseDeviceAccessory {
5
5
  device;
@@ -1,5 +1,5 @@
1
- import { hap } from "./hap.js";
2
- import { BaseDataAccessory } from "./base-data-accessory.js";
1
+ import { hap } from './hap.js';
2
+ import { BaseDataAccessory } from './base-data-accessory.js';
3
3
  import { logInfo } from 'ring-client-api/util';
4
4
  const burglarStates = [
5
5
  'burglar-alarm',
@@ -1,32 +1,32 @@
1
1
  import { RingApi, RingCamera, RingChime, RingDevice, RingDeviceCategory, RingDeviceType, RingIntercom, } from 'ring-client-api';
2
- import { hap } from "./hap.js";
3
- import { SecurityPanel } from "./security-panel.js";
4
- import { Chime } from "./chime.js";
5
- import { BrightnessOnly } from "./brightness-only.js";
6
- import { ContactSensor } from "./contact-sensor.js";
7
- import { MotionSensor } from "./motion-sensor.js";
8
- import { Lock } from "./lock.js";
9
- import { SmokeAlarm } from "./smoke-alarm.js";
10
- import { CoAlarm } from "./co-alarm.js";
11
- import { SmokeCoListener } from "./smoke-co-listener.js";
12
- import { controlCenterDisplayName, debug, getSystemId, updateHomebridgeConfig, } from "./config.js";
13
- import { Beam } from "./beam.js";
14
- import { MultiLevelSwitch } from "./multi-level-switch.js";
15
- import { Fan } from "./fan.js";
16
- import { Outlet } from "./outlet.js";
17
- import { Switch } from "./switch.js";
18
- import { Camera } from "./camera.js";
19
- import { PanicButtons } from "./panic-buttons.js";
2
+ import { hap } from './hap.js';
3
+ import { SecurityPanel } from './security-panel.js';
4
+ import { Chime } from './chime.js';
5
+ import { BrightnessOnly } from './brightness-only.js';
6
+ import { ContactSensor } from './contact-sensor.js';
7
+ import { MotionSensor } from './motion-sensor.js';
8
+ import { Lock } from './lock.js';
9
+ import { SmokeAlarm } from './smoke-alarm.js';
10
+ import { CoAlarm } from './co-alarm.js';
11
+ import { SmokeCoListener } from './smoke-co-listener.js';
12
+ import { controlCenterDisplayName, debug, getSystemId, updateHomebridgeConfig, } from './config.js';
13
+ import { Beam } from './beam.js';
14
+ import { MultiLevelSwitch } from './multi-level-switch.js';
15
+ import { Fan } from './fan.js';
16
+ import { Outlet } from './outlet.js';
17
+ import { Switch } from './switch.js';
18
+ import { Camera } from './camera.js';
19
+ import { PanicButtons } from './panic-buttons.js';
20
20
  import { logInfo, useLogger } from 'ring-client-api/util';
21
- import { FloodFreezeSensor } from "./flood-freeze-sensor.js";
22
- import { FreezeSensor } from "./freeze-sensor.js";
23
- import { TemperatureSensor } from "./temperature-sensor.js";
24
- import { WaterSensor } from "./water-sensor.js";
25
- import { LocationModeSwitch } from "./location-mode-switch.js";
26
- import { Thermostat } from "./thermostat.js";
27
- import { UnknownZWaveSwitchSwitch } from "./unknown-zwave-switch.js";
28
- import { Intercom } from "./intercom.js";
29
- import { Valve } from "./valve.js";
21
+ import { FloodFreezeSensor } from './flood-freeze-sensor.js';
22
+ import { FreezeSensor } from './freeze-sensor.js';
23
+ import { TemperatureSensor } from './temperature-sensor.js';
24
+ import { WaterSensor } from './water-sensor.js';
25
+ import { LocationModeSwitch } from './location-mode-switch.js';
26
+ import { Thermostat } from './thermostat.js';
27
+ import { UnknownZWaveSwitchSwitch } from './unknown-zwave-switch.js';
28
+ import { Intercom } from './intercom.js';
29
+ import { Valve } from './valve.js';
30
30
  const ignoreHiddenDeviceTypes = [
31
31
  RingDeviceType.RingNetAdapter,
32
32
  RingDeviceType.ZigbeeAdapter,
@@ -39,7 +39,7 @@ const ignoreHiddenDeviceTypes = [
39
39
  ];
40
40
  export const platformName = 'Ring';
41
41
  export const pluginName = 'homebridge-ring';
42
- function getAccessoryClass(device) {
42
+ export function getAccessoryClass(device) {
43
43
  const { deviceType } = device;
44
44
  if (device.data.status === 'disabled') {
45
45
  return null;
@@ -229,8 +229,7 @@ export class RingPlatform {
229
229
  }
230
230
  const createHomebridgeAccessory = () => {
231
231
  const accessory = new api.platformAccessory(displayName, uuid, isCamera
232
- ? 17 /* hap.Categories.CAMERA */
233
- : 11 /* hap.Categories.SECURITY_SYSTEM */);
232
+ ? 17 /* hap.Categories.CAMERA */ : 11 /* hap.Categories.SECURITY_SYSTEM */);
234
233
  if (isCamera) {
235
234
  logInfo(`Configured camera ${uuid} ${deviceType} ${displayName}`);
236
235
  externalAccessories.push(accessory);
@@ -1,6 +1,6 @@
1
- import { BaseDeviceAccessory } from "./base-device-accessory.js";
1
+ import { BaseDeviceAccessory } from './base-device-accessory.js';
2
2
  import { allAlarmStates, RingDeviceType } from 'ring-client-api';
3
- import { hap } from "./hap.js";
3
+ import { hap } from './hap.js';
4
4
  import { logError, logInfo } from 'ring-client-api/util';
5
5
  function isValidNightModeBypass(mode) {
6
6
  return mode && (mode === 'all' || mode === 'some');
@@ -45,7 +45,7 @@ export class SecurityPanel extends BaseDeviceAccessory {
45
45
  characteristicType: Characteristic.On,
46
46
  serviceType: Service.Switch,
47
47
  name: this.device.name + ' Siren',
48
- getValue: (data) => data.siren && data.siren.state === 'on',
48
+ getValue: (data) => Boolean(data.siren && data.siren.state === 'on'),
49
49
  setValue: (value) => {
50
50
  if (value) {
51
51
  return this.device.location.soundSiren();
@@ -1,5 +1,5 @@
1
- import { BaseDeviceAccessory } from "./base-device-accessory.js";
2
- import { hap } from "./hap.js";
1
+ import { BaseDeviceAccessory } from './base-device-accessory.js';
2
+ import { hap } from './hap.js';
3
3
  export class SmokeAlarm extends BaseDeviceAccessory {
4
4
  device;
5
5
  accessory;
@@ -1,5 +1,5 @@
1
- import { BaseDeviceAccessory } from "./base-device-accessory.js";
2
- import { hap } from "./hap.js";
1
+ import { BaseDeviceAccessory } from './base-device-accessory.js';
2
+ import { hap } from './hap.js';
3
3
  export class SmokeCoListener extends BaseDeviceAccessory {
4
4
  device;
5
5
  accessory;
package/lib/switch.js CHANGED
@@ -1,5 +1,5 @@
1
- import { BaseDeviceAccessory } from "./base-device-accessory.js";
2
- import { hap } from "./hap.js";
1
+ import { BaseDeviceAccessory } from './base-device-accessory.js';
2
+ import { hap } from './hap.js';
3
3
  import { logInfo } from 'ring-client-api/util';
4
4
  export class Switch extends BaseDeviceAccessory {
5
5
  device;
@@ -1,5 +1,5 @@
1
- import { BaseDeviceAccessory } from "./base-device-accessory.js";
2
- import { hap } from "./hap.js";
1
+ import { BaseDeviceAccessory } from './base-device-accessory.js';
2
+ import { hap } from './hap.js';
3
3
  import { distinctUntilChanged, map } from 'rxjs/operators';
4
4
  export class TemperatureSensor extends BaseDeviceAccessory {
5
5
  device;
package/lib/thermostat.js CHANGED
@@ -2,8 +2,8 @@ import { combineLatest } from 'rxjs';
2
2
  import { distinctUntilChanged, map, switchMap } from 'rxjs/operators';
3
3
  import { RingDeviceType } from 'ring-client-api';
4
4
  import { logDebug, logError, logInfo } from 'ring-client-api/util';
5
- import { BaseDeviceAccessory } from "./base-device-accessory.js";
6
- import { hap } from "./hap.js";
5
+ import { BaseDeviceAccessory } from './base-device-accessory.js';
6
+ import { hap } from './hap.js';
7
7
  export class Thermostat extends BaseDeviceAccessory {
8
8
  onTemperature;
9
9
  device;
@@ -1,5 +1,5 @@
1
- import { BaseDeviceAccessory } from "./base-device-accessory.js";
2
- import { hap } from "./hap.js";
1
+ import { BaseDeviceAccessory } from './base-device-accessory.js';
2
+ import { hap } from './hap.js';
3
3
  import { logInfo } from 'ring-client-api/util';
4
4
  export class UnknownZWaveSwitchSwitch extends BaseDeviceAccessory {
5
5
  device;
package/lib/valve.js CHANGED
@@ -1,5 +1,5 @@
1
- import { BaseDeviceAccessory } from "./base-device-accessory.js";
2
- import { hap } from "./hap.js";
1
+ import { BaseDeviceAccessory } from './base-device-accessory.js';
2
+ import { hap } from './hap.js';
3
3
  import { logInfo } from 'ring-client-api/util';
4
4
  export class Valve extends BaseDeviceAccessory {
5
5
  device;
@@ -1,5 +1,5 @@
1
- import { BaseDeviceAccessory } from "./base-device-accessory.js";
2
- import { hap } from "./hap.js";
1
+ import { BaseDeviceAccessory } from './base-device-accessory.js';
2
+ import { hap } from './hap.js';
3
3
  import { distinctUntilChanged, filter, map } from 'rxjs/operators';
4
4
  import { logInfo } from 'ring-client-api/util';
5
5
  export class WaterSensor extends BaseDeviceAccessory {
package/package.json CHANGED
@@ -1,32 +1,33 @@
1
1
  {
2
2
  "name": "homebridge-ring",
3
3
  "displayName": "Ring",
4
- "version": "14.3.0",
4
+ "version": "15.0.0-beta.0",
5
5
  "description": "Homebridge plugin for Ring doorbells, cameras, security alarm system and smart lighting",
6
6
  "type": "module",
7
7
  "main": "lib/index.js",
8
8
  "scripts": {
9
- "build": "rm -rf lib && tsc && cp -r ./homebridge-ui/public ./lib/homebridge-ui/public/",
9
+ "build": "rm -rf lib && tsc && cp -r ./homebridge-ui/public ./lib/homebridge-ui/public/ && rm -rf ./lib/test",
10
10
  "lint": "eslint .",
11
- "dev": "concurrently -c yellow,blue --kill-others \"npm:dev:build\" \"npm:dev:run\" ",
12
- "dev:build": "tsc --watch --preserveWatchOutput",
13
- "dev:run": "RING_DEBUG=true node --watch ../../node_modules/.bin/homebridge -U ../../.homebridge"
11
+ "test": "vitest run",
12
+ "dev": "bash ./dev-homebridge.sh",
13
+ "dev:run": "npm run dev"
14
14
  },
15
15
  "dependencies": {
16
16
  "@homebridge/camera-utils": "^3.0.0",
17
- "@homebridge/plugin-ui-utils": "^2.1.2",
18
- "ring-client-api": "14.3.0",
19
- "werift": "0.22.4"
17
+ "@homebridge/plugin-ui-utils": "^2.2.7",
18
+ "ring-client-api": "15.0.0-beta.0",
19
+ "werift": "0.26.0"
20
20
  },
21
21
  "devDependencies": {
22
- "concurrently": "^9.2.1",
22
+ "@homebridge/hap-nodejs": "^2.3.0",
23
23
  "eslint-config-shared": "*",
24
- "homebridge": "1.11.1",
24
+ "homebridge": "2.5.0",
25
25
  "tsconfig": "*",
26
- "typescript": "5.9.3"
26
+ "typescript": "7.0.2",
27
+ "vitest": "^5.1.0"
27
28
  },
28
29
  "engines": {
29
- "node": "^20 || ^22 || ^24",
30
+ "node": "^22.19.0 || ^24 || ^26",
30
31
  "homebridge": ">=1.4.0 || ^2.0.0-beta.0"
31
32
  },
32
33
  "repository": {