Daisy: added heart rate, update steps on each step

pull/1537/head
hughbarney 2022-03-02 19:21:41 +00:00
parent 8424cc9150
commit 4521873a53
4 changed files with 142 additions and 74 deletions

View File

@ -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

View File

@ -1,4 +1,4 @@
# Daisy # Daisy ![](app.png)
*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/)
![](screenshot_daisy1.png) ![](screenshot_daisy1.png)
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)
![](screenshot_daisy2.jpg)

View File

@ -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() {

View File

@ -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",