02-Architecture / 02.04.Report-Field-Schema

02.04.Report Field Schema

02.04. Report Field Schema

Purpose

A single source of truth for every vehicle API field displayed in the report. The schema defines what each field is, how to format it, and where it belongs. Both the API (PDF generation) and WUI (browser report) consume the same schema. Brand-agnostic engine — each car manufacturer provides its own field mappings.

Location

bnc-cpt-api/src/python/cpt-api/app/services/
├── report_field_schema/
│   ├── __init__.py        # Engine: FieldDef, FieldType, RegionContext, format_field(),
│   │                      #   REGION_DEFAULTS, FORMATTERS, build_context(), format_report()
│   ├── tesla_fields.py    # Tesla field mappings (250 fields)
│   ├── bmw_fields.py      # (future) BMW field mappings
│   ├── volvo_fields.py    # (future) Volvo field mappings
│   └── ...                # One file per brand
└── tesla_provider_normalization.py  # Integration point — calls format_report(),
                                     #   adds formattedFields + regionContext to report

Architecture

┌──────────────────────────────────────────────────────────────────┐
│  report_field_schema/__init__.py  — THE ENGINE (brand-agnostic)        │
│                                                                  │
│  FieldDef          dataclass — metadata for one field            │
│  FieldType         enum — selects which formatter to use         │
│  RegionContext     dataclass — region, locale, units, currency   │
│  REGION_DEFAULTS   dict — region → default units/currency        │
│  FORMATTERS        dict — FieldType → formatter function         │
│  build_context()   region + gui_settings + locale → RegionContext│
│  format_field()    field_path + raw_value + context → string     │
│  format_report()   raw_report + brand_fields + context → fields[]│
└──────────────┬───────────────────────────────────────────────────┘
               │ uses
               ▼
┌──────────────────────────────────────────────────────────────────┐
│  report_field_schema/tesla.py  — TESLA FIELD MAPPINGS                  │
│                                                                  │
│  TESLA_FIELDS: dict[str, FieldDef]                               │
│    key = "{source}.{api_field}"  e.g. "vehicle_state.df"         │
│    value = FieldDef (all metadata for that field)                │
│                                                                  │
│  Every Tesla API field that appears in the report is listed here.│
│  Adding a field = one entry. Removing = delete the entry.        │
└──────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────┐
│  report_field_schema/bmw.py  — BMW FIELD MAPPINGS (future)             │
│                                                                  │
│  BMW_FIELDS: dict[str, FieldDef]                                 │
│    Same FieldDef, same FieldTypes, same formatters.              │
│    Only the API field paths and labels change.                   │
└──────────────────────────────────────────────────────────────────┘

FieldDef Schema

class FieldType(str, Enum):
    """Every Tesla field maps to exactly one type. The type selects the formatter."""

    # Boolean / state
    OPEN_CLOSE    = "open_close"      # 0/1 → Closed/Open
    BOOLEAN       = "boolean"         # true/false → Yes/No
    ENABLED       = "enabled"         # true/false → Enabled/Disabled
    ACTIVE        = "active"          # true/false → Active/Inactive

    # Numeric with units
    DISTANCE      = "distance"        # miles → km (if metric region)
    SPEED         = "speed"           # mph → km/h (if metric region)
    TEMPERATURE   = "temperature"     # °F → °C (if metric region)
    PRESSURE      = "pressure"        # psi/bar (from gui_settings)
    PERCENT       = "percent"         # append %
    POWER_KW      = "power_kw"        # append kW
    ENERGY_KWH    = "energy_kwh"      # append kWh
    CURRENT_A     = "current_a"       # append A
    VOLTAGE_V     = "voltage_v"       # append V
    CURRENCY      = "currency"        # 0 → "Included", else €34,990 / $34,990
    DURATION_SEC  = "duration_sec"    # 2700 → "45 min", 7200 → "2h 0m"
    DURATION_MIN  = "duration_min"    # 90 → "1h 30m"
    DURATION_MS   = "duration_ms"     # milliseconds → "3:45" (media)
    TIMESTAMP_MS  = "timestamp_ms"    # unix ms → locale date string
    TIMESTAMP_ISO = "timestamp_iso"   # ISO 8601 → locale date string
    DATE          = "date"            # date string → locale date

    # Enums / coded values
    SNAKE_CASE    = "snake_case"      # all_week → "All Week"
    CLIMATE_MODE  = "climate_mode"    # dog_mode → "Dog Mode"
    SEAT_HEATER   = "seat_heater"    # 0-3 → Off/Low/Medium/High
    SEAT_FAN      = "seat_fan"       # 0-3 → Off/Low/Medium/High
    CHARGING_STATE = "charging_state" # Charging/Complete/Stopped/etc.
    SHIFT_STATE   = "shift_state"    # P/R/N/D → Park/Reverse/Neutral/Drive

    # Pass-through
    TEXT          = "text"            # display as-is
    MONO          = "mono"            # display as-is, monospace (VINs, IDs)
    VERSION       = "version"         # software version string, as-is
    HIDDEN        = "hidden"          # do not display (tokens, internal IDs)


class FieldDef:
    """Metadata for a single Tesla API field."""

    # Identity
    key: str              # "{source}.{api_field}" — unique registry key
    label: str            # Human-readable label for the report
    type: FieldType       # Selects the formatter

    # Source mapping
    source: str           # Tesla API object: vehicle_state, charge_state, climate_state,
                          #   vehicle_config, vehicle_data, gui_settings, vehicle_specs, etc.
    api_field: str        # Field name within the source object

    # Presentation
    section: str          # Report section this field belongs to
    panel: str            # Sub-panel within the section
    mono: bool            # Render in monospace font (VIN, IDs)
    precision: int | None # Decimal places for numeric values (None = auto)

    # Unit context
    native_unit: str | None  # What Tesla API returns: "mph", "miles", "ms", "sec", "fahrenheit"
    display_unit: str | None # Override display unit (normally derived from RegionContext)

    # Documentation
    description: str | None  # Human explanation — also feeds glossary
    tesla_docs_ref: str | None  # Link to Tesla API docs for this field

    # Visibility
    plan: str             # Minimum plan: "basic" | "pro" | "expert"
    hidden: bool          # Suppress from customer-facing report
    deprecated: bool      # Field still in API but no longer shown

Field Type → Formatter Rules

FieldType Input example EU output US output Rule
open_close 0 Closed Closed 0→Closed, 1→Open
boolean true Yes Yes true→Yes, false→No
enabled true Enabled Enabled true→Enabled, false→Disabled
active true Active Active true→Active, false→Inactive
distance 102.5 (mi) 165.0 km 102.5 mi × 1.60934 if metric
speed 85 (mph) 137 km/h 85 mph × 1.60934 if metric
temperature 72.0 (°F) 22.2 °C 72.0 °F (F-32)×5/9 if metric
pressure 2.8 (bar) 2.8 bar 40.6 psi convert per gui_settings
percent 42 42% 42% append %
power_kw 11 11 kW 11 kW append kW
energy_kwh 32.5 32.5 kWh 32.5 kWh append kWh
current_a 16 16 A 16 A append A
voltage_v 233 233 V 233 V append V
currency 34990 €34,990 $34,990 region→symbol, 0→"Included"
duration_sec 2700 45 min 45 min sec→human (Xh Ym)
duration_min 90 1h 30m 1h 30m min→human
duration_ms 225000 3:45 3:45 ms→M:SS (media)
timestamp_ms 1773766748638 15 Mar 2026, 14:32 Mar 15, 2026, 2:32 PM locale-aware
timestamp_iso 2026-03-15T14:32:00Z 15 Mar 2026, 14:32 Mar 15, 2026, 2:32 PM locale-aware
date 2024-06-15 15 Jun 2024 Jun 15, 2024 locale-aware
snake_case all_week All Week All Week replace _ with space, title case
climate_mode dog_mode Dog Mode Dog Mode known mapping
seat_heater 2 Medium Medium 0→Off, 1→Low, 2→Medium, 3→High
seat_fan 1 Low Low same as seat_heater
charging_state Charging Charging Charging pass-through (already readable)
shift_state P Park Park P→Park, R→Reverse, N→Neutral, D→Drive
text JT3 JT3 JT3 as-is
mono LRW3E7... LRW3E7... LRW3E7... as-is, monospace
version 2026.2.3 2026.2.3 2026.2.3 as-is
hidden (any) not rendered

Region → Defaults Mapping

Region Currency Distance Speed Temp Pressure Date format
EU EUR (€) km km/h °C bar dd MMM yyyy
US USD ($) mi mph °F psi MMM dd, yyyy
UK GBP (£) mi mph °C psi dd MMM yyyy
CN CNY (¥) km km/h °C bar yyyy-MM-dd
CA CAD ($) km km/h °C psi MMM dd, yyyy
AU AUD ($) km km/h °C psi dd MMM yyyy
JP JPY (¥) km km/h °C kPa yyyy/MM/dd
KR KRW (₩) km km/h °C bar yyyy.MM.dd
MX MXN ($) km km/h °C psi dd/MM/yyyy
AE AED (د.إ) km km/h °C bar dd MMM yyyy

Note: gui_settings from the vehicle ALWAYS override region defaults. The owner may have set their car to miles in an EU region — we respect that.

Report Section → Panel Mapping

Each field declares its section and panel. This defines the report structure:

Section Panel Description
identity model Make, Model, Year, Vehicle Name
identity registration VIN, Region, Software Version
identity ids User ID, Vehicle ID, ID String
state security Locked, Sentry, Valet, User Present
state controls Dashcam, Camera, Remote Start, Autopark
state diagnostics Speed limits, Feature bitmask
access access_service Access Type, Calendar, Supercharging
access connectivity Notifications, Remote Control, Parsed Calendar
access camera_tokens Webcam, Dashcam clip, Backseat tokens
doors doors_trunks DF, DR, FT, RT, PF, PR
doors windows FD, FP, RD, RP windows
climate temperature Inside/outside temp, driver/passenger set temps
climate systems AC, fan, defrost, HVAC, heater, COP
climate seats Seat heaters (FL/FR/RL/RR), seat fans, steering wheel
battery health SoH, battery level, usable level, capacity
battery range Battery range, estimated range, ideal range
battery condition Battery condition, age context, mileage context
charging current_session Charging state, power, rate, voltage, amps
charging limits Charge limit, limit std, limit max
charging additions Miles added (rated/ideal), energy added
charging hardware Fast charger type, charger phases, connector
charging schedule Off-peak, preconditioning, scheduled departure
warranty coverages Basic warranty, battery warranty, odometer limit
alerts recent Alert list with date, text, category, code
specs vehicle_specs Trim, paint, wheels, interior, production date
specs equipment_price Equipment price breakdown
specs option_codes Factory option codes
software status Installed version, pending, update status
software release_notes Release note titles + versions
media playback Now playing, source, volume
gui settings Distance units, temp units, pressure units

How to Add a New Field

  1. Add entry to TESLA_FIELDS in tesla_fields.py: python "charge_state.new_field": FieldDef( key="charge_state.new_field", label="New Field Label", type=FieldType.PERCENT, source="charge_state", api_field="new_field", section="charging", panel="current_session", description="What this field means to the end user", ),

  2. If the field needs a new FieldType, add the type to the enum and add a formatter function in report_field_schema/__init__.py.

  3. Run tests — the registry has validation that catches:

  4. Duplicate keys
  5. Missing formatter for a FieldType
  6. Fields referencing non-existent sections/panels

How to Change Formatting Rules

All formatting logic is in report_field_schema/__init__.py in the FORMATTERS dict. Each FieldType maps to one function. Change the function, every field of that type updates.

Example: to change how currencies display:

# report_field_schema/__init__.py
def format_currency(value: Any, ctx: RegionContext) -> str:
    if not isinstance(value, (int, float)):
        return "Not available"
    if value == 0:
        return "Included"
    symbol = CURRENCY_SYMBOLS[ctx.currency]
    formatted = f"{value:,.0f}" if value == int(value) else f"{value:,.2f}"
    return f"{symbol}{formatted}"

How to Change Region Rules

Region defaults are in report_field_schema/__init__.py in REGION_DEFAULTS. The vehicle's gui_settings override these defaults.

To add a new region:

REGION_DEFAULTS["NO"] = RegionDefaults(
    currency="NOK",
    currency_symbol="kr",
    distance_unit="km",
    speed_unit="km/h",
    temp_unit="C",
    pressure_unit="bar",
    date_format="dd.MM.yyyy",
)

API Integration

tesla_provider_normalization.py builds the report dict and then calls format_report() to add formattedFields and regionContext. The integration is in build_vehicle_acquisition_outcome(). If formatting fails, the report still returns with empty formattedFields (graceful degradation).

The report endpoint now returns both raw and formatted:

{
  "vehicle": { ... },
  "vehicleData": { ... },
  "formatted_fields": [
    {
      "key": "vehicle_state.df",
      "label": "Driver Front Door",
      "value": "Closed",
      "raw_value": 0,
      "type": "open_close",
      "section": "doors",
      "panel": "doors_trunks",
      "mono": false
    },
    {
      "key": "charge_state.timestamp",
      "label": "Battery Timestamp",
      "value": "15 Mar 2026, 14:32",
      "raw_value": 1773766748638,
      "type": "timestamp_ms",
      "section": "battery",
      "panel": "health",
      "mono": false
    }
  ],
  "region_context": {
    "region": "EU",
    "currency": "EUR",
    "distance_unit": "km",
    "speed_unit": "km/h",
    "temp_unit": "C",
    "pressure_unit": "bar",
    "locale": "en"
  }
}

The WUI renders formatted_fields grouped by section → panel. No formatting logic needed in the frontend.

WUI Consumption

The WUI report component becomes a pure layout engine:

// Group fields by section → panel
const sections = computed(() => {
  const grouped = {};
  for (const field of report.value.formatted_fields) {
    if (!grouped[field.section]) grouped[field.section] = {};
    if (!grouped[field.section][field.panel]) grouped[field.section][field.panel] = [];
    grouped[field.section][field.panel].push(field);
  }
  return grouped;
});
<section v-for="(panels, sectionKey) in sections" :key="sectionKey" class="chapter">
  <div v-for="(fields, panelKey) in panels" :key="panelKey" class="subpanel">
    <h3 class="subpanel-title">{{ panelTitle(panelKey) }}</h3>
    <div class="rows">
      <div v-for="field in fields" :key="field.key" class="row">
        <span class="label">{{ field.label }}</span>
        <span class="value" :class="{ mono: field.mono }">{{ field.value }}</span>
      </div>
    </div>
  </div>
</section>

No makeRow(), no displayText(), no formatPercent() — the API handles all of it.


Tesla Field Audit

Migrated from bnc-cpt-api.TESLA-REPORT-FIELD-MAPPING.md (original audit by ysg, 2026-03-17).

Audit Sources

Purpose of the Audit

Current Endpoint Coverage

Backend report builder fetches these modules:

Module Code location
vehicle_data tesla_fleet.py
charging_history tesla_fleet.py
recent_alerts tesla_fleet.py
service_data tesla_fleet.py
warranty tesla_fleet.py
release_notes tesla_fleet.py
options tesla_fleet.py
vehicle_specs tesla_fleet.py

Name mapping notes: - warranty_details → current code maps to warrantyDetails - specs → current code maps to vehicleSpecs

Mapping Rule

  1. Fetch as much meaningful Tesla data as possible.
  2. Map it into the API report object.
  3. Render all useful fields in a null-safe way.
  4. If a field is missing, omit it or show Not available.
  5. Decide package boundaries only after the full report surface is understood.

Packages must depend on capability groups, not on every field always being present.

Top-Level Payload Status

Sample payload section Fetched now Rendered now Target
vehicles partial no low
vehicle_data yes partial yes
charging_history yes partial yes
recent_alerts yes yes yes
service_data yes partial yes
warranty_details yes yes yes
release_notes yes partial yes
options yes partial yes
specs yes partial yes

Audit Scope

The field comparison was checked against three concrete layers:

  1. Older raw Tesla endpoint fixture in src/python/cpt-api/tests/tesla_api_responses.json
  2. Newer normalized report payload in ../bnc-cpt-wui/dat/tmp/sec/tesla-report-data.json
  3. Current rendered report output in the existing report surfaces

Result: - The newer report payload still contains the same broad vehicleData field groups as the older raw fixture - The main gap is still rendering, not source availability - This document therefore distinguishes between fetched / present in report payload / rendered

Live Report Reduction States

Observed on 2026-03-17: live WUI raw report payloads are not always as complete as the normalized sample.

The report stack must distinguish these states per module:

State Meaning
present Module data available
empty Module returned but contains no data
failed Module fetch failed
unauthorized Tesla returned 401/403
not_requested Module was not fetched
dropped_during_assembly Available upstream but absent in final report object

