Skip to content

Commit fde6181

Browse files
committed
Removed websocket support
1 parent 8b46960 commit fde6181

File tree

17 files changed

+3423
-3710
lines changed

17 files changed

+3423
-3710
lines changed

README.md

Lines changed: 150 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -1,167 +1,150 @@
1-
# Python LabThings (for Flask)
2-
3-
[![LabThings](https://img.shields.io/badge/-LabThings-8E00FF?style=flat&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz4NCjxzdmcgY2xpcC1ydWxlPSJldmVub2RkIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIyIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAxNjMgMTYzIiB4bWw6c3BhY2U9InByZXNlcnZlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Im0xMjIuMjQgMTYyLjk5aDQwLjc0OHYtMTYyLjk5aC0xMDEuODd2NDAuNzQ4aDYxLjEyMnYxMjIuMjR6IiBmaWxsPSIjZmZmIi8+PHBhdGggZD0ibTAgMTIuMjI0di0xMi4yMjRoNDAuNzQ4djEyMi4yNGg2MS4xMjJ2NDAuNzQ4aC0xMDEuODd2LTEyLjIyNGgyMC4zNzR2LTguMTVoLTIwLjM3NHYtOC4xNDloOC4wMTl2LTguMTVoLTguMDE5di04LjE1aDIwLjM3NHYtOC4xNDloLTIwLjM3NHYtOC4xNWg4LjAxOXYtOC4xNWgtOC4wMTl2LTguMTQ5aDIwLjM3NHYtOC4xNWgtMjAuMzc0di04LjE0OWg4LjAxOXYtOC4xNWgtOC4wMTl2LTguMTVoMjAuMzc0di04LjE0OWgtMjAuMzc0di04LjE1aDguMDE5di04LjE0OWgtOC4wMTl2LTguMTVoMjAuMzc0di04LjE1aC0yMC4zNzR6IiBmaWxsPSIjZmZmIi8+PC9zdmc+DQo=)](https://github.com/labthings/)
4-
[![ReadTheDocs](https://readthedocs.org/projects/python-labthings/badge/?version=latest&style=flat)](https://python-labthings.readthedocs.io/en/latest/)
5-
[![PyPI](https://img.shields.io/pypi/v/labthings)](https://pypi.org/project/labthings/)
6-
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
7-
[![codecov](https://codecov.io/gh/labthings/python-labthings/branch/master/graph/badge.svg)](https://codecov.io/gh/labthings/python-labthings)
8-
[![Riot.im](https://img.shields.io/badge/chat-on%20riot.im-368BD6)](https://riot.im/app/#/room/#labthings:matrix.org)
9-
10-
A thread-based Python implementation of the LabThings API structure, based on the Flask microframework.
11-
12-
## Installation
13-
14-
`pip install labthings`
15-
16-
## Quickstart example
17-
18-
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.
19-
20-
```python
21-
#!/usr/bin/env python
22-
import time
23-
24-
from labthings import ActionView, PropertyView, create_app, fields, find_component, op
25-
from labthings.example_components import PretendSpectrometer
26-
from labthings.json import encode_json
27-
28-
"""
29-
Class for our lab component functionality. This could include serial communication,
30-
equipment API calls, network requests, or a "virtual" device as seen here.
31-
"""
32-
33-
34-
"""
35-
Create a view to view and change our integration_time value,
36-
and register is as a Thing property
37-
"""
38-
39-
40-
# Wrap in a semantic annotation to autmatically set schema and args
41-
class DenoiseProperty(PropertyView):
42-
"""Value of integration_time"""
43-
44-
schema = fields.Int(required=True, minimum=100, maximum=500)
45-
semtype = "LevelProperty"
46-
47-
@op.readproperty
48-
def get(self):
49-
# When a GET request is made, we'll find our attached component
50-
my_component = find_component("org.labthings.example.mycomponent")
51-
return my_component.integration_time
52-
53-
@op.writeproperty
54-
def put(self, new_property_value):
55-
# Find our attached component
56-
my_component = find_component("org.labthings.example.mycomponent")
57-
58-
# Apply the new value
59-
my_component.integration_time = new_property_value
60-
61-
return my_component.integration_time
62-
63-
@op.observeproperty
64-
def websocket(self, ws):
65-
# Find our attached component
66-
my_component = find_component("org.labthings.example.mycomponent")
67-
initial_value = None
68-
while not ws.closed:
69-
time.sleep(1)
70-
if my_component.integration_time != initial_value:
71-
ws.send(encode_json(my_component.integration_time))
72-
initial_value = my_component.integration_time
73-
74-
75-
"""
76-
Create a view to quickly get some noisy data, and register is as a Thing property
77-
"""
78-
79-
80-
class QuickDataProperty(PropertyView):
81-
"""Show the current data value"""
82-
83-
# Marshal the response as a list of floats
84-
schema = fields.List(fields.Float())
85-
86-
@op.readproperty
87-
def get(self):
88-
# Find our attached component
89-
my_component = find_component("org.labthings.example.mycomponent")
90-
return my_component.data
91-
92-
@op.observeproperty
93-
def websocket(self, ws):
94-
# Find our attached component
95-
my_component = find_component("org.labthings.example.mycomponent")
96-
while not ws.closed:
97-
ws.send(encode_json(my_component.data))
98-
99-
100-
"""
101-
Create a view to start an averaged measurement, and register is as a Thing action
102-
"""
103-
104-
105-
class MeasurementAction(ActionView):
106-
# Expect JSON parameters in the request body.
107-
# Pass to post function as dictionary argument.
108-
args = {
109-
"averages": fields.Integer(
110-
missing=20, example=20, description="Number of data sets to average over",
111-
)
112-
}
113-
# Marshal the response as a list of numbers
114-
schema = fields.List(fields.Number)
115-
116-
# Main function to handle POST requests
117-
@op.invokeaction
118-
def post(self, args):
119-
"""Start an averaged measurement"""
120-
121-
# Find our attached component
122-
my_component = find_component("org.labthings.example.mycomponent")
123-
124-
# Get arguments and start a background task
125-
n_averages = args.get("averages")
126-
127-
# Return the task information
128-
return my_component.average_data(n_averages)
129-
130-
131-
# Create LabThings Flask app
132-
app, labthing = create_app(
133-
__name__,
134-
title="My Lab Device API",
135-
description="Test LabThing-based API",
136-
version="0.1.0",
137-
)
138-
139-
# Attach an instance of our component
140-
# Usually a Python object controlling some piece of hardware
141-
my_spectrometer = PretendSpectrometer()
142-
labthing.add_component(my_spectrometer, "org.labthings.example.mycomponent")
143-
144-
145-
# Add routes for the API views we created
146-
labthing.add_view(DenoiseProperty, "/integration_time")
147-
labthing.add_view(QuickDataProperty, "/quick-data")
148-
labthing.add_view(MeasurementAction, "/actions/measure")
149-
150-
151-
# Start the app
152-
if __name__ == "__main__":
153-
from labthings import Server
154-
155-
Server(app).run()
156-
```
157-
158-
## Acknowledgements
159-
160-
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).
161-
162-
## Developer notes
163-
164-
### Changelog generation
165-
166-
* `npm install -g conventional-changelog-cli`
167-
* `conventional-changelog -r 1 --config ./changelog.config.js -i CHANGELOG.md -s`
1+
# Python LabThings (for Flask)
2+
3+
[![LabThings](https://img.shields.io/badge/-LabThings-8E00FF?style=flat&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz4NCjxzdmcgY2xpcC1ydWxlPSJldmVub2RkIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIyIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAxNjMgMTYzIiB4bWw6c3BhY2U9InByZXNlcnZlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Im0xMjIuMjQgMTYyLjk5aDQwLjc0OHYtMTYyLjk5aC0xMDEuODd2NDAuNzQ4aDYxLjEyMnYxMjIuMjR6IiBmaWxsPSIjZmZmIi8+PHBhdGggZD0ibTAgMTIuMjI0di0xMi4yMjRoNDAuNzQ4djEyMi4yNGg2MS4xMjJ2NDAuNzQ4aC0xMDEuODd2LTEyLjIyNGgyMC4zNzR2LTguMTVoLTIwLjM3NHYtOC4xNDloOC4wMTl2LTguMTVoLTguMDE5di04LjE1aDIwLjM3NHYtOC4xNDloLTIwLjM3NHYtOC4xNWg4LjAxOXYtOC4xNWgtOC4wMTl2LTguMTQ5aDIwLjM3NHYtOC4xNWgtMjAuMzc0di04LjE0OWg4LjAxOXYtOC4xNWgtOC4wMTl2LTguMTVoMjAuMzc0di04LjE0OWgtMjAuMzc0di04LjE1aDguMDE5di04LjE0OWgtOC4wMTl2LTguMTVoMjAuMzc0di04LjE1aC0yMC4zNzR6IiBmaWxsPSIjZmZmIi8+PC9zdmc+DQo=)](https://github.com/labthings/)
4+
[![ReadTheDocs](https://readthedocs.org/projects/python-labthings/badge/?version=latest&style=flat)](https://python-labthings.readthedocs.io/en/latest/)
5+
[![PyPI](https://img.shields.io/pypi/v/labthings)](https://pypi.org/project/labthings/)
6+
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
7+
[![codecov](https://codecov.io/gh/labthings/python-labthings/branch/master/graph/badge.svg)](https://codecov.io/gh/labthings/python-labthings)
8+
[![Riot.im](https://img.shields.io/badge/chat-on%20riot.im-368BD6)](https://riot.im/app/#/room/#labthings:matrix.org)
9+
10+
A thread-based Python implementation of the LabThings API structure, based on the Flask microframework.
11+
12+
## Installation
13+
14+
`pip install labthings`
15+
16+
## Quickstart example
17+
18+
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.
19+
20+
```python
21+
#!/usr/bin/env python
22+
import time
23+
24+
from labthings import ActionView, PropertyView, create_app, fields, find_component, op
25+
from labthings.example_components import PretendSpectrometer
26+
from labthings.json import encode_json
27+
28+
"""
29+
Class for our lab component functionality. This could include serial communication,
30+
equipment API calls, network requests, or a "virtual" device as seen here.
31+
"""
32+
33+
34+
"""
35+
Create a view to view and change our integration_time value,
36+
and register is as a Thing property
37+
"""
38+
39+
40+
# Wrap in a semantic annotation to autmatically set schema and args
41+
class DenoiseProperty(PropertyView):
42+
"""Value of integration_time"""
43+
44+
schema = fields.Int(required=True, minimum=100, maximum=500)
45+
semtype = "LevelProperty"
46+
47+
@op.readproperty
48+
def get(self):
49+
# When a GET request is made, we'll find our attached component
50+
my_component = find_component("org.labthings.example.mycomponent")
51+
return my_component.integration_time
52+
53+
@op.writeproperty
54+
def put(self, new_property_value):
55+
# Find our attached component
56+
my_component = find_component("org.labthings.example.mycomponent")
57+
58+
# Apply the new value
59+
my_component.integration_time = new_property_value
60+
61+
return my_component.integration_time
62+
63+
64+
"""
65+
Create a view to quickly get some noisy data, and register is as a Thing property
66+
"""
67+
68+
69+
class QuickDataProperty(PropertyView):
70+
"""Show the current data value"""
71+
72+
# Marshal the response as a list of floats
73+
schema = fields.List(fields.Float())
74+
75+
@op.readproperty
76+
def get(self):
77+
# Find our attached component
78+
my_component = find_component("org.labthings.example.mycomponent")
79+
return my_component.data
80+
81+
82+
83+
"""
84+
Create a view to start an averaged measurement, and register is as a Thing action
85+
"""
86+
87+
88+
class MeasurementAction(ActionView):
89+
# Expect JSON parameters in the request body.
90+
# Pass to post function as dictionary argument.
91+
args = {
92+
"averages": fields.Integer(
93+
missing=20, example=20, description="Number of data sets to average over",
94+
)
95+
}
96+
# Marshal the response as a list of numbers
97+
schema = fields.List(fields.Number)
98+
99+
# Main function to handle POST requests
100+
@op.invokeaction
101+
def post(self, args):
102+
"""Start an averaged measurement"""
103+
104+
# Find our attached component
105+
my_component = find_component("org.labthings.example.mycomponent")
106+
107+
# Get arguments and start a background task
108+
n_averages = args.get("averages")
109+
110+
# Return the task information
111+
return my_component.average_data(n_averages)
112+
113+
114+
# Create LabThings Flask app
115+
app, labthing = create_app(
116+
__name__,
117+
title="My Lab Device API",
118+
description="Test LabThing-based API",
119+
version="0.1.0",
120+
)
121+
122+
# Attach an instance of our component
123+
# Usually a Python object controlling some piece of hardware
124+
my_spectrometer = PretendSpectrometer()
125+
labthing.add_component(my_spectrometer, "org.labthings.example.mycomponent")
126+
127+
128+
# Add routes for the API views we created
129+
labthing.add_view(DenoiseProperty, "/integration_time")
130+
labthing.add_view(QuickDataProperty, "/quick-data")
131+
labthing.add_view(MeasurementAction, "/actions/measure")
132+
133+
134+
# Start the app
135+
if __name__ == "__main__":
136+
from labthings import Server
137+
138+
Server(app).run()
139+
```
140+
141+
## Acknowledgements
142+
143+
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).
144+
145+
## Developer notes
146+
147+
### Changelog generation
148+
149+
* `npm install -g conventional-changelog-cli`
150+
* `conventional-changelog -r 1 --config ./changelog.config.js -i CHANGELOG.md -s`

0 commit comments

Comments
 (0)