@gregoriusrippenstein/node-red-contrib-introspection 0.4.5 → 0.5.1

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.
@@ -15,7 +15,7 @@
15
15
  url: "ClientCode/" + nodeid,
16
16
  type: "POST",
17
17
  contentType: "application/json; charset=utf-8",
18
- data: JSON.stringify(data),
18
+ data: JSON.stringify(data),
19
19
 
20
20
  success: function (resp) {
21
21
  },
@@ -31,6 +31,27 @@
31
31
  });
32
32
  };
33
33
 
34
+ var doStatus = (sts, nodeid, _msg) => {
35
+ $.ajax({
36
+ url: "ClientCode/" + nodeid + "/status",
37
+ type: "POST",
38
+ contentType: "application/json; charset=utf-8",
39
+ data: JSON.stringify(sts),
40
+
41
+ success: function (resp) {
42
+ },
43
+
44
+ error: function (jqXHR, textStatus, errorThrown) {
45
+ RED.notify("ClientCode Communcation Failure: " +
46
+ nodeid + ": " + textStatus, {
47
+ type: "error",
48
+ id: nodeid,
49
+ timeout: 3000
50
+ });
51
+ }
52
+ });
53
+ }
54
+
34
55
  var doError = (msg, nodeid, _msg) => {
35
56
  RED.notify("ClientCode Failed: " + nodeid + ": " + msg, {
36
57
  type: "error",
@@ -43,15 +64,13 @@
43
64
 
44
65
  var nodeid = data.nodeid;
45
66
  var _msg = data._msg;
67
+ var msg = data._msg;
46
68
 
47
69
  var node = {
48
- send: (dt) => {
49
- doSend(dt, nodeid, _msg)
50
- },
51
- error: (mg) => {
52
- doError(mg, nodeid, _msg)
53
- },
54
- id: data.nodeid
70
+ id: data.nodeid,
71
+ send: (dt) => { doSend(dt, nodeid, _msg) },
72
+ error: (mg) => { doError(mg, nodeid, _msg) },
73
+ status: (sts) => { doStatus(sts, nodeid, _msg) }
55
74
  };
56
75
 
57
76
  var payload = data.payload;
@@ -105,8 +124,19 @@
105
124
 
106
125
  this.editor = RED.editor.createEditor({
107
126
  id: 'node-input-clientcode-editor',
108
- mode: 'ace/mode/javascript',
127
+ mode: 'ace/mode/nrjavascript',
109
128
  stateId: stateId,
129
+ globals: {
130
+ msg:true,
131
+ RED: true,
132
+ node: true,
133
+ console: true,
134
+ Buffer: true,
135
+ setTimeout: true,
136
+ clearTimeout: true,
137
+ setInterval: true,
138
+ clearInterval: true
139
+ },
110
140
  value: $("#node-input-clientcode").val()
111
141
  });
112
142
 
@@ -202,4 +232,34 @@
202
232
 
203
233
  <script type="text/html" data-help-name="ClientCode">
204
234
  <p>Execute Javascript code in the Node-RED frontend, triggered from the server.</p>
235
+
236
+ The code is executed in the users browser and has the following enviornment defined:
237
+ <pre>
238
+ <code>
239
+ Variables:
240
+ nodeid - id of this node
241
+ _msg - a reference to the msg object passed to this node
242
+ msg - alias for _msg
243
+ node.id - id of this node, same as `nodeid` above
244
+ payload - a reference to msg.payload as it was received by this node
245
+ topic - a reference to the msg.topic as it was recieved by this node
246
+
247
+ Functionality:
248
+ node.send({..}) - send data to the output port of this node
249
+ node.error("msg") - generate an error for the node
250
+ node.status( { fill: 'red', shape: 'dot', text: 'hello world'}) - generate a status for the node.
251
+ </code>
252
+ </pre>
253
+
254
+ <p>
255
+ This might be confusing since the code in the node <b>is not</b>
256
+ executed on the Node-RED server, rather it is executed in the client
257
+ browser.
258
+ <p>
259
+ This of course makes no sense for flows that are executed "head less"
260
+ on the server in the backgrond on some dark and stormy night. The ClientCode
261
+ node is intended for immediate, frontend-only use.
262
+ <p>
263
+ An example flow with some use cases is included and can be accessed either via
264
+ the Import Flow dialog or accessed online <a style="color: blue" href="https://flowhub.org/f/e02ba6e534f7a0f4" target="_blank">at here <i class="fa fa-external-link"></i></a>.
205
265
  </script>
@@ -47,4 +47,27 @@ module.exports = function(RED) {
47
47
  }
48
48
  });
49
49
 
50
+
51
+ RED.httpAdmin.post("/ClientCode/:id/status",
52
+ RED.auth.needsPermission("ClientCode.write"),
53
+ (req, res) => {
54
+ var node = RED.nodes.getNode(req.params.id);
55
+ if (node != null) {
56
+ try {
57
+ if (req.body && node.type == "ClientCode") {
58
+ node.status(req.body);
59
+ res.sendStatus(200);
60
+ } else {
61
+ res.sendStatus(404);
62
+ }
63
+ } catch (err) {
64
+ res.sendStatus(500);
65
+ node.error("ClientCode: Submission failed: " +
66
+ err.toString())
67
+ }
68
+ } else {
69
+ res.sendStatus(404);
70
+ }
71
+ });
72
+
50
73
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gregoriusrippenstein/node-red-contrib-introspection",
3
- "version": "0.4.5",
3
+ "version": "0.5.1",
4
4
  "dependencies": {
5
5
  "got": "latest"
6
6
  },
@@ -15,14 +15,13 @@
15
15
  },
