BangleApps/apps/activepedom/widget.js

242 lines
8.5 KiB
JavaScript
Raw Normal View History

2020-04-13 11:04:18 +00:00
(() => {
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
2024-03-04 20:34:50 +00:00
//var timerStoreData = 0; //timer to store data
2020-04-13 11:04:18 +00:00
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
2020-04-16 06:21:27 +00:00
var lastUpdate = new Date(); //used to reset counted steps on new day
2020-04-20 15:34:33 +00:00
var width = 46; //width of widget
2020-04-13 11:04:18 +00:00
2020-04-16 06:21:27 +00:00
//used for statistics and debugging
var stepsTooShort = 0;
2020-04-13 11:04:18 +00:00
var stepsTooLong = 0;
var stepsOutsideTime = 0;
2020-04-16 06:21:27 +00:00
var distance = 0; //distance travelled
2020-04-20 15:34:33 +00:00
const s = require('Storage');
2020-04-13 11:04:18 +00:00
const SETTINGS_FILE = 'activepedom.settings.json';
const PEDOMFILE = "activepedom.steps.json";
2020-04-20 15:34:33 +00:00
var dataFile;
var storeDataInterval = 5*60*1000; //ms
2020-04-13 11:04:18 +00:00
let settings;
//load settings
2020-04-13 11:04:18 +00:00
function loadSettings() {
2020-04-20 15:34:33 +00:00
settings = s.readJSON(SETTINGS_FILE, 1) || {};
}
function storeData() {
2024-03-13 10:51:40 +00:00
let now = new Date();
let month = now.getMonth() + 1; //month is 0-based
2020-04-27 08:35:11 +00:00
if (month < 10) month = "0" + month; //leading 0
2024-03-13 10:51:40 +00:00
let filename = "activepedom" + now.getFullYear() + month + now.getDate() + ".data"; //new file for each day
2020-04-20 15:34:33 +00:00
dataFile = s.open(filename,"a");
2020-04-27 08:35:11 +00:00
if (dataFile) { //check if filen already exists
2020-04-21 14:04:56 +00:00
if (dataFile.getLength() == 0) {
2020-04-27 08:35:11 +00:00
//new day, set steps to 0
stepsCounted = 0;
stepsTooShort = 0;
stepsTooLong = 0;
stepsOutsideTime = 0;
2020-04-21 14:04:56 +00:00
}
dataFile.write([
now.getTime(),
2020-04-27 08:35:11 +00:00
stepsCounted,
2020-04-21 14:04:56 +00:00
active,
stepsTooShort,
stepsTooLong,
stepsOutsideTime,
].join(",")+"\n");
}
2020-04-27 08:35:11 +00:00
dataFile = undefined; //save memory
2020-04-13 11:04:18 +00:00
}
2020-04-16 06:21:27 +00:00
2020-04-13 11:04:18 +00:00
//return setting
function setting(key) {
2020-04-16 06:21:27 +00:00
//define default settings
const DEFAULTS = {
'cMaxTime' : 1100,
'cMinTime' : 240,
'stepThreshold' : 30,
'intervalResetActive' : 30000,
'stepSensitivity' : 80,
'stepGoal' : 10000,
'stepLength' : 75,
'lineOne' : "Distance",
'lineTwo' : "Steps",
2020-04-16 06:21:27 +00:00
};
if (!settings) { loadSettings(); }
return (key in settings) ? settings[key] : DEFAULTS[key];
2020-04-13 11:04:18 +00:00
}
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
2020-04-16 06:21:27 +00:00
function kFormatterSteps(num) {
2020-04-13 11:04:18 +00:00
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
2020-04-20 15:34:33 +00:00
stepsTooLong++; //count steps which are not counted, because time too long
2020-04-13 11:04:18 +00:00
steps--;
}
//Remove step if time between first and second step is too short
if (stepTimeDiff <= setting('cMinTime')) { //milliseconds
2020-04-20 15:34:33 +00:00
stepsTooShort++; //count steps which are not counted, because time too short
2020-04-13 11:04:18 +00:00
steps--;
}
2020-04-20 15:34:33 +00:00
//Step threshold reached
2020-04-13 11:04:18 +00:00
if (steps >= setting('stepThreshold')) {
if (active == 0) {
2020-04-13 12:57:47 +00:00
stepsCounted = stepsCounted + (setting('stepThreshold') -1) ; //count steps needed to reach active status, last step is counted anyway, so treshold -1
2020-04-20 15:34:33 +00:00
stepsOutsideTime = stepsOutsideTime - 10; //substract steps needed to reach active status
2020-04-13 11:04:18 +00:00
}
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++;
}
2020-04-16 06:21:27 +00:00
settings = 0; //reset settings to save memory
2020-04-13 11:04:18 +00:00
}
function draw() {
var height = 23; //width is deined globally
2020-04-20 15:34:33 +00:00
distance = (stepsCounted * setting('stepLength')) / 100 /1000; //distance in km
2020-04-13 11:04:18 +00:00
//Check if same day
let date = new Date();
if (lastUpdate.getDate() == date.getDate()){ //if same day
}
2020-04-20 15:34:33 +00:00
else { //different day, set all steps to 0
stepsCounted = 0;
stepsTooShort = 0;
stepsTooLong = 0;
stepsOutsideTime = 0;
2020-04-13 11:04:18 +00:00
}
lastUpdate = date;
// not everyone likes a widget, having refreshed lastUpdate we can exit
if (setting('lineOne') == 'Hide' && setting('lineTwo') == 'Hide') {
settings = 0; //reset settings to save memory
return;
}
2020-04-13 11:04:18 +00:00
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);
2020-04-16 06:21:27 +00:00
if (setting('lineOne') == 'Steps') {
g.drawString(kFormatterSteps(stepsCounted),this.x+1,this.y); //first line, big number, steps
}
if (setting('lineOne') == 'Distance') {
g.drawString(distance.toFixed(2),this.x+1,this.y); //first line, big number, distance
}
2020-04-13 11:04:18 +00:00
g.setFont("6x8", 1);
g.setColor(0xFFFF); //white
2020-04-16 06:21:27 +00:00
if (setting('lineTwo') == 'Steps') {
g.drawString(stepsCounted,this.x+1,this.y+14); //second line, small number, steps
}
if (setting('lineTwo') == 'Distance') {
g.drawString(distance.toFixed(3) + "km",this.x+1,this.y+14); //second line, small number, distance
}
2020-04-13 11:04:18 +00:00
//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
2020-04-16 06:21:27 +00:00
settings = 0; //reset settings to save memory
2020-04-13 11:04:18 +00:00
}
//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
};
2020-04-20 15:34:33 +00:00
s.write(PEDOMFILE,d); //write array to file
2020-04-13 11:04:18 +00:00
});
//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
{ // new scope ensures pedomData gets freed
let pedomData = s.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;
}
2020-04-13 11:04:18 +00:00
}
2020-04-16 06:21:27 +00:00
2020-04-13 11:04:18 +00:00
setStepSensitivity(setting('stepSensitivity')); //set step sensitivity (80 is standard, 400 is muss less sensitive)
2024-03-04 20:34:50 +00:00
/*timerStoreData =*/ setInterval(storeData, storeDataInterval); //store data regularly
2021-01-06 23:16:57 +00:00
//Add widget, use: WIDGETS.activepedom.getSteps() inside another App to return todays step count
WIDGETS["activepedom"]={area:"tl",width:width,draw:draw, getSteps:()=>stepsCounted};
})();