Merge branch 'espruino:master' into master
|
@ -1,5 +1,10 @@
|
|||
# App Name
|
||||
|
||||
More information on making apps:
|
||||
|
||||
* http://www.espruino.com/Bangle.js+First+App
|
||||
* http://www.espruino.com/Bangle.js+App+Loader
|
||||
|
||||
Describe the app...
|
||||
|
||||
Add screen shots (if possible) to the app folder and link then into this file with 
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: New Widget!
|
|
@ -0,0 +1,27 @@
|
|||
# Clock Info Name
|
||||
|
||||
More info on making Clock Infos and what they are: http://www.espruino.com/Bangle.js+Clock+Info
|
||||
|
||||
Describe the clock info...
|
||||
|
||||
Add screen shots (if possible) to the app folder and link then into this file with 
|
||||
|
||||
## Usage
|
||||
|
||||
Describe how to use it
|
||||
|
||||
## Features
|
||||
|
||||
Name the function
|
||||
|
||||
## Controls
|
||||
|
||||
Name the buttons and what they are used for
|
||||
|
||||
## Requests
|
||||
|
||||
Name who should be contacted for support/update requests
|
||||
|
||||
## Creator
|
||||
|
||||
Your name
|
|
@ -0,0 +1,16 @@
|
|||
(function() {
|
||||
return {
|
||||
name: "Bangle",
|
||||
// img: 24x24px image for this list of items. The default "Bangle" list has its own image so this is not needed
|
||||
items: [
|
||||
{ name : "Item1",
|
||||
get : function() { return { text : "TextOfItem1",
|
||||
// v : 10, min : 0, max : 100, - optional
|
||||
img : atob("GBiBAAAAAAAAAAAYAAD/AAOBwAYAYAwAMAgAEBgAGBAACBCBCDHDjDCBDBAACBAACBhCGAh+EAwYMAYAYAOBwAD/AAAYAAAAAAAAAA==") }},
|
||||
show : function() {},
|
||||
hide : function() {},
|
||||
// run : function() {} optional (called when tapped)
|
||||
}
|
||||
]
|
||||
};
|
||||
}) // must not have a semi-colon!
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
@ -0,0 +1,14 @@
|
|||
{ "id": "7chname",
|
||||
"name": "My clock info's human readable name",
|
||||
"shortName":"Short Name",
|
||||
"version":"0.01",
|
||||
"description": "A detailed description of my clock info",
|
||||
"icon": "icon.png",
|
||||
"type": "clkinfo",
|
||||
"tags": "clkinfo",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"7chname.clkinfo.js","url":"clkinfo.js"}
|
||||
]
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
# Widget Name
|
||||
|
||||
More info on making Widgets and what they are: http://www.espruino.com/Bangle.js+Widgets
|
||||
|
||||
Describe the app...
|
||||
|
||||
Add screen shots (if possible) to the app folder and link then into this file with 
|
||||
|
|
After Width: | Height: | Size: 1.6 KiB |
|
@ -3,7 +3,7 @@
|
|||
"shortName":"Short Name",
|
||||
"version":"0.01",
|
||||
"description": "A detailed description of my great widget",
|
||||
"icon": "widget.png",
|
||||
"icon": "icon.png",
|
||||
"type": "widget",
|
||||
"tags": "widget",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
/* run widgets in their own function scope so they don't interfere with
|
||||
currently-running apps */
|
||||
/* run widgets in their own function scope if they need to define local
|
||||
variables so they don't interfere with currently-running apps */
|
||||
(() => {
|
||||
function draw() {
|
||||
g.reset(); // reset the graphics context to defaults (color/font/etc)
|
||||
// add your code
|
||||
g.drawString("X", this.x, this.y);
|
||||
}
|
||||
|
||||
// add your widget
|
||||
WIDGETS["mywidget"]={
|
||||
area:"tl", // tl (top left), tr (top right), bl (bottom left), br (bottom right), be aware that not all apps support widgets at the bottom of the screen
|
||||
width: 28, // how wide is the widget? You can change this and call Bangle.drawWidgets() to re-layout
|
||||
draw:draw // called to draw the widget
|
||||
draw:function() {
|
||||
g.reset(); // reset the graphics context to defaults (color/font/etc)
|
||||
// add your code
|
||||
g.drawString("X", this.x, this.y);
|
||||
} // called to draw the widget
|
||||
};
|
||||
})()
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: New Widget!
|
|
@ -0,0 +1,48 @@
|
|||
(function() {
|
||||
var heading, cnt;
|
||||
function magHandler(m) {
|
||||
var h = m.heading;
|
||||
if (isNaN(heading) || isNaN(h))
|
||||
heading = h;
|
||||
else {
|
||||
// Average
|
||||
if (Math.abs(heading-h)>180) {
|
||||
if (h<180 && heading>180) h+=360;
|
||||
if (h>180 && heading<180) h-=360;
|
||||
}
|
||||
heading = heading*0.8 + h*0.2;
|
||||
if (heading<0) heading+=360;
|
||||
if (heading>=360) heading-=360;
|
||||
}
|
||||
// only draw 1 in 2 to try and save some power!
|
||||
if (!(1&cnt++)) ci.items[0].emit('redraw');
|
||||
}
|
||||
var ci = {
|
||||
name: "Bangle",
|
||||
items: [
|
||||
{ name : "Compass",
|
||||
get : function() {
|
||||
var g = Graphics.createArrayBuffer(24,24,1,{msb:true});
|
||||
if (isNaN(heading))
|
||||
g.drawLine(8,12,16,12);
|
||||
else
|
||||
g.fillPoly(g.transformVertices([0,-10,4,10,-4,10],{x:12,y:12,rotate:-heading/57}));
|
||||
return { text : isNaN(heading)?"--":Math.round(heading),
|
||||
v : 0|heading, min : 0, max : 360,
|
||||
img : g.asImage("string") }},
|
||||
show : function() {
|
||||
Bangle.setCompassPower(1,"clkinfomag");
|
||||
Bangle.on('mag',magHandler);
|
||||
cnt=0;
|
||||
heading = undefined;
|
||||
},
|
||||
hide : function() {
|
||||
Bangle.removeListener('mag', magHandler);
|
||||
Bangle.setCompassPower(0,"clkinfomag");
|
||||
},
|
||||
run : function() { Bangle.resetCompass(); }
|
||||
}
|
||||
]
|
||||
};
|
||||
return ci;
|
||||
}) // must not have a semi-colon!
|
After Width: | Height: | Size: 1.7 KiB |
|
@ -0,0 +1,12 @@
|
|||
{ "id": "clkinfomag",
|
||||
"name": "Compass Clockinfo",
|
||||
"version":"0.01",
|
||||
"description": "Extra information to add to clock screens. When selected, displays the compass heading and an arrow pointing North",
|
||||
"icon": "icon.png",
|
||||
"type": "clkinfo",
|
||||
"tags": "clkinfo,compass,mag,magnetometer",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"clkinfomag.clkinfo.js","url":"clkinfo.js"}
|
||||
]
|
||||
}
|
|
@ -1,2 +1,3 @@
|
|||
0.01: Moved from modules/clock_info.js
|
||||
0.02: Fix settings page
|
||||
0.03: Reported image for battery now reflects charge level
|
|
@ -53,10 +53,17 @@ exports.load = function() {
|
|||
items: [
|
||||
{ name : "Battery",
|
||||
hasRange : true,
|
||||
get : () => { let v = E.getBattery(); return {
|
||||
text : v + "%", v : v, min:0, max:100,
|
||||
img : atob(Bangle.isCharging() ? "GBiBAAABgAADwAAHwAAPgACfAAHOAAPkBgHwDwP4Hwf8Pg/+fB//OD//kD//wD//4D//8D//4B//QB/+AD/8AH/4APnwAHAAACAAAA==" : "GBiBAAAAAAAAAAAAAAAAAAAAAD//+P///IAAAr//Ar//Ar//A7//A7//A7//A7//Ar//AoAAAv///D//+AAAAAAAAAAAAAAAAAAAAA==")
|
||||
}},
|
||||
get : () => { let v = E.getBattery();
|
||||
var img;
|
||||
if (!Bangle.isCharging()) {
|
||||
var s=24,g = Graphics.createArrayBuffer(24,24,1,{msb:true});
|
||||
g.fillRect(0,6,s-3,18).clearRect(2,8,s-5,16).fillRect(s-2,10,s,15).fillRect(3,9,3+v*(s-9)/100,15);
|
||||
img = g.asImage("string");
|
||||
} else img=atob("GBiBAAABgAADwAAHwAAPgACfAAHOAAPkBgHwDwP4Hwf8Pg/+fB//OD//kD//wD//4D//8D//4B//QB/+AD/8AH/4APnwAHAAACAAAA==");
|
||||
return {
|
||||
text : v + "%", v : v, min:0, max:100, img : img
|
||||
}
|
||||
},
|
||||
show : function() { this.interval = setInterval(()=>this.emit('redraw'), 60000); Bangle.on("charging", batteryUpdateHandler); batteryUpdateHandler(); },
|
||||
hide : function() { clearInterval(this.interval); delete this.interval; Bangle.removeListener("charging", batteryUpdateHandler); },
|
||||
},
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{ "id": "clock_info",
|
||||
"name": "Clock Info Module",
|
||||
"shortName": "Clock Info",
|
||||
"version":"0.02",
|
||||
"version":"0.03",
|
||||
"description": "A library used by clocks to provide extra information on the clock face (Altitude, BPM, etc)",
|
||||
"icon": "app.png",
|
||||
"type": "module",
|
||||
|
|
|
@ -1 +1,4 @@
|
|||
0.01: New app!
|
||||
0.02: Allow boot exceptions, e.g. to load DST
|
||||
0.03: Permit exceptions to load in low-power mode, e.g. daylight saving time.
|
||||
Also avoid polluting global scope.
|
||||
|
|
|
@ -35,14 +35,16 @@ var draw = function () {
|
|||
var dateStr = require("locale").date(date, 0).toUpperCase() +
|
||||
"\n" +
|
||||
require("locale").dow(date, 0).toUpperCase();
|
||||
var x2 = x + 6;
|
||||
var y2 = y + 66;
|
||||
g.reset()
|
||||
.clearRect(Bangle.appRect)
|
||||
.setFont("Vector", 55)
|
||||
.setFontAlign(0, 0)
|
||||
.drawString(timeStr, x, y)
|
||||
.setFont("Vector", 24)
|
||||
.drawString(dateStr, x, y + 56)
|
||||
.drawString("".concat(E.getBattery(), "%"), x, y + 104);
|
||||
.drawString(dateStr, x2, y2)
|
||||
.drawString("".concat(E.getBattery(), "%"), x2, y2 + 48);
|
||||
if (nextDraw)
|
||||
clearTimeout(nextDraw);
|
||||
nextDraw = setTimeout(function () {
|
||||
|
@ -75,9 +77,9 @@ var reload = function () {
|
|||
};
|
||||
reload();
|
||||
Bangle.emit("drained", E.getBattery());
|
||||
var _a = require("Storage").readJSON("".concat(app, ".setting.json"), true) || {}, _b = _a.disableBoot, disableBoot = _b === void 0 ? false : _b, _c = _a.restore, restore = _c === void 0 ? 20 : _c;
|
||||
var _a = require("Storage").readJSON("".concat(app, ".setting.json"), true) || {}, _b = _a.keepStartup, keepStartup = _b === void 0 ? true : _b, _c = _a.restore, restore = _c === void 0 ? 20 : _c, _d = _a.exceptions, exceptions = _d === void 0 ? ["widdst.0"] : _d;
|
||||
function drainedRestore() {
|
||||
if (disableBoot) {
|
||||
if (!keepStartup) {
|
||||
try {
|
||||
eval(require('Storage').read('bootupdate.js'));
|
||||
}
|
||||
|
@ -87,16 +89,28 @@ function drainedRestore() {
|
|||
}
|
||||
load();
|
||||
}
|
||||
if (disableBoot) {
|
||||
var checkCharge_1 = function () {
|
||||
if (E.getBattery() < restore)
|
||||
return;
|
||||
drainedRestore();
|
||||
};
|
||||
if (Bangle.isCharging())
|
||||
checkCharge_1();
|
||||
Bangle.on("charging", function (charging) {
|
||||
if (charging)
|
||||
checkCharge_1();
|
||||
});
|
||||
var checkCharge = function () {
|
||||
if (E.getBattery() < restore)
|
||||
return;
|
||||
drainedRestore();
|
||||
};
|
||||
if (Bangle.isCharging())
|
||||
checkCharge();
|
||||
Bangle.on("charging", function (charging) {
|
||||
if (charging)
|
||||
checkCharge();
|
||||
});
|
||||
if (!keepStartup) {
|
||||
var storage = require("Storage");
|
||||
for (var _i = 0, exceptions_1 = exceptions; _i < exceptions_1.length; _i++) {
|
||||
var boot = exceptions_1[_i];
|
||||
try {
|
||||
var js = storage.read("".concat(boot, ".boot.js"));
|
||||
if (js)
|
||||
eval(js);
|
||||
}
|
||||
catch (e) {
|
||||
console.log("error loading boot exception \"".concat(boot, "\": ").concat(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,8 @@ const draw = () => {
|
|||
const dateStr = require("locale").date(date, 0).toUpperCase() +
|
||||
"\n" +
|
||||
require("locale").dow(date, 0).toUpperCase();
|
||||
const x2 = x + 6;
|
||||
const y2 = y + 66;
|
||||
|
||||
g.reset()
|
||||
.clearRect(Bangle.appRect)
|
||||
|
@ -59,8 +61,8 @@ const draw = () => {
|
|||
.setFontAlign(0, 0)
|
||||
.drawString(timeStr, x, y)
|
||||
.setFont("Vector", 24)
|
||||
.drawString(dateStr, x, y + 56)
|
||||
.drawString(`${E.getBattery()}%`, x, y + 104);
|
||||
.drawString(dateStr, x2, y2)
|
||||
.drawString(`${E.getBattery()}%`, x2, y2 + 48);
|
||||
|
||||
if(nextDraw) clearTimeout(nextDraw);
|
||||
nextDraw = setTimeout(() => {
|
||||
|
@ -97,12 +99,12 @@ reload();
|
|||
Bangle.emit("drained", E.getBattery());
|
||||
|
||||
// restore normal boot on charge
|
||||
const { disableBoot = false, restore = 20 }: DrainedSettings
|
||||
const { keepStartup = true, restore = 20, exceptions = ["widdst.0"] }: DrainedSettings
|
||||
= require("Storage").readJSON(`${app}.setting.json`, true) || {};
|
||||
|
||||
// re-enable normal boot code when we're above a threshold:
|
||||
function drainedRestore() { // "public", to allow users to call
|
||||
if(disableBoot){
|
||||
if(!keepStartup){
|
||||
try{
|
||||
eval(require('Storage').read('bootupdate.js'));
|
||||
}catch(e){
|
||||
|
@ -112,16 +114,26 @@ function drainedRestore() { // "public", to allow users to call
|
|||
load(); // necessary after updating boot.0
|
||||
}
|
||||
|
||||
if(disableBoot){
|
||||
const checkCharge = () => {
|
||||
if(E.getBattery() < restore) return;
|
||||
drainedRestore();
|
||||
};
|
||||
const checkCharge = () => {
|
||||
if(E.getBattery() < restore) return;
|
||||
drainedRestore();
|
||||
};
|
||||
|
||||
if (Bangle.isCharging())
|
||||
checkCharge();
|
||||
if (Bangle.isCharging())
|
||||
checkCharge();
|
||||
|
||||
Bangle.on("charging", charging => {
|
||||
if(charging) checkCharge();
|
||||
});
|
||||
Bangle.on("charging", charging => {
|
||||
if(charging) checkCharge();
|
||||
});
|
||||
|
||||
if(!keepStartup){
|
||||
const storage = require("Storage");
|
||||
for(const boot of exceptions){
|
||||
try{
|
||||
const js = storage.read(`${boot}.boot.js`);
|
||||
if(js) eval(js);
|
||||
}catch(e){
|
||||
console.log(`error loading boot exception "${boot}": ${e}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
{
|
||||
var _a = require("Storage").readJSON("drained.setting.json", true) || {}, _b = _a.battery, threshold_1 = _b === void 0 ? 5 : _b, _c = _a.interval, interval = _c === void 0 ? 10 : _c, _d = _a.disableBoot, disableBoot_1 = _d === void 0 ? false : _d;
|
||||
(function () {
|
||||
var _a = require("Storage").readJSON("drained.setting.json", true) || {}, _b = _a.battery, threshold = _b === void 0 ? 5 : _b, _c = _a.interval, interval = _c === void 0 ? 10 : _c, _d = _a.keepStartup, keepStartup = _d === void 0 ? true : _d;
|
||||
drainedInterval = setInterval(function () {
|
||||
if (Bangle.isCharging())
|
||||
return;
|
||||
if (E.getBattery() > threshold_1)
|
||||
if (E.getBattery() > threshold)
|
||||
return;
|
||||
var app = "drained.app.js";
|
||||
if (disableBoot_1)
|
||||
if (!keepStartup)
|
||||
require("Storage").write(".boot0", "if(typeof __FILE__ === \"undefined\" || __FILE__ !== \"".concat(app, "\") setTimeout(load, 100, \"").concat(app, "\");"));
|
||||
load(app);
|
||||
}, interval * 60 * 1000);
|
||||
}
|
||||
})();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
const { battery: threshold = 5, interval = 10, disableBoot = false }: DrainedSettings
|
||||
(() => {
|
||||
const { battery: threshold = 5, interval = 10, keepStartup = true }: DrainedSettings
|
||||
= require("Storage").readJSON(`drained.setting.json`, true) || {};
|
||||
|
||||
drainedInterval = setInterval(() => {
|
||||
|
@ -10,7 +10,7 @@ drainedInterval = setInterval(() => {
|
|||
|
||||
const app = "drained.app.js";
|
||||
|
||||
if(disableBoot)
|
||||
if(!keepStartup)
|
||||
require("Storage").write(
|
||||
".boot0",
|
||||
`if(typeof __FILE__ === "undefined" || __FILE__ !== "${app}") setTimeout(load, 100, "${app}");`
|
||||
|
@ -18,4 +18,4 @@ drainedInterval = setInterval(() => {
|
|||
|
||||
load(app);
|
||||
}, interval * 60 * 1000);
|
||||
}
|
||||
})()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "drained",
|
||||
"name": "Drained",
|
||||
"version": "0.01",
|
||||
"version": "0.03",
|
||||
"description": "Switches to displaying a simple clock when the battery percentage is low, and disables some peripherals",
|
||||
"readme": "README.md",
|
||||
"icon": "icon.png",
|
||||
|
@ -14,5 +14,8 @@
|
|||
{"name":"drained.app.js","url":"app.js"},
|
||||
{"name":"drained.settings.js","url":"settings.js"},
|
||||
{"name":"drained.img","url":"app-icon.js","evaluate":true}
|
||||
],
|
||||
"data": [
|
||||
{"name":"drained.setting.json"}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,26 +1,19 @@
|
|||
(function (back) {
|
||||
var _a, _b, _c, _d;
|
||||
var _a, _b, _c, _d, _e;
|
||||
var SETTINGS_FILE = "drained.setting.json";
|
||||
var storage = require("Storage");
|
||||
var settings = storage.readJSON(SETTINGS_FILE, true) || {};
|
||||
(_a = settings.battery) !== null && _a !== void 0 ? _a : (settings.battery = 5);
|
||||
(_b = settings.restore) !== null && _b !== void 0 ? _b : (settings.restore = 20);
|
||||
(_c = settings.interval) !== null && _c !== void 0 ? _c : (settings.interval = 10);
|
||||
(_d = settings.disableBoot) !== null && _d !== void 0 ? _d : (settings.disableBoot = false);
|
||||
(_d = settings.keepStartup) !== null && _d !== void 0 ? _d : (settings.keepStartup = true);
|
||||
(_e = settings.exceptions) !== null && _e !== void 0 ? _e : (settings.exceptions = ["widdst.0"]);
|
||||
var save = function () {
|
||||
storage.writeJSON(SETTINGS_FILE, settings);
|
||||
};
|
||||
E.showMenu({
|
||||
var menu = {
|
||||
"": { "title": "Drained" },
|
||||
"< Back": back,
|
||||
"Keep startup code": {
|
||||
value: settings.disableBoot,
|
||||
format: function () { return settings.disableBoot ? "No" : "Yes"; },
|
||||
onchange: function () {
|
||||
settings.disableBoot = !settings.disableBoot;
|
||||
save();
|
||||
},
|
||||
},
|
||||
"Trigger at batt%": {
|
||||
value: settings.battery,
|
||||
min: 0,
|
||||
|
@ -54,5 +47,44 @@
|
|||
save();
|
||||
},
|
||||
},
|
||||
});
|
||||
"Keep startup code": {
|
||||
value: settings.keepStartup,
|
||||
onchange: function (b) {
|
||||
settings.keepStartup = b;
|
||||
save();
|
||||
updateAndRedraw();
|
||||
},
|
||||
},
|
||||
};
|
||||
var updateAndRedraw = function () {
|
||||
setTimeout(function () { E.showMenu(menu); }, 10);
|
||||
if (settings.keepStartup) {
|
||||
delete menu["Startup exceptions"];
|
||||
return;
|
||||
}
|
||||
menu["Startup exceptions"] = function () { return E.showMenu(bootExceptions); };
|
||||
var bootExceptions = {
|
||||
"": { "title": "Startup exceptions" },
|
||||
"< Back": function () { return E.showMenu(menu); },
|
||||
};
|
||||
storage.list(/\.boot\.js/)
|
||||
.map(function (name) { return name.replace(".boot.js", ""); })
|
||||
.forEach(function (name) {
|
||||
bootExceptions[name] = {
|
||||
value: settings.exceptions.indexOf(name) >= 0,
|
||||
onchange: function (b) {
|
||||
if (b) {
|
||||
settings.exceptions.push(name);
|
||||
}
|
||||
else {
|
||||
var i = settings.exceptions.indexOf(name);
|
||||
if (i >= 0)
|
||||
settings.exceptions.splice(i, 1);
|
||||
}
|
||||
save();
|
||||
},
|
||||
};
|
||||
});
|
||||
};
|
||||
updateAndRedraw();
|
||||
});
|
||||
|
|
|
@ -2,7 +2,8 @@ type DrainedSettings = {
|
|||
battery?: number,
|
||||
restore?: number,
|
||||
interval?: number,
|
||||
disableBoot?: ShortBoolean,
|
||||
keepStartup?: ShortBoolean,
|
||||
exceptions?: string[],
|
||||
};
|
||||
|
||||
(back => {
|
||||
|
@ -13,23 +14,16 @@ type DrainedSettings = {
|
|||
settings.battery ??= 5;
|
||||
settings.restore ??= 20;
|
||||
settings.interval ??= 10;
|
||||
settings.disableBoot ??= false;
|
||||
settings.keepStartup ??= true;
|
||||
settings.exceptions ??= ["widdst.0"]; // daylight savings
|
||||
|
||||
const save = () => {
|
||||
storage.writeJSON(SETTINGS_FILE, settings)
|
||||
};
|
||||
|
||||
E.showMenu({
|
||||
const menu: Menu = {
|
||||
"": { "title": "Drained" },
|
||||
"< Back": back,
|
||||
"Keep startup code": {
|
||||
value: settings.disableBoot,
|
||||
format: () => settings.disableBoot ? "No" : "Yes",
|
||||
onchange: () => {
|
||||
settings.disableBoot = !settings.disableBoot;
|
||||
save();
|
||||
},
|
||||
},
|
||||
"Trigger at batt%": {
|
||||
value: settings.battery,
|
||||
min: 0,
|
||||
|
@ -63,5 +57,48 @@ type DrainedSettings = {
|
|||
save();
|
||||
},
|
||||
},
|
||||
});
|
||||
"Keep startup code": {
|
||||
value: settings.keepStartup as boolean,
|
||||
onchange: (b: boolean) => {
|
||||
settings.keepStartup = b;
|
||||
save();
|
||||
updateAndRedraw();
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const updateAndRedraw = () => {
|
||||
// will change the menu, queue redraw:
|
||||
setTimeout(() => { E.showMenu(menu) }, 10);
|
||||
|
||||
if (settings.keepStartup) {
|
||||
delete menu["Startup exceptions"];
|
||||
return;
|
||||
}
|
||||
menu["Startup exceptions"] = () => E.showMenu(bootExceptions);
|
||||
|
||||
const bootExceptions: Menu = {
|
||||
"": { "title" : "Startup exceptions" },
|
||||
"< Back": () => E.showMenu(menu),
|
||||
};
|
||||
|
||||
storage.list(/\.boot\.js/)
|
||||
.map(name => name.replace(".boot.js", ""))
|
||||
.forEach((name: string) => {
|
||||
bootExceptions[name] = {
|
||||
value: settings.exceptions!.indexOf(name) >= 0,
|
||||
onchange: (b: boolean) => {
|
||||
if (b) {
|
||||
settings.exceptions!.push(name);
|
||||
} else {
|
||||
const i = settings.exceptions!.indexOf(name);
|
||||
if (i >= 0) settings.exceptions!.splice(i, 1);
|
||||
}
|
||||
save();
|
||||
},
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
updateAndRedraw();
|
||||
}) satisfies SettingsFunc
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: First release
|
|
@ -0,0 +1,124 @@
|
|||
Graphics.prototype.setFontLECO1976Regular42 = function(scale) {
|
||||
// Actual height 42 (41 - 0)
|
||||
return g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAA/AAAAAAAAH/AAAAAAAA//AAAAAAAP//AAAAAAB///AAAAAAP///AAAAAB////AAAAAf////AAAAD////4AAAAf////AAAAH////4AAAA////+AAAAA////wAAAAA///+AAAAAA///gAAAAAA//8AAAAAAA//gAAAAAAA/4AAAAAAAA/AAAAAAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////gD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4B/gH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/wB////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/wB////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAH+AAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), 46, atob("ERkmHyYmJiYmJCYmEQ=="), 60+(scale<<8)+(1<<16));
|
||||
};
|
||||
|
||||
Graphics.prototype.setFontLECO1976Regular22 = function(scale) {
|
||||
// Actual height 22 (21 - 0)
|
||||
return g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/nA/+cD/5wP/nAAAAAAAAPwAA/gAD+AAPwAAAAAD+AAP4AA/gAAAAAAAAAAAAAcOAP//A//8D//wP//AHDgAcOAP//A//8D//wP//AHDgAAAAAAAAH/jgf+OB/44H/jj8OP/w4//Dj/8OPxw/4HD/gcP+Bw/4AAAAAAAP+AA/8AD/wQOHHA4c8D//wP/8A//gAD4AAfAAH/8A//wP//A84cDjhwIP/AA/8AB/wAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA4f8Dh/wOH/A4f8ABwAAAAAAAAD8AAP4AA/gAD8AAAAAAAAAAAEAAD+AB//A///v/D//gB/wABwAAAAAADgAA/wAf/4P8///wf/4AP8AAOAAAAAAAAAyAAHcAAPwAD/gAP/AA/8AA/AAH8AAMwAAAAAAAAAAAAADgAAOAAA4AAf8AD/wAP/AA/8AAOAAA4AADgAAAAAAAAAAD8AAfwAB/AAD8AAAAAAAADgAAOAAA4AADgAAOAAA4AADgAAAAAAAAAADgAAOAAA4AADgAAAAAAAAABwAB/AA/8A//gP/gA/wADwAAIAAAAAAD//wP//A//8D//wOAHA4AcDgBwOAHA//8D//wP//A//8AAAAAAAA4AcDgBwOAHA//8D//wP//A//8AABwAAHAAAcAAAAAAAA+f8D5/wPn/A+f8DhxwOHHA4ccDhxwP/HA/8cD/xwP/HAAAAAAAAOAHA4AcDhxwOHHA4ccDhxwOHHA4ccD//wP//A//8D//wAAAAAAAD/wAP/AA/8AD/wAAHAAAcAABwAAHAA//8D//wP//A//8AAAAAAAA/98D/3wP/fA/98DhxwOHHA4ccDhxwOH/A4f8Dh/wOH/AAAAAAAAP//A//8D//wP//A4ccDhxwOHHA4ccDh/wOH/A4f8Dh/wAAAAAAAD4AAPgAA+AADgAAOAAA4AADgAAP//A//8D//wP//AAAAAAAAP//A//8D//wP//A4ccDhxwOHHA4ccD//wP//A//8D//wAAAAAAAD/xwP/HA/8cD/xwOHHA4ccDhxwOHHA//8D//wP//A//8AAAAAAAAOA4A4DgDgOAOA4AAAAAAAAOA/A4H8DgfwOA/AAAAAAAAB4AAPwAA/AAD8AAf4ABzgAPPAA8cAHh4AAAAAAAAAAAAHHAAccABxwAHHAAccABxwAHHAAccABxwAHHAAAAAAAAAOHAA4cADzwAPPAAf4AB/gAD8AAPwAAeAAB4AAAAAAAAA+AAD4AAPgAA+ecDh9wOH3A4fcDhwAP/AA/8AD/wAP/AAAAAAAAAP//4///j//+P//44ADjn/OOf845/zjnHOP8c4//zj//OP/84AAAAAAAP//A//8D//wP//A4cADhwAOHAA4cAD//wP//A//8D//wAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA//8D//wP9/A/j8AAAAAAAA//8D//wP//A//8DgBwOAHA4AcDgBwOAHA4AcDgBwOAHAAAAAAAAP//A//8D//wP//A4AcDgBwOAHA8A8D//wH/+AP/wAf+AAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA4ccDhxwOAHA4AcAAAAAAAA//8D//wP//A//8DhwAOHAA4cADhwAOHAA4cADgAAOAAAAAAD//wP//A//8D//wOAHA4ccDhxwOHHA4f8Dh/wOH/A4f8AAAAAAAA//8D//wP//A//8ABwAAHAAAcAABwAP//A//8D//wP//AAAAAAAAP//A//8D//wP//AAAAAAAAOAHA4AcDgBwOAHA4AcDgBwOAHA//8D//wP//A//8AAAAAAAA//8D//wP//A//8AHwAA/AAP8AB/wAPn/A8f8DB/wIH/AAAAAAAAP//A//8D//wP//AAAcAABwAAHAAAcAABwAAHAAAAAAAAP//A//8D//wP//Af8AAP+AAH/AAD8AAHwAD/AB/wAf8AP+AA//8D//wP//AAAAAAAAP//A//8D//wP//AfwAAfwAAfwAAfwAAfwP//A//8D//wAAAAAAAAAAAP//A//8D//wP//A4AcDgBwOAHA4AcD//wP//A//8D//wAAAAAAAD//wP//A//8D//wOHAA4cADhwAOHAA/8AD/wAP/AA/8AAAAAP//A//8D//wP//A4AcDgBwOAHA4AcD//+P//4///j//+AAA4AADgAAAP//A//8D//wP//A4eADh+AOH8A4f4D/3wP/HA/8MD/wQAAAAAAAD/xwP/HA/8cD/xwOHHA4ccDhxwOHHA4f8Dh/wOH/A4f8AAAAAAAA4AADgAAOAAA//8D//wP//A//8DgAAOAAA4AADgAAAAAA//8D//wP//A//8AABwAAHAAAcAABwP//A//8D//wP//AAAADAAAPgAA/wAD/4AB/8AA/8AAfwAB/AA/8Af+AP/AA/wAD4AAMAAA4AAD+AAP/gA//8AH/wAB/AAf8Af/wP/4A/4AD/gAP/4AH/8AB/wAB/AB/8D//wP/gA/gADgAAIABA4AcDwDwPw/Afn4Af+AA/wAD/AA//AH5+A/D8DwDwOAHAgAEAAAAP/AA/8AD/wAP/AAAf8AB/wAH/AAf8D/wAP/AA/8AD/wAAAAAAAADh/wOH/A4f8Dh/wOHHA4ccDhxwOHHA/8cD/xwP/HA/8cAAAAAAAAf//9///3///f//9wAA3AADcAAMAAAOAAA/gAD/wAH/8AB/8AA/wAAPAAAEAAAAHAADcAANwAB3///f//9///wAA"), 32, atob("BwYLDg4UDwYJCQwMBgkGCQ4MDg4ODg4NDg4GBgwMDA4PDg4ODg4NDg4GDQ4MEg8ODQ8ODgwODhQODg4ICQg="), 22+(scale<<8)+(1<<16));
|
||||
};
|
||||
|
||||
{
|
||||
const SETTINGS_FILE = "pebblepp.json";
|
||||
let settings = require("Storage").readJSON(SETTINGS_FILE,1)|| {'bg': '#0f0', 'color': 'Green', 'theme':'System', 'showlock':false};
|
||||
let theme;
|
||||
let drawTimeout;
|
||||
|
||||
const h = g.getHeight();
|
||||
const w = g.getWidth();
|
||||
const ha = 2*h/5 - 4;
|
||||
const h2 = 3*h/5 - 10;
|
||||
const h3 = 7*h/8;
|
||||
|
||||
let draw = function() {
|
||||
let locale = require("locale");
|
||||
let date = new Date();
|
||||
let time = locale.time(date, 1);
|
||||
|
||||
g.reset();
|
||||
g.setBgColor(theme.bg).clearRect(0, h2, w, h3);
|
||||
g.setFontLECO1976Regular42().setFontAlign(0, -1);
|
||||
g.setColor(theme.fg);
|
||||
g.drawString(time, w/2, h2 + 8);
|
||||
|
||||
// queue next draw
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
draw();
|
||||
}, 60000 - (Date.now() % 60000));
|
||||
};
|
||||
|
||||
let loadThemeColors = function() {
|
||||
theme = {fg: g.theme.fg, bg: g.theme.bg, day: g.toColor(0,0,0)};
|
||||
if (settings.theme === "Dark") {
|
||||
theme.fg = g.toColor(1,1,1);
|
||||
theme.bg = g.toColor(0,0,0);
|
||||
} else if (settings.theme === "Light") {
|
||||
theme.fg = g.toColor(0,0,0);
|
||||
theme.bg = g.toColor(1,1,1);
|
||||
}
|
||||
// day and steps
|
||||
if (settings.color == 'Blue' || settings.color == 'Red')
|
||||
theme.day = g.toColor(1,1,1); // white on blue or red best contrast
|
||||
};
|
||||
loadThemeColors();
|
||||
|
||||
// Load the clock infos
|
||||
let clockInfoItems = require("clock_info").load();
|
||||
let clockInfoDraw = (itm, info, options) => {
|
||||
// itm: the item containing name/hasRange/etc
|
||||
// info: data returned from itm.get() containing text/img/etc
|
||||
// options: options passed into addInteractive
|
||||
// Clear the background - if focussed, add a border
|
||||
g.reset().setBgColor(theme.bg).setColor(theme.fg);
|
||||
var b = 0; // border
|
||||
if (options.focus) { // white border
|
||||
b = 4;
|
||||
g.clearRect(options.x, options.y, options.x+options.w-1, options.y+options.h-1);
|
||||
}
|
||||
g.setBgColor(settings.bg).clearRect(options.x+b, options.y+b, options.x+options.w-1-b, options.y+options.h-1-b);
|
||||
// we're drawing center-aligned here
|
||||
var midx = options.x+options.w/2;
|
||||
if (info.img) { // draw the image
|
||||
// TODO: we could replace certain images with our own ones here...
|
||||
var y = options.y+8;
|
||||
if (g.floodFill) {
|
||||
/* img is (usually) a black and white transparent image. But we really would like the bits in
|
||||
the middle of it to be white. So what we do is we draw a slightly bigger rectangle in white,
|
||||
draw the image, and then flood-fill the rectangle back to the background color. floodFill
|
||||
was only added in 2v18 so we have to check for it and fallback if not. */
|
||||
g.setBgColor(theme.bg).clearRect(midx-25,y-1,midx+24,y+48);
|
||||
g.drawImage(info.img, midx-24,y,{scale:2});
|
||||
g.floodFill(midx-25,y,settings.bg);
|
||||
} else { // fallback
|
||||
g.drawImage(info.img, midx-24,y,{scale:2});
|
||||
}
|
||||
}
|
||||
g.setFontLECO1976Regular22().setFontAlign(0, 0);
|
||||
g.drawString(info.text, midx,options.y+options.h-12); // draw the text
|
||||
};
|
||||
|
||||
let clockInfoMenuA = require("clock_info").addInteractive(clockInfoItems, {
|
||||
x : 0, y: 0, w: w/2, h:h/2,
|
||||
draw : clockInfoDraw
|
||||
});
|
||||
let clockInfoMenuB = require("clock_info").addInteractive(clockInfoItems, {
|
||||
x : w/2, y: 0, w: w/2, h:h/2,
|
||||
draw : clockInfoDraw
|
||||
});
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
Bangle.setUI({
|
||||
mode : "clock",
|
||||
remove : function() {
|
||||
// Called to unload all of the clock app
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
clockInfoMenuA.remove();
|
||||
clockInfoMenuB.remove();
|
||||
delete Graphics.prototype.setFontLECO1976Regular22;
|
||||
delete Graphics.prototype.setFontLECO1976Regular42;
|
||||
require("widget_utils").show(); // re-show widgets
|
||||
}});
|
||||
|
||||
Bangle.loadWidgets();
|
||||
require("widget_utils").swipeOn(); // hide widgets, make them visible with a swipe
|
||||
g.setBgColor(settings.bg).clear(); // start off with completely clear background
|
||||
// contrast bar (top)
|
||||
g.setColor(theme.fg).fillRect(0, h2 - 6, w, h2);
|
||||
// contrast bar (bottom)
|
||||
g.setColor(theme.fg).fillRect(0, h3, w, h3 + 6);
|
||||
|
||||
draw();
|
||||
}
|
After Width: | Height: | Size: 1.6 KiB |
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwcBpMkyQC/AUVIBZEh4QROoMP/QIFoEkiQIFjl//4IEwUIkGChIIDyH/AAI+Ggg1Fw4RCDQgCBkARJ8J0MHwcIEZgC/ATJrCwDRFBALXHWwIIFQYwjDyAjNEAMECILREfZACgagYCNgAAQ/4Aj+PHAQUcv///lxAoQCBuIRBwEH//wHgIRBBAMAgf+BAMBEZHHEYUAv0HEYf+EYIAB8F/4AFC8YLEAAIjGAoQjJI4oFBI4wjCCI4jBCIgjDn6IF8YDCwJHFTQxHC/gjCQwQ1DNYKPICI8ccgRHFAGikBd4QCMJ4QAOpMkyQC/AQg="))
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"id": "pebblepp",
|
||||
"name": "Pebble++ Clock",
|
||||
"shortName": "Pebble++",
|
||||
"version": "0.01",
|
||||
"description": "A pebble style clock (based on the 'Pebble Clock' app) but with two configurable ClockInfo items at the top",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock,clkinfo",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"pebblepp.app.js","url":"app.js"},
|
||||
{"name":"pebblepp.settings.js","url":"settings.js"},
|
||||
{"name":"pebblepp.img","url":"icon.js","evaluate":true}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 1.2 KiB |
|
@ -0,0 +1,50 @@
|
|||
(function(back) {
|
||||
const SETTINGS_FILE = "pebblepp.json";
|
||||
|
||||
// TODO Only the color/theme indices should be written in the settings file so the labels can be translated
|
||||
|
||||
// Initialize with default settings...
|
||||
let s = {'bg': '#0f0', 'color': 'Green', 'theme':'System', 'showlock':false}
|
||||
|
||||
// ...and overwrite them with any saved values
|
||||
// This way saved values are preserved if a new version adds more settings
|
||||
const storage = require('Storage');
|
||||
let settings = storage.readJSON(SETTINGS_FILE, 1) || s;
|
||||
const saved = settings || {};
|
||||
for (const key in saved) {
|
||||
s[key] = saved[key]
|
||||
}
|
||||
|
||||
function save() {
|
||||
settings = s;
|
||||
storage.write(SETTINGS_FILE, settings);
|
||||
}
|
||||
|
||||
var color_options = ['Green','Orange','Cyan','Purple','Red','Blue'];
|
||||
var bg_code = ['#0f0','#ff0','#0ff','#f0f','#f00','#00f'];
|
||||
var theme_options = ['System', 'Light', 'Dark'];
|
||||
|
||||
E.showMenu({
|
||||
'': { 'title': 'Pebble++ Clock' },
|
||||
/*LANG*/'< Back': back,
|
||||
/*LANG*/'Colour': {
|
||||
value: 0 | color_options.indexOf(s.color),
|
||||
min: 0, max: 5,
|
||||
format: v => color_options[v],
|
||||
onchange: v => {
|
||||
s.color = color_options[v];
|
||||
s.bg = bg_code[v];
|
||||
save();
|
||||
}
|
||||
},
|
||||
/*LANG*/'Theme': {
|
||||
value: 0 | theme_options.indexOf(s.theme),
|
||||
min: 0, max: theme_options.length - 1,
|
||||
format: v => theme_options[v],
|
||||
onchange: v => {
|
||||
s.theme = theme_options[v];
|
||||
save();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
|
@ -1,2 +1,3 @@
|
|||
0.01: New App!
|
||||
0.02: Trim old entries from the popcon app cache
|
||||
0.03: Avoid polluting global scope
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
{
|
||||
var oldRead_1 = require("Storage").readJSON;
|
||||
var oneMonth_1 = 1000 * 86400 * 28;
|
||||
var monthAgo_1 = Date.now() - oneMonth_1;
|
||||
var cache_1;
|
||||
var ensureCache_1 = function () {
|
||||
if (!cache_1) {
|
||||
cache_1 = oldRead_1("popcon.cache.json", true);
|
||||
if (!cache_1)
|
||||
cache_1 = {};
|
||||
(function () {
|
||||
var oldRead = require("Storage").readJSON;
|
||||
var oneMonth = 1000 * 86400 * 28;
|
||||
var monthAgo = Date.now() - oneMonth;
|
||||
var cache;
|
||||
var ensureCache = function () {
|
||||
if (!cache) {
|
||||
cache = oldRead("popcon.cache.json", true);
|
||||
if (!cache)
|
||||
cache = {};
|
||||
}
|
||||
return cache_1;
|
||||
return cache;
|
||||
};
|
||||
var trimCache_1 = function (cache) {
|
||||
var threeMonthsBack = Date.now() - oneMonth_1 * 3;
|
||||
var trimCache = function (cache) {
|
||||
var threeMonthsBack = Date.now() - oneMonth * 3;
|
||||
var del = [];
|
||||
for (var k in cache)
|
||||
if (cache[k].last < threeMonthsBack)
|
||||
|
@ -22,21 +22,21 @@
|
|||
delete cache[k];
|
||||
}
|
||||
};
|
||||
var saveCache_1 = function (cache, orderChanged) {
|
||||
trimCache_1(cache);
|
||||
var saveCache = function (cache, orderChanged) {
|
||||
trimCache(cache);
|
||||
require("Storage").writeJSON("popcon.cache.json", cache);
|
||||
if (orderChanged) {
|
||||
var info = oldRead_1("popcon.info", true);
|
||||
var info = oldRead("popcon.info", true);
|
||||
info.cacheBuster = !info.cacheBuster;
|
||||
require("Storage").writeJSON("popcon.info", info);
|
||||
}
|
||||
};
|
||||
var sortCache_1 = function () {
|
||||
var ents = Object.values(cache_1);
|
||||
var sortCache = function () {
|
||||
var ents = Object.values(cache);
|
||||
ents.sort(function (a, b) {
|
||||
var n;
|
||||
var am = (a.last > monthAgo_1);
|
||||
var bm = (b.last > monthAgo_1);
|
||||
var am = (a.last > monthAgo);
|
||||
var bm = (b.last > monthAgo);
|
||||
n = bm - am;
|
||||
if (n)
|
||||
return n;
|
||||
|
@ -64,31 +64,31 @@
|
|||
};
|
||||
require("Storage").readJSON = (function (fname, skipExceptions) {
|
||||
var _a;
|
||||
var j = oldRead_1(fname, skipExceptions);
|
||||
var j = oldRead(fname, skipExceptions);
|
||||
if (/\.info$/.test(fname)) {
|
||||
var cache_2 = ensureCache_1();
|
||||
var cache_1 = ensureCache();
|
||||
var so = void 0;
|
||||
if (j.src && (so = (_a = cache_2[j.src]) === null || _a === void 0 ? void 0 : _a.sortorder) != null)
|
||||
if (j.src && (so = (_a = cache_1[j.src]) === null || _a === void 0 ? void 0 : _a.sortorder) != null)
|
||||
j.sortorder = so;
|
||||
else
|
||||
j.sortorder = 99;
|
||||
}
|
||||
return j;
|
||||
});
|
||||
var oldLoad_1 = load;
|
||||
var oldLoad = load;
|
||||
global.load = function (src) {
|
||||
if (src) {
|
||||
var cache_3 = ensureCache_1();
|
||||
var ent = cache_3[src] || (cache_3[src] = {
|
||||
var cache_2 = ensureCache();
|
||||
var ent = cache_2[src] || (cache_2[src] = {
|
||||
pop: 0,
|
||||
last: 0,
|
||||
sortorder: -10,
|
||||
});
|
||||
ent.pop++;
|
||||
ent.last = Date.now();
|
||||
var orderChanged = sortCache_1();
|
||||
saveCache_1(cache_3, orderChanged);
|
||||
var orderChanged = sortCache();
|
||||
saveCache(cache_2, orderChanged);
|
||||
}
|
||||
return oldLoad_1(src);
|
||||
return oldLoad(src);
|
||||
};
|
||||
}
|
||||
})();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
(() => {
|
||||
type Timestamp = number;
|
||||
type Cache = {
|
||||
[key: string]: {
|
||||
|
@ -113,4 +113,4 @@ global.load = (src: string) => {
|
|||
|
||||
return oldLoad(src);
|
||||
};
|
||||
}
|
||||
})()
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "popconlaunch",
|
||||
"name": "Popcon Launcher",
|
||||
"shortName": "Popcon",
|
||||
"version": "0.02",
|
||||
"version": "0.03",
|
||||
"description": "Launcher modification - your launchers will display your favourite (popular) apps first. Overrides `readJSON`, may slow down your watch",
|
||||
"readme": "README.md",
|
||||
"icon": "app.png",
|
||||
|
|
|
@ -9,4 +9,6 @@
|
|||
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
|
||||
0.09: Accidental version bump
|
||||
0.09: Accidental version bump
|
||||
0.10: Use charging state on boot for auto calibration
|
||||
Log additional timestamp for trace log
|
||||
|
|
|
@ -42,10 +42,10 @@
|
|||
|
||||
|
||||
let logPower = (type, oldstate, state, app) => {
|
||||
logFile.write("p," + type + ',' + (oldstate?1:0) + ',' + (state?1:0) + ',' + app + "\n");
|
||||
logFile.write(Date.now() + ",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");
|
||||
logFile.write(Date.now() + "," + type + ',' + duration + ',' + source.replace(/\n/g, " ").replace(/,/g,"") + "\n");
|
||||
};
|
||||
|
||||
let lastPowerOn = {};
|
||||
|
@ -152,13 +152,14 @@
|
|||
return v;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
if (settings.autoCalibration){
|
||||
let chargeStart;
|
||||
if (Bangle.isCharging()) chargeStart = Date.now();
|
||||
Bangle.on("charging", (charging)=>{
|
||||
if (charging) chargeStart = Date.now();
|
||||
if (!chargeStart && charging) chargeStart = Date.now();
|
||||
if (chargeStart && !charging && (Date.now() - chargeStart > 1000*60*60*3)) require("powermanager").setCalibration();
|
||||
if (!charging) chargeStart = undefined;
|
||||
});
|
||||
}
|
||||
})();
|
||||
})();
|
||||
|
|
|
@ -1,6 +1,15 @@
|
|||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||
|
||||
<style>
|
||||
.table_wrapper{
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
margin-right: 0.8rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="content"></div>
|
||||
|
@ -119,18 +128,21 @@ function viewDeferredTable(filename) {
|
|||
<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>${timeFormat(duration)}</b>. Timeouts/intervals have run for <b>${timeFormat(sum)} (${(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`;
|
||||
</div>
|
||||
|
||||
<div class="table_wrapper">
|
||||
|
||||
<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>`;
|
||||
htmlOverview += `</tbody></table></div>`;
|
||||
domContent.innerHTML = htmlOverview;
|
||||
domContent.querySelector("#back").addEventListener("click",event => {
|
||||
show();
|
||||
|
@ -234,24 +246,46 @@ function viewDetailsTable(filename) {
|
|||
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`;
|
||||
<div>
|
||||
This is a trace log of all logged entries. Power is logged with type, state transition (old → new) and calling app if available. Functions are logged with execution duration and source if available.
|
||||
</div>
|
||||
<div class="table_wrapper">
|
||||
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Time</th>
|
||||
<th>Type</th>
|
||||
<th>Info</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>\n`;
|
||||
let rows = data.trim().split("\n");
|
||||
let firstTimestamp;
|
||||
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>`
|
||||
let col = 0;
|
||||
if (!firstTimestamp) firstTimestamp = cols[0];
|
||||
|
||||
if (cols[1] == "p"){
|
||||
cols[1] = "Power";
|
||||
htmlOverview += `<tr>
|
||||
<td>${timeFormat(cols[col++]-firstTimestamp)}</td>
|
||||
<td>${cols[col++]}</td>
|
||||
<td>${cols[col++]}</br>${cols[col++]} → ${cols[col++]}</br>${cols[col++]}</td>
|
||||
</tr>`
|
||||
} else {
|
||||
htmlOverview += `<tr>
|
||||
<td>${timeFormat(cols[col++]-firstTimestamp)}</td>
|
||||
<td>${cols[col++]=="t"?"Timeout":"Interval"}</td>
|
||||
<td>${new Number(cols[col++]).toFixed(0)}ms</br><pre>${cols[col++]}</pre></td>
|
||||
</tr>`
|
||||
}
|
||||
}
|
||||
htmlOverview += `</tbody></table>`;
|
||||
htmlOverview += `</tbody></table></div>`;
|
||||
|
||||
|
||||
domContent.innerHTML = htmlOverview;
|
||||
domContent.querySelector("#back").addEventListener("click",event => {
|
||||
show();
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "powermanager",
|
||||
"name": "Power Manager",
|
||||
"shortName": "Power Manager",
|
||||
"version": "0.09",
|
||||
"version": "0.10",
|
||||
"description": "Allow configuration of warnings and thresholds for battery charging and display.",
|
||||
"icon": "app.png",
|
||||
"type": "bootloader",
|
||||
|
|
|
@ -140,6 +140,19 @@
|
|||
onchange: v => {
|
||||
writeSettings("logDetails", v);
|
||||
}
|
||||
},
|
||||
'Clear logs': function (){
|
||||
E.showPrompt("Delete logs and reload?").then((v)=>{
|
||||
if (v) {
|
||||
require('Storage').open("powermanager.log","w").erase();
|
||||
require("Storage").erase("powermanager.def.json");
|
||||
require("Storage").erase("powermanager.hw.json");
|
||||
load();
|
||||
} else
|
||||
E.showMenu(submenu_logging);
|
||||
}).catch(()=>{
|
||||
E.showMenu(submenu_logging);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
0.01: New App!
|
||||
0.02: New 'Settings Menu' to choose your favorite color and switch between light or dark themes
|
||||
0.03: New 'Leading Zero' and 'Date Suffix' options in 'Settings Menu'
|
||||
|
|
|
@ -12,14 +12,21 @@ Shadow Clock uses the "Londrina" font in a user selectable color and surrounds i
|
|||
|
||||
## Configuration
|
||||
|
||||
Anton Clock is configured by the standard settings mechanism of Bangle.js's operating system:
|
||||
Shadow Clock is configured by the standard settings mechanism of Bangle.js's operating system:
|
||||
Open the `Settings` app, then the `Apps` submenu and below it the `Shadow Clock` menu.
|
||||
You configure Shadow Clock by selecting a `Light` or `Dark` system wide theme and then selecting the `Color` of the clock numbers.
|
||||
You can configure Shadow Clock by selecting a `Light` or `Dark` system wide theme, selecting the `Color` of the clock numbers, enabling or disabling the suffix, and enabling or disabling the leading zero.
|
||||
|
||||
### Configuration Options
|
||||
|
||||
* Theme: Choose between `Light` and `Dark` themes.
|
||||
* Color: Choose the color of the clock numbers from the available options.
|
||||
* Date Suffix: `Yes` will show "10th" and `No` will show "10".
|
||||
* Lead Zero: `Yes` to show the leading zero in 24hr mode, `No` to hide.
|
||||
|
||||
## Compatibility
|
||||
|
||||
Shadow Clock should be fully compatible with with Bangle.js 1 and Bangle.js 2. However, it was built and tested with Bangle.js 2
|
||||
Shadow Clock should be fully compatible with with Bangle.js 1 and Bangle.js 2. However, it was built and tested with Bangle.js 2.
|
||||
|
||||
## Creator
|
||||
|
||||
[stweedo](https://github.com/stweedo)
|
||||
[stweedo](https://github.com/stweedo)
|
|
@ -1 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwxH+64Afq17AAlWq2sBYOtB4Urq2BFzeBwONAAeyHAQvCrPX1g3BlYvb02BjwADF4QmBwdZF4MrleBgAvlgAwBgqQBlcAF72HF46PBwaPDwK/dVoLpBAAwMBweDFTYA/AH4A/AH4A/AF2lAAOsBRIAF6AMDp9Wp+lFyNWkYABitWFwoKCAAsHBgMyBAkVwIuO6EVCod6F50A6+BBI0VMRxGEF6BfBIwZqHABRGCo5EIF4cOgEHAAUA0scBYQDDCAIvMhx7CCYTfDAYIvDK4McNoaODh1PPgcO1jsNiskOYQTBh0cjlW1iDHfwJfDjmeoDKEABSlCgAlCF4QJGcgwPEfQ5eMFQIvDRYIfClesioADEgcrSAguEwJeNh1AAgQkBwOlkkkQoOBvQACIgRAB685F4wJBF5oAGp4UJ1i6CeQK/Dg4ECZYQvTComsnNdrp+B0rADlaKD1l6X52BgAACJIgvEFIcO0sOF4cAAgeISgYvLMg8HKoOBAAIvEAAsOL4bwGFxyuDg/QGoYZBT5AKBXQYvVCYUH1gvFqwjGTwWBGA0AOwIAN6EOhytBAwIFBh1PAoN6g8VskVmUAZoelBQVABQIuPMAS6DfYWBMgI9CwMrq2lDA2Bq1PFqLpCHBAWKMIJGBAgRDEABiKBlYTDAYcA1gKBqwgFlYMBLoIaBCIKZDABh+BDIQVBC4WsF4YOBGAmsBQSgCAIIvQFYIiBKwReDFQI3DEIlWFIIsBCwIABF6JhClZhBZQo8BAYIOBIoh0CBIJ5DAH4AEPYKBDWwSGCAoKIDAoIwcEwJ/CRYICCFoI8BQ4YKBMDhWBEwIjBYQcAAYQKBZIRfdAIJVCQgLkDAYJjCcX4A/AH4A/AFwA=="))
|
||||
require("heatshrink").decompress(atob("mEwxH+64Ae1mBAA+sBgOt1oPBqwJBF7lcsQAEw+HEwWtwetwIvBqwvcsUeAAlDLAOs1tZwfXlcrq0ANIQviFAOtl4viw9DAAlo1l7L4ODR4OIZISPcb4QAGBgOJL4IAg1gAHFUIA/AH4A/AH4A51lkAA+ICI2IBYWB1msxAAH1gvNjkjAA0VDAuICAcVwIGCjgADAoMyF7msoALEF4IWHkYvOioACGYgvFro7FF5JfO6+BqwACioYChwvDNwwvFhwAEqz0RWYcclbjBGIOzWwYvHBIJ6BrruNAAs5JgeBXQMcnOIwM5hxsCR5McwJeZAwMVPgWlL5CaFMCJeFXYR/BJodAF4iKBAARgDrouPcYZeCBANdmVWAoSWBF4fXwIADJIcyF59dLwo+IX4gLFwJKChyQOLwcVlYPOfgNkAAOIJQIvSLxwvGd4c5LwYvPDwlWBQmIAAIvMaoIEDF51kCQUHCQgoCW4aPGFYgzDVRReOF4QJCF4q6EFwcHVRQvDmUOh0rOIusisVY4YRBA4IkDrsVnNAisylYuNDwQACBQ+BQwIREAwgOCq160otOAH4A/ACdWldWVYK/GqwBBAoIKCBAQLDAIIOBFx+sF4QCDcgIJBAgMrlYCBHwIUDGAQPDF6QADGAQvDEQZiCOYQtCHYQDBF6AwBAARcBJIQHEBQaTCAoQACAgTDVFYYGDB5LycXIbdELQQJCAAKJCLoQ0YXIatBYYQGCgDPGAIKMXF4YdCAIZlCGohrEMgKUcACCQBF9oA/AH4Ad"))
|
||||
|
|
|
@ -1,95 +1,113 @@
|
|||
// Clock with large colored digits using the "Londrina" font and a slightly larger "Londrina Shadow" font on top
|
||||
|
||||
Graphics.prototype.setFontLondrinaSolid = function() {
|
||||
// Actual height 59 (64 - 6)
|
||||
return this.setFontCustom(
|
||||
E.toString(require('heatshrink').decompress(atob('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'))),
|
||||
46,
|
||||
atob("DyEqHigoJikpJygqEQ=="),
|
||||
81 | 65536
|
||||
);
|
||||
};
|
||||
|
||||
Graphics.prototype.setFontLondrinaShadow = function() {
|
||||
// Actual height 63 (67 - 5)
|
||||
return this.setFontCustom(
|
||||
E.toString(require('heatshrink').decompress(atob('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'))),
|
||||
46,
|
||||
atob("DyEqHigoJikpJygqEQ=="),
|
||||
81 | 65536
|
||||
);
|
||||
};
|
||||
|
||||
Graphics.prototype.setFontDotGothic16 = function() {
|
||||
// Actual height 20 (19 - 0)
|
||||
return this.setFontCustom(
|
||||
E.toString(require('heatshrink').decompress(atob('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'))),
|
||||
32,
|
||||
atob("CgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoA"),
|
||||
20|65536
|
||||
);
|
||||
};
|
||||
|
||||
let storage = require('Storage');
|
||||
|
||||
var settings = Object.assign({
|
||||
// default values
|
||||
color: "#0ff",
|
||||
theme: "light",
|
||||
}, storage.readJSON("shadowclk.json", true) || {});
|
||||
|
||||
(function() {
|
||||
let drawTimeout;
|
||||
|
||||
function draw() {
|
||||
var x = g.getWidth() / 2;
|
||||
var y = g.getHeight() / 2;
|
||||
g.reset().clearRect(Bangle.appRect);
|
||||
var date = new Date();
|
||||
var hour = String(date.getHours()).padStart(2, '0');
|
||||
if (hour[0] === '0') hour = hour[1];
|
||||
var minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
var timeStr = hour + ':' + minutes;
|
||||
var color = settings.color;
|
||||
g.setFontAlign(0, 0).setFont("LondrinaSolid").setColor(color).drawString(timeStr, x - 1, y);
|
||||
g.reset().setFontAlign(0, 0).setFont("LondrinaShadow").drawString(timeStr, x - 1, y);
|
||||
|
||||
var locale = require("locale");
|
||||
var dayOfMonth = date.getDate();
|
||||
var month = locale.month(date, 1).slice(0, 1).toUpperCase() + locale.month(date, 1).slice(1).toLowerCase();
|
||||
var year = date.getFullYear();
|
||||
var dayOfMonthStr = dayOfMonth.toString();
|
||||
if (dayOfMonth === 1 || dayOfMonth === 21 || dayOfMonth === 31) {
|
||||
dayOfMonthStr += "st";
|
||||
} else if (dayOfMonth === 2 || dayOfMonth === 22) {
|
||||
dayOfMonthStr += "nd";
|
||||
} else if (dayOfMonth === 3 || dayOfMonth === 23) {
|
||||
dayOfMonthStr += "rd";
|
||||
} else {
|
||||
dayOfMonthStr += "th";
|
||||
Graphics.prototype.setFontLondrinaSolid = function () {
|
||||
// Actual height 59 (64 - 6)
|
||||
return this.setFontCustom(
|
||||
E.toString(require('heatshrink').decompress(atob('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'))),
|
||||
46,
|
||||
atob("DyEqHigoJikpJygqEQ=="),
|
||||
81 | 65536
|
||||
);
|
||||
};
|
||||
|
||||
Graphics.prototype.setFontLondrinaShadow = function () {
|
||||
// Actual height 63 (67 - 5)
|
||||
return this.setFontCustom(
|
||||
E.toString(require('heatshrink').decompress(atob('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'))),
|
||||
46,
|
||||
atob("DyEqHigoJikpJygqEQ=="),
|
||||
81 | 65536
|
||||
);
|
||||
};
|
||||
|
||||
Graphics.prototype.setFontRighteous = function () {
|
||||
// Actual height 16 (15 - 0)
|
||||
return this.setFontCustom(
|
||||
E.toString(require('heatshrink').decompress(atob('ABN//ABBv0QA4N4AIVwAogACoEC+EH+EP4EOAovACIcPsEfsF7vABBjfghPAjEAv0As0Yv14n/gg+Aj34vP4sPYgH4gFgEgMP8Ef+F7nABBjcwjEwIwZNFEYM//H//3wn3AgwLBwEC8EO/3+v/8E4IfDmMAn+A/8A/0AAYIHBncAGQMDAIUHgEf4ABBBIYAEjwBChgGBB4YBJAAU4AIS1BhAFBgP4h/wHoKPBXYIlDj/gn/wuF4uE4sEYAYLXBBYK9BSwLhDAIKODuP4uYBB3ABDv84n84jgSCEoNzAIIRFFoUc8DFBv/gAIN+A4IBCWoIBBj7XEFoIBDu4BCH4XwsPggPAdIItBf4N+nFOnEOnB/Bg4tBwAmBQINwOoP4u5rB4CNEsEdS4ILBLIMYAIIFBBIINBgFAhznCn/AuPIuHYRIN7N4TnBZQcHnABBgTLBXoOeAIMBdYUHgEPwAZBnvgnHgiFAD4cNwABLCIcwsAbBdILbBg+AeIJoCPIfYTIR7BgABBv8AnwTCgIZB8EHuEP7ABE4EOoEH4CzBCoLNBQ4N48FwAIjvCboYACBIIBDuapBAIQJDnypBQYKBBcIU4vFwnABHmEwFI4PFXoSBCHpoVBsEYCJGAAISZDJY14JZD7BmJhCE48DVIIBEBYYVJgE4AIYJC+F/MYQVHJIXAJISBCoEIFJoBEgDRBToRxCU4wJECYI/Jn8AWIMD8EBG4rjQUILTNuHgAId/EYPAj+AFZ0+vF+n/2FaBhD7ArBjiHBXIM4ue4AI34uLtBoCLDS5YBBHIRbBDIN4dJXgCYNggF+C4JvB/CjBgZLBCIOADwNAFIYJBc4jxJF4YXBoEIRYN8E4SHBToa/BBoIRBvgtBAIOAgfAIIJDCwAJBBoIRBsAnCToKJBGoW4vzEBnFgIIMA//+AIfgh3AgxhBRoJ1BcoMH+AxBLYUACIIVBAIIdDBYMYCAQdBAYMQQoUAgYBMAAUwDY0BHYUP/EOnEOjADBA4ILDWYR5BWoPwCYQBECYMDboQnIAIUPnEGmA3NcogjH/ADBh+4EYcMTIK3C//49kA5gbHnYBCn8P/8H/0P/iFDaoQTBgABDPYRBB/EgCIgFBkEPv/+v/8A4IhFgPggY9Ch14hiwBhARFHYZlCHYgHFNYJBDgYZCB45THXJjhCZoaBC/7fHCYYjBEp3/EIJjLJoIBBgcYh/YAIMMAIf4AIVwCIN/8D3DSoMMjABBh+Ah4/CgChBAIQ1DHYKRCCYUBQISFBh/Ah66CAoQjGEIgHFCIPAhEIOYV4TIaBDJZE7gE/AIMP/0P/hLChBlCGYQBCNYUAjEDRoOAv/8//+/n+8EOAIIfBBIIBBv/+4EGBoYJC/kHwAfBCoMHAIgrBAYMGgA='))),
|
||||
32,
|
||||
atob("BAQHCQcLCAQGBgkIBAgECAoFCQkKCQoICgoEBAgJCAoKCwsKCgkJCwsEBwoJDQsMCgwKCgkLCg0KCgkGCAYFCgQKCggKCAYKCQQECQQMCQkKCgcIBwkJDAgJCAcEBwcA"),
|
||||
16 | 65536
|
||||
);
|
||||
};
|
||||
|
||||
let appSettings = require("Storage").readJSON("shadowclk.json", 1) || {};
|
||||
let settings = require("Storage").readJSON("setting.json", 1) || {};
|
||||
let is12Hour = settings["12hour"] !== undefined ? settings["12hour"] : false;
|
||||
let color = appSettings.color !== undefined ? appSettings.color : "#0ff";
|
||||
let enableLeadingZero = appSettings.enableLeadingZero !== undefined ? appSettings.enableLeadingZero : false;
|
||||
let enableSuffix = appSettings.enableSuffix !== undefined ? appSettings.enableSuffix : true;
|
||||
|
||||
// Draw the time and date
|
||||
(function () {
|
||||
let drawTimeout;
|
||||
|
||||
function draw() {
|
||||
var x = g.getWidth() / 2;
|
||||
var y = g.getHeight() / 2;
|
||||
g.reset().clearRect(Bangle.appRect);
|
||||
var date = new Date();
|
||||
var hour = date.getHours();
|
||||
var minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
|
||||
// Handle 12-hour format
|
||||
if (is12Hour) {
|
||||
hour = hour % 12 || 12; // Convert 0 to 12 for 12-hour format
|
||||
} else {
|
||||
// If the leading zero option is enabled and hour is less than 10, add leading zero
|
||||
if (enableLeadingZero && hour < 10) {
|
||||
hour = '0' + hour;
|
||||
}
|
||||
var dayOfWeek = locale.dow(date, 0).slice(0, 1).toUpperCase() + locale.dow(date, 0).slice(1).toLowerCase();
|
||||
var dateStr = month + " " + dayOfMonthStr + ", " + year + "\n" + dayOfWeek;
|
||||
g.setFontAlign(0, 0).setFont("DotGothic16").drawString(dateStr, x, y + 48);
|
||||
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(() => {
|
||||
drawTimeout = undefined;
|
||||
draw();
|
||||
}, 60000 - (Date.now() % 60000));
|
||||
}
|
||||
|
||||
Bangle.setUI({
|
||||
mode: "clock",
|
||||
remove: function() {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
delete Graphics.prototype.setFontLondrinaSolid;
|
||||
delete Graphics.prototype.setFontLondrinaShadow;
|
||||
delete Graphics.prototype.setFontDotGothic16;
|
||||
|
||||
var timeStr = hour + ':' + minutes;
|
||||
|
||||
// Handle midnight in 12-hour format specifically
|
||||
if (is12Hour && hour === 0) {
|
||||
timeStr = '12' + timeStr.substring(2);
|
||||
}
|
||||
|
||||
g.setFontAlign(0, 0).setFont("LondrinaSolid").setColor(color).drawString(timeStr, x - 1, y);
|
||||
g.reset().setFontAlign(0, 0).setFont("LondrinaShadow").drawString(timeStr, x - 1, y);
|
||||
|
||||
var locale = require("locale");
|
||||
var dayOfMonth = date.getDate();
|
||||
var month = locale.month(date, 1).slice(0, 1).toUpperCase() + locale.month(date, 1).slice(1).toLowerCase();
|
||||
var year = date.getFullYear();
|
||||
var dayOfMonthStr = dayOfMonth.toString();
|
||||
if (enableSuffix) {
|
||||
var suffix = "";
|
||||
if (dayOfMonth === 1 || dayOfMonth === 21 || dayOfMonth === 31) {
|
||||
suffix = "st";
|
||||
} else if (dayOfMonth === 2 || dayOfMonth === 22) {
|
||||
suffix = "nd";
|
||||
} else if (dayOfMonth === 3 || dayOfMonth === 23) {
|
||||
suffix = "rd";
|
||||
} else {
|
||||
suffix = "th";
|
||||
}
|
||||
});
|
||||
Bangle.loadWidgets();
|
||||
draw();
|
||||
setTimeout(Bangle.drawWidgets, 0);
|
||||
})();
|
||||
dayOfMonthStr += suffix;
|
||||
}
|
||||
var dayOfWeek = locale.dow(date, 0).slice(0, 1).toUpperCase() + locale.dow(date, 0).slice(1).toLowerCase();
|
||||
var dateStr = month + " " + dayOfMonthStr + ", " + year + "\n" + dayOfWeek;
|
||||
g.setFontAlign(0, 0).setFont("Righteous").drawString(dateStr, x, y + 56);
|
||||
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(() => {
|
||||
drawTimeout = undefined;
|
||||
draw();
|
||||
}, 60000 - (Date.now() % 60000));
|
||||
}
|
||||
Bangle.setUI({
|
||||
mode: "clock",
|
||||
remove: function () {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
delete Graphics.prototype.setFontLondrinaSolid;
|
||||
delete Graphics.prototype.setFontLondrinaShadow;
|
||||
delete Graphics.prototype.setFontRighteous;
|
||||
}
|
||||
});
|
||||
Bangle.loadWidgets();
|
||||
draw();
|
||||
setTimeout(Bangle.drawWidgets, 0);
|
||||
})();
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 446 B |
|
@ -6,15 +6,30 @@
|
|||
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Londrina+Solid&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Londrina+Shadow&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=DotGothic16&display=swap" rel="stylesheet">
|
||||
<title>3-Bit Color Picker</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Righteous&display=swap" rel="stylesheet">
|
||||
<title>Shadow Clock Customizer</title>
|
||||
<style>
|
||||
.underlined-heading {
|
||||
position: relative;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.underlined-heading::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
border-bottom: 5px solid;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
|
||||
.color-button {
|
||||
|
@ -72,40 +87,87 @@
|
|||
display: block;
|
||||
}
|
||||
|
||||
#toggle-bg {
|
||||
margin-top: 20px;
|
||||
.button-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 360px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.button-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
flex-basis: 0;
|
||||
margin: 5px;
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
#upload {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
#upload-container {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
#message-container {
|
||||
height: 40px;
|
||||
/* adjust the height based on your desired fixed height */
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
<script src="../../core/lib/interface.js"></script>
|
||||
<div class="main-container">
|
||||
<h1>3-Bit Color Picker</h1>
|
||||
<h1 class="underlined-heading">Clock Customizer</h1>
|
||||
<div id="color-buttons-container"></div>
|
||||
<button id="toggle-bg" class="btn btn-primary" onclick="toggleBackground()">Light/Dark</button>
|
||||
<div class="button-container">
|
||||
<div class="button-row">
|
||||
<button id="toggle-hour-format" class="btn btn-primary" onclick="toggleHourFormat()">Time Mode: 24 Hour</button>
|
||||
<button id="toggle-bg" class="btn btn-primary" onclick="toggleBackground()">Theme: Light</button>
|
||||
</div>
|
||||
<div class="button-row">
|
||||
<button id="toggle-leading-zero" class="btn btn-primary" onclick="toggleLeadingZero()">Leading Zero:
|
||||
OFF</button>
|
||||
<button id="toggle-suffix" class="btn btn-primary" onclick="toggleSuffix()">Suffix: ON</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="preview-box">
|
||||
<canvas id="preview-canvas" width="176" height="176"></canvas>
|
||||
</div>
|
||||
<button id="upload" class="btn btn-primary">Upload</button>
|
||||
<div id="upload-container">
|
||||
<button id="upload" class="btn btn-primary">Upload</button>
|
||||
</div>
|
||||
<div id="message-container">
|
||||
<div id="message"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
const messageDiv = document.getElementById('message');
|
||||
|
||||
// Initialize app and system settings
|
||||
let selectedColor = "#0ff";
|
||||
let isDarkBg = false;
|
||||
let leadingZero = false;
|
||||
let prevLeadingZeroState = false;
|
||||
let suffix = true;
|
||||
let is12Hour = false;
|
||||
|
||||
// Create and add color buttons to the UI
|
||||
let colors = ['#000', '#f00', '#0f0', '#ff0', '#00f', '#f0f', '#0ff', '#fff'];
|
||||
let colorButtonsContainer = document.getElementById('color-buttons-container');
|
||||
colors.forEach((color, i) => {
|
||||
|
@ -121,38 +183,98 @@
|
|||
});
|
||||
});
|
||||
|
||||
let leadingZeroButton = document.getElementById('toggle-leading-zero');
|
||||
let hourFormatButton = document.getElementById('toggle-hour-format');
|
||||
let suffixButton = document.getElementById('toggle-suffix');
|
||||
let bgButton = document.getElementById('toggle-bg');
|
||||
|
||||
// Function to update the button text based on the current state
|
||||
function updateButtonState() {
|
||||
if (leadingZeroButton.disabled) {
|
||||
leadingZeroButton.textContent = prevLeadingZeroState ? 'Leading Zero: ON' : 'Leading Zero: OFF';
|
||||
} else {
|
||||
leadingZeroButton.textContent = leadingZero ? 'Leading Zero: ON' : 'Leading Zero: OFF';
|
||||
}
|
||||
hourFormatButton.textContent = is12Hour ? 'Time Mode: 12 Hour' : 'Time Mode: 24 Hour';
|
||||
suffixButton.textContent = suffix ? 'Suffix: ON' : 'Suffix: OFF';
|
||||
bgButton.textContent = isDarkBg ? 'Theme: Dark' : 'Theme: Light';
|
||||
|
||||
if (is12Hour) {
|
||||
prevLeadingZeroState = leadingZero; // Store the state before disabling
|
||||
leadingZeroButton.disabled = true;
|
||||
leadingZeroButton.textContent = prevLeadingZeroState ? 'Leading Zero: ON' : 'Leading Zero: OFF';
|
||||
} else {
|
||||
leadingZero = prevLeadingZeroState; // Retrieve the stored state
|
||||
leadingZeroButton.disabled = false;
|
||||
leadingZeroButton.textContent = leadingZero ? 'Leading Zero: ON' : 'Leading Zero: OFF';
|
||||
}
|
||||
}
|
||||
|
||||
function toggleLeadingZero() {
|
||||
if (!is12Hour) { // Only allow toggle if in 24-hour mode
|
||||
leadingZero = !leadingZero;
|
||||
prevLeadingZeroState = leadingZero; // Update previous state on each toggle
|
||||
updateButtonState();
|
||||
drawText(selectedColor);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleHourFormat() {
|
||||
is12Hour = !is12Hour;
|
||||
updateButtonState(); // This will also correctly update leadingZero
|
||||
drawText(selectedColor);
|
||||
}
|
||||
|
||||
|
||||
// Toggle the background color between dark and light
|
||||
function toggleBackground() {
|
||||
isDarkBg = !isDarkBg; // Toggle the background state
|
||||
let previewBox = document.getElementById("preview-box");
|
||||
previewBox.style.backgroundColor = isDarkBg ? "black" : "white";
|
||||
updateButtonState();
|
||||
drawText(selectedColor); // Redraw the text with updated color
|
||||
}
|
||||
|
||||
function toggleSuffix() {
|
||||
suffix = !suffix;
|
||||
document.getElementById("toggle-suffix").textContent = `Suffix: ${suffix ? 'On' : 'Off'}`;
|
||||
updateButtonState();
|
||||
drawText(selectedColor);
|
||||
}
|
||||
|
||||
function formatTime(date) {
|
||||
let hours = date.getHours();
|
||||
let minutes = date.getMinutes();
|
||||
let formattedHours = hours < 10 ? `${hours}` : `${hours}`;
|
||||
// If 12 hour format is selected, adjust hours
|
||||
if (is12Hour) {
|
||||
hours = hours % 12;
|
||||
hours = hours ? hours : 12; // the hour '0' should be '12' in 12h format
|
||||
}
|
||||
let formattedHours = (!is12Hour && leadingZero && hours < 10) ? `0${hours}` : `${hours}`;
|
||||
let formattedMinutes = minutes < 10 ? `0${minutes}` : `${minutes}`;
|
||||
return `${formattedHours}:${formattedMinutes}`;
|
||||
}
|
||||
|
||||
function getCurrentDate() {
|
||||
// Get the current date as a formatted string
|
||||
function getCurrentDate(suffix) {
|
||||
let months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
|
||||
let suffixes = ["st", "nd", "rd", "th", "th", "th", "th", "th", "th", "th", "th", "th", "th", "th", "th", "th", "th", "th", "th", "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th", "th", "st"];
|
||||
let date = new Date();
|
||||
let month = months[date.getMonth()];
|
||||
let day = date.getDate();
|
||||
let suffix = suffixes[day - 1];
|
||||
let suffixString = suffix ? suffixes[day - 1] : '';
|
||||
let year = date.getFullYear();
|
||||
return `${month} ${day}${suffix}, ${year}`;
|
||||
return `${month} ${day}${suffixString}, ${year}`;
|
||||
}
|
||||
|
||||
// Get the current day of the week as a string
|
||||
function getCurrentDay() {
|
||||
let days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
||||
let date = new Date();
|
||||
return days[date.getDay()];
|
||||
}
|
||||
|
||||
function toggleBackground() {
|
||||
isDarkBg = !isDarkBg; // Toggle the background state
|
||||
let previewBox = document.getElementById("preview-box");
|
||||
previewBox.style.backgroundColor = isDarkBg ? "black" : "white";
|
||||
drawText(selectedColor); // Redraw the text with updated color
|
||||
}
|
||||
|
||||
// Draw the time, date, and day of the week on the canvas
|
||||
function drawText(color) {
|
||||
let canvas = document.getElementById("preview-canvas");
|
||||
let ctx = canvas.getContext("2d");
|
||||
|
@ -194,17 +316,17 @@
|
|||
ctx.fillText(currentTime, xPos, yPos);
|
||||
|
||||
// Set the font for the date
|
||||
ctx.font = "19px DotGothic16";
|
||||
ctx.font = "13px Righteous";
|
||||
|
||||
// Get the current date
|
||||
let currentDate = getCurrentDate();
|
||||
let currentDate = getCurrentDate(suffix);
|
||||
|
||||
// Measure the date width and calculate the horizontal position
|
||||
let dateWidth = ctx.measureText(currentDate).width;
|
||||
xPos = (canvas.width - dateWidth) / 2;
|
||||
|
||||
// Draw the date
|
||||
yPos += 20;
|
||||
yPos += 28;
|
||||
ctx.fillText(currentDate, xPos, yPos);
|
||||
|
||||
// Get the current day of the week
|
||||
|
@ -215,9 +337,12 @@
|
|||
xPos = (canvas.width - dayWidth) / 2;
|
||||
|
||||
// Draw the day of the week
|
||||
ctx.fillText(currentDay, xPos, yPos + 20);
|
||||
ctx.fillText(currentDay, xPos, yPos + 12);
|
||||
}
|
||||
|
||||
// Converts a hexadecimal color code to a 16-bit integer value.
|
||||
// If the input hex string is in short format (#rgb), it's converted to long format (#rrggbb) first.
|
||||
// Returns the 16-bit integer value representing the color.
|
||||
function hexToDec(hex) {
|
||||
if (hex.length === 4) {
|
||||
hex = `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}`;
|
||||
|
@ -249,21 +374,23 @@
|
|||
};
|
||||
}
|
||||
|
||||
function saveThemeToSettings(theme) {
|
||||
Puck.eval('require("Storage").readJSON("setting.json", true)', (data) => {
|
||||
// Save the provided theme to Bangle.js storage and set Shadow Clock as the default clock
|
||||
function saveThemeToSettings(themeColors) {
|
||||
Puck.eval('[require("Storage").readJSON("setting.json", true)]', (dataArray) => {
|
||||
let data = dataArray ? dataArray[0] : null;
|
||||
if (data) {
|
||||
// Ensure that data.theme exists
|
||||
if (!data.theme) {
|
||||
data.theme = {};
|
||||
}
|
||||
|
||||
// Save all theme values
|
||||
for (let key in theme) {
|
||||
data.theme[key] = theme[key];
|
||||
for (let key in themeColors) {
|
||||
data.theme[key] = themeColors[key];
|
||||
}
|
||||
|
||||
data.clock = "shadowclk.app.js"; // Set Shadow Clock as default
|
||||
|
||||
// Save settings for 12 or 24 hour mode
|
||||
data["12hour"] = is12Hour;
|
||||
// Set Shadow Clock as default
|
||||
data.clock = "shadowclk.app.js";
|
||||
Puck.write(`require("Storage").write("setting.json", ${JSON.stringify(data)});\n`, (result) => {
|
||||
console.log('Theme saved:', result);
|
||||
});
|
||||
|
@ -271,91 +398,88 @@
|
|||
});
|
||||
}
|
||||
|
||||
// Load Shadow Clock color and theme setting
|
||||
let selectedColor = "#0ff";
|
||||
let isDarkBg = false;
|
||||
|
||||
function loadSettings(callback) {
|
||||
// Set a timeout for loading the settings
|
||||
const timeout = new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error("Timeout occurred")), 500)
|
||||
);
|
||||
|
||||
const loadSettingsFromStorage = new Promise((resolve) => {
|
||||
Puck.eval('require("Storage").readJSON("shadowclk.json", true)', (data) => {
|
||||
if (data) {
|
||||
// Apply color and theme from JSON
|
||||
const { color, theme } = data;
|
||||
selectedColor = color;
|
||||
isDarkBg = theme === "dark";
|
||||
displayMessage("Previous settings loaded.", 3000);
|
||||
} else {
|
||||
// Use default values if there is no data for color and theme
|
||||
selectedColor = "#0ff";
|
||||
isDarkBg = false;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
Puck.eval('[require("Storage").readJSON("shadowclk.json", true)]', (dataArray) => {
|
||||
let data = dataArray ? dataArray[0] : null;
|
||||
if (data) {
|
||||
// Apply color, theme, enableLeadingZero, enableSuffix and timeMode from JSON
|
||||
let { color, theme, enableLeadingZero, enableSuffix, enable12Hour } = data;
|
||||
selectedColor = color !== undefined ? color : selectedColor;
|
||||
isDarkBg = theme !== undefined ? (theme === "dark") : isDarkBg;
|
||||
// Use the JSON values if they exist, otherwise use the current values
|
||||
leadingZero = enableLeadingZero !== undefined ? enableLeadingZero : leadingZero;
|
||||
prevLeadingZeroState = leadingZero; // Store the current state
|
||||
suffix = enableSuffix !== undefined ? enableSuffix : suffix;
|
||||
is12Hour = enable12Hour !== undefined ? enable12Hour : is12Hour; // Update the time mode from the settings
|
||||
displayMessage("Previous settings loaded.", 3000);
|
||||
} else {
|
||||
// If no JSON data, load defaults
|
||||
selectedColor = selectedColor;
|
||||
isDarkBg = isDarkBg;
|
||||
leadingZero = leadingZero;
|
||||
prevLeadingZeroState = leadingZero;
|
||||
suffix = suffix;
|
||||
is12Hour = is12Hour;
|
||||
displayMessage("Defaults loaded.", 3000);
|
||||
}
|
||||
updateButtonState(); // Update button state regardless
|
||||
callback();
|
||||
});
|
||||
|
||||
Promise.race([loadSettingsFromStorage, timeout])
|
||||
.then(() => callback())
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
// Use default values in case of a timeout or error
|
||||
selectedColor = "#0ff";
|
||||
isDarkBg = false;
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
// Update the time display every second
|
||||
function updateTime() {
|
||||
setInterval(() => {
|
||||
drawText(selectedColor);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// Display a message for a given duration and then remove it
|
||||
function displayMessage(text, timeout) {
|
||||
// Remove any existing message
|
||||
while (messageDiv.firstChild) {
|
||||
messageDiv.removeChild(messageDiv.firstChild);
|
||||
}
|
||||
|
||||
// Create a new message element
|
||||
const message = document.createElement('p');
|
||||
message.innerHTML = text; // Use innerHTML instead of textContent
|
||||
message.style.fontSize = '24px';
|
||||
messageDiv.appendChild(message);
|
||||
|
||||
// Remove the message element after the timeout
|
||||
setTimeout(() => {
|
||||
messageDiv.removeChild(message);
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
// Attach an event listener to the 'upload' button to save color and theme settings to Bangle.js storage
|
||||
document.getElementById("upload").addEventListener("click", function () {
|
||||
// Save theme settings to Bangle.js
|
||||
let themeColors = createThemeColors(isDarkBg);
|
||||
saveThemeToSettings(themeColors);
|
||||
|
||||
// Save Shadow Clock color setting
|
||||
// Save Shadow Clock app setting
|
||||
let data = {
|
||||
color: selectedColor,
|
||||
theme: isDarkBg ? "dark" : "light"
|
||||
theme: isDarkBg ? "dark" : "light",
|
||||
enableLeadingZero: leadingZero,
|
||||
enableSuffix: suffix,
|
||||
enable12Hour: is12Hour
|
||||
};
|
||||
|
||||
// Write the updated settings back to shadowclk.json
|
||||
Puck.write(`require("Storage").write("shadowclk.json", ${JSON.stringify(data)});\n`, (result) => {
|
||||
console.log('Color saved:', result);
|
||||
console.log('App settings saved:', result);
|
||||
});
|
||||
|
||||
// Display the message using displayMessage function
|
||||
displayMessage('Configuration sent...<br>Hold button on Bangle.js', 5000);
|
||||
});
|
||||
|
||||
// Load required fonts for the application
|
||||
function loadFonts() {
|
||||
return Promise.all([
|
||||
document.fonts.load('81px Londrina Solid'),
|
||||
document.fonts.load('81px Londrina Shadow'),
|
||||
document.fonts.load('19px DotGothic16')
|
||||
document.fonts.load('13px Righteous')
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -367,4 +491,4 @@
|
|||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "shadowclk",
|
||||
"name": "Shadow Clock",
|
||||
"version": "0.02",
|
||||
"version": "0.03",
|
||||
"description": "A simple clock using the Londrina font in color with a shadowed outline. Based on the Anton Clock.",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{
|
||||
|
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.1 KiB |
|
@ -1,18 +1,17 @@
|
|||
(function(back) {
|
||||
let teletextColors = ["#000", "#f00", "#0f0", "#ff0", "#00f", "#f0f", "#0ff", "#fff"];
|
||||
let teletextColorNames = ["Black", "Red", "Green", "Yellow", "Blue", "Magenta", "Cyan", "White"];
|
||||
let sysSettings = require('Storage').readJSON("setting.json", 1) || {};
|
||||
|
||||
// Load and set default settings
|
||||
let appSettings = Object.assign({
|
||||
color: teletextColors[6],
|
||||
theme: 'light',
|
||||
enableSuffix: true,
|
||||
enableLeadingZero: false,
|
||||
enable12Hour: '24hour' // default time mode
|
||||
}, require('Storage').readJSON("shadowclk.json", true) || {});
|
||||
|
||||
// Save settings to storage
|
||||
function writeSettings() {
|
||||
require('Storage').writeJSON("shadowclk.json", appSettings);
|
||||
}
|
||||
|
||||
// Colors from 'Light BW' and 'Dark BW' themes
|
||||
function createThemeColors(mode) {
|
||||
let cl = x => g.setColor(x).getColor();
|
||||
|
@ -66,15 +65,36 @@
|
|||
|
||||
// Read the current system theme
|
||||
function getCurrentTheme() {
|
||||
let s = require('Storage').readJSON("setting.json", 1) || {};
|
||||
if (!s.theme) {
|
||||
if (!sysSettings.theme) {
|
||||
return appSettings.theme; // fallback to appSettings.theme (light or dark)
|
||||
}
|
||||
return s.theme.dark ? 'dark' : 'light';
|
||||
return sysSettings.theme.dark ? 'dark' : 'light';
|
||||
}
|
||||
|
||||
// Read the current time mode
|
||||
function getCurrentTimeMode() {
|
||||
if (!sysSettings['12hour']) {
|
||||
return appSettings.enable12Hour; // fallback to appSettings.enable12Hour
|
||||
}
|
||||
return sysSettings['12hour'] ? '12hour' : '24hour';
|
||||
}
|
||||
|
||||
// Save settings to storage
|
||||
function writeSettings() {
|
||||
appSettings.enable12Hour = appSettings.enable12Hour === '12hour' ? '12hour' : '24hour';
|
||||
require('Storage').writeJSON("shadowclk.json", appSettings);
|
||||
}
|
||||
|
||||
// Save time mode to system settings
|
||||
function writeTimeModeSetting() {
|
||||
sysSettings['12hour'] = appSettings.enable12Hour === '12hour';
|
||||
require('Storage').writeJSON("setting.json", sysSettings);
|
||||
}
|
||||
|
||||
function showMenu() {
|
||||
appSettings.theme = getCurrentTheme();
|
||||
appSettings.enable12Hour = getCurrentTimeMode();
|
||||
|
||||
E.showMenu({
|
||||
"": {
|
||||
"title": "Shadow Clock"
|
||||
|
@ -96,9 +116,34 @@
|
|||
writeSettings();
|
||||
},
|
||||
format: v => teletextColorNames[v]
|
||||
},
|
||||
'Date Suffix:': {
|
||||
value: appSettings.enableSuffix,
|
||||
format: v => v ? 'Yes' : 'No',
|
||||
onchange: v => {
|
||||
appSettings.enableSuffix = v;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
'Lead Zero:': {
|
||||
value: appSettings.enableLeadingZero,
|
||||
format: v => v ? 'Yes' : 'No',
|
||||
onchange: v => {
|
||||
appSettings.enableLeadingZero = v;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
'Time Mode:': {
|
||||
value: (appSettings.enable12Hour === '12hour'),
|
||||
format: v => v ? '12 Hr' : '24 Hr',
|
||||
onchange: v => {
|
||||
appSettings.enable12Hour = v ? '12hour' : '24hour';
|
||||
writeSettings();
|
||||
writeTimeModeSetting();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// Initially show the menu
|
||||
showMenu();
|
||||
});
|
||||
});
|
|
@ -1,3 +1,3 @@
|
|||
0.01: Initial Version: Display at under 30% battery
|
||||
0.02: Display while charging
|
||||
|
||||
0.03: Do not clear outside of widget bar
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{ "id": "widminbat",
|
||||
"name": "Minimal Battery",
|
||||
"shortName":"MinBat",
|
||||
"version":"0.02",
|
||||
"version":"0.03",
|
||||
"description": "A minimal version of the battery widget that only appears if the battery is running low (below 30%)",
|
||||
"icon": "widget.png",
|
||||
"type": "widget",
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
var bat = E.getBattery();
|
||||
var x = this.x, y = this.y;
|
||||
g.reset();
|
||||
g.clearRect(x,y,x+s,y+24);
|
||||
g.clearRect(x,y,x+s,y+23);
|
||||
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 barWidth = bat*(s-12)/100;
|
||||
var color = bat < 15 ? "#f00" : (bat <= 30 ? "#f80" : "#0f0");
|
||||
|
|
|
@ -9,30 +9,67 @@ declare module Sched {
|
|||
SAT = 64,
|
||||
}
|
||||
|
||||
type VibratePattern = "." | "," | "-" | ":" | ";" | "=";
|
||||
type Dows = number;
|
||||
type Milliseconds = number;
|
||||
|
||||
// slight hack - all objects have a `on()`, this unions with that type so we can add it to an object
|
||||
type OnBoolean<T extends boolean = boolean>
|
||||
= T | Object["on"];
|
||||
|
||||
type VibratePattern = string; // "." | "," | "-" | ":" | ";" | "="
|
||||
|
||||
type DateString = `${number}-${number}-${number}`;
|
||||
|
||||
type NewSched = {
|
||||
msg?: string,
|
||||
appid?: string,
|
||||
dow?: Dows,
|
||||
on?: OnBoolean<false>,
|
||||
js?: string,
|
||||
} & (NewTimer | NewAlarm);
|
||||
|
||||
type NewTimer = { timer: number };
|
||||
type NewAlarm = { t: number, date?: DateString };
|
||||
|
||||
type DefaultSched = {
|
||||
on: OnBoolean<true>,
|
||||
del: boolean,
|
||||
rp: false,
|
||||
as: false,
|
||||
dow: Dows,
|
||||
last: number,
|
||||
vibrate: VibratePattern,
|
||||
};
|
||||
|
||||
type DefaultAlarm = DefaultSched & { t: number };
|
||||
|
||||
type DefaultTimer = DefaultSched & { timer: number };
|
||||
|
||||
type Sched = {
|
||||
id?: string,
|
||||
appid?: string,
|
||||
on: boolean,
|
||||
dow?: number,
|
||||
// from NewSched / set in setAlarm()
|
||||
msg: string,
|
||||
last: number,
|
||||
appid?: string,
|
||||
dow: Dows,
|
||||
on: OnBoolean,
|
||||
timer?: Milliseconds, // this is a timer
|
||||
|
||||
// setAlarm adds:
|
||||
id: string,
|
||||
t: Milliseconds, // time of day since midnight (in ms)
|
||||
|
||||
// optional NewSched
|
||||
vibrate?: VibratePattern,
|
||||
hidden?: boolean,
|
||||
as?: boolean, // auto snooze
|
||||
del?: boolean,
|
||||
js?: string,
|
||||
data?: unknown,
|
||||
|
||||
// set by sched
|
||||
last?: number,
|
||||
} & (
|
||||
{
|
||||
t: number, // time of day since midnight (in ms, set automatically when timer starts)
|
||||
} | {
|
||||
timer: number, // this is a timer - the time in ms
|
||||
}
|
||||
) & (
|
||||
{
|
||||
date: `${number}-${number}-${number}`,
|
||||
date: DateString,
|
||||
rp?: Repeat,
|
||||
} | {
|
||||
date: undefined,
|
||||
|
@ -62,18 +99,18 @@ declare module Sched {
|
|||
|
||||
function getAlarm(id: string): Sched | undefined;
|
||||
|
||||
function getActiveAlarms (alarms: Sched[], time?: Date): Sched[];
|
||||
function getActiveAlarms(alarms: Sched[], time?: Date): Sched[];
|
||||
|
||||
function setAlarm(id: string, alarm?: Sched): void;
|
||||
function setAlarm(id: string, alarm?: NewSched): void;
|
||||
|
||||
function getTimeToAlarm(alarm: Sched, time?: Date): number | undefined;
|
||||
function getTimeToAlarm(alarm: Sched | undefined | null, time?: Date): number | undefined;
|
||||
function getTimeToAlarm(alarm?: undefined | null, time?: Date): undefined;
|
||||
|
||||
function reload(): void;
|
||||
|
||||
function newDefaultAlarm(): Sched;
|
||||
function newDefaultAlarm(): DefaultAlarm;
|
||||
|
||||
function newDefaultTimer(): Sched;
|
||||
function newDefaultTimer(): DefaultTimer;
|
||||
|
||||
function getSettings(): SchedSettings;
|
||||
|
||||
|
|