Merge pull request #2555 from CarlR9/master

'Temperature Graph' Bangle 2 app
pull/2556/head
Gordon Williams 2023-02-03 09:41:01 +00:00 committed by GitHub
commit d9dfb3adee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 453 additions and 0 deletions

2
apps/tempgraph/ChangeLog Normal file
View File

@ -0,0 +1,2 @@
0.01: 3/Feb/2023 Added 'Temperature Graph' app to depository.

36
apps/tempgraph/README.md Normal file
View File

@ -0,0 +1,36 @@
# Temperature Graph
**Temperature Graph** (tempgraph) is a Bangle.js 2 app for recording graphs of the temperature for various time periods from 10 minutes to 7 days long. It samples the watch's temperature sensor 150 times while creating a graph, regardless of the time period selected.
### Menu Options
* **Widgets** Toggles the watch's widgets on and off. With them off gives you a bigger graph when viewing it.
* **Duration** Select the time period for drawing the graph, from 10 minutes to 7 days long.
* **Draw Graph** Draws the graph.
* Tapping the screen toggles the graph between Celsius (red) and Fahrenheit (blue).
* Pressing the watch button takes you back to the menu. **Note:** While the graph can still be viewed after returning to the menu, you can't continue recording it if you had returned to the menu before the time period was up. The graph is saved in the watch though so it's still there the next time you start the app.
* **Show Graph** Shows the last drawn graph.
* Tapping the screen toggles the graph between Celsius (red) and Fahrenheit (blue).
* Pressing the watch button takes you back to the menu.
* **Save Graph** Sends a screengrab of the graph to the Espruino Web IDE from where you can save it as you would any image on a webpage.
* **Save Data** Sends a CSV file of the graph's temperature data to the Espruino Web IDE where you can save it for further use. I suggest you use the Espruino Web IDE's Terminal Logger (selected in the IDE's Settings/General) to record the data as it's sent. This is the easiest way to save it as a text file.
* **Show Temp** Shows the current temperature.
### Note
Using the watch in a normal fashion can raise the temperature it's sensing to quite a few degrees above the surrounding temperature and it may take half an hour or so to drop to close to the surrounding temperature. After that it seems to give quite accurate readings, assuming the thermometer I've been comparing it to is itself reasonably accurate. So best to load the app then not touch the watch for half an hour before starting a recording. This is assuming you're not wearing the app and are just using it to record the temperature where you've put the watch. You could of course wear it and it'll still draw a graph, which might also be useful.
### Screenshots
![](screenshot_1.png)
![](screenshot_2.png)
![](screenshot_3.png)
### Creator
Carl Read ([mail](mailto:cread98@orcon.net.nz), [github](https://github.com/CarlR9))
#### License
[MIT License](LICENSE)

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwxH+goA/AH4AgrHXABFYF0XXkYAK64utGENYFxoABSTxeHXYJglF+UAAIQvEBApfhE4UAF4IDBFwZf/X7hfsR4K/tL96/vRwpf/X/5fJGYK/tL9u02i/tF4KOFL/6/XF4ZftR4K/tL96/vRwpf/X/5fJGYK/tL96/vRwpf/X7gADF8ouBGA4v/F/6/urAmGABFYF7pgIL0owPF0KSC64AIRj4A/AH4ACA="))

395
apps/tempgraph/app.js Normal file
View File

@ -0,0 +1,395 @@
// Temperature Graph
// BangleJS Script
Bangle.setBarometerPower(true,"tempgraph");
Bangle.loadWidgets();
var wids=WIDGETS;
var widsOn=true;
var rm=null;
var gt=null;
var dg=null;
var Layout=require("Layout");
var C=true;
var temp,tempMode,readErrCnt,watchButton2;
var graph=require("Storage").readJSON("tempgraph.json",true);
if(graph==undefined) {
graph=[];
}
var timesData=[
// dur=duration, u=time units, d=divisions on graph, s=seconds per unit.
{dur:10,u:"Mins",d:5,s:60},
{dur:20,u:"Mins",d:4,s:60},
{dur:30,u:"Mins",d:3,s:60},
{dur:40,u:"Mins",d:4,s:60},
{dur:1,u:"Hr",d:4,s:3600},
{dur:2,u:"Hrs",d:4,s:3600},
{dur:3,u:"Hrs",d:3,s:3600},
{dur:4,u:"Hrs",d:4,s:3600},
{dur:6,u:"Hrs",d:6,s:3600},
{dur:8,u:"Hrs",d:4,s:3600},
{dur:12,u:"Hrs",d:6,s:3600},
{dur:16,u:"Hrs",d:4,s:3600},
{dur:20,u:"Hrs",d:5,s:3600},
{dur:1,u:"Day",d:4,s:3600},
{dur:2,u:"Days",d:4,s:86400},
{dur:3,u:"Days",d:3,s:86400},
{dur:4,u:"Days",d:4,s:86400},
{dur:5,u:"Days",d:5,s:86400},
{dur:6,u:"Days",d:6,s:86400},
{dur:7,u:"Days",d:7,s:86400}
];
var times=[];
for(n=0;n<timesData.length;n++){
times.push(timesData[n].dur+" "+timesData[n].u);
}
var durInd=0;
var duration=times[durInd];
function drawWids(){
g.clear();
if(widsOn){
WIDGETS=wids;
Bangle.drawWidgets();
} else {
WIDGETS={};
}
}
function openMenu(){
drawWids();
E.showMenu(menu);
}
function redoMenu(){
clearInterval(rm);
E.showMenu();
openMenu();
}
function refreshMenu(){
rm = setInterval(redoMenu,100);
}
function getF(c){
// Get Fahrenheit temperature from Celsius.
return c*1.8+32;
}
function getT(){
Bangle.getPressure().then(p=>{
temp=p.temperature;
if(tempMode=="drawGraph"&&graph.length>0&&Math.abs(graph[graph.length-1].temp-temp)>10&&readErrCnt<2){
// A large change in temperature may be a reading error. ie. A 0C or less reading after
// a 20C reading. So if this happens, the reading is repeated up to 2 times to hopefully
// skip such errors.
readErrCnt++;
print("readErrCnt "+readErrCnt);
return;
}
clearInterval(gt);
readErrCnt=0;
switch (tempMode){
case "showTemp":
showT();
break;
case "drawGraph":
var date=new Date();
var dateStr=require("locale").date(date).trim();
var hrs=date.getHours();
var mins=date.getMinutes();
var secs=date.getSeconds();
graph.push({
temp:temp,
date:dateStr,
hrs:hrs,
mins:mins,
secs:secs
});
if(graph.length==1){
graph[0].dur=durInd;
}
require("Storage").writeJSON("tempgraph.json", graph);
if(graph.length==150){
clearInterval(dg);
}
drawG();
}
});
}
function getTemp(){
readErrCnt=0;
gt = setInterval(getT,800);
}
function setButton(){
var watchButton=setWatch(function(){
clearInterval(gt);
clearInterval(dg);
clearWatch(watchButton);
Bangle.removeListener("touch",screenTouch);
openMenu();
},BTN);
Bangle.on('touch',screenTouch);
}
function setButton2(){
watchButton2=setWatch(function(){
clearWatch(watchButton2);
openMenu();
},BTN);
}
function zPad(n){
return n.toString().padStart(2,0);
}
function screenTouch(n,ev){
if(ev.y>23&&ev.y<152){
C=C==false;
drawG(false);
}
}
function drawG(){
function cf(t){
if(C){
return t;
}
return getF(t);
}
drawWids();
var top=1;
var bar=21;
var barBot=175-22;
if(widsOn){
top=25;
bar=bar+24;
barBot=barBot-24;
}
var low=graph[0].temp;
var hi=low;
for(n=0;n<graph.length;n++){
var t=graph[n].temp;
if(low>t){
low=t;
}
if(hi<t){
hi=t;
}
}
var tempHi=Math.ceil((cf(hi)+2)/10)*10;
var tempLow=Math.floor((cf(low)-2)/10)*10;
var div=2;
if(tempHi-tempLow>10){
div=5;
}
if(C){
g.setColor(1,0,0);
}else{
g.setColor(0,0,1);
}
var step=(barBot-bar)/((tempHi-tempLow)/div);
for(n=0;n<graph.length;n++){
var pos=tempLow-cf(graph[n].temp);
g.drawLine(n+3,pos*(step/div)+barBot,n+3,barBot+3);
}
g.fillRect(161,barBot+5,174,barBot+20);
g.setColor(1,1,1);
g.setFont("6x8:2");
if(C){
g.drawString("C",163,barBot+5);
}else{
g.drawString("F",163,barBot+5);
}
g.setColor(0,0,0);
g.setFont6x15();
g.drawString("Temperature Graph - "+times[graph[0].dur],1,top);
g.drawRect(2,bar-4,153,barBot+4);
g.setFont("6x8:1");
var num=tempHi;
for(n=bar;n<=barBot;n=n+step){
g.drawLine(3,n,152,n);
g.drawString(num.toString().padStart(3," "),155,n-4);
num=num-div;
}
step=151/timesData[graph[0].dur].d;
for(n=step+2;n<152;n=n+step){
g.drawLine(n,bar-4,n,barBot+4);
}
grSt=graph[0];
g.drawString("Start: "+grSt.date+" "+grSt.hrs+":"+zPad(grSt.mins),1,barBot+6);
var lastT=graph[graph.length-1].temp;
g.drawString("Last Reading:",1,barBot+14);
g.setColor(1,0,0);
g.drawString(lastT.toFixed(1)+"C",85,barBot+14);
g.setColor(0,0,1);
g.drawString(getF(lastT).toFixed(1)+"F",121,barBot+14);
process.memory(true);
}
function drawGraph(){
setButton();
tempMode="drawGraph";
durInd=times.indexOf(duration);
graph=[];
getTemp();
dg=setInterval(getTemp,1000*timesData[durInd].dur*timesData[durInd].s/150);
}
function showGraph(){
setButton();
drawG();
}
function noBluetooth(){
if(NRF.getSecurityStatus().connected){
return false;
}else{
message("Error! Your\nBangle Watch\ncurrently has\nno Bluetooth\nconnection.");
return true;
}
}
function saveGraph(){
if(noBluetooth()){
return;
}
drawG();
g.flip();
g.dump();
message("Graph has\nbeen sent\nto Web IDE\nfor saving.\n");
}
function saveData(){
if(noBluetooth()){
return;
}
drawG();
g.flip();
print("Temperature Graph - "+times[graph[0].dur]+"\n");
print("\"Date\",\"Time\",\"Celsius\",\"Fahrenheit\"");
for(n=0;n<graph.length;n++){
var gr=graph[n];
print("\""+gr.date+"\",\""+gr.hrs+":"+zPad(gr.mins)+":"+zPad(gr.secs)+"\","+gr.temp+","+getF(gr.temp));
}
message("Data has\nbeen sent\nto Web IDE\nfor saving.\n");
}
function message(mes){
setButton2();
var messageLO=new Layout({
type:"v",c:[
{type:"txt",font:"6x8:2",width:171,label:mes,id:"label"},
{type:"btn",font:"6x8:2",pad:3,label:"OK",cb:l=>exit()},
],lazy:true
});
drawWids();
messageLO.render();
}
function showT(){
tempLO.lab1.label=tempLO.lab3.label;
tempLO.lab2.label=tempLO.lab4.label;
tempLO.lab3.label=tempLO.lab5.label;
tempLO.lab4.label=tempLO.lab6.label;
tempLO.lab5.label=temp.toFixed(2)+"C";
tempLO.lab6.label=getF(temp).toFixed(2)+"F";
tempLO.render();
}
function exit(){
clearWatch(watchButton2);
openMenu();
}
function showTemp(){
tempMode="showTemp";
setButton2();
tempLO=new Layout({
type:"v",c:[
{type:"h",c:[
{type:"txt",pad:5,col:"#f77",font:"6x8:2",label:" ",id:"lab1"},
{type:"txt",pad:5,col:"#77f",font:"6x8:2",label:" ",id:"lab2"}
]},
{type:"h",c:[
{type:"txt",pad:5,col:"#f77",font:"6x8:2",label:" ",id:"lab3"},
{type:"txt",pad:5,col:"#77f",font:"6x8:2",label:" ",id:"lab4"}
]},
{type:"h",c:[
{type:"txt",pad:5,col:"#f00",font:"6x8:2",label:" ",id:"lab5"},
{type:"txt",pad:5,col:"#00f",font:"6x8:2",label:" ",id:"lab6"}
]},
{type:"h",c:[
{type:"btn",pad:2,font:"6x8:2",label:"Temp",cb:l=>getTemp()},
{type:"btn",pad:2,font:"6x8:2",label:"Exit",cb:l=>exit()}
]}
]
},{lazy:true});
tempLO.render();
getTemp();
}
var menu={
"":{
"title":" Temp. Graph"
},
"Widgets":{
value:widsOn,
format:vis=>vis?"Hide":"Show",
onchange:vis=>{
widsOn=vis;
refreshMenu();
}
},
"Duration":{
value:times.indexOf(duration),
min:0,max:times.length-1,step:1,wrap:true,
format:tim=>times[tim],
onchange:(dur)=>{
duration=times[dur];
}
},
"Draw Graph":function(){
E.showMenu();
drawGraph();
},
"Show Graph" : function(){
E.showMenu();
if(graph.length>0){
showGraph();
}else{
message("No graph to\nshow as no\ngraph has been\ndrawn yet.");
}
},
"Save Graph" : function(){
E.showMenu();
if(graph.length>0){
saveGraph();
}else{
message("No graph to\nsave as no\ngraph has been\ndrawn yet.");
}
},
"Save Data" : function(){
E.showMenu();
if(graph.length>0){
saveData();
}else{
message("No data to\nsave as no\ngraph has been\ndrawn yet.");
}
},
"Show Temp":function(){
E.showMenu();
showTemp();
}
};
openMenu();

BIN
apps/tempgraph/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@ -0,0 +1,19 @@
{ "id": "tempgraph",
"name": "Temperature Graph",
"shortName":"Temp Graph",
"version":"0.01",
"description": "An app for recording the temperature for time periods ranging from 10 minutes to 7 days.",
"icon": "app.png",
"type": "app",
"tags": "temperature,tempgraph,graph",
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"tempgraph.app.js","url":"app.js"},
{"name":"tempgraph.img","url":"app-icon.js","evaluate":true}
],
"data": [
{"name":"tempgraph.json"}
],
"screenshots": [{"url":"screenshot_1.png"},{"url":"screenshot_2.png"},{"url":"screenshot_3.png"}]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB