Read OLT health, pull per-PON statistics, and push ONU configuration — description and VLAN — across BDCOM, VSOL and Solitine first, with Huawei MA and ZTE C3xx built in. CLI, SNMP or NETCONF underneath; identical JSON on top.
Initial support per the project brief covers BDCOM, VSOL and Solitine — both EPON and GPON. Huawei MA series and ZTE C3xx ship in the same box.
These credentials are mandatory to add for every vendor you want to reach live. The client provides the values (management IP, CLI username and password). Until a vendor is registered below, all its endpoints run in simulation mode — responses are generated, no device is touched. Passwords are stored server-side (file permission 0600) and are never returned by any GET endpoint.
# MANDATORY — without this the vendor stays in simulation mode curl -X POST __API_BASE__/api/v1/devices/bdcom/credentials \ -H 'Content-Type: application/json' \ -d '{ "host": "10.0.0.11", # OLT management IP (required) "port": 22, # 22 SSH / 23 Telnet (optional) "protocol": "ssh", # ssh | telnet (optional) "username": "client_user", # CLI user (required) "password": "client_pass", # CLI pass (required) "snmp_community": "public", # optional "pon_types": ["EPON","GPON"], # optional "live_mode": true # false = simulate only }'
The same body works for vsol, solitine, huawei and zte — just change the vendor in the URL. Check what is configured any time with GET /api/v1/devices, verify reachability with POST /api/v1/devices/{vendor}/test, and remove credentials with DELETE /api/v1/devices/{vendor}/credentials.
Three core operations, one consistent schema for every vendor: overall OLT health, per-PON statistics, and ONU status with description / VLAN updates.
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/devices/{vendor}/credentials | Mandatory first. Register host / username / password for a vendor. Enables live mode. |
| GET | /api/v1/devices | Credential configuration status per vendor — live vs simulation, source, required fields. |
| POST | /api/v1/devices/{vendor}/test | TCP reachability test against the registered host:port. |
| DELETE | /api/v1/devices/{vendor}/credentials | Remove stored credentials; vendor falls back to simulation. |
| PATCH | /api/v1/devices/live-mode | Toggle live CLI execution globally: {"live_mode": true}. |
| GET | /health | Service health, version, live-mode flag. |
| GET | /olts/{vendor} | Operation 1 — overall health: status, uptime, temperature, CPU, memory, firmware, PON types. |
| GET | /pons/{vendor} | Operation 2 — per-PON statistics: type (EPON/GPON), status, ONU count, RX/TX power, errors, distance. |
| GET | /onus/{vendor}[/{onu_id}] | Operation 3a — ONU status details. Optional filters: ?pon_id=1, ?status=online. |
| PUT PATCH | /onus/{vendor}/{onu_id} | Operation 3b — push config: {"description": "...", "vlan": 1-4094}. PUT = full, PATCH = partial. Returns the exact CLI plan. |
Fire real requests against this deployment and inspect the JSON — no setup needed. Requests run from your browser against the same origin.
Exactly what each call returns in simulation mode — the same shape you will get against real devices once credentials are registered.
{
"vendor": "bdcom",
"status": "online",
"mode": "simulation",
"model": "P3310C",
"uptime": "540h 15m",
"temperature": 41.0,
"cpu_usage": 8.0,
"memory_usage": 38.0,
"firmware_version": "V2.1.4",
"protocols": ["CLI/SSH", "SNMP"],
"pon_types": ["EPON", "GPON"],
"last_seen": "2026-09-03T19:40:00+06:00"
}
[
{
"vendor": "bdcom",
"pon_id": 1, "pon_type": "EPON",
"status": "active",
"onu_count": 16,
"rx_power": -15.3, "tx_power": -5.2,
"error_count": 0,
"distance_km": 12.5
},
{
"vendor": "bdcom",
"pon_id": 2, "pon_type": "GPON",
"status": "active",
"onu_count": 8,
"rx_power": -16.8, "tx_power": -4.9,
"error_count": 2,
"distance_km": 9.8
}
]
{
"onu": {
"vendor": "bdcom", "onu_id": 1,
"pon_id": 1,
"serial": "BDC0M1A10001",
"status": "online",
"description": "Living Room ONT",
"vlan": 100,
"rx_power": -21.2, "tx_power": -9.8,
"uptime": "120h 10m"
},
"updated": {"description": true, "vlan": true},
"push": {
"mode": "simulation",
"commands": [
"ont description 1 'Living Room ONT'",
"ont vlan 1 100",
"write memory"
],
"persist": "write memory",
"executed": false,
"detail": "simulation mode — no credentials
configured; CLI plan generated, device untouched"
}
}
The same flow in five languages: read health, list PONs, list ONUs, push a description + VLAN update. The API is plain JSON over HTTP — any language with an HTTP client works.
# 0. MANDATORY — register credentials (once per vendor) curl -X POST __API_BASE__/api/v1/devices/bdcom/credentials \ -H 'Content-Type: application/json' \ -d '{"host":"10.0.0.11","username":"client_user","password":"client_pass","live_mode":false}' # 1. Overall health curl -s __API_BASE__/olts/bdcom # 2. Per-PON statistics curl -s __API_BASE__/pons/bdcom # 3. ONU status curl -s __API_BASE__/onus/bdcom # 4. Push config — description + VLAN curl -s -X PUT __API_BASE__/onus/bdcom/1 \ -H 'Content-Type: application/json' \ -d '{"description":"Living Room ONT","vlan":100}'
import requests BASE = "__API_BASE__" # 0. MANDATORY — register credentials (once per vendor) requests.post(f"{BASE}/api/v1/devices/bdcom/credentials", json={ "host": "10.0.0.11", "username": "client_user", "password": "client_pass", "live_mode": False, }).raise_for_status() # 1. Overall health health = requests.get(f"{BASE}/olts/bdcom").json() print(health["status"], health["model"]) # 2. Per-PON statistics pons = requests.get(f"{BASE}/pons/bdcom").json() for p in pons: print(p["pon_id"], p["pon_type"], p["onu_count"]) # 3. ONU status onus = requests.get(f"{BASE}/onus/bdcom").json() # 4. Push config — description + VLAN r = requests.put(f"{BASE}/onus/bdcom/1", json={"description": "Living Room ONT", "vlan": 100}).json() print(r["updated"], r["push"]["commands"])
const BASE = "__API_BASE__"; // 0. MANDATORY — register credentials (once per vendor) await fetch(`${BASE}/api/v1/devices/bdcom/credentials`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ host: "10.0.0.11", username: "client_user", password: "client_pass", live_mode: false }), }); // 1. Overall health const health = await (await fetch(`${BASE}/olts/bdcom`)).json(); // 2. Per-PON statistics const pons = await (await fetch(`${BASE}/pons/bdcom`)).json(); // 3. ONU status const onus = await (await fetch(`${BASE}/onus/bdcom`)).json(); // 4. Push config — description + VLAN const res = await fetch(`${BASE}/onus/bdcom/1`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ description: "Living Room ONT", vlan: 100 }), }); const out = await res.json(); console.log(out.updated, out.push.commands);
package main import ( "bytes" "encoding/json" "fmt" "net/http" "io" ) func post(url string, body any) { b, _ := json.Marshal(body) resp, err := http.Post(url, "application/json", bytes.NewReader(b)) if err != nil { panic(err) } defer resp.Body.Close() out, _ := io.ReadAll(resp.Body) fmt.Println(resp.Status, string(out)) } func main() { base := "__API_BASE__" // 0. MANDATORY — register credentials post(base+"/api/v1/devices/bdcom/credentials", map[string]any{ "host": "10.0.0.11", "username": "client_user", "password": "client_pass", "live_mode": false, }) // 1–3. GET endpoints for, path := range []string{"/olts/bdcom", "/pons/bdcom", "/onus/bdcom"} { resp, _ := http.Get(base + path) out, _ := io.ReadAll(resp.Body) resp.Body.Close() fmt.Println(path, string(out)) } // 4. Push config post(base+"/onus/bdcom/1", map[string]any{ "description": "Living Room ONT", "vlan": 100, }) }
<?php $BASE = "__API_BASE__"; function call($method, $path, $body = null) { global $BASE; $ch = curl_init($BASE . $path); curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => $method, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Content-Type: application/json"], CURLOPT_POSTFIELDS => $body ? json_encode($body) : null, ]); return json_decode(curl_exec($ch), true); } // 0. MANDATORY — register credentials call("POST", "/api/v1/devices/bdcom/credentials", [ "host" => "10.0.0.11", "username" => "client_user", "password" => "client_pass", "live_mode" => false, ]); // 1–3. Reads $health = call("GET", "/olts/bdcom"); $pons = call("GET", "/pons/bdcom"); $onus = call("GET", "/onus/bdcom"); // 4. Push config — description + VLAN $out = call("PUT", "/onus/bdcom/1", [ "description" => "Living Room ONT", "vlan" => 100, ]); print_r($out["updated"]);
Every vendor driver maps its native CLI/SNMP/NETCONF output onto these three shapes — your integration code never changes when a new vendor is added.
Four steps from zero to a running API — locally or on any Linux server. Python 3.11+ required.
Clone the private repository or unpack the delivery package.
git clone git@github.com:dotprogrammers/olt-api.git cd olt-api
Isolated Python venv with pinned dependencies.
python3 -m venv venv source venv/bin/activate pip install -r requirements.txt
No .env editing needed — POST the client's test credentials to the credentials API, or set VENDOR_HOST / VENDOR_USER / VENDOR_PASS variables.
curl -X POST localhost:8000/api/v1/devices/bdcom/credentials \
-H 'Content-Type: application/json' \
-d '{"host":"10.0.0.11","username":"u","password":"p"}'Uvicorn for dev; systemd for production. Docs auto-mounted.
uvicorn main:app --host 0.0.0.0 --port 8000 # Swagger UI → /docs # Docs hub → / # ReDoc → /redoc
Production reference on this server: systemd unit olt-api-vendor.service behind nginx — source at /var/www/olt-api-project/, credentials store at device_credentials.json (0600).