node-red-contrib-mobius-flow-dali
Version:
Node-RED nodes to work with MOBiUSFlow and DALI devices
302 lines (301 loc) • 10.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CircuitAddressingInterface = void 0;
const events_1 = require("events");
const daliCommon = require('@ia/dali-common');
class CircuitAddressingInterface extends events_1.EventEmitter {
circuit;
name;
operationInProgress = false;
outTopic = '';
addressingInProgress = false;
devices = [];
addresses = [];
populatedDeviceIndex = 0;
expNoDevices;
addressingTimeout;
scanningTimeout;
scanningTimeoutTime;
addressingTimeoutTime;
queryingLoop;
ungroupingLoop;
daliMaster;
constructor(addrTimeoutTime, scanningTimeoutTime, expectedDevices, conID, getNode) {
super();
this.daliMaster = getNode(conID).connection;
this.circuit = this.daliMaster.circuit.split('/')[1];
this.name = getNode(conID).name;
this.expNoDevices = expectedDevices;
this.addressingTimeoutTime = addrTimeoutTime;
this.scanningTimeoutTime = scanningTimeoutTime;
// Start Event listeners
this.processFetchTypeResponse();
this.daliMaster.on('addressingStatus', (message) => {
this.updateStatus(message);
});
}
/**
* Send a notification
* @param message - Message sent with notification
*/
updateStatus(message) {
this.emit('addressingNotification', message);
}
/**
* Ungroup all DALI devices
*/
ungroup() {
this.operationInProgress = true;
this.updateStatus(`Ungrouping All`);
let i = 0;
this.ungroupingLoop = setInterval(() => {
this.daliMaster.sendNormalDali({
topic: 'daliCommand',
payload: {
daliCircuitID: this.circuit,
daliCommandCode: 112 + i,
daliCommandName: daliCommon.daliLookup.commandLookup[112 + i],
daliAddressingMode: 'broadcast'
}
});
i = i + 1;
this.updateStatus(`Ungrouping ${Math.round((i / 16) * 100)}% complete...`);
if (i >= 16) {
clearInterval(this.ungroupingLoop);
this.updateStatus(`Ungrouping Complete`);
this.operationInProgress = false;
}
}, 1500);
}
/**
* Swap the short addresses of two DALI devices
* @param addr1 - Current short address of device 1
* @param addr2 - Current short address of device 2
*/
swap(addr1, addr2) {
this.operationInProgress = true;
this.updateStatus(`Swapped DALI addresses ${addr1} & ${addr2}, scan to confirm change`);
this.daliMaster.addressSwap(addr1, addr2);
this.operationInProgress = false;
}
/**
* Delete the short address of a DALI device
* @param addr - The current short address of the device to delete
*/
del(addr) {
this.operationInProgress = true;
this.updateStatus(`Deleted DALI address ${addr}, scan to confirm change`);
this.daliMaster.addressDelete(addr);
this.operationInProgress = false;
}
/**
* Perform full addressing on DALI circuit
*/
addressCircuit() {
this.operationInProgress = true;
this.addressingInProgress = true;
this.addresses = [];
this.outTopic = 'daliAddress';
this.updateStatus(`DALI Addressing In Progress...`);
this.startAddressingTimeout();
this.daliMaster.fullAddressCircuit(this.expNoDevices, (err, quantityOfFoundAddresses) => {
if (!err) {
this.finishAddressing(quantityOfFoundAddresses);
}
else {
this.updateStatus(`Addressing Error: ${err}`);
}
});
}
/**
* Add any unaddressed devices to an already addressed circuit
*/
addToCircuit() {
this.operationInProgress = true;
this.addresses = [];
this.outTopic = 'daliAdd';
this.startAddressingTimeout();
this.updateStatus(`Started DALI Addressing (Unaddressed Only)...`);
this.daliMaster.addAddressToCircuit((err, quantityOfFoundAddresses) => {
if (!err) {
this.finishAddressing(quantityOfFoundAddresses);
}
else {
this.updateStatus(`Addressing Error: ${err}`);
}
});
}
/**
* Finish an addressing process
*/
finishAddressing(quantityOfFoundAddresses) {
this.daliMaster.abortAddressing();
this.stopAddressingTimeout();
this.addressingInProgress = false;
this.updateStatus(`DALI Addressing Complete`);
if (quantityOfFoundAddresses > 0) {
this.scanCircuit();
}
else {
this.updateStatus(`No devices to addressed`);
this.operationInProgress = false;
}
}
/**
* Scan a circuit for all devices with a defined short address
*/
scanCircuit() {
this.operationInProgress = true;
this.addresses = [];
this.outTopic = 'daliScan';
this.startScanningTimeout();
this.daliMaster.scanCircuit((err, addresses) => {
if (!err) {
this.addresses = addresses;
this.finishScan();
}
else {
this.updateStatus(`Addressing Error: ${err}`);
}
});
}
/**
* Finish a scan process
*/
finishScan() {
this.updateStatus(`DALI Scan Complete`);
this.addresses = [...new Set(this.addresses)];
if (this.addresses.length > 0) {
this.queryTypes();
}
else {
this.updateStatus(`No devices found`);
this.stopScanningTimeout();
this.operationInProgress = false;
}
}
/**
* Gather and store query responses when querying device types
*/
processFetchTypeResponse() {
this.daliMaster.on('queryResponse', (msg) => {
msg = JSON.parse(JSON.stringify(msg));
msg = daliCommon.daliMain.daliProcess(msg);
if (this.operationInProgress === true && msg.payload.daliCommandCode === 153) {
this.devices[this.populatedDeviceIndex] = {
daliAddress: msg.payload.daliAddress,
deviceType: msg.payload.responseData.deviceType,
deviceTypeName: daliCommon.daliLookup.deviceTypeLookup[msg.payload.responseData.deviceType],
linked: false,
selected: false,
linkedTo: 'None'
};
}
});
}
/**
* Query device types of all newly added devices
*/
queryTypes() {
this.operationInProgress = true;
this.devices = [];
while (this.devices.length < this.addresses.length) {
this.devices.push(null);
}
this.queryingLoop = setInterval(() => {
let j = 0;
let k = 0;
while (j < this.devices.length) {
if (this.devices[j] === null) {
this.daliMaster.sendNormalDali({
topic: 'daliCommand',
payload: {
daliCircuitID: this.circuit,
daliCommandCode: 153,
daliCommandName: 'Query Device Type',
daliAddressingMode: 'short',
daliAddress: this.addresses[j],
}
});
j = 65;
}
else {
k = k + 1;
}
j = j + 1;
}
this.updateStatus(`Querying Types ${Math.round((k / this.addresses.length) * 100)}% Complete`);
this.populatedDeviceIndex = k;
if (k === this.addresses.length) {
clearInterval(this.queryingLoop);
this.finishQuerying();
}
}, 500);
}
/**
* Finsih a querying device types process
*/
finishQuerying() {
this.operationInProgress = false;
this.stopScanningTimeout();
if (this.devices.length !== this.expNoDevices) {
this.updateStatus(`Complete, Expected ${this.expNoDevices}, found ${this.devices.length}`);
}
else {
this.updateStatus(`Complete`);
}
this.emit('daliAddressingFunction', {
topic: this.outTopic,
payload: {
daliCircuitID: this.circuit,
numberOfDevices: this.devices.length,
expectedNumberOfDevices: this.expNoDevices,
devices: this.devices
}
});
}
/**
* Start a timeout to ensure any given process continues for a maximum time
*/
startScanningTimeout() {
this.scanningTimeout = setTimeout(() => {
this.daliMaster.abortScanning();
clearInterval(this.queryingLoop);
this.updateStatus('Scanning Timed Out');
this.operationInProgress = false;
}, this.scanningTimeoutTime);
}
/**
* Stop function timeout if a given process finishes
*/
stopScanningTimeout() {
clearTimeout(this.scanningTimeout);
}
/**
* Start a monitor to detect when no more devices are responding during an addressing process
*/
startAddressingTimeout() {
this.addressingTimeout = setTimeout(() => {
this.updateStatus('Addressing Finished due to timeout');
this.finishAddressing(0);
}, this.addressingTimeoutTime);
}
/**
* Stop the addressing monitor when addressing has finished
*/
stopAddressingTimeout() {
clearTimeout(this.addressingTimeout);
}
/**
* On node close: clear all intervals and timeouts, stop listening to events
*/
close() {
clearInterval(this.queryingLoop);
clearInterval(this.ungroupingLoop);
clearTimeout(this.scanningTimeout);
clearTimeout(this.addressingTimeout);
this.daliMaster.removeAllListeners('queryResponse');
this.daliMaster.removeAllListeners('addressingResponse');
}
}
exports.CircuitAddressingInterface = CircuitAddressingInterface;