@signalk/vedirect-serial-usb 1.5.0 → 1.7.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.
Files changed (63) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +36 -24
  3. package/dist/Parser.d.ts +50 -0
  4. package/dist/Parser.d.ts.map +1 -0
  5. package/dist/Parser.js +350 -0
  6. package/dist/Parser.js.map +1 -0
  7. package/dist/constants.d.ts +5 -0
  8. package/dist/constants.d.ts.map +1 -0
  9. package/dist/constants.js +14 -0
  10. package/dist/constants.js.map +1 -0
  11. package/dist/fields.d.ts +14 -0
  12. package/dist/fields.d.ts.map +1 -0
  13. package/dist/fields.js +439 -0
  14. package/dist/fields.js.map +1 -0
  15. package/dist/index.d.ts +4 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +305 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/productIds.d.ts +9 -0
  20. package/dist/productIds.d.ts.map +1 -0
  21. package/dist/productIds.js +95 -0
  22. package/dist/productIds.js.map +1 -0
  23. package/dist/serial.d.ts +14 -0
  24. package/dist/serial.d.ts.map +1 -0
  25. package/dist/serial.js +106 -0
  26. package/dist/serial.js.map +1 -0
  27. package/dist/standalone.d.ts +22 -0
  28. package/dist/standalone.d.ts.map +1 -0
  29. package/dist/standalone.js +73 -0
  30. package/dist/standalone.js.map +1 -0
  31. package/dist/tcp.d.ts +5 -0
  32. package/dist/tcp.d.ts.map +1 -0
  33. package/dist/tcp.js +106 -0
  34. package/dist/tcp.js.map +1 -0
  35. package/dist/types.d.ts +181 -0
  36. package/dist/types.d.ts.map +1 -0
  37. package/dist/types.js +15 -0
  38. package/dist/types.js.map +1 -0
  39. package/dist/udp.d.ts +5 -0
  40. package/dist/udp.d.ts.map +1 -0
  41. package/dist/udp.js +67 -0
  42. package/dist/udp.js.map +1 -0
  43. package/docs/screenshots/1_config.png +0 -0
  44. package/logo.svg +46 -0
  45. package/package.json +79 -19
  46. package/.github/FUNDING.yml +0 -1
  47. package/.travis.yml +0 -13
  48. package/index.js +0 -159
  49. package/lib/Parser.js +0 -396
  50. package/lib/fields.js +0 -503
  51. package/lib/filestream.js +0 -38
  52. package/lib/productIds.json +0 -84
  53. package/lib/serial.js +0 -48
  54. package/lib/tcp.js +0 -53
  55. package/lib/udp.js +0 -26
  56. package/log/one.txt +0 -18
  57. package/log/run-log.js +0 -7
  58. package/log/sampleOutput.txt +0 -297
  59. package/migration.js +0 -57
  60. package/run.js +0 -10
  61. package/standalone.js +0 -68
  62. package/test/index.js +0 -52
  63. package/todo.md +0 -14
