Skip to main content
Digital skills enable the robot to interact with external services—sending emails, making API calls, retrieving information. These are always code skills, implementing explicit protocols and handling authentication, errors, and network conditions.

Characteristics

Unlike physical skills that deal with real-world variability, digital skills operate in a deterministic domain:
  • Protocol-based: Follow defined APIs and standards
  • Atomic: Many operations cannot be cancelled once started
  • Reliable: Once working, behavior is consistent
  • Network-dependent: Must handle connectivity issues

Built-in Skills

SendEmail

Sends email notifications, typically for alerts or status updates.
class SendEmail(Skill):
    @property
    def name(self):
        return "send_email"

    def guidelines(self):
        return (
            "Use to send an emergency email notification. Provide a subject and "
            "message. You can optionally provide a list of recipients, otherwise "
            "it will be sent to the default list. This should be used when a "
            "potential emergency is detected and assistance might be required."
        )

    def execute(self, subject: str, message: str, recipients: list[str] = None):
        # Send via SMTP
        # Returns (message, SkillResult)
Parameters:
ParameterTypeRequiredDescription
subjectstrYesEmail subject line.
messagestrYesEmail body content.
recipientslist[str]NoRecipients (defaults to configured list).

SendPictureViaEmail

Sends an email with the robot’s current camera view attached.
class SendPictureViaEmail(Skill):
    # Declare camera image dependency - updated at 50Hz while skill runs
    image = RobotState(RobotStateType.LAST_MAIN_CAMERA_IMAGE_B64)

    def execute(self, subject: str, message: str, recipient: str = None):
        if not self.image:
            return "No image available to send", SkillResult.FAILURE
        # Attach image and send
This skill demonstrates state dependencies—using the RobotState descriptor to declare required robot state that the system updates at 50Hz.

RetrieveEmails

Fetches recent emails from configured account.
class RetrieveEmails(Skill):
    def guidelines(self):
        return (
            "Use to retrieve recent emails from the configured email account. "
            "Provide the number of emails to retrieve (default is 5). "
            "Returns email subjects and content."
        )

    def execute(self, count: int = 5):
        # Connect to IMAP, fetch emails
        # Returns formatted email list

Creating Digital Skills

Template

import os
from brain_client.skill_types import Skill, SkillResult

class MyDigitalSkill(Skill):
    def __init__(self, logger):
        super().__init__(logger)
        self.api_key = os.environ.get("SERVICE_API_KEY")
        if not self.api_key:
            raise ValueError("SERVICE_API_KEY not configured")

    @property
    def name(self):
        return "my_digital_skill"

    def guidelines(self):
        return "Use when [describe appropriate use cases]"

    def execute(self, param: str):
        try:
            # Call external service
            result = self._call_service(param)
            return f"Success: {result}", SkillResult.SUCCESS
        except TimeoutError:
            return "Service timed out", SkillResult.FAILURE
        except Exception as e:
            return f"Error: {e}", SkillResult.FAILURE

    def cancel(self):
        return "Operation cannot be cancelled"

Best Practices

Authentication
  • Store credentials in environment variables or secret managers
  • Never hardcode passwords or API keys
  • Validate credentials at initialization
Error Handling
def execute(self, query: str):
    try:
        response = self.client.call(query, timeout=10)
        return f"Result: {response}", SkillResult.SUCCESS
    except RateLimitError:
        return "Rate limit exceeded", SkillResult.FAILURE
    except NetworkError:
        return "Network unavailable", SkillResult.FAILURE
    except Exception as e:
        return f"Unexpected error: {e}", SkillResult.FAILURE
Timeouts
  • Always set explicit timeouts on network calls
  • Prevent blocking indefinitely on slow services
Idempotency
  • Design operations to be safely retryable when possible
  • Consider partial failure scenarios

Requesting Robot State

Skills declare sensor data dependencies using the RobotState descriptor:
from brain_client.skill_types import RobotState, RobotStateType

class MySkill(Skill):
    # Declare state dependencies at class level
    image = RobotState(RobotStateType.LAST_MAIN_CAMERA_IMAGE_B64)
    odom = RobotState(RobotStateType.LAST_ODOM)

    def execute(self):
        if self.image:
            # Use the latest camera frame
            pass
The system automatically updates declared state at 50Hz while your skill runs. Always check for None on first access. Available state types:
State TypeDescription
LAST_MAIN_CAMERA_IMAGE_B64Latest camera frame (base64 JPEG).
LAST_ODOMCurrent odometry.
LAST_MAPOccupancy grid.
LAST_HEAD_POSITIONHead tilt angle.
See Robot State for more details on the RobotState system.

Cancellation

Many digital operations are atomic and cannot be meaningfully cancelled:
def cancel(self):
    return "Email sending is an atomic operation that cannot be canceled once started"
BASIC understands this limitation and factors it into planning decisions.