pymammotion 0.4.0b6__py3-none-any.whl → 0.4.0b8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3020 @@
1
+ #
2
+ # Copyright (c) 2014-2018 Alibaba Group. All rights reserved.
3
+ # License-Identifier: Apache-2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+ #
18
+
19
+ from enum import Enum
20
+ import hashlib
21
+ import hmac
22
+ import json
23
+ import logging
24
+ import os
25
+ import queue
26
+ import random
27
+ import re
28
+ import ssl
29
+ import string
30
+ import sys
31
+ import threading
32
+ import time
33
+ import urllib.parse
34
+ import urllib.request
35
+
36
+ import paho.mqtt.client as mqtt
37
+ from paho.mqtt.enums import CallbackAPIVersion
38
+
39
+ REQUIRED_MAJOR_VERSION = 3
40
+ REQUIRED_MINOR_VERSION = 5
41
+
42
+
43
+ # check Python version
44
+ def lk_check_python_version(major_version, minor_version) -> None:
45
+ version = sys.version_info
46
+ if version[0] < major_version or (version[0] == major_version and version[1] < minor_version):
47
+ print(
48
+ "WARNING: linkit requires Python %d.%d or higher, and the current version is %s"
49
+ % (major_version, minor_version, sys.version)
50
+ )
51
+
52
+
53
+ lk_check_python_version(REQUIRED_MAJOR_VERSION, REQUIRED_MINOR_VERSION)
54
+
55
+
56
+ class LinkKit:
57
+ TAG_KEY = "attrKey"
58
+ TAG_VALUE = "attrValue"
59
+
60
+ class LinkKitState(Enum):
61
+ INITIALIZED = 1
62
+ CONNECTING = 2
63
+ CONNECTED = 3
64
+ DISCONNECTING = 4
65
+ DISCONNECTED = 5
66
+ DESTRUCTING = 6
67
+ DESTRUCTED = 7
68
+
69
+ class ErrorCode(Enum):
70
+ SUCCESS = 0
71
+ # 错误码一共三位数,第一位为0表示为mqtt相关的错误码
72
+ NETWORK_DISCONNECTED = -0x001 # 当前mqtt连接断开
73
+ INVALID_TOPIC = -0x002 # 输入的topic为空
74
+ INVALID_QOS = -0x003 # 输入的qos非法
75
+ PAHO_MQTT_ERROR = -0x004 # 底层的mqtt报错
76
+ # 0x100开头的表示为网关相关的错误码
77
+ NULL_SUBDEV_ERR = -0x101 # 输入的子设备信息为空错误
78
+ SUBDEV_NOT_ARRAY_ERR = -0x102 # 输入数据并非数组错误
79
+ ARRAY_LENGTH_ERR = -0x103 # 数组长度错误
80
+ # 0x300开头的表示为动态注册相关的错误码
81
+ DYNREG_AUTH_FAILED = -0x301 # 预注册动态注册认证失败
82
+ DYNREG_AUTH_NWL_FAILED = -0x302 # 免白动态注册认证失败
83
+ # 0x400开头表示为OTA有关的错误
84
+ OTA_INVALID_PARAM = -0x401 # 参数非法
85
+ OTA_DIGEST_MISMATCH = -0x402 # 校验和不通过
86
+ OTA_PUB_FAILED = -0x403 # 上报失败
87
+ OTA_INVALID_SIGN_METHOD = -0x404 # 非法校验方法
88
+ OTA_DOWNLOAD_FAIL = -0x405 # 下载失败
89
+ OTA_INVALID_URL = -0x406 # 无法访问HTTP链接
90
+ OTA_INVALID_PATH = -0x407 # 存储路径打开失败
91
+
92
+ class StateError(Exception):
93
+ def __init__(self, err) -> None:
94
+ Exception.__init__(self, err)
95
+
96
+ class Shadow:
97
+ def __init__(self) -> None:
98
+ self.__version = None
99
+ self.__timestamp = None
100
+ self.__state = None
101
+ self.__metadata = None
102
+ self.__latest_shadow_lock = threading.Lock()
103
+ self.__latest_received_time = None
104
+ self.__lastest_received_payload = None
105
+
106
+ def get_version(self):
107
+ with self.__latest_shadow_lock:
108
+ return self.__version
109
+
110
+ def get_metadata(self):
111
+ with self.__latest_shadow_lock:
112
+ return self.__metadata
113
+
114
+ def get_state(self):
115
+ with self.__latest_shadow_lock:
116
+ return self.__state
117
+
118
+ def set_state(self, state) -> None:
119
+ with self.__latest_shadow_lock:
120
+ self.__state = state
121
+
122
+ def set_metadata(self, metadata) -> None:
123
+ with self.__latest_shadow_lock:
124
+ self.__metadata = metadata
125
+
126
+ def set_version(self, version) -> None:
127
+ with self.__latest_shadow_lock:
128
+ self.__version = version
129
+
130
+ def set_timestamp(self, timestamp) -> None:
131
+ with self.__latest_shadow_lock:
132
+ self.__timestamp = timestamp
133
+
134
+ def set_latest_recevied_time(self, timestamp) -> None:
135
+ with self.__latest_shadow_lock:
136
+ self.__latest_received_time = timestamp
137
+
138
+ def get_latest_recevied_time(self):
139
+ with self.__latest_shadow_lock:
140
+ return self.__latest_received_time
141
+
142
+ def set_latest_recevied_payload(self, payload) -> None:
143
+ with self.__latest_shadow_lock:
144
+ self.__latest_received_payload = payload
145
+
146
+ def get_latest_recevied_payload(self):
147
+ with self.__latest_shadow_lock:
148
+ return self.__latest_received_payload
149
+
150
+ def to_dict(self):
151
+ return {
152
+ "state": self.__state,
153
+ "metadata": self.__metadata,
154
+ "version": self.__version,
155
+ "timestamp": self.__timestamp,
156
+ }
157
+
158
+ def to_json_string(self):
159
+ return json.dumps(self.to_dict())
160
+
161
+ class __HandlerTask:
162
+ def __init__(self, logger=None) -> None:
163
+ self.__logger = logger
164
+ if self.__logger is not None:
165
+ self.__logger.info("HandlerTask init enter")
166
+ self.__message_queue = queue.Queue(2000)
167
+ self.__cmd_callback = {}
168
+ self.__started = False
169
+ self.__exited = False
170
+ self.__thread = None
171
+
172
+ def register_cmd_callback(self, cmd, callback) -> int:
173
+ if self.__started is False:
174
+ if cmd != "req_exit":
175
+ self.__cmd_callback[cmd] = callback
176
+ return 0
177
+ else:
178
+ return 1
179
+ else:
180
+ return 2
181
+
182
+ def post_message(self, cmd, value) -> bool:
183
+ self.__logger.debug("post_message :%r " % cmd)
184
+ if self.__started and self.__exited is False:
185
+ try:
186
+ self.__message_queue.put((cmd, value), timeout=5)
187
+ except queue.Full as e:
188
+ self.__logger.error("queue full: %r" % e)
189
+ return False
190
+ self.__logger.debug("post_message success")
191
+ return True
192
+ self.__logger.debug("post_message fail started:%r,exited:%r" % (self.__started, self.__exited))
193
+ return False
194
+
195
+ def start(self) -> int:
196
+ if self.__logger is not None:
197
+ self.__logger.info("HandlerTask start")
198
+ if self.__started is False:
199
+ if self.__logger is not None:
200
+ self.__logger.info("HandlerTask try start")
201
+ self.__exited = False
202
+ self.__started = True
203
+ self.__message_queue = queue.Queue(2000)
204
+ self.__thread = threading.Thread(target=self.__thread_runnable)
205
+ self.__thread.daemon = True
206
+ self.__thread.start()
207
+ return 0
208
+ return 1
209
+
210
+ def stop(self) -> None:
211
+ if self.__started and self.__exited is False:
212
+ self.__exited = True
213
+ self.__message_queue.put(("req_exit", None))
214
+
215
+ def wait_stop(self) -> None:
216
+ if self.__started is True:
217
+ self.__thread.join()
218
+
219
+ def __thread_runnable(self) -> None:
220
+ if self.__logger is not None:
221
+ self.__logger.debug("thread runnable enter")
222
+ while True:
223
+ cmd, value = self.__message_queue.get()
224
+ self.__logger.debug("thread runnable pop cmd:%r" % cmd)
225
+ if cmd == "req_exit":
226
+ break
227
+ if self.__cmd_callback[cmd] is not None:
228
+ try:
229
+ self.__cmd_callback[cmd](value)
230
+ except Exception as e:
231
+ if self.__logger is not None:
232
+ self.__logger.error("thread runnable raise exception:%s" % e)
233
+ self.__started = False
234
+ if self.__logger is not None:
235
+ self.__logger.debug("thread runnable exit")
236
+
237
+ class LoopThread:
238
+ def __init__(self, logger=None) -> None:
239
+ self.__logger = logger
240
+ if logger is not None:
241
+ self.__logger.info("LoopThread init enter")
242
+ self.__callback = None
243
+ self.__thread = None
244
+ self.__started = False
245
+ self.__req_wait = threading.Event()
246
+ if logger is not None:
247
+ self.__logger.info("LoopThread init exit")
248
+
249
+ def start(self, callback) -> int:
250
+ if self.__started is True:
251
+ self.__logger.info("LoopThread already ")
252
+ return 1
253
+ else:
254
+ self.__callback = callback
255
+ self.__thread = threading.Thread(target=self.__thread_main)
256
+ self.__thread.daemon = True
257
+ self.__thread.start()
258
+ return 0
259
+
260
+ def stop(self) -> None:
261
+ self.__req_wait.wait()
262
+ self.__req_wait.clear()
263
+
264
+ def __thread_main(self) -> None:
265
+ self.__started = True
266
+ try:
267
+ if self.__logger is not None:
268
+ self.__logger.debug("LoopThread thread enter")
269
+ if self.__callback is not None:
270
+ self.__callback()
271
+ if self.__logger is not None:
272
+ self.__logger.debug("LoopThread thread exit")
273
+ except Exception as e:
274
+ self.__logger.error("LoopThread thread Exception:" + str(e))
275
+ self.__started = False
276
+ self.__req_wait.set()
277
+
278
+ def _on_file_upload_start(self, id, upload_file_info, user_data) -> None:
279
+ if self.__on_file_upload_begin != None:
280
+ self.__on_file_upload_begin(id, upload_file_info, self.__user_data)
281
+
282
+ def _on_file_upload_end(self, id, upload_file_info, upload_file_result, user_data) -> None:
283
+ if self.__on_file_upload_end != None:
284
+ self.__on_file_upload_end(id, upload_file_info, upload_file_result, self.__user_data)
285
+
286
+ def _on_file_upload_progress(self, id, upload_file_result, upload_file_info, user_data) -> None:
287
+ pass
288
+
289
+ class __LinkKitLog:
290
+ def __init__(self) -> None:
291
+ self.__logger = logging.getLogger("linkkit")
292
+ self.__enabled = False
293
+
294
+ def enable_logger(self) -> None:
295
+ self.__enabled = True
296
+
297
+ def disable_logger(self) -> None:
298
+ self.__enabled = False
299
+
300
+ def is_enabled(self):
301
+ return self.__enabled
302
+
303
+ def config_logger(self, level) -> None:
304
+ self.__logger.setLevel(level)
305
+
306
+ def debug(self, fmt, *args) -> None:
307
+ if self.__enabled:
308
+ self.__logger.debug(fmt, *args)
309
+
310
+ def warring(self, fmt, *args) -> None:
311
+ if self.__enabled:
312
+ self.__logger.warning(fmt, *args)
313
+
314
+ def info(self, fmt, *args) -> None:
315
+ if self.__enabled:
316
+ self.__logger.info(fmt, *args)
317
+
318
+ def error(self, fmt, *args) -> None:
319
+ if self.__enabled:
320
+ self.__logger.error(fmt, *args)
321
+
322
+ def critical(self, fmt, *args) -> None:
323
+ if self.__enabled:
324
+ self.__logger.critical(fmt, *args)
325
+
326
+ __USER_TOPIC_PREFIX = "/%s/%s/%s"
327
+ __ALIYUN_BROKER_CA_DATA = """
328
+ -----BEGIN CERTIFICATE-----
329
+ MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
330
+ A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
331
+ b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw
332
+ MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
333
+ YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT
334
+ aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ
335
+ jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp
336
+ xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp
337
+ 1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG
338
+ snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ
339
+ U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8
340
+ 9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
341
+ BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B
342
+ AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz
343
+ yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE
344
+ 38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP
345
+ AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad
346
+ DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
347
+ HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
348
+ -----END CERTIFICATE-----
349
+ -----BEGIN CERTIFICATE-----
350
+ MIID3zCCAsegAwIBAgISfiX6mTa5RMUTGSC3rQhnestIMA0GCSqGSIb3DQEBCwUA
351
+ MHcxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhaaGVqaWFuZzERMA8GA1UEBwwISGFu
352
+ Z3pob3UxEzARBgNVBAoMCkFsaXl1biBJb1QxEDAOBgNVBAsMB1Jvb3QgQ0ExGzAZ
353
+ BgNVBAMMEkFsaXl1biBJb1QgUm9vdCBDQTAgFw0yMzA3MDQwNjM2NThaGA8yMDUz
354
+ MDcwNDA2MzY1OFowdzELMAkGA1UEBhMCQ04xETAPBgNVBAgMCFpoZWppYW5nMREw
355
+ DwYDVQQHDAhIYW5nemhvdTETMBEGA1UECgwKQWxpeXVuIElvVDEQMA4GA1UECwwH
356
+ Um9vdCBDQTEbMBkGA1UEAwwSQWxpeXVuIElvVCBSb290IENBMIIBIjANBgkqhkiG
357
+ 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoK//6vc2oXhnvJD7BVhj6grj7PMlN2N4iNH4
358
+ GBmLmMdkF1z9eQLjksYc4Zid/FX67ypWFtdycOei5ec0X00m53Gvy4zLGBo2uKgi
359
+ T9IxMudmt95bORZbaph4VK82gPNU4ewbiI1q2loRZEHRdyPORTPpvNLHu8DrYBnY
360
+ Vg5feEYLLyhxg5M1UTrT/30RggHpaa0BYIPxwsKyylQ1OskOsyZQeOyPe8t8r2D4
361
+ RBpUGc5ix4j537HYTKSyK3Hv57R7w1NzKtXoOioDOm+YySsz9sTLFajZkUcQci4X
362
+ aedyEeguDLAIUKiYicJhRCZWljVlZActorTgjCY4zRajodThrQIDAQABo2MwYTAO
363
+ BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUkWHoKi2h
364
+ DlS1/rYpcT/Ue+aKhP8wHwYDVR0jBBgwFoAUkWHoKi2hDlS1/rYpcT/Ue+aKhP8w
365
+ DQYJKoZIhvcNAQELBQADggEBADrrLcBY7gDXN8/0KHvPbGwMrEAJcnF9z4MBxRvt
366
+ rEoRxhlvRZzPi7w/868xbipwwnksZsn0QNIiAZ6XzbwvIFG01ONJET+OzDy6ZqUb
367
+ YmJI09EOe9/Hst8Fac2D14Oyw0+6KTqZW7WWrP2TAgv8/Uox2S05pCWNfJpRZxOv
368
+ Lr4DZmnXBJCMNMY/X7xpcjylq+uCj118PBobfH9Oo+iAJ4YyjOLmX3bflKIn1Oat
369
+ vdJBtXCj3phpfuf56VwKxoxEVR818GqPAHnz9oVvye4sQqBp/2ynrKFxZKUaJtk0
370
+ 7UeVbtecwnQTrlcpWM7ACQC0OO0M9+uNjpKIbksv1s11xu0=
371
+ -----END CERTIFICATE-----
372
+ -----BEGIN CERTIFICATE-----
373
+ MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDE
374
+ gMB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2
375
+ JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNM
376
+ zQxMjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBS
377
+ NjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCAiI
378
+ wDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQ
379
+ ssgrRIxutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuT
380
+ ToVBu1kZguSgMpE3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSK
381
+ vGRMIRxDaNc9PIrFsmbVkJq3MQbFvuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n
382
+ 16qIfKtJwLnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqMPKq0pPbzlUoSB239jLKJz9
383
+ CgYXfIWHSw1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+azayOeSsJ
384
+ Da38O+2HBNXk7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiW
385
+ m05OWgtH8wY2SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4
386
+ UoQSwC+n+7o/hbguyCLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQ
387
+ Ce24DWJfncBZ4nWUx2OVvq+aWh2IMP0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFl
388
+ WQaYw55PfWzjMpYrZxCRXluDocZXFSxZba/jJvcE+kNb7gu3GduyYsRtYQUigAZ
389
+ cIN5kZeR1BonvzceMgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjA
390
+ PBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToD
391
+ AfBgNVHSMEGDAWgBSubAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFA
392
+ AOCAgEAgyXt6NH9lVLNnsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcW
393
+ c+ZfwFSY1XS+wc3iEZGtIxg93eFyRJa0lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKP
394
+ rmzU+sQghoefEQzd5Mr6155wsTLxDKZmOMNOsIeDjHfrYBzN2VAAiKrlNIC5waN
395
+ rlU/yDXNOd8v9EDERm8tLjvUYAGm0CuiVdjaExUd1URhxN25mW7xocBFymFe944
396
+ Hn+Xds+qkxV/ZoVqW/hpvvfcDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl
397
+ +68KnyBr3TsTjxKM4kEaSHpzoHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU
398
+ 3/gKbaKxCXcPu9czc8FB10jZpnOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTO
399
+ wY3WzvUy2MmeFe8nI+z1TIvWfspA9MRf/TuTAjB0yPEL+GltmZWrSZVxykzLsVi
400
+ VO6LAUP5MSeGbEYNNVMnbrt9x+vJJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9
401
+ x8jvOOJckvB595yEunQtYQEgfn7R8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+tJDf
402
+ LRVpOoERIyNiwmcUVhAn21klJwGW45hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA=
403
+ -----END CERTIFICATE-----
404
+ -----BEGIN CERTIFICATE-----
405
+ MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDME
406
+ YxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYD
407
+ VQQDExNHbG9iYWxTaWduIFJvb3QgRTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MD
408
+ MyMDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24g
409
+ bnYtc2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBFNDYwdjAQBgcqhkjOPQ
410
+ IBBgUrgQQAIgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qUuwzSChDH2omw
411
+ lwxwEwkBjtjqR+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIvU4
412
+ Tb+0cUB+hflGddyXqBPCCjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8E
413
+ BTADAQH/MB0GA1UdDgQWBBQxCpCPtsad0kRLgLWi5h+xEk8blTAKBggqhkjOPQ
414
+ QDAwNoADBlAjEA31SQ7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZkvLtoURMMA/cV
415
+ i4RguYv/Uo7njLwcAjA8+RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/alt
416
+ P+CAezNIm8BZ/3Hobui3A=
417
+ -----END CERTIFICATE-----
418
+ -----BEGIN CERTIFICATE-----
419
+ MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAU
420
+ AMEYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGg
421
+ YDVQQDExNHbG9iYWxTaWduIFJvb3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2M
422
+ DMyMDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24g
423
+ bnYtc2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIiMA0GCSqGSIb
424
+ 3DQEBAQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08
425
+ EsCVeJOaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUl
426
+ ghYruQGvGIFAha/r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTq
427
+ a1VbkNud316HCkD7rRlr+/fKYIje2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/O
428
+ rffGFqfUo0q3v84RLHIf8E6M6cqJaESvWJ3En7YEtbWaBkoe0G1h6zD8K+kZPT
429
+ Xhc+CtI4wSEy132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvjK8Cd+RTy
430
+ G/FWaha/LIWFzXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0N
431
+ XfeD412lPFzYE+cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JM
432
+ WKmIJ5jqSngiCNI/onccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+F
433
+ fy7dXxd7Pj2Fxzsx2sZy/N78CsHpdlseVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7
434
+ /mrLZqrcZdCinkqaByFrgY/bxFn63iLABJzjqls2k+g9vXqhnQt2sQvHnf3PmKg
435
+ Gwvgqo6GDoLclcqUC4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQ
436
+ H/BAUwAwEB/zAdBgNVHQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIh
437
+ vcNAQEMBQADggIBAHx47PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048
438
+ p9gkUbJUHJNOxO97k4VgJuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63b
439
+ EIaZHU1VNaL8FpO7XJqti2kM3S+LGteWygxk6x9PbTZ4IevPuzz5i+6zoYMzRx6
440
+ Fcg0XERczzF2sUyQQCPtIkpnnpHs6i58FZFZ8d4kuaPp92CC1r2LpXFNqD6v6MV
441
+ enQTqnMdzGxRBF6XLE+0xRFFRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZmOUdk
442
+ LG5NrmJ7v2B0GbhWrJKsFjLtrWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSy
443
+ BQ7N0H3qqJZ4d16GLuc1CLgSkZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7f
444
+ XwgNNgyYMqIgXQBztSvwyeqiv5u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJ
445
+ Mbfc2hIkCwU9D9SGuTSyxTDYWnP4vkYxboznxSjBF25cfe1lNj2M8FawTSLfJvd
446
+ kzrnE6JwYZ+vj+vYxXX4M2bUdGc6N3ec592kD3ZDZopD8p/7DEJ4Y9HiD2971KE
447
+ 9dJeFt0g5QdYg/NA6s/rob8SKunE3vouXsXgxT7PntgMTzlSdriVZzH81Xwj3QE
448
+ UxeCp6
449
+ -----END CERTIFICATE-----
450
+ """
451
+
452
+ def __init__(
453
+ self,
454
+ host_name,
455
+ product_key,
456
+ device_name,
457
+ device_secret,
458
+ auth_type=None,
459
+ username=None,
460
+ client_id=None,
461
+ password=None,
462
+ instance_id=None,
463
+ product_secret=None,
464
+ user_data=None,
465
+ ) -> None:
466
+ # logging configs
467
+ self.__just_for_pycharm_autocomplete = False
468
+
469
+ def __str_is_empty(value) -> bool:
470
+ if value is None or value == "":
471
+ return True
472
+ else:
473
+ return False
474
+
475
+ # param check
476
+ if __str_is_empty(host_name):
477
+ raise ValueError("host_name wrong")
478
+ if __str_is_empty(product_key):
479
+ raise ValueError("product key wrong")
480
+ if __str_is_empty(device_name):
481
+ raise ValueError("device name wrong")
482
+ if __str_is_empty(device_secret) and __str_is_empty(product_secret):
483
+ lack_other_auth_info = __str_is_empty(username) or __str_is_empty(client_id) or __str_is_empty(password)
484
+ if lack_other_auth_info:
485
+ raise ValueError("device secret & product secret are both empty")
486
+
487
+ self.__link_log = LinkKit.__LinkKitLog()
488
+ self.__PahoLog = logging.getLogger("Paho")
489
+ self.__PahoLog.setLevel(logging.DEBUG)
490
+
491
+ # config internal property
492
+ self.__host_name = host_name
493
+ self.__product_key = product_key
494
+ self.__device_name = device_name
495
+ self.__device_secret = device_secret
496
+ self.__product_secret = product_secret
497
+ self.__user_data = user_data
498
+ self.__device_interface_info = ""
499
+ self.__device_mac = None
500
+ self.__cellular_IMEI = None
501
+ self.__cellular_ICCID = None
502
+ self.__cellular_IMSI = None
503
+ self.__cellular_MSISDN = None
504
+ self.__mqtt_client = None
505
+ self.__sdk_version = "1.2.13"
506
+ self.__sdk_program_language = "Python"
507
+ self.__endpoint = None
508
+ self.__check_hostname = True
509
+ self.__instance_id = instance_id
510
+
511
+ self.__mqtt_port = 8883
512
+ self.__mqtt_protocol = "MQTTv311"
513
+ self.__mqtt_transport = "TCP"
514
+ self.__mqtt_secure = "TLS"
515
+ self.__mqtt_keep_alive = 60
516
+ self.__mqtt_clean_session = True
517
+ self.__mqtt_max_inflight_message = 20
518
+ self.__mqtt_max_queued_message = 40
519
+ self.__mqtt_auto_reconnect_min_sec = 1
520
+ self.__mqtt_auto_reconnect_max_sec = 60
521
+ self.__mqtt_auto_reconnect_sec = 0
522
+ self.__mqtt_request_timeout = 10
523
+ # 动态注册(免预注册)的flag
524
+ self.__dynamic_register_nwl_flag = 0
525
+ # 动态注册(预注册)的flag
526
+ self.__dynamic_register_flag = 0
527
+ self.__linkkit_state = LinkKit.LinkKitState.INITIALIZED
528
+ self.__aliyun_broker_ca_data = self.__ALIYUN_BROKER_CA_DATA
529
+ self.__force_reconnect = False
530
+
531
+ self.__latest_shadow = LinkKit.Shadow()
532
+ self.__auth_type = auth_type
533
+ self.__username = username
534
+ self.__client_id = client_id
535
+ self.__password = password
536
+
537
+ # aliyun IoT callbacks
538
+ self.__on_device_dynamic_register = None
539
+
540
+ # mqtt callbacks
541
+ self.__on_connect = None
542
+ self.__on_disconnect = None
543
+ self.__on_publish_topic = None
544
+ self.__on_subscribe_topic = None
545
+ self.__on_unsubscribe_topic = None
546
+ self.__on_topic_message = None
547
+
548
+ self.__on_topic_rrpc_message = None
549
+ self.__on_subscribe_rrpc_topic = None
550
+ self.__on_unsubscribe_rrpc_topic = None
551
+
552
+ # thing model callbacks
553
+ self.__on_thing_create = None
554
+ self.__on_thing_enable = None
555
+ self.__on_thing_disable = None
556
+ self.__on_thing_raw_data_arrived = None
557
+ self.__on_thing_raw_data_post = None
558
+ self.__on_thing_call_service = None
559
+ self.__on_thing_prop_changed = None
560
+ self.__on_thing_event_post = None
561
+ self.__on_thing_prop_post = None
562
+ self.__on_thing_shadow_get = None
563
+ self.__on_thing_device_info_update = None
564
+ self.__on_thing_device_info_delete = None
565
+
566
+ self.__on_file_upload_begin = None
567
+ self.__on_file_upload_end = None
568
+
569
+ self.__user_topics = {}
570
+ self.__user_topics_subscribe_request = {}
571
+ self.__user_topics_unsubscribe_request = {}
572
+ self.__user_topics_request_lock = threading.Lock()
573
+ self.__user_topics_unsubscribe_request_lock = threading.Lock()
574
+
575
+ self.__user_rrpc_topics = {}
576
+ self.__user_rrpc_topics_lock = threading.RLock()
577
+ self.__user_rrpc_topics_subscribe_request = {}
578
+ self.__user_rrpc_topics_unsubscribe_request = {}
579
+ self.__user_rrpc_topics_subscribe_request_lock = threading.RLock()
580
+ self.__user_rrpc_topics_unsubscribe_request_lock = threading.RLock()
581
+ self.__user_rrpc_request_ids = []
582
+ self.__user_rrpc_request_id_index_map = {}
583
+ self.__user_rrpc_request_ids_lock = threading.RLock()
584
+ self.__user_rrpc_request_max_len = 100
585
+
586
+ # things topic - Alink
587
+ self.__thing_topic_prop_post = "/sys/%s/%s/thing/event/property/post" % (self.__product_key, self.__device_name)
588
+ self.__thing_topic_prop_post_reply = self.__thing_topic_prop_post + "_reply"
589
+ self.__thing_topic_prop_set = "/sys/%s/%s/thing/service/property/set" % (self.__product_key, self.__device_name)
590
+ self.__thing_topic_prop_set_reply = self.__thing_topic_prop_set + "_reply"
591
+ self.__thing_topic_prop_get = "/sys/%s/%s/thing/service/property/get" % (self.__product_key, self.__device_name)
592
+ self.__thing_topic_event_post_pattern = "/sys/%s/%s/thing/event/%s/post"
593
+ self.__gateway_topic_add_subdev_topo = "/sys/%s/%s/thing/topo/add" % (self.__product_key, self.__device_name)
594
+ self.__gateway_topic_add_subdev_topo_reply = self.__gateway_topic_add_subdev_topo + "_reply"
595
+
596
+ self.__gateway_topic_delete_subdev_topo = "/sys/%s/%s/thing/topo/delete" % (
597
+ self.__product_key,
598
+ self.__device_name,
599
+ )
600
+ self.__gateway_topic_delete_subdev_topo_reply = self.__gateway_topic_delete_subdev_topo + "_reply"
601
+
602
+ self.__gateway_topic_login_subdev = "/ext/session/%s/%s/combine/batch_login" % (
603
+ self.__product_key,
604
+ self.__device_name,
605
+ )
606
+ self.__gateway_topic_login_subdev_reply = self.__gateway_topic_login_subdev + "_reply"
607
+
608
+ self.__gateway_topic_logout_subdev = "/ext/session/%s/%s/combine/batch_logout" % (
609
+ self.__product_key,
610
+ self.__device_name,
611
+ )
612
+ self.__gateway_topic_logout_subdev_reply = self.__gateway_topic_logout_subdev + "_reply"
613
+
614
+ self.__gateway_topic_register_subdev = "/sys/%s/%s/thing/sub/register" % (
615
+ self.__product_key,
616
+ self.__device_name,
617
+ )
618
+ self.__gateway_topic_register_subdev_reply = self.__gateway_topic_register_subdev + "_reply"
619
+
620
+ self.__gateway_topic_product_register_subdev = "/sys/%s/%s/thing/proxy/provisioning/product_register" % (
621
+ self.__product_key,
622
+ self.__device_name,
623
+ )
624
+ self.__gateway_topic_product_register_subdev_reply = self.__gateway_topic_product_register_subdev + "_reply"
625
+
626
+ self.__gateway_topic_topo_change = "/sys/%s/%s/thing/topo/change" % (self.__product_key, self.__device_name)
627
+ self.__dynamic_register_nwl_topic = "/ext/regnwl"
628
+ self.__dynamic_register_topic = "/ext/register"
629
+
630
+ self.__ota_report_version_topic = "/ota/device/inform/%s/%s" % (self.__product_key, self.__device_name)
631
+ self.__ota_push_topic = "/ota/device/upgrade/%s/%s" % (self.__product_key, self.__device_name)
632
+ self.__ota_pull_topic = "/sys/%s/%s/thing/ota/firmware/get" % (self.__product_key, self.__device_name)
633
+ self.__ota_pull_reply_topic = "/sys/%s/%s/thing/ota/firmware/get_reply" % (
634
+ self.__product_key,
635
+ self.__device_name,
636
+ )
637
+
638
+ self.__thing_prop_post_mid = {}
639
+ self.__thing_prop_post_mid_lock = threading.Lock()
640
+ self.__thing_prop_set_reply_mid = {}
641
+ self.__thing_prop_set_reply_mid_lock = threading.Lock()
642
+ self.__gateway_add_subdev_topo_mid = {}
643
+ self.__gateway_add_subdev_topo_mid_lock = threading.Lock()
644
+ self.__gateway_delete_subdev_topo_mid = {}
645
+ self.__gateway_delete_subdev_topo_mid_lock = threading.Lock()
646
+ # event:post topic
647
+ self.__thing_topic_event_post = {}
648
+ self.__thing_topic_event_post_reply = set()
649
+ self.__thing_events = set()
650
+ self.__thing_request_id_max = 1000000
651
+ self.__thing_request_value = 0
652
+ self.__thing_request_id = {}
653
+ self.__thing_request_id_lock = threading.Lock()
654
+ self.__thing_event_post_mid = {}
655
+ self.__thing_event_post_mid_lock = threading.Lock()
656
+
657
+ self.__thing_topic_shadow_get = "/shadow/get/%s/%s" % (self.__product_key, self.__device_name)
658
+ self.__thing_topic_shadow_update = "/shadow/update/%s/%s" % (self.__product_key, self.__device_name)
659
+ self.__thing_shadow_mid = {}
660
+ self.__thing_shadow_mid_lock = threading.Lock()
661
+
662
+ # service topic
663
+ self.__thing_topic_service_pattern = "/sys/%s/%s/thing/service/%s"
664
+ self.__thing_topic_services = set()
665
+ self.__thing_topic_services_reply = set()
666
+ self.__thing_services = set()
667
+ self.__thing_answer_service_mid = {}
668
+ self.__thing_answer_service_mid_lock = threading.Lock()
669
+
670
+ # thing topic - raw
671
+ self.__thing_topic_raw_up = "/sys/%s/%s/thing/model/up_raw" % (self.__product_key, self.__device_name)
672
+ self.__thing_topic_raw_up_reply = self.__thing_topic_raw_up + "_reply"
673
+ self.__thing_topic_raw_down = "/sys/%s/%s/thing/model/down_raw" % (self.__product_key, self.__device_name)
674
+ self.__thing_topic_raw_down_reply = self.__thing_topic_raw_down + "_reply"
675
+ self.__thing_raw_up_mid = {}
676
+ self.__thing_raw_up_mid_lock = threading.Lock()
677
+ self.__thing_raw_down_reply_mid = {}
678
+ self.__thing_raw_down_reply_mid_lock = threading.Lock()
679
+
680
+ # thing topic - device_info
681
+ self.__thing_topic_update_device_info_up = "/sys/%s/%s/thing/deviceinfo/update" % (
682
+ self.__product_key,
683
+ self.__device_name,
684
+ )
685
+ self.__thing_topic_update_device_info_reply = self.__thing_topic_update_device_info_up + "_reply"
686
+ self.__thing_topic_delete_device_info_up = "/sys/%s/%s/thing/deviceinfo/delete" % (
687
+ self.__product_key,
688
+ self.__device_name,
689
+ )
690
+ self.__thing_topic_delete_device_info_reply = self.__thing_topic_delete_device_info_up + "_reply"
691
+ self.__thing_update_device_info_up_mid = {}
692
+ self.__thing_update_device_info_up_mid_lock = threading.Lock()
693
+ self.__thing_delete_device_info_up_mid = {}
694
+ self.__thing_delete_device_info_up_mid_lock = threading.Lock()
695
+
696
+ # properties
697
+ self.__thing_properties_set = set()
698
+ self.__thing_properties_get = set()
699
+ self.__thing_properties_post = set()
700
+
701
+ # thing setup state
702
+ self.__thing_setup_state = False
703
+ self.__thing_raw_only = False
704
+ self.__thing_enable_state = False
705
+ self.__on_gateway_add_subdev_topo_reply = None
706
+ self.__on_gateway_delete_subdev_topo_reply = None
707
+ self.__on_gateway_login_subdev_reply = None
708
+ self.__on_gateway_logout_subdev_reply = None
709
+ self.__on_gateway_register_subdev_reply = None
710
+ self.__on_gateway_product_register_subdev_reply = None
711
+ self.__on_gateway_topo_change = None
712
+ self.__on_device_dynamic_register_nwl_reply = None
713
+ self.__on_ota_message_arrived = None
714
+
715
+ if self.__just_for_pycharm_autocomplete:
716
+ self.__mqtt_client = mqtt.Client(CallbackAPIVersion.VERSION1)
717
+
718
+ # device interface info
719
+ self.__device_info_topic = "/sys/%s/%s/thing/deviceinfo/update" % (self.__product_key, self.__device_name)
720
+ self.__device_info_topic_reply = self.__device_info_topic + "_reply"
721
+ self.__device_info_mid_lock = threading.Lock()
722
+ self.__device_info_mid = {}
723
+
724
+ # connect_async
725
+ self.__connect_async_req = False
726
+ self.__worker_loop_exit_req = False
727
+ self.__worker_loop_runing_state = False
728
+ self.__worker_loop_exit_req_lock = threading.Lock()
729
+
730
+ # loop thread
731
+ self.__loop_thread = LinkKit.LoopThread(self.__link_log)
732
+
733
+ # HandlerTask
734
+ self.__handler_task = LinkKit.__HandlerTask(self.__link_log)
735
+ self.__handler_task_cmd_on_connect = "on_connect"
736
+ self.__handler_task_cmd_on_disconnect = "on_disconnect"
737
+ self.__handler_task_cmd_on_message = "on_message"
738
+ self.__handler_task_cmd_on_publish = "on_publish"
739
+ self.__handler_task_cmd_on_subscribe = "on_subscribe"
740
+ self.__handler_task_cmd_on_unsubscribe = "on_unsubscribe"
741
+ self.__handler_task.register_cmd_callback(
742
+ self.__handler_task_cmd_on_connect, self.__handler_task_on_connect_callback
743
+ )
744
+ self.__handler_task.register_cmd_callback(
745
+ self.__handler_task_cmd_on_disconnect, self.__handler_task_on_disconnect_callback
746
+ )
747
+ self.__handler_task.register_cmd_callback(
748
+ self.__handler_task_cmd_on_message, self.__handler_task_on_message_callback
749
+ )
750
+ self.__handler_task.register_cmd_callback(
751
+ self.__handler_task_cmd_on_publish, self.__handler_task_on_publish_callback
752
+ )
753
+ self.__handler_task.register_cmd_callback(
754
+ self.__handler_task_cmd_on_subscribe, self.__handler_task_on_subscribe_callback
755
+ )
756
+ self.__handler_task.register_cmd_callback(
757
+ self.__handler_task_cmd_on_unsubscribe, self.__handler_task_on_unsubscribe_callback
758
+ )
759
+ self.__handler_task.start()
760
+
761
+ @property
762
+ def on_device_dynamic_register(self):
763
+ return None
764
+
765
+ @on_device_dynamic_register.setter
766
+ def on_device_dynamic_register(self, value) -> None:
767
+ self.__on_device_dynamic_register = value
768
+
769
+ @property
770
+ def on_connect(self):
771
+ return self.__on_connect
772
+
773
+ @on_connect.setter
774
+ def on_connect(self, value) -> None:
775
+ self.__on_connect = value
776
+
777
+ @property
778
+ def on_disconnect(self):
779
+ return self.__on_disconnect
780
+
781
+ @on_disconnect.setter
782
+ def on_disconnect(self, value) -> None:
783
+ self.__on_disconnect = value
784
+
785
+ @property
786
+ def on_publish_topic(self):
787
+ return None
788
+
789
+ @on_publish_topic.setter
790
+ def on_publish_topic(self, value) -> None:
791
+ self.__on_publish_topic = value
792
+
793
+ @property
794
+ def on_subscribe_topic(self):
795
+ return None
796
+
797
+ @on_subscribe_topic.setter
798
+ def on_subscribe_topic(self, value) -> None:
799
+ self.__on_subscribe_topic = value
800
+
801
+ @property
802
+ def on_unsubscribe_topic(self):
803
+ return None
804
+
805
+ @on_unsubscribe_topic.setter
806
+ def on_unsubscribe_topic(self, value) -> None:
807
+ self.__on_unsubscribe_topic = value
808
+
809
+ @property
810
+ def on_topic_message(self):
811
+ return None
812
+
813
+ @on_topic_message.setter
814
+ def on_topic_message(self, value) -> None:
815
+ self.__on_topic_message = value
816
+
817
+ @property
818
+ def on_topic_rrpc_message(self):
819
+ return None
820
+
821
+ @on_topic_rrpc_message.setter
822
+ def on_topic_rrpc_message(self, value) -> None:
823
+ self.__on_topic_rrpc_message = value
824
+
825
+ @property
826
+ def on_thing_create(self):
827
+ return None
828
+
829
+ @on_thing_create.setter
830
+ def on_thing_create(self, value) -> None:
831
+ self.__on_thing_create = value
832
+
833
+ @property
834
+ def on_thing_enable(self):
835
+ return None
836
+
837
+ @on_thing_enable.setter
838
+ def on_thing_enable(self, value) -> None:
839
+ self.__on_thing_enable = value
840
+
841
+ @property
842
+ def on_thing_disable(self):
843
+ return None
844
+
845
+ @on_thing_disable.setter
846
+ def on_thing_disable(self, value) -> None:
847
+ self.__on_thing_disable = value
848
+
849
+ @property
850
+ def on_thing_raw_data_arrived(self):
851
+ return None
852
+
853
+ @on_thing_raw_data_arrived.setter
854
+ def on_thing_raw_data_arrived(self, value) -> None:
855
+ self.__on_thing_raw_data_arrived = value
856
+
857
+ @property
858
+ def on_thing_raw_data_post(self):
859
+ return self.__on_thing_raw_data_post
860
+
861
+ @property
862
+ def on_thing_device_info_update(self):
863
+ return self.__on_thing_device_info_update
864
+
865
+ @on_thing_device_info_update.setter
866
+ def on_thing_device_info_update(self, value) -> None:
867
+ self.__on_thing_device_info_update = value
868
+
869
+ @property
870
+ def on_thing_device_info_delete(self):
871
+ return self.__on_thing_device_info_delete
872
+
873
+ @on_thing_device_info_delete.setter
874
+ def on_thing_device_info_delete(self, value) -> None:
875
+ self.__on_thing_device_info_delete = value
876
+
877
+ @on_thing_raw_data_post.setter
878
+ def on_thing_raw_data_post(self, value) -> None:
879
+ self.__on_thing_raw_data_post = value
880
+
881
+ @property
882
+ def on_thing_call_service(self):
883
+ return None
884
+
885
+ @on_thing_call_service.setter
886
+ def on_thing_call_service(self, value) -> None:
887
+ self.__on_thing_call_service = value
888
+
889
+ @property
890
+ def on_thing_prop_changed(self):
891
+ return None
892
+
893
+ @on_thing_prop_changed.setter
894
+ def on_thing_prop_changed(self, value) -> None:
895
+ self.__on_thing_prop_changed = value
896
+
897
+ @property
898
+ def on_thing_event_post(self):
899
+ return self.__on_thing_event_post
900
+
901
+ @on_thing_event_post.setter
902
+ def on_thing_event_post(self, value) -> None:
903
+ self.__on_thing_event_post = value
904
+
905
+ @property
906
+ def on_thing_prop_post(self):
907
+ return self.__on_thing_prop_post
908
+
909
+ @on_thing_prop_post.setter
910
+ def on_thing_prop_post(self, value) -> None:
911
+ self.__on_thing_prop_post = value
912
+
913
+ @property
914
+ def on_thing_shadow_get(self):
915
+ return self.__on_thing_shadow_get
916
+
917
+ @on_thing_shadow_get.setter
918
+ def on_thing_shadow_get(self, value) -> None:
919
+ self.__on_thing_shadow_get = value
920
+
921
+ @property
922
+ def on_file_upload_begin(self):
923
+ return self.__on_file_upload_begin
924
+
925
+ @on_file_upload_begin.setter
926
+ def on_file_upload_begin(self, value) -> None:
927
+ self.__on_file_upload_begin = value
928
+
929
+ @property
930
+ def on_file_upload_end(self):
931
+ return self.__on_file_upload_end
932
+
933
+ @on_file_upload_end.setter
934
+ def on_file_upload_end(self, value) -> None:
935
+ self.__on_file_upload_end = value
936
+
937
+ @property
938
+ def on_gateway_add_subdev_topo_reply(self):
939
+ return None
940
+
941
+ @on_gateway_add_subdev_topo_reply.setter
942
+ def on_gateway_add_subdev_topo_reply(self, value) -> None:
943
+ self.__on_gateway_add_subdev_topo_reply = value
944
+
945
+ @property
946
+ def on_gateway_delete_subdev_topo_reply(self):
947
+ return None
948
+
949
+ @on_gateway_delete_subdev_topo_reply.setter
950
+ def on_gateway_delete_subdev_topo_reply(self, value) -> None:
951
+ self.__on_gateway_delete_subdev_topo_reply = value
952
+
953
+ @property
954
+ def on_gateway_login_subdev_reply(self):
955
+ return None
956
+
957
+ @on_gateway_login_subdev_reply.setter
958
+ def on_gateway_login_subdev_reply(self, value) -> None:
959
+ self.__on_gateway_login_subdev_reply = value
960
+
961
+ @property
962
+ def on_gateway_logout_subdev_reply(self):
963
+ return None
964
+
965
+ @on_gateway_logout_subdev_reply.setter
966
+ def on_gateway_logout_subdev_reply(self, value) -> None:
967
+ self.__on_gateway_logout_subdev_reply = value
968
+
969
+ @property
970
+ def on_gateway_register_subdev_reply(self):
971
+ return None
972
+
973
+ @on_gateway_register_subdev_reply.setter
974
+ def on_gateway_register_subdev_reply(self, value) -> None:
975
+ self.__on_gateway_register_subdev_reply = value
976
+
977
+ @property
978
+ def on_gateway_product_register_subdev_reply(self):
979
+ return None
980
+
981
+ @on_gateway_product_register_subdev_reply.setter
982
+ def on_gateway_product_register_subdev_reply(self, value) -> None:
983
+ self.__on_gateway_product_register_subdev_reply = value
984
+
985
+ @property
986
+ def on_device_dynamic_register_nwl_reply(self):
987
+ return None
988
+
989
+ @on_device_dynamic_register_nwl_reply.setter
990
+ def on_device_dynamic_register_nwl_reply(self, value) -> None:
991
+ self.__on_device_dynamic_register_nwl_reply = value
992
+
993
+ @property
994
+ def on_ota_message_arrived(self):
995
+ return None
996
+
997
+ @on_ota_message_arrived.setter
998
+ def on_ota_message_arrived(self, value) -> None:
999
+ self.__on_ota_message_arrived = value
1000
+
1001
+ @property
1002
+ def on_gateway_topo_change(self):
1003
+ return None
1004
+
1005
+ @on_gateway_topo_change.setter
1006
+ def on_gateway_topo_change(self, value) -> None:
1007
+ self.__on_gateway_topo_change = value
1008
+
1009
+ def enable_logger(self, level) -> None:
1010
+ self.__link_log.config_logger(level)
1011
+ self.__link_log.enable_logger()
1012
+ if self.__mqtt_client is not None:
1013
+ self.__mqtt_client.enable_logger(self.__PahoLog)
1014
+ self.__PahoLog.setLevel(level)
1015
+
1016
+ def disable_logger(self) -> None:
1017
+ self.__link_log.disable_logger()
1018
+ if self.__mqtt_client is not None:
1019
+ self.__mqtt_client.disable_logger()
1020
+
1021
+ def config_logger(self, level) -> None:
1022
+ self.__link_log.config_logger(level)
1023
+ if self.__mqtt_client is not None:
1024
+ self.__PahoLog.setLevel(level)
1025
+
1026
+ def config_http2(self, endpoint=None):
1027
+ raise LinkKit.StateError("not supported any more")
1028
+
1029
+ def config_mqtt(
1030
+ self,
1031
+ port=8883,
1032
+ protocol="MQTTv311",
1033
+ transport="TCP",
1034
+ secure="TLS",
1035
+ keep_alive=60,
1036
+ clean_session=True,
1037
+ max_inflight_message=20,
1038
+ max_queued_message=40,
1039
+ auto_reconnect_min_sec=1,
1040
+ auto_reconnect_max_sec=60,
1041
+ cadata=None,
1042
+ endpoint=None,
1043
+ check_hostname=True,
1044
+ ):
1045
+ if self.__linkkit_state is not LinkKit.LinkKitState.INITIALIZED:
1046
+ raise LinkKit.StateError("not in INITIALIZED state")
1047
+ if port < 1 or port > 65535:
1048
+ raise ValueError("port wrong")
1049
+ if protocol not in ("MQTTv311", "MQTTv31"):
1050
+ raise ValueError("protocol wrong")
1051
+ if transport != "TCP":
1052
+ raise ValueError("transport wrong")
1053
+ if secure != "TLS" and secure != "":
1054
+ raise ValueError("secure wrong")
1055
+ if keep_alive < 30 or keep_alive > 1200:
1056
+ raise ValueError("keep_alive range wrong")
1057
+ if clean_session is not True and clean_session is not False:
1058
+ raise ValueError("clean session wrong")
1059
+ if max_queued_message < 0:
1060
+ raise ValueError("max_queued_message wrong")
1061
+ if max_inflight_message < 0:
1062
+ raise ValueError("max_inflight_message wrong")
1063
+ if auto_reconnect_min_sec < 1 or auto_reconnect_min_sec > 120 * 60:
1064
+ raise ValueError("auto_reconnect_min_sec wrong")
1065
+ if auto_reconnect_max_sec < 1 or auto_reconnect_max_sec > 120 * 60:
1066
+ raise ValueError("auto_reconnect_max_sec wrong")
1067
+ if auto_reconnect_min_sec > auto_reconnect_max_sec:
1068
+ raise ValueError("auto_reconnect_max_sec less than auto_reconnect_min_sec")
1069
+
1070
+ self.__link_log.info("config_mqtt enter")
1071
+ if self.__linkkit_state == LinkKit.LinkKitState.INITIALIZED:
1072
+ if port is not None:
1073
+ self.__mqtt_port = port
1074
+ if protocol is not None:
1075
+ self.__mqtt_protocol = protocol
1076
+ if transport is not None:
1077
+ self.__mqtt_transport = transport
1078
+ if secure is not None:
1079
+ self.__mqtt_secure = secure
1080
+ if keep_alive is not None:
1081
+ self.__mqtt_keep_alive = keep_alive
1082
+ if clean_session is not None:
1083
+ self.__mqtt_clean_session = clean_session
1084
+ if max_inflight_message is not None:
1085
+ self.__mqtt_max_inflight_message = max_inflight_message
1086
+ if max_queued_message is not None:
1087
+ self.__mqtt_max_queued_message = max_queued_message
1088
+ if auto_reconnect_min_sec is not None:
1089
+ self.__mqtt_auto_reconnect_min_sec = auto_reconnect_min_sec
1090
+ if auto_reconnect_max_sec is not None:
1091
+ self.__mqtt_auto_reconnect_max_sec = auto_reconnect_max_sec
1092
+ if cadata is not None:
1093
+ self.__aliyun_broker_ca_data = cadata
1094
+ if endpoint is not None:
1095
+ self.__endpoint = endpoint
1096
+ if check_hostname is not None:
1097
+ self.__check_hostname = check_hostname
1098
+ if check_hostname == False:
1099
+ self.__link_log.info("skip hostname check")
1100
+
1101
+ def config_device_info(self, interface_info) -> int:
1102
+ if self.__linkkit_state is not LinkKit.LinkKitState.INITIALIZED:
1103
+ raise LinkKit.StateError("LinkKit object not in INITIALIZED")
1104
+ if not isinstance(interface_info, str):
1105
+ raise ValueError("interface info must be string")
1106
+ if len(interface_info) > 160:
1107
+ return 1
1108
+ self.__device_interface_info = interface_info
1109
+ return 0
1110
+
1111
+ def get_product(self):
1112
+ return self.__product_key
1113
+
1114
+ def get_device_name(self):
1115
+ return self.__device_name
1116
+
1117
+ def get_endpoint(self):
1118
+ return self.__endpoint
1119
+
1120
+ def get_h2_endpoint(self):
1121
+ raise LinkKit.StateError("not supported any more")
1122
+
1123
+ def get_actual_endpoint(self):
1124
+ return self.__generate_endpoint()
1125
+
1126
+ def get_actual_h2_endpoint(self):
1127
+ raise LinkKit.StateError("not supported any more")
1128
+
1129
+ def __load_json(self, payload):
1130
+ return json.loads(self.__to_str(payload))
1131
+
1132
+ def __to_str(self, payload):
1133
+ if type(payload) is bytes:
1134
+ return str(payload, "utf-8")
1135
+ else:
1136
+ return payload
1137
+
1138
+ def upload_file_sync(self, local_filename, remote_filename=None, over_write=True, timeout=None):
1139
+ raise LinkKit.StateError("not supported any more")
1140
+
1141
+ def upload_file_async(self, local_filename, remote_filename=None, over_write=True):
1142
+ raise LinkKit.StateError("not supported any more")
1143
+
1144
+ def __upload_device_interface_info(self) -> int:
1145
+ request_id = self.__get_thing_request_id()
1146
+ payload = {
1147
+ "id": request_id,
1148
+ "version": "1.0",
1149
+ "params": [
1150
+ {"domain": "SYSTEM", "attrKey": "SYS_SDK_LANGUAGE", "attrValue": self.__sdk_program_language},
1151
+ {"domain": "SYSTEM", "attrKey": "SYS_LP_SDK_VERSION", "attrValue": self.__sdk_version},
1152
+ {"domain": "SYSTEM", "attrKey": "SYS_SDK_IF_INFO", "attrValue": self.__device_interface_info},
1153
+ ],
1154
+ "method": "thing.deviceinfo.update",
1155
+ }
1156
+ with self.__device_info_mid_lock:
1157
+ rc, mid = self.__mqtt_client.publish(self.__device_info_topic, json.dumps(payload), 0)
1158
+ if rc == mqtt.MQTT_ERR_SUCCESS:
1159
+ self.__device_info_mid[mid] = self.__timestamp()
1160
+ return 0
1161
+ else:
1162
+ return 1
1163
+
1164
+ def destruct(self) -> None:
1165
+ if self.__linkkit_state is LinkKit.LinkKitState.DESTRUCTED:
1166
+ self.__link_log.info("LinkKit object has already destructed")
1167
+ return
1168
+ self.__link_log.debug("destruct enter")
1169
+ if (
1170
+ self.__linkkit_state == LinkKit.LinkKitState.CONNECTED
1171
+ or self.__linkkit_state == LinkKit.LinkKitState.CONNECTING
1172
+ ):
1173
+ self.__linkkit_state = LinkKit.LinkKitState.DESTRUCTING
1174
+ if self.__connect_async_req:
1175
+ with self.__worker_loop_exit_req_lock:
1176
+ self.__worker_loop_exit_req = True
1177
+ if self.__mqtt_client is not None:
1178
+ self.__mqtt_client.disconnect()
1179
+ self.__handler_task.wait_stop()
1180
+ else:
1181
+ self.__linkkit_state = LinkKit.LinkKitState.DESTRUCTING
1182
+ if self.__connect_async_req:
1183
+ with self.__worker_loop_exit_req_lock:
1184
+ self.__worker_loop_exit_req = True
1185
+ self.__handler_task.stop()
1186
+ self.__handler_task.wait_stop()
1187
+ self.__linkkit_state = LinkKit.LinkKitState.DESTRUCTED
1188
+
1189
+ def destroy(self) -> None:
1190
+ self.destruct()
1191
+
1192
+ def check_state(self):
1193
+ return self.__linkkit_state
1194
+
1195
+ @staticmethod
1196
+ def __generate_random_str(randomlength=16):
1197
+ """Generate radom string"""
1198
+ random_str = ""
1199
+ for i in range(randomlength):
1200
+ random_str += random.choice(string.digits + string.ascii_letters)
1201
+ return random_str
1202
+
1203
+ # 基于HTTPS的一型一密预注册
1204
+ def __dynamic_register_device(self):
1205
+ pk = self.__product_key
1206
+ ps = self.__product_secret
1207
+ dn = self.__device_name
1208
+ random_str = self.__generate_random_str(15)
1209
+ context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cadata=self.__aliyun_broker_ca_data)
1210
+ sign_content = "deviceName%sproductKey%srandom%s" % (dn, pk, random_str)
1211
+ sign = hmac.new(ps.encode("utf-8"), sign_content.encode("utf-8"), hashlib.sha256).hexdigest()
1212
+ post_data = {"productKey": pk, "deviceName": dn, "random": random_str, "sign": sign, "signMethod": "hmacsha256"}
1213
+ data = urllib.parse.urlencode(post_data)
1214
+ data = data.encode("ascii")
1215
+ request_url = "https://iot-auth.%s.aliyuncs.com/auth/register/device" % self.__host_name
1216
+ with urllib.request.urlopen(request_url, data, context=context) as f:
1217
+ reply_data = f.read().decode("utf-8")
1218
+ reply_obj = self.__load_json(reply_data)
1219
+ if reply_obj["code"] == 200:
1220
+ reply_obj_data = reply_obj["data"]
1221
+ if reply_obj_data is not None:
1222
+ return 0, reply_obj_data["deviceSecret"]
1223
+ else:
1224
+ return 1, reply_obj["message"]
1225
+
1226
+ def __config_mqtt_client_internal(self):
1227
+ self.__link_log.info("start connect")
1228
+
1229
+ timestamp = str(int(time.time()))
1230
+ if self.__mqtt_secure == "TLS":
1231
+ securemode = 2
1232
+ else:
1233
+ securemode = 3
1234
+ if self.__device_interface_info:
1235
+ sii_option = "sii=%s," % (self.__device_interface_info)
1236
+ else:
1237
+ sii_option = ""
1238
+
1239
+ # 普通的一机一密认证方式
1240
+ if self.__is_valid_str(self.__device_secret):
1241
+ client_id = "%s&%s|securemode=%d,signmethod=hmacsha1,ext=1,_ss=1,lan=%s,_v=%s,%stimestamp=%s|" % (
1242
+ self.__product_key,
1243
+ self.__device_name,
1244
+ securemode,
1245
+ self.__sdk_program_language,
1246
+ self.__sdk_version,
1247
+ sii_option,
1248
+ timestamp,
1249
+ )
1250
+ username = self.__device_name + "&" + self.__product_key
1251
+ # calc sign
1252
+ sign_content = "clientId%sdeviceName%sproductKey%stimestamp%s" % (
1253
+ self.__product_key + "&" + self.__device_name,
1254
+ self.__device_name,
1255
+ self.__product_key,
1256
+ timestamp,
1257
+ )
1258
+ password = hmac.new(
1259
+ self.__device_secret.encode("utf-8"), sign_content.encode("utf-8"), hashlib.sha1
1260
+ ).hexdigest()
1261
+ # 通过username, passwd, cilentid直连接入的方式
1262
+ elif (
1263
+ self.__is_valid_str(self.__client_id)
1264
+ and self.__is_valid_str(self.__username)
1265
+ and self.__is_valid_str(self.__password)
1266
+ ):
1267
+ # 如果已经通过基于mqtt的免预注册一型一密协议获取到了client_id, user_name, password
1268
+ client_id = self.__client_id
1269
+ username = self.__username
1270
+ password = self.__password
1271
+ # 基于mqtt协议的一型一密,包括预注册和非预注册两种, 尝试去获取username, clientid和password
1272
+ elif self.__is_valid_str(self.__product_secret):
1273
+ if self.__auth_type == "regnwl":
1274
+ # 通过一型一密免预注册协议发起认证
1275
+ self.__dynamic_register_nwl_flag = 1
1276
+ elif self.__auth_type == "register":
1277
+ # 通过一型一密预注册协议发起认证
1278
+ self.__dynamic_register_flag = 1
1279
+ else:
1280
+ raise LinkKit.StateError("unknow dynreg param error")
1281
+
1282
+ auth_type_str = self.__auth_type
1283
+ random_str = self.__generate_random_str(15)
1284
+ if self.__is_valid_str(self.__instance_id):
1285
+ client_id = "%s.%s|random=%s,authType=%s,securemode=2,signmethod=hmacsha256,instanceId=%s|" % (
1286
+ self.__device_name,
1287
+ self.__product_key,
1288
+ random_str,
1289
+ auth_type_str,
1290
+ self.__instance_id,
1291
+ )
1292
+ else:
1293
+ client_id = "%s.%s|random=%s,authType=%s,securemode=2,signmethod=hmacsha256|" % (
1294
+ self.__device_name,
1295
+ self.__product_key,
1296
+ random_str,
1297
+ auth_type_str,
1298
+ )
1299
+ username = "%s&%s" % (self.__device_name, self.__product_key)
1300
+ password_src = "deviceName%sproductKey%srandom%s" % (self.__device_name, self.__product_key, random_str)
1301
+ password = hmac.new(
1302
+ self.__product_secret.encode("utf-8"), password_src.encode("utf-8"), hashlib.sha256
1303
+ ).hexdigest()
1304
+ else:
1305
+ raise LinkKit.StateError("unknow auth error")
1306
+
1307
+ # mqtt client start initialize
1308
+ mqtt_protocol_version = mqtt.MQTTv311
1309
+ if self.__mqtt_protocol == "MQTTv311":
1310
+ mqtt_protocol_version = mqtt.MQTTv311
1311
+ elif self.__mqtt_protocol == "MQTTv31":
1312
+ mqtt_protocol_version = mqtt.MQTTv31
1313
+ self.__mqtt_client = mqtt.Client(
1314
+ client_id=client_id, clean_session=self.__mqtt_clean_session, protocol=mqtt_protocol_version
1315
+ )
1316
+
1317
+ if self.__link_log.is_enabled():
1318
+ self.__mqtt_client.enable_logger(self.__PahoLog)
1319
+ self.__mqtt_client.username_pw_set(username, password)
1320
+ self.__mqtt_client.on_connect = self.__on_internal_connect
1321
+ self.__mqtt_client.on_disconnect = self.__on_internal_disconnect
1322
+ self.__mqtt_client.on_message = self.__on_internal_message
1323
+ self.__mqtt_client.on_publish = self.__on_internal_publish
1324
+ self.__mqtt_client.on_subscribe = self.__on_internal_subscribe
1325
+ self.__mqtt_client.on_unsubscribe = self.__on_internal_unsubscribe
1326
+
1327
+ self.__mqtt_client.reconnect_delay_set(self.__mqtt_auto_reconnect_min_sec, self.__mqtt_auto_reconnect_max_sec)
1328
+ self.__mqtt_client.max_queued_messages_set(self.__mqtt_max_queued_message)
1329
+ self.__mqtt_client.max_inflight_messages_set(self.__mqtt_max_inflight_message)
1330
+
1331
+ # mqtt set tls
1332
+ self.__link_log.debug("current working directory:" + os.getcwd())
1333
+ if self.__mqtt_secure == "TLS":
1334
+ context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cadata=self.__aliyun_broker_ca_data)
1335
+ context.check_hostname = self.__check_hostname
1336
+ self.__mqtt_client.tls_set_context(context)
1337
+ # mqtt client start connect
1338
+ self.__host_name_internal = self.__generate_endpoint()
1339
+
1340
+ def __generate_endpoint(self):
1341
+ if self.__endpoint:
1342
+ return self.__endpoint
1343
+ elif self.__host_name == "127.0.0.1" or self.__host_name == "localhost":
1344
+ return self.__host_name
1345
+ elif self.__is_valid_str(self.__instance_id):
1346
+ return self.__instance_id + ".mqtt.iothub.aliyuncs.com"
1347
+ else:
1348
+ return "%s.iot-as-mqtt.%s.aliyuncs.com" % (self.__product_key, self.__host_name)
1349
+
1350
+ def connect(self):
1351
+ raise LinkKit.StateError("not supported")
1352
+
1353
+ def connect_async(self):
1354
+ self.__link_log.debug("connect_async")
1355
+ if self.__linkkit_state not in (LinkKit.LinkKitState.INITIALIZED, LinkKit.LinkKitState.DISCONNECTED):
1356
+ raise LinkKit.StateError("not in INITIALIZED or DISCONNECTED state")
1357
+ self.__connect_async_req = True
1358
+ with self.__worker_loop_exit_req_lock:
1359
+ self.__worker_loop_exit_req = False
1360
+ return self.__loop_thread.start(self.__loop_forever_internal)
1361
+
1362
+ def disconnect(self) -> None:
1363
+ self.__link_log.debug("disconnect")
1364
+ if self.__linkkit_state is not LinkKit.LinkKitState.CONNECTED:
1365
+ self.__link_log.info("already disconnected, return")
1366
+ return
1367
+ self.__linkkit_state = LinkKit.LinkKitState.DISCONNECTING
1368
+ if self.__connect_async_req:
1369
+ with self.__worker_loop_exit_req_lock:
1370
+ self.__worker_loop_exit_req = True
1371
+ self.__mqtt_client.disconnect()
1372
+ self.__loop_thread.stop()
1373
+
1374
+ @staticmethod
1375
+ def __check_topic_string(topic):
1376
+ if len(topic) > 128 or len(topic) == 0:
1377
+ raise ValueError("topic string length too long,need decrease %d bytes" % (128 - len(topic)))
1378
+
1379
+ def publish_topic(self, topic, payload=None, qos=1):
1380
+ if self.__linkkit_state is not LinkKit.LinkKitState.CONNECTED:
1381
+ self.__link_log.error("disconnected, pub fail")
1382
+ return LinkKit.ErrorCode.NETWORK_DISCONNECTED.value, None
1383
+ if topic is None or len(topic) == 0:
1384
+ return LinkKit.ErrorCode.INVALID_TOPIC.value, None
1385
+ if qos != 0 and qos != 1:
1386
+ return LinkKit.ErrorCode.INVALID_QOS.value, None
1387
+ self.__check_topic_string(topic)
1388
+ rc, mid = self.__mqtt_client.publish(topic, payload, qos)
1389
+ if rc == 0:
1390
+ return LinkKit.ErrorCode.SUCCESS.value, mid
1391
+ else:
1392
+ return LinkKit.ErrorCode.PAHO_MQTT_ERROR.value, None
1393
+
1394
+ def subscribe_topic(self, topic, qos=1):
1395
+ if self.__linkkit_state is not LinkKit.LinkKitState.CONNECTED:
1396
+ self.__link_log.error("disconnected, sub fail")
1397
+ return LinkKit.ErrorCode.NETWORK_DISCONNECTED.value, None
1398
+ if isinstance(topic, tuple):
1399
+ topic, qos = topic
1400
+
1401
+ if isinstance(topic, str):
1402
+ if qos < 0 or qos > 1:
1403
+ return LinkKit.ErrorCode.INVALID_QOS.value, None
1404
+ if topic is None or len(topic) == 0:
1405
+ return LinkKit.ErrorCode.INVALID_TOPIC.value, None
1406
+ self.__check_topic_string(topic)
1407
+ if topic not in self.__user_topics:
1408
+ self.__user_topics_request_lock.acquire()
1409
+ ret = self.__mqtt_client.subscribe(topic, qos)
1410
+ rc, mid = ret
1411
+ if rc == mqtt.MQTT_ERR_SUCCESS:
1412
+ self.__user_topics_subscribe_request[mid] = [(topic, qos)]
1413
+ self.__user_topics_request_lock.release()
1414
+ if rc == mqtt.MQTT_ERR_SUCCESS:
1415
+ return 0, mid
1416
+ if rc == mqtt.MQTT_ERR_NO_CONN:
1417
+ return 2, None
1418
+ return 3, None
1419
+ else:
1420
+ return 1, None
1421
+ elif isinstance(topic, list):
1422
+ topic_qos_list = []
1423
+ user_topic_dict = {}
1424
+ for t, q in topic:
1425
+ if q < 0 or q > 1:
1426
+ return LinkKit.ErrorCode.INVALID_QOS.value, None
1427
+ if t is None or len(t) == 0 or not isinstance(t, str):
1428
+ return LinkKit.ErrorCode.INVALID_TOPIC.value, None
1429
+ self.__check_topic_string(t)
1430
+ user_topic_dict[t] = q
1431
+ topic_qos_list.append((t, q))
1432
+ self.__user_topics_request_lock.acquire()
1433
+ ret = self.__mqtt_client.subscribe(topic_qos_list)
1434
+ rc, mid = ret
1435
+ if rc == mqtt.MQTT_ERR_SUCCESS:
1436
+ self.__user_topics_subscribe_request[mid] = topic_qos_list
1437
+ self.__link_log.debug("__user_topics_subscribe_request add mid:%d" % mid)
1438
+ self.__user_topics_request_lock.release()
1439
+ return rc, mid
1440
+ else:
1441
+ self.__link_log.error("Parameter type wrong")
1442
+ return LinkKit.ErrorCode.INVALID_TOPIC.value, None
1443
+
1444
+ def unsubscribe_topic(self, topic):
1445
+ if self.__linkkit_state is not LinkKit.LinkKitState.CONNECTED:
1446
+ self.__link_log.error("disconnected, unsub fail")
1447
+ return LinkKit.ErrorCode.NETWORK_DISCONNECTED.value, None
1448
+ unsubscribe_topics = []
1449
+ if topic is None or topic == "":
1450
+ return LinkKit.ErrorCode.INVALID_TOPIC.value, None
1451
+ if isinstance(topic, str):
1452
+ self.__check_topic_string(topic)
1453
+ if topic not in self.__user_topics:
1454
+ return 1, None
1455
+ unsubscribe_topics.append(topic)
1456
+ elif isinstance(topic, list):
1457
+ for one_topic in topic:
1458
+ self.__check_topic_string(one_topic)
1459
+ if one_topic in self.__user_topics:
1460
+ unsubscribe_topics.append(one_topic)
1461
+ else:
1462
+ pass
1463
+ with self.__user_topics_unsubscribe_request_lock:
1464
+ if len(unsubscribe_topics) == 0:
1465
+ return 2, None
1466
+ ret = self.__mqtt_client.unsubscribe(unsubscribe_topics)
1467
+ rc, mid = ret
1468
+ if rc == mqtt.MQTT_ERR_SUCCESS:
1469
+ self.__user_topics_unsubscribe_request[mid] = unsubscribe_topics
1470
+ return ret
1471
+ else:
1472
+ return 1, None
1473
+
1474
+ def __make_rrpc_topic(self, topic) -> str:
1475
+ return "/ext/rrpc/+%s" % (topic)
1476
+
1477
+ def subscribe_rrpc_topic(self, topic):
1478
+ if self.__linkkit_state is not LinkKit.LinkKitState.CONNECTED:
1479
+ self.__link_log.error("disconnected, sub rrpc fail")
1480
+ return LinkKit.ErrorCode.NETWORK_DISCONNECTED.value, None
1481
+ qos = 0
1482
+ if isinstance(topic, str):
1483
+ if topic is None or len(topic) == 0:
1484
+ return LinkKit.ErrorCode.INVALID_TOPIC.value, None
1485
+ self.__check_topic_string(topic)
1486
+ topic = self.__tidy_topic(topic)
1487
+ rrpc_topic = self.__make_rrpc_topic(topic)
1488
+ with self.__user_rrpc_topics_lock:
1489
+ not_exist = topic not in self.__user_rrpc_topics.keys()
1490
+ if not_exist:
1491
+ with self.__user_rrpc_topics_lock:
1492
+ self.__user_rrpc_topics[topic] = qos
1493
+ with self.__user_rrpc_topics_subscribe_request_lock:
1494
+ ret = self.__mqtt_client.subscribe(rrpc_topic, qos)
1495
+ rc, mid = ret
1496
+ if rc == mqtt.MQTT_ERR_SUCCESS:
1497
+ self.__user_rrpc_topics_subscribe_request[mid] = [(rrpc_topic, qos)]
1498
+ if rc == mqtt.MQTT_ERR_SUCCESS:
1499
+ return 0, mid
1500
+ if rc == mqtt.MQTT_ERR_NO_CONN:
1501
+ return 2, None
1502
+ return 3, None
1503
+ else:
1504
+ return 1, None
1505
+ elif isinstance(topic, list):
1506
+ topic_qos_list = []
1507
+ for t in topic:
1508
+ if t is None or len(t) == 0 or not isinstance(t, str):
1509
+ return LinkKit.ErrorCode.INVALID_TOPIC.value, None
1510
+ self.__check_topic_string(t)
1511
+ t = self.__tidy_topic(t)
1512
+ rrpc_t = self.__make_rrpc_topic(t)
1513
+ with self.__user_rrpc_topics_lock:
1514
+ self.__user_rrpc_topics[t] = qos
1515
+ topic_qos_list.append((rrpc_t, qos))
1516
+ with self.__user_rrpc_topics_subscribe_request_lock:
1517
+ ret = self.__mqtt_client.subscribe(topic_qos_list)
1518
+ rc, mid = ret
1519
+ if rc == mqtt.MQTT_ERR_SUCCESS:
1520
+ self.__user_rrpc_topics_subscribe_request[mid] = topic_qos_list
1521
+ self.__link_log.debug("__user_rrpc_topics_subscribe_request add mid:%d" % mid)
1522
+ return rc, mid
1523
+ else:
1524
+ self.__link_log.debug("Parameter type wrong")
1525
+ return LinkKit.ErrorCode.INVALID_TOPIC.value, None
1526
+
1527
+ def unsubscribe_rrpc_topic(self, topic):
1528
+ if self.__linkkit_state is not LinkKit.LinkKitState.CONNECTED:
1529
+ self.__link_log.error("disconnected, unsub rrpc fail")
1530
+ return LinkKit.ErrorCode.NETWORK_DISCONNECTED.value, None
1531
+ unsubscribe_topics = []
1532
+ if topic is None or topic == "":
1533
+ return LinkKit.ErrorCode.INVALID_TOPIC.value, None
1534
+ if isinstance(topic, str):
1535
+ self.__check_topic_string(topic)
1536
+ topic = self.__tidy_topic(topic)
1537
+ with self.__user_rrpc_topics_lock:
1538
+ if topic not in self.__user_rrpc_topics:
1539
+ return 1, None
1540
+ rrpc_topic = self.__make_rrpc_topic(topic)
1541
+ unsubscribe_topics.append(rrpc_topic)
1542
+ with self.__user_rrpc_topics_lock:
1543
+ del self.__user_rrpc_topics[topic]
1544
+
1545
+ elif isinstance(topic, list):
1546
+ for one_topic in topic:
1547
+ self.__check_topic_string(one_topic)
1548
+ one_topic = self.__tidy_topic(one_topic)
1549
+ with self.__user_rrpc_topics_lock:
1550
+ if one_topic in self.__user_rrpc_topics:
1551
+ rrpc_topic = self.__make_rrpc_topic(one_topic)
1552
+ unsubscribe_topics.append(rrpc_topic)
1553
+ del self.__user_rrpc_topics[one_topic]
1554
+ else:
1555
+ pass
1556
+ with self.__user_rrpc_topics_unsubscribe_request_lock:
1557
+ if len(unsubscribe_topics) == 0:
1558
+ return 2, None
1559
+ ret = self.__mqtt_client.unsubscribe(unsubscribe_topics)
1560
+ rc, mid = ret
1561
+ if rc == mqtt.MQTT_ERR_SUCCESS:
1562
+ self.__user_rrpc_topics_unsubscribe_request[mid] = unsubscribe_topics
1563
+ return ret
1564
+ else:
1565
+ return 1, None
1566
+
1567
+ def __on_internal_connect_safe(self, client, user_data, session_flag, rc) -> None:
1568
+ if rc == 0:
1569
+ self.__reset_reconnect_wait()
1570
+ self.__force_reconnect = False
1571
+ session_flag_internal = {"session present": session_flag}
1572
+ self.__handler_task.post_message(
1573
+ self.__handler_task_cmd_on_connect, (client, user_data, session_flag_internal, rc)
1574
+ )
1575
+
1576
+ def __loop_forever_internal(self):
1577
+ self.__link_log.debug("enter")
1578
+ self.__linkkit_state = LinkKit.LinkKitState.CONNECTING
1579
+
1580
+ # 为了保持存量设备兼容,保留了基于https的需要预注册的一型一密的预注册
1581
+ if not self.__is_valid_str(self.__device_secret) and self.__is_valid_str(self.__product_secret):
1582
+ lack_other_auth_info = (
1583
+ not self.__is_valid_str(self.__username)
1584
+ or not self.__is_valid_str(self.__client_id)
1585
+ or not self.__is_valid_str(self.__password)
1586
+ )
1587
+ if not self.__is_valid_str(self.__auth_type) and lack_other_auth_info:
1588
+ rc, value = self.__dynamic_register_device()
1589
+ try:
1590
+ self.__on_device_dynamic_register(rc, value, self.__user_data)
1591
+ if rc == 0:
1592
+ self.__device_secret = value
1593
+ else:
1594
+ self.__link_log.error("dynamic register device fail:" + value)
1595
+ self.__linkkit_state = LinkKit.LinkKitState.INITIALIZED
1596
+ return 1
1597
+ except Exception as e:
1598
+ self.__link_log.error(e)
1599
+ self.__linkkit_state = LinkKit.LinkKitState.INITIALIZED
1600
+ return 2
1601
+ try:
1602
+ self.__config_mqtt_client_internal()
1603
+ except ssl.SSLError as e:
1604
+ self.__link_log.error("config mqtt raise exception:" + str(e))
1605
+ self.__linkkit_state = LinkKit.LinkKitState.INITIALIZED
1606
+ self.__on_internal_connect_safe(None, None, 0, 6)
1607
+ return
1608
+
1609
+ try:
1610
+ self.__mqtt_client.connect_async(
1611
+ host=self.__host_name_internal, port=self.__mqtt_port, keepalive=self.__mqtt_keep_alive
1612
+ )
1613
+ except Exception as e:
1614
+ self.__link_log.error("__loop_forever_internal connect raise exception:" + str(e))
1615
+ self.__linkkit_state = LinkKit.LinkKitState.INITIALIZED
1616
+ self.__on_internal_connect_safe(None, None, 0, 7)
1617
+ return
1618
+ while True:
1619
+ if self.__worker_loop_exit_req:
1620
+ if self.__linkkit_state == LinkKit.LinkKitState.DESTRUCTING:
1621
+ self.__handler_task.stop()
1622
+ self.__linkkit_state = LinkKit.LinkKitState.DESTRUCTED
1623
+ break
1624
+ try:
1625
+ self.__linkkit_state = LinkKit.LinkKitState.CONNECTING
1626
+ self.__mqtt_client.reconnect()
1627
+ except OSError as e:
1628
+ self.__link_log.error(e)
1629
+ # if isinstance(e, socket.timeout):
1630
+ # self.__link_log.error("connect timeout")
1631
+ # self.__on_internal_connect_safe(None, None, 0, 8)
1632
+ # self.__reconnect_wait()
1633
+ # continue
1634
+ # if isinstance(e, ssl.SSLError):
1635
+ # self.__on_internal_connect_safe(None, None, 0, 6)
1636
+ # return
1637
+ if self.__linkkit_state == LinkKit.LinkKitState.CONNECTING:
1638
+ self.__linkkit_state = LinkKit.LinkKitState.DISCONNECTED
1639
+ self.__on_internal_connect_safe(None, None, 0, 9)
1640
+ if self.__linkkit_state == LinkKit.LinkKitState.DESTRUCTING:
1641
+ self.__handler_task.stop()
1642
+ self.__linkkit_state = LinkKit.LinkKitState.DESTRUCTED
1643
+ break
1644
+ self.__reconnect_wait()
1645
+ continue
1646
+ # 1. ca wrong 2.socket create timeout 3.connect timeout, call on_connect error
1647
+ # connect success
1648
+ rc = mqtt.MQTT_ERR_SUCCESS
1649
+ while rc == mqtt.MQTT_ERR_SUCCESS:
1650
+ try:
1651
+ rc = self.__mqtt_client.loop(self.__mqtt_request_timeout)
1652
+ except Exception as e:
1653
+ self.__link_log.info("loop error:" + str(e))
1654
+ self.__clean_timeout_message()
1655
+ self.__clean_thing_timeout_request_id()
1656
+ if self.__force_reconnect is True:
1657
+ self.__force_reconnect = False
1658
+ break
1659
+
1660
+ if self.__linkkit_state == LinkKit.LinkKitState.CONNECTED:
1661
+ self.__on_internal_disconnect(None, None, 1)
1662
+ self.__link_log.info("loop return:%r" % rc)
1663
+
1664
+ if self.__worker_loop_exit_req:
1665
+ if self.__linkkit_state == LinkKit.LinkKitState.DESTRUCTING:
1666
+ self.__handler_task.stop()
1667
+ self.__linkkit_state = LinkKit.LinkKitState.DESTRUCTED
1668
+ break
1669
+ self.__reconnect_wait()
1670
+ # 在mqtt + 一型一密免预注册的情况下,由于三元组不对,导致无法与服务器建连,因此需要退出,否则会一直重试
1671
+ if self.__dynamic_register_nwl_flag == 1:
1672
+ if self.__on_device_dynamic_register_nwl_reply is not None:
1673
+ self.__on_device_dynamic_register_nwl_reply(
1674
+ LinkKit.ErrorCode.DYNREG_AUTH_NWL_FAILED.value, None, None, None
1675
+ )
1676
+ self.__linkkit_state = LinkKit.LinkKitState.DISCONNECTED
1677
+ self.__dynamic_register_nwl_flag = 0
1678
+ break
1679
+ # 在mqtt + 一型一密预注册的情况下,由于三元组不对,导致无法与服务器建连,因此需要退出,否则会一直重试
1680
+ if self.__dynamic_register_flag == 1:
1681
+ if self.__on_device_dynamic_register is not None:
1682
+ self.__on_device_dynamic_register(LinkKit.ErrorCode.DYNREG_AUTH_FAILED.value, None, None)
1683
+ self.__linkkit_state = LinkKit.LinkKitState.DISCONNECTED
1684
+ self.__dynamic_register_flag = 0
1685
+ break
1686
+
1687
+ def __clean_timeout_message(self) -> None:
1688
+ # self.__link_log.debug("__clean_timeout_message enter")
1689
+ expire_timestamp = self.__timestamp() - self.__mqtt_request_timeout * 1000
1690
+ with self.__thing_prop_post_mid_lock:
1691
+ self.__clean_timeout_message_item(self.__thing_prop_post_mid, expire_timestamp)
1692
+ with self.__thing_event_post_mid_lock:
1693
+ self.__clean_timeout_message_item(self.__thing_event_post_mid, expire_timestamp)
1694
+ with self.__thing_answer_service_mid_lock:
1695
+ self.__clean_timeout_message_item(self.__thing_answer_service_mid, expire_timestamp)
1696
+ with self.__thing_raw_up_mid_lock:
1697
+ self.__clean_timeout_message_item(self.__thing_raw_up_mid, expire_timestamp)
1698
+ with self.__thing_raw_down_reply_mid_lock:
1699
+ self.__clean_timeout_message_item(self.__thing_raw_down_reply_mid, expire_timestamp)
1700
+ with self.__thing_prop_set_reply_mid_lock:
1701
+ self.__clean_timeout_message_item(self.__thing_prop_set_reply_mid, expire_timestamp)
1702
+ self.__clean_timeout_message_item(self.__device_info_mid, expire_timestamp)
1703
+ # self.__link_log.debug("__clean_timeout_message exit")
1704
+
1705
+ def __clean_timeout_message_item(self, mids, expire_time) -> None:
1706
+ for mid in list(mids.keys()):
1707
+ if mids[mid] < expire_time:
1708
+ timestamp = mids.pop(mid)
1709
+ self.__link_log.error("__clean_timeout_message_item pop:%r,timestamp:%r", mid, timestamp)
1710
+
1711
+ def __reconnect_wait(self) -> None:
1712
+ if self.__mqtt_auto_reconnect_sec == 0:
1713
+ self.__mqtt_auto_reconnect_sec = self.__mqtt_auto_reconnect_min_sec
1714
+ else:
1715
+ self.__mqtt_auto_reconnect_sec = min(self.__mqtt_auto_reconnect_sec * 2, self.__mqtt_auto_reconnect_max_sec)
1716
+ self.__mqtt_auto_reconnect_sec += random.randint(1, self.__mqtt_auto_reconnect_sec)
1717
+ time.sleep(self.__mqtt_auto_reconnect_sec)
1718
+
1719
+ def __reset_reconnect_wait(self) -> None:
1720
+ self.__mqtt_auto_reconnect_sec = 0
1721
+
1722
+ def start_worker_loop(self) -> None:
1723
+ pass
1724
+
1725
+ def thing_setup(self, file=None) -> int:
1726
+ if self.__linkkit_state is not LinkKit.LinkKitState.INITIALIZED:
1727
+ raise LinkKit.StateError("not in INITIALIZED state")
1728
+ if self.__thing_setup_state:
1729
+ return 1
1730
+ if file is None:
1731
+ self.__thing_raw_only = True
1732
+ self.__thing_setup_state = True
1733
+ return 0
1734
+ try:
1735
+ with open(file, encoding="utf-8") as f:
1736
+ tsl = json.load(f)
1737
+ index = 0
1738
+ while "events" in tsl and index < len(tsl["events"]):
1739
+ identifier = tsl["events"][index]["identifier"]
1740
+ if identifier == "post":
1741
+ output_data = tsl["events"][index]["outputData"]
1742
+ output_data_index = 0
1743
+ while output_data_index < len(output_data):
1744
+ output_data_item = output_data[output_data_index]["identifier"]
1745
+ self.__thing_properties_post.add(output_data_item)
1746
+ output_data_index += 1
1747
+ else:
1748
+ self.__thing_events.add(identifier)
1749
+ index += 1
1750
+ index = 0
1751
+ while "services" in tsl and index < len(tsl["services"]):
1752
+ identifier = tsl["services"][index]["identifier"]
1753
+ if identifier == "set":
1754
+ input_data = tsl["services"][index]["inputData"]
1755
+ input_data_index = 0
1756
+ while input_data_index < len(input_data):
1757
+ input_data_item = input_data[input_data_index]
1758
+ self.__thing_properties_set.add(input_data_item["identifier"])
1759
+ input_data_index += 1
1760
+ elif identifier == "get":
1761
+ output_data = tsl["services"][index]["outputData"]
1762
+ output_data_index = 0
1763
+ while output_data_index < len(output_data):
1764
+ output_data_item = output_data[output_data_index]
1765
+ self.__thing_properties_get.add(output_data_item["identifier"])
1766
+ output_data_index += 1
1767
+ else:
1768
+ self.__thing_services.add(identifier)
1769
+ service_reply_topic = self.__thing_topic_service_pattern % (
1770
+ self.__product_key,
1771
+ self.__device_name,
1772
+ identifier + "_reply",
1773
+ )
1774
+ self.__thing_topic_services_reply.add(service_reply_topic)
1775
+ index += 1
1776
+
1777
+ for event in self.__thing_events:
1778
+ post_topic = self.__thing_topic_event_post_pattern % (self.__product_key, self.__device_name, event)
1779
+ self.__thing_topic_event_post[event] = post_topic
1780
+ self.__thing_topic_event_post_reply.add(post_topic + "_reply")
1781
+ # service topic
1782
+ for service in self.__thing_services:
1783
+ self.__thing_topic_services.add(
1784
+ self.__thing_topic_service_pattern % (self.__product_key, self.__device_name, service)
1785
+ )
1786
+
1787
+ except Exception as e:
1788
+ self.__link_log.info("file open error:" + str(e))
1789
+ return 2
1790
+ self.__thing_setup_state = True
1791
+ return 0
1792
+
1793
+ def thing_raw_post_data(self, payload):
1794
+ if self.__linkkit_state is not LinkKit.LinkKitState.CONNECTED:
1795
+ self.__link_log.error("disconnected, post raw fail")
1796
+ return LinkKit.ErrorCode.NETWORK_DISCONNECTED.value
1797
+ with self.__thing_raw_up_mid_lock:
1798
+ rc, mid = self.__mqtt_client.publish(self.__thing_topic_raw_up, payload, 0)
1799
+ if rc == mqtt.MQTT_ERR_SUCCESS:
1800
+ self.__thing_raw_up_mid[mid] = self.__timestamp()
1801
+ return 0
1802
+ return 1
1803
+
1804
+ def thing_raw_data_reply(self, payload):
1805
+ if self.__linkkit_state is not LinkKit.LinkKitState.CONNECTED:
1806
+ self.__link_log.error("disconnected, raw data reply fail")
1807
+ return LinkKit.ErrorCode.NETWORK_DISCONNECTED.value
1808
+ with self.__thing_raw_down_reply_mid_lock:
1809
+ rc, mid = self.__mqtt_client.publish(self.__thing_topic_raw_down_reply, payload, 0)
1810
+ if rc == mqtt.MQTT_ERR_SUCCESS:
1811
+ self.__thing_raw_down_reply_mid[mid] = self.__timestamp()
1812
+ return 0
1813
+ return 1
1814
+
1815
+ def thing_update_device_info(self, payload):
1816
+ if self.__linkkit_state is not LinkKit.LinkKitState.CONNECTED:
1817
+ self.__link_log.error("disconnected, update device info fail")
1818
+ return LinkKit.ErrorCode.NETWORK_DISCONNECTED.value, None
1819
+ if not self.__thing_setup_state or not self.__thing_enable_state:
1820
+ raise LinkKit.StateError("not in SETUP & ENABLE state")
1821
+ return 1, None
1822
+ request_id = self.__get_thing_request_id()
1823
+ with self.__thing_update_device_info_up_mid_lock:
1824
+ rc, mid = self.__mqtt_client.publish(
1825
+ self.__thing_topic_update_device_info_up,
1826
+ self.__pack_alink_request(request_id, "thing.deviceinfo.update", payload),
1827
+ 0,
1828
+ )
1829
+ if rc == mqtt.MQTT_ERR_SUCCESS:
1830
+ self.__thing_update_device_info_up_mid[mid] = self.__timestamp()
1831
+ return rc, request_id
1832
+ return 1, None
1833
+
1834
+ def thing_delete_device_info(self, payload):
1835
+ if self.__linkkit_state is not LinkKit.LinkKitState.CONNECTED:
1836
+ self.__link_log.error("disconnected, delete device info fail")
1837
+ return LinkKit.ErrorCode.NETWORK_DISCONNECTED.value, None
1838
+ if not self.__thing_setup_state or not self.__thing_enable_state:
1839
+ return 1
1840
+ request_id = self.__get_thing_request_id()
1841
+ with self.__thing_delete_device_info_up_mid_lock:
1842
+ rc, mid = self.__mqtt_client.publish(
1843
+ self.__thing_topic_delete_device_info_up,
1844
+ self.__pack_alink_request(request_id, "thing.deviceinfo.delete", payload),
1845
+ 0,
1846
+ )
1847
+ if rc == mqtt.MQTT_ERR_SUCCESS:
1848
+ self.__thing_delete_device_info_up_mid[mid] = self.__timestamp()
1849
+ return rc, request_id
1850
+ return 1, None
1851
+
1852
+ def thing_update_tags(self, tagMap):
1853
+ if not isinstance(tagMap, dict):
1854
+ raise ValueError("tagMap must be a dictionary")
1855
+ return 1, None
1856
+
1857
+ payload = []
1858
+ for k, v in tagMap.items():
1859
+ payload.append({LinkKit.TAG_KEY: k, LinkKit.TAG_VALUE: v})
1860
+ return self.thing_update_device_info(payload)
1861
+
1862
+ def thing_remove_tags(self, tagKeys):
1863
+ if not isinstance(tagKeys, list) and not isinstance(tagKeys, tuple):
1864
+ raise ValueError("tagKeys must be a list or tuple")
1865
+ return 1, None
1866
+
1867
+ payload = []
1868
+ for tagKey in tagKeys:
1869
+ payload.append({LinkKit.TAG_KEY: tagKey})
1870
+ return self.thing_delete_device_info(payload)
1871
+
1872
+ def __pack_alink_request(self, request_id, method, params):
1873
+ request = {"id": request_id, "version": "1.0", "params": params, "method": method}
1874
+ return json.dumps(request)
1875
+
1876
+ def thing_answer_service(self, identifier, request_id, code, data=None):
1877
+ if self.__linkkit_state is not LinkKit.LinkKitState.CONNECTED:
1878
+ self.__link_log.error("disconnected, answer service fail")
1879
+ return LinkKit.ErrorCode.NETWORK_DISCONNECTED.value
1880
+ if not self.__thing_setup_state or not self.__thing_enable_state:
1881
+ return 1
1882
+ if data is None:
1883
+ data = {}
1884
+ response = {"id": request_id, "code": code, "data": data}
1885
+
1886
+ item = self.__pop_rrpc_service("alink_" + str(request_id))
1887
+ if item:
1888
+ service_reply_topic = item["topic"]
1889
+ else:
1890
+ service_reply_topic = self.__thing_topic_service_pattern % (
1891
+ self.__product_key,
1892
+ self.__device_name,
1893
+ identifier + "_reply",
1894
+ )
1895
+ with self.__thing_answer_service_mid_lock:
1896
+ rc, mid = self.__mqtt_client.publish(service_reply_topic, json.dumps(response), 0)
1897
+ if rc == mqtt.MQTT_ERR_SUCCESS:
1898
+ self.__thing_answer_service_mid[mid] = self.__timestamp()
1899
+ return 0
1900
+ return 1
1901
+
1902
+ def __get_thing_request_id(self):
1903
+ with self.__thing_request_id_lock:
1904
+ self.__thing_request_value += 1
1905
+ if self.__thing_request_value > self.__thing_request_id_max:
1906
+ self.__thing_request_value = 0
1907
+ if len(self.__thing_request_id) > self.__mqtt_max_queued_message:
1908
+ return None
1909
+ if self.__thing_request_value not in self.__thing_request_id:
1910
+ self.__thing_request_id[self.__thing_request_value] = self.__timestamp()
1911
+ self.__link_log.debug("__get_thing_request_id pop:%r" % self.__thing_request_value)
1912
+ return str(self.__thing_request_value)
1913
+ return None
1914
+
1915
+ def __back_thing_request_id(self, post_id) -> None:
1916
+ with self.__thing_request_id_lock:
1917
+ try:
1918
+ self.__thing_request_id.pop(int(post_id))
1919
+ except Exception as e:
1920
+ self.__link_log.error("__back_thing_request_id pop:%r,%r" % (post_id, e))
1921
+
1922
+ def __reset_thing_request_id(self) -> None:
1923
+ with self.__thing_request_id_lock:
1924
+ self.__thing_request_value = 0
1925
+ self.__thing_request_id.clear()
1926
+
1927
+ def __clean_thing_timeout_request_id(self) -> None:
1928
+ with self.__thing_request_id_lock:
1929
+ expire_timestamp = self.__timestamp() - self.__mqtt_request_timeout * 1000
1930
+ for request_id in list(self.__thing_request_id.keys()):
1931
+ if self.__thing_request_id[request_id] < expire_timestamp:
1932
+ timestamp = self.__thing_request_id.pop(request_id)
1933
+ self.__link_log.error("__clean_thing_timeout_request_id pop:%r,timestamp:%r", request_id, timestamp)
1934
+
1935
+ def thing_trigger_event(self, event_tuple):
1936
+ if self.__linkkit_state is not LinkKit.LinkKitState.CONNECTED:
1937
+ self.__link_log.error("disconnected, trigger event fail")
1938
+ return LinkKit.ErrorCode.NETWORK_DISCONNECTED.value, None
1939
+ if not self.__thing_setup_state or not self.__thing_enable_state:
1940
+ return 1, None
1941
+ if isinstance(event_tuple, tuple):
1942
+ event, params = event_tuple
1943
+ else:
1944
+ return 1, None
1945
+ if event not in self.__thing_topic_event_post.keys():
1946
+ return 1, None
1947
+ request_id = self.__get_thing_request_id()
1948
+ if request_id is None:
1949
+ return 1
1950
+ request = {
1951
+ "id": request_id,
1952
+ "version": "1.0",
1953
+ "params": {
1954
+ "value": params,
1955
+ },
1956
+ "method": "thing.event.%s.post" % event,
1957
+ }
1958
+ with self.__thing_event_post_mid_lock:
1959
+ event_topic = self.__thing_topic_event_post[event]
1960
+ self.__link_log.debug("thing_trigger_event publish topic")
1961
+ rc, mid = self.__mqtt_client.publish(event_topic, json.dumps(request), 0)
1962
+ self.__link_log.debug("thing_trigger_event publish done")
1963
+ if rc == mqtt.MQTT_ERR_SUCCESS:
1964
+ self.__thing_event_post_mid[mid] = self.__timestamp()
1965
+ return 0, request_id
1966
+ else:
1967
+ return 1, None
1968
+
1969
+ def thing_post_property(self, property_data):
1970
+ if self.__linkkit_state is not LinkKit.LinkKitState.CONNECTED:
1971
+ self.__link_log.error("disconnected, post property fail")
1972
+ return LinkKit.ErrorCode.NETWORK_DISCONNECTED.value, None
1973
+ if not self.__thing_setup_state or not self.__thing_enable_state:
1974
+ return 1, None
1975
+ request_params = property_data
1976
+ request_id = self.__get_thing_request_id()
1977
+ if request_id is None:
1978
+ return 1, None
1979
+ request = {"id": request_id, "version": "1.0", "params": request_params, "method": "thing.event.property.post"}
1980
+ with self.__thing_prop_post_mid_lock:
1981
+ rc, mid = self.__mqtt_client.publish(self.__thing_topic_prop_post, json.dumps(request), 1)
1982
+ if rc == mqtt.MQTT_ERR_SUCCESS:
1983
+ self.__thing_prop_post_mid[mid] = self.__timestamp()
1984
+ return 0, request_id
1985
+ else:
1986
+ return 1, None
1987
+
1988
+ def __on_internal_async_message(self, message) -> None:
1989
+ self.__link_log.debug("__on_internal_async_message topic:%r" % message.topic)
1990
+ triggered_flag = 0
1991
+
1992
+ if message.topic == self.__thing_topic_prop_set:
1993
+ payload = self.__load_json(message.payload)
1994
+ params = payload["params"]
1995
+ try:
1996
+ reply = {"id": payload["id"], "code": 200, "data": {}}
1997
+ with self.__thing_prop_set_reply_mid_lock:
1998
+ rc, mid = self.__mqtt_client.publish(self.__thing_topic_prop_set_reply, json.dumps(reply), 1)
1999
+ if rc == 0:
2000
+ self.__link_log.info("prop changed reply success,mid:%d" % mid)
2001
+ self.__thing_prop_set_reply_mid[mid] = self.__timestamp()
2002
+ self.__link_log.info("prop changed reply success")
2003
+ else:
2004
+ self.__link_log.info("prop changed reply fail")
2005
+ if self.__on_thing_prop_changed is not None:
2006
+ triggered_flag = 1
2007
+ self.__on_thing_prop_changed(params, self.__user_data)
2008
+ except Exception as e:
2009
+ self.__link_log.error("on_thing_prop_changed raise exception:%s" % e)
2010
+ elif message.topic == self.__device_info_topic_reply:
2011
+ payload = self.__load_json(message.payload)
2012
+ request_id = payload["id"]
2013
+ code = payload["code"]
2014
+ reply_message = payload["message"]
2015
+ data = payload["data"]
2016
+ self.__back_thing_request_id(request_id)
2017
+ if code != 200:
2018
+ self.__link_log.error("upload device info reply error:%s" % reply_message)
2019
+ try:
2020
+ if self.__on_thing_device_info_update is not None:
2021
+ triggered_flag = 1
2022
+ self.__on_thing_device_info_update(request_id, code, data, reply_message, self.__user_data)
2023
+ except Exception as e:
2024
+ self.__link_log.error("__on_thing_device_info_update process raise exception:%s" % e)
2025
+ elif message.topic == self.__thing_topic_prop_post_reply:
2026
+ payload = self.__load_json(message.payload)
2027
+ request_id = payload["id"]
2028
+ code = payload["code"]
2029
+ data = payload["data"]
2030
+ reply_message = payload["message"]
2031
+ try:
2032
+ if self.__on_thing_prop_post is not None:
2033
+ triggered_flag = 1
2034
+ self.__on_thing_prop_post(request_id, code, data, reply_message, self.__user_data)
2035
+ except Exception as e:
2036
+ self.__link_log.error("on_thing_prop_post raise exception:%s" % e)
2037
+ self.__back_thing_request_id(request_id)
2038
+ elif message.topic == self.__thing_topic_prop_get:
2039
+ pass
2040
+ elif message.topic in self.__thing_topic_event_post_reply:
2041
+ event = message.topic.split("/", 7)[6]
2042
+ payload = self.__load_json(message.payload)
2043
+ request_id = payload["id"]
2044
+ code = payload["code"]
2045
+ data = payload["data"]
2046
+ reply_message = payload["message"]
2047
+ self.__link_log.info("on_thing_event_post message:%s" % reply_message)
2048
+ try:
2049
+ if self.on_thing_event_post is not None:
2050
+ triggered_flag = 1
2051
+ self.on_thing_event_post(event, request_id, code, data, reply_message, self.__user_data)
2052
+ except Exception as e:
2053
+ self.__link_log.error("on_thing_event_post raise exception:%s" % e)
2054
+ self.__back_thing_request_id(request_id)
2055
+ elif message.topic in self.__thing_topic_services:
2056
+ identifier = message.topic.split("/", 6)[6]
2057
+ payload = self.__load_json(message.payload)
2058
+ try:
2059
+ request_id = payload["id"]
2060
+ params = payload["params"]
2061
+ if self.__on_thing_call_service is not None:
2062
+ triggered_flag = 1
2063
+ self.__on_thing_call_service(identifier, request_id, params, self.__user_data)
2064
+ except Exception as e:
2065
+ self.__link_log.error("on_thing_call_service raise exception: %s" % e)
2066
+ elif message.topic == self.__thing_topic_raw_down:
2067
+ try:
2068
+ if self.__on_thing_raw_data_arrived is not None:
2069
+ triggered_flag = 1
2070
+ self.__on_thing_raw_data_arrived(message.payload, self.__user_data)
2071
+ except Exception as e:
2072
+ self.__link_log.error("on_thing_raw_data_arrived process raise exception:%s" % e)
2073
+ elif message.topic == self.__thing_topic_raw_up_reply:
2074
+ try:
2075
+ if self.__on_thing_raw_data_post is not None:
2076
+ triggered_flag = 1
2077
+ self.__on_thing_raw_data_post(message.payload, self.__user_data)
2078
+ except Exception as e:
2079
+ self.__link_log.error("on_thing_raw_post_data process raise exception:%s" % e)
2080
+ elif message.topic == self.__thing_topic_update_device_info_reply:
2081
+ try:
2082
+ if self.__on_thing_device_info_update is not None:
2083
+ triggered_flag = 1
2084
+ payload = self.__load_json(message.payload)
2085
+ request_id = payload["id"]
2086
+ code = payload["code"]
2087
+ data = payload["data"]
2088
+ msg = payload["message"]
2089
+ self.__on_thing_device_info_update(request_id, code, data, msg, self.__user_data)
2090
+ except Exception as e:
2091
+ self.__link_log.error("__on_thing_device_info_update process raise exception:%s" % e)
2092
+ elif message.topic == self.__thing_topic_delete_device_info_reply:
2093
+ try:
2094
+ if self.__on_thing_device_info_delete is not None:
2095
+ triggered_flag = 1
2096
+ payload = self.__load_json(message.payload)
2097
+ request_id = payload["id"]
2098
+ code = payload["code"]
2099
+ data = payload["data"]
2100
+ msg = payload["message"]
2101
+ self.__on_thing_device_info_delete(request_id, code, data, msg, self.__user_data)
2102
+ except Exception as e:
2103
+ self.__link_log.error("__on_thing_device_info_update process raise exception:%s" % e)
2104
+ elif message.topic == self.__thing_topic_shadow_get:
2105
+ self.__try_parse_try_shadow(message.payload)
2106
+ try:
2107
+ if self.__on_thing_shadow_get is not None:
2108
+ triggered_flag = 1
2109
+ self.__on_thing_shadow_get(self.__load_json(message.payload), self.__user_data)
2110
+ except Exception as e:
2111
+ self.__link_log.error("__on_thing_shadow_get process raise exception:%s" % e)
2112
+ elif message.topic.startswith("/ext/rrpc/"):
2113
+ triggered_flag = self.__try_parse_rrpc_topic(message)
2114
+ elif message.topic == self.__gateway_topic_topo_change:
2115
+ try:
2116
+ payload = self.__load_json(message.payload)
2117
+ request_id = payload["id"]
2118
+ params = payload["params"]
2119
+ if self.__on_gateway_topo_change is not None:
2120
+ triggered_flag = 1
2121
+ self.__on_gateway_topo_change(request_id, params, self.__user_data)
2122
+ except Exception as e:
2123
+ self.__link_log.error("__on_gateway_topo_change process raise exception:%s" % e)
2124
+ elif message.topic == self.__gateway_topic_add_subdev_topo_reply:
2125
+ try:
2126
+ payload = self.__load_json(message.payload)
2127
+ request_id = payload["id"]
2128
+ code = payload["code"]
2129
+ data = payload["data"]
2130
+ msg = payload["message"]
2131
+ self.__back_thing_request_id(request_id)
2132
+ if self.__on_gateway_add_subdev_topo_reply is not None:
2133
+ triggered_flag = 1
2134
+ self.__on_gateway_add_subdev_topo_reply(request_id, code, data, msg, self.__user_data)
2135
+ except Exception as e:
2136
+ self.__link_log.error("__on_gateway_add_subdev_topo_reply process raise exception:%s" % e)
2137
+ elif message.topic == self.__gateway_topic_delete_subdev_topo_reply:
2138
+ try:
2139
+ payload = self.__load_json(message.payload)
2140
+ request_id = payload["id"]
2141
+ code = payload["code"]
2142
+ data = payload["data"]
2143
+ msg = payload["message"]
2144
+ self.__back_thing_request_id(request_id)
2145
+ if self.__on_gateway_delete_subdev_topo_reply is not None:
2146
+ triggered_flag = 1
2147
+ self.__on_gateway_delete_subdev_topo_reply(request_id, code, data, msg, self.__user_data)
2148
+ except Exception as e:
2149
+ self.__link_log.error("__on_gateway_delete_subdev_topo_reply process raise exception:%s" % e)
2150
+ elif message.topic == self.__gateway_topic_login_subdev_reply:
2151
+ try:
2152
+ payload = self.__load_json(message.payload)
2153
+ request_id = payload["id"]
2154
+ code = payload["code"]
2155
+ data = payload["data"]
2156
+ msg = payload["message"]
2157
+ self.__back_thing_request_id(request_id)
2158
+ if self.__on_gateway_login_subdev_reply is not None:
2159
+ triggered_flag = 1
2160
+ self.__on_gateway_login_subdev_reply(request_id, code, data, msg, self.__user_data)
2161
+ except Exception as e:
2162
+ self.__link_log.error("__on_gateway_login_subdev_reply process raise exception:%s" % e)
2163
+ elif message.topic == self.__gateway_topic_logout_subdev_reply:
2164
+ try:
2165
+ payload = self.__load_json(message.payload)
2166
+ request_id = payload["id"]
2167
+ code = payload["code"]
2168
+ data = payload["data"]
2169
+ msg = payload["message"]
2170
+ self.__back_thing_request_id(request_id)
2171
+ if self.__on_gateway_logout_subdev_reply is not None:
2172
+ triggered_flag = 1
2173
+ self.__on_gateway_logout_subdev_reply(request_id, code, data, msg, self.__user_data)
2174
+ except Exception as e:
2175
+ self.__link_log.error("__on_gateway_logout_subdev_reply process raise exception:%s" % e)
2176
+ elif message.topic == self.__gateway_topic_register_subdev_reply:
2177
+ try:
2178
+ payload = self.__load_json(message.payload)
2179
+ request_id = payload["id"]
2180
+ code = payload["code"]
2181
+ data = payload["data"]
2182
+ msg = payload["message"]
2183
+ self.__back_thing_request_id(request_id)
2184
+ if self.__on_gateway_register_subdev_reply is not None:
2185
+ triggered_flag = 1
2186
+ self.__on_gateway_register_subdev_reply(request_id, code, data, msg, self.__user_data)
2187
+ except Exception as e:
2188
+ self.__link_log.error("__on_gateway_register_subdev_reply process raise exception:%s" % e)
2189
+ elif message.topic == self.__gateway_topic_product_register_subdev_reply:
2190
+ try:
2191
+ payload = self.__load_json(message.payload)
2192
+ request_id = payload["id"]
2193
+ code = payload["code"]
2194
+ data = payload["data"]
2195
+ msg = payload["message"]
2196
+ self.__back_thing_request_id(request_id)
2197
+ if self.__on_gateway_product_register_subdev_reply is not None:
2198
+ triggered_flag = 1
2199
+ self.__on_gateway_product_register_subdev_reply(request_id, code, data, msg, self.__user_data)
2200
+ except Exception as e:
2201
+ self.__link_log.error("__on_gateway_product_register_subdev_reply process raise exception:%s" % e)
2202
+ elif message.topic == self.__dynamic_register_topic:
2203
+ try:
2204
+ payload = self.__load_json(message.payload)
2205
+ device_secret = payload["deviceSecret"]
2206
+ self.disconnect()
2207
+ self.__dynamic_register_flag = 0
2208
+ if self.__on_device_dynamic_register is not None:
2209
+ triggered_flag = 1
2210
+ self.__on_device_dynamic_register(LinkKit.ErrorCode.SUCCESS.value, device_secret, None)
2211
+ except Exception as e:
2212
+ self.__link_log.error("__on_device_dynamic_register process raise exception:%s" % e)
2213
+
2214
+ elif message.topic == self.__dynamic_register_nwl_topic:
2215
+ # 一型一密免动态注册获取到username和token
2216
+ try:
2217
+ payload = self.__load_json(message.payload)
2218
+ client_id = payload["clientId"]
2219
+ client_id = client_id + "|authType=connwl,securemode=-2,_ss=1,ext=3,lan=%s,_v=%s|" % (
2220
+ self.__sdk_program_language,
2221
+ self.__sdk_version,
2222
+ )
2223
+ product_key = payload["productKey"]
2224
+ device_name = payload["deviceName"]
2225
+ username = device_name + "&" + product_key
2226
+ password = payload["deviceToken"]
2227
+ self.disconnect()
2228
+ self.__dynamic_register_nwl_flag = 0
2229
+ if self.__on_device_dynamic_register_nwl_reply is not None:
2230
+ triggered_flag = 1
2231
+ self.__on_device_dynamic_register_nwl_reply(
2232
+ LinkKit.ErrorCode.SUCCESS.value, client_id, username, password
2233
+ )
2234
+ except Exception as e:
2235
+ self.__link_log.error("__on_device_dynamic_register_nwl_reply process raise exception:%s" % e)
2236
+ elif message.topic == self.__ota_push_topic or message.topic == self.__ota_pull_reply_topic:
2237
+ try:
2238
+ payload = json.loads(self.__to_str(message.payload))
2239
+ data = payload.setdefault("data", "")
2240
+
2241
+ json_data = json.dumps(data)
2242
+ download_info = json.loads(str(json_data))
2243
+
2244
+ url = download_info.setdefault("url", "")
2245
+ version = download_info.setdefault("version", "")
2246
+ size = download_info.setdefault("size", "")
2247
+ sign_method = download_info.setdefault("signMethod", "")
2248
+ sign = download_info.setdefault("sign", "")
2249
+ extra = download_info.setdefault("extData", "")
2250
+ module = download_info.setdefault("module", "default")
2251
+
2252
+ if (
2253
+ not self.__is_valid_str(url)
2254
+ or not self.__is_valid_str(version)
2255
+ or not self.__is_valid_str(size)
2256
+ or not self.__is_valid_str(sign_method)
2257
+ or not self.__is_valid_str(sign)
2258
+ ):
2259
+ self.__link_log.error("invalid download params")
2260
+ return
2261
+
2262
+ ota_notice_type = 0
2263
+ if message.topic == self.__ota_push_topic:
2264
+ ota_notice_type = 1
2265
+ else:
2266
+ ota_notice_type = 2
2267
+
2268
+ if self.__on_ota_message_arrived is not None:
2269
+ triggered_flag = 1
2270
+ self.__on_ota_message_arrived(
2271
+ ota_notice_type, version, size, url, sign_method, sign, module, str(extra)
2272
+ )
2273
+ except Exception as e:
2274
+ self.__link_log.error("__on_ota_message_arrived process raise exception:%s" % e)
2275
+
2276
+ if triggered_flag == 1:
2277
+ return
2278
+
2279
+ if self.__on_topic_message is not None:
2280
+ try:
2281
+ self.__on_topic_message(message.topic, message.payload, message.qos, self.__user_data)
2282
+ except Exception as e:
2283
+ self.__link_log.error("on_topic_message process raise exception:%s" % e)
2284
+ else:
2285
+ self.__link_log.error("receive unscubscibe topic : %s" % message.topic)
2286
+
2287
+ def query_ota_firmware(self, module=None):
2288
+ request_id = self.__get_thing_request_id()
2289
+ if request_id is None:
2290
+ request_id = "1"
2291
+
2292
+ if self.__is_valid_str(module):
2293
+ payload = '{"id": %s, "params": {"module": "%s"}}' % (request_id, module)
2294
+ else:
2295
+ payload = '{"id": %s, "params": {}}' % request_id
2296
+
2297
+ rc = self.__mqtt_client.publish(self.__ota_pull_topic, payload, 0)
2298
+ if rc == mqtt.MQTT_ERR_SUCCESS:
2299
+ return 0
2300
+ else:
2301
+ return LinkKit.ErrorCode.OTA_PUB_FAILED
2302
+
2303
+ def __cal_file_sign(self, filename, sign_method):
2304
+ if sign_method == "Md5":
2305
+ hash_tool = hashlib.md5()
2306
+ elif sign_method == "SHA256":
2307
+ hash_tool = hashlib.sha256()
2308
+ else:
2309
+ return LinkKit.ErrorCode.OTA_INVALID_SIGN_METHOD, -1
2310
+
2311
+ with open(filename, "rb") as f:
2312
+ while True:
2313
+ chunk = f.read(4096)
2314
+ if not chunk:
2315
+ break
2316
+ hash_tool.update(chunk)
2317
+ return LinkKit.ErrorCode.SUCCESS, hash_tool.hexdigest()
2318
+
2319
+ def ota_report_version(self, module, version):
2320
+ if not self.__is_valid_str(version):
2321
+ return LinkKit.ErrorCode.OTA_INVALID_PARAM
2322
+
2323
+ request_id = "1"
2324
+ if self.__is_valid_str(module):
2325
+ payload = '{"id": %s,"params": {"version":"%s","module": "%s"}}' % (request_id, version, module)
2326
+ else:
2327
+ payload = '{"id": %s, "params": {"version": "%s"}}' % (request_id, version)
2328
+
2329
+ rc = self.__mqtt_client.publish(self.__ota_report_version_topic, payload, 0)
2330
+ if rc == mqtt.MQTT_ERR_SUCCESS:
2331
+ return LinkKit.ErrorCode.SUCCESS
2332
+ else:
2333
+ return LinkKit.ErrorCode.OTA_PUB_FAILED
2334
+
2335
+ def download_ota_firmware(self, url, local_path, sign_method, sign, download_step=10 * 1024):
2336
+ if (
2337
+ not self.__is_valid_str(url)
2338
+ or not self.__is_valid_str(local_path)
2339
+ or not self.__is_valid_str(sign_method)
2340
+ or not self.__is_valid_str(sign)
2341
+ ):
2342
+ return LinkKit.ErrorCode.OTA_INVALID_PARAM
2343
+
2344
+ context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cadata=self.__aliyun_broker_ca_data)
2345
+
2346
+ try:
2347
+ conn = urllib.request.urlopen(url, context=context)
2348
+ except Exception as e:
2349
+ # Return code error (e.g. 404, 501, ...)
2350
+ self.__link_log.error("HTTPError: {} %s" % e)
2351
+ return LinkKit.ErrorCode.OTA_INVALID_URL
2352
+ else:
2353
+ # 200
2354
+ self.__link_log.info("https return 200")
2355
+
2356
+ try:
2357
+ file = open(local_path, "wb")
2358
+ except OSError as e:
2359
+ self.__link_log.error("open file error: {}" + e.filename)
2360
+ return LinkKit.ErrorCode.OTA_INVALID_PATH
2361
+ else:
2362
+ # Download ota file
2363
+ with file:
2364
+ while True:
2365
+ try:
2366
+ data = conn.read(download_step)
2367
+ except Exception as e:
2368
+ self.__link_log.error("download exception %s" % e)
2369
+ return LinkKit.ErrorCode.OTA_DOWNLOAD_FAIL
2370
+ else:
2371
+ if len(data) <= 0:
2372
+ break
2373
+ else:
2374
+ file.write(data)
2375
+
2376
+ # Compare checksum
2377
+ ret, firmware_sign = self.__cal_file_sign(local_path, sign_method)
2378
+ if ret == LinkKit.ErrorCode.SUCCESS and firmware_sign == sign:
2379
+ self.__link_log.info("sign match")
2380
+ return LinkKit.ErrorCode.SUCCESS
2381
+ else:
2382
+ self.__link_log.error("sign mismatch, expect:" + firmware_sign + ", actually:" + sign)
2383
+ return LinkKit.ErrorCode.OTA_DIGEST_MISMATCH
2384
+
2385
+ def __parse_raw_topic(self, topic):
2386
+ return re.search("/ext/rrpc/.*?(/.*)", topic).group(1)
2387
+
2388
+ def __tidy_topic(self, topic):
2389
+ if topic == None:
2390
+ return None
2391
+ topic = topic.strip()
2392
+ if len(topic) == 0:
2393
+ return None
2394
+ if topic[0] != "/":
2395
+ topic = "/" + topic
2396
+ return topic
2397
+
2398
+ def __push_rrpc_service(self, item) -> None:
2399
+ with self.__user_rrpc_request_ids_lock:
2400
+ if len(self.__user_rrpc_request_ids) > self.__user_rrpc_request_max_len:
2401
+ removed_item = self.__user_rrpc_request_ids.pop(0)
2402
+ del self.__user_rrpc_request_id_index_map[removed_item["id"]]
2403
+
2404
+ self.__user_rrpc_request_ids.append(item)
2405
+ self.__user_rrpc_request_id_index_map[item["id"]] = 0
2406
+
2407
+ def __pop_rrpc_service(self, id):
2408
+ with self.__user_rrpc_request_ids_lock:
2409
+ if id not in self.__user_rrpc_request_id_index_map:
2410
+ return None
2411
+ del self.__user_rrpc_request_id_index_map[id]
2412
+ for index in range(len(self.__user_rrpc_request_ids)):
2413
+ item = self.__user_rrpc_request_ids[index]
2414
+ if item["id"] == id:
2415
+ del self.__user_rrpc_request_ids[index]
2416
+ return item
2417
+ return None
2418
+
2419
+ def thing_answer_rrpc(self, id, response):
2420
+ item = self.__pop_rrpc_service("rrpc_" + id)
2421
+ if item == None:
2422
+ self.__link_log.error("answer_rrpc_topic, the id does not exist: %s" % id)
2423
+ return 1, None
2424
+ rc, mid = self.__mqtt_client.publish(item["topic"], response, 0)
2425
+ self.__link_log.debug("reply topic:%s" % item["topic"])
2426
+ return rc, mid
2427
+
2428
+ def __try_parse_rrpc_topic(self, message):
2429
+ self.__link_log.debug("receive a rrpc topic:%s" % message.topic)
2430
+ raw_topic = self.__parse_raw_topic(message.topic)
2431
+ triggered = 0
2432
+ # if it is a service, log it...
2433
+ if raw_topic.startswith("/sys") and raw_topic in self.__thing_topic_services:
2434
+ identifier = raw_topic.split("/", 6)[6]
2435
+ payload = self.__load_json(self.__to_str(message.payload))
2436
+ try:
2437
+ request_id = payload["id"]
2438
+ params = payload["params"]
2439
+ item_id = "alink_" + request_id
2440
+ item = {
2441
+ "id": item_id,
2442
+ "request_id": request_id,
2443
+ "payload": payload,
2444
+ "identifier": identifier,
2445
+ "topic": message.topic,
2446
+ }
2447
+ self.__push_rrpc_service(item)
2448
+ if self.__on_thing_call_service is not None:
2449
+ triggered = 1
2450
+ self.__on_thing_call_service(identifier, request_id, params, self.__user_data)
2451
+ except Exception as e:
2452
+ self.__link_log.error("on_thing_call_service raise exception: %s" % e)
2453
+ return triggered
2454
+
2455
+ # parse
2456
+ with self.__user_rrpc_topics_subscribe_request_lock:
2457
+ with self.__user_rrpc_topics_lock:
2458
+ if raw_topic not in self.__user_rrpc_topics:
2459
+ self.__link_log.error("%s is not in the rrpc-subscribed list" % raw_topic)
2460
+ return
2461
+ if not self.__on_topic_rrpc_message:
2462
+ return
2463
+ try:
2464
+ rrpc_id = message.topic.split("/", 4)[3]
2465
+ item_id = "rrpc_" + rrpc_id
2466
+ item = {"id": item_id, "payload": message.payload, "topic": message.topic}
2467
+ self.__push_rrpc_service(item)
2468
+ self.__on_topic_rrpc_message(rrpc_id, message.topic, message.payload, message.qos, self.__user_data)
2469
+ # self.__mqtt_client.publish(message.topic, response, 0)
2470
+ # self.__link_log.debug('reply topic:%s' % message.topic)
2471
+ except Exception as e:
2472
+ self.__link_log.error("on_topic_rrpc_message process raise exception:%r" % e)
2473
+
2474
+ def __try_parse_try_shadow(self, payload) -> None:
2475
+ try:
2476
+ self.__latest_shadow.set_latest_recevied_time(self.__timestamp())
2477
+ self.__latest_shadow.set_latest_recevied_payload(payload)
2478
+
2479
+ # parse the pay load
2480
+ msg = self.__load_json(payload)
2481
+ # set version
2482
+ if "version" in msg:
2483
+ self.__latest_shadow.set_version(msg["version"])
2484
+ elif "payload" in msg and "version" in msg["payload"]:
2485
+ self.__latest_shadow.set_version(msg["payload"]["version"])
2486
+
2487
+ # set timestamp
2488
+ if "timestamp" in msg:
2489
+ self.__latest_shadow.set_timestamp(msg["timestamp"])
2490
+ elif "payload" in msg and "timestamp" in msg["payload"]:
2491
+ self.__latest_shadow.set_timestamp(msg["payload"]["timestamp"])
2492
+
2493
+ # set state and metadata
2494
+ if "payload" in msg and msg["payload"]["status"] == "success":
2495
+ if "state" in msg["payload"]:
2496
+ self.__latest_shadow.set_state(msg["payload"]["state"])
2497
+ if "metadata" in msg["payload"]:
2498
+ self.__latest_shadow.set_metadata(msg["payload"]["metadata"])
2499
+ except Exception:
2500
+ pass
2501
+
2502
+ def thing_update_shadow(self, reported, version):
2503
+ request = {"state": {"reported": reported}, "method": "update", "version": version}
2504
+ return self.__thing_update_shadow(request)
2505
+
2506
+ def thing_get_shadow(self):
2507
+ request = {"method": "get"}
2508
+ return self.__thing_update_shadow(request)
2509
+
2510
+ def local_get_latest_shadow(self):
2511
+ return self.__latest_shadow
2512
+
2513
+ def __thing_update_shadow(self, request):
2514
+ if self.__linkkit_state is not LinkKit.LinkKitState.CONNECTED:
2515
+ self.__link_log.error("disconnected, update shadow fail")
2516
+ return LinkKit.ErrorCode.NETWORK_DISCONNECTED.value, None
2517
+ if not self.__thing_setup_state or not self.__thing_enable_state:
2518
+ return 1, None
2519
+ with self.__thing_shadow_mid_lock:
2520
+ rc, mid = self.__mqtt_client.publish(self.__thing_topic_shadow_update, json.dumps(request), 1)
2521
+ if rc == mqtt.MQTT_ERR_SUCCESS:
2522
+ self.__thing_shadow_mid[mid] = self.__timestamp()
2523
+ return 0, mid
2524
+ else:
2525
+ return 1, None
2526
+
2527
+ def __on_internal_message(self, client, user_data, message) -> None:
2528
+ self.__link_log.info("__on_internal_message")
2529
+ self.__handler_task.post_message(self.__handler_task_cmd_on_message, (client, user_data, message))
2530
+ # self.__worker_thread.async_post_message(message)
2531
+
2532
+ def __handler_task_on_message_callback(self, value) -> None:
2533
+ client, user_data, message = value
2534
+ self.__on_internal_async_message(message)
2535
+
2536
+ def __on_internal_connect(self, client, user_data, session_flag, rc) -> None:
2537
+ self.__link_log.info("__on_internal_connect")
2538
+ if rc == 0:
2539
+ self.__reset_reconnect_wait()
2540
+ # self.__upload_device_interface_info()
2541
+ self.__handler_task.post_message(self.__handler_task_cmd_on_connect, (client, user_data, session_flag, rc))
2542
+
2543
+ def __handler_task_on_connect_callback(self, value) -> None:
2544
+ client, user_data, session_flag, rc = value
2545
+ self.__link_log.info("__on_internal_connect enter")
2546
+ self.__link_log.debug("session:%d, return code:%d" % (session_flag["session present"], rc))
2547
+ if rc == 0:
2548
+ self.__linkkit_state = LinkKit.LinkKitState.CONNECTED
2549
+ # self.__worker_thread.start()
2550
+ if self.__on_connect is not None:
2551
+ try:
2552
+ self.__on_connect(session_flag["session present"], rc, self.__user_data)
2553
+ except Exception as e:
2554
+ self.__link_log.error("on_connect process raise exception:%r" % e)
2555
+ if self.__thing_setup_state:
2556
+ self.__thing_enable_state = True
2557
+ if self.__on_thing_enable:
2558
+ self.__on_thing_enable(self.__user_data)
2559
+
2560
+ def __on_internal_disconnect(self, client, user_data, rc) -> None:
2561
+ self.__link_log.info("__on_internal_disconnect enter")
2562
+ if self.__linkkit_state == LinkKit.LinkKitState.DESTRUCTING:
2563
+ self.__linkkit_state = LinkKit.LinkKitState.DESTRUCTED
2564
+ elif (
2565
+ self.__linkkit_state == LinkKit.LinkKitState.DISCONNECTING
2566
+ or self.__linkkit_state == LinkKit.LinkKitState.CONNECTED
2567
+ ):
2568
+ self.__linkkit_state = LinkKit.LinkKitState.DISCONNECTED
2569
+ elif self.__linkkit_state == LinkKit.LinkKitState.DISCONNECTED:
2570
+ self.__link_log.error("__on_internal_disconnect enter from wrong state:%r" % self.__linkkit_state)
2571
+ return
2572
+ else:
2573
+ self.__link_log.error("__on_internal_disconnect enter from wrong state:%r" % self.__linkkit_state)
2574
+
2575
+ return
2576
+ self.__user_topics.clear()
2577
+ self.__user_topics_subscribe_request.clear()
2578
+ self.__user_topics_unsubscribe_request.clear()
2579
+
2580
+ self.__user_rrpc_topics.clear()
2581
+ self.__user_rrpc_topics_subscribe_request.clear()
2582
+ self.__user_rrpc_topics_unsubscribe_request.clear()
2583
+
2584
+ self.__thing_prop_post_mid.clear()
2585
+ self.__thing_event_post_mid.clear()
2586
+ self.__thing_answer_service_mid.clear()
2587
+ self.__thing_raw_down_reply_mid.clear()
2588
+ self.__thing_raw_up_mid.clear()
2589
+ self.__thing_shadow_mid.clear()
2590
+ self.__device_info_mid.clear()
2591
+ self.__thing_update_device_info_up_mid.clear()
2592
+ self.__thing_delete_device_info_up_mid.clear()
2593
+ self.__handler_task.post_message(self.__handler_task_cmd_on_disconnect, (client, user_data, rc))
2594
+ if self.__linkkit_state == LinkKit.LinkKitState.DESTRUCTED:
2595
+ self.__handler_task.stop()
2596
+
2597
+ def __handler_task_on_disconnect_callback(self, value) -> None:
2598
+ self.__link_log.info("__handler_task_on_disconnect_callback enter")
2599
+ client, user_data, rc = value
2600
+ if self.__thing_setup_state:
2601
+ if self.__thing_enable_state:
2602
+ self.__thing_enable_state = False
2603
+ if self.__on_thing_disable is not None:
2604
+ try:
2605
+ self.__on_thing_disable(self.__user_data)
2606
+ except Exception as e:
2607
+ self.__link_log.error("on_thing_disable process raise exception:%r" % e)
2608
+ if self.__on_disconnect is not None:
2609
+ try:
2610
+ self.__on_disconnect(rc, self.__user_data)
2611
+ except Exception as e:
2612
+ self.__link_log.error("on_disconnect process raise exception:%r" % e)
2613
+
2614
+ def __on_internal_publish(self, client, user_data, mid) -> None:
2615
+ self.__handler_task.post_message(self.__handler_task_cmd_on_publish, (client, user_data, mid))
2616
+
2617
+ def __gateway_add_subdev_topo(self, subdev_array):
2618
+ request_params = []
2619
+ for subdev in subdev_array:
2620
+ ret = self.__validate_subdev_param(subdev, 3)
2621
+ if ret != 0:
2622
+ return ret, None
2623
+
2624
+ pk = subdev[0]
2625
+ dn = subdev[1]
2626
+ ds = subdev[2]
2627
+ millis = str(self.__timestamp())
2628
+ client_id = pk + "." + dn
2629
+
2630
+ sign_content = "clientId%sdeviceName%sproductKey%stimestamp%s" % (client_id, dn, pk, millis)
2631
+ sign = hmac.new(ds.encode("utf-8"), sign_content.encode("utf-8"), hashlib.sha256).hexdigest()
2632
+ params = {
2633
+ "productKey": pk,
2634
+ "deviceName": dn,
2635
+ "clientId": client_id,
2636
+ "timestamp": millis,
2637
+ "signmethod": "hmacSha256",
2638
+ "sign": sign,
2639
+ }
2640
+ request_params.append(params)
2641
+ request_id = self.__get_thing_request_id()
2642
+ if request_id is None:
2643
+ return 1, None
2644
+ request = {
2645
+ "id": request_id,
2646
+ "version": "1.0",
2647
+ "params": request_params,
2648
+ }
2649
+ with self.__gateway_add_subdev_topo_mid_lock:
2650
+ rc, mid = self.__mqtt_client.publish(self.__gateway_topic_add_subdev_topo, json.dumps(request), 1)
2651
+ if rc == mqtt.MQTT_ERR_SUCCESS:
2652
+ self.__gateway_add_subdev_topo_mid[mid] = self.__timestamp()
2653
+ return 0, request_id
2654
+ else:
2655
+ return 1, None
2656
+
2657
+ def gateway_add_subdev_topo(self, subdev_array):
2658
+ return self.__gateway_add_subdev_topo(subdev_array)
2659
+
2660
+ def __validate_subdev_param(self, subdev, expected_len):
2661
+ if subdev is None:
2662
+ return LinkKit.ErrorCode.NULL_SUBDEV_ERR.value
2663
+ if not isinstance(subdev, list):
2664
+ return LinkKit.ErrorCode.SUBDEV_NOT_ARRAY_ERR.value
2665
+ if len(subdev) < expected_len:
2666
+ self.__link_log.error("input subdev length mismatch")
2667
+ return LinkKit.ErrorCode.ARRAY_LENGTH_ERR.value
2668
+ else:
2669
+ return LinkKit.ErrorCode.SUCCESS.value
2670
+
2671
+ def __gateway_delete_subdev_topo(self, subdev_array):
2672
+ request_params = []
2673
+ for subdev in subdev_array:
2674
+ ret = self.__validate_subdev_param(subdev, 2)
2675
+ if ret != 0:
2676
+ return ret, None
2677
+
2678
+ pk = subdev[0]
2679
+ dn = subdev[1]
2680
+
2681
+ params = {
2682
+ "productKey": pk,
2683
+ "deviceName": dn,
2684
+ }
2685
+ request_params.append(params)
2686
+ request_id = self.__get_thing_request_id()
2687
+ if request_id is None:
2688
+ return 1, None
2689
+ request = {
2690
+ "id": request_id,
2691
+ "version": "1.0",
2692
+ "params": request_params,
2693
+ }
2694
+ with self.__gateway_delete_subdev_topo_mid_lock:
2695
+ rc, mid = self.__mqtt_client.publish(self.__gateway_topic_delete_subdev_topo, json.dumps(request), 1)
2696
+ if rc == mqtt.MQTT_ERR_SUCCESS:
2697
+ self.__gateway_delete_subdev_topo_mid[mid] = self.__timestamp()
2698
+ return 0, request_id
2699
+ else:
2700
+ return 1, None
2701
+
2702
+ def gateway_delete_subdev_topo(self, subdev_array):
2703
+ return self.__gateway_delete_subdev_topo(subdev_array)
2704
+
2705
+ def __gateway_login_subdev(self, subdev_array):
2706
+ device_list = []
2707
+ for subdev in subdev_array:
2708
+ ret = self.__validate_subdev_param(subdev, 3)
2709
+ if ret != 0:
2710
+ return ret, None
2711
+
2712
+ pk = subdev[0]
2713
+ dn = subdev[1]
2714
+ ds = subdev[2]
2715
+ millis = str(self.__timestamp())
2716
+ client_id = pk + "." + dn + "|lan=Python,_v=2.2.1,_ss=1|"
2717
+
2718
+ sign_content = "clientId%sdeviceName%sproductKey%stimestamp%s" % (client_id, dn, pk, millis)
2719
+ sign = hmac.new(ds.encode("utf-8"), sign_content.encode("utf-8"), hashlib.sha256).hexdigest()
2720
+ dev_params = {
2721
+ "productKey": pk,
2722
+ "deviceName": dn,
2723
+ "clientId": client_id,
2724
+ "timestamp": millis,
2725
+ "cleanSession": "false",
2726
+ "sign": sign,
2727
+ }
2728
+ device_list.append(dev_params)
2729
+ request_params = {
2730
+ "signMethod": "hmacSha256",
2731
+ "deviceList": device_list,
2732
+ }
2733
+ request_id = self.__get_thing_request_id()
2734
+ if request_id is None:
2735
+ return 1, None
2736
+ request = {
2737
+ "id": request_id,
2738
+ "version": "1.0",
2739
+ "params": request_params,
2740
+ }
2741
+ rc, mid = self.__mqtt_client.publish(self.__gateway_topic_login_subdev, json.dumps(request), 0)
2742
+ if rc == mqtt.MQTT_ERR_SUCCESS:
2743
+ return 0, request_id
2744
+ else:
2745
+ return 1, None
2746
+
2747
+ def gateway_login_subdev(self, subdev_array):
2748
+ return self.__gateway_login_subdev(subdev_array)
2749
+
2750
+ def gateway_logout_subdev(self, subdev_array):
2751
+ return self.__gateway_logout_subdev(subdev_array)
2752
+
2753
+ def __gateway_logout_subdev(self, subdev_array):
2754
+ request_params = []
2755
+ for subdev in subdev_array:
2756
+ ret = self.__validate_subdev_param(subdev, 2)
2757
+ if ret != 0:
2758
+ return ret, None
2759
+
2760
+ pk = subdev[0]
2761
+ dn = subdev[1]
2762
+
2763
+ params = {
2764
+ "productKey": pk,
2765
+ "deviceName": dn,
2766
+ }
2767
+ request_params.append(params)
2768
+ request_id = self.__get_thing_request_id()
2769
+ if request_id is None:
2770
+ return 1, None
2771
+ request = {
2772
+ "id": request_id,
2773
+ "version": "1.0",
2774
+ "params": request_params,
2775
+ }
2776
+ rc, mid = self.__mqtt_client.publish(self.__gateway_topic_logout_subdev, json.dumps(request), 0)
2777
+ if rc == mqtt.MQTT_ERR_SUCCESS:
2778
+ return 0, request_id
2779
+ else:
2780
+ return 1, None
2781
+
2782
+ def __gateway_register_subdev(self, subdev_array):
2783
+ request_params = []
2784
+ for subdev in subdev_array:
2785
+ ret = self.__validate_subdev_param(subdev, 2)
2786
+ if ret != 0:
2787
+ return ret, None
2788
+
2789
+ pk = subdev[0]
2790
+ dn = subdev[1]
2791
+
2792
+ params = {
2793
+ "productKey": pk,
2794
+ "deviceName": dn,
2795
+ }
2796
+ request_params.append(params)
2797
+ request_id = self.__get_thing_request_id()
2798
+ if request_id is None:
2799
+ return 1, None
2800
+ request = {
2801
+ "id": request_id,
2802
+ "version": "1.0",
2803
+ "params": request_params,
2804
+ }
2805
+ rc, mid = self.__mqtt_client.publish(self.__gateway_topic_register_subdev, json.dumps(request), 0)
2806
+ if rc == mqtt.MQTT_ERR_SUCCESS:
2807
+ return 0, request_id
2808
+ else:
2809
+ return 1, None
2810
+
2811
+ def gateway_register_subdev(self, subdev_array):
2812
+ return self.__gateway_register_subdev(subdev_array)
2813
+
2814
+ def gateway_product_register_subdev(self, subdev_array):
2815
+ return self.__gateway_product_register_subdev(subdev_array)
2816
+
2817
+ def __gateway_product_register_subdev(self, subdev_array):
2818
+ device_list = []
2819
+ for subdev in subdev_array:
2820
+ ret = self.__validate_subdev_param(subdev, 3)
2821
+ if ret != 0:
2822
+ return ret, None
2823
+
2824
+ pk = subdev[0]
2825
+ dn = subdev[1]
2826
+ ps = subdev[2]
2827
+ random_str = self.__generate_random_str(15)
2828
+
2829
+ sign_content = "deviceName%sproductKey%srandom%s" % (dn, pk, random_str)
2830
+ sign = hmac.new(ps.encode("utf-8"), sign_content.encode("utf-8"), hashlib.sha256).hexdigest()
2831
+ dev_params = {
2832
+ "productKey": pk,
2833
+ "deviceName": dn,
2834
+ "random": random_str,
2835
+ "signMethod": "hmacSha256",
2836
+ "sign": sign,
2837
+ }
2838
+ device_list.append(dev_params)
2839
+ request_params = {
2840
+ "proxieds": device_list,
2841
+ }
2842
+ request_id = self.__get_thing_request_id()
2843
+ if request_id is None:
2844
+ return 1, None
2845
+ request = {
2846
+ "id": request_id,
2847
+ "version": "1.0",
2848
+ "params": request_params,
2849
+ }
2850
+ rc, mid = self.__mqtt_client.publish(self.__gateway_topic_product_register_subdev, json.dumps(request), 0)
2851
+ if rc == mqtt.MQTT_ERR_SUCCESS:
2852
+ self.__link_log.debug("mid for product dynamic register:%d" % mid)
2853
+ return 0, request_id
2854
+ else:
2855
+ return 1, None
2856
+
2857
+ def __handler_task_on_publish_callback(self, value) -> None:
2858
+ client, user_data, mid = value
2859
+ self.__link_log.debug("__on_internal_publish message:%d" % mid)
2860
+ with self.__thing_event_post_mid_lock:
2861
+ if mid in self.__thing_event_post_mid:
2862
+ self.__thing_event_post_mid.pop(mid)
2863
+ self.__link_log.debug("__on_internal_publish event post mid removed")
2864
+ return
2865
+ with self.__thing_prop_post_mid_lock:
2866
+ if mid in self.__thing_prop_post_mid:
2867
+ self.__thing_prop_post_mid.pop(mid)
2868
+ self.__link_log.debug("__on_internal_publish prop post mid removed")
2869
+ return
2870
+ with self.__thing_prop_set_reply_mid_lock:
2871
+ if mid in self.__thing_prop_set_reply_mid:
2872
+ self.__thing_prop_set_reply_mid.pop(mid)
2873
+ self.__link_log.debug("__on_internal_publish prop set reply mid removed")
2874
+ return
2875
+ with self.__thing_answer_service_mid_lock:
2876
+ if mid in self.__thing_answer_service_mid:
2877
+ self.__thing_answer_service_mid.pop(mid)
2878
+ self.__link_log.debug("__thing_answer_service_mid mid removed")
2879
+ return
2880
+ with self.__thing_raw_up_mid_lock:
2881
+ if mid in self.__thing_raw_up_mid:
2882
+ self.__thing_raw_up_mid.pop(mid)
2883
+ self.__link_log.debug("__thing_raw_up_mid mid removed")
2884
+ return
2885
+ with self.__thing_raw_down_reply_mid_lock:
2886
+ if mid in self.__thing_raw_down_reply_mid:
2887
+ self.__thing_raw_down_reply_mid.pop(mid)
2888
+ self.__link_log.debug("__thing_raw_down_reply_mid mid removed")
2889
+ return
2890
+ with self.__device_info_mid_lock:
2891
+ if mid in self.__device_info_mid:
2892
+ self.__device_info_mid.pop(mid)
2893
+ self.__link_log.debug("__device_info_mid mid removed")
2894
+ return
2895
+ with self.__thing_shadow_mid_lock:
2896
+ if mid in self.__thing_shadow_mid:
2897
+ self.__thing_shadow_mid.pop(mid)
2898
+ self.__link_log.debug("__thing_shadow_mid mid removed")
2899
+ return
2900
+ with self.__thing_update_device_info_up_mid_lock:
2901
+ if mid in self.__thing_update_device_info_up_mid:
2902
+ self.__thing_update_device_info_up_mid.pop(mid)
2903
+ self.__link_log.debug("__thing_update_device_info_up_mid mid removed")
2904
+ return
2905
+ with self.__thing_delete_device_info_up_mid_lock:
2906
+ if mid in self.__thing_delete_device_info_up_mid:
2907
+ self.__thing_delete_device_info_up_mid.pop(mid)
2908
+ self.__link_log.debug("__thing_delete_device_info_up_mid mid removed")
2909
+ return
2910
+ with self.__gateway_add_subdev_topo_mid_lock:
2911
+ if mid in self.__gateway_add_subdev_topo_mid:
2912
+ self.__gateway_add_subdev_topo_mid.pop(mid)
2913
+ self.__link_log.debug("__gateway_add_subdev_topo_mid removed")
2914
+ return
2915
+ with self.__gateway_delete_subdev_topo_mid_lock:
2916
+ if mid in self.__gateway_delete_subdev_topo_mid:
2917
+ self.__gateway_delete_subdev_topo_mid.pop(mid)
2918
+ self.__link_log.debug("__gateway_delete_subdev_topo_mid removed")
2919
+ return
2920
+ if self.__on_publish_topic is not None:
2921
+ self.__on_publish_topic(mid, self.__user_data)
2922
+
2923
+ def __on_internal_subscribe(self, client, user_data, mid, granted_qos) -> None:
2924
+ self.__handler_task.post_message(self.__handler_task_cmd_on_subscribe, (client, user_data, mid, granted_qos))
2925
+
2926
+ def __handler_task_on_subscribe_callback(self, value) -> None:
2927
+ client, user_data, mid, granted_qos = value
2928
+ self.__link_log.debug(
2929
+ "__on_internal_subscribe mid:%d granted_qos:%s" % (mid, str(",".join("%s" % it for it in granted_qos)))
2930
+ )
2931
+ # try to read rrpc
2932
+ with self.__user_rrpc_topics_subscribe_request_lock:
2933
+ if mid in self.__user_rrpc_topics_subscribe_request:
2934
+ self.__user_rrpc_topics_subscribe_request.pop(mid)
2935
+ if self.__on_subscribe_rrpc_topic:
2936
+ try:
2937
+ self.__on_subscribe_rrpc_topic(mid, granted_qos, self.__user_data)
2938
+ except Exception as err:
2939
+ self.__link_log.error("Caught exception in on_subscribe_topic: %s", err)
2940
+ return
2941
+
2942
+ # try to read other topic
2943
+ topics_requests = None
2944
+ self.__user_topics_request_lock.acquire()
2945
+ if mid in self.__user_topics_subscribe_request:
2946
+ topics_requests = self.__user_topics_subscribe_request.pop(mid)
2947
+ self.__user_topics_request_lock.release()
2948
+ if topics_requests is not None:
2949
+ return_topics = []
2950
+ for index in range(len(topics_requests)):
2951
+ if granted_qos[index] < 0 or granted_qos[index] > 1:
2952
+ self.__link_log.error("topics:%s, granted wrong:%d" % (topics_requests[index], granted_qos[index]))
2953
+ else:
2954
+ self.__user_topics[topics_requests[index][0]] = granted_qos[index]
2955
+ return_topics.append((topics_requests[index], granted_qos[index]))
2956
+ if self.__on_subscribe_topic is not None:
2957
+ try:
2958
+ self.__on_subscribe_topic(mid, granted_qos, self.__user_data)
2959
+ except Exception as err:
2960
+ self.__link_log.error("Caught exception in on_subscribe_topic: %s", err)
2961
+
2962
+ def __on_internal_unsubscribe(self, client, user_data, mid) -> None:
2963
+ self.__handler_task.post_message(self.__handler_task_cmd_on_unsubscribe, (client, user_data, mid))
2964
+
2965
+ def __handler_task_on_unsubscribe_callback(self, value) -> None:
2966
+ client, user_data, mid = value
2967
+ self.__link_log.debug("__on_internal_unsubscribe mid:%d" % mid)
2968
+ unsubscribe_request = None
2969
+ # try to read rrpc
2970
+ with self.__user_rrpc_topics_unsubscribe_request_lock:
2971
+ if mid in self.__user_rrpc_topics_unsubscribe_request:
2972
+ self.__user_rrpc_topics_unsubscribe_request.pop(mid)
2973
+ if self.__on_unsubscribe_rrpc_topic:
2974
+ try:
2975
+ self.__on_unsubscribe_rrpc_topic(mid, self.__user_data)
2976
+ except Exception as err:
2977
+ self.__link_log.error("Caught exception in on_unsubscribe_rrpc_topic: %s", err)
2978
+ return
2979
+
2980
+ with self.__user_topics_unsubscribe_request_lock:
2981
+ if mid in self.__user_topics_unsubscribe_request:
2982
+ unsubscribe_request = self.__user_topics_unsubscribe_request.pop(mid)
2983
+ if unsubscribe_request is not None:
2984
+ for t in unsubscribe_request:
2985
+ self.__link_log.debug("__user_topics:%s" % str(self.__user_topics))
2986
+ try:
2987
+ self.__user_topics.pop(t)
2988
+ except Exception as e:
2989
+ self.__link_log.error("__on_internal_unsubscribe e:" + str(e))
2990
+ return
2991
+ if self.__on_unsubscribe_topic is not None:
2992
+ try:
2993
+ self.__on_unsubscribe_topic(mid, self.__user_data)
2994
+ except Exception as err:
2995
+ self.__link_log.error("Caught exception in on_unsubscribe_topic: %s", err)
2996
+
2997
+ def dump_user_topics(self):
2998
+ return self.__user_topics
2999
+
3000
+ def force_reconnect(self) -> None:
3001
+ self.__link_log.error("force reconnecting")
3002
+ self.__force_reconnect = True
3003
+
3004
+ @staticmethod
3005
+ def to_user_topic(topic):
3006
+ topic_section = topic.split("/", 3)
3007
+ user_topic = topic_section[3]
3008
+ return user_topic
3009
+
3010
+ def to_full_topic(self, topic):
3011
+ return self.__USER_TOPIC_PREFIX % (self.__product_key, self.__device_name, topic)
3012
+
3013
+ def __is_valid_str(self, user_str) -> bool:
3014
+ if user_str is None or user_str == "":
3015
+ return False
3016
+ return True
3017
+
3018
+ @staticmethod
3019
+ def __timestamp():
3020
+ return int(time.time() * 1000)