สำหรับผู้ให้บริการสถานีชาร์จFor charge point operators

ข้อกำหนดการเชื่อมต่อข้อมูลสถานีStation Data Integration Specification

เชื่อมต่อ OCPI 2.2.1Connects over OCPI 2.2.1

1บทบาทและขอบเขตRole and scope#

PugEV รับข้อมูลอย่างเดียว ไม่ได้เป็นเจ้าของหรือเดินระบบสถานีPugEV only receives. We do not own or operate any charging station.

ตู้ชาร์จEVSE หน้างาน
OCPP
CPOระบบหลังบ้าน · CSMS
OCPI
eMSPpugev-apiรับ locations มาเก็บ
REST
แอป PugEVหมุดบนแผนที่
เราใช้เราไม่ใช้
versions · credentials · locationssessions · cdrs · tokens
tariffs (ไม่บังคับ)commands · chargingprofiles · hubclientinfo
  • อ่านอย่างเดียว ไม่มีการเขียนกลับเข้าระบบ CPO
  • ไม่สั่งเริ่ม/หยุดชาร์จ ไม่จองหัว ไม่รับชำระเงิน
  • ไม่ส่งข้อมูลที่ระบุตัวผู้ใช้ปลายทางให้ CPO
Charge pointthe EVSE on site
OCPP
CPObackend · CSMS
OCPI
eMSPpugev-apistores locations
REST
PugEV apppins on the map
We useWe do not use
versions · credentials · locationssessions · cdrs · tokens
tariffs (optional)commands · chargingprofiles · hubclientinfo
  • Read-only. Nothing is ever written back into the CPO’s system.
  • No remote start/stop, no reservations, no payment.
  • Nothing that identifies an end user is ever sent to the CPO.

2โครงสร้างข้อมูลThe data model#

สามชั้นซ้อนกัน จุดที่คนพลาดบ่อยคือสถานะอยู่ชั้นกลาง ไม่ใช่ชั้นล่างสุดThree nested levels. The thing people get wrong: status lives on the middle one.

Locationสถานี 1 แห่ง

idnameaddresscoordinatesopening_timeslast_updated

EVSE[]หัวชาร์จ 1 ตัว จอดได้ 1 คัน

uidevse_idstatuscapabilities

status อยู่ชั้นนี้

Connector[]หัวเสียบบนตัวเดียวกัน

idstandardpower_typemax_power_kwtariff_ids

ตู้ DC ที่มีทั้ง CCS2 และ CHAdeMO = 1 EVSE, 2 Connector เพราะเสียบได้ทีละหัว สถานะจึงมีค่าเดียว

Locationone site

idnameaddresscoordinatesopening_timeslast_updated

EVSE[]one charge point, one parking bay

uidevse_idstatuscapabilities

status lives here

Connector[]the plugs on that unit

idstandardpower_typemax_power_kwtariff_ids

A DC unit with both CCS2 and CHAdeMO is 1 EVSE and 2 connectors — only one can be in use at a time, so there is a single status.

3OCPI — เปิดสายOCPI — opening the link#

ไม่ใช่การขอ API key ฝ่ายเดียว — ทั้งสองฝั่งต้องมีเซิร์ฟเวอร์ และยืนยันตัวตนให้กันและกันNot a one-way API key — both sides run a server, and each authenticates to the other.

  1. นอกระบบCPO ส่ง TOKEN_A ให้เรา ใช้ลงทะเบียนได้ครั้งเดียว
  2. PugEV → CPOเราถามว่ารองรับเวอร์ชันไหน แล้วรับรายการ endpoint ทั้งหมดจากคำตอบ ไม่มี URL ไหน hardcode
    GET /ocpi/versions
    Authorization: Token <TOKEN_A>
    
    200 OK
    {
      "data": [
        {
          "version": "2.2.1",
          "url": ".../ocpi/2.2.1"
        }
      ],
      "status_code": 1000
    }
  3. PugEV → CPOเราส่ง TOKEN_C ที่ออกให้ CPO พร้อม URL versions ของเรา
    POST /ocpi/2.2.1/credentials
    Authorization: Token <TOKEN_A>
    
    {
      "token": "<TOKEN_C>",
      "url": "https://.../ocpi/versions",
      "roles": [
        {
          "role": "EMSP",
          "country_code": "TH",
          "party_id": "..."
        }
      ]
    }
  4. CPO → PugEVCPO เรียก versions ของเราด้วย TOKEN_C เพื่อยืนยันว่าปลายทางเปิดอยู่จริง — ขั้นนี้พังบ่อยที่สุด
  5. CPO → PugEVตอบ 200 พร้อม TOKEN_B แล้ว TOKEN_A หมดอายุทันที สายเปิดใช้งาน
