forked from FOSS/BangleApps
183 lines
5.5 KiB
JavaScript
183 lines
5.5 KiB
JavaScript
var deviceInfo = {};
|
|
if (Bangle.getAccel().x < -0.7)
|
|
g.setRotation(3); // assume watch in charge cradle
|
|
// Tile sizes
|
|
var TILESIZE = 60;
|
|
// Tiles along width of screen
|
|
var TILEX = 4;
|
|
|
|
// Map devices to nice names...
|
|
var deviceNames = {
|
|
"eb:44:c1:71:2e:89 random" : "Office",
|
|
"c4:7c:8d:6a:ac:79 public" : "Peacelily"
|
|
};
|
|
|
|
var scanHandlers = [
|
|
{ filter : {serviceData:{"fe95":{}}}, // Xiaomi
|
|
handler : function(device) {
|
|
if (!device.serviceData["fe95"]) return;
|
|
var d = new DataView(device.serviceData["fe95"]);
|
|
var frame = d.getUint16(0,true);
|
|
var offset = 5;
|
|
if (frame&16) offset+=6; // mac address
|
|
if (frame&32) offset+=1; // capabilitities
|
|
if (frame&64) { // event
|
|
var l = d.getUint8(offset+2);
|
|
var code = d.getUint16(offset,true);
|
|
if (!deviceInfo[device.id]) deviceInfo[device.id]={id:device.id};
|
|
event = deviceInfo[device.id];
|
|
switch (code) {
|
|
case 0x1004: event.temperature = d.getInt16(offset+3,true)/10; break;
|
|
case 0x1006: event.humidity = d.getInt16(offset+3)/10; break;
|
|
case 0x100D:
|
|
event.temperature = d.getInt16(offset+3,true)/10;
|
|
event.humidity = d.getInt16(offset+5)/10; break;
|
|
case 0x1008: event.moisture = d.getUint8(offset+3); break;
|
|
case 0x1009: event.fertility = d.getUint16(offset+3,true)/10; break;
|
|
// case 0x1007: break; // 3 bytes? got 84,0,0 or 68,0,0
|
|
default: event.code = code;
|
|
event.raw = new Uint8Array(d.buffer, offset+3, l);
|
|
break;
|
|
}
|
|
}}}, {
|
|
filter : {serviceData:{"1809":{}}}, // Standard Bluetooth
|
|
handler : function(device) {
|
|
if (!device.serviceData["1809"]) return;
|
|
var d = new DataView(device.serviceData["1809"]);
|
|
if (!deviceInfo[device.id]) deviceInfo[device.id]={id:device.id,name:device.name};
|
|
event = deviceInfo[device.id];
|
|
event.temperature = d.getInt16(0,1)/100;
|
|
}}, {
|
|
filter : { manufacturerData:{0x0590:{}} }, // Espruino
|
|
handler : function(device) {
|
|
if (!device.manufacturerData) return;
|
|
var j;
|
|
try { j = JSON.parse(E.toString(device.manufacturerData)); }
|
|
catch (e) { return; } // not JSON
|
|
if (!deviceInfo[device.id]) deviceInfo[device.id]={id:device.id,name:device.name};
|
|
event = deviceInfo[device.id];
|
|
if (j.t) event.temperature = j.t;
|
|
if (j.a) event.alert = j.a;
|
|
}}
|
|
];
|
|
|
|
function getImgHum() {
|
|
return require("heatshrink").decompress(atob("jUoxH+AEtlsoYYDS4ZYDAYaVDLAYFDSQYHDSIZYDBIaPDLAYLDRoZYDBoaLDLAYPDRIZYDCIaHDLAYTDQoZYDCoaDDOQYXAA+JxIYX1utDSwYBAAIzYGiwZUTgpODQpzPGGgY3OdI4aRDIIaMDJIYCDIztDGRwaJP5oaWDAwaRDBAbOC5YcKB5I="));
|
|
}
|
|
function getImgTemp() {
|
|
return require("heatshrink").decompress(atob("iUqxH+AA2sAAQLHCBASMCAoSLCPOBAAQRfI/5Hn3YACy4ACCL4ADCL5H/I/AQHCRAQJCQwQLCQgQNCQYRQCB4A/ADaPjYqTpSCRYQGCZALFA"));
|
|
}
|
|
|
|
function drawAlert(tile,x,y) {
|
|
g.setFont("Vector",56).setFontAlign(0,0);
|
|
g.drawString("!",x+TILESIZE/2,y+10+TILESIZE/2);
|
|
}
|
|
|
|
function drawMoisture(tile,x,y) {
|
|
g.drawImage(getImgHum(),x+2,y+18);
|
|
g.setFont("Vector",28);
|
|
g.drawString(tile.device.moisture,x+26,y+12);
|
|
}
|
|
|
|
function drawTemperature(tile,x,y) {
|
|
g.drawImage(getImgTemp(),x+3,y+16);
|
|
g.setFont("Vector",30);
|
|
var t = Math.round(tile.device.temperature);
|
|
g.drawString(t,x+25,y+13);
|
|
}
|
|
|
|
function getTiles() {
|
|
var tiles = [];
|
|
Object.keys(deviceInfo).forEach(id=>{
|
|
var dev = deviceInfo[id];
|
|
if (dev.alert) {
|
|
tiles.push({
|
|
alert: true, device: dev,
|
|
draw: drawAlert
|
|
});
|
|
}
|
|
if (dev.moisture && dev.moisture<40) {
|
|
tiles.push({
|
|
alert: true, device: dev,
|
|
draw: drawMoisture
|
|
});
|
|
}
|
|
if (dev.temperature) {
|
|
tiles.push({
|
|
device: dev,
|
|
draw: drawTemperature
|
|
});
|
|
}
|
|
});
|
|
tiles.sort((a,b)=>(b.alert|0)-(a.alert|0))
|
|
return tiles;
|
|
}
|
|
|
|
|
|
g.clear();
|
|
require("Font7x11Numeric7Seg").add(Graphics);
|
|
function drawClock() {
|
|
var d = new Date();
|
|
var size = 3;
|
|
var x = (g.getWidth()/2) - size*6,
|
|
y = size;
|
|
g.reset();
|
|
g.setFont("7x11Numeric7Seg",size).setFontAlign(1,-1);
|
|
g.drawString(d.getHours(), x, y, true);
|
|
g.setFontAlign(-1,-1);
|
|
if (d.getSeconds()&1) g.drawString(":", x,y);
|
|
g.drawString(("0"+d.getMinutes()).substr(-2),x+size*4,y, true);
|
|
// draw seconds
|
|
g.setFont("7x11Numeric7Seg",size/2);
|
|
g.drawString(("0"+d.getSeconds()).substr(-2),x+size*18,y + size*7, true);
|
|
// date
|
|
var s = d.toString().split(" ").slice(0,4).join(" ");
|
|
g.reset().setFontAlign(0,-1);
|
|
g.drawString(s,g.getWidth()/2, y + size*12, true);
|
|
// keep screen on
|
|
g.flip();
|
|
}
|
|
function drawTiles() {
|
|
// draw tiles
|
|
var tiles = getTiles();
|
|
for (var i=0;i<6;i++) {
|
|
var x = (i%TILEX)*TILESIZE;
|
|
var y = TILESIZE + TILESIZE*((i/TILEX)|0);
|
|
g.reset();
|
|
var tile = tiles[i];
|
|
if (tile && tile.alert) {
|
|
g.setBgColor(0.5,0,0);
|
|
}
|
|
g.clearRect(x,y,x+TILESIZE-1,y+TILESIZE-1);
|
|
if (tile) {
|
|
g.reset().setFont("6x8");
|
|
var t = deviceNames[tile.device.id];
|
|
if (!t) t = tile.device.name || tile.device.id.substr(0,17);
|
|
g.drawString(t,x+2,y+2);
|
|
tile.draw(tile, x, y);
|
|
if (tile.alert) {
|
|
g.setColor(1,1,0);
|
|
g.drawRect(x,y,x+TILESIZE-1,y+TILESIZE-1);
|
|
}
|
|
}
|
|
}
|
|
g.flip(); // keep forcing display on
|
|
}
|
|
|
|
setInterval(drawClock, 1000);
|
|
setInterval(drawTiles, 10000);
|
|
drawClock();
|
|
drawTiles();
|
|
|
|
function parseDevice(dev) {
|
|
if (!dev.serviceData) dev.serviceData={};
|
|
scanHandlers.forEach(s=>s.handler(dev));
|
|
}
|
|
NRF.setScan(parseDevice, { filters: scanHandlers.map(s=>s.filter), timeout: 2000 });
|
|
|
|
if (Bangle.isCharging()) {
|
|
Bangle.on("charging", isCharging => {
|
|
if (!isCharging) load();
|
|
});
|
|
}
|