mirror of https://github.com/espruino/BangleApps
Merge branch 'master' into runplus
commit
7386e0c78d
|
@ -3,3 +3,4 @@
|
||||||
0.03: Exit as first menu option, dont show decimal places for seconds
|
0.03: Exit as first menu option, dont show decimal places for seconds
|
||||||
0.04: Localisation, change Exit->Back to allow back-arrow to appear on 2v13 firmware
|
0.04: Localisation, change Exit->Back to allow back-arrow to appear on 2v13 firmware
|
||||||
0.05: Add max G values during recording, record actual G values and magnitude to CSV
|
0.05: Add max G values during recording, record actual G values and magnitude to CSV
|
||||||
|
0.06: Convert Yes/No On/Off in settings to checkboxes
|
||||||
|
|
|
@ -26,8 +26,7 @@ function showMenu() {
|
||||||
viewLogs();
|
viewLogs();
|
||||||
},
|
},
|
||||||
/*LANG*/"Log raw data" : {
|
/*LANG*/"Log raw data" : {
|
||||||
value : logRawData,
|
value : !!logRawData,
|
||||||
format : v => v?/*LANG*/"Yes":/*LANG*/"No",
|
|
||||||
onchange : v => { logRawData=v; }
|
onchange : v => { logRawData=v; }
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "accellog",
|
"id": "accellog",
|
||||||
"name": "Acceleration Logger",
|
"name": "Acceleration Logger",
|
||||||
"shortName": "Accel Log",
|
"shortName": "Accel Log",
|
||||||
"version": "0.05",
|
"version": "0.06",
|
||||||
"description": "Logs XYZ acceleration data to a CSV file that can be downloaded to your PC",
|
"description": "Logs XYZ acceleration data to a CSV file that can be downloaded to your PC",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "outdoor",
|
"tags": "outdoor",
|
||||||
|
|
|
@ -8,3 +8,4 @@
|
||||||
0.08: Use default Bangle formatter for booleans
|
0.08: Use default Bangle formatter for booleans
|
||||||
0.09: New app screen (instead of showing settings or the alert) and some optimisations
|
0.09: New app screen (instead of showing settings or the alert) and some optimisations
|
||||||
0.10: Add software back button via setUI
|
0.10: Add software back button via setUI
|
||||||
|
0.11: Add setting to unlock screen
|
||||||
|
|
|
@ -26,6 +26,12 @@
|
||||||
if (!(storage.readJSON('setting.json', 1) || {}).quiet) {
|
if (!(storage.readJSON('setting.json', 1) || {}).quiet) {
|
||||||
Bangle.buzz(400);
|
Bangle.buzz(400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((storage.readJSON('activityreminder.s.json', 1) || {}).unlock) {
|
||||||
|
Bangle.setLocked(false);
|
||||||
|
Bangle.setLCDPower(1);
|
||||||
|
}
|
||||||
|
|
||||||
setTimeout(load, 20000);
|
setTimeout(load, 20000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"name": "Activity Reminder",
|
"name": "Activity Reminder",
|
||||||
"shortName":"Activity Reminder",
|
"shortName":"Activity Reminder",
|
||||||
"description": "A reminder to take short walks for the ones with a sedentary lifestyle",
|
"description": "A reminder to take short walks for the ones with a sedentary lifestyle",
|
||||||
"version":"0.10",
|
"version":"0.11",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "app",
|
"type": "app",
|
||||||
"tags": "tool,activity",
|
"tags": "tool,activity",
|
||||||
|
|
|
@ -75,7 +75,14 @@
|
||||||
settings.tempThreshold = v;
|
settings.tempThreshold = v;
|
||||||
activityreminder.writeSettings(settings);
|
activityreminder.writeSettings(settings);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
'Unlock on alarm': {
|
||||||
|
value: !!settings.unlock,
|
||||||
|
onchange: v => {
|
||||||
|
settings.unlock = v;
|
||||||
|
activityreminder.writeSettings(settings);
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return mainMenu;
|
return mainMenu;
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
var mainmenu = {
|
var mainmenu = {
|
||||||
"" : { "title" : "Android" },
|
"" : { "title" : "Android" },
|
||||||
"< Back" : back,
|
"< Back" : back,
|
||||||
/*LANG*/"Connected" : { value : NRF.getSecurityStatus().connected?"Yes":"No" },
|
/*LANG*/"Connected" : { value : NRF.getSecurityStatus().connected?/*LANG*/"Yes":/*LANG*/"No" },
|
||||||
/*LANG*/"Find Phone" : () => E.showMenu({
|
/*LANG*/"Find Phone" : () => E.showMenu({
|
||||||
"" : { "title" : /*LANG*/"Find Phone" },
|
"" : { "title" : /*LANG*/"Find Phone" },
|
||||||
"< Back" : ()=>E.showMenu(mainmenu),
|
"< Back" : ()=>E.showMenu(mainmenu),
|
||||||
|
|
|
@ -10,6 +10,12 @@ Standard # of drag handlers: 0-10 (Default: 0, must be changed for backswipe to
|
||||||
|
|
||||||
Standard # of handlers settings are used to fine tune when backswipe should trigger the back function. E.g. when using a keyboard that works on drags, we don't want the backswipe to trigger when we just wanted to select a letter. This might not be able to cover all cases however.
|
Standard # of handlers settings are used to fine tune when backswipe should trigger the back function. E.g. when using a keyboard that works on drags, we don't want the backswipe to trigger when we just wanted to select a letter. This might not be able to cover all cases however.
|
||||||
|
|
||||||
|
To get an indication for standard # of handlers `Bangle["#onswipe"]` and `Bangle["#ondrag"]` can be entered in the [Espruino Web IDE](https://www.espruino.com/ide) console field. They return `undefined` if no handler is active, a function if one is active, or a list of functions if multiple are active. Calling this on the clock app is a good start.
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
- Possibly add option to tweak standard # of handlers on per app basis.
|
||||||
|
|
||||||
## Creator
|
## Creator
|
||||||
Kedlub
|
Kedlub
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: New App!
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwwhC/AH4ARkQAHBwsBiIACiAHBgQXIkAXJiIuKGAwWEC4cjmYABn//AAMyC63yC653FC6HwC5aQBC5ybIC44WChGAWxMgC44rCxGIZxYXFIoYXBGAQNCAAQXILYYXBGAUDBoK0EC5AsBC4QwEC5wAEC853BhAWDI6CPCFwp3OX4ouCC8xHXCAJ3VX94XCwBHVGIiPTU4oNCAAQWBX5gDBgQRCAAoXGGAUIFwQXHkAXHJIgABCw4IBC5sAiIAEiAgHAAQXLHBAYIC+6wJQYIADgIXGGBJ3FC4iOBAH4A/ACAA=="))
|
|
@ -0,0 +1,41 @@
|
||||||
|
E.showMessage("Scanning...");
|
||||||
|
var devices = [];
|
||||||
|
|
||||||
|
setInterval(function() {
|
||||||
|
NRF.findDevices(function(devs) {
|
||||||
|
devs.forEach(dev=>{
|
||||||
|
var existing = devices.find(d=>d.id==dev.id);
|
||||||
|
if (existing) {
|
||||||
|
existing.timeout = 0;
|
||||||
|
existing.rssi = (existing.rssi*3 + dev.rssi)/4;
|
||||||
|
} else {
|
||||||
|
dev.timeout = 0;
|
||||||
|
dev.new = 0;
|
||||||
|
devices.push(dev);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
devices.forEach(d=>{d.timeout++;d.new++});
|
||||||
|
devices = devices.filter(dev=>dev.timeout<8);
|
||||||
|
devices.sort((a,b)=>b.rssi - a.rssi);
|
||||||
|
g.clear(1).setFont("12x20");
|
||||||
|
var wasNew = false;
|
||||||
|
devices.forEach((d,y)=>{
|
||||||
|
y*=20;
|
||||||
|
var n = d.name;
|
||||||
|
if (!n) n=d.id.substr(0,22);
|
||||||
|
if (d.new<4) {
|
||||||
|
g.fillRect(0,y,g.getWidth(),y+19);
|
||||||
|
g.setColor(g.theme.bg);
|
||||||
|
if (d.rssi > -70) wasNew = true;
|
||||||
|
} else {
|
||||||
|
g.setColor(g.theme.fg);
|
||||||
|
}
|
||||||
|
g.setFontAlign(-1,-1);
|
||||||
|
g.drawString(n,0,y);
|
||||||
|
g.setFontAlign(1,-1);
|
||||||
|
g.drawString(0|d.rssi,g.getWidth()-1,y);
|
||||||
|
});
|
||||||
|
g.flip();
|
||||||
|
Bangle.setLCDBrightness(wasNew);
|
||||||
|
}, 1200);
|
||||||
|
}, 1500);
|
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
|
@ -0,0 +1,14 @@
|
||||||
|
{ "id": "blescanner",
|
||||||
|
"name": "BLE Scanner",
|
||||||
|
"shortName":"BLE Scan",
|
||||||
|
"version":"0.01",
|
||||||
|
"description": "Scans for bluetooth devices nearby and shows their names on the screen ordered by signal strength. The most recently discovered items are highlighted.",
|
||||||
|
"icon": "app.png",
|
||||||
|
"screenshots" : [ { "url":"screenshot.png" } ],
|
||||||
|
"tags": "tool,bluetooth",
|
||||||
|
"supports" : ["BANGLEJS2"],
|
||||||
|
"storage": [
|
||||||
|
{"name":"blescanner.app.js","url":"app.js"},
|
||||||
|
{"name":"blescanner.img","url":"app-icon.js","evaluate":true}
|
||||||
|
]
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
|
@ -0,0 +1,16 @@
|
||||||
|
# Bluetooth Advert
|
||||||
|
|
||||||
|
This app advertises and exports (over Bluetooth) live data from the bangle's sensors:
|
||||||
|
|
||||||
|
- Heart Rate
|
||||||
|
- Accelerometer readings
|
||||||
|
- Pressure
|
||||||
|
- GPS information
|
||||||
|
- Magnetic flux
|
||||||
|
|
||||||
|
Swipe in any direction to access settings, and tap a setting to toggle it.
|
||||||
|
Hit back to return to the details screen, which shows sensor data being exported.
|
||||||
|
|
||||||
|
# TypeScript
|
||||||
|
|
||||||
|
This app is written in TypeScript, see [typescript/README.md](/typescript/README.md) for more info
|
|
@ -1,15 +1,5 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
var __assign = (this && this.__assign) || function () {
|
var __assign = Object.assign;
|
||||||
__assign = Object.assign || function(t) {
|
|
||||||
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
||||||
s = arguments[i];
|
|
||||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
||||||
t[p] = s[p];
|
|
||||||
}
|
|
||||||
return t;
|
|
||||||
};
|
|
||||||
return __assign.apply(this, arguments);
|
|
||||||
};
|
|
||||||
var Layout = require("Layout");
|
var Layout = require("Layout");
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// ts helpers:
|
||||||
|
const __assign = Object.assign;
|
||||||
|
|
||||||
const Layout = require("Layout") as Layout_.Layout;
|
const Layout = require("Layout") as Layout_.Layout;
|
||||||
|
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
|
|
|
@ -96,7 +96,7 @@ function draw(){
|
||||||
if (!isNaN(bt.battery)) layout.btBattery.label = bt.battery + "%";
|
if (!isNaN(bt.battery)) layout.btBattery.label = bt.battery + "%";
|
||||||
if (bt.rr) layout.btRR.label = bt.rr.join(",");
|
if (bt.rr) layout.btRR.label = bt.rr.join(",");
|
||||||
if (!isNaN(bt.location)) layout.btLocation.label = BODY_LOCS[bt.location];
|
if (!isNaN(bt.location)) layout.btLocation.label = BODY_LOCS[bt.location];
|
||||||
if (bt.contact !== undefined) layout.btContact.label = bt.contact ? "Yes":"No";
|
if (bt.contact !== undefined) layout.btContact.label = bt.contact ? /*LANG*/"Yes":/*LANG*/"No";
|
||||||
if (!isNaN(bt.energy)) layout.btEnergy.label = bt.energy.toFixed(0) + "kJ";
|
if (!isNaN(bt.energy)) layout.btEnergy.label = bt.energy.toFixed(0) + "kJ";
|
||||||
} else {
|
} else {
|
||||||
layout.bt.label = "--";
|
layout.bt.label = "--";
|
||||||
|
|
|
@ -9,3 +9,7 @@ Tap to start, tap again to pause. Tap again to restart
|
||||||
## Requests
|
## Requests
|
||||||
|
|
||||||
[Contact Rob](https://www.github.com/bobrippling) for features/bugs
|
[Contact Rob](https://www.github.com/bobrippling) for features/bugs
|
||||||
|
|
||||||
|
# TypeScript
|
||||||
|
|
||||||
|
This app is written in TypeScript, see [typescript/README.md](/typescript/README.md) for more info
|
||||||
|
|
|
@ -4,3 +4,4 @@
|
||||||
0.04: Use default Bangle formatter for booleans
|
0.04: Use default Bangle formatter for booleans
|
||||||
0.05: Improved colors (connected vs disconnected)
|
0.05: Improved colors (connected vs disconnected)
|
||||||
0.06: Tell clock widgets to hide.
|
0.06: Tell clock widgets to hide.
|
||||||
|
0.07: Convert Yes/No On/Off in settings to checkboxes
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "clockcal",
|
"id": "clockcal",
|
||||||
"name": "Clock & Calendar",
|
"name": "Clock & Calendar",
|
||||||
"version": "0.06",
|
"version": "0.07",
|
||||||
"description": "Clock with Calendar",
|
"description": "Clock with Calendar",
|
||||||
"readme":"README.md",
|
"readme":"README.md",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
|
|
|
@ -106,18 +106,11 @@
|
||||||
writeSettings();
|
writeSettings();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'Load deafauls?': {
|
'Load defaults': () => {
|
||||||
value: 0,
|
settings = defaults;
|
||||||
min: 0, max: 1,
|
writeSettings();
|
||||||
format: v => ["No", "Yes"][v],
|
load();
|
||||||
onchange: v => {
|
}
|
||||||
if (v == 1) {
|
|
||||||
settings = defaults;
|
|
||||||
writeSettings();
|
|
||||||
load();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
// Show the menu
|
// Show the menu
|
||||||
E.showMenu(menu);
|
E.showMenu(menu);
|
||||||
|
|
|
@ -6,3 +6,4 @@
|
||||||
0.06: Now read wheel rev as well as cadence sensor
|
0.06: Now read wheel rev as well as cadence sensor
|
||||||
Improve connection code
|
Improve connection code
|
||||||
0.07: Make Bangle.js 2 compatible
|
0.07: Make Bangle.js 2 compatible
|
||||||
|
0.08: Convert Yes/No On/Off in settings to checkboxes
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "cscsensor",
|
"id": "cscsensor",
|
||||||
"name": "Cycling speed sensor",
|
"name": "Cycling speed sensor",
|
||||||
"shortName": "CSCSensor",
|
"shortName": "CSCSensor",
|
||||||
"version": "0.07",
|
"version": "0.08",
|
||||||
"description": "Read BLE enabled cycling speed and cadence sensor and display readings on watch",
|
"description": "Read BLE enabled cycling speed and cadence sensor and display readings on watch",
|
||||||
"icon": "icons8-cycling-48.png",
|
"icon": "icons8-cycling-48.png",
|
||||||
"tags": "outdoors,exercise,ble,bluetooth",
|
"tags": "outdoors,exercise,ble,bluetooth",
|
||||||
|
|
|
@ -23,17 +23,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const menu = {
|
const menu = {
|
||||||
'': { 'title': 'Cycle speed sensor' },
|
'': { 'title': /*LANG*/'Cycle speed sensor' },
|
||||||
'< Back': back,
|
'< Back': back,
|
||||||
'Wheel circ.(mm)': {
|
/*LANG*/'Wheel circ.(mm)': {
|
||||||
value: s.wheelcirc,
|
value: s.wheelcirc,
|
||||||
min: 800,
|
min: 800,
|
||||||
max: 2400,
|
max: 2400,
|
||||||
step: 5,
|
step: 5,
|
||||||
onchange: save('wheelcirc'),
|
onchange: save('wheelcirc'),
|
||||||
},
|
},
|
||||||
'Reset total distance': function() {
|
/*LANG*/'Reset total distance': function() {
|
||||||
E.showPrompt("Zero total distance?", {buttons: {"No":false, "Yes":true}}).then(function(v) {
|
E.showPrompt(/*LANG*/"Zero total distance?", {buttons: {/*LANG*/"No":false, /*LANG*/"Yes":true}}).then(function(v) {
|
||||||
if (v) {
|
if (v) {
|
||||||
s['totaldist'] = 0;
|
s['totaldist'] = 0;
|
||||||
storage.write(SETTINGS_FILE, s);
|
storage.write(SETTINGS_FILE, s);
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
0.01: New App!
|
0.01: New App!
|
||||||
0.02: Add lightning
|
0.02: Add lightning
|
||||||
|
0.03: Convert Yes/No On/Off in settings to checkboxes
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{ "id": "f9lander",
|
{ "id": "f9lander",
|
||||||
"name": "Falcon9 Lander",
|
"name": "Falcon9 Lander",
|
||||||
"shortName":"F9lander",
|
"shortName":"F9lander",
|
||||||
"version":"0.02",
|
"version":"0.03",
|
||||||
"description": "Land a rocket booster",
|
"description": "Land a rocket booster",
|
||||||
"icon": "f9lander.png",
|
"icon": "f9lander.png",
|
||||||
"screenshots" : [ { "url":"f9lander_screenshot1.png" }, { "url":"f9lander_screenshot2.png" }, { "url":"f9lander_screenshot3.png" }],
|
"screenshots" : [ { "url":"f9lander_screenshot1.png" }, { "url":"f9lander_screenshot2.png" }, { "url":"f9lander_screenshot3.png" }],
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
/**
|
/**
|
||||||
* @param {function} back Use back() to return to settings menu
|
* @param {function} back Use back() to return to settings menu
|
||||||
*/
|
*/
|
||||||
const boolFormat = v => v ? /*LANG*/"On" : /*LANG*/"Off";
|
|
||||||
(function(back) {
|
(function(back) {
|
||||||
const SETTINGS_FILE = 'f9settings.json'
|
const SETTINGS_FILE = 'f9settings.json'
|
||||||
// initialize with default settings...
|
// initialize with default settings...
|
||||||
|
@ -27,8 +26,7 @@ const boolFormat = v => v ? /*LANG*/"On" : /*LANG*/"Off";
|
||||||
'': { 'title': 'OpenWind' },
|
'': { 'title': 'OpenWind' },
|
||||||
'< Back': back,
|
'< Back': back,
|
||||||
'Lightning': {
|
'Lightning': {
|
||||||
value: settings.lightning,
|
value: !!settings.lightning,
|
||||||
format: boolFormat,
|
|
||||||
onchange: save('lightning'),
|
onchange: save('lightning'),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ var m;
|
||||||
var files;
|
var files;
|
||||||
|
|
||||||
function delete_file(fn) {
|
function delete_file(fn) {
|
||||||
E.showPrompt("Delete\n"+fn+"?", {buttons: {"No":false, "Yes":true}}).then(function(v) {
|
E.showPrompt(/*LANG*/"Delete\n"+fn+"?", {buttons: {/*LANG*/"No":false, /*LANG*/"Yes":true}}).then(function(v) {
|
||||||
if (v) {
|
if (v) {
|
||||||
if (fn.charCodeAt(fn.length-1)==1) {
|
if (fn.charCodeAt(fn.length-1)==1) {
|
||||||
var fh = STOR.open(fn.substr(0, fn.length-1), "r");
|
var fh = STOR.open(fn.substr(0, fn.length-1), "r");
|
||||||
|
|
|
@ -24,13 +24,13 @@
|
||||||
var mainmenu = {
|
var mainmenu = {
|
||||||
"" : { "title" : "Gadgetbridge" },
|
"" : { "title" : "Gadgetbridge" },
|
||||||
"< Back" : back,
|
"< Back" : back,
|
||||||
"Connected" : { value : NRF.getSecurityStatus().connected?"Yes":"No" },
|
/*LANG*/"Connected" : { value : NRF.getSecurityStatus().connected?/*LANG*/"Yes":/*LANG*/"No" },
|
||||||
"Show Icon" : {
|
/*LANG*/"Show Icon" : {
|
||||||
value: settings().showIcon,
|
value: settings().showIcon,
|
||||||
onchange: setIcon
|
onchange: setIcon
|
||||||
},
|
},
|
||||||
"Find Phone" : function() { E.showMenu(findPhone); },
|
/*LANG*/"Find Phone" : function() { E.showMenu(findPhone); },
|
||||||
"Record HRM" : {
|
/*LANG*/"Record HRM" : {
|
||||||
value: !!settings().hrm,
|
value: !!settings().hrm,
|
||||||
onchange: v => updateSetting('hrm', v)
|
onchange: v => updateSetting('hrm', v)
|
||||||
}
|
}
|
||||||
|
@ -38,8 +38,8 @@
|
||||||
|
|
||||||
var findPhone = {
|
var findPhone = {
|
||||||
"" : { "title" : "-- Find Phone --" },
|
"" : { "title" : "-- Find Phone --" },
|
||||||
"On" : _=>gb({t:"findPhone",n:true}),
|
/*LANG*/"On" : _=>gb({t:"findPhone",n:true}),
|
||||||
"Off" : _=>gb({t:"findPhone",n:false}),
|
/*LANG*/"Off" : _=>gb({t:"findPhone",n:false}),
|
||||||
"< Back" : function() { E.showMenu(mainmenu); },
|
"< Back" : function() { E.showMenu(mainmenu); },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -73,3 +73,5 @@
|
||||||
* Display current and next segment in red so that you know where to go.
|
* Display current and next segment in red so that you know where to go.
|
||||||
* Avoid angles flickering at low speed at the cost of less refresh.
|
* Avoid angles flickering at low speed at the cost of less refresh.
|
||||||
* Splash screen while waiting for gps signal.
|
* Splash screen while waiting for gps signal.
|
||||||
|
|
||||||
|
0.17: Convert Yes/No On/Off in settings to checkboxes
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "gipy",
|
"id": "gipy",
|
||||||
"name": "Gipy",
|
"name": "Gipy",
|
||||||
"shortName": "Gipy",
|
"shortName": "Gipy",
|
||||||
"version": "0.16",
|
"version": "0.17",
|
||||||
"description": "Follow gpx files using the gps. Don't get lost in your bike trips and hikes.",
|
"description": "Follow gpx files using the gps. Don't get lost in your bike trips and hikes.",
|
||||||
"allow_emulator":false,
|
"allow_emulator":false,
|
||||||
"icon": "gipy.png",
|
"icon": "gipy.png",
|
||||||
|
|
|
@ -19,8 +19,7 @@
|
||||||
"< Back": () => back(),
|
"< Back": () => back(),
|
||||||
"keep gps alive": {
|
"keep gps alive": {
|
||||||
value: !!settings.keep_gps_alive, // !! converts undefined to false
|
value: !!settings.keep_gps_alive, // !! converts undefined to false
|
||||||
format: (v) => (v ? "Yes" : "No"),
|
onchange: v => {
|
||||||
onchange: (v) => {
|
|
||||||
settings.keep_gps_alive = v;
|
settings.keep_gps_alive = v;
|
||||||
writeSettings();
|
writeSettings();
|
||||||
},
|
},
|
||||||
|
|
|
@ -46,8 +46,8 @@ var mainMenu = {
|
||||||
"Reset Homework": function() {
|
"Reset Homework": function() {
|
||||||
E.showPrompt("Are you sure you want to delete homework.txt?", {
|
E.showPrompt("Are you sure you want to delete homework.txt?", {
|
||||||
buttons: {
|
buttons: {
|
||||||
"No": false,
|
/*LANG*/"No": false,
|
||||||
"Yes": true
|
/*LANG*/"Yes": true
|
||||||
}
|
}
|
||||||
}).then(function(v) {
|
}).then(function(v) {
|
||||||
if (v) {
|
if (v) {
|
||||||
|
|
|
@ -21,11 +21,11 @@ var screens = [
|
||||||
name: "Hardware",
|
name: "Hardware",
|
||||||
items: [
|
items: [
|
||||||
{name: "Battery", fun: () => E.getBattery() + "%"},
|
{name: "Battery", fun: () => E.getBattery() + "%"},
|
||||||
{name: "Charge?", fun: () => Bangle.isCharging() ? "Yes" : "No"},
|
{name: "Charge?", fun: () => Bangle.isCharging() ? /*LANG*/"Yes" : /*LANG*/"No"},
|
||||||
{name: "TempInt.", fun: () => locale.temp(parseInt(E.getTemperature()))},
|
{name: "TempInt.", fun: () => locale.temp(parseInt(E.getTemperature()))},
|
||||||
{name: "Bluetooth", fun: () => NRF.getSecurityStatus().connected ? "Conn" : "NoConn"},
|
{name: "Bluetooth", fun: () => NRF.getSecurityStatus().connected ? /*LANG*/"Conn" : /*LANG*/"NoConn"},
|
||||||
{name: "GPS", fun: () => Bangle.isGPSOn() ? "On" : "Off"},
|
{name: "GPS", fun: () => Bangle.isGPSOn() ? /*LANG*/"On" : /*LANG*/"Off"},
|
||||||
{name: "Compass", fun: () => Bangle.isCompassOn() ? "On" : "Off"},
|
{name: "Compass", fun: () => Bangle.isCompassOn() ? /*LANG*/"On" : /*LANG*/"Off"},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
0.01: New App!
|
0.01: New App!
|
||||||
0.02: Introduced settings to customize the layout and functionality of the keyboard.
|
0.02: Introduced settings to customize the layout and functionality of the keyboard.
|
||||||
|
0.03: Convert Yes/No On/Off in settings to checkboxes
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{ "id": "kbtouch",
|
{ "id": "kbtouch",
|
||||||
"name": "Touch keyboard",
|
"name": "Touch keyboard",
|
||||||
"version":"0.02",
|
"version":"0.03",
|
||||||
"description": "A library for text input via onscreen keyboard",
|
"description": "A library for text input via onscreen keyboard",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type":"textinput",
|
"type":"textinput",
|
||||||
|
|
|
@ -25,22 +25,16 @@
|
||||||
onchange: v => updateSetting("textSize", v)
|
onchange: v => updateSetting("textSize", v)
|
||||||
},
|
},
|
||||||
/*LANG*/'Offset keyboard': {
|
/*LANG*/'Offset keyboard': {
|
||||||
value: settings().offsetKeyboard,
|
value: !!settings().offsetKeyboard,
|
||||||
min: 0, max: 1,
|
onchange: v => updateSetting("offsetKeyboard", v?1:0)
|
||||||
format: v => [/*LANG*/"No",/*LANG*/"Yes"][v],
|
|
||||||
onchange: v => updateSetting("offsetKeyboard", v)
|
|
||||||
},
|
},
|
||||||
/*LANG*/'Loop around': {
|
/*LANG*/'Loop around': {
|
||||||
value: settings().loopAround,
|
value: !!settings().loopAround,
|
||||||
min: 0, max: 1,
|
onchange: v => updateSetting("loopAround", v?1:0)
|
||||||
format: v => [/*LANG*/"No",/*LANG*/"Yes"][v],
|
|
||||||
onchange: v => updateSetting("loopAround", v)
|
|
||||||
},
|
},
|
||||||
/*LANG*/'One-to-one input and release to select': {
|
/*LANG*/'One-to-one input and release to select': {
|
||||||
value: settings().oneToOne,
|
value: !!settings().oneToOne,
|
||||||
min: 0, max: 1,
|
onchange: v => updateSetting("oneToOne", v?1:0)
|
||||||
format: v => [/*LANG*/"No",/*LANG*/"Yes"][v],
|
|
||||||
onchange: v => updateSetting("oneToOne", v)
|
|
||||||
},
|
},
|
||||||
/*LANG*/'Speed scaling': {
|
/*LANG*/'Speed scaling': {
|
||||||
value: settings().speedScaling,
|
value: settings().speedScaling,
|
||||||
|
@ -49,10 +43,8 @@
|
||||||
onchange: v => updateSetting("speedScaling", v)
|
onchange: v => updateSetting("speedScaling", v)
|
||||||
}
|
}
|
||||||
///*LANG*/'Release to select': {
|
///*LANG*/'Release to select': {
|
||||||
// value: 1|settings().fontSize,
|
// value: !!(1|settings().fontSize),
|
||||||
// min: 0, max: 1,
|
// onchange: v => updateSetting("releaseToSelect", v?1:0)
|
||||||
// format: v => [/*LANG*/"No",/*LANG*/"Yes"][v],
|
|
||||||
// onchange: v => updateSetting("releaseToSelect", v)
|
|
||||||
//}
|
//}
|
||||||
};
|
};
|
||||||
E.showMenu(mainmenu);
|
E.showMenu(mainmenu);
|
||||||
|
|
|
@ -73,7 +73,7 @@ let lastTemp = 0;
|
||||||
|
|
||||||
const phone = {
|
const phone = {
|
||||||
get status() {
|
get status() {
|
||||||
return NRF.getSecurityStatus().connected ? "Yes" : "No";
|
return NRF.getSecurityStatus().connected ? /*LANG*/"Yes" : /*LANG*/"No";
|
||||||
},
|
},
|
||||||
message: null,
|
message: null,
|
||||||
messageTimeout: null,
|
messageTimeout: null,
|
||||||
|
|
|
@ -87,3 +87,4 @@
|
||||||
0.62: Remove '.show' field, tidyup and fix .open if fast load not enabled
|
0.62: Remove '.show' field, tidyup and fix .open if fast load not enabled
|
||||||
0.63: Fix messages app loading on clock without fast load
|
0.63: Fix messages app loading on clock without fast load
|
||||||
0.64: Ensure we don't get 'undefined' as the message body
|
0.64: Ensure we don't get 'undefined' as the message body
|
||||||
|
0.65: Make sure messages are saved if not in the clock app (fix #2460)
|
||||||
|
|
|
@ -29,7 +29,7 @@ exports.listener = function(type, msg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const appSettings = require("Storage").readJSON("messages.settings.json", 1) || {};
|
const appSettings = require("Storage").readJSON("messages.settings.json", 1) || {};
|
||||||
let loadMessages = (Bangle.CLOCK || event.important); // should we load the messages app?
|
let loadMessages = (Bangle.CLOCK || msg.important); // should we load the messages app?
|
||||||
if (type==="music") {
|
if (type==="music") {
|
||||||
if (Bangle.CLOCK && msg.state && msg.title && appSettings.openMusic) loadMessages = true;
|
if (Bangle.CLOCK && msg.state && msg.title && appSettings.openMusic) loadMessages = true;
|
||||||
else return;
|
else return;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "messagegui",
|
"id": "messagegui",
|
||||||
"name": "Message UI",
|
"name": "Message UI",
|
||||||
"shortName": "Messages",
|
"shortName": "Messages",
|
||||||
"version": "0.64",
|
"version": "0.65",
|
||||||
"description": "Default app to display notifications from iOS and Gadgetbridge/Android",
|
"description": "Default app to display notifications from iOS and Gadgetbridge/Android",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "app",
|
"type": "app",
|
||||||
|
|
|
@ -3,3 +3,4 @@
|
||||||
0.03: Fix icons broken in 0v02 (#2386)
|
0.03: Fix icons broken in 0v02 (#2386)
|
||||||
Store all icons in a separate binary file (much faster lookup)
|
Store all icons in a separate binary file (much faster lookup)
|
||||||
0.04: Add message icon for 'clock'
|
0.04: Add message icon for 'clock'
|
||||||
|
0.05: Add message icon for 'jira'
|
||||||
|
|
Binary file not shown.
|
@ -87,6 +87,7 @@ exports.getColor = function(msg,options) {
|
||||||
if (st.iconColorMode == 'mono') return options.default;
|
if (st.iconColorMode == 'mono') return options.default;
|
||||||
const s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase();
|
const s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase();
|
||||||
return {
|
return {
|
||||||
|
// This file is generated by /icons/generate.js. If you need to modify its content, you should do it there instead.
|
||||||
// generic colors, using B2-safe colors
|
// generic colors, using B2-safe colors
|
||||||
// DO NOT USE BLACK OR WHITE HERE, just leave the declaration out and then the theme's fg color will be used
|
// DO NOT USE BLACK OR WHITE HERE, just leave the declaration out and then the theme's fg color will be used
|
||||||
"airbnb": "#ff385c", // https://news.airbnb.com/media-assets/category/brand/
|
"airbnb": "#ff385c", // https://news.airbnb.com/media-assets/category/brand/
|
||||||
|
@ -107,6 +108,7 @@ exports.getColor = function(msg,options) {
|
||||||
"google home": "#fbbc05",
|
"google home": "#fbbc05",
|
||||||
// "home assistant": "#41bdf5", // ha-blue is #41bdf5, but that's the background
|
// "home assistant": "#41bdf5", // ha-blue is #41bdf5, but that's the background
|
||||||
"instagram": "#ff0069", // https://about.instagram.com/brand/gradient
|
"instagram": "#ff0069", // https://about.instagram.com/brand/gradient
|
||||||
|
"jira": "#0052cc", //https://atlassian.design/resources/logo-library
|
||||||
"lieferando": "#ff8000",
|
"lieferando": "#ff8000",
|
||||||
"linkedin": "#0a66c2", // https://brand.linkedin.com/
|
"linkedin": "#0a66c2", // https://brand.linkedin.com/
|
||||||
"messenger": "#0078ff",
|
"messenger": "#0078ff",
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
{ "app":"google play store", "icon":"google play store.png" },
|
{ "app":"google play store", "icon":"google play store.png" },
|
||||||
{ "app":"home assistant", "icon":"home assistant.png" },
|
{ "app":"home assistant", "icon":"home assistant.png" },
|
||||||
{ "app":"instagram", "icon":"instagram.png" },
|
{ "app":"instagram", "icon":"instagram.png" },
|
||||||
|
{ "app":"jira", "icon":"jira.png" },
|
||||||
{ "app":"kalender", "icon":"kalender.png" },
|
{ "app":"kalender", "icon":"kalender.png" },
|
||||||
{ "app":"keep notes", "icon":"google keep.png" },
|
{ "app":"keep notes", "icon":"google keep.png" },
|
||||||
{ "app":"lieferando", "icon":"lieferando.png" },
|
{ "app":"lieferando", "icon":"lieferando.png" },
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 224 B |
|
@ -2,7 +2,7 @@ exports.getImage = function(msg) {
|
||||||
if (msg.img) return atob(msg.img);
|
if (msg.img) return atob(msg.img);
|
||||||
let s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase();
|
let s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase();
|
||||||
if (msg.id=="music") s="music";
|
if (msg.id=="music") s="music";
|
||||||
let match = ",default|0,airbnb|1,alarm|2,alarmclockreceiver|2,amazon shopping|3,bibel|4,bitwarden|5,1password|5,lastpass|5,dashlane|5,bring|6,calendar|7,etar|7,chat|8,chrome|9,clock|2,corona-warn|10,bmo|11,desjardins|11,rbc mobile|11,nbc|11,rabobank|11,scotiabank|11,td (canada)|11,discord|12,drive|13,element|14,facebook|15,messenger|16,firefox|17,firefox beta|17,firefox nightly|17,f-droid|5,neo store|5,aurora droid|5,github|18,gitlab|19,gmx|20,google|21,google home|22,google play store|23,home assistant|24,instagram|25,kalender|26,keep notes|27,lieferando|28,linkedin|29,maps|30,organic maps|30,osmand|30,mastodon|31,fedilab|31,tooot|31,tusky|31,mattermost|32,n26|33,netflix|34,news|35,cbc news|35,rc info|35,reuters|35,ap news|35,la presse|35,nbc news|35,nextbike|36,nina|37,outlook mail|38,paypal|39,phone|40,plex|41,pocket|42,post & dhl|43,proton mail|44,reddit|45,sync pro|45,sync dev|45,boost|45,infinity|45,slide|45,signal|46,skype|47,slack|48,snapchat|49,starbucks|50,steam|51,teams|52,telegram|53,telegram foss|53,threema|54,tiktok|55,to do|56,opentasks|56,tasks|56,transit|57,twitch|58,twitter|59,uber|60,lyft|60,vlc|61,warnapp|62,whatsapp|63,wordfeud|64,youtube|65,newpipe|65,zoom|66,meet|66,music|67,sms message|0,mail|0,gmail|0,".match(new RegExp(`,${s}\\|(\\d+)`))
|
let match = ",default|0,airbnb|1,alarm|2,alarmclockreceiver|2,amazon shopping|3,bibel|4,bitwarden|5,1password|5,lastpass|5,dashlane|5,bring|6,calendar|7,etar|7,chat|8,chrome|9,clock|2,corona-warn|10,bmo|11,desjardins|11,rbc mobile|11,nbc|11,rabobank|11,scotiabank|11,td (canada)|11,discord|12,drive|13,element|14,facebook|15,messenger|16,firefox|17,firefox beta|17,firefox nightly|17,f-droid|5,neo store|5,aurora droid|5,github|18,gitlab|19,gmx|20,google|21,google home|22,google play store|23,home assistant|24,instagram|25,jira|26,kalender|27,keep notes|28,lieferando|29,linkedin|30,maps|31,organic maps|31,osmand|31,mastodon|32,fedilab|32,tooot|32,tusky|32,mattermost|33,n26|34,netflix|35,news|36,cbc news|36,rc info|36,reuters|36,ap news|36,la presse|36,nbc news|36,nextbike|37,nina|38,outlook mail|39,paypal|40,phone|41,plex|42,pocket|43,post & dhl|44,proton mail|45,reddit|46,sync pro|46,sync dev|46,boost|46,infinity|46,slide|46,signal|47,skype|48,slack|49,snapchat|50,starbucks|51,steam|52,teams|53,telegram|54,telegram foss|54,threema|55,tiktok|56,to do|57,opentasks|57,tasks|57,transit|58,twitch|59,twitter|60,uber|61,lyft|61,vlc|62,warnapp|63,whatsapp|64,wordfeud|65,youtube|66,newpipe|66,zoom|67,meet|67,music|68,sms message|0,mail|0,gmail|0,".match(new RegExp(`,${s}\\|(\\d+)`))
|
||||||
return require("Storage").read("messageicons.img", (match===null)?0:match[1]*76, 76);
|
return require("Storage").read("messageicons.img", (match===null)?0:match[1]*76, 76);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ exports.getColor = function(msg,options) {
|
||||||
if (st.iconColorMode == 'mono') return options.default;
|
if (st.iconColorMode == 'mono') return options.default;
|
||||||
const s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase();
|
const s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase();
|
||||||
return {
|
return {
|
||||||
|
// This file is generated by /icons/generate.js. If you need to modify its content, you should do it there instead.
|
||||||
// generic colors, using B2-safe colors
|
// generic colors, using B2-safe colors
|
||||||
// DO NOT USE BLACK OR WHITE HERE, just leave the declaration out and then the theme's fg color will be used
|
// DO NOT USE BLACK OR WHITE HERE, just leave the declaration out and then the theme's fg color will be used
|
||||||
"airbnb": "#ff385c", // https://news.airbnb.com/media-assets/category/brand/
|
"airbnb": "#ff385c", // https://news.airbnb.com/media-assets/category/brand/
|
||||||
|
@ -33,6 +34,7 @@ exports.getColor = function(msg,options) {
|
||||||
"google home": "#fbbc05",
|
"google home": "#fbbc05",
|
||||||
// "home assistant": "#41bdf5", // ha-blue is #41bdf5, but that's the background
|
// "home assistant": "#41bdf5", // ha-blue is #41bdf5, but that's the background
|
||||||
"instagram": "#ff0069", // https://about.instagram.com/brand/gradient
|
"instagram": "#ff0069", // https://about.instagram.com/brand/gradient
|
||||||
|
"jira": "#0052cc", //https://atlassian.design/resources/logo-library
|
||||||
"lieferando": "#ff8000",
|
"lieferando": "#ff8000",
|
||||||
"linkedin": "#0a66c2", // https://brand.linkedin.com/
|
"linkedin": "#0a66c2", // https://brand.linkedin.com/
|
||||||
"messenger": "#0078ff",
|
"messenger": "#0078ff",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "messageicons",
|
"id": "messageicons",
|
||||||
"name": "Message Icons",
|
"name": "Message Icons",
|
||||||
"version": "0.04",
|
"version": "0.05",
|
||||||
"description": "Library containing a list of icons and colors for apps",
|
"description": "Library containing a list of icons and colors for apps",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|
|
@ -3,4 +3,5 @@
|
||||||
0.03: Optional show lock status via color
|
0.03: Optional show lock status via color
|
||||||
0.04: Ensure that widgets are always hidden in fullscreen mode
|
0.04: Ensure that widgets are always hidden in fullscreen mode
|
||||||
0.05: Better lock/unlock animation
|
0.05: Better lock/unlock animation
|
||||||
0.06: Use widget_utils.
|
0.06: Use widget_utils
|
||||||
|
0.07: Convert Yes/No On/Off in settings to checkboxes
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "neonx",
|
"id": "neonx",
|
||||||
"name": "Neon X & IO X Clock",
|
"name": "Neon X & IO X Clock",
|
||||||
"shortName": "Neon X Clock",
|
"shortName": "Neon X Clock",
|
||||||
"version": "0.06",
|
"version": "0.07",
|
||||||
"description": "Pebble Neon X & Neon IO X for Bangle.js",
|
"description": "Pebble Neon X & Neon IO X for Bangle.js",
|
||||||
"icon": "neonx.png",
|
"icon": "neonx.png",
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
|
|
|
@ -25,11 +25,9 @@
|
||||||
"" : { "title":"Neon X & IO"},
|
"" : { "title":"Neon X & IO"},
|
||||||
"< Back": back,
|
"< Back": back,
|
||||||
"Neon IO X": {
|
"Neon IO X": {
|
||||||
value: 0 | neonXSettings.io,
|
value: !!neonXSettings.io,
|
||||||
min: 0, max: 1,
|
|
||||||
format: v => v ? "On" : "Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
neonXSettings.io = v;
|
neonXSettings.io = v?1:0;
|
||||||
updateSettings();
|
updateSettings();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -43,27 +41,23 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Date on touch": {
|
"Date on touch": {
|
||||||
value: 0 | neonXSettings.showDate,
|
value: !!neonXSettings.showDate,
|
||||||
min: 0, max: 1,
|
|
||||||
format: v => v ? "On" : "Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
neonXSettings.showDate = v;
|
neonXSettings.showDate = v?1:0;
|
||||||
updateSettings();
|
updateSettings();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'Fullscreen': {
|
'Fullscreen': {
|
||||||
value: false | neonXSettings.fullscreen,
|
value: !!neonXSettings.fullscreen,
|
||||||
format: () => (neonXSettings.fullscreen ? 'Yes' : 'No'),
|
onchange: v => {
|
||||||
onchange: () => {
|
neonXSettings.fullscreen = v;
|
||||||
neonXSettings.fullscreen = !neonXSettings.fullscreen;
|
|
||||||
updateSettings();
|
updateSettings();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'Show lock': {
|
'Show lock': {
|
||||||
value: false | neonXSettings.showLock,
|
value: !!neonXSettings.showLock,
|
||||||
format: () => (neonXSettings.showLock ? 'Yes' : 'No'),
|
onchange: v => {
|
||||||
onchange: () => {
|
neonXSettings.showLock = v;
|
||||||
neonXSettings.showLock = !neonXSettings.showLock;
|
|
||||||
updateSettings();
|
updateSettings();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,3 +8,4 @@
|
||||||
0.08: Add new draw styles, tidy up draw functionality
|
0.08: Add new draw styles, tidy up draw functionality
|
||||||
0.09: Tweak for faster rendering
|
0.09: Tweak for faster rendering
|
||||||
0.10: Enhance for use with Bangle2, insert new draw mode 'thickfill'
|
0.10: Enhance for use with Bangle2, insert new draw mode 'thickfill'
|
||||||
|
0.11: Convert Yes/No On/Off in settings to checkboxes
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "numerals",
|
"id": "numerals",
|
||||||
"name": "Numerals Clock",
|
"name": "Numerals Clock",
|
||||||
"shortName": "Numerals Clock",
|
"shortName": "Numerals Clock",
|
||||||
"version": "0.10",
|
"version": "0.11",
|
||||||
"description": "A simple big numerals clock",
|
"description": "A simple big numerals clock",
|
||||||
"icon": "numerals.png",
|
"icon": "numerals.png",
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
|
|
|
@ -30,10 +30,8 @@
|
||||||
onchange: v=> { numeralsSettings.drawMode=dm[v]; updateSettings();}
|
onchange: v=> { numeralsSettings.drawMode=dm[v]; updateSettings();}
|
||||||
},
|
},
|
||||||
"Date on touch": {
|
"Date on touch": {
|
||||||
value: 0|numeralsSettings.showDate,
|
value: !!numeralsSettings.showDate,
|
||||||
min:0,max:1,
|
onchange: v=> { numeralsSettings.showDate=v?1:0; updateSettings();}
|
||||||
format: v=>v?"On":"Off",
|
|
||||||
onchange: v=> { numeralsSettings.showDate=v; updateSettings();}
|
|
||||||
},
|
},
|
||||||
"< back": back
|
"< back": back
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,3 +4,8 @@
|
||||||
0.04: Remove calibration with current voltage (Calibrate->Auto) as it is now handled by settings app
|
0.04: Remove calibration with current voltage (Calibrate->Auto) as it is now handled by settings app
|
||||||
Allow automatic calibration on every charge longer than 3 hours
|
Allow automatic calibration on every charge longer than 3 hours
|
||||||
0.05: Add back button to settings menu.
|
0.05: Add back button to settings menu.
|
||||||
|
0.06: Allow logging of some things using power
|
||||||
|
Add widget for live monitoring of power use
|
||||||
|
0.07: Convert Yes/No On/Off in settings to checkboxes
|
||||||
|
0.08: Fix the wrapping of intervals/timeouts with parameters
|
||||||
|
Fix the widget drawing if widgets are hidden and Bangle.setLCDBrightness is called
|
|
@ -7,6 +7,26 @@ Features:
|
||||||
* Force monotonic battery percentage or voltage
|
* Force monotonic battery percentage or voltage
|
||||||
* Automatic calibration on charging uninterrupted longer than 3 hours (reloads of the watch reset the timer).
|
* Automatic calibration on charging uninterrupted longer than 3 hours (reloads of the watch reset the timer).
|
||||||
|
|
||||||
|
|
||||||
|
## Widget
|
||||||
|
|
||||||
|
The widget shows an approximate current power use. There is a power gauge showing the estimation of the currently used power and the currently active sensor with the biggest power draw.
|
||||||
|
G for GPS, H for pulse sensor and C for compass.
|
||||||
|
|
||||||
|
## Logging
|
||||||
|
|
||||||
|
You can switch on logging in the options to diagnose unexpected power use. Currently the logging can capture the code running from timeouts and intervals and the power up and down of some devices. The captured times are probably not perfect but should be good enough to indicate problems.
|
||||||
|
|
||||||
|
Do not use trace logging for extended time, it uses a lot of storage and can fill up the flash quite quick.
|
||||||
|
|
||||||
|
### TODO
|
||||||
|
|
||||||
|
* Wrap functions given as strings to setTimeout/setInterval
|
||||||
|
* Handle eval in setTimeout/setInterval
|
||||||
|
* Track functions executed as event handlers
|
||||||
|
* Track buzzer
|
||||||
|
* Modify browser interface to estimate power use like widget does
|
||||||
|
|
||||||
## Internals
|
## Internals
|
||||||
|
|
||||||
Battery calibration offset is set by writing `batFullVoltage` in setting.json
|
Battery calibration offset is set by writing `batFullVoltage` in setting.json
|
||||||
|
@ -14,6 +34,10 @@ Battery calibration offset is set by writing `batFullVoltage` in setting.json
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
* Optionally keep battery history and show as graph
|
* Optionally keep battery history and show as graph
|
||||||
|
* Capture some more stuff in logging
|
||||||
|
* Event driven code execution
|
||||||
|
* Buzzer
|
||||||
|
* Better tracking of display on time
|
||||||
|
|
||||||
## Creator
|
## Creator
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,115 @@
|
||||||
require('Storage').readJSON("powermanager.json", true) || {}
|
require('Storage').readJSON("powermanager.json", true) || {}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (settings.log) {
|
||||||
|
let logFile = require('Storage').open("powermanager.log","a");
|
||||||
|
let def = require('Storage').readJSON("powermanager.def.json", true) || {};
|
||||||
|
if (!def.start) def.start = Date.now();
|
||||||
|
if (!def.deferred) def.deferred = {};
|
||||||
|
let hw = require('Storage').readJSON("powermanager.hw.json", true) || {};
|
||||||
|
if (!hw.start) hw.start = Date.now();
|
||||||
|
if (!hw.power) hw.power = {};
|
||||||
|
|
||||||
|
const saveEvery = 1000 * 60 * 5;
|
||||||
|
const TO_WRAP = ["GPS","Compass","Barometer","HRM","LCD"];
|
||||||
|
|
||||||
|
let save = ()=>{
|
||||||
|
let defExists = require("Storage").read("powermanager.def.json")!==undefined;
|
||||||
|
if (!(!defExists && def.saved)){
|
||||||
|
def.saved = Date.now();
|
||||||
|
require('Storage').writeJSON("powermanager.def.json", def);
|
||||||
|
}
|
||||||
|
let hwExists = require("Storage").read("powermanager.hw.json")!==undefined;
|
||||||
|
if (!(!hwExists && hw.saved)){
|
||||||
|
hw.saved = Date.now();
|
||||||
|
require('Storage').writeJSON("powermanager.hw.json", hw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setInterval(save, saveEvery);
|
||||||
|
|
||||||
|
E.on("kill", ()=>{
|
||||||
|
for (let c of TO_WRAP){
|
||||||
|
if (lastPowerOn[c] && Bangle["is"+c+"On"]()){
|
||||||
|
hw.power[c] += Date.now() - lastPowerOn[c];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
save();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
let logPower = (type, oldstate, state, app) => {
|
||||||
|
logFile.write("p," + type + ',' + (oldstate?1:0) + ',' + (state?1:0) + ',' + app + "\n");
|
||||||
|
};
|
||||||
|
let logDeferred = (type, duration, source) => {
|
||||||
|
logFile.write(type + ',' + duration + ',' + source.replace(/\n/g, " ").replace(/,/g,"") + "\n");
|
||||||
|
};
|
||||||
|
|
||||||
|
let lastPowerOn = {};
|
||||||
|
|
||||||
|
for (let c of TO_WRAP){
|
||||||
|
let functionName = "set" + c + "Power";
|
||||||
|
let checkName = "is" + c + "On";
|
||||||
|
let type = c + "";
|
||||||
|
lastPowerOn[type] = (!lastPowerOn[type] && Bangle[checkName]()) ? Date.now() : undefined;
|
||||||
|
|
||||||
|
lastPowerOn[type] = Date.now();
|
||||||
|
|
||||||
|
Bangle[functionName] = ((o) => (a,b) => {
|
||||||
|
let oldstate = Bangle[checkName]();
|
||||||
|
let result = o(a,b);
|
||||||
|
if (!lastPowerOn[type] && result) {
|
||||||
|
//switched on, store time
|
||||||
|
lastPowerOn[type] = Date.now();
|
||||||
|
} else if (lastPowerOn[type] && !result){
|
||||||
|
//switched off
|
||||||
|
hw.power[type] += Date.now() - lastPowerOn[type];
|
||||||
|
lastPowerOn[type] = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.logDetails) logPower(type, oldstate, result, b);
|
||||||
|
return result;
|
||||||
|
})(Bangle[functionName]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let functions = {};
|
||||||
|
let wrapDeferred = ((o,t) => (a) => {
|
||||||
|
if (a == eval || typeof a == "string") {
|
||||||
|
return o.apply(this, arguments);
|
||||||
|
} else {
|
||||||
|
let wrapped = a;
|
||||||
|
if (!a.__wrapped){
|
||||||
|
wrapped = ()=>{
|
||||||
|
let start = Date.now();
|
||||||
|
let result = a.apply(undefined, arguments.slice(2)); // function arguments for deferred calls start at index 2, first is function, second is time
|
||||||
|
let end = Date.now()-start;
|
||||||
|
let f = a.toString().substring(0,100);
|
||||||
|
if (settings.logDetails) logDeferred(t, end, f);
|
||||||
|
if (!def.deferred[f]) def.deferred[f] = 0;
|
||||||
|
def.deferred[f] += end;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
//copy over properties of functions
|
||||||
|
for (let p in a){
|
||||||
|
wrapped[p] = a[p];
|
||||||
|
}
|
||||||
|
//mark function as wrapped
|
||||||
|
wrapped.__wrapped = true;
|
||||||
|
}
|
||||||
|
let newArgs = arguments.slice();
|
||||||
|
newArgs[0] = wrapped;
|
||||||
|
return o.apply(this, newArgs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
global.setTimeout = wrapDeferred(global.setTimeout, "t");
|
||||||
|
global.setInterval = wrapDeferred(global.setInterval, "i");
|
||||||
|
}
|
||||||
|
|
||||||
if (settings.warnEnabled){
|
if (settings.warnEnabled){
|
||||||
var chargingInterval;
|
var chargingInterval;
|
||||||
|
|
||||||
function handleCharging(charging){
|
let handleCharging = (charging) => {
|
||||||
if (charging){
|
if (charging){
|
||||||
if (chargingInterval) clearInterval(chargingInterval);
|
if (chargingInterval) clearInterval(chargingInterval);
|
||||||
chargingInterval = setInterval(()=>{
|
chargingInterval = setInterval(()=>{
|
||||||
|
@ -20,7 +125,7 @@
|
||||||
clearInterval(chargingInterval);
|
clearInterval(chargingInterval);
|
||||||
chargingInterval = undefined;
|
chargingInterval = undefined;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
Bangle.on("charging",handleCharging);
|
Bangle.on("charging",handleCharging);
|
||||||
handleCharging(Bangle.isCharging());
|
handleCharging(Bangle.isCharging());
|
||||||
|
|
|
@ -2,5 +2,6 @@
|
||||||
"warnEnabled": false,
|
"warnEnabled": false,
|
||||||
"warn": 96,
|
"warn": 96,
|
||||||
"forceMonoVoltage": false,
|
"forceMonoVoltage": false,
|
||||||
"forceMonoPercentage": false
|
"forceMonoPercentage": false,
|
||||||
|
"log": false
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,268 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="content"></div>
|
||||||
|
|
||||||
|
<script src="../../core/lib/interface.js"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
var domContent = document.getElementById("content");
|
||||||
|
|
||||||
|
function download(filename, callback) {
|
||||||
|
Util.showModal("Downloading power info...");
|
||||||
|
Util.readStorage(filename, data => {
|
||||||
|
Util.hideModal();
|
||||||
|
callback(data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function show() {
|
||||||
|
Util.showModal("Loading...");
|
||||||
|
domContent.innerHTML = "";
|
||||||
|
var htmlOverview = `<table class="table table-striped table-hover">
|
||||||
|
<div>This needs "Logging" to be enabled in power manager settings. The deferred function calls table is only updated on the bangle on reloads.</div>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Type</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Deferred function calls</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-primary" filename="powermanager.def.json" task="deftable">Table</button>
|
||||||
|
<button class="btn btn-error" filename="powermanager.def.json" task="clear" style="float: right;margin-right: 5px;">Clear</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Hardware</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-primary" filename="powermanager.hw.json" task="hardwaretable">Table</button>
|
||||||
|
<button class="btn btn-error" filename="powermanager.hw.json" task="clear" style="float: right;margin-right: 5px;">Clear</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Details (Trace)</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-primary" filename="powermanager.log" task="detailstable">Table</button>
|
||||||
|
<button class="btn btn-error" filename="powermanager.log" task="detailsclear" style="float: right;margin-right: 5px;">Clear</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>`;
|
||||||
|
domContent.innerHTML = htmlOverview;
|
||||||
|
Util.hideModal();
|
||||||
|
var buttons = domContent.querySelectorAll("button");
|
||||||
|
for (var i=0;i<buttons.length;i++) {
|
||||||
|
buttons[i].addEventListener("click",event => {
|
||||||
|
var button = event.currentTarget;
|
||||||
|
var filename = button.getAttribute("filename");
|
||||||
|
if (!filename) return;
|
||||||
|
var task = button.getAttribute("task");
|
||||||
|
if (task=="detailsclear") {
|
||||||
|
Util.showModal("Clearing...");
|
||||||
|
Util.eraseStorageFile(filename,()=>{
|
||||||
|
Util.hideModal();
|
||||||
|
show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (task=="clear") {
|
||||||
|
Util.showModal("Clearing...");
|
||||||
|
Util.eraseStorage(filename,()=>{
|
||||||
|
Util.hideModal();
|
||||||
|
show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (task=="deftable") {
|
||||||
|
viewDeferredTable(filename);
|
||||||
|
}
|
||||||
|
if (task=="hardwaretable") {
|
||||||
|
viewHardwareTable(filename);
|
||||||
|
}
|
||||||
|
if (task=="detailstable") {
|
||||||
|
viewDetailsTable(filename);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function viewDeferredTable(filename) {
|
||||||
|
Puck.eval(`require("Storage").list("powermanager.def.json").length > 0`, (f)=>{
|
||||||
|
if (f) {
|
||||||
|
Util.showModal("Reading summarized info...");
|
||||||
|
Util.readStorage(
|
||||||
|
filename, data => {
|
||||||
|
Util.hideModal();
|
||||||
|
let parsed = JSON.parse(data);
|
||||||
|
|
||||||
|
let sum = 0;
|
||||||
|
let rows = [];
|
||||||
|
for (var i in parsed.deferred) {
|
||||||
|
sum += parsed.deferred[i];
|
||||||
|
rows.push({func: i, time: parsed.deferred[i]});
|
||||||
|
}
|
||||||
|
rows.sort((a,b)=>{return b.time/sum - a.time/sum;});
|
||||||
|
let tableRows = "";
|
||||||
|
for (var i in rows) {
|
||||||
|
let c = rows[i];
|
||||||
|
tableRows += `<tr>
|
||||||
|
<td>${(c.time/1000).toFixed(2)}s</td>
|
||||||
|
<td>${(c.time/sum*100).toFixed(2)}%</td>
|
||||||
|
<td><pre>${c.func}</pre></td>`
|
||||||
|
}
|
||||||
|
|
||||||
|
let duration = parsed.saved - parsed.start;
|
||||||
|
|
||||||
|
var htmlOverview = `<h1>Deferred function calls</h1>
|
||||||
|
<button class="btn btn-primary" id="back" style="float: right;margin-right: 5px;margin-left: 10px;">Back</button>
|
||||||
|
<div>
|
||||||
|
This are functions used in timeouts and intervals and their accumulated execution times. Recorded in a time span of <b>${Math.round((duration)/1000)}s</b>. Timeouts/intervals have run for <b>${Math.round(sum/1000)}s (${(sum/duration*100).toFixed(2)}%)</b>. Percentages are calculated from summarized timeout/interval running time.
|
||||||
|
</div>
|
||||||
|
<table class="table table-striped table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Time</th>
|
||||||
|
<th>Percentage</th>
|
||||||
|
<th>Function</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>\n`;
|
||||||
|
htmlOverview += tableRows;
|
||||||
|
htmlOverview += `</tbody></table>`;
|
||||||
|
domContent.innerHTML = htmlOverview;
|
||||||
|
domContent.querySelector("#back").addEventListener("click",event => {
|
||||||
|
show();
|
||||||
|
});
|
||||||
|
|
||||||
|
//try finding possible sources for the given function, currently does not work because function.toString() not being identical to code in the *.js files.
|
||||||
|
|
||||||
|
/*Puck.eval(`require("Storage").list(/.*.js$/)`, (f)=>{
|
||||||
|
console.log("Found files:", f, rows[1].func);
|
||||||
|
for (let file of f){
|
||||||
|
let query = `require("Storage").read('${file}').includes('${rows[1].func}')`;
|
||||||
|
console.log("Query: " + query);
|
||||||
|
Puck.eval(query, (r)=>{
|
||||||
|
if (r) domContent.querySelector("#row_1").innerHTML = file;
|
||||||
|
console.log("Found", file, r)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});*/
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
var htmlOverview = `<h1>Deferred function calls</h1>
|
||||||
|
<button class="btn btn-primary" id="back" style="float: right;margin-right: 5px;">Back</button>
|
||||||
|
<div>
|
||||||
|
No data available.
|
||||||
|
</div>`;
|
||||||
|
domContent.innerHTML = htmlOverview;
|
||||||
|
domContent.querySelector("#back").addEventListener("click",event => {
|
||||||
|
show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function viewHardwareTable(filename) {
|
||||||
|
Puck.eval(`require("Storage").list("powermanager.hw.json").length > 0`, (f)=>{
|
||||||
|
if (f) {
|
||||||
|
Util.showModal("Reading hardware info...");
|
||||||
|
Util.readStorage(
|
||||||
|
filename, data => {
|
||||||
|
Util.hideModal();
|
||||||
|
let parsed = JSON.parse(data);
|
||||||
|
console.log("Hardware", parsed);
|
||||||
|
let duration = parsed.saved - parsed.start;
|
||||||
|
|
||||||
|
let rows = [];
|
||||||
|
for (var i in parsed.power) {
|
||||||
|
rows.push({func: i, time: parsed.power[i]});
|
||||||
|
}
|
||||||
|
rows.sort((a,b)=>{return b.time/duration - a.time/duration;});
|
||||||
|
let tableRows = "";
|
||||||
|
for (var i in rows) {
|
||||||
|
let c = rows[i];
|
||||||
|
tableRows += `<tr>
|
||||||
|
<td>${(c.time/1000).toFixed(2)}s</td>
|
||||||
|
<td>${(c.time/duration*100).toFixed(2)}%</td>
|
||||||
|
<td>${c.func}</td>`
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var htmlOverview = `<h1>Hardware power</h1>
|
||||||
|
<button class="btn btn-primary" id="back" style="float: right;margin-right: 5px;margin-left: 10px;">Back</button>
|
||||||
|
<div>
|
||||||
|
Recorded in a time span of <b>${Math.round(duration/1000)}s</b>. Percentages are calculated from recording time.
|
||||||
|
</div>
|
||||||
|
<table class="table table-striped table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Time</th>
|
||||||
|
<th>Percentage</th>
|
||||||
|
<th>Device</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>\n`;
|
||||||
|
htmlOverview += tableRows;
|
||||||
|
htmlOverview += `</tbody></table>`;
|
||||||
|
domContent.innerHTML = htmlOverview;
|
||||||
|
domContent.querySelector("#back").addEventListener("click",event => {
|
||||||
|
show();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
var htmlOverview = `<h1>Hardware power</h1>
|
||||||
|
<button class="btn btn-primary" id="back" style="float: right;margin-right: 5px;">Back</button>
|
||||||
|
<div>
|
||||||
|
No data available.
|
||||||
|
</div>`;
|
||||||
|
domContent.innerHTML = htmlOverview;
|
||||||
|
domContent.querySelector("#back").addEventListener("click",event => {
|
||||||
|
show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function viewDetailsTable(filename) {
|
||||||
|
Util.showModal("Reading details info...");
|
||||||
|
Util.readStorageFile(
|
||||||
|
filename, data => {
|
||||||
|
Util.hideModal();
|
||||||
|
|
||||||
|
var htmlOverview = `<h1>Detailed logging</h1>
|
||||||
|
|
||||||
|
This is a trace log of all logged power entries, first column denotes the type. p for power, i for interval and t for timeout. Power is logged with old state, new state and calling app if available. Functions are logged with execution duraion and source if available.
|
||||||
|
|
||||||
|
<button class="btn btn-primary" id="back" style="float: right;margin-right: 5px;">Back</button>
|
||||||
|
<table class="table table-striped table-hover">
|
||||||
|
<tbody>\n`;
|
||||||
|
let rows = data.trim().split("\n");
|
||||||
|
for (var row of rows) {
|
||||||
|
let cols = row.split(",");
|
||||||
|
htmlOverview += `<tr>
|
||||||
|
<td>${cols[0]}</td>
|
||||||
|
<td>${cols[1]}</td>
|
||||||
|
<td>${cols[2]}</td>
|
||||||
|
<td>${cols[3]}</td>
|
||||||
|
<td>${cols[4]}</td>
|
||||||
|
</tr>`
|
||||||
|
}
|
||||||
|
htmlOverview += `</tbody></table>`;
|
||||||
|
domContent.innerHTML = htmlOverview;
|
||||||
|
domContent.querySelector("#back").addEventListener("click",event => {
|
||||||
|
show();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onInit() {
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -2,17 +2,25 @@
|
||||||
"id": "powermanager",
|
"id": "powermanager",
|
||||||
"name": "Power Manager",
|
"name": "Power Manager",
|
||||||
"shortName": "Power Manager",
|
"shortName": "Power Manager",
|
||||||
"version": "0.05",
|
"version": "0.08",
|
||||||
"description": "Allow configuration of warnings and thresholds for battery charging and display.",
|
"description": "Allow configuration of warnings and thresholds for battery charging and display.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "bootloader",
|
"type": "bootloader",
|
||||||
"tags": "tool",
|
"tags": "tool",
|
||||||
"supports": ["BANGLEJS2"],
|
"supports": ["BANGLEJS2"],
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
|
"interface": "interface.html",
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"powermanager.boot.js","url":"boot.js"},
|
{"name":"powermanager.boot.js","url":"boot.js"},
|
||||||
{"name":"powermanager.settings.js","url":"settings.js"},
|
{"name":"powermanager.settings.js","url":"settings.js"},
|
||||||
|
{"name":"powermanager.wid.js","url":"widget.js"},
|
||||||
{"name":"powermanager","url":"lib.js"},
|
{"name":"powermanager","url":"lib.js"},
|
||||||
{"name":"powermanager.default.json","url":"default.json"}
|
{"name":"powermanager.default.json","url":"default.json"}
|
||||||
|
],
|
||||||
|
"data": [
|
||||||
|
{"name":"powermanager.hw.json"},
|
||||||
|
{"name":"powermanager.def.json"},
|
||||||
|
{"name":"powermanager.json"},
|
||||||
|
{"name":"powermanager.log"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,9 @@
|
||||||
'title': 'Power Manager'
|
'title': 'Power Manager'
|
||||||
},
|
},
|
||||||
"< Back" : back,
|
"< Back" : back,
|
||||||
|
'Widget': function() {
|
||||||
|
E.showMenu(submenu_widget);
|
||||||
|
},
|
||||||
'Monotonic percentage': {
|
'Monotonic percentage': {
|
||||||
value: !!settings.forceMonoPercentage,
|
value: !!settings.forceMonoPercentage,
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
|
@ -41,6 +44,9 @@
|
||||||
},
|
},
|
||||||
'Calibrate': function() {
|
'Calibrate': function() {
|
||||||
E.showMenu(submenu_calibrate);
|
E.showMenu(submenu_calibrate);
|
||||||
|
},
|
||||||
|
'Logging': function() {
|
||||||
|
E.showMenu(submenu_logging);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -100,7 +106,6 @@
|
||||||
},
|
},
|
||||||
'Enabled': {
|
'Enabled': {
|
||||||
value: !!settings.warnEnabled,
|
value: !!settings.warnEnabled,
|
||||||
format: v => settings.warnEnabled ? "On" : "Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
writeSettings("warnEnabled", v);
|
writeSettings("warnEnabled", v);
|
||||||
}
|
}
|
||||||
|
@ -117,5 +122,61 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var submenu_logging = {
|
||||||
|
'': {
|
||||||
|
title: "Logging",
|
||||||
|
back: function() {
|
||||||
|
E.showMenu(mainmenu);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'Enabled': {
|
||||||
|
value: !!settings.log,
|
||||||
|
onchange: v => {
|
||||||
|
writeSettings("log", v);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'Trace': {
|
||||||
|
value: !!settings.logDetails,
|
||||||
|
onchange: v => {
|
||||||
|
writeSettings("logDetails", v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var submenu_widget = {
|
||||||
|
'': {
|
||||||
|
title: "Widget",
|
||||||
|
back: function() {
|
||||||
|
E.showMenu(mainmenu);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'Enabled': {
|
||||||
|
value: !!settings.widget,
|
||||||
|
onchange: v => {
|
||||||
|
writeSettings("widget", v);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'Refresh': {
|
||||||
|
min: 0.5,
|
||||||
|
max: 60,
|
||||||
|
step: 0.5,
|
||||||
|
value: settings.refreshUnlocked || 1,
|
||||||
|
format: v => v + "s",
|
||||||
|
onchange: v => {
|
||||||
|
writeSettings("refreshUnlocked", v);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'Refresh locked': {
|
||||||
|
min: 5,
|
||||||
|
max: 120,
|
||||||
|
step: 5,
|
||||||
|
value: settings.refreshLocked || 60,
|
||||||
|
format: v => v + "s",
|
||||||
|
onchange: v => {
|
||||||
|
writeSettings("refreshLocked", v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
E.showMenu(mainmenu);
|
E.showMenu(mainmenu);
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
/* run widgets in their own function scope so they don't interfere with
|
||||||
|
currently-running apps */
|
||||||
|
(() => {
|
||||||
|
const s = require("Storage").readJSON("powermanager.json") || {};
|
||||||
|
|
||||||
|
if (!s.widget) return;
|
||||||
|
|
||||||
|
const SYSTICKMAX = peek32(0xE000E014);
|
||||||
|
const SYSTICKWAIT = SYSTICKMAX/64000; // 64 MHz clock rate, Systick counting down on every non idle clock
|
||||||
|
|
||||||
|
const GU = require("graphics_utils");
|
||||||
|
const APPROX_IDLE = 0.3;
|
||||||
|
const APPROX_HIGH_BW_BLE = 0.5;
|
||||||
|
const APPROX_COMPASS = process.HWVERSION == 2 ? 5.5 : 2;
|
||||||
|
const APPROX_HRM = process.HWVERSION == 2 ? 1 : 2.5;
|
||||||
|
const APPROX_CPU = 3;
|
||||||
|
const APPROX_GPS = process.HWVERSION == 2 ? 25 : 30;
|
||||||
|
const APPROX_TOUCH = 2.5;
|
||||||
|
const APPROX_BACKLIGHT = process.HWVERSION == 2 ? 16 : 40;
|
||||||
|
const MAX = APPROX_IDLE + APPROX_HIGH_BW_BLE + APPROX_COMPASS + APPROX_HRM + APPROX_CPU + APPROX_GPS + APPROX_TOUCH + APPROX_BACKLIGHT;
|
||||||
|
|
||||||
|
let settings = require("Storage").readJSON("setting.json") || {};
|
||||||
|
|
||||||
|
let brightnessSetting = settings.brightness || 1;
|
||||||
|
Bangle.setLCDBrightness = ((o) => (a) => {
|
||||||
|
brightnessSetting = a;
|
||||||
|
WIDGETS.powermanager.draw(WIDGETS.powermanager);
|
||||||
|
return o(a);
|
||||||
|
})(Bangle.setLCDBrightness);
|
||||||
|
|
||||||
|
let brightness = () => {
|
||||||
|
return process.HWVERSION == 2 ? (brightnessSetting * APPROX_BACKLIGHT) : (brightnessSetting * 0.9 * 33 + 7);
|
||||||
|
};
|
||||||
|
|
||||||
|
function doDraw(w, cpu){
|
||||||
|
g.reset();
|
||||||
|
|
||||||
|
let current = APPROX_IDLE + cpu * APPROX_CPU;
|
||||||
|
let mostExpensive = "P";
|
||||||
|
|
||||||
|
if (!Bangle.isLocked()) current += APPROX_TOUCH + brightness();
|
||||||
|
if (Bangle.isCompassOn()) {
|
||||||
|
current += APPROX_COMPASS;
|
||||||
|
mostExpensive = "C";
|
||||||
|
}
|
||||||
|
if (Bangle.isHRMOn()) {
|
||||||
|
current += APPROX_HRM;
|
||||||
|
mostExpensive = "H";
|
||||||
|
}
|
||||||
|
if (Bangle.isGPSOn()) {
|
||||||
|
current += APPROX_GPS;
|
||||||
|
mostExpensive = "G";
|
||||||
|
}
|
||||||
|
|
||||||
|
current = current / MAX;
|
||||||
|
|
||||||
|
g.clearRect(w.x, w.y, w.x + 23, w.y + 23);
|
||||||
|
|
||||||
|
g.setColor(g.theme.fg);
|
||||||
|
|
||||||
|
g.setFont6x15();
|
||||||
|
g.setFontAlign(0, 0);
|
||||||
|
g.drawString(mostExpensive, w.x + 12, w.y + 15);
|
||||||
|
let end = 135 + (current * (405 - 135));
|
||||||
|
g.setColor(current > 0.7 ? "#f00" : (current > 0.3 ? "#ff0" : "#0f0"));
|
||||||
|
GU.fillArc(g, w.x + 12, w.y + 12, 9, 12, GU.degreesToRadians(135), GU.degreesToRadians(end), GU.degreesToRadians(30));
|
||||||
|
|
||||||
|
g.setColor(g.theme.fg);
|
||||||
|
let endCpu = 135 + (cpu * (405 - 135));
|
||||||
|
GU.fillArc(g, w.x + 12, w.y + 12, 5.5, 8, GU.degreesToRadians(135), GU.degreesToRadians(endCpu), GU.degreesToRadians(30));
|
||||||
|
}
|
||||||
|
let sTimeout;
|
||||||
|
let s2Timeout;
|
||||||
|
let systickDiff;
|
||||||
|
function draw(w) {
|
||||||
|
let nextRefresh = Bangle.isLocked() ? ((s.refreshLocked || 60) * 1000 ): ((s.refreshUnlocked || 1) * 1000)
|
||||||
|
|
||||||
|
if (s2Timeout) clearTimeout(s2Timeout);
|
||||||
|
if (sTimeout) clearTimeout(sTimeout);
|
||||||
|
|
||||||
|
let t,systickNow;
|
||||||
|
sTimeout = setTimeout(()=>{
|
||||||
|
systickNow = peek32(0xE000E018);
|
||||||
|
t = Date.now();
|
||||||
|
}, nextRefresh - SYSTICKWAIT - 100);
|
||||||
|
|
||||||
|
s2Timeout = setTimeout(() => {
|
||||||
|
let tLater = Date.now();
|
||||||
|
let systickLater = peek32(0xE000E018);
|
||||||
|
systickDiff = systickLater - systickNow;
|
||||||
|
if (systickDiff < 0) systickDiff += SYSTICKMAX;
|
||||||
|
}, nextRefresh - 100);
|
||||||
|
|
||||||
|
doDraw(w, systickDiff ? (1 - systickDiff/SYSTICKMAX) : 0);
|
||||||
|
|
||||||
|
if (w.timeoutId !== undefined) {
|
||||||
|
clearTimeout(w.timeoutId);
|
||||||
|
}
|
||||||
|
w.timeoutId = setTimeout(() => {
|
||||||
|
w.timeoutId = undefined;
|
||||||
|
w.draw(w);
|
||||||
|
}, nextRefresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add your widget
|
||||||
|
WIDGETS.powermanager = {
|
||||||
|
area: "tl",
|
||||||
|
width: 24,
|
||||||
|
draw: draw
|
||||||
|
};
|
||||||
|
|
||||||
|
Bangle.on("lock", ()=>{WIDGETS.powermanager.draw(WIDGETS.powermanager);});
|
||||||
|
|
||||||
|
// conserve memory
|
||||||
|
delete settings;
|
||||||
|
})();
|
|
@ -25,3 +25,4 @@
|
||||||
0.19: Fix track plotting code
|
0.19: Fix track plotting code
|
||||||
0.20: Automatic translation of some more strings.
|
0.20: Automatic translation of some more strings.
|
||||||
0.21: Speed report now uses speed units from locale
|
0.21: Speed report now uses speed units from locale
|
||||||
|
0.22: Convert Yes/No On/Off in settings to checkboxes
|
||||||
|
|
|
@ -56,7 +56,6 @@ function showMainMenu() {
|
||||||
'< Back': ()=>{load();},
|
'< Back': ()=>{load();},
|
||||||
/*LANG*/'RECORD': {
|
/*LANG*/'RECORD': {
|
||||||
value: !!settings.recording,
|
value: !!settings.recording,
|
||||||
format: v=>v?/*LANG*/"On":/*LANG*/"Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
E.showMenu();
|
E.showMenu();
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "recorder",
|
"id": "recorder",
|
||||||
"name": "Recorder",
|
"name": "Recorder",
|
||||||
"shortName": "Recorder",
|
"shortName": "Recorder",
|
||||||
"version": "0.21",
|
"version": "0.22",
|
||||||
"description": "Record GPS position, heart rate and more in the background, then download to your PC.",
|
"description": "Record GPS position, heart rate and more in the background, then download to your PC.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,outdoors,gps,widget",
|
"tags": "tool,outdoors,gps,widget",
|
||||||
|
|
|
@ -13,3 +13,4 @@
|
||||||
0.12: Fix for recorder not stopping at end of run. Bug introduced in 0.11
|
0.12: Fix for recorder not stopping at end of run. Bug introduced in 0.11
|
||||||
0.13: Revert #1578 (stop duplicate entries) as with 2v12 menus it causes other boxes to be wiped (fix #1643)
|
0.13: Revert #1578 (stop duplicate entries) as with 2v12 menus it causes other boxes to be wiped (fix #1643)
|
||||||
0.14: Fix Bangle.js 1 issue where after the 'overwrite track' menu, the start/stop button stopped working
|
0.14: Fix Bangle.js 1 issue where after the 'overwrite track' menu, the start/stop button stopped working
|
||||||
|
0.15: Keep run state between runs (allowing you to exit and restart the app)
|
||||||
|
|
|
@ -41,6 +41,13 @@ var statIDs = [settings.B1,settings.B2,settings.B3,settings.B4,settings.B5,setti
|
||||||
var exs = ExStats.getStats(statIDs, settings);
|
var exs = ExStats.getStats(statIDs, settings);
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
|
|
||||||
|
function setStatus(running) {
|
||||||
|
layout.button.label = running ? "STOP" : "START";
|
||||||
|
layout.status.label = running ? "RUN" : "STOP";
|
||||||
|
layout.status.bgCol = running ? "#0f0" : "#f00";
|
||||||
|
layout.render();
|
||||||
|
}
|
||||||
|
|
||||||
// Called to start/stop running
|
// Called to start/stop running
|
||||||
function onStartStop() {
|
function onStartStop() {
|
||||||
var running = !exs.state.active;
|
var running = !exs.state.active;
|
||||||
|
@ -77,12 +84,9 @@ function onStartStop() {
|
||||||
} else {
|
} else {
|
||||||
exs.stop();
|
exs.stop();
|
||||||
}
|
}
|
||||||
layout.button.label = running ? "STOP" : "START";
|
|
||||||
layout.status.label = running ? "RUN" : "STOP";
|
|
||||||
layout.status.bgCol = running ? "#0f0" : "#f00";
|
|
||||||
// if stopping running, don't clear state
|
// if stopping running, don't clear state
|
||||||
// so we can at least refer to what we've done
|
// so we can at least refer to what we've done
|
||||||
layout.render();
|
setStatus(running);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,13 +109,14 @@ for (var i=0;i<statIDs.length;i+=2) {
|
||||||
lc.push({ type:"h", filly:1, c:[
|
lc.push({ type:"h", filly:1, c:[
|
||||||
{type:"txt", font:fontHeading, label:"GPS", id:"gps", fillx:1, bgCol:"#f00" },
|
{type:"txt", font:fontHeading, label:"GPS", id:"gps", fillx:1, bgCol:"#f00" },
|
||||||
{type:"txt", font:fontHeading, label:"00:00", id:"clock", fillx:1, bgCol:g.theme.fg, col:g.theme.bg },
|
{type:"txt", font:fontHeading, label:"00:00", id:"clock", fillx:1, bgCol:g.theme.fg, col:g.theme.bg },
|
||||||
{type:"txt", font:fontHeading, label:"STOP", id:"status", fillx:1 }
|
{type:"txt", font:fontHeading, label:"---", id:"status", fillx:1 }
|
||||||
]});
|
]});
|
||||||
// Now calculate the layout
|
// Now calculate the layout
|
||||||
var layout = new Layout( {
|
var layout = new Layout( {
|
||||||
type:"v", c: lc
|
type:"v", c: lc
|
||||||
},{lazy:true, btns:[{ label:"START", cb: onStartStop, id:"button"}]});
|
},{lazy:true, btns:[{ label:"---", cb: onStartStop, id:"button"}]});
|
||||||
delete lc;
|
delete lc;
|
||||||
|
setStatus(exs.state.active);
|
||||||
layout.render();
|
layout.render();
|
||||||
|
|
||||||
function configureNotification(stat) {
|
function configureNotification(stat) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{ "id": "run",
|
{ "id": "run",
|
||||||
"name": "Run",
|
"name": "Run",
|
||||||
"version":"0.14",
|
"version":"0.15",
|
||||||
"description": "Displays distance, time, steps, cadence, pace and more for runners.",
|
"description": "Displays distance, time, steps, cadence, pace and more for runners.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "run,running,fitness,outdoors,gps",
|
"tags": "run,running,fitness,outdoors,gps",
|
||||||
|
|
|
@ -14,5 +14,6 @@
|
||||||
0.13: Revert #1578 (stop duplicate entries) as with 2v12 menus it causes other boxes to be wiped (fix #1643)
|
0.13: Revert #1578 (stop duplicate entries) as with 2v12 menus it causes other boxes to be wiped (fix #1643)
|
||||||
0.14: Fix Bangle.js 1 issue where after the 'overwrite track' menu, the start/stop button stopped working
|
0.14: Fix Bangle.js 1 issue where after the 'overwrite track' menu, the start/stop button stopped working
|
||||||
0.15: Diverge from the standard "Run" app. Swipe to intensity interface a la Karvonen (curtesy of FTeacher at https://github.com/f-teacher)
|
0.15: Diverge from the standard "Run" app. Swipe to intensity interface a la Karvonen (curtesy of FTeacher at https://github.com/f-teacher)
|
||||||
|
Keep run state between runs (allowing you to exit and restart the app)
|
||||||
0.16: Don't clear zone 2b indicator segment when updating HRM reading.
|
0.16: Don't clear zone 2b indicator segment when updating HRM reading.
|
||||||
Write to correct settings file, fixing settings not working.
|
Write to correct settings file, fixing settings not working.
|
||||||
|
|
|
@ -52,11 +52,18 @@ let statIDs = [settings.B1,settings.B2,settings.B3,settings.B4,settings.B5,setti
|
||||||
let exs = ExStats.getStats(statIDs, settings);
|
let exs = ExStats.getStats(statIDs, settings);
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
|
|
||||||
|
function setStatus(running) {
|
||||||
|
layout.button.label = running ? "STOP" : "START";
|
||||||
|
layout.status.label = running ? "RUN" : "STOP";
|
||||||
|
layout.status.bgCol = running ? "#0f0" : "#f00";
|
||||||
|
layout.render();
|
||||||
|
}
|
||||||
|
|
||||||
// Called to start/stop running
|
// Called to start/stop running
|
||||||
function onStartStop() {
|
function onStartStop() {
|
||||||
let running = !exs.state.active;
|
let running = !exs.state.active;
|
||||||
let prepPromises = [];
|
let prepPromises = [];
|
||||||
|
https://github.com/espruino/BangleApps/pull/2600/conflict?name=apps%252Frunplus%252Fapp.js&ancestor_oid=15e0427592259f3112ee70d43bc7ce6fab20e1d8&base_oid=06270850f707ea94957a2990fb83425e7dc68d7f&head_oid=633e47983e015446796f69837501c8fc72255031
|
||||||
// start/stop recording
|
// start/stop recording
|
||||||
// Do this first in case recorder needs to prompt for
|
// Do this first in case recorder needs to prompt for
|
||||||
// an overwrite before we start tracking exstats
|
// an overwrite before we start tracking exstats
|
||||||
|
@ -88,12 +95,9 @@ function onStartStop() {
|
||||||
} else {
|
} else {
|
||||||
exs.stop();
|
exs.stop();
|
||||||
}
|
}
|
||||||
layout.button.label = running ? "STOP" : "START";
|
|
||||||
layout.status.label = running ? "RUN" : "STOP";
|
|
||||||
layout.status.bgCol = running ? "#0f0" : "#f00";
|
|
||||||
// if stopping running, don't clear state
|
// if stopping running, don't clear state
|
||||||
// so we can at least refer to what we've done
|
// so we can at least refer to what we've done
|
||||||
layout.render();
|
setStatus(running);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,13 +120,14 @@ for (let i=0;i<statIDs.length;i+=2) {
|
||||||
lc.push({ type:"h", filly:1, c:[
|
lc.push({ type:"h", filly:1, c:[
|
||||||
{type:"txt", font:fontHeading, label:"GPS", id:"gps", fillx:1, bgCol:"#f00" },
|
{type:"txt", font:fontHeading, label:"GPS", id:"gps", fillx:1, bgCol:"#f00" },
|
||||||
{type:"txt", font:fontHeading, label:"00:00", id:"clock", fillx:1, bgCol:g.theme.fg, col:g.theme.bg },
|
{type:"txt", font:fontHeading, label:"00:00", id:"clock", fillx:1, bgCol:g.theme.fg, col:g.theme.bg },
|
||||||
{type:"txt", font:fontHeading, label:"STOP", id:"status", fillx:1 }
|
{type:"txt", font:fontHeading, label:"---", id:"status", fillx:1 }
|
||||||
]});
|
]});
|
||||||
// Now calculate the layout
|
// Now calculate the layout
|
||||||
let layout = new Layout( {
|
let layout = new Layout( {
|
||||||
type:"v", c: lc
|
type:"v", c: lc
|
||||||
},{lazy:true, btns:[{ label:"START", cb: ()=>{if (karvonenActive) {stopKarvonenUI();run();} onStartStop();}, id:"button"}]});
|
},{lazy:true, btns:[{ label:"---", cb: ()=>{if (karvonnenActive) {stopKarvonnenUI();run();} onStartStop();}, id:"button"}]});
|
||||||
delete lc;
|
delete lc;
|
||||||
|
setStatus(exs.state.active);
|
||||||
layout.render();
|
layout.render();
|
||||||
|
|
||||||
function configureNotification(stat) {
|
function configureNotification(stat) {
|
||||||
|
|
|
@ -63,3 +63,4 @@
|
||||||
0.56: make System menu items shorter and more consistant, Eg 'Clock', intead
|
0.56: make System menu items shorter and more consistant, Eg 'Clock', intead
|
||||||
of 'Select Clock'
|
of 'Select Clock'
|
||||||
0.57: Settings.log = 0,1,2,3 for off,display,log,both
|
0.57: Settings.log = 0,1,2,3 for off,display,log,both
|
||||||
|
0.58: On/Off settings items now use checkboxes
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "setting",
|
"id": "setting",
|
||||||
"name": "Settings",
|
"name": "Settings",
|
||||||
"version": "0.57",
|
"version": "0.58",
|
||||||
"description": "A menu for setting up Bangle.js",
|
"description": "A menu for setting up Bangle.js",
|
||||||
"icon": "settings.png",
|
"icon": "settings.png",
|
||||||
"tags": "tool,system",
|
"tags": "tool,system",
|
||||||
|
|
|
@ -64,8 +64,6 @@ if (("object" != typeof settings) ||
|
||||||
("object" != typeof settings.options))
|
("object" != typeof settings.options))
|
||||||
resetSettings();
|
resetSettings();
|
||||||
|
|
||||||
const boolFormat = v => v ? /*LANG*/"On" : /*LANG*/"Off";
|
|
||||||
|
|
||||||
function showMainMenu() {
|
function showMainMenu() {
|
||||||
|
|
||||||
const mainmenu = {
|
const mainmenu = {
|
||||||
|
@ -102,7 +100,6 @@ function showAlertsMenu() {
|
||||||
if (BANGLEJS2) {
|
if (BANGLEJS2) {
|
||||||
beepMenuItem = {
|
beepMenuItem = {
|
||||||
value: settings.beep!=false,
|
value: settings.beep!=false,
|
||||||
format: boolFormat,
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.beep = v;
|
settings.beep = v;
|
||||||
updateSettings();
|
updateSettings();
|
||||||
|
@ -134,7 +131,6 @@ function showAlertsMenu() {
|
||||||
/*LANG*/'Beep': beepMenuItem,
|
/*LANG*/'Beep': beepMenuItem,
|
||||||
/*LANG*/'Vibration': {
|
/*LANG*/'Vibration': {
|
||||||
value: settings.vibrate,
|
value: settings.vibrate,
|
||||||
format: boolFormat,
|
|
||||||
onchange: () => {
|
onchange: () => {
|
||||||
settings.vibrate = !settings.vibrate;
|
settings.vibrate = !settings.vibrate;
|
||||||
updateSettings();
|
updateSettings();
|
||||||
|
@ -169,7 +165,6 @@ function showBLEMenu() {
|
||||||
/*LANG*/'Make Connectable': ()=>makeConnectable(),
|
/*LANG*/'Make Connectable': ()=>makeConnectable(),
|
||||||
/*LANG*/'BLE': {
|
/*LANG*/'BLE': {
|
||||||
value: settings.ble,
|
value: settings.ble,
|
||||||
format: boolFormat,
|
|
||||||
onchange: () => {
|
onchange: () => {
|
||||||
settings.ble = !settings.ble;
|
settings.ble = !settings.ble;
|
||||||
updateSettings();
|
updateSettings();
|
||||||
|
@ -177,7 +172,6 @@ function showBLEMenu() {
|
||||||
},
|
},
|
||||||
/*LANG*/'Programmable': {
|
/*LANG*/'Programmable': {
|
||||||
value: settings.blerepl,
|
value: settings.blerepl,
|
||||||
format: boolFormat,
|
|
||||||
onchange: () => {
|
onchange: () => {
|
||||||
settings.blerepl = !settings.blerepl;
|
settings.blerepl = !settings.blerepl;
|
||||||
updateSettings();
|
updateSettings();
|
||||||
|
@ -428,7 +422,6 @@ function showLCDMenu() {
|
||||||
},
|
},
|
||||||
/*LANG*/'Wake on BTN1': {
|
/*LANG*/'Wake on BTN1': {
|
||||||
value: settings.options.wakeOnBTN1,
|
value: settings.options.wakeOnBTN1,
|
||||||
format: boolFormat,
|
|
||||||
onchange: () => {
|
onchange: () => {
|
||||||
settings.options.wakeOnBTN1 = !settings.options.wakeOnBTN1;
|
settings.options.wakeOnBTN1 = !settings.options.wakeOnBTN1;
|
||||||
updateOptions();
|
updateOptions();
|
||||||
|
@ -439,7 +432,6 @@ function showLCDMenu() {
|
||||||
Object.assign(lcdMenu, {
|
Object.assign(lcdMenu, {
|
||||||
/*LANG*/'Wake on BTN2': {
|
/*LANG*/'Wake on BTN2': {
|
||||||
value: settings.options.wakeOnBTN2,
|
value: settings.options.wakeOnBTN2,
|
||||||
format: boolFormat,
|
|
||||||
onchange: () => {
|
onchange: () => {
|
||||||
settings.options.wakeOnBTN2 = !settings.options.wakeOnBTN2;
|
settings.options.wakeOnBTN2 = !settings.options.wakeOnBTN2;
|
||||||
updateOptions();
|
updateOptions();
|
||||||
|
@ -447,7 +439,6 @@ function showLCDMenu() {
|
||||||
},
|
},
|
||||||
/*LANG*/'Wake on BTN3': {
|
/*LANG*/'Wake on BTN3': {
|
||||||
value: settings.options.wakeOnBTN3,
|
value: settings.options.wakeOnBTN3,
|
||||||
format: boolFormat,
|
|
||||||
onchange: () => {
|
onchange: () => {
|
||||||
settings.options.wakeOnBTN3 = !settings.options.wakeOnBTN3;
|
settings.options.wakeOnBTN3 = !settings.options.wakeOnBTN3;
|
||||||
updateOptions();
|
updateOptions();
|
||||||
|
@ -456,7 +447,6 @@ function showLCDMenu() {
|
||||||
Object.assign(lcdMenu, {
|
Object.assign(lcdMenu, {
|
||||||
/*LANG*/'Wake on FaceUp': {
|
/*LANG*/'Wake on FaceUp': {
|
||||||
value: settings.options.wakeOnFaceUp,
|
value: settings.options.wakeOnFaceUp,
|
||||||
format: boolFormat,
|
|
||||||
onchange: () => {
|
onchange: () => {
|
||||||
settings.options.wakeOnFaceUp = !settings.options.wakeOnFaceUp;
|
settings.options.wakeOnFaceUp = !settings.options.wakeOnFaceUp;
|
||||||
updateOptions();
|
updateOptions();
|
||||||
|
@ -464,7 +454,6 @@ function showLCDMenu() {
|
||||||
},
|
},
|
||||||
/*LANG*/'Wake on Touch': {
|
/*LANG*/'Wake on Touch': {
|
||||||
value: settings.options.wakeOnTouch,
|
value: settings.options.wakeOnTouch,
|
||||||
format: boolFormat,
|
|
||||||
onchange: () => {
|
onchange: () => {
|
||||||
settings.options.wakeOnTouch = !settings.options.wakeOnTouch;
|
settings.options.wakeOnTouch = !settings.options.wakeOnTouch;
|
||||||
updateOptions();
|
updateOptions();
|
||||||
|
@ -472,7 +461,6 @@ function showLCDMenu() {
|
||||||
},
|
},
|
||||||
/*LANG*/'Wake on Twist': {
|
/*LANG*/'Wake on Twist': {
|
||||||
value: settings.options.wakeOnTwist,
|
value: settings.options.wakeOnTwist,
|
||||||
format: boolFormat,
|
|
||||||
onchange: () => {
|
onchange: () => {
|
||||||
settings.options.wakeOnTwist = !settings.options.wakeOnTwist;
|
settings.options.wakeOnTwist = !settings.options.wakeOnTwist;
|
||||||
updateOptions();
|
updateOptions();
|
||||||
|
|
|
@ -17,3 +17,5 @@
|
||||||
0.14: Reduce update interval of current time when seconds are not shown
|
0.14: Reduce update interval of current time when seconds are not shown
|
||||||
Limit logging on Bangle.js 1 to one day due to low memory
|
Limit logging on Bangle.js 1 to one day due to low memory
|
||||||
Add plot logged data to settings
|
Add plot logged data to settings
|
||||||
|
0.15: Convert Yes/No On/Off in settings to checkboxes
|
||||||
|
0.16: Fix Keep alarm enabled inverted settings
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "sleepphasealarm",
|
"id": "sleepphasealarm",
|
||||||
"name": "SleepPhaseAlarm",
|
"name": "SleepPhaseAlarm",
|
||||||
"shortName": "SleepPhaseAlarm",
|
"shortName": "SleepPhaseAlarm",
|
||||||
"version": "0.14",
|
"version": "0.16",
|
||||||
"description": "Uses the accelerometer to estimate sleep and wake states with the principle of Estimation of Stationary Sleep-segments (ESS, see https://ubicomp.eti.uni-siegen.de/home/datasets/ichi14/index.html.en). This app will read the next alarm from the alarm application and will wake you up to 30 minutes early at the best guessed time when you are almost already awake.",
|
"description": "Uses the accelerometer to estimate sleep and wake states with the principle of Estimation of Stationary Sleep-segments (ESS, see https://ubicomp.eti.uni-siegen.de/home/datasets/ichi14/index.html.en). This app will read the next alarm from the alarm application and will wake you up to 30 minutes early at the best guessed time when you are almost already awake.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,alarm",
|
"tags": "tool,alarm",
|
||||||
|
|
|
@ -90,10 +90,9 @@
|
||||||
E.showMenu({
|
E.showMenu({
|
||||||
"" : { "title" : "SleepPhaseAlarm" },
|
"" : { "title" : "SleepPhaseAlarm" },
|
||||||
'Keep alarm enabled': {
|
'Keep alarm enabled': {
|
||||||
value: !!config.settings.disableAlarm,
|
value: !config.settings.disableAlarm,
|
||||||
format: v => v?"No":"Yes",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
config.settings.disableAlarm = v;
|
config.settings.disableAlarm = !v;
|
||||||
writeSettings();
|
writeSettings();
|
||||||
}
|
}
|
||||||
}, "< Back" : () => back(),
|
}, "< Back" : () => back(),
|
||||||
|
|
|
@ -9,3 +9,4 @@
|
||||||
0.09: Added button control toggle and other live controls to new settings screen.
|
0.09: Added button control toggle and other live controls to new settings screen.
|
||||||
0.10: Tell clock widgets to hide.
|
0.10: Tell clock widgets to hide.
|
||||||
0.11: Added new styling and watch faces
|
0.11: Added new styling and watch faces
|
||||||
|
0.12: Convert Yes/No On/Off in settings to checkboxes
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "slidingtext",
|
"id": "slidingtext",
|
||||||
"name": "Sliding Clock",
|
"name": "Sliding Clock",
|
||||||
"version": "0.11",
|
"version": "0.12",
|
||||||
"description": "Inspired by the Pebble sliding clock, old times are scrolled off the screen and new times on. You are also able to change language on the fly so you can see the time written in other languages using button 1. Currently English, French, Japanese, Spanish and German are supported",
|
"description": "Inspired by the Pebble sliding clock, old times are scrolled off the screen and new times on. You are also able to change language on the fly so you can see the time written in other languages using button 1. Currently English, French, Japanese, Spanish and German are supported",
|
||||||
"icon": "slidingtext.png",
|
"icon": "slidingtext.png",
|
||||||
"screenshots": [{"url":"slidingtext-screenshot.english.png"},{"url":"slidingtext-screenshot.english2.png"},{"url":"slidingtext-screenshot.hybrid.png"}],
|
"screenshots": [{"url":"slidingtext-screenshot.english.png"},{"url":"slidingtext-screenshot.english2.png"},{"url":"slidingtext-screenshot.hybrid.png"}],
|
||||||
|
|
|
@ -173,8 +173,7 @@
|
||||||
"Colour": stringInSettings("color_scheme", ["black","white", "red","grey","purple","blue"]),
|
"Colour": stringInSettings("color_scheme", ["black","white", "red","grey","purple","blue"]),
|
||||||
"Style": stringInSettings("date_format", locales, (l)=>locale_mappings[l] ),
|
"Style": stringInSettings("date_format", locales, (l)=>locale_mappings[l] ),
|
||||||
"Live Control": {
|
"Live Control": {
|
||||||
value: (settings.enable_live_controls !== undefined ? settings.enable_live_controls : true),
|
value: (settings.enable_live_controls !== undefined ? !!settings.enable_live_controls : true),
|
||||||
format: v => v ? "On" : "Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.enable_live_controls = v;
|
settings.enable_live_controls = v;
|
||||||
writeSettings();
|
writeSettings();
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
'': {'title': 'SloMo Clock'},
|
'': {'title': 'SloMo Clock'},
|
||||||
'< Back': back,
|
'< Back': back,
|
||||||
'Colours' : function() { E.showMenu(colMenu); }
|
'Colours' : function() { E.showMenu(colMenu); }
|
||||||
//,'Widget Space Top' : {value : settings.widTop, format : v => v?"On":"Off",onchange : () => { settings.widTop = !settings.widTop; writeSettings(); }
|
//,'Widget Space Top' : {value : settings.widTop, onchange : () => { settings.widTop = !settings.widTop; writeSettings(); }
|
||||||
//,'Widget Space Bottom' : {value : settings.widBot, format : v => v?"On":"Off",onchange : () => { settings.widBot = !settings.widBot; writeSettings(); }
|
//,'Widget Space Bottom' : {value : settings.widBot, onchange : () => { settings.widBot = !settings.widBot; writeSettings(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
const colMenu = {
|
const colMenu = {
|
||||||
|
|
|
@ -6,3 +6,4 @@
|
||||||
0.06: Added adjustment for Bangle.js magnetometer heading fix
|
0.06: Added adjustment for Bangle.js magnetometer heading fix
|
||||||
0.07: Add settings file with the option to disable the slow direction updates
|
0.07: Add settings file with the option to disable the slow direction updates
|
||||||
0.08: Use tilt compensation from new magnav library
|
0.08: Use tilt compensation from new magnav library
|
||||||
|
0.09: Convert Yes/No On/Off in settings to checkboxes
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "waypointer",
|
"id": "waypointer",
|
||||||
"name": "Way Pointer",
|
"name": "Way Pointer",
|
||||||
"version": "0.08",
|
"version": "0.09",
|
||||||
"description": "Navigate to a waypoint using the GPS for bearing and compass to point way, uses the same waypoint interface as GPS Navigation",
|
"description": "Navigate to a waypoint using the GPS for bearing and compass to point way, uses the same waypoint interface as GPS Navigation",
|
||||||
"icon": "waypointer.png",
|
"icon": "waypointer.png",
|
||||||
"tags": "tool,outdoors,gps",
|
"tags": "tool,outdoors,gps",
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
"< Back" : () => back(),
|
"< Back" : () => back(),
|
||||||
'Smooth arrow rot': {
|
'Smooth arrow rot': {
|
||||||
value: !!settings.smoothDirection,
|
value: !!settings.smoothDirection,
|
||||||
format: v => v?"Yes":"No",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.smoothDirection = v;
|
settings.smoothDirection = v;
|
||||||
writeSettings();
|
writeSettings();
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Charging Status
|
||||||
|
|
||||||
|
This widget shows a yellow lightning icon to indicate that the watch is charging. A short buzz is also emitted on initial charging connection.
|
||||||
|
Nothing is shown when not charging.
|
||||||
|
|
||||||
|
# TypeScript
|
||||||
|
|
||||||
|
This app is written in TypeScript, see [typescript/README.md](/typescript/README.md) for more info
|
|
@ -5,3 +5,4 @@
|
||||||
0.03: Fix Bell not appearing on alarms > 24h and redrawing interval
|
0.03: Fix Bell not appearing on alarms > 24h and redrawing interval
|
||||||
Update to match the default alarm widget, and not show itself when an alarm is hidden.
|
Update to match the default alarm widget, and not show itself when an alarm is hidden.
|
||||||
0.04: Fix check for active alarm
|
0.04: Fix check for active alarm
|
||||||
|
0.05: Convert Yes/No On/Off in settings to checkboxes
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "widalarmeta",
|
"id": "widalarmeta",
|
||||||
"name": "Alarm & Timer ETA",
|
"name": "Alarm & Timer ETA",
|
||||||
"shortName": "Alarm ETA",
|
"shortName": "Alarm ETA",
|
||||||
"version": "0.04",
|
"version": "0.05",
|
||||||
"description": "A widget that displays the time to the next Alarm or Timer in hours and minutes, maximum 24h (configurable).",
|
"description": "A widget that displays the time to the next Alarm or Timer in hours and minutes, maximum 24h (configurable).",
|
||||||
"icon": "widget.png",
|
"icon": "widget.png",
|
||||||
"type": "widget",
|
"type": "widget",
|
||||||
|
|
|
@ -26,7 +26,6 @@
|
||||||
},
|
},
|
||||||
/*LANG*/'Draw bell': {
|
/*LANG*/'Draw bell': {
|
||||||
value: !!settings.drawBell,
|
value: !!settings.drawBell,
|
||||||
format: v => v?/*LANG*/"Yes":/*LANG*/"No",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.drawBell = v;
|
settings.drawBell = v;
|
||||||
writeSettings();
|
writeSettings();
|
||||||
|
|
|
@ -7,3 +7,4 @@
|
||||||
0.08: Ensure battery updates every 60s even if LCD was on at boot and stays on
|
0.08: Ensure battery updates every 60s even if LCD was on at boot and stays on
|
||||||
0.09: Misc speed/memory tweaks
|
0.09: Misc speed/memory tweaks
|
||||||
0.10: Color changes due to the battery level
|
0.10: Color changes due to the battery level
|
||||||
|
0.11: Change level for medium charge (50% -> 40%), and darken color on light themes as yellow was almost invisible
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "widbat",
|
"id": "widbat",
|
||||||
"name": "Battery Level Widget",
|
"name": "Battery Level Widget",
|
||||||
"version": "0.10",
|
"version": "0.11",
|
||||||
"description": "Show the current battery level and charging status in the top right of the clock",
|
"description": "Show the current battery level and charging status in the top right of the clock",
|
||||||
"icon": "widget.png",
|
"icon": "widget.png",
|
||||||
"type": "widget",
|
"type": "widget",
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
g.setColor(g.theme.fg).fillRect(x,y+2,x+s-4,y+21).clearRect(x+2,y+4,x+s-6,y+19).fillRect(x+s-3,y+10,x+s,y+14);
|
g.setColor(g.theme.fg).fillRect(x,y+2,x+s-4,y+21).clearRect(x+2,y+4,x+s-6,y+19).fillRect(x+s-3,y+10,x+s,y+14);
|
||||||
var battery = E.getBattery();
|
var battery = E.getBattery();
|
||||||
if(battery < 20) {g.setColor("#f00");}
|
if(battery < 20) {g.setColor("#f00");}
|
||||||
else if (battery < 50) {g.setColor("#ff0");}
|
else if (battery < 40) {g.setColor(g.theme.dark ? "#ff0" : "#f80");}
|
||||||
else {g.setColor("#0f0");}
|
else {g.setColor("#0f0");}
|
||||||
g.fillRect(x+4,y+6,x+4+battery*(s-12)/100,y+17);
|
g.fillRect(x+4,y+6,x+4+battery*(s-12)/100,y+17);
|
||||||
}};
|
}};
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
0.01: Initial version
|
0.01: Initial version
|
||||||
0.02: Checks for correct firmware; E.setDST(...) moved to boot.js
|
0.02: Checks for correct firmware; E.setDST(...) moved to boot.js
|
||||||
|
0.03: Convert Yes/No On/Off in settings to checkboxes
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{ "id": "widdst",
|
{ "id": "widdst",
|
||||||
"name": "Daylight Saving",
|
"name": "Daylight Saving",
|
||||||
"version":"0.02",
|
"version":"0.03",
|
||||||
"description": "Widget to set daylight saving rules. Requires Espruino 2v14.49 or later - see the instructions below for more information.",
|
"description": "Widget to set daylight saving rules. Requires Espruino 2v14.49 or later - see the instructions below for more information.",
|
||||||
"icon": "icon.png",
|
"icon": "icon.png",
|
||||||
"type": "widget",
|
"type": "widget",
|
||||||
|
|
|
@ -138,16 +138,14 @@
|
||||||
},
|
},
|
||||||
"< Back": () => back(),
|
"< Back": () => back(),
|
||||||
/*LANG*/"Enabled": {
|
/*LANG*/"Enabled": {
|
||||||
value: settings.has_dst,
|
value: !!settings.has_dst,
|
||||||
format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.has_dst = v;
|
settings.has_dst = v;
|
||||||
writeSettings();
|
writeSettings();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/*LANG*/"Icon": {
|
/*LANG*/"Icon": {
|
||||||
value: settings.show_icon,
|
value: !!settings.show_icon,
|
||||||
format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.show_icon = v;
|
settings.show_icon = v;
|
||||||
writeSettings();
|
writeSettings();
|
||||||
|
|
|
@ -148,7 +148,7 @@
|
||||||
</label>
|
</label>
|
||||||
<label class="form-switch">
|
<label class="form-switch">
|
||||||
<input type="checkbox" id="settings-minify">
|
<input type="checkbox" id="settings-minify">
|
||||||
<i class="form-icon"></i> Minify apps before upload (smaller, faster apps - BETA)
|
<i class="form-icon"></i> Minify apps before upload (BETA, not recommended. Uploads smaller, faster apps but this may cause some apps to stop working)
|
||||||
</label>
|
</label>
|
||||||
<label class="form-switch">
|
<label class="form-switch">
|
||||||
<input type="checkbox" id="settings-settime">
|
<input type="checkbox" id="settings-settime">
|
||||||
|
|
330
lang/pt_BR.json
330
lang/pt_BR.json
|
@ -1,167 +1,167 @@
|
||||||
{
|
{
|
||||||
"//": "Portuguese Brasil language translations",
|
"//": "Portuguese Brasil language translations",
|
||||||
"GLOBAL": {
|
"GLOBAL": {
|
||||||
"//": "Translations that apply for all apps",
|
"//": "Translations that apply for all apps",
|
||||||
"New Timer": "Novo Temporizador",
|
"New Timer": "Novo Temporizador",
|
||||||
"New Alarm": "Novo Alarme",
|
"New Alarm": "Novo Alarme",
|
||||||
"Auto snooze": "Soneca automática",
|
"Auto snooze": "Soneca automática",
|
||||||
"week": "semana",
|
"week": "semana",
|
||||||
"circle 3": "círculo 3",
|
"circle 3": "círculo 3",
|
||||||
"(repeat)": "(repetir)",
|
"(repeat)": "(repetir)",
|
||||||
"Save": "Salvar",
|
"Save": "Salvar",
|
||||||
"Keep Msgs": "Manter Msgs",
|
"Keep Msgs": "Manter Msgs",
|
||||||
"music": "música",
|
"music": "música",
|
||||||
"circle 4": "círculo 4",
|
"circle 4": "círculo 4",
|
||||||
"circle 2": "círculo 2",
|
"circle 2": "círculo 2",
|
||||||
"circle count": "contagem em círculo",
|
"circle count": "contagem em círculo",
|
||||||
"circle 1": "círculo 1",
|
"circle 1": "círculo 1",
|
||||||
"battery warn": "aviso de bateria",
|
"battery warn": "aviso de bateria",
|
||||||
"show widgets": "exibir widgets",
|
"show widgets": "exibir widgets",
|
||||||
"data": "dados",
|
"data": "dados",
|
||||||
"heartrate": "frequência cardíaca",
|
"heartrate": "frequência cardíaca",
|
||||||
"distance goal": "distancia de chegada",
|
"distance goal": "distancia de chegada",
|
||||||
"Circle": "Círculo",
|
"Circle": "Círculo",
|
||||||
"colorize icon": "colorir ícone",
|
"colorize icon": "colorir ícone",
|
||||||
"min. confidence": "min. confiança",
|
"min. confidence": "min. confiança",
|
||||||
"minimum": "mínimo",
|
"minimum": "mínimo",
|
||||||
"maximum": "máximo",
|
"maximum": "máximo",
|
||||||
"Heartrate": "Frequência Cardíaca",
|
"Heartrate": "Frequência Cardíaca",
|
||||||
"weather circle": "círculo meteorológico",
|
"weather circle": "círculo meteorológico",
|
||||||
"step length": "comprimento do passo",
|
"step length": "comprimento do passo",
|
||||||
"valid period": "período válido",
|
"valid period": "período válido",
|
||||||
"TAP right top/bottom": "TAP superior/inferior direita",
|
"TAP right top/bottom": "TAP superior/inferior direita",
|
||||||
"Vector font size": "Tamanho de letra vectorial",
|
"Vector font size": "Tamanho de letra vectorial",
|
||||||
"Yes\ndefinitely": "Sim\ndefinitivamente",
|
"Yes\ndefinitely": "Sim\ndefinitivamente",
|
||||||
"BTNs 1:startlap 2:exit 3:reset": "BTNs 1:startlap 2:exit 3:reset",
|
"BTNs 1:startlap 2:exit 3:reset": "BTNs 1:startlap 2:exit 3:reset",
|
||||||
"STEPS": "ETAPAS",
|
"STEPS": "ETAPAS",
|
||||||
"Font": "Fonte",
|
"Font": "Fonte",
|
||||||
"Show clocks": "Mostrar relógios",
|
"Show clocks": "Mostrar relógios",
|
||||||
"App Source\nNot found": "Fonte do aplicativo\nNão encontrado",
|
"App Source\nNot found": "Fonte do aplicativo\nNão encontrado",
|
||||||
"Mark Unread": "Marcar como não lido",
|
"Mark Unread": "Marcar como não lido",
|
||||||
"View Message": "Ver Mensagem",
|
"View Message": "Ver Mensagem",
|
||||||
"start&lap/reset, BTN1: EXIT": "start&lap/reset, BTN1: SAÍDA",
|
"start&lap/reset, BTN1: EXIT": "start&lap/reset, BTN1: SAÍDA",
|
||||||
"Launcher Settings": "Configurações do Launcher",
|
"Launcher Settings": "Configurações do Launcher",
|
||||||
"Delete All Messages": "Apagar todas as mensagens",
|
"Delete All Messages": "Apagar todas as mensagens",
|
||||||
"Delete all messages": "Apagar todas as mensagens",
|
"Delete all messages": "Apagar todas as mensagens",
|
||||||
"Utils": "Utils",
|
"Utils": "Utils",
|
||||||
"LCD": "LCD",
|
"LCD": "LCD",
|
||||||
"Apps": "Apps",
|
"Apps": "Apps",
|
||||||
"Record Run": "Gravar Corrida",
|
"Record Run": "Gravar Corrida",
|
||||||
"No Messages": "Sem Mensagens",
|
"No Messages": "Sem Mensagens",
|
||||||
"Unread timer": "Temporizador não visto",
|
"Unread timer": "Temporizador não visto",
|
||||||
"Are you sure": "Tem certeza",
|
"Are you sure": "Tem certeza",
|
||||||
"Make Connectable": "Habilitar Conexão",
|
"Make Connectable": "Habilitar Conexão",
|
||||||
"Piezo": "Piezo",
|
"Piezo": "Piezo",
|
||||||
"Bluetooth": "Bluetooth",
|
"Bluetooth": "Bluetooth",
|
||||||
"BLE": "BLE",
|
"BLE": "BLE",
|
||||||
"Programmable": "Programável",
|
"Programmable": "Programável",
|
||||||
"Vibration": "Vibração",
|
"Vibration": "Vibração",
|
||||||
"Quiet Mode": "Modo Silencioso",
|
"Quiet Mode": "Modo Silencioso",
|
||||||
"Foreground": "Primeiro plano",
|
"Foreground": "Primeiro plano",
|
||||||
"Passkey BETA": "Senha BETA",
|
"Passkey BETA": "Senha BETA",
|
||||||
"HID": "HID",
|
"HID": "HID",
|
||||||
"Light BW": "BW Leve",
|
"Light BW": "BW Leve",
|
||||||
"Foreground 2": "Primeiro plano 2",
|
"Foreground 2": "Primeiro plano 2",
|
||||||
"Dark BW": "BW Escuro",
|
"Dark BW": "BW Escuro",
|
||||||
"Background": "Plano de Fundo",
|
"Background": "Plano de Fundo",
|
||||||
"Highlight FG": "Destaque FG",
|
"Highlight FG": "Destaque FG",
|
||||||
"Customize": "Personalizar",
|
"Customize": "Personalizar",
|
||||||
"Background 2": "Plano de Fundo 2",
|
"Background 2": "Plano de Fundo 2",
|
||||||
"Wake on BTN3": "Acordar no BTN3",
|
"Wake on BTN3": "Acordar no BTN3",
|
||||||
"Wake on BTN2": "Acordar no BTN2",
|
"Wake on BTN2": "Acordar no BTN2",
|
||||||
"Highlight BG": "Destaque BG",
|
"Highlight BG": "Destaque BG",
|
||||||
"LCD Timeout": "Tempo limite do LCD",
|
"LCD Timeout": "Tempo limite do LCD",
|
||||||
"Wake on FaceUp": "Acordar no FaceUp",
|
"Wake on FaceUp": "Acordar no FaceUp",
|
||||||
"Wake on BTN1": "Acordar no BTN1",
|
"Wake on BTN1": "Acordar no BTN1",
|
||||||
"Wake on Twist": "Acordar ao Balançar",
|
"Wake on Twist": "Acordar ao Balançar",
|
||||||
"Wake on Touch": "Acordar ao Tocar",
|
"Wake on Touch": "Acordar ao Tocar",
|
||||||
"Connect device\nto add to\nwhitelist": "Ligar dispositivo\npara adicionar a\nWhitelist",
|
"Connect device\nto add to\nwhitelist": "Ligar dispositivo\npara adicionar a\nWhitelist",
|
||||||
"Remove": "Remover",
|
"Remove": "Remover",
|
||||||
"Add Device": "Adicionar dispositivo",
|
"Add Device": "Adicionar dispositivo",
|
||||||
"LCD Brightness": "Luminosidade do LCD",
|
"LCD Brightness": "Luminosidade do LCD",
|
||||||
"Twist Max Y": "Max Y do Balanço",
|
"Twist Max Y": "Max Y do Balanço",
|
||||||
"Utilities": "Utilidades",
|
"Utilities": "Utilidades",
|
||||||
"Twist Threshold": "Limiar do Balanço",
|
"Twist Threshold": "Limiar do Balanço",
|
||||||
"Time Zone": "Fuso horário",
|
"Time Zone": "Fuso horário",
|
||||||
"Twist Timeout": "Timeout do Balanço",
|
"Twist Timeout": "Timeout do Balanço",
|
||||||
"Clock Style": "Estilo do Relógio",
|
"Clock Style": "Estilo do Relógio",
|
||||||
"Debug Info": "Debug Infos",
|
"Debug Info": "Debug Infos",
|
||||||
"Log": "Logs",
|
"Log": "Logs",
|
||||||
"Storage": "Armazenamento",
|
"Storage": "Armazenamento",
|
||||||
"Rewrite Settings": "Re-escrever Configurações",
|
"Rewrite Settings": "Re-escrever Configurações",
|
||||||
"Compacting...\nTakes approx\n1 minute": "Compactando...\nLeva aproximadamente\n1 minuto",
|
"Compacting...\nTakes approx\n1 minute": "Compactando...\nLeva aproximadamente\n1 minuto",
|
||||||
"Flatten Battery": "Drenar Bateria",
|
"Flatten Battery": "Drenar Bateria",
|
||||||
"Reset Settings": "Resetar Configurações",
|
"Reset Settings": "Resetar Configurações",
|
||||||
"Compact Storage": "Armazenamento compacto",
|
"Compact Storage": "Armazenamento compacto",
|
||||||
"Stay Connectable": "Manter Conectavel",
|
"Stay Connectable": "Manter Conectavel",
|
||||||
"Turn Off": "Desativar",
|
"Turn Off": "Desativar",
|
||||||
"Connectable": "Conectável",
|
"Connectable": "Conectável",
|
||||||
"This will remove everything": "Esta ação irá apagar tudo",
|
"This will remove everything": "Esta ação irá apagar tudo",
|
||||||
"Date": "Data",
|
"Date": "Data",
|
||||||
"Month": "Mês",
|
"Month": "Mês",
|
||||||
"Second": "Segundo",
|
"Second": "Segundo",
|
||||||
"Minute": "Minuto",
|
"Minute": "Minuto",
|
||||||
"Flattening battery - this can take hours.\nLong-press button to cancel": "Drenando Bateria - isto pode demorar horas.\nSegure o Botão para Cancelar",
|
"Flattening battery - this can take hours.\nLong-press button to cancel": "Drenando Bateria - isto pode demorar horas.\nSegure o Botão para Cancelar",
|
||||||
"Reset to Defaults": "Resetar Dispositivo",
|
"Reset to Defaults": "Resetar Dispositivo",
|
||||||
"Hour": "Hora",
|
"Hour": "Hora",
|
||||||
"No Clocks Found": "Não encontramos nenhum Relogio",
|
"No Clocks Found": "Não encontramos nenhum Relogio",
|
||||||
"Right": "Confirmar",
|
"Right": "Confirmar",
|
||||||
"No app has settings": "Nenhum aplicativo possui configurações",
|
"No app has settings": "Nenhum aplicativo possui configurações",
|
||||||
"App Settings": "Configurações do aplicativo",
|
"App Settings": "Configurações do aplicativo",
|
||||||
"OFF": "DESLIGADO",
|
"OFF": "DESLIGADO",
|
||||||
"Side": "Lado",
|
"Side": "Lado",
|
||||||
"Left": "Esquerda",
|
"Left": "Esquerda",
|
||||||
"Sort Order": "Ordem de classificação",
|
"Sort Order": "Ordem de classificação",
|
||||||
"Widgets": "Widgets",
|
"Widgets": "Widgets",
|
||||||
"Invalid settings": "Configurações inválidas",
|
"Invalid settings": "Configurações inválidas",
|
||||||
"Sleep Phase Alarm": "Alarme da Fase do Sono",
|
"Sleep Phase Alarm": "Alarme da Fase do Sono",
|
||||||
"Alarm": "Alarme",
|
"Alarm": "Alarme",
|
||||||
"Minutes": "Minutos",
|
"Minutes": "Minutos",
|
||||||
"TIMER": "TIMER",
|
"TIMER": "TIMER",
|
||||||
"Hours": "Horário",
|
"Hours": "Horário",
|
||||||
"on": "em",
|
"on": "em",
|
||||||
"Reset All": "Resetar tudo",
|
"Reset All": "Resetar tudo",
|
||||||
"Repeat": "Repetir",
|
"Repeat": "Repetir",
|
||||||
"Delete": "Deletar",
|
"Delete": "Deletar",
|
||||||
"Enabled": "Habilitado",
|
"Enabled": "Habilitado",
|
||||||
"Reset all widgets": "Redefinir todos os widgets",
|
"Reset all widgets": "Redefinir todos os widgets",
|
||||||
"Reset": "Resetar",
|
"Reset": "Resetar",
|
||||||
"goal": "meta",
|
"goal": "meta",
|
||||||
"Message": "Mensagem",
|
"Message": "Mensagem",
|
||||||
"Beep": "Bip",
|
"Beep": "Bip",
|
||||||
"Vibrate": "Vibrar",
|
"Vibrate": "Vibrar",
|
||||||
"System": "Sistema",
|
"System": "Sistema",
|
||||||
"Alerts": "Alertas",
|
"Alerts": "Alertas",
|
||||||
"Locale": "Localização",
|
"Locale": "Localização",
|
||||||
"Set Time": "Tempo Definido",
|
"Set Time": "Tempo Definido",
|
||||||
"Whitelist": "Whitelist",
|
"Whitelist": "Whitelist",
|
||||||
"Select Clock": "Selecionar Relógio",
|
"Select Clock": "Selecionar Relógio",
|
||||||
"BACK": "VOLTAR",
|
"BACK": "VOLTAR",
|
||||||
"Timer": "Temporizador",
|
"Timer": "Temporizador",
|
||||||
"Error in settings": "Erro nas Configurações",
|
"Error in settings": "Erro nas Configurações",
|
||||||
"Disable": "Desativar",
|
"Disable": "Desativar",
|
||||||
"Factory Reset": "Reset de Fábrica",
|
"Factory Reset": "Reset de Fábrica",
|
||||||
"Connected": "Conectado",
|
"Connected": "Conectado",
|
||||||
"ALARM": "ALARME",
|
"ALARM": "ALARME",
|
||||||
"Sleep": "Dormir",
|
"Sleep": "Dormir",
|
||||||
"Messages": "Mensagens",
|
"Messages": "Mensagens",
|
||||||
"Hide": "Esconder",
|
"Hide": "Esconder",
|
||||||
"Show": "Mostrar",
|
"Show": "Mostrar",
|
||||||
"On": "Ativo",
|
"On": "Ativo",
|
||||||
"Ok": "Ok",
|
"Ok": "Ok",
|
||||||
"No": "Não",
|
"No": "Não",
|
||||||
"Settings": "Configurações",
|
"Settings": "Configurações",
|
||||||
"steps": "passos",
|
"steps": "passos",
|
||||||
"back": "voltar",
|
"back": "voltar",
|
||||||
"Steps": "Passos",
|
"Steps": "Passos",
|
||||||
"Year": "Ano",
|
"Year": "Ano",
|
||||||
"Yes": "Sim",
|
"Yes": "Sim",
|
||||||
"Loading": "Carregando",
|
"Loading": "Carregando",
|
||||||
"Music": "Música",
|
"Music": "Música",
|
||||||
"color": "cor",
|
"color": "cor",
|
||||||
"off": "desativado",
|
"off": "desativado",
|
||||||
"Off": "Desativado",
|
"Off": "Desativado",
|
||||||
"Theme": "Tema",
|
"Theme": "Tema",
|
||||||
"Back": "Voltar"
|
"Back": "Voltar"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -93,6 +93,16 @@ var state = {
|
||||||
// list of active stats (indexed by ID)
|
// list of active stats (indexed by ID)
|
||||||
var stats = {};
|
var stats = {};
|
||||||
|
|
||||||
|
const DATA_FILE = "exstats.json";
|
||||||
|
// Load the state from a saved file if there was one
|
||||||
|
state = Object.assign(state, require("Storage").readJSON(DATA_FILE,1)||{});
|
||||||
|
// force step history to a uint8array
|
||||||
|
state.stepHistory = new Uint8Array(state.stepHistory);
|
||||||
|
// when we exit, write the current state
|
||||||
|
E.on('kill', function() {
|
||||||
|
require("Storage").writeJSON(DATA_FILE, state);
|
||||||
|
});
|
||||||
|
|
||||||
// distance between 2 lat and lons, in meters, Mean Earth Radius = 6371km
|
// distance between 2 lat and lons, in meters, Mean Earth Radius = 6371km
|
||||||
// https://www.movable-type.co.uk/scripts/latlong.html
|
// https://www.movable-type.co.uk/scripts/latlong.html
|
||||||
// (Equirectangular approximation)
|
// (Equirectangular approximation)
|
||||||
|
@ -359,9 +369,10 @@ exports.getStats = function(statIDs, options) {
|
||||||
state.notify.time.next = state.startTime + options.notify.time.increment;
|
state.notify.time.next = state.startTime + options.notify.time.increment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reset();
|
if (!state.active) reset(); // we might already be active
|
||||||
return {
|
return {
|
||||||
stats : stats, state : state,
|
stats : stats,
|
||||||
|
state : state,
|
||||||
start : function() {
|
start : function() {
|
||||||
state.active = true;
|
state.active = true;
|
||||||
reset();
|
reset();
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
"removeComments": true,
|
"removeComments": true,
|
||||||
|
|
||||||
"newLine": "lf",
|
"newLine": "lf",
|
||||||
"noEmitHelpers": false,
|
"noEmitHelpers": true, // we link to specific banglejs implementations
|
||||||
"noEmitOnError": false,
|
"noEmitOnError": false,
|
||||||
"preserveConstEnums": false,
|
"preserveConstEnums": false,
|
||||||
"importsNotUsedAsValues": "error",
|
"importsNotUsedAsValues": "error",
|
||||||
|
|
|
@ -44,6 +44,7 @@ declare module ClockInfo {
|
||||||
w: number,
|
w: number,
|
||||||
h: number,
|
h: number,
|
||||||
draw(itm: MenuItem, info: Item, options: InteractiveOptions): void,
|
draw(itm: MenuItem, info: Item, options: InteractiveOptions): void,
|
||||||
|
app?: string, // used to remember clock_info locations, per app
|
||||||
};
|
};
|
||||||
|
|
||||||
type InteractiveOptions =
|
type InteractiveOptions =
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue