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