/ src / settings-ui / Settings.UI / ViewModels / LightSwitchViewModel.cs
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  }