Merge branch 'master' of github.com:espruino/BangleApps

pull/1174/head^2
Gordon Williams 2022-01-04 10:13:24 +00:00
commit 7eb746ae48
13 changed files with 634 additions and 47 deletions

View File

@ -167,7 +167,7 @@
{
"id": "setting",
"name": "Settings",
"version": "0.38",
"version": "0.39",
"description": "A menu for setting up Bangle.js",
"icon": "settings.png",
"tags": "tool,system",
@ -4697,6 +4697,8 @@
"tags": "tool,timer",
"readme":"README.md",
"supports":["BANGLEJS2"],
"screenshots": [{"url":"screenshot1.png"},{"url":"screenshot2.png"},{"url":"screenshot3.png"}],
"allow_emulator": true,
"storage": [
{"name":"a_speech_timer.app.js","url":"app.js"},
{"name":"a_speech_timer.img","url":"app-icon.js","evaluate":true}
@ -4945,10 +4947,12 @@
"id":"awairmonitor",
"name":"Awair Monitor",
"icon": "app.png",
"screenshots": [{"url":"screenshot.png"}],
"allow_emulator": true,
"version":"0.01",
"version":"0.03",
"description": "Displays the level of CO2, VOC, PM 2.5, Humidity and Temperature, from your Awair device.",
"tags": "tool,health",
"type": "clock",
"tags": "clock,tool,health",
"readme":"README.md",
"supports":["BANGLEJS2"],
"storage": [
@ -5094,6 +5098,21 @@
{"name":"ltherm.img","url":"icon.js","evaluate":true}
]
},
{
"id": "widviztime",
"name": "Widget Autohide Widget",
"shortName": "Viz Time Widget",
"version": "0.01",
"description": "The widgets will be shown for four seconds after the device is unlocked.",
"icon": "eye.png",
"type": "widget",
"tags": "widget",
"readme":"README.md",
"supports": ["BANGLEJS","BANGLEJS2"],
"storage": [
{"name":"widviztime.wid.js","url":"widget.js"}
]
},
{
"id": "supf",
"name": "Simple Clock with Date",

View File

@ -1 +1,3 @@
0.01: Beta version for Bangle 2 paired with Chrome (2021/12/11)
0.02: The app is now a clock, the data is greyed after the connection is lost (2021/12/22)
0.03: Set the Awair's IP directly on the webpage (2021/12/27)

View File

@ -5,11 +5,10 @@ Displays the level of CO2, VOC, PM 2.5, Humidity and Temperature, from your Awai
* What you need:
* A BangleJS 2
* An Awair device [with local API enabled](https://support.getawair.com/hc/en-us/articles/360049221014-Awair-Local-API-Feature)
* The web app [awair_to_bangle.html](awair_to_bangle.html) that will retrive the data from your Awair device and sent it to your BangleJS 2 through Chrome's Bluetooth LE connection
* The web app [awair_to_bangle.html](awair_to_bangle.html) that will retrieve the data from your Awair device and sent it to your BangleJS 2 through Chrome's Bluetooth LE connection
* How to get started
* Open awair_to_bangle.html with a text/code editor and input the IP address of your Awair on top (const awair_ip_1 = "192.168.xx.xx")
* Launch the Awair Monitor app on your BangleJS
* Open awair_to_bangle.html on Chrome and click "Connect BangleJS" - it connects to your watch the same way as the Bangle app store
* Open awair_to_bangle.html on Chrome (desktop or Android), input the IP address of your Awair device, and click "Connect BangleJS" - it connects to your watch the same way as the Bangle app store
* Once connected to the watch with the app running, the watch app is updated once per second
![](screenshot.png)

View File

@ -30,6 +30,8 @@ var bt_temp_history = new Array(10).fill(0);
var internal_last_update = -1;
var display_frozen = false;
function draw() {
g.reset().clearRect(0,24,g.getWidth(),g.getHeight());
@ -47,14 +49,8 @@ function draw() {
g.drawString("Humi", 125, 100);
g.drawString("Temp", 160, 100);
g.setFont("HaxorNarrow7x17");
g.drawString(""+bt_current_co2, 18, 110);
g.drawString(""+bt_current_voc, 53, 110);
g.drawString(""+bt_current_pm25, 88, 110);
g.drawString(""+bt_current_humi, 123, 110);
g.drawString(""+bt_current_temp, 158, 110);
if (last_update != bt_last_update) {
display_frozen = false;
last_update = bt_last_update;
internal_last_update = last_update;
if (last_update % 10 == 0) {
@ -65,16 +61,29 @@ function draw() {
bt_temp_history.shift(); bt_temp_history.push(bt_current_temp);
}
}
if (internal_last_update == -1) {
g.drawString("Waiting for connection", 88, 164);
} else if (internal_last_update > last_update + 5) {
} else if ((internal_last_update > last_update + 5) && (internal_last_update < last_update + 60)) {
g.drawString("Trying to reconnect since " + (internal_last_update - last_update), 88, 164);
} else if (internal_last_update > last_update + 5) {
display_frozen = true;
g.drawString("Waiting for connection", 88, 164);
}
if (display_frozen) { g.setColor("#888"); }
g.setFont("HaxorNarrow7x17");
g.drawString(""+bt_current_co2, 18, 110);
g.drawString(""+bt_current_voc, 53, 110);
g.drawString(""+bt_current_pm25, 88, 110);
g.drawString(""+bt_current_humi, 123, 110);
g.drawString(""+bt_current_temp, 158, 110);
for (i = 0; i < 10; i++) {
// max height = 32
if (display_frozen) { g.setColor("#888"); }
// max height = 32
g.drawLine(10+i*2, 150-(Math.min(Math.max(bt_co2_history[i],400), 1200)-400)/25, 10+i*2, 150);
g.drawLine(45+i*2, 150-(Math.min(Math.max(bt_voc_history[i],0), 1440)-0)/45, 45+i*2, 150);
g.drawLine(80+i*2, 150-(Math.min(Math.max(bt_pm25_history[i],0), 32)-0)/1, 80+i*2, 150);
@ -91,6 +100,7 @@ function draw() {
}
// init
Bangle.setUI("clock");
require("FontHaxorNarrow7x17").add(Graphics);
g.clear();
Bangle.loadWidgets();

View File

@ -7,15 +7,15 @@
// Don't forget to enable the Local API on your Awair before using this
// https://support.getawair.com/hc/en-us/articles/360049221014-Awair-Local-API-Feature
const awair_ip_1 = "192.168.2.2"; // <- INPUT YOUR AWAIR IP ADDRESS HERE
const awair_name_1 = "Awair";
var bt_connection;
var is_connected = false;
var reconnect_counter = 5;
var reconnect_attempt_counter = 1;
var is_chart_started = false;
window.onload = function() {
function initChart() {
var chart_co2;
var chart_voc;
var chart_pm;
@ -23,6 +23,8 @@ window.onload = function() {
var chart_humidity;
var dataPoints_1 = [];
var posx = 0;
var awair_ip_1 = document.getElementById('inputawairip').value;
$.getJSON("http://"+awair_ip_1+"/air-data/latest", function(data) {
$.each(data, function(key, value){
@ -105,11 +107,12 @@ window.onload = function() {
let current_humi = dataPoints_1['humid'][dataPoints_1['humid'].length-1].y;
let current_temp = dataPoints_1['temp'][dataPoints_1['temp'].length-1].y;
let last_update = dataPoints_1['temp'].length-1;
if (is_connected && bt_connection.isOpen) {
if (is_connected && bt_connection && bt_connection.isOpen) {
bt_connection.write('\x10bt_current_co2='+current_co2+';bt_current_voc='+current_voc+';bt_current_pm25='+current_pm25+';bt_current_humi='+current_humi+';bt_current_temp='+current_temp+';bt_last_update='+last_update+';\n');
console.log("Sent data through Bluetooth");
} else if (is_connected && !bt_connection.isOpen) {
} else if (is_connected && bt_connection && !bt_connection.isOpen) {
console.log("Disconnected - Next attempt to reconnect in " + reconnect_counter);
reconnect_counter--;
@ -131,7 +134,6 @@ window.onload = function() {
}
}
setTimeout(function(){updateChart()}, 1000);
});
}
@ -148,10 +150,16 @@ function connectBT() {
bt_connection = c;
is_connected = true;
reconnect_attempt_counter = 1;
if (!is_chart_started) {
initChart();
is_chart_started = true;
}
});
}
function disconnectBT() {
console.log("Disconnect Bluetooth button pressed. bt_connection value below.")
console.log(bt_connection);
if (is_connected && bt_connection) {
bt_connection.close();
is_connected = false;
@ -167,23 +175,21 @@ function disconnectBT() {
<p style="color: #F7FAFC">
<b>How to use</b>
<br/><br/>
<br><br>
Step 1: Enable the Local API on your Awair: https://support.getawair.com/hc/en-us/articles/360049221014-Awair-Local-API-Feature
<br/><br/>
Step 2: Modify this HTML file to input the IP address of your Awair on top (const awair_ip_1 = "192.168.xx.xx")
<br/><br/>
Step 3: Launch the Awair Monitor app on your BangleJS
<br/><br/>
Step 4: Click "Connect BangleJS"
<br/><br/>
Step 5: Optionally, open the web inspector's console (Right click > Inspector > Console) to read the bluetooth logs
<br><br>
Step 2: Launch the Awair Monitor app on your BangleJS
<br><br>
Step 3: Input your Awair IP address and click the Connect button:
<input type="text" id="inputawairip" value="192.168.2.1">
<input type="button" value="Connect BangleJS" onclick="connectBT();">
<br><br>
Step 4: Optionally, open the web inspector's console (Right click &gt; Inspector &gt; Console) to read the Bluetooth logs
<br><br>
Step 5: Once you are done, click the Disconnect button to properly close the Blutooth connection
<center><button onclick="disconnectBT();">Disconnect BangleJS</button></center>
</p>
<center>
<button onclick="connectBT();">Connect BangleJS</button>
<button onclick="disconnectBT();">Disconnect BangleJS</button>
</center>
<br/><br/>
<div id="chartContainer_co2" style="height: 300px; max-width: 920px; margin: 0px auto; margin-bottom: 64px;"></div>
@ -192,4 +198,476 @@ Step 5: Optionally, open the web inspector's console (Right click > Inspector >
<div id="chartContainer_humidity" style="height: 300px; max-width: 920px; margin: 0px auto; margin-bottom: 64px;"></div>
<div id="chartContainer_temperature" style="height: 300px; max-width: 920px; margin: 0px auto; margin-bottom: 64px;"></div>
</body>
</html>
</html>(buf));
}
function str2ab(str) {
var buf = new ArrayBuffer(str.length);
var bufView = new Uint8Array(buf);
for (var i=0, strLen=str.length; i<strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
function handleQueue() {
if (!queue.length) return;
var q = queue.shift();
log(3,"Executing "+JSON.stringify(q)+" from queue");
if (q.type == "write") puck.write(q.data, q.callback, q.callbackNewline);
else log(1,"Unknown queue item "+JSON.stringify(q));
}
function connect(callback) {
if (!checkIfSupported()) return;
var connection = {
on : function(evt,cb) { this["on"+evt]=cb; },
emit : function(evt,data) { if (this["on"+evt]) this["on"+evt](data); },
isOpen : false,
isOpening : true,
txInProgress : false
};
var btServer = undefined;
var btService;
var connectionDisconnectCallback;
var txCharacteristic;
var rxCharacteristic;
var txDataQueue = [];
var flowControlXOFF = false;
var chunkSize = DEFAULT_CHUNKSIZE;
connection.close = function() {
connection.isOpening = false;
if (connection.isOpen) {
connection.isOpen = false;
connection.emit('close');
} else {
if (callback) callback(null);
}
if (btServer) {
btServer.disconnect();
btServer = undefined;
txCharacteristic = undefined;
rxCharacteristic = undefined;
chunkSize = DEFAULT_CHUNKSIZE;
}
};
connection.write = function(data, callback) {
if (data) txDataQueue.push({data:data,callback:callback,maxLength:data.length});
if (connection.isOpen && !connection.txInProgress) writeChunk();
function writeChunk() {
if (flowControlXOFF) { // flow control - try again later
setTimeout(writeChunk, 50);
return;
}
var chunk;
if (!txDataQueue.length) {
puck.writeProgress();
return;
}
var txItem = txDataQueue[0];
puck.writeProgress(txItem.maxLength - txItem.data.length, txItem.maxLength);
if (txItem.data.length <= chunkSize) {
chunk = txItem.data;
txItem.data = undefined;
} else {
chunk = txItem.data.substr(0,chunkSize);
txItem.data = txItem.data.substr(chunkSize);
}
connection.txInProgress = true;
log(2, "Sending "+ JSON.stringify(chunk));
txCharacteristic.writeValue(str2ab(chunk)).then(function() {
log(3, "Sent");
if (!txItem.data) {
txDataQueue.shift(); // remove this element
if (txItem.callback)
txItem.callback();
}
connection.txInProgress = false;
writeChunk();
}).catch(function(error) {
log(1, 'SEND ERROR: ' + error);
txDataQueue = [];
connection.close();
});
}
};
navigator.bluetooth.requestDevice({
filters:[
{ namePrefix: 'Puck.js' },
{ namePrefix: 'Pixl.js' },
{ namePrefix: 'MDBT42Q' },
{ namePrefix: 'RuuviTag' },
{ namePrefix: 'iTracker' },
{ namePrefix: 'Thingy' },
{ namePrefix: 'Espruino' },
{ services: [ NORDIC_SERVICE ] }
], optionalServices: [ NORDIC_SERVICE ]}).then(function(device) {
log(1, 'Device Name: ' + device.name);
log(1, 'Device ID: ' + device.id);
// Was deprecated: Should use getPrimaryServices for this in future
//log('BT> Device UUIDs: ' + device.uuids.join('\n' + ' '.repeat(21)));
device.addEventListener('gattserverdisconnected', function() {
log(1, "Disconnected (gattserverdisconnected)");
connection.close();
});
connection.device = device;
connection.reconnect(callback);
}).catch(function(error) {
log(1, 'ERROR: ' + error);
connection.close();
});
connection.reconnect = function(callback) {
connection.device.gatt.connect().then(function(server) {
log(1, "Connected");
btServer = server;
return server.getPrimaryService(NORDIC_SERVICE);
}).then(function(service) {
log(2, "Got service");
btService = service;
return btService.getCharacteristic(NORDIC_RX);
}).then(function (characteristic) {
rxCharacteristic = characteristic;
log(2, "RX characteristic:"+JSON.stringify(rxCharacteristic));
rxCharacteristic.addEventListener('characteristicvaluechanged', function(event) {
var dataview = event.target.value;
var data = ab2str(dataview.buffer);
if (data.length > chunkSize) {
log(2, "Received packet of length "+data.length+", increasing chunk size");
chunkSize = data.length;
}
if (puck.flowControl) {
for (var i=0;i<data.length;i++) {
var ch = data.charCodeAt(i);
var remove = true;
if (ch==19) {// XOFF
log(2,"XOFF received => pause upload");
flowControlXOFF = true;
} else if (ch==17) {// XON
log(2,"XON received => resume upload");
flowControlXOFF = false;
} else
remove = false;
if (remove) { // remove character
data = data.substr(0,i-1)+data.substr(i+1);
i--;
}
}
}
log(3, "Received "+JSON.stringify(data));
connection.emit('data', data);
});
return rxCharacteristic.startNotifications();
}).then(function() {
return btService.getCharacteristic(NORDIC_TX);
}).then(function (characteristic) {
txCharacteristic = characteristic;
log(2, "TX characteristic:"+JSON.stringify(txCharacteristic));
}).then(function() {
connection.txInProgress = false;
connection.isOpen = true;
connection.isOpening = false;
isBusy = false;
queue = [];
callback(connection);
connection.emit('open');
// if we had any writes queued, do them now
connection.write();
}).catch(function(error) {
log(1, 'ERROR: ' + error);
connection.close();
});
};
return connection;
};
// ----------------------------------------------------------
var connection;
/* convenience function... Write data, call the callback with data:
callbackNewline = false => if no new data received for ~0.2 sec
callbackNewline = true => after a newline */
function write(data, callback, callbackNewline) {
if (!checkIfSupported()) return;
let result;
/// If there wasn't a callback function, then promisify
if (typeof callback !== 'function') {
callbackNewline = callback;
result = new Promise((resolve, reject) => callback = (value, err) => {
if (err) reject(err);
else resolve(value);
});
}
if (isBusy) {
log(3, "Busy - adding Puck.write to queue");
queue.push({type:"write", data:data, callback:callback, callbackNewline:callbackNewline});
return result;
}
var cbTimeout;
function onWritten() {
if (callbackNewline) {
connection.cb = function(d) {
var newLineIdx = connection.received.indexOf("\n");
if (newLineIdx>=0) {
var l = connection.received.substr(0,newLineIdx);
connection.received = connection.received.substr(newLineIdx+1);
connection.cb = undefined;
if (cbTimeout) clearTimeout(cbTimeout);
cbTimeout = undefined;
if (callback)
callback(l);
isBusy = false;
handleQueue();
}
};
}
// wait for any received data if we have a callback...
var maxTime = 300; // 30 sec - Max time we wait in total, even if getting data
var dataWaitTime = callbackNewline ? 100/*10 sec if waiting for newline*/ : 3/*300ms*/;
var maxDataTime = dataWaitTime; // max time we wait after having received data
cbTimeout = setTimeout(function timeout() {
cbTimeout = undefined;
if (maxTime) maxTime--;
if (maxDataTime) maxDataTime--;
if (connection.hadData) maxDataTime=dataWaitTime;
if (maxDataTime && maxTime) {
cbTimeout = setTimeout(timeout, 100);
} else {
connection.cb = undefined;
if (callback)
callback(connection.received);
isBusy = false;
handleQueue();
connection.received = "";
}
connection.hadData = false;
}, 100);
}
if (connection && (connection.isOpen || connection.isOpening)) {
if (!connection.txInProgress) connection.received = "";
isBusy = true;
connection.write(data, onWritten);
return result
}
connection = connect(function(puck) {
if (!puck) {
connection = undefined;
if (callback) callback(null);
return;
}
connection.received = "";
connection.on('data', function(d) {
connection.received += d;
connection.hadData = true;
if (connection.cb) connection.cb(d);
});
connection.on('close', function(d) {
connection = undefined;
});
isBusy = true;
connection.write(data, onWritten);
});
return result
}
// ----------------------------------------------------------
var puck = {
/// Are we writing debug information? 0 is no, 1 is some, 2 is more, 3 is all.
debug : 1,
/// Should we use flow control? Default is true
flowControl : true,
/// Used internally to write log information - you can replace this with your own function
log : function(level, s) { if (level <= this.debug) console.log("<BLE> "+s)},
/// Called with the current send progress or undefined when done - you can replace this with your own function
writeProgress : function (charsSent, charsTotal) {
//console.log(charsSent + "/" + charsTotal);
},
/** Connect to a new device - this creates a separate
connection to the one `write` and `eval` use. */
connect : connect,
/// Write to Puck.js and call back when the data is written. Creates a connection if it doesn't exist
write : write,
/// Evaluate an expression and call cb with the result. Creates a connection if it doesn't exist
eval : function(expr, cb) {
const response = write('\x10Bluetooth.println(JSON.stringify(' + expr + '))\n', true)
.then(function (d) {
try {
return JSON.parse(d);
} catch (e) {
log(1, "Unable to decode " + JSON.stringify(d) + ", got " + e.toString());
return Promise.reject(d);
}
});
if (cb) {
return void response.then(cb, (err) => cb(null, err));
} else {
return response;
}
},
/// Write the current time to the Puck
setTime : function(cb) {
var d = new Date();
var cmd = 'setTime('+(d.getTime()/1000)+');';
// in 1v93 we have timezones too
cmd += 'if (E.setTimeZone) E.setTimeZone('+d.getTimezoneOffset()/-60+');\n';
write(cmd, cb);
},
/// Did `write` and `eval` manage to create a connection?
isConnected : function() {
return connection!==undefined;
},
/// get the connection used by `write` and `eval`
getConnection : function() {
return connection;
},
/// Close the connection used by `write` and `eval`
close : function() {
if (connection)
connection.close();
},
/** Utility function to fade out everything on the webpage and display
a window saying 'Click to continue'. When clicked it'll disappear and
'callback' will be called. This is useful because you can't initialise
Web Bluetooth unless you're doing so in response to a user input.*/
modal : function(callback) {
var e = document.createElement('div');
e.style = 'position:absolute;top:0px;left:0px;right:0px;bottom:0px;opacity:0.5;z-index:100;background:black;';
e.innerHTML = '<div style="position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);font-family: Sans-Serif;font-size:400%;color:white;">Click to Continue...</div>';
e.onclick = function(evt) {
callback();
evt.preventDefault();
document.body.removeChild(e);
};
document.body.appendChild(e);
}
};
return puck;
}));
</script>
<script type="text/javascript">
// Don't forget to enable the Local API on your Awair before using this
// https://support.getawair.com/hc/en-us/articles/360049221014-Awair-Local-API-Feature
const awair_name_1 = "Awair";
var bt_connection;
var is_connected = false;
var reconnect_counter = 5;
var reconnect_attempt_counter = 1;
var is_chart_started = false;
function initChart() {
var chart_co2;
var chart_voc;
var chart_pm;
var chart_temperature;
var chart_humidity;
var dataPoints_1 = [];
var posx = 0;
var awair_ip_1 = document.getElementById('inputawairip').value;
$.getJSON("http://"+awair_ip_1+"/air-data/latest", function(data) {
$.each(data, function(key, value){
if (dataPoints_1[key] === undefined) { dataPoints_1[key] = []; }
if (key === "temp" || key === "humid") { dataPoints_1[key].push({x: posx, y: parseFloat(value)}); }
else { dataPoints_1[key].push({x: posx, y: parseInt(value)}); }
});
posx++;
chart_co2 = new CanvasJS.Chart("chartContainer_co2",{
title:{ text:"CO2", fontFamily: "helvetica", fontColor: "#F7FAFC", fontSize: 16, horizontalAlign: "left", padding: { left: 30 } },
axisX:{ labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
axisY:{ minimum: 0, labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
legend: { fontColor: "#F7FAFC", horizontalAlign: "center", verticalAlign: "bottom" },
data: [ { type: "line", lineColor: "#6648FF", showInLegend: true, legendText: awair_name_1, dataPoints : dataPoints_1.co2 }]
});
chart_voc = new CanvasJS.Chart("chartContainer_voc",{
title:{ text:"VOC", fontFamily: "helvetica", fontColor: "#F7FAFC", fontSize: 16, horizontalAlign: "left", padding: { left: 30 } },
axisX:{ labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
axisY:{ minimum: 0, labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
legend: { fontColor: "#F7FAFC", horizontalAlign: "center", verticalAlign: "bottom" },
data: [ { type: "line", lineColor: "#6648FF", showInLegend: true, legendText: awair_name_1, dataPoints : dataPoints_1.voc }]
});
chart_pm = new CanvasJS.Chart("chartContainer_pm",{
title:{ text:"PM", fontFamily: "helvetica", fontColor: "#F7FAFC", fontSize: 16, horizontalAlign: "left", padding: { left: 30 } },
axisX:{ labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
axisY:{ minimum: 0, labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
legend: { fontColor: "#F7FAFC", horizontalAlign: "center", verticalAlign: "bottom" },
data: [ { type: "line", lineColor: "#6648FF", showInLegend: true, legendText: awair_name_1, dataPoints : dataPoints_1.pm25 }]
});
chart_humidity = new CanvasJS.Chart("chartContainer_humidity",{
title:{ text:"Humidity", fontFamily: "helvetica", fontColor: "#F7FAFC", fontSize: 16, horizontalAlign: "left", padding: { left: 30 } },
axisX:{ labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
axisY:{ labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
legend: { fontColor: "#F7FAFC", horizontalAlign: "center", verticalAlign: "bottom" },
data: [ { type: "line", lineColor: "#6648FF", showInLegend: true, legendText: awair_name_1, dataPoints : dataPoints_1.humid }]
});
chart_temperature = new CanvasJS.Chart("chartContainer_temperature",{
title:{ text:"Temperature", fontFamily: "helvetica", fontColor: "#F7FAFC", fontSize: 16, horizontalAlign: "left", padding: { left: 30 } },
axisX:{ labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
axisY:{ labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
legend: { fontColor: "#F7FAFC", horizontalAlign: "center", verticalAlign: "bottom" },
data: [ { type: "line", lineColor: "#6648FF", showInLegend: true, legendText: awair_name_1, dataPoints : dataPoints_1.temp }]
});
chart_co2.set("backgroundColor", "#1A202C");
chart_voc.set("backgroundColor", "#1A202C");
chart_pm.set("backgroundColor", "#1A202C");
chart_humidity.set("backgroundColor", "#1A202C");
chart_temperature.set("backgroundColor", "#1A202C");
updateChart();
});
function updateChart() {
$.getJSON("http://"+awair_ip_1+"/air-data/latest", function(data) {
$.each(data, function(key, value){
if (dataPoints_1[key] === undefined) { dataPoints_1[key] = []; }
if (key === "temp" || key === "humid") { dataPoints_1[key].push({x: posx, y: parseFloat(value)}); }
else { dataPoints_1[key].push({x: posx, y: parseInt(value)}); }
});
posx++;
chart_co2.render();
chart_voc.render();
chart_pm.render();
chart_temperature.render();
chart_humidity.render();
chart_co2.title.set("text", "CO2 level (ppm)");
chart_voc.title.set("text", "VOC level (ppb)");
chart_pm.title.set("text", "PM2.5 level (ug/m³)");
chart_humidity.title.set("text", "Humidity level (%)");
chart_temperature.title.set("text", "Temperature level (°C)");
let current_co2 = dataPoints_1['co2'][dataPoints_1['co2'].length-1].y;
let current_voc = dataPoints_1['voc'][dataPoints_1['voc'].length-1].y;
let current_pm25 = dataPoints_1['pm25'][dataPoints_1['pm25'].length-1].y;
let current_humi = dataPoints_1['humid'][dataPoints_1['humid'].length-1].y;
let current_temp = dataPoints_1['temp'][dataPoints_1['temp'].length-1].y;
let last_update = dataPoints_1['temp'].length-1;
if (is_connected && bt_connection && bt_connection.isOpen) {
bt_connection.write('\x10bt_current_co2='+current_co2+';bt_current_voc='+current_voc+';bt_current_pm25='+current_pm25+';bt_current_humi='+current_humi+';bt_current_temp='+current_temp+';bt_

View File

@ -16,13 +16,19 @@ var lastFix = {
time: 0,
satellites: 0
};
var nofix = 0;
var SATinView = 0;
var nofBD = 0;
var nofGP = 0;
function formatTime(now) {
var fd = now.toUTCString().split(" ");
var time = fd[4].substr(0, 5);
var date = [fd[0], fd[1], fd[2]].join(" ");
return time + " - " + date;
if (now == undefined) {
return "no GPS time available";
} else {
var fd = now.toUTCString().split(" ");
var time = fd[4].substr(0, 5);
var date = [fd[0], fd[1], fd[2]].join(" ");
return time + " - " + date;
}
}
function getMaidenHead(param1,param2){
var lat=-100.0;
@ -77,9 +83,9 @@ function onGPS(fix) {
{type:"txt", font:"6x8", label:"Waiting for GPS" },
{type:"h", c: [
{type:"txt", font:"10%", label:fix.satellites, pad:2, id:"sat" },
{type:"txt", font:"6x8", pad:3, label:"Satellites" }
{type:"txt", font:"6x8", pad:3, label:"Satellites used" }
]},
{type:"txt", font:"6x8", label:"", id:"progress" }
{type:"txt", font:"6x8", label:"", fillx:true, id:"progress" }
]},{lazy:true});
}
g.clearRect(0,24,g.getWidth(),g.getHeight());
@ -87,7 +93,6 @@ function onGPS(fix) {
}
lastFix = fix;
if (fix.fix) {
nofix = 0;
var locale = require("locale");
var satellites = fix.satellites;
var maidenhead = getMaidenHead(fix.lat,fix.lon);
@ -100,12 +105,21 @@ function onGPS(fix) {
layout.maidenhead.label = "Maidenhead: "+maidenhead;
} else {
layout.sat.label = fix.satellites;
nofix = (nofix+1) % 4;
layout.progress.label = ".".repeat(nofix) + " ".repeat(4-nofix);
layout.progress.label = "in view: " + SATinView;
}
layout.render();
}
function onGPSraw(nmea) {
if (nmea.slice(3,6) == "GSV") {
// console.log(nmea);
if (nmea.slice(0,7) == "$BDGSV,") nofBD = Number(nmea.slice(11,13));
if (nmea.slice(0,7) == "$GPGSV,") nofGP = Number(nmea.slice(11,13));
SATinView = nofBD + nofGP;
}
}
Bangle.loadWidgets();
Bangle.drawWidgets();
Bangle.on('GPS', onGPS);
Bangle.on('GPS-raw', onGPSraw);

View File

@ -589,7 +589,7 @@ var locales = {
month: "leden,únor,březen,duben,květen,červen,červenec,srpen,září,říjen,listopad,prosinec",
abday: "ne,po,út,st,čt,pá,so",
day: "neděle,pondělí,úterý,středa,čtvrtek,pátek,sobota",
trans: { yes: "tak", Yes: "Tak", no: "nie", No: "Nie", ok: "ok", on: "na", off: "poza" }
trans: { yes: "ano", Yes: "Ano", no: "ne", No: "Ne", ok: "ok", on: "zap", off: "vyp" }
},
"sl_SI": {
lang: "sl_SI",

View File

@ -41,3 +41,4 @@
0.36: Added 'Utils' menu with helpful utilities for restoring Bangle.js
0.37: Going into passkey menu now saves settings with passkey
0.38: Restructed menus as per forum discussion
0.39: Fix misbehaving debug info option

View File

@ -499,6 +499,8 @@ function showUtilMenu() {
'< Back': ()=>showMainMenu(),
'Debug Info': {
value: E.clip(0|settings.log,0,2),
min: 0,
max: 2,
format: v => ["Hide","Show","Log"][E.clip(0|v,0,2)],
onchange: v => {
settings.log = v;

View File

@ -0,0 +1,8 @@
# Widget Autohide Widget
This widget is forked from the "Widget Visibility Widget"
It should make widgets completely hidden (except for 4 seconds after the watch is unlocked)
Additional features that I want to implement:
- Only show widgets when in app launcher
- Make timeout adjustable
- Disable widgets completely

View File

@ -0,0 +1 @@
0.01: New Widget, forked from widviz

BIN
apps/widviztime/eye.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

53
apps/widviztime/widget.js Normal file
View File

@ -0,0 +1,53 @@
(() => {
var saved = null;
function hide() {
if (!Bangle.isLCDOn() || saved) return;
saved = [];
for (var wd of WIDGETS) {
saved.push({
d: wd.draw,
a: wd.area
});
wd.draw = () => {};
wd.area = "";
}
g.setColor(0, 0, 0);
g.fillRect(0, 0, g.getWidth(), 23);
}
function reveal() {
if (!Bangle.isLCDOn() || !saved) return;
for (var wd of WIDGETS) {
var o = saved.shift();
wd.draw = o.d;
wd.area = o.a;
}
Bangle.drawWidgets();
saved = null;
}
function draw() {
g.setColor(0x07ff);
g.drawImage(atob("GBgBAAAAAAAAAAAAAAAAAH4AAf+AB4HgDgBwHDw4OH4cMOcMYMMGYMMGMOcMOH4cHDw4DgBwB4HgAf+AAH4AAAAAAAAAAAAAAAAA"), this.x, this.y);
}
WIDGETS.viz = {
area: "tl",
width: 24,
draw: draw
};
Bangle.on('lock', (locked) => {
if (!locked) {
reveal();
setTimeout(function() {
hide();
}, 4000);
}
});
})();