DuckTyping.Ikea.Dirigera 1.2.0

Prefix Reserved
dotnet add package DuckTyping.Ikea.Dirigera --version 1.2.0
                    
NuGet\Install-Package DuckTyping.Ikea.Dirigera -Version 1.2.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="DuckTyping.Ikea.Dirigera" Version="1.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DuckTyping.Ikea.Dirigera" Version="1.2.0" />
                    
Directory.Packages.props
<PackageReference Include="DuckTyping.Ikea.Dirigera" />
                    
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 DuckTyping.Ikea.Dirigera --version 1.2.0
                    
#r "nuget: DuckTyping.Ikea.Dirigera, 1.2.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 DuckTyping.Ikea.Dirigera@1.2.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=DuckTyping.Ikea.Dirigera&version=1.2.0
                    
Install as a Cake Addin
#tool nuget:?package=DuckTyping.Ikea.Dirigera&version=1.2.0
                    
Install as a Cake Tool

IKEA DIRIGERA Hub client library

❗ This is an unofficial library and is not affiliated with or endorsed by IKEA.

An unofficial client library for the IKEA DIRIGERA smart home hub.
Connects to the API via the local network, handles OAuth2 PKCE pairing, allows for controlling lights, and streams real-time device readings over WebSocket.

Installation

Install via the terminal or the "Manage NuGet Packages" in your IDE of choice.

dotnet add package DuckTyping.Ikea.Dirigera

First-time pairing

The hub uses an OAuth2 PKCE flow that requires a one-time physical button press on the DIRIGERA hub. When you start the application for the first time (or after a token expires) you will see:

Press the Action button on the DIRIGERA hub (short press). Waiting 60s...

Press the small button on the bottom of the hub within 60 seconds. The token is then saved to disk and reused on all subsequent starts (no button press needed until the token expires).

The default token file location is ~/.dirigera-tokens.json. You can override this with StorePath in the settings (see below).

Setup with dependency injection

Register the client in your Program.cs or wherever you configure your IServiceCollection:

builder.Services.AddDirigeraClient(settings =>
{
    settings.Ip = "192.168.1.100"; // local IP address of your DIRIGERA hub
});

To find the hub IP, check your router's DHCP leases or look for a device named DIRIGERA on your network.

Optional settings

builder.Services.AddDirigeraClient(settings =>
{
    settings.Ip = "192.168.1.100";
    settings.StorePath = "/var/myapp/dirigera-tokens.json"; // custom token file location
});
Setting Default Description
Ip (required) Local IP address of the DIRIGERA hub
StorePath ~/.dirigera-tokens.json Path where the OAuth token is persisted

Listening for sensor readings

Inject IDirigeraClient and call ListenAsync. It returns an IAsyncEnumerable<SensorReading> that you consume with await foreach. The connection and pairing happen automatically on the first call.

await foreach (var reading in client.ListenAsync(cancellationToken))
{
    switch (reading)
    {
        case EnvironmentReading env:
            Console.WriteLine($"{env.Name}  Temp={env.CurrentTemperature}°C  RH={env.CurrentRH}%  CO2={env.CurrentCO2}ppm");
            break;

        case DoorSensorReading door:
            Console.WriteLine($"{door.Name} is {(door.IsOpen ? "open" : "closed")}");
            break;

        case MotionSensorReading motion:
            Console.WriteLine($"{motion.Name}: {(motion.IsDetected ? "motion detected" : "clear")}");
            break;

        case WaterSensorReading water:
            Console.WriteLine($"{water.Name}: {(water.IsLeaking ? "LEAK DETECTED" : "dry")}");
            break;
    }
}

ListenAsync reconnects automatically with exponential backoff if the hub becomes unreachable, and stops cleanly when the CancellationToken is cancelled.

Reading types

All readings inherit from SensorReading which exposes:

Property Type Description
DeviceId string Unique device identifier from the hub
Name string? Custom name set in the IKEA Home app
Time DateTimeOffset Timestamp of the reading

