node-red-contrib-boolean-logic-ultimate
Advanced tools
Comparing version 1.0.14 to 1.0.16
@@ -10,5 +10,19 @@ module.exports = function (RED) { | ||
node.curPayload = false; | ||
node.isBlinking = false; // Is the timer running? | ||
node.on('input', function (msg) { | ||
if (msg.hasOwnProperty("interval")) { | ||
try { | ||
node.blinkfrequency = msg.interval; | ||
if (node.isBlinking) // 29/05/2020 If was blinking, restart the timer with the new interval | ||
{ | ||
if (node.tBlinker !== null) clearInterval(node.tBlinker); | ||
node.tBlinker = setInterval(handleTimer, node.blinkfrequency); // Start the timer that handles the queue of telegrams | ||
} | ||
} catch (error) { | ||
node.blinkfrequency = 500; | ||
setNodeStatus({ fill: "red", shape: "dot", text: "Invalid interval received" }); | ||
} | ||
} | ||
if (msg.hasOwnProperty("payload")) { | ||
@@ -19,19 +33,13 @@ // 06/11/2019 | ||
node.tBlinker = setInterval(handleTimer, node.blinkfrequency); // Start the timer that handles the queue of telegrams | ||
node.isBlinking = true; | ||
setNodeStatus({ fill: "green", shape: "dot", text: "-> On" }); | ||
} else { | ||
if (node.tBlinker !== null) clearInterval(node.tBlinker); | ||
node.isBlinking = false; | ||
setNodeStatus({ fill: "red", shape: "dot", text: "|| Off" }); | ||
node.send({ payload: false }); | ||
node.curPayload = false; | ||
} | ||
} | ||
if (msg.hasOwnProperty("interval")) { | ||
try { | ||
node.blinkfrequency = msg.interval; | ||
} catch (error) { | ||
node.blinkfrequency = 500; | ||
setNodeStatus({ fill: "red", shape: "dot", text: "Invalid interval received" }); | ||
} | ||
} | ||
}); | ||
@@ -41,2 +49,3 @@ | ||
if (node.tBlinker !== null) clearInterval(node.tBlinker); | ||
node.isBlinking = false; | ||
node.send({ payload: false }); | ||
@@ -43,0 +52,0 @@ }); |
@@ -1,3 +0,3 @@ | ||
module.exports = function(RED) { | ||
function BooleanLogicUltimate(config) { | ||
module.exports = function (RED) { | ||
function BooleanLogicUltimate(config) { | ||
RED.nodes.createNode(this, config); | ||
@@ -10,3 +10,3 @@ var node = this; | ||
var decimal = /^\s*[+-]{0,1}\s*([\d]+(\.[\d]*)*)\s*$/ | ||
// Helper for the config html, to be able to delete the peristent states file | ||
@@ -31,10 +31,10 @@ RED.httpAdmin.get("/stateoperation_delete", RED.auth.needsPermission('BooleanLogicUltimate.read'), function (req, res) { | ||
node.jSonStates = JSON.parse(contents); | ||
setNodeStatus({fill: "blue",shape: "ring",text: "Loaded persistent states (" + Object.keys(node.jSonStates).length + " total)."}); | ||
setNodeStatus({ fill: "blue", shape: "ring", text: "Loaded persistent states (" + Object.keys(node.jSonStates).length + " total)." }); | ||
} | ||
} catch (error) { | ||
setNodeStatus({fill: "grey",shape: "ring",text: "No persistent states"}); | ||
setNodeStatus({ fill: "grey", shape: "ring", text: "No persistent states" }); | ||
} | ||
} else { | ||
setNodeStatus({fill: "yellow",shape: "dot",text: "Waiting for input states"}); | ||
setNodeStatus({ fill: "yellow", shape: "dot", text: "Waiting for input states" }); | ||
} | ||
@@ -45,18 +45,16 @@ | ||
initUndefinedInputs(); | ||
this.on('input', function (msg) { | ||
var topic = msg.topic; | ||
var payload = msg.payload; | ||
if (topic !== undefined && payload !== undefined) { | ||
var value = ToBoolean( payload ); | ||
var value = ToBoolean(payload); | ||
// 14/08/2019 if inputs are initialized, remove a "dummy" item from the state's array, as soon as new topic arrives | ||
if(node.sInitializeWith !== "WaitForPayload") | ||
{ | ||
if (node.sInitializeWith !== "WaitForPayload") { | ||
// Search if the current topic is in the state array | ||
if (typeof node.jSonStates[topic] === "undefined") | ||
{ | ||
if (typeof node.jSonStates[topic] === "undefined") { | ||
// Delete one dummy | ||
@@ -69,26 +67,26 @@ for (let index = 0; index < node.config.inputCount; index++) { | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
// Add current attribute | ||
node.jSonStates[topic] = value; | ||
// Save the state array to a perisistent file | ||
if (node.config.persist == true) { | ||
if (node.config.persist == true) { | ||
try { | ||
if (!fs.existsSync("states")) fs.mkdirSync("states"); | ||
fs.writeFileSync("states/" + node.id.toString(),JSON.stringify(node.jSonStates)); | ||
fs.writeFileSync("states/" + node.id.toString(), JSON.stringify(node.jSonStates)); | ||
} catch (error) { | ||
setNodeStatus({fill: "red",shape: "dot",text: "Node cannot write to filesystem: " + error}); | ||
setNodeStatus({ fill: "red", shape: "dot", text: "Node cannot write to filesystem: " + error }); | ||
} | ||
} | ||
// Do we have as many inputs as we expect? | ||
var keyCount = Object.keys(node.jSonStates).length; | ||
if( keyCount == node.config.inputCount ) { | ||
if (keyCount == node.config.inputCount) { | ||
var resAND = CalculateResult("AND"); | ||
@@ -108,25 +106,22 @@ var resOR = CalculateResult("OR"); | ||
&& node.config.triggertopic !== "" | ||
&& msg.hasOwnProperty("topic") && msg.topic !=="" | ||
&& node.config.triggertopic === msg.topic) | ||
{ | ||
SetResult(resAND, resOR, resXOR, node.config.topic,msg); | ||
} else | ||
{ | ||
setNodeStatus({ fill: "grey", shape: "ring", text: "Saved (" + (msg.hasOwnProperty("topic") ? msg.topic : "empty input topic") + ") " + value}); | ||
&& msg.hasOwnProperty("topic") && msg.topic !== "" | ||
&& node.config.triggertopic === msg.topic) { | ||
SetResult(resAND, resOR, resXOR, node.config.topic, msg); | ||
} else { | ||
setNodeStatus({ fill: "grey", shape: "ring", text: "Saved (" + (msg.hasOwnProperty("topic") ? msg.topic : "empty input topic") + ") " + value }); | ||
} | ||
} else | ||
{ | ||
SetResult(resAND, resOR, resXOR, node.config.topic,msg); | ||
} else { | ||
SetResult(resAND, resOR, resXOR, node.config.topic, msg); | ||
} | ||
} | ||
else if(keyCount > node.config.inputCount ) { | ||
setNodeStatus({ fill: "gray", shape: "ring", text: "Reset due to unexpected new topic"}); | ||
else if (keyCount > node.config.inputCount) { | ||
setNodeStatus({ fill: "gray", shape: "ring", text: "Reset due to unexpected new topic" }); | ||
DeletePersistFile(); | ||
} else { | ||
setNodeStatus({ fill: "green", shape: "ring", text: "Arrived topic " + keyCount + " of " + node.config.inputCount}); | ||
setNodeStatus({ fill: "green", shape: "ring", text: "Arrived topic " + keyCount + " of " + node.config.inputCount }); | ||
} | ||
} | ||
}); | ||
this.on('close', function(removed, done) { | ||
this.on('close', function (removed, done) { | ||
if (removed) { | ||
@@ -142,9 +137,9 @@ // This node has been deleted | ||
function DeletePersistFile (){ | ||
function DeletePersistFile() { | ||
// Detele the persist file | ||
try { | ||
if (fs.existsSync("states/" + node.id.toString())) fs.unlinkSync("states/" + node.id.toString()); | ||
setNodeStatus({fill: "red",shape: "ring",text: "Persistent states deleted ("+node.id.toString()+")."}); | ||
setNodeStatus({ fill: "red", shape: "ring", text: "Persistent states deleted (" + node.id.toString() + ")." }); | ||
} catch (error) { | ||
setNodeStatus({fill: "red",shape: "ring",text: "Error deleting persistent file: " + error.toString()}); | ||
setNodeStatus({ fill: "red", shape: "ring", text: "Error deleting persistent file: " + error.toString() }); | ||
} | ||
@@ -157,4 +152,3 @@ node.jSonStates = {}; // Resets inputs | ||
function initUndefinedInputs() { | ||
if (node.sInitializeWith !== "WaitForPayload") | ||
{ | ||
if (node.sInitializeWith !== "WaitForPayload") { | ||
var nTotalDummyToCreate = Number(node.config.inputCount) - Object.keys(node.jSonStates).length; | ||
@@ -166,11 +160,10 @@ if (nTotalDummyToCreate > 0) { | ||
} | ||
setTimeout(() => { setNodeStatus({fill: "green",shape: "ring",text: "Initialized " + nTotalDummyToCreate + " undefined inputs with " + node.sInitializeWith});}, 4000) | ||
} | ||
setTimeout(() => { setNodeStatus({ fill: "green", shape: "ring", text: "Initialized " + nTotalDummyToCreate + " undefined inputs with " + node.sInitializeWith }); }, 4000) | ||
} | ||
} | ||
} | ||
function setNodeStatus({fill, shape, text}) | ||
{ | ||
function setNodeStatus({ fill, shape, text }) { | ||
var dDate = new Date(); | ||
node.status({fill: fill,shape: shape,text: text + " (" + dDate.getDate() + ", " + dDate.toLocaleTimeString() + ")"}) | ||
node.status({ fill: fill, shape: shape, text: text + " (" + dDate.getDate() + ", " + dDate.toLocaleTimeString() + ")" }) | ||
} | ||
@@ -180,4 +173,4 @@ | ||
var res; | ||
if( _operation == "XOR") { | ||
if (_operation == "XOR") { | ||
res = PerformXOR(); | ||
@@ -189,52 +182,51 @@ } | ||
res = node.jSonStates[keys[0]]; | ||
for( var i = 1; i < keys.length; ++i ) { | ||
for (var i = 1; i < keys.length; ++i) { | ||
var key = keys[i]; | ||
res = PerformSimpleOperation( _operation, res, node.jSonStates[key] ); | ||
res = PerformSimpleOperation(_operation, res, node.jSonStates[key]); | ||
} | ||
} | ||
return res; | ||
} | ||
function PerformXOR() | ||
{ | ||
return res; | ||
} | ||
function PerformXOR() { | ||
// XOR = exclusively one input is true. As such, we just count the number of true values and compare to 1. | ||
var trueCount = 0; | ||
for( var key in node.jSonStates ) { | ||
if( node.jSonStates[key] ) { | ||
for (var key in node.jSonStates) { | ||
if (node.jSonStates[key]) { | ||
trueCount++; | ||
} | ||
} | ||
return trueCount == 1; | ||
} | ||
function PerformSimpleOperation( operation, val1, val2 ) { | ||
function PerformSimpleOperation(operation, val1, val2) { | ||
var res; | ||
if( operation === "AND" ) { | ||
if (operation === "AND") { | ||
res = val1 && val2; | ||
} | ||
else if( operation === "OR" ) { | ||
else if (operation === "OR") { | ||
res = val1 || val2; | ||
} | ||
else { | ||
node.error( "Unknown operation: " + operation ); | ||
node.error("Unknown operation: " + operation); | ||
} | ||
return res; | ||
} | ||
function ToBoolean( value ) { | ||
function ToBoolean(value) { | ||
var res = false; | ||
if (typeof value === 'boolean') { | ||
res = value; | ||
} | ||
else if( typeof value === 'number' || typeof value === 'string' ) { | ||
} | ||
else if (typeof value === 'number' || typeof value === 'string') { | ||
// Is it formated as a decimal number? | ||
if( decimal.test( value ) ) { | ||
var v = parseFloat( value ); | ||
if (decimal.test(value)) { | ||
var v = parseFloat(value); | ||
res = v != 0; | ||
@@ -246,57 +238,37 @@ } | ||
} | ||
return res; | ||
}; | ||
function SetResult(_valueAND, _valueOR, _valueXOR, optionalTopic,_msg) { | ||
setNodeStatus({fill: "green",shape: "dot",text: "(AND)" + _valueAND + " (OR)" +_valueOR + " (XOR)" +_valueXOR}); | ||
// 24/01/2020 Output the entire input msg by duplicating the input and replacing only relevant fields. | ||
function SetResult(_valueAND, _valueOR, _valueXOR, optionalTopic, _msg) { | ||
setNodeStatus({ fill: "green", shape: "dot", text: "(AND)" + (_valueAND !== null ? _valueAND : "---") + " (OR)" + (_valueOR !== null ? _valueOR : "---") + " (XOR)" + (_valueXOR !== null ? _valueXOR : "---") }); | ||
var msgAND = null; | ||
if (_valueAND != null) { | ||
var msgAND = RED.util.cloneMessage(_msg); | ||
msgAND = RED.util.cloneMessage(_msg); | ||
msgAND.topic = optionalTopic === undefined ? "result" : optionalTopic; | ||
msgAND.operation = "AND"; | ||
msgAND.payload = _valueAND; | ||
} | ||
if (_valueOR!=null){ | ||
var msgOR = RED.util.cloneMessage(_msg); | ||
var msgOR = null; | ||
if (_valueOR != null) { | ||
msgOR = RED.util.cloneMessage(_msg); | ||
msgOR.topic = optionalTopic === undefined ? "result" : optionalTopic; | ||
msgOR.operation = "OR"; | ||
msgOR.payload= _valueOR; | ||
msgOR.payload = _valueOR; | ||
} | ||
var msgXOR = null; | ||
if (_valueXOR != null) { | ||
var msgXOR = RED.util.cloneMessage(_msg); | ||
msgXOR = RED.util.cloneMessage(_msg); | ||
msgXOR.topic = optionalTopic === undefined ? "result" : optionalTopic; | ||
msgXOR.operation = "XOR"; | ||
msgXOR.payload= _valueXOR; | ||
msgXOR.payload = _valueXOR; | ||
} | ||
node.send([msgAND, msgOR, msgXOR]); | ||
// if (_valueAND!=null){ | ||
// var msgAND = { | ||
// topic: optionalTopic === undefined ? "result" : optionalTopic, | ||
// operation:"AND", | ||
// payload: _valueAND | ||
// }; | ||
// } | ||
// if (_valueOR!=null){ | ||
// var msgOR = { | ||
// topic: optionalTopic === undefined ? "result" : optionalTopic, | ||
// operation:"OR", | ||
// payload: _valueOR | ||
// }; | ||
// } | ||
// if (_valueXOR!=null){ | ||
// var msgXOR = { | ||
// topic: optionalTopic === undefined ? "result" : optionalTopic, | ||
// operation:"XOR", | ||
// payload: _valueXOR | ||
// }; | ||
// } | ||
// node.send([msgAND,msgOR,msgXOR]); | ||
}; | ||
} | ||
RED.nodes.registerType("BooleanLogicUltimate",BooleanLogicUltimate); | ||
} | ||
RED.nodes.registerType("BooleanLogicUltimate", BooleanLogicUltimate); | ||
} |
@@ -26,7 +26,7 @@ module.exports = function(RED) { | ||
if (bRes === true) { | ||
setNodeStatus({ fill: "green", shape: "dot", text: "(Send) true,null" }); | ||
setNodeStatus({ fill: "green", shape: "dot", text: "(Send) true" }); | ||
node.send([msgTrue, null]); | ||
} else | ||
{ | ||
setNodeStatus( {fill: "green" ,shape: "dot" ,text: "(Send) null,false"}); | ||
setNodeStatus( {fill: "green" ,shape: "dot" ,text: "(Send) false"}); | ||
node.send([null, msgFalse]); | ||
@@ -33,0 +33,0 @@ } |
@@ -7,2 +7,10 @@ # node-red-contrib-boolean-logic-ultimate | ||
<p> | ||
<b>Version 1.0.16</b>May 2020<br/> | ||
- BlinkerUltimate: if you set the interval while the blinker is running, yet the new interval is applied immediately.</br> | ||
</p> | ||
<p> | ||
<b>Version 1.0.15</b>May 2020<br/> | ||
- Adjusted status of Boolean Logic ultimate. Replaced the text "null", with --- for better understanding.</br> | ||
</p> | ||
<p> | ||
<b>Version 1.0.14</b><br/> | ||
@@ -9,0 +17,0 @@ - NEW: added blinker node. Thanks to @Marco for the suggestion.</br> |
{ | ||
"name": "node-red-contrib-boolean-logic-ultimate", | ||
"version": "1.0.14", | ||
"version": "1.0.16", | ||
"description": "A set of Node-RED enhanced boolean logic, with persisten values after reboot and more", | ||
@@ -5,0 +5,0 @@ "author": "Supergiovane (https://github.com/Supergiovane)", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
248441
443