2019-10-30 17:33:58 +00:00
|
|
|
Puck.debug=3;
|
|
|
|
|
2019-11-03 11:13:21 +00:00
|
|
|
// FIXME: use UART lib so that we handle errors properly
|
2019-10-30 17:33:58 +00:00
|
|
|
var Comms = {
|
2020-03-09 14:31:29 +00:00
|
|
|
reset : (opt) => new Promise((resolve,reject) => {
|
|
|
|
Puck.write(`\x03\x10reset(${opt=="wipe"?"1":""});\n`, (result) => {
|
2020-03-04 16:40:25 +00:00
|
|
|
if (result===null) return reject("Connection failed");
|
2020-02-28 11:44:25 +00:00
|
|
|
setTimeout(resolve,500);
|
2020-02-27 15:52:20 +00:00
|
|
|
});
|
2020-02-28 11:44:25 +00:00
|
|
|
}),
|
2020-02-27 15:52:20 +00:00
|
|
|
uploadApp : (app,skipReset) => {
|
2019-11-10 10:22:33 +00:00
|
|
|
return AppInfo.getFiles(app, httpGet).then(fileContents => {
|
|
|
|
return new Promise((resolve,reject) => {
|
2020-03-03 09:03:10 +00:00
|
|
|
console.log("uploadApp",fileContents.map(f=>f.name).join(", "));
|
|
|
|
// Upload each file one at a time
|
|
|
|
function doUploadFiles() {
|
|
|
|
// No files left - print 'reboot' message
|
|
|
|
if (fileContents.length==0) {
|
|
|
|
Puck.write(`\x10E.showMessage('Hold BTN3\\nto reload')\n`,(result) => {
|
|
|
|
if (result===null) return reject("");
|
2020-03-03 09:10:28 +00:00
|
|
|
resolve(app);
|
2020-03-03 09:03:10 +00:00
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var f = fileContents.shift();
|
|
|
|
console.log(`Upload ${f.name} => ${JSON.stringify(f.content)}`);
|
|
|
|
// Chould check CRC here if needed instead of returning 'OK'...
|
|
|
|
// E.CRC32(require("Storage").read(${JSON.stringify(app.name)}))
|
|
|
|
Puck.write(`\x10${f.cmd};Bluetooth.println("OK")\n`,(result) => {
|
|
|
|
if (!result || result.trim()!="OK") return reject("Unexpected response "+(result||""));
|
|
|
|
doUploadFiles();
|
|
|
|
}, true); // wait for a newline
|
|
|
|
}
|
|
|
|
// Start the upload
|
2020-02-27 15:52:20 +00:00
|
|
|
function doUpload() {
|
2020-03-03 09:03:10 +00:00
|
|
|
Puck.write(`\x10E.showMessage('Uploading\\n${app.id}...')\n`,(result) => {
|
2020-02-27 15:52:20 +00:00
|
|
|
if (result===null) return reject("");
|
2020-03-03 09:03:10 +00:00
|
|
|
doUploadFiles();
|
2020-02-27 15:52:20 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
if (skipReset) {
|
|
|
|
doUpload();
|
|
|
|
} else {
|
|
|
|
// reset to ensure we have enough memory to upload what we need to
|
2020-03-04 16:40:25 +00:00
|
|
|
Comms.reset().then(doUpload, reject)
|
2020-02-27 15:52:20 +00:00
|
|
|
}
|
2019-10-30 17:33:58 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
getInstalledApps : () => {
|
|
|
|
return new Promise((resolve,reject) => {
|
2019-11-06 15:53:38 +00:00
|
|
|
Puck.write("\x03",(result) => {
|
|
|
|
if (result===null) return reject("");
|
2020-02-28 17:02:26 +00:00
|
|
|
Puck.eval('require("Storage").list(/\.info$/).map(f=>{var j=require("Storage").readJSON(f,1)||{};j.id=f.slice(0,-5);return j})', (appList,err) => {
|
2019-11-06 15:53:38 +00:00
|
|
|
if (appList===null) return reject(err || "");
|
2019-11-03 11:13:21 +00:00
|
|
|
console.log("getInstalledApps", appList);
|
2019-10-30 17:33:58 +00:00
|
|
|
resolve(appList);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2019-11-03 11:13:21 +00:00
|
|
|
},
|
|
|
|
removeApp : app => { // expects an app structure
|
2020-03-05 15:34:37 +00:00
|
|
|
var storage = [{name:app.id+".info"}].concat(app.storage);
|
|
|
|
var cmds = storage.map(file=>{
|
2019-11-03 11:13:21 +00:00
|
|
|
return `\x10require("Storage").erase(${toJS(file.name)});\n`;
|
|
|
|
}).join("");
|
|
|
|
console.log("removeApp", cmds);
|
2020-02-27 15:52:20 +00:00
|
|
|
return Comms.reset().then(new Promise((resolve,reject) => {
|
|
|
|
Puck.write(`\x03\x10E.showMessage('Erasing\\n${app.id}...')${cmds}\x10E.showMessage('Hold BTN3\\nto reload')\n`,(result) => {
|
2019-11-06 15:53:38 +00:00
|
|
|
if (result===null) return reject("");
|
2019-11-03 11:13:21 +00:00
|
|
|
resolve();
|
|
|
|
});
|
2020-02-27 15:52:20 +00:00
|
|
|
}));
|
2019-11-07 09:26:46 +00:00
|
|
|
},
|
|
|
|
removeAllApps : () => {
|
2020-03-09 14:31:29 +00:00
|
|
|
return Comms.reset("wipe").then(() => new Promise((resolve,reject) => {
|
2020-02-27 15:52:20 +00:00
|
|
|
// Use write with newline here so we wait for it to finish
|
|
|
|
Puck.write('\x10E.showMessage("Erasing...");require("Storage").eraseAll();Bluetooth.println("OK")\n', (result,err) => {
|
2020-02-28 11:44:25 +00:00
|
|
|
if (!result || result.trim()!="OK") return reject(err || "");
|
2020-02-27 15:52:20 +00:00
|
|
|
resolve();
|
|
|
|
}, true /* wait for newline */);
|
|
|
|
}));
|
2019-11-07 09:26:46 +00:00
|
|
|
},
|
|
|
|
setTime : () => {
|
|
|
|
return new Promise((resolve,reject) => {
|
2020-01-17 11:44:57 +00:00
|
|
|
var d = new Date();
|
|
|
|
var tz = d.getTimezoneOffset()/-60
|
|
|
|
var cmd = '\x03\x10setTime('+(d.getTime()/1000)+');';
|
|
|
|
// in 1v93 we have timezones too
|
|
|
|
cmd += 'E.setTimeZone('+tz+');';
|
2020-02-28 17:02:26 +00:00
|
|
|
cmd += "(s=>{s&&(s.timezone="+tz+")&&require('Storage').write('setting.json',s);})(require('Storage').readJSON('setting.json',1))\n";
|
2020-01-17 11:44:57 +00:00
|
|
|
Puck.write(cmd, (result) => {
|
2019-11-07 09:26:46 +00:00
|
|
|
if (result===null) return reject("");
|
|
|
|
resolve();
|
|
|
|
});
|
|
|
|
});
|
2019-11-12 10:56:31 +00:00
|
|
|
},
|
2019-11-12 14:41:03 +00:00
|
|
|
disconnectDevice: () => {
|
|
|
|
var connection = Puck.getConnection();
|
|
|
|
|
|
|
|
if (!connection) return;
|
|
|
|
|
|
|
|
connection.close();
|
|
|
|
},
|
2019-11-12 10:56:31 +00:00
|
|
|
watchConnectionChange : cb => {
|
|
|
|
var connected = Puck.isConnected();
|
|
|
|
|
|
|
|
//TODO Switch to an event listener when Puck will support it
|
|
|
|
var interval = setInterval(() => {
|
|
|
|
if (connected === Puck.isConnected()) return;
|
|
|
|
|
|
|
|
connected = Puck.isConnected();
|
|
|
|
cb(connected);
|
|
|
|
}, 1000);
|
|
|
|
|
|
|
|
//stop watching
|
|
|
|
return () => {
|
|
|
|
clearInterval(interval);
|
|
|
|
};
|
2019-12-24 13:47:02 +00:00
|
|
|
},
|
|
|
|
listFiles : () => {
|
|
|
|
return new Promise((resolve,reject) => {
|
|
|
|
Puck.write("\x03",(result) => {
|
|
|
|
if (result===null) return reject("");
|
|
|
|
//use encodeURIComponent to serialize octal sequence of append files
|
|
|
|
Puck.eval('require("Storage").list().map(encodeURIComponent)', (files,err) => {
|
|
|
|
if (files===null) return reject(err || "");
|
|
|
|
files = files.map(decodeURIComponent);
|
|
|
|
console.log("listFiles", files);
|
|
|
|
resolve(files);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
readFile : (file) => {
|
|
|
|
return new Promise((resolve,reject) => {
|
|
|
|
//encode name to avoid serialization issue due to octal sequence
|
|
|
|
const name = encodeURIComponent(file);
|
|
|
|
Puck.write("\x03",(result) => {
|
|
|
|
if (result===null) return reject("");
|
2020-01-17 11:44:57 +00:00
|
|
|
//TODO: big files will not fit in RAM.
|
2019-12-24 13:47:02 +00:00
|
|
|
//we should loop and read chunks one by one.
|
|
|
|
//Use btoa for binary content
|
|
|
|
Puck.eval(`btoa(require("Storage").read(decodeURIComponent("${name}"))))`, (content,err) => {
|
|
|
|
if (content===null) return reject(err || "");
|
|
|
|
resolve(atob(content));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2019-10-30 17:33:58 +00:00
|
|
|
}
|
|
|
|
};
|