From b40156bdc858ee5bb3af983003d1a8a381a8cba0 Mon Sep 17 00:00:00 2001 From: Habib Rehman <13956188+habibrehmansg@users.noreply.github.com> Date: Wed, 18 Dec 2024 19:49:16 +0800 Subject: [PATCH 01/10] Add plugin framework [wip] -Added volume plugin -Added weather plugin --- InfoPanel.Contract/IPanelData.cs | 7 - InfoPanel.Contract/PanelData.cs | 22 --- .../InfoPanel.Plugins.Loader.csproj | 13 ++ InfoPanel.Plugins.Loader/PluginExtensions.cs | 17 ++ InfoPanel.Plugins.Loader/PluginLoadContext.cs | 42 +++++ InfoPanel.Plugins.Loader/PluginLoader.cs | 78 ++++++++++ .../InfoPanel.Plugins.Simulator.csproj | 16 ++ InfoPanel.Plugins.Simulator/Program.cs | 50 ++++++ InfoPanel.Plugins/IPlugin.cs | 11 ++ InfoPanel.Plugins/IPluginSensor.cs | 23 +++ .../InfoPanel.Plugins.csproj | 1 + .../InfoPanel.Extras.csproj | 28 ++++ InfoPanel.VolumePlugin/VolumePlugin.cs | 35 +++++ InfoPanel.VolumePlugin/VolumeSensor.cs | 19 +++ InfoPanel.VolumePlugin/WeatherPlugin.cs | 147 ++++++++++++++++++ InfoPanel.VolumePlugin/WeatherSensor.cs | 19 +++ InfoPanel.sln | 68 ++++++-- InfoPanel/App.xaml.cs | 16 +- InfoPanel/Drawing/PanelDraw.cs | 2 - InfoPanel/InfoPanel.csproj | 19 ++- InfoPanel/Services/BeadaPanelTask.cs | 2 - InfoPanel/Utils/SensorMapping.cs | 1 - 22 files changed, 574 insertions(+), 62 deletions(-) delete mode 100644 InfoPanel.Contract/IPanelData.cs delete mode 100644 InfoPanel.Contract/PanelData.cs create mode 100644 InfoPanel.Plugins.Loader/InfoPanel.Plugins.Loader.csproj create mode 100644 InfoPanel.Plugins.Loader/PluginExtensions.cs create mode 100644 InfoPanel.Plugins.Loader/PluginLoadContext.cs create mode 100644 InfoPanel.Plugins.Loader/PluginLoader.cs create mode 100644 InfoPanel.Plugins.Simulator/InfoPanel.Plugins.Simulator.csproj create mode 100644 InfoPanel.Plugins.Simulator/Program.cs create mode 100644 InfoPanel.Plugins/IPlugin.cs create mode 100644 InfoPanel.Plugins/IPluginSensor.cs rename InfoPanel.Contract/InfoPanel.Contract.csproj => InfoPanel.Plugins/InfoPanel.Plugins.csproj (84%) create mode 100644 InfoPanel.VolumePlugin/InfoPanel.Extras.csproj create mode 100644 InfoPanel.VolumePlugin/VolumePlugin.cs create mode 100644 InfoPanel.VolumePlugin/VolumeSensor.cs create mode 100644 InfoPanel.VolumePlugin/WeatherPlugin.cs create mode 100644 InfoPanel.VolumePlugin/WeatherSensor.cs diff --git a/InfoPanel.Contract/IPanelData.cs b/InfoPanel.Contract/IPanelData.cs deleted file mode 100644 index 229a846..0000000 --- a/InfoPanel.Contract/IPanelData.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace InfoPanel.Contract -{ - public interface IPanelData //interface that both the plugin loader and each plugin must implement - { - Task GetData(); - } -} diff --git a/InfoPanel.Contract/PanelData.cs b/InfoPanel.Contract/PanelData.cs deleted file mode 100644 index 20851f0..0000000 --- a/InfoPanel.Contract/PanelData.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace InfoPanel.Contract -{ - //this is the main data object that will be returned from each plugin - public class PanelData - { - public string CollectionName { get; set; } //name of the collection. Added this in case if there are multiple values that need to be sent by the plugin - public IEnumerable EntryList { get; set; } //list of entries - } - - public class Entry - { - public string Name { get; set; } //name of entry (Would be something like "Fan Speed", "CPU Usage", "Water Temp",... - public string Value { get; set; } //The result value. Now while it only accepts strings, you can use something like double.TryParse() to get the value out - } - -} diff --git a/InfoPanel.Plugins.Loader/InfoPanel.Plugins.Loader.csproj b/InfoPanel.Plugins.Loader/InfoPanel.Plugins.Loader.csproj new file mode 100644 index 0000000..e1c6008 --- /dev/null +++ b/InfoPanel.Plugins.Loader/InfoPanel.Plugins.Loader.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/InfoPanel.Plugins.Loader/PluginExtensions.cs b/InfoPanel.Plugins.Loader/PluginExtensions.cs new file mode 100644 index 0000000..dce3d30 --- /dev/null +++ b/InfoPanel.Plugins.Loader/PluginExtensions.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace InfoPanel.Plugins.Loader +{ + public static class PluginExtensions + { + public static string Id(this IPlugin panelData) + { + return panelData.ToString(); + } + } +} diff --git a/InfoPanel.Plugins.Loader/PluginLoadContext.cs b/InfoPanel.Plugins.Loader/PluginLoadContext.cs new file mode 100644 index 0000000..3aab75c --- /dev/null +++ b/InfoPanel.Plugins.Loader/PluginLoadContext.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.Loader; +using System.Text; +using System.Threading.Tasks; + +namespace InfoPanel.Plugins.Loader +{ + public class PluginLoadContext : AssemblyLoadContext + { + private AssemblyDependencyResolver _resolver; + + public PluginLoadContext(string pluginPath) + { + _resolver = new AssemblyDependencyResolver(pluginPath); + } + + protected override Assembly Load(AssemblyName assemblyName) + { + string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName); + if (assemblyPath != null) + { + return LoadFromAssemblyPath(assemblyPath); + } + + return null; + } + + protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) + { + string libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName); + if (libraryPath != null) + { + return LoadUnmanagedDllFromPath(libraryPath); + } + + return IntPtr.Zero; + } + } +} diff --git a/InfoPanel.Plugins.Loader/PluginLoader.cs b/InfoPanel.Plugins.Loader/PluginLoader.cs new file mode 100644 index 0000000..4226497 --- /dev/null +++ b/InfoPanel.Plugins.Loader/PluginLoader.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace InfoPanel.Plugins.Loader +{ + public class PluginLoader + { + public void test(string folder) + { + var plugins= Directory.GetFiles(folder, "InfoPanel.*.dll"); + IEnumerable commands = plugins.SelectMany(pluginPath => + { + Assembly pluginAssembly = LoadPlugin(pluginPath); + return CreateCommands(pluginAssembly); + }).ToList(); + + //foreach (var command in commands) + //{ + // Trace.WriteLine(command); + // var panelDatas = command.GetData(); + // foreach(var panelData in panelDatas) + // { + // Trace.WriteLine(panelData.CollectionName); + // foreach(var item in panelData.EntryList) + // { + // Trace.WriteLine($"{item.Name}: {item.Value} {item.Unit}"); + // } + // } + //} + } + + public IEnumerable InitializePlugin(string pluginPath) + { + Assembly pluginAssembly = LoadPlugin(pluginPath); + return CreateCommands(pluginAssembly); + } + + + static Assembly LoadPlugin(string pluginPath) + { + PluginLoadContext loadContext = new(pluginPath); + return loadContext.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension(pluginPath))); + } + + static IEnumerable CreateCommands(Assembly assembly) + { + int count = 0; + + foreach (Type type in assembly.GetTypes()) + { + if (typeof(IPlugin).IsAssignableFrom(type)) + { + if (Activator.CreateInstance(type) is IPlugin result) + { + count++; + yield return result; + } + } + } + + if (count == 0) + { + string availableTypes = string.Join(",", assembly.GetTypes().Select(t => t.FullName)); + throw new ApplicationException( + $"Can't find any type which implements ICommand in {assembly} from {assembly.Location}.\n" + + $"Available types: {availableTypes}"); + } + } + + + } +} diff --git a/InfoPanel.Plugins.Simulator/InfoPanel.Plugins.Simulator.csproj b/InfoPanel.Plugins.Simulator/InfoPanel.Plugins.Simulator.csproj new file mode 100644 index 0000000..9d7c3f4 --- /dev/null +++ b/InfoPanel.Plugins.Simulator/InfoPanel.Plugins.Simulator.csproj @@ -0,0 +1,16 @@ + + + + Exe + net8.0-windows + enable + enable + + + + + + + + + diff --git a/InfoPanel.Plugins.Simulator/Program.cs b/InfoPanel.Plugins.Simulator/Program.cs new file mode 100644 index 0000000..8fd8447 --- /dev/null +++ b/InfoPanel.Plugins.Simulator/Program.cs @@ -0,0 +1,50 @@ +using InfoPanel.Plugins; +using InfoPanel.Plugins.Loader; + +PluginLoader pluginLoader = new(); + +//\InfoPanel\InfoPanel.Plugins.Simulator\bin\Debug\net8.0-windows +var plugins = pluginLoader.InitializePlugin("..\\..\\..\\..\\InfoPanel.VolumePlugin\\bin\\x64\\Debug\\net8.0-windows\\InfoPanel.Extras.dll"); + +List loadedPlugins = []; + +foreach (var plugin in plugins) +{ + loadedPlugins.Add(plugin); + plugin.Initialize(); +} + +new Task(async () => +{ + while (true) + { + foreach (var plugin in loadedPlugins) + { + await plugin.UpdateAsync(); + } + await Task.Delay(300); + } +}).Start(); + +while (true) +{ + Console.Clear(); + + foreach (var plugin in loadedPlugins) + { + Console.Write("-"); + Console.WriteLine($"{plugin.Name} ({plugin.GetType().FullName})"); + var panelDatas = plugin.GetData(); + + foreach (var panelData in panelDatas) + { + Console.Write("--"); + Console.WriteLine($"{panelData.Name}: {panelData.Value}{panelData.Unit}"); + } + + Console.WriteLine(); + } + + Thread.Sleep(10); +} + diff --git a/InfoPanel.Plugins/IPlugin.cs b/InfoPanel.Plugins/IPlugin.cs new file mode 100644 index 0000000..77a5484 --- /dev/null +++ b/InfoPanel.Plugins/IPlugin.cs @@ -0,0 +1,11 @@ +namespace InfoPanel.Plugins +{ + public interface IPlugin + { + string Name { get; } + void Initialize(); + List GetData(); + Task UpdateAsync(); + void Close(); + } +} diff --git a/InfoPanel.Plugins/IPluginSensor.cs b/InfoPanel.Plugins/IPluginSensor.cs new file mode 100644 index 0000000..ee14992 --- /dev/null +++ b/InfoPanel.Plugins/IPluginSensor.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace InfoPanel.Plugins +{ + public enum IPluginSensorValueType + { + Double, + String + } + + public interface IPluginSensor + { + string Id { get; } + string Name { get; } + IPluginSensorValueType ValueType { get; } + object Value { get; set; } + string? Unit { get; } + } +} diff --git a/InfoPanel.Contract/InfoPanel.Contract.csproj b/InfoPanel.Plugins/InfoPanel.Plugins.csproj similarity index 84% rename from InfoPanel.Contract/InfoPanel.Contract.csproj rename to InfoPanel.Plugins/InfoPanel.Plugins.csproj index fa71b7a..9d1c41f 100644 --- a/InfoPanel.Contract/InfoPanel.Contract.csproj +++ b/InfoPanel.Plugins/InfoPanel.Plugins.csproj @@ -4,6 +4,7 @@ net8.0 enable enable + AnyCPU;x64 diff --git a/InfoPanel.VolumePlugin/InfoPanel.Extras.csproj b/InfoPanel.VolumePlugin/InfoPanel.Extras.csproj new file mode 100644 index 0000000..0c0bbbd --- /dev/null +++ b/InfoPanel.VolumePlugin/InfoPanel.Extras.csproj @@ -0,0 +1,28 @@ + + + + net8.0-windows + enable + enable + true + x64 + + + + + + + + + + + false + runtime + + + + + + + + diff --git a/InfoPanel.VolumePlugin/VolumePlugin.cs b/InfoPanel.VolumePlugin/VolumePlugin.cs new file mode 100644 index 0000000..8ca1e77 --- /dev/null +++ b/InfoPanel.VolumePlugin/VolumePlugin.cs @@ -0,0 +1,35 @@ +using InfoPanel.Plugins; +using NAudio.CoreAudioApi; + +namespace InfoPanel.Extras +{ + public class VolumePlugin : IPlugin + { + private MMDeviceEnumerator? _deviceEnumerator; + private readonly VolumeSensor _volumeSensor = new(); + + string IPlugin.Name => "Volume Plugin"; + + void IPlugin.Initialize() + { + _deviceEnumerator = new MMDeviceEnumerator(); + } + + void IPlugin.Close() + { + _deviceEnumerator?.Dispose(); + } + + List IPlugin.GetData() + { + return [_volumeSensor]; + } + + Task IPlugin.UpdateAsync() + { + using var defaultDevice = _deviceEnumerator?.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia); + _volumeSensor.Value = Math.Round((defaultDevice?.AudioEndpointVolume.MasterVolumeLevelScalar ?? 0) * 100); + return Task.CompletedTask; + } + } +} diff --git a/InfoPanel.VolumePlugin/VolumeSensor.cs b/InfoPanel.VolumePlugin/VolumeSensor.cs new file mode 100644 index 0000000..e812078 --- /dev/null +++ b/InfoPanel.VolumePlugin/VolumeSensor.cs @@ -0,0 +1,19 @@ +using InfoPanel.Plugins; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace InfoPanel.Extras +{ + internal class VolumeSensor() : IPluginSensor + { + public string Id => "volume"; + public string Name => "Master Volume"; + public IPluginSensorValueType ValueType => IPluginSensorValueType.Double; + public object Value { get; set; } = 0; + public string? Unit => "%"; + + } +} diff --git a/InfoPanel.VolumePlugin/WeatherPlugin.cs b/InfoPanel.VolumePlugin/WeatherPlugin.cs new file mode 100644 index 0000000..dafda7d --- /dev/null +++ b/InfoPanel.VolumePlugin/WeatherPlugin.cs @@ -0,0 +1,147 @@ +using InfoPanel.Plugins; +using IniParser; +using IniParser.Model; +using OpenWeatherMap.Standard; +using System.Diagnostics; +using System.Reflection; + +namespace InfoPanel.Extras +{ + public class WeatherPlugin : IPlugin + { + private readonly Stopwatch _stopwatch = new(); + + private Current? _current; + private string? _city; + + private readonly WeatherSensor _name = new("name", "Name", IPluginSensorValueType.String, "-"); + private readonly WeatherSensor _weather = new("weather", "Weather", IPluginSensorValueType.String, "-"); + private readonly WeatherSensor _weatherDesc = new("weather_desc", "Weather Description", IPluginSensorValueType.String, "-"); + private readonly WeatherSensor _weatherIcon = new("weather_icon", "Weather Icon", IPluginSensorValueType.String, "-"); + private readonly WeatherSensor _weatherIconUrl = new("weather_icon_url", "Weather Icon URL", IPluginSensorValueType.String, "-"); + + private readonly WeatherSensor _temp = new("temp", "Temperature", IPluginSensorValueType.Double, 0, "°C"); + private readonly WeatherSensor _maxTemp = new("max_temp", "Maximum Temperature", IPluginSensorValueType.Double, 0, "°C"); + private readonly WeatherSensor _minTemp = new("min_temp", "Minimum Temperature", IPluginSensorValueType.Double, 0, "°C"); + private readonly WeatherSensor _pressure = new("pressure", "Pressure", IPluginSensorValueType.Double, 0, "hPa"); + private readonly WeatherSensor _seaLevel = new("sea_level", "Sea Level", IPluginSensorValueType.Double, 0, "hPa"); + private readonly WeatherSensor _groundLevel = new("ground_level", "Ground Level", IPluginSensorValueType.Double, 0, "hPa"); + private readonly WeatherSensor _feelsLike = new("feels_like", "Feels Like", IPluginSensorValueType.Double, 0, "°C"); + private readonly WeatherSensor _humidity = new("humidity", "Humidity", IPluginSensorValueType.Double, 0, "%"); + + private readonly WeatherSensor _windSpeed = new("wind_speed", "Wind Speed", IPluginSensorValueType.Double, 0, "m/s"); + private readonly WeatherSensor _windDeg = new("wind_deg", "Wind Degree", IPluginSensorValueType.Double, 0, "°"); + private readonly WeatherSensor _windGust = new("wind_gust", "Wind Gust", IPluginSensorValueType.Double, 0, "m/s"); + + private readonly WeatherSensor _clouds = new("clouds", "Clouds", IPluginSensorValueType.Double, 0, "%"); + + private readonly WeatherSensor _rain = new("rain", "Rain", IPluginSensorValueType.Double, 0, "mm/h"); + private readonly WeatherSensor _snow = new("snow", "Snow", IPluginSensorValueType.Double, 0, "mm/h"); + + string IPlugin.Name => "Weather Plugin"; + + void IPlugin.Initialize() + { + Assembly assembly = Assembly.GetExecutingAssembly(); + var configPath = $"{assembly.ManifestModule.FullyQualifiedName}.ini"; + + var parser = new FileIniDataParser(); + IniData config; + if (!File.Exists(configPath)) + { + config = new IniData(); + config["Weather Plugin"]["APIKey"] = ""; + config["Weather Plugin"]["City"] = "Singapore"; + parser.WriteFile(configPath, config); + }else + { + config = parser.ReadFile(configPath); + + var apiKey = config["Weather Plugin"]["APIKey"]; + _city = config["Weather Plugin"]["City"]; + + if(!string.IsNullOrEmpty(apiKey) && !string.IsNullOrEmpty(_city)) + { + _current = new(apiKey, OpenWeatherMap.Standard.Enums.WeatherUnits.Metric); + } + } + } + + void IPlugin.Close() + { + } + + List IPlugin.GetData() + { + return [ + _name, + _weather, + _weatherDesc, + _weatherIcon, + _weatherIconUrl, + _temp, + _maxTemp, + _minTemp, + _pressure, + _seaLevel, + _groundLevel, + _feelsLike, + _humidity, + _windSpeed, + _windDeg, + _windGust, + _clouds, + _rain, + _snow + ]; + } + + async Task IPlugin.UpdateAsync() + { + // Update weather data every minute + if (!_stopwatch.IsRunning || _stopwatch.ElapsedMilliseconds > 60000) + { + Trace.WriteLine("WeatherPlugin: Getting weather data"); + await GetWeather(); + _stopwatch.Restart(); + } + } + + private async Task GetWeather() + { + if(_current == null) + { + return; + } + + var result = await _current.GetWeatherDataByCityNameAsync("Singapore"); + + if (result != null) + { + _name.Value = result.Name; + _weather.Value = result.Weathers[0].Main; + _weatherDesc.Value = result.Weathers[0].Description; + _weatherIcon.Value = result.Weathers[0].Icon; + _weatherIconUrl.Value = $"https://openweathermap.org/img/wn/{result.Weathers[0].Icon}@2x.png"; + + _temp.Value = result.WeatherDayInfo.Temperature; + _maxTemp.Value = result.WeatherDayInfo.MaximumTemperature; + _minTemp.Value = result.WeatherDayInfo.MinimumTemperature; + _pressure.Value = result.WeatherDayInfo.Pressure; + _seaLevel.Value = result.WeatherDayInfo.SeaLevel; + _groundLevel.Value = result.WeatherDayInfo.GroundLevel; + _feelsLike.Value = result.WeatherDayInfo.FeelsLike; + _humidity.Value = result.WeatherDayInfo.Humidity; + + _windSpeed.Value = result.Wind.Speed; + _windDeg.Value = result.Wind.Degree; + _windGust.Value = result.Wind.Gust; + + _clouds.Value = result.Clouds.All; + + _rain.Value = result.Rain.LastHour; + _snow.Value = result.Snow.LastHour; + } + } + } +} diff --git a/InfoPanel.VolumePlugin/WeatherSensor.cs b/InfoPanel.VolumePlugin/WeatherSensor.cs new file mode 100644 index 0000000..cfaf6f6 --- /dev/null +++ b/InfoPanel.VolumePlugin/WeatherSensor.cs @@ -0,0 +1,19 @@ +using InfoPanel.Plugins; +using OpenWeatherMap.Standard.Enums; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace InfoPanel.Extras +{ + internal class WeatherSensor(string id, string name, IPluginSensorValueType valueType, object value, string? unit = null): IPluginSensor + { + public string Id => id; + public string Name => name; + public IPluginSensorValueType ValueType => valueType; + public object Value { get; set; } = value; + public string? Unit => unit; + } +} diff --git a/InfoPanel.sln b/InfoPanel.sln index 4e53ce8..fd001ad 100644 --- a/InfoPanel.sln +++ b/InfoPanel.sln @@ -5,7 +5,13 @@ VisualStudioVersion = 17.6.33815.320 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InfoPanel", "InfoPanel\InfoPanel.csproj", "{59D47BC5-EBEE-48EC-BCF4-09C4BB0A31A8}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InfoPanel.Contract", "InfoPanel.Contract\InfoPanel.Contract.csproj", "{336F46A0-A634-4B65-9082-DA9C00313658}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InfoPanel.Extras", "InfoPanel.VolumePlugin\InfoPanel.Extras.csproj", "{DABE7840-A86C-410D-B420-29C716A0A9FA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InfoPanel.Plugins", "InfoPanel.Plugins\InfoPanel.Plugins.csproj", "{903C16DD-BF96-44B7-B058-17F587194E89}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InfoPanel.Plugins.Simulator", "InfoPanel.Plugins.Simulator\InfoPanel.Plugins.Simulator.csproj", "{2573766B-535D-49FC-867F-5255568A1940}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InfoPanel.Plugins.Loader", "InfoPanel.Plugins.Loader\InfoPanel.Plugins.Loader.csproj", "{299B147B-6B0C-430F-9B91-AC1FB80AAF95}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -29,18 +35,54 @@ Global {59D47BC5-EBEE-48EC-BCF4-09C4BB0A31A8}.Release|x64.Build.0 = Release|x64 {59D47BC5-EBEE-48EC-BCF4-09C4BB0A31A8}.Release|x86.ActiveCfg = Release|Any CPU {59D47BC5-EBEE-48EC-BCF4-09C4BB0A31A8}.Release|x86.Build.0 = Release|Any CPU - {336F46A0-A634-4B65-9082-DA9C00313658}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {336F46A0-A634-4B65-9082-DA9C00313658}.Debug|Any CPU.Build.0 = Debug|Any CPU - {336F46A0-A634-4B65-9082-DA9C00313658}.Debug|x64.ActiveCfg = Debug|Any CPU - {336F46A0-A634-4B65-9082-DA9C00313658}.Debug|x64.Build.0 = Debug|Any CPU - {336F46A0-A634-4B65-9082-DA9C00313658}.Debug|x86.ActiveCfg = Debug|Any CPU - {336F46A0-A634-4B65-9082-DA9C00313658}.Debug|x86.Build.0 = Debug|Any CPU - {336F46A0-A634-4B65-9082-DA9C00313658}.Release|Any CPU.ActiveCfg = Release|Any CPU - {336F46A0-A634-4B65-9082-DA9C00313658}.Release|Any CPU.Build.0 = Release|Any CPU - {336F46A0-A634-4B65-9082-DA9C00313658}.Release|x64.ActiveCfg = Release|Any CPU - {336F46A0-A634-4B65-9082-DA9C00313658}.Release|x64.Build.0 = Release|Any CPU - {336F46A0-A634-4B65-9082-DA9C00313658}.Release|x86.ActiveCfg = Release|Any CPU - {336F46A0-A634-4B65-9082-DA9C00313658}.Release|x86.Build.0 = Release|Any CPU + {DABE7840-A86C-410D-B420-29C716A0A9FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DABE7840-A86C-410D-B420-29C716A0A9FA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DABE7840-A86C-410D-B420-29C716A0A9FA}.Debug|x64.ActiveCfg = Debug|x64 + {DABE7840-A86C-410D-B420-29C716A0A9FA}.Debug|x64.Build.0 = Debug|x64 + {DABE7840-A86C-410D-B420-29C716A0A9FA}.Debug|x86.ActiveCfg = Debug|Any CPU + {DABE7840-A86C-410D-B420-29C716A0A9FA}.Debug|x86.Build.0 = Debug|Any CPU + {DABE7840-A86C-410D-B420-29C716A0A9FA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DABE7840-A86C-410D-B420-29C716A0A9FA}.Release|Any CPU.Build.0 = Release|Any CPU + {DABE7840-A86C-410D-B420-29C716A0A9FA}.Release|x64.ActiveCfg = Release|Any CPU + {DABE7840-A86C-410D-B420-29C716A0A9FA}.Release|x64.Build.0 = Release|Any CPU + {DABE7840-A86C-410D-B420-29C716A0A9FA}.Release|x86.ActiveCfg = Release|Any CPU + {DABE7840-A86C-410D-B420-29C716A0A9FA}.Release|x86.Build.0 = Release|Any CPU + {903C16DD-BF96-44B7-B058-17F587194E89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {903C16DD-BF96-44B7-B058-17F587194E89}.Debug|Any CPU.Build.0 = Debug|Any CPU + {903C16DD-BF96-44B7-B058-17F587194E89}.Debug|x64.ActiveCfg = Debug|x64 + {903C16DD-BF96-44B7-B058-17F587194E89}.Debug|x64.Build.0 = Debug|x64 + {903C16DD-BF96-44B7-B058-17F587194E89}.Debug|x86.ActiveCfg = Debug|Any CPU + {903C16DD-BF96-44B7-B058-17F587194E89}.Debug|x86.Build.0 = Debug|Any CPU + {903C16DD-BF96-44B7-B058-17F587194E89}.Release|Any CPU.ActiveCfg = Release|Any CPU + {903C16DD-BF96-44B7-B058-17F587194E89}.Release|Any CPU.Build.0 = Release|Any CPU + {903C16DD-BF96-44B7-B058-17F587194E89}.Release|x64.ActiveCfg = Release|Any CPU + {903C16DD-BF96-44B7-B058-17F587194E89}.Release|x64.Build.0 = Release|Any CPU + {903C16DD-BF96-44B7-B058-17F587194E89}.Release|x86.ActiveCfg = Release|Any CPU + {903C16DD-BF96-44B7-B058-17F587194E89}.Release|x86.Build.0 = Release|Any CPU + {2573766B-535D-49FC-867F-5255568A1940}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2573766B-535D-49FC-867F-5255568A1940}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2573766B-535D-49FC-867F-5255568A1940}.Debug|x64.ActiveCfg = Debug|Any CPU + {2573766B-535D-49FC-867F-5255568A1940}.Debug|x64.Build.0 = Debug|Any CPU + {2573766B-535D-49FC-867F-5255568A1940}.Debug|x86.ActiveCfg = Debug|Any CPU + {2573766B-535D-49FC-867F-5255568A1940}.Debug|x86.Build.0 = Debug|Any CPU + {2573766B-535D-49FC-867F-5255568A1940}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2573766B-535D-49FC-867F-5255568A1940}.Release|Any CPU.Build.0 = Release|Any CPU + {2573766B-535D-49FC-867F-5255568A1940}.Release|x64.ActiveCfg = Release|Any CPU + {2573766B-535D-49FC-867F-5255568A1940}.Release|x64.Build.0 = Release|Any CPU + {2573766B-535D-49FC-867F-5255568A1940}.Release|x86.ActiveCfg = Release|Any CPU + {2573766B-535D-49FC-867F-5255568A1940}.Release|x86.Build.0 = Release|Any CPU + {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Debug|Any CPU.Build.0 = Debug|Any CPU + {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Debug|x64.ActiveCfg = Debug|Any CPU + {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Debug|x64.Build.0 = Debug|Any CPU + {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Debug|x86.ActiveCfg = Debug|Any CPU + {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Debug|x86.Build.0 = Debug|Any CPU + {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Release|Any CPU.ActiveCfg = Release|Any CPU + {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Release|Any CPU.Build.0 = Release|Any CPU + {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Release|x64.ActiveCfg = Release|Any CPU + {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Release|x64.Build.0 = Release|Any CPU + {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Release|x86.ActiveCfg = Release|Any CPU + {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/InfoPanel/App.xaml.cs b/InfoPanel/App.xaml.cs index 4272355..e4deb65 100644 --- a/InfoPanel/App.xaml.cs +++ b/InfoPanel/App.xaml.cs @@ -1,5 +1,6 @@ using InfoPanel.Models; using InfoPanel.Monitors; +using InfoPanel.Plugins.Loader; using InfoPanel.Services; using InfoPanel.Utils; using InfoPanel.ViewModels; @@ -8,7 +9,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Win32; -using Prise.DependencyInjection; using Sentry; using System; using System.Collections.Generic; @@ -36,9 +36,6 @@ public partial class App : System.Windows.Application //.ConfigureAppConfiguration(c => { c.SetBasePath(Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)); }) .ConfigureServices((context, services) => { - //add plugins - services.AddPrise(); - // App Host services.AddHostedService(); @@ -116,7 +113,7 @@ void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptio SentrySdk.CaptureException(e.Exception); // If you want to avoid the application from crashing: - e.Handled = true; + //e.Handled = true; } protected override void OnExit(ExitEventArgs e) @@ -202,9 +199,7 @@ protected override async void OnStartup(StartupEventArgs e) await StartPanels(); - - //var video = new VideoBackgroundItem("C:\\Users\\Habib\\Desktop\\88inchENG\\video\\rani.mp4"); - //await video.Test(); + testPlugin(); } private void OnSessionEnding(object sender, SessionEndingEventArgs e) @@ -337,9 +332,8 @@ private void DisplayWindow_Closed(object? sender, EventArgs e) public void testPlugin() { - // var pluginPath = Path.Combine(AppContext.BaseDirectory, "plugins"); //set the path where people should put plugins. I chose a folder called plugins in the same directory as the exe - - //var scanResult = await this._pluginLoader.FindPlugin(pluginPath); + var pluginLoader = new PluginLoader(); + pluginLoader.test("plugins"); } } } diff --git a/InfoPanel/Drawing/PanelDraw.cs b/InfoPanel/Drawing/PanelDraw.cs index 0b7d756..3ec77ae 100644 --- a/InfoPanel/Drawing/PanelDraw.cs +++ b/InfoPanel/Drawing/PanelDraw.cs @@ -7,8 +7,6 @@ using System.Drawing.Drawing2D; using System.Linq; using System.Numerics; -using System.Windows.Media.Media3D; -using Windows.Graphics.Imaging; namespace InfoPanel.Drawing { diff --git a/InfoPanel/InfoPanel.csproj b/InfoPanel/InfoPanel.csproj index 8026b42..cba0aea 100644 --- a/InfoPanel/InfoPanel.csproj +++ b/InfoPanel/InfoPanel.csproj @@ -2,18 +2,17 @@ WinExe - net8.0-windows10.0.17763.0 + net8.0-windows enable true true Resources\Images\favicon.ico - x64;AnyCPU + x64 $(AssemblyVersion) $(AssemblyVersion) 1.2.8.0 False en - AnyCPU Resources\app.manifest @@ -160,7 +159,6 @@ - @@ -170,6 +168,11 @@ + + + + + @@ -258,4 +261,12 @@ + + diff --git a/InfoPanel/Services/BeadaPanelTask.cs b/InfoPanel/Services/BeadaPanelTask.cs index d8611c4..17941a3 100644 --- a/InfoPanel/Services/BeadaPanelTask.cs +++ b/InfoPanel/Services/BeadaPanelTask.cs @@ -12,8 +12,6 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using System.Windows.Forms; -using Windows.Storage.Streams; namespace InfoPanel { diff --git a/InfoPanel/Utils/SensorMapping.cs b/InfoPanel/Utils/SensorMapping.cs index f3e8f2f..7c7810b 100644 --- a/InfoPanel/Utils/SensorMapping.cs +++ b/InfoPanel/Utils/SensorMapping.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Windows.Media.Capture.Core; namespace InfoPanel.Utils { From 62f394d154e5691a585d233e83de4da3d00f13e6 Mon Sep 17 00:00:00 2001 From: Habib Rehman <13956188+habibrehmansg@users.noreply.github.com> Date: Thu, 19 Dec 2024 12:55:05 +0800 Subject: [PATCH 02/10] Add public ip plugin (Ipify) --- InfoPanel.Plugins.Loader/PluginLoader.cs | 10 +++--- InfoPanel.Plugins.Simulator/Program.cs | 2 +- InfoPanel.VolumePlugin/IpifyPlugin.cs | 45 ++++++++++++++++++++++++ InfoPanel.VolumePlugin/IpifySensor.cs | 19 ++++++++++ 4 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 InfoPanel.VolumePlugin/IpifyPlugin.cs create mode 100644 InfoPanel.VolumePlugin/IpifySensor.cs diff --git a/InfoPanel.Plugins.Loader/PluginLoader.cs b/InfoPanel.Plugins.Loader/PluginLoader.cs index 4226497..4291ed9 100644 --- a/InfoPanel.Plugins.Loader/PluginLoader.cs +++ b/InfoPanel.Plugins.Loader/PluginLoader.cs @@ -14,11 +14,11 @@ public class PluginLoader public void test(string folder) { var plugins= Directory.GetFiles(folder, "InfoPanel.*.dll"); - IEnumerable commands = plugins.SelectMany(pluginPath => - { - Assembly pluginAssembly = LoadPlugin(pluginPath); - return CreateCommands(pluginAssembly); - }).ToList(); + //IEnumerable commands = plugins.SelectMany(pluginPath => + //{ + // Assembly pluginAssembly = LoadPlugin(pluginPath); + // return CreateCommands(pluginAssembly); + //}).ToList(); //foreach (var command in commands) //{ diff --git a/InfoPanel.Plugins.Simulator/Program.cs b/InfoPanel.Plugins.Simulator/Program.cs index 8fd8447..6c058bf 100644 --- a/InfoPanel.Plugins.Simulator/Program.cs +++ b/InfoPanel.Plugins.Simulator/Program.cs @@ -45,6 +45,6 @@ Console.WriteLine(); } - Thread.Sleep(10); + Thread.Sleep(1000); } diff --git a/InfoPanel.VolumePlugin/IpifyPlugin.cs b/InfoPanel.VolumePlugin/IpifyPlugin.cs new file mode 100644 index 0000000..2a6d6ec --- /dev/null +++ b/InfoPanel.VolumePlugin/IpifyPlugin.cs @@ -0,0 +1,45 @@ +using InfoPanel.Plugins; +using NAudio.CoreAudioApi; +using System.Diagnostics; + +namespace InfoPanel.Extras +{ + public class IpifyPlugin : IPlugin + { + private readonly Stopwatch _stopwatch = new(); + private readonly IpifySensor _ipifySensor = new(); + private readonly HttpClient _httpClient = new(); + + string IPlugin.Name => "Ipify Plugin"; + + void IPlugin.Initialize() + { + } + + void IPlugin.Close() + { + _httpClient.Dispose(); + } + + List IPlugin.GetData() + { + return [_ipifySensor]; + } + + async Task IPlugin.UpdateAsync() + { + if (!_stopwatch.IsRunning || _stopwatch.ElapsedMilliseconds > 60000) + { + Trace.WriteLine("IpifyPlugin: Getting IP"); + await GetIp(); + _stopwatch.Restart(); + } + } + + private async Task GetIp() + { + var ip = await _httpClient.GetStringAsync("https://api.ipify.org"); + _ipifySensor.Value = ip; + } + } +} diff --git a/InfoPanel.VolumePlugin/IpifySensor.cs b/InfoPanel.VolumePlugin/IpifySensor.cs new file mode 100644 index 0000000..e371ddc --- /dev/null +++ b/InfoPanel.VolumePlugin/IpifySensor.cs @@ -0,0 +1,19 @@ +using InfoPanel.Plugins; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace InfoPanel.Extras +{ + internal class IpifySensor() : IPluginSensor + { + public string Id => "ip"; + public string Name => "Public IP"; + public IPluginSensorValueType ValueType => IPluginSensorValueType.String; + public object Value { get; set; } = "-"; + public string? Unit => null; + + } +} From 75729c94774b4cffe0fefbdd19cdf595a28a7ca3 Mon Sep 17 00:00:00 2001 From: Habib Rehman <13956188+habibrehmansg@users.noreply.github.com> Date: Fri, 20 Dec 2024 19:05:22 +0800 Subject: [PATCH 03/10] Enhance plugin support -Add managed plugin update loop -Add Drive info plugin -Add default plugin impl --- InfoPanel.Extras/DriveInfoPlugin.cs | 99 ++++++++++++ .../InfoPanel.Extras.csproj | 3 +- InfoPanel.Extras/IpifyPlugin.cs | 74 +++++++++ InfoPanel.Extras/VolumePlugin.cs | 46 ++++++ InfoPanel.Extras/WeatherPlugin.cs | 143 +++++++++++++++++ .../InfoPanel.Plugins.Loader.csproj | 2 +- InfoPanel.Plugins.Loader/PluginExtensions.cs | 17 -- InfoPanel.Plugins.Loader/PluginLoader.cs | 2 +- InfoPanel.Plugins.Loader/PluginWrapper.cs | 131 ++++++++++++++++ .../InfoPanel.Plugins.Simulator.csproj | 2 - InfoPanel.Plugins.Simulator/Program.cs | 83 +++++++--- InfoPanel.Plugins/BasePlugin.cs | 27 ++++ InfoPanel.Plugins/IPlugin.cs | 7 +- InfoPanel.Plugins/IPluginContainer.cs | 10 ++ InfoPanel.Plugins/IPluginSensor.cs | 9 +- InfoPanel.Plugins/IPluginText.cs | 15 ++ InfoPanel.Plugins/IdUtil.cs | 44 ++++++ InfoPanel.Plugins/InfoPanel.Plugins.csproj | 3 +- InfoPanel.Plugins/PluginContainer.cs | 11 ++ InfoPanel.Plugins/PluginSensor.cs | 30 ++++ InfoPanel.Plugins/PluginText.cs | 25 +++ InfoPanel.VolumePlugin/IpifyPlugin.cs | 45 ------ InfoPanel.VolumePlugin/IpifySensor.cs | 19 --- InfoPanel.VolumePlugin/VolumePlugin.cs | 35 ----- InfoPanel.VolumePlugin/VolumeSensor.cs | 19 --- InfoPanel.VolumePlugin/WeatherPlugin.cs | 147 ------------------ InfoPanel.VolumePlugin/WeatherSensor.cs | 19 --- InfoPanel.sln | 60 +------ InfoPanel/InfoPanel.csproj | 1 - InfoPanel/Services/BackgroundTask.cs | 3 +- 30 files changed, 733 insertions(+), 398 deletions(-) create mode 100644 InfoPanel.Extras/DriveInfoPlugin.cs rename {InfoPanel.VolumePlugin => InfoPanel.Extras}/InfoPanel.Extras.csproj (92%) create mode 100644 InfoPanel.Extras/IpifyPlugin.cs create mode 100644 InfoPanel.Extras/VolumePlugin.cs create mode 100644 InfoPanel.Extras/WeatherPlugin.cs delete mode 100644 InfoPanel.Plugins.Loader/PluginExtensions.cs create mode 100644 InfoPanel.Plugins.Loader/PluginWrapper.cs create mode 100644 InfoPanel.Plugins/BasePlugin.cs create mode 100644 InfoPanel.Plugins/IPluginContainer.cs create mode 100644 InfoPanel.Plugins/IPluginText.cs create mode 100644 InfoPanel.Plugins/IdUtil.cs create mode 100644 InfoPanel.Plugins/PluginContainer.cs create mode 100644 InfoPanel.Plugins/PluginSensor.cs create mode 100644 InfoPanel.Plugins/PluginText.cs delete mode 100644 InfoPanel.VolumePlugin/IpifyPlugin.cs delete mode 100644 InfoPanel.VolumePlugin/IpifySensor.cs delete mode 100644 InfoPanel.VolumePlugin/VolumePlugin.cs delete mode 100644 InfoPanel.VolumePlugin/VolumeSensor.cs delete mode 100644 InfoPanel.VolumePlugin/WeatherPlugin.cs delete mode 100644 InfoPanel.VolumePlugin/WeatherSensor.cs diff --git a/InfoPanel.Extras/DriveInfoPlugin.cs b/InfoPanel.Extras/DriveInfoPlugin.cs new file mode 100644 index 0000000..6f5f4e8 --- /dev/null +++ b/InfoPanel.Extras/DriveInfoPlugin.cs @@ -0,0 +1,99 @@ +using InfoPanel.Plugins; + +namespace InfoPanel.Extras +{ + public class DriveInfoPlugin : BasePlugin + { + public override TimeSpan UpdateInterval => TimeSpan.FromSeconds(1); + + private readonly List _containers = []; + + public DriveInfoPlugin() : base("Drive Info Plugin") + { + } + + public override void Close() + { + throw new NotImplementedException(); + } + + public override void Initialize() + { + foreach (DriveInfo drive in DriveInfo.GetDrives()) + { + if (drive.IsReady) + { + PluginContainer container = new(drive.Name); + container.Text.Add(new PluginText("name", "Name", drive.Name)); + container.Text.Add(new PluginText("type", "Type", drive.DriveType.ToString())); + container.Text.Add(new PluginText("volume_label", "Volume Label", drive.VolumeLabel)); + container.Text.Add(new PluginText("format", "Format", drive.DriveFormat)); + container.Sensors.Add(new PluginSensor("total_size", "Total Size", drive.TotalSize / 1024 / 1024, "MB")); + container.Sensors.Add(new PluginSensor("free_space", "Free Space", drive.TotalFreeSpace / 1024 / 1024, "MB")); + container.Sensors.Add(new PluginSensor("available_space", "Available Space", drive.AvailableFreeSpace / 1024 / 1024, "MB")); + container.Sensors.Add(new PluginSensor("used_space", "Used Space", (drive.TotalSize - drive.TotalFreeSpace) / 1024 / 1024, "MB")); + + _containers.Add(container); + } + } + } + + public override void Load(List containers) + { + containers.AddRange(_containers); + } + + public override void Update() + { + throw new NotImplementedException(); + } + + public override Task UpdateAsync(CancellationToken cancellationToken) + { + foreach (var container in _containers) + { + DriveInfo drive = new(container.Name); + + if (drive.IsReady) + { + foreach (var text in container.Text) + { + switch (text.Id) + { + case "type": + text.Value = drive.DriveType.ToString(); + break; + case "volume_label": + text.Value = drive.VolumeLabel; + break; + case "format": + text.Value = drive.DriveFormat; + break; + } + } + + foreach (var sensor in container.Sensors) + { + switch (sensor.Id) + { + case "total_size": + sensor.Value = drive.TotalSize / 1024 / 1024; + break; + case "free_space": + sensor.Value = drive.TotalFreeSpace / 1024 / 1024; + break; + case "available_space": + sensor.Value = drive.AvailableFreeSpace / 1024 / 1024; + break; + case "used_space": + sensor.Value = (drive.TotalSize - drive.TotalFreeSpace) / 1024 / 1024; + break; + } + } + } + } + + return Task.CompletedTask; + } + } +} diff --git a/InfoPanel.VolumePlugin/InfoPanel.Extras.csproj b/InfoPanel.Extras/InfoPanel.Extras.csproj similarity index 92% rename from InfoPanel.VolumePlugin/InfoPanel.Extras.csproj rename to InfoPanel.Extras/InfoPanel.Extras.csproj index 0c0bbbd..3916edc 100644 --- a/InfoPanel.VolumePlugin/InfoPanel.Extras.csproj +++ b/InfoPanel.Extras/InfoPanel.Extras.csproj @@ -5,11 +5,10 @@ enable enable true - x64 - + diff --git a/InfoPanel.Extras/IpifyPlugin.cs b/InfoPanel.Extras/IpifyPlugin.cs new file mode 100644 index 0000000..533ecef --- /dev/null +++ b/InfoPanel.Extras/IpifyPlugin.cs @@ -0,0 +1,74 @@ +using InfoPanel.Plugins; +using System.Diagnostics; + +namespace InfoPanel.Extras +{ + public class IpifyPlugin : BasePlugin + { + private readonly Stopwatch _stopwatch = new(); + private readonly PluginText _ipv4Sensor = new("IPv4", "-"); + private readonly PluginText _ipv6Sensor = new("IPv6", "-"); + private readonly HttpClient _httpClient = new(); + + public override TimeSpan UpdateInterval => TimeSpan.FromMinutes(5); + + public IpifyPlugin() : base("Ipify Plugin") + { + } + + public override void Initialize() + { + } + + public override void Load(List containers) + { + var container = new PluginContainer("Public IP"); + container.Text.Add(_ipv4Sensor); + container.Text.Add(_ipv6Sensor); + containers.Add(container); + } + + public override void Close() + { + _httpClient.Dispose(); + } + + public override void Update() + { + throw new NotImplementedException(); + } + + public override async Task UpdateAsync(CancellationToken cancellationToken) + { + if (!_stopwatch.IsRunning || _stopwatch.ElapsedMilliseconds > 60000) + { + Trace.WriteLine("IpifyPlugin: Getting IP"); + await GetIp(cancellationToken); + _stopwatch.Restart(); + } + } + + private async Task GetIp(CancellationToken cancellationToken) + { + try + { + var ipv4 = await _httpClient.GetStringAsync("https://api.ipify.org", cancellationToken); + _ipv4Sensor.Value = ipv4; + } + catch + { + Trace.WriteLine("IpifyPlugin: Failed to get IPv6"); + } + + try + { + var ipv6 = await _httpClient.GetStringAsync("https://api6.ipify.org", cancellationToken); + _ipv6Sensor.Value = ipv6; + } + catch + { + Trace.WriteLine("IpifyPlugin: Failed to get IPv6"); + } + } + } +} diff --git a/InfoPanel.Extras/VolumePlugin.cs b/InfoPanel.Extras/VolumePlugin.cs new file mode 100644 index 0000000..8f0d82e --- /dev/null +++ b/InfoPanel.Extras/VolumePlugin.cs @@ -0,0 +1,46 @@ +using InfoPanel.Plugins; +using NAudio.CoreAudioApi; + +namespace InfoPanel.Extras +{ + public class VolumePlugin : BasePlugin + { + private MMDeviceEnumerator? _deviceEnumerator; + private readonly PluginSensor _volumeSensor = new("Master Volume", 0, "%"); + + public VolumePlugin() : base("Volume Plugin") + { + } + + public override TimeSpan UpdateInterval => TimeSpan.FromMilliseconds(50); + + public override void Initialize() + { + _deviceEnumerator = new MMDeviceEnumerator(); + } + + public override void Close() + { + _deviceEnumerator?.Dispose(); + } + + public override void Load(List containers) + { + var container = new PluginContainer("Default"); + container.Sensors.Add(_volumeSensor); + containers.Add(container); + } + + public override Task UpdateAsync(CancellationToken cancellationToken) + { + Update(); + return Task.CompletedTask; + } + + public override void Update() + { + using var defaultDevice = _deviceEnumerator?.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia); + _volumeSensor.Value = (float)Math.Round((defaultDevice?.AudioEndpointVolume.MasterVolumeLevelScalar ?? 0) * 100); + } + } +} diff --git a/InfoPanel.Extras/WeatherPlugin.cs b/InfoPanel.Extras/WeatherPlugin.cs new file mode 100644 index 0000000..a839fcd --- /dev/null +++ b/InfoPanel.Extras/WeatherPlugin.cs @@ -0,0 +1,143 @@ +using InfoPanel.Plugins; +using IniParser; +using IniParser.Model; +using OpenWeatherMap.Standard; +using System.Diagnostics; +using System.Reflection; + +namespace InfoPanel.Extras +{ + public class WeatherPlugin : BasePlugin + { + private readonly Stopwatch _stopwatch = new(); + + private Current? _current; + private string? _city; + + private readonly PluginText _name = new("name", "Name", "-"); + private readonly PluginText _weather = new("weather", "Weather", "-"); + private readonly PluginText _weatherDesc = new("weather_desc", "Weather Description", "-"); + private readonly PluginText _weatherIcon = new("weather_icon", "Weather Icon", "-"); + private readonly PluginText _weatherIconUrl = new("weather_icon_url", "Weather Icon URL", "-"); + + private readonly PluginSensor _temp = new("temp", "Temperature", 0, "°C"); + private readonly PluginSensor _maxTemp = new("max_temp", "Maximum Temperature", 0, "°C"); + private readonly PluginSensor _minTemp = new("min_temp", "Minimum Temperature", 0, "°C"); + private readonly PluginSensor _pressure = new("pressure", "Pressure", 0, "hPa"); + private readonly PluginSensor _seaLevel = new("sea_level", "Sea Level", 0, "hPa"); + private readonly PluginSensor _groundLevel = new("ground_level", "Ground Level", 0, "hPa"); + private readonly PluginSensor _feelsLike = new("feels_like", "Feels Like", 0, "°C"); + private readonly PluginSensor _humidity = new("humidity", "Humidity", 0, "%"); + + private readonly PluginSensor _windSpeed = new("wind_speed", "Wind Speed", 0, "m/s"); + private readonly PluginSensor _windDeg = new("wind_deg", "Wind Degree", 0, "°"); + private readonly PluginSensor _windGust = new("wind_gust", "Wind Gust", 0, "m/s"); + + private readonly PluginSensor _clouds = new("clouds", "Clouds", 0, "%"); + + private readonly PluginSensor _rain = new("rain", "Rain", 0, "mm/h"); + private readonly PluginSensor _snow = new("snow", "Snow", 0, "mm/h"); + + public WeatherPlugin() : base("Weather Plugin") + { + } + + public override TimeSpan UpdateInterval => TimeSpan.FromMinutes(1); + + public override void Initialize() + { + Assembly assembly = Assembly.GetExecutingAssembly(); + var configPath = $"{assembly.ManifestModule.FullyQualifiedName}.ini"; + + var parser = new FileIniDataParser(); + IniData config; + if (!File.Exists(configPath)) + { + config = new IniData(); + config["Weather Plugin"]["APIKey"] = ""; + config["Weather Plugin"]["City"] = "Singapore"; + parser.WriteFile(configPath, config); + } + else + { + config = parser.ReadFile(configPath); + + var apiKey = config["Weather Plugin"]["APIKey"]; + _city = config["Weather Plugin"]["City"]; + + if (!string.IsNullOrEmpty(apiKey) && !string.IsNullOrEmpty(_city)) + { + _current = new(apiKey, OpenWeatherMap.Standard.Enums.WeatherUnits.Metric); + } + } + } + + public override void Close() + { + } + + public override void Load(List containers) + { + if (_city != null) + { + var container = new PluginContainer(_city); + container.Text.AddRange([_name, _weather, _weatherDesc, _weatherIcon, _weatherIconUrl]); + container.Sensors.AddRange([_temp, _maxTemp, _minTemp, _pressure, _seaLevel, _groundLevel, _feelsLike, _humidity, _windSpeed, _windDeg, _windGust, _clouds, _rain, _snow]); + containers.Add(container); + } + } + + public override void Update() + { + throw new NotImplementedException(); + } + + public override async Task UpdateAsync(CancellationToken cancellationToken) + { + // Update weather data every minute + if (!_stopwatch.IsRunning || _stopwatch.ElapsedMilliseconds > 60000) + { + Trace.WriteLine("WeatherPlugin: Getting weather data"); + await GetWeather(); + _stopwatch.Restart(); + } + } + + private async Task GetWeather() + { + if (_current == null) + { + return; + } + + var result = await _current.GetWeatherDataByCityNameAsync("Singapore"); + + if (result != null) + { + _name.Value = result.Name; + _weather.Value = result.Weathers[0].Main; + _weatherDesc.Value = result.Weathers[0].Description; + _weatherIcon.Value = result.Weathers[0].Icon; + _weatherIconUrl.Value = $"https://openweathermap.org/img/wn/{result.Weathers[0].Icon}@2x.png"; + + _temp.Value = result.WeatherDayInfo.Temperature; + _maxTemp.Value = result.WeatherDayInfo.MaximumTemperature; + _minTemp.Value = result.WeatherDayInfo.MinimumTemperature; + _pressure.Value = result.WeatherDayInfo.Pressure; + _seaLevel.Value = result.WeatherDayInfo.SeaLevel; + _groundLevel.Value = result.WeatherDayInfo.GroundLevel; + _feelsLike.Value = result.WeatherDayInfo.FeelsLike; + _humidity.Value = result.WeatherDayInfo.Humidity; + + _windSpeed.Value = result.Wind.Speed; + _windDeg.Value = result.Wind.Degree; + _windGust.Value = result.Wind.Gust; + + _clouds.Value = result.Clouds.All; + + _rain.Value = result.Rain.LastHour; + _snow.Value = result.Snow.LastHour; + } + } + } +} diff --git a/InfoPanel.Plugins.Loader/InfoPanel.Plugins.Loader.csproj b/InfoPanel.Plugins.Loader/InfoPanel.Plugins.Loader.csproj index e1c6008..a53ded9 100644 --- a/InfoPanel.Plugins.Loader/InfoPanel.Plugins.Loader.csproj +++ b/InfoPanel.Plugins.Loader/InfoPanel.Plugins.Loader.csproj @@ -1,7 +1,7 @@  - net8.0 + net8.0-windows enable enable diff --git a/InfoPanel.Plugins.Loader/PluginExtensions.cs b/InfoPanel.Plugins.Loader/PluginExtensions.cs deleted file mode 100644 index dce3d30..0000000 --- a/InfoPanel.Plugins.Loader/PluginExtensions.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading.Tasks; - -namespace InfoPanel.Plugins.Loader -{ - public static class PluginExtensions - { - public static string Id(this IPlugin panelData) - { - return panelData.ToString(); - } - } -} diff --git a/InfoPanel.Plugins.Loader/PluginLoader.cs b/InfoPanel.Plugins.Loader/PluginLoader.cs index 4291ed9..c8b447e 100644 --- a/InfoPanel.Plugins.Loader/PluginLoader.cs +++ b/InfoPanel.Plugins.Loader/PluginLoader.cs @@ -13,7 +13,7 @@ public class PluginLoader { public void test(string folder) { - var plugins= Directory.GetFiles(folder, "InfoPanel.*.dll"); + //var plugins= Directory.GetFiles(folder, "InfoPanel.*.dll"); //IEnumerable commands = plugins.SelectMany(pluginPath => //{ // Assembly pluginAssembly = LoadPlugin(pluginPath); diff --git a/InfoPanel.Plugins.Loader/PluginWrapper.cs b/InfoPanel.Plugins.Loader/PluginWrapper.cs new file mode 100644 index 0000000..983acaf --- /dev/null +++ b/InfoPanel.Plugins.Loader/PluginWrapper.cs @@ -0,0 +1,131 @@ +using System.Diagnostics; + +namespace InfoPanel.Plugins.Loader +{ + public class PluginWrapper(IPlugin plugin) + { + public IPlugin Plugin { get; } = plugin; + public List PluginContainers { get; } = []; + + public string Id => Plugin.Id; + public string Name => Plugin.Name; + + public TimeSpan UpdateInterval => Plugin.UpdateInterval; + + private readonly Stopwatch _stopwatch = new(); + private long _updateTimeMilliseconds = 0; + public long UpdateTimeMilliseconds => _updateTimeMilliseconds; + + private static readonly SemaphoreSlim _startStopSemaphore = new(1, 1); + + private CancellationTokenSource? _cts; + private Task? _task; + + public bool IsRunning => _task is not null && !_task.IsCompleted && _cts is not null && !_cts.IsCancellationRequested; + + public void Update() + { + // If the plugin is running or the interval is not set to <0, we don't want to update it manually + if (IsRunning || Plugin.UpdateInterval.TotalMilliseconds > 0) return; + + try + { + _stopwatch.Restart(); + Plugin.Update(); + _updateTimeMilliseconds = _stopwatch.ElapsedMilliseconds; + } + catch (Exception ex) + { + + } + } + + public async Task Initialize() + { + await _startStopSemaphore.WaitAsync(); + try + { + Plugin.Initialize(); + Plugin.Load(PluginContainers); + + // If the plugin is running or the interval is not set to >0, we don't want to start it + if (IsRunning || Plugin.UpdateInterval.TotalMilliseconds <= 0) return; + _cts = new CancellationTokenSource(); + _task = Task.Run(() => DoWorkAsync(_cts.Token), _cts.Token); + } + finally + { + _startStopSemaphore.Release(); + } + } + + public async Task StopAsync() + { + await _startStopSemaphore.WaitAsync(); + try + { + if (_cts is null || _task is null) return; + + _cts.Cancel(); + + try + { + await _task; + } + catch (OperationCanceledException) + { + // Task was canceled + } + catch (Exception ex) + { + Console.WriteLine($"Exception during task stop: {ex.Message}"); + } + finally + { + DisposeResources(); + } + } + finally + { + _startStopSemaphore.Release(); + } + } + + private async Task DoWorkAsync(CancellationToken cancellationToken) + { + await Task.Delay(300, cancellationToken); + + Trace.WriteLine($"Plugin {Name} task started"); + try + { + while (!cancellationToken.IsCancellationRequested) + { + try + { + _stopwatch.Restart(); + await Plugin.UpdateAsync(cancellationToken); + _updateTimeMilliseconds = _stopwatch.ElapsedMilliseconds; + } + catch (Exception ex) + { + Trace.WriteLine($"Exception during task execution: {ex.Message}"); + } + + await Task.Delay(Plugin.UpdateInterval, cancellationToken); + } + } + catch (TaskCanceledException) + { + // Task was canceled + } + } + + private void DisposeResources() + { + _cts?.Dispose(); + _task?.Dispose(); + _cts = null; + _task = null; + } + } +} diff --git a/InfoPanel.Plugins.Simulator/InfoPanel.Plugins.Simulator.csproj b/InfoPanel.Plugins.Simulator/InfoPanel.Plugins.Simulator.csproj index 9d7c3f4..5c1f97a 100644 --- a/InfoPanel.Plugins.Simulator/InfoPanel.Plugins.Simulator.csproj +++ b/InfoPanel.Plugins.Simulator/InfoPanel.Plugins.Simulator.csproj @@ -9,8 +9,6 @@ - - diff --git a/InfoPanel.Plugins.Simulator/Program.cs b/InfoPanel.Plugins.Simulator/Program.cs index 6c058bf..ac75bcd 100644 --- a/InfoPanel.Plugins.Simulator/Program.cs +++ b/InfoPanel.Plugins.Simulator/Program.cs @@ -1,50 +1,83 @@ -using InfoPanel.Plugins; -using InfoPanel.Plugins.Loader; +using InfoPanel.Plugins.Loader; +using System.Text; PluginLoader pluginLoader = new(); //\InfoPanel\InfoPanel.Plugins.Simulator\bin\Debug\net8.0-windows -var plugins = pluginLoader.InitializePlugin("..\\..\\..\\..\\InfoPanel.VolumePlugin\\bin\\x64\\Debug\\net8.0-windows\\InfoPanel.Extras.dll"); -List loadedPlugins = []; +var currentDirectory = Directory.GetCurrentDirectory(); +var pluginPath = Path.Combine(currentDirectory, "..\\..\\..\\..\\InfoPanel.Extras\\bin\\Debug\\net8.0-windows", "InfoPanel.Extras.dll"); -foreach (var plugin in plugins) -{ - loadedPlugins.Add(plugin); - plugin.Initialize(); -} +var plugins = pluginLoader.InitializePlugin(pluginPath); -new Task(async () => +Dictionary loadedPlugins = []; + +foreach (var plugin in plugins) { - while (true) + PluginWrapper pluginWrapper = new(plugin); + if (loadedPlugins.TryAdd(pluginWrapper.Name, pluginWrapper)) { - foreach (var plugin in loadedPlugins) + try { - await plugin.UpdateAsync(); + await pluginWrapper.Initialize(); + Console.WriteLine($"Plugin {pluginWrapper.Name} loaded successfully"); } - await Task.Delay(300); + catch (Exception ex) + { + Console.WriteLine($"Plugin {pluginWrapper.Name} failed to load: {ex.Message}"); + } + } + else + { + Console.WriteLine($"Plugin {pluginWrapper.Name} already loaded or duplicate plugin/name"); } -}).Start(); + + //break; +} + +Thread.Sleep(1000); +Console.Clear(); + +StringBuilder buffer = new(); +string lastOutput = string.Empty; while (true) { - Console.Clear(); + buffer.Clear(); - foreach (var plugin in loadedPlugins) + foreach (var wrapper in loadedPlugins.Values) { - Console.Write("-"); - Console.WriteLine($"{plugin.Name} ({plugin.GetType().FullName})"); - var panelDatas = plugin.GetData(); + wrapper.Update(); + + buffer.AppendLine($"-{wrapper.Name} ({wrapper.Plugin.GetType().FullName}) [UpdateInterval={wrapper.UpdateInterval.TotalMilliseconds}ms, UpdateTime={wrapper.UpdateTimeMilliseconds}ms]"); - foreach (var panelData in panelDatas) + foreach (var container in wrapper.PluginContainers) { - Console.Write("--"); - Console.WriteLine($"{panelData.Name}: {panelData.Value}{panelData.Unit}"); + buffer.AppendLine($"--{container.Name}"); + foreach (var text in container.Text) + { + var id = $"/{wrapper.Id}/{container.Id}/{text.Id}"; + buffer.AppendLine($"---{text.Name}: {text.Value}"); + } + + foreach (var text in container.Sensors) + { + buffer.AppendLine($"---{text.Name}: {text.Value}{text.Unit}"); + } } - Console.WriteLine(); + buffer.AppendLine(); + } + + // Only update the console if the output has changed with double buffering to reduce flicker + var output = buffer.ToString(); + if (output != lastOutput) + { + lastOutput = output; + Console.Clear(); + Console.WriteLine(output); } - Thread.Sleep(1000); + Thread.Sleep(30); } diff --git a/InfoPanel.Plugins/BasePlugin.cs b/InfoPanel.Plugins/BasePlugin.cs new file mode 100644 index 0000000..95ea0e2 --- /dev/null +++ b/InfoPanel.Plugins/BasePlugin.cs @@ -0,0 +1,27 @@ +namespace InfoPanel.Plugins +{ + public abstract class BasePlugin : IPlugin + { + public string Id { get; } + public string Name { get; } + public abstract TimeSpan UpdateInterval { get; } + + public BasePlugin(string name) + { + Id = IdUtil.Encode(name); + Name = name; + } + + public BasePlugin(string id, string name) + { + Id = id; + Name = name; + } + + public abstract void Close(); + public abstract void Initialize(); + public abstract void Load(List containers); + public abstract void Update(); + public abstract Task UpdateAsync(CancellationToken cancellationToken); + } +} diff --git a/InfoPanel.Plugins/IPlugin.cs b/InfoPanel.Plugins/IPlugin.cs index 77a5484..109e870 100644 --- a/InfoPanel.Plugins/IPlugin.cs +++ b/InfoPanel.Plugins/IPlugin.cs @@ -2,10 +2,13 @@ { public interface IPlugin { + string Id { get; } string Name { get; } + TimeSpan UpdateInterval { get; } void Initialize(); - List GetData(); - Task UpdateAsync(); + void Load(List containers); + void Update(); + Task UpdateAsync(CancellationToken cancellationToken); void Close(); } } diff --git a/InfoPanel.Plugins/IPluginContainer.cs b/InfoPanel.Plugins/IPluginContainer.cs new file mode 100644 index 0000000..27709e7 --- /dev/null +++ b/InfoPanel.Plugins/IPluginContainer.cs @@ -0,0 +1,10 @@ +namespace InfoPanel.Plugins +{ + public interface IPluginContainer + { + string Id { get; } + string Name { get; } + List Text { get; } + List Sensors { get; } + } +} diff --git a/InfoPanel.Plugins/IPluginSensor.cs b/InfoPanel.Plugins/IPluginSensor.cs index ee14992..56ce384 100644 --- a/InfoPanel.Plugins/IPluginSensor.cs +++ b/InfoPanel.Plugins/IPluginSensor.cs @@ -6,18 +6,11 @@ namespace InfoPanel.Plugins { - public enum IPluginSensorValueType - { - Double, - String - } - public interface IPluginSensor { string Id { get; } string Name { get; } - IPluginSensorValueType ValueType { get; } - object Value { get; set; } + float Value { get; set; } string? Unit { get; } } } diff --git a/InfoPanel.Plugins/IPluginText.cs b/InfoPanel.Plugins/IPluginText.cs new file mode 100644 index 0000000..797b1e9 --- /dev/null +++ b/InfoPanel.Plugins/IPluginText.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace InfoPanel.Plugins +{ + public interface IPluginText + { + string Id { get; } + string Name { get; } + string Value { get; set; } + } +} diff --git a/InfoPanel.Plugins/IdUtil.cs b/InfoPanel.Plugins/IdUtil.cs new file mode 100644 index 0000000..a74857f --- /dev/null +++ b/InfoPanel.Plugins/IdUtil.cs @@ -0,0 +1,44 @@ +using System.Globalization; +using System.Text; +using System.Text.RegularExpressions; + +namespace InfoPanel.Plugins +{ + internal partial class IdUtil + { + [GeneratedRegex(@"[^a-z0-9-]", RegexOptions.IgnoreCase)] + private static partial Regex AlphaNumericRegex(); + + [GeneratedRegex(@"\s+")] + private static partial Regex WhitespaceRegex(); + + public static string Encode(string input) + { + // Normalize the input string to decompose combined characters into base characters + diacritics + string normalized = input.Normalize(NormalizationForm.FormD); + + // Use StringBuilder to filter out non-spacing marks (diacritics) and other non-alphanumeric characters + var sb = new StringBuilder(); + foreach (var c in normalized) + { + UnicodeCategory unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c); + if (unicodeCategory != UnicodeCategory.NonSpacingMark && (char.IsLetterOrDigit(c) || char.IsWhiteSpace(c))) + { + sb.Append(c); + } + } + + // Convert to lowercase + string cleaned = sb.ToString().Normalize(NormalizationForm.FormC).ToLowerInvariant(); + + // Replace spaces with hyphens + string dashed = WhitespaceRegex().Replace(cleaned, "-").Trim('-'); + + // Optionally, you can further refine the output by ensuring it matches URL-safe characters only + // This regex will ensure only alphanumeric and dash remain + string slug = AlphaNumericRegex().Replace(dashed, ""); + + return slug; + } + } +} diff --git a/InfoPanel.Plugins/InfoPanel.Plugins.csproj b/InfoPanel.Plugins/InfoPanel.Plugins.csproj index 9d1c41f..d62d4ce 100644 --- a/InfoPanel.Plugins/InfoPanel.Plugins.csproj +++ b/InfoPanel.Plugins/InfoPanel.Plugins.csproj @@ -1,10 +1,9 @@  - net8.0 + net8.0-windows enable enable - AnyCPU;x64 diff --git a/InfoPanel.Plugins/PluginContainer.cs b/InfoPanel.Plugins/PluginContainer.cs new file mode 100644 index 0000000..4c672c3 --- /dev/null +++ b/InfoPanel.Plugins/PluginContainer.cs @@ -0,0 +1,11 @@ +namespace InfoPanel.Plugins +{ + public class PluginContainer(string name) : IPluginContainer + { + public string Id { get; } = IdUtil.Encode(name); + public string Name { get; } = name; + public List Text { get; } = []; + + public List Sensors { get; } = []; + } +} diff --git a/InfoPanel.Plugins/PluginSensor.cs b/InfoPanel.Plugins/PluginSensor.cs new file mode 100644 index 0000000..b5746ef --- /dev/null +++ b/InfoPanel.Plugins/PluginSensor.cs @@ -0,0 +1,30 @@ +namespace InfoPanel.Plugins +{ + public class PluginSensor : IPluginSensor + { + public string Id { get; } + + public string Name { get; } + + public float Value { get; set; } + + public string? Unit { get; } + + public PluginSensor(string id, string name, float value, string? unit = null) + { + Id = id; + Name = name; + Value = value; + Unit = unit; + } + + public PluginSensor(string name, float value, string? unit = null) + { + Id = IdUtil.Encode(name); + Name = name; + Value = value; + Unit = unit; + + } + } +} diff --git a/InfoPanel.Plugins/PluginText.cs b/InfoPanel.Plugins/PluginText.cs new file mode 100644 index 0000000..47e0c1b --- /dev/null +++ b/InfoPanel.Plugins/PluginText.cs @@ -0,0 +1,25 @@ +namespace InfoPanel.Plugins +{ + public class PluginText : IPluginText + { + public string Id { get; } + + public string Name { get; } + + public string Value { get; set; } + + public PluginText(string id, string name, string value) + { + Id = id; + Name = name; + Value = value; + } + + public PluginText(string name, string value) + { + Id = IdUtil.Encode(name); + Name = name; + Value = value; + } + } +} diff --git a/InfoPanel.VolumePlugin/IpifyPlugin.cs b/InfoPanel.VolumePlugin/IpifyPlugin.cs deleted file mode 100644 index 2a6d6ec..0000000 --- a/InfoPanel.VolumePlugin/IpifyPlugin.cs +++ /dev/null @@ -1,45 +0,0 @@ -using InfoPanel.Plugins; -using NAudio.CoreAudioApi; -using System.Diagnostics; - -namespace InfoPanel.Extras -{ - public class IpifyPlugin : IPlugin - { - private readonly Stopwatch _stopwatch = new(); - private readonly IpifySensor _ipifySensor = new(); - private readonly HttpClient _httpClient = new(); - - string IPlugin.Name => "Ipify Plugin"; - - void IPlugin.Initialize() - { - } - - void IPlugin.Close() - { - _httpClient.Dispose(); - } - - List IPlugin.GetData() - { - return [_ipifySensor]; - } - - async Task IPlugin.UpdateAsync() - { - if (!_stopwatch.IsRunning || _stopwatch.ElapsedMilliseconds > 60000) - { - Trace.WriteLine("IpifyPlugin: Getting IP"); - await GetIp(); - _stopwatch.Restart(); - } - } - - private async Task GetIp() - { - var ip = await _httpClient.GetStringAsync("https://api.ipify.org"); - _ipifySensor.Value = ip; - } - } -} diff --git a/InfoPanel.VolumePlugin/IpifySensor.cs b/InfoPanel.VolumePlugin/IpifySensor.cs deleted file mode 100644 index e371ddc..0000000 --- a/InfoPanel.VolumePlugin/IpifySensor.cs +++ /dev/null @@ -1,19 +0,0 @@ -using InfoPanel.Plugins; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace InfoPanel.Extras -{ - internal class IpifySensor() : IPluginSensor - { - public string Id => "ip"; - public string Name => "Public IP"; - public IPluginSensorValueType ValueType => IPluginSensorValueType.String; - public object Value { get; set; } = "-"; - public string? Unit => null; - - } -} diff --git a/InfoPanel.VolumePlugin/VolumePlugin.cs b/InfoPanel.VolumePlugin/VolumePlugin.cs deleted file mode 100644 index 8ca1e77..0000000 --- a/InfoPanel.VolumePlugin/VolumePlugin.cs +++ /dev/null @@ -1,35 +0,0 @@ -using InfoPanel.Plugins; -using NAudio.CoreAudioApi; - -namespace InfoPanel.Extras -{ - public class VolumePlugin : IPlugin - { - private MMDeviceEnumerator? _deviceEnumerator; - private readonly VolumeSensor _volumeSensor = new(); - - string IPlugin.Name => "Volume Plugin"; - - void IPlugin.Initialize() - { - _deviceEnumerator = new MMDeviceEnumerator(); - } - - void IPlugin.Close() - { - _deviceEnumerator?.Dispose(); - } - - List IPlugin.GetData() - { - return [_volumeSensor]; - } - - Task IPlugin.UpdateAsync() - { - using var defaultDevice = _deviceEnumerator?.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia); - _volumeSensor.Value = Math.Round((defaultDevice?.AudioEndpointVolume.MasterVolumeLevelScalar ?? 0) * 100); - return Task.CompletedTask; - } - } -} diff --git a/InfoPanel.VolumePlugin/VolumeSensor.cs b/InfoPanel.VolumePlugin/VolumeSensor.cs deleted file mode 100644 index e812078..0000000 --- a/InfoPanel.VolumePlugin/VolumeSensor.cs +++ /dev/null @@ -1,19 +0,0 @@ -using InfoPanel.Plugins; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace InfoPanel.Extras -{ - internal class VolumeSensor() : IPluginSensor - { - public string Id => "volume"; - public string Name => "Master Volume"; - public IPluginSensorValueType ValueType => IPluginSensorValueType.Double; - public object Value { get; set; } = 0; - public string? Unit => "%"; - - } -} diff --git a/InfoPanel.VolumePlugin/WeatherPlugin.cs b/InfoPanel.VolumePlugin/WeatherPlugin.cs deleted file mode 100644 index dafda7d..0000000 --- a/InfoPanel.VolumePlugin/WeatherPlugin.cs +++ /dev/null @@ -1,147 +0,0 @@ -using InfoPanel.Plugins; -using IniParser; -using IniParser.Model; -using OpenWeatherMap.Standard; -using System.Diagnostics; -using System.Reflection; - -namespace InfoPanel.Extras -{ - public class WeatherPlugin : IPlugin - { - private readonly Stopwatch _stopwatch = new(); - - private Current? _current; - private string? _city; - - private readonly WeatherSensor _name = new("name", "Name", IPluginSensorValueType.String, "-"); - private readonly WeatherSensor _weather = new("weather", "Weather", IPluginSensorValueType.String, "-"); - private readonly WeatherSensor _weatherDesc = new("weather_desc", "Weather Description", IPluginSensorValueType.String, "-"); - private readonly WeatherSensor _weatherIcon = new("weather_icon", "Weather Icon", IPluginSensorValueType.String, "-"); - private readonly WeatherSensor _weatherIconUrl = new("weather_icon_url", "Weather Icon URL", IPluginSensorValueType.String, "-"); - - private readonly WeatherSensor _temp = new("temp", "Temperature", IPluginSensorValueType.Double, 0, "°C"); - private readonly WeatherSensor _maxTemp = new("max_temp", "Maximum Temperature", IPluginSensorValueType.Double, 0, "°C"); - private readonly WeatherSensor _minTemp = new("min_temp", "Minimum Temperature", IPluginSensorValueType.Double, 0, "°C"); - private readonly WeatherSensor _pressure = new("pressure", "Pressure", IPluginSensorValueType.Double, 0, "hPa"); - private readonly WeatherSensor _seaLevel = new("sea_level", "Sea Level", IPluginSensorValueType.Double, 0, "hPa"); - private readonly WeatherSensor _groundLevel = new("ground_level", "Ground Level", IPluginSensorValueType.Double, 0, "hPa"); - private readonly WeatherSensor _feelsLike = new("feels_like", "Feels Like", IPluginSensorValueType.Double, 0, "°C"); - private readonly WeatherSensor _humidity = new("humidity", "Humidity", IPluginSensorValueType.Double, 0, "%"); - - private readonly WeatherSensor _windSpeed = new("wind_speed", "Wind Speed", IPluginSensorValueType.Double, 0, "m/s"); - private readonly WeatherSensor _windDeg = new("wind_deg", "Wind Degree", IPluginSensorValueType.Double, 0, "°"); - private readonly WeatherSensor _windGust = new("wind_gust", "Wind Gust", IPluginSensorValueType.Double, 0, "m/s"); - - private readonly WeatherSensor _clouds = new("clouds", "Clouds", IPluginSensorValueType.Double, 0, "%"); - - private readonly WeatherSensor _rain = new("rain", "Rain", IPluginSensorValueType.Double, 0, "mm/h"); - private readonly WeatherSensor _snow = new("snow", "Snow", IPluginSensorValueType.Double, 0, "mm/h"); - - string IPlugin.Name => "Weather Plugin"; - - void IPlugin.Initialize() - { - Assembly assembly = Assembly.GetExecutingAssembly(); - var configPath = $"{assembly.ManifestModule.FullyQualifiedName}.ini"; - - var parser = new FileIniDataParser(); - IniData config; - if (!File.Exists(configPath)) - { - config = new IniData(); - config["Weather Plugin"]["APIKey"] = ""; - config["Weather Plugin"]["City"] = "Singapore"; - parser.WriteFile(configPath, config); - }else - { - config = parser.ReadFile(configPath); - - var apiKey = config["Weather Plugin"]["APIKey"]; - _city = config["Weather Plugin"]["City"]; - - if(!string.IsNullOrEmpty(apiKey) && !string.IsNullOrEmpty(_city)) - { - _current = new(apiKey, OpenWeatherMap.Standard.Enums.WeatherUnits.Metric); - } - } - } - - void IPlugin.Close() - { - } - - List IPlugin.GetData() - { - return [ - _name, - _weather, - _weatherDesc, - _weatherIcon, - _weatherIconUrl, - _temp, - _maxTemp, - _minTemp, - _pressure, - _seaLevel, - _groundLevel, - _feelsLike, - _humidity, - _windSpeed, - _windDeg, - _windGust, - _clouds, - _rain, - _snow - ]; - } - - async Task IPlugin.UpdateAsync() - { - // Update weather data every minute - if (!_stopwatch.IsRunning || _stopwatch.ElapsedMilliseconds > 60000) - { - Trace.WriteLine("WeatherPlugin: Getting weather data"); - await GetWeather(); - _stopwatch.Restart(); - } - } - - private async Task GetWeather() - { - if(_current == null) - { - return; - } - - var result = await _current.GetWeatherDataByCityNameAsync("Singapore"); - - if (result != null) - { - _name.Value = result.Name; - _weather.Value = result.Weathers[0].Main; - _weatherDesc.Value = result.Weathers[0].Description; - _weatherIcon.Value = result.Weathers[0].Icon; - _weatherIconUrl.Value = $"https://openweathermap.org/img/wn/{result.Weathers[0].Icon}@2x.png"; - - _temp.Value = result.WeatherDayInfo.Temperature; - _maxTemp.Value = result.WeatherDayInfo.MaximumTemperature; - _minTemp.Value = result.WeatherDayInfo.MinimumTemperature; - _pressure.Value = result.WeatherDayInfo.Pressure; - _seaLevel.Value = result.WeatherDayInfo.SeaLevel; - _groundLevel.Value = result.WeatherDayInfo.GroundLevel; - _feelsLike.Value = result.WeatherDayInfo.FeelsLike; - _humidity.Value = result.WeatherDayInfo.Humidity; - - _windSpeed.Value = result.Wind.Speed; - _windDeg.Value = result.Wind.Degree; - _windGust.Value = result.Wind.Gust; - - _clouds.Value = result.Clouds.All; - - _rain.Value = result.Rain.LastHour; - _snow.Value = result.Snow.LastHour; - } - } - } -} diff --git a/InfoPanel.VolumePlugin/WeatherSensor.cs b/InfoPanel.VolumePlugin/WeatherSensor.cs deleted file mode 100644 index cfaf6f6..0000000 --- a/InfoPanel.VolumePlugin/WeatherSensor.cs +++ /dev/null @@ -1,19 +0,0 @@ -using InfoPanel.Plugins; -using OpenWeatherMap.Standard.Enums; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace InfoPanel.Extras -{ - internal class WeatherSensor(string id, string name, IPluginSensorValueType valueType, object value, string? unit = null): IPluginSensor - { - public string Id => id; - public string Name => name; - public IPluginSensorValueType ValueType => valueType; - public object Value { get; set; } = value; - public string? Unit => unit; - } -} diff --git a/InfoPanel.sln b/InfoPanel.sln index fd001ad..329fd5e 100644 --- a/InfoPanel.sln +++ b/InfoPanel.sln @@ -5,84 +5,40 @@ VisualStudioVersion = 17.6.33815.320 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InfoPanel", "InfoPanel\InfoPanel.csproj", "{59D47BC5-EBEE-48EC-BCF4-09C4BB0A31A8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InfoPanel.Extras", "InfoPanel.VolumePlugin\InfoPanel.Extras.csproj", "{DABE7840-A86C-410D-B420-29C716A0A9FA}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InfoPanel.Plugins", "InfoPanel.Plugins\InfoPanel.Plugins.csproj", "{903C16DD-BF96-44B7-B058-17F587194E89}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InfoPanel.Plugins.Simulator", "InfoPanel.Plugins.Simulator\InfoPanel.Plugins.Simulator.csproj", "{2573766B-535D-49FC-867F-5255568A1940}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InfoPanel.Plugins.Loader", "InfoPanel.Plugins.Loader\InfoPanel.Plugins.Loader.csproj", "{299B147B-6B0C-430F-9B91-AC1FB80AAF95}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InfoPanel.Extras", "InfoPanel.Extras\InfoPanel.Extras.csproj", "{53662165-B047-4425-92F9-991221173CF4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 - Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {59D47BC5-EBEE-48EC-BCF4-09C4BB0A31A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {59D47BC5-EBEE-48EC-BCF4-09C4BB0A31A8}.Debug|Any CPU.Build.0 = Debug|Any CPU {59D47BC5-EBEE-48EC-BCF4-09C4BB0A31A8}.Debug|x64.ActiveCfg = Debug|x64 {59D47BC5-EBEE-48EC-BCF4-09C4BB0A31A8}.Debug|x64.Build.0 = Debug|x64 - {59D47BC5-EBEE-48EC-BCF4-09C4BB0A31A8}.Debug|x86.ActiveCfg = Debug|Any CPU - {59D47BC5-EBEE-48EC-BCF4-09C4BB0A31A8}.Debug|x86.Build.0 = Debug|Any CPU - {59D47BC5-EBEE-48EC-BCF4-09C4BB0A31A8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {59D47BC5-EBEE-48EC-BCF4-09C4BB0A31A8}.Release|Any CPU.Build.0 = Release|Any CPU {59D47BC5-EBEE-48EC-BCF4-09C4BB0A31A8}.Release|x64.ActiveCfg = Release|x64 {59D47BC5-EBEE-48EC-BCF4-09C4BB0A31A8}.Release|x64.Build.0 = Release|x64 - {59D47BC5-EBEE-48EC-BCF4-09C4BB0A31A8}.Release|x86.ActiveCfg = Release|Any CPU - {59D47BC5-EBEE-48EC-BCF4-09C4BB0A31A8}.Release|x86.Build.0 = Release|Any CPU - {DABE7840-A86C-410D-B420-29C716A0A9FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DABE7840-A86C-410D-B420-29C716A0A9FA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DABE7840-A86C-410D-B420-29C716A0A9FA}.Debug|x64.ActiveCfg = Debug|x64 - {DABE7840-A86C-410D-B420-29C716A0A9FA}.Debug|x64.Build.0 = Debug|x64 - {DABE7840-A86C-410D-B420-29C716A0A9FA}.Debug|x86.ActiveCfg = Debug|Any CPU - {DABE7840-A86C-410D-B420-29C716A0A9FA}.Debug|x86.Build.0 = Debug|Any CPU - {DABE7840-A86C-410D-B420-29C716A0A9FA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DABE7840-A86C-410D-B420-29C716A0A9FA}.Release|Any CPU.Build.0 = Release|Any CPU - {DABE7840-A86C-410D-B420-29C716A0A9FA}.Release|x64.ActiveCfg = Release|Any CPU - {DABE7840-A86C-410D-B420-29C716A0A9FA}.Release|x64.Build.0 = Release|Any CPU - {DABE7840-A86C-410D-B420-29C716A0A9FA}.Release|x86.ActiveCfg = Release|Any CPU - {DABE7840-A86C-410D-B420-29C716A0A9FA}.Release|x86.Build.0 = Release|Any CPU - {903C16DD-BF96-44B7-B058-17F587194E89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {903C16DD-BF96-44B7-B058-17F587194E89}.Debug|Any CPU.Build.0 = Debug|Any CPU - {903C16DD-BF96-44B7-B058-17F587194E89}.Debug|x64.ActiveCfg = Debug|x64 - {903C16DD-BF96-44B7-B058-17F587194E89}.Debug|x64.Build.0 = Debug|x64 - {903C16DD-BF96-44B7-B058-17F587194E89}.Debug|x86.ActiveCfg = Debug|Any CPU - {903C16DD-BF96-44B7-B058-17F587194E89}.Debug|x86.Build.0 = Debug|Any CPU - {903C16DD-BF96-44B7-B058-17F587194E89}.Release|Any CPU.ActiveCfg = Release|Any CPU - {903C16DD-BF96-44B7-B058-17F587194E89}.Release|Any CPU.Build.0 = Release|Any CPU + {903C16DD-BF96-44B7-B058-17F587194E89}.Debug|x64.ActiveCfg = Debug|Any CPU + {903C16DD-BF96-44B7-B058-17F587194E89}.Debug|x64.Build.0 = Debug|Any CPU {903C16DD-BF96-44B7-B058-17F587194E89}.Release|x64.ActiveCfg = Release|Any CPU {903C16DD-BF96-44B7-B058-17F587194E89}.Release|x64.Build.0 = Release|Any CPU - {903C16DD-BF96-44B7-B058-17F587194E89}.Release|x86.ActiveCfg = Release|Any CPU - {903C16DD-BF96-44B7-B058-17F587194E89}.Release|x86.Build.0 = Release|Any CPU - {2573766B-535D-49FC-867F-5255568A1940}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2573766B-535D-49FC-867F-5255568A1940}.Debug|Any CPU.Build.0 = Debug|Any CPU {2573766B-535D-49FC-867F-5255568A1940}.Debug|x64.ActiveCfg = Debug|Any CPU {2573766B-535D-49FC-867F-5255568A1940}.Debug|x64.Build.0 = Debug|Any CPU - {2573766B-535D-49FC-867F-5255568A1940}.Debug|x86.ActiveCfg = Debug|Any CPU - {2573766B-535D-49FC-867F-5255568A1940}.Debug|x86.Build.0 = Debug|Any CPU - {2573766B-535D-49FC-867F-5255568A1940}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2573766B-535D-49FC-867F-5255568A1940}.Release|Any CPU.Build.0 = Release|Any CPU {2573766B-535D-49FC-867F-5255568A1940}.Release|x64.ActiveCfg = Release|Any CPU {2573766B-535D-49FC-867F-5255568A1940}.Release|x64.Build.0 = Release|Any CPU - {2573766B-535D-49FC-867F-5255568A1940}.Release|x86.ActiveCfg = Release|Any CPU - {2573766B-535D-49FC-867F-5255568A1940}.Release|x86.Build.0 = Release|Any CPU - {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Debug|Any CPU.Build.0 = Debug|Any CPU {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Debug|x64.ActiveCfg = Debug|Any CPU {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Debug|x64.Build.0 = Debug|Any CPU - {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Debug|x86.ActiveCfg = Debug|Any CPU - {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Debug|x86.Build.0 = Debug|Any CPU - {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Release|Any CPU.ActiveCfg = Release|Any CPU - {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Release|Any CPU.Build.0 = Release|Any CPU {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Release|x64.ActiveCfg = Release|Any CPU {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Release|x64.Build.0 = Release|Any CPU - {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Release|x86.ActiveCfg = Release|Any CPU - {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Release|x86.Build.0 = Release|Any CPU + {53662165-B047-4425-92F9-991221173CF4}.Debug|x64.ActiveCfg = Debug|Any CPU + {53662165-B047-4425-92F9-991221173CF4}.Debug|x64.Build.0 = Debug|Any CPU + {53662165-B047-4425-92F9-991221173CF4}.Release|x64.ActiveCfg = Release|Any CPU + {53662165-B047-4425-92F9-991221173CF4}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/InfoPanel/InfoPanel.csproj b/InfoPanel/InfoPanel.csproj index cba0aea..cb0a8a3 100644 --- a/InfoPanel/InfoPanel.csproj +++ b/InfoPanel/InfoPanel.csproj @@ -171,7 +171,6 @@ - diff --git a/InfoPanel/Services/BackgroundTask.cs b/InfoPanel/Services/BackgroundTask.cs index 74d933a..f160507 100644 --- a/InfoPanel/Services/BackgroundTask.cs +++ b/InfoPanel/Services/BackgroundTask.cs @@ -70,8 +70,9 @@ private void DisposeResources() { Trace.WriteLine("Disposing resources"); _cts?.Dispose(); - _task = null; + _task?.Dispose(); _cts = null; + _task = null; } } } From 11b74fea0e1e70744b07e7c93d6694f6a4a8b3b9 Mon Sep 17 00:00:00 2001 From: Habib Rehman <13956188+habibrehmansg@users.noreply.github.com> Date: Fri, 20 Dec 2024 22:19:09 +0800 Subject: [PATCH 04/10] Implement plugin support to UI & draw tasks --- InfoPanel.Extras/DriveInfoPlugin.cs | 76 +++-- InfoPanel.Extras/IpifyPlugin.cs | 4 +- InfoPanel.Extras/VolumePlugin.cs | 2 +- InfoPanel.Extras/WeatherPlugin.cs | 4 +- InfoPanel.Plugins.Simulator/Program.cs | 21 +- InfoPanel.Plugins/IPluginContainer.cs | 3 +- InfoPanel.Plugins/IPluginData.cs | 8 + InfoPanel.Plugins/IPluginSensor.cs | 4 +- InfoPanel.Plugins/IPluginText.cs | 4 +- InfoPanel.Plugins/PluginContainer.cs | 4 +- InfoPanel/App.xaml.cs | 8 +- InfoPanel/Drawing/GraphDraw.cs | 58 +++- InfoPanel/Enums/SensorType.cs | 9 + InfoPanel/Extensions/DoubleExtensions.cs | 26 ++ InfoPanel/InfoPanel.csproj | 3 + InfoPanel/Models/ChartDisplayItem.cs | 35 ++- InfoPanel/Models/GaugeDisplayItem.cs | 19 +- InfoPanel/Models/ISensorItem.cs | 8 +- InfoPanel/Models/SensorDisplayItem.cs | 148 +++++----- InfoPanel/Models/SensorId.cs | 46 ---- InfoPanel/Models/SensorImageDisplayItem.cs | 18 +- InfoPanel/Models/SensorReader.cs | 17 ++ InfoPanel/Models/SensorReading.cs | 7 + InfoPanel/Models/SharedModel.cs | 6 +- InfoPanel/Monitors/PluginMonitor.cs | 148 ++++++++++ InfoPanel/Services/TuringPanelATask.cs | 2 +- InfoPanel/Services/TuringPanelCTask.cs | 2 +- InfoPanel/Services/TuringPanelETask.cs | 2 +- .../ViewModels/Components/PluginSensorsVM.cs | 35 +++ InfoPanel/ViewModels/Components/TreeItem.cs | 33 ++- .../Components/Sensors/HwInfoSensors.xaml.cs | 8 +- .../Components/Sensors/LibreSensors.xaml.cs | 8 +- .../Components/Sensors/PluginSensors.xaml | 208 ++++++++++++++ .../Components/Sensors/PluginSensors.xaml.cs | 260 ++++++++++++++++++ InfoPanel/Views/Pages/DesignPage.xaml | 29 +- 35 files changed, 1077 insertions(+), 196 deletions(-) create mode 100644 InfoPanel.Plugins/IPluginData.cs create mode 100644 InfoPanel/Enums/SensorType.cs create mode 100644 InfoPanel/Extensions/DoubleExtensions.cs delete mode 100644 InfoPanel/Models/SensorId.cs create mode 100644 InfoPanel/Monitors/PluginMonitor.cs create mode 100644 InfoPanel/ViewModels/Components/PluginSensorsVM.cs create mode 100644 InfoPanel/Views/Components/Sensors/PluginSensors.xaml create mode 100644 InfoPanel/Views/Components/Sensors/PluginSensors.xaml.cs diff --git a/InfoPanel.Extras/DriveInfoPlugin.cs b/InfoPanel.Extras/DriveInfoPlugin.cs index 6f5f4e8..093be21 100644 --- a/InfoPanel.Extras/DriveInfoPlugin.cs +++ b/InfoPanel.Extras/DriveInfoPlugin.cs @@ -24,14 +24,14 @@ public override void Initialize() if (drive.IsReady) { PluginContainer container = new(drive.Name); - container.Text.Add(new PluginText("name", "Name", drive.Name)); - container.Text.Add(new PluginText("type", "Type", drive.DriveType.ToString())); - container.Text.Add(new PluginText("volume_label", "Volume Label", drive.VolumeLabel)); - container.Text.Add(new PluginText("format", "Format", drive.DriveFormat)); - container.Sensors.Add(new PluginSensor("total_size", "Total Size", drive.TotalSize / 1024 / 1024, "MB")); - container.Sensors.Add(new PluginSensor("free_space", "Free Space", drive.TotalFreeSpace / 1024 / 1024, "MB")); - container.Sensors.Add(new PluginSensor("available_space", "Available Space", drive.AvailableFreeSpace / 1024 / 1024, "MB")); - container.Sensors.Add(new PluginSensor("used_space", "Used Space", (drive.TotalSize - drive.TotalFreeSpace) / 1024 / 1024, "MB")); + container.Entries.Add(new PluginText("name", "Name", drive.Name)); + container.Entries.Add(new PluginText("type", "Type", drive.DriveType.ToString())); + container.Entries.Add(new PluginText("volume_label", "Volume Label", drive.VolumeLabel)); + container.Entries.Add(new PluginText("format", "Format", drive.DriveFormat)); + container.Entries.Add(new PluginSensor("total_size", "Total Size", drive.TotalSize / 1024 / 1024, "MB")); + container.Entries.Add(new PluginSensor("free_space", "Free Space", drive.TotalFreeSpace / 1024 / 1024, "MB")); + container.Entries.Add(new PluginSensor("available_space", "Available Space", drive.AvailableFreeSpace / 1024 / 1024, "MB")); + container.Entries.Add(new PluginSensor("used_space", "Used Space", (drive.TotalSize - drive.TotalFreeSpace) / 1024 / 1024, "MB")); _containers.Add(container); } @@ -56,37 +56,65 @@ public override Task UpdateAsync(CancellationToken cancellationToken) if (drive.IsReady) { - foreach (var text in container.Text) + foreach (var entry in container.Entries) { - switch (text.Id) + switch (entry.Id) { case "type": - text.Value = drive.DriveType.ToString(); + { + if (entry is PluginText text) + { + text.Value = drive.DriveType.ToString(); + } + } break; case "volume_label": - text.Value = drive.VolumeLabel; + { + if (entry is PluginText text) + { + text.Value = drive.VolumeLabel; + } + } break; case "format": - text.Value = drive.DriveFormat; + { + if (entry is PluginText text) + { + text.Value = drive.DriveFormat; + } + } break; - } - } - - foreach (var sensor in container.Sensors) - { - switch (sensor.Id) - { case "total_size": - sensor.Value = drive.TotalSize / 1024 / 1024; + { + if (entry is PluginSensor sensor) + { + sensor.Value = drive.TotalSize / 1024 / 1024; + } + } break; case "free_space": - sensor.Value = drive.TotalFreeSpace / 1024 / 1024; + { + if (entry is PluginSensor sensor) + { + sensor.Value = drive.TotalFreeSpace / 1024 / 1024; + } + } break; case "available_space": - sensor.Value = drive.AvailableFreeSpace / 1024 / 1024; + { + if (entry is PluginSensor sensor) + { + sensor.Value = drive.AvailableFreeSpace / 1024 / 1024; + } + } break; case "used_space": - sensor.Value = (drive.TotalSize - drive.TotalFreeSpace) / 1024 / 1024; + { + if (entry is PluginSensor sensor) + { + sensor.Value = (drive.TotalSize - drive.TotalFreeSpace) / 1024 / 1024; + } + } break; } } diff --git a/InfoPanel.Extras/IpifyPlugin.cs b/InfoPanel.Extras/IpifyPlugin.cs index 533ecef..da9cbd2 100644 --- a/InfoPanel.Extras/IpifyPlugin.cs +++ b/InfoPanel.Extras/IpifyPlugin.cs @@ -23,8 +23,8 @@ public override void Initialize() public override void Load(List containers) { var container = new PluginContainer("Public IP"); - container.Text.Add(_ipv4Sensor); - container.Text.Add(_ipv6Sensor); + container.Entries.Add(_ipv4Sensor); + container.Entries.Add(_ipv6Sensor); containers.Add(container); } diff --git a/InfoPanel.Extras/VolumePlugin.cs b/InfoPanel.Extras/VolumePlugin.cs index 8f0d82e..5c713a0 100644 --- a/InfoPanel.Extras/VolumePlugin.cs +++ b/InfoPanel.Extras/VolumePlugin.cs @@ -27,7 +27,7 @@ public override void Close() public override void Load(List containers) { var container = new PluginContainer("Default"); - container.Sensors.Add(_volumeSensor); + container.Entries.Add(_volumeSensor); containers.Add(container); } diff --git a/InfoPanel.Extras/WeatherPlugin.cs b/InfoPanel.Extras/WeatherPlugin.cs index a839fcd..27bf35c 100644 --- a/InfoPanel.Extras/WeatherPlugin.cs +++ b/InfoPanel.Extras/WeatherPlugin.cs @@ -81,8 +81,8 @@ public override void Load(List containers) if (_city != null) { var container = new PluginContainer(_city); - container.Text.AddRange([_name, _weather, _weatherDesc, _weatherIcon, _weatherIconUrl]); - container.Sensors.AddRange([_temp, _maxTemp, _minTemp, _pressure, _seaLevel, _groundLevel, _feelsLike, _humidity, _windSpeed, _windDeg, _windGust, _clouds, _rain, _snow]); + container.Entries.AddRange([_name, _weather, _weatherDesc, _weatherIcon, _weatherIconUrl]); + container.Entries.AddRange([_temp, _maxTemp, _minTemp, _pressure, _seaLevel, _groundLevel, _feelsLike, _humidity, _windSpeed, _windDeg, _windGust, _clouds, _rain, _snow]); containers.Add(container); } } diff --git a/InfoPanel.Plugins.Simulator/Program.cs b/InfoPanel.Plugins.Simulator/Program.cs index ac75bcd..8c52a94 100644 --- a/InfoPanel.Plugins.Simulator/Program.cs +++ b/InfoPanel.Plugins.Simulator/Program.cs @@ -1,4 +1,5 @@ -using InfoPanel.Plugins.Loader; +using InfoPanel.Plugins; +using InfoPanel.Plugins.Loader; using System.Text; PluginLoader pluginLoader = new(); @@ -54,15 +55,17 @@ foreach (var container in wrapper.PluginContainers) { buffer.AppendLine($"--{container.Name}"); - foreach (var text in container.Text) + foreach (var entry in container.Entries) { - var id = $"/{wrapper.Id}/{container.Id}/{text.Id}"; - buffer.AppendLine($"---{text.Name}: {text.Value}"); - } - - foreach (var text in container.Sensors) - { - buffer.AppendLine($"---{text.Name}: {text.Value}{text.Unit}"); + var id = $"/{wrapper.Id}/{container.Id}/{entry.Id}"; + + if(entry is IPluginText text) + { + buffer.AppendLine($"---{text.Name}: {text.Value}"); + }else if(entry is IPluginSensor sensor) + { + buffer.AppendLine($"---{sensor.Name}: {sensor.Value}{sensor.Unit}"); + } } } diff --git a/InfoPanel.Plugins/IPluginContainer.cs b/InfoPanel.Plugins/IPluginContainer.cs index 27709e7..cf86005 100644 --- a/InfoPanel.Plugins/IPluginContainer.cs +++ b/InfoPanel.Plugins/IPluginContainer.cs @@ -4,7 +4,6 @@ public interface IPluginContainer { string Id { get; } string Name { get; } - List Text { get; } - List Sensors { get; } + List Entries { get; } } } diff --git a/InfoPanel.Plugins/IPluginData.cs b/InfoPanel.Plugins/IPluginData.cs new file mode 100644 index 0000000..d8b8eea --- /dev/null +++ b/InfoPanel.Plugins/IPluginData.cs @@ -0,0 +1,8 @@ +namespace InfoPanel.Plugins +{ + public interface IPluginData + { + string Id { get; } + string Name { get; } + } +} diff --git a/InfoPanel.Plugins/IPluginSensor.cs b/InfoPanel.Plugins/IPluginSensor.cs index 56ce384..cec4100 100644 --- a/InfoPanel.Plugins/IPluginSensor.cs +++ b/InfoPanel.Plugins/IPluginSensor.cs @@ -6,10 +6,8 @@ namespace InfoPanel.Plugins { - public interface IPluginSensor + public interface IPluginSensor: IPluginData { - string Id { get; } - string Name { get; } float Value { get; set; } string? Unit { get; } } diff --git a/InfoPanel.Plugins/IPluginText.cs b/InfoPanel.Plugins/IPluginText.cs index 797b1e9..615da89 100644 --- a/InfoPanel.Plugins/IPluginText.cs +++ b/InfoPanel.Plugins/IPluginText.cs @@ -6,10 +6,8 @@ namespace InfoPanel.Plugins { - public interface IPluginText + public interface IPluginText: IPluginData { - string Id { get; } - string Name { get; } string Value { get; set; } } } diff --git a/InfoPanel.Plugins/PluginContainer.cs b/InfoPanel.Plugins/PluginContainer.cs index 4c672c3..16c9177 100644 --- a/InfoPanel.Plugins/PluginContainer.cs +++ b/InfoPanel.Plugins/PluginContainer.cs @@ -4,8 +4,6 @@ public class PluginContainer(string name) : IPluginContainer { public string Id { get; } = IdUtil.Encode(name); public string Name { get; } = name; - public List Text { get; } = []; - - public List Sensors { get; } = []; + public List Entries { get; } = []; } } diff --git a/InfoPanel/App.xaml.cs b/InfoPanel/App.xaml.cs index e4deb65..b155946 100644 --- a/InfoPanel/App.xaml.cs +++ b/InfoPanel/App.xaml.cs @@ -190,7 +190,13 @@ protected override async void OnStartup(StartupEventArgs e) HWHash.SetDelay(300); HWHash.Launch(); - await LibreMonitor.Instance.StartAsync(); + if (ConfigModel.Instance.Settings.LibreHardwareMonitor) + { + LibreMonitor.Instance.SetRing0(ConfigModel.Instance.Settings.LibreHardMonitorRing0); + await LibreMonitor.Instance.StartAsync(); + } + + await PluginMonitor.Instance.StartAsync(); SystemEvents.SessionEnding += OnSessionEnding; SystemEvents.PowerModeChanged += OnPowerChange; diff --git a/InfoPanel/Drawing/GraphDraw.cs b/InfoPanel/Drawing/GraphDraw.cs index 66fc66d..af77fef 100644 --- a/InfoPanel/Drawing/GraphDraw.cs +++ b/InfoPanel/Drawing/GraphDraw.cs @@ -1,5 +1,6 @@ using InfoPanel.Models; using InfoPanel.Monitors; +using InfoPanel.Plugins; using LibreHardwareMonitor.Hardware; using Microsoft.Extensions.Caching.Memory; using System; @@ -19,6 +20,7 @@ internal class GraphDraw }); private static readonly ConcurrentDictionary<(UInt32, UInt32, UInt32), Queue> GraphDataCache = []; private static readonly ConcurrentDictionary> GraphDataCache2 = []; + private static readonly ConcurrentDictionary> GraphDataCache3 = []; private static readonly Stopwatch Stopwatch = new(); static GraphDraw() @@ -56,6 +58,21 @@ private static Queue GetGraphDataQueue(string libreSensorId) return result; } + private static Queue GetGraphPluginDataQueue(string pluginSensorId) + { + var key = pluginSensorId; + + GraphDataCache3.TryGetValue(key, out Queue? result); + + if (result == null) + { + result = new Queue(); + GraphDataCache3.TryAdd(key, result); + } + + return result; + } + public static void Run(ChartDisplayItem chartDisplayItem, MyGraphics g) { var elapsedMilliseconds = Stopwatch.ElapsedMilliseconds; @@ -68,7 +85,7 @@ public static void Run(ChartDisplayItem chartDisplayItem, MyGraphics g) if (queue != null) { HWHash.SENSORHASH.TryGetValue(key, out HWHash.HWINFO_HASH value); - + lock (queue) { queue.Enqueue(value.ValueNow); @@ -98,6 +115,28 @@ public static void Run(ChartDisplayItem chartDisplayItem, MyGraphics g) } } } + + foreach (var key in GraphDataCache3.Keys) + { + GraphDataCache3.TryGetValue(key, out Queue? queue); + if (queue != null) + { + if(PluginMonitor.SENSORHASH.TryGetValue(key, out PluginMonitor.PluginReading value) && value.Data is PluginSensor sensor) + { + lock (queue) + { + queue.Enqueue(sensor.Value); + + if (queue.Count > 4096) + { + queue.Dequeue(); + } + } + } + + } + } + Stopwatch.Restart(); } @@ -108,10 +147,14 @@ public static void Run(ChartDisplayItem chartDisplayItem, MyGraphics g) Queue queue; - if (chartDisplayItem.SensorType == Models.SensorType.Libre) + if (chartDisplayItem.SensorType == Enums.SensorType.Libre) { queue = GetGraphDataQueue(chartDisplayItem.LibreSensorId); } + else if (chartDisplayItem.SensorType == Enums.SensorType.Plugin) + { + queue = GetGraphPluginDataQueue(chartDisplayItem.PluginSensorId); + } else { queue = GetGraphDataQueue(chartDisplayItem.Id, chartDisplayItem.Instance, chartDisplayItem.EntryId); @@ -128,7 +171,7 @@ public static void Run(ChartDisplayItem chartDisplayItem, MyGraphics g) { tempValues = [.. queue]; } - + double minValue = chartDisplayItem.MinValue; double maxValue = chartDisplayItem.MaxValue; @@ -327,8 +370,8 @@ public static void Run(ChartDisplayItem chartDisplayItem, MyGraphics g) { if (barDisplayItem.Gradient) { - g.FillRectangle(barDisplayItem.Color, usageRect.X, usageRect.Y, usageRect.Width, usageRect.Height, barDisplayItem.GradientColor, frameRect.Width >= frameRect.Height); - + g.FillRectangle(barDisplayItem.Color, usageRect.X, usageRect.Y, usageRect.Width, usageRect.Height, barDisplayItem.GradientColor, frameRect.Width >= frameRect.Height); + } else { @@ -369,10 +412,11 @@ public static void Run(ChartDisplayItem chartDisplayItem, MyGraphics g) if (chartDisplayItem is not DonutDisplayItem && chartDisplayItem.Frame) { - if(g is CompatGraphics) + if (g is CompatGraphics) { g.DrawRectangle(chartDisplayItem.FrameColor, 1, 0, 0, chartDisplayItem.Width - 1, chartDisplayItem.Height - 1); - } else + } + else { g.DrawRectangle(chartDisplayItem.FrameColor, 1, 0, 0, chartDisplayItem.Width, chartDisplayItem.Height); } diff --git a/InfoPanel/Enums/SensorType.cs b/InfoPanel/Enums/SensorType.cs new file mode 100644 index 0000000..d5706c2 --- /dev/null +++ b/InfoPanel/Enums/SensorType.cs @@ -0,0 +1,9 @@ +namespace InfoPanel.Enums +{ + public enum SensorType + { + HwInfo = 0, + Libre = 1, + Plugin = 2 + } +} diff --git a/InfoPanel/Extensions/DoubleExtensions.cs b/InfoPanel/Extensions/DoubleExtensions.cs new file mode 100644 index 0000000..3a3187e --- /dev/null +++ b/InfoPanel/Extensions/DoubleExtensions.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace InfoPanel.Extensions +{ + public static class DoubleExtensions + { + public static string ToFormattedString(this double value, string format = "F2") + { + // Check if the value is a whole number + if (value == Math.Truncate(value)) + { + // If it's a whole number, format without decimals + return value.ToString("F0"); + } + else + { + // Otherwise, format with up to two decimal places + return value.ToString(format).TrimEnd('0').TrimEnd('.'); + } + } + } +} diff --git a/InfoPanel/InfoPanel.csproj b/InfoPanel/InfoPanel.csproj index cb0a8a3..0f48d1f 100644 --- a/InfoPanel/InfoPanel.csproj +++ b/InfoPanel/InfoPanel.csproj @@ -188,6 +188,9 @@ Code + + Code + Code diff --git a/InfoPanel/Models/ChartDisplayItem.cs b/InfoPanel/Models/ChartDisplayItem.cs index afa62e7..0fe2ab1 100644 --- a/InfoPanel/Models/ChartDisplayItem.cs +++ b/InfoPanel/Models/ChartDisplayItem.cs @@ -1,4 +1,5 @@ -using System; +using InfoPanel.Enums; +using System; using System.Drawing; using System.Windows; @@ -67,6 +68,16 @@ public string LibreSensorId } } + private string _pluginSensorId = string.Empty; + public string PluginSensorId + { + get { return _pluginSensorId; } + set + { + SetProperty(ref _pluginSensorId, value); + } + } + public SensorValueType _valueType = SensorValueType.NOW; public SensorValueType ValueType { @@ -248,6 +259,11 @@ public string Color public ChartDisplayItem() { } + public ChartDisplayItem(string name) : base(name) + { + SensorName = name; + } + public ChartDisplayItem(string name, string libreSensorId) : base(name) { SensorName = name; @@ -273,6 +289,7 @@ public ChartDisplayItem(string name, UInt32 id, UInt32 instance, UInt32 entryId) { SensorType.HwInfo => SensorReader.ReadHwInfoSensor(Id, Instance, EntryId), SensorType.Libre => SensorReader.ReadLibreSensor(LibreSensorId), + SensorType.Plugin => SensorReader.ReadPluginSensor(PluginSensorId), _ => null, }; } @@ -388,6 +405,12 @@ public GraphDisplayItem() { Name = "Graph"; } + + public GraphDisplayItem(string name, GraphType type) : base(name) + { + Type = type; + } + public GraphDisplayItem(string name, GraphType type, string libreSensorId) : base(name, libreSensorId) { Type = type; @@ -450,6 +473,9 @@ public BarDisplayItem() Name = "Bar"; } + public BarDisplayItem(string name) : base(name) + { } + public BarDisplayItem(string name, string libreSensorId) : base(name, libreSensorId) { } @@ -524,6 +550,13 @@ public DonutDisplayItem() Name = "Donut"; } + public DonutDisplayItem(string name) : base(name) + { + Frame = false; + BackgroundColor = "#FFDCDCDC"; + Width = 100; Height = 100; + } + public DonutDisplayItem(string name, string libreSensorId) : base(name, libreSensorId) { Frame = false; diff --git a/InfoPanel/Models/GaugeDisplayItem.cs b/InfoPanel/Models/GaugeDisplayItem.cs index 605e723..5166ad5 100644 --- a/InfoPanel/Models/GaugeDisplayItem.cs +++ b/InfoPanel/Models/GaugeDisplayItem.cs @@ -1,4 +1,5 @@ -using System; +using InfoPanel.Enums; +using System; using System.Collections.ObjectModel; using System.Drawing; using System.Linq; @@ -70,6 +71,16 @@ public string LibreSensorId } } + private string _pluginSensorId = string.Empty; + public string PluginSensorId + { + get { return _pluginSensorId; } + set + { + SetProperty(ref _pluginSensorId, value); + } + } + public SensorValueType _valueType = SensorValueType.NOW; public SensorValueType ValueType { @@ -171,6 +182,11 @@ public GaugeDisplayItem() Name = "Gauge"; } + public GaugeDisplayItem(string name) : base(name) + { + SensorName = name; + } + public GaugeDisplayItem(string name, string libreSensorId) : base(name) { SensorName = name; @@ -193,6 +209,7 @@ public GaugeDisplayItem(string name, UInt32 id, UInt32 instance, UInt32 entryId) { SensorType.HwInfo => SensorReader.ReadHwInfoSensor(Id, Instance, EntryId), SensorType.Libre => SensorReader.ReadLibreSensor(LibreSensorId), + SensorType.Plugin => SensorReader.ReadPluginSensor(PluginSensorId), _ => null, }; } diff --git a/InfoPanel/Models/ISensorItem.cs b/InfoPanel/Models/ISensorItem.cs index c5edb71..44cafe7 100644 --- a/InfoPanel/Models/ISensorItem.cs +++ b/InfoPanel/Models/ISensorItem.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using InfoPanel.Enums; +using System; namespace InfoPanel.Models { @@ -20,6 +17,7 @@ internal interface ISensorItem UInt32 EntryId { get; set; } string LibreSensorId { get; set; } + string PluginSensorId { get; set; } SensorValueType ValueType { get; set; } SensorReading? GetValue(); diff --git a/InfoPanel/Models/SensorDisplayItem.cs b/InfoPanel/Models/SensorDisplayItem.cs index 2c53ada..dfd9cf1 100644 --- a/InfoPanel/Models/SensorDisplayItem.cs +++ b/InfoPanel/Models/SensorDisplayItem.cs @@ -1,10 +1,7 @@ -using LibreHardwareMonitor.Hardware; +using InfoPanel.Enums; +using LibreHardwareMonitor.Hardware; using System; -using System.Collections.Generic; using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace InfoPanel.Models { @@ -21,8 +18,8 @@ public string SensorName } } - private SensorType _sensorIdType = SensorType.HwInfo; - public SensorType SensorType + private Enums.SensorType _sensorIdType = Enums.SensorType.HwInfo; + public Enums.SensorType SensorType { get { return _sensorIdType; } set @@ -71,6 +68,16 @@ public string LibreSensorId } } + private string _pluginSensorId = string.Empty; + public string PluginSensorId + { + get { return _pluginSensorId; } + set + { + SetProperty(ref _pluginSensorId, value); + } + } + public SensorValueType _valueType = SensorValueType.NOW; public SensorValueType ValueType { @@ -248,17 +255,22 @@ public SensorDisplayItem() SensorName = string.Empty; } + public SensorDisplayItem(string name) : base(name) + { + SensorName = name; + } + public SensorDisplayItem(string name, string libreSensorId) : base(name) { SensorName = name; - SensorType = SensorType.Libre; + SensorType = Enums.SensorType.Libre; LibreSensorId = libreSensorId; } public SensorDisplayItem(string name, uint id, uint instance, uint entryId) : base(name) { SensorName = name; - SensorType = SensorType.HwInfo; + SensorType = Enums.SensorType.HwInfo; Id = id; Instance = instance; EntryId = entryId; @@ -268,8 +280,9 @@ public SensorDisplayItem(string name, uint id, uint instance, uint entryId) : ba { return SensorType switch { - SensorType.HwInfo => SensorReader.ReadHwInfoSensor(Id, Instance, EntryId), - SensorType.Libre => SensorReader.ReadLibreSensor(LibreSensorId), + Enums.SensorType.HwInfo => SensorReader.ReadHwInfoSensor(Id, Instance, EntryId), + Enums.SensorType.Libre => SensorReader.ReadLibreSensor(LibreSensorId), + Enums.SensorType.Plugin => SensorReader.ReadPluginSensor(PluginSensorId), _ => null, }; } @@ -353,70 +366,79 @@ public override string EvaluateText() private string EvaluateText(SensorReading sensorReading) { - var value = string.Empty; - - double sensorReadingValue; - - switch (ValueType) + string? value; + // new string sensor handling + if (!string.IsNullOrEmpty(sensorReading.ValueText)) { - case SensorValueType.MIN: - sensorReadingValue = sensorReading.ValueMin; - break; - case SensorValueType.MAX: - sensorReadingValue = sensorReading.ValueMax; - break; - case SensorValueType.AVERAGE: - sensorReadingValue = sensorReading.ValueAvg; - break; - default: - sensorReadingValue = sensorReading.ValueNow; - break; + value = sensorReading.ValueText; } - - sensorReadingValue = sensorReadingValue * MultiplicationModifier + AdditionModifier; - - if (AbsoluteAddition) + else { - sensorReadingValue = Math.Abs(sensorReadingValue); - } - if (OverridePrecision) - { - switch (Precision) + double sensorReadingValue; + + switch (ValueType) { - case 1: - value = string.Format("{0:0.0}", sensorReadingValue); + case SensorValueType.MIN: + sensorReadingValue = sensorReading.ValueMin; break; - case 2: - value = string.Format("{0:0.00}", sensorReadingValue); + case SensorValueType.MAX: + sensorReadingValue = sensorReading.ValueMax; break; - case 3: - value = string.Format("{0:0.000}", sensorReadingValue); + case SensorValueType.AVERAGE: + sensorReadingValue = sensorReading.ValueAvg; break; default: - value = string.Format("{0:0}", Math.Floor(sensorReadingValue)); + sensorReadingValue = sensorReading.ValueNow; break; } - } - else - { - switch (sensorReading.Unit.ToLower()) + + sensorReadingValue = sensorReadingValue * MultiplicationModifier + AdditionModifier; + + if (AbsoluteAddition) { - case "gb": - value = string.Format("{0:0.0}", sensorReadingValue); - break; - case "kb/s": - case "mb/s": - case "mbar/min": - case "mbar": - value = string.Format("{0:0.00}", sensorReadingValue); - break; - case "v": - value = string.Format("{0:0.000}", sensorReadingValue); - break; - default: - value = string.Format("{0:0}", sensorReadingValue); - break; + sensorReadingValue = Math.Abs(sensorReadingValue); + } + + + if (OverridePrecision) + { + switch (Precision) + { + case 1: + value = string.Format("{0:0.0}", sensorReadingValue); + break; + case 2: + value = string.Format("{0:0.00}", sensorReadingValue); + break; + case 3: + value = string.Format("{0:0.000}", sensorReadingValue); + break; + default: + value = string.Format("{0:0}", Math.Floor(sensorReadingValue)); + break; + } + } + else + { + switch (sensorReading.Unit.ToLower()) + { + case "gb": + value = string.Format("{0:0.0}", sensorReadingValue); + break; + case "kb/s": + case "mb/s": + case "mbar/min": + case "mbar": + value = string.Format("{0:0.00}", sensorReadingValue); + break; + case "v": + value = string.Format("{0:0.000}", sensorReadingValue); + break; + default: + value = string.Format("{0:0}", sensorReadingValue); + break; + } } } diff --git a/InfoPanel/Models/SensorId.cs b/InfoPanel/Models/SensorId.cs deleted file mode 100644 index 5a5d797..0000000 --- a/InfoPanel/Models/SensorId.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace InfoPanel.Models -{ - public enum SensorType - { - HwInfo = 0, - Libre = 1, - } - - public abstract class SensorId - { - } - - public class HwInfoSensorId : SensorId - { - public UInt32 Id { get; set; } - public UInt32 Instance { get; set; } - public UInt32 EntryId { get; set; } - - public HwInfoSensorId() { } - - public HwInfoSensorId(UInt32 id, UInt32 instance, UInt32 entryId) - { - Id = id; - Instance = instance; - EntryId = entryId; - } - } - - public class LibreSensorId : SensorId - { - public string SensorIdValue { get; set; } - - public LibreSensorId() { } - - public LibreSensorId(string sensorId) - { - SensorIdValue = sensorId; - } - } -} diff --git a/InfoPanel/Models/SensorImageDisplayItem.cs b/InfoPanel/Models/SensorImageDisplayItem.cs index 636c1d1..1ddc10b 100644 --- a/InfoPanel/Models/SensorImageDisplayItem.cs +++ b/InfoPanel/Models/SensorImageDisplayItem.cs @@ -1,4 +1,5 @@ -using System; +using InfoPanel.Enums; +using System; namespace InfoPanel.Models { @@ -65,6 +66,16 @@ public string LibreSensorId } } + private string _pluginSensorId = string.Empty; + public string PluginSensorId + { + get { return _pluginSensorId; } + set + { + SetProperty(ref _pluginSensorId, value); + } + } + public SensorValueType _valueType = SensorValueType.NOW; public SensorValueType ValueType { @@ -98,6 +109,10 @@ public double Threshold2 public SensorImageDisplayItem(): base() { } + public SensorImageDisplayItem(string name, Guid profileGuid) : base(name, profileGuid) + { + SensorName = name; + } public SensorImageDisplayItem(string name, Guid profileGuid, uint id, uint instance, uint entryId) : base(name, profileGuid) { SensorName = name; @@ -120,6 +135,7 @@ public SensorImageDisplayItem(string name, Guid profileGuid, string libreSensorI { SensorType.HwInfo => SensorReader.ReadHwInfoSensor(Id, Instance, EntryId), SensorType.Libre => SensorReader.ReadLibreSensor(LibreSensorId), + SensorType.Plugin => SensorReader.ReadPluginSensor(PluginSensorId), _ => null, }; } diff --git a/InfoPanel/Models/SensorReader.cs b/InfoPanel/Models/SensorReader.cs index c0a1967..a84986d 100644 --- a/InfoPanel/Models/SensorReader.cs +++ b/InfoPanel/Models/SensorReader.cs @@ -1,4 +1,5 @@ using InfoPanel.Monitors; +using InfoPanel.Plugins; using LibreHardwareMonitor.Hardware; using System; @@ -24,5 +25,21 @@ internal class SensorReader } return null; } + + public static SensorReading? ReadPluginSensor(string sensorId) + { + if (PluginMonitor.SENSORHASH.TryGetValue(sensorId, out PluginMonitor.PluginReading reading)) + { + if (reading.Data is IPluginSensor sensor) + { + return new SensorReading(0, 0, 0, sensor.Value, sensor.Unit ?? ""); + } + else if (reading.Data is IPluginText text) + { + return new SensorReading(text.Value); + } + } + return null; + } } } diff --git a/InfoPanel/Models/SensorReading.cs b/InfoPanel/Models/SensorReading.cs index c7611bd..33d519e 100644 --- a/InfoPanel/Models/SensorReading.cs +++ b/InfoPanel/Models/SensorReading.cs @@ -26,10 +26,17 @@ public SensorReading(float valueMin, float valueMax, float valueAvg, float value Unit = unit; } + public SensorReading(string value) + { + ValueText = value; + Unit = string.Empty; + } + public double ValueMin; public double ValueMax; public double ValueAvg; public double ValueNow; + public string? ValueText; public string Unit; } } diff --git a/InfoPanel/Models/SharedModel.cs b/InfoPanel/Models/SharedModel.cs index ad8f459..e3b436b 100644 --- a/InfoPanel/Models/SharedModel.cs +++ b/InfoPanel/Models/SharedModel.cs @@ -1198,7 +1198,7 @@ public void ImportProfile(string importPath) { foreach (DisplayItem displayItem in displayItems) { - if (displayItem is SensorDisplayItem sensorDisplayItem && sensorDisplayItem.SensorType == SensorType.HwInfo) + if (displayItem is SensorDisplayItem sensorDisplayItem && sensorDisplayItem.SensorType == Enums.SensorType.HwInfo) { if (!HWHash.SENSORHASH.TryGetValue((sensorDisplayItem.Id, sensorDisplayItem.Instance, sensorDisplayItem.EntryId), out _)) { @@ -1212,7 +1212,7 @@ public void ImportProfile(string importPath) } } } - else if (displayItem is ChartDisplayItem chartDisplayItem && chartDisplayItem.SensorType == SensorType.HwInfo) + else if (displayItem is ChartDisplayItem chartDisplayItem && chartDisplayItem.SensorType == Enums.SensorType.HwInfo) { if (!HWHash.SENSORHASH.TryGetValue((chartDisplayItem.Id, chartDisplayItem.Instance, chartDisplayItem.EntryId), out _)) { @@ -1226,7 +1226,7 @@ public void ImportProfile(string importPath) } } } - else if (displayItem is GaugeDisplayItem gaugeDisplayItem && gaugeDisplayItem.SensorType == SensorType.HwInfo) + else if (displayItem is GaugeDisplayItem gaugeDisplayItem && gaugeDisplayItem.SensorType == Enums.SensorType.HwInfo) { if (!HWHash.SENSORHASH.TryGetValue((gaugeDisplayItem.Id, gaugeDisplayItem.Instance, gaugeDisplayItem.EntryId), out _)) { diff --git a/InfoPanel/Monitors/PluginMonitor.cs b/InfoPanel/Monitors/PluginMonitor.cs new file mode 100644 index 0000000..bb41615 --- /dev/null +++ b/InfoPanel/Monitors/PluginMonitor.cs @@ -0,0 +1,148 @@ +using InfoPanel.Plugins; +using InfoPanel.Plugins.Loader; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace InfoPanel.Monitors +{ + internal class PluginMonitor : BackgroundTask + { + private static readonly Lazy _instance = new(() => new PluginMonitor()); + public static PluginMonitor Instance => _instance.Value; + + private static readonly ConcurrentDictionary HEADER_DICT = new(); + public static readonly ConcurrentDictionary SENSORHASH = new(); + + private PluginMonitor() { } + + + + Dictionary loadedPlugins = []; + + private async Task load() + { + PluginLoader pluginLoader = new(); + + var currentDirectory = Directory.GetCurrentDirectory(); + var pluginPath = Path.Combine(currentDirectory, "..\\..\\..\\..\\..\\InfoPanel.Extras\\bin\\Debug\\net8.0-windows", "InfoPanel.Extras.dll"); + + var plugins = pluginLoader.InitializePlugin(pluginPath); + + + foreach (var plugin in plugins) + { + PluginWrapper pluginWrapper = new(plugin); + if (loadedPlugins.TryAdd(pluginWrapper.Name, pluginWrapper)) + { + try + { + await pluginWrapper.Initialize(); + Console.WriteLine($"Plugin {pluginWrapper.Name} loaded successfully"); + } + catch (Exception ex) + { + Console.WriteLine($"Plugin {pluginWrapper.Name} failed to load: {ex.Message}"); + } + } + else + { + Console.WriteLine($"Plugin {pluginWrapper.Name} already loaded or duplicate plugin/name"); + } + + //break; + } + + } + + protected override async Task DoWorkAsync(CancellationToken token) + { + await Task.Delay(300, token); + try + { + var stopwatch = new Stopwatch(); + stopwatch.Start(); + + await load(); + + stopwatch.Stop(); + Trace.WriteLine($"Computer open: {stopwatch.ElapsedMilliseconds}ms"); + + try + { + + while (!token.IsCancellationRequested) + { + int indexOrder = 0; + foreach (var wrapper in loadedPlugins.Values) + { + wrapper.Update(); + + foreach (var container in wrapper.PluginContainers) + { + foreach (var entry in container.Entries) + { + var id = $"/{wrapper.Id}/{container.Id}/{entry.Id}"; + SENSORHASH[id] = new() + { + Id = id, + Name = entry.Name, + ContainerId = container.Id, + ContainerName = container.Name, + PluginId = wrapper.Id, + PluginName = wrapper.Name, + Data = entry, + IndexOrder = indexOrder++ + }; + } + } + } + + await Task.Delay(100, token); + + } + } + catch (TaskCanceledException) + { + Trace.WriteLine("Task cancelled"); + } + catch (Exception ex) + { + Trace.WriteLine($"Exception during work: {ex.Message}"); + } + finally + { + HEADER_DICT.Clear(); + SENSORHASH.Clear(); + } + } + catch (Exception e) + { + Trace.WriteLine($"LibreMonitor: Init error: {e.Message}"); + } + } + + public static List GetOrderedList() + { + List OrderedList = [.. SENSORHASH.Values.OrderBy(x => x.IndexOrder)]; + return OrderedList; + } + + public record struct PluginReading + { + public string Id { get; set; } + public string Name { get; set; } + public string ContainerId { get; set; } + public string ContainerName { get; set; } + public string PluginId { get; set; } + public string PluginName { get; set; } + public IPluginData Data { get; set; } + public int IndexOrder { get; set; } + } + } +} diff --git a/InfoPanel/Services/TuringPanelATask.cs b/InfoPanel/Services/TuringPanelATask.cs index e8a3f57..f6f8aa4 100644 --- a/InfoPanel/Services/TuringPanelATask.cs +++ b/InfoPanel/Services/TuringPanelATask.cs @@ -29,7 +29,7 @@ private TuringPanelATask() if (ConfigModel.Instance.GetProfile(profileGuid) is Profile profile) { - var bitmap = PanelDrawTask.Render(profile, false); + var bitmap = PanelDrawTask.Render(profile, false, videoBackgroundFallback: true); var rotation = ConfigModel.Instance.Settings.TuringPanelARotation; if (rotation != ViewModels.LCD_ROTATION.RotateNone) { diff --git a/InfoPanel/Services/TuringPanelCTask.cs b/InfoPanel/Services/TuringPanelCTask.cs index 0641064..fa72a3d 100644 --- a/InfoPanel/Services/TuringPanelCTask.cs +++ b/InfoPanel/Services/TuringPanelCTask.cs @@ -28,7 +28,7 @@ private TuringPanelCTask() if (ConfigModel.Instance.GetProfile(profileGuid) is Profile profile) { - var bitmap = PanelDrawTask.Render(profile, false); + var bitmap = PanelDrawTask.Render(profile, false, videoBackgroundFallback: true); var rotation = ConfigModel.Instance.Settings.TuringPanelCRotation; if (rotation != ViewModels.LCD_ROTATION.RotateNone) { diff --git a/InfoPanel/Services/TuringPanelETask.cs b/InfoPanel/Services/TuringPanelETask.cs index 8d98a50..067a4d6 100644 --- a/InfoPanel/Services/TuringPanelETask.cs +++ b/InfoPanel/Services/TuringPanelETask.cs @@ -30,7 +30,7 @@ private TuringPanelETask() if (ConfigModel.Instance.GetProfile(profileGuid) is Profile profile) { - var bitmap = PanelDrawTask.Render(profile, false); + var bitmap = PanelDrawTask.Render(profile, false, videoBackgroundFallback: true); var rotation = ConfigModel.Instance.Settings.TuringPanelERotation; if (rotation != ViewModels.LCD_ROTATION.RotateNone) { diff --git a/InfoPanel/ViewModels/Components/PluginSensorsVM.cs b/InfoPanel/ViewModels/Components/PluginSensorsVM.cs new file mode 100644 index 0000000..f8b5c4f --- /dev/null +++ b/InfoPanel/ViewModels/Components/PluginSensorsVM.cs @@ -0,0 +1,35 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using System.Collections.ObjectModel; + +namespace InfoPanel.ViewModels.Components +{ + public partial class PluginSensorsVM : ObservableObject + { + public ObservableCollection Sensors { get; set; } + + private PluginSensorItem? selectedItem; + public PluginSensorItem? SelectedItem + { + get { return selectedItem; } + set { SetProperty(ref selectedItem, value); } + } + + public PluginSensorsVM() + { + Sensors = []; + } + + public TreeItem? FindParentSensorItem(object id) + { + foreach (var sensorItem in Sensors) + { + if (sensorItem.Id.Equals(id)) + { + return sensorItem; + } + } + + return null; + } + } +} diff --git a/InfoPanel/ViewModels/Components/TreeItem.cs b/InfoPanel/ViewModels/Components/TreeItem.cs index 1d3edbe..14ed6f7 100644 --- a/InfoPanel/ViewModels/Components/TreeItem.cs +++ b/InfoPanel/ViewModels/Components/TreeItem.cs @@ -1,10 +1,9 @@ using CommunityToolkit.Mvvm.ComponentModel; +using InfoPanel.Extensions; using InfoPanel.Models; using LibreHardwareMonitor.Hardware; using System; using System.Collections.ObjectModel; -using System.Drawing; -using System.Xml.Linq; namespace InfoPanel.ViewModels.Components { @@ -79,8 +78,8 @@ public LibreGroupTreeItem(object id, string name, LibreHardwareMonitor.Hardware. } public abstract class SensorTreeItem(object id, string name) : TreeItem(id, name) { - private double _value; - public double Value + private string _value = string.Empty; + public string Value { get { return _value; } set { SetProperty(ref _value, value); } @@ -108,7 +107,7 @@ public override void Update() var sensorReading = SensorReader.ReadHwInfoSensor(ParentId, ParentInstance, SensorId); if(sensorReading.HasValue) { - Value = sensorReading.Value.ValueNow; + Value = sensorReading.Value.ValueNow.ToFormattedString(); Unit = sensorReading.Value.Unit; } } @@ -124,7 +123,29 @@ public override void Update() var sensorReading = SensorReader.ReadLibreSensor(SensorId); if (sensorReading.HasValue) { - Value = sensorReading.Value.ValueNow; + Value = sensorReading.Value.ValueNow.ToFormattedString(); + Unit = sensorReading.Value.Unit; + } + } + } + + public class PluginTreeItem(object id, string name) :TreeItem(id, name) + { + + } + + + public partial class PluginSensorItem(object id, string name, string sensorId) : SensorTreeItem(id, name) + { + public string SensorId { get; set; } = sensorId; + + public override void Update() + { + //Update sensor value + var sensorReading = SensorReader.ReadPluginSensor(SensorId); + if (sensorReading.HasValue) + { + Value = sensorReading.Value.ValueText ?? sensorReading.Value.ValueNow.ToFormattedString(); Unit = sensorReading.Value.Unit; } } diff --git a/InfoPanel/Views/Components/Sensors/HwInfoSensors.xaml.cs b/InfoPanel/Views/Components/Sensors/HwInfoSensors.xaml.cs index 9b57f2c..074efb9 100644 --- a/InfoPanel/Views/Components/Sensors/HwInfoSensors.xaml.cs +++ b/InfoPanel/Views/Components/Sensors/HwInfoSensors.xaml.cs @@ -143,7 +143,7 @@ private void ButtonReplace_Click(object sender, RoutedEventArgs e) { displayItem.Name = sensorItem.Name; displayItem.SensorName = sensorItem.Name; - displayItem.SensorType = SensorType.HwInfo; + displayItem.SensorType = Enums.SensorType.HwInfo; displayItem.Id = sensorItem.ParentId; displayItem.Instance = sensorItem.ParentInstance; displayItem.EntryId = sensorItem.SensorId; @@ -153,7 +153,7 @@ private void ButtonReplace_Click(object sender, RoutedEventArgs e) { chartDisplayItem.Name = sensorItem.Name; chartDisplayItem.SensorName = sensorItem.Name; - chartDisplayItem.SensorType = SensorType.HwInfo; + chartDisplayItem.SensorType = Enums.SensorType.HwInfo; chartDisplayItem.Id = sensorItem.ParentId; chartDisplayItem.Instance = sensorItem.ParentInstance; chartDisplayItem.EntryId = sensorItem.SensorId; @@ -162,7 +162,7 @@ private void ButtonReplace_Click(object sender, RoutedEventArgs e) { gaugeDisplayItem.Name = sensorItem.Name; gaugeDisplayItem.SensorName = sensorItem.Name; - gaugeDisplayItem.SensorType = SensorType.HwInfo; + gaugeDisplayItem.SensorType = Enums.SensorType.HwInfo; gaugeDisplayItem.Id = sensorItem.ParentId; gaugeDisplayItem.Instance = sensorItem.ParentInstance; gaugeDisplayItem.EntryId = sensorItem.SensorId; @@ -170,7 +170,7 @@ private void ButtonReplace_Click(object sender, RoutedEventArgs e) { sensorImageDisplayItem.Name = sensorItem.Name; sensorImageDisplayItem.SensorName = sensorItem.Name; - sensorImageDisplayItem.SensorType = SensorType.HwInfo; + sensorImageDisplayItem.SensorType = Enums.SensorType.HwInfo; sensorImageDisplayItem.Id = sensorItem.ParentId; sensorImageDisplayItem.Instance = sensorItem.ParentInstance; sensorImageDisplayItem.EntryId = sensorItem.SensorId; diff --git a/InfoPanel/Views/Components/Sensors/LibreSensors.xaml.cs b/InfoPanel/Views/Components/Sensors/LibreSensors.xaml.cs index 8b87611..b80c98a 100644 --- a/InfoPanel/Views/Components/Sensors/LibreSensors.xaml.cs +++ b/InfoPanel/Views/Components/Sensors/LibreSensors.xaml.cs @@ -173,7 +173,7 @@ private void ButtonReplace_Click(object sender, RoutedEventArgs e) { displayItem.Name = sensorItem.Name; displayItem.SensorName = sensorItem.Name; - displayItem.SensorType = Models.SensorType.Libre; + displayItem.SensorType = Enums.SensorType.Libre; displayItem.LibreSensorId = sensorItem.SensorId; displayItem.Unit = sensorItem.Unit; } @@ -181,21 +181,21 @@ private void ButtonReplace_Click(object sender, RoutedEventArgs e) { chartDisplayItem.Name = sensorItem.Name; chartDisplayItem.SensorName = sensorItem.Name; - chartDisplayItem.SensorType = Models.SensorType.Libre; + chartDisplayItem.SensorType = Enums.SensorType.Libre; chartDisplayItem.LibreSensorId = sensorItem.SensorId; } else if (SharedModel.Instance.SelectedItem is GaugeDisplayItem gaugeDisplayItem) { gaugeDisplayItem.Name = sensorItem.Name; gaugeDisplayItem.SensorName = sensorItem.Name; - gaugeDisplayItem.SensorType = Models.SensorType.Libre; + gaugeDisplayItem.SensorType = Enums.SensorType.Libre; gaugeDisplayItem.LibreSensorId = sensorItem.SensorId; } else if (SharedModel.Instance.SelectedItem is SensorImageDisplayItem sensorImageDisplayItem) { sensorImageDisplayItem.Name = sensorItem.Name; sensorImageDisplayItem.SensorName = sensorItem.Name; - sensorImageDisplayItem.SensorType = Models.SensorType.Libre; + sensorImageDisplayItem.SensorType = Enums.SensorType.Libre; sensorImageDisplayItem.LibreSensorId = sensorItem.SensorId; } } diff --git a/InfoPanel/Views/Components/Sensors/PluginSensors.xaml b/InfoPanel/Views/Components/Sensors/PluginSensors.xaml new file mode 100644 index 0000000..bd93744 --- /dev/null +++ b/InfoPanel/Views/Components/Sensors/PluginSensors.xaml @@ -0,0 +1,208 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sensor Items + + + + + + + + + + + + + + + + + + diff --git a/InfoPanel/Views/Components/Sensors/PluginSensors.xaml.cs b/InfoPanel/Views/Components/Sensors/PluginSensors.xaml.cs new file mode 100644 index 0000000..90198d0 --- /dev/null +++ b/InfoPanel/Views/Components/Sensors/PluginSensors.xaml.cs @@ -0,0 +1,260 @@ +using InfoPanel.Models; +using InfoPanel.Monitors; +using InfoPanel.ViewModels.Components; +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Threading; + +namespace InfoPanel.Views.Components +{ + /// + /// Interaction logic for HWiNFOSensors.xaml + /// + public partial class PluginSensors : System.Windows.Controls.UserControl + { + private PluginSensorsVM ViewModel { get; set; } + + private readonly DispatcherTimer UpdateTimer = new() { Interval = TimeSpan.FromSeconds(1) }; + + public PluginSensors() + { + ViewModel = new PluginSensorsVM(); + DataContext = ViewModel; + + InitializeComponent(); + + Loaded += LibreSensors_Loaded; + Unloaded += LibreSensors_Unloaded; + } + + private void LibreSensors_Loaded(object sender, RoutedEventArgs e) + { + if (UpdateTimer != null) + { + UpdateTimer.Tick += Timer_Tick; + + Timer_Tick(this, new EventArgs()); + UpdateTimer.Start(); + } + } + + private void LibreSensors_Unloaded(object sender, RoutedEventArgs e) + { + if (UpdateTimer != null) + { + UpdateTimer.Stop(); + UpdateTimer.Tick -= Timer_Tick; + } + } + private void Timer_Tick(object? sender, EventArgs e) + { + LoadSensorTree(); + UpdateSensorDetails(); + } + + private void LoadSensorTree() + { + //for (int parentIndex = ViewModel.Sensors.Count - 1; parentIndex >= 0; parentIndex--) + //{ + // var parent = ViewModel.Sensors[parentIndex]; + + // for (int typeIndex = parent.Children.Count - 1; typeIndex >= 0; typeIndex--) + // { + // var type = parent.Children[typeIndex]; + + // for (int i = type.Children.Count - 1; i >= 0; i--) + // { + // if (type.Children[i] is LibreSensorItem item) + // { + // if (!LibreMonitor.SENSORHASH.ContainsKey(item.SensorId)) + // { + // type.Children.RemoveAt(i); + // } + // } + // } + + // if (type.Children.Count == 0) + // { + // parent.Children.RemoveAt(typeIndex); + // } + // } + + // if (parent.Children.Count == 0) + // { + // ViewModel.Sensors.RemoveAt(parentIndex); + // } + //} + + foreach (PluginMonitor.PluginReading reading in PluginMonitor.GetOrderedList()) + { + //construct plugin + var parent = ViewModel.FindParentSensorItem(reading.PluginId); + if (parent == null) + { + parent = new PluginTreeItem(reading.PluginId, reading.PluginName); + ViewModel.Sensors.Add(parent); + } + + //construct container + var container = parent.FindChild(reading.ContainerId); + + if (container == null) + { + container = new PluginTreeItem(reading.ContainerId, reading.ContainerName); + parent.Children.Add(container); + } + + //construct actual sensor + var child = container.FindChild(reading.Id); + if (child == null) + { + child = new PluginSensorItem(reading.Id, reading.Name, reading.Id); + container.Children.Add(child); + } + } + } + + private void TreeViewInfo_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs e) + { + if (e.NewValue is PluginSensorItem sensorItem) + { + ViewModel.SelectedItem = sensorItem; + sensorItem.Update(); + } + else + { + ViewModel.SelectedItem = null; + } + } + + private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e) + { + var scrollViewer = (ScrollViewer)sender; + scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - e.Delta); + e.Handled = true; + } + + private void UpdateSensorDetails() + { + ViewModel.SelectedItem?.Update(); + } + + + private void ButtonSelect_Click(object sender, RoutedEventArgs e) + { + if (ViewModel.SelectedItem is PluginSensorItem sensorItem) + { + var item = new SensorDisplayItem(sensorItem.Name) + { + SensorType = Enums.SensorType.Plugin, + PluginSensorId = sensorItem.SensorId, + Font = SharedModel.Instance.SelectedProfile!.Font, + FontSize = SharedModel.Instance.SelectedProfile!.FontSize, + Color = SharedModel.Instance.SelectedProfile!.Color, + Unit = sensorItem.Unit, + }; + + SharedModel.Instance.AddDisplayItem(item); + SharedModel.Instance.SelectedItem = item; + } + } + + private void ButtonReplace_Click(object sender, RoutedEventArgs e) + { + if (ViewModel.SelectedItem is PluginSensorItem sensorItem) + { + if (SharedModel.Instance.SelectedItem is SensorDisplayItem displayItem) + { + displayItem.Name = sensorItem.Name; + displayItem.SensorName = sensorItem.Name; + displayItem.SensorType = Enums.SensorType.Plugin; + displayItem.PluginSensorId = sensorItem.SensorId; + displayItem.Unit = sensorItem.Unit; + } + else if (SharedModel.Instance.SelectedItem is ChartDisplayItem chartDisplayItem) + { + chartDisplayItem.Name = sensorItem.Name; + chartDisplayItem.SensorName = sensorItem.Name; + chartDisplayItem.SensorType = Enums.SensorType.Plugin; + chartDisplayItem.PluginSensorId = sensorItem.SensorId; + } + else if (SharedModel.Instance.SelectedItem is GaugeDisplayItem gaugeDisplayItem) + { + gaugeDisplayItem.Name = sensorItem.Name; + gaugeDisplayItem.SensorName = sensorItem.Name; + gaugeDisplayItem.SensorType = Enums.SensorType.Plugin; + gaugeDisplayItem.PluginSensorId = sensorItem.SensorId; + } + else if (SharedModel.Instance.SelectedItem is SensorImageDisplayItem sensorImageDisplayItem) + { + sensorImageDisplayItem.Name = sensorItem.Name; + sensorImageDisplayItem.SensorName = sensorItem.Name; + sensorImageDisplayItem.SensorType = Enums.SensorType.Plugin; + sensorImageDisplayItem.PluginSensorId = sensorItem.SensorId; + } + } + } + + private void ButtonAddGraph_Click(object sender, RoutedEventArgs e) + { + if (ViewModel.SelectedItem is PluginSensorItem sensorItem) + { + var item = new GraphDisplayItem(sensorItem.Name, GraphDisplayItem.GraphType.LINE); + item.PluginSensorId = sensorItem.SensorId; + item.SensorType = Enums.SensorType.Plugin; + SharedModel.Instance.AddDisplayItem(item); + SharedModel.Instance.SelectedItem = item; + } + } + + private void ButtonAddBar_Click(object sender, RoutedEventArgs e) + { + if (ViewModel.SelectedItem is PluginSensorItem sensorItem) + { + var item = new BarDisplayItem(sensorItem.Name); + item.PluginSensorId = sensorItem.SensorId; + item.SensorType = Enums.SensorType.Plugin; + SharedModel.Instance.AddDisplayItem(item); + SharedModel.Instance.SelectedItem = item; + } + } + + private void ButtonAddDonut_Click(object sender, RoutedEventArgs e) + { + if (ViewModel.SelectedItem is PluginSensorItem sensorItem) + { + var item = new DonutDisplayItem(sensorItem.Name); + item.PluginSensorId = sensorItem.SensorId; + item.SensorType = Enums.SensorType.Plugin; + SharedModel.Instance.AddDisplayItem(item); + SharedModel.Instance.SelectedItem = item; + } + } + + private void ButtonAddCustom_Click(object sender, RoutedEventArgs e) + { + if (ViewModel.SelectedItem is PluginSensorItem sensorItem) + { + var item = new GaugeDisplayItem(sensorItem.Name); + item.PluginSensorId = sensorItem.SensorId; + item.SensorType = Enums.SensorType.Plugin; + SharedModel.Instance.AddDisplayItem(item); + SharedModel.Instance.SelectedItem = item; + } + } + + private void ButtonAddSensorImage_Click(object sender, RoutedEventArgs e) + { + if (ViewModel.SelectedItem is PluginSensorItem sensorItem && SharedModel.Instance.SelectedProfile is Profile selectedProfile) + { + var item = new SensorImageDisplayItem(sensorItem.Name, selectedProfile.Guid); + item.PluginSensorId = sensorItem.SensorId; + item.SensorType = Enums.SensorType.Plugin; + SharedModel.Instance.AddDisplayItem(item); + SharedModel.Instance.SelectedItem = item; + } + } + } +} diff --git a/InfoPanel/Views/Pages/DesignPage.xaml b/InfoPanel/Views/Pages/DesignPage.xaml index 7041c51..2b1775e 100644 --- a/InfoPanel/Views/Pages/DesignPage.xaml +++ b/InfoPanel/Views/Pages/DesignPage.xaml @@ -30,7 +30,7 @@ Margin="0,50,10,0" CornerRadius="8,8,8,8"> - + - + + + + + + + Plugins + + + + + + + + + From b8316c66bb9f6f5f8fb445bc96800a87a2d2a889 Mon Sep 17 00:00:00 2001 From: Habib Rehman <13956188+habibrehmansg@users.noreply.github.com> Date: Fri, 20 Dec 2024 23:20:31 +0800 Subject: [PATCH 05/10] Support multiple volume devices --- InfoPanel.Extras/VolumePlugin.cs | 122 +++++++++++++++++++++++++-- InfoPanel.Plugins/PluginContainer.cs | 18 +++- InfoPanel/Monitors/PluginMonitor.cs | 2 +- 3 files changed, 129 insertions(+), 13 deletions(-) diff --git a/InfoPanel.Extras/VolumePlugin.cs b/InfoPanel.Extras/VolumePlugin.cs index 5c713a0..23dbedc 100644 --- a/InfoPanel.Extras/VolumePlugin.cs +++ b/InfoPanel.Extras/VolumePlugin.cs @@ -1,12 +1,12 @@ using InfoPanel.Plugins; using NAudio.CoreAudioApi; +using System.ComponentModel; namespace InfoPanel.Extras { public class VolumePlugin : BasePlugin { - private MMDeviceEnumerator? _deviceEnumerator; - private readonly PluginSensor _volumeSensor = new("Master Volume", 0, "%"); + private readonly List _containers = []; public VolumePlugin() : base("Volume Plugin") { @@ -16,19 +16,39 @@ public VolumePlugin() : base("Volume Plugin") public override void Initialize() { - _deviceEnumerator = new MMDeviceEnumerator(); + //add default first + using MMDeviceEnumerator deviceEnumerator = new(); + using var defaultDevice = deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia); + + PluginContainer container = new("Default"); + container.Entries.Add(new PluginText("device_friendly_name", "Device Name", defaultDevice.DeviceFriendlyName)); + container.Entries.Add(new PluginText("friendly_name", "Name", defaultDevice.FriendlyName)); + container.Entries.Add(new PluginText("short_name", "Short Name", defaultDevice.FriendlyName.Replace($"({defaultDevice.DeviceFriendlyName})", ""))); + container.Entries.Add(new PluginSensor("volume", "Volume", (float)Math.Round(defaultDevice.AudioEndpointVolume.MasterVolumeLevelScalar * 100), "%")); + container.Entries.Add(new PluginText("mute", "Mute", defaultDevice.AudioEndpointVolume.Mute.ToString())); + _containers.Add(container); + + var devices = deviceEnumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active); + foreach (var device in devices) + { + container = new(device.ID, device.FriendlyName); + container.Entries.Add(new PluginText("device_friendly_name", "Device Name", device.DeviceFriendlyName)); + container.Entries.Add(new PluginText("friendly_name", "Name", device.FriendlyName)); + container.Entries.Add(new PluginText("short_name", "Short Name", device.FriendlyName.Replace($"({defaultDevice.DeviceFriendlyName})", ""))); + container.Entries.Add(new PluginSensor("volume", "Volume", (float)Math.Round(device.AudioEndpointVolume.MasterVolumeLevelScalar * 100), "%")); + container.Entries.Add(new PluginText("mute", "Mute", device.AudioEndpointVolume.Mute.ToString())); + _containers.Add(container); + device.Dispose(); + } } public override void Close() { - _deviceEnumerator?.Dispose(); } public override void Load(List containers) { - var container = new PluginContainer("Default"); - container.Entries.Add(_volumeSensor); - containers.Add(container); + containers.AddRange(_containers); } public override Task UpdateAsync(CancellationToken cancellationToken) @@ -39,8 +59,92 @@ public override Task UpdateAsync(CancellationToken cancellationToken) public override void Update() { - using var defaultDevice = _deviceEnumerator?.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia); - _volumeSensor.Value = (float)Math.Round((defaultDevice?.AudioEndpointVolume.MasterVolumeLevelScalar ?? 0) * 100); + using MMDeviceEnumerator deviceEnumerator = new(); + + foreach(var container in _containers) + { + MMDevice? device = null; + + try + { + if (container.Name == "Default") + { + device = deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia); + } + else + { + device = deviceEnumerator.GetDevice(container.Id); + } + + if (device != null) + { + foreach (var entry in container.Entries) + { + switch (entry.Id) + { + case "device_friendly_name": + { + if (entry is PluginText text) + { + text.Value = device.DeviceFriendlyName; + } + } + break; + case "friendly_name": + { + if (entry is PluginText text) + { + text.Value = device.FriendlyName; + } + } + break; + case "short_name": + { + if (entry is PluginText text) + { + text.Value = device.FriendlyName.Replace($"({device.DeviceFriendlyName})", ""); + } + } + break; + case "volume": + { + if (entry is PluginSensor sensor) + { + sensor.Value = (float)Math.Round(device.AudioEndpointVolume.MasterVolumeLevelScalar * 100); + } + } + break; + case "mute": + { + if (entry is PluginText text) + { + text.Value = device.AudioEndpointVolume.Mute.ToString(); + } + } + break; + } + } + } + } + finally + { + device?.Dispose(); + } + } + + //var devices = deviceEnumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active); + //foreach (var device in devices) + //{ + // container = new(device.DeviceFriendlyName); + // container.Entries.Add(new PluginText("name", "Name", device.DeviceFriendlyName)); + // container.Entries.Add(new PluginSensor("volume", "Volume", (float)Math.Round(device.AudioEndpointVolume.MasterVolumeLevelScalar * 100), "%")); + // container.Entries.Add(new PluginText("mute", "Mute", device.AudioEndpointVolume.Mute.ToString())); + // _containers.Add(container); + // device.Dispose(); + //} + + //using var defaultDevice = _deviceEnumerator?.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia); + //_volumeSensor.Value = (float)Math.Round((defaultDevice?.AudioEndpointVolume.MasterVolumeLevelScalar ?? 0) * 100); } } } diff --git a/InfoPanel.Plugins/PluginContainer.cs b/InfoPanel.Plugins/PluginContainer.cs index 16c9177..c1eb710 100644 --- a/InfoPanel.Plugins/PluginContainer.cs +++ b/InfoPanel.Plugins/PluginContainer.cs @@ -1,9 +1,21 @@ namespace InfoPanel.Plugins { - public class PluginContainer(string name) : IPluginContainer + public class PluginContainer : IPluginContainer { - public string Id { get; } = IdUtil.Encode(name); - public string Name { get; } = name; + public PluginContainer(string id, string name) + { + Id = id; + Name = name; + } + + public PluginContainer(string name) + { + Id = IdUtil.Encode(name); + Name = name; + } + + public string Id { get; } + public string Name { get; } public List Entries { get; } = []; } } diff --git a/InfoPanel/Monitors/PluginMonitor.cs b/InfoPanel/Monitors/PluginMonitor.cs index bb41615..8087664 100644 --- a/InfoPanel/Monitors/PluginMonitor.cs +++ b/InfoPanel/Monitors/PluginMonitor.cs @@ -71,7 +71,7 @@ protected override async Task DoWorkAsync(CancellationToken token) await load(); stopwatch.Stop(); - Trace.WriteLine($"Computer open: {stopwatch.ElapsedMilliseconds}ms"); + Trace.WriteLine($"Plugins loaded: {stopwatch.ElapsedMilliseconds}ms"); try { From 25d61569c2a83722138daa7dae22a4a12323090b Mon Sep 17 00:00:00 2001 From: Habib Rehman <13956188+habibrehmansg@users.noreply.github.com> Date: Sat, 21 Dec 2024 13:35:53 +0800 Subject: [PATCH 06/10] Cleanup plugin loading locations --- InfoPanel.Extras/DriveInfoPlugin.cs | 2 +- InfoPanel.Extras/InfoPanel.Extras.csproj | 5 - InfoPanel.Extras/IpifyPlugin.cs | 2 +- InfoPanel.Extras/VolumePlugin.cs | 58 ++------- InfoPanel.Extras/WeatherPlugin.cs | 2 +- InfoPanel.Plugins.Loader/PluginLoader.cs | 35 +---- InfoPanel.Plugins.Loader/PluginWrapper.cs | 5 + InfoPanel/App.xaml.cs | 9 -- InfoPanel/InfoPanel.csproj | 50 ++++--- InfoPanel/Monitors/PluginMonitor.cs | 151 +++++++++++----------- InfoPanel/Resources/Images/logo.ico | Bin 0 -> 15086 bytes InfoPanel/Utils/FpsCounter.cs | 4 - InfoPanel/Views/Pages/DesignPage.xaml | 2 +- 13 files changed, 128 insertions(+), 197 deletions(-) create mode 100644 InfoPanel/Resources/Images/logo.ico diff --git a/InfoPanel.Extras/DriveInfoPlugin.cs b/InfoPanel.Extras/DriveInfoPlugin.cs index 093be21..f19bb26 100644 --- a/InfoPanel.Extras/DriveInfoPlugin.cs +++ b/InfoPanel.Extras/DriveInfoPlugin.cs @@ -8,7 +8,7 @@ public class DriveInfoPlugin : BasePlugin private readonly List _containers = []; - public DriveInfoPlugin() : base("Drive Info Plugin") + public DriveInfoPlugin() : base("drive-info-plugin", "Drive Info") { } diff --git a/InfoPanel.Extras/InfoPanel.Extras.csproj b/InfoPanel.Extras/InfoPanel.Extras.csproj index 3916edc..2fa6b05 100644 --- a/InfoPanel.Extras/InfoPanel.Extras.csproj +++ b/InfoPanel.Extras/InfoPanel.Extras.csproj @@ -19,9 +19,4 @@ runtime - - - - - diff --git a/InfoPanel.Extras/IpifyPlugin.cs b/InfoPanel.Extras/IpifyPlugin.cs index da9cbd2..36e6cbd 100644 --- a/InfoPanel.Extras/IpifyPlugin.cs +++ b/InfoPanel.Extras/IpifyPlugin.cs @@ -12,7 +12,7 @@ public class IpifyPlugin : BasePlugin public override TimeSpan UpdateInterval => TimeSpan.FromMinutes(5); - public IpifyPlugin() : base("Ipify Plugin") + public IpifyPlugin() : base("ipify-plugin", "Public IP - Ipify") { } diff --git a/InfoPanel.Extras/VolumePlugin.cs b/InfoPanel.Extras/VolumePlugin.cs index 23dbedc..ddbee04 100644 --- a/InfoPanel.Extras/VolumePlugin.cs +++ b/InfoPanel.Extras/VolumePlugin.cs @@ -8,7 +8,9 @@ public class VolumePlugin : BasePlugin { private readonly List _containers = []; - public VolumePlugin() : base("Volume Plugin") + private MMDeviceEnumerator? _deviceEnumerator; + + public VolumePlugin() : base("volume-plugin","Volume Info") { } @@ -17,18 +19,15 @@ public VolumePlugin() : base("Volume Plugin") public override void Initialize() { //add default first - using MMDeviceEnumerator deviceEnumerator = new(); - using var defaultDevice = deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia); + _deviceEnumerator = new(); + using var defaultDevice = _deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia); PluginContainer container = new("Default"); - container.Entries.Add(new PluginText("device_friendly_name", "Device Name", defaultDevice.DeviceFriendlyName)); - container.Entries.Add(new PluginText("friendly_name", "Name", defaultDevice.FriendlyName)); - container.Entries.Add(new PluginText("short_name", "Short Name", defaultDevice.FriendlyName.Replace($"({defaultDevice.DeviceFriendlyName})", ""))); container.Entries.Add(new PluginSensor("volume", "Volume", (float)Math.Round(defaultDevice.AudioEndpointVolume.MasterVolumeLevelScalar * 100), "%")); container.Entries.Add(new PluginText("mute", "Mute", defaultDevice.AudioEndpointVolume.Mute.ToString())); _containers.Add(container); - var devices = deviceEnumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active); + var devices = _deviceEnumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active); foreach (var device in devices) { container = new(device.ID, device.FriendlyName); @@ -44,6 +43,7 @@ public override void Initialize() public override void Close() { + _deviceEnumerator?.Dispose(); } public override void Load(List containers) @@ -59,8 +59,6 @@ public override Task UpdateAsync(CancellationToken cancellationToken) public override void Update() { - using MMDeviceEnumerator deviceEnumerator = new(); - foreach(var container in _containers) { MMDevice? device = null; @@ -69,11 +67,11 @@ public override void Update() { if (container.Name == "Default") { - device = deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia); + device = _deviceEnumerator?.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia); } else { - device = deviceEnumerator.GetDevice(container.Id); + device = _deviceEnumerator?.GetDevice(container.Id); } if (device != null) @@ -82,30 +80,6 @@ public override void Update() { switch (entry.Id) { - case "device_friendly_name": - { - if (entry is PluginText text) - { - text.Value = device.DeviceFriendlyName; - } - } - break; - case "friendly_name": - { - if (entry is PluginText text) - { - text.Value = device.FriendlyName; - } - } - break; - case "short_name": - { - if (entry is PluginText text) - { - text.Value = device.FriendlyName.Replace($"({device.DeviceFriendlyName})", ""); - } - } - break; case "volume": { if (entry is PluginSensor sensor) @@ -131,20 +105,6 @@ public override void Update() device?.Dispose(); } } - - //var devices = deviceEnumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active); - //foreach (var device in devices) - //{ - // container = new(device.DeviceFriendlyName); - // container.Entries.Add(new PluginText("name", "Name", device.DeviceFriendlyName)); - // container.Entries.Add(new PluginSensor("volume", "Volume", (float)Math.Round(device.AudioEndpointVolume.MasterVolumeLevelScalar * 100), "%")); - // container.Entries.Add(new PluginText("mute", "Mute", device.AudioEndpointVolume.Mute.ToString())); - // _containers.Add(container); - // device.Dispose(); - //} - - //using var defaultDevice = _deviceEnumerator?.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia); - //_volumeSensor.Value = (float)Math.Round((defaultDevice?.AudioEndpointVolume.MasterVolumeLevelScalar ?? 0) * 100); } } } diff --git a/InfoPanel.Extras/WeatherPlugin.cs b/InfoPanel.Extras/WeatherPlugin.cs index 27bf35c..5a2e25a 100644 --- a/InfoPanel.Extras/WeatherPlugin.cs +++ b/InfoPanel.Extras/WeatherPlugin.cs @@ -38,7 +38,7 @@ public class WeatherPlugin : BasePlugin private readonly PluginSensor _rain = new("rain", "Rain", 0, "mm/h"); private readonly PluginSensor _snow = new("snow", "Snow", 0, "mm/h"); - public WeatherPlugin() : base("Weather Plugin") + public WeatherPlugin() : base("weather-plugin","Weather Info - OpenWeatherMap") { } diff --git a/InfoPanel.Plugins.Loader/PluginLoader.cs b/InfoPanel.Plugins.Loader/PluginLoader.cs index c8b447e..802a83f 100644 --- a/InfoPanel.Plugins.Loader/PluginLoader.cs +++ b/InfoPanel.Plugins.Loader/PluginLoader.cs @@ -1,40 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; +using System.Reflection; namespace InfoPanel.Plugins.Loader { public class PluginLoader { - public void test(string folder) - { - //var plugins= Directory.GetFiles(folder, "InfoPanel.*.dll"); - //IEnumerable commands = plugins.SelectMany(pluginPath => - //{ - // Assembly pluginAssembly = LoadPlugin(pluginPath); - // return CreateCommands(pluginAssembly); - //}).ToList(); - - //foreach (var command in commands) - //{ - // Trace.WriteLine(command); - // var panelDatas = command.GetData(); - // foreach(var panelData in panelDatas) - // { - // Trace.WriteLine(panelData.CollectionName); - // foreach(var item in panelData.EntryList) - // { - // Trace.WriteLine($"{item.Name}: {item.Value} {item.Unit}"); - // } - // } - //} - } - public IEnumerable InitializePlugin(string pluginPath) { Assembly pluginAssembly = LoadPlugin(pluginPath); @@ -72,7 +41,5 @@ static IEnumerable CreateCommands(Assembly assembly) $"Available types: {availableTypes}"); } } - - } } diff --git a/InfoPanel.Plugins.Loader/PluginWrapper.cs b/InfoPanel.Plugins.Loader/PluginWrapper.cs index 983acaf..6468ac4 100644 --- a/InfoPanel.Plugins.Loader/PluginWrapper.cs +++ b/InfoPanel.Plugins.Loader/PluginWrapper.cs @@ -126,6 +126,11 @@ private void DisposeResources() _task?.Dispose(); _cts = null; _task = null; + + try + { + Plugin.Close(); + }catch { } } } } diff --git a/InfoPanel/App.xaml.cs b/InfoPanel/App.xaml.cs index b155946..106b47d 100644 --- a/InfoPanel/App.xaml.cs +++ b/InfoPanel/App.xaml.cs @@ -203,9 +203,6 @@ protected override async void OnStartup(StartupEventArgs e) Exit += App_Exit; await StartPanels(); - - - testPlugin(); } private void OnSessionEnding(object sender, SessionEndingEventArgs e) @@ -335,11 +332,5 @@ private void DisplayWindow_Closed(object? sender, EventArgs e) DisplayWindows.Remove(displayWindow.Profile.Guid); } } - - public void testPlugin() - { - var pluginLoader = new PluginLoader(); - pluginLoader.test("plugins"); - } } } diff --git a/InfoPanel/InfoPanel.csproj b/InfoPanel/InfoPanel.csproj index 0f48d1f..99b9d73 100644 --- a/InfoPanel/InfoPanel.csproj +++ b/InfoPanel/InfoPanel.csproj @@ -14,6 +14,36 @@ False en Resources\app.manifest + ..\InfoPanel.Extras\bin\$(Platform)\$(Configuration)\$(TargetFramework) + bin\$(Platform)\$(Configuration)\$(TargetFramework)\plugins + + + + + + + + + + + + + + + + + + + + + + + + full + + + + none @@ -24,28 +54,16 @@ Lib\d2dwinform.dll - Lib\LibreHardwareMonitorLib.dll + Lib\LibreHardwareMonitorLib.dll - Lib\TuringSmartScreenLib.dll + Lib\TuringSmartScreenLib.dll - Lib\TuringSmartScreenLib.Helpers.GDI.dll + Lib\TuringSmartScreenLib.Helpers.GDI.dll - - - - - - full - - - - full - - @@ -97,6 +115,7 @@ + @@ -143,6 +162,7 @@ + diff --git a/InfoPanel/Monitors/PluginMonitor.cs b/InfoPanel/Monitors/PluginMonitor.cs index 8087664..196dbba 100644 --- a/InfoPanel/Monitors/PluginMonitor.cs +++ b/InfoPanel/Monitors/PluginMonitor.cs @@ -11,119 +11,116 @@ namespace InfoPanel.Monitors { - internal class PluginMonitor : BackgroundTask + internal class PluginMonitor: BackgroundTask { private static readonly Lazy _instance = new(() => new PluginMonitor()); public static PluginMonitor Instance => _instance.Value; - private static readonly ConcurrentDictionary HEADER_DICT = new(); public static readonly ConcurrentDictionary SENSORHASH = new(); - private PluginMonitor() { } - + private readonly Dictionary _loadedPlugins = []; + private PluginMonitor() { } - Dictionary loadedPlugins = []; - - private async Task load() + protected override async Task DoWorkAsync(CancellationToken token) { - PluginLoader pluginLoader = new(); - - var currentDirectory = Directory.GetCurrentDirectory(); - var pluginPath = Path.Combine(currentDirectory, "..\\..\\..\\..\\..\\InfoPanel.Extras\\bin\\Debug\\net8.0-windows", "InfoPanel.Extras.dll"); - - var plugins = pluginLoader.InitializePlugin(pluginPath); - + await Task.Delay(300, token); - foreach (var plugin in plugins) + try { - PluginWrapper pluginWrapper = new(plugin); - if (loadedPlugins.TryAdd(pluginWrapper.Name, pluginWrapper)) + var stopwatch = Stopwatch.StartNew(); + await LoadPluginsAsync(); + stopwatch.Stop(); + Trace.WriteLine($"Plugins loaded: {stopwatch.ElapsedMilliseconds}ms"); + + while (!token.IsCancellationRequested) { - try - { - await pluginWrapper.Initialize(); - Console.WriteLine($"Plugin {pluginWrapper.Name} loaded successfully"); - } - catch (Exception ex) + stopwatch.Restart(); + foreach (var plugin in _loadedPlugins.Values) { - Console.WriteLine($"Plugin {pluginWrapper.Name} failed to load: {ex.Message}"); + try + { + plugin.Update(); + } + catch { } } + stopwatch.Stop(); + //Trace.WriteLine($"Plugins updated: {stopwatch.ElapsedMilliseconds}ms"); + await Task.Delay(100, token); } - else + } + catch (TaskCanceledException) + { + Trace.WriteLine("Task cancelled"); + } + catch (Exception ex) + { + Trace.WriteLine($"Exception during work: {ex.Message}"); + } + finally + { + foreach (var loadedPluginWrapper in _loadedPlugins.Values) { - Console.WriteLine($"Plugin {pluginWrapper.Name} already loaded or duplicate plugin/name"); + await loadedPluginWrapper.StopAsync(); } - //break; + _loadedPlugins.Clear(); } - } - protected override async Task DoWorkAsync(CancellationToken token) + private async Task LoadPluginsAsync() { - await Task.Delay(300, token); - try - { - var stopwatch = new Stopwatch(); - stopwatch.Start(); + PluginLoader pluginLoader = new(); - await load(); + var pluginDirectory = Path.Combine(Directory.GetCurrentDirectory(), "plugins"); - stopwatch.Stop(); - Trace.WriteLine($"Plugins loaded: {stopwatch.ElapsedMilliseconds}ms"); - - try + foreach (var directory in Directory.GetDirectories(pluginDirectory)) + { + foreach (var pluginFile in Directory.GetFiles(directory, "InfoPanel.*.dll")) { + var plugins = pluginLoader.InitializePlugin(pluginFile); - while (!token.IsCancellationRequested) + foreach (var plugin in plugins) { - int indexOrder = 0; - foreach (var wrapper in loadedPlugins.Values) + PluginWrapper wrapper = new(plugin); + if (_loadedPlugins.TryAdd(wrapper.Name, wrapper)) { - wrapper.Update(); - - foreach (var container in wrapper.PluginContainers) + try { - foreach (var entry in container.Entries) + await wrapper.Initialize(); + Console.WriteLine($"Plugin {wrapper.Name} loaded successfully"); + + int indexOrder = 0; + foreach (var container in wrapper.PluginContainers) { - var id = $"/{wrapper.Id}/{container.Id}/{entry.Id}"; - SENSORHASH[id] = new() + foreach (var entry in container.Entries) { - Id = id, - Name = entry.Name, - ContainerId = container.Id, - ContainerName = container.Name, - PluginId = wrapper.Id, - PluginName = wrapper.Name, - Data = entry, - IndexOrder = indexOrder++ - }; + var id = $"/{wrapper.Id}/{container.Id}/{entry.Id}"; + SENSORHASH[id] = new() + { + Id = id, + Name = entry.Name, + ContainerId = container.Id, + ContainerName = container.Name, + PluginId = wrapper.Id, + PluginName = wrapper.Name, + Data = entry, + IndexOrder = indexOrder++ + }; + } } } + catch (Exception ex) + { + Console.WriteLine($"Plugin {wrapper.Name} failed to load: {ex.Message}"); + } + } + else + { + Console.WriteLine($"Plugin {wrapper.Name} already loaded or duplicate plugin/name"); } - - await Task.Delay(100, token); - } } - catch (TaskCanceledException) - { - Trace.WriteLine("Task cancelled"); - } - catch (Exception ex) - { - Trace.WriteLine($"Exception during work: {ex.Message}"); - } - finally - { - HEADER_DICT.Clear(); - SENSORHASH.Clear(); - } - } - catch (Exception e) - { - Trace.WriteLine($"LibreMonitor: Init error: {e.Message}"); } } diff --git a/InfoPanel/Resources/Images/logo.ico b/InfoPanel/Resources/Images/logo.ico new file mode 100644 index 0000000000000000000000000000000000000000..56a88ff19430a4ec8b6ef11aef97f901d8153f16 GIT binary patch literal 15086 zcmds82UL_t*B+wL6pbmS81t#S!7gG!tTcPqhz+C)NSCrKZRx#9*=1qrU9g}LMFmAH zs4+rRR8$njU1LW<#4gW2vj`Hq`AoiZ{ypdJy!*cM&VBBkJMG@PgeZ^>4t6X1lf!w1>%`R?5@!>!AsLChCg1m@|XV5 z=-mZ_J|WQT{ROmDeuSUdSnNzUmQ`)zdtKP!+4I6S2XeaDi^RC7f1KnqQ76@Z@{u4* z)u;1^eTSi4K9avXuoHpvk`dS>)WyP)|AF3kE;dE#Kb4jE9jV(Ju2X*?<>QQy>74Aa z8Pzs(`a|XGP8iskVDP6cG=RPzWTt^T$HL*dcr<=iAQ=7ar-(3{in@}B>c)Ly&h`u) z#mz~JYiADX4pzVQT~@NT4AuLgBHsmprjs$f-}mx++Dg4~r68>K{_c3iimjH42RE*$ zoj>e{zwmB-pRcQq)Br=+%^QuXUxN{1JRM7vIk>bYy!K9EykhMkH^tMtools6{sbm( zd;SAHCWvy^!IRrXh&P(sN$}r-q&X_asCmf9O zM6nMK+5Frrt78I=bv?f8Z?x?VKp$ZrMDc<(@Td$cN$Zg@rub@9-AZGpC3v2E9 zwFS=JiVsg`ahfWVt3~z;Fy_0@Ux}f$odL8zhJVo+mg6TN-Q1}<-NJ>_c%kIuhsQE? z?j1@$av;IusioEk4D0&wU;1Jo6((d!bLe~nT>+T`1yNn4LrNp z>@Fai1%l4`PVU-i=8nfD%k7URnDDe?4D36LjT~q#ItjGEtjk= zdxyQt=9xZ}G%2z9N&cY|&)epimWr?b=IN;sho$|Aqt2kNc1!x1uUxEH>!8#3qKnaB zb1#cgT|LZ(5$6??U;XlbdDqowC~;jryrZ+hAT_?;fDO+21MayQ4~B=S61**j-wRx+ zx-o*Ut|k)9=oszx;V}5DP$B--lj$6)+|SE&XpE<+(oG)=Wq6wphvmG!u%UBei2YbZ zxvE1b(7Gw{n;MfIHoM=pBpZ?)vEXk@9cZIY!fZ#7P}|YngKbnCf~-fJ4Oyu|=j_pN z(Nn_gq1~a}wS%1IkN5%pmSd0>JOkOQW@AnC{IeV5mpE)qG3;KLq(e3&n7lI%?-h$k zw9|MJ<)rp$6mQ&uNZ#18Xs2;cV%^jbY@-T&x^9f>-bp_0TgNY(Pe*m{4D*@&ksdS~ zTT}Eau;5zFC6`OdUmT$BtndxqC#36LmqU*-Q4k9;=(Xnpod2#OQO-Vjl zh!beSe!)Pf_i9;t*qp<7vr^@oPO$q$3DXC3m;au~`4R!<6Ht<2fu`>)O)q|8TqhHN(6vsB6YrrJn&_={H9c?&GJ~hlwnkz0knT|Wyk+fb zmARGw*dIQ_M&S|^rx?J?U<@Yw*hOA<$>`o#9k>Xm3!QOcrw1+v~?lZ|j@ zhZD}0xFXqOu6&(kYmUm-o#8NdILgv(aBW{OZtM$bzOg%`s%~Gj!R=#(9qJBcl0@Gr zB++Mb&t$(z)j46)ksdgW>T?jr_4rIa*S6}qJ>S-A_G~NEp(iDh9X^X>h0WyTM$VFM6wk-n=XIE4f@X>fY0{2ctVP}GQ8~^t87!HB3n5 zY))J#ElStHk}*HMajj}?la)+<)o(w+jwEwDIU5I@PsHA|m2&=!Uwhs4r~P5BIRtlh z#o+EPq4ds95r-1lo~cK+WiI3FTCFeLpT8W-)Lvfi+w*+YwPx=x5pF#lR}MvCZ#Ew@ z2mF8l<0;VU{gqr6hJ)>aEHssHXNM4Xibc};?II2(vUj}+*|Tmr=eJE}(j(igVW`P{ zFZsvy=z>IFT^MQ&#)1*O>3&ERi&cNd?LU&}z9rcSsx6q*cw~5eD7%QzS z5OL}@i%9uaD^gx)#i`t3D?PK9NBh6;d+}GNwsql{1PvEgBQwMlQxw0$g5iD8@JAX@ zy%vuuGO@~JHu`m>`d3rJt%6w8Z4^nbtrv1Ak(0X|$%zsN&e<})RC?G0rc?XBC;zW- z{%A7LR17q1M`0o#OGYW8zB~gsxfVFO2HDOA=tuWZD<&x6`Z^)5KJnV zfE%gV@5;GU?kT-q836O?gWiun?c?(&vMB#jpnfOzm@*hR{mr+dRl>h!bWZ3D+`5TWOjB_cGIFk?|JrNhqIT0U0u2%+; zYsUgPx2r;>P3NLuIZNr?`9B+jN}qS2_NPpF2cVelqnD~s{<|{KT($;JOLCBEwM@?6 zP-PHK#)ji)OsKRxGL%yh6-sVbMUY!3!#Q`)MoAykieWWd`MvnJ>V%Ci)$X5R&fs2H zIFyUq+tToKXD;q<$wZ==j(iWTKYSp{BSP?7NRYHFIEX`uG@gzjcTPof9@NB2A6`tL z{D=K5{tQR!I9L7pQ^XiAKy7+78rG*lY`jE1{&bZGU~hmwN__mJMc%%gVsBs4bSjpT zi1X-Ny!2shGOT6||J(dqbMv|#hb)p%+CneH&VL!QZ`*MeSC)XL=uSl(Wd^D^1X=N@>Z3u^8(L|_z^Qj!i zi*3K{H{&~^TW1)I7zVNa3glY5BhB2onVxy7;*1>)#3oK1;!K?6d!Snfvk3|aX*`h3 zX*!S^djCNB)yKc(;NHG0lt#M1cpyyearX*8-UV;f?s)cUDvOpCqr~mnE9=7f_y`N+@4HC-0Z|z3#>#NfbGg5=E*~qq|p1RykA%!_WR66@~-h zA;_|^g8s;%bf5FF{7k-d=wL+a8Y9=njq#b+NilaIX;!?h5|fqYLIdlIk;^PEM(SCcM;ol{8m(_jyo}#ZW<2N( z)OH4TcVI^#JRwA*o{(;}gmm_yu7eLjtS#0iYn$Fo>ELf|7xfj`Ed2Qf^<~^}TsFYNZTYZodCU3|U)!l~Y~z3NFf~p> zob;$4*g(0nqwat?_DlQivR~Twk)v*3IO+9=v*Ex;?j}P^eJoYx20N;K61Z|Ki4L>< zUqF3{r@M&?@iZU*k&B_yNWR{{EWTdM7^c3;l$D#6EM82l(+7Xu}@ z87U*gVGP1};}GFA0g=w?h;h@rEcTilkQg|#m#=9zk`lV`--1m-9On{$i(w?dO0}E6 z`EXO;6+~F1c~ot zBn3`wP7j@VJZIHB)3w6I-PT0^Lh^)W|EL^ccH>C6y;`RbTh*yS)+398tw%l#u~k91 zy(%J|#v;sqG;HSegJz$ubdT1u9=Gl}C;r?Gd|hQE`AOW^`AlHIsqc;LlYPc z!)&GQZLyH{`xque%bvA^JhrmRQ3Lt03sI1;6q^%uP?)le`PJ%6vQ3irymP6z|i$vKg5uvk24;tt3h~bZu3Z2J;`TXKNG(i1Q2(udtJ+)pK`Sr&b^qRdf z^TjbgAd`U+ySA7y06Ag5pd`x})E|hwxn|hE)&jrh+ej;Zwf8t$!0&NNC`fbyX`DyQ@Eycxr;io~SQt3M3v{SUGlb84Q z46pGdMI?Amhwk`3P^LaCcBW(Xwfb~}ugN%+q%W8I6pw7O$Fa?hIKGAVw5rg#{LEH2 zy|YDLUCtGGko_B4{CaN_Ml6_2#DZxZsn1a(PM}c`@2UBKVNMH}jO@_qa^Itm$w%;wNGVbnLB<^90q zVE$0cX@gM|w;UHsJaK8aFC{mb8wx)pL;}qkt5D?I+1wm zD8#yJ8c;uP4byiE?wQCJ&PA&KRC@mIkI~oB*!&+zlKhvRgy6V6bo-rwow*xyJUN7p;!#{Mwsn+>ILz!2Qr!ziq#ZhxHNI6OmFhnU+0D0u8Ge_=D!8~;K*IRCS{-4;YTL;s6 z+&YljhCeN6I!O(j#!U~JD$Aj^k}sNvIQNN|H|+Zs|N09&+VgwcHN)?&I|kPe2T%&c z&GKN>m4`yM#}5lf{O7ekH73_kdR{ad-w(g1I^u2_^$+ifLc{JDS^XX{mr@)4)vM+Z zO5EJY*|PlDc_>U;2=f{JK8U}9;rG%Xk2}|%GT$Wtk##6QS23k7FhWU z^|yWi{kP&_ZOv8ciM0{NXsk-a-P6ffq{6|1VZE>~oe$nGW9S^(`6ZqVzxP6QJlGwF zyE|g3D_bPHy-mcuO=ASDpWW-0lhU=u+ioidO&BiBJQ0`!-Es)D2}zmveEtIYg%hx$nk4a{JY|D zw}{$Zp-5J@C6;?@%X9r7++*&DTl*wPbJm5%PhZNP%k5`@I_~eFv5Z17 z>NbgFH#Um6H-3Gt|B7ukq++WL_gImQ?93iVT>RY)&N?GMD1O}C0yJ*HgRA-2ne7gJ zwL#DD_ow)&PyY;!X`IXjDp#YqA{(yrC&+!fOoyKH)zP?Bf?Jznaeci|c6FVQOR3d- z$nl*HjJT&t9b^{{y5h=FAGqnMemML#bAWr>fje7iEToXeBevqtiWF);J*oXr{a5Ay zr)f;$IPHT!DE{BG;juuY4Zp`cbu<*j(D;?qm=I`eXiUe>oc`a{IBJ2i)ID@zYq+Z7N6IHZ-1FkA*azv{bb(nvSjpPUX|M z4()^TJTxE3fyeyk_yu#+QI{{qm0TgDS<$i!nbBNIZTi1(P(b5N04rPPLBR_;w?hrPL1j`+xEeiZJXkAk=H_z$-JnoJ)F*(qup zcc~1wb^+IRpz-tunxn?>Q~Nng@gK`aGmWV=@6W}P(rgIks<+vX^YjUjWrN`iw>wrPKL;u&PL+Nr8xMQQ2F0AUP$fyZEFJCk12iLk;Z|CV^4Mf-NUV+ z`!}lpk<@p17 z;-C1B;Ae8MbxV$o;+r?97mb6&qPZjw4+^tzXG0op<|TnQWqcccTa7WOSVdz5!9m#L z?=LG22;i0m1h(nF@syCpI%Bx^tA(;BwGxUy{U62O+83-;`doqLXDVWoyEU%oCE(WD z6kN`fz=7IKtNyJfs8Z~~DE0M6v6qi*ySFcwQXBreG*)`IO2mCoEtWmGkOW-Gr1&TO zWBPB8pItK>VKEo;@x!s*lZTq5SlDSj$8SDv6n6Us&={)^i?PZ!yLodryFZWlG@TNY zrYbS_;h8wulk+rZ;m<7U*O>gT;cu0P$-!c>j+51}faaXC_!YZnHbw|aFTDo#6wc&3(S4A?3{y}9pnu({!>AtpV4KD4BL!{*|Q1AJrd`@HSe+aBEw$T=Ep1|VG zwpd5w%{f+XEWVFK6CUD?9nXo49rcrV-U^a-_*qBPRcJe=OY>9=Ij{X=PW z4}Z_V`Cf)rz}O|ZdqzU)4sF^_xEMsVOchw z9LS|UfDCL4a>O#a?la$O`?=zM@w0wpZLs|MVC^MXXXlRfcAoOQ2AW@hWK;fqihWbO ziIaxJ+@+($?77Xf8UHITj^v3+{jMZZSCZ7__ReIz`W-3d4aF%>?-WtLM^PG0Y0HFk zO)L^Dbuo=Y|06+hypK(>x{Ne>d;^!M(~)ECgbfa!$g>eV%M);ZO7j!S6HNKbsGW3? z(0F}}PhKWX8WpaSN!fdq|eqkpx@8n?b(A)0(wn zQoTM-p?0lU>HO;0{Frqf@fwj-ZX9lwsGk$%dsZ&yd8DB|a*xYOyp zl48n#m}J5)qB@^SZM@Tq^U6B`dyh&xqr=JJm@x9YFs$3b$WYULp@GMD2l+SeV)=7^ zl;|FDog?+9O`Zg`AHKqb@4v!IO)aFF*#R-4d4b zNP@%jJ=uFe`dd|tLxRW-8w;{C(685aAMb!dfydX`cbsLO=*n5%DJNDj-LpP>`AQebN|P`h4XEE zB=&AZY-mj+X7=BTjBGq2^(=2h=vv%}SZ3)FX<+khgpN6hwtTlW=xw}OpUabla5-VAv5i55S(t(veG_Q!3&8#7B`rjy4_NM=nVkP^36)WlgrvAT_ kwM?FF&*f~DyHyWudTG^DTT&L)71LLnUR(9d62u7kKZkB + Source="/InfoPanel;component/Resources/Images/logo.ico" /> Date: Sat, 21 Dec 2024 15:07:52 +0800 Subject: [PATCH 07/10] Add plugin ui --- InfoPanel.Extras/DriveInfoPlugin.cs | 4 +- InfoPanel.Extras/IpifyPlugin.cs | 3 +- InfoPanel.Extras/VolumePlugin.cs | 4 +- InfoPanel.Extras/WeatherPlugin.cs | 12 +- InfoPanel.Plugins.Loader/PluginWrapper.cs | 5 +- InfoPanel.Plugins.Simulator/Program.cs | 2 +- InfoPanel.Plugins/BasePlugin.cs | 20 +- InfoPanel.Plugins/IPlugin.cs | 2 + InfoPanel.Plugins/PluginSensor.cs | 25 +- InfoPanel.Plugins/PluginText.cs | 20 +- InfoPanel/App.xaml.cs | 2 + InfoPanel/InfoPanel.csproj | 3 + InfoPanel/Monitors/PluginMonitor.cs | 4 +- InfoPanel/ViewModels/HomeViewModel.cs | 3 + InfoPanel/ViewModels/PluginsViewModel.cs | 39 +++ InfoPanel/Views/Pages/HomePage.xaml | 314 ++++++++++--------- InfoPanel/Views/Pages/PluginsPage.xaml | 155 +++++++++ InfoPanel/Views/Pages/PluginsPage.xaml.cs | 24 ++ InfoPanel/Views/Pages/UpdatesPage.xaml.cs | 5 +- InfoPanel/Views/Windows/FluentWindow.xaml | 5 + InfoPanel/Views/Windows/FluentWindow.xaml.cs | 8 + 21 files changed, 457 insertions(+), 202 deletions(-) create mode 100644 InfoPanel/ViewModels/PluginsViewModel.cs create mode 100644 InfoPanel/Views/Pages/PluginsPage.xaml create mode 100644 InfoPanel/Views/Pages/PluginsPage.xaml.cs diff --git a/InfoPanel.Extras/DriveInfoPlugin.cs b/InfoPanel.Extras/DriveInfoPlugin.cs index f19bb26..7c399b8 100644 --- a/InfoPanel.Extras/DriveInfoPlugin.cs +++ b/InfoPanel.Extras/DriveInfoPlugin.cs @@ -6,9 +6,11 @@ public class DriveInfoPlugin : BasePlugin { public override TimeSpan UpdateInterval => TimeSpan.FromSeconds(1); + public override string? ConfigFilePath => null; + private readonly List _containers = []; - public DriveInfoPlugin() : base("drive-info-plugin", "Drive Info") + public DriveInfoPlugin() : base("drive-info-plugin", "Drive Info", "Retrieves local disk space information.") { } diff --git a/InfoPanel.Extras/IpifyPlugin.cs b/InfoPanel.Extras/IpifyPlugin.cs index 36e6cbd..108c6c3 100644 --- a/InfoPanel.Extras/IpifyPlugin.cs +++ b/InfoPanel.Extras/IpifyPlugin.cs @@ -10,9 +10,10 @@ public class IpifyPlugin : BasePlugin private readonly PluginText _ipv6Sensor = new("IPv6", "-"); private readonly HttpClient _httpClient = new(); + public override string? ConfigFilePath => null; public override TimeSpan UpdateInterval => TimeSpan.FromMinutes(5); - public IpifyPlugin() : base("ipify-plugin", "Public IP - Ipify") + public IpifyPlugin() : base("ipify-plugin", "Public IP - Ipify", "IPv4 & IPv6 lookup via ipify.org API.") { } diff --git a/InfoPanel.Extras/VolumePlugin.cs b/InfoPanel.Extras/VolumePlugin.cs index ddbee04..a3f84a1 100644 --- a/InfoPanel.Extras/VolumePlugin.cs +++ b/InfoPanel.Extras/VolumePlugin.cs @@ -10,10 +10,10 @@ public class VolumePlugin : BasePlugin private MMDeviceEnumerator? _deviceEnumerator; - public VolumePlugin() : base("volume-plugin","Volume Info") + public VolumePlugin() : base("volume-plugin","Volume Info", "Retrieves audio output devices and relevant details. Powered by NAudio.") { } - + public override string? ConfigFilePath => null; public override TimeSpan UpdateInterval => TimeSpan.FromMilliseconds(50); public override void Initialize() diff --git a/InfoPanel.Extras/WeatherPlugin.cs b/InfoPanel.Extras/WeatherPlugin.cs index 5a2e25a..a7ec7bc 100644 --- a/InfoPanel.Extras/WeatherPlugin.cs +++ b/InfoPanel.Extras/WeatherPlugin.cs @@ -38,29 +38,31 @@ public class WeatherPlugin : BasePlugin private readonly PluginSensor _rain = new("rain", "Rain", 0, "mm/h"); private readonly PluginSensor _snow = new("snow", "Snow", 0, "mm/h"); - public WeatherPlugin() : base("weather-plugin","Weather Info - OpenWeatherMap") + public WeatherPlugin() : base("weather-plugin","Weather Info - OpenWeatherMap", "Retrieves weather information periodically from openweathermap.org. API key required.") { } + private string? _configFilePath = null; + public override string? ConfigFilePath => _configFilePath; public override TimeSpan UpdateInterval => TimeSpan.FromMinutes(1); public override void Initialize() { Assembly assembly = Assembly.GetExecutingAssembly(); - var configPath = $"{assembly.ManifestModule.FullyQualifiedName}.ini"; + _configFilePath = $"{assembly.ManifestModule.FullyQualifiedName}.ini"; var parser = new FileIniDataParser(); IniData config; - if (!File.Exists(configPath)) + if (!File.Exists(_configFilePath)) { config = new IniData(); config["Weather Plugin"]["APIKey"] = ""; config["Weather Plugin"]["City"] = "Singapore"; - parser.WriteFile(configPath, config); + parser.WriteFile(_configFilePath, config); } else { - config = parser.ReadFile(configPath); + config = parser.ReadFile(_configFilePath); var apiKey = config["Weather Plugin"]["APIKey"]; _city = config["Weather Plugin"]["City"]; diff --git a/InfoPanel.Plugins.Loader/PluginWrapper.cs b/InfoPanel.Plugins.Loader/PluginWrapper.cs index 6468ac4..a002bfb 100644 --- a/InfoPanel.Plugins.Loader/PluginWrapper.cs +++ b/InfoPanel.Plugins.Loader/PluginWrapper.cs @@ -2,13 +2,16 @@ namespace InfoPanel.Plugins.Loader { - public class PluginWrapper(IPlugin plugin) + public class PluginWrapper(string fileName, IPlugin plugin) { + public string FileName => fileName; public IPlugin Plugin { get; } = plugin; public List PluginContainers { get; } = []; public string Id => Plugin.Id; public string Name => Plugin.Name; + public string Description => Plugin.Description; + public string? ConfigFilePath => Plugin.ConfigFilePath; public TimeSpan UpdateInterval => Plugin.UpdateInterval; diff --git a/InfoPanel.Plugins.Simulator/Program.cs b/InfoPanel.Plugins.Simulator/Program.cs index 8c52a94..f2da00d 100644 --- a/InfoPanel.Plugins.Simulator/Program.cs +++ b/InfoPanel.Plugins.Simulator/Program.cs @@ -15,7 +15,7 @@ foreach (var plugin in plugins) { - PluginWrapper pluginWrapper = new(plugin); + PluginWrapper pluginWrapper = new(Path.GetFileName(pluginPath), plugin); if (loadedPlugins.TryAdd(pluginWrapper.Name, pluginWrapper)) { try diff --git a/InfoPanel.Plugins/BasePlugin.cs b/InfoPanel.Plugins/BasePlugin.cs index 95ea0e2..f5d7610 100644 --- a/InfoPanel.Plugins/BasePlugin.cs +++ b/InfoPanel.Plugins/BasePlugin.cs @@ -1,21 +1,17 @@ namespace InfoPanel.Plugins { - public abstract class BasePlugin : IPlugin + public abstract class BasePlugin(string id, string name, string description) : IPlugin { - public string Id { get; } - public string Name { get; } - public abstract TimeSpan UpdateInterval { get; } + public string Id { get; } = id; + public string Name { get; } = name; - public BasePlugin(string name) - { - Id = IdUtil.Encode(name); - Name = name; - } + public string Description { get; } = description; + + public abstract string? ConfigFilePath { get; } + public abstract TimeSpan UpdateInterval { get; } - public BasePlugin(string id, string name) + public BasePlugin(string name, string description = "") : this(IdUtil.Encode(name), name, description) { - Id = id; - Name = name; } public abstract void Close(); diff --git a/InfoPanel.Plugins/IPlugin.cs b/InfoPanel.Plugins/IPlugin.cs index 109e870..88a05d9 100644 --- a/InfoPanel.Plugins/IPlugin.cs +++ b/InfoPanel.Plugins/IPlugin.cs @@ -4,6 +4,8 @@ public interface IPlugin { string Id { get; } string Name { get; } + string Description { get; } + string? ConfigFilePath { get; } TimeSpan UpdateInterval { get; } void Initialize(); void Load(List containers); diff --git a/InfoPanel.Plugins/PluginSensor.cs b/InfoPanel.Plugins/PluginSensor.cs index b5746ef..4d9fbf1 100644 --- a/InfoPanel.Plugins/PluginSensor.cs +++ b/InfoPanel.Plugins/PluginSensor.cs @@ -1,30 +1,17 @@ namespace InfoPanel.Plugins { - public class PluginSensor : IPluginSensor + public class PluginSensor(string id, string name, float value, string? unit = null) : IPluginSensor { - public string Id { get; } + public string Id { get; } = id; - public string Name { get; } + public string Name { get; } = name; - public float Value { get; set; } + public float Value { get; set; } = value; - public string? Unit { get; } + public string? Unit { get; } = unit; - public PluginSensor(string id, string name, float value, string? unit = null) + public PluginSensor(string name, float value, string? unit = null) : this(IdUtil.Encode(name), name, value, unit) { - Id = id; - Name = name; - Value = value; - Unit = unit; - } - - public PluginSensor(string name, float value, string? unit = null) - { - Id = IdUtil.Encode(name); - Name = name; - Value = value; - Unit = unit; - } } } diff --git a/InfoPanel.Plugins/PluginText.cs b/InfoPanel.Plugins/PluginText.cs index 47e0c1b..13989b2 100644 --- a/InfoPanel.Plugins/PluginText.cs +++ b/InfoPanel.Plugins/PluginText.cs @@ -1,25 +1,15 @@ namespace InfoPanel.Plugins { - public class PluginText : IPluginText + public class PluginText(string id, string name, string value) : IPluginText { - public string Id { get; } + public string Id { get; } = id; - public string Name { get; } + public string Name { get; } = name; - public string Value { get; set; } + public string Value { get; set; } = value; - public PluginText(string id, string name, string value) + public PluginText(string name, string value): this(IdUtil.Encode(name), name, value) { - Id = id; - Name = name; - Value = value; - } - - public PluginText(string name, string value) - { - Id = IdUtil.Encode(name); - Name = name; - Value = value; } } } diff --git a/InfoPanel/App.xaml.cs b/InfoPanel/App.xaml.cs index 106b47d..98f5428 100644 --- a/InfoPanel/App.xaml.cs +++ b/InfoPanel/App.xaml.cs @@ -71,6 +71,8 @@ public partial class App : System.Windows.Application services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/InfoPanel/InfoPanel.csproj b/InfoPanel/InfoPanel.csproj index 99b9d73..81f7864 100644 --- a/InfoPanel/InfoPanel.csproj +++ b/InfoPanel/InfoPanel.csproj @@ -214,6 +214,9 @@ Code + + Code + Code diff --git a/InfoPanel/Monitors/PluginMonitor.cs b/InfoPanel/Monitors/PluginMonitor.cs index 196dbba..0220467 100644 --- a/InfoPanel/Monitors/PluginMonitor.cs +++ b/InfoPanel/Monitors/PluginMonitor.cs @@ -18,7 +18,7 @@ internal class PluginMonitor: BackgroundTask public static readonly ConcurrentDictionary SENSORHASH = new(); - private readonly Dictionary _loadedPlugins = []; + public readonly Dictionary _loadedPlugins = []; private PluginMonitor() { } @@ -82,7 +82,7 @@ private async Task LoadPluginsAsync() foreach (var plugin in plugins) { - PluginWrapper wrapper = new(plugin); + PluginWrapper wrapper = new(Path.GetFileName(pluginFile), plugin); if (_loadedPlugins.TryAdd(wrapper.Name, wrapper)) { try diff --git a/InfoPanel/ViewModels/HomeViewModel.cs b/InfoPanel/ViewModels/HomeViewModel.cs index e4d813d..dfa635c 100644 --- a/InfoPanel/ViewModels/HomeViewModel.cs +++ b/InfoPanel/ViewModels/HomeViewModel.cs @@ -47,6 +47,9 @@ private void OnNavigate(string? parameter) case "navigate_to_about": _navigationService.Navigate(typeof(Views.Pages.AboutPage)); return; + case "navigate_to_plugins": + _navigationService.Navigate(typeof(Views.Pages.PluginsPage)); + return; case "navigate_to_settings": _navigationService.Navigate(typeof(Views.Pages.SettingsPage)); return; diff --git a/InfoPanel/ViewModels/PluginsViewModel.cs b/InfoPanel/ViewModels/PluginsViewModel.cs new file mode 100644 index 0000000..363c1f0 --- /dev/null +++ b/InfoPanel/ViewModels/PluginsViewModel.cs @@ -0,0 +1,39 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using InfoPanel.Models; +using InfoPanel.Monitors; +using System.Collections.ObjectModel; +using System.IO; +using System.Reflection; + +namespace InfoPanel.ViewModels +{ + public class PluginsViewModel: ObservableObject + { + public string PluginsFolder { get; } + public ObservableCollection Plugins { get; set; } = []; + + public PluginsViewModel() { + + PluginsFolder = Path.Combine(Directory.GetCurrentDirectory(), "plugins"); + + foreach(var wrapper in PluginMonitor.Instance._loadedPlugins.Values) + { + Plugins.Add(new PluginViewModel + { + FileName = wrapper.FileName, + Name = wrapper.Name, + Description = wrapper.Description, + ConfigFilePath = wrapper.ConfigFilePath + }); + } + } + } + + public class PluginViewModel + { + public required string FileName { get; set; } + public required string Name { get; set; } + public required string Description { get; set; } + public required string? ConfigFilePath { get; set; } + } +} diff --git a/InfoPanel/Views/Pages/HomePage.xaml b/InfoPanel/Views/Pages/HomePage.xaml index be0d526..cfc2aab 100644 --- a/InfoPanel/Views/Pages/HomePage.xaml +++ b/InfoPanel/Views/Pages/HomePage.xaml @@ -1,16 +1,17 @@ - + @@ -35,111 +36,140 @@ + Margin="16" + HorizontalAlignment="Left" + VerticalAlignment="Center" + Background="Transparent"> + FontSize="28" + FontWeight="Bold" + Text="Welcome to InfoPanel." /> + FontSize="18" + Opacity=".8" + Text="Level up your desktop." /> - - + + - - - + + + + Margin="0" + FontSize="13" + FontWeight="Medium" + Text="Profiles" /> + FontSize="12" + Foreground="{DynamicResource TextFillColorTertiaryBrush}" + Text="Create and manage your profiles." /> - + + Margin="0" + FontSize="13" + FontWeight="Medium" + Text="Design" /> + FontSize="12" + Foreground="{DynamicResource TextFillColorTertiaryBrush}" + Text="Customisation and design." /> - + + Margin="0" + FontSize="13" + FontWeight="Medium" + Text="Updates" /> + FontSize="12" + Foreground="{DynamicResource TextFillColorTertiaryBrush}" + Text="Check for updates and view version history." /> - + + Margin="0" + FontSize="13" + FontWeight="Medium" + Text="Plugins" /> + FontSize="12" + Foreground="{DynamicResource TextFillColorTertiaryBrush}" + Text="View activated plugins." /> - + + Margin="0" + FontSize="13" + FontWeight="Medium" + Text="Settings" /> + FontSize="12" + Foreground="{DynamicResource TextFillColorTertiaryBrush}" + Text="Manage settings." /> + + + + + + - - - + + + @@ -150,27 +180,28 @@ Text="Check for updates and view version history." /> - + FontSize="13" + FontWeight="Medium" + Text="Love InfoPanel?" /> + + Grid.Column="2" + Width="150" + Margin="0,0,0,0" + VerticalAlignment="Center" + Appearance="Secondary" + Content="Review" + NavigateUri="ms-windows-store://review/?ProductId=XPFP7C8H5446ZD" /> - + @@ -180,27 +211,28 @@ Text="Check for updates and view version history." /> - + FontSize="13" + FontWeight="Medium" + Text="Support Development" /> + + Grid.Column="2" + Width="150" + Margin="0,0,0,0" + VerticalAlignment="Center" + Appearance="Secondary" + Content="Donate" + NavigateUri="https://www.buymeacoffee.com/urfath3r" /> - + @@ -211,27 +243,28 @@ Text="Check for updates and view version history." /> - + FontSize="13" + FontWeight="Medium" + Text="Discord" /> + + Grid.Column="2" + Width="150" + Margin="0,0,0,0" + VerticalAlignment="Center" + Appearance="Secondary" + Content="Join" + NavigateUri="https://discord.gg/cQnjdMC7Qc" /> - + @@ -242,22 +275,23 @@ Text="Check for updates and view version history." /> - + FontSize="13" + FontWeight="Medium" + Text="Reddit" /> + + Grid.Column="2" + Width="150" + Margin="0,0,0,0" + VerticalAlignment="Center" + Appearance="Secondary" + Content="Launch" + NavigateUri="https://www.reddit.com/r/InfoPanel/" /> @@ -265,7 +299,7 @@ Text="Check for updates and view version history." /> - + diff --git a/InfoPanel/Views/Pages/PluginsPage.xaml b/InfoPanel/Views/Pages/PluginsPage.xaml new file mode 100644 index 0000000..fee16c5 --- /dev/null +++ b/InfoPanel/Views/Pages/PluginsPage.xaml @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + Warning! + Plugins run under the same privileges as InfoPanel (Administrator). Do not load untrusted plugins. Always verify the source of the plugin. + If unsure, do NOT use the plugin. + + + + + + + + + + + + + diff --git a/InfoPanel/Views/Pages/PluginsPage.xaml.cs b/InfoPanel/Views/Pages/PluginsPage.xaml.cs new file mode 100644 index 0000000..05aa05d --- /dev/null +++ b/InfoPanel/Views/Pages/PluginsPage.xaml.cs @@ -0,0 +1,24 @@ +using InfoPanel.ViewModels; +using System.Windows.Controls; + +namespace InfoPanel.Views.Pages +{ + /// + /// Interaction logic for AboutPage.xaml + /// + public partial class PluginsPage : Page + { + public PluginsViewModel ViewModel + { + get; + } + + public PluginsPage(PluginsViewModel viewModel) + { + ViewModel = viewModel; + DataContext = viewModel; + + InitializeComponent(); + } + } +} diff --git a/InfoPanel/Views/Pages/UpdatesPage.xaml.cs b/InfoPanel/Views/Pages/UpdatesPage.xaml.cs index badaab8..4b7eaf6 100644 --- a/InfoPanel/Views/Pages/UpdatesPage.xaml.cs +++ b/InfoPanel/Views/Pages/UpdatesPage.xaml.cs @@ -3,14 +3,13 @@ using InfoPanel.Models; using InfoPanel.ViewModels; using System; +using System.Diagnostics; using System.IO; using System.Net.Http; -using System.Threading.Tasks; using System.Threading; +using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; -using static InfoPanel.Views.Pages.AboutPage; -using System.Diagnostics; namespace InfoPanel.Views.Pages { diff --git a/InfoPanel/Views/Windows/FluentWindow.xaml b/InfoPanel/Views/Windows/FluentWindow.xaml index 5fdf5dd..3d387b1 100644 --- a/InfoPanel/Views/Windows/FluentWindow.xaml +++ b/InfoPanel/Views/Windows/FluentWindow.xaml @@ -108,6 +108,11 @@ Icon="PhoneUpdate20" PageTag="updates" PageType="{x:Type pages:UpdatesPage}" /> + Date: Sat, 21 Dec 2024 17:42:04 +0800 Subject: [PATCH 08/10] Fix github build. Hopefully? --- .github/workflows/dotnet-desktop.yml | 2 +- InfoPanel.Extras/DriveInfoPlugin.cs | 4 +-- InfoPanel.Extras/InfoPanel.Extras.csproj | 2 ++ .../InfoPanel.Plugins.Loader.csproj | 1 + .../InfoPanel.Plugins.Simulator.csproj | 1 + InfoPanel.Plugins/InfoPanel.Plugins.csproj | 1 + InfoPanel.sln | 16 +++++------ InfoPanel/App.xaml.cs | 1 + InfoPanel/InfoPanel.csproj | 27 ++++++++++++++----- .../PublishProfiles/FolderProfile.pubxml | 18 +++++++++++++ 10 files changed, 55 insertions(+), 18 deletions(-) create mode 100644 InfoPanel/Properties/PublishProfiles/FolderProfile.pubxml diff --git a/.github/workflows/dotnet-desktop.yml b/.github/workflows/dotnet-desktop.yml index b882936..d1c0e24 100644 --- a/.github/workflows/dotnet-desktop.yml +++ b/.github/workflows/dotnet-desktop.yml @@ -25,7 +25,7 @@ jobs: - name: Build InfoPanel run: | dotnet restore - dotnet publish InfoPanel\InfoPanel.csproj -c Release -o "${{ github.workspace }}\InfoPanel\bin\publish\win-x64" -r win-x64 --self-contained -p:PublishReadyToRun=true + dotnet publish -p:PublishProfile=FolderProfile - name: Install and Compile Inno Setup Installer shell: powershell diff --git a/InfoPanel.Extras/DriveInfoPlugin.cs b/InfoPanel.Extras/DriveInfoPlugin.cs index 7c399b8..201fa14 100644 --- a/InfoPanel.Extras/DriveInfoPlugin.cs +++ b/InfoPanel.Extras/DriveInfoPlugin.cs @@ -25,8 +25,8 @@ public override void Initialize() { if (drive.IsReady) { - PluginContainer container = new(drive.Name); - container.Entries.Add(new PluginText("name", "Name", drive.Name)); + PluginContainer container = new(drive.Name.TrimEnd('\\')); + container.Entries.Add(new PluginText("name", "Name", drive.Name.TrimEnd('\\'))); container.Entries.Add(new PluginText("type", "Type", drive.DriveType.ToString())); container.Entries.Add(new PluginText("volume_label", "Volume Label", drive.VolumeLabel)); container.Entries.Add(new PluginText("format", "Format", drive.DriveFormat)); diff --git a/InfoPanel.Extras/InfoPanel.Extras.csproj b/InfoPanel.Extras/InfoPanel.Extras.csproj index 2fa6b05..3374a61 100644 --- a/InfoPanel.Extras/InfoPanel.Extras.csproj +++ b/InfoPanel.Extras/InfoPanel.Extras.csproj @@ -5,6 +5,8 @@ enable enable true + win-x64 + x64 diff --git a/InfoPanel.Plugins.Loader/InfoPanel.Plugins.Loader.csproj b/InfoPanel.Plugins.Loader/InfoPanel.Plugins.Loader.csproj index a53ded9..4da6965 100644 --- a/InfoPanel.Plugins.Loader/InfoPanel.Plugins.Loader.csproj +++ b/InfoPanel.Plugins.Loader/InfoPanel.Plugins.Loader.csproj @@ -4,6 +4,7 @@ net8.0-windows enable enable + x64 diff --git a/InfoPanel.Plugins.Simulator/InfoPanel.Plugins.Simulator.csproj b/InfoPanel.Plugins.Simulator/InfoPanel.Plugins.Simulator.csproj index 5c1f97a..0182113 100644 --- a/InfoPanel.Plugins.Simulator/InfoPanel.Plugins.Simulator.csproj +++ b/InfoPanel.Plugins.Simulator/InfoPanel.Plugins.Simulator.csproj @@ -5,6 +5,7 @@ net8.0-windows enable enable + x64 diff --git a/InfoPanel.Plugins/InfoPanel.Plugins.csproj b/InfoPanel.Plugins/InfoPanel.Plugins.csproj index d62d4ce..3b925f9 100644 --- a/InfoPanel.Plugins/InfoPanel.Plugins.csproj +++ b/InfoPanel.Plugins/InfoPanel.Plugins.csproj @@ -4,6 +4,7 @@ net8.0-windows enable enable + x64 diff --git a/InfoPanel.sln b/InfoPanel.sln index 329fd5e..6a5c440 100644 --- a/InfoPanel.sln +++ b/InfoPanel.sln @@ -25,20 +25,20 @@ Global {59D47BC5-EBEE-48EC-BCF4-09C4BB0A31A8}.Release|x64.Build.0 = Release|x64 {903C16DD-BF96-44B7-B058-17F587194E89}.Debug|x64.ActiveCfg = Debug|Any CPU {903C16DD-BF96-44B7-B058-17F587194E89}.Debug|x64.Build.0 = Debug|Any CPU - {903C16DD-BF96-44B7-B058-17F587194E89}.Release|x64.ActiveCfg = Release|Any CPU - {903C16DD-BF96-44B7-B058-17F587194E89}.Release|x64.Build.0 = Release|Any CPU + {903C16DD-BF96-44B7-B058-17F587194E89}.Release|x64.ActiveCfg = Release|x64 + {903C16DD-BF96-44B7-B058-17F587194E89}.Release|x64.Build.0 = Release|x64 {2573766B-535D-49FC-867F-5255568A1940}.Debug|x64.ActiveCfg = Debug|Any CPU {2573766B-535D-49FC-867F-5255568A1940}.Debug|x64.Build.0 = Debug|Any CPU - {2573766B-535D-49FC-867F-5255568A1940}.Release|x64.ActiveCfg = Release|Any CPU - {2573766B-535D-49FC-867F-5255568A1940}.Release|x64.Build.0 = Release|Any CPU + {2573766B-535D-49FC-867F-5255568A1940}.Release|x64.ActiveCfg = Release|x64 + {2573766B-535D-49FC-867F-5255568A1940}.Release|x64.Build.0 = Release|x64 {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Debug|x64.ActiveCfg = Debug|Any CPU {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Debug|x64.Build.0 = Debug|Any CPU - {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Release|x64.ActiveCfg = Release|Any CPU - {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Release|x64.Build.0 = Release|Any CPU + {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Release|x64.ActiveCfg = Release|x64 + {299B147B-6B0C-430F-9B91-AC1FB80AAF95}.Release|x64.Build.0 = Release|x64 {53662165-B047-4425-92F9-991221173CF4}.Debug|x64.ActiveCfg = Debug|Any CPU {53662165-B047-4425-92F9-991221173CF4}.Debug|x64.Build.0 = Debug|Any CPU - {53662165-B047-4425-92F9-991221173CF4}.Release|x64.ActiveCfg = Release|Any CPU - {53662165-B047-4425-92F9-991221173CF4}.Release|x64.Build.0 = Release|Any CPU + {53662165-B047-4425-92F9-991221173CF4}.Release|x64.ActiveCfg = Release|x64 + {53662165-B047-4425-92F9-991221173CF4}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/InfoPanel/App.xaml.cs b/InfoPanel/App.xaml.cs index 98f5428..0d385e9 100644 --- a/InfoPanel/App.xaml.cs +++ b/InfoPanel/App.xaml.cs @@ -274,6 +274,7 @@ public static async Task CleanShutDown() { await StopPanels(); await LibreMonitor.Instance.StopAsync(); + await PluginMonitor.Instance.StopAsync(); //shutdown Application.Current.Shutdown(); } diff --git a/InfoPanel/InfoPanel.csproj b/InfoPanel/InfoPanel.csproj index 81f7864..0b57053 100644 --- a/InfoPanel/InfoPanel.csproj +++ b/InfoPanel/InfoPanel.csproj @@ -3,6 +3,7 @@ WinExe net8.0-windows + win-x64 enable true true @@ -14,24 +15,36 @@ False en Resources\app.manifest - ..\InfoPanel.Extras\bin\$(Platform)\$(Configuration)\$(TargetFramework) - bin\$(Platform)\$(Configuration)\$(TargetFramework)\plugins + ..\InfoPanel.Extras\bin\$(Platform)\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier) + - + - - + + + + - + - + + + + + + + + + + + diff --git a/InfoPanel/Properties/PublishProfiles/FolderProfile.pubxml b/InfoPanel/Properties/PublishProfiles/FolderProfile.pubxml new file mode 100644 index 0000000..12e2e2d --- /dev/null +++ b/InfoPanel/Properties/PublishProfiles/FolderProfile.pubxml @@ -0,0 +1,18 @@ + + + + + Release + x64 + bin\publish\win-x64\ + FileSystem + <_TargetId>Folder + net8.0-windows + win-x64 + true + false + true + + \ No newline at end of file From aad4246874965fc4a39e8bc0bbe05adcd0337fbd Mon Sep 17 00:00:00 2001 From: Habib Rehman <13956188+habibrehmansg@users.noreply.github.com> Date: Sat, 21 Dec 2024 17:57:36 +0800 Subject: [PATCH 09/10] Minor path fix --- InfoPanel/InfoPanel.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/InfoPanel/InfoPanel.csproj b/InfoPanel/InfoPanel.csproj index 0b57053..22f4e7e 100644 --- a/InfoPanel/InfoPanel.csproj +++ b/InfoPanel/InfoPanel.csproj @@ -40,11 +40,11 @@ - + - + From 56f1dccce727552c99a9cb787d4f147187229ce4 Mon Sep 17 00:00:00 2001 From: Habib Rehman <13956188+habibrehmansg@users.noreply.github.com> Date: Sun, 22 Dec 2024 12:14:12 +0800 Subject: [PATCH 10/10] Optimisations --- InfoPanel.Extras/IpifyPlugin.cs | 8 +------ InfoPanel.Extras/VolumePlugin.cs | 33 ++++++++++++++++++++++------- InfoPanel.Extras/WeatherPlugin.cs | 11 +--------- InfoPanel/Drawing/GraphDraw.cs | 11 +++++++++- InfoPanel/Monitors/PluginMonitor.cs | 2 +- 5 files changed, 38 insertions(+), 27 deletions(-) diff --git a/InfoPanel.Extras/IpifyPlugin.cs b/InfoPanel.Extras/IpifyPlugin.cs index 108c6c3..d5e217c 100644 --- a/InfoPanel.Extras/IpifyPlugin.cs +++ b/InfoPanel.Extras/IpifyPlugin.cs @@ -5,7 +5,6 @@ namespace InfoPanel.Extras { public class IpifyPlugin : BasePlugin { - private readonly Stopwatch _stopwatch = new(); private readonly PluginText _ipv4Sensor = new("IPv4", "-"); private readonly PluginText _ipv6Sensor = new("IPv6", "-"); private readonly HttpClient _httpClient = new(); @@ -41,12 +40,7 @@ public override void Update() public override async Task UpdateAsync(CancellationToken cancellationToken) { - if (!_stopwatch.IsRunning || _stopwatch.ElapsedMilliseconds > 60000) - { - Trace.WriteLine("IpifyPlugin: Getting IP"); - await GetIp(cancellationToken); - _stopwatch.Restart(); - } + await GetIp(cancellationToken); } private async Task GetIp(CancellationToken cancellationToken) diff --git a/InfoPanel.Extras/VolumePlugin.cs b/InfoPanel.Extras/VolumePlugin.cs index a3f84a1..0970af3 100644 --- a/InfoPanel.Extras/VolumePlugin.cs +++ b/InfoPanel.Extras/VolumePlugin.cs @@ -10,6 +10,8 @@ public class VolumePlugin : BasePlugin private MMDeviceEnumerator? _deviceEnumerator; + private readonly Dictionary _deviceCache = []; + public VolumePlugin() : base("volume-plugin","Volume Info", "Retrieves audio output devices and relevant details. Powered by NAudio.") { } @@ -37,13 +39,19 @@ public override void Initialize() container.Entries.Add(new PluginSensor("volume", "Volume", (float)Math.Round(device.AudioEndpointVolume.MasterVolumeLevelScalar * 100), "%")); container.Entries.Add(new PluginText("mute", "Mute", device.AudioEndpointVolume.Mute.ToString())); _containers.Add(container); - device.Dispose(); + + _deviceCache.TryAdd(device.ID, device); } } public override void Close() { _deviceEnumerator?.Dispose(); + foreach (var device in _deviceCache.Values) + { + device.Dispose(); + } + _deviceCache.Clear(); } public override void Load(List containers) @@ -67,11 +75,24 @@ public override void Update() { if (container.Name == "Default") { - device = _deviceEnumerator?.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia); + var tempDevice = _deviceEnumerator?.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia); + if (tempDevice != null) + { + device = _deviceCache.GetValueOrDefault(tempDevice.ID); + + if(device == null) + { + _deviceCache.TryAdd(tempDevice.ID, tempDevice); + device = tempDevice; + } else + { + tempDevice.Dispose(); + } + } } else { - device = _deviceEnumerator?.GetDevice(container.Id); + device = _deviceCache.GetValueOrDefault(container.Id); } if (device != null) @@ -99,11 +120,7 @@ public override void Update() } } } - } - finally - { - device?.Dispose(); - } + }catch { } } } } diff --git a/InfoPanel.Extras/WeatherPlugin.cs b/InfoPanel.Extras/WeatherPlugin.cs index a7ec7bc..87d67bd 100644 --- a/InfoPanel.Extras/WeatherPlugin.cs +++ b/InfoPanel.Extras/WeatherPlugin.cs @@ -2,15 +2,12 @@ using IniParser; using IniParser.Model; using OpenWeatherMap.Standard; -using System.Diagnostics; using System.Reflection; namespace InfoPanel.Extras { public class WeatherPlugin : BasePlugin { - private readonly Stopwatch _stopwatch = new(); - private Current? _current; private string? _city; @@ -96,13 +93,7 @@ public override void Update() public override async Task UpdateAsync(CancellationToken cancellationToken) { - // Update weather data every minute - if (!_stopwatch.IsRunning || _stopwatch.ElapsedMilliseconds > 60000) - { - Trace.WriteLine("WeatherPlugin: Getting weather data"); - await GetWeather(); - _stopwatch.Restart(); - } + await GetWeather(); } private async Task GetWeather() diff --git a/InfoPanel/Drawing/GraphDraw.cs b/InfoPanel/Drawing/GraphDraw.cs index af77fef..9820482 100644 --- a/InfoPanel/Drawing/GraphDraw.cs +++ b/InfoPanel/Drawing/GraphDraw.cs @@ -340,7 +340,16 @@ public static void Run(ChartDisplayItem chartDisplayItem, MyGraphics g) } } - var value = Math.Max(tempValues.LastOrDefault(0.0) - minValue, 0); + //var value = Math.Max(tempValues.LastOrDefault(0.0) - minValue, 0); + + var value = 0.0; + var sensorReading = barDisplayItem.GetValue(); + + if(sensorReading.HasValue) + { + value = sensorReading.Value.ValueNow; + } + value = Math.Min(value, maxValue); value = value / (maxValue - minValue); value = value * Math.Max(frameRect.Width, frameRect.Height); diff --git a/InfoPanel/Monitors/PluginMonitor.cs b/InfoPanel/Monitors/PluginMonitor.cs index 0220467..5977b55 100644 --- a/InfoPanel/Monitors/PluginMonitor.cs +++ b/InfoPanel/Monitors/PluginMonitor.cs @@ -40,7 +40,7 @@ protected override async Task DoWorkAsync(CancellationToken token) { try { - plugin.Update(); + //plugin.Update(); } catch { } }