Supported Devices
  • All Energy Control Cloud products (CT meters, infrared meter readers, P1 meters, smart plugs, Linky meter readers, etc.)
  • All products using Energy Control Cloud WIFI modules (energy storage systems, inverters, batteries, etc.)
Security Authentication & Encryption
All API calls require strict signature verification to ensure communication security and data integrity.
Step 1: Get Access Credentials
Contact AECC official to obtain exclusive access credentials:
  • companyCode: Enterprise unique identifier (used for request header identity)
  • key: API signature key (only used for local signature generation, never transmit in plaintext)
Credential Security Recommendations:
  • Keys should be stored in server-side config files or environment variables, never hardcoded in client code
  • Regularly review key usage (quarterly recommended); contact official immediately if anomalies found
  • Use different credentials for different environments (test/production)
  • Limit key access to core developers; change keys when personnel leave
Step 2: Construct String to Sign
Follow these fixed rules to concatenate the signature string (incorrect order will cause verification failure):
1 Sort business parameters
Sort all business request parameters (excluding time and sign) by Unicode code point ascending order, concatenate as key=value&key=value format.
2 Append timestamp and key
At the end of the sorted string, append time={UTC+0 timestamp in seconds}&key={assigned key}.
Complete Example
aiMode=0&batRatedCapacity=1&batRatedChargingPower=1000&customTimes=00:00,12:00,1000&13:00,15:00,-2000&dataTime=2025-06-26&energyMode=2&priceCompany=Germany&time=1732756652&key=2a1891544dbcf8e8b45b36d03187485a
Detailed Construction Steps
Step 2.1 Extract business parameters: Extract all business parameters from request body, exclude time and sign fields.
{
  "energyMode": "2",
  "aiMode": "0",
  "customTimes": "00:00,12:00,1000&13:00,15:00,-2000",
  "batRatedCapacity": "1",
  "batRatedChargingPower": "1000",
  "dataTime": "2025-06-26",
  "priceCompany": "Germany"
}
Step 2.2 Sort parameter names: Sort parameter names by Unicode ascending order.
aiMode, batRatedCapacity, batRatedChargingPower, customTimes, dataTime, energyMode, priceCompany
Step 2.3 Concatenate parameter string: Concatenate in sorted order with key=value format, joined by &.
aiMode=0&batRatedCapacity=1&batRatedChargingPower=1000&customTimes=00:00,12:00,1000&13:00,15:00,-2000&dataTime=2025-06-26&energyMode=2&priceCompany=Germany
Step 2.4 Append timestamp and key: Append time and key at the end of the string.
aiMode=0&batRatedCapacity=1&batRatedChargingPower=1000&customTimes=00:00,12:00,1000&13:00,15:00,-2000&dataTime=2025-06-26&energyMode=2&priceCompany=Germany&time=1732756652&key=2a1891544dbcf8e8b45b36d03187485a
⚠️ Notes:
  • Empty string values still participate in signature (e.g., status=&time=...&key=...)
  • Special characters in values do not need URL encoding, use raw values directly
  • Timestamp must use UTC+0 second-level timestamp, consistent with server
  • Key must use the original key assigned by official, no conversion allowed
Step 3: Generate Signature
Use standard MD5 algorithm to hash the concatenated string. The output must be converted to lowercase string as the sign parameter value.
Multi-language Signature Examples
Java Example
import java.security.MessageDigest;
import java.util.TreeMap;

public class SignGenerator {
    public static String generateSign(TreeMap params, String key) {
        try {
            StringBuilder sb = new StringBuilder();
            // Concatenate business parameters
            for (String paramKey : params.keySet()) {
                if (!paramKey.equals("sign")) {
                    sb.append(paramKey).append("=").append(params.get(paramKey)).append("&");
                }
            }
            // Append time and key
            sb.append("time=").append(params.get("time")).append("&key=").append(key);
            
            // MD5 encrypt and convert to lowercase
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest(sb.toString().getBytes("UTF-8"));
            StringBuilder hexString = new StringBuilder();
            for (byte b : digest) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) hexString.append('0');
                hexString.append(hex);
            }
            return hexString.toString().toLowerCase();
        } catch (Exception e) {
            throw new RuntimeException("Signature generation failed", e);
        }
    }
}
Python Example
import hashlib
from urllib.parse import urlencode

def generate_sign(params, key):
    """
    Generate API signature
    :param params: Business parameter dict (contains time)
    :param key: Assigned key
    :return: MD5 signature string (lowercase)
    """
    # Exclude sign field, sort by key
    sorted_params = sorted([(k, v) for k, v in params.items() if k != 'sign'])
    # Concatenate parameters
    param_str = '&'.join([f"{k}={v}" for k, v in sorted_params])
    # Append time and key
    sign_str = f"{param_str}&time={params['time']}&key={key}"
    # MD5 encrypt and convert to lowercase
    return hashlib.md5(sign_str.encode('utf-8')).hexdigest().lower()

# Usage example
params = {
    'energyMode': '2',
    'aiMode': '0',
    'customTimes': '00:00,12:00,1000&13:00,15:00,-2000',
    'batRatedCapacity': '1',
    'batRatedChargingPower': '1000',
    'dataTime': '2025-06-26',
    'priceCompany': 'Germany',
    'time': '1732756652'
}
sign = generate_sign(params, '2a1891544dbcf8e8b45b36d03187485a')
print(f"Generated signature: {sign}")
JavaScript Example
const crypto = require('crypto');

function generateSign(params, key) {
    // Get sorted parameter keys (exclude sign)
    const sortedKeys = Object.keys(params)
        .filter(k => k !== 'sign')
        .sort();
    
    // Concatenate parameter string
    const paramString = sortedKeys
        .map(k => `=`)
        .join('&');
    
    // Append time and key
    const signString = `&time=&key=`;
    
    // MD5 encrypt and convert to lowercase
    return crypto.createHash('md5')
        .update(signString, 'utf8')
        .digest('hex')
        .toLowerCase();
}

// Usage example
const params = {
    energyMode: '2',
    aiMode: '0',
    customTimes: '00:00,12:00,1000&13:00,15:00,-2000',
    batRatedCapacity: '1',
    batRatedChargingPower: '1000',
    dataTime: '2025-06-26',
    priceCompany: 'Germany',
    time: '1732756652'
};
const sign = generateSign(params, '2a1891544dbcf8e8b45b36d03187485a');
console.log('Generated signature:', sign);
C# Example
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

public class SignGenerator
{
    public static string GenerateSign(Dictionary parameters, string key)
    {
        // Exclude sign field, sort by key
        var sortedParams = parameters
            .Where(p => p.Key != "sign")
            .OrderBy(p => p.Key, StringComparer.Ordinal)
            .Select(p => $"{p.Key}={p.Value}");
        
        // Concatenate parameters
        string paramString = string.Join("&", sortedParams);
        
        // Append time and key
        string signString = $"{paramString}&time={parameters["time"]}&key={key}";
        
        // MD5 encrypt and convert to lowercase
        using (var md5 = MD5.Create())
        {
            byte[] hashBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(signString));
            StringBuilder sb = new StringBuilder();
            foreach (byte b in hashBytes)
            {
                sb.Append(b.ToString("x2")); // Convert to lowercase hex
            }
            return sb.ToString();
        }
    }
}
Step 4: Make API Request
  • Header: Must include companyCode: {Enterprise Code} and Accept-Language: en-US.
  • Body: In addition to business parameters, must include time (exact UTC+0 timestamp used in signature) and sign (MD5 signature value).
Complete Request Example (cURL)
curl --location 'https://api.aecc.com/openApi/price/setEnergyMode' \
--header 'companyCode: AECC2024001' \
--header 'Accept-Language: en-US' \
--header 'Content-Type: application/json' \
--data '{
    "energyMode": "2",
    "aiMode": "0",
    "customTimes": "00:00,12:00,1000&13:00,15:00,-2000",
    "batRatedCapacity": "1",
    "batRatedChargingPower": "1000",
    "dataTime": "2025-06-26",
    "priceCompany": "Germany",
    "time": "1732756652",
    "sign": "c3757db87150d5efbb45009d9253d375"
}'
Complete Request Example (Java - HttpClient)
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

public class ApiClient {
    private static final String API_URL = "https://api.aecc.com/openApi/price/setEnergyMode";
    private static final String COMPANY_CODE = "AECC2024001";
    private static final String KEY = "2a1891544dbcf8e8b45b36d03187485a";

