Merge branch 'master' of https://github.com/espruino/BangleApps
# Conflicts: # apps.jsonpull/218/head
31
README.md
|
@ -120,7 +120,7 @@ Apps are listed in the Bangle.js menu, accessible from a clock app via the middl
|
|||
|
||||
#### `app-icon.js`
|
||||
|
||||
The icon image and short description is used in the menu entry as selection possibility.
|
||||
The icon image and short description is used in Bangle.js's launcher.
|
||||
|
||||
Use the Espruino [image converter](https://www.espruino.com/Image+Converter) and upload your `app.png` file.
|
||||
|
||||
|
@ -136,11 +136,14 @@ Follow this steps to create a readable icon as image string.
|
|||
Replace this line with the image converter output:
|
||||
|
||||
```
|
||||
require("heatshrink").decompress(atob("mEwwJC/AH4A/AH4AgA=="));
|
||||
require("heatshrink").decompress(atob("mEwwJC/AH4A/AH4AgA=="))
|
||||
```
|
||||
|
||||
Keep in mind to use this converter for creating images you like to draw with `g.drawImage()` with your app.
|
||||
You can also use this converter for creating images you like to draw with `g.drawImage()` with your app.
|
||||
|
||||
Apps that need widgets can call `Bangle.loadWidgets()` **once** at startup to load
|
||||
them, and then `Bangle.drawWidgets()` to draw them onto the screen whenever the app
|
||||
has call to completely clear the screen. Widgets themselves will update as and when needed.
|
||||
|
||||
### Widget Example
|
||||
|
||||
|
@ -149,6 +152,23 @@ 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.js` - widget code
|
||||
|
||||
Widgets are just small bits of code that run whenever an app that supports them
|
||||
calls `Bangle.loadWidgets()`. If they want to display something in the 24px high
|
||||
widget bars at the top and bottom of the screen they can add themselves to
|
||||
the global `WIDGETS` array with:
|
||||
|
||||
```
|
||||
WIDGETS["mywidget"]={
|
||||
area:"tl", // tl (top left), tr (top right), bl (bottom left), br (bottom right)
|
||||
width: 24, // how wide is the widget? You can change this and call Bangle.drawWidgets() to re-layout
|
||||
draw:draw // called to draw the widget
|
||||
};
|
||||
```
|
||||
|
||||
When the widget is to be drawn, `x` and `y` values are set up in `WIDGETS["mywidget"]`
|
||||
and `draw` can then use `this.x` and `this.y` to figure out where it needs to draw to.
|
||||
|
||||
|
||||
### `app.info` format
|
||||
|
||||
This is the file that's **auto-generated** and loaded onto Bangle.js by the App Loader,
|
||||
|
@ -306,6 +326,11 @@ See [apps/gpsrec/interface.html](the GPS Recorder) for a full example.
|
|||
|
||||
- 'Welcome' apps define a file called `welcome.js` which the booloader picks up. This then chain-loads the welcome app itself.
|
||||
|
||||
- 'Alarm' apps define a file called `alarm.js` which handles the actual alarm window.
|
||||
|
||||
- Locale is handled by `require("locale")`. An app may create a `locale` file in Storage which is
|
||||
a module that overwrites Bangle.js's default locale.
|
||||
|
||||
|
||||
### Graphic areas
|
||||
|
||||
|
|
105
apps.json
|
@ -2,7 +2,7 @@
|
|||
{ "id": "boot",
|
||||
"name": "Bootloader",
|
||||
"icon": "bootloader.png",
|
||||
"version":"0.07",
|
||||
"version":"0.10",
|
||||
"description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings",
|
||||
"tags": "tool,system",
|
||||
"type":"bootloader",
|
||||
|
@ -28,7 +28,7 @@
|
|||
{ "id": "about",
|
||||
"name": "About",
|
||||
"icon": "app.png",
|
||||
"version":"0.01",
|
||||
"version":"0.04",
|
||||
"description": "Bangle.js About page - showing software version, stats, and a collaborative mural from the Bangle.js KickStarter backers",
|
||||
"tags": "tool,system",
|
||||
"allow_emulator":true,
|
||||
|
@ -37,10 +37,23 @@
|
|||
{"name":"about.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "locale",
|
||||
"name": "Languages",
|
||||
"icon": "locale.png",
|
||||
"version":"0.01",
|
||||
"description": "Translations for different countries",
|
||||
"tags": "tool,system,locale,translate",
|
||||
"type": "locale",
|
||||
"custom":"locale.html",
|
||||
"storage": [
|
||||
{"name":"locale"}
|
||||
],
|
||||
"sortorder" : -10
|
||||
},
|
||||
{ "id": "welcome",
|
||||
"name": "Welcome",
|
||||
"icon": "app.png",
|
||||
"version":"0.03",
|
||||
"version":"0.04",
|
||||
"description": "Appears at first boot and explains how to use Bangle.js",
|
||||
"tags": "start,welcome",
|
||||
"allow_emulator":true,
|
||||
|
@ -53,7 +66,7 @@
|
|||
{ "id": "gbridge",
|
||||
"name": "Gadgetbridge",
|
||||
"icon": "app.png",
|
||||
"version":"0.03",
|
||||
"version":"0.04",
|
||||
"description": "The default notification handler for Gadgetbridge notifications from Android",
|
||||
"tags": "tool,system,android,widget",
|
||||
"storage": [
|
||||
|
@ -79,7 +92,7 @@
|
|||
{ "id": "setting",
|
||||
"name": "Settings",
|
||||
"icon": "settings.png",
|
||||
"version":"0.05",
|
||||
"version":"0.06",
|
||||
"description": "A menu for setting up Bangle.js",
|
||||
"tags": "tool,system",
|
||||
"storage": [
|
||||
|
@ -93,7 +106,7 @@
|
|||
"name": "Default Alarm",
|
||||
"shortName":"Alarms",
|
||||
"icon": "app.png",
|
||||
"version":"0.02",
|
||||
"version":"0.04",
|
||||
"description": "Set and respond to alarms",
|
||||
"tags": "tool,alarm,widget",
|
||||
"storage": [
|
||||
|
@ -130,16 +143,17 @@
|
|||
{"name":"aclock.img","url":"clock-analog-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "clck3x2",
|
||||
"name": "3x2 Pixel Clock",
|
||||
"icon": "clock3x2.png",
|
||||
"version":"0.03",
|
||||
"description": "This is a simple clock using minimalistic 3x2 pixel numerical digits",
|
||||
{ "id": "clock2x3",
|
||||
"name": "2x3 Pixel Clock",
|
||||
"icon": "clock2x3.png",
|
||||
"version":"0.04",
|
||||
"description": "This is a simple clock using minimalist 2x3 pixel numerical digits",
|
||||
"tags": "clock",
|
||||
"type": "clock",
|
||||
"allow_emulator":true,
|
||||
"storage": [
|
||||
{"name":"clck3x2.app.js","url":"clock3x2.js"},
|
||||
{"name":"clck3x2.img","url":"clock3x2-icon.js","evaluate":true}
|
||||
{"name":"clock2x3.app.js","url":"clock2x3-app.js"},
|
||||
{"name":"clock2x3.img","url":"clock2x3-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "trex",
|
||||
|
@ -236,7 +250,7 @@
|
|||
{ "id": "gpsrec",
|
||||
"name": "GPS Recorder",
|
||||
"icon": "app.png",
|
||||
"version":"0.04",
|
||||
"version":"0.06",
|
||||
"interface": "interface.html",
|
||||
"description": "Application that allows you to record a GPS track. Can run in background",
|
||||
"tags": "tool,outdoors,gps,widget",
|
||||
|
@ -247,6 +261,20 @@
|
|||
{"name":"gpsrec.wid.js","url":"widget.js"}
|
||||
]
|
||||
},
|
||||
{ "id": "heart",
|
||||
"name": "Heart Rate Recorder",
|
||||
"icon": "app.png",
|
||||
"version":"0.01",
|
||||
"interface": "interface.html",
|
||||
"description": "Application that allows you to record your heart rate. Can run in background",
|
||||
"tags": "tool,health,widget",
|
||||
"storage": [
|
||||
{"name":"heart.app.js","url":"app.js"},
|
||||
{"name":"heart.json","url":"app-settings.json","evaluate":true},
|
||||
{"name":"heart.img","url":"app-icon.js","evaluate":true},
|
||||
{"name":"heart.wid.js","url":"widget.js"}
|
||||
]
|
||||
},
|
||||
{ "id": "slevel",
|
||||
"name": "Spirit Level",
|
||||
"icon": "spiritlevel.png",
|
||||
|
@ -272,7 +300,7 @@
|
|||
{ "id": "widbat",
|
||||
"name": "Battery Level Widget",
|
||||
"icon": "widget.png",
|
||||
"version":"0.02",
|
||||
"version":"0.04",
|
||||
"description": "Show the current battery level and charging status in the top right of the clock",
|
||||
"tags": "widget,battery",
|
||||
"type":"widget",
|
||||
|
@ -283,7 +311,7 @@
|
|||
{ "id": "widbt",
|
||||
"name": "Bluetooth Widget",
|
||||
"icon": "widget.png",
|
||||
"version":"0.01",
|
||||
"version":"0.03",
|
||||
"description": "Show the current Bluetooth connection status in the top right of the clock",
|
||||
"tags": "widget,bluetooth",
|
||||
"type":"widget",
|
||||
|
@ -305,7 +333,7 @@
|
|||
{ "id": "widhrm",
|
||||
"name": "Simple Heart Rate widget",
|
||||
"icon": "widget.png",
|
||||
"version":"0.01",
|
||||
"version":"0.03",
|
||||
"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",
|
||||
|
@ -316,7 +344,7 @@
|
|||
{ "id": "stetho",
|
||||
"name": "Stethoscope",
|
||||
"icon": "stetho.png",
|
||||
"version":"0.0198",
|
||||
"version":"0.01",
|
||||
"description": "Hear your heart rate",
|
||||
"tags": "health",
|
||||
"storage": [
|
||||
|
@ -459,7 +487,7 @@
|
|||
{ "id": "widnceu",
|
||||
"name": "NCEU Logo Widget",
|
||||
"icon": "widget.png",
|
||||
"version":"0.01",
|
||||
"version":"0.02",
|
||||
"description": "Show the NodeConf EU logo in the top left",
|
||||
"tags": "widget",
|
||||
"type":"widget",
|
||||
|
@ -467,9 +495,6 @@
|
|||
{"name":"widnceu.wid.js","url":"widget.js"}
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
|
||||
{ "id": "sclock",
|
||||
"name": "Simple Clock",
|
||||
"icon": "clock-simple.png",
|
||||
|
@ -638,7 +663,7 @@
|
|||
"id": "gpsinfo",
|
||||
"name": "GPS Info",
|
||||
"icon": "gps-info.png",
|
||||
"version":"0.01",
|
||||
"version":"0.02",
|
||||
"description": "An application that displays information about altitude, lat/lon, satellites and time",
|
||||
"tags": "gps",
|
||||
"type": "app",
|
||||
|
@ -691,7 +716,7 @@
|
|||
{ "id": "widclk",
|
||||
"name": "Digital clock widget",
|
||||
"icon": "widget.png",
|
||||
"version":"0.01",
|
||||
"version":"0.03",
|
||||
"description": "A simple digital clock widget",
|
||||
"tags": "widget,clock",
|
||||
"type":"widget",
|
||||
|
@ -702,7 +727,7 @@
|
|||
{ "id": "widpedom",
|
||||
"name": "Pedometer widget",
|
||||
"icon": "widget.png",
|
||||
"version":"0.06",
|
||||
"version":"0.08",
|
||||
"description": "Daily pedometer widget",
|
||||
"tags": "widget",
|
||||
"type":"widget",
|
||||
|
@ -753,6 +778,7 @@
|
|||
{ "id": "flagrse",
|
||||
"name": "Espruino Flag Raiser",
|
||||
"icon": "app.png",
|
||||
"version":"0.01",
|
||||
"description": "App to send a command to another Espruino to cause it to raise a flag",
|
||||
"tags": "",
|
||||
"storage": [
|
||||
|
@ -774,6 +800,19 @@
|
|||
{"name":"pipboy.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "torch",
|
||||
"name": "Torch",
|
||||
"shortName":"Torch",
|
||||
"icon": "app.png",
|
||||
"version":"0.01",
|
||||
"description": "Turns screen white to help you see in the dark. Select from the launcher or press BTN3 four times in quick succession to start when in normal clock mode",
|
||||
"tags": "tool,torch",
|
||||
"storage": [
|
||||
{"name":"torch.app.js","url":"app.js"},
|
||||
{"name":"torch.wid.js","url":"widget.js"},
|
||||
{"name":"torch.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "wohrm",
|
||||
"name": "Workout Heart Rate Monitor",
|
||||
"icon": "wohrm.png",
|
||||
|
@ -783,9 +822,19 @@
|
|||
"type": "app",
|
||||
"allow_emulator":true,
|
||||
"storage": [
|
||||
{"name":"+wohrm","url":"wohrm.json"},
|
||||
{"name":"-wohrm","url":"wohrm.js"},
|
||||
{"name":"*wohrm","url":"wohrm-icon.js","evaluate":true}
|
||||
{"name":"wohrm.app.js","url":"wohrm.js"},
|
||||
{"name":"wohrm.img","url":"wohrm-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "widid",
|
||||
"name": "Bluetooth ID Widget",
|
||||
"icon": "widget.png",
|
||||
"version":"0.02",
|
||||
"description": "Display the last two tuple of your Bangle.js MAC address in the widget section. This is useful for figuring out which Bangle.js to connect to if you have more than one Bangle.js!",
|
||||
"tags": "widget,address,mac",
|
||||
"type":"widget",
|
||||
"storage": [
|
||||
{"name":"widid.wid.js","url":"widget.js"}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
"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}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
"tags": "widget",
|
||||
"type": "widget",
|
||||
"storage": [
|
||||
{"name":"+7chname","url":"widget.json"},
|
||||
{"name":"=7chname","url":"widget.js"}
|
||||
{"name":"7chname.wid.js","url":"widget.js"}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
/* run widgets in their own function scope so they don't interfere with
|
||||
currently-running apps */
|
||||
(() => {
|
||||
// add the width
|
||||
var xpos = WIDGETPOS.tr-24;/*<the widget width>*/;
|
||||
WIDGETPOS.tr-= 28;/* the widget width plus some extra pixel to keep distance to others */;
|
||||
|
||||
// draw your widget at xpos
|
||||
function draw() {
|
||||
g.reset(); // reset the graphics context to defaults (color/font/etc)
|
||||
// add your code
|
||||
g.drawString("X", this.x, this.y);
|
||||
}
|
||||
|
||||
// add your widget
|
||||
WIDGETS["mywidget"]={draw:draw};
|
||||
|
||||
WIDGETS["mywidget"]={
|
||||
area:"tl", // tl (top left), tr (top right), bl (bottom left), br (bottom right)
|
||||
width: 28, // how wide is the widget? You can change this and call Bangle.drawWidgets() to re-layout
|
||||
draw:draw // called to draw the widget
|
||||
};
|
||||
})()
|
||||
|
|
|
@ -1 +1,4 @@
|
|||
0.01: New App!
|
||||
0.02: Update version checker for new filename type
|
||||
0.03: Actual pixels as of 5 Mar 2020
|
||||
0.04: Actual pixels as of 9 Mar 2020
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
0.01: New App!
|
||||
0.02: Fix issues with alarm scheduling
|
||||
0.03: More alarm scheduling issues
|
||||
0.04: Tweaks for variable size widget system
|
||||
|
|
|
@ -10,7 +10,7 @@ function formatTime(t) {
|
|||
|
||||
function getCurrentHr() {
|
||||
var time = new Date();
|
||||
return time.getHours()+(time.getMinutes()/60);
|
||||
return time.getHours()+(time.getMinutes()/60)+(time.getSeconds()/3600);
|
||||
}
|
||||
|
||||
function showAlarm(alarm) {
|
||||
|
@ -47,8 +47,8 @@ function showAlarm(alarm) {
|
|||
|
||||
// Check for alarms
|
||||
var day = (new Date()).getDate();
|
||||
var hr = getCurrentHr();
|
||||
var alarms = require("Storage").readJSON("alarm.json")||[];
|
||||
var hr = getCurrentHr()+10000; // get current time - 10s in future to ensure we alarm if we've started the app a tad early
|
||||
var alarms = require("Storage").readJSON("alarm.json",1)||[];
|
||||
var active = alarms.filter(a=>a.on&&(a.hr<hr)&&(a.last!=day));
|
||||
if (active.length) {
|
||||
// if there's an alarm, show it
|
||||
|
@ -56,5 +56,5 @@ if (active.length) {
|
|||
showAlarm(active[0]);
|
||||
} else {
|
||||
// otherwise just go back to default app
|
||||
load();
|
||||
setTimeout(load, 100);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
var alarms = require("Storage").readJSON("alarm.json")||[];
|
||||
var alarms = require("Storage").readJSON("alarm.json",1)||[];
|
||||
/*alarms = [
|
||||
{ on : true,
|
||||
hr : 6.5, // hours + minutes/60
|
||||
|
@ -19,7 +19,7 @@ function formatTime(t) {
|
|||
|
||||
function getCurrentHr() {
|
||||
var time = new Date();
|
||||
return time.getHours()+(time.getMinutes()/60);
|
||||
return time.getHours()+(time.getMinutes()/60)+(time.getSeconds()/3600);
|
||||
}
|
||||
|
||||
function showMainMenu() {
|
||||
|
|
|
@ -1,17 +1,11 @@
|
|||
(() => {
|
||||
var alarms = require('Storage').readJSON('alarm.json')||[];
|
||||
var alarms = require('Storage').readJSON('alarm.json',1)||[];
|
||||
alarms = alarms.filter(alarm=>alarm.on);
|
||||
if (!alarms.length) return;
|
||||
if (!alarms.length) return; // no alarms, no widget!
|
||||
delete alarms;
|
||||
// add the width
|
||||
var xpos = WIDGETPOS.tl;
|
||||
WIDGETPOS.tl += 24;/* the widget width plus some extra pixel to keep distance to others */;
|
||||
|
||||
// draw your widget at xpos
|
||||
|
||||
// add the widget
|
||||
WIDGETS["alarm"]={draw:function() {
|
||||
WIDGETS["alarm"]={area:"tl",width:24,draw:function() {
|
||||
g.setColor(-1);
|
||||
g.drawImage(atob("GBgBAAAAAAAAABgADhhwDDwwGP8YGf+YMf+MM//MM//MA//AA//AA//AA//AA//AA//AB//gD//wD//wAAAAADwAABgAAAAAAAAA"),xpos,0);
|
||||
g.drawImage(atob("GBgBAAAAAAAAABgADhhwDDwwGP8YGf+YMf+MM//MM//MA//AA//AA//AA//AA//AA//AB//gD//wD//wAAAAADwAABgAAAAAAAAA"),this.x,this.y);
|
||||
}};
|
||||
})()
|
||||
|
|
|
@ -4,3 +4,7 @@
|
|||
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
|
||||
0.08: Fix issues if BLE=off, 'Make Connectable' is chosen, and the loader resets Bangle.js (fix #108)
|
||||
0.09: Only check GPS for time after a fresh boot
|
||||
0.10: Stop users calling save() (fix #125)
|
||||
If Debug info is set to 'show' don't move to Terminal if connected!
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// This ALWAYS runs at boot
|
||||
E.setFlags({pretokenise:1});
|
||||
// Load settings...
|
||||
var s = require('Storage').readJSON('setting.json')||{};
|
||||
var s = require('Storage').readJSON('setting.json',1)||{};
|
||||
if (s.ble!==false) {
|
||||
if (s.HID) { // Human interface device
|
||||
Bangle.HID = E.toUint8Array(atob("BQEJBqEBhQIFBxngKecVACUBdQGVCIEClQF1CIEBlQV1AQUIGQEpBZEClQF1A5EBlQZ1CBUAJXMFBxkAKXOBAAkFFQAm/wB1CJUCsQLABQwJAaEBhQEVACUBdQGVAQm1gQIJtoECCbeBAgm4gQIJzYECCeKBAgnpgQIJ6oECwA=="));
|
||||
|
@ -12,11 +12,12 @@ 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)
|
||||
if (s.log && !NRF.getSecurityStatus().connected) 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();
|
||||
// we just reset, so BLE should be on.
|
||||
// Don't disconnect if something is already connected to us
|
||||
if (s.ble===false && !NRF.getSecurityStatus().connected) NRF.sleep();
|
||||
// Set time, vibrate, beep, etc
|
||||
if (!s.vibrate) Bangle.buzz=Promise.resolve;
|
||||
if (!s.beep) Bangle.beep=Promise.resolve;
|
||||
|
@ -24,45 +25,27 @@ Bangle.setLCDTimeout(s.timeout);
|
|||
if (!s.timeout) Bangle.setLCDPower(1);
|
||||
E.setTimeZone(s.timezone);
|
||||
delete s;
|
||||
// stop users doing bad things!
|
||||
global.save = function() { throw new Error("You can't use save() on Bangle.js without overwriting the bootloader!"); }
|
||||
// check for alarms
|
||||
function checkAlarm() {
|
||||
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)+(time.getSeconds()/3600);
|
||||
if (!require('Storage').read("alarm.js")) {
|
||||
console.log("No alarm app!");
|
||||
require('Storage').write('alarm.json',"[]")
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
var alarms = require('Storage').readJSON('alarm.json',1)||[];
|
||||
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)+(time.getSeconds()/3600);
|
||||
if (!require('Storage').read("alarm.js")) {
|
||||
console.log("No alarm app!");
|
||||
require('Storage').write('alarm.json',"[]")
|
||||
} else {
|
||||
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");
|
||||
Bangle.on('GPS',function cb(g) {
|
||||
Bangle.setGPSPower(0);
|
||||
Bangle.removeListener("GPS",cb);
|
||||
if (!g.time || (g.time.getFullYear()<2000) ||
|
||||
(g.time.getFullYear()==2250)) {
|
||||
//console.log("GPS receiver's time not set");
|
||||
return;
|
||||
}
|
||||
setTime(g.time.getTime()/1000);
|
||||
//console.log("GPS time",g.time.toString());
|
||||
checkAlarm();
|
||||
});
|
||||
Bangle.setGPSPower(1);
|
||||
} else checkAlarm();
|
||||
delete checkAlarm;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// This runs after a 'fresh' boot
|
||||
var settings={};
|
||||
try { settings = require("Storage").readJSON('setting.json'); } catch (e) {}
|
||||
var settings=require("Storage").readJSON('setting.json',1)||{};
|
||||
if (!settings.welcomed && require("Storage").read("welcome.js")!==undefined) {
|
||||
setTimeout(()=>load("welcome.js"));
|
||||
} else {
|
||||
|
@ -8,16 +7,32 @@ if (!settings.welcomed && require("Storage").read("welcome.js")!==undefined) {
|
|||
var clockApp = settings.clock;
|
||||
if (clockApp) clockApp = require("Storage").read(clockApp)
|
||||
if (!clockApp) {
|
||||
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);
|
||||
var clockApps = require("Storage").list(/\.info$/).map(app=>require("Storage").readJSON(app,1)||{}).filter(app=>app.type=="clock").sort((a, b) => a.sortorder - b.sortorder);
|
||||
if (clockApps && clockApps.length > 0)
|
||||
clockApp = require("Storage").read(clockApps[0].src);
|
||||
delete clockApps;
|
||||
}
|
||||
if (clockApp) eval(clockApp);
|
||||
else E.showMessage("No Clock Found");
|
||||
delete clockApp;
|
||||
if (!clockApp) clockApp='E.showMessage("No Clock Found")';
|
||||
delete settings;
|
||||
// check to see if our clock is wrong - if it is use GPS time
|
||||
if ((new Date()).getFullYear()==1970) {
|
||||
E.showMessage("Searching for\nGPS 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)) {
|
||||
// GPS receiver's time not set - just boot clock anyway
|
||||
eval(clockApp);delete clockApp;
|
||||
return;
|
||||
}
|
||||
// We have a GPS time. Set time and reboot (to load alarms properly)
|
||||
setTime(g.time.getTime()/1000);
|
||||
load();
|
||||
});
|
||||
Bangle.setGPSPower(1);
|
||||
} else {
|
||||
eval(clockApp);
|
||||
delete clockApp;
|
||||
}
|
||||
}
|
||||
delete settings;
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
0.02: Modified for use with new bootloader and firmware
|
||||
0.03: Added 'reset' so we don't get the font color from widgets
|
||||
0.04: Changed name from clck3x2 to clock2x3
|
|
@ -76,8 +76,7 @@ function drawTime() {
|
|||
}
|
||||
|
||||
let t = d.getSeconds()*1000 + d.getMilliseconds();
|
||||
let delta = (60000 - t) % 60000; // time till next minute
|
||||
idTimeout = setTimeout(drawTime, delta);
|
||||
idTimeout = setTimeout(drawTime, 60000 - t); // time till next minute
|
||||
}
|
||||
|
||||
// special function to handle display switch on
|
Before Width: | Height: | Size: 145 B After Width: | Height: | Size: 145 B |
|
@ -69,7 +69,7 @@ function showApps() {
|
|||
var list = storage.list(/\.info$/).filter((a)=> {
|
||||
return a !== 'setting.info';
|
||||
}).sort().map((app) => {
|
||||
var ret = storage.readJSON(app);
|
||||
var ret = storage.readJSON(app,1)||{};
|
||||
ret[''] = app;
|
||||
return ret;
|
||||
});
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
0.01: Initial version
|
||||
0.02: Increase contrast (darker notification background, white text)
|
||||
0.03: Gadgetbridge widget now shows connection state
|
||||
0.04: Tweaks for variable size widget system
|
|
@ -109,19 +109,17 @@
|
|||
function draw() {
|
||||
g.setColor(-1);
|
||||
if (NRF.getSecurityStatus().connected)
|
||||
g.drawImage(require("heatshrink").decompress(atob("i0WwgHExAABCIwJCBYwJEBYkIBQ2ACgvzCwoECx/z/AKDD4WD+YLBEIYKCx//+cvnAKCBwU/mc4/8/HYv//Ev+Y4EEAePn43DBQkzn4rCEIoABBIwKHO4cjmczK42I6mqlqEEBQeIBQaDED4IgDUhi6KaBbmIA==")),xpos+1,1);
|
||||
g.drawImage(require("heatshrink").decompress(atob("i0WwgHExAABCIwJCBYwJEBYkIBQ2ACgvzCwoECx/z/AKDD4WD+YLBEIYKCx//+cvnAKCBwU/mc4/8/HYv//Ev+Y4EEAePn43DBQkzn4rCEIoABBIwKHO4cjmczK42I6mqlqEEBQeIBQaDED4IgDUhi6KaBbmIA==")),this.x+1,this.y+1);
|
||||
else
|
||||
g.drawImage(require("heatshrink").decompress(atob("i0WwQFC1WgAgYFDAgIFClQFCwEK1W/AoIPB1f+CAMq1f7/WqwQPB/fq1Gq1/+/4dC/2/CAIaB/YbBAAO///qAoX/B4QbBDQQ7BDQQrBAAWoIIIACIIIVC0ECB4cACAZiBAoRtCAoIDBA")),xpos+1,1);
|
||||
g.drawImage(require("heatshrink").decompress(atob("i0WwQFC1WgAgYFDAgIFClQFCwEK1W/AoIPB1f+CAMq1f7/WqwQPB/fq1Gq1/+/4dC/2/CAIaB/YbBAAO///qAoX/B4QbBDQQ7BDQQrBAAWoIIIACIIIVC0ECB4cACAZiBAoRtCAoIDBA")),this.x+1,this.y+1);
|
||||
}
|
||||
function changed() {
|
||||
draw();
|
||||
WIDGETS["gbridgew"].draw();
|
||||
g.flip();// turns screen on
|
||||
}
|
||||
NRF.on('connected',changed);
|
||||
NRF.on('disconnected',changed);
|
||||
|
||||
var xpos = WIDGETPOS.tl;
|
||||
WIDGETPOS.tl+=24;
|
||||
WIDGETS["gbridgew"]={draw:draw};
|
||||
WIDGETS["gbridgew"]={area:"tl",width:24,draw:draw};
|
||||
|
||||
})();
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.02: Ensure screen doesn't display garbage at startup
|
|
@ -2,6 +2,7 @@ var img = require("heatshrink").decompress(atob("mEwghC/AH4AKg9wC6t3u4uVC6wWBI6t
|
|||
|
||||
Bangle.setGPSPower(1);
|
||||
Bangle.setLCDMode("doublebuffered");
|
||||
E.showMessage("Loading..."); // avoid showing rubbish on screen
|
||||
|
||||
var lastFix = {
|
||||
fix: 0,
|
||||
|
|
|
@ -2,3 +2,5 @@
|
|||
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
|
||||
0.05: Tweaks for variable size widget system
|
||||
0.06: Ensure widget update itself (fix #118) and change to using icons
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
var settings = require("Storage").readJSON("gpsrec.json")||{};
|
||||
var settings = require("Storage").readJSON("gpsrec.json",1)||{};
|
||||
|
||||
function getFN(n) {
|
||||
return ".gpsrc"+n.toString(36);
|
||||
|
|
|
@ -176,7 +176,7 @@ function getTrackList() {
|
|||
for (var i=0;i<buttons.length;i++) {
|
||||
buttons[i].addEventListener("click",event => {
|
||||
var button = event.currentTarget;
|
||||
var trackid = button.getAttribute("trackid");
|
||||
var trackid = parseInt(button.getAttribute("trackid"));
|
||||
var task = button.getAttribute("task");
|
||||
if (task=="delete") {
|
||||
showModal("Deleting Track...");
|
||||
|
|
|
@ -1,44 +1,29 @@
|
|||
(() => {
|
||||
// add the width
|
||||
var xpos = WIDGETPOS.tl;
|
||||
WIDGETPOS.tl += 24;/* the widget width plus some extra pixel to keep distance to others */;
|
||||
var settings = {};
|
||||
var hasFix = false;
|
||||
var fixToggle = false; // toggles once for each reading
|
||||
var gpsTrack; // file for GPS track
|
||||
var periodCtr = 0;
|
||||
|
||||
// draw your widget at xpos
|
||||
// draw your widget
|
||||
function draw() {
|
||||
if (!settings.recording) return;
|
||||
g.reset();
|
||||
g.setFont("4x6");
|
||||
g.setFontAlign(0,0);
|
||||
g.clearRect(xpos,0,xpos+23,23);
|
||||
|
||||
if (!settings.recording) {
|
||||
g.setColor("#606060");
|
||||
g.drawImage(atob("GBgCAAAAAAAAAAQAAAAAAD8AAAAAAP/AAAAAAP/wAAAAAH/8C9AAAB/8L/QAAAfwv/wAAAHS//wAAAAL//gAAAAf/+AAAAAf/4AAAAL//gAAAAD/+DwAAAB/Uf8AAAAfA//AAAACAf/wAAAAAH/0AAAAAB/wAAAAAAfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"),this.x,this.y);
|
||||
if (hasFix) {
|
||||
g.setColor("#FF0000");
|
||||
g.drawImage(fixToggle ? atob("CgoCAAAAA0AAOAAD5AAPwAAAAAAAAAAAAAAAAA==") : atob("CgoCAAABw0AcOAHj5A8PwHwAAvgAB/wABUAAAA=="),this.x,this.y+14);
|
||||
} else {
|
||||
g.setColor("#ff0000");
|
||||
if (hasFix) {
|
||||
if (fixToggle) {
|
||||
g.fillCircle(xpos+11,11,9);
|
||||
g.setColor("#000000");
|
||||
} else
|
||||
g.drawCircle(xpos+11,11,9);
|
||||
} else {
|
||||
g.setColor(fixToggle ? "#ff0000" : "#7f0000");
|
||||
g.drawString("NO",xpos+12,5);
|
||||
g.drawString("FIX",xpos+12,19);
|
||||
}
|
||||
g.setColor("#0000FF");
|
||||
if (fixToggle)
|
||||
g.setFont("6x8").drawString("?",this.x,this.y+14);
|
||||
}
|
||||
g.drawString("GPS",xpos+12,12);
|
||||
g.setColor(-1); // change color back to be nice to other apps
|
||||
}
|
||||
|
||||
function onGPS(fix) {
|
||||
hasFix = fix.fix;
|
||||
fixToggle = !fixToggle;
|
||||
draw();
|
||||
WIDGETS["gpsrec"].draw();
|
||||
if (hasFix) {
|
||||
periodCtr--;
|
||||
if (periodCtr<=0) {
|
||||
|
@ -53,25 +38,30 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Called by the GPS app to reload settings and decide what's
|
||||
// Called by the GPS app to reload settings and decide what to do
|
||||
function reload() {
|
||||
settings = require("Storage").readJSON("gpsrec.json")||{};
|
||||
settings = require("Storage").readJSON("gpsrec.json",1)||{};
|
||||
settings.period = settings.period||1;
|
||||
settings.file |= 0;
|
||||
|
||||
Bangle.removeListener('GPS',onGPS);
|
||||
if (settings.recording) {
|
||||
WIDGETS["gpsrec"].width = 24;
|
||||
Bangle.on('GPS',onGPS);
|
||||
Bangle.setGPSPower(1);
|
||||
var n = settings.file.toString(36);
|
||||
gpsTrack = require("Storage").open(".gpsrc"+n,"a");
|
||||
} else {
|
||||
WIDGETS["gpsrec"].width = 0;
|
||||
Bangle.setGPSPower(0);
|
||||
gpsTrack = undefined;
|
||||
}
|
||||
draw();
|
||||
}
|
||||
reload();
|
||||
// add the widget
|
||||
WIDGETS["gpsrec"]={draw:draw,reload:reload};
|
||||
WIDGETS["gpsrec"]={area:"tl",width:24,draw:draw,reload:function() {
|
||||
reload();
|
||||
Bangle.drawWidgets(); // relayout all widgets
|
||||
}};
|
||||
// load settings, set correct widget width
|
||||
reload();
|
||||
})()
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
0.01: New App!
|
||||
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwhC/AH4AWzIAByAHDhIICCpINDDAgIIFpAADBBQuKE4QIIFxgAKC7g9HABSbIBQQXWGxgXEKQxOMC5AhBC66WMC5AuBJ5h3ICoI3LeAwKBBAICBD4TmHC48ACgQCCfxC/HAgYXDL44vFA4YRDAoiOIHAgXFYRAXFBwwIIOw4OGIxKmIC5ylHGAoXIXpBIGLxxIIIx6IJFxwwNCxQwLFxYwLCxgwJFxowJCxwwHFx4wHCyAwFFyIwFCyQYDCygA/AH4AFA"))
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"isRecording":false,
|
||||
"fileNbr":0
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
var settings = require("Storage").readJSON("heart.json",1)||{};
|
||||
|
||||
function getFileNbr(n) {
|
||||
return ".heart"+n.toString(36);
|
||||
}
|
||||
|
||||
function updateSettings() {
|
||||
require("Storage").write("heart.json", settings);
|
||||
if (WIDGETS["heart"])
|
||||
WIDGETS["heart"].reload();
|
||||
}
|
||||
|
||||
function showMainMenu() {
|
||||
const mainMenu = {
|
||||
'': { 'title': 'Heart Recorder' },
|
||||
'RECORD': {
|
||||
value: !!settings.isRecording,
|
||||
format: v=>v?"On":"Off",
|
||||
onchange: v => {
|
||||
settings.isRecording = v;
|
||||
updateSettings();
|
||||
}
|
||||
},
|
||||
'File Number': {
|
||||
value: settings.fileNbr|0,
|
||||
min: 0,
|
||||
max: 35,
|
||||
step: 1,
|
||||
onchange: v => {
|
||||
settings.isRecording = false;
|
||||
settings.fileNbr = v;
|
||||
updateSettings();
|
||||
}
|
||||
},
|
||||
'View Records': viewRecords,
|
||||
'< Back': ()=>{load();}
|
||||
};
|
||||
return E.showMenu(mainMenu);
|
||||
}
|
||||
|
||||
function viewRecords() {
|
||||
const menu = {
|
||||
'': { 'title': 'Heart Records' }
|
||||
};
|
||||
var found = false;
|
||||
for (var n=0;n<36;n++) {
|
||||
var f = require("Storage").open(getFileNbr(n),"r");
|
||||
if (f.readLine()!==undefined) {
|
||||
menu["Record "+n] = viewRecord.bind(null,n);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
menu["No Records Found"] = function(){};
|
||||
menu['< Back'] = showMainMenu;
|
||||
return E.showMenu(menu);
|
||||
}
|
||||
|
||||
function viewRecord(n) {
|
||||
const menu = {
|
||||
'': { 'title': 'Heart Record '+n }
|
||||
};
|
||||
var heartCount = 0;
|
||||
var heartTime;
|
||||
var f = require("Storage").open(getFileNbr(n),"r");
|
||||
var l = f.readLine();
|
||||
if (l!==undefined) {
|
||||
var c = l.split(",");
|
||||
heartTime = new Date(c[0]*1000);
|
||||
}
|
||||
while (l!==undefined) {
|
||||
heartCount++;
|
||||
// TODO: min/max/average of heart rate?
|
||||
l = f.readLine();
|
||||
}
|
||||
if (heartTime)
|
||||
menu[" "+heartTime.toString().substr(4,17)] = function(){};
|
||||
menu[heartCount+" records"] = function(){};
|
||||
// TODO: option to draw it? Just scan through, project using min/max
|
||||
menu['Erase'] = function() {
|
||||
E.showPrompt("Delete Record?").then(function(v) {
|
||||
if (v) {
|
||||
settings.isRecording = false;
|
||||
updateSettings();
|
||||
var f = require("Storage").open(getFileNbr(n),"r");
|
||||
f.erase();
|
||||
viewRecords();
|
||||
} else
|
||||
viewRecord(n);
|
||||
});
|
||||
};
|
||||
menu['< Back'] = viewRecords;
|
||||
print(menu);
|
||||
return E.showMenu(menu);
|
||||
}
|
||||
|
||||
showMainMenu();
|
After Width: | Height: | Size: 883 B |
|
@ -0,0 +1,149 @@
|
|||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="records"></div>
|
||||
|
||||
<div class="modal active" id="status-modal">
|
||||
<div class="modal-overlay"></div>
|
||||
<div class="modal-container">
|
||||
<div class="modal-header">
|
||||
<div class="modal-name h5">Please wait</div>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="content">
|
||||
Loading...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../../lib/interface.js"></script>
|
||||
<script>
|
||||
var domRecords = document.getElementById("records");
|
||||
var domModal = document.getElementById("status-modal");
|
||||
|
||||
function showModal(name) {
|
||||
domModal.querySelector(".content").innerHTML = name;
|
||||
domModal.classList.add("active");
|
||||
}
|
||||
function hideModal(name) {
|
||||
domModal.classList.remove("active");
|
||||
}
|
||||
|
||||
function saveRecord(record,name) {
|
||||
var csv = `${record.map(rec=>[rec.time, rec.bpm, rec.confidence].join(",")).join("\n")}`;
|
||||
var a = document.createElement("a"),
|
||||
file = new Blob([csv], {type: "Comma-separated value file"});
|
||||
var url = URL.createObjectURL(file);
|
||||
a.href = url;
|
||||
a.download = name+".csv";
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
setTimeout(function() {
|
||||
document.body.removeChild(a);
|
||||
window.URL.revokeObjectURL(url);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
|
||||
function recordLineToObject(l, hasRecordNbr) {
|
||||
var t = l.trim().split(",");
|
||||
var n = hasRecordNbr?1:0;
|
||||
var o = {
|
||||
time: parseInt(t[n+0]),
|
||||
bpm: parseFloat(t[n+1]),
|
||||
confidence: parseFloat(t[n+2]),
|
||||
};
|
||||
if (hasRecordNbr)
|
||||
o.number = t[0];
|
||||
return o;
|
||||
}
|
||||
|
||||
function downloadRecord(recordNbr, callback) {
|
||||
showModal("Downloading heart rate record...");
|
||||
Puck.write(`\x10(function() {
|
||||
var f = require("Storage").open(".heart${recordNbr.toString(36)}","r");
|
||||
var l = f.readLine();
|
||||
while (l!==undefined) { Bluetooth.print(l); l = f.readLine(); }
|
||||
})()\n`,recordList=>{
|
||||
hideModal();
|
||||
var record = recordList.trim().split("\n").map(l=>recordLineToObject(l,false));
|
||||
callback(record);
|
||||
});
|
||||
}
|
||||
|
||||
function getRecordList() {
|
||||
showModal("Loading heart rate records...");
|
||||
domRecords.innerHTML = "";
|
||||
Puck.write(`\x10(function() {
|
||||
for (var n=0;n<36;n++) {
|
||||
var f = require("Storage").open(".heart"+n.toString(36),"r");
|
||||
var l = f.readLine();
|
||||
if (l!==undefined)
|
||||
Bluetooth.println(n+","+l.trim());
|
||||
}
|
||||
})()\n`,recordList=>{
|
||||
var recordLines = recordList.trim().split("\n");
|
||||
var html = `<div class="container">
|
||||
<div class="columns">\n`;
|
||||
recordLines.forEach(l => {
|
||||
var record = recordLineToObject(l, true /*has record number*/);
|
||||
html += `
|
||||
<div class="column col-12">
|
||||
<div class="card-header">
|
||||
<div class="card-title h5">Heart Rate Record ${record.number}</div>
|
||||
<div class="card-subtitle text-gray">${(new Date(record.time*1000)).toString().substr(0,24)}</div>
|
||||
</div>
|
||||
<div class="card-body"></div>
|
||||
<div class="card-footer">
|
||||
<button class="btn btn-primary" recordNbr="${record.number}" task="download">Download</button>
|
||||
<button class="btn btn-default" recordNbr="${record.number}" task="delete">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
if (recordLines.length==0) {
|
||||
html += `
|
||||
<div class="column col-12">
|
||||
<div class="card-header">
|
||||
<div class="card-title h5">No record</div>
|
||||
<div class="card-subtitle text-gray">No heart rate record found</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
html += `
|
||||
</div>
|
||||
</div>`;
|
||||
domRecords.innerHTML = html;
|
||||
hideModal();
|
||||
var buttons = domRecords.querySelectorAll("button");
|
||||
for (var i=0;i<buttons.length;i++) {
|
||||
buttons[i].addEventListener("click",event => {
|
||||
var button = event.currentTarget;
|
||||
var recordNbr = parseInt(button.getAttribute("recordNbr"));
|
||||
var task = button.getAttribute("task");
|
||||
if (task=="delete") {
|
||||
showModal("Deleting record...");
|
||||
Puck.write(`\x10require("Storage").open(".heart${recordNbr.toString(36)}","r").erase()\n`,()=>{
|
||||
hideModal();
|
||||
getRecordList();
|
||||
});
|
||||
}
|
||||
if (task=="download") {
|
||||
downloadRecord(recordNbr, record => saveRecord(record, `HeartRateRecord${recordNbr}`));
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function onInit() {
|
||||
getRecordList();
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,50 @@
|
|||
(() => {
|
||||
var settings = {};
|
||||
var hrmToggle = true; // toggles once for each reading
|
||||
var recFile; // file for heart rate recording
|
||||
|
||||
// draw your widget
|
||||
function draw() {
|
||||
if (!settings.isRecording) return;
|
||||
g.reset();
|
||||
g.setFontAlign(0,0);
|
||||
g.clearRect(this.x,this.y,this.x+23,this.y+23);
|
||||
g.setColor(hrmToggle?"#ff0000":"#ff8000");
|
||||
g.fillCircle(this.x+6,this.y+6,4); // draw heart left circle
|
||||
g.fillCircle(this.x+16,this.y+6,4); // draw heart right circle
|
||||
g.fillPoly([this.x+2,this.y+8,this.x+20,this.y+8,this.x+11,this.y+18]); // draw heart bottom triangle
|
||||
g.setColor(-1); // change color back to be nice to other apps
|
||||
}
|
||||
|
||||
function onHRM(hrm) {
|
||||
hrmToggle = !hrmToggle;
|
||||
WIDGETS["heart"].draw();
|
||||
if (recFile) recFile.write([getTime().toFixed(0),hrm.bpm,hrm.confidence].join(",")+"\n");
|
||||
}
|
||||
|
||||
// Called by the heart app to reload settings and decide what's
|
||||
function reload() {
|
||||
settings = require("Storage").readJSON("heart.json",1)||{};
|
||||
settings.fileNbr |= 0;
|
||||
|
||||
Bangle.removeListener('HRM',onHRM);
|
||||
if (settings.isRecording) {
|
||||
WIDGETS["heart"].width = 24;
|
||||
Bangle.on('HRM',onHRM);
|
||||
Bangle.setHRMPower(1);
|
||||
var n = settings.fileNbr.toString(36);
|
||||
recFile = require("Storage").open(".heart"+n,"a");
|
||||
} else {
|
||||
WIDGETS["heart"].width = 0;
|
||||
Bangle.setHRMPower(0);
|
||||
recFile = undefined;
|
||||
}
|
||||
}
|
||||
// add the widget
|
||||
WIDGETS["heart"]={area:"tl",width:24,draw:draw,reload:function() {
|
||||
reload();
|
||||
Bangle.drawWidgets(); // relayout all widgets
|
||||
}};
|
||||
// load settings, set correct widget width
|
||||
reload();
|
||||
})()
|
|
@ -5,7 +5,7 @@ the touchscreen
|
|||
|
||||
var storage = require('Storage');
|
||||
|
||||
const settings = storage.readJSON('setting.json') || { HID: false };
|
||||
const settings = storage.readJSON('setting.json',1) || { HID: false };
|
||||
const KEY = {
|
||||
A : 4 ,
|
||||
B : 5 ,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
var storage = require('Storage');
|
||||
|
||||
const settings = storage.readJSON('setting.json') || { HID: false };
|
||||
const settings = storage.readJSON('setting.json',1) || { HID: false };
|
||||
|
||||
var sendHid, next, prev, toggle, up, down, profile;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
var storage = require('Storage');
|
||||
|
||||
const settings = storage.readJSON('setting.json') || { HID: false };
|
||||
const settings = storage.readJSON('setting.json',1) || { HID: false };
|
||||
|
||||
var sendHid, next, prev, toggle, up, down, profile;
|
||||
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
var s = require("Storage");
|
||||
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);
|
||||
var apps = s.list(/\.info$/).map(app=>s.readJSON(app,1)||{name:"DEAD: "+app.substr(1)}).filter(app=>app.type=="app" || app.type=="clock" || !app.type);
|
||||
apps.sort((a,b)=>{
|
||||
var n=(0|a.sortorder)-(0|b.sortorder);
|
||||
if (n) return n; // do sortorder first
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
|
@ -0,0 +1,88 @@
|
|||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>Please choose a language from the following list:</p>
|
||||
<div class="form-group">
|
||||
<select id="languages" class="form-select">
|
||||
</select>
|
||||
</div>
|
||||
<p>Then click <button id="upload" class="btn btn-primary">Upload</button></p>
|
||||
|
||||
<script src="../../lib/customize.js"></script>
|
||||
<script src="locales.js"></script>
|
||||
|
||||
<script>
|
||||
|
||||
var languageSelector = document.getElementById("languages");
|
||||
languageSelector.innerHTML = Object.keys(locales).map(l=>`<option value="${l}">${l}</option>`).join("\n");
|
||||
|
||||
document.getElementById("upload").addEventListener("click", function() {
|
||||
|
||||
var lang = languageSelector.options[languageSelector.selectedIndex].value;
|
||||
console.log(lang);
|
||||
|
||||
locale = locales[lang];
|
||||
if (!locale) {
|
||||
alert("Language not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
var replaceList = {
|
||||
"%Y": "${d.getFullYear()}",
|
||||
"%y": "${(d.getFullYear().toString()).substr(-2)}",
|
||||
"%m": "${('0'+(d.getMonth()+1).toString()).substr(-2)}",
|
||||
"%-m": "${d.getMonth()+1}",
|
||||
"%d": "${('0'+d.getDate()).substr(-2)}",
|
||||
"%-d": "${d.getDate()}",
|
||||
"%HH": "${('0'+d.getHours()).substr(-2)}",
|
||||
"%MM": "${('0'+d.getMinutes()).substr(-2)}",
|
||||
"%SS": "${('0'+d.getSeconds()).substr(-2)}",
|
||||
"%A": "${locale.day.split(',')[d.getDay()]}",
|
||||
"%a": "${locale.abday.split(',')[d.getDay()]}",
|
||||
"%B": "${locale.month.split(',')[d.getMonth()]}",
|
||||
"%b": "${locale.abmonth.split(',')[d.getMonth()]}",
|
||||
"%p": "${(d.getHours()<12)?locale.ampm[0].toUpperCase():locale.ampm[1].toUpperCase()}",
|
||||
"%P": "${(d.getHours()<12)?locale.ampm[0].toLowerCase():locale.ampm[1].toLowerCase()}"
|
||||
};
|
||||
|
||||
var timeN = locales[lang].timePattern[0];
|
||||
var timeS = locales[lang].timePattern[1];
|
||||
var dateN = locales[lang].datePattern[0];
|
||||
var dateS = locales[lang].datePattern[1];
|
||||
Object.keys(replaceList).forEach(e => {
|
||||
timeN = timeN.replace(e,replaceList[e]);
|
||||
timeS = timeS.replace(e,replaceList[e]);
|
||||
dateN = dateN.replace(e,replaceList[e]);
|
||||
dateS = dateS.replace(e,replaceList[e]);
|
||||
});
|
||||
|
||||
|
||||
var app = `
|
||||
locale = ${JSON.stringify(locales[lang])};
|
||||
exports = {
|
||||
lang: locale.lang,
|
||||
currencySym: String.fromCharCode(locale.currency_symbol),
|
||||
dow: (d,short) => {day = d.getDay();return (short) ? locale.abday.split(",")[day] : locale.day.split(",")[day];},
|
||||
month: (d,short) => { month = d.getMonth(); return (short) ? locale.abmonth.split(",")[month] : locale.month.split(",")[month];},
|
||||
number: n => n.toString().replace(locale.thousands_sep, locale.decimal_point),
|
||||
currency: n => n.toFixed(2).replace(locale.thousands_sep, locale.decimal_point) + locale.currency_symbol,
|
||||
distance: n => (n < 1000) ? Math.round(n) + locale.distance[0] : Math.round(n/1000) + locale.distance[1],
|
||||
speed: s => Math.round(s) +locale.speed,
|
||||
temp: t => Math.round(t) + locale.temperature,
|
||||
translate: s => {s=""+s;return locale.trans[s]||locale.trans[s.toLowerCase()]||s},
|
||||
date: (d,short) => (short) ? \`${dateS}\`: \`${dateN}\`,
|
||||
time: (d,short) => (short) ? \`${timeS}\`: \`${timeN}\`,
|
||||
};`;
|
||||
|
||||
sendCustomizedApp({
|
||||
storage:[
|
||||
{name:"locale", content:app}
|
||||
]
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
After Width: | Height: | Size: 1.7 KiB |
|
@ -0,0 +1,357 @@
|
|||
/*
|
||||
%Y year four digits
|
||||
%y last two digits of year (00..99)
|
||||
%m month (01..12)
|
||||
%d day of month (e.g, 01)
|
||||
|
||||
%a locale's abbreviated weekday name (e.g., Sun)
|
||||
%A locale's full weekday name (e.g., Sunday)
|
||||
%b locale's abbreviated month name (e.g., Jan)
|
||||
%B locale's full month name (e.g., January)
|
||||
|
||||
%H hour (00..23)
|
||||
%M minute (00..59)
|
||||
%S second (00..60)
|
||||
%p locale's equivalent of either AM or PM; blank if not known
|
||||
%P like %p, but lower case
|
||||
*/
|
||||
|
||||
var locales = {
|
||||
"en_GB": { // this is default
|
||||
lang: "en_GB",
|
||||
decimal_point: ".",
|
||||
thousands_sep: ",",
|
||||
currency_symbol: "£",
|
||||
int_curr_symbol: "GBP",
|
||||
speed: 'mph',
|
||||
distance: { "0": "mi", "1": "kmi" },
|
||||
temperature: '°C',
|
||||
ampm: {0:"am",1:"pm"},
|
||||
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
|
||||
datePattern: { 0: "%b %d %Y", 1: "%d/%m/%Y" }, // Feb 28 2020" // "01/03/2020"(short)
|
||||
abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec",
|
||||
month: "January,February,March,April,May,June,July,August,September,October,November,December",
|
||||
abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat",
|
||||
day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday",
|
||||
trans: { /*yes: "yes", Yes: "Yes", no: "no", No: "No", ok: "ok", on: "on", off: "off"*/ }},
|
||||
"de_DE": {
|
||||
lang: "de_DE",
|
||||
decimal_point: ",",
|
||||
thousands_sep: ".",
|
||||
currency_symbol: "\x80",
|
||||
int_curr_symbol: "EUR",
|
||||
speed: "kmh",
|
||||
distance: { 0: "m", 1: "km" },
|
||||
temperature: "°C",
|
||||
ampm: {0:"",1:""},
|
||||
timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" },
|
||||
datePattern: { 0: "%A, %d. %B %Y", "1": "%d.%m.%Y" }, // Sonntag, 1. März 2020 // 01.01.20
|
||||
abmonth: "Jan,Feb,Mär,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Dez",
|
||||
month: "Januar,Februar,März,April,Mai,Juni,Juli,August,September,Oktober,November,Dezember",
|
||||
abday: "So,Mo,Di,Mi,Do,Fr,Sa",
|
||||
day: "Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag",
|
||||
trans: { yes: "ja", Yes: "Ja", no: "nein", No: "Nein", ok: "ok", on: "an", off: "aus" }},
|
||||
"en_US": {
|
||||
lang: "en_US",
|
||||
decimal_point: ".",
|
||||
thousands_sep: ",",
|
||||
currency_symbol: "$",
|
||||
int_curr_symbol: "USD",
|
||||
speed: "mph",
|
||||
distance: { 0: "mi", 1: "kmi" },
|
||||
temperature: "°F",
|
||||
ampm: {0:"am",1:"pm"},
|
||||
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
|
||||
datePattern: { 0: "", 1: "%m/%d/%y" },
|
||||
abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec",
|
||||
month: "January,February,March,April,May,June,July,August,September,October,November,December",
|
||||
abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat",
|
||||
day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday",
|
||||
trans: { yes: "yes", Yes: "Yes", no: "no", No: "No", ok: "ok", on: "on", off: "off" }},
|
||||
"en_JP": { // we do not have the font, so it is not ja_JP
|
||||
lang: "en_JP",
|
||||
decimal_point: ".",
|
||||
thousands_sep: ",",
|
||||
currency_symbol: "¥",
|
||||
int_curr_symbol: "JPY",
|
||||
speed: "kmh",
|
||||
distance: { 0: "m", 1: "km" },
|
||||
temperature: "°F",
|
||||
ampm: {0:"",1:""},
|
||||
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
|
||||
datePattern: { 0: "%y/%M/%d", 1: "%y/%m;/%d" },
|
||||
abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec",
|
||||
month: "January,February,March,April,May,June,July,August,September,October,November,December",
|
||||
abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat",
|
||||
day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday",
|
||||
trans: { yes: "yes", Yes: "Yes", no: "no", No: "No", ok: "ok", on: "on", off: "off" }},
|
||||
"nl_NL": {
|
||||
lang: "nl_NL",
|
||||
decimal_point: ",",
|
||||
thousands_sep: ".",
|
||||
currency_symbol: "\x80",
|
||||
int_curr_symbol: "EUR",
|
||||
speed: "kmh",
|
||||
distance: { 0: "m", 1: "km" },
|
||||
temperature: "°C",
|
||||
ampm: {0:"",1:""},
|
||||
timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" },
|
||||
datePattern: { 0: "%A %B %d %Y", 1: "%d.%m.%y" }, // zondag 1 maart 2020 // 01.01.20
|
||||
abday: "zo,ma,di,wo,do,vr,za",
|
||||
day: "zondag,maandag,dinsdag,woensdag,donderdag,vrijdag,zaterdag",
|
||||
abmonth: "jan,feb,mrt,apr,mei,jun,jul,aug,sep,okt,nov,dec",
|
||||
month: "januari,februari,maart,april,mei,juni,juli,augustus,september,oktober,november,december",
|
||||
trans: { yes: "yes", Yes: "Yes", no: "no", No: "No", ok: "ok", on: "on", off: "off" }},
|
||||
"en_CA": {
|
||||
lang: "en_CA",
|
||||
decimal_point: ".",
|
||||
thousands_sep: ",",
|
||||
currency_symbol: "$",
|
||||
int_curr_symbol: "CAD",
|
||||
speed: "mph",
|
||||
distance: { 0: "mi", 1: "kmi" },
|
||||
temperature: "°F",
|
||||
ampm: {0:"am",1:"pm"},
|
||||
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
|
||||
datePattern: { 0: "%A, %B %d, %Y", "1": "%Y-%m-%d" }, // Sunday, March 1, 2020 // 2012-12-20
|
||||
abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec",
|
||||
month: "January,February,March,April,May,June,July,August,September,October,November,December",
|
||||
abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat",
|
||||
day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday",
|
||||
trans: { yes: "yes", Yes: "Yes", no: "no", No: "No", ok: "ok", on: "on", off: "off" }},
|
||||
"fr_FR": {
|
||||
lang: "fr_FR",
|
||||
decimal_point: ",",
|
||||
thousands_sep: ".",
|
||||
currency_symbol: "\x80",
|
||||
int_curr_symbol: "EUR",
|
||||
speed: "kmh",
|
||||
distance: { 0: "m", 1: "km" },
|
||||
temperature: "°C",
|
||||
ampm: {0:"",1:""},
|
||||
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
|
||||
datePattern: { 0: "%A %d %B %Y", "1": "%d/%m/%Y" }, // dimanche 1 mars 2020 // 01/03/2020
|
||||
abmonth: "janv,févr,mars,avril,mai,juin,juil,août,sept,oct,nov,déc",
|
||||
month: "janvier,février,mars,avril,mai,juin,juillet,août,septembre,octobre,novembre,décembre",
|
||||
abday: "dim,lun,mar,mer,jeu,ven,sam",
|
||||
day: "dimanche,lundi,mardi,mercredi,jeudi,vendredi,samedi",
|
||||
trans : { yes : "oui", Yes: "Oui", no: "no", No: "No", ok : "ok", on: "on", off: "off" }},
|
||||
"sv_SE": {
|
||||
lang: "sv_SE",
|
||||
decimal_point: ",",
|
||||
thousands_sep: ".",
|
||||
currency_symbol: "kr",
|
||||
int_curr_symbol: "SKR",
|
||||
speed: "kmh",
|
||||
distance: { 0: "m", 1: "km" },
|
||||
temperature: "°C",
|
||||
ampm: {0:"fm",1:"em"},
|
||||
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
|
||||
datePattern: { 0: "%A %B %d %Y", "1": "%Y-%m-%d" }, // söndag 1 mars 2020 // 2020-03-01
|
||||
abmonth: "jan,feb,mars,apr,maj,juni,juli,aug,sep,okt,nov,dec",
|
||||
month: "januari,februari,mars,april,maj,juni,juli,augusti,september,oktober,november,december",
|
||||
abday: "sön,mån,tis,ons,tors,fre,lör",
|
||||
day: "söndag,måndag,tisdag,onsdag,torsdag,fredag,lördag",
|
||||
trans : { yes : "ja", Yes: "Ja", no: "nej", No: "Nej", ok : "ok", on: "on", off: "off" }},
|
||||
"en_AU": {
|
||||
lang: "en_AU",
|
||||
decimal_point: ".",
|
||||
thousands_sep: ",",
|
||||
currency_symbol: "$",
|
||||
int_curr_symbol: "AUD",
|
||||
speed: "mph",
|
||||
distance: { 0: "mi", 1: "kmi" },
|
||||
temperature: "°F",
|
||||
ampm: {0:"am",1:"pm"},
|
||||
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
|
||||
datePattern: { 0: "%A, %B %d, %Y", "1": "%m/%d/%y" }, // Sunday, 1 March 2020 // 1/3/20
|
||||
abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec",
|
||||
month: "January,February,March,April,May,June,July,August,September,October,November,December",
|
||||
abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat",
|
||||
day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday",
|
||||
trans: { yes: "yes", Yes: "Yes", no: "no", No: "No", ok: "ok", on: "on", off: "off" }},
|
||||
"de_AT": {
|
||||
lang: "de_AT",
|
||||
decimal_point: ",",
|
||||
thousands_sep: ".",
|
||||
currency_symbol: "\x80",
|
||||
int_curr_symbol: "EUR",
|
||||
speed: "kmh",
|
||||
distance: { 0: "m", 1: "km" },
|
||||
temperature: "°C",
|
||||
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
|
||||
datePattern: { 0: "%A, %d. %B %Y", "1": "%d.%m.%y" }, // Sonntag, 1. März 2020 // 01.03.20
|
||||
abmonth: "Jän,Feb,März,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Dez",
|
||||
month: "Jänner,Februar,März,April,Mai,Juni,Juli,August,September,Oktober,November,Dezember",
|
||||
abday: "So,Mo,Di,Mi,Do,Fr,Sa",
|
||||
day: "Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag",
|
||||
trans: { yes: "ja", Yes: "Ja", no: "nein", No: "Nein", ok: "ok", on: "an", off: "aus" }},
|
||||
"en_IL": {
|
||||
lang: "en_IL",
|
||||
decimal_point: ",",
|
||||
thousands_sep: ".",
|
||||
currency_symbol: "₪",
|
||||
int_curr_symbol: "ILS",
|
||||
speed: "kmh",
|
||||
distance: { 0: "m", 1: "km" },
|
||||
temperature: "°F",
|
||||
ampm: {0:"am",1:"pm"},
|
||||
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
|
||||
datePattern: { 0: "%A, %B %d, %Y", "1": "%d/%m/%Y" }, // Sunday, 1 March 2020 // 01/03/2020
|
||||
abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec",
|
||||
month: "January,February,March,April,May,June,July,August,September,October,November,December",
|
||||
abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat",
|
||||
day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday",
|
||||
trans: { yes: "yes", Yes: "Yes", no: "no", No: "No", ok: "ok", on: "on", off: "off" }},
|
||||
"es_ES": {
|
||||
lang: "es_ES",
|
||||
decimal_point: ",",
|
||||
thousands_sep: ".",
|
||||
currency_symbol: "\x80",
|
||||
int_curr_symbol: "EUR",
|
||||
speed: "kmh",
|
||||
distance: { 0: "m", 1: "km" },
|
||||
temperature: "°C",
|
||||
ampm: {0:"",1:""},
|
||||
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
|
||||
datePattern: { 0: "%A, %d de %B de %Y", "1": "%d/%m/%y" }, // domingo, 1 de marzo de 2020 // 01/03/20
|
||||
abmonth: "ene,feb,mar,abr,may,jun,jul,ago,sept,oct,nov,dic",
|
||||
month: "enero,febrero,marzo,abril,mayo,junio,julio,agosto,septiembre,octubre,noviembre,diciembre",
|
||||
abday: "dom,lun,mar,mié,jue,vie,sáb",
|
||||
day: "domingo,lunes,martes,miércoles,jueves,viernes,sábado",
|
||||
trans: { yes : "sí", Yes: "Sí",no: "no", No: "No", ok : "ok", on: "on", off: "off" }},
|
||||
"fr_BE": {
|
||||
lang: "fr_BE",
|
||||
decimal_point: ",",
|
||||
thousands_sep: ".",
|
||||
currency_symbol: "\x80",
|
||||
int_curr_symbol: "EUR",
|
||||
speed: "kmh",
|
||||
distance: { 0: "m", 1: "km" },
|
||||
temperature: "°C",
|
||||
ampm: {0: "",1: ""},
|
||||
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
|
||||
datePattern: { 0: "%A %B %d %Y", "1": "%d/%m/%y" }, // dimanche 1 mars 2020 // 01/03/20
|
||||
abmonth: "anv.,févr.,mars,avril,mai,juin,juil.,août,sept.,oct.,nov.,déc.",
|
||||
month: "janvier,février,mars,avril,mai,juin,juillet,août,septembre,octobre,novembre,décembre",
|
||||
abday: "dim,lun,mar,mer,jeu,ven,sam",
|
||||
day: "dimanche,lundi,mardi,mercredi,jeudi,vendredi,samedi",
|
||||
trans : { yes : "oui", Yes: "Oui", no: "no", No: "No", ok : "ok", on: "on", off: "off" }},
|
||||
"fi_FI": {
|
||||
lang: "fi_FI",
|
||||
decimal_point: ",",
|
||||
thousands_sep: ".",
|
||||
currency_symbol: "\x80",
|
||||
int_curr_symbol: "EUR",
|
||||
speed: "kmh",
|
||||
distance: { 0: "m", 1: "km" },
|
||||
temperature: "°C",
|
||||
ampm: {0: "ap",1: "ip"},
|
||||
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" }, // 17.00.00 // 17.00
|
||||
datePattern: { 0: "%A %d. %B %Y", "1": "%-d/%-m/%Y" }, // sunnuntai 1. maaliskuuta 2020 // 1.3.2020
|
||||
abmonth: "tammik,helmik,maalisk,huhtik,toukok,kesäk,heinäk,elok,syysk,lokak,marrask,jouluk",
|
||||
month: "tammikuuta,helmikuuta,maaliskuuta,huhtikuuta,toukokuuta,kesäkuuta,heinäkuuta,elokuuta,syyskuuta,lokakuuta,marraskuuta,joulukuuta",
|
||||
abday: "su,ma,ti,ke,to,pe,la",
|
||||
day: "sunnuntaina,maanantaina,tiistaina,keskiviikkona,torstaina,perjantaina,lauantaina",
|
||||
trans : { yes : "oui", Yes: "Oui", no: "no", No: "No", ok : "ok", on: "on", off: "off" }},
|
||||
"de_CH": {
|
||||
lang: "de_CH",
|
||||
decimal_point: ",",
|
||||
thousands_sep: ".",
|
||||
currency_symbol: "CHF",
|
||||
int_curr_symbol: "CHF",
|
||||
speed: "kmh",
|
||||
distance: { 0: "m", 1: "km" },
|
||||
temperature: "°C",
|
||||
ampm: {0:"vorm",1:" nachm"},
|
||||
timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" },
|
||||
datePattern: { 0: "%A, %d. %B %Y", "1": "%d.%m.%Y" }, // Sonntag, 1. März 2020 // 1.3.2020
|
||||
abmonth: "Jan,Feb,März,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Dez",
|
||||
month: "Januar,Februar,März,April,Mai,Juni,Juli,August,September,Oktober,November,Dezember",
|
||||
abday: "So,Mo,Di,Mi,Do,Fr,Sa",
|
||||
day: "Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag",
|
||||
trans: { yes: "ja", Yes: "Ja", no: "nein", No: "Nein", ok: "ok", on: "an", off: "aus" }},
|
||||
"fr_CH": {
|
||||
lang: "fr_CH",
|
||||
decimal_point: ",",
|
||||
thousands_sep: ".",
|
||||
currency_symbol: "CHF",
|
||||
int_curr_symbol: "CHF",
|
||||
speed: "kmh",
|
||||
distance: { 0: "m", 1: "km" },
|
||||
temperature: "°C",
|
||||
ampm: {0:"AM",1:"PM"},
|
||||
timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" },
|
||||
datePattern: { 0: "%A %d %B %Y", "1": "%d/%m/%y" }, // dimanche 1 mars 2020 // 01/03/20
|
||||
abmonth: "anv.,févr.,mars,avril,mai,juin,juil.,août,sept.,oct.,nov.,déc.",
|
||||
month: "janvier,février,mars,avril,mai,juin,juillet,août,septembre,octobre,novembre,décembre",
|
||||
abday: "dim,lun,mar,mer,jeu,ven,sam",
|
||||
day: "dimanche,lundi,mardi,mercredi,jeudi,vendredi,samedi",
|
||||
trans : { yes : "oui", Yes: "Oui", no: "no", No: "No", ok : "ok", on: "on", off: "off" }},
|
||||
"it_CH": {
|
||||
lang: "it_CH",
|
||||
decimal_point: ",",
|
||||
thousands_sep: ".",
|
||||
currency_symbol: "CHF",
|
||||
int_curr_symbol: "CHF",
|
||||
speed: 'kmh',
|
||||
distance: { "0": "m", "1": "km" },
|
||||
temperature: '°C',
|
||||
timePattern: { 0: "%HH.%MM.%SS ", 1: "%HH.%MM" }, // 17.00.00 // 17.00
|
||||
datePattern: { 0: "%A %B %d %Y", "1": "%d/%m/%Y" }, // sunnuntai 1. maaliskuuta 2020 // 1.3.2020
|
||||
abmonth: "gen,feb,mar,apr,mag,giu,lug,ago,set,ott,nov,dic",
|
||||
month: "gennaio,febbraio,marzo,aprile,maggio,giugno,luglio,agosto,settembre,ottobre,novembre,dicembre",
|
||||
abday : "dom,lun,mar,mer,gio,ven,sab",
|
||||
day: "domenica,lunedì,martedì,mercoledì,giovedì,venerdì, sabato",
|
||||
trans : { yes: "sì", Yes: "Sì", no: "no", No: "No", ok: "ok", on: "on", off: "off" }},
|
||||
"wae_CH" : {
|
||||
lang: "wae_CH",
|
||||
decimal_point: ",",
|
||||
thousands_sep: ".",
|
||||
currency_symbol: "CHF",
|
||||
int_curr_symbol: "CHF",
|
||||
speed: 'kmh',
|
||||
distance: { "0": "m", "1": "km" },
|
||||
temperature: '°C',
|
||||
timePattern: { 0: "%HH.%MM.%SS ", 1: "%HH.%MM" }, // 17.00.00 // 17.00
|
||||
datePattern: { 0: "%A, %d. %B %Y", "1": "%Y-%m-%d" }, // Sunntag, 1. Märze 2020 // 2020-03-01
|
||||
abmonth: "Jen,Hor,Mär,Abr,Mei,Brá,Hei,Öig,Her,Wím,Win,Chr",
|
||||
month: "Jenner,Hornig,Märze,Abrille,Meije,Bráčet,Heiwet,Öigšte,Herbštmánet,Wímánet,Wintermánet,Chrištmánet",
|
||||
abday: "Sun,Män,Ziš,Mit,Fró,Fri,Sam",
|
||||
day: "Sunntag,Mäntag,Zištag,Mittwuč,Fróntag,Fritag,Samštag",
|
||||
trans : { yes: "sì", Yes: "Sì", no: "no", No: "No", ok: "ok", on: "on", off: "off" }},
|
||||
"tr_TR": { // this is default
|
||||
lang: "tr_TR",
|
||||
decimal_point: ",",
|
||||
thousands_sep: ".",
|
||||
currency_symbol: "TL",
|
||||
int_curr_symbol: "TRY",
|
||||
speed: 'kmh',
|
||||
distance: { "0": "m", "1": "km" },
|
||||
temperature: '°C',
|
||||
ampm: {0:"öö",1:"ös"},
|
||||
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
|
||||
datePattern: { 0: "%d %w %Y %A", 1: "%d/%m/%Y" }, // 1 Mart 2020 Pazar // "01/03/2020"
|
||||
abmonth: "Oca,Sub,Mar,Nis,May,Haz,Tem,Agu,Eyl,Eki,Kas,Ara",
|
||||
month: "Ocak,Subat,Mart,Nisan,Mayis,Haziran,Temmuz,Agustos,Eylul,Ekim,Kasim,Aralik",
|
||||
abday: "Paz,Pzt,Sal,Car,Per,Cum,Cmt",
|
||||
day: "Pazar,Pazartesi,Sali,Carsamba,Persembe,Cuma,Cumartesi",
|
||||
trans: { yes: "evet", Yes: "Evet", no: "hayir", No: "Hayir", ok: "tamam", on: "acik", off: "kapali" }},
|
||||
"hu_HU": {
|
||||
lang: "hu_HU",
|
||||
decimal_point: ",",
|
||||
thousands_sep: " ",
|
||||
currency_symbol: "Ft",
|
||||
int_curr_symbol: "HUF",
|
||||
speed: 'kph',
|
||||
distance: { "0": "m", "1": "km" },
|
||||
temperature: '°C',
|
||||
ampm: {0:"de",1:"du"},
|
||||
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
|
||||
datePattern: { 0: "%Y %d %b", 1: "%Y.%m.%d" }, // 2020 Feb 28" // "2020.03.01."(short)
|
||||
abmonth: "Jan,Feb,Már,Ápr,Máj,Jún,Júl,Aug,Szep,Okt,Nov,Dec",
|
||||
month: "Január,Február,Március,Április,Május,Június,Július,Augusztus,Szeptember,Október,November,December",
|
||||
abday: "Vas,Hét,Ke,Szer,Csüt,Pén,Szom",
|
||||
day: "Vasárnap,Hétfő,Kedd,Szerda,Csütörtök,Péntek,Szombat",
|
||||
trans: { yes: "igen", Yes: "Igen", no: "nem", No: "Nem", ok: "ok", on: "be", off: "ki" }},
|
||||
};
|
|
@ -115,11 +115,9 @@ function info() {
|
|||
}
|
||||
|
||||
function cleanup() {
|
||||
try {
|
||||
var settings = require("Storage").readJSON('setting.json');
|
||||
settings.welcomed = true;
|
||||
require("Storage").write('setting.json',settings);
|
||||
} catch (e) {}
|
||||
var settings = require("Storage").readJSON('setting.json',1)||{};
|
||||
settings.welcomed = true;
|
||||
require("Storage").write('setting.json',settings);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
</head>
|
||||
<body>
|
||||
|
||||
<p>Enter a URL: <input type="text" id="url" value="http://espruino.com"></p>
|
||||
<p>Enter a URL: <input type="text" id="url" class="form-input" value="http://espruino.com"></p>
|
||||
<p>Try your QR Code: <div id="qrcode"></div></p>
|
||||
<p>Click <button id="upload" class="btn btn-primary">Upload</button></p>
|
||||
|
||||
|
|
|
@ -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.json")||{})["12hour"];
|
||||
var is12Hour = (require("Storage").readJSON("setting.json",1)||{})["12hour"];
|
||||
|
||||
function drawSimpleClock() {
|
||||
// get date
|
||||
|
|
|
@ -2,3 +2,4 @@
|
|||
0.03: Add support for Welcome app
|
||||
0.04: Add setting to disable log messages
|
||||
0.05: Fix Settings json
|
||||
0.06: Remove distance setting as there's a separate app for Locale now
|
||||
|
|
|
@ -9,6 +9,5 @@
|
|||
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)
|
||||
}
|
||||
|
|
|
@ -21,15 +21,12 @@ function resetSettings() {
|
|||
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)
|
||||
};
|
||||
updateSettings();
|
||||
}
|
||||
|
||||
try {
|
||||
settings = storage.readJSON('setting.json');
|
||||
} catch (e) {}
|
||||
settings = storage.readJSON('setting.json',1);
|
||||
if (!settings) resetSettings();
|
||||
|
||||
const boolFormat = v => v ? "On" : "Off";
|
||||
|
@ -143,15 +140,7 @@ function showLocaleMenu() {
|
|||
settings["12hour"] = v;
|
||||
updateSettings();
|
||||
}
|
||||
},
|
||||
'Distance/Speed': {
|
||||
value: settings.distance=="mile",
|
||||
format: v => v?"mile":"km",
|
||||
onchange: v => {
|
||||
settings.distance = v?"mile":"kilometer";
|
||||
updateSettings();
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
return E.showMenu(localemenu);
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwxH+AH4A/AH4Acq0yF1tWlksF10yqwuuSVIuGSVF/FwySm5nM0YuGSUovBGAIuGAgIukGAQuGAgIvlSQozEF0iSEeowvlGAT1HF0iSDeo4vlegSShF5fMv4uFSLQkC0QACSRguevErld4GBKSDFz4ABF5CSBLsIvLdDK7GFwiPIF0AvEFw4tbFwztKFrguDF4gADFkAuFF44unF4wuoGAouqAAwu/ABtWqwutmUsmQutF4JhKF0iSJdT4uFAoIwGFz4wCFwgECF0qRCFwo3BF0qSDMQiSBF0owCFwgFBF86SBF1qSFF1SSDF1gA/AH4A1A"))
|
|
@ -0,0 +1,8 @@
|
|||
Bangle.setLCDPower(1);
|
||||
Bangle.setLCDTimeout(0);
|
||||
g.reset();
|
||||
g.fillRect(0,0,g.getWidth(),g.getHeight());
|
||||
// Any button turns off
|
||||
setWatch(()=>load(), BTN1);
|
||||
setWatch(()=>load(), BTN2);
|
||||
setWatch(()=>load(), BTN3);
|
After Width: | Height: | Size: 1.6 KiB |
|
@ -0,0 +1,18 @@
|
|||
var clickTimes = [];
|
||||
var CLICK_COUNT = 4; // number of taps
|
||||
var CLICK_PERIOD = 1; // second
|
||||
|
||||
// we don't actually create/draw a widget here at all...
|
||||
|
||||
Bangle.on("lcdPower",function(on) {
|
||||
// First click (that turns LCD on) isn't given to
|
||||
// setWatch, so handle it here
|
||||
if (on) clickTimes=[getTime()];
|
||||
});
|
||||
setWatch(function(e) {
|
||||
while (clickTimes.length>=CLICK_COUNT) clickTimes.shift();
|
||||
clickTimes.push(e.time);
|
||||
var clickPeriod = e.time-clickTimes[0];
|
||||
if (clickTimes.length==CLICK_COUNT && clickPeriod<CLICK_PERIOD)
|
||||
load("torch.app.js");
|
||||
}, BTN3, {repeat:true, edge:"rising"});
|
|
@ -1,3 +1,4 @@
|
|||
0.01: New App!
|
||||
0.02: Animate balloon intro
|
||||
0.03: BTN3 now won't restart when at the end
|
||||
0.04: Fix regression after tweaks to Storage.readJSON
|
||||
|
|
|
@ -283,11 +283,9 @@ setWatch(()=>move(1), BTN3, {repeat:true});
|
|||
setWatch(()=>{
|
||||
// If we're on the last page
|
||||
if (sceneNumber == scenes.length-1) {
|
||||
try {
|
||||
var settings = require("Storage").readJSON('setting.json');
|
||||
settings.welcomed = true;
|
||||
require("Storage").write('setting.json',settings);
|
||||
} catch (e) {}
|
||||
var settings = require("Storage").readJSON('setting.json',1)||{};
|
||||
settings.welcomed = true;
|
||||
require("Storage").write('setting.json',settings);
|
||||
load();
|
||||
}
|
||||
}, BTN2, {repeat:true,edge:"rising"});
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
0.02: Now refresh battery monitor every minute if LCD on
|
||||
0.03: Tweaks for variable size widget system
|
||||
0.04: Ensure redrawing works with variable size widget system
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
(function(){
|
||||
var img_charge = E.toArrayBuffer(atob("DhgBHOBzgc4HOP////////////////////3/4HgB4AeAHgB4AeAHgB4AeAHg"));
|
||||
var CHARGING = 0x07E0;
|
||||
var xpos = WIDGETPOS.tr-64;
|
||||
WIDGETPOS.tr-=68;
|
||||
|
||||
function setWidth() {
|
||||
WIDGETS["bat"].width = 40 + (Bangle.isCharging()?16:0);
|
||||
}
|
||||
function draw() {
|
||||
var s = 63;
|
||||
var x = xpos, y = 0;
|
||||
g.clearRect(x,y,x+s,y+23);
|
||||
var s = 39;
|
||||
var x = this.x, y = this.y;
|
||||
if (Bangle.isCharging()) {
|
||||
g.setColor(CHARGING).drawImage(img_charge,x,y);
|
||||
g.setColor(CHARGING).drawImage(atob("DhgBHOBzgc4HOP////////////////////3/4HgB4AeAHgB4AeAHgB4AeAHg"),x,y);
|
||||
x+=16;
|
||||
s-=16;
|
||||
}
|
||||
g.setColor(-1);
|
||||
g.fillRect(x,y+2,x+s-4,y+21);
|
||||
|
@ -20,11 +18,16 @@ function draw() {
|
|||
g.setColor(CHARGING).fillRect(x+4,y+6,x+4+E.getBattery()*(s-12)/100,y+17);
|
||||
g.setColor(-1);
|
||||
}
|
||||
Bangle.on('charging',function(charging) { draw(); g.flip(); if(charging)Bangle.buzz(); });
|
||||
Bangle.on('charging',function(charging) {
|
||||
if(charging) Bangle.buzz();
|
||||
setWidth();
|
||||
Bangle.drawWidgets(); // relayout widgets
|
||||
g.flip();
|
||||
});
|
||||
var batteryInterval;
|
||||
Bangle.on('lcdPower', function(on) {
|
||||
if (on) {
|
||||
draw();
|
||||
WIDGETS["bat"].draw();
|
||||
// refresh once a minute if LCD on
|
||||
if (!batteryInterval)
|
||||
batteryInterval = setInterval(draw, 60000);
|
||||
|
@ -35,5 +38,6 @@ Bangle.on('lcdPower', function(on) {
|
|||
}
|
||||
}
|
||||
});
|
||||
WIDGETS["battery"]={draw:draw};
|
||||
WIDGETS["bat"]={area:"tr",width:40,draw:draw};
|
||||
setWidth();
|
||||
})()
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
0.02: Tweaks for variable size widget system
|
||||
0.03: Ensure redrawing works with variable size widget system
|
|
@ -1,22 +1,19 @@
|
|||
(function(){
|
||||
var img_bt = E.toArrayBuffer(atob("CxQBBgDgFgJgR4jZMawfAcA4D4NYybEYIwTAsBwDAA=="));
|
||||
var xpos = WIDGETPOS.tr-24;
|
||||
WIDGETPOS.tr-=24;
|
||||
|
||||
function draw() {
|
||||
var x = xpos, y = 0;
|
||||
g.reset();
|
||||
if (NRF.getSecurityStatus().connected)
|
||||
g.setColor(0,0.5,1);
|
||||
else
|
||||
g.setColor(0.3,0.3,0.3);
|
||||
g.drawImage(img_bt,10+x,2+y);
|
||||
g.setColor(1,1,1);
|
||||
g.drawImage(img_bt,10+this.x,2+this.y);
|
||||
}
|
||||
function changed() {
|
||||
draw();
|
||||
WIDGETS["bluetooth"].draw();
|
||||
g.flip();// turns screen on
|
||||
}
|
||||
NRF.on('connected',changed);
|
||||
NRF.on('disconnected',changed);
|
||||
WIDGETS["bluetooth"]={draw:draw};
|
||||
WIDGETS["bluetooth"]={area:"tr",width:24,draw:draw};
|
||||
})()
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
0.02: Now refresh battery monitor every minute if LCD on
|
||||
0.03: Ensure redrawing works with variable size widget system
|
|
@ -1,39 +1,27 @@
|
|||
(() => {
|
||||
let intervalRef = null;
|
||||
var width = 5 * 6*2
|
||||
var xpos = WIDGETPOS.tr - width;
|
||||
WIDGETPOS.tr -= (width + 2);
|
||||
|
||||
|
||||
function draw() {
|
||||
// Widget (0,0,239,23)
|
||||
let date = new Date();
|
||||
var dateArray = date.toString().split(" ");
|
||||
g.setColor(1,1,1);
|
||||
g.setFont("6x8", 2);
|
||||
g.setFontAlign(-1, 0);
|
||||
g.drawString(dateArray[4].substr(0, 5), xpos, 11, true); // 5 * 6*2 = 60
|
||||
g.flip();
|
||||
g.reset().setFont("6x8", 2).setFontAlign(-1, 0);
|
||||
var time = require("locale").time(new Date(),1);
|
||||
g.drawString(time, this.x, this.y+11, true); // 5 * 6*2 = 60
|
||||
}
|
||||
function clearTimers(){
|
||||
if(intervalRef) {
|
||||
clearInterval(intervalRef);
|
||||
intervalRef = null;
|
||||
}
|
||||
if(intervalRef) {
|
||||
clearInterval(intervalRef);
|
||||
intervalRef = null;
|
||||
}
|
||||
}
|
||||
function startTimers(){
|
||||
if(intervalRef) clearTimers();
|
||||
intervalRef = setInterval(draw, 60*1000);
|
||||
draw();
|
||||
intervalRef = setInterval(draw, 60*1000);
|
||||
WIDGETS["wdclk"].draw();
|
||||
}
|
||||
Bangle.on('lcdPower', (on) => {
|
||||
if (on) {
|
||||
// startTimers(); // comment out as it is called by app anyway
|
||||
} else {
|
||||
clearTimers();
|
||||
}
|
||||
clearTimers();
|
||||
if (on) startTimers();
|
||||
});
|
||||
|
||||
// add your widget
|
||||
WIDGETS["wdclk"]={draw:startTimers};
|
||||
|
||||
})()
|
||||
WIDGETS["wdclk"]={area:"tr",width:width,draw:draw};
|
||||
if (Bangle.isLCDOn) intervalRef = setInterval(draw, 60*1000);
|
||||
})()
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
0.01: New Widget!
|
||||
0.02: Tweaks for variable size widget system
|
||||
0.03: Ensure redrawing works with variable size widget system
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
(() => {
|
||||
var xpos = WIDGETPOS.tl;
|
||||
var width = 24;
|
||||
WIDGETPOS.tl += width+2;
|
||||
var currentBPM = undefined;
|
||||
var lastBPM = undefined;
|
||||
var firstBPM = true; // first reading since sensor turned on
|
||||
|
||||
function draw() {
|
||||
var width = 24;
|
||||
g.reset();
|
||||
g.setFont("6x8", 1);
|
||||
g.setFontAlign(0, 0);
|
||||
g.clearRect(xpos,15,xpos+width,24); // erase background
|
||||
g.clearRect(this.x,this.y+15,this.x+width,this.y+23); // erase background
|
||||
var bpm = currentBPM, isCurrent = true;
|
||||
if (bpm===undefined) {
|
||||
bpm = lastBPM;
|
||||
|
@ -19,9 +17,9 @@
|
|||
if (bpm===undefined)
|
||||
bpm = "--";
|
||||
g.setColor(isCurrent ? "#ffffff" : "#808080");
|
||||
g.drawString(bpm, xpos+width/2, 19);
|
||||
g.drawString(bpm, this.x+width/2, this.y+19);
|
||||
g.setColor(isCurrent ? "#ff0033" : "#808080");
|
||||
g.drawImage(atob("CgoCAAABpaQ//9v//r//5//9L//A/+AC+AAFAA=="),xpos+(width-10)/2,1);
|
||||
g.drawImage(atob("CgoCAAABpaQ//9v//r//5//9L//A/+AC+AAFAA=="),this.x+(width-10)/2,this.y+1);
|
||||
g.setColor(-1);
|
||||
}
|
||||
|
||||
|
@ -31,7 +29,7 @@
|
|||
Bangle.setHRMPower(1);
|
||||
firstBPM = true;
|
||||
currentBPM = undefined;
|
||||
draw();
|
||||
WIDGETS["hrm"].draw();
|
||||
} else {
|
||||
Bangle.setHRMPower(0);
|
||||
}
|
||||
|
@ -44,10 +42,10 @@
|
|||
currentBPM = d.bpm;
|
||||
lastBPM = currentBPM;
|
||||
}
|
||||
draw();
|
||||
WIDGETS["hrm"].draw();
|
||||
});
|
||||
Bangle.setHRMPower(Bangle.isLCDOn());
|
||||
|
||||
// add your widget
|
||||
WIDGETS["hrm"]={draw:draw};
|
||||
WIDGETS["hrm"]={area:"tl",width:24,draw:draw};
|
||||
})();
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
0.01: New Widget!
|
||||
0.02: Tweaks for variable size widget system
|
|
@ -0,0 +1,12 @@
|
|||
/* jshint esversion: 6 */
|
||||
(() => {
|
||||
var id = NRF.getAddress().substr().substr(12).split(":");
|
||||
|
||||
// draw your widget at xpos
|
||||
function draw() {
|
||||
g.reset().setColor(0, 0.5, 1).setFont("6x8", 1);
|
||||
g.drawString(id[0], this.x+2, this.y+4, true);
|
||||
g.drawString(id[1], this.x+2, this.y+14, true);
|
||||
}
|
||||
WIDGETS["widid"] = { area:"tr", width:16, draw: draw };
|
||||
})();
|
After Width: | Height: | Size: 2.1 KiB |
|
@ -0,0 +1,2 @@
|
|||
0.01: New Widget!
|
||||
0.02: Tweaks for variable size widget system
|
|
@ -1,13 +1,5 @@
|
|||
(function(){
|
||||
var img = E.toArrayBuffer(atob("SxgCAAAAAAAAAAAAAAAAAAAAAAAAALwDwH/gD/0B//Af+AAD4C8f/wL8Dwf/8H//C//C//AAD9C8f/wL9Dw//+H//i4AD0AH8D/C8fAAL/Dz///H//y8ALgAf/D/i8fAALrzz///H//2//PAAv/Dz28f/gLz7z///H//29VPQAv/Dx+8fqQLw/y///H//y4ALwAP/Dwv8fAALwfw//9H//i+qD8FC0DwP8fAALwPwP/4H/+C//A//AADwD8fAAAAAAC/QD+gAAAAL4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGqooBkD+AP0fgvgAAAAAAAAAAL/88C4NBw0NBjQcAAAAAAAAAALQA8C4AAyQDBjAJAAAAAAAAAALQA8C4AAzACBjAKAAAAAAAAAAL/88C4ABTACRjQbAAAAAAAAAAL/48C4AHDACRgvjAAAAAAAAAALQA8C4AcDACBgACAAAAAAAAAALQA+D0BwCQDBgANAAAAAAAAAAL/8P/wHAA0NBgAoAAAAAAAAAAGqoC+AP/0HgAQuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
|
||||
var xpos = WIDGETPOS.tl;
|
||||
WIDGETPOS.tl+=75;
|
||||
|
||||
|
||||
WIDGETS["nceu"]={draw:()=>{
|
||||
var x = xpos, y = 0;
|
||||
g.setColor(0.17,0.2,0.5);
|
||||
g.drawImage(img,x,y);
|
||||
WIDGETS["nceu"]={area:"tl",width:75,draw:function(){
|
||||
g.reset().setColor(0.17,0.2,0.5);
|
||||
g.drawImage(atob("SxgCAAAAAAAAAAAAAAAAAAAAAAAAALwDwH/gD/0B//Af+AAD4C8f/wL8Dwf/8H//C//C//AAD9C8f/wL9Dw//+H//i4AD0AH8D/C8fAAL/Dz///H//y8ALgAf/D/i8fAALrzz///H//2//PAAv/Dz28f/gLz7z///H//29VPQAv/Dx+8fqQLw/y///H//y4ALwAP/Dwv8fAALwfw//9H//i+qD8FC0DwP8fAALwPwP/4H/+C//A//AADwD8fAAAAAAC/QD+gAAAAL4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGqooBkD+AP0fgvgAAAAAAAAAAL/88C4NBw0NBjQcAAAAAAAAAALQA8C4AAyQDBjAJAAAAAAAAAALQA8C4AAzACBjAKAAAAAAAAAAL/88C4ABTACRjQbAAAAAAAAAAL/48C4AHDACRgvjAAAAAAAAAALQA8C4AcDACBgACAAAAAAAAAALQA+D0BwCQDBgANAAAAAAAAAAL/8P/wHAA0NBgAoAAAAAAAAAAGqoC+AP/0HgAQuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"),this.x,this.y);
|
||||
g.setColor(1,1,1);
|
||||
}};
|
||||
})()
|
||||
|
|
|
@ -3,3 +3,5 @@
|
|||
0.04: Fix pedometer reload when going back to clock app
|
||||
0.05: Add foot icon, use tidier font, and move to the left of the screen
|
||||
0.06: Fix widget position increment
|
||||
0.07: Tweaks for variable size widget system
|
||||
0.08: Ensure redrawing works with variable size widget system
|
||||
|
|
|
@ -1,17 +1,11 @@
|
|||
(() => {
|
||||
const PEDOMFILE = "wpedom.json";
|
||||
// add the width
|
||||
// WIDGETPOS.tr is originally 208 without any widgets
|
||||
var xpos = WIDGETPOS.tl;
|
||||
var width = 24;
|
||||
WIDGETPOS.tl += (width + 2);
|
||||
|
||||
let lastUpdate = new Date();
|
||||
let stp_today = 0;
|
||||
|
||||
// draw your widget at xpos
|
||||
// draw your widget
|
||||
function draw() {
|
||||
// Widget (0,0,239,23)
|
||||
var width = 24;
|
||||
if (stp_today > 99999){
|
||||
stp_today = stp_today % 100000; // cap to five digits + comma = 6 characters
|
||||
}
|
||||
|
@ -24,9 +18,9 @@
|
|||
g.setFont("6x8", 1);
|
||||
}
|
||||
g.setFontAlign(0, 0); // align to x: center, y: center
|
||||
g.clearRect(xpos,15,xpos+width,24); // erase background
|
||||
g.drawString(stps, xpos+width/2, 19);
|
||||
g.drawImage(atob("CgoCLguH9f2/7+v6/79f56CtAAAD9fw/n8Hx9A=="),xpos+(width-10)/2,2);
|
||||
g.clearRect(this.x,this.y+15,this.x+width,this.y+23); // erase background
|
||||
g.drawString(stps, this.x+width/2, this.y+19);
|
||||
g.drawImage(atob("CgoCLguH9f2/7+v6/79f56CtAAAD9fw/n8Hx9A=="),this.x+(width-10)/2,this.y+2);
|
||||
}
|
||||
|
||||
Bangle.on('step', (up) => {
|
||||
|
@ -39,11 +33,11 @@
|
|||
}
|
||||
lastUpdate = date;
|
||||
//console.log("up: " + up + " stp: " + stp_today + " " + date.toString());
|
||||
if (Bangle.isLCDOn()) draw();
|
||||
if (Bangle.isLCDOn()) WIDGETS["wpedom"].draw();
|
||||
});
|
||||
// redraw when the LCD turns on
|
||||
Bangle.on('lcdPower', function(on) {
|
||||
if (on) draw();
|
||||
if (on) WIDGETS["wpedom"].draw();
|
||||
});
|
||||
// When unloading, save state
|
||||
E.on('kill', () => {
|
||||
|
@ -55,9 +49,9 @@
|
|||
});
|
||||
|
||||
// add your widget
|
||||
WIDGETS["wpedom"]={draw:draw};
|
||||
WIDGETS["wpedom"]={area:"tl",width:26,draw:draw};
|
||||
// Load data at startup
|
||||
let pedomData = require("Storage").readJSON(PEDOMFILE);
|
||||
let pedomData = require("Storage").readJSON(PEDOMFILE,1);
|
||||
if (pedomData) {
|
||||
if (pedomData.lastUpdate)
|
||||
lastUpdate = new Date(pedomData.lastUpdate);
|
||||
|
|
|
@ -10,16 +10,10 @@ var APPDIR = ROOTDIR+'/apps';
|
|||
var APPJSON = ROOTDIR+'/apps.json';
|
||||
var OUTFILE = ROOTDIR+'/firmware.js';
|
||||
var APPS = [ // IDs of apps to install
|
||||
"boot",
|
||||
"launch",
|
||||
"mclock",
|
||||
"setting",
|
||||
"astroid",
|
||||
"gpstime",
|
||||
"compass",
|
||||
"sbt",
|
||||
"sbat"
|
||||
"boot","launch","mclock","setting",
|
||||
"about","alarm","widbat","widbt","welcome"
|
||||
];
|
||||
var MINIFY = true;
|
||||
|
||||
var fs = require("fs");
|
||||
var AppInfo = require(ROOTDIR+"/appinfo.js");
|
||||
|
@ -28,14 +22,27 @@ var appfiles = [];
|
|||
|
||||
function fileGetter(url) {
|
||||
console.log("Loading "+url)
|
||||
/*if (url.endsWith(".js")) {
|
||||
var f = url.slice(0,-3);
|
||||
console.log("MINIFYING "+f);
|
||||
const execSync = require('child_process').execSync;
|
||||
code = execSync(`espruino --board BANGLEJS --minify ${f}.js -o ${f}.min.js`);
|
||||
console.log(code.toString());
|
||||
url = f+".min.js";
|
||||
}*/
|
||||
if (MINIFY) {
|
||||
/*if (url.endsWith(".js")) {
|
||||
var f = url.slice(0,-3);
|
||||
console.log("MINIFYING "+f);
|
||||
const execSync = require('child_process').execSync;
|
||||
// --config PRETOKENISE=true
|
||||
// --minify
|
||||
code = execSync(`espruino --config SET_TIME_ON_WRITE=false --minify --board BANGLEJS ${f}.js -o ${f}.min.js`);
|
||||
console.log(code.toString());
|
||||
url = f+".min.js";
|
||||
}*/
|
||||
if (url.endsWith(".json")) {
|
||||
var f = url.slice(0,-5);
|
||||
console.log("MINIFYING JSON "+f);
|
||||
var j = eval("("+fs.readFileSync(url).toString()+")");
|
||||
var code = JSON.stringify(j);
|
||||
//console.log(code);
|
||||
url = f+".min.json";
|
||||
fs.writeFileSync(url, code);
|
||||
}
|
||||
}
|
||||
return Promise.resolve(fs.readFileSync(url).toString());
|
||||
}
|
||||
|
||||
|
@ -50,6 +57,9 @@ Promise.all(APPS.map(appid => {
|
|||
var js = "// Generated by BangleApps/bin/firmwaremaker.js\n";
|
||||
appfiles.forEach((file) => {
|
||||
js += file.cmd+"\n";
|
||||
/*if (file.crc && file.evaluate!==true) {
|
||||
js += `\x10if (E.CRC32(require('Storage').read(${JSON.stringify(file.name)}))!=${file.crc}){console.log("${file.name} invalid");FAIL++}\n`;
|
||||
}*/
|
||||
});
|
||||
fs.writeFileSync(OUTFILE, js);
|
||||
console.log("Output written to "+OUTFILE);
|
||||
|
|
|
@ -18,11 +18,11 @@ try {
|
|||
var BASEDIR = __dirname+"/../";
|
||||
var APPSDIR = BASEDIR+"apps/";
|
||||
function ERROR(s) {
|
||||
console.error(s);
|
||||
console.error("ERROR: "+s);
|
||||
process.exit(1);
|
||||
}
|
||||
function WARN(s) {
|
||||
console.log(s);
|
||||
console.log("Warning: "+s);
|
||||
}
|
||||
|
||||
var appsFile, apps;
|
||||
|
@ -39,13 +39,24 @@ try{
|
|||
|
||||
apps.forEach((app,addIdx) => {
|
||||
if (!app.id) ERROR(`App ${appIdx} has no id`);
|
||||
console.log(`Checking ${app.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`);
|
||||
else {
|
||||
if (!fs.existsSync(appDir+"ChangeLog")) {
|
||||
if (app.version != "0.01")
|
||||
WARN(`App ${app.id} has no ChangeLog`);
|
||||
} else {
|
||||
var versions = fs.readFileSync(appDir+"ChangeLog").toString().match(/\d+\.\d+:/g);
|
||||
var lastChangeLog = versions.pop().slice(0,-1);
|
||||
if (lastChangeLog != app.version)
|
||||
WARN(`App ${app.id} app version (${app.version}) and ChangeLog (${lastChangeLog}) don't agree`);
|
||||
}
|
||||
}
|
||||
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`);
|
||||
|
@ -59,8 +70,10 @@ apps.forEach((app,addIdx) => {
|
|||
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`);
|
||||
var fileContents = "";
|
||||
if (file.content) fileContents = file.content;
|
||||
if (file.url) fileContents = fs.readFileSync(appDir+file.url).toString();
|
||||
if (file.evaluate) {
|
||||
var fileContents = file.content ? file.content : fs.readFileSync(appDir+file.url).toString();
|
||||
try {
|
||||
acorn.parse("("+fileContents+")");
|
||||
} catch(e) {
|
||||
|
@ -74,6 +87,21 @@ apps.forEach((app,addIdx) => {
|
|||
ERROR(`App ${app.id}'s ${file.name} has evaluate:true but is not valid JS expression`);
|
||||
}
|
||||
}
|
||||
if (file.name.endsWith(".js")) {
|
||||
// TODO: actual lint?
|
||||
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} is a JS file but isn't valid JS`);
|
||||
}
|
||||
}
|
||||
});
|
||||
//console.log(fileNames);
|
||||
if (isApp && !fileNames.includes(app.id+".app.js")) ERROR(`App ${app.id} has no entrypoint`);
|
||||
|
|
44
comms.js
|
@ -2,28 +2,47 @@ Puck.debug=3;
|
|||
|
||||
// FIXME: use UART lib so that we handle errors properly
|
||||
var Comms = {
|
||||
reset : () => new Promise((resolve,reject) => {
|
||||
Puck.write("\x03\x10reset();\n", (result) => {
|
||||
if (result===null) return reject("");
|
||||
reset : (opt) => new Promise((resolve,reject) => {
|
||||
Puck.write(`\x03\x10reset(${opt=="wipe"?"1":""});\n`, (result) => {
|
||||
if (result===null) return reject("Connection failed");
|
||||
setTimeout(resolve,500);
|
||||
});
|
||||
}),
|
||||
uploadApp : (app,skipReset) => {
|
||||
return AppInfo.getFiles(app, httpGet).then(fileContents => {
|
||||
return new Promise((resolve,reject) => {
|
||||
fileContents = fileContents.map(storageFile=>storageFile.cmd).join("\n")+"\n";
|
||||
console.log("uploadApp",fileContents);
|
||||
console.log("uploadApp",fileContents.map(f=>f.name).join(", "));
|
||||
// Upload each file one at a time
|
||||
function doUploadFiles() {
|
||||
// No files left - print 'reboot' message
|
||||
if (fileContents.length==0) {
|
||||
Puck.write(`\x10E.showMessage('Hold BTN3\\nto reload')\n`,(result) => {
|
||||
if (result===null) return reject("");
|
||||
resolve(app);
|
||||
});
|
||||
return;
|
||||
}
|
||||
var f = fileContents.shift();
|
||||
console.log(`Upload ${f.name} => ${JSON.stringify(f.content)}`);
|
||||
// Chould check CRC here if needed instead of returning 'OK'...
|
||||
// E.CRC32(require("Storage").read(${JSON.stringify(app.name)}))
|
||||
Puck.write(`\x10${f.cmd};Bluetooth.println("OK")\n`,(result) => {
|
||||
if (!result || result.trim()!="OK") return reject("Unexpected response "+(result||""));
|
||||
doUploadFiles();
|
||||
}, true); // wait for a newline
|
||||
}
|
||||
// Start the upload
|
||||
function doUpload() {
|
||||
Puck.write(`\x10E.showMessage('Uploading\\n${app.id}...')\n${fileContents}\x10E.showMessage('Hold BTN3\\nto reload')\n`,(result) => {
|
||||
Puck.write(`\x10E.showMessage('Uploading\\n${app.id}...')\n`,(result) => {
|
||||
if (result===null) return reject("");
|
||||
resolve(appJSON);
|
||||
doUploadFiles();
|
||||
});
|
||||
}
|
||||
if (skipReset) {
|
||||
doUpload();
|
||||
} else {
|
||||
// reset to ensure we have enough memory to upload what we need to
|
||||
Comms.reset().then(doUpload)
|
||||
Comms.reset().then(doUpload, reject)
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -32,7 +51,7 @@ getInstalledApps : () => {
|
|||
return new Promise((resolve,reject) => {
|
||||
Puck.write("\x03",(result) => {
|
||||
if (result===null) return reject("");
|
||||
Puck.eval('require("Storage").list(/\.info$/).map(f=>{var j=require("Storage").readJSON(f)||{};j.id=f.slice(0,-5);return j})', (appList,err) => {
|
||||
Puck.eval('require("Storage").list(/\.info$/).map(f=>{var j=require("Storage").readJSON(f,1)||{};j.id=f.slice(0,-5);return j})', (appList,err) => {
|
||||
if (appList===null) return reject(err || "");
|
||||
console.log("getInstalledApps", appList);
|
||||
resolve(appList);
|
||||
|
@ -41,7 +60,8 @@ getInstalledApps : () => {
|
|||
});
|
||||
},
|
||||
removeApp : app => { // expects an app structure
|
||||
var cmds = app.storage.map(file=>{
|
||||
var storage = [{name:app.id+".info"}].concat(app.storage);
|
||||
var cmds = storage.map(file=>{
|
||||
return `\x10require("Storage").erase(${toJS(file.name)});\n`;
|
||||
}).join("");
|
||||
console.log("removeApp", cmds);
|
||||
|
@ -53,7 +73,7 @@ removeApp : app => { // expects an app structure
|
|||
}));
|
||||
},
|
||||
removeAllApps : () => {
|
||||
return Comms.reset().then(() => new Promise((resolve,reject) => {
|
||||
return Comms.reset("wipe").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 || "");
|
||||
|
@ -68,7 +88,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.json',s);})(require('Storage').readJSON('setting.json'))\n";
|
||||
cmd += "(s=>{s&&(s.timezone="+tz+")&&require('Storage').write('setting.json',s);})(require('Storage').readJSON('setting.json',1))\n";
|
||||
Puck.write(cmd, (result) => {
|
||||
if (result===null) return reject("");
|
||||
resolve();
|
||||
|
|
63
firmware.js
After Width: | Height: | Size: 472 B |
After Width: | Height: | Size: 926 B |
23
index.html
|
@ -29,13 +29,19 @@
|
|||
}
|
||||
.chip {
|
||||
cursor: pointer;
|
||||
}
|
||||
.tile-content { position: relative; }
|
||||
.link-github {
|
||||
position:absolute;
|
||||
top: 36px;
|
||||
left: -24px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header class="navbar-primary navbar">
|
||||
<section class="navbar-section">
|
||||
<a href="https://banglejs.com" class="navbar-brand mr-2"><img src="img/banglejs-logo-sml.png" alt="Bangle.js"> App Loader</a>
|
||||
<a href="https://banglejs.com" target="_blank" class="navbar-brand mr-2"><img src="img/banglejs-logo-sml.png" alt="Bangle.js"> App Loader</a>
|
||||
<!-- <a href="#" class="btn btn-link">...</a> -->
|
||||
</section>
|
||||
<section class="navbar-section">
|
||||
|
@ -50,9 +56,8 @@
|
|||
</header>
|
||||
|
||||
<div class="container" style="padding-top:4px">
|
||||
<p><span class="label label-error">App Loader is incompatible with 'old' Bangle.js firmwares</span>
|
||||
<a href="https://www.espruino.com/Bangle.js#firmware-updates" target="_blank">(<a href="http://forum.espruino.com/conversations/344251/" target="_blank">more info</a>) Please update to the <a href="https://www.espruino.com/Bangle.js#firmware-updates">latest firmware</a> or
|
||||
<a href="https://banglejs.com/oldapps/">use the legacy apps</a>.
|
||||
<p><b>Note:</b> If you have a version of Bangle.js firmware before 2v04, please update to the <a href="https://www.espruino.com/Bangle.js#firmware-updates" target="_blank">latest firmware</a> or
|
||||
<a href="https://banglejs.com/oldapps/">use the legacy app loader</a>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
@ -104,15 +109,15 @@
|
|||
<div class="container bangle-tab" id="aboutcontainer" style="display:none">
|
||||
<div class="hero bg-gray">
|
||||
<div class="hero-body">
|
||||
<a href="https://banglejs.com"><img src="img/banglejs-logo-mid.png" alt="Bangle.js"></a>
|
||||
<a href="https://banglejs.com" target="_blank"><img src="img/banglejs-logo-mid.png" alt="Bangle.js"></a>
|
||||
<h2>App Loader</h2>
|
||||
<p>A tool for uploading and removing apps from <a href="https://banglejs.com">Bangle.js Smart Watches</a></p>
|
||||
<p>A tool for uploading and removing apps from <a href="https://banglejs.com" target="_blank">Bangle.js Smart Watches</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container" style="padding-top: 8px;">
|
||||
<p>Using <a href="https://espruino.com/">Espruino</a>, Icons from <a href="https://icons8.com/">icons8.com</a></p>
|
||||
<p>Check out <a href="https://github.com/espruino/BangleApps">the Source on GitHub</a> for more information.</p>
|
||||
|
||||
<p>Check out <a href="https://github.com/espruino/BangleApps" target="_blank">the Source on GitHub</a>, or
|
||||
find out <a href="https://www.espruino.com/Bangle.js+App+Loader" target="_blank">how to add your own app</p>
|
||||
<p>Using <a href="https://espruino.com/" target="_blank">Espruino</a>, Icons from <a href="https://icons8.com/" target="_blank">icons8.com</a></p>
|
||||
|
||||
<h3>Utilities</h3>
|
||||
<p><button class="btn" id="settime">Set Bangle.js Time</button>
|
||||
|
|
8
index.js
|
@ -272,11 +272,12 @@ function refreshLibrary() {
|
|||
if (versionInfo) versionInfo = " <small>("+versionInfo+")</small>";
|
||||
return `<div class="tile column col-6 col-sm-12 col-xs-12">
|
||||
<div class="tile-icon">
|
||||
<figure class="avatar"><img src="apps/${app.icon?`${app.id}/${app.icon}`:"unknown.png"}" alt="${escapeHtml(app.name)}"></figure>
|
||||
<figure class="avatar"><img src="apps/${app.icon?`${app.id}/${app.icon}`:"unknown.png"}" alt="${escapeHtml(app.name)}"></figure><br/>
|
||||
</div>
|
||||
<div class="tile-content">
|
||||
<p class="tile-title text-bold">${escapeHtml(app.name)} ${versionInfo}</p>
|
||||
<p class="tile-subtitle">${escapeHtml(app.description)}</p>
|
||||
<a href="https://github.com/espruino/BangleApps/tree/master/apps/${app.id}" target="_blank" class="link-github"><img src="img/github-icon-sml.png" alt="See the code on GitHub"/></a>
|
||||
</div>
|
||||
<div class="tile-action">
|
||||
<button class="btn btn-link btn-action btn-lg ${(appInstalled&&app.interface)?"":"d-hide"}" appid="${app.id}" title="Download data from app"><i class="icon icon-download"></i></button>
|
||||
|
@ -302,13 +303,14 @@ function refreshLibrary() {
|
|||
// check icon to figure out what we should do
|
||||
if (icon.classList.contains("icon-share")) {
|
||||
// emulator
|
||||
var file = app.storage.find(f=>f.name[0]=='-');
|
||||
var file = app.storage.find(f=>f.name.endsWith('.js'));
|
||||
if (!file) {
|
||||
console.error("No entrypoint found for "+appid);
|
||||
return;
|
||||
}
|
||||
var baseurl = window.location.href;
|
||||
var url = baseurl+"apps/"+app.id+"/"+file.url;
|
||||
baseurl = baseurl.substr(0,baseurl.lastIndexOf("/"));
|
||||
var url = baseurl+"/apps/"+app.id+"/"+file.url;
|
||||
window.open(`https://espruino.com/ide/emulator.html?codeurl=${url}&upload`);
|
||||
} else if (icon.classList.contains("icon-upload")) {
|
||||
// upload
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
"acorn": ""
|
||||
},
|
||||
"scripts": {
|
||||
"test": "node bin/sanitycheck.js"
|
||||
"test": "node bin/sanitycheck.js",
|
||||
"start": "npx http-server"
|
||||
}
|
||||
}
|
||||
|
|