@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.
Files changed (62) hide show
  1. package/LICENSE +13 -0
  2. package/README.md +107 -0
  3. package/config.schema.json +90 -0
  4. package/dist/devices/camera.js +368 -0
  5. package/dist/devices/camera.js.map +1 -0
  6. package/dist/devices/carbonMonoxideSensor.js +44 -0
  7. package/dist/devices/carbonMonoxideSensor.js.map +1 -0
  8. package/dist/devices/contactSensor.js +65 -0
  9. package/dist/devices/contactSensor.js.map +1 -0
  10. package/dist/devices/device.js +147 -0
  11. package/dist/devices/device.js.map +1 -0
  12. package/dist/devices/dimmerSwitch.js +12 -0
  13. package/dist/devices/dimmerSwitch.js.map +1 -0
  14. package/dist/devices/garageDoor.js +58 -0
  15. package/dist/devices/garageDoor.js.map +1 -0
  16. package/dist/devices/index.js +55 -0
  17. package/dist/devices/index.js.map +1 -0
  18. package/dist/devices/lightGroup.js +15 -0
  19. package/dist/devices/lightGroup.js.map +1 -0
  20. package/dist/devices/lightSwitch.js +42 -0
  21. package/dist/devices/lightSwitch.js.map +1 -0
  22. package/dist/devices/lock.js +49 -0
  23. package/dist/devices/lock.js.map +1 -0
  24. package/dist/devices/motionSensor.js +73 -0
  25. package/dist/devices/motionSensor.js.map +1 -0
  26. package/dist/devices/multiLevelSwitchBase.js +55 -0
  27. package/dist/devices/multiLevelSwitchBase.js.map +1 -0
  28. package/dist/devices/panel.js +79 -0
  29. package/dist/devices/panel.js.map +1 -0
  30. package/dist/devices/smokeSensor.js +47 -0
  31. package/dist/devices/smokeSensor.js.map +1 -0
  32. package/dist/devices/thermostat.js +216 -0
  33. package/dist/devices/thermostat.js.map +1 -0
  34. package/dist/homebridge-ui/public/index.html +138 -0
  35. package/dist/homebridge-ui/server.js +120 -0
  36. package/dist/homebridge-ui/server.js.map +1 -0
  37. package/dist/index.js +9 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/platform.js +393 -0
  40. package/dist/platform.js.map +1 -0
  41. package/dist/settings.js +12 -0
  42. package/dist/settings.js.map +1 -0
  43. package/dist/types.js +2 -0
  44. package/dist/types.js.map +1 -0
  45. package/dist/utils/debounce.js +49 -0
  46. package/dist/utils/debounce.js.map +1 -0
  47. package/dist/utils/ports.js +39 -0
  48. package/dist/utils/ports.js.map +1 -0
  49. package/dist/utils/redact.js +19 -0
  50. package/dist/utils/redact.js.map +1 -0
  51. package/dist/utils/sanitizeName.js +22 -0
  52. package/dist/utils/sanitizeName.js.map +1 -0
  53. package/dist/vivint/api.js +246 -0
  54. package/dist/vivint/api.js.map +1 -0
  55. package/dist/vivint/datapatch.js +52 -0
  56. package/dist/vivint/datapatch.js.map +1 -0
  57. package/dist/vivint/dictionary.js +66 -0
  58. package/dist/vivint/dictionary.js.map +1 -0
  59. package/dist/vivint/eventStream.js +77 -0
  60. package/dist/vivint/eventStream.js.map +1 -0
  61. package/dist/vivint/vivint_dictionary.json +8720 -0
  62. package/package.json +62 -0
