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
<PackageReference Include="zencontrol.Sdk.Tpi" Version="2.0.0" />
<PackageVersion Include="zencontrol.Sdk.Tpi" Version="2.0.0" />
<PackageReference Include="zencontrol.Sdk.Tpi" />
paket add zencontrol.Sdk.Tpi --version 2.0.0
#r "nuget: zencontrol.Sdk.Tpi, 2.0.0"
#:package zencontrol.Sdk.Tpi@2.0.0
#addin nuget:?package=zencontrol.Sdk.Tpi&version=2.0.0
#tool nuget:?package=zencontrol.Sdk.Tpi&version=2.0.0
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 | Versions 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. |
-
.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 1.x: Initial release
Version 2.x: Updates to the SDK and made minor breaking changes that include spelling mistakes and functionality.