diff --git a/.eslintignore b/.eslintignore
index f28e67b54..1e3abd9ff 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,11 +1,6 @@
-apps/animclk/V29.LBM.js
-apps/banglerun/rollup.config.js
-apps/schoolCalendar/fullcalendar/main.js
-apps/authentiwatch/qr_packed.js
-apps/qrcode/qr-scanner.umd.min.js
-apps/gipy/pkg/gps.js
-apps/health/chart.min.js
-*.test.js
-# typescript/generated files
-apps/btadv/*.js
+# Needs to be ignored because it uses ESM export/import
+apps/gipy/pkg/gps.js
+
+# Needs to be ignored because it includes broken JS
+apps/health/chart.min.js
diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml
index 7c0cfca3a..bebe18748 100644
--- a/.github/workflows/nodejs.yml
+++ b/.github/workflows/nodejs.yml
@@ -11,10 +11,10 @@ jobs:
uses: actions/checkout@v3
with:
submodules: recursive
- - name: Use Node.js 16.x
+ - name: Use Node.js 18.x
uses: actions/setup-node@v3
with:
- node-version: 16.x
+ node-version: 18.x
- name: Install testing dependencies
run: npm ci
- name: Test all apps and widgets
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 000000000..69aa0ab3d
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,9 @@
+Contributing to BangleApps
+==========================
+
+https://github.com/espruino/BangleApps?tab=readme-ov-file#getting-started
+has some links to tutorials on developing for Bangle.js.
+
+Please check out the Wiki to get an idea what sort of things
+we'd like to see for contributed apps: https://github.com/espruino/BangleApps/wiki/App-Contribution
+
diff --git a/README.md b/README.md
index ed6a501ef..ee1c22061 100644
--- a/README.md
+++ b/README.md
@@ -251,7 +251,7 @@ and which gives information about the app for the Launcher.
"description": "...", // long description (can contain markdown)
"icon": "icon.png", // icon in apps/
"screenshots" : [ { "url":"screenshot.png" } ], // optional screenshot for app
- "type":"...", // optional(if app) -
+ "type":"...", // optional(if app) -
// 'app' - an application
// 'clock' - a clock - required for clocks to automatically start
// 'widget' - a widget
@@ -300,7 +300,7 @@ and which gives information about the app for the Launcher.
"customConnect": true, // if supplied, ensure we are connected to a device
// before the "custom.html" iframe is loaded. An
// onInit function in "custom.html" is then called
- // with info on the currently connected device.
+ // with info on the currently connected device.
"interface": "interface.html", // if supplied, apps/interface.html is loaded in an
// iframe, and it may interact with the connected Bangle
@@ -328,9 +328,9 @@ and which gives information about the app for the Launcher.
{"name":"appid.data.json", // filename used in storage
"storageFile":true // if supplied, file is treated as storageFile
"url":"", // if supplied URL of file to load (currently relative to apps/)
- "content":"...", // if supplied, this content is loaded directly
+ "content":"...", // if supplied, this content is loaded directly
"evaluate":true, // if supplied, data isn't quoted into a String before upload
- // (eg it's evaluated as JS)
+ // (eg it's evaluated as JS)
},
{"wildcard":"appid.data.*" // wildcard of filenames used in storage
}, // this is mutually exclusive with using "name"
@@ -424,9 +424,9 @@ See [apps/gpsrec/interface.html](the GPS Recorder) for a full example.
### Adding configuration to the "Settings" menu
-Apps (or widgets) can add their own settings to the "Settings" menu under "App/widget settings".
+Apps (or widgets) can add their own settings to the "Settings" menu under "App/widget settings".
To do so, the app needs to include a `settings.js` file, containing a single function
-that handles configuring the app.
+that handles configuring the app.
When the app settings are opened, this function is called with one
argument, `back`: a callback to return to the settings menu.
@@ -449,12 +449,12 @@ Example `settings.js`
'Monkeys': {
value: settings.monkeys,
onchange: (m) => {save('monkeys', m)}
- }
+ }
};
E.showMenu(appMenu)
})
```
-In this example the app needs to add `myappid.settings.js` to `storage` in `metadata.json`.
+In this example the app needs to add `myappid.settings.js` to `storage` in `metadata.json`.
It should also add `myappid.json` to `data`, to make sure it is cleaned up when the app is uninstalled.
```json
{ "id": "myappid",
@@ -554,6 +554,30 @@ You can use `g.setColor(r,g,b)` OR `g.setColor(16bitnumber)` - some common 16 bi
| GreenYellow | 0xAFE5 |
| Pink | 0xF81F |
+## Fonts
+
+A recent addition to Bangle.js is the ability to use extra fonts with support for more characters.
+For example [all regions](https://banglejs.com/apps/?id=fontall) or [extended](https://banglejs.com/apps/?id=fontext) fonts.
+
+Once installed, these apps cause a new font, `Intl` to be added to `Graphics`, which can be used with just `g.setFont("Intl")`.
+
+There is also a `font` library - this is not implemented yet, but more information about the planned implementation
+is available at https://github.com/espruino/BangleApps/issues/3109
+
+For now, to make your app work with the international font, you can check if `Graphics.prototype.setFontIntl` exists,
+and if so you can change the font you plan on using:
+
+```JS
+myFont = "6x8:2";
+if (Graphics.prototype.setFontIntl)
+ myFont = "Intl";
+```
+
+Any new Font library must contain the metadata `"icon": "app.png", "tags": "font", "type": "module", "provides_modules" : ["fonts"],`
+and should provide a `font` library, as well as a `boot.js` that adds `Graphics.prototype.setFontIntl`. If you plan on
+making a new library it's best to just copy one of the existing ones for now.
+
+
## API Reference
[Reference](http://www.espruino.com/Reference#software)
diff --git a/android.html b/android.html
index a6f393e25..a0bc6075a 100644
--- a/android.html
+++ b/android.html
@@ -2,7 +2,7 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/clockbg/lib.js b/apps/clockbg/lib.js
new file mode 100644
index 000000000..0da62453c
--- /dev/null
+++ b/apps/clockbg/lib.js
@@ -0,0 +1,27 @@
+let settings = Object.assign({
+ style : "randomcolor",
+ colors : ["#F00","#0F0","#00F"]
+},require("Storage").readJSON("clockbg.json")||{});
+if (settings.style=="image")
+ settings.img = require("Storage").read(settings.fn);
+if (settings.style=="randomcolor") {
+ settings.style = "color";
+ var n = (0|(Math.random()*settings.colors.length)) % settings.colors.length;
+ settings.color = settings.colors[n];
+}
+
+// Fill a rectangle with the current background style, rect = {x,y,w,h}
+// eg require("clockbg").fillRect({x:10,y:10,w:50,h:50})
+// require("clockbg").fillRect(Bangle.appRect)
+exports.fillRect = function(rect,y,x2,y2) {
+ if ("object"!=typeof rect) rect = {x:rect,y:y,w:1+x2-rect,h:1+y2-y};
+ if (settings.img) {
+ g.setClipRect(rect.x, rect.y, rect.x+rect.w-1, rect.y+rect.h-1).drawImage(settings.img).setClipRect(0,0,g.getWidth()-1,g.getHeight()-1);
+ } else if (settings.style == "color") {
+ g.setBgColor(settings.color).clearRect(rect);
+ } else {
+ console.log("clockbg: No background set!");
+ g.setBgColor(g.theme.bg).clearRect(rect);
+ }
+};
+
diff --git a/apps/clockbg/metadata.json b/apps/clockbg/metadata.json
new file mode 100644
index 000000000..456590907
--- /dev/null
+++ b/apps/clockbg/metadata.json
@@ -0,0 +1,21 @@
+{ "id": "clockbg",
+ "name": "Clock Backgrounds",
+ "shortName":"Backgrounds",
+ "version": "0.02",
+ "description": "Library that allows clocks to include a custom background, from a library or uploaded.",
+ "icon": "app.png",
+ "screenshots": [{"url":"screenshot.png"}],
+ "type": "module",
+ "readme": "README.md",
+ "provides_modules" : ["clockbg"],
+ "tags": "module,background",
+ "supports" : ["BANGLEJS2"],
+ "interface": "interface.html",
+ "storage": [
+ {"name":"clockbg","url":"lib.js"},
+ {"name":"clockbg.settings.js","url":"settings.js"}
+ ], "data": [
+ {"wildcard":"clockbg.bg*.img"},
+ {"name":"clockbg.json"}
+ ]
+}
diff --git a/apps/clockbg/screenshot.png b/apps/clockbg/screenshot.png
new file mode 100644
index 000000000..f9d395e74
Binary files /dev/null and b/apps/clockbg/screenshot.png differ
diff --git a/apps/clockbg/settings.js b/apps/clockbg/settings.js
new file mode 100644
index 000000000..05135a1b4
--- /dev/null
+++ b/apps/clockbg/settings.js
@@ -0,0 +1,95 @@
+(function(back) {
+let settings = Object.assign({
+ style : "randomcolor",
+ colors : ["#F00","#0F0","#00F"]
+},require("Storage").readJSON("clockbg.json")||{});
+
+function saveSettings() {
+ if (settings.style!="image")
+ delete settings.fn;
+ if (settings.style!="color")
+ delete settings.color;
+ if (settings.style!="randomcolor")
+ delete settings.colors;
+ require("Storage").writeJSON("clockbg.json", settings);
+}
+
+function getColorsImage(cols) {
+ var bpp = 1;
+ if (cols.length>4) bpp=4;
+ else if (cols.length>2) bpp=2;
+ var b = Graphics.createArrayBuffer(16*cols.length,16,bpp);
+ b.palette = new Uint16Array(1<{
+ b.setColor(i).fillRect(i*16,0,i*16+15,15);
+ b.palette[i] = g.toColor(c);
+ });
+ return "\0"+b.asImage("string");
+}
+
+function showModeMenu() {
+ E.showMenu({
+ "" : {title:/*LANG*/"Background", back:showMainMenu},
+ /*LANG*/"Solid Color" : function() {
+ var cols = ["#F00","#0F0","#FF0",
+ "#00F","#F0F","#0FF",
+ "#000","#888","#fff",];
+ var menu = {"":{title:/*LANG*/"Colors", back:showModeMenu}};
+ cols.forEach(col => {
+ menu["-"+getColorsImage([col])] = () => {
+ settings.style = "color";
+ settings.color = col;
+ saveSettings();
+ showMainMenu();
+ };
+ });
+ E.showMenu(menu);
+ },
+ /*LANG*/"Random Color" : function() {
+ var cols = [
+ ["#F00","#0F0","#FF0","#00F","#F0F","#0FF"],
+ ["#F00","#0F0","#00F"],
+ ];
+ var menu = {"":{title:/*LANG*/"Colors", back:showModeMenu}};
+ cols.forEach(col => {
+ menu[getColorsImage(col)] = () => {
+ settings.style = "randomcolor";
+ settings.colors = col;
+ saveSettings();
+ showMainMenu();
+ };
+ });
+ E.showMenu(menu);
+ },
+ /*LANG*/"Image" : function() {
+ let images = require("Storage").list(/clockbg\..*\.img/);
+ if (images.length) {
+ var menu = {"":{title:/*LANG*/"Images", back:showModeMenu}};
+ images.forEach(im => {
+ menu[im.slice(8,-4)] = () => {
+ settings.style = "image";
+ settings.fn = im;
+ saveSettings();
+ showMainMenu();
+ };
+ });
+ E.showMenu(menu);
+ } else {
+ E.showAlert("Please use App Loader to upload images").then(showModeMenu);
+ }
+ },
+ });
+}
+
+function showMainMenu() {
+ E.showMenu({
+ "" : {title:/*LANG*/"Clock Background", back:back},
+ /*LANG*/"Mode" : {
+ value : settings.style,
+ onchange : showModeMenu
+ }
+ });
+}
+
+showMainMenu();
+})
\ No newline at end of file
diff --git a/apps/clockcal/ChangeLog b/apps/clockcal/ChangeLog
index 5657bf26d..6780313ce 100644
--- a/apps/clockcal/ChangeLog
+++ b/apps/clockcal/ChangeLog
@@ -5,4 +5,6 @@
0.05: Improved colors (connected vs disconnected)
0.06: Tell clock widgets to hide.
0.07: Convert Yes/No On/Off in settings to checkboxes
-0.08: Fixed typo in settings.js for DRAGDOWN to make option work
\ No newline at end of file
+0.08: Fixed typo in settings.js for DRAGDOWN to make option work
+0.09: You can now back out of the calendar using the button
+0.10: Fix linter warnings
diff --git a/apps/clockcal/README.md b/apps/clockcal/README.md
index d30205be0..bc05081ad 100644
--- a/apps/clockcal/README.md
+++ b/apps/clockcal/README.md
@@ -7,23 +7,24 @@ I know that it seems redundant because there already **is** a *time&cal*-app, bu
|:--:|:-|
||locked: triggers only one minimal update/min|
||unlocked: smaller clock, but with seconds|
-||swipe up for big calendar, (up down to scroll, left/right to exit)|
+||swipe up for big calendar ⬆️/⬇️ to scroll ⬅️/➡️ to exit|
## Configurable Features
- Number of calendar rows (weeks)
-- Buzz on connect/disconnect (I know, this should be an extra widget, but for now, it is included)
+- Buzz on connect/disconnect (feel free to disable and use a widget)
- Clock Mode (24h/12h). (No am/pm indicator)
- First day of the week
- Red Saturday/Sunday
- Swipe/Drag gestures to launch features or apps.
-## Auto detects your message/music apps:
-- swiping down will search your files for an app with the string "message" in its filename and launch it. (configurable)
-- swiping right will search your files for an app with the string "music" in its filename and launch it. (configurable)
+## Integrated swipe launcher: (Configure in Settings)
+- ⬇️ (down) will search your files for an app with the string "**message**"
+- ➡️ (right) will search your files for an app with the string "**music**"
+- ⬅️ (left) will search your files for an app with the string "**agenda**"
+- ⬆️ (up) will show the **internal full calendar**
## Feedback
-The clock works for me in a 24h/MondayFirst/WeekendFree environment but is not well-tested with other settings.
-So if something isn't working, please tell me: https://github.com/foostuff/BangleApps/issues
+If something isn't working, please tell me: https://github.com/Stuff-etc/BangleApps/issues (I moved my github repo)
## Planned features:
- Internal lightweight music control, because switching apps has a loading time.
diff --git a/apps/clockcal/app.js b/apps/clockcal/app.js
index 58ddd7ef5..185f2adea 100644
--- a/apps/clockcal/app.js
+++ b/apps/clockcal/app.js
@@ -24,15 +24,25 @@ const DEBUG = false;
var state = "watch";
var monthOffset = 0;
+// FIXME: These variables should maybe be defined inside relevant functions below. The linter complained they were not defined (i.e. they were added to global scope if I understand correctly).
+let dayInterval;
+let secondInterval;
+let minuteInterval;
+let newmonth;
+let bottomrightY;
+let bottomrightX;
+let rMonth;
+let dimSeconds;
+
/*
* Calendar features
*/
function drawFullCalendar(monthOffset) {
- addMonths = function (_d, _am) {
- var ay = 0, m = _d.getMonth(), y = _d.getFullYear();
+ const addMonths = function (_d, _am) {
+ let ay = 0, m = _d.getMonth(), y = _d.getFullYear();
while ((m + _am) > 11) { ay++; _am -= 12; }
while ((m + _am) < 0) { ay--; _am += 12; }
- n = new Date(_d.getTime());
+ let n = new Date(_d.getTime());
n.setMonth(m + _am);
n.setFullYear(y + ay);
return n;
@@ -45,10 +55,10 @@ function drawFullCalendar(monthOffset) {
if (typeof dayInterval !== "undefined") clearTimeout(dayInterval);
if (typeof secondInterval !== "undefined") clearTimeout(secondInterval);
if (typeof minuteInterval !== "undefined") clearTimeout(minuteInterval);
- d = addMonths(Date(), monthOffset);
- tdy = Date().getDate() + "." + Date().getMonth();
+ var d = addMonths(Date(), monthOffset);
+ let tdy = Date().getDate() + "." + Date().getMonth();
newmonth = false;
- c_y = 0;
+ let c_y = 0;
g.reset();
g.setBgColor(0);
g.clear();
@@ -60,8 +70,8 @@ function drawFullCalendar(monthOffset) {
rD.setDate(rD.getDate() - dow);
var rDate = rD.getDate();
bottomrightY = c_y - 3;
- clrsun = s.REDSUN ? '#f00' : '#fff';
- clrsat = s.REDSUN ? '#f00' : '#fff';
+ let clrsun = s.REDSUN ? '#f00' : '#fff';
+ let clrsat = s.REDSUN ? '#f00' : '#fff';
var fg = [clrsun, '#fff', '#fff', '#fff', '#fff', '#fff', clrsat];
for (var y = 1; y <= 11; y++) {
bottomrightY += CELL_H;
@@ -90,7 +100,7 @@ function caldrawMonth(rDate, c, m, rD) {
g.setColor(c);
g.setFont("Vector", 18);
g.setFontAlign(-1, 1, 1);
- drawyear = ((rMonth % 11) == 0) ? String(rD.getFullYear()).substr(-2) : "";
+ let drawyear = ((rMonth % 11) == 0) ? String(rD.getFullYear()).substr(-2) : "";
g.drawString(m + drawyear, bottomrightX, bottomrightY - CELL_H, 1);
newmonth = false;
}
@@ -124,7 +134,7 @@ function drawMinutes() {
var d = new Date();
var hours = s.MODE24 ? d.getHours().toString().padStart(2, ' ') : ((d.getHours() + 24) % 12 || 12).toString().padStart(2, ' ');
var minutes = d.getMinutes().toString().padStart(2, '0');
- var textColor = NRF.getSecurityStatus().connected ? '#99f' : '#fff';
+ var textColor = NRF.getSecurityStatus().connected ? '#fff' : '#f00';
var size = 50;
var clock_x = (w - 20) / 2;
if (dimSeconds) {
@@ -156,7 +166,7 @@ function drawSeconds() {
}
function drawWatch() {
- if (DEBUG) console.log("CALENDAR");
+ if (DEBUG) console.log("DRAWWATCH");
monthOffset = 0;
state = "watch";
var d = new Date();
@@ -197,6 +207,7 @@ function drawWatch() {
if (DEBUG) console.log("Next Day:" + (nextday / 3600));
if (typeof dayInterval !== "undefined") clearTimeout(dayInterval);
dayInterval = setTimeout(drawWatch, nextday * 1000);
+ if (DEBUG) console.log("ended DRAWWATCH. next refresh in " + nextday + "s");
}
function BTevent() {
@@ -211,8 +222,12 @@ function action(a) {
g.reset();
if (typeof secondInterval !== "undefined") clearTimeout(secondInterval);
if (DEBUG) console.log("action:" + a);
+ state = "unknown";
+ console.log("state -> unknown");
+ let l;
switch (a) {
case "[ignore]":
+ drawWatch();
break;
case "[calend.]":
drawFullCalendar();
@@ -229,6 +244,12 @@ function action(a) {
load(l[0]);
} else E.showAlert("Message app not found", "Not found").then(drawWatch);
break;
+ case "[AI:agenda]":
+ l = require("Storage").list(RegExp("agenda.*app.js"));
+ if (l.length > 0) {
+ load(l[0]);
+ } else E.showAlert("Agenda app not found", "Not found").then(drawWatch);
+ break;
default:
l = require("Storage").list(RegExp(a + ".app.js"));
if (l.length > 0) {
@@ -276,7 +297,6 @@ function input(dir) {
drawWatch();
}
break;
-
}
}
@@ -309,3 +329,10 @@ NRF.on('disconnect', BTevent);
dimSeconds = Bangle.isLocked();
drawWatch();
+setWatch(function() {
+ if (state == "watch") {
+ Bangle.showLauncher()
+ } else if (state == "calendar") {
+ drawWatch();
+ }
+}, BTN1, {repeat:true, edge:"falling"});
diff --git a/apps/clockcal/metadata.json b/apps/clockcal/metadata.json
index 998115827..b84b08575 100644
--- a/apps/clockcal/metadata.json
+++ b/apps/clockcal/metadata.json
@@ -1,7 +1,7 @@
{
"id": "clockcal",
"name": "Clock & Calendar",
- "version": "0.08",
+ "version": "0.10",
"description": "Clock with Calendar",
"readme":"README.md",
"icon": "app.png",
diff --git a/apps/clockcal/settings.js b/apps/clockcal/settings.js
index a406f3cf7..ea613f5c0 100644
--- a/apps/clockcal/settings.js
+++ b/apps/clockcal/settings.js
@@ -1,6 +1,6 @@
(function (back) {
var FILE = "clockcal.json";
- defaults={
+ const defaults={
CAL_ROWS: 4, //number of calendar rows.(weeks) Shouldn't exceed 5 when using widgets.
BUZZ_ON_BT: true, //2x slow buzz on disconnect, 2x fast buzz on connect. Will be extra widget eventually
MODE24: true, //24h mode vs 12h mode
@@ -9,19 +9,19 @@
REDSAT: true, // Use red color for saturday?
DRAGDOWN: "[AI:messg]",
DRAGRIGHT: "[AI:music]",
- DRAGLEFT: "[ignore]",
+ DRAGLEFT: "[AI:agenda]",
DRAGUP: "[calend.]"
};
- settings = Object.assign(defaults, require('Storage').readJSON(FILE, true) || {});
+ let settings = Object.assign(defaults, require('Storage').readJSON(FILE, true) || {});
- actions = ["[ignore]","[calend.]","[AI:music]","[AI:messg]"];
+ let actions = ["[ignore]","[calend.]","[AI:music]","[AI:messg]","[AI:agenda]"];
require("Storage").list(RegExp(".app.js")).forEach(element => actions.push(element.replace(".app.js","")));
function writeSettings() {
require('Storage').writeJSON(FILE, settings);
}
- menu = {
+ const menu = {
"": { "title": "Clock & Calendar" },
"< Back": () => back(),
'Buzz(dis)conn.?': {
diff --git a/apps/color_catalog/ChangeLog b/apps/color_catalog/ChangeLog
index b79d0c85b..1ae723feb 100644
--- a/apps/color_catalog/ChangeLog
+++ b/apps/color_catalog/ChangeLog
@@ -1 +1,2 @@
0.01: 1st ver,RGB565 and RGB888 colors in a common UI/UX
+0.02: Minor code improvements
diff --git a/apps/color_catalog/app.js b/apps/color_catalog/app.js
index 58951d1c6..d5014bcaa 100644
--- a/apps/color_catalog/app.js
+++ b/apps/color_catalog/app.js
@@ -11,7 +11,7 @@ var v_model=process.env.BOARD;
console.log("device="+v_model);
var x_max_screen=g.getWidth();//240;
- var y_max_screen=g.getHeight(); //240;
+ //var y_max_screen=g.getHeight(); //240;
var y_wg_bottom=g.getHeight()-25;
var y_wg_top=25;
if (v_model=='BANGLEJS') {
@@ -20,7 +20,7 @@ var v_model=process.env.BOARD;
var y_btn2=124; //harcoded for bangle.js cuz it is not the half of
} else x_max_usable_area=240;
- var contador=1;
+ //var contador=1;
var cont_items=0;
var cont_row=0;
var v_boxes_row=4;
@@ -31,7 +31,7 @@ var v_model=process.env.BOARD;
var v_font1size=11;
var v_fontsize=13;
var v_color_b_area='#111111';//black
- var v_color_b_area2=0x5AEB;//Dark
+ //var v_color_b_area2=0x5AEB;//Dark
var v_color_text='#FB0E01';
var v_color_statictxt='#e56e06'; //orange RGB format rrggbb
//RGB565 requires only 16 (5+6+5) bits/2 bytes
diff --git a/apps/color_catalog/metadata.json b/apps/color_catalog/metadata.json
index 3146a146f..0669e6b0b 100644
--- a/apps/color_catalog/metadata.json
+++ b/apps/color_catalog/metadata.json
@@ -2,7 +2,7 @@
"id": "color_catalog",
"name": "Colors Catalog",
"shortName": "Colors Catalog",
- "version": "0.01",
+ "version": "0.02",
"description": "Displays RGB565 and RGB888 colors, its name and code in screen.",
"icon": "app.png",
"tags": "Color,input,buttons,touch,UI",
diff --git a/apps/colorful_clock/ChangeLog b/apps/colorful_clock/ChangeLog
index 54ee389e3..e38a7c5a5 100644
--- a/apps/colorful_clock/ChangeLog
+++ b/apps/colorful_clock/ChangeLog
@@ -1,3 +1,4 @@
...
0.03: First update with ChangeLog Added
0.04: Tell clock widgets to hide.
+0.05: Minor code improvements
diff --git a/apps/colorful_clock/app.js b/apps/colorful_clock/app.js
index ba6272e9b..b58892311 100644
--- a/apps/colorful_clock/app.js
+++ b/apps/colorful_clock/app.js
@@ -120,7 +120,6 @@
let twoPi = 2*Math.PI;
let Pi = Math.PI;
- let halfPi = Math.PI/2;
let sin = Math.sin, cos = Math.cos;
diff --git a/apps/colorful_clock/metadata.json b/apps/colorful_clock/metadata.json
index 237acf81c..9e77e12c5 100644
--- a/apps/colorful_clock/metadata.json
+++ b/apps/colorful_clock/metadata.json
@@ -1,7 +1,7 @@
{ "id": "colorful_clock",
"name": "Colorful Analog Clock",
"shortName":"Colorful Clock",
- "version":"0.04",
+ "version": "0.05",
"description": "a colorful analog clock",
"icon": "app-icon.png",
"type": "clock",
diff --git a/apps/colorwheel/app.js b/apps/colorwheel/app.js
index 7874c3f54..e8367d329 100644
--- a/apps/colorwheel/app.js
+++ b/apps/colorwheel/app.js
@@ -64,13 +64,14 @@
switch (true) {
case (Radius > outerRadius): Color = '#000000'; break;
case (Radius < innerRadius): Color = '#FFFFFF'; break;
- default:
+ default: {
let Phi = Math.atan2(dy,dx) + halfPi;
if (Phi < 0) { Phi += twoPi; }
if (Phi > twoPi) { Phi -= twoPi; }
let Index = Math.floor(12*Phi/twoPi);
Color = ColorList[Index];
+ }
}
g.setColor(1,1,1);
g.fillCircle(CenterX,CenterY, innerRadius);
diff --git a/apps/configurable_clock/ChangeLog b/apps/configurable_clock/ChangeLog
index 9d55c1a91..59708756a 100644
--- a/apps/configurable_clock/ChangeLog
+++ b/apps/configurable_clock/ChangeLog
@@ -1,3 +1,4 @@
...
0.02: First update with ChangeLog Added
0.03: Tell clock widgets to hide.
+0.04: Minor code improvements
diff --git a/apps/configurable_clock/app.js b/apps/configurable_clock/app.js
index 45c86c7e9..4192954ae 100644
--- a/apps/configurable_clock/app.js
+++ b/apps/configurable_clock/app.js
@@ -748,7 +748,6 @@
let twoPi = 2*Math.PI, deg2rad = Math.PI/180;
let Pi = Math.PI;
- let halfPi = Math.PI/2;
let sin = Math.sin, cos = Math.cos;
@@ -894,7 +893,7 @@
g.setFontAlign(-1,0);
g.drawString('9', CenterX-outerRadius,CenterY);
break;
- case '1-12':
+ case '1-12': {
let innerRadius = outerRadius * 0.9 - 10;
let dark = g.theme.dark;
@@ -942,6 +941,7 @@
g.drawString(i == 0 ? '12' : '' + i, x,y);
}
+ }
}
let now = new Date();
diff --git a/apps/configurable_clock/metadata.json b/apps/configurable_clock/metadata.json
index 687a5b212..246a5dc21 100644
--- a/apps/configurable_clock/metadata.json
+++ b/apps/configurable_clock/metadata.json
@@ -1,7 +1,7 @@
{ "id": "configurable_clock",
"name": "Configurable Analog Clock",
"shortName":"Configurable Clock",
- "version":"0.03",
+ "version": "0.04",
"description": "an analog clock with several kinds of faces, hands and colors to choose from",
"icon": "app-icon.png",
"type": "clock",
diff --git a/apps/contacts/ChangeLog b/apps/contacts/ChangeLog
index 5560f00bc..b09594212 100644
--- a/apps/contacts/ChangeLog
+++ b/apps/contacts/ChangeLog
@@ -1 +1,3 @@
0.01: New App!
+0.02: Minor code improvements
+0.03: Minor code improvements
diff --git a/apps/contacts/contacts.app.js b/apps/contacts/contacts.app.js
index 85eef625b..8d20e8646 100644
--- a/apps/contacts/contacts.app.js
+++ b/apps/contacts/contacts.app.js
@@ -2,8 +2,8 @@
var Layout = require("Layout");
-const W = g.getWidth();
-const H = g.getHeight();
+//const W = g.getWidth();
+//const H = g.getHeight();
var wp = require('Storage').readJSON("contacts.json", true) || [];
// Use this with corrupted contacts
@@ -66,7 +66,7 @@ function showNumpad(text, key_, callback) {
s = key + text.substr(key.length, 999);
g.setFont("Vector:24").setFontAlign(1,0).drawString(s,g.getWidth(),12);
}
- ds="12%";
+ const ds="12%";
var numPad = new Layout ({
type:"v", c: [{
type:"v", c: [
@@ -131,7 +131,6 @@ function removeCard() {
}
function askPosition(callback) {
- let full = "";
showNumpad("dddDDDddd", "", function() {
callback(key, "");
});
diff --git a/apps/contacts/metadata.json b/apps/contacts/metadata.json
index 1228fd0bf..525c7ef6e 100644
--- a/apps/contacts/metadata.json
+++ b/apps/contacts/metadata.json
@@ -1,6 +1,6 @@
{ "id": "contacts",
"name": "Contacts",
- "version":"0.01",
+ "version": "0.03",
"description": "Provides means of storing user contacts, viewing/editing them on device and from the App loader",
"icon": "app.png",
"tags": "tool",
diff --git a/apps/contourclock/ChangeLog b/apps/contourclock/ChangeLog
index 2fa26b89a..6078b4ff3 100644
--- a/apps/contourclock/ChangeLog
+++ b/apps/contourclock/ChangeLog
@@ -11,3 +11,4 @@
0.29: Fixed a bug that would leave old font files in storage.
0.30: Added options to show widgets and date on twist and tap. New fonts.
0.31: Bugfix, no more freeze.
+0.32: Minor code improvements
diff --git a/apps/contourclock/app.js b/apps/contourclock/app.js
index 1a510f014..55d29a3dd 100644
--- a/apps/contourclock/app.js
+++ b/apps/contourclock/app.js
@@ -2,8 +2,8 @@
let drawTimeout;
let extrasTimeout;
let onLock;
- let onTap;
- let onTwist;
+ //let onTap;
+ //let onTwist;
let settings = require('Storage').readJSON("contourclock.json", true) || {};
if (settings.fontIndex == undefined) {
settings.fontIndex = 0;
@@ -60,7 +60,6 @@
drawTimeout = undefined;
draw();
}, 60000 - (Date.now() % 60000));
- let date = new Date();
g.reset();
if (extrasShown) drawExtras();
else hideExtras();
diff --git a/apps/contourclock/metadata.json b/apps/contourclock/metadata.json
index 5c97ce933..61c0f5643 100644
--- a/apps/contourclock/metadata.json
+++ b/apps/contourclock/metadata.json
@@ -1,7 +1,7 @@
{ "id": "contourclock",
"name": "Contour Clock",
"shortName" : "Contour Clock",
- "version":"0.31",
+ "version": "0.32",
"icon": "app.png",
"readme": "README.md",
"description": "A Minimalist clockface with large Digits.",
diff --git a/apps/coretemp/ChangeLog b/apps/coretemp/ChangeLog
index 7386bbc35..30c775a49 100644
--- a/apps/coretemp/ChangeLog
+++ b/apps/coretemp/ChangeLog
@@ -2,3 +2,4 @@
0.02: Cleanup interface and add settings, widget, add skin temp reporting.
0.03: Move code for recording to this app
0.04: Use default Bangle formatter for booleans
+0.05: Minor code improvements
diff --git a/apps/coretemp/coretemp.js b/apps/coretemp/coretemp.js
index 7cbbe3577..0337891e1 100644
--- a/apps/coretemp/coretemp.js
+++ b/apps/coretemp/coretemp.js
@@ -1,6 +1,6 @@
// Simply listen for core events and show data
-var btm = g.getHeight() - 1;
+//var btm = g.getHeight() - 1;
var px = g.getWidth() / 2;
// Dark or light logo
diff --git a/apps/coretemp/metadata.json b/apps/coretemp/metadata.json
index 87cb42722..2b7de0bf0 100644
--- a/apps/coretemp/metadata.json
+++ b/apps/coretemp/metadata.json
@@ -1,7 +1,7 @@
{
"id": "coretemp",
"name": "CoreTemp",
- "version": "0.04",
+ "version": "0.05",
"description": "Display CoreTemp device sensor data",
"icon": "coretemp.png",
"type": "app",
diff --git a/apps/counter2/ChangeLog b/apps/counter2/ChangeLog
new file mode 100644
index 000000000..58eacf613
--- /dev/null
+++ b/apps/counter2/ChangeLog
@@ -0,0 +1,4 @@
+0.01: New App!
+0.02: Added Settings & readme
+0.03: Fix lint warnings
+0.04: Fix lint warnings
diff --git a/apps/counter2/README.md b/apps/counter2/README.md
new file mode 100644
index 000000000..d57844aae
--- /dev/null
+++ b/apps/counter2/README.md
@@ -0,0 +1,24 @@
+# Counter2 by Michael
+
+I needed an HP/XP-Tracker for a game, so i made one.
+The counter state gets saved. Best to use this with pattern launcher or ClockCal
+
+- Colored Background Mode
+- 
+- Colored Text Mode
+- 
+
+## Howto
+ - Tap top side or swipe up to increase counter
+ - Tap bottom side or swipe down to decrease counter
+ - Hold (600ms) to reset to default value (configurable)
+ - Press button to exit
+
+## Configurable Features
+- Default value Counter 1
+- Default value Counter 2
+- Buzz on interact
+- Colored Text/Background
+
+## Feedback
+If something isn't working, please tell me: https://github.com/Stuff-etc/BangleApps/issues
diff --git a/apps/counter2/app-icon.js b/apps/counter2/app-icon.js
new file mode 100644
index 000000000..fda8d1e21
--- /dev/null
+++ b/apps/counter2/app-icon.js
@@ -0,0 +1 @@
+require("heatshrink").decompress(atob("mEwwcAyVJkgCFAwwCBAgd5CI+eCI2T/IRH/wR7n//AAPyCIdPBAX8CKpr/CLTpSCOipB8gRFXoPJCIknCJAIBOoYRCagLNCa4f8Q4gREI4tP8mT/41HCKJHFGoQRG+QKBLI4RHLIx9CCJ7zBGpxZCPoyhQYpIIBYor7kCP4R8YoX/WY69DAIM/BAT+BdIYICeYQRTGqKP/CNIA=="))
\ No newline at end of file
diff --git a/apps/counter2/app.js b/apps/counter2/app.js
new file mode 100644
index 000000000..42b59cf5d
--- /dev/null
+++ b/apps/counter2/app.js
@@ -0,0 +1,95 @@
+Bangle.loadWidgets();
+
+var s = Object.assign({
+ counter0:10,
+ counter1:20,
+ max0:15,
+ max1:25,
+ buzz: true,
+ colortext: true,
+}, require('Storage').readJSON("counter2.json", true) || {});
+
+const f1 = (s.colortext) ? "#f00" : "#fff";
+const f2 = (s.colortext) ? "#00f" : "#fff";
+const b1 = (s.colortext) ? g.theme.bg : "#f00";
+const b2 = (s.colortext) ? g.theme.bg : "#00f";
+
+var drag;
+
+const screenwidth = g.getWidth();
+const screenheight = g.getHeight();
+const halfwidth = screenwidth / 2;
+const halfheight = screenheight / 2;
+
+const counter = [];
+counter[0] = s.counter0;
+counter[1] = s.counter1;
+const defaults = [];
+defaults[0] = s.max0;
+defaults[1] = s.max1;
+
+function saveSettings() {
+ s.counter0 = counter[0];
+ s.counter1 = counter[1];
+ s.max0 = defaults[0];
+ s.max1 = defaults[1];
+ require('Storage').writeJSON("counter2.json", s);
+}
+
+let ignoreonce = false;
+var dragtimeout;
+
+function updateScreen() {
+ g.setBgColor(b1);
+ g.clearRect(0, 0, halfwidth, screenheight);
+ g.setBgColor(b2);
+ g.clearRect(halfwidth, 0, screenwidth, screenheight);
+ g.setFont("Vector", 60).setFontAlign(0, 0);
+ g.setColor(f1);
+ g.drawString(Math.floor(counter[0]), halfwidth * 0.5, halfheight);
+ g.setColor(f2);
+ g.drawString(Math.floor(counter[1]), halfwidth * 1.5, halfheight);
+ saveSettings();
+ if (s.buzz) Bangle.buzz(50,.5);
+ Bangle.drawWidgets();
+}
+
+Bangle.on("drag", e => {
+ const c = (e.x < halfwidth) ? 0 : 1;
+ if (!drag) {
+ if (ignoreonce) {
+ ignoreonce = false;
+ return;
+ }
+ drag = { x: e.x, y: e.y };
+ dragtimeout = setTimeout(function () { resetcounter(c); }, 600); //if dragging for 500ms, reset counter
+ }
+ else if (drag && !e.b) { // released
+ let adjust = 0;
+ const dx = e.x - drag.x, dy = e.y - drag.y;
+ if (Math.abs(dy) > Math.abs(dx) + 30) {
+ adjust = (dy > 0) ? -1 : 1;
+ } else {
+ adjust = (e.y > halfwidth) ? -1 : 1;
+ }
+ counter[c] += adjust;
+ updateScreen();
+ drag = undefined;
+ clearTimeout(dragtimeout);
+ }
+});
+
+function resetcounter(which) {
+ counter[which] = defaults[which];
+ console.log("resetting counter ", which);
+ updateScreen();
+ drag = undefined;
+ ignoreonce = true;
+}
+
+
+updateScreen();
+
+setWatch(function() {
+ load();
+}, BTN1, {repeat:true, edge:"falling"});
diff --git a/apps/counter2/counter2-icon.png b/apps/counter2/counter2-icon.png
new file mode 100644
index 000000000..c16e9c0c7
Binary files /dev/null and b/apps/counter2/counter2-icon.png differ
diff --git a/apps/counter2/counter2-screenshot.png b/apps/counter2/counter2-screenshot.png
new file mode 100644
index 000000000..0864acb64
Binary files /dev/null and b/apps/counter2/counter2-screenshot.png differ
diff --git a/apps/counter2/counter2dark-screenshot.png b/apps/counter2/counter2dark-screenshot.png
new file mode 100644
index 000000000..2f0fd07c1
Binary files /dev/null and b/apps/counter2/counter2dark-screenshot.png differ
diff --git a/apps/counter2/metadata.json b/apps/counter2/metadata.json
new file mode 100644
index 000000000..400abf267
--- /dev/null
+++ b/apps/counter2/metadata.json
@@ -0,0 +1,18 @@
+{
+ "id": "counter2",
+ "name": "Counter2",
+ "version": "0.04",
+ "description": "Dual Counter",
+ "readme":"README.md",
+ "icon": "counter2-icon.png",
+ "tags": "tool",
+ "supports": ["BANGLEJS2"],
+ "screenshots": [{"url":"counter2-screenshot.png"},{"url":"counter2dark-screenshot.png"}],
+ "allow_emulator": true,
+ "storage": [
+ {"name":"counter2.app.js","url":"app.js"},
+ {"name":"counter2.settings.js","url":"settings.js"},
+ {"name":"counter2.img","url":"app-icon.js","evaluate":true}
+ ],
+ "data": [{"name":"counter2.json"}]
+}
diff --git a/apps/counter2/settings.js b/apps/counter2/settings.js
new file mode 100644
index 000000000..b38df1824
--- /dev/null
+++ b/apps/counter2/settings.js
@@ -0,0 +1,55 @@
+(function (back) {
+ var FILE = "counter2.json";
+ const defaults={
+ counter0:12,
+ counter1:0,
+ max0:12,
+ max1:0,
+ buzz: true,
+ colortext: true,
+ };
+ const settings = Object.assign(defaults, require('Storage').readJSON(FILE, true) || {});
+
+ function writeSettings() {
+ require('Storage').writeJSON(FILE, settings);
+ }
+
+ const menu = {
+ "": { "title": "Counter2" },
+ "< Back": () => back(),
+ 'Default C1': {
+ value: settings[0],
+ min: -99, max: 99,
+ onchange: v => {
+ settings.max0 = v;
+ writeSettings();
+ }
+ },
+ 'Default C2': {
+ value: settings[2],
+ min: -99, max: 99,
+ onchange: v => {
+ settings.max1 = v;
+ writeSettings();
+ }
+ },
+ 'Color': {
+ value: settings.colortext,
+ format: v => v?"Text":"Backg",
+ onchange: v => {
+ settings.colortext = v;
+ console.log("Color",v);
+ writeSettings();
+ }
+ },
+ 'Vibrate': {
+ value: settings.buzz,
+ onchange: v => {
+ settings.buzz = v;
+ writeSettings();
+ }
+ }
+ };
+ // Show the menu
+ E.showMenu(menu);
+});
diff --git a/apps/cscsensor/ChangeLog b/apps/cscsensor/ChangeLog
index 5264e8d42..c01b35503 100644
--- a/apps/cscsensor/ChangeLog
+++ b/apps/cscsensor/ChangeLog
@@ -7,3 +7,4 @@
Improve connection code
0.07: Make Bangle.js 2 compatible
0.08: Convert Yes/No On/Off in settings to checkboxes
+0.09: Automatically reconnect on error
diff --git a/apps/cscsensor/cscsensor.app.js b/apps/cscsensor/cscsensor.app.js
index 4ebe7d57e..1ad7a9e98 100644
--- a/apps/cscsensor/cscsensor.app.js
+++ b/apps/cscsensor/cscsensor.app.js
@@ -226,7 +226,7 @@ function getSensorBatteryLevel(gatt) {
function connection_setup() {
mySensor.screenInit = true;
E.showMessage("Scanning for CSC sensor...");
- NRF.requestDevice({ filters: [{services:["1816"]}]}).then(function(d) {
+ NRF.requestDevice({ filters: [{services:["1816"]}], maxInterval: 100}).then(function(d) {
device = d;
E.showMessage("Found device");
return device.gatt.connect();
@@ -249,6 +249,7 @@ function connection_setup() {
}).catch(function(e) {
E.showMessage(e.toString(), "ERROR");
console.log(e);
+ setTimeout(connection_setup, 1000);
});
}
diff --git a/apps/cscsensor/metadata.json b/apps/cscsensor/metadata.json
index d7c3add53..5d70251da 100644
--- a/apps/cscsensor/metadata.json
+++ b/apps/cscsensor/metadata.json
@@ -2,7 +2,7 @@
"id": "cscsensor",
"name": "Cycling speed sensor",
"shortName": "CSCSensor",
- "version": "0.08",
+ "version": "0.09",
"description": "Read BLE enabled cycling speed and cadence sensor and display readings on watch",
"icon": "icons8-cycling-48.png",
"tags": "outdoors,exercise,ble,bluetooth,bike,cycle,bicycle",
diff --git a/apps/cycling/ChangeLog b/apps/cycling/ChangeLog
index ec66c5568..b7e50d38d 100644
--- a/apps/cycling/ChangeLog
+++ b/apps/cycling/ChangeLog
@@ -1 +1,2 @@
0.01: Initial version
+0.02: Minor code improvements
diff --git a/apps/cycling/blecsc-emu.js b/apps/cycling/blecsc-emu.js
index ca5058545..1a313e08a 100644
--- a/apps/cycling/blecsc-emu.js
+++ b/apps/cycling/blecsc-emu.js
@@ -1,5 +1,5 @@
// UUID of the Bluetooth CSC Service
-const SERVICE_UUID = "1816";
+//const SERVICE_UUID = "1816";
// UUID of the CSC measurement characteristic
const MEASUREMENT_UUID = "2a5b";
diff --git a/apps/cycling/metadata.json b/apps/cycling/metadata.json
index caf93eda3..95c0ca068 100644
--- a/apps/cycling/metadata.json
+++ b/apps/cycling/metadata.json
@@ -2,7 +2,7 @@
"id": "cycling",
"name": "Bangle Cycling",
"shortName": "Cycling",
- "version": "0.01",
+ "version": "0.02",
"description": "Display live values from a BLE CSC sensor",
"icon": "icons8-cycling-48.png",
"tags": "outdoors,exercise,ble,bluetooth",
diff --git a/apps/daisy/ChangeLog b/apps/daisy/ChangeLog
index 751164c07..e9568acfc 100644
--- a/apps/daisy/ChangeLog
+++ b/apps/daisy/ChangeLog
@@ -8,3 +8,4 @@
0.08: fix idle timer always getting set to true
0.09: Use 'modules/suncalc.js' to avoid it being copied 8 times for different apps
0.10: Use widget_utils.
+0.11: Minor code improvements
diff --git a/apps/daisy/app.js b/apps/daisy/app.js
index 3b3975105..cba3e762d 100644
--- a/apps/daisy/app.js
+++ b/apps/daisy/app.js
@@ -1,6 +1,4 @@
var SunCalc = require("suncalc"); // from modules folder
-const storage = require('Storage');
-const locale = require("locale");
const widget_utils = require('widget_utils');
const SETTINGS_FILE = "daisy.json";
const LOCATION_FILE = "mylocation.json";
@@ -196,9 +194,9 @@ function draw() {
function drawClock() {
var date = new Date();
- var timeStr = require("locale").time(date,1);
+ //var timeStr = require("locale").time(date,1);
var da = date.toString().split(" ");
- var time = da[4].substr(0,5);
+ //var time = da[4].substr(0,5);
var hh = da[4].substr(0,2);
var mm = da[4].substr(3,2);
var steps = getSteps();
diff --git a/apps/daisy/metadata.json b/apps/daisy/metadata.json
index 471f8e56f..a292ef777 100644
--- a/apps/daisy/metadata.json
+++ b/apps/daisy/metadata.json
@@ -1,6 +1,6 @@
{ "id": "daisy",
"name": "Daisy",
- "version":"0.10",
+ "version": "0.11",
"dependencies": {"mylocation":"app"},
"description": "A beautiful digital clock with large ring guage, idle timer and a cyclic information line that includes, day, date, steps, battery, sunrise and sunset times",
"icon": "app.png",
diff --git a/apps/dane_tcr/ChangeLog b/apps/dane_tcr/ChangeLog
index 69424b1f4..05ef79052 100644
--- a/apps/dane_tcr/ChangeLog
+++ b/apps/dane_tcr/ChangeLog
@@ -6,3 +6,4 @@
0.06: remove app image as it is unused
0.07: Bump version number for change to apps.json causing 404 on upload
0.08: Use default Bangle formatter for booleans
+0.09: Minor code improvements
diff --git a/apps/dane_tcr/app.js b/apps/dane_tcr/app.js
index ce75c55cb..ce8c98025 100644
--- a/apps/dane_tcr/app.js
+++ b/apps/dane_tcr/app.js
@@ -1,11 +1,6 @@
var d = require("dane_arwes");
var Arwes = d.default();
-const yOffset = 23;
-const width = g.getWidth();
-const height = g.getHeight();
-const xyCenter = width / 2 + 4;
-
const Storage = require("Storage");
const filename = 'dane_tcr.json';
let settings = Storage.readJSON(filename,1) || {
diff --git a/apps/dane_tcr/metadata.json b/apps/dane_tcr/metadata.json
index 5527c846d..c6a649f0e 100644
--- a/apps/dane_tcr/metadata.json
+++ b/apps/dane_tcr/metadata.json
@@ -2,7 +2,7 @@
"id": "dane_tcr",
"name": "DANE Touch Launcher",
"shortName": "DANE Toucher",
- "version": "0.08",
+ "version": "0.09",
"description": "Touch enable left to right launcher in the style of the DANE Watchface",
"icon": "app.png",
"type": "launch",
diff --git a/apps/dclock/clock-dev.js b/apps/dclock/clock-dev.js
index d2c3893d5..914234060 100644
--- a/apps/dclock/clock-dev.js
+++ b/apps/dclock/clock-dev.js
@@ -70,7 +70,7 @@ function drawSimpleClock() {
var dom = new Date(d.getFullYear(), d.getMonth()+1, 0).getDate();
//Days since full moon
- var knownnew = new Date(2020,02,24,09,28,0);
+ var knownnew = new Date(2020,2,24,9,28,0);
// Get millisecond difference and divide down to cycles
var cycles = (d.getTime()-knownnew.getTime())/1000/60/60/24/29.53;
diff --git a/apps/devstopwatch/ChangeLog b/apps/devstopwatch/ChangeLog
index 7e90e061e..11567d141 100644
--- a/apps/devstopwatch/ChangeLog
+++ b/apps/devstopwatch/ChangeLog
@@ -5,4 +5,6 @@
realigned quick n dirty screen positions
help adjusted to fit bangle1 & bangle2 screen-size with widgets
fixed bangle2 colors for chrono and last lap highlight
- added screen for bangle2 and a small README
\ No newline at end of file
+ added screen for bangle2 and a small README
+0.05: Minor code improvements
+0.06: Minor code improvements
diff --git a/apps/devstopwatch/app.js b/apps/devstopwatch/app.js
index d2a4b1117..747573c0c 100644
--- a/apps/devstopwatch/app.js
+++ b/apps/devstopwatch/app.js
@@ -12,7 +12,7 @@ const FONT = '6x8';
const CHRONO = '/* C H R O N O */';
-var reset = false;
+//var reset = false;
var currentLap = '';
var chronoInterval;
@@ -43,7 +43,7 @@ Bangle.setUI("clockupdown", btn=>{
function resetChrono() {
state.laps = [EMPTY_H, EMPTY_H, EMPTY_LAP, EMPTY_LAP, EMPTY_LAP, EMPTY_LAP, EMPTY_LAP];
state.started = false;
- reset = true;
+ //reset = true;
state.currentLapIndex = 1;
currentLap = '';
@@ -61,11 +61,11 @@ function chronometer() {
state.whenStarted = rightNow;
state.whenStartedTotal = rightNow;
state.started = true;
- reset = false;
+ //reset = false;
}
currentLap = calculateLap(state.whenStarted);
- total = calculateLap(state.whenStartedTotal);
+ const total = calculateLap(state.whenStartedTotal);
state.laps[0] = total;
state.laps[1] = currentLap;
@@ -123,7 +123,7 @@ function printChrono() {
g.setColor(g.theme.fg);
let suffix = ' ';
if (state.currentLapIndex === i) {
- let suffix = '*';
+ let suffix = '*'; //TODO: Should `let` be removed here?
if (process.env.HWVERSION==2) g.setColor("#0ee");
else g.setColor("#f70");
}
diff --git a/apps/devstopwatch/metadata.json b/apps/devstopwatch/metadata.json
index c4b6c7a67..f8e3fe106 100644
--- a/apps/devstopwatch/metadata.json
+++ b/apps/devstopwatch/metadata.json
@@ -2,7 +2,7 @@
"id": "devstopwatch",
"name": "Dev Stopwatch",
"shortName": "Dev Stopwatch",
- "version": "0.04",
+ "version": "0.06",
"description": "Stopwatch with 5 laps supported (cyclically replaced)",
"icon": "app.png",
"tags": "stopwatch,chrono,timer,chronometer",
diff --git a/apps/diceroll/ChangeLog b/apps/diceroll/ChangeLog
index 89dff4011..284e78368 100644
--- a/apps/diceroll/ChangeLog
+++ b/apps/diceroll/ChangeLog
@@ -1 +1,2 @@
-0.01: App created
\ No newline at end of file
+0.01: App created
+0.02: Minor code improvements
diff --git a/apps/diceroll/app.js b/apps/diceroll/app.js
index d514ce92f..61a3d9917 100644
--- a/apps/diceroll/app.js
+++ b/apps/diceroll/app.js
@@ -105,4 +105,4 @@ function main() {
Bangle.setLCDPower(1);
}
-var interval = setInterval(main, 300);
\ No newline at end of file
+setInterval(main, 300);
\ No newline at end of file
diff --git a/apps/diceroll/metadata.json b/apps/diceroll/metadata.json
index 81a2f8bfd..256ad8a80 100644
--- a/apps/diceroll/metadata.json
+++ b/apps/diceroll/metadata.json
@@ -2,7 +2,7 @@
"name": "Dice-n-Roll",
"shortName":"Dice-n-Roll",
"icon": "app.png",
- "version":"0.01",
+ "version": "0.02",
"description": "A dice app with a few different dice.",
"screenshots": [{"url":"diceroll_screenshot.png"}],
"tags": "game",
diff --git a/apps/diract/ChangeLog b/apps/diract/ChangeLog
index 34fc73a76..272d01ab8 100644
--- a/apps/diract/ChangeLog
+++ b/apps/diract/ChangeLog
@@ -1,2 +1,3 @@
0.01: New App!
0.02: Tweaked proximity identification settings
+0.03: Minor code improvements
diff --git a/apps/diract/diract.js b/apps/diract/diract.js
index 69f0a88e4..d4effca89 100644
--- a/apps/diract/diract.js
+++ b/apps/diract/diract.js
@@ -74,6 +74,7 @@ let digestTime = new Uint8Array([ 0, 0, 0 ]);
let numberOfDigestPages = 0;
let sensorData = [ 0x82, 0x08, 0x3f ];
let cyclicCount = 0;
+let encodedBattery = 0;
let lastDigestTime = Math.round(getTime());
let lastResetTime = Math.round(getTime());
let isExciterPresent = false;
@@ -517,7 +518,7 @@ function updateSensorData() {
encodedBattery = encodeBatteryPercentage();
}
- encodedAcceleration = encodeAcceleration();
+ let encodedAcceleration = encodeAcceleration();
sensorData[0] = ((encodedAcceleration.x << 2) & 0xfc) |
((encodedAcceleration.y >> 4) & 0x3f);
diff --git a/apps/diract/metadata.json b/apps/diract/metadata.json
index af9406e91..2b6cd810e 100644
--- a/apps/diract/metadata.json
+++ b/apps/diract/metadata.json
@@ -2,7 +2,7 @@
"id": "diract",
"name": "DirAct",
"shortName": "DirAct",
- "version": "0.02",
+ "version": "0.03",
"description": "Proximity interaction detection.",
"icon": "diract.png",
"type": "app",
diff --git a/apps/distortclk/ChangeLog b/apps/distortclk/ChangeLog
index 4c7291526..11be002af 100644
--- a/apps/distortclk/ChangeLog
+++ b/apps/distortclk/ChangeLog
@@ -1,2 +1,3 @@
0.01: New face!
0.02: Improved clock
+0.03: Minor code improvements
diff --git a/apps/distortclk/app.js b/apps/distortclk/app.js
index a9fdd1ef2..715899fbb 100644
--- a/apps/distortclk/app.js
+++ b/apps/distortclk/app.js
@@ -26,7 +26,7 @@ function time() {
var d = new Date();
var day = d.getDate();
var time = require("locale").time(d,1);
- var date = require("locale").date(d);
+ //var date = require("locale").date(d);
var mo = require("date_utils").month(d.getMonth()+1,0);
g.setFontAlign(0,0);
diff --git a/apps/distortclk/metadata.json b/apps/distortclk/metadata.json
index 125dac590..cd1bf9d4d 100644
--- a/apps/distortclk/metadata.json
+++ b/apps/distortclk/metadata.json
@@ -2,7 +2,7 @@
"id": "distortclk",
"name": "Distort Clock",
"shortName":"Distort Clock",
- "version": "0.02",
+ "version": "0.03",
"description": "A clockface",
"icon": "app.png",
"type": "clock",
diff --git a/apps/dotclock/ChangeLog b/apps/dotclock/ChangeLog
index 563db87e7..cb2e8bd49 100644
--- a/apps/dotclock/ChangeLog
+++ b/apps/dotclock/ChangeLog
@@ -1,3 +1,4 @@
0.01: Based on the Analog Clock app, minimal dot
0.02: Remove hardcoded hour buzz (you can install widchime if you miss it)
0.03: Use setUI, adjust for themes and different size screens
+0.04: Minor code improvements
diff --git a/apps/dotclock/clock-dot.js b/apps/dotclock/clock-dot.js
index 66255d1b4..0127cd488 100644
--- a/apps/dotclock/clock-dot.js
+++ b/apps/dotclock/clock-dot.js
@@ -1,6 +1,5 @@
const big = g.getWidth()>200;
const locale = require('locale');
-const p = Math.PI / 2;
const pRad = Math.PI / 180;
let timer = null;
let currentDate = new Date();
diff --git a/apps/dotclock/metadata.json b/apps/dotclock/metadata.json
index 396e63917..e8d7415fd 100644
--- a/apps/dotclock/metadata.json
+++ b/apps/dotclock/metadata.json
@@ -1,7 +1,7 @@
{
"id": "dotclock",
"name": "Dot Clock",
- "version": "0.03",
+ "version": "0.04",
"description": "A Minimal Dot Analog Clock",
"icon": "clock-dot.png",
"type": "clock",
diff --git a/apps/doztime/ChangeLog b/apps/doztime/ChangeLog
index 77d82eff9..03c1877b4 100644
--- a/apps/doztime/ChangeLog
+++ b/apps/doztime/ChangeLog
@@ -5,3 +5,4 @@
0.05: extraneous comments and code removed
display improved
now supports Adjust Clock widget, if installed
+0.06: Minor code improvements
\ No newline at end of file
diff --git a/apps/doztime/app-bangle1.js b/apps/doztime/app-bangle1.js
index 38c5acbac..a176ef270 100644
--- a/apps/doztime/app-bangle1.js
+++ b/apps/doztime/app-bangle1.js
@@ -164,7 +164,7 @@ function drawTime()
x =
10368*dt.getHours()+172.8*dt.getMinutes()+2.88*dt.getSeconds()+0.00288*dt.getMilliseconds();
let msg = "00000"+Math.floor(x).toString(12);
- let time = msg.substr(-5,3)+"."+msg.substr(-2);
+ let time = msg.substr(-5,3)+"."+msg.substr(-2); //TODO: should `time` and `wait` have been defined outside the if block?
let wait = 347*(1-(x%1));
timeDef = time6;
} else {
@@ -210,8 +210,8 @@ Bangle.loadWidgets();
Bangle.drawWidgets();
// Functions for weather mode - TODO
-function drawWeather() {}
-function modeWeather() {}
+//function drawWeather() {}
+//function modeWeather() {}
// Start time on twist
Bangle.on('twist', function() {
@@ -223,9 +223,8 @@ function fixTime() {
Bangle.on("GPS",function cb(g) {
Bangle.setGPSPower(0,"time");
Bangle.removeListener("GPS",cb);
- if (!g.time || (g.time.getFullYear()<2000) ||
- (g.time.getFullYear()>2200)) {
- } else {
+ if (g.time && (g.time.getFullYear()>=2000) &&
+ (g.time.getFullYear()<=2200)) {
// We have a GPS time. Set time
setTime(g.time.getTime()/1000);
}
diff --git a/apps/doztime/app-bangle2.js b/apps/doztime/app-bangle2.js
index 8a315118f..603ac8904 100644
--- a/apps/doztime/app-bangle2.js
+++ b/apps/doztime/app-bangle2.js
@@ -125,7 +125,7 @@ function formatDate(res,dateFormat){
}
function writeDozTime(text,def){
- let pts = def.pts;
+ //let pts = def.pts;
let x=def.pt0[0];
let y=def.pt0[1];
g_t.clear();
@@ -141,7 +141,7 @@ function writeDozTime(text,def){
function writeDozDate(text,def,colour){
dateColour = colour;
- let pts = def.pts;
+ //let pts = def.pts;
let x=def.pt0[0];
let y=def.pt0[1];
g_d.clear();
@@ -174,7 +174,7 @@ function drawTime()
x =
10368*dt.getHours()+172.8*dt.getMinutes()+2.88*dt.getSeconds()+0.00288*dt.getMilliseconds();
let msg = "00000"+Math.floor(x).toString(12);
- let time = msg.substr(-5,3)+"."+msg.substr(-2);
+ let time = msg.substr(-5,3)+"."+msg.substr(-2); //TODO: should `time` and `wait` have been defined outside the if block?
let wait = 347*(1-(x%1));
timeDef = time6;
} else {
diff --git a/apps/doztime/metadata.json b/apps/doztime/metadata.json
index a05bf1470..83be15724 100644
--- a/apps/doztime/metadata.json
+++ b/apps/doztime/metadata.json
@@ -2,7 +2,7 @@
"id": "doztime",
"name": "Dozenal Digital Time",
"shortName": "Dozenal Digital",
- "version": "0.05",
+ "version": "0.06",
"description": "A dozenal Holocene calendar and dozenal diurnal digital clock",
"icon": "app.png",
"type": "clock",
diff --git a/apps/drinkcounter/ChangeLog b/apps/drinkcounter/ChangeLog
index d8d174c4c..0541d11de 100644
--- a/apps/drinkcounter/ChangeLog
+++ b/apps/drinkcounter/ChangeLog
@@ -1,4 +1,5 @@
0.10: Initial release - still work in progress
0.15: Added settings and calculations
0.20: Added status saving
-0.25: Adopted for Bangle.js 1 - kind of
\ No newline at end of file
+0.25: Adopted for Bangle.js 1 - kind of
+0.26: Minor code improvements
diff --git a/apps/drinkcounter/app.js b/apps/drinkcounter/app.js
index 323d9fb41..b231930d7 100644
--- a/apps/drinkcounter/app.js
+++ b/apps/drinkcounter/app.js
@@ -22,7 +22,7 @@ const maxDrinks = 2; // 3 drinks
var firstDrinkTime = null;
var firstDrinkTimeTime = null;
-var confBeerSize;
+//var confBeerSize;
var confSex;
var confWeight;
var confWeightUnit;
@@ -97,7 +97,7 @@ function loadMySettings() {
function def (value, def) {return value !== undefined ? value : def;}
var settings = require('Storage').readJSON(SETTINGSFILE, true) || {};
- confBeerSize = def(settings.beerSize, "0.3L");
+ //confBeerSize = def(settings.beerSize, "0.3L");
confSex = def(settings.sex, "male");
confWeight = def(settings.weight, 80);
confWeightUnit = def(settings.weightUnit, "Kilo");
diff --git a/apps/drinkcounter/metadata.json b/apps/drinkcounter/metadata.json
index 2b8d7fe71..315a5845b 100644
--- a/apps/drinkcounter/metadata.json
+++ b/apps/drinkcounter/metadata.json
@@ -2,7 +2,7 @@
"id": "drinkcounter",
"name": "Drink Counter",
"shortName": "Drink Counter",
- "version": "0.25",
+ "version": "0.26",
"description": "Counts drinks you had for science. Calculates blood alcohol content (BAC)",
"allow_emulator":true,
"icon": "drinkcounter.png",
diff --git a/apps/dtlaunch/ChangeLog b/apps/dtlaunch/ChangeLog
index 6c096f45b..5cac5770e 100644
--- a/apps/dtlaunch/ChangeLog
+++ b/apps/dtlaunch/ChangeLog
@@ -29,3 +29,4 @@ immediately follows the correct theme.
when moving pages. Add caching for faster startups.
0.23: Bangle 1: Fix issue with missing icons, added touch screen interactions
0.24: Add buzz-on-interaction setting
+0.25: Minor code improvements
diff --git a/apps/dtlaunch/app-b2.js b/apps/dtlaunch/app-b2.js
index a3ddd2538..2108910fc 100644
--- a/apps/dtlaunch/app-b2.js
+++ b/apps/dtlaunch/app-b2.js
@@ -42,7 +42,7 @@
let Npages = Math.ceil(Napps/4);
let maxPage = Npages-1;
let selected = -1;
- let oldselected = -1;
+ //let oldselected = -1;
let page = 0;
const XOFF = 24;
const YOFF = 30;
@@ -104,7 +104,7 @@
let swipeListenerDt = function(dirLeftRight, dirUpDown){
updateTimeoutToClock();
selected = -1;
- oldselected=-1;
+ //oldselected=-1;
if(settings.swipeExit && dirLeftRight==1) Bangle.showClock();
if (dirUpDown==-1||dirLeftRight==-1){
++page; if (page>maxPage) page=0;
diff --git a/apps/dtlaunch/metadata.json b/apps/dtlaunch/metadata.json
index 5e25b61fb..bac0ed369 100644
--- a/apps/dtlaunch/metadata.json
+++ b/apps/dtlaunch/metadata.json
@@ -1,7 +1,7 @@
{
"id": "dtlaunch",
"name": "Desktop Launcher",
- "version": "0.24",
+ "version": "0.25",
"description": "Desktop style App Launcher with six (four for Bangle 2) apps per page - fast access if you have lots of apps installed.",
"screenshots": [{"url":"shot1.png"},{"url":"shot2.png"},{"url":"shot3.png"}],
"icon": "icon.png",
diff --git a/apps/dwm-clock/ChangeLog b/apps/dwm-clock/ChangeLog
index 5560f00bc..7727f3cc4 100644
--- a/apps/dwm-clock/ChangeLog
+++ b/apps/dwm-clock/ChangeLog
@@ -1 +1,2 @@
0.01: New App!
+0.02: Minor code improvements
diff --git a/apps/dwm-clock/app.js b/apps/dwm-clock/app.js
index 773777ca5..6d9bd3767 100644
--- a/apps/dwm-clock/app.js
+++ b/apps/dwm-clock/app.js
@@ -143,7 +143,7 @@ function renderScreen() {
}
function renderAndQueue() {
- timeoutID = setTimeout(renderAndQueue, 60000 - (Date.now() % 60000));
+ /*timeoutID =*/ setTimeout(renderAndQueue, 60000 - (Date.now() % 60000));
renderScreen();
}
@@ -162,7 +162,7 @@ var now = new Date();
var defLonOffset = getLongitudeOffset().lon;
var lonOffset = defLonOffset;
-var timeoutID;
+//var timeoutID;
var timeoutIDTouch;
Bangle.on('drag', function(touch) {
diff --git a/apps/dwm-clock/metadata.json b/apps/dwm-clock/metadata.json
index 677c78f17..2a03c396c 100644
--- a/apps/dwm-clock/metadata.json
+++ b/apps/dwm-clock/metadata.json
@@ -2,7 +2,7 @@
"id": "dwm-clock",
"name": "Daylight World Map Clock",
"shortName": "DWM Clock",
- "version": "0.01",
+ "version": "0.02",
"description": "A clock with a daylight world map",
"readme":"README.md",
"icon": "app.png",
diff --git a/apps/edisonsball/ChangeLog b/apps/edisonsball/ChangeLog
index b71b8bb0d..c871dbe41 100644
--- a/apps/edisonsball/ChangeLog
+++ b/apps/edisonsball/ChangeLog
@@ -1,2 +1,4 @@
0.01: Initial version
0.02: Added BangleJS Two
+0.03: Minor code improvements
+0.04: Minor code improvements
diff --git a/apps/edisonsball/app.js b/apps/edisonsball/app.js
index 2aa317829..39b764dfe 100644
--- a/apps/edisonsball/app.js
+++ b/apps/edisonsball/app.js
@@ -104,10 +104,10 @@ function getStandardDeviation (array) {
}
function checkHR() {
- var bpm = currentBPM, isCurrent = true;
+ var bpm = currentBPM; //isCurrent = true;
if (bpm===undefined) {
bpm = lastBPM;
- isCurrent = false;
+ //isCurrent = false;
}
if (bpm===undefined || bpm < lower_limit_BPM || bpm > upper_limit_BPM)
bpm = "--";
@@ -118,8 +118,8 @@ function checkHR() {
if(HR_samples.length == 5){
g.clear();
- average_HR = average(HR_samples).toFixed(0);
- stdev_HR = getStandardDeviation (HR_samples).toFixed(1);
+ let average_HR = average(HR_samples).toFixed(0);
+ let stdev_HR = getStandardDeviation (HR_samples).toFixed(1);
if (ISBANGLEJS1) {
g.drawString("HR: " + average_HR, 120,100);
diff --git a/apps/edisonsball/metadata.json b/apps/edisonsball/metadata.json
index dfeb4451e..8526c7926 100644
--- a/apps/edisonsball/metadata.json
+++ b/apps/edisonsball/metadata.json
@@ -2,7 +2,7 @@
"id": "edisonsball",
"name": "Edison's Ball",
"shortName": "Edison's Ball",
- "version": "0.02",
+ "version": "0.04",
"description": "Hypnagogia/Micro-Sleep alarm for experimental use in exploring sleep transition and combating drowsiness",
"icon": "app-icon.png",
"tags": "sleep,hyponagogia,quick,nap",
diff --git a/apps/elapsed_t/ChangeLog b/apps/elapsed_t/ChangeLog
new file mode 100644
index 000000000..45cb47c9a
--- /dev/null
+++ b/apps/elapsed_t/ChangeLog
@@ -0,0 +1,2 @@
+0.01: New App!
+0.02: Handle AM/PM time in the "set target" menu. Add yesterday/today/tomorrow when showing target date to improve readability.
\ No newline at end of file
diff --git a/apps/elapsed_t/README.md b/apps/elapsed_t/README.md
new file mode 100644
index 000000000..dc2173409
--- /dev/null
+++ b/apps/elapsed_t/README.md
@@ -0,0 +1,27 @@
+# Elapsed Time Clock
+A clock that calculates the time difference between now (in blue/cyan) and any given target date (in red/orange).
+
+The results is show in years, months, days, hours, minutes, seconds. To save battery life, the seconds are shown only when the watch is unlocked, or can be disabled entirely.
+
+The time difference is positive if the target date is in the past and negative if it is in the future.
+
+
+
+
+
+
+# Settings
+## Time and date formats:
+- time can be shown in 24h or in AM/PM format
+- date can be shown in DD/MM/YYYY, MM/DD/YYYY or YYYY-MM-DD format
+
+## Display years and months
+You can select if the difference is shown with years, months and days, or just days.
+
+# TODO
+- add the option to set an alarm to the target date
+- add an offset to said alarm (e.g. x hours/days... before/after)
+
+# Author
+
+paul-arg [github](https://github.com/paul-arg)
\ No newline at end of file
diff --git a/apps/elapsed_t/app-icon.js b/apps/elapsed_t/app-icon.js
new file mode 100644
index 000000000..0e9a434fc
--- /dev/null
+++ b/apps/elapsed_t/app-icon.js
@@ -0,0 +1 @@
+require("heatshrink").decompress(atob("mEwwcA/4A/AH8kyVJARAQE/YRLn4RD/IRT5cs2QCEEbQgFAQYjIrMlAQwjR5JHIsv2pNkz3JsgjKl/yEAO/I5l/+REBz/7I5f/EYf/I5Vf//2rNlz//8gjJAgIjE/hHIy7xEAAQjIDoIAG+RHHCA///wjHCJIjHMoI1HEY+zCI6zJv4dCFIX9R5PPR4vsEZNJCILXC/77JyXLn4jD/b7KpMnI4fZBARHHpcsEYW2AQIjKARBHIDoICECJIjRpZKCAQYjbCMH/CJVLCAgA/AHYA=="))
diff --git a/apps/elapsed_t/app.js b/apps/elapsed_t/app.js
new file mode 100644
index 000000000..b38735a32
--- /dev/null
+++ b/apps/elapsed_t/app.js
@@ -0,0 +1,474 @@
+const APP_NAME = "elapsed_t";
+
+//const COLOUR_BLACK = 0x0;
+//const COLOUR_DARK_GREY = 0x4208; // same as: g.setColor(0.25, 0.25, 0.25)
+const COLOUR_GREY = 0x8410; // same as: g.setColor(0.5, 0.5, 0.5)
+const COLOUR_LIGHT_GREY = 0xc618; // same as: g.setColor(0.75, 0.75, 0.75)
+const COLOUR_RED = 0xf800; // same as: g.setColor(1, 0, 0)
+const COLOUR_BLUE = 0x001f; // same as: g.setColor(0, 0, 1)
+//const COLOUR_YELLOW = 0xffe0; // same as: g.setColor(1, 1, 0)
+//const COLOUR_LIGHT_CYAN = 0x87ff; // same as: g.setColor(0.5, 1, 1)
+//const COLOUR_DARK_YELLOW = 0x8400; // same as: g.setColor(0.5, 0.5, 0)
+//const COLOUR_DARK_CYAN = 0x0410; // same as: g.setColor(0, 0.5, 0.5)
+const COLOUR_CYAN = "#00FFFF";
+const COLOUR_ORANGE = 0xfc00; // same as: g.setColor(1, 0.5, 0)
+
+const SCREEN_WIDTH = g.getWidth();
+const SCREEN_HEIGHT = g.getHeight();
+const BIG_FONT_SIZE = 38;
+const SMALL_FONT_SIZE = 22;
+
+var arrowFont = atob("BwA4AcAOAHADgBwA4McfOf3e/+P+D+A+AOA="); // contains only the > character
+
+var now = new Date();
+
+var settings = Object.assign({
+ // default values
+ displaySeconds: true,
+ displayMonthsYears: true,
+ dateFormat: 0,
+ time24: true
+}, require('Storage').readJSON(APP_NAME + ".settings.json", true) || {});
+
+var temp_displaySeconds = settings.displaySeconds;
+
+var data = Object.assign({
+ // default values
+ target: {
+ isSet: false,
+ Y: now.getFullYear(),
+ M: now.getMonth() + 1, // Month is zero-based, so add 1
+ D: now.getDate(),
+ h: now.getHours(),
+ m: now.getMinutes(),
+ s: 0
+ }
+}, require('Storage').readJSON(APP_NAME + ".data.json", true) || {});
+
+function writeData() {
+ require('Storage').writeJSON(APP_NAME + ".data.json", data);
+}
+
+function writeSettings() {
+ require('Storage').writeJSON(APP_NAME + ".settings.json", settings);
+ temp_displaySeconds = settings.temp_displaySeconds;
+}
+
+let inMenu = false;
+
+Bangle.on('touch', function (zone, e) {
+ if (!inMenu) {
+ if (drawTimeout) clearTimeout(drawTimeout);
+ E.showMenu(menu);
+ inMenu = true;
+ }
+});
+
+function pad2(number) {
+ return (String(number).padStart(2, '0'));
+}
+
+function formatDateTime(date, dateFormat, time24, showSeconds) {
+ var formattedDateTime = {
+ date: "",
+ time: ""
+ };
+
+ var DD = pad2(date.getDate());
+ var MM = pad2(date.getMonth() + 1); // Month is zero-based
+ var YYYY = date.getFullYear();
+ var h = date.getHours();
+ var hh = pad2(date.getHours());
+ var mm = pad2(date.getMinutes());
+ var ss = pad2(date.getSeconds());
+
+ switch (dateFormat) {
+ case 0:
+ formattedDateTime.date = `${DD}/${MM}/${YYYY}`;
+ break;
+
+ case 1:
+ formattedDateTime.date = `${MM}/${DD}/${YYYY}`;
+ break;
+
+ case 2:
+ formattedDateTime.date = `${YYYY}-${MM}-${DD}`;
+ break;
+
+ default:
+ formattedDateTime.date = `${YYYY}-${MM}-${DD}`;
+ break;
+ }
+
+ if (time24) {
+ formattedDateTime.time = `${hh}:${mm}${showSeconds ? `:${ss}` : ''}`;
+ } else {
+ var ampm = (h >= 12 ? 'PM' : 'AM');
+ var h_ampm = h % 12;
+ h_ampm = (h_ampm == 0 ? 12 : h_ampm);
+ formattedDateTime.time = `${h_ampm}:${mm}${showSeconds ? `:${ss}` : ''} ${ampm}`;
+ }
+
+ return formattedDateTime;
+}
+
+function formatHourToAMPM(h){
+ var ampm = (h >= 12 ? 'PM' : 'AM');
+ var h_ampm = h % 12;
+ h_ampm = (h_ampm == 0 ? 12 : h_ampm);
+ return `${h_ampm} ${ampm}`
+}
+
+function howManyDaysInMonth(month, year) {
+ return new Date(year, month, 0).getDate();
+}
+
+function handleExceedingDay() {
+ var maxDays = howManyDaysInMonth(data.target.M, data.target.Y);
+ menu.Day.max = maxDays;
+ if (data.target.D > maxDays) {
+ menu.Day.value = maxDays;
+ data.target.D = maxDays;
+ }
+}
+
+var menu = {
+ "": {
+ "title": "Set target",
+ back: function () {
+ E.showMenu();
+ Bangle.setUI("clock");
+ inMenu = false;
+ draw();
+ }
+ },
+ 'Day': {
+ value: data.target.D,
+ min: 1, max: 31, wrap: true,
+ onchange: v => {
+ data.target.D = v;
+ }
+ },
+ 'Month': {
+ value: data.target.M,
+ min: 1, max: 12, noList: true, wrap: true,
+ onchange: v => {
+ data.target.M = v;
+ handleExceedingDay();
+ }
+ },
+ 'Year': {
+ value: data.target.Y,
+ min: 1900, max: 2100,
+ onchange: v => {
+ data.target.Y = v;
+ handleExceedingDay();
+ }
+ },
+ 'Hours': {
+ value: data.target.h,
+ min: 0, max: 23, wrap: true,
+ onchange: v => {
+ data.target.h = v;
+ },
+ format: function (v) {return(settings.time24 ? pad2(v) : formatHourToAMPM(v))}
+ },
+ 'Minutes': {
+ value: data.target.m,
+ min: 0, max: 59, wrap: true,
+ onchange: v => {
+ data.target.m = v;
+ },
+ format: function (v) { return pad2(v); }
+ },
+ 'Seconds': {
+ value: data.target.s,
+ min: 0, max: 59, wrap: true,
+ onchange: v => {
+ data.target.s = v;
+ },
+ format: function (v) { return pad2(v); }
+ },
+ 'Save': function () {
+ E.showMenu();
+ inMenu = false;
+ Bangle.setUI("clock");
+ setTarget(true);
+ writeSettings();
+ temp_displaySeconds = settings.displaySeconds;
+ updateQueueMillis(settings.displaySeconds);
+ draw();
+ },
+ 'Reset': function () {
+ E.showMenu();
+ inMenu = false;
+ Bangle.setUI("clock");
+ setTarget(false);
+ updateQueueMillis(settings.displaySeconds);
+ draw();
+ }
+};
+
+function setTarget(set) {
+ if (set) {
+ target = new Date(
+ data.target.Y,
+ data.target.M - 1,
+ data.target.D,
+ data.target.h,
+ data.target.m,
+ data.target.s
+ );
+ data.target.isSet = true;
+ } else {
+ target = new Date();
+ Object.assign(
+ data,
+ {
+ target: {
+ isSet: false,
+ Y: now.getFullYear(),
+ M: now.getMonth() + 1, // Month is zero-based, so add 1
+ D: now.getDate(),
+ h: now.getHours(),
+ m: now.getMinutes(),
+ s: 0
+ }
+ }
+ );
+ menu.Day.value = data.target.D;
+ menu.Month.value = data.target.M;
+ menu.Year.value = data.target.Y;
+ menu.Hours.value = data.target.h;
+ menu.Minutes.value = data.target.m;
+ menu.Seconds.value = 0;
+ }
+
+ writeData();
+}
+
+var target;
+setTarget(data.target.isSet);
+
+var drawTimeout;
+var queueMillis = 1000;
+
+
+function queueDraw() {
+ if (drawTimeout) clearTimeout(drawTimeout);
+ var delay = queueMillis - (Date.now() % queueMillis);
+ if (queueMillis == 60000 && signIsNegative()) {
+ delay += 1000;
+ }
+
+ drawTimeout = setTimeout(function () {
+ drawTimeout = undefined;
+ draw();
+ }, delay);
+}
+
+function updateQueueMillis(displaySeconds) {
+ if (displaySeconds) {
+ queueMillis = 1000;
+ } else {
+ queueMillis = 60000;
+ }
+}
+
+Bangle.on('lock', function (on, reason) {
+ if (on) { // screen is locked
+ temp_displaySeconds = false;
+ updateQueueMillis(false);
+ draw();
+ } else { // screen is unlocked
+ temp_displaySeconds = settings.displaySeconds;
+ updateQueueMillis(temp_displaySeconds);
+ draw();
+ }
+});
+
+function signIsNegative() {
+ var now = new Date();
+ return (now < target);
+}
+
+function diffToTarget() {
+ var diff = {
+ sign: "+",
+ Y: "0",
+ M: "0",
+ D: "0",
+ hh: "00",
+ mm: "00",
+ ss: "00"
+ };
+
+ if (!data.target.isSet) {
+ return (diff);
+ }
+
+ var now = new Date();
+ diff.sign = now < target ? '-' : '+';
+
+ if (settings.displayMonthsYears) {
+ var start;
+ var end;
+
+ if (now > target) {
+ start = target;
+ end = now;
+ } else {
+ start = now;
+ end = target;
+ }
+
+ diff.Y = end.getFullYear() - start.getFullYear();
+ diff.M = end.getMonth() - start.getMonth();
+ diff.D = end.getDate() - start.getDate();
+ diff.hh = end.getHours() - start.getHours();
+ diff.mm = end.getMinutes() - start.getMinutes();
+ diff.ss = end.getSeconds() - start.getSeconds();
+
+ // Adjust negative differences
+ if (diff.ss < 0) {
+ diff.ss += 60;
+ diff.mm--;
+ }
+ if (diff.mm < 0) {
+ diff.mm += 60;
+ diff.hh--;
+ }
+ if (diff.hh < 0) {
+ diff.hh += 24;
+ diff.D--;
+ }
+ if (diff.D < 0) {
+ var lastMonthDays = new Date(end.getFullYear(), end.getMonth(), 0).getDate();
+ diff.D += lastMonthDays;
+ diff.M--;
+ }
+ if (diff.M < 0) {
+ diff.M += 12;
+ diff.Y--;
+ }
+
+
+ } else {
+ var timeDifference = target - now;
+ timeDifference = Math.abs(timeDifference);
+
+ // Calculate days, hours, minutes, and seconds
+ diff.D = Math.floor(timeDifference / (1000 * 60 * 60 * 24));
+ diff.hh = Math.floor((timeDifference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
+ diff.mm = Math.floor((timeDifference % (1000 * 60 * 60)) / (1000 * 60));
+ diff.ss = Math.floor((timeDifference % (1000 * 60)) / 1000);
+ }
+
+ // add zero padding
+ diff.hh = pad2(diff.hh);
+ diff.mm = pad2(diff.mm);
+ diff.ss = pad2(diff.ss);
+
+ return diff;
+}
+
+function draw() {
+ var now = new Date();
+ var nowFormatted = formatDateTime(now, settings.dateFormat, settings.time24, temp_displaySeconds);
+ var targetFormatted = formatDateTime(target, settings.dateFormat, settings.time24, true);
+ var diff = diffToTarget();
+
+ const nowY = now.getFullYear();
+ const nowM = now.getMonth();
+ const nowD = now.getDate();
+
+ const targetY = target.getFullYear();
+ const targetM = target.getMonth();
+ const targetD = target.getDate();
+
+ var diffYMD;
+ if (settings.displayMonthsYears)
+ diffYMD = `${diff.sign}${diff.Y}Y ${diff.M}M ${diff.D}D`;
+ else
+ diffYMD = `${diff.sign}${diff.D}D`;
+
+ var diff_hhmm = `${diff.hh}:${diff.mm}`;
+
+ g.clearRect(0, 24, SCREEN_WIDTH, SCREEN_HEIGHT);
+ //console.log("drawing");
+
+ let y = 24; //Bangle.getAppRect().y;
+
+ // draw current date
+ g.setFont("Vector", SMALL_FONT_SIZE).setFontAlign(-1, -1).setColor(g.theme.dark ? COLOUR_CYAN : COLOUR_BLUE);
+ g.drawString(nowFormatted.date, 4, y);
+ y += SMALL_FONT_SIZE;
+
+ // draw current time
+ g.setFont("Vector", SMALL_FONT_SIZE).setFontAlign(-1, -1).setColor(g.theme.dark ? COLOUR_CYAN : COLOUR_BLUE);
+ g.drawString(nowFormatted.time, 4, y);
+ y += SMALL_FONT_SIZE;
+
+ // draw arrow
+ g.setFontCustom(arrowFont, 62, 16, 13).setFontAlign(-1, -1).setColor(g.theme.dark ? COLOUR_ORANGE : COLOUR_RED);
+ g.drawString(">", 4, y + 3);
+
+ if (data.target.isSet) {
+ g.setFont("Vector", SMALL_FONT_SIZE).setFontAlign(-1, -1).setColor(g.theme.dark ? COLOUR_ORANGE : COLOUR_RED);
+
+ if (nowY == targetY && nowM == targetM && nowD == targetD) {
+ // today
+ g.drawString("TODAY", 4 + 16 + 6, y);
+ } else if (nowY == targetY && nowM == targetM && nowD - targetD == 1) {
+ // yesterday
+ g.drawString("YESTERDAY", 4 + 16 + 6, y);
+ } else if (nowY == targetY && nowM == targetM && targetD - nowD == 1) {
+ // tomorrow
+ g.drawString("TOMORROW", 4 + 16 + 6, y);
+ } else {
+ // general case
+ // draw target date
+ g.drawString(targetFormatted.date, 4 + 16 + 6, y);
+ }
+
+ y += SMALL_FONT_SIZE;
+
+ // draw target time
+ g.setFont("Vector", SMALL_FONT_SIZE).setFontAlign(-1, -1).setColor(g.theme.dark ? COLOUR_ORANGE : COLOUR_RED);
+ g.drawString(targetFormatted.time, 4, y);
+ y += SMALL_FONT_SIZE + 4;
+
+ } else {
+ // draw NOT SET
+ g.setFont("Vector", SMALL_FONT_SIZE).setFontAlign(-1, -1).setColor(g.theme.dark ? COLOUR_ORANGE : COLOUR_RED);
+ g.drawString("NOT SET", 4 + 16 + 6, y);
+ y += 2 * SMALL_FONT_SIZE + 4;
+ }
+
+ // draw separator
+ g.setColor(g.theme.fg);
+ g.drawLine(0, y - 4, SCREEN_WIDTH, y - 4);
+
+ // draw diffYMD
+ g.setFont("Vector", SMALL_FONT_SIZE).setFontAlign(0, -1).setColor(g.theme.fg);
+ g.drawString(diffYMD, SCREEN_WIDTH / 2, y);
+ y += SMALL_FONT_SIZE;
+
+ // draw diff_hhmm
+ g.setFont("Vector", BIG_FONT_SIZE).setFontAlign(0, -1).setColor(g.theme.fg);
+ g.drawString(diff_hhmm, SCREEN_WIDTH / 2, y);
+
+ // draw diff_ss
+ if (temp_displaySeconds) {
+ g.setFont("Vector", SMALL_FONT_SIZE).setFontAlign(-1, -1).setColor(g.theme.dark ? COLOUR_LIGHT_GREY : COLOUR_GREY);
+ g.drawString(diff.ss, SCREEN_WIDTH / 2 + 52, y + 13);
+ }
+
+ queueDraw();
+}
+
+Bangle.loadWidgets();
+Bangle.drawWidgets();
+Bangle.setUI("clock");
+
+draw();
diff --git a/apps/elapsed_t/app.png b/apps/elapsed_t/app.png
new file mode 100644
index 000000000..c2cac4fa1
Binary files /dev/null and b/apps/elapsed_t/app.png differ
diff --git a/apps/elapsed_t/metadata.json b/apps/elapsed_t/metadata.json
new file mode 100644
index 000000000..7f8460e49
--- /dev/null
+++ b/apps/elapsed_t/metadata.json
@@ -0,0 +1,20 @@
+{
+ "id": "elapsed_t",
+ "name": "Elapsed Time Clock",
+ "shortName": "Elapsed Time",
+ "type": "clock",
+ "version":"0.02",
+ "description": "A clock that calculates the time difference between now and any given target date.",
+ "tags": "clock,tool",
+ "supports": ["BANGLEJS2"],
+ "storage": [
+ {"name":"elapsed_t.app.js","url":"app.js"},
+ {"name":"elapsed_t.settings.js","url":"settings.js"},
+ {"name":"elapsed_t.img","url":"app-icon.js","evaluate":true}
+ ],
+ "data": [{"name":"elapsed_t.data.json"}],
+ "icon": "app.png",
+ "readme": "README.md",
+ "screenshots": [{ "url": "screenshot1.png" }, { "url": "screenshot2.png" }, { "url": "screenshot3.png" }, { "url": "screenshot4.png" }],
+ "allow_emulator":true
+}
diff --git a/apps/elapsed_t/screenshot1.png b/apps/elapsed_t/screenshot1.png
new file mode 100644
index 000000000..d15a5a9ae
Binary files /dev/null and b/apps/elapsed_t/screenshot1.png differ
diff --git a/apps/elapsed_t/screenshot2.png b/apps/elapsed_t/screenshot2.png
new file mode 100644
index 000000000..00ad8aa36
Binary files /dev/null and b/apps/elapsed_t/screenshot2.png differ
diff --git a/apps/elapsed_t/screenshot3.png b/apps/elapsed_t/screenshot3.png
new file mode 100644
index 000000000..8ca6212f6
Binary files /dev/null and b/apps/elapsed_t/screenshot3.png differ
diff --git a/apps/elapsed_t/screenshot4.png b/apps/elapsed_t/screenshot4.png
new file mode 100644
index 000000000..e2a10ab62
Binary files /dev/null and b/apps/elapsed_t/screenshot4.png differ
diff --git a/apps/elapsed_t/settings.js b/apps/elapsed_t/settings.js
new file mode 100644
index 000000000..d3a7cb357
--- /dev/null
+++ b/apps/elapsed_t/settings.js
@@ -0,0 +1,55 @@
+(function(back) {
+ var APP_NAME = "elapsed_t";
+ var FILE = APP_NAME + ".settings.json";
+ // Load settings
+ var settings = Object.assign({
+ // default values
+ displaySeconds: true,
+ displayMonthsYears: true,
+ dateFormat: 0,
+ time24: true
+ }, require('Storage').readJSON(APP_NAME + ".settings.json", true) || {});
+
+ function writeSettings() {
+ require('Storage').writeJSON(FILE, settings);
+ }
+
+ var dateFormats = ["DD/MM/YYYY", "MM/DD/YYYY", "YYYY-MM-DD"];
+
+ // Show the menu
+ E.showMenu({
+ "" : { "title" : "Elapsed Time" },
+ "< Back" : () => back(),
+ 'Show\nseconds': {
+ value: !!settings.displaySeconds,
+ onchange: v => {
+ settings.displaySeconds = v;
+ writeSettings();
+ }
+ },
+ 'Show months/\nyears': {
+ value: !!settings.displayMonthsYears,
+ onchange: v => {
+ settings.displayMonthsYears = v;
+ writeSettings();
+ }
+ },
+ 'Time format': {
+ value: !!settings.time24,
+ onchange: v => {
+ settings.time24 = v;
+ writeSettings();
+ },
+ format: function (v) {return v ? "24h" : "AM/PM";}
+ },
+ 'Date format': {
+ value: settings.dateFormat,
+ min: 0, max: 2, wrap: true,
+ onchange: v => {
+ settings.dateFormat = v;
+ writeSettings();
+ },
+ format: function (v) {return dateFormats[v];}
+ }
+ });
+})
diff --git a/apps/encourageclk/ChangeLog b/apps/encourageclk/ChangeLog
index 83c07f784..9cb404008 100644
--- a/apps/encourageclk/ChangeLog
+++ b/apps/encourageclk/ChangeLog
@@ -1,3 +1,4 @@
0.01: New face :)
0.02: code improvements
0.03: code improvments to queuedraw and draw
+0.04: Minor code improvements
diff --git a/apps/encourageclk/app.js b/apps/encourageclk/app.js
index a78a788ba..1f19cc314 100644
--- a/apps/encourageclk/app.js
+++ b/apps/encourageclk/app.js
@@ -3,7 +3,6 @@
require("FontHaxorNarrow7x17").add(Graphics);
require("FontDylex7x13").add(Graphics);
-const storage = require('Storage');
const locale = require("locale");
const dateutil = require("date_utils");
const currentFont=g.getFont();
@@ -52,7 +51,7 @@ function queueDraw() {
function draw() {
var time = locale.time(d, 1);
- var date = locale.date(d);
+ //var date = locale.date(d);
var mo = dateutil.month(d.getMonth() + 1, 1);
g.drawImage(bgimg,0,offset); //bg
diff --git a/apps/encourageclk/metadata.json b/apps/encourageclk/metadata.json
index 4e5d630cf..f3816c9de 100644
--- a/apps/encourageclk/metadata.json
+++ b/apps/encourageclk/metadata.json
@@ -2,7 +2,7 @@
"id": "encourageclk",
"name": "Encouragement & Positivity Clock",
"shortName":"Encouragement Clock",
- "version": "0.03",
+ "version": "0.04",
"description": "Tap on the watch for a note of encouragement",
"icon": "app.png",
"type": "clock",
diff --git a/apps/espruinoctrl/ChangeLog b/apps/espruinoctrl/ChangeLog
index 819ae56cb..522cba63e 100644
--- a/apps/espruinoctrl/ChangeLog
+++ b/apps/espruinoctrl/ChangeLog
@@ -1,2 +1,3 @@
0.01: New App!
0.02: Disable not existing BTN3 on Bangle.js 2, set maximum transmit power
+0.03: Now use BTN2 on Bangle.js 1, and on Bangle.js 2 use the middle button to return to the menu
\ No newline at end of file
diff --git a/apps/espruinoctrl/README.md b/apps/espruinoctrl/README.md
index 7b2e434e7..59c96b0de 100644
--- a/apps/espruinoctrl/README.md
+++ b/apps/espruinoctrl/README.md
@@ -14,7 +14,8 @@ with 4 options:
with this address will be connected to directly. If not specified a menu
showing available Espruino devices is popped up.
* **RX** - If checked, the app will display any data received from the
-device being connected to. Use this if you want to print data - eg: `print(E.getBattery())`
+device being connected to (waiting 500ms after the last data before disconnecting).
+Use this if you want to print data - eg: `print(E.getBattery())`
When done, click 'Upload'. Your changes will be saved to local storage
so they'll be remembered next time you upload from the same device.
@@ -25,4 +26,9 @@ Simply load the app and you'll see a menu with the menu items
you defined. Select one and you'll be able to connect to the device
and send the command.
-If a command should wait for a response then
+The Bangle will connect to the device, send the command, and if:
+
+* `RX` isn't set it will disconnect immediately and return to the menu
+* `RX` is set it will listen for a response and write it to the screen, before
+disconnecting after 500ms of inactivity. To return to the menu after this, press the button.
+
diff --git a/apps/espruinoctrl/custom.html b/apps/espruinoctrl/custom.html
index 2329ad214..27ef1eb53 100644
--- a/apps/espruinoctrl/custom.html
+++ b/apps/espruinoctrl/custom.html
@@ -194,16 +194,14 @@ function sendCommandRX(device, text, callback) {
function done() {
Terminal.println("\\n============\\n Disconnected");
device.disconnect();
- if (global.BTN3 !== undefined) {
- setTimeout(function() {
- setWatch(function() {
- if (callback) callback();
- resolve();
- }, BTN3);
- g.reset().setFont("6x8",2).setFontAlign(0,0,1);
- g.drawString("Back", g.getWidth()-10, g.getHeight()-50);
- }, 200);
- }
+ setTimeout(function() {
+ setWatch(function() {
+ if (callback) callback();
+ resolve();
+ }, (process.env.HWVERSION==2) ? BTN1 : BTN2);
+ g.reset().setFont("6x8",2).setFontAlign(0,0,1);
+ g.drawString("Back", g.getWidth()-10, g.getHeight()/2);
+ }, 200);
}
device.getPrimaryService("6e400001-b5a3-f393-e0a9-e50e24dcca9e").then(function(s) {
service = s;
diff --git a/apps/espruinoctrl/metadata.json b/apps/espruinoctrl/metadata.json
index 9308b4a46..4f5fa01c8 100644
--- a/apps/espruinoctrl/metadata.json
+++ b/apps/espruinoctrl/metadata.json
@@ -2,7 +2,7 @@
"id": "espruinoctrl",
"name": "Espruino Control",
"shortName": "Espruino Ctrl",
- "version": "0.02",
+ "version": "0.03",
"description": "Send commands to other Espruino devices via the Bluetooth UART interface. Customisable commands!",
"icon": "app.png",
"tags": "tool,bluetooth",
diff --git a/apps/espruinoterm/ChangeLog b/apps/espruinoterm/ChangeLog
index 5560f00bc..7727f3cc4 100644
--- a/apps/espruinoterm/ChangeLog
+++ b/apps/espruinoterm/ChangeLog
@@ -1 +1,2 @@
0.01: New App!
+0.02: Minor code improvements
diff --git a/apps/espruinoterm/app.js b/apps/espruinoterm/app.js
index 348190db4..1253b253a 100644
--- a/apps/espruinoterm/app.js
+++ b/apps/espruinoterm/app.js
@@ -7,7 +7,7 @@ var R = Bangle.appRect;
var termg = Graphics.createArrayBuffer(R.w, R.h, 1, {msb:true});
var termVisible = false;
termg.setFont("6x8");
-term = require("VT100").connect(termg, {
+let term = require("VT100").connect(termg, {
charWidth : 6,
charHeight : 8
});
diff --git a/apps/espruinoterm/metadata.json b/apps/espruinoterm/metadata.json
index 25e6183e1..d967e0e1a 100644
--- a/apps/espruinoterm/metadata.json
+++ b/apps/espruinoterm/metadata.json
@@ -2,7 +2,7 @@
"id": "espruinoterm",
"name": "Espruino Terminal",
"shortName": "Espruino Term",
- "version": "0.01",
+ "version": "0.02",
"description": "Send commands to other Espruino devices via the Bluetooth UART interface, and see the result on a VT100 terminal. Customisable commands!",
"icon": "app.png",
"screenshots": [{"url":"screenshot.png"}],
diff --git a/apps/f9lander/ChangeLog b/apps/f9lander/ChangeLog
index b5a33bd2e..8aed5d989 100644
--- a/apps/f9lander/ChangeLog
+++ b/apps/f9lander/ChangeLog
@@ -1,3 +1,4 @@
0.01: New App!
0.02: Add lightning
0.03: Convert Yes/No On/Off in settings to checkboxes
+0.04: Minor code improvements
diff --git a/apps/f9lander/app.js b/apps/f9lander/app.js
index 2f17a5bd5..d195a7c67 100644
--- a/apps/f9lander/app.js
+++ b/apps/f9lander/app.js
@@ -45,7 +45,7 @@ var booster = { x : g.getWidth()/4 + Math.random()*g.getWidth()/2,
var exploded = false;
var nExplosions = 0;
-var landed = false;
+//var landed = false;
var lightning = 0;
var settings = require("Storage").readJSON('f9settings.json', 1) || {};
diff --git a/apps/f9lander/metadata.json b/apps/f9lander/metadata.json
index 5a3887c9e..868b70f71 100644
--- a/apps/f9lander/metadata.json
+++ b/apps/f9lander/metadata.json
@@ -1,7 +1,7 @@
{ "id": "f9lander",
"name": "Falcon9 Lander",
"shortName":"F9lander",
- "version":"0.03",
+ "version": "0.04",
"description": "Land a rocket booster",
"icon": "f9lander.png",
"screenshots" : [ { "url":"f9lander_screenshot1.png" }, { "url":"f9lander_screenshot2.png" }, { "url":"f9lander_screenshot3.png" }],
diff --git a/apps/fallout_clock/.gitignore b/apps/fallout_clock/.gitignore
new file mode 100644
index 000000000..e5f9ba937
--- /dev/null
+++ b/apps/fallout_clock/.gitignore
@@ -0,0 +1,7 @@
+node_modules/
+res/
+
+fallout_clock.code-workspace
+
+package.json
+package-lock.json
diff --git a/apps/fallout_clock/ChangeLog b/apps/fallout_clock/ChangeLog
new file mode 100644
index 000000000..ee9876b1a
--- /dev/null
+++ b/apps/fallout_clock/ChangeLog
@@ -0,0 +1,5 @@
+0.10: (20240125) Basic Working Clock.
+0.11: (20240125) Widgets Added. Improved Interval Loop.
+0.12: (20240221) Fix: Month Reporting Wrong.
+0.20: (20240223) Created as a Package.
+0.21: (20240223) Added StandardJS and NPM.
diff --git a/apps/fallout_clock/LICENSE b/apps/fallout_clock/LICENSE
new file mode 100644
index 000000000..d9d472761
--- /dev/null
+++ b/apps/fallout_clock/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 Zachary D. Skelton
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/apps/fallout_clock/README.md b/apps/fallout_clock/README.md
new file mode 100644
index 000000000..b48e7e762
--- /dev/null
+++ b/apps/fallout_clock/README.md
@@ -0,0 +1,29 @@
+# Fallout Clock
+
+Inspired by the aesthetic of the Fallout series, this clock face looks to emulate the color and feel of a PipBoy.
+
+
+
+## Usage
+
+You can also go into Settings, and choose it as the default clock under **Select Clock**.
+
+## Planned Features:
+- Display Steps as Health
+- Display Heartrate
+- Brighter Color when the backlight is not on.
+- Configurable Settings
+
+## Controls
+
+Zero Settings, Zero Configuration. Install and add as your clockface.
+
+## Requests
+
+To request new features, add [an issue](https://github.com/zskelton/fallout_clock/issues).
+
+## Creator
+
+Zachary D. Skelton \
+[Skelton Networks](https://skeltonnetworks.com)\
+[Github](https://github.com/zskelton)
\ No newline at end of file
diff --git a/apps/fallout_clock/app-icon.js b/apps/fallout_clock/app-icon.js
new file mode 100644
index 000000000..c84c6fb48
--- /dev/null
+++ b/apps/fallout_clock/app-icon.js
@@ -0,0 +1 @@
+atob("MDDDAb88//9u/1r/1/YZrgAAit4kkkkkkkkkkAAVIkkkkkkkkkkkkkkkkkkAAAARJJIkkkkkkkkkkkkkkkAAAACJJJJUkkkkkkkkkkkkkAAAAARJJJJAAkkkkkkkkkkkAAAAACpJJJKgAAkkkkkkkkkgAAAAAVJJJJIAAAEkkkkkkkkAAAAACpJfpJUAAAAkkkkkkkgAAAAABJf/9JAAAAAEkkkkkkAAAAAARJdf/+gAAAAAkkkkkgAAAAAC//dL//gAAAAAEkkkkAAAAYADpJJL//8AAAAAAkkkkAAAD8AdJJJL///gAAAAAkkkgAAADr/pJJL////0AAAAAEkkgAAABJJL/pfb////gAAAAAkkAAAADpJeu22X////4AAAAAkkAAAADpL1tttuf/7f8AAAAAkgAAAAb/+ttttuSSS7/AAAAAEgAAAAdeyttttySSSb/gAAAAEgAAAC9eWtttuaySSf/2SSSSkgAAAVfxtttttyySX//9JJJQAAAAAJetttttttyST//9JJJUAAAABJeOaNyNutySW//9JJKgAAAARJdu6N1tvRySS3/JJJUAAAACJJVuVu1tzRyST2/JJKAAAAAVJL1ttyttuNuSWW7pJKgAAACpJLxtt6NtttuSS27pJUAAAAVJJLxtt6ttttuSWT9JKgAAAAJJJLxttzNtttuSST9JIAAAAiJJJL1ttttt2NuSSS9JUAAAA2222212xtty3RySSS9KgAAAEgAAAAZ6OW2tu1ySST9QAAAAEgAAAAaW1ttu2VySSXKAAAAAEkAAAACtu221ttySbdKgAAAAEkAAAADNty1ttuST9JUAAAAAkkAAAAAVty1ttuSXpKAAAAAAkkgAAAACtttttyT9JIAAAAAEkkkAAAAARttttyfdJUAAAAAEkkkAAAAACtttuSzJKgAAAAAkkkkgAAAAAWtuSSfpQAAAAAEkkkkkAAAAADa2yT9JAAAAAAkkkkkkgAAAAD7e3/pKgAAAAAkkkkkkkAAAAVL/9JJUAAAAAEkkkkkkkgAAARJJJJKAAAAAEkkkkkkkkkAAAJJJJJIAAAAAkkkkkkkkkkkACpJJJJUAAAAEkkkkkkkkkkkgCJJJJKgAAAEkkkkkkkkkkkkklJJJJQAAAEkkkkkkkkkkkkkkkkpJJAAEkkkkkkkkk=")
diff --git a/apps/fallout_clock/clock.js b/apps/fallout_clock/clock.js
new file mode 100644
index 000000000..56bb68a3a
--- /dev/null
+++ b/apps/fallout_clock/clock.js
@@ -0,0 +1,141 @@
+/* global Bangle, Graphics, g */
+
+// NAME: Fallout Clock (Bangle.js 2)
+// DOCS: https://www.espruino.com/ReferenceBANGLEJS2
+// AUTHOR: Zachary D. Skelton
+// VERSION: 0.1.0 (24JAN2024) - Creating [ Maj.Min.Bug ] REF: https://semver.org/
+// LICENSE: MIT License (2024) [ https://opensource.org/licenses/MIT ]
+
+/* THEME COLORS */
+// Dark Full - #000000 - (0,0.00,0)
+// Dark Half - #002f00 - (0,0.18,0)
+// Dark Zero - #005f00 - (0,0.37,0)
+// Light Zero - #008e00 - (0,0.55,0)
+// Light Half - #00bf00 - (0,0.75,0)
+// Light Full - #00ee00 - (0,0.93,0)
+
+/* FONTS */
+// Font: Good Time Rg - https://www.dafont.com/good-times.font
+// Large = 50px
+Graphics.prototype.setLargeFont = function () {
+ this.setFontCustom(
+ atob('AAAAAAAAAAAAAAAAAAAAAABAAAAAAAB8AAAAAAA/gAAAAAAP4AAAAAAD+AAAAAAA/gAAAAAAP4AAAAAAB8AAAAAAAGAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAADwAAAAAAD8AAAAAAD/AAAAAAD/wAAAAAD/8AAAAAH/8AAAAAH/8AAAAAH/4AAAAAH/4AAAAAH/4AAAAAH/4AAAAAP/4AAAAAP/wAAAAAP/wAAAAAP/wAAAAAP/wAAAAAH/wAAAAAB/wAAAAAAfgAAAAAAHgAAAAAABgAAAAAAAAAAAAAAAAAAOAAAAAAB//AAAAAB//8AAAAB///wAAAA////AAAAf///4AAAP////AAAH////4AAD/+B//AAB/8AD/wAAf8AAP+AAP+AAB/gAD/AAAP8AA/gAAB/AAf4AAAf4AH8AAAD+AB/AAAA/gAfwAAAP4AH8AAAD+AB/AAAA/gAfwAAAP4AH8AAAD+AB/AAAA/gAfwAAAP4AH8AAAD+AB/gAAB/gAP4AAAfwAD/AAAP8AA/4AAH+AAH/AAD/gAB/8AD/wAAP/4H/8AAB////+AAAP////AAAB////gAAAP///wAAAB///wAAAAH//wAAAAAf/wAAAAAAOAAAAAAAAAAAAAfgAAAAAAH8AAAAAAB/AAAAAAAfwAAAAAAH8AAAAAAB/AAAAAAAfwAAAAAAH+AAAAAAB/wAAAAAAf/////wAD/////8AA//////AAH/////wAA/////8AAH/////AAAf////wAAA////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/wAH8AA//8AB/AA///AAfwAf//wAH8AH//8AB/AD///AAfwA///wAH8Af//8AB/AH8B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwD+AfwAH+B/AH8AB///wB/AAf//8AfwAD///AH8AA///gB/AAH//wAfwAA//4AH8AAH/8AB/AAAf8AAPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8AB/AAAB/AAfwAAAfwAH8AAAH8AB/AAAB/AAfwAAAfwAH8AAAH8AB/AAAB/AAfwAAAfwAH8AfAH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8A/gH8AB/gP4D/AAf8H/A/wAH///8/8AA/////+AAP/////gAB/////4AAf/8//8AAD/+P/+AAAf/B//AAAA/AH/AAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAH///AAAAB///8AAAAf///gAAAH///8AAAB////gAAAf///4AAAH////AAAAAAD/wAAAAAAP8AAAAAAB/AAAAAAAfwAAAAAAD+AAAAAAA/gAAAAAAP4AAAAAAD+AAAAAAA/gAAAAAAP4AAAAAAD+AAAAAAA/gAAAAAAP4AAAAAAD+AAAAAAA/gAAAAAAP4AAAAAAD+AAAAAAA/gAAAAAAP4AAAAAAD+AAAH/////8AB//////AAf/////wAH/////8AB//////AAf/////wAH/////8AAAAAP4AAAAAAD+AAAAAAA/gAAAAAAP4AAAAAAD+AAAAAAA/AAAAAAAAAAAAAAAAAAAAH///AD8AB///4B/AAf//+AfwAH///gH8AB///4B/AAf//+AfwAH///gH8AB///4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH8D/AAfwB/h/wAH8Af//8AB/AD//+AAfwA///gAH8AH//wAB/AA//4AAfgAH/8AAAAAAf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHwAAAAAB//4AAAAB///wAAAB////AAAA////4AAAf////AAAP////4AAH/////AAB//fv/wAA/8H4f+AAP+B+D/gAH/AfgP8AB/gH4D/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+A/wAH8Afwf8AB/AH///AAfwA///gAH8AP//4AB/AD//8AAfwAf/+AAH8AD//AAAAAAP/gAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAB/AAAABAAfwAAAAwAH8AAAAcAB/AAAAfAAfwAAAPwAH8AAAH8AB/AAAD/AAfwAAD/wAH8AAB/8AB/AAA//AAfwAAf/gAH8AAP/gAB/AAP/wAAfwAH/4AAH8AD/8AAB/AB/8AAAfwB/+AAAH8A//AAAB/Af/gAAAfwP/gAAAH8P/wAAAB/H/4AAAAfz/8AAAAH9/8AAAAB//+AAAAAf//AAAAAH//gAAAAB//gAAAAAf/wAAAAAH/4AAAAAB/8AAAAAAf8AAAAAAH+AAAAAAB/AAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP4AAAD/gP/gAAB/+H/8AAA//z//gAAf////8AAP/////gAD/////4AB//////AAf+P/B/wAH+A/gP8AB/AP4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8AfgH8AB/AH4B/AAfwB+AfwAH8A/gH8AB/gP8D/AAf+P/h/wAH/////8AA/////+AAP/////gAB/////wAAP/8//4AAB/+H/8AAAH+A/+AAAAAAD+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAH/4AAAAAH//gAAAAD//8AAAAB///AD8AA///4B/AAP///AfwAH///wH8AB/4f8B/AAf4B/AfwAH8APwH8AB/AD8B/AAfwA/AfwAH8APwH8AB/AD8B/AAfwA/AfwAH8APwH8AB/AD8B/AAfwA/AfwAH8APwH8AB/AD8B/AAfwA/AfwAH8APwH8AB/AD8B/AAfwA/AfwAH8APwH8AB/AD8B/AAfwA/AfwAH+APwP8AB/wD8D/AAP+A/B/gAD/wPx/4AAf/j9/+AAH/////AAA/////gAAH////4AAA////8AAAH///8AAAAf//+AAAAA//8AAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAEAAAAD4AHwAAAB/AD+AAAAfwA/gAAAH8AP4AAAB/AD+AAAAfwA/gAAAD4AHwAAAAMAAYAAAAAAAAAAAAAAAAA'),
+ 46,
+ atob('DRYqEykpKiwsJi0rDQ=='),
+ 50 | 65536
+ )
+ return this
+}
+
+// Medium = 16px ()
+Graphics.prototype.setMediumFont = function () {
+ this.setFontCustom(
+ atob('AAAAAAAADwAAAB8AAAAPAAAAGwAAAb8AAB/9AAH/kAAv+QAA/4AAAPQAAAACvkAAH//wAD///AC/Qf4A/AA/APQAHwDwAA8A9AAfAPwAPwC+Qf4AP//8AB//9AAG/4AAoAAAAPAAAAD4AAAA////AL///wAv//8AAAAAAPAL/wDwH/8A8D//APA9DwDwPA8A8DwPAPA8DwDwPA8A9DwPAP78DwD/+A8AL+APAPAADwDwAA8A8BQPAPA8DwDwPA8A8DwPAPA8DwDwPA8A9DwfAP7/vwD///8AP+v8AAUBUACqqQAA//9AAP//wABVW8AAAAPAAAADwAAAA8AAAAPAAAADwAD///8A////AP///wAAA8AAAAKAAAAAAAD//A8A//wPAP/8DwDwPA8A8DwPAPA8DwDwPA8A8DwPAPA8DwDwPR8A8D+/APAv/gCgC/gAC//gAD///AC///4A/Tx/APg8LwDwPA8A8DwPAPA8DwDwPA8A8D0fAPA/vwDwL/4AoAv4AAAAQABQAAAA8AAHAPAAHwDwAL8A8AP/APAf+ADwf9AA8v9AAP/4AAD/4AAA/0AAAP0AAAAAAEAAL9v4AL///gD//78A+H0vAPA8DwDwPA8A8DwPAPA8DwDwPA8A9D0fAP7/vwD///8AP9v8AC/4AAC//g8A/r8PAPQfDwDwDw8A8A8PAPAPDwDwDw8A+A8fAP5PfwC///4AL//8AAb/4AAAAAAAAAAAAAA8DwAAfB8AADwPAA=='),
+ 46,
+ atob('BAcNBg0NDg4ODA4OBA=='),
+ 16 | 131072
+ )
+ return this
+}
+
+/* VARIABLES */
+// Const
+const H = g.getHeight()
+const W = g.getWidth()
+// Mutable
+let timer = null
+
+/* UTILITY FUNCTIONS */
+// Return String of Current Time
+function getCurrentTime () {
+ try {
+ const d = new Date()
+ const h = d.getHours()
+ const m = d.getMinutes()
+ return `${h}:${m.toString().padStart(2, 0)}`
+ } catch (e) {
+ console.log(e)
+ return '0:00'
+ }
+}
+
+// Return String of Current Date
+function getCurrentDate () {
+ try {
+ const d = new Date()
+ const year = d.getFullYear()
+ const month = d.getMonth()
+ const day = d.getDate()
+ const display = `${month + 1}.${day.toString().padStart(2, 0)}.${year}`
+ return display
+ } catch (e) {
+ console.log(e)
+ return '0.0.0000'
+ }
+}
+
+// Set A New Draw for the Next Minute
+function setNextDraw () {
+ console.log('tick')
+ // Clear Timeout
+ if (timer) {
+ clearInterval(timer)
+ }
+ // Calculate time until next minute
+ const d = new Date()
+ const s = d.getSeconds()
+ const ms = d.getMilliseconds()
+ const delay = 60000 - (s * 1000) - ms
+ // Set Timeout
+ timer = setInterval(draw, delay)
+}
+
+function draw () {
+ // Reset Variables
+ g.reset()
+ // Set Background Color
+ g.setBgColor(0, 0, 0)
+ // Draw Background
+ g.setColor(0, 0, 0)
+ g.fillRect(0, 0, W, H)
+ // Set Font for Time
+ g.setColor(0, 0.93, 0)
+ g.setLargeFont()
+ g.setFontAlign(0, 0)
+ // Draw Time
+ const time = getCurrentTime()
+ g.drawString(time, W / 2, H / 2, true /* clear background */)
+ // Set Font for Date
+ g.setColor(0, 0.75, 0)
+ g.setMediumFont()
+ g.setFontAlign(0, 1)
+ // Draw Date
+ const dateStr = getCurrentDate()
+ g.drawString(dateStr, W / 2, H - 45, true)
+ // Draw Border
+ g.setColor(0, 0.93, 0)
+ g.drawLine(5, 36, W - 5, 36)
+ g.drawLine(5, H - 9, W - 5, H - 9)
+ g.setColor(0, 0.18, 0)
+ g.fillRect(0, 27, W, 32)
+ g.fillRect(0, H, W, H - 5)
+ // Draw Widgets
+ Bangle.drawWidgets()
+ // Schedule Next Draw
+ setNextDraw()
+}
+
+/* MAIN LOOP */
+function main () {
+ // Clear Screen
+ g.clear()
+ // Set as Clock to Enable Launcher Screen on BTN1
+ Bangle.setUI('clock')
+ // Load Widgets
+ Bangle.loadWidgets()
+ // Draw Clock
+ draw()
+}
+
+/* BOOT CODE */
+main()
diff --git a/apps/fallout_clock/icon.png b/apps/fallout_clock/icon.png
new file mode 100644
index 000000000..fc9bc1fdc
Binary files /dev/null and b/apps/fallout_clock/icon.png differ
diff --git a/apps/fallout_clock/metadata.json b/apps/fallout_clock/metadata.json
new file mode 100644
index 000000000..20861411a
--- /dev/null
+++ b/apps/fallout_clock/metadata.json
@@ -0,0 +1,18 @@
+{
+ "id":"fallout_clock",
+ "name":"Fallout Clock",
+ "version":"0.21",
+ "description":"A simple clock for the Fallout fan",
+ "icon":"icon.png",
+ "type":"clock",
+ "tags": "clock,fallout,green,retro",
+ "supports": ["BANGLEJS2"],
+ "readme": "README.md",
+ "storage": [
+ {"name":"fallout_clock.app.js", "url":"clock.js"},
+ {"name":"fallout_clock.img", "url":"app-icon.js", "evaluate":true}
+ ],
+ "screenshots": [
+ {"url":"./screenshot.png", "name":"Fallout Clock Screenshot"}
+ ]
+}
diff --git a/apps/fallout_clock/res/fallout_icon.png b/apps/fallout_clock/res/fallout_icon.png
new file mode 100644
index 000000000..fc9bc1fdc
Binary files /dev/null and b/apps/fallout_clock/res/fallout_icon.png differ
diff --git a/apps/fallout_clock/res/good times rg.otf b/apps/fallout_clock/res/good times rg.otf
new file mode 100644
index 000000000..53c181cca
Binary files /dev/null and b/apps/fallout_clock/res/good times rg.otf differ
diff --git a/apps/fallout_clock/res/screenshot.png b/apps/fallout_clock/res/screenshot.png
new file mode 100644
index 000000000..253554b72
Binary files /dev/null and b/apps/fallout_clock/res/screenshot.png differ
diff --git a/apps/fallout_clock/screenshot.png b/apps/fallout_clock/screenshot.png
new file mode 100644
index 000000000..253554b72
Binary files /dev/null and b/apps/fallout_clock/screenshot.png differ
diff --git a/apps/fastload/README.md b/apps/fastload/README.md
index f2ff71e93..f7fab4933 100644
--- a/apps/fastload/README.md
+++ b/apps/fastload/README.md
@@ -1,11 +1,17 @@
+#### ⚠️EXPERIMENTAL⚠️
+
# Fastload Utils
-*EXPERIMENTAL* Use this with caution. When you find something misbehaving please check if the problem actually persists when removing this app.
+Use this with caution. When you find something misbehaving please check if the problem actually persists when removing this app.
This allows fast loading of all apps with two conditions:
* Loaded app contains `Bangle.loadWidgets`. This is needed to prevent problems with apps not expecting widgets to be already loaded.
* Current app can be removed completely from RAM.
+#### ⚠️ KNOWN ISSUES ⚠️
+
+* Fastload currently does not play nice with the automatic reload option of the apploader. App installs and upgrades are unreliable since the fastload causes code to run after reset and interfere with the upload process.
+
## Settings
* Activate app history and navigate back through recent apps instead of immediately loading the clock face
diff --git a/apps/fastreset/README.md b/apps/fastreset/README.md
index 44b855454..b23023f4a 100644
--- a/apps/fastreset/README.md
+++ b/apps/fastreset/README.md
@@ -1,6 +1,6 @@
# Fast Reset
-Reset the watch by pressing the hardware button just a little bit longer than a click. If 'Fastload Utils' is installed this will typically be done with fastloading. A buzz acts as indicator.
+Reset the watch to the clock face by pressing the hardware button just a little bit longer than a click. If 'Fastload Utils' is installed this will typically be done with fastloading. A buzz acts as indicator.
Fast Reset was developed with the app history feature of 'Fastload Utils' in mind. If many apps are in the history stack, the user may want a fast way to exit directly to the clock face without using the firmwares reset function.
diff --git a/apps/fastreset/metadata.json b/apps/fastreset/metadata.json
index 455649b48..ccd5e1ce4 100644
--- a/apps/fastreset/metadata.json
+++ b/apps/fastreset/metadata.json
@@ -2,7 +2,7 @@
"name": "Fast Reset",
"shortName":"Fast Reset",
"version":"0.03",
- "description": "Reset the watch by pressing the hardware button just a little bit longer than a click. If 'Fastload Utils' is installed this will typically be done with fastloading. A buzz acts as indicator.",
+ "description": "Reset the watch to the clock face by pressing the hardware button just a little bit longer than a click. If 'Fastload Utils' is installed this will typically be done with fastloading. A buzz acts as indicator.",
"icon": "app.png",
"type": "bootloader",
"tags": "system",
diff --git a/apps/fclock/ChangeLog b/apps/fclock/ChangeLog
index 7e7307c59..35fa366a4 100644
--- a/apps/fclock/ChangeLog
+++ b/apps/fclock/ChangeLog
@@ -1,3 +1,5 @@
0.01: First published version of app
0.02: Move to Bangle.setUI to launcher support
0.03: Tell clock widgets to hide.
+0.04: Minor code improvements
+0.05: Minor code improvements
diff --git a/apps/fclock/fclock.app.js b/apps/fclock/fclock.app.js
index 838a5578d..52607b9fc 100644
--- a/apps/fclock/fclock.app.js
+++ b/apps/fclock/fclock.app.js
@@ -2,7 +2,7 @@ var minutes;
var seconds;
var hours;
var date;
-var first = true;
+//var first = true;
var locale = require('locale');
var _12hour = (require("Storage").readJSON("setting.json", 1) || {})["12hour"] || false;
@@ -86,7 +86,7 @@ const drawSec = function (sections, color) {
const drawClock = function () {
- currentTime = new Date();
+ const currentTime = new Date();
//Get date as a string
date = dateStr(currentTime);
@@ -163,12 +163,10 @@ const drawHR = function () {
}
if (grow) {
- color = settings.hr.color;
- g.setColor(color);
+ g.setColor(settings.hr.color);
g.fillCircle(settings.hr.x, settings.hr.y, size);
} else {
- color = "#000000";
- g.setColor(color);
+ g.setColor("#000000");
g.drawCircle(settings.hr.x, settings.hr.y, size);
}
};
diff --git a/apps/fclock/metadata.json b/apps/fclock/metadata.json
index dffb197a2..3491be0e2 100644
--- a/apps/fclock/metadata.json
+++ b/apps/fclock/metadata.json
@@ -2,7 +2,7 @@
"id": "fclock",
"name": "fclock",
"shortName": "F Clock",
- "version": "0.03",
+ "version": "0.05",
"description": "Simple design of a digital clock",
"icon": "app.png",
"type": "clock",
diff --git a/apps/fileman/ChangeLog b/apps/fileman/ChangeLog
index f5af86229..cc1456b31 100644
--- a/apps/fileman/ChangeLog
+++ b/apps/fileman/ChangeLog
@@ -1,3 +1,4 @@
0.01: New app!
0.02: Improve handling of large amounts of files (fix #579)
0.03: Update RegExp use (Was using backreference instead of character code)
+
diff --git a/apps/fileman/manage_files.html b/apps/fileman/manage_files.html
new file mode 100644
index 000000000..8501d468a
--- /dev/null
+++ b/apps/fileman/manage_files.html
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
Stats
+
{{s[0]}}
{{s[1]}}
+
+
Files
+
+
+
+
+
+
Filename
show
delete
+
+
+
{{file}}
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/fileman/metadata.json b/apps/fileman/metadata.json
index f5589e396..52f2fd06d 100644
--- a/apps/fileman/metadata.json
+++ b/apps/fileman/metadata.json
@@ -7,6 +7,7 @@
"icon": "icons8-filing-cabinet-48.png",
"tags": "tools",
"supports": ["BANGLEJS","BANGLEJS2"],
+ "interface": "manage_files.html",
"readme": "README.md",
"storage": [
{"name":"fileman.app.js","url":"fileman.app.js"},
diff --git a/apps/flashcards/ChangeLog b/apps/flashcards/ChangeLog
index 6f9ed7196..4c0434f5b 100644
--- a/apps/flashcards/ChangeLog
+++ b/apps/flashcards/ChangeLog
@@ -2,4 +2,5 @@
1.10: Download cards data from Trello public board
1.20: Configuration instructions added and card layout optimized
1.30: Font size can be changed in Settings
-1.31: Fix for fast-loading support
\ No newline at end of file
+1.31: Fix for fast-loading support
+1.32: Minor code improvements
diff --git a/apps/flashcards/app.js b/apps/flashcards/app.js
index d2118f8cb..43dde213e 100644
--- a/apps/flashcards/app.js
+++ b/apps/flashcards/app.js
@@ -39,7 +39,7 @@
while (str.length > maxLength) {
let found = false;
// Inserts new line at first whitespace of the line
- for (i = maxLength - 1; i > 0; i--) {
+ for (let i = maxLength - 1; i > 0; i--) {
if (str.charAt(i)==' ') {
res = res + [str.slice(0, i), "\n"].join('');
str = str.slice(i + 1);
diff --git a/apps/flashcards/metadata.json b/apps/flashcards/metadata.json
index 096e7e918..dee6a9e3a 100644
--- a/apps/flashcards/metadata.json
+++ b/apps/flashcards/metadata.json
@@ -2,7 +2,7 @@
"id": "flashcards",
"name": "Flash Cards",
"shortName": "Flash Cards",
- "version": "1.31",
+ "version": "1.32",
"description": "Flash cards based on public Trello board",
"readme":"README.md",
"screenshots" : [ { "url":"screenshot.png" }],
diff --git a/apps/flightdash/ChangeLog b/apps/flightdash/ChangeLog
new file mode 100644
index 000000000..b0e36941a
--- /dev/null
+++ b/apps/flightdash/ChangeLog
@@ -0,0 +1,2 @@
+1.00: initial release
+1.01: Minor code improvements
diff --git a/apps/flightdash/README.md b/apps/flightdash/README.md
new file mode 100644
index 000000000..07b753178
--- /dev/null
+++ b/apps/flightdash/README.md
@@ -0,0 +1,76 @@
+# Flight Dashboard
+
+Shows basic flight and navigation instruments.
+
+
+
+Basic flight data includes:
+
+- Ground speed
+- Track
+- Altimeter
+- VSI
+- Local time
+
+You can also set a destination to get nav guidance:
+
+- Distance from destination
+- Bearing to destination
+- Estimated Time En-route (minutes and seconds)
+- Estimated Time of Arrival (in UTC)
+
+The speed/distance and altitude units are configurable.
+
+Altitude data can be derived from GPS or the Bangle's barometer.
+
+
+## DISCLAIMER
+
+Remember to Aviate - Navigate - Communicate! Do NOT get distracted by your
+gadgets, keep your eyes looking outside and do NOT rely on this app for actual
+navigation!
+
+
+## Usage
+
+After installing the app, use the "interface" page (floppy disk icon) in the
+App Loader to filter and upload a list of airports (to be used as navigation
+destinations). Due to memory constraints, only up to about 500 airports can be
+stored on the Bangle itself (recommended is around 100 - 150 airports max.).
+
+Then, on the Bangle, access the Flight-Dash settings, either through the
+Settings app (Settings -> Apps -> Flight-Dash) or a tap anywhere in the
+Flight-Dash app itself. The following settings are available:
+
+- **Nav Dest.**: Choose the navigation destination:
+ - Nearest airports (from the uploaded list)
+ - Search the uploaded list of airports
+ - User waypoints (which can be set/edited through the settings)
+ - Nearest airports (queried online through AVWX - requires Internet connection at the time)
+- **Speed** and **Altitude**: Set the preferred units of measurements.
+- **Use Baro**: If enabled, altitude information is derived from the Bangle's barometer (instead of using GPS altitude).
+
+If the barometer is used for altitude information, the current QNH value is
+also displayed. It can be adjusted by swiping up/down in the app.
+
+To query the nearest airports online through AVWX, you have to install - and
+configure - the [avwx](?id=avwx) module.
+
+The app requires a text input method (to set user waypoint names, and search
+for airports), and if not already installed will automatically install the
+default "textinput" app as a dependency.
+
+
+## Hint
+
+Under the bearing "band", the current nav destination is displayed. Next to
+that, you'll also find the cardinal direction you are approaching **from**.
+This can be useful for inbound radio calls. Together with the distance, the
+current altitude and the ETA, you have all the information required to make
+radio calls like a pro!
+
+
+## Author
+
+Flaparoo [github](https://github.com/flaparoo)
+
diff --git a/apps/flightdash/flightdash-icon.js b/apps/flightdash/flightdash-icon.js
new file mode 100644
index 000000000..3a2e2757c
--- /dev/null
+++ b/apps/flightdash/flightdash-icon.js
@@ -0,0 +1 @@
+require("heatshrink").decompress(atob("mEwwhC/AH4A/AHcCkAX/C/4X9kUiC/4XcgczmcwRSArBkYWBAAMyA4KUMC4QWDAAIXOAAUziMTmMRmZdRmQXDkZhIHQJ1IAAYXGBgoNDgQJFLoQhFDQ84wQFDlGDBwxBInGIDAUoxAXFJosDOIIXDAAgXCPoJkGBAKfBmc6C4ujBIINBiYXIEIMK1AWDxWgHoQXMgGqC4eqKoYXHL4QFChQYC1QuBEwbcHZo7hHBpYA/AH4A/AH4"))
diff --git a/apps/flightdash/flightdash.app.js b/apps/flightdash/flightdash.app.js
new file mode 100644
index 000000000..ac0146210
--- /dev/null
+++ b/apps/flightdash/flightdash.app.js
@@ -0,0 +1,527 @@
+/*
+ * Flight Dashboard - Bangle.js
+ */
+
+const COLOUR_BLACK = 0x0000; // same as: g.setColor(0, 0, 0)
+const COLOUR_WHITE = 0xffff; // same as: g.setColor(1, 1, 1)
+const COLOUR_GREEN = 0x07e0; // same as: g.setColor(0, 1, 0)
+const COLOUR_YELLOW = 0xffe0; // same as: g.setColor(1, 1, 0)
+const COLOUR_MAGENTA = 0xf81f; // same as: g.setColor(1, 0, 1)
+const COLOUR_CYAN = 0x07ff; // same as: g.setColor(0, 1, 1)
+const COLOUR_LIGHT_BLUE = 0x841f; // same as: g.setColor(0.5, 0.5, 1)
+
+const APP_NAME = 'flightdash';
+
+const horizontalCenter = g.getWidth() / 2;
+//const verticalCenter = g.getHeight() / 2;
+
+const dataFontHeight = 22;
+const secondaryFontHeight = 18;
+const labelFontHeight = 12;
+
+
+//globals
+var settings = {};
+
+//var updateInterval;
+
+var speed = '-'; var speedPrev = -1;
+var track = '-'; var trackPrev = -1;
+var lat = 0; var lon = 0;
+var distance = '-'; var distancePrev = -1;
+var bearing = '-'; var bearingPrev = -1;
+var relativeBearing = 0; var relativeBearingPrev = -1;
+var fromCardinal = '-';
+var ETAdate = new Date();
+var ETA = '-'; var ETAPrev = '';
+
+var QNH = Math.round(Bangle.getOptions().seaLevelPressure); var QNHPrev = -1;
+
+var altitude = '-'; var altitudePrev = -1;
+
+var VSI = '-'; var VSIPrev = -1;
+var VSIraw = 0;
+var VSIprevTimestamp = Date.now();
+var VSIprevAltitude;
+var VSIsamples = 0; var VSIsamplesCount = 0;
+
+var speedUnit = 'N/A';
+var distanceUnit = 'N/A';
+var altUnit = 'N/A';
+
+
+// date object to time string in format (HH:MM[:SS])
+function timeStr(date, seconds) {
+ let timeStr = date.getHours().toString();
+ if (timeStr.length == 1) timeStr = '0' + timeStr;
+ let minutes = date.getMinutes().toString();
+ if (minutes.length == 1) minutes = '0' + minutes;
+ timeStr += ':' + minutes;
+ if (seconds) {
+ let seconds = date.getSeconds().toString();
+ if (seconds.length == 1) seconds = '0' + seconds;
+ timeStr += ':' + seconds;
+ }
+ return timeStr;
+}
+
+// add thousands separator to number
+function addThousandSeparator(n) {
+ let s = n.toString();
+ if (s.length > 3) {
+ return s.substr(0, s.length - 3) + ',' + s.substr(s.length - 3, 3);
+ } else {
+ return s;
+ }
+}
+
+
+// update VSI
+function updateVSI(alt) {
+ VSIsamples += alt; VSIsamplesCount += 1;
+ let VSInewTimestamp = Date.now();
+ if (VSIprevTimestamp + 1000 <= VSInewTimestamp) { // update VSI every 1 second
+ let VSInewAltitude = VSIsamples / VSIsamplesCount;
+ if (VSIprevAltitude) {
+ let VSIinterval = (VSInewTimestamp - VSIprevTimestamp) / 1000;
+ VSIraw = (VSInewAltitude - VSIprevAltitude) * 60 / VSIinterval; // extrapolate to change / minute
+ }
+ VSIprevTimestamp = VSInewTimestamp;
+ VSIprevAltitude = VSInewAltitude;
+ VSIsamples = 0; VSIsamplesCount = 0;
+ }
+
+ VSI = Math.floor(VSIraw / 10) * 10; // "smooth" VSI value
+ if (settings.altimeterUnits == 0) { // Feet
+ VSI = Math.round(VSI * 3.28084);
+ } // nothing else required since VSI is already in meters ("smoothed")
+
+ if (VSI > 9999) VSI = 9999;
+ else if (VSI < -9999) VSI = -9999;
+}
+
+// update GPS-derived information
+function updateGPS(fix) {
+ if (!('fix' in fix) || fix.fix == 0 || fix.satellites < 4) return;
+
+ speed = 'N/A';
+ if (settings.speedUnits == 0) { // Knots
+ speed = Math.round(fix.speed * 0.539957);
+ } else if (settings.speedUnits == 1) { // km/h
+ speed = Math.round(fix.speed);
+ } else if (settings.speedUnits == 2) { // MPH
+ speed = Math.round(fix.speed * 0.621371);
+ }
+ if (speed > 9999) speed = 9999;
+
+ if (! settings.useBaro) { // use GPS altitude
+ altitude = 'N/A';
+ if (settings.altimeterUnits == 0) { // Feet
+ altitude = Math.round(fix.alt * 3.28084);
+ } else if (settings.altimeterUnits == 1) { // Meters
+ altitude = Math.round(fix.alt);
+ }
+ if (altitude > 99999) altitude = 99999;
+
+ updateVSI(fix.alt);
+ }
+
+ track = Math.round(fix.course);
+ if (isNaN(track)) track = '-';
+ else if (track < 10) track = '00'+track;
+ else if (track < 100) track = '0'+track;
+
+ lat = fix.lat;
+ lon = fix.lon;
+
+ // calculation from https://www.movable-type.co.uk/scripts/latlong.html
+ const latRad1 = lat * Math.PI/180;
+ const latRad2 = settings.destLat * Math.PI/180;
+ const lonRad1 = lon * Math.PI/180;
+ const lonRad2 = settings.destLon * Math.PI/180;
+
+ // distance (using "Equirectangular approximation")
+ let x = (lonRad2 - lonRad1) * Math.cos((latRad1 + latRad2) / 2);
+ let y = (latRad2 - latRad1);
+ let distanceNumber = Math.sqrt(x*x + y*y) * 6371; // in km - 6371 = mean Earth radius
+ if (settings.speedUnits == 0) { // NM
+ distanceNumber = distanceNumber * 0.539957;
+ } else if (settings.speedUnits == 2) { // miles
+ distanceNumber = distanceNumber * 0.621371;
+ }
+ if (distanceNumber > 99.9) {
+ distance = '>100';
+ } else {
+ distance = (Math.round(distanceNumber * 10) / 10).toString();
+ if (! distance.includes('.'))
+ distance += '.0';
+ }
+
+ // bearing
+ y = Math.sin(lonRad2 - lonRad1) * Math.cos(latRad2);
+ x = Math.cos(latRad1) * Math.sin(latRad2) -
+ Math.sin(latRad1) * Math.cos(latRad2) * Math.cos(lonRad2 - lonRad1);
+ let nonNormalisedBearing = Math.atan2(y, x);
+ bearing = Math.round((nonNormalisedBearing * 180 / Math.PI + 360) % 360);
+
+ if (bearing > 337 || bearing < 23) {
+ fromCardinal = 'S';
+ } else if (bearing < 68) {
+ fromCardinal = 'SW';
+ } else if (bearing < 113) {
+ fromCardinal = 'W';
+ } else if (bearing < 158) {
+ fromCardinal = 'NW';
+ } else if (bearing < 203) {
+ fromCardinal = 'N';
+ } else if (bearing < 248) {
+ fromCardinal = 'NE';
+ } else if (bearing < 293) {
+ fromCardinal = 'E';
+ } else{
+ fromCardinal = 'SE';
+ }
+
+ if (bearing < 10) bearing = '00'+bearing;
+ else if (bearing < 100) bearing = '0'+bearing;
+
+ relativeBearing = parseInt(bearing) - parseInt(track);
+ if (isNaN(relativeBearing)) relativeBearing = 0;
+ if (relativeBearing > 180) relativeBearing -= 360;
+ else if (relativeBearing < -180) relativeBearing += 360;
+
+ // ETA
+ if (speed) {
+ let ETE = distanceNumber * 3600 / speed;
+ let now = new Date();
+ ETAdate = new Date(now + (now.getTimezoneOffset() * 1000 * 60) + ETE*1000);
+ if (ETE < 86400) {
+ ETA = timeStr(ETAdate, false);
+ } else {
+ ETA = '>24h';
+ }
+ } else {
+ ETAdate = new Date();
+ ETA = '-';
+ }
+}
+
+
+// update barometric information
+function updatePressure(e) {
+ altitude = 'N/A';
+ if (settings.altimeterUnits == 0) { // Feet
+ altitude = Math.round(e.altitude * 3.28084);
+ } else if (settings.altimeterUnits == 1) { // Meters
+ altitude = Math.round(e.altitude); // altitude is given in meters
+ }
+ if (altitude > 99999) altitude = 99999;
+
+ updateVSI(e.altitude);
+}
+
+
+// (re-)draw all read-outs
+function draw(initial) {
+
+ g.setBgColor(COLOUR_BLACK);
+
+ // speed
+ if (speed != speedPrev || initial) {
+ g.setFontAlign(-1, -1).setFont("Vector", dataFontHeight).setColor(COLOUR_GREEN);
+ g.clearRect(0, 0, 55, dataFontHeight);
+ g.drawString(speed.toString(), 0, 0, false);
+ if (initial) {
+ g.setFontAlign(-1, -1).setFont("Vector", labelFontHeight).setColor(COLOUR_CYAN);
+ g.drawString(speedUnit, 0, dataFontHeight, false);
+ }
+ speedPrev = speed;
+ }
+
+
+ // distance
+ if (distance != distancePrev || initial) {
+ g.setFontAlign(1, -1).setFont("Vector", dataFontHeight).setColor(COLOUR_WHITE);
+ g.clearRect(g.getWidth() - 58, 0, g.getWidth(), dataFontHeight);
+ g.drawString(distance, g.getWidth(), 0, false);
+ if (initial) {
+ g.setFontAlign(1, -1).setFont("Vector", labelFontHeight).setColor(COLOUR_CYAN);
+ g.drawString(distanceUnit, g.getWidth(), dataFontHeight, false);
+ }
+ distancePrev = distance;
+ }
+
+
+ // track (+ static track/bearing content)
+ let trackY = 18;
+ let destInfoY = trackY + 53;
+ if (track != trackPrev || initial) {
+ g.setFontAlign(0, -1).setFont("Vector", dataFontHeight).setColor(COLOUR_WHITE);
+ g.clearRect(horizontalCenter - 29, trackY, horizontalCenter + 28, trackY + dataFontHeight);
+ g.drawString(track.toString() + "\xB0", horizontalCenter + 3, trackY, false);
+ if (initial) {
+ let y = trackY + dataFontHeight + 1;
+ g.setColor(COLOUR_YELLOW);
+ g.drawRect(horizontalCenter - 30, trackY - 3, horizontalCenter + 29, y);
+ g.drawLine(0, y, g.getWidth(), y);
+ y += dataFontHeight + 5;
+ g.drawLine(0, y, g.getWidth(), y);
+
+ g.setFontAlign(1, -1).setFont("Vector", secondaryFontHeight).setColor(COLOUR_MAGENTA);
+ g.drawString(settings.destID, horizontalCenter, destInfoY, false);
+ }
+ trackPrev = track;
+ }
+
+
+ // bearing
+ if (bearing != bearingPrev || relativeBearing != relativeBearingPrev || initial) {
+ let bearingY = trackY + 27;
+
+ g.clearRect(0, bearingY, g.getWidth(), bearingY + dataFontHeight);
+
+ g.setColor(COLOUR_YELLOW);
+ for (let i = Math.floor(relativeBearing * 2.5) % 25; i <= g.getWidth(); i += 25) {
+ g.drawLine(i, bearingY + 3, i, bearingY + 16);
+ }
+
+ let bearingX = horizontalCenter + relativeBearing * 2.5;
+ if (bearingX > g.getWidth() - 26) bearingX = g.getWidth() - 26;
+ else if (bearingX < 26) bearingX = 26;
+ g.setFontAlign(0, -1).setFont("Vector", dataFontHeight).setColor(COLOUR_MAGENTA);
+ g.drawString(bearing.toString() + "\xB0", bearingX + 3, bearingY, false);
+
+ g.clearRect(horizontalCenter + 42, destInfoY, horizontalCenter + 69, destInfoY + secondaryFontHeight);
+ g.setFontAlign(-1, -1).setFont("Vector", secondaryFontHeight).setColor(COLOUR_MAGENTA);
+ g.drawString(fromCardinal, horizontalCenter + 42, destInfoY, false);
+ if (initial) {
+ g.setFontAlign(-1, -1).setFont("Vector", labelFontHeight).setColor(COLOUR_CYAN);
+ g.drawString(' from', horizontalCenter, destInfoY, false);
+ }
+
+ bearingPrev = bearing;
+ relativeBearingPrev = relativeBearing;
+ }
+
+
+ let row3y = g.getHeight() - 48;
+
+ // QNH
+ if (settings.useBaro) {
+ if (QNH != QNHPrev || initial) {
+ let QNHy = row3y - secondaryFontHeight - 2;
+ g.setFontAlign(0, 1).setFont("Vector", secondaryFontHeight).setColor(COLOUR_WHITE);
+ g.clearRect(horizontalCenter - 29, QNHy - secondaryFontHeight, horizontalCenter + 22, QNHy);
+ g.drawString(QNH.toString(), horizontalCenter - 3, QNHy, false);
+ if (initial) {
+ g.setFontAlign(0, -1).setFont("Vector", labelFontHeight).setColor(COLOUR_CYAN);
+ g.drawString('QNH', horizontalCenter - 3, QNHy, false);
+ }
+ QNHPrev = QNH;
+ }
+ }
+
+
+ // VSI
+ if (VSI != VSIPrev || initial) {
+ g.setFontAlign(-1, 1).setFont("Vector", secondaryFontHeight).setColor(COLOUR_WHITE);
+ g.clearRect(0, row3y - secondaryFontHeight, 51, row3y);
+ g.drawString(VSI.toString(), 0, row3y, false);
+ if (initial) {
+ g.setFontAlign(-1, 1).setFont("Vector", labelFontHeight).setColor(COLOUR_CYAN);
+ g.drawString(altUnit + '/min', 0, row3y - secondaryFontHeight, false);
+ }
+
+ let VSIarrowX = 6;
+ let VSIarrowY = row3y - 42;
+ g.clearRect(VSIarrowX - 7, VSIarrowY - 10, VSIarrowX + 6, VSIarrowY + 10);
+ g.setColor(COLOUR_WHITE);
+ if (VSIraw > 30) { // climbing
+ g.fillRect(VSIarrowX - 1, VSIarrowY, VSIarrowX + 1, VSIarrowY + 10);
+ g.fillPoly([ VSIarrowX , VSIarrowY - 11,
+ VSIarrowX + 7, VSIarrowY,
+ VSIarrowX - 7, VSIarrowY]);
+ } else if (VSIraw < -30) { // descending
+ g.fillRect(VSIarrowX - 1, VSIarrowY - 10, VSIarrowX + 1, VSIarrowY);
+ g.fillPoly([ VSIarrowX , VSIarrowY + 11,
+ VSIarrowX + 7, VSIarrowY,
+ VSIarrowX - 7, VSIarrowY ]);
+ }
+ }
+
+
+ // altitude
+ if (altitude != altitudePrev || initial) {
+ g.setFontAlign(1, 1).setFont("Vector", secondaryFontHeight).setColor(COLOUR_WHITE);
+ g.clearRect(g.getWidth() - 65, row3y - secondaryFontHeight, g.getWidth(), row3y);
+ g.drawString(addThousandSeparator(altitude), g.getWidth(), row3y, false);
+ if (initial) {
+ g.setFontAlign(1, 1).setFont("Vector", labelFontHeight).setColor(COLOUR_CYAN);
+ g.drawString(altUnit, g.getWidth(), row3y - secondaryFontHeight, false);
+ }
+ altitudePrev = altitude;
+ }
+
+
+ // time
+ let now = new Date();
+ let nowUTC = new Date(now + (now.getTimezoneOffset() * 1000 * 60));
+ g.setFontAlign(-1, 1).setFont("Vector", dataFontHeight).setColor(COLOUR_LIGHT_BLUE);
+ let timeStrMetrics = g.stringMetrics(timeStr(now, false));
+ g.drawString(timeStr(now, false), 0, g.getHeight(), true);
+
+ let seconds = now.getSeconds().toString();
+ if (seconds.length == 1) seconds = '0' + seconds;
+ g.setFontAlign(-1, 1).setFont("Vector", secondaryFontHeight);
+ g.drawString(seconds, timeStrMetrics.width + 2, g.getHeight() - 1, true);
+
+ if (initial) {
+ g.setFontAlign(-1, 1).setFont("Vector", labelFontHeight).setColor(COLOUR_CYAN);
+ g.drawString('LOCAL', 0, g.getHeight() - dataFontHeight, false);
+ }
+
+
+ // ETE
+ let ETEy = g.getHeight() - dataFontHeight;
+ let ETE = '-';
+ if (ETA != '-') {
+ let ETEseconds = Math.floor((ETAdate - nowUTC) / 1000);
+ if (ETEseconds < 0) ETEseconds = 0;
+ ETE = ETEseconds % 60;
+ if (ETE < 10) ETE = '0' + ETE;
+ ETE = Math.floor(ETEseconds / 60) + ':' + ETE;
+ if (ETE.length > 6) ETE = '>999m';
+ }
+ g.clearRect(horizontalCenter - 35, ETEy - secondaryFontHeight, horizontalCenter + 29, ETEy);
+ g.setFontAlign(0, 1).setFont("Vector", secondaryFontHeight).setColor(COLOUR_WHITE);
+ g.drawString(ETE, horizontalCenter - 3, ETEy, false);
+ if (initial) {
+ g.setFontAlign(0, 1).setFont("Vector", labelFontHeight).setColor(COLOUR_CYAN);
+ g.drawString('ETE', horizontalCenter - 3, ETEy - secondaryFontHeight, false);
+ }
+
+
+ // ETA
+ if (ETA != ETAPrev || initial) {
+ g.clearRect(g.getWidth() - 63, g.getHeight() - dataFontHeight, g.getWidth(), g.getHeight());
+ g.setFontAlign(1, 1).setFont("Vector", dataFontHeight).setColor(COLOUR_WHITE);
+ g.drawString(ETA, g.getWidth(), g.getHeight(), false);
+ if (initial) {
+ g.setFontAlign(1, 1).setFont("Vector", labelFontHeight).setColor(COLOUR_CYAN);
+ g.drawString('UTC ETA', g.getWidth(), g.getHeight() - dataFontHeight, false);
+ }
+ ETAPrev = ETA;
+ }
+}
+
+
+function handleSwipes(directionLR, directionUD) {
+ if (directionUD == -1) { // up -> increase QNH
+ QNH = Math.round(Bangle.getOptions().seaLevelPressure);
+ QNH++;
+ Bangle.setOptions({'seaLevelPressure': QNH});
+ } else if (directionUD == 1) { // down -> decrease QNH
+ QNH = Math.round(Bangle.getOptions().seaLevelPressure);
+ QNH--;
+ Bangle.setOptions({'seaLevelPressure': QNH});
+ }
+}
+
+function handleTouch(button, xy) {
+ if ('handled' in xy && xy.handled) return;
+ Bangle.removeListener('touch', handleTouch);
+ if (settings.useBaro) {
+ Bangle.removeListener('swipe', handleSwipes);
+ }
+
+ // any touch -> show settings
+ clearInterval(updateTimeInterval);
+ Bangle.setGPSPower(false, APP_NAME);
+ if (settings.useBaro)
+ Bangle.setBarometerPower(false, APP_NAME);
+
+ eval(require("Storage").read(APP_NAME+'.settings.js'))( () => {
+ E.showMenu();
+ // "clear" values potentially affected by a settings change
+ speed = '-'; distance = '-';
+ altitude = '-'; VSI = '-';
+ // re-launch
+ start();
+ });
+}
+
+
+/*
+ * main
+ */
+function start() {
+
+ // read in the settings
+ settings = Object.assign({
+ useBaro: false,
+ speedUnits: 0, // KTS
+ altimeterUnits: 0, // FT
+ destID: 'KOSH',
+ destLat: 43.9844,
+ destLon: -88.5570,
+ }, require('Storage').readJSON(APP_NAME+'.json', true) || {});
+
+ // set units
+ if (settings.speedUnits == 0) { // Knots
+ speedUnit = 'KTS';
+ distanceUnit = 'NM';
+ } else if (settings.speedUnits == 1) { // km/h
+ speedUnit = 'KPH';
+ distanceUnit = 'KM';
+ } else if (settings.speedUnits == 2) { // MPH
+ speedUnit = 'MPH';
+ distanceUnit = 'SM';
+ }
+
+ if (settings.altimeterUnits == 0) { // Feet
+ altUnit = 'FT';
+ } else if (settings.altimeterUnits == 1) { // Meters
+ altUnit = 'M';
+ }
+
+ // initialise
+ g.reset();
+ g.setBgColor(COLOUR_BLACK);
+ g.clear();
+
+ // draw incl. static components
+ draw(true);
+
+ // enable timeout/interval and sensors
+ setTimeout(function() {
+ draw();
+ updateTimeInterval = setInterval(draw, 1000);
+ }, 1000 - (Date.now() % 1000));
+
+ Bangle.setGPSPower(true, APP_NAME);
+ Bangle.on('GPS', updateGPS);
+
+ if (settings.useBaro) {
+ Bangle.setBarometerPower(true, APP_NAME);
+ Bangle.on('pressure', updatePressure);
+ }
+
+ // handle interaction
+ if (settings.useBaro) {
+ Bangle.on('swipe', handleSwipes);
+ }
+ Bangle.on('touch', handleTouch);
+ setWatch(e => { Bangle.showClock(); }, BTN1); // exit on button press
+}
+
+start();
+
+
+/*
+// TMP for testing:
+//settings.speedUnits = 1;
+//settings.altimeterUnits = 1;
+QNH = 1013;
+updateGPS({"fix":1,"speed":228,"alt":3763,"course":329,"lat":36.0182,"lon":-75.6713});
+updatePressure({"altitude":3700});
+*/
diff --git a/apps/flightdash/flightdash.png b/apps/flightdash/flightdash.png
new file mode 100644
index 000000000..8230bc0c1
Binary files /dev/null and b/apps/flightdash/flightdash.png differ
diff --git a/apps/flightdash/flightdash.settings.js b/apps/flightdash/flightdash.settings.js
new file mode 100644
index 000000000..cd1ecdac6
--- /dev/null
+++ b/apps/flightdash/flightdash.settings.js
@@ -0,0 +1,327 @@
+(function(back) {
+ const APP_NAME = 'flightdash';
+ const FILE = APP_NAME+'.json';
+
+ // if the avwx module is available, include an extra menu item to query nearest airports via AVWX
+ var avwx;
+ try {
+ avwx = require('avwx');
+ } catch (error) {
+ // avwx module not installed
+ }
+
+ // Load settings
+ var settings = Object.assign({
+ useBaro: false,
+ speedUnits: 0, // KTS
+ altimeterUnits: 0, // FT
+ destID: 'KOSH',
+ destLat: 43.9844,
+ destLon: -88.5570,
+ }, require('Storage').readJSON(FILE, true) || {});
+
+ function writeSettings() {
+ require('Storage').writeJSON(FILE, settings);
+ }
+
+ // update the nav destination
+ function updateNavDest(destID, destLat, destLon) {
+ settings.destID = destID.replace(/[\W]+/g, '').slice(0, 7);
+ settings.destLat = parseFloat(destLat);
+ settings.destLon = parseFloat(destLon);
+ writeSettings();
+ createDestMainMenu();
+ }
+
+ var airports; // cache list of airports
+ function readAirportsList(empty_cb) {
+ if (airports) { // airport list has already been read in
+ return true;
+ }
+ airports = require('Storage').readJSON(APP_NAME+'.airports.json', true);
+ if (! airports) {
+ E.showPrompt('No airports stored - download from the Bangle Apps Loader!',
+ {title: 'Flight-Dash', buttons: {OK: true} }).then((v) => {
+ empty_cb();
+ });
+ return false;
+ }
+ return true;
+ }
+
+ // use GPS fix
+ var afterGPSfixMenu = 'destNearest';
+ function getLatLon(fix) {
+ if (!('fix' in fix) || fix.fix == 0 || fix.satellites < 4) return;
+ Bangle.setGPSPower(false, APP_NAME+'-settings');
+ Bangle.removeListener('GPS', getLatLon);
+ switch (afterGPSfixMenu) {
+ case 'destNearest':
+ loadNearest(fix.lat, fix.lon);
+ break;
+ case 'createUserWaypoint':
+ {
+ if (!('userWaypoints' in settings))
+ settings.userWaypoints = [];
+ let newIdx = settings.userWaypoints.length;
+ settings.userWaypoints[newIdx] = {
+ 'ID': 'USER'+(newIdx + 1),
+ 'lat': fix.lat,
+ 'lon': fix.lon,
+ };
+ writeSettings();
+ showUserWaypoints();
+ break;
+ }
+ case 'destAVWX':
+ // the free ("hobby") account of AVWX is limited to 10 nearest stations
+ avwx.request('station/near/'+fix.lat+','+fix.lon, 'n=10&airport=true&reporting=false', data => {
+ loadAVWX(data);
+ }, error => {
+ console.log(error);
+ E.showPrompt('AVWX query failed: '+error, {title: 'Flight-Dash', buttons: {OK: true} }).then((v) => {
+ createDestMainMenu();
+ });
+ });
+ break;
+ default:
+ back();
+ }
+ }
+
+ // find nearest airports
+ function loadNearest(lat, lon) {
+ if (! readAirportsList(createDestMainMenu))
+ return;
+
+ const latRad1 = lat * Math.PI/180;
+ const lonRad1 = lon * Math.PI/180;
+ for (let i = 0; i < airports.length; i++) {
+ const latRad2 = airports[i].la * Math.PI/180;
+ const lonRad2 = airports[i].lo * Math.PI/180;
+ let x = (lonRad2 - lonRad1) * Math.cos((latRad1 + latRad2) / 2);
+ let y = (latRad2 - latRad1);
+ airports[i].distance = Math.sqrt(x*x + y*y) * 6371;
+ }
+ let nearest = airports.sort((a, b) => a.distance - b.distance).slice(0, 14);
+
+ let destNearest = {
+ '' : { 'title' : 'Nearest' },
+ '< Back' : () => createDestMainMenu(),
+ };
+ for (let i in nearest) {
+ let airport = nearest[i];
+ destNearest[airport.i+' - '+airport.n] =
+ () => setTimeout(updateNavDest, 10, airport.i, airport.la, airport.lo);
+ }
+
+ E.showMenu(destNearest);
+ }
+
+ // process the data returned by AVWX
+ function loadAVWX(data) {
+ let AVWXairports = JSON.parse(data.resp);
+
+ let destAVWX = {
+ '' : { 'title' : 'Nearest (AVWX)' },
+ '< Back' : () => createDestMainMenu(),
+ };
+ for (let i in AVWXairports) {
+ let airport = AVWXairports[i].station;
+ let airport_id = ( airport.icao ? airport.icao : airport.gps );
+ destAVWX[airport_id+' - '+airport.name] =
+ () => setTimeout(updateNavDest, 10, airport_id, airport.latitude, airport.longitude);
+ }
+
+ E.showMenu(destAVWX);
+ }
+
+ // individual user waypoint menu
+ function showUserWaypoint(idx) {
+ let wayptID = settings.userWaypoints[idx].ID;
+ let wayptLat = settings.userWaypoints[idx].lat;
+ let wayptLon = settings.userWaypoints[idx].lon;
+ let destUser = {
+ '' : { 'title' : wayptID },
+ '< Back' : () => showUserWaypoints(),
+ };
+ destUser['Set as Dest.'] =
+ () => setTimeout(updateNavDest, 10, wayptID, wayptLat, wayptLon);
+ destUser['Edit ID'] = function() {
+ require('textinput').input({text: wayptID}).then(result => {
+ if (result) {
+ if (result.length > 7) {
+ console.log('test');
+ E.showPrompt('ID is too long!\n(max. 7 chars)',
+ {title: 'Flight-Dash', buttons: {OK: true} }).then((v) => {
+ showUserWaypoint(idx);
+ });
+ } else {
+ settings.userWaypoints[idx].ID = result;
+ writeSettings();
+ showUserWaypoint(idx);
+ }
+ } else {
+ showUserWaypoint(idx);
+ }
+ });
+ };
+ destUser['Delete'] = function() {
+ E.showPrompt('Delete user waypoint '+wayptID+'?',
+ {'title': 'Flight-Dash'}).then((v) => {
+ if (v) {
+ settings.userWaypoints.splice(idx, 1);
+ writeSettings();
+ showUserWaypoints();
+ } else {
+ showUserWaypoint(idx);
+ }
+ });
+ };
+
+ E.showMenu(destUser);
+ }
+
+ // user waypoints menu
+ function showUserWaypoints() {
+ let destUser = {
+ '' : { 'title' : 'User Waypoints' },
+ '< Back' : () => createDestMainMenu(),
+ };
+ for (let i in settings.userWaypoints) {
+ let waypt = settings.userWaypoints[i];
+ let idx = i;
+ destUser[waypt.ID] =
+ () => setTimeout(showUserWaypoint, 10, idx);
+ }
+ destUser['Create New'] = function() {
+ E.showMessage('Waiting for GPS fix', {title: 'Flight-Dash'});
+ afterGPSfixMenu = 'createUserWaypoint';
+ Bangle.setGPSPower(true, APP_NAME+'-settings');
+ Bangle.on('GPS', getLatLon);
+ };
+
+ E.showMenu(destUser);
+ }
+
+ // destination main menu
+ function createDestMainMenu() {
+ let destMainMenu = {
+ '' : { 'title' : 'Nav Dest.' },
+ '< Back' : () => E.showMenu(mainMenu),
+ };
+ destMainMenu['Is: '+settings.destID] = {};
+ destMainMenu['Nearest'] = function() {
+ E.showMessage('Waiting for GPS fix', {title: 'Flight-Dash'});
+ afterGPSfixMenu = 'destNearest';
+ Bangle.setGPSPower(true, APP_NAME+'-settings');
+ Bangle.on('GPS', getLatLon);
+ };
+ destMainMenu['Search'] = function() {
+ require('textinput').input({text: ''}).then(result => {
+ if (result) {
+ if (! readAirportsList(createDestMainMenu))
+ return;
+
+ result = result.toUpperCase();
+ let matches = [];
+ let tooManyFound = false;
+ for (let i in airports) {
+ if (airports[i].i.toUpperCase().includes(result) ||
+ airports[i].n.toUpperCase().includes(result)) {
+ matches.push(airports[i]);
+ if (matches.length >= 15) {
+ tooManyFound = true;
+ break;
+ }
+ }
+ }
+ if (! matches.length) {
+ E.showPrompt('No airports found!', {title: 'Flight-Dash', buttons: {OK: true} }).then((v) => {
+ createDestMainMenu();
+ });
+ return;
+ }
+
+ let destSearch = {
+ '' : { 'title' : 'Search Results' },
+ '< Back' : () => createDestMainMenu(),
+ };
+ for (let i in matches) {
+ let airport = matches[i];
+ destSearch[airport.i+' - '+airport.n] =
+ () => setTimeout(updateNavDest, 10, airport.i, airport.la, airport.lo);
+ }
+ if (tooManyFound) {
+ destSearch['More than 15 airports found!'] = {};
+ }
+
+ E.showMenu(destSearch);
+ } else {
+ createDestMainMenu();
+ }
+ });
+ };
+ destMainMenu['User waypts'] = function() { showUserWaypoints(); };
+ if (avwx) {
+ destMainMenu['Nearest (AVWX)'] = function() {
+ E.showMessage('Waiting for GPS fix', {title: 'Flight-Dash'});
+ afterGPSfixMenu = 'destAVWX';
+ Bangle.setGPSPower(true, APP_NAME+'-settings');
+ Bangle.on('GPS', getLatLon);
+ };
+ }
+ E.showMenu(destMainMenu);
+ }
+
+ // main menu
+ mainMenu = {
+ '' : { 'title' : 'Flight-Dash' },
+ '< Back' : () => {
+ Bangle.setGPSPower(false, APP_NAME+'-settings');
+ Bangle.removeListener('GPS', getLatLon);
+ back();
+ },
+ 'Nav Dest.': () => createDestMainMenu(),
+ 'Speed': {
+ value: parseInt(settings.speedUnits) || 0,
+ min: 0,
+ max: 2,
+ format: v => {
+ switch (v) {
+ case 0: return 'Knots';
+ case 1: return 'km/h';
+ case 2: return 'MPH';
+ }
+ },
+ onchange: v => {
+ settings.speedUnits = v;
+ writeSettings();
+ }
+ },
+ 'Altitude': {
+ value: parseInt(settings.altimeterUnits) || 0,
+ min: 0,
+ max: 1,
+ format: v => {
+ switch (v) {
+ case 0: return 'Feet';
+ case 1: return 'Meters';
+ }
+ },
+ onchange: v => {
+ settings.altimeterUnits = v;
+ writeSettings();
+ }
+ },
+ 'Use Baro': {
+ value: !!settings.useBaro, // !! converts undefined to false
+ onchange: v => {
+ settings.useBaro = v;
+ writeSettings();
+ }
+ },
+ };
+
+ E.showMenu(mainMenu);
+})
diff --git a/apps/flightdash/interface.html b/apps/flightdash/interface.html
new file mode 100644
index 000000000..d0f57f316
--- /dev/null
+++ b/apps/flightdash/interface.html
@@ -0,0 +1,186 @@
+
+
+
+
+
+
+
+
+
You can upload a list of airports, which can then be used as the
+ navigation destinations in the Flight-Dash. It is recommended to only
+ upload up to 100 - 150 airports max. Due to memory contraints on the
+ Bangle, no more than 500 airports can be uploaded.
+
+
The database of airports is based on OurAirports.
+
+
Filter Airports
+
+
+ nm of
+
+ /
+
+
+
+ This is using a simple lat/lon "block" - and
+ not within a proper radius around the given lat/lon position. An easy
+ way to find a lat/lon pair is to search for an airport based on ident
+ or name, and then use the found coordinates.
+
+
+
- or -
+
+
+
+
+
- or -
+
+
+
+
+
Only 1 of the above filters is applied, with higher up in the list taking precedence.
+
+
+
+
+
+
+
+
+
diff --git a/apps/flightdash/jquery-csv.min.js b/apps/flightdash/jquery-csv.min.js
new file mode 100644
index 000000000..cbaefa6b8
--- /dev/null
+++ b/apps/flightdash/jquery-csv.min.js
@@ -0,0 +1 @@
+RegExp.escape=function(r){return r.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&")},function(){"use strict";var p;(p="undefined"!=typeof jQuery&&jQuery?jQuery:{}).csv={defaults:{separator:",",delimiter:'"',headers:!0},hooks:{castToScalar:function(r,e){if(isNaN(r))return r;if(/\./.test(r))return parseFloat(r);var a=parseInt(r);return isNaN(a)?null:a}},parsers:{parse:function(r,e){var a=e.separator,t=e.delimiter;e.state.rowNum||(e.state.rowNum=1),e.state.colNum||(e.state.colNum=1);var o=[],s=[],n=0,i="",l=!1;function u(){if(n=0,i="",e.start&&e.state.rowNum=e.end&&(l=!0),e.state.rowNum++,e.state.colNum=1}function c(){if(void 0===e.onParseValue)s.push(i);else if(e.headers&&1===e.state.rowNum)s.push(i);else{var r=e.onParseValue(i,e.state);!1!==r&&s.push(r)}i="",n=0,e.state.colNum++}var f=RegExp.escape(a),d=RegExp.escape(t),m=/(D|S|\r\n|\n|\r|[^DS\r\n]+)/,p=m.source;return p=(p=p.replace(/S/g,f)).replace(/D/g,d),m=new RegExp(p,"gm"),r.replace(m,function(r){if(!l)switch(n){case 0:if(r===a){i+="",c();break}if(r===t){n=1;break}if(/^(\r\n|\n|\r)$/.test(r)){c(),u();break}i+=r,n=3;break;case 1:if(r===t){n=2;break}i+=r,n=1;break;case 2:if(r===t){i+=r,n=1;break}if(r===a){c();break}if(/^(\r\n|\n|\r)$/.test(r)){c(),u();break}throw Error("CSVDataError: Illegal State [Row:"+e.state.rowNum+"][Col:"+e.state.colNum+"]");case 3:if(r===a){c();break}if(/^(\r\n|\n|\r)$/.test(r)){c(),u();break}if(r===t)throw Error("CSVDataError: Illegal Quote [Row:"+e.state.rowNum+"][Col:"+e.state.colNum+"]");throw Error("CSVDataError: Illegal Data [Row:"+e.state.rowNum+"][Col:"+e.state.colNum+"]");default:throw Error("CSVDataError: Unknown State [Row:"+e.state.rowNum+"][Col:"+e.state.colNum+"]")}}),0!==s.length&&(c(),u()),o},splitLines:function(r,a){if(r){var t=(a=a||{}).separator||p.csv.defaults.separator,o=a.delimiter||p.csv.defaults.delimiter;a.state=a.state||{},a.state.rowNum||(a.state.rowNum=1);var e=[],s=0,n="",i=!1,l=RegExp.escape(t),u=RegExp.escape(o),c=/(D|S|\n|\r|[^DS\r\n]+)/,f=c.source;return f=(f=f.replace(/S/g,l)).replace(/D/g,u),c=new RegExp(f,"gm"),r.replace(c,function(r){if(!i)switch(s){case 0:if(r===t){n+=r,s=0;break}if(r===o){n+=r,s=1;break}if("\n"===r){d();break}if(/^\r$/.test(r))break;n+=r,s=3;break;case 1:if(r===o){n+=r,s=2;break}n+=r,s=1;break;case 2:var e=n.substr(n.length-1);if(r===o&&e===o){n+=r,s=1;break}if(r===t){n+=r,s=0;break}if("\n"===r){d();break}if("\r"===r)break;throw Error("CSVDataError: Illegal state [Row:"+a.state.rowNum+"]");case 3:if(r===t){n+=r,s=0;break}if("\n"===r){d();break}if("\r"===r)break;if(r===o)throw Error("CSVDataError: Illegal quote [Row:"+a.state.rowNum+"]");throw Error("CSVDataError: Illegal state [Row:"+a.state.rowNum+"]");default:throw Error("CSVDataError: Unknown state [Row:"+a.state.rowNum+"]")}}),""!==n&&d(),e}function d(){if(s=0,a.start&&a.state.rowNum=a.end&&(i=!0),a.state.rowNum++}},parseEntry:function(r,e){var a=e.separator,t=e.delimiter;e.state.rowNum||(e.state.rowNum=1),e.state.colNum||(e.state.colNum=1);var o=[],s=0,n="";function i(){if(void 0===e.onParseValue)o.push(n);else{var r=e.onParseValue(n,e.state);!1!==r&&o.push(r)}n="",s=0,e.state.colNum++}if(!e.match){var l=RegExp.escape(a),u=RegExp.escape(t),c=/(D|S|\n|\r|[^DS\r\n]+)/.source;c=(c=c.replace(/S/g,l)).replace(/D/g,u),e.match=new RegExp(c,"gm")}return r.replace(e.match,function(r){switch(s){case 0:if(r===a){n+="",i();break}if(r===t){s=1;break}if("\n"===r||"\r"===r)break;n+=r,s=3;break;case 1:if(r===t){s=2;break}n+=r,s=1;break;case 2:if(r===t){n+=r,s=1;break}if(r===a){i();break}if("\n"===r||"\r"===r)break;throw Error("CSVDataError: Illegal State [Row:"+e.state.rowNum+"][Col:"+e.state.colNum+"]");case 3:if(r===a){i();break}if("\n"===r||"\r"===r)break;if(r===t)throw Error("CSVDataError: Illegal Quote [Row:"+e.state.rowNum+"][Col:"+e.state.colNum+"]");throw Error("CSVDataError: Illegal Data [Row:"+e.state.rowNum+"][Col:"+e.state.colNum+"]");default:throw Error("CSVDataError: Unknown State [Row:"+e.state.rowNum+"][Col:"+e.state.colNum+"]")}}),i(),o}},helpers:{collectPropertyNames:function(r){var e=[],a=[],t=[];for(e in r)for(a in r[e])r[e].hasOwnProperty(a)&&t.indexOf(a)<0&&"function"!=typeof r[e][a]&&t.push(a);return t}},toArray:function(r,e,a){if(void 0!==e&&"function"==typeof e){if(void 0!==a)return console.error("You cannot 3 arguments with the 2nd argument being a function");a=e,e={}}e=void 0!==e?e:{};var t={};t.callback=void 0!==a&&"function"==typeof a&&a,t.separator="separator"in e?e.separator:p.csv.defaults.separator,t.delimiter="delimiter"in e?e.delimiter:p.csv.defaults.delimiter;var o=void 0!==e.state?e.state:{};e={delimiter:t.delimiter,separator:t.separator,onParseEntry:e.onParseEntry,onParseValue:e.onParseValue,state:o};var s=p.csv.parsers.parseEntry(r,e);if(!t.callback)return s;t.callback("",s)},toArrays:function(r,e,a){if(void 0!==e&&"function"==typeof e){if(void 0!==a)return console.error("You cannot 3 arguments with the 2nd argument being a function");a=e,e={}}e=void 0!==e?e:{};var t={};t.callback=void 0!==a&&"function"==typeof a&&a,t.separator="separator"in e?e.separator:p.csv.defaults.separator,t.delimiter="delimiter"in e?e.delimiter:p.csv.defaults.delimiter;var o=[];if(void 0!==(e={delimiter:t.delimiter,separator:t.separator,onPreParse:e.onPreParse,onParseEntry:e.onParseEntry,onParseValue:e.onParseValue,onPostParse:e.onPostParse,start:e.start,end:e.end,state:{rowNum:1,colNum:1}}).onPreParse&&(r=e.onPreParse(r,e.state)),o=p.csv.parsers.parse(r,e),void 0!==e.onPostParse&&(o=e.onPostParse(o,e.state)),!t.callback)return o;t.callback("",o)},toObjects:function(r,e,a){if(void 0!==e&&"function"==typeof e){if(void 0!==a)return console.error("You cannot 3 arguments with the 2nd argument being a function");a=e,e={}}e=void 0!==e?e:{};var t={};t.callback=void 0!==a&&"function"==typeof a&&a,t.separator="separator"in e?e.separator:p.csv.defaults.separator,t.delimiter="delimiter"in e?e.delimiter:p.csv.defaults.delimiter,t.headers="headers"in e?e.headers:p.csv.defaults.headers,e.start="start"in e?e.start:1,t.headers&&e.start++,e.end&&t.headers&&e.end++;var o,s=[];e={delimiter:t.delimiter,separator:t.separator,onPreParse:e.onPreParse,onParseEntry:e.onParseEntry,onParseValue:e.onParseValue,onPostParse:e.onPostParse,start:e.start,end:e.end,state:{rowNum:1,colNum:1},match:!1,transform:e.transform};var n={delimiter:t.delimiter,separator:t.separator,start:1,end:1,state:{rowNum:1,colNum:1},headers:!0};void 0!==e.onPreParse&&(r=e.onPreParse(r,e.state));var i=p.csv.parsers.splitLines(r,n),l=p.csv.toArray(i[0],n);o=p.csv.parsers.splitLines(r,e),e.state.colNum=1,e.state.rowNum=l?2:1;for(var u=0,c=o.length;u
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Folder Launcher
+
+
+
+
+
+
+
+
+
+
+
+
App
+
Folder
+
Move to
+
+
+
+
+
+
+
Add a new folder
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/folderlaunch/metadata.json b/apps/folderlaunch/metadata.json
index 0cf83abb4..0194fcf66 100644
--- a/apps/folderlaunch/metadata.json
+++ b/apps/folderlaunch/metadata.json
@@ -10,6 +10,7 @@
"BANGLEJS2"
],
"readme": "README.md",
+ "interface": "interface.html",
"storage": [
{
"name": "folderlaunch.app.js",
diff --git a/apps/followtherecipe/ChangeLog b/apps/followtherecipe/ChangeLog
index 9ba6a29bd..68ca62642 100644
--- a/apps/followtherecipe/ChangeLog
+++ b/apps/followtherecipe/ChangeLog
@@ -1 +1,2 @@
-0.01: New App
\ No newline at end of file
+0.01: New App
+0.02: Minor code improvements
diff --git a/apps/followtherecipe/app.js b/apps/followtherecipe/app.js
index 8238a6c07..056632e27 100644
--- a/apps/followtherecipe/app.js
+++ b/apps/followtherecipe/app.js
@@ -1,10 +1,7 @@
-const storage = require("Storage");
const settings = require("Storage").readJSON("followtherecipe.json");
-const locale = require('locale');
-var ENV = process.env;
+//const locale = require('locale');
var W = g.getWidth(), H = g.getHeight();
var screen = 0;
-var Layout = require("Layout");
let maxLenghtHorizontal = 16;
let maxLenghtvertical = 6;
diff --git a/apps/followtherecipe/metadata.json b/apps/followtherecipe/metadata.json
index 8ffeac2d2..0c1de0817 100644
--- a/apps/followtherecipe/metadata.json
+++ b/apps/followtherecipe/metadata.json
@@ -3,7 +3,7 @@
"name": "Follow The Recipe",
"shortName":"FTR",
"icon": "icon.png",
- "version": "0.01",
+ "version": "0.02",
"description": "Follow The Recipe (FTR) is a bangle.js app to follow a recipe step by step",
"type": "app",
"tags": "tool, tools, cook",
diff --git a/apps/fontall/ChangeLog b/apps/fontall/ChangeLog
new file mode 100644
index 000000000..5560f00bc
--- /dev/null
+++ b/apps/fontall/ChangeLog
@@ -0,0 +1 @@
+0.01: New App!
diff --git a/apps/fontall/README.md b/apps/fontall/README.md
new file mode 100644
index 000000000..9b53058d9
--- /dev/null
+++ b/apps/fontall/README.md
@@ -0,0 +1,22 @@
+# Fonts (all languages)
+
+This library provides an international font that can be used to display messages.
+
+The font is the 16px high [GNU Unifont](https://unifoundry.com/unifont/index.html).
+All characters from Unicode codepoint 32 up until codepoint 65535 (U+FFFF) are included here,
+which should be enough for most languages.
+
+**The font is 2MB and takes a while to upload** - if you don't require all the languages
+it provides, consider installing another Font library like [extended fonts](https://banglejs.com/apps/?id=fontsext)
+that contains just the characters you need instead.
+
+## Usage
+
+See [the BangleApps README file](https://github.com/espruino/BangleApps/blob/master/README.md#api-reference)
+for more information on fonts.
+
+
+## Recreating font.pbf
+
+* Go to `bin` directory
+* Run `./font_creator.js "All" ../apps/fontall/font.pbf`
\ No newline at end of file
diff --git a/apps/fontall/app.png b/apps/fontall/app.png
new file mode 100644
index 000000000..e029647b7
Binary files /dev/null and b/apps/fontall/app.png differ
diff --git a/apps/fontall/boot.js b/apps/fontall/boot.js
new file mode 100644
index 000000000..07b99570e
--- /dev/null
+++ b/apps/fontall/boot.js
@@ -0,0 +1 @@
+Graphics.prototype.setFontIntl = function() { return this.setFontPBF(require("Storage").read("fontall.pbf")); };
\ No newline at end of file
diff --git a/apps/fontall/font.pbf b/apps/fontall/font.pbf
new file mode 100644
index 000000000..2f5374987
Binary files /dev/null and b/apps/fontall/font.pbf differ
diff --git a/apps/fontall/lib.js b/apps/fontall/lib.js
new file mode 100644
index 000000000..8d5caf366
--- /dev/null
+++ b/apps/fontall/lib.js
@@ -0,0 +1,3 @@
+exports.getFont = (options) => {
+ return "Intl"; // placeholder for now - see https://github.com/espruino/BangleApps/issues/3109
+};
\ No newline at end of file
diff --git a/apps/fontall/metadata.json b/apps/fontall/metadata.json
new file mode 100644
index 000000000..b0a9e4466
--- /dev/null
+++ b/apps/fontall/metadata.json
@@ -0,0 +1,16 @@
+{ "id": "fontall",
+ "name": "Fonts (all languages)",
+ "version":"0.01",
+ "description": "Installs a font containing over 50,000 Unifont characters for Chinese, Japanese, Korean, Russian, and more. **Requires 2MB storage**",
+ "icon": "app.png",
+ "tags": "font",
+ "type": "module",
+ "provides_modules" : ["font"],
+ "supports" : ["BANGLEJS2"],
+ "readme": "README.md",
+ "storage": [
+ {"name":"font","url":"lib.js"},
+ {"name":"fontall.boot.js","url":"boot.js"},
+ {"name":"fontall.pbf","url":"font.pbf"}
+ ]
+}
diff --git a/apps/fontext/ChangeLog b/apps/fontext/ChangeLog
new file mode 100644
index 000000000..5560f00bc
--- /dev/null
+++ b/apps/fontext/ChangeLog
@@ -0,0 +1 @@
+0.01: New App!
diff --git a/apps/fontext/README.md b/apps/fontext/README.md
new file mode 100644
index 000000000..f8f99f3a4
--- /dev/null
+++ b/apps/fontext/README.md
@@ -0,0 +1,25 @@
+# Fonts (extended)
+
+This library provides an international font that can be used to display messages.
+
+The font is the 16px high [GNU Unifont](https://unifoundry.com/unifont/index.html).
+All characters from Unicode codepoint 32 up until codepoint 1103 (U+044F) are included here,
+which should be enough for [around 90% of languages](https://arxiv.org/pdf/1801.07779.pdf#page=5)
+but **not** Chinese/Japanese/Korean.
+
+The font is 20kb so is far more sensible than the [2MB all regions](https://banglejs.com/apps/?id=fontsall) font
+if you don't require non-latin languages.
+
+
+https://arxiv.org/pdf/1801.07779.pdf#page=5
+
+## Usage
+
+See [the BangleApps README file](https://github.com/espruino/BangleApps/blob/master/README.md#api-reference)
+for more information on fonts.
+
+
+## Recreating font.pbf
+
+* Go to `bin` directory
+* Run `./font_creator.js "Extended" ../apps/fontext/font.pbf`
\ No newline at end of file
diff --git a/apps/fontext/app.png b/apps/fontext/app.png
new file mode 100644
index 000000000..e029647b7
Binary files /dev/null and b/apps/fontext/app.png differ
diff --git a/apps/fontext/boot.js b/apps/fontext/boot.js
new file mode 100644
index 000000000..e52483e16
--- /dev/null
+++ b/apps/fontext/boot.js
@@ -0,0 +1 @@
+Graphics.prototype.setFontIntl = function() { return this.setFontPBF(require("Storage").read("fontext.pbf")); };
\ No newline at end of file
diff --git a/apps/fontext/font.pbf b/apps/fontext/font.pbf
new file mode 100644
index 000000000..1636df8e2
Binary files /dev/null and b/apps/fontext/font.pbf differ
diff --git a/apps/fontext/lib.js b/apps/fontext/lib.js
new file mode 100644
index 000000000..8d5caf366
--- /dev/null
+++ b/apps/fontext/lib.js
@@ -0,0 +1,3 @@
+exports.getFont = (options) => {
+ return "Intl"; // placeholder for now - see https://github.com/espruino/BangleApps/issues/3109
+};
\ No newline at end of file
diff --git a/apps/fontext/metadata.json b/apps/fontext/metadata.json
new file mode 100644
index 000000000..ddb5d2dc7
--- /dev/null
+++ b/apps/fontext/metadata.json
@@ -0,0 +1,17 @@
+{ "id": "fontext",
+ "name": "Fonts (150+ languages)",
+ "version":"0.01",
+ "description": "Installs a font containing 1000 Unifont characters, which should handle the majority of non-Chinese/Japanese/Korean languages (only 20kb)",
+ "icon": "app.png",
+ "tags": "font",
+ "type": "module",
+ "provides_modules" : ["font"],
+ "default" : true,
+ "supports" : ["BANGLEJS2"],
+ "readme": "README.md",
+ "storage": [
+ {"name":"font","url":"lib.js"},
+ {"name":"fontext.boot.js","url":"boot.js"},
+ {"name":"fontext.pbf","url":"font.pbf"}
+ ]
+}
diff --git a/apps/fontkorean/ChangeLog b/apps/fontkorean/ChangeLog
new file mode 100644
index 000000000..7b83706bf
--- /dev/null
+++ b/apps/fontkorean/ChangeLog
@@ -0,0 +1 @@
+0.01: First release
diff --git a/apps/fontkorean/README.md b/apps/fontkorean/README.md
new file mode 100644
index 000000000..6d9eecd45
--- /dev/null
+++ b/apps/fontkorean/README.md
@@ -0,0 +1,17 @@
+# Fonts (Korean)
+
+This library provides an Korean font that can be used to display messages.
+
+The font is the 16px high [GNU Unifont](https://unifoundry.com/unifont/index.html).
+Korean characters from Unicode codepoint 32-255, 0x1100-0x11FF, 0x3130-0x318F, 0xA960-0xA97F
+
+## Usage
+
+See [the BangleApps README file](https://github.com/espruino/BangleApps/blob/master/README.md#api-reference)
+for more information on fonts.
+
+
+## Recreating fontkorean.pbf
+
+* Go to `bin` directory
+* Run `./font_creator.js "Korean" ../apps/fontkorean/font.pbf`
diff --git a/apps/fontkorean/app.png b/apps/fontkorean/app.png
new file mode 100644
index 000000000..a4b02ea3a
Binary files /dev/null and b/apps/fontkorean/app.png differ
diff --git a/apps/fontkorean/boot.js b/apps/fontkorean/boot.js
new file mode 100644
index 000000000..5f3a24433
--- /dev/null
+++ b/apps/fontkorean/boot.js
@@ -0,0 +1 @@
+Graphics.prototype.setFontIntl = function() { return this.setFontPBF(require("Storage").read("fontkorean.pbf")); };
\ No newline at end of file
diff --git a/apps/fontkorean/font.pbf b/apps/fontkorean/font.pbf
new file mode 100644
index 000000000..f08545b85
Binary files /dev/null and b/apps/fontkorean/font.pbf differ
diff --git a/apps/fontkorean/lib.js b/apps/fontkorean/lib.js
new file mode 100644
index 000000000..bcb3b4bca
--- /dev/null
+++ b/apps/fontkorean/lib.js
@@ -0,0 +1,3 @@
+exports.getFont = (options) => {
+ return "Intl"; // placeholder for now - see https://github.com/espruino/BangleApps/issues/3109
+};
\ No newline at end of file
diff --git a/apps/fontkorean/metadata.json b/apps/fontkorean/metadata.json
new file mode 100644
index 000000000..700b12afd
--- /dev/null
+++ b/apps/fontkorean/metadata.json
@@ -0,0 +1,16 @@
+{ "id": "fontkorean",
+ "name": "Korean font",
+ "version":"0.01",
+ "description": "Installs a font data, Unifont characters for Korean **Requires 420 KB storage**",
+ "icon": "app.png",
+ "tags": "font",
+ "type": "module",
+ "provides_modules" : ["font"],
+ "supports" : ["BANGLEJS2"],
+ "readme": "README.md",
+ "storage": [
+ {"name":"font","url":"lib.js"},
+ {"name":"fontkorean.boot.js","url":"boot.js"},
+ {"name":"fontkorean.pbf","url":"font.pbf"}
+ ]
+}
diff --git a/apps/forge/ChangeLog b/apps/forge/ChangeLog
index 0c651c90b..ade90fc36 100644
--- a/apps/forge/ChangeLog
+++ b/apps/forge/ChangeLog
@@ -1,2 +1,3 @@
0.01: attempt to import
0.02: Make it possible for Fastload Utils to fastload into this app.
+0.03: Minor code improvements
diff --git a/apps/forge/forge.app.js b/apps/forge/forge.app.js
index b179fb540..5169a04e9 100644
--- a/apps/forge/forge.app.js
+++ b/apps/forge/forge.app.js
@@ -2,9 +2,9 @@
"Bangle.loadWidgets()"; // Facilitates fastloading to this app via Fastload Utils, while still not loading widgets on standard `load` calls.
-st = require('Storage');
+const st = require('Storage');
-l = /^a\..*\.js$/;
+let l = /^a\..*\.js$/;
//l = /.*\.js/;
l = st.list(l, {sf:false});
diff --git a/apps/forge/metadata.json b/apps/forge/metadata.json
index 6e13a4df3..43baca169 100644
--- a/apps/forge/metadata.json
+++ b/apps/forge/metadata.json
@@ -1,6 +1,6 @@
{ "id": "forge",
"name": "App Forge",
- "version":"0.02",
+ "version": "0.03",
"description": "Easy way to run development versions of your apps",
"icon": "app.png",
"readme": "README.md",
diff --git a/apps/ftclock/mkFourTwentyTz.js b/apps/ftclock/mkFourTwentyTz.js
index 4e7829aa3..7c6d25995 100644
--- a/apps/ftclock/mkFourTwentyTz.js
+++ b/apps/ftclock/mkFourTwentyTz.js
@@ -1,3 +1,5 @@
+/* This file is designed to be run on the desktop, not Bangle.js */
+/* eslint-env node */
let fs = require('fs');
let csv = require('csv');
@@ -30,7 +32,7 @@ fs.createReadStream(__dirname+'/country.csv')
} else {
country = countries[r[1]]; // e.g. United States
}
- zone = zones[r[0]] || { "name": `${city}, ${country}` };
+ let zone = zones[r[0]] || { "name": `${city}, ${country}` };
let starttime = parseInt(r[3] || "0"), // Bugger. They're feeding us blanks for UTC now
offs = parseInt(r[4]);
if (offs<0) {
@@ -43,15 +45,15 @@ fs.createReadStream(__dirname+'/country.csv')
zones[r[0]] = zone;
})
.on('end', () => {
- for (z in zones) {
- zone = zones[z];
+ for (let z in zones) {
+ let zone = zones[z];
if (zone.offs%60) continue; // One a dem funky timezones. Ignore.
- zonelist = offsdict[zone.offs] || [];
+ let zonelist = offsdict[zone.offs] || [];
zonelist.push(zone.name);
offsdict[zone.offs] = zonelist;
}
- offsets = [];
- for (o in offsdict) {
+ let offsets = [];
+ for (let o in offsdict) {
offsets.unshift(parseInt(o));
}
fs.open("fourTwentyTz.js","w", (err, fd) => {
@@ -67,7 +69,7 @@ fs.createReadStream(__dirname+'/country.csv')
fs.write(fd, ";\n", handleWrite);
fs.write(fd, "exports.timezones = function(offs) {\n", handleWrite);
fs.write(fd, " switch (offs) {\n", handleWrite);
- for (i=0; i
-
This tool allows you to update the firmware on Bangle.js 2 devices
+
This tool allows you to update the firmware on Bangle.js 2 devices
from within the App Loader.
@@ -32,7 +32,7 @@
bit of code that runs when Bangle.js starts, and it is able to update the
Bangle.js firmware. Normally you would update firmware via this Firmware
Updater app, but if for some reason Bangle.js will not boot, you can
- always use DFU to do the update manually.
+ always use DFU to do the update manually.
On DFU 2v19 and earlier, iOS devices could have issues updating firmware - 2v20 fixes this.
DFU is itself a bootloader, but here we're calling it DFU to avoid confusion
with the Bootloader app in the app loader (which prepares Bangle.js for running apps).
@@ -42,7 +42,7 @@
Advanced
Firmware updates via this tool work differently to the NRF Connect method mentioned on
- the Bangle.js 2 page. Firmware
+ the Bangle.js 2 page. Firmware
is uploaded to a file on the Bangle. Once complete the Bangle reboots and DFU copies
the new firmware into internal Storage.
In addition to the links above, you can upload a hex or zip file directly below. This file should be an .app_hex
@@ -58,6 +58,15 @@