ZenovaConnect

Device data for your energy application

ZenovaConnect is for teams already building energy software: monitoring SaaS, asset operations platforms, VPPs, EMS, storage aggregators, C&I energy-carbon platforms, or in-house enterprise systems. You do not need to write one parser per vendor — we normalize disparate device sources into stable entities, metrics, alarms, and time-series, then deliver them through the channel you choose.

5-step quickstart

Get an API key → add vendor credentials → register webhook → receive data → expand to more vendors.

  1. Request your API key

    Submit the contact form; our sales engineering team issues a sandbox account (username + password) within one business day. The Free tier includes a permanent 10-device quota.

  2. Add vendor credentials

    Once you have an account, exchange username/password for an access_token. All subsequent API calls carry this token via the Authorization header:

    add-vendor.sh
    # After receiving credentials, exchange username/password for an access_token
    curl -X POST 'https://api.zenovaconnect.com/sol-emqx/login' \
      -d 'username=YOUR_USERNAME' \
      -d 'password=YOUR_PASSWORD'
    
    # Response:
    # {
    #   "success": true,
    #   "code": 200,
    #   "result": { "access_token": "eyJ..." }
    # }
  3. Register your webhook

    After getting the token, hand us the vendor account credentials (issued by the vendor developer portal) and call the sync endpoint to pull every plant + device under that account:

    register-webhook.py
    import requests
    
    TOKEN = "eyJ..."
    headers = {"Authorization": f"Bearer {TOKEN}"}
    
    # Sync all plants and devices under a vendor account
    resp = requests.get(
        "https://api.zenovaconnect.com/apiDevice/syncApiPlantAndDevice",
        headers=headers,
        params={"dataAccessUserId": "your-account-id"}
    )
    
    plants = resp.json()["result"]
    print(f"Synced {len(plants)} plants")
  4. Receive incoming data

    Implement a webhook endpoint and we'll push device data in the unified BaseData schema. Verify the HMAC signature first, then handle the payload:

    receive-webhook.js
    // Your webhook endpoint receives device data pushed by ZenovaConnect
    app.post('/webhooks/zenova', (req, res) => {
      const payload = req.body;
    
      // Verify the signature (HMAC-SHA256)
      const sig = req.headers['x-zenova-signature'];
      if (!verifyHmac(payload, sig, process.env.ZENOVA_SECRET)) {
        return res.status(401).end();
      }
    
      // payload follows the unified BaseData schema
      const { DataSources, InverterSN, EDay, PowerACOutputActive } = payload;
      console.log(`[${DataSources}] ${InverterSN} today ${EDay} kWh`);
    
      res.status(200).end();
    });
  5. Scale to more vendors

    Adding more vendor accounts is just repeating Step 3. ZenovaConnect natively supports 15 vendors today (Huawei, Sungrow, Goodwe, Solis, Growatt, Sineng, Aiswei, Sofar, Hopewind, APsystems, Maitian, Sigenergy, Hoymiles, Yibang, Hikvision). Bulk device management, plant grouping, and online/offline events are all available via the OpenAPI.

Unified Schema

No matter which vendor it came from, the fields, units, and precision are the same.

Why this matters

15+ vendors use 15+ different field names, units, and timezone conventions. We normalize them into a single 50+ field BaseData schema and push that to you — write your parser once, and adding new vendors never touches your code.

Below are two real-shape samples from different vendors (BaseData schema). 50+ fields cover inverter, collector, and component-level operating data — three-phase voltage/current (UAC1/IAC1/UAC2/IAC2/UAC3/IAC3) + multi-MPPT inputs (UPV1/IPV1...) + alarm arrays. Notice the DataSources field tells you which vendor it came from, InverterSN carries the real device SN, and every other field is identical across vendors:

From Goodwe (SEMS Portal)

goodwe-payload.json
{
  "deviceType": "inverter",
  "CollectorSN": "C1234567890",
  "InverterSN": "INV20250526001",
  "DataSources": "goodwe",
  "Timezone": "GMT+8",
  "Datetime": 1748246400,
  "currentDateTime": "2026-05-26 12:00:00",
  "State": "1",
  "StateDesc": "正常发电",
  "EDay": 35.6,
  "ETotal": 128500.8,
  "PowerACOutputActive": 8.75,
  "Temperature": 42.5,
  "FrqAC": 50.01,
  "UAC1": 231.5,
  "IAC1": 12.6,
  "UPV1": 620.5,
  "IPV1": 9.8,
  "AlertArray": [],
  "turnonTime": "2026-05-26 06:30:00"
}