16
16
  "node-red": {
17
17
  "version": ">=2.0.0",
18
+ "plugins": {
19
+ "screenshot": "plugins/screenshot.html",
20
+ "orphans": "plugins/orphans.html"
21
+ },
18
22
  "nodes": {
19
23
  "seeker": "nodes/05-seeker.js",
20
24
  "sink": "nodes/10-sink.js",
21
- "screenshot": "nodes/15-screenshot.js",
22
- "orphans": "nodes/20-orphans.js",
23
- "ismobile": "nodes/25-ismobile.js",
24
- "navigator": "nodes/30-navigator.js",
25
- "drawsvg": "nodes/40-drawsvg.js",
26
25
  "getflows": "nodes/45-get-flows.js",
27
26
  "sendflow": "nodes/50-send-flow.js",
28
27
  "clientcode": "nodes/60-client-code.js"
@@ -0,0 +1,45 @@
1
+ <script type="text/javascript">
2
+ (function() {
3
+ function setupTreelist(){var e=collectOrphans();if(0==e.length){RED.notify("No Orphans Found",{type:"warning",timeout:2e3});try{$("#node-input-orphan-target-container-div").treeList("empty")}catch(e){}}else{try{$("#node-input-orphan-target-container-div").treeList("empty")}catch(e){$("#node-input-orphan-target-container-div").css({width:"100%",height:"400px"}).treeList({multi:!1}).on("treelistitemmouseover",function(e,t){t.node&&t.node.z==RED.workspaces.active()&&(RED.view.reveal(t.node.id,!0),RED.view.redraw())}).on("treelistitemmouseout",function(e,t){}).on("treelistselect",function(e,t){t.node&&(RED.workspaces.show(t.node.z,!1,!1,!0),RED.view.reveal(t.node.id,!0),RED.view.redraw())}),$("#node-input-orphan-target-filter").show();var n=$("#node-input-orphan-target-filter").searchBox({style:"compact",delay:300,change:function(){var e,t=$(this).val().trim().toLowerCase();""===t?($("#node-input-orphan-target-container-div").treeList("filter",null),n.searchBox("count","")):(e=$("#node-input-orphan-target-container-div").treeList("filter",function(e){return-1<e.label.toLowerCase().indexOf(t)||-1<e.node.type.toLowerCase().indexOf(t)}),n.searchBox("count",e+" / "+$("#node-input-orphan-target-container-div").treeList("data").length))}})}$("#node-input-orphan-target-container-div").treeList("data",e.sort((e,t)=>e.node.z>t.node.z))}}function collectOrphans(){const t=new Set;var n=[],r=(RED.nodes.eachLink(e=>{t.add(e.source),t.add(e.target)}),RED.nodes.eachNode(e=>{t.has(e)||n.push(e)}),[]),i={};return n.forEach(function(e){var t=RED.nodes.getType(e.type);if(t){var n=t.label,n=("function"==typeof n?n.call(e):n)||"",o=e.type;if(0===o.indexOf("subflow:"))return}t&&n||(n=e.type),i[e.id]={node:e,label:n,sublabel:o,selected:!1,checkbox:!1},r.push(i[e.id])}),r}
4
+
5
+ var initialiseSidebarOrphanNodeOnce = () => {
6
+ RED.events.off('runtime-state', initialiseSidebarOrphanNodeOnce);
7
+
8
+ // The html content of the sidebar has been specified below as a data-template, from where it can be loaded:
9
+ var content = $($('script[type="text/x-red"][data-template-name="Orphans"]').i18n().html());
10
+
11
+ // Add a "Your sidebar" tabsheet to the right sidebar panel, in which this sidebar panel can be displayed
12
+ // --> more details: https://nodered.org/docs/api/ui/sidebar/
13
+ RED.sidebar.addTab({
14
+ id: "Orphans",
15
+ label: "Orphans", // short name for the tab
16
+ name: "Orphan Nodes", // long name for the menu
17
+ content: content,
18
+ enableOnEdit: true,
19
+ iconClass: "fa fa-life-ring" // your fontawesome icon
20
+ });
21
+
22
+ // When the user has entered new data in the sidebar, then store it into the config node
23
+ $("#node-input-orphan-find-btn").on("click", function(e) {
24
+ if ( e ) { e.preventDefault() }
25
+ setupTreelist();
26
+ })
27
+ };
28
+ RED.events.on('runtime-state', initialiseSidebarOrphanNodeOnce);
29
+ })();
30
+ </script>
31
+
32
+ <!-- The html for the right sidebar plugin screen -->
33
+ <script type="text/x-red" data-template-name="Orphans">
34
+ <div class="form-row node-input-target-row" style="margin-left: 10px; margin-top: 30px">
35
+ <button id="node-input-orphan-find-btn"
36
+ class="red-ui-button">Find Orphans</button>
37
+ </div>
38
+
39
+ <div class="form-row node-input-target-row node-input-target-list-row" style="margin-left: 10px; position: relative; min-height: 200px; margin-right: 15px;">
40
+ <div style="margin-bottom: 5px; width: 35%; padding-left: 60%;">
41
+ <input type="text" id="node-input-orphan-target-filter" style="display: none;">
42
+ </div>
43
+ <div id="node-input-orphan-target-container-div"></div>
44
+ </div>
45
+ </script>
@@ -0,0 +1,74 @@
1
+ <script type="text/javascript">
2
+ (function() {
3
+ var globalRefToSvgData;
4
+
5
+ function nr_intro_generate_svg_3_1(r){return e=>{try{handleSvgObject($($("svg[width=8000]")[0]),e)}catch(t){var n="Error Generating SVG: "+JSON.stringify(t);r.notify(n,{type:"error"}),e('<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg width="1000" height="1000" viewBox="0 0 1000 1000" pointer-events="all" style="cursor: crosshair; touch-action: none;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><style>.small { font: bold 20px sans-serif; fill: red;}</style><text x="10" y="30" class="small">'+n+"</text></svg>")}}}function nr_intro_generate_svg_3_0(r){return e=>{try{handleSvgObject($($("svg")[0]),e)}catch(t){var n="Error Generating SVG: "+JSON.stringify(t);r.notify(n,{type:"error"}),e('<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg width="1000" height="1000" viewBox="0 0 1000 1000" pointer-events="all" style="cursor: crosshair; touch-action: none;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><style>.small { font: bold 20px sans-serif; fill: red;}</style><text x="10" y="30" class="small">'+n+"</text></svg>")}}}function handleSvgObject(o,e){var t=o.clone();t.find("foreignObject").remove(),t.find("svg.__screenshot").remove();function a(t,e){for(var n=0;n<t.length;n++){var s=t.item(n),i=e[n];["stroke-width","fill-opacity","stroke-opacity","opacity","stroke-dasharray"].forEach(function(t){s.setAttribute(t,$(i).attr(t)||$(i).css(t))}),["fill","stroke"].forEach(function(t){var e,n,r=(e=$(i).attr(t)||$(i).css(t))&&null!==e&&"none"!=e?(n=e.match(/^#(.)(.)(.)$/))?"#"+n[1]+n[1]+n[2]+n[2]+n[3]+n[3]:(n=e.match(/^#......$/))?e:null===(n=e.match(/^rgb\(([0-9]+),\s+([0-9]+),\s+([0-9]+)/))?(r=e.match(/^rgba\(([0-9]+),\s+([0-9]+),\s+([0-9]+),\s+([0-9]+)/))?{clr:"#"+("0"+parseInt(r[1],10).toString(16)).slice(-2)+("0"+parseInt(r[2],10).toString(16)).slice(-2)+("0"+parseInt(r[3],10).toString(16)).slice(-2),opa:r[4]}:(console.log("Screenshot node: returned unknown color: "+e),e):"#"+("0"+parseInt(n[1],10).toString(16)).slice(-2)+("0"+parseInt(n[2],10).toString(16)).slice(-2)+("0"+parseInt(n[3],10).toString(16)).slice(-2):"none";"object"==typeof r?(s.setAttribute(t+"-opacity",r.opa),s.setAttribute(t,r.clr)):s.setAttribute(t,r)}),$(i).hasClass("hide")&&("g"==s.tagName&&s.setAttribute("opacity","0"),s.setAttribute("visibility","hidden"))}}var n='<?xml version="1.0" standalone="no"?>\r\n<svg '+('width="'+o.attr("width")+'" height="'+o.attr("height")+'"')+' pointer-events="all" style="cursor: crosshair; touch-action: none;" xmlns="http://www.w3.org/2000/svg" class="__screenshot" xmlns:xlink="http://www.w3.org/1999/xlink">\r\n',t=t.html(),l=(new DOMParser).parseFromString(n+t+"\r\n</svg>","image/svg+xml"),r=e=>(["g","rect","line","path","circle","image","text"].forEach(t=>{$(e.getElementsByTagName(t)).each((t,e)=>{e.setAttribute("class",""),e.setAttribute("id","")})}),e),s=(["g","rect","line","path","circle","image"].forEach(function(t){a(l.getElementsByTagName(t),o.find(t))}),["text"].forEach(function(t){a(l.getElementsByTagName(t),o.find(t));for(var e=l.getElementsByTagName(t),n=o.find(t),r=0;r<e.length;r++){var s=e.item(r),i=n[r];["font-family","font-size","font-size-adjust","font-stretch","font-style","font-variant","font-weight","text-anchor","dominant-baseline"].forEach(function(t){s.setAttribute(t,$(i).attr(t)||$(i).css(t))})}}),l.getElementsByTagName("image")),g={},i=(n,r,s)=>{var i=n.getAttribute("xlink:href"),o=i.substr(-4,4).toLowerCase(),a={".jpg":"jpeg",jpeg:"jpeg",".png":"png",".svg":"svg+xml"};if(g[i])return n.setAttribute("xlink:href","data:image/"+a[o]+";base64,"+g[i]),s(r-1);switch(o){case".jpg":case"jpeg":case".png":var l=new XMLHttpRequest;l.open("GET",i,!0),l.responseType="arraybuffer";l.onload=function(t){var e=l.response;e&&(e=(t=>{for(var e="",n=new Uint8Array(t),r=n.byteLength,s=0;s<r;s++)e+=String.fromCharCode(n[s]);return window.btoa(e)})(e),g[i]=e,n.setAttribute("xlink:href","data:image/"+a[o]+";base64,"+e)),s(r-1)},l.send(null);break;case".svg":$.get(i,function(t){var e=new XMLSerializer,e=btoa(e.serializeToString(t));g[i]=e,n.setAttribute("xlink:href","data:image/svg+xml;base64,"+e),s(r-1)})}},c=t=>{t<0?e((new XMLSerializer).serializeToString(r(l))):i(s.item(t),t,c)};0<s.length?i(s.item(s.length-1),s.length-1,c):e((new XMLSerializer).serializeToString(r(l)))}function generatorFunctionForVersion(t){var n,e=t.settings.version.split("."),r=e[0],e=e[1];if("3"==r){if("0"==e)return nr_intro_generate_svg_3_0(t);if("1"==e)return nr_intro_generate_svg_3_1(t)}return n=t,t=>{var e="Node-RED version ("+n.settings.version+") not supported";n.notify(e,{type:"error"}),t&&t('<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg width="1000" height="1000" viewBox="0 0 1000 1000" pointer-events="all" style="cursor: crosshair; touch-action: none;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><style>.small { font: bold 20px sans-serif; fill: red;}</style><text x="10" y="30" class="small">'+e+"</text></svg>")}}
6
+
7
+ // Add your plugin as a new tabsheet in the right sidebar AFTER the flow editor is completely started
8
+ var initialiseConfigNodeOnce = () => {
9
+ RED.events.off('runtime-state', initialiseConfigNodeOnce);
10
+
11
+ // The html content of the sidebar has been specified below as a data-template, from where it can be loaded:
12
+ var content = $($('script[type="text/x-red"][data-template-name="Screenshot"]').i18n().html());
13
+
14
+ // Add a "Your sidebar" tabsheet to the right sidebar panel, in which this sidebar panel can be displayed
15
+ // --> more details: https://nodered.org/docs/api/ui/sidebar/
16
+ RED.sidebar.addTab({
17
+ id: "Screenshot",
18
+ label: "Screenshot", // short name for the tab
19
+ name: "Screenshot", // long name for the menu
20
+ content: content,
21
+ enableOnEdit: true,
22
+ iconClass: "fa fa-camera" // your fontawesome icon
23
+ });
24
+
25
+ $('#node-screenshot-capture-btn').on("click", function (e) {
26
+ if ( e ) { e.preventDefault() }
27
+
28
+ $('#node-input-screenshot-svgcontainer').html( "Please wait, screenshot being prepared ..." );
29
+
30
+ generatorFunctionForVersion(RED)( (svgdata) => {
31
+ $('#node-input-screenshot-svgcontainer').html(svgdata);
32
+ globalRefToSvgData = svgdata;
33
+ });
34
+ })
35
+
36
+ // handle the download button under the editor window.
37
+ $('#node-screenshot-download-svg').on("click", function (e) {
38
+ if (e) { e.preventDefault(); }
39
+ var svgBlob = new Blob([globalRefToSvgData], {type:"image/svg+xml;charset=utf-8"});
40
+ var svgUrl = URL.createObjectURL(svgBlob);
41
+ var downloadLink = document.createElement("a");
42
+ downloadLink.href = svgUrl;
43
+ downloadLink.download = "screenshot.svg";
44
+ document.body.appendChild(downloadLink);
45
+ downloadLink.click();
46
+ document.body.removeChild(downloadLink);
47
+ });
48
+
49
+ $('#node-screenshot-copy-to-clipboard-svg').on("click", function (e) {
50
+ if (e) { e.preventDefault(); }
51
+ if ( RED.clipboard.copyText(globalRefToSvgData) ) {
52
+ RED.notify("SVG copied to clipboard", { type: "success" });
53
+ }
54
+ });
55
+ };
56
+ RED.events.on('runtime-state', initialiseConfigNodeOnce);
57
+ })();
58
+ </script>
59
+
60
+ <!-- The html for the right sidebar plugin screen -->
61
+ <script type="text/x-red" data-template-name="Screenshot">
62
+ <div class="form-row" style="margin-left: 10px; margin-top: 30px;">
63
+ <button type="button" id="node-screenshot-capture-btn" class="red-ui-button red-ui-button-large"><i class="fa fa-camera"></i> Capture</button>
64
+ </div>
65
+
66
+ <div class="form-row" style="min-height: 300px; overflow: scroll; margin-left: 10px; margin-right: 15px; height: 300px; border: 1px rgb(196, 196, 196) solid; border-radius: 5px;">
67
+ <div id="node-input-screenshot-svgcontainer"></div>
68
+ </div>
69
+
70
+ <div class="form-row" style="margin-left: 10px; margin-top: 5px;">
71
+ <button type="button" id="node-screenshot-download-svg" class="red-ui-button red-ui-button-large"><i class="fa fa-download"></i>Download</button>
72
+ <button type="button" id="node-screenshot-copy-to-clipboard-svg" class="red-ui-button red-ui-button-large"><i class="fa fa-clipboard"></i>Copy to Clipboard</button>
73
+ </div>
74
+ </script>
@@ -1,155 +0,0 @@
1
- [
2
- {
3
- "id": "e815c52d1e6a0d25",
4
- "type": "tab",
5
- "label": "Trigger Screenshot Example",
6
- "disabled": false,
7
- "info": "",
8
- "env": []
9
- },
10
- {
11
- "id": "03f322255fd8cad1",
12
- "type": "http in",
13
- "z": "e815c52d1e6a0d25",
14
- "name": "[POST] screenshot",
15
- "url": "/screenshot",
16
- "method": "post",
17
- "upload": false,
18
- "swaggerDoc": "",
19
- "x": 388,
20
- "y": 305,
21
- "wires": [
22
- [
23
- "ddabce426445e225",
24
- "a9f9ce494ec4edc1",
25
- "c81d27b03cf13c69"
26
- ]
27
- ]
28
- },
29
- {
30
- "id": "ddabce426445e225",
31
- "type": "debug",
32
- "z": "e815c52d1e6a0d25",
33
- "name": "debug 1",
34
- "active": false,
35
- "tosidebar": true,
36
- "console": false,
37
- "tostatus": false,
38
- "complete": "true",
39
- "targetType": "full",
40
- "statusVal": "",
41
- "statusType": "auto",
42
- "x": 1175,
43
- "y": 307,
44
- "wires": []
45
- },
46
- {
47
- "id": "e62ae897ba4c777f",
48
- "type": "http response",
49
- "z": "e815c52d1e6a0d25",
50
- "name": "",
51
- "statusCode": "",
52
- "headers": {
53
- "content-type": "application/json"
54
- },
55
- "x": 817,
56
- "y": 624,
57
- "wires": []
58
- },
59
- {
60
- "id": "a9f9ce494ec4edc1",
61
- "type": "function",
62
- "z": "e815c52d1e6a0d25",
63
- "name": "respond with ok",
64
- "func": "msg.payload = {\n status: \"ok\",\n data: {\n }\n};\nmsg.statusCode = 200;\nreturn msg;\n",
65
- "outputs": 1,
66
- "noerr": 0,
67
- "initialize": "",
68
- "finalize": "",
69
- "libs": [],
70
- "x": 622,
71
- "y": 517,
72
- "wires": [
73
- [
74
- "e62ae897ba4c777f"
75
- ]
76
- ]
77
- },
78
- {
79
- "id": "c81d27b03cf13c69",
80
- "type": "function",
81
- "z": "e815c52d1e6a0d25",
82
- "name": "set filename & payload",
83
- "func": "msg.filename = \"/data/content/screenshot-\" + Date.now() + \".svg\";\n\nmsg.payload = msg.payload[\"d\"];\n\nreturn msg;",
84
- "outputs": 1,
85
- "noerr": 0,
86
- "initialize": "",
87
- "finalize": "",
88
- "libs": [],
89
- "x": 744,
90
- "y": 410,
91
- "wires": [
92
- [
93
- "00c967aaf678c503"
94
- ]
95
- ]
96
- },
97
- {
98
- "id": "00c967aaf678c503",
99
- "type": "file",
100
- "z": "e815c52d1e6a0d25",
101
- "name": "",
102
- "filename": "filename",
103
- "filenameType": "msg",
104
- "appendNewline": false,
105
- "createDir": true,
106
- "overwriteFile": "true",
107
- "encoding": "none",
108
- "x": 1012,
109
- "y": 505,
110
- "wires": [
111
- [
112
- "ddabce426445e225"
113
- ]
114
- ]
115
- },
116
- {
117
- "id": "32cde1648d31e9b4",
118
- "type": "Screenshot",
119
- "z": "e815c52d1e6a0d25",
120
- "name": "",
121
- "screenshot": "",
122
- "x": 461,
123
- "y": 108,
124
- "wires": []
125
- },
126
- {
127
- "id": "3957fb7da9d89393",
128
- "type": "inject",
129
- "z": "e815c52d1e6a0d25",
130
- "name": "",
131
- "props": [
132
- {
133
- "p": "payload"
134
- },
135
- {
136
- "p": "topic",
137
- "vt": "str"
138
- }
139
- ],
140
- "repeat": "600",
141
- "crontab": "",
142
- "once": true,
143
- "onceDelay": "2",
144
- "topic": "",
145
- "payload": "",
146
- "payloadType": "date",
147
- "x": 213,
148
- "y": 108,
149
- "wires": [
150
- [
151
- "32cde1648d31e9b4"
152
- ]
153
- ]
154
- }
155
- ]