added new app
parent
b75c2751c9
commit
6c4d3f41a5
|
|
@ -0,0 +1 @@
|
||||||
|
1.0: first working version of App
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
# The Nightwatch
|
||||||
|
|
||||||
|
Snuggle into your sleeping bag, hang the Bangle on the tent wall
|
||||||
|
and check the screen before you fall asleep:
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
Reads temperature and pressure sensor. Shows current, maximal and minimal values
|
||||||
|
since the start of the app. Also show a graph of the last 20 measures.
|
||||||
|
|
||||||
|
Swipe left/right between values.
|
||||||
|
|
||||||
|
Screen is updated periodically, time step is configurable in settings.
|
||||||
|
|
||||||
|
|
||||||
|
# Creator
|
||||||
|
|
||||||
|
[Niko Komin](https://www.laikaundfreunde.de/niko-komin/)
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"id":"nightwatch",
|
||||||
|
"readme":"README.md",
|
||||||
|
"name":"The Nightwatch",
|
||||||
|
"shortName":"Nightwatch",
|
||||||
|
"supports" : ["BANGLEJS2"],
|
||||||
|
"icon":"nightwatch.icon.png",
|
||||||
|
"screenshots" : [ { "url":"screenshot.png","url":"screenshot2.png" } ],
|
||||||
|
"version":"1.0",
|
||||||
|
"description":"Logs sensor readings (currently T and p), show min/max and graph.",
|
||||||
|
"tags": "tools,outdoors",
|
||||||
|
"storage": [
|
||||||
|
{"name":"nightwatch.app.js","url":"nightwatch.app.js"},
|
||||||
|
{"name":"nightwatch.img","url":"nightwatch.icon.js","evaluate":true}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
require("Storage").write("nightwatch.info",{
|
||||||
|
"id":"nightwatch",
|
||||||
|
"name":"nightwatch",
|
||||||
|
"src":"nightwatch.app.js",
|
||||||
|
"icon":"nightwatch.icon.png"
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,175 @@
|
||||||
|
// PTLOGGER
|
||||||
|
// MEASURES p AND T PERIODICALLY AND UPDATES MIN & MAX VALS
|
||||||
|
// DISPLAYS EITHER OF BOTH
|
||||||
|
|
||||||
|
|
||||||
|
var settings = Object.assign({
|
||||||
|
dt: 5, //time interval in minutes
|
||||||
|
}, require('Storage').readJSON("nightwatch.json", true) || {});
|
||||||
|
|
||||||
|
let dt = settings.dt;
|
||||||
|
delete settings;
|
||||||
|
|
||||||
|
var timerID;
|
||||||
|
|
||||||
|
const highColor = '#35b779';//#6dcd59;
|
||||||
|
const lowColor = '#eb005c';//#3d4a89;//#482878;
|
||||||
|
const normColor = '#000000';
|
||||||
|
const historyAmnt = 24;
|
||||||
|
|
||||||
|
|
||||||
|
const TData = {
|
||||||
|
ondisplay:true,
|
||||||
|
unit: '\xB0C',
|
||||||
|
accuracy: 1,
|
||||||
|
value : 100, t_value:'0:00',
|
||||||
|
values : new Array(historyAmnt),
|
||||||
|
maxval : -100, t_max:'0:00',
|
||||||
|
minval : 100, t_min:'0:00'
|
||||||
|
};
|
||||||
|
|
||||||
|
const PData = {
|
||||||
|
ondisplay:false,
|
||||||
|
unit: 'mbar',
|
||||||
|
accuracy: 0,
|
||||||
|
value : 0, t_value:'0:00',
|
||||||
|
values : new Array(historyAmnt),
|
||||||
|
maxval : 0, t_max:'0:00',
|
||||||
|
minval : 10000, t_min:'0:00'
|
||||||
|
};
|
||||||
|
|
||||||
|
function minMaxString(val,accuracy,unit,time){
|
||||||
|
return time+' '+val.toFixed(accuracy)+unit;
|
||||||
|
// return val.toFixed(accuracy)+unit+'('+time+')';
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateScreen() {
|
||||||
|
// what are we showing right now?
|
||||||
|
let data;
|
||||||
|
if (TData.ondisplay){data = TData;}
|
||||||
|
else {data = PData;}
|
||||||
|
|
||||||
|
// make strings
|
||||||
|
let valueString = data.value.toFixed(data.accuracy)+data.unit;
|
||||||
|
let minString = minMaxString(data.minval, data.accuracy, data.unit, data.t_min);
|
||||||
|
let maxString = minMaxString(data.maxval, data.accuracy, data.unit, data.t_max);
|
||||||
|
|
||||||
|
// LETS PAINT
|
||||||
|
g.clear();
|
||||||
|
g.setFontAlign(0, 0);
|
||||||
|
|
||||||
|
// MINUM AND MAXIMUM VALUES AND TIMES
|
||||||
|
g.setFont("Vector:18");
|
||||||
|
g.setColor(normColor);
|
||||||
|
g.drawString(maxString, g.getWidth() / 2, 11);
|
||||||
|
g.drawString(minString, g.getWidth() / 2, g.getHeight() - 11);
|
||||||
|
|
||||||
|
g.setColor(normColor);
|
||||||
|
|
||||||
|
// TIME OF LAST MEASURE AND SIZE OF INTERVAL
|
||||||
|
g.setFontAlign(-1, 0);
|
||||||
|
g.drawString(data.t_value, 0, g.getHeight()/2 - 25);
|
||||||
|
g.setFontAlign(1, 0);
|
||||||
|
g.drawString('dt='+dt+'min', g.getWidth() , g.getHeight()/2 - 25);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
// GRAPH OF MEASUREMENT HISTORY
|
||||||
|
g.setFont("Vector:16");
|
||||||
|
const graphHeight=35;
|
||||||
|
const graphWidth=g.getWidth()-30;
|
||||||
|
const graphLocX = 15;
|
||||||
|
const graphLocY = g.getHeight() - 16 - 18 - graphHeight;
|
||||||
|
|
||||||
|
// DRAW SOME KIND OF AXES
|
||||||
|
g.setColor(0.4,0.4,0.4);
|
||||||
|
g.drawRect(graphLocX,graphLocY,graphLocX+graphWidth,graphLocY+graphHeight);
|
||||||
|
g.drawLine(graphLocX,graphLocY+graphHeight/2,graphLocX+graphWidth,graphLocY+graphHeight/2);
|
||||||
|
g.drawLine(graphLocX+graphWidth/2,graphLocY,graphLocX+graphWidth/2,graphLocY+graphHeight);
|
||||||
|
g.drawLine(graphLocX+graphWidth/4,graphLocY,graphLocX+graphWidth/4,graphLocY+graphHeight);
|
||||||
|
g.drawLine(graphLocX+3*graphWidth/4,graphLocY,graphLocX+3*graphWidth/4,graphLocY+graphHeight);
|
||||||
|
g.setColor(normColor);
|
||||||
|
|
||||||
|
// DRAW LINE
|
||||||
|
require("graph").drawLine(g, data.values, {
|
||||||
|
x:graphLocX,
|
||||||
|
y:graphLocY,
|
||||||
|
width:graphWidth,
|
||||||
|
height:graphHeight
|
||||||
|
});
|
||||||
|
|
||||||
|
let graphMax=Math.max.apply(Math,data.values);
|
||||||
|
let graphMin=Math.min.apply(Math,data.values);
|
||||||
|
g.setFontAlign(0, 0);
|
||||||
|
g.setColor(highColor);
|
||||||
|
g.drawString(graphMax.toFixed(data.accuracy), g.getWidth()/2, g.getHeight() - 16 - 18 - graphHeight);
|
||||||
|
g.setColor(lowColor);
|
||||||
|
g.drawString(graphMin.toFixed(data.accuracy), g.getWidth()/2, g.getHeight() - 16 - 18);
|
||||||
|
g.setColor(normColor);
|
||||||
|
|
||||||
|
let historyLength = (historyAmnt*dt >= 60)?('-'+historyAmnt*dt/60+'h'):('-'+historyAmnt*dt+'"');
|
||||||
|
|
||||||
|
g.drawString(historyLength,25, g.getHeight() - 16 - 18 - graphHeight/2);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
// LAST MEASURE
|
||||||
|
g.setFontAlign(0, 0);
|
||||||
|
g.setFont('Vector:36');
|
||||||
|
g.drawString(valueString, g.getWidth() / 2, g.getHeight() / 2);
|
||||||
|
|
||||||
|
data.ondisplay = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateMinMax( data, currentValue ){
|
||||||
|
data.values.push(currentValue);
|
||||||
|
data.values.shift();
|
||||||
|
data.value=currentValue;
|
||||||
|
|
||||||
|
let now = new Date();
|
||||||
|
data.t_value = now.getHours()+':'+String(now.getMinutes()).padStart(2, '0');
|
||||||
|
if (currentValue < data.minval){data.t_min=data.t_value;data.minval = currentValue;}
|
||||||
|
if (currentValue > data.maxval){data.t_max=data.t_value;data.maxval = currentValue;}
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchDisplay(){
|
||||||
|
if (TData.ondisplay) {TData.ondisplay=false;PData.ondisplay=true;updateScreen();}
|
||||||
|
else {PData.ondisplay=false;TData.ondisplay=true;updateScreen();}
|
||||||
|
}
|
||||||
|
|
||||||
|
function settingsPage(){
|
||||||
|
Bangle.on('swipe',function (){});
|
||||||
|
eval(require("Storage").read("nightwatch.settings.js"))(()=>load());
|
||||||
|
Bangle.on('swipe',switchDisplay);
|
||||||
|
console.log(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePressureSensorReading(data) {
|
||||||
|
updateMinMax(TData,data.temperature);
|
||||||
|
updateMinMax(PData,data.pressure);
|
||||||
|
}
|
||||||
|
|
||||||
|
function startup(){
|
||||||
|
// testing in emulator
|
||||||
|
// handlePressureSensorReading({ "temperature": 28.64251302083, "pressure": 1004.66520303803, "altitude": 71.72072902749 });
|
||||||
|
// updateScreen();
|
||||||
|
|
||||||
|
// ON STARTUP:
|
||||||
|
// fill current reading into data,
|
||||||
|
// before `updateMinMax` uses it
|
||||||
|
Bangle.getPressure().then(d=>{TData.value=d.temperature;
|
||||||
|
TData.values.fill(d.temperature);
|
||||||
|
PData.value=d.pressure;
|
||||||
|
PData.values.fill(d.pressure);
|
||||||
|
handlePressureSensorReading(d);
|
||||||
|
updateScreen();});
|
||||||
|
Bangle.on('swipe',switchDisplay);
|
||||||
|
|
||||||
|
//Bangle.on('tap',settingsPage);
|
||||||
|
|
||||||
|
timerID = setInterval( function() {
|
||||||
|
Bangle.getPressure().then(d=>{handlePressureSensorReading(d);updateScreen();});
|
||||||
|
}, dt * 60000);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
startup();
|
||||||
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEw4MA///ospETUQAgc//gFDv4FF/wFP/4FF/5PCgIFChF/AoWA/1/+YFBx/+g4EBAAPAFAIEBEgUDBQYAN/E/AgQvDDoXHDocH4wFDgf8v4RCDooAMj/4AoZcBcM8DOAgFFgJSDAqQAhA=="))
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 959 B |
|
|
@ -0,0 +1,6 @@
|
||||||
|
require("Storage").write("nightwatch.info",{
|
||||||
|
"id":"nightwatch",
|
||||||
|
"name":"The Nightwatch",
|
||||||
|
"src":"nightwatch.app.js",
|
||||||
|
"icon":"nightwatch.icon.png"
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
(function(back) {
|
||||||
|
var FILE = "nightwatch.json";
|
||||||
|
// Load settings
|
||||||
|
var settings = Object.assign({
|
||||||
|
dt: 30,
|
||||||
|
}, require('Storage').readJSON(FILE, true) || {});
|
||||||
|
|
||||||
|
function writeSettings() {
|
||||||
|
require('Storage').writeJSON(FILE, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the menu
|
||||||
|
E.showMenu({
|
||||||
|
"" : { "title" : "nightwatch" },
|
||||||
|
"< Back" : () => back(),
|
||||||
|
'log freq (min)': {
|
||||||
|
value: 0|settings.dt, // 0| converts undefined to 0
|
||||||
|
min: 1, max: 60,
|
||||||
|
onchange: v => {
|
||||||
|
settings.dt = v;
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
Loading…
Reference in New Issue