Skip to content

Commit 4bb3b31

Browse files
committed
Add "check for update" functionality and display a hint in the Generic Button Editor
Issue #199
1 parent b0bb893 commit 4bb3b31

8 files changed

Lines changed: 332 additions & 175 deletions

File tree

StreamDeckSimHub.Plugin/ActionEditor/GenericButtonEditor.xaml

Lines changed: 165 additions & 154 deletions
Large diffs are not rendered by default.

StreamDeckSimHub.Plugin/ActionEditor/ViewModels/SettingsViewModel.cs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,20 @@
33

44
using System.Collections.ObjectModel;
55
using System.Windows;
6+
using System.Windows.Media;
67
using CommunityToolkit.Mvvm.ComponentModel;
78
using CommunityToolkit.Mvvm.Input;
8-
using NLog;
99
using StreamDeckSimHub.Plugin.Actions.GenericButton.Model;
1010
using StreamDeckSimHub.Plugin.Actions.Model;
1111
using StreamDeckSimHub.Plugin.SimHub;
1212
using StreamDeckSimHub.Plugin.SimHub.ShakeIt;
1313
using StreamDeckSimHub.Plugin.Tools;
14+
using StreamDeckSimHub.Plugin.Tools.AutoUpdate;
1415

1516
namespace StreamDeckSimHub.Plugin.ActionEditor.ViewModels;
1617

1718
public partial class SettingsViewModel : ObservableObject, IViewModel
1819
{
19-
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
20-
2120
private readonly Settings _settings;
2221
private readonly ImageManager _imageManager;
2322
private readonly ISimHubConnection _simHubConnection;
@@ -37,6 +36,10 @@ partial void OnNameChanged(string value)
3736

3837
public string NameForTitle => string.IsNullOrWhiteSpace(Name) ? "Generic Button Editor" : "Generic Button Editor: " + Name;
3938

39+
[ObservableProperty] private string _version = "Version " + ThisAssembly.AssemblyFileVersion;
40+
[ObservableProperty] private string _newVersion = string.Empty;
41+
[ObservableProperty] private Brush _newVersionBrush = Brushes.Transparent;
42+
4043
/// List of DisplayItems (as ViewModels).
4144
public ObservableCollection<DisplayItemViewModel> DisplayItems { get; }
4245

@@ -94,6 +97,22 @@ public SettingsViewModel(Settings settings, ImageManager imageManager, ISimHubCo
9497
FlatCommandItems.Add(CommandItemToViewModel(commandItem, kvp.Key));
9598
}
9699
}
100+
101+
var currentVersion = new Version(ThisAssembly.AssemblyFileVersion);
102+
if (UpdateStatus.LatestVersionException != null)
103+
{
104+
NewVersion = $"Error checking for new version: {UpdateStatus.LatestVersionException.Message}";
105+
}
106+
else if (UpdateStatus.LatestVersion != null && UpdateStatus.LatestVersion > currentVersion)
107+
{
108+
NewVersion = $"New version available: {UpdateStatus.LatestVersion}";
109+
NewVersionBrush = new SolidColorBrush(Color.FromRgb(170, 80, 80));
110+
}
111+
else
112+
{
113+
NewVersion = "You are using the latest version.";
114+
NewVersionBrush = new SolidColorBrush(Color.FromRgb(30, 120, 30));
115+
}
97116
}
98117

99118
#region IViewModel

StreamDeckSimHub.Plugin/App.xaml.cs

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
// LGPL-3.0-or-later (see file COPYING and COPYING.LESSER)
33

44
using System.Windows;
5-
using CommunityToolkit.Mvvm.Messaging;
65
using Microsoft.Extensions.DependencyInjection;
76
using Microsoft.Extensions.Hosting;
87
using NLog;
98
using StreamDeckSimHub.Plugin.ActionEditor;
109
using StreamDeckSimHub.Plugin.Actions.GenericButton.Model;
1110
using StreamDeckSimHub.Plugin.SimHub;
1211
using StreamDeckSimHub.Plugin.Tools;
12+
using StreamDeckSimHub.Plugin.Tools.AutoUpdate;
1313

