From 692ff509e51e73d63952f737c12ab5d5e99008fc Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 4 Feb 2020 16:13:06 +0000 Subject: [PATCH 001/407] Add button clear everything and reload default apps --- defaultapps.json | 1 + index.html | 3 ++- index.js | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 defaultapps.json diff --git a/defaultapps.json b/defaultapps.json new file mode 100644 index 000000000..11db89641 --- /dev/null +++ b/defaultapps.json @@ -0,0 +1 @@ +["boot","launch","mclock","setting","sbat","sbt"] diff --git a/index.html b/index.html index 4360c3bef..0141365b7 100644 --- a/index.html +++ b/index.html @@ -126,7 +126,8 @@

Utilities

-

+ +

diff --git a/index.js b/index.js index 8d5442697..fa986ac21 100644 --- a/index.js +++ b/index.js @@ -432,3 +432,36 @@ document.getElementById("removeall").addEventListener("click",event=>{ }); }); }); +// Install all default apps in one go +document.getElementById("installdefault").addEventListener("click",event=>{ + var defaultApps + httpGet("defaultapps.json").then(json=>{ + defaultApps = JSON.parse(json); + defaultApps = defaultApps.map( appid => appJSON.find(app=>app.id==appid) ); + if (defaultApps.some(x=>x===undefined)) + throw "Not all apps found"; + return showPrompt("Install Defaults","Remove everything and install default apps?"); + }).then(() => { + return Comms.removeAllApps(); + }).then(()=>{ + showToast("Existing apps removed","success"); + return new Promise(resolve => { + function upload() { + var app = defaultApps.shift(); + if (app===undefined) return resolve(); + Comms.uploadApp(app).then((appJSON) => { + if (appJSON) appsInstalled.push(appJSON); + showToast(app.name+" Uploaded", "success"); + upload(); + }); + } + upload(); + }); + }).then(()=>{ + showToast("Default apps successfully installed!","success"); + refreshMyApps(); + refreshLibrary(); + }).catch(err=>{ + showToast("App Install failed, "+err,"error"); + }); +}); From 0e7742d9365e2fc2579b1451431bb64734140b99 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 4 Feb 2020 16:15:37 +0000 Subject: [PATCH 002/407] Add better sort orders for most people, and remove the HID minification hack as it's no longer needed --- apps.json | 21 +++++++-------------- apps/hidkbd/hid-keyboard.min.js | 1 - 2 files changed, 7 insertions(+), 15 deletions(-) delete mode 100644 apps/hidkbd/hid-keyboard.min.js diff --git a/apps.json b/apps.json index 76151a627..3ba73b8ff 100644 --- a/apps.json +++ b/apps.json @@ -61,7 +61,6 @@ "storage": [ {"name":"+setting","url":"settings.json"}, {"name":"-setting","url":"settings.js"}, - {"name":"=setting","url":"settings-init.js"}, {"name":"@setting","url":"settings-default.json","evaluate":true}, {"name":"*setting","url":"settings-icon.js","evaluate":true} ], @@ -309,7 +308,7 @@ "tags": "bluetooth", "storage": [ {"name":"+hidkbd","url":"hid-keyboard.json"}, - {"name":"-hidkbd","url":"hid-keyboard.min.js"}, + {"name":"-hidkbd","url":"hid-keyboard.js"}, {"name":"*hidkbd","url":"hid-keyboard-icon.js","evaluate":true} ] }, @@ -343,8 +342,7 @@ {"name":"*pig","url":"animals-pig.js","evaluate":true}, {"name":"*sheep","url":"animals-sheep.js","evaluate":true}, {"name":"*mouse","url":"animals-mouse.js","evaluate":true} - ], - "sortorder" : 1 + ] }, { "id": "qrcode", "name": "Custom QR Code", @@ -404,8 +402,7 @@ {"name":"*nfr","url":"start-nfr.js","evaluate":true}, {"name":"*nodew","url":"start-nodew.js","evaluate":true}, {"name":"*tf","url":"start-tf.js","evaluate":true} - ], - "sortorder" : -1 + ] }, { "id": "ncfrun", "name": "NCEU 5K Fun Run", @@ -417,8 +414,7 @@ {"name":"+ncfrun","url":"nceu-funrun.json"}, {"name":"-ncfrun","url":"nceu-funrun.js"}, {"name":"*ncfrun","url":"nceu-funrun-icon.js","evaluate":true} - ], - "sortorder" : -1 + ] }, { "id": "nceuwid", "name": "NCEU Logo Widget", @@ -430,8 +426,7 @@ "storage": [ {"name":"+nceuwid","url":"nceu-widget.json"}, {"name":"=nceuwid","url":"nceu-widget.js"} - ], - "sortorder" : -1 + ] }, @@ -734,8 +729,7 @@ {"name":"+ctrclk","url":"app.json"}, {"name":"-ctrclk","url":"app.js"}, {"name":"*ctrclk","url":"app-icon.js","evaluate":true} - ], - "sortorder" : -9 + ] }, { "id": "demoapp", "name": "Demo Loop", @@ -749,7 +743,6 @@ {"name":"+demoapp","url":"app.json"}, {"name":"-demoapp","url":"app.js"}, {"name":"*demoapp","url":"app-icon.js","evaluate":true} - ], - "sortorder" : -9 + ] } ] diff --git a/apps/hidkbd/hid-keyboard.min.js b/apps/hidkbd/hid-keyboard.min.js deleted file mode 100644 index 5110be4d3..000000000 --- a/apps/hidkbd/hid-keyboard.min.js +++ /dev/null @@ -1 +0,0 @@ -function drawApp(){function b(a){return{width:8,height:a.length,bpp:1,buffer:new Uint8Array(a).buffer};}g.clear(),g.setFont('6x8',2),g.setFontAlign(0,0),g.drawString(profile,120,120);const a=g.getWidth()-18;g.drawImage(b([16,56,124,254,16,16,16,16]),a,40),g.drawImage(b([16,16,16,16,254,124,56,16]),a,194),g.drawImage(b([0,8,12,14,255,14,12,8]),a,116);}var storage=require('Storage');const settings=storage.readJSON('@setting')||{HID:!1};var sendHid,next,prev,toggle,up,down,profile;settings.HID?(profile='Keyboard',sendHid=function(b,a){try{NRF.sendHIDReport([2,0,0,b,0,0,0,0,0],()=>NRF.sendHIDReport([2,0,0,0,0,0,0,0,0],()=>a&&a();););}catch(a){print(a);}},next=function(a){sendHid(79,a);},prev=function(a){sendHid(80,a);},toggle=function(a){sendHid(44,a);},up=function(a){sendHid(82,a);},down=function(a){sendHid(81,a);}):(E.showMessage('HID disabled'),setTimeout(load,1000)),next&&(settings.HIDGestures&&Bangle.on('aiGesture',v=>switch(v){case'swipeleft':E.showMessage('next');setTimeout(drawApp,1000);next(()=>;);break;case'swiperight':E.showMessage('prev');setTimeout(drawApp,1000);prev(()=>;);break;}),setWatch(function(b){var a=b.time-b.lastTime;a>0.3&&a<0.9?(E.showMessage('prev'),setTimeout(drawApp,1000),prev(()=>;)):(E.showMessage('up'),setTimeout(drawApp,1000),up(()=>;));},BTN1,{edge:'falling',repeat:!0,debounce:50}),setWatch(function(b){var a=b.time-b.lastTime;a>0.3&&a<0.9?(E.showMessage('next'),setTimeout(drawApp,1000),next(()=>;)):(E.showMessage('down'),setTimeout(drawApp,1000),down(()=>;));},BTN3,{edge:'falling',repeat:!0,debounce:50}),setWatch(function(a){E.showMessage('toggle'),setTimeout(drawApp,1000),toggle();},BTN2,{edge:'falling',repeat:!0,debounce:50}),drawApp()); \ No newline at end of file From 527ff57e997a9b27625c33b37b6858afbe601396 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 4 Feb 2020 16:17:23 +0000 Subject: [PATCH 003/407] Simplify settings and add locale submenu, add 12/24 hour option (fix #76), add distance format option, and ensure settings all apply at boot by moving them to the bootloader 'app' (fix #89) --- apps/boot/boot0.js | 19 +++++++ apps/hidkbd/hid-keyboard.js | 31 ++++++------ apps/setting/settings-default.json | 19 +++---- apps/setting/settings-init.js | 27 ---------- apps/setting/settings.js | 80 ++++++++++++++---------------- 5 files changed, 80 insertions(+), 96 deletions(-) delete mode 100644 apps/setting/settings-init.js diff --git a/apps/boot/boot0.js b/apps/boot/boot0.js index 9f34f1b15..dbee62d9c 100644 --- a/apps/boot/boot0.js +++ b/apps/boot/boot0.js @@ -1,5 +1,24 @@ // This ALWAYS runs at boot E.setFlags({pretokenise:1}); +// Load settings... +var s = require('Storage').readJSON('@setting')||{}; +if (s.ble!==false) { + if (s.HID) { // Humen interface device + Bangle.HID = E.toUint8Array(atob("BQEJBqEBhQIFBxngKecVACUBdQGVCIEClQF1CIEBlQV1AQUIGQEpBZEClQF1A5EBlQZ1CBUAJXMFBxkAKXOBAAkFFQAm/wB1CJUCsQLABQwJAaEBhQEVACUBdQGVAQm1gQIJtoECCbeBAgm4gQIJzYECCeKBAgnpgQIJ6oECwA==")); + NRF.setServices({}, {uart:true, hid:Bangle.HID}); + } +} +// If not programmable, force terminal onto screen +if (s.dev===false) Terminal.setConsole(true); +// we just reset, so BLE should be on +if (s.ble===false) NRF.sleep(); +// Set time, vibrate, beep, etc +if (!s.vibrate) Bangle.buzz=Promise.resolve; +if (!s.beep) Bangle.beep=Promise.resolve; +Bangle.setLCDTimeout(s.timeout); +if (!s.timeout) Bangle.setLCDPower(1); +E.setTimeZone(s.timezone); +delete s; // All of this is just shim for older Bangles if (!Bangle.loadWidgets) { Bangle.loadWidgets = function(){ diff --git a/apps/hidkbd/hid-keyboard.js b/apps/hidkbd/hid-keyboard.js index 1e734edc5..01f5090f0 100644 --- a/apps/hidkbd/hid-keyboard.js +++ b/apps/hidkbd/hid-keyboard.js @@ -49,23 +49,20 @@ function drawApp() { } if (next) { - - if (settings.HIDGestures) { - Bangle.on('aiGesture', (v) => { - switch (v) { - case 'swipeleft': - E.showMessage('next'); - setTimeout(drawApp, 1000); - next(() => {}); - break; - case 'swiperight': - E.showMessage('prev'); - setTimeout(drawApp, 1000); - prev(() => {}); - break; - } - }); - } + Bangle.on('aiGesture', (v) => { + switch (v) { + case 'swipeleft': + E.showMessage('next'); + setTimeout(drawApp, 1000); + next(() => {}); + break; + case 'swiperight': + E.showMessage('prev'); + setTimeout(drawApp, 1000); + prev(() => {}); + break; + } + }); setWatch(function(e) { var len = e.time - e.lastTime; diff --git a/apps/setting/settings-default.json b/apps/setting/settings-default.json index 860fe200b..50d223a69 100644 --- a/apps/setting/settings-default.json +++ b/apps/setting/settings-default.json @@ -1,11 +1,12 @@ { - ble: true, // Bluetooth enabled by default - dev: true, // Espruino IDE enabled by default - timeout: 10, // Default LCD timeout in seconds - vibrate: true, // Vibration enabled by default. App must support - beep: true, // Beep enabled by default. App must support - timezone: 0, // Set the timezone for the device - HID : false, // BLE HID mode, off by default - HIDGestures: false, - debug: false, // Debug mode disabled by default. App must support + ble: true, // Bluetooth enabled by default + dev: true, // Is REPL on Bluetooth - can Espruino IDE be used? + timeout: 10, // Default LCD timeout in seconds + vibrate: true, // Vibration enabled by default. App must support + beep: true, // Beep enabled by default. App must support + timezone: 0, // Set the timezone for the device + HID : false, // BLE HID mode, off by default + clock: null, // a string for the default clock's name + "12hour" : false, // 12 or 24 hour clock? + distance : "kilometer" // or "mile" } diff --git a/apps/setting/settings-init.js b/apps/setting/settings-init.js deleted file mode 100644 index 69854ad74..000000000 --- a/apps/setting/settings-init.js +++ /dev/null @@ -1,27 +0,0 @@ -Bangle.HID = E.toUint8Array(atob("BQEJBqEBhQIFBxngKecVACUBdQGVCIEClQF1CIEBlQV1AQUIGQEpBZEClQF1A5EBlQZ1CBUAJXMFBxkAKXOBAAkFFQAm/wB1CJUCsQLABQwJAaEBhQEVACUBdQGVAQm1gQIJtoECCbeBAgm4gQIJzYECCeKBAgnpgQIJ6oECwA==")); - -(function() { - var s = require('Storage').readJSON('@setting'); - var adv = { uart: true }; - if (s.ble) { - if (s.dev) - Bluetooth.setConsole(true); - else - Terminal.setConsole(true); - if (s.HID) { - adv.hid = Bangle.HID; - } else - delete Bangle.HID; - } - NRF.setServices({}, adv); - // we just reset, so BLE should be on - try { // disable advertising if BLE should be off - if (!s.ble) NRF.sleep(); - else NRF.wake(); - } catch(e) {} - if (!s.vibrate) Bangle.buzz=Promise.resolve; - if (!s.beep) Bangle.beep=Promise.resolve; - Bangle.setLCDTimeout(s.timeout); - if (!s.timeout) Bangle.setLCDPower(1); - E.setTimeZone(s.timezone); -})() diff --git a/apps/setting/settings.js b/apps/setting/settings.js index 4b71b1c3d..a6a009806 100644 --- a/apps/setting/settings.js +++ b/apps/setting/settings.js @@ -5,13 +5,7 @@ g.clear(); const storage = require('Storage'); let settings; -function debug(msg, arg) { - if (settings.debug) - console.log(msg, arg); -} - function updateSettings() { - debug('updating settings', settings); //storage.erase('@setting'); // - not needed, just causes extra writes if settings were the same storage.write('@setting', settings); } @@ -25,9 +19,9 @@ function resetSettings() { beep: true, timezone: 0, HID : false, - HIDGestures: false, - debug: false, - clock: null + clock: null, + "12hour" : false, + distance : "kilometer" // or "mile" }; setLCDTimeout(settings.timeout); updateSettings(); @@ -43,6 +37,7 @@ const boolFormat = (v) => v ? "On" : "Off"; function showMainMenu() { const mainmenu = { '': { 'title': 'Settings' }, + 'Make Connectable': makeConnectable, 'BLE': { value: settings.ble, format: boolFormat, @@ -93,7 +88,28 @@ function showMainMenu() { } } }, + 'Locale': showLocaleMenu, 'Select Clock': showClockMenu, + 'HID': { + value: settings.HID, + format: boolFormat, + onchange: () => { + settings.HID = !settings.HID; + updateSettings(); + } + }, + 'Set Time': showSetTimeMenu, + 'Reset Settings': showResetMenu, + 'Turn Off': Bangle.off, + '< Back': ()=> {load();} + }; + return E.showMenu(mainmenu); +} + +function showLocaleMenu() { + const localemenu = { + '': { 'title': 'Locale' }, + '< Back': showMainMenu, 'Time Zone': { value: settings.timezone, min: -11, @@ -104,37 +120,24 @@ function showMainMenu() { updateSettings(); } }, - 'HID': { - value: settings.HID, - format: boolFormat, - onchange: () => { - settings.HID = !settings.HID; + 'Clock Style': { + value: !!settings["12hour"], + format : v => v?"12hr":"24hr", + onchange: v => { + settings["12hour"] = v; updateSettings(); } }, - 'HID Gestures': { - value: settings.HIDGestures, - format: boolFormat, - onchange: () => { - settings.HIDGestures = !settings.HIDGestures; + 'Distance/Speed': { + value: settings.distanceunit=="mile", + format: v => v?"mile":"kilometer", + onchange: v => { + settings.distanceunit = v?"mile":"kilometer"; updateSettings(); } }, - 'Debug': { - value: settings.debug, - format: boolFormat, - onchange: () => { - settings.debug = !settings.debug; - updateSettings(); - } - }, - 'Set Time': showSetTimeMenu, - 'Make Connectable': makeConnectable, - 'Reset Settings': showResetMenu, - 'Turn Off': Bangle.off, - '< Back': ()=> {load();} }; - return E.showMenu(mainmenu); + return E.showMenu(localemenu); } function showResetMenu() { @@ -149,16 +152,7 @@ function showResetMenu() { } setTimeout(showMainMenu, 50); }); - }, - // this is include for debugging. remove for production - /*'Erase': () => { - storage.erase('=setting'); - storage.erase('-setting'); - storage.erase('@setting'); - storage.erase('*setting'); - storage.erase('+setting'); - E.reboot(); - }*/ + } }; return E.showMenu(resetmenu); } From c2234d39e591a1ee7decdf5065c55576114d0a1a Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 4 Feb 2020 16:19:29 +0000 Subject: [PATCH 004/407] Add newlines to stop app names overflowing - fix #87 --- apps/files/files.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/files/files.js b/apps/files/files.js index b5b77a7f8..9f3f7b46f 100644 --- a/apps/files/files.js +++ b/apps/files/files.js @@ -31,7 +31,7 @@ function showMainMenu() { } function eraseApp(app) { - E.showMessage('Erasing ' + app.name + '...'); + E.showMessage('Erasing\n' + app.name + '...'); storage.erase(app['']); storage.erase(app.icon); storage.erase(app.src); @@ -44,7 +44,7 @@ function showAppMenu(app) { }, '< Back': () => m = showApps(), 'Erase': () => { - E.showPrompt('Erase ' + app.name + '?').then((v) => { + E.showPrompt('Erase\n' + app.name + '?').then((v) => { if (v) { Bangle.buzz(100, 1); eraseApp(app); From a6f23a5cc58e9aa815e9b2caabc8481a0e6b09c7 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 4 Feb 2020 16:30:31 +0000 Subject: [PATCH 005/407] UI tweaks for uploader --- index.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/index.js b/index.js index fa986ac21..242d60b2d 100644 --- a/index.js +++ b/index.js @@ -422,36 +422,37 @@ document.getElementById("settime").addEventListener("click",event=>{ }); document.getElementById("removeall").addEventListener("click",event=>{ showPrompt("Remove All","Really remove all apps?").then(() => { - Comms.removeAllApps().then(()=>{ - appsInstalled = []; - showToast("All apps removed","success"); - refreshMyApps(); - refreshLibrary(); - }, err=>{ - showToast("App removal failed, "+err,"error"); - }); + return Comms.removeAllApps(); + }).then(()=>{ + appsInstalled = []; + showToast("All apps removed","success"); + return getInstalledApps(); + }).catch(err=>{ + showToast("App removal failed, "+err,"error"); }); }); // Install all default apps in one go document.getElementById("installdefault").addEventListener("click",event=>{ - var defaultApps + var defaultApps, appCount; httpGet("defaultapps.json").then(json=>{ defaultApps = JSON.parse(json); defaultApps = defaultApps.map( appid => appJSON.find(app=>app.id==appid) ); if (defaultApps.some(x=>x===undefined)) throw "Not all apps found"; + appCount = defaultApps.length; return showPrompt("Install Defaults","Remove everything and install default apps?"); }).then(() => { return Comms.removeAllApps(); }).then(()=>{ - showToast("Existing apps removed","success"); + appsInstalled = []; + showToast(`Existing apps removed. Installing ${appCount} apps...`); return new Promise(resolve => { function upload() { var app = defaultApps.shift(); if (app===undefined) return resolve(); Comms.uploadApp(app).then((appJSON) => { if (appJSON) appsInstalled.push(appJSON); - showToast(app.name+" Uploaded", "success"); + showToast(`(${appCount-defaultApps.length}/${appCount}) ${app.name} Uploaded`); upload(); }); } @@ -459,8 +460,7 @@ document.getElementById("installdefault").addEventListener("click",event=>{ }); }).then(()=>{ showToast("Default apps successfully installed!","success"); - refreshMyApps(); - refreshLibrary(); + return getInstalledApps(); }).catch(err=>{ showToast("App Install failed, "+err,"error"); }); From 925207e491da0ad87c38939c535398f2af99f583 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Wed, 5 Feb 2020 10:27:24 +0000 Subject: [PATCH 006/407] Attempt at swapping scenes more cleanly --- apps/demoapp/app.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/demoapp/app.js b/apps/demoapp/app.js index b84839b45..cb3136196 100644 --- a/apps/demoapp/app.js +++ b/apps/demoapp/app.js @@ -9,6 +9,7 @@ var scenes = [ y+=step; if (y>70) { clearInterval(i); + i = undefined; } g.clearRect(0,y-(step+1),240,y-1); @@ -159,10 +160,13 @@ var scenes = [ ]; var sceneNo = scenes.length-1; -var stop = scenes[sceneNo](); -setInterval(function() { +var stop; +function next() { sceneNo++; if (sceneNo>=scenes.length) sceneNo=0; - stop(); + if (stop) stop(); + clearInterval(); stop = scenes[sceneNo](); -}, 10000); + setTimeout(next, 10000); +} +next() From a180f98991c928f19dda7a732cedf00f3f905bec Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Wed, 5 Feb 2020 10:28:23 +0000 Subject: [PATCH 007/407] Simple example to control an Espruino device by setting a characteristic on it --- apps.json | 12 +++++++++ apps/flagrse/app-icon.js | 1 + apps/flagrse/app.js | 57 +++++++++++++++++++++++++++++++++++++++ apps/flagrse/app.json | 5 ++++ apps/flagrse/app.png | Bin 0 -> 326 bytes 5 files changed, 75 insertions(+) create mode 100644 apps/flagrse/app-icon.js create mode 100644 apps/flagrse/app.js create mode 100644 apps/flagrse/app.json create mode 100644 apps/flagrse/app.png diff --git a/apps.json b/apps.json index 76151a627..6c805e9e2 100644 --- a/apps.json +++ b/apps.json @@ -751,5 +751,17 @@ {"name":"*demoapp","url":"app-icon.js","evaluate":true} ], "sortorder" : -9 + }, + { "id": "flagrse", + "name": "Espruino Flag Raiser", + "icon": "app.png", + "description": "App to send a command to an Espruino-based bluetooth Flag", + "tags": "", + "storage": [ + {"name":"+flagrse","url":"app.json"}, + {"name":"-flagrse","url":"app.js"}, + {"name":"*flagrse","url":"app-icon.js","evaluate":true} + ] } + ] diff --git a/apps/flagrse/app-icon.js b/apps/flagrse/app-icon.js new file mode 100644 index 000000000..68d95b518 --- /dev/null +++ b/apps/flagrse/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwxH+AH4A/AH4AJmUyF/4Adstl1ovvAAIvvGNQvGGNAvIGMwvKGMgvMGMQvOGMAvQGLwvSGLgvUGLQvWGLAvYGKwv/R+zvtFrAvUFrQvSFrgvQFrwvOFsAvMFsQvKFsgvIFswvGFtAvEFtQABmUyF1gv/F/4v/F/4v/F/4v/F/4A/AH4A/AAwA=")) diff --git a/apps/flagrse/app.js b/apps/flagrse/app.js new file mode 100644 index 000000000..e7e8e2445 --- /dev/null +++ b/apps/flagrse/app.js @@ -0,0 +1,57 @@ + +function redraw() { + var img = require("heatshrink").decompress(atob("sFgxH+AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/ACcyAARD/L/5f/If5f/AHdlAAWtIn5ffAAJG/L75h/L8Jh/L8Jh/L8Jh/L8Jh/L8Jh/L8Jh/L8Jh/L8Jh/L8Jh/L8Jh/L8Jh/L8Jh/L8Jh+L8Rh8L8hh6L8xh4L9Bh2L9Rh0L9hhyL9xhwL+BhuL+RhsL+hhqL/5f/Lvpf0LtRfyLthfwLtxfuLuBfsLuRfqLuhfoLuxfmLvBfkLvRfiLvhfgLvxfeLn5fdLX5fdLH5fdK35fdKn5fdKX5fdKH5fdJ35fdJn5fdJX5fdJH5fdI35fdIn4AamQACIf5f/L/5D/L/5f/If5f/L/5D/L/5f/If5f/L/5D/L/5f/If5f/L/5D/L/5f/If5f/L/5D/L/5f/If5f/L/5D/L/4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4AnA")); + g.clear(); + g.drawImage(img, 120-96, 120-96, {scale:2}); +} + + // Code for button (Puck.js) + var busy = false; + +var lastTry = getTime(); + +function flag() { + E.showMessage("Working..."); + if (busy && lastTry+5 zv*bw_jT8?RvM*4YGhISS=_A+5E)ToKT1rx-4k0d^Cv@~o(sBktq$;q#;#*+T=q>y^MFyf0!swDk}u;#Pd{ge5|*hV0W;^SvV^qP z`7xf@*5u5vD=kydV59sh!5PI1PG^dze>0i%BQJzQ+~F}(MhrtQ_ku#k Date: Wed, 5 Feb 2020 10:52:51 +0000 Subject: [PATCH 008/407] Try to get time from GPS if we rebooted and powered on without any proper time - fix #91 --- apps/boot/boot0.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/apps/boot/boot0.js b/apps/boot/boot0.js index dbee62d9c..fb6fa128a 100644 --- a/apps/boot/boot0.js +++ b/apps/boot/boot0.js @@ -19,6 +19,22 @@ Bangle.setLCDTimeout(s.timeout); if (!s.timeout) Bangle.setLCDPower(1); E.setTimeZone(s.timezone); delete s; +// check to see if our clock is wrong - if it is use GPS time +if ((new Date()).getFullYear()==1970) { + console.log("Searching for GPS time"); + Bangle.on('GPS',function cb(g) { + Bangle.setGPSPower(0); + Bangle.removeListener("GPS",cb); + if (!g.time || (g.time.getFullYear()<2000) || + (g.time.getFullYear()==2250)) { + console.log("GPS receiver's time not set"); + return; + } + setTime(g.time.getTime()/1000); + console.log("GPS time",g.time.toString()); + }); + Bangle.setGPSPower(1); +} // All of this is just shim for older Bangles if (!Bangle.loadWidgets) { Bangle.loadWidgets = function(){ From 8b8147a543798b449ec48a484a892c67f8e2a9be Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Wed, 5 Feb 2020 15:45:41 +0000 Subject: [PATCH 009/407] docs --- apps.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps.json b/apps.json index 3ba73b8ff..37cf6d387 100644 --- a/apps.json +++ b/apps.json @@ -56,7 +56,7 @@ "name": "Settings", "icon": "settings.png", "version":"0.02", - "description": "A menu for setting up Bangle.js - by default this disables Bluetooth unless you enable 'BLE' AND 'Dev'", + "description": "A menu for setting up Bangle.js", "tags": "tool,system", "storage": [ {"name":"+setting","url":"settings.json"}, From e33202122bc162ad871c02436d4bbbd52e0cf790 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 7 Feb 2020 13:44:55 +0000 Subject: [PATCH 010/407] Store number of steps when unloading from memory (fix #84) --- apps.json | 2 +- apps/wpedom/ChangeLog | 1 + apps/wpedom/pedometer_widget.js | 97 +++++++++++++++++++-------------- 3 files changed, 58 insertions(+), 42 deletions(-) create mode 100644 apps/wpedom/ChangeLog diff --git a/apps.json b/apps.json index 37cf6d387..f9e1d48ce 100644 --- a/apps.json +++ b/apps.json @@ -694,7 +694,7 @@ { "id": "wpedom", "name": "Pedometer widget", "icon": "pedometer_widget.png", - "version":"0.01", + "version":"0.02", "description": "Daily pedometer widget", "tags": "widget", "type":"widget", diff --git a/apps/wpedom/ChangeLog b/apps/wpedom/ChangeLog new file mode 100644 index 000000000..6e6e016a2 --- /dev/null +++ b/apps/wpedom/ChangeLog @@ -0,0 +1 @@ +0.02: Save number of steps when unloading from memory diff --git a/apps/wpedom/pedometer_widget.js b/apps/wpedom/pedometer_widget.js index 5eaae41fb..1a45ddd31 100644 --- a/apps/wpedom/pedometer_widget.js +++ b/apps/wpedom/pedometer_widget.js @@ -1,50 +1,65 @@ (() => { + const PEDOMFILE = "@wpedom"; + // add the width + // WIDGETPOS.tr is originally 208 without any widgets + var xpos = WIDGETPOS.tr; // draw string with right alignment + var width = 48; + WIDGETPOS.tr -= (width + 2); - // add the width - // WIDGETPOS.tr is originally 208 without any widgets - var xpos = WIDGETPOS.tr; // draw string with right alignment - var width = 48; - WIDGETPOS.tr -= (width + 2); + let lastUpdate = new Date(); + let stp_today = 0; - let lastUpdate = new Date(); - let stp_today = 0; + function erase() { + g.clearRect(xpos-width, 0, xpos, 23); + } - function erase() { - g.clearRect(xpos-width, 0, xpos, 23); + // draw your widget at xpos + function draw() { + // Widget (0,0,239,23) + if (stp_today > 99999){ + stp_today = stp_today % 100000; // cap to five digits + comma = 6 characters + erase(); } - - // draw your widget at xpos - function draw() { - // Widget (0,0,239,23) - if (stp_today > 99999){ - stp_today = stp_today % 100000; // cap to five digits + comma = 6 characters - erase(); - } - let stps = stp_today.toString(); - if (stps.length > 3){ - stps = stps.slice(0,-3) + "," + stps.slice(-3); - } - g.setColor(1,1,1); - g.setFont("4x6", 2); - g.setFontAlign(1, 0); // align to x: right, y: center - g.drawString(stps, xpos, 11, true); // 6 * 4*2 = 48 - g.flip(); + let stps = stp_today.toString(); + if (stps.length > 3){ + stps = stps.slice(0,-3) + "," + stps.slice(-3); } - - Bangle.on('step', (up) => { - let date = new Date(); - if (lastUpdate.getDate() == date.getDate()){ - stp_today += 1; - } else { - stp_today = 1; - erase(); - } - lastUpdate = date; - //console.log("up: " + up + " stp: " + stp_today + " " + date.toString()); - draw(); - }); + g.setColor(1,1,1); + g.setFont("4x6", 2); + g.setFontAlign(1, 0); // align to x: right, y: center + g.drawString(stps, xpos, 11, true); // 6 * 4*2 = 48 + g.flip(); + } - // add your widget - WIDGETS["wpedom"]={draw:draw}; + Bangle.on('step', (up) => { + let date = new Date(); + if (lastUpdate.getDate() == date.getDate()){ + stp_today += 1; + } else { + stp_today = 1; + erase(); + } + lastUpdate = date; + //console.log("up: " + up + " stp: " + stp_today + " " + date.toString()); + draw(); + }); + // When unloading, save state + E.on('kill', () => { + let d = { + lastUpdate : lastUpdate.toString(), + stepsToday : stp_today + }; + require("Storage").write(PEDOMFILE,d); + }); + + // add your widget + WIDGETS["wpedom"]={draw:draw}; + // Load data at startup + let pedomData = require("Storage").readJSON(PEDOMFILE); + if (pedomData) { + if (pedomData.lastUpdate) + lastUpdate = new Date(pedomData.lastUpdate); + stp_today = pedomData.stepsToday|0; + } })() From 9a8a887736cda72af0c3740c8913df7e82e80d03 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 7 Feb 2020 13:45:22 +0000 Subject: [PATCH 011/407] Add changelog to example --- apps/_example_app/ChangeLog | 1 + apps/_example_widget/ChangeLog | 1 + 2 files changed, 2 insertions(+) create mode 100644 apps/_example_app/ChangeLog create mode 100644 apps/_example_widget/ChangeLog diff --git a/apps/_example_app/ChangeLog b/apps/_example_app/ChangeLog new file mode 100644 index 000000000..efbc53fb0 --- /dev/null +++ b/apps/_example_app/ChangeLog @@ -0,0 +1 @@ +0.00: New App! diff --git a/apps/_example_widget/ChangeLog b/apps/_example_widget/ChangeLog new file mode 100644 index 000000000..7bfdc9d75 --- /dev/null +++ b/apps/_example_widget/ChangeLog @@ -0,0 +1 @@ +0.00: New Widget! From f91ce5b2197c70f93f65de2f84433a5f4796ba63 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 7 Feb 2020 13:47:59 +0000 Subject: [PATCH 012/407] docs --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0f453e0bb..cd6edbd47 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ easily distinguish between file types, we use the following: * `*stuff` is an image * `-stuff` is JS code * `=stuff` is JS code for stuff that is run at boot time - eg. handling settings or creating widgets on the clock screen +* `@stuff` is used for JSON settings for an app ## Developing your own app @@ -215,6 +216,8 @@ about the app. ## Coding hints +- Need to save state? Use the `E.on('kill',...)` event to save JSON to a file called `@7chname`, then load it at startup. + - use `g.setFont(.., size)` to multiply the font size, eg ("6x8",3) : "18x24" - use `g.drawString(text,x,y,true)` to draw with background color to overwrite existing text @@ -227,7 +230,7 @@ about the app. - using `g.setLCDBrightness()` can save you power during long periods with lcd on -- chaining graphics methodes, eg `g.setColor(0xFD20).setFontAlign(0,0).setfont("6x8",3)` +- chaining graphics methods, eg `g.setColor(0xFD20).setFontAlign(0,0).setfont("6x8",3)` ### Graphic areas @@ -286,7 +289,7 @@ You can use `g.setColor(r,g,b)` OR `g.setColor(16bitnumber)` - some common 16 bi The [`testing`](testing) folder contains snippets of code that might be useful for your apps. * `testing/colors.js` - 16 bit colors as name value pairs -* `testing/gpstrack.js` - code to store a GPS track in Bagle.js storage and output it back to the console +* `testing/gpstrack.js` - code to store a GPS track in Bangle.js storage and output it back to the console * `testing/map` - code for splitting an image into map tiles and then displaying them ## Credits From d8e5af43be2fbc77a1b41f9cbbe7be74d3af75e5 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 7 Feb 2020 14:02:00 +0000 Subject: [PATCH 013/407] Remove 12h clock, change original 24h clock to read settings and present time accordingly (fix #82) --- apps.json | 20 ++------- apps/sclock/ChangeLog | 1 + apps/sclock/clock-simple.js | 19 ++++++++ apps/sclock/clock-simple.json | 2 +- apps/stclck/ChangeLog | 2 - apps/stclck/clock-simple-icon.js | 1 - apps/stclck/clock-simple.js | 72 ------------------------------- apps/stclck/clock-simple.json | 7 --- apps/stclck/clock-simple.png | Bin 590 -> 0 bytes 9 files changed, 24 insertions(+), 100 deletions(-) delete mode 100644 apps/stclck/ChangeLog delete mode 100644 apps/stclck/clock-simple-icon.js delete mode 100644 apps/stclck/clock-simple.js delete mode 100644 apps/stclck/clock-simple.json delete mode 100755 apps/stclck/clock-simple.png diff --git a/apps.json b/apps.json index a47451b87..dedbb22a1 100644 --- a/apps.json +++ b/apps.json @@ -432,10 +432,10 @@ { "id": "sclock", - "name": "Simple 24hr Clock", + "name": "Simple Clock", "icon": "clock-simple.png", - "version":"0.03", - "description": "Simple Digital 24 Hour Clock", + "version":"0.04", + "description": "A Simple Digital Clock", "tags": "clock", "type":"clock", "allow_emulator":true, @@ -445,20 +445,6 @@ {"name":"*sclock","url":"clock-simple-icon.js","evaluate":true} ] }, - { "id": "stclck", - "name": "Simple 12hr Clock", - "icon": "clock-simple.png", - "version":"0.03", - "description": "Simple Digital 12 Hour Clock", - "tags": "clock", - "type":"clock", - "allow_emulator":true, - "storage": [ - {"name":"+stclck","url":"clock-simple.json"}, - {"name":"-stclck","url":"clock-simple.js"}, - {"name":"*stclck","url":"clock-simple-icon.js","evaluate":true} - ] - }, { "id": "gesture", "name": "Gesture Test", "icon": "gesture.png", diff --git a/apps/sclock/ChangeLog b/apps/sclock/ChangeLog index 91fa53402..afe0c99cb 100644 --- a/apps/sclock/ChangeLog +++ b/apps/sclock/ChangeLog @@ -1,2 +1,3 @@ 0.02: Modified for use with new bootloader and firmware 0.03: Actually make into 24h clock since there's a 12h variant +0.04: Make this clock do 12h and 24h diff --git a/apps/sclock/clock-simple.js b/apps/sclock/clock-simple.js index f20080328..6fad41f17 100644 --- a/apps/sclock/clock-simple.js +++ b/apps/sclock/clock-simple.js @@ -10,6 +10,9 @@ const yposDate = 130; const yposYear = 175; const yposGMT = 220; +// Check settings for what type our clock should be +var is12Hour = (require("Storage").readJSON("@setting")||{})["12hour"]; + function drawSimpleClock() { // get date var d = new Date(); @@ -22,8 +25,24 @@ function drawSimpleClock() { var time = da[4].substr(0, 5).split(":"); var hours = time[0], minutes = time[1]; + var meridian = ""; + if (is12Hour) { + hours = parseInt(hours,10); + meridian = "AM"; + if (hours == 0) { + hours = 12; + meridian = "AM"; + } else if (hours >= 12) { + meridian = "PM"; + if (hours>12) hours -= 12; + } + hours = (" "+hours).substr(-2); + } + g.setFont(font, timeFontSize); g.drawString(`${hours}:${minutes}`, xyCenter, yposTime, true); + g.setFont(font, gmtFontSize); + g.drawString(meridian, xyCenter + 102, yposTime + 10, true); // draw Day, name of month, Date var date = [da[0], da[1], da[2]].join(" "); diff --git a/apps/sclock/clock-simple.json b/apps/sclock/clock-simple.json index 9a0a040aa..66cf827ba 100644 --- a/apps/sclock/clock-simple.json +++ b/apps/sclock/clock-simple.json @@ -1,5 +1,5 @@ { - "name":"24h Clock", + "name":"Simple Clock", "type":"clock", "icon":"*sclock", "src":"-sclock", diff --git a/apps/stclck/ChangeLog b/apps/stclck/ChangeLog deleted file mode 100644 index afa047eda..000000000 --- a/apps/stclck/ChangeLog +++ /dev/null @@ -1,2 +0,0 @@ -0.02: Modified for use with new bootloader and firmware -0.03: Actually made this work diff --git a/apps/stclck/clock-simple-icon.js b/apps/stclck/clock-simple-icon.js deleted file mode 100644 index b41bd6fcc..000000000 --- a/apps/stclck/clock-simple-icon.js +++ /dev/null @@ -1 +0,0 @@ -require("heatshrink").decompress(atob("mEwxH+AH4A/AH4ATiwAGFdYzlFp4xeFyYwZD49kxGt2fX6+z1uIsgxcDQtAxArCAA+zxFAGDAYFxAsJAAuIGCxcF1ouPAAOsGCouERRSUKSYguoGARgRCIiMSAAutGCDqUABNkF5yNEFzKRQLzwABxAvRdgYFBDgYFFBphgEF5lkEJwNOYIaORF7KQMBYetEJoDHAo+sF56+DF7TAMBYaBQBpwv/R97vvxCPdxAvLGAdkF7tkFxbAIF7C+MSBQAXRxovEoAvboAvNMD69DFxYvEi2sFy+sDwgvLGAryDACTsEFxrCGGCmzXh5gJSSaMFF6AwGshiPdQguSGA8WxAxK2eIRYguUGBBjBxGsGYWz1mILYwuWGJQANFq4wWFzQxSFrozNFcYA/AH4Av")) diff --git a/apps/stclck/clock-simple.js b/apps/stclck/clock-simple.js deleted file mode 100644 index 8eed2cfed..000000000 --- a/apps/stclck/clock-simple.js +++ /dev/null @@ -1,72 +0,0 @@ -/* jshint esversion: 6 */ -const timeFontSize = 6; -const dateFontSize = 3; -const gmtFontSize = 2; -const font = "6x8"; - -const xyCenter = g.getWidth() / 2; -const yposTime = 75; -const yposDate = 130; -const yposYear = 175; -const yposGMT = 220; - -function drawSimpleClock() { - // get date - var d = new Date(); - var da = d.toString().split(" "); - - // drawSting centered - g.setFontAlign(0, 0); - - // draw time - var time = da[4].substr(0, 5).split(":"); - var hours = parseInt(time[0],10), - minutes = time[1]; - var meridian = "AM"; - if (hours == 0) { - hours = 12; - meridian = "PM"; - } - if (hours > 12) { - hours -= 12; - meridian = "PM"; - } - g.setFont(font, timeFontSize); - g.drawString(`${(" "+hours).substr(-2)}:${minutes}`, xyCenter, yposTime, true); - g.setFont(font, gmtFontSize); - g.drawString(meridian, xyCenter + 102, yposTime + 10, true); - - // draw Day, name of month, Date - var date = [da[0], da[1], da[2]].join(" "); - g.setFont(font, dateFontSize); - - g.drawString(date, xyCenter, yposDate, true); - - // draw year - g.setFont(font, dateFontSize); - g.drawString(d.getFullYear(), xyCenter, yposYear, true); - - // draw gmt - var gmt = da[5]; - g.setFont(font, gmtFontSize); - g.drawString(gmt, xyCenter, yposGMT, true); -} - -// handle switch display on by pressing BTN1 -Bangle.on('lcdPower', function(on) { - if (on) drawSimpleClock(); -}); - -// clean app screen -g.clear(); -Bangle.loadWidgets(); -Bangle.drawWidgets(); - -// refesh every 15 sec -setInterval(drawSimpleClock, 15E3); - -// draw now -drawSimpleClock(); - -// Show launcher when middle button pressed -setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"}); diff --git a/apps/stclck/clock-simple.json b/apps/stclck/clock-simple.json deleted file mode 100644 index f30e88ea4..000000000 --- a/apps/stclck/clock-simple.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "12h Clock", - "type": "clock", - "icon": "*stclck", - "src": "-stclck", - "sortorder": -10 -} diff --git a/apps/stclck/clock-simple.png b/apps/stclck/clock-simple.png deleted file mode 100755 index 824062aedaded6605ecab000d555a519557ddd50..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 590 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-s`2jv5u0Wb$%|V8>2N~8JVpx5U zVcj8yHTxJ=?PXYX48#RWtv&!EAqdC@i9l^T=394*!|0D^)Q|n3+{bZ=Ml6vn$cx?FmfJu z=|nYGZBG7NyFkWYu9|`GKL@vI-P>F}efgxFEK8y>zXwnKEyp^E(ahAYZiIW>)3mr6OV%r$25F}!S;>jsvYc^>)ZPo1~z znD6MiRJZ4K82{t2){WNhq8oRJukHH%<#y|%xX8>Waa|v8ZV YS?g0SGc5aa6O=GKUHx3vIVCg!0Fhi2)Bpeg From 575954aaaff49686eaf46a6c4110b6517b82144f Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 7 Feb 2020 14:51:31 +0000 Subject: [PATCH 014/407] If app is installed but older, offer 'update' button (fix #79). Fix occasional 'null' error from clicking button rather than icon Tidy up button handling in 'Library' --- index.js | 99 +++++++++++++++++++++++++++++++++----------------------- utils.js | 21 ++++++++++++ 2 files changed, 80 insertions(+), 40 deletions(-) diff --git a/index.js b/index.js index 242d60b2d..cb9352331 100644 --- a/index.js +++ b/index.js @@ -149,19 +149,10 @@ function refreshLibrary() { } panelbody.innerHTML = visibleApps.map((app,idx) => { - var icon = "icon-upload"; - var versionInfo = app.version || ""; - if (app.custom) - icon = "icon-menu"; - if (appsInstalled.find(a=>a.id==app.id)) { - icon = "icon-delete"; - versionInfo+=" installed"; - } - var buttons = ""; + var appInstalled = appsInstalled.find(a=>a.id==app.id); + var version = getVersionInfo(app, appInstalled); + var versionInfo = version.text; if (versionInfo) versionInfo = " ("+versionInfo+")"; - if (app.allow_emulator) - buttons += ``; - buttons += ``; return `
${escapeHtml(app.name)}
@@ -171,7 +162,11 @@ function refreshLibrary() {

${escapeHtml(app.description)}

- ${buttons} + + + + +
`;}).join(""); @@ -181,10 +176,12 @@ function refreshLibrary() { tab.setAttribute("data-badge", appJSON.length); htmlToArray(panelbody.getElementsByTagName("button")).forEach(button => { button.addEventListener("click",event => { - var icon = event.target; - var appid = icon.getAttribute("appid"); - var app = appJSON.find(app=>app.id==appid); - if (!app) return; + var button = event.currentTarget; + var icon = button.firstChild; + var appid = button.getAttribute("appid"); + var app = appNameToApp(appid); + if (!app) throw new Error("App "+appid+" not found"); + // check icon to figure out what we should do if (icon.classList.contains("icon-share")) { // emulator var file = app.storage.find(f=>f.name[0]=='-'); @@ -196,6 +193,7 @@ function refreshLibrary() { var url = baseurl+"apps/"+app.id+"/"+file.url; window.open(`https://espruino.com/ide/emulator.html?codeurl=${url}&upload`); } else if (icon.classList.contains("icon-upload")) { + // upload icon.classList.remove("icon-upload"); icon.classList.add("loading"); Comms.uploadApp(app).then((appJSON) => { @@ -204,12 +202,14 @@ function refreshLibrary() { icon.classList.remove("loading"); icon.classList.add("icon-delete"); refreshMyApps(); + refreshLibrary(); }).catch(err => { showToast("Upload failed, "+err, "error"); icon.classList.remove("loading"); icon.classList.add("icon-upload"); }); } else if (icon.classList.contains("icon-menu")) { + // custom HTML update if (app.custom) { icon.classList.remove("icon-menu"); icon.classList.add("loading"); @@ -219,16 +219,23 @@ function refreshLibrary() { icon.classList.remove("loading"); icon.classList.add("icon-delete"); refreshMyApps(); + refreshLibrary(); }).catch(err => { showToast("Customise failed, "+err, "error"); icon.classList.remove("loading"); icon.classList.add("icon-menu"); }); } - } else { + } else if (icon.classList.contains("icon-delete")) { + // Remove app icon.classList.remove("icon-delete"); icon.classList.add("loading"); removeApp(app); + } else if (icon.classList.contains("icon-refresh")) { + // Update app + icon.classList.remove("icon-refresh"); + icon.classList.add("loading"); + updateApp(app); } }); }); @@ -239,14 +246,29 @@ refreshLibrary(); function removeApp(app) { return showPrompt("Delete","Really remove '"+app.name+"'?").then(() => { - Comms.removeApp(app).then(()=>{ - appsInstalled = appsInstalled.filter(a=>a.id!=app.id); - showToast(app.name+" removed successfully","success"); - refreshMyApps(); - refreshLibrary(); - }, err=>{ - showToast(app.name+" removal failed, "+err,"error"); - }); + return Comms.removeApp(app); + }).then(()=>{ + appsInstalled = appsInstalled.filter(a=>a.id!=app.id); + showToast(app.name+" removed successfully","success"); + refreshMyApps(); + refreshLibrary(); + }, err=>{ + showToast(app.name+" removal failed, "+err,"error"); + }); +} + +function updateApp(app) { + Comms.removeApp(app).then(()=>{ + showToast(app.name+" removed successfully. Updating...",); + appsInstalled = appsInstalled.filter(a=>a.id!=app.id); + return Comms.uploadApp(app); + }).then((appJSON) => { + if (appJSON) appsInstalled.push(appJSON); + showToast(app.name+" Updated!", "success"); + refreshMyApps(); + refreshLibrary(); + }, err=>{ + showToast(app.name+" update failed, "+err,"error"); }); } @@ -281,34 +303,31 @@ function refreshMyApps() { tab.setAttribute("data-badge", appsInstalled.length); panelbody.innerHTML = appsInstalled.map(appJSON => { var app = appNameToApp(appJSON.id); -var version = ""; -if (!appJSON.version) { - version = "Unknown version"; - if (app.version) version += ", latest "+app.version; -} else { - version = appJSON.version; - if (app.version == appJSON.version) version += ", up to date"; - else if (app.version) version += ", latest "+app.version; -} +var version = getVersionInfo(app, appJSON); return `
${escapeHtml(app.name)}
-

${escapeHtml(app.name)} (${version})

+

${escapeHtml(app.name)} (${version.text})

${escapeHtml(app.description)}

- + +
`}).join(""); htmlToArray(panelbody.getElementsByTagName("button")).forEach(button => { button.addEventListener("click",event => { - var icon = event.target; - var appid = icon.getAttribute("appid"); + var button = event.currentTarget; + var icon = button.firstChild; + var appid = button.getAttribute("appid"); var app = appNameToApp(appid); - removeApp(app); + if (!app) throw new Error("App "+appid+" not found"); + // check icon to figure out what we should do + if (icon.classList.contains("icon-delete")) removeApp(app); + if (icon.classList.contains("icon-refresh")) updateApp(app); }); }); } diff --git a/utils.js b/utils.js index 86e8db3e2..699b247a9 100644 --- a/utils.js +++ b/utils.js @@ -42,3 +42,24 @@ function appSorter(a,b) { if (sa>sb) return 1; return (a.name==b.name) ? 0 : ((a.name Date: Fri, 7 Feb 2020 17:09:00 +0000 Subject: [PATCH 015/407] reset draw styles --- apps/sclock/clock-simple.js | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/sclock/clock-simple.js b/apps/sclock/clock-simple.js index 6fad41f17..e298c9d3a 100644 --- a/apps/sclock/clock-simple.js +++ b/apps/sclock/clock-simple.js @@ -18,6 +18,7 @@ function drawSimpleClock() { var d = new Date(); var da = d.toString().split(" "); + g.reset(); // default draw styles // drawSting centered g.setFontAlign(0, 0); From a59ea3eee6a246dd11419b7b0ff99aec6432216c Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 7 Feb 2020 17:09:18 +0000 Subject: [PATCH 016/407] tidy examples --- apps/_example_app/add_to_apps.json | 1 + apps/_example_widget/add_to_apps.json | 1 + apps/_example_widget/widget.js | 18 +++++++++--------- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/apps/_example_app/add_to_apps.json b/apps/_example_app/add_to_apps.json index 6c7172ec6..9043b0058 100644 --- a/apps/_example_app/add_to_apps.json +++ b/apps/_example_app/add_to_apps.json @@ -2,6 +2,7 @@ { "id": "7chname", "name": "My app's human readable name", "icon": "app.png", + "version":"0.01", "description": "A detailed description of my great app", "tags": "", "storage": [ diff --git a/apps/_example_widget/add_to_apps.json b/apps/_example_widget/add_to_apps.json index 705e504fc..8dc1c3cb4 100644 --- a/apps/_example_widget/add_to_apps.json +++ b/apps/_example_widget/add_to_apps.json @@ -2,6 +2,7 @@ { "id": "7chname", "name": "My widget's human readable name", "icon": "widget.png", + "version":"0.01", "description": "A detailed description of my great widget", "tags": "", "storage": [ diff --git a/apps/_example_widget/widget.js b/apps/_example_widget/widget.js index 6818f0502..01f9d1e19 100644 --- a/apps/_example_widget/widget.js +++ b/apps/_example_widget/widget.js @@ -1,16 +1,16 @@ /* run widgets in their own function scope so they don't interfere with currently-running apps */ (() => { - // add the width - var xpos = WIDGETPOS.tr-24;/**/; - WIDGETPOS.tr-= 28;/* the widget width plus some extra pixel to keep distance to others */; + // add the width + var xpos = WIDGETPOS.tr-24;/**/; + WIDGETPOS.tr-= 28;/* the widget width plus some extra pixel to keep distance to others */; - // draw your widget at xpos - function draw() { - // add your code - } + // draw your widget at xpos + function draw() { + // add your code + } - // add your widget - WIDGETS["mywidget"]={draw:draw}; + // add your widget + WIDGETS["mywidget"]={draw:draw}; })() From 2bca5d4747f9d06c79b0ddcaacd0d34565c7d4d6 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 7 Feb 2020 17:16:45 +0000 Subject: [PATCH 017/407] Working GPS track recording - just need to fix the 'interface.html' to do the comms and do something with the data collected --- README.md | 4 ++ apps.json | 21 ++++++- apps/gpsrec/ChangeLog | 1 + apps/gpsrec/app-icon.js | 1 + apps/gpsrec/app-settings.json | 5 ++ apps/gpsrec/app.js | 115 ++++++++++++++++++++++++++++++++++ apps/gpsrec/app.json | 5 ++ apps/gpsrec/app.png | Bin 0 -> 1457 bytes apps/gpsrec/interface.html | 37 +++++++++++ apps/gpsrec/widget.js | 76 ++++++++++++++++++++++ index.js | 66 ++++++++++++++++++- 11 files changed, 325 insertions(+), 6 deletions(-) create mode 100644 apps/gpsrec/ChangeLog create mode 100644 apps/gpsrec/app-icon.js create mode 100644 apps/gpsrec/app-settings.json create mode 100644 apps/gpsrec/app.js create mode 100644 apps/gpsrec/app.json create mode 100644 apps/gpsrec/app.png create mode 100644 apps/gpsrec/interface.html create mode 100644 apps/gpsrec/widget.js diff --git a/README.md b/README.md index cd6edbd47..eaf8c6f4c 100644 --- a/README.md +++ b/README.md @@ -193,6 +193,10 @@ about the app. // iframe, and it must post back an 'app' structure // like this one with 'storage','name' and 'id' set up + "interface": "interface.html", // if supplied, apps/interface.html is loaded in an + // iframe, and it may interact with the connected Bangle + // to retrieve information from it + "allow_emulator":true, // if 'app.js' will run in the emulator, set to true to // add an icon to allow your app to be tested diff --git a/apps.json b/apps.json index dedbb22a1..32d490768 100644 --- a/apps.json +++ b/apps.json @@ -173,7 +173,7 @@ "icon": "gpstime.png", "version":"0.01", "description": "Update the Bangle.js's clock based on the time from the GPS receiver", - "tags": "tool", + "tags": "tool,gps", "storage": [ {"name":"+gpstime","url":"gpstime.json"}, {"name":"-gpstime","url":"gpstime.js"}, @@ -185,7 +185,7 @@ "icon": "openlocation.png", "version":"0.01", "description": "Convert your current GPS location to a series of characters", - "tags": "tool,outdoors", + "tags": "tool,outdoors,gps", "storage": [ {"name":"+openloc","url":"openlocation.json"}, {"name":"-openloc","url":"openlocation.js","evaluate":true} @@ -196,13 +196,28 @@ "icon": "speedo.png", "version":"0.01", "description": "Show the current speed according to the GPS", - "tags": "tool,outdoors", + "tags": "tool,outdoors,gps", "storage": [ {"name":"+speedo","url":"speedo.json"}, {"name":"-speedo","url":"speedo.js"}, {"name":"*speedo","url":"speedo-icon.js","evaluate":true} ] }, + { "id": "gpsrec", + "name": "GPS Recorder", + "icon": "app.png", + "version":"0.01", + "interface": "interface.html", + "description": "Application that allows you to record a GPS track. Can run in background", + "tags": "tool,outdoors,gps", + "storage": [ + {"name":"+gpsrec","url":"app.json"}, + {"name":"-gpsrec","url":"app.js"}, + {"name":"@gpsrec","url":"app-settings.json","evaluate":true}, + {"name":"*gpsrec","url":"app-icon.js","evaluate":true}, + {"name":"=gpsrec","url":"widget.js"} + ] + }, { "id": "slevel", "name": "Spirit Level", "icon": "spiritlevel.png", diff --git a/apps/gpsrec/ChangeLog b/apps/gpsrec/ChangeLog new file mode 100644 index 000000000..efbc53fb0 --- /dev/null +++ b/apps/gpsrec/ChangeLog @@ -0,0 +1 @@ +0.00: New App! diff --git a/apps/gpsrec/app-icon.js b/apps/gpsrec/app-icon.js new file mode 100644 index 000000000..9ba966765 --- /dev/null +++ b/apps/gpsrec/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A/AF/W63P54XVCigACF4IACC6QsUF44yKC44sUF5QyEC5QuWF5fPC5Yv/F/3OAYgviwNdroqCwF6wHP1V6vQwKF61eiAABGAQqBFYOkAYOr62rwADBF63V1ZOCmgvCmgvB1Wk1QEB0ml6vVGYOAF65PBDQU6F4rvH6qYB0ovXDQN66vW1hgBmhnBF5AwBAgOlSQgvR5+lC4fPFpAvECASSFd6weBABR3HSQYvpSQQvsUILWBF9XVX4OkF9bvd0ocB5/O1XOR5er0oIDF62AJoPPAYIzBF5QAFF6QoBE4OrVgKACGYIvI6GI1gvXFYN60q/D52k5ySFFwc0iEQrxfXK4TvGSQoTCwQuBAAJhDF6GIxHQ6vVGgQAESQfOTwQvZnQWBmgXDF4qSC5+kGYOr62sR4U0R6WsI4eCF5AzETwYYBwWC6AvlX4gAIR553DR5Ivhd4OCFwYvpAAwv/F7wMBF6ouLF5APHF54sMF44SNF5IsPF4gUSGQQXVAH4A/AH4A/ADY")) diff --git a/apps/gpsrec/app-settings.json b/apps/gpsrec/app-settings.json new file mode 100644 index 000000000..7e1c8ee72 --- /dev/null +++ b/apps/gpsrec/app-settings.json @@ -0,0 +1,5 @@ +{ + "recording":false, + "file":0, + "period":1 +} diff --git a/apps/gpsrec/app.js b/apps/gpsrec/app.js new file mode 100644 index 000000000..890cf95bf --- /dev/null +++ b/apps/gpsrec/app.js @@ -0,0 +1,115 @@ +Bangle.loadWidgets(); +Bangle.drawWidgets(); + +var settings = require("Storage").readJSON("@gpsrec")||{}; + +function getFN(n) { + return ".gpsrc"+n.toString(36); +} + +function updateSettings() { + require("Storage").write("@gpsrec", settings); + if (WIDGETS["gpsrec"]) + WIDGETS["gpsrec"].reload(); +} + +function showMainMenu() { + const mainmenu = { + '': { 'title': 'GPS Record' }, + 'RECORD': { + value: !!settings.recording, + format: v=>v?"On":"Off", + onchange: v => { + settings.recording = v; + updateSettings(); + } + }, + 'File #': { + value: settings.file|0, + min: 0, + max: 35, + step: 1, + onchange: v => { + settings.recording = false; + settings.file = v; + updateSettings(); + } + }, + 'Time Period': { + value: settings.period||1, + min: 1, + max: 60, + step: 1, + onchange: v => { + settings.recording = false; + settings.period = v; + updateSettings(); + } + }, + 'View Tracks': viewTracks, + '< Back': ()=>{load();} + }; + return E.showMenu(mainmenu); +} + +function viewTracks() { + const menu = { + '': { 'title': 'GPS Tracks' } + }; + var found = false; + for (var n=0;n<36;n++) { + var f = require("Storage").open(getFN(n),"r"); + if (f.readLine()!==undefined) { + menu["Track "+n] = viewTrack.bind(null,n); + found = true; + } + } + if (!found) + menu["No Tracks found"] = function(){}; + menu['< Back'] = showMainMenu; + return E.showMenu(menu); +} + +function viewTrack(n) { + const menu = { + '': { 'title': 'GPS Track '+n } + }; + var trackCount = 0; + var trackTime; + var f = require("Storage").open(getFN(n),"r"); + var l = f.readLine(); + if (l!==undefined) { + var c = l.split(","); + trackTime = new Date(0|c[0]); + } + while (l!==undefined) { + trackCount++; + // TODO: min/max/length of track? + l = f.readLine(); + } + if (trackTime) + menu[" "+trackTime.toISOString().substr(0,16).replace("T"," ")] = function(){}; + menu[trackCount+" records"] = function(){}; + // TODO: option to draw it? Just scan through, project using min/max + menu['Erase'] = function() { + E.showPrompt("Delete Track?").then(function(v) { + if (v) { + settings.recording = false; + updateSettings(); + var f = require("Storage").open(getFN(n),"r"); + f.erase(); + viewTracks(); + } else + viewTrack(n); + }); + }; + menu['< Back'] = viewTracks; + print(menu); + return E.showMenu(menu); +} + +showMainMenu(); + + +// f = require("Storage").open(".gpsrc"+n,"r"); +// f.readLine()... diff --git a/apps/gpsrec/app.json b/apps/gpsrec/app.json new file mode 100644 index 000000000..aa8416a77 --- /dev/null +++ b/apps/gpsrec/app.json @@ -0,0 +1,5 @@ +{ + "name":"GPS Recorder", + "icon":"*gpsrec", + "src":"-gpsrec" +} diff --git a/apps/gpsrec/app.png b/apps/gpsrec/app.png new file mode 100644 index 0000000000000000000000000000000000000000..ab6d37c3b1b8e8a71eddd4d282b16ccde0955605 GIT binary patch literal 1457 zcmV;i1y1^jP)im|AXN1>=70+pA-Jn!5`7h^`K zjB{rgDvSAVZf4Fo|L^?ocfRwTTu32>6jFGa5M~psj-FV7B5X$xw&QIpMrUcqU_L11 z?*BF!$xY=K9IvjJ$tP|B(~?Flix@nuQ&`QRM8!no7aX-S=Bi0R)TF0ks(@)L#1^B4 zKW+h5&%b! z4%6Qs!0onDU!P4+kB{RgM$qeq*txfgY?pmjYbQm(=S^lBFXaPpq0i2p2{VUYZwFvx zGz7rts2F~ZNua98vdZ|VhisRf>sM~k)iKE04aMXybC#Rp2<+HX$>ak+_6!@B zo4dH#{SVJqm1-)dxCLyfT|;ZjO^Ql#F&M(7vvQHl<@c`8ci+kqyC0K2zRO9c6R>C4 z2>3&sKNCJX&1XBv-#Z5aoH=&3)UJ8#f1^&NFmraT(QGl2o0q|0|2XcVY)vsq=|ahZ zAi$12FW~nDNlUlH4qzVM#6E9;p@DHEN!H{)uXaF|6@L2u99~a=((*iN>MLT#By9&2 zW%eAhBopw5FFda&s3|5XJJ8bHP4n4aii&L1);bCJWp4KdDJU>wwd!LNJcP+?r0&i2 z^xnM3>WZbBVv-`D^F}|a3fO7@YqtgUHW8(m$qaku!W_Is#&B)hy$rwo< z;Czps3%B%C7RGL+ngxiGz^OI^lOoiV3B2>cW)w9%20SrN4407@3_r)mB~CUCW3odV5Q}DxCFlXP383aUKZJ7&pg|<<C z7#S9UF>d`mMA@o*c2$HpbkT?>7*2Y*OJQT7lso}{^cW(^0}Oiverz%E+2+{2+Tk$b za4d}cE-Fgn`gIRRlO-~37CIDu|Io*=YX;KHu=BYPW{tI>$xWW2nJ5W<4RJb+?AwC*?of=q>vtHrqaG&QTrB8ioY9u=zHs(ctnA^a*4$Y zlQ26Z0h+sz)u{5;+WGO;r()iX)A3@K0)%!{zCe&yV0OKTG4mKY+eGZR1o)#Mfty%p zM^%rYlMf47ueJZjcn&}vJXnPyeuNx&8O66CiTsayM>D7+C#N*Z_(-SmL=!WujV8|j z1OZKLVN&&okhQff9@40scfYGc5kEl*tS7CkC)xZdq>w@i|3Cf(p6@7l7yKG+00000 LNkvXXu0mjf@;$L- literal 0 HcmV?d00001 diff --git a/apps/gpsrec/interface.html b/apps/gpsrec/interface.html new file mode 100644 index 000000000..cf6b2c311 --- /dev/null +++ b/apps/gpsrec/interface.html @@ -0,0 +1,37 @@ + + + + + + +

