TMRemote SDK - Build plugins, integrate via REST API, or extend with custom devices.
Triggers initiate workflows ·
Actions execute operations ·
Events notify of changes
REST API
Control TMRemote via HTTP from any device on your network.
Endpoints
| Endpoint | Method | Description |
/api/status | GET | Get current system status |
/api/profiles | GET | List all monitor profiles |
/api/profiles/{name}/load | POST | Load a monitor profile |
/api/scenarios | GET | List all scenarios |
/api/scenarios/{name}/run | POST | Execute a scenario |
/api/matrix/status | GET | Get matrix routing status |
/api/matrix/route | POST | Set video routing (input, output) |
/api/displays | GET | List all displays with info |
/api/displays/topology | POST | Set display topology (extend/clone/internal/external) |
/api/plugin/* | * | Plugin-registered routes |
Example: Run a Scenario
curl -X POST http://localhost:8080/api/scenarios/Gaming/run
Example: Route Matrix
curl -X POST http://localhost:8080/api/matrix/route \
-H "Content-Type: application/json" \
-d '{"input": 1, "output": 3}'
Plugin Development
Create plugins to add new devices, automation, and API endpoints.
Quick Start
- Create a class implementing
IDevicePlugin
- Add your plugin DLL to the Plugins folder
- Enable via Settings → Plugins tab
Minimal Plugin
public class MyPlugin : IDevicePlugin
{
public string PluginId => "my-plugin";
public string DisplayName => "My Custom Plugin";
public Version Version => new Version(1, 0, 0);
public string Description => "My plugin description";
public string Author => "Your Name";
public int MinHostVersion => 1;
public bool IsEnabled { get; set; }
public IEnumerable<IDevice> ConnectedDevices => Array.Empty<IDevice>();
public Task InitializeAsync(IPluginHost host)
{
// Register custom API route (path, HTTP method, handler)
host.RegisterApiRoute("myplugin/status", "GET", async req =>
new ApiResponse { Success = true, Data = "OK" });
// Use logger
host.Logger.Info("Plugin initialized");
return Task.CompletedTask;
}
// Required interface members
public Task ShutdownAsync(PluginCloseReason reason) => Task.CompletedTask;
public void ShowConfigDialog(IntPtr parentWindow) { }
public Task<IEnumerable<IDiscoveredDevice>> DiscoverDevicesAsync()
=> Task.FromResult<IEnumerable<IDiscoveredDevice>>(Array.Empty<IDiscoveredDevice>());
public Task<IDevice> ConnectDeviceAsync(DeviceConfig config) => Task.FromResult<IDevice>(null);
public IEnumerable<IActionDefinition> GetActionDefinitions() => Array.Empty<IActionDefinition>();
public UserControl CreateSettingsPanel() => null;
public PluginRequirements GetRequirements() => new PluginRequirements();
public void Dispose() { }
}
Key IPluginHost Services
| Property/Method | Description |
host.Logger | IPluginLogger for diagnostic output |
host.GetPluginDataPath(id) | Get plugin's data storage folder |
host.LoadSettings<T>(id) | Load plugin settings from storage |
host.SaveSettings<T>(id, settings) | Save plugin settings to storage |
host.ShowNotification(title, msg, icon) | Show tray notification |
host.GetDevice(deviceId) | Get device from any plugin |
host.GetDevicesByType<T>() | Get devices by interface type |
host.RegisterApiRoute(path, method, handler) | Add custom HTTP endpoint |
host.RegisterHotkey(id, name, callback) | Register global hotkey |
host.Subscribe<TEvent>(handler) | Subscribe to plugin events |
Device Interfaces
Plugins expose devices through interfaces. Use IMatrixDevice for matrix switchers with labeled I/O ports.
IDevice (Base Interface)
| Property/Method | Description |
string DeviceId | Unique device identifier |
string DisplayName | Human-readable name |
string PluginId | Owning plugin ID |
string Address | Device address (IP/hostname) |
bool IsConnected | Connection state |
ConnectAsync() | Connect to device |
DisconnectAsync() | Disconnect from device |
ConnectionStateChanged | Fired on connect/disconnect |
ErrorOccurred | Fired on device errors |
IMatrixDevice (extends IDevice)
For matrix switching devices with input/output ports. Labels are used in routing grid tooltips.
| Property | Description |
string[] InputLabels | Labels for input ports (8 elements) |
string[] OutputLabels | Labels for output ports (8 elements) |
Example: Matrix Device
public class MyMatrixDevice : IMatrixDevice
{
public string DeviceId => _config.DeviceId;
public string DisplayName => _config.DisplayName;
public string PluginId => "my-matrix";
public string Address => _config.IpAddress;
public bool IsConnected => _client.IsConnected;
// IMatrixDevice - labels shown in UI tooltips
public string[] InputLabels => _config.InputLabels;
public string[] OutputLabels => _config.OutputLabels;
// ... connection methods, events
}
Services Reference
Core service interfaces for advanced integration.
IMatrixService
Interface for HDMI matrix control operations.
Connection
| Property/Method | Description |
bool IsConnected | Whether matrix is connected |
string IpAddress | Current matrix IP address |
ConnectAsync(string ip) | Connect to matrix at IP |
Disconnect() | Disconnect from matrix |
DiscoverAsync(int timeout) | Discover devices on network |
Routing
| Method | Description |
SetRouteAsync(int input, int output) | Route input to single output |
SetAllRoutesAsync(int[] routes) | Set all 8 output routes |
SetAllRoutesVerifiedAsync(int[] routes) | Set routes with verification |
GetVideoStatusAsync() | Get current routing status |
Power & Audio
| Method | Description |
SetPowerAsync(bool on) | Set power state |
GetPowerStatusAsync() | Get power status |
SetExternalAudioAsync(int input) | Set external audio (0=off) |
GetAudioStatusAsync() | Get audio status |
CEC Commands
| Method | Description |
CecPowerOnOutputAsync(int port) | Power on output display |
CecPowerOffOutputAsync(int port) | Power off output display |
CecPowerOnInputAsync(int port) | Power on input source |
CecPowerOffInputAsync(int port) | Power off input source |
Presets
| Method | Description |
RecallPresetAsync(int index) | Recall hardware preset (1-8) |
SavePresetAsync(int index) | Save to hardware preset (1-8) |
Events
| Event | Description |
RoutingChanged | Fired when video routing changes |
PowerChanged | Fired when power state changes |
AudioChanged | Fired when audio routing changes |
StateChanged | Fired on any state change |
ErrorOccurred | Fired on communication errors |
Example Usage
// Connect and route
await matrix.ConnectAsync("192.168.1.100");
// Route input 1 to outputs 1-4
await matrix.SetAllRoutesAsync(new[] { 1, 1, 1, 1, 2, 2, 3, 4 });
// Power on all TVs
for (int i = 1; i <= 8; i++)
await matrix.CecPowerOnOutputAsync(i);
// Set external audio to input 3
await matrix.SetExternalAudioAsync(3);
IProfileService
Interface for monitor profiles and scenarios.
Monitor Profiles
| Method | Description |
GetMonitorProfiles() | Get list of profile names |
ApplyMonitorProfileAsync(name) | Apply a monitor profile |
SaveMonitorProfileAsync(name) | Save current display config |
DeleteMonitorProfileAsync(name) | Delete a profile |
Scenarios
| Method | Description |
GetScenarios() | Get list of scenario names |
LoadScenarioAsync(name) | Load scenario details |
ExecuteScenarioAsync(name, progress) | Execute a scenario |
SaveScenarioAsync(scenario) | Save scenario |
DeleteScenarioAsync(name) | Delete scenario |
ScenarioInfo Properties
| Property | Type | Description |
Name | string | Scenario name |
Description | string | Description text |
VideoRouting | int[8] | Input for each output |
MonitorProfileName | string | Monitor profile to apply |
ExternalAudioInput | int | External audio input (0=off) |
PowerOn | bool? | Power on matrix first |
Enabled | bool | Whether scenario is active |
Example Usage
// Execute a scenario with progress
var progress = new Progress<ScenarioProgress>(p => {
Console.WriteLine($"[{p.StepNumber}/{p.TotalSteps}] {p.CurrentStep}");
});
var result = await profiles.ExecuteScenarioAsync("Gaming", progress);
if (result.Success)
Console.WriteLine($"Completed in {result.Duration.TotalSeconds}s");
else
Console.WriteLine($"Failed: {result.Message}");
IPluginService
Interface for plugin management and API routing.
Plugin Information
| Method | Description |
GetPlugins() | Get all plugin info |
GetPlugin(pluginId) | Get specific plugin info |
PluginCount | Number of loaded plugins |
Plugin State
| Method | Description |
IsPluginEnabled(pluginId) | Check if plugin is enabled |
EnablePlugin(pluginId) | Enable a plugin |
DisablePlugin(pluginId) | Disable a plugin |
ReloadPluginAsync(pluginId) | Reload a plugin |
Plugin Configuration
| Method | Description |
ShowPluginSettings(pluginId, hwnd) | Show plugin config dialog |
GetPluginRequirements(pluginId) | Get plugin requirements |
AreRequirementsMet(pluginId) | Check if requirements met |
API Routing
| Method | Description |
GetRegisteredApiRoutes() | List all API routes |
HandleApiRequestAsync(request) | Route API request to plugin |
Plugin Development
Create plugins by implementing the IDevicePlugin interface.
IDevicePlugin Interface
public interface IDevicePlugin : IDisposable
{
// Identity
string PluginId { get; }
string DisplayName { get; }
string Author { get; }
Version Version { get; }
string Description { get; }
int MinHostVersion { get; }
// Lifecycle
Task InitializeAsync(IPluginHost host);
Task ShutdownAsync(PluginCloseReason reason);
void ShowConfigDialog(IntPtr parentWindow);
bool IsEnabled { get; set; }
// Device Management
Task<IEnumerable<IDiscoveredDevice>> DiscoverDevicesAsync();
Task<IDevice> ConnectDeviceAsync(DeviceConfig config);
IEnumerable<IDevice> ConnectedDevices { get; }
// Actions
IEnumerable<IActionDefinition> GetActionDefinitions();
// UI
UserControl CreateSettingsPanel();
// Requirements
PluginRequirements GetRequirements();
}
IPluginHost Interface
The IPluginHost provides access to TMRemote services:
public interface IPluginHost
{
// Logging
IPluginLogger Logger { get; }
// Configuration & Storage
string GetPluginDataPath(string pluginId);
T LoadSettings<T>(string pluginId) where T : class, new();
void SaveSettings<T>(string pluginId, T settings) where T : class;
// UI Integration
void ShowNotification(string title, string message, NotificationIcon icon);
void RequestSettingsRefresh();
void NavigateToSettingsTab(string tabName);
// Firewall Management
bool RequestFirewallAccess(PortRequirement port);
bool RevokeFirewallAccess(PortRequirement port);
bool IsFirewallPortOpen(int port);
// System Enumerations
IReadOnlyList<DisplayInfo> GetDisplays();
IReadOnlyList<AudioEndpointInfo> GetAudioEndpoints(AudioFlow flow);
IReadOnlyList<string> GetMonitorProfiles();
IReadOnlyList<string> GetScenarios();
IReadOnlyList<PluginInfo> GetPlugins();
// Device Enumeration (Cross-Plugin)
IReadOnlyList<IDevice> GetAllDevices();
IReadOnlyList<IDevice> GetDevicesByPlugin(string pluginId);
IReadOnlyList<T> GetDevicesByType<T>() where T : class, IDevice;
void RegisterDevice(IDevice device);
void UnregisterDevice(string deviceId);
IDevice GetDevice(string deviceId);
// REST API Extension
void RegisterApiRoute(string path, string method, Func<ApiRequest, Task<ApiResponse>> handler);
void UnregisterApiRoute(string path, string method);
// Event Subscription
void Subscribe<TEvent>(Action<TEvent> handler) where TEvent : IPluginEvent;
void Unsubscribe<TEvent>(Action<TEvent> handler) where TEvent : IPluginEvent;
void Publish<TEvent>(TEvent evt) where TEvent : IPluginEvent;
// Hotkey Registration
void RegisterHotkey(string hotkeyId, string displayName, Action callback);
void UnregisterHotkey(string hotkeyId);
// Services
T GetService<T>() where T : class;
INavigationPanelService NavigationPanels { get; }
// Application Info
Version HostVersion { get; }
string ApplicationPath { get; }
int InterfaceVersion { get; }
int ApiRevision { get; }
}
Example Plugin
public class MyPlugin : IDevicePlugin
{
private IPluginHost _host;
public string PluginId => "my-plugin";
public string DisplayName => "My Custom Plugin";
public string Description => "Does amazing things";
public Version Version => new Version(1, 0, 0);
public string Author => "Your Name";
public int MinHostVersion => 1;
public bool IsEnabled { get; set; }
public IEnumerable<IDevice> ConnectedDevices => _devices;
private List<IDevice> _devices = new();
public async Task InitializeAsync(IPluginHost host)
{
_host = host;
_host.Logger.Info("MyPlugin initializing...");
// Register API route (requires path AND HTTP method)
_host.RegisterApiRoute("myplugin/status", "GET", HandleStatusRequest);
// Subscribe to events
_host.Subscribe<RoutingChangedEvent>(OnRoutingChanged);
}
private Task<ApiResponse> HandleStatusRequest(ApiRequest request)
{
return Task.FromResult(new ApiResponse
{
Success = true,
Data = new { Status = "OK", Version = Version.ToString() }
});
}
private void OnRoutingChanged(RoutingChangedEvent e)
{
_host.Logger.Info($"Routing changed: {string.Join(",", e.Routes)}");
}
public Task ShutdownAsync(PluginCloseReason reason)
{
_host.Unsubscribe<RoutingChangedEvent>(OnRoutingChanged);
return Task.CompletedTask;
}
public void ShowConfigDialog(IntPtr parentWindow) { }
public Task<IEnumerable<IDiscoveredDevice>> DiscoverDevicesAsync()
=> Task.FromResult<IEnumerable<IDiscoveredDevice>>(Array.Empty<IDiscoveredDevice>());
public Task<IDevice> ConnectDeviceAsync(DeviceConfig config) => Task.FromResult<IDevice>(null);
public IEnumerable<IActionDefinition> GetActionDefinitions() => Array.Empty<IActionDefinition>();
public UserControl CreateSettingsPanel() => null;
public PluginRequirements GetRequirements() => new PluginRequirements();
public void Dispose() { }
}
Navigation Panel Provider
Plugins that want to provide custom control panels implement INavigationPanelProvider:
public interface INavigationPanelProvider
{
// Synchronous - returns immediately with current state (may be stale)
NavigationPanelDefinition GetPanelDefinition(IDevice device = null);
// Asynchronous - waits for device state to load before returning
Task<NavigationPanelDefinition> GetPanelDefinitionAsync(IDevice device = null);
// Execute control actions
Task<bool> ExecuteControlAsync(IDevice device, string controlId);
Task<bool> ExecuteControlAsync(IDevice device, string controlId, object value);
// Get current control state
Task<object> GetControlStateAsync(IDevice device, string controlId);
}
Sync vs Async Panel Definition
| Method | When to Use | Behavior |
GetPanelDefinition |
Quick checks, device list population |
Returns immediately with cached/default state |
GetPanelDefinitionAsync |
Initial panel display, after device connection |
Fetches current device state before building panel |
Why Async Matters
Device state (patterns, controls, volume, etc.) is fetched asynchronously. If you build the panel
before state is loaded, it will show stale or empty data. GetPanelDefinitionAsync ensures
state is loaded first:
public async Task<NavigationPanelDefinition> GetPanelDefinitionAsync(IDevice device = null)
{
var myDevice = device as MyDevice ?? _connectedDevices.FirstOrDefault();
// Load current state before building panel
if (myDevice != null && myDevice.IsConnected)
{
await myDevice.RefreshStateAsync(); // Fetch current state from device
}
// Now build panel with up-to-date state
return BuildPanelDefinition(myDevice);
}
Panel Definition Structure
var panel = new NavigationPanelDefinition
{
PanelId = "my-panel",
DisplayName = "My Device",
PluginId = PluginId,
PollIntervalMs = 5000 // Refresh state every 5 seconds
};
// Add control sections
var section = new ControlSection
{
SectionId = "controls",
DisplayName = "Device Controls",
Layout = SectionLayout.Row
};
section.Controls.Add(new ControlDefinition
{
ControlId = "power",
DisplayName = "Power",
Type = ControlType.Toggle,
StandardControl = StandardControl.Power
});
panel.Sections.Add(section);
CLI Integration
The ServicesCLI class provides programmatic CLI access:
// Create CLI with services
var cli = new ServicesCLI(
matrix: AppServices.Instance.Matrix,
profiles: AppServices.Instance.Profiles,
plugins: AppServices.Instance.Plugins
);
// Execute commands
int exitCode = await cli.ExecuteAsync(new[] { "route", "1", "3" });
await cli.ExecuteAsync(new[] { "scenario", "run", "Gaming" });
await cli.ExecuteAsync(new[] { "plugins" });
Available Commands
| Category | Commands |
| General | help, status |
| Matrix | connect, disconnect, discover, route, routes, power, audio, preset, cec |
| Profiles | profiles, profile (apply/save/delete) |
| Scenarios | scenarios, scenario (run/show/delete) |
| Plugins | plugins, plugin (enable/disable/reload/info), api-routes |
TMRemote SDK 2.73.26.5