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.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.06: Convert Yes/No On/Off in settings to checkboxes
|
||||
|
|
|
@ -26,8 +26,7 @@ function showMenu() {
|
|||
viewLogs();
|
||||
},
|
||||
/*LANG*/"Log raw data" : {
|
||||
value : logRawData,
|
||||
format : v => v?/*LANG*/"Yes":/*LANG*/"No",
|
||||
value : !!logRawData,
|
||||
onchange : v => { logRawData=v; }
|
||||
},
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "accellog",
|
||||
"name": "Acceleration Logger",
|
||||
"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",
|
||||
"icon": "app.png",
|
||||
"tags": "outdoor",
|
||||
|
|
|
@ -8,3 +8,4 @@
|
|||
0.08: Use default Bangle formatter for booleans
|
||||
0.09: New app screen (instead of showing settings or the alert) and some optimisations
|
||||
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) {
|
||||
Bangle.buzz(400);
|
||||
}
|
||||
|
||||
if ((storage.readJSON('activityreminder.s.json', 1) || {}).unlock) {
|
||||
Bangle.setLocked(false);
|
||||
Bangle.setLCDPower(1);
|
||||
}
|
||||
|
||||
setTimeout(load, 20000);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"name": "Activity Reminder",
|
||||
"shortName":"Activity Reminder",
|
||||
"description": "A reminder to take short walks for the ones with a sedentary lifestyle",
|
||||
"version":"0.10",
|
||||
"version":"0.11",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
"tags": "tool,activity",
|
||||
|
|
|
@ -75,7 +75,14 @@
|
|||
settings.tempThreshold = v;
|
||||
activityreminder.writeSettings(settings);
|
||||
}
|
||||
},
|
||||
'Unlock on alarm': {
|
||||
value: !!settings.unlock,
|
||||
onchange: v => {
|
||||
settings.unlock = v;
|
||||
activityreminder.writeSettings(settings);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return mainMenu;
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
var mainmenu = {
|
||||
"" : { "title" : "Android" },
|
||||
"< 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({
|
||||
"" : { "title" : /*LANG*/"Find Phone" },
|
||||
"< 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.
|
||||
|
||||
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
|
||||
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";
|
||||
var __assign = (this && this.__assign) || function () {
|
||||
__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 __assign = Object.assign;
|
||||
var Layout = require("Layout");
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// ts helpers:
|
||||
const __assign = Object.assign;
|
||||
|
||||
const Layout = require("Layout") as Layout_.Layout;
|
||||
|
||||
Bangle.loadWidgets();
|
||||
|
|
|
@ -96,7 +96,7 @@ function draw(){
|
|||
if (!isNaN(bt.battery)) layout.btBattery.label = bt.battery + "%";
|
||||
if (bt.rr) layout.btRR.label = bt.rr.join(",");
|
||||
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";
|
||||
} else {
|
||||
layout.bt.label = "--";
|
||||
|
|
|
@ -9,3 +9,7 @@ Tap to start, tap again to pause. Tap again to restart
|
|||
## Requests
|
||||
|
||||
[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.05: Improved colors (connected vs disconnected)
|
||||
0.06: Tell clock widgets to hide.
|
||||
0.07: Convert Yes/No On/Off in settings to checkboxes
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "clockcal",
|
||||
"name": "Clock & Calendar",
|
||||
"version": "0.06",
|
||||
"version": "0.07",
|
||||
"description": "Clock with Calendar",
|
||||
"readme":"README.md",
|
||||
"icon": "app.png",
|
||||
|
|
|
@ -106,18 +106,11 @@
|
|||
writeSettings();
|
||||
}
|
||||
},
|
||||
'Load deafauls?': {
|
||||
value: 0,
|
||||
min: 0, max: 1,
|
||||
format: v => ["No", "Yes"][v],
|
||||
onchange: v => {
|
||||
if (v == 1) {
|
||||
'Load defaults': () => {
|
||||
settings = defaults;
|
||||
writeSettings();
|
||||
load();
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
// Show the menu
|
||||
E.showMenu(menu);
|
||||
|
|
|
@ -6,3 +6,4 @@
|
|||
0.06: Now read wheel rev as well as cadence sensor
|
||||
Improve connection code
|
||||
0.07: Make Bangle.js 2 compatible
|
||||
0.08: Convert Yes/No On/Off in settings to checkboxes
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "cscsensor",
|
||||
"name": "Cycling speed sensor",
|
||||
"shortName": "CSCSensor",
|
||||
"version": "0.07",
|
||||
"version": "0.08",
|
||||
"description": "Read BLE enabled cycling speed and cadence sensor and display readings on watch",
|
||||
"icon": "icons8-cycling-48.png",
|
||||
"tags": "outdoors,exercise,ble,bluetooth",
|
||||
|
|
|
@ -23,17 +23,17 @@
|
|||
}
|
||||
}
|
||||
const menu = {
|
||||
'': { 'title': 'Cycle speed sensor' },
|
||||
'': { 'title': /*LANG*/'Cycle speed sensor' },
|
||||
'< Back': back,
|
||||
'Wheel circ.(mm)': {
|
||||
/*LANG*/'Wheel circ.(mm)': {
|
||||
value: s.wheelcirc,
|
||||
min: 800,
|
||||
max: 2400,
|
||||
step: 5,
|
||||
onchange: save('wheelcirc'),
|
||||
},
|
||||
'Reset total distance': function() {
|
||||
E.showPrompt("Zero total distance?", {buttons: {"No":false, "Yes":true}}).then(function(v) {
|
||||
/*LANG*/'Reset total distance': function() {
|
||||
E.showPrompt(/*LANG*/"Zero total distance?", {buttons: {/*LANG*/"No":false, /*LANG*/"Yes":true}}).then(function(v) {
|
||||
if (v) {
|
||||
s['totaldist'] = 0;
|
||||
storage.write(SETTINGS_FILE, s);
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
0.01: New App!
|
||||
0.02: Add lightning
|
||||
0.03: Convert Yes/No On/Off in settings to checkboxes
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{ "id": "f9lander",
|
||||
"name": "Falcon9 Lander",
|
||||
"shortName":"F9lander",
|
||||
"version":"0.02",
|
||||
"version":"0.03",
|
||||
"description": "Land a rocket booster",
|
||||
"icon": "f9lander.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
|
||||
*/
|
||||
const boolFormat = v => v ? /*LANG*/"On" : /*LANG*/"Off";
|
||||
(function(back) {
|
||||
const SETTINGS_FILE = 'f9settings.json'
|
||||
// initialize with default settings...
|
||||
|
@ -27,8 +26,7 @@ const boolFormat = v => v ? /*LANG*/"On" : /*LANG*/"Off";
|
|||
'': { 'title': 'OpenWind' },
|
||||
'< Back': back,
|
||||
'Lightning': {
|
||||
value: settings.lightning,
|
||||
format: boolFormat,
|
||||
value: !!settings.lightning,
|
||||
onchange: save('lightning'),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ var m;
|
|||
var files;
|
||||
|
||||
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 (fn.charCodeAt(fn.length-1)==1) {
|
||||
var fh = STOR.open(fn.substr(0, fn.length-1), "r");
|
||||
|
|
|
@ -24,13 +24,13 @@
|
|||
var mainmenu = {
|
||||
"" : { "title" : "Gadgetbridge" },
|
||||
"< Back" : back,
|
||||
"Connected" : { value : NRF.getSecurityStatus().connected?"Yes":"No" },
|
||||
"Show Icon" : {
|
||||
/*LANG*/"Connected" : { value : NRF.getSecurityStatus().connected?/*LANG*/"Yes":/*LANG*/"No" },
|
||||
/*LANG*/"Show Icon" : {
|
||||
value: settings().showIcon,
|
||||
onchange: setIcon
|
||||
},
|
||||
"Find Phone" : function() { E.showMenu(findPhone); },
|
||||
"Record HRM" : {
|
||||
/*LANG*/"Find Phone" : function() { E.showMenu(findPhone); },
|
||||
/*LANG*/"Record HRM" : {
|
||||
value: !!settings().hrm,
|
||||
onchange: v => updateSetting('hrm', v)
|
||||
}
|
||||
|
@ -38,8 +38,8 @@
|
|||
|
||||
var findPhone = {
|
||||
"" : { "title" : "-- Find Phone --" },
|
||||
"On" : _=>gb({t:"findPhone",n:true}),
|
||||
"Off" : _=>gb({t:"findPhone",n:false}),
|
||||
/*LANG*/"On" : _=>gb({t:"findPhone",n:true}),
|
||||
/*LANG*/"Off" : _=>gb({t:"findPhone",n:false}),
|
||||
"< Back" : function() { E.showMenu(mainmenu); },
|
||||
};
|
||||
|
||||
|
|
|
@ -73,3 +73,5 @@
|
|||
* 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.
|
||||
* Splash screen while waiting for gps signal.
|
||||
|
||||
0.17: Convert Yes/No On/Off in settings to checkboxes
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "gipy",
|
||||
"name": "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.",
|
||||
"allow_emulator":false,
|
||||
"icon": "gipy.png",
|
||||
|
|
|
@ -19,8 +19,7 @@
|
|||
"< Back": () => back(),
|
||||
"keep gps alive": {
|
||||
value: !!settings.keep_gps_alive, // !! converts undefined to false
|
||||
format: (v) => (v ? "Yes" : "No"),
|
||||
onchange: (v) => {
|
||||
onchange: v => {
|
||||
settings.keep_gps_alive = v;
|
||||
writeSettings();
|
||||
},
|
||||
|
|
|
@ -46,8 +46,8 @@ var mainMenu = {
|
|||
"Reset Homework": function() {
|
||||
E.showPrompt("Are you sure you want to delete homework.txt?", {
|
||||
buttons: {
|
||||
"No": false,
|
||||
"Yes": true
|
||||
/*LANG*/"No": false,
|
||||
/*LANG*/"Yes": true
|
||||
}
|
||||
}).then(function(v) {
|
||||
if (v) {
|
||||
|
|
|
@ -21,11 +21,11 @@ var screens = [
|
|||
name: "Hardware",
|
||||
items: [
|
||||
{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: "Bluetooth", fun: () => NRF.getSecurityStatus().connected ? "Conn" : "NoConn"},
|
||||
{name: "GPS", fun: () => Bangle.isGPSOn() ? "On" : "Off"},
|
||||
{name: "Compass", fun: () => Bangle.isCompassOn() ? "On" : "Off"},
|
||||
{name: "Bluetooth", fun: () => NRF.getSecurityStatus().connected ? /*LANG*/"Conn" : /*LANG*/"NoConn"},
|
||||
{name: "GPS", fun: () => Bangle.isGPSOn() ? /*LANG*/"On" : /*LANG*/"Off"},
|
||||
{name: "Compass", fun: () => Bangle.isCompassOn() ? /*LANG*/"On" : /*LANG*/"Off"},
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
0.01: New App!
|
||||
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",
|
||||
"name": "Touch keyboard",
|
||||
"version":"0.02",
|
||||
"version":"0.03",
|
||||
"description": "A library for text input via onscreen keyboard",
|
||||
"icon": "app.png",
|
||||
"type":"textinput",
|
||||
|
|
|
@ -25,22 +25,16 @@
|
|||
onchange: v => updateSetting("textSize", v)
|
||||
},
|
||||
/*LANG*/'Offset keyboard': {
|
||||
value: settings().offsetKeyboard,
|
||||
min: 0, max: 1,
|
||||
format: v => [/*LANG*/"No",/*LANG*/"Yes"][v],
|
||||
onchange: v => updateSetting("offsetKeyboard", v)
|
||||
value: !!settings().offsetKeyboard,
|
||||
onchange: v => updateSetting("offsetKeyboard", v?1:0)
|
||||
},
|
||||
/*LANG*/'Loop around': {
|
||||
value: settings().loopAround,
|
||||
min: 0, max: 1,
|
||||
format: v => [/*LANG*/"No",/*LANG*/"Yes"][v],
|
||||
onchange: v => updateSetting("loopAround", v)
|
||||
value: !!settings().loopAround,
|
||||
onchange: v => updateSetting("loopAround", v?1:0)
|
||||
},
|
||||
/*LANG*/'One-to-one input and release to select': {
|
||||
value: settings().oneToOne,
|
||||
min: 0, max: 1,
|
||||
format: v => [/*LANG*/"No",/*LANG*/"Yes"][v],
|
||||
onchange: v => updateSetting("oneToOne", v)
|
||||
value: !!settings().oneToOne,
|
||||
onchange: v => updateSetting("oneToOne", v?1:0)
|
||||
},
|
||||
/*LANG*/'Speed scaling': {
|
||||
value: settings().speedScaling,
|
||||
|
@ -49,10 +43,8 @@
|
|||
onchange: v => updateSetting("speedScaling", v)
|
||||
}
|
||||
///*LANG*/'Release to select': {
|
||||
// value: 1|settings().fontSize,
|
||||
// min: 0, max: 1,
|
||||
// format: v => [/*LANG*/"No",/*LANG*/"Yes"][v],
|
||||
// onchange: v => updateSetting("releaseToSelect", v)
|
||||
// value: !!(1|settings().fontSize),
|
||||
// onchange: v => updateSetting("releaseToSelect", v?1:0)
|
||||
//}
|
||||
};
|
||||
E.showMenu(mainmenu);
|
||||
|
|
|
@ -73,7 +73,7 @@ let lastTemp = 0;
|
|||
|
||||
const phone = {
|
||||
get status() {
|
||||
return NRF.getSecurityStatus().connected ? "Yes" : "No";
|
||||
return NRF.getSecurityStatus().connected ? /*LANG*/"Yes" : /*LANG*/"No";
|
||||
},
|
||||
message: null,
|
||||
messageTimeout: null,
|
||||
|
|
|
@ -87,3 +87,4 @@
|
|||
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.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) || {};
|
||||
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 (Bangle.CLOCK && msg.state && msg.title && appSettings.openMusic) loadMessages = true;
|
||||
else return;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "messagegui",
|
||||
"name": "Message UI",
|
||||
"shortName": "Messages",
|
||||
"version": "0.64",
|
||||
"version": "0.65",
|
||||
"description": "Default app to display notifications from iOS and Gadgetbridge/Android",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
|
|
|
@ -3,3 +3,4 @@
|
|||
0.03: Fix icons broken in 0v02 (#2386)
|
||||
Store all icons in a separate binary file (much faster lookup)
|
||||
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;
|
||||
const s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase();
|
||||
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
|
||||
// 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/
|
||||
|
@ -107,6 +108,7 @@ exports.getColor = function(msg,options) {
|
|||
"google home": "#fbbc05",
|
||||
// "home assistant": "#41bdf5", // ha-blue is #41bdf5, but that's the background
|
||||
"instagram": "#ff0069", // https://about.instagram.com/brand/gradient
|
||||
"jira": "#0052cc", //https://atlassian.design/resources/logo-library
|
||||
"lieferando": "#ff8000",
|
||||
"linkedin": "#0a66c2", // https://brand.linkedin.com/
|
||||
"messenger": "#0078ff",
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
{ "app":"google play store", "icon":"google play store.png" },
|
||||
{ "app":"home assistant", "icon":"home assistant.png" },
|
||||
{ "app":"instagram", "icon":"instagram.png" },
|
||||
{ "app":"jira", "icon":"jira.png" },
|
||||
{ "app":"kalender", "icon":"kalender.png" },
|
||||
{ "app":"keep notes", "icon":"google keep.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);
|
||||
let s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase();
|
||||
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);
|
||||
};
|
||||
|
||||
|
@ -13,6 +13,7 @@ exports.getColor = function(msg,options) {
|
|||
if (st.iconColorMode == 'mono') return options.default;
|
||||
const s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase();
|
||||
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
|
||||
// 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/
|
||||
|
@ -33,6 +34,7 @@ exports.getColor = function(msg,options) {
|
|||
"google home": "#fbbc05",
|
||||
// "home assistant": "#41bdf5", // ha-blue is #41bdf5, but that's the background
|
||||
"instagram": "#ff0069", // https://about.instagram.com/brand/gradient
|
||||
"jira": "#0052cc", //https://atlassian.design/resources/logo-library
|
||||
"lieferando": "#ff8000",
|
||||
"linkedin": "#0a66c2", // https://brand.linkedin.com/
|
||||
"messenger": "#0078ff",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "messageicons",
|
||||
"name": "Message Icons",
|
||||
"version": "0.04",
|
||||
"version": "0.05",
|
||||
"description": "Library containing a list of icons and colors for apps",
|
||||
"icon": "app.png",
|
||||
"type": "module",
|
||||
|
|
|
@ -3,4 +3,5 @@
|
|||
0.03: Optional show lock status via color
|
||||
0.04: Ensure that widgets are always hidden in fullscreen mode
|
||||
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",
|
||||
"name": "Neon X & IO X Clock",
|
||||
"shortName": "Neon X Clock",
|
||||
"version": "0.06",
|
||||
"version": "0.07",
|
||||
"description": "Pebble Neon X & Neon IO X for Bangle.js",
|
||||
"icon": "neonx.png",
|
||||
"type": "clock",
|
||||
|
|
|
@ -25,11 +25,9 @@
|
|||
"" : { "title":"Neon X & IO"},
|
||||
"< Back": back,
|
||||
"Neon IO X": {
|
||||
value: 0 | neonXSettings.io,
|
||||
min: 0, max: 1,
|
||||
format: v => v ? "On" : "Off",
|
||||
value: !!neonXSettings.io,
|
||||
onchange: v => {
|
||||
neonXSettings.io = v;
|
||||
neonXSettings.io = v?1:0;
|
||||
updateSettings();
|
||||
}
|
||||
},
|
||||
|
@ -43,27 +41,23 @@
|
|||
}
|
||||
},
|
||||
"Date on touch": {
|
||||
value: 0 | neonXSettings.showDate,
|
||||
min: 0, max: 1,
|
||||
format: v => v ? "On" : "Off",
|
||||
value: !!neonXSettings.showDate,
|
||||
onchange: v => {
|
||||
neonXSettings.showDate = v;
|
||||
neonXSettings.showDate = v?1:0;
|
||||
updateSettings();
|
||||
}
|
||||
},
|
||||
'Fullscreen': {
|
||||
value: false | neonXSettings.fullscreen,
|
||||
format: () => (neonXSettings.fullscreen ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
neonXSettings.fullscreen = !neonXSettings.fullscreen;
|
||||
value: !!neonXSettings.fullscreen,
|
||||
onchange: v => {
|
||||
neonXSettings.fullscreen = v;
|
||||
updateSettings();
|
||||
},
|
||||
},
|
||||
'Show lock': {
|
||||
value: false | neonXSettings.showLock,
|
||||
format: () => (neonXSettings.showLock ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
neonXSettings.showLock = !neonXSettings.showLock;
|
||||
value: !!neonXSettings.showLock,
|
||||
onchange: v => {
|
||||
neonXSettings.showLock = v;
|
||||
updateSettings();
|
||||
},
|
||||
},
|
||||
|
|
|
@ -8,3 +8,4 @@
|
|||
0.08: Add new draw styles, tidy up draw functionality
|
||||
0.09: Tweak for faster rendering
|
||||
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",
|
||||
"name": "Numerals Clock",
|
||||
"shortName": "Numerals Clock",
|
||||
"version": "0.10",
|
||||
"version": "0.11",
|
||||
"description": "A simple big numerals clock",
|
||||
"icon": "numerals.png",
|
||||
"type": "clock",
|
||||
|
|
|
@ -30,10 +30,8 @@
|
|||
onchange: v=> { numeralsSettings.drawMode=dm[v]; updateSettings();}
|
||||
},
|
||||
"Date on touch": {
|
||||
value: 0|numeralsSettings.showDate,
|
||||
min:0,max:1,
|
||||
format: v=>v?"On":"Off",
|
||||
onchange: v=> { numeralsSettings.showDate=v; updateSettings();}
|
||||
value: !!numeralsSettings.showDate,
|
||||
onchange: v=> { numeralsSettings.showDate=v?1:0; updateSettings();}
|
||||
},
|
||||
"< back": back
|
||||
};
|
||||
|
|
|
@ -4,3 +4,8 @@
|
|||
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
|
||||
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
|
||||
* 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
|
||||
|
||||
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
|
||||
|
||||
* 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
|
||||
|
||||
|
|
|
@ -4,10 +4,115 @@
|
|||
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){
|
||||
var chargingInterval;
|
||||
|
||||
function handleCharging(charging){
|
||||
let handleCharging = (charging) => {
|
||||
if (charging){
|
||||
if (chargingInterval) clearInterval(chargingInterval);
|
||||
chargingInterval = setInterval(()=>{
|
||||
|
@ -20,7 +125,7 @@
|
|||
clearInterval(chargingInterval);
|
||||
chargingInterval = undefined;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Bangle.on("charging",handleCharging);
|
||||
handleCharging(Bangle.isCharging());
|
||||
|
|
|
@ -2,5 +2,6 @@
|
|||
"warnEnabled": false,
|
||||
"warn": 96,
|
||||
"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",
|
||||
"name": "Power Manager",
|
||||
"shortName": "Power Manager",
|
||||
"version": "0.05",
|
||||
"version": "0.08",
|
||||
"description": "Allow configuration of warnings and thresholds for battery charging and display.",
|
||||
"icon": "app.png",
|
||||
"type": "bootloader",
|
||||
"tags": "tool",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"interface": "interface.html",
|
||||
"storage": [
|
||||
{"name":"powermanager.boot.js","url":"boot.js"},
|
||||
{"name":"powermanager.settings.js","url":"settings.js"},
|
||||
{"name":"powermanager.wid.js","url":"widget.js"},
|
||||
{"name":"powermanager","url":"lib.js"},
|
||||
{"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'
|
||||
},
|
||||
"< Back" : back,
|
||||
'Widget': function() {
|
||||
E.showMenu(submenu_widget);
|
||||
},
|
||||
'Monotonic percentage': {
|
||||
value: !!settings.forceMonoPercentage,
|
||||
onchange: v => {
|
||||
|
@ -41,6 +44,9 @@
|
|||
},
|
||||
'Calibrate': function() {
|
||||
E.showMenu(submenu_calibrate);
|
||||
},
|
||||
'Logging': function() {
|
||||
E.showMenu(submenu_logging);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -100,7 +106,6 @@
|
|||
},
|
||||
'Enabled': {
|
||||
value: !!settings.warnEnabled,
|
||||
format: v => settings.warnEnabled ? "On" : "Off",
|
||||
onchange: 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);
|
||||
})
|
||||
|
|
|
@ -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.20: Automatic translation of some more strings.
|
||||
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();},
|
||||
/*LANG*/'RECORD': {
|
||||
value: !!settings.recording,
|
||||
format: v=>v?/*LANG*/"On":/*LANG*/"Off",
|
||||
onchange: v => {
|
||||
setTimeout(function() {
|
||||
E.showMenu();
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "recorder",
|
||||
"name": "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.",
|
||||
"icon": "app.png",
|
||||
"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.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.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);
|
||||
// ---------------------------
|
||||
|
||||
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
|
||||
function onStartStop() {
|
||||
var running = !exs.state.active;
|
||||
|
@ -77,12 +84,9 @@ function onStartStop() {
|
|||
} else {
|
||||
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
|
||||
// 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:[
|
||||
{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:"STOP", id:"status", fillx:1 }
|
||||
{type:"txt", font:fontHeading, label:"---", id:"status", fillx:1 }
|
||||
]});
|
||||
// Now calculate the layout
|
||||
var layout = new Layout( {
|
||||
type:"v", c: lc
|
||||
},{lazy:true, btns:[{ label:"START", cb: onStartStop, id:"button"}]});
|
||||
},{lazy:true, btns:[{ label:"---", cb: onStartStop, id:"button"}]});
|
||||
delete lc;
|
||||
setStatus(exs.state.active);
|
||||
layout.render();
|
||||
|
||||
function configureNotification(stat) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{ "id": "run",
|
||||
"name": "Run",
|
||||
"version":"0.14",
|
||||
"version":"0.15",
|
||||
"description": "Displays distance, time, steps, cadence, pace and more for runners.",
|
||||
"icon": "app.png",
|
||||
"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.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)
|
||||
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.
|
||||
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);
|
||||
// ---------------------------
|
||||
|
||||
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
|
||||
function onStartStop() {
|
||||
let running = !exs.state.active;
|
||||
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
|
||||
// Do this first in case recorder needs to prompt for
|
||||
// an overwrite before we start tracking exstats
|
||||
|
@ -88,12 +95,9 @@ function onStartStop() {
|
|||
} else {
|
||||
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
|
||||
// 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:[
|
||||
{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:"STOP", id:"status", fillx:1 }
|
||||
{type:"txt", font:fontHeading, label:"---", id:"status", fillx:1 }
|
||||
]});
|
||||
// Now calculate the layout
|
||||
let layout = new Layout( {
|
||||
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;
|
||||
setStatus(exs.state.active);
|
||||
layout.render();
|
||||
|
||||
function configureNotification(stat) {
|
||||
|
|
|
@ -63,3 +63,4 @@
|
|||
0.56: make System menu items shorter and more consistant, Eg 'Clock', intead
|
||||
of 'Select Clock'
|
||||
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",
|
||||
"name": "Settings",
|
||||
"version": "0.57",
|
||||
"version": "0.58",
|
||||
"description": "A menu for setting up Bangle.js",
|
||||
"icon": "settings.png",
|
||||
"tags": "tool,system",
|
||||
|
|
|
@ -64,8 +64,6 @@ if (("object" != typeof settings) ||
|
|||
("object" != typeof settings.options))
|
||||
resetSettings();
|
||||
|
||||
const boolFormat = v => v ? /*LANG*/"On" : /*LANG*/"Off";
|
||||
|
||||
function showMainMenu() {
|
||||
|
||||
const mainmenu = {
|
||||
|
@ -102,7 +100,6 @@ function showAlertsMenu() {
|
|||
if (BANGLEJS2) {
|
||||
beepMenuItem = {
|
||||
value: settings.beep!=false,
|
||||
format: boolFormat,
|
||||
onchange: v => {
|
||||
settings.beep = v;
|
||||
updateSettings();
|
||||
|
@ -134,7 +131,6 @@ function showAlertsMenu() {
|
|||
/*LANG*/'Beep': beepMenuItem,
|
||||
/*LANG*/'Vibration': {
|
||||
value: settings.vibrate,
|
||||
format: boolFormat,
|
||||
onchange: () => {
|
||||
settings.vibrate = !settings.vibrate;
|
||||
updateSettings();
|
||||
|
@ -169,7 +165,6 @@ function showBLEMenu() {
|
|||
/*LANG*/'Make Connectable': ()=>makeConnectable(),
|
||||
/*LANG*/'BLE': {
|
||||
value: settings.ble,
|
||||
format: boolFormat,
|
||||
onchange: () => {
|
||||
settings.ble = !settings.ble;
|
||||
updateSettings();
|
||||
|
@ -177,7 +172,6 @@ function showBLEMenu() {
|
|||
},
|
||||
/*LANG*/'Programmable': {
|
||||
value: settings.blerepl,
|
||||
format: boolFormat,
|
||||
onchange: () => {
|
||||
settings.blerepl = !settings.blerepl;
|
||||
updateSettings();
|
||||
|
@ -428,7 +422,6 @@ function showLCDMenu() {
|
|||
},
|
||||
/*LANG*/'Wake on BTN1': {
|
||||
value: settings.options.wakeOnBTN1,
|
||||
format: boolFormat,
|
||||
onchange: () => {
|
||||
settings.options.wakeOnBTN1 = !settings.options.wakeOnBTN1;
|
||||
updateOptions();
|
||||
|
@ -439,7 +432,6 @@ function showLCDMenu() {
|
|||
Object.assign(lcdMenu, {
|
||||
/*LANG*/'Wake on BTN2': {
|
||||
value: settings.options.wakeOnBTN2,
|
||||
format: boolFormat,
|
||||
onchange: () => {
|
||||
settings.options.wakeOnBTN2 = !settings.options.wakeOnBTN2;
|
||||
updateOptions();
|
||||
|
@ -447,7 +439,6 @@ function showLCDMenu() {
|
|||
},
|
||||
/*LANG*/'Wake on BTN3': {
|
||||
value: settings.options.wakeOnBTN3,
|
||||
format: boolFormat,
|
||||
onchange: () => {
|
||||
settings.options.wakeOnBTN3 = !settings.options.wakeOnBTN3;
|
||||
updateOptions();
|
||||
|
@ -456,7 +447,6 @@ function showLCDMenu() {
|
|||
Object.assign(lcdMenu, {
|
||||
/*LANG*/'Wake on FaceUp': {
|
||||
value: settings.options.wakeOnFaceUp,
|
||||
format: boolFormat,
|
||||
onchange: () => {
|
||||
settings.options.wakeOnFaceUp = !settings.options.wakeOnFaceUp;
|
||||
updateOptions();
|
||||
|
@ -464,7 +454,6 @@ function showLCDMenu() {
|
|||
},
|
||||
/*LANG*/'Wake on Touch': {
|
||||
value: settings.options.wakeOnTouch,
|
||||
format: boolFormat,
|
||||
onchange: () => {
|
||||
settings.options.wakeOnTouch = !settings.options.wakeOnTouch;
|
||||
updateOptions();
|
||||
|
@ -472,7 +461,6 @@ function showLCDMenu() {
|
|||
},
|
||||
/*LANG*/'Wake on Twist': {
|
||||
value: settings.options.wakeOnTwist,
|
||||
format: boolFormat,
|
||||
onchange: () => {
|
||||
settings.options.wakeOnTwist = !settings.options.wakeOnTwist;
|
||||
updateOptions();
|
||||
|
|
|
@ -17,3 +17,5 @@
|
|||
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
|
||||
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",
|
||||
"name": "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.",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,alarm",
|
||||
|
|
|
@ -90,10 +90,9 @@
|
|||
E.showMenu({
|
||||
"" : { "title" : "SleepPhaseAlarm" },
|
||||
'Keep alarm enabled': {
|
||||
value: !!config.settings.disableAlarm,
|
||||
format: v => v?"No":"Yes",
|
||||
value: !config.settings.disableAlarm,
|
||||
onchange: v => {
|
||||
config.settings.disableAlarm = v;
|
||||
config.settings.disableAlarm = !v;
|
||||
writeSettings();
|
||||
}
|
||||
}, "< Back" : () => back(),
|
||||
|
|
|
@ -9,3 +9,4 @@
|
|||
0.09: Added button control toggle and other live controls to new settings screen.
|
||||
0.10: Tell clock widgets to hide.
|
||||
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",
|
||||
"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",
|
||||
"icon": "slidingtext.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"]),
|
||||
"Style": stringInSettings("date_format", locales, (l)=>locale_mappings[l] ),
|
||||
"Live Control": {
|
||||
value: (settings.enable_live_controls !== undefined ? settings.enable_live_controls : true),
|
||||
format: v => v ? "On" : "Off",
|
||||
value: (settings.enable_live_controls !== undefined ? !!settings.enable_live_controls : true),
|
||||
onchange: v => {
|
||||
settings.enable_live_controls = v;
|
||||
writeSettings();
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
'': {'title': 'SloMo Clock'},
|
||||
'< Back': back,
|
||||
'Colours' : function() { E.showMenu(colMenu); }
|
||||
//,'Widget Space Top' : {value : settings.widTop, format : v => v?"On":"Off",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 Top' : {value : settings.widTop, onchange : () => { settings.widTop = !settings.widTop; writeSettings(); }
|
||||
//,'Widget Space Bottom' : {value : settings.widBot, onchange : () => { settings.widBot = !settings.widBot; writeSettings(); }
|
||||
};
|
||||
|
||||
const colMenu = {
|
||||
|
|
|
@ -6,3 +6,4 @@
|
|||
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.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",
|
||||
"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",
|
||||
"icon": "waypointer.png",
|
||||
"tags": "tool,outdoors,gps",
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
"< Back" : () => back(),
|
||||
'Smooth arrow rot': {
|
||||
value: !!settings.smoothDirection,
|
||||
format: v => v?"Yes":"No",
|
||||
onchange: v => {
|
||||
settings.smoothDirection = v;
|
||||
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
|
||||
Update to match the default alarm widget, and not show itself when an alarm is hidden.
|
||||
0.04: Fix check for active alarm
|
||||
0.05: Convert Yes/No On/Off in settings to checkboxes
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "widalarmeta",
|
||||
"name": "Alarm & Timer 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).",
|
||||
"icon": "widget.png",
|
||||
"type": "widget",
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
},
|
||||
/*LANG*/'Draw bell': {
|
||||
value: !!settings.drawBell,
|
||||
format: v => v?/*LANG*/"Yes":/*LANG*/"No",
|
||||
onchange: v => {
|
||||
settings.drawBell = v;
|
||||
writeSettings();
|
||||
|
|
|
@ -7,3 +7,4 @@
|
|||
0.08: Ensure battery updates every 60s even if LCD was on at boot and stays on
|
||||
0.09: Misc speed/memory tweaks
|
||||
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",
|
||||
"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",
|
||||
"icon": "widget.png",
|
||||
"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);
|
||||
var battery = E.getBattery();
|
||||
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");}
|
||||
g.fillRect(x+4,y+6,x+4+battery*(s-12)/100,y+17);
|
||||
}};
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
0.01: Initial version
|
||||
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",
|
||||
"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.",
|
||||
"icon": "icon.png",
|
||||
"type": "widget",
|
||||
|
|
|
@ -138,16 +138,14 @@
|
|||
},
|
||||
"< Back": () => back(),
|
||||
/*LANG*/"Enabled": {
|
||||
value: settings.has_dst,
|
||||
format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
|
||||
value: !!settings.has_dst,
|
||||
onchange: v => {
|
||||
settings.has_dst = v;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
/*LANG*/"Icon": {
|
||||
value: settings.show_icon,
|
||||
format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
|
||||
value: !!settings.show_icon,
|
||||
onchange: v => {
|
||||
settings.show_icon = v;
|
||||
writeSettings();
|
||||
|
|
|
@ -148,7 +148,7 @@
|
|||
</label>
|
||||
<label class="form-switch">
|
||||
<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 class="form-switch">
|
||||
<input type="checkbox" id="settings-settime">
|
||||
|
|
|
@ -93,6 +93,16 @@ var state = {
|
|||
// list of active stats (indexed by ID)
|
||||
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
|
||||
// https://www.movable-type.co.uk/scripts/latlong.html
|
||||
// (Equirectangular approximation)
|
||||
|
@ -359,9 +369,10 @@ exports.getStats = function(statIDs, options) {
|
|||
state.notify.time.next = state.startTime + options.notify.time.increment;
|
||||
}
|
||||
}
|
||||
reset();
|
||||
if (!state.active) reset(); // we might already be active
|
||||
return {
|
||||
stats : stats, state : state,
|
||||
stats : stats,
|
||||
state : state,
|
||||
start : function() {
|
||||
state.active = true;
|
||||
reset();
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
"removeComments": true,
|
||||
|
||||
"newLine": "lf",
|
||||
"noEmitHelpers": false,
|
||||
"noEmitHelpers": true, // we link to specific banglejs implementations
|
||||
"noEmitOnError": false,
|
||||
"preserveConstEnums": false,
|
||||
"importsNotUsedAsValues": "error",
|
||||
|
|
|
@ -44,6 +44,7 @@ declare module ClockInfo {
|
|||
w: number,
|
||||
h: number,
|
||||
draw(itm: MenuItem, info: Item, options: InteractiveOptions): void,
|
||||
app?: string, // used to remember clock_info locations, per app
|
||||
};
|
||||
|
||||
type InteractiveOptions =
|
||||
|
|
|
@ -8688,6 +8688,15 @@ interface ObjectConstructor {
|
|||
*/
|
||||
entries(object: any): Array<[string, any]>;
|
||||
|
||||
/**
|
||||
* Transforms an array of key-value pairs into an object
|
||||
*
|
||||
* @param {any} entries - An array of `[key,value]` pairs to be used to create an object
|
||||
* @returns {any} An object containing all the specified pairs
|
||||
* @url http://www.espruino.com/Reference#l_Object_fromEntries
|
||||
*/
|
||||
fromEntries(entries: any): any;
|
||||
|
||||
/**
|
||||
* Creates a new object with the specified prototype object and properties.
|
||||
* properties are currently unsupported.
|
||||
|
@ -8709,6 +8718,15 @@ interface ObjectConstructor {
|
|||
*/
|
||||
getOwnPropertyDescriptor(obj: any, name: any): any;
|
||||
|
||||
/**
|
||||
* Get information on all properties in the object (from `Object.getOwnPropertyDescriptor`), or just `{}` if no properties
|
||||
*
|
||||
* @param {any} obj - The object
|
||||
* @returns {any} An object containing all the property descriptors of an object
|
||||
* @url http://www.espruino.com/Reference#l_Object_getOwnPropertyDescriptors
|
||||
*/
|
||||
getOwnPropertyDescriptors(obj: any): any;
|
||||
|
||||
/**
|
||||
* Add a new property to the Object. 'Desc' is an object with the following fields:
|
||||
* * `configurable` (bool = false) - can this property be changed/deleted (not
|
||||
|
@ -8945,32 +8963,32 @@ interface Function {
|
|||
/**
|
||||
* This executes the function with the supplied 'this' argument and parameters
|
||||
*
|
||||
* @param {any} this - The value to use as the 'this' argument when executing the function
|
||||
* @param {any} thisArg - The value to use as the 'this' argument when executing the function
|
||||
* @param {any} params - Optional Parameters
|
||||
* @returns {any} The return value of executing this function
|
||||
* @url http://www.espruino.com/Reference#l_Function_call
|
||||
*/
|
||||
call(this: any, ...params: any[]): any;
|
||||
call(thisArg: any, ...params: any[]): any;
|
||||
|
||||
/**
|
||||
* This executes the function with the supplied 'this' argument and parameters
|
||||
*
|
||||
* @param {any} this - The value to use as the 'this' argument when executing the function
|
||||
* @param {any} thisArg - The value to use as the 'this' argument when executing the function
|
||||
* @param {any} args - Optional Array of Arguments
|
||||
* @returns {any} The return value of executing this function
|
||||
* @url http://www.espruino.com/Reference#l_Function_apply
|
||||
*/
|
||||
apply(this: any, args: any): any;
|
||||
apply(thisArg: any, args: ArrayLike<any>): any;
|
||||
|
||||
/**
|
||||
* This executes the function with the supplied 'this' argument and parameters
|
||||
*
|
||||
* @param {any} this - The value to use as the 'this' argument when executing the function
|
||||
* @param {any} thisArg - The value to use as the 'this' argument when executing the function
|
||||
* @param {any} params - Optional Default parameters that are prepended to the call
|
||||
* @returns {any} The 'bound' function
|
||||
* @url http://www.espruino.com/Reference#l_Function_bind
|
||||
*/
|
||||
bind(this: any, ...params: any[]): any;
|
||||
bind(thisArg: any, ...params: any[]): any;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue