BangleApps/apps/widbaroalarm/widget.js

258 lines
7.0 KiB
JavaScript

(function() {
let medianPressure;
let threeHourAvrPressure;
let currentPressures = [];
const LOG_FILE = "widbaroalarm.log.json";
const SETTINGS_FILE = "widbaroalarm.json";
const storage = require('Storage');
let settings;
function loadSettings() {
settings = Object.assign(
storage.readJSON("widbaroalarm.default.json", true) || {},
storage.readJSON(SETTINGS_FILE, true) || {}
);
}
loadSettings();
function setting(key) {
return settings[key];
}
function saveSetting(key, value) {
settings[key] = value;
storage.write(SETTINGS_FILE, settings);
}
const interval = setting("interval");
let history3 = storage.readJSON(LOG_FILE, true) || []; // history of recent 3 hours
function showAlarm(body, key) {
if (body == undefined) return;
E.showPrompt(body, {
title: "Pressure alarm",
buttons: {
"Ok": 1,
"Dismiss": 2,
"Pause": 3
}
}).then(function(v) {
const tsNow = Math.round(Date.now() / 1000); // seconds
if (v == 1) {
saveSetting(key, tsNow);
}
if (v == 2) {
// save timestamp of the future so that we do not warn again for the same event until then
saveSetting(key, tsNow + 60 * setting('dismissDelayMin'));
}
if (v == 3) {
// save timestamp of the future so that we do not warn again for the same event until then
saveSetting(key, tsNow + 60 * setting('pauseDelayMin'));
}
load();
});
if (setting("buzz") &&
!(storage.readJSON('setting.json', 1) || {}).quiet) {
Bangle.buzz();
}
setTimeout(load, 20000);
}
function doWeNeedToWarn(key) {
const tsNow = Math.round(Date.now() / 1000); // seconds
return setting(key) == 0 || setting(key) < tsNow;
}
function checkForAlarms(pressure) {
if (pressure == undefined || pressure <= 0) return;
let alreadyWarned = false;
const ts = Math.round(Date.now() / 1000); // seconds
const d = {
"ts": ts,
"p": pressure
};
// delete entries older than 3h
for (let i = 0; i < history3.length; i++) {
if (history3[i]["ts"] < ts - (3 * 60 * 60)) {
history3.shift();
}
}
// delete oldest entries until we have max 50
while (history3.length > 50) {
history3.shift();
}
if (setting("lowalarm")) {
// Is below the alarm threshold?
if (pressure <= setting("min")) {
if (!doWeNeedToWarn("lastLowWarningTs")) {
showAlarm("Pressure low: " + Math.round(pressure) + " hPa", "lastLowWarningTs");
alreadyWarned = true;
}
} else {
saveSetting("lastLowWarningTs", 0);
}
}
if (setting("highalarm")) {
// Is above the alarm threshold?
if (pressure >= setting("max")) {
if (doWeNeedToWarn("lastHighWarningTs")) {
showAlarm("Pressure high: " + Math.round(pressure) + " hPa", "lastHighWarningTs");
alreadyWarned = true;
}
} else {
saveSetting("lastHighWarningTs", 0);
}
}
if (history3.length > 0 && !alreadyWarned) {
// 3h change detection
const drop3halarm = setting("drop3halarm");
const raise3halarm = setting("raise3halarm");
if (drop3halarm > 0 || raise3halarm > 0) {
// we need at least 30min of data for reliable detection
const diffDateAge = Math.abs(history3[0]["ts"] - ts);
if (diffDateAge < 10 * 60) { // todo change to 1800
return;
}
// Get oldest entry:
const oldestPressure = history3[0]["p"];
if (oldestPressure != undefined && oldestPressure > 0) {
const diffPressure = Math.abs(oldestPressure - pressure);
// drop alarm
if (drop3halarm > 0 && oldestPressure > pressure) {
if (diffPressure > drop3halarm) {
if (doWeNeedToWarn("lastDropWarningTs")) {
showAlarm((Math.round(diffPressure * 10) / 10) + " hPa/3h from " +
Math.round(oldestPressure) + " to " + Math.round(pressure) + " hPa", "lastDropWarningTs");
}
} else {
saveSetting("lastDropWarningTs", 0);
}
} else {
saveSetting("lastDropWarningTs", 0);
}
// raise alarm
if (raise3halarm > 0 && oldestPressure < pressure) {
if (diffPressure > raise3halarm) {
if (doWeNeedToWarn("lastRaiseWarningTs")) {
showAlarm((Math.round(diffPressure * 10) / 10) + " hPa/3h from " +
Math.round(oldestPressure) + " to " + Math.round(pressure) + " hPa", "lastRaiseWarningTs");
}
} else {
saveSetting("lastRaiseWarningTs", 0);
}
} else {
saveSetting("lastRaiseWarningTs", 0);
}
}
}
}
history3.push(d);
// write data to storage
storage.writeJSON(LOG_FILE, history3);
// calculate 3h average for widget
let sum = 0;
for (let i = 0; i < history3.length; i++) {
sum += history3[i]["p"];
}
threeHourAvrPressure = sum / history3.length;
}
/*
turn on barometer power
take multiple measurements
sort the results
take the middle one (median)
turn off barometer power
*/
function check() {
const MEDIANLENGTH = 20;
Bangle.setBarometerPower(true, "widbaroalarm");
Bangle.on('pressure', function(e) {
while (currentPressures.length > MEDIANLENGTH) currentPressures.pop();
currentPressures.unshift(e.pressure);
median = currentPressures.slice().sort();
if (median.length > 10) {
var mid = median.length >> 1;
medianPressure = Math.round(E.sum(median.slice(mid - 4, mid + 5)) / 9);
if (medianPressure > 0) {
turnOff();
checkForAlarms(medianPressure);
}
}
});
setTimeout(function() {
turnOff();
}, 10000);
}
function turnOff() {
if (Bangle.isBarometerOn())
Bangle.setBarometerPower(false, "widbaroalarm");
}
function reload() {
check();
}
function draw() {
if (global.WIDGETS != undefined && typeof global.WIDGETS === "object") {
global.WIDGETS["baroalarm"] = {
width: setting("show") ? 24 : 0,
reload: reload,
area: "tr",
draw: draw
};
}
g.reset();
if (setting("show")) {
g.setFont("6x8", 1).setFontAlign(1, 0);
if (medianPressure == undefined) {
check();
const x = this.x,
y = this.y;
g.drawString("...", x + 24, y + 6);
setTimeout(function() {
g.setFont("6x8", 1).setFontAlign(1, 0);
g.drawString(Math.round(medianPressure), x + 24, y + 6);
}, 10000);
} else {
g.drawString(Math.round(medianPressure), this.x + 24, this.y + 6);
}
if (threeHourAvrPressure != undefined && threeHourAvrPressure > 0) {
g.drawString(Math.round(threeHourAvrPressure), this.x + 24, this.y + 6 + 10);
}
}
}
if (interval > 0) {
setInterval(check, interval * 60000);
}
draw();
})();