wjc
2023-06-28 14de918a79943e4961b09fa01ed320c6cad41f2e
HDLLinkLocalSdk/src/main/java/com/hdl/sdk/link/zigbee/HDLLinkLocalZigBee.java
New file
@@ -0,0 +1,392 @@
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.sdk.link.common.event.EventListener;
import com.hdl.sdk.link.common.exception.HDLLinkCode;
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", 5);//闪烁时间(秒)
        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);
    }
}