From eb21149682f1c96c472f68ec7a4dd5326008161b Mon Sep 17 00:00:00 2001 From: nopoz Date: Wed, 7 Jan 2026 12:54:59 -0800 Subject: [PATCH 1/8] Add ShowTemperatureUnit setting to hide F/C from weather display --- ImmichFrame.Core/Interfaces/IServerSettings.cs | 1 + ImmichFrame.WebApi.Tests/Resources/TestV2.json | 1 + ImmichFrame.WebApi.Tests/Resources/TestV2.yml | 1 + .../Helpers/Config/ServerSettingsV1.cs | 1 + ImmichFrame.WebApi/Models/ClientSettingsDto.cs | 2 ++ ImmichFrame.WebApi/Models/ServerSettings.cs | 1 + .../src/lib/components/elements/clock.svelte | 12 ++++++------ immichFrame.Web/src/lib/immichFrameApi.ts | 1 + 8 files changed, 14 insertions(+), 6 deletions(-) diff --git a/ImmichFrame.Core/Interfaces/IServerSettings.cs b/ImmichFrame.Core/Interfaces/IServerSettings.cs index fea6c442..0f4f1340 100644 --- a/ImmichFrame.Core/Interfaces/IServerSettings.cs +++ b/ImmichFrame.Core/Interfaces/IServerSettings.cs @@ -59,6 +59,7 @@ public interface IGeneralSettings public string Style { get; } public string? BaseFontSize { get; } public bool ShowWeatherDescription { get; } + public bool ShowTemperatureUnit { get; } public string? WeatherIconUrl { get; } public bool ImageZoom { get; } public bool ImagePan { get; } diff --git a/ImmichFrame.WebApi.Tests/Resources/TestV2.json b/ImmichFrame.WebApi.Tests/Resources/TestV2.json index 4d603dc9..5c361070 100644 --- a/ImmichFrame.WebApi.Tests/Resources/TestV2.json +++ b/ImmichFrame.WebApi.Tests/Resources/TestV2.json @@ -30,6 +30,7 @@ "Style": "Style_TEST", "BaseFontSize": "BaseFontSize_TEST", "ShowWeatherDescription": true, + "ShowTemperatureUnit": true, "WeatherIconUrl": "WeatherIconUrl_TEST", "ImageZoom": true, "ImagePan": true, diff --git a/ImmichFrame.WebApi.Tests/Resources/TestV2.yml b/ImmichFrame.WebApi.Tests/Resources/TestV2.yml index 47f45947..7ab9e5ca 100644 --- a/ImmichFrame.WebApi.Tests/Resources/TestV2.yml +++ b/ImmichFrame.WebApi.Tests/Resources/TestV2.yml @@ -29,6 +29,7 @@ General: Style: Style_TEST BaseFontSize: BaseFontSize_TEST ShowWeatherDescription: true + ShowTemperatureUnit: true WeatherIconUrl: WeatherIconUrl_TEST ImageZoom: true ImagePan: true diff --git a/ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs b/ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs index 076f36da..01102d25 100644 --- a/ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs +++ b/ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs @@ -128,6 +128,7 @@ class GeneralSettingsV1Adapter(ServerSettingsV1 _delegate) : IGeneralSettings public string Style => _delegate.Style; public string? BaseFontSize => _delegate.BaseFontSize; public bool ShowWeatherDescription => _delegate.ShowWeatherDescription; + public bool ShowTemperatureUnit => true; public string? WeatherIconUrl => _delegate.WeatherIconUrl; public bool ImageZoom => _delegate.ImageZoom; public bool ImagePan => _delegate.ImagePan; diff --git a/ImmichFrame.WebApi/Models/ClientSettingsDto.cs b/ImmichFrame.WebApi/Models/ClientSettingsDto.cs index ff0f9e75..ffa82cac 100644 --- a/ImmichFrame.WebApi/Models/ClientSettingsDto.cs +++ b/ImmichFrame.WebApi/Models/ClientSettingsDto.cs @@ -25,6 +25,7 @@ public class ClientSettingsDto public string Style { get; set; } public string? BaseFontSize { get; set; } public bool ShowWeatherDescription { get; set; } + public bool ShowTemperatureUnit { get; set; } public string? WeatherIconUrl { get; set; } public bool ImageZoom { get; set; } public bool ImagePan { get; set; } @@ -57,6 +58,7 @@ public static ClientSettingsDto FromGeneralSettings(IGeneralSettings generalSett dto.Style = generalSettings.Style; dto.BaseFontSize = generalSettings.BaseFontSize; dto.ShowWeatherDescription = generalSettings.ShowWeatherDescription; + dto.ShowTemperatureUnit = generalSettings.ShowTemperatureUnit; dto.WeatherIconUrl = generalSettings.WeatherIconUrl; dto.ImageZoom = generalSettings.ImageZoom; dto.ImagePan = generalSettings.ImagePan; diff --git a/ImmichFrame.WebApi/Models/ServerSettings.cs b/ImmichFrame.WebApi/Models/ServerSettings.cs index 74d0fb8e..a2220e2c 100644 --- a/ImmichFrame.WebApi/Models/ServerSettings.cs +++ b/ImmichFrame.WebApi/Models/ServerSettings.cs @@ -58,6 +58,7 @@ public class GeneralSettings : IGeneralSettings, IConfigSettable public string Style { get; set; } = "none"; public string? BaseFontSize { get; set; } public bool ShowWeatherDescription { get; set; } = true; + public bool ShowTemperatureUnit { get; set; } = true; public string? WeatherIconUrl { get; set; } = "https://openweathermap.org/img/wn/{IconId}.png"; public bool ImageZoom { get; set; } = true; public bool ImagePan { get; set; } = false; diff --git a/immichFrame.Web/src/lib/components/elements/clock.svelte b/immichFrame.Web/src/lib/components/elements/clock.svelte index a19ab602..57e76a8b 100644 --- a/immichFrame.Web/src/lib/components/elements/clock.svelte +++ b/immichFrame.Web/src/lib/components/elements/clock.svelte @@ -82,18 +82,18 @@ class="text-xl sm:text-xl md:text-2xl lg:text-3xl font-semibold text-shadow-sm weather-info" > {#if $configStore.weatherIconUrl && primaryIconId()} - {weather.description} {/if} - +
{weather.location},
{weather.temperature?.toFixed(1)}
-
{weather.unit}
+
{$configStore.showTemperatureUnit === false ? '°' : (weather.unit ?? '°')}
- + {#if $configStore.showWeatherDescription}

{weather.description} diff --git a/immichFrame.Web/src/lib/immichFrameApi.ts b/immichFrame.Web/src/lib/immichFrameApi.ts index e8dae934..fa792a18 100644 --- a/immichFrame.Web/src/lib/immichFrameApi.ts +++ b/immichFrame.Web/src/lib/immichFrameApi.ts @@ -207,6 +207,7 @@ export type ClientSettingsDto = { style?: string | null; baseFontSize?: string | null; showWeatherDescription?: boolean; + showTemperatureUnit?: boolean; weatherIconUrl?: string | null; imageZoom?: boolean; imagePan?: boolean; From bab544b87c2c75bb35d715f3cfb5526b941ad34a Mon Sep 17 00:00:00 2001 From: nopoz Date: Tue, 6 Jan 2026 16:51:34 -0800 Subject: [PATCH 2/8] Add settings option for whole number temperatures to simplify display --- ImmichFrame.Core/Interfaces/IServerSettings.cs | 1 + ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs | 1 + ImmichFrame.WebApi/Models/ClientSettingsDto.cs | 2 ++ ImmichFrame.WebApi/Models/ServerSettings.cs | 1 + immichFrame.Web/src/lib/components/elements/clock.svelte | 2 +- immichFrame.Web/src/lib/immichFrameApi.ts | 1 + 6 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ImmichFrame.Core/Interfaces/IServerSettings.cs b/ImmichFrame.Core/Interfaces/IServerSettings.cs index 0f4f1340..0264d71e 100644 --- a/ImmichFrame.Core/Interfaces/IServerSettings.cs +++ b/ImmichFrame.Core/Interfaces/IServerSettings.cs @@ -60,6 +60,7 @@ public interface IGeneralSettings public string? BaseFontSize { get; } public bool ShowWeatherDescription { get; } public bool ShowTemperatureUnit { get; } + public bool UseWholeNumberTemperatures { get; } public string? WeatherIconUrl { get; } public bool ImageZoom { get; } public bool ImagePan { get; } diff --git a/ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs b/ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs index 01102d25..ddaaec38 100644 --- a/ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs +++ b/ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs @@ -129,6 +129,7 @@ class GeneralSettingsV1Adapter(ServerSettingsV1 _delegate) : IGeneralSettings public string? BaseFontSize => _delegate.BaseFontSize; public bool ShowWeatherDescription => _delegate.ShowWeatherDescription; public bool ShowTemperatureUnit => true; + public bool UseWholeNumberTemperatures => false; public string? WeatherIconUrl => _delegate.WeatherIconUrl; public bool ImageZoom => _delegate.ImageZoom; public bool ImagePan => _delegate.ImagePan; diff --git a/ImmichFrame.WebApi/Models/ClientSettingsDto.cs b/ImmichFrame.WebApi/Models/ClientSettingsDto.cs index ffa82cac..932e52a4 100644 --- a/ImmichFrame.WebApi/Models/ClientSettingsDto.cs +++ b/ImmichFrame.WebApi/Models/ClientSettingsDto.cs @@ -26,6 +26,7 @@ public class ClientSettingsDto public string? BaseFontSize { get; set; } public bool ShowWeatherDescription { get; set; } public bool ShowTemperatureUnit { get; set; } + public bool UseWholeNumberTemperatures { get; set; } public string? WeatherIconUrl { get; set; } public bool ImageZoom { get; set; } public bool ImagePan { get; set; } @@ -59,6 +60,7 @@ public static ClientSettingsDto FromGeneralSettings(IGeneralSettings generalSett dto.BaseFontSize = generalSettings.BaseFontSize; dto.ShowWeatherDescription = generalSettings.ShowWeatherDescription; dto.ShowTemperatureUnit = generalSettings.ShowTemperatureUnit; + dto.UseWholeNumberTemperatures = generalSettings.UseWholeNumberTemperatures; dto.WeatherIconUrl = generalSettings.WeatherIconUrl; dto.ImageZoom = generalSettings.ImageZoom; dto.ImagePan = generalSettings.ImagePan; diff --git a/ImmichFrame.WebApi/Models/ServerSettings.cs b/ImmichFrame.WebApi/Models/ServerSettings.cs index a2220e2c..37b1bc13 100644 --- a/ImmichFrame.WebApi/Models/ServerSettings.cs +++ b/ImmichFrame.WebApi/Models/ServerSettings.cs @@ -59,6 +59,7 @@ public class GeneralSettings : IGeneralSettings, IConfigSettable public string? BaseFontSize { get; set; } public bool ShowWeatherDescription { get; set; } = true; public bool ShowTemperatureUnit { get; set; } = true; + public bool UseWholeNumberTemperatures { get; set; } = false; public string? WeatherIconUrl { get; set; } = "https://openweathermap.org/img/wn/{IconId}.png"; public bool ImageZoom { get; set; } = true; public bool ImagePan { get; set; } = false; diff --git a/immichFrame.Web/src/lib/components/elements/clock.svelte b/immichFrame.Web/src/lib/components/elements/clock.svelte index 57e76a8b..a1b24c6c 100644 --- a/immichFrame.Web/src/lib/components/elements/clock.svelte +++ b/immichFrame.Web/src/lib/components/elements/clock.svelte @@ -90,7 +90,7 @@ {/if}

{weather.location},
-
{weather.temperature?.toFixed(1)}
+
{$configStore.useWholeNumberTemperatures ? Math.round(weather.temperature ?? 0) : weather.temperature?.toFixed(1)}
{$configStore.showTemperatureUnit === false ? '°' : (weather.unit ?? '°')}
diff --git a/immichFrame.Web/src/lib/immichFrameApi.ts b/immichFrame.Web/src/lib/immichFrameApi.ts index fa792a18..36158143 100644 --- a/immichFrame.Web/src/lib/immichFrameApi.ts +++ b/immichFrame.Web/src/lib/immichFrameApi.ts @@ -208,6 +208,7 @@ export type ClientSettingsDto = { baseFontSize?: string | null; showWeatherDescription?: boolean; showTemperatureUnit?: boolean; + useWholeNumberTemperatures?: boolean; weatherIconUrl?: string | null; imageZoom?: boolean; imagePan?: boolean; From 55cd19c2b535ba2c3330b7bbfe26d02c97a1a699 Mon Sep 17 00:00:00 2001 From: nopoz Date: Tue, 6 Jan 2026 23:34:25 -0800 Subject: [PATCH 3/8] Fix undefined temperature handling and add unit tests for UseWholeNumberTemperatures --- .../Helpers/Config/ConfigLoaderTest.cs | 12 ++++++++++-- ImmichFrame.WebApi.Tests/Resources/TestV2.json | 1 + ImmichFrame.WebApi.Tests/Resources/TestV2.yml | 1 + .../src/lib/components/elements/clock.svelte | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/ImmichFrame.WebApi.Tests/Helpers/Config/ConfigLoaderTest.cs b/ImmichFrame.WebApi.Tests/Helpers/Config/ConfigLoaderTest.cs index 15c3254b..a5b73d39 100644 --- a/ImmichFrame.WebApi.Tests/Helpers/Config/ConfigLoaderTest.cs +++ b/ImmichFrame.WebApi.Tests/Helpers/Config/ConfigLoaderTest.cs @@ -70,7 +70,7 @@ public void TestLoadConfigV2Yaml() private void VerifyConfig(IServerSettings serverSettings, bool usePrefix, bool expectNullApiKeyFile) { - VerifyProperties(serverSettings.GeneralSettings); + VerifyProperties(serverSettings.GeneralSettings, "", expectNullApiKeyFile); VerifyAccounts(serverSettings.Accounts, usePrefix, expectNullApiKeyFile); } @@ -117,7 +117,15 @@ private void VerifyProperties(object o, string? prefix = "", bool expectNullApiK } break; case var t when t == typeof(Boolean): - Assert.That(value, Is.EqualTo(true), prop.Name); + // V1 config doesn't support UseWholeNumberTemperatures, so it defaults to false + if (prop.Name.Equals("UseWholeNumberTemperatures") && expectNullApiKeyFile) + { + Assert.That(value, Is.EqualTo(false), prop.Name); + } + else + { + Assert.That(value, Is.EqualTo(true), prop.Name); + } break; case var t when t == typeof(int): Assert.That(value, Is.EqualTo(7), prop.Name); diff --git a/ImmichFrame.WebApi.Tests/Resources/TestV2.json b/ImmichFrame.WebApi.Tests/Resources/TestV2.json index 5c361070..0dd90f34 100644 --- a/ImmichFrame.WebApi.Tests/Resources/TestV2.json +++ b/ImmichFrame.WebApi.Tests/Resources/TestV2.json @@ -31,6 +31,7 @@ "BaseFontSize": "BaseFontSize_TEST", "ShowWeatherDescription": true, "ShowTemperatureUnit": true, + "UseWholeNumberTemperatures": true, "WeatherIconUrl": "WeatherIconUrl_TEST", "ImageZoom": true, "ImagePan": true, diff --git a/ImmichFrame.WebApi.Tests/Resources/TestV2.yml b/ImmichFrame.WebApi.Tests/Resources/TestV2.yml index 7ab9e5ca..e9dc491c 100644 --- a/ImmichFrame.WebApi.Tests/Resources/TestV2.yml +++ b/ImmichFrame.WebApi.Tests/Resources/TestV2.yml @@ -30,6 +30,7 @@ General: BaseFontSize: BaseFontSize_TEST ShowWeatherDescription: true ShowTemperatureUnit: true + UseWholeNumberTemperatures: true WeatherIconUrl: WeatherIconUrl_TEST ImageZoom: true ImagePan: true diff --git a/immichFrame.Web/src/lib/components/elements/clock.svelte b/immichFrame.Web/src/lib/components/elements/clock.svelte index a1b24c6c..3d4a5909 100644 --- a/immichFrame.Web/src/lib/components/elements/clock.svelte +++ b/immichFrame.Web/src/lib/components/elements/clock.svelte @@ -90,7 +90,7 @@ {/if}
{weather.location},
-
{$configStore.useWholeNumberTemperatures ? Math.round(weather.temperature ?? 0) : weather.temperature?.toFixed(1)}
+
{$configStore.useWholeNumberTemperatures ? Math.round(weather.temperature ?? 0) : (weather.temperature ?? 0).toFixed(1)}
{$configStore.showTemperatureUnit === false ? '°' : (weather.unit ?? '°')}
From 640e23e6242e1d90e85a86c3cf4eafda9f1f3c09 Mon Sep 17 00:00:00 2001 From: nopoz Date: Wed, 7 Jan 2026 13:09:13 -0800 Subject: [PATCH 4/8] Replace UseWholeNumberTemperatures bool with TemperatureDecimalDigits int (0-2) for flexible temperature precision --- ImmichFrame.Core/Interfaces/IServerSettings.cs | 2 +- .../Helpers/Config/ConfigLoaderTest.cs | 14 +++++++------- ImmichFrame.WebApi.Tests/Resources/TestV2.json | 2 +- ImmichFrame.WebApi.Tests/Resources/TestV2.yml | 2 +- .../Helpers/Config/ServerSettingsV1.cs | 2 +- ImmichFrame.WebApi/Models/ClientSettingsDto.cs | 4 ++-- ImmichFrame.WebApi/Models/ServerSettings.cs | 2 +- .../src/lib/components/elements/clock.svelte | 2 +- immichFrame.Web/src/lib/immichFrameApi.ts | 2 +- 9 files changed, 16 insertions(+), 16 deletions(-) diff --git a/ImmichFrame.Core/Interfaces/IServerSettings.cs b/ImmichFrame.Core/Interfaces/IServerSettings.cs index 0264d71e..833ea38c 100644 --- a/ImmichFrame.Core/Interfaces/IServerSettings.cs +++ b/ImmichFrame.Core/Interfaces/IServerSettings.cs @@ -60,7 +60,7 @@ public interface IGeneralSettings public string? BaseFontSize { get; } public bool ShowWeatherDescription { get; } public bool ShowTemperatureUnit { get; } - public bool UseWholeNumberTemperatures { get; } + public int TemperatureDecimalDigits { get; } public string? WeatherIconUrl { get; } public bool ImageZoom { get; } public bool ImagePan { get; } diff --git a/ImmichFrame.WebApi.Tests/Helpers/Config/ConfigLoaderTest.cs b/ImmichFrame.WebApi.Tests/Helpers/Config/ConfigLoaderTest.cs index a5b73d39..29132f27 100644 --- a/ImmichFrame.WebApi.Tests/Helpers/Config/ConfigLoaderTest.cs +++ b/ImmichFrame.WebApi.Tests/Helpers/Config/ConfigLoaderTest.cs @@ -117,19 +117,19 @@ private void VerifyProperties(object o, string? prefix = "", bool expectNullApiK } break; case var t when t == typeof(Boolean): - // V1 config doesn't support UseWholeNumberTemperatures, so it defaults to false - if (prop.Name.Equals("UseWholeNumberTemperatures") && expectNullApiKeyFile) + Assert.That(value, Is.EqualTo(true), prop.Name); + break; + case var t when t == typeof(int): + // V1 config doesn't support TemperatureDecimalDigits, so it defaults to 1 + if (prop.Name.Equals("TemperatureDecimalDigits") && expectNullApiKeyFile) { - Assert.That(value, Is.EqualTo(false), prop.Name); + Assert.That(value, Is.EqualTo(1), prop.Name); } else { - Assert.That(value, Is.EqualTo(true), prop.Name); + Assert.That(value, Is.EqualTo(7), prop.Name); } break; - case var t when t == typeof(int): - Assert.That(value, Is.EqualTo(7), prop.Name); - break; case var t when t == typeof(double): Assert.That(value, Is.EqualTo(7.7d), prop.Name); break; diff --git a/ImmichFrame.WebApi.Tests/Resources/TestV2.json b/ImmichFrame.WebApi.Tests/Resources/TestV2.json index 0dd90f34..167fd0af 100644 --- a/ImmichFrame.WebApi.Tests/Resources/TestV2.json +++ b/ImmichFrame.WebApi.Tests/Resources/TestV2.json @@ -31,7 +31,7 @@ "BaseFontSize": "BaseFontSize_TEST", "ShowWeatherDescription": true, "ShowTemperatureUnit": true, - "UseWholeNumberTemperatures": true, + "TemperatureDecimalDigits": 7, "WeatherIconUrl": "WeatherIconUrl_TEST", "ImageZoom": true, "ImagePan": true, diff --git a/ImmichFrame.WebApi.Tests/Resources/TestV2.yml b/ImmichFrame.WebApi.Tests/Resources/TestV2.yml index e9dc491c..d7746231 100644 --- a/ImmichFrame.WebApi.Tests/Resources/TestV2.yml +++ b/ImmichFrame.WebApi.Tests/Resources/TestV2.yml @@ -30,7 +30,7 @@ General: BaseFontSize: BaseFontSize_TEST ShowWeatherDescription: true ShowTemperatureUnit: true - UseWholeNumberTemperatures: true + TemperatureDecimalDigits: 7 WeatherIconUrl: WeatherIconUrl_TEST ImageZoom: true ImagePan: true diff --git a/ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs b/ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs index ddaaec38..08dadf51 100644 --- a/ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs +++ b/ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs @@ -129,7 +129,7 @@ class GeneralSettingsV1Adapter(ServerSettingsV1 _delegate) : IGeneralSettings public string? BaseFontSize => _delegate.BaseFontSize; public bool ShowWeatherDescription => _delegate.ShowWeatherDescription; public bool ShowTemperatureUnit => true; - public bool UseWholeNumberTemperatures => false; + public int TemperatureDecimalDigits => 1; public string? WeatherIconUrl => _delegate.WeatherIconUrl; public bool ImageZoom => _delegate.ImageZoom; public bool ImagePan => _delegate.ImagePan; diff --git a/ImmichFrame.WebApi/Models/ClientSettingsDto.cs b/ImmichFrame.WebApi/Models/ClientSettingsDto.cs index 932e52a4..116ecf2f 100644 --- a/ImmichFrame.WebApi/Models/ClientSettingsDto.cs +++ b/ImmichFrame.WebApi/Models/ClientSettingsDto.cs @@ -26,7 +26,7 @@ public class ClientSettingsDto public string? BaseFontSize { get; set; } public bool ShowWeatherDescription { get; set; } public bool ShowTemperatureUnit { get; set; } - public bool UseWholeNumberTemperatures { get; set; } + public int TemperatureDecimalDigits { get; set; } public string? WeatherIconUrl { get; set; } public bool ImageZoom { get; set; } public bool ImagePan { get; set; } @@ -60,7 +60,7 @@ public static ClientSettingsDto FromGeneralSettings(IGeneralSettings generalSett dto.BaseFontSize = generalSettings.BaseFontSize; dto.ShowWeatherDescription = generalSettings.ShowWeatherDescription; dto.ShowTemperatureUnit = generalSettings.ShowTemperatureUnit; - dto.UseWholeNumberTemperatures = generalSettings.UseWholeNumberTemperatures; + dto.TemperatureDecimalDigits = generalSettings.TemperatureDecimalDigits; dto.WeatherIconUrl = generalSettings.WeatherIconUrl; dto.ImageZoom = generalSettings.ImageZoom; dto.ImagePan = generalSettings.ImagePan; diff --git a/ImmichFrame.WebApi/Models/ServerSettings.cs b/ImmichFrame.WebApi/Models/ServerSettings.cs index 37b1bc13..b5f38f6a 100644 --- a/ImmichFrame.WebApi/Models/ServerSettings.cs +++ b/ImmichFrame.WebApi/Models/ServerSettings.cs @@ -59,7 +59,7 @@ public class GeneralSettings : IGeneralSettings, IConfigSettable public string? BaseFontSize { get; set; } public bool ShowWeatherDescription { get; set; } = true; public bool ShowTemperatureUnit { get; set; } = true; - public bool UseWholeNumberTemperatures { get; set; } = false; + public int TemperatureDecimalDigits { get; set; } = 1; public string? WeatherIconUrl { get; set; } = "https://openweathermap.org/img/wn/{IconId}.png"; public bool ImageZoom { get; set; } = true; public bool ImagePan { get; set; } = false; diff --git a/immichFrame.Web/src/lib/components/elements/clock.svelte b/immichFrame.Web/src/lib/components/elements/clock.svelte index 3d4a5909..f0c4e1be 100644 --- a/immichFrame.Web/src/lib/components/elements/clock.svelte +++ b/immichFrame.Web/src/lib/components/elements/clock.svelte @@ -90,7 +90,7 @@ {/if}
{weather.location},
-
{$configStore.useWholeNumberTemperatures ? Math.round(weather.temperature ?? 0) : (weather.temperature ?? 0).toFixed(1)}
+
{(weather.temperature ?? 0).toFixed($configStore.temperatureDecimalDigits ?? 1)}
{$configStore.showTemperatureUnit === false ? '°' : (weather.unit ?? '°')}
diff --git a/immichFrame.Web/src/lib/immichFrameApi.ts b/immichFrame.Web/src/lib/immichFrameApi.ts index 36158143..5a4f6352 100644 --- a/immichFrame.Web/src/lib/immichFrameApi.ts +++ b/immichFrame.Web/src/lib/immichFrameApi.ts @@ -208,7 +208,7 @@ export type ClientSettingsDto = { baseFontSize?: string | null; showWeatherDescription?: boolean; showTemperatureUnit?: boolean; - useWholeNumberTemperatures?: boolean; + temperatureDecimalDigits?: number; weatherIconUrl?: string | null; imageZoom?: boolean; imagePan?: boolean; From 1eaf18f7010357f5e617a6600e6da6f3c74df97c Mon Sep 17 00:00:00 2001 From: nopoz Date: Wed, 7 Jan 2026 13:23:15 -0800 Subject: [PATCH 5/8] Add validation for TemperatureDecimalDigits (0-2) in GeneralSettings.Validate() --- ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs | 10 +++++++++- ImmichFrame.WebApi/Models/ServerSettings.cs | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs b/ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs index 08dadf51..e9364029 100644 --- a/ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs +++ b/ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs @@ -138,6 +138,14 @@ class GeneralSettingsV1Adapter(ServerSettingsV1 _delegate) : IGeneralSettings public string Layout => _delegate.Layout; public string Language => _delegate.Language; - public void Validate() { } + public void Validate() + { + if (TemperatureDecimalDigits < 0 || TemperatureDecimalDigits > 2) + { + throw new ArgumentOutOfRangeException(nameof(TemperatureDecimalDigits), + TemperatureDecimalDigits, + "TemperatureDecimalDigits must be between 0 and 2."); + } + } } } diff --git a/ImmichFrame.WebApi/Models/ServerSettings.cs b/ImmichFrame.WebApi/Models/ServerSettings.cs index b5f38f6a..7c1f9013 100644 --- a/ImmichFrame.WebApi/Models/ServerSettings.cs +++ b/ImmichFrame.WebApi/Models/ServerSettings.cs @@ -75,7 +75,15 @@ public class GeneralSettings : IGeneralSettings, IConfigSettable public string? Webhook { get; set; } public string? AuthenticationSecret { get; set; } - public void Validate() { } + public void Validate() + { + if (TemperatureDecimalDigits < 0 || TemperatureDecimalDigits > 2) + { + throw new ArgumentOutOfRangeException(nameof(TemperatureDecimalDigits), + TemperatureDecimalDigits, + "TemperatureDecimalDigits must be between 0 and 2."); + } + } } public class ServerAccountSettings : IAccountSettings, IConfigSettable From 5f4b6b74754bc9363df1a2d2294e2cd55171a9d9 Mon Sep 17 00:00:00 2001 From: nopoz Date: Fri, 16 Jan 2026 12:18:23 -0800 Subject: [PATCH 6/8] Add ExcludedPeople setting to filter out specific people from People results --- .../Logic/Pool/PersonAssetsPoolTests.cs | 1 + .../Interfaces/IServerSettings.cs | 1 + .../Logic/Pool/PeopleAssetsPool.cs | 67 +++++++++++-------- .../Resources/TestV2.json | 6 ++ ImmichFrame.WebApi.Tests/Resources/TestV2.yml | 4 ++ .../Helpers/Config/ServerSettingsV1.cs | 2 + ImmichFrame.WebApi/Models/ServerSettings.cs | 1 + docker/Settings.example.json | 3 + docker/Settings.example.yml | 2 + docker/example.env | 1 + 10 files changed, 61 insertions(+), 27 deletions(-) diff --git a/ImmichFrame.Core.Tests/Logic/Pool/PersonAssetsPoolTests.cs b/ImmichFrame.Core.Tests/Logic/Pool/PersonAssetsPoolTests.cs index 54977293..20c62705 100644 --- a/ImmichFrame.Core.Tests/Logic/Pool/PersonAssetsPoolTests.cs +++ b/ImmichFrame.Core.Tests/Logic/Pool/PersonAssetsPoolTests.cs @@ -39,6 +39,7 @@ public void Setup() _personAssetsPool = new TestablePersonAssetsPool(_mockApiCache.Object, _mockImmichApi.Object, _mockAccountSettings.Object); _mockAccountSettings.SetupGet(s => s.People).Returns(new List()); + _mockAccountSettings.SetupGet(s => s.ExcludedPeople).Returns(new List()); } private AssetResponseDto CreateAsset(string id, AssetTypeEnum type = AssetTypeEnum.IMAGE) => new AssetResponseDto { Id = id, Type = type }; diff --git a/ImmichFrame.Core/Interfaces/IServerSettings.cs b/ImmichFrame.Core/Interfaces/IServerSettings.cs index 833ea38c..15829a0b 100644 --- a/ImmichFrame.Core/Interfaces/IServerSettings.cs +++ b/ImmichFrame.Core/Interfaces/IServerSettings.cs @@ -24,6 +24,7 @@ public interface IAccountSettings public List ExcludedAlbums { get; } public List People { get; } public List Tags { get; } + public List ExcludedPeople { get; } public int? Rating { get; } public void ValidateAndInitialize(); diff --git a/ImmichFrame.Core/Logic/Pool/PeopleAssetsPool.cs b/ImmichFrame.Core/Logic/Pool/PeopleAssetsPool.cs index 8aa52bd8..5be0f66d 100644 --- a/ImmichFrame.Core/Logic/Pool/PeopleAssetsPool.cs +++ b/ImmichFrame.Core/Logic/Pool/PeopleAssetsPool.cs @@ -1,4 +1,5 @@ using ImmichFrame.Core.Api; +using ImmichFrame.Core.Helpers; using ImmichFrame.Core.Interfaces; namespace ImmichFrame.Core.Logic.Pool; @@ -7,6 +8,13 @@ public class PersonAssetsPool(IApiCache apiCache, ImmichApi immichApi, IAccountS { protected override async Task> LoadAssets(CancellationToken ct = default) { + var excludedPersonAssets = new List(); + + foreach (var personId in accountSettings.ExcludedPeople) + { + excludedPersonAssets.AddRange(await LoadAssetsForPerson(personId, ct)); + } + var personAssets = new List(); var people = accountSettings.People; @@ -17,34 +25,39 @@ protected override async Task> LoadAssets(Cancella foreach (var personId in people) { - int page = 1; - int batchSize = 1000; - int total; - do - { - var metadataBody = new MetadataSearchDto - { - Page = page, - Size = batchSize, - PersonIds = [personId], - WithExif = true, - WithPeople = true - }; - - if (!accountSettings.ShowVideos) - { - metadataBody.Type = AssetTypeEnum.IMAGE; - } - - var personInfo = await immichApi.SearchAssetsAsync(metadataBody, ct); - - total = personInfo.Assets.Total; - - personAssets.AddRange(personInfo.Assets.Items); - page++; - } while (total == batchSize); + personAssets.AddRange(await LoadAssetsForPerson(personId, ct)); } - return personAssets; + return personAssets.WhereExcludes(excludedPersonAssets, t => t.Id); + } + + private async Task> LoadAssetsForPerson(Guid personId, CancellationToken ct) + { + var assets = new List(); + int page = 1; + int batchSize = 1000; + int total; + + do + { + var metadataBody = new MetadataSearchDto + { + Page = page, + Size = batchSize, + PersonIds = [personId], + Type = AssetTypeEnum.IMAGE, + WithExif = true, + WithPeople = true + }; + + var personInfo = await immichApi.SearchAssetsAsync(metadataBody, ct); + + total = personInfo.Assets.Total; + + assets.AddRange(personInfo.Assets.Items); + page++; + } while (total == batchSize); + + return assets; } } \ No newline at end of file diff --git a/ImmichFrame.WebApi.Tests/Resources/TestV2.json b/ImmichFrame.WebApi.Tests/Resources/TestV2.json index 167fd0af..b5f4c16e 100644 --- a/ImmichFrame.WebApi.Tests/Resources/TestV2.json +++ b/ImmichFrame.WebApi.Tests/Resources/TestV2.json @@ -63,6 +63,9 @@ ], "Tags": [ "Account1.Tags_TEST" + ], + "ExcludedPeople": [ + "00000000-0000-0000-0000-000000000001" ] }, { @@ -88,6 +91,9 @@ ], "Tags": [ "Account2.Tags_TEST" + ], + "ExcludedPeople": [ + "00000000-0000-0000-0000-000000000001" ] } ] diff --git a/ImmichFrame.WebApi.Tests/Resources/TestV2.yml b/ImmichFrame.WebApi.Tests/Resources/TestV2.yml index d7746231..ba97f8a6 100644 --- a/ImmichFrame.WebApi.Tests/Resources/TestV2.yml +++ b/ImmichFrame.WebApi.Tests/Resources/TestV2.yml @@ -57,6 +57,8 @@ Accounts: - 00000000-0000-0000-0000-000000000001 Tags: - Account1.Tags_TEST + ExcludedPeople: + - 00000000-0000-0000-0000-000000000001 - ImmichServerUrl: Account2.ImmichServerUrl_TEST ApiKey: Account2.ApiKey_TEST ApiKeyFile: Account2.ApiKeyFile_TEST @@ -76,3 +78,5 @@ Accounts: - 00000000-0000-0000-0000-000000000001 Tags: - Account2.Tags_TEST + ExcludedPeople: + - 00000000-0000-0000-0000-000000000001 diff --git a/ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs b/ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs index e9364029..7d098d2f 100644 --- a/ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs +++ b/ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs @@ -22,6 +22,7 @@ public class ServerSettingsV1 : IConfigSettable public List ExcludedAlbums { get; set; } = new List(); public List People { get; set; } = new List(); public List Tags { get; set; } = new List(); + public List ExcludedPeople { get; set; } = new List(); public int? Rating { get; set; } public List Webcalendars { get; set; } = new List(); public int RefreshAlbumPeopleInterval { get; set; } = 12; @@ -93,6 +94,7 @@ class AccountSettingsV1Adapter(ServerSettingsV1 _delegate) : IAccountSettings public List ExcludedAlbums => _delegate.ExcludedAlbums; public List People => _delegate.People; public List Tags => _delegate.Tags; + public List ExcludedPeople => _delegate.ExcludedPeople; public int? Rating => _delegate.Rating; public void ValidateAndInitialize() { } diff --git a/ImmichFrame.WebApi/Models/ServerSettings.cs b/ImmichFrame.WebApi/Models/ServerSettings.cs index 7c1f9013..cb659d8d 100644 --- a/ImmichFrame.WebApi/Models/ServerSettings.cs +++ b/ImmichFrame.WebApi/Models/ServerSettings.cs @@ -103,6 +103,7 @@ public class ServerAccountSettings : IAccountSettings, IConfigSettable public List ExcludedAlbums { get; set; } = new(); public List People { get; set; } = new(); public List Tags { get; set; } = new(); + public List ExcludedPeople { get; set; } = new(); public int? Rating { get; set; } public void ValidateAndInitialize() diff --git a/docker/Settings.example.json b/docker/Settings.example.json index a86a4d00..4c42d11c 100644 --- a/docker/Settings.example.json +++ b/docker/Settings.example.json @@ -62,6 +62,9 @@ "Tags": [ "Vacation", "Travel/Europe" + ], + "ExcludedPeople": [ + "UUID" ] } ] diff --git a/docker/Settings.example.yml b/docker/Settings.example.yml index 173b31a5..a5c2582b 100644 --- a/docker/Settings.example.yml +++ b/docker/Settings.example.yml @@ -57,3 +57,5 @@ Accounts: Tags: - Vacation - Travel/Europe + ExcludedPeople: + - UUID diff --git a/docker/example.env b/docker/example.env index 51d80ed5..7db77563 100644 --- a/docker/example.env +++ b/docker/example.env @@ -24,6 +24,7 @@ ApiKey=KEY # Albums=ALBUM1,ALBUM2 # ExcludedAlbums=ALBUM3,ALBUM4 # People=PERSON1,PERSON2 +# ExcludedPeople=PERSON3,PERSON4 # Webcalendars=https://calendar.google.com/calendar/ical/XXXXXX/public/basic.ics,https://user:pass@calendar.immichframe.dev/dav/calendars/basic.ics # RefreshAlbumPeopleInterval=12 # ShowClock=true From e71013c7fd6b1fc3f21733b65e21a93ec66bd2e0 Mon Sep 17 00:00:00 2001 From: nopoz Date: Sat, 17 Jan 2026 13:52:05 -0800 Subject: [PATCH 7/8] Fix pagination in PeopleAssetsPool to check returned item count instead of total --- ImmichFrame.Core/Logic/Pool/PeopleAssetsPool.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ImmichFrame.Core/Logic/Pool/PeopleAssetsPool.cs b/ImmichFrame.Core/Logic/Pool/PeopleAssetsPool.cs index 5be0f66d..896b5d01 100644 --- a/ImmichFrame.Core/Logic/Pool/PeopleAssetsPool.cs +++ b/ImmichFrame.Core/Logic/Pool/PeopleAssetsPool.cs @@ -36,7 +36,7 @@ private async Task> LoadAssetsForPerson(Guid personId, Ca var assets = new List(); int page = 1; int batchSize = 1000; - int total; + int lastPageCount; do { @@ -52,11 +52,11 @@ private async Task> LoadAssetsForPerson(Guid personId, Ca var personInfo = await immichApi.SearchAssetsAsync(metadataBody, ct); - total = personInfo.Assets.Total; + lastPageCount = personInfo.Assets.Items.Count; assets.AddRange(personInfo.Assets.Items); page++; - } while (total == batchSize); + } while (lastPageCount == batchSize); return assets; } From d87a98c12a5b67033c81e7c235a34e0e9db82083 Mon Sep 17 00:00:00 2001 From: nopoz Date: Thu, 5 Feb 2026 11:48:27 -0800 Subject: [PATCH 8/8] Address code review feedback from PR #582 - Move People null check before ExcludedPeople loading in PeopleAssetsPool - to avoid unnecessary API calls, and fix TemperatureDecimalDigits test - fixture value from 7 to 2 to match the validated 0-2 range. --- .../Logic/Pool/PeopleAssetsPool.cs | 18 +++++++++--------- .../Helpers/Config/ConfigLoaderTest.cs | 2 +- ImmichFrame.WebApi.Tests/Resources/TestV2.json | 2 +- ImmichFrame.WebApi.Tests/Resources/TestV2.yml | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ImmichFrame.Core/Logic/Pool/PeopleAssetsPool.cs b/ImmichFrame.Core/Logic/Pool/PeopleAssetsPool.cs index 896b5d01..929aa6fa 100644 --- a/ImmichFrame.Core/Logic/Pool/PeopleAssetsPool.cs +++ b/ImmichFrame.Core/Logic/Pool/PeopleAssetsPool.cs @@ -8,26 +8,26 @@ public class PersonAssetsPool(IApiCache apiCache, ImmichApi immichApi, IAccountS { protected override async Task> LoadAssets(CancellationToken ct = default) { - var excludedPersonAssets = new List(); - - foreach (var personId in accountSettings.ExcludedPeople) - { - excludedPersonAssets.AddRange(await LoadAssetsForPerson(personId, ct)); - } - var personAssets = new List(); var people = accountSettings.People; - if (people == null) + if (people == null || people.Count == 0) { return personAssets; } - + foreach (var personId in people) { personAssets.AddRange(await LoadAssetsForPerson(personId, ct)); } + var excludedPersonAssets = new List(); + + foreach (var personId in accountSettings.ExcludedPeople) + { + excludedPersonAssets.AddRange(await LoadAssetsForPerson(personId, ct)); + } + return personAssets.WhereExcludes(excludedPersonAssets, t => t.Id); } diff --git a/ImmichFrame.WebApi.Tests/Helpers/Config/ConfigLoaderTest.cs b/ImmichFrame.WebApi.Tests/Helpers/Config/ConfigLoaderTest.cs index 29132f27..be9dd5d8 100644 --- a/ImmichFrame.WebApi.Tests/Helpers/Config/ConfigLoaderTest.cs +++ b/ImmichFrame.WebApi.Tests/Helpers/Config/ConfigLoaderTest.cs @@ -127,7 +127,7 @@ private void VerifyProperties(object o, string? prefix = "", bool expectNullApiK } else { - Assert.That(value, Is.EqualTo(7), prop.Name); + Assert.That(value, Is.EqualTo(prop.Name.Equals("TemperatureDecimalDigits") ? 2 : 7), prop.Name); } break; case var t when t == typeof(double): diff --git a/ImmichFrame.WebApi.Tests/Resources/TestV2.json b/ImmichFrame.WebApi.Tests/Resources/TestV2.json index b5f4c16e..81699a0d 100644 --- a/ImmichFrame.WebApi.Tests/Resources/TestV2.json +++ b/ImmichFrame.WebApi.Tests/Resources/TestV2.json @@ -31,7 +31,7 @@ "BaseFontSize": "BaseFontSize_TEST", "ShowWeatherDescription": true, "ShowTemperatureUnit": true, - "TemperatureDecimalDigits": 7, + "TemperatureDecimalDigits": 2, "WeatherIconUrl": "WeatherIconUrl_TEST", "ImageZoom": true, "ImagePan": true, diff --git a/ImmichFrame.WebApi.Tests/Resources/TestV2.yml b/ImmichFrame.WebApi.Tests/Resources/TestV2.yml index ba97f8a6..933ea51d 100644 --- a/ImmichFrame.WebApi.Tests/Resources/TestV2.yml +++ b/ImmichFrame.WebApi.Tests/Resources/TestV2.yml @@ -30,7 +30,7 @@ General: BaseFontSize: BaseFontSize_TEST ShowWeatherDescription: true ShowTemperatureUnit: true - TemperatureDecimalDigits: 7 + TemperatureDecimalDigits: 2 WeatherIconUrl: WeatherIconUrl_TEST ImageZoom: true ImagePan: true