Skip to content

Instantly share code, notes, and snippets.

@moritzmhmk
Created October 5, 2023 10:33
Show Gist options
  • Save moritzmhmk/2b7b846af84b485841b302373623bb81 to your computer and use it in GitHub Desktop.
Save moritzmhmk/2b7b846af84b485841b302373623bb81 to your computer and use it in GitHub Desktop.
Read and parse data from the "Lono Thermo Hygro" sensor.
/**
* Copyright 2023 moritzmhmk.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the “Software”),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* ----------------------------------------------------------------------------
*
* The sensor in question is described here:
* https://web.archive.org/web/20231005102046/https://files2.elv.com/public/11/1155/115549/Internet/115549_w030_bda.pdf
*
* The sensor was sold under different brandings (e.g. Lono, Mebus).
*
* The BLE protocol is very simple: The data consists of 5 bytes "TTGCH".
* TT: time in "5-minute-steps" since the measurement
* G: Group of the sensor.
* C: Temperature in degrees celsius + 40 (i.e. 61 = 21°C)
* H: Humidity in percent.
*/
const noble = require("@abandonware/noble");
noble.on("stateChange", async (state) => {
if (state === "poweredOn") {
await noble.startScanningAsync(["180f"], false);
}
});
noble.on("discover", async (peripheral) => {
// console.log(`${peripheral.address} (${peripheral.advertisement.localName})`);
if (peripheral.advertisement.localName === "NGE76") {
console.log(peripheral);
console.log(peripheral.advertisement); // 1809, 180f
await peripheral.connectAsync();
const { services, characteristics } =
await peripheral.discoverAllServicesAndCharacteristicsAsync();
services.forEach((service) => {
console.log(`${service.uuid} (${service.name}, ${service.type}))`);
service.characteristics.forEach((c) => {
console.log(`- ${c.uuid} (${c.name}, ${c.type})) ${c.properties}`);
});
});
const modelNumber = characteristics.find((c) => c.uuid === "2a24");
console.log("Model", (await modelNumber.readAsync()).toString());
const serialNumber = characteristics.find((c) => c.uuid === "2a25");
console.log("Serial", (await serialNumber.readAsync()).toString());
const firmwareRevision = characteristics.find((c) => c.uuid === "2a26");
console.log("Firmware", (await firmwareRevision.readAsync()).toString());
const hardwareRevision = characteristics.find((c) => c.uuid === "2a27");
console.log("Hardware", (await hardwareRevision.readAsync()).toString());
const softwareRevision = characteristics.find((c) => c.uuid === "2a28");
console.log("Software", (await softwareRevision.readAsync()).toString());
const manufacturerName = characteristics.find((c) => c.uuid === "2a29");
console.log(
"Manufacturer",
(await manufacturerName.readAsync()).toString()
);
const batteryLevel = characteristics.find((c) => c.uuid === "2a19");
console.log("Battery", (await batteryLevel.readAsync()).readUint8() + "%");
const temperatureMeasurement = characteristics.find(
(c) => c.uuid === "2a1c"
);
temperatureMeasurement.on("notify", (data) => console.log("notify", data));
temperatureMeasurement.on("data", (data) => {
const FIVE_MINUTES = 5 * 60 * 1000;
const timestamp = new Date(
Math.ceil(Date.now() / FIVE_MINUTES - data.readUInt16BE(0)) *
FIVE_MINUTES
);
const channel = data.readUInt8(2);
const temperature = data.readUInt8(3) - 40;
const humidity = data.readUint8(4);
console.log(
timestamp.toLocaleTimeString(),
"Channel #" + channel,
temperature + "°C",
humidity + "%"
);
});
temperatureMeasurement.subscribe();
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment