From e9c6dd17d7cab8185cc0badf9a57ea7cf155faaf Mon Sep 17 00:00:00 2001 From: Changjun Lim Date: Wed, 27 Feb 2019 14:04:03 -0800 Subject: [PATCH 01/19] modify gateway to accept the server address with HTTPS or no protocol --- cisco_deviot/gateway.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/cisco_deviot/gateway.py b/cisco_deviot/gateway.py index 9286688..16b9e59 100644 --- a/cisco_deviot/gateway.py +++ b/cisco_deviot/gateway.py @@ -32,11 +32,15 @@ def __init__(self, name, deviot_server, connector_server, kind="device", account self.name = name self.kind = kind self.owner = account + if urlparse(deviot_server).scheme == '': + deviot_server = "https://" + deviot_server self.deviot_server = urlparse(deviot_server) self.mode = MODE_MQTT self.things = {} self.__registration_started = False self.__registered = 0 + if urlparse(connector_server).scheme == '': + connector_server = "tcp://" + connector_server self.connector = MqttConnector(self, connector_server) self.host = self.connector.host self.port = self.connector.port @@ -75,7 +79,10 @@ def __register(self): ("sensors", [thing.get_model() for thing in self.things.values()])]) json_string = json.dumps(self.__del_none(model), sort_keys=False) try: - conn = httplib.HTTPConnection(self.deviot_server.netloc) + if self.deviot_server.scheme == 'http': + conn = httplib.HTTPConnection(self.deviot_server.netloc) + else: + conn = httplib.HTTPSConnection(self.deviot_server.netloc) conn.request("POST", "/api/v1/gateways", json_string, {'Content-Type': 'application/json'}) response = conn.getresponse() code = int(response.status) From b21cca7c5f383b6b1e002cf026a301fe1cdaa2bf Mon Sep 17 00:00:00 2001 From: Changjun Lim Date: Wed, 27 Feb 2019 16:31:23 -0800 Subject: [PATCH 02/19] update README --- README.md | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c3bf7ad..fbeca37 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,49 @@ -#gateway-python-sdk +# gateway-python-sdk Python SDK for DevIoT gateway service + +Version: Python 2 + +## How to use +#### 0) Check the version of python +In order to use this SDK, the version of python should be 2.x. You can check it with the following command. +``` +python --version +``` + +#### 1) Set up SDK package (linux, mac) +copy SDK git repository +``` +git clone https://wwwin-github.cisco.com/DevIoT/gateway-python-sdk.git +cd gateway-pythohn-sdk +``` +install sdk package on python 2 +``` +python setup.py install +``` + +#### 2) Connect gateway (python) +import sdk +``` +from cisco_deviot.gateway import Gateway +from cisco_deviot.thing import Thing +``` +construct a Gateway object +``` +app = Gateway("gateway_name", "deviot.cisco.com", "deviot.cisco.com:18883", "device", "deviot_id@cisco.com") +``` +contruct a sensor instance and register the sensor to the gateway +``` +thing = Thing("sensor-id", "sensor-name", "sensor-kind") +app.register(thing) +``` +connect gateway to the server +``` +app.start() +``` + +#### 3) disconnect gateway +disconnect gateway +``` +app.stop() +``` \ No newline at end of file From b97783f150c49fe8382d57646a68f55b64227f4e Mon Sep 17 00:00:00 2001 From: Changjun Lim Date: Tue, 12 Mar 2019 10:51:44 -0700 Subject: [PATCH 03/19] refactor code and add comments --- cisco_deviot/__init__.py | 4 ++-- cisco_deviot/gateway.py | 7 ++++--- cisco_deviot/mqtt_connector.py | 13 ++++++++++--- cisco_deviot/thing.py | 3 +++ setup.py | 10 +++++----- 5 files changed, 24 insertions(+), 13 deletions(-) diff --git a/cisco_deviot/__init__.py b/cisco_deviot/__init__.py index fce2632..b52d65e 100644 --- a/cisco_deviot/__init__.py +++ b/cisco_deviot/__init__.py @@ -30,8 +30,8 @@ def get_logger(name, level=logging.INFO): logger = get_logger("DevIoT") -print """ +print(""" ___ ________ ______ / _ \___ _ __/ _/ __ \/_ __/ / // / -_) |/ // // /_/ / / / -/____/\__/|___/___/\____/ /_/""" +/____/\__/|___/___/\____/ /_/""") diff --git a/cisco_deviot/gateway.py b/cisco_deviot/gateway.py index 16b9e59..a90f85a 100644 --- a/cisco_deviot/gateway.py +++ b/cisco_deviot/gateway.py @@ -32,14 +32,14 @@ def __init__(self, name, deviot_server, connector_server, kind="device", account self.name = name self.kind = kind self.owner = account - if urlparse(deviot_server).scheme == '': + if urlparse(deviot_server).scheme == '': # the default protocol for a DevIot server is HTTPS deviot_server = "https://" + deviot_server self.deviot_server = urlparse(deviot_server) self.mode = MODE_MQTT self.things = {} self.__registration_started = False self.__registered = 0 - if urlparse(connector_server).scheme == '': + if urlparse(connector_server).scheme == '': # the default protocol of MQTT is TCP connector_server = "tcp://" + connector_server self.connector = MqttConnector(self, connector_server) self.host = self.connector.host @@ -144,6 +144,7 @@ def send_data(self, data): else: logger.warn("{connector} not connected yet".format(connector=self.connector)) + # args: payload of the subscribed MQTT message def call_action(self, args): tid = None if "id" in args: @@ -165,7 +166,7 @@ def call_action(self, args): action = args.pop("action") try: t.call_action(action, args) - except Exception, error: + except Exception as error: logger.error("failed to call {tid}.{action}({args}): {error}".format(tid=tid, action=action, args=args, diff --git a/cisco_deviot/mqtt_connector.py b/cisco_deviot/mqtt_connector.py index 04e89ca..2a9b60f 100644 --- a/cisco_deviot/mqtt_connector.py +++ b/cisco_deviot/mqtt_connector.py @@ -36,6 +36,8 @@ def __init__(self, gateway, mqtt_server): surl = urlparse(mqtt_server) self.host = surl.hostname self.port = surl.port + if self.port is None: + self.port = 1883 self.data = "/deviot/{ns}/{name}/data".format(name=name, ns=ns) self.action = "/deviot/{ns}/{name}/action".format(name=name, ns=ns) @@ -48,6 +50,9 @@ def stop(self): self.client.disconnect() def __on_connect__(self, client, userdata, flags, rc): + if rc != 0: + logger.error("mqtt connection bad returned code={code}".format(code=rc)) + self.__reconnect(2) self.client.subscribe(self.action) self.__connected = True logger.info("{server}{topic} connected".format(server=self, topic=self.action)) @@ -55,8 +60,10 @@ def __on_connect__(self, client, userdata, flags, rc): def __on_disconnect__(self, client, userdata, rc): logger.warn("{server} disconnected".format(server=self)) self.__connected = False - backoff = 2 - while not self.__connected: + self.__reconnect(2) + + def __reconnect(self, backoff): + while not self.is_connected(): logger.info("reconnecting to {server} in {sec} seconds ...".format(server=self, sec=backoff)) time.sleep(backoff) backoff = min(128, backoff * 2) @@ -74,7 +81,7 @@ def __on_message__(self, client, userdata, msg): try: args = json.loads(message) self.gateway.call_action(args) - except Exception, error: + except Exception as error: logger.error("failed to process message {message}: {error}".format(message=message, error=error)) def publish(self, data): diff --git a/cisco_deviot/thing.py b/cisco_deviot/thing.py index 831da68..050852e 100644 --- a/cisco_deviot/thing.py +++ b/cisco_deviot/thing.py @@ -36,6 +36,8 @@ class Property: def __init__(self, name, type=0, value=None, range=None, unit=None, description=None): self.name = name self.type = type + if value is None: + value = default_value_for_type(type) self.value = value self.range = range self.unit = unit @@ -110,6 +112,7 @@ def add_action(self, *thing_actions): logger.error("invalid property {property}, only string and Property are supported".format(property=act)) return self + # method: action name def call_action(self, method, args): action_method = getattr(self, method) matches = list(a for a in self.actions if a.name == method) diff --git a/setup.py b/setup.py index 6632732..dfb722f 100644 --- a/setup.py +++ b/setup.py @@ -20,13 +20,13 @@ name = "cisco_deviot", version = "0.1.2", description = "Gateway SDK for Cisco DevIoT", - long_description=long_description, - url="https://github.com/CiscoDevIoT/gateway-python-sdk", + long_description = long_description, + url = "https://github.com/CiscoDevIoT/gateway-python-sdk", author = "Haihua Xiao", author_email = "hhxiao@gmail.com", license = "Apache 2.0", # See https://pypi.python.org/pypi?%3Aaction=list_classifiers - classifiers=[ + classifiers = [ # How mature is this project? Common values are # 3 - Alpha # 4 - Beta @@ -50,8 +50,8 @@ 'Programming Language :: Python :: 3.5', ], # What does your project relate to? - keywords='cisco devnet deviot gateway', + keywords = 'cisco devnet deviot gateway', zip_safe = False, packages = find_packages(), - install_requires=['paho-mqtt'], + install_requires = ['paho-mqtt'], ) From 04b0296423d1990ff36a53e5110a849651c5ac43 Mon Sep 17 00:00:00 2001 From: Changjun Lim Date: Tue, 12 Mar 2019 11:01:56 -0700 Subject: [PATCH 04/19] update README referring DevIoT-Python-SDK --- README.md | 95 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 83 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index fbeca37..3dbed3f 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,31 @@ Python SDK for DevIoT gateway service -Version: Python 2 +You can use this SDK to register devices to DevIoT, and sync up data and actions between the devices and DevIoT. -## How to use -#### 0) Check the version of python +## Requirement +1. [Python 2.7](https://www.python.org/downloads/):This SDK base on the Python 2.7.10 +2. [paho-mqtt](https://eclipse.org/paho/clients/python/): this SDK use this library to build a MQTT client + +## Usage +1. You can use sample code to register GrovePi sensors and simulated sensors to DevIoT. +2. You can also use SDK to register other sensors and systems to DevIoT. + +## Term + +- **Gateway**: 'Gateway' is a device connected to DevIoT like a raspberry pi board or a mobile phone +- **Thing**: 'Thing' is a sensor or a module in a gateway like a light sensor, LED, or GPS. A thing is an icon in DevIoT. A gateway can have several things. +- **Property**: 'Property' is a variable measured by a thing. For instance, Latitude and longitude are properties of a GPS. A thing can have several properties. +- **Action**: 'Action' is a command for a module. For example, turning on and off LED are two different actions. A thing can have several actions. + +## Getting Started +#### 0) Check the version of python (terminal) In order to use this SDK, the version of python should be 2.x. You can check it with the following command. ``` python --version ``` -#### 1) Set up SDK package (linux, mac) +#### 1) Set up SDK package (terminal) copy SDK git repository ``` git clone https://wwwin-github.cisco.com/DevIoT/gateway-python-sdk.git @@ -22,28 +37,84 @@ install sdk package on python 2 python setup.py install ``` -#### 2) Connect gateway (python) -import sdk +#### 2) Connect gateway (python code) +Import SDK ``` from cisco_deviot.gateway import Gateway from cisco_deviot.thing import Thing +from cisco_deviot.thing import Property ``` -construct a Gateway object +Construct a Gateway object ``` app = Gateway("gateway_name", "deviot.cisco.com", "deviot.cisco.com:18883", "device", "deviot_id@cisco.com") ``` -contruct a sensor instance and register the sensor to the gateway + +Contruct a thing instance +``` +thing = Thing("thing-id", "thing-name", "thing-kind") +``` + +Add a property to the thing +``` +property = Property("variable_name", PropertyTypeInt, 0) +thing.add_property(property); +``` + +Register the sensor to the gateway ``` -thing = Thing("sensor-id", "sensor-name", "sensor-kind") app.register(thing) ``` -connect gateway to the server + +Connect the gateway to the server ``` app.start() ``` -#### 3) disconnect gateway +#### 3) disconnect gateway (python code) disconnect gateway ``` app.stop() -``` \ No newline at end of file +``` +  +## How to run Sample_Code_for_GrovePi_Sensor.py +#### Build the hardware +1. Prepare your RaspberryPi os environment in your SD card + +* Download the OS for RaspberryPi form here[RASPBIAN JESSIE](https://www.raspberrypi.org/downloads/raspbian/) +* Format you SD card +* Use window install the OS image to the SD card. you can use [Win32 Disk Manager](https://sourceforge.net/projects/win32diskimager/) do this + I strongly recommend you do this use windows, i have met many issues when i installed it by mac os +* Attach the SD card to the RaspberryPi + +You also can do this follow [here](https://www.raspberrypi.org/documentation/installation/noobs.md) + +2. Join the GrovePi with RaspberryPi. if you correct, it should be like this + +3. Connect RaspberryPi with the power and network. + +4. Connect RaspberryPi with Display use the HDMI cables. + +#### Build the software environment +5. Install the Python 2.7. Check the python version of RaspberryPi os. this sample code base on python2.7.3 or later. in most time, the RaspberryPi os have installed the python2.7.3 or later, if not, you can +install the python follow [here](https://www.raspberrypi.org/documentation/linux/software/python.md) + +6. Install GrovePi SDK. + +* Make sure your Raspberry Pi is connected to the internet. +* Type follow command in terminal window + + sudo apt-get update + sudo apt-get install rpi.gpio + +* [Follow this tutorial for setting up the GrovePi](http://www.dexterindustries.com/GrovePi/get-started-with-the-grovepi/setting-software/). +* Restart the Raspberry Pi. + +Your SD card now has what it needs to start using the GrovePi! +[Here is info more about install GrovePi SDK](http://www.dexterindustries.com/GrovePi/get-started-with-the-grovepi/) + +#### Run Grove Pi sample +* Cd to your work space in terminal window +* Type follow command: + + python Sample_Code_for_GrovePi_Sensor.py +  From 4b29d358318727ea67a1a42df8f55e56c478ca22 Mon Sep 17 00:00:00 2001 From: Changjun Lim Date: Tue, 12 Mar 2019 16:00:15 -0700 Subject: [PATCH 05/19] modify the MQTT connection part and update README --- README.md | 106 +++++++++++++++++++++++++++++++-- cisco_deviot/gateway.py | 8 +++ cisco_deviot/mqtt_connector.py | 15 ++++- cisco_deviot/thing.py | 15 ++++- 4 files changed, 138 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3dbed3f..05ef236 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ Python SDK for DevIoT gateway service You can use this SDK to register devices to DevIoT, and sync up data and actions between the devices and DevIoT. ## Requirement -1. [Python 2.7](https://www.python.org/downloads/):This SDK base on the Python 2.7.10 -2. [paho-mqtt](https://eclipse.org/paho/clients/python/): this SDK use this library to build a MQTT client +1. [Python 2.7](https://www.python.org/downloads/): This SDK base on the Python 2.7.10 +2. [paho-mqtt](https://eclipse.org/paho/clients/python/): This SDK use this library to build a MQTT client ## Usage 1. You can use sample code to register GrovePi sensors and simulated sensors to DevIoT. @@ -46,7 +46,8 @@ from cisco_deviot.thing import Property ``` Construct a Gateway object ``` -app = Gateway("gateway_name", "deviot.cisco.com", "deviot.cisco.com:18883", "device", "deviot_id@cisco.com") +account = "your_id@cisco.com" +app = Gateway("gateway_name", "deviot.cisco.com", "deviot.cisco.com:18883", "device", account) ``` Contruct a thing instance @@ -60,6 +61,15 @@ property = Property("variable_name", PropertyTypeInt, 0) thing.add_property(property); ``` +Add an action to the thing +``` +thing.add_action("sameple_action") +def custom_function(): + print("action") + +thing.sameple_action = custom_function +``` + Register the sensor to the gateway ``` app.register(thing) @@ -95,7 +105,7 @@ You also can do this follow [here](https://www.raspberrypi.org/documentation/ins 4. Connect RaspberryPi with Display use the HDMI cables. #### Build the software environment -5. Install the Python 2.7. Check the python version of RaspberryPi os. this sample code base on python2.7.3 or later. in most time, the RaspberryPi os have installed the python2.7.3 or later, if not, you can +5. Install the Python 2.7. Check the python version of RaspberryPi os. this sample code base on python 2.7.3 or later. in most time, the RaspberryPi os have installed the python 2.7.3 or later, if not, you can install the python follow [here](https://www.raspberrypi.org/documentation/linux/software/python.md) 6. Install GrovePi SDK. @@ -118,3 +128,91 @@ Your SD card now has what it needs to start using the GrovePi! python Sample_Code_for_GrovePi_Sensor.py   +## API +### Gateway +#### Constructor +``` +Gateway(name, deviot_server, connector_server, kind="device", account="") +``` +The Gateway() constructor takes the following arguments: +**name** +The unique name of a gateway +**deviot_server** +The address for the DevIoT server. It does not need to add the protocol. The default protocol is HTTPS(secure connection). The public DevIoT server address is 'deviot.cisco.com' +**connector_server** +The address for the MQTT server. It does not need to add the protcol. The default protocol is TCP(unsecure connection) and the default port number is 1883. The public MQTT server address is 'deviot.cisco.com:18883' +**kind** +The kind of a gateway +**account** +Your DevIoT account. you also can use empty string, it will let all account get the data of your gateway. +#### register() +``` +register(thing) +``` +The register() function adds a thing to the gateway. The thing should not have been already registered. +**thing** +A *Thing* instance to register +#### deregister() +``` +deregister(thing) +``` +The deregister() function deletes a thing from the gateway. +**thing** +A *Thing* instance to deregister +#### update_thing() +``` +update_thing(thing_id, **new_value) +``` +The update_thing() function updates the values of a thing. +**thing_id** +The id of the thing to be updated +**\*\*new_value** +The keyword arguments for the updated values. The key is the name of properties and the value is the new value. + +#### start() +``` +start() +``` +Connect the things of the gateway to the DevIoT server and the MQTT server +#### stop() +``` +stop() +``` +Disconnect the gateway from the DevIoT server and the MQTT server. +### Thing +#### Constructor +``` +Thing(id, name, kind=None) +``` +The Thing() constructor takes the following arguments: +**id** +the unique id of a thing +**name** +the display name of a thing in DevIot +**kind** +the kind of a thing + +#### add_property() +``` + +``` + + +### Property +#### Constructor +``` +Property(name, type=0, value=None, range=None, unit=None, description=None) +``` +The Property() constructor takes the following arguments: +**name** +The name of a property. It should be unique in a thing. +**type** +The variable type of a property. There are 4 types: int, bool, string, color. You can use PropertyTypeInt, PropertyTypeBool, PropertyTypeString, and PropertyTypeColor. +**value** +The value of a property. +**range** +The range of a property's value. +**unit** +The unit of a property. It is string value. +**description** +The description for a property. It is shown at the page of each thing. diff --git a/cisco_deviot/gateway.py b/cisco_deviot/gateway.py index a90f85a..8d429be 100644 --- a/cisco_deviot/gateway.py +++ b/cisco_deviot/gateway.py @@ -138,12 +138,20 @@ def deregister(self, thing): else: logger.warn("thing {thing} is not registered".format(thing=thing)) + def get_data(self): + return {key: value.get_data() for (key, value) in self.things.items()} + def send_data(self, data): if self.connector.is_connected: self.connector.publish(data) else: logger.warn("{connector} not connected yet".format(connector=self.connector)) + def update_thing(self, thing_id, **new_values): + if thing_id in self.things: + thing = self.things[thing_id] + thing.update_property(**new_values) + # args: payload of the subscribed MQTT message def call_action(self, args): tid = None diff --git a/cisco_deviot/mqtt_connector.py b/cisco_deviot/mqtt_connector.py index 2a9b60f..91abbad 100644 --- a/cisco_deviot/mqtt_connector.py +++ b/cisco_deviot/mqtt_connector.py @@ -12,6 +12,7 @@ import json +import threading from urlparse import urlparse import time @@ -28,6 +29,7 @@ def __init__(self, gateway, mqtt_server): self.client.on_disconnect = self.__on_disconnect__ self.client.on_message = self.__on_message__ self.__connected = False + self.__connection_start = False ns = gateway.owner.replace("@", "_").replace(".", "_").replace("/", "_") if ns == "": ns = "_" @@ -37,16 +39,27 @@ def __init__(self, gateway, mqtt_server): self.host = surl.hostname self.port = surl.port if self.port is None: - self.port = 1883 + self.port = 1883 # The default port number self.data = "/deviot/{ns}/{name}/data".format(name=name, ns=ns) self.action = "/deviot/{ns}/{name}/action".format(name=name, ns=ns) + def __publish_function(self): + while self.__connection_start: + if self.is_connected(): + self.publish(self.gateway.get_data()) + time.sleep(0.5) + def start(self): self.client.connect_async(self.host, self.port, 60) self.client.loop_start() + self.__connection_start = True + thread = threading.Thread(target=MqttConnector.__publish_function, args=(self,)) + thread.daemon = True + thread.start() logger.info("connecting to {server} ...".format(server=self)) def stop(self): + self.__connection_start = False self.client.disconnect() def __on_connect__(self, client, userdata, flags, rc): diff --git a/cisco_deviot/thing.py b/cisco_deviot/thing.py index 050852e..b0db332 100644 --- a/cisco_deviot/thing.py +++ b/cisco_deviot/thing.py @@ -51,6 +51,9 @@ def get_model(self): ("unit", self.unit), ("description", self.description)]) + def __str__(self): + return "{name}:{value}{unit}".format(name=self.name, value=self.value, unit=self.unit) + class Action: def __init__(self, name, **kwargs): @@ -90,10 +93,13 @@ def get_model(self): def __str__(self): return "{id}.{name}({kind})".format(id=self.id, name=self.name, kind=self.kind) + def get_data(self): + return {prop.name: prop.value for prop in self.properties} + def add_property(self, *thing_properties): for prop in thing_properties: if isinstance(prop, str): - self.properties.append(Property(prop, PropertyTypeInt)) + self.properties.append(Property(prop)) elif isinstance(prop, Property): self.properties.append(prop) else: @@ -102,6 +108,13 @@ def add_property(self, *thing_properties): return self + def update_property(self, **new_properties): + for new_prop_name in new_properties: + for prop in self.properties: + if new_prop_name == prop.name: + prop.value = new_properties[new_prop_name] + break + def add_action(self, *thing_actions): for act in thing_actions: if isinstance(act, str): From 8712c16e79b3d990fe6880e86965f4498314a1a1 Mon Sep 17 00:00:00 2001 From: Changjun Lim Date: Tue, 12 Mar 2019 16:12:06 -0700 Subject: [PATCH 06/19] change the style and location of constant --- README.md | 3 ++- cisco_deviot/constants.py | 4 ++++ cisco_deviot/thing.py | 16 ++++++---------- 3 files changed, 12 insertions(+), 11 deletions(-) create mode 100644 cisco_deviot/constants.py diff --git a/README.md b/README.md index 05ef236..b363f8d 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ Import SDK from cisco_deviot.gateway import Gateway from cisco_deviot.thing import Thing from cisco_deviot.thing import Property +import constants ``` Construct a Gateway object ``` @@ -57,7 +58,7 @@ thing = Thing("thing-id", "thing-name", "thing-kind") Add a property to the thing ``` -property = Property("variable_name", PropertyTypeInt, 0) +property = Property("variable_name", constants.PROPERTY_TYPE_INT, 0) thing.add_property(property); ``` diff --git a/cisco_deviot/constants.py b/cisco_deviot/constants.py new file mode 100644 index 0000000..928e2cb --- /dev/null +++ b/cisco_deviot/constants.py @@ -0,0 +1,4 @@ +PROPERTY_TYPE_INT = 0 +PROPERTY_TYPE_STRING = 1 +PROPERTY_TYPE_BOOL = 2 +PROPERTY_TYPE_COLOR = 3 \ No newline at end of file diff --git a/cisco_deviot/thing.py b/cisco_deviot/thing.py index b0db332..157662a 100644 --- a/cisco_deviot/thing.py +++ b/cisco_deviot/thing.py @@ -13,27 +13,23 @@ import collections from cisco_deviot import logger - -PropertyTypeInt = 0 -PropertyTypeString = 1 -PropertyTypeBool = 2 -PropertyTypeColor = 3 +import constants def default_value_for_type(stype): - if stype == PropertyTypeInt: + if stype == constants.PROPERTY_TYPE_INT: return 0 - if stype == PropertyTypeBool: + if stype == constants.PROPERTY_TYPE_BOOL: return False - if stype == PropertyTypeString: + if stype == constants.PROPERTY_TYPE_STRING: return "" - if stype == PropertyTypeColor: + if stype == constants.PROPERTY_TYPE_COLOR: return "FFFFFF" return None class Property: - def __init__(self, name, type=0, value=None, range=None, unit=None, description=None): + def __init__(self, name, type=constants.PROPERTY_TYPE_INT, value=None, range=None, unit=None, description=None): self.name = name self.type = type if value is None: From ed30f253eaf59275b432a1988828b9f0c4bb42ec Mon Sep 17 00:00:00 2001 From: Changjun Lim Date: Tue, 12 Mar 2019 16:38:46 -0700 Subject: [PATCH 07/19] update README --- README.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b363f8d..72661ac 100644 --- a/README.md +++ b/README.md @@ -195,10 +195,18 @@ the kind of a thing #### add_property() ``` - +add_property(*thing_properties) ``` - - +The add_property() adds properties to a Thing instance. When a string is used for arguments, new Property instance named the string is added to the Thing instance. +**\*thing_properties** +The list of Property instances or the string value of properties' name. +#### add_action() +``` +add_action(*thing_actions) +``` +The add_action() adds actions to a Thing instance. When a string is used for arguments, new Action instance named the string is added to the Thing instance. +**\*thing_properties** +The list of Property instances or the string value of actions' name. ### Property #### Constructor ``` @@ -208,7 +216,7 @@ The Property() constructor takes the following arguments: **name** The name of a property. It should be unique in a thing. **type** -The variable type of a property. There are 4 types: int, bool, string, color. You can use PropertyTypeInt, PropertyTypeBool, PropertyTypeString, and PropertyTypeColor. +The variable type of a property. There are 4 types: int, bool, string, color. You can use constants.PROPERTY_TYPE_INT, constants.PROPERTY_TYPE_BOOL, constants.PROPERTY_TYPE_STRING, constants.PROPERTY_TYPE_COLOR after importing constants. **value** The value of a property. **range** From 22a1d5ea95806b198242d22cb197b3eb90df6972 Mon Sep 17 00:00:00 2001 From: Changjun Lim Date: Tue, 12 Mar 2019 16:57:51 -0700 Subject: [PATCH 08/19] fix markdown format error in README --- README.md | 81 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 72661ac..303b409 100644 --- a/README.md +++ b/README.md @@ -135,39 +135,39 @@ Your SD card now has what it needs to start using the GrovePi! ``` Gateway(name, deviot_server, connector_server, kind="device", account="") ``` -The Gateway() constructor takes the following arguments: -**name** -The unique name of a gateway -**deviot_server** -The address for the DevIoT server. It does not need to add the protocol. The default protocol is HTTPS(secure connection). The public DevIoT server address is 'deviot.cisco.com' -**connector_server** -The address for the MQTT server. It does not need to add the protcol. The default protocol is TCP(unsecure connection) and the default port number is 1883. The public MQTT server address is 'deviot.cisco.com:18883' -**kind** -The kind of a gateway -**account** +The Gateway() constructor takes the following arguments: +**name** +The unique name of a gateway +**deviot_server** +The address for the DevIoT server. It does not need to add the protocol. The default protocol is HTTPS(secure connection). The public DevIoT server address is 'deviot.cisco.com' +**connector_server** +The address for the MQTT server. It does not need to add the protcol. The default protocol is TCP(unsecure connection) and the default port number is 1883. The public MQTT server address is 'deviot.cisco.com:18883' +**kind** +The kind of a gateway +**account** Your DevIoT account. you also can use empty string, it will let all account get the data of your gateway. #### register() ``` register(thing) ``` -The register() function adds a thing to the gateway. The thing should not have been already registered. -**thing** +The register() function adds a thing to the gateway. The thing should not have been already registered. +**thing** A *Thing* instance to register #### deregister() ``` deregister(thing) ``` -The deregister() function deletes a thing from the gateway. -**thing** +The deregister() function deletes a thing from the gateway. +**thing** A *Thing* instance to deregister #### update_thing() ``` update_thing(thing_id, **new_value) ``` -The update_thing() function updates the values of a thing. -**thing_id** -The id of the thing to be updated -**\*\*new_value** +The update_thing() function updates the values of a thing. +**thing_id** +The id of the thing to be updated +**\*\*new_value** The keyword arguments for the updated values. The key is the name of properties and the value is the new value. #### start() @@ -185,43 +185,44 @@ Disconnect the gateway from the DevIoT server and the MQTT server. ``` Thing(id, name, kind=None) ``` -The Thing() constructor takes the following arguments: -**id** -the unique id of a thing -**name** -the display name of a thing in DevIot -**kind** +The Thing() constructor takes the following arguments: +**id** +the unique id of a thing +**name** +the display name of a thing in DevIot +**kind** the kind of a thing #### add_property() ``` add_property(*thing_properties) ``` -The add_property() adds properties to a Thing instance. When a string is used for arguments, new Property instance named the string is added to the Thing instance. -**\*thing_properties** +The add_property() adds properties to a Thing instance. When a string is used for arguments, new Property instance named the string is added to the Thing instance. +**\*thing_properties** The list of Property instances or the string value of properties' name. #### add_action() ``` add_action(*thing_actions) ``` -The add_action() adds actions to a Thing instance. When a string is used for arguments, new Action instance named the string is added to the Thing instance. -**\*thing_properties** +The add_action() adds actions to a Thing instance. When a string is used for arguments, new Action instance named the string is added to the Thing instance. +**\*thing_properties** The list of Property instances or the string value of actions' name. + ### Property #### Constructor ``` Property(name, type=0, value=None, range=None, unit=None, description=None) ``` -The Property() constructor takes the following arguments: -**name** -The name of a property. It should be unique in a thing. -**type** -The variable type of a property. There are 4 types: int, bool, string, color. You can use constants.PROPERTY_TYPE_INT, constants.PROPERTY_TYPE_BOOL, constants.PROPERTY_TYPE_STRING, constants.PROPERTY_TYPE_COLOR after importing constants. -**value** -The value of a property. -**range** -The range of a property's value. -**unit** -The unit of a property. It is string value. -**description** +The Property() constructor takes the following arguments: +**name** +The name of a property. It should be unique in a thing. +**type** +The variable type of a property. There are 4 types: int, bool, string, color. You can use constants.PROPERTY_TYPE_INT, constants.PROPERTY_TYPE_BOOL, constants.PROPERTY_TYPE_STRING, constants.PROPERTY_TYPE_COLOR after importing constants. +**value** +The value of a property. +**range** +The range of a property's value. +**unit** +The unit of a property. It is string value. +**description** The description for a property. It is shown at the page of each thing. From ab67ff22fae1b6c66bec10599a284d136f833504 Mon Sep 17 00:00:00 2001 From: Changjun Lim Date: Thu, 14 Mar 2019 10:18:57 -0700 Subject: [PATCH 09/19] expand the range of arguments of functions in Gateway and update README --- README.md | 80 ++++++++++++++++++++++----------------- cisco_deviot/constants.py | 6 ++- cisco_deviot/gateway.py | 54 ++++++++++++++++---------- 3 files changed, 85 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 303b409..5273ccc 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,15 @@ # gateway-python-sdk -Python SDK for DevIoT gateway service +### Python SDK for DevIoT service ([https://deviot.cisco.com/](https://deviot.cisco.com/)) You can use this SDK to register devices to DevIoT, and sync up data and actions between the devices and DevIoT. +## Table of contents + +* [Getting Started](#getting-started) +* [Run sample code on Raspberry Pi](#run-sample-code-on-raspberry-pi) +* [API](#api) + ## Requirement 1. [Python 2.7](https://www.python.org/downloads/): This SDK base on the Python 2.7.10 2. [paho-mqtt](https://eclipse.org/paho/clients/python/): This SDK use this library to build a MQTT client @@ -14,7 +20,7 @@ You can use this SDK to register devices to DevIoT, and sync up data and actions ## Term -- **Gateway**: 'Gateway' is a device connected to DevIoT like a raspberry pi board or a mobile phone +- **Gateway**: 'Gateway' is a device connected to DevIoT like a Raspberry Pi board or a mobile phone - **Thing**: 'Thing' is a sensor or a module in a gateway like a light sensor, LED, or GPS. A thing is an icon in DevIoT. A gateway can have several things. - **Property**: 'Property' is a variable measured by a thing. For instance, Latitude and longitude are properties of a GPS. A thing can have several properties. - **Action**: 'Action' is a command for a module. For example, turning on and off LED are two different actions. A thing can have several actions. @@ -27,7 +33,7 @@ python --version ``` #### 1) Set up SDK package (terminal) -copy SDK git repository +Clone SDK git repository ``` git clone https://wwwin-github.cisco.com/DevIoT/gateway-python-sdk.git cd gateway-pythohn-sdk @@ -87,47 +93,51 @@ disconnect gateway app.stop() ```   -## How to run Sample_Code_for_GrovePi_Sensor.py +## Run sample code on Raspberry Pi #### Build the hardware -1. Prepare your RaspberryPi os environment in your SD card - -* Download the OS for RaspberryPi form here[RASPBIAN JESSIE](https://www.raspberrypi.org/downloads/raspbian/) +###### 1. Prepare your Raspberry Pi os environment in your SD card +* Download the OS for Raspberry Pi form [RASPBIAN JESSIE](https://www.raspberrypi.org/downloads/raspbian/) * Format you SD card -* Use window install the OS image to the SD card. you can use [Win32 Disk Manager](https://sourceforge.net/projects/win32diskimager/) do this - I strongly recommend you do this use windows, i have met many issues when i installed it by mac os -* Attach the SD card to the RaspberryPi +* Use window install the OS image to the SD card. you can use [Win32 Disk Manager](https://sourceforge.net/projects/win32diskimager/). + I strongly recommend you do this using Windows, I have met many issues when i installed it by mac OS +* Attach the SD card to the Raspberry Pi -You also can do this follow [here](https://www.raspberrypi.org/documentation/installation/noobs.md) +You also can follow [this instructions](https://www.raspberrypi.org/documentation/installation/noobs.md) -2. Join the GrovePi with RaspberryPi. if you correct, it should be like this +######2. Connect the GrovePi to the Raspberry Pi. -3. Connect RaspberryPi with the power and network. +######3. Connect Raspberry Pi with the power and network. -4. Connect RaspberryPi with Display use the HDMI cables. +######4. Connect Raspberry Pi with Display using HDMI cable. #### Build the software environment -5. Install the Python 2.7. Check the python version of RaspberryPi os. this sample code base on python 2.7.3 or later. in most time, the RaspberryPi os have installed the python 2.7.3 or later, if not, you can -install the python follow [here](https://www.raspberrypi.org/documentation/linux/software/python.md) +######5. Install the Python 2.7. +* Check the version of python that Raspberry Pi has. This sample code is based on python 2.7.3 or later. in most time, the Raspberry Pi os have installed the python 2.7.3 or later, if not, you can install the python follow [here](https://www.raspberrypi.org/documentation/linux/software/python.md) -6. Install GrovePi SDK. +######6. Install GrovePi SDK. -* Make sure your Raspberry Pi is connected to the internet. -* Type follow command in terminal window +* Make sure your Raspberry Pi is connected to the Internet. +* Type the following commands in terminal window sudo apt-get update sudo apt-get install rpi.gpio -* [Follow this tutorial for setting up the GrovePi](http://www.dexterindustries.com/GrovePi/get-started-with-the-grovepi/setting-software/). -* Restart the Raspberry Pi. +* [Follow the tutorial for setting up the GrovePi](http://www.dexterindustries.com/GrovePi/get-started-with-the-grovepi/setting-software/). +* Reboot your Raspberry Pi board. -Your SD card now has what it needs to start using the GrovePi! -[Here is info more about install GrovePi SDK](http://www.dexterindustries.com/GrovePi/get-started-with-the-grovepi/) +Your SD card now has what it needs to start using the GrovePi. +[More information about installing GrovePi SDK](http://www.dexterindustries.com/GrovePi/get-started-with-the-grovepi/) -#### Run Grove Pi sample -* Cd to your work space in terminal window -* Type follow command: - - python Sample_Code_for_GrovePi_Sensor.py +#### Run GrovePi sample code + +######7. Download SDK and run the sample code +* Download SDK and go to the main directory of the repo + + git clone https://wwwin-github.cisco.com/DevIoT/gateway-python-sdk.git + cd gateway-pythohn-sdk +* Run the sample python code + + python sample_code_for_GrovePi_sensor.py   ## API ### Gateway @@ -148,25 +158,25 @@ The kind of a gateway Your DevIoT account. you also can use empty string, it will let all account get the data of your gateway. #### register() ``` -register(thing) +register(*things) ``` -The register() function adds a thing to the gateway. The thing should not have been already registered. +The register() function adds things to the gateway. The thing should not have been already registered. **thing** A *Thing* instance to register #### deregister() ``` -deregister(thing) +deregister(*things) ``` -The deregister() function deletes a thing from the gateway. +The deregister() function deletes things from the gateway. **thing** A *Thing* instance to deregister #### update_thing() ``` -update_thing(thing_id, **new_value) +update_thing(thing, **new_value) ``` The update_thing() function updates the values of a thing. -**thing_id** -The id of the thing to be updated +**thing** +The thing to be updated. It can be the string id of a thing or a Thing instance. **\*\*new_value** The keyword arguments for the updated values. The key is the name of properties and the value is the new value. diff --git a/cisco_deviot/constants.py b/cisco_deviot/constants.py index 928e2cb..7264c93 100644 --- a/cisco_deviot/constants.py +++ b/cisco_deviot/constants.py @@ -1,4 +1,8 @@ PROPERTY_TYPE_INT = 0 PROPERTY_TYPE_STRING = 1 PROPERTY_TYPE_BOOL = 2 -PROPERTY_TYPE_COLOR = 3 \ No newline at end of file +PROPERTY_TYPE_COLOR = 3 + +MODE_HTTP_PULL = 0 +MODE_HTTP_PUSH = 1 +MODE_MQTT = 2 diff --git a/cisco_deviot/gateway.py b/cisco_deviot/gateway.py index 8d429be..e670dea 100644 --- a/cisco_deviot/gateway.py +++ b/cisco_deviot/gateway.py @@ -18,12 +18,10 @@ from urlparse import urlparse from cisco_deviot import logger -from cisco_deviot.mqtt_connector import MqttConnector +from mqtt_connector import MqttConnector +from thing import Thing - -MODE_HTTP_PULL = 0 -MODE_HTTP_PUSH = 1 -MODE_MQTT = 2 +import constants class Gateway: @@ -35,7 +33,7 @@ def __init__(self, name, deviot_server, connector_server, kind="device", account if urlparse(deviot_server).scheme == '': # the default protocol for a DevIot server is HTTPS deviot_server = "https://" + deviot_server self.deviot_server = urlparse(deviot_server) - self.mode = MODE_MQTT + self.mode = constants.MODE_MQTT self.things = {} self.__registration_started = False self.__registered = 0 @@ -124,19 +122,28 @@ def stop(self): else: logger.warn("gateway service {name} already stopped".format(name=self)) - def register(self, thing): - if thing.id in self.things: - logger.warn("thing {thing} is already registered".format(thing=thing)) - else: - self.things[thing.id] = thing - logger.info("thing {thing} registered".format(thing=thing)) - def deregister(self, thing): - if thing.id in self.things: - del self.things[thing.id] - logger.info("thing {thing} deregistered".format(thing=thing)) - else: - logger.warn("thing {thing} is not registered".format(thing=thing)) + def register(self, *things): + for thing in things: + if not isinstance(thing, Thing): + logger.error("Invalid thing {thing}, only Thing instance is supported".format(thing=thing)) + continue + if thing.id in self.things: + logger.warn("thing {thing} is already registered".format(thing=thing)) + else: + self.things[thing.id] = thing + logger.info("thing {thing} registered".format(thing=thing)) + + def deregister(self, *things): + for thing in things: + if not isinstance(thing, Thing): + logger.error("Invalid thing {thing}, only Thing instance is supported".format(thing=thing)) + continue + if thing.id in self.things: + del self.things[thing.id] + logger.info("thing {thing} deregistered".format(thing=thing)) + else: + logger.warn("thing {thing} is not registered".format(thing=thing)) def get_data(self): return {key: value.get_data() for (key, value) in self.things.items()} @@ -147,10 +154,19 @@ def send_data(self, data): else: logger.warn("{connector} not connected yet".format(connector=self.connector)) - def update_thing(self, thing_id, **new_values): + def update_thing(self, thing, **new_values): + if isinstance(thing, str): + thing_id = thing + elif isinstance(thing, Thing): + thing_id = thing.id + else: + logger.error("Invalid thing {thing}, only string and Thing are supported".format(thing=thing)) + return if thing_id in self.things: thing = self.things[thing_id] thing.update_property(**new_values) + else: + logger.warn("There is no thing with id {id}".format(id=thing_id)) # args: payload of the subscribed MQTT message def call_action(self, args): From 3a3f69d48cc2416d13d9d5314a1b0c46fd8f1a12 Mon Sep 17 00:00:00 2001 From: Changjun Lim Date: Thu, 14 Mar 2019 10:22:24 -0700 Subject: [PATCH 10/19] Add the sample code for GrovePi --- cisco_deviot/thing.py | 2 +- sample_code_for_GrovePi_sensor.py | 74 +++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 sample_code_for_GrovePi_sensor.py diff --git a/cisco_deviot/thing.py b/cisco_deviot/thing.py index 157662a..6e3db5d 100644 --- a/cisco_deviot/thing.py +++ b/cisco_deviot/thing.py @@ -118,7 +118,7 @@ def add_action(self, *thing_actions): elif isinstance(act, Action): self.actions.append(act) else: - logger.error("invalid property {property}, only string and Property are supported".format(property=act)) + logger.error("invalid action {action}, only string and Action are supported".format(action=act)) return self # method: action name diff --git a/sample_code_for_GrovePi_sensor.py b/sample_code_for_GrovePi_sensor.py new file mode 100644 index 0000000..c38e64e --- /dev/null +++ b/sample_code_for_GrovePi_sensor.py @@ -0,0 +1,74 @@ +import time +import traceback +from grovepi import grovepi + +from cisco_deviot.gateway import Gateway +from cisco_deviot.thing import Thing +from cisco_deviot.thing import Property +from cisco_deviot import constants + +pin = {"A0":14, "A1":15, "A2":16, "D2":2, "D3":3, "D4":4, "D5":5, "D6":6, "D7":7, "D8":8} + +# connect each sensor to a pin +light_sensor_pin = pin["A2"] +led_pin = pin["D3"] +ultrasonic_sensor_pin = pin["D4"] +temperature_sensor_pin = pin["D7"] +button_sensor_pin = pin["D8"] + +grovepi.pinMode(light_sensor_pin, "INPUT") +grovepi.pinMode(led_pin, "OUTPUT") +grovepi.pinMode(ultrasonic_sensor_pin, "INPUT") +grovepi.pinMode(temperature_sensor_pin, "INPUT") +grovepi.pinMode(button_sensor_pin, "INPUT") + + +# turn on/off the led when receive action from DevIot +# action name will be 'on' or 'off' +def trigger_grove_led(action): + print('led get action:' + action) + if action == 'on': + return lambda:grovepi.digitalWrite(led, 1) + else: + return lambda:grovepi.digitalWrite(led, 0) + +account = 'your_id@cisco.com' +app = Gateway('grovepi', 'deviot.cisco.com', 'deviot.cisco.com:18883', account) + +# the parameters of a Thing constructor are: id, display name, kind +light_sensor = Thing('grove_light', 'GroveLight', 'light') +light_sensor.add_property('light') +ultrasonic_sensor = Thing('grove_distance', 'GroveDistance', 'distance') +ultrasonic_sensor.add_property('distance') +temperature_sensor = Thing('grove_temp_hum', 'GroveTempHumd', 'temperature') +temperature_sensor.add_property('temperature', 'humidity') +button_sensor = Thing('grove_button', 'GroveButton', 'button') +button_sensor.add_property(Property('value', constants.PROPERTY_TYPE_BOOL)) +led = Thing('grove_led', "GroveLED", led) +led.add_action('on', 'off') +led.on = trigger_grove_led('on') +led.off = trigger_grove_led('off') + +app.register(light_sensor, ultrasonic_sensor, temperature_sensor, button_sensor, led) + +app.start() + +while True: + try: + light_value = grovepi.analogRead(light_sensor_pin) + [temperature_value, humidity_value] = grovepi.dht(temperature_sensor_pin, 0) + distance_value = grovepi.ultrasonicRead(ultrasonic_sensor_pin) + button_value = grovepi.digitalRead(button_sensor_pin) + + light_sensor.update_property(light=light_value) + app.update_thing(light_sensor, light=light_value) + app.update_thing(temperature_sensor, temperature=temperature_value, humidity=humidity_value) + app.update_thing(ultrasonic_sensor, distance=distance_value) + app.update_thing(button_sensor, value=button_value) + + time.sleep(0.3) + except: + traceback.print_exc() + break + +app.stop() \ No newline at end of file From 59a69c3ba5f5faf2959cfad1a32092c4311389ec Mon Sep 17 00:00:00 2001 From: Changjun Lim Date: Fri, 5 Apr 2019 15:17:40 -0700 Subject: [PATCH 11/19] use Enum instead of constants class, and add error handling --- README.md | 52 +------- cisco_deviot.egg-info/PKG-INFO | 193 +++++++++++++++++++++++++++++- cisco_deviot.egg-info/SOURCES.txt | 1 + cisco_deviot/constants.py | 8 -- cisco_deviot/gateway.py | 19 ++- cisco_deviot/mqtt_connector.py | 4 +- cisco_deviot/thing.py | 46 ++++--- 7 files changed, 242 insertions(+), 81 deletions(-) delete mode 100644 cisco_deviot/constants.py diff --git a/README.md b/README.md index 5273ccc..d1d6179 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,8 @@ You can use this SDK to register devices to DevIoT, and sync up data and actions * [API](#api) ## Requirement -1. [Python 2.7](https://www.python.org/downloads/): This SDK base on the Python 2.7.10 -2. [paho-mqtt](https://eclipse.org/paho/clients/python/): This SDK use this library to build a MQTT client +1. [Python 2.7](https://www.python.org/downloads/): This SDK is based on the Python 2.7.3 +2. [paho-mqtt](https://eclipse.org/paho/clients/python/): This SDK uses this library to build a MQTT client ## Usage 1. You can use sample code to register GrovePi sensors and simulated sensors to DevIoT. @@ -93,52 +93,6 @@ disconnect gateway app.stop() ```   -## Run sample code on Raspberry Pi -#### Build the hardware -###### 1. Prepare your Raspberry Pi os environment in your SD card -* Download the OS for Raspberry Pi form [RASPBIAN JESSIE](https://www.raspberrypi.org/downloads/raspbian/) -* Format you SD card -* Use window install the OS image to the SD card. you can use [Win32 Disk Manager](https://sourceforge.net/projects/win32diskimager/). - I strongly recommend you do this using Windows, I have met many issues when i installed it by mac OS -* Attach the SD card to the Raspberry Pi - -You also can follow [this instructions](https://www.raspberrypi.org/documentation/installation/noobs.md) - -######2. Connect the GrovePi to the Raspberry Pi. - -######3. Connect Raspberry Pi with the power and network. - -######4. Connect Raspberry Pi with Display using HDMI cable. - -#### Build the software environment -######5. Install the Python 2.7. -* Check the version of python that Raspberry Pi has. This sample code is based on python 2.7.3 or later. in most time, the Raspberry Pi os have installed the python 2.7.3 or later, if not, you can install the python follow [here](https://www.raspberrypi.org/documentation/linux/software/python.md) - -######6. Install GrovePi SDK. - -* Make sure your Raspberry Pi is connected to the Internet. -* Type the following commands in terminal window - - sudo apt-get update - sudo apt-get install rpi.gpio - -* [Follow the tutorial for setting up the GrovePi](http://www.dexterindustries.com/GrovePi/get-started-with-the-grovepi/setting-software/). -* Reboot your Raspberry Pi board. - -Your SD card now has what it needs to start using the GrovePi. -[More information about installing GrovePi SDK](http://www.dexterindustries.com/GrovePi/get-started-with-the-grovepi/) - -#### Run GrovePi sample code - -######7. Download SDK and run the sample code -* Download SDK and go to the main directory of the repo - - git clone https://wwwin-github.cisco.com/DevIoT/gateway-python-sdk.git - cd gateway-pythohn-sdk -* Run the sample python code - - python sample_code_for_GrovePi_sensor.py -  ## API ### Gateway #### Constructor @@ -227,7 +181,7 @@ The Property() constructor takes the following arguments: **name** The name of a property. It should be unique in a thing. **type** -The variable type of a property. There are 4 types: int, bool, string, color. You can use constants.PROPERTY_TYPE_INT, constants.PROPERTY_TYPE_BOOL, constants.PROPERTY_TYPE_STRING, constants.PROPERTY_TYPE_COLOR after importing constants. +The variable type of a property. There are 4 types: int, bool, string, color. You can use PropertyType.INT, PropertyType.BOOL, PropertyType.STRING, PropertyType.COLOR after importing 'PropertyType' class from 'cisco_deviot.thing'. **value** The value of a property. **range** diff --git a/cisco_deviot.egg-info/PKG-INFO b/cisco_deviot.egg-info/PKG-INFO index e2ef1fb..721dcda 100644 --- a/cisco_deviot.egg-info/PKG-INFO +++ b/cisco_deviot.egg-info/PKG-INFO @@ -6,9 +6,198 @@ Home-page: https://github.com/CiscoDevIoT/gateway-python-sdk Author: Haihua Xiao Author-email: hhxiao@gmail.com License: Apache 2.0 -Description: #gateway-python-sdk +Description: # gateway-python-sdk - Python SDK for DevIoT gateway service + ### Python SDK for DevIoT service ([https://deviot.cisco.com/](https://deviot.cisco.com/)) + + You can use this SDK to register devices to DevIoT, and sync up data and actions between the devices and DevIoT. + + ## Table of contents + + * [Getting Started](#getting-started) + * [Run sample code on Raspberry Pi](#run-sample-code-on-raspberry-pi) + * [API](#api) + + ## Requirement + 1. [Python 2.7](https://www.python.org/downloads/): This SDK is based on the Python 2.7.3 + 2. [paho-mqtt](https://eclipse.org/paho/clients/python/): This SDK uses this library to build a MQTT client + + ## Usage + 1. You can use sample code to register GrovePi sensors and simulated sensors to DevIoT. + 2. You can also use SDK to register other sensors and systems to DevIoT. + + ## Term + + - **Gateway**: 'Gateway' is a device connected to DevIoT like a Raspberry Pi board or a mobile phone + - **Thing**: 'Thing' is a sensor or a module in a gateway like a light sensor, LED, or GPS. A thing is an icon in DevIoT. A gateway can have several things. + - **Property**: 'Property' is a variable measured by a thing. For instance, Latitude and longitude are properties of a GPS. A thing can have several properties. + - **Action**: 'Action' is a command for a module. For example, turning on and off LED are two different actions. A thing can have several actions. + + ## Getting Started + #### 0) Check the version of python (terminal) + In order to use this SDK, the version of python should be 2.x. You can check it with the following command. + ``` + python --version + ``` + + #### 1) Set up SDK package (terminal) + Clone SDK git repository + ``` + git clone https://wwwin-github.cisco.com/DevIoT/gateway-python-sdk.git + cd gateway-pythohn-sdk + ``` + install sdk package on python 2 + ``` + python setup.py install + ``` + + #### 2) Connect gateway (python code) + Import SDK + ``` + from cisco_deviot.gateway import Gateway + from cisco_deviot.thing import Thing + from cisco_deviot.thing import Property + import constants + ``` + Construct a Gateway object + ``` + account = "your_id@cisco.com" + app = Gateway("gateway_name", "deviot.cisco.com", "deviot.cisco.com:18883", "device", account) + ``` + + Contruct a thing instance + ``` + thing = Thing("thing-id", "thing-name", "thing-kind") + ``` + + Add a property to the thing + ``` + property = Property("variable_name", constants.PROPERTY_TYPE_INT, 0) + thing.add_property(property); + ``` + + Add an action to the thing + ``` + thing.add_action("sameple_action") + def custom_function(): + print("action") + + thing.sameple_action = custom_function + ``` + + Register the sensor to the gateway + ``` + app.register(thing) + ``` + + Connect the gateway to the server + ``` + app.start() + ``` + + #### 3) disconnect gateway (python code) + disconnect gateway + ``` + app.stop() + ``` +   + ## API + ### Gateway + #### Constructor + ``` + Gateway(name, deviot_server, connector_server, kind="device", account="") + ``` + The Gateway() constructor takes the following arguments: + **name** + The unique name of a gateway + **deviot_server** + The address for the DevIoT server. It does not need to add the protocol. The default protocol is HTTPS(secure connection). The public DevIoT server address is 'deviot.cisco.com' + **connector_server** + The address for the MQTT server. It does not need to add the protcol. The default protocol is TCP(unsecure connection) and the default port number is 1883. The public MQTT server address is 'deviot.cisco.com:18883' + **kind** + The kind of a gateway + **account** + Your DevIoT account. you also can use empty string, it will let all account get the data of your gateway. + #### register() + ``` + register(*things) + ``` + The register() function adds things to the gateway. The thing should not have been already registered. + **thing** + A *Thing* instance to register + #### deregister() + ``` + deregister(*things) + ``` + The deregister() function deletes things from the gateway. + **thing** + A *Thing* instance to deregister + #### update_thing() + ``` + update_thing(thing, **new_value) + ``` + The update_thing() function updates the values of a thing. + **thing** + The thing to be updated. It can be the string id of a thing or a Thing instance. + **\*\*new_value** + The keyword arguments for the updated values. The key is the name of properties and the value is the new value. + + #### start() + ``` + start() + ``` + Connect the things of the gateway to the DevIoT server and the MQTT server + #### stop() + ``` + stop() + ``` + Disconnect the gateway from the DevIoT server and the MQTT server. + ### Thing + #### Constructor + ``` + Thing(id, name, kind=None) + ``` + The Thing() constructor takes the following arguments: + **id** + the unique id of a thing + **name** + the display name of a thing in DevIot + **kind** + the kind of a thing + + #### add_property() + ``` + add_property(*thing_properties) + ``` + The add_property() adds properties to a Thing instance. When a string is used for arguments, new Property instance named the string is added to the Thing instance. + **\*thing_properties** + The list of Property instances or the string value of properties' name. + #### add_action() + ``` + add_action(*thing_actions) + ``` + The add_action() adds actions to a Thing instance. When a string is used for arguments, new Action instance named the string is added to the Thing instance. + **\*thing_properties** + The list of Property instances or the string value of actions' name. + + ### Property + #### Constructor + ``` + Property(name, type=0, value=None, range=None, unit=None, description=None) + ``` + The Property() constructor takes the following arguments: + **name** + The name of a property. It should be unique in a thing. + **type** + The variable type of a property. There are 4 types: int, bool, string, color. You can use constants.PROPERTY_TYPE_INT, constants.PROPERTY_TYPE_BOOL, constants.PROPERTY_TYPE_STRING, constants.PROPERTY_TYPE_COLOR after importing constants. + **value** + The value of a property. + **range** + The range of a property's value. + **unit** + The unit of a property. It is string value. + **description** + The description for a property. It is shown at the page of each thing. Keywords: cisco devnet deviot gateway Platform: UNKNOWN diff --git a/cisco_deviot.egg-info/SOURCES.txt b/cisco_deviot.egg-info/SOURCES.txt index 567f095..4bd9f37 100644 --- a/cisco_deviot.egg-info/SOURCES.txt +++ b/cisco_deviot.egg-info/SOURCES.txt @@ -6,6 +6,7 @@ requirements.txt setup.cfg setup.py cisco_deviot/__init__.py +cisco_deviot/constants.py cisco_deviot/gateway.py cisco_deviot/mqtt_connector.py cisco_deviot/thing.py diff --git a/cisco_deviot/constants.py b/cisco_deviot/constants.py deleted file mode 100644 index 7264c93..0000000 --- a/cisco_deviot/constants.py +++ /dev/null @@ -1,8 +0,0 @@ -PROPERTY_TYPE_INT = 0 -PROPERTY_TYPE_STRING = 1 -PROPERTY_TYPE_BOOL = 2 -PROPERTY_TYPE_COLOR = 3 - -MODE_HTTP_PULL = 0 -MODE_HTTP_PUSH = 1 -MODE_MQTT = 2 diff --git a/cisco_deviot/gateway.py b/cisco_deviot/gateway.py index e670dea..b1ea618 100644 --- a/cisco_deviot/gateway.py +++ b/cisco_deviot/gateway.py @@ -15,13 +15,19 @@ import threading import time import json +import importlib from urlparse import urlparse from cisco_deviot import logger from mqtt_connector import MqttConnector from thing import Thing -import constants +from enum import EnumMeta + +class Mode(EnumMeta): + HTTP_PULL = 0 + HTTP_PUSH = 1 + MQTT = 2 class Gateway: @@ -33,7 +39,7 @@ def __init__(self, name, deviot_server, connector_server, kind="device", account if urlparse(deviot_server).scheme == '': # the default protocol for a DevIot server is HTTPS deviot_server = "https://" + deviot_server self.deviot_server = urlparse(deviot_server) - self.mode = constants.MODE_MQTT + self.mode = Mode.MQTT self.things = {} self.__registration_started = False self.__registered = 0 @@ -104,7 +110,7 @@ def __register(self): self.__registered = 1 def start(self): - if self.__registration_started: + if self.is_started(): logger.warn("gateway service {name} already started".format(name=self)) else: self.__registration_started = True @@ -115,20 +121,21 @@ def start(self): logger.info("gateway service {name} started".format(name=self)) def stop(self): - if self.__registration_started: + if self.is_started(): self.__registration_started = False self.connector.stop() logger.info("gateway service {name} stopped".format(name=self)) else: logger.warn("gateway service {name} already stopped".format(name=self)) + def is_started(self): + return self.__registration_started def register(self, *things): for thing in things: if not isinstance(thing, Thing): logger.error("Invalid thing {thing}, only Thing instance is supported".format(thing=thing)) - continue - if thing.id in self.things: + elif thing.id in self.things: logger.warn("thing {thing} is already registered".format(thing=thing)) else: self.things[thing.id] = thing diff --git a/cisco_deviot/mqtt_connector.py b/cisco_deviot/mqtt_connector.py index 91abbad..53411b2 100644 --- a/cisco_deviot/mqtt_connector.py +++ b/cisco_deviot/mqtt_connector.py @@ -46,7 +46,9 @@ def __init__(self, gateway, mqtt_server): def __publish_function(self): while self.__connection_start: if self.is_connected(): - self.publish(self.gateway.get_data()) + data = self.gateway.get_data() + if len(data) != 0: + self.publish(data) time.sleep(0.5) def start(self): diff --git a/cisco_deviot/thing.py b/cisco_deviot/thing.py index 6e3db5d..dc17c43 100644 --- a/cisco_deviot/thing.py +++ b/cisco_deviot/thing.py @@ -13,23 +13,31 @@ import collections from cisco_deviot import logger +from enum import EnumMeta import constants def default_value_for_type(stype): - if stype == constants.PROPERTY_TYPE_INT: + if stype == PropertyType.INT: return 0 - if stype == constants.PROPERTY_TYPE_BOOL: + if stype == PropertyType.BOOL: return False - if stype == constants.PROPERTY_TYPE_STRING: + if stype == PropertyType.STRING: return "" - if stype == constants.PROPERTY_TYPE_COLOR: + if stype == PropertyType.COLOR: return "FFFFFF" return None +class PropertyType(EnumMeta): + INT = 0 + STRING = 1 + BOOL = 2 + COLOR = 3 + + class Property: - def __init__(self, name, type=constants.PROPERTY_TYPE_INT, value=None, range=None, unit=None, description=None): + def __init__(self, name, type=PropertyType.INT, value=None, range=None, unit=None, description=None): self.name = name self.type = type if value is None: @@ -92,23 +100,31 @@ def __str__(self): def get_data(self): return {prop.name: prop.value for prop in self.properties} + # check the property, of which name is 'prop', is in properies + def _has_property(self, prop): + return (prop in [property.name for property in self.properties]) + def add_property(self, *thing_properties): - for prop in thing_properties: - if isinstance(prop, str): - self.properties.append(Property(prop)) - elif isinstance(prop, Property): - self.properties.append(prop) + for property in thing_properties: + if isinstance(property, str): + added_property = Property(property) + elif isinstance(property, Property): + added_property = property else: - logger.error( - "invalid property {property}, only string and Property are supported".format(property=prop)) + logger.error("invalid property {property}, only string and Property are supported".format(property=property)) + continue + if self._has_property(added_property.name): + logger.error("Invalid property name {property}, already registered.".format(property=added_property.name)) + break + self.properties.append(property) return self def update_property(self, **new_properties): for new_prop_name in new_properties: - for prop in self.properties: - if new_prop_name == prop.name: - prop.value = new_properties[new_prop_name] + for existing_prop in self.properties: + if new_prop_name == existing_prop.name: + existing_prop.value = new_properties[new_prop_name] break def add_action(self, *thing_actions): From 67372416da2b7398c8ff07a173800fa49fb2bf7c Mon Sep 17 00:00:00 2001 From: Changjun Lim Date: Fri, 5 Apr 2019 15:51:11 -0700 Subject: [PATCH 12/19] Add load() method to Gateway class --- README.md | 10 ++++++++++ cisco_deviot/gateway.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/README.md b/README.md index d1d6179..363a806 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,16 @@ register(*things) The register() function adds things to the gateway. The thing should not have been already registered. **thing** A *Thing* instance to register +#### load() +``` +load(filename, class_directory=None) +``` +The load() function registers things from an JSON file named [filename] and the custom Thing-sub classes inside [class_directory]. +**filename** +The JSON file having information of things. filename should include its extension. The way to write this JSON file is described in **gateway-python-starter-kit**. +**class_directory** +The directory which has custom Thing-sub class. If the Thing-sub class is defined in the same directory, class_directory should be omitted or be None. + #### deregister() ``` deregister(*things) diff --git a/cisco_deviot/gateway.py b/cisco_deviot/gateway.py index b1ea618..35407f8 100644 --- a/cisco_deviot/gateway.py +++ b/cisco_deviot/gateway.py @@ -141,6 +141,37 @@ def register(self, *things): self.things[thing.id] = thing logger.info("thing {thing} registered".format(thing=thing)) + # load JSON file 'filename' and register instances of custom Thing class which are defined in 'class_directory' + def load(self, filename, class_directory=None): + try: + with open(filename) as json_data: + things = json.load(json_data) + except IOError: + logger.error("Cannot open such file: {file}".format(file=filename)) + return + + for i, thing in enumerate(things): + name = str(thing.get("name", "")) + if name == "": + logger.error("There is no sensor name") + continue + stype = str(thing.get("type", "thing")) + sid = stype + "_" + name + "-" + str(i) + if stype == 'thing': + c = Thing + else: + try: + class_name = stype if (class_directory is None) else (class_directory + "." + stype) + m = importlib.import_module(class_name) + c = getattr(m, stype.capitalize()) + except: + logger.error("There is no Thing class called {name}".format(name=class_name)) + continue + instance = c(sid, name) + if "options" in thing: + instance.options = thing["options"] + self.register(instance) + def deregister(self, *things): for thing in things: if not isinstance(thing, Thing): From 0ad2802e256bf2cc55230ee96a0b82dab9af288f Mon Sep 17 00:00:00 2001 From: Changjun Lim Date: Tue, 9 Apr 2019 14:49:21 -0700 Subject: [PATCH 13/19] update README and set the default value for the server address in Gateway --- README.md | 24 ++++++++++++------------ cisco_deviot/gateway.py | 17 +++++++++-------- setup.py | 2 +- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 363a806..0ac88b3 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,8 @@ python --version #### 1) Set up SDK package (terminal) Clone SDK git repository ``` -git clone https://wwwin-github.cisco.com/DevIoT/gateway-python-sdk.git -cd gateway-pythohn-sdk +git clone https://github.com/ailuropoda0/gateway-python-sdk.git +cd gateway-python-sdk ``` install sdk package on python 2 ``` @@ -44,17 +44,17 @@ python setup.py install ``` #### 2) Connect gateway (python code) +You can refer the example code in [gateway-python-starter-kit](https://wwwin-github.cisco.com/DevIoT/gateway-python-starter-kit). + Import SDK ``` from cisco_deviot.gateway import Gateway -from cisco_deviot.thing import Thing -from cisco_deviot.thing import Property -import constants +from cisco_deviot.thing import Thing, Property, PropertyType ``` Construct a Gateway object ``` account = "your_id@cisco.com" -app = Gateway("gateway_name", "deviot.cisco.com", "deviot.cisco.com:18883", "device", account) +app = Gateway(name="gateway_name", account=account) ``` Contruct a thing instance @@ -64,7 +64,7 @@ thing = Thing("thing-id", "thing-name", "thing-kind") Add a property to the thing ``` -property = Property("variable_name", constants.PROPERTY_TYPE_INT, 0) +property = Property("variable_name", PropertyType.INT, 0) thing.add_property(property); ``` @@ -114,7 +114,7 @@ Your DevIoT account. you also can use empty string, it will let all account get ``` register(*things) ``` -The register() function adds things to the gateway. The thing should not have been already registered. +The register() function adds things to the gateway. The thing should not have been already registered. **thing** A *Thing* instance to register #### load() @@ -122,9 +122,9 @@ A *Thing* instance to register load(filename, class_directory=None) ``` The load() function registers things from an JSON file named [filename] and the custom Thing-sub classes inside [class_directory]. -**filename** -The JSON file having information of things. filename should include its extension. The way to write this JSON file is described in **gateway-python-starter-kit**. -**class_directory** +**filename** +The JSON file having information of things. filename should include its extension. The way to write this JSON file is described in **gateway-python-starter-kit**. +**class_directory** The directory which has custom Thing-sub class. If the Thing-sub class is defined in the same directory, class_directory should be omitted or be None. #### deregister() @@ -132,7 +132,7 @@ The directory which has custom Thing-sub class. If the Thing-sub class is define deregister(*things) ``` The deregister() function deletes things from the gateway. -**thing** +**\*things** A *Thing* instance to deregister #### update_thing() ``` diff --git a/cisco_deviot/gateway.py b/cisco_deviot/gateway.py index 35407f8..3f4345c 100644 --- a/cisco_deviot/gateway.py +++ b/cisco_deviot/gateway.py @@ -16,6 +16,7 @@ import time import json import importlib +import socket from urlparse import urlparse from cisco_deviot import logger @@ -31,7 +32,7 @@ class Mode(EnumMeta): class Gateway: - def __init__(self, name, deviot_server, connector_server, kind="device", account=""): + def __init__(self, name, deviot_server="deviot.cisco.com", connector_server="deviot.cisco.com:18883", kind="device", account=""): name = name.replace("-", "_") self.name = name self.kind = kind @@ -147,16 +148,16 @@ def load(self, filename, class_directory=None): with open(filename) as json_data: things = json.load(json_data) except IOError: - logger.error("Cannot open such file: {file}".format(file=filename)) + logger.error("cannot open such file: {file}".format(file=filename)) return - for i, thing in enumerate(things): + for thing in things: + stype = str(thing.get("type", "thing")) name = str(thing.get("name", "")) if name == "": - logger.error("There is no sensor name") - continue - stype = str(thing.get("type", "thing")) - sid = stype + "_" + name + "-" + str(i) + name = socket.gethostname() + "'s " + stype + logger.warn("There is no sensor name") + sid = name + "_" + str(int(time.time())) if stype == 'thing': c = Thing else: @@ -165,7 +166,7 @@ def load(self, filename, class_directory=None): m = importlib.import_module(class_name) c = getattr(m, stype.capitalize()) except: - logger.error("There is no Thing class called {name}".format(name=class_name)) + logger.error("error to import the class {name}".format(name=class_name)) continue instance = c(sid, name) if "options" in thing: diff --git a/setup.py b/setup.py index dfb722f..20a681d 100644 --- a/setup.py +++ b/setup.py @@ -54,4 +54,4 @@ zip_safe = False, packages = find_packages(), install_requires = ['paho-mqtt'], - ) +) From 654c0df4f0555a5e9284b0b51b9f19cd64b76c22 Mon Sep 17 00:00:00 2001 From: Changjun Lim Date: Tue, 30 Apr 2019 11:48:12 -0700 Subject: [PATCH 14/19] update README --- README.md | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0ac88b3..748585c 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,11 @@ ### Python SDK for DevIoT service ([https://deviot.cisco.com/](https://deviot.cisco.com/)) -You can use this SDK to register devices to DevIoT, and sync up data and actions between the devices and DevIoT. +You can use this SDK to register devices to DevIoT and sync up data and actions between the devices and DevIoT. ## Table of contents * [Getting Started](#getting-started) -* [Run sample code on Raspberry Pi](#run-sample-code-on-raspberry-pi) * [API](#api) ## Requirement @@ -97,15 +96,15 @@ app.stop() ### Gateway #### Constructor ``` -Gateway(name, deviot_server, connector_server, kind="device", account="") +Gateway(name, deviot_server="deviot.cisco.com", connector_server="deviot.cisco.com:18883", kind="device", account="") ``` The Gateway() constructor takes the following arguments: **name** The unique name of a gateway **deviot_server** -The address for the DevIoT server. It does not need to add the protocol. The default protocol is HTTPS(secure connection). The public DevIoT server address is 'deviot.cisco.com' +The address for the DevIoT server. The default value is 'deviot.cisco.com'. It does not need to add the protocol. The default protocol is HTTPS(secure connection). **connector_server** -The address for the MQTT server. It does not need to add the protcol. The default protocol is TCP(unsecure connection) and the default port number is 1883. The public MQTT server address is 'deviot.cisco.com:18883' +The address for the MQTT server. The default value is 'deviot.cisco.com:18883'. It does not need to add the protcol. The default protocol is TCP(unsecure connection) and the default port number is 1883. **kind** The kind of a gateway **account** @@ -200,3 +199,22 @@ The range of a property's value. The unit of a property. It is string value. **description** The description for a property. It is shown at the page of each thing. + +### Action +#### Constructor +``` +Action(name, **kwargs) +``` +The Action() constructor takes the following arguments: +**name** +The name of Action. The method with the same name should be defined in a class or an instance. So the name must not overlap with other methods of Thing class like *add_property* and *add_action, and the name cannot contain space ' '. +**\*\*kwargs** +The keyword arguments for properties in an action. Key is the name of a property and the value of the property. It is not mandatory. + +#### add_parameter() +``` +add_parameter(action_parameter) +``` +The *add_parameter* adds properties to an Action instance. +**action_parameter** +A Property instance to be added From 3a1f4892b3f9fec1d578111d7f34d21eb7217302 Mon Sep 17 00:00:00 2001 From: Changjun Lim Date: Wed, 8 May 2019 13:37:28 -0700 Subject: [PATCH 15/19] Update README and refactor codes --- README.md | 14 ++++++++------ cisco_deviot/gateway.py | 25 +++++++++++++------------ cisco_deviot/mqtt_connector.py | 4 ++-- cisco_deviot/thing.py | 2 -- 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 748585c..afd121f 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,8 @@ You can use this SDK to register devices to DevIoT and sync up data and actions 2. [paho-mqtt](https://eclipse.org/paho/clients/python/): This SDK uses this library to build a MQTT client ## Usage -1. You can use sample code to register GrovePi sensors and simulated sensors to DevIoT. -2. You can also use SDK to register other sensors and systems to DevIoT. +1. You can use SDK to register your own sensors and systems to DevIoT. +2. Use [Starter-ki](https://wwwin-github.cisco.com/DevIoT/gateway-python-starter-kit) to run sample codes ## Term @@ -52,7 +52,7 @@ from cisco_deviot.thing import Thing, Property, PropertyType ``` Construct a Gateway object ``` -account = "your_id@cisco.com" +account = "your_id@cisco.com" # enter your DevIoT account app = Gateway(name="gateway_name", account=account) ``` @@ -60,14 +60,16 @@ Contruct a thing instance ``` thing = Thing("thing-id", "thing-name", "thing-kind") ``` +You can only use Thing as either an input component(sensor) or an output component. +If your thing has both properties and actions, it acts as an input component. -Add a property to the thing +(1) Input: Add a property to the thing ``` property = Property("variable_name", PropertyType.INT, 0) thing.add_property(property); ``` -Add an action to the thing +(2) Output: Add an action to the thing ``` thing.add_action("sameple_action") def custom_function(): @@ -184,7 +186,7 @@ The list of Property instances or the string value of actions' name. ### Property #### Constructor ``` -Property(name, type=0, value=None, range=None, unit=None, description=None) +Property(name, type=PropertyType.INT, value=None, range=None, unit=None, description=None) ``` The Property() constructor takes the following arguments: **name** diff --git a/cisco_deviot/gateway.py b/cisco_deviot/gateway.py index 3f4345c..0a21567 100644 --- a/cisco_deviot/gateway.py +++ b/cisco_deviot/gateway.py @@ -72,23 +72,12 @@ def __del_none(self, d): return d def __register(self): - import collections - model = collections.OrderedDict([("name", self.name), - ("kind", self.kind), - ("mode", self.mode), - ("owner", self.owner), - ("host", self.host), - ("port", self.port), - ("data", self.data), - ("action", self.action), - ("sensors", [thing.get_model() for thing in self.things.values()])]) - json_string = json.dumps(self.__del_none(model), sort_keys=False) try: if self.deviot_server.scheme == 'http': conn = httplib.HTTPConnection(self.deviot_server.netloc) else: conn = httplib.HTTPSConnection(self.deviot_server.netloc) - conn.request("POST", "/api/v1/gateways", json_string, {'Content-Type': 'application/json'}) + conn.request("POST", "/api/v1/gateways", self.get_model(), {'Content-Type': 'application/json'}) response = conn.getresponse() code = int(response.status) if code < 200 or code > 300: @@ -237,5 +226,17 @@ def call_action(self, args): else: logger.warn("thing {thing} not registered".format(thing=tid)) + def get_model(self): + model = collections.OrderedDict([("name", self.name), + ("kind", self.kind), + ("mode", self.mode), + ("owner", self.owner), + ("host", self.host), + ("port", self.port), + ("data", self.data), + ("action", self.action), + ("sensors", [thing.get_model() for thing in self.things.values()])]) + return json.dumps(self.__del_none(model), sort_keys=False) + def __str__(self): return "{name}({kind})".format(name=self.name, kind=self.kind) diff --git a/cisco_deviot/mqtt_connector.py b/cisco_deviot/mqtt_connector.py index 53411b2..219bf15 100644 --- a/cisco_deviot/mqtt_connector.py +++ b/cisco_deviot/mqtt_connector.py @@ -38,8 +38,8 @@ def __init__(self, gateway, mqtt_server): surl = urlparse(mqtt_server) self.host = surl.hostname self.port = surl.port - if self.port is None: - self.port = 1883 # The default port number + if self.port is None: # The default MQTT port number is 1883 + self.port = 1883 self.data = "/deviot/{ns}/{name}/data".format(name=name, ns=ns) self.action = "/deviot/{ns}/{name}/action".format(name=name, ns=ns) diff --git a/cisco_deviot/thing.py b/cisco_deviot/thing.py index dc17c43..ea8df5c 100644 --- a/cisco_deviot/thing.py +++ b/cisco_deviot/thing.py @@ -14,8 +14,6 @@ from cisco_deviot import logger from enum import EnumMeta -import constants - def default_value_for_type(stype): if stype == PropertyType.INT: From 2c5fc6dd9635108ebf77c001e57dbc9a6ab81d55 Mon Sep 17 00:00:00 2001 From: Changjun Lim Date: Thu, 9 May 2019 13:38:02 -0700 Subject: [PATCH 16/19] Add the part to deregister a gateway from DevIoT via HTTP(S) and Refactor the connection part --- cisco_deviot/gateway.py | 73 +++++++++++++++++++++++----------- cisco_deviot/mqtt_connector.py | 2 +- 2 files changed, 51 insertions(+), 24 deletions(-) diff --git a/cisco_deviot/gateway.py b/cisco_deviot/gateway.py index 0a21567..fe186d1 100644 --- a/cisco_deviot/gateway.py +++ b/cisco_deviot/gateway.py @@ -17,6 +17,7 @@ import json import importlib import socket +import collections from urlparse import urlparse from cisco_deviot import logger @@ -40,10 +41,14 @@ def __init__(self, name, deviot_server="deviot.cisco.com", connector_server="dev if urlparse(deviot_server).scheme == '': # the default protocol for a DevIot server is HTTPS deviot_server = "https://" + deviot_server self.deviot_server = urlparse(deviot_server) + if self.deviot_server.scheme == 'http': + self.__connection = httplib.HTTPConnection(self.deviot_server.netloc) + else: + self.__connection = httplib.HTTPSConnection(self.deviot_server.netloc) self.mode = Mode.MQTT self.things = {} self.__registration_started = False - self.__registered = 0 + self.__registered = False if urlparse(connector_server).scheme == '': # the default protocol of MQTT is TCP connector_server = "tcp://" + connector_server self.connector = MqttConnector(self, connector_server) @@ -57,7 +62,7 @@ def __registration_function(self): time.sleep(2) if self.__registration_started: self.__register() - time.sleep(8) + time.sleep(18) def __del_none(self, d): if isinstance(d, dict): @@ -71,34 +76,53 @@ def __del_none(self, d): self.__del_none(x) return d + # register the gateway to DevIoT server using HTTP(S) def __register(self): try: - if self.deviot_server.scheme == 'http': - conn = httplib.HTTPConnection(self.deviot_server.netloc) - else: - conn = httplib.HTTPSConnection(self.deviot_server.netloc) - conn.request("POST", "/api/v1/gateways", self.get_model(), {'Content-Type': 'application/json'}) - response = conn.getresponse() + self.__connection.request("POST", "/api/v1/gateways", self.get_model(), {'Content-Type': 'application/json'}) + response = self.__connection.getresponse() code = int(response.status) if code < 200 or code > 300: - if self.__registered != 1: - logger.error("failed to register gateway {name} to {server}: {c}-{e}".format(name=self, - server=self.deviot_server.netloc, - c=code, - e=response.reason)) - self.__registered = 1 + logger.error("failed to register gateway {name} to {server}: {c}-{e}".format(name=self, + server=self.deviot_server.netloc, + c=code, + e=response.reason)) + self.__registered = False elif not self.__registered: - if self.__registered != 2: - logger.info("registered gateway {name} to {server}".format(name=self, - server=self.deviot_server.netloc)) - self.__registered = 2 + logger.info("registered gateway {name} to {server}".format(name=self, + server=self.deviot_server.netloc)) + self.__registered = True except IOError as e: - if self.__registered != 1: - logger.error("failed to register gateway {name} to {server}: {e}".format(name=self, - server=self.deviot_server.netloc, - e=e)) - self.__registered = 1 + logger.error("failed to register gateway {name} to {server}: {e}".format(name=self, + server=self.deviot_server.netloc, + e=e)) + self.__registered = False + # deregister the gateway from DevIoT server + def __deregister(self): + if self.__registered: + try: + self.__connection.request("DELETE", "/api/v1/gateways/" + self.name) + response = self.__connection.getresponse() + code = int(response.status) + if code < 200 or code > 300: + logger.error("failed to deregister gateway {name} from {server}: {c}-{e}".format(name=self, + server=self.deviot_server.netloc, + c=code, + e=response.reason)) + self.__registered = False + elif self.__registered: + logger.info("deregistered gateway {name} from {server}".format(name=self, + server=self.deviot_server.netloc)) + self.__registered = False + except IOError as e: + logger.error("failed to deregister gateway {name} to {server}: {e}".format(name=self, + server=self.deviot_server.netloc, + e=e)) + else: + logger.info("gateway {name} has already been deregistered from {server}".format(name=self, + server=self.deviot_server.netloc)) + def start(self): if self.is_started(): logger.warn("gateway service {name} already started".format(name=self)) @@ -113,6 +137,7 @@ def start(self): def stop(self): if self.is_started(): self.__registration_started = False + self.__deregister() self.connector.stop() logger.info("gateway service {name} stopped".format(name=self)) else: @@ -121,6 +146,7 @@ def stop(self): def is_started(self): return self.__registration_started + # register things to the gateway def register(self, *things): for thing in things: if not isinstance(thing, Thing): @@ -162,6 +188,7 @@ def load(self, filename, class_directory=None): instance.options = thing["options"] self.register(instance) + # deregister things from the gateway def deregister(self, *things): for thing in things: if not isinstance(thing, Thing): diff --git a/cisco_deviot/mqtt_connector.py b/cisco_deviot/mqtt_connector.py index 219bf15..67146ec 100644 --- a/cisco_deviot/mqtt_connector.py +++ b/cisco_deviot/mqtt_connector.py @@ -78,7 +78,7 @@ def __on_disconnect__(self, client, userdata, rc): self.__reconnect(2) def __reconnect(self, backoff): - while not self.is_connected(): + while not self.is_connected() and self.__connection_start: logger.info("reconnecting to {server} in {sec} seconds ...".format(server=self, sec=backoff)) time.sleep(backoff) backoff = min(128, backoff * 2) From 7f72049183acfe16530d9d05b3a364a969b95a0c Mon Sep 17 00:00:00 2001 From: Changjun Lim Date: Thu, 9 May 2019 15:03:50 -0700 Subject: [PATCH 17/19] fix ResponseNotReady error and edit README --- README.md | 4 ++-- cisco_deviot/gateway.py | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index afd121f..d0a59bb 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ thing.add_property(property); (2) Output: Add an action to the thing ``` thing.add_action("sameple_action") -def custom_function(): +def custom_function(): ## define your action function print("action") thing.sameple_action = custom_function @@ -122,7 +122,7 @@ A *Thing* instance to register ``` load(filename, class_directory=None) ``` -The load() function registers things from an JSON file named [filename] and the custom Thing-sub classes inside [class_directory]. +The load() function registers things from an JSON file named [filename] and the custom Thing-sub classes inside [class_directory]. **filename** The JSON file having information of things. filename should include its extension. The way to write this JSON file is described in **gateway-python-starter-kit**. **class_directory** diff --git a/cisco_deviot/gateway.py b/cisco_deviot/gateway.py index fe186d1..9ebef58 100644 --- a/cisco_deviot/gateway.py +++ b/cisco_deviot/gateway.py @@ -81,6 +81,7 @@ def __register(self): try: self.__connection.request("POST", "/api/v1/gateways", self.get_model(), {'Content-Type': 'application/json'}) response = self.__connection.getresponse() + response.read() code = int(response.status) if code < 200 or code > 300: logger.error("failed to register gateway {name} to {server}: {c}-{e}".format(name=self, @@ -104,6 +105,7 @@ def __deregister(self): try: self.__connection.request("DELETE", "/api/v1/gateways/" + self.name) response = self.__connection.getresponse() + response.read() code = int(response.status) if code < 200 or code > 300: logger.error("failed to deregister gateway {name} from {server}: {c}-{e}".format(name=self, From 3183c106b602f4921786a259f4c58c74b45929d4 Mon Sep 17 00:00:00 2001 From: Changjun Lim Date: Fri, 10 May 2019 17:17:28 -0700 Subject: [PATCH 18/19] Add Python 3 part to README --- README.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d0a59bb..22bbd80 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ You can use this SDK to register devices to DevIoT and sync up data and actions * [API](#api) ## Requirement -1. [Python 2.7](https://www.python.org/downloads/): This SDK is based on the Python 2.7.3 +1. [Python](https://www.python.org/downloads/): This SDK is written in Python. We support both Python 2 and 3. 2. [paho-mqtt](https://eclipse.org/paho/clients/python/): This SDK uses this library to build a MQTT client ## Usage @@ -26,18 +26,27 @@ You can use this SDK to register devices to DevIoT and sync up data and actions ## Getting Started #### 0) Check the version of python (terminal) -In order to use this SDK, the version of python should be 2.x. You can check it with the following command. +Before using this SDK, you need to check the version of python. The master branch is written in **Python 2**. You can check it with the following command. ``` python --version ``` +If your python version is 3.x, you need to switch to 'python3' branch in the next step. + + #### 1) Set up SDK package (terminal) Clone SDK git repository ``` git clone https://github.com/ailuropoda0/gateway-python-sdk.git cd gateway-python-sdk ``` -install sdk package on python 2 +(**ONLY for Python 3 users**) Switch to Python 3 +``` +git fetch origin python3:python3 +git checkout python3 +``` + +Install sdk package on python ``` python setup.py install ``` From 0a57968a40b07bd2585af36af968c0ce33b7bddc Mon Sep 17 00:00:00 2001 From: Changjun Lim Date: Thu, 16 May 2019 11:43:31 -0700 Subject: [PATCH 19/19] Update README referring to Haihua's review --- README.md | 15 ++++--- cisco_deviot.egg-info/PKG-INFO | 74 ++++++++++++++++++++++--------- cisco_deviot.egg-info/SOURCES.txt | 1 - 3 files changed, 60 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 22bbd80..5d3f870 100644 --- a/README.md +++ b/README.md @@ -11,18 +11,18 @@ You can use this SDK to register devices to DevIoT and sync up data and actions ## Requirement 1. [Python](https://www.python.org/downloads/): This SDK is written in Python. We support both Python 2 and 3. -2. [paho-mqtt](https://eclipse.org/paho/clients/python/): This SDK uses this library to build a MQTT client +2. [paho-mqtt](https://eclipse.org/paho/clients/python/): This SDK uses this library to build an MQTT client ## Usage 1. You can use SDK to register your own sensors and systems to DevIoT. -2. Use [Starter-ki](https://wwwin-github.cisco.com/DevIoT/gateway-python-starter-kit) to run sample codes +2. Use [Starter-kit](https://wwwin-github.cisco.com/DevIoT/gateway-python-starter-kit) to run sample codes ## Term - **Gateway**: 'Gateway' is a device connected to DevIoT like a Raspberry Pi board or a mobile phone - **Thing**: 'Thing' is a sensor or a module in a gateway like a light sensor, LED, or GPS. A thing is an icon in DevIoT. A gateway can have several things. - **Property**: 'Property' is a variable measured by a thing. For instance, Latitude and longitude are properties of a GPS. A thing can have several properties. -- **Action**: 'Action' is a command for a module. For example, turning on and off LED are two different actions. A thing can have several actions. +- **Action**: 'Action' is a command for a module. For example, turning on and off LED is two different actions. A thing can have several actions. ## Getting Started #### 0) Check the version of python (terminal) @@ -46,7 +46,7 @@ git fetch origin python3:python3 git checkout python3 ``` -Install sdk package on python +Install SDK package on python ``` python setup.py install ``` @@ -65,12 +65,13 @@ account = "your_id@cisco.com" # enter your DevIoT account app = Gateway(name="gateway_name", account=account) ``` -Contruct a thing instance +Construct a Thing instance ``` thing = Thing("thing-id", "thing-name", "thing-kind") ``` -You can only use Thing as either an input component(sensor) or an output component. -If your thing has both properties and actions, it acts as an input component. +A Thing instance can have both *properties* and *actions*. But you can only use it as *either* an input component *or* an output component in each project. + +In DevIoT project page, click the 'Connect' button and connect the thing component with a rules engine by double-clicking them. The type of the component is determined by which component is first double-clicked between the thing component (input) and the rules engine (output). (1) Input: Add a property to the thing ``` diff --git a/cisco_deviot.egg-info/PKG-INFO b/cisco_deviot.egg-info/PKG-INFO index 721dcda..e2a6a12 100644 --- a/cisco_deviot.egg-info/PKG-INFO +++ b/cisco_deviot.egg-info/PKG-INFO @@ -10,12 +10,11 @@ Description: # gateway-python-sdk ### Python SDK for DevIoT service ([https://deviot.cisco.com/](https://deviot.cisco.com/)) - You can use this SDK to register devices to DevIoT, and sync up data and actions between the devices and DevIoT. + You can use this SDK to register devices to DevIoT and sync up data and actions between the devices and DevIoT. ## Table of contents * [Getting Started](#getting-started) - * [Run sample code on Raspberry Pi](#run-sample-code-on-raspberry-pi) * [API](#api) ## Requirement @@ -23,8 +22,8 @@ Description: # gateway-python-sdk 2. [paho-mqtt](https://eclipse.org/paho/clients/python/): This SDK uses this library to build a MQTT client ## Usage - 1. You can use sample code to register GrovePi sensors and simulated sensors to DevIoT. - 2. You can also use SDK to register other sensors and systems to DevIoT. + 1. You can use SDK to register your own sensors and systems to DevIoT. + 2. Use [Starter-ki](https://wwwin-github.cisco.com/DevIoT/gateway-python-starter-kit) to run sample codes ## Term @@ -43,8 +42,8 @@ Description: # gateway-python-sdk #### 1) Set up SDK package (terminal) Clone SDK git repository ``` - git clone https://wwwin-github.cisco.com/DevIoT/gateway-python-sdk.git - cd gateway-pythohn-sdk + git clone https://github.com/ailuropoda0/gateway-python-sdk.git + cd gateway-python-sdk ``` install sdk package on python 2 ``` @@ -52,34 +51,36 @@ Description: # gateway-python-sdk ``` #### 2) Connect gateway (python code) + You can refer the example code in [gateway-python-starter-kit](https://wwwin-github.cisco.com/DevIoT/gateway-python-starter-kit). + Import SDK ``` from cisco_deviot.gateway import Gateway - from cisco_deviot.thing import Thing - from cisco_deviot.thing import Property - import constants + from cisco_deviot.thing import Thing, Property, PropertyType ``` Construct a Gateway object ``` - account = "your_id@cisco.com" - app = Gateway("gateway_name", "deviot.cisco.com", "deviot.cisco.com:18883", "device", account) + account = "your_id@cisco.com" # enter your DevIoT account + app = Gateway(name="gateway_name", account=account) ``` Contruct a thing instance ``` thing = Thing("thing-id", "thing-name", "thing-kind") ``` + You can only use Thing as either an input component(sensor) or an output component. + If your thing has both properties and actions, it acts as an input component. - Add a property to the thing + (1) Input: Add a property to the thing ``` - property = Property("variable_name", constants.PROPERTY_TYPE_INT, 0) + property = Property("variable_name", PropertyType.INT, 0) thing.add_property(property); ``` - Add an action to the thing + (2) Output: Add an action to the thing ``` thing.add_action("sameple_action") - def custom_function(): + def custom_function(): ## define your action function print("action") thing.sameple_action = custom_function @@ -105,15 +106,15 @@ Description: # gateway-python-sdk ### Gateway #### Constructor ``` - Gateway(name, deviot_server, connector_server, kind="device", account="") + Gateway(name, deviot_server="deviot.cisco.com", connector_server="deviot.cisco.com:18883", kind="device", account="") ``` The Gateway() constructor takes the following arguments: **name** The unique name of a gateway **deviot_server** - The address for the DevIoT server. It does not need to add the protocol. The default protocol is HTTPS(secure connection). The public DevIoT server address is 'deviot.cisco.com' + The address for the DevIoT server. The default value is 'deviot.cisco.com'. It does not need to add the protocol. The default protocol is HTTPS(secure connection). **connector_server** - The address for the MQTT server. It does not need to add the protcol. The default protocol is TCP(unsecure connection) and the default port number is 1883. The public MQTT server address is 'deviot.cisco.com:18883' + The address for the MQTT server. The default value is 'deviot.cisco.com:18883'. It does not need to add the protcol. The default protocol is TCP(unsecure connection) and the default port number is 1883. **kind** The kind of a gateway **account** @@ -122,15 +123,25 @@ Description: # gateway-python-sdk ``` register(*things) ``` - The register() function adds things to the gateway. The thing should not have been already registered. + The register() function adds things to the gateway. The thing should not have been already registered. **thing** A *Thing* instance to register + #### load() + ``` + load(filename, class_directory=None) + ``` + The load() function registers things from an JSON file named [filename] and the custom Thing-sub classes inside [class_directory]. + **filename** + The JSON file having information of things. filename should include its extension. The way to write this JSON file is described in **gateway-python-starter-kit**. + **class_directory** + The directory which has custom Thing-sub class. If the Thing-sub class is defined in the same directory, class_directory should be omitted or be None. + #### deregister() ``` deregister(*things) ``` The deregister() function deletes things from the gateway. - **thing** + **\*things** A *Thing* instance to deregister #### update_thing() ``` @@ -183,13 +194,13 @@ Description: # gateway-python-sdk ### Property #### Constructor ``` - Property(name, type=0, value=None, range=None, unit=None, description=None) + Property(name, type=PropertyType.INT, value=None, range=None, unit=None, description=None) ``` The Property() constructor takes the following arguments: **name** The name of a property. It should be unique in a thing. **type** - The variable type of a property. There are 4 types: int, bool, string, color. You can use constants.PROPERTY_TYPE_INT, constants.PROPERTY_TYPE_BOOL, constants.PROPERTY_TYPE_STRING, constants.PROPERTY_TYPE_COLOR after importing constants. + The variable type of a property. There are 4 types: int, bool, string, color. You can use PropertyType.INT, PropertyType.BOOL, PropertyType.STRING, PropertyType.COLOR after importing 'PropertyType' class from 'cisco_deviot.thing'. **value** The value of a property. **range** @@ -199,6 +210,25 @@ Description: # gateway-python-sdk **description** The description for a property. It is shown at the page of each thing. + ### Action + #### Constructor + ``` + Action(name, **kwargs) + ``` + The Action() constructor takes the following arguments: + **name** + The name of Action. The method with the same name should be defined in a class or an instance. So the name must not overlap with other methods of Thing class like *add_property* and *add_action, and the name cannot contain space ' '. + **\*\*kwargs** + The keyword arguments for properties in an action. Key is the name of a property and the value of the property. It is not mandatory. + + #### add_parameter() + ``` + add_parameter(action_parameter) + ``` + The *add_parameter* adds properties to an Action instance. + **action_parameter** + A Property instance to be added + Keywords: cisco devnet deviot gateway Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha diff --git a/cisco_deviot.egg-info/SOURCES.txt b/cisco_deviot.egg-info/SOURCES.txt index 4bd9f37..567f095 100644 --- a/cisco_deviot.egg-info/SOURCES.txt +++ b/cisco_deviot.egg-info/SOURCES.txt @@ -6,7 +6,6 @@ requirements.txt setup.cfg setup.py cisco_deviot/__init__.py -cisco_deviot/constants.py cisco_deviot/gateway.py cisco_deviot/mqtt_connector.py cisco_deviot/thing.py