The last state matters — current live evidence shows cases where a module is available upstream but absent in the final live report object.

Missing-From-Rendered Report (Audit Inventory)

Fields present in audited raw/new payloads but not fully surfaced in the rendered report.

Vehicle Snapshot / System Capabilities

Battery / Charging Detail

Climate / Comfort Detail

Vehicle Config / Hardware Detail

TPMS Detail

Preferences / Regional Settings Detail

Media / Context Detail

Options / Specs / Notes Still Underrepresented

Additional Fields Observed In Real Payloads

Fields present in audited payloads and/or live raw reports, not explicitly enumerated in the missing lists above. These are documented report candidates.

Core Battery / Charging State

Climate / Cabin State

Note: bioweapon_mode and seat_heater_rear_center are model-specific (observed in Model Y) — must remain null-safe.

Vehicle Config / Identity Detail

Vehicle State / Media / Update Detail

TPMS Pressure Values

Release Notes / Specs Metadata

Shared Confirmed Field Groups

Verified present in both tesla_api_responses.json and tesla-report-data.json:

The missing items above are not blocked by the audited sample payloads themselves.

Priority Field Groups

1. Vehicle Snapshot

Already useful, still underused: display_name, vehicle_name, locked, sentry_mode, sentry_mode_available, valet_mode, valet_pin_needed, webcam_available, webcam_supported, webcam_selfie_supported, dashcam_clip_save_supported, dashcam_clip_save_available, dashcam_state

2. Battery / Charging

Already fetched in vehicle_data, not fully surfaced: charge_amps, charge_current_request, charge_current_request_max, charge_energy_added, charge_rate, charge_port_door_open, charge_port_latch, charge_port_color, fast_charger_present, fast_charger_brand, fast_charger_type, off_peak_charging_enabled, off_peak_charging_times, preconditioning_enabled, preconditioning_times, scheduled_charging_pending, scheduled_charging_start_time, scheduled_departure_time, scheduled_departure_time_minutes, supercharger_session_trip_planner, time_to_full_charge, trip_charging

3. Climate / Comfort

Mostly available but still underused: cabin_overheat_protection, allow_cabin_overheat_protection, cabin_overheat_protection_actively_cooling, defrost_mode, fan_status, driver_temp_setting, passenger_temp_setting, is_climate_on, is_front_defroster_on, is_rear_defroster_on, seat heaters (all positions), seat fans, steering_wheel_heater, steering_wheel_heat_level, side_mirror_heaters, wiper_blade_heater

4. Vehicle Config / Hardware

Good commercial value: charge_port_type, driver_assist, exterior_color, exterior_trim, interior_trim_type, wheel_type, roof_color, rear_drive_unit, rear_seat_heaters, has_seat_cooling, has_air_suspension, has_ludicrous_mode, performance_package, spoiler_type, eu_vehicle, supports_qr_pairing

5. Software / System State

Useful for ownership insight: car_version, software_update, autopark_state_v2, notifications_supported, remote_start, remote_start_enabled, remote_start_supported, speed_limit_mode, service_mode, service_mode_plus, santa_mode, is_user_present

6. TPMS

Partly rendered, should be expanded: all four pressures, soft warnings, hard warnings, last-seen pressure timestamps.

7. GUI / User Settings

Secondary but available: gui_charge_rate_units, gui_distance_units, gui_temperature_units, gui_tirepressure_units, gui_range_display, gui_24_hour_time

8. Media / Context

Lower priority: media_info, media_state. Optional "Context / Infotainment" section.

Implementation Targets (Best Value-to-Effort)

These fields are already inside vehicleData:

  1. charge_state scheduling and charger details
  2. climate_state comfort/heating fields
  3. vehicle_config hardware and trim details
  4. vehicle_state dashcam / sentry / software / remote-start state
  5. Expanded TPMS data

Safety Requirement

The report stack must stay resilient when Tesla returns less than the sample:

Guardrails covered by: src/python/cpt-api/tests/test_pdf_service.py

Implementation Order

  1. Extend API/PDF/WUI mapping for already-fetched fields
  2. Add tests for rich payload and sparse payload behavior
  3. Review resulting full report
  4. Only then finalize package-level inclusions