https://github.com/labthings/python-labthings
Python implementation of LabThings, based on the Flask microframework
https://github.com/labthings/python-labthings
flask-microframework iot iot-framework laboratory python webthings
Last synced: about 2 months ago
JSON representation
Python implementation of LabThings, based on the Flask microframework
- Host: GitHub
- URL: https://github.com/labthings/python-labthings
- Owner: labthings
- License: gpl-3.0
- Created: 2020-01-17T13:17:19.000Z (about 6 years ago)
- Default Branch: master
- Last Pushed: 2024-05-15T08:26:54.000Z (almost 2 years ago)
- Last Synced: 2024-08-10T07:35:30.172Z (over 1 year ago)
- Topics: flask-microframework, iot, iot-framework, laboratory, python, webthings
- Language: Python
- Homepage:
- Size: 3.94 MB
- Stars: 18
- Watchers: 3
- Forks: 2
- Open Issues: 24
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# Python LabThings (for Flask)
[](https://github.com/labthings/)
[](https://python-labthings.readthedocs.io/en/latest/)
[](https://pypi.org/project/labthings/)
[](https://github.com/psf/black)
[](https://codecov.io/gh/labthings/python-labthings)
[](https://riot.im/app/#/room/#labthings:matrix.org)
A thread-based Python implementation of the LabThings API structure, based on the Flask microframework.
## Installation
`pip install labthings`
## Quickstart example
This example assumes a `PretendSpectrometer` class, which already has `data` and `integration_time` attributes, as well as an `average_data(n)` method. LabThings allows you to easily convert this existing instrument control code into a fully documented, standardised web API complete with auto-discovery and automatic background task threading.
```python
#!/usr/bin/env python
import time
from labthings import ActionView, PropertyView, create_app, fields, find_component, op
from labthings.example_components import PretendSpectrometer
from labthings.json import encode_json
"""
Class for our lab component functionality. This could include serial communication,
equipment API calls, network requests, or a "virtual" device as seen here.
"""
"""
Create a view to view and change our integration_time value,
and register is as a Thing property
"""
# Wrap in a semantic annotation to automatically set schema and args
class DenoiseProperty(PropertyView):
"""Value of integration_time"""
schema = fields.Int(required=True, minimum=100, maximum=500)
semtype = "LevelProperty"
@op.readproperty
def get(self):
# When a GET request is made, we'll find our attached component
my_component = find_component("org.labthings.example.mycomponent")
return my_component.integration_time
@op.writeproperty
def put(self, new_property_value):
# Find our attached component
my_component = find_component("org.labthings.example.mycomponent")
# Apply the new value
my_component.integration_time = new_property_value
return my_component.integration_time
"""
Create a view to quickly get some noisy data, and register is as a Thing property
"""
class QuickDataProperty(PropertyView):
"""Show the current data value"""
# Marshal the response as a list of floats
schema = fields.List(fields.Float())
@op.readproperty
def get(self):
# Find our attached component
my_component = find_component("org.labthings.example.mycomponent")
return my_component.data
"""
Create a view to start an averaged measurement, and register is as a Thing action
"""
class MeasurementAction(ActionView):
# Expect JSON parameters in the request body.
# Pass to post function as dictionary argument.
args = {
"averages": fields.Integer(
missing=20, example=20, description="Number of data sets to average over",
)
}
# Marshal the response as a list of numbers
schema = fields.List(fields.Number)
# Main function to handle POST requests
@op.invokeaction
def post(self, args):
"""Start an averaged measurement"""
# Find our attached component
my_component = find_component("org.labthings.example.mycomponent")
# Get arguments and start a background task
n_averages = args.get("averages")
# Return the task information
return my_component.average_data(n_averages)
# Create LabThings Flask app
app, labthing = create_app(
__name__,
title="My Lab Device API",
description="Test LabThing-based API",
version="0.1.0",
)
# Attach an instance of our component
# Usually a Python object controlling some piece of hardware
my_spectrometer = PretendSpectrometer()
labthing.add_component(my_spectrometer, "org.labthings.example.mycomponent")
# Add routes for the API views we created
labthing.add_view(DenoiseProperty, "/integration_time")
labthing.add_view(QuickDataProperty, "/quick-data")
labthing.add_view(MeasurementAction, "/actions/measure")
# Start the app
if __name__ == "__main__":
from labthings import Server
Server(app).run()
```
## Acknowledgements
Much of the code surrounding default response formatting has been liberally taken from [Flask-RESTful](https://github.com/flask-restful/flask-restful). The integrated [Marshmallow](https://github.com/marshmallow-code/marshmallow) support was inspired by [Flask-Marshmallow](https://github.com/marshmallow-code/flask-marshmallow) and [Flask-ApiSpec](https://github.com/jmcarp/flask-apispec).
## Developer notes
### Changelog generation
* `npm install -g conventional-changelog-cli`
* `npx conventional-changelog -r 1 --config ./changelog.config.js -i CHANGELOG.md -s`