node-red-contrib-mobius-flow-dali
Version:
Node-RED nodes to work with MOBiUSFlow and DALI devices
384 lines (383 loc) • 13.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CircuitFlashInterface = void 0;
const events_1 = require("events");
const dali_linker_1 = require("./dali-linker");
class CircuitFlashInterface extends events_1.EventEmitter {
circuit;
daliMaster;
flashLoop;
currentlyFlashing;
devices = [];
deviceIndex = 0;
selectedObjectIns = '0001';
addressingMode = 'short';
onLevel = 80;
offLevel = 0;
flashRate = 750;
groupAddress = 0;
linker;
constructor(conID, getNode) {
super();
this.daliMaster = getNode(conID).connection;
this.circuit = this.daliMaster.circuit.split('/')[1];
this.flashLoop = null;
this.currentlyFlashing = false;
this.linker = new dali_linker_1.DaliLinker(this.daliMaster.circuit.split('/')[0], this.circuit, this.daliMaster.serviceConn);
}
/**
* Send status update
* @param message - Status update message
*/
updateStatus(message) {
this.emit('flashNotification', message);
}
/**
* Send updated device list
*/
sendUpdatedDeviceList() {
this.emit('interfaceLuminaireDevices', this.devices);
}
/**
* Send updated object list
*/
sendUpdatedObjectList() {
this.linker.fetchObjects('0258', (err, response) => {
if (!err) {
const objects = [];
for (let i = 0; i < response.length; i++) {
objects.push({
daliAddress: response[i].daliAddress,
ins: response[i].ins,
selected: (() => (response[i].ins === this.selectedObjectIns) ? true : false)()
});
}
this.emit('interfaceLuminaireObjects', objects);
}
else {
this.updateStatus(err);
}
});
}
/**
* Update the device list
* @param deviceList - The new device list
*/
updateDeviceList(deviceList) {
this.devices = deviceList;
for (let i = 0; i < this.devices.length; i++) {
this.devices[i].selected = (i === this.deviceIndex) ? true : false;
}
this.linker.fetchObjects('0258', (err, response) => {
if (!err) {
const linkedAddresses = [];
const instances = [];
for (let i = 0; i < response.length; i++) {
if (response[i].daliAddress !== -1) {
linkedAddresses.push(response[i].daliAddress);
instances.push(response[i].ins);
}
}
for (let i = 0; i < this.devices.length; i++) {
if (!this.devices[i].linked) {
for (let j = 0; j < linkedAddresses.length; j++) {
if (this.devices[i].daliAddress === linkedAddresses[j]) {
this.devices[i].linked = true;
this.devices[i].linkedTo = instances[j];
}
}
}
}
this.sendUpdatedDeviceList();
}
else {
this.updateStatus(err);
this.sendUpdatedDeviceList();
}
});
}
/**
* Update the flash settings
* @param addrMode - The new addressing mode (short / group / broadcast)
* @param onLevel - The new on light level (0 - 100)
* @param offLevel - The new off light level (0 - 100)
* @param rate - The flash rate
*/
updateSettings(addrMode, onLevel, offLevel, rate) {
this.addressingMode = addrMode;
this.onLevel = onLevel;
this.offLevel = offLevel;
this.flashRate = rate;
}
/**
* Select a device within the device list
* @param addr - The DALI short address of the device to be selected
*/
selectDevice(addr) {
switch (this.addressingMode) {
case 'short':
for (let i = 0; i < this.devices.length; i++) {
if (this.devices[i].daliAddress === addr) {
this.devices[i].selected = true;
this.deviceIndex = i;
}
else {
this.devices[i].selected = false;
}
}
this.sendUpdatedDeviceList();
break;
case 'group':
this.groupAddress = addr;
break;
}
if (this.currentlyFlashing) {
this.stopFlash();
this.startFlash();
}
this.notifyOfStateChange(addr);
}
/**
* Select a new mobius luminaire object
* @param ins - The ins of the mobius luminaire object to be selected (0001 - 003F)
*/
selectObject(ins) {
this.selectedObjectIns = ins;
this.sendUpdatedObjectList();
}
/**
* Send relevent notifications when a new device is selected
* @param addr - DALI short address of new device
*/
notifyOfStateChange(addr) {
switch (this.addressingMode) {
case 'short':
let isLinked = false;
for (let i = 0; i < this.devices.length; i++) {
if (this.devices[i].daliAddress === addr) {
isLinked = this.devices[i].linked;
}
}
if (this.currentlyFlashing) {
this.updateStatus(isLinked ? `Flashing device ${addr} (already linked)` : `Flashing device ${addr}`);
}
else {
this.updateStatus(isLinked ? `Device ${addr} selected (already linked)` : `Device ${addr} selected`);
}
this.sendUpdatedDeviceList();
break;
case 'group':
this.groupAddress = addr;
this.updateStatus(this.currentlyFlashing ? `Flashing group ${addr}` : `Group ${addr} selected`);
break;
case 'broadcast':
if (this.currentlyFlashing) {
this.updateStatus('Flashing all');
}
break;
}
}
/**
* Send a DALI command to control direct power to luminaires
* @param addressingMode - The DALI addressing mode
* @param address - The DALI address (group address or short address)
* @param level - The set lighting level (0 - 100)
*/
sendDirectArcPowerCommand(addressingMode, address, level) {
this.daliMaster.sendNormalDali({
topic: 'daliCommand',
payload: {
daliCircuitID: this.daliMaster.circuit.split('/')[1],
daliCommandCode: 999,
daliCommandName: 'Direct Power Control',
daliAddressingMode: addressingMode,
daliAddress: address,
level: level
}
});
}
/**
* Start the flash process
*/
startFlash() {
const addr = (this.addressingMode === 'short') ? this.devices[this.deviceIndex].daliAddress : this.groupAddress;
if (this.devices.length >= 1 || this.addressingMode === 'broadcast') {
let state = true;
this.currentlyFlashing = true;
this.notifyOfStateChange(addr);
this.sendDirectArcPowerCommand(this.addressingMode, addr, this.offLevel);
this.flashLoop = setInterval(() => {
this.sendDirectArcPowerCommand(this.addressingMode, addr, (state ? this.onLevel : this.offLevel));
state = !state;
}, this.flashRate);
}
else {
this.updateStatus('No luminaires to flash');
}
}
/**
* Stop the flash process
*/
stopFlash() {
this.updateStatus(`Flash Stopped`);
this.currentlyFlashing = false;
clearInterval(this.flashLoop);
let addr;
if (this.addressingMode === 'short') {
addr = this.devices[this.deviceIndex].daliAddress;
}
else {
addr = this.groupAddress;
}
this.sendDirectArcPowerCommand(this.addressingMode, addr, this.onLevel);
}
/**
* Select the next luminaire in the device list
*/
flashNext() {
if (this.addressingMode === 'short') {
this.devices[this.deviceIndex].selected = false;
this.deviceIndex += 1;
if (this.deviceIndex === this.devices.length) {
this.deviceIndex = 0;
}
this.devices[this.deviceIndex].selected = true;
this.sendUpdatedDeviceList();
this.notifyOfStateChange(this.devices[this.deviceIndex].daliAddress);
if (this.currentlyFlashing) {
this.stopFlash();
this.startFlash();
}
}
else {
this.updateStatus('This function can only be used if short addressing is selected');
}
}
/**
* Flash the previous luminaire in the device list
*/
flashPrevious() {
if (this.addressingMode === 'short') {
this.devices[this.deviceIndex].selected = false;
this.deviceIndex -= 1;
if (this.deviceIndex < 0) {
this.deviceIndex = (this.devices.length - 1);
}
this.devices[this.deviceIndex].selected = true;
this.sendUpdatedDeviceList();
this.notifyOfStateChange(this.devices[this.deviceIndex].daliAddress);
if (this.currentlyFlashing) {
this.stopFlash();
this.startFlash();
}
}
else {
this.updateStatus('This function can only be used if short addressing is selected');
}
}
/**
* Link the currently selected luminaire device to the currently selected mobius object
*/
linkLuminaire() {
if (this.addressingMode === 'short') {
if (this.devices.length >= 1) {
for (let i = 0; i < this.devices.length; i++) { // Unlink any devices currently linked to the selected instance
if (this.devices[i].linkedTo === this.selectedObjectIns) {
this.devices[i].linked = false;
this.devices[i].linkedTo = 'None';
}
}
if (!this.devices[this.deviceIndex].linked) {
this.linker.link('0258', this.selectedObjectIns, this.devices[this.deviceIndex].daliAddress, (err) => {
if (err) {
this.updateStatus(`${err}`);
}
else {
this.devices[this.deviceIndex].linked = true;
this.devices[this.deviceIndex].linkedTo = this.selectedObjectIns;
this.sendUpdatedDeviceList();
this.sendUpdatedObjectList();
this.updateStatus(`Linked device ${this.devices[this.deviceIndex].daliAddress} to object ${this.selectedObjectIns}`);
}
});
}
else {
this.updateStatus(`Device ${this.devices[this.deviceIndex].daliAddress} already linked`);
}
}
else {
this.updateStatus(`No devices to link`);
}
}
else {
this.updateStatus(`Linking can only be used when using short addressing`);
}
}
/**
* Unlink all luminaire objects
*/
unlinkAllMobObjects() {
this.linker.unlinkAll('0258', (err) => {
if (!err) {
this.sendUpdatedObjectList();
}
else {
this.updateStatus(err);
}
});
for (let i = 0; i < this.devices.length; i++) {
this.devices[i].linkedTo = 'None';
this.devices[i].linked = false;
}
this.sendUpdatedObjectList();
this.sendUpdatedDeviceList();
}
/**
* Unlink the selected luminaire object
*/
unlinkMobObject() {
this.linker.unlink('0258', this.selectedObjectIns, (err) => {
if (!err) {
this.sendUpdatedObjectList();
}
else {
this.updateStatus(err);
}
});
for (let i = 0; i < this.devices.length; i++) {
if (this.devices[i].linkedTo === this.selectedObjectIns) {
this.devices[i].linkedTo = 'None';
this.devices[i].linked = false;
}
}
this.sendUpdatedDeviceList();
}
/**
* Unlink a device / object pair as a result of a swap or delete process
* @param addr - The DALI short address of the device within the pair to be unlinked
*/
unlinkOnSwap(addr) {
let ins = '';
for (let i = 0; i < this.devices.length; i++) {
if (this.devices[i].daliAddress === addr) {
this.devices[i].linked = false;
ins = this.devices[i].linkedTo;
this.devices[i].linkedTo = 'None';
}
}
this.sendUpdatedDeviceList();
this.linker.unlink('0258', ins, (err) => {
if (!err) {
this.sendUpdatedObjectList();
}
else {
this.updateStatus(err);
}
});
}
changeToThisCircuit() {
this.sendUpdatedDeviceList();
this.sendUpdatedObjectList();
}
}
exports.CircuitFlashInterface = CircuitFlashInterface;