# 命令使用文档

# 概述

在HanClouds中,把通过平台下行到设备的数据简称为命令。在实际应用中,命令可能是控制指令也可能不是控制指令。只有通过mqtt协议连接到HanClouds、与HanClouds之间有长连接的设备才可以接受下行数据。

HanClouds向外提供restful api用于向设备下发控制指令,跟踪命令的生命周期,还提供了离线命令保存功能。

# 命令功能简介

# 命令功能说明

命令处理过程大概步骤如下:

  1. 用户调用HanClouds API向某个设备(deviceA)下发命令,HanClouds立即生成一个全局唯一的uuid来标识此命令,后面称为commandId,并返回给调用者。
  2. HanClouds会把命令通过mqtt publish消息以QoS1(at least once)下发给在线的设备,在topic中有deviceKey和commandId
  3. 设备收到publish消息后,应立即回复mqtt publish ack消息,向平台反馈其已经收到命令
  4. 设备继续执行命令要求的动作,执行完毕后,向平台publish一条topic 为cmdack/{deviceKey}/commandId的消息,告知HanClouds,本命令已经处理完毕
  5. 应用侧调用restful api,通过commandId查询某个命令的执行状态,查询设备是否已经收到该命令,是否已经执行完毕等等

# 命令保存功能说明

命令保存功能是指应用层调用HanClouds的API下发命令时设备并未在线(未通过mqtt长连接和平台建立长连接),HanClouds把命令保存起来,等设备后续通过mqtt上线后,HanClouds就把命令下发给设备。为此应用层在调用HanClouds API时,会输入一个命令保存时长的参数,其含义为“如果设备当前时刻不在线,那么在保存时长范围内,如果设备上线,就把命令下发给设备”。

对于未下发给设备的命令可以调用API进行取消。

# 命令状态说明

HanClouds目前有几下几种命令状态:

  • SAVED(1) 命令已保存,但还没发送给设备
  • SENDED(2) 命令已发送,但还没收到设备的应答
  • ACKED(3) 命令已发送,且已收到设备的应答
  • EXPIRED(4) 命令已发送,但在超时时间内没有收到应答
  • COMPLETE(5) 收到设备publish到cmdack/{deviceKey}/{cmdId}的消息,表示已处理了某个命令
  • FAILED(-1) 命令发送失败
  • CANCELED(-2) 用户取消了命令的发送

# 下发命令

向设备下发命令需拥有下列鉴权参数之一:

  • 用户级{authKey:authSecret},可以给用户下的任意设备发命令
  • 产品级{cmdKey:cmdSecret},可以给产品下的任意设备发命令
  • 设备的cmdToken,可以给单个设备发命令

通过产品级鉴权参数下发命令代码示例:

   /**
     * 通过产品级鉴权参数下发命令
     *
     * @param cmd 命令内容
     */
    public String sendCmd(String cmd){
        putProductAuthParams(productKey,cmdKey,cmdSecret);
        DeviceCmdSendRequest request=new DeviceCmdSendRequest();
        request.setDeviceKey(deviceKey);
        request.setContent(cmd);
        request.setDataType(DataTypeEnum.JSON);
        request.setValidTime(120);
        try {
            StringResponse response = execute(request);
            return response.getValue();
        }catch (HanCloudsException e){
            logger.error(e.toString());
            return null;
        }
    }

sendCmd函数的第一行设置产品级{cmdKey:cmdSecret}

putProductAuthParams(productKey,cmdKey,cmdSecret);

第二行创建DeviceCmdSendRequest对象

DeviceCmdSendRequest request=new DeviceCmdSendRequest();

接下来设置请求的参数

    request.setDeviceKey(deviceKey);
    request.setContent(cmd);
    request.setDataType(DataTypeEnum.JSON);
    request.setValidTime(120);

deviceKey:指明给哪个设备发送命令

content:命令的内容

dataType: 数据类型,命令下发的数据都为json数据

validTime: 有效时间,最长60天,单位为秒,默认为0,表示非离线命令。如果下发命令的时候设备离线,平台将保存命令至validTime指定的时间,设备在有效时间内上线平台就下发命令,保存时间超过validTime平台就删除命令。

通过用户级{authKey:authSecret}、设备的cmdToken下发命令也是同样的过程,只是在设置鉴权参数的时候略有不同

// 设置用户鉴权参数
putUserAuthParams(userKey,authKey,authSecret);
// 设置设备鉴权参数
putDeviceAuthParams(deviceKey,cmdToken);

如果命令发送成功,返回值是命令的ID,通过ID可以查询命令的状态。

# 下发命令类型说明

平台命令分为两类,模板命令和自定义命令: 模板命令:下发命令时,需选择要下发的模型命令名称,并填写输入参数; 自定义命令:payload为自定义命令数据,命令的内容数据类型有5种: -json json数据格式 -int int数据格式 -string string类型,字符串数据 -double double数据类型 -bin bin类型数据 在下发自定义控制命令时,需要告知命令的数据类型。

对于命令数据及其过程HanClouds都会保存,并对外提供查询历史命令的接口。

# 下发命令消息说明

