mirror of https://github.com/espruino/BangleApps
Add Xiaomi Mijia temperature sensor display
parent
8425f0b50a
commit
67e269486b
|
@ -0,0 +1 @@
|
|||
0.01: Create mitherm app with support for pvvx firmware only
|
|
@ -0,0 +1,22 @@
|
|||
Reads BLE advertisement data from Xiaomi temperature/humidity sensors running the
|
||||
`pvvx` custom firmware (https://github.com/pvvx/ATC_MiThermometer).
|
||||
|
||||
## Features
|
||||
|
||||
* Display temperature
|
||||
* Display humidity
|
||||
* Display battery state of sensor
|
||||
* Auto-refresh every 5 minutes
|
||||
* Manual refresh on demand
|
||||
* Add aliases for MAC addresses to easily recognise devices
|
||||
|
||||
## Planned features
|
||||
|
||||
* Supprt for other advertising formats:
|
||||
* atc1441 format
|
||||
* BTHome
|
||||
* Xiaomi Mijia format
|
||||
* Configurable auto-refresh interval
|
||||
* Configurable scan length (currently 30s)
|
||||
* Alerts when temperature outside defined limits (with a widget or bootcode to
|
||||
work when app is inactive)
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwhC/AH4Ac5gWVhnM4AWVAAIYTCwQABCywYRIoYADJJwWHDB4RD5sz7hJPFIlP//0MRxFE6f/AAM9JJgWE4gWCAANMDBZcEn4XE+ZiKFwhcBCYPdDYRiEGAoXDLgf97vfMQwXILggXFMQYXHLgoXB6czMQoXHLgQXJMQQXG4YWEI44ABngXGh4XHF4v/+DAGC6DXGC5BHGC509F4IXTdwIABV4gXOIwIABJAoX/C6p3Xa4a/UABAXfgczABswC/4XmAH4A/ABY"))
|
|
@ -0,0 +1,172 @@
|
|||
var filterTemperature = [{
|
||||
serviceData: {
|
||||
"181a": {}
|
||||
}
|
||||
}];
|
||||
var results = {};
|
||||
var macs = [];
|
||||
|
||||
var aliases = require("Storage").readJSON("mitherm.json", true);
|
||||
if (!aliases) aliases = {};
|
||||
|
||||
var lastSeen = {};
|
||||
var current = 0;
|
||||
var scanning = false;
|
||||
var timeoutDraw;
|
||||
var timeoutScan;
|
||||
|
||||
|
||||
const scan = function() {
|
||||
if (!scanning) { // Don't start scanning if already doing so.
|
||||
scanning = true;
|
||||
if (timeoutScan) clearTimeout(timeoutScan);
|
||||
timeoutScan = setTimeout(scan, 300000); // Scan again in 5 minutes.
|
||||
drawScanState(scanning);
|
||||
NRF.findDevices(function(devices) {
|
||||
onDevices(devices);
|
||||
}, {
|
||||
filters: filterTemperature,
|
||||
timeout: 30000 // Scan for 30s
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const onDevices = function(devices) {
|
||||
let now = Date.now();
|
||||
for (let i = 0; i < devices.length; i++) {
|
||||
let device = devices[i];
|
||||
|
||||
let processedData = extractData(device.data);
|
||||
console.log({
|
||||
rssi: device.rssi,
|
||||
data: processedData
|
||||
});
|
||||
if (!macs.includes(processedData.MAC)) {
|
||||
macs.push(processedData.MAC);
|
||||
}
|
||||
results[processedData.MAC] = processedData;
|
||||
lastSeen[processedData.MAC] = now;
|
||||
}
|
||||
console.log("Scan complete.");
|
||||
scanning = false;
|
||||
writeOutput();
|
||||
};
|
||||
|
||||
|
||||
const extractData = function(thedata) {
|
||||
let data = DataView(thedata);
|
||||
let MAC = [];
|
||||
for (let i = 9; i > 3; i--) {
|
||||
MAC.push(data.getUint8(i, true).toString(16).padStart(2, "0"));
|
||||
}
|
||||
out = {
|
||||
size: data.getUint8(0, true),
|
||||
uid: data.getUint8(1, true),
|
||||
UUID: data.getUint16(2, true),
|
||||
MAC: MAC.join(":"),
|
||||
temperature: data.getInt16(10, true) * 0.01,
|
||||
humidity: data.getUint16(12, true) * 0.01,
|
||||
battery_mv: data.getUint16(14, true),
|
||||
battery_level: data.getUint8(16, true),
|
||||
};
|
||||
return out;
|
||||
};
|
||||
|
||||
|
||||
const writeOutput = function() {
|
||||
let now = Date.now();
|
||||
if (timeoutDraw) clearTimeout(timeoutDraw);
|
||||
timeoutDraw = setTimeout(writeOutput, 60000); // Refresh in 1 minute.
|
||||
g.clear(true);
|
||||
Bangle.drawWidgets();
|
||||
g.reset();
|
||||
drawScanState(scanning);
|
||||
|
||||
if (macs.length == 0) return;
|
||||
|
||||
processedData = results[macs[current]];
|
||||
g.setFont12x20(2);
|
||||
g.drawString(`${processedData.temperature.toFixed(2)}°C`, 10, 30);
|
||||
g.drawString(`${processedData.humidity.toFixed(2)} %`, 10, 70);
|
||||
|
||||
g.setFont6x15();
|
||||
g.drawString(`${((now - lastSeen[macs[current]]) / 60000).toFixed(0)} min ago`, 10, 130);
|
||||
g.drawString(`${processedData.battery_level} % battery`, 80, 130);
|
||||
g.drawString(` ${processedData.MAC in aliases ? aliases[processedData.MAC] : processedData.MAC}: ${current + 1} / ${macs.length}`, 10, 150);
|
||||
};
|
||||
|
||||
|
||||
const scrollDevices = function(directionLR) {
|
||||
// Swipe left or right to move between devices.
|
||||
current -= directionLR; // inverted feels a more familiar gesture.
|
||||
if (current + 1 > macs.length)
|
||||
current = 0;
|
||||
if (current < 0)
|
||||
current = macs.length - 1;
|
||||
writeOutput();
|
||||
};
|
||||
|
||||
const drawScanState = function(state) {
|
||||
if (state)
|
||||
g.fillRect(160, 160, 170, 170);
|
||||
else
|
||||
g.clearRect(160, 160, 170, 170);
|
||||
};
|
||||
|
||||
const setAlias = function(mac, alias) {
|
||||
if (alias === "") {
|
||||
delete aliases[mac];
|
||||
}
|
||||
else {
|
||||
aliases[mac] = alias;
|
||||
require("Storage").writeJSON("mitherm.json", aliases);
|
||||
}
|
||||
};
|
||||
|
||||
const changeAlias = function(mac) {
|
||||
g.clear();
|
||||
require("textinput").input((mac in aliases) ? aliases[mac] : "").then(function(text) {
|
||||
setAlias(mac, text);
|
||||
setUI();
|
||||
writeOutput();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const setUI = function() {
|
||||
Bangle.setUI({
|
||||
mode: "custom",
|
||||
swipe: scrollDevices,
|
||||
btn: function() {
|
||||
E.showMenu(actionsMenu);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const actionsMenu = {
|
||||
"": {
|
||||
"title": "-- Actions --",
|
||||
"back": function() {
|
||||
E.showMenu();
|
||||
},
|
||||
"remove": function() {
|
||||
setUI();
|
||||
writeOutput();
|
||||
},
|
||||
},
|
||||
"Scan now": function() {
|
||||
scan();
|
||||
E.showMenu();
|
||||
},
|
||||
"Edit alias": function() {
|
||||
changeAlias(macs[current]);
|
||||
},
|
||||
};
|
||||
|
||||
setUI();
|
||||
Bangle.loadWidgets();
|
||||
g.setClipRect(Bangle.appRect);
|
||||
scan();
|
||||
writeOutput();
|
Binary file not shown.
After Width: | Height: | Size: 863 B |
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"id": "mitherm",
|
||||
"name": "Xiaomi Mijia Temperature and Humidity display",
|
||||
"shortName": "MiTherm",
|
||||
"version": "0.01",
|
||||
"description": "Reads and displays data from Xiaomi temperature/humidity sensors running custom firmware",
|
||||
"icon": "app.png",
|
||||
"tags": "xiaomi,mi,ble,bluetooth,thermometer,humidity",
|
||||
"readme": "README.md",
|
||||
"supports": ["BANGLEJS", "BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"mitherm.app.js","url":"app.js"},
|
||||
{"name":"mitherm.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue