From cb6bcd722cfbafd65e7f589d4b50f74e9aa67d58 Mon Sep 17 00:00:00 2001 From: k3ldar Date: Sun, 13 Sep 2026 08:43:03 +0200 Subject: [PATCH] Update for play store --- .editorconfig | 12 +- .gitignore | 3 + PowerControlHubApp/AppShell.xaml | 51 ++++----- PowerControlHubApp/Internal/Constants.cs | 4 +- PowerControlHubApp/MauiProgram.cs | 3 +- .../Platforms/Android/AndroidManifest.xml | 2 +- .../Platforms/Android/MainApplication.cs | 35 +++++- PowerControlHubApp/PowerControlHubApp.csproj | 23 +++- .../Resources/Splash/splash.jpg | Bin 0 -> 18890 bytes .../Resources/Splash/splash.svg | 107 ++++++++++++++++-- .../Services/ConfigConnection.cs | 12 +- .../Services/DashboardConnection.cs | 12 +- .../Services/PowerHubService.cs | 7 +- .../ViewModels/BaseViewModel.cs | 67 ++++++++++- .../ExternalSensorDetailViewModel.cs | 2 +- .../ViewModels/LocalSensorDetailViewModel.cs | 2 +- .../ViewModels/NextionSettingsViewModel.cs | 6 +- .../ViewModels/SystemViewModel.cs | 6 +- .../ViewModels/TimeSettingsViewModel.cs | 40 ++++--- .../Views/ExternalSensorConfigPage.xaml | 7 -- .../Views/LocalSensorConfigPage.xaml | 7 -- .../Views/MqttSettingsPage.xaml | 5 - PowerControlHubApp/Views/RelayConfigPage.xaml | 7 -- PowerControlHubApp/Views/StatusBarView.xaml | 30 +++-- .../Views/StatusBarView.xaml.cs | 42 +++++++ PowerControlHubApp/Views/SystemPage.xaml | 12 +- PowerControlHubApp/Views/TopBarView.xaml | 10 ++ PowerControlHubApp/Views/TopBarView.xaml.cs | 26 +++++ 28 files changed, 411 insertions(+), 129 deletions(-) create mode 100644 PowerControlHubApp/Resources/Splash/splash.jpg diff --git a/.editorconfig b/.editorconfig index ef67e0b..56b2f89 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,13 +9,13 @@ dotnet_diagnostic.CC0009.severity = error dotnet_diagnostic.CC0010.severity = error -dotnet_diagnostic.IDE0028.severity = error -dotnet_diagnostic.IDE0290.severity = none -dotnet_diagnostic.IDE0305.severity = error +dotnet_diagnostic.IDE0028.severity = error # Use collection initializer +dotnet_diagnostic.IDE0290.severity = none # Use 'var' when variable type is apparent +dotnet_diagnostic.IDE0305.severity = error # Use 'var' when variable type is apparent -dotnet_diagnostic.CS0144.severity = error -dotnet_diagnostic.CS8632.severity = error +dotnet_diagnostic.CS0144.severity = error # The body of 'method' cannot be an iterator block because 'type' does not implement 'IEnumerable' or 'IEnumerator' +dotnet_diagnostic.CS8632.severity = error # nullable reference types -dotnet_diagnostic.CA1822.severity = error +dotnet_diagnostic.CA1822.severity = error # mark members as static diff --git a/.gitignore b/.gitignore index 821bceb..b536965 100644 --- a/.gitignore +++ b/.gitignore @@ -338,3 +338,6 @@ StaticElectrics/__vm/ /SmartFuseBox/__vm/.SmartFuseBox.vsarduino.h /LocalOnly /.github/copilot-instructions.md +/PowerControlHubApp/pch-release-key.keystore +/directory.props.build +/BuildPlayStoreApp.txt diff --git a/PowerControlHubApp/AppShell.xaml b/PowerControlHubApp/AppShell.xaml index a0393de..587328a 100644 --- a/PowerControlHubApp/AppShell.xaml +++ b/PowerControlHubApp/AppShell.xaml @@ -6,42 +6,31 @@ xmlns:views="clr-namespace:PowerControlHubApp.Views" xmlns:localization="clr-namespace:PowerControlHubApp.Resources.Localization" Title="PowerControlHub" + FlyoutBehavior="Flyout" Shell.NavBarIsVisible="False"> - + + + - + + + - + + + - + + + - + + + - + + + - + \ No newline at end of file diff --git a/PowerControlHubApp/Internal/Constants.cs b/PowerControlHubApp/Internal/Constants.cs index f76ea0f..35c95f2 100644 --- a/PowerControlHubApp/Internal/Constants.cs +++ b/PowerControlHubApp/Internal/Constants.cs @@ -12,6 +12,8 @@ internal static class Constants #endif public const string ApplicationNameWithIcon = IconWarning + SingleSpace + ApplicationName; public const string ApplicationName = "Power Control Hub"; + public const string AndroidLogTag = "PowerControlHub"; + public const string UnknownExceptionMessage = "Unknown exception"; public const string SummarySeparator = ";"; public const string IconTimePrefix = "🕐"; public const string IconExternalSensor = "📡"; @@ -28,7 +30,7 @@ internal static class Constants public const string IconLocalSensor = "🔌"; public const string IconRelaySymbol = "⚡"; public const string IconRemove = "🗑"; - public const string NavBack = "..."; + public const string NavBack = ".."; public const string FailSeparator = ";"; public const string ColorAsHex1 = "#44cc44"; public const string ColorError = "#cc4444"; diff --git a/PowerControlHubApp/MauiProgram.cs b/PowerControlHubApp/MauiProgram.cs index 951eb92..c63c84f 100644 --- a/PowerControlHubApp/MauiProgram.cs +++ b/PowerControlHubApp/MauiProgram.cs @@ -81,7 +81,8 @@ public static MauiApp CreateMauiApp() PowerHubService service = new PowerHubService( sp.GetRequiredService(), sp.GetRequiredService(), - sp.GetRequiredService()); + sp.GetRequiredService(), + sp.GetRequiredService()); return service; }); diff --git a/PowerControlHubApp/Platforms/Android/AndroidManifest.xml b/PowerControlHubApp/Platforms/Android/AndroidManifest.xml index e9937ad..d4f487a 100644 --- a/PowerControlHubApp/Platforms/Android/AndroidManifest.xml +++ b/PowerControlHubApp/Platforms/Android/AndroidManifest.xml @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/PowerControlHubApp/Platforms/Android/MainApplication.cs b/PowerControlHubApp/Platforms/Android/MainApplication.cs index 25dd32a..916a03b 100644 --- a/PowerControlHubApp/Platforms/Android/MainApplication.cs +++ b/PowerControlHubApp/Platforms/Android/MainApplication.cs @@ -1,5 +1,8 @@ using Android.App; using Android.Runtime; +using PowerControlHubApp.Internal; +using System; +using System.Threading.Tasks; namespace PowerControlHubApp { @@ -11,6 +14,34 @@ public MainApplication(IntPtr handle, JniHandleOwnership ownership) { } - protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + protected override MauiApp CreateMauiApp() + { + AndroidEnvironment.UnhandledExceptionRaiser += OnAndroidUnhandledException; + AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; + TaskScheduler.UnobservedTaskException += OnUnobservedTaskException; + + return MauiProgram.CreateMauiApp(); + } + + private static void OnAndroidUnhandledException(object sender, RaiseThrowableEventArgs e) + { + LogException(e.Exception); + } + + private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e) + { + LogException(e.ExceptionObject as Exception); + } + + private static void OnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) + { + LogException(e.Exception); + e.SetObserved(); + } + + private static void LogException(Exception exception) + { + Android.Util.Log.Error(Constants.AndroidLogTag, exception?.ToString() ?? Constants.UnknownExceptionMessage); + } } -} +} \ No newline at end of file diff --git a/PowerControlHubApp/PowerControlHubApp.csproj b/PowerControlHubApp/PowerControlHubApp.csproj index e9d10e9..3028cc8 100644 --- a/PowerControlHubApp/PowerControlHubApp.csproj +++ b/PowerControlHubApp/PowerControlHubApp.csproj @@ -2,7 +2,7 @@ net10.0-android - $(TargetFrameworks);net10.0-ios;net10.0-maccatalyst + $(TargetFrameworks);net10.0-maccatalyst $(TargetFrameworks);net10.0-windows10.0.19041.0 - com.companyname.powercontrolhubapp + pch.si.carter.powercontrolhubapp 1.0 @@ -47,6 +47,23 @@ 21.0 10.0.17763.0 10.0.17763.0 + False + pch-release-key.keystore + + + + True + True + --v2-signing-enabled true + + True + + + + True + True + --v2-signing-enabled true + True @@ -54,7 +71,7 @@ - + diff --git a/PowerControlHubApp/Resources/Splash/splash.jpg b/PowerControlHubApp/Resources/Splash/splash.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3085f758a9fc76257a7efe1a1c3264874893506a GIT binary patch literal 18890 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

dMit(1ab7F#%N`j+eZMtv0 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 DqUF96 literal 0 HcmV?d00001 diff --git a/PowerControlHubApp/Resources/Splash/splash.svg b/PowerControlHubApp/Resources/Splash/splash.svg index 21dfb25..384b30f 100644 --- a/PowerControlHubApp/Resources/Splash/splash.svg +++ b/PowerControlHubApp/Resources/Splash/splash.svg @@ -1,8 +1,99 @@ - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + System Initializing… + + diff --git a/PowerControlHubApp/Services/ConfigConnection.cs b/PowerControlHubApp/Services/ConfigConnection.cs index 20a650f..82fe1b3 100644 --- a/PowerControlHubApp/Services/ConfigConnection.cs +++ b/PowerControlHubApp/Services/ConfigConnection.cs @@ -152,9 +152,15 @@ private static SocketsHttpHandler CreateHandler() NoDelay = true }; socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); - socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, SecondsTen); - socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, SecondsFive); - socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveRetryCount, SecondsThree); + try + { + socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, SecondsTen); + socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, SecondsFive); + socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveRetryCount, SecondsThree); + } + catch + { + } await socket.ConnectAsync(context.DnsEndPoint, ct); return new NetworkStream(socket, ownsSocket: true); } diff --git a/PowerControlHubApp/Services/DashboardConnection.cs b/PowerControlHubApp/Services/DashboardConnection.cs index c3982de..bc8c254 100644 --- a/PowerControlHubApp/Services/DashboardConnection.cs +++ b/PowerControlHubApp/Services/DashboardConnection.cs @@ -141,9 +141,15 @@ private static SocketsHttpHandler CreateHandler() NoDelay = true }; socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); - socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, SecondsTen); - socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, SecondsFive); - socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveRetryCount, SecondsThree); + try + { + socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, SecondsTen); + socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, SecondsFive); + socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveRetryCount, SecondsThree); + } + catch + { + } await socket.ConnectAsync(context.DnsEndPoint, ct); return new NetworkStream(socket, ownsSocket: true); } diff --git a/PowerControlHubApp/Services/PowerHubService.cs b/PowerControlHubApp/Services/PowerHubService.cs index 1c4e2a9..def8f65 100644 --- a/PowerControlHubApp/Services/PowerHubService.cs +++ b/PowerControlHubApp/Services/PowerHubService.cs @@ -25,13 +25,16 @@ public class PowerHubService { private readonly IDashboardConnection _dashboardConnection; private readonly IConfigConnection _configConnection; + private readonly IDashboardProvider _dashboardProvider; public PowerHubService(IDashboardConnection dashboardConnection, IConfigConnection configConnection, - IMessageBus messageBus) + IMessageBus messageBus, + IDashboardProvider dashboardProvider) { _dashboardConnection = dashboardConnection ?? throw new ArgumentNullException(nameof(dashboardConnection)); _configConnection = configConnection ?? throw new ArgumentNullException(nameof(configConnection)); + _dashboardProvider = dashboardProvider ?? throw new ArgumentNullException(nameof(dashboardProvider)); messageBus.Subscribe(OnAuthConfigChanged); } @@ -40,6 +43,8 @@ public PowerHubService(IDashboardConnection dashboardConnection, public string BaseUrl => _dashboardConnection is DashboardConnection dc ? dc.BaseUrl : string.Empty; + public IndexModel CurrentIndex => _dashboardProvider?.CurrentIndex; + public void Configure(string ipAddress, int port, string apiKey = "", string hmacKey = "") { if (_dashboardConnection is DashboardConnection dc) diff --git a/PowerControlHubApp/ViewModels/BaseViewModel.cs b/PowerControlHubApp/ViewModels/BaseViewModel.cs index 6eaff20..85dbee5 100644 --- a/PowerControlHubApp/ViewModels/BaseViewModel.cs +++ b/PowerControlHubApp/ViewModels/BaseViewModel.cs @@ -28,6 +28,7 @@ public abstract class BaseViewModel : INotifyPropertyChanged private string _systemUptime = DoubleDash; private bool _hasWarnings; private bool _isApplyingRemoteState = true; + private bool _hasData; protected BaseViewModel(PowerHubService service, LogService log) { @@ -40,6 +41,7 @@ protected BaseViewModel(PowerHubService service, LogService log) protected LogService Log => _log; protected PowerHubService Service => _service; + protected IndexModel LastKnownIndex => _service.CurrentIndex; public ObservableCollection LogEntries => _log.Entries; @@ -242,6 +244,8 @@ protected virtual async Task ExecuteRefreshAsync(CancellationToken ct) if (IsBusy) return; + ApplyCachedIfNeeded(); + IsBusy = true; DeviceUrl = _service.BaseUrl; @@ -254,9 +258,8 @@ protected virtual async Task ExecuteRefreshAsync(CancellationToken ct) IsApplyingRemoteState = true; try { - UpdateSystem(index.System); - UpdateHasWarnings(index.Warning); - OnDataFetched(index); + ApplyIndex(index); + _hasData = true; IsConnected = true; StatusMessage = $"Updated {DateTime.Now:HH:mm:ss}"; } @@ -291,6 +294,39 @@ protected virtual async Task ExecuteRefreshAsync(CancellationToken ct) } } + private void ApplyIndex(IndexModel index) + { + UpdateSystem(index.System); + UpdateHasWarnings(index.Warning); + OnDataFetched(index); + } + + private void ApplyCachedIfNeeded() + { + if (_hasData) + return; + + IndexModel cached = _service.CurrentIndex; + + if (cached is null) + return; + + _hasData = true; + + MainThread.BeginInvokeOnMainThread(() => + { + IsApplyingRemoteState = true; + try + { + ApplyIndex(cached); + } + finally + { + IsApplyingRemoteState = false; + } + }); + } + ///

/// Override to apply view-model-specific updates after the common data /// has been fetched and system status has been written. @@ -325,6 +361,31 @@ private void UpdateHasWarnings(WarningModel warning) !warning.Active.Equals(NibbleZero, StringComparison.OrdinalIgnoreCase); } + protected static Task ConfirmAsync(string title, string message, string accept, string cancel) + { + Page page = CurrentPage(); + + if (page is null) + return Task.FromResult(false); + + return page.DisplayAlertAsync(title, message, accept, cancel); + } + + protected static Task AlertAsync(string title, string message, string cancel) + { + Page page = CurrentPage(); + + if (page is null) + return Task.CompletedTask; + + return page.DisplayAlertAsync(title, message, cancel); + } + + private static Page CurrentPage() + { + return Application.Current?.Windows.FirstOrDefault()?.Page ?? Shell.Current?.CurrentPage; + } + public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string name = null) diff --git a/PowerControlHubApp/ViewModels/ExternalSensorDetailViewModel.cs b/PowerControlHubApp/ViewModels/ExternalSensorDetailViewModel.cs index ff03c41..e7bc484 100644 --- a/PowerControlHubApp/ViewModels/ExternalSensorDetailViewModel.cs +++ b/PowerControlHubApp/ViewModels/ExternalSensorDetailViewModel.cs @@ -246,7 +246,7 @@ private async Task RemoveAsync() if (!Service.IsConfigured || _sensorIndex < 0) return; - bool confirmed = await Application.Current.Windows[0].Page.DisplayAlertAsync( + bool confirmed = await ConfirmAsync( RemoveSensor, $"Remove sensor at index {_sensorIndex}?\n\nThis cannot be undone.", Remove, diff --git a/PowerControlHubApp/ViewModels/LocalSensorDetailViewModel.cs b/PowerControlHubApp/ViewModels/LocalSensorDetailViewModel.cs index 82251ee..f09b290 100644 --- a/PowerControlHubApp/ViewModels/LocalSensorDetailViewModel.cs +++ b/PowerControlHubApp/ViewModels/LocalSensorDetailViewModel.cs @@ -819,7 +819,7 @@ private async Task RemoveAsync() if (!Service.IsConfigured || _sensorIndex < 0) return; - bool confirmed = await Application.Current.Windows[0].Page.DisplayAlertAsync( + bool confirmed = await ConfirmAsync( RemoveSensor, $"Remove local sensor at index {_sensorIndex}?\n\nThis cannot be undone.", Remove, diff --git a/PowerControlHubApp/ViewModels/NextionSettingsViewModel.cs b/PowerControlHubApp/ViewModels/NextionSettingsViewModel.cs index 0f5fad2..8d13f62 100644 --- a/PowerControlHubApp/ViewModels/NextionSettingsViewModel.cs +++ b/PowerControlHubApp/ViewModels/NextionSettingsViewModel.cs @@ -269,7 +269,7 @@ public async Task SaveAllAsync() public async Task RebootAsync() { - bool confirmed = await Application.Current.Windows[0].Page.DisplayAlertAsync(NextionRebootTitle, NextionRebootMessage, Reboot, Cancel); + bool confirmed = await ConfirmAsync(NextionRebootTitle, NextionRebootMessage, Reboot, Cancel); if (!confirmed) return; @@ -277,11 +277,11 @@ public async Task RebootAsync() try { await Service.SaveSettingsAsync(); - await Application.Current.Windows[0].Page.DisplayAlertAsync(Reboot, NextionRebootSavedMessage, OK); + await AlertAsync(Reboot, NextionRebootSavedMessage, OK); } catch { - await Application.Current.Windows[0].Page.DisplayAlertAsync(Reboot, NextionRebootFailed, OK); + await AlertAsync(Reboot, NextionRebootFailed, OK); } } diff --git a/PowerControlHubApp/ViewModels/SystemViewModel.cs b/PowerControlHubApp/ViewModels/SystemViewModel.cs index bf38401..93ae50a 100644 --- a/PowerControlHubApp/ViewModels/SystemViewModel.cs +++ b/PowerControlHubApp/ViewModels/SystemViewModel.cs @@ -318,11 +318,7 @@ public async Task InstallFirmwareAsync() return; string message = string.Format(OtaDialogMessage, AvailableFirmwareVersion); - bool confirmed = await Application.Current.Windows[0].Page.DisplayAlertAsync( - OtaDialogTitle, - message, - OtaDialogAccept, - MsgCancel); + bool confirmed = await ConfirmAsync(OtaDialogTitle, message, OtaDialogAccept, MsgCancel); if (!confirmed) return; diff --git a/PowerControlHubApp/ViewModels/TimeSettingsViewModel.cs b/PowerControlHubApp/ViewModels/TimeSettingsViewModel.cs index fdf4868..53760f2 100644 --- a/PowerControlHubApp/ViewModels/TimeSettingsViewModel.cs +++ b/PowerControlHubApp/ViewModels/TimeSettingsViewModel.cs @@ -126,6 +126,11 @@ public async Task RefreshAsync() if (!Service.IsConfigured || _isRefreshing) return; + IndexModel cached = LastKnownIndex; + + if (cached is not null) + ApplyTimeAndTimezone(cached); + IsRefreshing = true; try @@ -134,23 +139,7 @@ public async Task RefreshAsync() MainThread.BeginInvokeOnMainThread(() => { - if (index?.System?.Time.Year >= MinimumValidDateTimeYear) - { - _deviceTimeAtCapture = index.System.Time; - _captureUtcTicks = DateTime.UtcNow.Ticks; - DeviceTime = _deviceTimeAtCapture.ToString(DeviceTimeFormat); - } - else - { - DeviceTime = DoubleDash; - } - - if (index?.Config != null) - { - int offset = index.Config.TimezoneOffset; - MatchTimezoneIndexByOffset(offset); - } - + ApplyTimeAndTimezone(index); StatusMessage = $"Updated {DateTime.Now:HH:mm:ss}"; OnPropertyChanged(nameof(HasStatusMessage)); }); @@ -172,6 +161,23 @@ public async Task RefreshAsync() } } + private void ApplyTimeAndTimezone(IndexModel index) + { + if (index?.System?.Time.Year >= MinimumValidDateTimeYear) + { + _deviceTimeAtCapture = index.System.Time; + _captureUtcTicks = DateTime.UtcNow.Ticks; + DeviceTime = _deviceTimeAtCapture.ToString(DeviceTimeFormat); + } + else + { + DeviceTime = DoubleDash; + } + + if (index?.Config != null) + MatchTimezoneIndexByOffset(index.Config.TimezoneOffset); + } + private void MatchTimezoneIndexByOffset(int offsetHours) { for (int i = 0; i < TimeZoneOptionsList.Count; i++) diff --git a/PowerControlHubApp/Views/ExternalSensorConfigPage.xaml b/PowerControlHubApp/Views/ExternalSensorConfigPage.xaml index acd7451..5c7ceb7 100644 --- a/PowerControlHubApp/Views/ExternalSensorConfigPage.xaml +++ b/PowerControlHubApp/Views/ExternalSensorConfigPage.xaml @@ -55,13 +55,6 @@ - - -