zencontrol.Sdk.Tpi 2.0.0

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

Introduction

The zencontrol platform has been designed for modern buildings with integrated systems from HVAC, BMS, Security to Fans, Shades and Audio Visual systems. One of the ways in which zencontrol encourages connected buildings is the provision of a UDP based network lighting protocol for control of connected DALI luminaires from 3rd party systems. The UDP system allows integration to existing systems and networks infrastructure. The following describes the specification and usage of the network based lighting interface.

Prerequisites

  • Requires a controller connected to the local network
  • The controller requires TPI or TPI Advanced to be enabled

Wiring

  • Requires a controller connected to the local network
  • The controller requires TPI or TPI Advanced to be enabled
  • The controller will require virtual buttons enabled as well as TPI for button press events
  • The controller will have 1 or more ECG (Electronic Control Gear) connected to the DALI bus

Protocol Overview

The TPI protocol uses a 7-byte command structure over UDP port 5108:

Byte 1 Bytes 2–4 Byte 5 Byte 6 Byte 7
Control Data (HI/MID/LO) Address Command Checksum

Responses are 3 bytes: Answer Type | Answer | Checksum

Four modes of operation:

  • Mode 0 — DALI lighting commands (direct arc levels and indirect relative commands)
  • Mode 1 — Controller configuration (Inhibit, Profile changes)
  • Mode 2 — Virtual instance triggers (push buttons, absolute inputs, occupancy sensors)
  • Mode 3 — Quick queries (last heard scene, current scene, actual level)

For the full protocol specification, see Documentation/extract.txt.

Example Usage

Send a Mode 0 command (DALI lighting)

Direct or indirect commands to the controller's DALI bus.

using var client = new Client("192.168.2.82", 5108);

// Set device address 5 to arc level 200 (direct command)
var directCmd = new Mode0Command(TpiAddress.GetAddress(5), arcLevel: 200);
var reply = client.SendModeCommand(directCmd);

// Recall scene 0 on group 6 (indirect command)
var indirectCmd = new Mode0Command(
    TpiAddress.GetGroup(6), Mode0Command.IndirectCommands.Scene0);
reply = client.SendModeCommand(indirectCmd);

// Turn off all devices (broadcast)
var broadcastCmd = new Mode0Command(
    TpiAddress.GetBroadcast(), Mode0Command.IndirectCommands.Off);
reply = client.SendModeCommand(broadcastCmd);

Send a Mode 1 command (controller configuration)

using var client = new Client("192.168.2.82", 5108);

// Inhibit address 42 for 30 seconds
var inhibitCmd = Mode1Command.CreateInhibit(
    TpiAddress.GetAddress(42), durationSeconds: 30);
var reply = client.SendModeCommand(inhibitCmd);

// Release inhibit (duration = 0)
var releaseCmd = Mode1Command.CreateInhibit(
    TpiAddress.GetAddress(42), durationSeconds: 0);
reply = client.SendModeCommand(releaseCmd);

// Request a profile change
var profileCmd = Mode1Command.CreateProfile(profileNumber: 5);
reply = client.SendModeCommand(profileCmd);

// Return to scheduled profile
var scheduledCmd = Mode1Command.CreateProfile(profileNumber: 0);
reply = client.SendModeCommand(scheduledCmd);

Send a Mode 2 command (virtual instances)

Simulate sensors, push buttons or absolute inputs as a virtual device. This can trigger sequences or other commands configured on the controller.

using var client = new Client("192.168.2.82", 5108);

// Short press on virtual instance 0 (Application Controller)
var shortPress = new Mode2Command(
    instanceAddress: 0x00, Mode2Command.Command.PushButtonShortPress);
var reply = client.SendModeCommand(shortPress);

// Long press on virtual instance 10 (Room Controller instance 0)
var longPress = new Mode2Command(
    instanceAddress: 0x0A, Mode2Command.Command.PushButtonLongPressAction);
reply = client.SendModeCommand(longPress);

