@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/dist/index.js ADDED
@@ -0,0 +1,305 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ /**
36
+ * Signal K server plugin entry point.
37
+ *
38
+ * Reads VE.Direct data over Serial, UDP or TCP, parses it (src/Parser.ts) and
39
+ * forwards the resulting Signal K deltas to the host. Supports several
40
+ * connections at once (`options.vedirect[]`) and keeps an in-memory fallback
41
+ * for the legacy single-connection config format.
42
+ */
43
+ const serial = __importStar(require("./serial"));
44
+ const udp = __importStar(require("./udp"));
45
+ const tcp = __importStar(require("./tcp"));
46
+ const Parser_1 = require("./Parser");
47
+ const constants_1 = require("./constants");
48
+ // VE.Direct HEX "Set" frames toggling the BMV-7xx built-in relay (register
49
+ // 0x034E), exposing it as a writable Signal K switch for other plugins.
50
+ //
51
+ // : 8 4E03 00 VV CC \n
52
+ // | Set reg LE flags val checksum
53
+ //
54
+ // CC is chosen so (0x08 + every payload byte) & 0xFF == 0x55 (VE.Direct HEX
55
+ // checksum): on -> val 0x01 -> :84E030001FB, off -> val 0x00 -> :84E030000FC.
56
+ // The BMV only acts on these once its relay is set to remote control.
57
+ const RELAY_ON = ':84E030001FB\n';
58
+ const RELAY_OFF = ':84E030000FC\n';
59
+ // Maps a Signal K relay PUT value to its VE.Direct command. Accepts the numeric
60
+ // 1/0 the relay state is published as (see the RELAY field in src/fields.ts) and
61
+ // boolean true/false for convenience. Returns null for any out-of-range value so
62
+ // the handler can reject it.
63
+ function relayCommand(value) {
64
+ if (value === 1 || value === true) {
65
+ return RELAY_ON;
66
+ }
67
+ if (value === 0 || value === false) {
68
+ return RELAY_OFF;
69
+ }
70
+ return null;
71
+ }
72
+ const createPlugin = function (app) {
73
+ const parser = [];
74
+ // Unregister callbacks for the relay PUT handlers, indexed by connection like
75
+ // `parser`, so stop() can tear each one down.
76
+ const putUnregister = [];
77
+ // Relay paths already claimed by a registered handler. Two serial connections
78
+ // configured with the same BMV name resolve to the same path; the host keeps
79
+ // only one handler, so a PUT could drive the wrong port. The duplicate is
80
+ // refused instead. Cleared on stop().
81
+ const registeredRelayPaths = new Set();
82
+ let shaddow = null;
83
+ // Builds the PUT handler for one serial connection's BMV relay. It writes the
84
+ // matching VE.Direct HEX command and reports failure (rather than a false 200)
85
+ // when the value is out of range or the serial port is not open. A 200 means
86
+ // the frame was written to the port, not that the relay switched: the BMV sends
87
+ // no acknowledgement and acts only when its relay is in remote control, so the
88
+ // reply says "sent", not "confirmed". Each outcome is logged so a command that
89
+ // looks accepted but changes nothing is still diagnosable from the plugin log.
90
+ function relayPutHandler(connectionIndex) {
91
+ return (_context, path, value) => {
92
+ const command = relayCommand(value);
93
+ if (command === null) {
94
+ app.debug(`Relay PUT on ${path} rejected: ${JSON.stringify(value)} is not 0/1 or true/false`);
95
+ return {
96
+ state: 'COMPLETED',
97
+ statusCode: 400,
98
+ message: `Invalid relay value ${JSON.stringify(value)} for ${path}; expected 0/1 or true/false`
99
+ };
100
+ }
101
+ const desired = command === RELAY_ON ? 'on' : 'off';
102
+ if (!serial.write(command, connectionIndex)) {
103
+ app.debug(`Relay PUT on ${path} ignored: serial connection ${connectionIndex} is not open`);
104
+ return {
105
+ state: 'COMPLETED',
106
+ statusCode: 503,
107
+ message: `Serial connection ${connectionIndex} is not open; relay unchanged`
108
+ };
109
+ }
110
+ app.debug(`Relay PUT on ${path}: ${desired} command sent to serial connection ${connectionIndex} (the BMV applies it only when its relay is in remote control)`);
111
+ return {
112
+ state: 'COMPLETED',
113
+ statusCode: 200,
114
+ message: `Relay ${desired} command sent; takes effect only when the BMV relay is set to remote control`
115
+ };
116
+ };
117
+ }
118
+ // Exposes the BMV relay as a writable Signal K path for one serial connection,
119
+ // anchored on the configured BMV name (matching the RELAY read field's path,
120
+ // electrical.batteries.<bmv>). The relay belongs to the BMV-7xx, so it is
121
+ // registered only for battery monitors (the default when deviceType is unset),
122
+ // never for a solar charger, and only when a BMV name is configured to anchor
123
+ // the path. The skipped cases are logged rather than silent: the RELAY field
124
+ // still publishes the relay state, so a writable path that is quietly absent
125
+ // would be a mystery to whoever tries to PUT to it.
126
+ function registerRelayHandler(conn, connectionIndex) {
127
+ if (conn.deviceType === 'Solar charger') {
128
+ app.debug(`Serial connection ${connectionIndex} is a solar charger, not a BMV; relay control disabled`);
129
+ return;
130
+ }
131
+ if (!conn.bmv) {
132
+ app.debug(`Serial connection ${connectionIndex} has no BMV name; relay control disabled`);
133
+ return;
134
+ }
135
+ const path = `electrical.batteries.${conn.bmv}.relay`;
136
+ if (registeredRelayPaths.has(path)) {
137
+ app.debug(`Relay path ${path} is already registered by another connection; relay control disabled for serial connection ${connectionIndex}`);
138
+ return;
139
+ }
140
+ registeredRelayPaths.add(path);
141
+ putUnregister[connectionIndex] = app.registerPutHandler('vessels.self', path, relayPutHandler(connectionIndex));
142
+ }
143
+ function startConnection(conn, connectionIndex) {
144
+ const instance = new Parser_1.VEDirectParser(shaddow ?? undefined);
145
+ parser[connectionIndex] = instance;
146
+ instance.on('delta', (delta) => {
147
+ app.handleMessage(constants_1.PLUGIN_ID, delta);
148
+ });
149
+ if (conn.device === 'Serial') {
150
+ serial.open(conn.connection, parser, app.debug, connectionIndex);
151
+ registerRelayHandler(conn, connectionIndex);
152
+ }
153
+ else if (conn.device === 'UDP') {
154
+ udp.listen(conn.port, parser, app.debug, connectionIndex);
155
+ }
156
+ else if (conn.device === 'TCP') {
157
+ tcp.connect(conn.connection, conn.port, parser, app.debug, connectionIndex);
158
+ }
159
+ }
160
+ // Builds the current multi-connection config from a legacy flat config so
161
+ // that installations predating the `vedirect[]` rework keep working without
162
+ // a persisted migration.
163
+ function fromLegacy(options) {
164
+ const base = {
165
+ ignoreChecksum: options.ignoreChecksum ?? true,
166
+ mainBatt: options.mainBatt ?? 'House',
167
+ auxBatt: options.auxBatt ?? 'Starter',
168
+ bmv: options.bmv ?? 'bmv',
169
+ solar: options.solar ?? 'Main',
170
+ deviceType: options.deviceType
171
+ };
172
+ if (options.device) {
173
+ return { device: 'Serial', connection: options.device, port: 0, ...base };
174
+ }
175
+ if (options.udpPort) {
176
+ return {
177
+ device: 'UDP',
178
+ connection: 'localhost',
179
+ port: options.udpPort,
180
+ ...base
181
+ };
182
+ }
183
+ if (options.host) {
184
+ return {
185
+ device: 'TCP',
186
+ connection: options.host,
187
+ port: options.tcpPort ?? 0,
188
+ ...base
189
+ };
190
+ }
191
+ return null;
192
+ }
193
+ const plugin = {
194
+ id: constants_1.PLUGIN_ID,
195
+ name: 'VE.Direct to Signal K',
196
+ description: 'VE.Direct to Signal K',
197
+ start(options) {
198
+ shaddow = options;
199
+ if (options.vedirect !== undefined) {
200
+ options.vedirect.forEach((conn, connectionIndex) => {
201
+ startConnection(conn, connectionIndex);
202
+ });
203
+ return;
204
+ }
205
+ const legacy = fromLegacy(options);
206
+ if (legacy !== null) {
207
+ shaddow = { ...options, vedirect: [legacy] };
208
+ startConnection(legacy, 0);
209
+ }
210
+ },
211
+ stop() {
212
+ if (!shaddow) {
213
+ return;
214
+ }
215
+ if (shaddow.vedirect !== undefined) {
216
+ shaddow.vedirect.forEach((conn, connectionIndex) => {
217
+ parser[connectionIndex]?.removeAllListeners();
218
+ delete parser[connectionIndex];
219
+ putUnregister[connectionIndex]?.();
220
+ delete putUnregister[connectionIndex];
221
+ if (conn.device === 'Serial') {
222
+ serial.close(app.debug, connectionIndex);
223
+ }
224
+ else if (conn.device === 'UDP') {
225
+ udp.close(app.debug, connectionIndex);
226
+ }
227
+ else {
228
+ tcp.close(app.debug, connectionIndex);
229
+ }
230
+ });
231
+ registeredRelayPaths.clear();
232
+ }
233
+ shaddow = null;
234
+ },
235
+ schema: {
236
+ type: 'object',
237
+ properties: {
238
+ vedirect: {
239
+ type: 'array',
240
+ title: 'Connections',
241
+ description: 'Connections to VE.Direct devices',
242
+ items: {
243
+ type: 'object',
244
+ required: [],
245
+ properties: {
246
+ device: {
247
+ type: 'string',
248
+ default: 'Serial',
249
+ title: 'Select device',
250
+ enum: ['Serial', 'UDP', 'TCP']
251
+ },
252
+ connection: {
253
+ type: 'string',
254
+ title: 'Connection details',
255
+ description: 'Serial: e.g. /dev/ttyUSB0, UDP: ignored or TCP: IP address',
256
+ default: '/dev/ttyUSB0'
257
+ },
258
+ port: {
259
+ type: 'number',
260
+ title: 'Port',
261
+ description: 'Serial: ignored, UDP/TCP: port',
262
+ default: 7878
263
+ },
264
+ deviceType: {
265
+ type: 'string',
266
+ title: 'Device type',
267
+ description: 'Battery monitor keeps V/I under electrical.batteries; Solar charger (MPPT) routes V/I under electrical.solar to avoid clashing with a battery monitor on the same bank',
268
+ default: 'Battery monitor',
269
+ enum: ['Battery monitor', 'Solar charger']
270
+ },
271
+ ignoreChecksum: {
272
+ type: 'boolean',
273
+ title: 'Ignore Checksum',
274
+ default: true
275
+ },
276
+ mainBatt: {
277
+ type: 'string',
278
+ title: 'Main Battery name in SK path',
279
+ default: 'House'
280
+ },
281
+ auxBatt: {
282
+ type: 'string',
283
+ title: 'Aux Battery name in SK path',
284
+ default: 'Starter'
285
+ },
286
+ bmv: {
287
+ type: 'string',
288
+ title: 'BMV name in SK path',
289
+ default: 'bmv'
290
+ },
291
+ solar: {
292
+ type: 'string',
293
+ title: 'Solar name in SK path',
294
+ default: 'Main'
295
+ }
296
+ }
297
+ }
298
+ }
299
+ }
300
+ }
301
+ };
302
+ return plugin;
303
+ };
304
+ module.exports = createPlugin;
305
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Victron product ID (PID) to human-readable model name.
3
+ *
4
+ * Kept as a TypeScript module rather than a JSON asset so that `tsc` emits
5
+ * it into `dist/` like any other source file (tsc does not copy `.json`
6
+ * files to the output directory).
7
+ */
8
+ export declare const productIds: Record<string, string>;
9
+ //# sourceMappingURL=productIds.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"productIds.d.ts","sourceRoot":"","sources":["../src/productIds.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAmF7C,CAAA"}
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.productIds = void 0;
4
+ /**
5
+ * Victron product ID (PID) to human-readable model name.
6
+ *
7
+ * Kept as a TypeScript module rather than a JSON asset so that `tsc` emits
8
+ * it into `dist/` like any other source file (tsc does not copy `.json`
9
+ * files to the output directory).
10
+ */
11
+ exports.productIds = {
12
+ '0x203': 'BMV-700',
13
+ '0x204': 'BMV-702',
14
+ '0x205': 'BMV-700H',
15
+ '0x300': 'BlueSolar MPPT 70/15',
16
+ '0xA040': 'BlueSolar MPPT 75/50',
17
+ '0xA041': 'BlueSolar MPPT 150/35',
18
+ '0xA042': 'BlueSolar MPPT 75/15',
19
+ '0xA043': 'BlueSolar MPPT 100/15',
20
+ '0xA044': 'BlueSolar MPPT 100/30',
21
+ '0xA045': 'BlueSolar MPPT 100/50',
22
+ '0xA046': 'BlueSolar MPPT 150/70',
23
+ '0xA047': 'BlueSolar MPPT 150/100',
24
+ '0xA049': 'BlueSolar MPPT 100/50 rev2',
25
+ '0xA04A': 'BlueSolar MPPT 100/30 rev2',
26
+ '0xA04B': 'BlueSolar MPPT 150/35 rev2',
27
+ '0xA04C': 'BlueSolar MPPT 75/10',
28
+ '0xA04D': 'BlueSolar MPPT 150/45',
29
+ '0xA04E': 'BlueSolar MPPT 150/60',
30
+ '0xA04F': 'BlueSolar MPPT 150/85',
31
+ '0xA050': 'SmartSolar MPPT 250/100',
32
+ '0xA051': 'SmartSolar MPPT 150/100*',
33
+ '0xA052': 'SmartSolar MPPT 150/85*',
34
+ '0xA053': 'SmartSolar MPPT 75/15',
35
+ '0xA054': 'SmartSolar MPPT 75/10',
36
+ '0xA055': 'SmartSolar MPPT 100/15',
37
+ '0xA056': 'SmartSolar MPPT 100/30',
38
+ '0xA057': 'SmartSolar MPPT 100/50',
39
+ '0xA058': 'SmartSolar MPPT 150/35',
40
+ '0xA059': 'SmartSolar MPPT 150/100 rev2',
41
+ '0xA05A': 'SmartSolar MPPT 150/85 rev2',
42
+ '0xA05B': 'SmartSolar MPPT 250/70',
43
+ '0xA05C': 'SmartSolar MPPT 250/85',
44
+ '0xA05D': 'SmartSolar MPPT 250/60',
45
+ '0xA05E': 'SmartSolar MPPT 250/45',
46
+ '0xA05F': 'SmartSolar MPPT 100/20',
47
+ '0xA060': 'SmartSolar MPPT 100/20 48V',
48
+ '0xA061': 'SmartSolar MPPT 150/45',
49
+ '0xA062': 'SmartSolar MPPT 150/60',
50
+ '0xA063': 'SmartSolar MPPT 150/70',
51
+ '0xA064': 'SmartSolar MPPT 250/85 rev2',
52
+ '0xA065': 'SmartSolar MPPT 250/100 rev2',
53
+ '0xA201': 'Phoenix Inverter 12V 250VA 230V*',
54
+ '0xA202': 'Phoenix Inverter 24V 250VA 230V*',
55
+ '0xA204': 'Phoenix Inverter 48V 250VA 230V*',
56
+ '0xA211': 'Phoenix Inverter 12V 375VA 230V*',
57
+ '0xA212': 'Phoenix Inverter 24V 375VA 230V*',
58
+ '0xA214': 'Phoenix Inverter 48V 375VA 230V*',
59
+ '0xA221': 'Phoenix Inverter 12V 500VA 230V*',
60
+ '0xA222': 'Phoenix Inverter 24V 500VA 230V*',
61
+ '0xA224': 'Phoenix Inverter 48V 500VA 230V*',
62
+ '0xA231': 'Phoenix Inverter 12V 250VA 230V',
63
+ '0xA232': 'Phoenix Inverter 24V 250VA 230V',
64
+ '0xA234': 'Phoenix Inverter 48V 250VA 230V',
65
+ '0xA239': 'Phoenix Inverter 12V 250VA 120V',
66
+ '0xA23A': 'Phoenix Inverter 24V 250VA 120V',
67
+ '0xA23C': 'Phoenix Inverter 48V 250VA 120V',
68
+ '0xA241': 'Phoenix Inverter 12V 375VA 230V',
69
+ '0xA242': 'Phoenix Inverter 24V 375VA 230V',
70
+ '0xA244': 'Phoenix Inverter 48V 375VA 230V',
71
+ '0xA249': 'Phoenix Inverter 12V 375VA 120V',
72
+ '0xA24A': 'Phoenix Inverter 24V 375VA 120V',
73
+ '0xA24C': 'Phoenix Inverter 48V 375VA 120V',
74
+ '0xA251': 'Phoenix Inverter 12V 500VA 230V',
75
+ '0xA252': 'Phoenix Inverter 24V 500VA 230V',
76
+ '0xA254': 'Phoenix Inverter 48V 500VA 230V',
77
+ '0xA259': 'Phoenix Inverter 12V 500VA 120V',
78
+ '0xA25A': 'Phoenix Inverter 24V 500VA 120V',
79
+ '0xA25C': 'Phoenix Inverter 48V 500VA 120V',
80
+ '0xA261': 'Phoenix Inverter 12V 800VA 230V',
81
+ '0xA262': 'Phoenix Inverter 24V 800VA 230V',
82
+ '0xA264': 'Phoenix Inverter 48V 800VA 230V',
83
+ '0xA269': 'Phoenix Inverter 12V 800VA 120V',
84
+ '0xA26A': 'Phoenix Inverter 24V 800VA 120V',
85
+ '0xA26C': 'Phoenix Inverter 48V 800VA 120V',
86
+ '0xA271': 'Phoenix Inverter 12V 1200VA 230V',
87
+ '0xA272': 'Phoenix Inverter 24V 1200VA 230V',
88
+ '0xA274': 'Phoenix Inverter 48V 1200VA 230V',
89
+ '0xA279': 'Phoenix Inverter 12V 1200VA 120V',
90
+ '0xA27A': 'Phoenix Inverter 24V 1200VA 120V',
91
+ '0xA27C': 'Phoenix Inverter 48V 1200VA 120V',
92
+ '0xA345': 'Phoenix Smart Charger 12V 30A 3 rev2',
93
+ '0xA381': 'BMV-712 Smart'
94
+ };
95
+ //# sourceMappingURL=productIds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"productIds.js","sourceRoot":"","sources":["../src/productIds.ts"],"names":[],"mappings":";;;AAAA;;;;;;GAMG;AACU,QAAA,UAAU,GAA2B;IAChD,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,UAAU;IACnB,OAAO,EAAE,sBAAsB;IAC/B,QAAQ,EAAE,sBAAsB;IAChC,QAAQ,EAAE,uBAAuB;IACjC,QAAQ,EAAE,sBAAsB;IAChC,QAAQ,EAAE,uBAAuB;IACjC,QAAQ,EAAE,uBAAuB;IACjC,QAAQ,EAAE,uBAAuB;IACjC,QAAQ,EAAE,uBAAuB;IACjC,QAAQ,EAAE,wBAAwB;IAClC,QAAQ,EAAE,4BAA4B;IACtC,QAAQ,EAAE,4BAA4B;IACtC,QAAQ,EAAE,4BAA4B;IACtC,QAAQ,EAAE,sBAAsB;IAChC,QAAQ,EAAE,uBAAuB;IACjC,QAAQ,EAAE,uBAAuB;IACjC,QAAQ,EAAE,uBAAuB;IACjC,QAAQ,EAAE,yBAAyB;IACnC,QAAQ,EAAE,0BAA0B;IACpC,QAAQ,EAAE,yBAAyB;IACnC,QAAQ,EAAE,uBAAuB;IACjC,QAAQ,EAAE,uBAAuB;IACjC,QAAQ,EAAE,wBAAwB;IAClC,QAAQ,EAAE,wBAAwB;IAClC,QAAQ,EAAE,wBAAwB;IAClC,QAAQ,EAAE,wBAAwB;IAClC,QAAQ,EAAE,8BAA8B;IACxC,QAAQ,EAAE,6BAA6B;IACvC,QAAQ,EAAE,wBAAwB;IAClC,QAAQ,EAAE,wBAAwB;IAClC,QAAQ,EAAE,wBAAwB;IAClC,QAAQ,EAAE,wBAAwB;IAClC,QAAQ,EAAE,wBAAwB;IAClC,QAAQ,EAAE,4BAA4B;IACtC,QAAQ,EAAE,wBAAwB;IAClC,QAAQ,EAAE,wBAAwB;IAClC,QAAQ,EAAE,wBAAwB;IAClC,QAAQ,EAAE,6BAA6B;IACvC,QAAQ,EAAE,8BAA8B;IACxC,QAAQ,EAAE,kCAAkC;IAC5C,QAAQ,EAAE,kCAAkC;IAC5C,QAAQ,EAAE,kCAAkC;IAC5C,QAAQ,EAAE,kCAAkC;IAC5C,QAAQ,EAAE,kCAAkC;IAC5C,QAAQ,EAAE,kCAAkC;IAC5C,QAAQ,EAAE,kCAAkC;IAC5C,QAAQ,EAAE,kCAAkC;IAC5C,QAAQ,EAAE,kCAAkC;IAC5C,QAAQ,EAAE,iCAAiC;IAC3C,QAAQ,EAAE,iCAAiC;IAC3C,QAAQ,EAAE,iCAAiC;IAC3C,QAAQ,EAAE,iCAAiC;IAC3C,QAAQ,EAAE,iCAAiC;IAC3C,QAAQ,EAAE,iCAAiC;IAC3C,QAAQ,EAAE,iCAAiC;IAC3C,QAAQ,EAAE,iCAAiC;IAC3C,QAAQ,EAAE,iCAAiC;IAC3C,QAAQ,EAAE,iCAAiC;IAC3C,QAAQ,EAAE,iCAAiC;IAC3C,QAAQ,EAAE,iCAAiC;IAC3C,QAAQ,EAAE,iCAAiC;IAC3C,QAAQ,EAAE,iCAAiC;IAC3C,QAAQ,EAAE,iCAAiC;IAC3C,QAAQ,EAAE,iCAAiC;IAC3C,QAAQ,EAAE,iCAAiC;IAC3C,QAAQ,EAAE,iCAAiC;IAC3C,QAAQ,EAAE,iCAAiC;IAC3C,QAAQ,EAAE,iCAAiC;IAC3C,QAAQ,EAAE,iCAAiC;IAC3C,QAAQ,EAAE,iCAAiC;IAC3C,QAAQ,EAAE,iCAAiC;IAC3C,QAAQ,EAAE,iCAAiC;IAC3C,QAAQ,EAAE,kCAAkC;IAC5C,QAAQ,EAAE,kCAAkC;IAC5C,QAAQ,EAAE,kCAAkC;IAC5C,QAAQ,EAAE,kCAAkC;IAC5C,QAAQ,EAAE,kCAAkC;IAC5C,QAAQ,EAAE,kCAAkC;IAC5C,QAAQ,EAAE,sCAAsC;IAChD,QAAQ,EAAE,eAAe;CAC1B,CAAA"}
@@ -0,0 +1,14 @@
1
+ import type { VEDirectParser } from './Parser';
2
+ import type { DebugFn } from './types';
3
+ export declare function open(device: string, parser: VEDirectParser[], debug: DebugFn, connectionIndex: number): void;
4
+ export declare function close(debug: DebugFn, connectionIndex: number): void;
5
+ /**
6
+ * Writes a raw command to a connection's serial port, used for outbound
7
+ * VE.Direct HEX control frames (e.g. setting the BMV relay). Returns true only
8
+ * when the port is open and the write was issued; false when no open port
9
+ * exists for the index (including the gap while an unexpectedly-closed port
10
+ * awaits reconnect) so the caller can report failure instead of assuming the
11
+ * command reached the device.
12
+ */
13
+ export declare function write(message: string, connectionIndex: number): boolean;
14
+ //# sourceMappingURL=serial.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serial.d.ts","sourceRoot":"","sources":["../src/serial.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAC9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAQtC,wBAAgB,IAAI,CAClB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,cAAc,EAAE,EACxB,KAAK,EAAE,OAAO,EACd,eAAe,EAAE,MAAM,GACtB,IAAI,CA4CN;AAED,wBAAgB,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,GAAG,IAAI,CAGnE;AAED;;;;;;;GAOG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAkBvE"}
package/dist/serial.js ADDED
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.open = open;
4
+ exports.close = close;
5
+ exports.write = write;
6
+ /**
7
+ * Serial (VE.Direct to USB) transport.
8
+ *
9
+ * Opens a serial port per connection index, splits the stream on `\r`
10
+ * (VE.Direct line terminator) and feeds each line into the matching parser.
11
+ * If the port closes unexpectedly (e.g. the USB cable is unplugged) it is
12
+ * re-opened after a back-off, unless it was deliberately closed via `close()`
13
+ * (which clears the slot and cancels any pending reconnect). Ports are tracked
14
+ * by connection index so several connections can run side by side.
15
+ */
16
+ const serialport_1 = require("serialport");
17
+ const parser_delimiter_1 = require("@serialport/parser-delimiter");
18
+ const BAUD_RATE = 19200;
19
+ const RECONNECT_DELAY_MS = 10000;
20
+ const port = [];
21
+ const reconnectTimer = [];
22
+ function open(device, parser, debug, connectionIndex) {
23
+ closePort(connectionIndex);
24
+ debug(`Serial: connecting to ${device}`);
25
+ const sp = new serialport_1.SerialPort({ path: device, baudRate: BAUD_RATE });
26
+ port[connectionIndex] = sp;
27
+ sp.on('open', () => {
28
+ debug(`Connected to ${device}`);
29
+ });
30
+ sp.on('data', (chunk) => {
31
+ debug(`${chunk}`);
32
+ });
33
+ const parsed = sp.pipe(new parser_delimiter_1.DelimiterParser({ delimiter: '\r', includeDelimiter: true }));
34
+ parsed.on('data', (chunk) => {
35
+ // Chunk is a node.js Buffer
36
+ parser[connectionIndex]?.addChunk(chunk, connectionIndex);
37
+ debug(`${chunk}`);
38
+ });
39
+ sp.on('error', (err) => {
40
+ debug(`SerialPort error on ${device}: ${err.message}`);
41
+ });
42
+ sp.on('close', () => {
43
+ // Reconnect only if this is still the active port for the slot. A deliberate
44
+ // close() clears the slot first, and a re-open replaces it, so in both cases
45
+ // the stale port must not trigger a reconnect.
46
+ if (port[connectionIndex] === sp) {
47
+ debug(`Serial port ${device} closed; reconnecting in ${RECONNECT_DELAY_MS}ms`);
48
+ reconnectTimer[connectionIndex] = setTimeout(() => {
49
+ reconnectTimer[connectionIndex] = undefined;
50
+ open(device, parser, debug, connectionIndex);
51
+ }, RECONNECT_DELAY_MS);
52
+ }
53
+ });
54
+ }
55
+ function close(debug, connectionIndex) {
56
+ closePort(connectionIndex);
57
+ debug('Serial port closed');
58
+ }
59
+ /**
60
+ * Writes a raw command to a connection's serial port, used for outbound
61
+ * VE.Direct HEX control frames (e.g. setting the BMV relay). Returns true only
62
+ * when the port is open and the write was issued; false when no open port
63
+ * exists for the index (including the gap while an unexpectedly-closed port
64
+ * awaits reconnect) so the caller can report failure instead of assuming the
65
+ * command reached the device.
66
+ */
67
+ function write(message, connectionIndex) {
68
+ const sp = port[connectionIndex];
69
+ if (sp === undefined || !sp.isOpen) {
70
+ return false;
71
+ }
72
+ try {
73
+ sp.write(message);
74
+ // True means the write was issued, not flushed: serialport surfaces most
75
+ // write failures asynchronously via the 'error' event, which this
76
+ // synchronous boolean cannot reflect. The isOpen guard above covers the
77
+ // common "port not open" case, which is the failure mode worth reporting.
78
+ return true;
79
+ }
80
+ catch {
81
+ // The port can still drop between the isOpen check and the write; treat
82
+ // that as a failed write rather than a thrown error to the PUT requester.
83
+ return false;
84
+ }
85
+ }
86
+ // Cancels any pending reconnect and closes the active port for a slot. The slot
87
+ // is cleared before close() so the port's 'close' handler does not schedule a
88
+ // reconnect for a deliberate teardown.
89
+ function closePort(connectionIndex) {
90
+ const timer = reconnectTimer[connectionIndex];
91
+ if (timer !== undefined) {
92
+ clearTimeout(timer);
93
+ reconnectTimer[connectionIndex] = undefined;
94
+ }
95
+ const sp = port[connectionIndex];
96
+ if (sp !== undefined) {
97
+ port[connectionIndex] = undefined;
98
+ try {
99
+ sp.close();
100
+ }
101
+ catch {
102
+ // best-effort: the port may already be gone
103
+ }
104
+ }
105
+ }
106
+ //# sourceMappingURL=serial.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serial.js","sourceRoot":"","sources":["../src/serial.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Standalone library wrapper.
3
+ *
4
+ * Shims the signalk-server host so the plugin can be consumed as a plain
5
+ * Node.js library (see the README "Usage as a library" section). The plugin's
6
+ * `app.handleMessage` deltas are re-emitted as `delta` events on this
7
+ * EventEmitter; functionality is otherwise identical to running inside
8
+ * signalk-server.
9
+ */
10
+ import { EventEmitter } from 'events';
11
+ import type { VEDirectConfig } from './types';
12
+ declare class VEDirect extends EventEmitter {
13
+ private readonly _debug;
14
+ private readonly app;
15
+ private readonly plugin;
16
+ constructor(config?: VEDirectConfig, _debug?: boolean);
17
+ start(): void;
18
+ stop(): void;
19
+ debug(...args: unknown[]): void;
20
+ }
21
+ export = VEDirect;
22
+ //# sourceMappingURL=standalone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standalone.d.ts","sourceRoot":"","sources":["../src/standalone.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AAErC,OAAO,KAAK,EAKV,cAAc,EACf,MAAM,SAAS,CAAA;AAEhB,cAAM,QAAS,SAAQ,YAAY;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA0C;IAC9D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;gBAEnB,MAAM,GAAE,cAAmB,EAAE,MAAM,UAAQ;IAwCvD,KAAK,IAAI,IAAI;IASb,IAAI,IAAI,IAAI;IASZ,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;CAKhC;AAED,SAAS,QAAQ,CAAA"}