diff --git a/nodes/xilica-interval.html b/nodes/xilica-interval.html
new file mode 100644
index 0000000..196c049
--- /dev/null
+++ b/nodes/xilica-interval.html
@@ -0,0 +1,40 @@
+
+
+
+
+
+
diff --git a/nodes/xilica-interval.js b/nodes/xilica-interval.js
new file mode 100644
index 0000000..33519b9
--- /dev/null
+++ b/nodes/xilica-interval.js
@@ -0,0 +1,33 @@
+module.exports = function (RED) {
+ function XilicaInterval(config) {
+ RED.nodes.createNode(this, config);
+ const node = this;
+
+ node.interval = parseInt(config.interval, 10) || 100;
+
+ node.on("input", (msg, send, done) => {
+ send =
+ send ||
+ function () {
+ node.send.apply(node, arguments);
+ };
+ done = done || function () {};
+
+ let interval = node.interval;
+ if (Object.prototype.hasOwnProperty.call(msg, "interval")) {
+ const v = parseInt(msg.interval, 10);
+ if (!Number.isNaN(v) && v > 0) {
+ interval = v;
+ }
+ }
+
+ msg.payload = "INTERVAL " + interval;
+
+ send(msg);
+ done();
+ });
+ }
+
+ RED.nodes.registerType("xilica-interval", XilicaInterval);
+};
+
diff --git a/nodes/xilica-subscribe.html b/nodes/xilica-subscribe.html
new file mode 100644
index 0000000..ef4bc7c
--- /dev/null
+++ b/nodes/xilica-subscribe.html
@@ -0,0 +1,79 @@
+
+
+
+
+
+
diff --git a/nodes/xilica-subscribe.js b/nodes/xilica-subscribe.js
new file mode 100644
index 0000000..7b77901
--- /dev/null
+++ b/nodes/xilica-subscribe.js
@@ -0,0 +1,153 @@
+module.exports = function (RED) {
+ function buildNamesFromList(targets) {
+ return targets
+ .split(/\r?\n/)
+ .map((l) => l.trim())
+ .filter((l) => l.length > 0);
+ }
+
+ function buildNamesStartsWith(targets, startIndex, endIndex) {
+ const prefixes = buildNamesFromList(targets);
+ const names = [];
+ if (startIndex > endIndex) {
+ const tmp = startIndex;
+ startIndex = endIndex;
+ endIndex = tmp;
+ }
+ prefixes.forEach((prefix) => {
+ for (let i = startIndex; i <= endIndex; i += 1) {
+ names.push(prefix + i);
+ }
+ });
+ return names;
+ }
+
+ function buildNamesEndsWith(targets, startIndex, endIndex) {
+ const suffixes = buildNamesFromList(targets);
+ const names = [];
+ if (startIndex > endIndex) {
+ const tmp = startIndex;
+ startIndex = endIndex;
+ endIndex = tmp;
+ }
+ suffixes.forEach((suffix) => {
+ for (let i = startIndex; i <= endIndex; i += 1) {
+ names.push(String(i) + suffix);
+ }
+ });
+ return names;
+ }
+
+ function buildNamesStartsAndEnds(targets, startIndex, endIndex) {
+ const patterns = buildNamesFromList(targets);
+ const names = [];
+ if (startIndex > endIndex) {
+ const tmp = startIndex;
+ startIndex = endIndex;
+ endIndex = tmp;
+ }
+ patterns.forEach((pattern) => {
+ const parts = pattern.split("|");
+ const prefix = (parts[0] || "").trim();
+ const suffix = (parts[1] || "").trim();
+ if (!prefix && !suffix) {
+ return;
+ }
+ for (let i = startIndex; i <= endIndex; i += 1) {
+ names.push(prefix + i + suffix);
+ }
+ });
+ return names;
+ }
+
+ function buildControlNames(mode, targets, startIndex, endIndex) {
+ if (mode === "startsWith") {
+ return buildNamesStartsWith(targets, startIndex, endIndex);
+ }
+ if (mode === "endsWith") {
+ return buildNamesEndsWith(targets, startIndex, endIndex);
+ }
+ if (mode === "startsAndEnds") {
+ return buildNamesStartsAndEnds(targets, startIndex, endIndex);
+ }
+ return buildNamesFromList(targets);
+ }
+
+ function normaliseAction(action) {
+ const a = (action || "").toString().toLowerCase();
+ if (a === "unsubscribe" || a === "unsub") {
+ return "UNSUBSCRIBE";
+ }
+ return "SUBSCRIBE";
+ }
+
+ function XilicaSubscribe(config) {
+ RED.nodes.createNode(this, config);
+ const node = this;
+
+ node.action = config.action || "subscribe";
+ node.mode = config.mode || "list";
+ node.targets = config.targets || "";
+ node.startIndex = parseInt(config.startIndex, 10) || 1;
+ node.endIndex = parseInt(config.endIndex, 10) || node.startIndex;
+ node.transport = config.transport || "TCP";
+
+ node.on("input", (msg, send, done) => {
+ send =
+ send ||
+ function () {
+ node.send.apply(node, arguments);
+ };
+ done = done || function () {};
+
+ const action = normaliseAction(msg.action || node.action);
+ const mode = (msg.mode || node.mode || "list").toString();
+
+ const targets =
+ typeof msg.targets === "string" && msg.targets.trim().length
+ ? msg.targets
+ : node.targets;
+
+ let startIndex = node.startIndex;
+ let endIndex = node.endIndex;
+ if (Object.prototype.hasOwnProperty.call(msg, "startIndex")) {
+ const v = parseInt(msg.startIndex, 10);
+ if (!Number.isNaN(v)) {
+ startIndex = v;
+ }
+ }
+ if (Object.prototype.hasOwnProperty.call(msg, "endIndex")) {
+ const v = parseInt(msg.endIndex, 10);
+ if (!Number.isNaN(v)) {
+ endIndex = v;
+ }
+ }
+
+ const transport =
+ typeof msg.transport === "string" && msg.transport.trim().length
+ ? msg.transport.trim()
+ : node.transport;
+
+ const names = buildControlNames(mode, targets || "", startIndex, endIndex);
+
+ if (!names.length) {
+ node.warn("xilica-subscribe: no targets defined");
+ send(msg);
+ done();
+ return;
+ }
+
+ const lines = names.map(
+ (name) => action + " " + name + ' "' + transport + '"',
+ );
+
+ msg.payload = lines.join("\r");
+
+ send(msg);
+ done();
+ });
+ }
+
+ RED.nodes.registerType("xilica-subscribe", XilicaSubscribe);
+};
+
diff --git a/package.json b/package.json
index 42af8b5..ef6bb44 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,9 @@
"nodes": {
"xilica-ping": "nodes/xilica-ping.js",
"xilica-connection": "nodes/xilica-connection.js",
- "xilica-command": "nodes/xilica-command.js"
+ "xilica-command": "nodes/xilica-command.js",
+ "xilica-interval": "nodes/xilica-interval.js",
+ "xilica-subscribe": "nodes/xilica-subscribe.js"
}
}
}