diff --git a/RESOURCES.md b/RESOURCES.md new file mode 100644 index 0000000..db66e3f --- /dev/null +++ b/RESOURCES.md @@ -0,0 +1,11 @@ +# Resources + +This a list of attributions and giving credit to the sources and inspiration for various elements of this project + +## Images + +### Icons + +
+[plugin by Galaktionoff from the Noun Project](https://thenounproject.com/search/?q=plugin&i=1692300#) + diff --git a/Templates/StreamDeck.PluginTemplate.Csharp/content/.template.config/template.json b/Templates/StreamDeck.PluginTemplate.Csharp/content/.template.config/template.json index 108ec74..54439b2 100644 --- a/Templates/StreamDeck.PluginTemplate.Csharp/content/.template.config/template.json +++ b/Templates/StreamDeck.PluginTemplate.Csharp/content/.template.config/template.json @@ -34,7 +34,8 @@ "datatype": "string", "description": "The name of the stream deck plugin.", "defaultValue": "MyPlugin", - "replaces": "$(PluginName)" + "replaces": "$(PluginName)", + "fileRename": "_PluginName_" }, "uuid": { "type": "parameter", diff --git a/Templates/StreamDeck.PluginTemplate.Csharp/content/DefaultPluginAction.cs b/Templates/StreamDeck.PluginTemplate.Csharp/content/DefaultPluginAction.cs deleted file mode 100644 index 440ed5b..0000000 --- a/Templates/StreamDeck.PluginTemplate.Csharp/content/DefaultPluginAction.cs +++ /dev/null @@ -1,79 +0,0 @@ -using StreamDeckLib; -using StreamDeckLib.Messages; -using System.Dynamic; -using System.Threading.Tasks; - -namespace _StreamDeckPlugin_ -{ - - // This value must match the UUID for the action in the manifest.json - // file, so that it can be called from the Stream Deck. - [ActionUuid(Uuid = "$(UUID).DefaultPluginAction")] - internal class DefaultPluginAction : BaseStreamDeckAction - { - private static int _Counter = 0; - private static bool _IsPropertyInspectorConnected = false; - - public override async Task OnKeyUp(StreamDeckEventPayload args) - { - _Counter++; - await Manager.SetTitleAsync(args.context, _Counter.ToString()); - - if (_Counter % 10 == 0) - { - await Manager.ShowAlertAsync(args.context); - } - else if (_Counter % 15 == 0) - { - await Manager.OpenUrlAsync(args.context, "https://www.bing.com"); - } - else if (_Counter % 3 == 0) - { - await Manager.ShowOkAsync(args.context); - } - else if (_Counter % 7 == 0) - { - await Manager.SetImageAsync(args.context, "Fritz.png"); - } - } - - public override async Task OnWillAppear(StreamDeckEventPayload args) - { - if (args.payload != null && args.payload.settings != null && args.payload.settings.counter != null) - { - _Counter = args.payload.settings.counter; - } - await Manager.SetTitleAsync(args.context, _Counter.ToString()); - } - - public override async Task OnWillDisappear(StreamDeckEventPayload args) - { - - var settings = new { counter = _Counter }; - - await Manager.SetSettingsAsync(args.context, settings); - } - - public override Task OnPropertyInspectorConnected(PropertyInspectorEventPayload args) - { - _IsPropertyInspectorConnected = true; - return Task.CompletedTask; - } - - public override Task OnPropertyInspectorDisconnected(PropertyInspectorEventPayload args) - { - _IsPropertyInspectorConnected = false; - return Task.CompletedTask; - } - - public async override Task OnPropertyInspectorMessageReceived(PropertyInspectorEventPayload args) - { - if (args.SettingsPayloadHasProperty("starting_number")) - { - _Counter = args.GetSettingsPayloadValue("starting_number"); - await Manager.SetTitleAsync(args.context, _Counter.ToString()); - } - - } - } -} diff --git a/Templates/StreamDeck.PluginTemplate.Csharp/content/_PluginName_Action.cs b/Templates/StreamDeck.PluginTemplate.Csharp/content/_PluginName_Action.cs new file mode 100644 index 0000000..4ff3243 --- /dev/null +++ b/Templates/StreamDeck.PluginTemplate.Csharp/content/_PluginName_Action.cs @@ -0,0 +1,67 @@ +using StreamDeckLib; +using StreamDeckLib.Messages; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace _StreamDeckPlugin_ +{ + [ActionUuid(Uuid="$(UUID)")] + public class $(PluginName)Action : BaseStreamDeckActionWithSettingsModel + { + // Cheer 342 cpayette 15/2/19 + // Cheer 100 devlead 15/2/19 + // Cheer 200 kevin_downs Jan 11, 2019 + // Cheer 401 cpayette Jan 15, 2019 + // Cheer 2501 themikejolley Jan 15, 2019 + // Cheer 100 wolfgang_blitz Jan 15, 2019 + // Cheer 157 jongalloway Jan 15, 2019 + // Cheer 100 johanb Jan 15, 2019 + // Cheer 400 faniereynders Jan 15, 2019 + // Cheer 100 TomMcQ Jan 15, 2019 + // Cheer 361 Crazy240sx Jan 15, 2019 + // Cheer 600 yarrgh Jan 15, 2019 + // Cheer 1030 kulu83 Jan 15, 2019 + // Cheer 2500 Auth0Bobby Jan 15, 2019 + + public override async Task OnKeyUp(StreamDeckEventPayload args) + { + SettingsModel.Counter++; + await Manager.SetTitleAsync(args.context, SettingsModel.Counter.ToString()); + + if (SettingsModel.Counter % 10 == 0) + { + await Manager.ShowAlertAsync(args.context); + } + else if (SettingsModel.Counter % 15 == 0) + { + await Manager.OpenUrlAsync(args.context, "https://www.bing.com"); + } + else if (SettingsModel.Counter % 3 == 0) + { + await Manager.ShowOkAsync(args.context); + } + else if (SettingsModel.Counter % 7 == 0) + { + await Manager.SetImageAsync(args.context, "images/Fritz.png"); + } + + //update settings + await Manager.SetSettingsAsync(args.context, SettingsModel); + } + + public override async Task OnDidReceiveSettings(StreamDeckEventPayload args) + { + await base.OnDidReceiveSettings(args); + await Manager.SetTitleAsync(args.context, SettingsModel.Counter.ToString()); + } + + public override async Task OnWillAppear(StreamDeckEventPayload args) + { + await base.OnWillAppear(args); + await Manager.SetTitleAsync(args.context, SettingsModel.Counter.ToString()); + } + + } +} diff --git a/Templates/StreamDeck.PluginTemplate.Csharp/content/_StreamDeckPlugin_.cmd b/Templates/StreamDeck.PluginTemplate.Csharp/content/_StreamDeckPlugin_.cmd new file mode 100644 index 0000000..65c672e --- /dev/null +++ b/Templates/StreamDeck.PluginTemplate.Csharp/content/_StreamDeckPlugin_.cmd @@ -0,0 +1,2 @@ +cd %appdata%\Elgato\StreamDeck\Plugins\$(UUID) +_StreamDeckPlugin_.exe -break %* diff --git a/Templates/StreamDeck.PluginTemplate.Csharp/content/_StreamDeckPlugin_.csproj b/Templates/StreamDeck.PluginTemplate.Csharp/content/_StreamDeckPlugin_.csproj index aad5c36..21ab1ef 100644 --- a/Templates/StreamDeck.PluginTemplate.Csharp/content/_StreamDeckPlugin_.csproj +++ b/Templates/StreamDeck.PluginTemplate.Csharp/content/_StreamDeckPlugin_.csproj @@ -65,6 +65,33 @@ PreserveNewest + + PreserveNewest + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + + + PreserveNewest + diff --git a/Templates/StreamDeck.PluginTemplate.Csharp/content/images/Fritz.png b/Templates/StreamDeck.PluginTemplate.Csharp/content/images/Fritz.png new file mode 100644 index 0000000..e0f8970 Binary files /dev/null and b/Templates/StreamDeck.PluginTemplate.Csharp/content/images/Fritz.png differ diff --git a/Templates/StreamDeck.PluginTemplate.Csharp/content/images/category/categoryIcon.png b/Templates/StreamDeck.PluginTemplate.Csharp/content/images/category/categoryIcon.png new file mode 100644 index 0000000..2132054 Binary files /dev/null and b/Templates/StreamDeck.PluginTemplate.Csharp/content/images/category/categoryIcon.png differ diff --git a/Templates/StreamDeck.PluginTemplate.Csharp/content/images/category/categoryIcon@2x.png b/Templates/StreamDeck.PluginTemplate.Csharp/content/images/category/categoryIcon@2x.png new file mode 100644 index 0000000..e8541bb Binary files /dev/null and b/Templates/StreamDeck.PluginTemplate.Csharp/content/images/category/categoryIcon@2x.png differ diff --git a/Templates/StreamDeck.PluginTemplate.Csharp/content/manifest.json b/Templates/StreamDeck.PluginTemplate.Csharp/content/manifest.json index 9b5f934..a3224c0 100644 --- a/Templates/StreamDeck.PluginTemplate.Csharp/content/manifest.json +++ b/Templates/StreamDeck.PluginTemplate.Csharp/content/manifest.json @@ -2,7 +2,7 @@ "Actions": [ { "Icon": "images/actionIcon", - "Name": "$(PluginName)", + "Name": "$(PluginName)Action", "States": [ { "Image": "images/actionDefaultImage", @@ -15,7 +15,10 @@ "UUID": "$(UUID).DefaultPluginAction" } ], - "Author": "", + "Category": "Your Plugin Category", + "CategoryIcon": "images/category/categoryIcon.png", + "Disabled": false, + "Author": "Your Name", "CodePathWin": "_StreamDeckPlugin_.exe", "CodePathMac": "_StreamDeckPlugin_", "PropertyInspectorPath": "property_inspector/property_inspector.html", @@ -24,7 +27,11 @@ "Icon": "images/pluginIcon", "URL": "https://www.elgato.com/gaming/stream-deck", "Version": "1.0", - "OS": [ + "SDKVersion": 2, + "Software": { + "MinimumVersion": "4.1" + }, + "OS": [ { "Platform": "mac", "MinimumVersion" : "10.11" diff --git a/Templates/StreamDeck.PluginTemplate.Csharp/content/models/CounterSettingsModel.cs b/Templates/StreamDeck.PluginTemplate.Csharp/content/models/CounterSettingsModel.cs new file mode 100644 index 0000000..4095586 --- /dev/null +++ b/Templates/StreamDeck.PluginTemplate.Csharp/content/models/CounterSettingsModel.cs @@ -0,0 +1,7 @@ +namespace _StreamDeckPlugin_.Models +{ + public class CounterSettingsModel + { + public int Counter { get; set; } = 0; + } +} diff --git a/Templates/StreamDeck.PluginTemplate.Csharp/content/property_inspector/js/property-inspector.js b/Templates/StreamDeck.PluginTemplate.Csharp/content/property_inspector/js/property-inspector.js index 0406265..9282c63 100644 --- a/Templates/StreamDeck.PluginTemplate.Csharp/content/property_inspector/js/property-inspector.js +++ b/Templates/StreamDeck.PluginTemplate.Csharp/content/property_inspector/js/property-inspector.js @@ -2,39 +2,60 @@ // as well as some info about our plugin, as sent by Stream Deck software var websocket = null, uuid = null, - inInfo = null, - actionInfo = {}; + inInfo = null, + actionInfo = {}, + settingsModel = { + Counter: 0 + }; -function connectSocket(inPort, inUUID, inRegisterEvent, inInfo, inActionInfo) { +function connectElgatoStreamDeckSocket(inPort, inUUID, inRegisterEvent, inInfo, inActionInfo) { uuid = inUUID; actionInfo = JSON.parse(inActionInfo); inInfo = JSON.parse(inInfo); websocket = new WebSocket('ws://localhost:' + inPort); + //initialize values + if (actionInfo.payload.settings.settingsModel) { + settingsModel.Counter = actionInfo.payload.settings.settingsModel.Counter; + } + + document.getElementById('txtCounterValue').value = settingsModel.Counter; + websocket.onopen = function () { - var json = { event: inRegisterEvent, uuid: inUUID }; - // register property inspector to Stream Deck - websocket.send(JSON.stringify(json)); - sendValueToPlugin('propertyInspectorConnected', 'property_inspector'); - }; -} + var json = { event: inRegisterEvent, uuid: inUUID }; + // register property inspector to Stream Deck + websocket.send(JSON.stringify(json)); -window.addEventListener('unload', function (event) { - sendValueToPlugin('propertyInspectorDisconnected', 'property_inspector'); -}); + }; -function sendValueToPlugin(value, param) { - if (websocket) { - const json = { - "action": actionInfo['action'], - "event": "sendToPlugin", - "context": uuid, - "payload": { - [param]: value - } - }; - websocket.send(JSON.stringify(json)); + websocket.onmessage = function (evt) { + // Received message from Stream Deck + var jsonObj = JSON.parse(evt.data); + var sdEvent = jsonObj['event']; + switch (sdEvent) { + case "didReceiveSettings": + if (jsonObj.payload.settings.settingsModel.Counter) { + settingsModel.Counter = jsonObj.payload.settings.settingsModel.Counter; + document.getElementById('txtCounterValue').value = settingsModel.Counter; + } + break; + default: + break; } + }; } +const setSettings = (value, param) => { + if (websocket) { + settingsModel[param] = value; + var json = { + "event": "setSettings", + "context": uuid, + "payload": { + "settingsModel": settingsModel + } + }; + websocket.send(JSON.stringify(json)); + } +}; diff --git a/Templates/StreamDeck.PluginTemplate.Csharp/content/property_inspector/property_inspector.html b/Templates/StreamDeck.PluginTemplate.Csharp/content/property_inspector/property_inspector.html index cca8caa..8956485 100644 --- a/Templates/StreamDeck.PluginTemplate.Csharp/content/property_inspector/property_inspector.html +++ b/Templates/StreamDeck.PluginTemplate.Csharp/content/property_inspector/property_inspector.html @@ -14,9 +14,9 @@ Elgato SDK Documentation -> https://developer.elgato.com/documentation/stream-deck/sdk/property-inspector/ --> -
-
Starting Number
- +
+
Counter Value
+
diff --git a/src/SamplePlugin/MySampleAction.cs b/src/SamplePlugin/MySampleAction.cs deleted file mode 100644 index ccdc0ac..0000000 --- a/src/SamplePlugin/MySampleAction.cs +++ /dev/null @@ -1,100 +0,0 @@ -using Microsoft.Extensions.Logging; -using SamplePlugin.Models; -using Serilog.Core; -using StreamDeckLib; -using StreamDeckLib.Messages; -using System; -using System.Threading.Tasks; - -namespace SamplePlugin -{ - [ActionUuid(Uuid = "com.csharpfritz.samplePlugin.action")] - internal class MySampleAction : BaseStreamDeckAction - { - - // Cheer 200 kevin_downs Jan 11, 2019 - // Cheer 401 cpayette Jan 15, 2019 - // Cheer 2501 themikejolley Jan 15, 2019 - // Cheer 100 wolfgang_blitz Jan 15, 2019 - // Cheer 157 jongalloway Jan 15, 2019 - // Cheer 100 johanb Jan 15, 2019 - // Cheer 400 faniereynders Jan 15, 2019 - // Cheer 100 TomMcQ Jan 15, 2019 - // Cheer 361 Crazy240sx Jan 15, 2019 - // Cheer 600 yarrgh Jan 15, 2019 - // Cheer 1030 kulu83 Jan 15, 2019 - // Cheer 2500 Auth0Bobby Jan 15, 2019 - - private SampleActionSettingsModel _settingsModel = new SampleActionSettingsModel(); - private bool _IsPropertyInspectorConnected = false; - - public override async Task OnKeyUp(StreamDeckEventPayload args) - { - - // Cheer 342 cpayette 15/2/19 - // Cheer 100 devlead 15/2/19 - - Logger.LogTrace($"Button pressed: {args}"); - - _settingsModel.Counter++; - await Manager.SetTitleAsync(args.context, _settingsModel.Counter.ToString()); - - if (_settingsModel.Counter % 10 == 0) - { - await Manager.ShowAlertAsync(args.context); - } - else if (_settingsModel.Counter % 15 == 0) - { - await Manager.OpenUrlAsync(args.context, "https://www.bing.com"); - } - else if (_settingsModel.Counter % 3 == 0) - { - await Manager.ShowOkAsync(args.context); - } - else if (_settingsModel.Counter == 0) - { - await Manager.SetImageAsync(args.context, "images/Fritz.png"); - } - - await Manager.SendToPropertyInspectorAsync(args.context, _settingsModel); - } - - public override async Task OnWillAppear(StreamDeckEventPayload args) - { - - if (args.PayloadSettingsHasProperty("Counter")) - { - _settingsModel.Counter = args.GetPayloadSettingsValue("Counter"); - } - await Manager.SetTitleAsync(args.context, _settingsModel.Counter.ToString()); - } - - public override async Task OnWillDisappear(StreamDeckEventPayload args) - { - await Manager.SetSettingsAsync(args.context, _settingsModel); - } - - public override async Task OnPropertyInspectorConnected(PropertyInspectorEventPayload args) - { - _IsPropertyInspectorConnected = true; - await Manager.SendToPropertyInspectorAsync(args.context, _settingsModel); - } - - public override Task OnPropertyInspectorDisconnected(PropertyInspectorEventPayload args) - { - _IsPropertyInspectorConnected = false; - return Task.CompletedTask; - } - - public override async Task OnPropertyInspectorMessageReceived(PropertyInspectorEventPayload args) - { - if (args.SettingsPayloadHasProperty("Counter")) - { - _settingsModel.Counter = args.GetSettingsPayloadValue("Counter"); - - } - await Manager.SetTitleAsync(args.context, _settingsModel.Counter.ToString()); - - } - } -} diff --git a/src/SamplePlugin/MySamplePluginAction.cs b/src/SamplePlugin/MySamplePluginAction.cs new file mode 100644 index 0000000..8b03973 --- /dev/null +++ b/src/SamplePlugin/MySamplePluginAction.cs @@ -0,0 +1,65 @@ +using SamplePlugin.Models; +using StreamDeckLib; +using StreamDeckLib.Messages; +using System.Threading.Tasks; + +namespace SamplePlugin +{ + [ActionUuid(Uuid = "com.csharpfritz.samplePlugin.action")] + public class MySamplePluginAction : BaseStreamDeckActionWithSettingsModel + { + // Cheer 342 cpayette 15/2/19 + // Cheer 100 devlead 15/2/19 + // Cheer 200 kevin_downs Jan 11, 2019 + // Cheer 401 cpayette Jan 15, 2019 + // Cheer 2501 themikejolley Jan 15, 2019 + // Cheer 100 wolfgang_blitz Jan 15, 2019 + // Cheer 157 jongalloway Jan 15, 2019 + // Cheer 100 johanb Jan 15, 2019 + // Cheer 400 faniereynders Jan 15, 2019 + // Cheer 100 TomMcQ Jan 15, 2019 + // Cheer 361 Crazy240sx Jan 15, 2019 + // Cheer 600 yarrgh Jan 15, 2019 + // Cheer 1030 kulu83 Jan 15, 2019 + // Cheer 2500 Auth0Bobby Jan 15, 2019 + + public override async Task OnKeyUp(StreamDeckEventPayload args) + { + SettingsModel.Counter++; + await Manager.SetTitleAsync(args.context, SettingsModel.Counter.ToString()); + + if (SettingsModel.Counter % 10 == 0) + { + await Manager.ShowAlertAsync(args.context); + } + else if (SettingsModel.Counter % 15 == 0) + { + await Manager.OpenUrlAsync(args.context, "https://www.bing.com"); + } + else if (SettingsModel.Counter % 3 == 0) + { + await Manager.ShowOkAsync(args.context); + } + else if (SettingsModel.Counter % 7 == 0) + { + await Manager.SetImageAsync(args.context, "images/Fritz.png"); + } + + //update settings + await Manager.SetSettingsAsync(args.context, SettingsModel); + } + + public override async Task OnDidReceiveSettings(StreamDeckEventPayload args) + { + await base.OnDidReceiveSettings(args); + await Manager.SetTitleAsync(args.context, SettingsModel.Counter.ToString()); + } + + public override async Task OnWillAppear(StreamDeckEventPayload args) + { + await base.OnWillAppear(args); + await Manager.SetTitleAsync(args.context, SettingsModel.Counter.ToString()); + } + + } +} diff --git a/src/SamplePlugin/Program.cs b/src/SamplePlugin/Program.cs index 619c8b3..25979a6 100644 --- a/src/SamplePlugin/Program.cs +++ b/src/SamplePlugin/Program.cs @@ -1,30 +1,24 @@ -using StreamDeckLib; +using StreamDeckLib; using System.Threading.Tasks; namespace SamplePlugin { - class Program { - // Cheer 200 careypayette February 14, 2019 // Cheer 100 roberttables February 14, 2019 // Cheer 100 careypayette February 15, 2019 // Cheer 100 devlead 15/2/2019 - static async Task Main(string[] args) { - using (var config = StreamDeckLib.Config.ConfigurationBuilder.BuildDefaultConfiguration(args)) { - await ConnectionManager.Initialize(args, config.LoggerFactory) - .RegisterAllActions(typeof(Program).Assembly) - .StartAsync(); - + .RegisterAllActions(typeof(Program).Assembly) + .StartAsync(); } - } + } } diff --git a/src/SamplePlugin/SamplePlugin.csproj b/src/SamplePlugin/SamplePlugin.csproj index f68b9ea..32f25ad 100644 --- a/src/SamplePlugin/SamplePlugin.csproj +++ b/src/SamplePlugin/SamplePlugin.csproj @@ -77,16 +77,7 @@ PreserveNewest - Always - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest + PreserveNewest diff --git a/src/SamplePlugin/images/category/categoryIcon.png b/src/SamplePlugin/images/category/categoryIcon.png new file mode 100644 index 0000000..2132054 Binary files /dev/null and b/src/SamplePlugin/images/category/categoryIcon.png differ diff --git a/src/SamplePlugin/images/category/categoryIcon@2x.png b/src/SamplePlugin/images/category/categoryIcon@2x.png new file mode 100644 index 0000000..e8541bb Binary files /dev/null and b/src/SamplePlugin/images/category/categoryIcon@2x.png differ diff --git a/src/SamplePlugin/manifest.json b/src/SamplePlugin/manifest.json index fe86212..22255fc 100644 --- a/src/SamplePlugin/manifest.json +++ b/src/SamplePlugin/manifest.json @@ -1,20 +1,23 @@ { - "Actions": [ - { - "Icon": "images/actionIcon", - "Name": "SamplePlugin", - "States": [ - { - "Image": "images/actionDefaultImage", - "TitleAlignment": "middle", - "FontSize": "16" - } - ], - "SupportedInMultiActions": false, - "Tooltip": "How many times did you get pwned today? Keep track with this counter.", - "UUID": "com.csharpfritz.samplePlugin.action" - } - ], + "Actions": [ + { + "Icon": "images/actionIcon", + "Name": "SamplePluginAction", + "States": [ + { + "Image": "images/actionDefaultImage", + "TitleAlignment": "middle", + "FontSize": "16" + } + ], + "SupportedInMultiActions": false, + "Tooltip": "How many times did you get pwned today? Keep track with this counter.", + "UUID": "com.csharpfritz.samplePlugin.action" + } + ], + "Category": "Fritz & Friends", + "CategoryIcon": "images/category/categoryIcon.png", + "Disabled": false, "Author": "Jeffrey T. Fritz", "CodePathWin": "SamplePlugin.cmd", "CodePathMac": "SamplePlugin", @@ -22,8 +25,12 @@ "Description": "The Sample Plugin", "Name": "Sample Plugin", "Icon": "images/pluginIcon", - "URL": "https://www.elgato.com/gaming/stream-deck", - "Version": "1.2", + "URL": "https://github.com/FritzAndFriends", + "Version": "1.3", + "SDKVersion": 2, + "Software": { + "MinimumVersion": "4.1" + }, "OS": [ { "Platform": "mac", diff --git a/src/SamplePlugin/models/CounterSettingsModel.cs b/src/SamplePlugin/models/CounterSettingsModel.cs new file mode 100644 index 0000000..74e0551 --- /dev/null +++ b/src/SamplePlugin/models/CounterSettingsModel.cs @@ -0,0 +1,7 @@ +namespace SamplePlugin.Models +{ + public class CounterSettingsModel + { + public int Counter { get; set; } = 0; + } +} diff --git a/src/SamplePlugin/models/SampleActionSettingsModel.cs b/src/SamplePlugin/models/SampleActionSettingsModel.cs new file mode 100644 index 0000000..1f07685 --- /dev/null +++ b/src/SamplePlugin/models/SampleActionSettingsModel.cs @@ -0,0 +1,7 @@ +namespace SamplePlugin.Models +{ + public class SampleActionSettingsModel + { + public int Counter { get; set; } = 0; + } +} diff --git a/src/SamplePlugin/property_inspector/js/property-inspector.js b/src/SamplePlugin/property_inspector/js/property-inspector.js index faf71e2..8e090fc 100644 --- a/src/SamplePlugin/property_inspector/js/property-inspector.js +++ b/src/SamplePlugin/property_inspector/js/property-inspector.js @@ -1,5 +1,5 @@ // global websocket, used to communicate from/to Stream Deck software -// as well as some info about our plugin, as sent by Stream Deck software +// as well as some info about our plugin, as sent by Stream Deck software var websocket = null, uuid = null, inInfo = null, @@ -8,68 +8,53 @@ var websocket = null, Counter: 0 }; -function connectSocket(inPort, inUUID, inRegisterEvent, inInfo, inActionInfo) { +function connectElgatoStreamDeckSocket(inPort, inUUID, inRegisterEvent, inInfo, inActionInfo) { uuid = inUUID; actionInfo = JSON.parse(inActionInfo); inInfo = JSON.parse(inInfo); websocket = new WebSocket('ws://localhost:' + inPort); + //initialize values + if (actionInfo.payload.settings.settingsModel) { + settingsModel.Counter = actionInfo.payload.settings.settingsModel.Counter; + } + + document.getElementById('txtCounterValue').value = settingsModel.Counter; + websocket.onopen = function () { - var json = { event: inRegisterEvent, uuid: inUUID }; - // register property inspector to Stream Deck - websocket.send(JSON.stringify(json)); - sendEventToPlugin('propertyInspectorConnected'); + var json = { event: inRegisterEvent, uuid: inUUID }; + // register property inspector to Stream Deck + websocket.send(JSON.stringify(json)); + }; websocket.onmessage = function (evt) { - // Received message from Stream Deck - var jsonObj = JSON.parse(evt.data); - var sdEvent = jsonObj['event']; - switch (sdEvent) { - case "sendToPropertyInspector": - if (jsonObj.payload.settingsModel.Counter) { - settingsModel.Counter = jsonObj.payload.settingsModel.Counter; - document.getElementById('txtCounterValue').value = settingsModel.Counter; + // Received message from Stream Deck + var jsonObj = JSON.parse(evt.data); + var sdEvent = jsonObj['event']; + switch (sdEvent) { + case "didReceiveSettings": + if (jsonObj.payload.settings.settingsModel.Counter) { + settingsModel.Counter = jsonObj.payload.settings.settingsModel.Counter; + document.getElementById('txtCounterValue').value = settingsModel.Counter; + } + break; + default: + break; } - break; - default: - break; - } }; } -window.addEventListener('unload', function (event) { - sendEventToPlugin('propertyInspectorDisconnected'); -}); - -function sendValueToPlugin(value, param) { - if (websocket) { - settingsModel[param] = value; - const json = { - "action": actionInfo['action'], - "event": "sendToPlugin", - "context": uuid, - "payload": { - "settingsModel": settingsModel - } - }; - websocket.send(JSON.stringify(json)); - } -} - -function sendEventToPlugin(value, param) { +function setSettings(value, param) { if (websocket) { settingsModel[param] = value; - const json = { - "action": actionInfo['action'], - "event": "sendToPlugin", + var json = { + "event": "setSettings", "context": uuid, "payload": { - "property_inspector": value + "settingsModel": settingsModel } }; websocket.send(JSON.stringify(json)); } -} - - +}; diff --git a/src/SamplePlugin/property_inspector/property_inspector.html b/src/SamplePlugin/property_inspector/property_inspector.html index f88f6ed..7ed2669 100644 --- a/src/SamplePlugin/property_inspector/property_inspector.html +++ b/src/SamplePlugin/property_inspector/property_inspector.html @@ -13,10 +13,10 @@ and Elgato SDK Documentation -> https://developer.elgato.com/documentation/stream-deck/sdk/property-inspector/ --> - -
-
Counter
- + +
+
Counter Value
+
diff --git a/src/StreamDeckLib.Test/ActionManager_UnitTests.cs b/src/StreamDeckLib.Test/ActionManager_UnitTests.cs new file mode 100644 index 0000000..6834f99 --- /dev/null +++ b/src/StreamDeckLib.Test/ActionManager_UnitTests.cs @@ -0,0 +1,123 @@ +using System; +using Xunit; + +namespace StreamDeckLib.Test +{ + + public class ActionManagerShould + { + + [Fact] + public void ThrowActionNotRegisteredException_WhenRetrievingAnUnregisteredAction() + { + // Arrange + using (var SUT = new ActionManager(null)) + { + // Act + + // Assert + Assert.Throws(() => SUT.GetAction("UUID1")); + + } + } + + [Fact] + public void ShowOneSingleActionRegistered_WhenRegisteringOnlyOneAction() + { + // + // Arrange + // + + var testAction = new StubAction(); + BaseStreamDeckAction returnedAction; + + // + // Act + // + using (var SUT = new ActionManager(null)) + { + + SUT.RegisterAction("UUID1"); + returnedAction = SUT.GetActionInstance("UUID1"); + } + + // + // Assert + // + Assert.NotNull(returnedAction); + + Assert.IsType(returnedAction); + } + + + + [Fact] + public void ShouldReturnTrue_WhenInquiredAboutRegistrationOfRegisteredActionUUID() + { + // + // Arrange + // + using (var SUT = new ActionManager(null)) + { + + // + // Act + // + SUT.RegisterAction("UUID1"); + + // + // Assert + // + Assert.True(SUT.IsActionRegistered("UUID1")); + + } + } + + + [Fact] + public void ShouldReturnFalse_WhenInquiredAboutRegistrationOfUnregisteredActionUUID() + { + // + // Arrange + // + using (var SUT = new ActionManager(null)) + { + + // + // Act + // + SUT.RegisterAction("UUID1"); + + // + // Assert + // + Assert.False(SUT.IsActionRegistered("UUID2")); + + } + } + + + + [Fact] + public void ShouldReturnTrue_WhenInquiredAboutRegistrationOfActionUUIDWithDifferentCasing() + { + // + // Arrange + // + using (var SUT = new ActionManager(null)) + { + + // + // Act + // + SUT.RegisterAction("UUID1"); + + // + // Assert + // + Assert.True(SUT.IsActionRegistered("uuID1")); + } + } + + } +} \ No newline at end of file diff --git a/src/StreamDeckLib.Test/ConnectionManager_UnitTests.cs b/src/StreamDeckLib.Test/ConnectionManager_UnitTests.cs index 9ea6167..47b56ed 100644 --- a/src/StreamDeckLib.Test/ConnectionManager_UnitTests.cs +++ b/src/StreamDeckLib.Test/ConnectionManager_UnitTests.cs @@ -23,51 +23,62 @@ public void ThrowAnArgumentException_WhenInitializedWithEmptyArgs() action.Should().Throw(); } + [Fact] + public async Task ShouldRegisterEvent_WhenInitializedWithCorrectArgs() + { + // Arrange + var stub = new StubProxy() + { + InspectRegister = (e, uuid) => + { + Assert.Equal(StubProxy.TEST_EVENT, e); + Assert.Equal(uuid, uuid); + } + }; + + // Act + var tokenSource = new CancellationTokenSource(); + var task = ConnectionManager.Initialize(StubProxy.ValidCommandLineArguments, null, stub) + .StartAsync(tokenSource.Token); + + // Assert + Assert.Null(task.Exception); + tokenSource.Cancel(); + + } + + [Fact] - public async Task ShouldRegisterEvent_WhenInitializedWithCorrectArgs() + public async Task ShouldRegisterAllActions_WhenRegisteringAllActions() { - // Arrange - var stub = new StubProxy() - { - InspectRegister = (e, uuid) => - { - Assert.Equal(StubProxy.TEST_EVENT, e); - Assert.Equal(uuid, uuid); - } - }; - // Act - var tokenSource = new CancellationTokenSource(); - var task = ConnectionManager.Initialize(StubProxy.ValidCommandLineArguments, null, stub) - .StartAsync(tokenSource.Token); - - // Assert - Assert.Null(task.Exception); - tokenSource.Cancel(); + Func action = () => ConnectionManager.Initialize(StubProxy.ValidCommandLineArguments) + .RegisterAllActions(this.GetType().Assembly); - } + action.Should() + .NotThrow("No problems when registering All Actions"); - //[Fact] - //public async Task ShouldThrowArgumentNullException_WhenRegisteringANullAction() - //{ - // // - // // Arrange - // // + } - // Func action = () => ConnectionManager.Initialize(StubProxy.ValidCommandLineArguments) - // .RegisterAction(null); + [Fact] + public async Task ShouldHaveConnectionManagerAssigned_WhenGettingAnActionInstance() + { + var cm = ConnectionManager.Initialize(StubProxy.ValidCommandLineArguments) + .RegisterActionType("Unique_Action_ID_1", typeof(StubAction)); + var action = cm.GetInstanceOfAction("FAKECONTEXT", "Unique_Action_ID_1") as StubAction; + action.GetConnectionManager().Should().NotBeNull("An action must have a connection manager assigned"); - // // - // // Act - // // - // action. + } - // // - // // Assert - // // - // Should().Throw("A null BaseStreamDeckAction can not and should not be registered."); - //} + [Fact] + public async Task ShouldHaveLoggerAssigned_WhenGettingAnActionInstance() + { + var cm = ConnectionManager.Initialize(StubProxy.ValidCommandLineArguments) + .RegisterActionType("Unique_Action_ID_1", typeof(StubAction)); + var action = cm.GetInstanceOfAction("FAKECONTEXT", "Unique_Action_ID_1") as StubAction; + action.Logger.Should().NotBeNull("An action must have a logger assigned"); + } [Fact] @@ -107,47 +118,33 @@ public async Task ShouldThrowDuplicateActionRegistrationException_WhenRegisterin // Assert // - //TODO: Wording? "unique UUID" is like saying "PIN number". It's "just" a test, but still syntactically incorrect. + //TODO: Wording? "unique UUID" is like saying "PIN number". It's "just" a test, but still syntactically incorrect. action.Should() .Throw("each BaseStreamDeckAction type should have its own unique UUID."); } - //[Fact] - //public async Task ShouldNotThrowAnyExceptions_WhenRegistringMultipleUniqueActions() - //{ - // // - // // Arrange - // // - - - // Func action = () => ConnectionManager.Initialize(StubProxy.ValidCommandLineArguments) - // .RegisterAction(new StubAction("Unique_Action_ID_1")) - // .RegisterAction(new StubAction("Unique_Action_ID_2")); - - - // // - // // Assert - // // - - // //TODO: Wording? "unique UUID" is like saying "PIN number". It's "just" a test, but still syntactically incorrect. - // action.Should() - // .NotThrow("registering multiple unique actions is valid"); - //} - [Fact] - public async Task ShouldRegisterAllActions_WhenRegisteringAllActions() + public async Task ShouldNotThrowAnyExceptions_WhenRegistringMultipleUniqueActions() { + // + // Arrange + // - Func action = () => ConnectionManager.Initialize(StubProxy.ValidCommandLineArguments) - .RegisterAllActions(this.GetType().Assembly); - action.Should() - .NotThrow("No problems when registering All Actions"); + Func action = () => ConnectionManager.Initialize(StubProxy.ValidCommandLineArguments) + .RegisterActionType("com.csharpfritz.samplePlugin.action", typeof(StubAction)) + .RegisterActionType("com.csharpfritz.samplePlugin.action2", typeof(StubAction)); - } + // + // Assert + // + //TODO: Wording? "unique UUID" is like saying "PIN number". It's "just" a test, but still syntactically incorrect. + action.Should() + .NotThrow("registering multiple unique actions is valid"); + } } diff --git a/src/StreamDeckLib.Test/Stubs/StubAction.cs b/src/StreamDeckLib.Test/Stubs/StubAction.cs index e2d1d42..cb1c694 100644 --- a/src/StreamDeckLib.Test/Stubs/StubAction.cs +++ b/src/StreamDeckLib.Test/Stubs/StubAction.cs @@ -1,9 +1,15 @@ +using System; + namespace StreamDeckLib.Test { - [ActionUuid(Uuid ="Test UUID")] + [ActionUuid(Uuid = "Test UUID")] public class StubAction : BaseStreamDeckAction { + internal ConnectionManager GetConnectionManager() + { + return this.Manager; + } } } diff --git a/src/StreamDeckLib/ActionManager.cs b/src/StreamDeckLib/ActionManager.cs new file mode 100644 index 0000000..0c1a616 --- /dev/null +++ b/src/StreamDeckLib/ActionManager.cs @@ -0,0 +1,319 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; + +namespace StreamDeckLib +{ + + public partial class ActionManager : IDisposable + { + #region Type and instance properties + + // We will use a string equality comparer for our dictionaries which ignores case and is for invariant culture. + private static readonly IEqualityComparer _StringEqualityComparer = StringComparer.Create(CultureInfo.InvariantCulture, true); + + //string is the action UUID, type is the class type of the action + private readonly Dictionary _Actions; + + //string is the context, there will only ever be one action per context + private readonly Dictionary _ActionInstances; + + // The logger we will receive when being instantiated. Defaults to a NullLogger is none is specified. + private readonly ILogger _Logger; + + private ConnectionManager _ConnectionManager; + + #endregion + + + #region Constructors + + private ActionManager() + { + this._Actions = new Dictionary(_StringEqualityComparer); + this._ActionInstances = new Dictionary(_StringEqualityComparer); + } + + /// + /// Initializes a new instance of the class. + /// + /// An instance of a logger ( class. + public ActionManager(ConnectionManager manager, ILogger logger = null) : this() + { + this._Logger = logger ?? NullLoggerFactory.Instance.CreateLogger(nameof(ActionManager)); + this._ConnectionManager = manager; + } + + #endregion + + + #region Action registration methods + + + /// + /// Registers an action type for a given UUID. + /// + /// The instance of . + /// The UUID for the action. + /// The type to be registered for the . Must inherit from . + public ActionManager RegisterAction(string actionUUID) + where TActionType : BaseStreamDeckAction + { + this._Logger.LogTrace($"{nameof(ActionManager)}.{nameof(RegisterAction)}(string)"); + + this._Actions.Add(actionUUID, typeof(TActionType)); + return this; + } + + + /// + /// Registers an action type for a given UUID. + /// + /// The instance of . + /// The UUID for the action. + /// The type to be registered for the . Must inherit from . + /// If the does not inherit from , + /// a exception is thrown. + public ActionManager RegisterActionType(string actionUuid, Type actionType) + { + this._Logger.LogTrace($"{nameof(ActionManager)}.{nameof(RegisterActionType)}(string, Type)"); + + // Check that we've got a UUID to use for registration. + if (string.IsNullOrWhiteSpace(actionUuid)) + { + throw new IncompleteActionDefinitionException($"The UUID for the \"{actionType.Name}\" action was not specified."); + } + + if (_Actions.ContainsKey(actionUuid)) + { + throw new DuplicateActionRegistrationException(actionUuid); + } + + // Ensure that the type we're registering inherits from BaseStreamDeckAction. + if (!actionType.IsSubclassOf(typeof(BaseStreamDeckAction))) + { + throw new TypeDoesNotInheritFromBaseStreamDeckAction(actionType.Name, actionType.FullName, actionType.Assembly.FullName); + } + + this._Actions.Add(actionUuid, actionType); + return this; + } + + + /// + /// Registers all actions which are decorated with an + /// withn a given . + /// + /// The instance of . + /// The from within which actions are to be registered. + public ActionManager RegisterAllActions(Assembly actionsAssembly) + { + this._Logger.LogTrace($"{nameof(ActionManager)}.{nameof(RegisterAllActions)}(assembly)"); + + var actions = actionsAssembly.GetTypes().Where(type => Attribute.IsDefined(type, typeof(ActionUuidAttribute))); + foreach (var actionType in actions) + { + var attr = actionType.GetCustomAttributes(typeof(ActionUuidAttribute), true).FirstOrDefault() as ActionUuidAttribute; + this.RegisterActionType(attr.Uuid, actionType); + } + return this; + } + + #endregion + + + #region Action instance creation and retrieval + + /// + /// Gets an instance of an action from an action UUID. + /// + /// A new instance of an action. + /// The UUID of the action type which is requested. + /// The specific type of action to be returned. + /// If no type is found to match the , an exception + /// of type is thrown. + public TActionType GetActionInstance(string actionUUID) + where TActionType : BaseStreamDeckAction + { + this._Logger.LogTrace($"{nameof(ActionManager)}.{nameof(GetActionInstance)}(string)"); + + if (this._Actions.ContainsKey(actionUUID)) + { + var instance = Activator.CreateInstance(this._Actions[actionUUID]) as TActionType; + instance.Logger = _Logger; + instance.Manager = _ConnectionManager; + return instance; + } + + throw new ActionNotRegisteredException(actionUUID); + } + + + /// + /// Checks whether an action is registered for a given UUID. + /// + /// true, if action registered was registered, false otherwise. + /// The UUID to check + public bool IsActionRegistered(string actionUUID) + { + this._Logger.LogTrace($"{nameof(ActionManager)}.{nameof(IsActionRegistered)}(string)"); + + return this._Actions.ContainsKey(actionUUID); + } + + + /// + /// Gets a new instance of an registered with a UUID + /// of . This instance will not be stored or managed, + /// and cannot be reused. + /// + /// A new instance of the action + /// The UUID of the action type which is to be instantiated + /// + /// Throws a exception if there is no action with + /// a UUID of registered. + /// + public BaseStreamDeckAction GetAction(string actionUUID) + { + this._Logger.LogTrace($"{nameof(ActionManager)}.{nameof(GetAction)}(string)"); + + return this.CreateActionInstanceByUUID(actionUUID); + } + + + private BaseStreamDeckAction CreateActionInstanceByUUID(string actionUuid, bool throwIfNotRegistered = true) + { + this._Logger.LogTrace($"{nameof(ActionManager)}.{nameof(CreateActionInstanceByUUID)}(string, bool)"); + if (this._Actions.ContainsKey(actionUuid)) + { + var instance = Activator.CreateInstance(this._Actions[actionUuid]) as BaseStreamDeckAction; + instance.Logger = _Logger; + instance.Manager = _ConnectionManager; + return instance; + + } + + if (throwIfNotRegistered) + { + throw new ActionNotRegisteredException(actionUuid); + } + + return null; + } + + #endregion + + + #region Specific action instance creation, registration, and retrieval (by Stream Deck context) + + + /// + /// Registers the action instance. + /// + /// The action instance. + /// The key to be used when registering the action instance. + /// This is typically the value of the . + /// Action instance. + public ActionManager RegisterActionInstance(string instanceKey, BaseStreamDeckAction actionInstance) + { + this._Logger.LogTrace($"{nameof(ActionManager)}.{nameof(RegisterAction)}(string, BaseStreamDeckAction)"); + return this.RegisterActionInstanceInternal(instanceKey, actionInstance); + } + + private ActionManager RegisterActionInstanceInternal(string instanceKey, BaseStreamDeckAction actionInstance, bool throwIfInstanceIsNull = true) + { + this._Logger.LogTrace($"{nameof(ActionManager)}.{nameof(RegisterActionInstanceInternal)}(string, BaseStreamDeckAction, bool)"); + + if (null == actionInstance && (!throwIfInstanceIsNull)) + { + this._Logger.LogDebug($"The instance to register with a key of {instanceKey} was null, but prevent raising an exception was specified."); + return this; + } + + if (null == actionInstance && throwIfInstanceIsNull) + { + throw new ArgumentNullException($"Could not register an action instance with a key of \"{instanceKey}\""); + } + + this._Logger.LogDebug($"Registering an instance of {actionInstance.GetType().FullName} with a key of \"{instanceKey}\"."); + if (this._ActionInstances.ContainsKey(instanceKey)) + { + throw new DuplicateActionInstanceRegistrationException(instanceKey); + } + + this._ActionInstances.Add(instanceKey, actionInstance); + + return this; + } + + + /// + /// Gets the instance of action. + /// + /// The instance of action. + /// Context. + /// Action UUID. + internal BaseStreamDeckAction GetActionForContext(string context, string actionUuid) + { + this._Logger.LogTrace($"{nameof(ActionManager)}.{nameof(GetActionForContext)}(string, string)"); + + //see if context exists, if so, return the associated action + if (this._ActionInstances.ContainsKey(context)) + { + return this._ActionInstances[context]; + } + + //see if we have a recorded type for the action + var actionInstance = this.CreateActionInstanceByUUID(actionUuid, false); + this.RegisterActionInstanceInternal(context, actionInstance, false); + + return actionInstance; + } + + + #endregion + + + #region IDisposable Support + + private bool disposedValue = false; // To detect redundant calls + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + // TODO: dispose managed state (managed objects). + } + + // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. + // TODO: set large fields to null. + + disposedValue = true; + } + } + + // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. + // ~ActionManager() { + // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + // Dispose(false); + // } + + // This code added to correctly implement the disposable pattern. + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(true); + // TODO: uncomment the following line if the finalizer is overridden above. + // GC.SuppressFinalize(this); + } + + #endregion + + } +} diff --git a/src/StreamDeckLib/BaseStreamDeckAction.cs b/src/StreamDeckLib/BaseStreamDeckAction.cs index 0c41f32..48595f2 100644 --- a/src/StreamDeckLib/BaseStreamDeckAction.cs +++ b/src/StreamDeckLib/BaseStreamDeckAction.cs @@ -56,11 +56,13 @@ public string ActionUuid public virtual Task OnApplicationDidTerminate(StreamDeckEventPayload args) => Task.CompletedTask; - public virtual Task OnPropertyInspectorMessageReceived(PropertyInspectorEventPayload args) => Task.CompletedTask; + public virtual Task OnDidReceiveSettings(StreamDeckEventPayload args) => Task.CompletedTask; - public virtual Task OnPropertyInspectorConnected(PropertyInspectorEventPayload args) => Task.CompletedTask; + public virtual Task OnDidReceiveGlobalSettings(StreamDeckEventPayload args) => Task.CompletedTask; - public virtual Task OnPropertyInspectorDisconnected(PropertyInspectorEventPayload args) => Task.CompletedTask; - - } + public virtual Task OnPropertyInspectorDidDisappear(StreamDeckEventPayload args) => Task.CompletedTask; + + public virtual Task OnPropertyInspectorDidAppear(StreamDeckEventPayload args) => Task.CompletedTask; + + } } diff --git a/src/StreamDeckLib/BaseStreamDeckActionWithSettingsModel.cs b/src/StreamDeckLib/BaseStreamDeckActionWithSettingsModel.cs new file mode 100644 index 0000000..7a3db3a --- /dev/null +++ b/src/StreamDeckLib/BaseStreamDeckActionWithSettingsModel.cs @@ -0,0 +1,41 @@ +using StreamDeckLib.Messages; +using System; +using System.Threading.Tasks; + +namespace StreamDeckLib +{ + public abstract class BaseStreamDeckActionWithSettingsModel : BaseStreamDeckAction + { + public T SettingsModel { get; } = Activator.CreateInstance(); + + public override Task OnDidReceiveSettings(StreamDeckEventPayload args) + { + SetModelProperties(args); + return Task.CompletedTask; + } + + public override Task OnWillAppear(StreamDeckEventPayload args) + { + SetModelProperties(args); + return Task.CompletedTask; + } + + protected void SetModelProperties(StreamDeckEventPayload args) + { + var properties = typeof(T).GetProperties(); + foreach (var prop in properties) + { + if (args.payload != null && args.payload.settings != null && args.payload.settings.settingsModel != null) + { + if (args.PayloadSettingsHasProperty(prop.Name)) + { + var value = args.GetPayloadSettingsValue(prop.Name); + var value2 = Convert.ChangeType(value, prop.PropertyType); + prop.SetValue(SettingsModel, value2); + } + } + } + } + + } +} diff --git a/src/StreamDeckLib/ConnectionManager.cs b/src/StreamDeckLib/ConnectionManager.cs index c354c5a..9bd3846 100644 --- a/src/StreamDeckLib/ConnectionManager.cs +++ b/src/StreamDeckLib/ConnectionManager.cs @@ -24,8 +24,20 @@ public partial class ConnectionManager : IDisposable private string _RegisterEvent; private IStreamDeckProxy _Proxy; + // Cheer 225 cpayette 26/2/19 + // Cheer 10700 roberttables 26/2/19 + // Cheer 840 auth0bobby 26/2/19 + // Cheer 13629 themichaeljolley 26/2/19 + // Cheer 182 sqlmistermagoo 26/2/19 + // Cheer 100 acrophobicpixie 26/2/19 + // Cheer 492 danerd 26/2/19 + // Cheer 500 tealoldman 26/2/19 + // Cheer 500 kittishomestead 26/2/19 + // Cheer 5500 electrichavoc 26/2/19 + private ConnectionManager() { + this._ActionManager = new ActionManager(this, _Logger); } public Messages.Info Info { get; private set; } @@ -70,7 +82,9 @@ public static ConnectionManager Initialize(string[] commandLineArgs, private static ConnectionManager Initialize(int port, string uuid, string registerEvent, string info, - ILoggerFactory loggerFactory, IStreamDeckProxy streamDeckProxy) + ILoggerFactory loggerFactory, + IStreamDeckProxy streamDeckProxy, + ActionManager actionManager = null) { // TODO: Validate the info parameter var myInfo = JsonConvert.DeserializeObject(info); @@ -152,43 +166,19 @@ private async Task Run(CancellationToken token) continue; } - if (_ActionEventsIgnore.Contains(msg.Event)) { continue; } - - var action = GetInstanceOfAction(msg.context, msg.action); - if (action == null) - { - _Logger.LogWarning($"The action requested (\"{msg.action}\") was not found as being registered with the plugin"); - continue; - } - - - //property inspector payload - if (msg.Event == "sendToPlugin") + if (string.IsNullOrWhiteSpace(msg.context) && string.IsNullOrWhiteSpace(msg.action)) + { + _Logger.LogInformation($"System event received: ${msg.Event}"); + continue; + } + var action = GetInstanceOfAction(msg.context, msg.action); + if (action == null) { - var piMsg = JsonConvert.DeserializeObject(jsonString); - if (piMsg.EventPayloadHasProperty("property_inspector")) - { - //property inspector event - var piEvent = piMsg.GetEventPayloadValue("property_inspector"); - if (!_PropertyInspectorActionDictionary.ContainsKey(piEvent)) - { - _Logger.LogWarning($"Plugin does not handle the Property Inspector event '{piEvent}'"); - continue; - } - else - { - _PropertyInspectorActionDictionary[piEvent]?.Invoke(action, piMsg); - continue; - - } - - } - - //property inspector property value event - _PropertyInspectorActionDictionary[piMsg.Event]?.Invoke(action, piMsg); + _Logger.LogWarning($"The action requested (\"{msg.action}\") was not found as being registered with the plugin"); continue; } + if (!_EventDictionary.ContainsKey(msg.Event)) { _Logger.LogWarning($"Plugin does not handle the event '{msg.Event}'"); @@ -280,30 +270,26 @@ public async Task SetSettingsAsync(string context, dynamic value) await _Proxy.SendStreamDeckEvent(args); } - public async Task SetStateAsync(string context, int state) + public async Task SetGlobalSettingsAsync(string context, dynamic value) { - var args = new SetStateArgs + var args = new SetGlobalSettingsArgs() { context = context, - payload = new SetStateArgs.Payload - { - state = state - } + payload = new { settingsModel = value } }; await _Proxy.SendStreamDeckEvent(args); } - public async Task SendToPropertyInspectorAsync(string context, dynamic payload) + public async Task SetStateAsync(string context, int state) { - - var uuid = _contextActions[context].ActionUuid; - - var args = new SendToPropertyInspectorArgs + var args = new SetStateArgs { - action = uuid, - context = context, - payload = new { settingsModel = payload } + context = context, + payload = new SetStateArgs.Payload + { + state = state + } }; await _Proxy.SendStreamDeckEvent(args); @@ -339,6 +325,20 @@ public async Task OpenUrlAsync(string context, string url) await _Proxy.SendStreamDeckEvent(args); } + + public async Task GetSettingsAsync(string context) + { + var args = new GetSettingsArgs() { context = context }; + await _Proxy.SendStreamDeckEvent(args); + } + + + public async Task GetGlobalSettingsAsync(string context) + { + var args = new GetGlobalSettingsArgs() { context = context }; + await _Proxy.SendStreamDeckEvent(args); + } + #endregion #region IDisposable Support diff --git a/src/StreamDeckLib/ConnectionManager_Actions.cs b/src/StreamDeckLib/ConnectionManager_Actions.cs index b05e04b..4bc4ec3 100644 --- a/src/StreamDeckLib/ConnectionManager_Actions.cs +++ b/src/StreamDeckLib/ConnectionManager_Actions.cs @@ -7,59 +7,25 @@ namespace StreamDeckLib { partial class ConnectionManager { - //string is the action UUID, type is the class type of the action - private Dictionary _actions = new Dictionary(); - //string is the context, there will only ever be one action per context - private Dictionary _contextActions = new Dictionary(); + private ActionManager _ActionManager; - //Cheer 100 svavablount 15/2/19 - public ConnectionManager RegisterActionType(string actionUuid, Type actionType) - { - if (string.IsNullOrWhiteSpace(actionUuid)){ - throw new IncompleteActionDefinitionException(""); - } - if(_actions.ContainsKey(actionUuid)) - { - throw new DuplicateActionRegistrationException(actionUuid); - } - _actions.Add(actionUuid, actionType); - return this; - } + //Cheer 100 svavablount 15/2/19 + public ConnectionManager RegisterActionType(string actionUuid, Type actionType) + { + this._ActionManager.RegisterActionType(actionUuid, actionType); + return this; + } - public BaseStreamDeckAction GetInstanceOfAction(string context, string actionUuid) - { - //see if context exists, if so, return the associated action - if (_contextActions.Any(x => x.Key.Equals(context))) - { - return _contextActions[context]; - } - else - { - //see if we have a recorded type for the action - if (_actions.Any(x => x.Key.Equals(actionUuid))) + public BaseStreamDeckAction GetInstanceOfAction(string context, string actionUuid) { - var t = _actions[actionUuid]; - var action = Activator.CreateInstance(t) as BaseStreamDeckAction; - action.Manager = this; - action.Logger = _LoggerFactory.CreateLogger(action.ActionUuid); - _contextActions.Add(context, action); - return action; + return this._ActionManager.GetActionForContext(context, actionUuid); } - } - return null; - } + public ConnectionManager RegisterAllActions(Assembly assembly) + { + this._ActionManager.RegisterAllActions(assembly); + return this; + } - public ConnectionManager RegisterAllActions(Assembly assembly) - { - var actions = assembly.GetTypes().Where(t => Attribute.IsDefined(t, typeof(ActionUuidAttribute))); - foreach (var actionType in actions) - { - var attr = actionType.GetCustomAttributes(typeof(ActionUuidAttribute), true).FirstOrDefault() as ActionUuidAttribute; - this.RegisterActionType(attr.Uuid, actionType); - } - return this; } - - } } diff --git a/src/StreamDeckLib/ConnectionManager_Events.cs b/src/StreamDeckLib/ConnectionManager_Events.cs index b0b2917..77ce420 100644 --- a/src/StreamDeckLib/ConnectionManager_Events.cs +++ b/src/StreamDeckLib/ConnectionManager_Events.cs @@ -9,33 +9,23 @@ partial class ConnectionManager { // Cheer ramblinggeek 100 January 21, 2019 - private static readonly Dictionary> _EventDictionary = new Dictionary>() { - ["keyDown"] = (action, args) => action.OnKeyDown(args), - ["keyUp"] = (action, args) => action.OnKeyUp(args), - ["willAppear"] = (action, args) => action.OnWillAppear(args), - ["willDisappear"] = (action, args) => action.OnWillDisappear(args), - ["titleParametersDidChange"] = (action, args) => action.OnTitleParametersDidChange(args), - ["deviceDidConnect"] = (action, args) => action.OnDeviceDidConnect(args), - ["deviceDidDisconnect"] = (action, args) => action.OnDeviceDidDisconnect(args), - ["applicationDidLaunch"] = (action, args) => action.OnApplicationDidLaunch(args), - ["applicationDidTerminate"] = (action, args) => action.OnApplicationDidTerminate(args), - }; - - private static readonly string[] _ActionEventsIgnore = new[] { - "deviceDidConnect", "deviceDidDisconnect", "applicationDidLaunch", "applicationDidTerminate" + ["keyDown"] = (plugin, args) => plugin.OnKeyDown(args), + ["keyUp"] = (plugin, args) => plugin.OnKeyUp(args), + ["willAppear"] = (plugin, args) => plugin.OnWillAppear(args), + ["willDisappear"] = (plugin, args) => plugin.OnWillDisappear(args), + ["titleParametersDidChange"] = (plugin, args) => plugin.OnTitleParametersDidChange(args), + ["deviceDidConnect"] = (plugin, args) => plugin.OnDeviceDidConnect(args), + ["deviceDidDisconnect"] = (plugin, args) => plugin.OnDeviceDidDisconnect(args), + ["applicationDidLaunch"] = (plugin, args) => plugin.OnApplicationDidLaunch(args), + ["applicationDidTerminate"] = (plugin, args) => plugin.OnApplicationDidTerminate(args), + ["didReceiveSettings"] = (plugin, args) => plugin.OnDidReceiveSettings(args), + ["didReceiveGlobalSettings"] = (plugin, args) => plugin.OnDidReceiveGlobalSettings(args), + ["propertyInspectorDidDisappear"] = (plugin, args) => plugin.OnPropertyInspectorDidDisappear(args), + ["propertyInspectorDidAppear"] = (plugin, args) => plugin.OnPropertyInspectorDidAppear(args) }; - - private static readonly Dictionary> _PropertyInspectorActionDictionary - = new Dictionary>() - { - ["propertyInspectorConnected"] = (action, args) => action.OnPropertyInspectorConnected(args), - ["propertyInspectorDisconnected"] = (action, args) => action.OnPropertyInspectorDisconnected(args), - ["sendToPlugin"] = (action, args) => action.OnPropertyInspectorMessageReceived(args) - }; - } } diff --git a/src/StreamDeckLib/Exceptions/DuplicateActionInstanceRegistrationException.cs b/src/StreamDeckLib/Exceptions/DuplicateActionInstanceRegistrationException.cs new file mode 100644 index 0000000..42b0a92 --- /dev/null +++ b/src/StreamDeckLib/Exceptions/DuplicateActionInstanceRegistrationException.cs @@ -0,0 +1,28 @@ +using System; +using System.Runtime.Serialization; + +namespace StreamDeckLib +{ + [Serializable] + public class DuplicateActionInstanceRegistrationException : Exception + { + private static string GenerateErrorMessageForUUID(string key) => $"An action with a key value of \"{key}\" has already been registered."; + + public string Key { get; } + + public DuplicateActionInstanceRegistrationException(string key) : base(GenerateErrorMessageForUUID(key)) + { + this.Key = key; + } + + public DuplicateActionInstanceRegistrationException(string uuid, Exception innerException) : base(GenerateErrorMessageForUUID(uuid), innerException) + { + } + + protected DuplicateActionInstanceRegistrationException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + +} diff --git a/src/StreamDeckLib/Exceptions/TypeDoesNotInheritFromBaseStreamDeckAction.cs b/src/StreamDeckLib/Exceptions/TypeDoesNotInheritFromBaseStreamDeckAction.cs new file mode 100644 index 0000000..7781f0b --- /dev/null +++ b/src/StreamDeckLib/Exceptions/TypeDoesNotInheritFromBaseStreamDeckAction.cs @@ -0,0 +1,41 @@ +using System; +using System.Reflection; +using System.Runtime.Serialization; + +namespace StreamDeckLib +{ + [Serializable] + public class TypeDoesNotInheritFromBaseStreamDeckAction : Exception + { + private string fullName; + private string assemblyName; + + public TypeDoesNotInheritFromBaseStreamDeckAction() + { + } + + public TypeDoesNotInheritFromBaseStreamDeckAction(string message) : base(message) + { + } + + + public TypeDoesNotInheritFromBaseStreamDeckAction(string simpleName, string fullName, string assemblyName) + : this($"The type \"{simpleName}\" (\"{fullName}\") from assembly \"{assemblyName}\" does not inherit from required base class \"{nameof(BaseStreamDeckAction)}\".") + { + + this.fullName = fullName; + + this.assemblyName = assemblyName; + + } + + + public TypeDoesNotInheritFromBaseStreamDeckAction(string message, Exception innerException) : base(message, innerException) + { + } + + protected TypeDoesNotInheritFromBaseStreamDeckAction(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/src/StreamDeckLib/Messages/BaseStreamDeckArgs.cs b/src/StreamDeckLib/Messages/BaseStreamDeckArgs.cs index d798e2e..0833ba8 100644 --- a/src/StreamDeckLib/Messages/BaseStreamDeckArgs.cs +++ b/src/StreamDeckLib/Messages/BaseStreamDeckArgs.cs @@ -1,15 +1,15 @@ -using Newtonsoft.Json; +using Newtonsoft.Json; namespace StreamDeckLib.Messages { - public abstract class BaseStreamDeckArgs - { - [JsonProperty(PropertyName = "event")] - public abstract string Event - { - get; - } + public abstract class BaseStreamDeckArgs + { + [JsonProperty(PropertyName = "event")] + public abstract string Event + { + get; + } - public string context { get; set; } - } + public string context { get; set; } + } } diff --git a/src/StreamDeckLib/Messages/GetGlobalSettingsArgs.cs b/src/StreamDeckLib/Messages/GetGlobalSettingsArgs.cs new file mode 100644 index 0000000..0e4c617 --- /dev/null +++ b/src/StreamDeckLib/Messages/GetGlobalSettingsArgs.cs @@ -0,0 +1,7 @@ +namespace StreamDeckLib.Messages +{ + public class GetGlobalSettingsArgs : BaseStreamDeckArgs + { + public override string Event => "getGlobalSettings"; + } +} diff --git a/src/StreamDeckLib/Messages/GetSettingsArgs.cs b/src/StreamDeckLib/Messages/GetSettingsArgs.cs new file mode 100644 index 0000000..2d64395 --- /dev/null +++ b/src/StreamDeckLib/Messages/GetSettingsArgs.cs @@ -0,0 +1,7 @@ +namespace StreamDeckLib.Messages +{ + public class GetSettingsArgs : BaseStreamDeckArgs + { + public override string Event => "getSettings"; + } +} diff --git a/src/StreamDeckLib/Messages/LogMessageArgs.cs b/src/StreamDeckLib/Messages/LogMessageArgs.cs new file mode 100644 index 0000000..633c349 --- /dev/null +++ b/src/StreamDeckLib/Messages/LogMessageArgs.cs @@ -0,0 +1,12 @@ +namespace StreamDeckLib.Messages +{ + public class LogMessageArgs : BaseStreamDeckArgs + { + public override string Event => "logMessage"; + public Payload payload { get; set; } + public class Payload + { + public string message { get; set; } + } + } +} diff --git a/src/StreamDeckLib/Messages/SendToPropertyInspectorArgs.cs b/src/StreamDeckLib/Messages/SendToPropertyInspectorArgs.cs deleted file mode 100644 index 0ad0505..0000000 --- a/src/StreamDeckLib/Messages/SendToPropertyInspectorArgs.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace StreamDeckLib.Messages -{ - public class SendToPropertyInspectorArgs : BaseStreamDeckArgs - { - - public override string Event => "sendToPropertyInspector"; - - public string action { get; set; } - - public dynamic payload { get; set; } - - } - -} diff --git a/src/StreamDeckLib/Messages/SetGlobalSettingsArgs.cs b/src/StreamDeckLib/Messages/SetGlobalSettingsArgs.cs new file mode 100644 index 0000000..9ae2a8c --- /dev/null +++ b/src/StreamDeckLib/Messages/SetGlobalSettingsArgs.cs @@ -0,0 +1,8 @@ +namespace StreamDeckLib.Messages +{ + public class SetGlobalSettingsArgs : BaseStreamDeckArgs + { + public override string Event => "setGlobalSettings"; + public dynamic payload { get; set; } + } +} diff --git a/src/StreamDeckLib/Messages/SetStateArgs.cs b/src/StreamDeckLib/Messages/SetStateArgs.cs index 7d8a430..be46e28 100644 --- a/src/StreamDeckLib/Messages/SetStateArgs.cs +++ b/src/StreamDeckLib/Messages/SetStateArgs.cs @@ -1,4 +1,4 @@ -namespace StreamDeckLib.Messages +namespace StreamDeckLib.Messages { public class SetStateArgs : BaseStreamDeckArgs { @@ -6,7 +6,7 @@ public class SetStateArgs : BaseStreamDeckArgs public Payload payload { get; set; } public class Payload { - public int state{ get; set; } + public int state { get; set; } } } diff --git a/src/StreamDeckLib/Messages/ShowAlertArgs.cs b/src/StreamDeckLib/Messages/ShowAlertArgs.cs index 704b5fa..cdbd0ed 100644 --- a/src/StreamDeckLib/Messages/ShowAlertArgs.cs +++ b/src/StreamDeckLib/Messages/ShowAlertArgs.cs @@ -1,7 +1,7 @@ -namespace StreamDeckLib.Messages +namespace StreamDeckLib.Messages { - public class ShowAlertArgs : BaseStreamDeckArgs - { - public override string Event => "showAlert"; - } + public class ShowAlertArgs : BaseStreamDeckArgs + { + public override string Event => "showAlert"; + } } diff --git a/src/StreamDeckLib/Messages/ShowOkArgs.cs b/src/StreamDeckLib/Messages/ShowOkArgs.cs index a357fc9..c6669db 100644 --- a/src/StreamDeckLib/Messages/ShowOkArgs.cs +++ b/src/StreamDeckLib/Messages/ShowOkArgs.cs @@ -1,7 +1,7 @@ -namespace StreamDeckLib.Messages +namespace StreamDeckLib.Messages { - public class ShowOkArgs : BaseStreamDeckArgs - { - public override string Event => "showOk"; - } + public class ShowOkArgs : BaseStreamDeckArgs + { + public override string Event => "showOk"; + } } diff --git a/src/StreamDeckLib/Messages/StreamDeckEventPayloadExtensions.cs b/src/StreamDeckLib/Messages/StreamDeckEventPayloadExtensions.cs index 7061988..866dae2 100644 --- a/src/StreamDeckLib/Messages/StreamDeckEventPayloadExtensions.cs +++ b/src/StreamDeckLib/Messages/StreamDeckEventPayloadExtensions.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Linq; using System; using System.ComponentModel; @@ -9,7 +9,7 @@ public static class StreamDeckEventPayloadExtensions public static bool PayloadSettingsHasProperty(this StreamDeckEventPayload obj, string propertyName) { var jObj = obj.payload.settings as JObject; - + return jObj.Count > 0 && obj.payload.settings.settingsModel[propertyName] != null; } diff --git a/src/StreamDeckLib/Messages/StreamDeckInfo.cs b/src/StreamDeckLib/Messages/StreamDeckInfo.cs index fd17e63..e738fec 100644 --- a/src/StreamDeckLib/Messages/StreamDeckInfo.cs +++ b/src/StreamDeckLib/Messages/StreamDeckInfo.cs @@ -1,9 +1,10 @@ -namespace StreamDeckLib.Messages +namespace StreamDeckLib.Messages { public class Info { public Application application { get; set; } public Device[] devices { get; set; } + public int devicePixelRatio { get; set; } public class Application { diff --git a/src/StreamDeckLib/StreamDeckProxy.cs b/src/StreamDeckLib/StreamDeckProxy.cs index a1e3e43..d270259 100644 --- a/src/StreamDeckLib/StreamDeckProxy.cs +++ b/src/StreamDeckLib/StreamDeckProxy.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json; +using Newtonsoft.Json; using StreamDeckLib.Messages; using System; using System.Collections.Generic;