Skip to content

Kuando base

busylight_core.vendors.kuando.kuando_base

Kuando vendor base class.

Classes

busylight_core.vendors.kuando.kuando_base.KuandoBase

KuandoBase(hardware, *, reset=False, exclusive=True)

Bases: Light

Base class for Kuando Busylight devices.

Provides common functionality for all Kuando devices, primarily the Busylight product line. Use this as a base class when implementing new Kuando device variants.

Initialize a Light instance with the specified hardware device.

Creates a Light instance bound to the given hardware device and configures it for use. The hardware should be obtained from Hardware.enumerate() and verified with the class's claims() method.

Use this constructor when you have specific hardware and want to create a Light instance for direct device control.

:param hardware: Hardware instance representing the device to control :param reset: Reset the device to a known state during initialization :param exclusive: Acquire exclusive access to prevent interference :raises HardwareUnsupportedError: If Light class cannot control hardware

Source code in src/busylight_core/light.py
def __init__(
    self,
    hardware: Hardware,
    *,
    reset: bool = False,
    exclusive: bool = True,
) -> None:
    """Initialize a Light instance with the specified hardware device.

    Creates a Light instance bound to the given hardware device and
    configures it for use. The hardware should be obtained from
    Hardware.enumerate() and verified with the class's claims() method.

    Use this constructor when you have specific hardware and want to
    create a Light instance for direct device control.

    :param hardware: Hardware instance representing the device to control
    :param reset: Reset the device to a known state during initialization
    :param exclusive: Acquire exclusive access to prevent interference
    :raises HardwareUnsupportedError: If Light class cannot control hardware
    """
    if not self.__class__.claims(hardware):
        raise HardwareUnsupportedError(hardware, self.__class__)

    self.hardware = hardware
    self._reset = reset
    self._exclusive = exclusive

    if exclusive:
        self.hardware.acquire()

    if reset:
        self.reset()
Attributes
busylight_core.vendors.kuando.kuando_base.KuandoBase.event_loop cached property
event_loop

The default event loop.

Returns the currently running event loop, or creates a new one if no event loop is currently running.

busylight_core.vendors.kuando.kuando_base.KuandoBase.tasks cached property
tasks

Active asyncio tasks that are associated with this instance.

Dictionary mapping task names to their corresponding asyncio.Task objects.

busylight_core.vendors.kuando.kuando_base.KuandoBase.task_info cached property
task_info

Enhanced task information with priority and status tracking.

Dictionary mapping task names to TaskInfo objects containing metadata about priority, creation time, and current status.

busylight_core.vendors.kuando.kuando_base.KuandoBase.task_strategy cached property
task_strategy

Return the appropriate task instantiation function based on environment.

Automatically detects if we're in an asyncio context and returns the corresponding task creation function. Both functions have identical call signatures for transparent usage.

:return: Function to create tasks (asyncio or threading based)

busylight_core.vendors.kuando.kuando_base.KuandoBase.supported_device_ids class-attribute
supported_device_ids = {}
busylight_core.vendors.kuando.kuando_base.KuandoBase.hardware instance-attribute
hardware = hardware
busylight_core.vendors.kuando.kuando_base.KuandoBase.path cached property
path

The path to the hardware device.

busylight_core.vendors.kuando.kuando_base.KuandoBase.platform cached property
platform

The discovered operating system platform name.

busylight_core.vendors.kuando.kuando_base.KuandoBase.exclusive property
exclusive

Return True if the light has exclusive access to the hardware.

busylight_core.vendors.kuando.kuando_base.KuandoBase.was_reset property
was_reset

Return True if the light was reset when the hardware was initialized.

busylight_core.vendors.kuando.kuando_base.KuandoBase.sort_key cached property
sort_key

Return a tuple used for sorting lights.

The tuple consists of: - vendor name in lowercase - device name in lowercase - hardware path

busylight_core.vendors.kuando.kuando_base.KuandoBase.name cached property
name

The human-readable marketing name of this light.

busylight_core.vendors.kuando.kuando_base.KuandoBase.hex property
hex

The hexadecimal representation of the light's state.

busylight_core.vendors.kuando.kuando_base.KuandoBase.read_strategy property
read_strategy

The read method used by this light.

busylight_core.vendors.kuando.kuando_base.KuandoBase.write_strategy property
write_strategy

The write method used by this light.

busylight_core.vendors.kuando.kuando_base.KuandoBase.color abstractmethod property writable
color

Get the current RGB color of the light.

busylight_core.vendors.kuando.kuando_base.KuandoBase.is_lit property
is_lit

Check if the light is currently lit.

Functions
busylight_core.vendors.kuando.kuando_base.KuandoBase.vendor staticmethod
vendor()

Return the vendor name for Kuando devices.

Provides the official vendor branding for user interfaces and device identification.

:return: Official vendor name string

Source code in src/busylight_core/vendors/kuando/kuando_base.py
@staticmethod
def vendor() -> str:
    """Return the vendor name for Kuando devices.

    Provides the official vendor branding for user interfaces
    and device identification.

    :return: Official vendor name string
    """
    return "Kuando"
busylight_core.vendors.kuando.kuando_base.KuandoBase.add_task
add_task(
    name,
    func,
    priority=NORMAL,
    replace=False,
    interval=None,
)

Create a task using environment-appropriate strategy.

The environment (asyncio vs non-asyncio) automatically determines whether to use asyncio tasks or threading timers. Both strategies support the same function signatures and periodic execution.

:param name: Unique identifier for the task :param func: Function to execute (sync or async) :param priority: Task priority (used for asyncio tasks only) :param replace: Whether to replace existing task with same name :param interval: For periodic tasks, repeat interval in seconds :return: Created asyncio.Task or threading.Timer

Source code in src/busylight_core/mixins/taskable.py
def add_task(
    self,
    name: str,
    func: TaskFunction,
    priority: TaskPriority = TaskPriority.NORMAL,
    replace: bool = False,
    interval: float | None = None,
) -> asyncio.Task | threading.Timer:
    """Create a task using environment-appropriate strategy.

    The environment (asyncio vs non-asyncio) automatically determines whether
    to use asyncio tasks or threading timers. Both strategies support the same
    function signatures and periodic execution.

    :param name: Unique identifier for the task
    :param func: Function to execute (sync or async)
    :param priority: Task priority (used for asyncio tasks only)
    :param replace: Whether to replace existing task with same name
    :param interval: For periodic tasks, repeat interval in seconds
    :return: Created asyncio.Task or threading.Timer
    """
    # Clean up existing task if replacing
    if replace:
        self.cancel_task(name)
    elif self._task_exists(name):
        return self._get_existing_task(name)

    # Backward compatibility: handle coroutine functions that expect to be called
    # with arguments in the old style (coroutine(self) instead of func(self))
    if interval is None and asyncio.iscoroutinefunction(func):
        # Old-style coroutine that should be called directly
        return self._create_asyncio_task_legacy(name, func, priority)

    # Use environment-determined strategy
    return self.task_strategy(name, func, interval)
busylight_core.vendors.kuando.kuando_base.KuandoBase.cancel_task
cancel_task(name)

Cancel task regardless of which strategy created it.

:param name: Name of task to cancel :return: The cancelled task/timer or None if not found

Source code in src/busylight_core/mixins/taskable.py
def cancel_task(self, name: str) -> asyncio.Task | threading.Timer | None:
    """Cancel task regardless of which strategy created it.

    :param name: Name of task to cancel
    :return: The cancelled task/timer or None if not found
    """
    # Try asyncio first
    if name in self.tasks:
        return self._cancel_asyncio_task(name)

    # Ensure _thread_tasks is initialized
    if not hasattr(self, "_thread_tasks"):
        self._thread_tasks = {}
        self._task_lock = threading.Lock()

    # Try threading
    if name in self._thread_tasks:
        return self._cancel_thread_task(name)

    return None
