]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commitdiff
feat(ui-cli): add registration and connector columns to station list, fix command...
authorJérôme Benoit <jerome.benoit@sap.com>
Fri, 17 Apr 2026 22:33:34 +0000 (00:33 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Fri, 17 Apr 2026 22:33:34 +0000 (00:33 +0200)
Station list improvements:
- Add Registration column (Accepted/Pending/Rejected) after Hash ID
- Add Connectors column with per-connector status letters (1:A 2:O etc.)
- Two-letter abbreviations for ambiguous statuses (Fi/F, SE/SS)
- Remove dead countConnectors function, replace with formatConnectors
- Replace countConnectors tests with formatConnectors tests (7 tests)

Command description semantics:
- All OCPP: 'Send OCPP X' -> 'Request station(s) to send OCPP X'
- Connection: 'Open/Close WebSocket connection' -> '... to CSMS on station(s)'
- Connector: 'Lock/Unlock a connector' -> '... connector on station(s)'
- Transaction: 'Start/Stop a transaction' -> '... on station(s)'
- SKILL.md: 'send OCPP messages' -> 'trigger OCPP messages from stations to the CSMS'
- README: updated accordingly

ui/cli/README.md
ui/cli/skills/evse-simulator/SKILL.md
ui/cli/src/commands/connection.ts
ui/cli/src/commands/connector.ts
ui/cli/src/commands/ocpp.ts
ui/cli/src/commands/transaction.ts
ui/cli/src/output/format.ts
ui/cli/src/output/renderers.ts
ui/cli/tests/format.test.ts

index 2ff767b2eae4bcb4e21831fb2be0375d77c80e91..cd180d5e8bf396d80ae03a675068255b6515d3c9 100644 (file)
@@ -201,15 +201,15 @@ evse-cli transaction stop --transaction-id <id> [hashId...]
 
 #### ocpp
 
-Send OCPP commands directly to charging stations:
+Request charging station(s) to send OCPP messages to the CSMS:
 
 ```shell
-evse-cli ocpp heartbeat [hashId...]
-evse-cli ocpp authorize --id-tag <tag> [hashId...]
-evse-cli ocpp boot-notification [hashId...]
-evse-cli ocpp status-notification --connector-id <id> --error-code <code> --status <status> [hashId...]
-evse-cli ocpp meter-values --connector-id <id> [hashId...]
-evse-cli ocpp data-transfer --vendor-id <id> [--message-id <id>] [--data <json>] [hashId...]
+evse-cli ocpp heartbeat [hashId...]                                                               # Heartbeat
+evse-cli ocpp authorize --id-tag <tag> [hashId...]                                                # Authorize
+evse-cli ocpp boot-notification [hashId...]                                                       # BootNotification
+evse-cli ocpp status-notification --connector-id <id> --error-code <code> --status <status> [hashId...]  # StatusNotification
+evse-cli ocpp meter-values --connector-id <id> [hashId...]                                        # MeterValues
+evse-cli ocpp data-transfer --vendor-id <id> [--message-id <id>] [--data <json>] [hashId...]      # DataTransfer
 ```
 
 Other OCPP commands (no extra options): `diagnostics-status-notification`, `firmware-status-notification`, `get-15118-ev-certificate`, `get-certificate-status`, `log-status-notification`, `notify-customer-information`, `notify-report`, `security-event-notification`, `sign-certificate`, `transaction-event`.
index ec457048aa39021b2094341f23037712800fe31c..af5af7612ffed5e2b6cb80815b8e5c10ee8e3617 100644 (file)
@@ -1,6 +1,6 @@
 ---
 name: evse-simulator
-description: Control and monitor an OCPP charging station simulator via CLI. Use when users ask to manage charging stations, send OCPP messages, start/stop the simulator or ATG, list stations or templates, or check simulator state.
+description: Control and monitor an OCPP charging station simulator via CLI. Use when users ask to manage charging stations, trigger OCPP messages from stations to the CSMS, start/stop the simulator or ATG, list stations or templates, or check simulator state.
 license: Apache-2.0
 compatibility: Requires the simulator UI server to be running with WebSocket enabled.
 metadata:
@@ -105,6 +105,8 @@ evse-cli transaction stop --transaction-id <id> [hashId...]
 
 ### OCPP Messages
 
+Request station(s) to send OCPP messages to the CSMS:
+
 ```shell
 evse-cli ocpp heartbeat [hashId...]
 evse-cli ocpp boot-notification [hashId...]
@@ -162,7 +164,7 @@ evse-cli station add -t keba-ocpp2.station-template -n 3 --auto-start
 evse-cli atg start
 ```
 
-### Send OCPP heartbeat to all stations
+### Request all stations to send OCPP Heartbeat
 
 ```shell
 evse-cli ocpp heartbeat
index fcf6c5386093caec45dee58ae76a9da3c0cce6cb..b9ccd9a9e7ec31012103d22931d73b7e00e0ccf9 100644 (file)
@@ -9,7 +9,7 @@ export const createConnectionCommands = (program: Command): Command => {
 
   cmd
     .command('open [hashIds...]')
-    .description('Open WebSocket connection')
+    .description('Open WebSocket connection to CSMS on station(s)')
     .action(async (hashIds: string[]) => {
       const payload: RequestPayload = buildHashIdsPayload(hashIds)
       await runAction(program, ProcedureName.OPEN_CONNECTION, payload)
@@ -17,7 +17,7 @@ export const createConnectionCommands = (program: Command): Command => {
 
   cmd
     .command('close [hashIds...]')
-    .description('Close WebSocket connection')
+    .description('Close WebSocket connection to CSMS on station(s)')
     .action(async (hashIds: string[]) => {
       const payload: RequestPayload = buildHashIdsPayload(hashIds)
       await runAction(program, ProcedureName.CLOSE_CONNECTION, payload)
index 25ffadf9a0b4bfc2152874771aa3a4aa355f2301..17f41e0e7afe87d4a5c819177a81370b3b73289d 100644 (file)
@@ -9,7 +9,7 @@ export const createConnectorCommands = (program: Command): Command => {
 
   cmd
     .command('lock [hashIds...]')
-    .description('Lock a connector')
+    .description('Lock connector on station(s)')
     .requiredOption('--connector-id <id>', 'connector ID', parseInteger)
     .action(async (hashIds: string[], options: { connectorId: number }) => {
       const payload: RequestPayload = {
@@ -21,7 +21,7 @@ export const createConnectorCommands = (program: Command): Command => {
 
   cmd
     .command('unlock [hashIds...]')
-    .description('Unlock a connector')
+    .description('Unlock connector on station(s)')
     .requiredOption('--connector-id <id>', 'connector ID', parseInteger)
     .action(async (hashIds: string[], options: { connectorId: number }) => {
       const payload: RequestPayload = {
index 4f46731af1982cee6b745273ad5640a8b0d30598..17ea825ac22abd50d6ef0a828aa6e35121a0b235 100644 (file)
@@ -9,7 +9,7 @@ export const createOcppCommands = (program: Command): Command => {
 
   cmd
     .command('authorize [hashIds...]')
-    .description('Send OCPP Authorize')
+    .description('Request station(s) to send OCPP Authorize')
     .requiredOption('--id-tag <tag>', 'RFID tag for authorization')
     .action(async (hashIds: string[], options: { idTag: string }) => {
       const payload: RequestPayload = {
@@ -21,7 +21,7 @@ export const createOcppCommands = (program: Command): Command => {
 
   cmd
     .command('data-transfer [hashIds...]')
-    .description('Send OCPP DataTransfer')
+    .description('Request station(s) to send OCPP DataTransfer')
     .option('--vendor-id <id>', 'vendor identifier')
     .option('--message-id <id>', 'message identifier')
     .option('--data <json>', 'data payload (JSON string)')
@@ -44,7 +44,7 @@ export const createOcppCommands = (program: Command): Command => {
 
   cmd
     .command('meter-values [hashIds...]')
-    .description('Send OCPP MeterValues')
+    .description('Request station(s) to send OCPP MeterValues')
     .requiredOption('--connector-id <id>', 'connector ID', parseInteger)
     .action(async (hashIds: string[], options: { connectorId: number }) => {
       const payload: RequestPayload = {
@@ -56,7 +56,7 @@ export const createOcppCommands = (program: Command): Command => {
 
   cmd
     .command('status-notification [hashIds...]')
-    .description('Send OCPP StatusNotification')
+    .description('Request station(s) to send OCPP StatusNotification')
     .requiredOption('--connector-id <id>', 'connector ID', parseInteger)
     .requiredOption('--error-code <code>', 'connector error code')
     .requiredOption('--status <status>', 'connector status')
@@ -76,46 +76,58 @@ export const createOcppCommands = (program: Command): Command => {
     )
 
   const simpleOcppCommands: [string, string, ProcedureName][] = [
-    ['boot-notification', 'Send OCPP BootNotification', ProcedureName.BOOT_NOTIFICATION],
+    [
+      'boot-notification',
+      'Request station(s) to send OCPP BootNotification',
+      ProcedureName.BOOT_NOTIFICATION,
+    ],
     [
       'diagnostics-status-notification',
-      'Send OCPP DiagnosticsStatusNotification',
+      'Request station(s) to send OCPP DiagnosticsStatusNotification',
       ProcedureName.DIAGNOSTICS_STATUS_NOTIFICATION,
     ],
     [
       'firmware-status-notification',
-      'Send OCPP FirmwareStatusNotification',
+      'Request station(s) to send OCPP FirmwareStatusNotification',
       ProcedureName.FIRMWARE_STATUS_NOTIFICATION,
     ],
     [
       'get-15118-ev-certificate',
-      'Send OCPP Get15118EVCertificate',
+      'Request station(s) to send OCPP Get15118EVCertificate',
       ProcedureName.GET_15118_EV_CERTIFICATE,
     ],
     [
       'get-certificate-status',
-      'Send OCPP GetCertificateStatus',
+      'Request station(s) to send OCPP GetCertificateStatus',
       ProcedureName.GET_CERTIFICATE_STATUS,
     ],
-    ['heartbeat', 'Send OCPP Heartbeat', ProcedureName.HEARTBEAT],
+    ['heartbeat', 'Request station(s) to send OCPP Heartbeat', ProcedureName.HEARTBEAT],
     [
       'log-status-notification',
-      'Send OCPP LogStatusNotification',
+      'Request station(s) to send OCPP LogStatusNotification',
       ProcedureName.LOG_STATUS_NOTIFICATION,
     ],
     [
       'notify-customer-information',
-      'Send OCPP NotifyCustomerInformation',
+      'Request station(s) to send OCPP NotifyCustomerInformation',
       ProcedureName.NOTIFY_CUSTOMER_INFORMATION,
     ],
-    ['notify-report', 'Send OCPP NotifyReport', ProcedureName.NOTIFY_REPORT],
+    ['notify-report', 'Request station(s) to send OCPP NotifyReport', ProcedureName.NOTIFY_REPORT],
     [
       'security-event-notification',
-      'Send OCPP SecurityEventNotification',
+      'Request station(s) to send OCPP SecurityEventNotification',
       ProcedureName.SECURITY_EVENT_NOTIFICATION,
     ],
-    ['sign-certificate', 'Send OCPP SignCertificate', ProcedureName.SIGN_CERTIFICATE],
-    ['transaction-event', 'Send OCPP TransactionEvent', ProcedureName.TRANSACTION_EVENT],
+    [
+      'sign-certificate',
+      'Request station(s) to send OCPP SignCertificate',
+      ProcedureName.SIGN_CERTIFICATE,
+    ],
+    [
+      'transaction-event',
+      'Request station(s) to send OCPP TransactionEvent',
+      ProcedureName.TRANSACTION_EVENT,
+    ],
   ]
 
   for (const [name, description, procedureName] of simpleOcppCommands) {
index 59bfdae0b706ed77d9614109f080a9e49f9e2af4..ec9fe99249e5896552be4b513108eebe3e49958d 100644 (file)
@@ -9,7 +9,7 @@ export const createTransactionCommands = (program: Command): Command => {
 
   cmd
     .command('start [hashIds...]')
-    .description('Start a transaction')
+    .description('Start a transaction on station(s)')
     .requiredOption('--connector-id <id>', 'connector ID', parseInteger)
     .requiredOption('--id-tag <tag>', 'RFID tag for authorization')
     .action(async (hashIds: string[], options: { connectorId: number; idTag: string }) => {
@@ -23,7 +23,7 @@ export const createTransactionCommands = (program: Command): Command => {
 
   cmd
     .command('stop [hashIds...]')
-    .description('Stop a transaction')
+    .description('Stop a transaction on station(s)')
     .requiredOption('--transaction-id <id>', 'transaction ID', parseInteger)
     .action(async (hashIds: string[], options: { transactionId: number }) => {
       const payload: RequestPayload = {
index b3a057f23cf809e0fcd8ebbd98047ad823ef6291..b7bcf229557c8b59a6c23191ea70971879124618 100644 (file)
@@ -1,6 +1,6 @@
 import chalk from 'chalk'
 import Table from 'cli-table3'
-import { type ConnectorEntry, type EvseEntry, OCPP16ChargePointStatus } from 'ui-common'
+import { type ConnectorEntry, type EvseEntry } from 'ui-common'
 
 const NO_BORDER = {
   bottom: '',
@@ -49,22 +49,26 @@ export const wsIcon = (wsState: number | undefined): string => {
   }
 }
 
-export const countConnectors = (
-  evses: EvseEntry[],
-  connectors: ConnectorEntry[]
-): { available: number; total: number } => {
-  let total = 0
-  let available = 0
+const STATUS_ABBREVIATIONS: Record<string, string> = {
+  Finishing: 'Fi',
+  SuspendedEV: 'SE',
+  SuspendedEVSE: 'SS',
+}
+
+const statusLetter = (status: string | undefined): string => {
+  if (status == null || status.length === 0) return '?'
+  return STATUS_ABBREVIATIONS[status] ?? status.charAt(0).toUpperCase()
+}
+
+export const formatConnectors = (evses: EvseEntry[], connectors: ConnectorEntry[]): string => {
+  const entries: string[] = []
 
   if (evses.length > 0) {
     for (const evse of evses) {
       if (evse.evseId > 0) {
         for (const c of evse.evseStatus.connectors) {
           if (c.connectorId > 0) {
-            total++
-            if (c.connectorStatus.status === OCPP16ChargePointStatus.AVAILABLE) {
-              available++
-            }
+            entries.push(`${c.connectorId.toString()}:${statusLetter(c.connectorStatus.status)}`)
           }
         }
       }
@@ -72,15 +76,12 @@ export const countConnectors = (
   } else {
     for (const c of connectors) {
       if (c.connectorId > 0) {
-        total++
-        if (c.connectorStatus.status === OCPP16ChargePointStatus.AVAILABLE) {
-          available++
-        }
+        entries.push(`${c.connectorId.toString()}:${statusLetter(c.connectorStatus.status)}`)
       }
     }
   }
 
-  return { available, total }
+  return entries.length > 0 ? chalk.dim(entries.join(' ')) : chalk.dim('–')
 }
 
 export const fuzzyTime = (ts: number | undefined): string => {
index 982046b05f246d3280d5d0f36c830c68d088ec90..5755ebfe278187372f95718eeaaa50ce93da279d 100644 (file)
@@ -7,7 +7,7 @@ import type { StationListPayload } from '../types.js'
 
 import {
   borderlessTable,
-  countConnectors,
+  formatConnectors,
   fuzzyTime,
   statusIcon,
   truncateId,
@@ -127,15 +127,27 @@ const renderStationList = (payload: StationListPayload): void => {
     return
   }
 
-  const table = borderlessTable(['', 'Name', 'Hash ID', 'WS', 'OCPP', 'Template', 'Updated'])
+  const table = borderlessTable([
+    '',
+    'Name',
+    'Hash ID',
+    'Reg',
+    'WS',
+    'Connectors',
+    'OCPP',
+    'Template',
+    'Updated',
+  ])
   for (const cs of stations) {
     const si = cs.stationInfo
-    const { available, total } = countConnectors(cs.evses ?? [], cs.connectors ?? [])
+    const reg = cs.bootNotificationResponse?.status
     table.push([
       statusIcon(cs.started),
       si.chargingStationId,
       chalk.dim(truncateId(si.hashId)),
-      `${wsIcon(cs.wsState)} ${chalk.dim(`${available.toString()}/${total.toString()}`)}`,
+      reg ?? chalk.dim('–'),
+      wsIcon(cs.wsState),
+      formatConnectors(cs.evses ?? [], cs.connectors ?? []),
       chalk.dim(si.ocppVersion ?? '–'),
       chalk.dim(si.templateName.replace('.station-template', '')),
       fuzzyTime(cs.timestamp),
index 2212e0073caaba68df551a065177b09c84af2835..9508eadd084ebcbd4af884ee943ecd0db161ae78 100644 (file)
@@ -3,7 +3,13 @@ import assert from 'node:assert'
 import { describe, it } from 'node:test'
 import { OCPP16AvailabilityType, OCPP16ChargePointStatus } from 'ui-common'
 
-import { countConnectors, fuzzyTime, statusIcon, truncateId, wsIcon } from '../src/output/format.js'
+import {
+  formatConnectors,
+  fuzzyTime,
+  statusIcon,
+  truncateId,
+  wsIcon,
+} from '../src/output/format.js'
 
 await describe('format helpers', async () => {
   await describe('truncateId', async () => {
@@ -90,183 +96,178 @@ await describe('format helpers', async () => {
     })
   })
 
-  await describe('countConnectors', async () => {
-    await it('counts from evses when evses present (evseId > 0, connectorId > 0)', () => {
-      const evses = [
-        {
-          evseId: 1,
-          evseStatus: {
-            availability: OCPP16AvailabilityType.OPERATIVE,
-            connectors: [
-              {
-                connectorId: 1,
-                connectorStatus: {
-                  availability: OCPP16AvailabilityType.OPERATIVE,
-                  status: OCPP16ChargePointStatus.AVAILABLE,
+  await describe('formatConnectors', async () => {
+    await it('formats connectors from evses', () => {
+      const result = formatConnectors(
+        [
+          {
+            evseId: 1,
+            evseStatus: {
+              availability: OCPP16AvailabilityType.OPERATIVE,
+              connectors: [
+                {
+                  connectorId: 1,
+                  connectorStatus: {
+                    availability: OCPP16AvailabilityType.OPERATIVE,
+                    status: OCPP16ChargePointStatus.AVAILABLE,
+                  },
                 },
-              },
-              {
-                connectorId: 2,
-                connectorStatus: {
-                  availability: OCPP16AvailabilityType.OPERATIVE,
-                  status: OCPP16ChargePointStatus.CHARGING,
+                {
+                  connectorId: 2,
+                  connectorStatus: {
+                    availability: OCPP16AvailabilityType.OPERATIVE,
+                    status: OCPP16ChargePointStatus.OCCUPIED,
+                  },
                 },
-              },
-            ],
+              ],
+            },
           },
-        },
-      ]
-      const result = countConnectors(evses, [])
-      assert.deepStrictEqual(result, { available: 1, total: 2 })
+        ],
+        []
+      )
+      assert.ok(result.includes('1:A'))
+      assert.ok(result.includes('2:O'))
     })
 
     await it('skips evseId 0', () => {
-      const evses = [
-        {
-          evseId: 0,
-          evseStatus: {
-            availability: OCPP16AvailabilityType.OPERATIVE,
-            connectors: [
-              {
-                connectorId: 1,
-                connectorStatus: {
-                  availability: OCPP16AvailabilityType.OPERATIVE,
-                  status: OCPP16ChargePointStatus.AVAILABLE,
+      const result = formatConnectors(
+        [
+          {
+            evseId: 0,
+            evseStatus: {
+              availability: OCPP16AvailabilityType.OPERATIVE,
+              connectors: [
+                {
+                  connectorId: 1,
+                  connectorStatus: {
+                    availability: OCPP16AvailabilityType.OPERATIVE,
+                    status: OCPP16ChargePointStatus.AVAILABLE,
+                  },
                 },
-              },
-            ],
+              ],
+            },
           },
-        },
-        {
-          evseId: 1,
-          evseStatus: {
-            availability: OCPP16AvailabilityType.OPERATIVE,
-            connectors: [
-              {
-                connectorId: 1,
-                connectorStatus: {
-                  availability: OCPP16AvailabilityType.OPERATIVE,
-                  status: OCPP16ChargePointStatus.AVAILABLE,
-                },
-              },
-            ],
-          },
-        },
-      ]
-      const result = countConnectors(evses, [])
-      assert.deepStrictEqual(result, { available: 1, total: 1 })
+        ],
+        []
+      )
+      assert.ok(result.includes('–'))
     })
 
-    await it('skips connectorId 0 within evses', () => {
-      const evses = [
-        {
-          evseId: 1,
-          evseStatus: {
-            availability: OCPP16AvailabilityType.OPERATIVE,
-            connectors: [
-              {
-                connectorId: 0,
-                connectorStatus: {
-                  availability: OCPP16AvailabilityType.OPERATIVE,
-                  status: OCPP16ChargePointStatus.AVAILABLE,
+    await it('skips connectorId 0', () => {
+      const result = formatConnectors(
+        [
+          {
+            evseId: 1,
+            evseStatus: {
+              availability: OCPP16AvailabilityType.OPERATIVE,
+              connectors: [
+                {
+                  connectorId: 0,
+                  connectorStatus: {
+                    availability: OCPP16AvailabilityType.OPERATIVE,
+                    status: OCPP16ChargePointStatus.AVAILABLE,
+                  },
                 },
-              },
-              {
-                connectorId: 1,
-                connectorStatus: {
-                  availability: OCPP16AvailabilityType.OPERATIVE,
-                  status: OCPP16ChargePointStatus.AVAILABLE,
+                {
+                  connectorId: 1,
+                  connectorStatus: {
+                    availability: OCPP16AvailabilityType.OPERATIVE,
+                    status: OCPP16ChargePointStatus.AVAILABLE,
+                  },
                 },
-              },
-            ],
+              ],
+            },
           },
-        },
-      ]
-      const result = countConnectors(evses, [])
-      assert.deepStrictEqual(result, { available: 1, total: 1 })
-    })
-
-    await it('counts available connectors (status === AVAILABLE)', () => {
-      const evses = [
-        {
-          evseId: 1,
-          evseStatus: {
-            availability: OCPP16AvailabilityType.OPERATIVE,
-            connectors: [
-              {
-                connectorId: 1,
-                connectorStatus: {
-                  availability: OCPP16AvailabilityType.OPERATIVE,
-                  status: OCPP16ChargePointStatus.AVAILABLE,
-                },
-              },
-              {
-                connectorId: 2,
-                connectorStatus: {
-                  availability: OCPP16AvailabilityType.OPERATIVE,
-                  status: OCPP16ChargePointStatus.UNAVAILABLE,
-                },
-              },
-              {
-                connectorId: 3,
-                connectorStatus: {
-                  availability: OCPP16AvailabilityType.OPERATIVE,
-                  status: OCPP16ChargePointStatus.FAULTED,
-                },
-              },
-            ],
-          },
-        },
-      ]
-      const result = countConnectors(evses, [])
-      assert.deepStrictEqual(result, { available: 1, total: 3 })
+        ],
+        []
+      )
+      assert.ok(result.includes('1:A'))
+      assert.ok(!result.includes('0:'))
     })
 
     await it('falls back to connectors array when evses empty', () => {
-      const connectors = [
-        {
-          connectorId: 1,
-          connectorStatus: {
-            availability: OCPP16AvailabilityType.OPERATIVE,
-            status: OCPP16ChargePointStatus.AVAILABLE,
+      const result = formatConnectors(
+        [],
+        [
+          {
+            connectorId: 1,
+            connectorStatus: {
+              availability: OCPP16AvailabilityType.OPERATIVE,
+              status: OCPP16ChargePointStatus.AVAILABLE,
+            },
           },
-        },
-        {
-          connectorId: 2,
-          connectorStatus: {
-            availability: OCPP16AvailabilityType.OPERATIVE,
-            status: OCPP16ChargePointStatus.CHARGING,
+          {
+            connectorId: 2,
+            connectorStatus: {
+              availability: OCPP16AvailabilityType.OPERATIVE,
+              status: OCPP16ChargePointStatus.CHARGING,
+            },
           },
-        },
-      ]
-      const result = countConnectors([], connectors)
-      assert.deepStrictEqual(result, { available: 1, total: 2 })
+        ]
+      )
+      assert.ok(result.includes('1:A'))
+      assert.ok(result.includes('2:C'))
+    })
+
+    await it('returns dash for empty arrays', () => {
+      const result = formatConnectors([], [])
+      assert.ok(result.includes('–'))
     })
 
-    await it('skips connectorId 0 in connectors fallback', () => {
-      const connectors = [
-        {
-          connectorId: 0,
-          connectorStatus: {
-            availability: OCPP16AvailabilityType.OPERATIVE,
-            status: OCPP16ChargePointStatus.AVAILABLE,
+    await it('uses two-letter abbreviations for ambiguous statuses', () => {
+      const result = formatConnectors(
+        [],
+        [
+          {
+            connectorId: 1,
+            connectorStatus: {
+              availability: OCPP16AvailabilityType.OPERATIVE,
+              status: OCPP16ChargePointStatus.SUSPENDED_EV,
+            },
           },
-        },
-        {
-          connectorId: 1,
-          connectorStatus: {
-            availability: OCPP16AvailabilityType.OPERATIVE,
-            status: OCPP16ChargePointStatus.AVAILABLE,
+          {
+            connectorId: 2,
+            connectorStatus: {
+              availability: OCPP16AvailabilityType.OPERATIVE,
+              status: OCPP16ChargePointStatus.SUSPENDED_EVSE,
+            },
           },
-        },
-      ]
-      const result = countConnectors([], connectors)
-      assert.deepStrictEqual(result, { available: 1, total: 1 })
+          {
+            connectorId: 3,
+            connectorStatus: {
+              availability: OCPP16AvailabilityType.OPERATIVE,
+              status: OCPP16ChargePointStatus.FINISHING,
+            },
+          },
+          {
+            connectorId: 4,
+            connectorStatus: {
+              availability: OCPP16AvailabilityType.OPERATIVE,
+              status: OCPP16ChargePointStatus.FAULTED,
+            },
+          },
+        ]
+      )
+      assert.ok(result.includes('1:SE'))
+      assert.ok(result.includes('2:SS'))
+      assert.ok(result.includes('3:Fi'))
+      assert.ok(result.includes('4:F'))
     })
 
-    await it('returns {available: 0, total: 0} for empty arrays', () => {
-      const result = countConnectors([], [])
-      assert.deepStrictEqual(result, { available: 0, total: 0 })
+    await it('handles undefined status', () => {
+      const result = formatConnectors(
+        [],
+        [
+          {
+            connectorId: 1,
+            connectorStatus: {
+              availability: OCPP16AvailabilityType.OPERATIVE,
+              status: undefined as unknown as OCPP16ChargePointStatus,
+            },
+          },
+        ]
+      )
+      assert.ok(result.includes('1:?'))
     })
   })