@jgrimard/homebridge-vivint 2.0.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/LICENSE +13 -0
- package/README.md +107 -0
- package/config.schema.json +90 -0
- package/dist/devices/camera.js +368 -0
- package/dist/devices/camera.js.map +1 -0
- package/dist/devices/carbonMonoxideSensor.js +44 -0
- package/dist/devices/carbonMonoxideSensor.js.map +1 -0
- package/dist/devices/contactSensor.js +65 -0
- package/dist/devices/contactSensor.js.map +1 -0
- package/dist/devices/device.js +147 -0
- package/dist/devices/device.js.map +1 -0
- package/dist/devices/dimmerSwitch.js +12 -0
- package/dist/devices/dimmerSwitch.js.map +1 -0
- package/dist/devices/garageDoor.js +58 -0
- package/dist/devices/garageDoor.js.map +1 -0
- package/dist/devices/index.js +55 -0
- package/dist/devices/index.js.map +1 -0
- package/dist/devices/lightGroup.js +15 -0
- package/dist/devices/lightGroup.js.map +1 -0
- package/dist/devices/lightSwitch.js +42 -0
- package/dist/devices/lightSwitch.js.map +1 -0
- package/dist/devices/lock.js +49 -0
- package/dist/devices/lock.js.map +1 -0
- package/dist/devices/motionSensor.js +73 -0
- package/dist/devices/motionSensor.js.map +1 -0
- package/dist/devices/multiLevelSwitchBase.js +55 -0
- package/dist/devices/multiLevelSwitchBase.js.map +1 -0
- package/dist/devices/panel.js +79 -0
- package/dist/devices/panel.js.map +1 -0
- package/dist/devices/smokeSensor.js +47 -0
- package/dist/devices/smokeSensor.js.map +1 -0
- package/dist/devices/thermostat.js +216 -0
- package/dist/devices/thermostat.js.map +1 -0
- package/dist/homebridge-ui/public/index.html +138 -0
- package/dist/homebridge-ui/server.js +120 -0
- package/dist/homebridge-ui/server.js.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/platform.js +393 -0
- package/dist/platform.js.map +1 -0
- package/dist/settings.js +12 -0
- package/dist/settings.js.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/debounce.js +49 -0
- package/dist/utils/debounce.js.map +1 -0
- package/dist/utils/ports.js +39 -0
- package/dist/utils/ports.js.map +1 -0
- package/dist/utils/redact.js +19 -0
- package/dist/utils/redact.js.map +1 -0
- package/dist/utils/sanitizeName.js +22 -0
- package/dist/utils/sanitizeName.js.map +1 -0
- package/dist/vivint/api.js +246 -0
- package/dist/vivint/api.js.map +1 -0
- package/dist/vivint/datapatch.js +52 -0
- package/dist/vivint/datapatch.js.map +1 -0
- package/dist/vivint/dictionary.js +66 -0
- package/dist/vivint/dictionary.js.map +1 -0
- package/dist/vivint/eventStream.js +77 -0
- package/dist/vivint/eventStream.js.map +1 -0
- package/dist/vivint/vivint_dictionary.json +8720 -0
- package/package.json +62 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C;;GAEG;AACH,eAAe,CAAC,GAAQ,EAAE,EAAE;IAC1B,GAAG,CAAC,gBAAgB,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC;AACtD,CAAC,CAAC"}
|
package/dist/platform.js
ADDED
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
import { findHandlerByClassName, findHandlerForData, IRRELEVANT_DEVICE_TYPES, } from './devices/index.js';
|
|
2
|
+
import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js';
|
|
3
|
+
import { sanitizeDeviceName } from './utils/sanitizeName.js';
|
|
4
|
+
import { VivintApiClient, VivintAuthError, VivintRateLimitError } from './vivint/api.js';
|
|
5
|
+
import { VivintDict } from './vivint/dictionary.js';
|
|
6
|
+
import { VivintEventStream } from './vivint/eventStream.js';
|
|
7
|
+
const DEFAULT_API_LOGIN_REFRESH_SECS = 1200;
|
|
8
|
+
const MIN_API_LOGIN_REFRESH_SECS = 300;
|
|
9
|
+
// Startup retry backoff. Rate-limit responses back off much harder, because
|
|
10
|
+
// retrying into a Cloudflare block can extend it to 24 hours.
|
|
11
|
+
const RETRY_BASE_MS = 30_000;
|
|
12
|
+
const RETRY_MAX_MS = 15 * 60_000;
|
|
13
|
+
const RETRY_RATE_LIMITED_BASE_MS = 5 * 60_000;
|
|
14
|
+
const RETRY_RATE_LIMITED_MAX_MS = 60 * 60_000;
|
|
15
|
+
const RETRY_AUTH_MS = 60 * 60_000;
|
|
16
|
+
export class VivintPlatform {
|
|
17
|
+
log;
|
|
18
|
+
api;
|
|
19
|
+
Service;
|
|
20
|
+
Characteristic;
|
|
21
|
+
config;
|
|
22
|
+
vivintApi;
|
|
23
|
+
ffmpegPath = 'ffmpeg';
|
|
24
|
+
cachedAccessories = new Map();
|
|
25
|
+
devices = new Map();
|
|
26
|
+
panelDeviceId = 0;
|
|
27
|
+
lastSnapshotTime = 0;
|
|
28
|
+
eventStream;
|
|
29
|
+
sessionRefreshTimer;
|
|
30
|
+
pollTimer;
|
|
31
|
+
retryTimer;
|
|
32
|
+
shuttingDown = false;
|
|
33
|
+
pollInFlight = false;
|
|
34
|
+
pollConsecutiveFailures = 0;
|
|
35
|
+
pollTicksToSkip = 0;
|
|
36
|
+
constructor(log, config, api) {
|
|
37
|
+
this.log = log;
|
|
38
|
+
this.api = api;
|
|
39
|
+
this.config = config;
|
|
40
|
+
this.Service = api.hap.Service;
|
|
41
|
+
this.Characteristic = api.hap.Characteristic;
|
|
42
|
+
this.eventStream = new VivintEventStream(log);
|
|
43
|
+
this.api.on('didFinishLaunching', () => {
|
|
44
|
+
this.initialize().catch(error => this.log.error('Unexpected initialization error:', error));
|
|
45
|
+
});
|
|
46
|
+
this.api.on('shutdown', () => {
|
|
47
|
+
this.shuttingDown = true;
|
|
48
|
+
this.stopTimers();
|
|
49
|
+
this.eventStream.stop();
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Called by Homebridge for every accessory restored from cache.
|
|
54
|
+
*/
|
|
55
|
+
configureAccessory(accessory) {
|
|
56
|
+
this.log.debug('Loading accessory from cache:', accessory.displayName);
|
|
57
|
+
this.cachedAccessories.set(accessory.UUID, accessory);
|
|
58
|
+
}
|
|
59
|
+
get apiLoginRefreshSecs() {
|
|
60
|
+
const configured = this.config.apiLoginRefreshSecs ?? DEFAULT_API_LOGIN_REFRESH_SECS;
|
|
61
|
+
if (configured < MIN_API_LOGIN_REFRESH_SECS) {
|
|
62
|
+
this.log.warn(`apiLoginRefreshSecs of ${configured}s is too aggressive and risks Vivint rate limiting; using ${MIN_API_LOGIN_REFRESH_SECS}s.`);
|
|
63
|
+
return MIN_API_LOGIN_REFRESH_SECS;
|
|
64
|
+
}
|
|
65
|
+
return configured;
|
|
66
|
+
}
|
|
67
|
+
async initialize() {
|
|
68
|
+
const refreshToken = this.config.refreshToken;
|
|
69
|
+
if (!refreshToken || typeof refreshToken !== 'string' || refreshToken.trim().length === 0) {
|
|
70
|
+
this.log.error('No Vivint refresh token is configured. Open the plugin settings in the Homebridge UI '
|
|
71
|
+
+ 'and use the sign-in form (it supports Vivint\'s multi-factor authentication) to generate one. '
|
|
72
|
+
+ 'Cached accessories will remain visible but will not update.');
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
await this.resolveFfmpegPath();
|
|
76
|
+
this.vivintApi = new VivintApiClient(refreshToken, this.log);
|
|
77
|
+
this.connectWithRetry(1);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Connects to the Vivint API, retrying forever with error-appropriate
|
|
81
|
+
* backoff. The plugin stays alive during Vivint or network outages and
|
|
82
|
+
* recovers on its own.
|
|
83
|
+
*/
|
|
84
|
+
connectWithRetry(attempt) {
|
|
85
|
+
if (this.shuttingDown) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
this.vivintApi.connect().then(() => {
|
|
89
|
+
this.log.info('Connected to the Vivint API.');
|
|
90
|
+
this.onConnected();
|
|
91
|
+
}).catch((error) => {
|
|
92
|
+
let delayMs;
|
|
93
|
+
if (error instanceof VivintAuthError) {
|
|
94
|
+
this.log.error(`${error.message} Will re-check once per hour in case the token is updated.`);
|
|
95
|
+
delayMs = RETRY_AUTH_MS;
|
|
96
|
+
}
|
|
97
|
+
else if (error instanceof VivintRateLimitError) {
|
|
98
|
+
delayMs = Math.min(RETRY_RATE_LIMITED_BASE_MS * 2 ** (attempt - 1), RETRY_RATE_LIMITED_MAX_MS);
|
|
99
|
+
this.log.warn(`${error.message} Retrying in ${Math.round(delayMs / 60000)} minute(s).`);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
delayMs = Math.min(RETRY_BASE_MS * 2 ** (attempt - 1), RETRY_MAX_MS);
|
|
103
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
104
|
+
this.log.warn(`Could not connect to Vivint (${message}). Retrying in ${Math.round(delayMs / 1000)} second(s).`);
|
|
105
|
+
}
|
|
106
|
+
// Add jitter so many Homebridge instances do not retry in lockstep.
|
|
107
|
+
delayMs = Math.round(delayMs * (0.85 + Math.random() * 0.3));
|
|
108
|
+
this.retryTimer = setTimeout(() => this.connectWithRetry(attempt + 1), delayMs);
|
|
109
|
+
this.retryTimer.unref();
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
onConnected() {
|
|
113
|
+
try {
|
|
114
|
+
this.reconcileAccessories();
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
this.log.error('Error while setting up accessories:', error);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
this.handleSnapshot();
|
|
121
|
+
this.eventStream.start(this.vivintApi.messageBroadcastChannel, message => this.handlePubNubMessage(message));
|
|
122
|
+
this.startTimers();
|
|
123
|
+
}
|
|
124
|
+
async resolveFfmpegPath() {
|
|
125
|
+
try {
|
|
126
|
+
const mod = await import('ffmpeg-for-homebridge');
|
|
127
|
+
const resolved = typeof mod === 'string' ? mod : mod.default;
|
|
128
|
+
if (typeof resolved === 'string' && resolved.length > 0) {
|
|
129
|
+
this.ffmpegPath = resolved;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
this.log.debug('ffmpeg-for-homebridge binary not available, falling back to ffmpeg from PATH.');
|
|
134
|
+
}
|
|
135
|
+
this.log.debug('Using ffmpeg at:', this.ffmpegPath);
|
|
136
|
+
}
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
// Accessory lifecycle
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
/**
|
|
141
|
+
* The UUID seed must stay identical to previous plugin versions so users'
|
|
142
|
+
* existing accessories (rooms, automations, names) survive upgrades.
|
|
143
|
+
*/
|
|
144
|
+
serialFor(data) {
|
|
145
|
+
const hex = (value) => (value || 0).toString(16).padStart(8, '0');
|
|
146
|
+
return `${hex(data.SerialNumber32Bit)}:${hex(data.SerialNumber)}:${data.Id}`;
|
|
147
|
+
}
|
|
148
|
+
isIgnored(data) {
|
|
149
|
+
const ignored = this.config.ignoreDeviceTypes ?? [];
|
|
150
|
+
return ignored.includes(data.Type ?? '')
|
|
151
|
+
|| ignored.includes(`${data.EquipmentCode}`)
|
|
152
|
+
|| ignored.includes(`${data.Id}`);
|
|
153
|
+
}
|
|
154
|
+
reconcileAccessories() {
|
|
155
|
+
const snapshotDevices = this.vivintApi.deviceSnapshot().Devices.filter(data => data.Id);
|
|
156
|
+
const managed = [];
|
|
157
|
+
for (const data of snapshotDevices) {
|
|
158
|
+
if (IRRELEVANT_DEVICE_TYPES.includes(data.Type ?? '')) {
|
|
159
|
+
this.log.debug(`Skipping unusable device [Type]:${data.Type} [ID]:${data.Id}`);
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
const handler = findHandlerForData(data);
|
|
163
|
+
if (!handler) {
|
|
164
|
+
this.log.info(`Device not (yet) supported [ID]:${data.Id} [Type]:${data.Type} [EquipmentCode]:${data.EquipmentCode} [Name]:${data.Name}`);
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (this.isIgnored(data)) {
|
|
168
|
+
this.log.info(`Ignored device [ID]:${data.Id} [Type]:${data.Type} [EquipmentCode]:${data.EquipmentCode} [Name]:${data.Name}`);
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (this.config.logDeviceList === true) {
|
|
172
|
+
this.log.info(`Managing device [ID]:${data.Id} [Type]:${data.Type} [EquipmentCode]:${data.EquipmentCode} [Name]:${data.Name}`);
|
|
173
|
+
}
|
|
174
|
+
const uuid = this.api.hap.uuid.generate(`Vivint:${data.Id}:${this.serialFor(data)}`);
|
|
175
|
+
managed.push({ data, handlerName: handler.className, uuid });
|
|
176
|
+
}
|
|
177
|
+
// Remove cached accessories that are stale, changed type, or are cameras
|
|
178
|
+
// that must be re-published because camera streaming was toggled off.
|
|
179
|
+
const removed = [];
|
|
180
|
+
for (const [uuid, accessory] of this.cachedAccessories) {
|
|
181
|
+
const match = managed.find(m => m.uuid === uuid);
|
|
182
|
+
const typeChanged = match && match.handlerName !== accessory.context.deviceClassName;
|
|
183
|
+
const cameraDisabled = match && this.config.disableCameras === true
|
|
184
|
+
&& accessory.getService(this.Service.CameraRTPStreamManagement) !== undefined;
|
|
185
|
+
if (!match || typeChanged || cameraDisabled) {
|
|
186
|
+
removed.push(accessory);
|
|
187
|
+
this.cachedAccessories.delete(uuid);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (removed.length > 0) {
|
|
191
|
+
this.log.info(`Removing ${removed.length} accessories`);
|
|
192
|
+
this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, removed);
|
|
193
|
+
}
|
|
194
|
+
// Create and register accessories that are not in the cache.
|
|
195
|
+
const added = [];
|
|
196
|
+
for (const { data, handlerName, uuid } of managed) {
|
|
197
|
+
let accessory = this.cachedAccessories.get(uuid);
|
|
198
|
+
if (!accessory) {
|
|
199
|
+
accessory = this.createAccessory(data, handlerName, uuid);
|
|
200
|
+
added.push(accessory);
|
|
201
|
+
this.cachedAccessories.set(uuid, accessory);
|
|
202
|
+
}
|
|
203
|
+
this.bindAccessory(accessory, data);
|
|
204
|
+
}
|
|
205
|
+
if (added.length > 0) {
|
|
206
|
+
this.log.info(`Adding ${added.length} accessories`);
|
|
207
|
+
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, added);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
createAccessory(data, handlerName, uuid) {
|
|
211
|
+
const handler = findHandlerByClassName(handlerName);
|
|
212
|
+
// Categories is a const enum in hap-nodejs; go through a structural cast
|
|
213
|
+
// to use its runtime object as a value.
|
|
214
|
+
const hapCategories = this.api.hap.Categories;
|
|
215
|
+
const category = handler.inferCategory(data, hapCategories);
|
|
216
|
+
const deviceName = sanitizeDeviceName(data.Name, data.Id);
|
|
217
|
+
const serial = this.serialFor(data);
|
|
218
|
+
const accessory = new this.api.platformAccessory(deviceName, uuid, category);
|
|
219
|
+
accessory.context.name = deviceName;
|
|
220
|
+
accessory.context.id = data.Id;
|
|
221
|
+
accessory.context.deviceClassName = handlerName;
|
|
222
|
+
accessory.context.serial = serial;
|
|
223
|
+
let manufacturer = 'Vivint';
|
|
224
|
+
let model = data.EquipmentCode !== undefined
|
|
225
|
+
? this.vivintApi.getDictionaryKeyByValue(VivintDict.EquipmentCode, data.EquipmentCode) ?? handlerName
|
|
226
|
+
: handlerName;
|
|
227
|
+
// Devices bridged from other ecosystems report their origin in ActualType.
|
|
228
|
+
if (data.ActualType) {
|
|
229
|
+
const parts = data.ActualType.split('_');
|
|
230
|
+
if (parts.length > 0) {
|
|
231
|
+
manufacturer = parts[0].toUpperCase();
|
|
232
|
+
}
|
|
233
|
+
if (parts.length > 1) {
|
|
234
|
+
model = parts[1].toUpperCase();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
const informationService = accessory.getService(this.Service.AccessoryInformation);
|
|
238
|
+
informationService
|
|
239
|
+
.setCharacteristic(this.Characteristic.Manufacturer, manufacturer)
|
|
240
|
+
.setCharacteristic(this.Characteristic.Model, model)
|
|
241
|
+
.setCharacteristic(this.Characteristic.SerialNumber, serial);
|
|
242
|
+
const firmware = data.CurrentSoftwareVersion || data.SoftwareVersion;
|
|
243
|
+
if (firmware) {
|
|
244
|
+
informationService.setCharacteristic(this.Characteristic.FirmwareRevision, firmware);
|
|
245
|
+
}
|
|
246
|
+
return accessory;
|
|
247
|
+
}
|
|
248
|
+
bindAccessory(accessory, data) {
|
|
249
|
+
const handler = findHandlerByClassName(accessory.context.deviceClassName);
|
|
250
|
+
if (!handler) {
|
|
251
|
+
this.log.warn(`Unknown device class '${accessory.context.deviceClassName}' for accessory '${accessory.displayName}', skipping.`);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
try {
|
|
255
|
+
const device = new handler(this, accessory, data);
|
|
256
|
+
this.devices.set(device.id, device);
|
|
257
|
+
if (accessory.context.deviceClassName === 'Panel') {
|
|
258
|
+
this.panelDeviceId = device.id;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
this.log.error(`Failed to set up accessory '${accessory.displayName}':`, error);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// ---------------------------------------------------------------------------
|
|
266
|
+
// State updates
|
|
267
|
+
// ---------------------------------------------------------------------------
|
|
268
|
+
handleSnapshot() {
|
|
269
|
+
const snapshot = this.vivintApi.deviceSnapshot();
|
|
270
|
+
this.lastSnapshotTime = this.vivintApi.deviceSnapshotTs();
|
|
271
|
+
this.log.debug(`Handling device snapshot for timestamp ${this.lastSnapshotTime}`);
|
|
272
|
+
// The partition-level security status lives on the panel device in HomeKit.
|
|
273
|
+
const panelData = snapshot.Devices.find(device => device.Id === this.panelDeviceId);
|
|
274
|
+
if (panelData) {
|
|
275
|
+
panelData.Status = snapshot.Status;
|
|
276
|
+
}
|
|
277
|
+
for (const [deviceId, device] of this.devices) {
|
|
278
|
+
const data = snapshot.Devices.find(item => item.Id === deviceId);
|
|
279
|
+
if (data) {
|
|
280
|
+
device.handleSnapshot(data);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
handlePubNubMessage(rawMessage) {
|
|
285
|
+
const message = this.vivintApi.parsePubNub(rawMessage);
|
|
286
|
+
this.log.debug('Parsed PubNub message:', JSON.stringify(message));
|
|
287
|
+
if (!message.Data) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
// Messages from Nest and MyQ devices do not carry PlatformContext info.
|
|
291
|
+
const messageTimestamp = message.Data.PlatformContext?.Timestamp;
|
|
292
|
+
if (messageTimestamp !== undefined && messageTimestamp < this.lastSnapshotTime) {
|
|
293
|
+
this.log.debug('Ignoring stale realtime update', messageTimestamp, '<', this.lastSnapshotTime);
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
if (message.Id === this.vivintApi.panelId && message.Data.Status !== undefined) {
|
|
297
|
+
// Partition-level arm state change; route it to the panel device.
|
|
298
|
+
message.Data.Devices = [{
|
|
299
|
+
Id: this.panelDeviceId,
|
|
300
|
+
Status: message.Data.Status,
|
|
301
|
+
}];
|
|
302
|
+
}
|
|
303
|
+
else if (message.Type === VivintDict.ObjectType.InboxMessage
|
|
304
|
+
&& typeof message.Data.Subject === 'string'
|
|
305
|
+
&& message.Data.Subject.includes('failed to lock')) {
|
|
306
|
+
// "Alert: <name> failed to lock" inbox messages indicate a jammed lock.
|
|
307
|
+
const lockName = message.Data.Subject.split('Alert: ')[1]?.split(' failed to lock')[0];
|
|
308
|
+
const lockDevice = [...this.devices.values()].find(device => device.data.Type === VivintDict.PanelDeviceType.DoorLock && device.name === lockName);
|
|
309
|
+
if (lockDevice) {
|
|
310
|
+
message.Data.Devices = [{
|
|
311
|
+
Id: lockDevice.id,
|
|
312
|
+
Status: this.Characteristic.LockCurrentState.JAMMED,
|
|
313
|
+
}];
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
for (const patch of message.Data.Devices ?? []) {
|
|
317
|
+
this.devices.get(patch.Id)?.handlePatch(patch);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
// ---------------------------------------------------------------------------
|
|
321
|
+
// Periodic refresh
|
|
322
|
+
// ---------------------------------------------------------------------------
|
|
323
|
+
startTimers() {
|
|
324
|
+
this.stopTimers();
|
|
325
|
+
const refreshSecs = this.apiLoginRefreshSecs;
|
|
326
|
+
// Session renewal keeps the auth cookie fresh; failures are retried on
|
|
327
|
+
// the next cycle rather than in a tight loop.
|
|
328
|
+
this.sessionRefreshTimer = setInterval(() => {
|
|
329
|
+
this.vivintApi.renewSession()
|
|
330
|
+
.then(() => this.vivintApi.renewPanelLogin())
|
|
331
|
+
.catch(error => {
|
|
332
|
+
if (error instanceof VivintAuthError) {
|
|
333
|
+
this.log.error(error.message);
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
this.log.warn('Could not renew the Vivint session (will retry on the next cycle):', error instanceof Error ? error.message : error);
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
}, refreshSecs * 1000);
|
|
340
|
+
this.sessionRefreshTimer.unref();
|
|
341
|
+
// Periodic snapshot polling keeps the realtime notification stream active
|
|
342
|
+
// and reconverges state after missed events.
|
|
343
|
+
this.pollTimer = setInterval(() => this.pollSystemInfo(), (refreshSecs / 20) * 1000);
|
|
344
|
+
this.pollTimer.unref();
|
|
345
|
+
}
|
|
346
|
+
stopTimers() {
|
|
347
|
+
for (const timer of [this.sessionRefreshTimer, this.pollTimer, this.retryTimer]) {
|
|
348
|
+
if (timer) {
|
|
349
|
+
clearTimeout(timer);
|
|
350
|
+
clearInterval(timer);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
this.sessionRefreshTimer = this.pollTimer = this.retryTimer = undefined;
|
|
354
|
+
}
|
|
355
|
+
async pollSystemInfo() {
|
|
356
|
+
if (this.pollInFlight) {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
// After repeated failures, skip an exponentially growing number of ticks
|
|
360
|
+
// so an outage does not turn into a stream of doomed API requests.
|
|
361
|
+
if (this.pollTicksToSkip > 0) {
|
|
362
|
+
this.pollTicksToSkip--;
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
this.pollInFlight = true;
|
|
366
|
+
try {
|
|
367
|
+
await this.vivintApi.renewSystemInfo();
|
|
368
|
+
this.handleSnapshot();
|
|
369
|
+
if (this.pollConsecutiveFailures > 0) {
|
|
370
|
+
this.log.info('Vivint API polling recovered.');
|
|
371
|
+
}
|
|
372
|
+
this.pollConsecutiveFailures = 0;
|
|
373
|
+
}
|
|
374
|
+
catch (error) {
|
|
375
|
+
this.pollConsecutiveFailures++;
|
|
376
|
+
this.pollTicksToSkip = Math.min(2 ** this.pollConsecutiveFailures, 32) - 1;
|
|
377
|
+
if (error instanceof VivintRateLimitError) {
|
|
378
|
+
this.pollTicksToSkip = Math.max(this.pollTicksToSkip, 16);
|
|
379
|
+
}
|
|
380
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
381
|
+
if (this.pollConsecutiveFailures === 1) {
|
|
382
|
+
this.log.warn(`Could not refresh device states (${message}). Backing off; realtime updates may still arrive.`);
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
this.log.debug(`Device state refresh still failing (attempt ${this.pollConsecutiveFailures}): ${message}`);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
finally {
|
|
389
|
+
this.pollInFlight = false;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
//# sourceMappingURL=platform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
|
package/dist/settings.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The platform name users put in config.json ("platform": "Vivint").
|
|
3
|
+
*/
|
|
4
|
+
export const PLATFORM_NAME = 'Vivint';
|
|
5
|
+
/**
|
|
6
|
+
* Plugin identifier used when registering accessories. Deliberately kept as the
|
|
7
|
+
* unscoped name (not the scoped package.json name) to match the identifier used
|
|
8
|
+
* by @balansse/homebridge-vivint, so cached accessories carry over between the
|
|
9
|
+
* two; Homebridge resolves the mismatch via its plugin-rename fallback.
|
|
10
|
+
*/
|
|
11
|
+
export const PLUGIN_NAME = 'homebridge-vivint';
|
|
12
|
+
//# sourceMappingURL=settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,QAAQ,CAAC;AAEtC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,mBAAmB,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debounces an async function on the leading edge: the first call runs
|
|
3
|
+
* immediately, subsequent calls during the wait window are coalesced and
|
|
4
|
+
* resolve with the result of a single trailing invocation using the most
|
|
5
|
+
* recent arguments.
|
|
6
|
+
*/
|
|
7
|
+
export function debounceLeading(fn, waitMs) {
|
|
8
|
+
let timer;
|
|
9
|
+
let lastRun = 0;
|
|
10
|
+
let pending;
|
|
11
|
+
const flush = () => {
|
|
12
|
+
const batch = pending;
|
|
13
|
+
pending = undefined;
|
|
14
|
+
timer = undefined;
|
|
15
|
+
if (!batch) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
lastRun = Date.now();
|
|
19
|
+
fn(...batch.args).then(result => batch.resolvers.forEach(r => r.resolve(result)), error => batch.resolvers.forEach(r => r.reject(error)));
|
|
20
|
+
};
|
|
21
|
+
return (...args) => {
|
|
22
|
+
const now = Date.now();
|
|
23
|
+
if (!timer && now - lastRun >= waitMs) {
|
|
24
|
+
// Leading edge: run immediately, then open a quiet window.
|
|
25
|
+
lastRun = now;
|
|
26
|
+
timer = setTimeout(() => {
|
|
27
|
+
timer = undefined;
|
|
28
|
+
if (pending) {
|
|
29
|
+
flush();
|
|
30
|
+
}
|
|
31
|
+
}, waitMs);
|
|
32
|
+
return fn(...args);
|
|
33
|
+
}
|
|
34
|
+
// Within the window: coalesce into one trailing call with latest args.
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
if (pending) {
|
|
37
|
+
pending.args = args;
|
|
38
|
+
pending.resolvers.push({ resolve, reject });
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
pending = { args, resolvers: [{ resolve, reject }] };
|
|
42
|
+
}
|
|
43
|
+
if (!timer) {
|
|
44
|
+
timer = setTimeout(flush, waitMs);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=debounce.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debounce.js","sourceRoot":"","sources":["../../src/utils/debounce.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAC7B,EAAiC,EACjC,MAAc;IAEd,IAAI,KAAiC,CAAC;IACtC,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,OAAgH,CAAC;IAErH,MAAM,KAAK,GAAG,GAAG,EAAE;QACjB,MAAM,KAAK,GAAG,OAAO,CAAC;QACtB,OAAO,GAAG,SAAS,CAAC;QACpB,KAAK,GAAG,SAAS,CAAC;QAClB,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO;QACT,CAAC;QACD,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACrB,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CACpB,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EACzD,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CACvD,CAAC;IACJ,CAAC,CAAC;IAEF,OAAO,CAAC,GAAG,IAAU,EAAc,EAAE;QACnC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,CAAC,KAAK,IAAI,GAAG,GAAG,OAAO,IAAI,MAAM,EAAE,CAAC;YACtC,2DAA2D;YAC3D,OAAO,GAAG,GAAG,CAAC;YACd,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBACtB,KAAK,GAAG,SAAS,CAAC;gBAClB,IAAI,OAAO,EAAE,CAAC;oBACZ,KAAK,EAAE,CAAC;gBACV,CAAC;YACH,CAAC,EAAE,MAAM,CAAC,CAAC;YACX,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;QACrB,CAAC;QACD,uEAAuE;QACvE,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACxC,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;gBACpB,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;YAC9C,CAAC;iBAAM,CAAC;gBACN,OAAO,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;YACvD,CAAC;YACD,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,KAAK,GAAG,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YACpC,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createServer } from 'node:net';
|
|
2
|
+
/**
|
|
3
|
+
* Returns a free UDP/TCP port assigned by the OS, or verifies a specific port is free.
|
|
4
|
+
*/
|
|
5
|
+
function getFreePort(port) {
|
|
6
|
+
return new Promise(resolve => {
|
|
7
|
+
const server = createServer();
|
|
8
|
+
server.unref();
|
|
9
|
+
server.on('error', () => resolve(undefined));
|
|
10
|
+
server.listen(port ?? 0, () => {
|
|
11
|
+
const address = server.address();
|
|
12
|
+
const found = typeof address === 'object' && address ? address.port : undefined;
|
|
13
|
+
server.close(() => resolve(found));
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Reserves `count` consecutive free ports. ffmpeg uses the next port up for
|
|
19
|
+
* RTCP by default, so consecutive availability matters.
|
|
20
|
+
*/
|
|
21
|
+
export async function reservePorts(count = 1, attemptsLeft = 10) {
|
|
22
|
+
if (attemptsLeft <= 0) {
|
|
23
|
+
throw new Error('Unable to reserve a block of consecutive free ports');
|
|
24
|
+
}
|
|
25
|
+
const first = await getFreePort();
|
|
26
|
+
if (first === undefined) {
|
|
27
|
+
return reservePorts(count, attemptsLeft - 1);
|
|
28
|
+
}
|
|
29
|
+
const ports = [first];
|
|
30
|
+
for (let i = 1; i < count; i++) {
|
|
31
|
+
const next = await getFreePort(first + i);
|
|
32
|
+
if (next !== first + i) {
|
|
33
|
+
return reservePorts(count, attemptsLeft - 1);
|
|
34
|
+
}
|
|
35
|
+
ports.push(next);
|
|
36
|
+
}
|
|
37
|
+
return ports;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=ports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ports.js","sourceRoot":"","sources":["../../src/utils/ports.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAExC;;GAEG;AACH,SAAS,WAAW,CAAC,IAAa;IAChC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE;QAC3B,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;QAC9B,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;QAC7C,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,EAAE,GAAG,EAAE;YAC5B,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;YACjC,MAAM,KAAK,GAAG,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;YAChF,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAK,GAAG,CAAC,EAAE,YAAY,GAAG,EAAE;IAC7D,IAAI,YAAY,IAAI,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACzE,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,WAAW,EAAE,CAAC;IAClC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,CAAC,CAAC,CAAC;IAC/C,CAAC;IACD,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC;IACtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QAC1C,IAAI,IAAI,KAAK,KAAK,GAAG,CAAC,EAAE,CAAC;YACvB,OAAO,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,CAAC,CAAC,CAAC;QAC/C,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers to keep secrets (session cookies, RTSP credentials) out of the logs.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Replaces embedded credentials in URLs (e.g. rtsp://user:pass@host) with placeholders.
|
|
6
|
+
*/
|
|
7
|
+
export function redactUrlCredentials(text) {
|
|
8
|
+
return text.replace(/([a-z]+:\/\/)[^/\s:@]+:[^/\s@]*@/gi, '$1****:****@');
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Produces a short, non-reversible descriptor of a token for debug logging.
|
|
12
|
+
*/
|
|
13
|
+
export function describeToken(token) {
|
|
14
|
+
if (!token) {
|
|
15
|
+
return '(none)';
|
|
16
|
+
}
|
|
17
|
+
return `(token, ${token.length} chars)`;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=redact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.js","sourceRoot":"","sources":["../../src/utils/redact.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,OAAO,IAAI,CAAC,OAAO,CAAC,oCAAoC,EAAE,cAAc,CAAC,CAAC;AAC5E,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,KAAyB;IACrD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,OAAO,WAAW,KAAK,CAAC,MAAM,SAAS,CAAC;AAC1C,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sanitizes a device name by removing characters HomeKit does not accept,
|
|
3
|
+
* trimming whitespace and ensuring it starts and ends with an alphanumeric
|
|
4
|
+
* character. Falls back to a generic name containing the device ID.
|
|
5
|
+
*/
|
|
6
|
+
export function sanitizeDeviceName(name, deviceId) {
|
|
7
|
+
if (!name) {
|
|
8
|
+
return `Unnamed device ID ${deviceId}`;
|
|
9
|
+
}
|
|
10
|
+
let cleanedName = name.replace(/[^a-zA-Z0-9\s']/g, '');
|
|
11
|
+
cleanedName = cleanedName.trim().replace(/\s+/g, ' ');
|
|
12
|
+
if (cleanedName.length > 0) {
|
|
13
|
+
if (!/^[a-zA-Z0-9\s]/.test(cleanedName[0])) {
|
|
14
|
+
cleanedName = cleanedName.substring(1);
|
|
15
|
+
}
|
|
16
|
+
if (!/[a-zA-Z0-9]$/.test(cleanedName[cleanedName.length - 1])) {
|
|
17
|
+
cleanedName = cleanedName.substring(0, cleanedName.length - 1);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return cleanedName || `Unnamed device ID ${deviceId}`;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=sanitizeName.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitizeName.js","sourceRoot":"","sources":["../../src/utils/sanitizeName.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAwB,EAAE,QAAyB;IACpF,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,qBAAqB,QAAQ,EAAE,CAAC;IACzC,CAAC;IACD,IAAI,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;IACvD,WAAW,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACtD,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3C,WAAW,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QACzC,CAAC;QACD,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9D,WAAW,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC,EAAE,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;IACD,OAAO,WAAW,IAAI,qBAAqB,QAAQ,EAAE,CAAC;AACxD,CAAC"}
|