Skip to content

Commit

Permalink
Update to 0.0.2
Browse files Browse the repository at this point in the history
  • Loading branch information
ualex73 committed Jul 5, 2019
1 parent 049ee89 commit 0b9d2ab
Show file tree
Hide file tree
Showing 4 changed files with 276 additions and 1 deletion.
55 changes: 54 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,54 @@
# goslide-api

# GoSlide Open Cloud API

Python API to utilise the GoSlide Open Cloud JSON API

## Requirements

- Python >= 3.5.2

## Usage
```python

import asyncio
from goslideapi import GoSlideCloud

loop = asyncio.get_event_loop()
goslide = GoSlideCloud('email', 'password')

login = loop.run_until_complete(goslide.login())
if login:

# Get the slide list
slides = loop.run_until_complete(goslide.slidesoverview())
if slides:
for slidedev in slides:
print(slidedev['device_id'], slidedev['device_name'])
print(' ', slidedev['device_info']['pos'])
else:
print('Something went wrong while retrieving the slide information')

# Open slide with id 1
result = loop.run_until_complete(goslide.slideopen(1))
if result:
print('Succesfully opened slide 1')
else:
print('Failed opened slide 1')

# Close slide with id 1
result = loop.run_until_complete(goslide.slideclose(1))

loop.run_until_complete(goslide.logout())
else:
print('login failed')
```

## TODO:

- Test with a real slide (awaiting delivery ;-))
- Expose more API functions

## License

Apache License 2.0

2 changes: 2 additions & 0 deletions goslideapi/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .goslideapi import *
name = 'goslideapi'
193 changes: 193 additions & 0 deletions goslideapi/goslideapi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@

import json
import logging
import asyncio
import aiohttp

_LOGGER = logging.getLogger(__name__)

BASEURL = 'https://api.goslide.io/api/{}'

class GoSlideCloud:
"""API Wrapper for the Go Slide devices"""

def __init__(self, username, password):
"""Create the object with required parameters."""
self._username = username
self._password = password
self._authenticated = False
self._accesstoken = ''
self._expiretoken = None
self._authfailed = 0

async def _dorequest(self, type, urlsuffix, data=None):
"""Internal request handler."""
headers = {'Content-Type': 'application/json'}

if self._authenticated:
headers['Authorization'] = 'Bearer {}'.format(self._accesstoken)

_LOGGER.debug('%s API: %s, data=%s', type, BASEURL.format(urlsuffix), json.dumps(data))

async with aiohttp.request(type, BASEURL.format(urlsuffix), headers=headers, json=data) as resp:
if resp.status == 200:
textdata = await resp.text()
_LOGGER.debug('REQUEST=%s, HTTPCode=200, Data=%s', BASEURL.format(urlsuffix), textdata)
try:
jsondata = json.loads(textdata)
except:
_LOGGER.error('Invalid JSON response "%s"', textdata)
jsondata = None
pass

return jsondata
else:
textdata = await resp.text()
_LOGGER.error('REQUEST=%s, HTTPCode=%s, Data=%s', BASEURL.format(urlsuffix), resp.status, textdata)

if resp.status == 401:
self._authfailed += 1

return None


async def _request(self, *args):
"""Wrapper around dorequest to do at least 1 retry if we got a HTTP=401."""
resp = await self._dorequest(*args)

if self._authfailed > 0:
_LOGGER.warning('Retrying request')
resp = await self._dorequest(*args)
if self._authfailed > 0:
_LOGGER.error('Request failed')

return resp


async def _checkauth(self):
"""Check if we are authenticated and if we should refresh our token if it less valid then 7 days."""
if self._authenticated:
import datetime

if self._expiretoken != None:
if (self._expiretoken - datetime.datetime.now(datetime.timezone.utc)).days <= 7:
result = await self.login()
return result
else:
return True
else:
return await self.login()


async def login(self):
"""Login to the Cloud API and retrieve a token."""
import datetime

