diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..b83632eaa --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.htaccess +node_modules +package-lock.json diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..f3d0d2159 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,3 @@ +language: node_js +node_js: + - "node" diff --git a/README.md b/README.md index f79c8d4d4..45643d6df 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ Bangle.js App Loader (and Apps) ================================ +[![Build Status](https://travis-ci.org/espruino/BangleApps.svg?branch=master)](https://travis-ci.org/espruino/BangleApps) + Try it live at [banglejs.com/apps](https://banglejs.com/apps) ## How does it work? @@ -17,11 +19,11 @@ listed in `apps.json`, loads them, and sends them over Web Bluetooth. Filenames in storage are limited to 8 characters. To easily distinguish between file types, we use the following: -* `+stuff` is JSON for an app -* `*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 +* `stuff.info` is JSON that describes an app - this is auto-generated by the App Loader +* `stuff.img` is an image +* `stuff.app.js` is JS code +* `stuff.wid.js` is JS code for widgets +* `stuff.json` is used for JSON settings for an app ## Developing your own app @@ -32,35 +34,25 @@ easily distinguish between file types, we use the following: ## Adding your app to the menu -* Come up with a unique 7 character name, we'll assume `7chname` +* Come up with a unique (all lowercase, nu spaces) name, we'll assume `7chname`. Bangle.js +is limited to 28 char filenames and appends a file extension (eg `.js`) so please +try and keep filenames short to avoid overflowing the buffer. * Create a folder called `apps/`, lets assume `apps/7chname` * We'd recommend that you copy files from 'Example Applications' (below) as a base, or... * `apps/7chname/app.png` should be a 48px icon * Use http://www.espruino.com/Image+Converter to create `apps/7chname/app-icon.js`, using a 1 bit, 4 bit or 8 bit Web Palette "Image String" -* Create an entry in `apps/7chname/app.json` as follows: - -``` -{ - "name":"Short Name", - "icon":"*7chname", - "src":"-7chname" -} -``` - -See `app.json / widget.json` below for more info on the correct format. - * Create an entry in `apps.json` as follows: ``` { "id": "7chname", "name": "My app's human readable name", + "shortName" : "Short Name", "icon": "app.png", "description": "A detailed description of my great app", "tags": "", "storage": [ - {"name":"+7chname","url":"app.json"}, - {"name":"-7chname","url":"app.js"}, - {"name":"*7chname","url":"app-icon.js","evaluate":true} + {"name":"7chname.app.js","url":"app.js"}, + {"name":"7chname.img","url":"app-icon.js","evaluate":true} ], }, ``` @@ -77,36 +69,34 @@ This is the best way to test... * Run your personal `Bangle App Loader` at https://\.github.io/BangleApps/index.html to load apps onto your device * Your apps should be inside it - if there are problems, check your web browser's 'developer console' for errors +**Note:** It's a great idea to get a local copy of the repository on your PC, +then run `bin/sanitycheck.js` - it'll run through a bunch of common issues +that there might be. + Be aware of the delay between commits and updates on github.io - it can take a few minutes (and a 'hard refresh' of your browser) for changes to take effect. ### Offline -You can add the following to the Espruino Web IDE: +Using the 'Storage' icon in [the Web IDE](https://www.espruino.com/ide/) +(4 discs), upload your files into the places described in your JSON: -``` -// replace with your 7chname app name -var appname = "mygreat"; +* `app-icon.js` -> `7chname.img` -require("Storage").write('*'+appname, - // place app-icon.js contents here -); +Now load `app.js` up in the editor, and click the down-arrow to the bottom +right of the `Send to Espruino` icon. Click `Storage` and then either choose +`7chname.app.js` (if you'd uploaded your app previously), or `New File` +and then enter `7chname.app.js` as the name. -// -require("Storage").write("+"+appname,{ - "name":"My Great App","type":"", - "icon":"*"+appname, - "src":"-"+appname, -}); +Now, clicking the `Send to Espruino` icon will load the app directly into +Espruino **and** will automatically run it. -require("Storage").write("-"+appname,` -// place contents of app.js here -// be aware of double-quoting templated strings -` -``` - -When you upload code this way, your app will be uploaded to Bangle.js's menu +When you upload code this way, your app will even be uploaded to Bangle.js's menu without you having to use the `Bangle App Loader` +**Note:** Widgets need to be run inside a clock or app, so if you're +developing a widget you need to go go `Settings` -> `Communications` -> `Load after saving` +and set it to `Load default application`. + ## Example Applications To make the process easier we've come up with some example applications that you can use as a base @@ -114,21 +104,23 @@ when creating your own. Just come up with a unique 7 character name, copy `apps/ or `apps/_example_widget` to `apps/7chname`, and add `apps/_example_X/add_to_apps.json` to `apps.json`. +**If you're making a widget** please start the name with `wid` to make +it easy to find! + ### App Example The app example is available in [`apps/_example_app`](apps/_example_app) Apps are listed in the Bangle.js menu, accessible from a clock app via the middle button. -* `add_to_apps.json` - insert into `apps.json`, describes the widget to bootloader and loader +* `add_to_apps.json` - insert into `apps.json`, describes the app to bootloader and loader * `app.png` - app icon - 48x48px * `app-icon.js` - JS version of the icon (made with http://www.espruino.com/Image+Converter) for use in Bangle.js's menu -* `app.json` - short app name for Bangle.js menu and storage filenames * `app.js` - app code #### `app-icon.js` -The icon image and short description is used in the menu entry as selection posibility. +The icon image and short description is used in the menu entry as selection possibility. Use the Espruino [image converter](https://www.espruino.com/Image+Converter) and upload your `app.png` file. @@ -155,13 +147,12 @@ Keep in mind to use this converter for creating images you like to draw with `g. The widget example is available in [`apps/_example_widget`](apps/_example_widget) * `add_to_apps.json` - insert into `apps.json`, describes the widget to bootloader and loader -* `widget.json` - short widget name and storage names * `widget.js` - widget code -### `app.json` / `widget.json` format +### `app.info` format -This is the file that's loaded onto Bangle.js, which gives information -about the app. +This is the file that's **auto-generated** and loaded onto Bangle.js by the App Loader, +and which gives information about the app for the Launcher. ``` { @@ -184,9 +175,10 @@ about the app. ``` { "id": "appid", // 7 character app id "name": "Readable name", // readable name + "shortName": "Short name", // short name for launcher "icon": "icon.png", // icon in apps/ "description": "...", // long description - "type":"...", // optional(if app) - 'app'/'widget'/'launch' + "type":"...", // optional(if app) - 'app'/'widget'/'launch'/'bootloader' "tags": "", // comma separated tag list for searching "custom": "custom.html", // if supplied, apps/custom.html is loaded in an @@ -203,7 +195,7 @@ about the app. // add an icon to allow your app to be tested "storage": [ // list of files to add to storage - {"name":"-appid", // filename to use in storage + {"name":"appid.js", // filename to use in storage "url":"", // URL of file to load (currently relative to apps/) "content":"..." // if supplied, this content is loaded directly "evaluate":true // if supplied, data isn't quoted into a String before upload @@ -245,13 +237,8 @@ version of what's in `apps.json`: sendCustomizedApp({ id : "7chname", storage:[ - {name:"-7chname", content:app_source_code}, - {name:"+7chname", content:JSON.stringify({ - name:"My app's name", - icon:"*7chname", - src:"-7chname" - })}, - {name:"*7chname", content:'require("heatshrink").decompress(atob("mEwg...4"))', evaluate:true}, + {name:"7chname.app.js", content:app_source_code}, + {name:"7chname.img", content:'require("heatshrink").decompress(atob("mEwg...4"))', evaluate:true}, ] }); }); @@ -299,8 +286,6 @@ See [apps/gpsrec/interface.html](the GPS Recorder) for a full example. ## 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 @@ -315,6 +300,13 @@ See [apps/gpsrec/interface.html](the GPS Recorder) for a full example. - chaining graphics methods, eg `g.setColor(0xFD20).setFontAlign(0,0).setfont("6x8",3)` +### Misc Notes + +- Need to save state? Use the `E.on('kill',...)` event to save JSON to a file called `7chname.json`, then load it at startup. + +- 'Welcome' apps define a file called `welcome.js` which the booloader picks up. This then chain-loads the welcome app itself. + + ### Graphic areas The screen is parted in a widget and app area for lcd mode `direct`(default). diff --git a/appinfo.js b/appinfo.js index 7d49647fe..151227f45 100644 --- a/appinfo.js +++ b/appinfo.js @@ -20,21 +20,12 @@ var AppInfo = { })).then(fileContents => { // now we just have a list of files + contents... // filter out empty files fileContents = fileContents.filter(x=>x!==undefined); + // What about minification? + // Add app's info JSON + return AppInfo.createAppJSON(app, fileContents); + }).then(fileContents => { // then map each file to a command to load into storage fileContents.forEach(storageFile => { - // check if this is the JSON file - if (storageFile.name[0]=="+") { - storageFile.evaluate = true; - var json = {}; - try { - json = JSON.parse(storageFile.content); - } catch (e) { - reject(storageFile.name+" is not valid JSON"); - } - if (app.version) json.version = app.version; - json.files = fileContents.map(storageFile=>storageFile.name).join(","); - storageFile.content = JSON.stringify(json); - } // format ready for Espruino var js; if (storageFile.evaluate) { @@ -49,6 +40,35 @@ var AppInfo = { }).catch(err => reject(err)); }); }, + createAppJSON : (app, fileContents) => { + return new Promise((resolve,reject) => { + var appJSONName = app.id+".info"; + // Check we don't already have a JSON file! + var appJSONFile = fileContents.find(f=>f.name==appJSONName); + if (appJSONFile) reject("App JSON file explicitly specified!"); + // Now actually create the app JSON + var json = { + id : app.id + }; + if (app.shortName) json.name = app.shortName; + else json.name = app.name; + if (app.type && app.type!="app") json.type = app.type; + if (fileContents.find(f=>f.name==app.id+".app.js")) + json.src = app.id+".app.js"; + if (fileContents.find(f=>f.name==app.id+".img")) + json.icon = app.id+".img"; + if (app.sortorder) json.sortorder = app.sortorder; + if (app.version) json.version = app.version; + var fileList = fileContents.map(storageFile=>storageFile.name); + fileList.unshift(appJSONName); // do we want this? makes life easier! + json.files = fileList.join(","); + fileContents.push({ + name : appJSONName, + content : JSON.stringify(json) + }); + resolve(fileContents); + }); + } }; if ("undefined"!=typeof module) diff --git a/apps.json b/apps.json index ca784bd37..ab6542ad3 100644 --- a/apps.json +++ b/apps.json @@ -2,25 +2,26 @@ { "id": "boot", "name": "Bootloader", "icon": "bootloader.png", - "version":"0.05", + "version":"0.07", "description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings", "tags": "tool,system", + "type":"bootloader", "storage": [ {"name":".boot0","url":"boot0.js"}, - {"name":".bootcde","url":"bootloader.js"}, - {"name":"+boot","url":"bootloader.json"} + {"name":".bootcde","url":"bootloader.js"} ], "sortorder" : -10 }, { "id": "launch", "name": "Default Launcher", + "shortName":"Launcher", "icon": "app.png", "version":"0.01", "description": "This is needed by Bangle.js to display a menu allowing you to choose your own applications. You can replace this with a customised launcher.", "tags": "tool,system,launcher", + "type":"launch", "storage": [ - {"name":"+launch","url":"app.json"}, - {"name":"-launch","url":"app.js"} + {"name":"launch.app.js","url":"app.js"} ], "sortorder" : -10 }, @@ -32,22 +33,21 @@ "tags": "tool,system", "allow_emulator":true, "storage": [ - {"name":"+about","url":"app.json"}, - {"name":"-about","url":"app.js"}, - {"name":"*about","url":"app-icon.js","evaluate":true} + {"name":"about.app.js","url":"app.js"}, + {"name":"about.img","url":"app-icon.js","evaluate":true} ] }, { "id": "welcome", "name": "Welcome", "icon": "app.png", - "version":"0.02", + "version":"0.03", "description": "Appears at first boot and explains how to use Bangle.js", - "tags": "welcome", + "tags": "start,welcome", "allow_emulator":true, "storage": [ - {"name":"+welcome","url":"app.json"}, - {"name":"-welcome","url":"app.js"}, - {"name":"*welcome","url":"app-icon.js","evaluate":true} + {"name":"welcome.js","url":"welcome.js"}, + {"name":"welcome.app.js","url":"app.js"}, + {"name":"welcome.img","url":"app-icon.js","evaluate":true} ] }, { "id": "gbridge", @@ -57,10 +57,9 @@ "description": "The default notification handler for Gadgetbridge notifications from Android", "tags": "tool,system,android,widget", "storage": [ - {"name":"+gbridge","url":"app.json"}, - {"name":"-gbridge","url":"app.js"}, - {"name":"*gbridge","url":"app-icon.js","evaluate":true}, - {"name":"=gbridge","url":"widget.js"} + {"name":"gbridge.app.js","url":"app.js"}, + {"name":"gbridge.img","url":"app-icon.js","evaluate":true}, + {"name":"gbridge.wid.js","url":"widget.js"} ] }, { "id": "mclock", @@ -72,38 +71,37 @@ "type":"clock", "allow_emulator":true, "storage": [ - {"name":"+mclock","url":"clock-morphing.json"}, - {"name":"-mclock","url":"clock-morphing.js"}, - {"name":"*mclock","url":"clock-morphing-icon.js","evaluate":true} + {"name":"mclock.app.js","url":"clock-morphing.js"}, + {"name":"mclock.img","url":"clock-morphing-icon.js","evaluate":true} ], "sortorder" : -9 }, { "id": "setting", "name": "Settings", "icon": "settings.png", - "version":"0.03", + "version":"0.05", "description": "A menu for setting up Bangle.js", "tags": "tool,system", "storage": [ - {"name":"+setting","url":"settings.json"}, - {"name":"-setting","url":"settings.js"}, - {"name":"@setting","url":"settings-default.json","evaluate":true}, - {"name":"*setting","url":"settings-icon.js","evaluate":true} + {"name":"setting.app.js","url":"settings.js"}, + {"name":"setting.json","url":"settings-default.json","evaluate":true}, + {"name":"setting.img","url":"settings-icon.js","evaluate":true} ], "sortorder" : -2 }, { "id": "alarm", "name": "Default Alarm", + "shortName":"Alarms", "icon": "app.png", - "version":"0.01", + "version":"0.02", "description": "Set and respond to alarms", "tags": "tool,alarm,widget", "storage": [ - {"name":"+alarm","url":"app.json"}, - {"name":"-alarm","url":"app.js"}, - {"name":"@alarm","content":"[]"}, - {"name":"*alarm","url":"app-icon.js","evaluate":true}, - {"name":"=alarm","url":"widget.js"} + {"name":"alarm.app.js","url":"app.js"}, + {"name":"alarm.js","url":"alarm.js"}, + {"name":"alarm.json","content":"[]"}, + {"name":"alarm.img","url":"app-icon.js","evaluate":true}, + {"name":"alarm.wid.js","url":"widget.js"} ] }, { "id": "wclock", @@ -115,9 +113,8 @@ "type":"clock", "allow_emulator":true, "storage": [ - {"name":"+wclock","url":"clock-word.json"}, - {"name":"-wclock","url":"clock-word.js"}, - {"name":"*wclock","url":"clock-word-icon.js","evaluate":true} + {"name":"wclock.app.js","url":"clock-word.js"}, + {"name":"wclock.img","url":"clock-word-icon.js","evaluate":true} ] }, { "id": "aclock", @@ -129,9 +126,8 @@ "type":"clock", "allow_emulator":true, "storage": [ - {"name":"+aclock","url":"clock-analog.json"}, - {"name":"-aclock","url":"clock-analog.js"}, - {"name":"*aclock","url":"clock-analog-icon.js","evaluate":true} + {"name":"aclock.app.js","url":"clock-analog.js"}, + {"name":"aclock.img","url":"clock-analog-icon.js","evaluate":true} ] }, { "id": "clck3x2", @@ -142,9 +138,8 @@ "tags": "clock", "allow_emulator":true, "storage": [ - {"name":"+clck3x2","url":"clock3x2.json"}, - {"name":"-clck3x2","url":"clock3x2.js"}, - {"name":"*clck3x2","url":"clock3x2-icon.js","evaluate":true} + {"name":"clck3x2.app.js","url":"clock3x2.js"}, + {"name":"clck3x2.img","url":"clock3x2-icon.js","evaluate":true} ] }, { "id": "trex", @@ -155,9 +150,8 @@ "tags": "game", "allow_emulator":true, "storage": [ - {"name":"+trex","url":"trex.json"}, - {"name":"-trex","url":"trex.js"}, - {"name":"*trex","url":"trex-icon.js","evaluate":true} + {"name":"trex.app.js","url":"trex.js"}, + {"name":"trex.img","url":"trex-icon.js","evaluate":true} ] }, { "id": "astroid", @@ -168,9 +162,8 @@ "tags": "game", "allow_emulator":true, "storage": [ - {"name":"+astroid","url":"asteroids.json"}, - {"name":"-astroid","url":"asteroids.js"}, - {"name":"*astroid","url":"asteroids-icon.js","evaluate":true} + {"name":"astroid.app.js","url":"asteroids.js"}, + {"name":"astroid.img","url":"asteroids-icon.js","evaluate":true} ] }, { "id": "clickms", @@ -180,9 +173,8 @@ "description": "Get several friends to start the game, then compete to see who can press BTN1 the most!", "tags": "game", "storage": [ - {"name":"+clickms","url":"click-master.json"}, - {"name":"-clickms","url":"click-master.js"}, - {"name":"*clickms","url":"click-master-icon.js","evaluate":true} + {"name":"clickms.app.js","url":"click-master.js"}, + {"name":"clickms.img","url":"click-master-icon.js","evaluate":true} ] }, { "id": "horsey", @@ -192,9 +184,8 @@ "description": "Get several friends to start the game, then compete to see who can press BTN1 the most!", "tags": "game", "storage": [ - {"name":"+horsey","url":"horse-race.json"}, - {"name":"-horsey","url":"horse-race.js"}, - {"name":"*horsey","url":"horse-race-icon.js","evaluate":true} + {"name":"horsey.app.js","url":"horse-race.js"}, + {"name":"horsey.img","url":"horse-race-icon.js","evaluate":true} ] }, { "id": "compass", @@ -204,32 +195,31 @@ "description": "Simple compass that points North", "tags": "tool,outdoors", "storage": [ - {"name":"+compass","url":"compass.json"}, - {"name":"-compass","url":"compass.js"}, - {"name":"*compass","url":"compass-icon.js","evaluate":true} + {"name":"compass.app.js","url":"compass.js"}, + {"name":"compass.img","url":"compass-icon.js","evaluate":true} ] }, { "id": "gpstime", "name": "GPS Time", "icon": "gpstime.png", - "version":"0.02", + "version":"0.03", "description": "Update the Bangle.js's clock based on the time from the GPS receiver", "tags": "tool,gps", "storage": [ - {"name":"+gpstime","url":"gpstime.json"}, - {"name":"-gpstime","url":"gpstime.js"}, - {"name":"*gpstime","url":"gpstime-icon.js","evaluate":true} + {"name":"gpstime.app.js","url":"gpstime.js"}, + {"name":"gpstime.img","url":"gpstime-icon.js","evaluate":true} ] }, { "id": "openloc", "name": "Open Location / Plus Codes", - "icon": "openlocation.png", + "shortName": "Open Location", + "icon": "app.png", "version":"0.01", "description": "Convert your current GPS location to a series of characters", "tags": "tool,outdoors,gps", "storage": [ - {"name":"+openloc","url":"openlocation.json"}, - {"name":"-openloc","url":"openlocation.js","evaluate":true} + {"name":"openloc.app.js","url":"app.js"}, + {"name":"openloc.img","url":"app-icon.js","evaluate":true} ] }, { "id": "speedo", @@ -239,24 +229,22 @@ "description": "Show the current speed according to the GPS", "tags": "tool,outdoors,gps", "storage": [ - {"name":"+speedo","url":"speedo.json"}, - {"name":"-speedo","url":"speedo.js"}, - {"name":"*speedo","url":"speedo-icon.js","evaluate":true} + {"name":"speedo.app.js","url":"speedo.js"}, + {"name":"speedo.img","url":"speedo-icon.js","evaluate":true} ] }, { "id": "gpsrec", "name": "GPS Recorder", "icon": "app.png", - "version":"0.01", + "version":"0.04", "interface": "interface.html", "description": "Application that allows you to record a GPS track. Can run in background", "tags": "tool,outdoors,gps,widget", "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"} + {"name":"gpsrec.app.js","url":"app.js"}, + {"name":"gpsrec.json","url":"app-settings.json","evaluate":true}, + {"name":"gpsrec.img","url":"app-icon.js","evaluate":true}, + {"name":"gpsrec.wid.js","url":"widget.js"} ] }, { "id": "slevel", @@ -266,9 +254,8 @@ "description": "Show the current angle of the watch, so you can use it to make sure something is absolutely flat", "tags": "tool", "storage": [ - {"name":"+slevel","url":"spiritlevel.json"}, - {"name":"-slevel","url":"spiritlevel.js"}, - {"name":"*slevel","url":"spiritlevel-icon.js","evaluate":true} + {"name":"slevel.app.js","url":"spiritlevel.js"}, + {"name":"slevel.img","url":"spiritlevel-icon.js","evaluate":true} ] }, { "id": "files", @@ -278,33 +265,30 @@ "description": "Show currently installed apps, free space, and allow their deletion from the watch", "tags": "tool,system", "storage": [ - {"name":"+files","url":"files.json"}, - {"name":"-files","url":"files.js"}, - {"name":"*files","url":"files-icon.js","evaluate":true} + {"name":"files.app.js","url":"files.js"}, + {"name":"files.img","url":"files-icon.js","evaluate":true} ] }, - { "id": "sbat", + { "id": "widbat", "name": "Battery Level Widget", - "icon": "widget-battery.png", + "icon": "widget.png", "version":"0.02", "description": "Show the current battery level and charging status in the top right of the clock", "tags": "widget,battery", "type":"widget", "storage": [ - {"name":"+sbat","url":"widget-battery.json"}, - {"name":"=sbat","url":"widget-battery.js"} + {"name":"widbat.wid.js","url":"widget.js"} ] }, - { "id": "sbt", + { "id": "widbt", "name": "Bluetooth Widget", - "icon": "widget-bluetooth.png", + "icon": "widget.png", "version":"0.01", "description": "Show the current Bluetooth connection status in the top right of the clock", "tags": "widget,bluetooth", "type":"widget", "storage": [ - {"name":"+sbt","url":"widget-bluetooth.json"}, - {"name":"=sbt","url":"widget-bluetooth.js"} + {"name":"widbt.wid.js","url":"widget.js"} ] }, { "id": "hrm", @@ -314,20 +298,19 @@ "description": "Measure your current heart rate", "tags": "health", "storage": [ - {"name":"+hrm","url":"heartrate.json"}, - {"name":"-hrm","url":"heartrate.js"}, - {"name":"*hrm","url":"heartrate-icon.js","evaluate":true} + {"name":"hrm.app.js","url":"heartrate.js"}, + {"name":"hrm.img","url":"heartrate-icon.js","evaluate":true} ] }, - { "id": "whrm", + { "id": "widhrm", "name": "Simple Heart Rate widget", "icon": "widget.png", "version":"0.01", "description": "When the screen is on, the widget turns on the heart rate monitor and displays the current heart rate (or last known in grey). For this to work well you'll need at least a 15 second LCD Timeout.", "tags": "health,widget", + "type": "widget", "storage": [ - {"name":"+whrm","url":"widget.json"}, - {"name":"=whrm","url":"widget.js"} + {"name":"widhrm.wid.js","url":"widget.js"} ] }, { "id": "stetho", @@ -337,9 +320,8 @@ "description": "Hear your heart rate", "tags": "health", "storage": [ - {"name":"+stetho","url":"stetho.json"}, - {"name":"-stetho","url":"stetho.js"}, - {"name":"*stetho","url":"stetho-icon.js","evaluate":true} + {"name":"stetho.app.js","url":"stetho.js"}, + {"name":"stetho.img","url":"stetho-icon.js","evaluate":true} ] }, { "id": "swatch", @@ -350,45 +332,44 @@ "tags": "health", "allow_emulator":true, "storage": [ - {"name":"+swatch","url":"stopwatch.json"}, - {"name":"-swatch","url":"stopwatch.js"}, - {"name":"*swatch","url":"stopwatch-icon.js","evaluate":true} + {"name":"swatch.app.js","url":"stopwatch.js"}, + {"name":"swatch.img","url":"stopwatch-icon.js","evaluate":true} ] }, { "id": "hidmsic", "name": "Bluetooth Music Controls", + "shortName": "Music Control", "icon": "hid-music.png", "version":"0.01", "description": "Enable HID in settings, pair with your phone, then use this app to control music from your watch!", "tags": "bluetooth", "storage": [ - {"name":"+hidmsic","url":"hid-music.json"}, - {"name":"-hidmsic","url":"hid-music.js"}, - {"name":"*hidmsic","url":"hid-music-icon.js","evaluate":true} + {"name":"hidmsic.app.js","url":"hid-music.js"}, + {"name":"hidmsic.img","url":"hid-music-icon.js","evaluate":true} ] }, { "id": "hidkbd", "name": "Bluetooth Keyboard", + "shortName": "Bluetooth Kbd", "icon": "hid-keyboard.png", "version":"0.01", "description": "Enable HID in settings, pair with your phone/PC, then use this app to control other apps", "tags": "bluetooth", "storage": [ - {"name":"+hidkbd","url":"hid-keyboard.json"}, - {"name":"-hidkbd","url":"hid-keyboard.js"}, - {"name":"*hidkbd","url":"hid-keyboard-icon.js","evaluate":true} + {"name":"hidkbd.app.js","url":"hid-keyboard.js"}, + {"name":"hidkbd.img","url":"hid-keyboard-icon.js","evaluate":true} ] }, { "id": "hidbkbd", "name": "Binary Bluetooth Keyboard", + "shortName": "Binary BT Kbd", "icon": "hid-binary-keyboard.png", "version":"0.01", "description": "Enable HID in settings, pair with your phone/PC, then type messages using the onscreen keyboard by tapping repeatedly on the key you want", "tags": "bluetooth", "storage": [ - {"name":"+hidbkbd","url":"hid-binary-keyboard.json"}, - {"name":"-hidbkbd","url":"hid-binary-keyboard.js"}, - {"name":"*hidbkbd","url":"hid-binary-keyboard-icon.js","evaluate":true} + {"name":"hidbkbd.app.js","url":"hid-binary-keyboard.js"}, + {"name":"hidbkbd.img","url":"hid-binary-keyboard-icon.js","evaluate":true} ] }, { "id": "animals", @@ -398,17 +379,16 @@ "description": "Simple toddler's game - displays a different number of animals each time the screen is pressed", "tags": "game", "storage": [ - {"name":"+animals","url":"animals.json"}, - {"name":"-animals","url":"animals.js"}, - {"name":"*animals","url":"animals-icon.js","evaluate":true}, - {"name":"*snake","url":"animals-snake.js","evaluate":true}, - {"name":"*duck","url":"animals-duck.js","evaluate":true}, - {"name":"*swan","url":"animals-swan.js","evaluate":true}, - {"name":"*fox","url":"animals-fox.js","evaluate":true}, - {"name":"*camel","url":"animals-camel.js","evaluate":true}, - {"name":"*pig","url":"animals-pig.js","evaluate":true}, - {"name":"*sheep","url":"animals-sheep.js","evaluate":true}, - {"name":"*mouse","url":"animals-mouse.js","evaluate":true} + {"name":"animals.app.js","url":"animals.js"}, + {"name":"animals.img","url":"animals-icon.js","evaluate":true}, + {"name":"animals-snake.img","url":"animals-snake.js","evaluate":true}, + {"name":"animals-duck.img","url":"animals-duck.js","evaluate":true}, + {"name":"animals-swan.img","url":"animals-swan.js","evaluate":true}, + {"name":"animals-fox.img","url":"animals-fox.js","evaluate":true}, + {"name":"animals-camel.img","url":"animals-camel.js","evaluate":true}, + {"name":"animals-pig.img","url":"animals-pig.js","evaluate":true}, + {"name":"animals-sheep.img","url":"animals-sheep.js","evaluate":true}, + {"name":"animals-mouse.img","url":"animals-mouse.js","evaluate":true} ] }, { "id": "qrcode", @@ -419,9 +399,8 @@ "tags": "", "custom": "qrcode.html", "storage": [ - {"name":"-qrcode"}, - {"name":"+qrcode"}, - {"name":"=qrcode"} + {"name":"qrcode.app.js"}, + {"name":"qrcode.img"} ] }, { "id": "beer", @@ -432,9 +411,8 @@ "tags": "", "custom": "beercompass.html", "storage": [ - {"name":"-beer"}, - {"name":"+beer"}, - {"name":"=beer"} + {"name":"beer.app.js"}, + {"name":"beer.img"} ] }, { "id": "route", @@ -445,30 +423,26 @@ "tags": "", "custom": "route.html", "storage": [ - {"name":"-route"}, - {"name":"+route"}, - {"name":"=route"} + {"name":"route.app.js"}, + {"name":"route.img"} ] }, - - - { "id": "ncstart", "name": "NCEU Startup", "icon": "start.png", "version":"0.02", "description": "NodeConfEU 2019 'First Start' Sequence", - "tags": "start", + "tags": "start,welcome", "storage": [ - {"name":"+ncstart","url":"start.json"}, - {"name":".boot3","url":"start.js"}, - {"name":"*ncstart","url":"start-icon.js","evaluate":true}, - {"name":"*bangle","url":"start-bangle.js","evaluate":true}, - {"name":"*nceu","url":"start-nceu.js","evaluate":true}, - {"name":"*nfr","url":"start-nfr.js","evaluate":true}, - {"name":"*nodew","url":"start-nodew.js","evaluate":true}, - {"name":"*tf","url":"start-tf.js","evaluate":true} + {"name":"welcome.js","url":"welcome.js"}, + {"name":"ncstart.app.js","url":"start.js"}, + {"name":"ncstart.img","url":"start-icon.js","evaluate":true}, + {"name":"nc-bangle.img","url":"start-bangle.js","evaluate":true}, + {"name":"nc-nceu.img","url":"start-nceu.js","evaluate":true}, + {"name":"nc-nfr.img","url":"start-nfr.js","evaluate":true}, + {"name":"nc-nodew.img","url":"start-nodew.js","evaluate":true}, + {"name":"nc-tf.img","url":"start-tf.js","evaluate":true} ] }, { "id": "ncfrun", @@ -478,21 +452,19 @@ "description": "Display a map of the NodeConf EU 2019 5K Fun Run route and your location on it", "tags": "health", "storage": [ - {"name":"+ncfrun","url":"nceu-funrun.json"}, - {"name":"-ncfrun","url":"nceu-funrun.js"}, - {"name":"*ncfrun","url":"nceu-funrun-icon.js","evaluate":true} + {"name":"ncfrun.app.js","url":"nceu-funrun.js"}, + {"name":"ncfrun.img","url":"nceu-funrun-icon.js","evaluate":true} ] }, - { "id": "nceuwid", + { "id": "widnceu", "name": "NCEU Logo Widget", - "icon": "nceu-widget.png", + "icon": "widget.png", "version":"0.01", "description": "Show the NodeConf EU logo in the top left", "tags": "widget", "type":"widget", "storage": [ - {"name":"+nceuwid","url":"nceu-widget.json"}, - {"name":"=nceuwid","url":"nceu-widget.js"} + {"name":"widnceu.wid.js","url":"widget.js"} ] }, @@ -507,9 +479,8 @@ "type":"clock", "allow_emulator":true, "storage": [ - {"name":"+sclock","url":"clock-simple.json"}, - {"name":"-sclock","url":"clock-simple.js"}, - {"name":"*sclock","url":"clock-simple-icon.js","evaluate":true} + {"name":"sclock.app.js","url":"clock-simple.js"}, + {"name":"sclock.img","url":"clock-simple-icon.js","evaluate":true} ] }, { "id": "gesture", @@ -520,11 +491,10 @@ "tags": "gesture,ai", "type":"app", "storage": [ - {"name":"+gesture","url":"gesture.json"}, - {"name":"-gesture","url":"gesture.js"}, + {"name":"gesture.app.js","url":"gesture.js"}, {"name":".tfnames","url":"gesture-tfnames.js","evaluate":true}, {"name":".tfmodel","url":"gesture-tfmodel.js","evaluate":true}, - {"name":"*gesture","url":"gesture-icon.js","evaluate":true} + {"name":"gesture.img","url":"gesture-icon.js","evaluate":true} ] }, { "id": "pparrot", @@ -536,9 +506,8 @@ "type":"app", "allow_emulator":true, "storage": [ - {"name":"+pparrot","url":"party-parrot.json"}, - {"name":"-pparrot","url":"party-parrot.js"}, - {"name":"*pparrot","url":"party-parrot-icon.js","evaluate":true} + {"name":"pparrot.app.js","url":"party-parrot.js"}, + {"name":"pparrot.img","url":"party-parrot-icon.js","evaluate":true} ] }, { "id": "hrings", @@ -550,9 +519,8 @@ "type":"app", "allow_emulator":true, "storage": [ - {"name":"+hrings","url":"hypno-rings.json"}, - {"name":"-hrings","url":"hypno-rings.js"}, - {"name":"*hrings","url":"hypno-rings-icon.js","evaluate":true} + {"name":"hrings.app.js","url":"hypno-rings.js"}, + {"name":"hrings.img","url":"hypno-rings-icon.js","evaluate":true} ] }, { "id": "morse", @@ -563,9 +531,8 @@ "tags": "morse,sound,visual,input", "type":"app", "storage": [ - {"name":"+morse","url":"morse-code.json"}, - {"name":"-morse","url":"morse-code.js"}, - {"name":"*morse","url":"morse-code-icon.js","evaluate":true} + {"name":"morse.app.js","url":"morse-code.js"}, + {"name":"morse.img","url":"morse-code-icon.js","evaluate":true} ] }, { @@ -576,9 +543,8 @@ "description": "Scan for advertising BLE devices", "tags" : "bluetooth", "storage" : [ - {"name":"+blescan","url":"blescan.json"}, - {"name":"-blescan","url":"blescan.js"}, - {"name":"*blescan","url":"blescan-icon.js", "evaluate":true} + {"name":"blescan.app.js","url":"blescan.js"}, + {"name":"blescan.img","url":"blescan-icon.js", "evaluate":true} ] }, { "id": "mmonday", @@ -588,9 +554,8 @@ "description": "The Bangles make a comeback", "tags": "sound", "storage": [ - {"name":"+mmonday","url":"manic-monday.json"}, - {"name":"-mmonday","url":"manic-monday.js"}, - {"name":"*mmonday","url":"manic-monday-icon.js","evaluate":true} + {"name":"mmonday.app.js","url":"manic-monday.js"}, + {"name":"mmonday.img","url":"manic-monday-icon.js","evaluate":true} ] }, { "id": "jbells", @@ -601,9 +566,8 @@ "tags": "sound", "type":"app", "storage": [ - {"name":"+jbells","url":"jbells.json"}, - {"name":"-jbells","url":"jbells.js"}, - {"name":"*jbells","url":"jbells-icon.js","evaluate":true} + {"name":"jbells.app.js","url":"jbells.js"}, + {"name":"jbells.img","url":"jbells-icon.js","evaluate":true} ] }, { "id": "scolor", @@ -615,9 +579,8 @@ "type":"app", "allow_emulator":true, "storage": [ - {"name":"+scolor","url":"show-color.json"}, - {"name":"-scolor","url":"show-color.js"}, - {"name":"*scolor","url":"show-color-icon.js","evaluate":true} + {"name":"scolor.app.js","url":"show-color.js"}, + {"name":"scolor.img","url":"show-color-icon.js","evaluate":true} ] }, { "id": "miclock", @@ -629,9 +592,8 @@ "type":"clock", "allow_emulator":true, "storage": [ - {"name":"+miclock","url":"clock-mixed.json"}, - {"name":"-miclock","url":"clock-mixed.js"}, - {"name":"*miclock","url":"clock-mixed-icon.js","evaluate":true} + {"name":"miclock.app.js","url":"clock-mixed.js"}, + {"name":"miclock.img","url":"clock-mixed-icon.js","evaluate":true} ] }, { "id": "bclock", @@ -643,9 +605,8 @@ "type":"clock", "allow_emulator":true, "storage": [ - {"name":"+bclock","url":"clock-binary.json"}, - {"name":"-bclock","url":"clock-binary.js"}, - {"name":"*bclock","url":"clock-binary-icon.js","evaluate":true} + {"name":"bclock.app.js","url":"clock-binary.js"}, + {"name":"bclock.img","url":"clock-binary-icon.js","evaluate":true} ] }, { "id": "clotris", @@ -656,9 +617,8 @@ "tags": "game", "allow_emulator":true, "storage": [ - {"name":"+clotris","url":"clock-tris.json"}, - {"name":"-clotris","url":"clock-tris.js"}, - {"name":"*clotris","url":"clock-tris-icon.js","evaluate":true}, + {"name":"clotris.app.js","url":"clock-tris.js"}, + {"name":"clotris.img","url":"clock-tris-icon.js","evaluate":true}, {"name":".trishig","url":"clock-tris-high"} ] }, @@ -670,9 +630,8 @@ "tags": "game", "allow_emulator":true, "storage": [ - {"name":"+flappy","url":"app.json"}, - {"name":"-flappy","url":"app.js"}, - {"name":"*flappy","url":"app-icon.js","evaluate":true} + {"name":"flappy.app.js","url":"app.js"}, + {"name":"flappy.img","url":"app-icon.js","evaluate":true} ] }, { @@ -684,9 +643,8 @@ "tags": "gps", "type": "app", "storage": [ - {"name": "+gpsinfo","url": "gps-info.json"}, - {"name": "-gpsinfo","url": "gps-info.js"}, - {"name": "*gpsinfo","url": "gps-info-icon.js","evaluate": true} + {"name":"gpsinfo.app.js","url": "gps-info.js"}, + {"name":"gpsinfo.img","url": "gps-info-icon.js","evaluate": true} ] }, { @@ -699,13 +657,13 @@ "type": "app", "allow_emulator":true, "storage": [ - {"name": "+pomodo","url": "pomodoro.json"}, - {"name": "-pomodo","url": "pomodoro.js"}, - {"name": "*pomodo","url": "pomodoro-icon.js","evaluate": true} + {"name":"pomodo.app.js","url": "pomodoro.js"}, + {"name":"pomodo.img","url": "pomodoro-icon.js","evaluate": true} ] }, { "id": "blobclk", "name": "Large Digit Blob Clock", + "shortName" : "Blob Clock", "icon": "clock-blob.png", "version":"0.03", "description": "A clock with big digits", @@ -713,9 +671,8 @@ "type":"clock", "allow_emulator":true, "storage": [ - {"name":"+blobclk","url":"clock-blob.json"}, - {"name":"-blobclk","url":"clock-blob.js"}, - {"name":"*blobclk","url":"clock-blob-icon.js","evaluate":true} + {"name":"blobclk.app.js","url":"clock-blob.js"}, + {"name":"blobclk.img","url":"clock-blob-icon.js","evaluate":true} ] }, { "id": "boldclk", @@ -727,33 +684,30 @@ "type":"clock", "allow_emulator":true, "storage": [ - {"name":"+boldclk","url":"bold_clock.json"}, - {"name":"-boldclk","url":"bold_clock.js"}, - {"name":"*boldclk","url":"bold_clock-icon.js","evaluate":true} + {"name":"boldclk.app.js","url":"bold_clock.js"}, + {"name":"boldclk.img","url":"bold_clock-icon.js","evaluate":true} ] }, - { "id": "wdclk", + { "id": "widclk", "name": "Digital clock widget", - "icon": "digital_clock_widget.png", + "icon": "widget.png", "version":"0.01", "description": "A simple digital clock widget", "tags": "widget,clock", "type":"widget", "storage": [ - {"name":"+wdclk","url":"digital_clock_widget.json"}, - {"name":"=wdclk","url":"digital_clock_widget.js"} + {"name":"widclk.wid.js","url":"widget.js"} ] }, - { "id": "wpedom", + { "id": "widpedom", "name": "Pedometer widget", - "icon": "pedometer_widget.png", + "icon": "widget.png", "version":"0.06", "description": "Daily pedometer widget", "tags": "widget", "type":"widget", "storage": [ - {"name":"+wpedom","url":"pedometer_widget.json"}, - {"name":"=wpedom","url":"pedometer_widget.js"} + {"name":"widpedom.wid.js","url":"widget.js"} ] }, { "id": "berlinc", @@ -765,9 +719,8 @@ "type":"clock", "allow_emulator":true, "storage": [ - {"name":"+berlinc","url":"berlin-clock.json"}, - {"name":"-berlinc","url":"berlin-clock.js"}, - {"name":"*berlinc","url":"berlin-clock-icon.js","evaluate":true} + {"name":"berlinc.app.js","url":"berlin-clock.js"}, + {"name":"berlinc.img","url":"berlin-clock-icon.js","evaluate":true} ] }, { "id": "ctrclk", @@ -779,9 +732,8 @@ "type":"clock", "allow_emulator":true, "storage": [ - {"name":"+ctrclk","url":"app.json"}, - {"name":"-ctrclk","url":"app.js"}, - {"name":"*ctrclk","url":"app-icon.js","evaluate":true} + {"name":"ctrclk.app.js","url":"app.js"}, + {"name":"ctrclk.img","url":"app-icon.js","evaluate":true} ] }, { "id": "demoapp", @@ -793,9 +745,8 @@ "type":"app", "allow_emulator":true, "storage": [ - {"name":"+demoapp","url":"app.json"}, - {"name":"-demoapp","url":"app.js"}, - {"name":"*demoapp","url":"app-icon.js","evaluate":true} + {"name":"demoapp.app.js","url":"app.js"}, + {"name":"demoapp.img","url":"app-icon.js","evaluate":true} ], "sortorder" : -9 }, @@ -805,9 +756,22 @@ "description": "App to send a command to another Espruino to cause it to raise a flag", "tags": "", "storage": [ - {"name":"+flagrse","url":"app.json"}, - {"name":"-flagrse","url":"app.js"}, - {"name":"*flagrse","url":"app-icon.js","evaluate":true} + {"name":"flagrse.app.js","url":"app.js"}, + {"name":"flagrse.img","url":"app-icon.js","evaluate":true} + ] + }, + { + "id": "pipboy", + "name": "Pipboy", + "icon": "app.png", + "version": "0.01", + "description": "Pipboy themed clock", + "tags": "clock", + "type":"clock", + "allow_emulator":true, + "storage": [ + {"name":"pipboy.app.js","url":"app.js"}, + {"name":"pipboy.img","url":"app-icon.js","evaluate":true} ] }, { "id": "wohrm", diff --git a/apps/_example_app/add_to_apps.json b/apps/_example_app/add_to_apps.json index 9043b0058..c75f9ed7d 100644 --- a/apps/_example_app/add_to_apps.json +++ b/apps/_example_app/add_to_apps.json @@ -1,6 +1,7 @@ // Create an entry in apps.json as follows: { "id": "7chname", "name": "My app's human readable name", + "shortName":"Short Name", "icon": "app.png", "version":"0.01", "description": "A detailed description of my great app", diff --git a/apps/_example_app/app.json b/apps/_example_app/app.json deleted file mode 100644 index 65168c5a1..000000000 --- a/apps/_example_app/app.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Short Name", - "icon":"*7chname", - "src":"-7chname" -} diff --git a/apps/_example_widget/add_to_apps.json b/apps/_example_widget/add_to_apps.json index 149c6a3dd..3011fe744 100644 --- a/apps/_example_widget/add_to_apps.json +++ b/apps/_example_widget/add_to_apps.json @@ -1,10 +1,12 @@ // Create an entry in apps.json as follows: { "id": "7chname", "name": "My widget's human readable name", + "shortName":"Short Name", "icon": "widget.png", "version":"0.01", "description": "A detailed description of my great widget", "tags": "widget", + "type": "widget", "storage": [ {"name":"+7chname","url":"widget.json"}, {"name":"=7chname","url":"widget.js"} diff --git a/apps/_example_widget/widget.json b/apps/_example_widget/widget.json deleted file mode 100644 index efc522dee..000000000 --- a/apps/_example_widget/widget.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name":"widgetname", "type":"widget", - "src":"=7chname" -} diff --git a/apps/about/app.json b/apps/about/app.json deleted file mode 100644 index dd1b7c06f..000000000 --- a/apps/about/app.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"About", - "icon":"*about", - "src":"-about" -} diff --git a/apps/aclock/clock-analog.json b/apps/aclock/clock-analog.json deleted file mode 100644 index 049d47406..000000000 --- a/apps/aclock/clock-analog.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name":"Analog Clock","type":"clock", - "icon":"*aclock", - "src":"-aclock", - "sortorder":-10 -} diff --git a/apps/alarm/ChangeLog b/apps/alarm/ChangeLog index 5560f00bc..248faad95 100644 --- a/apps/alarm/ChangeLog +++ b/apps/alarm/ChangeLog @@ -1 +1,2 @@ 0.01: New App! +0.02: Fix issues with alarm scheduling diff --git a/apps/alarm/alarm.js b/apps/alarm/alarm.js new file mode 100644 index 000000000..70dc75b49 --- /dev/null +++ b/apps/alarm/alarm.js @@ -0,0 +1,60 @@ +// Chances are boot0.js got run already and scheduled *another* +// 'load(alarm.js)' - so let's remove it first! +clearInterval(); + +function formatTime(t) { + var hrs = 0|t; + var mins = Math.round((t-hrs)*60); + return hrs+":"+("0"+mins).substr(-2); +} + +function getCurrentHr() { + var time = new Date(); + return time.getHours()+(time.getMinutes()/60); +} + +function showAlarm(alarm) { + var msg = formatTime(alarm.hr); + var buzzCount = 10; + if (alarm.msg) + msg += "\n"+alarm.msg; + E.showPrompt(msg,{ + title:"ALARM!", + buttons : {"Sleep":true,"Ok":false} // default is sleep so it'll come back in 10 mins + }).then(function(sleep) { + buzzCount = 0; + if (sleep) { + alarm.hr += 10/60; // 10 minutes + } else { + alarm.last = (new Date()).getDate(); + if (!alarm.rp) alarm.on = false; + } + require("Storage").write("alarm.json",JSON.stringify(alarms)); + load(); + }); + function buzz() { + Bangle.buzz(100).then(()=>{ + setTimeout(()=>{ + Bangle.buzz(100).then(function() { + if (buzzCount--) + setTimeout(buzz, 3000); + }); + },100); + }); + } + buzz(); +} + +// Check for alarms +var day = (new Date()).getDate(); +var hr = getCurrentHr(); +var alarms = require("Storage").readJSON("alarm.json")||[]; +var active = alarms.filter(a=>a.on&&(a.hra.hr-b.hr); + showAlarm(active[0]); +} else { + // otherwise just go back to default app + load(); +} diff --git a/apps/alarm/app.js b/apps/alarm/app.js index 153a6ef3b..0c6ca2d4e 100644 --- a/apps/alarm/app.js +++ b/apps/alarm/app.js @@ -1,7 +1,7 @@ Bangle.loadWidgets(); Bangle.drawWidgets(); -var alarms = require("Storage").readJSON("@alarm")||[]; +var alarms = require("Storage").readJSON("alarm.json")||[]; /*alarms = [ { on : true, hr : 6.5, // hours + minutes/60 @@ -13,7 +13,7 @@ var alarms = require("Storage").readJSON("@alarm")||[]; function formatTime(t) { var hrs = 0|t; - var mins = 0|((t-hrs)*60); + var mins = Math.round((t-hrs)*60); return hrs+":"+("0"+mins).substr(-2); } @@ -47,7 +47,7 @@ function editAlarm(alarmIndex) { if (!newAlarm) { var a = alarms[alarmIndex]; hrs = 0|a.hr; - mins = 0|((a.hr-hrs)*60); + mins = Math.round((a.hr-hrs)*60); en = a.on; repeat = a.rp; } @@ -55,15 +55,11 @@ function editAlarm(alarmIndex) { '': { 'title': 'Alarms' }, 'Hours': { value: hrs, - min: 0, - max: 23, - onchange: v=>hrs=v + onchange: function(v){if (v<0)v=23;if (v>23)v=0;hrs=v;this.value=v;} // no arrow fn -> preserve 'this' }, 'Minutes': { value: mins, - min: 0, - max: 60, - onchange: v=>mins=v + onchange: function(v){if (v<0)v=59;if (v>59)v=0;mins=v;this.value=v;} // no arrow fn -> preserve 'this' }, 'Enabled': { value: en, @@ -79,8 +75,8 @@ function editAlarm(alarmIndex) { function getAlarm() { var hr = hrs+(mins/60); var day = 0; - // If alarm is for tomorrow not today, set day - if (hr > getCurrentHr()) + // If alarm is for tomorrow not today (eg, in the past), set day + if (hr < getCurrentHr()) day = (new Date()).getDate(); // Save alarm return { @@ -91,13 +87,13 @@ function editAlarm(alarmIndex) { if (newAlarm) { menu["> New Alarm"] = function() { alarms.push(getAlarm()); - require("Storage").write("@alarm",JSON.stringify(alarms)); + require("Storage").write("alarm.json",JSON.stringify(alarms)); showMainMenu(); }; } else { menu["> Save"] = function() { alarms[alarmIndex] = getAlarm(); - require("Storage").write("@alarm",JSON.stringify(alarms)); + require("Storage").write("alarm.json",JSON.stringify(alarms)); showMainMenu(); }; } @@ -105,47 +101,4 @@ function editAlarm(alarmIndex) { return E.showMenu(menu); } -function showAlarm(alarm) { - var msg = formatTime(alarm.hr); - var buzzCount = 10; - if (alarm.msg) - msg += "\n"+alarm.msg; - E.showPrompt(msg,{ - title:"ALARM!", - buttons : {"Sleep":true,"Ok":false} // default is sleep so it'll come back in 10 mins - }).then(function(sleep) { - buzzCount = 0; - if (sleep) { - alarm.hr += 10/60; // 10 minutes - } else { - alarm.last = (new Date()).getDate(); - if (!alarm.rp) alarm.on = false; - } - require("Storage").write("@alarm",JSON.stringify(alarms)); - load(); - }); - function buzz() { - Bangle.buzz(100).then(()=>{ - setTimeout(()=>{ - Bangle.buzz(100).then(function() { - if (buzzCount--) - setTimeout(buzz, 3000); - }); - },100); - }); - } - buzz(); -} - -// Check for alarms -var day = (new Date()).getDate(); -var hr = getCurrentHr(); -var active = alarms.filter(a=>a.on&&(a.hra.hr-b.hr); - showAlarm(active[0]); -} else { - // otherwise show the main menu - showMainMenu(); -} +showMainMenu(); diff --git a/apps/alarm/app.json b/apps/alarm/app.json deleted file mode 100644 index 25b8c6a00..000000000 --- a/apps/alarm/app.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Alarms", - "icon":"*alarm", - "src":"-alarm" -} diff --git a/apps/alarm/widget.js b/apps/alarm/widget.js index 9a8bb01e7..385dd8f2b 100644 --- a/apps/alarm/widget.js +++ b/apps/alarm/widget.js @@ -1,5 +1,5 @@ (() => { - var alarms = require('Storage').readJSON('@alarm')||[]; + var alarms = require('Storage').readJSON('alarm.json')||[]; alarms = alarms.filter(alarm=>alarm.on); if (!alarms.length) return; delete alarms; diff --git a/apps/animals/animals-camel.js b/apps/animals/animals-camel.js index 227ea39c7..bea702739 100644 --- a/apps/animals/animals-camel.js +++ b/apps/animals/animals-camel.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A/AH4Az4/HDDAACCywaSCpgeLBQOjzowRIxZPMBIWj0YvjNRKQSCpIWBEBq/VF6hrKdpwuKABYvXFywlDXjw5aF1gvKF0gv5F0ovvFxQxkF9ztLF8ItJF5XGAAIrfeJYtB4D7TFyQlEFwg8INgZtFF6wuCF4wAFBoPAF7ouNF8AfCF56ZFFyofEF9YgCF5z6GF9y+TC4SAFF9ZgNBgovZMAeczgRJBYovXMAtPAAIQIBYouXDAWjEYUrGBMrBYgvY/15vNVCtAADqoACCs4A/AH4A/AH4A/ABY") +require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A/AH4Az4/HDDAACCywaSCpgeLBQOjzowRIxZPMBIWj0YvjNRKQSCpIWBEBq/VF6hrKdpwuKABYvXFywlDXjw5aF1gvKF0gv5F0ovvFxQxkF9ztLF8ItJF5XGAAIrfeJYtB4D7TFyQlEFwg8INgZtFF6wuCF4wAFBoPAF7ouNF8AfCF56ZFFyofEF9YgCF5z6GF9y+TC4SAFF9ZgNBgovZMAeczgRJBYovXMAtPAAIQIBYouXDAWjEYUrGBMrBYgvY/15vNVCtAADqoACCs4A/AH4A/AH4A/ABY")) diff --git a/apps/animals/animals-pig.js b/apps/animals/animals-pig.js index d02a9c5e4..ca4416ed4 100644 --- a/apps/animals/animals-pig.js +++ b/apps/animals/animals-pig.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A/AH4A/ACXJ5PKAQfKAAPJB4nDAAIsaEwQAJ5IrCAAYbFGyBWBFyooCGxQABPAyDEABYuHGxInFAAQGGLyoIGLxQvCFyAvJBIzjKdB6OLagYwDc5K/DFyIVBAAprHF5IsTHaAuKRoSOSABwvMXyZhPF5iNfF9nJp9PGB4vh5PD3u8X86PCFwO8AAS/mGQe9zAACF/CPdF4aPD3rwmGAu9FxRghegQuKL8IvBFxYwhFx6QdFpxedXIIuQLpHJ4YdCBoQDDFQosRLxAaB4YAEIRIQDFyQvEFhYwGCQgvX/wcOCYYuWDYSmCF6CfEF6mlz96vX+z+evVPCZwABCJYAJvVVAAVPAAYTNCJoAJFwYvPCY5gUAH4A/AH4A/AH4A2") +require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A/AH4A/ACXJ5PKAQfKAAPJB4nDAAIsaEwQAJ5IrCAAYbFGyBWBFyooCGxQABPAyDEABYuHGxInFAAQGGLyoIGLxQvCFyAvJBIzjKdB6OLagYwDc5K/DFyIVBAAprHF5IsTHaAuKRoSOSABwvMXyZhPF5iNfF9nJp9PGB4vh5PD3u8X86PCFwO8AAS/mGQe9zAACF/CPdF4aPD3rwmGAu9FxRghegQuKL8IvBFxYwhFx6QdFpxedXIIuQLpHJ4YdCBoQDDFQosRLxAaB4YAEIRIQDFyQvEFhYwGCQgvX/wcOCYYuWDYSmCF6CfEF6mlz96vX+z+evVPCZwABCJYAJvVVAAVPAAYTNCJoAJFwYvPCY5gUAH4A/AH4A/AH4A2")) diff --git a/apps/animals/animals-sheep.js b/apps/animals/animals-sheep.js index 29ab696ee..4f7603aa2 100644 --- a/apps/animals/animals-sheep.js +++ b/apps/animals/animals-sheep.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A/AGesAAQuuGAQ1jFQoAKF1wwdFyQycF6wwVFi4wWEiOBq1WqoABvQHBvWALsl6p4ADF4IIBGwSNjMAJdCAAVVGwQwPXjWBFoMrF4IwOFzVWp94lV4vIwNFzS8CvGi0YFCGBSNadohdCF9gAGdsgvuGJTvkGAgABFxv+wAwcAAQsLAAVPF9lPAAOIF1tWF7qMOp4DBxAABXtIADGAWB0oupGAeAvQABwJmGAwmlwQuZAAQuCF4SYDAgRtBBoeswKsDF7gsEF4+BqovfAANWF5VWFwIvZGAf+EAYuDBooOEF8ANJF/4vREIQv7BxIvZEIwvvBwQveEIIEDF9QA/AH4A/AH4ASA=") +require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A/AGesAAQuuGAQ1jFQoAKF1wwdFyQycF6wwVFi4wWEiOBq1WqoABvQHBvWALsl6p4ADF4IIBGwSNjMAJdCAAVVGwQwPXjWBFoMrF4IwOFzVWp94lV4vIwNFzS8CvGi0YFCGBSNadohdCF9gAGdsgvuGJTvkGAgABFxv+wAwcAAQsLAAVPF9lPAAOIF1tWF7qMOp4DBxAABXtIADGAWB0oupGAeAvQABwJmGAwmlwQuZAAQuCF4SYDAgRtBBoeswKsDF7gsEF4+BqovfAANWF5VWFwIvZGAf+EAYuDBooOEF8ANJF/4vREIQv7BxIvZEIwvvBwQveEIIEDF9QA/AH4A/AH4ASA=")) diff --git a/apps/animals/animals.js b/apps/animals/animals.js index 38e1a4812..f92882e08 100644 --- a/apps/animals/animals.js +++ b/apps/animals/animals.js @@ -13,7 +13,7 @@ function next(e) { } while (current && current==last); g.clear(); var n = 1 + (0|(Math.random()*3.9)); - var img = require("Storage").read("*"+current); + var img = require("Storage").read("animals-"+current+".img"); if (n==1) g.drawImage(img,120,120,{scale:4,rotate:Math.random()-0.5}); else diff --git a/apps/animals/animals.json b/apps/animals/animals.json deleted file mode 100644 index a2d24941f..000000000 --- a/apps/animals/animals.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Animals Game", "type":"app", - "icon":"*animals", - "src":"-animals" -} diff --git a/apps/astroid/asteroids.json b/apps/astroid/asteroids.json deleted file mode 100644 index 7215be590..000000000 --- a/apps/astroid/asteroids.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Asteroids!","type":"app", - "icon":"*astroid", - "src":"-astroid" -} diff --git a/apps/bclock/clock-binary.json b/apps/bclock/clock-binary.json deleted file mode 100644 index c00dd9d76..000000000 --- a/apps/bclock/clock-binary.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name":"Binary Clock", - "type":"clock", - "icon":"*bclock", - "src":"-bclock" - } - \ No newline at end of file diff --git a/apps/beer/beercompass.html b/apps/beer/beercompass.html index cb5590fa7..434f0f6a9 100644 --- a/apps/beer/beercompass.html +++ b/apps/beer/beercompass.html @@ -196,20 +196,12 @@ Bangle.on('mag', function(m) { Bangle.setCompassPower(1); Bangle.setGPSPower(1); g.clear();`; - var json = JSON.stringify({ - name:"Beer Compass", - icon:"*beer", - src:"-beer" -}); var icon = `require("heatshrink").decompress(atob("mEwghC/AB0O/4AG8AXNgYXHmAXl94XH+AXNn4XH/wXW+YX/C6oWHAAIXN7sz9vdAAoXN9sznvuAAXf/vuC53jC4Xd7wXQ93jn3u9vv9vt7wXT/4tBAgIXQ7wvCC4PgC5sO6czIQJfBC6PumaPDC6wwCC50NYAJcBVgIDBCxrAFbgYXP7yoDF6TADL4YXPVAIXCRyAXC7wXW9zwBC6cNC9zABC4gWQC653CR4fQC6x3TF6gXXI4M9d6wAEC9EN73dAAZfQgczAAkwC/4XXAH4"))`; sendCustomizedApp({ - id : "beer", - storage:[ - {name:"-beer", content:app}, - {name:"+beer", content:json}, - {name:"*beer", content:icon, evaluate:true}, + {name:"beer.app.js", content:app}, + {name:"beer.img", content:icon, evaluate:true}, ] }); }); diff --git a/apps/berlinc/berlin-clock.json b/apps/berlinc/berlin-clock.json deleted file mode 100644 index 7c0d6e411..000000000 --- a/apps/berlinc/berlin-clock.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name":"Berlin Clock", - "type":"clock", - "icon":"*berlinc", - "src":"-berlinc" -} diff --git a/apps/blescan/blescan.json b/apps/blescan/blescan.json deleted file mode 100644 index d9e7f28f1..000000000 --- a/apps/blescan/blescan.json +++ /dev/null @@ -1,7 +0,0 @@ - -{ - "name": "BLE Scanner", - "type":"app", - "icon": "*blescan", - "src": "-blescan" -} diff --git a/apps/blobclk/clock-blob.json b/apps/blobclk/clock-blob.json deleted file mode 100644 index b548acd80..000000000 --- a/apps/blobclk/clock-blob.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name":"Large Clock", - "type":"clock", - "icon":"*blobclk", - "src":"-blobclk", - "sortorder":-10 -} diff --git a/apps/boldclk/bold_clock.json b/apps/boldclk/bold_clock.json deleted file mode 100644 index 45ceede6b..000000000 --- a/apps/boldclk/bold_clock.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name":"Bold Clock", - "type":"clock", - "icon":"*boldclk", - "src":"-boldclk", - "sortorder":-10 -} \ No newline at end of file diff --git a/apps/boot/ChangeLog b/apps/boot/ChangeLog index aebf43427..3c6dfec29 100644 --- a/apps/boot/ChangeLog +++ b/apps/boot/ChangeLog @@ -2,3 +2,5 @@ 0.03: Fix issue switching clockfaces via menu 0.04: Add alarm functionality 0.05: Add Welcome screen on boot +0.06: Disable GPS time log messages, add (default=1) setting to hide log messages +0.07: Fix issues with alarm scheduling diff --git a/apps/boot/boot0.js b/apps/boot/boot0.js index ecd8dc550..7741dd376 100644 --- a/apps/boot/boot0.js +++ b/apps/boot/boot0.js @@ -1,15 +1,20 @@ // This ALWAYS runs at boot E.setFlags({pretokenise:1}); // Load settings... -var s = require('Storage').readJSON('@setting')||{}; +var s = require('Storage').readJSON('setting.json')||{}; if (s.ble!==false) { - if (s.HID) { // Humen interface device + if (s.HID) { // Human 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); +if (s.blerepl===false) { // If not programmable, force terminal off Bluetooth + if (s.log) Terminal.setConsole(true); // if showing debug, force REPL onto terminal + else E.setConsole(null,{force:true}); // on new (2v05+) firmware we have E.setConsole which allows a 'null' console +} else { + if (s.log) Terminal.setConsole(); // if showing debug, put REPL on terminal (until connection) + else Bluetooth.setConsole(true); // else if no debug, force REPL to Bluetooth +} // we just reset, so BLE should be on if (s.ble===false) NRF.sleep(); // Set time, vibrate, beep, etc @@ -21,82 +26,43 @@ E.setTimeZone(s.timezone); delete s; // check for alarms function checkAlarm() { - var alarms = require('Storage').readJSON('@alarm')||[]; + var alarms = require('Storage').readJSON('alarm.json')||[]; var time = new Date(); var active = alarms.filter(a=>a.on&&(a.last!=time.getDate())); if (active.length) { active = active.sort((a,b)=>a.hr-b.hr); - var hr = time.getHours()+(time.getMinutes()/60); - if (!require('Storage').read("-alarm")) { + var hr = time.getHours()+(time.getMinutes()/60)+(time.getSeconds()/3600); + if (!require('Storage').read("alarm.js")) { console.log("No alarm app!"); - require('Storage').write('@alarm',"[]") + require('Storage').write('alarm.json',"[]") } else { - if (active[0].hr < hr) { - // fire alarm now - load("-alarm"); - } else { - // execute alarm at the correct time - setTimeout(function() { - load("-alarm"); - },3600000*(active[0].hr-hr)); - } + var t = 3600000*(active[0].hr-hr); + if (t<1000) t=1000; + /* execute alarm at the correct time. We avoid execing immediately + since this code will get called AGAIN when alarm.js is loaded. alarm.js + will then clearInterval() to get rid of this call so it can proceed + normally. */ + setTimeout(function() { + load("alarm.js"); + },t); } } } // 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"); + //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"); + //console.log("GPS receiver's time not set"); return; } setTime(g.time.getTime()/1000); - console.log("GPS time",g.time.toString()); + //console.log("GPS time",g.time.toString()); checkAlarm(); }); Bangle.setGPSPower(1); } else checkAlarm(); delete checkAlarm; -// Check for -// All of this is just shim for older Bangles -if (!Bangle.loadWidgets) { - Bangle.loadWidgets = function(){ - global.WIDGETPOS={tl:32,tr:g.getWidth()-32,bl:32,br:g.getWidth()-32}; - global.WIDGETS={}; - require("Storage").list().filter(a=>a[0]=='=').forEach(widget=>eval(require("Storage").read(widget))); - }; - Bangle.drawWidgets = function(){ - for(var w of WIDGETS)w.draw() - }; - Bangle.showLauncher = function(){ - var l = require("Storage").list().filter(a=>a[0]=='+').map(app=>{ - try { return require("Storage").readJSON(app); } catch (e) {} - }).find(app=>app.type=="launch"); - if (l) load(l.src); - else E.showMessage("Launcher\nnot found"); - }; - var _load = load; - global.load = function(x) { - if (!x) _load(x); - else setTimeout(function(){ - // attempt to remove any currently-running code - delete Bangle.buzz; - delete Bangle.beep; - Bangle.setLCDOffset&&Bangle.setLCDOffset(0); - Bangle.setLCDMode("direct"); - g.clear(); - clearInterval(); - clearWatch(); - Bangle.removeAllListeners(); - NRF.removeAllListeners(); - Bluetooth.removeAllListeners(); - E.removeAllListeners(); - for (var i in global) if (i!="g") delete global[i]; - setTimeout('eval(require("Storage").read("'+x+'"));',20); - },20); - } -} diff --git a/apps/boot/bootloader.js b/apps/boot/bootloader.js index fc21ee60c..07a1623eb 100644 --- a/apps/boot/bootloader.js +++ b/apps/boot/bootloader.js @@ -1,14 +1,14 @@ // This runs after a 'fresh' boot var settings={}; -try { settings = require("Storage").readJSON('@setting'); } catch (e) {} -if (!settings.welcomed && require("Storage").read("-welcome")!==undefined) { - setTimeout(()=>load("-welcome")); +try { settings = require("Storage").readJSON('setting.json'); } catch (e) {} +if (!settings.welcomed && require("Storage").read("welcome.js")!==undefined) { + setTimeout(()=>load("welcome.js")); } else { // load clock if specified var clockApp = settings.clock; if (clockApp) clockApp = require("Storage").read(clockApp) if (!clockApp) { - var clockApps = require("Storage").list().filter(a=>a[0]=='+').map(app=>{ + var clockApps = require("Storage").list(/\.info$/).map(app=>{ try { return require("Storage").readJSON(app); } catch (e) {} }).filter(app=>app.type=="clock").sort((a, b) => a.sortorder - b.sortorder); diff --git a/apps/boot/bootloader.json b/apps/boot/bootloader.json deleted file mode 100644 index dc568a319..000000000 --- a/apps/boot/bootloader.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name":"Bootloader","type":"boot" -} diff --git a/apps/clck3x2/clock3x2-icon.js b/apps/clck3x2/clock3x2-icon.js index 5ef420d1e..961e259fb 100644 --- a/apps/clck3x2/clock3x2-icon.js +++ b/apps/clck3x2/clock3x2-icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("mEwgRC/AH4A/gED/k/5/wh/wgAFCBcg7NgAVBh/zDoYLkHaAFqAH4A/AH4AW")); +require("heatshrink").decompress(atob("mEwgRC/AH4A/gED/k/5/wh/wgAFCBcg7NgAVBh/zDoYLkHaAFqAH4A/AH4AW")) diff --git a/apps/clck3x2/clock3x2.json b/apps/clck3x2/clock3x2.json deleted file mode 100644 index 2d445787e..000000000 --- a/apps/clck3x2/clock3x2.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name":"Clock 3x2 Pix", - "type":"clock", - "icon":"*clck3x2", - "src":"-clck3x2" -} diff --git a/apps/clickms/click-master.json b/apps/clickms/click-master.json deleted file mode 100644 index 6a2874259..000000000 --- a/apps/clickms/click-master.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Click Master", - "icon": "*clickms", - "src":"-clickms" -} diff --git a/apps/clotris/clock-tris.json b/apps/clotris/clock-tris.json deleted file mode 100644 index ddbb7d10c..000000000 --- a/apps/clotris/clock-tris.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Clock-Tris", - "icon":"*clotris", - "src":"-clotris" -} \ No newline at end of file diff --git a/apps/compass/compass.json b/apps/compass/compass.json deleted file mode 100644 index 7abc9a733..000000000 --- a/apps/compass/compass.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Compass","type":"app", - "icon":"*compass", - "src":"-compass" -} diff --git a/apps/ctrclk/app.json b/apps/ctrclk/app.json deleted file mode 100644 index 68fbfdb70..000000000 --- a/apps/ctrclk/app.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "Centerclock", - "type": "clock", - "icon": "*ctrclk", - "src": "-ctrclk" -} diff --git a/apps/cube/cube.json b/apps/cube/cube.json deleted file mode 100644 index 440a78c72..000000000 --- a/apps/cube/cube.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name":"Cube", - "type":"app", - "icon":"*cube", - "src":"-cube" - } diff --git a/apps/demoapp/app.json b/apps/demoapp/app.json deleted file mode 100644 index fab184c15..000000000 --- a/apps/demoapp/app.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Demo Loop", - "icon":"*demoapp", - "src":"-demoapp" -} diff --git a/apps/files/files.js b/apps/files/files.js index 9f3f7b46f..ab8324b43 100644 --- a/apps/files/files.js +++ b/apps/files/files.js @@ -66,8 +66,8 @@ function showApps() { '< Back': () => m = showMainMenu(), }; - var list = storage.list().filter((a)=> { - return a[0]=='+' && a !== '+setting'; + var list = storage.list(/\.info$/).filter((a)=> { + return a !== 'setting.info'; }).sort().map((app) => { var ret = storage.readJSON(app); ret[''] = app; diff --git a/apps/files/files.json b/apps/files/files.json deleted file mode 100644 index db43a7cdc..000000000 --- a/apps/files/files.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"App Manager","type":"app", - "icon":"*files", - "src":"-files" -} diff --git a/apps/flagrse/app.json b/apps/flagrse/app.json deleted file mode 100644 index 2fee14e86..000000000 --- a/apps/flagrse/app.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Flag Raiser", - "icon":"*flagrse", - "src":"-flagrse" -} diff --git a/apps/flappy/app.json b/apps/flappy/app.json deleted file mode 100644 index 262ed2658..000000000 --- a/apps/flappy/app.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Flappy Bird", - "icon":"*flappy", - "src":"-flappy" -} diff --git a/apps/gbridge/app.json b/apps/gbridge/app.json deleted file mode 100644 index f5c8f3991..000000000 --- a/apps/gbridge/app.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Gadgetbridge", - "icon":"*gbridge", - "src":"-gbridge" -} diff --git a/apps/gesture/gesture.json b/apps/gesture/gesture.json deleted file mode 100644 index 48903e978..000000000 --- a/apps/gesture/gesture.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Gesture Test", "type":"app", - "icon":"*gesture", - "src":"-gesture" -} diff --git a/apps/gpsinfo/gps-info.json b/apps/gpsinfo/gps-info.json deleted file mode 100644 index b86b11d58..000000000 --- a/apps/gpsinfo/gps-info.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "GPS Info", - "type": "app", - "icon": "*gpsinfo", - "src": "-gpsinfo" -} diff --git a/apps/gpsrec/ChangeLog b/apps/gpsrec/ChangeLog index 45fc2f3d0..668ce1991 100644 --- a/apps/gpsrec/ChangeLog +++ b/apps/gpsrec/ChangeLog @@ -1,2 +1,4 @@ 0.01: New App! 0.02: Fix GPS time logging +0.03: Fix GPS time display in gpsrec app +0.04: Properly Fix GPS time display in gpsrec app diff --git a/apps/gpsrec/app.js b/apps/gpsrec/app.js index a06938e60..bac7e92f8 100644 --- a/apps/gpsrec/app.js +++ b/apps/gpsrec/app.js @@ -1,14 +1,14 @@ Bangle.loadWidgets(); Bangle.drawWidgets(); -var settings = require("Storage").readJSON("@gpsrec")||{}; +var settings = require("Storage").readJSON("gpsrec.json")||{}; function getFN(n) { return ".gpsrc"+n.toString(36); } function updateSettings() { - require("Storage").write("@gpsrec", settings); + require("Storage").write("gpsrec.json", settings); if (WIDGETS["gpsrec"]) WIDGETS["gpsrec"].reload(); } @@ -80,7 +80,7 @@ function viewTrack(n) { var l = f.readLine(); if (l!==undefined) { var c = l.split(","); - trackTime = new Date(0|c[0]); + trackTime = new Date(parseInt(c[0])); } while (l!==undefined) { trackCount++; @@ -104,7 +104,6 @@ function viewTrack(n) { }); }; menu['< Back'] = viewTracks; - print(menu); return E.showMenu(menu); } diff --git a/apps/gpsrec/app.json b/apps/gpsrec/app.json deleted file mode 100644 index aa8416a77..000000000 --- a/apps/gpsrec/app.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"GPS Recorder", - "icon":"*gpsrec", - "src":"-gpsrec" -} diff --git a/apps/gpsrec/widget.js b/apps/gpsrec/widget.js index 5ca32efde..7e25a92ad 100644 --- a/apps/gpsrec/widget.js +++ b/apps/gpsrec/widget.js @@ -55,7 +55,7 @@ // Called by the GPS app to reload settings and decide what's function reload() { - settings = require("Storage").readJSON("@gpsrec")||{}; + settings = require("Storage").readJSON("gpsrec.json")||{}; settings.period = settings.period||1; settings.file |= 0; diff --git a/apps/gpstime/ChangeLog b/apps/gpstime/ChangeLog new file mode 100644 index 000000000..e91b36f52 --- /dev/null +++ b/apps/gpstime/ChangeLog @@ -0,0 +1 @@ +0.03: Fix time output on new firmwares when no GPS time set (fix #104) diff --git a/apps/gpstime/gpstime.js b/apps/gpstime/gpstime.js index 285fb64ba..97487ac85 100644 --- a/apps/gpstime/gpstime.js +++ b/apps/gpstime/gpstime.js @@ -5,11 +5,11 @@ Bangle.setLCDTimeout(0); g.clear(); - - var fix; +Bangle.setGPSPower(1); Bangle.on('GPS',function(f) { fix = f; + g.reset(1); g.setFont("6x8",2); g.setFontAlign(0,0); g.clearRect(90,30,239,90); @@ -22,9 +22,12 @@ Bangle.on('GPS',function(f) { } g.setFont("6x8"); g.drawString(fix.satellites+" satellites",170,80); - + g.clearRect(0,100,239,239); - var t = fix.time.toString().split(" ");/* + var t = ["","","","---",""]; + if (fix.time!==undefined) + t = fix.time.toString().split(" "); + /* [ "Sun", "Nov", @@ -42,23 +45,24 @@ Bangle.on('GPS',function(f) { g.drawString(t[3],120,160); // year g.setFont("6x8",3); g.drawString(t[4],120,185); // time - // timezone - var tz = (new Date()).getTimezoneOffset()/60; - if (tz==0) tz="UTC"; - else if (tz>0) tz="UTC+"+tz; - else tz="UTC"+tz; - g.setFont("6x8",2); - g.drawString(tz,120,210); // gmt - g.setFontAlign(0,0,3); - g.drawString("Set",230,120); - g.setFontAlign(0,0); + if (fix.time) { + // timezone + var tz = (new Date()).getTimezoneOffset()/60; + if (tz==0) tz="UTC"; + else if (tz>0) tz="UTC+"+tz; + else tz="UTC"+tz; + g.setFont("6x8",2); + g.drawString(tz,120,210); // gmt + g.setFontAlign(0,0,3); + g.drawString("Set",230,120); + g.setFontAlign(0,0); + } }); setInterval(function() { g.drawImage(img,48,48,{scale:1.5,rotate:Math.sin(getTime()*2)/2}); },100); setWatch(function() { - setTime(fix.time.getTime()/1000); + if (fix.time!==undefined) + setTime(fix.time.getTime()/1000); }, BTN2, {repeat:true}); - -Bangle.setGPSPower(1) diff --git a/apps/gpstime/gpstime.json b/apps/gpstime/gpstime.json deleted file mode 100644 index 813914e08..000000000 --- a/apps/gpstime/gpstime.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"GPS Time","type":"app", - "icon":"*gpstime", - "src":"-gpstime" -} diff --git a/apps/hidbkbd/hid-binary-keyboard.js b/apps/hidbkbd/hid-binary-keyboard.js index 6c595ff8e..d48d97b47 100644 --- a/apps/hidbkbd/hid-binary-keyboard.js +++ b/apps/hidbkbd/hid-binary-keyboard.js @@ -5,7 +5,7 @@ the touchscreen var storage = require('Storage'); -const settings = storage.readJSON('@setting') || { HID: false }; +const settings = storage.readJSON('setting.json') || { HID: false }; const KEY = { A : 4 , B : 5 , diff --git a/apps/hidbkbd/hid-binary-keyboard.json b/apps/hidbkbd/hid-binary-keyboard.json deleted file mode 100644 index e2059a9c2..000000000 --- a/apps/hidbkbd/hid-binary-keyboard.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Binary Keyboard","type":"app", - "icon": "*hidbkbd", - "src": "-hidbkbd" -} diff --git a/apps/hidkbd/hid-keyboard.js b/apps/hidkbd/hid-keyboard.js index 01f5090f0..fe850024e 100644 --- a/apps/hidkbd/hid-keyboard.js +++ b/apps/hidkbd/hid-keyboard.js @@ -1,6 +1,6 @@ var storage = require('Storage'); -const settings = storage.readJSON('@setting') || { HID: false }; +const settings = storage.readJSON('setting.json') || { HID: false }; var sendHid, next, prev, toggle, up, down, profile; diff --git a/apps/hidkbd/hid-keyboard.json b/apps/hidkbd/hid-keyboard.json deleted file mode 100644 index 11653b803..000000000 --- a/apps/hidkbd/hid-keyboard.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Keyboard Control","type":"app", - "icon": "*hidkbd", - "src": "-hidkbd" -} diff --git a/apps/hidmsic/hid-music.js b/apps/hidmsic/hid-music.js index d4dbd5f45..afee7ade9 100644 --- a/apps/hidmsic/hid-music.js +++ b/apps/hidmsic/hid-music.js @@ -1,6 +1,6 @@ var storage = require('Storage'); -const settings = storage.readJSON('@setting') || { HID: false }; +const settings = storage.readJSON('setting.json') || { HID: false }; var sendHid, next, prev, toggle, up, down, profile; diff --git a/apps/hidmsic/hid-music.json b/apps/hidmsic/hid-music.json deleted file mode 100644 index 419ed196e..000000000 --- a/apps/hidmsic/hid-music.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Music Control","type":"app", - "icon": "*hidmsic", - "src": "-hidmsic" -} diff --git a/apps/horsey/horse-race.json b/apps/horsey/horse-race.json deleted file mode 100644 index cf276e2ac..000000000 --- a/apps/horsey/horse-race.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Horse Race", - "icon": "*horsey", - "src":"-horsey" -} diff --git a/apps/hrings/hypno-rings.json b/apps/hrings/hypno-rings.json deleted file mode 100644 index a21a84561..000000000 --- a/apps/hrings/hypno-rings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Hypno Rings","type":"app", - "icon":"*hrings", - "src":"-hrings" -} \ No newline at end of file diff --git a/apps/hrm/heartrate.json b/apps/hrm/heartrate.json deleted file mode 100644 index 72b86993e..000000000 --- a/apps/hrm/heartrate.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Heart Rate","type":"app", - "icon":"*hrm", - "src":"-hrm" -} diff --git a/apps/jbells/jbells.json b/apps/jbells/jbells.json deleted file mode 100644 index 20638a2d6..000000000 --- a/apps/jbells/jbells.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Jingle Bells","type":"app", - "icon":"*jbells", - "src":"-jbells" -} \ No newline at end of file diff --git a/apps/launch/app.js b/apps/launch/app.js index 1b98ba835..e93c2a330 100644 --- a/apps/launch/app.js +++ b/apps/launch/app.js @@ -1,5 +1,5 @@ var s = require("Storage"); -var apps = s.list().filter(a=>a[0]=='+').map(app=>{ +var apps = s.list(/\.info$/).map(app=>{ try { return s.readJSON(app); } catch (e) { return {name:"DEAD: "+app.substr(1)} } }).filter(app=>app.type=="app" || app.type=="clock" || !app.type); diff --git a/apps/launch/app.json b/apps/launch/app.json deleted file mode 100644 index 20a78cd1d..000000000 --- a/apps/launch/app.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name":"Launcher","type":"launch", - "src":"-launch" -} diff --git a/apps/mclock/clock-morphing.json b/apps/mclock/clock-morphing.json deleted file mode 100644 index bf629ac87..000000000 --- a/apps/mclock/clock-morphing.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name":"Morphing Clock","type":"clock", - "icon":"*mclock", - "src":"-mclock", - "sortorder":-10 -} diff --git a/apps/miclock/clock-mixed.json b/apps/miclock/clock-mixed.json deleted file mode 100644 index b5396287a..000000000 --- a/apps/miclock/clock-mixed.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name":"Mixed Clock","type":"clock", - "icon":"*miclock", - "src":"-miclock", - "sortorder":-10 -} diff --git a/apps/mmonday/manic-monday.json b/apps/mmonday/manic-monday.json deleted file mode 100644 index 1454658dd..000000000 --- a/apps/mmonday/manic-monday.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Manic Monday tone", - "icon": "*mmonday", - "src":"-mmonday" -} diff --git a/apps/morse/morse-code.json b/apps/morse/morse-code.json deleted file mode 100644 index bbd142c18..000000000 --- a/apps/morse/morse-code.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Morse Code","type":"app", - "icon":"*morse", - "src":"-morse" -} \ No newline at end of file diff --git a/apps/nceuwid/nceu-widget.json b/apps/nceuwid/nceu-widget.json deleted file mode 100644 index b7c4faa70..000000000 --- a/apps/nceuwid/nceu-widget.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"nceuwid", - "type":"widget", - "src":"=nceuwid" -} diff --git a/apps/ncfrun/nceu-funrun.json b/apps/ncfrun/nceu-funrun.json deleted file mode 100644 index 6a62f1a66..000000000 --- a/apps/ncfrun/nceu-funrun.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name":"5K Fun Run","type":"app", - "icon":"*ncfrun", - "src":"-ncfrun", - "sortorder":-1 -} diff --git a/apps/ncstart/start.js b/apps/ncstart/start.js index 1a619555f..208c554fd 100644 --- a/apps/ncstart/start.js +++ b/apps/ncstart/start.js @@ -1,5 +1,3 @@ -NRF.sleep(); -var g = Graphics.getInstance(); g.setFontAlign(1, 1, 0); const d = g.getWidth() - 18; function c(a) { @@ -53,7 +51,7 @@ function logos() { ]; function next() { var n = logos.shift(); - var img = require("Storage").read("*"+n[0]); + var img = require("Storage").read("nc-"+n[0]+".img"); g.clear(); g.drawImage(img, n[1], n[2]); n[3](); @@ -117,16 +115,11 @@ function info() { } function cleanup() { - E.showMessage('Loading...'); - var s = require('Storage'); - s.erase('*nfr'); - s.erase('*nceu'); - s.erase('*bangle'); - s.erase('*nodew'); - s.erase('*tf'); - s.erase('+ncstart'); - s.erase('.boot3'); - s.erase('*ncstart'); + try { + var settings = require("Storage").readJSON('setting.json'); + settings.welcomed = true; + require("Storage").write('setting.json',settings); + } catch (e) {} return Promise.resolve(); } diff --git a/apps/ncstart/start.json b/apps/ncstart/start.json deleted file mode 100644 index 73665eb7f..000000000 --- a/apps/ncstart/start.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "NCEU Start", - "type": "app", - "icon": "*ncstart", - "src": ".boot3" -} diff --git a/apps/ncstart/welcome.js b/apps/ncstart/welcome.js new file mode 100644 index 000000000..1117d16c6 --- /dev/null +++ b/apps/ncstart/welcome.js @@ -0,0 +1 @@ +eval(require("Storage").read("ncstart.app.js")) diff --git a/apps/nyancat/code.js b/apps/nyancat/code.js new file mode 100644 index 000000000..e69de29bb diff --git a/apps/openloc/openlocation-icon.js b/apps/openloc/app-icon.js similarity index 100% rename from apps/openloc/openlocation-icon.js rename to apps/openloc/app-icon.js diff --git a/apps/openloc/openlocation.js b/apps/openloc/app.js similarity index 100% rename from apps/openloc/openlocation.js rename to apps/openloc/app.js diff --git a/apps/openloc/openlocation.png b/apps/openloc/app.png similarity index 100% rename from apps/openloc/openlocation.png rename to apps/openloc/app.png diff --git a/apps/openloc/openlocation.json b/apps/openloc/openlocation.json deleted file mode 100644 index c02a5a233..000000000 --- a/apps/openloc/openlocation.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Open Location","type":"app", - "icon":"*openloc", - "src":"-openloc" -} diff --git a/apps/pipboy/app-icon.js b/apps/pipboy/app-icon.js new file mode 100644 index 000000000..935210156 --- /dev/null +++ b/apps/pipboy/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwxH+AC4fDDjAuSgwACGFIuBhgACGAJjmLoQvDGAJkhbAguGGYwxbRAoAMGDZZMGBAvSbIpdSGCxYCW5jpDHhKSRDYQgGAwY8ETZYvPDZBXEAAwUFNAowOF44bFNJI2ENISRQdIqzLYhAxDAoRgScZgwFdow1DF54tQdpRMDF5jjHLiDxWF44wJBZIJFF5qPGF5blDLxIvPeAjsOF7RgCc6QvId6AvUd5QACF5pyEKAwlGF5qORcIwoFBgIwHFwwvTU4gvILxYuOXwouKA5AwGFxzuIDYYvkDQ6GIABKqEL6ryGF8QbHF6QXFX6wvuYIQvnFJgMBAAQva/wgMBQT4CF5QPCeB65CABgwCHxYuOF6L5JHYYuPSAyDFBRS6TMA4AME4RdHFyhgCLRLoJLzBgDExa7JFywwVFzQwTFwIADGDC5NRohDBSTQdCGCARCGDRNCGRQuFSobAYGAYxIFwoTDGKpMGWggADA4Q1FYipNGGA7NHYawVBD44qDGIbEHIwwlEA=")) diff --git a/apps/pipboy/app.js b/apps/pipboy/app.js new file mode 100644 index 000000000..050fd366b --- /dev/null +++ b/apps/pipboy/app.js @@ -0,0 +1,86 @@ +const bigFont = 4; +const smallFont = 2; +const tinyFont = 1; +const green = 0x0661; +const darkGreen = 0x0461; +const darkerGreen = 0x0261; +const pip = require("heatshrink").decompress(atob("klQyAlihNhgNhNP5FC0MjokboUJ8JC64ABBgNAjdDifiikhjfjjekjcDgOhImMKoUbscTsUbwUT4QBC8UMkMMgUT8MLwcBS8/hiNiIo2DhkBgkhhfhHoMT0MT0RLBjYJCCIMTocBUoIAiiIvBgcKsMSG4KLChY1CjckgUhgOAhJDBocL4RTBAYMT0ZJliS9BwUDQIchgWAhXCgVAgUghWhiXihWBgUAhdjhYTBkEB8ELkUTgcA8BHfgNAZ4MT8UTwcCJIOjikjihVB8QDBAIcToUSRYNDkdjjWDgOhjdjhVBI8EAgVBiXhQ4MTkcb4ZPCTILLBAYPBjZDBAIUL8QBBd4KRBWIMboZHfiPCOIMKsK5BR4XChkBAIUChkiheibYMT0RPBBoJZBgWgiRfD0RHfhMhhPhjcjhcBiQrBwSHBRocLRINCgUAhWBR4SZBsatCI4IRBsRHfAAMKkJBBhYBCgfBgZDBAYQFC0Q3BicCgehgbRBscKoMCkBlBjXiI8IxBifDgVAidDhfiIoMLkMUgUb4USQ4MiAoLlDiejgVhI4L3BjUjIr8BIILPBwUBwEa8YzBhQ/BoTLBjejgOgiTVEhkChdhiXCI4MB4MA8BHgsAxBjkDP4MjskTgZ3BTIMMkMb8cKLINCS4KPEcIMChWiK4LVhgJ5EAIJJBOoKTBbIQLB4I9BA4QJDCoOigZLBocJ8JHiwELGoPAgfghdCBIJ7BH4mhAYXAAYMDAIQNE0UKwJHioETwZ/C8cT0cS4UDgCBC4UTHIKjCiaNChfiB4SVDoUA4BJhicjhVgjeEjfDifiikCiZPBwUS4MB4ES0Ub4UUgMMgJDBAYJTBiXjIsIABiPiaIK5BgWggXhhXhgQJB4Mi8kakRJBHoJHDLIViiWCWYJHjhNBhWCHoMK0MbgkbkkTgYHBKYMTgUC4DVCcYUbscB8BDjAArFBOoKLCoECgADBaoMToUTsMLwML0MLBIUJ4JFpAAK3BidDhfiQIXCQIIBBRIcEgJFC0UKoJFrbYnBjeDibHBAIUMgIFC8QBC4UKsEA8EZ4cRJd0BwDPC8Y/BI4IBBR4IHBheijXkgOBjcCjcDVoJHriUiiWigVhiY3BS4PBI4JLCwcKkUK0UMgULwSrBiPBRtEhidDQII3BgMgPoMKgRRBhVhgPAiWBieCiehhQBBoKpBJYJFjiPihPhhdChfBieiGIMKwEKkI5BJIMTsUL4UL4afBgUgidjBIMbscJsBFfhOhjdDicigUAhWBYYMT8MT4QBHcIMCwIVBU4TnCMIMbgarBaLkgIoML8UT8Q1BiViYYI/DikCAoRXCgOAiWCCoIbBAIRHBEIPjBoJHbiR7D8UMgQ9BIoMKbIIJCAIMLkMSoSjCIYUMgIBECYIFCKYL/BIq8KgMTwRtBXIcL0UKsELRIIJBBYUCwEK4UL4UD4MDCoPgCIITEEIYJBgUBsLTUsMTka1DAIS3B0UCoETQYIvCT4MKoLXBH4QXCAYIBDEInhSIIZCsTTUsQZBNIXigkBGIVjgUhZIQ7DTIIPBoSHESoRDFAIYlBT4MToUBkCNQsETsRFBgaFBAoPhEIUiR4WiB4QxBwK+BhYTCZojPDAIYJCgfgAoRjBkKNQ0UTZoLVBgMKOYPihiLBoYhBToQJBXocALYbTEEIJHFE4IJCUYQFBkUA8CNMPoOCGIIBCoLPC4aPCsTNCBoIvD4MCkC/BHYrZEAoKTGdIQDB0UJ4KNMsR9CDYeCgR9BwZ9CsQHBGYhHB4RHGJIT1CNoTdGMIQnC0MSwSNKgEbobBBYYQXCgQrBkYpCJ4T9BI4sKkELoQzEMoKtB0BhBQYKnCT4Z5EjdCHoIAHTYIbBZYIBBDIWihWBhWhhYBBI4UDsJPCDIOhI4TjBD4PBAIJ9C8SdCkQdBBYIbCEoITDwUJ0JHHhTLBe4xPBhUigVBA4TNBoIhBA4KjC8RZBhcCDoKvDC4cTgUCkMbwgPDXoIfDikCiWBIw3ggOAidjI4ZFBiXCBoWgS4JHEoRZDBYMK4MKB4Q1BI4YDC4USsUKAoOjF4TrCAIcbkSNGoQZCeoODFYMboZRBB4LvBicjjeDCoMLoSBER4PhhWCV4StDJ4UbscSkQhBiXjjZJCLIsTsUBsBHDGoWChVhgUBhVBiPiLAnAhPgB4MJNoLJCQoJHBichTYWjAIJXBKIMCsMKkLHBjWDhOhOYJHFLocB8A0BhNhLYMLwJTBjUjiPChNBIwYFBjXjiXhiVha4RLB4ADBDYMCkEB4ABCLoQZBCoL9BjcjfoILBDIMD8AhCAISBBGoMR0USDIIbCEoMS0ZlBI4cBIINjhkCNIXiOIZzCZoODc4IBBSYQJBCYUT4cTgcA0DLBieCBYTXCAoMSwZHCwSJBV4JJBN4MST4Mga4ngY4MLoMbGYJrDW4QtC4RLBAIRDBE4INB0UKOYNggEhgFgiWiCYQPB8BPBhUCYoTRBokCwEbskJ4KzBhUia4j/B4cCsC3CgKlBAIXCKIQvCIIIvBjcidoJvCkMa8UBgMAdYPBjahCjbPB8MKGYQ3BjUDgPhjcEhUhiWhV4MJwTXCsDDCBYMCiVjhUBTIL7BKIIBBgVAhQ3B4BDCOoPjDoa5CB4MAhWCGYI3BEoMjkkSoQ3CoMSkZJBgPgNYITBhIPDoELwUDV4PBidjidDiXihXChWBF4IxCSYMCidihehhfAgfADIL7Ba4JBCwMCDYInBoTZCoDJDiNikXkiQpBoUZ8YNDM4MLsUD8EEgAvBGYI3CHIgNBAIYVBC4cL4T3BEYKZBjdjNYIBBCYYjBWII5DAAMJ0MawbjDAAnAjcje4MLSIPiGIJrBegMTOIPhB4IFEAIITBBYUCgMSAIKDBgKLC0ULsJFBWoMJ4I7GABgXBFYMMgIBBJILNBXYMT0JDBHoTpCLYQDD4USW4gnCI4NhhSzBkMSwcBR4wANhViI4aRDV4MKKYRDBBogBEJ4RdCf4sTgUTIYJpBoMRwRFTcocTsR7E0UCkELgQHCAJo5CI4lgI4MCoMasZPBADHgbIIxEwUCgELkQHC0BDHgYDB0IBBgPhNosa4cBkEA4BGZbIUiieiYYWjgPgTIwBKieCRIIjDZoMR4ZDbWYmAidCI41jA4RJBAI6nBBoNiV4I/fABMSwUb4RHDjejikCAJphBI9cBoETGINigUAhbfBapkL4UK4UJ4MKDAIAohOgjXjgUgheBhfAgY9B0IBBAoIHCAIOihVCiXCiXDI9JJCwMCgKPCI4YBIieCgPhicjjWEI9YABhUhY4I9C8JDEAoIBCdYJHCLwJGtI4NCikDikCAIsMAIUT8RDBCYMbwapBR+iRFRoeihVhhWhdYMS0RHtgUhgY1B0EDAIPAAoQDB4MToUCgELgMDA4MiR+sDR4Q9BBIUhgPgieCgYDBoRHwaYoBD8DPDa4IJBhkAiXjI91BifihkCifhAoMUgUMgMb4cKgMSsQNBhkhJ4JHugKNCIoIBCA4cbocBsEJ4MT0RVBgUBI9sJHoOhaIQDB4EDBIUSsITDjWjkcjgMgI9sB4BHEAIPgSoVihOhLYkBiNCItpHC0CLDAYMDI4OijXjHt5HKwET4cL8UTAIPjiciTYMI8BH4gES4ULkcS8USkUJ8AJBiTPwAA8KsUKsMCoECsMK0MTgUTsZH1hPhhdChaNB4MT0RBC4cKTINCgMgImGghPihdigfBgeggfAhehhXBgHAZ+qLCwULAYPCiaNBoTbBgNAIuoABiOiiQBB0LJBhUhgKLBAEgA==")); + +function topLine() { + + g.setColor(green); + g.setFontAlign(0, -1, 0); + + g.moveTo(0, 50).lineTo(30, 50); + g.lineTo(30, 40); + g.lineTo(35, 40).moveTo(90, 40).lineTo(95, 40); + g.lineTo(95, 50); + g.lineTo(239, 50); + + g.setFont("6x8", smallFont); + g.drawString("STAT", 65, 34); + g.drawString("INV", 130, 34); + g.drawString("DATA", 190, 34); + g.setColor(darkGreen); + g.drawString("STATUS", 45, 55); + g.drawString("SPEC", 122, 55); + g.drawString("PERKS", 195, 55); +} + +function bottomLine() { + + g.setColor(darkGreen); + g.fillRect(5, 175, 60, 185); + g.fillRect(67, 175, 140, 185); + + g.setColor(darkerGreen); + g.fillRect(5, 190, 70, 200); + g.fillRect(75, 190, 239, 200); + + g.setFont("6x8", tinyFont); + g.drawString("STIM (0)", 32, 177); + g.drawString("RADAWAY (0)", 105, 177); + g.setColor(green); + g.drawString("HP 115/115", 38, 192); + g.drawString("LEVEL 6", 100, 192); + g.drawRect(127, 192, 235, 198); +} + +function boy() { + g.drawImage(pip, 165, 85); +} + +function drawClock() { + + var t = new Date(); + var h = t.getHours(); + var m = t.getMinutes(); + var time = ("0" + h).substr(-2) + ":" + ("0" + m).substr(-2); + + g.setFont("6x8",bigFont); + g.setColor(green); + g.setFontAlign(0, -1, 0); + + g.clearRect(0, 110, 150, 140); + g.drawString(time, 70, 110); +} + +function drawAll() { + topLine(); + boy(); + bottomLine(); + drawClock(); +} + +Bangle.on('lcdPower', function(on) { + if (on) drawAll(); +}); + +g.clear(); +Bangle.loadWidgets(); +Bangle.drawWidgets(); +setInterval(drawClock, 1E4); +drawAll(); + +setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"}); + diff --git a/apps/pipboy/app.png b/apps/pipboy/app.png new file mode 100644 index 000000000..018b5c7bb Binary files /dev/null and b/apps/pipboy/app.png differ diff --git a/apps/pomodo/pomodoro.json b/apps/pomodo/pomodoro.json deleted file mode 100644 index c017048df..000000000 --- a/apps/pomodo/pomodoro.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Pomodoro","type":"app", - "icon":"*pomodo", - "src":"-pomodo" -} diff --git a/apps/pparrot/party-parrot.json b/apps/pparrot/party-parrot.json deleted file mode 100644 index abf950e47..000000000 --- a/apps/pparrot/party-parrot.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Party Parrot","type":"app", - "icon":"*pparrot", - "src":"-pparrot" -} \ No newline at end of file diff --git a/apps/qrcode/qrcode.html b/apps/qrcode/qrcode.html index bdc34def5..8a0027929 100644 --- a/apps/qrcode/qrcode.html +++ b/apps/qrcode/qrcode.html @@ -41,19 +41,11 @@ g.setColor(0,0,0); g.drawString(url,120,230); g.setColor(1,1,1); `; - var json = JSON.stringify({ - name:"QR Code", - icon:"*qrcode", - src:"-qrcode" - }); var icon = `require("heatshrink").decompress(atob("mEwgP/AEX8gE8nkAn4FSngCWF6xfYDgIABHAQFPDQXD4YgDApxNDMooFOAQIdDAqIvWfcYA="))`; sendCustomizedApp({ - id : "qrcode", - storage:[ - {name:"-qrcode", content:app}, - {name:"+qrcode", content:json}, - {name:"*qrcode", content:icon, evaluate:true}, + {name:"qrcode.app.js", content:app}, + {name:"qrcode.img", content:icon, evaluate:true}, ] }); diff --git a/apps/route/route.html b/apps/route/route.html index f44fb97c5..2417aa232 100644 --- a/apps/route/route.html +++ b/apps/route/route.html @@ -240,20 +240,12 @@ Bangle.setGPSPower(1); Bangle.setCompassPower(1); g.clear(); `; -var json = JSON.stringify({ -name:"Run Route", -icon:"*route", -src:"-route" -}); var icon = `require("heatshrink").decompress(atob("mEwgIkhvgFE/wEDgOHAocDgYFEgOAAp4XEEYsB4w1E5hBKnByFKw8/AQNAAQP/4EAAIMB4HggBABHoNwCwUGE4kOgEYBAMAhk+hgIBAoM/hkEAoMIv8MC4QFChARCAoIMCDoQXChkcjA1EAoJBBg5dCJoJHDKYWAsCGD4AJBAAXBDYIlCsYFBGwUzPok+AokcsOOmIUCAogAWA=="))`; sendCustomizedApp({ - id : "route", - storage:[ - {name:"-route", content:app}, - {name:"+route", content:json}, - {name:"*route", content:icon, evaluate:true}, + {name:"route.app.js", content:app}, + {name:"route.img", content:icon, evaluate:true}, ] }); }); diff --git a/apps/sbat/widget-battery.json b/apps/sbat/widget-battery.json deleted file mode 100644 index 14cea82b3..000000000 --- a/apps/sbat/widget-battery.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name":"Battery Level","type":"widget", - "src":"=sbat" -} diff --git a/apps/sbt/widget-bluetooth.json b/apps/sbt/widget-bluetooth.json deleted file mode 100644 index 249db6fe4..000000000 --- a/apps/sbt/widget-bluetooth.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name":"bluetooth","type":"widget", - "src":"=sbt" -} diff --git a/apps/sclock/clock-simple.js b/apps/sclock/clock-simple.js index e298c9d3a..9bedbc2d0 100644 --- a/apps/sclock/clock-simple.js +++ b/apps/sclock/clock-simple.js @@ -11,7 +11,7 @@ const yposYear = 175; const yposGMT = 220; // Check settings for what type our clock should be -var is12Hour = (require("Storage").readJSON("@setting")||{})["12hour"]; +var is12Hour = (require("Storage").readJSON("setting.json")||{})["12hour"]; function drawSimpleClock() { // get date diff --git a/apps/sclock/clock-simple.json b/apps/sclock/clock-simple.json deleted file mode 100644 index 66cf827ba..000000000 --- a/apps/sclock/clock-simple.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name":"Simple Clock", - "type":"clock", - "icon":"*sclock", - "src":"-sclock", - "sortorder":-10 -} diff --git a/apps/scolor/show-color-icon.js b/apps/scolor/show-color-icon.js index d8b644f5a..eba80bce6 100644 --- a/apps/scolor/show-color-icon.js +++ b/apps/scolor/show-color-icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("mEwxH+64A/AH4A/AH4AllYADqAADugAD4QAD5wADxgADnwADF/4v/F/4vMFQg0EFScyAAYv/F/4v/F5l0AAYqEGggqOlgADF/4v/F/4vMFQnOAAYqEGggqJmgADF/4v/F/4vMFRM+AAYqEGggqEnYADF/4v/F/4vMFR0sAAYqEGglrAAYv/F/4v/F5gqTnYADFQg0EF/4v/F/4vMAH4A/AH4A/AH4AIA")); +require("heatshrink").decompress(atob("mEwxH+64A/AH4A/AH4AllYADqAADugAD4QAD5wADxgADnwADF/4v/F/4vMFQg0EFScyAAYv/F/4v/F5l0AAYqEGggqOlgADF/4v/F/4vMFQnOAAYqEGggqJmgADF/4v/F/4vMFRM+AAYqEGggqEnYADF/4v/F/4vMFR0sAAYqEGglrAAYv/F/4v/F5gqTnYADFQg0EF/4v/F/4vMAH4A/AH4A/AH4AIA")) diff --git a/apps/scolor/show-color.json b/apps/scolor/show-color.json deleted file mode 100644 index 70d28e9d3..000000000 --- a/apps/scolor/show-color.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Show Color","type":"app", - "icon":"*scolor", - "src":"-scolor" -} diff --git a/apps/setting/ChangeLog b/apps/setting/ChangeLog index 21d81d42c..b23865f81 100644 --- a/apps/setting/ChangeLog +++ b/apps/setting/ChangeLog @@ -1,2 +1,4 @@ 0.02: Add support for 30-minute timezones. 0.03: Add support for Welcome app +0.04: Add setting to disable log messages +0.05: Fix Settings json diff --git a/apps/setting/settings-default.json b/apps/setting/settings-default.json index 50d223a69..efaf83bc3 100644 --- a/apps/setting/settings-default.json +++ b/apps/setting/settings-default.json @@ -1,6 +1,7 @@ { ble: true, // Bluetooth enabled by default - dev: true, // Is REPL on Bluetooth - can Espruino IDE be used? + blerepl: true, // Is REPL on Bluetooth - can Espruino IDE be used? + log: false, // Do log messages appear on screen? 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 @@ -9,4 +10,5 @@ clock: null, // a string for the default clock's name "12hour" : false, // 12 or 24 hour clock? distance : "kilometer" // or "mile" + // welcomed : undefined/true (whether welcome app should show) } diff --git a/apps/setting/settings.js b/apps/setting/settings.js index e1f85f86a..568a64047 100644 --- a/apps/setting/settings.js +++ b/apps/setting/settings.js @@ -5,34 +5,34 @@ const storage = require('Storage'); let settings; function updateSettings() { - //storage.erase('@setting'); // - not needed, just causes extra writes if settings were the same - storage.write('@setting', settings); + //storage.erase('setting.json'); // - not needed, just causes extra writes if settings were the same + storage.write('setting.json', settings); } function resetSettings() { settings = { - ble: true, - dev: true, - timeout: 10, - vibrate: true, - beep: true, - timezone: 0, - HID : false, - clock: null, - "12hour" : false, + ble: true, // Bluetooth enabled by default + blerepl: true, // Is REPL on Bluetooth - can Espruino IDE be used? + log: false, // Do log messages appear on screen? + 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" // welcomed : undefined/true (whether welcome app should show) }; - Bangle.setLCDTimeout(settings.timeout); updateSettings(); } try { - settings = storage.readJSON('@setting'); + settings = storage.readJSON('setting.json'); } catch (e) {} if (!settings) resetSettings(); -const boolFormat = (v) => v ? "On" : "Off"; +const boolFormat = v => v ? "On" : "Off"; function showMainMenu() { const mainmenu = { @@ -47,10 +47,18 @@ function showMainMenu() { } }, 'Programmable': { - value: settings.dev, + value: settings.blerepl, format: boolFormat, onchange: () => { - settings.dev = !settings.dev; + settings.blerepl = !settings.blerepl; + updateSettings(); + } + }, + 'Debug info': { + value: settings.log, + format: v => v ? "Show" : "Hide", + onchange: () => { + settings.log = !settings.log; updateSettings(); } }, @@ -179,7 +187,7 @@ function makeConnectable() { }); } function showClockMenu() { - var clockApps = require("Storage").list().filter(a=>a[0]=='+').map(app=>{ + var clockApps = require("Storage").list(/\.info$/).map(app=>{ try { return require("Storage").readJSON(app); } catch (e) {} }).filter(app=>app.type=="clock").sort((a, b) => a.sortorder - b.sortorder); diff --git a/apps/setting/settings.json b/apps/setting/settings.json deleted file mode 100644 index 4c2a0927c..000000000 --- a/apps/setting/settings.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "Settings","type":"app", - "icon": "*settings", - "src": "-settings", - "sortorder": -15 -} diff --git a/apps/slevel/spiritlevel.json b/apps/slevel/spiritlevel.json deleted file mode 100644 index 0a77fd60f..000000000 --- a/apps/slevel/spiritlevel.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Spirit Level","type":"app", - "icon":"*slevel", - "src":"-slevel" -} diff --git a/apps/speedo/speedo.json b/apps/speedo/speedo.json deleted file mode 100644 index ce1c8b82e..000000000 --- a/apps/speedo/speedo.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Speedo","type":"app", - "icon":"*speedo", - "src":"-speedo" -} diff --git a/apps/stetho/stetho.json b/apps/stetho/stetho.json deleted file mode 100644 index 239fffb68..000000000 --- a/apps/stetho/stetho.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Stethoscope","type":"app", - "icon":"*stetho", - "src":"-stetho" -} diff --git a/apps/swatch/stopwatch.json b/apps/swatch/stopwatch.json deleted file mode 100644 index ab4d401f5..000000000 --- a/apps/swatch/stopwatch.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Stopwatch","type":"app", - "icon":"*swatch", - "src":"-swatch" -} diff --git a/apps/trex/trex.json b/apps/trex/trex.json deleted file mode 100644 index e54d4ddb6..000000000 --- a/apps/trex/trex.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"T-Rex","type":"app", - "icon":"*trex", - "src":"-trex" -} diff --git a/apps/wclock/clock-word.json b/apps/wclock/clock-word.json deleted file mode 100644 index 594b73f17..000000000 --- a/apps/wclock/clock-word.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name":"Word Clock","type":"clock", - "icon":"*wclock", - "src":"-wclock", - "sortorder":-10 -} diff --git a/apps/wdclk/digital_clock_widget.json b/apps/wdclk/digital_clock_widget.json deleted file mode 100644 index 5d73e70e5..000000000 --- a/apps/wdclk/digital_clock_widget.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Digital Clock Widget", "type":"widget", - "src":"=wdclk" - } - \ No newline at end of file diff --git a/apps/welcome/ChangeLog b/apps/welcome/ChangeLog index 39b3afcf6..8747cdad3 100644 --- a/apps/welcome/ChangeLog +++ b/apps/welcome/ChangeLog @@ -1,2 +1,3 @@ 0.01: New App! 0.02: Animate balloon intro +0.03: BTN3 now won't restart when at the end diff --git a/apps/welcome/app.js b/apps/welcome/app.js index 6b88f20ad..79a085b14 100644 --- a/apps/welcome/app.js +++ b/apps/welcome/app.js @@ -250,6 +250,7 @@ var scenes = [ var sceneNumber = 0; function move(dir) { + if (dir>0 && sceneNumber+1 == scenes.length) return; // at the end sceneNumber = (sceneNumber+dir)%scenes.length; if (sceneNumber<0) sceneNumber=0; clearInterval(); @@ -270,12 +271,12 @@ function move(dir) { } } if (sceneNumber < scenes.length-1) - setTimeout(next, 5000); + setTimeout(function() { + move(1); + }, 5000); } -function next() { - move(1); -} + Bangle.on('swipe',move); setWatch(()=>move(1), BTN3, {repeat:true}); @@ -283,9 +284,9 @@ setWatch(()=>{ // If we're on the last page if (sceneNumber == scenes.length-1) { try { - var settings = require("Storage").readJSON('@setting'); + var settings = require("Storage").readJSON('setting.json'); settings.welcomed = true; - require("Storage").write('@setting',settings); + require("Storage").write('setting.json',settings); } catch (e) {} load(); } diff --git a/apps/welcome/app.json b/apps/welcome/app.json deleted file mode 100644 index 584f9fc40..000000000 --- a/apps/welcome/app.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Welcome", - "icon":"*welcome", - "src":"-welcome" -} diff --git a/apps/welcome/welcome.js b/apps/welcome/welcome.js new file mode 100644 index 000000000..6a56c2954 --- /dev/null +++ b/apps/welcome/welcome.js @@ -0,0 +1 @@ +eval(require("Storage").read("welcome.app.js")) diff --git a/apps/whrm/widget.json b/apps/whrm/widget.json deleted file mode 100644 index fbc51b789..000000000 --- a/apps/whrm/widget.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name":"Heart Rate Widget", "type":"widget", - "src":"=whrm" -} diff --git a/apps/sbat/ChangeLog b/apps/widbat/ChangeLog similarity index 100% rename from apps/sbat/ChangeLog rename to apps/widbat/ChangeLog diff --git a/apps/sbat/widget-battery.js b/apps/widbat/widget.js similarity index 100% rename from apps/sbat/widget-battery.js rename to apps/widbat/widget.js diff --git a/apps/sbat/widget-battery.png b/apps/widbat/widget.png similarity index 100% rename from apps/sbat/widget-battery.png rename to apps/widbat/widget.png diff --git a/apps/sbt/widget-bluetooth.js b/apps/widbt/widget.js similarity index 100% rename from apps/sbt/widget-bluetooth.js rename to apps/widbt/widget.js diff --git a/apps/sbt/widget-bluetooth.png b/apps/widbt/widget.png similarity index 100% rename from apps/sbt/widget-bluetooth.png rename to apps/widbt/widget.png diff --git a/apps/wdclk/digital_clock_widget.js b/apps/widclk/widget.js similarity index 100% rename from apps/wdclk/digital_clock_widget.js rename to apps/widclk/widget.js diff --git a/apps/wdclk/digital_clock_widget.png b/apps/widclk/widget.png similarity index 100% rename from apps/wdclk/digital_clock_widget.png rename to apps/widclk/widget.png diff --git a/apps/whrm/ChangeLog b/apps/widhrm/ChangeLog similarity index 100% rename from apps/whrm/ChangeLog rename to apps/widhrm/ChangeLog diff --git a/apps/whrm/widget.js b/apps/widhrm/widget.js similarity index 100% rename from apps/whrm/widget.js rename to apps/widhrm/widget.js diff --git a/apps/whrm/widget.png b/apps/widhrm/widget.png similarity index 100% rename from apps/whrm/widget.png rename to apps/widhrm/widget.png diff --git a/apps/nceuwid/nceu-widget.js b/apps/widnceu/widget.js similarity index 100% rename from apps/nceuwid/nceu-widget.js rename to apps/widnceu/widget.js diff --git a/apps/nceuwid/nceu-widget.png b/apps/widnceu/widget.png similarity index 100% rename from apps/nceuwid/nceu-widget.png rename to apps/widnceu/widget.png diff --git a/apps/wpedom/ChangeLog b/apps/widpedom/ChangeLog similarity index 100% rename from apps/wpedom/ChangeLog rename to apps/widpedom/ChangeLog diff --git a/apps/wpedom/pedometer_widget.js b/apps/widpedom/widget.js similarity index 98% rename from apps/wpedom/pedometer_widget.js rename to apps/widpedom/widget.js index f281eaf7b..84a4bb211 100644 --- a/apps/wpedom/pedometer_widget.js +++ b/apps/widpedom/widget.js @@ -1,5 +1,5 @@ (() => { - const PEDOMFILE = "@wpedom"; + const PEDOMFILE = "wpedom.json"; // add the width // WIDGETPOS.tr is originally 208 without any widgets var xpos = WIDGETPOS.tl; diff --git a/apps/wpedom/pedometer_widget.png b/apps/widpedom/widget.png similarity index 100% rename from apps/wpedom/pedometer_widget.png rename to apps/widpedom/widget.png diff --git a/apps/wpedom/pedometer_widget.json b/apps/wpedom/pedometer_widget.json deleted file mode 100644 index 18703a2de..000000000 --- a/apps/wpedom/pedometer_widget.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name":"Pedometer Widget", "type":"widget", - "src":"=wpedom" - } - \ No newline at end of file diff --git a/bin/sanitycheck.js b/bin/sanitycheck.js new file mode 100644 index 000000000..a2e912527 --- /dev/null +++ b/bin/sanitycheck.js @@ -0,0 +1,82 @@ +#!/usr/bin/nodejs +/* Checks for any obvious problems in apps.json +*/ + +var fs = require("fs"); +var acorn; +try { + acorn = require("acorn"); +} catch (e) { + console.log("====================================================="); + console.log(" ACORN NOT FOUND"); + console.log(" ---------------"); + console.log(""); + console.log(" This means we won't sanity-check uploaded JSON"); + console.log("====================================================="); +} + +var BASEDIR = __dirname+"/../"; +var APPSDIR = BASEDIR+"apps/"; +function ERROR(s) { + console.error(s); + process.exit(1); +} +function WARN(s) { + console.log(s); +} + +var appsFile, apps; +try { + appsFile = fs.readFileSync(BASEDIR+"apps.json"); +} catch (e) { + ERROR("apps.json not found"); +} +try{ + apps = JSON.parse(appsFile); +} catch (e) { + ERROR("apps.json not valid JSON"); +} + +apps.forEach((app,addIdx) => { + if (!app.id) ERROR(`App ${appIdx} has no id`); + console.log(`Checking ${app.id}...`); + var appDir = APPSDIR+app.id+"/"; + if (!fs.existsSync(APPSDIR+app.id)) ERROR(`App ${app.id} has no directory`); + if (!app.name) ERROR(`App ${app.id} has no name`); + var isApp = !app.type || app.type=="app"; + if (app.name.length>20 && !app.shortName && isApp) ERROR(`App ${app.id} has a long name, but no shortName`); + if (!app.version) WARN(`App ${app.id} has no version`); + if (!app.description) ERROR(`App ${app.id} has no description`); + if (!app.icon) ERROR(`App ${app.id} has no icon`); + if (!fs.existsSync(appDir+app.icon)) ERROR(`App ${app.id} icon doesn't exist`); + if (app.custom && !fs.existsSync(appDir+app.custom)) ERROR(`App ${app.id} custom HTML doesn't exist`); + if (app.interface && !fs.existsSync(appDir+app.interface)) ERROR(`App ${app.id} interface HTML doesn't exist`); + var fileNames = []; + app.storage.forEach((file) => { + if (!file.name) ERROR(`App ${app.id} has a file with no name`); + if (fileNames.includes(file.name)) + ERROR(`App ${app.id} file ${file.name} is a duplicate`); + fileNames.push(file.name); + if (file.url) if (!fs.existsSync(appDir+file.url)) ERROR(`App ${app.id} file ${file.url} doesn't exist`); + if (!file.url && !file.content && !app.custom) ERROR(`App ${app.id} file ${file.name} has no contents`); + if (file.evaluate) { + var fileContents = file.content ? file.content : fs.readFileSync(appDir+file.url).toString(); + try { + acorn.parse("("+fileContents+")"); + } catch(e) { + console.log("====================================================="); + console.log(" PARSE OF "+appDir+file.url+" failed."); + console.log(""); + console.log(e); + console.log("====================================================="); + console.log(fileContents); + console.log("====================================================="); + ERROR(`App ${app.id}'s ${file.name} has evaluate:true but is not valid JS expression`); + } + } + }); + //console.log(fileNames); + if (isApp && !fileNames.includes(app.id+".app.js")) ERROR(`App ${app.id} has no entrypoint`); + if (isApp && !fileNames.includes(app.id+".img")) ERROR(`App ${app.id} has no JS icon`); + if (app.type=="widget" && !fileNames.includes(app.id+".wid.js")) ERROR(`Widget ${app.id} has no entrypoint`); +}); diff --git a/comms.js b/comms.js index f59a45962..9f685500e 100644 --- a/comms.js +++ b/comms.js @@ -2,30 +2,29 @@ Puck.debug=3; // FIXME: use UART lib so that we handle errors properly var Comms = { -uploadApp : app => { +reset : () => new Promise((resolve,reject) => { + Puck.write("\x03\x10reset();\n", (result) => { + if (result===null) return reject(""); + setTimeout(resolve,500); + }); +}), +uploadApp : (app,skipReset) => { return AppInfo.getFiles(app, httpGet).then(fileContents => { return new Promise((resolve,reject) => { - var appJSONFile = fileContents.find(f=>f.name=="+"+app.id); - var appJSON = undefined; - if (appJSONFile) - try { - appJSON=JSON.parse(appJSONFile.content); - appJSON.id = app.id; - } catch(e) { - console.log("Error decoding app JSON for",app.id,e); - } fileContents = fileContents.map(storageFile=>storageFile.cmd).join("\n")+"\n"; console.log("uploadApp",fileContents); - // reset to ensure we have enough memory to upload what we need to - Puck.write("\x03reset();\n", (result) => { - if (result===null) return reject(""); - setTimeout(() => { // wait for reset - Puck.write("\x10E.showMessage('Uploading...')\n"+fileContents+"\x10E.showMessage('Hold BTN3\\nto reload')\n",(result) => { - if (result===null) return reject(""); - resolve(appJSON); - }); - },500); - }); + function doUpload() { + Puck.write(`\x10E.showMessage('Uploading\\n${app.id}...')\n${fileContents}\x10E.showMessage('Hold BTN3\\nto reload')\n`,(result) => { + if (result===null) return reject(""); + resolve(appJSON); + }); + } + if (skipReset) { + doUpload(); + } else { + // reset to ensure we have enough memory to upload what we need to + Comms.reset().then(doUpload) + } }); }); }, @@ -33,7 +32,7 @@ getInstalledApps : () => { return new Promise((resolve,reject) => { Puck.write("\x03",(result) => { if (result===null) return reject(""); - Puck.eval('require("Storage").list().filter(f=>f[0]=="+").map(f=>{var j=require("Storage").readJSON(f)||{};j.id=f.substr(1);return j})', (appList,err) => { + Puck.eval('require("Storage").list(/\.info$/).map(f=>{var j=require("Storage").readJSON(f)||{};j.id=f.slice(0,-5);return j})', (appList,err) => { if (appList===null) return reject(err || ""); console.log("getInstalledApps", appList); resolve(appList); @@ -46,24 +45,21 @@ removeApp : app => { // expects an app structure return `\x10require("Storage").erase(${toJS(file.name)});\n`; }).join(""); console.log("removeApp", cmds); - return new Promise((resolve,reject) => { - Puck.write("\x03"+cmds+"\x10E.showMessage('Hold BTN3\\nto reload')\n",(result) => { + 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) => { if (result===null) return reject(""); resolve(); }); - }); + })); }, removeAllApps : () => { - return new Promise((resolve,reject) => { - // Use eval here so we wait for it to finish - Puck.eval('require("Storage").eraseAll()||true', (result,err) => { - if (result===null) return reject(err || ""); - Puck.write('\x03\x10reset()\n',(result) => { - if (result===null) return reject(""); - resolve(); - }); - }); - }); + return Comms.reset().then(() => new Promise((resolve,reject) => { + // 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) => { + if (!result || result.trim()!="OK") return reject(err || ""); + resolve(); + }, true /* wait for newline */); + })); }, setTime : () => { return new Promise((resolve,reject) => { @@ -72,7 +68,7 @@ setTime : () => { var cmd = '\x03\x10setTime('+(d.getTime()/1000)+');'; // in 1v93 we have timezones too cmd += 'E.setTimeZone('+tz+');'; - cmd += "(s=>{s&&(s.timezone="+tz+")&&require('Storage').write('@setting',s);})(require('Storage').readJSON('@setting'))\n"; + cmd += "(s=>{s&&(s.timezone="+tz+")&&require('Storage').write('setting.json',s);})(require('Storage').readJSON('setting.json'))\n"; Puck.write(cmd, (result) => { if (result===null) return reject(""); resolve(); diff --git a/defaultapps.json b/defaultapps.json index 2544af001..df508f2e4 100644 --- a/defaultapps.json +++ b/defaultapps.json @@ -1 +1 @@ -["boot","launch","mclock","setting","about","alarm","sbat","sbt","welcome"] +["boot","launch","mclock","setting","about","alarm","widbat","widbt","welcome"] diff --git a/firmware.js b/firmware.js index 08f34e99b..a6923ab1c 100644 --- a/firmware.js +++ b/firmware.js @@ -1,12 +1,12 @@ // Generated by BangleApps/bin/firmwaremaker.js -require('Storage').write(".bootcde","E.setFlags({pretokenise:1});\nvar startapp;\ntry {\n startapp = require('Storage').readJSON('+start');\n} catch (e) {}\nif (startapp) {\n eval(require(\"Storage\").read(startapp.src));\n} else {\n setWatch(function displayMenu() {\n Bangle.setLCDOffset(0); // remove notifications\n Bangle.setLCDMode(\"direct\");\n g.clear();\n // attempt to remove any currently-running code\n clearInterval();\n clearWatch();\n Bangle.removeAllListeners();\n NRF.removeAllListeners();\n Bluetooth.removeAllListeners();\n E.removeAllListeners();\n delete GB;\n delete WIDGETS;\n delete WIDGETPOS;\n delete drawWidgets;\n var s = require(\"Storage\");\n var apps = s.list().filter(a=>a[0]=='+').map(app=>{\n try { return s.readJSON(app); }\n catch (e) { return {name:\"DEAD: \"+app.substr(1)} }\n }).filter(app=>app.type==\"app\" || app.type==\"clock\" || !app.type);\n apps.sort((a,b)=>{\n var n=(0|a.sortorder)-(0|b.sortorder);\n if (n) return n; // do sortorder first\n if (a.nameb.name) return 1;\n return 0;\n });\n var selected = 0;\n var menuScroll = 0;\n var menuShowing = false;\n\n function drawMenu() {\n g.setFont(\"6x8\",2);\n g.setFontAlign(-1,0);\n var n = 3;\n if (selected>=n+menuScroll) menuScroll = 1+selected-n;\n if (selectedn+menuScroll) g.fillPoly([120,239,100,219,140,219]);\n else g.clearRect(100,219,140,239);\n for (var i=0;i0) {\n selected--;\n drawMenu();\n }\n }, BTN1, {repeat:true});\n setWatch(function() {\n if (selected+1a[0]=='=').forEach(widget=>eval(require(\"Storage\").read(widget)));\n setTimeout(drawWidgets,100);\n // load clock if specified\n var clockApp = settings.clock;\n if (clockApp) clockApp = require(\"Storage\").read(clockApp)\n if (!clockApp) {\n var clockApps = require(\"Storage\").list().filter(a=>a[0]=='+').map(app=>{\n try { return require(\"Storage\").readJSON(app); }\n catch (e) {}\n }).filter(app=>app.type==\"clock\").sort((a, b) => a.sortorder - b.sortorder);\n if (clockApps && clockApps.length > 0)\n clockApp = require(\"Storage\").read(clockApps[0].src);\n delete clockApps;\n }\n if (clockApp) eval(clockApp);\n else E.showMessage(\"No Clock Found\");\n delete clockApp;\n}\n"); +require('Storage').write(".bootcde","E.setFlags({pretokenise:1});\nvar startapp;\ntry {\n startapp = require('Storage').readJSON('+start');\n} catch (e) {}\nif (startapp) {\n eval(require(\"Storage\").read(startapp.src));\n} else {\n setWatch(function displayMenu() {\n Bangle.setLCDOffset(0); // remove notifications\n Bangle.setLCDMode(\"direct\");\n g.clear();\n // attempt to remove any currently-running code\n clearInterval();\n clearWatch();\n Bangle.removeAllListeners();\n NRF.removeAllListeners();\n Bluetooth.removeAllListeners();\n E.removeAllListeners();\n delete GB;\n delete WIDGETS;\n delete WIDGETPOS;\n delete drawWidgets;\n var s = require(\"Storage\");\n var apps = s.list().filter(a=>a[0]=='+').map(app=>{\n try { return s.readJSON(app); }\n catch (e) { return {name:\"DEAD: \"+app.substr(1)} }\n }).filter(app=>app.type==\"app\" || app.type==\"clock\" || !app.type);\n apps.sort((a,b)=>{\n var n=(0|a.sortorder)-(0|b.sortorder);\n if (n) return n; // do sortorder first\n if (a.nameb.name) return 1;\n return 0;\n });\n var selected = 0;\n var menuScroll = 0;\n var menuShowing = false;\n\n function drawMenu() {\n g.setFont(\"6x8\",2);\n g.setFontAlign(-1,0);\n var n = 3;\n if (selected>=n+menuScroll) menuScroll = 1+selected-n;\n if (selectedn+menuScroll) g.fillPoly([120,239,100,219,140,219]);\n else g.clearRect(100,219,140,239);\n for (var i=0;i0) {\n selected--;\n drawMenu();\n }\n }, BTN1, {repeat:true});\n setWatch(function() {\n if (selected+1a[0]=='=').forEach(widget=>eval(require(\"Storage\").read(widget)));\n setTimeout(drawWidgets,100);\n // load clock if specified\n var clockApp = settings.clock;\n if (clockApp) clockApp = require(\"Storage\").read(clockApp)\n if (!clockApp) {\n var clockApps = require(\"Storage\").list().filter(a=>a[0]=='+').map(app=>{\n try { return require(\"Storage\").readJSON(app); }\n catch (e) {}\n }).filter(app=>app.type==\"clock\").sort((a, b) => a.sortorder - b.sortorder);\n if (clockApps && clockApps.length > 0)\n clockApp = require(\"Storage\").read(clockApps[0].src);\n delete clockApps;\n }\n if (clockApp) eval(clockApp);\n else E.showMessage(\"No Clock Found\");\n delete clockApp;\n}\n"); require('Storage').write("+mclock",{"name":"Morphing Clock","type":"clock","icon":"*mclock","src":"-mclock","sortorder":-10,"version":"0.01","files":"+mclock,-mclock,*mclock"}); require('Storage').write("-mclock","(function(){ // make our own scope so this is GC'd when intervals are cleared\n// Offscreen buffer\nvar buf = Graphics.createArrayBuffer(240,86,1,{msb:true});\nfunction flip() {\n g.setColor(1,1,1);\n g.drawImage({width:buf.getWidth(),height:buf.getHeight(),buffer:buf.buffer},0,50);\n}\n// The last time that we displayed\nvar lastTime = \" \";\n// If animating, this is the interval's id\nvar animInterval;\n\n/* Get array of lines from digit d to d+1.\n n is the amount (0..1)\n maxFive is true is this digit only counts 0..5 */\nconst DIGITS = {\n\" \":n=>[],\n\"0\":n=>[\n[n,0,1,0],\n[1,0,1,1],\n[1,1,1,2],\n[n,2,1,2],\n[n,1,n,2],\n[n,0,n,1]],\n\"1\":n=>[\n[1-n,0,1,0],\n[1,0,1,1],\n[1-n,1,1,1],\n[1-n,1,1-n,2],\n[1-n,2,1,2]],\n\"2\":n=>[\n[0,0,1,0],\n[1,0,1,1],\n[0,1,1,1],\n[0,1+n,0,2],\n[1,2-n,1,2],\n[0,2,1,2]],\n\"3\":n=>[\n[0,0,1-n,0],\n[0,0,0,n],\n[1,0,1,1],\n[0,1,1,1],\n[1,1,1,2],\n[n,2,1,2]],\n\"4\":n=>[\n[0,0,0,1],\n[1,0,1-n,0],\n[1,0,1,1-n],\n[0,1,1,1],\n[1,1,1,2],\n[1-n,2,1,2]],\n\"5\": (n,maxFive)=>maxFive ? [ // 5 -> 0\n[0,0,0,1],\n[0,0,1,0],\n[n,1,1,1],\n[1,1,1,2],\n[0,2,1,2],\n[0,2,0,2],\n[1,1-n,1,1],\n[0,1,0,1+n]] : [ // 5 -> 6\n[0,0,0,1],\n[0,0,1,0],\n[0,1,1,1],\n[1,1,1,2],\n[0,2,1,2],\n[0,2-n,0,2]],\n\"6\":n=>[\n[0,0,0,1-n],\n[0,0,1,0],\n[n,1,1,1],\n[1,1-n,1,1],\n[1,1,1,2],\n[n,2,1,2],\n[0,1-n,0,2-2*n]],\n\"7\":n=>[\n[0,0,0,n],\n[0,0,1,0],\n[1,0,1,1],\n[1-n,1,1,1],\n[1,1,1,2],\n[1-n,2,1,2],\n[1-n,1,1-n,2]],\n\"8\":n=>[\n[0,0,0,1],\n[0,0,1,0],\n[1,0,1,1],\n[0,1,1,1],\n[1,1,1,2],\n[0,2,1,2],\n[0,1,0,2-n]],\n\"9\":n=>[\n[0,0,0,1],\n[0,0,1,0],\n[1,0,1,1],\n[0,1,1-n,1],\n[0,1,0,1+n],\n[1,1,1,2],\n[0,2,1,2]],\n\":\":n=>[\n[0.4,0.4,0.6,0.4],\n[0.6,0.4,0.6,0.6],\n[0.6,0.6,0.4,0.6],\n[0.4,0.4,0.4,0.6],\n[0.4,1.4,0.6,1.4],\n[0.6,1.4,0.6,1.6],\n[0.6,1.6,0.4,1.6],\n[0.4,1.4,0.4,1.6]]\n};\n\n/* Draw a transition between lastText and thisText.\n 'n' is the amount - 0..1 */\nfunction draw(lastText,thisText,n) {\n buf.clear();\n var x = 1; // x offset\n const p = 2; // padding around digits\n var y = p; // y offset\n const s = 34; // character size\n for (var i=0;i{\n if (c[0]!=c[2]) // horiz\n buf.fillRect(x+c[0]*s,y+c[1]*s-p,x+c[2]*s,y+c[3]*s+p);\n else if (c[1]!=c[3]) // vert\n buf.fillRect(x+c[0]*s-p,y+c[1]*s,x+c[2]*s+p,y+c[3]*s);\n });\n if (thisCh==\":\") x-=4;\n x+=s+p+7;\n }\n y += 2*s;\n var d = new Date();\n buf.setFont(\"6x8\");\n buf.setFontAlign(-1,-1);\n buf.drawString((\"0\"+d.getSeconds()).substr(-2), x, y-8);\n // date\n buf.setFontAlign(0,-1);\n var date = d.toString().substr(0,15);\n buf.drawString(date, buf.getWidth()/2, y+8);\n flip();\n}\n\n/* Show the current time, and animate if needed */\nfunction showTime() {\n if (!Bangle.isLCDOn()) return;\n if (animInterval) return; // in animation - quit\n var d = new Date();\n var t = (\" \"+d.getHours()).substr(-2)+\":\"+\n (\"0\"+d.getMinutes()).substr(-2);\n var l = lastTime;\n // same - don't animate\n if (t==l) {\n draw(t,l,0);\n return;\n }\n var n = 0;\n animInterval = setInterval(function() {\n n += 1/10;\n if (n>=1) {\n n=1;\n clearInterval(animInterval);\n animInterval=0;\n }\n draw(l,t,n);\n }, 20);\n lastTime = t;\n}\n\nBangle.on('lcdPower',function(on) {\n if (on) {\n showTime();\n drawWidgets();\n }\n});\n\ng.clear();\n// Update time once a second\nsetInterval(showTime, 1000);\nshowTime();\n})();\n"); require('Storage').write("*mclock",require("heatshrink").decompress(atob("mEwghC/AE8IxAAEwAWVDB4WIDBwWJAAIWPmf//8zDBpFDwYVBAAc4JJYWJDAoXKn4SC+EPAgXzC5JGCx4qDC4n//BIIEIRCEC4v/GBBdHC4xhCIw5dDC5BhCJAgXCRQoXGJAQXEUhAXHJAyNGC5KRCC7p2FC5B4CC5kggQXOBwvyBQMvSA4XL+EIwCoIC8ZHCgYXNO44LBBIiPPCAIwFC5DXGAAMwGAjvPGA4XIwYXHGALBDnAXFhCQHGAaOFwAXGPA4bFC4xIMIxIXDJBJGEC4xICSJCNEIwowEMJBdCFwwXEMJBdCC5BICDA4WDIw4wEAAMzCoMzBAgWIDAwAGCxRJEAAxFJDBgWNDBAWPAH4AYA=="))); -require('Storage').write("+setting",{"name":"Settings","type":"app","icon":"*settings","src":"-settings","version":"0.01","files":"+setting,-setting,=setting,@setting,*setting"}); -require('Storage').write("-setting","Bangle.setLCDPower(1);\nBangle.setLCDTimeout(0);\n\ng.clear();\nconst storage = require('Storage');\nlet settings;\n\nfunction debug(msg, arg) {\n if (settings.debug)\n console.log(msg, arg);\n}\n\nfunction updateSettings() {\n debug('updating settings', settings);\n //storage.erase('@setting'); // - not needed, just causes extra writes if settings were the same\n storage.write('@setting', settings);\n}\n\nfunction resetSettings() {\n settings = {\n ble: true,\n dev: true,\n timeout: 10,\n vibrate: true,\n beep: true,\n timezone: 0,\n HID : false,\n HIDGestures: false,\n debug: false,\n clock: null\n };\n setLCDTimeout(settings.timeout);\n updateSettings();\n}\n\ntry {\n settings = storage.readJSON('@setting');\n} catch (e) {}\nif (!settings) resetSettings();\n\nconst boolFormat = (v) => v ? \"On\" : \"Off\";\n\nfunction showMainMenu() {\n const mainmenu = {\n '': { 'title': 'Settings' },\n 'BLE': {\n value: settings.ble,\n format: boolFormat,\n onchange: () => {\n settings.ble = !settings.ble;\n updateSettings();\n }\n },\n 'Programmable': {\n value: settings.dev,\n format: boolFormat,\n onchange: () => {\n settings.dev = !settings.dev;\n updateSettings();\n }\n },\n 'LCD Timeout': {\n value: settings.timeout,\n min: 0,\n max: 60,\n step: 5,\n onchange: v => {\n settings.timeout = 0 | v;\n updateSettings();\n Bangle.setLCDTimeout(settings.timeout);\n }\n },\n 'Beep': {\n value: settings.beep,\n format: boolFormat,\n onchange: () => {\n settings.beep = !settings.beep;\n updateSettings();\n if (settings.beep) {\n Bangle.beep(1);\n }\n }\n },\n 'Vibration': {\n value: settings.vibrate,\n format: boolFormat,\n onchange: () => {\n settings.vibrate = !settings.vibrate;\n updateSettings();\n if (settings.vibrate) {\n VIBRATE.write(1);\n setTimeout(()=>VIBRATE.write(0), 10);\n }\n }\n },\n 'Select Clock': showClockMenu,\n 'Time Zone': {\n value: settings.timezone,\n min: -11,\n max: 12,\n step: 1,\n onchange: v => {\n settings.timezone = 0 | v;\n updateSettings();\n }\n },\n 'HID': {\n value: settings.HID,\n format: boolFormat,\n onchange: () => {\n settings.HID = !settings.HID;\n updateSettings();\n }\n },\n 'HID Gestures': {\n value: settings.HIDGestures,\n format: boolFormat,\n onchange: () => {\n settings.HIDGestures = !settings.HIDGestures;\n updateSettings();\n }\n },\n 'Debug': {\n value: settings.debug,\n format: boolFormat,\n onchange: () => {\n settings.debug = !settings.debug;\n updateSettings();\n }\n },\n 'Set Time': showSetTimeMenu,\n 'Make Connectable': makeConnectable,\n 'Reset Settings': showResetMenu,\n 'Turn Off': Bangle.off,\n '< Back': load\n };\n return Bangle.menu(mainmenu);\n}\n\nfunction showResetMenu() {\n const resetmenu = {\n '': { 'title': 'Reset' },\n '< Back': showMainMenu,\n 'Reset Settings': () => {\n E.showPrompt('Reset Settings?').then((v) => {\n if (v) {\n E.showMessage('Resetting');\n resetSettings();\n }\n setTimeout(showMainMenu, 50);\n });\n },\n // this is include for debugging. remove for production\n /*'Erase': () => {\n storage.erase('=setting');\n storage.erase('-setting');\n storage.erase('@setting');\n storage.erase('*setting');\n storage.erase('+setting');\n E.reboot();\n }*/\n };\n return Bangle.menu(resetmenu);\n}\n\nfunction makeConnectable() {\n try { NRF.wake(); } catch(e) {}\n Bluetooth.setConsole(1);\n var name=\"Bangle.js \"+NRF.getAddress().substr(-5).replace(\":\",\"\");\n E.showPrompt(name+\"\\nStay Connectable?\",{title:\"Connectable\"}).then(r=>{\n if (settings.ble!=r) {\n settings.ble = r;\n updateSettings();\n }\n if (!r) try { NRF.sleep(); } catch(e) {}\n showMainMenu();\n });\n}\nfunction showClockMenu() {\n var clockApps = require(\"Storage\").list().filter(a=>a[0]=='+').map(app=>{\n try { return require(\"Storage\").readJSON(app); }\n catch (e) {}\n }).filter(app=>app.type==\"clock\").sort((a, b) => a.sortorder - b.sortorder);\n const clockMenu = {\n '': {\n 'title': 'Select Clock',\n },\n '< Back': showMainMenu,\n };\n clockApps.forEach((app,index) => {\n var label = app.name;\n if ((!settings.clock && index === 0) || (settings.clock === app.src)) {\n label = \"* \"+label;\n }\n clockMenu[label] = () => {\n if (settings.clock !== app.src) {\n settings.clock = app.src;\n updateSettings();\n showMainMenu();\n }\n };\n });\n if (clockApps.length === 0) {\n clockMenu[\"No Clocks Found\"] = () => {};\n }\n return Bangle.menu(clockMenu);\n}\n\n\n\nfunction showSetTimeMenu() {\n d = new Date();\n const timemenu = {\n '': {\n 'title': 'Set Time',\n 'predraw': function() {\n d = new Date();\n timemenu.Hour.value = d.getHours();\n timemenu.Minute.value = d.getMinutes();\n timemenu.Second.value = d.getSeconds();\n timemenu.Date.value = d.getDate();\n timemenu.Month.value = d.getMonth() + 1;\n timemenu.Year.value = d.getFullYear();\n }\n },\n '< Back': showMainMenu,\n 'Hour': {\n value: d.getHours(),\n min: 0,\n max: 23,\n step: 1,\n onchange: v => {\n d = new Date();\n d.setHours(v);\n setTime(d.getTime()/1000);\n }\n },\n 'Minute': {\n value: d.getMinutes(),\n min: 0,\n max: 59,\n step: 1,\n onchange: v => {\n d = new Date();\n d.setMinutes(v);\n setTime(d.getTime()/1000);\n }\n },\n 'Second': {\n value: d.getSeconds(),\n min: 0,\n max: 59,\n step: 1,\n onchange: v => {\n d = new Date();\n d.setSeconds(v);\n setTime(d.getTime()/1000);\n }\n },\n 'Date': {\n value: d.getDate(),\n min: 1,\n max: 31,\n step: 1,\n onchange: v => {\n d = new Date();\n d.setDate(v);\n setTime(d.getTime()/1000);\n }\n },\n 'Month': {\n value: d.getMonth() + 1,\n min: 1,\n max: 12,\n step: 1,\n onchange: v => {\n d = new Date();\n d.setMonth(v - 1);\n setTime(d.getTime()/1000);\n }\n },\n 'Year': {\n value: d.getFullYear(),\n min: 2019,\n max: 2100,\n step: 1,\n onchange: v => {\n d = new Date();\n d.setFullYear(v);\n setTime(d.getTime()/1000);\n }\n }\n };\n return Bangle.menu(timemenu);\n}\n\nshowMainMenu();\n"); -require('Storage').write("=setting","Bangle.HID = E.toUint8Array(atob(\"BQEJBqEBhQIFBxngKecVACUBdQGVCIEClQF1CIEBlQV1AQUIGQEpBZEClQF1A5EBlQZ1CBUAJXMFBxkAKXOBAAkFFQAm/wB1CJUCsQLABQwJAaEBhQEVACUBdQGVAQm1gQIJtoECCbeBAgm4gQIJzYECCeKBAgnpgQIJ6oECwA==\"));\n\n(function() {\n var s = require('Storage').readJSON('@setting');\n var adv = { uart: true };\n if (s.ble) {\n if (s.dev)\n Bluetooth.setConsole(true);\n else\n Terminal.setConsole(true);\n if (s.HID) {\n adv.hid = Bangle.HID;\n } else\n delete Bangle.HID;\n }\n NRF.setServices({}, adv);\n // we just reset, so BLE should be on\n try { // disable advertising if BLE should be off\n if (!s.ble) NRF.sleep();\n else NRF.wake();\n } catch(e) {}\n if (!s.vibrate) Bangle.buzz=Promise.resolve;\n if (!s.beep) Bangle.beep=Promise.resolve;\n Bangle.setLCDTimeout(s.timeout);\n if (!s.timeout) Bangle.setLCDPower(1);\n E.setTimeZone(s.timezone);\n})()\n"); -require('Storage').write("@setting",{ +require('Storage').write("+setting",{"name":"Settings","type":"app","icon":"*settings","src":"-settings","version":"0.01","files":"+setting,-setting,=setting,setting.json,*setting"}); +require('Storage').write("-setting","Bangle.setLCDPower(1);\nBangle.setLCDTimeout(0);\n\ng.clear();\nconst storage = require('Storage');\nlet settings;\n\nfunction debug(msg, arg) {\n if (settings.debug)\n console.log(msg, arg);\n}\n\nfunction updateSettings() {\n debug('updating settings', settings);\n //storage.erase('setting.json'); // - not needed, just causes extra writes if settings were the same\n storage.write('setting.json', settings);\n}\n\nfunction resetSettings() {\n settings = {\n ble: true,\n dev: true,\n timeout: 10,\n vibrate: true,\n beep: true,\n timezone: 0,\n HID : false,\n HIDGestures: false,\n debug: false,\n clock: null\n };\n setLCDTimeout(settings.timeout);\n updateSettings();\n}\n\ntry {\n settings = storage.readJSON('setting.json');\n} catch (e) {}\nif (!settings) resetSettings();\n\nconst boolFormat = (v) => v ? \"On\" : \"Off\";\n\nfunction showMainMenu() {\n const mainmenu = {\n '': { 'title': 'Settings' },\n 'BLE': {\n value: settings.ble,\n format: boolFormat,\n onchange: () => {\n settings.ble = !settings.ble;\n updateSettings();\n }\n },\n 'Programmable': {\n value: settings.dev,\n format: boolFormat,\n onchange: () => {\n settings.dev = !settings.dev;\n updateSettings();\n }\n },\n 'LCD Timeout': {\n value: settings.timeout,\n min: 0,\n max: 60,\n step: 5,\n onchange: v => {\n settings.timeout = 0 | v;\n updateSettings();\n Bangle.setLCDTimeout(settings.timeout);\n }\n },\n 'Beep': {\n value: settings.beep,\n format: boolFormat,\n onchange: () => {\n settings.beep = !settings.beep;\n updateSettings();\n if (settings.beep) {\n Bangle.beep(1);\n }\n }\n },\n 'Vibration': {\n value: settings.vibrate,\n format: boolFormat,\n onchange: () => {\n settings.vibrate = !settings.vibrate;\n updateSettings();\n if (settings.vibrate) {\n VIBRATE.write(1);\n setTimeout(()=>VIBRATE.write(0), 10);\n }\n }\n },\n 'Select Clock': showClockMenu,\n 'Time Zone': {\n value: settings.timezone,\n min: -11,\n max: 12,\n step: 1,\n onchange: v => {\n settings.timezone = 0 | v;\n updateSettings();\n }\n },\n 'HID': {\n value: settings.HID,\n format: boolFormat,\n onchange: () => {\n settings.HID = !settings.HID;\n updateSettings();\n }\n },\n 'HID Gestures': {\n value: settings.HIDGestures,\n format: boolFormat,\n onchange: () => {\n settings.HIDGestures = !settings.HIDGestures;\n updateSettings();\n }\n },\n 'Debug': {\n value: settings.debug,\n format: boolFormat,\n onchange: () => {\n settings.debug = !settings.debug;\n updateSettings();\n }\n },\n 'Set Time': showSetTimeMenu,\n 'Make Connectable': makeConnectable,\n 'Reset Settings': showResetMenu,\n 'Turn Off': Bangle.off,\n '< Back': load\n };\n return Bangle.menu(mainmenu);\n}\n\nfunction showResetMenu() {\n const resetmenu = {\n '': { 'title': 'Reset' },\n '< Back': showMainMenu,\n 'Reset Settings': () => {\n E.showPrompt('Reset Settings?').then((v) => {\n if (v) {\n E.showMessage('Resetting');\n resetSettings();\n }\n setTimeout(showMainMenu, 50);\n });\n },\n // this is include for debugging. remove for production\n /*'Erase': () => {\n storage.erase('=setting');\n storage.erase('-setting');\n storage.erase('setting.json');\n storage.erase('*setting');\n storage.erase('+setting');\n E.reboot();\n }*/\n };\n return Bangle.menu(resetmenu);\n}\n\nfunction makeConnectable() {\n try { NRF.wake(); } catch(e) {}\n Bluetooth.setConsole(1);\n var name=\"Bangle.js \"+NRF.getAddress().substr(-5).replace(\":\",\"\");\n E.showPrompt(name+\"\\nStay Connectable?\",{title:\"Connectable\"}).then(r=>{\n if (settings.ble!=r) {\n settings.ble = r;\n updateSettings();\n }\n if (!r) try { NRF.sleep(); } catch(e) {}\n showMainMenu();\n });\n}\nfunction showClockMenu() {\n var clockApps = require(\"Storage\").list().filter(a=>a[0]=='+').map(app=>{\n try { return require(\"Storage\").readJSON(app); }\n catch (e) {}\n }).filter(app=>app.type==\"clock\").sort((a, b) => a.sortorder - b.sortorder);\n const clockMenu = {\n '': {\n 'title': 'Select Clock',\n },\n '< Back': showMainMenu,\n };\n clockApps.forEach((app,index) => {\n var label = app.name;\n if ((!settings.clock && index === 0) || (settings.clock === app.src)) {\n label = \"* \"+label;\n }\n clockMenu[label] = () => {\n if (settings.clock !== app.src) {\n settings.clock = app.src;\n updateSettings();\n showMainMenu();\n }\n };\n });\n if (clockApps.length === 0) {\n clockMenu[\"No Clocks Found\"] = () => {};\n }\n return Bangle.menu(clockMenu);\n}\n\n\n\nfunction showSetTimeMenu() {\n d = new Date();\n const timemenu = {\n '': {\n 'title': 'Set Time',\n 'predraw': function() {\n d = new Date();\n timemenu.Hour.value = d.getHours();\n timemenu.Minute.value = d.getMinutes();\n timemenu.Second.value = d.getSeconds();\n timemenu.Date.value = d.getDate();\n timemenu.Month.value = d.getMonth() + 1;\n timemenu.Year.value = d.getFullYear();\n }\n },\n '< Back': showMainMenu,\n 'Hour': {\n value: d.getHours(),\n min: 0,\n max: 23,\n step: 1,\n onchange: v => {\n d = new Date();\n d.setHours(v);\n setTime(d.getTime()/1000);\n }\n },\n 'Minute': {\n value: d.getMinutes(),\n min: 0,\n max: 59,\n step: 1,\n onchange: v => {\n d = new Date();\n d.setMinutes(v);\n setTime(d.getTime()/1000);\n }\n },\n 'Second': {\n value: d.getSeconds(),\n min: 0,\n max: 59,\n step: 1,\n onchange: v => {\n d = new Date();\n d.setSeconds(v);\n setTime(d.getTime()/1000);\n }\n },\n 'Date': {\n value: d.getDate(),\n min: 1,\n max: 31,\n step: 1,\n onchange: v => {\n d = new Date();\n d.setDate(v);\n setTime(d.getTime()/1000);\n }\n },\n 'Month': {\n value: d.getMonth() + 1,\n min: 1,\n max: 12,\n step: 1,\n onchange: v => {\n d = new Date();\n d.setMonth(v - 1);\n setTime(d.getTime()/1000);\n }\n },\n 'Year': {\n value: d.getFullYear(),\n min: 2019,\n max: 2100,\n step: 1,\n onchange: v => {\n d = new Date();\n d.setFullYear(v);\n setTime(d.getTime()/1000);\n }\n }\n };\n return Bangle.menu(timemenu);\n}\n\nshowMainMenu();\n"); +require('Storage').write("=setting","Bangle.HID = E.toUint8Array(atob(\"BQEJBqEBhQIFBxngKecVACUBdQGVCIEClQF1CIEBlQV1AQUIGQEpBZEClQF1A5EBlQZ1CBUAJXMFBxkAKXOBAAkFFQAm/wB1CJUCsQLABQwJAaEBhQEVACUBdQGVAQm1gQIJtoECCbeBAgm4gQIJzYECCeKBAgnpgQIJ6oECwA==\"));\n\n(function() {\n var s = require('Storage').readJSON('setting.json');\n var adv = { uart: true };\n if (s.ble) {\n if (s.dev)\n Bluetooth.setConsole(true);\n else\n Terminal.setConsole(true);\n if (s.HID) {\n adv.hid = Bangle.HID;\n } else\n delete Bangle.HID;\n }\n NRF.setServices({}, adv);\n // we just reset, so BLE should be on\n try { // disable advertising if BLE should be off\n if (!s.ble) NRF.sleep();\n else NRF.wake();\n } catch(e) {}\n if (!s.vibrate) Bangle.buzz=Promise.resolve;\n if (!s.beep) Bangle.beep=Promise.resolve;\n Bangle.setLCDTimeout(s.timeout);\n if (!s.timeout) Bangle.setLCDPower(1);\n E.setTimeZone(s.timezone);\n})()\n"); +require('Storage').write("setting.json",{ ble: true, // Bluetooth enabled by default dev: true, // Espruino IDE enabled by default timeout: 10, // Default LCD timeout in seconds diff --git a/index.html b/index.html index 0141365b7..eeac78468 100644 --- a/index.html +++ b/index.html @@ -6,7 +6,7 @@ - Bangle.js loader + Bangle.js App Loader