AECC 能管系统 SDK 接入指南

安全鉴权与加密流程

所有开放接口调用均需通过严格的签名验证,确保通信安全与数据完整性。

1. 获取接入凭证

联系 AECC 官方获取专属接入凭证:

凭证安全管理建议:

2. 构造待签名字符串

按以下固定规则拼接签名原文,顺序错误将导致验签失败:

详细构造步骤:

步骤 2.1:提取业务参数 从请求体中提取所有业务参数,排除 time 和 sign 字段:

{
  "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"
}
步骤 2.2:参数名排序
按 Unicode 编码升序排列参数名:
aiMode, batRatedCapacity, batRatedChargingPower, customTimes, dataTime, energyMode, priceCompany

步骤 2.3:拼接参数字符串 按排序结果拼接为 key=value 格式,用 & 连接:

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

步骤 2.4:追加时间戳与密钥 在字符串末尾追加 time 和 key:

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

注意事项:

3. 生成签名值

使用标准 MD5 算法对拼接完成的待签名字符串进行哈希运算,输出结果必须转换为 全小写 字符串,作为 sign 参数值。

多语言签名示例:

Java 示例:

import java.security.MessageDigest;
import java.util.TreeMap;

public class SignGenerator {
    
    public static String generateSign(TreeMap<String, String> params, String key) {
        try {
            // 参数已排序(TreeMap自动排序)
            StringBuilder sb = new StringBuilder();
            
            // 拼接业务参数
            for (String paramKey : params.keySet()) {
                if (!paramKey.equals("sign")) {  // 排除sign字段
                    sb.append(paramKey).append("=").append(params.get(paramKey)).append("&");
                }
            }
            
            // 追加time和key
            sb.append("time=").append(params.get("time")).append("&key=").append(key);
            
            // MD5加密并转小写
            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("签名生成失败", e);
        }
    }
}
Python 示例:
import hashlib
from urllib.parse import urlencode

def generate_sign(params, key):
    """
    生成API签名
    :param params: 业务参数字典(包含time)
    :param key: 分配的密钥
    :return: MD5签名字符串(小写)
    """
    # 排除sign字段,按key排序
    sorted_params = sorted([(k, v) for k, v in params.items() if k != 'sign'])
    
    # 拼接参数
    param_str = '&'.join([f"{k}={v}" for k, v in sorted_params])
    
    # 追加time和key
    sign_str = f"{param_str}&time={params['time']}&key={key}"
    
    # MD5加密并转小写
    return hashlib.md5(sign_str.encode('utf-8')).hexdigest().lower()

# 使用示例
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"生成的签名: {sign}")
JavaScript 实现:
const crypto = require('crypto');

/**
 * 生成API签名
 * @param {Object} params - 业务参数对象(包含time)
 * @param {string} key - 分配的密钥
 * @returns {string} MD5签名字符串(小写)
 */
function generateSign(params, key) {
    // 获取排序后的参数键(排除sign)
    const sortedKeys = Object.keys(params)
        .filter(k => k !== 'sign')
        .sort();
    
    // 拼接参数字符串
    const paramString = sortedKeys
        .map(k => `${k}=${params[k]}`)
        .join('&');
    
    // 追加time和key
    const signString = `${paramString}&time=${params.time}&key=${key}`;
    
    // MD5加密并转小写
    return crypto.createHash('md5')
        .update(signString, 'utf8')
        .digest('hex')
        .toLowerCase();
}

// 使用示例
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('生成的签名:', sign);
C# 实现:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

public class SignGenerator
{
    public static string GenerateSign(Dictionary<string, string> parameters, string key)
    {
        // 排除sign字段,按key排序
        var sortedParams = parameters
            .Where(p => p.Key != "sign")
            .OrderBy(p => p.Key, StringComparer.Ordinal)
            .Select(p => $"{p.Key}={p.Value}");
        
        // 拼接参数
        string paramString = string.Join("&", sortedParams);
        
        // 追加time和key
        string signString = $"{paramString}&time={parameters["time"]}&key={key}";
        
        // MD5加密并转小写
        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"));  // 转小写十六进制
            }
            return sb.ToString();
        }
    }
}