package/.travis.yml DELETED
@@ -1,13 +0,0 @@
1
- language: node_js
2
- node_js:
3
- - "lts/*"
4
- jobs:
5
- include:
6
- - stage: test
7
- script:
8
- - npm test
9
- branches:
10
- only:
11
- - master
12
- - development
13
- - /^greenkeeper/.*$/
package/index.js DELETED
@@ -1,159 +0,0 @@
1
- const serial = require('./lib/serial')
2
- const udp = require('./lib/udp')
3
- const tcp = require('./lib/tcp')
4
- const Parser = require('./lib/Parser')
5
- const VEDirect = require('./standalone')
6
-
7
- module.exports = function (app) {
8
- let parser = []
9
- let shaddow = null
10
- const plugin = {}
11
- plugin.id = 'vedirect-signalk'
12
- plugin.name = 'VE.Direct to Signal K'
13
- plugin.description = plugin.name
14
-
15
- plugin.start = function (options) {
16
- shaddow = options;
17
-
18
- if (typeof options.vedirect !== 'undefined') {
19
- Object.keys(options.vedirect).forEach(items => {
20
- parser[items] = new Parser(options)
21
-
22
- parser[items].on('delta', delta => {
23
- app.handleMessage('pluginId', delta)
24
- })
25
-
26
- let type = options.vedirect[items].device;
27
- let connection = options.vedirect[items].connection;
28
- let port = options.vedirect[items].port;
29
- if (type == 'Serial') {
30
- serial.open(connection, parser, app.debug, items)
31
- } else if (type == 'UDP') {
32
- udp.listen(port, parser, app.debug, items)
33
- } else if (type == 'TCP') {
34
- tcp.connect(connection, port, parser, app.debug, items)
35
- }
36
- });
37
- } else {
38
- if (options.device) {
39
- oldConfig = {vedirect: [{device: 'Serial', connection: options.device, ignoreChecksum: options.ignoreChecksum,
40
- mainBatt: options.mainBatt, auxBatt: options.auxBatt, bmv: options.bmv, solar: options.solar}]}
41
- } else if (options.udpPort) {
42
- oldConfig = {vedirect: [{device: 'UDP', port: options.udpPort, ignoreChecksum: options.ignoreChecksum,
43
- mainBatt: options.mainBatt, auxBatt: options.auxBatt, bmv: options.bmv, solar: options.solar}]}
44
- } else if (options.host) {
45
- oldConfig = {vedirect: [{device: 'TCP', connection: options.host, port: options.tcpPort, ignoreChecksum: options.ignoreChecksum,
46
- mainBatt: options.mainBatt, auxBatt: options.auxBatt, bmv: options.bmv, solar: options.solar}]}
47
- }
48
- parser[0] = new Parser(oldConfig)
49
-
50
- parser[0].on('delta', delta => {
51
- app.handleMessage('pluginId', delta)
52
- })
53
-
54
- if (options.device) {
55
- serial.open(options.device, parser, app.debug, 0)
56
- } else if (options.udpPort) {
57
- udp.listen(options.udpPort, parser, app.debug, 0)
58
- } else if (options.host) {
59
- tcp.connect(options.host, options.tcpPort, parser, app.debug, 0)
60
- }
61
- }
62
- }
63
-
64
- plugin.stop = function () {
65
- if (shaddow) {
66
- if (typeof shaddow.vedirect !== 'undefined') {
67
- Object.keys(shaddow.vedirect).forEach(items => {
68
- parser[items].removeAllListeners()
69
- parser[items] = null
70
- let type = shaddow.vedirect[items].device;
71
- if (type == 'Serial') {
72
- serial.close(app.debug, items)
73
- } else if (type == 'UDP') {
74
- udp.close(app.debug, items)
75
- } else {
76
- tcp.close(app.debug, items)
77
- }
78
- });
79
- } else {
80
- parser[0].removeAllListeners()
81
- parser[0] = null
82
- if (shaddow.device) {
83
- serial.close(app.debug, 0)
84
- } else if (shaddow.udpPort) {
85
- udp.close(app.debug, 0)
86
- } else if (shaddow.host) {
87
- tcp.close(app.debug, 0)
88
- }
89
- }
90
- shaddow = null
91
- }
92
- }
93
-
94
- plugin.schema = {
95
- type: 'object',
96
- properties: {
97
- vedirect: {
98
- type: 'array',
99
- title: 'Connections',
100
- description: 'Connections to VE.Direct devices',
101
- items: {
102
- type: 'object',
103
- required: [],
104
- properties: {
105
- device: {
106
- type: 'string',
107
- default: 'Serial',
108
- title: 'Select device',
109
- enum: [
110
- 'Serial',
111
- 'UDP',
112
- 'TCP',
113
- ],
114
- },
115
- connection: {
116
- type: 'string',
117
- title: 'Connection details',
118
- description: 'Serial: e.g. /dev/ttyUSB0, UDP: ignored or TCP: IP address',
119
- default: '/dev/ttyUSB0'
120
- },
121
- port: {
122
- type: 'number',
123
- title: 'Port',
124
- description: 'Serial: ignored, UDP/TCP: port',
125
- default: 7878
126
- },
127
- ignoreChecksum: {
128
- type: 'boolean',
129
- title: 'Ignore Checksum',
130
- default: true
131
- },
132
- mainBatt: {
133
- type: 'string',
134
- title: 'Main Battery name in SK path',
135
- default: 'House'
136
- },
137
- auxBatt: {
138
- type: 'string',
139
- title: 'Aux Battery name in SK path',
140
- default: 'Starter'
141
- },
142
- bmv: {
143
- type: 'string',
144
- title: 'BMV name in SK path',
145
- default: 'bmv'
146
- },
147
- solar: {
148
- type: 'string',
149
- title: 'Solar name in SK path',
150
- default: 'Main'
151
- }
152
- },
153
- },
154
- },
155
- },
156
- }
157
-
158
- return plugin
159
- }
package/lib/Parser.js DELETED
@@ -1,396 +0,0 @@
1
- const debug = require('debug')
2
- const EventEmitter = require('events')
3
-
4
- const defaults = {
5
- productIds: require('./productIds.json'),
6
- fields: require('./fields'),
7
- defaultUnitId: 'victronDevice',
8
- mainBatt: 'house',
9
- auxBatt: 'starter',
10
- bmv: 'bmv',
11
- solar: 'solar'
12
- }
13
-
14
- class VEDirectParser extends EventEmitter {
15
- constructor (opts) {
16
- super()
17
- this.options = Object.assign({}, defaults, opts || {})
18
- this.fields = this.options.fields
19
- this.debug = debug('signalk-vedirect-parser')
20
- this.line = []
21
- this.data = {}
22
- this.cache = ''
23
- this.sum = 0
24
- }
25
-
26
- addChunk (buf, items) {
27
- if (!Buffer.isBuffer(buf)) {
28
- return this.warn('addChunk: incoming data is not a buffer: ' + typeof buf)
29
- }
30
-
31
- const chunk = buf.toString('ascii')
32
-
33
- buf.forEach(b => {
34
- this.sum += b
35
- })
36
-
37
- this.cache += chunk
38
- if (chunk.toLowerCase().includes('checksum')) {
39
- // Last line of block. Verify checksum of block in cache and parse line-by-line if checksum is correct.
40
- this._verifyCacheAndParse(items)
41
- }
42
- }
43
-
44
- parse (line) {
45
- if (typeof line !== 'string') {
46
- return
47
- }
48
- this.line = line.trim().split('\t')
49
- this._parse()
50
- }
51
-
52
- set (key, value) {
53
- this.data[key] = value
54
- this.emit('change', Object.assign({}, this.data))
55
- this.emit('set', { key, value })
56
- }
57
-
58
- get (key) {
59
- return this.data[key]
60
- }
61
-
62
- unset (key) {
63
- if (!this.data.hasOwnProperty(key)) {
64
- return
65
- }
66
-
67
- this.debug(`Unsetting ${key}`)
68
- this.emit('change', Object.assign({}, this.data))
69
- this.emit('unset', key)
70
-
71
- this.data[key] = null
72
- delete this.data[key]
73
- }
74
-
75
- getData () {
76
- return Object.assign({}, this.data)
77
- }
78
-
79
- _verifyCacheAndParse (items) {
80
- // Verify checksum if ignoreChecksum isn't true.
81
- if (this.options.vedirect[items].ignoreChecksum !== true && this.sum % 256 !== 0) {
82
- this.warn(`block checksum doesn't equal 0: ${this.sum % 256}`)
83
- this.cache = ''
84
- this.sum = 0
85
- return
86
- }
87
-
88
- this.cache
89
- .trim()
90
- .split('\n')
91
- .map(line => line.trim())
92
- .filter(line => line !== '')
93
- .forEach(line => {
94
- this.line = line.split('\t')
95
- this._parse()
96
- })
97
-
98
- this.cache = ''
99
- this.sum = 0
100
- this.generateDelta(items)
101
- }
102
-
103
- _parse () {
104
- let field = null
105
- let data = null
106
- let converted = null
107
-
108
- if (!Array.isArray(this.line) || this.line.length !== 2) {
109
- return this.warn('_parse() called on invalid line: ' + JSON.stringify({ line: this.line }))
110
- }
111
-
112
- field = String(this.line[0]).toUpperCase()
113
-
114
- if (!Object.keys(this.fields).includes(field) || typeof this.fields[field] !== 'object' || this.fields[field] === null) {
115
- return this.warn(`No field definition for: ${field}, ignoring`)
116
- }
117
-
118
- field = this.fields[field]
119
- data = this.line[1]
120
-
121
- if (data === undefined || data === null) {
122
- return this.warn(`Data is NULL, ignoring`)
123
- }
124
-
125
- if (typeof field.value === 'function') {
126
- converted = field.value(data, this)
127
- } else {
128
- converted = data
129
- }
130
-
131
- if (typeof converted !== 'undefined' && converted !== null) {
132
- this.set(field.name, Object.assign({}, field, {
133
- value: converted
134
- }))
135
- }
136
- }
137
-
138
- getAlarmReason (alarmReason) {
139
- alarmReason = parseInt(alarmReason, 10)
140
-
141
- switch (alarmReason) {
142
- case 1:
143
- return 'Low voltage'
144
-
145
- case 2:
146
- return 'High voltage'
147
-
148
- case 4:
149
- return 'Low state-of-charge'
150
-
151
- case 8:
152
- return 'Low starter voltage'
153
-
154
- case 16:
155
- return 'High starter voltage'
156
-
157
- case 32:
158
- return 'Low temperature'
159
-
160
- case 64:
161
- return 'High temperature'
162
-
163
- case 128:
164
- return 'Mid voltage'
165
-
166
- case 256:
167
- return 'Overload'
168
-
169
- case 512:
170
- return 'DC ripple'
171
-
172
- case 1024:
173
- return 'Low V AC out'
174
-
175
- case 2048:
176
- return 'High V AC out'
177
-
178
- default:
179
- return null
180
- }
181
- }
182
-
183
- getErrorString (err) {
184
- err = parseInt(err, 10)
185
-
186
- switch (err) {
187
- case 2:
188
- return 'Battery voltage too high'
189
-
190
- case 17:
191
- return 'Charger temperature too high'
192
-
193
- case 18:
194
- return 'Charger overcurrent'
195
-
196
- // Can be ignored according to victron protocol
197
- // case 19:
198
- // return 'Charger current reversed'
199
-
200
- case 20:
201
- return 'Bulk time limit exceeded'
202
-
203
- // Can be ignored according to victron protocol
204
- // case 21:
205
- // return 'Current sensor issue (sensor bias/sensor broken)'
206
-
207
- case 26:
208
- return 'Terminals overheated'
209
-
210
- case 33:
211
- return 'Input voltage too high (solar panel)'
212
-
213
- case 34:
214
- return 'Input current too high (solar panel)'
215
-
216
- case 38:
217
- return 'Input shutdown (due to excessive battery voltage)'
218
-
219
- case 116:
220
- return 'Factory calibration data lost'
221
-
222
- case 117:
223
- return 'Invalid/incompatible firmware'
224
-
225
- case 119:
226
- return 'User settings invalid'
227
-
228
- default:
229
- return null
230
- }
231
- }
232
-
233
- getMode (mode) {
234
- mode = parseInt(mode, 10)
235
-
236
- switch (mode) {
237
- case 2:
238
- return 'on'
239
-
240
- case 4:
241
- return 'off'
242
-
243
- case 5:
244
- return 'eco'
245
-
246
- default:
247
- return null
248
- }
249
- }
250
-
251
- getStateOfOperation (cs) {
252
- cs = parseInt(cs, 10)
253
-
254
- switch (cs) {
255
- case 0:
256
- return 'off'
257
-
258
- case 1:
259
- return 'low power'
260
-
261
- case 2:
262
- return 'fault'
263
-
264
- case 3:
265
- return 'bulk'
266
-
267
- case 4:
268
- return 'absorption'
269
-
270
- case 5:
271
- return 'float'
272
-
273
- case 9:
274
- return 'inverting'
275
-
276
- default:
277
- return null
278
- }
279
- }
280
-
281
- getTrackerOperationMode (mppt) {
282
- mppt = parseInt(mppt, 10)
283
-
284
- switch (mppt) {
285
- case 0:
286
- return 'off'
287
-
288
- case 1:
289
- return 'voltage or current limited'
290
-
291
- case 2:
292
- return 'mpp tracker active'
293
-
294
- default:
295
- return null
296
- }
297
- }
298
-
299
- getProductLongname (pid) {
300
- pid = String(pid)
301
-
302
- if (!pid.includes('0x')) {
303
- pid = `0x${pid}`
304
- }
305
-
306
- if (!Object.keys(this.options.productIds).includes(pid)) {
307
- return 'Unknown'
308
- }
309
-
310
- return this.options.productIds[pid]
311
- }
312
-
313
- generateDelta (items) {
314
- const keys = Object.keys(this.data)
315
-
316
- const values = keys.map(name => {
317
- const path = this.getPath(name, items)
318
- if (path === null) {
319
- return null
320
- }
321
- return {
322
- path,
323
- value: this.data[name].value
324
- }
325
- }).filter(update => update !== null)
326
-
327
- if (values.length === 0) {
328
- return this.warn('No mutations in this delta')
329
- }
330
-
331
- this.emit('delta', {
332
- context: 'vessels.self',
333
- updates: [
334
- {
335
- source: {
336
- label: '@signalk/vedirect-serial-usb',
337
- type: 'VE.direct'
338
- },
339
- timestamp: new Date().toISOString(),
340
- values
341
- }
342
- ]
343
- })
344
- }
345
-
346
- getPath (name, items) {
347
- return Object.keys(this.fields).reduce((found, key) => {
348
- const field = this.fields[key]
349
- if (field.name === name && field.hasOwnProperty('path')) {
350
- let unitID = ''
351
- found = field.path
352
-
353
- if (field.hasOwnProperty('unitId')) {
354
- if (field.unitId === 'mainBatt' && this.options.hasOwnProperty('mainBatt')) {
355
- unitID = this.options.vedirect[items].mainBatt
356
- }
357
-
358
- if (field.unitId === 'auxBatt' && this.options.hasOwnProperty('auxBatt')) {
359
- unitID = this.options.vedirect[items].auxBatt
360
- }
361
-
362
- if (field.unitId === 'bmv' && this.options.hasOwnProperty('bmv')) {
363
- unitID = this.options.vedirect[items].bmv
364
- }
365
-
366
- if (field.unitId === 'solar' && this.options.hasOwnProperty('solar')) {
367
- unitID = this.options.vedirect[items].solar
368
- }
369
-
370
- if ((!unitID || unitID === '') && this.options.hasOwnProperty('defaultUnitId') && typeof this.options.defaultUnitId === 'string') {
371
- unitID = this.options.defaultUnitId
372
- }
373
-
374
- if (this.options.hasOwnProperty('overrideUnitId') && typeof this.options.overrideUnitId === 'string') {
375
- unitID = this.options.overrideUnitId
376
- }
377
-
378
- found = found.replace('*', unitID)
379
- }
380
- }
381
- return found
382
- }, null)
383
- }
384
-
385
- warn (message) {
386
- this.debug(`Warning: ${message}`)
387
- this.emit('warn', message)
388
- }
389
-
390
- error (err) {
391
- this.debug(`Error: ${err.message}`)
392
- this.emit('error', err)
393
- }
394
- }
395
-
396
- module.exports = VEDirectParser