BusyTag.Lib 0.4.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package BusyTag.Lib --version 0.4.0
                    
NuGet\Install-Package BusyTag.Lib -Version 0.4.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="BusyTag.Lib" Version="0.4.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="BusyTag.Lib" Version="0.4.0" />
                    
Directory.Packages.props
<PackageReference Include="BusyTag.Lib" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add BusyTag.Lib --version 0.4.0
                    
#r "nuget: BusyTag.Lib, 0.4.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package BusyTag.Lib@0.4.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=BusyTag.Lib&version=0.4.0
                    
Install as a Cake Addin
#tool nuget:?package=BusyTag.Lib&version=0.4.0
                    
Install as a Cake Tool

BusyTag.Lib

NuGet Version Downloads License: MIT .NET Platform

A powerful and intuitive .NET library for seamless BusyTag device management via serial communication. Control LED patterns, manage files, and configure devices across Windows, macOS, and Linux platforms with ease.

šŸš€ Key Features

  • šŸ” Cross-platform device discovery - Automatic detection on Windows, macOS, and Linux (not fully tested)
  • šŸ“” Robust serial communication - Reliable connection management with automatic reconnection
  • šŸ“ Complete file management - Upload, download, and delete files with real-time progress tracking
  • šŸ’” Advanced LED control - Solid colors, custom patterns, and predefined animations
  • āš™ļø Device configuration - Brightness, storage, Wi-Fi, and system settings management
  • šŸ“¢ Real-time notifications - Comprehensive event system for all device operations
  • šŸ”„ Firmware update support - Built-in firmware update capabilities with progress tracking
  • šŸŽØ Rich pattern library - 30+ predefined LED patterns including police, running lights, and pulses

šŸ“¦ Installation

Package Manager Console

Install-Package BusyTag.Lib

.NET CLI

dotnet add package BusyTag.Lib

PackageReference

<PackageReference Include="BusyTag.Lib" Version="0.3.0" />

šŸƒā€ā™‚ļø Quick Start

1. Device Discovery and Connection

using BusyTag.Lib;

// Create and configure the manager
using var manager = new BusyTagManager();

// Subscribe to device events
manager.DeviceConnected += (sender, port) => 
    Console.WriteLine($"āœ… Device connected on {port}");
manager.DeviceDisconnected += (sender, port) => 
    Console.WriteLine($"āŒ Device disconnected from {port}");

// Start automatic device scanning every 5 seconds
manager.StartPeriodicDeviceSearch(intervalMs: 5000);

// Manual device discovery
var devices = await manager.FindBusyTagDevice();
if (devices?.Any() == true)
{
    Console.WriteLine($"šŸŽÆ Found devices: {string.Join(", ", devices)}");
    
    // Connect to first device
    var device = new BusyTagDevice(devices.First());
    
    // Subscribe to connection events
    device.ConnectionStateChanged += (sender, connected) => 
        Console.WriteLine($"šŸ”— Connection: {(connected ? "Connected" : "Disconnected")}");
    
    await device.Connect();
    
    if (device.IsConnected)
    {
        Console.WriteLine($"šŸ”— Connected to {device.DeviceName}");
        Console.WriteLine($"šŸ“± Firmware: {device.FirmwareVersion}");
        Console.WriteLine($"šŸ’¾ Free space: {device.FreeStorageSize:N0} bytes");
    }
}

2. Basic LED Control

// Simple color control
await device.SetSolidColorAsync("red", brightness: 80);
await device.SetSolidColorAsync("blue", brightness: 100);
await device.SetSolidColorAsync("off"); // Turn off LEDs

// Custom RGB values
await device.SendRgbColorAsync(red: 255, green: 128, blue: 0, ledBits: 127);

3. File Management

// Upload a file with progress tracking
device.FileUploadProgress += (sender, args) =>
    Console.WriteLine($"šŸ“¤ Uploading {args.FileName}: {args.ProgressLevel:F1}%");

await device.SendNewFile(@"C:\path\to\image.png");

// Display image on device
await device.ShowPictureAsync("image.png");

šŸ’” LED Control

Solid Colors