4. 发起 API 请求

完整请求示例(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"
}'
完整请求示例(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;
import org.apache.http.util.EntityUtils;

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);
        
        // 设置请求头
        httpPost.setHeader("companyCode", COMPANY_CODE);
        httpPost.setHeader("Accept-Language", "en-US");
        httpPost.setHeader("Content-Type", "application/json");
        
        // 准备业务参数
        long currentTime = System.currentTimeMillis() / 1000;  // UTC+0秒级时间戳
        TreeMap<String, String> 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));
        
        // 生成签名
        String sign = SignGenerator.generateSign(params, KEY);
        params.put("sign", sign);
        
        // 构造JSON请求体
        JSONObject requestBody = new JSONObject();
        for (String key : params.keySet()) {
            requestBody.put(key, params.get(key));
        }
        
        httpPost.setEntity(new StringEntity(requestBody.toString(), "UTF-8"));
        
        // 发送请求
        String response = httpClient.execute(httpPost, response -> {
            int statusCode = response.getStatusLine().getStatusCode();
            String responseBody = EntityUtils.toString(response.getEntity());
            System.out.println("响应状态码: " + statusCode);
            return responseBody;
        });
        
        System.out.println("响应内容: " + response);
        httpClient.close();
    }
}
服务端验签流程:
服务端按以下步骤验证请求合法性:
身份校验:根据请求头中的 companyCode 查询对应的企业密钥

⚠️ 安全须知:

核心接口调用示例

以下按「动态电价」与「设备服务」两类接口提供核心调用示例,覆盖签名生成、请求构造与响应解析全流程。

能源调控与分时电价

动态电价接口地址前缀为 POST /openApi/price/,包含设置能源模式、获取电价区域列表、查询电价、获取电价策略时间段 4 个接口。此类接口为纯服务端计算接口,不涉及具体设备:Body 无需传 deviceSn,由「业务参数 + time + sign」组成,业务参数随接口不同(如 priceCompany、dataTime、energyMode 等),参数值均为 JSON 字符串。请求头与设备接口一致(companyCode、Content-Type: application/json 必选)。

示例一:设置能源调控模式

该接口用于配置储能设备的运行模式(智能/自定义/关闭),并获取下发至采集器的加密控制报文。

POST
/openApi/price/setEnergyMode

请求体示例:

{
  "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"
}
名称位置类型必选中文名说明
companyCodeheaderstring唯一码AECC 分配的调用方识别码,缺失时返回 result=10001
Content-Typeheaderstring固定为 application/json
Accept-Languageheaderstring提示语言(如 en-US、zh-CN),只影响 msg,不影响 result 与业务字段
bodybodyobject业务参数 + time + sign,参数值均为 JSON 字符串
timebodystring秒级时间戳(UTC+0)10 位数字字符串,与服务器时间差须小于 3600 秒
signbodystring签名信息32 位小写 MD5 签名,每次请求重新计算
priceCompanybodystring电价区域德国="Germany"
batRatedCapacitybodystring电池额定充满电量(kWh)0~100 充满需要消耗的能量
batRatedChargingPowerbodystring电池额定充电功率(W)根据电池充电功率和容量计算充电时间来选择最低波谷时间
dataTimebodystring当天日期yyyy-MM-dd
energyModebodyint能源模式0:无模式,采集器不会对设备进行调控;1:智能模式;2:自定义模式
aiModebodyintAI调控使能0:关闭 1:开启,选择智能模式可设置此值,不传默认关闭。
customTimesbodystring自定义时间段用户自定义设置的充放电时间段,多个时间段使用&隔开,最多16个,格式如:00:00,12:00,1000&13:00,15:00,-2000
baseDischargePowerbodyint基础放电功率

