kirby-ocpp 0.1.0 → 0.1.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/README.md +1 -0
- package/dist/cli.js +0 -0
- package/dist/ocpp/client.js +8 -3
- package/dist/station.d.ts +1 -0
- package/dist/station.js +8 -5
- package/dist/tui.d.ts +1 -0
- package/dist/tui.js +74 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -88,6 +88,7 @@ Global shortcuts:
|
|
|
88
88
|
- `q`: quit.
|
|
89
89
|
|
|
90
90
|
Forms prompt for values such as `idTag`, connector id, measurands, and `DataTransfer` payloads. There is no built-in default RFID/idTag.
|
|
91
|
+
On the Connector tab, `s` cycles the selected connector status and `S` opens a list of all OCPP connector statuses.
|
|
91
92
|
|
|
92
93
|
## Persistence
|
|
93
94
|
|
package/dist/cli.js
CHANGED
|
File without changes
|
package/dist/ocpp/client.js
CHANGED
|
@@ -54,9 +54,14 @@ export class OcppClient extends EventEmitter {
|
|
|
54
54
|
}
|
|
55
55
|
this.rejectPending(new Error("Connection closed"));
|
|
56
56
|
this.stopPingTimer();
|
|
57
|
-
this.socket
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
const socket = this.socket;
|
|
58
|
+
socket.removeAllListeners();
|
|
59
|
+
socket.on("error", () => { });
|
|
60
|
+
if (socket.readyState === WebSocket.OPEN) {
|
|
61
|
+
socket.close();
|
|
62
|
+
}
|
|
63
|
+
else if (socket.readyState === WebSocket.CONNECTING) {
|
|
64
|
+
socket.terminate();
|
|
60
65
|
}
|
|
61
66
|
this.socket = undefined;
|
|
62
67
|
}
|
package/dist/station.d.ts
CHANGED
|
@@ -57,6 +57,7 @@ export declare class Station extends EventEmitter {
|
|
|
57
57
|
setDiagnosticsOutcome(outcome: DiagnosticsOutcome): void;
|
|
58
58
|
setFirmwareOutcome(outcome: FirmwareOutcome): void;
|
|
59
59
|
cycleStatus(connectorId?: number): Promise<void>;
|
|
60
|
+
setStatus(connectorId: number | undefined, status: ConnectorStatus): Promise<void>;
|
|
60
61
|
addConnector(): number;
|
|
61
62
|
private bindClientEvents;
|
|
62
63
|
private handleCentralSystemCall;
|
package/dist/station.js
CHANGED
|
@@ -495,14 +495,17 @@ export class Station extends EventEmitter {
|
|
|
495
495
|
async cycleStatus(connectorId = this.config.connectorId) {
|
|
496
496
|
const connector = this.connectors.get(connectorId);
|
|
497
497
|
const next = nextConnectorStatus(connector.status);
|
|
498
|
+
await this.setStatus(connectorId, next);
|
|
499
|
+
}
|
|
500
|
+
async setStatus(connectorId = this.config.connectorId, status) {
|
|
498
501
|
this.connectors.patch(connectorId, {
|
|
499
|
-
status
|
|
500
|
-
errorCode:
|
|
501
|
-
info:
|
|
502
|
-
vendorId:
|
|
502
|
+
status,
|
|
503
|
+
errorCode: status === "Faulted" ? "OtherError" : "NoError",
|
|
504
|
+
info: status === "Faulted" ? "Manual fault" : undefined,
|
|
505
|
+
vendorId: status === "Faulted" ? this.config.vendor : undefined,
|
|
503
506
|
vendorErrorCode: undefined
|
|
504
507
|
});
|
|
505
|
-
await this.statusNotification(connectorId,
|
|
508
|
+
await this.statusNotification(connectorId, status);
|
|
506
509
|
}
|
|
507
510
|
addConnector() {
|
|
508
511
|
const { id: nextId } = this.connectors.add();
|
package/dist/tui.d.ts
CHANGED
package/dist/tui.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import blessed from "blessed";
|
|
3
|
+
import { CONNECTOR_STATUS_SEQUENCE } from "./station/connector-status.js";
|
|
3
4
|
const TABS = [
|
|
4
5
|
{ id: "station", label: "Station" },
|
|
5
6
|
{ id: "connector", label: "Connector" },
|
|
@@ -10,7 +11,7 @@ const TABS = [
|
|
|
10
11
|
{ id: "scenarios", label: "Scenarios" }
|
|
11
12
|
];
|
|
12
13
|
const LEVEL_STYLE = {
|
|
13
|
-
info: "{
|
|
14
|
+
info: "{white-fg}info{/white-fg}",
|
|
14
15
|
success: "{green-fg}ok{/green-fg}",
|
|
15
16
|
warn: "{yellow-fg}warn{/yellow-fg}",
|
|
16
17
|
error: "{red-fg}err{/red-fg}",
|
|
@@ -19,14 +20,27 @@ const LEVEL_STYLE = {
|
|
|
19
20
|
};
|
|
20
21
|
const DEFAULT_MEASURANDS = "Energy.Active.Import.Register,Power.Active.Import,Current.Import,Voltage,Temperature,SoC";
|
|
21
22
|
const THEME = {
|
|
23
|
+
background: "black",
|
|
24
|
+
foreground: "white",
|
|
22
25
|
panelBorder: "cyan",
|
|
23
26
|
headerBg: "black",
|
|
24
27
|
headerFg: "white",
|
|
25
28
|
footerBg: "black",
|
|
26
29
|
footerFg: "white",
|
|
30
|
+
mutedFg: "cyan",
|
|
27
31
|
selectedBg: "white",
|
|
28
|
-
selectedFg: "black"
|
|
32
|
+
selectedFg: "black",
|
|
33
|
+
focusBorder: "yellow"
|
|
29
34
|
};
|
|
35
|
+
function fgTag(color, content) {
|
|
36
|
+
return `{${color}-fg}${content}{/${color}-fg}`;
|
|
37
|
+
}
|
|
38
|
+
function muted(content) {
|
|
39
|
+
return fgTag(THEME.mutedFg, content);
|
|
40
|
+
}
|
|
41
|
+
function disabledLine(content) {
|
|
42
|
+
return muted(`${content} (disabled)`);
|
|
43
|
+
}
|
|
30
44
|
export class Tui {
|
|
31
45
|
station;
|
|
32
46
|
screen;
|
|
@@ -71,6 +85,8 @@ export class Tui {
|
|
|
71
85
|
border: "line",
|
|
72
86
|
padding: { left: 1, right: 1 },
|
|
73
87
|
style: {
|
|
88
|
+
fg: THEME.foreground,
|
|
89
|
+
bg: THEME.background,
|
|
74
90
|
selected: { bg: THEME.selectedBg, fg: THEME.selectedFg },
|
|
75
91
|
border: { fg: THEME.panelBorder }
|
|
76
92
|
}
|
|
@@ -85,6 +101,8 @@ export class Tui {
|
|
|
85
101
|
border: "line",
|
|
86
102
|
padding: { left: 1, right: 1 },
|
|
87
103
|
style: {
|
|
104
|
+
fg: THEME.foreground,
|
|
105
|
+
bg: THEME.background,
|
|
88
106
|
border: { fg: THEME.panelBorder }
|
|
89
107
|
}
|
|
90
108
|
});
|
|
@@ -100,6 +118,8 @@ export class Tui {
|
|
|
100
118
|
border: "line",
|
|
101
119
|
padding: { left: 1, right: 1 },
|
|
102
120
|
style: {
|
|
121
|
+
fg: THEME.foreground,
|
|
122
|
+
bg: THEME.background,
|
|
103
123
|
selected: { bg: THEME.selectedBg, fg: THEME.selectedFg },
|
|
104
124
|
border: { fg: THEME.panelBorder }
|
|
105
125
|
}
|
|
@@ -123,6 +143,8 @@ export class Tui {
|
|
|
123
143
|
style: { bg: THEME.panelBorder }
|
|
124
144
|
},
|
|
125
145
|
style: {
|
|
146
|
+
fg: THEME.foreground,
|
|
147
|
+
bg: THEME.background,
|
|
126
148
|
border: { fg: THEME.panelBorder }
|
|
127
149
|
}
|
|
128
150
|
});
|
|
@@ -281,7 +303,7 @@ export class Tui {
|
|
|
281
303
|
const compact = this.screenColumns() < 110;
|
|
282
304
|
const tabLine = TABS.map((tab, index) => {
|
|
283
305
|
const label = compact ? `${index + 1}:${tab.label.slice(0, 4)}` : `${index + 1}:${tab.label}`;
|
|
284
|
-
return tab.id === this.activeTab ? `{black-fg}{white-bg}${label}{/white-bg}{/black-fg}` :
|
|
306
|
+
return tab.id === this.activeTab ? `{black-fg}{white-bg}${label}{/white-bg}{/black-fg}` : muted(label);
|
|
285
307
|
}).join(" ");
|
|
286
308
|
this.header.setContent([
|
|
287
309
|
` {bold}kirby-ocpp{/bold} ${this.station.config.chargePointId} -> ${this.station.config.centralSystemUrl}`,
|
|
@@ -357,9 +379,11 @@ export class Tui {
|
|
|
357
379
|
this.actions = this.actionsForTab();
|
|
358
380
|
const width = this.centerContentWidth();
|
|
359
381
|
const items = this.actions.map((action) => {
|
|
360
|
-
const
|
|
361
|
-
const suffix =
|
|
362
|
-
|
|
382
|
+
const disabled = action.disabled?.() ?? false;
|
|
383
|
+
const suffix = disabled ? " (disabled)" : "";
|
|
384
|
+
const labelWidth = Math.max(12, width - 6 - suffix.length);
|
|
385
|
+
const line = `${action.key.padEnd(2)} ${truncateText(action.label, labelWidth)}`;
|
|
386
|
+
return disabled ? disabledLine(line) : line;
|
|
363
387
|
});
|
|
364
388
|
this.actionList.setLabel(` ${this.tabLabel(this.activeTab)} actions `);
|
|
365
389
|
this.actionList.setItems(items);
|
|
@@ -394,6 +418,7 @@ export class Tui {
|
|
|
394
418
|
return [
|
|
395
419
|
action("p", "Plug / unplug EV", "Toggle EV plug state on the selected connector.", () => this.togglePlug()),
|
|
396
420
|
action("s", "Cycle status", "Cycle StatusNotification status on the selected connector.", () => this.runStationTask(() => this.station.cycleStatus(this.selectedConnectorId))),
|
|
421
|
+
action("S", "Set status", "Choose any StatusNotification status for the selected connector.", () => this.setStatusFlow()),
|
|
397
422
|
action("n", "Send StatusNotification", "Send current selected connector status now.", () => this.runStationTask(() => this.station.statusNotification(this.selectedConnectorId, this.selectedConnector()?.status, true))),
|
|
398
423
|
action("f", "Fault / clear fault", "Inject or clear a fault on the selected connector.", () => this.faultFlow()),
|
|
399
424
|
action("]", "Next connector", "Select next connector.", () => this.selectNextConnector()),
|
|
@@ -460,6 +485,8 @@ export class Tui {
|
|
|
460
485
|
"d",
|
|
461
486
|
"D",
|
|
462
487
|
"S-d",
|
|
488
|
+
"S",
|
|
489
|
+
"S-s",
|
|
463
490
|
"e",
|
|
464
491
|
"f",
|
|
465
492
|
"h",
|
|
@@ -542,6 +569,8 @@ export class Tui {
|
|
|
542
569
|
tags: true,
|
|
543
570
|
keys: true,
|
|
544
571
|
style: {
|
|
572
|
+
fg: THEME.foreground,
|
|
573
|
+
bg: THEME.background,
|
|
545
574
|
border: { fg: THEME.panelBorder }
|
|
546
575
|
}
|
|
547
576
|
});
|
|
@@ -555,6 +584,8 @@ export class Tui {
|
|
|
555
584
|
keys: true,
|
|
556
585
|
tags: true,
|
|
557
586
|
style: {
|
|
587
|
+
fg: THEME.foreground,
|
|
588
|
+
bg: THEME.background,
|
|
558
589
|
border: { fg: THEME.panelBorder }
|
|
559
590
|
}
|
|
560
591
|
});
|
|
@@ -570,6 +601,8 @@ export class Tui {
|
|
|
570
601
|
vi: false,
|
|
571
602
|
items: [],
|
|
572
603
|
style: {
|
|
604
|
+
fg: THEME.foreground,
|
|
605
|
+
bg: THEME.background,
|
|
573
606
|
selected: { bg: THEME.selectedBg, fg: THEME.selectedFg },
|
|
574
607
|
border: { fg: THEME.panelBorder }
|
|
575
608
|
}
|
|
@@ -581,7 +614,8 @@ export class Tui {
|
|
|
581
614
|
right: 2,
|
|
582
615
|
height: 1,
|
|
583
616
|
tags: true,
|
|
584
|
-
|
|
617
|
+
style: { fg: THEME.foreground, bg: THEME.background },
|
|
618
|
+
content: muted("Type to filter Up/Down select Enter run Esc cancel")
|
|
585
619
|
});
|
|
586
620
|
let entries = [];
|
|
587
621
|
let selectedIndex = 0;
|
|
@@ -608,7 +642,7 @@ export class Tui {
|
|
|
608
642
|
selectedIndex = clamp(selectedIndex, 0, Math.max(0, entries.length - 1));
|
|
609
643
|
box.setLabel(` Commands${trimmedQuery ? `: ${truncateText(trimmedQuery, 28)}` : ""} `);
|
|
610
644
|
if (entries.length === 0) {
|
|
611
|
-
list.setItems(["
|
|
645
|
+
list.setItems([muted("No matching commands")]);
|
|
612
646
|
list.select(0);
|
|
613
647
|
this.screen.render();
|
|
614
648
|
return;
|
|
@@ -697,6 +731,8 @@ export class Tui {
|
|
|
697
731
|
alwaysScroll: false,
|
|
698
732
|
padding: { left: 2, right: 2 },
|
|
699
733
|
style: {
|
|
734
|
+
fg: THEME.foreground,
|
|
735
|
+
bg: THEME.background,
|
|
700
736
|
border: { fg: THEME.panelBorder }
|
|
701
737
|
},
|
|
702
738
|
scrollbar: {
|
|
@@ -720,7 +756,7 @@ export class Tui {
|
|
|
720
756
|
` id=${this.station.config.chargePointId}`,
|
|
721
757
|
` url=${this.station.config.centralSystemUrl}`,
|
|
722
758
|
"",
|
|
723
|
-
"
|
|
759
|
+
muted("Esc or q closes this help.")
|
|
724
760
|
].join("\n"));
|
|
725
761
|
const close = () => {
|
|
726
762
|
box.destroy();
|
|
@@ -738,9 +774,8 @@ export class Tui {
|
|
|
738
774
|
paletteEntryLine(entry) {
|
|
739
775
|
const tab = this.tabLabel(entry.tab).padEnd(11);
|
|
740
776
|
const key = entry.action.key.padEnd(2);
|
|
741
|
-
const
|
|
742
|
-
|
|
743
|
-
return `${state}${tab} ${key} ${entry.action.label}${endState}`;
|
|
777
|
+
const line = `${tab} ${key} ${entry.action.label}`;
|
|
778
|
+
return entry.action.disabled?.() ? disabledLine(line) : line;
|
|
744
779
|
}
|
|
745
780
|
paletteEntries(query) {
|
|
746
781
|
const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
@@ -798,6 +833,9 @@ export class Tui {
|
|
|
798
833
|
stopTransactionFlow() {
|
|
799
834
|
this.promptChoice("StopTransaction reason", STOP_REASONS, (reason) => this.runStationTask(() => this.station.stopTransaction(this.selectedConnectorId, readStopReason(reason), this.selectedConnector()?.lastIdTag)));
|
|
800
835
|
}
|
|
836
|
+
setStatusFlow() {
|
|
837
|
+
this.promptChoice("Connector status", CONNECTOR_STATUS_SEQUENCE, (status) => this.runStationTask(() => this.station.setStatus(this.selectedConnectorId, readConnectorStatus(status))));
|
|
838
|
+
}
|
|
801
839
|
startTransactionFlow() {
|
|
802
840
|
this.promptForm("StartTransaction", [
|
|
803
841
|
{ name: "connectorId", label: "connectorId", initialValue: String(this.selectedConnectorId) },
|
|
@@ -956,6 +994,8 @@ export class Tui {
|
|
|
956
994
|
tags: true,
|
|
957
995
|
keys: true,
|
|
958
996
|
style: {
|
|
997
|
+
fg: THEME.foreground,
|
|
998
|
+
bg: THEME.background,
|
|
959
999
|
border: { fg: THEME.panelBorder }
|
|
960
1000
|
}
|
|
961
1001
|
});
|
|
@@ -972,6 +1012,8 @@ export class Tui {
|
|
|
972
1012
|
keys: true,
|
|
973
1013
|
tags: true,
|
|
974
1014
|
style: {
|
|
1015
|
+
fg: THEME.foreground,
|
|
1016
|
+
bg: THEME.background,
|
|
975
1017
|
border: { fg: THEME.panelBorder }
|
|
976
1018
|
}
|
|
977
1019
|
}));
|
|
@@ -982,7 +1024,8 @@ export class Tui {
|
|
|
982
1024
|
right: 2,
|
|
983
1025
|
height: 1,
|
|
984
1026
|
tags: true,
|
|
985
|
-
|
|
1027
|
+
style: { fg: THEME.foreground, bg: THEME.background },
|
|
1028
|
+
content: muted("Tab/Up/Down field Left/Right cursor C-u clear Enter submit Esc cancel")
|
|
986
1029
|
});
|
|
987
1030
|
let focusedIndex = 0;
|
|
988
1031
|
let closed = false;
|
|
@@ -995,7 +1038,7 @@ export class Tui {
|
|
|
995
1038
|
const value = values[index] ?? "";
|
|
996
1039
|
const cursor = clamp(cursors[index] ?? value.length, 0, value.length);
|
|
997
1040
|
input.setContent(renderFieldValue(value, cursor, width, focusedIndex === index));
|
|
998
|
-
input.style.border = { fg: focusedIndex === index ?
|
|
1041
|
+
input.style.border = { fg: focusedIndex === index ? THEME.focusBorder : THEME.panelBorder };
|
|
999
1042
|
};
|
|
1000
1043
|
const renderInputs = () => {
|
|
1001
1044
|
inputBoxes.forEach((_input, index) => renderInput(index));
|
|
@@ -1128,6 +1171,8 @@ export class Tui {
|
|
|
1128
1171
|
vi: true,
|
|
1129
1172
|
items: choices.map((choice) => ` ${choice}`),
|
|
1130
1173
|
style: {
|
|
1174
|
+
fg: THEME.foreground,
|
|
1175
|
+
bg: THEME.background,
|
|
1131
1176
|
selected: { bg: THEME.selectedBg, fg: THEME.selectedFg },
|
|
1132
1177
|
border: { fg: THEME.panelBorder }
|
|
1133
1178
|
}
|
|
@@ -1205,12 +1250,12 @@ export class Tui {
|
|
|
1205
1250
|
alwaysScroll: false,
|
|
1206
1251
|
content: this.plainLogContent(),
|
|
1207
1252
|
style: {
|
|
1208
|
-
fg:
|
|
1209
|
-
bg:
|
|
1253
|
+
fg: THEME.foreground,
|
|
1254
|
+
bg: THEME.background
|
|
1210
1255
|
},
|
|
1211
1256
|
scrollbar: {
|
|
1212
1257
|
ch: " ",
|
|
1213
|
-
style: { bg:
|
|
1258
|
+
style: { bg: THEME.panelBorder }
|
|
1214
1259
|
}
|
|
1215
1260
|
});
|
|
1216
1261
|
this.bindLogNavigationKeys(this.plainLogView);
|
|
@@ -1275,7 +1320,7 @@ export class Tui {
|
|
|
1275
1320
|
appendLogEntry(entry) {
|
|
1276
1321
|
this.logBox.log(this.formatLogEntryForTui(entry));
|
|
1277
1322
|
if (entry.details) {
|
|
1278
|
-
this.logBox.log(`
|
|
1323
|
+
this.logBox.log(` ${muted(entry.details)}`);
|
|
1279
1324
|
}
|
|
1280
1325
|
}
|
|
1281
1326
|
filteredLogs() {
|
|
@@ -1432,6 +1477,10 @@ function readStopReason(value) {
|
|
|
1432
1477
|
const normalized = value.trim();
|
|
1433
1478
|
return isStopReason(normalized) ? normalized : "Local";
|
|
1434
1479
|
}
|
|
1480
|
+
function readConnectorStatus(value) {
|
|
1481
|
+
const normalized = value.trim();
|
|
1482
|
+
return isConnectorStatus(normalized) ? normalized : "Available";
|
|
1483
|
+
}
|
|
1435
1484
|
function parseObjectJson(value) {
|
|
1436
1485
|
try {
|
|
1437
1486
|
const parsed = JSON.parse(value);
|
|
@@ -1503,6 +1552,9 @@ function normalizeActionKey(keyName) {
|
|
|
1503
1552
|
if (keyName === "S-d") {
|
|
1504
1553
|
return "D";
|
|
1505
1554
|
}
|
|
1555
|
+
if (keyName === "S-s") {
|
|
1556
|
+
return "S";
|
|
1557
|
+
}
|
|
1506
1558
|
if (keyName === "S-u") {
|
|
1507
1559
|
return "U";
|
|
1508
1560
|
}
|
|
@@ -1515,7 +1567,7 @@ function legacyActionTab(key) {
|
|
|
1515
1567
|
if (["b", "h", "x"].includes(key)) {
|
|
1516
1568
|
return "station";
|
|
1517
1569
|
}
|
|
1518
|
-
if (["p", "s", "n", "f"].includes(key)) {
|
|
1570
|
+
if (["p", "s", "S", "n", "f"].includes(key)) {
|
|
1519
1571
|
return "connector";
|
|
1520
1572
|
}
|
|
1521
1573
|
if (["a", "t", "e", "m", "M"].includes(key)) {
|
|
@@ -1554,6 +1606,9 @@ const STOP_REASONS = [
|
|
|
1554
1606
|
function isStopReason(value) {
|
|
1555
1607
|
return STOP_REASONS.includes(value);
|
|
1556
1608
|
}
|
|
1609
|
+
function isConnectorStatus(value) {
|
|
1610
|
+
return CONNECTOR_STATUS_SEQUENCE.includes(value);
|
|
1611
|
+
}
|
|
1557
1612
|
const CHARGE_POINT_ERROR_CODES = [
|
|
1558
1613
|
"ConnectorLockFailure",
|
|
1559
1614
|
"EVCommunicationError",
|