Merge remote-tracking branch 'upstream/master'
116
apps.json
|
@ -4,11 +4,12 @@
|
|||
"tags": "tool,system",
|
||||
"type":"bootloader",
|
||||
"icon": "bootloader.png",
|
||||
"version":"0.22",
|
||||
"version":"0.25",
|
||||
"description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings",
|
||||
"storage": [
|
||||
{"name":".boot0","url":"boot0.js"},
|
||||
{"name":".bootcde","url":"bootloader.js"}
|
||||
{"name":".bootcde","url":"bootloader.js"},
|
||||
{"name":"bootupdate.js","url":"bootupdate.js"}
|
||||
],
|
||||
"sortorder" : -10
|
||||
},
|
||||
|
@ -41,7 +42,7 @@
|
|||
"name": "Launcher (Default)",
|
||||
"shortName":"Launcher",
|
||||
"icon": "app.png",
|
||||
"version":"0.04",
|
||||
"version":"0.06",
|
||||
"description": "This is needed by Bangle.js to display a menu allowing you to choose your own applications. You can replace this with a customised launcher.",
|
||||
"tags": "tool,system,launcher",
|
||||
"type":"launch",
|
||||
|
@ -53,7 +54,7 @@
|
|||
{ "id": "about",
|
||||
"name": "About",
|
||||
"icon": "app.png",
|
||||
"version":"0.07",
|
||||
"version":"0.08",
|
||||
"description": "Bangle.js About page - showing software version, stats, and a collaborative mural from the Bangle.js KickStarter backers",
|
||||
"tags": "tool,system",
|
||||
"allow_emulator":true,
|
||||
|
@ -104,7 +105,7 @@
|
|||
{ "id": "welcome",
|
||||
"name": "Welcome",
|
||||
"icon": "app.png",
|
||||
"version":"0.09",
|
||||
"version":"0.10",
|
||||
"description": "Appears at first boot and explains how to use Bangle.js",
|
||||
"tags": "start,welcome",
|
||||
"allow_emulator":true,
|
||||
|
@ -171,13 +172,12 @@
|
|||
{ "id": "setting",
|
||||
"name": "Settings",
|
||||
"icon": "settings.png",
|
||||
"version":"0.24",
|
||||
"version":"0.26",
|
||||
"description": "A menu for setting up Bangle.js",
|
||||
"tags": "tool,system",
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"setting.app.js","url":"settings.js"},
|
||||
{"name":"setting.boot.js","url":"boot.js"},
|
||||
{"name":"setting.img","url":"settings-icon.js","evaluate":true}
|
||||
],
|
||||
"data": [
|
||||
|
@ -216,6 +216,32 @@
|
|||
{"name":"wclock.img","url":"clock-word-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "fontclock",
|
||||
"name": "Font Clock",
|
||||
"icon": "fontclock.png",
|
||||
"version":"0.01",
|
||||
"description": "Choose the font and design of clock face from a library of available designs",
|
||||
"tags": "clock",
|
||||
"type":"clock",
|
||||
"allow_emulator":false,
|
||||
"readme": "README.md",
|
||||
"custom":"custom.html",
|
||||
"storage": [
|
||||
{"name":"fontclock.app.js","url":"fontclock.js"},
|
||||
{"name":"fontclock.img","url":"fontclock-icon.js","evaluate":true},
|
||||
{"name":"fontclock.hand.js","url":"fontclock.hand.js"},
|
||||
{"name":"fontclock.thinhand.js","url":"fontclock.thinhand.js"},
|
||||
{"name":"fontclock.thickhand.js","url":"fontclock.thickhand.js"},
|
||||
{"name":"fontclock.hourscriber.js","url":"fontclock.hourscriber.js"},
|
||||
{"name":"fontclock.font.js","url":"fontclock.font.js"},
|
||||
{"name":"fontclock.font.abril_ff50.js","url":"fontclock.font.abril_ff50.js"},
|
||||
{"name":"fontclock.font.cpstc58.js","url":"fontclock.font.cpstc58.js"},
|
||||
{"name":"fontclock.font.mntn25.js","url":"fontclock.font.mntn25.js"},
|
||||
{"name":"fontclock.font.mntn50.js","url":"fontclock.font.mntn50.js"},
|
||||
{"name":"fontclock.font.vector25.js","url":"fontclock.font.vector25.js"},
|
||||
{"name":"fontclock.font.vector50.js","url":"fontclock.font.vector50.js"}
|
||||
]
|
||||
},
|
||||
{ "id": "slidingtext",
|
||||
"name": "Sliding Clock",
|
||||
"icon": "slidingtext.png",
|
||||
|
@ -223,7 +249,7 @@
|
|||
"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",
|
||||
"tags": "clock",
|
||||
"type":"clock",
|
||||
"allow_emulator":true,
|
||||
"allow_emulator":false,
|
||||
"readme": "README.md",
|
||||
"custom":"custom.html",
|
||||
"storage": [
|
||||
|
@ -242,8 +268,8 @@
|
|||
{ "id": "sweepclock",
|
||||
"name": "Sweep Clock",
|
||||
"icon": "sweepclock.png",
|
||||
"version":"0.02",
|
||||
"description": "Smooth sweep secondhand with single hour numeral. Use button1 to toggle the numeral font and button3 to change the colour theme",
|
||||
"version":"0.04",
|
||||
"description": "Smooth sweep secondhand with single hour numeral. Use button 1 to toggle the numeral font, button 3 to change the colour theme and button 4 to change the date placement",
|
||||
"tags": "clock",
|
||||
"type":"clock",
|
||||
"allow_emulator":true,
|
||||
|
@ -545,7 +571,7 @@
|
|||
{ "id": "widbat",
|
||||
"name": "Battery Level Widget",
|
||||
"icon": "widget.png",
|
||||
"version":"0.05",
|
||||
"version":"0.06",
|
||||
"description": "Show the current battery level and charging status in the top right of the clock",
|
||||
"tags": "widget,battery",
|
||||
"type":"widget",
|
||||
|
@ -553,6 +579,17 @@
|
|||
{"name":"widbat.wid.js","url":"widget.js"}
|
||||
]
|
||||
},
|
||||
{ "id": "widlock",
|
||||
"name": "Lock Widget",
|
||||
"icon": "widget.png",
|
||||
"version":"0.01",
|
||||
"description": "On devices with always-on display (Bangle.js 2) this displays lock icon whenever the display is locked",
|
||||
"tags": "widget,lock",
|
||||
"type":"widget",
|
||||
"storage": [
|
||||
{"name":"widlock.wid.js","url":"widget.js"}
|
||||
]
|
||||
},
|
||||
{ "id": "widbatpc",
|
||||
"name": "Battery Level Widget (with percentage)",
|
||||
"shortName": "Battery Widget",
|
||||
|
@ -628,7 +665,7 @@
|
|||
{ "id": "hrm",
|
||||
"name": "Heart Rate Monitor",
|
||||
"icon": "heartrate.png",
|
||||
"version":"0.03",
|
||||
"version":"0.04",
|
||||
"description": "Measure your heart rate and see live sensor data",
|
||||
"tags": "health",
|
||||
"storage": [
|
||||
|
@ -820,6 +857,19 @@
|
|||
{"name":"sclock.img","url":"clock-simple-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "s7clk",
|
||||
"name": "Simple 7 segment Clock",
|
||||
"icon": "icon.png",
|
||||
"version":"0.02",
|
||||
"description": "A simple 7 segment Clock with date",
|
||||
"tags": "clock",
|
||||
"type":"clock",
|
||||
"allow_emulator":true,
|
||||
"storage": [
|
||||
{"name":"s7clk.app.js","url":"app.js"},
|
||||
{"name":"s7clk.img","url":"icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "vibrclock",
|
||||
"name": "Vibrate Clock",
|
||||
"icon": "app.png",
|
||||
|
@ -1088,7 +1138,7 @@
|
|||
{ "id": "widpedom",
|
||||
"name": "Pedometer widget",
|
||||
"icon": "widget.png",
|
||||
"version":"0.12",
|
||||
"version":"0.13",
|
||||
"description": "Daily pedometer widget",
|
||||
"tags": "widget",
|
||||
"type":"widget",
|
||||
|
@ -1234,7 +1284,7 @@
|
|||
"name": "Commandline-Clock",
|
||||
"shortName":"CLI-Clock",
|
||||
"icon": "app.png",
|
||||
"version":"0.11",
|
||||
"version":"0.12",
|
||||
"description": "Simple CLI-Styled Clock",
|
||||
"tags": "clock,cli,command,bash,shell",
|
||||
"type":"clock",
|
||||
|
@ -2234,7 +2284,7 @@
|
|||
{ "id": "multiclock",
|
||||
"name": "Multi Clock",
|
||||
"icon": "multiclock.png",
|
||||
"version":"0.12",
|
||||
"version":"0.13",
|
||||
"description": "Clock with multiple faces - Big, Analogue, Digital, Text, Time-Date.\n Switch between faces with BTN1 & BTN3",
|
||||
"readme": "README.md",
|
||||
"tags": "clock",
|
||||
|
@ -3044,7 +3094,7 @@
|
|||
"name": "Gadgetbridge Music Controls",
|
||||
"shortName":"Music Controls",
|
||||
"icon": "icon.png",
|
||||
"version":"0.04",
|
||||
"version":"0.05",
|
||||
"description": "Control the music on your Gadgetbridge-connected phone",
|
||||
"tags": "tools,bluetooth,gadgetbridge,music",
|
||||
"type":"app",
|
||||
|
@ -3155,7 +3205,7 @@
|
|||
"id": "omnitrix",
|
||||
"name":"Omnitrix",
|
||||
"icon":"omnitrix.png",
|
||||
"version": "1.0",
|
||||
"version": "0.01",
|
||||
"readme": "README.md",
|
||||
"description": "An Omnitrix Showpiece",
|
||||
"tags": "game",
|
||||
|
@ -3169,7 +3219,7 @@
|
|||
"name": "Bat Clock",
|
||||
"shortName":"Bat Clock",
|
||||
"icon": "bat-clock.png",
|
||||
"version":"1.0",
|
||||
"version":"0.01",
|
||||
"description": "Morphing Clock, with an awesome \"The Dark Knight\" themed logo.",
|
||||
"tags": "clock",
|
||||
"type": "clock",
|
||||
|
@ -3178,5 +3228,35 @@
|
|||
{"name":"batclock.app.js","url":"bat-clock.app.js"},
|
||||
{"name":"batclock.img","url":"bat-clock.icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id":"doztime",
|
||||
"name":"Dozenal Time",
|
||||
"shortName":"Dozenal Time",
|
||||
"icon":"app.png",
|
||||
"version":"0.01",
|
||||
"description":"A dozenal Holocene calendar and dozenal diurnal clock",
|
||||
"tags":"clock",
|
||||
"type":"clock",
|
||||
"allow_emulator":true,
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"doztime.app.js","url":"app.js"},
|
||||
{"name":"doztime.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id":"gbtwist",
|
||||
"name":"Gadgetbridge Twist Control",
|
||||
"shortName":"Twist Control",
|
||||
"icon":"app.png",
|
||||
"version":"0.01",
|
||||
"description":"Shake your wrist to control your music app via Gadgetbridge",
|
||||
"tags":"tools,bluetooth,gadgetbridge,music",
|
||||
"type":"app",
|
||||
"allow_emulator":false,
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"gbtwist.app.js","url":"app.js"},
|
||||
{"name":"gbtwist.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -5,3 +5,4 @@
|
|||
0.05: Actual pixels as of 27 Apr 2020
|
||||
0.06: Actual pixels as of 12 Jun 2020
|
||||
0.07: Pressing a button now exits immediately (fix #618)
|
||||
0.08: Make about (mostly) work on non-240px screens
|
||||
|
|
|
@ -21,3 +21,6 @@
|
|||
0.20: Allow Gadgetbridge to work even with programmable:off
|
||||
0.21: Handle echo off char from Gadgetbridge app when programmable:off (fix #558)
|
||||
0.22: Stop LCD timeout being disabled on first run (when there is no settings.json)
|
||||
0.23: Move to a precalculated .boot0 file which should speed up load time
|
||||
0.24: Add Bangle.setUI polyfill
|
||||
0.25: Fix error in 'no clock app' message
|
||||
|
|
|
@ -1,68 +1,2 @@
|
|||
// This ALWAYS runs at boot
|
||||
E.setFlags({pretokenise:1});
|
||||
// Load settings...
|
||||
var s = require('Storage').readJSON('setting.json',1)||{};
|
||||
if (s.ble!==false) {
|
||||
if (s.HID) { // Human interface device
|
||||
if (s.HID=="joy") Bangle.HID = E.toUint8Array(atob("BQEJBKEBCQGhAAUJGQEpBRUAJQGVBXUBgQKVA3UBgQMFAQkwCTEVgSV/dQiVAoECwMA="));
|
||||
else if (s.HID=="kb") Bangle.HID = E.toUint8Array(atob("BQEJBqEBBQcZ4CnnFQAlAXUBlQiBApUBdQiBAZUFdQEFCBkBKQWRApUBdQORAZUGdQgVACVzBQcZAClzgQAJBRUAJv8AdQiVArECwA=="));
|
||||
else /*kbmedia*/Bangle.HID = E.toUint8Array(atob("BQEJBqEBhQIFBxngKecVACUBdQGVCIEClQF1CIEBlQV1AQUIGQEpBZEClQF1A5EBlQZ1CBUAJXMFBxkAKXOBAAkFFQAm/wB1CJUCsQLABQwJAaEBhQEVACUBdQGVAQm1gQIJtoECCbeBAgm4gQIJzYECCeKBAgnpgQIJ6oECwA=="));
|
||||
NRF.setServices({}, {uart:true, hid:Bangle.HID});
|
||||
}
|
||||
}
|
||||
if (s.blerepl===false) { // If not programmable, force terminal off Bluetooth
|
||||
if (s.log) Terminal.setConsole(true); // if showing debug, force REPL onto terminal
|
||||
else E.setConsole(null,{force:true}); // on new (2v05+) firmware we have E.setConsole which allows a 'null' console
|
||||
/* If not programmable add our own handler for Bluetooth data
|
||||
to allow Gadgetbridge commands to be received*/
|
||||
Bluetooth.line="";
|
||||
Bluetooth.on('data',function(d) {
|
||||
var l = (Bluetooth.line + d).split("\n");
|
||||
Bluetooth.line = l.pop();
|
||||
l.forEach(n=>Bluetooth.emit("line",n));
|
||||
});
|
||||
Bluetooth.on('line',function(l) {
|
||||
if (l.startsWith('\x10')) l=l.slice(1);
|
||||
if (l.startsWith('GB({') && l.endsWith('})') && global.GB)
|
||||
try { global.GB(JSON.parse(l.slice(3,-1))); } catch(e) {}
|
||||
});
|
||||
} else {
|
||||
if (s.log && !NRF.getSecurityStatus().connected) Terminal.setConsole(); // if showing debug, put REPL on terminal (until connection)
|
||||
else Bluetooth.setConsole(true); // else if no debug, force REPL to Bluetooth
|
||||
}
|
||||
// we just reset, so BLE should be on.
|
||||
// Don't disconnect if something is already connected to us
|
||||
if (s.ble===false && !NRF.getSecurityStatus().connected) NRF.sleep();
|
||||
// Set time, vibrate, beep, etc
|
||||
if (!Bangle.F_BEEPSET) {
|
||||
if (!s.vibrate) Bangle.buzz=Promise.resolve;
|
||||
if (s.beep===false) Bangle.beep=Promise.resolve;
|
||||
else if (s.beep=="vib") Bangle.beep = function (time, freq) {
|
||||
return new Promise(function(resolve) {
|
||||
if ((0|freq)<=0) freq=4000;
|
||||
if ((0|time)<=0) time=200;
|
||||
if (time>5000) time=5000;
|
||||
analogWrite(D13,0.1,{freq:freq});
|
||||
setTimeout(function() {
|
||||
digitalWrite(D13,0);
|
||||
resolve();
|
||||
}, time);
|
||||
});
|
||||
};
|
||||
}
|
||||
if (s.timeout!==undefined) Bangle.setLCDTimeout(s.timeout);
|
||||
if (!s.timeout) Bangle.setLCDPower(1);
|
||||
E.setTimeZone(s.timezone);
|
||||
delete s;
|
||||
// Draw out of memory errors onto the screen
|
||||
E.on('errorFlag', function(errorFlags) {
|
||||
g.reset(1).setColor("#ff0000").setFont("6x8").setFontAlign(0,1).drawString(errorFlags,g.getWidth()/2,g.getHeight()-1).flip();
|
||||
print("Interpreter error:", errorFlags);
|
||||
E.getErrorFlags(); // clear flags so we get called next time
|
||||
});
|
||||
// stop users doing bad things!
|
||||
global.save = function() { throw new Error("You can't use save() on Bangle.js without overwriting the bootloader!"); }
|
||||
// Load *.boot.js files
|
||||
require('Storage').list(/\.boot\.js/).forEach(bootFile=>{
|
||||
eval(require('Storage').read(bootFile));
|
||||
});
|
||||
// Initially this runs and rewrites itself
|
||||
eval(require('Storage').read('bootupdate.js'));
|
||||
|
|
|
@ -14,11 +14,7 @@ if (!clockApp) {
|
|||
if (clockApp)
|
||||
clockApp = require("Storage").read(clockApp.src);
|
||||
}
|
||||
if (!clockApp) clockApp=`E.showMessage("No Clock Found");
|
||||
setWatch(() => {
|
||||
Bangle.showLauncher();
|
||||
}, BTN2, {repeat:false,edge:"falling"});)
|
||||
`;
|
||||
if (!clockApp) clockApp=`E.showMessage("No Clock Found");setWatch(()=>{Bangle.showLauncher();}, BTN2, {repeat:false,edge:"falling"});`;
|
||||
// check to see if our clock is wrong - if it is use GPS time
|
||||
if ((new Date()).getFullYear()<2000) {
|
||||
E.showMessage("Searching for\nGPS time");
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
/* This rewrites boot0.js based on current settings. If settings changed then it
|
||||
recalculates, but this avoids us doing a whole bunch of reconfiguration most
|
||||
of the time. */
|
||||
E.showMessage("Updating boot0...");
|
||||
var s = require('Storage').readJSON('setting.json',1)||{};
|
||||
var boot = "";
|
||||
var CRC = E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\.boot\.js/));
|
||||
boot += `if (E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\.boot\.js/))!=${CRC}) { eval(require('Storage').read('bootupdate.js'));} else {\n`;
|
||||
boot += `E.setFlags({pretokenise:1});\n`;
|
||||
if (s.ble!==false) {
|
||||
if (s.HID) { // Human interface device
|
||||
if (s.HID=="joy") boot += `Bangle.HID = E.toUint8Array(atob("BQEJBKEBCQGhAAUJGQEpBRUAJQGVBXUBgQKVA3UBgQMFAQkwCTEVgSV/dQiVAoECwMA="));`;
|
||||
else if (s.HID=="kb") boot += `Bangle.HID = E.toUint8Array(atob("BQEJBqEBBQcZ4CnnFQAlAXUBlQiBApUBdQiBAZUFdQEFCBkBKQWRApUBdQORAZUGdQgVACVzBQcZAClzgQAJBRUAJv8AdQiVArECwA=="));`
|
||||
else /*kbmedia*/boot += `Bangle.HID = E.toUint8Array(atob("BQEJBqEBhQIFBxngKecVACUBdQGVCIEClQF1CIEBlQV1AQUIGQEpBZEClQF1A5EBlQZ1CBUAJXMFBxkAKXOBAAkFFQAm/wB1CJUCsQLABQwJAaEBhQEVACUBdQGVAQm1gQIJtoECCbeBAgm4gQIJzYECCeKBAgnpgQIJ6oECwA=="));`;
|
||||
boot += `NRF.setServices({}, {uart:true, hid:Bangle.HID});\n`;
|
||||
}
|
||||
}
|
||||
if (s.blerepl===false) { // If not programmable, force terminal off Bluetooth
|
||||
if (s.log) boot += `Terminal.setConsole(true);\n`; // if showing debug, force REPL onto terminal
|
||||
else boot += `E.setConsole(null,{force:true});\n`; // on new (2v05+) firmware we have E.setConsole which allows a 'null' console
|
||||
/* If not programmable add our own handler for Bluetooth data
|
||||
to allow Gadgetbridge commands to be received*/
|
||||
boot += `
|
||||
Bluetooth.line="";
|
||||
Bluetooth.on('data',function(d) {
|
||||
var l = (Bluetooth.line + d).split("\n");
|
||||
Bluetooth.line = l.pop();
|
||||
l.forEach(n=>Bluetooth.emit("line",n));
|
||||
});
|
||||
Bluetooth.on('line',function(l) {
|
||||
if (l.startsWith('\x10')) l=l.slice(1);
|
||||
if (l.startsWith('GB({') && l.endsWith('})') && global.GB)
|
||||
try { global.GB(JSON.parse(l.slice(3,-1))); } catch(e) {}
|
||||
});\n`;
|
||||
} else {
|
||||
if (s.log) boot += `if (!NRF.getSecurityStatus().connected) Terminal.setConsole();\n`; // if showing debug, put REPL on terminal (until connection)
|
||||
else boot += `Bluetooth.setConsole(true);\n`; // else if no debug, force REPL to Bluetooth
|
||||
}
|
||||
// we just reset, so BLE should be on.
|
||||
// Don't disconnect if something is already connected to us
|
||||
if (s.ble===false) boot += `if (!NRF.getSecurityStatus().connected) NRF.sleep();\n`;
|
||||
// Set time
|
||||
if (s.timeout!==undefined) boot += `Bangle.setLCDTimeout(${s.timeout});\n`;
|
||||
if (!s.timeout) boot += `Bangle.setLCDPower(1);\n`;
|
||||
boot += `E.setTimeZone(${s.timezone});`;
|
||||
// Set vibrate, beep, etc IF on older firmwares
|
||||
if (!Bangle.F_BEEPSET) {
|
||||
if (!s.vibrate) boot += `Bangle.buzz=Promise.resolve;\n`
|
||||
if (s.beep===false) boot += `Bangle.beep=Promise.resolve;\n`
|
||||
else if (s.beep=="vib") boot += `Bangle.beep = function (time, freq) {
|
||||
return new Promise(function(resolve) {
|
||||
if ((0|freq)<=0) freq=4000;
|
||||
if ((0|time)<=0) time=200;
|
||||
if (time>5000) time=5000;
|
||||
analogWrite(D13,0.1,{freq:freq});
|
||||
setTimeout(function() {
|
||||
digitalWrite(D13,0);
|
||||
resolve();
|
||||
}, time);
|
||||
});
|
||||
};\n`;
|
||||
}
|
||||
// Draw out of memory errors onto the screen
|
||||
boot += `E.on('errorFlag', function(errorFlags) {
|
||||
g.reset(1).setColor("#ff0000").setFont("6x8").setFontAlign(0,1).drawString(errorFlags,g.getWidth()/2,g.getHeight()-1).flip();
|
||||
print("Interpreter error:", errorFlags);
|
||||
E.getErrorFlags(); // clear flags so we get called next time
|
||||
});\n`;
|
||||
// stop users doing bad things!
|
||||
if (global.save) boot += `global.save = function() { throw new Error("You can't use save() on Bangle.js without overwriting the bootloader!"); }\n`;
|
||||
// Apply any settings-specific stuff
|
||||
if (s.options) boot+=`Bangle.setOptions(${E.toJS(s.options)});\n`;
|
||||
if (s.quiet && s.qmOptions) boot+=`Bangle.setOptions(${E.toJS(s.qmOptions)});\n`;
|
||||
if (s.quiet && s.qmBrightness) {
|
||||
if (s.qmBrightness!=1) boot+=`Bangle.setLCDBrightness(${s.qmBrightness});\n`;
|
||||
} else {
|
||||
if (s.brightness && s.brightness!=1) boot+=`Bangle.setLCDBrightness(${s.brightness});\n`;
|
||||
}
|
||||
if (s.quiet && s.qmTimeout) boot+=`Bangle.setLCDTimeout(${s.qmTimeout});\n`;
|
||||
if (s.passkey!==undefined && s.passkey.length==6) boot+=`NRF.setSecurity({passkey:${s.passkey}, mitm:1, display:1});\n`;
|
||||
if (s.whitelist) boot+=`NRF.on('connect', function(addr) { if (!(require('Storage').readJSON('setting.json',1)||{}).whitelist.includes(addr)) NRF.disconnect(); });\n`;
|
||||
// Pre-2v10 firmwares without a theme/setUI
|
||||
if (!g.theme) {
|
||||
boot += `g.theme={fg:-1,bg:0,fg2:-1,bg2:7,fgH:-1,bgH:0x02F7};\n`;
|
||||
}
|
||||
if (!Bangle.setUI) {
|
||||
boot += `Bangle.setUI=function(mode, cb) {
|
||||
if (Bangle.btnWatches) {
|
||||
Bangle.btnWatches.forEach(clearWatch);
|
||||
delete Bangle.btnWatches;
|
||||
}
|
||||
if (Bangle.swipeHandler) {
|
||||
Bangle.removeListener("swipe", Bangle.swipeHandler);
|
||||
delete Bangle.swipeHandler;
|
||||
}
|
||||
if (Bangle.touchandler) {
|
||||
Bangle.removeListener("touch", Bangle.touchHandler);
|
||||
delete Bangle.touchHandler;
|
||||
}
|
||||
function b() {
|
||||
try{Bangle.buzz(20);}catch(e){}
|
||||
}
|
||||
if (!mode) return;
|
||||
else if (mode=="updown") {
|
||||
Bangle.btnWatches = [
|
||||
setWatch(function() { b();cb(-1); }, BTN1, {repeat:1}),
|
||||
setWatch(function() { b();cb(1); }, BTN3, {repeat:1}),
|
||||
setWatch(function() { b();cb(); }, BTN2, {repeat:1})
|
||||
];
|
||||
} else if (mode=="leftright") {
|
||||
Bangle.btnWatches = [
|
||||
setWatch(function() { b();cb(-1); }, BTN1, {repeat:1}),
|
||||
setWatch(function() { b();cb(1); }, BTN3, {repeat:1}),
|
||||
setWatch(function() { b();cb(); }, BTN2, {repeat:1})
|
||||
];
|
||||
Bangle.swipeHandler = d => {b();cb(d);};
|
||||
Bangle.on("swipe", Bangle.swipeHandler);
|
||||
Bangle.touchHandler = d => {b();cb();};
|
||||
Bangle.on("touch", Bangle.touchHandler);
|
||||
} else
|
||||
throw new Error("Unknown UI mode");
|
||||
};\n`;
|
||||
}
|
||||
// Append *.boot.js files
|
||||
require('Storage').list(/\.boot\.js/).forEach(bootFile=>{
|
||||
boot += "//"+bootFile+"\n"+require('Storage').read(bootFile)+"\n";
|
||||
});
|
||||
boot += "}\n";// initial 'if'
|
||||
var s = require('Storage').write('.boot0',boot);
|
||||
delete boot;
|
||||
E.showMessage("Reloading...");
|
||||
eval(require('Storage').read('.boot0'));
|
||||
eval(require('Storage').read('.bootcde'));
|
|
@ -3,3 +3,4 @@
|
|||
0.09: Add BTN1 status line with ID,Fw ver, mem %, battery %
|
||||
0.10: Icon fixed for transparency
|
||||
0.11: added Heart Rate Monitor status and ability to turn on/off
|
||||
0.12: added support for different locales
|
||||
|
|
|
@ -2,7 +2,6 @@ var fontsize = 3;
|
|||
var locale = require("locale");
|
||||
var marginTop = 40;
|
||||
var flag = false;
|
||||
var WeekDays = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
|
||||
|
||||
var hrtOn = false;
|
||||
var hrtStr = "Hrt: ??? bpm";
|
||||
|
@ -26,19 +25,14 @@ function drawAll(){
|
|||
}
|
||||
|
||||
function updateRest(now){
|
||||
let date = locale.date(now,false);
|
||||
writeLine(WeekDays[now.getDay()],1);
|
||||
writeLine(date,2);
|
||||
writeLine(locale.dow(now),1);
|
||||
writeLine(locale.date(now,1),2);
|
||||
drawInfo(5);
|
||||
}
|
||||
function updateTime(){
|
||||
if (!Bangle.isLCDOn()) return;
|
||||
let now = new Date();
|
||||
let h = now.getHours();
|
||||
let m = now.getMinutes();
|
||||
h = h>=10?h:"0"+h;
|
||||
m = m>=10?m:"0"+m;
|
||||
writeLine(h+":"+m,0);
|
||||
writeLine(locale.time(now,1),0);
|
||||
writeLine(flag?" ":"_",3);
|
||||
flag = !flag;
|
||||
if(now.getMinutes() == 0)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
|
@ -0,0 +1,14 @@
|
|||
Dozenal Time
|
||||
============
|
||||
|
||||
A dozenal Holocene calendar and a dozenal diurnal clock. For information about them, go to https://dozenal.ae-web.ca/pdf/dozenal-calendar.pdf and https://dozenal.ae-web.ca/pdf/about-short.pdf. They've been in use for some years.
|
||||
|
||||
In the dozenal number base, ten and eleven are single digits, and 10 is a dozen. The clock simply divides the day by successive powers of a dozen. The day or parts of it may be divided easily into halves, thirds, quarters, sixths, or twelfths (dozenths). There is no conglomeration of bases two, ten, twelve, and sixty, as in the current system of time measurement.
|
||||
|
||||
The annual calendar has a dozen months of 5 weeks each, each week having 6 days. The 5 or 6 days beyond 360 (dozenal 260) are added where they keep the season beginnings the most accurate.
|
||||
|
||||
The year itself begins on the December solstice. Because that always happens, there is no need of a leap-year rule to keep the seasons from drifting.
|
||||
|
||||
The epoch (year numbering) begins in the last year when the perihelion coincided with the June solstice, near the beginning of the Holocene era. That astronomical basis makes the calendar free from politics, religion, or geography.
|
||||
|
||||
While the year number remains cardinal, BTN5 toggles between cardinal and ordinal for the rest of the calendar segments. BTN4 adds or removes a quickly changing digit to or from the clock.
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("lEowggdkUiCKIADCJcCkUjmYACmUikAlKB4ImDAoQSJkYhBFAQECAQI5HBQU//4AC+YUCHowzBCQfzAYYKCEw8vEgYqD+QoGgQbBHAYADCwIoBCYkiEwhPEBAIoBHgY6BExHyHwQhBFAQ6BkYTHDgcyHgcCHRZlDCYQsBTYg6GDAJQDPoI6LAAIPBCYRiHHQhkDCYRiHHQhkCCYKKBCYzzBA4yMBCYTVEGYITEBYITZHY5PHUAJjITIJjHRZINBIYoTDWZAoFWYbbJFALbHgUyX4oPDXIcjMQITBmZkHFYszCYZkJMQoTCKAQ8IHQZOCHgYoKkQ6DHgYoEcIgmBHQg8CFAIPCCYfzBQQSEFAbrFCQImHFAQUCkczmYECAQISGHoYzBAAQFCCRA9BEwYoDHI4pFAAgRLCooRPABg="))
|
|
@ -0,0 +1,225 @@
|
|||
// Positioning values for graphics buffers
|
||||
const g_height = 80; // total graphics height
|
||||
const g_x_off = 16; // position from left
|
||||
const g_y_off = (240 - g_height)/2; // vertical center for graphics region
|
||||
const g_width = 240 - 2 * g_x_off; // total graphics width
|
||||
const g_height_d = 32; // height of date region
|
||||
const g_y_off_d = 0; // y position of date region within graphics region
|
||||
const spacing = 0; // space between date and time in graphics region
|
||||
const g_y_off_t = g_y_off_d + g_height_d + spacing; // y position of time within graphics region
|
||||
const g_height_t = 48; // height of time region
|
||||
|
||||
// Other vars
|
||||
const A1 = [30,30,30,30,31,31,31,31,31,31,30,30];
|
||||
const B1 = [30,30,30,30,30,31,31,31,31,31,30,30];
|
||||
const B2 = [30,30,30,30,31,31,31,31,31,30,30,30];
|
||||
const timeColour = "#f2f2f2";
|
||||
const dateColours = ["#ff0000","#ffa500","#ffff00","#00b800","#0000ff","#ff00ff","#ff0080"];
|
||||
const calen10 = {"size":32,"pt0":[32-g_x_off,16],"step":[20,0],"dx":-4.5,"dy":-4.5}; // positioning for usual calendar line
|
||||
const calen7 = {"size":32,"pt0":[62-g_x_off,16],"step":[20,0],"dx":-4.5,"dy":-4.5}; // positioning for S-day calendar line
|
||||
const time5 = {"size":48,"pt0":[64-g_x_off,24],"step":[30,0],"dx":-6.5,"dy":-6.5}; // positioning for lull time line; was 64
|
||||
const time6 = {"size":48,"pt0":[48-g_x_off,24],"step":[30,0],"dx":-6.5,"dy":-6.5}; // positioning for twinkling time line
|
||||
const baseYear = 11584;
|
||||
const baseDate = Date(2020,11,21); // month values run from 0 to 11
|
||||
let accum = new Date(baseDate.getTime());
|
||||
let sequence = [];
|
||||
let timeActiveUntil;
|
||||
let addTimeDigit = false;
|
||||
let dateFormat = false;
|
||||
let lastX = 999999999;
|
||||
let res = {};
|
||||
//var last_time_log = 0;
|
||||
|
||||
// Date and time graphics buffers
|
||||
var dateColour = "#ffffff"; // override later
|
||||
var g_d = Graphics.createArrayBuffer(g_width,g_height_d,1,{'msb':true});
|
||||
var g_t = Graphics.createArrayBuffer(g_width,g_height_t,1,{'msb':true});
|
||||
// Set screen mode and function to write graphics buffers
|
||||
Bangle.setLCDMode();
|
||||
g.clear(); // start with blank screen
|
||||
g.flip = function()
|
||||
{
|
||||
g.setColor(dateColour);
|
||||
g.drawImage(
|
||||
{
|
||||
width:g_width,
|
||||
height:g_height_d,
|
||||
buffer:g_d.buffer
|
||||
}, g_x_off, g_y_off + g_y_off_d);
|
||||
g.setColor(timeColour);
|
||||
g.drawImage(
|
||||
{
|
||||
width:g_width,
|
||||
height:g_height_t,
|
||||
buffer:g_t.buffer
|
||||
}, g_x_off, g_y_off + g_y_off_t);
|
||||
};
|
||||
|
||||
setWatch(function(){ modeTime(); }, BTN1, {repeat:true} );
|
||||
setWatch(function(){ Bangle.showLauncher(); }, BTN2, { repeat: false, edge: "falling" });
|
||||
setWatch(function(){ modeWeather(); }, BTN3, {repeat:true});
|
||||
setWatch(function(){ toggleTimeDigits(); }, BTN4, {repeat:true});
|
||||
setWatch(function(){ toggleDateFormat(); }, BTN5, {repeat:true});
|
||||
|
||||
function buildSequence(targ){
|
||||
for(let i=0;i<targ.length;++i){
|
||||
sequence.push(new Date(accum.getTime()));
|
||||
accum.setDate(accum.getDate()+targ[i]);
|
||||
}
|
||||
}
|
||||
buildSequence(B2);
|
||||
buildSequence(B2);
|
||||
buildSequence(A1);
|
||||
buildSequence(B1);
|
||||
buildSequence(B2);
|
||||
buildSequence(B2);
|
||||
buildSequence(A1);
|
||||
buildSequence(B1);
|
||||
buildSequence(B2);
|
||||
buildSequence(B2);
|
||||
buildSequence(A1);
|
||||
buildSequence(B1);
|
||||
buildSequence(B2);
|
||||
|
||||
function getDate(dt){
|
||||
let index = sequence.findIndex(n => n > dt)-1;
|
||||
let year = baseYear+parseInt(index/12);
|
||||
let month = index % 12;
|
||||
let day = parseInt((dt-sequence[index])/86400000);
|
||||
let colour = dateColours[day % 6];
|
||||
if(day==30){ colour=dateColours[6]; }
|
||||
return({"year":year,"month":month,"day":day,"colour":colour});
|
||||
}
|
||||
function toggleTimeDigits(){
|
||||
addTimeDigit = !addTimeDigit;
|
||||
modeTime();
|
||||
}
|
||||
function toggleDateFormat(){
|
||||
dateFormat = !dateFormat;
|
||||
modeTime();
|
||||
}
|
||||
function formatDate(res,dateFormat){
|
||||
let yyyy = res.year.toString(12);
|
||||
calenDef = calen10;
|
||||
if(!dateFormat){ //ordinal format
|
||||
let mm = ("0"+(res.month+1).toString(12)).substr(-2);
|
||||
let dd = ("0"+(res.day+1).toString(12)).substr(-2);
|
||||
if(res.day==30){
|
||||
calenDef = calen7;
|
||||
let m = ((res.month+1).toString(12)).substr(-2);
|
||||
return(yyyy+"-"+"S"+m); // ordinal format
|
||||
}
|
||||
return(yyyy+"-"+mm+"-"+dd);
|
||||
}
|
||||
let m = res.month.toString(12); // cardinal format
|
||||
let w = parseInt(res.day/6);
|
||||
let d = res.day%6;
|
||||
//return(yyyy+"-"+res.month+"-"+w+"-"+d);
|
||||
return(yyyy+"-"+m+"-"+w+"-"+d);
|
||||
}
|
||||
|
||||
function writeDozTime(text,def,colour){
|
||||
let pts = def.pts;
|
||||
let x=def.pt0[0];
|
||||
let y=def.pt0[1];
|
||||
g_t.clear();
|
||||
g_t.setFont("Vector",def.size);
|
||||
for(let i in text){
|
||||
if(text[i]=="a"){ g_t.setFontAlign(0,0,2); g_t.drawString("2",x+def.dx,y+def.dy); }
|
||||
else if(text[i]=="b"){ g_t.setFontAlign(0,0,2); g_t.drawString("3",x+def.dx,y+def.dy); }
|
||||
else{ g_t.setFontAlign(0,0,0); g_t.drawString(text[i],x,y); }
|
||||
x = x+def.step[0];
|
||||
y = y+def.step[1];
|
||||
}
|
||||
}
|
||||
function writeDozDate(text,def,colour){
|
||||
dateColour = colour;
|
||||
let pts = def.pts;
|
||||
let x=def.pt0[0];
|
||||
let y=def.pt0[1];
|
||||
g_d.clear();
|
||||
g_d.setFont("Vector",def.size);
|
||||
for(let i in text){
|
||||
if(text[i]=="a"){ g_d.setFontAlign(0,0,2); g_d.drawString("2",x+def.dx,y+def.dy); }
|
||||
else if(text[i]=="b"){ g_d.setFontAlign(0,0,2); g_d.drawString("3",x+def.dx,y+def.dy); }
|
||||
else{ g_d.setFontAlign(0,0,0); g_d.drawString(text[i],x,y); }
|
||||
x = x+def.step[0];
|
||||
y = y+def.step[1];
|
||||
}
|
||||
}
|
||||
|
||||
// Functions for time mode
|
||||
function drawTime()
|
||||
{
|
||||
let dt = new Date();
|
||||
let date = "";
|
||||
let timeDef;
|
||||
let x = 0;
|
||||
dt.setDate(dt.getDate());
|
||||
if(addTimeDigit){
|
||||
x =
|
||||
10368*dt.getHours()+172.8*dt.getMinutes()+2.88*dt.getSeconds()+0.00288*dt.getMilliseconds();
|
||||
let msg = "00000"+Math.floor(x).toString(12);
|
||||
let time = msg.substr(-5,3)+"."+msg.substr(-2);
|
||||
let wait = 347*(1-(x%1));
|
||||
timeDef = time6;
|
||||
} else {
|
||||
x =
|
||||
864*dt.getHours()+14.4*dt.getMinutes()+0.24*dt.getSeconds()+0.00024*dt.getMilliseconds();
|
||||
let msg = "0000"+Math.floor(x).toString(12);
|
||||
let time = msg.substr(-4,3)+"."+msg.substr(-1);
|
||||
let wait = 4167*(1-(x%1));
|
||||
timeDef = time5;
|
||||
}
|
||||
if(lastX > x){ res = getDate(dt); } // calculate date once at start-up and once when turning over to a new day
|
||||
date = formatDate(res,dateFormat);
|
||||
if(dt<timeActiveUntil)
|
||||
{
|
||||
// Write to background buffers, then display on screen
|
||||
writeDozDate(date,calenDef,res.colour);
|
||||
writeDozTime(time,timeDef,timeColour);
|
||||
g.flip();
|
||||
// Ready next interval
|
||||
setTimeout(drawTime,wait);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clear screen
|
||||
g_d.clear();
|
||||
g_t.clear();
|
||||
g.flip();
|
||||
}
|
||||
lastX = x;
|
||||
}
|
||||
function modeTime()
|
||||
{
|
||||
timeActiveUntil = new Date();
|
||||
timeActiveUntil.setDate(timeActiveUntil.getDate());
|
||||
timeActiveUntil.setSeconds(timeActiveUntil.getSeconds()+15);
|
||||
//Bangle.setLCDPower(true);
|
||||
clearTimeout();
|
||||
drawTime();
|
||||
}
|
||||
|
||||
Bangle.loadWidgets();
|
||||
|
||||
// Time-logging function
|
||||
/*function logTime(label)
|
||||
{
|
||||
var d = new Date();
|
||||
var t = d.getTime();
|
||||
var diff_test = t - last_time_log;
|
||||
last_time_log = t;
|
||||
console.log(label + " at time: " + t + ", since last: " + diff_test);
|
||||
}*/
|
||||
|
||||
// Functions for weather mode - TODO
|
||||
function drawWeather() {}
|
||||
function modeWeather() {}
|
||||
|
||||
// Start time on twist
|
||||
Bangle.on('twist', function() {
|
||||
modeTime();
|
||||
});
|
||||
|
||||
Bangle.drawWidgets();
|
After Width: | Height: | Size: 2.4 KiB |
|
@ -0,0 +1 @@
|
|||
0.01: Initial Release
|
|
@ -0,0 +1,28 @@
|
|||
# Font Clock
|
||||
|
||||
The Font Clock allows you to choose the font and clock style.
|
||||
|
||||

|
||||
|
||||
## Usage
|
||||
|
||||
### Choose the Clock Face from the selection
|
||||
|
||||
Before uploading the upload page will ask which clock face you like to choose. Please choose using the provided pull down. As you look through the different selections a sample image will be shown to the right hand side.
|
||||
|
||||
Once you have chosen your watch face press the upload button and the selection will be uploaded to the watch
|
||||
|
||||
### Button 3
|
||||
Button 3 (bottom right button) is used to change the background colour.
|
||||
|
||||
## Further Details
|
||||
|
||||
For further details of design and working please visit [The Project Page](https://www.notion.so/adrianwkirk/Sweep-hand-clock-6aa5b6b3d1074d4e87fc947975b1e4b7)
|
||||
|
||||
## Requests
|
||||
|
||||
Reach out to adrian@adriankirk.com if you have feature requests or notice bugs.
|
||||
|
||||
## Creator
|
||||
|
||||
Made by [Adrian Kirk](mailto:adrian@adriankirk.com)
|
After Width: | Height: | Size: 24 KiB |
|
@ -0,0 +1,210 @@
|
|||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>Please select watch display</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<select id="display_selection" name="display_selection" onchange="change_image()" >
|
||||
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<img id="selected_image" src="display-01.png">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>Click <button id="upload" class="btn btn-primary">Upload</button></p>
|
||||
|
||||
<script src="../../core/lib/customize.js"></script>
|
||||
|
||||
<script>
|
||||
function change_image() {
|
||||
var idx = document.getElementById("display_selection").selectedIndex;
|
||||
set_image(idx);
|
||||
}
|
||||
|
||||
function set_image(idx){
|
||||
var image = document.getElementById('selected_image');
|
||||
image.src = "display-0" + (idx + 1) + ".png";
|
||||
}
|
||||
|
||||
var displays_choices=[
|
||||
{
|
||||
name: "Abril FatFace 4",
|
||||
numerals: [12,3,6,9],
|
||||
fonts: ["abril_ff50"],
|
||||
radius: 80,
|
||||
color_schemes : [
|
||||
{
|
||||
name: "black",
|
||||
background : [0.0,0.0,0.0],
|
||||
second_hand: [1.0,1.0,0.0]
|
||||
},
|
||||
{
|
||||
name: "red",
|
||||
background : [1.0,0.0,0.0],
|
||||
second_hand: [1.0,1.0,0.0]
|
||||
},
|
||||
{
|
||||
name: "grey",
|
||||
background : [0.5,0.5,0.5],
|
||||
},
|
||||
{
|
||||
name: "purple",
|
||||
background : [1.0,0.0,1.0]
|
||||
},
|
||||
{
|
||||
name: "blue",
|
||||
background : [0.4,0.7,1.0]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Montoon 4",
|
||||
numerals: [12,3,6,9],
|
||||
fonts: ["mntn50"],
|
||||
radius: 80,
|
||||
color_schemes : [
|
||||
{
|
||||
name: "black",
|
||||
background : [0.0,0.0,0.0],
|
||||
second_hand: [1.0,1.0,0.0]
|
||||
},
|
||||
{
|
||||
name: "grey",
|
||||
background : [0.5,0.5,0.5]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Vector 12",
|
||||
numerals: [12,1,2,3,4,5,6,7,8,9,10,11],
|
||||
fonts: ["vector25"],
|
||||
radius: 90,
|
||||
color_schemes : [
|
||||
{
|
||||
name: "black",
|
||||
background : [0.0,0.0,0.0],
|
||||
second_hand: [1.0,0.0,0.0]
|
||||
},
|
||||
{
|
||||
name: "grey",
|
||||
background : [0.5,0.5,0.5],
|
||||
second_hand: [0.0,0.0,0.0]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Copaset 4",
|
||||
numerals: [12,3,6,9],
|
||||
fonts: ["cpstc58"],
|
||||
radius: 75,
|
||||
color_schemes : [
|
||||
{
|
||||
name: "black",
|
||||
background : [0.0,0.0,0.0],
|
||||
second_hand: [1.0,0.0,0.0]
|
||||
},
|
||||
{
|
||||
name: "red",
|
||||
background : [1.0,0.0,0.0],
|
||||
second_hand: [1.0,1.0,0.0]
|
||||
},
|
||||
{
|
||||
name: "grey",
|
||||
background : [0.5,0.5,0.5],
|
||||
second_hand: [0.0,0.0,0.0]
|
||||
},
|
||||
{
|
||||
name: "purple",
|
||||
background : [1.0,0.0,1.0]
|
||||
},
|
||||
{
|
||||
name: "blue",
|
||||
background : [0.4,0.7,1.0]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Vector 4",
|
||||
numerals: [12,3,6,9],
|
||||
fonts: ["vector50"],
|
||||
radius: 75,
|
||||
color_schemes : [
|
||||
{
|
||||
name: "black",
|
||||
background : [0.0,0.0,0.0],
|
||||
second_hand: [1.0,0.0,0.0],
|
||||
},
|
||||
{
|
||||
name: "red",
|
||||
background : [1.0,0.0,0.0],
|
||||
second_hand: [1.0,1.0,0.0]
|
||||
},
|
||||
{
|
||||
name: "grey",
|
||||
background : [0.5,0.5,0.5],
|
||||
second_hand: [0.0,0.0,0.0]
|
||||
},
|
||||
{
|
||||
name: "purple",
|
||||
background : [1.0,0.0,1.0]
|
||||
},
|
||||
{
|
||||
name: "blue",
|
||||
background : [0.4,0.7,1.0]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
var selected_choice = "Abril FatFace 4"
|
||||
try{
|
||||
var stored = localStorage.getItem('fontclock.font.json')
|
||||
if(stored) {
|
||||
var selected_config = JSON.parse(stored);
|
||||
selected_choice = selected_config.name;
|
||||
}
|
||||
} catch(e){
|
||||
console.log("failed to load languages:" + e);
|
||||
}
|
||||
console.log("selected choice:" + selected_choice);
|
||||
var selection=document.getElementById("display_selection");
|
||||
for (var i=0; i<displays_choices.length; i++) {
|
||||
|
||||
var option = document.createElement('option');
|
||||
var curr_choice = displays_choices[i];
|
||||
option.name = curr_choice.name;
|
||||
option.text = curr_choice.name;
|
||||
selection.add(option);
|
||||
}
|
||||
selection.value = selected_choice;
|
||||
set_image(selection.selectedIndex)
|
||||
|
||||
// When the 'upload' button is clicked...
|
||||
document.getElementById("upload").addEventListener("click", function() {
|
||||
var new_config;
|
||||
console.log("selection:" + selection.value);
|
||||
for(var i=0; i<displays_choices.length; i++) {
|
||||
if (displays_choices[i].name == selection.value) {
|
||||
new_config = displays_choices[i];
|
||||
console.log("new_config:" + JSON.stringify(new_config));
|
||||
}
|
||||
}
|
||||
localStorage.setItem('fontclock.font.json',JSON.stringify(new_config));
|
||||
// send finished app (in addition to contents of app.json)
|
||||
sendCustomizedApp({
|
||||
storage:[
|
||||
{name:"fontclock.font.json", content:JSON.stringify(new_config)},
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 8.7 KiB |
After Width: | Height: | Size: 6.5 KiB |
After Width: | Height: | Size: 6.9 KiB |
After Width: | Height: | Size: 6.1 KiB |
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("lEowkA/4AvmUiAA0/CRHzkczAA0vExM/n/zn8zAIPzCZUi/8j+cvmUzAgI7JBQITHkY6JCwRNEIYITIDoQSEExXyDoQSDn4mKHQ4mKLoImRHQQmPMIYTDExY6HExY6HExQ6HYgISJHQ4TBAgbXOAAb3Ba5giBn8/H4zXHMYfzEww6I+cyPJAtEToizBNoQTFLo0yBAKMI+UikUjIwQSBJg61ICALGMPQgQBJhB6IbJjcGJhw6DCQJMMUIhMOHQavBCRo6CJh46DTJo6EJh5eCTJwADdwISQJiIAo"))
|
|
@ -0,0 +1,51 @@
|
|||
var NumeralFont = require("fontclock.font.js");
|
||||
|
||||
const DIM_30x38 = [30,38];
|
||||
const DIM_49x38 = [49,38];
|
||||
|
||||
class DigitNumeralFont extends NumeralFont{
|
||||
constructor(){
|
||||
super();
|
||||
// dimension map provides the dimensions of the character for
|
||||
// each number for plotting and collision detection
|
||||
this.widths = atob("DRIhFRwdHhsfGh8fDQ==");
|
||||
this.font = atob("AAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAD/AAAAAAB/4AAAAAAf+AAAAAAH/gAAAAAB/4AAAAAAf+AAAAAAD/AAAAAAA/gAAAAAABgAAAAAAAAAAAAAAAAAAAAAAAAHAAAAAAAPwAAAAAAf8AAAAAA/+AAAAAB/8AAAAAD/wAAAAAH/gAAAAAP/AAAAAAf+AAAAAA/8AAAAAB/4AAAAAD/wAAAAAH/gAAAAAP/AAAAAAH+AAAAAAB8AAAAAAAIAAAAAAAAAAAAAAAAAAH/8AAAAAP//8AAAAP///wAAAH///+AAAD////4AAB/////AAA/////wAAf////+AAH/////wAD/////8AA//////AAP/gAA/wADwAAAAeAA4AAAADgAMAAAAA4ADAAAAAOAAwAAAADgAMAAAAA4ADgAAAAeAA/gAAA/AAP/////wAD/////8AAf/////AAH/////gAA/////4AAH////8AAB////+AAAP////AAAA////gAAAD///gAAAAH//AAAAAAAAAAAAGAAAAAwABgAAAAMAAYAAAADAAGAAAAAwADgAAAAMAA//////AAP/////wAD/////8AA//////AAP/////wAD/////8AA//////AAP/////wAD/////8AA//////AAP/////wAAAAAAAMAAAAAAADAAAAAAAAwAAAAAAAMAAAAAAAAAAAAAAAAAAAHwAAD8AAH/AAB/AAD/wAA/wAA/+AAf8AAf/gAP/AAH/4AH/wAD/+AD/8AA//gB//AAOPwA//wADD4Aff8AAwAAPn/AAMAAPx/wADAAH8f8AA4AH+H/AAPgP/B/wAD///gf8AA///4H/AAP//8B/wAD//+Af8AAf//AH/AAH//wB/wAA//4A/8AAH/4Af/AAA/8A//wAAD8Af/8AAAAAD+AAAAAAAAAAAAAAAAAAAAAAAAPwAAA/gAP+AAAf8AD/wAAP/gB/+AAH/4Af/wAB/+AH/8AAf/gB//AAP/4wP/wAD/8OD+OAAw/DgPDgAMDAwAA4ADAAcAAOAAwAHAADgAOAH4AA4AD///AAeAA///+A/AAP/////wAD/////8AA//9///AAP//f//gAB//n//4AAf/w//+AAD/8P//AAAf+B//gAAB+AP/wAAAAAB/4AAAAAADwAAAAAAAAAAAAAAAeAAAAAAAfgAAAAAAf4AAAAAAPmAAAAAAPhgAAAAAPwYAAAAAPwGAAAAAHwBgAAAAHwAYDAAAH4AGAwAAH4ABgMAAH4AAYDAAD4AAGAwAD/////8AA//////AAP/////wAD/////8AA//////AAP/////wAD/////8AA//////AAP/////wAD/////8AAAAAAYDAAAAAAGAwAAAAABgMAAAAAAYBAAAAAB/4AAAAAAf+AAAAAAAAAAAAAAAD4AAAAAAB/gAAAAAA/8AAP//wf/gAD//8H/4AA//3B//AAP8Bgf/wAD/A4D/8AAfwOA/jgAH8DAH44AB/gwAAOAAf4MAADgAH+DAAA4AB/g4AAeAAf8PwA/AAH/D///wAB/w///8AAf8P///AAD/j///wAA/4f//4AAP+H//+AAH/A///AAD/wP//gAA/gB//wAAAAAH/4AAAAAAfwAAAAAAAAAAAAAAAAAAAAAD//wAAAAH///AAAAH///8AAAD////gAAD////8AAB/////gAAf////4AAP/////AAH/////wAB/////8AA//////gAP8B4AD4AD4A4AAOAA4AMAADgAOAHAAA4ADABwAAOAAwAcAAHgAMAH4AP4ADD5///8AA5/f///AAP/////wAD/////8AAf/v//+AAH/7///AAA/+f//wAAH/H//4AAA/gf/4AAABgD/8AAAAAAH4AAAAAAAAAAAAAAAAAAAAf/wAAAAAP/8AAAAAD/8AAAAAA/8AAAAAAP+AAA/AAD/gAA/4AA/4AA//AAP+AAf/wAD/gAf/+AA/4AP//gAP+AH//4AD/gD//+AA/4B///gAP+B/+BwAD/g/8AAAA/4f8AAAAP+P8AAAAD/n8AAAAA/78AAAAAP/+AAAAAD/+AAAAAA/+AAAAAAP+AAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAP+AAAB/gH/4AAA/8D//AAAf/h//wAAP/8f/+AAH//v//gAB//7//8AAf/////AAP/////wAD/////+AA//////gAP//+AB4ADgAeAAOAAwADgADgAMAA4AA4ADAAOAAOAA4AHwADgAP//+AD4AD/////+AA//////AAP/////wAD//7//8AAf/+///AAH//P//gAA//x//4AAH/4f/8AAA/8D/+AAAD8Af/AAAAAAB/AAAAAAAAAAAAA/gAAAAAA//APwAAA//8H+AAAf//j/wAAP//4/+AAD///f/gAB/////8AAf//+//AAP///v/wAD///7+eAA////PjgAPgAPwA4ADgAA4AOAAwAAOADgAMAADgA4ADAAA4AeAAwAAcAfgAPAAeA/wAD/////8AA//////AAP/////gAB/////wAAf////8AAD////+AAAf////AAAH////gAAAf///gAAAD///gAAAAH//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8AH4AAAB/gD/AAAAf8B/4AAAP/Af+AAAD/wH/gAAA/8B/4AAAH/Af+AAAB/wD/AAAAP4A/gAAAAwABgAAAAAAAA==");
|
||||
var scale = 1; // size multiplier for this font
|
||||
this.size = 50+(scale<<8)+(1<<16);
|
||||
this.y_offset = -12;
|
||||
|
||||
|
||||
}
|
||||
getDimensions(hour){
|
||||
//return this.dimension_map[hour];
|
||||
switch (hour){
|
||||
case 10:
|
||||
case 11:
|
||||
case 12:
|
||||
return DIM_49x38;
|
||||
default:
|
||||
return DIM_30x38;
|
||||
|
||||
}
|
||||
}
|
||||
hour_txt(hour){ return hour.toString(); }
|
||||
draw(hour_txt,x,y){
|
||||
/* going to leave this in here for future testing.
|
||||
uncomment this so that it draws a box behind the string
|
||||
so we can guess the digit dimensions*/
|
||||
/*var dim = [30,38];
|
||||
g.setColor(0.5,0,0);
|
||||
g.fillPoly([x,y,
|
||||
x+dim[0],y,
|
||||
x+dim[0],y+dim[1],
|
||||
x,y+dim[1]
|
||||
]);
|
||||
g.setColor(1.0,1.0,1.0);*/
|
||||
g.setFontAlign(-1.0,-1.0,0);
|
||||
g.setFontCustom(this.font, 46, this.widths, this.size);
|
||||
g.drawString(hour_txt,x,y+this.y_offset );
|
||||
}
|
||||
getName(){return "Digit";}
|
||||
}
|
||||
|
||||
module.exports = [DigitNumeralFont];
|
|
@ -0,0 +1,59 @@
|
|||
var NumeralFont = require("fontclock.font.js");
|
||||
|
||||
const DIM_20x58 = [20,58];
|
||||
const DIM_30x58 = [30,58];
|
||||
const DIM_40x58 = [40,58];
|
||||
const DIM_50x58 = [50,58];
|
||||
class DigitNumeralFont extends NumeralFont{
|
||||
constructor(){
|
||||
super();
|
||||
// dimension map provides the dimesions of the character for
|
||||
// each number for plotting and collision detection
|
||||
this.font = atob("AAAA/+AAAAAAB///wAAAAB////8AAAA/////+AAAP/////8AAD//////8AAf/8AAf/8AD/8AAAH/4Af+AAAAD/wD/gAAAAD/gf4AAAAAH+D/AAAAAAP8P4AAAAAAf5/AAAAAAA/n4AAAAAAB+/gAAAAAAH/+AAAAAAAf/wAAAAAAA//AAAAAAAD/8AAAAAAAP/4AAAAAAB//gAAAAAAH9+AAAAAAAfn8AAAAAAD+fwAAAAAAP4/gAAAAAB/D/AAAAAAP8H/AAAAAB/gP+AAAAAf8Af/AAAAH/gA//gAAD/8AB//8AH//gAB//////8AAB//////AAAB/////wAAAA////4AAAAAP//4AAAAAAAAAAAAAAGAAAAAAAAA8AAAAAAAAH8AAAAAAAA/wAAAAAAAH+AAAAAAAA/wAAAAAAAH+AAAAAAAA/wAAAAAAAH////////w/////////H////////8/////////3//////////////////8AAAAAAAAAAAAAAAAAADAAAAAAAAAcQAAAAAAAHzwAAAAAAA/PwAAAAAAH9/AAAAAAB/34AAAAAAP/fgAAAAAD//+AAAAAAf//wAAAAAH///AAAAAA///8AAAAAH///4AAAAB/4//gAAAAP/D/+AAAAD/wP34AAAAf+A/fwAAAD/wD8/gAAA/8APz/AAAH/gA/H+AAB/4AD8f8AAP/AAPw/8AD/wAA/B/+A/+AAD8D////wAAPwH///8AAA/AH///gAAD8AH//4AAAPwAH/+AAAAAAAAAAAAAAD8AAAAAAAAPwAAAAAAAA/AAgAAAAD/8AHAAAAAP/wB8AAAAA//APwAAAAD/8D/AAAAAP/wf8AAAAB//H/wAAAAH/8//gAAAAfv//+AAAAD+///8AAAAP7//fwAAAB/P/w/gAAAP8/+D/AAAB/j/wH+AAAP8P8AP8AAB/w/gAf8AAf+D4AA/8AH/wPAAB////+AwAAD////gCAAAH///8AAAAAH///AAAAAAD//wAAAAAAA/wAAAAAAAAAAAAAAAAAAAQAAAAAAAAHAAAAAAAAD8AAAAAAAA/wAAAAAAAP/AAAAAAAD/8AAAAAAB//wAAAAAAf//AAAAAAH//8AAAAAB//PwAAAAAf/w/AAAAAP/8D8AAAAD//APwAAAA//gA/AAAAP/4AD8AAAH/+AAPwAAB//gAA/AAAf/4AAD8AAH/8AAAPwAD//AAAA/AAP/wAAAD8AA/8AAAAPwAD/AAAAA/gAPgAAA/////4AAAD////+AAAAP////wAAAA/////AAAAD////8AAAAAA/AAAAAAAAD8AAAAAAAAPwAAAAAAAAAAAAAA8AAAAAAAB/wAAAAAAD//AAAAAP///8AAAAA////wAAAAD////AAAAAP///8AAAAA//4PwAAAAH/8A/gAAAAf/wB+AAAAB+/AH4AAAAP78AfwAAAA/vwA/AAAAH8/AD+AAAA/z8AP8AAAD+PwAf4AAAf4/AA/wAAH/D8AD/gAA/4PwAH/gAf/A/AAP/8f/4AAAAf////AAAAAf///wAAAAA///8AAAAAAf//AAAAAAAH/gAAAAAAAAAAAAAAAAH/4AAAAAAP//8AAAAAD///+AAAAB////8AAAAf////8AAAH//8f/4AAA//8AD/wAAP//AAD/gAB//wAAH/AAP/+AAAH+AB//wAAAP4AP/+AAAAfwB//wAAAB/AP9/AAAAD+B/n4AAAAH4H8/gAAAAfg/j8AAAAB/H8PwAAAAH8fw/AAAAAPz+D8AAAAA/P4PwAAAAD9/A/AAAAAf38D8AAAAB/fgP4AAAAH5+A/gAAAAfv4B/AAAAD+/gH8AAAAPz+AP4AAAB/PwA/wAAAP8/AB/gAAB/gAAD/AAAP8AAAP+AAD/gAAAf+AA/8AAAA//gf/gAAAA////8AAAAB////gAAAAB///4AAAAAB//+AAAAAAA//AAAAAAAAAAAAPwAAAAAAAA/AAAAAAAAD8AAAAAAAAfwAAAAAAAP/AAAAAAAH/8AAAAAAD//wAAAAAD///AAAAAB///8AAAAA///vwAAAA///w/AAAAP//wD8AAAP//4APwAAH//8AA/AAD//+AAD8AD//+AAAPwB///AAAA/A///gAAAD8f//wAAAAP///4AAAAA///4AAAAAD//8AAAAAAP/+AAAAAAA/+AAAAAAAD/AAAAAAAAPgAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAH//wAAAAAB///wAAAAAf///wAAAAD////wAAAAf////gAAAH/wAf/AAAA/8AAP+AAAD/AAAf8AAAf4AAAf4AAD/AAAA/gB/P4AAAB/A///AAAAH8H//8AAAAP4///gAAAAfn//+AAAAB+f//wAAAAH/+B/AAAAAf/4H8AAAAA//APwAAAAD/8A/AAAAAP/4H8AAAAB/fw/wAAAAH9///gAAAAfj//+AAAAB+P//4AAAAP4P//wAAAA/gf//gAAAH8APD/AAAA/wAAH8AAAH+AAAf8AAA/wAAA/4AAH/AAAB/4AB/4AAAD/8A//AAAAH////wAAAAP///+AAAAAP///gAAAAAP//4AAAAAAH/+AAAAAAAAAAAAAAA/wAAAAAAA//8AAAAAAP//+AAAAAD///8AAAAA////8AAAAH////4AAAA/+AD/wAAAH/AAD/gAAA/4AAD/AAAH+AAAH+AAAfwAAAP8APz+AAAAfwA/P4AAAA/gH9/AAAAD+Af34AAAAH4B+fgAAAAfwH7+AAAAA/A/v4AAAAD8D+/AAAAAPwPz8AAAAA/B/PwAAAAD8P8/gAAAAPw/j+AAAAA/H8H4AAAAH8/wfgAAAAf3+B/AAAAB+fwH8AAAAP//AP4AAAB//4A/wAAAH//AB/gAAA//4AD/AAAP//AAH+AAB//wAAf+AAf/+AAAf/AP//gAAA/////8AAAB/////AAAAB////wAAAAB///4AAAAAB//4AAAAAAAAAAAAAAA=");
|
||||
this.widths = atob("Jg8dGiAaKBsoKA==");
|
||||
}
|
||||
getDimensions(hour){
|
||||
switch(hour){
|
||||
case 1:
|
||||
return DIM_20x58;
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 7:
|
||||
return DIM_30x58;
|
||||
case 6:
|
||||
case 8:
|
||||
case 9:
|
||||
case 11:
|
||||
case 12:
|
||||
return DIM_40x58;
|
||||
case 10:
|
||||
return DIM_50x58;
|
||||
default:
|
||||
return DIM_30x58;
|
||||
}
|
||||
}
|
||||
hour_txt(hour){ return hour.toString(); }
|
||||
draw(hour_txt,x,y){
|
||||
/* going to leave this in here for future testing.
|
||||
uncomment this so that it draws a box behind the string
|
||||
so we can guess the digit dimensions
|
||||
dim = [50,58];
|
||||
g.setColor(0.5,0,0);
|
||||
g.fillPoly([x,y,
|
||||
x+dim[0],y,
|
||||
x+dim[0],y+dim[1],
|
||||
x,y+dim[1]
|
||||
]);
|
||||
g.setColor(1.0,1.0,1.0);*/
|
||||
//g.setFontCopasetic40x58Numeric();
|
||||
//g.setFontAlign(-1,-1,0);
|
||||
g.setFontAlign(-1,-1,0);
|
||||
g.setFontCustom(this.font, 48, this.widths, 58);
|
||||
g.drawString(hour_txt,x,y);
|
||||
}
|
||||
getName(){return "Digit";}
|
||||
}
|
||||
|
||||
module.exports = [DigitNumeralFont];
|
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* We want to be able to change the font so we set up
|
||||
* pure virtual for all fonts implementtions to use
|
||||
*/
|
||||
class NumeralFont {
|
||||
/**
|
||||
* The screen dimensions of what we are going to
|
||||
* display for the given hour.
|
||||
*/
|
||||
getDimensions(hour){return [0,0];}
|
||||
/**
|
||||
* The characters that are going to be returned for
|
||||
* the hour.
|
||||
*/
|
||||
hour_txt(hour){ return ""; }
|
||||
/**
|
||||
* method to draw text at the required coordinates
|
||||
*/
|
||||
draw(hour_txt,x,y){ return "";}
|
||||
/**
|
||||
* Called from the settings loader to identify the font
|
||||
*/
|
||||
getName(){return "";}
|
||||
}
|
||||
|
||||
module.exports = NumeralFont;
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"name": "Vector 4",
|
||||
"numerals": [12,3,6,9],
|
||||
"fonts": ["vector50"],
|
||||
"radius": 75,
|
||||
"color_schemes" : [
|
||||
{
|
||||
"name": "black",
|
||||
"background" : [0.0,0.0,0.0],
|
||||
"second_hand": [1.0,0.0,0.0],
|
||||
},
|
||||
{
|
||||
"name": "red",
|
||||
"background" : [1.0,0.0,0.0],
|
||||
"second_hand": [1.0,1.0,0.0]
|
||||
},
|
||||
{
|
||||
"name": "grey",
|
||||
"background" : [0.5,0.5,0.5],
|
||||
"second_hand": [0.0,0.0,0.0]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
var NumeralFont = require("fontclock.font.js");
|
||||
|
||||
const DIM_25x25 = [25,25];
|
||||
const DIM_10x25 = [10,25];
|
||||
const DIM_20x25 = [20,25];
|
||||
const DIM_31x25 = [31,25];
|
||||
const DIM_15x25 = [15,25];
|
||||
|
||||
class DigitNumeralFont extends NumeralFont{
|
||||
constructor(){
|
||||
super();
|
||||
// dimension map provides the dimensions of the character for
|
||||
// each number for plotting and collision detection
|
||||
this.widths = atob("BgsVCw8PEBEUEBQUBw==");
|
||||
this.font = atob("AAAAAAAAAAAAp9bgAAAAAAAAAAAADr+vAAAAAAAAAAAAAOv68AAAAAAAAAAAAA6/rwAAAAAAAAAAAADr+fAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAXwAAAAAAAAAAAAXz//wAAAAAAAAAXz//7q+AAAAAAAXz//8q+//wAAAAXz//8q+//yr3wAXz//8q+//yr3//ZAL/8q+//yr3//ZMAAAW+//2q3//ZQAAAAAC/2q3//pQAAAAAAAAF3//pQAAAAAAAAAAAnpQAAAAAAAAAAAAAAAAABL3//tgQAAAAAAAAj//su9//0gAAAAAC7/vv///73/gAAAAB/8/9u7u7/+34AAAA78/r/////c/+9QAAf9/P/LvLu/+///AADu/++//+7/7Pv79QAv37+/sQAAb/7978AF/f3vwAAAAD+/v+4Aj9779QAAAADs+/vwCP3vv1AAAAAOz7+/AF/P3fsAAAAC+/v94AL9+/v5AAAD/9/f/QAP3+/8/9ze/7+/v2AAj9+//Lztu+/P//AAAP/f3P////6//fcAAAL/z/y7u7vv7PoAAAAD/+z////9z/oAAAAAAK//3LvO/+QAAAAAAAAH3///6zAAAAAAAAAAAAAAAAAAAAAAC96fQAAAAAAAAAAAAL769AAAAAAAAAAAAAvvr5ZmZmZmZmZmAAC++v//////////8AAL76/bu7u7u7u7uwAAvvr/7u7u7u7u7uAAC++v/u7u7u7u7u4AAL76/KqqqqqqqqqgAAvvr///////////AAAjQlVVVVVVVVVVUAAAAAAAAAAAAAAAAAAAhTAAAAAAAAAyUlAAD7+udQAAAAHfv68AAPv777AAAAX/+/rwAC+/y/kAAAn+77+vAAb8/q9gAB79//v68ACf7frzAF/9/t+/rwAH/d+/QK/u/P/7+vAAX8/t/u/f/P7Pv68AAvv7+uzv3vz/+/rwAA/P3///z/v/Pr+vAACPv9u67939EOv68AAA/6///7/3AA6/rwAAAv/Ku+/iAADr+fAAAACu//5gAAAAAAAAAAAAAAAAAAAAAAAAAAhTAAAAAAAAAAIrIAD7+udgAAAAGo379gAPr7/rAAAAAfv935AB+vvvcjMkFQ/Pv+sAT6/d9K/r+vDs+/3QB/zvvzr+v68Nz7+/AJ/d+/Ov6/rx3Pv78Ab779+I/N/PX8+/zwAvv8/P/5/v7/z7/+AA+/z+m/r7/Kv9/PkACvv7///8+//7398gAB/9/bvvzvy8/89wAABv++/93+nf/b+gAAAALv/e/9//3v+wAAAAAASd21AVrcogAAAAAAAAAAAb753JAAAAAAAAAALP/frusAAAAAAAAE3/zO+u6wAAAAAABe/7z/367rAAAAAAf/+9/7vvrusAAAAG/+rv+s/9+u6wAAAAra//rf+5367rAAAABv/q7/q//vrusAAAAK2v/5z/w1+u6wAAAAb/6d/7IAX67rAAAACsr/+AL//vru//4AAG/+YAAaqr+u7aqgAAnUAAAD///67v//AAAAAAAABVWPruxVUAAAAAAAAAACtphgAAAAAAAAAAAAAAAAAAAAA0U3d3d3d1AADMAAAL76//////0ACr9AAAvvr9zMzMyABd/vAAC++v7u7u7qAPz79QAL76//////wPz975AAvvr8rN7Oyw+vv+wAC++vQN37/ODs+/vgAL769A6/v9wN37+/AAvvr0Dq+/3Q/Pv78AC++vQN38/vv8+/3QAL769Ar8397+7/36AAvvr0Bfv8/s/7/PMAC++vQA77+9/a+fwAAL769ABP3f///f8gAAVnSRAAb/mrzP8wAAAAAAAAAC3///wQAAAAAAAAAAAAJiAAAAAAAAAAA2ZmZiAAAAAAAAAK7//////+YAAAAAA//Lu7u7up77AAAABP+//+7u7v/5/QAAAP7fyN///+y/+/cAAH+/n/2qqqvv7vzwAA/f3+r/////v8+/cAD7+/v82rye38/e6wBPv9zrn9388fv7/NAI/O+va+/Pzw3Pv68Aj93689z7/dDc+vrwBPv+v06/z90Pz7++AA+/3vnO/Pz+/PvuwAD7+/o4/O/56/38+QAN/v5QP8/f//7PzxAAP89QAL+f3czfj6AAAK9wAAH/v///z/EAAADQAAAC79q879EAAAAAAAAAAJ3//YAAAAAAAAAAAAAAAAAAAAAL3p9AAAAAAAAAAAAAvvr0AAAAAAAAAAAAC++vQAAAAAAAAAAAAL769AAAAAAAAFrgAAvvr0AAAAAFrv/9AAC++vQAAFvv/9u98AAL769Wvv/8u9//6gAArN7//8u+//67z/AACv/8u+//27z//roAAFu+//273//rvP/wAAv/273//rvP//xxAABb3//rvP//xxAAAAAL/rvP//thAAAAAAAAXP/+thAAAAAAAAAACutgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGt/qYCvv2kAAAAAAb/69/+/+vP/AAAAAT+z//q/8//6/8QAAP8/7u9/P+7z/v7AADv36//+/v///778gAfv7/Lr/7++3/vz6AG+/7+/9+/v//Pz+0Aj8789d77+/Ds+/zwCf3Pryvuv68N36+vAJ/c+vK+6/rwzfr68An9z68r7r+vDN+vrwCPzvz1v+z78ez6+/AE+/7//fzv3+/Pv98AD7+/ne+/v57e/e/QAO7v3//9/u/v+vr2AAT9/7u9+v68uvz/AAAM/O////v///z/EAAACv+6vP/+u6z/IAAAAATP//1H3//7IAAAAAAAABAAAAEAAAAAAAAABJkwAAAAAAAAAAAAr///+gAAAAEQAAAB783dvP0QAADNAAAA37/93/n8AAC79QAAT5/c/9358wBt/vAADu/v+8/+79Afz79QAPn7+//Pv58Pv975AD+f7/zP3fjw+vv+wAf7789T+/6vTs+/vgCf3fvyP8/789z7+/AG+++/SP7Pvw+fr74AL5/e+837+/X3+v3AAPv7+//d3d797/35AA3+7/vMzMzK75+/MAA/r97//////r/fwAAAz5/7uqqqqt/d8gAAAe/N//////6v9gAAAACv/bqqqr3/4gAAAAAAOM/////aUAAAAAAAAAAAAAAAAAAAAAAAAAAQEQABARAAAAAAAABvvuoF+u6wAAAAAAAG++6gX67rAAAAAAAAb77qBfrusAAAAAAABvvuoF+u6wAAAAAAAE16pwPXunAAAAAAAAAAAAAAAAAAAA==");
|
||||
var scale = 1; // size multiplier for this font
|
||||
this.size = 25+(scale<<8)+(4<<16);
|
||||
this.y_offset = 0;
|
||||
|
||||
}
|
||||
getDimensions(hour){
|
||||
//return this.dimension_map[hour];
|
||||
switch(hour){
|
||||
case 0:
|
||||
case 12:
|
||||
return DIM_25x25;
|
||||
case 1:
|
||||
return DIM_10x25;
|
||||
case 6:
|
||||
case 8:
|
||||
case 9:
|
||||
case 11:
|
||||
return DIM_20x25;
|
||||
case 10:
|
||||
return DIM_31x25;
|
||||
default:
|
||||
return DIM_15x25;
|
||||
}
|
||||
}
|
||||
hour_txt(hour){ return hour.toString(); }
|
||||
draw(hour_txt,x,y){
|
||||
/* going to leave this in here for future testing.
|
||||
uncomment this so that it draws a box behind the string
|
||||
so we can guess the digit dimensions*/
|
||||
/*var dim = [30,25];
|
||||
g.setColor(0.5,0,0);
|
||||
g.fillPoly([x,y,
|
||||
x+dim[0],y,
|
||||
x+dim[0],y+dim[1],
|
||||
x,y+dim[1]
|
||||
]);
|
||||
g.setColor(1.0,1.0,1.0);*/
|
||||
g.setFontAlign(-1.0,-1.0,0);
|
||||
g.setFontCustom(this.font, 46, this.widths, this.size);
|
||||
g.drawString(hour_txt,x,y+this.y_offset );
|
||||
}
|
||||
getName(){return "Digit";}
|
||||
}
|
||||
|
||||
module.exports = [DigitNumeralFont];
|
|
@ -0,0 +1,39 @@
|
|||
var NumeralFont = require("fontclock.font.js");
|
||||
|
||||
const DIM_14x22 = [14,22];
|
||||
const DIM_27x22 = [27,22];
|
||||
class DigitNumeralFont extends NumeralFont{
|
||||
constructor(){
|
||||
super();
|
||||
}
|
||||
getDimensions(hour){
|
||||
if (hour < 10){
|
||||
return DIM_14x22;
|
||||
} else {
|
||||
return DIM_27x22;
|
||||
}
|
||||
}
|
||||
hour_txt(hour){ return hour.toString(); }
|
||||
draw(hour_txt,x,y){
|
||||
if(hour_txt == null)
|
||||
return;
|
||||
|
||||
/* going to leave this in here for future testing.
|
||||
uncomment this so that it draws a box behind the string
|
||||
so we can guess the digit dimensions
|
||||
var dim = [14,22];
|
||||
g.setColor(0.5,0,0);
|
||||
g.fillPoly([x,y,
|
||||
x+dim[0],y,
|
||||
x+dim[0],y+dim[1],
|
||||
x,y+dim[1]
|
||||
]);
|
||||
g.setColor(1.0,1.0,1.0);*/
|
||||
g.setFontAlign(-1,-1,0);
|
||||
g.setFont("Vector",25);
|
||||
g.drawString(hour_txt,x,y);
|
||||
}
|
||||
getName(){return "Digit";}
|
||||
}
|
||||
|
||||
module.exports = [DigitNumeralFont];
|
|
@ -0,0 +1,91 @@
|
|||
var NumeralFont = require("fontclock.font.js");
|
||||
|
||||
const DIM_28x44 = [28,44];
|
||||
const DIM_54x44 = [54,44];
|
||||
|
||||
class DigitNumeralFont extends NumeralFont{
|
||||
constructor(){
|
||||
super();
|
||||
}
|
||||
getDimensions(hour){
|
||||
if (hour < 10){
|
||||
return DIM_28x44;
|
||||
} else {
|
||||
return DIM_54x44;
|
||||
}
|
||||
}
|
||||
hour_txt(hour){ return hour.toString(); }
|
||||
draw(hour_txt,x,y){
|
||||
if(hour_txt == null)
|
||||
return;
|
||||
|
||||
/* going to leave this in here for future testing.
|
||||
uncomment this so that it draws a box behind the string
|
||||
so we can guess the digit dimensions
|
||||
var dim = [14,22];
|
||||
g.setColor(0.5,0,0);
|
||||
g.fillPoly([x,y,
|
||||
x+dim[0],y,
|
||||
x+dim[0],y+dim[1],
|
||||
x,y+dim[1]
|
||||
]);
|
||||
g.setColor(1.0,1.0,1.0);*/
|
||||
g.setFontAlign(-1,-1,0);
|
||||
g.setFont("Vector",50);
|
||||
g.drawString(hour_txt,x,y);
|
||||
}
|
||||
getName(){return "Digit";}
|
||||
}
|
||||
|
||||
const DIM_50x40 = [50,40];
|
||||
const DIM_70x40 = [70,40];
|
||||
class RomanNumeralFont extends NumeralFont{
|
||||
constructor(){
|
||||
super();
|
||||
}
|
||||
getText(hour){
|
||||
switch (hour){
|
||||
case 1 : return 'I';
|
||||
case 2 : return 'II';
|
||||
case 3 : return 'III';
|
||||
case 4 : return 'IV';
|
||||
case 5 : return 'V';
|
||||
case 6 : return 'VI';
|
||||
case 7 : return 'VII';
|
||||
case 8 : return 'VIII';
|
||||
case 9 : return 'IX';
|
||||
case 10: return 'X';
|
||||
case 11: return 'XI';
|
||||
case 12: return 'XII';
|
||||
default: return '';
|
||||
}
|
||||
}
|
||||
getDimensions(hour){
|
||||
switch (hour){
|
||||
case 3:
|
||||
case 6:
|
||||
case 9:
|
||||
return DIM_50x40;
|
||||
case 12:
|
||||
return DIM_70x40;
|
||||
default:
|
||||
return DIM_70x40;
|
||||
}
|
||||
}
|
||||
hour_txt(hour){ return this.getText(hour); }
|
||||
draw(hour_txt,x,y){
|
||||
/*var dim = DIM_70x40;
|
||||
g.setColor(0.5,0,0);
|
||||
g.fillPoly([x,y,
|
||||
x+dim[0],y,
|
||||
x+dim[0],y+dim[1],
|
||||
x,y+dim[1]
|
||||
]);*/
|
||||
g.setFontAlign(-1,-1,0);
|
||||
g.setFont("Vector",50);
|
||||
g.drawString(hour_txt,x,y);
|
||||
}
|
||||
getName(){return "Roman";}
|
||||
}
|
||||
|
||||
module.exports = [DigitNumeralFont,RomanNumeralFont];
|
|
@ -0,0 +1,10 @@
|
|||
class Hand {
|
||||
/**
|
||||
* Pure virtual class for all Hand classes to extend.
|
||||
* a hand class will have 1 main function
|
||||
* moveTo which will move the hand to the given angle.
|
||||
*/
|
||||
moveTo(angle){}
|
||||
}
|
||||
|
||||
module.exports = Hand;
|
|
@ -0,0 +1,137 @@
|
|||
|
||||
const TWO_PI = 2* Math.PI;
|
||||
|
||||
// The problem with the trig inverse functions on
|
||||
// a full circle is that the sector information will be lost
|
||||
// Choosing to use arcsin because you can get back the
|
||||
// sector with the help of the original coordinates
|
||||
function reifyasin(x,y,asin_angle){
|
||||
if(x >= 0 && y >= 0){
|
||||
return asin_angle;
|
||||
} else if(x >= 0 && y < 0){
|
||||
return Math.PI - asin_angle;
|
||||
} else if(x < 0 && y < 0){
|
||||
return Math.PI - asin_angle;
|
||||
} else {
|
||||
return TWO_PI + asin_angle;
|
||||
}
|
||||
}
|
||||
|
||||
// rebase and angle so be between -pi and pi
|
||||
// rather than 0 to 2PI
|
||||
function rebaseNegative(angle){
|
||||
if(angle > Math.PI){
|
||||
return angle - TWO_PI;
|
||||
} else {
|
||||
return angle;
|
||||
}
|
||||
}
|
||||
|
||||
// rebase an angle so that it is between 0 to 2pi
|
||||
// rather than -pi to pi
|
||||
function rebasePositive(angle){
|
||||
if(angle < 0){
|
||||
return angle + TWO_PI;
|
||||
} else {
|
||||
return angle;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Hour Scriber is responsible for drawing the numeral
|
||||
* on the screen at the requested angle.
|
||||
* It allows for the font to be changed on the fly.
|
||||
*/
|
||||
class HourScriber {
|
||||
constructor(radius, numeral_font, draw_test, bg_colour_supplier, numeral_colour_supplier, hour){
|
||||
this.radius = radius;
|
||||
this.numeral_font = numeral_font;
|
||||
this.draw_test = draw_test;
|
||||
this.curr_numeral_font = numeral_font;
|
||||
this.bg_colour_supplier = bg_colour_supplier;
|
||||
this.numeral_colour_supplier = numeral_colour_supplier;
|
||||
this.hours = hour;
|
||||
this.curr_hour_x = -1;
|
||||
this.curr_hour_y = -1;
|
||||
this.curr_hours = -1;
|
||||
this.curr_hour_str = null;
|
||||
this.last_draw_time = null;
|
||||
}
|
||||
setNumeralFont(numeral_font){
|
||||
this.numeral_font = numeral_font;
|
||||
}
|
||||
toString(){
|
||||
return "HourScriber{numeralfont=" + this.numeral_font.getName() + ",hours=" + this.hours + "}";
|
||||
}
|
||||
draw(){
|
||||
var changed = false;
|
||||
if(this.curr_hours != this.hours || this.curr_numeral_font !=this.numeral_font){
|
||||
var background = this.bg_colour_supplier();
|
||||
g.setColor(background[0],background[1],background[2]);
|
||||
this.curr_numeral_font.draw(this.curr_hour_str,
|
||||
this.curr_hour_x,
|
||||
this.curr_hour_y);
|
||||
//console.log("erasing old hour display:" + this.curr_hour_str + " color:" + background);
|
||||
var hours_frac = this.hours / 12;
|
||||
var angle = TWO_PI*hours_frac;
|
||||
var dimensions = this.numeral_font.getDimensions(this.hours);
|
||||
// we set the radial coord to be in the middle
|
||||
// of the drawn text.
|
||||
var width = dimensions[0];
|
||||
var height = dimensions[1];
|
||||
var delta_center_x = this.radius*Math.sin(angle) - width/2;
|
||||
var delta_center_y = this.radius*Math.cos(angle) + height/2;
|
||||
this.curr_hour_x = screen_center_x + delta_center_x;
|
||||
this.curr_hour_y = screen_center_y - delta_center_y;
|
||||
this.curr_hour_str = this.numeral_font.hour_txt(this.hours);
|
||||
// now work out the angle of the beginning and the end of the
|
||||
// text box so we know when to redraw
|
||||
// bottom left angle
|
||||
var x1 = delta_center_x;
|
||||
var y1 = delta_center_y;
|
||||
var r1 = Math.sqrt(x1*x1 + y1*y1);
|
||||
var angle1 = reifyasin(x1,y1,Math.asin(x1/r1));
|
||||
// bottom right angle
|
||||
var x2 = delta_center_x;
|
||||
var y2 = delta_center_y - height;
|
||||
var r2 = Math.sqrt(x2*x2 + y2*y2);
|
||||
var angle2 = reifyasin(x2,y2,Math.asin(x2/r2));
|
||||
// top left angle
|
||||
var x3 = delta_center_x + width;
|
||||
var y3 = delta_center_y;
|
||||
var r3 = Math.sqrt(x3*x3 + y3*y3);
|
||||
var angle3 = reifyasin(x3,y3, Math.asin(x3/r3));
|
||||
// top right angle
|
||||
var x4 = delta_center_x + width;
|
||||
var y4 = delta_center_y - height;
|
||||
var r4 = Math.sqrt(x4*x4 + y4*y4);
|
||||
var angle4 = reifyasin(x4,y4,Math.asin(x4/r4));
|
||||
if(Math.min(angle1,angle2,angle3,angle4) < Math.PI && Math.max(angle1,angle2,angle3,angle4) > 1.5*Math.PI){
|
||||
angle1 = rebaseNegative(angle1);
|
||||
angle2 = rebaseNegative(angle2);
|
||||
angle3 = rebaseNegative(angle3);
|
||||
angle3 = rebaseNegative(angle4);
|
||||
this.angle_from = rebasePositive( Math.min(angle1,angle2,angle3,angle4) );
|
||||
this.angle_to = rebasePositive( Math.max(angle1,angle2,angle3,angle4) );
|
||||
} else {
|
||||
this.angle_from = Math.min(angle1,angle2,angle3,angle4);
|
||||
this.angle_to = Math.max(angle1,angle2,angle3,angle4);
|
||||
}
|
||||
//console.log(angle1 + "/" + angle2 + " / " + angle3 + " / " + angle4);
|
||||
//console.log( this.angle_from + " to " + this.angle_to);
|
||||
this.curr_hours = this.hours;
|
||||
this.curr_numeral_font = this.numeral_font;
|
||||
changed = true;
|
||||
}
|
||||
if(changed ||
|
||||
this.draw_test(this.angle_from, this.angle_to, this.last_draw_time) ){
|
||||
var numeral_color = this.numeral_colour_supplier();
|
||||
g.setColor(numeral_color[0],numeral_color[1],numeral_color[2]);
|
||||
this.numeral_font.draw(this.curr_hour_str,this.curr_hour_x,this.curr_hour_y);
|
||||
this.last_draw_time = new Date();
|
||||
//console.log("redraw digit:" + this.hours);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HourScriber;
|
|
@ -0,0 +1,436 @@
|
|||
/**
|
||||
* Adrian Kirk 2021-03
|
||||
* Simple Clock showing 1 numeral for the hour
|
||||
* with a smooth sweep second.
|
||||
*/
|
||||
|
||||
var ThinHand = require("fontclock.thinhand.js");
|
||||
var ThickHand = require("fontclock.thickhand.js");
|
||||
var HourScriber = require("fontclock.hourscriber.js");
|
||||
|
||||
const screen_center_x = g.getWidth()/2;
|
||||
const screen_center_y = 10 + (g.getHeight()+10)/2;
|
||||
const TWO_PI = 2* Math.PI;
|
||||
|
||||
|
||||
SETTING_PREFIX = "fontclock";
|
||||
// load the date formats and languages required
|
||||
const FONTS_FILE = SETTING_PREFIX +".font.json";
|
||||
const DEFAULT_FONTS = [ "cpstc58" ];
|
||||
const DEFAULT_NUMERALS = [12,3,6,9];
|
||||
const DEFAULT_RADIUS = 70;
|
||||
var color_schemes = [
|
||||
{
|
||||
name: "black",
|
||||
background : [0.0,0.0,0.0],
|
||||
}
|
||||
];
|
||||
var fonts = DEFAULT_NUMERALS;
|
||||
var numerals = DEFAULT_NUMERALS;
|
||||
var radius = DEFAULT_RADIUS;
|
||||
|
||||
var fonts_info = null;
|
||||
try {
|
||||
fonts_info = require("Storage").readJSON(FONTS_FILE);
|
||||
} catch(e){
|
||||
console.log("failed to load fonts file:" + FONTS_FILE + e);
|
||||
}
|
||||
if(fonts_info != null){
|
||||
console.log("loaded font:" + JSON.stringify(fonts_info));
|
||||
fonts = fonts_info.fonts;
|
||||
numerals = fonts_info.numerals;
|
||||
radius = fonts_info.radius;
|
||||
color_schemes = fonts_info.color_schemes;
|
||||
} else {
|
||||
fonts = DEFAULT_FONTS;
|
||||
numerals = DEFAULT_NUMERALS;
|
||||
radius = DEFAULT_RADIUS;
|
||||
console.log("no fonts loaded defaulting to:" + fonts);
|
||||
}
|
||||
|
||||
if(fonts == null || fonts.length == 0){
|
||||
fonts = DEFAULT_FONTS;
|
||||
console.log("defaulting fonts to locale:" + fonts);
|
||||
}
|
||||
|
||||
let color_scheme_index = 0;
|
||||
|
||||
// The force draw is set to true to force all objects to redraw themselves
|
||||
let force_redraw = true;
|
||||
let bg_colour_supplier = ()=>color_schemes[color_scheme_index].background;
|
||||
var WHITE = [1.0,1.0,1.0];
|
||||
function default_white(color){
|
||||
if(color == null){
|
||||
return WHITE
|
||||
} else {
|
||||
return color;
|
||||
}
|
||||
}
|
||||
|
||||
// The seconds hand is the main focus and is set to redraw on every cycle
|
||||
let seconds_hand = new ThinHand(screen_center_x,
|
||||
screen_center_y,
|
||||
95,
|
||||
0,
|
||||
(angle, last_draw_time) => false,
|
||||
bg_colour_supplier,
|
||||
()=>default_white(color_schemes[color_scheme_index].second_hand));
|
||||
|
||||
// The minute hand is set to redraw at a 250th of a circle,
|
||||
// when the second hand is ontop or slighly overtaking
|
||||
// or when a force_redraw is called
|
||||
const minute_hand_angle_tolerance = TWO_PI/25
|
||||
let minutes_hand_redraw = function(angle, last_draw_time){
|
||||
return force_redraw || (seconds_hand.angle > angle &&
|
||||
Math.abs(seconds_hand.angle - angle) < minute_hand_angle_tolerance &&
|
||||
new Date().getTime() - last_draw_time.getTime() > 500);
|
||||
};
|
||||
|
||||
let minutes_hand = new ThinHand(screen_center_x,
|
||||
screen_center_y,
|
||||
80, minute_hand_angle_tolerance,
|
||||
minutes_hand_redraw,
|
||||
bg_colour_supplier,
|
||||
()=>default_white(color_schemes[color_scheme_index].minute_hand));
|
||||
// The hour hand is a thick hand so we have to redraw when the minute hand
|
||||
// overlaps from its behind angle coverage to its ahead angle coverage.
|
||||
let hour_hand_redraw = function(angle_from, angle_to, last_draw_time){
|
||||
return force_redraw || (seconds_hand.angle >= angle_from &&
|
||||
seconds_hand.angle <= angle_to &&
|
||||
new Date().getTime() - last_draw_time.getTime() > 500);
|
||||
};
|
||||
let hours_hand = new ThickHand(screen_center_x,
|
||||
screen_center_y,
|
||||
40,
|
||||
TWO_PI/600,
|
||||
hour_hand_redraw,
|
||||
bg_colour_supplier,
|
||||
() => default_white(color_schemes[color_scheme_index].hour_hand),
|
||||
5,
|
||||
4);
|
||||
|
||||
function draw_clock(){
|
||||
var date = new Date();
|
||||
draw_background();
|
||||
draw_hour_digits();
|
||||
draw_seconds(date);
|
||||
draw_mins(date);
|
||||
draw_hours(date);
|
||||
force_redraw = false;
|
||||
}
|
||||
// drawing the second the millisecond as we need the fine gradation
|
||||
// for the sweep second hand.
|
||||
function draw_seconds(date){
|
||||
var seconds = date.getSeconds() + date.getMilliseconds()/1000;
|
||||
var seconds_frac = seconds / 60;
|
||||
var seconds_angle = TWO_PI*seconds_frac;
|
||||
seconds_hand.moveTo(seconds_angle);
|
||||
}
|
||||
// drawing the minute includes the second and millisec to make the
|
||||
// movement as continuous as possible.
|
||||
function draw_mins(date,seconds_angle){
|
||||
var mins = date.getMinutes() + date.getSeconds()/60 + date.getMilliseconds()/(60*1000);
|
||||
var mins_frac = mins / 60;
|
||||
var mins_angle = TWO_PI*mins_frac;
|
||||
var redraw = minutes_hand.moveTo(mins_angle);
|
||||
if(redraw){
|
||||
//console.log("redraw mins");
|
||||
}
|
||||
}
|
||||
|
||||
function draw_hours(date){
|
||||
var hours = (date.getHours() % 12) + date.getMinutes()/60 + date.getSeconds()/3600;
|
||||
var hours_frac = hours / 12;
|
||||
var hours_angle = TWO_PI*hours_frac;
|
||||
var redraw = hours_hand.moveTo(hours_angle);
|
||||
if(redraw){
|
||||
//console.log("redraw hours");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
let numeral_fonts = [];
|
||||
for(var i=0; i< fonts.length; i++) {
|
||||
var file = SETTING_PREFIX +".font." + fonts[i] + ".js"
|
||||
console.log("loading font set:" + fonts[i] + "->" + file);
|
||||
var loaded_fonts = require(file);
|
||||
for (var j = 0; j < loaded_fonts[j]; j++) {
|
||||
var loaded_font = new loaded_fonts[j];
|
||||
numeral_fonts.push(loaded_font);
|
||||
console.log("loaded font name:" + loaded_font.getName())
|
||||
}
|
||||
}
|
||||
|
||||
let numeral_fonts_index = 0;
|
||||
const ONE_POINT_FIVE_PI = 1.5*Math.PI;
|
||||
/**
|
||||
* predicate for deciding when the digit has to be redrawn
|
||||
*/
|
||||
let hour_numeral_redraw = function(angle_from, angle_to, last_draw_time){
|
||||
var seconds_hand_angle = seconds_hand.angle;
|
||||
// we have to cope with the 12 problem where the
|
||||
// left side of the box has a value almost 2PI and the right
|
||||
// side has a small positive value. The values are rebased so
|
||||
// that they can be compared
|
||||
if(angle_from > angle_to && angle_from > ONE_POINT_FIVE_PI){
|
||||
angle_from = angle_from - TWO_PI;
|
||||
if(seconds_hand_angle > Math.PI)
|
||||
seconds_hand_angle = seconds_hand_angle - TWO_PI;
|
||||
}
|
||||
//console.log("initial:" + angle_from + "/" + angle_to + " seconds " + seconds_hand_angle);
|
||||
var redraw = force_redraw ||
|
||||
(seconds_hand_angle >= angle_from && seconds_hand_angle <= angle_to && seconds_hand.last_draw_time.getTime() > last_draw_time.getTime()) ||
|
||||
(minutes_hand.last_draw_time.getTime() > last_draw_time.getTime());
|
||||
if(redraw){
|
||||
//console.log(angle_from + "/" + angle_to + " seconds " + seconds_hand_angle);
|
||||
}
|
||||
return redraw;
|
||||
};
|
||||
|
||||
// now add the numbers to the clock face
|
||||
var numeral_colour_supplier = () => default_white(color_schemes[color_scheme_index].numeral);
|
||||
var hour_scribers = [];
|
||||
console.log("numerals:" + numerals + " length:" + numerals.length)
|
||||
console.log("radius:" + radius)
|
||||
for(var digit_idx=0; digit_idx<numerals.length; digit_idx++){
|
||||
var digit = numerals[digit_idx];
|
||||
var scriber = new HourScriber(radius,
|
||||
numeral_fonts[numeral_fonts_index],
|
||||
hour_numeral_redraw,
|
||||
bg_colour_supplier,
|
||||
numeral_colour_supplier,
|
||||
digit
|
||||
);
|
||||
hour_scribers.push(scriber);
|
||||
//console.log("digit:" + digit + "->" + scriber);
|
||||
}
|
||||
//console.log("hour_scribers:" + hour_scribers );
|
||||
|
||||
/**
|
||||
* Called from button 1 to change the numerals that are
|
||||
* displayed on the clock face
|
||||
*/
|
||||
function next_font() {
|
||||
var curr_font = numeral_fonts_index;
|
||||
numeral_fonts_index = numeral_fonts_index + 1;
|
||||
if (numeral_fonts_index >= numeral_fonts.length) {
|
||||
numeral_fonts_index = 0;
|
||||
}
|
||||
|
||||
if (curr_font != numeral_fonts_index) {
|
||||
console.log("numeral font changed")
|
||||
for (var i = 0; i < hour_scribers.length; i++) {
|
||||
hour_scribers[i].setNumeralFont(
|
||||
numeral_fonts[numeral_fonts_index]);
|
||||
}
|
||||
force_redraw = true;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const hour_zone_angle = hour_scribers.length/TWO_PI;
|
||||
function draw_hour_digits() {
|
||||
if(force_redraw){
|
||||
for(var i=0; i<hour_scribers.length; i++){
|
||||
var scriber = hour_scribers[i];
|
||||
//console.log("idx:" + i + "->" + scriber);
|
||||
scriber.draw();
|
||||
}
|
||||
} else {
|
||||
var hour_scriber_idx = (0.5 + (seconds_hand.angle * hour_zone_angle)) | 0;
|
||||
if (hour_scriber_idx >= hour_scribers.length)
|
||||
hour_scriber_idx = 0;
|
||||
|
||||
//console.log("angle:" + seconds_hand.angle + " idx:" + hour_scriber_idx);
|
||||
if (hour_scriber_idx >= 0) {
|
||||
hour_scribers[hour_scriber_idx].draw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function draw_background(){
|
||||
if(force_redraw){
|
||||
background = color_schemes[color_scheme_index].background;
|
||||
g.setColor(background[0],background[1],background[2]);
|
||||
g.fillPoly([0,25,
|
||||
0,240,
|
||||
240,240,
|
||||
240,25
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
function next_colorscheme(){
|
||||
var prev_color_scheme_index = color_scheme_index;
|
||||
color_scheme_index += 1;
|
||||
color_scheme_index = color_scheme_index % color_schemes.length;
|
||||
//console.log("color_scheme_index=" + color_scheme_index);
|
||||
force_redraw = true;
|
||||
if(prev_color_scheme_index == color_scheme_index){
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* called from load_settings on startup to
|
||||
* set the color scheme to named value
|
||||
*/
|
||||
function set_colorscheme(colorscheme_name){
|
||||
console.log("setting color scheme:" + colorscheme_name);
|
||||
for (var i=0; i < color_schemes.length; i++) {
|
||||
if(color_schemes[i].name == colorscheme_name){
|
||||
color_scheme_index = i;
|
||||
force_redraw = true;
|
||||
console.log("match");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* called from load_settings on startup
|
||||
* to set the font to named value
|
||||
*/
|
||||
function set_font(font_name){
|
||||
console.log("setting font:" + font_name);
|
||||
for (var i=0; i < numeral_fonts.length; i++) {
|
||||
if(numeral_fonts[i].getName() == font_name) {
|
||||
numeral_fonts_index = i;
|
||||
force_redraw = true;
|
||||
console.log("match");
|
||||
for (var j = 0; j < hour_scribers.length; j++) {
|
||||
hour_scribers[j].setNumeralFont(numeral_fonts[numeral_fonts_index]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on startup to set the watch to the last preference settings
|
||||
*/
|
||||
function load_settings(){
|
||||
try{
|
||||
var file = SETTING_PREFIX + ".settings.json";
|
||||
settings = require("Storage").readJSON(file);
|
||||
if(settings != null){
|
||||
console.log(file + " loaded:" + JSON.stringify(settings));
|
||||
if(settings.color_scheme != null){
|
||||
set_colorscheme(settings.color_scheme);
|
||||
}
|
||||
if(settings.font != null){
|
||||
set_font(settings.font);
|
||||
}
|
||||
} else {
|
||||
console.log(file + " not found - no settings to load");
|
||||
}
|
||||
} catch(e){
|
||||
console.log("failed to load settings:" + e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on button press to save down the last preference settings
|
||||
*/
|
||||
function save_settings(){
|
||||
var settings = {
|
||||
font : numeral_fonts[numeral_fonts_index].getName(),
|
||||
color_scheme : color_schemes[color_scheme_index].name,
|
||||
};
|
||||
var file = SETTING_PREFIX + ".settings.json";
|
||||
console.log(file + ": saving:" + JSON.stringify(settings));
|
||||
require("Storage").writeJSON(file,settings);
|
||||
}
|
||||
|
||||
// Boiler plate code for setting up the clock,
|
||||
// below
|
||||
let intervalRef = null;
|
||||
|
||||
function clearTimers(){
|
||||
if(intervalRef) {
|
||||
clearInterval(intervalRef);
|
||||
intervalRef = null;
|
||||
}
|
||||
}
|
||||
|
||||
function startTimers(){
|
||||
setTimeout(scheduleDrawClock,100);
|
||||
draw_clock();
|
||||
}
|
||||
|
||||
// The clock redraw is set to 100ms. This is the smallest number
|
||||
// that give the (my) human eye the illusion of a continious sweep
|
||||
// second hand.
|
||||
function scheduleDrawClock(){
|
||||
if(intervalRef) clearTimers();
|
||||
intervalRef = setInterval(draw_clock, 100);
|
||||
draw_clock();
|
||||
}
|
||||
|
||||
function reset_clock(){
|
||||
force_redraw = true;
|
||||
}
|
||||
|
||||
Bangle.on('lcdPower', (on) => {
|
||||
if (on) {
|
||||
console.log("lcdPower: on");
|
||||
reset_clock();
|
||||
startTimers();
|
||||
} else {
|
||||
console.log("lcdPower: off");
|
||||
reset_clock();
|
||||
clearTimers();
|
||||
}
|
||||
});
|
||||
|
||||
Bangle.on('faceUp',function(up){
|
||||
console.log("faceUp: " + up + " LCD: " + Bangle.isLCDOn());
|
||||
if (up && !Bangle.isLCDOn()) {
|
||||
//console.log("faceUp and LCD off");
|
||||
clearTimers();
|
||||
Bangle.setLCDPower(true);
|
||||
}
|
||||
});
|
||||
|
||||
g.clear();
|
||||
load_settings();
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
startTimers();
|
||||
|
||||
function button1pressed() {
|
||||
if (next_font()) {
|
||||
save_settings();
|
||||
}
|
||||
}
|
||||
|
||||
function button2pressed() {
|
||||
clearTimers();
|
||||
// the clock is being unloaded so we clear out the big
|
||||
// data structures for the launcher
|
||||
hour_scribers = [];
|
||||
Bangle.showLauncher();
|
||||
}
|
||||
|
||||
function button3pressed(){
|
||||
if(next_colorscheme()) {
|
||||
save_settings();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle button 1 being pressed
|
||||
setWatch(button1pressed, BTN1,{repeat:true,edge:"falling"});
|
||||
|
||||
// Handle button 1 being pressed
|
||||
setWatch(button2pressed, BTN2,{repeat:true,edge:"falling"});
|
||||
|
||||
// Handle button 3 being pressed
|
||||
setWatch(button3pressed, BTN3,{repeat:true,edge:"falling"});
|
||||
|
After Width: | Height: | Size: 6.5 KiB |
|
@ -0,0 +1,103 @@
|
|||
var Hand = require("fontclock.hand.js");
|
||||
|
||||
class ThickHand extends Hand {
|
||||
/**
|
||||
* The thick hand is created from a filled polygone, so its slower to
|
||||
* draw so to be used sparingly with few redraws
|
||||
*/
|
||||
constructor(centerX,
|
||||
centerY,
|
||||
length,
|
||||
tolerance,
|
||||
draw_test,
|
||||
color_bg_supplier,
|
||||
color_fg_supplier,
|
||||
base_height,
|
||||
thickness){
|
||||
super();
|
||||
this.centerX = centerX;
|
||||
this.centerY = centerY;
|
||||
this.length = length;
|
||||
this.color_bg_supplier = color_bg_supplier;
|
||||
this.color_fg_supplier = color_fg_supplier;
|
||||
this.base_height = base_height;
|
||||
// angle from the center to the top corners of the rectangle
|
||||
this.delta_top = Math.atan(thickness/(2*length));
|
||||
// angle from the center to the bottom corners of the rectangle
|
||||
this.delta_base = Math.atan(thickness/(2*base_height));
|
||||
// the radius that the bottom corners of the rectangle move through
|
||||
this.vertex_radius_base = Math.sqrt( (thickness*thickness/4) + base_height * base_height);
|
||||
// the radius that the top corners of the rectangle move through
|
||||
this.vertex_radius_top = Math.sqrt( (thickness*thickness/4) + length * length);
|
||||
// last records the last plotted values (so we don't have to keep recalculating
|
||||
this.last_x1 = centerX;
|
||||
this.last_y1 = centerY;
|
||||
this.last_x2 = centerX;
|
||||
this.last_y2 = centerY;
|
||||
this.last_x3 = centerX;
|
||||
this.last_y3 = centerY;
|
||||
this.last_x4 = centerX;
|
||||
this.last_y4 = centerY;
|
||||
// The change in angle from the last plotted angle before we actually redraw
|
||||
this.tolerance = tolerance;
|
||||
// predicate test that is called if the hand is not going to redraw to see
|
||||
// if there is an externally defined reason for redrawing (like another hand)
|
||||
this.draw_test = draw_test;
|
||||
this.angle = -1;
|
||||
this.last_draw_time = null;
|
||||
}
|
||||
// method to move the hand to a new angle
|
||||
moveTo(angle){
|
||||
if(Math.abs(angle - this.angle) > this.tolerance || this.draw_test(this.angle - this.delta_base,this.angle + this.delta_base ,this.last_draw_time) ){
|
||||
//var background = color_schemes[color_scheme_index].background;
|
||||
var background = this.color_bg_supplier;
|
||||
g.setColor(background[0],background[1],background[2]);
|
||||
g.fillPoly([this.last_x1,
|
||||
this.last_y1,
|
||||
this.last_x2,
|
||||
this.last_y2,
|
||||
this.last_x3,
|
||||
this.last_y3,
|
||||
this.last_x4,
|
||||
this.last_y4
|
||||
]);
|
||||
// bottom left
|
||||
var x1 = this.centerX +
|
||||
this.vertex_radius_base*Math.sin(angle - this.delta_base);
|
||||
var y1 = this.centerY - this.vertex_radius_base*Math.cos(angle - this.delta_base);
|
||||
// bottom right
|
||||
var x2 = this.centerX +
|
||||
this.vertex_radius_base*Math.sin(angle + this.delta_base);
|
||||
var y2 = this.centerY - this.vertex_radius_base*Math.cos(angle + this.delta_base);
|
||||
// top right
|
||||
var x3 = this.centerX + this.vertex_radius_top*Math.sin(angle + this.delta_top);
|
||||
var y3 = this.centerY - this.vertex_radius_top*Math.cos(angle + this.delta_top);
|
||||
// top left
|
||||
var x4 = this.centerX + this.vertex_radius_top*Math.sin(angle - this.delta_top);
|
||||
var y4 = this.centerY - this.vertex_radius_top*Math.cos(angle - this.delta_top);
|
||||
//var hand_color = color_schemes[color_scheme_index][this.color_theme];
|
||||
var hand_color = this.color_fg_supplier();
|
||||
g.setColor(hand_color[0],hand_color[1],hand_color[2]);
|
||||
g.fillPoly([x1,y1,
|
||||
x2,y2,
|
||||
x3,y3,
|
||||
x4,y4
|
||||
]);
|
||||
this.last_x1 = x1;
|
||||
this.last_y1 = y1;
|
||||
this.last_x2 = x2;
|
||||
this.last_y2 = y2;
|
||||
this.last_x3 = x3;
|
||||
this.last_y3 = y3;
|
||||
this.last_x4 = x4;
|
||||
this.last_y4 = y4;
|
||||
this.angle = angle;
|
||||
this.last_draw_time = new Date();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ThickHand;
|
|
@ -0,0 +1,67 @@
|
|||
var Hand = require("fontclock.hand.js");
|
||||
|
||||
class ThinHand extends Hand {
|
||||
/**
|
||||
* The thin hand is created from a simple line, so its easy and fast
|
||||
* to draw.
|
||||
*/
|
||||
constructor(centerX,
|
||||
centerY,
|
||||
length,
|
||||
tolerance,
|
||||
draw_test,
|
||||
color_bg_supplier,
|
||||
color_fg_supplier){
|
||||
super();
|
||||
this.centerX = centerX;
|
||||
this.centerY = centerY;
|
||||
this.length = length;
|
||||
this.color_bg_supplier = color_bg_supplier;
|
||||
this.color_fg_supplier = color_fg_supplier;
|
||||
// The last x and y coordinates (not the centre) of the last draw
|
||||
this.last_x = centerX;
|
||||
this.last_y = centerY;
|
||||
// tolerance is the angle tolerance (from the last draw)
|
||||
// in radians for a redraw to be called.
|
||||
this.tolerance = tolerance;
|
||||
// draw test is a predicate (angle, time). This is called
|
||||
// when the hand thinks that it does not have to draw (from its internal tests)
|
||||
// to see if it has to draw because of another object.
|
||||
this.draw_test = draw_test;
|
||||
// The current angle of the hand. Set to -1 initially
|
||||
this.angle = -1;
|
||||
this.last_draw_time = null;
|
||||
this.active = false;
|
||||
}
|
||||
// method to move the hand to a new angle
|
||||
moveTo(angle){
|
||||
// first test to see of the angle called is beyond the tolerance
|
||||
// for a redraw
|
||||
if(Math.abs(angle - this.angle) > this.tolerance ||
|
||||
// and then call the predicate to see if a redraw is needed
|
||||
this.draw_test(this.angle,this.last_draw_time) ){
|
||||
// rub out the old hand line
|
||||
var background = this.color_bg_supplier();
|
||||
g.setColor(background[0],background[1],background[2]);
|
||||
g.drawLine(this.centerX, this.centerY, this.last_x, this.last_y);
|
||||
// Now draw the new hand line
|
||||
var hand_color = this.color_fg_supplier();
|
||||
g.setColor(hand_color[0],hand_color[1],hand_color[2]);
|
||||
var x2 = this.centerX + this.length*Math.sin(angle);
|
||||
var y2 = this.centerY - this.length*Math.cos(angle);
|
||||
g.drawLine(this.centerX, this.centerY, x2, y2);
|
||||
// and store the last draw details for the next call
|
||||
this.last_x = x2;
|
||||
this.last_y = y2;
|
||||
this.angle = angle;
|
||||
this.last_draw_time = new Date();
|
||||
this.active = true;
|
||||
return true;
|
||||
} else {
|
||||
this.active = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ThinHand;
|
|
@ -2,3 +2,4 @@
|
|||
0.02: Increase text brightness, improve controls, (try to) reduce memory usage
|
||||
0.03: Only auto-start if active app is a clock, auto close after 1 hour of inactivity
|
||||
0.04: Setting to disable touch controls, minor bugfix
|
||||
0.05: Setting to disable double/triple press control, remove touch controls setting, reduce fadeout flicker
|
|
@ -22,8 +22,9 @@ You can change these under `Settings`->`App/Widget Settings`->`Music Controls`.
|
|||
Automatically load the app when you play music and close when the music stops.
|
||||
(If the app opened automatically, it closes after music has been paused for 5 minutes.)
|
||||
|
||||
**Touch**:
|
||||
Enable touch controls?
|
||||
**Simple button**:
|
||||
Disable double/triple pressing Button 2: always simply toggle play/pause.
|
||||
(For music players which handle multiple button presses themselves.)
|
||||
|
||||
## Controls
|
||||
|
||||
|
|
|
@ -13,10 +13,6 @@ let info = {
|
|||
};
|
||||
const POUT = 300000; // auto close timeout when paused: 5 minutes (in ms)
|
||||
const IOUT = 3600000; // auto close timeout for inactivity: 1 hour (in ms)
|
||||
// Touch controls? 0: off, 1: when LCD on, 2: always
|
||||
let s = require("Storage").readJSON("gbmusic.json", 1) || {};
|
||||
const TCTL = ("touch" in s) ? (s.touch|0)%3 : 1;
|
||||
delete s;
|
||||
|
||||
///////////////////////
|
||||
// Self-repeating timeouts
|
||||
|
@ -42,7 +38,7 @@ function fadeOut() {
|
|||
if (!Bangle.isLCDOn() || !fade) {
|
||||
return;
|
||||
}
|
||||
drawMusic();
|
||||
drawMusic(false); // don't clear: draw over existing text to prevent flicker
|
||||
setTimeout(fadeOut, 500);
|
||||
}
|
||||
function brightness() {
|
||||
|
@ -131,7 +127,7 @@ function f2hex(f) {
|
|||
return ("00"+(Math.round(f*255)).toString(16)).substr(-2);
|
||||
}
|
||||
/**
|
||||
* @param name
|
||||
* @param {string} name - musicinfo property "num"/"artist"/"album"/"track"
|
||||
* @return {string} Semi-random color to use for given info
|
||||
*/
|
||||
function infoColor(name) {
|
||||
|
@ -174,7 +170,6 @@ function trackColor() {
|
|||
////////////////////
|
||||
/**
|
||||
* Draw date and time
|
||||
* @return {*}
|
||||
*/
|
||||
function drawDateTime() {
|
||||
const now = new Date;
|
||||
|
@ -209,8 +204,9 @@ function drawDateTime() {
|
|||
|
||||
/**
|
||||
* Draw track number and total count
|
||||
* @param {boolean} clr - Clear area before redrawing?
|
||||
*/
|
||||
function drawNum() {
|
||||
function drawNum(clr) {
|
||||
let num = "";
|
||||
if ("n" in info && info.n>0) {
|
||||
num = "#"+info.n;
|
||||
|
@ -220,9 +216,11 @@ function drawNum() {
|
|||
}
|
||||
g.reset();
|
||||
g.setFont("Vector", 30)
|
||||
.setFontAlign(1, -1) // top right
|
||||
.clearRect(225, 30, 120, 60)
|
||||
.drawString(num, 225, 30);
|
||||
.setFontAlign(1, -1); // top right
|
||||
if (clr) {
|
||||
g.clearRect(225, 30, 120, 60);
|
||||
}
|
||||
g.drawString(num, 225, 30);
|
||||
}
|
||||
/**
|
||||
* Clear rectangle used by track title
|
||||
|
@ -232,8 +230,9 @@ function clearTrack() {
|
|||
}
|
||||
/**
|
||||
* Draw track title
|
||||
* @param {boolean} clr - Clear area before redrawing?
|
||||
*/
|
||||
function drawTrack() {
|
||||
function drawTrack(clr) {
|
||||
let size = fitText(info.track);
|
||||
if (size<25) {
|
||||
// the title is too long: start the scroller
|
||||
|
@ -250,7 +249,9 @@ function drawTrack() {
|
|||
g.setFont("Vector", size)
|
||||
.setFontAlign(0, 1) // center bottom
|
||||
.setColor(trackColor());
|
||||
if (clr) {
|
||||
clearTrack();
|
||||
}
|
||||
g.drawString(info.track, 119, 109);
|
||||
}
|
||||
/**
|
||||
|
@ -270,8 +271,9 @@ function drawScroller() {
|
|||
|
||||
/**
|
||||
* Draw track artist and album
|
||||
* @param {boolean} clr - Clear area before redrawing?
|
||||
*/
|
||||
function drawArtistAlbum() {
|
||||
function drawArtistAlbum(clr) {
|
||||
// we just use small enough fonts to make these always fit
|
||||
// calculate stuff before clear+redraw
|
||||
const aCol = infoColor("artist");
|
||||
|
@ -285,7 +287,9 @@ function drawArtistAlbum() {
|
|||
bSiz = 20;
|
||||
}
|
||||
g.reset();
|
||||
if (clr) {
|
||||
g.clearRect(0, 120, 240, 189);
|
||||
}
|
||||
let top = 124;
|
||||
if (info.artist) {
|
||||
g.setFont("Vector", aSiz)
|
||||
|
@ -347,7 +351,6 @@ function controlColor(ctrl) {
|
|||
return (ctrl in tCommand) ? "#ff0000" : "#008800";
|
||||
}
|
||||
function drawControl(ctrl, x, y) {
|
||||
if (!TCTL) {return;}
|
||||
g.setColor(controlColor(ctrl));
|
||||
const s = 20;
|
||||
if (stat!==controlState) {
|
||||
|
@ -379,10 +382,14 @@ function drawControls() {
|
|||
controlState = stat;
|
||||
}
|
||||
|
||||
function drawMusic() {
|
||||
drawNum();
|
||||
drawTrack();
|
||||
drawArtistAlbum();
|
||||
/**
|
||||
* @param {boolean} [clr=true] Clear area before redrawing?
|
||||
*/
|
||||
function drawMusic(clr) {
|
||||
clr = !(clr===false); // undefined means yes
|
||||
drawNum(clr);
|
||||
drawTrack(clr);
|
||||
drawArtistAlbum(clr);
|
||||
}
|
||||
|
||||
////////////////////////
|
||||
|
@ -390,7 +397,7 @@ function drawMusic() {
|
|||
///////////////////////
|
||||
/**
|
||||
* Update music info
|
||||
* @param e
|
||||
* @param {Object} e - Gadgetbridge musicinfo event
|
||||
*/
|
||||
function musicInfo(e) {
|
||||
info = e;
|
||||
|
@ -410,7 +417,11 @@ function musicInfo(e) {
|
|||
}
|
||||
}
|
||||
|
||||
let tPxt, tIxt;
|
||||
let tPxt, tIxt; // Timeouts to eXiT when Paused/Inactive for too long
|
||||
/**
|
||||
* Update music state
|
||||
* @param {Object} e - Gadgetbridge musicstate event
|
||||
*/
|
||||
function musicState(e) {
|
||||
stat = e.state;
|
||||
// if paused for five minutes, load the clock
|
||||
|
@ -446,6 +457,7 @@ function musicState(e) {
|
|||
}
|
||||
}
|
||||
if (Bangle.isLCDOn()) {
|
||||
drawMusic(false); // redraw in case we were fading out but resumed play
|
||||
drawControls();
|
||||
}
|
||||
}
|
||||
|
@ -473,12 +485,20 @@ function startButtonWatches() {
|
|||
tPress = setTimeout(() => {Bangle.showLauncher();}, 3000);
|
||||
}
|
||||
}, BTN2, {repeat: true, edge: "rising"});
|
||||
const s = require("Storage").readJSON("gbmusic.json", 1) || {};
|
||||
if (s.simpleButton) {
|
||||
setWatch(() => {
|
||||
clearTimeout(tPress);
|
||||
togglePlay();
|
||||
}, BTN2, {repeat: true, edge: "falling"});
|
||||
} else {
|
||||
setWatch(() => {
|
||||
nPress++;
|
||||
clearTimeout(tPress);
|
||||
tPress = setTimeout(handleButton2Press, 500);
|
||||
}, BTN2, {repeat: true, edge: "falling"});
|
||||
}
|
||||
}
|
||||
function handleButton2Press() {
|
||||
tPress = null;
|
||||
switch(nPress) {
|
||||
|
@ -500,7 +520,7 @@ function handleButton2Press() {
|
|||
let tCommand = {};
|
||||
/**
|
||||
* Send command and highlight corresponding control
|
||||
* @param command "play/pause/next/previous/volumeup/volumedown"
|
||||
* @param {string} command - "play"/"pause"/"next"/"previous"/"volumeup"/"volumedown"
|
||||
*/
|
||||
function sendCommand(command) {
|
||||
Bluetooth.println(JSON.stringify({t: "music", n: command}));
|
||||
|
@ -520,9 +540,8 @@ function togglePlay() {
|
|||
sendCommand(stat==="play" ? "pause" : "play");
|
||||
}
|
||||
function startTouchWatches() {
|
||||
if (!TCTL) {return;}
|
||||
Bangle.on("touch", side => {
|
||||
if (TCTL<2 && !Bangle.isLCDOn()) {return;}
|
||||
if (!Bangle.isLCDOn()) {return;} // for <2v10 firmware
|
||||
switch(side) {
|
||||
case 1:
|
||||
sendCommand(stat==="play" ? "pause" : "previous");
|
||||
|
@ -535,7 +554,7 @@ function startTouchWatches() {
|
|||
}
|
||||
});
|
||||
Bangle.on("swipe", dir => {
|
||||
if (TCTL<2 && !Bangle.isLCDOn()) {return;}
|
||||
if (!Bangle.isLCDOn()) {return;} // for <2v10 firmware
|
||||
sendCommand(dir===1 ? "previous" : "next");
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5,12 +5,11 @@
|
|||
const SETTINGS_FILE = "gbmusic.json",
|
||||
storage = require("Storage"),
|
||||
translate = require("locale").translate;
|
||||
const TOUCH_OPTIONS = ["Off", "When LCD on", "Always"];
|
||||
|
||||
// initialize with default settings...
|
||||
let s = {
|
||||
autoStart: true,
|
||||
touch: 1,
|
||||
simpleButton: false,
|
||||
};
|
||||
// ...and overwrite them with any saved values
|
||||
// This way saved values are preserved if a new version adds more settings
|
||||
|
@ -19,24 +18,27 @@
|
|||
s[key] = saved[key];
|
||||
}
|
||||
|
||||
function save(key, value) {
|
||||
function save(key) {
|
||||
return function (value) {
|
||||
s[key] = value;
|
||||
storage.write(SETTINGS_FILE, s);
|
||||
}
|
||||
}
|
||||
|
||||
const yesNo = (v) => translate(v ? "Yes" : "No");
|
||||
let menu = {
|
||||
"": {"title": "Music Control"},
|
||||
};
|
||||
menu[translate("< Back")] = back;
|
||||
menu[translate("Auto start")] = {
|
||||
value: s.autoStart,
|
||||
format: v => translate(v ? "Yes" : "No"),
|
||||
onchange: v => {save("autoStart", v);},
|
||||
value: !!s.autoStart,
|
||||
format: yesNo,
|
||||
onchange: save("autoStart"),
|
||||
};
|
||||
menu[translate("Touch")] = {
|
||||
value: s.touch|0,
|
||||
format: v => translate(TOUCH_OPTIONS[(v+3)%3]),
|
||||
onchange: v => {save("touch", (v+3)%3);},
|
||||
menu[translate("Simple button")] = {
|
||||
value: !!s.simpleButton,
|
||||
format: yesNo,
|
||||
onchange: save("simpleButton"),
|
||||
};
|
||||
|
||||
E.showMenu(menu);
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: Initial version
|
|
@ -0,0 +1,15 @@
|
|||
# Gadgetbridge Twist Control
|
||||
|
||||
Control your music app (e.g. MortPlayer Music [a folder based, not tag based player] ) that handles multiple play-commands (same as using a single-button-headset's button to change songs) on your Gadgetbridge-connected phone.
|
||||
- Activate counting for 4 seconds with a twist (beeps at start and end of counting)
|
||||
- twist multiple times for:
|
||||
play/pause (1),
|
||||
next song (2),
|
||||
prev. song (3),
|
||||
next folder (4),
|
||||
prev. folder (5),
|
||||
reset counter (6)
|
||||
- the command to be sent is shown in green
|
||||
- Volume up/down is controlled by BTN1/BTN3 presses
|
||||
|
||||

|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwIYVhAFEjgFEh4FEg+AAocD4AME8ADCgPAvAFCj/8nkQAoN//8enAQB///44FBgYFB8f4FoIFB+IFBh/+n/4AocH/AXBj/+gP8FIIFDFwM//0x/wFDAIIFNv4FB/4FNEaIFFj/gn5HCj+AAoUEh4FBMgUP4AFDw/gv/wAoPDPoKhBjnxAoKtBjl4TYLICninBagUPWYLJPFoIADZIYABnj6KABIA="))
|
|
@ -0,0 +1,97 @@
|
|||
// just a watch, to fill an empty screen
|
||||
|
||||
function drwClock() {
|
||||
var d = new Date();
|
||||
var h = d.getHours(), m = d.getMinutes();
|
||||
var time = ("0"+h).substr(-2) + ":" + ("0"+m).substr(-2);
|
||||
g.reset();
|
||||
g.setFont('6x8',7);
|
||||
g.setFontAlign(-1,-1);
|
||||
g.drawString(time,20,80);
|
||||
}
|
||||
|
||||
g.clear();
|
||||
drwClock();
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// control music by twist/buttons
|
||||
|
||||
var counter = 0; //stores your counted your twists
|
||||
var tstate = false; //are you ready to count the twists?
|
||||
|
||||
function playx() {
|
||||
Bluetooth.println(JSON.stringify({t:"music", n:"play"}));
|
||||
}
|
||||
|
||||
function volup() {
|
||||
Bluetooth.println(JSON.stringify({t:"music", n:"volumeup"}));
|
||||
}
|
||||
|
||||
function voldn() {
|
||||
Bluetooth.println(JSON.stringify({t:"music", n:"volumedown"}));
|
||||
}
|
||||
|
||||
function sendCmd() {
|
||||
print (counter);
|
||||
Bangle.beep(200,3000);
|
||||
if (tstate==false && counter>0){
|
||||
do {playx(); counter--;}
|
||||
while (counter >= 1);
|
||||
}
|
||||
}
|
||||
|
||||
function twistctrl() {
|
||||
if (tstate==false){
|
||||
tstate=true;
|
||||
setTimeout('tstate=false',4000);
|
||||
setTimeout(sendCmd,4100);
|
||||
Bangle.beep(200,3000);
|
||||
}
|
||||
else{
|
||||
g.clearRect(10,140,230,200);
|
||||
if (tstate==true){
|
||||
if (counter < 5){
|
||||
counter++;
|
||||
drwCmd();
|
||||
Bangle.buzz(100,2);
|
||||
}
|
||||
else {
|
||||
counter = 0;
|
||||
Bangle.buzz(400);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function drwCmd() {
|
||||
g.setFont('6x8',6);
|
||||
g.setColor(0.3,1,0.3);
|
||||
g.clearRect(10,140,230,200);
|
||||
switch (counter){
|
||||
case 1:
|
||||
g.drawString('play',50,150);
|
||||
break;
|
||||
case 2:
|
||||
g.drawString('next',50,150);
|
||||
break;
|
||||
case 3:
|
||||
g.drawString('prev',50,150);
|
||||
break;
|
||||
case 4:
|
||||
g.drawString('nx f',50,150);
|
||||
break;
|
||||
case 5:
|
||||
g.drawString('pr f',50,150);
|
||||
break;
|
||||
case 0:
|
||||
g.clearRect(10,140,230,200);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setWatch(volup,BTN1,{repeat:true});
|
||||
setWatch(voldn,BTN3,{repeat:true});
|
||||
Bangle.on('twist',twistctrl);
|
||||
setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"});
|
After Width: | Height: | Size: 906 B |
|
@ -1,3 +1,4 @@
|
|||
0.01: New App!
|
||||
0.02: Use HRM data and calculations from Bangle.js (don't access hardware directly)
|
||||
0.03: Fix timing issues, and use 1/2 scale to keep graph on screen
|
||||
0.04: Update for new firmwares that have a 'HRM-raw' event
|
||||
|
|
|
@ -4,18 +4,25 @@ Bangle.setHRMPower(1);
|
|||
var hrmInfo, hrmOffset = 0;
|
||||
var hrmInterval;
|
||||
function onHRM(h) {
|
||||
// this is the first time we're called
|
||||
if (counter!==undefined) {
|
||||
// the first time we're called remove
|
||||
// the countdown
|
||||
counter = undefined;
|
||||
g.clear();
|
||||
}
|
||||
hrmInfo = h;
|
||||
hrmOffset = 0;
|
||||
/* On 2v09 and earlier firmwares the only solution for realtime
|
||||
HRM was to look at the 'raw' array that got reported. If you timed
|
||||
it right you could grab the data pretty much as soon as it was written.
|
||||
In new firmwares, '.raw' is not available. */
|
||||
if (hrmInterval) clearInterval(hrmInterval);
|
||||
hrmInterval = undefined;
|
||||
if (hrmInfo.raw) {
|
||||
hrmOffset = 0;
|
||||
setTimeout(function() {
|
||||
hrmInterval = setInterval(readHRM,41);
|
||||
}, 40);
|
||||
}
|
||||
|
||||
var px = g.getWidth()/2;
|
||||
g.setFontAlign(0,0);
|
||||
|
@ -28,13 +35,32 @@ function onHRM(h) {
|
|||
g.drawString("BPM",px+15,45);
|
||||
}
|
||||
Bangle.on('HRM', onHRM);
|
||||
/* On newer (2v10) firmwares we can subscribe to get
|
||||
HRM events as they happen */
|
||||
Bangle.on('HRM-raw', function(v) {
|
||||
var a = v.raw;
|
||||
hrmOffset++;
|
||||
if (hrmOffset>g.getWidth()) {
|
||||
hrmOffset=0;
|
||||
g.clearRect(0,90,239,239);
|
||||
g.moveTo(-100,0);
|
||||
}
|
||||
|
||||
y = E.clip(170 - (v.raw*2),100,230);
|
||||
g.setColor(1,1,1);
|
||||
g.lineTo(hrmOffset, y);
|
||||
});
|
||||
|
||||
// It takes 5 secs for us to get the first HRM event
|
||||
var counter = 5;
|
||||
function countDown() {
|
||||
E.showMessage("Please wait...\n"+counter--);
|
||||
if (counter) setTimeout(countDown, 1000);
|
||||
if (counter) {
|
||||
g.drawString(counter--,g.getWidth()/2,g.getHeight()/2, true);
|
||||
setTimeout(countDown, 1000);
|
||||
}
|
||||
}
|
||||
g.clear().setFont("6x8",2).setFontAlign(0,0);
|
||||
g.drawString("Please wait...",g.getWidth()/2,g.getHeight()/2 - 16);
|
||||
countDown();
|
||||
|
||||
|
||||
|
|
|
@ -2,3 +2,5 @@
|
|||
0.02: Only store relevant app data (saves RAM when many apps)
|
||||
0.03: Allow scrolling to wrap around (fix #382)
|
||||
0.04: Now displays widgets
|
||||
0.05: Use g.theme for colours
|
||||
0.06: Use Bangle.setUI for buttons
|
||||
|
|
|
@ -12,47 +12,44 @@ var menuScroll = 0;
|
|||
var menuShowing = false;
|
||||
|
||||
function drawMenu() {
|
||||
g.setFont("6x8",2);
|
||||
g.setFontAlign(-1,0);
|
||||
var n = 3;
|
||||
g.reset().setFont("6x8",2).setFontAlign(-1,0);
|
||||
var w = g.getWidth();
|
||||
var h = g.getHeight();
|
||||
var m = w/2;
|
||||
var n = (h-48)/64;
|
||||
if (selected>=n+menuScroll) menuScroll = 1+selected-n;
|
||||
if (selected<menuScroll) menuScroll = selected;
|
||||
// arrows
|
||||
g.setColor(menuScroll ? -1 : 0);
|
||||
g.fillPoly([120,6,106,20,134,20]);
|
||||
g.setColor((apps.length>n+menuScroll) ? -1 : 0);
|
||||
g.fillPoly([120,233,106,219,134,219]);
|
||||
g.setColor(menuScroll ? g.theme.fg : g.theme.bg);
|
||||
g.fillPoly([m,6,m-14,20,m+14,20]);
|
||||
g.setColor((apps.length>n+menuScroll) ? g.theme.fg : g.theme.bg);
|
||||
g.fillPoly([m,h-7,m-14,h-21,m+14,h-21]);
|
||||
// draw
|
||||
g.setColor(-1);
|
||||
g.setColor(g.theme.fg);
|
||||
for (var i=0;i<n;i++) {
|
||||
var app = apps[i+menuScroll];
|
||||
if (!app) break;
|
||||
var y = 24+i*64;
|
||||
if (i+menuScroll==selected) {
|
||||
g.setColor(0.3,0.3,0.3);
|
||||
g.fillRect(0,y,239,y+63);
|
||||
g.setColor(1,1,1);
|
||||
g.drawRect(0,y,239,y+63);
|
||||
g.setColor(g.theme.bgH).fillRect(0,y,w-1,y+63);
|
||||
g.setColor(g.theme.fgH).drawRect(0,y,w-1,y+63);
|
||||
} else
|
||||
g.clearRect(0,y,239,y+63);
|
||||
g.clearRect(0,y,w-1,y+63);
|
||||
g.drawString(app.name,64,y+32);
|
||||
var icon=undefined;
|
||||
if (app.icon) icon = s.read(app.icon);
|
||||
if (icon) try {g.drawImage(icon,8,y+8);} catch(e){}
|
||||
}
|
||||
}
|
||||
g.clear();
|
||||
drawMenu();
|
||||
setWatch(function() {
|
||||
selected--;
|
||||
Bangle.setUI("updown",dir=>{
|
||||
if (dir) {
|
||||
selected += dir;
|
||||
if (selected<0) selected = apps.length-1;
|
||||
drawMenu();
|
||||
}, BTN1, {repeat:true});
|
||||
setWatch(function() {
|
||||
selected++;
|
||||
if (selected>=apps.length) selected = 0;
|
||||
drawMenu();
|
||||
}, BTN3, {repeat:true});
|
||||
setWatch(function() { // run
|
||||
} else {
|
||||
if (!apps[selected].src) return;
|
||||
if (require("Storage").read(apps[selected].src)===undefined) {
|
||||
E.showMessage("App Source\nNot found");
|
||||
|
@ -61,6 +58,7 @@ setWatch(function() { // run
|
|||
E.showMessage("Loading...");
|
||||
load(apps[selected].src);
|
||||
}
|
||||
}, BTN2, {repeat:true,edge:"falling"});
|
||||
}
|
||||
});
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
|
|
@ -10,3 +10,4 @@
|
|||
0.10: Added GPS and Grid Ref clock faces
|
||||
0.11: Updated Pedometer clock to retrieve steps from either wpedom or activepedom
|
||||
0.12: Removed GPS and Grid Ref clock faces, superceded by GPS setup and Walkers Clock
|
||||
0.13: Localised digi.js and timdat.js
|
|
@ -1,5 +1,7 @@
|
|||
(() => {
|
||||
|
||||
var locale = require("locale");
|
||||
|
||||
function getFace(){
|
||||
|
||||
var buf = Graphics.createArrayBuffer(240,92,1,{msb:true});
|
||||
|
@ -19,7 +21,7 @@ function getFace(){
|
|||
buf.drawString(time,buf.getWidth()/2,0);
|
||||
buf.setFont("6x8",2);
|
||||
buf.setFontAlign(0,-1);
|
||||
var date = d.toString().substr(0,15);
|
||||
var date = locale.dow(d, 1) + " " + locale.date(d, 1);
|
||||
buf.drawString(date, buf.getWidth()/2, 70);
|
||||
flip();
|
||||
}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
(() => {
|
||||
var locale = require("locale");
|
||||
var dayFirst = ["en_GB", "en_IN", "en_NAV", "de_DE", "nl_NL", "fr_FR", "en_NZ", "en_AU", "de_AT", "en_IL", "es_ES", "fr_BE", "de_CH", "fr_CH", "it_CH", "it_IT", "tr_TR", "pt_BR", "cs_CZ", "pt_PT"];
|
||||
var withDot = ["de_DE", "nl_NL", "de_AT", "de_CH", "hu_HU", "cs_CZ", "sl_SI"];
|
||||
|
||||
function getFace(){
|
||||
|
||||
var lastmin=-1;
|
||||
function drawClock(){
|
||||
var d=Date();
|
||||
if (d.getMinutes()==lastmin) return;
|
||||
d=d.toString().split(' ');
|
||||
var min=d[4].substr(3,2);
|
||||
var sec=d[4].substr(-2);
|
||||
var tm=d[4].substring(0,5);
|
||||
var hr=d[4].substr(0,2);
|
||||
lastmin=min;
|
||||
var tm=d.toString().split(' ')[4].substring(0,5);
|
||||
lastmin=d.getMinutes();
|
||||
g.reset();
|
||||
g.clearRect(0,24,239,239);
|
||||
var w=g.getWidth();
|
||||
|
@ -19,7 +19,16 @@
|
|||
g.drawString(tm,4+(w-g.stringWidth(tm))/2,64);
|
||||
g.setFontVector(36);
|
||||
g.setColor(0x07ff);
|
||||
var dt=d[0]+" "+d[1]+" "+d[2];//+" "+d[3];
|
||||
var dt=locale.dow(d, 1) + " ";
|
||||
if (dayFirst.includes(locale.name)) {
|
||||
dt+=d.getDate();
|
||||
if (withDot.includes(locale.name)) {
|
||||
dt+=".";
|
||||
}
|
||||
dt+=" " + locale.month(d, 1);
|
||||
} else {
|
||||
dt+=locale.month(d, 1) + " " + d.getDate();
|
||||
}
|
||||
g.drawString(dt,(w-g.stringWidth(dt))/2,160);
|
||||
g.flip();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
0.01: New App!
|
||||
0.02: Tweaks for Q3 watch
|
|
@ -0,0 +1,41 @@
|
|||
require("Font7x11Numeric7Seg").add(Graphics);
|
||||
|
||||
function draw() {
|
||||
var d = new Date();
|
||||
var size = Math.floor(g.getWidth()/(7*6));
|
||||
var x = (g.getWidth()/2) - size*6,
|
||||
y = (g.getHeight()/2) - size*7;
|
||||
g.reset().clearRect(0,y,g.getWidth(),y+size*12+8);
|
||||
g.setFont("7x11Numeric7Seg",size).setFontAlign(1,-1);
|
||||
g.drawString(d.getHours(), x, y);
|
||||
g.setFontAlign(-1,-1);
|
||||
if (d.getSeconds()&1) g.drawString(":", x,y);
|
||||
g.drawString(("0"+d.getMinutes()).substr(-2),x+size*4,y);
|
||||
// draw seconds
|
||||
g.setFont("7x11Numeric7Seg",size/2);
|
||||
g.drawString(("0"+d.getSeconds()).substr(-2),x+size*18,y + size*7);
|
||||
// date
|
||||
var s = d.toString().split(" ").slice(0,4).join(" ");
|
||||
g.setFont("6x8").setFontAlign(0,-1);
|
||||
g.drawString(s,g.getWidth()/2, y + size*12);
|
||||
}
|
||||
|
||||
// Only update when display turns on
|
||||
if (process.env.BOARD!="SMAQ3") // hack for Q3 which is always-on
|
||||
Bangle.on('lcdPower', function(on) {
|
||||
if (secondInterval)
|
||||
clearInterval(secondInterval);
|
||||
secondInterval = undefined;
|
||||
if (on)
|
||||
secondInterval = setInterval(draw, 1000);
|
||||
draw();
|
||||
});
|
||||
|
||||
g.clear();
|
||||
var secondInterval = setInterval(draw, 1000);
|
||||
draw();
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"});
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mUygP/AC5BlH4MAn/gAwN/4EP/AFBsEMhkBwEAjEDgYJBgEGgHA4EYDwOAmEwBIIYyj/wgf+AoMH/kA/4eBJXwYLVxgAjh//AC3w"))
|
After Width: | Height: | Size: 279 B |
|
@ -27,3 +27,5 @@
|
|||
0.22: Move HID to BLE menu
|
||||
0.23: Change max time offset to 13 for NZ summer daylight time (NZDT)
|
||||
0.24: Add Quiet Mode settings
|
||||
0.25: Move boot.js code into 'boot' app itself
|
||||
0.26: Use Bangle.softOff if available as this keeps the time
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
(() => {
|
||||
var settings = require('Storage').readJSON('setting.json', true);
|
||||
if (!settings) return;
|
||||
if (settings.options) Bangle.setOptions(settings.options);
|
||||
if (settings.quiet && settings.qmOptions) Bangle.setOptions(settings.qmOptions);
|
||||
if (settings.quiet && settings.qmBrightness) {
|
||||
if (settings.qmBrightness!=1) Bangle.setLCDBrightness(settings.qmBrightness);
|
||||
} else {
|
||||
if (settings.brightness && settings.brightness!=1) Bangle.setLCDBrightness(settings.brightness);
|
||||
}
|
||||
if (settings.quiet && settings.qmTimeout) Bangle.setLCDTimeout(s.qmTimeout);
|
||||
if (settings.passkey!==undefined && settings.passkey.length==6) NRF.setSecurity({passkey:settings.passkey, mitm:1, display:1});
|
||||
if (settings.whitelist) NRF.on('connect', function(addr) { if (!settings.whitelist.includes(addr)) NRF.disconnect(); });
|
||||
delete settings;
|
||||
})()
|
|
@ -115,7 +115,7 @@ function showMainMenu() {
|
|||
'Set Time': ()=>showSetTimeMenu(),
|
||||
'LCD': ()=>showLCDMenu(),
|
||||
'Reset Settings': ()=>showResetMenu(),
|
||||
'Turn Off': ()=>Bangle.off(),
|
||||
'Turn Off': ()=>{ if (Bangle.softOff) Bangle.softOff(); else Bangle.off() },
|
||||
'< Back': ()=>load()
|
||||
};
|
||||
return E.showMenu(mainmenu);
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
0.01: Initial Release
|
||||
0.02: Added Colour Themes
|
||||
0.03: Added Date
|
||||
0.04: Memory Footprint reduction
|
||||
|
|
|
@ -12,7 +12,7 @@ Use Button 1 (the top right button) to change the numeral type
|
|||
|
||||
| Default clock face | Roman Numeral Font | No Digits |
|
||||
| ---- | ---- | ---- |
|
||||
|  |  |  |
|
||||
|  |  |  |
|
||||
|
||||
|
||||
|
||||
|
@ -21,9 +21,14 @@ Button 3 (bottom right button) is used to change the colour
|
|||
|
||||
| Red | Grey | Purple |
|
||||
| ---- | ---- | ---- |
|
||||
|  |  |  |
|
||||
|  |  |  |
|
||||
|
||||
### Button 4
|
||||
Button 4 (bottom left of the screen) is used to change the date position. Note after cycling through the date positions there is the no date option.
|
||||
|
||||
| Top Right | Bottom Right | Bottom Left | Top Left |
|
||||
| ---- | ---- | ---- | ---- |
|
||||
|  |  |  |  |
|
||||
|
||||
## Further Details
|
||||
|
||||
|
@ -31,7 +36,7 @@ For further details of design and working please visit [The Project Page](https:
|
|||
|
||||
## Requests
|
||||
|
||||
Reach out to adrian@adriankirk.com if you have feature requests or notice bugs.
|
||||
Please reach out to adrian@adriankirk.com if you have feature requests or notice bugs.
|
||||
|
||||
## Creator
|
||||
|
||||
|
|
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 19 KiB |
|
@ -6,6 +6,7 @@
|
|||
|
||||
const screen_center_x = g.getWidth()/2;
|
||||
const screen_center_y = 10 + g.getHeight()/2;
|
||||
const TWO_PI = 2*Math.PI;
|
||||
|
||||
require("FontCopasetic40x58Numeric").add(Graphics);
|
||||
|
||||
|
@ -14,46 +15,40 @@ const color_schemes = [
|
|||
name: "black",
|
||||
background : [0.0,0.0,0.0],
|
||||
second_hand: [1.0,0.0,0.0],
|
||||
minute_hand: [1.0,1.0,1.0],
|
||||
hour_hand: [1.0,1.0,1.0],
|
||||
numeral:[1.0,1.0,1.0]
|
||||
},
|
||||
{
|
||||
name: "red",
|
||||
background : [1.0,0.0,0.0],
|
||||
second_hand: [1.0,1.0,0.0],
|
||||
minute_hand: [1.0,1.0,1.0],
|
||||
hour_hand: [1.0,1.0,1.0],
|
||||
numeral:[1.0,1.0,1.0]
|
||||
},
|
||||
{
|
||||
name: "grey",
|
||||
background : [0.5,0.5,0.5],
|
||||
second_hand: [0.0,0.0,0.0],
|
||||
minute_hand: [1.0,1.0,1.0],
|
||||
hour_hand: [1.0,1.0,1.0],
|
||||
numeral:[1.0,1.0,1.0]
|
||||
},
|
||||
{
|
||||
name: "purple",
|
||||
background : [1.0,0.0,1.0],
|
||||
second_hand: [1.0,1.0,0.0],
|
||||
minute_hand: [1.0,1.0,1.0],
|
||||
hour_hand: [1.0,1.0,1.0],
|
||||
numeral:[1.0,1.0,1.0]
|
||||
},
|
||||
{
|
||||
name: "blue",
|
||||
background : [0.4,0.7,1.0],
|
||||
second_hand: [0.5,0.5,0.5],
|
||||
minute_hand: [1.0,1.0,1.0],
|
||||
hour_hand: [1.0,1.0,1.0],
|
||||
numeral:[1.0,1.0,1.0]
|
||||
}
|
||||
];
|
||||
|
||||
let color_scheme_index = 0;
|
||||
|
||||
const WHITE = [1.0,1.0,1.0];
|
||||
function default_white(color){
|
||||
if(color == null){
|
||||
return WHITE;
|
||||
} else {
|
||||
return color;
|
||||
}
|
||||
}
|
||||
|
||||
class Hand {
|
||||
/**
|
||||
* Pure virtual class for all Hand classes to extend.
|
||||
|
@ -101,14 +96,14 @@ class ThinHand extends Hand {
|
|||
// and then call the predicate to see if a redraw is needed
|
||||
this.draw_test(this.angle,this.last_draw_time) ){
|
||||
// rub out the old hand line
|
||||
background = color_schemes[color_scheme_index].background;
|
||||
var background = color_schemes[color_scheme_index].background;
|
||||
g.setColor(background[0],background[1],background[2]);
|
||||
g.drawLine(this.centerX, this.centerY, this.last_x, this.last_y);
|
||||
// Now draw the new hand line
|
||||
hand_color = color_schemes[color_scheme_index][this.color_theme];
|
||||
var hand_color = default_white(color_schemes[color_scheme_index][this.color_theme]);
|
||||
g.setColor(hand_color[0],hand_color[1],hand_color[2]);
|
||||
x2 = this.centerX + this.length*Math.sin(angle);
|
||||
y2 = this.centerY - this.length*Math.cos(angle);
|
||||
var x2 = this.centerX + this.length*Math.sin(angle);
|
||||
var y2 = this.centerY - this.length*Math.cos(angle);
|
||||
g.drawLine(this.centerX, this.centerY, x2, y2);
|
||||
// and store the last draw details for the next call
|
||||
this.last_x = x2;
|
||||
|
@ -170,7 +165,7 @@ class ThickHand extends Hand {
|
|||
// method to move the hand to a new angle
|
||||
moveTo(angle){
|
||||
if(Math.abs(angle - this.angle) > this.tolerance || this.draw_test(this.angle - this.delta_base,this.angle + this.delta_base ,this.last_draw_time) ){
|
||||
background = color_schemes[color_scheme_index].background;
|
||||
var background = color_schemes[color_scheme_index].background;
|
||||
g.setColor(background[0],background[1],background[2]);
|
||||
g.fillPoly([this.last_x1,
|
||||
this.last_y1,
|
||||
|
@ -182,20 +177,20 @@ class ThickHand extends Hand {
|
|||
this.last_y4
|
||||
]);
|
||||
// bottom left
|
||||
x1 = this.centerX +
|
||||
var x1 = this.centerX +
|
||||
this.vertex_radius_base*Math.sin(angle - this.delta_base);
|
||||
y1 = this.centerY - this.vertex_radius_base*Math.cos(angle - this.delta_base);
|
||||
var y1 = this.centerY - this.vertex_radius_base*Math.cos(angle - this.delta_base);
|
||||
// bottom right
|
||||
x2 = this.centerX +
|
||||
var x2 = this.centerX +
|
||||
this.vertex_radius_base*Math.sin(angle + this.delta_base);
|
||||
y2 = this.centerY - this.vertex_radius_base*Math.cos(angle + this.delta_base);
|
||||
var y2 = this.centerY - this.vertex_radius_base*Math.cos(angle + this.delta_base);
|
||||
// top right
|
||||
x3 = this.centerX + this.vertex_radius_top*Math.sin(angle + this.delta_top);
|
||||
y3 = this.centerY - this.vertex_radius_top*Math.cos(angle + this.delta_top);
|
||||
var x3 = this.centerX + this.vertex_radius_top*Math.sin(angle + this.delta_top);
|
||||
var y3 = this.centerY - this.vertex_radius_top*Math.cos(angle + this.delta_top);
|
||||
// top left
|
||||
x4 = this.centerX + this.vertex_radius_top*Math.sin(angle - this.delta_top);
|
||||
y4 = this.centerY - this.vertex_radius_top*Math.cos(angle - this.delta_top);
|
||||
hand_color = color_schemes[color_scheme_index][this.color_theme];
|
||||
var x4 = this.centerX + this.vertex_radius_top*Math.sin(angle - this.delta_top);
|
||||
var y4 = this.centerY - this.vertex_radius_top*Math.cos(angle - this.delta_top);
|
||||
var hand_color = default_white(color_schemes[color_scheme_index][this.color_theme]);
|
||||
g.setColor(hand_color[0],hand_color[1],hand_color[2]);
|
||||
g.fillPoly([x1,y1,
|
||||
x2,y2,
|
||||
|
@ -227,20 +222,22 @@ let seconds_hand = new ThinHand(screen_center_x,
|
|||
0,
|
||||
(angle, last_draw_time) => false,
|
||||
"second_hand");
|
||||
|
||||
// The minute hand is set to redraw at a 250th of a circle,
|
||||
// when the second hand is ontop or slighly overtaking
|
||||
// or when a force_redraw is called
|
||||
let minutes_hand_redraw = function(angle, last_draw_time){
|
||||
return force_redraw || (seconds_hand.angle > angle &&
|
||||
Math.abs(seconds_hand.angle - angle) <2*Math.PI/25 &&
|
||||
Math.abs(seconds_hand.angle - angle) <TWO_PI/25 &&
|
||||
new Date().getTime() - last_draw_time.getTime() > 500);
|
||||
};
|
||||
let minutes_hand = new ThinHand(screen_center_x,
|
||||
screen_center_y,
|
||||
80,
|
||||
2*Math.PI/250,
|
||||
TWO_PI/250,
|
||||
minutes_hand_redraw,
|
||||
"minute_hand");
|
||||
"minute_hand"
|
||||
);
|
||||
// The hour hand is a thick hand so we have to redraw when the minute hand
|
||||
// overlaps from its behind andle coverage to its ahead angle coverage.
|
||||
let hour_hand_redraw = function(angle_from, angle_to, last_draw_time){
|
||||
|
@ -251,46 +248,106 @@ let hour_hand_redraw = function(angle_from, angle_to, last_draw_time){
|
|||
let hours_hand = new ThickHand(screen_center_x,
|
||||
screen_center_y,
|
||||
40,
|
||||
2*Math.PI/600,
|
||||
TWO_PI/600,
|
||||
hour_hand_redraw,
|
||||
"hour_hand",
|
||||
5,
|
||||
4);
|
||||
|
||||
function draw_clock(){
|
||||
date = new Date();
|
||||
var date = new Date();
|
||||
draw_background();
|
||||
draw_hour_digit(date);
|
||||
draw_seconds(date);
|
||||
draw_mins(date);
|
||||
draw_hours(date);
|
||||
draw_date(date);
|
||||
force_redraw = false;
|
||||
}
|
||||
|
||||
var local = require('locale');
|
||||
var last_date = null;
|
||||
var last_datestr = null;
|
||||
var last_coords = null;
|
||||
const date_coords = [
|
||||
{ name: "topright", coords:[180,30]},
|
||||
{ name: "bottomright", coords:[180,220]},
|
||||
{ name: "bottomleft", coords: [5,220]},
|
||||
{ name: "topleft", coords:[5,30]},
|
||||
{ name: "offscreen", coords: [240,30]}
|
||||
];
|
||||
|
||||
var date_coord_index = 0;
|
||||
|
||||
function draw_date(date){
|
||||
if(force_redraw || last_date == null || last_date.getDate() != date.getDate()){
|
||||
//console.log("redrawing date");
|
||||
g.setFontAlign(-1,-1,0);
|
||||
g.setFont("Vector",15);
|
||||
if(last_coords != null && last_datestr != null) {
|
||||
var background = color_schemes[color_scheme_index].background;
|
||||
g.setColor(background[0], background[1], background[2]);
|
||||
g.drawString(last_datestr, last_coords[0], last_coords[1]);
|
||||
}
|
||||
var coords = date_coords[date_coord_index].coords;
|
||||
if(coords != null) {
|
||||
var date_format = local.dow(date,1) + " " + date.getDate();
|
||||
var numeral_color = default_white(color_schemes[color_scheme_index].numeral);
|
||||
g.setColor(numeral_color[0], numeral_color[1], numeral_color[2]);
|
||||
g.drawString(date_format, coords[0], coords[1]);
|
||||
last_date = date;
|
||||
last_datestr = date_format;
|
||||
last_coords = coords;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function next_datecoords() {
|
||||
date_coord_index = date_coord_index + 1;
|
||||
if (date_coord_index >= date_coords.length) {
|
||||
date_coord_index = 0;
|
||||
}
|
||||
//console.log("date coord index->" + date_coord_index);
|
||||
force_redraw = true;
|
||||
}
|
||||
|
||||
function set_datecoords(date_name){
|
||||
console.log("setting date:" + date_name);
|
||||
for (var i=0; i < date_coords.length; i++) {
|
||||
if(date_coords[i].name == date_name){
|
||||
date_coord_index = i;
|
||||
force_redraw = true;
|
||||
console.log("date match");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// drawing the second the millisecond as we need the fine gradation
|
||||
// for the sweep second hand.
|
||||
function draw_seconds(date){
|
||||
seconds = date.getSeconds() + date.getMilliseconds()/1000;
|
||||
seconds_frac = seconds / 60;
|
||||
seconds_angle = 2*Math.PI*seconds_frac;
|
||||
var seconds = date.getSeconds() + date.getMilliseconds()/1000;
|
||||
var seconds_frac = seconds / 60;
|
||||
var seconds_angle = TWO_PI*seconds_frac;
|
||||
seconds_hand.moveTo(seconds_angle);
|
||||
}
|
||||
// drawing the minute includes the second and millisec to make the
|
||||
// movement as continuous as possible.
|
||||
function draw_mins(date,seconds_angle){
|
||||
mins = date.getMinutes() + date.getSeconds()/60 + date.getMilliseconds()/(60*1000);
|
||||
mins_frac = mins / 60;
|
||||
mins_angle = 2*Math.PI*mins_frac;
|
||||
redraw = minutes_hand.moveTo(mins_angle);
|
||||
var mins = date.getMinutes() + date.getSeconds()/60 + date.getMilliseconds()/(60*1000);
|
||||
var mins_frac = mins / 60;
|
||||
var mins_angle = TWO_PI*mins_frac;
|
||||
var redraw = minutes_hand.moveTo(mins_angle);
|
||||
if(redraw){
|
||||
//console.log("redraw mins");
|
||||
}
|
||||
}
|
||||
|
||||
function draw_hours(date){
|
||||
hours = (date.getHours() % 12) + date.getMinutes()/60 + date.getSeconds()/3600;
|
||||
hours_frac = hours / 12;
|
||||
hours_angle = 2*Math.PI*hours_frac;
|
||||
redraw = hours_hand.moveTo(hours_angle);
|
||||
var hours = (date.getHours() % 12) + date.getMinutes()/60 + date.getSeconds()/3600;
|
||||
var hours_frac = hours / 12;
|
||||
var hours_angle = TWO_PI*hours_frac;
|
||||
var redraw = hours_hand.moveTo(hours_angle);
|
||||
if(redraw){
|
||||
//console.log("redraw hours");
|
||||
}
|
||||
|
@ -329,27 +386,36 @@ class NoFont extends NumeralFont{
|
|||
getName(){return "NoFont";}
|
||||
}
|
||||
|
||||
const COPASET_DIM_20x58 = [20,58];
|
||||
const COPASET_DIM_30x58 = [30,58];
|
||||
const COPASET_DIM_40x58 = [40,58];
|
||||
const COPASET_DIM_50x58 = [50,58];
|
||||
|
||||
class CopasetFont extends NumeralFont{
|
||||
constructor(){
|
||||
super();
|
||||
// dimesion map provides the dimesions of the character for
|
||||
// each number for plotting and collision detection
|
||||
this.dimension_map = {
|
||||
1 : [20,58],
|
||||
2 : [30,58],
|
||||
3 : [30,58],
|
||||
4 : [30,58],
|
||||
5 : [30,58],
|
||||
6 : [40,58],
|
||||
7 : [30,58],
|
||||
8 : [40,58],
|
||||
9 : [40,58],
|
||||
10: [50,58],
|
||||
11: [40,58],
|
||||
12: [40,58]
|
||||
};
|
||||
}
|
||||
getDimensions(hour){return this.dimension_map[hour];}
|
||||
getDimensions(hour){
|
||||
switch(hour){
|
||||
case 1: return COPASET_DIM_20x58;
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 7:
|
||||
return COPASET_DIM_30x58;
|
||||
case 6:
|
||||
case 8:
|
||||
case 9:
|
||||
case 11:
|
||||
case 12:
|
||||
return COPASET_DIM_40x58;
|
||||
case 10:
|
||||
return COPASET_DIM_50x58;
|
||||
default:
|
||||
return COPASET_DIM_30x58;
|
||||
}
|
||||
}
|
||||
hour_txt(hour){ return hour.toString(); }
|
||||
draw(hour_txt,x,y){
|
||||
/* going to leave this in here for future testing.
|
||||
|
@ -363,51 +429,69 @@ class CopasetFont extends NumeralFont{
|
|||
x,y+dim[1]
|
||||
]);
|
||||
g.setColor(1.0,1.0,1.0);*/
|
||||
g.setFontAlign(-1,-1,0);
|
||||
g.setFontCopasetic40x58Numeric();
|
||||
g.drawString(hour_txt,x,y);
|
||||
}
|
||||
getName(){return "Copaset";}
|
||||
}
|
||||
|
||||
|
||||
const ROMAN_DIM_10x40 = [10,40];
|
||||
const ROMAN_DIM_20x40 = [20,40];
|
||||
const ROMAN_DIM_25x40 = [25,40];
|
||||
const ROMAN_DIM_30x40 = [30,40];
|
||||
const ROMAN_DIM_40x40 = [40,40];
|
||||
const ROMAN_DIM_60x40 = [60,40];
|
||||
const ROMAN_DIM_70x40 = [70,40];
|
||||
class RomanNumeralFont extends NumeralFont{
|
||||
constructor(){
|
||||
super();
|
||||
// text map provides the mapping between hour and roman numeral
|
||||
this.txt_map = {
|
||||
1 : 'I',
|
||||
2 : 'II',
|
||||
3 : 'III',
|
||||
4 : 'IV',
|
||||
5 : 'V',
|
||||
6 : 'VI',
|
||||
7 : 'VII',
|
||||
8 : 'VIII',
|
||||
9 : 'IX',
|
||||
10: 'X',
|
||||
11: 'XI',
|
||||
12: 'XII'
|
||||
};
|
||||
// dimesion map provides the dimesions of the characters for
|
||||
// each hour for plotting and collision detection
|
||||
this.dimension_map = {
|
||||
1 : [10,40],
|
||||
2 : [25,40],
|
||||
3 : [40,40],
|
||||
4 : [40,40],
|
||||
5 : [30,40],
|
||||
6 : [40,40],
|
||||
7 : [60,40],
|
||||
8 : [70,40],
|
||||
9 : [40,40],
|
||||
10: [20,40],
|
||||
11: [40,40],
|
||||
12: [60,40]
|
||||
};
|
||||
}
|
||||
getDimensions(hour){ return this.dimension_map[hour];}
|
||||
hour_txt(hour){ return this.txt_map[hour]; }
|
||||
getText(hour){
|
||||
switch (hour){
|
||||
case 1 : return 'I';
|
||||
case 2 : return 'II';
|
||||
case 3 : return 'III';
|
||||
case 4 : return 'IV';
|
||||
case 5 : return 'V';
|
||||
case 6 : return 'VI';
|
||||
case 7 : return 'VII';
|
||||
case 8 : return 'VIII';
|
||||
case 9 : return 'IX';
|
||||
case 10: return 'X';
|
||||
case 11: return 'XI';
|
||||
case 12: return 'XII';
|
||||
default: return '';
|
||||
}
|
||||
}
|
||||
getDimensions(hour){
|
||||
switch (hour){
|
||||
case 1:
|
||||
return ROMAN_DIM_10x40;
|
||||
case 2:
|
||||
return ROMAN_DIM_25x40;
|
||||
case 3:
|
||||
case 4:
|
||||
case 6:
|
||||
case 9:
|
||||
case 11:
|
||||
case 12:
|
||||
return ROMAN_DIM_40x40;
|
||||
case 5:
|
||||
return ROMAN_DIM_30x40;
|
||||
case 7:
|
||||
return ROMAN_DIM_60x40;
|
||||
case 8:
|
||||
return ROMAN_DIM_70x40;
|
||||
case 10:
|
||||
return ROMAN_DIM_20x40;
|
||||
default:
|
||||
return ROMAN_DIM_40x40;
|
||||
}
|
||||
}
|
||||
hour_txt(hour){ return this.getText(hour); }
|
||||
draw(hour_txt,x,y){
|
||||
g.setFontAlign(-1,-1,0);
|
||||
g.setFont("Vector",40);
|
||||
g.drawString(hour_txt,x,y);
|
||||
}
|
||||
|
@ -426,7 +510,7 @@ function reifyasin(x,y,asin_angle){
|
|||
} else if(x < 0 && y < 0){
|
||||
return Math.PI - asin_angle;
|
||||
} else {
|
||||
return 2*Math.PI + asin_angle;
|
||||
return TWO_PI + asin_angle;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -434,7 +518,7 @@ function reifyasin(x,y,asin_angle){
|
|||
// rather than 0 to 2PI
|
||||
function rebaseNegative(angle){
|
||||
if(angle > Math.PI){
|
||||
return angle - 2*Math.PI;
|
||||
return angle - TWO_PI;
|
||||
} else {
|
||||
return angle;
|
||||
}
|
||||
|
@ -444,7 +528,7 @@ function rebaseNegative(angle){
|
|||
// rather than -pi to pi
|
||||
function rebasePositive(angle){
|
||||
if(angle < 0){
|
||||
return angle + 2*Math.PI;
|
||||
return angle + TWO_PI;
|
||||
} else {
|
||||
return angle;
|
||||
}
|
||||
|
@ -471,48 +555,48 @@ class HourScriber {
|
|||
this.numeral_font = numeral_font;
|
||||
}
|
||||
drawHour(hours){
|
||||
changed = false;
|
||||
var changed = false;
|
||||
if(this.curr_hours != hours || this.curr_numeral_font !=this.numeral_font){
|
||||
background = color_schemes[color_scheme_index].background;
|
||||
var background = color_schemes[color_scheme_index].background;
|
||||
g.setColor(background[0],background[1],background[2]);
|
||||
this.curr_numeral_font.draw(this.curr_hour_str,
|
||||
this.curr_hour_x,
|
||||
this.curr_hour_y);
|
||||
//console.log("erasing old hour");
|
||||
hours_frac = hours / 12;
|
||||
angle = 2*Math.PI*hours_frac;
|
||||
dimensions = this.numeral_font.getDimensions(hours);
|
||||
var hours_frac = hours / 12;
|
||||
var angle = TWO_PI*hours_frac;
|
||||
var dimensions = this.numeral_font.getDimensions(hours);
|
||||
// we set the radial coord to be in the middle
|
||||
// of the drawn text.
|
||||
width = dimensions[0];
|
||||
height = dimensions[1];
|
||||
delta_center_x = this.radius*Math.sin(angle) - width/2;
|
||||
delta_center_y = this.radius*Math.cos(angle) + height/2;
|
||||
var width = dimensions[0];
|
||||
var height = dimensions[1];
|
||||
var delta_center_x = this.radius*Math.sin(angle) - width/2;
|
||||
var delta_center_y = this.radius*Math.cos(angle) + height/2;
|
||||
this.curr_hour_x = screen_center_x + delta_center_x;
|
||||
this.curr_hour_y = screen_center_y - delta_center_y;
|
||||
this.curr_hour_str = this.numeral_font.hour_txt(hours);
|
||||
// now work out the angle of the beginning and the end of the
|
||||
// text box so we know when to redraw
|
||||
// bottom left angle
|
||||
x1 = delta_center_x;
|
||||
y1 = delta_center_y;
|
||||
r1 = Math.sqrt(x1*x1 + y1*y1);
|
||||
angle1 = reifyasin(x1,y1,Math.asin(x1/r1));
|
||||
var x1 = delta_center_x;
|
||||
var y1 = delta_center_y;
|
||||
var r1 = Math.sqrt(x1*x1 + y1*y1);
|
||||
var angle1 = reifyasin(x1,y1,Math.asin(x1/r1));
|
||||
// bottom right angle
|
||||
x2 = delta_center_x;
|
||||
y2 = delta_center_y - height;
|
||||
r2 = Math.sqrt(x2*x2 + y2*y2);
|
||||
angle2 = reifyasin(x2,y2,Math.asin(x2/r2));
|
||||
var x2 = delta_center_x;
|
||||
var y2 = delta_center_y - height;
|
||||
var r2 = Math.sqrt(x2*x2 + y2*y2);
|
||||
var angle2 = reifyasin(x2,y2,Math.asin(x2/r2));
|
||||
// top left angle
|
||||
x3 = delta_center_x + width;
|
||||
y3 = delta_center_y;
|
||||
r3 = Math.sqrt(x3*x3 + y3*y3);
|
||||
angle3 = reifyasin(x3,y3, Math.asin(x3/r3));
|
||||
var x3 = delta_center_x + width;
|
||||
var y3 = delta_center_y;
|
||||
var r3 = Math.sqrt(x3*x3 + y3*y3);
|
||||
var angle3 = reifyasin(x3,y3, Math.asin(x3/r3));
|
||||
// top right angle
|
||||
x4 = delta_center_x + width;
|
||||
y4 = delta_center_y - height;
|
||||
r4 = Math.sqrt(x4*x4 + y4*y4);
|
||||
angle4 = reifyasin(x4,y4,Math.asin(x4/r4));
|
||||
var x4 = delta_center_x + width;
|
||||
var y4 = delta_center_y - height;
|
||||
var r4 = Math.sqrt(x4*x4 + y4*y4);
|
||||
var angle4 = reifyasin(x4,y4,Math.asin(x4/r4));
|
||||
if(Math.min(angle1,angle2,angle3,angle4) < Math.PI && Math.max(angle1,angle2,angle3,angle4) > 1.5*Math.PI){
|
||||
angle1 = rebaseNegative(angle1);
|
||||
angle2 = rebaseNegative(angle2);
|
||||
|
@ -532,7 +616,7 @@ class HourScriber {
|
|||
}
|
||||
if(changed ||
|
||||
this.draw_test(this.angle_from, this.angle_to, this.last_draw_time) ){
|
||||
numeral_color = color_schemes[color_scheme_index].numeral;
|
||||
var numeral_color = default_white(color_schemes[color_scheme_index].numeral);
|
||||
g.setColor(numeral_color[0],numeral_color[1],numeral_color[2]);
|
||||
this.numeral_font.draw(this.curr_hour_str,this.curr_hour_x,this.curr_hour_y);
|
||||
this.last_draw_time = new Date();
|
||||
|
@ -547,18 +631,18 @@ let numeral_fonts_index = 0;
|
|||
* predicate for deciding when the digit has to be redrawn
|
||||
*/
|
||||
let hour_numeral_redraw = function(angle_from, angle_to, last_draw_time){
|
||||
seconds_hand_angle = seconds_hand.angle;
|
||||
var seconds_hand_angle = seconds_hand.angle;
|
||||
// we have to cope with the 12 problem where the
|
||||
// left side of the box has a value almost 2PI and the right
|
||||
// side has a small positive value. The values are rebased so
|
||||
// that they can be compared
|
||||
if(angle_from > angle_to && angle_from > 1.5*Math.PI){
|
||||
angle_from = angle_from - 2*Math.PI;
|
||||
angle_from = angle_from - TWO_PI;
|
||||
if(seconds_hand_angle > Math.PI)
|
||||
seconds_hand_angle = seconds_hand_angle - 2*Math.PI;
|
||||
seconds_hand_angle = seconds_hand_angle - TWO_PI;
|
||||
}
|
||||
//console.log("initial:" + angle_from + "/" + angle_to + " seconds " + seconds_hand_angle);
|
||||
redraw = force_redraw ||
|
||||
var redraw = force_redraw ||
|
||||
(seconds_hand_angle >= angle_from && seconds_hand_angle <= angle_to) ||
|
||||
(minutes_hand.last_draw_time.getTime() > last_draw_time.getTime());
|
||||
if(redraw){
|
||||
|
@ -585,8 +669,8 @@ function next_font(){
|
|||
}
|
||||
|
||||
function draw_hour_digit(date){
|
||||
hours = date.getHours() % 12;
|
||||
mins = date.getMinutes();
|
||||
var hours = date.getHours() % 12;
|
||||
var mins = date.getMinutes();
|
||||
if(mins > 30){
|
||||
hours = (hours +1) % 12;
|
||||
}
|
||||
|
@ -598,7 +682,7 @@ function draw_hour_digit(date){
|
|||
|
||||
function draw_background(){
|
||||
if(force_redraw){
|
||||
background = color_schemes[color_scheme_index].background;
|
||||
var background = color_schemes[color_scheme_index].background;
|
||||
g.setColor(background[0],background[1],background[2]);
|
||||
g.fillPoly([0,25,
|
||||
0,240,
|
||||
|
@ -625,7 +709,7 @@ function set_colorscheme(colorscheme_name){
|
|||
if(color_schemes[i].name == colorscheme_name){
|
||||
color_scheme_index = i;
|
||||
force_redraw = true;
|
||||
console.log("match");
|
||||
console.log("color scheme match");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -641,7 +725,7 @@ function set_font(font_name){
|
|||
if(numeral_fonts[i].getName() == font_name){
|
||||
numeral_fonts_index = i;
|
||||
force_redraw = true;
|
||||
console.log("match");
|
||||
console.log("font match");
|
||||
hour_scriber.setNumeralFont(numeral_fonts[numeral_fonts_index]);
|
||||
break;
|
||||
}
|
||||
|
@ -653,7 +737,7 @@ function set_font(font_name){
|
|||
*/
|
||||
function load_settings(){
|
||||
try{
|
||||
settings = require("Storage").readJSON("sweepclock.settings.json");
|
||||
var settings = require("Storage").readJSON("sweepclock.settings.json");
|
||||
if(settings != null){
|
||||
console.log("loaded:" + JSON.stringify(settings));
|
||||
if(settings.color_scheme != null){
|
||||
|
@ -662,6 +746,9 @@ function load_settings(){
|
|||
if(settings.font != null){
|
||||
set_font(settings.font);
|
||||
}
|
||||
if(settings.date != null){
|
||||
set_datecoords(settings.date);
|
||||
}
|
||||
} else {
|
||||
console.log("no settings to load");
|
||||
}
|
||||
|
@ -670,16 +757,24 @@ function load_settings(){
|
|||
}
|
||||
}
|
||||
|
||||
function print_memoryusage(){
|
||||
var m = process.memory();
|
||||
var pc = Math.round(m.usage*100/m.total);
|
||||
console.log("memory usage: " + pc + "%");
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on button press to save down the last preference settings
|
||||
*/
|
||||
function save_settings(){
|
||||
settings = {
|
||||
var settings = {
|
||||
font : numeral_fonts[numeral_fonts_index].getName(),
|
||||
color_scheme : color_schemes[color_scheme_index].name,
|
||||
date: date_coords[date_coord_index].name
|
||||
};
|
||||
console.log("saving:" + JSON.stringify(settings));
|
||||
require("Storage").writeJSON("sweepclock.settings.json",settings);
|
||||
print_memoryusage();
|
||||
}
|
||||
|
||||
// Boiler plate code for setting up the clock,
|
||||
|
@ -708,14 +803,12 @@ function scheduleDrawClock(){
|
|||
}
|
||||
|
||||
function reset_clock(){
|
||||
g.clear();
|
||||
force_redraw = true;
|
||||
}
|
||||
|
||||
Bangle.on('lcdPower', (on) => {
|
||||
if (on) {
|
||||
console.log("lcdPower: on");
|
||||
Bangle.drawWidgets();
|
||||
reset_clock();
|
||||
startTimers();
|
||||
} else {
|
||||
|
@ -753,9 +846,17 @@ function button3pressed(){
|
|||
save_settings();
|
||||
}
|
||||
|
||||
function button4pressed(){
|
||||
//console.log("button 4 pressed");
|
||||
next_datecoords();
|
||||
save_settings();
|
||||
}
|
||||
|
||||
// Handle button 1 being pressed
|
||||
setWatch(button1pressed, BTN1,{repeat:true,edge:"falling"});
|
||||
|
||||
// Handle button 3 being pressed
|
||||
setWatch(button3pressed, BTN3,{repeat:true,edge:"falling"});
|
||||
|
||||
// Handle button 3 being pressed
|
||||
setWatch(button4pressed, BTN4,{repeat:true,edge:"falling"});
|
||||
|
|
|
@ -11,3 +11,4 @@
|
|||
0.09: Allow welcome to run after a fresh install
|
||||
More useful app menu
|
||||
BTN2 now goes to menu on release
|
||||
0.10: Tweaks to reduce memory usage
|
||||
|
|
|
@ -11,7 +11,7 @@ function animate(seq,period) {
|
|||
// Fade in to FG color with angled lines
|
||||
function fade(callback) {
|
||||
var n = 0;
|
||||
function f() {
|
||||
function f() {"ram"
|
||||
for (var i=n;i<240;i+=10) {
|
||||
g.drawLine(i,0,0,i);
|
||||
g.drawLine(i,240,240,i);
|
||||
|
@ -24,16 +24,17 @@ function fade(callback) {
|
|||
f();
|
||||
}
|
||||
|
||||
|
||||
var scenes = [
|
||||
function() {
|
||||
var SCENE_COUNT=11;
|
||||
function getScene(n) {
|
||||
if (n==0) return function() {
|
||||
g.clear(1);
|
||||
g.setFont("4x6",2);
|
||||
var n=0;
|
||||
var l = Bangle.getLogo();
|
||||
var i = setInterval(function() {
|
||||
n+=0.04;
|
||||
g.setColor(n,n,n);
|
||||
g.drawImage(Bangle.getLogo(),(240-222)/2,(240-100)/2);
|
||||
g.drawImage(l,(240-222)/2,(240-100)/2);
|
||||
if (n>=1) {
|
||||
clearInterval(i);
|
||||
setTimeout(()=>g.drawString("Open",34,144), 500);
|
||||
|
@ -41,7 +42,8 @@ var scenes = [
|
|||
setTimeout(()=>g.drawString("Smart Watch",34,168), 1500);
|
||||
}
|
||||
},50);
|
||||
},function() {
|
||||
};
|
||||
if (n==1) return function() {
|
||||
var img = require("heatshrink").decompress(atob("ptRxH+qYAfvl70mj5gAC0ekvd8FkAAdz3HJAYAH4+eJXWkJJYAF0hK2vfNJaIAB5t7S3fN5/V6wAD6vOTg9SumXy2W3QAB3eXul2JdnO63XAApPEVYvAJQIACJoRQDzBLoJQ3W5/NIwr4GJohMFAAROgJYvVJQiPGABZNN3bsdvYyESwnWJSIAC3RNM3V1JjZAES4nVJSYAB4xMNJrbkE56WD5xLVdB5NbFofNJbgABJh26qREPrFXrlbAAWjFgfWJgRLaTQhMLy5KNJINhsJLDrYrD5xLC6pLa5nGTR7oLq9bJQJMKTAXWJbbnR3RLJSoRMHv4pC5rkec6SaIrBLGw2r2XW1epcoqYeJiOXJYziEsOH2RBBw7lF56Yg5nGc6FScZOGJQPX2TmDFIfVTEBMSc4hLEw5KB6+rsJMH63X6pMf5hMQzBLCq5LD1ZLEJhTlfJiWXTA2GJYpMIcwPNc2O6TAuGRIPX1igDJg/PJmyYDcgXWwxMH1ApC53XcsHAJiVYcg2HJYZME0YpC5vWJkhLNJgLlDTAeFJhF/FQfVJkG6JiGXcomyJgOrJYhMErYqD53NJj7lRzBMDcoeGJhzoBJb3GJiN1qZBCJgWyJYpNF1LigAAXAJiNSJgzlGJgt/JkZLRy9TJgeHJhznFcuSZGw5MHJomjcuhLBqdcJiSaiTChMV1CYxy5LCqdXIAWy6+rJhCalTCN2JgdYH4WHJiGpTF7kDc43W2RMJTUZLQzBLFc4mr6+GJh2jTFmXJYyaEwuyc5Sag4xLZTQmG2WFJhxNaJYZMLJZSaEJoOHTR9/Ja+6JbdTqRNETRRNF1JLV4BLcAANYI5ToK1BLYJhWYJZwABq5NoJZ91JaAABdAZNS0ZLey9SJaRNYv5KM426JZmXuxKUJrKcL0lTzBLKzBKYJrVXvfGSol7EYWXJI27zF1JLQADq5NUrgYB4wAEEIV0comXI7wAFrCcPJgYWBTIIAETIN2JYmWuhMkdSdYCgOeJgueqRLFyzhfTi9bq4TC45MF49TuuXJlpONcogAC0hKB0gHDvZMEqRMpAANSq9crlbJAYADqwRDxGk0mIA4eCTQOeveXJdYAHqxNFdAeIAAQGCrOI0oHEAGVXTRJMGvgGCwRM7TAZMHwQGCvhM1rBMERIhMGAwdZJmtSqVTwNcwJEDJg19cvIADa4d9JhANDJnSLHJgrl6AAhFFAwpZDegjn7vhMGcvwABrJAFJgjl/TQpBBI4jl/AAN8TQhHDcv4ADcJBMDvpM+IYaeDAAhL+qd9SgycEJn7iEAA18Jf7nEcv4AIrJLIcv6aMcv4ADvhMHrJJ/AAbl/c6ZM/AAt9cv7nSIv7nLcv4AHrLl/TRpJBvgnjA=="));
|
||||
g.reset();
|
||||
g.setColor("#6633ff");
|
||||
|
@ -73,7 +75,8 @@ var scenes = [
|
|||
},20);
|
||||
},3500);
|
||||
|
||||
},function() {
|
||||
};
|
||||
if (n==2) return function() {
|
||||
g.reset();
|
||||
g.setBgColor("#ffa800");g.clear();
|
||||
g.setFont("6x8",2);
|
||||
|
@ -88,8 +91,8 @@ var scenes = [
|
|||
()=>g.drawString("2",200,120),
|
||||
()=>g.drawString("3",200,200)
|
||||
],200);
|
||||
},
|
||||
function() {
|
||||
};
|
||||
if (n==3) return function() {
|
||||
g.reset();
|
||||
g.setBgColor("#00a8ff");g.clear();
|
||||
g.setFontAlign(0,0);
|
||||
|
@ -98,8 +101,8 @@ var scenes = [
|
|||
g.setFontAlign(-1,-1);
|
||||
g.setFont("6x8",2);
|
||||
g.drawString("Move up\nin menus\n\nTurn Bangle.js on\nif it was off", 20,40);
|
||||
},
|
||||
function() {
|
||||
};
|
||||
if (n==4) return function() {
|
||||
g.reset();
|
||||
g.setBgColor("#00a8ff");g.clear();
|
||||
g.setFontAlign(0,0);
|
||||
|
@ -108,8 +111,8 @@ var scenes = [
|
|||
g.setFontAlign(-1,-1);
|
||||
g.setFont("6x8",2);
|
||||
g.drawString("Select menu\nitem\n\nLaunch app\nwhen watch\nis showing", 20,70);
|
||||
},
|
||||
function() {
|
||||
};
|
||||
if (n==5) return function() {
|
||||
g.reset();
|
||||
g.setBgColor("#00a8ff");g.clear();
|
||||
g.setFontAlign(0,0);
|
||||
|
@ -118,8 +121,8 @@ var scenes = [
|
|||
g.setFontAlign(-1,-1);
|
||||
g.setFont("6x8",2);
|
||||
g.drawString("Move down\nin menus\n\nLong press\nto exit app\nand go back\nto clock", 20,100);
|
||||
},
|
||||
function() {
|
||||
};
|
||||
if (n==6) return function() {
|
||||
g.reset();
|
||||
g.setBgColor("#ff3300");g.clear();
|
||||
g.setFontAlign(0,0);
|
||||
|
@ -129,8 +132,8 @@ var scenes = [
|
|||
g.setFontAlign(-1,-1);
|
||||
g.setFont("6x8",2);
|
||||
g.drawString("If Bangle.js\never stops,\nhold buttons\n1 and 2 for\naround six\nseconds.\n\n\n\nBangle.js will\nthen reboot.", 20,20);
|
||||
},
|
||||
function() {
|
||||
};
|
||||
if (n==7) return function() {
|
||||
g.reset();
|
||||
g.setBgColor("#00a8ff");g.clear();
|
||||
g.setFont("6x8",2);
|
||||
|
@ -147,8 +150,8 @@ var scenes = [
|
|||
g.drawString("work too. Try now",x,y+=h);
|
||||
g.drawString("to change page.",x,y+=h);}
|
||||
],300);
|
||||
},
|
||||
function() {
|
||||
};
|
||||
if (n==8) return function() {
|
||||
g.reset();
|
||||
g.setBgColor("#339900");g.clear();
|
||||
g.setFont("6x8",2);
|
||||
|
@ -165,8 +168,8 @@ var scenes = [
|
|||
g.drawString("with a Bluetooth",x,y+=h);
|
||||
g.drawString("capable device",x,y+=h);},
|
||||
],400);
|
||||
},
|
||||
function() {
|
||||
};
|
||||
if (n==9) return function() {
|
||||
g.reset();
|
||||
g.setBgColor("#990066");g.clear();
|
||||
g.setFont("6x8",2);
|
||||
|
@ -227,8 +230,8 @@ var scenes = [
|
|||
}
|
||||
|
||||
setInterval(draw,50);
|
||||
},
|
||||
function() {
|
||||
};
|
||||
if (n==10) return function() {
|
||||
g.reset();
|
||||
g.setBgColor("#660099");g.clear();
|
||||
g.setFontAlign(0,0);
|
||||
|
@ -245,18 +248,18 @@ var scenes = [
|
|||
g.drawString("Bangle.js",x,y+=h);}
|
||||
],400);
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
var sceneNumber = 0;
|
||||
|
||||
function move(dir) {
|
||||
if (dir>0 && sceneNumber+1 == scenes.length) return; // at the end
|
||||
sceneNumber = (sceneNumber+dir)%scenes.length;
|
||||
if (dir>0 && sceneNumber+1 == SCENE_COUNT) return; // at the end
|
||||
sceneNumber = (sceneNumber+dir)%SCENE_COUNT;
|
||||
if (sceneNumber<0) sceneNumber=0;
|
||||
clearInterval();
|
||||
scenes[sceneNumber]();
|
||||
getScene(sceneNumber)();
|
||||
if (sceneNumber>1) {
|
||||
var l = scenes.length;
|
||||
var l = SCENE_COUNT;
|
||||
for (var i=0;i<l-2;i++) {
|
||||
var x = 120+(i-(l-2)/2)*12;
|
||||
if (i<sceneNumber-1) {
|
||||
|
@ -270,7 +273,7 @@ function move(dir) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (sceneNumber < scenes.length-1)
|
||||
if (sceneNumber < SCENE_COUNT-1)
|
||||
setTimeout(function() {
|
||||
move(1);
|
||||
}, 5000);
|
||||
|
@ -282,7 +285,7 @@ Bangle.on('swipe',move);
|
|||
setWatch(()=>move(1), BTN3, {repeat:true});
|
||||
setWatch(()=>{
|
||||
// If we're on the last page
|
||||
if (sceneNumber == scenes.length-1) {
|
||||
if (sceneNumber == SCENE_COUNT-1) {
|
||||
load();
|
||||
}
|
||||
}, BTN2, {repeat:true,edge:"falling"});
|
||||
|
|
|
@ -2,3 +2,4 @@
|
|||
0.03: Tweaks for variable size widget system
|
||||
0.04: Ensure redrawing works with variable size widget system
|
||||
0.05: Fix regression stopping correct widget updates
|
||||
0.06: Use 'g.theme' (requires bootloader 0.23)
|
||||
|
|
|
@ -7,16 +7,16 @@
|
|||
function draw() {
|
||||
var s = 39;
|
||||
var x = this.x, y = this.y;
|
||||
g.reset();
|
||||
if (Bangle.isCharging()) {
|
||||
g.setColor(CHARGING).drawImage(atob("DhgBHOBzgc4HOP////////////////////3/4HgB4AeAHgB4AeAHgB4AeAHg"),x,y);
|
||||
x+=16;
|
||||
}
|
||||
g.setColor(-1);
|
||||
g.setColor(g.theme.fg);
|
||||
g.fillRect(x,y+2,x+s-4,y+21);
|
||||
g.clearRect(x+2,y+4,x+s-6,y+19);
|
||||
g.fillRect(x+s-3,y+10,x+s,y+14);
|
||||
g.setColor(CHARGING).fillRect(x+4,y+6,x+4+E.getBattery()*(s-12)/100,y+17);
|
||||
g.setColor(-1);
|
||||
}
|
||||
Bangle.on('charging',function(charging) {
|
||||
if(charging) Bangle.buzz();
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
(function(){
|
||||
var img_bt = E.toArrayBuffer(atob("CxQBBgDgFgJgR4jZMawfAcA4D4NYybEYIwTAsBwDAA=="));
|
||||
|
||||
function draw() {
|
||||
g.reset();
|
||||
if (NRF.getSecurityStatus().connected)
|
||||
g.setColor(0,0.5,1);
|
||||
else
|
||||
g.setColor(0.3,0.3,0.3);
|
||||
g.drawImage(img_bt,10+this.x,2+this.y);
|
||||
g.drawImage(atob("CxQBBgDgFgJgR4jZMawfAcA4D4NYybEYIwTAsBwDAA=="),10+this.x,2+this.y);
|
||||
}
|
||||
function changed() {
|
||||
WIDGETS["bluetooth"].draw();
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
(function(){
|
||||
if (!Bangle.isGPSOn) return; // old firmware
|
||||
var img = E.toArrayBuffer(atob("GBiBAAAAAAAAAAAAAA//8B//+BgYGBgYGBgYGBgYGBgYGBgYGB//+B//+BgYGBgYGBgYGBgYGBgYGBgYGB//+A//8AAAAAAAAAAAAA=="));
|
||||
|
||||
function draw() {
|
||||
g.reset();
|
||||
|
@ -9,7 +8,7 @@
|
|||
} else {
|
||||
g.setColor(0.3,0.3,0.3); // off = grey
|
||||
}
|
||||
g.drawImage(img, 10+this.x, 2+this.y);
|
||||
g.drawImage(atob("GBiBAAAAAAAAAAAAAA//8B//+BgYGBgYGBgYGBgYGBgYGBgYGB//+B//+BgYGBgYGBgYGBgYGBgYGBgYGB//+A//8AAAAAAAAAAAAA=="), 10+this.x, 2+this.y);
|
||||
}
|
||||
|
||||
var timerInterval;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
(function(){
|
||||
if (!Bangle.isHRMOn) return; // old firmware
|
||||
var img = E.toArrayBuffer(atob("FhaBAAAAAAAAAAAAAcDgD8/AYeGDAwMMDAwwADDAAMOABwYAGAwAwBgGADAwAGGAAMwAAeAAAwAAAAAAAAAAAAA="));
|
||||
|
||||
function draw() {
|
||||
g.reset();
|
||||
|
@ -9,7 +8,7 @@
|
|||
} else {
|
||||
g.setColor(0.3,0.3,0.3); // off = grey
|
||||
}
|
||||
g.drawImage(img, 10+this.x, 2+this.y);
|
||||
g.drawImage(atob("FhaBAAAAAAAAAAAAAcDgD8/AYeGDAwMMDAwwADDAAMOABwYAGAwAwBgGADAwAGGAAMwAAeAAAwAAAAAAAAAAAAA="), 10+this.x, 2+this.y);
|
||||
}
|
||||
|
||||
var timerInterval;
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
/* jshint esversion: 6 */
|
||||
(() => {
|
||||
var icon = require("heatshrink").decompress(atob("jEYwIKHgwCBhwCBh4CEggPCkACBmAXDBwVZ+EB+F4gEsjl8EgMP+EChk/gEMh+ehkA+YIBxwxBnF/4HggH/wEAj0AA=="));
|
||||
var color = 0x4A69;
|
||||
|
||||
function draw() {
|
||||
g.reset().setColor(color).drawImage(icon, this.x + 1, 0);
|
||||
g.reset().setColor(color).drawImage(require("heatshrink").decompress(atob("jEYwIKHgwCBhwCBh4CEggPCkACBmAXDBwVZ+EB+F4gEsjl8EgMP+EChk/gEMh+ehkA+YIBxwxBnF/4HggH/wEAj0AA==")), this.x + 1, 0);
|
||||
}
|
||||
|
||||
WIDGETS["widhwt"] = { area: "tr", width: 26, draw: draw };
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
/* jshint esversion: 6 */
|
||||
(() => {
|
||||
var id = NRF.getAddress().substr().substr(12).split(":");
|
||||
|
||||
// draw your widget at xpos
|
||||
function draw() {
|
||||
var id = NRF.getAddress().substr().substr(12).split(":");
|
||||
g.reset().setColor(0, 0.5, 1).setFont("6x8", 1);
|
||||
g.drawString(id[0], this.x+2, this.y+4, true);
|
||||
g.drawString(id[1], this.x+2, this.y+14, true);
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: First commit
|
|
@ -0,0 +1,10 @@
|
|||
(function(){
|
||||
Bangle.on('lcdPower', function(on) {
|
||||
WIDGETS["lock"].width = Bangle.isLCDOn()?0:16;
|
||||
Bangle.drawWidgets();
|
||||
});
|
||||
WIDGETS["lock"]={area:"tl",width:Bangle.isLCDOn()?0:16,draw:function(w) {
|
||||
if (!Bangle.isLCDOn())
|
||||
g.reset().drawImage(atob("DhABH+D/wwMMDDAwwMf/v//4f+H/h/8//P/z///f/g=="), w.x, w.y);
|
||||
}};
|
||||
})()
|
After Width: | Height: | Size: 728 B |
|
@ -9,3 +9,4 @@
|
|||
0.10: Fix daily goal, don't store settings in separate file
|
||||
0.11: added getSteps() method for apps to retrieve step count
|
||||
0.12: Respect Quiet Mode
|
||||
0.13: Now use system color theme
|
||||
|
|
|
@ -4,11 +4,6 @@
|
|||
'goal': 10000,
|
||||
'progress': false,
|
||||
}
|
||||
const COLORS = {
|
||||
'white': -1,
|
||||
'progress': 0x001F, // Blue
|
||||
'done': 0x03E0, // DarkGreen
|
||||
}
|
||||
const TAU = Math.PI*2;
|
||||
let lastUpdate = new Date();
|
||||
let stp_today = 0;
|
||||
|
@ -27,7 +22,7 @@
|
|||
function drawProgress(stps) {
|
||||
const width = 24, half = width/2;
|
||||
const goal = setting('goal'), left = Math.max(goal-stps,0);
|
||||
const c = left ? COLORS.progress : COLORS.done;
|
||||
const c = left ? "#00f" : "#090"; // blue or dark green
|
||||
g.setColor(c).fillCircle(this.x + half, this.y + half, half);
|
||||
if (left) {
|
||||
const f = left/goal; // fraction to blank out
|
||||
|
@ -47,7 +42,7 @@
|
|||
p[i - 2] += this.x;
|
||||
p[i - 1] += this.y;
|
||||
}
|
||||
g.setColor(0).fillPoly(p);
|
||||
g.setColor(g.theme.bg).fillPoly(p);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,10 +53,9 @@
|
|||
stp_today = stp_today % 100000; // cap to five digits + comma = 6 characters
|
||||
}
|
||||
let stps = stp_today.toString();
|
||||
g.reset();
|
||||
g.clearRect(this.x, this.y, this.x + width, this.y + 23); // erase background
|
||||
g.reset().clearRect(this.x, this.y, this.x + width, this.y + 23); // erase background
|
||||
if (setting('progress')){ drawProgress.call(this, stps); }
|
||||
g.setColor(COLORS.white);
|
||||
g.setColor(g.theme.fg);
|
||||
if (stps.length > 3){
|
||||
stps = stps.slice(0,-3) + "," + stps.slice(-3);
|
||||
g.setFont("4x6", 1); // if big, shrink text to fix
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
/* jshint esversion: 6 */
|
||||
(() => {
|
||||
const CBS = 0x41f, CBC = 0x07E0;
|
||||
var batS = require("heatshrink").decompress(atob("j0TwIHEv///kD////EfAYPwuEAgPB4EAg/HCgMfzgDBvwOC/IOC84ONDoUcFgc/AYOAHYRDE"));
|
||||
var xo = 6, xl = 22, yo = 9, h = 17;
|
||||
|
||||
function draw() {
|
||||
g.reset().setColor(CBS).drawImage(batS, this.x + 1, this.y + 4);
|
||||
g.reset().setColor(CBS).drawImage(require("heatshrink").decompress(atob("j0TwIHEv///kD////EfAYPwuEAgPB4EAg/HCgMfzgDBvwOC/IOC84ONDoUcFgc/AYOAHYRDE")), this.x + 1, this.y + 4);
|
||||
g.setColor(0).fillRect(this.x + xo, this.y + yo, this.x + xl, this.y + h);
|
||||
var cbc = (Bangle.isCharging()) ? CBC : CBS;
|
||||
g.setColor(cbc).fillRect(this.x + xo, this.y + yo, this.x + (xl - xo) / 100 * E.getBattery() + xo, this.y + h);
|
||||
|
|
|
@ -21,9 +21,8 @@
|
|||
}
|
||||
|
||||
function draw(){
|
||||
var img = E.toArrayBuffer(atob("GBgBAAAAAAAAAAAAAAAAAH4AAf+AB4HgDgBwHDw4OH4cMOcMYMMGYMMGMOcMOH4cHDw4DgBwB4HgAf+AAH4AAAAAAAAAAAAAAAAA"));
|
||||
g.setColor(0x07ff);
|
||||
g.drawImage(img,this.x,this.y);
|
||||
g.drawImage(atob("GBgBAAAAAAAAAAAAAAAAAH4AAf+AB4HgDgBwHDw4OH4cMOcMYMMGYMMGMOcMOH4cHDw4DgBwB4HgAf+AAH4AAAAAAAAAAAAAAAAA"),this.x,this.y);
|
||||
}
|
||||
|
||||
WIDGETS["viz"] ={area:"tl", width:24,draw:draw};
|
||||
|
|
2
core
|
@ -1 +1 @@
|
|||
Subproject commit 1b1293a5eb9b8bb9e4f743c4599f0587f597d368
|
||||
Subproject commit 3f2ff467f22b746da94160e59ff89b621601b261
|