ring-client-api 14.0.0-beta.0 → 14.0.0-beta.2

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 CHANGED
@@ -18,12 +18,12 @@ export interface RingApiOptions extends SessionOptions {
18
18
  };
19
19
  }
20
20
  export declare class RingApi extends Subscribed {
21
- readonly options: RingApiOptions & RefreshTokenAuth;
22
21
  readonly restClient: RingRestClient;
23
22
  readonly onRefreshTokenUpdated: import("rxjs").Observable<{
24
23
  oldRefreshToken?: string;
25
24
  newRefreshToken: string;
26
25
  }>;
26
+ readonly options: RingApiOptions & RefreshTokenAuth;
27
27
  constructor(options: RingApiOptions & RefreshTokenAuth);
28
28
  fetchRingDevices(): Promise<{
29
29
  doorbots: CameraData[];
package/lib/api.js CHANGED
@@ -12,9 +12,9 @@ import { PushReceiver } from '@eneris/push-receiver';
12
12
  import { RingIntercom } from "./ring-intercom.js";
13
13
  import JSONbig from 'json-bigint';
14
14
  export class RingApi extends Subscribed {
15
- options;
16
15
  restClient;
17
16
  onRefreshTokenUpdated;
17
+ options;
18
18
  constructor(options) {
19
19
  super();
20
20
  this.options = options;
package/lib/location.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import type { Socket as SocketIOSocket } from 'socket.io-client';
2
1
  import type { Observable } from 'rxjs';
2
+ import { WebSocket } from 'undici';
3
3
  import { BehaviorSubject, ReplaySubject, Subject } from 'rxjs';
4
4
  import type { AccountMonitoringStatus, AlarmMode, AssetSession, MessageDataType, MessageType, RingDeviceData, SocketIoMessage, TicketAsset, UserLocation, CameraEventResponse, CameraEventOptions, HistoryOptions, RingDeviceHistoryEvent, LocationModeResponse, LocationModeSettings, LocationMode, LocationModeSharing, LocationModeSettingsResponse, LocationModeInput } from './ring-types.ts';
5
5
  import type { RingRestClient } from './rest-client.ts';
@@ -9,16 +9,6 @@ import { RingDevice } from './ring-device.ts';
9
9
  import type { RingIntercom } from './ring-intercom.ts';
10
10
  import { Subscribed } from './subscribed.ts';
11
11
  export declare class Location extends Subscribed {
12
- readonly locationDetails: UserLocation;
13
- readonly cameras: RingCamera[];
14
- readonly chimes: RingChime[];
15
- readonly intercoms: RingIntercom[];
16
- readonly options: {
17
- hasHubs: boolean;
18
- hasAlarmBaseStation: boolean;
19
- locationModePollingSeconds?: number;
20
- };
21
- private restClient;
22
12
  private seq;
23
13
  onMessage: Subject<SocketIoMessage>;
24
14
  onDataUpdate: Subject<SocketIoMessage>;
@@ -31,13 +21,23 @@ export declare class Location extends Subscribed {
31
21
  private onLocationModeRequested;
32
22
  reconnecting: boolean;
33
23
  private disconnected;
34
- connectionPromise?: Promise<typeof SocketIOSocket>;
24
+ connectionPromise?: Promise<WebSocket>;
35
25
  securityPanel?: RingDevice;
36
26
  assets?: TicketAsset[];
37
27
  receivedAssetDeviceLists: string[];
38
28
  offlineAssets: string[];
39
29
  hasHubs: boolean;
40
30
  hasAlarmBaseStation: boolean;
31
+ readonly locationDetails: UserLocation;
32
+ readonly cameras: RingCamera[];
33
+ readonly chimes: RingChime[];
34
+ readonly intercoms: RingIntercom[];
35
+ readonly options: {
36
+ hasHubs: boolean;
37
+ hasAlarmBaseStation: boolean;
38
+ locationModePollingSeconds?: number;
39
+ };
40
+ private restClient;
41
41
  constructor(locationDetails: UserLocation, cameras: RingCamera[], chimes: RingChime[], intercoms: RingIntercom[], options: {
42
42
  hasHubs: boolean;
43
43
  hasAlarmBaseStation: boolean;
@@ -46,8 +46,8 @@ export declare class Location extends Subscribed {
46
46
  get id(): string;
47
47
  get locationId(): string;
48
48
  get name(): string;
49
- createConnection(): Promise<typeof SocketIOSocket>;
50
- getConnection(): Promise<SocketIOClient.Socket>;
49
+ createConnection(): Promise<WebSocket>;
50
+ getConnection(): Promise<WebSocket>;
51
51
  sendMessage(message: {
52
52
  msg: MessageType;
53
53
  datatype?: MessageDataType;
package/lib/location.js CHANGED
@@ -1,4 +1,4 @@
1
- import { connect as connectSocketIo } from 'socket.io-client';
1
+ import { WebSocket } from 'undici';
2
2
  import { BehaviorSubject, firstValueFrom, merge, ReplaySubject, Subject, } from 'rxjs';
3
3
  import { concatMap, debounceTime, distinctUntilChanged, filter, map, scan, shareReplay, skip, } from 'rxjs/operators';
4
4
  import { delay, generateUuid, logDebug, logError, logInfo } from "./util.js";
@@ -12,12 +12,6 @@ function flattenDeviceData(data) {
12
12
  return Object.assign({}, data.general && data.general.v2, data.device && data.device.v1);
13
13
  }
14
14
  export class Location extends Subscribed {
15
- locationDetails;
16
- cameras;
17
- chimes;
18
- intercoms;
19
- options;
20
- restClient;
21
15
  seq = 1;
22
16
  onMessage = new Subject();
23
17
  onDataUpdate = new Subject();
@@ -57,6 +51,12 @@ export class Location extends Subscribed {
57
51
  offlineAssets = [];
58
52
  hasHubs;
59
53
  hasAlarmBaseStation;
54
+ locationDetails;
55
+ cameras;
56
+ chimes;
57
+ intercoms;
58
+ options;
59
+ restClient;
60
60
  constructor(locationDetails, cameras, chimes, intercoms, options, restClient) {
61
61
  super();
62
62
  this.locationDetails = locationDetails;
@@ -80,7 +80,6 @@ export class Location extends Subscribed {
80
80
  }
81
81
  if (connectionStatus === 'online') {
82
82
  if (assetWasOffline) {
83
- // eslint-disable-next-line @typescript-eslint/no-empty-function
84
83
  this.requestList(deviceListMessageType, assetUuid).catch(() => { });
85
84
  this.offlineAssets = this.offlineAssets.filter((id) => id !== assetUuid);
86
85
  logInfo(`Ring ${asset.kind} ${assetUuid} has come back online`);
@@ -117,7 +116,7 @@ export class Location extends Subscribed {
117
116
  logError('Node 15 is not currently supported by the Ring client. Please install the latest Node 14 instead. May not be able to fetch devices from Ring Alarm and Smart Lighting Hubs on this version of node.');
118
117
  }
119
118
  const { assets, ticket, host } = await this.restClient.request({
120
- url: appApi('clap/tickets?locationID=' + this.id),
119
+ url: appApi(`clap/tickets?locationID=${this.id}&enableExtendedEmergencyCellUsage=true&requestedTransport=ws`),
121
120
  }), supportedAssets = assets.filter(isWebSocketSupportedAsset);
122
121
  this.assets = supportedAssets;
123
122
  this.receivedAssetDeviceLists.length = 0;
@@ -127,7 +126,7 @@ export class Location extends Subscribed {
127
126
  logError(errorMessage);
128
127
  throw new Error(errorMessage);
129
128
  }
130
- const connection = connectSocketIo(`wss://${host}/?authcode=${ticket}&ack=false&EIO=3`, { transports: ['websocket'] }), reconnect = () => {
129
+ const url = `wss://${host}/ws?authcode=${ticket}&ack=false`, socket = new WebSocket(url), reconnect = () => {
131
130
  if (this.reconnecting && this.connectionPromise) {
132
131
  return this.connectionPromise;
133
132
  }
@@ -136,30 +135,44 @@ export class Location extends Subscribed {
136
135
  logInfo('Reconnecting location socket.io connection');
137
136
  }
138
137
  this.reconnecting = true;
139
- connection.close();
138
+ socket.close();
140
139
  return (this.connectionPromise = delay(1000).then(() => {
141
140
  return this.createConnection();
142
141
  }));
143
142
  };
144
- this.reconnecting = false;
145
- connection.on('DataUpdate', (message) => {
146
- if (message.datatype === 'HubDisconnectionEventType') {
147
- logInfo('Location connection told to reconnect');
148
- return reconnect();
143
+ socket.addEventListener('message', (event) => {
144
+ try {
145
+ const { msg: message, channel } = JSON.parse(event.data);
146
+ this.onMessage.next(message);
147
+ if (message.datatype === 'HubDisconnectionEventType') {
148
+ logInfo('Location connection told to reconnect');
149
+ return reconnect();
150
+ }
151
+ if (channel === 'DataUpdate') {
152
+ this.onDataUpdate.next(message);
153
+ }
154
+ }
155
+ catch (e) {
156
+ logError('Error parsing message from server: ' + event.data);
149
157
  }
150
- this.onDataUpdate.next(message);
151
158
  });
152
- connection.on('message', (message) => this.onMessage.next(message));
153
- connection.on('error', reconnect);
154
- connection.on('disconnect', reconnect);
159
+ socket.addEventListener('error', (error) => {
160
+ logDebug('WebSocket error: ' + error);
161
+ return reconnect();
162
+ });
163
+ socket.addEventListener('close', (event) => {
164
+ logDebug(`WebSocket connection closed: ${event.code}, ${event.reason}`);
165
+ return reconnect();
166
+ });
167
+ this.reconnecting = false;
155
168
  return new Promise((resolve, reject) => {
156
- connection.once('connect', () => {
157
- resolve(connection);
169
+ socket.addEventListener('open', () => {
170
+ resolve(socket);
158
171
  this.onConnected.next(true);
159
172
  logInfo('Ring connected to socket.io server');
160
173
  assets.forEach((asset) => this.requestList(deviceListMessageType, asset.uuid));
161
- });
162
- connection.once('error', reject);
174
+ }, { once: true });
175
+ socket.addEventListener('error', reject, { once: true });
163
176
  }).catch(reconnect);
164
177
  }
165
178
  getConnection() {
@@ -174,7 +187,10 @@ export class Location extends Subscribed {
174
187
  async sendMessage(message) {
175
188
  const connection = await this.getConnection();
176
189
  message.seq = this.seq++;
177
- connection.emit('message', message);
190
+ connection.send(JSON.stringify({
191
+ channel: 'message',
192
+ msg: message,
193
+ }));
178
194
  }
179
195
  async sendCommandToSecurityPanel(commandType, data) {
180
196
  const securityPanel = await this.getSecurityPanel();
@@ -27,5 +27,4 @@ export async function logRefreshToken() {
27
27
  console.log('\nSuccessfully logged in to Ring. Please add the following to your config:\n');
28
28
  console.log(`"refreshToken": "${refreshToken}"`);
29
29
  }
30
- // eslint-disable-next-line @typescript-eslint/no-empty-function
31
30
  process.on('unhandledRejection', () => { });
@@ -29,7 +29,6 @@ export interface SessionOptions {
29
29
  controlCenterDisplayName?: string;
30
30
  }
31
31
  export declare class RingRestClient {
32
- private authOptions;
33
32
  refreshToken: string | undefined;
34
33
  private authConfig;
35
34
  private hardwareIdPromise;
@@ -49,6 +48,7 @@ export declare class RingRestClient {
49
48
  api_version: number;
50
49
  device_model: string;
51
50
  };
51
+ private authOptions;
52
52
  constructor(authOptions: (EmailAuth | RefreshTokenAuth) & SessionOptions);
53
53
  private getGrantData;
54
54
  getAuth(twoFactorAuthCode?: string): Promise<AuthTokenResponse>;
@@ -142,7 +142,6 @@ function parseAuthConfig(rawRefreshToken) {
142
142
  }
143
143
  }
144
144
  export class RingRestClient {
145
- authOptions;
146
145
  refreshToken;
147
146
  authConfig;
148
147
  hardwareIdPromise;
@@ -177,6 +176,7 @@ export class RingRestClient {
177
176
  onRefreshTokenUpdated = new ReplaySubject(1);
178
177
  onSession = new ReplaySubject(1);
179
178
  baseSessionMetadata;
179
+ authOptions;
180
180
  constructor(authOptions) {
181
181
  this.authOptions = authOptions;
182
182
  this.refreshToken =
@@ -17,12 +17,8 @@ export declare function getSearchQueryString(options: CameraEventOptions | (Hist
17
17
  })): string;
18
18
  export declare function cleanSnapshotUuid(uuid?: string | null): string | null | undefined;
19
19
  export declare class RingCamera extends Subscribed {
20
- private initialData;
21
- isDoorbot: boolean;
22
- private restClient;
23
- private avoidSnapshotBatteryDrain;
24
20
  id: number;
25
- deviceType: RingCameraKind.onvif_camera | Omit<RingCameraKind, RingCameraKind.onvif_camera>;
21
+ deviceType: "onvif_camera" | Omit<RingCameraKind, "onvif_camera">;
26
22
  model: string;
27
23
  onData: BehaviorSubject<AnyCameraData>;
28
24
  hasLight: boolean;
@@ -35,6 +31,10 @@ export declare class RingCamera extends Subscribed {
35
31
  onMotionStarted: import("rxjs").Observable<null>;
36
32
  onBatteryLevel: import("rxjs").Observable<number | null>;
37
33
  onInHomeDoorbellStatus: import("rxjs").Observable<boolean>;
34
+ private initialData;
35
+ isDoorbot: boolean;
36
+ private restClient;
37
+ private avoidSnapshotBatteryDrain;
38
38
  constructor(initialData: AnyCameraData, isDoorbot: boolean, restClient: RingRestClient, avoidSnapshotBatteryDrain: boolean);
39
39
  updateData(update: AnyCameraData): void;
40
40
  requestUpdate(): void;
@@ -64,12 +64,11 @@ export function cleanSnapshotUuid(uuid) {
64
64
  }
65
65
  const wiredModelsWithNoSnapshotDuringRecording = new Set([
66
66
  RingCameraKind.doorbell_graham_cracker,
67
+ ]), enabledDoorbellTypes = new Set([
68
+ DoorbellType.Mechanical,
69
+ DoorbellType.Digital,
67
70
  ]);
68
71
  export class RingCamera extends Subscribed {
69
- initialData;
70
- isDoorbot;
71
- restClient;
72
- avoidSnapshotBatteryDrain;
73
72
  id;
74
73
  deviceType;
75
74
  model;
@@ -91,6 +90,10 @@ export class RingCamera extends Subscribed {
91
90
  share());
92
91
  onBatteryLevel;
93
92
  onInHomeDoorbellStatus;
93
+ initialData;
94
+ isDoorbot;
95
+ restClient;
96
+ avoidSnapshotBatteryDrain;
94
97
  constructor(initialData, isDoorbot, restClient, avoidSnapshotBatteryDrain) {
95
98
  super();
96
99
  this.initialData = initialData;
@@ -188,8 +191,7 @@ export class RingCamera extends Subscribed {
188
191
  get hasInHomeDoorbell() {
189
192
  const { chime_settings } = this.data.settings;
190
193
  return (this.isDoorbot &&
191
- Boolean(chime_settings &&
192
- [DoorbellType.Mechanical, DoorbellType.Digital].includes(chime_settings.type)));
194
+ Boolean(chime_settings && enabledDoorbellTypes.has(chime_settings.type)));
193
195
  }
194
196
  doorbotUrl(path = '') {
195
197
  return clientApi(`doorbots/${this.id}/${path}`);
@@ -2,13 +2,13 @@ import type { ChimeData, ChimeUpdate, ChimeSoundKind, RingtoneOptions, ChimeHeal
2
2
  import type { RingRestClient } from './rest-client.ts';
3
3
  import { BehaviorSubject, Subject } from 'rxjs';
4
4
  export declare class RingChime {
5
- private initialData;
6
- private restClient;
7
5
  id: number;
8
6
  deviceType: import("./ring-types.ts").ChimeKind;
9
7
  model: string;
10
8
  onData: BehaviorSubject<ChimeData>;
11
9
  onRequestUpdate: Subject<unknown>;
10
+ private initialData;
11
+ private restClient;
12
12
  constructor(initialData: ChimeData, restClient: RingRestClient);
13
13
  updateData(update: ChimeData): void;
14
14
  requestUpdate(): void;
package/lib/ring-chime.js CHANGED
@@ -8,18 +8,18 @@ const settingsWhichRequireReboot = [
8
8
  'motion_audio_user_id',
9
9
  ];
10
10
  export class RingChime {
11
- initialData;
12
- restClient;
13
11
  id;
14
12
  deviceType;
15
13
  model;
16
14
  onData;
17
15
  onRequestUpdate = new Subject();
16
+ initialData;
17
+ restClient;
18
18
  constructor(initialData, restClient) {
19
19
  this.initialData = initialData;
20
20
  this.restClient = restClient;
21
- this.id = this.initialData.id;
22
- this.deviceType = this.initialData.kind;
21
+ this.id = initialData.id;
22
+ this.deviceType = initialData.kind;
23
23
  this.model = ChimeModel[this.deviceType] || 'Chime';
24
24
  this.onData = new BehaviorSubject(this.initialData);
25
25
  }
@@ -3,15 +3,15 @@ import type { RingDeviceData } from './ring-types.ts';
3
3
  import type { Location } from './location.ts';
4
4
  import { Subscribed } from './subscribed.ts';
5
5
  export declare class RingDevice extends Subscribed {
6
- private initialData;
7
- location: Location;
8
- assetId: string;
9
6
  onData: BehaviorSubject<RingDeviceData>;
10
7
  zid: string;
11
8
  id: string;
12
9
  deviceType: import("./ring-types.ts").RingDeviceType;
13
10
  categoryId: number;
14
11
  onComponentDevices: import("rxjs").Observable<RingDevice[]>;
12
+ private initialData;
13
+ location: Location;
14
+ assetId: string;
15
15
  constructor(initialData: RingDeviceData, location: Location, assetId: string);
16
16
  updateData(update: Partial<RingDeviceData>): void;
17
17
  get data(): RingDeviceData;
@@ -4,22 +4,22 @@ import { filter, map } from 'rxjs/operators';
4
4
  import { Subscribed } from "./subscribed.js";
5
5
  import { logError } from "./util.js";
6
6
  export class RingDevice extends Subscribed {
7
- initialData;
8
- location;
9
- assetId;
10
7
  onData;
11
8
  zid;
12
9
  id;
13
10
  deviceType;
14
11
  categoryId;
15
12
  onComponentDevices;
13
+ initialData;
14
+ location;
15
+ assetId;
16
16
  constructor(initialData, location, assetId) {
17
17
  super();
18
18
  this.initialData = initialData;
19
19
  this.location = location;
20
20
  this.assetId = assetId;
21
- this.onData = new BehaviorSubject(this.initialData);
22
- this.zid = this.initialData.zid;
21
+ this.onData = new BehaviorSubject(initialData);
22
+ this.zid = initialData.zid;
23
23
  this.id = this.zid;
24
24
  this.deviceType = this.initialData.deviceType;
25
25
  this.categoryId = this.initialData.categoryId;
@@ -2,15 +2,15 @@ import type { IntercomHandsetAudioData, PushNotification } from './ring-types.ts
2
2
  import type { RingRestClient } from './rest-client.ts';
3
3
  import { BehaviorSubject, Subject } from 'rxjs';
4
4
  export declare class RingIntercom {
5
- private initialData;
6
- private restClient;
7
5
  id: number;
8
- deviceType: import("./ring-types.ts").RingDeviceType.IntercomHandsetAudio;
6
+ deviceType: "intercom_handset_audio";
9
7
  onData: BehaviorSubject<IntercomHandsetAudioData>;
10
8
  onRequestUpdate: Subject<unknown>;
11
9
  onBatteryLevel: import("rxjs").Observable<number | null>;
12
10
  onDing: Subject<void>;
13
11
  onUnlocked: Subject<void>;
12
+ private initialData;
13
+ private restClient;
14
14
  constructor(initialData: IntercomHandsetAudioData, restClient: RingRestClient);
15
15
  updateData(update: IntercomHandsetAudioData): void;
16
16
  requestUpdate(): void;
@@ -5,8 +5,6 @@ import { distinctUntilChanged, map } from 'rxjs/operators';
5
5
  import { getBatteryLevel } from "./ring-camera.js";
6
6
  import { logError } from "./util.js";
7
7
  export class RingIntercom {
8
- initialData;
9
- restClient;
10
8
  id;
11
9
  deviceType;
12
10
  onData;
@@ -14,11 +12,13 @@ export class RingIntercom {
14
12
  onBatteryLevel;
15
13
  onDing = new Subject();
16
14
  onUnlocked = new Subject();
15
+ initialData;
16
+ restClient;
17
17
  constructor(initialData, restClient) {
18
18
  this.initialData = initialData;
19
19
  this.restClient = restClient;
20
- this.id = this.initialData.id;
21
- this.deviceType = this.initialData.kind;
20
+ this.id = initialData.id;
21
+ this.deviceType = initialData.kind;
22
22
  this.onData = new BehaviorSubject(this.initialData);
23
23
  this.onBatteryLevel = this.onData.pipe(map((data) => getBatteryLevel(data)), distinctUntilChanged());
24
24
  if (!initialData.subscribed) {