├── 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 | --------------------------------------------------------------------------------