framework-dotnet
framework-dotnet (FrameworkDotnet) is a .NET library for talking to Framework laptop and desktop embedded controller APIs through the native framework-system Rust FFI layer. Developed by community member TekuSP, it provides a managed API for detecting EC drivers, reading platform/product information, opening EC connections, reading firmware/power/fan/thermal snapshots, and controlling fan RPM and duty — all without exposing unsafe code to callers1.
The library wraps Framework's official framework-system Rust crate through an FFI extensions layer, using csbindgen to generate low-level C# interop bindings and UnitsNet for unit-bearing values. A hand-written API surface sits on top so normal .NET callers never need to work with pointers or unsafe code1.
Architecture
The project uses the following layered architecture1:
| Layer | Technology | Purpose |
|---|---|---|
| Native hardware access | framework-system (Rust, nested submodule) | Chromium EC interface |
| FFI extensions | framework-system-ffi-extensions (Rust submodule) | Native .NET FFI layer |
| Interop bindings | csbindgen | Generates C# bindings from Rust |
| Managed API | FrameworkDotnet (C#) | Hand-written, safe public API |
| Unit types | UnitsNet | Temperature, RotationalSpeed, Ratio, ElectricPotential, etc. |
Public API
The public API is centered around two main entry points1:
FrameworkSystem
- Detects platform and product information
- Checks driver support (
IsDriverSupported(...)) - Opens EC connections (
OpenDefaultEc())
IFrameworkEcConnection
- Reads firmware, power, fan capability, and thermal snapshots
- Sends fan control commands (set RPM, set duty, restore automatic control)
Snapshot Types
| Type | Data |
|---|---|
FrameworkEcFlashSnapshot | Current image, RO/RW firmware versions |
FrameworkPowerSnapshot | Power state, battery info |
FrameworkBatterySnapshot | Battery voltage, rate, capacity, state |
FrameworkFanCapabilitiesSnapshot | Fan count and capabilities |
FrameworkThermalSnapshot | Temperature sensors (0–7), fans (0–3), sensor count, sensor names |
FrameworkFanSnapshot | Fan speed, fan state |
FrameworkTemperatureSnapshot | Temperature, state |
FrameworkInputModulePositionSnapshot | Framework Laptop 16 input module physical position |
The public snapshot API intentionally stays aligned with the native Rust fixed-slot structs (e.g., FrameworkThermalSnapshot.Temperature_0 through Temperature_7, Fan_0 through Fan_3). Count-aware enumerable properties (ReportedBatteries, ReportedTemperatures, ReportedFans) return only entries reported by the corresponding count field1.
Units
Public unit-bearing values use UnitsNet1:
| Quantity | UnitsNet Type |
|---|---|
| Temperatures | UnitsNet.Temperature |
| Fan speeds | UnitsNet.RotationalSpeed |
| Fan duty | UnitsNet.Ratio |
| Battery voltage | UnitsNet.ElectricPotential |
| Battery rate | UnitsNet.ElectricCurrent |
| Battery capacities | UnitsNet.ElectricCharge |
Exception Types
Public API methods throw specific managed exception types rather than requiring callers to inspect status codes1:
FrameworkEcResponseExceptionand derived EC response exceptionsFrameworkInvalidFanIndexExceptionFrameworkBatteryStateExceptionand derived battery state exceptionsFrameworkTemperatureStateExceptionand derived temperature state exceptionsFrameworkFanStateExceptionand derived fan state exceptions
When native context is available, exceptions attempt to include native descriptions and device error messages from the Rust layer (best-effort).
Installation
NuGet
<PackageReference Include="FrameworkDotnet" Version="0.8.213" />
UnitsNet is already a dependency of the library, so callers do not need to add separate unit types manually1.
Building from Source
git clone https://github.com/TekuSP/framework-dotnet.git
cd framework-dotnet
dotnet build framework-dotnet/framework-dotnet.csproj
If generated bindings and native assets are already supplied externally, the Rust build step can be skipped:
dotnet build framework-dotnet/framework-dotnet.csproj /p:SkipRustBuild=true
Example
using FrameworkDotnet;
using FrameworkDotnet.Exceptions;
var frameworkSystem = new FrameworkSystem();
try
{
using var ec = frameworkSystem.OpenDefaultEc();
var thermal = ec.GetThermalSnapshot();
foreach (var temperature in thermal.ReportedTemperatures)
{
Console.WriteLine($"Temperature: {temperature.Temperature.DegreesCelsius}C ({temperature.State})");
}
foreach (var fan in thermal.ReportedFans)
{
Console.WriteLine($"Fan: {fan.Speed.RevolutionsPerMinute} RPM ({fan.FanState})");
}
}
catch (FrameworkEcResponseException ex)
{
Console.WriteLine($"EC response failure: {ex.Detail}");
Console.WriteLine($"Description: {ex.Description}");
}
Native Loading
| Platform | Native Library | Notes |
|---|---|---|
| Windows x64/arm64 | framework_lib_ffi.dll | Expected beside the application or in the packaged runtime-specific native layout |
| Linux x64/arm64 | libframework_lib_ffi.so | Expected beside the application or in the packaged runtime-specific native layout |
On both platforms, successful native loading alone does not guarantee that EC operations will succeed — permissions, kernel support, driver availability, and installed BIOS/firmware versions still matter. IsDriverSupported(...) should be treated as a runtime check1.
Technical Details
| Detail | Value |
|---|---|
| Language | C# (.NET 10.0) |
| License | MIT |
| Stars | 4 |
| Commits | 130+ |
| NuGet package | FrameworkDotnet v0.8.213 (source v0.9.0) |
| FFI layer | framework-system-ffi-extensions (Rust) with csbindgen |
| Unit library | UnitsNet |
| Platforms | Windows x64/arm64 (DLL), Linux x64/arm64 (.so) |
| Affiliation | Not affiliated with Framework Computer Inc. |
Related Projects
| Project | Description |
|---|---|
| Framework Tool TUI | Terminal dashboard using framework_lib (Rust) |
| Framework Control | GUI tool using framework_tool CLI |
| fw-fanctrl | Fan control daemon using ectool for EC access |
| FanFrame | Per-fan control GUI for Framework Desktop using framework_tool |