    public static void main(String[] args) throws Exception {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(API_URL);
        
        // Set headers
        httpPost.setHeader("companyCode", COMPANY_CODE);
        httpPost.setHeader("Accept-Language", "en-US");
        httpPost.setHeader("Content-Type", "application/json");
        
        // Prepare business parameters
        long currentTime = System.currentTimeMillis() / 1000; // UTC+0 second timestamp
        TreeMap params = new TreeMap<>();
        params.put("energyMode", "2");
        params.put("aiMode", "0");
        params.put("customTimes", "00:00,12:00,1000&13:00,15:00,-2000");
        params.put("batRatedCapacity", "1");
        params.put("batRatedChargingPower", "1000");
        params.put("dataTime", "2025-06-26");
        params.put("priceCompany", "Germany");
        params.put("time", String.valueOf(currentTime));
        
        // Generate signature
        String sign = SignGenerator.generateSign(params, KEY);
        params.put("sign", sign);
        
        // Construct JSON request body
        JSONObject requestBody = new JSONObject();
        for (String key : params.keySet()) {
            requestBody.put(key, params.get(key));
        }
        httpPost.setEntity(new StringEntity(requestBody.toString(), "UTF-8"));
        
        // Send request
        String response = httpClient.execute(httpPost, response -> {
            int statusCode = response.getStatusLine().getStatusCode();
            String responseBody = EntityUtils.toString(response.getEntity());
            System.out.println("Status code: " + statusCode);
            return responseBody;
        });
        System.out.println("Response: " + response);
        httpClient.close();
    }
}
Server-side Verification Process
  1. Identity verification: Query corresponding enterprise key based on companyCode in request header
  2. Timestamp verification: Verify if time parameter is within allowed time range (default ±5 minutes), prevent replay attacks
  3. Signature recalculation: Recalculate signature using same rules (sort params → append time and key → MD5 encrypt)
  4. Signature comparison: Compare calculated result with sign value in request, must match exactly
  5. Business processing: After verification passes, execute business logic and return result
⚠️ Security Notice:
  • Signature validity is strongly tied to timestamp; server validates timestamp effectiveness. Do not cache signatures for reuse.
  • If key is leaked, contact official immediately for reset to avoid security risks.
  • It is recommended to enable HTTPS in production environment to ensure transport layer security.
Core API Examples
The following provides complete calling examples for high-frequency core APIs, covering signature generation, request construction and response parsing.
Example 1: Set Energy Control Mode
POST
/openApi/price/setEnergyMode
Set energy control mode for storage devices (Smart/Custom/Off), and get encrypted control message to send to the collector.
Request Body
Parameter Location Type Required Description
companyCode header string Yes Enterprise unique identifier
Accept-Language header string Yes en-US
time body string Yes UTC+0 timestamp (seconds)
sign body string Yes Signature info
priceCompany body string Yes Price region (e.g., Germany="Germany")
batRatedCapacity body string Yes Battery rated full charge capacity (kWh), 0~100, energy needed to fully charge
batRatedChargingPower body string Yes Battery rated charging power (W), used with capacity to calculate charge time
dataTime body string Yes Today's date (yyyy-MM-dd)
energyMode body int Yes Energy mode: 0=Off (no control), 1=Smart mode, 2=Custom mode
aiMode body int No AI control enable: 0=Disabled, 1=Enabled (only for Smart mode, default 0)
customTimes body string No Custom time periods, format: startTime,endTime,power (e.g., 00:00,12:00,1000&13:00,15:00,-2000). Max 16 periods
Request Example
{
  "energyMode": "2",
  "aiMode": "0",
  "customTimes": "00:00,12:00,1000&13:00,15:00,-2000",
  "batRatedCapacity": "1",
  "batRatedChargingPower": "1000",
  "dataTime": "2025-06-26",
  "priceCompany": "Germany",
  "time": "1732756652",
  "sign": "c3757db87150d5efbb45009d9253d375"
}
Key Response Fields
  • packet: Hexadecimal control message. Requires secondary encryption and CRC16 checksum calculation per device protocol before sending to collector.
  • powerTimes: Time period strategy array, containing charging/discharging power commands for each time period.