1414
namespace StreamDeckSimHub.Plugin;
1515

@@ -25,7 +25,38 @@ public App()
2525
var localDevMode = Environment.GetCommandLineArgs().Length == 2 && Environment.GetCommandLineArgs()[1] == "dev";
2626
_host = Program.CreateHost(localDevMode);
2727
LogManager.GetCurrentClassLogger().Info("Starting StreamDeckSimHub plugin {version}", ThisAssembly.AssemblyFileVersion);
28+
}
29+
30+
private async void Application_Startup(object sender, StartupEventArgs e)
31+
{
32+
try
33+
{
34+
var simHubConnection = _host.Services.GetRequiredService<ISimHubConnection>();
35+
if (simHubConnection is SimHubConnection shc)
36+
{
37+
shc.Run();
38+
}
39+
await _host.StartAsync();
40+
}
41+
catch (Exception ex)
42+
{
43+
LogManager.GetCurrentClassLogger().Error(ex, "Error during application startup");
44+
}
45+
46+
47+
var localDevMode = Environment.GetCommandLineArgs().Length == 2 && Environment.GetCommandLineArgs()[1] == "dev";
48+
49+
if (localDevMode)
50+
{
51+
UpdateStatus.LatestVersion = new Version("99.99.1");
52+
}
53+
else
54+
{
55+
// Run version check in the background. We use it only in the GenericButtonEditor, which is not yet opened.
56+
_ = GetLatestVersionAsync();
57+
}
2858

59+
// Developer mode: Open a Generic Button Editor directly for testing
2960
if (localDevMode)
3061
{
3162
var settings = new Settings
@@ -47,29 +78,28 @@ public App()
4778
}
4879
}
4980

50-
private async void Application_Startup(object sender, StartupEventArgs e)
81+
private async void Application_Exit(object sender, ExitEventArgs e)
5182
{
52-
try
53-
{
54-
var simHubConnection = _host.Services.GetRequiredService<ISimHubConnection>();
55-
if (simHubConnection is SimHubConnection shc)
56-
{
57-
shc.Run();
58-
}
59-
WeakReferenceMessenger.Default.RegisterAll(this);
60-
await _host.StartAsync();
61-
}
62-
catch (Exception ex)
83+
using (_host)
6384
{
64-
LogManager.GetCurrentClassLogger().Error(ex, "Error during application startup");
85+
await _host.StopAsync();
6586
}
6687
}
6788

68-
private async void Application_Exit(object sender, ExitEventArgs e)
89+
private async Task GetLatestVersionAsync()
6990
{
70-
using (_host)
91+
try
7192
{
72-
await _host.StopAsync();
93+
var updater = _host.Services.GetRequiredService<AutoUpdater>();
94+
var versionInfo = await updater.GetLatestVersion();
95+
UpdateStatus.LatestVersion = new Version(versionInfo.TagName);
96+
UpdateStatus.LatestVersionException = null;
97+
}
98+
catch (Exception ex)
99+
{
100+
LogManager.GetCurrentClassLogger().Error($"Error checking for new version: {ex.Message}");
101+
UpdateStatus.LatestVersion = null;
102+
UpdateStatus.LatestVersionException = ex;
73103
}
74104
}
75105
}

StreamDeckSimHub.Plugin/Program.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using StreamDeckSimHub.Plugin.PropertyLogic;
1313
using StreamDeckSimHub.Plugin.SimHub;
1414
using StreamDeckSimHub.Plugin.Tools;
15+
using StreamDeckSimHub.Plugin.Tools.AutoUpdate;
1516

1617
namespace StreamDeckSimHub.Plugin;
1718