关键响应字段:

  • packet:十六进制控制报文,需按设备协议二次加密并计算 CRC16 校验后下发至采集器。
  • powerTimes:时段策略数组,包含各时间段的充放电功率指令。

示例二:查询分时电价数据

该接口用于获取指定区域、指定日期的分时电价信息,为智能调控策略提供数据支撑。

POST
/openApi/price/getPriceChart

请求体示例:

{
  "dataTime": "2024-09-07",
  "priceCompany": "Germany",
  "mode": "0",
  "time": "1725677116",
  "sign": "e07b26034722d166e7f059cb728ab3fd"
}
名称位置类型必选中文名说明
companyCodeheaderstring唯一码AECC 分配的调用方识别码,缺失时返回 result=10001
Content-Typeheaderstring固定为 application/json
Accept-Languageheaderstring提示语言(如 en-US、zh-CN),只影响 msg,不影响 result 与业务字段
bodybodyobject业务参数 + time + sign,参数值均为 JSON 字符串
dataTimebodystring日期yyyy-MM-dd
priceCompanybodystring电价区域如 Germany
modebodystring电价颗粒度0: 1小时 1: 15分钟 默认0
timebodystring秒级时间戳(UTC+0)10 位数字字符串,与服务器时间差须小于 3600 秒
signbodystring签名信息32 位小写 MD5 签名,每次请求重新计算

关键响应字段:

  • priceArr:24小时电价数组,单位为 EUR/MWh。
  • pricesDayList:时段明细列表,包含每个时间段的起止时间、电价数值及峰谷平标识。

逆变器 / 电表接口通用说明

设备接口地址前缀为 POST /openApi/device/。与电价接口不同,设备接口围绕具体设备展开:只需传递 deviceSn,不需要传设备型号代码(DTC),服务端根据授权的 deviceSn 自动路由并返回对应型号结构;查询接口 Body 只有 deviceSn + time + sign。查询接口(示例三、示例四,以及 getSetInfo)不要求设备在线;设置接口(示例五、示例六)要求设备在线,离线返回 result=20008。

本期开放 7 个电表型号,型号与设置的对应关系如下:

型号型号代码(DTC)setParam / setCustomParams
智能无线三相CT电表(单路)10002 / 65296 / 65348支持 currentReverseSet
智能电表-导轨采集器 WiFi+BLE65389支持 6 个极性调整字段
RS06、RS07、RS02、RC01、RS0960675 / 65417 / 88801不支持设置:在线返回 result=1 与设备不支持,离线先返回 result=20008

电表各型号字段概要:

型号额定信息(getBasicsInfo)实时信息(getRealTimeInfo)
智能无线三相CT电表(单路)lineType、currentReverseSet、ctTypectVersion、三相电压/电流、有功/无功/视在功率、功率因数、频率、正反向电量、负载识别等 36 字段(功率因数字段首字母小写,如 aphasePowerFactor)
RS07双路三相额定/设置实体:485通讯地址、两路电流反向/互感器相序/CT变比共 13 字段双路三相实时实体:两路 L1/L2/L3 电压、电流、功率、相位角、电能等 114 字段,另含 openDatalogVo 电表模块属性对象
RS06无独立额定实体,返回 deviceVo=nullRS06 实时实体:三相电压/电流/有功功率、分相电能、功率因数、故障代码、总电网/买/卖电量等 22 字段
RS02 / RC01 / RS09业务字段全空的外部接入电表实体,额定以实时接口为准外部接入电表实时实体:thirdPartyType、功率/电压/电流/频率、ctTotalUseEnergy 等 20 字段
智能电表-导轨采集器 WiFi+BLEctType 与两路互感器极性/相序 17 字段第一路/第二路(后缀 1/2)电压、电流、功率、电能全量字段

各型号完整字段表见《AECC能管系统SDK-完整版》设备服务章节。

空业务数据:设备绑定关系存在但数据尚未上报时,接口可能返回 result=0 且 obj.deviceVo=null(obj.deviceSn、obj.dataLogSn 也可能为 null),属正常情况。处理顺序:先判断 result=0,再判断 obj 与 obj.deviceVo 是否为 null,最后读取业务字段。字段顺序不作为契约,必须按字段名解析 JSON。

示例三:获取设备额定参数数据

该接口用于获取当前设备额定参数数据。

POST
/openApi/device/getBasicsInfo

请求体示例:

{
  "deviceSn": "NB2548300T110CHAB",
  "time": "1725450897",
  "sign": "e83ba9c021edd831ae69033f77528ac5"
}
名称位置类型必选中文名说明
companyCodeheaderstring唯一码AECC 分配的调用方识别码,缺失时返回 result=10001
Content-Typeheaderstring固定为 application/json
Accept-Languageheaderstring提示语言(如 en-US、zh-CN),只影响 msg,不影响 result 与业务字段
bodybodyobject只包含以下业务参数,参数值均为 JSON 字符串
deviceSnbodystring设备序列号无需传设备类型或数据组编号,服务端按绑定的数据实体自动路由
timebodystring秒级时间戳(UTC+0)10 位数字字符串,与服务器时间差须小于 3600 秒
signbodystring签名信息32 位小写 MD5 签名,每次请求重新计算

返回示例(逆变器设备,节选):

{
  "result": 0,
  "msg": "Request successfully.",
  "obj": {
    "deviceSn": "NB2548300T110CHAB",
    "dataLogSn": "AECCAA0437",
    "deviceState": 1,
    "deviceVo": {
      "serialNumber": "NB2548300T110CHAB",
      "ratedPower": 110.0,
      "mpptCount": 9,
      "stringCount": 18,
      "pvStartVoltage": 180.0,
      "gridVoltageType": 1,
      "mainSDPVersion": "V1.2.3"
    }
  },
  "data": null,
  "taskResult": null
}
  • 节选仅展示常用额定字段,完整字段见《AECC能管系统SDK-完整版》。

返回示例(电表设备,单路三相电表实体):

{
  "result": 0,
  "msg": "请求成功!",
  "obj": {
    "deviceSn": "EXAMPLE-METER-SN",
    "dataLogSn": "EXAMPLE-DATALOG-SN",
    "deviceState": 0,
    "deviceVo": {
      "lineType": 1,
      "currentReverseSet": 0,
      "ctType": 23
    }
  },
  "data": null,
  "taskResult": null
}
  • RS07 返回双路三相额定/设置实体(13 字段);RS06 额定固定返回 deviceVo=null;导轨采集器返回其额定/设置实体(17 字段);RS02/RC01/RS09 返回业务字段全空的外部接入电表实体,额定数据以实时接口为准。

示例四:获取设备实时信息数据

该接口用于获取当前设备实时信息数据。

POST
/openApi/device/getRealTimeInfo

请求体示例:

{
  "deviceSn": "NB2548300T110CHAB",
  "time": "1725450897",
  "sign": "e83ba9c021edd831ae69033f77528ac5"
}
名称位置类型必选中文名说明
companyCodeheaderstring唯一码AECC 分配的调用方识别码,缺失时返回 result=10001
Content-Typeheaderstring固定为 application/json
Accept-Languageheaderstring提示语言(如 en-US、zh-CN),只影响 msg,不影响 result 与业务字段
bodybodyobject只包含以下业务参数,参数值均为 JSON 字符串
deviceSnbodystring设备序列号无需传设备类型或数据组编号,服务端按绑定的数据实体自动路由
timebodystring秒级时间戳(UTC+0)10 位数字字符串,与服务器时间差须小于 3600 秒
signbodystring签名信息32 位小写 MD5 签名,每次请求重新计算

返回示例(逆变器设备,节选):

