- Pin .NET SDK to 8.0.424 in all CI jobs to avoid floating channel drift - Install SDK to runner temp via DOTNET_INSTALL_DIR to bypass C:\\Program Files\\dotnet overwrite/extract conflicts on Gitea Windows runners - Simplify release job to dotnet publish + upload-artifact - Exclude memory-bank/ via .gitignore (internal dev memory stays local)
141 lines
4.6 KiB
C#
141 lines
4.6 KiB
C#
using System.IO;
|
|
using System.Text.Json;
|
|
using RgbLedStripController.Core;
|
|
using MediaColor = System.Windows.Media.Color;
|
|
|
|
namespace RgbLedStripController.Services;
|
|
|
|
public sealed class SettingsStore
|
|
{
|
|
private readonly string _settingsPath;
|
|
private readonly SemaphoreSlim _writeLock = new(1, 1);
|
|
private readonly JsonSerializerOptions _jsonOptions = new()
|
|
{
|
|
WriteIndented = true,
|
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
|
};
|
|
|
|
public SettingsStore()
|
|
{
|
|
var settingsDirectory = Path.Combine(
|
|
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
|
"RgbLedStripController");
|
|
|
|
_settingsPath = Path.Combine(settingsDirectory, "settings.json");
|
|
}
|
|
|
|
public async Task<SettingsSnapshot> LoadAsync(CancellationToken cancellationToken = default)
|
|
{
|
|
if (!File.Exists(_settingsPath))
|
|
{
|
|
return CreateDefaultSnapshot();
|
|
}
|
|
|
|
try
|
|
{
|
|
await using var stream = File.OpenRead(_settingsPath);
|
|
var stored = await JsonSerializer.DeserializeAsync<StoredSettings>(stream, _jsonOptions, cancellationToken);
|
|
|
|
return new SettingsSnapshot(
|
|
stored?.KnownDevices ?? [],
|
|
new Dictionary<string, string>(stored?.DeviceAliases ?? new Dictionary<string, string>(), StringComparer.OrdinalIgnoreCase),
|
|
ParseColor(stored?.SelectedColorHex),
|
|
stored?.Brightness ?? 100,
|
|
stored?.EffectSpeed ?? 50,
|
|
stored?.CurrentMode ?? LightingMode.Rgb,
|
|
stored?.GrayscaleLevel ?? 100,
|
|
stored?.TemperatureLevel ?? 50,
|
|
stored?.SelectedEffectId ?? 0,
|
|
stored?.IsPoweredOn ?? false);
|
|
}
|
|
catch
|
|
{
|
|
return CreateDefaultSnapshot();
|
|
}
|
|
}
|
|
|
|
public async Task SaveAsync(SettingsSnapshot snapshot, CancellationToken cancellationToken = default)
|
|
{
|
|
await _writeLock.WaitAsync(cancellationToken);
|
|
|
|
try
|
|
{
|
|
var directory = Path.GetDirectoryName(_settingsPath);
|
|
if (!string.IsNullOrWhiteSpace(directory))
|
|
{
|
|
Directory.CreateDirectory(directory);
|
|
}
|
|
|
|
await using var stream = File.Create(_settingsPath);
|
|
var stored = new StoredSettings
|
|
{
|
|
KnownDevices = snapshot.KnownDevices.ToList(),
|
|
DeviceAliases = new Dictionary<string, string>(snapshot.DeviceAliases, StringComparer.OrdinalIgnoreCase),
|
|
SelectedColorHex = $"#{snapshot.SelectedColor.R:X2}{snapshot.SelectedColor.G:X2}{snapshot.SelectedColor.B:X2}",
|
|
Brightness = snapshot.Brightness,
|
|
EffectSpeed = snapshot.EffectSpeed,
|
|
CurrentMode = snapshot.CurrentMode,
|
|
GrayscaleLevel = snapshot.GrayscaleLevel,
|
|
TemperatureLevel = snapshot.TemperatureLevel,
|
|
SelectedEffectId = snapshot.SelectedEffectId,
|
|
IsPoweredOn = snapshot.IsPoweredOn
|
|
};
|
|
|
|
await JsonSerializer.SerializeAsync(stream, stored, _jsonOptions, cancellationToken);
|
|
}
|
|
finally
|
|
{
|
|
_writeLock.Release();
|
|
}
|
|
}
|
|
|
|
private static SettingsSnapshot CreateDefaultSnapshot()
|
|
=> new(
|
|
[],
|
|
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase),
|
|
MediaColor.FromRgb(255, 106, 78),
|
|
100,
|
|
50,
|
|
LightingMode.Rgb,
|
|
100,
|
|
50,
|
|
0,
|
|
false);
|
|
|
|
private static MediaColor ParseColor(string? value)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(value) || value.Length != 7 || value[0] != '#')
|
|
{
|
|
return MediaColor.FromRgb(255, 106, 78);
|
|
}
|
|
|
|
return MediaColor.FromRgb(
|
|
Convert.ToByte(value.Substring(1, 2), 16),
|
|
Convert.ToByte(value.Substring(3, 2), 16),
|
|
Convert.ToByte(value.Substring(5, 2), 16));
|
|
}
|
|
|
|
private sealed class StoredSettings
|
|
{
|
|
public List<DeviceProfile> KnownDevices { get; set; } = [];
|
|
|
|
public Dictionary<string, string> DeviceAliases { get; set; } = new(StringComparer.OrdinalIgnoreCase);
|
|
|
|
public string? SelectedColorHex { get; set; }
|
|
|
|
public byte Brightness { get; set; }
|
|
|
|
public byte EffectSpeed { get; set; }
|
|
|
|
public LightingMode CurrentMode { get; set; }
|
|
|
|
public byte GrayscaleLevel { get; set; }
|
|
|
|
public byte TemperatureLevel { get; set; }
|
|
|
|
public byte SelectedEffectId { get; set; }
|
|
|
|
public bool IsPoweredOn { get; set; }
|
|
}
|
|
}
|