UniteLabs
Connect a device

From source

Generate a configuration, run a connector from its source project, and configure logging.

If you have a connector's Python project, cloned from source or scaffolded with the connector-factory, you can configure and run it directly with uv, hatch, or poetry. This is the typical workflow during connector development and for advanced deployments where you run the connector from its project rather than as a packaged executable.

Want to build your own connector? See Build your own.

Create a configuration file

A connector is configured through a config.json (or config.yaml) file. Generate one with the config create command:

uv run config create --app unitelabs.connector_starter:create_app
The --app option is required for Windows. On MacOS and Linux, commands like connector start and config will generally work without providing this option. On these operating systems, needing to provide --app often points to an error in the connector package.

The create command by default will create a file called config.json in the current working directory. We support yaml and json file-types and allow users to specify their own path and file type by adding the --path argument:

uv run config create --app unitelabs.connector_starter:create_app --path /path/to/config.prod.json

Run the connector

Starting the connector named connector-starter with a configuration file at the default path is as simple as:

uv run connector start --app unitelabs.connector_starter:create_app

Both connector start and certificate generate CLIs have a --config-path or -cfg argument that allows one to pass in a path to their configuration file, enabling users to create and apply multiple configurations for their connectors.

uv run connector start --app unitelabs.connector_starter:create_app -cfg /path/to/config.prod.json
Deprecated: The -v/--verbose CLI flag on start and dev is deprecated and will be removed in future versions. Until then it overrides the root config level. Configure the log level through the logging section instead.

For more information about the config CLI, check out the help documentation:

uv run config --help

or for a sub-command, e.g. create:

uv run config create --help

Logging

A connector logs its activity so you can monitor it and diagnose problems. There is no need to set anything up to get useful logs. Leaving the logging field of your configuration unset (null, the default) applies sensible, command-specific presets automatically:

CommandConsole (stderr)JSON log file (./logs/connector.log)
connector devDEBUG, human-readable
connector startINFO, human-readableDEBUG, structured JSON

In production (connector start) the console shows INFO and above for a readable live view, while the rotating file records everything from DEBUG up as structured JSON for troubleshooting. Once the active file reaches 5 MB it rolls over to connector.log.1, connector.log.2, and so on. By default the connector keeps the active file plus the 199 most recent rotated files (backupCount), deleting the oldest on each rollover:

./logs/
├── connector.log         ← active file
├── connector.log.1       ← most recent rotation
├── connector.log.2       ← ...
└── connector.log.199     ← oldest kept; deleted on the next rollover
The backupCount default is a safety cap to stop logs from filling the disk. It bounds total usage to roughly 1 GB. It is not a retention or archival policy. It is the connector user's responsibility to manage log files by shipping them to a log aggregator, rotating them with an external tool, or backing them up before they are deleted. If you don't need a large local buffer, or don't care about the logs except for error debugging in case of crashes, you can lower backupCount (or maxBytes) in your logging config; setting backupCount to 0 keeps only the active file. For more information how to configure the file handler read the respective logging.handlers.RotatingFileHandler documentation.

Customizing the logging configuration

To change the defaults, set the logging field to a standard Python logging configuration dictionary. Once set, your configuration is used for every command, overwriting the defaults.

To scaffold a new config file pre-populated with a preset, use the --explicit-logging/-x flag of config create. The flag defaults to prod:

uv run config create --app unitelabs.connector_starter:create_app -x prod

Alternatively, copy one of the presets below into the logging section of your config file and adapt it:

"logging": {
  "version": 1,
  "disable_existing_loggers": false,
  "handlers": {
    "file": {
      "class": "unitelabs.cdk.logging.RotatingStructlogHandler",
      "filename": "./logs/connector.log",
      "maxBytes": 5242880,
      "backupCount": 200,
      "encoding": "utf-8",
      "level": "DEBUG"
    },
    "console": {
      "class": "unitelabs.cdk.logging.ConsoleHandler",
      "level": "INFO"
    }
  },
  "root": {
    "level": "DEBUG",
    "handlers": ["file", "console"]
  }
}

Adjusting log levels per module

Loggers are organized hierarchically by dotted path, so any module can be tuned independently by adding a loggers entry with its own level, while root sets the default for everything else:

"logging": {
  ...
  "loggers": {
      "sila.server.cloud_server": {
        "level": "INFO"
      },
      "unitelabs.cdk": {
        "level": "WARNING"
      },
      "connector_name": {
        "level": "DEBUG"
      }
    },
  ...
}

In the example above:

  • sila.server.cloud_server — the cloud server module emits INFO and above, dropping its DEBUG records.
  • unitelabs.cdk — everything within the CDK emits WARNING and above only.
  • connector_name — everything that specific connector logs, down to DEBUG, is captured.
Building a connector yourself and want to emit logs from your own code? See the developer Logging guide.

Use the connector

With your connector running, you can interact with your instrument in three ways:

  • GroundControl — read values, trigger actions, and monitor activity directly from the edge app.
  • UniteLabs Platform — view, inspect, and operate the connector from the Platform UI, and add it to workflows.
  • SDK & REST API — control the connector programmatically: discover services, explore modules and actions, and subscribe to live data.