// Predefined colors with brightness control
await device.SetSolidColorAsync("red", brightness: 80);
await device.SetSolidColorAsync("blue", brightness: 100);
await device.SetSolidColorAsync("green", brightness: 60);
await device.SetSolidColorAsync("yellow", brightness: 90);
await device.SetSolidColorAsync("cyan", brightness: 70);
await device.SetSolidColorAsync("magenta", brightness: 85);
await device.SetSolidColorAsync("white", brightness: 50);
await device.SetSolidColorAsync("off"); // Turn off LEDs

// Custom RGB values with LED bit control
await device.SendRgbColorAsync(
    red: 255, green: 128, blue: 0, 
    ledBits: 127); // Orange color on all LEDs (bits 0-6)

// Control specific LED segments
await device.SendRgbColorAsync(
    red: 255, green: 0, blue: 0, 
    ledBits: 120); // Red on outer LEDs only

// Get current LED state
device.ReceivedSolidColor += (sender, ledArgs) =>
    Console.WriteLine($"šŸ’” LED Status: #{ledArgs.Color} on bits {ledArgs.LedBits}");
await device.GetSolidColorAsync();

Custom LED Patterns

using BusyTag.Lib.Util;

// Create a custom emergency pattern
var emergencyPattern = new List<PatternLine>
{
    new PatternLine(127, "FF0000", 5, 100),  // Red flash - all LEDs, fast speed, 100ms delay
    new PatternLine(127, "000000", 5, 100),  // Off
    new PatternLine(127, "0000FF", 5, 100),  // Blue flash
    new PatternLine(127, "000000", 5, 100)   // Off
};

// Send and play the pattern
await device.SetNewCustomPattern(
    list: emergencyPattern, 
    playAfterSending: true, 
    playPatternNonStop: false);

// Control pattern playback
await device.PlayPatternAsync(allow: true, repeatCount: 5);
await device.PlayPatternAsync(allow: false, repeatCount: 0); // Stop pattern

// Monitor pattern status
device.PlayPatternStatus += (sender, isPlaying) =>
    Console.WriteLine($"šŸŽ­ Pattern: {(isPlaying ? "Playing" : "Stopped")}");

Predefined Patterns

using BusyTag.Lib.Util;

// Police pattern
var policePattern = PatternListCommands.PatternList[
    PatternListCommands.PatternName.GetPolice1];

if (policePattern != null)
{
    await device.SetNewCustomPattern(
        policePattern.PatternLines, 
        playAfterSending: true, 
        playPatternNonStop: true);
}

// Running lights
var runningRed = PatternListCommands.PatternList[
    PatternListCommands.PatternName.GetRedRunningLed];

// Pulse effects
var bluePulse = PatternListCommands.PatternList[
    PatternListCommands.PatternName.GetBluePulses];

// Get pattern by name
var pattern = PatternListCommands.PatternListByName("Police 2");
if (pattern != null)
{
    await device.SetNewCustomPattern(pattern.PatternLines, true, false);
}

šŸ“ File Management

File Upload with Progress Tracking

// Subscribe to upload events for detailed feedback
device.FileUploadProgress += (sender, args) =>
{
    Console.WriteLine($"šŸ“¤ Uploading {args.FileName}: {args.ProgressLevel:F1}%");
    
    // Update progress bar in your UI
    UpdateProgressBar(args.ProgressLevel);
};

device.FileUploadFinished += (sender, args) =>
{
    if (args.Success)
    {
        Console.WriteLine($"āœ… Upload completed: {args.FileName}");
    }
    else
    {
        Console.WriteLine($"āŒ Upload failed: {args.ErrorMessage}");
        Console.WriteLine($"šŸ” Error type: {args.ErrorType}");
    }
};

// Upload files
await device.SendNewFile(@"C:\Images\logo.png");
await device.SendNewFile(@"C:\Images\animation.gif");

// Cancel ongoing upload if needed
device.CancelFileUpload(false, UploadErrorType.Cancelled, "User cancelled");

File Operations

// Monitor file list changes
device.FileListUpdated += (sender, files) =>
{
    Console.WriteLine($"šŸ“‹ Files on device ({files.Count}):");
    foreach (var file in files)
    {
        var sizeStr = file.Size > 1024 * 1024 
            ? $"{file.Size / (1024.0 * 1024):F1} MB"
            : $"{file.Size / 1024.0:F1} KB";
        Console.WriteLine($"  šŸ“„ {file.Name} ({sizeStr})");
    }
};

