@chrispyduck/homie-sensors
Version:
Sensor library intended for ues with homie-device
227 lines • 9.11 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var AHT20_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.I2C_ADDRESS = void 0;
const homie_device_1 = require("@chrispyduck/homie-device");
const lodash_1 = require("lodash");
const I2CDevice_1 = require("./I2CDevice");
const ISensor_1 = require("../ISensor");
exports.I2C_ADDRESS = 0x38;
const commands = {
status: {
delay: 200,
id: 0x71,
},
init: {
delay: 10,
id: 0xBE,
args: Buffer.from([0x08, 0x00]),
},
measure: {
args: Buffer.from([0x33, 0x00]),
delay: 80,
id: 0xAC,
},
reset: {
delay: 20,
id: 0xBA,
}
};
const COMMAND_STATUS_BIT_CALIBRATION_ENABLE = 3;
const COMMAND_STATUS_BIT_BUSY = 7;
var State;
(function (State) {
State[State["Unknown"] = 0] = "Unknown";
State[State["Idle"] = 1] = "Idle";
State[State["ReceiveStatus"] = 10] = "ReceiveStatus";
State[State["ReceiveMeasurement"] = 11] = "ReceiveMeasurement";
State[State["Reset"] = 99] = "Reset";
})(State || (State = {}));
let AHT20 = AHT20_1 = class AHT20 extends I2CDevice_1.I2CDevice {
constructor(config) {
super("AHT20", lodash_1.merge({}, AHT20_1.DefaultConfiguration, config));
this.humidity$ = 0;
this.temperature$ = 0;
this.lastReset = new Date().getTime();
this.zeroReads = 0;
this.buffer = Buffer.alloc(8);
this.state$ = State.Unknown;
this.setState = (value) => {
if (value === this.state$)
return;
this.state$ = value;
this.emit("state", value);
};
this.onInit = async () => {
const status = await this.queryStatus();
if (!status.calibrationEnabled)
await this.initializeDevice();
await this.read();
};
this.reeset = async () => {
this.lastReset = new Date().getTime();
this.logger.verbose("Resetting AHT20");
this.setState(State.Reset);
await this.sendCommand(commands.reset);
await this.onInit();
};
this.register = (device) => {
const node = device.node({
name: "aht20",
friendlyName: "AHT20",
type: "sensor",
isRange: false,
});
const temperatureProperty = node.addProperty({
dataType: homie_device_1.PropertyDataType.float,
name: "temperature",
friendlyName: "Temperature",
settable: false,
format: "-20:120",
unit: "°F",
retained: true,
});
this.on("temperature", (t) => {
temperatureProperty.publishValue(t);
});
const humidityProperty = node.addProperty({
dataType: homie_device_1.PropertyDataType.float,
name: "humidity",
friendlyName: "Humidity",
settable: false,
format: "0:100",
unit: "%",
retained: true,
});
this.on("humidity", (h) => {
humidityProperty.publishValue(h);
});
};
this.queryStatus = async () => {
this.logger.verbose("Requesting device status");
this.setState(State.ReceiveStatus);
await this.sendCommand(commands.status);
return this.readStatus(State.Idle);
};
this.readStatus = async (stateIfIdle = State.Idle) => {
const readResult = await this.bus.i2cRead(exports.I2C_ADDRESS, 1, this.buffer);
if (readResult.bytesRead !== 1)
throw new Error(`Read an unexpected number of bytes in response. Got ${readResult.bytesRead} but expected 1.`);
const byte = readResult.buffer.readUInt8(0);
const result = {
busy: (byte & (1 << COMMAND_STATUS_BIT_BUSY)) > 0,
calibrationEnabled: (byte & (1 << COMMAND_STATUS_BIT_CALIBRATION_ENABLE)) > 0,
};
if (!result.busy)
this.setState(stateIfIdle);
this.logger.debug(`Received status response ${byte} (busy=${result.busy}, calibrationEnabled=${result.calibrationEnabled})`);
return result;
};
this.initializeDevice = async () => {
if (!this.bus)
throw new Error("Bus has not been opened. Did you forget to call init()?");
this.logger.verbose("Sending initialization command to AHT20");
await this.sendCommand(commands.init);
await this.delay(20);
super.emit("init");
};
this.read = async () => {
this.logger.debug("Requesting measurement");
this.setState(State.ReceiveStatus);
await this.sendCommand(commands.measure);
let status = await this.readStatus(State.ReceiveMeasurement);
while (status.busy) {
await this.delay(10);
status = await this.readStatus(State.ReceiveMeasurement);
}
const readResult = await this.bus.i2cRead(this.configuration.deviceId, 7, this.buffer);
if (readResult.bytesRead < 5) {
throw new Error(`Sensor returned ${readResult.bytesRead} bytes, but we need at least 5`);
}
let hum = readResult.buffer[1];
hum <<= 8;
hum |= readResult.buffer[2];
hum <<= 4;
hum |= readResult.buffer[3] >> 4;
hum = (hum * 100) / 0x100000;
this.humidity$ = hum;
let temp = readResult.buffer[3] & 0x0F;
temp <<= 8;
temp |= readResult.buffer[4];
temp <<= 8;
temp |= readResult.buffer[5];
temp = (temp * 200 / 0x100000) - 50;
this.temperature$ = temp * 1.8 + 32;
const crc_received = readResult.buffer[6];
const crc_computed = this.crc8x_simple(readResult.buffer, 6);
if (crc_received != crc_computed) {
this.logger.warn(`Received invalid data from sensor (crc received=${crc_received}, computed=${crc_computed}): ${readResult.buffer.toString("hex")}`);
}
else if (this.humidity$ == 0 && this.temperature$ == 0) {
this.zeroReads++;
const now = new Date().getTime();
const secondsSinceLastReset = (now - this.lastReset) / 1000;
if (secondsSinceLastReset >= 90 && this.zeroReads >= 5) {
this.logger.info(`Initiating automatic programmatic reset of AHT20 due to ${this.zeroReads} continuous all-zero readings`);
}
else {
this.logger.debug(`Received all-zero reading; count=${this.zeroReads}, secondsSinceLastReset=${secondsSinceLastReset}`);
}
}
else {
this.zeroReads = 0;
this.emit("humidity", this.humidity$);
this.emit("temperature", this.temperature$);
}
this.setState(State.Idle);
const returnValue = {
temperature: this.temperature$,
humidity: this.humidity$,
};
this.emit("read", returnValue);
return returnValue;
};
this.crc8x_simple = (input, length) => {
let crc = 0xFF;
for (let i = 0; i < length; i++) {
crc ^= input[i];
for (let k = 0; k < 8; k++)
crc = crc & 0x80 ? (crc << 1) ^ 0x31 : crc << 1;
}
crc &= 0xff;
return crc;
};
}
get humidity() { return this.humidity$; }
get temperature() { return this.temperature$; }
get state() {
return this.state$;
}
};
AHT20.DefaultConfiguration = {
type: "i2c",
model: "AHT20",
node: {
name: "AHT20",
friendlyName: "AHT20",
type: "Sensor",
isRange: false,
},
busNumber: 1,
deviceId: exports.I2C_ADDRESS
};
AHT20 = AHT20_1 = __decorate([
ISensor_1.staticImplements(),
__metadata("design:paramtypes", [Object])
], AHT20);
exports.default = AHT20;
//# sourceMappingURL=AHT20.js.map