@signalk/vedirect-serial-usb 1.5.0 → 1.6.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 +13 -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 +438 -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 +209 -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 +5 -0
  24. package/dist/serial.d.ts.map +1 -0
  25. package/dist/serial.js +78 -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 +69 -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 +159 -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
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Shared types for the vedirect-serial-usb plugin.
3
+ *
4
+ * `SignalKApp` and `Plugin` are minimal structural descriptions of the
5
+ * objects exchanged with the signalk-server host: only the members the
6
+ * plugin actually uses are modelled.
7
+ *
8
+ * The `Field` / `FieldContext` / `StoredField` triple decouples the field
9
+ * definition table (src/fields.ts) from the parser (src/Parser.ts). Field
10
+ * value functions receive a `FieldContext` rather than the concrete parser
11
+ * class, which keeps the two modules free of a runtime import cycle.
12
+ */
13
+ /** Best-effort log sink. signalk-server passes `app.debug`; the standalone
14
+ * shim and the network modules use the same shape. */
15
+ export type DebugFn = (message: string) => void;
16
+ /**
17
+ * What kind of Victron device a connection talks to. VE.Direct reuses the
18
+ * `V`/`I` labels for both battery monitors and solar chargers, so the device
19
+ * type decides where those readings land (see `Field.solarCharger`). Treated as
20
+ * a battery monitor when unset, which preserves the historical paths.
21
+ */
22
+ export type DeviceType = 'Battery monitor' | 'Solar charger';
23
+ /** One VE.Direct connection as configured in the plugin schema. */
24
+ export interface VEDirectConnection {
25
+ device: 'Serial' | 'UDP' | 'TCP';
26
+ connection: string;
27
+ port: number;
28
+ deviceType?: DeviceType;
29
+ ignoreChecksum: boolean;
30
+ mainBatt: string;
31
+ auxBatt: string;
32
+ bmv: string;
33
+ solar: string;
34
+ }
35
+ /**
36
+ * Plugin options. The current schema uses the `vedirect[]` array; the flat
37
+ * fields are the legacy single-connection format kept so that configs
38
+ * written before the multi-connection rework still start (the in-memory
39
+ * fallback in src/index.ts).
40
+ */
41
+ export interface PluginOptions {
42
+ vedirect?: VEDirectConnection[];
43
+ device?: string;
44
+ host?: string;
45
+ udpPort?: number;
46
+ tcpPort?: number;
47
+ ignoreChecksum?: boolean;
48
+ mainBatt?: string;
49
+ auxBatt?: string;
50
+ bmv?: string;
51
+ solar?: string;
52
+ deviceType?: DeviceType;
53
+ }
54
+ /** Flat single-connection configuration accepted by the standalone library
55
+ * wrapper (src/standalone.ts). */
56
+ export interface VEDirectConfig {
57
+ device?: string;
58
+ connection?: string;
59
+ port?: number;
60
+ host?: string;
61
+ udpPort?: number;
62
+ tcpPort?: number;
63
+ ignoreChecksum?: boolean;
64
+ mainBatt?: string;
65
+ auxBatt?: string;
66
+ bmv?: string;
67
+ solar?: string;
68
+ deviceType?: DeviceType;
69
+ }
70
+ /** Minimal structural type for the `app` the signalk-server host passes to
71
+ * the plugin factory. */
72
+ export interface SignalKApp {
73
+ handleMessage(id: string, delta: SKDelta): void;
74
+ debug: DebugFn;
75
+ }
76
+ /** Shape of the plugin object returned to the signalk-server host. */
77
+ export interface Plugin {
78
+ id: string;
79
+ name: string;
80
+ description: string;
81
+ start(options: PluginOptions): void;
82
+ stop(): void;
83
+ schema: object;
84
+ }
85
+ /** A Signal K delta message emitted for a parsed VE.Direct block.
86
+ *
87
+ * The source is carried as a `$source` ref string (`vedirect-signalk.<index>`)
88
+ * rather than a structured `source` object: a `source.src` field would make
89
+ * signalk-server misclassify the stream as NMEA 2000. See src/Parser.ts. */
90
+ export interface SKDelta {
91
+ context: string;
92
+ updates: Array<{
93
+ $source: string;
94
+ timestamp: string;
95
+ values: Array<{
96
+ path: string;
97
+ value: number | string | null;
98
+ }>;
99
+ }>;
100
+ }
101
+ /** Converts a raw VE.Direct token into a Signal K value. Receives the raw
102
+ * token and the parser (for cross-field lookups and enum decoding). Returns
103
+ * the value to store; `undefined` to skip the field, leaving any prior value
104
+ * untouched; or `null` to store an explicit null, which clears the value in
105
+ * Signal K (e.g. TTG -1, an infinite time-to-go). */
106
+ export type FieldValue = (value: string, instance: FieldContext) => number | string | null | undefined;
107
+ export type FieldType = 'metric' | 'ratio' | 'text' | 'boolean' | 'count';
108
+ /**
109
+ * Identifies which configured device name fills a field path's `*` placeholder.
110
+ * `mainBatt`/`auxBatt`/`bmv`/`solar` map to the per-connection config;
111
+ * `aux2Batt`/`inverter` have no config slot and fall back to the parser's
112
+ * `defaultUnitId`. Keeping this a union (rather than `string`) ties the field
113
+ * table to the resolver in `getPath`: adding a new unit here forces the switch
114
+ * there to handle it.
115
+ */
116
+ export type UnitId = 'mainBatt' | 'auxBatt' | 'aux2Batt' | 'bmv' | 'solar' | 'inverter';
117
+ /** A field definition: how to name, place and convert one VE.Direct token. */
118
+ export interface Field {
119
+ name: string;
120
+ path?: string;
121
+ unitId?: UnitId;
122
+ /**
123
+ * Alternative path and unit used when the connection is a solar charger.
124
+ * VE.Direct sends battery-monitor and charger data under the same labels, but
125
+ * a charger's reading describes its DC output, which belongs under
126
+ * electrical.solar, not electrical.batteries where it would clash with a
127
+ * monitor on the same bank.
128
+ */
129
+ solarCharger?: {
130
+ path: string;
131
+ unitId: UnitId;
132
+ };
133
+ units?: string;
134
+ type?: FieldType;
135
+ value?: FieldValue;
136
+ }
137
+ export type FieldMap = Record<string, Field>;
138
+ /** A field whose `value` has been resolved to a concrete reading and stored
139
+ * on the parser. */
140
+ export interface StoredField {
141
+ name: string;
142
+ value: number | string | null;
143
+ path?: string;
144
+ unitId?: UnitId;
145
+ units?: string;
146
+ type?: FieldType;
147
+ }
148
+ /** The slice of the parser that field value functions are allowed to call.
149
+ * Implemented by VEDirectParser. */
150
+ export interface FieldContext {
151
+ set(key: string, value: StoredField): void;
152
+ getAlarmReason(value: string | number): string | undefined;
153
+ getErrorString(value: string | number): string | undefined;
154
+ getStateOfOperation(value: string | number): string | undefined;
155
+ getMode(value: string | number): string | undefined;
156
+ getTrackerOperationMode(value: string | number): string | undefined;
157
+ getProductLongname(value: string): string;
158
+ }
159
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ /**
3
+ * Shared types for the vedirect-serial-usb plugin.
4
+ *
5
+ * `SignalKApp` and `Plugin` are minimal structural descriptions of the
6
+ * objects exchanged with the signalk-server host: only the members the
7
+ * plugin actually uses are modelled.
8
+ *
9
+ * The `Field` / `FieldContext` / `StoredField` triple decouples the field
10
+ * definition table (src/fields.ts) from the parser (src/Parser.ts). Field
11
+ * value functions receive a `FieldContext` rather than the concrete parser
12
+ * class, which keeps the two modules free of a runtime import cycle.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG"}
package/dist/udp.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import type { VEDirectParser } from './Parser';
2
+ import type { DebugFn } from './types';
3
+ export declare function listen(port: number, parser: VEDirectParser[], debug: DebugFn, connectionIndex: number): void;
4
+ export declare function close(debug: DebugFn, connectionIndex: number): void;
5
+ //# sourceMappingURL=udp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"udp.d.ts","sourceRoot":"","sources":["../src/udp.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAC9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAItC,wBAAgB,MAAM,CACpB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,cAAc,EAAE,EACxB,KAAK,EAAE,OAAO,EACd,eAAe,EAAE,MAAM,GACtB,IAAI,CAcN;AAED,wBAAgB,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,GAAG,IAAI,CAOnE"}
package/dist/udp.js ADDED
@@ -0,0 +1,67 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.listen = listen;
37
+ exports.close = close;
38
+ /**
39
+ * UDP transport.
40
+ *
41
+ * Binds a UDP socket per connection index and feeds each received datagram
42
+ * into the matching parser. Used when a bridge forwards a remote VE.Direct
43
+ * device's serial data as UDP packets.
44
+ */
45
+ const dgram = __importStar(require("dgram"));
46
+ const socket = [];
47
+ function listen(port, parser, debug, connectionIndex) {
48
+ const sock = dgram.createSocket({ type: 'udp4', reuseAddr: true });
49
+ socket[connectionIndex] = sock;
50
+ sock.on('listening', () => {
51
+ debug(`listening on UDP ${port}`);
52
+ });
53
+ sock.on('message', (msg, rinfo) => {
54
+ debug(`${rinfo.address}:${rinfo.port}:${msg}`);
55
+ parser[connectionIndex]?.addChunk(msg, connectionIndex);
56
+ });
57
+ sock.bind(port);
58
+ }
59
+ function close(debug, connectionIndex) {
60
+ const sock = socket[connectionIndex];
61
+ if (sock !== undefined) {
62
+ sock.close();
63
+ socket[connectionIndex] = undefined;
64
+ debug('UDP port closed');
65
+ }
66
+ }
67
+ //# sourceMappingURL=udp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"udp.js","sourceRoot":"","sources":["../src/udp.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAaA,wBAmBC;AAED,sBAOC;AAzCD;;;;;;GAMG;AACH,6CAA8B;AAI9B,MAAM,MAAM,GAAiC,EAAE,CAAA;AAE/C,SAAgB,MAAM,CACpB,IAAY,EACZ,MAAwB,EACxB,KAAc,EACd,eAAuB;IAEvB,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAClE,MAAM,CAAC,eAAe,CAAC,GAAG,IAAI,CAAA;IAE9B,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,GAAG,EAAE;QACxB,KAAK,CAAC,oBAAoB,IAAI,EAAE,CAAC,CAAA;IACnC,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAW,EAAE,KAAuB,EAAE,EAAE;QAC1D,KAAK,CAAC,GAAG,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,IAAI,IAAI,GAAG,EAAE,CAAC,CAAA;QAC9C,MAAM,CAAC,eAAe,CAAC,EAAE,QAAQ,CAAC,GAAG,EAAE,eAAe,CAAC,CAAA;IACzD,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACjB,CAAC;AAED,SAAgB,KAAK,CAAC,KAAc,EAAE,eAAuB;IAC3D,MAAM,IAAI,GAAG,MAAM,CAAC,eAAe,CAAC,CAAA;IACpC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,IAAI,CAAC,KAAK,EAAE,CAAA;QACZ,MAAM,CAAC,eAAe,CAAC,GAAG,SAAS,CAAA;QACnC,KAAK,CAAC,iBAAiB,CAAC,CAAA;IAC1B,CAAC;AACH,CAAC"}
Binary file
package/logo.svg ADDED
@@ -0,0 +1,46 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" role="img" aria-label="Victron VE.Direct">
2
+ <defs>
3
+ <linearGradient id="sea" x1="0" y1="0" x2="0" y2="1">
4
+ <stop offset="0" stop-color="#1b6ea8"/>
5
+ <stop offset="1" stop-color="#0b3a5e"/>
6
+ </linearGradient>
7
+ <clipPath id="card">
8
+ <rect x="0" y="0" width="128" height="128" rx="22"/>
9
+ </clipPath>
10
+ </defs>
11
+
12
+ <g clip-path="url(#card)">
13
+ <rect width="128" height="128" fill="url(#sea)"/>
14
+
15
+ <g stroke="#bee3f4" stroke-opacity="0.25" stroke-width="1" fill="none">
16
+ <path d="M0 24 H128 M0 48 H128 M0 72 H128 M0 96 H128"/>
17
+ <path d="M24 0 V128 M48 0 V128 M72 0 V128 M96 0 V128"/>
18
+ </g>
19
+
20
+ <!-- Victron-style energy waves radiating from the device -->
21
+ <g stroke="#35aee0" stroke-width="2.8" fill="none" stroke-linecap="round">
22
+ <path d="M53 37 Q61 29 69 37"/>
23
+ <path d="M48 37 Q61 23 74 37"/>
24
+ <path d="M43 37 Q61 17 79 37"/>
25
+ </g>
26
+
27
+ <!-- battery (monitor / charger that VE.Direct reads) -->
28
+ <rect x="53" y="40" width="16" height="7" rx="2.5" fill="#cbd5e1" stroke="#0b3a5e" stroke-width="2"/>
29
+ <rect x="40" y="47" width="42" height="56" rx="7" fill="#e7eef5" stroke="#0b3a5e" stroke-width="2.6"/>
30
+ <!-- charge level -->
31
+ <path d="M44 73 H78 V95 Q78 99 74 99 H48 Q44 99 44 95 Z" fill="#1f9bd7"/>
32
+
33
+ <!-- charge bolt -->
34
+ <polygon points="65,53 52,79 60,79 56,98 75,71 64,71 70,53" fill="#e63946" stroke="#0b3a5e" stroke-width="1.6" stroke-linejoin="round"/>
35
+
36
+ <!-- compass rose -->
37
+ <g transform="translate(104 104)">
38
+ <circle r="11" fill="#0b3a5e" fill-opacity="0.7" stroke="#bee3f4" stroke-width="0.8"/>
39
+ <polygon points="0,-9 2.2,0 0,0" fill="#e63946"/>
40
+ <polygon points="0,-9 -2.2,0 0,0" fill="#b5232f"/>
41
+ <polygon points="0,9 2.2,0 0,0" fill="#e2e8f0"/>
42
+ <polygon points="0,9 -2.2,0 0,0" fill="#cbd5e1"/>
43
+ <circle r="1.1" fill="#e2e8f0"/>
44
+ </g>
45
+ </g>
46
+ </svg>
package/package.json CHANGED
@@ -1,12 +1,52 @@
1
1
  {
2
2
  "name": "@signalk/vedirect-serial-usb",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Signal K Node.js server plugin to read VE.Direct data via serial USB",
5
- "main": "index.js",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "require": "./dist/index.js",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "./standalone": {
14
+ "types": "./dist/standalone.d.ts",
15
+ "require": "./dist/standalone.js",
16
+ "default": "./dist/standalone.js"
17
+ },
18
+ "./package.json": "./package.json"
19
+ },
20
+ "sideEffects": false,
21
+ "engines": {
22
+ "node": ">=20"
23
+ },
24
+ "files": [
25
+ "dist",
26
+ "logo.svg",
27
+ "docs/screenshots",
28
+ "README.md",
29
+ "LICENSE"
30
+ ],
31
+ "signalk": {
32
+ "displayName": "Victron VE.Direct",
33
+ "appIcon": "./logo.svg",
34
+ "screenshots": [
35
+ "./docs/screenshots/1_config.png"
36
+ ]
37
+ },
6
38
  "scripts": {
39
+ "build": "tsc -p tsconfig.build.json",
40
+ "clean": "rimraf dist",
41
+ "prebuild": "npm run clean",
42
+ "typecheck": "tsc --noEmit",
7
43
  "test": "mocha",
8
- "push": "npm publish --access public --scope @signalk .",
9
- "postinstall": "node ./migration.js"
44
+ "coverage": "c8 --reporter=text --reporter=lcov npm test",
45
+ "mutation": "stryker run",
46
+ "prettier:check": "prettier --check .",
47
+ "prettier": "prettier --write .",
48
+ "format": "npm run prettier",
49
+ "prepublishOnly": "npm run build"
10
50
  },