Example 2: Get Time-of-Use Electricity Price Data
POST
/openApi/price/getPriceChart
Get time-of-use electricity price data for specified region and date. Provides data support for intelligent control strategies.
Request Body
Parameter Location Type Required Description
companyCode header string Yes Enterprise unique identifier
Accept-Language header string Yes en-US
dataTime body string Yes Date (yyyy-MM-dd)
priceCompany body string Yes Price region (e.g., Germany, France)
mode body string No Price granularity: 0=1 hour, 1=15 minutes (default 0)
time body string Yes UTC+0 timestamp (seconds)
sign body string Yes Signature info
Request Example
{
  "dataTime": "2024-09-07",
  "priceCompany": "Germany",
  "mode": "0",
  "time": "1725677116",
  "sign": "e07b26034722d166e7f059cb728ab3fd"
}
Key Response Fields
  • priceArr: 24-hour electricity price array, unit is EUR/MWh.
  • pricesDayList: Time period detail list, containing start/end time, price value and peak/valley/flat identifier for each period.
Example 3: Get Device Rated Parameters
POST
/openApi/device/getBasicsInfo
Get current device rated parameter data.
Request Body
Parameter Location Type Required Description
companyCode header string Yes Enterprise unique identifier
Accept-Language header string Yes en-US
deviceSn body string Yes Device serial number
time body string Yes UTC+0 timestamp (seconds)
sign body string Yes Signature info
Request Example
{
  "deviceSn": "NB2548300T110CHAB",
  "time": "1725450897",
  "sign": "e83ba9c021edd831ae69033f77528ac5"
}
Key Response Fields
  • obj: Device rated data model.
Example 4: Get Device Real-time Information
POST
/openApi/device/getRealTimeInfo
Get current device real-time information data.
Request Body
Parameter Location Type Required Description
companyCode header string Yes Enterprise unique identifier
Accept-Language header string Yes en-US
deviceSn body string Yes Device serial number
time body string Yes UTC+0 timestamp (seconds)
sign body string Yes Signature info
Request Example
{
  "deviceSn": "NB2548300T110CHAB",
  "time": "1725450897",
  "sign": "e83ba9c021edd831ae69033f77528ac5"
}
Key Response Fields
  • obj: Device real-time data model.
Frequently Asked Questions (FAQ)
Q1: Signature calculation always returns "sign mismatch". How to troubleshoot?

Server recalculates signature using the same rules (sort business params by Unicode → append time and key at end → MD5 → lowercase) and compares with sign in request body. Any mismatch returns result=10001, msg="Signature exception".

Troubleshooting steps (by likelihood):

  1. Confirm sign is 32-character lowercase hex: MD5 output must be converted to lowercase. Using uppercase or Base64 will cause mismatch.
  2. Confirm parameter concatenation order matches original: Parameter names must be sorted by Unicode (dictionary order) ascending. Use TreeMap (Java) / sorted() (Python) / .sort() (JS) for automatic sorting.
  3. Confirm sign and time are not included in business parameter sorting: Only process business parameters during sorting. time is appended at the end, key is appended after time. sign field itself is not included in signature.
  4. Confirm parameter values are concatenated as-is, no URL encoding, no trimming: Empty values still participate (e.g., status=&time=...). Special characters like &, :, , (in customTimes field) are concatenated directly without URL encoding.
  5. Confirm key uses official assigned original key: Server looks up keySecret by companyCode and appends to signature. Client must use the same original key without any encoding/truncation/transformation.
  6. Confirm time in signature original and request body are identical: time must be both concatenated into signature AND placed in request body. Both must be exactly the same (same second-level timestamp string).
  7. Locally reproduce server algorithm: Print the concatenated string and use any online MD5 tool to calculate. If still inconsistent, provide the original string + your sign to official for assistance.
Q2: What is the valid timestamp range? What if server time differs from standard time?
  • Timestamp format: UTC+0 second-level Unix timestamp (10 digits, e.g., 1732756652). Do not use millisecond-level (13 digits) or local time with timezone.
  • Valid range: Server compares current UTC+0 second timestamp with time in request, allowing deviation of ±3600 seconds (±1 hour). Requests outside this window are considered expired, returning result=10001, msg="Signature exception".

