更新时间:2024-11-20 08:51:29下载pdf
本文档分为两部分,第一部分介绍如何集成涂鸦 Zigbee 业务,实现兼容涂鸦生态所有 Zigbee 子设备。第二部分是在第一部分基础上的扩展,介绍如何实现第三方 Zigbee 子设备接入。
综合 SDK 支持搭配涂鸦网关 Zigbee 模组使用,使用涂鸦网关 Zigbee 模组可以直接接入涂鸦生态所有 Zigbee 子设备。同时,SDK 也提供了接口,允许开发者自行把第三方 Zigbee 子设备接入到涂鸦生态,可以开发既支持接入涂鸦生态 Zigbee 子设备,又支持接入第三方 Zigbee 子设备的网关产品,提高产品的差异性。
本小节将会介绍如何集成涂鸦 Zigbee 业务,让网关能直接接入涂鸦生态所有 Zigbee 子设备。
集成涂鸦 Zigbee 业务涉及到硬件和软件两方面,硬件上主控与涂鸦 Zigbee 模组通过串口连接,软件上调用 Zigbee 的初始化和启动接口。
SDK 将通过串口与 Zigbee 模组进行通信,连接和设备管理等操作全部由 SDK 实现,应用无需处理任何逻辑。
Zigbee 初始化接口的入参主要是配置信息,为 JSON 数据,字段说明如下:
字段 | 描述 |
---|---|
storage_path | 存储路径,要求有读写权限,用于存储 Zigbee 相关数据,如 Zigbee 协调器的 Channel, PANID, Network Key 等,以及子设备的 endpoint, devid, cluster 等信息 |
cache_path | OTA 升级,存储固件的路径,要求有读写权限 |
dev_name | 串口设备名称 |
cts | 是否支持硬件流控 |
thread_mode | 使用线程还是进程方式运行 Zigbee HOST 应用 |
我们在 快速入门指南 的示例代码基础上,添加 Zigbee 功能实现:
// ...
#include "tuya_zigbee_api.h"
// ...
int main(int argc, char **argv)
{
// ...
ty_cJSON *zb_cfg = ty_cJSON_CreateObject();
if (zb_cfg == NULL) {
return OPRT_CJSON_GET_ERR;
}
ty_cJSON_AddStringToObject(zb_cfg, "storage_path", "./");
ty_cJSON_AddStringToObject(zb_cfg, "cache_path", "/tmp/");
ty_cJSON_AddStringToObject(zb_cfg, "dev_name", "/dev/ttyS2");
ty_cJSON_AddNumberToObject(zb_cfg, "cts", 1);
ty_cJSON_AddNumberToObject(zb_cfg, "thread_mode", 1);
/* initiate application service, more service in here */
TUYA_CALL_ERR_RETURN(tuya_zigbee_svc_init(zb_cfg));
TUYA_CALL_ERR_RETURN(tuya_user_svc_init(&gw_cbs));
// ...
/* start application service, more service in here */
TUYA_CALL_ERR_RETURN(tuya_zigbee_svc_start(zb_cfg));
TUYA_CALL_ERR_RETURN(tuya_user_svc_start(NULL));
// ...
return 0;
}
编译运行后,看到类似如下打印,则表示 Zigbee 已成功运行。理论上,使用涂鸦生态任意的 Zigbee 子设备,都能成功入网。
// ...
[01-01 00:00:08:778 TUYA D][tuya_z3.c:26760] nodeEuiStr = 086bd7fffed0cb51.
[01-01 00:00:08:778 TUYA D][tuya_z3.c:26761] nodeId = 0x0000.
[01-01 00:00:08:778 TUYA D][tuya_z3.c:26762] panId = 0xc0a7.
[01-01 00:00:08:778 TUYA D][tuya_z3.c:26763] channel = 0x0f.
[01-01 00:00:08:778 TUYA D][tuya_z3.c:26764] ver = 1.0.9.
[01-01 00:00:08:778 TUYA D][tuya_z3.c:26765] netStatus = 0x03.
[01-01 00:00:08:778 TUYA D][tuya_z3.c:26766] manufacturerID = 0x0001.
[01-01 00:00:08:778 TUYA D][tuya_z3.c:26767] netWorkKey:
dd 47 74 57 a8 10 05 20 bd 99 f0 aa 08 ea 27 57
// ...
本小节将会介绍如何接入第三方 Zigbee 子设备,开发出既支持接入涂鸦生态 Zigbee 子设备,又支持接入第三方 Zigbee 子设备的网关产品,实现产品差异化。
Zigbee 扩展第三方子设备是使用白名单的机制来实现的,开发者把第三方子设备的厂商号和型号添加到白名单列表,注册到 SDK。设备入网时 SDK 根据白名单进行匹配,厂商号和型号匹配上则触发设备管理回调,开发者在回调中实现相关业务逻辑。
接入第三方 Zigbee 子设备要求开发者具备 Zigbee 相关基础知识。此外,还要了解 SDK 接入子设备的开发流程,详见 接入子设备指南。
开始之前,先了解一下相关术语:
术语 | 解释 |
---|---|
SDK 子设备管理 | 指下行回调,数据流方向:云端 -> SDK -> 应用。与具体子设备协议无关,在回调中实现具体协议逻辑 |
Zigbee 子设备管理 | 指上行回调,数据流方向:Zigbee -> SDK -> 云端。第三方 Zigbee 子设备接入特有的,在回调中实现应用逻辑 |
第三方 Zigbee 子设备入网的主要逻辑:
第三方 Zigbee 子设备入网的交互流程图:
相关代码实现:
// ...
#include "tuya_zigbee_api.h"
#include "tuya_gw_subdev_api.h"
// ...
#define TY_TS_ONOF_LIGHT_ONE "TS0001"
#define TY_TS_ONOF_LIGHT_TWO "TS0002"
#define TY_TS_ONOF_LIGHT_THR "TS0003"
#define PROFILE_ID_HA 0x0104
#define PROFILE_ID_LL 0xC05E
#define ZCL_BASIC_CLUSTER_ID 0x0000
#define ZCL_POWER_CONFIG_CLUSTER_ID 0x0001
#define ZCL_DEVICE_TEMP_CLUSTER_ID 0x0002
#define ZCL_IDENTIFY_CLUSTER_ID 0x0003
#define ZCL_GROUPS_CLUSTER_ID 0x0004
#define ZCL_SCENES_CLUSTER_ID 0x0005
#define ZCL_ON_OFF_CLUSTER_ID 0x0006
#define ZCL_CMD_TYPE_GLOBAL 0x00
#define ZCL_CMD_TYPE_PRIVATE 0x01
#define ZCL_FRAME_TYPE_UNICAST 0x00
#define ZCL_FRAME_TYPE_MULTICAST 0x01
#define ZCL_FRAME_TYPE_BROADCAST 0x02
#define ZCL_OFF_COMMAND_ID 0x00
#define ZCL_ON_COMMAND_ID 0x01
#define ZCL_READ_ATTRIBUTES_COMMAND_ID 0x00
#define ZCL_READ_ATTRIBUTES_RESPONSE_COMMAND_ID 0x01
#define ZCL_REPORT_ATTRIBUTES_COMMAND_ID 0x0A
#define ZCL_READ_ATTRIBUTER_RESPONSE_HEADER 3 /* Attributer ID: 2 Bytes, Status: 1 Btye */
#define ZCL_REPORT_ATTRIBUTES_HEADER 2 /* Attributer ID: 2 Bytes */
#define DEVID_HA_IAS_ZONE 0x0402 // 0x0402 -> 0x00,IAS类,pir, 门磁
#define DEVID_HA_TEMPERATURE_SENSOR 0x0302 // 0x0302 -> 0x01,温度类(含温湿度)
#define DEVID_HA_ONOF_LIGHT 0x0100 // 0x0100 -> 0x02,照明开关类
#define DEVID_HA_SMART_PLUG 0x0051 // 0x0051 -> 0x03, 智能插座类
/* 举例,自定义 DEVID,保存到 UDDD */
#define TY_DEVID_HA_IAS_ZONE 0x00 // 0x0402 -> 0x00,IAS类,pir, 门磁
#define TY_DEVID_HA_TEMPERATURE_SENSOR 0x01 // 0x0302 -> 0x01,温度类(含温湿度)
#define TY_DEVID_HA_ONOF_LIGHT 0x02 // 0x0100 -> 0x02,照明开关类
#define TY_DEVID_HA_SMART_PLUG 0x03 // 0x0051 -> 0x03, 智能类插座类
/* 举例,自定义 TYPE,保存到 UDDD */
#define TY_TYPE_PIR 0x00 // DEVID_HA_IAS_ZONE
#define TY_TYPE_DOOR_SENSOR 0x01 // DEVID_HA_IAS_ZONE
#define TY_TYPE_FIRE_SENSOR 0x02 // DEVID_HA_IAS_ZONE
#define TY_TYPE_WATER_SENSOR 0x03 // DEVID_HA_IAS_ZONE
#define TY_TYPE_ONOF_LIGHT_ONE 0x00 // DEVID_HA_ONOF_LIGHT
#define TY_TYPE_ONOF_LIGHT_TWO 0x01 // DEVID_HA_ONOF_LIGHT
#define TY_TYPE_ONOF_LIGHT_THREE 0x02 // DEVID_HA_ONOF_LIGHT
TY_Z3_DEV_S my_z3_dev[] = {
{ "TUYATEC-nPGIPl5D", "TS0001" },
{ "TUYATEC-1xAC7DHQ", "TS0002" },
};
TY_Z3_DEVLIST_S my_z3_devlist = {
.devs = my_z3_dev,
.dev_num = CNTSOF(my_z3_dev),
};
STATIC VOID __z3_onoff_light_read_attr(CONST CHAR_T *dev_id)
{
OPERATE_RET op_ret = OPRT_OK;
TY_Z3_APS_FRAME_S frame = {0};
USHORT_T atrr_buf[5] = { 0x0000, 0x4001, 0x4002, 0x8001, 0x5000 };
strncpy(frame.id, dev_id, SIZEOF(frame.id));
frame.profile_id = PROFILE_ID_HA;
frame.cluster_id = ZCL_ON_OFF_CLUSTER_ID;
frame.cmd_type = ZCL_CMD_TYPE_GLOBAL;
frame.src_endpoint = 0x01;
frame.dst_endpoint = 0xff;
frame.cmd_id = ZCL_READ_ATTRIBUTES_COMMAND_ID;
frame.msg_length = SIZEOF(atrr_buf);
frame.message = (UCHAR_T *)atrr_buf;
op_ret = tuya_zigbee_send_data(&frame);
if (op_ret != OPRT_OK) {
PR_ERR("tuya_zigbee_send_data err: %d", op_ret);
return;
}
}
STATIC VOID __z3_onoff_light_report(TY_Z3_APS_FRAME_S *frame)
{
OPERATE_RET op_ret = OPRT_OK;
TY_OBJ_DP_S dp_data = {0};
if (frame->cluster_id != ZCL_ON_OFF_CLUSTER_ID) {
return;
}
dp_data.dpid = frame->src_endpoint;
dp_data.type = PROP_BOOL;
if (frame->cmd_id == ZCL_REPORT_ATTRIBUTES_COMMAND_ID) {
dp_data.value.dp_bool = frame->message[ZCL_REPORT_ATTRIBUTES_HEADER+1];
} else if (frame->cmd_id == ZCL_READ_ATTRIBUTES_RESPONSE_COMMAND_ID) {
dp_data.value.dp_bool = frame->message[ZCL_READ_ATTRIBUTER_RESPONSE_HEADER+1];
} else {
return;
}
op_ret = dev_report_dp_json_async(frame->id, &dp_data, 1);
if (op_ret != OPRT_OK) {
PR_ERR("dev_report_dp_json_async err: %d", op_ret);
return;
}
}
STATIC VOID __my_z3_dev_report(TY_Z3_APS_FRAME_S *frame)
{
OPERATE_RET op_ret = OPRT_OK;
DEV_DESC_IF_S *dev_if = NULL;
if (frame->profile_id != PROFILE_ID_HA) {
return;
}
dev_if = tuya_iot_get_dev_if(frame->id);
if (!dev_if || !dev_if->bind) {
PR_ERR("dev_id: %s is not bind", frame->id);
tuya_zigbee_del_dev(frame->id);
return;
}
op_ret = tuya_iot_fresh_dev_hb(frame->id);
if (op_ret != OPRT_OK) {
PR_WARN("tuya_iot_fresh_dev_hb err: %d", op_ret);
}
UCHAR_T uc_devid = (((dev_if->uddd) >> 8) & 0xFF);
switch (uc_devid)
{
case TY_DEVID_HA_ONOF_LIGHT:
__z3_onoff_light_report(frame);
break;
case TY_DEVID_HA_IAS_ZONE:
case TY_DEVID_HA_TEMPERATURE_SENSOR:
case TY_DEVID_HA_SMART_PLUG:
default:
PR_WARN("devid: %d is not support", uc_devid);
return;
}
}
STATIC VOID __my_z3_dev_join(TY_Z3_DESC_S *dev)
{
OPERATE_RET op_ret = OPRT_OK;
CHAR_T pid[PRODUCT_KEY_LEN+1]={0};
CHAR_T sw_ver[SW_VER_LEN+1] = {0};
USER_DEV_DTL_DEF_T uddd = 0;
/**
* 以下代码仅作为演示,开发者一般需要实现的逻辑:
* a) 解析设备信息,取关键信息,保存到 uddd,用于标识设备类型等;
* b) 关联 PID,调用绑定接口把设备绑定到云端。
*/
if (dev->profile_id[0] != PROFILE_ID_HA) {
return;
}
CHAR_T iver1 = (CHAR_T)(dev->version & 0x0F);
CHAR_T iver2 = (CHAR_T)((dev->version >> 4) & 0x03);
CHAR_T iver3 = (CHAR_T)((dev->version >> 6) & 0x03);
snprintf(sw_ver, SIZEOF(sw_ver), "%d.%d.%d", iver3, iver2, iver1);
switch (dev->device_id[0]) {
case DEVID_HA_ONOF_LIGHT:
uddd |= (TY_DEVID_HA_ONOF_LIGHT << 8);
if (0 == strncasecmp(dev->model_id, TY_TS_ONOF_LIGHT_ONE, strlen(TY_TS_ONOF_LIGHT_ONE))) {
uddd |= (TY_TYPE_ONOF_LIGHT_ONE & 0xFF);
strncpy(pid, "ckj1pnvy", SIZEOF(pid));
} else if (0 == strncasecmp(dev->model_id, TY_TS_ONOF_LIGHT_TWO, strlen(TY_TS_ONOF_LIGHT_TWO))) {
uddd |= (TY_TYPE_ONOF_LIGHT_TWO & 0xFF);
strncpy(pid, "tr4dshuh", SIZEOF(pid));
} else {
PR_WARN("model_id: %s is not supported", dev->model_id);
return;
}
break;
case DEVID_HA_IAS_ZONE:
case DEVID_HA_TEMPERATURE_SENSOR:
case DEVID_HA_SMART_PLUG:
default:
PR_WARN("devid: %d is not supported", dev->device_id[0]);
return;
}
op_ret = tuya_iot_gw_bind_dev(GP_DEV_ATH_1, uddd, dev->id, pid, sw_ver);
if (op_ret != OPRT_OK) {
PR_ERR("tuya_iot_gw_bind_dev err: %d", op_ret);
return;
}
}
STATIC VOID __dev_bind_cb(CONST CHAR_T *dev_id, CONST OPERATE_RET result)
{
OPERATE_RET op_ret = OPRT_OK;
DEV_DESC_IF_S *dev_if = NULL;
/**
* 以下代码仅作为演示,开发者一般需要实现的逻辑:
* a) 根据绑定状态,封装读取属性 ZCL 数据发给子设备。
* b) 配置心跳超时。
*/
if (result != OPRT_OK) {
PR_ERR("dev_id: %s bind err", dev_id);
tuya_zigbee_del_dev(dev_id);
return;
}
op_ret = tuya_iot_set_dev_hb_cfg(dev_id, 120, 3, FALSE);
if (op_ret != OPRT_OK) {
PR_WARN("tuya_iot_set_dev_hb_cfg err: %d", op_ret);
}
op_ret = tuya_iot_fresh_dev_hb(dev_id);
if (op_ret != OPRT_OK) {
PR_WARN("tuya_iot_fresh_dev_hb err: %d", op_ret);
}
dev_if = tuya_iot_get_dev_if(dev_id);
if (!dev_if || !dev_if->bind) {
PR_ERR("dev_id: %s is not bind", dev_id);
tuya_zigbee_del_dev(dev_id);
return;
}
UCHAR_T uc_devid = (((dev_if->uddd) >> 8) & 0xFF);
switch (uc_devid)
{
case TY_DEVID_HA_ONOF_LIGHT:
__z3_onoff_light_read_attr(dev_id);
break;
case TY_DEVID_HA_IAS_ZONE:
case TY_DEVID_HA_TEMPERATURE_SENSOR:
case TY_DEVID_HA_SMART_PLUG:
default:
PR_WARN("devid: %d is not support", uc_devid);
return;
}
}
STATIC OPERATE_RET __user_init(VOID)
{
OPERATE_RET rt = OPRT_OK;
/* Zigbee 子设备管理回调 */
TY_Z3_DEV_CBS_S z3_dev_cbs = {
.join = __my_z3_dev_join,
.report = __my_z3_dev_report,
};
/* SDK 子设备管理回调 */
TY_IOT_DEV_CBS_S dev_mgr_cbs = {
.dev_bind_ifm = __dev_bind_cb,
};
TUYA_CALL_ERR_RETURN(tuya_zigbee_custom_dev_mgr_init(&my_z3_devlist, &z3_dev_cbs));
TUYA_CALL_ERR_RETURN(tuya_iot_reg_gw_mgr_cb(GP_DEV_ATH_1, &dev_mgr_cbs));
return OPRT_OK;
}
int main(int argc, char **argv)
{
// ...
ty_cJSON *zb_cfg = ty_cJSON_CreateObject();
if (zb_cfg == NULL) {
return OPRT_CJSON_GET_ERR;
}
ty_cJSON_AddStringToObject(zb_cfg, "storage_path", "./");
ty_cJSON_AddStringToObject(zb_cfg, "cache_path", "/tmp/");
ty_cJSON_AddStringToObject(zb_cfg, "dev_name", "/dev/ttyS2");
ty_cJSON_AddNumberToObject(zb_cfg, "cts", 1);
ty_cJSON_AddNumberToObject(zb_cfg, "thread_mode", 1);
/* initiate application service, more service in here */
TUYA_CALL_ERR_RETURN(tuya_zigbee_svc_init(zb_cfg));
TUYA_CALL_ERR_RETURN(tuya_user_svc_init(&gw_cbs));
TUYA_CALL_ERR_RETURN(__user_init());
// ...
/* start application service, more service in here */
TUYA_CALL_ERR_RETURN(tuya_zigbee_svc_start(zb_cfg));
TUYA_CALL_ERR_RETURN(tuya_user_svc_start(NULL));
// ...
return 0;
}
第三方 Zigbee 子设备控制的主要逻辑:
说明:设备控制是以子设备数据上报作为响应状态,所以子设备收到控制指令完成控制动作之后,需要上报当前状态,应用把它转成功能点上报到云端。如果第三方 Zigbee 子设备实现控制后主动上报的逻辑,则需要应用在下发控制指令后,再下发查询指令。
设备控制的交互流程示意图:
相关代码实现:
// ...
STATIC VOID __z3_onoff_light_write_attr(CONST TY_RECV_OBJ_DP_S *cmd)
{
INT_T i= 0;
OPERATE_RET op_ret = OPRT_OK;
TY_Z3_APS_FRAME_S frame = {0};
strncpy(frame.id, cmd->cid, SIZEOF(frame.id));
frame.profile_id = PROFILE_ID_HA;
frame.src_endpoint = 0x01;
for (i = 0; i < cmd->dps_cnt; i++) {
switch (cmd->dps[i].dpid) {
case 1 ... 3:
frame.dst_endpoint = cmd->dps[i].dpid;
frame.cluster_id = ZCL_ON_OFF_CLUSTER_ID;
frame.cmd_type = ZCL_CMD_TYPE_PRIVATE;
frame.frame_type = ZCL_FRAME_TYPE_UNICAST;
frame.msg_length = 0;
if (cmd->dps[i].value.dp_bool) {
frame.cmd_id = ZCL_ON_COMMAND_ID;
} else {
frame.cmd_id = ZCL_OFF_COMMAND_ID;
}
break;
default:
PR_WARN("dev_id: %s, dpid: %d is not supported", cmd->cid, cmd->dps[i].dpid);
return;
}
op_ret = tuya_zigbee_send_data(&frame);
if (op_ret != OPRT_OK) {
PR_WARN("tuya_zigbee_send_data err: %d", op_ret);
}
}
}
STATIC VOID __z3_onoff_light_report(TY_Z3_APS_FRAME_S *frame)
{
OPERATE_RET op_ret = OPRT_OK;
TY_OBJ_DP_S dp_data = {0};
if (frame->cluster_id != ZCL_ON_OFF_CLUSTER_ID) {
return;
}
dp_data.dpid = frame->src_endpoint;
dp_data.type = PROP_BOOL;
if (frame->cmd_id == ZCL_REPORT_ATTRIBUTES_COMMAND_ID) {
dp_data.value.dp_bool = frame->message[ZCL_REPORT_ATTRIBUTES_HEADER+1];
} else if (frame->cmd_id == ZCL_READ_ATTRIBUTES_RESPONSE_COMMAND_ID) {
dp_data.value.dp_bool = frame->message[ZCL_READ_ATTRIBUTER_RESPONSE_HEADER+1];
} else {
return;
}
op_ret = dev_report_dp_json_async(frame->id, &dp_data, 1);
if (op_ret != OPRT_OK) {
PR_ERR("dev_report_dp_json_async err: %d", op_ret);
return;
}
}
STATIC VOID __dev_cmd_obj_cb(CONST TY_RECV_OBJ_DP_S *cmd)
{
DEV_DESC_IF_S *dev_if = NULL;
/**
* 以下代码仅作为演示,开发者一般需要实现的逻辑:
* a) 获取设备的 UDDD,根据 UDDD 映射到设备类型。
* b) 根据 UDDD,把功能点转成 ZCL 数据,下发给子设备。
*/
dev_if = tuya_iot_get_dev_if(cmd->cid);
if (!dev_if || !dev_if->bind) {
PR_ERR("dev_id: %s is not bind", cmd->cid);
return;
}
UCHAR_T uc_devid = (((dev_if->uddd) >> 8) & 0xFF);
switch (uc_devid)
{
case TY_DEVID_HA_ONOF_LIGHT:
__z3_onoff_light_write_attr(cmd);
break;
case TY_DEVID_HA_IAS_ZONE:
case TY_DEVID_HA_TEMPERATURE_SENSOR:
case TY_DEVID_HA_SMART_PLUG:
default:
PR_WARN("devid: %d is not support", uc_devid);
return;
}
}
STATIC VOID __my_z3_dev_report(TY_Z3_APS_FRAME_S *frame)
{
OPERATE_RET op_ret = OPRT_OK;
DEV_DESC_IF_S *dev_if = NULL;
if (frame->profile_id != PROFILE_ID_HA) {
return;
}
dev_if = tuya_iot_get_dev_if(frame->id);
if (!dev_if || !dev_if->bind) {
PR_ERR("dev_id: %s is not bind", frame->id);
tuya_zigbee_del_dev(frame->id);
return;
}
op_ret = tuya_iot_fresh_dev_hb(frame->id);
if (op_ret != OPRT_OK) {
PR_WARN("tuya_iot_fresh_dev_hb err: %d", op_ret);
}
UCHAR_T uc_devid = (((dev_if->uddd) >> 8) & 0xFF);
switch (uc_devid)
{
case TY_DEVID_HA_ONOF_LIGHT:
__z3_onoff_light_report(frame);
break;
case TY_DEVID_HA_IAS_ZONE:
case TY_DEVID_HA_TEMPERATURE_SENSOR:
case TY_DEVID_HA_SMART_PLUG:
default:
PR_WARN("devid: %d is not support", uc_devid);
return;
}
}
STATIC OPERATE_RET __user_init(VOID)
{
OPERATE_RET rt = OPRT_OK;
/* Zigbee 子设备管理回调 */
TY_Z3_DEV_CBS_S z3_dev_cbs = {
.join = __my_z3_dev_join,
.report = __my_z3_dev_report,
};
/* SDK 子设备管理回调 */
TY_IOT_DEV_CBS_S dev_mgr_cbs = {
.dev_bind_ifm = __dev_bind_cb,
};
TY_IOT_DP_CBS_S dev_dp_cbs = {
.obj = __dev_cmd_obj_cb,
};
TUYA_CALL_ERR_RETURN(tuya_zigbee_custom_dev_mgr_init(&my_z3_devlist, &z3_dev_cbs));
TUYA_CALL_ERR_RETURN(tuya_iot_reg_gw_mgr_cb(GP_DEV_ATH_1, &dev_mgr_cbs));
TUYA_CALL_ERR_RETURN(tuya_iot_reg_dp_cb(DP_DEV, GP_DEV_ATH_1, &dev_dp_cbs));
return OPRT_OK;
}
// ...
子设备在离线状态是通过心跳机制来维持,心跳一般有两种实现:
主动上报比较简单,当应用收到第三方 Zigbee 数据时刷新子设备心跳即可。
而被动查询是由 SDK 实现的心跳管理,应用只需要配置心跳超时以及心跳超时处理。
被动查询的主要逻辑:
相关代码实现:
// ...
STATIC VOID __dev_hb_cb(CONST CHAR_T *dev_id)
{
OPERATE_RET op_ret = OPRT_OK;
TY_Z3_APS_FRAME_S frame = {0};
USHORT_T atrr_buf[1] = { 0x0001 };
strncpy(frame.id, dev_id, SIZEOF(frame.id));
frame.profile_id = PROFILE_ID_HA;
frame.cluster_id = ZCL_BASIC_CLUSTER_ID;
frame.cmd_type = ZCL_CMD_TYPE_GLOBAL;
frame.src_endpoint = 0x01;
frame.dst_endpoint = 0xFF;
frame.cmd_id = ZCL_READ_ATTRIBUTES_COMMAND_ID;
frame.msg_length = SIZEOF(atrr_buf);
frame.message = (UCHAR_T *)atrr_buf;
op_ret = tuya_zigbee_send_data(&frame);
if (op_ret != OPRT_OK) {
PR_ERR("tuya_zigbee_send_data err: %d", op_ret);
return;
}
}
STATIC VOID __my_z3_dev_report(TY_Z3_APS_FRAME_S *frame)
{
OPERATE_RET op_ret = OPRT_OK;
DEV_DESC_IF_S *dev_if = NULL;
if (frame->profile_id != PROFILE_ID_HA) {
return;
}
dev_if = tuya_iot_get_dev_if(frame->id);
if (!dev_if || !dev_if->bind) {
PR_ERR("dev_id: %s is not bind", frame->id);
tuya_zigbee_del_dev(frame->id);
return;
}
op_ret = tuya_iot_fresh_dev_hb(frame->id);
if (op_ret != OPRT_OK) {
PR_WARN("tuya_iot_fresh_dev_hb err: %d", op_ret);
}
UCHAR_T uc_devid = (((dev_if->uddd) >> 8) & 0xFF);
switch (uc_devid)
{
case TY_DEVID_HA_ONOF_LIGHT:
__z3_onoff_light_report(frame);
break;
case TY_DEVID_HA_IAS_ZONE:
case TY_DEVID_HA_TEMPERATURE_SENSOR:
case TY_DEVID_HA_SMART_PLUG:
default:
PR_WARN("devid: %d is not support", uc_devid);
return;
}
}
STATIC VOID __my_z3_dev_notify(VOID)
{
OPERATE_RET op_ret = OPRT_OK;
DEV_DESC_IF_S *dev_if = NULL;
VOID *iter = NULL;
dev_if = tuya_iot_dev_traversal(&iter);
while (dev_if) {
if (dev_if->tp == GP_DEV_ATH_1) {
op_ret = tuya_iot_set_dev_hb_cfg(dev_if->id, 120, 3, FALSE);
if (op_ret != OPRT_OK) {
PR_WARN("tuya_iot_set_dev_hb_cfg err: %d", op_ret);
}
}
dev_if = tuya_iot_dev_traversal(&iter);
}
}
STATIC OPERATE_RET __user_init(VOID)
{
OPERATE_RET rt = OPRT_OK;
/* Zigbee 子设备管理回调 */
TY_Z3_DEV_CBS_S z3_dev_cbs = {
.join = __my_z3_dev_join,
.report = __my_z3_dev_report,
.notify = __my_z3_dev_notify,
};
/* SDK 子设备管理回调 */
TY_IOT_DEV_CBS_S dev_mgr_cbs = {
.dev_bind_ifm = __dev_bind_cb,
.dev_hb = __dev_hb_cb,
};
TY_IOT_DP_CBS_S dev_dp_cbs = {
.obj = __dev_cmd_obj_cb,
};
TUYA_CALL_ERR_RETURN(tuya_zigbee_custom_dev_mgr_init(&my_z3_devlist, &z3_dev_cbs));
TUYA_CALL_ERR_RETURN(tuya_iot_reg_gw_mgr_cb(GP_DEV_ATH_1, &dev_mgr_cbs));
TUYA_CALL_ERR_RETURN(tuya_iot_reg_dp_cb(DP_DEV, GP_DEV_ATH_1, &dev_dp_cbs));
return OPRT_OK;
}
// ...
移除设备有两种方式:
相关代码实现:
// ...
STATIC VOID __my_z3_dev_leave(CONST CHAR_T *dev_id)
{
OPERATE_RET op_ret = OPRT_OK;
op_ret = tuya_iot_gw_unbind_dev(dev_id);
if (op_ret != OPRT_OK) {
PR_ERR("tuya_iot_gw_unbind_dev err: %d", op_ret);
return;
}
}
STATIC VOID __dev_del_cb(CONST CHAR_T *dev_id, CONST GW_DELDEV_TYPE type)
{
OPERATE_RET op_ret = OPRT_OK;
op_ret = tuya_zigbee_del_dev(dev_id);
if (op_ret != OPRT_OK) {
PR_ERR("tuya_zigbee_del_dev err: %d", op_ret);
return;
}
}
STATIC VOID __dev_reset_cb(CONST CHAR_T *dev_id, DEV_RESET_TYPE_E type)
{
OPERATE_RET op_ret = OPRT_OK;
op_ret = tuya_zigbee_del_dev(dev_id);
if (op_ret != OPRT_OK) {
PR_ERR("tuya_zigbee_del_dev err: %d", op_ret);
return;
}
}
STATIC OPERATE_RET __user_init(VOID)
{
OPERATE_RET rt = OPRT_OK;
/* Zigbee 子设备管理回调 */
TY_Z3_DEV_CBS_S z3_dev_cbs = {
.join = __my_z3_dev_join,
.leave = __my_z3_dev_leave,
.report = __my_z3_dev_report,
.notify = __my_z3_dev_notify,
};
/* SDK 子设备管理回调 */
TY_IOT_DEV_CBS_S dev_mgr_cbs = {
.dev_del = __dev_del_cb,
.dev_bind_ifm = __dev_bind_cb,
.dev_hb = __dev_hb_cb,
.dev_reset = __dev_reset_cb,
};
TY_IOT_DP_CBS_S dev_dp_cbs = {
.obj = __dev_cmd_obj_cb,
};
TUYA_CALL_ERR_RETURN(tuya_zigbee_custom_dev_mgr_init(&my_z3_devlist, &z3_dev_cbs));
TUYA_CALL_ERR_RETURN(tuya_iot_reg_gw_mgr_cb(GP_DEV_ATH_1, &dev_mgr_cbs));
TUYA_CALL_ERR_RETURN(tuya_iot_reg_dp_cb(DP_DEV, GP_DEV_ATH_1, &dev_dp_cbs));
return OPRT_OK;
}
// ...
Zigbee 子设备升级使用的是标准 OTA Cluster,SDK 内部实现了固件下载和存储,以及把固件分块传输给子设备。提供了对应的接口给应用使用,简化应用的开发门槛。
关于如何把子设备固件上传到涂鸦开发者平台,可以参见 接入子设备指南-设备OTA。
设备升级的主要逻辑:
相关代码实现:
// ...
STATIC VOID __dev_upgrade_cb(CONST CHAR_T *dev_id, CONST FW_UG_S *fw)
{
OPERATE_RET op_ret = OPRT_OK;
op_ret = tuya_zigbee_upgrade_dev(dev_id, fw);
if (op_ret != OPRT_OK) {
PR_ERR("tuya_zigbee_upgrade_dev err: %d", op_ret);
return;
}
return;
}
STATIC VOID __my_z3_dev_upgrade_end(CONST CHAR_T *dev_id, INT_T rc, UCHAR_T version)
{
CHAR_T sw_ver[SW_VER_LEN+1] = {0};
if (rc != OPRT_OK) {
tuya_iot_dev_upgd_result_report(dev_id, GP_DEV_ATH_1, 4);
return;
}
CHAR_T iver1 = (CHAR_T)(version & 0x0F);
CHAR_T iver2 = (CHAR_T)((version >> 4) & 0x03);
CHAR_T iver3 = (CHAR_T)((version >> 6) & 0x03);
snprintf(sw_ver, SIZEOF(sw_ver), "%d.%d.%d", iver3, iver2, iver1);
tuya_iot_gw_subdevice_update(dev_id, sw_ver);
return;
}
STATIC OPERATE_RET __user_init(VOID)
{
OPERATE_RET rt = OPRT_OK;
/* Zigbee 子设备管理回调 */
TY_Z3_DEV_CBS_S z3_dev_cbs = {
.join = __my_z3_dev_join,
.leave = __my_z3_dev_leave,
.report = __my_z3_dev_report,
.notify = __my_z3_dev_notify,
.upgrade_end = __my_z3_dev_upgrade_end,
};
/* SDK 子设备管理回调 */
TY_IOT_DEV_CBS_S dev_mgr_cbs = {
.dev_del = __dev_del_cb,
.dev_bind_ifm = __dev_bind_cb,
.dev_hb = __dev_hb_cb,
.dev_reset = __dev_reset_cb,
.dev_upgrade = __dev_upgrade_cb,
};
TY_IOT_DP_CBS_S dev_dp_cbs = {
.obj = __dev_cmd_obj_cb,
};
TUYA_CALL_ERR_RETURN(tuya_zigbee_custom_dev_mgr_init(&my_z3_devlist, &z3_dev_cbs));
TUYA_CALL_ERR_RETURN(tuya_iot_reg_gw_mgr_cb(GP_DEV_ATH_1, &dev_mgr_cbs));
TUYA_CALL_ERR_RETURN(tuya_iot_reg_dp_cb(DP_DEV, GP_DEV_ATH_1, &dev_dp_cbs));
return OPRT_OK;
}
// ...
应用启动后,一般需要读取子设备当前的状态,然后上报到云端,实现状态同步,否则 App 面板上设备状态可能与真实状态不一致。
状态同步的主要逻辑:
相关代码实现:
// ...
STATIC VOID __my_z3_dev_report(TY_Z3_APS_FRAME_S *frame)
{
OPERATE_RET op_ret = OPRT_OK;
DEV_DESC_IF_S *dev_if = NULL;
if (frame->profile_id != PROFILE_ID_HA) {
return;
}
dev_if = tuya_iot_get_dev_if(frame->id);
if (!dev_if || !dev_if->bind) {
PR_ERR("dev_id: %s is not bind", frame->id);
tuya_zigbee_del_dev(frame->id);
return;
}
op_ret = tuya_iot_fresh_dev_hb(frame->id);
if (op_ret != OPRT_OK) {
PR_WARN("tuya_iot_fresh_dev_hb err: %d", op_ret);
}
UCHAR_T uc_devid = (((dev_if->uddd) >> 8) & 0xFF);
switch (uc_devid)
{
case TY_DEVID_HA_ONOF_LIGHT:
__z3_onoff_light_report(frame);
break;
case TY_DEVID_HA_IAS_ZONE:
case TY_DEVID_HA_TEMPERATURE_SENSOR:
case TY_DEVID_HA_SMART_PLUG:
default:
PR_WARN("devid: %d is not support", uc_devid);
return;
}
}
STATIC VOID __my_z3_dev_notify(VOID)
{
OPERATE_RET op_ret = OPRT_OK;
DEV_DESC_IF_S *dev_if = NULL;
VOID *iter = NULL;
dev_if = tuya_iot_dev_traversal(&iter);
while (dev_if) {
if ((dev_if->tp == GP_DEV_ATH_1) && dev_if->bind) {
op_ret = tuya_iot_set_dev_hb_cfg(dev_if->id, 120, 3, FALSE);
if (op_ret != OPRT_OK) {
PR_WARN("tuya_iot_set_dev_hb_cfg err: %d", op_ret);
}
UCHAR_T uc_devid = (((dev_if->uddd) >> 8) & 0xFF);
switch (uc_devid)
{
case TY_DEVID_HA_ONOF_LIGHT:
__z3_onoff_light_read_attr(dev_if->id);
break;
case TY_DEVID_HA_IAS_ZONE:
case TY_DEVID_HA_TEMPERATURE_SENSOR:
case TY_DEVID_HA_SMART_PLUG:
default:
PR_WARN("devid: %d is not support", uc_devid);
break;
}
}
dev_if = tuya_iot_dev_traversal(&iter);
}
}
STATIC OPERATE_RET __user_init(VOID)
{
OPERATE_RET rt = OPRT_OK;
/* Zigbee 子设备管理回调 */
TY_Z3_DEV_CBS_S z3_dev_cbs = {
.join = __my_z3_dev_join,
.leave = __my_z3_dev_leave,
.report = __my_z3_dev_report,
.notify = __my_z3_dev_notify,
};
/* SDK 子设备管理回调 */
TY_IOT_DEV_CBS_S dev_mgr_cbs = {
.dev_del = __dev_del_cb,
.dev_bind_ifm = __dev_bind_cb,
.dev_hb = __dev_hb_cb,
.dev_reset = __dev_reset_cb,
};
TY_IOT_DP_CBS_S dev_dp_cbs = {
.obj = __dev_cmd_obj_cb,
};
TUYA_CALL_ERR_RETURN(tuya_zigbee_custom_dev_mgr_init(&my_z3_devlist, &z3_dev_cbs));
TUYA_CALL_ERR_RETURN(tuya_iot_reg_gw_mgr_cb(GP_DEV_ATH_1, &dev_mgr_cbs));
TUYA_CALL_ERR_RETURN(tuya_iot_reg_dp_cb(DP_DEV, GP_DEV_ATH_1, &dev_dp_cbs));
return OPRT_OK;
}
// ...
#include <unistd.h>
#include "uni_log.h"
#include "base_os_adapter.h"
#include "tuya_iot_base_api.h"
#include "tuya_iot_com_api.h"
#include "tuya_iot_sdk_api.h"
#include "tuya_iot_sdk_defs.h"
#include "tuya_zigbee_api.h"
#if defined(TY_BT_MOD) && (TY_BT_MOD == 1)
#include "tuya_os_adapt_bt.h"
#endif
#define PID "fljmamufiym5fktz" // 替换成自己的产品 ID
#define UUID "tuya461dbc63aeeb991f" // 替换成自己的 UUID
#define AUTHKEY "c8X4PR4wx1gMFaQlaZu5dfgVvVRwB8Ug" // 替换成自己的 AUTHKEY
STATIC VOID __gw_reset_cb(GW_RESET_TYPE_E type)
{
PR_DEBUG("gw reset callback, type: %d", type);
if (GW_RESET_DATA_FACTORY != type) {
exit(0);
}
return;
}
STATIC VOID __gw_upgrade_cb(CONST FW_UG_S *fw)
{
PR_DEBUG("gw upgrade callback");
if (fw == NULL) {
PR_ERR("invalid param");
return;
}
PR_DEBUG(" tp: %d", fw->tp);
PR_DEBUG(" fw_url: %s", fw->fw_url);
PR_DEBUG(" sw_ver: %s", fw->sw_ver);
PR_DEBUG(" fw_hmac: %s", fw->fw_hmac);
PR_DEBUG(" file_size: %u", fw->file_size);
return;
}
STATIC VOID __gw_active_stat_cb(GW_STATUS_E status)
{
PR_DEBUG("gw active stat callback, status: %d", status);
return;
}
STATIC VOID __gw_reboot_cb(VOID)
{
PR_DEBUG("gw reboot callback");
exit(0);
return;
}
STATIC VOID __nw_stat_cb(IN CONST SDK_NW_STAT_T stat)
{
PR_DEBUG("network stat: %d", stat);
return;
}
STATIC VOID __wired_stat_cb(IN CONST SDK_WIRED_NW_STAT_T stat)
{
PR_DEBUG("wired stat: %d", stat);
return;
}
int main(int argc, char **argv)
{
OPERATE_RET rt = OPRT_OK;
GW_PROD_INFO_S prod_info = {0};
/* gw base callback */
TY_GW_INFRA_CBS_S gw_cbs = {
.gw_reset_cb = __gw_reset_cb,
.gw_upgrade_cb = __gw_upgrade_cb,
.gw_active_stat_cb = __gw_active_stat_cb,
.gw_reboot_cb = __gw_reboot_cb,
};
#if defined(TY_BT_MOD) && (TY_BT_MOD == 1)
tuya_os_adapt_reg_bt_intf();
#endif
/* initiate os-layer service*/
tuya_os_intf_init();
/* initiate iot-layer service */
TUYA_CALL_ERR_RETURN(tuya_iot_init("./"));
/* set the logging level to debug */
SET_PR_DEBUG_LEVEL(TY_LOG_LEVEL_DEBUG);
PR_DEBUG("SDK INFO: %s", tuya_iot_get_sdk_info());
/* set uuid and authkey */
prod_info.uuid = UUID;
prod_info.auth_key = AUTHKEY;
TUYA_CALL_ERR_RETURN(tuya_iot_set_gw_prod_info(&prod_info));
/* pre-initiate sdk service */
TUYA_CALL_ERR_RETURN(tuya_iot_sdk_pre_init(TRUE));
ty_cJSON *zb_cfg = ty_cJSON_CreateObject();
if (zb_cfg == NULL) {
return OPRT_CJSON_GET_ERR;
}
ty_cJSON_AddStringToObject(zb_cfg, "storage_path", "./");
ty_cJSON_AddStringToObject(zb_cfg, "cache_path", "/tmp/");
ty_cJSON_AddStringToObject(zb_cfg, "dev_name", "/dev/ttyS2");
ty_cJSON_AddNumberToObject(zb_cfg, "cts", 1);
ty_cJSON_AddNumberToObject(zb_cfg, "thread_mode", 1);
/* initiate application service, more service in here */
TUYA_CALL_ERR_RETURN(tuya_zigbee_svc_init(zb_cfg));
TUYA_CALL_ERR_RETURN(tuya_user_svc_init(&gw_cbs));
/* initiate sdk service */
#if defined(GW_SUPPORT_WIRED_WIFI) && (GW_SUPPORT_WIRED_WIFI==1)
TUYA_CALL_ERR_RETURN(tuya_iot_wired_wf_sdk_init(IOT_GW_NET_WIRED_WIFI, GWCM_OLD, WF_START_AP_ONLY, PID, USER_SW_VER, NULL, 0));
#elif defined(WIFI_GW) && (WIFI_GW==1)
TUYA_CALL_ERR_RETURN(tuya_iot_wf_sdk_init(GWCM_OLD, WF_START_AP_ONLY, PID, USER_SW_VER, NULL, 0));
#else
TUYA_CALL_ERR_RETURN(tuya_iot_sdk_init(PID, USER_SW_VER, NULL, 0));
#endif
/* register net stat notification callback */
TUYA_CALL_ERR_RETURN(tuya_iot_sdk_reg_netstat_cb(__nw_stat_cb, __wired_stat_cb, NULL));
/* start application service, more service in here */
TUYA_CALL_ERR_RETURN(tuya_zigbee_svc_start(zb_cfg));
TUYA_CALL_ERR_RETURN(tuya_user_svc_start(NULL));
while (1) {
sleep(10);
}
return 0;
}
#include <stdio.h>
#include "uni_log.h"
#include "base_os_adapter.h"
#include "tuya_hal_system.h"
#include "tuya_iot_base_api.h"
#include "tuya_iot_com_api.h"
#include "tuya_iot_sdk_api.h"
#include "tuya_iot_sdk_defs.h"
#include "tuya_zigbee_api.h"
#include "tuya_gw_subdev_api.h"
#if defined(TY_BT_MOD) && (TY_BT_MOD == 1)
#include "tuya_os_adapt_bt.h"
#endif
#define PID "fljmamufiym5fktz" // 替换成自己的产品 ID
#define UUID "tuya461dbc63aeeb991f" // 替换成自己的 UUID
#define AUTHKEY "c8X4PR4wx1gMFaQlaZu5dfgVvVRwB8Ug" // 替换成自己的 AUTHKEY
STATIC VOID __gw_reset_cb(GW_RESET_TYPE_E type)
{
PR_DEBUG("gw reset callback, type: %d", type);
if (GW_RESET_DATA_FACTORY != type) {
exit(0);
}
return;
}
STATIC VOID __gw_upgrade_cb(CONST FW_UG_S *fw)
{
PR_DEBUG("gw upgrade callback");
if (fw == NULL) {
PR_ERR("invalid param");
return;
}
PR_DEBUG(" tp: %d", fw->tp);
PR_DEBUG(" fw_url: %s", fw->fw_url);
PR_DEBUG(" sw_ver: %s", fw->sw_ver);
PR_DEBUG(" fw_hmac: %s", fw->fw_hmac);
PR_DEBUG(" file_size: %u", fw->file_size);
return;
}
STATIC VOID __gw_active_stat_cb(GW_STATUS_E status)
{
PR_DEBUG("gw active stat callback, status: %d", status);
return;
}
STATIC VOID __gw_reboot_cb(VOID)
{
PR_DEBUG("gw reboot callback");
exit(0);
return;
}
STATIC VOID __nw_stat_cb(IN CONST SDK_NW_STAT_T stat)
{
PR_DEBUG("network stat: %d", stat);
return;
}
STATIC VOID __wired_stat_cb(IN CONST SDK_WIRED_NW_STAT_T stat)
{
PR_DEBUG("wired stat: %d", stat);
return;
}
#define TY_TS_ONOF_LIGHT_ONE "TS0001"
#define TY_TS_ONOF_LIGHT_TWO "TS0002"
#define TY_TS_ONOF_LIGHT_THR "TS0003"
#define PROFILE_ID_HA 0x0104
#define PROFILE_ID_LL 0xC05E
#define ZCL_BASIC_CLUSTER_ID 0x0000
#define ZCL_POWER_CONFIG_CLUSTER_ID 0x0001
#define ZCL_DEVICE_TEMP_CLUSTER_ID 0x0002
#define ZCL_IDENTIFY_CLUSTER_ID 0x0003
#define ZCL_GROUPS_CLUSTER_ID 0x0004
#define ZCL_SCENES_CLUSTER_ID 0x0005
#define ZCL_ON_OFF_CLUSTER_ID 0x0006
#define ZCL_CMD_TYPE_GLOBAL 0x00
#define ZCL_CMD_TYPE_PRIVATE 0x01
#define ZCL_FRAME_TYPE_UNICAST 0x00
#define ZCL_FRAME_TYPE_MULTICAST 0x01
#define ZCL_FRAME_TYPE_BROADCAST 0x02
#define ZCL_OFF_COMMAND_ID 0x00
#define ZCL_ON_COMMAND_ID 0x01
#define ZCL_READ_ATTRIBUTES_COMMAND_ID 0x00
#define ZCL_READ_ATTRIBUTES_RESPONSE_COMMAND_ID 0x01
#define ZCL_REPORT_ATTRIBUTES_COMMAND_ID 0x0A
#define ZCL_READ_ATTRIBUTER_RESPONSE_HEADER 3 /* Attributer ID: 2 Bytes, Status: 1 Btye */
#define ZCL_REPORT_ATTRIBUTES_HEADER 2 /* Attributer ID: 2 Bytes */
#define DEVID_HA_IAS_ZONE 0x0402 // 0x0402 -> 0x00,IAS类,pir, 门磁
#define DEVID_HA_TEMPERATURE_SENSOR 0x0302 // 0x0302 -> 0x01,温度类(含温湿度)
#define DEVID_HA_ONOF_LIGHT 0x0100 // 0x0100 -> 0x02,照明开关类
#define DEVID_HA_SMART_PLUG 0x0051 // 0x0051 -> 0x03, 智能插座类
/* 举例,自定义 DEVID,保存到 UDDD */
#define TY_DEVID_HA_IAS_ZONE 0x00 // 0x0402 -> 0x00,IAS类,pir, 门磁
#define TY_DEVID_HA_TEMPERATURE_SENSOR 0x01 // 0x0302 -> 0x01,温度类(含温湿度)
#define TY_DEVID_HA_ONOF_LIGHT 0x02 // 0x0100 -> 0x02,照明开关类
#define TY_DEVID_HA_SMART_PLUG 0x03 // 0x0051 -> 0x03, 智能类插座类
/* 举例,自定义 TYPE,保存到 UDDD */
#define TY_TYPE_PIR 0x00 // DEVID_HA_IAS_ZONE
#define TY_TYPE_DOOR_SENSOR 0x01 // DEVID_HA_IAS_ZONE
#define TY_TYPE_FIRE_SENSOR 0x02 // DEVID_HA_IAS_ZONE
#define TY_TYPE_WATER_SENSOR 0x03 // DEVID_HA_IAS_ZONE
#define TY_TYPE_ONOF_LIGHT_ONE 0x00 // DEVID_HA_ONOF_LIGHT
#define TY_TYPE_ONOF_LIGHT_TWO 0x01 // DEVID_HA_ONOF_LIGHT
#define TY_TYPE_ONOF_LIGHT_THREE 0x02 // DEVID_HA_ONOF_LIGHT
TY_Z3_DEV_S my_z3_dev[] = {
{ "TUYATEC-nPGIPl5D", "TS0001" },
{ "TUYATEC-1xAC7DHQ", "TS0002" },
};
TY_Z3_DEVLIST_S my_z3_devlist = {
.devs = my_z3_dev,
.dev_num = CNTSOF(my_z3_dev),
};
STATIC VOID __z3_onoff_light_write_attr(CONST TY_RECV_OBJ_DP_S *cmd)
{
INT_T i= 0;
OPERATE_RET op_ret = OPRT_OK;
TY_Z3_APS_FRAME_S frame = {0};
strncpy(frame.id, cmd->cid, SIZEOF(frame.id));
frame.profile_id = PROFILE_ID_HA;
frame.src_endpoint = 0x01;
for (i = 0; i < cmd->dps_cnt; i++) {
switch (cmd->dps[i].dpid) {
case 1 ... 3:
frame.dst_endpoint = cmd->dps[i].dpid;
frame.cluster_id = ZCL_ON_OFF_CLUSTER_ID;
frame.cmd_type = ZCL_CMD_TYPE_PRIVATE;
frame.frame_type = ZCL_FRAME_TYPE_UNICAST;
frame.msg_length = 0;
if (cmd->dps[i].value.dp_bool) {
frame.cmd_id = ZCL_ON_COMMAND_ID;
} else {
frame.cmd_id = ZCL_OFF_COMMAND_ID;
}
break;
default:
PR_WARN("dev_id: %s, dpid: %d is not supported", cmd->cid, cmd->dps[i].dpid);
return;
}
op_ret = tuya_zigbee_send_data(&frame);
if (op_ret != OPRT_OK) {
PR_WARN("tuya_zigbee_send_data err: %d", op_ret);
}
}
}
STATIC VOID __z3_onoff_light_read_attr(CONST CHAR_T *dev_id)
{
OPERATE_RET op_ret = OPRT_OK;
TY_Z3_APS_FRAME_S frame = {0};
USHORT_T atrr_buf[5] = { 0x0000, 0x4001, 0x4002, 0x8001, 0x5000 };
strncpy(frame.id, dev_id, SIZEOF(frame.id));
frame.profile_id = PROFILE_ID_HA;
frame.cluster_id = ZCL_ON_OFF_CLUSTER_ID;
frame.cmd_type = ZCL_CMD_TYPE_GLOBAL;
frame.src_endpoint = 0x01;
frame.dst_endpoint = 0xFF;
frame.cmd_id = ZCL_READ_ATTRIBUTES_COMMAND_ID;
frame.msg_length = SIZEOF(atrr_buf);
frame.message = (UCHAR_T *)atrr_buf;
op_ret = tuya_zigbee_send_data(&frame);
if (op_ret != OPRT_OK) {
PR_ERR("tuya_zigbee_send_data err: %d", op_ret);
return;
}
}
STATIC VOID __z3_onoff_light_report(TY_Z3_APS_FRAME_S *frame)
{
OPERATE_RET op_ret = OPRT_OK;
TY_OBJ_DP_S dp_data = {0};
if (frame->cluster_id != ZCL_ON_OFF_CLUSTER_ID) {
return;
}
dp_data.dpid = frame->src_endpoint;
dp_data.type = PROP_BOOL;
if (frame->cmd_id == ZCL_REPORT_ATTRIBUTES_COMMAND_ID) {
dp_data.value.dp_bool = frame->message[ZCL_REPORT_ATTRIBUTES_HEADER+1];
} else if (frame->cmd_id == ZCL_READ_ATTRIBUTES_RESPONSE_COMMAND_ID) {
dp_data.value.dp_bool = frame->message[ZCL_READ_ATTRIBUTER_RESPONSE_HEADER+1];
} else {
return;
}
op_ret = dev_report_dp_json_async(frame->id, &dp_data, 1);
if (op_ret != OPRT_OK) {
PR_ERR("dev_report_dp_json_async err: %d", op_ret);
return;
}
}
STATIC VOID __my_z3_dev_report(TY_Z3_APS_FRAME_S *frame)
{
OPERATE_RET op_ret = OPRT_OK;
DEV_DESC_IF_S *dev_if = NULL;
if (frame->profile_id != PROFILE_ID_HA) {
return;
}
dev_if = tuya_iot_get_dev_if(frame->id);
if (!dev_if || !dev_if->bind) {
PR_ERR("dev_id: %s is not bind", frame->id);
tuya_zigbee_del_dev(frame->id);
return;
}
op_ret = tuya_iot_fresh_dev_hb(frame->id);
if (op_ret != OPRT_OK) {
PR_WARN("tuya_iot_fresh_dev_hb err: %d", op_ret);
}
UCHAR_T uc_devid = (((dev_if->uddd) >> 8) & 0xFF);
switch (uc_devid)
{
case TY_DEVID_HA_ONOF_LIGHT:
__z3_onoff_light_report(frame);
break;
case TY_DEVID_HA_IAS_ZONE:
case TY_DEVID_HA_TEMPERATURE_SENSOR:
case TY_DEVID_HA_SMART_PLUG:
default:
PR_WARN("devid: %d is not support", uc_devid);
return;
}
}
STATIC VOID __my_z3_dev_notify(VOID)
{
OPERATE_RET op_ret = OPRT_OK;
DEV_DESC_IF_S *dev_if = NULL;
VOID *iter = NULL;
dev_if = tuya_iot_dev_traversal(&iter);
while (dev_if) {
if ((dev_if->tp == GP_DEV_ATH_1) && dev_if->bind) {
op_ret = tuya_iot_set_dev_hb_cfg(dev_if->id, 120, 3, FALSE);
if (op_ret != OPRT_OK) {
PR_WARN("tuya_iot_set_dev_hb_cfg err: %d", op_ret);
}
UCHAR_T uc_devid = (((dev_if->uddd) >> 8) & 0xFF);
switch (uc_devid)
{
case TY_DEVID_HA_ONOF_LIGHT:
__z3_onoff_light_read_attr(dev_if->id);
break;
case TY_DEVID_HA_IAS_ZONE:
case TY_DEVID_HA_TEMPERATURE_SENSOR:
case TY_DEVID_HA_SMART_PLUG:
default:
PR_WARN("devid: %d is not support", uc_devid);
break;
}
}
dev_if = tuya_iot_dev_traversal(&iter);
}
}
STATIC VOID __my_z3_dev_upgrade_end(CONST CHAR_T *dev_id, INT_T rc, UCHAR_T version)
{
CHAR_T sw_ver[SW_VER_LEN+1] = {0};
if (rc != OPRT_OK) {
tuya_iot_dev_upgd_result_report(dev_id, GP_DEV_ATH_1, 4);
return;
}
CHAR_T iver1 = (CHAR_T)(version & 0x0F);
CHAR_T iver2 = (CHAR_T)((version >> 4) & 0x03);
CHAR_T iver3 = (CHAR_T)((version >> 6) & 0x03);
snprintf(sw_ver, SIZEOF(sw_ver), "%d.%d.%d", iver3, iver2, iver1);
tuya_iot_gw_subdevice_update(dev_id, sw_ver);
return;
}
STATIC VOID __my_z3_dev_join(TY_Z3_DESC_S *dev)
{
OPERATE_RET op_ret = OPRT_OK;
CHAR_T pid[PRODUCT_KEY_LEN+1]={0};
CHAR_T sw_ver[SW_VER_LEN+1] = {0};
USER_DEV_DTL_DEF_T uddd = 0;
/**
* 以下代码仅作为演示,开发者一般需要实现的逻辑:
* a) 解析设备信息,取关键信息,保存到 uddd,用于标识设备类型等;
* b) 关联 PID,调用绑定接口把设备绑定到云端。
*/
if (dev->profile_id[0] != PROFILE_ID_HA) {
return;
}
CHAR_T iver1 = (CHAR_T)(dev->version & 0x0F);
CHAR_T iver2 = (CHAR_T)((dev->version >> 4) & 0x03);
CHAR_T iver3 = (CHAR_T)((dev->version >> 6) & 0x03);
snprintf(sw_ver, SIZEOF(sw_ver), "%d.%d.%d", iver3, iver2, iver1);
switch (dev->device_id[0]) {
case DEVID_HA_ONOF_LIGHT:
uddd |= (TY_DEVID_HA_ONOF_LIGHT << 8);
if (0 == strncasecmp(dev->model_id, TY_TS_ONOF_LIGHT_ONE, strlen(TY_TS_ONOF_LIGHT_ONE))) {
uddd |= (TY_TYPE_ONOF_LIGHT_ONE & 0xFF);
strncpy(pid, "ckj1pnvy", SIZEOF(pid));
} else if (0 == strncasecmp(dev->model_id, TY_TS_ONOF_LIGHT_TWO, strlen(TY_TS_ONOF_LIGHT_TWO))) {
uddd |= (TY_TYPE_ONOF_LIGHT_TWO & 0xFF);
strncpy(pid, "tr4dshuh", SIZEOF(pid));
} else {
PR_WARN("model_id: %s is not supported", dev->model_id);
return;
}
break;
case DEVID_HA_IAS_ZONE:
case DEVID_HA_TEMPERATURE_SENSOR:
case DEVID_HA_SMART_PLUG:
default:
PR_WARN("devid: %d is not supported", dev->device_id[0]);
return;
}
op_ret = tuya_iot_gw_bind_dev(GP_DEV_ATH_1, uddd, dev->id, pid, sw_ver);
if (op_ret != OPRT_OK) {
PR_ERR("tuya_iot_gw_bind_dev err: %d", op_ret);
return;
}
}
STATIC VOID __my_z3_dev_leave(CONST CHAR_T *dev_id)
{
OPERATE_RET op_ret = OPRT_OK;
op_ret = tuya_iot_gw_unbind_dev(dev_id);
if (op_ret != OPRT_OK) {
PR_ERR("tuya_iot_gw_unbind_dev err: %d", op_ret);
return;
}
}
STATIC VOID __dev_del_cb(CONST CHAR_T *dev_id, CONST GW_DELDEV_TYPE type)
{
OPERATE_RET op_ret = OPRT_OK;
op_ret = tuya_zigbee_del_dev(dev_id);
if (op_ret != OPRT_OK) {
PR_ERR("tuya_zigbee_del_dev err: %d", op_ret);
return;
}
}
STATIC VOID __dev_reset_cb(CONST CHAR_T *dev_id, DEV_RESET_TYPE_E type)
{
OPERATE_RET op_ret = OPRT_OK;
op_ret = tuya_zigbee_del_dev(dev_id);
if (op_ret != OPRT_OK) {
PR_ERR("tuya_zigbee_del_dev err: %d", op_ret);
return;
}
}
STATIC VOID __dev_upgrade_cb(CONST CHAR_T *dev_id, CONST FW_UG_S *fw)
{
OPERATE_RET op_ret = OPRT_OK;
op_ret = tuya_zigbee_upgrade_dev(dev_id, fw);
if (op_ret != OPRT_OK) {
PR_ERR("tuya_zigbee_upgrade_dev err: %d", op_ret);
return;
}
return;
}
STATIC VOID __dev_bind_cb(CONST CHAR_T *dev_id, CONST OPERATE_RET result)
{
OPERATE_RET op_ret = OPRT_OK;
DEV_DESC_IF_S *dev_if = NULL;
/**
* 以下代码仅作为演示,开发者一般需要实现的逻辑:
* a) 根据绑定状态,封装读取属性 ZCL 数据发给子设备。
* b) 配置心跳超时。
*/
if (result != OPRT_OK) {
PR_ERR("dev_id: %s bind err", dev_id);
tuya_zigbee_del_dev(dev_id);
return;
}
op_ret = tuya_iot_set_dev_hb_cfg(dev_id, 120, 3, FALSE);
if (op_ret != OPRT_OK) {
PR_WARN("tuya_iot_set_dev_hb_cfg err: %d", op_ret);
}
op_ret = tuya_iot_fresh_dev_hb(dev_id);
if (op_ret != OPRT_OK) {
PR_WARN("tuya_iot_fresh_dev_hb err: %d", op_ret);
}
dev_if = tuya_iot_get_dev_if(dev_id);
if (!dev_if || !dev_if->bind) {
PR_ERR("dev_id: %s is not bind", dev_id);
tuya_zigbee_del_dev(dev_id);
return;
}
UCHAR_T uc_devid = (((dev_if->uddd) >> 8) & 0xFF);
switch (uc_devid)
{
case TY_DEVID_HA_ONOF_LIGHT:
__z3_onoff_light_read_attr(dev_id);
break;
case TY_DEVID_HA_IAS_ZONE:
case TY_DEVID_HA_TEMPERATURE_SENSOR:
case TY_DEVID_HA_SMART_PLUG:
default:
PR_WARN("devid: %d is not support", uc_devid);
return;
}
}
STATIC VOID __dev_hb_cb(CONST CHAR_T *dev_id)
{
OPERATE_RET op_ret = OPRT_OK;
TY_Z3_APS_FRAME_S frame = {0};
USHORT_T atrr_buf[1] = { 0x0001 };
strncpy(frame.id, dev_id, SIZEOF(frame.id));
frame.profile_id = PROFILE_ID_HA;
frame.cluster_id = ZCL_BASIC_CLUSTER_ID;
frame.cmd_type = ZCL_CMD_TYPE_GLOBAL;
frame.src_endpoint = 0x01;
frame.dst_endpoint = 0xFF;
frame.cmd_id = ZCL_READ_ATTRIBUTES_COMMAND_ID;
frame.msg_length = SIZEOF(atrr_buf);
frame.message = (UCHAR_T *)atrr_buf;
op_ret = tuya_zigbee_send_data(&frame);
if (op_ret != OPRT_OK) {
PR_ERR("tuya_zigbee_send_data err: %d", op_ret);
return;
}
}
STATIC VOID __dev_cmd_obj_cb(CONST TY_RECV_OBJ_DP_S *cmd)
{
DEV_DESC_IF_S *dev_if = NULL;
/**
* 以下代码仅作为演示,开发者一般需要实现的逻辑:
* a) 获取设备的 UDDD,根据 UDDD 映射到设备类型。
* b) 根据 UDDD,把功能点转成 ZCL 数据,下发给子设备。
*/
dev_if = tuya_iot_get_dev_if(cmd->cid);
if (!dev_if || !dev_if->bind) {
PR_ERR("dev_id: %s is not bind", cmd->cid);
return;
}
UCHAR_T uc_devid = (((dev_if->uddd) >> 8) & 0xFF);
switch (uc_devid)
{
case TY_DEVID_HA_ONOF_LIGHT:
__z3_onoff_light_write_attr(cmd);
break;
case TY_DEVID_HA_IAS_ZONE:
case TY_DEVID_HA_TEMPERATURE_SENSOR:
case TY_DEVID_HA_SMART_PLUG:
default:
PR_WARN("devid: %d is not support", uc_devid);
return;
}
}
STATIC OPERATE_RET __user_init(VOID)
{
OPERATE_RET rt = OPRT_OK;
/* Zigbee 子设备管理回调 */
TY_Z3_DEV_CBS_S z3_dev_cbs = {
.join = __my_z3_dev_join,
.leave = __my_z3_dev_leave,
.report = __my_z3_dev_report,
.notify = __my_z3_dev_notify,
.upgrade_end = __my_z3_dev_upgrade_end,
};
/* SDK 子设备管理回调 */
TY_IOT_DEV_CBS_S dev_mgr_cbs = {
.dev_del = __dev_del_cb,
.dev_bind_ifm = __dev_bind_cb,
.dev_hb = __dev_hb_cb,
.dev_reset = __dev_reset_cb,
.dev_upgrade = __dev_upgrade_cb,
};
TY_IOT_DP_CBS_S dev_dp_cbs = {
.obj = __dev_cmd_obj_cb,
};
TUYA_CALL_ERR_RETURN(tuya_zigbee_custom_dev_mgr_init(&my_z3_devlist, &z3_dev_cbs));
TUYA_CALL_ERR_RETURN(tuya_iot_reg_gw_mgr_cb(GP_DEV_ATH_1, &dev_mgr_cbs));
TUYA_CALL_ERR_RETURN(tuya_iot_reg_dp_cb(DP_DEV, GP_DEV_ATH_1, &dev_dp_cbs));
return OPRT_OK;
}
int main(int argc, char **argv)
{
OPERATE_RET rt = OPRT_OK;
GW_PROD_INFO_S prod_info = {0};
/* gw base callback */
TY_GW_INFRA_CBS_S gw_cbs = {
.gw_reset_cb = __gw_reset_cb,
.gw_upgrade_cb = __gw_upgrade_cb,
.gw_active_stat_cb = __gw_active_stat_cb,
.gw_reboot_cb = __gw_reboot_cb,
};
#if defined(TY_BT_MOD) && (TY_BT_MOD == 1)
tuya_os_adapt_reg_bt_intf();
#endif
/* initiate os-layer service*/
tuya_os_intf_init();
/* initiate iot-layer service */
TUYA_CALL_ERR_RETURN(tuya_iot_init("./"));
/* set the logging level to debug */
SET_PR_DEBUG_LEVEL(TY_LOG_LEVEL_DEBUG);
PR_DEBUG("SDK INFO: %s", tuya_iot_get_sdk_info());
/* set uuid and authkey */
prod_info.uuid = UUID;
prod_info.auth_key = AUTHKEY;
TUYA_CALL_ERR_RETURN(tuya_iot_set_gw_prod_info(&prod_info));
/* pre-initiate sdk service */
TUYA_CALL_ERR_RETURN(tuya_iot_sdk_pre_init(TRUE));
ty_cJSON *zb_cfg = ty_cJSON_CreateObject();
if (zb_cfg == NULL) {
return OPRT_CJSON_GET_ERR;
}
ty_cJSON_AddStringToObject(zb_cfg, "storage_path", "./");
ty_cJSON_AddStringToObject(zb_cfg, "cache_path", "/tmp/");
ty_cJSON_AddStringToObject(zb_cfg, "dev_name", "/dev/ttyS2");
ty_cJSON_AddNumberToObject(zb_cfg, "cts", 1);
ty_cJSON_AddNumberToObject(zb_cfg, "thread_mode", 1);
/* initiate application service, more service in here */
TUYA_CALL_ERR_RETURN(tuya_zigbee_svc_init(zb_cfg));
TUYA_CALL_ERR_RETURN(tuya_user_svc_init(&gw_cbs));
TUYA_CALL_ERR_RETURN(__user_init());
/* initiate sdk service */
#if defined(GW_SUPPORT_WIRED_WIFI) && (GW_SUPPORT_WIRED_WIFI==1)
TUYA_CALL_ERR_RETURN(tuya_iot_wired_wf_sdk_init(IOT_GW_NET_WIRED_WIFI, GWCM_OLD, WF_START_AP_ONLY, PID, USER_SW_VER, NULL, 0));
#elif defined(WIFI_GW) && (WIFI_GW==1)
TUYA_CALL_ERR_RETURN(tuya_iot_wf_sdk_init(GWCM_OLD, WF_START_AP_ONLY, PID, USER_SW_VER, NULL, 0));
#else
TUYA_CALL_ERR_RETURN(tuya_iot_sdk_init(PID, USER_SW_VER, NULL, 0));
#endif
/* register net stat notification callback */
TUYA_CALL_ERR_RETURN(tuya_iot_sdk_reg_netstat_cb(__nw_stat_cb, __wired_stat_cb, NULL));
/* start application service, more service in here */
TUYA_CALL_ERR_RETURN(tuya_zigbee_svc_start(zb_cfg));
TUYA_CALL_ERR_RETURN(tuya_user_svc_start(NULL));
tuya_user_svc_start(NULL);
while (1) {
tuya_hal_system_sleep(10*1000);
}
return 0;
}
该内容对您有帮助吗?
是意见反馈该内容对您有帮助吗?
是意见反馈