-
Notifications
You must be signed in to change notification settings - Fork 1
/
mobilealerts-api.js
155 lines (132 loc) · 5.49 KB
/
mobilealerts-api.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/* ~~~ credits ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Credits go to @sarnau for reverse engineering Mobile Alerts API. This gave me the Chance to adopt
his Findings into this Project for a better Approach of communicating with Mobile Alerts Servers.
https://github.com/sarnau/MMMMobileAlerts/blob/master/MobileAlertsGatewayApplicationAPI.markdown
*/
// ~~~ constants ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
const BASE_CONFIG = {
method: 'POST',
url: 'https://www.data199.com/api/v1/dashboard',
headers: {
"User-Agent" : "remotemonitor/478 CFNetwork/1485 Darwin/23.1.0",
"Accept-Language" : "en-us",
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
"Host" : "www.data199.com"
},
body: {
devicetoken: 'empty', // defaults to "empty"
vendorid: '44511524-8F90-49C1-A8F0-BAE5FE3EDF48', // iOS vendor UUID (returned by iOS, any UUID will do). Launch uuidgen from the terminal to generate a fresh one.
phoneid: '$PHONEID$', // Phone ID - probably generated by the server based on the vendorid (this string can be "Unknown" and it still works)
version: '1.52', // Info.plist CFBundleShortVersionString
build: '278', // Info.plist CFBundleVersion
executable: 'Mobile Alerts', // Info.plist CFBundleExecutable
bundle: 'de.synertronixx.remotemonitor' // [[NSBundle mainBundle] bundleIdentifier]
}
}
// ~~~ public functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
module.exports = {
createRequest: function(myPlatform, mySerials)
{
var request = JSON.parse(JSON.stringify(BASE_CONFIG)); // clone base config to request first!
var body = ''; // build temporary body...
for (var parm in request.body) {
if (request.body.hasOwnProperty(parm)) {
body += (body.length > 0 ? '&' : '');
body += (parm + '=' + request.body[parm]);
}
}
body = body.replace(/\$PHONEID\$/gi, myPlatform.PhoneID); // set configured phone id...
body += '&lang=' + myPlatform.Language; // set preferred language
body += '&timezoneoffset=' + (-1 * new Date().getTimezoneOffset()); // set local offset to UTC time
body += '&timeampm=' + (myPlatform.Clock ? 'true' : 'false'); // set 12h vs 24h clock
body += '&usecelsius=' + (myPlatform.Temperature ? 'false' : 'true'); // set Celcius vs Fahrenheit
body += '&usemm=' + (myPlatform.Rain ? 'false' : 'true'); // set mm va in
body += '&speedunit=' + myPlatform.Wind; // set wind speed (0: m/s, 1: km/h, 2: mph, 3: kn)
body += '×tamp=' + parseInt(new Date(new Date().toUTCString()) / 1000); // set current UTC timestamp
var md5 = body + 'uvh2r1qmbqk8dcgv0hc31a6l8s5cnb0ii7oglpfj' // SALT for the MD5
md5 = md5.replace(/\-/gi, '');
md5 = md5.replace(/\,/gi, '');
md5 = md5.replace(/\./gi, '');
md5 = md5.toLocaleLowerCase();
const crypto = require("crypto"); // create hash...
const utf8 = require('utf8');
var hash = crypto.createHash("md5").update(utf8.encode(md5)).digest('hex');
body += '&requesttoken=' + hash; // append hash, serials and filters to body...
body += '&deviceids=' + mySerials.join(',');
body += '&measurementcounts=';
for (var i = 0; i < mySerials.length; i++) {
switch (parseInt(mySerials[i].substr(0, 2))) {
case 0x8: // rain sensor -> all measurements
var f = '';
break;
default: // other sensors -> one measurement
var f = '1';
}
body += ((i > 0 ? ',' : '') + f);
}
request.body = body; // finalize our request...
return request;
},
fetchData: function(myPlatform, mySerials, myCallback)
{
myPlatform.debug('Fetching Data...');
request = this.createRequest(myPlatform, mySerials);
require('request')(request, function(myError, myResponse) {
if(myError) {
console.warn('There was an Error requesting Data from Mobile Alerts Servers: ' + myError);
} else {
switch (myResponse.statusCode)
{
case 403:
myPlatform.error('We were locked out from Mobile Alerts Team!');
break;
case 200:
myCallback(JSON.parse(myResponse.body));
break;
default:
myPlatform.warn(
'There was an unexpected Response from the Server: ' +
myResponse.statusCode + ' (myResponse.body)'
);
}
}
});
},
// ~~~ deprecated functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
fetchDataLegacy: function(myServer, myID, myCallback)
{
console.debug('Fetching Data...');
require('request')({
method: 'POST',
url: 'https://' + myServer + '/',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'MobileAlertsPleaseProvideOfficialAPI4ALL/1.0'
},
body: 'phoneid=' + myID
}, function(myError, myResponse) {
if(myError) {
console.warn('There was an Error requesting initial Data for Sensor-Matching: ' + myError);
} else {
switch (myResponse.statusCode)
{
case 403:
console.error(
'We were locked out from Mobile Alerts Team again! ' +
'Thank you guys for not providing an adequate public API' +
'for all users and sensors! :-('
);
break;
case 200:
myCallback(myResponse.body);
break;
default:
console.warn(
'There was an unexpected response code from the server: ' +
myResponse.statusCode + ' (myResponse.body)'
);
}
}
});
}
};