Pattern match on the concrete type to access sensor-specific properties:

DoorSensorReading

Property Type Description
IsOpen bool true when the door or window is open
Battery int? Battery level in percent

MotionSensorReading

Property Type Description
IsDetected bool true when motion or presence is detected
Battery int? Battery level in percent

WaterSensorReading

Property Type Description
IsLeaking bool true when water or moisture is detected
Battery int? Battery level in percent

EnvironmentReading

Property Type Description
CurrentTemperature double? Temperature in °C
CurrentRH int? Relative humidity in %
CurrentCO2 int? CO₂ concentration in ppm
CurrentPM25 double? Particulate matter (PM2.5) in µg/m³
Battery int? Battery level in percent

Controlling lights

Discover device IDs

Before controlling a light you need its device ID. Call GetDevicesAsync to list everything registered on the hub:

var devices = await client.GetDevicesAsync();
foreach (var device in devices)
    Console.WriteLine($"{device.Id}  {device.Name}  ({device.DeviceType})");

Copy the ID of the light you want to control (e.g. 2d3d4c0a-4624-45d1-bb3f-4f94b569ce48_11) and use it in the calls below.

On / Off / Toggle

await client.TurnOnAsync(lightId);
await client.TurnOffAsync(lightId);
await client.ToggleAsync(lightId);   // reads current state, then flips it

To turn a light on and set brightness in a single command, pass the brightness level as the second argument. Pass null to turn on without changing the current brightness:

await client.TurnOnAsync(lightId, brightness: 75);   // turn on at 75%
await client.TurnOnAsync(lightId, brightness: null);  // turn on, keep current brightness

Dimming

Brightness is a value from 1 (minimum) to 100 (maximum).

await client.DimAsync(lightId, brightness: 50);

Color temperature (white spectrum)

Color temperature is in Kelvin. Typical range for IKEA bulbs is 2200 K (warm white) to 4000 K (cool white).

await client.SetColorTemperatureAsync(lightId, kelvin: 2700);

Color (RGB)

Pass standard RGB values (0–255 per channel). The library converts them to the hue/saturation format the hub expects.

await client.SetColorAsync(lightId, r: 255, g: 120, b: 0);  // warm orange

To switch back to white after using color mode, call SetColorTemperatureAsync.

Reading light state

All light control methods return a LightStatus reflecting the target state of the command — not a live re-poll of the hub. The physical device may still be transitioning (e.g. dimming or changing color) when the method returns. To read the actual current state without issuing a command, use GetLightStatusAsync:

var status = await client.GetLightStatusAsync(lightId);
Console.WriteLine($"On={status.IsOn}  Brightness={status.Brightness}%  Mode={status.ColorMode}");

LightStatus properties

Property Type Description
DeviceId string Unique device identifier
Name string? Custom name set in the IKEA Home app
IsOn bool true if the light is currently on
Brightness int? Current brightness 1–100, or null if not dimmable
ColorMode LightColorMode Color, Temperature, or Unknown
ColorTemperature int? Color temperature in Kelvin (populated when ColorMode is Temperature)
Hue double? Hue in degrees 0–360 (populated when ColorMode is Color)
Saturation double? Saturation 0–100 (populated when ColorMode is Color)
IsReachable bool true if the hub can currently reach the device

Exceptions

Exception When thrown
DirigeraDeviceNotFoundException The device ID does not exist on the hub
DirigeraValidationException A parameter was out of range (e.g. brightness outside 1–100)
DirigeraException Any other hub error

Controlling outlets

On / Off / Toggle

await client.TurnOutletOnAsync(outletId);
await client.TurnOutletOffAsync(outletId);
await client.ToggleOutletAsync(outletId);   // reads current state, then flips it

Reading outlet state

All outlet methods return an OutletStatus. You can also read it without making any change:

var status = await client.GetOutletStatusAsync(outletId);
Console.WriteLine($"On={status.IsOn}  Reachable={status.IsReachable}");

OutletStatus properties

Property Type Description
DeviceId string Unique device identifier
Name string? Custom name set in the IKEA Home app
IsOn bool true if the outlet is currently on
IsReachable bool true if the hub can currently reach the device

Controlling blinds

Open / Close / Set position

await client.OpenBlindAsync(blindId);             // sets position to 0 (fully open)
await client.CloseBlindAsync(blindId);            // sets position to 100 (fully closed)
await client.SetBlindPositionAsync(blindId, 50);  // 0 = fully open, 100 = fully closed

Reading blind state

All blind methods return a BlindStatus. You can also read it without making any change:

var status = await client.GetBlindStatusAsync(blindId);
Console.WriteLine($"Current={status.CurrentLevel}  Target={status.TargetLevel}");

BlindStatus properties

Property Type Description
DeviceId string Unique device identifier
Name string? Custom name set in the IKEA Home app
CurrentLevel int Current position: 0 = fully open, 100 = fully closed
TargetLevel int? Position the motor is moving towards, or null if idle
IsReachable bool true if the hub can currently reach the device

Examples

Below are some examples of how to use the library.
Note; these are just example snippets and not full applications ready for production

Example: .NET Console application with lights control

A console application that lists all devices and exposes light control as CLI commands.

using DuckTyping.Ikea.Dirigera;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

var host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        services.AddDirigeraClient(settings =>
        {
            settings.Ip = "192.168.1.100";
        });
    })
    .Build();

var client = host.Services.GetRequiredService<IDirigeraClient>();

switch (args)
{
    case ["devices"]:
        var devices = await client.GetDevicesAsync();
        foreach (var d in devices)
            Console.WriteLine($"{d.Id}  {d.Name,-30}  {d.DeviceType}");
        break;

    case ["lights", "on", var id]:
        var on = await client.TurnOnAsync(id);
        Console.WriteLine($"{on.Name} is now on at {on.Brightness}%");
        break;

    case ["lights", "on", var id, var level] when int.TryParse(level, out var onBrightness):
        var onDimmed = await client.TurnOnAsync(id, onBrightness);
        Console.WriteLine($"{onDimmed.Name} is now on at {onDimmed.Brightness}%");
        break;

    case ["lights", "off", var id]:
        var off = await client.TurnOffAsync(id);
        Console.WriteLine($"{off.Name} is now off");
        break;

    case ["lights", "toggle", var id]:
        var toggled = await client.ToggleAsync(id);
        Console.WriteLine($"{toggled.Name} is now {(toggled.IsOn ? "on" : "off")}");
        break;

    case ["lights", "dim", var id, var level] when int.TryParse(level, out var brightness):
        var dimmed = await client.DimAsync(id, brightness);
        Console.WriteLine($"{dimmed.Name} brightness set to {dimmed.Brightness}%");
        break;

    case ["lights", "temp", var id, var kelvin] when int.TryParse(kelvin, out var k):
        var temp = await client.SetColorTemperatureAsync(id, k);
        Console.WriteLine($"{temp.Name} color temperature set to {temp.ColorTemperature}K");
        break;

    case ["lights", "color", var id, var r, var g, var b]
        when int.TryParse(r, out var rv) && int.TryParse(g, out var gv) && int.TryParse(b, out var bv):
        var colored = await client.SetColorAsync(id, rv, gv, bv);
        Console.WriteLine($"{colored.Name} color set  Hue={colored.Hue}  Sat={colored.Saturation}%");
        break;

    case ["lights", "status", var id]:
        var status = await client.GetLightStatusAsync(id);
        Console.WriteLine($"{status.Name}  On={status.IsOn}  Brightness={status.Brightness}%  Mode={status.ColorMode}");
        break;

    default:
        Console.WriteLine("""
            Usage:
              devices
              lights status  <id>
              lights on      <id>
              lights on      <id> <1-100>
              lights off     <id>
              lights toggle  <id>
              lights dim     <id> <1-100>
              lights temp    <id> <kelvin>
              lights color   <id> <r> <g> <b>
            """);
        break;
}

