package com.hdl.sdk.link.zigbee;
|
|
import com.google.gson.JsonArray;
|
import com.google.gson.JsonObject;
|
import com.google.gson.reflect.TypeToken;
|
import com.hdl.link.error.HDLLinkCode;
|
import com.hdl.sdk.link.common.event.EventListener;
|
import com.hdl.sdk.link.common.exception.HDLLinkException;
|
import com.hdl.sdk.link.common.utils.LogUtils;
|
import com.hdl.sdk.link.common.utils.ThreadToolUtils;
|
import com.hdl.sdk.link.common.utils.gson.GsonConvert;
|
import com.hdl.sdk.link.core.bean.ZigbeeResponse;
|
import com.hdl.sdk.link.core.callback.ZigbeeCallBack;
|
import com.hdl.sdk.link.core.connect.HDLZigbeeConnect;
|
import com.hdl.sdk.link.zigbee.bean.PanelVibrationBean;
|
import com.hdl.sdk.link.zigbee.bean.TimeDataBean;
|
import com.hdl.sdk.link.zigbee.bean.ZBBaseSendBean;
|
import com.hdl.sdk.link.zigbee.bean.ZBDeviceBean;
|
import com.hdl.sdk.link.zigbee.callback.ZBDeviceListCallBack;
|
import com.hdl.sdk.link.zigbee.config.ZigBee;
|
|
import java.util.ArrayList;
|
import java.util.List;
|
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
/**
|
* Created by jlchen on 12/16/21.
|
* ZigBee原生协议相关
|
*/
|
public class HDLLinkLocalZigBee {
|
private static final String TAG = "HDLLinkLocalZigBee";
|
private static final String Cluster_ID = "Cluster_ID";
|
private static final String Command = "Command";
|
//instance
|
private volatile static HDLLinkLocalZigBee instance;
|
|
//getInstance
|
public static synchronized HDLLinkLocalZigBee getInstance() {
|
if (instance == null) {
|
synchronized (HDLLinkLocalZigBee.class) {
|
if (instance == null) {
|
instance = new HDLLinkLocalZigBee();
|
}
|
}
|
}
|
return instance;
|
}
|
|
|
/**
|
* 发送消息给网关
|
*
|
* @param gatewayOid
|
* @param replyTopic 回复主题
|
* @param payload
|
* @param callback
|
*/
|
public void sendData(String gatewayOid, String replyTopic, String payload, ZigbeeCallBack callback) {
|
//注意 gatewayId是填要连接的网关,可以是主网关的Id,也可以是从网关的Id
|
HDLZigbeeConnect.getInstance().Send(gatewayOid, replyTopic, payload, callback);
|
}
|
|
/********************************网关相关********************************/
|
/**
|
* 发送指令到网关进行定位(网关LED闪烁识别)
|
*
|
* @param gatewayOid 网关id
|
* @param callback
|
*/
|
public void locationGateway(String gatewayOid, ZigbeeCallBack callback) {
|
JsonObject jObject = new JsonObject();
|
jObject.addProperty(Cluster_ID, 0);
|
jObject.addProperty(Command, ZigBee.CommandType.GwLinuxLocate);
|
sendData(gatewayOid, ZigBee.ReplyTopicType.GwLinuxLocate, jObject.toString(), callback);
|
}
|
|
/********************************设备相关********************************/
|
/**
|
* 设置设备入网方式
|
* 设置1次之后保持设置的方式
|
* 重启恢复默认方式
|
*
|
* @param gatewayOid 网关oid
|
* @param deviceJoiningMode 0:默认;1:新方式(快速入网)
|
*/
|
public void setDeviceJoiningMode(String gatewayOid, int deviceJoiningMode, ZigbeeCallBack callback) {
|
JsonObject jObject = new JsonObject();
|
jObject.addProperty("Cluster_ID", 0);
|
jObject.addProperty("Command", 180);
|
JsonObject data = new JsonObject();
|
data.addProperty("mode", deviceJoiningMode);
|
jObject.add("Data", data);
|
sendData(gatewayOid, ZigBee.ReplyTopicType.SetJoiningModeRespon, jObject.toString(), callback);
|
}
|
|
/**
|
* 设置搜索新设备开启或者关闭
|
*
|
* @param gatewayOid 网关oid
|
* @param time 开启搜索设备的时间(单位:秒) 0:关闭搜索 255:一直开启
|
* @param callback
|
*/
|
public void sendSearchDevice(String gatewayOid, int time, ZigbeeCallBack callback) {
|
ZBBaseSendBean<TimeDataBean> baseSendBean = new ZBBaseSendBean(0, ZigBee.CommandType.SearchNewDevice, new TimeDataBean(time));
|
sendData(gatewayOid, ZigBee.ReplyTopicType.SearchNewDevice, GsonConvert.getGson().toJson(baseSendBean), callback);
|
}
|
|
/**
|
* 搜索设备
|
*
|
* @param gatewayOid
|
* @param callback
|
*/
|
public void startSearchDevice(String gatewayOid, ZigbeeCallBack callback) {
|
sendSearchDevice(gatewayOid, 255, callback);
|
}
|
|
/**
|
* 暂停搜索设备
|
*
|
* @param gatewayOid
|
* @param callback
|
*/
|
public void stopSearchDevice(String gatewayOid, ZigbeeCallBack callback) {
|
sendSearchDevice(gatewayOid, 0, callback);
|
}
|
|
/**
|
* 定位设备 闪烁5秒
|
* 该条指令设备不回复,所有没callBack
|
* 发送该命令,设备指示灯将闪烁。cluster 列表中支持 Identify 功能,即包含 cluster=3 的设 备可以使用该接口。
|
*
|
* @param gatewayOid
|
* @param deviceAddr 单播时为设备节点的 mac 地 址 类型是字符(16 个字符) 组播时为组 id 类型是数值 广播时为 null
|
* @param epoint 设备端口号 和 mac 地址共同标识唯一的 zigbee 设备 数值范围 0-255
|
*/
|
public void locationDevice(String gatewayOid, String deviceAddr, int epoint) {
|
JsonObject jObject = new JsonObject();
|
jObject.addProperty("DeviceAddr", deviceAddr);
|
jObject.addProperty("Epoint", epoint);
|
jObject.addProperty(Cluster_ID, 3);
|
jObject.addProperty(Command, 0);
|
jObject.addProperty("SendMode", 2);//发送模式 组播: 1 单播: 2 广播: 15
|
JsonObject data = new JsonObject();
|
data.addProperty("Time", 10);//闪烁时间(秒)
|
jObject.add("Data", data);
|
sendData(gatewayOid, ZigBee.ReplyTopicType.Identify, jObject.toString(), null);
|
}
|
|
|
/**
|
* 设备列表-设备删除(使设备离网)
|
* 通过该指令可以使设备脱离当前 zigbee 网络,该设备信息也将会在网关上删除。
|
* 注意:将会删除同一 MAC 地址下所有不同端口的设备,如一个 2 路按键设备,它具有两个 按键,在设备列表上这两个按键具有相同的 mac,但端口号是不同的。使用该指令让任意 一个按键离网,另一个也会离网。
|
*
|
* @param gatewayOid
|
* @param compelClear 0:不强制清除。需要节点设备 反馈离网确认信息后才能删除设 备信息。
|
* 1:强制清除。不需要节点设备 反馈离网确认信息,直接删除设 备信息。
|
*/
|
public void removeDevice(String gatewayOid, int compelClear, List<String> deviceAddrList, ZigbeeCallBack callback) {
|
if (deviceAddrList == null || deviceAddrList.size() == 0) {
|
LogUtils.e("没有要删除到数据");
|
return;
|
}
|
JsonArray addrList = new JsonArray();
|
for (String addr : deviceAddrList) {
|
JsonObject aObject = new JsonObject();
|
aObject.addProperty("DeviceAddr", addr);
|
addrList.add(aObject);
|
}
|
JsonObject jObject = new JsonObject();
|
jObject.addProperty(Cluster_ID, 0);
|
jObject.addProperty(Command, ZigBee.CommandType.RemoveDevice);
|
JsonObject data = new JsonObject();
|
data.addProperty("CompelClear", compelClear);
|
data.add("DeviceAddrList", addrList);
|
jObject.add("Data", data);
|
sendData(gatewayOid, ZigBee.ReplyTopicType.RemoveDevice, jObject.toString(), callback);
|
}
|
|
/**
|
* 设备列表-获取本地网关设备 EPDeviceId 列表
|
* (网关一个一个返回端点设备详细信息)
|
*
|
* @param gatewayOid
|
* @param callback
|
*/
|
public void getZigBeeDeviceList(String gatewayOid, ZBDeviceListCallBack callback) {
|
JsonObject jObject = new JsonObject();
|
jObject.addProperty(Cluster_ID, 0);
|
jObject.addProperty(Command, ZigBee.CommandType.GetDeviceInfo);
|
//是否获取列表成功标记
|
AtomicBoolean isSuccess = new AtomicBoolean(false);
|
//搜索5s等待标记
|
AtomicInteger timeOut = new AtomicInteger(0);
|
//设备列表
|
List<ZBDeviceBean> list = new ArrayList<>();
|
//注册监听事件
|
EventListener zigBeeEventListener = new EventListener() {
|
@Override
|
public synchronized void onMessage(Object msg) {
|
if (msg instanceof ZigbeeResponse) {
|
ZigbeeResponse zigbeeResponse = (ZigbeeResponse) msg;
|
try {
|
if (zigbeeResponse.getTopic().equals(ZigBee.ReplyTopicType.GetDeviceInfo)) {
|
final ZBDeviceBean bean = GsonConvert.getGson().fromJson(zigbeeResponse.getData(), new TypeToken<ZBDeviceBean>() {
|
}.getType());
|
|
|
if (bean == null) {
|
LogUtils.i("获取到的Zigbee设备列表反系列化失败:\r\n" + zigbeeResponse.getData());
|
} else {
|
bean.setGatewayMac(zigbeeResponse.getOid());
|
LogUtils.i("zigbeeResponse ZBDeviceBean:" + bean.getDeviceAddr() + ":" + bean.getEpoint());
|
if (bean.getData().getTotalNum() == 0) {
|
//回复空的设备列表
|
list.clear();
|
if (callback != null) {
|
callback.onSuccess(list);
|
}
|
HDLZigbeeConnect.getInstance().removeListener(this);
|
isSuccess.set(true);
|
} else {
|
list.add(bean);
|
//LogUtils.i("getZigBeeDeviceList 重置时间为0");
|
timeOut.set(0);//收到一个设备则重置时间
|
//如果当前序号等于总数,代表是最后一个设备了,回调成功
|
if (bean.getData().getDeviceNum() == bean.getData().getTotalNum()) {
|
if (callback != null) {
|
callback.onSuccess(list);
|
}
|
HDLZigbeeConnect.getInstance().removeListener(this);
|
isSuccess.set(true);
|
LogUtils.i("getZigBeeDeviceList读取完成");
|
timeOut.set(6);//计时结束标记
|
}
|
|
}
|
}
|
}
|
} catch (Exception e) {
|
LogUtils.e(TAG, "接收的数据内容:" + zigbeeResponse.getData() + " 异常信息:" + e.getMessage());
|
}
|
}
|
}
|
};
|
HDLZigbeeConnect.getInstance().registerListener(zigBeeEventListener);
|
//发送数据
|
sendData(gatewayOid, "", jObject.toString(), null);
|
//5秒后还没有接收到数据,就回调失败
|
ScheduledExecutorService scheduledExecutorService = ThreadToolUtils.getInstance().newScheduledThreadPool(1);
|
scheduledExecutorService.schedule(new Runnable() {
|
@Override
|
public void run() {
|
while (timeOut.get() < 5) {
|
try {
|
timeOut.set(timeOut.get() + 1);
|
LogUtils.i("getZigBeeDeviceList 计时:" + timeOut.get() + "s");
|
Thread.sleep(1000);
|
} catch (InterruptedException e) {
|
e.printStackTrace();
|
}
|
}
|
//移除监听事件
|
HDLZigbeeConnect.getInstance().removeListener(zigBeeEventListener);
|
if (!isSuccess.get()) {
|
if (null != callback) {
|
callback.onError(HDLLinkException.getErrorWithCode(HDLLinkCode.HDL_GET_Zigbee_DEVICE_LIST_FAILURE_ERROR));
|
}
|
}
|
LogUtils.i("getZigBeeDeviceList 5s倒计时结束");
|
scheduledExecutorService.shutdownNow();
|
}
|
}, 1, TimeUnit.SECONDS);
|
|
}
|
|
/**
|
* 修改设备端口(按键)名称
|
*
|
* @param gatewayOid
|
* @param deviceAddr
|
* @param epoint
|
* @param deviceName
|
* @param callback
|
*/
|
public void renameDeviceName(String gatewayOid, String deviceAddr, int epoint, String deviceName, ZigbeeCallBack callback) {
|
JsonObject jObject = new JsonObject();
|
jObject.addProperty("DeviceAddr", deviceAddr);
|
jObject.addProperty("Epoint", epoint);
|
jObject.addProperty(Cluster_ID, 0);
|
jObject.addProperty(Command, ZigBee.CommandType.DeviceRename);
|
JsonObject data = new JsonObject();
|
data.addProperty("DeviceName", deviceName);
|
jObject.add("Data", data);
|
sendData(gatewayOid, ZigBee.ReplyTopicType.DeviceRename, jObject.toString(), callback);
|
}
|
|
/**
|
* 对端点回路设备功能类型进行设置如 onoffoutput 回路,进行设置为普通开关或插座或灯光
|
*
|
* @param gatewayOid
|
* @param deviceAddr 设备节点的 mac 地址 (最大 16 个字符)
|
* @param epoint 设备端口号 和 mac 地址共同标识唯一的 zigbee 设备 数值范围 0-255
|
* @param functionType 用来表示实际回路用于什么 功能,可扩展 0-继电器接普通开关 1-继电器接灯光 2-插座
|
* @param callback
|
*/
|
public void setEPDeviceFunctionType(String gatewayOid, String deviceAddr, int epoint, int functionType, ZigbeeCallBack callback) {
|
JsonObject jObject = new JsonObject();
|
jObject.addProperty("DeviceAddr", deviceAddr);
|
jObject.addProperty("Epoint", epoint);
|
jObject.addProperty(Cluster_ID, 0);
|
jObject.addProperty(Command, ZigBee.CommandType.SetEPDeviceFunctionType);
|
JsonObject data = new JsonObject();
|
data.addProperty("FunctionType", functionType);
|
jObject.add("Data", data);
|
sendData(gatewayOid, ZigBee.ReplyTopicType.SetEPDeviceFunctionType, jObject.toString(), callback);
|
}
|
|
|
/********面板震动功能********/
|
/**
|
* 获取简约面板震动功能的信息
|
*/
|
public void getPanelVibrationInfo(String gatewayOid, String deviceAddr, int epoint, String passData, ZigbeeCallBack callback) {
|
// //组装发送数据
|
// JsonObject jObject = new JsonObject();
|
// jObject.addProperty("DeviceAddr", deviceAddr);
|
// jObject.addProperty("Epoint", 200);
|
// jObject.addProperty(Cluster_ID, 64513);
|
// jObject.addProperty(Command, 0);
|
// JsonObject data = new JsonObject();
|
// data.addProperty("PassData", "050108110101");
|
// jObject.add("Data", data);
|
// sendClientDataPassthrough(device, jObject.toString(), "0802", 18, 6, null, callback);
|
// sendClientDataPassthrough(gatewayOid,deviceAddr,epoint,passData,callback);
|
|
}
|
|
/**
|
* 设置简约面板震动功能的信息
|
*
|
* @param device
|
* @param deviceAddr
|
* @param panelVibrationBean
|
* @param callback
|
*/
|
public void setPanelVibrationInfo(String device, String deviceAddr, PanelVibrationBean panelVibrationBean, ZigbeeCallBack callback) {
|
// String passData = "0800081104";
|
// passData += panelVibrationBean.getIsEnable() ? "01" : "00";
|
//// passData += Convert.ToString(panelVibrationBean.getStrength(), 16).PadLeft(2, '0');
|
//// String time = Convert.ToString(panelVibrationBean.getTime(), 16).PadLeft(4, '0');
|
//// //低位在前,高位在后
|
//// passData += time.Substring(2, 2);
|
//// passData += time.Substring(0, 2);
|
//
|
//// var jObject = new Newtonsoft.Json.Linq.JObject { { "DeviceAddr", DeviceAddr }, { "Epoint", 200 }, { "Cluster_ID", 64513 }, { "Command", 0 } };
|
//// var data = new Newtonsoft.Json.Linq.JObject { { "PassData", passData } };
|
//// jObject.Add("Data", data);
|
//
|
// JsonObject jObject = new JsonObject();
|
// jObject.addProperty("DeviceAddr", deviceAddr);
|
// jObject.addProperty("Epoint", 200);
|
// jObject.addProperty(Cluster_ID, 64513);
|
// jObject.addProperty(Command, 0);
|
// JsonObject data = new JsonObject();
|
// data.addProperty("PassData", passData);
|
// jObject.add("Data", data);
|
//
|
// sendClientDataPassthrough(device, jObject.toString(), "0002", 16, 6, null, callback);
|
}
|
|
|
/**
|
* 开关控制(仅用于cluster=6的设备) 设备支持cluster=6的设备才能调用该接口
|
*
|
* @param command 0:关闭 1:打开 2:取反
|
*/
|
public void switchControl(String gatewayOid, int command, ZBDeviceBean deviceBean) {
|
JsonObject jObject = new JsonObject();
|
jObject.addProperty("DeviceAddr", deviceBean.getDeviceAddr());
|
jObject.addProperty("Epoint", deviceBean.getEpoint());
|
jObject.addProperty(Cluster_ID, 6);
|
jObject.addProperty(Command, command);
|
jObject.addProperty("SendMode", 2);
|
sendData(gatewayOid, "", jObject.toString(), null);
|
}
|
|
}
|