LightSwitchViewModel.cs
1 // Copyright (c) Microsoft Corporation 2 // The Microsoft Corporation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 #nullable enable 6 7 using System; 8 using System.Collections.Generic; 9 using System.Collections.ObjectModel; 10 using System.Globalization; 11 using System.IO; 12 using System.Linq; 13 using System.Runtime.CompilerServices; 14 using System.Text.Json; 15 using System.Windows.Input; 16 using ManagedCommon; 17 using Microsoft.PowerToys.Settings.UI.Helpers; 18 using Microsoft.PowerToys.Settings.UI.Library; 19 using Microsoft.PowerToys.Settings.UI.Library.Helpers; 20 using Microsoft.PowerToys.Settings.UI.Library.Interfaces; 21 using Microsoft.PowerToys.Settings.UI.SerializationContext; 22 using Newtonsoft.Json.Linq; 23 using PowerDisplay.Common.Models; 24 using PowerDisplay.Common.Services; 25 using PowerToys.GPOWrapper; 26 using Settings.UI.Library; 27 using Settings.UI.Library.Helpers; 28 29 namespace Microsoft.PowerToys.Settings.UI.ViewModels 30 { 31 public partial class LightSwitchViewModel : PageViewModelBase 32 { 33 protected override string ModuleName => LightSwitchSettings.ModuleName; 34 35 private Func<string, int> SendConfigMSG { get; } 36 37 private GeneralSettings GeneralSettingsConfig { get; set; } 38 39 public ObservableCollection<SearchLocation> SearchLocations { get; } = new(); 40 41 public LightSwitchViewModel(ISettingsRepository<GeneralSettings> settingsRepository, LightSwitchSettings? initialSettings = null, Func<string, int>? ipcMSGCallBackFunc = null) 42 { 43 ArgumentNullException.ThrowIfNull(settingsRepository); 44 GeneralSettingsConfig = settingsRepository.SettingsConfig; 45 InitializeEnabledValue(); 46 47 _moduleSettings = initialSettings ?? new LightSwitchSettings(); 48 SendConfigMSG = ipcMSGCallBackFunc ?? (_ => 0); 49 50 ForceLightCommand = new RelayCommand(ForceLightNow); 51 ForceDarkCommand = new RelayCommand(ForceDarkNow); 52 53 AvailableScheduleModes = new ObservableCollection<string> 54 { 55 "Off", 56 "FixedHours", 57 "SunsetToSunrise", 58 "FollowNightLight", 59 }; 60 61 // Load PowerDisplay profiles 62 LoadPowerDisplayProfiles(); 63 64 // Check if PowerDisplay is enabled 65 CheckPowerDisplayEnabled(); 66 } 67 68 public override Dictionary<string, HotkeySettings[]> GetAllHotkeySettings() 69 { 70 var hotkeysDict = new Dictionary<string, HotkeySettings[]> 71 { 72 [ModuleName] = [ToggleThemeActivationShortcut], 73 }; 74 75 return hotkeysDict; 76 } 77 78 private void InitializeEnabledValue() 79 { 80 _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredLightSwitchEnabledValue(); 81 if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) 82 { 83 // Get the enabled state from GPO. 84 _enabledStateIsGPOConfigured = true; 85 _isEnabled = _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled; 86 } 87 else 88 { 89 _isEnabled = GeneralSettingsConfig.Enabled.LightSwitch; 90 } 91 } 92 93 private void ForceLightNow() 94 { 95 Logger.LogInfo("Sending custom action: forceLight"); 96 SendCustomAction("forceLight"); 97 } 98 99 private void ForceDarkNow() 100 { 101 Logger.LogInfo("Sending custom action: forceDark"); 102 SendCustomAction("forceDark"); 103 } 104 105 private void SendCustomAction(string actionName) 106 { 107 SendConfigMSG("{\"action\":{\"LightSwitch\":{\"action_name\":\"" + actionName + "\", \"value\":\"\"}}}"); 108 } 109 110 private void SaveSettings() 111 { 112 SendConfigMSG( 113 string.Format( 114 CultureInfo.InvariantCulture, 115 "{{ \"powertoys\": {{ \"{0}\": {1} }} }}", 116 LightSwitchSettings.ModuleName, 117 JsonSerializer.Serialize(_moduleSettings, SourceGenerationContextContext.Default.LightSwitchSettings))); 118 } 119 120 public LightSwitchSettings ModuleSettings 121 { 122 get => _moduleSettings; 123 set 124 { 125 if (_moduleSettings != value) 126 { 127 _moduleSettings = value; 128 129 OnPropertyChanged(nameof(ModuleSettings)); 130 RefreshModuleSettings(); 131 RefreshEnabledState(); 132 } 133 } 134 } 135 136 public bool IsEnabled 137 { 138 get => _isEnabled; 139 140 set 141 { 142 if (_enabledStateIsGPOConfigured) 143 { 144 // If it's GPO configured, shouldn't be able to change this state. 145 return; 146 } 147 148 if (value != _isEnabled) 149 { 150 _isEnabled = value; 151 152 // Set the status in the general settings configuration 153 GeneralSettingsConfig.Enabled.LightSwitch = value; 154 OutGoingGeneralSettings snd = new OutGoingGeneralSettings(GeneralSettingsConfig); 155 156 SendConfigMSG(snd.ToString()); 157 OnPropertyChanged(nameof(IsEnabled)); 158 } 159 } 160 } 161 162 public bool IsEnabledGpoConfigured 163 { 164 get => _enabledStateIsGPOConfigured; 165 } 166 167 public GpoRuleConfigured EnabledGPOConfiguration 168 { 169 get => _enabledGpoRuleConfiguration; 170 set 171 { 172 if (_enabledGpoRuleConfiguration != value) 173 { 174 _enabledGpoRuleConfiguration = value; 175 NotifyPropertyChanged(); 176 } 177 } 178 } 179 180 public string ScheduleMode 181 { 182 get => ModuleSettings.Properties.ScheduleMode.Value; 183 set 184 { 185 var oldMode = ModuleSettings.Properties.ScheduleMode.Value; 186 if (ModuleSettings.Properties.ScheduleMode.Value != value) 187 { 188 ModuleSettings.Properties.ScheduleMode.Value = value; 189 OnPropertyChanged(nameof(ScheduleMode)); 190 } 191 192 if (ModuleSettings.Properties.ScheduleMode.Value == "FixedHours" && oldMode != "FixedHours") 193 { 194 LightTime = 360; 195 DarkTime = 1080; 196 SunsetTimeSpan = null; 197 SunriseTimeSpan = null; 198 199 OnPropertyChanged(nameof(LightTimePickerValue)); 200 OnPropertyChanged(nameof(DarkTimePickerValue)); 201 } 202 203 if (ModuleSettings.Properties.ScheduleMode.Value == "SunsetToSunrise") 204 { 205 if (ModuleSettings.Properties.Latitude != "0.0" && ModuleSettings.Properties.Longitude != "0.0") 206 { 207 double lat = double.Parse(ModuleSettings.Properties.Latitude.Value, CultureInfo.InvariantCulture); 208 double lon = double.Parse(ModuleSettings.Properties.Longitude.Value, CultureInfo.InvariantCulture); 209 UpdateSunTimes(lat, lon); 210 } 211 } 212 } 213 } 214 215 public ObservableCollection<string> AvailableScheduleModes { get; } 216 217 public bool ChangeSystem 218 { 219 get => ModuleSettings.Properties.ChangeSystem.Value; 220 set 221 { 222 if (ModuleSettings.Properties.ChangeSystem.Value != value) 223 { 224 ModuleSettings.Properties.ChangeSystem.Value = value; 225 NotifyPropertyChanged(); 226 } 227 } 228 } 229 230 public bool ChangeApps 231 { 232 get => ModuleSettings.Properties.ChangeApps.Value; 233 set 234 { 235 if (ModuleSettings.Properties.ChangeApps.Value != value) 236 { 237 ModuleSettings.Properties.ChangeApps.Value = value; 238 NotifyPropertyChanged(); 239 } 240 } 241 } 242 243 public int LightTime 244 { 245 get => ModuleSettings.Properties.LightTime.Value; 246 set 247 { 248 if (ModuleSettings.Properties.LightTime.Value != value) 249 { 250 ModuleSettings.Properties.LightTime.Value = value; 251 NotifyPropertyChanged(); 252 253 OnPropertyChanged(nameof(LightTimeTimeSpan)); 254 OnPropertyChanged(nameof(SunriseOffsetMin)); 255 OnPropertyChanged(nameof(SunsetOffsetMin)); 256 257 if (ScheduleMode == "SunsetToSunrise") 258 { 259 SunriseTimeSpan = TimeSpan.FromMinutes(value); 260 } 261 } 262 } 263 } 264 265 public int DarkTime 266 { 267 get => ModuleSettings.Properties.DarkTime.Value; 268 set 269 { 270 if (ModuleSettings.Properties.DarkTime.Value != value) 271 { 272 ModuleSettings.Properties.DarkTime.Value = value; 273 NotifyPropertyChanged(); 274 275 OnPropertyChanged(nameof(DarkTimeTimeSpan)); 276 OnPropertyChanged(nameof(SunriseOffsetMax)); 277 OnPropertyChanged(nameof(SunsetOffsetMax)); 278 279 if (ScheduleMode == "SunsetToSunrise") 280 { 281 SunsetTimeSpan = TimeSpan.FromMinutes(value); 282 } 283 } 284 } 285 } 286 287 public int SunriseOffset 288 { 289 get => ModuleSettings.Properties.SunriseOffset.Value; 290 set 291 { 292 if (ModuleSettings.Properties.SunriseOffset.Value != value) 293 { 294 ModuleSettings.Properties.SunriseOffset.Value = value; 295 OnPropertyChanged(nameof(LightTimeTimeSpan)); 296 OnPropertyChanged(nameof(SunsetOffsetMin)); 297 } 298 } 299 } 300 301 public int SunsetOffset 302 { 303 get => ModuleSettings.Properties.SunsetOffset.Value; 304 set 305 { 306 if (ModuleSettings.Properties.SunsetOffset.Value != value) 307 { 308 ModuleSettings.Properties.SunsetOffset.Value = value; 309 OnPropertyChanged(nameof(DarkTimeTimeSpan)); 310 OnPropertyChanged(nameof(SunriseOffsetMax)); 311 } 312 } 313 } 314 315 public int SunriseOffsetMin 316 { 317 get 318 { 319 // Minimum: don't let adjusted sunrise go before 00:00 320 return -LightTime; 321 } 322 } 323 324 public int SunriseOffsetMax 325 { 326 get 327 { 328 // Maximum: adjusted sunrise must stay before adjusted sunset 329 int adjustedSunset = DarkTime + SunsetOffset; 330 return Math.Max(0, adjustedSunset - LightTime - 1); 331 } 332 } 333 334 public int SunsetOffsetMin 335 { 336 get 337 { 338 // Minimum: adjusted sunset must stay after adjusted sunrise 339 int adjustedSunrise = LightTime + SunriseOffset; 340 return Math.Min(0, adjustedSunrise - DarkTime + 1); 341 } 342 } 343 344 public int SunsetOffsetMax 345 { 346 get 347 { 348 // Maximum: don't let adjusted sunset go past 23:59 (1439 minutes) 349 return 1439 - DarkTime; 350 } 351 } 352 353 // === Computed projections (OneWay bindings only) === 354 public TimeSpan LightTimeTimeSpan 355 { 356 get 357 { 358 if (ScheduleMode == "SunsetToSunrise") 359 { 360 return TimeSpan.FromMinutes(LightTime + SunriseOffset); 361 } 362 else 363 { 364 return TimeSpan.FromMinutes(LightTime); 365 } 366 } 367 } 368 369 public TimeSpan DarkTimeTimeSpan 370 { 371 get 372 { 373 if (ScheduleMode == "SunsetToSunrise") 374 { 375 return TimeSpan.FromMinutes(DarkTime + SunsetOffset); 376 } 377 else 378 { 379 return TimeSpan.FromMinutes(DarkTime); 380 } 381 } 382 } 383 384 // === Values to pass to timeline === 385 public TimeSpan? SunriseTimeSpan 386 { 387 get => _sunriseTimeSpan; 388 set 389 { 390 if (_sunriseTimeSpan != value) 391 { 392 _sunriseTimeSpan = value; 393 NotifyPropertyChanged(); 394 } 395 } 396 } 397 398 public TimeSpan? SunsetTimeSpan 399 { 400 get => _sunsetTimeSpan; 401 set 402 { 403 if (_sunsetTimeSpan != value) 404 { 405 _sunsetTimeSpan = value; 406 NotifyPropertyChanged(); 407 } 408 } 409 } 410 411 // === Picker values (TwoWay binding targets for TimePickers) === 412 public TimeSpan LightTimePickerValue 413 { 414 get => TimeSpan.FromMinutes(LightTime); 415 set => LightTime = (int)value.TotalMinutes; 416 } 417 418 public TimeSpan DarkTimePickerValue 419 { 420 get => TimeSpan.FromMinutes(DarkTime); 421 set => DarkTime = (int)value.TotalMinutes; 422 } 423 424 public string Latitude 425 { 426 get => ModuleSettings.Properties.Latitude.Value; 427 set 428 { 429 if (ModuleSettings.Properties.Latitude.Value != value) 430 { 431 ModuleSettings.Properties.Latitude.Value = value; 432 NotifyPropertyChanged(); 433 } 434 } 435 } 436 437 public string Longitude 438 { 439 get => ModuleSettings.Properties.Longitude.Value; 440 set 441 { 442 if (ModuleSettings.Properties.Longitude.Value != value) 443 { 444 ModuleSettings.Properties.Longitude.Value = value; 445 NotifyPropertyChanged(); 446 } 447 } 448 } 449 450 private SearchLocation? _selectedSearchLocation; 451 452 public SearchLocation? SelectedCity 453 { 454 get => _selectedSearchLocation; 455 set 456 { 457 if (_selectedSearchLocation != value) 458 { 459 _selectedSearchLocation = value; 460 NotifyPropertyChanged(); 461 462 if (_selectedSearchLocation != null) 463 { 464 UpdateSunTimes(_selectedSearchLocation.Latitude, _selectedSearchLocation.Longitude, _selectedSearchLocation.City); 465 } 466 } 467 } 468 } 469 470 private string _syncButtonInformation = "Please sync your location"; 471 472 public string SyncButtonInformation 473 { 474 get => _syncButtonInformation; 475 set 476 { 477 if (_syncButtonInformation != value) 478 { 479 _syncButtonInformation = value; 480 NotifyPropertyChanged(); 481 } 482 } 483 } 484 485 private double _locationPanelLatitude; 486 private double _locationPanelLongitude; 487 488 public double LocationPanelLatitude 489 { 490 get => _locationPanelLatitude; 491 set 492 { 493 if (_locationPanelLatitude != value) 494 { 495 _locationPanelLatitude = value; 496 NotifyPropertyChanged(); 497 NotifyPropertyChanged(nameof(LocationPanelLightTime)); 498 } 499 } 500 } 501 502 public double LocationPanelLongitude 503 { 504 get => _locationPanelLongitude; 505 set 506 { 507 if (_locationPanelLongitude != value) 508 { 509 _locationPanelLongitude = value; 510 NotifyPropertyChanged(); 511 } 512 } 513 } 514 515 private int _locationPanelLightTime; 516 private int _locationPanelDarkTime; 517 518 public int LocationPanelLightTimeMinutes 519 { 520 get => _locationPanelLightTime; 521 set 522 { 523 if (_locationPanelLightTime != value) 524 { 525 _locationPanelLightTime = value; 526 NotifyPropertyChanged(); 527 NotifyPropertyChanged(nameof(LocationPanelLightTime)); 528 } 529 } 530 } 531 532 public int LocationPanelDarkTimeMinutes 533 { 534 get => _locationPanelDarkTime; 535 set 536 { 537 if (_locationPanelDarkTime != value) 538 { 539 _locationPanelDarkTime = value; 540 NotifyPropertyChanged(); 541 NotifyPropertyChanged(nameof(LocationPanelDarkTime)); 542 } 543 } 544 } 545 546 public TimeSpan LocationPanelLightTime => TimeSpan.FromMinutes(_locationPanelLightTime); 547 548 public TimeSpan LocationPanelDarkTime => TimeSpan.FromMinutes(_locationPanelDarkTime); 549 550 public HotkeySettings ToggleThemeActivationShortcut 551 { 552 get => ModuleSettings.Properties.ToggleThemeHotkey.Value; 553 554 set 555 { 556 if (value != ModuleSettings.Properties.ToggleThemeHotkey.Value) 557 { 558 if (value == null) 559 { 560 ModuleSettings.Properties.ToggleThemeHotkey.Value = LightSwitchProperties.DefaultToggleThemeHotkey; 561 } 562 else 563 { 564 ModuleSettings.Properties.ToggleThemeHotkey.Value = value; 565 } 566 567 NotifyPropertyChanged(); 568 569 SendConfigMSG( 570 string.Format( 571 CultureInfo.InvariantCulture, 572 "{{ \"powertoys\": {{ \"{0}\": {1} }} }}", 573 LightSwitchSettings.ModuleName, 574 JsonSerializer.Serialize(_moduleSettings, SourceGenerationContextContext.Default.LightSwitchSettings))); 575 } 576 } 577 } 578 579 public void NotifyPropertyChanged([CallerMemberName] string? propertyName = null) 580 { 581 Logger.LogInfo($"Changed the property {propertyName}"); 582 OnPropertyChanged(propertyName); 583 } 584 585 // PowerDisplay Integration Properties and Methods 586 public ObservableCollection<PowerDisplayProfile> AvailableProfiles 587 { 588 get => _availableProfiles; 589 set 590 { 591 if (_availableProfiles != value) 592 { 593 _availableProfiles = value; 594 NotifyPropertyChanged(); 595 } 596 } 597 } 598 599 public bool IsPowerDisplayEnabled 600 { 601 get => _isPowerDisplayEnabled; 602 set 603 { 604 if (_isPowerDisplayEnabled != value) 605 { 606 _isPowerDisplayEnabled = value; 607 NotifyPropertyChanged(); 608 NotifyPropertyChanged(nameof(ShowPowerDisplayDisabledWarning)); 609 } 610 } 611 } 612 613 public bool ShowPowerDisplayDisabledWarning => !IsPowerDisplayEnabled; 614 615 public bool EnableDarkModeProfile 616 { 617 get => ModuleSettings.Properties.EnableDarkModeProfile.Value; 618 set 619 { 620 if (ModuleSettings.Properties.EnableDarkModeProfile.Value != value) 621 { 622 ModuleSettings.Properties.EnableDarkModeProfile.Value = value; 623 NotifyPropertyChanged(); 624 NotifyPropertyChanged(nameof(ShowPowerDisplayDisabledWarning)); 625 SaveSettings(); 626 } 627 } 628 } 629 630 public bool EnableLightModeProfile 631 { 632 get => ModuleSettings.Properties.EnableLightModeProfile.Value; 633 set 634 { 635 if (ModuleSettings.Properties.EnableLightModeProfile.Value != value) 636 { 637 ModuleSettings.Properties.EnableLightModeProfile.Value = value; 638 NotifyPropertyChanged(); 639 NotifyPropertyChanged(nameof(ShowPowerDisplayDisabledWarning)); 640 SaveSettings(); 641 } 642 } 643 } 644 645 public PowerDisplayProfile? SelectedDarkModeProfile 646 { 647 get => _selectedDarkModeProfile; 648 set 649 { 650 if (_selectedDarkModeProfile != value) 651 { 652 _selectedDarkModeProfile = value; 653 654 // Sync with the string property stored in settings 655 var newProfileName = value?.Name ?? string.Empty; 656 if (ModuleSettings.Properties.DarkModeProfile.Value != newProfileName) 657 { 658 ModuleSettings.Properties.DarkModeProfile.Value = newProfileName; 659 SaveSettings(); 660 } 661 662 NotifyPropertyChanged(); 663 } 664 } 665 } 666 667 public PowerDisplayProfile? SelectedLightModeProfile 668 { 669 get => _selectedLightModeProfile; 670 set 671 { 672 if (_selectedLightModeProfile != value) 673 { 674 _selectedLightModeProfile = value; 675 676 // Sync with the string property stored in settings 677 var newProfileName = value?.Name ?? string.Empty; 678 if (ModuleSettings.Properties.LightModeProfile.Value != newProfileName) 679 { 680 ModuleSettings.Properties.LightModeProfile.Value = newProfileName; 681 SaveSettings(); 682 } 683 684 NotifyPropertyChanged(); 685 } 686 } 687 } 688 689 // Legacy string properties for backwards compatibility with settings persistence 690 public string DarkModeProfile 691 { 692 get => ModuleSettings.Properties.DarkModeProfile.Value; 693 set 694 { 695 if (ModuleSettings.Properties.DarkModeProfile.Value != value) 696 { 697 ModuleSettings.Properties.DarkModeProfile.Value = value; 698 699 // Sync with the object property 700 UpdateSelectedProfileFromName(value, isDarkMode: true); 701 702 NotifyPropertyChanged(); 703 } 704 } 705 } 706 707 public string LightModeProfile 708 { 709 get => ModuleSettings.Properties.LightModeProfile.Value; 710 set 711 { 712 if (ModuleSettings.Properties.LightModeProfile.Value != value) 713 { 714 ModuleSettings.Properties.LightModeProfile.Value = value; 715 716 // Sync with the object property 717 UpdateSelectedProfileFromName(value, isDarkMode: false); 718 719 NotifyPropertyChanged(); 720 } 721 } 722 } 723 724 private void LoadPowerDisplayProfiles() 725 { 726 try 727 { 728 var profilesData = ProfileService.LoadProfiles(); 729 730 AvailableProfiles.Clear(); 731 732 foreach (var profile in profilesData.Profiles) 733 { 734 AvailableProfiles.Add(profile); 735 } 736 737 Logger.LogInfo($"Loaded {profilesData.Profiles.Count} PowerDisplay profiles"); 738 739 // Sync selected profiles from settings 740 UpdateSelectedProfileFromName(ModuleSettings.Properties.DarkModeProfile.Value, isDarkMode: true); 741 UpdateSelectedProfileFromName(ModuleSettings.Properties.LightModeProfile.Value, isDarkMode: false); 742 } 743 catch (Exception ex) 744 { 745 Logger.LogError($"Failed to load PowerDisplay profiles: {ex.Message}"); 746 AvailableProfiles.Clear(); 747 } 748 } 749 750 /// <summary> 751 /// Helper method to sync the selected profile object from the profile name stored in settings. 752 /// If the configured profile no longer exists, clears the selection and updates settings. 753 /// </summary> 754 private void UpdateSelectedProfileFromName(string profileName, bool isDarkMode) 755 { 756 PowerDisplayProfile? matchingProfile = null; 757 758 if (!string.IsNullOrEmpty(profileName)) 759 { 760 matchingProfile = AvailableProfiles.FirstOrDefault(p => 761 p.Name.Equals(profileName, StringComparison.OrdinalIgnoreCase)); 762 763 // If the configured profile no longer exists, clear it from settings 764 if (matchingProfile == null) 765 { 766 Logger.LogWarning($"Configured {(isDarkMode ? "dark" : "light")} mode profile '{profileName}' no longer exists, clearing selection"); 767 768 if (isDarkMode) 769 { 770 ModuleSettings.Properties.DarkModeProfile.Value = string.Empty; 771 } 772 else 773 { 774 ModuleSettings.Properties.LightModeProfile.Value = string.Empty; 775 } 776 777 SaveSettings(); 778 } 779 } 780 781 if (isDarkMode) 782 { 783 if (_selectedDarkModeProfile != matchingProfile) 784 { 785 _selectedDarkModeProfile = matchingProfile; 786 NotifyPropertyChanged(nameof(SelectedDarkModeProfile)); 787 } 788 } 789 else 790 { 791 if (_selectedLightModeProfile != matchingProfile) 792 { 793 _selectedLightModeProfile = matchingProfile; 794 NotifyPropertyChanged(nameof(SelectedLightModeProfile)); 795 } 796 } 797 } 798 799 private void CheckPowerDisplayEnabled() 800 { 801 try 802 { 803 var settingsUtils = SettingsUtils.Default; 804 var generalSettings = settingsUtils.GetSettingsOrDefault<GeneralSettings>(string.Empty); 805 IsPowerDisplayEnabled = generalSettings?.Enabled?.PowerDisplay ?? false; 806 Logger.LogInfo($"PowerDisplay enabled status: {IsPowerDisplayEnabled}"); 807 } 808 catch (Exception ex) 809 { 810 Logger.LogError($"Failed to check PowerDisplay enabled status: {ex.Message}"); 811 IsPowerDisplayEnabled = false; 812 } 813 } 814 815 public void RefreshPowerDisplayStatus() 816 { 817 CheckPowerDisplayEnabled(); 818 NotifyPropertyChanged(nameof(ShowPowerDisplayDisabledWarning)); 819 } 820 821 public void RefreshEnabledState() 822 { 823 OnPropertyChanged(nameof(IsEnabled)); 824 } 825 826 public void RefreshModuleSettings() 827 { 828 OnPropertyChanged(nameof(ChangeSystem)); 829 OnPropertyChanged(nameof(ChangeApps)); 830 OnPropertyChanged(nameof(LightTime)); 831 OnPropertyChanged(nameof(DarkTime)); 832 OnPropertyChanged(nameof(SunriseOffset)); 833 OnPropertyChanged(nameof(SunsetOffset)); 834 OnPropertyChanged(nameof(Latitude)); 835 OnPropertyChanged(nameof(Longitude)); 836 OnPropertyChanged(nameof(ScheduleMode)); 837 OnPropertyChanged(nameof(EnableDarkModeProfile)); 838 OnPropertyChanged(nameof(EnableLightModeProfile)); 839 OnPropertyChanged(nameof(DarkModeProfile)); 840 OnPropertyChanged(nameof(LightModeProfile)); 841 } 842 843 private void UpdateSunTimes(double latitude, double longitude, string city = "n/a") 844 { 845 SunTimes result = SunCalc.CalculateSunriseSunset( 846 latitude, 847 longitude, 848 DateTime.Now.Year, 849 DateTime.Now.Month, 850 DateTime.Now.Day); 851 852 LightTime = (result.SunriseHour * 60) + result.SunriseMinute; 853 DarkTime = (result.SunsetHour * 60) + result.SunsetMinute; 854 Latitude = latitude.ToString(CultureInfo.InvariantCulture); 855 Longitude = longitude.ToString(CultureInfo.InvariantCulture); 856 857 if (city != "n/a") 858 { 859 SyncButtonInformation = city; 860 } 861 } 862 863 public void InitializeScheduleMode() 864 { 865 if (ScheduleMode == "SunsetToSunrise" && 866 double.TryParse(Latitude, NumberStyles.Float, CultureInfo.InvariantCulture, out double savedLat) && 867 double.TryParse(Longitude, NumberStyles.Float, CultureInfo.InvariantCulture, out double savedLng)) 868 { 869 var match = SearchLocations.FirstOrDefault(c => 870 Math.Abs(c.Latitude - savedLat) < 0.0001 && 871 Math.Abs(c.Longitude - savedLng) < 0.0001); 872 873 if (match != null) 874 { 875 SelectedCity = match; 876 } 877 878 SyncButtonInformation = SelectedCity != null 879 ? SelectedCity.City 880 : $"{Latitude}°,{Longitude}°"; 881 882 double lat = double.Parse(ModuleSettings.Properties.Latitude.Value, CultureInfo.InvariantCulture); 883 double lon = double.Parse(ModuleSettings.Properties.Longitude.Value, CultureInfo.InvariantCulture); 884 UpdateSunTimes(lat, lon); 885 886 SunriseTimeSpan = TimeSpan.FromMinutes(LightTime); 887 SunsetTimeSpan = TimeSpan.FromMinutes(DarkTime); 888 } 889 } 890 891 private bool _enabledStateIsGPOConfigured; 892 private GpoRuleConfigured _enabledGpoRuleConfiguration; 893 private LightSwitchSettings _moduleSettings; 894 private bool _isEnabled; 895 private TimeSpan? _sunriseTimeSpan; 896 private TimeSpan? _sunsetTimeSpan; 897 898 // PowerDisplay integration 899 private ObservableCollection<PowerDisplayProfile> _availableProfiles = new ObservableCollection<PowerDisplayProfile>(); 900 private bool _isPowerDisplayEnabled; 901 private PowerDisplayProfile? _selectedDarkModeProfile; 902 private PowerDisplayProfile? _selectedLightModeProfile; 903 904 public ICommand ForceLightCommand { get; } 905 906 public ICommand ForceDarkCommand { get; } 907 } 908 }