From 2d70a7a9fa049b1f9ecc4120d0e2a3c51851b095 Mon Sep 17 00:00:00 2001 From: Kit Klein Date: Sun, 29 Dec 2019 11:27:08 -0500 Subject: [PATCH 1/2] convert to async --- README.md | 51 +++++++++++++++++++++++++------------------ konnected/__init__.py | 42 +++++++++++++++++------------------ requirements.txt | 2 +- setup.py | 6 ++--- 4 files changed, 54 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index eac6763..45bb60b 100644 --- a/README.md +++ b/README.md @@ -11,40 +11,49 @@ For more information see [konnected.io](https://konnected.io) ## Usage ``` ->>> import konnected +import aiohttp +import asyncio +import konnected -# Initialize the Konnected client with a host and port. -# Every Konnected module uses a different HTTP port +async def fetch(session, url): + async with session.get(url) as response: + return await response.text() ->>> k = konnected.Client('192.168.86.10', '17000') +async def main(): + async with aiohttp.ClientSession() as session: + k = konnected.Client('192.168.86.10', '17000', session) -# Sync settings to the device + # Sync settings to the device ->>> k.put_settings(sensors=[{"pin":1}], actuators=[], auth_token='secureToken', endpoint='http://example.com') -True + print(await k.put_settings(sensors=[{"pin":1}], actuators=[], auth_token='secureToken', endpoint='http://example.com')) + # True -# Get status of each input pin ->>> k.get_device() -[{'state': 0, 'pin': 1}, {'state': 1, 'pin': 2}] + # Get status of each input pin -# Get status of a single input pin + print(await k.get_device()) + # [{'state': 0, 'pin': 1}, {'state': 1, 'pin': 2}] ->>> k.get_device(2) -[{'state': 1, 'pin': 2}] + # Get status of a single input pin -# Actuate an output pin + print(await k.get_device(2)) + # [{'state': 1, 'pin': 2}] ->>> k.put_device(pin=8, state=1) -{'state': 1, 'pin': 8} + # Actuate an output pin -# Get device status + print(await k.put_device(pin=8, state=1)) + # {'state': 1, 'pin': 8} ->>> k.get_status() -{'mac': '2c:3a:e8:43:8a:38', 'gw': '192.168.86.1', 'hwVersion': '2.0.5', 'rssi': -31, 'nm': '255.255.255.0', 'ip': '192.168.86.10', 'actuators': [{'trigger': 1, 'pin': 8}], 'port': 12426, 'uptime': 2349, 'heap': 23904, 'swVersion': '2.1.3', 'sensors': [{'state': 0, 'pin': 1}]} + # Get device status -''' + await k.get_status() + # {'mac': '2c:3a:e8:43:8a:38', 'gw': '192.168.86.1', 'hwVersion': '2.0.5', 'rssi': -31, 'nm': '255.255.255.0', 'ip': '192.168.86.10', 'actuators': [{'trigger': 1, 'pin': 8}], 'port': 12426, 'uptime': 2349, 'heap': 23904, 'swVersion': '2.1.3', 'sensors': [{'state': 0, 'pin': 1}]} + +if __name__ == '__main__': + loop = asyncio.get_event_loop() + loop.run_until_complete(main()) + ``` ## Python version -This library was designed to work with Python 3. Parts may work with Python 2.7, but if it does it's totally accidental. \ No newline at end of file +This library was designed to work with Python 3.5 and above. \ No newline at end of file diff --git a/konnected/__init__.py b/konnected/__init__.py index 844c5eb..87ce223 100644 --- a/konnected/__init__.py +++ b/konnected/__init__.py @@ -1,41 +1,39 @@ # Python library for interacting with Konnected devices # learn more at konnected.io - -import requests +import asyncio +import aiohttp import json import os -from requests.exceptions import RequestException; - __author__ = 'Nate Clark, Konnected Inc ' - class Client(object): - def __init__(self, host, port): + def __init__(self, host, port, websession): self.host = host self.port = port + self.websession = websession self.base_url = 'http://' + host + ':' + port - def get_device(self, pin=None): + async def get_device(self, pin=None): """ Query the status of a specific pin (or all configured pins if pin is ommitted) """ url = self.base_url + '/device' try: - r = requests.get(url, params={'pin': pin}, timeout=10) - return r.json() - except RequestException as err: + async with self.websession.get(url, params={'pin': pin}, timeout=10) as resp: + return await resp.json() + except aiohttp.ClientError as err: raise Client.ClientError(err) - def get_status(self): + async def get_status(self): """ Query the device status. Returns JSON of the device internal state """ url = self.base_url + '/status' try: - r = requests.get(url, timeout=10) - return r.json() - except RequestException as err: + async with self.websession.get(url, timeout=10) as resp: + return await resp.json() + except aiohttp.ClientError as err: raise Client.ClientError(err) - def put_device(self, pin, state, momentary=None, times=None, pause=None): + async def put_device(self, pin, state, momentary=None, times=None, pause=None): """ Actuate a device pin """ url = self.base_url + '/device' @@ -54,12 +52,12 @@ def put_device(self, pin, state, momentary=None, times=None, pause=None): payload["pause"] = pause try: - r = requests.put(url, json=payload, timeout=10) - return r.json() - except RequestException as err: + async with self.websession.put(url, json=payload, timeout=10) as resp: + return await resp.json() + except aiohttp.ClientError as err: raise Client.ClientError(err) - def put_settings(self, sensors=[], actuators=[], auth_token=None, + async def put_settings(self, sensors=[], actuators=[], auth_token=None, endpoint=None, blink=None, discovery=None, dht_sensors=[], ds18b20_sensors=[]): """ Sync settings to the Konnected device """ @@ -81,9 +79,9 @@ def put_settings(self, sensors=[], actuators=[], auth_token=None, payload['discovery'] = discovery try: - r = requests.put(url, json=payload, timeout=10) - return r.ok - except RequestException as err: + async with self.websession.put(url, json=payload, timeout=10) as resp: + return await resp.json() + except aiohttp.ClientError as err: raise Client.ClientError(err) class ClientError(Exception): diff --git a/requirements.txt b/requirements.txt index f229360..ce23571 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -requests +aiohttp \ No newline at end of file diff --git a/setup.py b/setup.py index 18d3657..af4bb2b 100644 --- a/setup.py +++ b/setup.py @@ -40,15 +40,15 @@ setup( name='konnected', - version='0.1.5', + version='1.0.0', packages=['konnected'], url='https://github.com/konnected-io/konnected-py', license=license, - description='A Python library for interacting with Konnected home automation controllers (see https://konnected.io)', + description='A async Python library for interacting with Konnected home automation controllers (see https://konnected.io)', long_description=long_description, author='Nate Clark, Konnected Inc', author_email='help@konnected.io', - install_requires=['requests>=2.0'], + install_requires=['aiohttp>=3.6.1'], project_urls={ 'Homepage': 'https://github.com/konnected-io/konnected-py', 'Website & Online Store': 'https://konnected.io', From 313ac83195748009f2056b757836e4e74ce2e9d1 Mon Sep 17 00:00:00 2001 From: Kit Klein Date: Sun, 29 Dec 2019 16:16:15 -0500 Subject: [PATCH 2/2] improved exception handling --- konnected/__init__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/konnected/__init__.py b/konnected/__init__.py index 87ce223..ce5df6c 100644 --- a/konnected/__init__.py +++ b/konnected/__init__.py @@ -21,7 +21,7 @@ async def get_device(self, pin=None): try: async with self.websession.get(url, params={'pin': pin}, timeout=10) as resp: return await resp.json() - except aiohttp.ClientError as err: + except (aiohttp.ClientError, asyncio.TimeoutError) as err: raise Client.ClientError(err) async def get_status(self): @@ -30,7 +30,7 @@ async def get_status(self): try: async with self.websession.get(url, timeout=10) as resp: return await resp.json() - except aiohttp.ClientError as err: + except (aiohttp.ClientError, asyncio.TimeoutError) as err: raise Client.ClientError(err) async def put_device(self, pin, state, momentary=None, times=None, pause=None): @@ -54,7 +54,7 @@ async def put_device(self, pin, state, momentary=None, times=None, pause=None): try: async with self.websession.put(url, json=payload, timeout=10) as resp: return await resp.json() - except aiohttp.ClientError as err: + except (aiohttp.ClientError, asyncio.TimeoutError) as err: raise Client.ClientError(err) async def put_settings(self, sensors=[], actuators=[], auth_token=None, @@ -80,8 +80,8 @@ async def put_settings(self, sensors=[], actuators=[], auth_token=None, try: async with self.websession.put(url, json=payload, timeout=10) as resp: - return await resp.json() - except aiohttp.ClientError as err: + return (resp.status >= 200 and resp.status < 300) + except (aiohttp.ClientError, asyncio.TimeoutError) as err: raise Client.ClientError(err) class ClientError(Exception):