node-alarm-dot-com 2.0.0-beta.7 → 2.1.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.
@@ -344,18 +344,18 @@ export interface ApiPartitionState extends ApiDeviceState {
344
344
  id: string;
345
345
  type: RelationshipType.Partition;
346
346
  attributes: {
347
- 'partitionId': number;
348
- 'state': SYSTEM_STATES;
349
- 'desiredState': SYSTEM_STATES;
350
- 'extendedArmingOptions': {
351
- 'Disarmed': [];
352
- 'ArmedStay': [];
353
- 'ArmedAway': [];
354
- 'ArmedNight': [];
347
+ partitionId: number;
348
+ state: SYSTEM_STATES;
349
+ desiredState: SYSTEM_STATES;
350
+ extendedArmingOptions: {
351
+ Disarmed: [];
352
+ ArmedStay: [];
353
+ ArmedAway: [];
354
+ ArmedNight: [];
355
355
  };
356
- 'invalidExtendedArmingOptions': {
357
- 'Disarmed': number[][];
358
- 'ArmedStay': number[][];
356
+ invalidExtendedArmingOptions: {
357
+ Disarmed: number[][];
358
+ ArmedStay: number[][];
359
359
  ArmedAway: number[][];
360
360
  ArmedNight: number[][];
361
361
  };
@@ -403,18 +403,18 @@ export interface PartitionState extends DeviceState {
403
403
  id: string;
404
404
  type: RelationshipType.Partition;
405
405
  attributes: {
406
- 'partitionId': number;
407
- 'state': SYSTEM_STATES;
408
- 'desiredState': SYSTEM_STATES;
409
- 'extendedArmingOptions': {
410
- 'Disarmed': [];
411
- 'ArmedStay': [];
412
- 'ArmedAway': [];
413
- 'ArmedNight': [];
406
+ partitionId: number;
407
+ state: SYSTEM_STATES;
408
+ desiredState: SYSTEM_STATES;
409
+ extendedArmingOptions: {
410
+ Disarmed: [];
411
+ ArmedStay: [];
412
+ ArmedAway: [];
413
+ ArmedNight: [];
414
414
  };
415
- 'invalidExtendedArmingOptions': {
416
- 'Disarmed': number[][];
417
- 'ArmedStay': number[][];
415
+ invalidExtendedArmingOptions: {
416
+ Disarmed: number[][];
417
+ ArmedStay: number[][];
418
418
  ArmedAway: number[][];
419
419
  ArmedNight: number[][];
420
420
  };
@@ -595,7 +595,7 @@ export interface ApiCameraState extends ApiDeviceState {
595
595
  httpsPort: number;
596
596
  shouldUseEntireImageForSnapshot: boolean;
597
597
  hasDdnsed: boolean;
598
- usageProtocolMapping: {};
598
+ usageProtocolMapping: object;
599
599
  isVirtualCamera: boolean;
600
600
  supportsLiveView: boolean;
601
601
  forcedAspectRatio: null;
@@ -692,7 +692,7 @@ export interface CameraState extends DeviceState {
692
692
  httpsPort: number;
693
693
  shouldUseEntireImageForSnapshot: boolean;
694
694
  hasDdnsed: boolean;
695
- usageProtocolMapping: {};
695
+ usageProtocolMapping: object;
696
696
  isVirtualCamera: boolean;
697
697
  supportsLiveView: boolean;
698
698
  forcedAspectRatio: null;
@@ -829,7 +829,7 @@ export interface ApiThermostatState extends ApiDeviceState {
829
829
  canAccessTroubleshootingWizard: boolean;
830
830
  troubleshootingWizard?: null;
831
831
  canBeAssociatedToVideoDevice: boolean;
832
- associatedCameraDeviceIds: {};
832
+ associatedCameraDeviceIds: object;
833
833
  macAddress: string;
834
834
  manufacturer: string;
835
835
  isOAuth: boolean;
@@ -859,7 +859,7 @@ export interface ApiThermostatState extends ApiDeviceState {
859
859
  };
860
860
  };
861
861
  thermostatSettingsTemplate: {
862
- data?: any;
862
+ data?: unknown;
863
863
  };
864
864
  remoteTemperatureSensors: {
865
865
  data: [];
@@ -868,7 +868,7 @@ export interface ApiThermostatState extends ApiDeviceState {
868
868
  };
869
869
  };
870
870
  boilerControlSystem: {
871
- data?: any;
871
+ data?: unknown;
872
872
  };
873
873
  valveSwitches: {
874
874
  data: [];
@@ -979,7 +979,7 @@ export interface ThermostatState extends DeviceState {
979
979
  canAccessTroubleshootingWizard: boolean;
980
980
  troubleshootingWizard?: null;
981
981
  canBeAssociatedToVideoDevice: boolean;
982
- associatedCameraDeviceIds: {};
982
+ associatedCameraDeviceIds: object;
983
983
  macAddress: string;
984
984
  manufacturer: string;
985
985
  isOAuth: boolean;
@@ -1009,7 +1009,7 @@ export interface ThermostatState extends DeviceState {
1009
1009
  };
1010
1010
  };
1011
1011
  thermostatSettingsTemplate: {
1012
- data?: any;
1012
+ data?: unknown;
1013
1013
  };
1014
1014
  remoteTemperatureSensors: {
1015
1015
  data: [];
@@ -1018,7 +1018,7 @@ export interface ThermostatState extends DeviceState {
1018
1018
  };
1019
1019
  };
1020
1020
  boilerControlSystem: {
1021
- data?: any;
1021
+ data?: unknown;
1022
1022
  };
1023
1023
  valveSwitches: {
1024
1024
  data: [];
@@ -1066,6 +1066,6 @@ export interface ApiDeviceState {
1066
1066
  export interface DeviceState {
1067
1067
  id: string;
1068
1068
  type: RelationshipType;
1069
- attributes: any;
1070
- relationships: any;
1069
+ attributes: object;
1070
+ relationships: object;
1071
1071
  }
@@ -5,4 +5,5 @@ export interface PartitionActionOptions {
5
5
  noEntryDelay: boolean;
6
6
  silentArming: boolean;
7
7
  nightArming: boolean;
8
+ forceBypass: boolean;
8
9
  }
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SensorType = void 0;
4
+ /* eslint-disable @typescript-eslint/naming-convention */
4
5
  /**
5
6
  * Number indicating the type of sensor according to the Alarm.com API
6
7
  */
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.REL_TYPES = exports.THERMOSTAT_STATES = exports.GARAGE_STATES = exports.LOCK_STATES = exports.LIGHT_STATES = exports.SENSOR_STATES = exports.SYSTEM_STATES = void 0;
4
+ /* eslint-disable @typescript-eslint/naming-convention */
4
5
  /***
5
6
  * State of the partition as defined by Alarm.com
6
7
  * @readonly
package/dist/index.js CHANGED
@@ -57,25 +57,27 @@ async function login(username, password, existingMfaToken) {
57
57
  let loginFormBody, identities, systems;
58
58
  // load initial alarm.com page to gather required hidden form fields
59
59
  await get(ADCLOGIN_URL)
60
- .then(res => {
60
+ .then((res) => {
61
61
  /* eslint-disable @typescript-eslint/naming-convention */
62
62
  const loginObj = {
63
- '__EVENTTARGET': null,
64
- '__EVENTARGUMENT': null,
65
- '__VIEWSTATEENCRYPTED': null,
66
- '__EVENTVALIDATION': res.body.match(/name="__EVENTVALIDATION".*?value="([^"]*)"/)[1],
67
- '__VIEWSTATE': res.body.match(/name="__VIEWSTATE".*?value="([^"]*)"/)[1],
68
- '__VIEWSTATEGENERATOR': res.body.match(/name="__VIEWSTATEGENERATOR".*?value="([^"]*)"/)[1],
69
- '__PREVIOUSPAGE': res.body.match(/name="__PREVIOUSPAGE".*?value="([^"]*)"/)[1],
70
- 'IsFromNewSite': '1',
71
- 'ctl00$ContentPlaceHolder1$loginform$txtUserName': username,
72
- 'txtPassword': password
63
+ __EVENTTARGET: null,
64
+ __EVENTARGUMENT: null,
65
+ __VIEWSTATEENCRYPTED: null,
66
+ __EVENTVALIDATION: res.body.match(/name="__EVENTVALIDATION".*?value="([^"]*)"/)[1],
67
+ __VIEWSTATE: res.body.match(/name="__VIEWSTATE".*?value="([^"]*)"/)[1],
68
+ __VIEWSTATEGENERATOR: res.body.match(/name="__VIEWSTATEGENERATOR".*?value="([^"]*)"/)[1],
69
+ __PREVIOUSPAGE: res.body.match(/name="__PREVIOUSPAGE".*?value="([^"]*)"/)[1],
70
+ IsFromNewSite: '1',
71
+ ctl00$ContentPlaceHolder1$loginform$txtUserName: username,
72
+ txtPassword: password
73
73
  };
74
74
  /* eslint-enable @typescript-eslint/naming-convention */
75
75
  // build login form body
76
- loginFormBody = Object.keys(loginObj).map(k => encodeURIComponent(k) + '=' + encodeURIComponent(loginObj[k])).join('&');
76
+ loginFormBody = Object.keys(loginObj)
77
+ .map((k) => encodeURIComponent(k) + '=' + encodeURIComponent(loginObj[k]))
78
+ .join('&');
77
79
  })
78
- .catch(err => {
80
+ .catch((err) => {
79
81
  throw new Error(`GET ${ADCLOGIN_URL} failed: ${err.message || err}`);
80
82
  });
81
83
  await (0, node_fetch_1.default)(ADCFORMLOGIN_URL, {
@@ -84,14 +86,16 @@ async function login(username, password, existingMfaToken) {
84
86
  headers: {
85
87
  'Content-Type': 'application/x-www-form-urlencoded',
86
88
  'User-Agent': UA,
87
- 'Cookie': `twoFactorAuthenticationId=${existingMfaToken};`
89
+ Cookie: `twoFactorAuthenticationId=${existingMfaToken};`
88
90
  },
89
91
  /* eslint-enable @typescript-eslint/naming-convention */
90
92
  body: loginFormBody,
91
93
  redirect: 'manual'
92
94
  })
93
- .then(res => {
94
- loginCookies = res.headers.raw()['set-cookie'].map(c => c.split(';')[0]).join('; ');
95
+ .then((res) => {
96
+ loginCookies = res.headers
97
+ .raw()['set-cookie'].map((c) => c.split(';')[0])
98
+ .join('; ');
95
99
  // gather ajaxkey for session headers
96
100
  const re = /afg=([^;]+);/.exec(loginCookies);
97
101
  if (!re) {
@@ -105,20 +109,20 @@ async function login(username, password, existingMfaToken) {
105
109
  await get(IDENTITIES_URL, {
106
110
  /* eslint-disable @typescript-eslint/naming-convention */
107
111
  headers: {
108
- 'Accept': 'application/vnd.api+json',
109
- 'Cookie': loginCookies,
110
- 'ajaxrequestuniquekey': ajaxKey,
111
- 'Referer': 'https://www.alarm.com/web/system/home',
112
+ Accept: 'application/vnd.api+json',
113
+ Cookie: loginCookies,
114
+ ajaxrequestuniquekey: ajaxKey,
115
+ Referer: 'https://www.alarm.com/web/system/home',
112
116
  'User-Agent': UA
113
117
  }
114
118
  /* eslint-enable @typescript-eslint/naming-convention */
115
119
  })
116
- .then(res => {
120
+ .then((res) => {
117
121
  // gather identities and systems
118
122
  identities = res.body;
119
123
  systems = (identities.data || []).map((d) => getValue(d, 'relationships.selectedSystem.data.id'));
120
124
  })
121
- .catch(err => {
125
+ .catch((err) => {
122
126
  throw new Error(`GET ${IDENTITIES_URL} failed: ${err.message || err}`);
123
127
  });
124
128
  return {
@@ -146,36 +150,38 @@ async function getCurrentState(systemID, authOpts) {
146
150
  const components = new Map();
147
151
  // push the results of getComponents into the components
148
152
  // Now we go through and get detailed information about all devices
149
- const partitionIDs = rels.partitions.data.map(partition => partition.id);
153
+ const partitionIDs = rels.partitions.data.map((partition) => partition.id);
150
154
  if (typeof partitionIDs[0] !== 'undefined') {
151
155
  components.set('partitions', await getComponents(PARTITIONS_URL, partitionIDs, authOpts));
152
156
  }
153
- const sensorIDs = rels.sensors.data.map(sensor => sensor.id);
157
+ const sensorIDs = rels.sensors.data.map((sensor) => sensor.id);
154
158
  if (typeof sensorIDs[0] !== 'undefined') {
155
159
  components.set('sensors', await getComponents(SENSORS_URL, sensorIDs, authOpts));
156
160
  }
157
- const lightIDs = rels.lights.data.map(light => light.id);
161
+ const lightIDs = rels.lights.data.map((light) => light.id);
158
162
  if (typeof lightIDs[0] !== 'undefined') {
159
163
  components.set('lights', await getComponents(LIGHTS_URL, lightIDs, authOpts));
160
164
  }
161
- const lockIDs = rels.locks.data.map(lock => lock.id);
165
+ const lockIDs = rels.locks.data.map((lock) => lock.id);
162
166
  if (typeof lockIDs[0] !== 'undefined') {
163
167
  components.set('locks', await getComponents(LOCKS_URL, lockIDs, authOpts));
164
168
  }
165
- const garageIDs = rels.garageDoors.data.map(garage => garage.id);
169
+ const garageIDs = rels.garageDoors.data.map((garage) => garage.id);
166
170
  if (typeof garageIDs[0] !== 'undefined') {
167
171
  components.set('garages', await getComponents(GARAGE_URL, garageIDs, authOpts));
168
172
  }
169
- return ({
173
+ return {
170
174
  id: res.data.id,
171
175
  attributes: res.data.attributes,
172
- partitions: components.has('partitions') ? components.get('partitions').data : [],
176
+ partitions: components.has('partitions')
177
+ ? components.get('partitions').data
178
+ : [],
173
179
  sensors: components.has('sensors') ? components.get('sensors').data : [],
174
180
  lights: components.has('lights') ? components.get('lights').data : [],
175
181
  locks: components.has('locks') ? components.get('locks').data : [],
176
182
  garages: components.has('garages') ? components.get('garages').data : [],
177
183
  relationships: rels
178
- });
184
+ };
179
185
  }
180
186
  exports.getCurrentState = getCurrentState;
181
187
  /**
@@ -190,7 +196,7 @@ async function getComponents(url, componentIDs, authOpts) {
190
196
  const IDs = Array.isArray(componentIDs) ? componentIDs : [componentIDs];
191
197
  let requests = [];
192
198
  if (IDs.length <= 50) {
193
- const getUrl = `${url}?${IDs.map(id => `ids%5B%5D=${id}`).join('&')}`;
199
+ const getUrl = `${url}?${IDs.map((id) => `ids%5B%5D=${id}`).join('&')}`;
194
200
  requests.push(authenticatedGet(getUrl, authOpts));
195
201
  }
196
202
  else {
@@ -199,10 +205,10 @@ async function getComponents(url, componentIDs, authOpts) {
199
205
  const shortenedUrls = [];
200
206
  while (IDs.length > 50) {
201
207
  const currentArray = IDs.splice(0, 50);
202
- shortenedUrls.push(`${url}?${currentArray.map(id => `ids%5B%5D=${id}`).join('&')}`);
208
+ shortenedUrls.push(`${url}?${currentArray.map((id) => `ids%5B%5D=${id}`).join('&')}`);
203
209
  }
204
- shortenedUrls.push(`${url}?${IDs.map(id => `ids%5B%5D=${id}`).join('&')}`);
205
- requests = shortenedUrls.map(u => authenticatedGet(u, authOpts));
210
+ shortenedUrls.push(`${url}?${IDs.map((id) => `ids%5B%5D=${id}`).join('&')}`);
211
+ requests = shortenedUrls.map((u) => authenticatedGet(u, authOpts));
206
212
  }
207
213
  return await combineAPIDeviceAPICalls(requests);
208
214
  }
@@ -214,7 +220,7 @@ async function combineAPIDeviceAPICalls(apiCalls) {
214
220
  included: []
215
221
  };
216
222
  for (const apiCall of apiStateCalls) {
217
- for (const apiData of (apiCall.data)) {
223
+ for (const apiData of apiCall.data) {
218
224
  stateToReturn.data.push(apiData);
219
225
  }
220
226
  for (const apiInclude of apiCall.included) {
@@ -237,7 +243,8 @@ function partitionAction(partitionID, action, authOpts, opts) {
237
243
  opts = opts || {
238
244
  noEntryDelay: false,
239
245
  silentArming: false,
240
- nightArming: false
246
+ nightArming: false,
247
+ forceBypass: false
241
248
  };
242
249
  const url = `${PARTITIONS_URL}${partitionID}/${action}`;
243
250
  const body = {
@@ -247,9 +254,12 @@ function partitionAction(partitionID, action, authOpts, opts) {
247
254
  };
248
255
  // We only want to set nightArming when told to do so
249
256
  //This is because calling nightArm when not supported will break the action
250
- if (opts.nightArming) {
257
+ if (opts.nightArming === true) {
251
258
  body['nightArming'] = true;
252
259
  }
260
+ if (opts.forceBypass === true) {
261
+ body['forceBypass'] = true;
262
+ }
253
263
  const postOpts = Object.assign({}, authOpts, { body });
254
264
  return authenticatedPost(url, postOpts);
255
265
  }
@@ -430,7 +440,7 @@ function getGarages(garageIDs, authOpts) {
430
440
  if (!Array.isArray(garageIDs)) {
431
441
  garageIDs = [garageIDs];
432
442
  }
433
- const query = garageIDs.map(id => `ids%5B%5D=${id}`).join('&');
443
+ const query = garageIDs.map((id) => `ids%5B%5D=${id}`).join('&');
434
444
  const url = `${GARAGE_URL}?${query}`;
435
445
  return authenticatedGet(url, authOpts);
436
446
  }
@@ -517,7 +527,11 @@ async function get(url, opts) {
517
527
  status = res.status;
518
528
  resHeaders = res.headers;
519
529
  const type = res.headers.get('content-type') || '';
520
- const body = await (type.indexOf('json') !== -1 ? (res.status === 204 ? {} : res.json()) : res.text());
530
+ const body = await (type.indexOf('json') !== -1
531
+ ? res.status === 204
532
+ ? {}
533
+ : res.json()
534
+ : res.text());
521
535
  if (status === 409) {
522
536
  throw new Error('Two factor is enabled on this account but not setup in the plugin.' +
523
537
  ' See the wiki for details');
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "node-alarm-dot-com",
3
- "version": "2.0.0-beta.7",
4
- "betaVersion": "2.0.0",
3
+ "version": "2.1.0-beta.0",
4
+ "betaVersion": "2.1.0",
5
5
  "description": "An interface module written in node.js to arm and disarm Alarm.com security systems.",
6
6
  "author": {
7
7
  "name": "Chase Lau",