@mschaeffler/node-red-hourmeter 0.2.5 → 1.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,7 +29,7 @@ The counter is started / stopped according to the value of the payload:
29
29
  ||on|started|
30
30
  ||start|started|
31
31
 
32
- A [local filesystem context store](https://nodered.org/docs/user-guide/context#saving-context-data-to-the-file-system) called `storeInFile` is needed to store the internal data.
32
+ A [local filesystem context store](https://nodered.org/docs/user-guide/context#saving-context-data-to-the-file-system) is needed to store the internal data.
33
33
 
34
34
  ### Input
35
35
 
@@ -37,7 +37,8 @@ A [local filesystem context store](https://nodered.org/docs/user-guide/context#s
37
37
  |:-------|:-------|:----------------------------------|
38
38
  |payload | | Starts or stopps the counting. |
39
39
  |reset |boolean |If true, resets the counter to 0.|
40
- |querry |boolean |If true, just querries the state of the counter.|
40
+ |set |boolean |If true, sets the counter to `payload`.|
41
+ |query |boolean |If true, just querries the state of the counter.|
41
42
 
42
43
  ### Outputs
43
44
 
@@ -45,13 +46,15 @@ A [local filesystem context store](https://nodered.org/docs/user-guide/context#s
45
46
 
46
47
  |msg. | type | description |
47
48
  |:-------|:-------|:----------------------------------|
48
- |payload | boolean | Is counting active?|
49
+ |payload | boolean| Is counting active?|
50
+ |reason | string | Reason of the message output.|
49
51
 
50
52
  #### hour counter
51
53
 
52
54
  |msg. | type | description |
53
55
  |:-------|:-------|:----------------------------------|
54
56
  |payload | number | Value of the hour counter.|
57
+ |reason | string | Reason of the message output.|
55
58
 
56
59
  ### Parameters
57
60
 
@@ -59,6 +62,8 @@ A [local filesystem context store](https://nodered.org/docs/user-guide/context#s
59
62
  |:-----|:-------|:----------------------------------|
60
63
  |topic| string | Topic to send output values with.|
61
64
  |cycle| number |Cyclic time of output; 0 is only at state change.|
65
+ |Contextstore|context store| context store for storing the values.|
66
+ |Status|boolean| the actual value as a node status.|
62
67
 
63
68
  ## Example Flow
64
69
 
@@ -1,12 +1,4 @@
1
1
  [
2
- {
3
- "id": "5535ca07b24e93fb",
4
- "type": "tab",
5
- "label": "Flow 1",
6
- "disabled": false,
7
- "info": "",
8
- "env": []
9
- },
10
2
  {
11
3
  "id": "3412a0829ce8c730",
12
4
  "type": "inject",
@@ -25,11 +17,11 @@
25
17
  "crontab": "",
26
18
  "once": false,
27
19
  "onceDelay": 0.1,
28
- "topic": "",
29
- "payload": "on",
30
- "payloadType": "str",
31
- "x": 430,
32
- "y": 300,
20
+ "topic": "FooBar",
21
+ "payload": "true",
22
+ "payloadType": "bool",
23
+ "x": 470,
24
+ "y": 320,
33
25
  "wires": [
34
26
  [
35
27
  "fd93870645a4a85f"
@@ -54,11 +46,11 @@
54
46
  "crontab": "",
55
47
  "once": false,
56
48
  "onceDelay": 0.1,
57
- "topic": "",
58
- "payload": "0",
59
- "payloadType": "num",
60
- "x": 430,
61
- "y": 340,
49
+ "topic": "FooBar",
50
+ "payload": "false",
51
+ "payloadType": "bool",
52
+ "x": 470,
53
+ "y": 360,
62
54
  "wires": [
63
55
  [
64
56
  "fd93870645a4a85f"
@@ -70,7 +62,7 @@
70
62
  "type": "debug",
71
63
  "z": "5535ca07b24e93fb",
72
64
  "name": "debug 8",
73
- "active": false,
65
+ "active": true,
74
66
  "tosidebar": true,
75
67
  "console": false,
76
68
  "tostatus": true,
@@ -92,10 +84,6 @@
92
84
  "p": "reset",
93
85
  "v": "true",
94
86
  "vt": "bool"
95
- },
96
- {
97
- "p": "topic",
98
- "vt": "str"
99
87
  }
100
88
  ],
101
89
  "repeat": "",
@@ -103,8 +91,8 @@
103
91
  "once": false,
104
92
  "onceDelay": 0.1,
105
93
  "topic": "",
106
- "x": 430,
107
- "y": 180,
94
+ "x": 470,
95
+ "y": 200,
108
96
  "wires": [
109
97
  [
110
98
  "fd93870645a4a85f"
@@ -121,10 +109,6 @@
121
109
  "p": "querry",
122
110
  "v": "true",
123
111
  "vt": "bool"
124
- },
125
- {
126
- "p": "topic",
127
- "vt": "str"
128
112
  }
129
113
  ],
130
114
  "repeat": "",
@@ -132,8 +116,8 @@
132
116
  "once": false,
133
117
  "onceDelay": 0.1,
134
118
  "topic": "",
135
- "x": 430,
136
- "y": 240,
119
+ "x": 470,
120
+ "y": 260,
137
121
  "wires": [
138
122
  [
139
123
  "fd93870645a4a85f"
@@ -147,6 +131,8 @@
147
131
  "name": "",
148
132
  "topic": "test",
149
133
  "cycle": "15",
134
+ "contextStore": "memoryOnly",
135
+ "showState": true,
150
136
  "x": 650,
151
137
  "y": 260,
152
138
  "wires": [
@@ -163,7 +149,7 @@
163
149
  "type": "debug",
164
150
  "z": "5535ca07b24e93fb",
165
151
  "name": "debug 9",
166
- "active": false,
152
+ "active": true,
167
153
  "tosidebar": true,
168
154
  "console": false,
169
155
  "tostatus": true,
@@ -174,5 +160,43 @@
174
160
  "x": 840,
175
161
  "y": 300,
176
162
  "wires": []
163
+ },
164
+ {
165
+ "id": "a831f554dd6fce40",
166
+ "type": "inject",
167
+ "z": "5535ca07b24e93fb",
168
+ "name": "set",
169
+ "props": [
170
+ {
171
+ "p": "set",
172
+ "v": "true",
173
+ "vt": "bool"
174
+ },
175
+ {
176
+ "p": "payload"
177
+ }
178
+ ],
179
+ "repeat": "",
180
+ "crontab": "",
181
+ "once": false,
182
+ "onceDelay": 0.1,
183
+ "topic": "",
184
+ "payload": "12",
185
+ "payloadType": "num",
186
+ "x": 470,
187
+ "y": 160,
188
+ "wires": [
189
+ [
190
+ "fd93870645a4a85f"
191
+ ]
192
+ ]
193
+ },
194
+ {
195
+ "id": "55acd484dd996916",
196
+ "type": "global-config",
197
+ "env": [],
198
+ "modules": {
199
+ "@mschaeffler/node-red-hourmeter": "1.0.0"
200
+ }
177
201
  }
178
202
  ]
Binary file
package/hourmeter.html CHANGED
@@ -5,7 +5,9 @@
5
5
  defaults: {
6
6
  name: {value:""},
7
7
  topic:{value:""},
8
- cycle:{value:"0", required:true, validate:RED.validators.number()}
8
+ cycle:{value:"0", required:true, validate:RED.validators.number()},
9
+ contextStore:{value:"",required:true},
10
+ showState:{value:false}
9
11
  },
10
12
  inputs:1,
11
13
  outputs:2,
@@ -18,7 +20,23 @@
18
20
  },
19
21
  inputLabels: 'on/off message',
20
22
  outputLabels: ['boolean value','hour counter'],
21
- paletteLabel: "hour meter"
23
+ paletteLabel: "hour meter",
24
+ oneditprepare: function() {
25
+ const node = this
26
+ // populate store names
27
+ $("#node-input-contextStore").empty();
28
+ //$("#node-input-contextStore").append(`<option value="none">None</option>`);
29
+ const defaultStore = RED.settings.hasOwnProperty('context') ? RED.settings.context.default : ''
30
+ RED.settings.context.stores.forEach(function(item){
31
+ if( item ) {
32
+ const opt = $(`<option value="${item}">${item}${item===defaultStore?" (default)":""}</option>`);
33
+ $("#node-input-contextStore").append(opt);
34
+ }
35
+ });
36
+ const currentStore = node.contextStore || '';
37
+ // select the current option
38
+ $("#node-input-contextStore").val(currentStore);
39
+ }
22
40
  });
23
41
  </script>
24
42
 
@@ -35,6 +53,14 @@
35
53
  <label for="node-input-cycle"><i class="fa fa-hourglass-o"></i> Cycle [min]</label>
36
54
  <input type="text" id="node-input-cycle">
37
55
  </div>
56
+ <div class="form-row">
57
+ <label for="node-input-contextStore"><i class="fa fa-database"></i> Contextstore</label>
58
+ <select type="text" id="node-input-contextStore"></select>
59
+ </div>
60
+ <div class="form-row">
61
+ <label for="node-input-showStatus"><i class="fa fa-star-o"></i> Status</label>
62
+ <input type="checkbox" id="node-input-showState" style="display:inline-block; width:20px; vertical-align:baseline;">
63
+ </div>
38
64
  </script>
39
65
 
40
66
  <script type="text/html" data-help-name="hourmeter">
@@ -56,7 +82,7 @@
56
82
  </table>
57
83
 
58
84
  <p>A <a href="https://nodered.org/docs/user-guide/context#saving-context-data-to-the-file-system">local filesystem context store</a>
59
- called <code>storeInFile</code> is needed to store the internal data.</p>
85
+ is needed to store the internal data.</p>
60
86
 
61
87
  <h3>Input</h3>
62
88
  <dl class="message-properties">
@@ -64,7 +90,9 @@
64
90
  <dd> Starts or stopps the counting.</dd>
65
91
  <dt>reset <span class="property-type">boolean</span></dt>
66
92
  <dd> If true, resets the counter to 0.</dd>
67
- <dt>querry <span class="property-type">boolean</span></dt>
93
+ <dt>set <span class="property-type">boolean</span></dt>
94
+ <dd> If true, sets the counter to <code>payload</code>.</dd>
95
+ <dt>query <span class="property-type">boolean</span></dt>
68
96
  <dd> If true, just querries the state of the counter.</dd>
69
97
  </dl>
70
98
 
@@ -74,12 +102,16 @@
74
102
  <dl class="message-properties">
75
103
  <dt>payload <span class="property-type">boolean</span></dt>
76
104
  <dd> Is counting active?</dd>
105
+ <dt>reason <span class="property-type">string</span></dt>
106
+ <dd> Reason of the message output.</dd>
77
107
  </dl>
78
108
 
79
109
  <h4>hour counter</h4>
80
110
  <dl class="message-properties">
81
111
  <dt>payload <span class="property-type">number</span></dt>
82
112
  <dd> Value of the hour counter.</dd>
113
+ <dt>reason <span class="property-type">string</span></dt>
114
+ <dd> Reason of the message output.</dd>
83
115
  </dl>
84
116
 
85
117
  <h3>Parameters</h3>
@@ -88,6 +120,10 @@
88
120
  <dd> Topic to send output values with.</dd>
89
121
  <dt>cycle <span class="property-type">number</span></dt>
90
122
  <dd> Cyclic time of output; 0 is only at state change.</dd>
123
+ <dt>Contextstore <span class="property-type">context store</span></dt>
124
+ <dd> context store for storing the values; <code>none</code> is no storage.</dd>
125
+ <dt>Status <span class="property-type">boolean</span></dt>
126
+ <dd> shows the actual value as a node status.</dd>
91
127
  </dl>
92
128
 
93
129
  </script>
package/hourmeter.js CHANGED
@@ -4,37 +4,86 @@ module.exports = function(RED)
4
4
  function HourMeterNode(config)
5
5
  {
6
6
  RED.nodes.createNode(this,config);
7
- this.config = config;
8
- this.topic = config.topic;
9
- this.cycle = config.cycle;
10
- this.interval_id = null;
7
+ this.config = config;
8
+ this.topic = config.topic ?? "";
9
+ this.cycle = Number( config.cycle ?? 0 );
10
+ this.contextStore = config.contextStore ?? "none";
11
+ this.showState = Boolean( config.showState );
12
+ this.interval_id = null;
11
13
  var node = this;
12
14
  var context = this.context();
15
+ node.status( "" );
13
16
 
14
17
  if( this.cycle > 0 )
15
18
  {
16
- this.interval_id = setInterval( function()
19
+ function emitQuery()
17
20
  {
18
- node.emit( "input", {querry:true} );
19
- }, this.cycle*60*1000 );
21
+ node.emit( "input", {query:true} );
22
+ }
23
+ setTimeout( emitQuery, 75 );
24
+ this.interval_id = setInterval( emitQuery, this.cycle*60*1000 );
20
25
  }
21
26
 
22
27
  node.on( 'input', function(msg,send,done)
23
28
  {
29
+ let data;
30
+ const now = Date.now();
31
+
32
+ function sendOutput(reason)
33
+ {
34
+ const out = data.counter/3600;
35
+ if( node.showState )
36
+ {
37
+ node.status( `${data.state} / ${out.toFixed(1)}` );
38
+ }
39
+ send( [
40
+ { topic:node.topic, payload:data.switchOn!==undefined, reason:reason },
41
+ { topic:node.topic, payload:out, reason:reason }
42
+ ] );
43
+ }
44
+
24
45
  if( msg.reset )
25
46
  {
26
- context.set( "data", undefined, "storeInFile" );
27
- node.status( "-" );
28
- send( [ { topic:this.topic, payload:false }, { topic:this.topic, payload:0 } ] );
47
+ data = { counter:0 }
48
+ context.set( "data", data, node.contextStore );
49
+ sendOutput( "reset" );
29
50
  }
30
51
  else
31
52
  {
32
- const now = Date.now();
33
- let data = context.get( "data", "storeInFile" ) ?? { counter:0 };
34
- if( ( !msg.querry ) && ( msg.payload!==undefined ) )
53
+ data = context.get( "data", node.contextStore ) ?? { counter:0 };
54
+ if( msg.set )
55
+ {
56
+ const value = Number( msg.payload );
57
+ if( value >= 0 )
58
+ {
59
+ data.counter = msg.payload * 3600;
60
+ if( data.switchOn !== undefined )
61
+ {
62
+ data.switchOn = now;
63
+ }
64
+ context.set( "data", data, node.contextStore );
65
+ sendOutput( "set" );
66
+ }
67
+ else
68
+ {
69
+ done( `invalid set value: ${msg.payload}` )
70
+ return;
71
+ }
72
+ }
73
+ else
35
74
  {
36
- if( msg.payload !== data.state )
75
+ if( msg.query )
37
76
  {
77
+ if( data.switchOn !== undefined )
78
+ {
79
+ data.counter += (now - data.switchOn)/1000;
80
+ data.switchOn = now;
81
+ }
82
+ sendOutput( "query" );
83
+ }
84
+ else
85
+ {
86
+ data.state = msg.payload;
38
87
  switch( msg.payload )
39
88
  {
40
89
  case true:
@@ -46,6 +95,7 @@ module.exports = function(RED)
46
95
  if( data.switchOn === undefined )
47
96
  {
48
97
  data.switchOn = now;
98
+ sendOutput( "on" );
49
99
  }
50
100
  break;
51
101
  case false:
@@ -59,36 +109,23 @@ module.exports = function(RED)
59
109
  {
60
110
  data.counter += (now - data.switchOn)/1000;
61
111
  delete data.switchOn;
112
+ sendOutput( "off" );
62
113
  }
63
114
  break;
115
+ default:
116
+ node.warn( `invalid payload: ${msg.payload}` );
64
117
  }
65
- data.state = msg.payload;
118
+ context.set( "data", data, node.contextStore );
66
119
  }
67
- context.set( "data", data, "storeInFile" );
68
120
  }
69
- else
70
- {
71
- if( data.switchOn !== undefined )
72
- {
73
- data.counter += (now - data.switchOn)/1000;
74
- data.switchOn = now;
75
- }
76
- }
77
- const out = data.counter/3600;
78
- node.status( `${data.state} / ${out.toFixed(1)}` );
79
- send( [ { topic:this.topic, payload:data.switchOn!==undefined }, { topic:this.topic, payload:out } ] );
80
121
  }
81
122
  done();
82
123
  } );
124
+
125
+ node.on('close', function() {
126
+ clearInterval( node.interval_id );
127
+ });
83
128
  }
84
129
 
85
130
  RED.nodes.registerType("hourmeter",HourMeterNode);
86
-
87
- HourMeterNode.prototype.close = function()
88
- {
89
- if( this.interval_id != null )
90
- {
91
- clearInterval( this.interval_id );
92
- }
93
- }
94
131
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mschaeffler/node-red-hourmeter",
3
- "version": "0.2.5",
3
+ "version": "1.0.0",
4
4
  "description": "A working hour meter for NodeRed.",
5
5
  "license": "LGPL-2.1",
6
6
  "homepage": "https://github.com/m-schaeffler/node-red-my-nodes/tree/main/node-red-hourmeter",
@@ -9,13 +9,13 @@
9
9
  "url": "git+https://github.com/m-schaeffler/node-red-my-nodes.git"
10
10
  },
11
11
  "engines": {
12
- "node": ">=14"
12
+ "node": ">=18"
13
13
  },
14
14
  "keywords": [
15
15
  "node-red"
16
16
  ],
17
17
  "node-red": {
18
- "version": ">=1.3.7",
18
+ "version": ">=3.1.0",
19
19
  "nodes": {
20
20
  "nop": "hourmeter.js"
21
21
  }
@@ -23,5 +23,8 @@
23
23
  "author": {
24
24
  "name": "Mathias Schäffler",
25
25
  "url": "https://github.com/m-schaeffler"
26
+ },
27
+ "scripts": {
28
+ "test": "mocha \"test/*_spec.js\""
26
29
  }
27
30
  }