@@ -54,5 +55,6 @@ static void ConfigureServices(HostBuilderContext context, IServiceCollection ser
5455
serviceCollection.AddSingleton<ActionEditorManager>();
5556
serviceCollection.AddSingleton<NCalcHandler>();
5657
serviceCollection.AddSingleton<SettingsConverter>();
58+
serviceCollection.AddSingleton<AutoUpdater>();
5759
}
5860
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright (C) 2025 Martin Renner
2+
// LGPL-3.0-or-later (see file COPYING and COPYING.LESSER)
3+
4+
using System.Net.Http;
5+
using Newtonsoft.Json;
6+
7+
namespace StreamDeckSimHub.Plugin.Tools.AutoUpdate;
8+
9+
public class AutoUpdater
10+
{
11+
private const string GitHubApiUrl = "https://api.github.com/repos/pre-martin/StreamDeckSimHubPlugin/releases/latest";
12+
private const string UserAgent = "StreamDeckSimHubPlugin-Updater";
13+
14+
/// <summary>
15+
/// Checks GitHub for the latest version of the plugin.
16+
/// </summary>
17+
/// <returns></returns>
18+
/// <exception cref="InvalidOperationException">If deserialization does not return an object</exception>
19+
public async Task<GitHubVersionInfo> GetLatestVersion()
20+
{
21+
HttpResponseMessage response;
22+
using (var httpClient = new HttpClient())
23+
{
24+
httpClient.DefaultRequestHeaders.Add("User-Agent", UserAgent);
25+
response = await httpClient.GetAsync(GitHubApiUrl);
26+
response.EnsureSuccessStatusCode();
27+
}
28+
29+
var content = await response.Content.ReadAsStringAsync();
30+
return JsonConvert.DeserializeObject<GitHubVersionInfo>(content) ??
31+
throw new InvalidOperationException("No version info returned from update check.");
32+
}
33+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright (C) 2025 Martin Renner
2+
// LGPL-3.0-or-later (see file COPYING and COPYING.LESSER)
3+
4+
using Newtonsoft.Json;
5+
6+
namespace StreamDeckSimHub.Plugin.Tools.AutoUpdate;
7+
8+
public class GitHubVersionInfo
9+
{
10+
[JsonProperty("tag_name", Required = Required.Always)]
11+
public string RawTagName { get; set; } = string.Empty;
12+
13+
[JsonIgnore]
14+
public string TagName => RawTagName.TrimStart('v', 'V');
15+
16+
[JsonProperty("draft")]
17+
public bool Draft { get; set; }
18+
19+
[JsonProperty("prerelease")]
20+
public bool Prerelease { get; set; }
21+
22+
[JsonProperty("assets")]
23+
public List<GitHubAsset> Assets { get; set; } = [];
24+
}
25+
26+
public class GitHubAsset
27+
{
28+
[JsonProperty("name", Required = Required.Always)]
29+
public string Name { get; set; } = string.Empty;
30+
31+
[JsonProperty("size")]
32+
public long Size { get; set; }
33+
34+
[JsonProperty("digest", Required = Required.Always)]
35+
public string Digest { get; set; } = string.Empty;
36+
37+
[JsonProperty("browser_download_url", Required = Required.Always)]
38+
public string BrowserDownloadUrl { get; set; } = string.Empty;
39+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (C) 2025 Martin Renner
2+
// LGPL-3.0-or-later (see file COPYING and COPYING.LESSER)
3+
4+
namespace StreamDeckSimHub.Plugin.Tools.AutoUpdate;
5+
6+
/// <summary>
7+
/// Stores the latest version information and any exception that occurred during update check.
8+
/// </summary>
9+
public static class UpdateStatus
10+
{
11+
/// <summary>
12+
/// The latest version available, or null if not available.
13+
/// </summary>
14+
public static Version? LatestVersion { get; set; }
15+
16+
/// <summary>
17+
/// The exception that occurred during update check, or null if successful.
18+
/// </summary>
19+
public static Exception? LatestVersionException { get; set; }
20+
}

StreamDeckSimHub.Plugin/Tools/PeriodicBackgroundService.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
// Copyright (C) 2024 Martin Renner
1+
// Copyright (C) 2025 Martin Renner
22
// LGPL-3.0-or-later (see file COPYING and COPYING.LESSER)
33

44
using Microsoft.Extensions.Hosting;
55
using NLog;
66

77
namespace StreamDeckSimHub.Plugin.Tools;
88

9+
/// <summary>
10+
/// This background service triggers a 'Tick' event every 100 milliseconds.
11+
/// </summary>
912
public class PeriodicBackgroundService : BackgroundService
1013
{
1114
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();

0 commit comments

Comments
 (0)