// Refresh file list
await device.GetFileListAsync();

// Display image on device
await device.ShowPictureAsync("logo.png");

// Monitor current image changes
device.ReceivedShowingPicture += (sender, imageName) =>
    Console.WriteLine($"šŸ–¼ļø Now showing: {imageName}");

// Download file from device to local cache
var localPath = await device.GetFileAsync("animation.gif");
if (!string.IsNullOrEmpty(localPath))
{
    Console.WriteLine($"šŸ“„ Downloaded to: {localPath}");
    
    // Check if file exists in cache
    if (device.FileExistsInCache("animation.gif"))
    {
        var cachedPath = device.GetFilePathFromCache("animation.gif");
        Console.WriteLine($"šŸ“‚ Cached at: {cachedPath}");
    }
}

// Delete files
await device.DeleteFile("old_image.png");

// Check if file exists on device
if (device.FileExistsInDeviceStorage("test.png"))
{
    var fileInfo = device.GetFileInfo("test.png");
    Console.WriteLine($"šŸ“Š File info: {fileInfo?.Name} - {fileInfo?.Size} bytes");
}

āš™ļø Device Configuration

Display and System Settings

// Control display brightness (0-100)
await device.SetDisplayBrightnessAsync(brightness: 75);

// Monitor brightness changes
device.ReceivedDisplayBrightness += (sender, brightness) =>
    Console.WriteLine($"šŸ”† Brightness set to: {brightness}%");

var currentBrightness = await device.GetDisplayBrightnessAsync();
Console.WriteLine($"šŸ”† Current brightness: {currentBrightness}%");

// Get comprehensive device information
await device.GetDeviceNameAsync();      // Updates device.DeviceName
await device.GetManufactureNameAsync(); // Updates device.ManufactureName  
await device.GetDeviceIdAsync();        // Updates device.Id
await device.GetFirmwareVersionAsync(); // Updates device.FirmwareVersion

Console.WriteLine($"šŸ“± Device: {device.DeviceName} by {device.ManufactureName}");
Console.WriteLine($"šŸ†” ID: {device.Id}");
Console.WriteLine($"šŸ“± Firmware: {device.FirmwareVersion} ({device.FirmwareVersionFloat})");
Console.WriteLine($"🌐 Local address: {device.LocalHostAddress}");

Storage Management

// Monitor storage operations
device.WritingInStorage += (sender, isWriting) =>
    Console.WriteLine($"šŸ’¾ Storage: {(isWriting ? "Writing..." : "Idle")}");

// Check storage space
await device.GetFreeStorageSizeAsync();
await device.GetTotalStorageSizeAsync();

var freeSpace = device.FreeStorageSize;
var totalSpace = device.TotalStorageSize;
var usedSpace = totalSpace - freeSpace;

Console.WriteLine($"šŸ’¾ Storage: {usedSpace:N0} / {totalSpace:N0} bytes used");
Console.WriteLine($"šŸ“Š {(usedSpace * 100.0 / totalSpace):F1}% full");

// Advanced storage operations
await device.SetUsbMassStorageActiveAsync(true);  // Enable USB mass storage
await device.SetAllowedAutoStorageScanAsync(false); // Disable auto scan
await device.ActivateFileStorageScanAsync(); // Manual storage scan

// āš ļø Destructive operations
await device.FormatDiskAsync();  // Format device storage
await device.RestartDeviceAsync(); // Restart device

WiFi Configuration (Firmware dependent)

// Monitor WiFi configuration
device.ReceivedWifiConfig += (sender, config) =>
    Console.WriteLine($"šŸ“¶ WiFi: {config.Ssid} (Password: {(string.IsNullOrEmpty(config.Password) ? "None" : "Set")})");

// Note: WiFi configuration methods depend on firmware version
// Check device.FirmwareVersionFloat for feature availability

šŸ“¢ Comprehensive Event System

The library provides extensive event notifications for all operations:

// Connection and device state
device.ConnectionStateChanged += (sender, connected) => 
    Console.WriteLine($"šŸ”— Connection: {(connected ? "Connected" : "Disconnected")}");