下发模型命令的topic为:ctl/{deviceKey}/{commandId}/{identifier},payload为模板命令的json数据。说明如下:

  • topic中deviceKey必须为在线设备的deviceKey,如果设备不在线,且在命令有效时间内设备没有上线,命令就会过期,即下发失败
  • payload在签名模式下为用dynamicSecret加密的密文,在非签名模式下为数据明文
  • ctl为固定前缀
  • deviceKey为该设备在平台上的唯一标识,Connect成功后,平台会返回,在本文档的后面部分会进行说明。
  • commandId为命令ID,为英文字符串,不可包含符号、特殊字符和中文
  • identifier为命令类型标识符;

下发自定义命令的topic为:cmd/{deviceKey}/{commandId}/dataType,payload为自定义命令数据,说明如下:

  • topic中deviceKey必须为在线设备的deviceKey,如果设备不在线,且在命令有效时间内设备没有上线,命令就会过期,即下发失败
  • payload在签名模式下为用dynamicSecret加密的密文,在非签名模式下为数据明文
  • cmd为固定前缀
  • deviceKey为该设备在平台上的唯一标识,Connect成功后,平台会返回,在本文档的后面部分会进行说明。
  • commandId为命令ID,试该设备下命令的唯一标识符,为英文字符串,不可包含符号、特殊字符和中文
  • dataType为命令数据所属类型,共5种。

# 查询命令状态

查询命令的状态需拥有下列鉴权参数之一:

  • 用户级{authKey:authSecret},可以查询用户下任意设备的命令状态
  • 产品级{queryKey:querySecret},可以查询产品下任意设备的命令状态
  • 设备的queryToken,可以查询单个设备的命令状态

通过产品级鉴权参数查询命令状态的代码示例:

   /**
     * 查询命令
     *
     * @param cmdId 命令ID
     * @return
     */
    public DeviceCommandDTO getCmd(String cmdId){
        putProductAuthParams(productKey,queryKey,querySecret);
        DeviceCommandRequest request=new DeviceCommandRequest();
        request.setDeviceKey(deviceKey);
        request.setCmdId(cmdId);
        try {
            DeviceCommandResponse response = execute(request);
            return response.getResponse();
        }catch (HanCloudsException e){
            logger.error(e.toString());
            return null;
        }
    }

查询结果是DeviceCommandDTO对象,部分关键代码如下:

public class DeviceCommandDTO { 
    /**
     * 命令Id,为命令的唯一标识
     */
    private String cmdId;
    /**
     * 接受此命令的设备userKey
     */
    private String userKey;
    /**
     * 接受此命令的设备productKey
     */
    private String productKey;
    /**
     * 接受此命令的设备deviceKey
     */
    private String deviceKey;
    /**
     * 平台收到命令请求的Timestamp
     */
    private Long timeReq;
    /**
     * 命令的有效截止时间
     */
    private Long timeDue;
    /**
     * 平台把命令下发给设备的时间戳
     */
    private Long timeSend;
    /**
     * 设备回复mqtt ack的响应的时间戳
     */
    private Long timeAck;
    /**
     * 设备把命令处理完成后响应的时间戳。 设备在topic  cmdack/...上下发响应的时间
     */
    private Long timeCpl;
    /**
     * 命令状态
     */
    private int state;
    /**
     * 数据类型
     */
    private int type;
    /**
     * 数据值
     */
    private Object data;
   /**
    * 输出参数
    */
   private Object output;
}

除了针对单个命令ID的查询,HanClouds还提供了批量查询一组命令、查询最新的命令、按用户/产品/设备统计命令数目等接口,请参阅API文档。

# 下发命令回复

平台向设备下发命令之后,设备可通过publish向平台上传设备命令回复数据,当使用网关设备登录时,平台向其子设备下发命令后,网关可代理子设备向平台上传命令回复数据。

# 命令回复消息说明

设备上传命令topic为:cmdack/{deviceKey}/{commandId},payload为设备上传命令回复的json数据,说明如下:

  • topic中deviceKey必须为本设备的deviceKey,否则数据校验不通过,上传命令失败;
  • payload在签名模式下为用dynamicSecret加密的密文,在非签名模式下为数据明文,请参考数据上传章节中数据加解密说明。
  • cmdack为固定前缀
  • deviceKey为该设备在平台上的唯一标识,Connect成功后,平台会返回,在本文档的后面部分会进行说明。
  • commandId为命令ID,试该设备下命令的唯一标识符,为英文字符串,不可包含符号、特殊字符和中文

子设备代理上传命令回复,topic为:cmdack/{chiledDeviceKey}/commandId ,payload为子设备上传的命令回复的json数据,说明如下:

  • topic中chiledDeviceKey必须为网关设备下在线的子设备的deviceKey,否则数据校验不通过,上传命令失败;
  • payload在签名模式下为用dynamicSecret加密的密文,在非签名模式下为数据明文,请参考数据上传章节中数据加解密说明。
  • cmdack为固定前缀
  • chiledDeviceKey为网关设备下的子设备在平台上的唯一标识,Connect成功后,平台会返回,在本文档的后面部分会进行说明。
  • commandId为命令ID,试该设备下命令的唯一标识符,为英文字符串,不可包含符号、特殊字符和中文