โทเคนใครออกใช้ทำอะไร
TOKEN_ACPOตั๋วลงทะเบียนครั้งเดียว
TOKEN_BCPOเราใช้เรียกเข้าระบบ CPO
TOKEN_CPugEVCPO ใช้เรียกเข้าระบบเรา

รองรับ 2.2.1 เป็นหลักและ 2.1.1 สำหรับระบบเดิม ส่วน 2.3.0 ยังไม่รองรับ แจ้งเราได้ถ้ามีเฉพาะเวอร์ชันนี้

  1. Out of bandThe CPO sends us TOKEN_A. Valid for one registration.
  2. PugEV → CPOWe ask which versions the CPO speaks and take the whole endpoint list from the answer. No URL is hardcoded.
    GET /ocpi/versions
    Authorization: Token <TOKEN_A>
    
    200 OK
    {
      "data": [
        {
          "version": "2.2.1",
          "url": ".../ocpi/2.2.1"
        }
      ],
      "status_code": 1000
    }
  3. PugEV → CPOWe send TOKEN_C — the token we issue to the CPO — plus our own versions URL.
    POST /ocpi/2.2.1/credentials
    Authorization: Token <TOKEN_A>
    
    {
      "token": "<TOKEN_C>",
      "url": "https://.../ocpi/versions",
      "roles": [
        {
          "role": "EMSP",
          "country_code": "TH",
          "party_id": "..."
        }
      ]
    }
  4. CPO → PugEVThe CPO calls our versions endpoint with TOKEN_C to confirm the far end is really live. This is the step that fails most often.
  5. CPO → PugEVReply 200 with TOKEN_B. TOKEN_A expires immediately and the link is live.
TokenIssued byUsed for
TOKEN_ACPOSingle-use registration ticket
TOKEN_BCPOWe call into the CPO’s system
TOKEN_CPugEVThe CPO calls into ours

2.2.1 is the target and 2.1.1 is supported for older stacks. 2.3.0 is not supported yet — tell us if it is all the platform runs.

4OCPI — ข้อมูลไหลยังไงOCPI — how data moves#

push ให้ความเร็ว pull คือประกันว่าข้อมูลไม่ค้างเงียบ ๆPush gives speed; pull is the insurance that nothing goes quietly stale.

เมื่อไรใครเริ่มทำอะไร
ตอนเปิดสายเราดึงGET locations ทั้งหมด แบ่งหน้า
ระหว่างวันCPO ส่งPATCH เฉพาะฟิลด์ที่เปลี่ยน ส่วนใหญ่คือ status
ทุกคืนเราดึงGET ด้วย date_from เมื่อวาน เพื่อสอบทาน
เราดึง — แบ่งหน้าด้วย offset/limit
GET /ocpi/2.2.1/locations?date_from=2026-09-04T00:00:00Z&offset=0&limit=100
Authorization: Token <TOKEN_B>

200 OK
X-Total-Count: 412
Link: <.../locations?offset=100&limit=100>; rel="next"
CPO ส่ง — TH/EXA คือ country_code/party_id ของ CPO
PATCH /ocpi/2.2.1/locations/TH/EXA/LOC0142/EVSE0001
Authorization: Token <TOKEN_C>