11
51
  "keywords": [
12
52
  "signalk-node-server-plugin"
@@ -21,23 +61,43 @@
21
61
  "type": "git",
22
62
  "url": "https://github.com/SignalK/vedirect-serial-usb"
23
63
  },
24
- "contributors": [],
25
64
  "dependencies": {
26
- "debug": "^4.0.1",
27
- "package-lock.json": "^1.0.0",
28
- "serialport": "^11.0.0"
29
- },
30
- "standard": {
31
- "globals": [
32
- "describe",
33
- "it",
34
- "expect",
35
- "before"
36
- ]
65
+ "@serialport/parser-delimiter": "^13.0.0",
66
+ "debug": "^4.4.3",
67
+ "serialport": "^13.0.0"
37
68
  },
38
69
  "devDependencies": {
39
- "chai": "^4.1.2",
40
- "chai-things": "^0.2.0",
41
- "mocha": "^10.2.0"
70
+ "@stryker-mutator/core": "^9.6.1",
71
+ "@stryker-mutator/mocha-runner": "^9.6.1",
72
+ "@types/chai": "^4.3.20",
73
+ "@types/debug": "^4.1.12",
74
+ "@types/mocha": "^10.0.10",
75
+ "@types/node": "^25.9.2",
76
+ "c8": "^11.0.0",
77
+ "chai": "^6.2.2",
78
+ "mocha": "^11.7.1",
79
+ "prettier": "^3.8.2",
80
+ "rimraf": "^6.1.3",
81
+ "tsx": "^4.21.0",
82
+ "typescript": "^6.0.3"
83
+ },
84
+ "c8": {
85
+ "extension": [
86
+ ".ts"
87
+ ],
88
+ "include": [
89
+ "src/**/*.ts"
90
+ ],
91
+ "exclude": [
92
+ "test/**",
93
+ "coverage/**",
94
+ "dist/**",
95
+ "src/types.ts"
96
+ ],
97
+ "all": true,
98
+ "reporter": [
99
+ "text",
100
+ "lcov"
101
+ ]
42
102
  }
43
103
  }
@@ -1 +0,0 @@
1
- github: joabakk
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
- }