@ngreatorex/homie-device
Version:
Homie Device for NodeJS
181 lines • 7.49 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DefaultConfiguration = void 0;
const lodash_1 = __importDefault(require("lodash"));
const mqtt_1 = __importDefault(require("mqtt"));
const framework_1 = require("./framework");
const HomieNode_1 = __importDefault(require("./HomieNode"));
const pkgJson = require("../package.json");
const homieVersion = "3.0.1";
const homieImplName = `nodejs:${pkgJson.name}`;
const homieImplVersion = pkgJson.version;
const DefaultStatsInterval = 60;
exports.DefaultConfiguration = {
friendlyName: "unknown",
ip: null,
mac: null,
mqtt: {
base_topic: "homie",
client: {
host: "localhost",
password: undefined,
port: 1883,
username: undefined,
},
connectionFactory: mqtt_1.default.connect,
},
name: "",
settings: {},
statsInterval: DefaultStatsInterval,
};
class HomieDevice extends framework_1.HomieTopologyRoot {
constructor(config) {
super(HomieDevice.interval$(config));
this.interval$ = null;
this.node = (config) => {
super.assertConfigurable();
this.logger.debug(`Registering new node "${config.friendlyName}" (${config.name})`);
return this.nodes$[config.name] = new HomieNode_1.default(this, config);
};
this.setup = () => {
var _a, _b, _c;
const opts = lodash_1.default.merge({}, (_a = this.config.mqtt) === null || _a === void 0 ? void 0 : _a.client, {
will: {
payload: "lost",
qos: 0,
retain: true,
topic: `${(_b = this.config.mqtt) === null || _b === void 0 ? void 0 : _b.base_topic}/${this.name}/$state`,
},
});
if (!this.config.mqtt || typeof ((_c = this.config.mqtt) === null || _c === void 0 ? void 0 : _c.connectionFactory) !== "function") {
throw new Error("No mqtt.connectionFactory is configured. This really shouldn't happen.");
}
this.mqttClient$ = this.client = this.config.mqtt.connectionFactory(opts);
this.mqttClient$.on("connect", this.onConnect);
this.mqttClient$.on("close", this.onDisconnect);
this.mqttClient$.on("offline", this.onOffline);
this.mqttClient$.on("error", this.onError);
this.mqttClient$.on("message", (topic, payload) => {
this.onMessage(topic, payload ? payload.toString() : null);
});
this.subscribe(`${this.name}/#`);
this.mqttClient$.subscribe(`${this.config.mqtt.base_topic}$broadcast/#`);
this.logger.info(`Homie device ${this.config.friendlyName} (${this.config.name}) connected`);
};
this.end = () => {
if (this.mqttClient$ === null) {
throw new Error("client has not been initialized");
}
this.publishAttribute("state", "disconnected");
this.mqttClient$.end();
};
this.onConnect = () => {
this.logger.verbose("Connected... creating standard publications and subscriptions");
const nodes = [];
lodash_1.default.each(this.nodes$, (node) => {
const text = node.isRange ? `${node.name}[]` : node.name;
nodes.push(text);
node.onConnect();
});
this.publishAttributes({
"fw/name": this.config.firmwareName,
"fw/version": this.config.firmwareVersion,
"homie": homieVersion,
"implementation": homieImplName,
"implementation/version": homieImplVersion,
"localip": this.config.ip,
"mac": this.config.mac,
"name": this.friendlyName,
"nodes": nodes.join(","),
"state": "init",
"stats": "interval,uptime",
});
this.onStatsInterval();
this.interval$ = setInterval(() => {
this.onStatsInterval();
}, this.statsInterval$ * 1000);
super.onConnect();
this.publishAttribute("state", "ready");
};
this.onDisconnect = () => {
super.onDisconnect();
if (this.interval$ != null) {
clearInterval(this.interval$);
}
this.interval$ = null;
lodash_1.default.each(this.nodes$, (node) => {
node.onDisconnect();
});
};
this.onOffline = () => {
super.onOffline();
lodash_1.default.each(this.nodes$, (node) => {
node.onOffline();
});
};
this.onError = (err) => {
super.onError(err);
lodash_1.default.each(this.nodes$, (node) => {
node.onError(err);
});
};
this.onMessage = (topic, msg) => {
const parts = topic.split("/");
const deviceTopic = parts.slice(2).join("/");
this.logger.debug(`received message: parts=${topic}, deviceTopic=${deviceTopic}`);
if (parts[1] === "$broadcast") {
this.logger.debug(`emitting broadcast for ${deviceTopic}: ${msg}`);
this.emit("broadcast", deviceTopic, msg);
return;
}
this.logger.debug(`emitting broadcast for ${deviceTopic}: ${msg}`);
this.emit("message", deviceTopic, msg);
this.emit(`message:${deviceTopic}`, msg);
if (parts[1] === this.name && parts[4] === "set") {
let nodeName = parts[2];
const propName = parts[3];
const value = msg;
const range = { isRange: false, index: 0 };
if (nodeName.includes("_")) {
range.isRange = true;
const nodeParts = nodeName.split("_");
nodeName = nodeParts[0];
range.index = parseInt(nodeParts[1], 10);
}
const node = this.nodes$[nodeName];
if (node && node.isRange === range.isRange) {
const prop = node.getProperty(propName);
if (prop) {
prop.invokeSetter(range, value);
}
}
}
};
this.startTime$ = Date.now();
this.nodes$ = {};
this.statsInterval$ = this.config.statsInterval || DefaultStatsInterval;
this.mqttClient$ = null;
}
static interval$(config) {
if (lodash_1.default.isString(config)) {
config = { name: config, friendlyName: config };
}
return lodash_1.default.merge({}, exports.DefaultConfiguration, config);
}
onStatsInterval() {
super.onStatsInterval();
const uptime = (Date.now() - this.startTime$) / 1000;
this.publishStats({
interval: this.statsInterval$,
uptime: lodash_1.default.round(uptime, 0),
});
lodash_1.default.each(this.nodes$, (node) => {
node.onStatsInterval();
});
}
}
exports.default = HomieDevice;
//# sourceMappingURL=HomieDevice.js.map