self._authenticated = False
self._accesstoken = ''

result = await self._request('POST', 'auth/login', {'email': self._username, 'password': self._password})
if result:
if 'access_token' in result:
self._authfailed = 0
self._authenticated = True
self._accesstoken = result['access_token']
if 'expires_at' in result:
self._expiretoken = datetime.datetime.strptime(result['expires_at'] + ' +0000', '%Y-%m-%d %H:%M:%S %z')
_LOGGER.debug('Auth login token expiry: %s', result['expires_at'])
else:
self._expiretoken = None
_LOGGER.error('Auth login JSON is missing the "expires_at" field in %s', result)

return self._authenticated


async def logout(self):
"""Logout of the Cloud API."""
if self._authenticated:
result = await self._request('POST', 'auth/logout')
return True
else:
return False


async def slidesoverview(self):
"""Retrieve the slides overview list. The format is:
[{"device_name": "", "device_id": 1, "id": 1, "slide_setup": "", "curtain_type": "", "device_info": {"pos": 0.0}, "zone_id": "", "touch_go": ""}, {...}]
"""
if self._checkauth:
result = await self._request('GET', 'slides/overview')
if result and 'slides' in result:
return result['slides']
else:
_LOGGER.error('Missing key "slides" in JSON response "%s"', json.dumps(result))
return None
else:
return None


async def slidegetposition(self, slideid):
"""Retrieve the slide position. The format is:
{"device_info": {"pos": 0.0}, "touch_go": ""}
"""
if self._checkauth:
result = await self._request('GET', 'slide/{}/info'.format(slideid))
if result and 'device_info' in result and 'pos' in result['device_info']:
return result['device_info']['pos']
else:
#_LOGGER.error('Missing key "device_info" and "pos" in JSON response "%s"', json.dumps(result))
return None
else:
return None


async def slidesetposition(self, slideid, posin):
"""Set the slide position, only 0.0 - 1.0 is allowed."""
try:
pos = float(posin)
except ValueError:
_LOGGER.error('SlideSetPosition called, but "%s" is not numeric', posin)
return None

if pos < 0 or pos > 1:
_LOGGER.error('SlideSetPosition called, but "%s" is not between 0.0 - 1.0', pos)
return None

if self._checkauth:
result = await self._request('POST', 'slide/{}/position'.format(slideid), {'pos': pos})
else:
return None


async def slideopen(self, slideid):
"""Open a slide."""
if self._checkauth:
result = await self._request('POST', 'slide/{}/position'.format(slideid), {'pos': 0.0})
else:
return None


async def slideclose(self, slideid):
"""Close a slide."""
if self._checkauth:
result = await self._request('POST', 'slide/{}/position'.format(slideid), {'pos': 1.0})
else:
return None


async def slidestop(self, slideid):
"""Stop a slide."""
if self._checkauth:
result = await self._request('POST', 'slide/{}/stop'.format(slideid))
else:
return None


async def slidecalibrate(self, slideid):
"""Calibrate a slide."""
if self._checkauth:
result = await self._request('POST', 'slide/{}/calibrate'.format(slideid))
else:
return None


27 changes: 27 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'''
Install goslide.io Open Cloud API
'''

import setuptools

with open('README.md') as f:
LONG_DESCRIPTION = f.read()

setuptools.setup(
name='goslide-api',
version='0.0.2',
url='https://github.com/ualex73/goslide-api',
license='Apache License 2.0',
author='Alexander Kuiper',
author_email='[email protected]',
description='Python API to utilise the goslide.io Open Cloud API',
long_description=LONG_DESCRIPTION,
long_description_content_type='text/markdown',
packages=setuptools.find_packages(),
install_requires=['aiohttp', 'asyncio'],
classifiers=[
'Programming Language :: Python :: 3',
'License :: OSI Approved :: Apache Software License',
'Operating System :: OS Independent',
],
)

0 comments on commit 0b9d2ab

Please sign in to comment.