device.ReceivedDeviceBasicInformation += (sender, received) => 
{
    if (received)
    {
        Console.WriteLine($"šŸ“± Device info received:");
        Console.WriteLine($"   Name: {device.DeviceName}");
        Console.WriteLine($"   Firmware: {device.FirmwareVersion}");
        Console.WriteLine($"   Storage: {device.FreeStorageSize:N0}/{device.TotalStorageSize:N0}");
    }
};

// File operations with detailed progress
device.FileListUpdated += (sender, files) => 
    Console.WriteLine($"šŸ“‹ File list updated: {files.Count} files");

device.FileUploadProgress += (sender, progress) => 
{
    // Update progress bar or status
    var percentage = (int)progress.ProgressLevel;
    var progressBar = new string('ā–ˆ', percentage / 2) + new string('ā–‘', 50 - percentage / 2);
    Console.Write($"\ršŸ“¤ {progress.FileName}: [{progressBar}] {percentage}%");
};

device.FileUploadFinished += (sender, result) => 
{
    Console.WriteLine(); // New line after progress
    if (result.Success)
    {
        Console.WriteLine($"āœ… Upload successful: {result.FileName}");
    }
    else
    {
        Console.WriteLine($"āŒ Upload failed: {result.FileName}");
        Console.WriteLine($"šŸ” Reason: {result.ErrorMessage}");
        Console.WriteLine($"šŸ“‹ Type: {result.ErrorType}");
    }
};

// LED and pattern events
device.ReceivedSolidColor += (sender, ledArgs) => 
    Console.WriteLine($"šŸ’” LED color updated: #{ledArgs.Color} (LEDs: {Convert.ToString(ledArgs.LedBits, 2).PadLeft(8, '0')})");

device.PlayPatternStatus += (sender, isPlaying) => 
    Console.WriteLine($"šŸŽ­ Pattern playback: {(isPlaying ? "ā–¶ļø Playing" : "ā¹ļø Stopped")}");

// System events
device.FirmwareUpdateStatus += (sender, progress) => 
{
    Console.WriteLine($"šŸ”„ Firmware update progress: {progress:F1}%");
    if (progress >= 100.0f)
        Console.WriteLine("āœ… Firmware update completed!");
};

device.ReceivedDisplayBrightness += (sender, brightness) =>
    Console.WriteLine($"šŸ”† Display brightness: {brightness}%");

device.ReceivedShowingPicture += (sender, imageName) =>
    Console.WriteLine($"šŸ–¼ļø Currently displaying: {imageName}");

device.WritingInStorage += (sender, isWriting) => 
{
    if (isWriting)
        Console.WriteLine("šŸ’¾ Storage write operation started...");
    else
        Console.WriteLine("āœ… Storage write operation completed");
};

device.ReceivedUsbMassStorageActive += (sender, isActive) =>
    Console.WriteLine($"šŸ”Œ USB Mass Storage: {(isActive ? "Enabled" : "Disabled")}");

šŸ–„ļø Platform Support

Windows

  • Device Discovery: WMI-based VID/PID detection (303A:81DF)
  • Port Format: COM1, COM2, COM3, etc.
  • Requirements: System.Management package
  • Permissions: Standard user permissions sufficient
  • Tested on: Windows 10/11

macOS

  • Device Discovery: system_profiler USB enumeration + AT command validation
  • Port Format: /dev/tty.usbmodem-xxx
  • Requirements: Xcode command line tools
  • Permissions: May require accessibility permissions for serial access
  • Tested on: macOS 10.15+ (Catalina and newer)

Linux

  • Device Discovery: lsusb command-line tool + AT command validation
  • Port Format: /dev/ttyUSB0, /dev/ttyACM0, etc.
  • Requirements: usbutils package
  • Permissions: User must be in dialout group: sudo usermod -a -G dialout $USER
  • Not fully tested

šŸŽØ Available LED Patterns

Category Patterns Description
Emergency Police1, Police2 Red/blue alternating patterns for emergency vehicles
Color Flashes Red, Green, Blue, Yellow, Cyan, Magenta, White Simple on/off flashing in various colors
Running Lights All colors Moving light effect across LED strip
Pulse Effects All colors Breathing/pulsing animation with fade in/out