Run it from the terminal:

dotnet run -- devices
dotnet run -- lights on    2d3d4c0a-4624-45d1-bb3f-4f94b569ce48_11
dotnet run -- lights on    2d3d4c0a-4624-45d1-bb3f-4f94b569ce48_11 75
dotnet run -- lights dim   2d3d4c0a-4624-45d1-bb3f-4f94b569ce48_11 40
dotnet run -- lights temp  2d3d4c0a-4624-45d1-bb3f-4f94b569ce48_11 2700
dotnet run -- lights color 2d3d4c0a-4624-45d1-bb3f-4f94b569ce48_11 255 120 0

Example: .NET Worker Service

Worker service that listens to events from the DIRIGERA Hub, the worker logs information based on the type of sensor reading that is received.

Program.cs

using DuckTyping.Ikea.Dirigera;

var builder = Host.CreateApplicationBuilder(args);

builder.Services.AddDirigeraClient(settings =>
{
    settings.Ip = builder.Configuration.GetRequiredSection("Dirigera:Ip").Value!;
});

builder.Services.AddHostedService<DirigeraWorker>();

var host = builder.Build();
host.Run();

appsettings.json

{
  "Dirigera": {
    "Ip": "192.168.1.100"
  }
}

DirigeraWorker.cs

public sealed class DirigeraWorker(IDirigeraClient client, ILogger<DirigeraWorker> logger) : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        logger.LogInformation("Connecting to DIRIGERA hub...");

        await foreach (var reading in client.ListenAsync(stoppingToken))
        {
            switch (reading)
            {
                case EnvironmentReading env:
                    logger.LogInformation(
                        "[Environment] {Name}  Temp={Temp}  RH={RH}%  CO2={CO2}ppm",
                        env.Name, env.CurrentTemperature, env.CurrentRH, env.CurrentCO2);
                    break;

                case DoorSensorReading door:
                    logger.LogInformation(
                        "[Door] {Name}  {State}  Bat={Battery}%",
                        door.Name, door.IsOpen ? "Open" : "Closed", door.Battery);
                    break;

                case MotionSensorReading motion:
                    logger.LogInformation(
                        "[Motion] {Name}  {State}  Bat={Battery}%",
                        motion.Name, motion.IsDetected ? "Motion" : "Clear", motion.Battery);
                    break;
            }
        }

        logger.LogInformation("Event loop stopped.");
    }
}

Example: Console application

A console application that listens to events from the DIRIGERA Hub and prints info to the console

using DuckTyping.Ikea.Dirigera;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

var host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        services.AddDirigeraClient(settings =>
        {
            settings.Ip = "192.168.1.100";
        });
    })
    .Build();

var client = host.Services.GetRequiredService<IDirigeraClient>();

using var cts = new CancellationTokenSource();
Console.CancelKeyPress += (_, e) =>
{
    e.Cancel = true;
    cts.Cancel();
};

await foreach (var reading in client.ListenAsync(cts.Token))
{
    Console.WriteLine($"{reading.Time:HH:mm:ss}  {reading.Name ?? reading.DeviceId}  {reading.GetType().Name}");
}

Notes

  • The hub uses a self-signed TLS certificate on port 8443. The library accepts this certificate automatically. DO NOT expose this port to the internet.
Product Compatible and additional computed target framework versions.
.NET 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-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  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
1.2.0 90 3/16/2026
1.1.0 85 3/15/2026
1.0.0 91 3/14/2026

- Changed the library status output to use the stderr/exceptions and not stdout to not pollute the output
- Fixed bug in the connect to Hub method
- Fixed bug in patch request to hub
- Fixed issue with when patching a light, the new status is incorrect
- Updated the lights "on" command to include -l (dim level of the light)