signalk-garmin-keypad-plugin 1.0.2
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/.codex +0 -0
- package/.github/workflows/publish.yml +42 -0
- package/README.md +69 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.js +395 -0
- package/dist/n2k.d.ts +19 -0
- package/dist/n2k.js +143 -0
- package/dist/pgns.d.ts +169 -0
- package/dist/pgns.js +454 -0
- package/dist/protocol.d.ts +22 -0
- package/dist/protocol.js +55 -0
- package/index.js +1 -0
- package/package.json +32 -0
- package/public/183.js +1 -0
- package/public/main.js +1 -0
- package/public/remoteEntry.js +1 -0
- package/resources/demo.png +0 -0
- package/tsconfig.json +13 -0
package/dist/n2k.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildDisplaySelect = exports.buildSleepWake = exports.setFingerprint = exports.ensureCounterAbove = exports.decodeCounter = exports.resetCounters = exports.buildPageNav = exports.buildSavePreset = exports.buildSelectPreset = exports.extractGroupIdFromPayload = exports.setGroupId = void 0;
|
|
4
|
+
const protocol_1 = require("./protocol");
|
|
5
|
+
let currentGroupId = protocol_1.DEFAULT_GROUP_ID;
|
|
6
|
+
function setGroupId(groupId) {
|
|
7
|
+
currentGroupId = groupId;
|
|
8
|
+
}
|
|
9
|
+
exports.setGroupId = setGroupId;
|
|
10
|
+
// Group ID is at payload bytes 7-10 in both 0xe5 and 0xe7 messages.
|
|
11
|
+
function extractGroupIdFromPayload(payload) {
|
|
12
|
+
if (!payload || !Buffer.isBuffer(payload) || payload.length < 11)
|
|
13
|
+
return null;
|
|
14
|
+
const id = payload.slice(7, 11);
|
|
15
|
+
if (id.every(b => b === 0))
|
|
16
|
+
return null;
|
|
17
|
+
return id;
|
|
18
|
+
}
|
|
19
|
+
exports.extractGroupIdFromPayload = extractGroupIdFromPayload;
|
|
20
|
+
// --- PGN 61184 builders (field-based, encoded by canboatjs) ---
|
|
21
|
+
function buildButtonPgn(command, paramName, paramValue, src) {
|
|
22
|
+
return {
|
|
23
|
+
pgn: protocol_1.PGN_SINGLE,
|
|
24
|
+
dst: protocol_1.DEFAULT_DST,
|
|
25
|
+
prio: protocol_1.DEFAULT_PRIO,
|
|
26
|
+
src,
|
|
27
|
+
'Manufacturer Code': 229,
|
|
28
|
+
'Industry Code': 4,
|
|
29
|
+
'Command': command,
|
|
30
|
+
'Product ID': protocol_1.PRODUCT_ID,
|
|
31
|
+
'Unknown 1': protocol_1.UNK1,
|
|
32
|
+
'Unknown 2': protocol_1.UNK2,
|
|
33
|
+
[paramName]: paramValue
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function buildSelectPreset(index, src = protocol_1.DEFAULT_SRC) {
|
|
37
|
+
if (!Number.isInteger(index) || index < 0 || index > 3) {
|
|
38
|
+
throw new Error(`Preset index must be 0-3, got ${index}`);
|
|
39
|
+
}
|
|
40
|
+
return buildButtonPgn(protocol_1.CMD_SELECT_PRESET, 'Preset Index', index, src);
|
|
41
|
+
}
|
|
42
|
+
exports.buildSelectPreset = buildSelectPreset;
|
|
43
|
+
function buildSavePreset(index, src = protocol_1.DEFAULT_SRC) {
|
|
44
|
+
if (!Number.isInteger(index) || index < 0 || index > 3) {
|
|
45
|
+
throw new Error(`Preset index must be 0-3, got ${index}`);
|
|
46
|
+
}
|
|
47
|
+
return buildButtonPgn(protocol_1.CMD_SAVE_PRESET, 'Preset Index', index, src);
|
|
48
|
+
}
|
|
49
|
+
exports.buildSavePreset = buildSavePreset;
|
|
50
|
+
function buildPageNav(direction, src = protocol_1.DEFAULT_SRC) {
|
|
51
|
+
const param = direction === 'next' ? 0 : 1;
|
|
52
|
+
return buildButtonPgn(protocol_1.CMD_PAGE_NAV, 'Direction', param, src);
|
|
53
|
+
}
|
|
54
|
+
exports.buildPageNav = buildPageNav;
|
|
55
|
+
// --- PGN 126720 builders (payload as pre-built Buffer) ---
|
|
56
|
+
// Per-property sequence counters (mimics real keypad behavior).
|
|
57
|
+
// Each property name maintains its own independent counter.
|
|
58
|
+
// Counter space is 10-bit (0-1023), wrapping at 1024.
|
|
59
|
+
// Bytes 5-6 encode counter C as: byte5 = 0x8e + (C & 7) * 0x10, byte6 = C >> 3.
|
|
60
|
+
// T6 must stay in 0x00-0x7f range (max counter 1023); displays silently
|
|
61
|
+
// reject messages with T6 >= 0x80.
|
|
62
|
+
const propertyCounters = new Map();
|
|
63
|
+
const MAX_SEQ = 0x3FF; // 1023 — maximum valid counter value
|
|
64
|
+
function resetCounters() {
|
|
65
|
+
propertyCounters.clear();
|
|
66
|
+
}
|
|
67
|
+
exports.resetCounters = resetCounters;
|
|
68
|
+
function decodeCounter(t5, t6) {
|
|
69
|
+
return (t6 << 3) | ((t5 - 0x8e) >> 4);
|
|
70
|
+
}
|
|
71
|
+
exports.decodeCounter = decodeCounter;
|
|
72
|
+
// Next buildTrailing call will send (stored + 1) & MAX_SEQ.
|
|
73
|
+
function ensureCounterAbove(property, minSeq) {
|
|
74
|
+
var _a;
|
|
75
|
+
const current = (_a = propertyCounters.get(property)) !== null && _a !== void 0 ? _a : -1;
|
|
76
|
+
const clamped = minSeq & MAX_SEQ;
|
|
77
|
+
if (current < clamped) {
|
|
78
|
+
propertyCounters.set(property, clamped);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.ensureCounterAbove = ensureCounterAbove;
|
|
82
|
+
// Keypad fingerprint bytes — must match the fingerprint stored on the GNX
|
|
83
|
+
// displays for the property being set. Displays persist the fingerprint of
|
|
84
|
+
// the last keypad that successfully changed each property and reject commands
|
|
85
|
+
// from a different fingerprint. Auto-discovered from bus traffic or set
|
|
86
|
+
// manually via config.
|
|
87
|
+
let keypadFingerprint = null;
|
|
88
|
+
function setFingerprint(fp) {
|
|
89
|
+
keypadFingerprint = fp;
|
|
90
|
+
}
|
|
91
|
+
exports.setFingerprint = setFingerprint;
|
|
92
|
+
function buildTrailing(property) {
|
|
93
|
+
var _a, _b, _c;
|
|
94
|
+
const prev = (_a = propertyCounters.get(property)) !== null && _a !== void 0 ? _a : -1;
|
|
95
|
+
const seq = (prev + 1) & MAX_SEQ;
|
|
96
|
+
propertyCounters.set(property, seq);
|
|
97
|
+
const buf = Buffer.alloc(7);
|
|
98
|
+
buf[0] = 0x2e;
|
|
99
|
+
buf[1] = 0x80 | (Math.random() * 128 | 0); // T1: random nonce, bit 7 must be set or displays reject
|
|
100
|
+
buf[2] = 0xb0; // T2: 1-bit state flag — 0xb0 is safe constant
|
|
101
|
+
buf[3] = (_b = keypadFingerprint === null || keypadFingerprint === void 0 ? void 0 : keypadFingerprint[0]) !== null && _b !== void 0 ? _b : 0; // T3: keypad fingerprint byte 1
|
|
102
|
+
buf[4] = (_c = keypadFingerprint === null || keypadFingerprint === void 0 ? void 0 : keypadFingerprint[1]) !== null && _c !== void 0 ? _c : 0; // T4: keypad fingerprint byte 2
|
|
103
|
+
buf[5] = 0x8e + (seq & 7) * 0x10; // T5: counter low bits
|
|
104
|
+
buf[6] = (seq >> 3) & 0x7f; // T6: counter high bits (must be 0x00-0x7f)
|
|
105
|
+
return buf;
|
|
106
|
+
}
|
|
107
|
+
function buildPropertyPayload(property, value) {
|
|
108
|
+
const strLen = property.length + 1;
|
|
109
|
+
const strBuf = Buffer.from(property + '\0', 'ascii');
|
|
110
|
+
const valueBuf = Buffer.from([value]);
|
|
111
|
+
// Skip first byte of header (command byte 0xe5) — canboatjs writes it from PGN Match
|
|
112
|
+
return Buffer.concat([
|
|
113
|
+
(0, protocol_1.buildPropertyHeader)(currentGroupId).slice(1),
|
|
114
|
+
Buffer.from([strLen]),
|
|
115
|
+
strBuf,
|
|
116
|
+
protocol_1.PROPERTY_SEPARATOR,
|
|
117
|
+
valueBuf,
|
|
118
|
+
buildTrailing(property)
|
|
119
|
+
]);
|
|
120
|
+
}
|
|
121
|
+
function buildPropertyPgn(property, value, src) {
|
|
122
|
+
return {
|
|
123
|
+
pgn: protocol_1.PGN_FAST,
|
|
124
|
+
dst: protocol_1.DEFAULT_DST,
|
|
125
|
+
prio: protocol_1.DEFAULT_PRIO,
|
|
126
|
+
src,
|
|
127
|
+
'Manufacturer Code': 229,
|
|
128
|
+
'Industry Code': 4,
|
|
129
|
+
'Command': 0xe5,
|
|
130
|
+
'Payload': buildPropertyPayload(property, value)
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function buildSleepWake(sleep, src = protocol_1.DEFAULT_SRC) {
|
|
134
|
+
return buildPropertyPgn(protocol_1.PROP_SLEEP, sleep ? protocol_1.SLEEP : protocol_1.WAKE, src);
|
|
135
|
+
}
|
|
136
|
+
exports.buildSleepWake = buildSleepWake;
|
|
137
|
+
function buildDisplaySelect(index, src = protocol_1.DEFAULT_SRC) {
|
|
138
|
+
if (!Number.isInteger(index) || index < 0) {
|
|
139
|
+
throw new Error(`Display index must be a non-negative integer, got ${index}`);
|
|
140
|
+
}
|
|
141
|
+
return buildPropertyPgn(protocol_1.PROP_DISPLAY, index, src);
|
|
142
|
+
}
|
|
143
|
+
exports.buildDisplaySelect = buildDisplaySelect;
|
package/dist/pgns.d.ts
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
export const PGNs: ({
|
|
2
|
+
PGN: number;
|
|
3
|
+
Id: string;
|
|
4
|
+
Description: string;
|
|
5
|
+
Type: string;
|
|
6
|
+
Complete: boolean;
|
|
7
|
+
Length: number;
|
|
8
|
+
Fields: ({
|
|
9
|
+
Order: number;
|
|
10
|
+
Id: string;
|
|
11
|
+
Name: string;
|
|
12
|
+
BitLength: number;
|
|
13
|
+
BitOffset: number;
|
|
14
|
+
BitStart: number;
|
|
15
|
+
Match: number;
|
|
16
|
+
FieldType: string;
|
|
17
|
+
LookupEnumeration: string;
|
|
18
|
+
Signed: boolean;
|
|
19
|
+
Resolution?: undefined;
|
|
20
|
+
} | {
|
|
21
|
+
Order: number;
|
|
22
|
+
Id: string;
|
|
23
|
+
Name: string;
|
|
24
|
+
BitLength: number;
|
|
25
|
+
BitOffset: number;
|
|
26
|
+
BitStart: number;
|
|
27
|
+
FieldType: string;
|
|
28
|
+
Match?: undefined;
|
|
29
|
+
LookupEnumeration?: undefined;
|
|
30
|
+
Signed?: undefined;
|
|
31
|
+
Resolution?: undefined;
|
|
32
|
+
} | {
|
|
33
|
+
Order: number;
|
|
34
|
+
Id: string;
|
|
35
|
+
Name: string;
|
|
36
|
+
BitLength: number;
|
|
37
|
+
BitOffset: number;
|
|
38
|
+
BitStart: number;
|
|
39
|
+
FieldType: string;
|
|
40
|
+
Resolution: number;
|
|
41
|
+
Signed: boolean;
|
|
42
|
+
Match: number;
|
|
43
|
+
LookupEnumeration?: undefined;
|
|
44
|
+
} | {
|
|
45
|
+
Order: number;
|
|
46
|
+
Id: string;
|
|
47
|
+
Name: string;
|
|
48
|
+
BitLength: number;
|
|
49
|
+
BitOffset: number;
|
|
50
|
+
BitStart: number;
|
|
51
|
+
FieldType: string;
|
|
52
|
+
Resolution: number;
|
|
53
|
+
Signed: boolean;
|
|
54
|
+
Match?: undefined;
|
|
55
|
+
LookupEnumeration?: undefined;
|
|
56
|
+
})[];
|
|
57
|
+
} | {
|
|
58
|
+
PGN: number;
|
|
59
|
+
Id: string;
|
|
60
|
+
Description: string;
|
|
61
|
+
Type: string;
|
|
62
|
+
Complete: boolean;
|
|
63
|
+
Fields: ({
|
|
64
|
+
Order: number;
|
|
65
|
+
Id: string;
|
|
66
|
+
Name: string;
|
|
67
|
+
BitLength: number;
|
|
68
|
+
BitOffset: number;
|
|
69
|
+
BitStart: number;
|
|
70
|
+
Match: number;
|
|
71
|
+
FieldType: string;
|
|
72
|
+
LookupEnumeration: string;
|
|
73
|
+
Signed: boolean;
|
|
74
|
+
Resolution?: undefined;
|
|
75
|
+
} | {
|
|
76
|
+
Order: number;
|
|
77
|
+
Id: string;
|
|
78
|
+
Name: string;
|
|
79
|
+
BitLength: number;
|
|
80
|
+
BitOffset: number;
|
|
81
|
+
BitStart: number;
|
|
82
|
+
FieldType: string;
|
|
83
|
+
Match?: undefined;
|
|
84
|
+
LookupEnumeration?: undefined;
|
|
85
|
+
Signed?: undefined;
|
|
86
|
+
Resolution?: undefined;
|
|
87
|
+
} | {
|
|
88
|
+
Order: number;
|
|
89
|
+
Id: string;
|
|
90
|
+
Name: string;
|
|
91
|
+
BitLength: number;
|
|
92
|
+
BitOffset: number;
|
|
93
|
+
BitStart: number;
|
|
94
|
+
FieldType: string;
|
|
95
|
+
Resolution: number;
|
|
96
|
+
Signed: boolean;
|
|
97
|
+
Match: number;
|
|
98
|
+
LookupEnumeration?: undefined;
|
|
99
|
+
} | {
|
|
100
|
+
Order: number;
|
|
101
|
+
Id: string;
|
|
102
|
+
Name: string;
|
|
103
|
+
BitLength: number;
|
|
104
|
+
BitOffset: number;
|
|
105
|
+
BitStart: number;
|
|
106
|
+
FieldType: string;
|
|
107
|
+
Signed: boolean;
|
|
108
|
+
Match?: undefined;
|
|
109
|
+
LookupEnumeration?: undefined;
|
|
110
|
+
Resolution?: undefined;
|
|
111
|
+
})[];
|
|
112
|
+
Length?: undefined;
|
|
113
|
+
} | {
|
|
114
|
+
PGN: number;
|
|
115
|
+
Id: string;
|
|
116
|
+
Description: string;
|
|
117
|
+
Type: string;
|
|
118
|
+
Complete: boolean;
|
|
119
|
+
Length: number;
|
|
120
|
+
Fields: ({
|
|
121
|
+
Order: number;
|
|
122
|
+
Id: string;
|
|
123
|
+
Name: string;
|
|
124
|
+
BitLength: number;
|
|
125
|
+
BitOffset: number;
|
|
126
|
+
BitStart: number;
|
|
127
|
+
Match: number;
|
|
128
|
+
FieldType: string;
|
|
129
|
+
LookupEnumeration: string;
|
|
130
|
+
Signed: boolean;
|
|
131
|
+
Resolution?: undefined;
|
|
132
|
+
} | {
|
|
133
|
+
Order: number;
|
|
134
|
+
Id: string;
|
|
135
|
+
Name: string;
|
|
136
|
+
BitLength: number;
|
|
137
|
+
BitOffset: number;
|
|
138
|
+
BitStart: number;
|
|
139
|
+
FieldType: string;
|
|
140
|
+
Match?: undefined;
|
|
141
|
+
LookupEnumeration?: undefined;
|
|
142
|
+
Signed?: undefined;
|
|
143
|
+
Resolution?: undefined;
|
|
144
|
+
} | {
|
|
145
|
+
Order: number;
|
|
146
|
+
Id: string;
|
|
147
|
+
Name: string;
|
|
148
|
+
BitLength: number;
|
|
149
|
+
BitOffset: number;
|
|
150
|
+
BitStart: number;
|
|
151
|
+
FieldType: string;
|
|
152
|
+
Resolution: number;
|
|
153
|
+
Signed: boolean;
|
|
154
|
+
Match: number;
|
|
155
|
+
LookupEnumeration?: undefined;
|
|
156
|
+
} | {
|
|
157
|
+
Order: number;
|
|
158
|
+
Id: string;
|
|
159
|
+
Name: string;
|
|
160
|
+
BitLength: number;
|
|
161
|
+
BitOffset: number;
|
|
162
|
+
BitStart: number;
|
|
163
|
+
FieldType: string;
|
|
164
|
+
Signed: boolean;
|
|
165
|
+
Match?: undefined;
|
|
166
|
+
LookupEnumeration?: undefined;
|
|
167
|
+
Resolution?: undefined;
|
|
168
|
+
})[];
|
|
169
|
+
})[];
|