// Set occupancy sensor to occupied
var occupied = new Mode2Command(
    instanceAddress: 0x02, Mode2Command.Command.OccupancyOccupied);
reply = client.SendModeCommand(occupied);

// Set absolute input to on
var inputOn = new Mode2Command(
    instanceAddress: 0x03, Mode2Command.Command.AbsoluteInputOn);
reply = client.SendModeCommand(inputOn);

Send a Mode 3 command (quick queries)

Device-specific queries for status information.

using var client = new Client("192.168.2.82", 5108);

var addr = TpiAddress.GetAddress(7);

// Query the last heard scene
var lastHeardCmd = new Mode3Command(addr, Mode3Command.Command.LastHeardScene);
var reply = client.SendModeCommand(lastHeardCmd);
Console.WriteLine($"Last Heard Scene reply: {reply}");

// Query the current scene
var currentSceneCmd = new Mode3Command(addr, Mode3Command.Command.CurrentScene);
reply = client.SendModeCommand(currentSceneCmd);
Console.WriteLine($"Current Scene reply: {reply}");

// Query the actual light level (returns level or 255 = MIXED)
var levelCmd = new Mode3Command(addr, Mode3Command.Command.QueryActualLevel);
reply = client.SendModeCommand(levelCmd);
if (reply.Answer == TpiReply.AnswerType.ReplyAnswer)
{
    byte level = reply.AnswerByte ?? 255;
    Console.WriteLine(
        $"Actual Level: {(level == 255 ? "MIXED" : level.ToString())}");
}

High-performance usage (keepOpen)

For sending many commands in sequence, use keepOpen: true to avoid open/close overhead on each call:

using var client = new Client("192.168.2.82", 5108, keepOpen: true);

for (int i = 0; i < 100; i++)
{
    var cmd = new Mode0Command(
        TpiAddress.GetAddress(7), arcLevel: (byte)(i % 255));
    client.SendModeCommand(cmd);
    Thread.Sleep(50);
}

Custom UDP transport

For fine-grained control over timeouts and networking:

var udp = new Udp("192.168.2.82", 5108);
udp.ReceiveTimeout = TimeSpan.FromMilliseconds(500);

using var client = new Client(udp, keepOpen: true);
udp.Open();

var reply = client.SendModeCommand(
    new Mode0Command(TpiAddress.GetAddress(0), arcLevel: 128));

udp.Close();

Understanding replies

var reply = client.SendModeCommand(...);

switch (reply.Answer)
{
    case TpiReply.AnswerType.ReplyOk:
        Console.WriteLine("Command accepted, no data returned");
        break;
    case TpiReply.AnswerType.ReplyAnswer:
        Console.WriteLine($"Command accepted, data: {reply.AnswerByte}");
        break;
    case TpiReply.AnswerType.ReplyNoAnswer:
        Console.WriteLine("No response from target device");
        break;
    case TpiReply.AnswerType.ReplyError:
        string error = reply.AnswerByte switch
        {
            1 => "Invalid Command",
            2 => "Short Circuit",
            _ => $"Unknown error: {reply.AnswerByte}"
        };
        Console.WriteLine($"Error: {error}");
        break;
    case null:
        Console.WriteLine("No reply received (timeout or connection issue)");
        break;
}

Address Helpers

// Individual devices (0–63)
TpiAddress.GetAddress(5);        // device address 5

// Groups (0–15)
TpiAddress.GetGroup(0);          // group 0
TpiAddress.GetGroup(12);         // group 12

// Broadcast (all devices)
TpiAddress.GetBroadcast();        // address 127

// From predefined enum
TpiAddress.GetAddressFromEnum(TpiAddress.Address.Address42);
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  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 was computed.  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 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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.
  • .NETStandard 2.1

    • No dependencies.

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
2.0.0 166 6/13/2026
1.0.0 223 10/12/2025

Version 1.x: Initial release
           Version 2.x: Updates to the SDK and made minor breaking changes that include spelling mistakes and functionality.