node-red-contrib-remote 2.0.4 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -29,6 +29,17 @@ There is a help page describing how the the nodes work together and how to conne
29
29
  [Hilfe in Deutsch](https://www.remote-red.com/de/hilfe/)
30
30
 
31
31
 
32
+ ## Remote-RED Partner Programm
33
+
34
+ If you want to scale up, have a look at the Remote-RED Partner Programm. It is made for everyone who does not only use Node-RED for themselves, but builds solutions for others:
35
+
36
+ - developers of own IoT projects (Node-RED and also pure Linux projects)
37
+ - installers of smart home and control systems
38
+ - manufacturers and platforms looking for a white label solution
39
+
40
+ As a partner you offer your customers encrypted access to their dashboards, push notifications and geofencing triggers - without developing your own app and without running your own server infrastructure.
41
+
42
+
32
43
  ## Contact
33
44
 
34
45
  You will find more information on [www.remote-red-com](https://www.remote-red.com). You can contact me by mail through info@remote-red.com.
@@ -36,11 +47,17 @@ You will find more information on [www.remote-red-com](https://www.remote-red.co
36
47
 
37
48
  ## Version History
38
49
 
50
+ Version 3.0.0
51
+ - Support for the Remote-RED Partner Programm
52
+
53
+ Version 2.0.5
54
+ - Improved reconnect for Starlink users
55
+
39
56
  Version 2.0.4
40
- - Fix for 'No endpoint detected' message
57
+ - Fix for 'No endpoint detected' message
41
58
 
42
59
  Version 2.0.3
43
- - More stable handling of connection losses
60
+ - More stable handling of connection losses
44
61
 
45
62
  Version 2.0.2
46
63
  - Crash on Node-RED startup fixed, extended error reporting
@@ -16,7 +16,13 @@
16
16
  "region_dev": "Entwicklung",
17
17
  "region_desc": "Der Standort kann nicht mehr geändert werden, sobald eine App registriert wurde.",
18
18
  "change_info": "Damit Änderungen aktiv werden führe einen Redeploy durch. Starte danach die App neu oder aktualisiere die Liste der Instanzen, indem Du die Liste nach unten ziehst.",
19
- "backup_info": "Diese Konfig-Node wurde aus einem Backup wiederhergestellt. Daher sind sicherheitsrelevante Informationen nicht enthalten. Bitte löschen diese Konfig-Node (über das Zahnrad Icon rechts oben) und die Verbindungen in Deinen Remote-RED Apps. Lege eine neue Konfig-Node an, verbinde diese neu mit den Apps und wähle diese neue Konfig-Node in allen davon abhängigen Nodes aus."
19
+ "backup_info": "Diese Konfig-Node wurde aus einem Backup wiederhergestellt. Daher sind sicherheitsrelevante Informationen nicht enthalten. Bitte löschen diese Konfig-Node (über das Zahnrad Icon rechts oben) und die Verbindungen in Deinen Remote-RED Apps. Lege eine neue Konfig-Node an, verbinde diese neu mit den Apps und wähle diese neue Konfig-Node in allen davon abhängigen Nodes aus.",
20
+ "partner_toggle": "Ich bin Teilnehmer am Remote-RED Partner Programm",
21
+ "customerhash": "CustomerHash",
22
+ "customerkey": "CustomerKey",
23
+ "customerkey_desc": "Der CustomerKey wird nur einmalig zur Registrierung verwendet und nicht gespeichert.",
24
+ "customerreference": "Kunden-Referenz (optional)",
25
+ "customerreference_desc": "Z.B. Seriennummer oder Endkunden-Nummer, max. 100 Zeichen. Dient der Zuordnung dieser Instanz im Partner-Portal und ist nachträglich nicht mehr änderbar."
20
26
  },
21
27
  "actions" : {
22
28
  "register_app": "Remote-RED App verbinden",
@@ -34,6 +40,12 @@
34
40
  "privacypolicyurl": "https://www.remote-red.com/de/datenschutz",
35
41
  "termsurl": "https://www.remote-red.com/de/nutzung"
36
42
  },
43
+ "partner": {
44
+ "title": "Remote-RED Partner Programm",
45
+ "info1": "Das Remote-RED Partner Programm richtet sich an Hersteller und Integratoren, die Remote-RED in eigene Produkte einbetten und über das Remote-RED Partner Portal eigene Kontingente an Instanzen verwalten und abrechnen. Falls Du Teilnehmer bist, kannst Du hier Deinen CustomerHash und CustomerKey eintragen, um diese Instanz Deinem Partner-Konto zuzuordnen. ",
46
+ "info1_readonly": "Diese Instanz ist bereits registriert und einem Partner-Konto zugeordnet. Die Zuordnung kann nachträglich nicht mehr geändert werden. Verwaltet wird die Instanz im Remote-RED Partner Portal. ",
47
+ "link": "Zum Remote-RED Partner Portal"
48
+ },
37
49
  "help": {
38
50
  "title": "Hilfe",
39
51
  "para1": "Pro Access-Node erstellt Du eine entsprechende Config-Node. In dieser wird hinterlegt, mit welchem Host, Port und URL die Verbindung stattfindet. Die Vorgabewerte zeigen als Beispiel das Dashboard. Es ist aber auch möglich weitere Seiten aus dem lokalen Netzwerk zu konfigurieren, insofern der Server auf dem Node-RED installiert ist darauf zugreifen kann.",
@@ -16,7 +16,13 @@
16
16
  "region_dev": "Development host",
17
17
  "region_desc": "The region can´t be changed after registering an app.",
18
18
  "change_info": "To apply changes, first carry out a redeploy. Then restart the app or refresh the list of instances by dragging the list down.",
19
- "backup_info": "This config node was restored from a backup. Therefore security relevant information are not included. Please delete this config-node (via the gear icon at the top right) and the connections in your Remote-RED apps. Create a new config-node, connect it to the apps and select this new config node in all dependent nodes."
19
+ "backup_info": "This config node was restored from a backup. Therefore security relevant information are not included. Please delete this config-node (via the gear icon at the top right) and the connections in your Remote-RED apps. Create a new config-node, connect it to the apps and select this new config node in all dependent nodes.",
20
+ "partner_toggle": "I'm a participant of the Remote-RED Partner Program",
21
+ "customerhash": "CustomerHash",
22
+ "customerkey": "CustomerKey",
23
+ "customerkey_desc": "The CustomerKey is only used once during registration and is not stored.",
24
+ "customerreference": "Customer reference (optional)",
25
+ "customerreference_desc": "E.g. serial number or end customer number, max. 100 characters. Used to identify this instance in the Partner Portal and cannot be changed afterwards."
20
26
  },
21
27
  "actions" : {
22
28
  "register_app": "Connect Remote-RED App",
@@ -34,6 +40,12 @@
34
40
  "privacypolicyurl": "https://www.remote-red.com/en/privacypolicy",
35
41
  "termsurl": "https://www.remote-red.com/en/terms"
36
42
  },
43
+ "partner": {
44
+ "title": "Remote-RED Partner Program",
45
+ "info1": "The Remote-RED Partner Program is aimed at manufacturers and integrators who embed Remote-RED into their own products and manage and bill their own instance quotas via the Remote-RED Partner Portal. If you are a participant, you can enter your CustomerHash and CustomerKey here to assign this instance to your partner account. ",
46
+ "info1_readonly": "This instance is already registered and assigned to a partner account. The assignment cannot be changed afterwards. The instance is managed in the Remote-RED Partner Portal. ",
47
+ "link": "Go to the Remote-RED Partner Portal"
48
+ },
37
49
  "help": {
38
50
  "title": "Help",
39
51
  "para1": "For each Access Node you create a corresponding Config Node. In this Config Node you define with which host, port and URL the connection will be established. The default values shows the dashboard as an example. It is also possible to configure other pages from the local network, as long as the Node-RED server can access them.",
@@ -15,8 +15,7 @@ module.exports = function(RED) {
15
15
  await killSSHProcess(node);
16
16
  }
17
17
 
18
- // Reset heartbeat status
19
- node.initialHeartbeatStatus = ''
18
+ // Reset heartbeat status for current tunnel
20
19
  node.lastHeartbeatStatus = ''
21
20
 
22
21
  // Create ssh command
@@ -76,7 +75,7 @@ module.exports = function(RED) {
76
75
  });
77
76
  } catch (e) {
78
77
  // TODO: Error: socket hang up
79
- node.error('startSSH error: ' + error);
78
+ node.error('startSSH error: ' + e);
80
79
  }
81
80
  }