busylight_core.vendors.kuando.kuando_base.KuandoBase.cancel_tasks
cancel_tasks(priority=None)

Cancel all tasks or tasks of specific priority.

Cancels either all tasks or only tasks matching the specified priority level.

:param priority: If specified, only cancel tasks of this priority level

Source code in src/busylight_core/mixins/taskable.py
def cancel_tasks(self, priority: TaskPriority | None = None) -> None:
    """Cancel all tasks or tasks of specific priority.

    Cancels either all tasks or only tasks matching the specified priority level.

    :param priority: If specified, only cancel tasks of this priority level
    """
    if priority is None:
        # Cancel all asyncio tasks (check if initialized)
        if hasattr(self, "_tasks") or "tasks" in self.__dict__:
            for task in self.tasks.values():
                task.cancel()
            self.tasks.clear()

        if hasattr(self, "_task_info") or "task_info" in self.__dict__:
            self.task_info.clear()

        # Cancel all threading tasks
        if hasattr(self, "_thread_tasks"):
            with self._task_lock:
                for timer in self._thread_tasks.values():
                    timer.cancel()
                self._thread_tasks.clear()
    # Priority-based cancellation for asyncio (existing)
    elif hasattr(self, "_task_info") or "task_info" in self.__dict__:
        to_cancel = [
            name
            for name, task_info in self.task_info.items()
            if task_info.priority == priority and task_info.is_running
        ]
        for name in to_cancel:
            self.task_info[name].task.cancel()
        self._cleanup_completed_tasks()
busylight_core.vendors.kuando.kuando_base.KuandoBase.get_task_status
get_task_status(name)

Get detailed status information for a task.

Returns comprehensive status information including running state, exceptions, priority, and creation time.

:param name: Name of task to check :return: Dictionary with task status details or None if not found

Source code in src/busylight_core/mixins/taskable.py
def get_task_status(self, name: str) -> dict[str, Any] | None:
    """Get detailed status information for a task.

    Returns comprehensive status information including running state,
    exceptions, priority, and creation time.

    :param name: Name of task to check
    :return: Dictionary with task status details or None if not found
    """
    task_info = self.task_info.get(name)
    if not task_info:
        task = self.tasks.get(name)
        if task:
            return {
                "name": name,
                "running": not task.done(),
                "cancelled": task.cancelled(),
                "has_exception": task.done()
                and not task.cancelled()
                and task.exception() is not None,
                "exception": task.exception() if task.done() else None,
                "priority": "unknown",
                "created_at": "unknown",
            }
        return None

    return {
        "name": task_info.name,
        "running": task_info.is_running,
        "cancelled": task_info.is_cancelled,
        "has_exception": task_info.has_exception,
        "exception": task_info.exception,
        "priority": task_info.priority.name,
        "created_at": task_info.created_at,
    }
busylight_core.vendors.kuando.kuando_base.KuandoBase.list_active_tasks
list_active_tasks()

Get list of currently active task names.

Returns sorted list of task names that are currently running.

:return: List of task names that are currently running

Source code in src/busylight_core/mixins/taskable.py
def list_active_tasks(self) -> list[str]:
    """Get list of currently active task names.

    Returns sorted list of task names that are currently running.

    :return: List of task names that are currently running
    """
    active = []

    # Check asyncio tasks
    for name, task_info in self.task_info.items():
        if task_info.is_running:
            active.append(name)

    for name, task in self.tasks.items():
        if name not in self.task_info and not task.done():
            active.append(name)

    # Check threading tasks
    with self._task_lock:
        for name, timer in self._thread_tasks.items():
            if timer.is_alive():
                active.append(name)

    return sorted(active)
busylight_core.vendors.kuando.kuando_base.KuandoBase.unique_device_names cached classmethod
unique_device_names()

Get all unique marketing names for devices supported by this class.

Returns the human-readable product names from the supported_device_ids mapping, with duplicates removed. Use this to display available device types to users or for device capability documentation.

:return: Sorted list of unique device marketing names

