SiLA 2 Integration#

EOS can host SiLA 2 servers inside devices or connect to external servers. Tasks use SilaClientContext for connection management and automatic LockController handling.

Setup#

Install the SiLA 2 Python package and supporting packages:

uv sync --group sila2

Hosting SiLA Servers#

Host SiLA servers inside EOS devices using SilaDeviceMixin:

device.py

from typing import Any

from eos import Device
from eos.integrations.sila import SilaDeviceMixin
from your_package.sila import Server as YourSilaServer


class YourDevice(Device, SilaDeviceMixin, type="your_device"):
    """Device that hosts a SiLA server."""

    class Config(Device.Config):
        sila_port: int = 0  # 0 = auto-assign
        sila_insecure: bool = True

    async def _initialize(self, config: Config) -> None:
        self.sila_add_server(
            name="server",
            server_class=YourSilaServer,
            port=config.sila_port,
            insecure=config.sila_insecure,
        )
        await self.sila_start_all()

    async def _cleanup(self) -> None:
        await self.sila_stop_all()

    async def _report(self) -> dict[str, Any]:
        return {**self.sila_get_status()}

Connecting to External SiLA Servers#

Manual Connection#

Connect to a server at a known address:

self.sila_add_server_connection(
    name="external",
    address="192.168.1.100",
    port=50051,
    insecure=True,
)

Autodiscovery#

Use SiLA autodiscovery to find servers on the network:

self.sila_add_server_connection(
    name="discovered",
    server_name="YourSilaServer",
    timeout=5.0,
    insecure=True,
)

Using Servers from Tasks#

Connect to SiLA servers using SilaClientContext:

task.py

from pydantic import BaseModel

from eos import task
from eos.integrations.sila import SilaClientContext
from your_package.devices.your_device.device import YourDevice
from your_package.sila import Client as YourSilaClient


class YourOutputs(BaseModel):
    result: str


@task("Your Task")
async def your_task(device: YourDevice, value: str) -> YourOutputs:
    """Call a SiLA command."""
    async with SilaClientContext.connect(device, YourSilaClient) as client:
        # Call commands
        response = client.YourFeature.YourCommand(Parameter=value)

        # Access properties
        property_value = client.YourFeature.YourProperty.get()

        return YourOutputs(result=response.Result)

For devices with multiple servers, specify the server name:

async with SilaClientContext.connect(device, Client, "server_name") as client:
    ...

Long-Lived Connections#

For connections that must persist beyond a single context, use create_client():

# Create client without automatic closing
client = await SilaClientContext.create_client(device, YourSilaClient)

# Manually lock if needed
client.lock(timeout=300)

# Use the client
response = client.Feature.Command()

# Manually unlock and close when done
client.unlock()
client.close()

Calling Servers from Device Code#

SiLA servers can also be called from within an EOS device:

from eos import Device
from eos.integrations.sila import SilaDeviceMixin, SilaClientContext
from your_package.sila import Client as YourSilaClient


class YourDevice(Device, SilaDeviceMixin, type="your_device"):
    async def _initialize(self, config: Device.Config) -> None:
        # Connect to external SiLA server
        self.sila_add_server_connection(
            name="external",
            server_name="ExternalServer",
            insecure=True,
        )

        # Call the server during initialization
        async with SilaClientContext.connect(self, YourSilaClient) as client:
            self._initial_value = client.Feature.Property.get()

LockController Support#

EOS automatically handles LockController when present:

  • Auto-detection: Checks if server has LockController

  • Auto-locking: Locks with unique UUID (default 60s)

  • Metadata injection: Adds lock identifier to all calls

  • Auto-retry: Waits up to 60s if locked

  • Auto-unlock: Releases on context exit

Custom Timeout#

# Lock for 120 seconds
async with SilaClientContext.connect(device, Client, lock_timeout=120) as client:
    ...

Custom Retry Behavior#

# Wait up to 30 seconds (60 retries × 0.5s) for lock
async with SilaClientContext.connect(
    device, Client,
    lock_timeout=120,
    lock_retry_delay=0.5,
    lock_max_retries=60
) as client:
    ...

Disable Auto-Locking#

# No automatic locking
async with SilaClientContext.connect(device, Client, lock_timeout=None) as client:
    ...

Manual Lock Control#

async with SilaClientContext.connect(device, Client, lock_timeout=None) as client:
    client.lock(timeout=90)  # Lock with auto-generated UUID
    response = client.Feature.Command()
    client.unlock()