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
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
347
|
+
partitionId: number;
|
348
|
+
state: SYSTEM_STATES;
|
349
|
+
desiredState: SYSTEM_STATES;
|
350
|
+
extendedArmingOptions: {
|
351
|
+
Disarmed: [];
|
352
|
+
ArmedStay: [];
|
353
|
+
ArmedAway: [];
|
354
|
+
ArmedNight: [];
|
355
355
|
};
|
356
|
-
|
357
|
-
|
358
|
-
|
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
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
406
|
+
partitionId: number;
|
407
|
+
state: SYSTEM_STATES;
|
408
|
+
desiredState: SYSTEM_STATES;
|
409
|
+
extendedArmingOptions: {
|
410
|
+
Disarmed: [];
|
411
|
+
ArmedStay: [];
|
412
|
+
ArmedAway: [];
|
413
|
+
ArmedNight: [];
|
414
414
|
};
|
415
|
-
|
416
|
-
|
417
|
-
|
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?:
|
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?:
|
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?:
|
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?:
|
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:
|
1070
|
-
relationships:
|
1069
|
+
attributes: object;
|
1070
|
+
relationships: object;
|
1071
1071
|
}
|
package/dist/_models/States.js
CHANGED
@@ -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
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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)
|
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
|
-
|
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
|
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
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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')
|
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
|
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
|
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.
|
4
|
-
"betaVersion": "2.
|
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",
|