-
-
Notifications
You must be signed in to change notification settings - Fork 71
Getting Started
- StreamDeck-Tools Template for Visual Studio - Automatically creates a project with all the files needed to compile a plugin. This is the best way to start a new plugin!
- Install.bat - Script that quickly uninstalls and reinstalls your plugin on the streamdeck (edit the batch file for more details).
- EasyPI - Additional library used to easily pass information from the PI (Property Inspector) to your plugin.
- Profiles Downloadable empty profiles for the XL (32-key), Classic (15-key), Mini (6-key) and Mobile devices at https://barraider.com/profiles
The easiest way to get started is using the Visual Studio Template mentioned above, and taking a look at the samples which demo all the basics to get a plugin working. However, if you don't use the template mentioned above, there are 2 main steps needed to get a plugin up and running:
- In your C# Console Application, Create a class that inherits the
PluginBaseabstract class.
Implement your logic, focusing on the methods provided in the base class.
Follow the samples here for more details
[PluginActionId("plugin.uuid.from.manifest.file")]
public class MyPlugin : PluginBase
{
// Create this constructor in your plugin and pass the objects to the PluginBase class
public MyPlugin(ISDConnection connection, InitialPayload payload) : base(connection, payload)
{
....
// TODO: Use the payload.Settings to see the various settings set in the Property Inspector (in my samples, I create a private class that holds the settings)
// Other relevant settings in the payload include the actual position of the plugin on the Stream Deck
// Note: By passing the `connection` object back to the PluginBase (using the `base` in the constructor), you now have access to a property called `Connection`
// throughout your plugin.
}
....
// TODO: Implement all the remaining abstract functions from PluginBase (or just leave them empty if you don't need them)
// Here is an example of how easy it is to populate settings from the Property Inspector
public override void ReceivedSettings(ReceivedSettingsPayload payload)
{
Tools.AutoPopulateSettings(settings, payload.Settings); // "settings" is a private class that holds the settings for your plugin's instance.
}
}
Note: Use the PluginActionId attribute to indicate the action UUID associated with this class (must match the UUID set in the manifest file)
- In your program.cs, just pass the args you received to the SDWrapper.Run() function, and you're done!
Example:
class Program
{
static void Main(string[] args)
{
SDWrapper.Run(args);
}
}
- There is no step 3 - that's it! The abstract functions from PluginBase that are implemented in MyPlugin hold all the basics needed for a plugin to work. You can always listen to additional events using the
Connectionproperty (see the "Subscribing to events" section below).
Next Topic: Samples
By following a very basic convention, the StreamDeck-Tools can handle populating all the settings between the PropertyInspector and your plugin. All the Stream-Deck Tools samples use this convention so you can see it in the samples too:
- In your Plugin create a private class that will hold your plugin's settings. In the samples and in this example, we will call the private class
PluginSettings - For each setting in your class, create a public property
- For each one of the public properties add a JsonPropery attribute. The
PropertyNamefield should be identical to the name of the setting's field in the PropertyInspector's payload.
private class PluginSettings
{
[JsonProperty(PropertyName = "title")]
public String Title { get; set; }
}
In the example above, we created a property named Title, and added a JsonProperty attribute with the PropertyName of title. This means in our Payload we should have a field with the name title
- If you followed this for all your other properties, use the
Tools.AutoPopulateSettings()method to Auto-populate all the properties inside yourReceivedSettingsfunction:
public override void ReceivedSettings(ReceivedSettingsPayload payload)
{
Tools.AutoPopulateSettings(settings, payload.Settings);
}
Note: If you're using the filepicker, it's a little bit trickier:
The Stream Deck SDK automatically appends a "C:\fakepath" to each file choosen through the SDK's filepicker. StreamDeck-Tools automatically can also auto-populate that field by adding an additional attribute named FilenameProperty to your property:
private class PluginSettings
{
[FilenameProperty]
[JsonProperty(PropertyName = "title")]
public String Title { get; set; }
}
This will tell the AutoPopulateSettings method to strip the "C:\fakepath" from the input.
But how do you make sure it shows correctly in the PropertyInspector too? Make sure you SAVE the settings back after StreamDeck-Tools fixes the filename:
public async override void ReceivedSettings(ReceivedSettingsPayload payload)
{
Tools.AutoPopulateSettings(settings, payload.Settings);
// Return fixed filename back to the Property Inspector
await Connection.SetSettingsAsync(JObject.FromObject(settings));
}
A full list of Stream Deck events are available here. You can subscribe to them using the Connection object in the plugin. IMPORTANT: Remember to unsubscribe in the Dispose() function as shown below:
// Subscribe in Constructor
public MyPlugin(SDConnection connection, InitialPayload payload) : base(connection, payload)
{
...
...
Connection.OnApplicationDidLaunch += Connection_OnApplicationDidLaunch;
Connection.OnApplicationDidTerminate += Connection_OnApplicationDidTerminate;
Connection.OnDeviceDidConnect += Connection_OnDeviceDidConnect;
Connection.OnDeviceDidDisconnect += Connection_OnDeviceDidDisconnect;
Connection.OnPropertyInspectorDidAppear += Connection_OnPropertyInspectorDidAppear;
Connection.OnPropertyInspectorDidDisappear += Connection_OnPropertyInspectorDidDisappear;
Connection.OnSendToPlugin += Connection_OnSendToPlugin;
Connection.OnTitleParametersDidChange += Connection_OnTitleParametersDidChange;
}
...
// Unsubscribe in Dispose
public override void Dispose()
{
Connection.OnApplicationDidLaunch -= Connection_OnApplicationDidLaunch;
Connection.OnApplicationDidTerminate -= Connection_OnApplicationDidTerminate;
Connection.OnDeviceDidConnect -= Connection_OnDeviceDidConnect;
Connection.OnDeviceDidDisconnect -= Connection_OnDeviceDidDisconnect;
Connection.OnPropertyInspectorDidAppear -= Connection_OnPropertyInspectorDidAppear;
Connection.OnPropertyInspectorDidDisappear -= Connection_OnPropertyInspectorDidDisappear;
Connection.OnSendToPlugin -= Connection_OnSendToPlugin;
Connection.OnTitleParametersDidChange -= Connection_OnTitleParametersDidChange;
Logger.Instance.LogMessage(TracingLevel.INFO, "Destructor called");
}
The following is an example of how you can use the title settings built-in the property inspector to show it as an image on the key:
// Note this exists in SdTools.Wrappers namespace
private SdTools.Wrappers.TitleParameters titleParameters = null;
private string userTitle;
// Constructor
public MyPlugin(SDConnection connection, InitialPayload payload) : base(connection, payload)
{
if (payload.Settings == null || payload.Settings.Count == 0)
{
// Create default settings and save them
this.settings = PluginSettings.CreateDefaultSettings();
_ = Connection.SetSettingsAsync(JObject.FromObject(this.settings));
}
else
{
this.settings = payload.Settings.ToObject<PluginSettings>();
}
// Get title information
Connection.OnTitleParametersDidChange += Connection_OnTitleParametersDidChange;
}
private void Connection_OnTitleParametersDidChange(object sender, SdTools.Wrappers.SDEventReceivedEventArgs<SdTools.Events.TitleParametersDidChange> e)
{
titleParameters = e.Event?.Payload?.TitleParameters;
userTitle = e.Event?.Payload?.Title;
}
// Display on key with OnTick
public async override void OnTick()
{
using (Bitmap img = Tools.GenerateGenericKeyImage(out Graphics graphics))
{
int height = img.Height;
int width = img.Width;
Tools.AddTextPathToGraphics(graphics, titleParameters, img.Height, img.Width, userTitle);
await Connection.SetImageAsync(img);
graphics.Dispose();
}
}
Using the GlobalSettingsManager you can get access to the plugin's global settings from anywhere in your code.
Below is an example of how to read and write to the Global Settings.
- Create a class that will store the fields of your Global Settings:
public class GlobalSettings
{
[JsonProperty(PropertyName = "myFirstField")]
public String MyFirstField { get; set; }
[JsonProperty(PropertyName = "mySecondFile")]
public bool MySecondField { get; set; }
}
- In the class you want to read/write the settings, subscribe to the
OnReceivedGlobalSettingsevent. Remember: If you subscribe to an event, you must also unsubscribe to it. So make sure your class has a Dispose function (inherits from IDisposable).
NOTE: If this is in your action where you inherit fromPluginBaseyou can skip this step as you already have aOnReceivedGlobalSettingsfunction as part of the PluginBase implementation
public class MyClass : IDisposable
{
public MyClass()
{
GlobalSettingsManager.Instance.OnReceivedGlobalSettings += MyClass_OnReceivedGlobalSettings;
}
public override void Dispose()
{
GlobalSettingsManager.Instance.OnReceivedGlobalSettings -= MyClass_OnReceivedGlobalSettings;
}
}
- Use
RequestGlobalSettings()method to request the Global Settings. You will then receive a callback in theOnReceivedGlobalSettingsyou set in step 2.
public class MyClass : IDisposable
{
public MyClass()
{
GlobalSettingsManager.Instance.OnReceivedGlobalSettings += MyClass_OnReceivedGlobalSettings;
GlobalSettingsManager.Instance.RequestGlobalSettings();
}
}
- Example of reading and saving settings
private void MyClass_OnReceivedGlobalSettings(object sender, ReceivedGlobalSettingsPayload payload)
{
// Global Settings exist
if (payload?.Settings != null && payload.Settings.Count > 0)
{
global = payload.Settings.ToObject<GlobalSettings>();
// global now has all the settings
// Console.Writeline(global.MyFirstField);
}
else // Global settings do not exist, create new one and SAVE it
{
Logger.Instance.LogMessage(TracingLevel.WARN, $"No global settings found, creating new object");
global = new GlobalSettings();
SetGlobalSettings();
}
}
// Saves the global object back the global settings
private void SetGlobalSettings()
{
Connection.SetGlobalSettingsAsync(JObject.FromObject(global));
}
© Copyright 2021 By BarRaider