{
  "result": 0,
  "msg": "Request successfully.",
  "obj": {
    "deviceSn": "NB2548300T110CHAB",
    "dataLogSn": "AECCAA0437",
    "deviceState": 1,
    "deviceVo": {
      "runStatus": 1,
      "pvTotalPower": 0.0,
      "dailyPvGenEnergy": 717.2,
      "totalPvGenEnergy": 29884.8,
      "dailyGridConnectGenEnergy": 678.9,
      "totalGridConnectGenEnergy": 28431.2,
      "gridTotalActivePower": null
    }
  },
  "data": null,
  "taskResult": null
}
  • 节选仅展示常用实时字段,完整字段见《AECC能管系统SDK-完整版》。

返回示例(电表设备,单路三相电表实体):

{
  "result": 0,
  "msg": "请求成功!",
  "obj": {
    "deviceSn": "EXAMPLE-METER-SN",
    "dataLogSn": "EXAMPLE-DATALOG-SN",
    "deviceState": 0,
    "deviceVo": {
      "ctAPhaseVoltage": 220.1,
      "ctBPhaseVoltage": 220.2,
      "ctCPhaseVoltage": 220.3,
      "ctAPhaseCurrent": 10.1,
      "ctBPhaseCurrent": 10.2,
      "ctCPhaseCurrent": 10.3,
      "ctThreePhaseTotalPower": 3000.6,
      "aphasePowerFactor": 0.99,
      "totalPowerFactor": 0.98,
      "frequency": 50.01,
      "forwardActiveEnergy": 123.45,
      "reverseActiveEnergy": 1.23,
      "totalActiveEnergy": 124.68,
      "ctType": 23
    }
  },
  "data": null,
  "taskResult": null
}
  • RS07 返回双路三相实时实体(114 字段,含 openDatalogVo 电表模块属性对象);RS06 返回 RS06 实时实体(22 字段);导轨采集器返回导轨采集器双路实时实体;RS02/RC01/RS09 返回外部接入电表实时实体(thirdPartyType、功率/电压/电流/频率、ctTotalUseEnergy 等)。单路三相实体实时响应的功率因数、视在功率字段名首字母小写(如 aphasePowerFactor,不是 aPhasePowerFactor)。本接口返回设备最新一帧实时快照,不支持时间范围查询、分页或历史数据查询。

示例五:设置设备参数--单个地址下发

该接口用于设置设备的单个参数。设置前可通过 getSetInfo(POST /openApi/device/getSetInfo,请求与示例三完全一致,仅 URL 不同)查询当前可设置项及其服务端值。

POST
/openApi/device/setParam

请求示例(逆变器设备):

{
  "deviceSn": "NB2548300T110CHAB",
  "paramName": "pvStartVoltage",
  "paramValue": "179",
  "time": "1785461710",
  "sign": "<md5-sign>"
}

返回示例(逆变器设备,obj 回显本次设置):

{
  "result": 0,
  "msg": "Setting successful.",
  "obj": {
    "pvStartVoltage": "179"
  },
  "data": null,
  "taskResult": null
}

请求示例(电表设备,智能无线三相CT电表·单路):

{
  "deviceSn": "<your-deviceSn>",
  "paramName": "currentReverseSet",
  "paramValue": "1",
  "time": "<utc-second-time>",
  "sign": "<md5-sign>"
}

返回示例(电表设备,obj 为 null):

