Browse code

- timeout and retry with incremental backoff - substr to substring

Louis authored on01/06/2023 21:50:13
Showing3 changed files
1 1
deleted file mode 100644
2 2
Binary files a/build/dscam-h-s.pbw and /dev/null differ
3 3
Binary files a/build/synocam_home_switch.pbw and b/build/synocam_home_switch.pbw differ
... ...
@@ -8,348 +8,349 @@ var messageKeys = require('message_keys');
8 8
 var sid;
9 9
 var status;
10 10
 var retry;
11
-retry = 1;
11
+retry = 0;
12 12
 
13 13
 
14 14
 function dec2hex(s) {
15
-  return (s < 15.5 ? '0' : '') + Math.round(s).toString(16);
15
+    return (s < 15.5 ? '0' : '') + Math.round(s).toString(16);
16 16
 }
17 17
 
18 18
 function hex2dec(s) {
19
-  return parseInt(s, 16);
19
+    return parseInt(s, 16);
20 20
 }
21 21
 
22 22
 function base32tohex(base32) {
23
-  var base32chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
24
-  var bits = "";
25
-  var hex = "";
26
-
27
-  for (var i = 0; i < base32.length; i++) {
28
-    var val = base32chars.indexOf(base32.charAt(i).toUpperCase());
29
-    bits += leftpad(val.toString(2), 5, '0');
30
-  }
31
-  //console.log('-- bits : ' + bits)
32
-
33
-  for (var i = 0; i + 4 <= bits.length; i += 4) {
34
-    var chunk = bits.substr(i, 4);
35
-    hex = hex + parseInt(chunk, 2).toString(16);
36
-  }
37
-  return hex;
23
+    var base32chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
24
+    var bits = "";
25
+    var hex = "";
26
+
27
+    for (var i = 0; i < base32.length; i++) {
28
+        var val = base32chars.indexOf(base32.charAt(i).toUpperCase());
29
+        bits += leftpad(val.toString(2), 5, '0');
30
+    }
31
+    //console.log('-- bits : ' + bits)
32
+
33
+    for (var i = 0; i + 4 <= bits.length; i += 4) {
34
+        var chunk = bits.substring(i, i + 4);
35
+        hex = hex + parseInt(chunk, 2).toString(16);
36
+    }
37
+    return hex;
38 38
 
39 39
 }
40 40
 
41 41
 function leftpad(str, len, pad) {
42
-  if (len + 1 >= str.length) {
43
-    str = Array(len + 1 - str.length).join(pad) + str;
44
-  }
45
-  return str;
42
+    if (len + 1 >= str.length) {
43
+        str = Array(len + 1 - str.length).join(pad) + str;
44
+    }
45
+    return str;
46 46
 }
47 47
 
48 48
 function getOtp() {
49
-  key = base32tohex(JSON.parse(localStorage.getItem('clay-settings')).OTP_seed)
50
-  //console.log('-- seed:' + JSON.parse(localStorage.getItem('clay-settings')).OTP_seed)
51
-  //console.log('-- key:' + key)
52
-  var epoch = Math.round(new Date().getTime() / 1000.0);
53
-  var time = leftpad(dec2hex(Math.floor(epoch / 30)), 16, '0');
54
-  // updated for jsSHA v2.0.0 - http://caligatio.github.io/jsSHA/
55
-  var shaObj = new jsSHA("SHA-1", "HEX");
56
-  shaObj.setHMACKey(key, "HEX");
57
-  shaObj.update(time);
58
-  var hmac = shaObj.getHMAC("HEX");
59
-  //console.log('-- hmac:' + hmac)
60
-  var offset = hex2dec(hmac.substring(hmac.length - 1));
61
-  //console.log('--offset:' + offset)
62
-  var otp = (hex2dec(hmac.substr(offset * 2, 8)) & hex2dec('7fffffff')) + '';
63
-  otp = (otp).substr(otp.length - 6, 6);
64
-  return otp
49
+    key = base32tohex(JSON.parse(localStorage.getItem('clay-settings')).OTP_seed)
50
+        //console.log('-- seed:' + JSON.parse(localStorage.getItem('clay-settings')).OTP_seed)
51
+        //console.log('-- key:' + key)
52
+    var epoch = Math.round(new Date().getTime() / 1000.0);
53
+    var time = leftpad(dec2hex(Math.floor(epoch / 30)), 16, '0');
54
+    // updated for jsSHA v2.0.0 - http://caligatio.github.io/jsSHA/
55
+    var shaObj = new jsSHA("SHA-1", "HEX");
56
+    shaObj.setHMACKey(key, "HEX");
57
+    shaObj.update(time);
58
+    var hmac = shaObj.getHMAC("HEX");
59
+    //console.log('-- hmac:' + hmac)
60
+    var offset = hex2dec(hmac.substring(hmac.length - 1));
61
+    //console.log('--offset:' + offset)
62
+    var otp = (hex2dec(hmac.substring(offset * 2, offset * 2 + 8)) & hex2dec('7fffffff')) + '';
63
+    otp = (otp).substring(otp.length - 6, otp.length);
64
+    return otp
65 65
 }
