plugins/shiny-client/skills/shiny-obd/SKILL.md
Generate code using Shiny.Obd, an OBD-II vehicle communication library for .NET with command-object pattern, adapter auto-detection, and BLE transport
npx skillsauth add shinyorg/skills shiny-obdInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are an expert in Shiny.Obd, a .NET library for communicating with vehicles through OBD-II adapters. It uses a command-object pattern with generic return types, pluggable transports (BLE first), and adapter auto-detection for ELM327 and OBDLink (STN) adapters.
Invoke this skill when the user wants to:
Shiny.Obd, Shiny.Obd.Ble, Shiny.Obd.CommandsShiny.Obd (core), Shiny.Obd.Ble (BLE transport)net10.0 (core), net10.0 (BLE)Every OBD command implements this. T is the parsed result type.
public interface IObdCommand<T>
{
string RawCommand { get; }
T Parse(byte[] data);
}
Validates mode+PID response header, strips it, and delegates to ParseData.
public abstract class ObdCommand<T> : IObdCommand<T>
{
protected ObdCommand(byte mode, byte pid);
public byte Mode { get; }
public byte Pid { get; }
public virtual string RawCommand { get; } // "{Mode:X2}{Pid:X2}"
protected abstract T ParseData(byte[] data); // data after header
}
public interface IObdConnection : IAsyncDisposable
{
bool IsConnected { get; }
Task Connect(CancellationToken ct = default);
Task Disconnect();
Task<T> Execute<T>(IObdCommand<T> command, CancellationToken ct = default);
Task<string> SendRaw(string command, CancellationToken ct = default);
}
public interface IObdTransport : IAsyncDisposable
{
bool IsConnected { get; }
Task Connect(CancellationToken ct = default);
Task Disconnect();
Task<string> Send(string command, CancellationToken ct = default);
}
public interface IObdDeviceScanner
{
Task Scan(Action<ObdDiscoveredDevice> onDeviceFound, CancellationToken ct = default);
}
Cancel the token to stop scanning. Each discovered device invokes the callback.
public class ObdDiscoveredDevice
{
public string Name { get; } // e.g. "OBDLink MX+"
public string Id { get; } // unique identifier (BLE UUID, IP, etc.)
public object NativeDevice { get; } // IPeripheral for BLE, IPEndPoint for WiFi, etc.
}
Two constructors:
ObdConnection(IObdTransport transport) — auto-detects adapter via ATIObdConnection(IObdTransport transport, IObdAdapterProfile profile) — uses explicit profile, skips detectionProperties:
DetectedAdapter — ObdAdapterInfo? with RawIdentifier (string) and Type (ObdAdapterType enum: Unknown, Elm327, ObdLink). Null when explicit profile used.Handles:
public interface IObdAdapterProfile
{
string Name { get; }
Task Initialize(IObdConnection connection, CancellationToken ct = default);
}
Built-in profiles:
Elm327AdapterProfile — ATZ, ATE0, ATL0, ATS1, ATH0, ATSP0ObdLinkAdapterProfile — extends Elm327 with STFAC, ATCAF1| Property | Type | Command | Return | Parse Formula |
|----------|------|---------|--------|---------------|
| VehicleSpeed | VehicleSpeedCommand | 010D | int (km/h) | A |
| EngineRpm | EngineRpmCommand | 010C | int (RPM) | ((A*256)+B)/4 |
| CoolantTemperature | CoolantTemperatureCommand | 0105 | int (°C) | A-40 |
| ThrottlePosition | ThrottlePositionCommand | 0111 | double (%) | (A*100)/255 |
| FuelLevel | FuelLevelCommand | 012F | double (%) | (A*100)/255 |
| CalculatedEngineLoad | CalculatedEngineLoadCommand | 0104 | double (%) | (A*100)/255 |
| IntakeAirTemperature | IntakeAirTemperatureCommand | 010F | int (°C) | A-40 |
| RuntimeSinceStart | RuntimeSinceStartCommand | 011F | TimeSpan | (A*256)+B seconds |
| Vin | VinCommand | 0902 | string | skip count byte, ASCII decode |
public class BleObdConfiguration
{
public string ServiceUuid { get; set; } = "FFF0";
public string ReadCharacteristicUuid { get; set; } = "FFF1";
public string WriteCharacteristicUuid { get; set; } = "FFF2";
public string? DeviceNameFilter { get; set; }
public TimeSpan CommandTimeout { get; set; } = TimeSpan.FromSeconds(10);
}
Three constructors:
BleObdTransport(IBleManager bleManager, BleObdConfiguration config) — scans for adapterBleObdTransport(IPeripheral peripheral, BleObdConfiguration config) — uses pre-discovered peripheralBleObdTransport(ObdDiscoveredDevice device, BleObdConfiguration config) — uses device from scannerUses Shiny.BluetoothLE v4 APIs:
ConnectAsync for task-based connectionNotifyCharacteristic for RX notificationsWriteCharacteristicAsync for TX writes> prompt, returns complete responsepublic class BarometricPressureCommand : ObdCommand<int>
{
public BarometricPressureCommand() : base(0x01, 0x33) { }
protected override int ParseData(byte[] data) => data[0];
}
public class ManufacturerCommand : IObdCommand<string>
{
public string RawCommand => "2101";
public string Parse(byte[] data) => BitConverter.ToString(data);
}
var transport = new BleObdTransport(bleManager, new BleObdConfiguration
{
DeviceNameFilter = "OBDLink"
});
var connection = new ObdConnection(transport);
await connection.Connect();
var speed = await connection.Execute(StandardCommands.VehicleSpeed);
var rpm = await connection.Execute(StandardCommands.EngineRpm);
var vin = await connection.Execute(StandardCommands.Vin);
var scanner = new BleObdDeviceScanner(bleManager);
var cts = new CancellationTokenSource();
ObdDiscoveredDevice? selected = null;
await scanner.Scan(device =>
{
selected = device;
cts.Cancel(); // stop after first device
}, cts.Token);
var transport = new BleObdTransport(selected!, new BleObdConfiguration());
var connection = new ObdConnection(transport);
await connection.Connect();
// In MauiProgram.cs
builder.Services.AddBluetoothLE(); // Shiny BLE v4 — namespace: Shiny
builder.Services.AddShinyObdBluetoothLE(new BleObdConfiguration
{
DeviceNameFilter = "OBD"
});
AddShinyObdBluetoothLE registers BleObdConfiguration and IObdDeviceScanner (BleObdDeviceScanner). Call AddBluetoothLE() separately for platform BLE support.
var connection = new ObdConnection(transport, new ObdLinkAdapterProfile());
await connection.Connect();
public class MyAdapterProfile : IObdAdapterProfile
{
public string Name => "MyAdapter";
public async Task Initialize(IObdConnection connection, CancellationToken ct = default)
{
await connection.SendRaw("ATZ", ct);
await Task.Delay(500, ct);
await connection.SendRaw("ATE0", ct);
await connection.SendRaw("ATSP6", ct); // force CAN 11-bit 500kbaud
}
}
public class WifiObdTransport : IObdTransport
{
public bool IsConnected { get; private set; }
public async Task Connect(CancellationToken ct = default) { /* TCP connect */ }
public Task Disconnect() { /* close socket */ }
public async Task<string> Send(string command, CancellationToken ct = default)
{
// Write command, read until '>' prompt, return response without '>'
}
public ValueTask DisposeAsync() { /* cleanup */ }
}
Async suffix (e.g. Connect, Execute, SendRaw).ObdCommand<T>.ParseData receives bytes AFTER the 2-byte mode+PID header is stripped.IObdCommand<T>.Parse receives ALL response bytes including mode+PID header.ObdConnection appends \r to all commands sent via SendRaw before passing to transport.SemaphoreSlim to serialize commands (one at a time)."41 0D 50") and multi-frame CAN ("0: 49 02 01 57 42\r1: 41 30...") formats.BleObdDeviceScanner deduplicates by peripheral UUID — each device is reported once.builder.Services.AddBluetoothLE() (namespace Shiny, no UseShiny needed in v4), then call builder.Services.AddShinyObdBluetoothLE() to register OBD BLE services.AddShinyObdBluetoothLE registers BleObdConfiguration and IObdDeviceScanner (BleObdDeviceScanner).samples/Sample.Maui/ with scan → select → dashboard flow.development
Guide for generating code that uses Shiny.Data.Sync for reliable, background-capable bidirectional JSON sync over HTTP on iOS, Android, Windows, Linux, macOS, and Blazor WASM
devops
Guide for implementing push notifications in .NET MAUI apps using Shiny.Push (native FCM/APNs) and Shiny.Push.AzureNotificationHubs
tools
Cross-platform local notification management for .NET MAUI apps using Shiny, supporting scheduled, repeating, and geofence-triggered notifications with channels, badges, and interactive actions.
tools
GPS tracking, geofence monitoring, and motion activity recognition for .NET MAUI, iOS, and Android using Shiny.Locations