Source code in src/busylight_core/light.py
@classmethod
@cache
def unique_device_names(cls) -> list[str]:
    """Get all unique marketing names for devices supported by this class.

    Returns the human-readable product names from the supported_device_ids
    mapping, with duplicates removed. Use this to display available device
    types to users or for device capability documentation.

    :return: Sorted list of unique device marketing names
    """
    return sorted(set(cls.supported_device_ids.values()))
busylight_core.vendors.kuando.kuando_base.KuandoBase.unique_device_ids cached classmethod
unique_device_ids()

Get all unique vendor/product ID pairs supported by this class.

Returns the USB vendor and product ID combinations that this class can control. Use this for hardware enumeration, udev rule generation, or debugging device detection issues.

:return: Sorted list of (vendor_id, product_id) tuples

Source code in src/busylight_core/light.py
@classmethod
@cache
def unique_device_ids(cls) -> list[tuple[int, int]]:
    """Get all unique vendor/product ID pairs supported by this class.

    Returns the USB vendor and product ID combinations that this class
    can control. Use this for hardware enumeration, udev rule generation,
    or debugging device detection issues.

    :return: Sorted list of (vendor_id, product_id) tuples
    """
    return sorted(set(cls.supported_device_ids.keys()))
busylight_core.vendors.kuando.kuando_base.KuandoBase.claims classmethod
claims(hardware)

Check if this class can control the given hardware device.

Determines whether this Light subclass supports the specific hardware by checking if the device's vendor/product ID pair matches any entry in the supported_device_ids mapping. Use this during device discovery to find the appropriate Light subclass for each detected device.

:param hardware: Hardware instance to test for compatibility :return: True if this class can control the hardware device

Source code in src/busylight_core/light.py
@classmethod
def claims(cls, hardware: Hardware) -> bool:
    """Check if this class can control the given hardware device.

    Determines whether this Light subclass supports the specific hardware
    by checking if the device's vendor/product ID pair matches any entry
    in the supported_device_ids mapping. Use this during device discovery
    to find the appropriate Light subclass for each detected device.

    :param hardware: Hardware instance to test for compatibility
    :return: True if this class can control the hardware device
    """
    return hardware.device_id in cls.supported_device_ids
busylight_core.vendors.kuando.kuando_base.KuandoBase.subclasses cached classmethod
subclasses()

Return a list of all subclasses of this class.

Source code in src/busylight_core/light.py
@classmethod
@cache
def subclasses(cls) -> list[type[Light]]:
    """Return a list of all subclasses of this class."""
    subclasses = []

    if cls != Light and cls.supported_device_ids:
        subclasses.append(cls)

    for subclass in cls.__subclasses__():
        subclasses.extend(subclass.subclasses())

    return sorted(subclasses, key=lambda s: s.__module__)
busylight_core.vendors.kuando.kuando_base.KuandoBase.supported_lights cached classmethod
supported_lights()

Return a dictionary of supported lights by vendor.

Keys are vendor names, values are a list of product names.

Source code in src/busylight_core/light.py
@classmethod
@cache
def supported_lights(cls) -> dict[str, list[str]]:
    """Return a dictionary of supported lights by vendor.

    Keys are vendor names, values are a list of product names.
    """
    supported_lights: dict[str, list[str]] = {}

    for subclass in cls.subclasses():
        names = supported_lights.setdefault(subclass.vendor(), [])
        names.extend(subclass.unique_device_names())

    return supported_lights
busylight_core.vendors.kuando.kuando_base.KuandoBase.available_hardware classmethod
available_hardware()

Discover all compatible hardware devices available for control.

Scans the system for USB devices that match known vendor/product ID combinations and groups them by the Light subclass that can control them. Use this for device discovery, inventory management, or when you need to present users with available device options.

The returned Hardware instances represent devices that were found and claimed by Light subclasses, but may still be in use by other processes. Actual device acquisition occurs during Light initialization.

:return: Mapping from Light subclass to list of compatible Hardware instances