Time deviation handling suggestions:

  1. Use server time: Generate time = current UTC+0 second timestamp before each request. Do not cache/reuse historical timestamps because signature is strongly tied to time.
  2. Calibrate server clock: If your server time differs from standard time (NTP) by more than a few tens of seconds, enable NTP clock sync (Linux: ntpd/chrony, Windows: w32time) to prevent clock drift.
  3. Do not try to widen the window: ±1 hour is server fixed policy and cannot be adjusted by client. If deviation exceeds 1 hour, only calibrate the clock.
  4. Container/Cloud environment note: Docker containers and VMs may have clock rollback after restart. Sync time when container starts.
Q3: Can key be passed in request header or URL?

No.

  • key's only purpose: Only participate in local signature calculation on client side (concatenate at end of signature string &key=xxx). It is not a request parameter, not in header, not in URL query.
  • How server gets key: Server looks up corresponding keySecret from database based on companyCode in request header to recalculate signature. companyCode is identity (transmitted with request), key is secret (only stored at both ends, not transmitted in plaintext).
  • Correct approach: companyCode in header: --header 'companyCode: AECC2024001'. key only participates in local MD5, never in any request field.

Security requirement: Keys should be stored in server-side config files or environment variables, never hardcode into client, never print to logs, never send with requests. If suspected leak, immediately contact official for reset.

Q4: What to do when interface times out or returns 5xx errors?

Open API HTTP layer basically returns 200. Business result is expressed through result field in response body. Real system-level exceptions are mapped to result=4000, msg="System error, please contact the administrator!".

  1. Client timeout settings: Set reasonable connection and read timeouts for each call (e.g., connection 5s, read 10s) to avoid threads blocking due to network jitter.
  2. Retry for result=4000 (system error): These are usually temporary server failures. Recommend exponential backoff: 1s → 2s → 4s, max 3 times. Must regenerate time and sign for retry, never reuse original request.
  3. Handle network timeout (no response): When client throws SocketTimeoutException or connection refused, also retry with exponential backoff. If multiple timeouts, first check local network and DNS, then contact official to confirm service status.
  4. Do NOT retry when: result=10001 (signature exception) - retry won't change result, fix signature/timestamp first. result=20001/20004 (business validation errors) - fix parameters before resending. result=10006/20002 (account/permission issues) - contact official.
  5. Rate limiting: Some interfaces have rate limiting configured. When triggered, an error will be returned via global exception. Client should lower call frequency, do not retry immediately.
Q5: Where to check meanings of code and msg fields in response?

Note: The status field in response is actually named result (not code). Below is described as "status code" for easier understanding.

Response structure:

{
  "result": 0,
  "msg": "Request successfully.",
  "data": { /* business data */ }
}
  • result: Status code. 0 means success, non-0 means failure.
  • msg: Status description text, supports i18n. Language determined by Accept-Language header (e.g., en-US, zh-CN), defaults to en-US if not provided.
  • data/obj: Business data carrier, returns specific business object on success.
Common Status Codes Reference Table
result Meaning Typical msg (en-US) Trigger Scenario
0 Success Request successfully. Business processed normally
1 General failure (varies) Business logic validation failed, msg explains details
10000 Not logged in / Token expired Please login. / token expired Token mode: claims invalid or expired
10001 Signature exception Signature exception Signature error, timestamp expired, companyCode/time/sign missing (merged code)
10002 Incorrect email format Incorrect email format. Register/bind email format invalid
10006 User disabled The user has been disabled. Account locked
20000 Incorrect parameter type Incorrect parameter type. Request parameter type mismatch
20001 Data cannot be empty Submitted data cannot be empty. Required parameter missing
20002 Permission exception Abnormal permissions. openApi auth: companyCode not exists, credentials not enabled (flagState!=1), or no operation permission
20003 Time format error Time format error. Date/time parameter format invalid
20004 Parameter format error Parameter format error. Parameter format validation failed
20005 No such parameter No this parameter. Missing required business parameter
20006 System error, try later System error, try later. Server business exception
20007 DeviceCode does not exist DeviceCode does not exist Invalid device type code
20008 Device off-line Device off-line Device has not reported data
20009 Device not exist Device not exist deviceSn not found in system
4000 System global error System error, please contact the administrator! Uncaught exception, transaction rollback, parameter parsing exception (recommend retry or contact official)
Troubleshooting tips:
  • Record complete result, msg and request parameters when calling for easier troubleshooting.
  • If msg is in English and you want to view Chinese, change Accept-Language to zh-CN.
👉 Contact us to get complete documentation or support. 👈
Contact Us