├── CHANGELOG.md
├── LICENSE
├── README-zh.md
├── README.md
├── docs
├── v2.1.x
│ ├── README.md
│ ├── python_install.sh
│ └── requirements.txt
└── v2.4.x
│ ├── README.md
│ ├── python_install.sh
│ └── requirements.txt
├── examples
├── HelloThing
│ └── index.py
├── Light
│ └── index.py
├── LightMonitor
│ └── index.py
└── LightSensor
│ └── index.py
├── leda_python
├── .DS_Store
├── __init__.py
├── deviceMbus.py
├── json_coder.py
├── leda.py
├── ledaException.py
├── mbus.py
├── mbusConfig.py
└── refactoring
│ ├── __init__.py
│ ├── dbus_connection.py
│ ├── dbus_service.py
│ └── thread.py
├── lethingaccesssdk
├── .DS_Store
├── __init__.py
└── thing_access.py
├── setup.py
└── unittests
└── thing_access_sdk_test.py
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ### v0.1.0
4 | * Add initial version.
5 |
6 | ### v0.2.0
7 | * Add get config API.
8 |
9 | ### v0.3.0
10 | * Add leda_python into project, add get driver information API, the version of Link IoT Edge is v2.1.0, if you choose LE Standard and want to support python runtime, please refer to docs.
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
--------------------------------------------------------------------------------
/README-zh.md:
--------------------------------------------------------------------------------
1 | [English](README.md)|[中文](README-zh.md)
2 |
3 | # Link IoT Edge设备接入SDK Python版
4 | 本项目提供一个基于python 3.5.2的SDK包,方便用户在[Link IoT Edge](https://help.aliyun.com/product/69083.html?spm=a2c4g.11186623.6.540.7c1b705eoBIMFA)上基于SDK编写驱动接入设备。
5 |
6 | ## 快速开始 - HelloThing
7 |
8 | `HelloThing`示例演示将设备接入Link IoT Edge的全过程。
9 |
10 | 1. 复制`examples/HelloThing`文件夹到你的工作目录。
11 | 2. 压缩`HelloThing`目录的内容为一个zip包,确保`index.py`在顶级目录下。
12 | 3. 进入Link IoT Edge控制台,**边缘实例**,**驱动管理**,**新建驱动**。
13 | 4. 语言类型选择*python3*。
14 | 5. 驱动名称设置为`HelloThing`,并上传前面准备好的zip包。
15 | 6. 创建一个产品。该产品包含一个`temperature`属性(int32类型)和一个`high_temperature`事件(int32类型和一个int32类型名为`temperature`的输入参数)。
16 | 7. 创建一个名为`HelloThing`的上述产品的设备。
17 | 8. 创建一个新的实例,并将Link IoT Edge网关设备加入到实例。
18 | 9. 进入设备驱动页,将之前添加的驱动加入到实例。
19 | 10. 将`HelloThing`设备添加到实例,并将`HelloThing`驱动作为其驱动。
20 | 11. 使用如下配置添加*消息路由*:
21 | * 消息来源:`HelloThing`设备
22 | * TopicFilter:属性
23 | * 消息目标:IoT Hub
24 | 12. 部署实例。`HelloThing`设备将每隔2秒上报属性到云端,可在Link IoT Edge控制台设备运行状态页面查看。
25 |
26 | ## 使用
27 | 首先,安装一个Link IoT Edge运行环境,可以参考[搭建边缘环境](https://help.aliyun.com/product/69083.html?spm=a2c4g.11186623.6.540.7c1b705eoBIMFA)。
28 |
29 | 然后,实现设备接入。参考如下:
30 |
31 | ``` python
32 | # -*- coding: utf-8 -*-
33 | import logging
34 | import time
35 | import lethingaccesssdk
36 | from threading import Timer
37 | # Base on device, User need to implement the getProperties, setProperties and callService function.
38 | class Temperature_device(lethingaccesssdk.ThingCallback):
39 | def __init__(self):
40 | self.temperature = 41
41 | self.humidity = 80
42 | def getProperties(self, input_value):
43 | '''
44 | Get properties from the physical thing and return the result.
45 | :param input_value:
46 | :return:
47 | '''
48 | retDict = {
49 | "temperature": 41,
50 | "humidity": 80
51 | }
52 | return 0, retDict
53 | def setProperties(self, input_value):
54 | '''
55 | Set properties to the physical thing and return the result.
56 | :param input_value:
57 | :return:
58 | '''
59 | return 0, {}
60 | def callService(self, name, input_value):
61 | '''
62 | Call services on the physical thing and return the result.
63 | :param name:
64 | :param input_value:
65 | :return:
66 | '''
67 | return 0, {}
68 | def thing_behavior(client, device):
69 | while True:
70 | properties = {"temperature": device.temperature,
71 | "humidity": device.humidity}
72 | client.reportProperties(properties)
73 | client.reportEvent("high_temperature", {"temperature": 41})
74 | time.sleep(2)
75 | try:
76 | thing_config = lethingaccesssdk.Config().getThingInfos()
77 | for config in thing_config:
78 | device = Temperature_device()
79 | client = lethingaccesssdk.ThingAccessClient(config)
80 | client.registerAndonline(device)
81 | t = Timer(2, thing_behavior, (client, device))
82 | t.start()
83 | except Exception as e:
84 | logging.error(e)
85 | # don't remove this function
86 | def handler(event, context):
87 | return 'hello world'
88 |
89 | ```
90 |
91 | 接下来,按照上述[快速开始](#快速开始---hellothing)的步骤上传和测试函数。
92 |
93 | ## API参考文档
94 |
95 | 主要的API参考文档如下:
96 |
97 | * **[getConfig()](#getConfig)**
98 | * **[ThingCallback()](#ThingCallback)**
99 | * ThingCallback#**[setProperties()](#setProperties)**
100 | * ThingCallback#**[getProperties()](#getProperties)**
101 | * ThingCallback#**[callService()](#callService)**
102 | * **[ThingAccessClient()](#thingaccessclient)**
103 | * ThingAccessClient#**[registerAndOnline()](#registerandonline)**
104 | * ThingAccessClient#**[reportEvent()](#reportevent)**
105 | * ThingAccessClient#**[reportProperties()](#reportproperties)**
106 | * ThingAccessClient#**[getTsl()](#getTsl)**
107 | * ThingAccessClient#**[getTslExtInfo()](#getTslExtInfo)**
108 | * ThingAccessClient#**[online()](#online)**
109 | * ThingAccessClient#**[offline()](#offline)**
110 | * ThingAccessClient#**[unregister()](#unregister)**
111 | * ThingAccessClient#**[cleanup()](#cleanup)**
112 | * **[Config()](#Config)**
113 | * Config#**[getThingInfos()](#getThingInfos)**
114 | * Config#**[getDriverInfo()](#getDriverInfo)**
115 |
116 | ---
117 |
118 | ### getConfig()
119 | 返回驱动相关配置。
120 |
121 | ---
122 |
123 | ### ThingCallback()
124 | 根据真实设备,命名一个类(如Demo_device)继承ThingCallback。 然后在Demo_device中实现setProperties、getProperties和callService三个函数。
125 |
126 | ---
127 |
128 | ### ThingCallback.setProperties(input_value)
129 | 设置具体设备属性函数。
130 |
131 | * input_value`dict `: 设置属性,eg:{"property1": xxx, "property2": yyy ...}.
132 | * 返回值:
133 | * code`int`: 若获取成功则返回LEDA_SUCCESS, 失败则返回错误码。
134 | * output`dict`: 数据内容自定义,若无返回数据,则值空:{}。
135 |
136 | ---
137 |
138 | ### ThingCallback.getProperties(input_value)
139 | 获取具体设备属性的函数.
140 |
141 | * input_value`list`:获取属性对应的名称. eg:['key1', 'key2']。
142 | * return:
143 | * code`int`: 若获取成功则返回LEDA_SUCCESS, 失败则返回错误码。
144 | * output`dict`: 返回值. eg:{'property1':xxx, 'property2':yyy, ...}。
145 |
146 | ---
147 |
148 | ### ThingCallback.callService(name, input_value)
149 | 调用设备服务函数。
150 |
151 | * parameter:
152 | * name`str`:函数名。
153 | * input_value`dict`:对应name函数的参数,eg: {"args1": xxx, "args2": yyy ...}。
154 | * return:
155 | * code`int`: 若获取成功则返回LEDA_SUCCESS, 失败则返回错误码。
156 | * output`dict`: 返回值. eg:{"key1": xxx, "key2": yyy, ...}。
157 |
158 | ---
159 |
160 | ### ThingAccessClient(config)
161 | 设备接入客户端类, 用户主要通过它上下线设备和主动上报设备属性或事件。
162 |
163 | * config`dict`: 包括云端分配的productKey和deviceName, eg:{"productKey": "xxx", "deviceName": "yyy"}。
164 |
165 | ---
166 |
167 | ### ThingAccessClient.registerAndOnline(ThingCallback)
168 | 注册设备到Link IoT Edge,并通知设备上线。
169 |
170 | * ThingCallback`object`: 设备callback对象。
171 |
172 | ---
173 |
174 | ### ThingAccessClient.reportEvent(eventName, args)
175 | 上报事件到Link IoT Edge。
176 |
177 | * eventName`str`: 事件名。
178 | * args`dict`: 事件信息. eg: {"key1": xxx, "key2": yyy, ...}。
179 |
180 | ---
181 |
182 | ### ThingAccessClient.reportProperties(properties)
183 | 上报属性到Link IoT Edge。
184 |
185 | * properties`dict`: 上报的属性. eg:{"property1": xxx, "property2": yyy, ...}。
186 |
187 | ---
188 |
189 | ### ThingAccessClient.getTsl()
190 | 获取TSL(Thing Specification Language)字符串。
191 |
192 | * 返回TSL字符串`str`。
193 |
194 | ---
195 |
196 | ### ThingAccessClient. getTslExtInfo()
197 | 获取TSL(Thing Specification Language)扩展信息字符串。
198 |
199 | * 返回TSL扩展信息字符串`str`。
200 |
201 | ---
202 |
203 |
204 | ### ThingAccessClient.online()
205 | 通知Link IoT Edge设备上线。
206 |
207 | ---
208 |
209 | ### ThingAccessClient.offline()
210 | 通知Link IoT Edge设备下线。
211 |
212 | ---
213 |
214 | ### ThingAccessClient.cleanup()
215 | 资源回收接口,您可以使用该接口回收您的资源。
216 |
217 | ---
218 |
219 | ### ThingAccessClient.unregister()
220 | 移除设备和Link IoT Edge的绑定关系。通常无需调用。
221 |
222 | ---
223 |
224 | ### Config()
225 | 基于当前驱动配置字符串构造新的Config对象。
226 |
227 | ---
228 |
229 | ### Config. getThingInfos()
230 | 返回所有设备相关信息,返回ThingInfo`List`。
231 | ThingInfo包括如下信息:
232 |
233 | * productKey `str `: 官网申请的productKey。
234 | * deviceName `str `: 设备名
235 | * custom`dict `:设备自定义配置
236 |
237 | ---
238 |
239 | ### Config. getDriverInfo()
240 | 返回驱动相关信息,返回DriverInfo`List`。
241 |
242 |
243 | ## 许可证
244 | ```
245 | Copyright (c) 2017-present Alibaba Group Holding Ltd.
246 |
247 | Licensed under the Apache License, Version 2.0 (the "License");
248 | you may not use this file except in compliance with the License.
249 | You may obtain a copy of the License at
250 |
251 | http://www.apache.org/licenses/LICENSE-2.0
252 |
253 | Unless required by applicable law or agreed to in writing, software
254 | distributed under the License is distributed on an "AS IS" BASIS,
255 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
256 | See the License for the specific language governing permissions and
257 | limitations under the License.
258 | ```
259 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [English](README.md)|[中文](README-zh.md)
2 |
3 | # Link IoT Edge Thing Access SDK for Python
4 | The project providers a python package to develop drivers which running on [Link IoT Edge](https://help.aliyun.com/product/69083.html?spm=a2c4g.11186623.6.540.7c1b705eoBIMFA) and helping things connect to it.
5 |
6 | ## Getting Started - HelloThing
7 |
8 | The `HelloThing` sample demonstrates you the procedure that connecting things to Link IoT Edge.
9 |
10 | 1. Copy `examples/HelloThing` folder to your workspace.
11 | 2. Zip up the content of `HelloThing` folder so that the `index.py` is on the top of the zip file structure.
12 | 3. Go to Link IoT Edge console, **Edge Management**, **Driver Management** and then **Create Driver**.
13 | 4. Choose the programming language as *python3*.
14 | 5. Set the driver name `HelloThing` and upload the previous zip file.
15 | 6. Create a product, which owns an property named `temperature`(type of int32), and an event named `high_temperature`(type of int32 and has a input parameter named `temperature` whose type is int32).
16 | 7. Create a device of the product created last step, with name `HelloThing`.
17 | 8. Create a new instance and add the Link IoT Edge gateway device into it.
18 | 9. Go to Thing Driver tab and add `HelloThing` driver into that instance.
19 | 10. Add the `HelloThing` device into the instance. Choose `HelloThing` as its driver.
20 | 11. Add a *Message Router* with the folowing configuration:
21 | * Source: `HelloThing` device
22 | * TopicFilter: Properties.
23 | * Target: IoT Hub
24 | 12. Deploy. A message from `HelloThing` device should be published to IoT Hub every 2 seconds. You can check this by going to the Device Running State page on Link IoT Edge console.
25 |
26 | ## Usage
27 | First build a [Link IoT Edge](https://help.aliyun.com/document_detail/85389.html?spm=a2c4g.11174283.6.549.4c892f21uaFPjl) development environment.
28 |
29 | Then connect things to Link IoT Edge. The most common use is as follows:
30 |
31 | ``` python
32 | # -*- coding: utf-8 -*-
33 | import logging
34 | import time
35 | import lethingaccesssdk
36 | from threading import Timer
37 | # Base on device, User need to implement the getProperties, setProperties and callService function.
38 | class Temperature_device(lethingaccesssdk.ThingCallback):
39 | def __init__(self):
40 | self.temperature = 41
41 | self.humidity = 80
42 | def getProperties(self, input_value):
43 | '''
44 | Get properties from the physical thing and return the result.
45 | :param input_value:
46 | :return:
47 | '''
48 | retDict = {
49 | "temperature": 41,
50 | "humidity": 80
51 | }
52 | return 0, retDict
53 | def setProperties(self, input_value):
54 | '''
55 | Set properties to the physical thing and return the result.
56 | :param input_value:
57 | :return:
58 | '''
59 | return 0, {}
60 | def callService(self, name, input_value):
61 | '''
62 | Call services on the physical thing and return the result.
63 | :param name:
64 | :param input_value:
65 | :return:
66 | '''
67 | return 0, {}
68 | def thing_behavior(client, device):
69 | while True:
70 | properties = {"temperature": device.temperature,
71 | "humidity": device.humidity}
72 | client.reportProperties(properties)
73 | client.reportEvent("high_temperature", {"temperature": 41})
74 | time.sleep(2)
75 | try:
76 | thing_config = lethingaccesssdk.Config().getThingInfos()
77 | for config in thing_config:
78 | device = Temperature_device()
79 | client = lethingaccesssdk.ThingAccessClient(config)
80 | client.registerAndonline(device)
81 | t = Timer(2, thing_behavior, (client, device))
82 | t.start()
83 | except Exception as e:
84 | logging.error(e)
85 | # don't remove this function
86 | def handler(event, context):
87 | return 'hello world'
88 |
89 | ```
90 |
91 | Next follow the [Getting Started](#getting-started---hellothing) to upload and test the function.
92 |
93 | ## References
94 |
95 | The main API references are as follows.
96 |
97 | * **[getConfig()](#getConfig)**
98 | * **[ThingCallback()](#ThingCallback)**
99 | * ThingCallback#**[setProperties()](#setProperties)**
100 | * ThingCallback#**[getProperties()](#getProperties)**
101 | * ThingCallback#**[callService()](#callService)**
102 | * **[ThingAccessClient()](#thingaccessclient)**
103 | * ThingAccessClient#**[registerAndOnline()](#registerandonline)**
104 | * ThingAccessClient#**[reportEvent()](#reportevent)**
105 | * ThingAccessClient#**[reportProperties()](#reportproperties)**
106 | * ThingAccessClient#**[getTsl()](#getTsl)**
107 | * ThingAccessClient#**[getTslExtInfo()](#getTslExtInfo)**
108 | * ThingAccessClient#**[online()](#online)**
109 | * ThingAccessClient#**[offline()](#offline)**
110 | * ThingAccessClient#**[unregister()](#unregister)**
111 | * ThingAccessClient#**[cleanup()](#cleanup)**
112 | * **[Config()](#Config)**
113 | * Config#**[getThingInfos()](#getThingInfos)**
114 | * Config#**[getDriverInfo()](#getDriverInfo)**
115 |
116 | ---
117 |
118 | ### getConfig()
119 | return config information under the driver
120 |
121 | ---
122 |
123 | ### ThingCallback()
124 | Base on device,user need to implement a class inherits from ThingCallback. and user need to implement the setProperties、getProperties and callService in this class.
125 |
126 | ---
127 |
128 | ### ThingCallback.setProperties(input_value)
129 | set device property.
130 |
131 | * input_value`dict `: the values of Property. eg:{"property1": 'xxx', "property2": 'yyy', ...}.
132 | * return
133 | * code`int`: if success it will return LEDA_SUCCESS, or return error code.
134 | * output`dict`: customer data,if no data, it will return {}.
135 |
136 | ---
137 |
138 | ### ThingCallback.getProperties(input_value)
139 | get device property.
140 |
141 | * input_value`list`:the Property. eg:['key1', 'key2'].
142 | * return:
143 | * code`int`: if success it will return LEDA_SUCCESS, or return error code.
144 | * output`dict`: output values. eg:{'property1':xxx, 'property2':yyy, ...}.
145 |
146 | ---
147 |
148 | ### ThingCallback.callService(name, input_value)
149 | call device function.
150 |
151 | * parameter:
152 | * name`str`: function name.
153 | * input_value`dict`:the parameter of funtion,eg: {"args1": 'xxx', "args2": 'yyy', ...}.
154 | * return:
155 | * code`int`: if success it will return LEDA_SUCCESS, or return error code.
156 | * output`dict`: output values. eg:{"key1": 'xxx', "key2": 'yyy', ...}.
157 |
158 | ---
159 |
160 | ### ThingAccessClient(config)
161 | Constructs a [ThingAccessClient](#thingaccessclient) with the specified config.
162 |
163 | * config`dict`: include productKey and deviceName allocated by Link IoT Edge, eg: {"productKey": "xxx", "deviceName": "yyy"}.
164 |
165 | ---
166 |
167 | ### ThingAccessClient.registerAndOnline(ThingCallback)
168 | Registers thing to Link IoT Edge platform and informs it that thing is connected. When register, DEVICE_NAME will be used first if it exists, or LOCAL_NAME is used.
169 |
170 | * ThingCallback`object`: ThingCallback object.
171 |
172 | ---
173 |
174 | ### ThingAccessClient.reportEvent(eventName, args)
175 | Reports a event to Link IoT Edge platform.
176 |
177 | * eventName`str`: Event name.
178 | * args`dict`: Event information. eg: {"key1": 'xxx', "key2": 'yyy', ...}.
179 |
180 | ---
181 |
182 | ### ThingAccessClient.reportProperties(properties)
183 | Reports the property values to Link IoT Edge platform.
184 |
185 | * properties`dict`: property values. eg:{"property1": 'xxx', "property2": 'yyy', ...}.
186 |
187 | ---
188 |
189 | ### ThingAccessClient.getTsl()
190 | Returns the TSL(Thing Specification Language) string`str`.
191 |
192 | ---
193 |
194 | ### ThingAccessClient.getTslExtInfo()
195 | Returns the TSL(Thing Specification Language) extend information string`str`.
196 |
197 | ---
198 |
199 |
200 | ### ThingAccessClient.online()
201 | Informs Link IoT Edge platform that thing is connected.
202 |
203 | ---
204 |
205 | ### ThingAccessClient.offline()
206 | Informs Link IoT Edge platform that thing is disconnected.
207 |
208 | ---
209 |
210 | ### ThingAccessClient.unregister()
211 | Removes the binding relationship between thing and Link IoT Edge platform. You usually don't call this function.
212 |
213 | ---
214 |
215 | ### ThingAccessClient.cleanup()
216 | Removes the binding relationship between thing and Link IoT Edge platform. You usually don't call this function.
217 |
218 | ---
219 |
220 | ### Config()
221 | base on current config return config object.
222 |
223 | ---
224 |
225 | ### Config. getThingInfos()
226 | return ThingInfo`List`。
227 | ThingInfo include:
228 |
229 | * productKey `str `: productKey。
230 | * deviceName `str `: deviceName
231 | * custom`dict `: custom config
232 |
233 | ---
234 |
235 | ### Config. getDriverInfo()
236 | return DriverInfo`List`。
237 |
238 | ## License
239 | ```
240 | Copyright (c) 2017-present Alibaba instance Holding Ltd.
241 |
242 | Licensed under the Apache License, Version 2.0 (the "License");
243 | you may not use this file except in compliance with the License.
244 | You may obtain a copy of the License at
245 |
246 | http://www.apache.org/licenses/LICENSE-2.0
247 |
248 | Unless required by applicable law or agreed to in writing, software
249 | distributed under the License is distributed on an "AS IS" BASIS,
250 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
251 | See the License for the specific language governing permissions and
252 | limitations under the License.
253 | ```
254 |
--------------------------------------------------------------------------------
/docs/v2.1.x/README.md:
--------------------------------------------------------------------------------
1 | # 安装指南
2 |
3 |
4 |
5 | ## 1. 安装Link IoT Edge版本 >= 2.1.0
6 |
7 | ## 2. 准备系统依赖库,需要系统支持编译.
8 | ### 2.1. Ubuntu
9 |
10 | ```
11 | sudo apt-get install libgirepository1.0-dev \
12 | libcairo2-dev gobject-introspection libreadline-dev \
13 | libdbus-1-dev libdbus-glib-1-dev libxml2-dev libxslt1-dev
14 |
15 | ```
16 | ### 2.2. CentOS
17 | ```
18 | sudo yum install dbus-devel dbus-glib-devel cairo-devel \
19 | gobject-introspection-devel cairo-gobject-devel readline-devel \
20 | libxml2-devel libxslt-devel
21 | ```
22 |
23 | ## 3. 安装python解析器和pip工具,python版本 >= 3.5.2
24 | 输入下述命令,确认是否已经安装了python3环境
25 | ```
26 | python3 -V
27 | ```
28 | 如果已经存在python3环境则直接进入步骤4,否则继续以下步骤
29 |
30 | ### 3.1. Ubuntu
31 | ```
32 | sudo apt-get update
33 | sudo apt-get install python3
34 | ```
35 |
36 | ### 3.2. CentOS
37 | ```
38 | sudo yum update
39 | sudo yum install python3
40 | ```
41 |
42 | ## 4. 安装python runtime依赖的第三方库
43 | ```
44 | ./python_install.sh
45 | ```
46 |
47 | # 问题列表
48 |
49 | 1. 安装过程中,遇到File "/usr/lib/python3.5/locale.py", line 594, in setlocale
50 | return _setlocale(category, locale)错误
51 |
52 | ```
53 | export LC_ALL=C
54 | ```
55 |
56 | 2. Python.h: No such file or directory
57 |
58 | ```
59 | for Ubuntu
60 | sudo apt-get install python3-dev
61 |
62 | for CentOS
63 | sudo yum install python3-devel
64 | ```
65 |
66 | 3. py3cairo.h: No such file or directory
67 |
68 | 查找该文件,一般在这里/usr/local/include/pycairo/
69 |
70 | ```
71 | mkdir /usr/include/pycairo
72 | cp /usr/local/include/pycairo/py3cairo.h /usr/include/pycairo
73 | ```
74 |
75 |
--------------------------------------------------------------------------------
/docs/v2.1.x/python_install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | PY3_VENV_PATH=/linkedge/gateway/build/bin/fc-runtime/linkedge/gateway/build/bin/py3_venv
4 |
5 | TUNA_INDEX="https://pypi.tuna.tsinghua.edu.cn/simple"
6 | TUNA_HOST="pypi.tuna.tsinghua.edu.cn"
7 |
8 | ALIYUN_INDEX="https://mirrors.aliyun.com/pypi/simple/"
9 | ALIYUN_HOST="mirrors.aliyun.com"
10 |
11 | PIP3_INDEX_URL=$TUNA_INDEX
12 | PIP3_TRUST_HOST=$TUNA_HOST
13 |
14 | REQUIREMENTS=`readlink -f requirements.txt`
15 | PACKAGES=`readlink -f packages`
16 |
17 | PIP3="python3 -m pip --retries 0 --timeout 1 --disable-pip-version-check --trusted-host $PIP3_TRUST_HOST"
18 | PIP3_INSTALL="$PIP3 install --index-url $PIP3_INDEX_URL -f $PACKAGES"
19 |
20 | sudo mkdir -p $PY3_VENV_PATH
21 | cd $PY3_VENV_PATH
22 | sudo python3 -m pip install virtualenv
23 | sudo python3 -m virtualenv .
24 |
25 | sudo sh -c ". bin/activate; $PIP3_INSTALL -r $REQUIREMENTS"
26 |
27 |
--------------------------------------------------------------------------------
/docs/v2.1.x/requirements.txt:
--------------------------------------------------------------------------------
1 | dbus-python==1.2.4
2 | monotonic==1.4
3 | opcua==0.98.9
4 | pymodbus==2.1.0
5 | ws4py==0.5.1
6 | virtualenv==15.1.0
7 | six==1.11.0
8 | lxml==4.3.4
9 | pyserial==3.4
10 | python-dateutil==2.7.2
11 | pytz==2018.3
12 | PyGObject==3.27.1
13 | pycairo==1.15.4
14 |
--------------------------------------------------------------------------------
/docs/v2.4.x/README.md:
--------------------------------------------------------------------------------
1 | # 安装指南
2 |
3 |
4 |
5 | ## 1. 安装Link IoT Edge版本 >= 2.4.0
6 |
7 | ## 2. 准备系统依赖库,需要系统支持编译.
8 | ### 2.1. Ubuntu
9 |
10 | ```
11 | sudo apt-get install libgirepository1.0-dev \
12 | libcairo2-dev gobject-introspection libreadline-dev \
13 | libdbus-1-dev libdbus-glib-1-dev libxml2-dev libxslt1-dev
14 |
15 | ```
16 | ### 2.2. CentOS
17 | ```
18 | sudo yum install dbus-devel dbus-glib-devel cairo-devel \
19 | gobject-introspection-devel cairo-gobject-devel readline-devel \
20 | libxml2-devel libxslt-devel
21 | ```
22 |
23 | ## 3. 安装python解析器和pip工具,python版本 >= 3.5.2
24 | 输入下述命令,确认是否已经安装了python3环境
25 | ```
26 | python3 -V
27 | ```
28 | 如果已经存在python3环境则直接进入步骤4,否则继续以下步骤
29 |
30 | ### 3.1. Ubuntu
31 | ```
32 | sudo apt-get update
33 | sudo apt-get install python3
34 | ```
35 |
36 | ### 3.2. CentOS
37 | ```
38 | sudo yum update
39 | sudo yum install python3
40 | ```
41 |
42 | ## 4. 安装python runtime依赖的第三方库
43 | ```
44 | ./python_install.sh
45 | ```
46 |
47 | # 问题列表
48 |
49 | 1. 安装过程中,遇到File "/usr/lib/python3.5/locale.py", line 594, in setlocale
50 | return _setlocale(category, locale)错误
51 |
52 | ```
53 | export LC_ALL=C
54 | ```
55 |
56 |
57 | 2. Python.h: No such file or directory
58 |
59 | ```
60 | for Ubuntu
61 | sudo apt-get install python3-dev
62 |
63 | for CentOS
64 | sudo yum install python3-devel
65 | ```
66 |
67 | 3. py3cairo.h: No such file or directory
68 |
69 | 查找该文件,一般在这里/usr/local/include/pycairo/
70 |
71 | ```
72 | mkdir /usr/include/pycairo
73 | cp /usr/local/include/pycairo/py3cairo.h /usr/include/pycairo
74 | ```
75 |
--------------------------------------------------------------------------------
/docs/v2.4.x/python_install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | PY3_VENV_PATH=/linkedge/gateway/build/bin/fc-runtime/linkedge/gateway/build/bin/py3_venv
4 |
5 | TUNA_INDEX="https://pypi.tuna.tsinghua.edu.cn/simple"
6 | TUNA_HOST="pypi.tuna.tsinghua.edu.cn"
7 |
8 | ALIYUN_INDEX="https://mirrors.aliyun.com/pypi/simple/"
9 | ALIYUN_HOST="mirrors.aliyun.com"
10 |
11 | PIP3_INDEX_URL=$TUNA_INDEX
12 | PIP3_TRUST_HOST=$TUNA_HOST
13 |
14 | REQUIREMENTS=`readlink -f requirements.txt`
15 | PACKAGES=`readlink -f packages`
16 |
17 | PIP3="python3 -m pip --retries 0 --timeout 1 --disable-pip-version-check --trusted-host $PIP3_TRUST_HOST"
18 | PIP3_INSTALL="$PIP3 install --index-url $PIP3_INDEX_URL -f $PACKAGES"
19 |
20 | sudo sh -c "$PIP3_INSTALL -r $REQUIREMENTS"
21 |
22 |
--------------------------------------------------------------------------------
/docs/v2.4.x/requirements.txt:
--------------------------------------------------------------------------------
1 | dbus-python==1.2.4
2 | monotonic==1.4
3 | opcua==0.98.9
4 | pymodbus==2.1.0
5 | ws4py==0.5.1
6 | virtualenv==15.1.0
7 | six==1.11.0
8 | lxml==4.3.4
9 | pyserial==3.4
10 | python-dateutil==2.7.2
11 | pytz==2018.3
12 | PyGObject==3.27.1
13 | pycairo==1.15.4
14 |
--------------------------------------------------------------------------------
/examples/HelloThing/index.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import logging
3 | import time
4 | import lethingaccesssdk
5 | from threading import Timer
6 |
7 |
8 | # Base on device, User need to implement the getProperties, setProperties and callService function.
9 | class Temperature_device(lethingaccesssdk.ThingCallback):
10 | def __init__(self):
11 | self.temperature = 41
12 | self.humidity = 80
13 |
14 | def getProperties(self, input_value):
15 | '''
16 | Get properties from the physical thing and return the result.
17 | :param input_value:
18 | :return:
19 | '''
20 | retDict = {
21 | "temperature": 41,
22 | "humidity": 80
23 | }
24 | return 0, retDict
25 |
26 | def setProperties(self, input_value):
27 | '''
28 | Set properties to the physical thing and return the result.
29 | :param input_value:
30 | :return:
31 | '''
32 | return 0, {}
33 |
34 | def callService(self, name, input_value):
35 | '''
36 | Call services on the physical thing and return the result.
37 | :param name:
38 | :param input_value:
39 | :return:
40 | '''
41 | return 0, {}
42 |
43 |
44 | def thing_behavior(client, app_callback):
45 | while True:
46 | properties = {"temperature": app_callback.temperature,
47 | "humidity": app_callback.humidity}
48 | client.reportProperties(properties)
49 | client.reportEvent("high_temperature", {"temperature": 41})
50 | time.sleep(2)
51 |
52 | try:
53 | infos = lethingaccesssdk.Config().getThingInfos()
54 | for info in infos:
55 | app_callback = Temperature_device()
56 | client = lethingaccesssdk.ThingAccessClient(info)
57 | client.registerAndOnline(app_callback)
58 | t = Timer(2, thing_behavior, (client, app_callback))
59 | t.start()
60 | except Exception as e:
61 | logging.error(e)
62 |
63 | # don't remove this function
64 | def handler(event, context):
65 | return 'hello world'
--------------------------------------------------------------------------------
/examples/Light/index.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import logging
3 | import lethingaccesssdk
4 | from threading import Timer
5 |
6 |
7 | class Light(object):
8 | def __init__(self):
9 | self._isOn = 1
10 |
11 | @property
12 | def isOn(self):
13 | return self._isOn
14 |
15 | @isOn.setter
16 | def isOn(self, value):
17 | self._isOn = value
18 |
19 | class Connector(lethingaccesssdk.ThingCallback):
20 | def __init__(self, config, light):
21 | self.light = light
22 | self._client = lethingaccesssdk.ThingAccessClient(config)
23 |
24 | def connect(self):
25 | self._client.registerAndOnline(self)
26 |
27 | def disconnect(self):
28 | self._client.offline()
29 |
30 | def callService(self, name, input_value):
31 | if name == "yourFunc":
32 | #do something
33 | return 0, {}
34 | return 100001, {}
35 |
36 | def getProperties(self, input_value):
37 | retDict = {}
38 | if 'LightSwitch' in input_value:
39 | retDict['LightSwitch'] = self.light.isOn
40 | return 0, retDict
41 |
42 | def setProperties(self, input_value):
43 | if 'LightSwitch' in input_value:
44 | value = input_value['LightSwitch']
45 | if value != self.light.isOn:
46 | self.light.isOn = value
47 | if(self._client):
48 | properties = {'LightSwitch': value}
49 | self._client.reportProperties(properties)
50 | return 0, {}
51 | else:
52 | return 100001, {}
53 |
54 | infos = lethingaccesssdk.Config().getThingInfos()
55 | for info in infos:
56 | print(info)
57 | try:
58 | light = Light()
59 | connector = Connector(info, light)
60 | connector.connect()
61 | except Exception as e:
62 | logging.error(e)
63 |
64 | # don't remove this function
65 | def handler(event, context):
66 | return 'hello world'
67 |
--------------------------------------------------------------------------------
/examples/LightMonitor/index.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import lecoresdk
3 | import json
4 |
5 |
6 | ON = 1
7 | OFF = 0
8 |
9 | def light_turn(status):
10 | it = lecoresdk.IoTData()
11 | set_params = {"productKey": "device productKey", # need to update your device productKey
12 | "deviceName": "device deviceName", # need to update your device deviceName
13 | "payload": {"LightSwitch":status}}
14 | res = it.setThingProperties(set_params)
15 |
16 | def handler(event, context):
17 | event_json = json.loads(event.decode("utf-8"))
18 | if "payload" in event_json:
19 | payload_json = json.loads(event_json["payload"])
20 | if "MeasuredIlluminance" in payload_json and "value" in payload_json["MeasuredIlluminance"]:
21 | MeasuredIlluminance = payload_json["MeasuredIlluminance"]["value"]
22 | if MeasuredIlluminance > 500:
23 | light_turn(OFF)
24 | elif MeasuredIlluminance <= 100:
25 | light_turn(ON)
26 | return 'hello world'
27 |
--------------------------------------------------------------------------------
/examples/LightSensor/index.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import logging
3 | import lethingaccesssdk
4 | from threading import Timer
5 |
6 |
7 | class Light_Sensor(object):
8 | def __init__(self):
9 | self._illuminance = 200
10 | self._delta = 100
11 | self._callback = None
12 |
13 | @property
14 | def illuminance(self):
15 | return self._illuminance
16 |
17 | def start(self):
18 | illuminance = self._illuminance
19 | delta = self._delta
20 | if (illuminance >= 600 or illuminance <= 100):
21 | delta = -delta;
22 | illuminance += delta
23 | self._delta = delta
24 | self._illuminance = illuminance
25 | if self._callback is not None:
26 | data = {"properties": illuminance}
27 | self._callback(data)
28 | t = Timer(2, self.start, ())
29 | t.start()
30 |
31 | def stop(self):
32 | self._callback = None
33 |
34 | def listen(self, callback):
35 | if callback is None:
36 | self.stop()
37 | else:
38 | self._callback = callback
39 | self.start()
40 |
41 | class Connector(lethingaccesssdk.ThingCallback):
42 | def __init__(self, config, lightSensor):
43 | self.lightSensor = lightSensor
44 | self._client = lethingaccesssdk.ThingAccessClient(config)
45 |
46 | def listenCallback(self, data):
47 | self._client.reportProperties({'MeasuredIlluminance': data["properties"]})
48 |
49 | def connect(self):
50 | self._client.registerAndOnline(self)
51 | self.lightSensor.listen(self.listenCallback)
52 |
53 | def disconnect(self):
54 | self._client.offline()
55 | self.lightSensor.listen(None)
56 |
57 | def callService(self, name, input_value):
58 | if name == "yourFunc":
59 | #do something
60 | return 0, {}
61 | return 100001, {}
62 |
63 | def getProperties(self, input_value):
64 | retDict = {}
65 | if 'MeasuredIlluminance' in input_value:
66 | retDict['MeasuredIlluminance'] = self.lightSensor.illuminance
67 | return 0, retDict
68 |
69 | def setProperties(self, input_value):
70 | logging.error("can't set value")
71 | return 100001, {}
72 |
73 | infos = lethingaccesssdk.Config().getThingInfos()
74 | for info in infos:
75 | print(info)
76 | try:
77 | lightSensor = Light_Sensor()
78 | connector = Connector(info, lightSensor)
79 | connector.connect()
80 | except Exception as e:
81 | logging.error(e)
82 |
83 | # don't remove this function
84 | def handler(event, context):
85 | return 'hello world'
86 |
--------------------------------------------------------------------------------
/leda_python/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliyun/linkedge-thing-access-sdk-python/131a8f5112b64229fe21b48738157b624ecf8c34/leda_python/.DS_Store
--------------------------------------------------------------------------------
/leda_python/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliyun/linkedge-thing-access-sdk-python/131a8f5112b64229fe21b48738157b624ecf8c34/leda_python/__init__.py
--------------------------------------------------------------------------------
/leda_python/deviceMbus.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 |
5 | # ====#====#====#====
6 | # file: deviceMbus
7 | # time: 1/30/18
8 | # ====#====#====#====
9 |
10 | from . import ledaException as exception
11 | from gi.repository import GLib
12 | import dbus.mainloop.glib
13 | from . import mbusConfig
14 | from .refactoring import dbus_service
15 | import threading
16 | import logging
17 | import json
18 | from . import mbus
19 | import os
20 | import re
21 | from . import json_coder
22 |
23 | _logger = logging.getLogger(__name__)
24 |
25 |
26 | class SyncMsg_Event(object):
27 | def __init__(self):
28 | self.event = threading.Event()
29 | self.clear()
30 | self.msg = None
31 |
32 | def clear(self):
33 | self.msg = None
34 | self.event.clear()
35 |
36 | def wait(self, time_s):
37 | self.event.wait(time_s)
38 |
39 | return self.msg
40 |
41 | def set(self, msg):
42 | self.msg = msg
43 | self.event.set()
44 |
45 |
46 | mbusLoopFlag = False
47 |
48 |
49 | def mbus_loop():
50 | try:
51 | _logger.debug("mbus main looping...")
52 |
53 | mainloop = GLib.MainLoop()
54 | mainloop.run()
55 | except:
56 | _logger.debug(">>>>>>>>>>>>> driver existed<<<<<<<<<<<<<<<")
57 | os._exit(0)
58 |
59 |
60 | class device_callback(object):
61 |
62 | def callService(self, name, input):
63 | '''
64 | :param name[string]: method name
65 | :param input[string]: formate: key-value json-string ,eg:
66 | {
67 | "params":{
68 | "args1": xxx,
69 | "args2": yyy
70 | }
71 | }
72 | :return:
73 | code[int] : 消息编码和msg对应
74 | msg[string] : 返回的执行状态
75 | output[string] : 方法返回的输出数据
76 | {
77 | "key1": xxx,
78 | "key2": yyy,
79 | ...
80 | }
81 | '''
82 | raise exception.LedaCallBackException("callService is empty")
83 |
84 | def getprofile_cb(self):
85 | '''
86 | :return: profile[string]: 设备三要素模型
87 | '''
88 | raise exception.LedaCallBackException("getprofile_cb is empty")
89 |
90 | def getProperties(self, input):
91 | '''
92 | :param input[string]: 格式json中的数组类型:[property1,property2]
93 | :return: 格式为key-value形式的json串,如:{property1:xxx,property2:yyy}
94 | '''
95 | raise exception.LedaCallBackException("getProperties is empty")
96 |
97 | def setProperties(self, input):
98 | '''
99 | :param input[string]:属性列表,其格式为key-value形式的json串,如:{property1:xxx,property2:yyy}
100 | :return: key-value json-string, if no data to return ,you should return "{}"
101 | '''
102 | raise exception.LedaCallBackException("setProperties is empty")
103 |
104 |
105 | class device_service(object):
106 | def __init__(self, cloud_id, device_name, product_key, bus_callback_object):
107 |
108 | self.cloud_id = cloud_id
109 | self.device_name = device_name
110 | self.product_key = product_key
111 | self.bus_callback_object = bus_callback_object
112 | self.deviceMbusHandle = None
113 | self.deviceMbusObject = None
114 |
115 | def get_cloud_id(self):
116 | return self.cloud_id
117 |
118 | def _getDeviceProfile(self, interface):
119 |
120 | @dbus.service.method(interface, out_signature='s')
121 | def getDeviceProfile(self):
122 | _logger.debug("cloud_id:%s, method: getDeviceProfile", self.cloud_id)
123 |
124 | try:
125 | profile = self.callback_funs.getprofile_cb()
126 | if (False == isinstance(profile, str)):
127 | _logger.warning("getprofile_cb(cloud_id:%s) return args type is invalid: %s", self.cloud_id,
128 | type(profile))
129 | retDict = {
130 | "code": exception.LEDA_ERROR_INVAILD_PARAM, # params invalid
131 | "message": "getprofile_cb(cloud_id:%s) return args type is invalid: %s" % (
132 | self.cloud_id, type(profile))
133 | }
134 | _logger.debug("getDeviceProfile(cloud_id:%s): retMsg: %s", self.cloud_id, retDict)
135 | return json.dumps(retDict, ensure_ascii = False)
136 | profile_dict = json.loads(profile)
137 |
138 | except(AttributeError, ValueError, TypeError) as err:
139 | _logger.warning('%s', err)
140 |
141 | retDict = {
142 | "code": exception.LEDA_ERROR_INVAILD_PARAM, # params invalid
143 | "message": "%s" % (err)
144 | }
145 | _logger.debug("getDeviceProfile(cloud_id:%s): retMsg: %s", self.cloud_id, retDict)
146 | return json.dumps(retDict, ensure_ascii = False)
147 |
148 | except:
149 | _logger.exception("Err")
150 |
151 | retDict = {
152 | "code": exception.LEDA_ERROR_FAILED, # params invalid
153 | "message": "unkonwn error"
154 | }
155 | _logger.debug("getDeviceProfile(cloud_id:%s): retMsg: %s", self.cloud_id, retDict)
156 | return json.dumps(retDict, ensure_ascii = False)
157 |
158 | retDict = {
159 | "code": exception.LEDA_SUCCESS,
160 | "message": "successfully",
161 | "params": {
162 | "deviceProfile": profile_dict
163 | }
164 | }
165 | _logger.debug("getDeviceProfile(cloud_id:%s): retMsg: %s", self.cloud_id, retDict)
166 | return json.dumps(retDict, ensure_ascii = False)
167 |
168 | return getDeviceProfile
169 |
170 | def _callServices(self, interface):
171 |
172 | @dbus.service.method(interface, in_signature='ss', out_signature='s')
173 | def callServices(self, methodName, inMsg):
174 | _logger.debug("callServices(cloud_id:%s) in params: method: %s, args: %s", self.cloud_id, methodName, inMsg)
175 |
176 | codeInfoDict = {
177 | exception.LEDA_SUCCESS: 'successfully',
178 | exception.LEDA_ERROR_INVAILD_PARAM: 'invalid params',
179 | exception.LEDA_ERROR_FAILED: 'exec failed',
180 | exception.LEDA_ERROR_TIMEOUT: 'timeout',
181 | exception.LEDA_ERROR_NOT_SUPPORT: 'not support',
182 | exception.LEDA_ERROR_PROPERTY_NOT_EXIST: 'property not exist',
183 | exception.LEDA_ERROR_PROPERTY_READ_ONLY: 'property read only',
184 | exception.LEDA_ERROR_PROPERTY_WRITE_ONLY: 'property write only',
185 | exception.LEDA_ERROR_SERVICE_NOT_EXIST: 'service not exist',
186 | exception.LEDA_ERROR_SERVICE_INPUT_PARAM: 'invalid service input params'
187 | }
188 |
189 | try:
190 | inArgs = json.loads(inMsg)["params"]
191 | if (methodName == "get"):
192 | code, retInfo = self.callback_funs.getProperties(inArgs)
193 | if (False == isinstance(retInfo, dict) or (False == isinstance(code, int))):
194 | _logger.warning("get(cloud_id:%s) return args type is invalid: %s", self.cloud_id,
195 | type(retInfo))
196 | retDict = {
197 | "code": exception.LEDA_ERROR_INVAILD_PARAM, # params invalid
198 | "message": "get(cloud_id:%s) return args type is invalid: %s" % (
199 | self.cloud_id, type(retInfo)),
200 | "params": {}
201 | }
202 | _logger.debug("get(cloud_id:%s): retMsg: %s", self.cloud_id, retDict)
203 | return json.dumps(retDict, ensure_ascii = False)
204 |
205 | if (exception.LEDA_SUCCESS != code):
206 | retDict = {
207 | "code": exception.LEDA_ERROR_FAILED,
208 | "message": "get(cloud_id:%s) exec failed" % (self.cloud_id),
209 | "params": {}
210 | }
211 | _logger.debug("get(cloud_id:%s): retMsg: %s", self.cloud_id, retDict)
212 | return json.dumps(retDict, ensure_ascii = False)
213 |
214 | retDict = {
215 | "code": code,
216 | "message": codeInfoDict[code],
217 | "params": retInfo
218 | }
219 | _logger.debug("get(cloud_id:%s): retMsg: %s", self.cloud_id, retDict)
220 | return json.dumps(retDict, cls=json_coder.Json_Encoder, ensure_ascii = False)
221 |
222 | elif (methodName == "set"):
223 | code, retInfo = self.callback_funs.setProperties(inArgs)
224 | if (False == isinstance(retInfo, dict) or (False == isinstance(code, int))):
225 | _logger.warning("set(cloud_id:%s) return args type is invalid: %s", self.cloud_id,
226 | type(retInfo))
227 |
228 | retDict = {
229 | "code": exception.LEDA_ERROR_INVAILD_PARAM, # params invalid
230 | "message": "set(cloud_id:%s) return args type is invalid: %s" % (
231 | self.cloud_id, type(retInfo)),
232 | "params": {}
233 | }
234 | _logger.debug("set(cloud_id:%s): retMsg: %s", self.cloud_id, retDict)
235 | return json.dumps(retDict, ensure_ascii = False)
236 |
237 | if (exception.LEDA_SUCCESS != code):
238 | retDict = {
239 | "code": exception.LEDA_ERROR_FAILED,
240 | "message": "set(cloud_id:%s) exec failed" % (self.cloud_id),
241 | "params": {}
242 | }
243 | _logger.debug("set(cloud_id:%s): retMsg: %s", self.cloud_id, retDict)
244 | return json.dumps(retDict, ensure_ascii = False)
245 |
246 | retDict = {
247 | "code": code,
248 | "message": codeInfoDict[code],
249 | "params": {}
250 | }
251 | _logger.debug("set(cloud_id:%s): retMsg: %s", self.cloud_id, retDict)
252 | return json.dumps(retDict, ensure_ascii = False)
253 |
254 | else:
255 | code, output = self.callback_funs.callService(methodName, inArgs)
256 | if ((False == isinstance(code, int)) or (False == isinstance(output, dict))):
257 | _logger.warning("callService(cloud_id:%s) return args type is invalid", self.cloud_id)
258 | retDict = {
259 | "code": exception.LEDA_ERROR_INVAILD_PARAM, # params invalid
260 | "message": "callService(cloud_id:%s) return args type is invalid" % (self.cloud_id),
261 | "params": {
262 | "code": exception.LEDA_ERROR_INVAILD_PARAM,
263 | "message": "callService(cloud_id:%s) return args type is invalid" % (self.cloud_id),
264 | "data": {}
265 | }
266 | }
267 | _logger.debug("callServices(cloud_id:%s): %s retMsg: %s", self.cloud_id, methodName, retDict)
268 | return json.dumps(retDict, ensure_ascii = False)
269 |
270 | if (exception.LEDA_SUCCESS != code):
271 | retDict = {
272 | "code": exception.LEDA_ERROR_FAILED,
273 | "message": "callService(cloud_id:%s) exec failed" % (self.cloud_id),
274 | "params": {
275 | "code": exception.LEDA_ERROR_FAILED,
276 | "message": "callService(cloud_id:%s) exec failed" % (self.cloud_id),
277 | "data": {}
278 | }
279 | }
280 | _logger.debug("callServices(cloud_id:%s): %s retMsg: %s", self.cloud_id, methodName,
281 | retDict)
282 | return json.dumps(retDict, ensure_ascii = False)
283 |
284 | data = output
285 |
286 | except(AttributeError, ValueError, TypeError, KeyError) as err:
287 | _logger.exception('Err')
288 | _logger.warning('%s', err)
289 | retDict = {
290 | "code": exception.LEDA_ERROR_INVAILD_PARAM, # params invalid
291 | "message": "%s" % (err),
292 | "params": {}
293 | }
294 | _logger.debug("callServices(cloud_id:%s): %s retMsg: %s", self.cloud_id, methodName, retDict)
295 | return json.dumps(retDict, ensure_ascii = False)
296 |
297 | except:
298 | _logger.exception("Err")
299 |
300 | retDict = {
301 | "code": exception.LEDA_ERROR_FAILED, # params invalid
302 | "message": "unkonwn error",
303 | "params": {}
304 | }
305 | _logger.warning("callServices(cloud_id:%s): %s retMsg: %s", self.cloud_id, methodName, retDict)
306 | return json.dumps(retDict, ensure_ascii = False)
307 |
308 | retDict = {
309 | "code": exception.LEDA_SUCCESS,
310 | "message": "successfully",
311 | "params": {
312 | "code": code,
313 | "message": codeInfoDict[code],
314 | "data": data
315 | }
316 | }
317 | _logger.debug("callServices(cloud_id:%s): %s retMsg: %s", self.cloud_id, methodName, retDict)
318 | return json.dumps(retDict, ensure_ascii = False)
319 |
320 | return callServices
321 |
322 | def _createMbusDynamicObject(self):
323 |
324 | interface = mbusConfig.CMP_DEVICE_WKN_PREFIX + self.cloud_id
325 |
326 | attrDict = {
327 | "callback_funs": self.bus_callback_object,
328 | "cloud_id": self.cloud_id,
329 |
330 | "callServices": self._callServices(interface),
331 | "getDeviceProfile": self._getDeviceProfile(interface)
332 | }
333 |
334 | DemoObjectClass = type("Device_" + self.product_key + self.device_name, (dbus_service.DbusObject,), attrDict)
335 | objPath = '/' + interface.replace('.', '/')
336 | objectHandle = self.deviceMbusHandle.createObject(DemoObjectClass, objPath)
337 |
338 | return objectHandle
339 |
340 | def releaseMbusObject(self):
341 | if (self.deviceMbusHandle):
342 | bus = self.deviceMbusHandle.getBus()
343 | wellKnownName = mbusConfig.CMP_DEVICE_WKN_PREFIX + self.cloud_id
344 | objectPath = '/' + wellKnownName.replace('.', '/')
345 | if (None != self.deviceMbusObject):
346 | self.deviceMbusObject.remove_from_connection(bus, objectPath)
347 | self.deviceMbusObject = None
348 |
349 | self.deviceMbusHandle.releaseName()
350 | self.deviceMbusHandle = None
351 |
352 | def device_report_property(self, report_info):
353 | '''
354 | :param report_info: report_info 上报信息,其格式为key-value形式的json串,如上报属性:
355 | {
356 | "property1": {
357 | "value" : "xxx",
358 | "time" : 1524448722000
359 | },
360 | "property1": {
361 | "value" : "yyy",
362 | "time" : 1524448722000
363 | }
364 | ...
365 | }
366 | :return:
367 | '''
368 |
369 | if (None == self.deviceMbusHandle):
370 | _logger.warning("device(%s) can't report property unless online ", self.cloud_id)
371 | raise exception.LedaReportPropertyException(
372 | "device(%s) can't report property unless online" % (self.cloud_id))
373 |
374 | if (False == isinstance(report_info, str)):
375 | raise exception.LedaReportPropertyException(
376 | "device(%s):device_report_property,params type is invalid: %s" % (self.cloud_id, type(report_info)))
377 |
378 | srcWKN = self.deviceMbusHandle.getName()
379 | srcInterface = srcWKN
380 | srcObjectPath = "/" + srcInterface.replace(".", "/")
381 | self.deviceMbusHandle.unicastSignal(srcObjectPath, srcInterface, mbusConfig.DMP_SUB_WKN, 's',
382 | "propertiesChanged", report_info)
383 |
384 | _logger.info("Device(%s): report properties: %s" % (self.cloud_id, report_info))
385 |
386 | def device_report_event(self, name, report_info):
387 | '''
388 | :param name: 事件名称
389 | :param report_info: 携带信息,其格式为key-value形式的json串, 如上报事件:
390 | {
391 | "params": {
392 | "value" : {
393 | "key1":"value1",
394 | "key2":"value2"
395 | },
396 | "time" : 1524448722000
397 | }
398 | }
399 | :return:
400 | '''
401 |
402 | if (None == self.deviceMbusHandle):
403 | _logger.warning("device(%s) can't report event unless online ", self.cloud_id)
404 | raise exception.LedaReportEventException("device(%s) can't report event unless online" % (self.cloud_id))
405 |
406 | if ((False == isinstance(report_info, str)) or (False == isinstance(name, str))):
407 | raise exception.LedaReportEventException(
408 | "device(%s):device_report_event,params type is invalid" % (self.cloud_id))
409 |
410 | if (len(name) > mbusConfig.STRING_NAME_MAX_LEN):
411 | raise exception.LedaReportEventException(
412 | "device(%s):device_report_event,params name is too long(%s)" % (len(name)))
413 |
414 | srcWKN = self.deviceMbusHandle.getName()
415 | srcInterface = srcWKN
416 | srcObjectPath = "/" + srcInterface.replace(".", "/")
417 |
418 | self.deviceMbusHandle.unicastSignal(srcObjectPath, srcInterface, mbusConfig.DMP_SUB_WKN, 's', name, report_info)
419 |
420 | _logger.info("Device(%s): report event(%s): %s" % (self.cloud_id, name, report_info))
421 |
422 |
423 | class driver_service(object):
424 | def __init__(self):
425 | self.driverMbusHandle = None
426 | self.driverMbusObject = None
427 | self.device_service_dict = {}
428 | self.deviceServiceDictLock = threading.Lock()
429 | self.driver_name = None
430 | self.driver_id = None
431 |
432 | def _exit(self, connection):
433 | _logger.warning("the connection is Abnormal(closed or bus daemon crashed),the process exited automatically")
434 | os._exit(0)
435 |
436 | def _notify_config(self, interface):
437 |
438 | @dbus.service.method(interface, in_signature='ss', out_signature='i')
439 | def notify_config(self, key, value):
440 | if (self.config_callback_obj):
441 | t = threading.Thread(target=self.config_callback_obj.deviceConfigCB, args=(key, value))
442 | t.setDaemon(True)
443 | t.start()
444 |
445 | return exception.LEDA_SUCCESS
446 |
447 | return notify_config
448 |
449 | def _getDeviceList(self, interface):
450 |
451 | @dbus.service.method(interface, in_signature='s', out_signature='s')
452 | def getDeviceList(self, deviceState):
453 |
454 | devNum = 0
455 | devList = []
456 |
457 | with self.deviceServiceDictLock:
458 |
459 | try:
460 |
461 | if (deviceState == ""):
462 | for pk_dn in self.device_service_dict:
463 | devNum += 1
464 | devList.append(self.device_service_dict[pk_dn][0]) # cloud_id
465 | elif (deviceState == "online"):
466 | for pk_dn in self.device_service_dict:
467 | if (None != self.device_service_dict[pk_dn][1].deviceMbusHandle):
468 | devNum += 1
469 | devList.append(self.device_service_dict[pk_dn][0]) # cloud_id
470 | elif (deviceState == "offline"):
471 | for pk_dn in self.device_service_dict:
472 | if (None == self.device_service_dict[pk_dn][1].deviceMbusHandle):
473 | devNum += 1
474 | devList.append(self.device_service_dict[pk_dn][0]) # cloud_id
475 | else:
476 | _logger.warning("method: getDeviceList inparams is invalid")
477 |
478 | except:
479 | _logger.exception("Err")
480 |
481 | outMsg = {
482 | "params": {
483 | "devNum": devNum,
484 | "devList": devList
485 | }
486 | }
487 |
488 | return json.dumps(outMsg, ensure_ascii = False)
489 |
490 | return getDeviceList
491 |
492 | def _releaseMbusObject(self):
493 | if (self.driverMbusHandle):
494 |
495 | with self.deviceServiceDictLock:
496 |
497 | for pk_dn in self.device_service_dict:
498 | self.device_service_dict[pk_dn][0] = None # clean cloud_id
499 | self.device_service_dict[pk_dn][1].device_disconnect()
500 |
501 | self.device_service_dict = {}
502 | try:
503 | bus = self.driverMbusHandle.getBus()
504 | except:
505 | _logger.exception('Err')
506 | return
507 |
508 | wellKnownName = mbusConfig.CMP_DRIVER_WKN_PREFIX + self.driver_id
509 |
510 | objectPath = '/' + wellKnownName.replace('.', '/')
511 | if (None != self.driverMbusObject):
512 | self.driverMbusObject.remove_from_connection(bus, objectPath)
513 | self.driverMbusObject = None
514 |
515 | self.driverMbusHandle.releaseName()
516 |
517 | def _createMbusDynamicObject(self, driver_name):
518 |
519 | interface = mbusConfig.CMP_DRIVER_WKN_PREFIX + driver_name
520 |
521 | attrDict = {
522 | "device_service_dict": self.device_service_dict,
523 | "deviceServiceDictLock": self.deviceServiceDictLock,
524 | "config_callback_obj": None,
525 |
526 | "getDeviceList": self._getDeviceList(interface),
527 | "notify_config": self._notify_config(interface)
528 | }
529 |
530 | DemoObjectClass = type("Driver_" + driver_name, (dbus.service.Object,), attrDict)
531 | objPath = '/' + interface.replace('.', '/')
532 | objectHandle = self.driverMbusHandle.createObject(DemoObjectClass, objPath)
533 |
534 | return objectHandle
535 |
536 | def _openMonitorMbusDaemon(self):
537 | bus = self.driverMbusHandle.getBus()
538 | bus.call_on_disconnection(self._exit)
539 |
540 | def driver_init_with_driverId(self, driver_id):
541 | global mbusLoopFlag
542 |
543 | if (False == mbusLoopFlag):
544 | dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
545 |
546 | if (False == isinstance(driver_id, str)):
547 | raise exception.LedaParamsException("driver_init_with_driverId: driver_id type is invalid")
548 |
549 | try:
550 | wkn = mbusConfig.CMP_DRIVER_WKN_PREFIX + driver_id
551 | self.driverMbusHandle = mbus.MbusConnect(wkn)
552 | self._openMonitorMbusDaemon()
553 | self.driverMbusObject = self._createMbusDynamicObject(driver_id)
554 | self.driver_id = driver_id
555 | except SystemExit:
556 | raise exception.LedaException("mbus existed")
557 | except:
558 | _logger.exception("Err")
559 | raise exception.LedaException("mbus connect failed")
560 |
561 | if (False == mbusLoopFlag):
562 | t = threading.Thread(target=mbus_loop, name="mbusLoop")
563 | t.setDaemon(True)
564 | t.start()
565 |
566 | mbusLoopFlag = True
567 |
568 | def driver_init(self, driver_name):
569 | global mbusLoopFlag
570 |
571 | if (False == mbusLoopFlag):
572 | dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
573 |
574 | if (False == isinstance(driver_name, str)):
575 | raise exception.LedaParamsException("driver_init: driver_name type is invalid")
576 |
577 | try:
578 | wkn = mbusConfig.CMP_DRIVER_WKN_PREFIX + driver_name
579 | self.driverMbusHandle = mbus.MbusConnect(wkn)
580 | self._openMonitorMbusDaemon()
581 | self.driverMbusObject = self._createMbusDynamicObject(driver_name)
582 | self.driver_name = driver_name
583 | except SystemExit:
584 | raise exception.LedaException("mbus existed")
585 | except:
586 | _logger.exception("Err")
587 | raise exception.LedaException("mbus connect failed")
588 |
589 | if (False == mbusLoopFlag):
590 | t = threading.Thread(target=mbus_loop, name="mbusLoop")
591 | t.setDaemon(True)
592 | t.start()
593 |
594 | mbusLoopFlag = True
595 |
596 | def driver_set_watchdog(self, thread_name, count_down):
597 | '''
598 | :param thread_name: 需要保活的线程名称
599 | :param count_down: 倒计时时间,-1表示停止保活
600 | :return:
601 | '''
602 | if (None == self.driverMbusHandle):
603 | raise exception.LedaBusHandleException("mbus Handle is None")
604 |
605 | if (False == isinstance(thread_name, str)):
606 | raise exception.LedaRPCMethodException("driver_set_watchdog: thread_name is valid:%s" % (thread_name))
607 | else:
608 | if ((len(thread_name) > mbusConfig.STRING_NAME_MAX_LEN) or (len(thread_name) == 0)):
609 | raise exception.LedaRPCMethodException("driver_set_watchdog: thread_name is valid:%s" % (thread_name))
610 |
611 | if (False == isinstance(count_down, int)):
612 | raise exception.LedaRPCMethodException("driver_set_watchdog: count_down is valid:%s" % (count_down))
613 | else:
614 | if ((count_down == 0) or (count_down < -2)):
615 | raise exception.LedaRPCMethodException("driver_set_watchdog: count_down is valid:%s" % (count_down))
616 |
617 | try:
618 | self.driverMbusHandle.feedDog(thread_name, count_down)
619 |
620 | except:
621 | _logger.exception("Err")
622 | raise exception.LedaFeedDogException("feed dog failed")
623 |
624 | def driver_exit(self):
625 | if (None == self.driverMbusHandle):
626 | _logger.debug("driverMbusHandle is None")
627 | else:
628 |
629 | self._releaseMbusObject()
630 |
631 | self.driverMbusHandle.close()
632 | self.driverMbusHandle = None
633 |
--------------------------------------------------------------------------------
/leda_python/json_coder.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | import json
5 | import binascii
6 |
7 |
8 | class Json_Encoder(json.JSONEncoder):
9 | def default(self, obj):
10 | if isinstance(obj, bytes):
11 | return str(binascii.b2a_hex(obj))[2:-1]
12 | else:
13 | return super(Json_Encoder, self).default(obj)
14 |
--------------------------------------------------------------------------------
/leda_python/leda.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 |
5 | # ====#====#====#====
6 | # file: lead
7 | # time: 5/29/18
8 | # ====#====#====#====
9 |
10 | # from cython.parallel import funcname, linenum
11 | from .deviceMbus import *
12 | from . import ledaException as exception
13 | from . import mbusConfig
14 | from . import json_coder
15 | import logging
16 | import threading
17 | import json
18 | import time
19 | import hashlib
20 | import os
21 |
22 | _logger = logging.getLogger(__name__)
23 |
24 |
25 | def funcname():
26 | return 1
27 |
28 |
29 | def linenum():
30 | return 1
31 |
32 |
33 | class BaseDeviceCallback(device_callback):
34 |
35 | def callService(self, name, input):
36 | '''
37 | :param name[string]: method name
38 | :param input[dict]: , eg:
39 | {
40 | "args1": xxx,
41 | "args2": yyy
42 | ...
43 | }
44 | :return:
45 | code[int]: 若获取成功则返回LEDA_SUCCESS, 失败则返回错误码(参考错误码定义:ledaException.py)
46 | output[dict]: , eg:
47 | {
48 | "key1": xxx,
49 | "key2": yyy,
50 | ...
51 | }
52 | '''
53 | raise exception.LedaCallBackException("callService is empty")
54 |
55 | def getProperties(self, input):
56 | '''
57 | :param input[list]: ,eg:[property1,property2 ...]
58 | :return:
59 | code[int]: 若获取成功则返回LEDA_SUCCESS, 失败则返回错误码(参考错误码定义:ledaException.py)
60 | output[dict]: , eg:
61 | {
62 | 'property1':xxx,
63 | 'property2':yyy,
64 | ...
65 | }
66 | '''
67 | raise exception.LedaCallBackException("getProperties is empty")
68 |
69 | def setProperties(self, input):
70 | '''
71 | :param input[dict]:, eg:
72 | {
73 | 'property1':xxx,
74 | 'property2':yyy,
75 | ...
76 | }
77 | :return:
78 | code[int]: 若获取成功则返回LEDA_SUCCESS, 失败则返回错误码(参考错误码定义:ledaException.py)
79 | output[dict]: 数据内容自定义,若无返回数据,则值空:{}
80 | '''
81 | raise exception.LedaCallBackException("setProperties is empty")
82 |
83 |
84 | class LedaConfigCallback(object):
85 |
86 | def deviceConfigCB(self, moduleName, moduleConfig):
87 | raise exception.LedaCallBackException("deviceConfigCB is empty")
88 |
89 |
90 | class fileUploadResultCallback(object):
91 |
92 | def fileUploadResult(self, fileName, resultCode, msg):
93 | '''
94 | :param fileName[string]: 文件名
95 | :param resultCode: 0: 成功, 1~N 失败
96 | :param msg: 成功时为云端存储的文件uri地址,失败时为具体原因
97 | :return:
98 | '''
99 | raise exception.LedaCallBackException("fileUploadResult is empty")
100 |
101 |
102 | class LedaSubDevice(device_service):
103 | def __init__(self, driver_name, cloud_id, device_name, product_key, bus_callback_object):
104 | super(LedaSubDevice, self).__init__(cloud_id, device_name, product_key, bus_callback_object)
105 | self.driverName = driver_name
106 | self.deviceCloudId = ''
107 | self.connectSync = SyncMsg_Event()
108 | self.disConnectSync = SyncMsg_Event()
109 | self.onofflineLock = threading.Lock()
110 |
111 | def _deviceConnect_relay_cb(self, replyMsg):
112 | syncMsg = {"state": False}
113 | _logger.debug("Device(%s): connect return msg: %s" % (self.cloud_id, replyMsg))
114 |
115 | try:
116 |
117 | retDict = json.loads(replyMsg)
118 |
119 | if (0 == retDict["code"]):
120 | _logger.info("Device(%s): is connected" % (self.cloud_id))
121 | syncMsg["state"] = True
122 | self.deviceCloudId = retDict['params']['deviceCloudId']
123 |
124 | if (None == self.deviceMbusObject):
125 | self.deviceMbusObject = self._createMbusDynamicObject()
126 | else:
127 | _logger.warning("Device(%s): connect return code is error: %d, errMsg: %s" % (
128 | self.cloud_id, retDict["code"], retDict["message"]))
129 | except:
130 | _logger.exception("Err")
131 | _logger.warning("replyMsg: %s is invalid", replyMsg)
132 |
133 | self.connectSync.set(syncMsg)
134 |
135 | def _deviceConnect_error_cb(self, errorMsg):
136 | syncMsg = {"state": False}
137 | _logger.warning("Device(%s): connect failed,errMsg: %s", self.cloud_id, errorMsg)
138 |
139 | self.connectSync.set(syncMsg)
140 |
141 | def device_connect(self):
142 | with self.onofflineLock:
143 | if (None == self.deviceMbusHandle):
144 | wellKonwnName = mbusConfig.CMP_DEVICE_WKN_PREFIX + self.cloud_id
145 | self.deviceMbusHandle = mbus.DeviceMbus(wellKonwnName)
146 | else:
147 | return
148 |
149 | reply_cb = self._deviceConnect_relay_cb
150 | error_cb = self._deviceConnect_error_cb
151 |
152 | inparams = {
153 | "productKey": self.product_key,
154 | "driverName": self.driverName,
155 | "deviceName": self.device_name,
156 | "isLocal": mbusConfig.IS_LOCAL_FLAG
157 | }
158 |
159 | self.connectSync.clear()
160 | self.deviceMbusHandle.connect(json.dumps(inparams, ensure_ascii = False), reply_cb, error_cb)
161 | syncMsg = self.connectSync.wait(mbusConfig.MERHOD_SYNC_TIMEOUT)
162 | if (None == syncMsg):
163 | self.releaseMbusObject()
164 | raise exception.LedaRPCMethodException("connect device time out", exception.LEDA_ERROR_TIMEOUT)
165 | elif (False == syncMsg["state"]):
166 | self.releaseMbusObject()
167 | raise exception.LedaRPCMethodException("connect device failed", exception.LEDA_ERROR_FAILED)
168 |
169 | def _deviceDisconnect_reply_cb(self, replyMsg):
170 | syncMsg = {"state": False}
171 |
172 | try:
173 | _logger.debug("Device(%s):disconnect return msg: %s" % (self.cloud_id, replyMsg))
174 | retDict = json.loads(replyMsg)
175 | if (0 == retDict["code"]):
176 | syncMsg["state"] = True
177 | _logger.info("Device(%s): is disconnected" % (self.cloud_id))
178 | else:
179 | _logger.warning("Device(%s): disconnect return code is error: %d, errMsg: %s" % (
180 | self.cloud_id, retDict["code"], retDict["message"]))
181 | except:
182 | _logger.exception("Err")
183 | _logger.warning("replyMsg: %s is invalid", replyMsg)
184 |
185 | self.disConnectSync.set(syncMsg)
186 |
187 | def _deviceDisconnect_error_cb(self, errorMsg):
188 | syncMsg = {"state": False}
189 | _logger.warning("Device(%s) disconnect failed,errMsg: %s", self.cloud_id, errorMsg)
190 |
191 | self.disConnectSync.set(syncMsg)
192 |
193 | def device_disconnect(self):
194 | with self.onofflineLock:
195 | if (None == self.deviceMbusHandle):
196 | return
197 |
198 | reply_cb = self._deviceDisconnect_reply_cb
199 | error_cb = self._deviceDisconnect_error_cb
200 |
201 | try:
202 | inparams = {'deviceCloudId': self.deviceCloudId}
203 | self.disConnectSync.clear()
204 | self.deviceMbusHandle.disconnect(json.dumps(inparams, ensure_ascii = False), reply_cb, error_cb)
205 | syncMsg = self.disConnectSync.wait(mbusConfig.MERHOD_SYNC_TIMEOUT)
206 | if ((None == syncMsg) or (False == syncMsg["state"])):
207 | _logger.warning("device(%s) disconnected failed", self.cloud_id)
208 |
209 | except:
210 | _logger.warning("device(%s) disconnected failed", self.cloud_id)
211 | _logger.exception("Err")
212 |
213 | self.releaseMbusObject()
214 | _logger.info("Device(%s): is disconnected" % (self.cloud_id))
215 |
216 | def online(self):
217 | self.device_connect()
218 |
219 | def offline(self):
220 | self.device_disconnect()
221 |
222 | def reportProperties(self, propertiesDict):
223 | '''上报属性
224 | :param propertiesDict[dict]: 格式如下:
225 | {
226 | "propertyName1": xxx,
227 | "propertyName2": yyy,
228 | ...
229 | }
230 | :return:
231 | '''
232 | if (False == isinstance(propertiesDict, dict)):
233 | raise exception.LedaReportPropertyException(
234 | "device(%s):reportProperties,params type is invalid: %s" % (self.cloud_id, type(propertiesDict)))
235 |
236 | tmpPropertiesDict = {}
237 | for key, value in propertiesDict.items():
238 | tmpPropertiesDict[key] = {}
239 | tmpPropertiesDict[key]['value'] = value
240 | tmpPropertiesDict[key]['time'] = int(round(time.time() * 1000))
241 |
242 | self.device_report_property(json.dumps(tmpPropertiesDict, cls=json_coder.Json_Encoder, ensure_ascii = False))
243 |
244 | def reportEvent(self, eventName, eventDict):
245 | '''上报属性
246 | :param eventName[string]
247 | :param eventDict[dict]: 格式如下:
248 | {
249 | "eventArgs1":xxx,
250 | "eventArgs2":yyy,
251 | "eventArgs3":zzz
252 | ...
253 | }
254 | :return:
255 | '''
256 | if ((False == isinstance(eventDict, dict)) or (False == isinstance(eventName, str))):
257 | raise exception.LedaReportEventException(
258 | "device(%s):reportEvent,params type is invalid" % (self.cloud_id))
259 |
260 | tmpEventDict = {
261 | 'params': {
262 | 'value': eventDict,
263 | 'time': int(round(time.time() * 1000))
264 | }
265 | }
266 |
267 | self.device_report_event(eventName, json.dumps(tmpEventDict, ensure_ascii = False))
268 |
269 |
270 | class LedaModule(driver_service):
271 | def __init__(self):
272 | super(LedaModule, self).__init__()
273 | self.getPdInfoSync = SyncMsg_Event()
274 | self.subConfigSync = SyncMsg_Event()
275 | self.registerModuleSync = SyncMsg_Event()
276 | self.unregisterModuleSync = SyncMsg_Event()
277 | self.unregisterDeviceSync = SyncMsg_Event()
278 | self.addFileUploadSync = SyncMsg_Event()
279 |
280 | def _registerModule_cb(self, inMsg):
281 |
282 | syncMsg = {}
283 | syncMsg["state"] = False
284 | syncMsg["msg"] = None
285 | s = '%s:%s' % (funcname(), linenum())
286 | _logger.debug("%s,registerDriver return msg: %s", s, inMsg)
287 |
288 | try:
289 | msgDict = json.loads(inMsg)
290 | if (0 != msgDict['code']):
291 | s = '%s:%s' % (funcname(), linenum())
292 | _logger.warning("%s,rpc method: registerDriver return code is error: %d" %
293 | (s, msgDict['code']))
294 | else:
295 | syncMsg["msg"] = ''
296 | syncMsg["state"] = True
297 | except:
298 | s = '%s:%s' % (funcname(), linenum())
299 | _logger.exception("%s,Err", s)
300 | _logger.warning("%s,replyMsg is invalid", s)
301 |
302 | self.registerModuleSync.set(syncMsg)
303 |
304 | def _registerModule_errCb(self, errMsg):
305 | s = '%s:%s' % (funcname(), linenum())
306 | _logger.warning("%s, registerDriver return errmsg: %s" % (s, errMsg))
307 |
308 | syncMsg = {}
309 | syncMsg["state"] = False
310 | syncMsg["msg"] = None
311 | self.registerModuleSync.set(syncMsg)
312 |
313 | def _registerModule(self):
314 | '''register driver
315 | '''
316 |
317 | inMsg = {}
318 | inMsg['params'] = {}
319 | inMsg['params']['driverLocalId'] = self.driver_name
320 | inMsg['params']['driverStartupTime'] = str(int(round(time.time() * 1000)))
321 |
322 | if (None == self.driverMbusHandle):
323 | raise exception.LedaBusHandleException("mbus Handle is None")
324 |
325 | reply_cb = self._registerModule_cb
326 | error_cb = self._registerModule_errCb
327 |
328 | self.registerModuleSync.clear()
329 | self.driverMbusHandle.registerDriver(json.dumps(inMsg, ensure_ascii = False), reply_cb, error_cb)
330 | syncMsg = self.registerModuleSync.wait(mbusConfig.MERHOD_SYNC_TIMEOUT)
331 | if (None == syncMsg):
332 | raise exception.LedaRPCMethodException("registerDriver time out", exception.LEDA_ERROR_TIMEOUT)
333 | elif (False == syncMsg["state"]):
334 | raise exception.LedaRPCMethodException("registerDriver failed", exception.LEDA_ERROR_FAILED)
335 |
336 | def _unregisterModule_cb(self, inMsg):
337 | syncMsg = {}
338 | syncMsg["state"] = False
339 | syncMsg["msg"] = None
340 | s = '%s:%s' % (funcname(), linenum())
341 | _logger.debug("%s,unregisterDriver return msg: %s", s, inMsg)
342 |
343 | try:
344 | msgDict = json.loads(inMsg)
345 | if (0 != msgDict['code']):
346 | s = '%s:%s' % (funcname(), linenum())
347 | _logger.warning("%s,rpc method: unregisterDriver return code is error: %d" %
348 | (s, msgDict['code']))
349 | else:
350 | syncMsg["msg"] = ''
351 | syncMsg["state"] = True
352 | except:
353 | s = '%s:%s' % (funcname(), linenum())
354 | _logger.exception("%s,Err", s)
355 | _logger.warning("%s,replyMsg is invalid", s)
356 |
357 | self.unregisterModuleSync.set(syncMsg)
358 |
359 | def _unregisterModule_errCb(self, errMsg):
360 | s = '%s:%s' % (funcname(), linenum())
361 | _logger.warning("%s, unregisterDriver return errmsg: %s" % (s, errMsg))
362 |
363 | syncMsg = {}
364 | syncMsg["state"] = False
365 | syncMsg["msg"] = None
366 | self.unregisterModuleSync.set(syncMsg)
367 |
368 | def _unregisterModule(self):
369 | '''unregister driver
370 | '''
371 |
372 | inMsg = {}
373 | inMsg['params'] = {}
374 | inMsg['params']['driverLocalId'] = self.driver_name
375 |
376 | if (None == self.driverMbusHandle):
377 | raise exception.LedaBusHandleException("mbus Handle is None")
378 |
379 | reply_cb = self._unregisterModule_cb
380 | error_cb = self._unregisterModule_errCb
381 |
382 | self.unregisterModuleSync.clear()
383 | self.driverMbusHandle.unregisterDriver(json.dumps(inMsg, ensure_ascii = False), reply_cb, error_cb)
384 | syncMsg = self.unregisterModuleSync.wait(mbusConfig.MERHOD_SYNC_TIMEOUT)
385 | if (None == syncMsg):
386 | raise exception.LedaRPCMethodException("unregisterDriver time out", exception.LEDA_ERROR_TIMEOUT)
387 | elif (False == syncMsg["state"]):
388 | raise exception.LedaRPCMethodException("unregisterDriver failed", exception.LEDA_ERROR_FAILED)
389 |
390 | def moduleInit(self, moduleName):
391 | '''模块初始化
392 |
393 | :param moduleName[string]: 模块名称
394 | :return:
395 | '''
396 |
397 | driver_id = os.environ.get("FUNCTION_ID")
398 | self.driver_init_with_driverId(driver_id)
399 | self.driver_name = moduleName
400 |
401 | self._registerModule()
402 |
403 | def moduleRelease(self):
404 | '''模块退出
405 | :return:
406 | '''
407 | self.driver_exit()
408 |
409 | def feedDog(self, thread_name, count_down_seconds):
410 | '''喂看门狗.
411 | :param thread_name: 需要保活的线程名称.
412 | :param count_down_seconds: 倒计时时间, -1表示停止保活, 单位:秒.
413 | :return:
414 | '''
415 |
416 | self.driver_set_watchdog(thread_name, count_down_seconds)
417 |
418 | def getConfig(self):
419 |
420 | key = 'gw_driverconfig_' + self.driver_id
421 | return self._getConfig(key)
422 |
423 | def getTSL(self, productKey):
424 | return self.getPdInfo(productKey)
425 |
426 | def getTSLConfig(self, productKey):
427 | key = 'gw_TSL_config_' + productKey
428 | return self._getConfig(key)
429 |
430 | def _getPdInfo_cb(self, code, value):
431 | syncMsg = {}
432 | syncMsg["state"] = False
433 | syncMsg["msg"] = None
434 | s = '%s:%s' % (funcname(), linenum())
435 | _logger.debug("%s,getPdInfo return msg: *************", s)
436 |
437 | try:
438 | if (0 != code):
439 | s = '%s:%s' % (funcname(), linenum())
440 | _logger.warning("%s,rpc method: getPdInfo return code is error: %d" %
441 | (s, code))
442 | else:
443 |
444 | syncMsg["msg"] = value
445 | syncMsg["state"] = True
446 | except:
447 | s = '%s:%s' % (funcname(), linenum())
448 | _logger.exception("%s,Err", s)
449 | _logger.warning("%s,replyMsg is invalid", s)
450 |
451 | self.getPdInfoSync.set(syncMsg)
452 |
453 | def _getPdInfo_errCb(self, errMsg):
454 | s = '%s:%s' % (funcname(), linenum())
455 | _logger.warning("%s, getPdInfo return errmsg: %s" % (s, errMsg))
456 |
457 | syncMsg = {}
458 | syncMsg["state"] = False
459 | syncMsg["msg"] = None
460 | self.getPdInfoSync.set(syncMsg)
461 |
462 | def getPdInfo(self, productKey=''):
463 | '''获取配置相关信息
464 |
465 | :param productKey[string]: 如果为空,则默认获取lead module 的配置信息
466 | :return: info[string]:输出信息内容
467 | '''
468 |
469 | key = 'gw_TSL_' + productKey if (productKey) else 'gw_driverconfig_' + self.driver_name
470 |
471 | return self._getConfig(key)
472 |
473 | def _getConfig(self, key):
474 | '''获取配置相关信息
475 | :return: info[string]:输出信息内容
476 | '''
477 |
478 | if (None == self.driverMbusHandle):
479 | raise exception.LedaBusHandleException("mbus Handle is None")
480 |
481 | if (False == isinstance(key, str)):
482 | raise exception.LedaParamsException("_getConfig: input args type is invalid")
483 |
484 | reply_cb = self._getPdInfo_cb
485 | error_cb = self._getPdInfo_errCb
486 |
487 | self.getPdInfoSync.clear()
488 | self.driverMbusHandle.getConfig(key, reply_cb, error_cb)
489 |
490 | syncMsg = self.getPdInfoSync.wait(mbusConfig.MERHOD_SYNC_TIMEOUT)
491 | if (None == syncMsg):
492 | raise exception.LedaRPCMethodException("getPdInfo time out", exception.LEDA_ERROR_TIMEOUT)
493 | elif (False == syncMsg["state"]):
494 | raise exception.LedaRPCMethodException("getPdInfo failed", exception.LEDA_ERROR_FAILED)
495 | else:
496 | info = syncMsg["msg"]
497 |
498 | return info
499 |
500 | def _subConfig_cb(self, code):
501 | syncMsg = {}
502 | syncMsg["state"] = False
503 | syncMsg["msg"] = None
504 | s = '%s:%s' % (funcname(), linenum())
505 | _logger.debug("%s,_subConfig_cb return code: %s", s, code)
506 |
507 | try:
508 | if (0 != code):
509 | s = '%s:%s' % (funcname(), linenum())
510 | _logger.warning("%s,rpc method: _subConfig_cb return code is error: %d" %
511 | (s, code))
512 | else:
513 |
514 | syncMsg["msg"] = ''
515 | syncMsg["state"] = True
516 | except:
517 | s = '%s:%s' % (funcname(), linenum())
518 | _logger.exception("%s,Err", s)
519 | _logger.warning("%s,replyMsg is invalid", s)
520 |
521 | self.subConfigSync.set(syncMsg)
522 |
523 | def _subConfig_errCb(self, errMsg):
524 | s = '%s:%s' % (funcname(), linenum())
525 | _logger.warning("%s, _subConfig_cb return errmsg: %s" % (s, errMsg))
526 |
527 | syncMsg = {}
528 | syncMsg["state"] = False
529 | syncMsg["msg"] = None
530 | self.subConfigSync.set(syncMsg)
531 |
532 | def subConfig(self, key, type=1):
533 | '''
534 | :param key: config name
535 | :param type: 0: Owner, 1: observer
536 | :return:
537 | '''
538 |
539 | if (None == self.driverMbusHandle):
540 | raise exception.LedaBusHandleException("mbus Handle is None")
541 |
542 | if (False == isinstance(key, str)):
543 | raise exception.LedaParamsException("subConfig: input args type is invalid")
544 |
545 | reply_cb = self._subConfig_cb
546 | error_cb = self._subConfig_errCb
547 |
548 | self.subConfigSync.clear()
549 | self.driverMbusHandle.subscribeConfig(key, type, reply_cb, error_cb)
550 |
551 | syncMsg = self.subConfigSync.wait(mbusConfig.MERHOD_SYNC_TIMEOUT)
552 | if (None == syncMsg):
553 | raise exception.LedaRPCMethodException("subConfig time out", exception.LEDA_ERROR_TIMEOUT)
554 | elif (False == syncMsg["state"]):
555 | raise exception.LedaRPCMethodException("subConfig failed", exception.LEDA_ERROR_FAILED)
556 |
557 | def registerDeviceConfigCallback(self, callbackObj):
558 | '''注册设备配置变更回调.
559 |
560 | :param callbackObj: 设备变更通知回调接口对象
561 | :return:
562 | '''
563 |
564 | if (False == isinstance(callbackObj, LedaConfigCallback)):
565 | raise exception.LedaCallBackException("bus callback object is invalid")
566 |
567 | self.driverMbusObject.config_callback_obj = callbackObj
568 | key = 'gw_driverconfig_' + self.driver_name
569 | self.subConfig(key)
570 |
571 | def driver_register_device(self, device_name, product_key, product_md5, profile, bus_callback_object):
572 |
573 | ''' register a device
574 | :param device_name: 由设备特征值组成的唯一描述信息,只能由字母和数字组成
575 | :param product_key: 通过productConfig获取产品唯一描述信息
576 | :param product_md5: productConfig算出md5
577 | :param profile : profile 设备三要素模型
578 | :param bus_callback_object: callback object
579 | :return: cloud_id
580 | '''
581 |
582 | if (None == self.driverMbusHandle):
583 | raise exception.LedaBusHandleException("mbus Handle is None")
584 |
585 | if (False == isinstance(bus_callback_object, device_callback)):
586 | raise exception.LedaCallBackException("bus callback object is invalid")
587 |
588 | if ((False == isinstance(device_name, str)) or
589 | (False == isinstance(product_key, str)) or
590 | (False == isinstance(product_md5, str)) or
591 | (False == isinstance(profile, str))):
592 | raise exception.LedaParamsException("cmp_bus_register_device: input args type is invalid")
593 |
594 | cloud_id = product_key + '_' + device_name
595 | subDevice = LedaSubDevice(self.driver_name, cloud_id, device_name, product_key, bus_callback_object)
596 |
597 | return subDevice
598 |
599 | def _unregister_device_cb(self, inMsg):
600 |
601 | syncMsg = {}
602 | syncMsg["state"] = False
603 | syncMsg["msg"] = None
604 | s = '%s:%s' % (funcname(), linenum())
605 | _logger.debug("%s,unregisterDevice return msg: %s", s, inMsg)
606 |
607 | try:
608 | msgDict = json.loads(inMsg)
609 | if (0 != msgDict['code']):
610 | s = '%s:%s' % (funcname(), linenum())
611 | _logger.warning("%s,rpc method: unregisterDevice return code is error: %d" %
612 | (s, msgDict['code']))
613 | else:
614 | syncMsg["msg"] = ''
615 | syncMsg["state"] = True
616 | except:
617 | s = '%s:%s' % (funcname(), linenum())
618 | _logger.exception("%s,Err", s)
619 | _logger.warning("%s,replyMsg is invalid", s)
620 |
621 | self.unregisterDeviceSync.set(syncMsg)
622 |
623 | def _unregister_device_errCb(self, errMsg):
624 | s = '%s:%s' % (funcname(), linenum())
625 | _logger.warning("%s, unregisterDevice return errmsg: %s" % (s, errMsg))
626 |
627 | syncMsg = {}
628 | syncMsg["state"] = False
629 | syncMsg["msg"] = None
630 | self.unregisterDeviceSync.set(syncMsg)
631 |
632 | def deviceUnregister(self, subDevice):
633 | ''' unregister device
634 |
635 | :param subDevice: device obj
636 | :return:
637 | '''
638 |
639 | if (False == isinstance(subDevice, LedaSubDevice)):
640 | raise exception.LedaParamsException('type of subDevice is invalid')
641 |
642 | with self.deviceServiceDictLock:
643 |
644 | pk_dn = subDevice.product_key + subDevice.device_name
645 |
646 | if (pk_dn not in self.device_service_dict):
647 | raise exception.LedaRPCMethodException("device(%s) is not exited" % subDevice.device_name)
648 | else:
649 | del self.device_service_dict[pk_dn]
650 |
651 | subDevice.offline()
652 | cloudId = subDevice.get_cloud_id()
653 |
654 | try:
655 | reply_cb = self._unregister_device_cb
656 | error_cb = self._unregister_device_errCb
657 | self.unregisterDeviceSync.clear()
658 |
659 | self.driverMbusHandle.unregisterDevice(cloudId, reply_cb, error_cb)
660 | syncMsg = self.unregisterDeviceSync.wait(mbusConfig.MERHOD_SYNC_TIMEOUT)
661 | if (None == syncMsg):
662 | raise exception.LedaRPCMethodException("unregister device time out", exception.LEDA_ERROR_TIMEOUT)
663 | elif (False == syncMsg["state"]):
664 | raise exception.LedaRPCMethodException("unregister device failed", exception.LEDA_ERROR_FAILED)
665 |
666 | except:
667 | s = '%s:%s' % (funcname(), linenum())
668 | _logger.exception("%s,Err", s)
669 |
670 | def deviceRegister(self, deviceName, productKey, productTsl, deviceCallBack):
671 | '''注册设备并上线设备(设备默认注册后即上线)
672 | :param deviceName[string]: 由设备特征值组成的唯一描述信息, 必须保证每个待接入设备名称不同.
673 | :param productKey[string]: 产品唯一描述信息, 由阿里提供, 在设备 tsl 里也可以查得到.
674 | :param productTsl[string]: 设备tsl, 由阿里提供描述规范, 描述了设备的能力
675 | :param deviceCallBack[obj]: 设备回调方法
676 | :return:ledaSubDev[obj]
677 | '''
678 |
679 | if (False == isinstance(productTsl, str)):
680 | raise exception.LedaParamsException("deviceRegister: input args type is invalid")
681 |
682 | m = hashlib.md5()
683 | m.update(productTsl.encode('utf-8'))
684 | productMd5 = m.hexdigest()
685 |
686 | with self.deviceServiceDictLock:
687 | pk_dn = productKey + deviceName
688 | if (pk_dn in self.device_service_dict):
689 | s = '%s:%s' % (funcname(), linenum())
690 | _logger.warning("%s,device_name(%s) has already registered" % (s, deviceName))
691 | sudDevice = self.device_service_dict[pk_dn][1]
692 | sudDevice.online()
693 | else:
694 |
695 | sudDevice = self.driver_register_device(deviceName, productKey, productMd5, productTsl, deviceCallBack)
696 | sudDevice.online()
697 | self.device_service_dict[pk_dn] = [sudDevice.get_cloud_id(), sudDevice]
698 |
699 | return sudDevice
700 |
701 | def _addFileUpload_cb(self, msg):
702 | syncMsg = {}
703 | syncMsg["state"] = False
704 | syncMsg["msg"] = None
705 | s = '%s:%s' % (funcname(), linenum())
706 | _logger.debug("%s,addFileUpload return msg:%s", s, msg)
707 |
708 | try:
709 | code = json.loads(msg)['code']
710 | if (0 != code):
711 | s = '%s:%s' % (funcname(), linenum())
712 | _logger.warning("%s,rpc method: addFileUpload return code is error: %d" %
713 | (s, code))
714 | else:
715 |
716 | syncMsg["msg"] = code
717 | syncMsg["state"] = True
718 | except:
719 | s = '%s:%s' % (funcname(), linenum())
720 | _logger.exception("%s,Err", s)
721 | _logger.warning("%s,replyMsg is invalid", s)
722 |
723 | self.addFileUploadSync.set(syncMsg)
724 |
725 | def _addFileUpload_errCb(self, errMsg):
726 | s = '%s:%s' % (funcname(), linenum())
727 | _logger.warning("%s, addFileUpload return errmsg: %s" % (s, errMsg))
728 |
729 | syncMsg = {}
730 | syncMsg["state"] = False
731 | syncMsg["msg"] = None
732 | self.addFileUploadSync.set(syncMsg)
733 |
734 | def asyncAddFileUpload(self, fileType=0, fileList=[]):
735 | '''
736 |
737 | :param fileType[int]: 0: 配置, 1: 日志
738 | :param fileList[list]:文件list
739 | return 0: success, others: failed
740 | '''
741 |
742 | if (None == self.driverMbusHandle):
743 | raise exception.LedaBusHandleException("mbus Handle is None")
744 |
745 | if ((fileType not in [0, 1]) or (False == isinstance(fileList, list))):
746 | raise exception.LedaParamsException("asyncAddFileUpload: input args type is invalid")
747 |
748 | reply_cb = self._addFileUpload_cb
749 | error_cb = self._addFileUpload_errCb
750 | fileList_str = ','.join(fileList)
751 |
752 | self.addFileUploadSync.clear()
753 | self.driverMbusHandle.addFileUpload(fileType, fileList_str, reply_cb, error_cb)
754 | syncMsg = self.addFileUploadSync.wait(mbusConfig.MERHOD_SYNC_TIMEOUT)
755 | if (None == syncMsg):
756 | raise exception.LedaRPCMethodException("addFileUpload time out", exception.LEDA_ERROR_TIMEOUT)
757 | elif (False == syncMsg["state"]):
758 | raise exception.LedaRPCMethodException("addFileUpload failed", exception.LEDA_ERROR_FAILED)
759 | else:
760 | info = syncMsg["msg"]
761 |
762 | return info
763 |
764 | def subFileUploadResult(self, callBack):
765 | if (None == self.driverMbusHandle):
766 | raise exception.LedaBusHandleException("mbus Handle is None")
767 |
768 | if (False == isinstance(callBack, fileUploadResultCallback)):
769 | raise exception.LedaCallBackException("bus callback object is invalid")
770 |
771 | remoteBusName = mbusConfig.DMP_FU_WKN
772 | remoteObjPath = mbusConfig.DMP_FU_OBJ_PATH
773 | remoteInterface = None
774 | CallBackMethod = callBack.fileUploadResult
775 | signalName = 'fileUploadResult'
776 | self.driverMbusHandle.subNotice(remoteBusName, remoteObjPath, remoteInterface, CallBackMethod, signalName)
777 |
--------------------------------------------------------------------------------
/leda_python/ledaException.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 |
5 | # /*返回状态*/
6 |
7 | LEDA_SUCCESS = 0 # 执行成功
8 | LEDA_ERROR_FAILED = 100000 # 执行失败
9 | LEDA_ERROR_INVAILD_PARAM = 100001 # 无效参数
10 | LEDA_ERROR_NO_MEM = 100002 # 没有内存
11 | LEDA_ERROR_TIMEOUT = 100006 # 超时
12 | LEDA_ERROR_NOT_SUPPORT = 100008 # 不支持
13 | LEDA_ERROR_PROPERTY_NOT_EXIST = 109002 # 属性不存在
14 | LEDA_ERROR_PROPERTY_READ_ONLY = 109003 # 属性不允许写
15 | LEDA_ERROR_PROPERTY_WRITE_ONLY = 109004 # 属性不允许读
16 | LEDA_ERROR_SERVICE_NOT_EXIST = 109005 # 服务不存在
17 | LEDA_ERROR_SERVICE_INPUT_PARAM = 109006 # 服务参数未验证
18 |
19 |
20 | class LedaException(Exception):
21 | '''
22 | base leda exception
23 | '''
24 |
25 | def __init__(self, msg="", value=LEDA_ERROR_FAILED):
26 | '''
27 | Initialize the exception
28 | :param value: the err code
29 | :param msg: the err msg
30 | '''
31 | self.value = value
32 | self.msg = msg
33 |
34 | def __str__(self):
35 | '''
36 | return the exception message
37 | :return str:
38 | '''
39 |
40 | return "Err: %s ErrCode: %s" % (str(self.msg), self.value)
41 |
42 | __repr__ = __str__
43 |
44 |
45 | class LedaParamsException(LedaException):
46 | ''' leda params error'''
47 |
48 | def __init__(self, msg="", value=LEDA_ERROR_FAILED):
49 | '''Initialize the exception
50 | :param value: the err code
51 | :param msg: the err msg
52 | '''
53 |
54 | message = "[LedaParams] %s" % (str(msg))
55 | LedaException.__init__(self, message, value)
56 |
57 |
58 | class LedaRPCMethodException(LedaException):
59 | ''' leda rpc method error'''
60 |
61 | def __init__(self, msg="", value=LEDA_ERROR_FAILED):
62 | '''Initialize the exception
63 | :param value: the err code
64 | :param msg: the err msg
65 | '''
66 |
67 | message = "[LedaRPCMethod] %s" % (str(msg))
68 | LedaException.__init__(self, message, value)
69 |
70 |
71 | class LedaBusHandleException(LedaException):
72 | ''' leda busHandle error'''
73 |
74 | def __init__(self, msg="", value=LEDA_ERROR_FAILED):
75 | '''Initialize the exception
76 | :param value: the err code
77 | :param msg: the err msg
78 | '''
79 |
80 | message = "[LedaBusHandle] %s" % (str(msg))
81 | LedaException.__init__(self, message, value)
82 |
83 |
84 | class LedaFeedDogException(LedaException):
85 | ''' leda feed dog error'''
86 |
87 | def __init__(self, msg="", value=LEDA_ERROR_FAILED):
88 | '''Initialize the exception
89 | :param value: the err code
90 | :param msg: the err msg
91 | '''
92 |
93 | message = "[LedaFeedDog] %s" % (str(msg))
94 | LedaException.__init__(self, message, value)
95 |
96 |
97 | class LedaCallBackException(LedaException):
98 | ''' leda call back error'''
99 |
100 | def __init__(self, msg="", value=LEDA_ERROR_FAILED):
101 | '''Initialize the exception
102 | :param value: the err code
103 | :param msg: the err msg
104 | '''
105 |
106 | message = "[LedaCallBack] %s" % (str(msg))
107 | LedaException.__init__(self, message, value)
108 |
109 |
110 | class LedaReportPropertyException(LedaException):
111 | ''' leda report property error'''
112 |
113 | def __init__(self, msg="", value=LEDA_ERROR_FAILED):
114 | '''Initialize the exception
115 | :param value: the err code
116 | :param msg: the err msg
117 | '''
118 |
119 | message = "[LedaReportProperty] %s" % (str(msg))
120 | LedaException.__init__(self, message, value)
121 |
122 |
123 | class LedaReportEventException(LedaException):
124 | ''' leda report event error'''
125 |
126 | def __init__(self, msg="", value=LEDA_ERROR_FAILED):
127 | '''Initialize the exception
128 | :param value: the err code
129 | :param msg: the err msg
130 | '''
131 |
132 | message = "[LedaReportEvent] %s" % (str(msg))
133 | LedaException.__init__(self, message, value)
134 |
--------------------------------------------------------------------------------
/leda_python/mbus.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 |
4 | from dbus.lowlevel import SignalMessage
5 | from dbus.bus import BusConnection
6 | from . import ledaException
7 | from .refactoring.dbus_connection import DbusConnection
8 | import dbus.service
9 | from . import mbusConfig
10 | import logging
11 | import dbus
12 | import json
13 | import sys
14 |
15 | _logger = logging.getLogger(__name__)
16 |
17 |
18 | class Mbus(DbusConnection):
19 |
20 | def close(self):
21 | super(Mbus, self).close()
22 |
23 |
24 | class MbusBase(object):
25 | _shareBus = None
26 |
27 | def __init__(self, wellKnownName, shareFlag=True, **kwargs):
28 | self.mbusNameObj = None
29 |
30 | try:
31 | if ((None != self.__class__._shareBus) and (True == shareFlag)):
32 | bus = self.__class__._shareBus
33 | else:
34 | bus = Mbus(**kwargs)
35 | MbusBase._shareBus = bus
36 |
37 | if (bus.name_has_owner(wellKnownName)):
38 | logger.warning("<><><><><><><> bus name: %s has existed " % (wellKnownName))
39 | bus.close()
40 | sys.exit(0)
41 | _logger.info("mbus connect successfully")
42 |
43 | except:
44 | raise ledaException.LedaException("dbus daemon is not found", ledaException.LEDA_ERROR_FAILED)
45 |
46 |
47 |
48 | busName = dbus.service.BusName(wellKnownName, bus) # request name
49 | self.mbusNameObj = busName
50 | _logger.info("mbus request name:%s" % (wellKnownName))
51 |
52 | def getBus(self):
53 | if (None == self.mbusNameObj):
54 | raise ledaException.LedaException("mbus name object is None")
55 |
56 | return self.mbusNameObj.get_bus()
57 |
58 | def getName(self):
59 | '''
60 | get the well known name
61 | :return:
62 | '''
63 | return self.mbusNameObj.get_name()
64 |
65 | def createObject(self, MbusObjectClass, objPath):
66 | dbusHandle = self.getBus()
67 |
68 | objectInstance = MbusObjectClass(dbusHandle, objPath)
69 |
70 | return objectInstance
71 |
72 | def releaseName(self):
73 | _logger.info("release mbusName: %s", self.getName())
74 | bus = self.getBus()
75 | bus.release_name(self.getName())
76 | self.mbusNameObj = None
77 |
78 | def getRemoteInterface(self, remoteBusName, remoteObjPath, remoteInterface):
79 | dbusHandle = self.getBus()
80 |
81 | objectHandle = dbusHandle.get_object(remoteBusName, remoteObjPath)
82 | interfaceHandle = dbus.Interface(objectHandle, remoteInterface)
83 |
84 | return interfaceHandle
85 |
86 | def addSignalReceiver(self, remoteBusName, remoteObjPath, remoteInterface, CallBackMethod, signalName):
87 |
88 | dbusHandle = self.getBus()
89 | dbusHandle.add_signal_receiver(CallBackMethod, bus_name=remoteBusName,
90 | path=remoteObjPath, dbus_interface=remoteInterface,
91 | signal_name=signalName)
92 |
93 | def unicastSignal(self, srcObjPath, srcInterface, desWellKnownName, signature, member, *args):
94 |
95 | msg = SignalMessage(srcObjPath, srcInterface, member)
96 | msg.set_destination(desWellKnownName)
97 | msg.append(signature=signature, *args)
98 | self.getBus().send_message(msg)
99 |
100 |
101 | class MbusConnect(MbusBase):
102 | def __init__(self, driver_wellKnownName):
103 |
104 | super(MbusConnect, self).__init__(driver_wellKnownName)
105 | self.bus = self.getBus()
106 |
107 | def feedDog(self, *args):
108 | if (None == self.mbusNameObj):
109 | raise ledaException.LedaException("driver mbus name object is None")
110 |
111 | wellKonwName = self.getName()
112 | objectPath = '/' + wellKonwName.replace('.', '/')
113 | argsTmp = []
114 | argsTmp.append(wellKonwName)
115 | for item in args: argsTmp.append(item)
116 | self.unicastSignal(objectPath, mbusConfig.DMP_WATCHDOG_WKN, mbusConfig.DMP_WATCHDOG_WKN, "ssi", "feedDog",
117 | *argsTmp)
118 |
119 | def unregisterDevice(self, cloudId, reply_cb=None, error_cb=None):
120 |
121 | if (None == self.mbusNameObj):
122 | raise ledaException.LedaException(" mbus name object is None")
123 |
124 | try:
125 | interfaceHandle = self.getRemoteInterface(mbusConfig.DMP_DIMU_WKN,
126 | mbusConfig.DMP_DIMU_OBJECT_PATH, mbusConfig.DMP_DIMU_INTERFACE)
127 |
128 | _logger.debug("unregisterDevice cloudId: %s" % (cloudId))
129 | interfaceHandle.unregisterDevice(cloudId, reply_handler=reply_cb, error_handler=error_cb,
130 | timeout=mbusConfig.METHOD_ACK_TIMEOUT)
131 | except dbus.exceptions.DBusException as err:
132 | _logger.warning('%s', err)
133 | raise ledaException.LedaRPCMethodException("rpc method: unregisterDevice failed",
134 | ledaException.LEDA_ERROR_FAILED)
135 |
136 | def getConfig(self, key, reply_cb=None, error_cb=None):
137 | '''获取配置
138 | :param key[string]: 配置名
139 | :param reply_cb: async reply call back
140 | :param error_cb: async error call back
141 | :return:
142 | '''
143 |
144 | if (None == self.mbusNameObj):
145 | raise ledaException.LedaException(" mbus name object is None")
146 |
147 | try:
148 | interfaceHandle = self.getRemoteInterface(mbusConfig.DMP_CM_WKN,
149 | mbusConfig.DMP_CM_OBJ_PATH, mbusConfig.DMP_CM_INTERFACE)
150 |
151 | interfaceHandle.get_config(key, reply_handler=reply_cb, error_handler=error_cb,
152 | timeout=mbusConfig.METHOD_ACK_TIMEOUT)
153 |
154 | except dbus.exceptions.DBusException as err:
155 | _logger.warning('%s', err)
156 | raise ledaException.LedaRPCMethodException("rpc method: get_config failed", ledaException.LEDA_ERROR_FAILED)
157 |
158 | def setConfig(self, key, value, reply_cb=None, error_cb=None):
159 | '''设置配置
160 | :param key: 配置名
161 | :param value: 配置内容
162 | :param reply_cb: async reply call back
163 | :param error_cb: async error call back
164 | :return
165 | '''
166 |
167 | if (None == self.mbusNameObj):
168 | raise ledaException.LedaException(" mbus name object is None")
169 |
170 | try:
171 | interfaceHandle = self.getRemoteInterface(mbusConfig.DMP_CM_WKN,
172 | mbusConfig.DMP_CM_OBJ_PATH, mbusConfig.DMP_CM_INTERFACE)
173 |
174 | interfaceHandle.set_config(key, value, reply_handler=reply_cb, error_handler=error_cb,
175 | timeout=mbusConfig.METHOD_ACK_TIMEOUT)
176 |
177 | except dbus.exceptions.DBusException as err:
178 | _logger.warning('%s', err)
179 | raise ledaException.LedaRPCMethodException("rpc method: set_config failed", ledaException.LEDA_ERROR_FAILED)
180 |
181 | def subscribeConfig(self, key, type, reply_cb=None, error_cb=None):
182 | ''' 订阅配置
183 |
184 | :param key[string]: 配置名
185 | :param type[int]: 订阅类型(0.拥有者,1.观察者)
186 | :param reply_cb: async reply call back
187 | :param error_cb: async error call back
188 | :return:
189 | '''
190 |
191 | driverWKN = self.getName()
192 |
193 | if (None == self.mbusNameObj):
194 | raise ledaException.LedaException(" mbus name object is None")
195 |
196 | try:
197 | interfaceHandle = self.getRemoteInterface(mbusConfig.DMP_CM_WKN,
198 | mbusConfig.DMP_CM_OBJ_PATH, mbusConfig.DMP_CM_INTERFACE)
199 |
200 | interfaceHandle.subscribe_config(driverWKN, key, type, reply_handler=reply_cb, error_handler=error_cb,
201 | timeout=mbusConfig.METHOD_ACK_TIMEOUT)
202 |
203 | except dbus.exceptions.DBusException as err:
204 | _logger.warning('%s', err)
205 | raise ledaException.LedaRPCMethodException("rpc method: subscribe_config failed",
206 | ledaException.LEDA_ERROR_FAILED)
207 |
208 | def unsubscribeConfig(self, key, reply_cb=None, error_cb=None):
209 | '''
210 | :param key: key[string]: 配置名
211 | :param reply_cb:
212 | :param error_cb:
213 | :return:
214 | '''
215 | driverWKN = self.getName()
216 |
217 | if (None == self.mbusNameObj):
218 | raise ledaException.LedaException(" mbus name object is None")
219 |
220 | try:
221 | interfaceHandle = self.getRemoteInterface(mbusConfig.DMP_CM_WKN,
222 | mbusConfig.DMP_CM_OBJ_PATH, mbusConfig.DMP_CM_INTERFACE)
223 |
224 | interfaceHandle.unsubscribe_config(driverWKN, key, reply_handler=reply_cb, error_handler=error_cb,
225 | timeout=mbusConfig.METHOD_ACK_TIMEOUT)
226 |
227 | except dbus.exceptions.DBusException as err:
228 | _logger.warning('%s', err)
229 | raise ledaException.LedaRPCMethodException("rpc method: unsubscribe_config failed",
230 | ledaException.LEDA_ERROR_FAILED)
231 |
232 | def registerDriver(self, inMsg, reply_cb=None, error_cb=None):
233 |
234 | dataJson = inMsg
235 |
236 | if (None == self.mbusNameObj):
237 | raise ledaException.LedaException(" mbus name object is None")
238 |
239 | try:
240 | interfaceHandle = self.getRemoteInterface(mbusConfig.DMP_DIMU_WKN,
241 | mbusConfig.DMP_DIMU_OBJECT_PATH, mbusConfig.DMP_DIMU_INTERFACE)
242 |
243 | _logger.debug("registerDriver in params: %s" % (dataJson))
244 | interfaceHandle.registerDriver(dataJson, reply_handler=reply_cb, error_handler=error_cb,
245 | timeout=mbusConfig.METHOD_ACK_TIMEOUT)
246 | except dbus.exceptions.DBusException as err:
247 | _logger.warning('%s', err)
248 | raise ledaException.LedaRPCMethodException("rpc method: registerDriver failed",
249 | ledaException.LEDA_ERROR_FAILED)
250 |
251 | def unregisterDriver(self, inMsg, reply_cb=None, error_cb=None):
252 |
253 | dataJson = inMsg
254 |
255 | if (None == self.mbusNameObj):
256 | raise ledaException.LedaException(" mbus name object is None")
257 |
258 | try:
259 | interfaceHandle = self.getRemoteInterface(mbusConfig.DMP_DIMU_WKN,
260 | mbusConfig.DMP_DIMU_OBJECT_PATH, mbusConfig.DMP_DIMU_INTERFACE)
261 |
262 | _logger.debug("unregisterDriver in params: %s" % (dataJson))
263 | interfaceHandle.unregisterDriver(dataJson, reply_handler=reply_cb, error_handler=error_cb,
264 | timeout=mbusConfig.METHOD_ACK_TIMEOUT)
265 | except dbus.exceptions.DBusException as err:
266 | _logger.warning('%s', err)
267 | raise ledaException.LedaRPCMethodException("rpc method: unregisterDriver failed",
268 | ledaException.LEDA_ERROR_FAILED)
269 |
270 | def addFileUpload(self, fileType=0, fileList='', reply_cb=None, error_cb=None):
271 | '''
272 | :param fileType[int]: 0: 配置, 1: 日志
273 | :param fileList[string]:文件名,逗号分割
274 | :param reply_cb:
275 | :param error_cb:
276 | :return:
277 | '''
278 |
279 | if (None == self.mbusNameObj):
280 | raise ledaException.LedaException(" mbus name object is None")
281 |
282 | try:
283 | interfaceHandle = self.getRemoteInterface(mbusConfig.DMP_FU_WKN,
284 | mbusConfig.DMP_FU_OBJ_PATH, mbusConfig.DMP_FU_INTERFACE)
285 |
286 | _logger.debug("addFileUpload in params: %s" % (fileList))
287 | interfaceHandle.addFileUpload(fileType, fileList, reply_handler=reply_cb, error_handler=error_cb,
288 | timeout=mbusConfig.METHOD_ACK_TIMEOUT)
289 | except dbus.exceptions.DBusException as err:
290 | _logger.warning('%s', err)
291 | raise ledaException.LedaRPCMethodException("rpc method: addFileUpload failed",
292 | ledaException.LEDA_ERROR_FAILED)
293 |
294 | def subNotice(self, remoteBusName, remoteObjPath, remoteInterface, CallBackMethod, signalName):
295 | if (None == self.mbusNameObj):
296 | raise ledaException.LedaException(" mbus name object is None")
297 |
298 | self.addSignalReceiver(remoteBusName, remoteObjPath, remoteInterface, CallBackMethod, signalName)
299 |
300 | def close(self):
301 |
302 | if (None == self.__class__._shareBus):
303 | raise ledaException.LedaException("connection object is already released")
304 | if (self.mbusNameObj):
305 | self.releaseName()
306 |
307 | self.bus.close()
308 | self.__class__._shareBus = None
309 |
310 |
311 | class DeviceMbus(MbusBase):
312 | def __init__(self, wellKnownName):
313 |
314 | if (None == MbusBase._shareBus):
315 | raise ledaException.LedaException("Err: you must init bus firstly")
316 |
317 | super(DeviceMbus, self).__init__(wellKnownName)
318 |
319 | def connect(self, dataJson, reply_cb, error_cb):
320 | '''rpc method: connect
321 | 功能等同于 startupDevice
322 | inParams: arg
323 | reply_cb: async reply call back
324 | error_cb: async reply call back
325 | '''
326 |
327 | try:
328 | interfaceHandle = self.getRemoteInterface(mbusConfig.DMP_DIMU_WKN,
329 | mbusConfig.DMP_DIMU_OBJECT_PATH, mbusConfig.DMP_DIMU_INTERFACE)
330 |
331 | interfaceHandle.connect(dataJson, reply_handler=reply_cb, error_handler=error_cb,
332 | timeout=mbusConfig.METHOD_ACK_TIMEOUT)
333 | except dbus.exceptions.DBusException as err:
334 | _logger.warning('%s', err)
335 | raise ledaException.LedaRPCMethodException("rpc method: connect failed", ledaException.LEDA_ERROR_FAILED)
336 |
337 | def disconnect(self, dataJson, reply_cb, error_cb):
338 | '''rpc method: shutdownDevice
339 | 功能等同于shutdownDevice
340 | inParms: dataJson
341 | reply_cb: async reply call back
342 | error_cb: async reply call back
343 | '''
344 |
345 | try:
346 | interfaceHandle = self.getRemoteInterface(mbusConfig.DMP_DIMU_WKN,
347 | mbusConfig.DMP_DIMU_OBJECT_PATH, mbusConfig.DMP_DIMU_INTERFACE)
348 |
349 | interfaceHandle.disconnect(dataJson, reply_handler=reply_cb, error_handler=error_cb,
350 | timeout=mbusConfig.METHOD_ACK_TIMEOUT)
351 | except dbus.exceptions.DBusException as err:
352 | _logger.warning('%s', err)
353 | raise ledaException.LedaRPCMethodException("rpc method: disconnect failed", ledaException.LEDA_ERROR_FAILED)
354 |
--------------------------------------------------------------------------------
/leda_python/mbusConfig.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 |
4 |
5 | DBUS_ADDRESS = "unix:path=/tmp/var/run/mbusd/mbusd_socket"
6 |
7 | '''
8 | DMP-DIMU
9 | '''
10 | DMP_DIMU_WKN = "iot.dmp.dimu"
11 | DMP_DIMU_OBJECT_PATH = "/iot/dmp/dimu"
12 | DMP_DIMU_INTERFACE = DMP_DIMU_WKN
13 |
14 | DMP_SUB_WKN = "iot.dmp.subscribe"
15 |
16 | # watchdog
17 | '''
18 | DMP-WATCHDOG
19 | '''
20 | DMP_WATCHDOG_WKN = "iot.gateway.watchdog"
21 | DMP_WATCHDOG_INTERFACE = DMP_WATCHDOG_WKN
22 |
23 | '''
24 | DMP-ConfigManager
25 | '''
26 | DMP_CM_WKN = 'iot.dmp.configmanager'
27 | DMP_CM_OBJ_PATH = '/iot/dmp/configmanager'
28 | DMP_CM_INTERFACE = DMP_CM_WKN
29 |
30 | '''
31 | DMP-FU(fileUpload)
32 | '''
33 | DMP_FU_WKN = 'iot.gateway.fileUploader'
34 | DMP_FU_OBJ_PATH = '/iot/gateway/fileUploader'
35 | DMP_FU_INTERFACE = DMP_FU_WKN
36 |
37 | CMP_DEVICE_WKN_PREFIX = "iot.device.id"
38 | CMP_DRIVER_WKN_PREFIX = "iot.driver.id"
39 |
40 | IS_LOCAL_FLAG = "False" # "True" or "False"
41 | STRING_NAME_MAX_LEN = 64
42 |
43 | # method response timeout
44 | METHOD_ACK_TIMEOUT = 8 # second
45 | MERHOD_SYNC_TIMEOUT = METHOD_ACK_TIMEOUT + 0.5
46 |
47 | # thread pool
48 | HREAD_POOL_THREAD_NUM = 5
49 | HREAD_POOL_QUEUE_NUM = 50
50 |
--------------------------------------------------------------------------------
/leda_python/refactoring/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliyun/linkedge-thing-access-sdk-python/131a8f5112b64229fe21b48738157b624ecf8c34/leda_python/refactoring/__init__.py
--------------------------------------------------------------------------------
/leda_python/refactoring/dbus_connection.py:
--------------------------------------------------------------------------------
1 | #!/usr/bine/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | from _dbus_bindings import (LOCAL_IFACE, LOCAL_PATH)
5 | from dbus.exceptions import DBusException
6 | from dbus.lowlevel import (
7 | ErrorMessage, MethodCallMessage,
8 | MethodReturnMessage, SignalMessage)
9 | from dbus._compat import is_py2, is_py3
10 | from dbus.bus import BusConnection
11 |
12 | if is_py3:
13 | from _dbus_bindings import String
14 | else:
15 | from _dbus_bindings import UTF8String
16 |
17 | from .. import mbusConfig
18 |
19 | import logging
20 |
21 | _logger = logging.getLogger(__name__)
22 |
23 |
24 | class DbusConnection(BusConnection):
25 |
26 | def __new__(cls, mainLoop=None):
27 | bus = BusConnection.__new__(cls, mbusConfig.DBUS_ADDRESS, mainloop=mainLoop)
28 | return bus
29 |
30 | def call_async(self, bus_name, object_path, dbus_interface, method,
31 | signature, args, reply_handler, error_handler,
32 | timeout=-1.0, byte_arrays=False,
33 | require_main_loop=True, **kwargs):
34 | """Call the given method, asynchronously.
35 |
36 | If the reply_handler is None, successful replies will be ignored.
37 | If the error_handler is None, failures will be ignored. If both
38 | are None, the implementation may request that no reply is sent.
39 |
40 | :Returns: The dbus.lowlevel.PendingCall.
41 | :Since: 0.81.0
42 | """
43 | if object_path == LOCAL_PATH:
44 | raise DBusException('Methods may not be called on the reserved '
45 | 'path %s' % LOCAL_PATH)
46 | if dbus_interface == LOCAL_IFACE:
47 | raise DBusException('Methods may not be called on the reserved '
48 | 'interface %s' % LOCAL_IFACE)
49 | # no need to validate other args - MethodCallMessage ctor will do
50 |
51 | get_args_opts = dict(byte_arrays=byte_arrays)
52 | if is_py2:
53 | get_args_opts['utf8_strings'] = kwargs.get('utf8_strings', False)
54 | elif 'utf8_strings' in kwargs:
55 | raise TypeError("unexpected keyword argument 'utf8_strings'")
56 |
57 | message = MethodCallMessage(destination=bus_name,
58 | path=object_path,
59 | interface=dbus_interface,
60 | method=method)
61 | # Add the arguments to the function
62 | try:
63 | message.append(signature=signature, *args)
64 | except Exception as e:
65 | logging.basicConfig()
66 | _logger.error('Unable to set arguments %r according to '
67 | 'signature %r: %s: %s',
68 | args, signature, e.__class__, e)
69 | raise
70 |
71 | if reply_handler is None and error_handler is None:
72 | # we don't care what happens, so just send it
73 | self.send_message(message)
74 | return
75 |
76 | if reply_handler is None:
77 | reply_handler = _noop
78 | if error_handler is None:
79 | error_handler = _noop
80 |
81 | def msg_reply_handler(message):
82 | if isinstance(message, MethodReturnMessage):
83 | reply_handler(*message.get_args_list(**get_args_opts))
84 | elif isinstance(message, ErrorMessage):
85 | error_handler(DBusException(name=message.get_error_name(),
86 | *message.get_args_list()))
87 | else:
88 | error_handler(TypeError('Unexpected type for reply '
89 | 'message: %r' % message))
90 |
91 | retInfo = self.send_message_with_reply(message, msg_reply_handler,
92 | timeout,
93 | require_main_loop=require_main_loop)
94 | self.flush()
95 | return retInfo
96 |
--------------------------------------------------------------------------------
/leda_python/refactoring/dbus_service.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | import dbus.service
5 | from . import thread
6 | from .. import mbusConfig
7 |
8 | import logging
9 | from collections import Sequence
10 | from dbus.service import _method_lookup, _method_reply_return, _method_reply_error
11 | from dbus import (ObjectPath, Signature, Struct)
12 | from dbus.lowlevel import MethodCallMessage
13 |
14 | _logger = logging.getLogger(__name__)
15 |
16 |
17 | class DbusObject(dbus.service.Object):
18 | s_threadPool = thread.ThreadPool(mbusConfig.HREAD_POOL_THREAD_NUM, mbusConfig.HREAD_POOL_QUEUE_NUM)
19 |
20 | def _message_cb(self, connection, message):
21 | if not isinstance(message, MethodCallMessage):
22 | return
23 |
24 | _logger.debug('method: %s', message.get_member())
25 | self.__class__.s_threadPool.addTask(self._message_cb_2, connection=connection, message=message)
26 |
27 | def _message_cb_2(self, connection, message):
28 | try:
29 | # lookup candidate method and parent method
30 | method_name = message.get_member()
31 | interface_name = message.get_interface()
32 | (candidate_method, parent_method) = _method_lookup(self, method_name, interface_name)
33 |
34 | # set up method call parameters
35 | args = message.get_args_list(**parent_method._dbus_get_args_options)
36 | keywords = {}
37 |
38 | if parent_method._dbus_out_signature is not None:
39 | signature = Signature(parent_method._dbus_out_signature)
40 | else:
41 | signature = None
42 |
43 | # set up async callback functions
44 | if parent_method._dbus_async_callbacks:
45 | (return_callback, error_callback) = parent_method._dbus_async_callbacks
46 | keywords[return_callback] = lambda *retval: _method_reply_return(connection, message, method_name,
47 | signature, *retval)
48 | keywords[error_callback] = lambda exception: _method_reply_error(connection, message, exception)
49 |
50 | # include the sender etc. if desired
51 | if parent_method._dbus_sender_keyword:
52 | keywords[parent_method._dbus_sender_keyword] = message.get_sender()
53 | if parent_method._dbus_path_keyword:
54 | keywords[parent_method._dbus_path_keyword] = message.get_path()
55 | if parent_method._dbus_rel_path_keyword:
56 | path = message.get_path()
57 | rel_path = path
58 | for exp in self._locations:
59 | # pathological case: if we're exported in two places,
60 | # one of which is a subtree of the other, then pick the
61 | # subtree by preference (i.e. minimize the length of
62 | # rel_path)
63 | if exp[0] is connection:
64 | if path == exp[1]:
65 | rel_path = '/'
66 | break
67 | if exp[1] == '/':
68 | # we already have rel_path == path at the beginning
69 | continue
70 | if path.startswith(exp[1] + '/'):
71 | # yes we're in this exported subtree
72 | suffix = path[len(exp[1]):]
73 | if len(suffix) < len(rel_path):
74 | rel_path = suffix
75 | rel_path = ObjectPath(rel_path)
76 | keywords[parent_method._dbus_rel_path_keyword] = rel_path
77 |
78 | if parent_method._dbus_destination_keyword:
79 | keywords[parent_method._dbus_destination_keyword] = message.get_destination()
80 | if parent_method._dbus_message_keyword:
81 | keywords[parent_method._dbus_message_keyword] = message
82 | if parent_method._dbus_connection_keyword:
83 | keywords[parent_method._dbus_connection_keyword] = connection
84 |
85 | # call method
86 | retval = candidate_method(self, *args, **keywords)
87 |
88 | # we're done - the method has got callback functions to reply with
89 | if parent_method._dbus_async_callbacks:
90 | return
91 |
92 | # otherwise we send the return values in a reply. if we have a
93 | # signature, use it to turn the return value into a tuple as
94 | # appropriate
95 | if signature is not None:
96 | signature_tuple = tuple(signature)
97 | # if we have zero or one return values we want make a tuple
98 | # for the _method_reply_return function, otherwise we need
99 | # to check we're passing it a sequence
100 | if len(signature_tuple) == 0:
101 | if retval == None:
102 | retval = ()
103 | else:
104 | raise TypeError('%s has an empty output signature but did not return None' %
105 | method_name)
106 | elif len(signature_tuple) == 1:
107 | retval = (retval,)
108 | else:
109 | if isinstance(retval, Sequence):
110 | # multi-value signature, multi-value return... proceed
111 | # unchanged
112 | pass
113 | else:
114 | raise TypeError('%s has multiple output values in signature %s but did not return a sequence' %
115 | (method_name, signature))
116 |
117 | # no signature, so just turn the return into a tuple and send it as normal
118 | else:
119 | if retval is None:
120 | retval = ()
121 | elif (isinstance(retval, tuple)
122 | and not isinstance(retval, Struct)):
123 | # If the return is a tuple that is not a Struct, we use it
124 | # as-is on the assumption that there are multiple return
125 | # values - this is the usual Python idiom. (fd.o #10174)
126 | pass
127 | else:
128 | retval = (retval,)
129 |
130 | _method_reply_return(connection, message, method_name, signature, *retval)
131 | except Exception as exception:
132 | # send error reply
133 | _method_reply_error(connection, message, exception)
134 |
--------------------------------------------------------------------------------
/leda_python/refactoring/thread.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:-utf-8 -*-
3 |
4 | import queue
5 | import threading
6 | import logging
7 |
8 | _logger = logging.getLogger('dbus_threadPool')
9 |
10 |
11 | class Thread(threading.Thread):
12 | '''
13 | 定义一个能够处理任务的线程类,属于自定义线程类,自定义线程类就需要定义run()函数
14 | '''
15 |
16 | def __init__(self, taskQueue):
17 | threading.Thread.__init__(self)
18 | self.taskQueue = taskQueue
19 | self.stopFlag = False
20 | self.setDaemon(True)
21 | self.start()
22 |
23 | def enableStopFlag(self):
24 | self.stopFlag = True
25 |
26 | def run(self):
27 | '''
28 | 重构run 方法
29 | '''
30 | while (True):
31 | if (True == self.stopFlag):
32 | break
33 |
34 | try:
35 | _logger.debug("")
36 | _logger.debug("getting ruleTask from dbusThreadPool queue...")
37 | func, kwargs = self.taskQueue.get(block=True, timeout=10)
38 | func(**kwargs)
39 | except queue.Empty:
40 | _logger.debug("dbusThreadPool Queue is empty and have no ruleTask in the queue")
41 | except:
42 | _logger.exception("dbusThreadPool: task exec err#####")
43 |
44 |
45 | class ThreadPool(object):
46 | '''
47 | 自定义线程池
48 | '''
49 |
50 | def __init__(self, threadNum, taskNum):
51 | self.threadList = []
52 | self.taskQueue = queue.Queue(maxsize=taskNum)
53 | self._init_threadPool(threadNum)
54 |
55 | def _init_threadPool(self, threadNum):
56 | for i in range(threadNum):
57 | thread = Thread(self.taskQueue)
58 | self.threadList.append(thread)
59 |
60 | def addTask(self, func, **kwargs):
61 |
62 | try:
63 | self.taskQueue.put((func, kwargs), block=False)
64 | except queue.Full:
65 | _logger.warning("dbusThreaPool Queue is overflowed")
66 | pass
67 |
68 | def releasThread(self):
69 | """
70 | 关闭线程池中所有的线程
71 | """
72 | for item in self.threadList:
73 | if (item.isAlive()):
74 | item.enableStopFlag()
75 | item.join()
76 |
77 |
78 | # 函数名称: createThread
79 | # 功能描述: 创建独立线程
80 | # 输入参数:
81 | # task: 任务
82 | # args: 任务的参数
83 | # 返 回 值: 无
84 | def createThread(task, args=()):
85 | t = threading.Thread(target=task, args=args)
86 | t.setDaemon(True)
87 | t.start()
88 |
--------------------------------------------------------------------------------
/lethingaccesssdk/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliyun/linkedge-thing-access-sdk-python/131a8f5112b64229fe21b48738157b624ecf8c34/lethingaccesssdk/.DS_Store
--------------------------------------------------------------------------------
/lethingaccesssdk/__init__.py:
--------------------------------------------------------------------------------
1 | from .thing_access import ThingAccessClient
2 | from .thing_access import ThingCallback
3 | from .thing_access import getConfig
4 | from .thing_access import Config
5 |
--------------------------------------------------------------------------------
/lethingaccesssdk/thing_access.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | import os
4 | import json
5 | import logging
6 | from leda_python import leda
7 |
8 |
9 | class ThingCallback(leda.BaseDeviceCallback):
10 | '''
11 | 根据真实设备,命名一个类(如Demo_device)继承ThingCallback。
12 | 然后在Demo_device中实现callServiceset, getProperties和setProperties三个函数。
13 | '''
14 |
15 | def callService(self, name, input):
16 | '''
17 | 调用设备服务函数
18 | parameter:
19 | param name[string]: 方法名
20 | param input[dict]: 方法参数, eg:
21 | {
22 | 'args1': 'xxx',
23 | 'args2': 'yyy',
24 | ...
25 | }
26 | return:
27 | code[int]: 若获取成功则返回LEDA_SUCCESS, 失败则返回错误码
28 | output[dict]: 返回值, eg:
29 | {
30 | 'key1': 'xxx',
31 | 'key2': 'yyy',
32 | ...
33 | }
34 | '''
35 | raise Exception("callService is empty")
36 |
37 | def getProperties(self, input):
38 | '''
39 | 获取设备属性函数
40 | param input[list]: 获取属性列表,eg:[property1,property2 ...]
41 | return:
42 | code[int]: 若获取成功则返回LEDA_SUCCESS, 失败则返回错误码
43 | output[dict]: 属性返回值, eg:
44 | {
45 | 'property1': 'xxx',
46 | 'property2': 'yyy',
47 | ...
48 | }
49 | '''
50 | raise Exception("getProperties is empty")
51 |
52 | def setProperties(self, input):
53 | '''
54 | 设置设备属性的函数
55 | param input[dict]:设置属性值, eg:
56 | {
57 | 'property1':'xxx',
58 | 'property2':'yyy',
59 | ...
60 | }
61 | return:
62 | code[int]: 若获取成功则返回LEDA_SUCCESS, 失败则返回错误码
63 | output[dict]: 数据内容自定义,若无返回数据,则值空:{}
64 | '''
65 | raise Exception("setProperties is empty")
66 |
67 |
68 | class ThingAccessClient(object):
69 | '''
70 | 设备接入客户端类,用户主要通过它操作设备上下线和主动上报设备属性或事件
71 | '''
72 |
73 | def __init__(self, config=None):
74 | '''
75 | 构造函数,使用设备的指定的ProductKey和DeviceName
76 | '''
77 | self._ThingAccess = leda_handler
78 | if config is not None and isinstance(config, dict):
79 | if "productKey" in config and "deviceName" in config:
80 | self.pk = config["productKey"]
81 | self.dn = config["deviceName"]
82 | else:
83 | logging.error("config is error")
84 | self.pk = None
85 | self.dn = None
86 | else:
87 | logging.error("can't init ThingAccessClient, parameter is error")
88 | self.pk = None
89 | self.dn = None
90 | self.device = None
91 |
92 | def getTsl(self):
93 | '''
94 | get tsl string
95 | return:
96 | deviceTsl[string]: device TSL
97 | '''
98 | try:
99 | deviceTsl = self._ThingAccess.getTSL(self.pk)
100 | except Exception as e:
101 | logging.error("get TSL failed")
102 | return None
103 | return deviceTsl
104 |
105 | def getTslConfig(self):
106 | '''
107 | get tsl config string
108 | return:
109 | deviceTslConfig[string]: device TSL config
110 | '''
111 | try:
112 | deviceTsl = self._ThingAccess.getTSLConfig(self.pk)
113 | except Exception as e:
114 | logging.error("get TSL Config failed")
115 | return None
116 | return deviceTsl
117 |
118 | def getTslExtInfo(self):
119 | '''
120 | get tsl config string
121 | return:
122 | deviceTslConfig[string]: device TSL config
123 | '''
124 | try:
125 | deviceTsl = self._ThingAccess.getTSLConfig(self.pk)
126 | except Exception as e:
127 | logging.error("get TSL Config failed")
128 | return None
129 | return deviceTsl
130 |
131 | def registerAndOnline(self, callback):
132 | '''
133 | device online
134 | param callback[ThingCallback]: ThingCallback object
135 | '''
136 | return self.registerAndonline(callback)
137 |
138 | def registerAndonline(self, callback):
139 | '''
140 | device online
141 | param callback[ThingCallback]: ThingCallback object
142 | '''
143 | subdevice = None
144 | if self.dn is None or self.pk is None:
145 | logging.error("product key or device name is None")
146 | return None
147 | subdevice = self._ThingAccess.deviceRegister(self.dn, self.pk, '{}', callback)
148 | self.device = subdevice
149 | return subdevice
150 |
151 | def unregister(self):
152 | '''
153 | device unregister
154 | '''
155 | if self.device is not None:
156 | self._ThingAccess.deviceUnregister(self.device)
157 |
158 | def online(self):
159 | '''
160 | device online
161 | '''
162 | if self.device is not None:
163 | self.device.online()
164 | else:
165 | logging.error("plese register and online firstly\n")
166 |
167 | def offline(self):
168 | '''
169 | device offline
170 | '''
171 | if self.device is not None:
172 | self.device.offline()
173 |
174 | def reportProperties(self, propertiesDict):
175 | '''
176 | report Property
177 | param propertiesDict[dict]:report property, eg:
178 | {
179 | 'property1':'xxx',
180 | 'property2':'yyy',
181 | ...
182 | }
183 | '''
184 | if self.device is not None:
185 | self.device.reportProperties(propertiesDict)
186 | else:
187 | logging.error("plese register and online firstly")
188 |
189 | def reportEvent(self, eventName, eventDict):
190 | '''
191 | report event
192 | param eventName[string]: Event name
193 | param eventDict[dict]: report event, eg:
194 | {
195 | "key1": 'xxx',
196 | "key2": 'yyy',
197 | ...
198 | }
199 | '''
200 | if self.device is not None:
201 | self.device.reportEvent(eventName, eventDict)
202 | else:
203 | logging.error("plese register and online firstly")
204 |
205 | def cleanup(self):
206 | self.offline()
207 | del (self)
208 |
209 |
210 | class Config(object):
211 | def __init__(self, config=None):
212 | self.config = config
213 |
214 | def getDriverInfo(self):
215 | '''
216 | get global driver info under driver
217 | return:
218 | driverInfo[dict]: driver Info
219 | '''
220 | return _driverInfo
221 |
222 | def getThingInfos(self):
223 | '''
224 | get device list under driver
225 | return:
226 | Thing infos[dict]: device List
227 | '''
228 | return _thingInfos
229 |
230 |
231 | def getConfig():
232 | '''
233 | get config under driver
234 | return:
235 | config[str]: config
236 | '''
237 | if _driverInfo is None:
238 | config = {"deviceList": _thingInfos}
239 | else:
240 | config = {
241 | "config": _driverInfo,
242 | "deviceList": _thingInfos}
243 | return json.dumps(config)
244 |
245 |
246 | _thingInfos = []
247 | _driverInfo = None
248 | device_name = os.environ.get("FUNCTION_ID")
249 | leda_handler = leda.LedaModule()
250 | if device_name is not None:
251 | leda_handler.moduleInit(device_name)
252 | try:
253 | _config_info = leda_handler.getConfig()
254 | configinfo = json.loads(_config_info)
255 | if "config" in configinfo:
256 | _driverInfo = configinfo["config"]
257 | if "deviceList" in configinfo:
258 | devices = configinfo["deviceList"]
259 | for i in range(0, len(devices)):
260 | config = {}
261 | config['productKey'] = devices[i].get('productKey')
262 | pk = config['productKey']
263 | config['deviceName'] = devices[i].get('deviceName')
264 | dn = config['deviceName']
265 | if 'custom' in devices[i]:
266 | config['custom'] = devices[i].get('custom')
267 | _thingInfos.append(config)
268 | except Exception as e:
269 | logging.error("get config failed, %s", e)
270 | else:
271 | logging.error("can't get driver name")
272 | try:
273 | config_env = {"deviceList": _thingInfos}
274 | os.environ["FC_DRIVER_CONFIG"] = json.dumps(config_env)
275 | except Exception as e:
276 | logging.error("set env config failed, %s", e)
277 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | from setuptools import setup
4 | import sys
5 |
6 |
7 | if not (sys.version_info[0] == 3 and sys.version_info[1] == 5 and sys.version_info[2] == 2):
8 | sys.exit("Link IoT Edge only support Python 3.5.2")
9 |
10 | setup(
11 | name = "lethingaccesssdk",
12 | version = "1.2",
13 | description = "Link IoT Edge Thing Access SDK for Function Compute",
14 | license = "Apache 2.0",
15 |
16 | url = "https://help.aliyun.com/product/69083.html?spm=a2c4g.11186623.6.540.66bd71b80FSb2h",
17 | packages = ['lethingaccesssdk', 'leda_python'],
18 | include_package_data = True,
19 | platforms = "any",
20 | install_requires = [
21 | 'setuptools>=16.0',
22 | ],
23 |
24 | scripts = [],
25 | entry_points = {
26 | 'console_scripts': [
27 | ]
28 | }
29 | )
30 |
--------------------------------------------------------------------------------
/unittests/thing_access_sdk_test.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #-*- coding: utf-8 -*-
3 | import sys
4 | import os
5 | sys.path.append("../")
6 | sys.path.append("../../../../../lib")
7 | os.environ["FUNCTION_NAME"] = "driver_name"
8 | import lethingaccesssdk
9 | import logging
10 | import json
11 | import mock
12 | import unittest
13 |
14 | _logger = logging.getLogger(__name__)
15 |
16 |
17 | class Demo_device(lethingaccesssdk.ThingCallback):
18 | def callService(self, name, input):
19 | pass
20 |
21 | def getProperties(self, input):
22 | pass
23 |
24 | def setProperties(self, input):
25 | pass
26 |
27 | class TestThingAccessSDK(unittest.TestCase):
28 | def test_registerAndonline(self):
29 | config = 123 # wrong type
30 | client = lethingaccesssdk.ThingAccessClient(config)
31 | demo = Demo_device()
32 | client.registerAndonline(demo)
33 |
34 | def test_registerAndonline1(self):
35 | config = {"productKey":"a1ICUKj3Pyf"} # miss parameter
36 | client = lethingaccesssdk.ThingAccessClient(config)
37 | demo = Demo_device()
38 | client.registerAndonline(demo)
39 |
40 | def test_registerAndonline2(self):
41 | config = {"productKey":123456, "deviceName":"Hb9TYuRNqqw445xGYmu3"} # wrong pk or dn
42 | client = lethingaccesssdk.ThingAccessClient(config)
43 | demo = Demo_device()
44 | client.registerAndonline(demo)
45 |
46 | def test_registerAndonline3(self):
47 | config = {"productKey":"a1ICUKj3Pyf", "deviceName":"Hb9TYuRNqqw445xGYmu3"} # wrong callback
48 | client = lethingaccesssdk.ThingAccessClient(config)
49 | demo = None
50 | client.registerAndonline(demo)
51 |
52 |
53 | def test_reportEvent(self):
54 | config = {"productKey":"a1ICUKj3Pyf", "deviceName":"Hb9TYuRNqqw445xGYmu3"}
55 | client = lethingaccesssdk.ThingAccessClient(config)
56 | demo = Demo_device()
57 | client.registerAndonline(demo)
58 | try:
59 | client.reportEvent("eventName", "abcd")
60 | except Exception as e:
61 | logging.error(e)
62 | client.offline()
63 |
64 | def test_reportEvent1(self):
65 | config = {"productKey":"a1ICUKj3Pyf", "deviceName":"Hb9TYuRNqqw445xGYmu3"}
66 | client = lethingaccesssdk.ThingAccessClient(config)
67 | demo = Demo_device()
68 | client.registerAndonline(demo)
69 | event = {"key":"value"}
70 | try:
71 | client.reportEvent("eventName", event)
72 | except Exception as e:
73 | logging.error(e)
74 | client.offline()
75 |
76 | def test_reportProperty(self):
77 | config = {"productKey":"a1ICUKj3Pyf", "deviceName":"Hb9TYuRNqqw445xGYmu3"}
78 | client = lethingaccesssdk.ThingAccessClient(config)
79 | demo = Demo_device()
80 | client.registerAndonline(demo)
81 | property = "property1"
82 | try:
83 | client.reportProperties(property)
84 | except Exception as e:
85 | logging.error(e)
86 | client.offline()
87 |
88 | def test_reportProperty1(self):
89 | config = {"productKey":"a1ICUKj3Pyf", "deviceName":"Hb9TYuRNqqw445xGYmu3"}
90 | client = lethingaccesssdk.ThingAccessClient(config)
91 | demo = Demo_device()
92 | client.registerAndonline(demo)
93 | property = {"property1":"values"}
94 | try:
95 | client.reportProperties(property)
96 | except Exception as e:
97 | logging.error(e)
98 | client.offline()
99 |
100 | if __name__ == '__main__':
101 | tc = TestThingAccessSDK()
102 |
103 | tc.test_registerAndonline()
104 | tc.test_registerAndonline1()
105 | tc.test_registerAndonline2()
106 | tc.test_registerAndonline3()
107 |
108 | tc.test_reportEvent()
109 | tc.test_reportEvent1()
110 |
111 | tc.test_reportProperty()
112 | tc.test_reportProperty1()
113 |
--------------------------------------------------------------------------------