82
81
 
@@ -170,6 +169,18 @@ module.exports = function(RED) {
170
169
  });
171
170
  }
172
171
 
172
+ function scheduleCheckServing(node, delay) {
173
+ // Only one checkServing timer per node: cancel a pending one before scheduling
174
+ clearTimeout(node.checkservingtimeout);
175
+ node.checkservingtimeout = setTimeout(checkServing, delay, node);
176
+ }
177
+
178
+ function scheduleTryConnect(node, delay) {
179
+ // Only one tryConnect timer per node: cancel a pending one before scheduling
180
+ clearTimeout(node.tryconnecttimeout);
181
+ node.tryconnecttimeout = setTimeout(tryConnect, delay, node);
182
+ }
183
+
173
184
  async function tryConnect(node) {
174
185
  // Request new instance slot and connect ssh
175
186
  try {
@@ -179,7 +190,7 @@ module.exports = function(RED) {
179
190
  }
180
191
 
181
192
  // Create timer to check if ssh process still serving in 10 seconds
182
- node.checkservingtimeout = setTimeout(checkServing, 1000*10, node);
193
+ scheduleCheckServing(node, 1000*10);
183
194
  }
184
195
 
185
196
  function checkServing(node) {
@@ -195,13 +206,13 @@ module.exports = function(RED) {
195
206
  } else if (node.errorcounter >= 4) {
196
207
  interval = 1000*50;
197
208
  }
198
- node.tryconnecttimeout = setTimeout(tryConnect, interval, node);
209
+ scheduleTryConnect(node, interval);
199
210
  } else {
200
211
  // Running > Reset error counter
201
212
  node.errorcounter = 0;
202
213
 
203
214
  // Schedule timer to check if still serving
204
- node.checkservingtimeout = setTimeout(checkServing, 1000*10, node);
215
+ scheduleCheckServing(node, 1000*10);
205
216
  }
206
217
  }
