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); 
 | 
    } 
 | 
  
 | 
} 
 |