@@ -0,0 +1,246 @@
1
+ import { VivintDict, getKeyByValueDeep, mapObject } from './dictionary.js';
2
+ const VIVINT_API_URL = 'https://www.vivintsky.com/api';
3
+ const REQUEST_TIMEOUT_MS = 20_000;
4
+ /**
5
+ * Generic error raised for failed Vivint API calls.
6
+ */
7
+ export class VivintApiError extends Error {
8
+ statusCode;
9
+ constructor(message, statusCode, options) {
10
+ super(message, options);
11
+ this.statusCode = statusCode;
12
+ this.name = 'VivintApiError';
13
+ }
14
+ }
15
+ /**
16
+ * The session token is missing, expired or revoked. The user must sign in
17
+ * again through the plugin settings UI.
18
+ */
19
+ export class VivintAuthError extends VivintApiError {
20
+ constructor(message, statusCode) {
21
+ super(message, statusCode);
22
+ this.name = 'VivintAuthError';
23
+ }
24
+ }
25
+ /**
26
+ * Vivint (or its Cloudflare front end) is throttling or blocking us. Callers
27
+ * must back off aggressively to avoid a long-term block.
28
+ */
29
+ export class VivintRateLimitError extends VivintApiError {
30
+ constructor(message, statusCode) {
31
+ super(message, statusCode);
32
+ this.name = 'VivintRateLimitError';
33
+ }
34
+ }
35
+ /**
36
+ * Client for the Vivint Sky API.
37
+ *
38
+ * Authentication uses a long-lived session cookie ("refresh token") obtained
39
+ * through the MFA sign-in flow in the plugin settings UI. The token is kept
40
+ * private and never written to the logs.
41
+ */
42
+ export class VivintApiClient {
43
+ log;
44
+ refreshToken;
45
+ sessionInfo;
46
+ systemInfo;
47
+ renewSessionInFlight;
48
+ panelId = 0;
49
+ partitionId = 0;
50
+ panelLogin;
51
+ constructor(refreshToken, log) {
52
+ this.log = log;
53
+ this.refreshToken = refreshToken.trim();
54
+ }
55
+ /**
56
+ * Authenticates and loads the initial system state. Must be called (and
57
+ * must succeed) before any other method is used.
58
+ */
59
+ async connect() {
60
+ await this.authenticate();
61
+ const system = this.sessionInfo.Users?.System;
62
+ if (!Array.isArray(system) || system.length === 0 || !system[0].PanelId) {
63
+ throw new VivintApiError('Vivint account has no panel associated with it');
64
+ }
65
+ this.panelId = system[0].PanelId;
66
+ const [systemInfo, panelLogin] = await Promise.all([
67
+ this.fetchSystemInfo(),
68
+ this.fetchPanelLogin(),
69
+ ]);
70
+ this.applySystemInfo(systemInfo);
71
+ this.panelLogin = panelLogin;
72
+ }
73
+ /**
74
+ * The PubNub channel suffix for this account's realtime event stream.
75
+ */
76
+ get messageBroadcastChannel() {
77
+ return this.sessionInfo.Users.MessageBroadcastChannel;
78
+ }
79
+ deviceSnapshot() {
80
+ return this.systemInfo.System.Partitions[0];
81
+ }
82
+ deviceSnapshotTs() {
83
+ return this.systemInfo.System.PanelContext.Timestamp;
84
+ }
85
+ parsePubNub(message) {
86
+ return mapObject(message);
87
+ }
88
+ getDictionaryKeyByValue(dictionary, value) {
89
+ return getKeyByValueDeep(dictionary, value);
90
+ }
91
+ /**
92
+ * Renews the session cookie. Concurrent calls are coalesced into a single
93
+ * request so a burst of 401s cannot stampede the auth endpoint.
94
+ */
95
+ renewSession() {
96
+ if (!this.renewSessionInFlight) {
97
+ this.renewSessionInFlight = this.authenticate().finally(() => {
98
+ this.renewSessionInFlight = undefined;
99
+ });
100
+ }
101
+ return this.renewSessionInFlight;
102
+ }
103
+ /**
104
+ * Refreshes the cached system snapshot. This periodic call also keeps the
105
+ * realtime notification stream active on Vivint's side.
106
+ */
107
+ async renewSystemInfo() {
108
+ this.applySystemInfo(await this.fetchSystemInfo());
109
+ }
110
+ /**
111
+ * Refreshes the panel's local RTSP credentials used for camera streaming.
112
+ */
113
+ async renewPanelLogin() {
114
+ this.panelLogin = await this.fetchPanelLogin();
115
+ }
116
+ async setGarageDoorState(garageDoorId, newState) {
117
+ await this.putDevice('door', garageDoorId, { [VivintDict.Fields.Id]: garageDoorId, [VivintDict.Fields.Status]: newState });
118
+ }
119
+ async setLockState(lockId, locked) {
120
+ await this.putDevice('locks', lockId, { [VivintDict.Fields.Id]: lockId, [VivintDict.Fields.Status]: locked });
121
+ }
122
+ async setPanelState(newState) {
123
+ await this.putDevice('armedstates', null, { armState: newState, forceArm: false });
124
+ }
125
+ async setThermostatFanState(thermostatId, newState) {
126
+ await this.putDevice('thermostats', thermostatId, { [VivintDict.Fields.FanMode]: newState });
127
+ }
128
+ async setThermostatState(thermostatId, newState) {
129
+ await this.putDevice('thermostats', thermostatId, { [VivintDict.Fields.OperatingMode]: newState });
130
+ }
131
+ async setThermostatHeatSetPoint(thermostatId, newTemperature) {
132
+ await this.putDevice('thermostats', thermostatId, { [VivintDict.Fields.HeatSetPoint]: newTemperature });
133
+ }
134
+ async setThermostatCoolSetPoint(thermostatId, newTemperature) {
135
+ await this.putDevice('thermostats', thermostatId, { [VivintDict.Fields.CoolSetPoint]: newTemperature });
136
+ }
137
+ async setSensorBypass(sensorId, bypassState) {
138
+ await this.putDevice('sensors', sensorId, { [VivintDict.Fields.Bypassed]: bypassState });
139
+ }
140
+ /**
141
+ * Sends a device state change. The alarm panel itself has no device id and
142
+ * is addressed at the partition level (pass id = null).
143
+ */
144
+ async putDevice(category, id, data) {
145
+ const path = id != null ? `${category}/${id}` : category;
146
+ await this.request(`/${this.panelId}/${this.partitionId}/${path}`, {
147
+ method: 'PUT',
148
+ body: JSON.stringify(data),
149
+ });
150
+ }
151
+ applySystemInfo(systemInfo) {
152
+ if (!systemInfo?.System?.Partitions?.length) {
153
+ throw new VivintApiError('Vivint API returned a system snapshot without partitions');
154
+ }
155
+ this.systemInfo = systemInfo;
156
+ this.partitionId = systemInfo.System.Partitions[0].PartitionId;
157
+ }
158
+ async fetchSystemInfo() {
159
+ return await this.requestJsonMapped(`/systems/${this.panelId}`);
160
+ }
161
+ async fetchPanelLogin() {
162
+ return await this.requestJsonMapped(`/panel-login/${this.panelId}`);
163
+ }
164
+ /**
165
+ * Authenticates with the current session cookie. Vivint may rotate the
166
+ * cookie in the response; when it does, the new value replaces the old one.
167
+ */
168
+ async authenticate() {
169
+ if (!this.refreshToken) {
170
+ throw new VivintAuthError('No refresh token configured. Sign in through the plugin settings UI to obtain one.');
171
+ }
172
+ const response = await this.rawRequest('/authuser', { method: 'GET' });
173
+ if (response.status === 401 || response.status === 403) {
174
+ throw new VivintAuthError('Vivint rejected the saved session token. Open the plugin settings UI and sign in again to obtain a new token.', response.status);
175
+ }
176
+ if (!response.ok) {
177
+ throw await this.errorFromResponse(response, 'authentication');
178
+ }
179
+ const rotated = this.extractSessionCookie(response);
180
+ if (rotated && rotated !== this.refreshToken) {
181
+ this.refreshToken = rotated;
182
+ this.log.debug('Vivint issued a rotated session token.');
183
+ }
184
+ this.sessionInfo = mapObject(await response.json());
185
+ }
186
+ extractSessionCookie(response) {
187
+ const setCookies = response.headers.getSetCookie?.() ?? [];
188
+ const sessionCookie = setCookies.find(cookie => cookie.startsWith('s='));
189
+ return sessionCookie?.split(';')[0];
190
+ }
191
+ /**
192
+ * Performs an authenticated request, transparently renewing the session
193
+ * once if it has expired.
194
+ */
195
+ async request(path, init = {}, isRetry = false) {
196
+ const response = await this.rawRequest(path, init);
197
+ if (response.status === 401 && !isRetry) {
198
+ this.log.debug('Vivint session expired, renewing and retrying request once.');
199
+ await this.renewSession();
200
+ return this.request(path, init, true);
201
+ }
202
+ if (!response.ok) {
203
+ throw await this.errorFromResponse(response, `${init.method ?? 'GET'} ${path}`);
204
+ }
205
+ return response;
206
+ }
207
+ async requestJsonMapped(path) {
208
+ const response = await this.request(path);
209
+ return mapObject(await response.json());
210
+ }
211
+ async rawRequest(path, init = {}) {
212
+ const url = `${VIVINT_API_URL}${path}`;
213
+ try {
214
+ return await fetch(url, {
215
+ method: init.method ?? 'GET',
216
+ body: init.body,
217
+ headers: {
218
+ 'Cookie': this.refreshToken,
219
+ 'Cache-Control': 'no-store',
220
+ ...(init.body ? { 'Content-Type': 'application/json' } : {}),
221
+ },
222
+ redirect: 'follow',
223
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
224
+ });
225
+ }
226
+ catch (error) {
227
+ // Network-level failure (offline, DNS, timeout). No response was
228
+ // received, so this is safe to retry with backoff.
229
+ const reason = error instanceof Error ? error.message : String(error);
230
+ throw new VivintApiError(`Could not reach the Vivint API (${reason}). Check the network connection.`, undefined, { cause: error });
231
+ }
232
+ }
233
+ async errorFromResponse(response, what) {
234
+ // Drain the body so the socket can be reused; content is not logged
235
+ // because Cloudflare block pages are noisy and API errors may echo data.
236
+ await response.text().catch(() => '');
237
+ if (response.status === 429 || response.status === 403 || response.status === 503) {
238
+ return new VivintRateLimitError(`Vivint API is throttling or blocking requests (HTTP ${response.status}). Backing off to avoid a long-term block.`, response.status);
239
+ }
240
+ if (response.status === 401) {
241
+ return new VivintAuthError('Vivint session is no longer valid. Sign in again through the plugin settings UI.', response.status);
242
+ }
243
+ return new VivintApiError(`Vivint API request failed (${what}): HTTP ${response.status}`, response.status);
244
+ }
245
+ }
246
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/vivint/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Applies incremental PubNub patches to cached device data, mutating in place.
3
+ *
4
+ * Patch keys may use dotted paths: applying {"a.b": 1} to {"a": {"b": 2, "c": 3}}
5
+ * results in {"a": {"b": 1, "c": 3}}. Array patches are applied element-wise.
6
+ */
7
+ function isPlainObject(value) {
8
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
9
+ }
10
+ function dataPatchArr(data, patch) {
11
+ for (let idx = 0; idx < patch.length; idx++) {
12
+ const patchValue = patch[idx];
13
+ if (isPlainObject(patchValue)) {
14
+ const target = data[idx];
15
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define -- mutual recursion with dataPatch
16
+ if (!isPlainObject(target) || !dataPatch(target, patchValue)) {
17
+ return false;
18
+ }
19
+ }
20
+ else {
21
+ data[idx] = patchValue;
22
+ }
23
+ }
24
+ return true;
25
+ }
26
+ export function dataPatch(data, patch) {
27
+ if (!isPlainObject(data)) {
28
+ return false;
29
+ }
30
+ for (const key of Object.keys(patch)) {
31
+ const selector = key.split('.');
32
+ if (selector.length > 1) {
33
+ const head = selector.shift();
34
+ const restPatch = { [selector.join('.')]: patch[key] };
35
+ const target = data[head];
36
+ if (!isPlainObject(target) || !dataPatch(target, restPatch)) {
37
+ return false;
38
+ }
39
+ }
40
+ else if (Array.isArray(patch[key])) {
41
+ const target = data[key];
42
+ if (!Array.isArray(target) || !dataPatchArr(target, patch[key])) {
43
+ return false;
44
+ }
45
+ }
46
+ else {
47
+ data[key] = patch[key];
48
+ }
49
+ }
50
+ return true;
51
+ }
52
+ //# sourceMappingURL=datapatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"datapatch.js","sourceRoot":"","sources":["../../src/vivint/datapatch.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,YAAY,CAAC,IAAe,EAAE,KAAgB;IACrD,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC;QAC5C,MAAM,UAAU,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,aAAa,CAAC,UAAU,CAAC,EAAE,CAAC;YAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;YACzB,sGAAsG;YACtG,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,CAAC;gBAC7D,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC;QACzB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAA6B,EAAE,KAA8B;IACrF,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,EAAY,CAAC;YACxC,MAAM,SAAS,GAA4B,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YAChF,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1B,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,CAAC;gBAC5D,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;aAAM,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACrC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;YACzB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,CAAc,CAAC,EAAE,CAAC;gBAC7E,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,66 @@
1
+ import { readFileSync } from 'node:fs';
2
+ const dictUrl = new URL('./vivint_dictionary.json', import.meta.url);
3
+ export const VivintDict = JSON.parse(readFileSync(dictUrl, 'utf8'));
4
+ function isPlainObject(value) {
5
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
6
+ }
7
+ /**
8
+ * Finds the (possibly nested) dictionary key whose value equals `value`.
9
+ * Nested keys are joined with underscores, except that the top-level "Fields"
10
+ * prefix is omitted (matching the original plugin's behavior).
11
+ */
12
+ export function getKeyByValueDeep(object, value) {
13
+ const key = Object.keys(object).find(k => object[k] === value);
14
+ if (key !== undefined) {
15
+ return key;
16
+ }
17
+ for (const property of Object.keys(object)) {
18
+ if (isPlainObject(object[property])) {
19
+ const result = getKeyByValueDeep(object[property], value);
20
+ if (result !== undefined) {
21
+ return (property !== 'Fields' ? property + '_' : '') + result;
22
+ }
23
+ }
24
+ }
25
+ return undefined;
26
+ }
27
+ // Reverse lookups over the large dictionary are expensive, and the same wire
28
+ // field names appear in every message, so cache the results per dictionary.
29
+ const reverseLookupCaches = new WeakMap();
30
+ function mapFieldName(property, dict) {
31
+ let cache = reverseLookupCaches.get(dict);
32
+ if (!cache) {
33
+ cache = new Map();
34
+ reverseLookupCaches.set(dict, cache);
35
+ }
36
+ const cached = cache.get(property);
37
+ if (cached !== undefined) {
38
+ return cached;
39
+ }
40
+ const mapped = getKeyByValueDeep(dict.Fields, property) || getKeyByValueDeep(dict, property) || property;
41
+ cache.set(property, mapped);
42
+ return mapped;
43
+ }
44
+ /**
45
+ * Recursively maps wire-format field names in an API object to their friendly
46
+ * dictionary names. Unknown fields are kept as-is.
47
+ */
48
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
49
+ export function mapObject(object, dict = VivintDict) {
50
+ const mappedObject = {};
51
+ for (const property of Object.keys(object)) {
52
+ const value = object[property];
53
+ const mappedProperty = mapFieldName(property, dict);
54
+ if (Array.isArray(value)) {
55
+ mappedObject[mappedProperty] = value.map(item => isPlainObject(item) || Array.isArray(item) ? mapObject(item, dict) : item);
56
+ }
57
+ else if (isPlainObject(value)) {
58
+ mappedObject[mappedProperty] = mapObject(value, dict);
59
+ }
60
+ else {
61
+ mappedObject[mappedProperty] = value;
62
+ }
63
+ }
64
+ return mappedObject;
65
+ }
66
+ //# sourceMappingURL=dictionary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dictionary.js","sourceRoot":"","sources":["../../src/vivint/dictionary.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ import PubNub from 'pubnub';
2
+ const PUBNUB_SUBSCRIBE_KEY = 'sub-c-6fb03d68-6a78-11e2-ae8f-12313f022c90';
3
+ /**
4
+ * Realtime device event stream. Vivint pushes device updates over PubNub;
5
+ * the SDK handles reconnection with its own backoff, we just surface status
6
+ * changes in the log without spamming.
7
+ */
8
+ export class VivintEventStream {
9
+ log;
10
+ pubnub;
11
+ connectionLost = false;
12
+ constructor(log) {
13
+ this.log = log;
14
+ }
15
+ start(messageBroadcastChannel, onMessage) {
16
+ this.stop();
17
+ const pubnub = new PubNub({
18
+ subscribeKey: PUBNUB_SUBSCRIBE_KEY,
19
+ userId: messageBroadcastChannel,
20
+ });
21
+ pubnub.addListener({
22
+ status: statusEvent => {
23
+ switch (statusEvent.category) {
24
+ case 'PNConnectedCategory':
25
+ this.connectionLost = false;
26
+ this.log.debug('Connected to the Vivint realtime event stream.');
27
+ break;
28
+ case 'PNReconnectedCategory':
29
+ this.connectionLost = false;
30
+ this.log.info('Reconnected to the Vivint realtime event stream.');
31
+ break;
32
+ case 'PNNetworkDownCategory':
33
+ case 'PNNetworkIssuesCategory':
34
+ case 'PNTimeoutCategory':
35
+ case 'PNDisconnectedUnexpectedlyCategory':
36
+ // Log the first drop at warn, subsequent ones at debug to avoid
37
+ // flooding the log during a longer outage.
38
+ if (!this.connectionLost) {
39
+ this.connectionLost = true;
40
+ this.log.warn('Lost connection to the Vivint realtime event stream, reconnecting automatically. '
41
+ + 'Device updates rely on periodic polling until the connection is restored.');
42
+ }
43
+ else {
44
+ this.log.debug('Vivint realtime event stream still down (%s).', statusEvent.category);
45
+ }
46
+ break;
47
+ default:
48
+ this.log.debug('Vivint realtime event stream status:', statusEvent.category);
49
+ }
50
+ },
51
+ message: event => {
52
+ try {
53
+ onMessage(event.message);
54
+ }
55
+ catch (error) {
56
+ // A malformed message must never take down the plugin.
57
+ this.log.error('Failed to process a Vivint realtime message:', error);
58
+ }
59
+ },
60
+ });
61
+ pubnub.subscribe({ channels: [`PlatformChannel#${messageBroadcastChannel}`] });
62
+ this.pubnub = pubnub;
63
+ }
64
+ stop() {
65
+ if (this.pubnub) {
66
+ try {
67
+ this.pubnub.unsubscribeAll();
68
+ this.pubnub.stop();
69
+ }
70
+ catch (error) {
71
+ this.log.debug('Error while shutting down the event stream:', error);
72
+ }
73
+ this.pubnub = undefined;
74
+ }
75
+ }
76
+ }
77
+ //# sourceMappingURL=eventStream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eventStream.js","sourceRoot":"","sources":["../../src/vivint/eventStream.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,MAAM,oBAAoB,GAAG,4CAA4C,CAAC;AAE1E;;;;GAIG;AACH,MAAM,OAAO,iBAAiB;IAIC;IAHrB,MAAM,CAAU;IAChB,cAAc,GAAG,KAAK,CAAC;IAE/B,YAA6B,GAAY;QAAZ,QAAG,GAAH,GAAG,CAAS;IAAG,CAAC;IAE7C,KAAK,CAAC,uBAA+B,EAAE,SAAoC;QACzE,IAAI,CAAC,IAAI,EAAE,CAAC;QAEZ,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC;YACxB,YAAY,EAAE,oBAAoB;YAClC,MAAM,EAAE,uBAAuB;SAChC,CAAC,CAAC;QAEH,MAAM,CAAC,WAAW,CAAC;YACjB,MAAM,EAAE,WAAW,CAAC,EAAE;gBACpB,QAAQ,WAAW,CAAC,QAAQ,EAAE,CAAC;oBAC/B,KAAK,qBAAqB;wBACxB,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC;wBAC5B,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAC;wBACjE,MAAM;oBACR,KAAK,uBAAuB;wBAC1B,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC;wBAC5B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;wBAClE,MAAM;oBACR,KAAK,uBAAuB,CAAC;oBAC7B,KAAK,yBAAyB,CAAC;oBAC/B,KAAK,mBAAmB,CAAC;oBACzB,KAAK,oCAAoC;wBACvC,gEAAgE;wBAChE,2CAA2C;wBAC3C,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;4BACzB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;4BAC3B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,mFAAmF;kCAC7F,2EAA2E,CAAC,CAAC;wBACnF,CAAC;6BAAM,CAAC;4BACN,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,+CAA+C,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;wBACxF,CAAC;wBACD,MAAM;oBACR;wBACE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,sCAAsC,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;gBAC/E,CAAC;YACH,CAAC;YACD,OAAO,EAAE,KAAK,CAAC,EAAE;gBACf,IAAI,CAAC;oBACH,SAAS,CAAC,KAAK,CAAC,OAAiB,CAAC,CAAC;gBACrC,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,uDAAuD;oBACvD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,8CAA8C,EAAE,KAAK,CAAC,CAAC;gBACxE,CAAC;YACH,CAAC;SACF,CAAC,CAAC;QAEH,MAAM,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC,mBAAmB,uBAAuB,EAAE,CAAC,EAAE,CAAC,CAAC;QAC/E,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,CAAC;gBACH,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;gBAC7B,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACrB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,6CAA6C,EAAE,KAAK,CAAC,CAAC;YACvE,CAAC;YACD,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QAC1B,CAAC;IACH,CAAC;CACF"}