Source code in src/busylight_core/light.py
@classmethod
def available_hardware(cls) -> dict[type[Light], list[Hardware]]:
    """Discover all compatible hardware devices available for control.

    Scans the system for USB devices that match known vendor/product ID
    combinations and groups them by the Light subclass that can control
    them. Use this for device discovery, inventory management, or when
    you need to present users with available device options.

    The returned Hardware instances represent devices that were found
    and claimed by Light subclasses, but may still be in use by other
    processes. Actual device acquisition occurs during Light initialization.

    :return: Mapping from Light subclass to list of compatible Hardware instances
    """
    available_lights: dict[type[Light], list[Hardware]] = {}

    for hardware in Hardware.enumerate():
        if cls.supported_device_ids:
            if cls.claims(hardware):
                logger.debug(f"{cls.__name__} claims {hardware}")
                claimed = available_lights.setdefault(cls, [])
                claimed.append(hardware)
        else:
            for subclass in cls.subclasses():
                if subclass.claims(hardware):
                    logger.debug(f"{subclass.__name__} claims {hardware}")
                    claimed = available_lights.setdefault(subclass, [])
                    claimed.append(hardware)

    return available_lights
busylight_core.vendors.kuando.kuando_base.KuandoBase.all_lights classmethod
all_lights(*, reset=True, exclusive=True)

Create initialized Light instances for all available compatible devices.

Discovers all compatible hardware and returns Light instances ready for immediate use. Each light is initialized with the specified configuration and can be used to control its device without further setup.

Use this when you want to control all connected lights simultaneously, such as for synchronized effects or system-wide status indication.

:param reset: Reset devices to known state during initialization :param exclusive: Acquire exclusive access to prevent interference :return: List of initialized Light instances, empty if none found

Source code in src/busylight_core/light.py
@classmethod
def all_lights(cls, *, reset: bool = True, exclusive: bool = True) -> list[Light]:
    """Create initialized Light instances for all available compatible devices.

    Discovers all compatible hardware and returns Light instances ready for
    immediate use. Each light is initialized with the specified configuration
    and can be used to control its device without further setup.

    Use this when you want to control all connected lights simultaneously,
    such as for synchronized effects or system-wide status indication.

    :param reset: Reset devices to known state during initialization
    :param exclusive: Acquire exclusive access to prevent interference
    :return: List of initialized Light instances, empty if none found
    """
    lights: list[Light] = []

    for subclass, devices in cls.available_hardware().items():
        for device in devices:
            try:
                lights.append(subclass(device, reset=reset, exclusive=exclusive))
            except Exception as error:
                logger.info(f"Failed to acquire {device}: {error}")

    return lights
busylight_core.vendors.kuando.kuando_base.KuandoBase.first_light classmethod
first_light(*, reset=True, exclusive=True)

Create the first available Light instance ready for immediate use.

Discovers compatible devices and returns the first successfully initialized Light instance. Use this when you need a single light for simple status indication and don't care about the specific device type or vendor.

:param reset: Reset device to known state during initialization :param exclusive: Acquire exclusive access to prevent interference :return: Initialized Light instance ready for control :raises NoLightsFoundError: If no compatible devices found or init fails

Source code in src/busylight_core/light.py
@classmethod
def first_light(cls, *, reset: bool = True, exclusive: bool = True) -> Light:
    """Create the first available Light instance ready for immediate use.

    Discovers compatible devices and returns the first successfully
    initialized Light instance. Use this when you need a single light
    for simple status indication and don't care about the specific
    device type or vendor.

    :param reset: Reset device to known state during initialization
    :param exclusive: Acquire exclusive access to prevent interference
    :return: Initialized Light instance ready for control
    :raises NoLightsFoundError: If no compatible devices found or init fails
    """
    for subclass, devices in cls.available_hardware().items():
        for device in devices:
            try:
                return subclass(device, reset=reset, exclusive=exclusive)
            except Exception as error:
                logger.info(f"Failed to acquire {device}: {error}")
                raise

    raise NoLightsFoundError(cls)
