UNPKG

@chrispyduck/homie-sensors

Version:

Sensor library intended for ues with homie-device

227 lines 9.11 kB
"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