{
  "status": "CHARGING",
  "last_updated": "2026-09-05T09:14:22Z"
}
WhenWho startsWhat happens
At connectionWe pullGET all locations, paginated
Through the dayCPO pushesPATCH only what changed, mostly status
NightlyWe pullGET with yesterday’s date_from, to reconcile
We pull — paginated with offset/limit
GET /ocpi/2.2.1/locations?date_from=2026-09-04T00:00:00Z&offset=0&limit=100
Authorization: Token <TOKEN_B>

200 OK
X-Total-Count: 412
Link: <.../locations?offset=100&limit=100>; rel="next"
CPO pushes — TH/EXA is the CPO’s country_code/party_id
PATCH /ocpi/2.2.1/locations/TH/EXA/LOC0142/EVSE0001
Authorization: Token <TOKEN_C>

{
  "status": "CHARGING",
  "last_updated": "2026-09-05T09:14:22Z"
}

5สถานะ 9 ค่าThe nine statuses#

ส่งค่าตาม OCPI มา เราแปลงเป็นสิ่งที่ผู้ใช้เห็นตามตารางนี้Send the OCPI value; this is what a driver ends up seeing.

ค่าที่ CPO ส่งผู้ใช้เห็น
AVAILABLEว่าง
CHARGING · RESERVEDใช้งานอยู่
BLOCKED · OUTOFORDERใช้ไม่ได้
INOPERATIVEปิดปรับปรุง
PLANNED · REMOVEDไม่แสดงบนแผนที่
UNKNOWNไม่มีข้อมูล
What the CPO sendsWhat the driver sees
AVAILABLEFree
CHARGING · RESERVEDIn use
BLOCKED · OUTOFORDERUnavailable
INOPERATIVEUnder maintenance
PLANNED · REMOVEDNot shown on the map
UNKNOWNNo data

6ข้อกำหนดที่เหลืออีกสามข้อThree more rules#

ข้อกำหนดทำไม
รหัสสถานีและ EVSE ห้ามเปลี่ยนผู้ใช้บันทึกสถานีโปรดและประวัติการชาร์จไว้ด้วยรหัสนี้
สถานะที่ไม่อัปเดตเกินรอบที่ตกลง จะถูกแสดงเป็น “ไม่มีข้อมูล”ค่าที่ค้างไว้อันตรายกว่าการไม่มีค่า
เวลาเปิดปิดใช้เขตเวลา Asia/Bangkok · ราคารวม VATให้ตัวเลขบนแอปตรงกับที่หน้างาน
RuleWhy
Site and EVSE identifiers never changeDrivers save favourites and charging history against them
A status not refreshed within the agreed interval shows as “no data”A stale value is more dangerous than no value
Opening hours in Asia/Bangkok · prices include VATSo the numbers in the app match the numbers on site

7ความปลอดภัยSecurity#

  • HTTPS TLS 1.2 ขึ้นไป ใบรับรองต้องตรวจสอบได้ เราไม่ปิดการตรวจไม่ว่ากรณีใด
  • โทเคนเก็บใน secret store แยกจากฐานข้อมูล หมุนเวียนได้ แจ้งล่วงหน้า 7 วัน
  • ต้องการ IP allowlist แจ้งได้ เรามีชุด IP ขาออกคงที่
  • เราจำกัดอัตราการเรียกฝั่งเราเอง และเคารพ 429 พร้อม Retry-After ของ CPO
  • ไม่มีข้อมูลผู้ใช้ปลายทางไหลไปหา CPO และเราไม่รับข้อมูลลูกค้าของ CPO

โทเคนรั่วหรือเห็นทราฟฟิกผิดปกติ แจ้ง [email protected] เราเพิกถอนภายใน 24 ชั่วโมงและออกชุดใหม่ให้

  • HTTPS, TLS 1.2 or later, verifiable certificate. We never disable validation, for any reason.
  • Tokens live in a secret store separate from the database and rotate on the CPO’s schedule with 7 days’ notice.
  • Want an IP allowlist? Ask — we have fixed egress addresses.
  • We rate-limit ourselves outbound and honour the CPO’s 429 and Retry-After.
  • No end-user data reaches the CPO, and we neither ask for nor accept its customer data.

If a token is exposed or unexpected traffic shows up, write to [email protected]. We revoke within 24 hours and issue replacements.