busylight_core.vendors.kuando.kuando_base.KuandoBase.udev_rules classmethod
udev_rules(mode=438)

Return a dictionary of udev rules for the light subclass.

The keys of the dictionary are device ID tuples, while the values are lists of udev rules for a particular light. If duplicate device IDs are encountered, the first device ID wins and subsequent device IDs are ignored.

:param mode: int - file permissions for the device, defaults to 0o666

Source code in src/busylight_core/light.py
@classmethod
def udev_rules(cls, mode: int = 0o666) -> dict[tuple[int, int], list[str]]:
    """Return a dictionary of udev rules for the light subclass.

    The keys of the dictionary are device ID tuples, while the
    values are lists of udev rules for a particular light.  If
    duplicate device IDs are encountered, the first device ID
    wins and subsequent device IDs are ignored.

    :param mode: int - file permissions for the device, defaults to 0o666
    """
    rules = {}

    rule_formats = [
        'SUBSYSTEMS=="usb", ATTRS{{idVendor}}=="{vid:04x}", ATTRS{{idProduct}}=="{pid:04x}", MODE="{mode:04o}"',  # noqa: E501
        'KERNEL=="hidraw*", ATTRS{{idVendor}}=="{vid:04x}", ATTRS{{idProduct}}=="{pid:04x}", MODE="{mode:04o}"',  # noqa: E501
    ]

    if cls.supported_device_ids:
        for vid, pid in cls.unique_device_ids():
            content = rules.setdefault((vid, pid), [])
            content.append(f"# {cls.vendor()} {cls.__name__} udev rules")
            for rule_format in rule_formats:
                content.append(rule_format.format(vid=vid, pid=pid, mode=mode))
    else:
        for subclass in cls.subclasses():
            subclass_rules = subclass.udev_rules(mode=mode)
            for key, value in subclass_rules.items():
                if key not in rules:
                    rules[key] = value

    return rules
busylight_core.vendors.kuando.kuando_base.KuandoBase.release
release()

Release the light's exclusive access to the hardware.

If the light was acquired in exclusive mode, this method releases the hardware resource, allowing other processes to access it. If the light was not acquired in exclusive mode, no action is taken.

Source code in src/busylight_core/light.py
def release(self) -> None:
    """Release the light's exclusive access to the hardware.

    If the light was acquired in exclusive mode, this method releases
    the hardware resource, allowing other processes to access it.
    If the light was not acquired in exclusive mode, no action is taken.
    """
    if self._exclusive:
        logger.debug(f"Releasing exclusive access to {self.name}")
        self.hardware.release()
        self._exclusive = False
busylight_core.vendors.kuando.kuando_base.KuandoBase.exclusive_access
exclusive_access()

Manage exclusive access to the light.

If the device is not acquired in exclusive mode, it will be acquired and released automatically.

No actions are taken if the light is already acquired in exclusive mode.

Source code in src/busylight_core/light.py
@contextlib.contextmanager
def exclusive_access(self) -> Generator[None, None, None]:
    """Manage exclusive access to the light.

    If the device is not acquired in exclusive mode, it will be
    acquired and released automatically.

    No actions are taken if the light is already acquired
    in exclusive mode.
    """
    if not self._exclusive:
        self.hardware.acquire()

    yield

    if not self._exclusive:
        self.hardware.release()
busylight_core.vendors.kuando.kuando_base.KuandoBase.update
update()

Send the current light state to the physical device.

Serializes the light's current state and transmits it to the hardware device using the appropriate platform-specific protocol. Call this method after making changes to light properties to apply them to the physical device.

The method handles platform-specific protocol differences automatically, such as adding leading zero bytes on Windows 10.

:raises LightUnavailableError: If device communication fails

