diff --git a/tracksolidApiDocumentation.md b/tracksolidApiDocumentation.md index 562bfc0..95a1815 100644 --- a/tracksolidApiDocumentation.md +++ b/tracksolidApiDocumentation.md @@ -2,7 +2,7 @@ > Consolidated from official Jimi IoT documentation at [tracksolidprodocs.jimicloud.com](https://tracksolidprodocs.jimicloud.com/) and [docs.jimicloud.com](https://docs.jimicloud.com/integration/integration.html). > -> API Spec Version: 2.7.7 | Last updated: 2026-04-08 +> API Spec Version: 2.7.7 | Last updated: 2026-04-11 --- @@ -81,6 +81,7 @@ Every API call includes these base parameters: - Token acquisition (`jimi.oauth.token.get`) can be called **at most once per minute** - `jimi.device.alarm.list` — time range limited to **1 month**, max **1000 rows** returned - Batch endpoints accept up to **50 IMEIs** per call +- `jimi.device.track.list` — per-device endpoint (no batch); each call can be slow (~137s observed in production for a 35-minute lookback). Do not call synchronously inside a 60s scheduler loop; run on a dedicated 30-minute schedule. --- @@ -440,14 +441,26 @@ Retrieve latest positions for all devices under an account. Single API call for |---|---|---| | `imei` | string | Device IMEI | | `deviceName` | string | Device name | -| `status` | string | Device status | +| `status` | string | Device status code | | `lat` | double | Latitude | | `lng` | double | Longitude | | `speed` | number | Speed (km/h) | +| `direction` | number | Heading (0–360 degrees) | | `gpsTime` | string | GPS fix timestamp | -| `accStatus` | string | ACC ignition status | +| `hbTime` | string | Heartbeat/server receive time | +| `accStatus` | string | ACC ignition status (`0`=off, `1`=on) | +| `gpsSignal` | int | GPS signal quality (0–4) | +| `gpsNum` | int | Satellites used | | `currentMileage` | number | Odometer reading | -| `expireFlag` | string | Expiration flag | +| `powerValue` | number | External power voltage | +| `elecQuantity` | number | Battery percentage | +| `posType` | string | Position type | +| `confidence` | int | Position confidence score | +| `expireFlag` | string | Subscription expiration flag | +| `activationFlag` | string | Activation status | +| `locDesc` | string | Reverse-geocoded address (if available) | + +> **Implementation note:** This is the fleet sweep endpoint called every 60 seconds. In production it returns ~19 active devices out of 63 queried, averaging ~200ms per call. Devices without a current position are silently omitted from the response — they are not returned with null coordinates. --- @@ -532,9 +545,15 @@ Extract trip and distance records within a time period. Supports batching up to | `imei` | string | Device IMEI | | `startTime` | string | Trip start time | | `endTime` | string | Trip end time | -| `distance` | number | Distance in kilometers | -| `avgSpeed` | number | Average speed (km/h) | -| `runTimeSecond` | int | Trip duration in seconds | +| `distance` | number | Distance — **stored as kilometres in DB** (see note below) | +| `avgSpeed` | number | Average speed (km/h) — field name: `avgSpeed` | +| `maxSpeed` | number | Maximum speed (km/h) — field name: `maxSpeed` | +| `runTimeSecond` | int | Driving time (seconds) | +| `idleSecond` | int | Idle time (seconds) | + +> **Distance unit note (BUG-02):** The raw `distance` value returned by this endpoint is in **kilometres** as labelled. However, the value was being stored incorrectly as millimetres due to an erroneous `× 1000` multiplication in earlier code. Migration 04 corrected all historical rows: `distance_km = stored_value / 1,000,000`. Current code stores `distance` directly as `distance_km` with no further conversion. The DB column was renamed from `distance_m` to `distance_km` to reflect this. +> +> **maxSpeed note (BUG-03):** `maxSpeed` was present in the API response but not mapped in `poll_trips()`. Fixed — now stored in `tracksolid.trips.max_speed_kmh`. --- @@ -564,10 +583,14 @@ Retrieve detailed positional waypoints for a specified timeframe. **Per-device** | `lng` | double | Longitude | | `gpsTime` | string | Timestamp of GPS fix | | `gpsSpeed` | int | Speed (km/h) | -| `direction` | int | Heading (0-360 degrees) | +| `direction` | int | Heading (0–360 degrees) | | `ignition` | string | Ignition state | | `accStatus` | string | ACC status | | `confidence` | int | Position confidence | +| `altitude` | int | Altitude (metres) — not populated by all device models | +| `satellite` | int | Satellite count | + +> **Implementation note (POLL-01):** Called per-device every 30 minutes with a 35-minute lookback window. Results written to `tracksolid.position_history` with `source='track_list'` (vs `source='poll'` for the 60s sweep). Use `ON CONFLICT DO NOTHING` — duplicate fixes from overlapping windows are safely discarded. Observed: ~5–6 waypoints per active device per 30-min window. Altitude field is returned but not populated by X3 and GT06E devices in this fleet. --- @@ -617,7 +640,7 @@ Analyze stationary and engine-running periods. | `end_time` | string | Yes | End time | | `start_row` | int | Yes | Pagination offset | | `page_size` | int | Yes | Page size | -| `acc_type` | string | Yes | ACC filter type | +| `acc_type` | string | Yes | ACC filter: **`0`** = all stops, `1` = ignition-off only | **Response (array):** @@ -626,10 +649,12 @@ Analyze stationary and engine-running periods. | `imei` | string | Device IMEI | | `startTime` | string | Parking/idle start | | `endTime` | string | Parking/idle end | -| `durSecond` | int | Total duration (seconds) | +| `durSecond` | int | Total duration (seconds) — **use this field for duration** | +| `stopSecond` | int | Stationary-only duration (seconds) — may be absent | | `addr` | string | Address / location | | `deviceName` | string | Device name | -| `stopSecond` | int | Stationary duration (seconds) | + +> **Implementation note (POLL-02):** Pass `acc_type=0` to capture all stop events (not just ignition-off). If `acc_type` is omitted or set to an empty string the API returns 0 rows. The primary duration field is `durSecond`; `stopSecond` is a secondary field that may not be populated. In production: 60 calls logged, all successful, 0 rows — expected while fleet is overnight-parked. Will populate once vehicles complete a full stop–start–stop cycle. --- @@ -690,16 +715,24 @@ Retrieve alarm events for devices within a time range. | `imei` | string | Device IMEI | | `model` | string | Device model | | `account` | string | Account | -| `alertTypeId` | string | Alarm type identifier | -| `alarmTypeName` | string | Alarm type display name | -| `alertTime` | string | Alarm trigger time | +| `alertTypeId` | string | Alarm type identifier — **polling field name** | +| `alarmTypeName` | string | Alarm type display name — **polling field name** | +| `alertTime` | string | Alarm trigger time — **polling field name** | | `positioningTime` | string | GPS fix time at alarm | | `lat` | double | Latitude | | `lng` | double | Longitude | | `speed` | number | Speed at alarm time | | `geoid` | string | Geo-fence ID (if geofence alarm) | -> **Note:** The documented response field names (`alertTypeId`, `alertTime`) may differ from what some code examples use (`alarmType`, `alarmTime`). Always verify against actual API responses. +> **Critical field name difference — polling vs push (BUG-01):** +> +> | Data | Polling (`jimi.device.alarm.list`) | Push (`/pushalarm`) | DB column | +> |---|---|---|---| +> | Type ID | `alertTypeId` | `alarmType` | `alarm_type` | +> | Type name | `alarmTypeName` | `alarmName` | `alarm_name` | +> | Time | `alertTime` | `gateTime` | `alarm_time` | +> +> Earlier code used the push field names (`alarmType`, `alarmName`) in the polling path — these keys are never present in the polling response, so `alarm_type` and `alarm_name` were always NULL. Fixed in `ingest_events_rev.py` (FIX-E06): polling now maps `item.get('alertTypeId')` → `alarm_type` and `item.get('alarmTypeName')` → `alarm_name`. **Verified in production:** vibration alarms now store type `3` and name `"Vibration alert"` correctly. --- @@ -1217,6 +1250,8 @@ You must configure your callback URL in the Tracksolid Pro platform. All push en | Endpoint | `{YourURL}/pushevent` | |---|---| +| **Handler** | `webhook_receiver_rev.py` — implemented (PUSH-01) | +| **DB table** | `tracksolid.device_events` | **`data_list` fields:** @@ -1233,6 +1268,7 @@ You must configure your callback URL in the Tracksolid Pro platform. All push en | Endpoint | `{YourURL}/pushhb` | |---|---| +| **Handler** | Not yet implemented — DB table `tracksolid.heartbeats` exists and is ready | **`data_list` fields (max 50 per request):** @@ -1278,6 +1314,10 @@ You must configure your callback URL in the Tracksolid Pro platform. All push en | Endpoint | `{YourURL}/pushalarm` | |---|---| +| **Handler** | `webhook_receiver_rev.py` — implemented | +| **DB table** | `tracksolid.alarms` | + +> **Field name note:** Push uses `alarmType` and `alarmName` — different from the polling endpoint which uses `alertTypeId` and `alarmTypeName`. See Section 6.1 for the full mapping table. **`data_list` fields (max 50 per request):** @@ -1333,6 +1373,8 @@ You must configure your callback URL in the Tracksolid Pro platform. All push en | Endpoint | `{YourURL}/pushoil` | |---|---| +| **Handler** | `webhook_receiver_rev.py` — implemented (PUSH-02) | +| **DB table** | `tracksolid.fuel_readings` (TimescaleDB hypertable) | **`data_list` fields (max 50 per request):** @@ -1342,7 +1384,7 @@ You must configure your callback URL in the Tracksolid Pro platform. All push en | `path` | int | Sensor ID | | `gateTime` | string | Reading time | | `value` | double | Oil level (divide by 100 for actual value) | -| `unit` | int | 1=cm, 2=%, 3=V, 4=L | +| `unit` | int | `1`=cm, `2`=%, `3`=V, `4`=L — stored as text label in DB | | `gpsTime` | string | Optional GPS time | | `lng`, `lat` | double | Optional GPS position | @@ -1368,6 +1410,8 @@ You must configure your callback URL in the Tracksolid Pro platform. All push en | Endpoint | `{YourURL}/pushtem` | |---|---| +| **Handler** | `webhook_receiver_rev.py` — implemented (PUSH-03) | +| **DB table** | `tracksolid.temperature_readings` (TimescaleDB hypertable) | **`data_list` fields:** @@ -1385,6 +1429,8 @@ You must configure your callback URL in the Tracksolid Pro platform. All push en | Endpoint | `{YourURL}/pushlbs` | |---|---| +| **Handler** | `webhook_receiver_rev.py` — implemented (PUSH-04) | +| **DB table** | `tracksolid.lbs_readings` | **`data_list` fields:** @@ -1552,8 +1598,10 @@ Called when a device finishes uploading media (photo, video, event clip) to Jimi | Endpoint | `{YourURL}/pushobd` | |---|---| +| **Handler** | `webhook_receiver_rev.py` — implemented | +| **DB table** | `tracksolid.obd_readings` | -> **Important:** This is the only documented method for receiving OBD data. There is no polling/pull endpoint for OBD. +> **Important:** This is the only documented method for receiving OBD data. There is no polling/pull endpoint for OBD. The handler extracts scalar fields (`engine_rpm`, `coolant_temp_c`, `fuel_level_pct`, `battery_voltage`, `intake_pressure`, `throttle_pct`, `vehicle_speed`, `engine_load_pct`) from `obdJson` using well-known OBD PID dataID keys, and also stores the full `obdJson` in the `obd_data JSONB` column. **`data_list` fields:** @@ -1576,6 +1624,7 @@ Called when a device finishes uploading media (photo, video, event clip) to Jimi | Endpoint | `{YourURL}/pushfaultinfo` | |---|---| +| **Handler** | Not yet implemented — DB table `tracksolid.fault_codes` exists and is ready | > **Important:** This is the only documented method for receiving DTC fault codes. @@ -1598,6 +1647,8 @@ Called when a device finishes uploading media (photo, video, event clip) to Jimi | Endpoint | `{YourURL}/pushtripreport` | |---|---| +| **Handler** | `webhook_receiver_rev.py` — implemented | +| **DB table** | `tracksolid.trips` | **`data_list` fields:** @@ -1647,19 +1698,39 @@ Common parameters: `deviceImei`, `proNo` (protocol number), `cmdContent` (JSON), ## Appendix B: API Coverage in This Codebase -| API Method | Pipeline | Status | -|---|---|---| -| `jimi.oauth.token.get` | `ts_shared_rev.py` | In use | -| `jimi.oauth.token.refresh` | `ts_shared_rev.py` | In use | -| `jimi.user.device.list` | `ingest_movement_rev.py` | In use | -| `jimi.track.device.detail` | `ingest_movement_rev.py` | In use | -| `jimi.user.device.location.list` | `ingest_movement_rev.py` | In use | -| `jimi.device.track.mileage` | `ingest_movement_rev.py` | In use | -| `jimi.device.alarm.list` | `ingest_events_rev.py` | In use — field mapping corrected (FIX-E06) | -| `jimi.device.obd.list` | `ingest_events_rev.py` | **Not a real endpoint** — OBD is push-only | -| `jimi.device.track.list` | `ingest_movement_rev.py` | **In use** — poll_track_list() every 30m (FIX-M14) | -| `jimi.device.location.get` | `ingest_movement_rev.py` | **In use** — get_device_locations() on-demand (FIX-M15) | -| `jimi.open.platform.report.parking` | `ingest_movement_rev.py` | **In use** — acc_type/durSecond fixed (FIX-M13) | -| `jimi.device.jimi.media.URL` | — | Not used (media catalog) | -| `jimi.device.media.event.URL` | — | Not used (alarm-triggered media) | -| All Data Push endpoints | — | Not used (webhook receiver needed) | +### Polling (Pull) Endpoints + +| API Method | File | Status | Notes | +|---|---|---|---| +| `jimi.oauth.token.get` | `ts_shared_rev.py` | ✅ In use | Token auto-refreshed, cached in DB | +| `jimi.oauth.token.refresh` | `ts_shared_rev.py` | ✅ In use | | +| `jimi.user.device.list` | `ingest_movement_rev.py` | ✅ In use | Fleet sync every 5h; 2 runs, 126 devices synced | +| `jimi.track.device.detail` | `ingest_movement_rev.py` | ✅ In use | Called alongside device.list | +| `jimi.user.device.location.list` | `ingest_movement_rev.py` | ✅ In use | Every 60s; 311 calls, 5,909 upserts, 0 failures | +| `jimi.device.track.mileage` | `ingest_movement_rev.py` | ✅ In use | Trip polling; `maxSpeed` now mapped (BUG-03 fixed) | +| `jimi.device.track.list` | `ingest_movement_rev.py` | ✅ In use | `poll_track_list()` every 30m (POLL-01 / FIX-M14); 15 calls, 80 waypoints | +| `jimi.device.location.get` | `ingest_movement_rev.py` | ✅ In use | `get_device_locations()` on-demand batch refresh (POLL-03 / FIX-M15); max 50 IMEIs/call | +| `jimi.open.platform.report.parking` | `ingest_movement_rev.py` | ✅ In use | `acc_type=0`, `durSecond` corrected (POLL-02 / FIX-M13); 60 calls, 0 rows (fleet overnight) | +| `jimi.device.alarm.list` | `ingest_events_rev.py` | ✅ In use | `alertTypeId`/`alarmTypeName` mapping fixed (BUG-01 / FIX-E06); 11 calls, 11 rows | +| `jimi.device.obd.list` | — | ❌ Does not exist | OBD data is push-only via `/pushobd` | +| `jimi.device.jimi.media.URL` | — | Not used | Media file catalog | +| `jimi.device.media.event.URL` | — | Not used | Alarm-triggered media | +| All other geofence/command/media polling | — | Not used | Available when needed | + +### Push (Webhook) Endpoints + +| Endpoint | File | Status | DB Table | +|---|---|---|---| +| `/pushalarm` | `webhook_receiver_rev.py` | ✅ Implemented | `tracksolid.alarms` | +| `/pushtripreport` | `webhook_receiver_rev.py` | ✅ Implemented | `tracksolid.trips` | +| `/pushobd` | `webhook_receiver_rev.py` | ✅ Implemented | `tracksolid.obd_readings` | +| `/pushevent` | `webhook_receiver_rev.py` | ✅ Implemented (PUSH-01) | `tracksolid.device_events` | +| `/pushoil` | `webhook_receiver_rev.py` | ✅ Implemented (PUSH-02) | `tracksolid.fuel_readings` | +| `/pushtem` | `webhook_receiver_rev.py` | ✅ Implemented (PUSH-03) | `tracksolid.temperature_readings` | +| `/pushlbs` | `webhook_receiver_rev.py` | ✅ Implemented (PUSH-04) | `tracksolid.lbs_readings` | +| `/pushhb` | — | ⚠️ Not yet wired | `tracksolid.heartbeats` table ready | +| `/pushfaultinfo` | — | ⚠️ Not yet wired | `tracksolid.fault_codes` table ready | +| `/pushgps` | — | Not used | GPS data received via polling | +| All other push endpoints | — | Not used | | + +> **Registration status:** All implemented webhook endpoints need to be registered in the Tracksolid Pro dashboard with your server's public URL before they will receive data. Tables exist and are ready; rows will be 0 until registration is complete.