BangleApps/apps/smartbatt/module.js

137 lines
3.9 KiB
JavaScript

{
var dataFile = "smartbattdata.json";
var interval;
var storage = require("Storage");
var logFile = "smartbattlog.json";
function getSettings() {
return Object.assign({
//Record Interval stored in ms
doLogging: false
}, require('Storage').readJSON("smartbatt.settings.json", true) || {});
}
function logBatterySample(entry) {
let log = storage.readJSON(logFile, 1) || [];
//get human-readable time
let d = new Date();
entry.time = d.getFullYear() + "-" +
("0" + (d.getMonth() + 1)).slice(-2) + "-" +
("0" + d.getDate()).slice(-2) + " " +
("0" + d.getHours()).slice(-2) + ":" +
("0" + d.getMinutes()).slice(-2) + ":" +
("0" + d.getSeconds()).slice(-2);
log.push(entry);
if (log.length > 100) log = log.slice(-100);
storage.writeJSON(logFile, log);
}
// Record current battery reading into current average
function recordBattery() {
let now = Date.now();
let data = getData();
let batt = E.getBattery();
let battChange = data.battLastRecorded - batt;
let deltaHours = (now - data.timeLastRecorded) / (1000 * 60 * 60);
// Default reason (in case we skip)
let reason = "Recorded";
if (battChange <= 0) {
reason = "Skipped: battery fluctuated or no change";
if (Math.abs(battChange) < 5) {
//less than 6% difference, average percents
var newBatt = (batt + data.battLastRecorded) / 2;
data.battLastRecorded = newBatt;
} else {
//probably charged, ignore average
data.battLastRecorded = batt;
}
storage.writeJSON(dataFile, data);
} else if (deltaHours <= 0 || !isFinite(deltaHours)) {
reason = "Skipped: invalid time delta";
data.timeLastRecorded = now;
data.battLastRecorded = batt;
storage.writeJSON(dataFile, data);
} else {
let weightCoefficient = 1;
let currentDrainage = battChange / deltaHours;
let newAvg = weightedAverage(data.avgBattDrainage, data.totalHours, currentDrainage, deltaHours * weightCoefficient);
data.avgBattDrainage = newAvg;
data.timeLastRecorded = now;
data.totalCycles += 1;
data.totalHours += deltaHours;
data.battLastRecorded = batt;
storage.writeJSON(dataFile, data);
reason = "Drainage recorded: " + currentDrainage.toFixed(3) + "%/hr";
}
if (getSettings().doLogging) {
// Always log the sample
logBatterySample({
battNow: batt,
battLast: data.battLastRecorded,
battChange: battChange,
deltaHours: deltaHours,
timeLastRecorded: data.timeLastRecorded,
avgDrainage: data.avgBattDrainage,
reason: reason
});
}
}
function weightedAverage(oldValue, oldWeight, newValue, newWeight) {
return (oldValue * oldWeight + newValue * newWeight) / (oldWeight + newWeight);
}
function getData() {
return storage.readJSON(dataFile, 1) || {
avgBattDrainage: 0,
battLastRecorded: E.getBattery(),
timeLastRecorded: Date.now(),
totalCycles: 0,
totalHours: 0,
};
}
// Estimate hours remaining
function estimateBatteryLife() {
let data = getData();
var batt = E.getBattery();
var hrsLeft = Math.abs(batt / data.avgBattDrainage);
return {
batt: batt,
hrsLeft: hrsLeft,
avgDrainage:data.avgBattDrainage,
totalHours:data.totalHours,
cycles:data.totalCycles
};
}
function deleteData() {
storage.erase(dataFile);
storage.erase(logFile);
}
// Expose public API
exports.record = recordBattery;
exports.deleteData = deleteData;
exports.get = estimateBatteryLife;
exports.changeInterval = function (newInterval) {
clearInterval(interval);
interval = setInterval(recordBattery, newInterval);
};
// Start recording every 5 minutes
interval = setInterval(recordBattery, 600000);
recordBattery(); // Log immediately
}