Source code in src/busylight_core/light.py
def update(self) -> None:
    """Send the current light state to the physical device.

    Serializes the light's current state and transmits it to the hardware
    device using the appropriate platform-specific protocol. Call this
    method after making changes to light properties to apply them to
    the physical device.

    The method handles platform-specific protocol differences automatically,
    such as adding leading zero bytes on Windows 10.

    :raises LightUnavailableError: If device communication fails
    """
    state = bytes(self)

    match self.platform:
        case "Windows_10":
            state = bytes([0]) + state
        case "Darwin" | "Linux" | "Windows_11":
            pass
        case _:
            logger.info(f"Unsupported OS {self.platform}, hoping for the best.")

    with self.exclusive_access():
        logger.debug(f"{self.name} payload {state.hex(':')}")
        try:
            self.write_strategy(state)
        except Exception as error:
            logger.error(f"{self}: {error}")
            raise LightUnavailableError(self) from None
busylight_core.vendors.kuando.kuando_base.KuandoBase.batch_update
batch_update()

Defer device updates until multiple properties are changed.

Context manager that accumulates multiple property changes and sends them to the device in a single update operation when exiting the context. Use this when changing multiple light properties (color, brightness, effects) to reduce USB communication overhead and improve performance.

:return: Context manager for batching multiple property updates

Source code in src/busylight_core/light.py
@contextlib.contextmanager
def batch_update(self) -> Generator[None, None, None]:
    """Defer device updates until multiple properties are changed.

    Context manager that accumulates multiple property changes and sends
    them to the device in a single update operation when exiting the
    context. Use this when changing multiple light properties (color,
    brightness, effects) to reduce USB communication overhead and improve
    performance.

    :return: Context manager for batching multiple property updates
    """
    yield
    self.update()
busylight_core.vendors.kuando.kuando_base.KuandoBase.on abstractmethod
on(color, led=0)

Activate the light with the specified RGB color.

Sets the light to display the given color immediately. This is the primary method for controlling light appearance and should be implemented by all device-specific subclasses.

For devices with multiple LEDs, use led parameter to target specific LEDs or set to 0 to affect all LEDs simultaneously. Single-LED devices should ignore the led parameter.

:param color: RGB intensity values from 0-255 for each color component :param led: Target LED index, 0 affects all LEDs on the device

Source code in src/busylight_core/light.py
@abc.abstractmethod
def on(
    self,
    color: tuple[int, int, int],
    led: int = 0,
) -> None:
    """Activate the light with the specified RGB color.

    Sets the light to display the given color immediately. This is the
    primary method for controlling light appearance and should be
    implemented by all device-specific subclasses.

    For devices with multiple LEDs, use led parameter to target specific
    LEDs or set to 0 to affect all LEDs simultaneously. Single-LED devices
    should ignore the led parameter.

    :param color: RGB intensity values from 0-255 for each color component
    :param led: Target LED index, 0 affects all LEDs on the device
    """
    raise NotImplementedError
busylight_core.vendors.kuando.kuando_base.KuandoBase.off
off(led=0)

Turn off the light by setting it to black.

Deactivates the specified LED(s) by setting their color to black (0, 0, 0). Use this to turn off status indication while keeping the device available for future color changes.

For multi-LED devices, specify the LED index or use 0 to turn off all LEDs. Single-LED devices ignore the led parameter.

:param led: Target LED index, 0 affects all LEDs on the device

Source code in src/busylight_core/light.py
def off(self, led: int = 0) -> None:
    """Turn off the light by setting it to black.

    Deactivates the specified LED(s) by setting their color to black (0, 0, 0).
    Use this to turn off status indication while keeping the device available
    for future color changes.

    For multi-LED devices, specify the LED index or use 0 to turn off all LEDs.
    Single-LED devices ignore the led parameter.

    :param led: Target LED index, 0 affects all LEDs on the device
    """
    self.on((0, 0, 0), led)
busylight_core.vendors.kuando.kuando_base.KuandoBase.reset
reset()

Turn the light off and cancel associated asynchronous tasks.

Source code in src/busylight_core/light.py
def reset(self) -> None:
    """Turn the light off and cancel associated asynchronous tasks."""
    self.off()
    self.cancel_tasks()