diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..cc8b97a
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,206 @@
+# Remove the line below if you want to inherit .editorconfig settings from higher directories
+root = true
+
+# C# files
+[*.cs]
+
+#### Core EditorConfig Options ####
+
+# Indentation and spacing
+indent_size = 4
+indent_style = space
+tab_width = 4
+
+# New line preferences
+end_of_line = crlf
+insert_final_newline = false
+
+#### .NET Coding Conventions ####
+
+# Organize usings
+dotnet_separate_import_directive_groups = true
+dotnet_sort_system_directives_first = true
+
+# this. and Me. preferences
+dotnet_style_qualification_for_event = true:warning
+dotnet_style_qualification_for_field = true:warning
+dotnet_style_qualification_for_method = true:warning
+dotnet_style_qualification_for_property = true:warning
+
+# Language keywords vs BCL types preferences
+dotnet_style_predefined_type_for_locals_parameters_members = false:warning
+dotnet_style_predefined_type_for_member_access = false:warning
+
+# Parentheses preferences
+dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:warning
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:warning
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary:warning
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:warning
+
+# Modifier preferences
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
+
+# Expression-level preferences
+csharp_style_deconstructed_variable_declaration = true:suggestion
+csharp_style_inlined_variable_declaration = true:warning
+csharp_style_throw_expression = true:warning
+dotnet_style_coalesce_expression = true:warning
+dotnet_style_collection_initializer = true:warning
+dotnet_style_explicit_tuple_names = true:warning
+dotnet_style_null_propagation = true:warning
+dotnet_style_object_initializer = false:suggestion
+dotnet_style_prefer_auto_properties = true:warning
+dotnet_style_prefer_compound_assignment = true:warning
+dotnet_style_prefer_conditional_expression_over_assignment = true:warning
+dotnet_style_prefer_conditional_expression_over_return = true:warning
+dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
+dotnet_style_prefer_inferred_tuple_names = true:suggestion
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning
+
+# Field preferences
+dotnet_style_readonly_field = true:warning
+
+# Parameter preferences
+dotnet_code_quality_unused_parameters = all:warning
+
+#### C# Coding Conventions ####
+
+# var preferences
+csharp_style_var_elsewhere = true:silent
+csharp_style_var_for_built_in_types = true:warning
+csharp_style_var_when_type_is_apparent = true:warning
+
+# Expression-bodied members
+csharp_style_expression_bodied_accessors = when_on_single_line:suggestion
+csharp_style_expression_bodied_constructors = when_on_single_line:suggestion
+csharp_style_expression_bodied_indexers = when_on_single_line:suggestion
+csharp_style_expression_bodied_lambdas = when_on_single_line:suggestion
+csharp_style_expression_bodied_local_functions = when_on_single_line:suggestion
+csharp_style_expression_bodied_methods = when_on_single_line:suggestion
+csharp_style_expression_bodied_operators = when_on_single_line:suggestion
+csharp_style_expression_bodied_properties = when_on_single_line:suggestion
+
+# Pattern matching preferences
+csharp_style_pattern_matching_over_as_with_null_check = true:warning
+csharp_style_pattern_matching_over_is_with_cast_check = true:warning
+
+# Null-checking preferences
+csharp_style_conditional_delegate_call = true:warning
+
+# Modifier preferences
+csharp_prefer_static_local_function = false:warning
+csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
+
+# Code-block preferences
+csharp_prefer_braces = true:warning
+csharp_prefer_simple_using_statement = false:warning
+
+# Expression-level preferences
+csharp_prefer_simple_default_expression = true:warning
+csharp_style_pattern_local_over_anonymous_function = true:warning
+csharp_style_prefer_index_operator = true:suggestion
+csharp_style_prefer_range_operator = true:suggestion
+csharp_style_unused_value_assignment_preference = discard_variable:suggestion
+csharp_style_unused_value_expression_statement_preference = discard_variable:suggestion
+
+# 'using' directive preferences
+csharp_using_directive_placement = inside_namespace:warning
+
+#### C# Formatting Rules ####
+
+# New line preferences
+csharp_new_line_before_catch = true
+csharp_new_line_before_else = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_open_brace = all
+csharp_new_line_between_query_expression_clauses = true
+
+# Indentation preferences
+csharp_indent_block_contents = true
+csharp_indent_braces = false
+csharp_indent_case_contents = true
+csharp_indent_case_contents_when_block = false
+csharp_indent_labels = one_less_than_current
+csharp_indent_switch_labels = true
+
+# Space preferences
+csharp_space_after_cast = false
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_after_comma = true
+csharp_space_after_dot = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_after_semicolon_in_for_statement = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_around_declaration_statements = false
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_before_comma = false
+csharp_space_before_dot = false
+csharp_space_before_open_square_brackets = false
+csharp_space_before_semicolon_in_for_statement = false
+csharp_space_between_empty_square_brackets = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_between_method_declaration_name_and_open_parenthesis = false
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_parentheses = false
+csharp_space_between_square_brackets = false
+
+# Wrapping preferences
+csharp_preserve_single_line_blocks = true
+csharp_preserve_single_line_statements = false
+
+#### Naming styles ####
+
+# Naming rules
+
+dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
+dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
+dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
+
+dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.types_should_be_pascal_case.symbols = types
+dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
+
+dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
+dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
+
+# Symbol specifications
+
+dotnet_naming_symbols.interface.applicable_kinds = interface
+dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal
+dotnet_naming_symbols.interface.required_modifiers =
+
+dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
+dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal
+dotnet_naming_symbols.types.required_modifiers =
+
+dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
+dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal
+dotnet_naming_symbols.non_field_members.required_modifiers =
+
+# Naming styles
+
+dotnet_naming_style.pascal_case.required_prefix =
+dotnet_naming_style.pascal_case.required_suffix =
+dotnet_naming_style.pascal_case.word_separator =
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+
+dotnet_naming_style.begins_with_i.required_prefix = I
+dotnet_naming_style.begins_with_i.required_suffix =
+dotnet_naming_style.begins_with_i.word_separator =
+dotnet_naming_style.begins_with_i.capitalization = pascal_case
+
+#### Diagnostic configuration ####
+
+dotnet_diagnostic.CA1028.severity = none
+dotnet_diagnostic.CA1031.severity = none
+dotnet_diagnostic.CA1303.severity = none
+dotnet_diagnostic.CA1060.severity = none
+dotnet_diagnostic.CA2101.severity = none
+
+dotnet_diagnostic.IDE0058.severity = none
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..1ff0c42
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,63 @@
+###############################################################################
+# Set default behavior to automatically normalize line endings.
+###############################################################################
+* text=auto
+
+###############################################################################
+# Set default behavior for command prompt diff.
+#
+# This is need for earlier builds of msysgit that does not have it on by
+# default for csharp files.
+# Note: This is only used by command line
+###############################################################################
+#*.cs diff=csharp
+
+###############################################################################
+# Set the merge driver for project and solution files
+#
+# Merging from the command prompt will add diff markers to the files if there
+# are conflicts (Merging from VS is not affected by the settings below, in VS
+# the diff markers are never inserted). Diff markers may cause the following
+# file extensions to fail to load in VS. An alternative would be to treat
+# these files as binary and thus will always conflict and require user
+# intervention with every merge. To do so, just uncomment the entries below
+###############################################################################
+#*.sln merge=binary
+#*.csproj merge=binary
+#*.vbproj merge=binary
+#*.vcxproj merge=binary
+#*.vcproj merge=binary
+#*.dbproj merge=binary
+#*.fsproj merge=binary
+#*.lsproj merge=binary
+#*.wixproj merge=binary
+#*.modelproj merge=binary
+#*.sqlproj merge=binary
+#*.wwaproj merge=binary
+
+###############################################################################
+# behavior for image files
+#
+# image files are treated as binary by default.
+###############################################################################
+#*.jpg binary
+#*.png binary
+#*.gif binary
+
+###############################################################################
+# diff behavior for common document formats
+#
+# Convert binary document formats to text before diffing them. This feature
+# is only available from the command line. Turn it on by uncommenting the
+# entries below.
+###############################################################################
+#*.doc diff=astextplain
+#*.DOC diff=astextplain
+#*.docx diff=astextplain
+#*.DOCX diff=astextplain
+#*.dot diff=astextplain
+#*.DOT diff=astextplain
+#*.pdf diff=astextplain
+#*.PDF diff=astextplain
+#*.rtf diff=astextplain
+#*.RTF diff=astextplain
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000..1801de1
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,19 @@
+**Version**
+
+
+**Submitting a...**
+
+[ ] Bug report
+[ ] Feature request
+
+**Current behavior:**
+
+
+**Expected behavior:**
+
+
+**Steps to reproduce:**
+
+
+**Other information:**
+
diff --git a/.github/New Text Document.txt b/.github/New Text Document.txt
new file mode 100644
index 0000000..a2a8551
--- /dev/null
+++ b/.github/New Text Document.txt
@@ -0,0 +1,39 @@
+## Description
+
+Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.
+
+Fixes # (issue)
+
+## Type of change
+
+Please delete options that are not relevant.
+
+- [ ] Bug fix (non-breaking change which fixes an issue)
+- [ ] New feature (non-breaking change which adds functionality)
+- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
+- [ ] This change requires a documentation update
+
+## How Has This Been Tested? [Optional]
+
+Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
+
+- [ ] Test A
+- [ ] Test B
+
+**Test Configuration**:
+* Firmware version:
+* Hardware:
+* Toolchain:
+* SDK:
+
+## Checklist:
+
+- [ ] My code follows the style guidelines of this project
+- [ ] I have performed a self-review of my own code
+- [ ] I have commented my code, particularly in hard-to-understand areas
+- [ ] I have made corresponding changes to the documentation
+- [ ] My changes generate no new warnings
+- [ ] I have added tests that prove my fix is effective or that my feature works
+- [ ] New and existing unit tests pass locally with my changes
+- [ ] Any dependent changes have been merged and published in downstream modules
+- [ ] I have checked my code and corrected any misspellings
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..056734d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,311 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# DNX
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Comment the next line if you want to checkin your web deploy settings
+# but database connection strings (with potential passwords) will be unencrypted
+#*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignoreable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+node_modules/
+orleans.codegen.cs
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+
+#
+# ANGULAR gitignore begin section
+#
+
+# See http://help.github.com/ignore-files/ for more about ignoring files.
+
+# compiled output
+/dist
+/tmp
+/out-tsc
+
+# dependencies
+/node_modules
+
+# IDEs and editors
+/.idea
+.project
+.classpath
+.c9/
+*.launch
+.settings/
+*.sublime-workspace
+
+# IDE - VSCode
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+
+# misc
+/.sass-cache
+/connect.lock
+/coverage
+/libpeerconnection.log
+npm-debug.log
+testem.log
+/typings
+
+# e2e
+/e2e/*.js
+/e2e/*.map
+
+# System Files
+*.xcuserstate
+*.xcactivitylog
+
+*.DotSettings
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..51879ab
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,5 @@
+
+
+ 7.3
+
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..eab197d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 Anthony Steiner
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/LoupedeckPackage.yaml b/LoupedeckPackage.yaml
new file mode 100644
index 0000000..17e885a
--- /dev/null
+++ b/LoupedeckPackage.yaml
@@ -0,0 +1,14 @@
+type: plugin4
+name: Webhooks
+displayName: Webhooks
+version: 1.0.0
+author: Steinerd.com
+copyright: Copyright (c) 2022 Anthony Steiner
+
+supportedDevices:
+ - LoupedeckCt
+ - LoupedeckLive
+
+pluginFileName: WebhooksPlugin.dll
+pluginFolderWin: ./bin/win/
+pluginFolderMac: ./bin/mac/
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1da6e89
--- /dev/null
+++ b/README.md
@@ -0,0 +1,90 @@
+# Webhooks Loupedeck Plugin
+[![License](http://img.shields.io/:license-MIT-blue.svg?style=flat)](LICENSE)
+![forks](https://img.shields.io/github/forks/Steinerd/Loupedeck.WebhooksPlugin?style=flat)
+![stars](https://img.shields.io/github/stars/Steinerd/Loupedeck.WebhooksPlugin?style=flat)
+![issues](https://img.shields.io/github/issues/Steinerd/Loupedeck.WebhooksPlugin?style=flat)
+[![downloads](https://img.shields.io/github/downloads/Steinerd/Loupedeck.WebhooksPlugin/latest/total?style=flat)](https://github.com/Steinerd/Loupedeck.WebhooksPlugin/compare)
+
+There is seemingly no built-in way to trigger a "call-and-forget" webhook within the native Loupedeck application.
+
+The Webhooks Loupedeck Plugin corrects the obvious omission of fundamental functionality in a macro controller.
+
+## Credits
+
+This plugin makes heavy use of [HarSharp](https://github.com/giacomelli/HarSharp), and its dependancies.
+
+--------
+
+# Table of Contents
+
+- [Installation](#installation)
+- [Usage](#usage)
+- [Support](#support)
+- [Contributing](#contributing)
+- [License](#license)
+
+# Installation
+
+Loupedeck Installation
+
+
+ 1. Go to [latest release](https://github.com/Steinerd/Loupedeck.WebhooksPlugin/releases/latest), and download the `lplug4` file to you computer
+ 1. Open (normally double-click) to install, the Loupedeck software should take care of the rest
+ 1. Restart Loupedeck (if not handled by the installer)
+ 1. In the Loupedeck interface, enable **Webhooks** by clicking Manage plugins
+ 1. Check the Webhooks box on to enable
+ 1. Drag the desired control onto your layout
+
+Once click it will bring you to a dynamic playback device selection page.
+
+
+IDE Installation
+ Made with Visual Studio 2022, C# will likely only compile in VS2019 or greater.
+
+ Assuming Loupedeck is already installed on your machine, make sure you've stopped it before you debug the project.
+
+ Debugging _should_ build the solution, which will then output the DLL, config, and pdb into your `%LocalAppData%\Loupedeck\Plugins` directory.
+
+ If all goes well, Loupedeck will then open and you can then debug.
+
+
+
+# Usage
+
+Follow the __Loupedeck Installation__ instructions above.
+
+You will need to familiarize yourself with HAR/Http Archive files. They're effectively just JSON files with specific schema for HTTP requests and their responses.
+
+There are numerous ways to create them, or export them in many different applications. Including Postman, Telerik Fiddler2, and even most browsers DevTools will allow you to export/copy web requests as HARs.
+
+An example HAR file for a fake IFTTT call can be found here: [example.har](example.har)
+
+***All HAR Files must be saved to `%userprofile%\.loupedeck\webhooks` ***
+
+You can have multiple `*.har` files with multiple requests, or one `.har` with all the requests. The plugin will treat them the same.
+
+Fields that deal in "size" or "times" can be set to 0. They're not used in the creation/execution of the requests.
+
+Once completed you will be able to add requests to the *Webhook actions* "Requsts" folder in the Loupedeck UI.
+
+1. Simply click the [+] button on the same row
+ 1. Skip the name it just gets clobbered the moment you select a hook
+1. Select HTTP Method (you will only available ones from your HAR files)
+1. Select Hook from the final dropdown and click Save
+
+The button generation leads a lot to be desired, I apologize. I personally just create a macro and drag it in as an action step.
+
+# Support
+
+[Submit an issue](https://github.com/Steinerd/Loupedeck.WebhooksPlugin/issues/new)
+
+Fill out the template to the best of your abilities and send it through.
+
+# Contribute
+
+Easily done. Just [open a pull request](https://github.com/Steinerd/Loupedeck.WebhooksPlugin/compare).
+
+Don't worry about specifics, I'll handle the minutia.
+
+# License
+The MIT-License for this plugin can be reviewed at [LICENSE](LICENSE) attached to this repo.
diff --git a/WebhooksPlugin.sln b/WebhooksPlugin.sln
new file mode 100644
index 0000000..2eb5ffd
--- /dev/null
+++ b/WebhooksPlugin.sln
@@ -0,0 +1,41 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.1.32328.378
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebhooksPlugin", "WebhooksPlugin\WebhooksPlugin.csproj", "{6187A600-57A1-485D-A88E-54E639F498CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{723E67AE-8DB8-4BD7-9234-6FA88C7A4AB3}"
+ ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
+ .gitignore = .gitignore
+ build-plugin.ps1 = build-plugin.ps1
+ Directory.Build.props = Directory.Build.props
+ LICENSE = LICENSE
+ LoupedeckPackage.yaml = LoupedeckPackage.yaml
+ README.md = README.md
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{AD13D4C6-72ED-4D8E-B465-6ED7D2420F3C}"
+ ProjectSection(SolutionItems) = preProject
+ .github\ISSUE_TEMPLATE.md = .github\ISSUE_TEMPLATE.md
+ .github\PULL_REQUEST_TEMPLATE.md = .github\PULL_REQUEST_TEMPLATE.md
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {6187A600-57A1-485D-A88E-54E639F498CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6187A600-57A1-485D-A88E-54E639F498CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6187A600-57A1-485D-A88E-54E639F498CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6187A600-57A1-485D-A88E-54E639F498CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {67C0F55D-B602-45A4-9E23-5F7A12159339}
+ EndGlobalSection
+EndGlobal
diff --git a/WebhooksPlugin/App.config b/WebhooksPlugin/App.config
new file mode 100644
index 0000000..270f7d7
--- /dev/null
+++ b/WebhooksPlugin/App.config
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/WebhooksPlugin/Commands/WebhooksCommand.cs b/WebhooksPlugin/Commands/WebhooksCommand.cs
new file mode 100644
index 0000000..20ab4d3
--- /dev/null
+++ b/WebhooksPlugin/Commands/WebhooksCommand.cs
@@ -0,0 +1,71 @@
+namespace Loupedeck.WebhooksPlugin
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using System.Threading.Tasks;
+ using System.Web;
+
+ using Loupedeck.WebhooksPlugin.Extensions;
+
+ public class WebhookCommand : PluginDynamicCommand
+ {
+ private static readonly Services.HarFileService harFileService = new Services.HarFileService();
+
+ public WebhookCommand() : base("Requests", "Webhook Requests", "Webhook Requests")
+ {
+ this.MakeProfileAction("tree");
+ }
+
+ protected override Boolean OnLoad()
+ {
+ return base.OnLoad();
+ }
+
+ protected override PluginProfileActionData GetProfileActionData()
+ {
+ var tree = new PluginProfileActionTree("Select HTTP Request to Run");
+
+ var webHooks = harFileService.Webhooks;
+
+ var wehHooksByHosts = webHooks.GroupBy(gb => gb.Request.Method);
+
+ tree.AddLevel("HTTP Method");
+ tree.AddLevel("Hook");
+
+ foreach (var group in wehHooksByHosts)
+ {
+ var node = tree.Root.AddNode(group.Key);
+
+ foreach (var entry in group)
+ {
+ node.AddItem(entry.Request.Comment ?? entry.Comment, entry.Request.Comment ?? entry.Comment, $"{entry.Request.Method} {entry.Request.Url}");
+ }
+ }
+
+ return tree;
+ }
+
+ protected override String GetCommandDisplayName(String actionParameter, PluginImageSize imageSize)
+ {
+ return string.IsNullOrEmpty(actionParameter) ? "Requests" : actionParameter;
+ }
+
+ protected override void RunCommand(String actionParameter)
+ {
+ harFileService.Webhooks.FirstOrDefault(f => f.Comment.Equals(actionParameter, StringComparison.OrdinalIgnoreCase))?.Request.Run();
+ }
+
+ protected override BitmapImage GetCommandImage(String actionParameter, PluginImageSize imageSize)
+ {
+ using (var bitmapBuilder = new BitmapBuilder(imageSize))
+ {
+ bitmapBuilder.Clear(BitmapColor.Black);
+
+ bitmapBuilder.DrawText(actionParameter, BitmapColor.White, 15, 13, 3);
+ return bitmapBuilder.ToImage();
+ }
+ }
+ }
+}
diff --git a/WebhooksPlugin/Extensions/HarExtensions.cs b/WebhooksPlugin/Extensions/HarExtensions.cs
new file mode 100644
index 0000000..26025e7
--- /dev/null
+++ b/WebhooksPlugin/Extensions/HarExtensions.cs
@@ -0,0 +1,48 @@
+namespace Loupedeck.WebhooksPlugin.Extensions
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using System.Threading.Tasks;
+
+ using System.Net.Http;
+
+ public static class HarExtensions
+ {
+ public static Task Run(this HarSharp.Request request)
+ {
+ return request.Method != HttpMethod.Get.Method
+ && (
+ string.IsNullOrEmpty(request.PostData.MimeType)
+ || string.IsNullOrEmpty(request.Headers.FirstOrDefault(h => h.Name.ToLower() == "content-type")?.Value)
+ )
+ ? Task.Factory.StartNew(() => { })
+ : Task.Factory.StartNew(() =>
+ {
+ using (var client = new HttpClient())
+ using (var req = new HttpRequestMessage(new HttpMethod(request.Method), request.Url))
+ {
+ request.Headers.ToList().ForEach(h =>
+ {
+ req.Headers.TryAddWithoutValidation(h.Name, h.Value);
+ });
+
+ if (request.Method != HttpMethod.Get.Method)
+ {
+ req.Content = new StringContent(request.PostData.Text, System.Text.Encoding.UTF8, request.PostData.MimeType);
+ }
+
+ using (var response = client.SendAsync(req).Result)
+ {
+ if ((int)response.StatusCode > 400)
+ {
+ Console.Error.WriteLine($"Webhoook threw an undesired status code of {(int)response.StatusCode} | Message: {response.Content.ReadAsStringAsync().Result}");
+ return;
+ }
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/WebhooksPlugin/FodyWeavers.xml b/WebhooksPlugin/FodyWeavers.xml
new file mode 100644
index 0000000..5029e70
--- /dev/null
+++ b/WebhooksPlugin/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/WebhooksPlugin/FodyWeavers.xsd b/WebhooksPlugin/FodyWeavers.xsd
new file mode 100644
index 0000000..44a5374
--- /dev/null
+++ b/WebhooksPlugin/FodyWeavers.xsd
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+
+
+
+
+ A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks
+
+
+
+
+ A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.
+
+
+
+
+ A list of unmanaged 32 bit assembly names to include, delimited with line breaks.
+
+
+
+
+ A list of unmanaged 64 bit assembly names to include, delimited with line breaks.
+
+
+
+
+ The order of preloaded assemblies, delimited with line breaks.
+
+
+
+
+
+ This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file.
+
+
+
+
+ Controls if .pdbs for reference assemblies are also embedded.
+
+
+
+
+ Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.
+
+
+
+
+ As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.
+
+
+
+
+ Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code.
+
+
+
+
+ Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior.
+
+
+
+
+ A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with |
+
+
+
+
+ A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |.
+
+
+
+
+ A list of unmanaged 32 bit assembly names to include, delimited with |.
+
+
+
+
+ A list of unmanaged 64 bit assembly names to include, delimited with |.
+
+
+
+
+ The order of preloaded assemblies, delimited with |.
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/WebhooksPlugin/GlobalSuppressions.cs b/WebhooksPlugin/GlobalSuppressions.cs
new file mode 100644
index 0000000..8844b09
--- /dev/null
+++ b/WebhooksPlugin/GlobalSuppressions.cs
@@ -0,0 +1,8 @@
+// This file is used by Code Analysis to maintain SuppressMessage
+// attributes that are applied to this project.
+// Project-level suppressions either have no target or are given
+// a specific target and scoped to a namespace, type, member, etc.
+
+using System.Diagnostics.CodeAnalysis;
+
+[assembly: SuppressMessage("Style", "IDE0049:Use framework type", Justification = "", Scope = "module")]
diff --git a/WebhooksPlugin/PluginConfiguration.json b/WebhooksPlugin/PluginConfiguration.json
new file mode 100644
index 0000000..cda6f07
--- /dev/null
+++ b/WebhooksPlugin/PluginConfiguration.json
@@ -0,0 +1,4 @@
+{
+ "displayName": "Webhooks Handler",
+ "supportedDevices": "Loupedeck20,Loupedeck30"
+}
diff --git a/WebhooksPlugin/Properties/AssemblyInfo.cs b/WebhooksPlugin/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..d1e44c9
--- /dev/null
+++ b/WebhooksPlugin/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Webhooks")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Steinerd.com")]
+[assembly: AssemblyProduct("Loupedeck2")]
+[assembly: AssemblyCopyright("Copyright (c) 2022 Anthony Steiner. All rights reserved.")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("6187A600-57A1-485D-A88E-54E639F498CA")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("4.3.0.7478")]
+[assembly: AssemblyVersion("1.0.*")]
diff --git a/WebhooksPlugin/Resources/16.png b/WebhooksPlugin/Resources/16.png
new file mode 100644
index 0000000..3c4d44d
Binary files /dev/null and b/WebhooksPlugin/Resources/16.png differ
diff --git a/WebhooksPlugin/Resources/256.png b/WebhooksPlugin/Resources/256.png
new file mode 100644
index 0000000..fce4e20
Binary files /dev/null and b/WebhooksPlugin/Resources/256.png differ
diff --git a/WebhooksPlugin/Resources/32.png b/WebhooksPlugin/Resources/32.png
new file mode 100644
index 0000000..24b0467
Binary files /dev/null and b/WebhooksPlugin/Resources/32.png differ
diff --git a/WebhooksPlugin/Resources/48.png b/WebhooksPlugin/Resources/48.png
new file mode 100644
index 0000000..6a1d622
Binary files /dev/null and b/WebhooksPlugin/Resources/48.png differ
diff --git a/WebhooksPlugin/Services/HarFileService.cs b/WebhooksPlugin/Services/HarFileService.cs
new file mode 100644
index 0000000..eee66f2
--- /dev/null
+++ b/WebhooksPlugin/Services/HarFileService.cs
@@ -0,0 +1,99 @@
+namespace Loupedeck.WebhooksPlugin.Services
+{
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Linq;
+ using System.Text;
+ using System.Threading.Tasks;
+
+ using HarSharp;
+
+ public class HarFileService
+ {
+
+ public List Webhooks { get; private set; }
+
+ private static string FULL_PATH { get => Path.Combine(WebhooksPlugin.UserProfilePath, WebhooksPlugin.DEFAULT_PATH); }
+
+ private FileSystemWatcher _fileWatcher = null;
+
+ public HarFileService(params string[] paths)
+ {
+ if (this.Webhooks == null)
+ {
+ this.Webhooks = new List();
+ }
+
+ this.Init(paths);
+
+ this._fileWatcher = new FileSystemWatcher(FULL_PATH);
+
+ this._fileWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.Size;
+ this._setFileWatchHandlers();
+ this._fileWatcher.Filter = "*.har";
+ this._fileWatcher.IncludeSubdirectories = true;
+ this._fileWatcher.EnableRaisingEvents = true;
+ }
+ private void _setFileWatchHandlers()
+ {
+ this._fileWatcher.Created += this.OnFileChange;
+ this._fileWatcher.Changed += this.OnFileChange;
+ this._fileWatcher.Deleted += this.OnFileChange;
+ }
+
+ private void _unsetFileWatchHanders()
+ {
+ this._fileWatcher.Created -= this.OnFileChange;
+ this._fileWatcher.Changed -= this.OnFileChange;
+ this._fileWatcher.Deleted -= this.OnFileChange;
+ }
+
+
+ ~HarFileService()
+ {
+ if (this._fileWatcher != null)
+ {
+ this._unsetFileWatchHanders();
+ this._fileWatcher.Dispose();
+ this._fileWatcher = null;
+ }
+ }
+
+ public void OnFileChange(object sender, FileSystemEventArgs e)
+ {
+ this._unsetFileWatchHanders();
+ this.Webhooks.Clear();
+ System.Threading.Tasks.Task.Delay(150).Wait();
+ this.Webhooks.AddRange(this._loadFromPath(FULL_PATH));
+ System.Threading.Tasks.Task.Delay(150).Wait();
+ this._setFileWatchHandlers();
+ }
+
+ private void Init(string[] paths)
+ {
+ this.Webhooks.AddRange(this._loadFromPath(FULL_PATH));
+
+ if (paths.Length >= 0)
+ {
+ paths.ToList().ForEach(path =>
+ {
+ this.Webhooks.AddRange(this._loadFromPath(path));
+ });
+ }
+ }
+
+ private IList _loadFromPath(string path)
+ {
+ var entries = new List();
+ var harFiles = System.IO.Directory.GetFiles(path, "*.har");
+
+ foreach (var file in harFiles)
+ {
+ entries.AddRange(HarConvert.DeserializeFromFile(file).Log.Entries);
+ }
+
+ return entries;
+ }
+ }
+}
diff --git a/WebhooksPlugin/WebhooksApplication.cs b/WebhooksPlugin/WebhooksApplication.cs
new file mode 100644
index 0000000..9cf55fb
--- /dev/null
+++ b/WebhooksPlugin/WebhooksApplication.cs
@@ -0,0 +1,12 @@
+namespace Loupedeck.WebhooksPlugin
+{
+ using System;
+
+ public class WebhooksApplication : ClientApplication
+ {
+ public WebhooksApplication() { }
+
+ protected override String GetProcessName() => String.Empty;
+ protected override String GetBundleName() => String.Empty;
+ }
+}
\ No newline at end of file
diff --git a/WebhooksPlugin/WebhooksPlugin.cs b/WebhooksPlugin/WebhooksPlugin.cs
new file mode 100644
index 0000000..8380257
--- /dev/null
+++ b/WebhooksPlugin/WebhooksPlugin.cs
@@ -0,0 +1,36 @@
+namespace Loupedeck.WebhooksPlugin
+{
+ using System;
+ using System.IO;
+
+ public class WebhooksPlugin : Plugin
+ {
+ internal const string DEFAULT_PATH = @".loupedeck\webhooks";
+ internal static string UserProfilePath { get => Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); }
+
+ public override Boolean HasNoApplication => true;
+ public override Boolean UsesApplicationApiOnly => true;
+
+ public override void Load() => this.Init();
+
+ public override void Unload() { }
+
+ private void OnApplicationStarted(Object sender, EventArgs e) { }
+
+ private void OnApplicationStopped(Object sender, EventArgs e) { }
+
+ public override void RunCommand(String commandName, String parameter) { }
+
+ public override void ApplyAdjustment(String adjustmentName, String parameter, Int32 diff) { }
+
+ private void Init()
+ {
+ this.Info.Icon16x16 = EmbeddedResources.ReadImage("Loupedeck.WebhooksPlugin.Resources.16.png");
+ this.Info.Icon32x32 = EmbeddedResources.ReadImage("Loupedeck.WebhooksPlugin.Resources.32.png");
+ this.Info.Icon48x48 = EmbeddedResources.ReadImage("Loupedeck.WebhooksPlugin.Resources.48.png");
+ this.Info.Icon256x256 = EmbeddedResources.ReadImage("Loupedeck.WebhooksPlugin.Resources.256.png");
+
+ Directory.CreateDirectory(Path.Combine(UserProfilePath, DEFAULT_PATH));
+ }
+ }
+}
diff --git a/WebhooksPlugin/WebhooksPlugin.csproj b/WebhooksPlugin/WebhooksPlugin.csproj
new file mode 100644
index 0000000..c258583
--- /dev/null
+++ b/WebhooksPlugin/WebhooksPlugin.csproj
@@ -0,0 +1,99 @@
+
+
+
+
+
+ Debug
+ AnyCPU
+ {6187A600-57A1-485D-A88E-54E639F498CA}
+ Library
+ Properties
+ Loupedeck.WebhooksPlugin
+ WebhooksPlugin
+ v4.7.2
+ 512
+
+
+
+
+ $(SolutionDir)..\obj\
+ $(SolutionDir)..\bin\
+ $(BaseOutputPath)$(Configuration)\
+
+
+ true
+ full
+ false
+ ..\..\..\..\AppData\Local\Loupedeck\Plugins\Webhooks\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ TRACE
+ prompt
+ 4
+ ..\..\..\..\AppData\Local\Loupedeck\Plugins\Webhooks\
+
+
+
+ ..\packages\Costura.Fody.4.1.0\lib\net40\Costura.dll
+
+
+ ..\packages\HarSharp.2.0.0\lib\netstandard2.0\HarSharp.dll
+
+
+ ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll
+
+
+ False
+ C:\Program Files (x86)\Loupedeck\Loupedeck2\PluginApi.dll
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
\ No newline at end of file
diff --git a/WebhooksPlugin/packages.config b/WebhooksPlugin/packages.config
new file mode 100644
index 0000000..23110b4
--- /dev/null
+++ b/WebhooksPlugin/packages.config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build-plugin.ps1 b/build-plugin.ps1
new file mode 100644
index 0000000..65b1b93
--- /dev/null
+++ b/build-plugin.ps1
@@ -0,0 +1,26 @@
+$version = "1.0"
+$project = "Webhooks"
+$dllName = "WebhooksPlugin.dll"
+$dllPath = "$($env:LOCALAPPDATA)\Loupedeck\Plugins\$project"
+$buildPath = ".builds"
+$outputFileName = "$project.$version"
+$zipPath = "$buildPath\$outputFileName.zip"
+$pluginName = "$outputFileName.lplug4"
+$loupedeckYaml = "LoupedeckPackage.yaml"
+$cwd = Get-Location
+
+New-Item -Path "$buildPath" -Force -Name "bin" -ItemType "directory" > $null
+New-Item -Path "$buildPath\bin" -Force -Name "win" -ItemType "directory" > $null
+New-Item -Path "$buildPath\bin" -Force -Name "mac" -ItemType "directory" > $null
+
+Copy-Item $loupedeckYaml -Force -Destination $buildPath > $null
+Copy-Item "$dllPath\$dllName" -Force -Destination "$buildPath\bin\win\$dllName" > $null
+
+$compress = @{
+ Path = "$buildPath\*"
+ CompressionLevel = "Fastest"
+ DestinationPath = $zipPath
+}
+Compress-Archive @Compress > $null
+
+Rename-Item $zipPath -Force -NewName $pluginName > $null
\ No newline at end of file
diff --git a/example.har b/example.har
new file mode 100644
index 0000000..8ec4999
--- /dev/null
+++ b/example.har
@@ -0,0 +1,104 @@
+{
+ "log": {
+ "pages": [],
+ "comment": "",
+ "entries": [
+ {
+ "comment": "IFTTT - Example ON",
+ "time": 0,
+ "serverIPAddress": "108.156.91.103",
+ "connection": "ClientPort:0;EgressPort:62999",
+ "request": {
+ "headersSize": 0,
+ "postData": {
+ "text": "",
+ "mimeType": "application/json"
+ },
+ "queryString": [],
+ "headers": [
+ {
+ "name": "Content-Type",
+ "value": "application/json"
+ },
+ {
+ "name": "Accept",
+ "value": "application/json"
+ },
+ {
+ "name": "Host",
+ "value": "maker.ifttt.com"
+ }
+ ],
+ "bodySize": 0,
+ "url": "https://maker.ifttt.com/trigger/random_trigger_on/with/key/bloop-blop",
+ "cookies": [],
+ "method": "GET",
+ "httpVersion": "HTTP/1.1"
+ },
+ "timings": {
+ "blocked": -1,
+ "ssl": 0,
+ "receive": 0,
+ "wait": 0,
+ "dns": 0,
+ "send": 0,
+ "connect": 0
+ },
+ "response": { },
+ "startedDateTime": "2022-04-08T16:33:37.2014031-04:00",
+ "cache": {}
+ },
+ {
+ "comment": "IFTTT - Example OFF",
+ "time": 0,
+ "serverIPAddress": "108.156.91.103",
+ "connection": "ClientPort:0;EgressPort:62999",
+ "request": {
+ "headersSize": 0,
+ "postData": {
+ "text": "",
+ "mimeType": "application/json"
+ },
+ "queryString": [],
+ "headers": [
+ {
+ "name": "Content-Type",
+ "value": "application/json"
+ },
+ {
+ "name": "Accept",
+ "value": "application/json"
+ },
+ {
+ "name": "Host",
+ "value": "maker.ifttt.com"
+ }
+ ],
+ "bodySize": 0,
+ "url": "https://maker.ifttt.com/trigger/random_trigger_off/with/key/bloop-blop",
+ "cookies": [],
+ "method": "GET",
+ "httpVersion": "HTTP/1.1"
+ },
+ "timings": {
+ "blocked": -1,
+ "ssl": 0,
+ "receive": 0,
+ "wait": 0,
+ "dns": 0,
+ "send": 0,
+ "connect": 0
+ },
+ "response": {},
+ "startedDateTime": "2022-04-08T16:33:37.2014031-04:00",
+ "cache": {}
+ }
+ ],
+ "creator": {
+ "name": "Fiddler",
+ "comment": "https://fiddler2.com",
+ "version": "5.0.20211.51073"
+ },
+ "version": "1.2"
+ }
+}
\ No newline at end of file