{
  "result": 0,
  "msg": "设置成功!",
  "obj": null,
  "data": null,
  "taskResult": null
}
关键规则:
  • paramName 区分大小写。电表可设置字段:智能无线三相CT电表(单路)仅 currentReverseSet(电流反向设置);智能电表-导轨采集器 WiFi+BLE 为 transformerPolarityAdjustA1/B1/C1/A2/B2/C2 共 6 个极性调整字段;RS06、RS07、RS02、RC01、RS09 不支持设置(在线返回 result=1 与设备不支持,离线先返回 result=20008)。电表参数值固定传 "0" 或 "1",其他参数名或非法值返回业务失败。
  • 设置接口要求设备在线,离线返回 result=20008;正常约 1 秒返回,未收到设备应答约 5 秒返回设置超时,客户端读超时应大于 5 秒。
  • result=0 表示设备已返回写入成功应答,服务端已保存本次设置值;超时或失败不要立即重试,先调用 getSetInfo 复核或间隔一段时间后再试,并保证同一设备的上一次设置请求已返回。

示例六:设置设备参数--多个地址下发

该接口用于一次设置设备的多个参数,object 为序列化后的 JSON 字符串(不是嵌套 JSON 对象),其值必须与参与签名的字符串逐字符一致,签名后不能再重新格式化或调整内部字段顺序。

POST
/openApi/device/setCustomParams

请求示例(逆变器设备):

{
  "deviceSn": "NB2548300T110CHAB",
  "object": "{\"underVoltProtectRecoverValue\":\"197\",\"overVoltProtectRecoverValue\":\"250\"}",
  "time": "1785462195",
  "sign": "<md5-sign>"
}

返回示例(逆变器设备,obj 回显本次设置):

{
  "result": 0,
  "msg": "Setting successful.",
  "obj": {
    "underVoltProtectRecoverValue": "197",
    "overVoltProtectRecoverValue": "250"
  },
  "data": null,
  "taskResult": null
}

请求示例(电表设备,智能无线三相CT电表·单路):

{
  "deviceSn": "<your-deviceSn>",
  "object": "{\"currentReverseSet\":\"1\"}",
  "time": "<utc-second-time>",
  "sign": "<md5-sign>"
}

请求示例(电表设备,导轨采集器同路多字段):

{
  "deviceSn": "<your-deviceSn>",
  "object": "{\"transformerPolarityAdjustA1\":\"1\",\"transformerPolarityAdjustB1\":\"0\",\"transformerPolarityAdjustC1\":\"1\"}",
  "time": "<utc-second-time>",
  "sign": "<md5-sign>"
}
  • 返回示例(电表设备):与示例五相同,result=0、obj=null。

关键规则:

  • 电表 object 内部字段值均为字符串 "0" 或 "1"。智能无线三相CT电表(单路)必须且只能包含 currentReverseSet 一个字段;智能电表-导轨采集器单次可传同一路(第一路或第二路)1~3 个字段,不能同时包含第一路和第二路字段,否则整体失败不下发;RS06、RS07、RS02、RC01、RS09 不支持设置。
  • object 不能为空对象、空字符串、非法 JSON、数组或包含未知字段,所有内部字段值不能为 null;仅本次提交的字段在成功后更新,同组未提交字段保持原值。
  • 在线要求、超时行为与失败处理与示例五一致:离线返回 result=20008,超时不立即重试,先 getSetInfo 复核。

常见问题(FAQ)

Q1:签名计算总返回 sign 不匹配,如何排查?

服务端在收到请求后会按相同规则(业务参数按 Unicode 升序拼接 → 末尾追加 time 和 key → MD5 → 转小写)重算签名,并与请求体中的 sign 做精确比较(区分大小写、不做 trim)。任一环节不一致都会判定为签名异常,统一返回 result=10001, msg="Signature exception"。

注意:签名异常、时间戳过期、companyCode/time/sign 缺失在服务端会合并为同一个 10001 错误,无法仅凭 code 区分,需按以下步骤逐项排查。

排查步骤(按命中率从高到低):

// 正确:Spring DigestUtils 返回的就是 32 位小写
String sign = DigestUtils.md5DigestAsHex(text.getBytes(StandardCharsets.UTF_8));
确认参数拼接顺序与原文一致
业务参数名必须按 Unicode(字典序)升序 排列,建议直接用 TreeMap(Java)/ sorted()(Python)/ .sort()(JS)自动排序。拼出来的字符串应为 k1=v1&k2=v2&...&time=xxx&key=xxx。