Pattern Parameters

  • LedBits: Controls which LEDs are active (0-255, binary mask)
  • Color: 6-digit hex color code (e.g., "FF0000" for red)
  • Speed: Animation speed (1-255, higher = faster)
  • Delay: Delay between steps in milliseconds

Pattern Access Examples

// By enum
var pattern = PatternListCommands.PatternList[PatternListCommands.PatternName.GetPolice1];

// By name
var pattern = PatternListCommands.PatternListByName("Police 1");

// List all available patterns
foreach (var kvp in PatternListCommands.PatternList)
{
    Console.WriteLine($"šŸŽØ {kvp.Value?.Name}: {kvp.Value?.PatternLines.Count} steps");
}

šŸ”§ Advanced Usage

Connection Management

public class BusyTagService
{
    private BusyTagDevice? _device;
    private readonly Timer _healthCheckTimer;
    
    public BusyTagService()
    {
        // Setup periodic health check
        _healthCheckTimer = new Timer(CheckConnection, null, 
            TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10));
    }
    
    private async void CheckConnection(object? state)
    {
        if (_device?.IsConnected != true)
        {
            Console.WriteLine("šŸ”„ Attempting to reconnect...");
            await ReconnectAsync();
        }
    }
    
    private async Task ReconnectAsync()
    {
        try
        {
            using var manager = new BusyTagManager();
            var devices = await manager.FindBusyTagDevice();
            
            if (devices?.Any() == true)
            {
                _device = new BusyTagDevice(devices.First());
                await _device.Connect();
                
                if (_device.IsConnected)
                {
                    Console.WriteLine("āœ… Reconnected successfully");
                    await InitializeDevice();
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"āŒ Reconnection failed: {ex.Message}");
        }
    }
    
    private async Task InitializeDevice()
    {
        // Set default brightness
        await _device.SetDisplayBrightnessAsync(80);
        
        // Show welcome pattern
        var welcomePattern = PatternListCommands.PatternList[
            PatternListCommands.PatternName.GetGreenPulses];
        await _device.SetNewCustomPattern(welcomePattern.PatternLines, true, false);
    }
}

Error Handling Best Practices

public async Task<bool> SafeDeviceOperation(BusyTagDevice device)
{
    if (!device.IsConnected)
    {
        Console.WriteLine("āŒ Device not connected");
        return false;
    }
    
    try
    {
        // Use timeout for all operations
        using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
        
        // Check firmware compatibility
        if (device.FirmwareVersionFloat < 2.0)
        {
            Console.WriteLine("āš ļø Old firmware detected, enabling compatibility mode");
            await device.SetUsbMassStorageActiveAsync(true);
        }
        
        // Perform operations with proper error handling
        var success = await device.SetSolidColorAsync("blue", brightness: 100);
        if (!success)
        {
            Console.WriteLine("āš ļø LED control failed");
            return false;
        }
        
        // Wait for confirmation
        await Task.Delay(1000, cts.Token);
        
        Console.WriteLine("āœ… Operation completed successfully");
        return true;
    }
    catch (InvalidOperationException ex)
    {
        Console.WriteLine($"āš ļø Device operation failed: {ex.Message}");
        
        // Attempt reconnection
        if (!device.IsConnected)
        {
            device.Disconnect();
        }
        
        return false;
    }
    catch (TimeoutException)
    {
        Console.WriteLine("ā±ļø Operation timed out");
        return false;
    }
    catch (Exception ex)
    {
        Console.WriteLine($"šŸ’„ Unexpected error: {ex.Message}");
        return false;
    }
}

Firmware Version Handling

public async Task HandleFirmwareVersions(BusyTagDevice device)
{
    var version = device.FirmwareVersionFloat;
    Console.WriteLine($"šŸ“± Firmware version: {version}");
    
    if (version >= 2.0)
    {
        Console.WriteLine("✨ Using new serial-based file transfer");
        // Modern firmware features
        await device.SetAllowedAutoStorageScanAsync(false);
        // Serial-based file operations available
    }
    else
    {
        Console.WriteLine("āš ļø Legacy firmware - limited features");
        // Basic LED control only
    }
    
    // Version-specific features
    if (version > 0.8)
    {
        // Advanced pattern support
        var pattern = PatternListCommands.PatternList[
            PatternListCommands.PatternName.GetPolice1];
        await device.SetNewCustomPattern(pattern.PatternLines, true, false);
    }
    
    if (version > 0.7)
    {
        // Auto storage scan control
        await device.SetAllowedAutoStorageScanAsync(false);
    }
}