From Huawei (FusionSolar)

huawei-payload.json
{
  "deviceType": "inverter",
  "CollectorSN": "C9876543210",
  "InverterSN": "INV20250526002",
  "DataSources": "huawei",
  "Timezone": "GMT+8",
  "Datetime": 1748246400,
  "currentDateTime": "2026-05-26 12:00:00",
  "State": "1",
  "StateDesc": "正常发电",
  "EDay": 41.2,
  "ETotal": 215800.4,
  "PowerACOutputActive": 9.12,
  "Temperature": 38.9,
  "FrqAC": 49.99,
  "UAC1": 230.8,
  "IAC1": 13.2,
  "UPV1": 615.7,
  "IPV1": 10.1,
  "AlertArray": [],
  "turnonTime": "2026-05-26 06:28:00"
}

The 50+ BaseData fields, timestamp timezone, and units (kWh / kW / V / A / Hz / °C) are uniform across every vendor. Your parsing logic gets written once — and the DataSources field always tells you which vendor a payload came from, which makes debugging trivial.

BaseData Schema field reference (excerpt)
FieldTypeDescription
deviceTypestringDevice type (always "inverter" for inverters)
DataSourcesstringVendor source (huawei / sungrow / goodwe... matches the /protocols matrix)
InverterSNstringVendor-native inverter serial number
DatetimelongSample timestamp (Unix seconds); currentDateTime gives the human-readable form
State / StateDescstringInverter status code + localized description, e.g. "1" / "正常发电"
EDaydoubleEnergy generated today (kWh)
ETotaldoubleLifetime energy generated (kWh)
PowerACOutputActivedoubleCurrent AC active power output (kW)
UAC1 / UAC2 / UAC3doubleThree-phase AC voltage (V) — industrial-grade three-phase data
IAC1 / IAC2 / IAC3doubleThree-phase AC current (A)
UPV1 – UPV32doublePer-string DC voltage (V), up to 32 strings
IPV1 – IPV32doublePer-string DC current (A), up to 32 strings
TemperaturedoubleInverter case temperature (°C)
AlertArray / WarnArray / StatusArrayarrayAlarm / warning / status arrays, passed through as-is

Push channels

Pick the channel that fits your stack. HTTP, Kafka, or MQTT.

HTTP Webhook

Universal. Any language or stack.

Fast onboarding and SaaS integrations.

security:
TLS 1.3 + HMAC
throughput:
10k req/s

Apache Kafka

High-throughput consumption from SaaS clusters.

Multi-consumer pipelines, data platforms, and event-driven architectures.

security:
SASL_SSL + TLS
throughput:
100k msg/s

MQTT 5.0

Standard IoT protocol, low-bandwidth friendly.

Real-time subscription, edge, and IoT scenarios.

security:
TLS + ACL + JWT
throughput:
30k msg/s

Delivery semantics: signatures, retries, idempotency

Every payload your system receives is signed, replayable, and carries an idempotency key. Three things to wire up at integration time.

HMAC signature verification

Webhook requests carry an X-ZenovaConnect-Signature header — HMAC-SHA256 over the raw body using your shared secret. Kafka uses SASL_SSL + TLS, MQTT uses TLS + JWT/ACL — no extra signing needed there. Your receiver must verify the signature before processing and respond with 401 on mismatch.

Retry policy

Webhook: non-2xx responses or timeouts (default 10s) trigger exponential backoff for up to 8 attempts spanning ~24 hours. Kafka uses native acks + consumer offsets, and we retry until ack. MQTT uses QoS 1; the broker handles redelivery. Your handler must be idempotent.

Idempotency key (idempotency_key)

Each payload carries an idempotency_key derived from vendor + device_id + ts, globally unique. Retries keep the same key — dedupe on it in your DB or Redis and the same device-timestamp event is processed exactly once.

Ready to start?

Request technical onboarding — our team will be in touch within one business day with a sandbox API key and credential setup guide.

Talk to engineering