Merge pull request #290 from Purple-Tentacle/master

Active Pedometer Widget
pull/293/head^2
Gordon Williams 2020-04-14 10:45:20 +01:00 committed by GitHub
commit 50a149d580
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 315 additions and 0 deletions

View File

@ -1108,6 +1108,20 @@
{"name":"openstmap.app.js","url":"app.js"},
{"name":"openstmap.img","url":"app-icon.js","evaluate":true}
]
},
{ "id": "activepedom",
"name": "Active Pedometer",
"shortName":"Active Pedometer",
"icon": "app.png",
"version":"0.01",
"description": "Pedometer that filters out arm movement and displays a step goal progress.",
"tags": "outdoors,widget",
"type":"widget",
"storage": [
{"name":"activepedom.wid.js","url":"widget.js"},
{"name":"activepedom.settings.js","url":"settings.js"},
{"name":"activepedom.img","url":"app-icon.js","evaluate":true}
]
},
{ "id": "tabata",
"name": "Tabata",

BIN
apps/activepedom/10600.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 B

BIN
apps/activepedom/1600.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 B

BIN
apps/activepedom/600.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

View File

@ -0,0 +1 @@
0.01: New Widget!

View File

@ -0,0 +1,38 @@
# Improved pedometer
Pedometer that filters out arm movement and displays a step goal progress.
I changed the step counting algorithm completely.
Now every step is counted when in status 'active', if the time difference between two steps is not too short or too long.
To get in 'active' mode, you have to reach the step threshold before the active timer runs out.
When you reach the step threshold, the steps needed to reach the threshold are counted as well.
## Screenshots
* 600 steps
![](600.png)
* 1600 steps
![](1600.png)
* 10600 steps
![](10600.png)
## Features
* Two line display
* Large number for good readability
* Small number with the exact steps counted
* Large number is displayed in green when status is 'active'
* Progress bar for step goal
* Counts steps only if they are reached in a certain time
* Filters out steps where time between two steps is too long or too short
* Step detection sensitivity from firmware can be configured
* Steps are saved to a file and read-in at start (to not lose step progress)
* Settings can be changed in Settings - App/widget settings - Active Pedometer
## Development version
* https://github.com/Purple-Tentacle/BangleAppsDev/tree/master/apps/pedometer
## Requests
If you have any feature requests, please post in this forum thread: http://forum.espruino.com/conversations/345754/

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwIGDvAEDgP+ApMD/4FVEZY1FABcP8AFDn/wAod/AocB//4AoUHAokPAokf5/8AocfAoc+j5HDvgFEvEf7+AAoP4AoJCC+E/54qCsE/wYkDn+AAos8AohZDj/AAohrEp4FEs5xEuJfDgF5Aon4GgYFBGgZOBnyJD+EeYgfgj4FEh6VD4AFDh+AAIJMCBoIFFLQQtBgYFCHIIFDjA3BC4I="))

BIN
apps/activepedom/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 836 B

View File

@ -0,0 +1,81 @@
// This file should contain exactly one function, which shows the app's settings
/**
* @param {function} back Use back() to return to settings menu
*/
(function(back) {
const SETTINGS_FILE = 'activepedom.settings.json';
// initialize with default settings...
let s = {
'cMaxTime' : 1100,
'cMinTime' : 240,
'stepThreshold' : 30,
'intervalResetActive' : 30000,
'stepSensitivity' : 80,
'stepGoal' : 10000,
};
// ...and overwrite them with any saved values
// This way saved values are preserved if a new version adds more settings
const storage = require('Storage');
const saved = storage.readJSON(SETTINGS_FILE, 1) || {};
for (const key in saved) {
s[key] = saved[key];
}
// creates a function to safe a specific setting, e.g. save('color')(1)
function save(key) {
return function (value) {
s[key] = value;
storage.write(SETTINGS_FILE, s);
WIDGETS["activepedom"].draw();
};
}
const menu = {
'': { 'title': 'Active Pedometer' },
'< Back': back,
'Max time (ms)': {
value: s.cMaxTime,
min: 0,
max: 10000,
step: 100,
onchange: save('cMaxTime'),
},
'Min time (ms)': {
value: s.cMinTime,
min: 0,
max: 500,
step: 10,
onchange: save('cMinTime'),
},
'Step threshold': {
value: s.stepThreshold,
min: 0,
max: 100,
step: 1,
onchange: save('stepThreshold'),
},
'Act.Res. (ms)': {
value: s.intervalResetActive,
min: 100,
max: 100000,
step: 1000,
onchange: save('intervalResetActive'),
},
'Step sens.': {
value: s.stepSensitivity,
min: 0,
max: 1000,
step: 10,
onchange: save('stepSensitivity'),
},
'Step goal': {
value: s.stepGoal,
min: 1000,
max: 100000,
step: 1000,
onchange: save('stepGoal'),
},
};
E.showMenu(menu);
});

180
apps/activepedom/widget.js Normal file
View File

@ -0,0 +1,180 @@
(() => {
var stepTimeDiff = 9999; //Time difference between two steps
var startTimeStep = new Date(); //set start time
var stopTimeStep = 0; //Time after one step
var timerResetActive = 0; //timer to reset active
var steps = 0; //steps taken
var stepsCounted = 0; //active steps counted
var active = 0; //x steps in y seconds achieved
var stepGoalPercent = 0; //percentage of step goal
var stepGoalBarLength = 0; //length og progress bar
var lastUpdate = new Date();
var width = 45;
var stepsTooShort = 0;
var stepsTooLong = 0;
var stepsOutsideTime = 0;
//define default settings
const DEFAULTS = {
'cMaxTime' : 1100,
'cMinTime' : 240,
'stepThreshold' : 30,
'intervalResetActive' : 30000,
'stepSensitivity' : 80,
'stepGoal' : 10000,
};
const SETTINGS_FILE = 'activepedom.settings.json';
const PEDOMFILE = "activepedom.steps.json";
let settings;
//load settings
function loadSettings() {
settings = require('Storage').readJSON(SETTINGS_FILE, 1) || {};
}
//return setting
function setting(key) {
if (!settings) { loadSettings(); }
return (key in settings) ? settings[key] : DEFAULTS[key];
}
function setStepSensitivity(s) {
function sqr(x) { return x*x; }
var X=sqr(8192-s);
var Y=sqr(8192+s);
Bangle.setOptions({stepCounterThresholdLow:X,stepCounterThresholdHigh:Y});
}
//format number to make them shorter
function kFormatter(num) {
if (num <= 999) return num; //smaller 1.000, return 600 as 600
if (num >= 1000 && num < 10000) { //between 1.000 and 10.000
num = Math.floor(num/100)*100;
return (num / 1000).toFixed(1).replace(/\.0$/, '') + 'k'; //return 1600 as 1.6k
}
if (num >= 10000) { //greater 10.000
num = Math.floor(num/1000)*1000;
return (num / 1000).toFixed(1).replace(/\.0$/, '') + 'k'; //return 10.600 as 10k
}
}
//Set Active to 0
function resetActive() {
active = 0;
steps = 0;
if (Bangle.isLCDOn()) WIDGETS["activepedom"].draw();
}
function calcSteps() {
stopTimeStep = new Date(); //stop time after each step
stepTimeDiff = stopTimeStep - startTimeStep; //time between steps in milliseconds
startTimeStep = new Date(); //start time again
//Remove step if time between first and second step is too long
if (stepTimeDiff >= setting('cMaxTime')) { //milliseconds
stepsTooLong++; //count steps which are note counted, because time too long
steps--;
}
//Remove step if time between first and second step is too short
if (stepTimeDiff <= setting('cMinTime')) { //milliseconds
stepsTooShort++; //count steps which are note counted, because time too short
steps--;
}
if (steps >= setting('stepThreshold')) {
if (active == 0) {
stepsCounted = stepsCounted + (setting('stepThreshold') -1) ; //count steps needed to reach active status, last step is counted anyway, so treshold -1
stepsOutsideTime = stepsOutsideTime - 10; //substract steps needed to reac active status
}
active = 1;
clearInterval(timerResetActive); //stop timer which resets active
timerResetActive = setInterval(resetActive, setting('intervalResetActive')); //reset active after timer runs out
steps = 0;
}
if (active == 1) {
stepsCounted++; //count steps
}
else {
stepsOutsideTime++;
}
}
function draw() {
var height = 23; //width is deined globally
var stepsDisplayLarge = kFormatter(stepsCounted);
//Check if same day
let date = new Date();
if (lastUpdate.getDate() == date.getDate()){ //if same day
}
else {
stepsCounted = 1; //set stepcount to 1
}
lastUpdate = date;
g.reset();
g.clearRect(this.x, this.y, this.x+width, this.y+height);
//draw numbers
if (active == 1) g.setColor(0x07E0); //green
else g.setColor(0xFFFF); //white
g.setFont("6x8", 2);
g.drawString(stepsDisplayLarge,this.x+1,this.y); //first line, big number
g.setFont("6x8", 1);
g.setColor(0xFFFF); //white
g.drawString(stepsCounted,this.x+1,this.y+14); //second line, small number
//draw step goal bar
stepGoalPercent = (stepsCounted / setting('stepGoal')) * 100;
stepGoalBarLength = width / 100 * stepGoalPercent;
if (stepGoalBarLength > width) stepGoalBarLength = width; //do not draw across width of widget
g.setColor(0x7BEF); //grey
g.fillRect(this.x, this.y+height, this.x+width, this.y+height); // draw background bar
g.setColor(0xFFFF); //white
g.fillRect(this.x, this.y+height, this.x+1, this.y+height-1); //draw start of bar
g.fillRect(this.x+width, this.y+height, this.x+width-1, this.y+height-1); //draw end of bar
g.fillRect(this.x, this.y+height, this.x+stepGoalBarLength, this.y+height); // draw progress bar
}
//This event is called just before the device shuts down for commands such as reset(), load(), save(), E.reboot() or Bangle.off()
E.on('kill', () => {
let d = { //define array to write to file
lastUpdate : lastUpdate.toISOString(),
stepsToday : stepsCounted,
stepsTooShort : stepsTooShort,
stepsTooLong : stepsTooLong,
stepsOutsideTime : stepsOutsideTime
};
require("Storage").write(PEDOMFILE,d); //write array to file
});
//When Step is registered by firmware
Bangle.on('step', (up) => {
steps++; //increase step count
calcSteps();
if (Bangle.isLCDOn()) WIDGETS["activepedom"].draw();
});
// redraw when the LCD turns on
Bangle.on('lcdPower', function(on) {
if (on) WIDGETS["activepedom"].draw();
});
//Read data from file and set variables
let pedomData = require("Storage").readJSON(PEDOMFILE,1);
if (pedomData) {
if (pedomData.lastUpdate) lastUpdate = new Date(pedomData.lastUpdate);
stepsCounted = pedomData.stepsToday|0;
stepsTooShort = pedomData.stepsTooShort;
stepsTooLong = pedomData.stepsTooLong;
stepsOutsideTime = pedomData.stepsOutsideTime;
}
setStepSensitivity(setting('stepSensitivity')); //set step sensitivity (80 is standard, 400 is muss less sensitive)
//Add widget
WIDGETS["activepedom"]={area:"tl",width:width,draw:draw};
})();