Skip to content

Commit 53cacbd

Browse files
committed
Revise EV data model to replace 'EV' with 'Charger' and introduce detailed charger data model with metrics for charge/discharge capabilities and phase-level measurements.
1 parent b68b71c commit 53cacbd

1 file changed

Lines changed: 90 additions & 2 deletions

File tree

docs/developer/data-models.md

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ You control the **Device** (inverter), but you represent its capabilities as sep
8686
- **PV (Photovoltaic)**: Solar generation
8787
- **Battery**: Energy storage
8888
- **Meter**: Grid import/export measurement
89-
- **EV**: Vehicle-to-grid capable electric vehicle
89+
- **Charger**: EV charger (uni- or bi-directional)
9090
- **Flexible Load**: Controllable consumption (heat pumps, HVAC, etc.)
9191

9292
## Hierarchy Example
@@ -98,7 +98,7 @@ WALLET: user_abc123
9898
│ ├─ DER: pv_rooftop (Solar PV)
9999
│ └─ DER: battery_01 (Home Battery)
100100
├─ DEVICE: ev_charger_01 (MQTT)
101-
│ └─ DER: ev_tesla_model3 (EV Battery)
101+
│ └─ DER: tesla_model3 (Charger)
102102
└─ DEVICE: smart_meter_01 (P1)
103103
└─ DER: grid_meter (Meter)
104104
```
@@ -307,3 +307,91 @@ Grid meter data with import/export and phase-level measurements:
307307
| `L3_W` | W | float | L3 Phase Power |
308308
| `total_import_Wh` | Wh | integer | Total Energy Imported |
309309
| `total_export_Wh` | Wh | integer | Total Energy Exported |
310+
311+
### Charger Data Model
312+
313+
EV charger data with charge/discharge metrics and phase-level measurements. This DER type represents the charger's capability as a flexible load/source (uni- or bi-directional), with optional vehicle-specific attributes.
314+
315+
**Example:**
316+
317+
```json
318+
{
319+
"type": "charger",
320+
"make": "Ambibox",
321+
"timestamp": 1731573040000,
322+
"read_time_ms": 42,
323+
"W": 6400,
324+
"lower_limit_W": -11000,
325+
"upper_limit_W": 11000,
326+
"L1_V": 228.03,
327+
"L1_A": 9.228,
328+
"L1_W": 2104,
329+
"L2_V": 227.92,
330+
"L2_A": 9.333,
331+
"L2_W": 2127,
332+
"L3_V": 227.62,
333+
"L3_A": 9.444,
334+
"L3_W": 2150,
335+
"offered_A": 10.0,
336+
"connected": true,
337+
"charging": true,
338+
"vehicle_id": "my_tesla_model3",
339+
"vehicle_capacity_Wh": 75000,
340+
"vehicle_SoC_fract": 0.65,
341+
"SoC_source": "vehicle",
342+
"capacity_source": "user",
343+
"total_import_Wh": 3623394,
344+
"total_export_Wh": 12000,
345+
"session_import_Wh": 15200,
346+
"session_export_Wh": 0
347+
}
348+
```
349+
350+
**Fields:**
351+
352+
| Field | Unit | Data Type | Description |
353+
|----------------------|-----------|-----------|-----------------------------------------------------------------------------|
354+
| `W` | W | integer | Active Power (+ charge/import to vehicle, - discharge/export from vehicle) |
355+
| `lower_limit_W` | W | integer | Most-negative allowable power (e.g., -11000 for V2G discharge capability; 0 for uni-directional chargers without discharge support) |
356+
| `upper_limit_W` | W | integer | Most-positive allowable power (e.g., 11000 for max charge) |
357+
| `L1_V` | V | float | L1 Phase Voltage |
358+
| `L1_A` | A | float | L1 Phase Current |
359+
| `L1_W` | W | float | L1 Phase Power (computed as V * A) |
360+
| `L2_V` | V | float | L2 Phase Voltage |
361+
| `L2_A` | A | float | L2 Phase Current |
362+
| `L2_W` | W | float | L2 Phase Power (computed as V * A) |
363+
| `L3_V` | V | float | L3 Phase Voltage |
364+
| `L3_A` | A | float | L3 Phase Current |
365+
| `L3_W` | W | float | L3 Phase Power (computed as V * A) |
366+
| `offered_A` | A | float | Current offered by charger (from OCPP) |
367+
| `connected` | - | boolean | Is a vehicle connected? |
368+
| `charging` | - | boolean | Is active charging/discharging occurring? |
369+
| `vehicle_id` | - | string, optional | User-supplied or detected vehicle reference (null if unknown) |
370+
| `vehicle_capacity_Wh`| Wh | integer, optional | Vehicle battery capacity (null if unknown) |
371+
| `vehicle_SoC_fract` | fraction | float, optional | State of Charge (0.0-1.0, null if unknown) |
372+
| `SoC_source` | - | string, optional | Data source ("vehicle", "estimated", "manual", "unknown") |
373+
| `capacity_source` | - | string, optional | Data source ("vehicle", "user", "default") |
374+
| `total_import_Wh` | Wh | integer | Total Energy Imported (lifetime charge energy) |
375+
| `total_export_Wh` | Wh | integer | Total Energy Exported (lifetime discharge energy; 0 if no V2G support) |
376+
| `session_import_Wh` | Wh | integer | Session Energy Imported (reset on connect/disconnect) |
377+
| `session_export_Wh` | Wh | integer | Session Energy Exported (reset on connect/disconnect) |
378+
379+
### Implications for EMS and Optimization
380+
381+
- **Control (realtime)**: EMS uses only W, lower_limit_W, upper_limit_W – it works regardless of data quality. If connected: false, clamp limits to 0. For phase balancing, use phase-data directly (e.g., to avoid overload on one phase). For chargers with V2G support (e.g., Ambibox), lower_limit can go negative – great for grid stabilization. For uni-directional chargers, lower_limit is 0.
382+
383+
- **Optimization (planning)**: This is where it gets interesting. If SoC and capacity are null/unknown, fallback to defaults (e.g., 60 kWh, start-SoC 0.3) and run conservatively: Optimize only based on limits, without long-term planning (e.g., "charge max now if spot price low, but no SoC-prediction"). With rich data (Ambibox/ISO 15118), full steam ahead: Calculate remaining Wh to full (capacity * (1 - SoC)), schedule discharge for peak hours. Estimation works ok for simple cases: At connect, set initial_SoC (user/default), update with session_import_Wh / capacity – but as you said, ignore losses initially (add 10% factor later). If the user charges elsewhere, reset at reconnect via user prompt.
384+
385+
- **Hierarchy-fit**: DEVICE becomes the charger's comm-point (OCPP/MQTT), DER is "charger". Fits perfectly with your example – SITE optimizes charger-DER alongside battery/PV. No need for sub-DERs.
386+
387+
### Thoughts on UX and Defaults for Launch
388+
389+
For December (V2X-launch), prioritize minimal viable: Run with Alt A (automatic + override) – it's user-friendly without being annoying. At connect (detect via OCPP):
390+
391+
1. If Ambibox/ISO 15118: Pull SoC/capacity auto.
392+
393+
2. Otherwise: Prompt "Car connected on charger X. Is it your [last/default car]? Yes/No" – choose from registry.
394+
395+
3. Override: Always button for manual SoC/input.
396+
397+
Defaults: 60 kWh capacity (average EV), 0.3 initial SoC (typical "low" level). For optimization, if unknown: Assume infinite capacity (no SoC-clamp) or conservative (plan for 20 kWh flex). That's enough for launch – accurate SoC is nice-to-have for advanced optimization, but realtime control (limits) is must-have. You can A/B-test API integrations (Tesla/Volvo) post-launch to automate more – e/acc-style, iterate fast.

0 commit comments

Comments
 (0)