Writing a Thing
In this section, we will write a simple example Thing that provides some functionality on the server.
Note
Usually, you will write your own Thing in a separate Python module and run it using a configuration file as described in Running LabThings-FastAPI. However, for this tutorial, we will write the Thing in a single file, and use a __name__ == "__main__" block to run it directly. This is not recommended for production code, but it is convenient for a tutorial.
Our first Thing will pretend to be a light: we can set its brightness and turn it on and off. A first, most basic implementation might look like:
import labthings_fastapi as lt
class Light(lt.Thing):
"""A computer-controlled light, our first example Thing."""
brightness: int = lt.property(default=100, ge=0, le=100)
"""The brightness of the light, in % of maximum."""
is_on: bool = lt.property(default=False, readonly=True)
"""Whether the light is currently on."""
@lt.action
def toggle(self):
"""Swap the light between on and off."""
self.is_on = not self.is_on
server = lt.ThingServer.from_things({"light": Light})
if __name__ == "__main__":
import uvicorn
# We run the server using `uvicorn`:
uvicorn.run(server.app, port=5000, ws="websockets-sansio")
If you visit http://localhost:5000/light, you will see the Thing Description. You can also interact with it using the OpenAPI documentation at http://localhost:5000/docs. If you visit http://localhost:5000/light/brightness, you can set the brightness of the light, and if you visit http://localhost:5000/light/is_on, you can see whether the light is on. Changing values on the server requires a PUT or POST request, which is easiest to do using the OpenAPI “Try it out” feature. Check that you can use a POST request to the toggle endpoint to turn the light on and off.
This example has both properties and actions. Properties are used to read and write values, while actions are used to perform operations that change the state of the Thing.
Properties
We have a property for the brightness of the light and a property to indicate whether the light is on or off. These are both “data properties” because they function just like variables.
is_on is a boolean value, so it may be either True or False. brightness is an integer, and the ge and le arguments constrain it to take a value between 0 and 100. See Property constraints for more details.
It’s also possible to have properties defined using a function, for example we could add in:
@lt.property
def status(self) -> str:
"""A human-readable status of the light."""
if self.is_on:
return f"The light is on at {self.brightness}% brightness."
else:
return "The light is off."
This is a “functional property” because its value is determined by a function. Functional properties may be read-only (as in this example) or read-write (see Properties).
Actions
The action toggle changes the state of the light by toggling the is_on property between True and False. For more detail on how actions work, see Actions.