šŸ“‹ System Requirements

  • .NET Runtime: 8.0 or 9.0
  • Operating Systems:
    • Windows 10 version 1903 or later
    • macOS 10.15 (Catalina) or later
    • Linux with kernel 4.0 or later
  • Hardware: BusyTag device with compatible firmware (v0.7+)
  • Permissions: Serial port access rights
  • Dependencies: See table below

šŸ“š Dependencies

Package Version Purpose Platform
System.IO.Ports 9.0.7 Serial communication All
System.Text.Json 9.0.7 JSON serialization All
System.Management 8.0.0 Windows device discovery Windows only

šŸ” Troubleshooting

Common Issues

Device not found

// Enable verbose logging
manager.EnableVerboseLogging = true;

// Check all serial ports
var allPorts = manager.AllSerialPorts();
Console.WriteLine($"Available ports: {string.Join(", ", allPorts)}");

// Manual port testing
foreach (var port in allPorts)
{
    try
    {
        var testDevice = new BusyTagDevice(port);
        await testDevice.Connect();
        if (testDevice.IsConnected)
        {
            Console.WriteLine($"āœ… BusyTag found on {port}");
            testDevice.Disconnect();
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"āŒ Port {port} failed: {ex.Message}");
    }
}

Connection timeout

// Increase timeout and add retry logic
for (int attempt = 1; attempt <= 3; attempt++)
{
    try
    {
        Console.WriteLine($"Connection attempt {attempt}/3");
        await device.Connect();
        
        if (device.IsConnected)
        {
            Console.WriteLine("āœ… Connected successfully");
            break;
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"āŒ Attempt {attempt} failed: {ex.Message}");
        if (attempt < 3)
        {
            await Task.Delay(2000); // Wait before retry
        }
    }
}

File upload failures

device.FileUploadFinished += (sender, result) =>
{
    if (!result.Success)
    {
        switch (result.ErrorType)
        {
            case UploadErrorType.FilenameToolong:
                Console.WriteLine("šŸ’” Try renaming file to be shorter");
                break;
            case UploadErrorType.InsufficientStorage:
                Console.WriteLine("šŸ’” Delete some files or use device.FreeUpStorage()");
                break;
            case UploadErrorType.ConnectionLost:
                Console.WriteLine("šŸ’” Check USB cable and try reconnecting");
                break;
            default:
                Console.WriteLine($"šŸ’” Error: {result.ErrorMessage}");
                break;
        }
    }
};

šŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

Development Setup

# Clone the repository
git clone https://github.com/busy-tag/busytag-lib.git

# Restore packages
dotnet restore

# Build the project
dotnet build

# Run tests
dotnet test

šŸ“ž Support & Community

šŸ”„ Changelog

v0.3.0 (Current)

  • šŸ”§ Little project configuration update

v0.2.3

  • šŸ“š Updated documentation and examples

v0.2.2

  • ✨ Enhanced device discovery reliability
  • šŸ› Fixed memory leaks in long-running applications
  • šŸ“± Improved macOS serial port detection
  • šŸ”§ Better error handling for connection failures

v0.2.1

  • šŸš€ Initial public release
  • šŸ” Cross-platform device discovery
  • šŸ“ Complete file management system
  • šŸ’” LED color and pattern control
  • šŸ“¢ Comprehensive event system
  • šŸ“š Predefined pattern library

<div align="center">

Made with ā¤ļø by BUSY TAG SIA

Empowering developers to create amazing IoT experiences

GitHub Stars Follow on Twitter

</div>

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-maccatalyst18.0 is compatible.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.5.2 164 8/6/2025
0.5.0 442 7/24/2025
0.4.0 438 7/24/2025
0.3.1 437 7/24/2025
0.3.0 436 7/24/2025
0.2.3 365 7/21/2025
0.2.2 366 7/21/2025
0.2.1 88 7/18/2025

v0.4.0 - Cross-platform compatibility fix
           - Fixed platform detection to use runtime instead of build-time
           - Library now works correctly regardless of build platform
           - Added fallback device discovery for all platforms
           - Improved error handling and logging