66 66
 
67 67
 function xhr_to_syno(method, url_path, onload_function, max_retry) {
68
-  console.log('------xhr start')
69
-  status = "";
70
-  if (JSON.parse(localStorage.getItem('clay-settings')).server) {
71
-    var server = JSON.parse(localStorage.getItem('clay-settings')).server
72
-    url = server + url_path;
73
-    var xhr = new XMLHttpRequest();
74
-    xhr.timeout = 6000; // time in milliseconds
75
-
76
-    xhr.open(method, url, true);
77
-    console.log('------xhr opened')
78
-    xhr.onload = function () {
79
-      console.log('------xhr onload')
80
-      if (xhr.readyState === 4) {
81
-        retry = 1;
82
-        console.log('------xhr request returned');
83
-        if (xhr.status == 200) {
84
-          onload_function(xhr);
85
-          return true;
86
-        } else {
87
-          console.log('------xhr request returned error code ' + xhr.status);
88
-          message = "Error (XHR"+xhr.status+")";
89
-          // Build message
90
-          var dict = {
91
-            'status': message,
92
-          };
93
-
94
-          // Send the message
95
-          Pebble.sendAppMessage(dict, function (e) {
96
-            console.log('sent');
97
-          }, function () {
98
-            console.log('failed');
99
-          });
100
-          return false;
101
-        }
102
-      }
103
-    };
104
-
105
-    xhr.ontimeout = function (e) {
106
-      retry++;
107
-      if (retry < max_retry) {
108
-        console.log('------xhr timed out retrying another time ');
109
-        //send back "timeout" to watch
110
-        message = "Time out, retrying...";
111
-
112
-        // Build message
113
-        var dict = {
114
-          'status': message,
68
+    console.log('------xhr start')
69
+    status = "";
70
+    if (JSON.parse(localStorage.getItem('clay-settings')).server) {
71
+        var server = JSON.parse(localStorage.getItem('clay-settings')).server
72
+        url = server + url_path;
73
+        var xhr = new XMLHttpRequest();
74
+        xhr.timeout = 60000; // time in milliseconds
75
+
76
+        xhr.open(method, url, true);
77
+        console.log('------xhr opened')
78
+        xhr.onload = function() {
79
+            console.log('------xhr onload')
80
+            if (xhr.readyState === 4) {
81
+                retry = 0;
82
+                console.log('------xhr request returned');
83
+                if (xhr.status == 200) {
84
+                    onload_function(xhr);
85
+                    return true;
86
+                } else {
87
+                    console.log('------xhr request returned error code ' + xhr.status);
88
+                    message = "Error (XHR" + xhr.status + ")";
89
+                    // Build message
90
+                    var dict = {
91
+                        'status': message,
92
+                    };
93
+
94
+                    // Send the message
95
+                    Pebble.sendAppMessage(dict, function(e) {
96
+                        console.log('sent');
97
+                    }, function() {
98
+                        console.log('failed');
99
+                    });
100
+                    return false;
101
+                }
102
+            }
115 103
         };
116 104
 
117
-        // Send the message
118
-        Pebble.sendAppMessage(dict, function (e) {
119
-          console.log('sent');
120
-        }, function () {
121
-          console.log('failed');
122
-        });
123
-        xhr_to_syno(method, url_path, onload_function, max_retry);
124
-      } else {
125
-        console.log('------xhr timed out ' + retry + ' times');
126
-        //send back "timeout" to watch
127
-        message = "Time out too many times, verify settings and connectivity";
128
-
129
-        // Build message
130
-        var dict = {
131
-          'status': message,
105
+        xhr.ontimeout = function(e) {
106
+            retry++;
107
+            if (retry < max_retry) {
108
+                console.log('------xhr timed out retrying another time ');
109
+                //send back "timeout" to watch
110
+                message = "Time out (" + retry + "), retrying...";
111
+
112
+                // Build message
113
+                var dict = {
114
+                    'status': message,
115
+                };
116
+
117
+                // Send the message
118
+                Pebble.sendAppMessage(dict, function(e) {
119
+                    console.log('sent');
120
+                }, function() {
121
+                    console.log('failed');
122
+                });
123
+
124
+                setTimeout(function() { xhr_to_syno(method, url_path, onload_function, max_retry) }, 60000 * retry);
125
+            } else {
126
+                console.log('------xhr timed out ' + retry + ' times');
127
+                //send back "timeout" to watch
128
+                message = "Time out " + retry + " times, verify settings and connectivity";
129
+
130
+                // Build message
131
+                var dict = {
132
+                    'status': message,
133
+                };
134
+
135
+                // Send the message
136
+                Pebble.sendAppMessage(dict, function(e) {
137
+                    console.log('sent');
138
+                }, function() {
139
+                    console.log('failed');
140
+                });
141
+                return false;
142
+            }
132 143
         };
144
+        xhr.send(null);
133 145
 
134
-        // Send the message
135
-        Pebble.sendAppMessage(dict, function (e) {
136
-          console.log('sent');
137
-        }, function () {
138
-          console.log('failed');
139
-        });
140
-        return false;
141
-      }
142
-    };
143
-    xhr.send(null);
144
-
145
-  } else {
146
-    Pebble.showSimpleNotificationOnPebble("DSCam H-S", "You need to set your Synology account and server.");
147
-  }
146
+    } else {
147
+        Pebble.showSimpleNotificationOnPebble("DSCam H-S", "You need to set your Synology account and server.");
148
+    }
148 149
 
149 150
 }
150 151
 
151 152
 
152 153
 function authenticate() {
153
-  var response;
154
-  sid = "";
155
-  console.log('---- authenticate');
156
-  if (JSON.parse(localStorage.getItem('clay-settings')).username && JSON.parse(localStorage.getItem('clay-settings')).password) {
157
-    var username = JSON.parse(localStorage.getItem('clay-settings')).username;
158
-    var password = JSON.parse(localStorage.getItem('clay-settings')).password;
159
-    console.log('-- username:' + username);
160
-    console.log('-- password:' + password.substring(0, 1) + '...');
161
-    var url_path = "/webapi/auth.cgi?api=SYNO.API.Auth&method=Login&version=6&account=" + username + "&passwd=" + password + "&session=SurveillanceStation&format=sid";
162
-    if (JSON.parse(localStorage.getItem('clay-settings')).OTP_enabled) {
163
-      var otp_code = getOtp()
164
-      console.log('-- otp_code is :' + otp_code)
165
-      url_path = url_path + "&otp_code=" + otp_code
166
-    }
167
-    var method = "GET";
168
-    onload_function = function (xhr) {
169
-      response = JSON.parse(xhr.responseText);
170
-      if (response.success == true) {
171
-        sid = response.data.sid;
172
-        console.log('------Authentication succeeded');
173
-        if (sid != "") {
174
-          message = "Welcome to Syno Cam Switch ! ready & authenticated";
175
-          // Build message
176
-          var dict = {
177
-            'auth': message,
178
-          };
179
-          // Send the message
180
-          Pebble.sendAppMessage(dict, function (e) {
181
-            console.log('sent');
182
-          }, function () {
183
-            console.log('failed');
184
-          });
185
-          get_status();
186
-        } else {
187
-          console.log('------Unexpected error : authentication is OK but no SID retrieved');
188
-          message = "Auth error, no SID";
189
-          // Build message
190
-          var dict = {
191
-            'auth': message,
192
-          };
193
-          // Send the message
194
-          Pebble.sendAppMessage(dict, function (e) {
195
-            console.log('sent');
196
-          }, function () {
197
-            console.log('failed');
198
-          });
154
+    var response;
155
+    sid = "";
156
+    console.log('---- authenticate');
157
+    if (JSON.parse(localStorage.getItem('clay-settings')).username && JSON.parse(localStorage.getItem('clay-settings')).password) {
158
+        var username = JSON.parse(localStorage.getItem('clay-settings')).username;
159
+        var password = JSON.parse(localStorage.getItem('clay-settings')).password;
160
+        console.log('-- username:' + username);
161
+        console.log('-- password:' + password.substring(0, 1) + '...');
162
+        var url_path = "/webapi/auth.cgi?api=SYNO.API.Auth&method=Login&version=6&account=" + username + "&passwd=" + password + "&session=SurveillanceStation&format=sid";
163
+        if (JSON.parse(localStorage.getItem('clay-settings')).OTP_enabled) {
164
+            var otp_code = getOtp()
165
+            console.log('-- otp_code is :' + otp_code)
166
+            url_path = url_path + "&otp_code=" + otp_code
199 167
         }
200
-      } else {
201
-        console.log('------Authentication failed : ' + JSON.stringify(response));
202
-        message = "Authentication failed";
203
-        // Build message
204
-        var dict = {
205
-          'auth': message,
168
+        var method = "GET";
169
+        onload_function = function(xhr) {
170
+            response = JSON.parse(xhr.responseText);
171
+            if (response.success == true) {
172
+                sid = response.data.sid;
173
+                console.log('------Authentication succeeded');
174
+                if (sid != "") {
175
+                    message = "Welcome to Syno Cam Switch ! ready & authenticated";
176
+                    // Build message
177
+                    var dict = {
178
+                        'auth': message,
179
+                    };
180
+                    // Send the message
181
+                    Pebble.sendAppMessage(dict, function(e) {
182
+                        console.log('sent');
183
+                    }, function() {
184
+                        console.log('failed');
185
+                    });
186
+                    get_status();
187
+                } else {
188
+                    console.log('------Unexpected error : authentication is OK but no SID retrieved');
189
+                    message = "Auth error, no SID";
190
+                    // Build message
191
+                    var dict = {
192
+                        'auth': message,
193
+                    };
194
+                    // Send the message
195
+                    Pebble.sendAppMessage(dict, function(e) {
196
+                        console.log('sent');
197
+                    }, function() {
198
+                        console.log('failed');
199
+                    });
200
+                }
201
+            } else {
202
+                console.log('------Authentication failed : ' + JSON.stringify(response));
203
+                message = "Authentication failed";
204
+                // Build message
205
+                var dict = {
206
+                    'auth': message,
207
+                };
208
+                // Send the message
209
+                Pebble.sendAppMessage(dict, function(e) {
210
+                    console.log('sent');
211
+                }, function() {
212
+                    console.log('failed');
213
+                });
214
+            }
206 215
         };
207
-        // Send the message
208
-        Pebble.sendAppMessage(dict, function (e) {
209
-          console.log('sent');
210
-        }, function () {
211
-          console.log('failed');
212
-        });
213
-      }
214
-    };
215
-    max_retry = 10;
216
-    xhr_to_syno(method, url_path, onload_function, max_retry);
217
-  } else {
218
-    console.log("--- failed to get settings");
219
-    Pebble.showSimpleNotificationOnPebble("DSCam H-S", "You need to set your Synology account and server.");
220
-  }
216
+        max_retry = 10;
217
+        xhr_to_syno(method, url_path, onload_function, max_retry);
218
+    } else {
219
+        console.log("--- failed to get settings");
220
+        Pebble.showSimpleNotificationOnPebble("DSCam H-S", "You need to set your Synology account and server.");
221
+    }
221 222
 
222 223
 
223 224
 }
224 225
 
225 226
 
226 227
 function get_status() {
227
-  var response;
228
-
229
-  if (sid != "") {
230
-    status = "";
231
-    console.log('---- get_status');
232
-
233
-    var url_path = "/webapi/entry.cgi?api=SYNO.SurveillanceStation.HomeMode&version=1&method=GetInfo&_sid=" + sid;
234
-    var method = "GET";
235
-
236
-    onload_function = function (xhr) {
237
-      response = JSON.parse(xhr.responseText);
238
-      if (response.success == true) {
239
-        status = response.data.on;
240
-        var message;
241
-        switch (status) {
242
-          case true:
243
-            message = "Home mode is ON (camera is off)";
244
-            break;
245
-          case false:
246
-            message = "Home mode is OFF (camera is on)";
247
-            break;
248
-          default:
249
-            message = "something happened, try again ! (IMPOSSIBLE)";
228
+    var response;
229
+
230
+    if (sid != "") {
231
+        status = "";
232
+        console.log('---- get_status');
233
+
234
+        var url_path = "/webapi/entry.cgi?api=SYNO.SurveillanceStation.HomeMode&version=1&method=GetInfo&_sid=" + sid;
235
+        var method = "GET";
236
+
237
+        onload_function = function(xhr) {
238
+            response = JSON.parse(xhr.responseText);
239
+            if (response.success == true) {
240
+                status = response.data.on;
241
+                var message;
242
+                switch (status) {
243
+                    case true:
244
+                        message = "Home mode is ON (camera is off)";
245
+                        break;
246
+                    case false:
247
+                        message = "Home mode is OFF (camera is on)";
248
+                        break;
249
+                    default:
250
+                        message = "something happened, try again ! (IMPOSSIBLE)";
251
+                }
252
+            } else {
253
+                message = "something happened, try again ! (G200)";
254
+            }
255
+            // Build message
256
+            var dict = {
257
+                'status': message,
258
+            };
259
+
260
+            // Send the message
261
+            Pebble.sendAppMessage(dict, function(e) {
262
+                console.log('sent');
263
+            }, function() {
264
+                console.log('failed');
265
+            });
250 266
         }
251
-      }else{
252
-        message = "something happened, try again ! (G200)";
253
-      }
254
-      // Build message
255
-      var dict = {
256
-        'status': message,
257
-      };
258
-
259
-      // Send the message
260
-      Pebble.sendAppMessage(dict, function (e) {
261
-        console.log('sent');
262
-      }, function () {
263
-        console.log('failed');
264
-      });
265
-    }
266 267
 
267
-    max_retry = 10;
268
-    xhr_to_syno(method, url_path, onload_function, max_retry);
268
+        max_retry = 10;
269
+        xhr_to_syno(method, url_path, onload_function, max_retry);
269 270
 
270
-  } else {
271
-    authenticate();
272
-    get_status();
273
-  }
271
+    } else {
272
+        authenticate();
273
+        get_status();
274
+    }
274 275
 
275 276
 }
276 277
 
277 278
 
278 279
 function switch_home(bool) {
279
-  var response;
280
-  console.log('---- authenticate');
281
-  if (sid != "") {
282
-    console.log('---- switching home mode to ' + bool);
283
-    url_path = "/webapi/entry.cgi?api=SYNO.SurveillanceStation.HomeMode&version=1&method=Switch&on=" + bool + "&_sid=" + sid;
284
-    method = "GET"
285
-    onload_switch_function = function (xhr) {
286
-      response = JSON.parse(xhr.responseText);
287
-      if (response.success == true) {
288
-        switch (bool) {
289
-          case true:
290
-            message = "Home mode is ON (camera is off)";
291
-            break;
292
-          case false:
293
-            message = "Home mode is OFF (camera is on)";
294
-            break;
295
-          default:
296
-            message = "something happened, try again ! (IMPOSSIBLE)";
280
+    var response;
281
+    console.log('---- authenticate');
282
+    if (sid != "") {
283
+        console.log('---- switching home mode to ' + bool);
284
+        url_path = "/webapi/entry.cgi?api=SYNO.SurveillanceStation.HomeMode&version=1&method=Switch&on=" + bool + "&_sid=" + sid;
285
+        method = "GET"
286
+        onload_switch_function = function(xhr) {
287
+            response = JSON.parse(xhr.responseText);
288
+            if (response.success == true) {
289
+                switch (bool) {
290
+                    case true:
291
+                        message = "Home mode is ON (camera is off)";
292
+                        break;
293
+                    case false:
294
+                        message = "Home mode is OFF (camera is on)";
295
+                        break;
296
+                    default:
297
+                        message = "something happened, try again ! (IMPOSSIBLE)";
298
+                }
299
+            } else {
300
+                message = "something happened, try again ! (S200)";
301
+            }
302
+            // Build message
303
+            dict = {
304
+                'status': message,
305
+            };
306
+
307
+            // Send the message
308
+            Pebble.sendAppMessage(dict, function(e) {
309
+                console.log('sent');
310
+            }, function() {
311
+                console.log('failed');
312
+            });
297 313
         }
298
-      }else{
299
-        message = "something happened, try again ! (S200)";
300
-      }
301
-      // Build message
302
-      dict = {
303
-        'status': message,
304
-      };
305
-
306
-      // Send the message
307
-      Pebble.sendAppMessage(dict, function (e) {
308
-        console.log('sent');
309
-      }, function () {
310
-        console.log('failed');
311
-      });
312
-    }
313
-    max_retry = 10;
314
-    xhr_to_syno(method, url_path, onload_switch_function, max_retry);
314
+        max_retry = 10;
315
+        xhr_to_syno(method, url_path, onload_switch_function, max_retry);
315 316
 
316 317
 
317
-  } else {
318
-    authenticate();
319
-    switch_home(bool);
320
-  }
318
+    } else {
319
+        authenticate();
320
+        switch_home(bool);
321
+    }
321 322
 
322 323
 }
323 324
 
324 325
 // Get JS readiness events
325
-Pebble.addEventListener('ready', function (e) {
326
-  console.log("---- local storage:");
327
-  console.log("user " + JSON.parse(localStorage.getItem('clay-settings')).username);
328
-  console.log('PebbleKit JS is ready');
329
-  // Update Watch on this
330
-  Pebble.sendAppMessage({ 'JSReady': 1 });
326
+Pebble.addEventListener('ready', function(e) {
327
+    console.log("---- local storage:");
328
+    console.log("user " + JSON.parse(localStorage.getItem('clay-settings')).username);
329
+    console.log('PebbleKit JS is ready');
330
+    // Update Watch on this
331
+    Pebble.sendAppMessage({ 'JSReady': 1 });
331 332
 });
332 333
 
333 334
 // Get AppMessage events
334
-Pebble.addEventListener('appmessage', function (e) {
335
-  // Get the dictionary from the message
336
-  var dict = e.payload;
337
-  console.log(dict[0].toString());
338
-  switch (dict[0]) {
339
-    case 'auth':
340
-      authenticate();
341
-      break;
342
-    case 'get':
343
-      get_status();
344
-      break;
345
-    case 'home_on':
346
-      switch_home(true);
347
-      break;
348
-    case 'home_off':
349
-      switch_home(false);
350
-      break;
351
-    default:
352
-      console.log('Sorry. I don\'t understand your request :' + dict[0]);
353
-  }
335
+Pebble.addEventListener('appmessage', function(e) {
336
+    // Get the dictionary from the message
337
+    var dict = e.payload;
338
+    console.log(dict[0].toString());
339
+    switch (dict[0]) {
340
+        case 'auth':
341
+            authenticate();
342
+            break;
343
+        case 'get':
344
+            get_status();
345
+            break;
346
+        case 'home_on':
347
+            switch_home(true);
348
+            break;
349
+        case 'home_off':
350
+            switch_home(false);
351
+            break;
352
+        default:
353
+            console.log('Sorry. I don\'t understand your request :' + dict[0]);
354
+    }
354 355
 
355 356
 });
356 357
\ No newline at end of file