-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -82,7 +82,6 @@ out | |
|
||
# Nuxt.js build / generate output | ||
.nuxt | ||
dist | ||
|
||
# Gatsby files | ||
.cache/ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
export declare const decodeH5074Values: (streamUpdate: string) => { | ||
battery: number; | ||
humidity: number; | ||
tempInC: number; | ||
tempInF: number; | ||
}; | ||
export declare const decodeH5075Values: (streamUpdate: string) => { | ||
battery: number; | ||
humidity: number; | ||
tempInC: number; | ||
tempInF: number; | ||
}; | ||
export declare const decodeH5101Values: (streamUpdate: string) => { | ||
battery: number; | ||
humidity: number; | ||
tempInC: number; | ||
tempInF: number; | ||
}; | ||
export declare const decodeH5179Values: (streamUpdate: string) => { | ||
battery: number; | ||
humidity: number; | ||
tempInC: number; | ||
tempInF: number; | ||
}; | ||
export declare const decodeAny: (streamUpdate: string) => { | ||
battery: number; | ||
humidity: number; | ||
tempInC: number; | ||
tempInF: number; | ||
}; | ||
//# sourceMappingURL=decode.d.ts.map |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.decodeAny = exports.decodeH5179Values = exports.decodeH5101Values = exports.decodeH5075Values = exports.decodeH5074Values = void 0; | ||
const validation_1 = require("./validation"); | ||
exports.decodeH5074Values = (streamUpdate) => { | ||
// inspired by https://github.com/Home-Is-Where-You-Hang-Your-Hack/sensor.goveetemp_bt_hci/blob/master/custom_components/govee_ble_hci/govee_advertisement.py#L116 | ||
const temp_lsb = streamUpdate | ||
.substring(8, 10) | ||
.concat(streamUpdate.substring(6, 8)); | ||
const hum_lsb = streamUpdate | ||
.substring(12, 14) | ||
.concat(streamUpdate.substring(10, 12)); | ||
const tempInC = twos_complement(parseInt(temp_lsb, 16)) / 100; | ||
const tempInF = (tempInC * 9) / 5 + 32; | ||
const humidity = parseInt(hum_lsb, 16) / 100; | ||
const battery = parseInt(streamUpdate.substring(14, 16), 16); | ||
return { | ||
battery, | ||
humidity, | ||
tempInC, | ||
tempInF, | ||
}; | ||
}; | ||
exports.decodeH5075Values = (streamUpdate) => { | ||
// TODO would be great to find a way to validate | ||
const encodedData = parseInt(streamUpdate.substring(6, 12), 16); | ||
const battery = parseInt(streamUpdate.substring(12, 14), 16); | ||
const tempInC = encodedData / 10000; | ||
const tempInF = (tempInC * 9) / 5 + 32; | ||
const humidity = (encodedData % 1000) / 10; | ||
return { | ||
battery, | ||
humidity, | ||
tempInC, | ||
tempInF, | ||
}; | ||
}; | ||
exports.decodeH5101Values = (streamUpdate) => { | ||
// TODO would be great to find a way to validate | ||
const encodedData = parseInt(streamUpdate.substring(8, 14), 16); | ||
const battery = parseInt(streamUpdate.substring(14, 16), 16); | ||
const tempInC = encodedData / 10000; | ||
const tempInF = (tempInC * 9) / 5 + 32; | ||
const humidity = (encodedData % 1000) / 10; | ||
return { | ||
battery, | ||
humidity, | ||
tempInC, | ||
tempInF, | ||
}; | ||
}; | ||
exports.decodeH5179Values = (streamUpdate) => { | ||
// TODO would be great to find a way to validate | ||
const temp_lsb = streamUpdate | ||
.substring(14, 16) | ||
.concat(streamUpdate.substring(16, 18)); | ||
const hum_lsb = streamUpdate | ||
.substring(18, 20) | ||
.concat(streamUpdate.substring(16, 18)); | ||
const tempInC = twos_complement(parseInt(temp_lsb, 16)) / 100; | ||
const tempInF = (tempInC * 9) / 5 + 32; | ||
const humidity = parseInt(hum_lsb, 16) / 100; | ||
const battery = parseInt(streamUpdate.substring(20, 22), 16); | ||
return { | ||
battery, | ||
humidity, | ||
tempInC, | ||
tempInF, | ||
}; | ||
}; | ||
exports.decodeAny = (streamUpdate) => { | ||
if (validation_1.isHt5074(streamUpdate)) { | ||
return exports.decodeH5074Values(streamUpdate); | ||
} | ||
if (validation_1.isHt5075(streamUpdate)) { | ||
return exports.decodeH5075Values(streamUpdate); | ||
} | ||
if (validation_1.isHt5101(streamUpdate)) { | ||
return exports.decodeH5101Values(streamUpdate); | ||
} | ||
if (validation_1.isHt5179(streamUpdate)) { | ||
return exports.decodeH5179Values(streamUpdate); | ||
} | ||
throw new Error("Unsupported stream update: " + streamUpdate); | ||
}; | ||
function twos_complement(n, w = 16) { | ||
// Adapted from: https://stackoverflow.com/a/33716541. | ||
if (n & (1 << (w - 1))) { | ||
n = n - (1 << w); | ||
} | ||
return n; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export {}; | ||
//# sourceMappingURL=decode.spec.d.ts.map |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const decode_1 = require("./decode"); | ||
const validationMatrix_1 = require("./validationMatrix"); | ||
it("should decode H5074 values", () => { | ||
// sources: https://github.com/wcbonner/GoveeBTTempLogger/blob/master/goveebttemplogger.cpp#L206 | ||
// https://github.com/neilsheps/GoveeTemperatureAndHumidity | ||
// 88EC00 0902 CD15 64 02 (Temp) 41.378°F (Humidity) 55.81% (Battery) 100% | ||
// 5.21 C | ||
const hex = "88EC000902CD156402"; | ||
const expectedReading = { | ||
battery: 100, | ||
humidity: 55.81, | ||
tempInC: 5.21, | ||
tempInF: 41.378, | ||
}; | ||
const reading = decode_1.decodeH5074Values(hex); | ||
expect(reading).toMatchObject(expectedReading); | ||
}); | ||
it("should decode H5075 values", () => { | ||
const hex = "88ec000368d15800"; | ||
const expectedReading = { | ||
battery: 88, | ||
humidity: 44.1, | ||
tempInC: 22.3441, | ||
tempInF: 72.21938, | ||
}; | ||
const reading = decode_1.decodeH5075Values(hex); | ||
expect(reading).toMatchObject(expectedReading); | ||
}); | ||
it("should decode H5101 values", () => { | ||
const hex = "0100010103165564"; | ||
const expectedReading = { | ||
battery: 100, | ||
humidity: 32.5, | ||
tempInC: 20.2325, | ||
tempInF: 68.41850000000001, | ||
}; | ||
const reading = decode_1.decodeH5101Values(hex); | ||
expect(reading).toMatchObject(expectedReading); | ||
}); | ||
it("should decode H5179 values", () => { | ||
const hex = "0188ec0001012e09740e64"; | ||
const expectedReading = { | ||
battery: 100, | ||
humidity: 37, | ||
tempInC: 24.2, | ||
tempInF: 75.56, | ||
}; | ||
const reading = decode_1.decodeH5179Values(hex); | ||
expect(reading).toMatchObject(expectedReading); | ||
}); | ||
describe("test matrix", () => { | ||
test("decode from any supported device", () => { | ||
validationMatrix_1.validationMatrix.forEach((x) => { | ||
const decodedResult = decode_1.decodeAny(x.mfgData); | ||
expect(decodedResult).toMatchSnapshot(); | ||
}); | ||
}); | ||
describe("pass heuristics", () => { | ||
let decodedResults = {}; | ||
beforeAll(() => { | ||
validationMatrix_1.validationMatrix.forEach((x) => { | ||
decodedResults[x.mfgData] = decode_1.decodeAny(x.mfgData); | ||
}); | ||
}); | ||
test("battery between 0 and 100", () => { | ||
validationMatrix_1.validationMatrix.forEach((x) => { | ||
const battery = decodedResults[x.mfgData].battery; | ||
expect(battery).toBeGreaterThanOrEqual(0); | ||
expect(battery).toBeLessThanOrEqual(100); | ||
}); | ||
}); | ||
test("humidity between 0 and 100", () => { | ||
validationMatrix_1.validationMatrix.forEach((x) => { | ||
const humidity = decodedResults[x.mfgData].humidity; | ||
expect(humidity).toBeGreaterThanOrEqual(0); | ||
expect(humidity).toBeLessThanOrEqual(100); | ||
}); | ||
}); | ||
test("temperature between -20F and 140F", () => { | ||
validationMatrix_1.validationMatrix.forEach((x) => { | ||
const tempInF = decodedResults[x.mfgData].tempInF; | ||
expect(tempInF).toBeGreaterThanOrEqual(-20); | ||
expect(tempInF).toBeLessThanOrEqual(140); | ||
}); | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export {}; | ||
//# sourceMappingURL=example.d.ts.map |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const index_1 = require("./index"); | ||
index_1.debug(true); | ||
console.log("=== start discovery"); | ||
index_1.startDiscovery((reading) => { | ||
console.log(reading); | ||
}); | ||
setTimeout(async () => { | ||
await index_1.stopDiscovery(); | ||
console.log("=== stop discovery"); | ||
}, 60000); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
export declare type GoveeReading = { | ||
uuid: string; | ||
address: string; | ||
model: string; | ||
tempInC: number; | ||
tempInF: number; | ||
humidity: number; | ||
battery: number; | ||
rssi: number; | ||
}; | ||
//# sourceMappingURL=goveeReading.d.ts.map |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { GoveeReading } from "./goveeReading"; | ||
export declare const debug: (on: boolean) => void; | ||
export declare const startDiscovery: (callback: (reading: GoveeReading) => void) => Promise<void>; | ||
export declare const stopDiscovery: () => Promise<void>; | ||
export declare const registerScanStart: (callback: Function) => void; | ||
export declare const registerScanStop: (callback: Function) => void; | ||
export * from "./goveeReading"; | ||
//# sourceMappingURL=index.d.ts.map |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.registerScanStop = exports.registerScanStart = exports.stopDiscovery = exports.startDiscovery = exports.debug = void 0; | ||
const noble_1 = __importDefault(require("@abandonware/noble")); | ||
const validation_1 = require("./validation"); | ||
const decode_1 = require("./decode"); | ||
process.env.NOBLE_REPORT_ALL_HCI_EVENTS = "1"; // needed on Linux including Raspberry Pi | ||
const h5075_uuid = "ec88"; | ||
const h5101_uuid = "0001"; | ||
let DEBUG = false; | ||
let discoverCallback; | ||
let scanStartCallback; | ||
let scanStopCallback; | ||
noble_1.default.on("discover", async (peripheral) => { | ||
const { id, uuid, address, state, rssi, advertisement } = peripheral; | ||
if (DEBUG) { | ||
console.log("discovered", id, uuid, address, state, rssi); | ||
} | ||
if (!validation_1.isValidPeripheral(peripheral)) { | ||
if (DEBUG) { | ||
let mfgData; | ||
if (advertisement.manufacturerData) { | ||
mfgData = advertisement.manufacturerData.toString("hex"); | ||
} | ||
console.log(`invalid peripheral, manufacturerData=[${mfgData}]`); | ||
} | ||
return; | ||
} | ||
const { localName, manufacturerData } = advertisement; | ||
const streamUpdate = manufacturerData.toString("hex"); | ||
if (DEBUG) { | ||
console.log(`${id}: ${streamUpdate}`); | ||
} | ||
const decodedValues = decode_1.decodeAny(streamUpdate); | ||
const current = { | ||
uuid, | ||
address, | ||
model: localName, | ||
battery: decodedValues.battery, | ||
humidity: decodedValues.humidity, | ||
tempInC: decodedValues.tempInC, | ||
tempInF: decodedValues.tempInF, | ||
rssi, | ||
}; | ||
if (discoverCallback) { | ||
discoverCallback(current); | ||
} | ||
}); | ||
noble_1.default.on("scanStart", () => { | ||
if (DEBUG) { | ||
console.log("scanStart"); | ||
} | ||
if (scanStartCallback) { | ||
scanStartCallback(); | ||
} | ||
}); | ||
noble_1.default.on("scanStop", () => { | ||
if (DEBUG) { | ||
console.log("scanStop"); | ||
} | ||
if (scanStopCallback) { | ||
scanStopCallback(); | ||
} | ||
}); | ||
exports.debug = (on) => { | ||
DEBUG = on; | ||
}; | ||
exports.startDiscovery = async (callback) => { | ||
discoverCallback = callback; | ||
await noble_1.default.startScanningAsync([h5075_uuid, h5101_uuid], true); | ||
}; | ||
exports.stopDiscovery = async () => { | ||
await noble_1.default.stopScanningAsync(); | ||
discoverCallback = undefined; | ||
scanStartCallback = undefined; | ||
scanStopCallback = undefined; | ||
}; | ||
exports.registerScanStart = (callback) => { | ||
scanStartCallback = callback; | ||
}; | ||
exports.registerScanStop = (callback) => { | ||
scanStopCallback = callback; | ||
}; | ||
__exportStar(require("./goveeReading"), exports); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
export declare type StreamReading = { | ||
battery: number; | ||
humidity: number; | ||
tempInC: number; | ||
tempInF: number; | ||
}; | ||
//# sourceMappingURL=streamReading.d.ts.map |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); |