Package: labthings
Python LabThings (for Flask)A thread-based Python implementation of the LabThings API structure, based on the Flask microframework.Installationpip install labthingsQuickstart exampleThis example assumes aPretendSpectrometerclass, which already hasdataandintegration_timeattributes, as well as anaverage_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.#!/usr/bin/env pythonimporttimefromlabthingsimportActionView,PropertyView,create_app,fields,find_component,opfromlabthings.example_componentsimportPretendSpectrometerfromlabthings.jsonimportencode_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 argsclassDenoiseProperty(PropertyView):"""Value of integration_time"""schema=fields.Int(required=True,minimum=100,maximum=500)semtype="LevelProperty"@op.readpropertydefget(self):# When a GET request is made, we'll find our attached componentmy_component=find_component("org.labthings.example.mycomponent")returnmy_component.integration_time@op.writepropertydefput(self,new_property_value):# Find our attached componentmy_component=find_component("org.labthings.example.mycomponent")# Apply the new valuemy_component.integration_time=new_property_valuereturnmy_component.integration_time"""Create a view to quickly get some noisy data, and register is as a Thing property"""classQuickDataProperty(PropertyView):"""Show the current data value"""# Marshal the response as a list of floatsschema=fields.List(fields.Float())@op.readpropertydefget(self):# Find our attached componentmy_component=find_component("org.labthings.example.mycomponent")returnmy_component.data"""Create a view to start an averaged measurement, and register is as a Thing action"""classMeasurementAction(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 numbersschema=fields.List(fields.Number)# Main function to handle POST requests@op.invokeactiondefpost(self,args):"""Start an averaged measurement"""# Find our attached componentmy_component=find_component("org.labthings.example.mycomponent")# Get arguments and start a background taskn_averages=args.get("averages")# Return the task informationreturnmy_component.average_data(n_averages)# Create LabThings Flask appapp,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 hardwaremy_spectrometer=PretendSpectrometer()labthing.add_component(my_spectrometer,"org.labthings.example.mycomponent")# Add routes for the API views we createdlabthing.add_view(DenoiseProperty,"/integration_time")labthing.add_view(QuickDataProperty,"/quick-data")labthing.add_view(MeasurementAction,"/actions/measure")# Start the appif__name__=="__main__":fromlabthingsimportServerServer(app).run()AcknowledgementsMuch of the code surrounding default response formatting has been liberally taken fromFlask-RESTful. The integratedMarshmallowsupport was inspired byFlask-MarshmallowandFlask-ApiSpec.Developer notesChangelog generationnpm install -g conventional-changelog-clinpx conventional-changelog -r 1 --config ./changelog.config.js -i CHANGELOG.md -s