服务端签名重算核心逻辑(参考实现):

// 1. 业务参数按 TreeMap 字典序拼接(排除 sign 和 time)
// 2. 末尾追加 &time={time}&key={keySecret}
// 3. MD5(text.getBytes("UTF-8")),结果为 32 位小写 hex
if (md5.equals(requestSign)) { /* 通过 */ }

Q2:时间戳有效范围是多少?如果服务器时间与标准时间有偏差怎么办?

时间偏差处理建议:

Q3:key 是否可以放在请求头或 URL 中传递?

不可以。 key(密钥)严禁以任何形式出现在网络请求中。

正确做法:

Q4:接口超时或返回 5xx 错误时应该如何处理?

开放接口的 HTTP 层基本固定返回 200,业务结果通过响应体的 result 字段表达;真正的系统级异常会统一映射为 result=4000, msg="System error, please contact the administrator!"。建议按以下策略处理:

1. 客户端超时设置

建议为每次调用设置合理的连接和读取超时(如连接 5s、读取 10s),避免因网络抖动导致线程长时间阻塞:

// HttpClient 示例
RequestConfig config = RequestConfig.custom()
        .setConnectTimeout(5000)      // 连接超时 5s
        .setSocketTimeout(10000)      // 读取超时 10s
        .build();
HttpPost httpPost = new HttpPost(url);
httpPost.setConfig(config);
2. 针对 result=4000(系统错误)的重试

3. 针对网络超时(无响应)的处理

4. 不要重试的情况

5. 限流

部分接口配置了接口请求限流。触发限流时会通过全局异常返回错误信息,客户端应降低调用频率,不要立即重试。

Q5:响应中的 code 和 msg 字段含义在哪里查看?

说明:响应体中的状态字段实际命名为 result(而非 code),下文为便于理解统一按"状态码"描述。

响应结构:

{
  "result": 0,
  "msg": "Request successfully.",
  "data": { /* 业务数据 */ }
}

常见状态码对照表:

result含义典型 msg(en-US)触发场景
0成功Request successfully.业务正常处理完成
1通用失败(由调用方传入)业务逻辑校验未通过,msg 会具体说明;或 companyCode 未开放所调用接口的 URL 权限;电表设置时参数名不支持、参数值非 0/1、object 为空或非法、导轨采集器同时传入两路极性调整字段、RS06/RS07/RS02/RC01/RS09 不支持设置、设置超时/失败
10000未登录/Token 失效Please login. / token expiredToken 模式下 claims 无效或过期
10001签名异常Signature exception签名错误、时间戳过期、companyCode/time/sign 缺失(合并码,无法细分)
10002邮箱格式错误Incorrect email format.注册/绑定邮箱格式不合法
10006用户被禁用The user has been disabled.账号被锁定
20000参数类型有误Incorrect parameter type.请求参数类型不匹配
20001数据不能为空Submitted data cannot be empty.必填参数缺失;或业务数据不存在(如电表设置数据行不存在)
20002权限异常Abnormal permissions.openApi 鉴权链路专用:companyCode 在系统中不存在、对应凭证未启用(flagState != 1)、或无操作权限
20003时间格式错误Time format error.日期/时间参数格式不合法
20004参数格式错误Parameter format error.参数格式校验未通过
20005无此参数No this parameter.缺少必要的业务参数
20006系统错误System error, try later.服务端业务异常
20007DTC 不存在DeviceCode does not exist设备类型编码无效;或设备绑定类型没有开放平台实现
20008设备离线Device off-line设备未上报数据;设置接口要求设备在线,离线返回此码
20009设备不存在Device not existdeviceSn 在系统中查不到
4000系统全局错误System error, please contact the administrator!未捕获异常、事务回滚、参数解析异常等(建议重试或联系官方)

排查建议:

联系我们