var Clay = require('pebble-clay'); var clayConfig = require('./config'); var clay = new Clay(clayConfig); const jsSHA = require('./sha'); var messageKeys = require('message_keys'); var sid; var status; var retry; retry = 1; function dec2hex(s) { return (s < 15.5 ? '0' : '') + Math.round(s).toString(16); } function hex2dec(s) { return parseInt(s, 16); } function base32tohex(base32) { var base32chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; var bits = ""; var hex = ""; for (var i = 0; i < base32.length; i++) { var val = base32chars.indexOf(base32.charAt(i).toUpperCase()); bits += leftpad(val.toString(2), 5, '0'); } //console.log('-- bits : ' + bits) for (var i = 0; i + 4 <= bits.length; i += 4) { var chunk = bits.substr(i, 4); hex = hex + parseInt(chunk, 2).toString(16); } return hex; } function leftpad(str, len, pad) { if (len + 1 >= str.length) { str = Array(len + 1 - str.length).join(pad) + str; } return str; } function getOtp() { key = base32tohex(JSON.parse(localStorage.getItem('clay-settings')).OTP_seed) //console.log('-- seed:' + JSON.parse(localStorage.getItem('clay-settings')).OTP_seed) //console.log('-- key:' + key) var epoch = Math.round(new Date().getTime() / 1000.0); var time = leftpad(dec2hex(Math.floor(epoch / 30)), 16, '0'); // updated for jsSHA v2.0.0 - http://caligatio.github.io/jsSHA/ var shaObj = new jsSHA("SHA-1", "HEX"); shaObj.setHMACKey(key, "HEX"); shaObj.update(time); var hmac = shaObj.getHMAC("HEX"); //console.log('-- hmac:' + hmac) var offset = hex2dec(hmac.substring(hmac.length - 1)); //console.log('--offset:' + offset) var otp = (hex2dec(hmac.substr(offset * 2, 8)) & hex2dec('7fffffff')) + ''; otp = (otp).substr(otp.length - 6, 6); return otp } function xhr_to_syno(method, url_path, onload_function, max_retry) { console.log('------xhr start') status = ""; if (JSON.parse(localStorage.getItem('clay-settings')).server) { var server = JSON.parse(localStorage.getItem('clay-settings')).server url = server + url_path; var xhr = new XMLHttpRequest(); xhr.timeout = 6000; // time in milliseconds xhr.open(method, url, true); console.log('------xhr opened') xhr.onload = function () { console.log('------xhr onload') if (xhr.readyState === 4) { retry = 1; console.log('------xhr request returned'); if (xhr.status == 200) { onload_function(xhr); return true; } else { console.log('------xhr request returned error code ' + xhr.status); message = "Error (XHR"+xhr.status+")"; // Build message var dict = { 'status': message, }; // Send the message Pebble.sendAppMessage(dict, function (e) { console.log('sent'); }, function () { console.log('failed'); }); return false; } } }; xhr.ontimeout = function (e) { retry++; if (retry < max_retry) { console.log('------xhr timed out retrying another time '); //send back "timeout" to watch message = "Time out, retrying..."; // Build message var dict = { 'status': message, }; // Send the message Pebble.sendAppMessage(dict, function (e) { console.log('sent'); }, function () { console.log('failed'); }); xhr_to_syno(method, url_path, onload_function, max_retry); } else { console.log('------xhr timed out ' + retry + ' times'); //send back "timeout" to watch message = "Time out too many times, verify settings and connectivity"; // Build message var dict = { 'status': message, }; // Send the message Pebble.sendAppMessage(dict, function (e) { console.log('sent'); }, function () { console.log('failed'); }); return false; } }; xhr.send(null); } else { Pebble.showSimpleNotificationOnPebble("DSCam H-S", "You need to set your Synology account and server."); } } function authenticate() { var response; sid = ""; console.log('---- authenticate'); if (JSON.parse(localStorage.getItem('clay-settings')).username && JSON.parse(localStorage.getItem('clay-settings')).password) { var username = JSON.parse(localStorage.getItem('clay-settings')).username; var password = JSON.parse(localStorage.getItem('clay-settings')).password; console.log('-- username:' + username); console.log('-- password:' + password.substring(0, 1) + '...'); var url_path = "/webapi/auth.cgi?api=SYNO.API.Auth&method=Login&version=6&account=" + username + "&passwd=" + password + "&session=SurveillanceStation&format=sid"; if (JSON.parse(localStorage.getItem('clay-settings')).OTP_enabled) { var otp_code = getOtp() console.log('-- otp_code is :' + otp_code) url_path = url_path + "&otp_code=" + otp_code } var method = "GET"; onload_function = function (xhr) { response = JSON.parse(xhr.responseText); if (response.success == true) { sid = response.data.sid; console.log('------Authentication succeeded'); if (sid != "") { message = "Welcome to Syno Cam Switch ! ready & authenticated"; // Build message var dict = { 'auth': message, }; // Send the message Pebble.sendAppMessage(dict, function (e) { console.log('sent'); }, function () { console.log('failed'); }); timed_switch_home(15*60); } else { console.log('------Unexpected error : authentication is OK but no SID retrieved'); message = "Auth error, no SID"; // Build message var dict = { 'auth': message, }; // Send the message Pebble.sendAppMessage(dict, function (e) { console.log('sent'); }, function () { console.log('failed'); }); } } else { console.log('------Authentication failed : ' + JSON.stringify(response)); message = "Authentication failed"; // Build message var dict = { 'auth': message, }; // Send the message Pebble.sendAppMessage(dict, function (e) { console.log('sent'); }, function () { console.log('failed'); }); } }; max_retry = 10; xhr_to_syno(method, url_path, onload_function, max_retry); } else { console.log("--- failed to get settings"); Pebble.showSimpleNotificationOnPebble("DSCam H-S", "You need to set your Synology account and server."); } } function get_status() { var response; if (sid != "") { status = ""; console.log('---- get_status'); var url_path = "/webapi/entry.cgi?api=SYNO.SurveillanceStation.HomeMode&version=1&method=GetInfo&_sid=" + sid; var method = "GET"; onload_function = function (xhr) { response = JSON.parse(xhr.responseText); if (response.success == true) { status = response.data.on; var message; switch (status) { case true: message = "Home mode is ON (camera is off)"; break; case false: message = "Home mode is OFF (camera is on)"; break; default: message = "something happened, try again ! (IMPOSSIBLE)"; } }else{ message = "something happened, try again ! (G200)"; } // Build message var dict = { 'status': message, }; // Send the message Pebble.sendAppMessage(dict, function (e) { console.log('sent'); }, function () { console.log('failed'); }); } max_retry = 10; xhr_to_syno(method, url_path, onload_function, max_retry); } else { authenticate(); get_status(); } } function timed_switch_home(duration) { var response; console.log('---- authenticate'); if (sid != "") { var epoch = Math.round(new Date().getTime() / 1000.0); var start_ts = epoch + 10 var end_ts = duration + start_ts var d= new Date(end_ts*1000) var end_time = d.getHours()+":"+d.getMinutes()+":"+d.getSeconds(); console.log('---- switching home mode for ' + duration + 'seconds, until '+end_time); url_path = "/webapi/entry.cgi?api=SYNO.SurveillanceStation.HomeMode&version=1&method=SaveOneTimeSwitch&onetime_enable_on=true&onetime_disable_on=true&onetime_enable_time=" + start_ts + "&onetime_disable_time=" + end_ts + "&_sid=" + sid; method = "GET" onload_switch_function = function (xhr) { response = JSON.parse(xhr.responseText); if (response.success == true) { message = "Home mode is ON until "+end_time; }else{ message = "something happened, try again ! (S200)"; } // Build message dict = { 'status': message, }; // Send the message Home mode is ON for 900 seconds Pebble.sendAppMessage(dict, function (e) { console.log('sent \"'+message+'\"'); }, function () { console.log('failed'); }); } max_retry = 10; xhr_to_syno(method, url_path, onload_switch_function, max_retry); } else { authenticate(); switch_home(bool); } } function switch_home(bool) { var response; console.log('---- authenticate'); if (sid != "") { console.log('---- switching home mode to ' + bool); url_path = "/webapi/entry.cgi?api=SYNO.SurveillanceStation.HomeMode&version=1&method=Switch&on=" + bool + "&_sid=" + sid; method = "GET" onload_switch_function = function (xhr) { response = JSON.parse(xhr.responseText); if (response.success == true) { switch (bool) { case true: message = "Home mode is ON (camera is off)"; break; case false: message = "Home mode is OFF (camera is on)"; break; default: message = "something happened, try again ! (IMPOSSIBLE)"; } }else{ message = "something happened, try again ! (S200)"; } // Build message dict = { 'status': message, }; // Send the message Pebble.sendAppMessage(dict, function (e) { console.log('sent'); }, function () { console.log('failed'); }); } max_retry = 10; xhr_to_syno(method, url_path, onload_switch_function, max_retry); } else { authenticate(); switch_home(bool); } } // Get JS readiness events Pebble.addEventListener('ready', function (e) { console.log("---- local storage:"); console.log("user " + JSON.parse(localStorage.getItem('clay-settings')).username); console.log('PebbleKit JS is ready'); // Update Watch on this Pebble.sendAppMessage({ 'JSReady': 1 }); }); // Get AppMessage events Pebble.addEventListener('appmessage', function (e) { // Get the dictionary from the message var dict = e.payload; console.log(dict[0].toString()); switch (dict[0]) { case 'auth': authenticate(); break; case 'get': get_status(); break; case 'home_on': switch_home(true); //timed_switch_home(15*60) break; case 'home_off': switch_home(false); break; default: console.log('Sorry. I don\'t understand your request :' + dict[0]); } });