mirror of https://github.com/espruino/BangleApps
Daisy: added heart rate, update steps on each step
parent
8424cc9150
commit
4521873a53
|
@ -1,2 +1,3 @@
|
||||||
0.01: first release
|
0.01: first release
|
||||||
0.02: added settings menu to change color
|
0.02: added settings menu to change color
|
||||||
|
0.03: added heart rate which is switched on when cycled to it through up/down touch on rhs
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Daisy
|
# Daisy 
|
||||||
|
|
||||||
*A beautiful digital clock with large ring guage, idle timer and a
|
*A beautiful digital clock with large ring guage, idle timer and a
|
||||||
cyclic information line that includes, day, date, steps, battery,
|
cyclic information line that includes, day, date, steps, battery,
|
||||||
|
@ -8,15 +8,20 @@ Written by: [Hugh Barney](https://github.com/hughbarney) For support
|
||||||
and discussion please post in the [Bangle JS
|
and discussion please post in the [Bangle JS
|
||||||
Forum](http://forum.espruino.com/microcosms/1424/)
|
Forum](http://forum.espruino.com/microcosms/1424/)
|
||||||
|
|
||||||
* Derived from `The Ring` proof of concept and the [Pastel clock](https://banglejs.com/apps/?q=pastel)
|
* Derived from [The Ring](https://banglejs.com/apps/?id=thering) proof of concept and the [Pastel clock](https://banglejs.com/apps/?q=pastel)
|
||||||
* Includes the [Lazybones](https://banglejs.com/apps/?q=lazybones) Idle warning timer
|
* Includes the [Lazybones](https://banglejs.com/apps/?q=lazybones) Idle warning timer
|
||||||
* Touch the top right/top left to cycle through the info display (Day, Date, Steps, Sunrise, Sunset)
|
* Touch the top right/top left to cycle through the info display (Day, Date, Steps, Sunrise, Sunset, Heart Rate)
|
||||||
|
* The heart rate monitor is turned on only when Heart rate is selected and will take a few seconds to settle
|
||||||
|
* The heart value is displayed in RED if the confidence value is less than 50%
|
||||||
|
* NOTE: The heart rate monitor of Bangle JS 2 is not very accurate when moving about.
|
||||||
|
See [#1248](https://github.com/espruino/BangleApps/issues/1248)
|
||||||
* Uses mylocation.json from MyLocation app to calculate sunrise and sunset times for your location
|
* Uses mylocation.json from MyLocation app to calculate sunrise and sunset times for your location
|
||||||
|
* If your Sunrise, Sunset times look odd make sure you have setup your location using
|
||||||
|
[MyLocation](https://banglejs.com/apps/?id=mylocation)
|
||||||
* The screen is updated every minute to save battery power
|
* The screen is updated every minute to save battery power
|
||||||
* Uses the [BloggerSansLight](https://www.1001fonts.com/rounded-fonts.html?page=3) font, which if free for commercial use
|
* Uses the [BloggerSansLight](https://www.1001fonts.com/rounded-fonts.html?page=3) font, which if free for commercial use
|
||||||
|
|
||||||
## Future Development
|
## Future Development
|
||||||
* Add a heart rate option in the information line that turns on when selected
|
|
||||||
* Use mini icons in the information line rather that text
|
* Use mini icons in the information line rather that text
|
||||||
* Add weather icons as per Pastel clock
|
* Add weather icons as per Pastel clock
|
||||||
* Add a lock icon to the screen
|
* Add a lock icon to the screen
|
||||||
|
@ -25,5 +30,3 @@ Forum](http://forum.espruino.com/microcosms/1424/)
|
||||||

|

|
||||||
|
|
||||||
It is worth looking at the real thing though as the screenshot does not do it justice.
|
It is worth looking at the real thing though as the screenshot does not do it justice.
|
||||||
(Though I need to redo this photo at some point)
|
|
||||||

|
|
||||||
|
|
|
@ -14,16 +14,19 @@ let warned = 0;
|
||||||
let idle = false;
|
let idle = false;
|
||||||
let IDLE_MINUTES = 26;
|
let IDLE_MINUTES = 26;
|
||||||
|
|
||||||
var pal1; // palette for 0-40%
|
let pal1; // palette for 0-40%
|
||||||
var pal2; // palette for 50-100%
|
let pal2; // palette for 50-100%
|
||||||
const infoWidth = 50;
|
const infoLine = (3*h/4) - 6;
|
||||||
const infoHeight = 14;
|
const infoWidth = 56;
|
||||||
|
const infoHeight = 11;
|
||||||
var drawingSteps = false;
|
var drawingSteps = false;
|
||||||
|
|
||||||
function log_debug(o) {
|
function log_debug(o) {
|
||||||
//print(o);
|
//print(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var hrmImg = require("heatshrink").decompress(atob("i0WgIKHgPh8Ef5/g///44CBz///1///5A4PnBQk///wA4PBA4MDA4MH/+Ah/8gEP4EAjw0GA"));
|
||||||
|
|
||||||
// https://www.1001fonts.com/rounded-fonts.html?page=3
|
// https://www.1001fonts.com/rounded-fonts.html?page=3
|
||||||
Graphics.prototype.setFontBloggerSansLight46 = function(scale) {
|
Graphics.prototype.setFontBloggerSansLight46 = function(scale) {
|
||||||
// Actual height 46 (45 - 0)
|
// Actual height 46 (45 - 0)
|
||||||
|
@ -109,7 +112,8 @@ const infoData = {
|
||||||
ID_SR: { calc: () => 'Sunrise: ' + sunRise },
|
ID_SR: { calc: () => 'Sunrise: ' + sunRise },
|
||||||
ID_SS: { calc: () => 'Sunset: ' + sunSet },
|
ID_SS: { calc: () => 'Sunset: ' + sunSet },
|
||||||
ID_STEP: { calc: () => 'Steps: ' + getSteps() },
|
ID_STEP: { calc: () => 'Steps: ' + getSteps() },
|
||||||
ID_BATT: { calc: () => 'Battery: ' + E.getBattery() + '%' }
|
ID_BATT: { calc: () => 'Battery: ' + E.getBattery() + '%' },
|
||||||
|
ID_HRM: { calc: () => hrmCurrent }
|
||||||
};
|
};
|
||||||
|
|
||||||
const infoList = Object.keys(infoData).sort();
|
const infoList = Object.keys(infoData).sort();
|
||||||
|
@ -121,6 +125,9 @@ function nextInfo() {
|
||||||
if (idx === infoList.length - 1) infoMode = infoList[0];
|
if (idx === infoList.length - 1) infoMode = infoList[0];
|
||||||
else infoMode = infoList[idx + 1];
|
else infoMode = infoList[idx + 1];
|
||||||
}
|
}
|
||||||
|
// power HRM on/off accordingly
|
||||||
|
Bangle.setHRMPower(infoMode == "ID_HRM" ? 1 : 0);
|
||||||
|
resetHrm();
|
||||||
}
|
}
|
||||||
|
|
||||||
function prevInfo() {
|
function prevInfo() {
|
||||||
|
@ -129,8 +136,125 @@ function prevInfo() {
|
||||||
if (idx === 0) infoMode = infoList[infoList.length - 1];
|
if (idx === 0) infoMode = infoList[infoList.length - 1];
|
||||||
else infoMode = infoList[idx - 1];
|
else infoMode = infoList[idx - 1];
|
||||||
}
|
}
|
||||||
|
// power HRM on/off accordingly
|
||||||
|
Bangle.setHRMPower(infoMode == "ID_HRM" ? 1 : 0);
|
||||||
|
resetHrm();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clearInfo() {
|
||||||
|
g.setColor(g.theme.bg);
|
||||||
|
//g.setColor(g.theme.fg);
|
||||||
|
g.fillRect((w/2) - infoWidth, infoLine - infoHeight, (w/2) + infoWidth, infoLine + infoHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawInfo() {
|
||||||
|
clearInfo();
|
||||||
|
g.setColor(g.theme.fg);
|
||||||
|
setSmallFont();
|
||||||
|
g.setFontAlign(0,0);
|
||||||
|
|
||||||
|
if (infoMode == "ID_HRM") {
|
||||||
|
clearInfo();
|
||||||
|
g.setColor('#f00'); // red
|
||||||
|
drawHeartIcon();
|
||||||
|
} else {
|
||||||
|
g.drawString((infoData[infoMode].calc()), w/2, infoLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawHeartIcon() {
|
||||||
|
g.drawImage(hrmImg, (w/2) - infoHeight - 20, infoLine - infoHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawHrm() {
|
||||||
|
if (idle) return; // dont draw while prompting
|
||||||
|
var d = new Date();
|
||||||
|
clearInfo();
|
||||||
|
g.setColor(d.getSeconds()&1 ? '#f00' : g.theme.bg);
|
||||||
|
drawHeartIcon();
|
||||||
|
setSmallFont();
|
||||||
|
g.setFontAlign(-1,0); // left
|
||||||
|
g.setColor(hrmConfidence >= 50 ? g.theme.fg : '#f00');
|
||||||
|
g.drawString(hrmCurrent, (w/2) + 10, infoLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
if (!idle)
|
||||||
|
drawClock();
|
||||||
|
else
|
||||||
|
drawIdle();
|
||||||
|
queueDraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawClock() {
|
||||||
|
var date = new Date();
|
||||||
|
var timeStr = require("locale").time(date,1);
|
||||||
|
var da = date.toString().split(" ");
|
||||||
|
var time = da[4].substr(0,5);
|
||||||
|
var hh = da[4].substr(0,2);
|
||||||
|
var mm = da[4].substr(3,2);
|
||||||
|
var steps = getSteps();
|
||||||
|
var p_steps = Math.round(100*(steps/10000));
|
||||||
|
|
||||||
|
g.reset();
|
||||||
|
g.setColor(g.theme.bg);
|
||||||
|
g.fillRect(0, 0, w, h);
|
||||||
|
g.drawImage(getGaugeImage(p_steps), 0, 0);
|
||||||
|
setLargeFont();
|
||||||
|
|
||||||
|
g.setColor(settings.fg);
|
||||||
|
g.setFontAlign(1,0); // right aligned
|
||||||
|
g.drawString(hh, (w/2) - 1, h/2);
|
||||||
|
|
||||||
|
g.setColor(g.theme.fg);
|
||||||
|
g.setFontAlign(-1,0); // left aligned
|
||||||
|
g.drawString(mm, (w/2) + 1, h/2);
|
||||||
|
|
||||||
|
drawInfo();
|
||||||
|
|
||||||
|
// recalc sunrise / sunset every hour
|
||||||
|
if (drawCount % 60 == 0)
|
||||||
|
updateSunRiseSunSet(new Date(), location.lat, location.lon);
|
||||||
|
drawCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawSteps() {
|
||||||
|
if (drawingSteps) return;
|
||||||
|
drawingSteps = true;
|
||||||
|
clearInfo();
|
||||||
|
setSmallFont();
|
||||||
|
g.setFontAlign(0,0);
|
||||||
|
g.setColor(g.theme.fg);
|
||||||
|
g.drawString('Steps ' + getSteps(), w/2, (3*h/4) - 4);
|
||||||
|
drawingSteps = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////// GAUGE images /////////////////////////////////////
|
||||||
|
|
||||||
|
var hrmCurrent = "--";
|
||||||
|
var hrmConfidence = 0;
|
||||||
|
|
||||||
|
function resetHrm() {
|
||||||
|
hrmCurrent = "--";
|
||||||
|
hrmConfidence = 0;
|
||||||
|
if (infoMode == "ID_HRM") {
|
||||||
|
clearInfo();
|
||||||
|
g.setColor('#f00'); // red
|
||||||
|
drawHeartIcon();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Bangle.on('HRM', function(hrm) {
|
||||||
|
hrmCurrent = hrm.bpm;
|
||||||
|
hrmConfidence = hrm.confidence;
|
||||||
|
log_debug("HRM=" + hrm.bpm + " (" + hrm.confidence + ")");
|
||||||
|
if (infoMode == "ID_HRM" ) drawHrm();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
///////////////// GAUGE images /////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
// putting into 1 function like this, rather than individual variables
|
// putting into 1 function like this, rather than individual variables
|
||||||
// reduces ram usage from 70%-13%
|
// reduces ram usage from 70%-13%
|
||||||
function getGaugeImage(p) {
|
function getGaugeImage(p) {
|
||||||
|
@ -247,68 +371,6 @@ function getGaugeImage(p) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function draw() {
|
|
||||||
if (!idle)
|
|
||||||
drawClock();
|
|
||||||
else
|
|
||||||
drawIdle();
|
|
||||||
queueDraw();
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawClock() {
|
|
||||||
var date = new Date();
|
|
||||||
var timeStr = require("locale").time(date,1);
|
|
||||||
var da = date.toString().split(" ");
|
|
||||||
var time = da[4].substr(0,5);
|
|
||||||
var hh = da[4].substr(0,2);
|
|
||||||
var mm = da[4].substr(3,2);
|
|
||||||
var steps = getSteps();
|
|
||||||
var p_steps = Math.round(100*(steps/10000));
|
|
||||||
|
|
||||||
g.reset();
|
|
||||||
g.setColor(g.theme.bg);
|
|
||||||
g.fillRect(0, 0, w, h);
|
|
||||||
g.drawImage(getGaugeImage(p_steps), 0, 0);
|
|
||||||
setLargeFont();
|
|
||||||
|
|
||||||
g.setColor(settings.fg);
|
|
||||||
g.setFontAlign(1,0); // right aligned
|
|
||||||
g.drawString(hh, (w/2) - 1, h/2);
|
|
||||||
|
|
||||||
g.setColor(g.theme.fg);
|
|
||||||
g.setFontAlign(-1,0); // left aligned
|
|
||||||
g.drawString(mm, (w/2) + 1, h/2);
|
|
||||||
|
|
||||||
setSmallFont();
|
|
||||||
g.setFontAlign(0,0); // left aligned
|
|
||||||
g.drawString((infoData[infoMode].calc()), w/2, (3*h/4) - 4);
|
|
||||||
|
|
||||||
// recalc sunrise / sunset every hour
|
|
||||||
if (drawCount % 60 == 0)
|
|
||||||
updateSunRiseSunSet(new Date(), location.lat, location.lon);
|
|
||||||
drawCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawSteps() {
|
|
||||||
if (drawingSteps) return;
|
|
||||||
drawingSteps = true;
|
|
||||||
setSmallFont();
|
|
||||||
g.setFontAlign(0,0);
|
|
||||||
var steps = getSteps();
|
|
||||||
g.setColor(g.theme.bg);
|
|
||||||
g.fillRect((w/2) - infoWidth, (3*h/4) - infoHeight, (w/2) + infoWidth, (3*h/4) + infoHeight);
|
|
||||||
g.setColor(g.theme.fg);
|
|
||||||
g.drawString('Steps ' + steps, w/2, (3*h/4) - 4);
|
|
||||||
drawingSteps = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Bangle.on('step', s => {
|
|
||||||
drawSteps();
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
///////////////// IDLE TIMER /////////////////////////////////////
|
///////////////// IDLE TIMER /////////////////////////////////////
|
||||||
|
|
||||||
function drawIdle() {
|
function drawIdle() {
|
||||||
|
@ -392,6 +454,8 @@ Bangle.on('step', s => {
|
||||||
}
|
}
|
||||||
idle = false;
|
idle = false;
|
||||||
warned = 0;
|
warned = 0;
|
||||||
|
|
||||||
|
if (infoMode == "ID_STEP") drawSteps();
|
||||||
});
|
});
|
||||||
|
|
||||||
function checkIdle() {
|
function checkIdle() {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{ "id": "daisy",
|
{ "id": "daisy",
|
||||||
"name": "Daisy",
|
"name": "Daisy",
|
||||||
"version":"0.02",
|
"version":"0.03",
|
||||||
"dependencies": {"mylocation":"app"},
|
"dependencies": {"mylocation":"app"},
|
||||||
"description": "A clock based on the Pastel clock with large ring guage for steps",
|
"description": "A clock based on the Pastel clock with large ring guage for steps",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
|
|
Loading…
Reference in New Issue