Implementing Devices

All devices must implement some requirements to be able to be controlled by the app.

device.json

Each device shall be individually controllable. In order to achieve this the app needs to know which device has which options. These options have to be defined in the file device.json. The app will request and render the options when it is navigated to the send page of a selected device.

Options

All options which the device is able to control have to be inside the “options” array:

"options": []

Each option has to provide all of the following tags:

name

The name which will be displayed for each option in the app. It is freely choosable.

id

A unique identifier for each option. Make sure that there are no duplicates, it must be unique on device scope, otherwise there will be errors when sending instructions to the device. The value of the id has some restrictions, which are the same like the ones of html id attributes. Which means:

  • It must at least contain one character

  • It is case sensitive

  • There can be no white spaces

This id can later also be used to retrieve values from the device configuration inside the device application, for example to check the a configured maximum value.

values

All values which are available for the option must be inside a json object:

"values": {}

Note

The value is strongly dependent of the type of option. See the corresponding description.

Note

Except for File Up-/Download and Info, adding options to device.json needs them to be placed as parameter in device.execute

Optional Option Tags

In addition to the required tags above, each option may define the following optional tags:

refresh

"refresh": true

A boolean which defaults to false. When set to true, the app reloads the device information (and refreshes the dashboard layout, if the device supports one) after a successful send. For options of type file-upload, the reload is triggered after a successful upload instead.

The state of the input form is preserved during the reload where possible, i.e. values already entered by the user are kept for all options which still exist after the reload.

This is useful when executing an instruction changes the options of the device itself, for example when the available options depend on a mode which the user can switch.

Note

Independently of this tag, the send page also has a manual reload button which the user can press at any time to reload the device information.

disabled

"disabled": true

A boolean which defaults to false. When set to true, the input is displayed but cannot be interacted with in the app, e.g. radio buttons are not clickable.

Note

The info type does not support this tag since it has no input to disable.

API Version

"apiVersion": "v2"

The version of the device RPC API which the app uses when calling device.execute. Currently "v1" and "v2" are supported:

  • "v1" passes each option as its own parameter to device.execute

  • "v2" wraps all option values into a single parameter list of the form [{"input": {...}}]

See the API v2 section of the execute method for details on the resulting parameter structure.

Note

If this field is omitted, the default API version configured in the webapp is used (see the device section of the frontend configuration, default "v2").

Dashboard

For a device to support the dashboard this option has to be set to true. It will be checked when registering a device. Make sure to implement the layout and the data for the dashboard on the device.

"dashboard_supported": true

Optionally, the height (in pixels) of the dashboard graph on the device’s send page can be set. It defaults to a larger value if omitted and only affects the send page - the device cards on the home page are not changed.

"dashboard_height": 650

Recording

If you have implemented the recording functionality, you must enable the support in the configuration file. Once you have enabled it you can start and stop the recording on the send page of the device.

"recording_supported": true

Example

An example of this file can be found on Gitlab.

RPC API

Our devices implement a json RPC API. Ping and Info are already implemented by the skeleton, the remaining methods have to be implemented by you in api/device/methods.py:

File Up and Download

The devices provide two endpoints over which some file exchange can happen. These endpoints can be used for example to download, recording data or to upload a file with a set of instructions for the device.

Hardware Communication

You will need to implement all communication to your hardware inside the hardware directory. Make sure to import all functionality which you want to have available in api/device/methods.py in the file hardware/__init__.py. Everything inside this file will be imported there.

When you need to install additional libraries or dependencies make sure to install them in the virtual environment using pip. When you are finished write them into the requirements.txt with

pip freeze > requirements.txt

For the hardware implementation it is suggested that you to use a folder structure like this:

rpc_api
├── api
│   └── ...
├── hardware
│   ├── HARWARE_OPTION_1
|   |   ├── __init__.py
|   |   ├── config.py
|   |   ├── hardware_option_1.py
│   |   └── ... optional helper files ...
│   ├── HARWARE_OPTION_2
|   |   ├── __init__.py
|   |   ├── config.py
|   |   ├── hardware_option_2.py
│   |   └── ... optional helper files ...
│   └── __init__.py
└── ...

Which may then look something like this:

rpc_api
├── api
│   └── ...
├── hardware
│   ├── parker_compax_3s      // hw package to communicate with a frequency converter via a serial
|   |   ├── __init__.py
|   |   ├── config.py
|   |   ├── compax3s.py
|   |   ├── exceptions.py
│   |   └── serial.py
│   ├── sensors               // hw package to read sensor data and provide them for a graph on the user interface
|   |   ├── __init__.py
|   |   ├── config.py
|   |   ├── graph.py
│   |   └── sensors.py
│   └── __init__.py
└── ...

However, you are free on how your structure will look.