{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "CoreCharge cabinet protocol v1.51 command catalog",
  "version": "1.51",
  "sourceRevisionDate": "2025-07-30",
  "publication": {
    "containsProductionData": false,
    "commandSemanticsPublic": true,
    "credentialsIncluded": false,
    "productionEndpointsIncluded": false,
    "examplesAreSynthetic": true
  },
  "wire": {
    "encoding": "ASCII",
    "delimiter": ",",
    "start": "{",
    "end": "}",
    "crc": "CRC-16/MODBUS over command and data fields plus comma plus first six PW characters, right-padded with 0",
    "crcFormat": "four uppercase hexadecimal characters"
  },
  "sharedTypes": {
    "messageId": "monotonically increasing; Unix timestamp recommended",
    "deviceId": "string, max 20 bytes",
    "orderId": "string, max 32 characters",
    "powerBankId": "string, max 15 characters baseline; NI profile may accept 16",
    "slotId": "one-based positive integer; zero has command-specific semantics",
    "battery": "integer 0..100",
    "fastCharge": {
      "0": "normal",
      "1": "22 W",
      "2": "unknown",
      "3": "45 W",
      "4": "65 W"
    }
  },
  "commands": [
    {
      "id": "CN",
      "category": "identity",
      "uplink": [
        ["CN", "deviceId", "address", "firmware", "rssi", "info1", "info2?", "info3?", "info4?", "crc"]
      ],
      "downlink": [
        ["CN", "confirmedDeviceId", "settings", "crc"]
      ],
      "notes": [
        "2G/4G maps address/info1/info2/info3/info4 to IMEI/ICCID/LAC/CI/VPN.",
        "Wi-Fi maps address/info1/info2/info3/info4 to MAC/IP/SSID/password/reserved.",
        "Device retries every 60 seconds until acknowledged.",
        "settings uses KEY:VALUE; entries and may include DOMAIN, PORT, HB, BP, UN, PW, ID, DM and TP."
      ]
    },
    {
      "id": "CS",
      "category": "configuration",
      "privileged": true,
      "downlink": [
        ["CS", "messageId", "operation", "param1", "param2", "crc"]
      ],
      "uplink": [
        ["CS", "sameMessageId", "operation", "result", "reserved", "crc"],
        ["CS", "sameMessageId", "currentProductKey", "inventoryInterval", "mqttHeartbeat", "crc"]
      ],
      "operations": {
        "RESETME": "restart cabinet",
        "RESETNET": "restart network module",
        "RECONNECT": "reconnect server",
        "HEARTINFO": "set inventory and MQTT heartbeat intervals",
        "LEDCFG": "set ambience and QR lights",
        "PBPOWER": "set power-bank supply mode",
        "PK": "set topic ProductKey",
        "UN": "set connection username",
        "PW": "set connection password and CRC secret source",
        "ID": "set device ID",
        "DM": "set server endpoint",
        "APN": "set cellular APN",
        "WIFI": "set SSID and Wi-Fi password",
        "SAVECFG": "persist pending configuration"
      },
      "result": {
        "0": "rejected",
        "1": "accepted"
      }
    },
    {
      "id": "GN",
      "category": "network",
      "downlink": [
        ["GN", "messageId", "crc"]
      ],
      "uplink": [
        ["GN", "sameMessageId", "imei", "iccid", "rssi", "lac", "ci", "networkStatus", "crc"],
        ["GN", "sameMessageId", "mac", "wifiModel", "rssi", "ip", "ssid", "wifiPassword", "crc"]
      ],
      "sensitiveFields": ["imei", "iccid", "mac", "ip", "ssid", "wifiPassword"]
    },
    {
      "id": "DS",
      "category": "configuration",
      "uplink": [
        ["DS", "messageId", "deviceId", "networkMode", "ambientLight", "powerBankSupply", "qrLight", "reversePowerSlot", "crc"]
      ],
      "downlink": [
        ["DS", "sameMessageId", "deviceId", "crc"]
      ],
      "notes": ["Device retries every 60 seconds until acknowledged."]
    },
    {
      "id": "AC",
      "category": "inventory",
      "uplink": [
        ["AC", "messageId", "deviceId", "address", "networkInfo", "rssi", "firmware", "totalSlots", "slotRecord1..N", "crc"]
      ],
      "record": ["slotId", "returnState", "powerBankId", "battery", "slotState"],
      "recordDelimiter": ":",
      "defaultIntervalSeconds": 180,
      "notes": [
        "slotState is S1S2S3 on older firmware and S1S2S3S4 on firmware suffix 240526.1 or later.",
        "S1 charging, S2 charge class, S3 lock, S4 availability."
      ]
    },
    {
      "id": "CQ",
      "category": "inventory",
      "downlink": [
        ["CQ", "messageId", "slotId", "crc"]
      ],
      "uplink": [
        ["CQ", "messageId", "slotId", "powerBankId", "battery", "slotState", "returnState", "crc"]
      ],
      "notes": [
        "slotId zero queries all slots and receives AC.",
        "positive slotId queries one slot and receives CQ."
      ]
    },
    {
      "id": "AE",
      "category": "health",
      "uplink": [
        ["AE", "messageId", "deviceId", "totalSlots", "slotExtended1..N", "crc"]
      ],
      "record": ["slotId", "status", "powerBankVersion", "cycleCount", "health", "actualCapacity", "realtimeElectrical"],
      "recordDelimiter": ":",
      "defaultIntervalSeconds": 600,
      "status": {
        "0": "empty",
        "1": "normal discharge or standby",
        "2": "normal charging",
        "3": "full",
        "201": "over-voltage protection",
        "202": "temperature protection",
        "203": "cable abnormal"
      }
    },
    {
      "id": "CM",
      "category": "rental-rules",
      "privileged": true,
      "downlink": [
        ["CM", "messageId", "minBattery", "borrowOp", "returnOp", "lockErrorOp", "voiceOp", "debugOp", "crc"],
        ["CM", "messageId", "0", "0", "0", "0", "0", "0", "crc"]
      ],
      "uplink": [
        ["CM", "sameMessageId", "minBattery", "borrowOp", "returnOp", "lockErrorOp", "voiceOp", "debugOp", "disabledMask", "inventoryInterval", "heartbeatInterval", "brandPrefix", "crc"]
      ]
    },
    {
      "id": "CC",
      "category": "inventory",
      "downlink": [
        ["CC", "messageId", "crc"]
      ],
      "uplink": [
        ["CC", "sameMessageId", "availableCount", "crc"]
      ]
    },
    {
      "id": "BW",
      "category": "rental",
      "downlink": [
        ["BW", "messageId", "orderId", "rentalProfile?", "crc"]
      ],
      "uplink": [
        ["BW", "sameMessageId", "orderId", "result", "slotId", "powerBankId", "battery", "lockState", "fastCharge", "crc"],
        ["KW", "sameMessageId", "orderId", "result", "slotId", "powerBankId", "battery", "lockState", "fastCharge", "crc"]
      ],
      "notes": [
        "The v1.51 source profile uses KW reply when rentalProfile is present and BW when absent.",
        "Accept both names by negotiated profile.",
        "A BR with a new message ID is required within 30 seconds."
      ]
    },
    {
      "id": "KW",
      "category": "rental",
      "downlink": [
        ["KW", "messageId", "orderId", "rentalProfile", "crc"]
      ],
      "uplink": [
        ["KW", "sameMessageId", "orderId", "result", "slotId", "powerBankId", "battery", "lockState", "fastCharge", "crc"],
        ["FB", "sameMessageId", "orderId", "result", "slotId", "powerBankId", "battery", "lockState", "fastCharge", "crc"]
      ],
      "notes": ["KW reports preparation; final physical release is reported as FB."]
    },
    {
      "id": "FB",
      "category": "rental",
      "downlink": [
        ["FB", "messageId", "orderId", "slotId", "crc"]
      ],
      "uplink": [
        ["FB", "sameMessageId", "orderId", "result", "slotId", "powerBankId", "battery", "lockState", "fastCharge", "crc"]
      ],
      "result": {
        "0": "no eligible power bank or failed",
        "1": "release succeeded",
        "2": "lock open, waiting for pickup",
        "100": "pickup timeout, lock closed",
        "110": "unlock failed, slot deprioritized until restart"
      },
      "notes": [
        "Positive slotId selects one slot.",
        "Use slotId zero only after profile-specific UAT because the source wording is ambiguous.",
        "Duplicate orderId returns the cached prior result."
      ]
    },
    {
      "id": "BR",
      "category": "rental",
      "downlink": [
        ["BR", "newMessageId", "orderId", "crc"]
      ],
      "notes": [
        "Acknowledges a preparation or release result.",
        "Message ID must be new and order ID must match."
      ]
    },
    {
      "id": "RS",
      "category": "return",
      "uplink": [
        ["RS", "lastMessageId", "deviceId", "result", "slotId", "powerBankId", "battery", "lockState", "fastCharge", "crc"]
      ],
      "downlink": [
        ["RS", "newMessageId", "slotNumber", "confirmCode", "crc"]
      ],
      "result": {
        "0": "return failed",
        "1": "return detected successfully",
        "2": "offline return"
      },
      "notes": ["deviceId is present in v1.51 examples and field definitions although omitted from one format line."]
    },
    {
      "id": "FR",
      "category": "return",
      "uplink": [
        ["FR", "lastMessageId", "deviceId", "result", "slotId", "powerBankId", "battery", "lockState", "cableState", "crc"]
      ],
      "downlink": [
        ["RS", "newMessageId", "slotNumber", "confirmCode", "crc"]
      ],
      "result": {
        "2": "lock not seated",
        "4": "brand prefix mismatch"
      },
      "notes": ["FR is acknowledged with RS."]
    },
    {
      "id": "FA",
      "category": "maintenance",
      "privileged": true,
      "downlink": [
        ["FA", "messageId", "slotNumber", "crc"]
      ],
      "uplink": [
        ["FA", "sameMessageId", "result", "crc"]
      ],
      "notes": ["slotNumber zero ejects all slots; action does not enable discharge."]
    },
    {
      "id": "FL",
      "category": "maintenance",
      "privileged": true,
      "downlink": [
        ["FL", "messageId", "slotNumber", "operation", "crc"]
      ],
      "uplink": [
        ["FL", "sameMessageId", "disabledMaskHex", "crc"]
      ],
      "operations": {
        "0": "enable",
        "1": "disable"
      }
    },
    {
      "id": "OT",
      "category": "firmware",
      "privileged": true,
      "downlink": [
        ["OT", "operation", "host", "port", "path", "md5", "fileSize", "crc"],
        ["OT", "operation", "crc"]
      ],
      "uplink": [
        ["OT", "result", "resultInfo", "crc"]
      ],
      "operations": {
        "0": "query progress",
        "1": "upgrade main-board MCU",
        "10": "upgrade all stacked sub-board MCUs",
        "11": "query all sub-board versions",
        "20": "upgrade 8-port expansion-board MCU",
        "111": "stop upgrade"
      }
    },
    {
      "id": "SQ",
      "category": "firmware",
      "downlink": [
        ["SQ", "messageId", "operation", "0", "crc"]
      ],
      "uplink": [
        ["SQ", "sameMessageId", "boardCount", "board1..N", "crc"]
      ],
      "record": ["index", "boardId", "firmwareVersion"],
      "recordDelimiter": ":"
    },
    {
      "id": "NI",
      "category": "maintenance",
      "privileged": true,
      "downlink": [
        ["NI", "messageId", "slotId", "newPowerBankId", "crc"]
      ],
      "uplink": [
        ["NI", "sameMessageId", "result", "currentPowerBankId", "crc"]
      ],
      "notes": [
        "Source profile documents slotId 1..12 and power bank ID up to 16 characters.",
        "Use the cabinet topology and 15-character baseline unless the profile confirms otherwise."
      ]
    },
    {
      "id": "KU",
      "category": "physical-event",
      "uplink": [
        ["KU", "lastMessageId", "deviceId", "keyOperation", "crc"]
      ],
      "acknowledgement": false,
      "keyOperation": {
        "1": "short press",
        "2": "long press of at least three seconds",
        "3": "double press"
      }
    }
  ]
}
