Data Models
Novacore's data model architecture covering identities, organizations, sites, devices, and distributed energy resources.
Platform Hierarchy
Novacore organizes energy resources in a five-level hierarchy:
IDENTITY → ORGANIZATION → SITE → DEVICE → DER
IDENTITY (Authentication Layer)
An Identity is the authentication primitive in Novacore — a public key bound to a type:
- Uniquely identified by a public key (Ed25519 or ES256)
- Types:
user,gateway,device,integration,admin,root - ID format: prefixed text (e.g.,
usr-...,zap-12345,smd-wallbox-001) - An identity can belong to multiple organizations
Human identities (user, admin) authenticate with Ed25519 keys. Machine identities (gateway, device, integration) authenticate with ES256 keys.
See Authentication for details on the challenge/verify flow.
ORGANIZATION (Ownership Layer)
An Organization is the root ownership entity:
- Owns sites, devices, and gateways
- Has an owner (the identity that created it)
- Members with roles: owner, admin, operator, viewer
- The boundary for access control and billing
See Organizations for details on roles and membership.
SITE (Logical Grouping)
A Site represents a complete energy system — everything "behind the meter":
- The logical boundary for energy optimization
- Typically corresponds to a physical location (home, building, facility)
- Contains all energy resources at that location
- The level where energy flows are balanced and optimized
- Belongs to exactly one organization
Example Site composition:
- Grid connection point (meter)
- Solar panels (PV)
- Battery storage
- EV charger
- Hybrid inverter
DEVICE (Physical Connection Point)
A Device represents the physical hardware you communicate with:
- The actual communication endpoint (Modbus address, MQTT client, P1 port)
- Often the electrical connection point
- What the Zap directly talks to via protocols
- Links to a gateway via
comm_identity_id
Device types:
| Type | Description |
|---|---|
inverter | Solar/hybrid inverter |
battery | Battery storage system |
ev_charger | EV charger |
v2x_charger | Bidirectional EV charger |
energy_meter | Grid or sub meter |
Device states:
| State | Description |
|---|---|
pending | Newly created, not yet active |
active | Operational, sending telemetry |
stale | No recent telemetry |
error | Hardware fault detected |
disconnected | No connection |
orphaned | Gateway reclaimed by different org |
One Device may expose multiple DERs.
DER (Distributed Energy Resource)
A DER is the logical representation of an energy resource or function:
- What the energy system "sees" and models
- The unit of energy generation, storage, or consumption
- Can be a physical component or a logical representation
DERs are often representations of capabilities "under" a Device, not always directly addressable entities.
Example: Hybrid Inverter
- DEVICE: The inverter itself (communication point via Modbus)
- DER #1: Solar PV (generation capability)
- DER #2: Battery (storage capability)
- DER #3: Grid connection (import/export measurement)
DER Types:
| Type | Description | Allowed on Device Types |
|---|---|---|
solar | Solar generation | inverter |
battery | Energy storage | inverter, battery |
meter | Grid import/export measurement | inverter, battery, energy_meter |
ev_charger_port | EV charger port | ev_charger, v2x_charger |
Hierarchy Example
IDENTITY: usr-paul-abc123 (Ed25519, user)
└─ ORGANIZATION: org-sourceful-home
├─ Members: paul (owner), tobias (admin), johan (operator)
└─ SITE: sit-main-street-42
├─ GATEWAY: zap-04772a97 (claimed to this org)
├─ DEVICE: dev-hybrid-inverter-01 (inverter, Modbus-TCP)
│ ├─ DER: pv-rooftop (solar)
│ └─ DER: battery-01 (battery)
├─ DEVICE: dev-v2x-charger-01 (v2x_charger, MQTT)
│ └─ DER: ev-port-1 (ev_charger_port)
└─ DEVICE: dev-smart-meter-01 (energy_meter, P1)
└─ DER: grid-meter (meter)
Telemetry Data Models
Units and Conventions
Units:
| Measurement | Unit |
|---|---|
| Power | W (watts) |
| Energy | Wh (watt-hours) |
| Voltage | V (volts) |
| Current | A (amperes) |
| Frequency | Hz (hertz) |
| Temperature | C (Celsius) |
| State of Charge | fraction (0.0 - 1.0) |
| Time | s or ms |
Sign Conventions:
| Condition | Sign |
|---|---|
| Generation (PV) | Negative |
| Charging (Battery) | Positive |
| Discharging (Battery) | Negative |
| Import (Meter) | Positive |
| Export (Meter) | Negative |
See Core Principles for detailed sign convention rules.
BaseDeviceData
Common fields shared by all device types:
| Field | Type | Description |
|---|---|---|
type | string | Object type ("pv", "battery", "meter", "v2x_charger") |
make | string | Manufacturer/brand name (optional) |
timestamp | integer | Timestamp of reading start (ms) |
read_time_ms | integer | Time taken to complete the reading |
PV (Solar) Data Model
{
"type": "pv",
"make": "Deye",
"W": -1500,
"rated_power_W": 3000,
"mppt1_V": 400,
"mppt1_A": -3.75,
"mppt2_V": 380,
"mppt2_A": -3.68,
"heatsink_C": 45,
"total_generation_Wh": 15000
}
| Field | Unit | Type | Description |
|---|---|---|---|
W | W | integer | Power Generation (always negative) |
rated_power_W | W | integer | System Rated Power |
mppt1_V | V | float | MPPT1 Voltage |
mppt1_A | A | float | MPPT1 Current |
mppt2_V | V | float | MPPT2 Voltage |
mppt2_A | A | float | MPPT2 Current |
heatsink_C | C | float | Inverter Temperature |
total_generation_Wh | Wh | integer | Total Energy Generated |
Battery Data Model
{
"type": "battery",
"make": "Tesla",
"W": 500,
"A": 10.5,
"V": 48.2,
"SoC_nom_fract": 0.75,
"heatsink_C": 25,
"total_charge_Wh": 8000,
"total_discharge_Wh": 7200
}
| Field | Unit | Type | Description |
|---|---|---|---|
W | W | integer | Active Power (+ charge, - discharge) |
A | A | float | Current (+ charge, - discharge) |
V | V | float | Voltage |
SoC_nom_fract | fraction | float | State of Charge (0.0-1.0) |
heatsink_C | C | float | Battery Temperature |
total_charge_Wh | Wh | integer | Total Energy Charged |
total_discharge_Wh | Wh | integer | Total Energy Discharged |
Meter Data Model
{
"type": "meter",
"make": "Kamstrup",
"W": 1200,
"Hz": 50.0,
"L1_V": 230,
"L1_A": 5.2,
"L1_W": 400,
"L2_V": 229,
"L2_A": 5.1,
"L2_W": 380,
"L3_V": 231,
"L3_A": 5.3,
"L3_W": 420,
"total_import_Wh": 25000,
"total_export_Wh": 18000
}
| Field | Unit | Type | Description |
|---|---|---|---|
W | W | integer | Total Active Power (+ import, - export) |
Hz | Hz | float | Grid Frequency |
L1_V | V | float | L1 Phase Voltage |
L1_A | A | float | L1 Phase Current |
L1_W | W | float | L1 Phase Power |
L2_V | V | float | L2 Phase Voltage |
L2_A | A | float | L2 Phase Current |
L2_W | W | float | L2 Phase Power |
L3_V | V | float | L3 Phase Voltage |
L3_A | A | float | L3 Phase Current |
L3_W | W | float | L3 Phase Power |
total_import_Wh | Wh | integer | Total Energy Imported |
total_export_Wh | Wh | integer | Total Energy Exported |
EV Charger Data Model
Telemetry for AC EV chargers (sourced from OCPP 1.6 / 2.0.1). Each connector on a charger is published as its own DER. For bidirectional V2X chargers, see V2X Charger Data Model.
{
"type": "ev_charger_port",
"make": "Zaptec",
"connector_status": "occupied",
"charging_state": "charging",
"W": 7200,
"L1_V": 230.5,
"L1_A": 10.4,
"L2_V": 231.2,
"L2_A": 10.3,
"L3_V": 229.8,
"L3_A": 10.5,
"total_charge_Wh": 125000,
"session_charge_Wh": 5000,
"session_id": 12345,
"upper_limit_W": [0, 11000],
"lower_limit_W": [0, 4500]
}
| Field | Unit | Type | Description |
|---|---|---|---|
connector_status | - | string | Connector availability (see Connector Status) |
charging_state | - | string | Vehicle/session charging state (see Charging State) |
status | - | string | Deprecated. Use connector_status + charging_state instead |
plug_connected | - | boolean | Deprecated. Use connector_status instead |
W | W | integer | Active power delivered to the EV |
L1_V / L2_V / L3_V | V | float | Phase Voltage |
L1_A / L2_A / L3_A | A | float | Phase Current |
total_charge_Wh | Wh | integer | Lifetime energy delivered to vehicles |
session_charge_Wh | Wh | integer | Energy delivered in the current session |
session_id | - | integer | OCPP transaction ID for the active session, null when no session |
upper_limit_W | W | int array | Upper bound of each allowed power band (see Power Limits) |
lower_limit_W | W | int array | Lower bound of each allowed power band (see Power Limits) |
V2X Charger Data Model
{
"type": "v2x_charger",
"make": "Ferroamp",
"connector_status": "occupied",
"charging_state": "charging",
"protocol": "ISO_15118_20",
"control_mode": "bi_dir",
"plug_connected": true,
"W": 5300,
"ac_W": 5300,
"A": 23.0,
"V": 230.5,
"Hz": 49.98,
"L1_V": 232.8,
"L1_A": 23.0,
"L1_W": 5354,
"dc_W": 5100,
"dc_V": 400.0,
"dc_A": 12.75,
"vehicle_soc_fract": 0.55,
"session_charge_Wh": 1500,
"session_discharge_Wh": 0,
"total_charge_Wh": 142000,
"total_discharge_Wh": 5100
}
| Field | Unit | Type | Description |
|---|---|---|---|
connector_status | - | string | Connector availability (see Connector Status) |
charging_state | - | string | Vehicle/session charging state (see Charging State) |
status | - | string | Deprecated. Use connector_status + charging_state instead |
protocol | - | string | Active protocol (e.g., ISO_15118_2, ISO_15118_20, DIN) |
control_mode | - | string | "unknown", "bi_dir" (bidirectional), or "uni_dir" (charge only) |
plug_connected | - | boolean | Deprecated. Use connector_status instead |
W | W | integer | Active Power, derived from ac_W or dc_W * 0.95. (+) Charging, (-) V2G |
ac_W | W | integer | AC Active Power (+) Charging, (-) V2G |
A | A | float | AC Grid Current (Total) |
V | V | float | AC Grid Voltage (Average) |
Hz | Hz | float | Grid Frequency |
L1_V / L2_V / L3_V | V | float | Phase Voltage |
L1_A / L2_A / L3_A | A | float | Phase Current |
L1_W / L2_W / L3_W | W | float | Phase Power |
dc_W | W | integer | DC Battery Power |
dc_V | V | float | DC Voltage level of the EV battery |
dc_A | A | float | DC Current flow |
vehicle_soc_fract | fraction | float | Vehicle State of Charge (0.0 - 1.0) |
ev_target_energy_req_Wh | Wh | integer | Energy needed to reach driver's target SoC |
ev_max_energy_req_Wh | Wh | integer | Empty space in battery available for charging |
ev_min_energy_req_Wh | Wh | integer | Energy available for V2G export |
session_charge_Wh | Wh | integer | Energy imported by EV this session |
session_discharge_Wh | Wh | integer | Energy exported by EV this session |
total_charge_Wh | Wh | integer | Lifetime energy delivered to EV |
total_discharge_Wh | Wh | integer | Lifetime energy exported from EV (V2G) |
Connector Status
Based on OCPP 2.0.1, with preparing and finishing retained from OCPP 1.6 for backward compatibility.
| Value | Meaning |
|---|---|
"unknown" | State not yet determined |
"available" | Ready, nothing plugged in |
"preparing" | Cable plugged in, session not started yet |
"finishing" | Session ending, cable still plugged in |
"reserved" | Locked for a specific user |
"unavailable" | Intentionally offline (maintenance, admin) |
"faulted" | Hardware/software fault |
"occupied" | In use (charging, discharging, or connected) |
Charging State
Based on OCPP 2.0.1, extended with discharging for V2G.
| Value | Meaning |
|---|---|
"unknown" | State not yet determined |
"charging" | Energy actively flowing to vehicle |
"discharging" | Energy flowing from vehicle to grid (V2G) |
"suspended_ev" | Vehicle not accepting power |
"suspended_evse" | Charger not delivering power |
"ev_connected" | Cable plugged in, no energy flowing yet |
"idle" | Transaction open but no EV connected |
Power Limits
upper_limit_W and lower_limit_W are arrays that together describe the allowed power bands. Each index i defines a band [lower_limit_W[i], upper_limit_W[i]]. The charger may operate at any power inside any of these bands; values between bands are not allowed.
AC EV charger (charge only) — typically two bands: idle and charge.
"upper_limit_W": [0, 11000],
"lower_limit_W": [0, 4500]
- Band 0:
[0, 0]— idle (0 W is always allowed) - Band 1:
[4500, 11000]— charging band
The charger can run at 0 W or anywhere from 4500 W to 11000 W. Values like 3000 W are not valid (they fall between bands).
When charging is not allowed at all (e.g., faulted or finishing), both arrays collapse to [0].
V2X charger (bidirectional) — typically three bands: discharge, idle, charge.
"upper_limit_W": [-500, 50, 7000],
"lower_limit_W": [-7000, -50, 500]
- Band 0:
[-7000, -500]— discharge (V2G) band - Band 1:
[-50, 50]— idle band - Band 2:
[500, 7000]— charge band
Related Documentation
- Core Principles - Sign conventions, units, time sourcing
- Organizations - Org model and roles
- Devices & Gateways API - Device provisioning endpoints
- Telemetry API - Accessing telemetry data