Welcome to the TellStick developer documentation!¶
TellStick ZNet allows developers to build own plugins and scripts run the device to extend the functionality with features not yet supported by Telldus.
It is also possible to alter the behaviour on how TellStick ZNet should interpret signals and messages from devices.
Intro¶
TellStick ZNet offers two ways of integrating custom scripts. They can be written in either Python or Lua. The difference is outlined below.
Python¶
Python plugins are only available for TellStick ZNet Pro. Python plugins cannot be run on TellStick ZNet Lite. Python plugins offers the most flexible solution since full access to the service is exposed. This also makes it fragile since Python plugins can affect the service negative.
Lua¶
Lua code is available on both TellStick ZNet Pro and TellStick ZNet Lite. Lua code runs in a sandbox and has only limited access to the system.
To create a Lua script you need to access the local web server in TellStick ZNet. Browse to: http://[ipaddress]/lua to access the editor.
Lua codes works by signals from the server triggers the execution.
Installation¶
This is the software running on the late generation TellStick ZNet Lite and TellStick Net
Prerequisites¶
This software is only supported under Linux and macOS.
Although most of the prerequisites can be installed sandboxed, locally in the project folder, there are some libraries that must exist on the system.
- The following applications must be available:
- python (2.7)
- virtualenv
- Node.js
Clone the Server code from here: https://github.com/telldus/tellstick-server
Linux¶
In many Linux distributions the packages for python and virtualenv already exist. On a Debian/Ubuntu based system these can be installed using this command:
sudo apt-get install python virtualenv
If virtualenv is not available on your system, you can install it with either pip or easy install:
sudo pip install virtualenv
or
sudo easy_install virtualenv
macOS¶
Pyhon is already shipped on macOS. You only need to install virtualenv using:
sudo easy_install virtualenv
Setup¶
To setup the source and prepare the base plugins run the following script:
./tellstick.sh setup
This vill create a virtualenv folder under the build folder and download and install the required libraries for running the software. The installation is completely sandboxed and nothing is installed in the system. You can wipe the build folder at any time to remove any installed packages.
Running¶
Start the server by issuing:
./tellstick.sh run
By default the server will restart itself any time it detects a file has been modified.
Check out and follow the instructions on getting the server software running on a computer here: https://github.com/telldus/tellstick-server
After installation the tellstick server is installed without any plugins. For development the lua-plugin is a recommended plugin to install. Install it with:
./tellstick.sh install lua
Python plugins¶
Python plugins offers the most flexible way of extending the functionality of TellStick. To get started a development environment should first be setup on a computer running Linux or macOS. Windows is not supported at the moment.
Telldus own plugins are open source and can be used as a base for new plugins. These can be found here: https://github.com/telldus/tellstick-server-plugins
This guide will describe the example plugin found here: https://github.com/telldus/tellstick-server-plugins/tree/master/templates/device
The plugin adds one dummy device to the system.
During the development it is recommended to install it within the server software. This way the software will
restart itself whenever a file has changed. To install it use the tellstick command install
:
./tellstick.sh install [path-to-plugin]
Replace [path-to-plugin] with the path to the plugin root folder.
Anatomy of a plugin¶
TellStick plugins are packaged as python eggs combined in a zip file. The eggs are signed with a pgp signature.
The metadata for a plugin is described in the file setup.py. This is a standard setuptools file with a couple custom configurations added.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
try:
from setuptools import setup
except ImportError:
from distutils.core import setup
setup(
name='Dummy device',
version='1.0',
author='Alice',
author_email='alice@wonderland.lit',
category='appliances',
color='#2c3e50',
description='Dummy template for implementings device plugins',
icon='dummy.png',
long_description="""
This plugin is used as a template when creating plugins that support new device types.
""",
packages=['dummy'],
package_dir = {'':'src'},
entry_points={ \
'telldus.startup': ['c = dummy:Dummy [cREQ]']
},
extras_require = dict(cREQ = 'Base>=0.1\nTelldus>=0.1'),
)
Most of the fields can be found in the setuptools documentation.
author: | The name of the developer of the plugin. This name must match the pgp signin certificate. |
||||||||
---|---|---|---|---|---|---|---|---|---|
author_email: | The email of the developer of the plugin. This must match the pgp singning certificate. |
||||||||
category: | This must be one of:
|
||||||||
color: | A color used in plugin selection user interface in the format #000000. |
||||||||
compatible_platforms: | |||||||||
Reserved for future use. |
|||||||||
description: | A short description of the plugins. This should only be one line. |
||||||||
entry_points: | TellStick plugins can be loaded by one of two entry points.
|
||||||||
icon: | Filename of icon in size 96x96. |
||||||||
long_description: | |||||||||
A long description describing the plugin. Markdown can be used. |
|||||||||
name: | The name of the plugin. |
||||||||
packages: | A list of python packages included in the plugin. This should match the folder structure of the files. Please see setuptools documentation for more information. |
||||||||
required_features: | |||||||||
Reserved for future use. |
|||||||||
version: | The version of the plugin. |
Building a deployable plugin¶
Once development is finished it’s time to package the code into a deployable package. Before this command a working
pgp code signing key must be setup on the computer. The name and email must match the metadata author
and author_email
specified in setup.py.
Setting up a key¶
You can safely skip this step if you already have a pgp-key setup on your computer.
gpg --gen-key
This will take you through a few questions that will configure your keys.
Please select what kind of key you want: (1) RSA and RSA (default)
What keysize do you want? 4096
Key is valid for? 0
Is this correct? y
Real name: Enter the same name as in setup.py
Email address: Enter the same email as in setup.py
Comment:
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
Enter passphrase: Enter a secure passphrase here (upper & lower case, digits, symbols)
Build the plugin¶
To build the package use the build-plugin
command to tellstick.sh
./tellstick.sh build-plugin [path-to-plugin]
Replace [path-to-plugin] with the path to the plugin root folder. During building the plugin will be signed using your pgp key and if a passphrase has been setup you will be asked for your password.
This will build a .zip file ready to be uploaded to a TellStick.
Concepts¶
Read more about the concepts used for building plugins
Methods, actions and sensor values¶
The core object in tellstick-server is the Device
object.
Device vs sensor?¶
In Telldus Live! devices and sensors are separated and two separate things. In tellstick-server
these are one object and all inherits telldus.Device
.
There is a convenience class telldus.Sensor
that sets some initial parameters for devices
that only supports sensor-values.
Methods¶
TellStick can control many different types of devices that support different features. For example, a bell does not support the on-signal and not all lamp switches support dimming.
To determine which methods a device supports, call the function
methods()
on the device.
See the following example to determine if a device supports on and off:
methods = device.methods()
if methods & Device.TURNON:
logging.debug('The device supports turning on')
if methods & Device.TURNON:
logging.debug('The device supports turning off')
if methods & Device.DIM:
logging.debug('The device is dimmable')
ON = 1
OFF = 2
DIM = 16
local methods = device:methods()
if (BitAND(methods, ON) == ON) then
print("The device supports turning on")
end
if (BitAND(methods, OFF) == OFF) then
print("The device supports turning off")
end
if (BitAND(methods, DIM) == DIM) then
print("The device is dimmable")
end
-- The lua version shipped with TellStick does not support bitwise operators
function BitAND(a,b)--Bitwise and
local p,c=1,0
while a>0 and b>0 do
local ra,rb=a%2,b%2
if ra+rb>1 then c=c+p end
a,b,p=(a-ra)/2,(b-rb)/2,p*2
end
return c
end
Observer pattern¶
Inter plugin communication is based on an observer pattern. Any plugin that wishes to publish events or pull information from other plugins may register an interface to observe.
Both observers and publishers must inherit from the Plugin
base class.
Publish the observer interface¶
An observer interface is a class that inherits IInterface
and contains only static functions.
Example:
class IExampleObserver(IInterface):
"""Example observer interface exporting two functions"""
def foo():
"""Example function no 1"""
def bar():
"""Example function no 2"""
The class that uses this creates and observer collection.
class ExamplePlugin(Plugin):
observers = ObserverCollection(IExampleObserver)
Multiple plugins may observe the interface but only one plugin may create the observer collection. Calling the observers can be made all by one och one by one by iterating the collection. The calling the whole collection at once the returned value by each observer will be discarded. It the return value is needed you must iterate and call each observer individually.
def callAllTheFoes(self):
self.observers.foo() # Note, the returned value cannot be used
def enterTheBars(self):
for observer in self.observers:
retVal = observer.bar()
if retVal is True:
logging.info("This bar is awesome")
class IExampleObserver(IInterface):
"""Example observer interface exporting two functions"""
def foo():
"""Example function no 1"""
def bar():
"""Example function no 2"""
class ExamplePlugin(Plugin):
observers = ObserverCollection(IExampleObserver)
Implementing the observer¶
To observe an interface the observing plugin must mask itself that it observes the interface by
using the implements()
function.
class ExampleObserverPlugin(Plugin):
implements(IExampleObserver)
It can then implement the interface functions. Note that it’s not necessary to implement all the functions from the interface.
class ExamplePlugin(Plugin):
implements(IExampleObserver)
def bar(self):
return self.isThisBarAwseome
Signals and slots¶
Signals and slots is another way of inter plugin communication. It is built on the observers framework but offers a more loosley coupled integration. It’s benefit against observers is:
- The observers (slots) does not need to import the interface from the publishers (signal). This allows the plugin to be loaded even when the publishing plugin is not available.
- The slots can be named freely so name collision is not as likley to occur.
The drawbacks instead are:
- The publishers does not know anything about the observers so it’s not possible the return a value from the observers. If a return value is required then use observers instead.
Signals¶
To trigger an event or send some data to an observer a Signal is used. A signal is created by using
the decorator @signal
.
Example:
from base import Plugin, signal
class MyPlugin(Plugin):
@signal('mysignal')
def foobar(self):
# Every time this function is called the signal "mysignal" is fired
logging.warning("mysignal is fired")
@signal
def hello(self):
# This signal takes the function name as the signal name
logging.warning("signal hello is fired")
Slots¶
To receive a signal a plugin declares a slot. The plugin must implement
base.ISignalObserver
to be able to receive any signals. Then use the decorator
@slot
on the function you wish to be called when the signal is fired.
Example:
from base import implements, ISignalObserver, Plugin, slot
class AnotherPlugin(Plugin):
implements(ISignalObserver)
@slot('mysignal')
def foobar(self):
logging.warning('Slot foobar was triggered by the signal "mysignal"')
Examples¶
Read more about the concepts used for building plugins
Configurations for plugins¶
If your plugin needs user set configuration values it can add this by using configurations.
Wrap your pluginclass with the @configuration
decorator.
from base import configuration, ConfigurationString, Plugin, ConfigurationList
@configuration(
companyName = ConfigurationString(
defaultValue='Telldus Technologies',
title='Company Name',
description='Name of the Company'
),
contacts = ConfigurationList(
defaultValue=['9879879879', '8529513579', '2619859867'],
title='company contacts',
description='Contacts of the company',
minLength=2
),
username = ConfigurationString(
defaultValue='admin@gmail.com',
title='Username',
description='Username of the company Administrator'
),
password = ConfigurationString(
defaultValue='admin1234',
title='Password',
description='Password of the company Administrator',
minLength=8,
maxLength=12
)
)
class Config(Plugin):
def companyInfo(self):
# access the companyName information from the configuration
return {
'companyName' : self.config('companyName'),
'contacts' : self.config('contacts'),
'username' : self.config('username'),
'password' : self.config('password'),
}
Here, the configuration store company information and return it when it called.
The configuration has following classes:
base.ConfigurationString
: this function use to store configuration value as a string.
base.ConfigurationNumber
: this function use to store configuration value as a number.
base.ConfigurationList
: this function use to store configuration value as a list.
base.ConfigurationDict
: this function use to store configuration value as a dictionary.
Call configuration to get company information using lua script :
local companyObject = require "configuration.Config"
local companyData = companyObject:companyInfo()
Sensor plugin development¶
This example shows how to extend the server with a new type of sensor.
Prepare the Sensor¶
In order to create the sensor, you have to import Sensor
class form the telldus
package
and extend it in your TemperatureSensor class
from base import Plugin, Application
from telldus import DeviceManager, Sensor
class TemperatureSensor(Sensor):
def __init__(self):
super(TemperatureSensor,self).__init__()
...
...
...
Export functions¶
All sensors exported must subclass Sensor
Minimal function to reimplement is :
localId
and
typeString
def localId(self):
'''Return a unique id number for this sensor. The id should not
be globally unique but only unique for this sensor type.
'''
return 1
def typeString(self):
'''Return the sensor type. Only one plugin at a time may export sensors using
the same typestring'''
return 'temperaturesensor'
Add Sensor¶
To add a sensor into plugin Temperature
:
class Temperature(Plugin):
'''This is the plugins main entry point and is a singleton
Manage and load the plugins here
'''
def __init__(self):
# The devicemanager is a globally manager handling all device types
self.deviceManager = DeviceManager(self.context)
# Load all devices this plugin handles here. Individual settings for the devices
# are handled by the devicemanager
self.deviceManager.addDevice(TemperatureSensor())
# When all devices has been loaded we need to call finishedLoading() to tell
# the manager we are finished. This clears old devices and caches
self.deviceManager.finishedLoading('temperaturesensor')
A complete example for Temperature sensor¶
# -*- coding: utf-8 -*-
from base import Application, Plugin
from telldus import DeviceManager, Sensor
class TemperatureSensor(Sensor):
'''All sensors exported must subclass Sensor
Minimal function to reimplement is:
localId
typeString
'''
@staticmethod
def localId():
'''Return a unique id number for this sensor. The id should not be
globally unique but only unique for this sensor type.
'''
return 2
@staticmethod
def typeString():
'''Return the sensor type. Only one plugin at a time may export sensors using
the same typestring'''
return 'temperature'
def updateValue(self):
"""setTempratureSensor value constantly."""
# This is dummy data
self.setSensorValue(Sensor.TEMPERATURE, 35, Sensor.SCALE_TEMPERATURE_CELCIUS)
class Temperature(Plugin):
'''This is the plugins main entry point and is a singleton
Manage and load the plugins here
'''
def __init__(self):
# The devicemanager is a globally manager handling all device types
self.deviceManager = DeviceManager(self.context)
# Load all devices this plugin handles here. Individual settings for the devices
# are handled by the devicemanager
self.sensor = TemperatureSensor()
self.deviceManager.addDevice(self.sensor)
# When all devices has been loaded we need to call finishedLoading() to tell
# the manager we are finished. This clears old devices and caches
self.deviceManager.finishedLoading('temperature')
Application().registerScheduledTask(self.updateValues, minutes=1, runAtOnce=True)
def updateValues(self):
self.sensor.updateValue()
Web interface¶
This example shows how a plugin can extend the local webinterface with its own page in the menu.
Base files¶
Before making any plugin we have to create a setup file for the plugin. For more about setup.py.
Add following code in setup.py:
try:
from setuptools import setup
from setuptools.command.install import install
except ImportError:
from distutils.core import setup
from distutils.command.install import install
import os
class buildweb(install):
def run(self):
print("generate web application")
os.system('npm install')
os.system('npm run build')
install.run(self)
setup(
name='Welcome',
version='0.1',
packages=['welcome'],
package_dir = {'':'src'},
cmdclass={'install': buildweb}, #Call the fuction buildweb
entry_points={ \
'telldus.plugins': ['c = welcome:Welcome [cREQ]']
},
extras_require = dict(cREQ = 'Base>=0.1\nTelldus>=0.1\nTelldusWeb>=0.1')
)
Now create plugin file :
For Interacting with UI import interface IWebReactHandler
from telldus.web
and implements it into the plugin.
from base import Plugin, implements
from telldus.web import IWebReactHandler
class Welcome(Plugin):
implements(IWebReactHandler)
@staticmethod
def getReactComponents():
return {
'welcome': {
'title': 'Welcome',
'script': 'welcome/welcome.js',
'tags': ['menu'],
}
}
Here, the function getReactComponents()
Return a list of components this plugin exports.
HTML interface¶
Run npm init
command in the root folder, It will ask for the package details and fill it out.
It will create a package.json file in your root folder.
Make following modifications in the package.json file.
{
"name": "welcome",
"version": "1.0.0",
"scripts": {
"build": "gulp",
"watch": "gulp watch"
},
"devDependencies": {
"babel-cli": "^6.18.0",
"babel-preset-es2015": "^6.16.0",
"babel-preset-react": "^6.16.0",
"babel-preset-stage-0": "^6.16.0",
"gulp": "^3.9.1",
"gulp-babel": "^6.1.2",
"gulp-cli": "^1.2.2",
"gulp-requirejs-optimize": "^1.2.0",
"requirejs": "^2.3.2"
}
}
That are major dependencies to run and display UI.
Now create gulpfile.js in the root folder.
Gulp is a toolkit for automating painful or time-consuming tasks in your development workflow, so you can stop messing around and build something. more
Add following task in gulpfile:
var gulp = require('gulp');
var babel = require("gulp-babel");
var requirejsOptimize = require('gulp-requirejs-optimize');
gulp.task('default', ['scripts'], function() {
});
gulp.task('jsx', function () {
return gulp.src('src/welcome/app/**/*.jsx')
.pipe(babel({
presets: ['es2015', 'stage-0', 'react']
}))
.pipe(gulp.dest('src/welcome/build'));
});
gulp.task('js', function () {
return gulp.src('src/welcome/app/**/*.js')
.pipe(gulp.dest('src/welcome/build'));
});
gulp.task('scripts', ['jsx', 'js'], function () {
return gulp.src('src/welcome/build/welcome/welcome.js')
.pipe(requirejsOptimize({
paths: {
'react': 'empty:',
'react-mdl': 'empty:',
'react-router': 'empty:'
},
baseUrl: 'src/welcome/build',
name: 'welcome/welcome'
}))
.pipe(gulp.dest('src/welcome/htdocs'));
});
gulp.task('watch', ['default'], function() {
gulp.watch('src/welcome/app/**/*.jsx', ['default']);
});
Here,
gulp task jsx
will copy all file from the specified path and convert it and paste it into the destination path.
gulp task js
and script
will do same as jsx
.
Create UI design¶
Now Design UI using react and give extension .jsx
and save this file to the path that you have given in gulpfile.
define(
['react', 'react-mdl', 'react-router'],
function(React, ReactMDL, ReactRouter) {
class WelcomeApp extends React.Component {
render() {
return (
<div>
<h1>hello world!</h1>
</div>
);
}
};
return WelcomeApp;
}
);
Now the plugin is ready to install and use.
API Documentation¶
This is where you can find API-level documentation for the various Python libraries available in TellStick firmware.
Module: base¶
Classes in the base module are only accessible from Python applications.
Application¶
-
class
base.
Application
(run=True)¶ This is the main application object in the server. There can only be once instance of this object. The default constructor returns the instance of this object.
-
registerScheduledTask
(fn, seconds=0, minutes=0, hours=0, days=0, runAtOnce=False, strictInterval=False, args=None, kwargs=None)¶ Register a semi regular scheduled task to run at a predefined interval. All calls will be made by the main thread.
Parameters: - fn (func) – The function to be called.
- seconds (integer) – The interval in seconds. Optional.
- minutes (integer) – The interval in minutes. Optional.
- hours (integer) – The interval in hours. Optional.
- days (integer) – The interval in days. Optional.
- runAtOnce (bool) – If the function should be called right away or wait one interval?
- strictInterval (bool) – Set this to True if the interval should be strict. That means if the interval is set to 60 seconds and it was run ater 65 seconds the next run will be in 55 seconds.
- args (list) – Any args to be supplied to the function. Supplied as *args.
- kwargs (dict) – Any keyworded args to be supplied to the function. Supplied as **kwargs.
Note
The interval in which this task is run is not exact and can be delayed one minute depending on the server load.
Note
Calls to this method are threadsafe.
-
static
defaultContext
()¶ Returns: the default context used by the application
-
queue
(fn, *args, **kwargs)¶ Queue a function to be executed later. All tasks in this queue will be run by the main thread. This is a thread safe function and can safely be used to syncronize with the main thread
Returns: True if the task was queued Returns: False if the server is shutting down
-
registerShutdown
(fn)¶ Register shutdown method. The method fn will be called the the server shuts down. Use this to clean up resources on shutdown.
Parameters: fn (func) – A function callback to call when the server shuts down
-
static
signal
(msg, *args, **kwargs)¶ Send a global signal to registered slots. It is not recommended to call this method directly but instead use the signal decorator. Any extra parameters supplied will be forwarded to the slot.
Parameters: msg (str) – The signal name
-
-
@
base.
mainthread
¶ This decorator forces a method to be run in the main thread regardless of which thread calls the method.
Configurations¶
-
@
base.
configuration
¶ This decorator should be applied on the Plugin class to add configuration values. Configuration values will be exposed automatically to the user.
Example:
@configuration( companyName = ConfigurationString( defaultValue='Telldus Technologies', title='Company Name', description='Name of the Company' ), ) class MyPlugin(Plugin): pass
-
class
base.
ConfigurationValue
(valueType, defaultValue, writable=True, readable=True, hidden=False)¶ Base class for configuration values. Do not use this class directly but use one of the subclasses instead.
-
class
base.
ConfigurationDict
(defaultValue={})¶ Configuration class used to store dictionaries
-
class
base.
ConfigurationNumber
(defaultValue=0)¶ Configuration class used to store numbers
-
class
base.
ConfigurationList
(defaultValue=[])¶ Configuration class used to store lists
-
class
base.
ConfigurationString
(defaultValue='', minLength=0, maxLength=0)¶ Configuration class used to store strings
Signals & Slots¶
-
class
base.
ISignalObserver
¶ Bases:
base.Plugin.IInterface
Implement this IInterface to recieve signals using the decorator
@slot
-
@
base.
signal
¶ This is a decorator for sending signals. Decorate any of your Plugins methods with this decorator and whenever the method is called the signal will be fired.
Parameters: name (str) – The signal name. This can be omitted and then the function name will be used as the name of the signal.
-
@
base.
slot
(message = '')¶ This is a decorator for receiveing signals. The class must implement
base.ISignalObserver
Parameters: message (str) – This is the signal name to receive
Module: scheduler¶
Module: telldus¶
Intro¶
TellStick ZNet has a local REST interface to integrate into third party applications not running on the TellStick ZNet itself
A list of all available functions can be browsed on the device itself. Browse to: http://[ipaddress]/api to list the functions.
Authentication¶
Before making any REST calls to TellStick ZNet the application must request a token that the user has authenticated.
Step 1 - Request a request token¶
Request a request token by performing a PUT call to the endpoint /api/token. You need to supply the application name as a parameter “app”
$ curl -i -d app="Example app" -X PUT http://0.0.0.0/api/token
HTTP/1.1 200 OK
Date: Fri, 15 Jan 2016 13:33:54 GMT
Content-Length: 148
Content-Type: text/html;charset=utf-8
Server: CherryPy/3.8.0
{
"authUrl": "http://0.0.0.0/api/authorize?token=0996b21ee3f74d2b99568d8207a8add9",
"token": "0996b21ee3f74d2b99568d8207a8add9"
}
Step 2 - Authenticate the app¶
Redirect the user to the url returned in step 1 to let him/her authenticate the app.
Step 3 - Exchange the request token for an access token¶
When the user has authenticated the request token in step 2 the application needs to exchange this for an access token. The access token can be used in the API requests. To exchange the request token for an access token perform a GET call to the same endpoint in step 1. Supply the request token as the parameter “token”.
$ curl -i -X GET http://0.0.0.0/api/token?token=0996b21ee3f74d2b99568d8207a8add9
HTTP/1.1 200 OK
Date: Fri, 15 Jan 2016 13:39:22 GMT
Content-Length: 230
Content-Type: text/html;charset=utf-8
Server: CherryPy/3.8.0
{
"allowRenew": true,
"expires": 1452951562,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImF1ZCI6IkV4YW1wbGUgYXBwIiwiZXhwIjoxNDUyOTUxNTYyfQ.eyJyZW5ldyI6dHJ1ZSwidHRsIjo4NjQwMH0.HeqoFM6-K5IuQa08Zr9HM9V2TKGRI9VxXlgdsutP7sg"
}
If the returned data contains allowRenew=true then the token was authorized to renew its expiration itself without letting the user authorize the app again. The application must renew the token before it expires or else the application must start the autorization again from step 1.
If allowRenew is not set to true it is not possible for the app to renew the token and it will always expire after the time set in the parameter “expires”.
Step 4 - Making a request¶
To make a requst to a TellStick ZNet API endpoint the token in step 3 must be supplied as a bearer token in the header. This is an example requesting a list of devices:
$ curl -i -X GET http://0.0.0.0/api/devices/list?supportedMethods=3 -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImF1ZCI6IkV4YW1wbGUgYXBwIiwiZXhwIjoxNDUyOTUxNTYyfQ.eyJyZW5ldyI6dHJ1ZSwidHRsIjo4NjQwMH0.HeqoFM6-K5IuQa08Zr9HM9V2TKGRI9VxXlgdsutP7sg"
HTTP/1.1 200 OK
Date: Tue, 19 Jan 2016 10:21:29 GMT
Content-Type: Content-Type: application/json; charset=utf-8
Server: CherryPy/3.7.0
{
"device": [
{
"id": 1,
"methods": 3,
"name": "Example device",
"state": 2,
"statevalue": "",
"type": "device"
}
]
}
Refreshing a token¶
If the user allowed the application to renew the token in steg 2 it can be renewed by the calling application. The token must be refreshed before it expires. If the token has expired the authentication must be restarted from step 1 again.
$ curl -i -X GET http://0.0.0.0/api/refreshToken -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImF1ZCI6IkV4YW1wbGUgYXBwIiwiZXhwIjoxNDUyOTUxNTYyfQ.eyJyZW5ldyI6dHJ1ZSwidHRsIjo4NjQwMH0.HeqoFM6-K5IuQa08Zr9HM9V2TKGRI9VxXlgdsutP7sg"
HTTP/1.1 200 OK
Date: Tue, 19 Jan 2016 10:21:29 GMT
Content-Type: Content-Type: application/json; charset=utf-8
Server: CherryPy/3.7.0
{
"expires": 1455295348,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImF1ZCI6IkV4YW1wbGUgYXBwIiwiZXhwIjoxNDU1Mjk1MzQ4fQ.eyJyZW5ldyI6dHJ1ZSwidHRsIjo4NjQwMH0.M4il4_2SqJwlCjmuXlU5DS6h-gX7493Tnk9oBJXbgPw"
}
The new token returned must be used from now on and the old be discarded.
Extending¶
It’s possible to extend the API with new functions from custom plugins.
Prepare the plugin¶
In order for the API plugin to know about this plugin it must implement the
interface IApiCallHandler
from api import IApiCallHandler
from base import Plugin, implements
class HelloWorld(Plugin):
implements(IApiCallHandler)
Export a function¶
Use the decorator @apicall
on the function you want to export. This example
exports the function helloworld/foobar
:
@apicall('helloworld', 'foobar')
def myfunction(self, arg1, arg2):
A complete example¶
from api import IApiCallHandler, apicall
from base import Plugin, implements
class HelloWorld(Plugin):
implements(IApiCallHandler)
@apicall('helloworld', 'foobar')
def myfunction(self, arg1, arg2):
"""
Docs for the function goes here
"""
return True