UniteLabs
Tutorial

Feature

What is a feature and how to create one.

Generating a new feature

Connector Directory Organization

Create a folder in src/unitelabs/package_name/features/ with the name of the feature in lowercase without any special characters including ' ' and '-'. Use underscores to separate words if your feature name consists of multiple words.

├── project-name
  ├── src/unitelabs/package_name
    ├── __init__.py
    ├── __main__.py
    └── io
      ├── __init__.py
      └── package_name_protocol.py
    └── features
      └── core
        └── random_number_provider
          ├── __init__.py
          └── random_number_provider.py

Within the SiLA specification, it is best practice to specify the feature type with an appendix. Feature types are service, controller, and provider. Whereas controllers and providers mostly contain functionality for control operations and data or information provisioning respectively, the service type contains functionalities of both.

Our example feature folder is src/unitelabs/package_name/features/core/random_number_provider/

Creating and Naming a new Feature

In the UniteLabs Connector Framework, the interface is designed following a code-first approach, hence the interface is written in Python and not using the XML-schema-based Feature Definition Language (FDL) as implemented in several SiLA reference implementations.

Create your first feature by defining a class with your feature name in PascalCase. Add connector details such as the originator, category, version, and maturity_level.

  • The originator follows a reverse domain name notation, consisting of a domain name followed by your organization name.
  • The category defines the functional group that the feature is grouped in, e.g. lhs, robot, weighing etc.
  • The version consists of a major and minor version and starts at 1.0 for published connectors.
  • The maturity level can be one of Draft, Verified, or Normative. A maturity level of Verified requires a validation step via the SiLA community in which at least one SiLA member checks your feature definition. Normative is reserved for features that are part of the standard and located in the SiLA Base repository.

So to start:

src/unitelabs/package_name/features/core/random_number_provider/random_number_provider.py
class RandomNumberProvider(sila.Feature):
    """
    This feature contains methods that provide a client with randomly generated numbers.
    """

    def __init__(self):
        super().__init__(
            originator="io.unitelabs",
            category="example",
            version="1.0",
            maturity_level="Draft",
        )

To add endpoints to the interface of this feature, you add methods to the class. Depending on the type of endpoint, a variety of decorators can be used. The framework uses decorators extensively to keep the feature definition and implementation files as lean as possible.

The @sila.UnobservableCommand, @sila.ObservableCommand and @sila.UnobservableProperty, @sila.ObservableProperty decorators are used to wrap python methods. For more information about decorator usage see the SiLA Data Endpoints Tutorial

Registering a feature

Open the __init__.py file in the connector directory:

├── project-name
  └── src/unitelabs/package_name
    └── __init__.py

Import the RandomNumberProvider feature and register it with the connector application using the app.register() method. Within this file you can also add and update the connector information, such as name, type, description, version, and the vendor url. The connector information is not just visible to the client, but also provides valuable information for auto-discovery as it is also part of the mDNS message that is broadcasted via zeroconf/bonjour in your local network. That makes it very easy to find, ultimately enabling plug 'n play!

__init__.py
from unitelabs.cdk import Connector

from .features.core.random_number_provider import RandomNumberProvider


async def create_app():
    """Creates the connector application"""
    app = Connector(
        {
            "sila_server": {
                "name": "Thermocycler",
                "type": "Example",
                "description": "Control the temperature of your samples with this thermocycler.",
                "version": "0.1.0",
                "vendor_url": "https://unitelabs.io/",
            }
        }
    )

    app.register(RandomNumberProvider())
    return app

Abstract Base Feature Classes

It is sometimes the case that we have shared functionality across multiple connectors which we would like to encapsulate into a shared Base Feature. Some examples of Base Features in the CDK include the SimulationControllerBase and WeighingServiceBase. Additionally, this features folder contains some fully-fledged Features, like the LockController which one can incorporate wholesale into their connectors.

Broadly what these base features attempt to do is to create an abstraction for a process, such as weighing a thing, such that any connector for a device that is capable of weighing things, can build upon this shared base class to expose a consistent weighing API.


Copyright © 2025