wjc
2025-01-07 90d5f028ccdaaaf64286f9d632cb335a4d0544b9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
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.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);
    }
 
}