kWh Electric

OEM Integration Platform - Technical Overview

A unified integration layer for connecting energy and smart-device OEM ecosystems - batteries, thermostats, water heaters, and other connected devices - into a single, consistent data and control interface.

1. OEM Integration Overview

Energy and smart-device ecosystems are fragmented. A residential battery, a smart thermostat, and a smart water heater each expose telemetry and control through completely different OEM APIs - different authentication schemes, different data models, different units, different event semantics. Any company building an application on top of multiple device categories (energy management, demand response, home automation, insurance, utility programs) ends up re-solving the same integration problem for every OEM it connects to.

The platform is designed as a standardized integration and normalization layer that sits between OEM device ecosystems and downstream applications. Instead of every application team building and maintaining bespoke connectors to each OEM (e.g., a battery manufacturer's cloud API, a thermostat manufacturer's cloud API, a water heater manufacturer's cloud API), the platform provides:

  • A single integration point for onboarding new OEMs
  • A canonical, cross-OEM data model for devices, telemetry, and events
  • A consistent way to enroll devices and issue control commands, regardless of the underlying OEM protocol

What problems this solves:

  • For downstream applications: one API and one data model instead of N different OEM schemas
  • For the ecosystem: faster time-to-integration when a new OEM or device category is added, since only the OEM-specific mapping layer changes - not the applications built on top

Architecture

flowchart TD A["OEM Devices
Battery / Thermostat / Water Heater / etc."] --> B["OEM Cloud APIs & Event Streams"] B --> C["Integration & Ingestion Layer"] C --> D["Normalization & Data Quality"] D --> E["Unified Device Data Model
Telemetry · Events · Enrollment · Control State"] E --> F["APIs / Events"] F --> G["Downstream Applications"] G -.->|Control Commands| E E -.->|Translated Commands| C C -.->|OEM-specific Command Format| B B -.-> A

Telemetry and enrollment data flow left to right (device → application). Control commands flow right to left (application → device), passing back through the same normalization layer so a command issued in canonical form is translated into the correct OEM-specific call.


2. Integration Methods

Method When it's appropriate Typical data Key considerations
REST APIs On-demand reads, enrollment actions, issuing control commands, low-to-moderate call volume Device metadata, current state, historical telemetry queries, control commands Auth (OAuth2/API key), rate limits, request idempotency for write operations
Event streams / Pub-Sub Near-real-time telemetry and state-change events at scale Telemetry readings, device online/offline events, alarms/diagnostics Consumer offset management, at-least-once delivery, ordering not guaranteed across partitions - see Reliability
Webhooks OEM-initiated push notifications for discrete events (enrollment status change, firmware update, alert) Event payloads, status changes Signature verification, retry/backoff on non-2xx responses, idempotency keys to handle re-delivery
Batch ingestion Historical backfill, OEMs that only expose bulk export (CSV/file-based) rather than live APIs Historical telemetry, device inventories File format validation, deduplication against already-ingested data, late-arriving batch handling

Most production OEM integrations combine at least two of these: a REST API for enrollment/control and either an event stream or webhooks for ongoing telemetry.


3. Data Model

OEM-specific schemas are mapped into a consistent canonical data model. Applications built on the platform interact only with this canonical model and are not exposed to OEM-specific field names, units, or structures.

Core entities:

EntityDescription
DeviceA single connected unit (battery, thermostat, water heater, etc.), identified by a canonical device ID mapped to the OEM's native device ID
Device MetadataMake, model, category, firmware version, install location, capabilities (e.g., supports control vs. telemetry-only)
TelemetryTime-series readings emitted by the device (e.g., state of charge, temperature, power draw)
Enrollment / Connection StateWhether a device is linked, its authorization status, and connection health
EventsDiscrete occurrences - alerts, faults, connectivity changes, firmware updates
Control Command / Command StateA control action issued to a device and its execution status (pending, acknowledged, completed, failed)
Diagnostics / AlertsFault codes, warnings, and health indicators normalized across OEMs where the OEM exposes them

Example: Device Metadata

{
  "device_id": "dev_8f2c1a",
  "canonical_type": "battery",
  "oem": "oem_a",
  "oem_device_id": "PWX-993211",
  "model": "Battery Gen2",
  "firmware_version": "3.4.1",
  "capabilities": ["telemetry", "control"],
  "install_location": {
    "site_id": "site_44a1",
    "timezone": "America/Los_Angeles"
  },
  "status": "active"
}

Example: Telemetry Event

{
  "device_id": "dev_8f2c1a",
  "timestamp": "2026-08-31T09:12:04Z",
  "metric": "state_of_charge",
  "value": 62.5,
  "unit": "percent",
  "source": "event_stream"
}

Example: Device Event

{
  "device_id": "dev_9a01f7",
  "event_type": "connectivity_lost",
  "severity": "warning",
  "timestamp": "2026-08-31T09:15:41Z",
  "details": {
    "last_seen": "2026-08-31T08:52:10Z"
  }
}

4. OEM Data Mapping

Each OEM exposes its own field names, units, and structures. The mapping layer translates each OEM's native schema into the canonical model above, so downstream applications never handle OEM-specific formats directly.

OEM field (example)Canonical field
battery_soc_pct (OEM A)telemetry.state_of_charge (unit: percent)
soc (OEM B, 0–1 scale)telemetry.state_of_charge (converted to percent)
tstat_setpoint_f (OEM C)telemetry.target_temperature (unit: celsius, converted)
dev_uid (OEM A)device.oem_device_id
serial (OEM B)device.oem_device_id

Mapping also normalizes:

  • Units - all canonical values use a fixed unit per metric; OEM-native units are converted at ingestion
  • Timestamps - all timestamps normalized to UTC ISO 8601, regardless of the timezone or format the OEM provides
  • Enumerations - OEM-specific status codes are mapped to a fixed canonical enum (e.g., varying "online/active/connected" states become a single connected value)
  • Missing values - represented explicitly as null rather than omitted, so applications can distinguish "not reported by OEM" from "zero"
  • Optional fields - canonical schema marks fields as optional where not all OEMs/device categories provide them (e.g., firmware version may be unavailable for some water heater OEMs)
  • OEM-specific extensions - fields with no canonical equivalent are preserved under a namespaced oem_extensions object rather than dropped, so no OEM data is silently lost
{
  "device_id": "dev_8f2c1a",
  "target_temperature": 51.7,
  "unit": "celsius",
  "oem_extensions": {
    "oem_a": {
      "eco_mode": true
    }
  }
}

5. Device Enrollment

Enrollment is the process by which a device account (typically an end customer's OEM account or a specific device) is linked into the platform so telemetry and control become available.

Typical enrollment flow:

  1. Authorization - customer authorizes access via OEM OAuth flow (or OEM-provided linking mechanism)
  2. Device discovery - platform queries the OEM API to enumerate devices available under the authorized account
  3. Mapping to canonical model - each discovered device is assigned a canonical device_id and its metadata is normalized
  4. Capability detection - platform records which capabilities (telemetry read, control write) the device/OEM actually supports, since this varies by OEM and device tier
  5. Activation - device begins ingesting telemetry and becomes eligible for control commands, if supported

Exact enrollment UX and OAuth flow details are OEM-specific and defined per integration.


6. Device Control

Where an OEM supports it, the platform allows control commands to be issued to a device using the canonical model rather than the OEM's native command format.

  • Commands are submitted against a canonical device_id with a canonical command type (e.g., set_target_temperature, set_charge_mode)
  • The platform translates the command into the OEM-specific API call or message format
  • Command execution is tracked as asynchronous state: pending → acknowledged → completed | failed, since most OEM devices do not confirm control actions synchronously
  • Not all devices or OEMs support control for all parameters - supported control actions are exposed per device via its capability metadata (see Data Model)

Which specific control actions are supported per OEM/device category is defined during that OEM's integration and documented separately.


7. Integration Lifecycle

StageWhat happens
1. Authentication / access setupCredentials or OAuth client configured for the OEM's API; scopes and permissions confirmed
2. Schema discoveryOEM's data model, available endpoints/streams, and supported device categories are catalogued
3. Field mappingOEM fields are mapped to the canonical model, including units, enums, and any OEM-specific extensions
4. Initial / backfill ingestionHistorical or existing device data is ingested to populate the canonical model before real-time ingestion begins
5. Real-time ingestionLive telemetry and events begin flowing via the appropriate integration method (stream, webhook, or polling)
6. Validation & reconciliationIngested data is checked against OEM source data for completeness and correctness before rollout
7. Production rolloutIntegration is enabled for live customer devices under that OEM
8. MonitoringOngoing tracking of ingestion health, data freshness, and error rates for the integration

8. Reliability & Data Quality

Production integrations across multiple OEM data sources need to handle inconsistency at the source. The platform's normalization layer is designed around the following:

  • Duplicate messages - ingestion is idempotent per event using a deterministic key (device ID + timestamp + metric), so re-delivered messages don't produce duplicate records
  • Out-of-order events - events are ordered by their canonical timestamp rather than arrival order; late-arriving data is inserted into its correct time position rather than appended
  • Missing telemetry - gaps are represented explicitly rather than interpolated, so applications can distinguish "device didn't report" from "value was zero"
  • Delayed data - ingestion timestamp and event timestamp are tracked separately so downstream consumers can identify and handle lag
  • Invalid values - values outside expected physical ranges (e.g., negative percentage) are flagged and quarantined rather than passed through silently
  • Schema changes - OEM schema/version changes are handled at the mapping layer so canonical output remains stable even if an OEM changes its native API
  • Retries - failed ingestion or command delivery is retried with backoff; retry behavior is method-specific (see Integration Methods)
  • Timestamp normalization - all timestamps are converted to UTC at ingestion, regardless of source format or timezone

9. API Reference - Minimal Example

The following illustrates the shape of the canonical API. It is a representative example, not a complete or currently implemented specification.

GET /devices

Returns devices enrolled under the authenticated account.

GET /devices
{
  "devices": [
    { "device_id": "dev_8f2c1a", "canonical_type": "battery", "status": "active" },
    { "device_id": "dev_9a01f7", "canonical_type": "thermostat", "status": "active" }
  ]
}

GET /devices/{device_id}

Returns canonical metadata for a specific device.

GET /devices/dev_8f2c1a
{
  "device_id": "dev_8f2c1a",
  "canonical_type": "battery",
  "model": "Battery Gen2",
  "capabilities": ["telemetry", "control"],
  "status": "active"
}

GET /devices/{device_id}/telemetry

Returns telemetry readings for a device over a time range.

GET /devices/dev_8f2c1a/telemetry?metric=state_of_charge&since=2026-08-30T00:00:00Z
{
  "device_id": "dev_8f2c1a",
  "metric": "state_of_charge",
  "readings": [
    { "timestamp": "2026-08-30T00:00:00Z", "value": 58.2 },
    { "timestamp": "2026-08-30T01:00:00Z", "value": 59.0 }
  ]
}

POST /devices/{device_id}/commands

Issues a control command to a device that supports it.

POST /devices/dev_8f2c1a/commands
{
  "command_type": "set_charge_mode",
  "parameters": { "mode": "backup_reserve" }
}
{
  "command_id": "cmd_3391a",
  "device_id": "dev_8f2c1a",
  "status": "pending"
}

10. OEM Integration FAQ

How do we authenticate to the platform?

Access is scoped per OEM integration using OAuth2 or API-key based credentials. Exact flow details are finalized per OEM during onboarding.

How are OEM-specific fields handled?

Fields with no canonical equivalent are preserved under an oem_extensions namespace rather than discarded, so no data is lost even if it isn't part of the standard model.

How is real-time data delivered?

Via event streams or webhooks, depending on what the OEM supports. Both are consumed into the same canonical model on the platform side.

What happens when messages arrive out of order?

Events are re-ordered by canonical timestamp on ingestion, not by arrival order.

How are duplicate events handled?

Ingestion is idempotent per event using a deterministic key, so re-delivered messages don't create duplicate records.

How are schema changes on the OEM side managed?

Schema changes are absorbed at the OEM-specific mapping layer, so the canonical model and downstream applications remain unaffected.

Can historical data be ingested?

Yes - batch/backfill ingestion is part of the standard integration lifecycle, run before real-time ingestion is enabled.

How is device identity maintained across systems?

Each device receives a canonical device_id that is mapped to the OEM's native device identifier and remains stable even if OEM-side identifiers change.

How are OEM-specific extensions supported without breaking the canonical model?

They're additive - stored alongside the canonical fields rather than replacing or altering the core schema.

Is device control supported, or only telemetry?

Both, where the OEM and device support it. Control commands are issued in canonical form and translated to the OEM-specific format; supported actions vary by device and are exposed via device capability metadata.

How is an integration monitored once in production?

Through ingestion health, data freshness, and error-rate tracking established during the monitoring stage of the integration lifecycle.

Updated September 2026