207
218
 
@@ -218,30 +229,39 @@ module.exports = function(RED) {
218
229
  })
219
230
  .then(response => {
220
231
  // Remember the status
221
- if ( node.initialHeartbeatStatus === '' ) {
222
- node.initialHeartbeatStatus = response.data.status
223
- }
224
232
  node.lastHeartbeatStatus = response.data.status
225
233
 
226
234
  // Based on the status...
227
235
  if ( node.lastHeartbeatStatus === 'OK' ) {
228
- node.initialHeartbeatStatus = node.lastHeartbeatStatus; // Wenn es jetzt OK ist, zählt es auch wie initial OK
236
+ // Successful heartbeat
237
+ node.configurationValidated = true; // Configuration is definitely OK
238
+ node.initialRetryCount = 0; // Reset retry counter on success
229
239
  setStatus(node, {fill:"green",shape:"dot",text:"remote-access.status.serving"});
230
240
  } else if ( node.lastHeartbeatStatus === 'NOTFOUND' ) {
231
- // The local endpoint responed a 404...
241
+ // The local endpoint responded with a 404...
232
242
  setStatus(node, {fill:"yellow",shape:"dot",text:"remote-access.status.heartbeaterrornotfound"});
233
243
  node.log(`Heartbeat detected no valid endpoint, got a 404 response. Please check the base URL in the connection settings.`);
234
- } else if ( node.lastHeartbeatStatus !== 'OK' ) {
235
- if ( node.initialHeartbeatStatus === 'OK' ) {
236
- // If the status before was ok > Restart communication
244
+ } else {
245
+ // Heartbeat error - decide whether to reconnect
246
+ if ( node.configurationValidated === true ) {
247
+ // Configuration was validated before → Reconnect
237
248
  setStatus(node, {fill:"red",shape:"dot",text:"remote-access.status.heartbeaterror"});
238
249
  node.log(`Heartbeat error. Reconnecting soon.`);
239
250
  // Set serving to false, checkServing will trigger reconnect and startSSH will kill old process
240
251
  node.serving = false;
241
252
  } else {
242
- // If the status before had also an error > Just change the label.
243
- setStatus(node, {fill:"yellow",shape:"dot",text:"remote-access.status.heartbeaterroractive"});
244
- node.log(`Heartbeat detected no valid endpoint. Please check your connection settings (Base URL, Serving Port and Protocol).`);
253
+ // Never worked yet - check if still in startup phase
254
+ if ( node.initialRetryCount < 3 ) {
255
+ // Still in startup phase - give it more chances (after reboot with slow network, Starlink reconnect, etc.)
256
+ node.initialRetryCount++;
257
+ setStatus(node, {fill:"yellow",shape:"dot",text:"remote-access.status.heartbeaterror"});
258
+ node.log(`Heartbeat error during startup (attempt ${node.initialRetryCount}/3). Reconnecting soon.`);
259
+ node.serving = false; // Trigger reconnect
260
+ } else {
261
+ // Too many failed attempts > Misconfiguration > NO reconnect
262
+ setStatus(node, {fill:"yellow",shape:"dot",text:"remote-access.status.heartbeaterroractive"});
263
+ node.log(`Heartbeat detected no valid endpoint. Please check your connection settings (Base URL, Serving Port and Protocol).`);
264
+ }
245
265
  }
246
266
  }
247
267
  })
@@ -359,8 +379,9 @@ module.exports = function(RED) {
359
379
  }
360
380
 
361
381
  // Init heartbeat
362
- node.initialHeartbeatStatus = ''
363
- node.lastHeartbeatStatus = ''
382
+ node.configurationValidated = false; // Was the configuration EVER successfully validated?
383
+ node.lastHeartbeatStatus = '';
384
+ node.initialRetryCount = 0; // Counts retry attempts during startup phase
364
385
  node.heartbeatinterval = setInterval(heartbeat, 5*60*1000, node);
365
386
 
366
387
  // Call API for announce the instacehash and authentication, retrive server and port.
@@ -11,8 +11,7 @@
11
11
  instancehash: {value:""},
12
12
  server: {value:""},
13
13
  region: {value:"de"},
14
- customerhash: {value:""},
15
- customerkey: {value:""}
14
+ customerhash: {value:""}
16
15
  },
