@ncd-io/node-red-enterprise-sensors 1.6.4 → 2.0.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.
@@ -0,0 +1,247 @@
1
+ const { toMac, signInt, msbLsb } = require('../utils');
2
+
3
+ // --- 1. DEFINE LOCAL FUNCTIONS ---
4
+ // These are defined as local variables so they can call each other easily.
5
+ module.exports = (globalDevices) => {
6
+
7
+ const get_write_buffer_size = (firmware) => {
8
+ return 14;
9
+ };
10
+
11
+ const get_config_map = (firmware) => {
12
+ console.log('Generating sync map for firmware version', firmware);
13
+
14
+ return {
15
+ "core_version": {
16
+ "read_index": 3,
17
+ "descriptions": {
18
+ "title": "Core Version",
19
+ "main_caption": "The version of the core communication stack."
20
+ },
21
+ "validator": {
22
+ "type": "uint8"
23
+ },
24
+ "tags": [
25
+ "system"
26
+ ]
27
+ },
28
+ "firmware_version": {
29
+ "read_index": 4,
30
+ "descriptions": {
31
+ "title": "Firmware Version",
32
+ "main_caption": "The application-specific firmware version."
33
+ },
34
+ "validator": {
35
+ "type": "uint8"
36
+ },
37
+ "tags": [
38
+ "system"
39
+ ]
40
+ },
41
+ "sensor_type": {
42
+ "read_index": 5,
43
+ "descriptions": {
44
+ "title": "Sensor Type",
45
+ "main_caption": "The hardware identifier for the specific sensor model."
46
+ },
47
+ "validator": {
48
+ "type": "uint16be"
49
+ },
50
+ "tags": [
51
+ "system"
52
+ ]
53
+ },
54
+ "tx_lifetime_counter": {
55
+ "read_index": 7,
56
+ "descriptions": {
57
+ "title": "Sampling Interval",
58
+ "main_caption": "Set how often will the sensor transmit measurement data. Note: For this sensor, this value functions as the sampling interval rather than a traditional delay.",
59
+ "sub_caption": "Default value: 20 milliseconds."
60
+ },
61
+ "validator": {
62
+ "type": "uint32be"
63
+ },
64
+ "tags": [
65
+ "diagnostics"
66
+ ]
67
+ },
68
+ "hardware_id": {
69
+ "read_index": 11,
70
+ "length": 3,
71
+ "descriptions": {
72
+ "title": "Hardware ID",
73
+ "main_caption": "A unique 3-byte hardware identifier."
74
+ },
75
+ "validator": {
76
+ "type": "buffer"
77
+ },
78
+ "tags": [
79
+ "system"
80
+ ]
81
+ },
82
+ "network_id": {
83
+ "read_index": 14,
84
+ "write_index": 3,
85
+ "length": 2,
86
+ "descriptions": {
87
+ "title": "Network ID",
88
+ "main_caption": ""
89
+ },
90
+ "default_value": "7fff",
91
+ "validator": {
92
+ "type": "hex",
93
+ "length": 4
94
+ },
95
+ "html_id": "pan_id",
96
+ "tags": [
97
+ "communications"
98
+ ]
99
+ },
100
+ "destination_address": {
101
+ "read_index": 16,
102
+ "write_index": 5,
103
+ "length": 4,
104
+ "descriptions": {
105
+ "title": "Destination Address",
106
+ "main_caption": ""
107
+ },
108
+ "default_value": "0000ffff",
109
+ "validator": {
110
+ "type": "mac",
111
+ "length": 8
112
+ },
113
+ "html_id": "destination",
114
+ "tags": [
115
+ "communications"
116
+ ]
117
+ },
118
+ "node_id": {
119
+ "read_index": 20,
120
+ "write_index": 9,
121
+ "descriptions": {
122
+ "title": "Node ID",
123
+ "main_caption": ""
124
+ },
125
+ "default_value": "0",
126
+ "validator": {
127
+ "type": "uint8",
128
+ "min": 0,
129
+ "max": 255,
130
+ "generated": true
131
+ },
132
+ "html_id": "node_id",
133
+ "tags": [
134
+ "generic"
135
+ ]
136
+ },
137
+ "report_rate": {
138
+ "read_index": 21,
139
+ "write_index": 10,
140
+ "descriptions": {
141
+ "title": "Delay",
142
+ "main_caption": ""
143
+ },
144
+ "default_value": "600",
145
+ "validator": {
146
+ "type": "uint32be",
147
+ "min": 0,
148
+ "max": 65535,
149
+ "generated": true
150
+ },
151
+ "html_id": "delay",
152
+ "tags": [
153
+ "generic"
154
+ ]
155
+ }
156
+ };
157
+ };
158
+
159
+ const sync_parse = (rep_buffer) => {
160
+ let response = {
161
+ 'human_readable': {},
162
+ 'machine_values': {}
163
+ };
164
+
165
+ // Get the map based on the sensor type byte
166
+ const sync_map = get_config_map(rep_buffer[4]);
167
+
168
+ for (const [key, config] of Object.entries(sync_map)) {
169
+ // Destructure 'type' from inside 'validator' and rename 'read_index' to 'idx'
170
+ const { read_index: idx, length, validator: { type } = {}, converter, options } = config;
171
+
172
+ // If for some reason a config doesn't have a validator/type, skip it
173
+ if (!type) continue;
174
+
175
+ switch (type) {
176
+ case 'uint8':
177
+ response.machine_values[key] = rep_buffer[idx];
178
+ break;
179
+ case 'uint16be':
180
+ response.machine_values[key] = rep_buffer.readUInt16BE(idx);
181
+ break;
182
+ case 'uint32be':
183
+ response.machine_values[key] = rep_buffer.readUInt32BE(idx);
184
+ break;
185
+ case 'buffer':
186
+ response.machine_values[key] = rep_buffer.subarray(idx, idx + length);
187
+ break;
188
+ case 'hex':
189
+ response.machine_values[key] = rep_buffer.subarray(idx, idx + length).toString('hex');
190
+ break;
191
+ case 'mac':
192
+ response.machine_values[key] = rep_buffer.subarray(idx, idx + length).toString('hex');
193
+ break;
194
+ }
195
+ let human_value = response.machine_values[key];
196
+ if(options && options[response.machine_values[key]]){
197
+ human_value = options[response.machine_values[key]];
198
+ }else{
199
+ if(converter && converter.multiplier){
200
+ human_value = human_value * converter.multiplier;
201
+ }
202
+ if(converter && converter.units){
203
+ human_value = human_value + converter.units;
204
+ }
205
+ }
206
+ response.human_readable[key] = human_value;
207
+ }
208
+ if (Object.hasOwn(response.machine_values, 'destination_address') && response.machine_values.destination_address.toLowerCase() === '00000000') {
209
+ console.log('##############################');
210
+ console.log('#########Dest Override########');
211
+ console.log('##############################');
212
+ response.destination_address = "0000ffff";
213
+ response.auto_raw_destination_address = "0000ffff";
214
+ };
215
+ return response;
216
+ };
217
+
218
+ const parse = (payload, parsed, mac) => {
219
+ let status = [];
220
+ if(payload[8] & 1) status.push('fatal');
221
+ if(payload[8] & 2) status.push('offset_reg');
222
+ if(payload[8] & 4) status.push('algorithm');
223
+ if(payload[8] & 8) status.push('output');
224
+ if(payload[8] & 16) status.push('self_diagnostic');
225
+ if(payload[8] & 32) status.push('out_of_range');
226
+ if(payload[8] & 64) status.push('memory');
227
+ if(payload[8] == 0) status = 'valid_data'
228
+ let res = {
229
+ status: status,
230
+ co2_ppm: signInt(payload.slice(9, 13).reduce(msbLsb), 32)
231
+ };
232
+ if(payload[7] == 1) res.co2_ppm = 'invalid_data';
233
+ return res;
234
+ };
235
+
236
+ // --- 2. EXPORT THE MODULE ---
237
+ // Export the module with all the necessary functions and properties
238
+ // that need to be called from outside the scrip
239
+ return {
240
+ type: 554,
241
+ name: 'Custom 400-50,000 PPM CO2 Sensor',
242
+ parse,
243
+ get_write_buffer_size,
244
+ get_config_map,
245
+ sync_parse
246
+ };
247
+ };
@@ -0,0 +1,257 @@
1
+ const { toMac, signInt, msbLsb } = require('../utils');
2
+
3
+ // --- 1. DEFINE LOCAL FUNCTIONS ---
4
+ // These are defined as local variables so they can call each other easily.
5
+ module.exports = (globalDevices) => {
6
+
7
+ const get_write_buffer_size = (firmware) => {
8
+ return 16;
9
+ };
10
+
11
+ const get_config_map = (firmware) => {
12
+ console.log('Generating sync map for firmware version', firmware);
13
+
14
+ return {
15
+ "core_version": {
16
+ "read_index": 3,
17
+ "descriptions": {
18
+ "title": "Core Version",
19
+ "main_caption": "The version of the core communication stack."
20
+ },
21
+ "validator": {
22
+ "type": "uint8"
23
+ },
24
+ "tags": [
25
+ "system"
26
+ ]
27
+ },
28
+ "firmware_version": {
29
+ "read_index": 4,
30
+ "descriptions": {
31
+ "title": "Firmware Version",
32
+ "main_caption": "The application-specific firmware version."
33
+ },
34
+ "validator": {
35
+ "type": "uint8"
36
+ },
37
+ "tags": [
38
+ "system"
39
+ ]
40
+ },
41
+ "sensor_type": {
42
+ "read_index": 5,
43
+ "descriptions": {
44
+ "title": "Sensor Type",
45
+ "main_caption": "The hardware identifier for the specific sensor model."
46
+ },
47
+ "validator": {
48
+ "type": "uint16be"
49
+ },
50
+ "tags": [
51
+ "system"
52
+ ]
53
+ },
54
+ "tx_lifetime_counter": {
55
+ "read_index": 7,
56
+ "descriptions": {
57
+ "title": "Sampling Interval",
58
+ "main_caption": "Set how often will the sensor transmit measurement data. Note: For this sensor, this value functions as the sampling interval rather than a traditional delay.",
59
+ "sub_caption": "Default value: 20 milliseconds."
60
+ },
61
+ "validator": {
62
+ "type": "uint32be"
63
+ },
64
+ "tags": [
65
+ "diagnostics"
66
+ ]
67
+ },
68
+ "hardware_id": {
69
+ "read_index": 11,
70
+ "length": 3,
71
+ "descriptions": {
72
+ "title": "Hardware ID",
73
+ "main_caption": "A unique 3-byte hardware identifier."
74
+ },
75
+ "validator": {
76
+ "type": "buffer"
77
+ },
78
+ "tags": [
79
+ "system"
80
+ ]
81
+ },
82
+ "network_id": {
83
+ "read_index": 14,
84
+ "write_index": 3,
85
+ "length": 2,
86
+ "descriptions": {
87
+ "title": "Network ID",
88
+ "main_caption": ""
89
+ },
90
+ "default_value": "7fff",
91
+ "validator": {
92
+ "type": "hex",
93
+ "length": 4
94
+ },
95
+ "html_id": "pan_id",
96
+ "tags": [
97
+ "communications"
98
+ ]
99
+ },
100
+ "destination_address": {
101
+ "read_index": 16,
102
+ "write_index": 5,
103
+ "length": 4,
104
+ "descriptions": {
105
+ "title": "Destination Address",
106
+ "main_caption": ""
107
+ },
108
+ "default_value": "0000ffff",
109
+ "validator": {
110
+ "type": "mac",
111
+ "length": 8
112
+ },
113
+ "html_id": "destination",
114
+ "tags": [
115
+ "communications"
116
+ ]
117
+ },
118
+ "node_id": {
119
+ "read_index": 20,
120
+ "write_index": 9,
121
+ "descriptions": {
122
+ "title": "Node ID",
123
+ "main_caption": ""
124
+ },
125
+ "default_value": "0",
126
+ "validator": {
127
+ "type": "uint8",
128
+ "min": 0,
129
+ "max": 255,
130
+ "generated": true
131
+ },
132
+ "html_id": "node_id",
133
+ "tags": [
134
+ "generic"
135
+ ]
136
+ },
137
+ "report_rate": {
138
+ "read_index": 21,
139
+ "write_index": 10,
140
+ "descriptions": {
141
+ "title": "Delay",
142
+ "main_caption": ""
143
+ },
144
+ "default_value": 3,
145
+ "validator": {
146
+ "type": "uint32be"
147
+ },
148
+ "html_id": "delay"
149
+ },
150
+ "boot_time": {
151
+ "read_index": 25,
152
+ "write_index": 15,
153
+ "descriptions": {
154
+ "title": "Sensor Boot Time",
155
+ "main_caption": "This value represents the number of milliseconds to wait after applying power to the sensor before taking a reading."
156
+ },
157
+ "default_value": 10,
158
+ "validator": {
159
+ "type": "uint8"
160
+ },
161
+ "html_id": "sensor_boot_time_78"
162
+ }
163
+ };
164
+ };
165
+
166
+ const sync_parse = (rep_buffer) => {
167
+ let response = {
168
+ 'human_readable': {},
169
+ 'machine_values': {}
170
+ };
171
+
172
+ // Get the map based on the sensor type byte
173
+ const sync_map = get_config_map(rep_buffer[4]);
174
+
175
+ for (const [key, config] of Object.entries(sync_map)) {
176
+ // Destructure 'type' from inside 'validator' and rename 'read_index' to 'idx'
177
+ const { read_index: idx, length, validator: { type } = {}, converter, options } = config;
178
+
179
+ // If for some reason a config doesn't have a validator/type, skip it
180
+ if (!type) continue;
181
+
182
+ switch (type) {
183
+ case 'uint8':
184
+ response.machine_values[key] = rep_buffer[idx];
185
+ break;
186
+ case 'uint16be':
187
+ response.machine_values[key] = rep_buffer.readUInt16BE(idx);
188
+ break;
189
+ case 'uint32be':
190
+ response.machine_values[key] = rep_buffer.readUInt32BE(idx);
191
+ break;
192
+ case 'buffer':
193
+ response.machine_values[key] = rep_buffer.subarray(idx, idx + length);
194
+ break;
195
+ case 'hex':
196
+ response.machine_values[key] = rep_buffer.subarray(idx, idx + length).toString('hex');
197
+ break;
198
+ case 'mac':
199
+ response.machine_values[key] = rep_buffer.subarray(idx, idx + length).toString('hex');
200
+ break;
201
+ }
202
+ let human_value = response.machine_values[key];
203
+ if(options && options[response.machine_values[key]]){
204
+ human_value = options[response.machine_values[key]];
205
+ }else{
206
+ if(converter && converter.multiplier){
207
+ human_value = human_value * converter.multiplier;
208
+ }
209
+ if(converter && converter.units){
210
+ human_value = human_value + converter.units;
211
+ }
212
+ }
213
+ response.human_readable[key] = human_value;
214
+ }
215
+ if (Object.hasOwn(response.machine_values, 'destination_address') && response.machine_values.destination_address.toLowerCase() === '00000000') {
216
+ console.log('##############################');
217
+ console.log('#########Dest Override########');
218
+ console.log('##############################');
219
+ response.destination_address = "0000ffff";
220
+ response.auto_raw_destination_address = "0000ffff";
221
+ };
222
+ return response;
223
+ };
224
+
225
+ const parse = (payload, parsed, mac) => {
226
+ let status = parsed[7] >> 1; // status (1-bit left shifted)
227
+ let res = {
228
+ temperature: signInt(parsed.slice(8, 10).reduce(msbLsb), 16)/100,
229
+ dielectic_const: parsed.slice(10, 12).reduce(msbLsb)/100
230
+ };
231
+ if(status == 0){
232
+ res.error = {
233
+ temperature: "read_failed",
234
+ dielectic_const: "read_failed"
235
+ }
236
+ }
237
+ if(status == 1){
238
+ res.error = {
239
+ temperature: "addr_failed",
240
+ dielectic_const: "addr_failed"
241
+ }
242
+ }
243
+ return res;
244
+ };
245
+
246
+ // --- 2. EXPORT THE MODULE ---
247
+ // Export the module with all the necessary functions and properties
248
+ // that need to be called from outside the scrip
249
+ return {
250
+ type: 93,
251
+ name: 'Oil Temperature and Moisture Sensor',
252
+ parse,
253
+ get_write_buffer_size,
254
+ get_config_map,
255
+ sync_parse
256
+ };
257
+ };
package/lib/utils.js ADDED
@@ -0,0 +1,27 @@
1
+ // lib/utils.js
2
+
3
+ module.exports = {
4
+ /**
5
+ * Converts a buffer or array to a colon-separated MAC string
6
+ */
7
+ toMac: (data) => {
8
+ // Ensure we are working with a Buffer for consistent hex conversion
9
+ const buf = Buffer.isBuffer(data) ? data : Buffer.from(data);
10
+ return buf.toString('hex').match(/.{1,2}/g).join(':');
11
+ },
12
+
13
+ /**
14
+ * Common bit-shifting for legacy MSB/LSB math if needed
15
+ */
16
+ msbLsb: (msb, lsb) => (msb << 8) | lsb,
17
+
18
+ /**
19
+ * Formats battery voltage from raw value
20
+ */
21
+ formatBattery: (val) => (val / 100).toFixed(2) + 'V',
22
+
23
+ signInt(i, b){
24
+ if(i.toString(2).length != b) return i;
25
+ return -(((~i) & ((1 << (b-1))-1))+1);
26
+ }
27
+ };
package/package.json CHANGED
@@ -1,43 +1,46 @@
1
1
  {
2
- "name": "@ncd-io/node-red-enterprise-sensors",
3
- "version": "1.6.4",
4
- "description": "",
5
- "main": "index.js",
6
- "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
8
- },
9
- "repository": {
10
- "type": "git",
11
- "url": "https://github.com/ncd-io/node-red-enterprise-sensors"
12
- },
13
- "keywords": [
14
- "Wireless",
15
- "NCD",
16
- "Sensors",
17
- "Digi",
18
- "node-red",
19
- "enterprise"
20
- ],
21
- "author": "Jacob Youngblood",
22
- "contributors": [
23
- {
24
- "name": "Trey Felton"
25
- },
26
- {
27
- "name": "Anil Bhaskar"
28
- },
29
- {
30
- "name": "Travis Elliott"
31
- }
32
- ],
33
- "license": "MIT",
34
- "node-red": {
35
- "version": ">=2.0.0",
36
- "nodes": {
37
- "ncd-wireless": "wireless.js"
38
- }
39
- },
40
- "dependencies": {
41
- "ncd-red-comm": "2.0.3"
42
- }
2
+ "name": "@ncd-io/node-red-enterprise-sensors",
3
+ "version": "2.0.1",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/ncd-io/node-red-enterprise-sensors"
12
+ },
13
+ "keywords": [
14
+ "Wireless",
15
+ "NCD",
16
+ "Sensors",
17
+ "Digi",
18
+ "node-red",
19
+ "enterprise"
20
+ ],
21
+ "author": "Jacob Youngblood",
22
+ "contributors": [
23
+ {
24
+ "name": "Trey Felton"
25
+ },
26
+ {
27
+ "name": "Eduardo Martinez"
28
+ },
29
+ {
30
+ "name": "Anil Bhaskar"
31
+ },
32
+ {
33
+ "name": "Travis Elliott"
34
+ }
35
+ ],
36
+ "license": "MIT",
37
+ "node-red": {
38
+ "version": ">=2.0.0",
39
+ "nodes": {
40
+ "ncd-wireless": "wireless.js"
41
+ }
42
+ },
43
+ "dependencies": {
44
+ "ncd-red-comm": "2.0.3"
45
+ }
43
46
  }