homebridge-smartsystem
Version:
SmartServer (Proxy Websockets to TCP sockets, Smappee MQTT, Duotecno IP Nodes, Homekit interface)
403 lines • 16.1 kB
JavaScript
"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.System = void 0;
const master_1 = require("./master");
const logger_1 = require("./logger");
const protocol_1 = require("./protocol");
const base_1 = require("../server/base");
const events_1 = require("events");
class System extends base_1.Base {
constructor() {
super("system");
this.isBrowser = true;
this.isSplitted = false;
this.moods = [];
this.controls = [];
this.temperatures = [];
this.stores = [];
this.scenes = [];
this.trigger = null;
this.emitter = new events_1.EventEmitter();
protocol_1.Protocol.setEmitter(this.emitter);
this.emitter.on('update', this.checkScenes.bind(this));
this.readScenes();
// open all masters listed in the config
this.masters = [];
}
setBrowser(isB) {
this.isBrowser = isB;
}
openMasters(readDB = false) {
return __awaiter(this, void 0, void 0, function* () {
for (let inx = 0; inx < this.config.cmasters.length; inx++) {
try {
if (this.config.cmasters[inx].active)
yield this.openMaster(this.config.cmasters[inx], readDB);
}
catch (err) {
(0, logger_1.log)("system", err);
}
}
});
}
closeMasters() {
return __awaiter(this, void 0, void 0, function* () {
for (let inx = 0; inx < this.masters.length; inx++) {
yield this.closeMaster(this.masters[inx]);
}
});
}
openMaster(config, readDB = false) {
return __awaiter(this, void 0, void 0, function* () {
const master = new master_1.Master(this, config);
this.masters.push(master);
// check for old configs that don't contain the active flag
if ((typeof master.config.active === "boolean") && (!master.config.active))
return;
try {
(0, logger_1.log)("system", "opening master: " + master.getAddress());
yield master.open();
if (!(yield master.login()))
throw (new Error("Failed to log in"));
(0, logger_1.log)("system", "logged in on: " + master.getAddress());
yield master.getDatabase(readDB);
(0, logger_1.log)("system", "master: " + master.getAddress() + " opened with " + master.nodes.length + " nodes.");
this.triggerRebuild();
return master;
}
catch (e) {
(0, logger_1.err)("system", "failed to open master (" + e.toString() + ")");
return null;
// throw(e);
}
});
}
closeMaster(master) {
return __awaiter(this, void 0, void 0, function* () {
// non-existing master -> do nothing
if (!master)
return;
// find its index (we need it to delete it from the master list)
let inx = this.findMasterInx(master);
// close if open
if (master.isOpen) {
try {
yield master.close();
}
catch (e) {
(0, logger_1.err)("system", "failed to close master on " + master.getAddress() + ":" + master.getPort());
}
}
// remove from list
if (inx >= 0)
this.masters.splice(inx, 1);
});
}
displayDatabases() {
this.masters.forEach(m => m.displayDatabase());
}
////////////////
// Setting up //
////////////////
addMaster(oldAddress, oldPort, cmaster) {
return __awaiter(this, void 0, void 0, function* () {
if (!cmaster.address)
return;
// see if this master already exists
let inx = this.findCMasterInx(oldAddress, oldPort);
// store in config if not yet known
if (inx < 0) {
this.config.cmasters.push(cmaster);
inx = this.masters.length - 1;
}
else {
// close to re-open (master is deleted from the master array)
const master = this.findMaster(oldAddress, oldPort);
yield this.closeMaster(master);
// update the config
this.config.cmasters[inx] = cmaster;
}
this.writeConfig();
// master is openened and added to the master array
return yield this.openMaster(cmaster, true);
});
}
deleteMaster(master) {
return __awaiter(this, void 0, void 0, function* () {
const masterAddress = master.getAddress();
const masterPort = master.getPort();
// remove from the config
let inx = this.findCMasterInx(masterAddress, masterPort);
if (inx >= 0) {
// remove from the active masters
yield this.closeMaster(master);
// remove the master from the config list
this.config.cmasters.splice(inx, 1);
// remove it's units from the config
this.config.cunits = this.config.cunits.filter(unit => (unit.masterPort != masterPort) || (unit.masterAddress != masterAddress));
this.writeConfig();
}
else {
(0, logger_1.err)("system", "didn't find the master " + master.getAddress() + ":" + master.getConfig().port + " in the config");
}
});
}
updateMasterConfig(master) {
let inx = this.findCMasterInx(master.getAddress(), master.getPort());
if (inx >= 0) {
this.config.cmasters[inx] = master.getConfig();
}
this.writeConfig();
}
setActiveState(item) {
// for nodes
if (item instanceof protocol_1.Node) {
const masterAddress = item.master.getAddress();
const masterPort = item.master.getPort();
// check if at least 1 unit is used
const unitConfig = this.config.cunits.find((cu) => (cu.logicalNodeAddress === item.logicalAddress) &&
(cu.masterAddress === masterAddress) &&
(cu.masterPort == masterPort));
// if there is: set active to true of this node
item.active = !!unitConfig;
}
// for units
if (item instanceof protocol_1.Unit) {
const masterAddress = item.node.master.getAddress();
const masterPort = item.node.master.getPort();
// see if there is a config record for this unit
const unitConfig = this.config.cunits.find((cu) => (cu.logicalAddress === item.logicalAddress) &&
(cu.logicalNodeAddress === item.logicalNodeAddress) &&
(cu.masterAddress === masterAddress) &&
(cu.masterPort == masterPort));
// if there is: copy active, used, displayName and group
if (unitConfig) {
item.active = (unitConfig.active === "Y");
item.used = (unitConfig.used === "Y");
item.displayName = unitConfig.displayName || unitConfig.name;
item.group = unitConfig.group;
(0, logger_1.log)("system", "System.setActiveState(unit) -> config name: " + unitConfig.name + ", item name: " + item.name);
}
else {
item.active = false;
}
}
}
//////////////////////////////////////
// Finding masters, nodes and units //
//////////////////////////////////////
findMaster(master, port) {
if (typeof master === "string") {
if (typeof port === "undefined") {
const parts = master.split(":");
if (parts.length > 1) {
master = parts[0];
port = parseInt(parts[1]);
}
else {
port = 5001;
}
}
return this.masters.find((m) => m && m.same(master, port));
}
else {
return this.masters.find((m) => m && m.same(master));
}
}
findMasterInx(master) {
return this.masters.findIndex((m) => m && m.same(master));
}
findCMasterInx(address, port) {
return this.config.cmasters.findIndex((m) => (m.address == address) && (m.port == port));
}
findNode(master, logicalAddress) {
if (master)
return master.nodes.find((n) => n && (n.logicalAddress === logicalAddress));
else
return null;
}
findUnit(master, A, B, C) {
let logicalNodeAddress = A;
let logicalAddress = B;
if (typeof master === "string") {
master = this.findMaster(master, A);
logicalNodeAddress = B;
logicalAddress = C;
}
const node = this.findNode(master, logicalNodeAddress);
if (node)
return node.units.find((u) => u && (u.logicalAddress === logicalAddress));
else
return null;
}
findUnitByAddress(logicalNodeAddress, logicalAddress) {
let unit = null;
this.masters.forEach((m) => {
if (m) {
const node = this.findNode(m, logicalNodeAddress);
if (node)
unit = node.units.find((u) => u && (u.logicalAddress === logicalAddress));
}
});
return unit;
}
findUnitByName(master, A, name) {
let unit = null;
if (typeof master === "string") {
master = this.findMaster(master, A);
}
else {
name = A;
}
this.masters.forEach((m) => {
if (m && m.same(master)) {
m.nodes.forEach((n) => {
if (n) {
n.units.forEach(u => {
if ((u.displayName === name) || (u.name === name))
unit = u;
});
}
});
}
});
return unit;
}
activeUnitsConfig() {
return this.config.cunits.filter(u => u.active == "Y");
}
allActiveUnits() {
return this.masters
.reduce((acc, m) => acc.concat(m.nodes), [])
.reduce((acc, n) => acc.concat(n.units), [])
.filter(u => u.active);
}
usedUnitsConfig() {
return this.config.cunits.filter(u => u.used == "Y");
}
allUsedUnits() {
return this.masters
.reduce((acc, m) => acc.concat(m.nodes), [])
.reduce((acc, n) => acc.concat(n.units), [])
.filter(u => u.used);
}
allMasters(doToMaster) {
this.masters.forEach(m => doToMaster(m));
}
//////////////////////////////////////////////////
// Getting the current state of units and nodes //
//////////////////////////////////////////////////
updateSystem(dontTrigger = false) {
this.config.cunits = this.allUsedUnits()
.map((u) => {
return { active: u.active ? "Y" : "N", used: "Y", group: u.group, name: u.name, displayName: u.displayName,
type: u.type, extendedType: u.extendedType,
masterAddress: u.node.master.getAddress(), masterPort: u.node.master.getPort(),
logicalNodeAddress: u.node.logicalAddress, logicalAddress: u.logicalAddress };
});
this.writeConfig();
if (dontTrigger)
this.triggerRebuild();
}
triggerRebuild(immediate = false) {
//log("system", "triggerRebuild requested")
if (this.trigger) {
//log("system", "killing pending rebuild")
clearTimeout(this.trigger);
this.trigger = null;
}
if (immediate) {
this.rebuildServices();
}
else {
this.trigger = setTimeout(() => {
this.trigger = null;
this.rebuildServices();
}, 2000);
}
}
rebuildServices() {
function compare(a, b) {
const an = a.getSort();
const bn = b.getSort();
if (an < bn)
return -1;
if (an > bn)
return 1;
return 0;
}
function compareN(a, b) {
const aname = a.name.toLowerCase();
const bname = b.name.toLowerCase();
if (aname < bname)
return -1;
if (aname > bname)
return 1;
return 0;
}
// sort masters, nodes in masters, units in nodes.
(0, logger_1.log)("system", "rebuildMasters (" + this.masters.length + ") -> nodes -> units");
this.masters.sort(compare);
this.masters.forEach((m) => {
m.nodes.sort(compare);
m.nodes.forEach((n) => n.units.sort(compare));
});
// sort selected controls, temperatures and moods.
(0, logger_1.log)("system", "rebuildServices");
const services = this.allActiveUnits();
this.controls = services.filter(s => s.isCtrl()).sort(compareN);
this.temperatures = services.filter(s => s.isTemperature()).sort(compareN);
this.moods = services.filter(s => (s.isMood() || s.isInput())).sort(compareN);
this.stores = this.controls.filter(s => s.isUpDown());
let complete = true;
// should be of all active masters, nodes and units !!
// this.allMasters(m => m.allNodes(n => {if (n.nrUnits != n.units.length) complete = false; }))
if (complete) {
(0, logger_1.log)("system", "emitting READY");
this.emitter.emit('ready', this.masters.length);
}
}
////////////
// Scenes //
////////////
checkScenes(unit) {
// called by updateState that is listening for status changes
const scene = this.scenes.find(s => unit.isUnit(s.trigger.masterAddress, s.trigger.masterPort, s.trigger.logicalNodeAddress, s.trigger.logicalAddress));
if (scene) {
(0, logger_1.debug)("system", "scene found -> " + scene.name + ", value = " + scene.trigger.value + " unit = " + unit.value);
console.log(scene);
// if (unit.sameValue(scene.trigger.value)) {
scene.units.forEach(u => {
const unit = this.findUnit(u.masterAddress, u.masterPort, u.logicalNodeAddress, u.logicalAddress);
if (unit) {
(0, logger_1.debug)("system", " - attached unit found -> " + unit.getDisplayName() + " -> " + u.value);
unit.setState(u.value);
}
});
// }
}
}
//////////////////
// Config stuff //
//////////////////
readScenes() {
this.scenes = this.read("scenes");
// order the scenes and reset the order indices
this.scenes.sort((a, b) => a.order - b.order);
this.scenes.forEach((g, i) => g.order = i);
}
writeScenes() {
this.write("scenes", this.scenes);
}
}
exports.System = System;
//# sourceMappingURL=system.js.map