17
16
  credentials: {
18
17
  instanceauth: {type:"text"}
@@ -26,10 +25,32 @@
26
25
  document.getElementById('remoteInstanceInputs').style.visibility = 'collapse';
27
26
 
28
27
  // Deactivate region if already registered
29
- if (document.getElementById('node-config-input-instancehash').value != "") {
28
+ const alreadyRegistered = document.getElementById('node-config-input-instancehash').value != "";
29
+ if (alreadyRegistered) {
30
30
  document.getElementById('node-config-input-region').disabled = true;
31
31
  }
32
32
 
33
+ // Partner-Bereich: aufklappbar, Toggle togglet Sichtbarkeit
34
+ document.getElementById('node-config-input-showpartner').onchange = function() {
35
+ togglePartnerSection(this.checked);
36
+ };
37
+ // Bereits ein customerhash gesetzt? Dann direkt aufgeklappt anzeigen.
38
+ const hasCustomerhash = document.getElementById('node-config-input-customerhash').value != "";
39
+ if (hasCustomerhash) {
40
+ document.getElementById('node-config-input-showpartner').checked = true;
41
+ }
42
+ togglePartnerSection(hasCustomerhash);
43
+
44
+ // Instanz schon registriert? Partner-Zuordnung ist dann nicht mehr änderbar.
45
+ if (alreadyRegistered) {
46
+ document.getElementById('node-config-input-showpartner').disabled = true;
47
+ document.getElementById('node-config-input-customerhash').disabled = true;
48
+ document.getElementById('customerkeyRow').style.display = 'none';
49
+ document.getElementById('customerreferenceRow').style.display = 'none';
50
+ document.getElementById('partnerInfoEditable').style.display = 'none';
51
+ document.getElementById('partnerInfoReadonly').style.display = '';
52
+ }
53
+
33
54
  // Insert legal link urls
34
55
  setTimeout(insertLinkUrls, 250);
35
56
 
@@ -82,6 +103,18 @@
82
103
  document.getElementById('legalTermsLink').setAttribute('href', document.getElementById('terms_url_template').innerHTML);
83
104
  }
84
105
 
106
+ function togglePartnerSection(show) {
107
+ // Shows/hides the Remote-RED Partner Programm section
108
+ const section = document.getElementById('partnerSection');
109
+ if (show) {
110
+ section.style.removeProperty('visibility');
111
+ section.style.removeProperty('max-height');
112
+ } else {
113
+ section.style.visibility = 'collapse';
114
+ section.style.maxHeight = '0px';
115
+ }
116
+ }
117
+
85
118
  function updateLocalhostInfo() {
86
119
  // Gives infos about localhost, maybe hides it.
87
120
  if (document.getElementById('node-config-input-host').value.toLowerCase() == 'localhost') {
@@ -103,7 +136,18 @@
103
136
  function registerInstance() {
104
137
  // Request InstanceHash when empty
105
138
  const region = document.getElementById('node-config-input-region').value;
106
- $.getJSON('contrib-remote/requestInstanceHash/' + region + '/0/0', function(data) {
139
+ const customerhash = document.getElementById('node-config-input-customerhash').value || '0';
140
+ const customerkey = document.getElementById('node-config-input-customerkeyinput').value || '0';
141
+ const customerreference = document.getElementById('node-config-input-customerreferenceinput').value || '';
142
+ let requestUrl = 'contrib-remote/requestInstanceHash/' + region + '/' + encodeURIComponent(customerhash) + '/' + encodeURIComponent(customerkey);
143
+ if (customerreference !== '') {
144
+ requestUrl += '?customerreference=' + encodeURIComponent(customerreference);
145
+ }
146
+ $.getJSON(requestUrl, function(data) {
147
+ // CustomerKey und CustomerReference werden nur einmalig zur Registrierung benoetigt - danach aus dem UI entfernen.
148
+ document.getElementById('node-config-input-customerkeyinput').value = '';
149
+ document.getElementById('node-config-input-customerreferenceinput').value = '';
150
+
107
151
  if ( data.error == undefined ) {
108
152
  // Fill UI
109
153
  document.getElementById('node-config-input-instancehash').value = data.instancehash;
@@ -267,6 +311,41 @@
267
311
  </div>
268
312
  <label><i class="fa fa-info-circle"></i> <span data-i18n="remote-config.label.region_desc"></label>
269
313
 
314
+ <br>
315
+ <br>
316
+ <div class="form-row">
317
+ <input type="checkbox" id="node-config-input-showpartner" style="display:inline-block; width:22px; vertical-align:top;">
318
+ <label for="node-config-input-showpartner" style="width:auto;" data-i18n="remote-config.label.partner_toggle"></label>
319
+ </div>
320
+ <div id="partnerSection" style="visibility: collapse; max-height: 0px;">
321
+ <div id="partnerInfo" style="max-width: 460px; border-width: 1px; border-style: dashed; border-color: #666; padding: 10px; margin-bottom: 10px;">
322
+ <label data-i18n="remote-config.partner.title" style="font-weight: bold;"></label>
323
+ <label id="partnerInfoEditable" data-i18n="remote-config.partner.info1"></label>
324
+ <label id="partnerInfoReadonly" data-i18n="remote-config.partner.info1_readonly" style="display: none;"></label>
325
+ <a id="partnerPortalLink" href="https://portal.remote-red.com" target="_blank"><i><label data-i18n="remote-config.partner.link" style="display: inline; color: #AD1626;"></label></i></a>
326
+ </div>
327
+ <div class="form-row">
328
+ <label for="node-config-input-customerhash"><span data-i18n="remote-config.label.customerhash"></span></label>
329
+ <input type="text" id="node-config-input-customerhash" placeholder="">
330
+ </div>
331
+ <div id="customerkeyRow">
332
+ <div class="form-row">
333
+ <label for="node-config-input-customerkeyinput"><span data-i18n="remote-config.label.customerkey"></span></label>
334
+ <input type="password" id="node-config-input-customerkeyinput" placeholder="">
335
+ </div>
336
+ <label><i class="fa fa-info-circle"></i> <span data-i18n="remote-config.label.customerkey_desc"></label>
337
+ <br>
338
+ </div>
339
+ <div id="customerreferenceRow">
340
+ <div class="form-row">
341
+ <label for="node-config-input-customerreferenceinput"><span data-i18n="remote-config.label.customerreference"></span></label>
342
+ <input type="text" id="node-config-input-customerreferenceinput" placeholder="" maxlength="100">
343
+ </div>
344
+ <label><i class="fa fa-info-circle"></i> <span data-i18n="remote-config.label.customerreference_desc"></label>
345
+ <br>
346
+ </div>
347
+ </div>
348
+
270
349
  <br>
271
350
  <button id="registerRemoteAppButton" type="button" class="red-ui-button" onclick="registerButtonClicked()">
272
351
  <span id="registerRemoteAppButtonText" data-i18n="remote-config.actions.register_app">
@@ -5,6 +5,7 @@ module.exports = function(RED) {
5
5
  const dns = require('dns');
6
6
  const dnsPromises = dns.promises;
7
7
  const fs = require('fs');
8
+ const path = require('path');
8
9
  var QRCode = require('qrcode');
9
10
 
10
11
  function RemoteConfigNode(n) {
@@ -18,6 +19,7 @@ module.exports = function(RED) {
18
19
  this.server = n.server;
19
20
  this.instanceauth = this.credentials.instanceauth;
20
21
  this.region = n.region;
22
+ this.customerhash = n.customerhash;
21
23
  }
22
24
 
23
25
  RED.nodes.registerType("remote-config",RemoteConfigNode, {
@@ -53,14 +55,12 @@ module.exports = function(RED) {
53
55
  .then(res => {
54
56
  const v4 = res.some(e => e.family === 4);
55
57
  const v6 = res.some(e => e.family === 6);
56
- testResults.push({type: 'DNS', test: `DNS lookup ${host}`, result: getErrorMessage(error)});
58
+ testResults.push({type: 'DNS', test: `DNS lookup ${host}`, result: `OK (${v4 ? 'IPv4' : ''}${v4 && v6 ? ', ' : ''}${v6 ? 'IPv6' : ''})`});
57
59
  console.log(res);
58
- // testResults.push({test: `DNS lookup ${host}`, result: res.alive ? `${res.time}ms` : 'Error'});
59
60
  })
60
61
  .catch(error => {
61
62
  console.log(error);
62
63
  console.log(`dns resolve ${host}: ${getErrorMessage(error)}`);
63
- // console.log(error);
64
64
  testResults.push({type: 'DNS', test: `DNS lookup ${host}`, result: getErrorMessage(error)});
65
65
  });
66
66
  });
@@ -129,12 +129,19 @@ module.exports = function(RED) {
129
129
  url = 'https://contact-' + req.params.regionorserver + '.remote-red.com/instanceHashRequest';
130
130
  }
131
131
 
132
- // Call API
133
- axiosInstance.post(url, {
132
+ // Build request body; customerreference is optional (query param, backwards compatible
133
+ // with older CLI/node versions that never send it, PARTNER-PORTAL-KONZEPT Kap. 8.3 pt. 7)
134
+ const postData = {
134
135
  'customerhash': req.params.customerhash,
135
136
  'customerkey': req.params.customerkey,
136
137
  'version': commons.getNodeVersion()
137
- })
138
+ };
139
+ if (req.query.customerreference !== undefined && req.query.customerreference !== '') {
140
+ postData.customerreference = req.query.customerreference;
141
+ }
142
+
143
+ // Call API
144
+ axiosInstance.post(url, postData)
138
145
  .then(response => {
139
146
  res.json(response.data);
140
147
  })
@@ -207,7 +214,8 @@ module.exports = function(RED) {
207
214
 
208
215
  RED.httpAdmin.get("/contrib-remote/getResouceFile/:filename", RED.auth.needsPermission('remote-config.read'), function(req,res) {
209
216
  // Sends a file as Base64
210
- const fileBase64 = fs.readFileSync(__dirname + '/resources/' + req.params.filename, {encoding: 'base64'});
217
+ const safeName = path.basename(req.params.filename);
218
+ const fileBase64 = fs.readFileSync(__dirname + '/resources/' + safeName, {encoding: 'base64'});
211
219
  res.json({ 'filedata': fileBase64 });
212
220
  });
213
221
 
@@ -75,7 +75,7 @@ module.exports = function(RED) {
75
75
  });
76
76
  } else {
77
77
  // To big...
78
- node.error("xxxThe message exceeded 3600 bytes. Can´t send.");
78
+ node.error("The message exceeded 3600 bytes. Can´t send.");
79
79
 
80
80
  // Output status if configured so
81
81
  if ( config.output == 2 ) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-remote",
3
- "version": "2.0.4",
3
+ "version": "3.0.0",
4
4
  "description": "Remote-RED is an ecosystem to bring remote access, push notification and geofencing to Node-RED. There are also additional functions like directly answer on a notification and homescreen widgets.",
5
5
  "author": "Thorsten Heilmann",
6
6
  "license": "GPL-3.0",