Merge branch 'master' into master
commit
051f2cb71b
20
apps.json
20
apps.json
|
|
@ -242,8 +242,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.03",
|
||||
"description": "Smooth sweep secondhand with single hour numeral. Use button1 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,
|
||||
|
|
@ -3044,7 +3044,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",
|
||||
|
|
@ -3085,7 +3085,7 @@
|
|||
{ "id": "kitchen",
|
||||
"name": "Kitchen Combo",
|
||||
"icon": "kitchen.png",
|
||||
"version":"0.08",
|
||||
"version":"0.10",
|
||||
"description": "Combination of the Stepo, Walkersclock, Arrow and Waypointer apps into a multiclock format. 'Everything but the kitchen sink'. Requires firmware v2.08.167 or later",
|
||||
"tags": "tool,outdoors,gps",
|
||||
"type":"clock",
|
||||
|
|
@ -3096,10 +3096,13 @@
|
|||
{"name":"stepo.kit.js","url":"stepo.kit.js"},
|
||||
{"name":"gps.kit.js","url":"gps.kit.js"},
|
||||
{"name":"digi.kit.js","url":"digi.kit.js"},
|
||||
{"name":"heart.kit.js","url":"heart.kit.js"},
|
||||
{"name":"swatch.kit.js","url":"swatch.kit.js"},
|
||||
{"name":"compass.kit.js","url":"compass.kit.js"},
|
||||
{"name":"waypoints.json","url":"waypoints.json","evaluate":false},
|
||||
{"name":"kitchen.img","url":"kitchen.icon.js","evaluate":true}
|
||||
],
|
||||
"data": [
|
||||
{"name":"waypoints.json","url":"waypoints.json"}
|
||||
]
|
||||
},
|
||||
{ "id": "qmsched",
|
||||
|
|
@ -3152,7 +3155,7 @@
|
|||
"id": "omnitrix",
|
||||
"name":"Omnitrix",
|
||||
"icon":"omnitrix.png",
|
||||
"version": "1.0",
|
||||
"version": "0.01",
|
||||
"readme": "README.md",
|
||||
"description": "An Omnitrix Showpiece",
|
||||
"tags": "game",
|
||||
|
|
@ -3166,7 +3169,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",
|
||||
|
|
@ -3180,11 +3183,12 @@
|
|||
"name":"Dozenal Time",
|
||||
"shortName":"Dozenal Time",
|
||||
"icon":"app.png",
|
||||
"version":"1.0",
|
||||
"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}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -6,3 +6,5 @@
|
|||
0.06: Reduced memory footprint of compass, used direct screen access rather than arrayBuffer
|
||||
0.07: Added error codes if dependancies are missing
|
||||
0.08: Improved error handling for missing firmware features, added template app.kit.js
|
||||
0.09: Added heart rate monitor app
|
||||
0.10: Converted Stepo to use direct screen writes, added a Trip Counter feature to stepo
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Kitchen Combo - a multiclock format of the waypointer, walkersclock, stepo and arrow apps.
|
||||
# Kitchen Combo - a multiclock format of the waypointer, walkersclock, stepo, stopwatch, heartrate and arrow apps.
|
||||
|
||||

|
||||
|
||||
|
|
@ -50,8 +50,10 @@ The following buttons depend on which face is currently in use
|
|||
- Show step count in the middle of the doughnut gauge
|
||||
- The gauge show percentage of steps out of a goal of 10000 steps
|
||||
- When the battery is less than 25% the doughnut turns red
|
||||
- Use BTN1 to switch to the Trip Counter, use long press to reset Trip Counter
|
||||
- Use BTN3 to switch to the next app
|
||||
|
||||
|
||||
## GPS
|
||||

|
||||
- Use BTN1 long press to switch the GPS on or off
|
||||
|
|
@ -66,10 +68,16 @@ The following buttons depend on which face is currently in use
|
|||
- Use BTN3 to switch to the next app.
|
||||
|
||||
## Swatch
|
||||

|
||||
- A simple stopwatch
|
||||
- BTN1 - start, stop
|
||||
- BTN2 - lap if the timer is running, reset if the timer is stopped
|
||||
|
||||
## Heart
|
||||

|
||||
- A simple heart rate monitor, at present the app is just showing the raw value from HRM.bpm
|
||||
- BTN1, long press, turn heart rate monitor on / off
|
||||
|
||||
## Waypointer
|
||||
- Use BTN1 to select previous waypoint (when GPS is on)
|
||||
- Use BTN2 to select the next waypoint (when GPS is on)
|
||||
|
|
@ -218,6 +226,16 @@ I have settled on directly writing to the screen using the Graphics
|
|||
object (g.) for the compass App. This creates a bit of flicker when
|
||||
the arrow moves but is more reliable than using the ArrayBuffer.
|
||||
|
||||
v0.09: Since adding the heart rate monitor I have noticed that I can
|
||||
sometimes can a memory error when switch through the Apps back to the
|
||||
Stepo App. I think this can be cured by statically allocating the
|
||||
ArrayBuffer for stepo rather than using new everytime you switch back
|
||||
into the stepo watch face. The problem is that the bangle memory
|
||||
management / defragmentation is quite slow to run.
|
||||
|
||||
v0.10: Revisited having a display buffer for the stepo part of the App.
|
||||
Now use direct screen writing as it means less memory allocation and
|
||||
reduces chance of getting a memory error on switching watch faces.
|
||||
|
||||
### Error Codes
|
||||
|
||||
|
|
@ -227,8 +245,12 @@ The following error codes will be displayed if one of the dependancies is not me
|
|||
* E-CALIB - no compass calibration data was found, see 'Compass Calibration'
|
||||
* E-FW - require firmware 2v08.187 or later to detect gps and compass power status
|
||||
|
||||
### Issues
|
||||
### Issues / Future enhancements
|
||||
|
||||
* GPS time display shows GMT and not BST, needs localising
|
||||
* Occassional buzzing after 2-3 days of use, seems to disappear after
|
||||
a reset to the launcher menu. Needs investigation
|
||||
* Automatically switch the GPS power setting from Super-E to PSMOO 10
|
||||
seconds after the LCD goes off. At present I just rely on using
|
||||
the GPSSetup app and set the GPS power mode that I want.
|
||||
* Add a small graph to the heart rate monitor app
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
//console.log(o);
|
||||
}
|
||||
|
||||
function init(gps,sw) {
|
||||
function init(gps,sw, hrm) {
|
||||
showMem("compass init() START");
|
||||
gpsObject = gps;
|
||||
intervalRefSec = undefined;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
const Y_ACTIVITY = 116;
|
||||
const Y_MODELINE = 200;
|
||||
|
||||
function init(gps,sw) {
|
||||
function init(gps,sw,hrm) {
|
||||
showMem("digi init 1");
|
||||
days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday","Friday", "Saturday"];
|
||||
prevInfo = "";
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
//console.log(o);
|
||||
}
|
||||
|
||||
function init(gps, sw) {
|
||||
function init(gps, sw, hrm) {
|
||||
log_debug("gps init");
|
||||
//log_debug(gps);
|
||||
gpsObject = gps;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,116 @@
|
|||
(() => {
|
||||
function getFace(){
|
||||
const Y_TIME = 30;
|
||||
const Y_ACTIVITY = 116;
|
||||
let prevTime;
|
||||
let prevBpm;
|
||||
let toggle = 1;
|
||||
let redrawHrmPower = true;
|
||||
let intervalRefSec;
|
||||
let img;
|
||||
let hrmObject;
|
||||
|
||||
function log_debug(o) {
|
||||
//console.log(o);
|
||||
}
|
||||
|
||||
function init(gps, sw, hrm) {
|
||||
img = require("heatshrink").decompress(atob("mEwwRC/ABf/+ADBh//BQgGB//AgYDBCAQWCA4QPCDAYSC//8n4EC4AiEAAo1EBZIeDAAn8BZoKHJAYL7L64LLTa6/DAAi/CKhDjGBZBIGIwQ8IHQQ8IHQYwHBQgwFFwgwGFwgwGFwowFBQwwDFwwwEFwwwEFw4wDBRAkBERAkCERIA/AAYA="));
|
||||
prevTime = "-";
|
||||
prevBpm = "-";
|
||||
toggle = 1;
|
||||
redrawHrmPower = true;
|
||||
hrmObject = hrm;
|
||||
intervalRefSec;
|
||||
g.clear();
|
||||
}
|
||||
|
||||
function freeResources() {
|
||||
prevTime = undefined;
|
||||
img = undefined;
|
||||
}
|
||||
|
||||
function startTimer() {
|
||||
draw();
|
||||
intervalRefSec = setInterval(draw, 1000);
|
||||
}
|
||||
|
||||
function stopTimer() {
|
||||
if (intervalRefSec) { intervalRefSec = clearInterval(intervalRefSec); }
|
||||
}
|
||||
|
||||
function onButtonShort(btn) {}
|
||||
|
||||
function onButtonLong(btn) {
|
||||
log_debug("toggleHRM");
|
||||
if (btn !== 1) return;
|
||||
if (!Bangle.isHRMOn) return; // old firmware
|
||||
hrmObject.toggleHRMPower();
|
||||
prevBpm = '-';
|
||||
toggle = 1; // ensure we draw the heart first
|
||||
redrawHrmPower = true;
|
||||
}
|
||||
|
||||
function draw() {
|
||||
let d = new Date();
|
||||
let da = d.toString().split(" ");
|
||||
let time = da[4].substr(0,5);
|
||||
|
||||
if (time !== prevTime) {
|
||||
prevTime = time;
|
||||
g.setColor(0);
|
||||
g.fillRect(0, Y_TIME, 239, Y_ACTIVITY -1);
|
||||
g.setColor(1,1,1);
|
||||
g.setFont("Vector",80);
|
||||
g.setFontAlign(0,-1);
|
||||
g.drawString(time, 120, Y_TIME);
|
||||
}
|
||||
|
||||
let bpm = hrmObject.getBpm();
|
||||
|
||||
if (!Bangle.isHRMOn()) {
|
||||
if (!redrawHrmPower) return;
|
||||
redrawHrmPower = false;
|
||||
g.setColor(0);
|
||||
g.drawImage(img, 12, 132, {scale:2});
|
||||
g.fillRect(120,120,239,239);
|
||||
g.setColor(255,0,0);
|
||||
//g.setColor(0xFFC0); // yellow
|
||||
g.drawImage(img, 12, 132, {scale:2});
|
||||
|
||||
g.setFont("Vector",40);
|
||||
g.setFontAlign(0,0);
|
||||
g.setColor(1,1,1);
|
||||
g.drawString("OFF", 180, 180);
|
||||
return;
|
||||
}
|
||||
|
||||
// draw the heart
|
||||
if (++toggle % 2 === 0) {
|
||||
g.setColor(0);
|
||||
g.fillRect(12, 132, 108, 228);
|
||||
} else {
|
||||
g.setColor(255,0,0);
|
||||
//g.setColor(0xFFC0); // yellow
|
||||
g.drawImage(img, 12, 132, {scale:2});
|
||||
}
|
||||
|
||||
// draw the bpm
|
||||
if (bpm !== prevBpm) {
|
||||
prevBpm = bpm;
|
||||
g.setColor(0);
|
||||
g.fillRect(120, 120, 239, 239);
|
||||
g.setColor(1,1,1);
|
||||
//g.setColor(0xFFC0); // yellow
|
||||
g.setFont("Vector",52);
|
||||
g.setFontAlign(0,0);
|
||||
g.drawString(bpm, 180, 180);
|
||||
}
|
||||
}
|
||||
|
||||
return {init:init, freeResources:freeResources, startTimer:startTimer, stopTimer:stopTimer,
|
||||
onButtonShort:onButtonShort, onButtonLong:onButtonLong};
|
||||
}
|
||||
|
||||
return getFace;
|
||||
})();
|
||||
|
|
@ -26,15 +26,17 @@ function nextFace(){
|
|||
|
||||
g.clear();
|
||||
g.reset();
|
||||
face.init(gpsObj, swObj);
|
||||
face.init(gpsObj, swObj, hrmObj, tripObject);
|
||||
startdraw();
|
||||
}
|
||||
|
||||
// when you feel the buzzer you know you have done a long press
|
||||
function longPressCheck() {
|
||||
Bangle.buzz();
|
||||
debug_log("long PressCheck() buzz");
|
||||
if (pressTimer) {
|
||||
clearInterval(pressTimer);
|
||||
debug_log("clear pressTimer 2");
|
||||
pressTimer = undefined;
|
||||
}
|
||||
}
|
||||
|
|
@ -45,6 +47,11 @@ function buttonPressed(btn) {
|
|||
nextFace();
|
||||
} else {
|
||||
firstPress = getTime();
|
||||
if (pressTimer) {
|
||||
debug_log("clear pressTimer 1");
|
||||
clearInterval(pressTimer);
|
||||
}
|
||||
debug_log("set pressTimer 1");
|
||||
pressTimer = setInterval(longPressCheck, 1500);
|
||||
}
|
||||
}
|
||||
|
|
@ -53,6 +60,7 @@ function buttonPressed(btn) {
|
|||
function buttonReleased(btn) {
|
||||
var dur = getTime() - firstPress;
|
||||
if (pressTimer) {
|
||||
debug_log("clear pressTimer 3");
|
||||
clearInterval(pressTimer);
|
||||
pressTimer = undefined;
|
||||
}
|
||||
|
|
@ -248,6 +256,7 @@ GPS.prototype.processFix = function(fix) {
|
|||
this.gpsState = this.GPS_RUNNING;
|
||||
if (!this.last_fix.fix && !(require("Storage").readJSON("setting.json", 1) || {}).quiet) {
|
||||
Bangle.buzz(); // buzz on first position
|
||||
debug_log("GPS fix buzz");
|
||||
}
|
||||
this.last_fix = fix;
|
||||
}
|
||||
|
|
@ -651,6 +660,122 @@ function stopwatchDraw() {
|
|||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
|
||||
Heart Rate Monitor
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
function HRM() {
|
||||
this.bpm = 0;
|
||||
this.confidence = 0;
|
||||
}
|
||||
|
||||
HRM.prototype.log_debug = function(o) {
|
||||
//console.log(o);
|
||||
}
|
||||
|
||||
HRM.prototype.toggleHRMPower = function() {
|
||||
this.log_debug("HRM.toggleHRMPower()");
|
||||
if (!Bangle.isHRMOn) return; // old firmware
|
||||
|
||||
if (!Bangle.isHRMOn()) {
|
||||
this.log_debug("HRM.toggleHRMPower(powerOn)");
|
||||
Bangle.removeListener('HRM', onHRM);
|
||||
Bangle.setHRMPower(1);
|
||||
Bangle.on('HRM', onHRM);
|
||||
} else {
|
||||
this.log_debug("HRM.toggleHRMPower(powerOff)");
|
||||
Bangle.removeListener('HRM', onHRM);
|
||||
Bangle.setHRMPower(0);
|
||||
}
|
||||
|
||||
// poke the hrt widget indicator to change
|
||||
if (WIDGETS.widhrt !== undefined) {
|
||||
WIDGETS.widhrt.draw();
|
||||
}
|
||||
}
|
||||
|
||||
HRM.prototype.getBpm = function() {
|
||||
return this.bpm;
|
||||
}
|
||||
|
||||
HRM.prototype.getConfidence = function() {
|
||||
return this.confidence;
|
||||
}
|
||||
|
||||
HRM.prototype.onHRM = function(hrm) {
|
||||
this.bpm = hrm.bpm;
|
||||
this.confidence = hrm.confidence;
|
||||
this.log_debug("onHRM:(bpm)" + this.bpm);
|
||||
this.log_debug("onHRM:(conf) " + this.confidence);
|
||||
}
|
||||
|
||||
let hrmObj = new HRM();
|
||||
|
||||
function onHRM(hrm) {
|
||||
hrmObj.onHRM(hrm);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
|
||||
Trip Counter
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
function TRIP() {
|
||||
this.showTrip = false;
|
||||
this.tripStart = 0;
|
||||
}
|
||||
|
||||
TRIP.prototype.resetTrip = function(steps) {
|
||||
this.tripStart = (0 + steps);
|
||||
console.log("resetTrip starting=" + this.tripStart);
|
||||
}
|
||||
|
||||
TRIP.prototype.getTrip = function(steps) {
|
||||
let tripSteps = (0 + steps) - this.tripStart;
|
||||
console.log("getTrip steps=" + steps);
|
||||
console.log("getTrip tripStart=" + this.tripStart);
|
||||
console.log("getTrip=" + tripSteps);
|
||||
return tripSteps;
|
||||
}
|
||||
|
||||
TRIP.prototype.getTripState = function() {
|
||||
return this.showTrip;
|
||||
}
|
||||
|
||||
TRIP.prototype.setTripState = function(t) {
|
||||
this.showTrip = t;
|
||||
}
|
||||
|
||||
let tripObject = new TRIP();
|
||||
|
||||
/*****************************************************************************
|
||||
|
||||
Debug Object
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
/*
|
||||
function DEBUG() {
|
||||
this.logfile = require("Storage").open("debug.log","a");
|
||||
}
|
||||
|
||||
DEBUG.prototype.log = function(msg) {
|
||||
let timestamp = new Date().toString().split(" ")[4];
|
||||
let line = timestamp + ", " + msg + "\n";
|
||||
this.logfile.write(line);
|
||||
}
|
||||
|
||||
debugObj = new DEBUG();
|
||||
*/
|
||||
|
||||
function debug_log(m) {
|
||||
//debugObj.log(m);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
|
||||
Start App
|
||||
|
|
@ -659,6 +784,6 @@ Start App
|
|||
|
||||
g.clear();
|
||||
Bangle.loadWidgets();
|
||||
face.init(gpsObj,swObj);
|
||||
face.init(gpsObj,swObj, hrmObj, tripObject);
|
||||
startdraw();
|
||||
setButtons();
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
|
|
@ -1,45 +1,29 @@
|
|||
(() => {
|
||||
function getFace(){
|
||||
var pal4color;
|
||||
var pal4red;
|
||||
var buf;
|
||||
var intervalRefSec;
|
||||
var trip;
|
||||
var prevSteps;
|
||||
|
||||
function init(g,sw) {
|
||||
showMem("stepo init 1");
|
||||
pal4color = new Uint16Array([0x0000,0xFFFF,0x7BEF,0xAFE5],0,2); // b,w,grey,greenyellow
|
||||
pal4red = new Uint16Array([0x0000,0xFFFF,0xF800,0xAFE5],0,2); // b,w,red,greenyellow
|
||||
buf = Graphics.createArrayBuffer(120,120,2,{msb:true});
|
||||
showMem("stepo init 2");
|
||||
function init(g,sw,hrm,tr) {
|
||||
trip = tr;
|
||||
}
|
||||
|
||||
function freeResources() {
|
||||
showMem("stepo free 1");
|
||||
pal4color = undefined;
|
||||
pal4red = undefined;
|
||||
buf = undefined;
|
||||
showMem("stepo free 2");
|
||||
trip = undefined;
|
||||
prevSteps = -1;
|
||||
}
|
||||
|
||||
function showMem(msg) {
|
||||
var val = process.memory();
|
||||
var str = msg + " " + Math.round(val.usage*100/val.total) + "%";
|
||||
//console.log(str);
|
||||
function onButtonShort(btn) {
|
||||
trip.setTripState(!trip.getTripState());
|
||||
drawStepText();
|
||||
}
|
||||
|
||||
function flip(x,y) {
|
||||
g.drawImage({width:120,height:120,bpp:2,buffer:buf.buffer, palette:pal4color}, x, y);
|
||||
buf.clear();
|
||||
function onButtonLong(btn) {
|
||||
trip.resetTrip(getSteps());
|
||||
trip.setTripState(true);
|
||||
drawStepText();
|
||||
}
|
||||
|
||||
function flip_red(x,y) {
|
||||
g.drawImage({width:120,height:120,bpp:2,buffer:buf.buffer, palette:pal4red}, x, y);
|
||||
buf.clear();
|
||||
}
|
||||
|
||||
function onButtonShort(btn) {}
|
||||
function onButtonLong(btn) {}
|
||||
|
||||
function radians(a) {
|
||||
return a*Math.PI/180;
|
||||
}
|
||||
|
|
@ -55,10 +39,16 @@
|
|||
|
||||
function drawSteps() {
|
||||
var i = 0;
|
||||
var cx = 60;
|
||||
var cy = 60;
|
||||
var cx = 60 + 60;
|
||||
var cy = 60 + 115;
|
||||
var r = 56;
|
||||
var steps = getSteps();
|
||||
|
||||
if (prevSteps == steps)
|
||||
return;
|
||||
|
||||
prevSteps = steps;
|
||||
|
||||
var percent = steps / 10000;
|
||||
|
||||
if (percent > 1) percent = 1;
|
||||
|
|
@ -67,35 +57,57 @@
|
|||
var midrot = -180 - (360 * percent);
|
||||
var endrot = -360 - 180;
|
||||
|
||||
buf.setColor(3); // green-yellow
|
||||
g.setColor(0xAFE5); // greenyellow
|
||||
|
||||
// draw guauge
|
||||
for (i = startrot; i > midrot; i -= 4) {
|
||||
x = cx + r * Math.sin(radians(i));
|
||||
y = cy + r * Math.cos(radians(i));
|
||||
buf.fillCircle(x,y,4);
|
||||
g.fillCircle(x,y,4);
|
||||
}
|
||||
|
||||
buf.setColor(2); // grey
|
||||
|
||||
// draw remainder of guage in grey
|
||||
for (i = midrot; i > endrot; i -= 4) {
|
||||
x = cx + r * Math.sin(radians(i));
|
||||
y = cy + r * Math.cos(radians(i));
|
||||
buf.fillCircle(x,y,4);
|
||||
}
|
||||
|
||||
// draw steps
|
||||
buf.setColor(1); // white
|
||||
buf.setFont("Vector", 24);
|
||||
buf.setFontAlign(0,0);
|
||||
buf.drawString(steps, cx, cy);
|
||||
|
||||
// change the remaining color to RED if battery is below 25%
|
||||
if (E.getBattery() > 25)
|
||||
flip(60,115);
|
||||
g.setColor(0x7BEF); // grey
|
||||
else
|
||||
flip_red(60,115);
|
||||
g.setColor(0xF800); // red
|
||||
|
||||
// draw remainder of guage in grey or red
|
||||
for (i = midrot; i > endrot; i -= 4) {
|
||||
x = cx + r * Math.sin(radians(i));
|
||||
y = cy + r * Math.cos(radians(i));
|
||||
g.fillCircle(x,y,4);
|
||||
}
|
||||
}
|
||||
|
||||
function drawStepText() {
|
||||
var cx = 60 + 60;
|
||||
var cy = 60 + 115;
|
||||
var r = 56;
|
||||
var steps = getSteps();
|
||||
|
||||
/*
|
||||
* if our trip count is greater than todays steps then we have
|
||||
* rolled over to the next day so we should reset the trip counter
|
||||
*/
|
||||
if (trip.getTrip(steps) < 0)
|
||||
trip.resetTrip(steps);
|
||||
|
||||
// show trip count or total steps today
|
||||
g.setFontAlign(0,0);
|
||||
g.setFont("Vector", 24);
|
||||
|
||||
// clear the space for the text
|
||||
g.clearRect(cx - (r - 12), cy - 16, cx + (r - 12), cy + 16);
|
||||
|
||||
if (trip.getTripState() == true) {
|
||||
g.setColor(0x7BEF); // grey
|
||||
//g.setColor(1,0,0); // red
|
||||
g.drawString(trip.getTrip(steps), cx, cy);
|
||||
} else {
|
||||
g.setColor(1,1,1); // white
|
||||
g.drawString(steps, cx, cy);
|
||||
}
|
||||
}
|
||||
|
||||
function draw() {
|
||||
|
|
@ -110,6 +122,7 @@
|
|||
g.drawString(time, 120, 30, true);
|
||||
|
||||
drawSteps();
|
||||
drawStepText();
|
||||
}
|
||||
|
||||
function getSteps() {
|
||||
|
|
@ -132,5 +145,4 @@
|
|||
}
|
||||
|
||||
return getFace;
|
||||
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
function getFace(){
|
||||
let swObject = undefined;
|
||||
|
||||
function init(gps, sw) {
|
||||
function init(gps, sw, hrm) {
|
||||
swObject = sw;
|
||||
g.clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
0.01: Initial Release
|
||||
0.02: Added Colour Themes
|
||||
0.03: Added Date
|
||||
|
|
|
|||
|
|
@ -23,7 +23,12 @@ Button 3 (bottom right button) is used to change the colour
|
|||
| ---- | ---- | ---- |
|
||||
|  |  |  |
|
||||
|
||||
### Button 4
|
||||
Button 4 (bottom left of screen) is used to change the date positioning (or to remove from the screen)
|
||||
|
||||
| Top Right | Bottom Right | Bottom Left | Top Left |
|
||||
| ---- | ---- | ---- | ---- |
|
||||
|  |  |  |  |
|
||||
|
||||
## Further Details
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
Binary file not shown.
|
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);
|
||||
|
||||
|
|
@ -101,14 +102,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 = 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 +171,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 +183,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 = 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 +228,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 +254,105 @@ 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;
|
||||
var 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 = 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].getName() == 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");
|
||||
}
|
||||
|
|
@ -363,6 +425,7 @@ 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);
|
||||
}
|
||||
|
|
@ -408,6 +471,7 @@ class RomanNumeralFont extends NumeralFont{
|
|||
getDimensions(hour){ return this.dimension_map[hour];}
|
||||
hour_txt(hour){ return this.txt_map[hour]; }
|
||||
draw(hour_txt,x,y){
|
||||
g.setFontAlign(-1,-1,0);
|
||||
g.setFont("Vector",40);
|
||||
g.drawString(hour_txt,x,y);
|
||||
}
|
||||
|
|
@ -426,7 +490,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 +498,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 +508,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 +535,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 +596,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 = 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 +611,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 +649,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 +662,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 +689,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 +705,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 +717,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 +726,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");
|
||||
}
|
||||
|
|
@ -674,9 +741,10 @@ function load_settings(){
|
|||
* 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);
|
||||
|
|
@ -753,9 +821,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"});
|
||||
|
|
|
|||
2
core
2
core
|
|
@ -1 +1 @@
|
|||
Subproject commit 1b1293a5eb9b8bb9e4f743c4599f0587f597d368
|
||||
Subproject commit 7d04c488496c873f392c5a068f72a6c75df40f70
|
||||
Loading…
Reference in New Issue