UNPKG

@andrei-tatar/node-red-contrib-nibetcp

Version:

Node-red integration for Nibe S series heatpumps using modbus over TCP

142 lines (141 loc) 6.35 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Nibe = void 0; const rxjs_1 = require("rxjs"); const operators_1 = require("rxjs/operators"); const modbus_1 = require("./modbus"); const registers_1 = require("./registers"); const tcp_1 = require("./tcp"); const DEFAULT_TIMEOUT = 1000; class Nibe { static createTcp(address, registerFilePath, logger) { const nibe = tcp_1.Tcp.create(address, logger === null || logger === void 0 ? void 0 : logger.scope("tcp")).pipe((0, operators_1.map)((x) => new modbus_1.Modbus(x)), (0, operators_1.map)((m) => new Nibe(m, registerFilePath))); return nibe; } constructor(modbus, registerFile) { this.modbus = modbus; this.registerFile = registerFile; this.registers$ = (0, rxjs_1.defer)(() => __awaiter(this, void 0, void 0, function* () { return yield (0, registers_1.loadAllRegisters)(this.registerFile); })).pipe((0, operators_1.shareReplay)(1)); } readRegister(labelOrAddress, timeoutMsec) { return this.registers$.pipe((0, operators_1.first)(), (0, operators_1.map)((registers) => { const reg = this.findRegister(labelOrAddress, registers); if (!reg) { throw new Error(`Could not find register '${labelOrAddress}'`); } return reg; }), (0, operators_1.switchMap)((reg) => this.readInternal(reg, timeoutMsec).pipe((0, operators_1.retry)({ count: 3 })))); } writeRegister(labelOrAddress, value, force, timeoutMsec) { return this.registers$.pipe((0, operators_1.first)(), (0, operators_1.map)((registers) => { const reg = this.findRegister(labelOrAddress, registers); if (!reg) { throw new Error(`Could not find register '${labelOrAddress}'`); } if (reg.type !== "MODBUS_HOLDING_REGISTER") { throw new Error(`Readonly register '${labelOrAddress}'`); } return reg; }), (0, operators_1.switchMap)((reg) => this.writeInteral(reg, value, force, timeoutMsec).pipe((0, operators_1.retry)({ count: 3 }))), (0, operators_1.ignoreElements)()); } findRegister(labelOrAddress, list) { const address = typeof labelOrAddress === "number" ? labelOrAddress : parseInt(labelOrAddress, 10); if (!isNaN(address) && address >= 30000) { return list.find((r) => r.address === address); } const match = list.filter((r) => r.label === labelOrAddress); match.sort((a, b) => a.type.localeCompare(b.type)); return match[0]; } writeInteral(register, value, force = false, timeoutMsec = DEFAULT_TIMEOUT) { const raw = value * register.divisionFactor; if (!force && ((register.minValue !== null && raw < register.minValue) || (register.maxValue !== null && raw > register.maxValue))) { throw new Error(`Invalid value for register ${register.address}: ${raw}`); } const data = this.writeValue(register.size, raw); return this.modbus.writeRegisters(register.address % 10000, data, timeoutMsec); } readInternal(register, timeoutMsec = DEFAULT_TIMEOUT) { const registerCount = register.size === "s32" || register.size === "u32" ? 2 : 1; const readKind = register.type === "MODBUS_HOLDING_REGISTER" ? "READ_HOLDING" : "READ_INPUT"; return this.modbus .readRegisters(readKind, register.address % 10000, registerCount, timeoutMsec) .pipe((0, operators_1.map)((v) => { const rawValue = this.readValue(v.data, register.size); const value = { value: rawValue / register.divisionFactor, label: register.label, unit: register.unit, address: register.address, formatted: `${rawValue / register.divisionFactor}${register.unit}`, rawValue, raw: Buffer.from(v.data), }; return value; })); } writeValue(size, value) { let data; switch (size) { case "u8": case "u16": data = Buffer.alloc(2); data.writeUInt16BE(value, 0); break; case "s8": case "s16": data = Buffer.alloc(2); data.writeInt16BE(value, 0); break; case "u32": case "s32": data = Buffer.alloc(4); data.writeUint16BE((value >> 16) & 0xffff, 0); data.writeUint16BE(value & 0xffff, 2); break; default: throw new Error(`Register size not supported '${size}'`); } return data; } readValue(data, size) { switch (size) { case "u8": return data.readUInt16BE(0); case "s8": return data.readInt16BE(0); case "u16": return data.readUInt16BE(0); case "s16": return data.readInt16BE(0); case "u32": const p1 = data.readUint16BE(0); const p2 = data.readUint16BE(2); return (p2 << 16) + p1; case "s32": const p1s = data.readUint16BE(0); const p2s = data.readUint16BE(2); return (p2s << 16) + p1s; default: throw new Error(`Register size not supported '${size}'`); } } } exports.Nibe = Nibe;