Hello!

+ + + + diff --git a/apps/gpsrec/widget.js b/apps/gpsrec/widget.js new file mode 100644 index 000000000..84870a38e --- /dev/null +++ b/apps/gpsrec/widget.js @@ -0,0 +1,76 @@ +(() => { + // add the width + var xpos = WIDGETPOS.tl; + WIDGETPOS.tl += 24;/* the widget width plus some extra pixel to keep distance to others */; + var settings = {}; + var hasFix = false; + var fixToggle = false; // toggles once for each reading + var gpsTrack; // file for GPS track + var periodCtr = 0; + + // draw your widget at xpos + function draw() { + g.reset(); + g.setFont("4x6"); + g.setFontAlign(0,0); + g.clearRect(xpos,0,xpos+23,23); + + if (!settings.recording) { + g.setColor("#606060"); + } else { + g.setColor("#ff0000"); + if (hasFix) { + if (fixToggle) { + g.fillCircle(xpos+11,11,9); + g.setColor("#000000"); + } else + g.drawCircle(xpos+11,11,9); + } else { + g.setColor(fixToggle ? "#ff0000" : "#7f0000"); + g.drawString("NO",xpos+12,5); + g.drawString("FIX",xpos+12,19); + } + } + g.drawString("GPS",xpos+12,12); + } + + function onGPS(fix) { + hasFix = fix.fix; + fixToggle = !fixToggle; + draw(); + if (hasFix) { + periodCtr--; + if (periodCtr<=0) { + periodCtr = settings.period; + if (gpsTrack) gpsTrack.write([ + fix.time.getTime()|0, + fix.lat.toFixed(5), + fix.lon.toFixed(5), + fix.alt + ].join(",")+"\n"); + } + } + } + + // Called by the GPS app to reload settings and decide what's + function reload() { + settings = require("Storage").readJSON("@gpsrec")||{}; + settings.period = settings.period||1; + settings.file |= 0; + + Bangle.removeListener('GPS',onGPS); + if (settings.recording) { + Bangle.on('GPS',onGPS); + Bangle.setGPSPower(1); + var n = settings.file.toString(36); + gpsTrack = require("Storage").open(".gpsrc"+n,"a"); + } else { + Bangle.setGPSPower(0); + gpsTrack = undefined; + } + draw(); + } + reload(); + // add the widget + WIDGETS["gpsrec"]={draw:draw,reload:reload}; +})() diff --git a/index.js b/index.js index cb9352331..a4ec8799b 100644 --- a/index.js +++ b/index.js @@ -86,6 +86,8 @@ function showPrompt(title, text) { }); } function handleCustomApp(app) { + // Pops up an IFRAME that allows an app to be customised + if (!app.custom) throw new Error("App doesn't have custom HTML"); return new Promise((resolve,reject) => { var modal = htmlElement(`