├── _config.yml ├── Images ├── icon.ico ├── icon.pdn ├── icon.png ├── icon-128.png ├── icon-16.ico ├── icon-16.pdn ├── icon-16.png ├── icon-32.png ├── icon-48.png ├── icon-16-white.ico ├── icon-16-white.pdn ├── icon-16-white.png ├── icon-16-white-outline.ico ├── icon-16-white-outline.pdn ├── icon-16-white-outline.png └── icon-32-high-contrast.png ├── CodeDeck.Windows ├── icon.ico ├── Resources │ ├── icon.ico │ ├── icon-16.ico │ ├── icon-16-white.ico │ └── icon-16-white-outline.ico ├── CodeDeck.Windows.csproj ├── Program.cs └── Properties │ ├── Resources.Designer.cs │ └── Resources.resx ├── CodeDeck ├── Images │ └── icon.png ├── Fonts │ ├── Ubuntu-Bold.ttf │ ├── Ubuntu-Italic.ttf │ ├── Ubuntu-Light.ttf │ ├── Ubuntu-Medium.ttf │ ├── Twemoji.Mozilla.ttf │ ├── Ubuntu-Regular.ttf │ ├── Ubuntu-BoldItalic.ttf │ ├── Ubuntu-LightItalic.ttf │ ├── Ubuntu-MediumItalic.ttf │ ├── Twemoji.Mozilla LICENSE.txt │ └── UFL.txt ├── Models │ ├── Configuration │ │ ├── PluginConfiguration.cs │ │ ├── Page.cs │ │ ├── StreamDeckConfiguration.cs │ │ ├── Profile.cs │ │ └── Key.cs │ └── FlatKeyConfiguration.cs ├── PluginLoader.cs ├── CodeDeck.csproj ├── Program.cs ├── Services │ └── ProcessMonitor.cs ├── KeyWrapper.cs └── ConfigurationProvider.cs ├── Screenshots ├── 00-streamdeck.png └── 01-notificationicon.png ├── CodeDeck.Plugins └── Plugins │ ├── Weather │ ├── Icons │ │ ├── fog.png │ │ ├── rain.png │ │ ├── snow.png │ │ ├── cloudy.png │ │ ├── sleet.png │ │ ├── fair_day.png │ │ ├── heavyrain.png │ │ ├── heavysnow.png │ │ ├── lightrain.png │ │ ├── lightsnow.png │ │ ├── clearsky_day.png │ │ ├── fair_night.png │ │ ├── heavysleet.png │ │ ├── lightsleet.png │ │ ├── clearsky_night.png │ │ ├── rainandthunder.png │ │ ├── snowandthunder.png │ │ ├── partlycloudy_day.png │ │ ├── rainshowers_day.png │ │ ├── rainshowers_night.png │ │ ├── sleetandthunder.png │ │ ├── sleetshowers_day.png │ │ ├── snowshowers_day.png │ │ ├── snowshowers_night.png │ │ ├── fair_polartwilight.png │ │ ├── heavyrainandthunder.png │ │ ├── heavysnowandthunder.png │ │ ├── lightrainandthunder.png │ │ ├── lightsnowandthunder.png │ │ ├── partlycloudy_night.png │ │ ├── sleetshowers_night.png │ │ ├── clearsky_polartwilight.png │ │ ├── heavyrainshowers_day.png │ │ ├── heavyrainshowers_night.png │ │ ├── heavysleetandthunder.png │ │ ├── heavysleetshowers_day.png │ │ ├── heavysnowshowers_day.png │ │ ├── heavysnowshowers_night.png │ │ ├── lightrainshowers_day.png │ │ ├── lightrainshowers_night.png │ │ ├── lightsleetandthunder.png │ │ ├── lightsleetshowers_day.png │ │ ├── lightsnowshowers_day.png │ │ ├── lightsnowshowers_night.png │ │ ├── heavysleetshowers_night.png │ │ ├── lightsleetshowers_night.png │ │ ├── partlycloudy_polartwilight.png │ │ ├── rainshowers_polartwilight.png │ │ ├── rainshowersandthunder_day.png │ │ ├── rainshowersandthunder_night.png │ │ ├── sleetshowers_polartwilight.png │ │ ├── sleetshowersandthunder_day.png │ │ ├── snowshowers_polartwilight.png │ │ ├── snowshowersandthunder_day.png │ │ ├── snowshowersandthunder_night.png │ │ ├── sleetshowersandthunder_night.png │ │ ├── heavyrainshowers_polartwilight.png │ │ ├── heavyrainshowersandthunder_day.png │ │ ├── heavyrainshowersandthunder_night.png │ │ ├── heavysleetshowers_polartwilight.png │ │ ├── heavysleetshowersandthunder_day.png │ │ ├── heavysnowshowers_polartwilight.png │ │ ├── heavysnowshowersandthunder_day.png │ │ ├── heavysnowshowersandthunder_night.png │ │ ├── lightrainshowers_polartwilight.png │ │ ├── lightrainshowersandthunder_day.png │ │ ├── lightrainshowersandthunder_night.png │ │ ├── lightsleetshowers_polartwilight.png │ │ ├── lightsnowshowers_polartwilight.png │ │ ├── lightssleetshowersandthunder_day.png │ │ ├── lightssnowshowersandthunder_day.png │ │ ├── heavysleetshowersandthunder_night.png │ │ ├── lightssleetshowersandthunder_night.png │ │ ├── lightssnowshowersandthunder_night.png │ │ ├── rainshowersandthunder_polartwilight.png │ │ ├── sleetshowersandthunder_polartwilight.png │ │ ├── snowshowersandthunder_polartwilight.png │ │ ├── heavyrainshowersandthunder_polartwilight.png │ │ ├── heavysleetshowersandthunder_polartwilight.png │ │ ├── heavysnowshowersandthunder_polartwilight.png │ │ ├── lightrainshowersandthunder_polartwilight.png │ │ ├── lightssleetshowersandthunder_polartwilight.png │ │ └── lightssnowshowersandthunder_polartwilight.png │ ├── Models │ │ └── Yr │ │ │ ├── DataInstant.cs │ │ │ ├── DataPeriodSummary.cs │ │ │ ├── DataPeriodDetails.cs │ │ │ ├── LocationForecast.cs │ │ │ ├── Properties.cs │ │ │ ├── DataPeriod.cs │ │ │ ├── TimeSeries.cs │ │ │ ├── TimeSeriesData.cs │ │ │ └── DataInstantDetails.cs │ ├── Weather.md │ └── YrImageTile.cs │ ├── Runner │ ├── lib │ │ └── System.Drawing.Common.dll │ ├── Runner.md │ └── Runner.cs │ ├── NissanConnect │ └── lib │ │ └── NissanConnectLib.dll │ ├── KeyboardSimulator │ ├── lib │ │ ├── InputSimulatorEx.dll │ │ └── AssemblyAttribute.dll │ └── KeyboardSimulator.cs │ ├── AudioDeviceSwitcher │ ├── lib │ │ ├── AudioSwitcher.AudioApi.dll │ │ ├── System.Drawing.Common.dll │ │ ├── System.Windows.Extensions.dll │ │ └── AudioSwitcher.AudioApi.CoreAudio.dll │ └── AudioDeviceSwitcher.cs │ ├── LogitechMice │ ├── LogitechMice.cs │ ├── Hid │ │ ├── HidPpReport.cs │ │ └── HidPp.cs │ ├── README.md │ ├── GProXSuperlightBatteryTile.cs │ └── G703BatteryTile.cs │ ├── PerformanceCounters │ ├── lib │ │ └── System.Diagnostics.PerformanceCounter.dll │ ├── README.md │ └── PerformanceCounters.cs │ ├── Lock │ ├── Lock.md │ └── Lock.cs │ ├── Counter │ ├── Counter.md │ └── Counter.cs │ ├── SteelSeriesRival3Wireless │ ├── README.md │ └── SteelSeriesRival3Wireless.cs │ ├── HyperXCloudAlphaWireless │ ├── README.md │ └── HyperXCloudAlphaWireless.cs │ ├── Clock │ ├── Clock.cs │ ├── Clock.md │ └── StopWatch.cs │ ├── HyperXCloudFlightWireless │ ├── README.md │ ├── Battery Level Reverse Engineering │ │ └── 2023.01.31 - HyperX NGenuity Battery Levels.txt │ └── HyperXCloudFlightWireless.cs │ ├── CounterStrikeGlobalOffensiveNetCon │ ├── README.md │ └── CounterStrikeGlobalOffensiveNetCon.cs │ ├── NetworkTools │ ├── README.md │ └── ExternalIpTile.cs │ ├── WebRequest │ ├── WebRequest.md │ └── WebRequest.cs │ ├── Template │ └── Template.cs │ └── MediaKeys │ └── MediaKeys.cs ├── CodeDeck.PluginAbstractions ├── SettingAttribute.cs ├── CodeDeckPlugin.cs ├── CodeDeck.PluginAbstractions.csproj └── Tile.cs ├── CodeDeck.Linux ├── Program.cs └── CodeDeck.Linux.csproj ├── .github └── FUNDING.yml ├── CodeDeck.sln └── .gitignore /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-slate 2 | -------------------------------------------------------------------------------- /Images/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/Images/icon.ico -------------------------------------------------------------------------------- /Images/icon.pdn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/Images/icon.pdn -------------------------------------------------------------------------------- /Images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/Images/icon.png -------------------------------------------------------------------------------- /Images/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/Images/icon-128.png -------------------------------------------------------------------------------- /Images/icon-16.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/Images/icon-16.ico -------------------------------------------------------------------------------- /Images/icon-16.pdn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/Images/icon-16.pdn -------------------------------------------------------------------------------- /Images/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/Images/icon-16.png -------------------------------------------------------------------------------- /Images/icon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/Images/icon-32.png -------------------------------------------------------------------------------- /Images/icon-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/Images/icon-48.png -------------------------------------------------------------------------------- /CodeDeck.Windows/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Windows/icon.ico -------------------------------------------------------------------------------- /CodeDeck/Images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck/Images/icon.png -------------------------------------------------------------------------------- /Images/icon-16-white.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/Images/icon-16-white.ico -------------------------------------------------------------------------------- /Images/icon-16-white.pdn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/Images/icon-16-white.pdn -------------------------------------------------------------------------------- /Images/icon-16-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/Images/icon-16-white.png -------------------------------------------------------------------------------- /CodeDeck/Fonts/Ubuntu-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck/Fonts/Ubuntu-Bold.ttf -------------------------------------------------------------------------------- /Screenshots/00-streamdeck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/Screenshots/00-streamdeck.png -------------------------------------------------------------------------------- /CodeDeck/Fonts/Ubuntu-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck/Fonts/Ubuntu-Italic.ttf -------------------------------------------------------------------------------- /CodeDeck/Fonts/Ubuntu-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck/Fonts/Ubuntu-Light.ttf -------------------------------------------------------------------------------- /CodeDeck/Fonts/Ubuntu-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck/Fonts/Ubuntu-Medium.ttf -------------------------------------------------------------------------------- /Images/icon-16-white-outline.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/Images/icon-16-white-outline.ico -------------------------------------------------------------------------------- /Images/icon-16-white-outline.pdn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/Images/icon-16-white-outline.pdn -------------------------------------------------------------------------------- /Images/icon-16-white-outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/Images/icon-16-white-outline.png -------------------------------------------------------------------------------- /Images/icon-32-high-contrast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/Images/icon-32-high-contrast.png -------------------------------------------------------------------------------- /CodeDeck.Windows/Resources/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Windows/Resources/icon.ico -------------------------------------------------------------------------------- /CodeDeck/Fonts/Twemoji.Mozilla.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck/Fonts/Twemoji.Mozilla.ttf -------------------------------------------------------------------------------- /CodeDeck/Fonts/Ubuntu-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck/Fonts/Ubuntu-Regular.ttf -------------------------------------------------------------------------------- /Screenshots/01-notificationicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/Screenshots/01-notificationicon.png -------------------------------------------------------------------------------- /CodeDeck/Fonts/Ubuntu-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck/Fonts/Ubuntu-BoldItalic.ttf -------------------------------------------------------------------------------- /CodeDeck/Fonts/Ubuntu-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck/Fonts/Ubuntu-LightItalic.ttf -------------------------------------------------------------------------------- /CodeDeck.Windows/Resources/icon-16.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Windows/Resources/icon-16.ico -------------------------------------------------------------------------------- /CodeDeck/Fonts/Ubuntu-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck/Fonts/Ubuntu-MediumItalic.ttf -------------------------------------------------------------------------------- /CodeDeck.Windows/Resources/icon-16-white.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Windows/Resources/icon-16-white.ico -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/fog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/fog.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/rain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/rain.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/snow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/snow.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/cloudy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/cloudy.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/sleet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/sleet.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/fair_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/fair_day.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/heavyrain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/heavyrain.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/heavysnow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/heavysnow.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/lightrain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/lightrain.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/lightsnow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/lightsnow.png -------------------------------------------------------------------------------- /CodeDeck.Windows/Resources/icon-16-white-outline.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Windows/Resources/icon-16-white-outline.ico -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/clearsky_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/clearsky_day.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/fair_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/fair_night.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/heavysleet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/heavysleet.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/lightsleet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/lightsleet.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/clearsky_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/clearsky_night.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/rainandthunder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/rainandthunder.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/snowandthunder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/snowandthunder.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/partlycloudy_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/partlycloudy_day.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/rainshowers_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/rainshowers_day.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/rainshowers_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/rainshowers_night.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/sleetandthunder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/sleetandthunder.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/sleetshowers_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/sleetshowers_day.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/snowshowers_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/snowshowers_day.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/snowshowers_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/snowshowers_night.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Runner/lib/System.Drawing.Common.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Runner/lib/System.Drawing.Common.dll -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/fair_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/fair_polartwilight.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/heavyrainandthunder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/heavyrainandthunder.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/heavysnowandthunder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/heavysnowandthunder.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/lightrainandthunder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/lightrainandthunder.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/lightsnowandthunder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/lightsnowandthunder.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/partlycloudy_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/partlycloudy_night.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/sleetshowers_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/sleetshowers_night.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/NissanConnect/lib/NissanConnectLib.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/NissanConnect/lib/NissanConnectLib.dll -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/clearsky_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/clearsky_polartwilight.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/heavyrainshowers_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/heavyrainshowers_day.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/heavyrainshowers_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/heavyrainshowers_night.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/heavysleetandthunder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/heavysleetandthunder.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/heavysleetshowers_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/heavysleetshowers_day.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/heavysnowshowers_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/heavysnowshowers_day.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/heavysnowshowers_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/heavysnowshowers_night.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/lightrainshowers_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/lightrainshowers_day.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/lightrainshowers_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/lightrainshowers_night.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/lightsleetandthunder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/lightsleetandthunder.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/lightsleetshowers_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/lightsleetshowers_day.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/lightsnowshowers_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/lightsnowshowers_day.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/lightsnowshowers_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/lightsnowshowers_night.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/KeyboardSimulator/lib/InputSimulatorEx.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/KeyboardSimulator/lib/InputSimulatorEx.dll -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/heavysleetshowers_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/heavysleetshowers_night.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/lightsleetshowers_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/lightsleetshowers_night.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/KeyboardSimulator/lib/AssemblyAttribute.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/KeyboardSimulator/lib/AssemblyAttribute.dll -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/partlycloudy_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/partlycloudy_polartwilight.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/rainshowers_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/rainshowers_polartwilight.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/rainshowersandthunder_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/rainshowersandthunder_day.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/rainshowersandthunder_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/rainshowersandthunder_night.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/sleetshowers_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/sleetshowers_polartwilight.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/sleetshowersandthunder_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/sleetshowersandthunder_day.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/snowshowers_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/snowshowers_polartwilight.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/snowshowersandthunder_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/snowshowersandthunder_day.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/snowshowersandthunder_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/snowshowersandthunder_night.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/sleetshowersandthunder_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/sleetshowersandthunder_night.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/AudioDeviceSwitcher/lib/AudioSwitcher.AudioApi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/AudioDeviceSwitcher/lib/AudioSwitcher.AudioApi.dll -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/AudioDeviceSwitcher/lib/System.Drawing.Common.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/AudioDeviceSwitcher/lib/System.Drawing.Common.dll -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/heavyrainshowers_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/heavyrainshowers_polartwilight.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/heavyrainshowersandthunder_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/heavyrainshowersandthunder_day.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/heavyrainshowersandthunder_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/heavyrainshowersandthunder_night.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/heavysleetshowers_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/heavysleetshowers_polartwilight.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/heavysleetshowersandthunder_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/heavysleetshowersandthunder_day.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/heavysnowshowers_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/heavysnowshowers_polartwilight.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/heavysnowshowersandthunder_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/heavysnowshowersandthunder_day.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/heavysnowshowersandthunder_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/heavysnowshowersandthunder_night.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/lightrainshowers_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/lightrainshowers_polartwilight.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/lightrainshowersandthunder_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/lightrainshowersandthunder_day.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/lightrainshowersandthunder_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/lightrainshowersandthunder_night.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/lightsleetshowers_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/lightsleetshowers_polartwilight.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/lightsnowshowers_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/lightsnowshowers_polartwilight.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/lightssleetshowersandthunder_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/lightssleetshowersandthunder_day.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/lightssnowshowersandthunder_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/lightssnowshowersandthunder_day.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/heavysleetshowersandthunder_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/heavysleetshowersandthunder_night.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/lightssleetshowersandthunder_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/lightssleetshowersandthunder_night.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/lightssnowshowersandthunder_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/lightssnowshowersandthunder_night.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/AudioDeviceSwitcher/lib/System.Windows.Extensions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/AudioDeviceSwitcher/lib/System.Windows.Extensions.dll -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/rainshowersandthunder_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/rainshowersandthunder_polartwilight.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/sleetshowersandthunder_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/sleetshowersandthunder_polartwilight.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/snowshowersandthunder_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/snowshowersandthunder_polartwilight.png -------------------------------------------------------------------------------- /CodeDeck.PluginAbstractions/SettingAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace CodeDeck.PluginAbstractions 2 | { 3 | [AttributeUsage(AttributeTargets.Property)] 4 | public class SettingAttribute : Attribute 5 | { 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/LogitechMice/LogitechMice.cs: -------------------------------------------------------------------------------- 1 | using CodeDeck.PluginAbstractions; 2 | 3 | namespace CodeDeck.Plugins.Plugins.LogitechMice; 4 | 5 | public partial class LogitechMice : CodeDeckPlugin 6 | { 7 | } 8 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/AudioDeviceSwitcher/lib/AudioSwitcher.AudioApi.CoreAudio.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/AudioDeviceSwitcher/lib/AudioSwitcher.AudioApi.CoreAudio.dll -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/heavyrainshowersandthunder_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/heavyrainshowersandthunder_polartwilight.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/heavysleetshowersandthunder_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/heavysleetshowersandthunder_polartwilight.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/heavysnowshowersandthunder_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/heavysnowshowersandthunder_polartwilight.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/lightrainshowersandthunder_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/lightrainshowersandthunder_polartwilight.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/lightssleetshowersandthunder_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/lightssleetshowersandthunder_polartwilight.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Icons/lightssnowshowersandthunder_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/Weather/Icons/lightssnowshowersandthunder_polartwilight.png -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/PerformanceCounters/lib/System.Diagnostics.PerformanceCounter.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hagronnestad/code-deck/HEAD/CodeDeck.Plugins/Plugins/PerformanceCounters/lib/System.Diagnostics.PerformanceCounter.dll -------------------------------------------------------------------------------- /CodeDeck.Linux/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace CodeDeck.Linux 4 | { 5 | internal class Program 6 | { 7 | static async Task Main(string[] args) 8 | { 9 | await CodeDeck.Program.Main(args); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Models/Yr/DataInstant.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace CodeDeck.Plugins.Plugins.Weather.Models.Yr 4 | { 5 | public class DataInstant 6 | { 7 | [JsonPropertyName("details")] 8 | public DataInstantDetails? Details { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Models/Yr/DataPeriodSummary.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace CodeDeck.Plugins.Plugins.Weather.Models.Yr 4 | { 5 | public class DataPeriodSummary 6 | { 7 | [JsonPropertyName("symbol_code")] 8 | public string? SymbolCode { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /CodeDeck/Models/Configuration/PluginConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CodeDeck.Models.Configuration 4 | { 5 | public class PluginConfiguration 6 | { 7 | public string? Name { get; set; } 8 | 9 | public Dictionary? Settings { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Models/Yr/DataPeriodDetails.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace CodeDeck.Plugins.Plugins.Weather.Models.Yr 4 | { 5 | public class DataPeriodDetails 6 | { 7 | [JsonPropertyName("precipitation_amount")] 8 | public double PrecipitationAmount { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Models/Yr/LocationForecast.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace CodeDeck.Plugins.Plugins.Weather.Models.Yr 4 | { 5 | public class LocationForecast 6 | { 7 | [JsonPropertyName("properties")] 8 | public Properties? Properties { get; set; } 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Lock/Lock.md: -------------------------------------------------------------------------------- 1 | # Lock 2 | 3 | A plugin that lets you lock your computer. 4 | 5 | ## LockTile 6 | 7 | This tile locks the computer when the associated key is pressed. 8 | 9 | ### Settings 10 | 11 | No settings. 12 | 13 | ### Example: 14 | 15 | ```json 16 | { 17 | "Plugin": "Lock", 18 | "Tile": "LockTile" 19 | } 20 | ``` 21 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Models/Yr/Properties.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace CodeDeck.Plugins.Plugins.Weather.Models.Yr 5 | { 6 | public class Properties 7 | { 8 | [JsonPropertyName("timeseries")] 9 | public List? Timeseries { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Counter/Counter.md: -------------------------------------------------------------------------------- 1 | # Counter 2 | 3 | This plugin is an example plugin. 4 | 5 | ## CounterTile 6 | 7 | The CounterTile increments a counter every time the key is pressed. 8 | 9 | ### Settings 10 | 11 | No settings. 12 | 13 | ### Example: 14 | 15 | ```json 16 | { 17 | "Plugin": "Counter", 18 | "Tile": "CounterTile", 19 | } 20 | ``` 21 | -------------------------------------------------------------------------------- /CodeDeck.PluginAbstractions/CodeDeckPlugin.cs: -------------------------------------------------------------------------------- 1 | namespace CodeDeck.PluginAbstractions 2 | { 3 | /// 4 | /// The base class for all plugins 5 | /// 6 | public abstract class CodeDeckPlugin 7 | { 8 | public static string PluginPath { get; set; } 9 | 10 | public static Dictionary? Settings { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /CodeDeck.Linux/CodeDeck.Linux.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | disable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Models/Yr/DataPeriod.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace CodeDeck.Plugins.Plugins.Weather.Models.Yr 4 | { 5 | public class DataPeriod 6 | { 7 | [JsonPropertyName("summary")] 8 | public DataPeriodSummary? Summary { get; set; } 9 | 10 | [JsonPropertyName("details")] 11 | public DataPeriodDetails? Details { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Models/Yr/TimeSeries.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace CodeDeck.Plugins.Plugins.Weather.Models.Yr 5 | { 6 | public class TimeSeries 7 | { 8 | [JsonPropertyName("time")] 9 | public DateTimeOffset Time { get; set; } 10 | 11 | [JsonPropertyName("data")] 12 | public TimeSeriesData? Data { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /CodeDeck.PluginAbstractions/CodeDeck.PluginAbstractions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /CodeDeck/Models/FlatKeyConfiguration.cs: -------------------------------------------------------------------------------- 1 | using CodeDeck.Models.Configuration; 2 | 3 | namespace CodeDeck.Models 4 | { 5 | public class FlatKeyConfiguration 6 | { 7 | public Key Key { get; set; } 8 | public Page Page { get; set; } 9 | public Profile Profile { get; set; } 10 | 11 | public FlatKeyConfiguration(Key key, Page page, Profile profile) 12 | { 13 | Key = key; 14 | Page = page; 15 | Profile = profile; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /CodeDeck/Models/Configuration/Page.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CodeDeck.Models.Configuration 4 | { 5 | /// 6 | /// Configuration class for a Page 7 | /// 8 | public class Page 9 | { 10 | public const string PAGE_DEFAULT_NAME = "DefaultPage"; 11 | 12 | public string Name { get; set; } = PAGE_DEFAULT_NAME; 13 | 14 | public string? ProcessStartedTrigger { get; set; } 15 | public bool ProcessStartedTriggerNavigatePreviousOnExit { get; set; } = false; 16 | 17 | public List Keys { get; set; } = new(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Models/Yr/TimeSeriesData.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace CodeDeck.Plugins.Plugins.Weather.Models.Yr 4 | { 5 | public class TimeSeriesData 6 | { 7 | [JsonPropertyName("instant")] 8 | public DataInstant? Instant { get; set; } 9 | 10 | [JsonPropertyName("next_1_hours")] 11 | public DataPeriod? Next1Hours { get; set; } 12 | 13 | [JsonPropertyName("next_6_hours")] 14 | public DataPeriod? Next6Hours { get; set; } 15 | 16 | [JsonPropertyName("next_12_hours")] 17 | public DataPeriod? Next12Hours { get; set; } 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /CodeDeck/Models/Configuration/StreamDeckConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CodeDeck.Models.Configuration 4 | { 5 | /// 6 | /// Configuration class for a complete Deck 7 | /// 8 | public class StreamDeckConfiguration 9 | { 10 | public string? DevicePath { get; set; } 11 | 12 | public int Brightness { get; set; } = 75; 13 | 14 | public string? FallbackFont { get; set; } = "Twemoji Mozilla"; 15 | 16 | public List Plugins { get; set; } = new(); 17 | 18 | public List Profiles { get; set; } = new(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /CodeDeck/Models/Configuration/Profile.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CodeDeck.Models.Configuration 4 | { 5 | /// 6 | /// Configuration class for a Profile 7 | /// 8 | public class Profile 9 | { 10 | public const string PROFILE_DEFAULT_NAME = "DefaultProfile"; 11 | public const string PROFILE_TYPE_NORMAL = "Normal"; 12 | public const string PROFILE_TYPE_LOCK_SCREEN = "LockScreen"; 13 | 14 | public string ProfileType { get; set; } = PROFILE_TYPE_NORMAL; 15 | 16 | public string Name { get; set; } = PROFILE_DEFAULT_NAME; 17 | public List Pages { get; set; } = new(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Models/Yr/DataInstantDetails.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace CodeDeck.Plugins.Plugins.Weather.Models.Yr 4 | { 5 | public class DataInstantDetails 6 | { 7 | [JsonPropertyName("air_pressure_at_sea_level")] 8 | public double AirPressureAtSeaLevel { get; set; } 9 | 10 | [JsonPropertyName("air_temperature")] 11 | public double AirTemperature { get; set; } 12 | 13 | [JsonPropertyName("wind_from_direction")] 14 | public double WindFromDirection { get; set; } 15 | 16 | [JsonPropertyName("wind_speed")] 17 | public double WindSpeed { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [hagronnestad] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Counter/Counter.cs: -------------------------------------------------------------------------------- 1 | using CodeDeck.PluginAbstractions; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace CodeDeck.Plugins.Plugins.Counter; 6 | 7 | public class Counter : CodeDeckPlugin 8 | { 9 | private static int _cnt = 0; 10 | 11 | public class CounterTile : Tile 12 | { 13 | public override async Task Init(CancellationToken cancellationToken) 14 | { 15 | Text = $"Counter\n{_cnt}"; 16 | 17 | await Task.CompletedTask; 18 | } 19 | 20 | public override async Task OnTilePressDown(CancellationToken cancellationToken) 21 | { 22 | _cnt++; 23 | Text = $"Counter\n{_cnt}"; 24 | 25 | await Task.CompletedTask; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Lock/Lock.cs: -------------------------------------------------------------------------------- 1 | using CodeDeck.PluginAbstractions; 2 | using System.Runtime.InteropServices; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace CodeDeck.Plugins.Plugins.Counter; 7 | 8 | public class Lock : CodeDeckPlugin 9 | { 10 | public class LockTile : Tile 11 | { 12 | [DllImport("user32.dll", SetLastError = true)] 13 | static extern bool LockWorkStation(); 14 | 15 | public override Task Init(CancellationToken cancellationToken) 16 | { 17 | Text = "🔒"; 18 | FontSize = 50; 19 | return base.Init(cancellationToken); 20 | } 21 | 22 | public override Task OnTilePressUp(CancellationToken cancellationToken) 23 | { 24 | LockWorkStation(); 25 | return base.OnTilePressDown(cancellationToken); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/LogitechMice/Hid/HidPpReport.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace CodeDeck.Plugins.Plugins.LogitechMice.Hid; 4 | 5 | public class HidPpReport 6 | { 7 | public byte ReportId; 8 | public byte DeviceIndex; 9 | public byte FeatureIndex; 10 | public byte FuncIndexAndSoftwareId; 11 | public byte[] Params = new byte[20 - 4]; 12 | 13 | public byte FuncIndex => (byte) (FuncIndexAndSoftwareId >> 4); 14 | public byte SoftwareId => (byte) (FuncIndexAndSoftwareId & 0x0F); 15 | 16 | public static HidPpReport? FromBytes(byte[] bytes) 17 | { 18 | if (bytes == null || bytes.Length < 20) return null; 19 | 20 | using var ms = new MemoryStream(bytes); 21 | using var r = new BinaryReader(ms); 22 | 23 | var report = new HidPpReport 24 | { 25 | ReportId = r.ReadByte(), 26 | DeviceIndex = r.ReadByte(), 27 | FeatureIndex = r.ReadByte(), 28 | FuncIndexAndSoftwareId = r.ReadByte(), 29 | Params = r.ReadBytes(20 - 4) 30 | }; 31 | return report; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/SteelSeriesRival3Wireless/README.md: -------------------------------------------------------------------------------- 1 | # SteelSeriesRival3Wireless 2 | 3 | This plugin shows the battery level of a SteelSeries Rival 3 Wireless mouse. 4 | 5 | - [SteelSeriesRival3Wireless](#steelseriesrival3wireless) 6 | - [BatteryTile](#batterytile) 7 | - [Settings](#settings) 8 | - [Examples](#examples) 9 | 10 | 11 | ## BatteryTile 12 | 13 | Shows the battery level of a SteelSeries Rival 3 Wireless mouse. 14 | 15 | ### Settings 16 | 17 | | Setting | Default | Description | 18 | | ------------------ | ------------ | --------------------------------------------------------------------- | 19 | | Format | `🔋\n{0}%` | Text format to use when the mouse is connected. | 20 | | FormatDisconnected | `🖱\n❌` | Text format to use when the mouse is disconnected. | 21 | | Interval | `600 000 ms` | Interval at which to refresh the battery value. Default `10` minutes. | 22 | 23 | ### Examples 24 | 25 | ```json 26 | { 27 | "Plugin": "SteelSeriesRival3Wireless", 28 | "Tile": "BatteryTile", 29 | "Settings": { 30 | "Format": "🖱 🔋\n{0}%", 31 | "Interval": "30000" 32 | } 33 | } 34 | ``` 35 | -------------------------------------------------------------------------------- /CodeDeck.Windows/CodeDeck.Windows.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WinExe 5 | net8.0-windows7.0 6 | true 7 | enable 8 | enable 9 | True 10 | CodeDeck.Windows.Program 11 | icon.ico 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | True 29 | True 30 | Resources.resx 31 | 32 | 33 | 34 | 35 | 36 | ResXFileCodeGenerator 37 | Resources.Designer.cs 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/HyperXCloudAlphaWireless/README.md: -------------------------------------------------------------------------------- 1 | # HyperXCloudAlphaWireless 2 | 3 | This plugin shows the battery level of a HyperX Cloud Alpha Wireless headset. 4 | 5 | - [HyperXCloudAlphaWireless](#hyperxcloudalphawireless) 6 | - [BatteryTile](#batterytile) 7 | - [Settings](#settings) 8 | - [Examples](#examples) 9 | 10 | 11 | ## BatteryTile 12 | 13 | Shows the battery level of a HyperX Cloud Alpha Wireless headset. 14 | 15 | ### Settings 16 | 17 | | Setting | Default | Description | 18 | | --------------------- | ------------ | ------------------------------------------------------------------------------------------ | 19 | | Format | `🔋\n{0}%` | Text format to use when headset is in normal use. | 20 | | FormatDisconnected | `🎧\n❌` | Text format to use when the headset is disconnected. | 21 | | Interval | `600 000 ms` | Interval at which to query the headset. Default `10` minutes. | 22 | 23 | ### Examples 24 | 25 | ```json 26 | { 27 | "Plugin": "HyperXCloudAlphaWireless", 28 | "Tile": "BatteryTile", 29 | "Settings": { 30 | "Format": "🎧\n{0}%", 31 | "Interval": "30000" 32 | } 33 | } 34 | ``` 35 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Clock/Clock.cs: -------------------------------------------------------------------------------- 1 | using CodeDeck.PluginAbstractions; 2 | using System; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace CodeDeck.Plugins.Plugins.Clock; 7 | 8 | public partial class Clock : CodeDeckPlugin 9 | { 10 | public partial class DigitalClockTile : Tile 11 | { 12 | [Setting] public string? Format { get; set; } 13 | [Setting] public int? Interval { get; set; } 14 | 15 | public override async Task Init(CancellationToken cancellationToken) 16 | { 17 | _ = Task.Run(() => UpdateTile(cancellationToken), cancellationToken); 18 | 19 | await Task.CompletedTask; 20 | } 21 | 22 | private async Task UpdateTile(CancellationToken cancellationToken) 23 | { 24 | for (; ; ) 25 | { 26 | if (cancellationToken.IsCancellationRequested) 27 | { 28 | return; 29 | } 30 | 31 | Text = DateTime.Now.ToString(Format ?? "HH\\:mm"); 32 | await Task.Delay(Interval ?? 1000, cancellationToken); 33 | } 34 | } 35 | 36 | public override async Task OnTilePressDown(CancellationToken cancellationToken) 37 | { 38 | Text = DateTime.Now.ToString(Format ?? "HH\\:mm"); 39 | 40 | await Task.CompletedTask; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/HyperXCloudFlightWireless/README.md: -------------------------------------------------------------------------------- 1 | # HyperXCloudFlightWireless 2 | 3 | This plugin shows the battery level of a HyperX Cloud Flight Wireless headset. 4 | 5 | - [HyperXCloudFlightWireless](#hyperxcloudflightwireless) 6 | - [BatteryTile](#batterytile) 7 | - [Settings](#settings) 8 | - [Examples](#examples) 9 | 10 | 11 | ## BatteryTile 12 | 13 | Shows the battery level of a HyperX Cloud Flight Wireless headset. 14 | 15 | ### Settings 16 | 17 | | Setting | Default | Description | 18 | | --------------------- | ------------ | ------------------------------------------------------------------------------------------ | 19 | | Format | `🔋\n{0}%` | Text format to use when headset is in normal use. | 20 | | FormatCharging | `⚡\n{0}%` | Text format to use when headset is charging. | 21 | | FormatDisconnected | `🎧\n❌` | Text format to use when the headset is disconnected. | 22 | | Interval | `600 000 ms` | Interval at which to query the headset. Default `10` minutes. | 23 | | UseNGenuityBatteryMap | `true` | Maps the raw battery values to a more linear scale used by the official NGenuity software. | 24 | 25 | ### Examples 26 | 27 | ```json 28 | { 29 | "Plugin": "HyperXCloudFlightWireless", 30 | "Tile": "BatteryTile", 31 | "Settings": { 32 | "Format": "🎧\n{0}%", 33 | "Interval": "30000" 34 | } 35 | } 36 | ``` 37 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/HyperXCloudFlightWireless/Battery Level Reverse Engineering/2023.01.31 - HyperX NGenuity Battery Levels.txt: -------------------------------------------------------------------------------- 1 | // Mapping of raw USB battery levels to the battery level reported by NGenuity software 2 | // These values are collected manually 3 | // The level reported by NGenuity software **should** provide a more linear value 4 | 5 | 100 => 100 6 | 99 => 100 7 | 98 => 100 8 | 97 => 100 9 | 96 => 100 10 | 95 => 100 11 | 94 => 100 12 | 93 => 100 13 | 92 => 100 14 | 91 => 100 15 | 90 => 100 16 | 89 => 100 17 | 88 => 100 18 | 87 => 100 19 | 86 => 100 20 | 85 => 100 21 | 84 => 100 22 | 83 => 100 23 | 82 => 100 24 | 81 => 100 25 | 80 => 100 26 | 79 => 100 27 | 78 => 95 28 | 77 => 95 29 | 76 => 95 30 | 75 => 95 31 | 74 => 95 32 | 73 => 90 33 | 72 => 90 34 | 71 => 90 35 | 70 => 90 36 | 69 => 90 37 | 68 => 85 38 | 67 => 85 39 | 66 => 85 40 | 65 => 85 41 | 64 => 85 42 | 63 => 80 43 | 62 => 80 44 | 61 => 80 45 | 60 => 80 46 | 59 => 80 47 | 58 => 80 48 | 57 => 75 49 | 56 => 75 50 | 55 => 75 51 | 54 => 75 52 | 53 => 75 53 | 52 => 70 54 | 51 => 70 55 | 50 => 70 56 | 49 => 70 57 | 48 => 65 58 | 47 => 65 59 | 46 => 60 60 | 45 => 60 61 | 44 => 60 62 | 43 => 60 63 | 42 => 60 64 | 41 => 55 65 | 40 => 55 66 | 39 => 50 67 | 38 => 45 68 | 37 => 45 69 | 36 => 40 70 | 35 => 40 71 | 34 => 35 72 | 33 => 35 73 | 32 => 30 74 | 31 => 30 75 | 30 => 25 76 | 29 => 25 77 | 28 => 20 78 | 27 => 20 79 | 26 => 20 80 | 25 => 20 81 | 24 => 20 82 | 23 => 20 83 | 22 => 15 84 | 21 => 15 85 | 20 => 15 86 | 19 => 15 87 | 18 => 15 88 | 17 => 15 89 | 16 => 10 90 | 15 => 10 91 | 14 => 10 92 | 13 => 10 93 | 12 => 10 94 | 11 => 10 95 | 10 => 5 96 | 09 => 5 97 | 08 => 5 98 | 07 => 5 99 | 06 => 5 100 | 05 => 5 101 | 04 => 0 102 | 03 => 0 103 | 02 => 0 104 | 01 => 0 105 | 00 => 0 106 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/PerformanceCounters/README.md: -------------------------------------------------------------------------------- 1 | # PerformanceCounters 2 | 3 | This plugin implements tiles to show current CPU, RAM and GPU usage. 4 | 5 | - [PerformanceCounters](#performancecounters) 6 | - [CpuUsageTile](#cpuusagetile) 7 | - [Settings](#settings) 8 | - [Examples](#examples) 9 | - [MemoryUsageTile](#memoryusagetile) 10 | - [Settings](#settings-1) 11 | - [Examples](#examples-1) 12 | - [GpuUsageTile](#gpuusagetile) 13 | - [Settings](#settings-2) 14 | - [Examples](#examples-2) 15 | 16 | 17 | ## CpuUsageTile 18 | 19 | This tile shows the current CPU usage. 20 | 21 | ### Settings 22 | 23 | | Setting | Default | Description | 24 | | ------- | ------------ | ------------------------- | 25 | | Format | `CPU\n{0} %` | The string format to use. | 26 | 27 | ### Examples 28 | 29 | ```json 30 | { 31 | "Plugin": "PerformanceCounters", 32 | "Tile": "CpuUsageTile", 33 | "Settings": { 34 | "Format": "🧠\n{0} %" 35 | } 36 | } 37 | ``` 38 | 39 | 40 | ## MemoryUsageTile 41 | 42 | This tile shows the current RAM usage. 43 | 44 | ### Settings 45 | 46 | | Setting | Default | Description | 47 | | ------- | ------------ | ------------------------- | 48 | | Format | `RAM\n{0} %` | The string format to use. | 49 | 50 | ### Examples 51 | 52 | ```json 53 | { 54 | "Plugin": "PerformanceCounters", 55 | "Tile": "MemoryUsageTile" 56 | } 57 | ``` 58 | 59 | 60 | ## GpuUsageTile 61 | 62 | This tile shows the current GPU usage. 63 | 64 | ### Settings 65 | 66 | | Setting | Default | Description | 67 | | ------- | ------------ | ------------------------- | 68 | | Format | `GPU\n{0} %` | The string format to use. | 69 | 70 | ### Examples 71 | 72 | ```json 73 | { 74 | "Plugin": "PerformanceCounters", 75 | "Tile": "GpuUsageTile" 76 | } 77 | ``` 78 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/CounterStrikeGlobalOffensiveNetCon/README.md: -------------------------------------------------------------------------------- 1 | # CounterStrikeGlobalOffensiveNetCon 2 | 3 | This is a plugin to interact with the CS:GO console. 4 | 5 | - [CounterStrikeGlobalOffensiveNetCon](#counterstrikeglobaloffensivenetcon) 6 | - [Usage](#usage) 7 | - [ExecuteCommand](#executecommand) 8 | - [Settings](#settings) 9 | - [Examples](#examples) 10 | - [Good Game Key](#good-game-key) 11 | - [Set In Game Volume To 10% Key](#set-in-game-volume-to-10-key) 12 | 13 | ## Usage 14 | Use launch option `-netconport ` on CS:GO to set a port number for netcon to listen on. 15 | 16 | ## ExecuteCommand 17 | Send a command to the CS:GO console. 18 | 19 | ### Settings 20 | | Setting | Default | Description | 21 | | ---------- | ----------- | ---------------------------------------------------------------------- | 22 | | HostName | `127.0.0.1` | The host to connect to. Can be a hostname or an IP address. | 23 | | NetConPort | `0` | This is the port number from the `-netconport ` launch option. | 24 | | Command | `null` | The command you want to send to the console. | 25 | 26 | ### Examples 27 | 28 | #### Good Game Key 29 | ```json 30 | { 31 | "Text": "GG", 32 | "FontSize": 40, 33 | "FontBold": true, 34 | "Plugin": "CounterStrikeGlobalOffensiveNetCon", 35 | "Tile": "ExecuteCommand", 36 | "Settings": { 37 | "NetConPort": "1337", 38 | "Command": "say gg\n" 39 | } 40 | } 41 | ``` 42 | 43 | #### Set In Game Volume To 10% Key 44 | ```json 45 | { 46 | "Text": "🔊\n10%", 47 | "FontSize": 25, 48 | "FontBold": true, 49 | "Plugin": "CounterStrikeGlobalOffensiveNetCon", 50 | "Tile": "ExecuteCommand", 51 | "Settings": { 52 | "NetConPort": "1337", 53 | "Command": "volume 0.1\n" 54 | } 55 | } 56 | ``` 57 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/CounterStrikeGlobalOffensiveNetCon/CounterStrikeGlobalOffensiveNetCon.cs: -------------------------------------------------------------------------------- 1 | using CodeDeck.PluginAbstractions; 2 | using System; 3 | using System.IO; 4 | using System.Net.Sockets; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | public class CounterStrikeGlobalOffensiveNetCon : CodeDeckPlugin 10 | { 11 | public class ExecuteCommand : Tile 12 | { 13 | [Setting] public string HostName { get; set; } = "127.0.0.1"; 14 | [Setting] public int NetConPort { get; set; } 15 | [Setting] public string? Command { get; set; } 16 | 17 | public override async Task Init(CancellationToken cancellationToken) 18 | { 19 | Text = $"CS:GO\nExecCmd"; 20 | await Task.CompletedTask; 21 | } 22 | 23 | public override async Task OnTilePressUp(CancellationToken cancellationToken) 24 | { 25 | if (Command is null) return; 26 | 27 | try 28 | { 29 | ShowIndicator = true; 30 | 31 | // Give ShowIndicator time to show for some visual feedback 32 | await Task.Delay(100, cancellationToken); 33 | 34 | // Connect to netcon and send the command 35 | using var c = new TcpClient(); 36 | await c.ConnectAsync(HostName, NetConPort, cancellationToken); 37 | using var s = c.GetStream(); 38 | using var tw = new StreamWriter(s); 39 | tw.AutoFlush = true; 40 | await tw.WriteLineAsync(new StringBuilder(Command), cancellationToken); 41 | c.Close(); 42 | } 43 | catch (Exception e) 44 | { 45 | Console.WriteLine($"Exception: {e.Message}"); 46 | } 47 | finally 48 | { 49 | ShowIndicator = false; 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/AudioDeviceSwitcher/AudioDeviceSwitcher.cs: -------------------------------------------------------------------------------- 1 | using AudioSwitcher.AudioApi; 2 | using AudioSwitcher.AudioApi.CoreAudio; 3 | using AudioSwitcher.AudioApi.Session; 4 | using CodeDeck.PluginAbstractions; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Media; 9 | using System.Runtime.Versioning; 10 | using System.Threading; 11 | using System.Threading.Tasks; 12 | 13 | namespace CodeDeck.Plugins.Plugins.AudioDeviceSwitcher 14 | { 15 | [SupportedOSPlatform("windows")] 16 | public class AudioDeviceSwitcher : CodeDeckPlugin 17 | { 18 | private static CoreAudioController? _audioController; 19 | private static IEnumerable? _devices; 20 | 21 | static AudioDeviceSwitcher() 22 | { 23 | _audioController = new CoreAudioController(); 24 | _devices = _audioController.GetPlaybackDevices(DeviceState.Active); 25 | } 26 | 27 | public class AudioDeviceSwitcherTile : Tile 28 | { 29 | [Setting] public string? Device { get; set; } 30 | 31 | private CoreAudioDevice? _device = null; 32 | 33 | public override async Task Init(CancellationToken cancellationToken) 34 | { 35 | if (Device == null) return; 36 | if (_devices == null) throw new Exception("No devices found!"); 37 | 38 | _device = _devices.FirstOrDefault(x => x.FullName == Device); 39 | if (_device == null) throw new Exception($"Device; '{Device}' not found."); 40 | 41 | await Task.CompletedTask; 42 | } 43 | 44 | public override async Task OnTilePressDown(CancellationToken cancellationToken) 45 | { 46 | if (_device == null) return; 47 | _device.SetAsDefault(); 48 | SystemSounds.Beep.Play(); 49 | await Task.CompletedTask; 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /CodeDeck/PluginLoader.cs: -------------------------------------------------------------------------------- 1 | using CodeDeck.Models.Configuration; 2 | using Microsoft.CodeAnalysis; 3 | using Microsoft.Extensions.Logging; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Linq; 7 | 8 | namespace CodeDeck 9 | { 10 | public class PluginLoader 11 | { 12 | private readonly ILogger _logger; 13 | private readonly StreamDeckConfiguration _streamDeckConfiguration; 14 | 15 | public List LoadedPlugins = new(); 16 | 17 | public PluginLoader(ILogger logger, ConfigurationProvider configurationProvider) 18 | { 19 | _logger = logger; 20 | _streamDeckConfiguration = configurationProvider.LoadedConfiguration; 21 | } 22 | 23 | public void LoadAllPlugins() 24 | { 25 | LoadedPlugins = GetAllPlugins(); 26 | LogPlugins(LoadedPlugins); 27 | } 28 | 29 | public List GetAllPlugins() 30 | { 31 | var pluginDirectories = Directory 32 | .GetDirectories("Plugins") 33 | .Select(x => Path.GetFullPath(x)); 34 | 35 | if (!pluginDirectories.Any()) return new(); 36 | 37 | var plugins = pluginDirectories.Select( 38 | x => new Plugin(_logger, x, GetPluginConfiguration(Path.GetFileName(x)))); 39 | 40 | return plugins.ToList(); 41 | } 42 | 43 | private PluginConfiguration? GetPluginConfiguration(string pluginName) 44 | { 45 | var config = _streamDeckConfiguration 46 | .Plugins? 47 | .FirstOrDefault(x => x.Name?.ToLower() == pluginName.ToLower()); 48 | 49 | return config; 50 | } 51 | 52 | public void LogPlugins(List plugins) 53 | { 54 | if (!plugins.Any()) 55 | { 56 | _logger.LogWarning("No plugins found!"); 57 | return; 58 | } 59 | 60 | _logger.LogInformation($"Found plugin(s): {string.Join(", ", plugins.Select(x => x.Name))}"); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/Weather.md: -------------------------------------------------------------------------------- 1 | # Weather 2 | The Weather plugin contains a collection of tiles for displaying weather data. 3 | 4 | 5 | - [Weather](#weather) 6 | - [YrImageTile](#yrimagetile) 7 | - [Usage](#usage) 8 | - [Settings](#settings) 9 | - [Example Config](#example-config) 10 | - [Interval](#interval) 11 | - [Lat / Lon](#lat--lon) 12 | - [Period](#period) 13 | 14 | 15 | ## YrImageTile 16 | This tile uses the [yr.no](https://developer.yr.no/) weather API to show an image that describes the upcoming weather. 17 | 18 | 19 | ## Usage 20 | The tile can be configured to show an image that describes the weather in the next 1, 6 or 12 hours. 21 | 22 | 23 | ### Settings 24 | 25 | | Setting | Default | Description | 26 | | -------- | ------------ | ---------------------------------------------------------- | 27 | | Interval | `600 000` ms | Update interval. Default is 10 minutes (`10 * 60 * 1000`). | 28 | | Lat | `0` | The latitude of the location to fetch weather data for. | 29 | | Lon | `0` | The longitude of the location to fetch weather data for. | 30 | | Period | `null` | The period of the weather summary. | 31 | 32 | 33 | 34 | ### Example Config 35 | 36 | ```json 37 | { 38 | "Plugin": "Weather", 39 | "Tile": "YrImageTile", 40 | "Text": "Next Hour", 41 | "FontSize": 14, 42 | "TextOffsetY": 20, 43 | "TextColor": "#ffffff", 44 | "ImagePadding": 7, 45 | "ImageOffsetX": 0, 46 | "ImageOffsetY": -8, 47 | "Settings": { 48 | "Lat": "59,1484", 49 | "Lon": "5,2611", 50 | "Period": "Next1Hours" 51 | } 52 | } 53 | ``` 54 | 55 | 56 | #### Interval 57 | The time between requesting an update from the Weather API. The interval must be set to a reasonable value. 58 | 59 | 60 | #### Lat / Lon 61 | The location to fetch weather data for. 62 | 63 | 64 | #### Period 65 | The period of the weather summary. This is based on the available data provided by the API. 66 | 67 | Possible values are: 68 | - `Next1Hours` (This is the default period if no period is specified.) 69 | - `Next6Hours` 70 | - `Next12Hours` 71 | -------------------------------------------------------------------------------- /CodeDeck.Windows/Program.cs: -------------------------------------------------------------------------------- 1 | using CodeDeck.Windows.Properties; 2 | using System.Diagnostics; 3 | 4 | namespace CodeDeck.Windows 5 | { 6 | internal class Program 7 | { 8 | private static NotifyIcon _ni = new(); 9 | private static ContextMenuStrip _cms = new(); 10 | 11 | public static void Main(string[] args) 12 | { 13 | _ = CodeDeck.Program.Main(args); 14 | 15 | var f = new Form() 16 | { 17 | Visible = false, 18 | ShowInTaskbar = false, 19 | WindowState = FormWindowState.Minimized 20 | }; 21 | f.Resize += (s, e) => { if (f.WindowState == FormWindowState.Minimized) f.Hide(); }; 22 | 23 | _ni.Visible = true; 24 | _ni.ContextMenuStrip = _cms; 25 | _ni.Icon = Resources.icon_16_white_outline; 26 | _ni.MouseUp += _ni_MouseClick; 27 | 28 | var mnuItemOpenConf = _cms.Items.Add("Open Configuration"); 29 | mnuItemOpenConf.Click += (s, e) => 30 | { 31 | try 32 | { 33 | Process.Start(new ProcessStartInfo() 34 | { 35 | FileName = ConfigurationProvider.ConfigFile, 36 | UseShellExecute = true 37 | }); 38 | } 39 | catch (Exception ex) 40 | { 41 | MessageBox.Show($"Could not open configuration file. Error: \"{ex.Message}\"", 42 | "Open Configuration", MessageBoxButtons.OK, MessageBoxIcon.Error); 43 | } 44 | }; 45 | 46 | var mnuItemExit = _cms.Items.Add("Exit"); 47 | mnuItemExit.Click += (s, e) => 48 | { 49 | _ni.Visible = false; 50 | CodeDeck.Program.StopHost(); 51 | f.Close(); 52 | Application.Exit(); 53 | }; 54 | 55 | Application.Run(f); 56 | } 57 | 58 | private static void _ni_MouseClick(object? sender, MouseEventArgs e) 59 | { 60 | if (e.Button == MouseButtons.Right) 61 | { 62 | _cms.Show(); 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/LogitechMice/Hid/HidPp.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace CodeDeck.Plugins.Plugins.LogitechMice.Hid; 6 | 7 | public class HidPp 8 | { 9 | public const byte SOFTWARE_ID = 0x08; 10 | public const byte MESSAGE_TYPE_SHORT = 0x10; 11 | public const byte MESSAGE_TYPE_LONG = 0x11; 12 | 13 | public const ushort HIDPP_FEATURE_ROOT = 0x0000; 14 | public const ushort HIDPP_FEATURE_BATTERY_VOLTAGE = 0x1001; 15 | public const ushort HIDPP_FEATURE_UNIFIED_BATTERY = 0x1004; 16 | 17 | public const byte HIDPP_PAGE_ROOT_IDX = 0x00; 18 | public const byte FUNCTION_ROOT_GET_FEATURE = 0x00; 19 | public const byte FUNCTION_ROOT_GET_PROTOCOL_VERSION = 0x10; 20 | public const byte CMD_BATTERY_VOLTAGE_GET_BATTERY_VOLTAGE = 0x00; 21 | 22 | 23 | public static byte[] CreateGetFeatureIndexPacket(byte device, ushort feature) 24 | { 25 | var packet = new List 26 | { 27 | MESSAGE_TYPE_LONG, 28 | device, 29 | HIDPP_PAGE_ROOT_IDX, 30 | (byte)(FUNCTION_ROOT_GET_FEATURE | SOFTWARE_ID) 31 | }; 32 | var featureBytes = BitConverter.GetBytes(feature).Reverse(); 33 | packet.AddRange(featureBytes); 34 | return packet.ToArray(); 35 | } 36 | 37 | public static byte[] CreateGetBatteryInformationPacket(byte device, byte featureIndex) 38 | { 39 | return new byte[] 40 | { 41 | MESSAGE_TYPE_LONG, 42 | device, 43 | featureIndex, 44 | (byte)(CMD_BATTERY_VOLTAGE_GET_BATTERY_VOLTAGE | SOFTWARE_ID) 45 | }; 46 | } 47 | 48 | public static byte[] CreateGetUnifiedBatteryPacket(byte deviceIndex, byte featureIndex) 49 | { 50 | byte funcAndId = (byte) ((1 << 4) | SOFTWARE_ID); // FuncIndex=1 51 | return new byte[] { MESSAGE_TYPE_LONG, deviceIndex, featureIndex, funcAndId }; 52 | } 53 | 54 | public static byte[] CreatePingPacket(byte device) 55 | { 56 | return new byte[] 57 | { 58 | MESSAGE_TYPE_LONG, 59 | device, 60 | HIDPP_PAGE_ROOT_IDX, 61 | (byte)(FUNCTION_ROOT_GET_PROTOCOL_VERSION | SOFTWARE_ID), 62 | 0x00, 63 | 0x00, 64 | 0xAA 65 | }; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /CodeDeck/Fonts/Twemoji.Mozilla LICENSE.txt: -------------------------------------------------------------------------------- 1 | # https://github.com/twitter/twemoji 2 | 3 | 4 | # https://github.com/mozilla/twemoji-colr 5 | 6 | ## License for the Code 7 | 8 | Copyright 2016-2018, Mozilla Foundation 9 | 10 | Licensed under the Apache License, Version 2.0 (the "License"); 11 | you may not use this file except in compliance with the License. 12 | You may obtain a copy of the License at 13 | 14 | http://www.apache.org/licenses/LICENSE-2.0 15 | 16 | Unless required by applicable law or agreed to in writing, software 17 | distributed under the License is distributed on an "AS IS" BASIS, 18 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | See the License for the specific language governing permissions and 20 | limitations under the License. 21 | 22 | 23 | 24 | ## License for the Visual Design 25 | 26 | The Emoji art in the twe-svg.zip archive comes from [Twemoji](https://twitter.github.io/twemoji), 27 | and is used and redistributed under the CC-BY-4.0 [license terms](https://github.com/twitter/twemoji#license) 28 | offered by the Twemoji project. 29 | 30 | ### Creative Commons Attribution 4.0 International (CC BY 4.0) 31 | https://creativecommons.org/licenses/by/4.0/legalcode 32 | or for the human readable summary: https://creativecommons.org/licenses/by/4.0/ 33 | 34 | 35 | #### You are free to: 36 | **Share** — copy and redistribute the material in any medium or format 37 | 38 | **Adapt** — remix, transform, and build upon the material for any purpose, even commercially. 39 | 40 | The licensor cannot revoke these freedoms as long as you follow the license terms. 41 | 42 | 43 | #### Under the following terms: 44 | **Attribution** — You must give appropriate credit, provide a link to the license, 45 | and indicate if changes were made. 46 | You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. 47 | 48 | **No additional restrictions** — You may not apply legal terms or **technological measures** 49 | that legally restrict others from doing anything the license permits. 50 | 51 | #### Notices: 52 | You do not have to comply with the license for elements of the material in the public domain 53 | or where your use is permitted by an applicable exception or limitation. No warranties are given. 54 | The license may not give you all of the permissions necessary for your intended use. 55 | For example, other rights such as publicity, privacy, or moral rights may limit how you use the material. 56 | -------------------------------------------------------------------------------- /CodeDeck/CodeDeck.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | disable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | PreserveNewest 31 | 32 | 33 | PreserveNewest 34 | 35 | 36 | PreserveNewest 37 | 38 | 39 | PreserveNewest 40 | 41 | 42 | PreserveNewest 43 | 44 | 45 | PreserveNewest 46 | 47 | 48 | PreserveNewest 49 | 50 | 51 | PreserveNewest 52 | 53 | 54 | PreserveNewest 55 | 56 | 57 | PreserveNewest 58 | 59 | 60 | PreserveNewest 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/KeyboardSimulator/KeyboardSimulator.cs: -------------------------------------------------------------------------------- 1 | using CodeDeck.PluginAbstractions; 2 | using InputSimulatorEx; 3 | using InputSimulatorEx.Native; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Diagnostics; 7 | using System.Linq; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | 11 | namespace CodeDeck.Plugins.Plugins.Counter; 12 | 13 | public class KeyboardSimulator : CodeDeckPlugin 14 | { 15 | public class TyperTile : Tile 16 | { 17 | [Setting] public string? Text { get; set; } 18 | 19 | private InputSimulator _inputSimulator = new(); 20 | 21 | public override Task Init(CancellationToken cancellationToken) 22 | { 23 | return base.Init(cancellationToken); 24 | } 25 | 26 | public override async Task OnTilePressDown(CancellationToken cancellationToken) 27 | { 28 | _inputSimulator.Keyboard.TextEntry(Text ?? "No text specified!"); 29 | await Task.CompletedTask; 30 | } 31 | } 32 | 33 | public class HotkeyTile : Tile 34 | { 35 | [Setting] public string? Modifiers { get; set; } 36 | [Setting] public string? Keys { get; set; } 37 | 38 | private List? _modifiers; 39 | private List? _keys; 40 | 41 | private InputSimulator _inputSimulator = new(); 42 | 43 | public override Task Init(CancellationToken cancellationToken) 44 | { 45 | try 46 | { 47 | _modifiers = Modifiers? 48 | .Split(" ") 49 | .Select(x => (VirtualKeyCode)Enum.Parse(typeof(VirtualKeyCode), x, true)) 50 | .ToList(); 51 | 52 | _keys = Keys? 53 | .Split(" ") 54 | .Select(x => (VirtualKeyCode)Enum.Parse(typeof(VirtualKeyCode), x, true)) 55 | .ToList(); 56 | } 57 | catch (Exception e) 58 | { 59 | Debug.WriteLine($"Exception in {nameof(HotkeyTile)}. Message: {e.Message}"); 60 | } 61 | 62 | return base.Init(cancellationToken); 63 | } 64 | 65 | public override Task OnTilePressUp(CancellationToken cancellationToken) 66 | { 67 | if (_modifiers is not null && _keys is not null) 68 | { 69 | _inputSimulator.Keyboard.ModifiedKeyStroke(_modifiers, _keys); 70 | } 71 | else if (_keys is not null && _keys.Count > 0) 72 | { 73 | _inputSimulator.Keyboard.KeyPress(_keys.First()); 74 | } 75 | 76 | return base.OnTilePressDown(cancellationToken); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Runner/Runner.md: -------------------------------------------------------------------------------- 1 | # Runner 2 | 3 | This plugin can start programs and open files and websites as well as run other shell actions. 4 | 5 | - [Runner](#runner) 6 | - [RunTile](#runtile) 7 | - [Settings](#settings) 8 | - [Program](#program) 9 | - [Arguments](#arguments) 10 | - [UseShellExecute](#useshellexecute) 11 | - [Examples](#examples) 12 | - [OpenWebsiteTile](#openwebsitetile) 13 | - [Settings](#settings-1) 14 | - [Program](#program-1) 15 | - [Examples](#examples-1) 16 | 17 | 18 | ## RunTile 19 | 20 | Runs a program by specifying an executable file or shortcut file. 21 | 22 | ### Settings 23 | 24 | | Setting | Default | Description | 25 | | --------------- | ------- | --------------------------------------- | 26 | | Program | `null` | The program, shortcut or action to run. | 27 | | Arguments | `null` | The arguments sent to the program. | 28 | | UseShellExecute | `false` | Set to `true` to run using the shell. | 29 | 30 | #### Program 31 | The name of the application to start, the name of a document, a folder or website. 32 | 33 | #### Arguments 34 | The set of command-line arguments to use when starting the application. 35 | 36 | #### UseShellExecute 37 | A value indicating whether to use the operating system shell to start the process. This must be set to `true` for most actions except running an executable file using its full file name. 38 | 39 | ### Examples 40 | 41 | Run `calc.exe`: 42 | ```json 43 | { 44 | "Plugin": "Runner", 45 | "Tile": "RunTile", 46 | "Settings": { 47 | "Program": "calc.exe", 48 | } 49 | } 50 | ``` 51 | 52 | Open `Network Connections`: 53 | ```json 54 | { 55 | "Plugin": "Runner", 56 | "Tile": "RunTile", 57 | "Settings": { 58 | "Program": "ncpa.cpl", 59 | "Arguments": null, 60 | "UseShellExecute": "true" 61 | } 62 | } 63 | ``` 64 | 65 | 66 | 67 | ## OpenWebsiteTile 68 | 69 | Opens a website. This tile is a wrapper for using `RunTile` with a website address as the `Program` and `UseShellExecute` set to `true`. 70 | 71 | This tile also tries to fetch the favicon of the website and use that as the tile image unless the image property has been overridden in the configuration. 72 | 73 | ### Settings 74 | 75 | | Setting | Default | Description | 76 | | ------- | ------- | -------------------- | 77 | | Url | `null` | The website to open. | 78 | 79 | 80 | #### Program 81 | The address of a website to open. 82 | 83 | ### Examples 84 | 85 | Open the `Code Deck` website: 86 | ```json 87 | { 88 | "Plugin": "Runner", 89 | "Tile": "OpenWebsiteTile", 90 | "Settings": { 91 | "Url": "https://heinandre.no/code-deck" 92 | } 93 | }, 94 | ``` 95 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/LogitechMice/README.md: -------------------------------------------------------------------------------- 1 | # Logitech Mice 2 | 3 | This plugin provides battery level information for Logitech mice. 4 | 5 | 6 | - [Logitech Mice](#logitech-mice) 7 | - [Supported Mice](#supported-mice) 8 | - [G703BatteryTile](#g703batterytile) 9 | - [Settings](#settings) 10 | - [Examples](#examples) 11 | - [GProXSuperlightBatteryTile](#gproxsuperlightbatterytile) 12 | - [Settings](#settings-1) 13 | - [Examples](#examples-1) 14 | 15 | 16 | ## Supported Mice 17 | - Logitech G703 Wireless 18 | - Logitech G Pro X Superlight Wireless 19 | 20 | 21 | ## G703BatteryTile 22 | Shows the battery level of a Logitech G703 Wireless mouse. 23 | 24 | ### Settings 25 | 26 | | Setting | Default | Description | 27 | | ------------------ | ------------------ | ---------------------------------------------------------------------------------------------------------------------------- | 28 | | Format | `🖱\n{0}%\n{1:N2}V` | Text format to use when the mouse is connected. `{0}` is the battery percentage and `{1}` is the voltage as a decimal value. | 29 | | FormatDisconnected | `🖱\n❌` | Text format to use when the mouse is disconnected. | 30 | | Interval | `600 000 ms` | Interval at which to refresh the battery value. Default `10` minutes. | 31 | 32 | ### Examples 33 | 34 | ```json 35 | { 36 | "Plugin": "LogitechMice", 37 | "Tile": "G703BatteryTile", 38 | "Settings": { 39 | "Format": "🖱️\n{0}%\n{1:N2}V", 40 | "FormatDisconnected": "🖱️\n💤", 41 | "Interval": "30000" 42 | } 43 | } 44 | ``` 45 | 46 | ## GProXSuperlightBatteryTile 47 | Shows the battery level of a Logitech G Pro X Superlight Wireless mouse. 48 | 49 | ### Settings 50 | 51 | | Setting | Default | Description | 52 | | ------------------ | ------------ | -------------------------------------------------------------------------------- | 53 | | Format | `🖱\n{0}%` | Text format to use when the mouse is connected. `{0}` is the battery percentage. | 54 | | FormatDisconnected | `🖱\n❌` | Text format to use when the mouse is disconnected. | 55 | | Interval | `600 000 ms` | Interval at which to refresh the battery value. Default `10` minutes. | 56 | 57 | ### Examples 58 | 59 | ```json 60 | { 61 | "Plugin": "LogitechMice", 62 | "Tile": "GProXSuperlightBatteryTile", 63 | "Settings": { 64 | "Format": "🖱️\n{0}%", 65 | "FormatDisconnected": "🖱️\n💤", 66 | "Interval": "30000" 67 | } 68 | } 69 | ``` 70 | -------------------------------------------------------------------------------- /CodeDeck/Program.cs: -------------------------------------------------------------------------------- 1 | using CodeDeck.Services; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Hosting; 4 | using Microsoft.Extensions.Logging; 5 | using System.IO; 6 | using System.Reflection; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace CodeDeck 11 | { 12 | public class Program 13 | { 14 | private static readonly CancellationTokenSource cts = new CancellationTokenSource(); 15 | 16 | public static async Task Main(string[] args) 17 | { 18 | var cwd = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location ?? Directory.GetCurrentDirectory()); 19 | if (cwd is not null) Directory.SetCurrentDirectory(cwd); 20 | 21 | using IHost host = Host.CreateDefaultBuilder(args) 22 | .ConfigureServices((_, services) => 23 | { 24 | services.AddLogging(); 25 | 26 | services.AddSingleton(); 27 | services.AddSingleton(); 28 | services.AddSingleton(); 29 | services.AddSingleton(); 30 | 31 | services.AddHostedService(); 32 | }) 33 | .ConfigureLogging((hostBuilder, logging) => 34 | { 35 | logging.ClearProviders(); 36 | 37 | logging.SetMinimumLevel(LogLevel.Debug); 38 | logging.AddConsole(); 39 | logging.AddDebug(); 40 | 41 | //logging.AddEventLog(); 42 | }) 43 | .Build(); 44 | 45 | await host.RunAsync(cts.Token); 46 | } 47 | 48 | public static void StopHost() 49 | { 50 | cts.Cancel(); 51 | } 52 | } 53 | 54 | public sealed class Worker : BackgroundService 55 | { 56 | private readonly ConfigurationProvider _configurationProvider; 57 | private readonly PluginLoader _pluginLoader; 58 | private readonly StreamDeckManager _streamDeckManager; 59 | 60 | public Worker(ConfigurationProvider configurationProvider, PluginLoader pluginLoader, StreamDeckManager streamDeckManager) 61 | { 62 | _configurationProvider = configurationProvider; 63 | _pluginLoader = pluginLoader; 64 | _streamDeckManager = streamDeckManager; 65 | } 66 | 67 | protected override async Task ExecuteAsync(CancellationToken stoppingToken) 68 | { 69 | _pluginLoader.LoadAllPlugins(); 70 | await _streamDeckManager.Start(); 71 | 72 | await Task.Delay(-1, stoppingToken); 73 | } 74 | 75 | public override Task StopAsync(CancellationToken cancellationToken) 76 | { 77 | _streamDeckManager.ClearKeys(); 78 | return base.StopAsync(cancellationToken); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/NetworkTools/README.md: -------------------------------------------------------------------------------- 1 | # NetworkTools 2 | 3 | This plugin contains a collection of network related tiles. 4 | 5 | - [NetworkTools](#networktools) 6 | - [ExternalIpTile](#externaliptile) 7 | - [Usage](#usage) 8 | - [Settings](#settings) 9 | - [Example Config](#example-config) 10 | - [ApiUrl](#apiurl) 11 | - [Format](#format) 12 | - [Interval](#interval) 13 | - [PadOctets](#padoctets) 14 | - [ShowIpOnTwoLines](#showipontwolines) 15 | 16 | 17 | ## ExternalIpTile 18 | This tile queries an external API that returns your external (public) IP address. 19 | 20 | 21 | ### Usage 22 | The tile will query your external IP address from the specified API at the specified interval and show the IP address on the key using the specified format string. 23 | 24 | You can manually trigger an update by pressing the key. 25 | 26 | 27 | ### Settings 28 | 29 | | Setting | Default | Description | 30 | | ---------------- | ----------------------- | ------------------------------------------------------------------------ | 31 | | ApiUrl | `https://api.ipify.org` | API to use to get the external IP address. | 32 | | Format | `Ext. IP:\n{0}` | A custom format string; `{0}` is replaced by the IP address. | 33 | | Interval | `60000` ms | Update interval. | 34 | | PadOctets | `false` | Set to `true` to pad all IP octets to three digits for better alignment. | 35 | | ShowIpOnTwoLines | `true` | Controls if the IP address should be wrapped on two lines or not. | 36 | 37 | ### Example Config 38 | 39 | ```json 40 | { 41 | "Plugin": "NetworkTools", 42 | "Tile": "ExternalIpTile", 43 | "FontSize": 16, 44 | "LineSpacing": 1.2, 45 | "Font": "Cascadia Code", 46 | "Settings": { 47 | "Interval": "3000", 48 | "ShowIpOnTwoLines": "true", 49 | "PadOctets": "false", 50 | "Format": "🌍\n{0}" 51 | } 52 | } 53 | ``` 54 | 55 | 56 | #### ApiUrl 57 | This is the URL to an API used to get the external IP address. The default API provider is; [`ipify API`](https://www.ipify.org/). It's possible to use a custom endpoint or another API service. The enpoint should return the `IPv4` address as `Content-Type: text/plain`. 58 | 59 | #### Format 60 | A custom format string; {0} is replaced by the IP address. Example using an emoji; `🌍\n{0}`. 61 | 62 | #### Interval 63 | The interval between calls to the API. 64 | 65 | #### PadOctets 66 | Set to `true` to pad all IP octets to three digits for better alignment. Example; `192.168.001.010` instead of `192.168.1.10`. Padding might look better when combined with `ShowIpOnTwoLines: true`. 67 | 68 | #### ShowIpOnTwoLines 69 | Controls if the IP address should be wrapped on two lines or not. Setting this to `true` will show the first two IP address octets on one line and the next two on the next line. 70 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/HyperXCloudAlphaWireless/HyperXCloudAlphaWireless.cs: -------------------------------------------------------------------------------- 1 | using CodeDeck.PluginAbstractions; 2 | using HidSharp; 3 | using System; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | /// 10 | /// HID report bytes found here: 11 | /// https://github.com/auto94/HyperX-Cloud-2-Battery-Monitor/blob/main/Cloud2BatteryMonitorUI/MainForm.cpp#L94 12 | /// 13 | public class HyperXCloudAlphaWireless : CodeDeckPlugin 14 | { 15 | public class BatteryTile : Tile 16 | { 17 | [Setting] public string? Format { get; set; } 18 | [Setting] public string? FormatDisconnected { get; set; } 19 | [Setting] public int? Interval { get; set; } 20 | 21 | private HidDevice? _device; 22 | 23 | public override async Task Init(CancellationToken cancellationToken) 24 | { 25 | _device = DeviceList.Local 26 | .GetHidDevices(0x03F0, 0x098D) 27 | .Where(x => x.GetMaxInputReportLength() == 31) 28 | .FirstOrDefault(); 29 | 30 | _ = Task.Run(() => BackgroundTask(cancellationToken), cancellationToken); 31 | 32 | await Task.CompletedTask; 33 | } 34 | 35 | public override async Task OnTilePressUp(CancellationToken cancellationToken) 36 | { 37 | await GetAsync(cancellationToken); 38 | } 39 | 40 | private async Task GetAsync(CancellationToken cancellationToken) 41 | { 42 | try 43 | { 44 | ShowIndicator = true; 45 | 46 | if (_device == null) return; 47 | 48 | HidStream s = _device.Open(); 49 | 50 | byte[] report = new byte[20]; 51 | report[0] = 0x21; 52 | report[1] = 0xbb; 53 | report[2] = 0x0b; 54 | 55 | await Task.Factory.FromAsync(s.BeginWrite, s.EndWrite, report, 0, report.Length, TaskCreationOptions.None); 56 | await Task.Factory.FromAsync(s.BeginRead, s.EndRead, report, 0, report.Length, TaskCreationOptions.None); 57 | 58 | var batteryLevel = report[3]; 59 | 60 | Text = string.Format(Format ?? "🔋\n{0}%", batteryLevel); 61 | } 62 | catch 63 | { 64 | Text = FormatDisconnected ?? "🎧\n❌"; 65 | } 66 | finally 67 | { 68 | ShowIndicator = false; 69 | } 70 | } 71 | 72 | private async Task BackgroundTask(CancellationToken cancellationToken) 73 | { 74 | for (; ; ) 75 | { 76 | if (cancellationToken.IsCancellationRequested) 77 | { 78 | Debug.WriteLine($"{nameof(BackgroundTask)} in {nameof(BatteryTile)} was cancelled!"); 79 | return; 80 | } 81 | 82 | GetAsync(cancellationToken).Wait(cancellationToken); 83 | await Task.Delay(Interval ?? 10 * 60 * 1000, cancellationToken); 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /CodeDeck/Services/ProcessMonitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Timers; 6 | 7 | namespace CodeDeck.Services 8 | { 9 | public class ProcessMonitor 10 | { 11 | public event EventHandler? ProcessStarted; 12 | public event EventHandler? ProcessExited; 13 | 14 | private readonly Dictionary _monitoredProcesses = new(); 15 | private readonly Timer _timer = new(); 16 | 17 | public ProcessMonitor() 18 | { 19 | _timer.Elapsed += HandleTimerElapsed; 20 | } 21 | 22 | public void Start() 23 | { 24 | _timer.Interval = 500; 25 | _timer.AutoReset = true; 26 | _timer.Enabled = true; 27 | } 28 | 29 | public void Stop() 30 | { 31 | _timer.Enabled = false; 32 | } 33 | 34 | public void Add(string name) 35 | { 36 | if (_monitoredProcesses.ContainsKey(name)) return; 37 | _monitoredProcesses.Add(name.ToLower(), false); 38 | } 39 | 40 | public void Remove(string name) 41 | { 42 | if (!_monitoredProcesses.ContainsKey(name)) return; 43 | _monitoredProcesses.Remove(name.ToLower()); 44 | } 45 | 46 | public void Clear() 47 | { 48 | _monitoredProcesses.Clear(); 49 | } 50 | 51 | private void HandleTimerElapsed(object? sender, ElapsedEventArgs e) 52 | { 53 | UpdateMonitoredProcesses(); 54 | } 55 | 56 | private void UpdateMonitoredProcesses() 57 | { 58 | if (_monitoredProcesses.Count == 0) return; 59 | 60 | var runningProcesses = Process.GetProcesses() 61 | .Select(x => x.ProcessName.ToLower()) 62 | .Distinct() 63 | .ToList(); 64 | 65 | foreach (var monitoredProcess in _monitoredProcesses.Keys) 66 | { 67 | if (runningProcesses.Contains(monitoredProcess)) 68 | // The monitored process is running 69 | { 70 | if (!_monitoredProcesses[monitoredProcess]) 71 | // Monitored process is not marked as running 72 | { 73 | // Mark process as running and invoke event 74 | _monitoredProcesses[monitoredProcess] = true; 75 | ProcessStarted?.Invoke(this, monitoredProcess); 76 | } 77 | } 78 | else 79 | // The monitored process is NOT running 80 | { 81 | // Continue if the monitored process is already marked as not running 82 | if (!_monitoredProcesses[monitoredProcess]) continue; 83 | 84 | // Monitored process is marked as running, mark as NOT running and invoke event 85 | _monitoredProcesses[monitoredProcess] = false; 86 | ProcessExited?.Invoke(this, monitoredProcess); 87 | } 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /CodeDeck.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.4.33122.133 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeDeck", "CodeDeck\CodeDeck.csproj", "{2EE46B70-1CD7-4F03-AA7A-F901385DD558}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{D0333636-23F1-4088-9BA9-AF8FEB903AB1}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeDeck.PluginAbstractions", "CodeDeck.PluginAbstractions\CodeDeck.PluginAbstractions.csproj", "{07B81259-2E6F-4038-A9E6-36E4FD0E1B75}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeDeck.Plugins", "CodeDeck.Plugins\CodeDeck.Plugins.csproj", "{768C2681-9F72-4657-AB57-8B1F74202167}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeDeck.Windows", "CodeDeck.Windows\CodeDeck.Windows.csproj", "{A2D361D7-EBC4-47E1-A823-5C8D78D099A4}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeDeck.Linux", "CodeDeck.Linux\CodeDeck.Linux.csproj", "{32159347-5F3A-4AD3-B590-A433DC19EBA5}" 17 | EndProject 18 | Global 19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 20 | Debug|Any CPU = Debug|Any CPU 21 | Release|Any CPU = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {2EE46B70-1CD7-4F03-AA7A-F901385DD558}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {2EE46B70-1CD7-4F03-AA7A-F901385DD558}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {2EE46B70-1CD7-4F03-AA7A-F901385DD558}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {2EE46B70-1CD7-4F03-AA7A-F901385DD558}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {07B81259-2E6F-4038-A9E6-36E4FD0E1B75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {07B81259-2E6F-4038-A9E6-36E4FD0E1B75}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {07B81259-2E6F-4038-A9E6-36E4FD0E1B75}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {07B81259-2E6F-4038-A9E6-36E4FD0E1B75}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {768C2681-9F72-4657-AB57-8B1F74202167}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {768C2681-9F72-4657-AB57-8B1F74202167}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {768C2681-9F72-4657-AB57-8B1F74202167}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {768C2681-9F72-4657-AB57-8B1F74202167}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {A2D361D7-EBC4-47E1-A823-5C8D78D099A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {A2D361D7-EBC4-47E1-A823-5C8D78D099A4}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {A2D361D7-EBC4-47E1-A823-5C8D78D099A4}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {A2D361D7-EBC4-47E1-A823-5C8D78D099A4}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {32159347-5F3A-4AD3-B590-A433DC19EBA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {32159347-5F3A-4AD3-B590-A433DC19EBA5}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {32159347-5F3A-4AD3-B590-A433DC19EBA5}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {32159347-5F3A-4AD3-B590-A433DC19EBA5}.Release|Any CPU.Build.0 = Release|Any CPU 44 | EndGlobalSection 45 | GlobalSection(SolutionProperties) = preSolution 46 | HideSolutionNode = FALSE 47 | EndGlobalSection 48 | GlobalSection(ExtensibilityGlobals) = postSolution 49 | SolutionGuid = {14FF87D6-821A-4D70-97A7-69F16E6531AE} 50 | EndGlobalSection 51 | EndGlobal 52 | -------------------------------------------------------------------------------- /CodeDeck/KeyWrapper.cs: -------------------------------------------------------------------------------- 1 | using CodeDeck.Models.Configuration; 2 | using CodeDeck.PluginAbstractions; 3 | using Microsoft.Extensions.Logging; 4 | using OpenMacroBoard.SDK; 5 | using SixLabors.ImageSharp; 6 | using System; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace CodeDeck 11 | { 12 | public class KeyWrapper 13 | { 14 | private readonly ILogger _logger; 15 | 16 | public Profile Profile { get; set; } 17 | public Page Page { get; set; } 18 | public Key Key { get; set; } 19 | 20 | public Plugin? Plugin { get; set; } 21 | public Tile? Tile { get; set; } 22 | public CancellationTokenSource CancellationTokenSource { get; } = new(); 23 | 24 | public Image? Image { get; set; } 25 | 26 | /// 27 | /// Cached composed key bitmap image 28 | /// 29 | public KeyBitmap? CachedComposedKeyBitmapImage { get; set; } 30 | 31 | /// 32 | /// Indicates if the wrapped key is currently being shown 33 | /// 34 | public bool IsShowing { get; set; } = false; 35 | 36 | public event EventHandler? Updated; 37 | 38 | public KeyWrapper(ILogger logger, Profile profile, Page page, Key key, Plugin? plugin) 39 | { 40 | _logger = logger; 41 | 42 | Profile = profile; 43 | Page = page; 44 | Key = key; 45 | Plugin = plugin; 46 | } 47 | 48 | public async Task InstantiateTileObjectAsync() 49 | { 50 | if (Plugin is null || Key.Tile is null) return; 51 | 52 | try 53 | { 54 | Tile = Plugin.CreateTileInstance(Key.Tile, Key.Settings); 55 | if (Tile is null) return; 56 | 57 | Tile.NotifyChange = NotifyChange_Action; 58 | 59 | await Tile.Init(CancellationTokenSource.Token); 60 | } 61 | catch (Exception e) 62 | { 63 | _logger.LogError($"Exception during 'Tile.Init' in plugin: {Plugin.Name}; tile: {Tile?.GetType().Name ?? "null"}. Message: '{e.Message}'"); 64 | } 65 | } 66 | 67 | public void NotifyChange_Action() 68 | { 69 | Updated?.Invoke(this, this); 70 | } 71 | 72 | public async void HandleKeyPressDown() 73 | { 74 | if (Tile == null) return; 75 | if (Key.DisableTilePress is not null && Key.DisableTilePress.Value) return; 76 | 77 | try 78 | { 79 | await Tile.OnTilePressDown(CancellationTokenSource.Token); 80 | } 81 | catch (Exception e) 82 | { 83 | _logger.LogDebug($"<{nameof(Tile.OnTilePressDown)}>: {e.Message}"); 84 | } 85 | } 86 | 87 | public async void HandleKeyPressUp() 88 | { 89 | if (Tile == null) return; 90 | if (Key.DisableTilePress is not null && Key.DisableTilePress.Value) return; 91 | 92 | try 93 | { 94 | await Tile.OnTilePressUp(CancellationTokenSource.Token); 95 | } 96 | catch (Exception e) 97 | { 98 | _logger.LogDebug($"<{nameof(Tile.OnTilePressUp)}>: {e.Message}"); 99 | } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/SteelSeriesRival3Wireless/SteelSeriesRival3Wireless.cs: -------------------------------------------------------------------------------- 1 | using CodeDeck.PluginAbstractions; 2 | using HidSharp; 3 | using System; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | /// 10 | /// Based on rivalcfg: https://github.com/flozz/rivalcfg/blob/master/rivalcfg/devices/rival3_wireless.py 11 | /// 12 | public class SteelSeriesRival3Wireless : CodeDeckPlugin 13 | { 14 | public class BatteryTile : Tile 15 | { 16 | [Setting] public string? Format { get; set; } 17 | [Setting] public string? FormatDisconnected { get; set; } 18 | [Setting] public int? Interval { get; set; } 19 | 20 | private HidDevice? _device; 21 | 22 | public override async Task Init(CancellationToken cancellationToken) 23 | { 24 | _device = FindDevice(); 25 | 26 | _ = Task.Run(() => BackgroundTask(cancellationToken), cancellationToken); 27 | 28 | await Task.CompletedTask; 29 | } 30 | 31 | private HidDevice? FindDevice() 32 | { 33 | return DeviceList.Local 34 | .GetHidDevices(0x1038, 0x1830) 35 | // Filter to pick the correct endpoint 36 | .Where(x => x.GetMaxFeatureReportLength() == 515) 37 | .Where(x => x.GetMaxInputReportLength() == 65) 38 | .Where(x => x.GetMaxOutputReportLength() == 65) 39 | .FirstOrDefault(); 40 | } 41 | 42 | public override async Task OnTilePressUp(CancellationToken cancellationToken) 43 | { 44 | await GetAsync(cancellationToken); 45 | } 46 | 47 | private async Task GetAsync(CancellationToken cancellationToken) 48 | { 49 | try 50 | { 51 | ShowIndicator = true; 52 | 53 | if (_device == null) _device = FindDevice(); 54 | if (_device == null) throw new ArgumentNullException(nameof(_device)); 55 | 56 | HidStream s = _device.Open(); 57 | 58 | byte[] report = new byte[3]; 59 | report[0] = 0x00; 60 | report[1] = 0xAA; 61 | report[2] = 0x01; 62 | 63 | byte[] response = new byte[3]; 64 | 65 | await Task.Factory.FromAsync(s.BeginWrite, s.EndWrite, report, 0, report.Length, TaskCreationOptions.None); 66 | await Task.Factory.FromAsync(s.BeginRead, s.EndRead, response, 0, response.Length, TaskCreationOptions.None); 67 | 68 | Text = string.Format(Format ?? "🔋\n{0}%", response[1]); 69 | } 70 | catch 71 | { 72 | Text = FormatDisconnected ?? "🖱\n❌"; 73 | } 74 | finally 75 | { 76 | ShowIndicator = false; 77 | } 78 | } 79 | 80 | private async Task BackgroundTask(CancellationToken cancellationToken) 81 | { 82 | for (; ; ) 83 | { 84 | if (cancellationToken.IsCancellationRequested) 85 | { 86 | Debug.WriteLine($"{nameof(BackgroundTask)} in {nameof(BatteryTile)} was cancelled!"); 87 | return; 88 | } 89 | 90 | GetAsync(cancellationToken).Wait(cancellationToken); 91 | await Task.Delay(Interval ?? 10 * 60 * 1000, cancellationToken); 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/NetworkTools/ExternalIpTile.cs: -------------------------------------------------------------------------------- 1 | using CodeDeck.PluginAbstractions; 2 | using System; 3 | using System.Linq; 4 | using System.Net.Http; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace CodeDeck.Plugins.Plugins.NetworkTools; 9 | 10 | public partial class NetworkTools : CodeDeckPlugin 11 | { 12 | public class ExternalIpTile : Tile 13 | { 14 | [Setting] public string ApiUrl { get; set; } = "https://api.ipify.org"; 15 | [Setting] public string Format { get; set; } = "Ext. IP:\n{0}"; 16 | [Setting] public int Interval { get; set; } = 60000; 17 | [Setting] public bool PadOctets { get; set; } = false; 18 | [Setting] public bool ShowIpOnTwoLines { get; set; } = true; 19 | 20 | private readonly HttpClient _client = new(); 21 | 22 | 23 | public override async Task Init(CancellationToken cancellationToken) 24 | { 25 | _ = Task.Run(() => BackgroundTask(cancellationToken), cancellationToken); 26 | 27 | await Task.CompletedTask; 28 | 29 | } 30 | 31 | public override async Task OnTilePressUp(CancellationToken cancellationToken) 32 | { 33 | await GetAsync(cancellationToken); 34 | } 35 | 36 | private async Task GetAsync(CancellationToken cancellationToken) 37 | { 38 | try 39 | { 40 | ShowIndicator = true; 41 | var result = await _client.GetStringAsync(ApiUrl, cancellationToken); 42 | 43 | Text = string.Format(Format, FormatIp(result, ShowIpOnTwoLines, PadOctets)); 44 | } 45 | catch (Exception) { } 46 | finally 47 | { 48 | ShowIndicator = false; 49 | } 50 | } 51 | 52 | private async Task BackgroundTask(CancellationToken cancellationToken) 53 | { 54 | for (; ; ) 55 | { 56 | if (cancellationToken.IsCancellationRequested) 57 | { 58 | return; 59 | } 60 | 61 | GetAsync(cancellationToken).Wait(cancellationToken); 62 | await Task.Delay(Interval, cancellationToken); 63 | } 64 | } 65 | 66 | private static string FormatIp(string ip, bool multiline, bool pad) 67 | { 68 | try 69 | { 70 | var parts = ip.Split('.'); 71 | if (parts.Length != 4) return ip; 72 | 73 | var ints = parts.Select(x => int.Parse(x)).ToList(); 74 | 75 | if (multiline) 76 | { 77 | if (pad) 78 | { 79 | return $"{ints[0]:D3}.{ints[1]:D3}\n{ints[2]:D3}.{ints[3]:D3}"; 80 | } 81 | else 82 | { 83 | return $"{ints[0]}.{ints[1]}\n{ints[2]}.{ints[3]}"; 84 | } 85 | } 86 | else 87 | { 88 | if (pad) 89 | { 90 | return $"{ints[0]:D3}.{ints[1]:D3}.{ints[2]:D3}.{ints[3]:D3}"; 91 | } 92 | else 93 | { 94 | return $"{ints[0]}.{ints[1]}.{ints[2]}.{ints[3]}"; 95 | } 96 | } 97 | } 98 | catch (Exception) 99 | { 100 | return ip; 101 | } 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /CodeDeck.PluginAbstractions/Tile.cs: -------------------------------------------------------------------------------- 1 | using SixLabors.ImageSharp; 2 | 3 | namespace CodeDeck.PluginAbstractions 4 | { 5 | /// 6 | /// Represents a Tile, a Tile can react to key presses or just display data 7 | /// 8 | public class Tile 9 | { 10 | /// 11 | /// Set by the framework, notifies the framework that the tile needs to be updated on the Deck 12 | /// 13 | public Action? NotifyChange { get; set; } 14 | 15 | 16 | private string? _text; 17 | public string? Text 18 | { 19 | get { return _text; } 20 | set 21 | { 22 | _text = value; 23 | NotifyChange?.Invoke(); 24 | } 25 | } 26 | 27 | private Color? _textColor; 28 | public Color? TextColor 29 | { 30 | get => _textColor; set 31 | { 32 | _textColor = value; 33 | NotifyChange?.Invoke(); 34 | } 35 | } 36 | 37 | private Color? _bgColor; 38 | public Color? BackgroundColor 39 | { 40 | get => _bgColor; set 41 | { 42 | _bgColor = value; 43 | NotifyChange?.Invoke(); 44 | } 45 | } 46 | 47 | private string? _font; 48 | public string? Font 49 | { 50 | get => _font; set 51 | { 52 | _font = value; 53 | NotifyChange?.Invoke(); 54 | } 55 | } 56 | 57 | private float? _fontSize; 58 | public float? FontSize 59 | { 60 | get => _fontSize; set 61 | { 62 | _fontSize = value; 63 | NotifyChange?.Invoke(); 64 | } 65 | } 66 | 67 | private Image? _image; 68 | public Image? Image 69 | { 70 | get => _image; set 71 | { 72 | _image = value; 73 | NotifyChange?.Invoke(); 74 | } 75 | } 76 | 77 | private int? _imagePadding; 78 | public int? ImagePadding 79 | { 80 | get => _imagePadding; set 81 | { 82 | _imagePadding = value; 83 | NotifyChange?.Invoke(); 84 | } 85 | } 86 | 87 | private bool? _showIndicator; 88 | public bool? ShowIndicator 89 | { 90 | get => _showIndicator; set 91 | { 92 | _showIndicator = value; 93 | NotifyChange?.Invoke(); 94 | } 95 | } 96 | 97 | private Color? _indicatorColor; 98 | public Color? IndicatorColor 99 | { 100 | get => _indicatorColor; set 101 | { 102 | _indicatorColor = value; 103 | NotifyChange?.Invoke(); 104 | } 105 | } 106 | 107 | public Dictionary? Settings { get; set; } 108 | 109 | 110 | public Tile() { } 111 | 112 | public virtual async Task Init(CancellationToken cancellationToken) 113 | { 114 | await Task.CompletedTask; 115 | } 116 | 117 | public virtual async Task DeInit() 118 | { 119 | await Task.CompletedTask; 120 | } 121 | 122 | public virtual async Task OnTilePressDown(CancellationToken cancellationToken) 123 | { 124 | await Task.CompletedTask; 125 | } 126 | 127 | public virtual async Task OnTilePressUp(CancellationToken cancellationToken) 128 | { 129 | await Task.CompletedTask; 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/WebRequest/WebRequest.md: -------------------------------------------------------------------------------- 1 | # WebRequest 2 | The WebRequest plugin contains a collection of tiles that can request and display data from the Internet. 3 | 4 | 5 | - [WebRequest](#webrequest) 6 | - [PlainTextTile](#plaintexttile) 7 | - [Usage](#usage) 8 | - [Settings](#settings) 9 | - [Example Config](#example-config) 10 | - [Url](#url) 11 | - [Format](#format) 12 | - [Interval](#interval) 13 | - [ImageTile](#imagetile) 14 | - [Usage](#usage-1) 15 | - [Settings](#settings-1) 16 | - [Example Config](#example-config-1) 17 | - [Url](#url-1) 18 | - [Interval](#interval-1) 19 | - [Crop](#crop) 20 | 21 | 22 | ## PlainTextTile 23 | This tile does a web request to the specified URL and shows the text result on the key. This tile should be used when requesting plain text (`Content-Type: text/plain`) data. 24 | 25 | 26 | ## Usage 27 | A use case for this tile could be to retrieve and display some sensor data or maybe get a random integer from [random.org](https://www.random.org). 28 | 29 | 30 | ### Settings 31 | 32 | | Setting | Default | Description | 33 | | -------- | ---------- | ---------------------------------- | 34 | | Url | `null` | The URL to request. | 35 | | Format | `{0}` | How to format the text on the key. | 36 | | Interval | `60000` ms | Update interval. | 37 | 38 | 39 | ### Example Config 40 | 41 | ```json 42 | { 43 | "FontSize": 22, 44 | "FontBold": true, 45 | "LineSpacing": 1.2, 46 | "Plugin": "WebRequest", 47 | "Tile": "PlainTextTile", 48 | "ActivityIndicatorColor": "#ffffff", 49 | "BackgroundColor": "#4d0977", 50 | "TextColor": "#bbbbbb", 51 | "Settings": { 52 | "Interval": "10000", 53 | "Url": "https://www.random.org/integers/?num=1&min=1&max=50&col=1&base=10&format=plain&rnd=new", 54 | "Format": "🌡️\n{0}°" 55 | } 56 | } 57 | ``` 58 | 59 | 60 | #### Url 61 | The URL to retrieve data from. The URL should point to some data served as plain text (`Content-Type: text/plain`). 62 | 63 | 64 | #### Format 65 | The format to use when showing the data on the key. 66 | 67 | Examples: 68 | - `{0}` - Shows only the retrieved data 69 | - `{0}°` - Shows the data with a degrees symbol suffix 70 | - `{0} %` - Shows the data with a space and a percent symbol suffix 71 | - `TEMP\n{0}°` - Shows the data with a `TEMP`-prefix followed by a linebreak and the data with a degrees symbol suffix 72 | 73 | 74 | #### Interval 75 | The time between requests to the specified URL. 76 | 77 | 78 | ## ImageTile 79 | This tile does a web request to the specified URL and shows the retrieved image on the key. This tile should be used to retrieve image data. The image data must be in a format listed [here](https://docs.sixlabors.com/articles/imagesharp/imageformats.html). 80 | 81 | 82 | ## Usage 83 | A use case could be to display an image from a webcam. 84 | 85 | 86 | ### Settings 87 | 88 | | Setting | Default | Description | 89 | | -------- | ---------- | ------------------------------- | 90 | | Url | `null` | The URL to request. | 91 | | Interval | `60000` ms | Update interval. | 92 | | Crop | `false` | Crop the image to fill the key. | 93 | 94 | 95 | ### Example Config 96 | ```json 97 | { 98 | "Plugin": "WebRequest", 99 | "Tile": "ImageTile", 100 | "Settings": { 101 | "Url": "https://heinandre.no/code-deck/Images/icon-128.png" 102 | } 103 | } 104 | ``` 105 | 106 | 107 | #### Url 108 | The URL to retrieve an image from. The image data must be in a format listed [here](https://docs.sixlabors.com/articles/imagesharp/imageformats.html). 109 | 110 | 111 | #### Interval 112 | The time between requests to the specified URL. 113 | 114 | 115 | #### Crop 116 | If set to `true`, the image will be cropped to a square aspect ratio to fill the entire key. The image will be cropped from the center of the image. 117 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Runner/Runner.cs: -------------------------------------------------------------------------------- 1 | using CodeDeck.PluginAbstractions; 2 | using SixLabors.ImageSharp; 3 | using System; 4 | using System.Diagnostics; 5 | using System.Drawing.Imaging; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Net.Http; 9 | using System.Runtime.Versioning; 10 | using System.Threading; 11 | using System.Threading.Tasks; 12 | using System.Reflection.Metadata; 13 | 14 | namespace CodeDeck.Plugins.Plugins.Runner 15 | { 16 | public class Runner : CodeDeckPlugin 17 | { 18 | private static readonly HttpClient _httpClient = new(); 19 | 20 | public class RunTile : Tile 21 | { 22 | [Setting] public string? Program { get; set; } 23 | [Setting] public string? Arguments { get; set; } 24 | [Setting] public bool? UseShellExecute { get; set; } 25 | 26 | public override Task Init(CancellationToken cancellationToken) 27 | { 28 | if (OperatingSystem.IsWindows() && Program is not null) 29 | { 30 | Image = GetAssociatedIcon(Program); 31 | } 32 | 33 | return base.Init(cancellationToken); 34 | } 35 | 36 | public override Task OnTilePressDown(CancellationToken cancellationToken) 37 | { 38 | if (Program is not null) 39 | { 40 | Process.Start(new ProcessStartInfo() 41 | { 42 | FileName = Program, 43 | Arguments = Arguments ?? "", 44 | UseShellExecute = UseShellExecute ?? false 45 | }); 46 | } 47 | 48 | return base.OnTilePressDown(cancellationToken); 49 | } 50 | 51 | [SupportedOSPlatform("windows")] 52 | private Image? GetAssociatedIcon(string fileName) 53 | { 54 | var fullFileName = GetFileNameUsingEnvironmentPath(fileName); 55 | if (!File.Exists(fullFileName)) return null; 56 | 57 | var icon = System.Drawing.Icon.ExtractAssociatedIcon(fullFileName); 58 | if (icon != null) 59 | { 60 | using var ms = new MemoryStream(); 61 | icon.ToBitmap().Save(ms, ImageFormat.Png); 62 | ms.Seek(0, SeekOrigin.Begin); 63 | return Image.Load(ms); 64 | } 65 | 66 | return null; 67 | } 68 | 69 | private string? GetFileNameUsingEnvironmentPath(string fileName) 70 | { 71 | if (File.Exists(fileName)) return fileName; 72 | 73 | var result = (Environment.GetEnvironmentVariable("PATH") ?? "") 74 | .Split(';') 75 | .Select(x => Path.Combine(x, fileName)) 76 | .Where(x => File.Exists(x)) 77 | .FirstOrDefault(); 78 | 79 | return result; 80 | } 81 | } 82 | 83 | public class OpenWebsiteTile : Tile 84 | { 85 | [Setting] public string? Url { get; set; } 86 | 87 | public override async Task Init(CancellationToken cancellationToken) 88 | { 89 | if (Url == null) return; 90 | 91 | var uri = new Uri(Url); 92 | var favicon = await _httpClient.GetByteArrayAsync($"http://www.google.com/s2/favicons?domain={uri.Host}&sz=64"); 93 | Image = Image.Load(favicon); 94 | } 95 | 96 | public override async Task OnTilePressDown(CancellationToken cancellationToken) 97 | { 98 | if (Url == null) return; 99 | 100 | Process.Start(new ProcessStartInfo() 101 | { 102 | FileName = Url, 103 | UseShellExecute = true, 104 | }); 105 | 106 | await Task.CompletedTask; 107 | } 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /CodeDeck/Models/Configuration/Key.cs: -------------------------------------------------------------------------------- 1 | using SixLabors.ImageSharp; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text.Json.Serialization; 5 | 6 | namespace CodeDeck.Models.Configuration 7 | { 8 | /// 9 | /// Configuration class for a Key 10 | /// 11 | public class Key 12 | { 13 | public const string KEY_TYPE_NORMAL = "Normal"; 14 | public const string KEY_TYPE_GO_BACK = "Back"; 15 | 16 | /// 17 | /// The Key index on the deck 18 | /// 19 | public int Index { get; set; } 20 | 21 | /// 22 | /// 23 | /// 24 | public string? Text { get; set; } 25 | public string? TextColor { get; set; } 26 | public int? TextOffsetX { get; set; } 27 | public int? TextOffsetY { get; set; } 28 | public float? LineSpacing { get; set; } 29 | public string? Font { get; set; } 30 | public float? FontSize { get; set; } 31 | public bool? FontBold { get; set; } 32 | public bool? FontItalic { get; set; } 33 | 34 | public string? BackgroundColor { get; set; } 35 | public string? Image { get; set; } 36 | public bool DisableTileImage { get; set; } = false; 37 | public int? ImagePadding { get; set; } 38 | public int? ImageOffsetX { get; set; } 39 | public int? ImageOffsetY { get; set; } 40 | 41 | public string? ActivityIndicatorColor { get; set; } 42 | 43 | public bool? ShowFolderIndicator { get; set; } 44 | public string? FolderIndicatorColor { get; set; } 45 | public bool? DisableTilePress { get; set; } 46 | 47 | /// 48 | /// Plugin reference 49 | /// 50 | public string? Plugin { get; set; } 51 | 52 | /// 53 | /// Tile reference 54 | /// 55 | public string? Tile { get; set; } 56 | 57 | public string KeyType { get; set; } = KEY_TYPE_NORMAL; 58 | public string? Profile { get; set; } 59 | public string? Page { get; set; } 60 | 61 | /// 62 | /// Settings for the Tile 63 | /// 64 | // TODO: Should this be a typed object, type defined by Tile 65 | public Dictionary? Settings { get; set; } 66 | 67 | 68 | 69 | [JsonIgnore] 70 | public Color? TextColorAsColor 71 | { 72 | get 73 | { 74 | try 75 | { 76 | if (TextColor != null) return Color.ParseHex(TextColor); 77 | } 78 | catch (Exception) { } 79 | 80 | return null; 81 | } 82 | } 83 | 84 | [JsonIgnore] 85 | public Color? BackgroundColorAsColor 86 | { 87 | get 88 | { 89 | try 90 | { 91 | if (BackgroundColor != null) return Color.ParseHex(BackgroundColor); 92 | } 93 | catch (Exception) { } 94 | 95 | return null; 96 | } 97 | } 98 | 99 | [JsonIgnore] 100 | public Color? ActivityIndicatorColorAsColor 101 | { 102 | get 103 | { 104 | try 105 | { 106 | if (ActivityIndicatorColor != null) return Color.ParseHex(ActivityIndicatorColor); 107 | } 108 | catch (Exception) { } 109 | 110 | return null; 111 | } 112 | } 113 | 114 | [JsonIgnore] 115 | public Color? FolderIndicatorColorAsColor 116 | { 117 | get 118 | { 119 | try 120 | { 121 | if (FolderIndicatorColor != null) return Color.ParseHex(FolderIndicatorColor); 122 | } 123 | catch (Exception) { } 124 | 125 | return null; 126 | } 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/PerformanceCounters/PerformanceCounters.cs: -------------------------------------------------------------------------------- 1 | using CodeDeck.PluginAbstractions; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Runtime.Versioning; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace CodeDeck.Plugins.Plugins.Counter; 10 | 11 | [SupportedOSPlatform("windows")] 12 | public class PerformanceCounters : CodeDeckPlugin 13 | { 14 | public class CpuUsageTile : Tile 15 | { 16 | [Setting] public string? Format { get; set; } 17 | 18 | private PerformanceCounter _cpuUsageCounter = new("Processor Information", "% Processor Utility", "_Total"); 19 | 20 | public override async Task Init(CancellationToken cancellationToken) 21 | { 22 | _ = Task.Run(() => UpdateTile(cancellationToken), cancellationToken); 23 | await Task.CompletedTask; 24 | } 25 | 26 | private async Task UpdateTile(CancellationToken cancellationToken) 27 | { 28 | for (; ; ) 29 | { 30 | if (cancellationToken.IsCancellationRequested) 31 | { 32 | return; 33 | } 34 | 35 | var cpuUsage = (int)_cpuUsageCounter.NextValue(); 36 | Text = string.Format(Format ?? "CPU\n{0} %", cpuUsage); 37 | await Task.Delay(1000, cancellationToken); 38 | } 39 | } 40 | } 41 | 42 | public class MemoryUsageTile : Tile 43 | { 44 | [Setting] public string? Format { get; set; } 45 | 46 | private PerformanceCounter _memUsageCounter = new("Memory", "% Committed Bytes In Use"); 47 | 48 | public override async Task Init(CancellationToken cancellationToken) 49 | { 50 | _ = Task.Run(() => UpdateTile(cancellationToken), cancellationToken); 51 | await Task.CompletedTask; 52 | } 53 | 54 | private async Task UpdateTile(CancellationToken cancellationToken) 55 | { 56 | for (; ; ) 57 | { 58 | if (cancellationToken.IsCancellationRequested) 59 | { 60 | return; 61 | } 62 | 63 | var memUsage = (int)_memUsageCounter.NextValue(); 64 | Text = string.Format(Format ?? "RAM\n{0} %", memUsage); 65 | await Task.Delay(1000, cancellationToken); 66 | } 67 | } 68 | } 69 | 70 | /// 71 | /// Based on: 72 | /// https://github.com/rocksdanister/lively/blob/d4972447531a0a670ad8f8c4724c7faf7c619d8b/src/livelywpf/livelywpf/Helpers/HWUsageMonitor.cs#L143 73 | /// 74 | public class GpuUsageTile : Tile 75 | { 76 | [Setting] public string? Format { get; set; } 77 | 78 | private List _gpuCounters = new(); 79 | 80 | public override async Task Init(CancellationToken cancellationToken) 81 | { 82 | var category = new PerformanceCounterCategory("GPU Engine"); 83 | 84 | _gpuCounters.AddRange(from string counterName in category.GetInstanceNames() 85 | where counterName.EndsWith("engtype_3D") 86 | from PerformanceCounter counter in category.GetCounters(counterName) 87 | where counter.CounterName == "Utilization Percentage" 88 | select counter); 89 | 90 | _ = Task.Run(() => UpdateTile(cancellationToken), cancellationToken); 91 | await Task.CompletedTask; 92 | } 93 | 94 | private async Task UpdateTile(CancellationToken cancellationToken) 95 | { 96 | for (; ; ) 97 | { 98 | if (cancellationToken.IsCancellationRequested) 99 | { 100 | return; 101 | } 102 | 103 | var usage = (int)_gpuCounters.Sum(x => x.NextValue()); 104 | Text = string.Format(Format ?? "GPU\n{0} %", usage); 105 | 106 | await Task.Delay(1000, cancellationToken); 107 | } 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Clock/Clock.md: -------------------------------------------------------------------------------- 1 | # Clock 2 | The clock plugin contains a collection of time related tiles. 3 | 4 | 5 | - [Clock](#clock) 6 | - [DigitalClockTile](#digitalclocktile) 7 | - [Settings](#settings) 8 | - [Example Config](#example-config) 9 | - [Format](#format) 10 | - [Interval](#interval) 11 | - [StopWatchTile](#stopwatchtile) 12 | - [Usage](#usage) 13 | - [Settings](#settings-1) 14 | - [Example Config](#example-config-1) 15 | - [Format](#format-1) 16 | - [Interval](#interval-1) 17 | - [HoldResetTime](#holdresettime) 18 | 19 | 20 | ## DigitalClockTile 21 | 22 | Shows the current time and/or date in digital format on a key. Time format can be customized. The update interval can be adjusted to fit the chosen time format. 23 | 24 | ### Settings 25 | 26 | | Setting | Default | Description | 27 | | -------- | --------- | ---------------------------------------------------------------------------------------------------- | 28 | | Format | `HH\\:mm` | [Format](https://learn.microsoft.com/en-us/dotnet/api/system.datetime.tostring?view=net-7.0) string. | 29 | | Interval | `1000` ms | Update interval. | 30 | 31 | ### Example Config 32 | 33 | ```json 34 | { 35 | "Plugin": "Clock", 36 | "Tile": "DigitalClockTile", 37 | "Settings": { 38 | "Format": "HH\\:mm\ndddd", 39 | "Interval": "60000" 40 | } 41 | } 42 | ``` 43 | 44 | #### Format 45 | Format string as documented [here](https://learn.microsoft.com/en-us/dotnet/api/system.datetime.tostring?view=net-7.0). 46 | 47 | ***Notice!*** Some characters like `:` must be escaped using `\`. The `\`-character itself must also be escaped in `JSON`. 48 | 49 | #### Interval 50 | Adjust the update interval to the desired resolution. If you include seconds in the time format you probably want to set the interval to `<= 1000 ms`. If your time format only includes hours an minutes, the interval might only need to be `~ 60000 ms`. 51 | 52 | 53 | 54 | ## StopWatchTile 55 | 56 | A stopwatch with customizable format. The update interval can be adjusted to fit the chosen time format. 57 | 58 | ### Usage 59 | Press the key once to start the stopwatch, press the key again to stop it. Hold the key for `500 ms` (configurable using `HoldToResetTime`) to reset the stopwatch. The activity indicator is shown while the stopwatch is running. 60 | 61 | 62 | ### Settings 63 | 64 | | Setting | Default | Description | 65 | | --------------- | ---------------- | --------------------------------------------------------------------------------------------------------------- | 66 | | Format | `\\⏱'\n'mm\\:ss` | [Format](https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-timespan-format-strings) string. | 67 | | Interval | `100` ms | Update interval. | 68 | | HoldToResetTime | `500` ms | Minimum time to hold the key down to reset the stopwatch. | 69 | 70 | ### Example Config 71 | 72 | A stopwatch configured with a format and interval allowing for higher resolution. 73 | 74 | ```json 75 | { 76 | "Plugin": "Clock", 77 | "Tile": "StopWatchTile", 78 | "Settings": { 79 | "Format": "\\⏱'\n'mm\\:ss\\.ff", 80 | "Interval": "10", 81 | "HoldToResetTime": "500" 82 | } 83 | } 84 | ``` 85 | 86 | #### Format 87 | Format string as documented [here](https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-timespan-format-strings). 88 | 89 | ***Notice!*** Some characters like `:` must be escaped using `\`. The `\`-character itself must also be escaped in `JSON`. 90 | 91 | #### Interval 92 | Adjust the update interval to the desired resolution. If you include seconds in the time format you probably want to set the interval to `<= 1000 ms`. If your time format includes milliseconds, the interval must be at the most `< 100 ms`, depending how high resolution you want. 93 | 94 | #### HoldResetTime 95 | The minimum amount of time in `milliseconds` to hold the associated key for before the stopwatch is reset. 96 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Clock/StopWatch.cs: -------------------------------------------------------------------------------- 1 | using CodeDeck.PluginAbstractions; 2 | using SixLabors.ImageSharp; 3 | using System; 4 | using System.Diagnostics; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace CodeDeck.Plugins.Plugins.Clock; 9 | 10 | public partial class Clock : CodeDeckPlugin 11 | { 12 | public partial class StopWatchTile : Tile 13 | { 14 | [Setting] public string Format { get; set; } = "\\⏱'\n'mm\\:ss"; 15 | [Setting] public int Interval { get; set; } = 100; 16 | [Setting] public int HoldToResetTime { get; set; } = 1000; 17 | 18 | private readonly Stopwatch _sw = new(); 19 | 20 | private DateTime? _timePressDown = null; 21 | 22 | private CancellationTokenSource? _ctsCheckLongPress; 23 | private CancellationTokenSource? _ctsFrameworkAndCheckLongPressCombined; 24 | private CancellationTokenSource? _ctsUpdateTile; 25 | private CancellationTokenSource? _ctsFrameworkAndUpdateTileCombined; 26 | 27 | 28 | public override async Task Init(CancellationToken cancellationToken) 29 | { 30 | FontSize = 25; 31 | SetText(TimeSpan.Zero); 32 | 33 | await Task.CompletedTask; 34 | } 35 | 36 | private void SetText(TimeSpan ts) 37 | { 38 | Text = ts.ToString(Format); 39 | } 40 | 41 | private async Task UpdateTile(CancellationToken cancellationToken) 42 | { 43 | for (; ; ) 44 | { 45 | if (cancellationToken.IsCancellationRequested) 46 | { 47 | return; 48 | } 49 | 50 | SetText(_sw.Elapsed); 51 | await Task.Delay(Interval, cancellationToken); 52 | } 53 | } 54 | 55 | private async Task CheckLongPress(CancellationToken cancellationToken) 56 | { 57 | for (; ; ) 58 | { 59 | if (cancellationToken.IsCancellationRequested) 60 | { 61 | return; 62 | } 63 | 64 | if (_timePressDown is not null && ((DateTime.Now - _timePressDown).Value.TotalMilliseconds >= HoldToResetTime)) 65 | { 66 | await OnLongPress(cancellationToken); 67 | return; 68 | } 69 | 70 | await Task.Delay(100, cancellationToken); 71 | } 72 | } 73 | 74 | public override async Task OnTilePressDown(CancellationToken cancellationToken) 75 | { 76 | _timePressDown = DateTime.Now; 77 | _ctsCheckLongPress = new CancellationTokenSource(); 78 | _ctsFrameworkAndCheckLongPressCombined = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _ctsCheckLongPress.Token); 79 | _ = CheckLongPress(_ctsFrameworkAndCheckLongPressCombined.Token); 80 | 81 | if (_sw.IsRunning) 82 | { 83 | _ctsFrameworkAndUpdateTileCombined?.Cancel(); 84 | _sw.Stop(); 85 | ShowIndicator = false; 86 | } 87 | else 88 | { 89 | _ctsUpdateTile = new CancellationTokenSource(); 90 | _ctsFrameworkAndUpdateTileCombined = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _ctsUpdateTile.Token); 91 | _ = UpdateTile(_ctsFrameworkAndUpdateTileCombined.Token); 92 | _sw.Start(); 93 | ShowIndicator = true; 94 | } 95 | 96 | SetText(_sw.Elapsed); 97 | 98 | await Task.CompletedTask; 99 | } 100 | 101 | public override Task OnTilePressUp(CancellationToken cancellationToken) 102 | { 103 | _ctsCheckLongPress?.Cancel(); 104 | return base.OnTilePressUp(cancellationToken); 105 | } 106 | 107 | private async Task OnLongPress(CancellationToken cancellationToken) 108 | { 109 | ShowIndicator = true; 110 | var c = IndicatorColor; 111 | IndicatorColor = Color.Red; 112 | await Task.Delay(500, cancellationToken); 113 | ShowIndicator = false; 114 | IndicatorColor = c; 115 | 116 | _sw.Reset(); 117 | SetText(_sw.Elapsed); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Template/Template.cs: -------------------------------------------------------------------------------- 1 | using CodeDeck.PluginAbstractions; 2 | using System; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | 7 | /// 8 | /// A wrapper class for the Plugin, this class MUST extend the CodeDeckPlugin class 9 | /// This class can be used as a container for static fields that are shared between Tile classes 10 | /// This class is never instantiated, but you may use a static constructor to initialize static fields: 11 | /// https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-constructors 12 | /// 13 | public class Template : CodeDeckPlugin 14 | { 15 | /// 16 | /// An example of a static field that can be shared between the Tile classes 17 | /// 18 | private static DateTime _dateTimeNow; 19 | 20 | /// 21 | /// A static constructor that initializes shared static fields 22 | /// 23 | static Template() 24 | { 25 | _dateTimeNow = DateTime.Now; 26 | } 27 | 28 | 29 | /// 30 | /// An example of how to implement a new Tile, a Tile class MUST extend the Tile class 31 | /// 32 | public class TemplateTileOne : Tile 33 | { 34 | /// 35 | /// A property annotated with the SettingAttribute 36 | /// This property will be automatically bound to the appropriate setting from the 37 | /// JSON configuration file. 38 | /// 39 | [Setting] public int? Counter { get; set; } 40 | 41 | /// 42 | /// The constructor for a Tile 43 | /// The constructor is called whenever a Tile is instantiated, but it's recommended to 44 | /// use the Init-method below instead. The Init-method supports cancellation, which is 45 | /// important to make sure that the plugin system can cleanly remove Tile-instances as 46 | /// needed. 47 | /// The constructor may be used for assigning private fields, but DO NOT start long 48 | /// running tasks in the constructor! 49 | /// 50 | public TemplateTileOne() 51 | { 52 | Counter = 0; 53 | } 54 | 55 | /// 56 | /// This method is called by the plugin system when this Tile is instantiated. 57 | /// Use this method as you would normally use the constructor. Make sure to forward 58 | /// the CancellationToken to any long running tasks or background tasks. 59 | /// 60 | /// 61 | public override Task Init(CancellationToken cancellationToken) 62 | { 63 | Text = $"TileOne\n{_dateTimeNow.ToShortTimeString()}\n{Counter}"; 64 | return base.Init(cancellationToken); 65 | } 66 | 67 | /// 68 | /// This method is called by the plugin system when a key associated with this 69 | /// Tile is pressed 70 | /// 71 | /// 72 | public override Task OnTilePressDown(CancellationToken cancellationToken) 73 | { 74 | return base.OnTilePressDown(cancellationToken); 75 | } 76 | 77 | /// 78 | /// This method is called by the plugin system when a key associated with this 79 | /// Tile is released 80 | /// 81 | /// 82 | public override Task OnTilePressUp(CancellationToken cancellationToken) 83 | { 84 | Counter++; 85 | Text = $"TileOne\n{_dateTimeNow.ToShortTimeString()}\n{Counter}"; 86 | return base.OnTilePressUp(cancellationToken); 87 | } 88 | 89 | /// 90 | /// This method is called by the plugin system when a Tile instance is removed, 91 | /// make sure to clean up any resources here. 92 | /// 93 | public override Task DeInit() 94 | { 95 | return base.DeInit(); 96 | } 97 | } 98 | 99 | 100 | /// 101 | /// A plugin my contain multiple Tile types 102 | /// 103 | public class TemplateTileTwo : Tile 104 | { 105 | public override Task Init(CancellationToken cancellationToken) 106 | { 107 | Text = "TileTwo"; 108 | return base.Init(cancellationToken); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/Weather/YrImageTile.cs: -------------------------------------------------------------------------------- 1 | using CodeDeck.PluginAbstractions; 2 | using SixLabors.ImageSharp; 3 | using System; 4 | using System.Net.Http; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using System.Net.Http.Json; 8 | using CodeDeck.Plugins.Plugins.Weather.Models.Yr; 9 | using System.IO; 10 | using System.Globalization; 11 | using System.Linq; 12 | 13 | namespace CodeDeck.Plugins.Plugins.Weather 14 | { 15 | public partial class Weather : CodeDeckPlugin 16 | { 17 | public class YrImageTile : Tile 18 | { 19 | [Setting] public int Interval { get; set; } = 10 * 60 * 1000; 20 | [Setting] public double Lat { get; set; } 21 | [Setting] public double Lon { get; set; } 22 | [Setting] public string? Period { get; set; } 23 | 24 | 25 | private static readonly HttpClient _client = new(); 26 | private const string API_BASE_URL = "https://api.met.no/weatherapi/locationforecast/2.0/"; 27 | private string _apiUrl = ""; 28 | 29 | public override async Task Init(CancellationToken cancellationToken) 30 | { 31 | _client.DefaultRequestHeaders.Add("User-Agent", "Code Deck"); 32 | 33 | var nfi = new NumberFormatInfo 34 | { 35 | NumberDecimalSeparator = ".", 36 | NumberDecimalDigits = 4 37 | }; 38 | 39 | _apiUrl = $"{API_BASE_URL}compact?lat={Lat.ToString(nfi)}&lon={Lon.ToString(nfi)}"; 40 | 41 | _ = Task.Run(() => BackgroundTask(cancellationToken), cancellationToken); 42 | 43 | await Task.CompletedTask; 44 | } 45 | 46 | public override async Task OnTilePressUp(CancellationToken cancellationToken) 47 | { 48 | await GetAsync(cancellationToken); 49 | } 50 | 51 | private async Task GetAsync(CancellationToken cancellationToken) 52 | { 53 | try 54 | { 55 | ShowIndicator = true; 56 | var data = await _client.GetFromJsonAsync(_apiUrl, cancellationToken); 57 | 58 | var timeSeries = data?.Properties?.Timeseries; 59 | if (timeSeries is null || !timeSeries.Any()) return null; 60 | 61 | var timeSeriesData = timeSeries.First().Data; 62 | 63 | string? symbolCode = null; 64 | 65 | switch (Period) 66 | { 67 | case null: 68 | case "Next1Hours": 69 | symbolCode = timeSeriesData?.Next1Hours?.Summary?.SymbolCode; 70 | break; 71 | case "Next6Hours": 72 | symbolCode = timeSeriesData?.Next6Hours?.Summary?.SymbolCode; 73 | break; 74 | case "Next12Hours": 75 | symbolCode = timeSeriesData?.Next12Hours?.Summary?.SymbolCode; 76 | break; 77 | 78 | default: 79 | break; 80 | } 81 | 82 | if (symbolCode is null) return null; 83 | 84 | var symbolFile = Path.GetFullPath($"Plugins/Weather/Icons/{symbolCode}.png"); 85 | if (!File.Exists(symbolFile)) return null; 86 | 87 | Image = Image.Load(symbolFile); 88 | 89 | return data; 90 | } 91 | catch (Exception e) 92 | { 93 | Console.WriteLine(e.Message); 94 | } 95 | finally 96 | { 97 | ShowIndicator = false; 98 | } 99 | 100 | return null; 101 | } 102 | 103 | private async Task BackgroundTask(CancellationToken cancellationToken) 104 | { 105 | for (; ; ) 106 | { 107 | if (cancellationToken.IsCancellationRequested) 108 | { 109 | return; 110 | } 111 | 112 | GetAsync(cancellationToken).Wait(cancellationToken); 113 | await Task.Delay(Interval, cancellationToken); 114 | } 115 | } 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/MediaKeys/MediaKeys.cs: -------------------------------------------------------------------------------- 1 | using CodeDeck.PluginAbstractions; 2 | using System; 3 | using System.Diagnostics; 4 | using System.Runtime.InteropServices; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace CodeDeck.Plugins.Plugins.Runner 9 | { 10 | public partial class MediaKeys : CodeDeckPlugin 11 | { 12 | public const int KEYEVENTF_EXTENTEDKEY = 1; 13 | public const int KEYEVENTF_KEYUP = 0; 14 | public const int VK_VOLUME_MUTE = 0xAD; 15 | public const int VK_VOLUME_DOWN = 0xAE; 16 | public const int VK_VOLUME_UP = 0xAF; 17 | public const int VK_MEDIA_NEXT_TRACK = 0xB0; 18 | public const int VK_MEDIA_PREV_TRACK = 0xB1; 19 | public const int VK_MEDIA_STOP = 0xB2; 20 | public const int VK_MEDIA_PLAY_PAUSE = 0xB3; 21 | 22 | [DllImport("user32.dll")] 23 | private static extern void keybd_event(byte virtualKey, byte scanCode, uint flags, IntPtr extraInfo); 24 | 25 | public class MuteTile : Tile 26 | { 27 | public override Task OnTilePressUp(CancellationToken cancellationToken) 28 | { 29 | keybd_event(VK_VOLUME_MUTE, 0, KEYEVENTF_EXTENTEDKEY, IntPtr.Zero); 30 | return Task.CompletedTask; 31 | } 32 | } 33 | 34 | public class VolumeDownTile : Tile 35 | { 36 | private CancellationTokenSource? _whileDownCts; 37 | 38 | public override async Task OnTilePressDown(CancellationToken cancellationToken) 39 | { 40 | _whileDownCts = new(); 41 | 42 | await Task.Run(async () => { 43 | while (!_whileDownCts?.IsCancellationRequested ?? false) 44 | { 45 | keybd_event(VK_VOLUME_DOWN, 0, KEYEVENTF_EXTENTEDKEY, IntPtr.Zero); 46 | await Task.Delay(75); 47 | } 48 | }, _whileDownCts.Token); 49 | } 50 | 51 | public override Task OnTilePressUp(CancellationToken cancellationToken) 52 | { 53 | _whileDownCts?.Cancel(); 54 | return Task.CompletedTask; 55 | } 56 | } 57 | 58 | public class VolumeUpTile : Tile 59 | { 60 | private CancellationTokenSource? _whileDownCts; 61 | 62 | public override async Task OnTilePressDown(CancellationToken cancellationToken) 63 | { 64 | _whileDownCts = new(); 65 | 66 | await Task.Run(async () => { 67 | while (!_whileDownCts?.IsCancellationRequested ?? false) 68 | { 69 | keybd_event(VK_VOLUME_UP, 0, KEYEVENTF_EXTENTEDKEY, IntPtr.Zero); 70 | await Task.Delay(75); 71 | } 72 | }, _whileDownCts.Token); 73 | } 74 | 75 | public override Task OnTilePressUp(CancellationToken cancellationToken) 76 | { 77 | _whileDownCts?.Cancel(); 78 | return Task.CompletedTask; 79 | } 80 | } 81 | 82 | public class NextTrackTile : Tile 83 | { 84 | public override Task OnTilePressUp(CancellationToken cancellationToken) 85 | { 86 | keybd_event(VK_MEDIA_NEXT_TRACK, 0, KEYEVENTF_EXTENTEDKEY, IntPtr.Zero); 87 | return Task.CompletedTask; 88 | } 89 | } 90 | 91 | public class PreviousTrackTile : Tile 92 | { 93 | public override Task OnTilePressUp(CancellationToken cancellationToken) 94 | { 95 | keybd_event(VK_MEDIA_PREV_TRACK, 0, KEYEVENTF_EXTENTEDKEY, IntPtr.Zero); 96 | return Task.CompletedTask; 97 | } 98 | } 99 | 100 | public class StopTile : Tile 101 | { 102 | public override Task OnTilePressUp(CancellationToken cancellationToken) 103 | { 104 | keybd_event(VK_MEDIA_STOP, 0, KEYEVENTF_EXTENTEDKEY, IntPtr.Zero); 105 | return Task.CompletedTask; 106 | } 107 | } 108 | 109 | public class PlayPauseTile : Tile 110 | { 111 | public override Task OnTilePressUp(CancellationToken cancellationToken) 112 | { 113 | keybd_event(VK_MEDIA_PLAY_PAUSE, 0, KEYEVENTF_EXTENTEDKEY, IntPtr.Zero); 114 | return Task.CompletedTask; 115 | } 116 | } 117 | 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /CodeDeck.Windows/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace CodeDeck.Windows.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CodeDeck.Windows.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). 65 | /// 66 | internal static System.Drawing.Icon icon { 67 | get { 68 | object obj = ResourceManager.GetObject("icon", resourceCulture); 69 | return ((System.Drawing.Icon)(obj)); 70 | } 71 | } 72 | 73 | /// 74 | /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). 75 | /// 76 | internal static System.Drawing.Icon icon_16 { 77 | get { 78 | object obj = ResourceManager.GetObject("icon-16", resourceCulture); 79 | return ((System.Drawing.Icon)(obj)); 80 | } 81 | } 82 | 83 | /// 84 | /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). 85 | /// 86 | internal static System.Drawing.Icon icon_16_white { 87 | get { 88 | object obj = ResourceManager.GetObject("icon-16-white", resourceCulture); 89 | return ((System.Drawing.Icon)(obj)); 90 | } 91 | } 92 | 93 | /// 94 | /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). 95 | /// 96 | internal static System.Drawing.Icon icon_16_white_outline { 97 | get { 98 | object obj = ResourceManager.GetObject("icon-16-white-outline", resourceCulture); 99 | return ((System.Drawing.Icon)(obj)); 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/HyperXCloudFlightWireless/HyperXCloudFlightWireless.cs: -------------------------------------------------------------------------------- 1 | using CodeDeck.PluginAbstractions; 2 | using HidSharp; 3 | using System; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | /// 10 | /// Inspired by: https://github.com/franco-giordano/cloud-flight-monitor 11 | /// 12 | public class HyperXCloudFlightWireless : CodeDeckPlugin 13 | { 14 | public class BatteryTile : Tile 15 | { 16 | [Setting] public string? Format { get; set; } 17 | [Setting] public string? FormatCharging { get; set; } 18 | [Setting] public string? FormatDisconnected { get; set; } 19 | [Setting] public int? Interval { get; set; } 20 | [Setting] public bool UseNGenuityBatteryMap { get; set; } = true; 21 | 22 | private int[] _batteryLevelsNGenuityMap = new int[] { 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 95, 95, 95, 95, 95, 90, 90, 90, 90, 90, 85, 85, 85, 85, 85, 80, 80, 80, 80, 80, 80, 75, 75, 75, 75, 75, 70, 70, 70, 70, 65, 65, 60, 60, 60, 60, 60, 55, 55, 50, 45, 45, 40, 40, 35, 35, 30, 30, 25, 25, 20, 20, 20, 20, 20, 20, 15, 15, 15, 15, 15, 15, 10, 10, 10, 10, 10, 10, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0 } 23 | .Reverse() 24 | .ToArray(); 25 | 26 | private HidDevice? _device; 27 | 28 | public override async Task Init(CancellationToken cancellationToken) 29 | { 30 | _device = DeviceList.Local 31 | .GetHidDevices(2385, 5828) 32 | .Where(x => x.GetMaxInputReportLength() == 20) 33 | .FirstOrDefault(); 34 | 35 | _ = Task.Run(() => BackgroundTask(cancellationToken), cancellationToken); 36 | 37 | await Task.CompletedTask; 38 | } 39 | 40 | public override async Task OnTilePressUp(CancellationToken cancellationToken) 41 | { 42 | await GetAsync(cancellationToken); 43 | } 44 | 45 | private async Task GetAsync(CancellationToken cancellationToken) 46 | { 47 | try 48 | { 49 | ShowIndicator = true; 50 | 51 | if (_device == null) return; 52 | 53 | HidStream s = _device.Open(); 54 | 55 | byte[] report = new byte[20]; 56 | report[0] = 0x21; 57 | report[1] = 0xff; 58 | report[2] = 0x05; 59 | 60 | await Task.Factory.FromAsync(s.BeginWrite, s.EndWrite, report, 0, report.Length, TaskCreationOptions.None); 61 | await Task.Factory.FromAsync(s.BeginRead, s.EndRead, report, 0, report.Length, TaskCreationOptions.None); 62 | 63 | var statusWord = BitConverter.ToUInt16(new byte[] { report[4], report[3] }, 0); 64 | 65 | // Bit 12 indicates charging 66 | var charging = (statusWord & (1 << 12)) != 0; 67 | 68 | // The SoC values below are NOT linear to the actual capacity, 69 | // it's probably linear to some voltage range 70 | if (charging) 71 | { 72 | // 6 bits are used for the battery level when charging, 73 | // that's a range from 0-63 74 | var v = statusWord & 0b111111; 75 | var p = v * 100 / 64; 76 | Text = string.Format(FormatCharging ?? "⚡\n{0}%", p); 77 | } 78 | else 79 | { 80 | // 9 bits are used for the battery level when discharging, 81 | // that's a range from 0-511 82 | var v = statusWord & 0b111111111; 83 | var p = v * 100 / 512; 84 | if (UseNGenuityBatteryMap) p = _batteryLevelsNGenuityMap[p]; 85 | Text = string.Format(Format ?? "🔋\n{0}%", p); 86 | } 87 | } 88 | catch 89 | { 90 | Text = FormatDisconnected ?? "🎧\n❌"; 91 | } 92 | finally 93 | { 94 | ShowIndicator = false; 95 | } 96 | } 97 | 98 | private async Task BackgroundTask(CancellationToken cancellationToken) 99 | { 100 | for (; ; ) 101 | { 102 | if (cancellationToken.IsCancellationRequested) 103 | { 104 | Debug.WriteLine($"{nameof(BackgroundTask)} in {nameof(BatteryTile)} was cancelled!"); 105 | return; 106 | } 107 | 108 | GetAsync(cancellationToken).Wait(cancellationToken); 109 | await Task.Delay(Interval ?? 10 * 60 * 1000, cancellationToken); 110 | } 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /CodeDeck/Fonts/UFL.txt: -------------------------------------------------------------------------------- 1 | ------------------------------- 2 | UBUNTU FONT LICENCE Version 1.0 3 | ------------------------------- 4 | 5 | PREAMBLE 6 | This licence allows the licensed fonts to be used, studied, modified and 7 | redistributed freely. The fonts, including any derivative works, can be 8 | bundled, embedded, and redistributed provided the terms of this licence 9 | are met. The fonts and derivatives, however, cannot be released under 10 | any other licence. The requirement for fonts to remain under this 11 | licence does not require any document created using the fonts or their 12 | derivatives to be published under this licence, as long as the primary 13 | purpose of the document is not to be a vehicle for the distribution of 14 | the fonts. 15 | 16 | DEFINITIONS 17 | "Font Software" refers to the set of files released by the Copyright 18 | Holder(s) under this licence and clearly marked as such. This may 19 | include source files, build scripts and documentation. 20 | 21 | "Original Version" refers to the collection of Font Software components 22 | as received under this licence. 23 | 24 | "Modified Version" refers to any derivative made by adding to, deleting, 25 | or substituting -- in part or in whole -- any of the components of the 26 | Original Version, by changing formats or by porting the Font Software to 27 | a new environment. 28 | 29 | "Copyright Holder(s)" refers to all individuals and companies who have a 30 | copyright ownership of the Font Software. 31 | 32 | "Substantially Changed" refers to Modified Versions which can be easily 33 | identified as dissimilar to the Font Software by users of the Font 34 | Software comparing the Original Version with the Modified Version. 35 | 36 | To "Propagate" a work means to do anything with it that, without 37 | permission, would make you directly or secondarily liable for 38 | infringement under applicable copyright law, except executing it on a 39 | computer or modifying a private copy. Propagation includes copying, 40 | distribution (with or without modification and with or without charging 41 | a redistribution fee), making available to the public, and in some 42 | countries other activities as well. 43 | 44 | PERMISSION & CONDITIONS 45 | This licence does not grant any rights under trademark law and all such 46 | rights are reserved. 47 | 48 | Permission is hereby granted, free of charge, to any person obtaining a 49 | copy of the Font Software, to propagate the Font Software, subject to 50 | the below conditions: 51 | 52 | 1) Each copy of the Font Software must contain the above copyright 53 | notice and this licence. These can be included either as stand-alone 54 | text files, human-readable headers or in the appropriate machine- 55 | readable metadata fields within text or binary files as long as those 56 | fields can be easily viewed by the user. 57 | 58 | 2) The font name complies with the following: 59 | (a) The Original Version must retain its name, unmodified. 60 | (b) Modified Versions which are Substantially Changed must be renamed to 61 | avoid use of the name of the Original Version or similar names entirely. 62 | (c) Modified Versions which are not Substantially Changed must be 63 | renamed to both (i) retain the name of the Original Version and (ii) add 64 | additional naming elements to distinguish the Modified Version from the 65 | Original Version. The name of such Modified Versions must be the name of 66 | the Original Version, with "derivative X" where X represents the name of 67 | the new work, appended to that name. 68 | 69 | 3) The name(s) of the Copyright Holder(s) and any contributor to the 70 | Font Software shall not be used to promote, endorse or advertise any 71 | Modified Version, except (i) as required by this licence, (ii) to 72 | acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with 73 | their explicit written permission. 74 | 75 | 4) The Font Software, modified or unmodified, in part or in whole, must 76 | be distributed entirely under this licence, and must not be distributed 77 | under any other licence. The requirement for fonts to remain under this 78 | licence does not affect any document created using the Font Software, 79 | except any version of the Font Software extracted from a document 80 | created using the Font Software may only be distributed under this 81 | licence. 82 | 83 | TERMINATION 84 | This licence becomes null and void if any of the above conditions are 85 | not met. 86 | 87 | DISCLAIMER 88 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 89 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 90 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF 91 | COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 92 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 93 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 94 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 95 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER 96 | DEALINGS IN THE FONT SOFTWARE. 97 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/WebRequest/WebRequest.cs: -------------------------------------------------------------------------------- 1 | using CodeDeck.PluginAbstractions; 2 | using SixLabors.ImageSharp.PixelFormats; 3 | using SixLabors.ImageSharp; 4 | using SixLabors.ImageSharp.Processing; 5 | using System; 6 | using System.Diagnostics; 7 | using System.Net.Http; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | 11 | namespace CodeDeck.Plugins.Plugins.WebRequest 12 | { 13 | public class WebRequest : CodeDeckPlugin 14 | { 15 | private static readonly HttpClient _client = new(); 16 | 17 | public class PlainTextTile : Tile 18 | { 19 | [Setting] public string? Url { get; set; } 20 | [Setting] public string? Format { get; set; } 21 | [Setting] public int? Interval { get; set; } 22 | 23 | public override async Task Init(CancellationToken cancellationToken) 24 | { 25 | _ = Task.Run(() => BackgroundTask(cancellationToken), cancellationToken); 26 | 27 | await Task.CompletedTask; 28 | } 29 | 30 | public override async Task OnTilePressUp(CancellationToken cancellationToken) 31 | { 32 | await GetAsync(cancellationToken); 33 | } 34 | 35 | private async Task GetAsync(CancellationToken cancellationToken) 36 | { 37 | try 38 | { 39 | ShowIndicator = true; 40 | Text = string.Format(Format ?? "{0}", await _client.GetStringAsync(Url, cancellationToken)); 41 | } 42 | catch (Exception) { } 43 | finally 44 | { 45 | ShowIndicator = false; 46 | } 47 | } 48 | 49 | private async Task BackgroundTask(CancellationToken cancellationToken) 50 | { 51 | for (; ; ) 52 | { 53 | if (cancellationToken.IsCancellationRequested) 54 | { 55 | Debug.WriteLine($"{nameof(BackgroundTask)} in {nameof(PlainTextTile)} with {Url} was cancelled!"); 56 | return; 57 | } 58 | 59 | GetAsync(cancellationToken).Wait(cancellationToken); 60 | await Task.Delay(Interval ?? 60000, cancellationToken); 61 | } 62 | } 63 | } 64 | 65 | public class ImageTile : Tile 66 | { 67 | [Setting] public string? Url { get; set; } 68 | [Setting] public int Interval { get; set; } = 60000; 69 | [Setting] public int Size { get; set; } = 72; 70 | [Setting] public bool Crop { get; set; } = false; 71 | 72 | public override async Task Init(CancellationToken cancellationToken) 73 | { 74 | _ = Task.Run(() => BackgroundTask(cancellationToken), cancellationToken); 75 | 76 | await Task.CompletedTask; 77 | } 78 | 79 | public override async Task OnTilePressUp(CancellationToken cancellationToken) 80 | { 81 | await GetAsync(cancellationToken); 82 | } 83 | 84 | private async Task GetAsync(CancellationToken cancellationToken) 85 | { 86 | try 87 | { 88 | ShowIndicator = true; 89 | var data = await _client.GetByteArrayAsync(Url, cancellationToken); 90 | var image = SixLabors.ImageSharp.Image.Load(data); 91 | 92 | if (image != null) 93 | { 94 | if (Crop) 95 | { 96 | var cropSize = Math.Min(image.Width, image.Height); 97 | 98 | image.Mutate(i => i.Crop(new Rectangle( 99 | image.Width / 2 - cropSize / 2, 100 | image.Height / 2 - cropSize / 2, 101 | cropSize, 102 | cropSize 103 | ))); 104 | } 105 | 106 | image.Mutate(i => i.Resize(new ResizeOptions() 107 | { 108 | Mode = ResizeMode.Max, 109 | Size = new Size(Size, Size) 110 | })); 111 | 112 | var i = new Image(Size, Size); 113 | i.Mutate(x => x.DrawImage(image, new Point((Size / 2) - image.Width / 2, (Size / 2) - image.Height / 2), 1f)); 114 | 115 | Image = i; 116 | } 117 | } 118 | catch (Exception) { } 119 | finally 120 | { 121 | ShowIndicator = false; 122 | } 123 | } 124 | 125 | private async Task BackgroundTask(CancellationToken cancellationToken) 126 | { 127 | for (; ; ) 128 | { 129 | if (cancellationToken.IsCancellationRequested) 130 | { 131 | return; 132 | } 133 | 134 | GetAsync(cancellationToken).Wait(cancellationToken); 135 | await Task.Delay(Interval, cancellationToken); 136 | } 137 | } 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /CodeDeck.Windows/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | ..\Resources\icon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 123 | 124 | 125 | ..\Resources\icon-16.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 126 | 127 | 128 | ..\Resources\icon-16-white.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 129 | 130 | 131 | ..\Resources\icon-16-white-outline.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 132 | 133 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/LogitechMice/GProXSuperlightBatteryTile.cs: -------------------------------------------------------------------------------- 1 | using CodeDeck.PluginAbstractions; 2 | using CodeDeck.Plugins.Plugins.LogitechMice.Hid; 3 | using HidSharp; 4 | using System; 5 | using System.Diagnostics; 6 | using System.Linq; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace CodeDeck.Plugins.Plugins.LogitechMice; 11 | 12 | public partial class LogitechMice : CodeDeckPlugin 13 | { 14 | public class GProXSuperlightBatteryTile : Tile 15 | { 16 | [Setting] public string? Format { get; set; } 17 | [Setting] public string? FormatDisconnected { get; set; } 18 | [Setting] public int? Interval { get; set; } 19 | [Setting] public byte DeviceIndex { get; set; } = 0x01; 20 | 21 | private HidDevice? _device; 22 | private byte? _batteryFeatureIndex = null; 23 | private int? _percentage = null; 24 | private DateTime _lastHandleHidPpReport = DateTime.Now; 25 | 26 | public override async Task Init(CancellationToken cancellationToken) 27 | { 28 | _device = FindDevice(); 29 | 30 | _ = Task.Run(() => ReadTask(cancellationToken), cancellationToken); 31 | _ = Task.Run(() => BackgroundTask(cancellationToken), cancellationToken); 32 | 33 | Text = FormatDisconnected ?? "🖱\n❌"; 34 | 35 | await GetBatteryInformation(cancellationToken); 36 | } 37 | 38 | public override async Task OnTilePressUp(CancellationToken cancellationToken) 39 | { 40 | await GetBatteryInformation(cancellationToken); 41 | } 42 | 43 | private static HidDevice? FindDevice() 44 | { 45 | return DeviceList.Local 46 | .GetHidDevices(0x046D, 0xC547) 47 | .Where(x => x.GetMaxInputReportLength() == 20) 48 | .FirstOrDefault(); 49 | } 50 | 51 | private async Task ReadTask(CancellationToken cancellationToken) 52 | { 53 | HidStream? hidStream = null; 54 | var buffer = new byte[512]; 55 | 56 | while (!cancellationToken.IsCancellationRequested) 57 | { 58 | try 59 | { 60 | _device = FindDevice(); 61 | if (_device == null) 62 | { 63 | await Task.Delay(1000, cancellationToken); 64 | continue; 65 | } 66 | 67 | hidStream = _device.Open(); 68 | if (hidStream == null) 69 | { 70 | await Task.Delay(1000, cancellationToken); 71 | continue; 72 | } 73 | 74 | var readBytes = await Task.Factory.FromAsync(hidStream.BeginRead, hidStream.EndRead, buffer, 0, buffer.Length, TaskCreationOptions.None); 75 | var data = buffer[0..readBytes]; 76 | HandleHidPpReport(data); 77 | } 78 | catch (TimeoutException) 79 | { 80 | if ((DateTime.Now - _lastHandleHidPpReport).TotalSeconds > 60) 81 | { 82 | Text = FormatDisconnected ?? "🖱\n❌"; 83 | } 84 | } 85 | catch (Exception e) 86 | { 87 | Debug.WriteLine($"{nameof(ReadTask)}: Exception: '{e.Message}'"); 88 | } 89 | } 90 | } 91 | 92 | private void HandleHidPpReport(byte[] data) 93 | { 94 | _lastHandleHidPpReport = DateTime.Now; 95 | 96 | var p = HidPpReport.FromBytes(data); 97 | if (p == null) return; 98 | if (p.DeviceIndex != DeviceIndex) return; 99 | if (p.FeatureIndex == 0x09) return; // Mouse move? 100 | 101 | // Feature index reply for unified battery (should be 0x06) 102 | if (p.FeatureIndex == HidPp.HIDPP_PAGE_ROOT_IDX && 103 | p.FuncIndex == HidPp.FUNCTION_ROOT_GET_FEATURE) 104 | { 105 | _batteryFeatureIndex = p.Params[0]; 106 | } 107 | // Unified battery result: feature index is the returned index (0x06), FuncIndex == 1 gives percentage in Params[0] 108 | else if (p.FeatureIndex == _batteryFeatureIndex && p.FuncIndex == 0x01) 109 | { 110 | var batteryLevel = p.Params[0]; 111 | _percentage = batteryLevel; 112 | Text = string.Format(Format ?? "🖱\n{0}%", _percentage); 113 | } 114 | } 115 | 116 | private async Task RequestBatteryInformationFeatureIndex(CancellationToken cancellationToken) 117 | { 118 | try 119 | { 120 | ShowIndicator = true; 121 | 122 | _device = FindDevice(); 123 | if (_device == null) throw new NullReferenceException(nameof(_device)); 124 | 125 | using var s = _device.Open(); 126 | 127 | var packet = HidPp.CreateGetFeatureIndexPacket(DeviceIndex, HidPp.HIDPP_FEATURE_UNIFIED_BATTERY); 128 | byte[] report = new byte[_device.GetMaxInputReportLength()]; 129 | Array.Copy(packet, report, packet.Length); 130 | await s.WriteAsync(report, 0, report.Length, cancellationToken); 131 | } 132 | catch (Exception e) 133 | { 134 | Debug.WriteLine($"{nameof(RequestBatteryInformationFeatureIndex)}: Exception: '{e.Message}'"); 135 | } 136 | finally 137 | { 138 | ShowIndicator = false; 139 | } 140 | } 141 | 142 | private async Task GetBatteryInformation(CancellationToken cancellationToken) 143 | { 144 | try 145 | { 146 | ShowIndicator = true; 147 | 148 | _device = FindDevice(); 149 | if (_device == null) throw new NullReferenceException(nameof(_device)); 150 | 151 | using var s = _device.Open(); 152 | 153 | await RequestBatteryInformationFeatureIndex(cancellationToken); 154 | await Task.Delay(250, cancellationToken); 155 | if (_batteryFeatureIndex == null) throw new Exception($"'{nameof(_batteryFeatureIndex)}' is still 'null'"); 156 | 157 | var packet = HidPp.CreateGetUnifiedBatteryPacket(DeviceIndex, _batteryFeatureIndex.Value); 158 | byte[] report = new byte[_device.GetMaxInputReportLength()]; 159 | Array.Copy(packet, report, packet.Length); 160 | await s.WriteAsync(report, 0, report.Length, cancellationToken); 161 | } 162 | catch (Exception e) 163 | { 164 | Debug.WriteLine($"{nameof(GetBatteryInformation)}: Exception: '{e.Message}'"); 165 | Text = FormatDisconnected ?? "🖱\n❌"; 166 | } 167 | finally 168 | { 169 | ShowIndicator = false; 170 | } 171 | } 172 | 173 | private async Task BackgroundTask(CancellationToken cancellationToken) 174 | { 175 | while (!cancellationToken.IsCancellationRequested) 176 | { 177 | await GetBatteryInformation(cancellationToken); 178 | await Task.Delay(Interval ?? 10 * 60 * 1000, cancellationToken); 179 | } 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | 33 | # Visual Studio 2015/2017 cache/options directory 34 | .vs/ 35 | # Uncomment if you have tasks that create the project's static files in wwwroot 36 | #wwwroot/ 37 | 38 | # Visual Studio 2017 auto generated files 39 | Generated\ Files/ 40 | 41 | # MSTest test Results 42 | [Tt]est[Rr]esult*/ 43 | [Bb]uild[Ll]og.* 44 | 45 | # NUnit 46 | *.VisualState.xml 47 | TestResult.xml 48 | nunit-*.xml 49 | 50 | # Build Results of an ATL Project 51 | [Dd]ebugPS/ 52 | [Rr]eleasePS/ 53 | dlldata.c 54 | 55 | # Benchmark Results 56 | BenchmarkDotNet.Artifacts/ 57 | 58 | # .NET Core 59 | project.lock.json 60 | project.fragment.lock.json 61 | artifacts/ 62 | 63 | # StyleCop 64 | StyleCopReport.xml 65 | 66 | # Files built by Visual Studio 67 | *_i.c 68 | *_p.c 69 | *_h.h 70 | *.ilk 71 | *.meta 72 | *.obj 73 | *.iobj 74 | *.pch 75 | *.pdb 76 | *.ipdb 77 | *.pgc 78 | *.pgd 79 | *.rsp 80 | *.sbr 81 | *.tlb 82 | *.tli 83 | *.tlh 84 | *.tmp 85 | *.tmp_proj 86 | *_wpftmp.csproj 87 | *.log 88 | *.vspscc 89 | *.vssscc 90 | .builds 91 | *.pidb 92 | *.svclog 93 | *.scc 94 | 95 | # Chutzpah Test files 96 | _Chutzpah* 97 | 98 | # Visual C++ cache files 99 | ipch/ 100 | *.aps 101 | *.ncb 102 | *.opendb 103 | *.opensdf 104 | *.sdf 105 | *.cachefile 106 | *.VC.db 107 | *.VC.VC.opendb 108 | 109 | # Visual Studio profiler 110 | *.psess 111 | *.vsp 112 | *.vspx 113 | *.sap 114 | 115 | # Visual Studio Trace Files 116 | *.e2e 117 | 118 | # TFS 2012 Local Workspace 119 | $tf/ 120 | 121 | # Guidance Automation Toolkit 122 | *.gpState 123 | 124 | # ReSharper is a .NET coding add-in 125 | _ReSharper*/ 126 | *.[Rr]e[Ss]harper 127 | *.DotSettings.user 128 | 129 | # JustCode is a .NET coding add-in 130 | .JustCode 131 | 132 | # TeamCity is a build add-in 133 | _TeamCity* 134 | 135 | # DotCover is a Code Coverage Tool 136 | *.dotCover 137 | 138 | # AxoCover is a Code Coverage Tool 139 | .axoCover/* 140 | !.axoCover/settings.json 141 | 142 | # Visual Studio code coverage results 143 | *.coverage 144 | *.coveragexml 145 | 146 | # NCrunch 147 | _NCrunch_* 148 | .*crunch*.local.xml 149 | nCrunchTemp_* 150 | 151 | # MightyMoose 152 | *.mm.* 153 | AutoTest.Net/ 154 | 155 | # Web workbench (sass) 156 | .sass-cache/ 157 | 158 | # Installshield output folder 159 | [Ee]xpress/ 160 | 161 | # DocProject is a documentation generator add-in 162 | DocProject/buildhelp/ 163 | DocProject/Help/*.HxT 164 | DocProject/Help/*.HxC 165 | DocProject/Help/*.hhc 166 | DocProject/Help/*.hhk 167 | DocProject/Help/*.hhp 168 | DocProject/Help/Html2 169 | DocProject/Help/html 170 | 171 | # Click-Once directory 172 | publish/ 173 | 174 | # Publish Web Output 175 | *.[Pp]ublish.xml 176 | *.azurePubxml 177 | # Note: Comment the next line if you want to checkin your web deploy settings, 178 | # but database connection strings (with potential passwords) will be unencrypted 179 | *.pubxml 180 | *.publishproj 181 | 182 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 183 | # checkin your Azure Web App publish settings, but sensitive information contained 184 | # in these scripts will be unencrypted 185 | PublishScripts/ 186 | 187 | # NuGet Packages 188 | *.nupkg 189 | # NuGet Symbol Packages 190 | *.snupkg 191 | # The packages folder can be ignored because of Package Restore 192 | **/[Pp]ackages/* 193 | # except build/, which is used as an MSBuild target. 194 | !**/[Pp]ackages/build/ 195 | # Uncomment if necessary however generally it will be regenerated when needed 196 | #!**/[Pp]ackages/repositories.config 197 | # NuGet v3's project.json files produces more ignorable files 198 | *.nuget.props 199 | *.nuget.targets 200 | 201 | # Microsoft Azure Build Output 202 | csx/ 203 | *.build.csdef 204 | 205 | # Microsoft Azure Emulator 206 | ecf/ 207 | rcf/ 208 | 209 | # Windows Store app package directories and files 210 | AppPackages/ 211 | BundleArtifacts/ 212 | Package.StoreAssociation.xml 213 | _pkginfo.txt 214 | *.appx 215 | *.appxbundle 216 | *.appxupload 217 | 218 | # Visual Studio cache files 219 | # files ending in .cache can be ignored 220 | *.[Cc]ache 221 | # but keep track of directories ending in .cache 222 | !?*.[Cc]ache/ 223 | 224 | # Others 225 | ClientBin/ 226 | ~$* 227 | *~ 228 | *.dbmdl 229 | *.dbproj.schemaview 230 | *.jfm 231 | *.pfx 232 | *.publishsettings 233 | orleans.codegen.cs 234 | 235 | # Including strong name files can present a security risk 236 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 237 | #*.snk 238 | 239 | # Since there are multiple workflows, uncomment next line to ignore bower_components 240 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 241 | #bower_components/ 242 | 243 | # RIA/Silverlight projects 244 | Generated_Code/ 245 | 246 | # Backup & report files from converting an old project file 247 | # to a newer Visual Studio version. Backup files are not needed, 248 | # because we have git ;-) 249 | _UpgradeReport_Files/ 250 | Backup*/ 251 | UpgradeLog*.XML 252 | UpgradeLog*.htm 253 | ServiceFabricBackup/ 254 | *.rptproj.bak 255 | 256 | # SQL Server files 257 | *.mdf 258 | *.ldf 259 | *.ndf 260 | 261 | # Business Intelligence projects 262 | *.rdl.data 263 | *.bim.layout 264 | *.bim_*.settings 265 | *.rptproj.rsuser 266 | *- [Bb]ackup.rdl 267 | *- [Bb]ackup ([0-9]).rdl 268 | *- [Bb]ackup ([0-9][0-9]).rdl 269 | 270 | # Microsoft Fakes 271 | FakesAssemblies/ 272 | 273 | # GhostDoc plugin setting file 274 | *.GhostDoc.xml 275 | 276 | # Node.js Tools for Visual Studio 277 | .ntvs_analysis.dat 278 | node_modules/ 279 | 280 | # Visual Studio 6 build log 281 | *.plg 282 | 283 | # Visual Studio 6 workspace options file 284 | *.opt 285 | 286 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 287 | *.vbw 288 | 289 | # Visual Studio LightSwitch build output 290 | **/*.HTMLClient/GeneratedArtifacts 291 | **/*.DesktopClient/GeneratedArtifacts 292 | **/*.DesktopClient/ModelManifest.xml 293 | **/*.Server/GeneratedArtifacts 294 | **/*.Server/ModelManifest.xml 295 | _Pvt_Extensions 296 | 297 | # Paket dependency manager 298 | .paket/paket.exe 299 | paket-files/ 300 | 301 | # FAKE - F# Make 302 | .fake/ 303 | 304 | # CodeRush personal settings 305 | .cr/personal 306 | 307 | # Python Tools for Visual Studio (PTVS) 308 | __pycache__/ 309 | *.pyc 310 | 311 | # Cake - Uncomment if you are using it 312 | # tools/** 313 | # !tools/packages.config 314 | 315 | # Tabs Studio 316 | *.tss 317 | 318 | # Telerik's JustMock configuration file 319 | *.jmconfig 320 | 321 | # BizTalk build output 322 | *.btp.cs 323 | *.btm.cs 324 | *.odx.cs 325 | *.xsd.cs 326 | 327 | # OpenCover UI analysis results 328 | OpenCover/ 329 | 330 | # Azure Stream Analytics local run output 331 | ASALocalRun/ 332 | 333 | # MSBuild Binary and Structured Log 334 | *.binlog 335 | 336 | # NVidia Nsight GPU debugger configuration file 337 | *.nvuser 338 | 339 | # MFractors (Xamarin productivity tool) working folder 340 | .mfractor/ 341 | 342 | # Local History for Visual Studio 343 | .localhistory/ 344 | 345 | # BeatPulse healthcheck temp database 346 | healthchecksdb 347 | 348 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 349 | MigrationBackup/ 350 | -------------------------------------------------------------------------------- /CodeDeck.Plugins/Plugins/LogitechMice/G703BatteryTile.cs: -------------------------------------------------------------------------------- 1 | using CodeDeck.PluginAbstractions; 2 | using CodeDeck.Plugins.Plugins.LogitechMice.Hid; 3 | using HidSharp; 4 | using System; 5 | using System.Diagnostics; 6 | using System.Linq; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace CodeDeck.Plugins.Plugins.LogitechMice; 11 | 12 | public partial class LogitechMice : CodeDeckPlugin 13 | { 14 | public class G703BatteryTile : Tile 15 | { 16 | [Setting] public string? Format { get; set; } 17 | [Setting] public string? FormatDisconnected { get; set; } 18 | [Setting] public int? Interval { get; set; } 19 | [Setting] public byte DeviceIndex { get; set; } = 0x01; 20 | 21 | private HidDevice? _device; 22 | private byte? _batteryFeatureIndex = null; 23 | private double? _voltage = null; 24 | private int? _percentage = null; 25 | private DateTime _lastHandleHidPpReport = DateTime.Now; 26 | 27 | public override async Task Init(CancellationToken cancellationToken) 28 | { 29 | _device = FindDevice(); 30 | 31 | _ = Task.Run(() => ReadTask(cancellationToken), cancellationToken); 32 | _ = Task.Run(() => BackgroundTask(cancellationToken), cancellationToken); 33 | 34 | Text = FormatDisconnected ?? "🖱\n❌"; 35 | 36 | await GetBatteryInformation(cancellationToken); 37 | } 38 | 39 | public override async Task OnTilePressUp(CancellationToken cancellationToken) 40 | { 41 | await GetBatteryInformation(cancellationToken); 42 | } 43 | 44 | private static HidDevice? FindDevice() 45 | { 46 | return DeviceList.Local 47 | .GetHidDevices(0x046D, 0xC539) 48 | .Where(x => x.GetMaxInputReportLength() == 20) 49 | .FirstOrDefault(); 50 | } 51 | 52 | private async Task ReadTask(CancellationToken cancellationToken) 53 | { 54 | HidStream? hidStream = null; 55 | var buffer = new byte[512]; 56 | 57 | while (!cancellationToken.IsCancellationRequested) 58 | { 59 | try 60 | { 61 | _device = FindDevice(); 62 | if (_device == null) 63 | { 64 | await Task.Delay(1000, cancellationToken); 65 | continue; 66 | } 67 | 68 | hidStream = _device.Open(); 69 | if (hidStream == null) 70 | { 71 | await Task.Delay(1000, cancellationToken); 72 | continue; 73 | } 74 | 75 | var readBytes = await Task.Factory.FromAsync(hidStream.BeginRead, hidStream.EndRead, buffer, 0, buffer.Length, TaskCreationOptions.None); 76 | var data = buffer[0..readBytes]; 77 | HandleHidPpReport(data); 78 | } 79 | catch (TimeoutException) 80 | { 81 | if ((DateTime.Now - _lastHandleHidPpReport).TotalSeconds > 60) 82 | { 83 | Text = FormatDisconnected ?? "🖱\n❌"; 84 | } 85 | } 86 | catch (Exception e) 87 | { 88 | Debug.WriteLine($"{nameof(RequestBatteryInformationFeatureIndex)}: Exception: '{e.Message}'"); 89 | } 90 | } 91 | } 92 | 93 | private void HandleHidPpReport(byte[] data) 94 | { 95 | _lastHandleHidPpReport = DateTime.Now; 96 | 97 | var p = HidPpReport.FromBytes(data); 98 | if (p == null) return; 99 | if (p.DeviceIndex != DeviceIndex) return; 100 | if (p.FeatureIndex == 0x09) return; // Mouse move? 101 | 102 | // Get feature response 103 | if (p.FeatureIndex == HidPp.HIDPP_PAGE_ROOT_IDX && 104 | p.FuncIndex == HidPp.FUNCTION_ROOT_GET_FEATURE) 105 | { 106 | _batteryFeatureIndex = p.Params[0]; 107 | } 108 | // Get battery information result 109 | else if (_batteryFeatureIndex != null && 110 | p.FeatureIndex == _batteryFeatureIndex && 111 | p.FuncIndex == HidPp.CMD_BATTERY_VOLTAGE_GET_BATTERY_VOLTAGE) 112 | { 113 | var voltage = (p.Params[0] << 8 | p.Params[1]); 114 | _voltage = voltage / 1000.0; 115 | _percentage = GetBatteryPercentageFromVoltage(voltage); 116 | 117 | Text = string.Format(Format ?? "🖱\n{0}%\n{1:N2}V", _percentage, _voltage); 118 | } 119 | } 120 | 121 | private async Task RequestBatteryInformationFeatureIndex(CancellationToken cancellationToken) 122 | { 123 | try 124 | { 125 | ShowIndicator = true; 126 | 127 | _device = FindDevice(); 128 | if (_device == null) throw new NullReferenceException(nameof(_device)); 129 | 130 | using var s = _device.Open(); 131 | 132 | var packet = HidPp.CreateGetFeatureIndexPacket(DeviceIndex, HidPp.HIDPP_FEATURE_BATTERY_VOLTAGE); 133 | byte[] report = new byte[_device.GetMaxInputReportLength()]; 134 | Array.Copy(packet, report, packet.Length); 135 | await s.WriteAsync(report, 0, report.Length, cancellationToken); 136 | } 137 | catch (Exception e) 138 | { 139 | Debug.WriteLine($"{nameof(RequestBatteryInformationFeatureIndex)}: Exception: '{e.Message}'"); 140 | } 141 | finally 142 | { 143 | ShowIndicator = false; 144 | } 145 | } 146 | 147 | private async Task GetBatteryInformation(CancellationToken cancellationToken) 148 | { 149 | try 150 | { 151 | ShowIndicator = true; 152 | 153 | _device = FindDevice(); 154 | if (_device == null) throw new NullReferenceException(nameof(_device)); 155 | 156 | using var s = _device.Open(); 157 | 158 | await RequestBatteryInformationFeatureIndex(cancellationToken); 159 | await Task.Delay(250, cancellationToken); 160 | if (_batteryFeatureIndex == null) throw new Exception($"'{nameof(_batteryFeatureIndex)}' is still 'null'"); 161 | 162 | var packet = HidPp.CreateGetBatteryInformationPacket(DeviceIndex, _batteryFeatureIndex.Value); 163 | byte[] report = new byte[_device.GetMaxInputReportLength()]; 164 | Array.Copy(packet, report, packet.Length); 165 | await s.WriteAsync(report, 0, report.Length, cancellationToken); 166 | } 167 | catch (Exception e) 168 | { 169 | Debug.WriteLine($"{nameof(GetBatteryInformation)}: Exception: '{e.Message}'"); 170 | Text = FormatDisconnected ?? "🖱\n❌"; 171 | } 172 | finally 173 | { 174 | ShowIndicator = false; 175 | } 176 | } 177 | 178 | private async Task BackgroundTask(CancellationToken cancellationToken) 179 | { 180 | while (!cancellationToken.IsCancellationRequested) 181 | { 182 | await GetBatteryInformation(cancellationToken); 183 | await Task.Delay(Interval ?? 10 * 60 * 1000, cancellationToken); 184 | } 185 | } 186 | 187 | private static readonly int[] _voltagesToPercentLut = { 188 | 4186, 4156, 4143, 4133, 4122, 4113, 4103, 4094, 4086, 4075, 189 | 4067, 4059, 4051, 4043, 4035, 4027, 4019, 4011, 4003, 3997, 190 | 3989, 3983, 3976, 3969, 3961, 3955, 3949, 3942, 3935, 3929, 191 | 3922, 3916, 3909, 3902, 3896, 3890, 3883, 3877, 3870, 3865, 192 | 3859, 3853, 3848, 3842, 3837, 3833, 3828, 3824, 3819, 3815, 193 | 3811, 3808, 3804, 3800, 3797, 3793, 3790, 3787, 3784, 3781, 194 | 3778, 3775, 3772, 3770, 3767, 3764, 3762, 3759, 3757, 3754, 195 | 3751, 3748, 3744, 3741, 3737, 3734, 3730, 3726, 3724, 3720, 196 | 3717, 3714, 3710, 3706, 3702, 3697, 3693, 3688, 3683, 3677, 197 | 3671, 3666, 3662, 3658, 3654, 3646, 3633, 3612, 3579, 3537 198 | }; 199 | 200 | private static int GetBatteryPercentageFromVoltage(int voltage) 201 | { 202 | for (int i = 0; i < _voltagesToPercentLut.Length; i++) 203 | { 204 | if (voltage >= _voltagesToPercentLut[i]) return 100 - i; 205 | } 206 | return 0; 207 | } 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /CodeDeck/ConfigurationProvider.cs: -------------------------------------------------------------------------------- 1 | using CodeDeck.Models; 2 | using CodeDeck.Models.Configuration; 3 | using Microsoft.Extensions.Logging; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text.Json; 9 | using System.Text.Json.Serialization; 10 | using System.Threading.Tasks; 11 | 12 | namespace CodeDeck 13 | { 14 | public class ConfigurationProvider 15 | { 16 | private StreamDeckConfiguration _loadedConfiguration; 17 | private List _loadedFlatConfiguration; 18 | private readonly ILogger _logger; 19 | private readonly FileSystemWatcher _fileSystemWatcher; 20 | 21 | public event EventHandler? ConfigurationChanged; 22 | 23 | public const string CONFIGURATION_FILE_NAME = "deck.json"; 24 | public const string CONFIGURATION_FOLDER_NAME = ".codedeck"; 25 | 26 | public static readonly string UserFolder; 27 | public static readonly string ConfigFolder; 28 | public static readonly string ConfigFile; 29 | 30 | public StreamDeckConfiguration LoadedConfiguration => _loadedConfiguration; 31 | 32 | public List LoadedFlatConfiguration => _loadedFlatConfiguration; 33 | 34 | static ConfigurationProvider() 35 | { 36 | UserFolder = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); 37 | ConfigFolder = Path.Combine(UserFolder, CONFIGURATION_FOLDER_NAME); 38 | ConfigFile = Path.Combine(ConfigFolder, CONFIGURATION_FILE_NAME); 39 | } 40 | 41 | public ConfigurationProvider(ILogger logger) 42 | { 43 | _logger = logger; 44 | 45 | // Make sure configuration folder exists 46 | if (!Directory.Exists(ConfigFolder)) 47 | { 48 | Directory.CreateDirectory(ConfigFolder); 49 | } 50 | 51 | // Load configuration from file or load a default configuration 52 | _loadedConfiguration = LoadConfiguration() ?? CreateDefaultConfiguration(); 53 | _loadedFlatConfiguration = GetFlatConfiguration(_loadedConfiguration); 54 | 55 | // Set up file system watcher 56 | _fileSystemWatcher = new FileSystemWatcher(ConfigFolder) 57 | { 58 | Filter = CONFIGURATION_FILE_NAME, 59 | EnableRaisingEvents = true, 60 | NotifyFilter = NotifyFilters.LastWrite 61 | }; 62 | 63 | // Register file system watcher changed event 64 | _fileSystemWatcher.Changed += Handle_FileSystemWatcher_Changed; 65 | } 66 | 67 | private async void Handle_FileSystemWatcher_Changed(object sender, FileSystemEventArgs e) 68 | { 69 | // TODO: Wait for file access ready in a better way 70 | await Task.Delay(500); // Wait for file to finish writing 71 | 72 | // Load configuration from file or load a default configuration 73 | _loadedConfiguration = LoadConfiguration() ?? CreateDefaultConfiguration(); 74 | _loadedFlatConfiguration = GetFlatConfiguration(_loadedConfiguration); 75 | 76 | // Invoke event handlers 77 | ConfigurationChanged?.Invoke(this, e); 78 | } 79 | 80 | private StreamDeckConfiguration? LoadConfiguration() 81 | { 82 | try 83 | { 84 | if (!DoesConfigurationFileExists()) 85 | { 86 | _logger.LogError($"Configuration file does not exist. Filename: {ConfigFile}"); 87 | var defaultConfiguration = CreateDefaultConfiguration(); 88 | var defaultJson = JsonSerializer.Serialize(defaultConfiguration, 89 | new JsonSerializerOptions() 90 | { 91 | WriteIndented = true, 92 | DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull 93 | }); 94 | File.WriteAllText(ConfigFile, defaultJson); 95 | _logger.LogWarning($"Default configuration file created. Filename: {ConfigFile}"); 96 | } 97 | 98 | var json = File.ReadAllText(ConfigFile); 99 | 100 | var configuration = JsonSerializer.Deserialize(json, new JsonSerializerOptions() 101 | { 102 | AllowTrailingCommas = true, 103 | ReadCommentHandling = JsonCommentHandling.Skip 104 | }); 105 | 106 | if (configuration is not null) 107 | { 108 | return configuration; 109 | } 110 | } 111 | catch (Exception e) 112 | { 113 | _logger.LogError($"Could not load configuration. Exception: {e.Message}"); 114 | } 115 | 116 | return null; 117 | } 118 | 119 | 120 | private static List GetFlatConfiguration(StreamDeckConfiguration configuration) 121 | { 122 | var flatConfiguration = ( 123 | from profile in configuration.Profiles 124 | from page in profile.Pages 125 | from key in page.Keys 126 | select new FlatKeyConfiguration(key, page, profile) 127 | ).ToList(); 128 | 129 | return flatConfiguration; 130 | } 131 | 132 | public static bool DoesConfigurationFileExists() 133 | { 134 | return File.Exists(ConfigFile); 135 | } 136 | 137 | public static StreamDeckConfiguration CreateDefaultConfiguration() 138 | { 139 | var config = new StreamDeckConfiguration 140 | { 141 | Brightness = 100, 142 | 143 | Profiles = new List() 144 | { 145 | new Profile() 146 | { 147 | Name = Profile.PROFILE_DEFAULT_NAME, 148 | Pages = new List() 149 | { 150 | new Page() 151 | { 152 | Name = Page.PAGE_DEFAULT_NAME, 153 | Keys = new List() 154 | { 155 | new Key() 156 | { 157 | Index = 1, 158 | Text = "CODE", 159 | BackgroundColor = "#0b367f" 160 | }, 161 | new Key() 162 | { 163 | Index = 2, 164 | Plugin = "Runner", 165 | Tile = "OpenWebsiteTile", 166 | Image = "Images/icon.png", 167 | ImagePadding = 5, 168 | Settings = new() { 169 | { "Url", "https://heinandre.no/code-deck" } 170 | }, 171 | }, 172 | new Key() 173 | { 174 | Index = 3, 175 | Text = "DECK", 176 | BackgroundColor = "#47750b" 177 | }, 178 | new Key() 179 | { 180 | Index = 11, 181 | Plugin = "Counter", 182 | Tile = "CounterTile", 183 | BackgroundColor = "#b52610" 184 | }, 185 | new Key() 186 | { 187 | Index = 13, 188 | Profile = "DefaultProfile", 189 | Page = "Page2", 190 | Text = "GO TO\nPAGE 2", 191 | }, 192 | } 193 | }, 194 | new Page() 195 | { 196 | Name = "Page2", 197 | Keys = new List() 198 | { 199 | new Key() 200 | { 201 | Index = 0, 202 | Text = "BACK", 203 | KeyType = Key.KEY_TYPE_GO_BACK, 204 | BackgroundColor = "#333333" 205 | }, 206 | new Key() 207 | { 208 | Index = 1, 209 | Text = "THIS IS\nPAGE 2", 210 | } 211 | } 212 | } 213 | } 214 | } 215 | } 216 | }; 217 | 218 | return config; 219 | } 220 | } 221 | } 222 | --------------------------------------------------------------------------------