From 41fe7d3bb00e7daefddd1a3600df550c15674229 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 09:03:46 +0000 Subject: [PATCH 2/3] Fix ImmichFrame config: map env_vars and schema options to Settings.yaml - Add General display options (Interval, ShowClock, etc.) to addon config schema - Add per-Account filter options (Albums, People, ShowFavorites, etc.) to Accounts schema - Rewrite 99-run.sh to generate complete Settings.yaml with General and Accounts sections - env_vars are automatically classified as General or Account-level settings - Schema options take precedence over env_vars - Full backward compatibility: existing env_var configs continue to work - Update README with comprehensive options documentation - Bump version to 1.0.32.0-4 Co-authored-by: alexbelgium <44178713+alexbelgium@users.noreply.github.com> --- immich_frame/CHANGELOG.md | 7 + immich_frame/README.md | 86 +++++++- immich_frame/config.yaml | 45 +++- immich_frame/rootfs/etc/cont-init.d/99-run.sh | 207 +++++++++++++++--- 4 files changed, 306 insertions(+), 39 deletions(-) diff --git a/immich_frame/CHANGELOG.md b/immich_frame/CHANGELOG.md index 2d858c13b..90c1015e9 100644 --- a/immich_frame/CHANGELOG.md +++ b/immich_frame/CHANGELOG.md @@ -1,3 +1,10 @@ +## 1.0.32.0-4 (17-03-2026) +- Fix: env_vars now properly written to Settings.yaml instead of only environment variables +- Added General config options to addon UI (Interval, ShowClock, PhotoDateFormat, Style, Layout, etc.) +- Added per-Account config options to addon UI (Albums, People, ShowFavorites, ShowMemories, etc.) +- env_vars are automatically classified as General or Account settings in Settings.yaml +- Full backward compatibility: existing env_vars configurations continue to work + ## 1.0.32.0-3 (16-03-2026) - Minor bugs fixed diff --git a/immich_frame/README.md b/immich_frame/README.md index 7ef9ec464..184c49dce 100644 --- a/immich_frame/README.md +++ b/immich_frame/README.md @@ -39,12 +39,71 @@ Webui can be found at `:8171`. ### Options +#### Connection + +| Option | Type | Description | +|--------|------|-------------| +| `ImmichServerUrl` | str | URL of your Immich server (e.g., `http://homeassistant:3001`). Used for single-account setup. | +| `ApiKey` | str | Immich API key for authentication. Used for single-account setup. | +| `Accounts` | list | List of Immich accounts for multi-account support. Each entry requires `ImmichServerUrl` and `ApiKey`, plus optional per-account filters (see below). | +| `TZ` | str | Timezone (e.g., `Europe/London`) | + +#### General (Display) Options + +These top-level options map to ImmichFrame's `General` settings and control the display behavior: + | Option | Type | Default | Description | |--------|------|---------|-------------| -| `ImmichServerUrl` | str | | URL of your Immich server (e.g., `http://homeassistant:3001`). Used for single-account setup. | -| `ApiKey` | str | | Immich API key for authentication. Used for single-account setup. | -| `Accounts` | list | `[]` | List of Immich accounts for multi-account support. Each entry requires `ImmichServerUrl` and `ApiKey`. | -| `TZ` | str | | Timezone (e.g., `Europe/London`) | +| `Interval` | int | 45 | Image display interval in seconds | +| `TransitionDuration` | float | 2 | Transition duration in seconds | +| `ShowClock` | bool | true | Display the current time | +| `ClockFormat` | str | `hh:mm` | Time format for the clock | +| `ClockDateFormat` | str | `eee, MMM d` | Date format for the clock | +| `ShowProgressBar` | bool | true | Display the progress bar | +| `ShowPhotoDate` | bool | true | Display the date of the current image | +| `PhotoDateFormat` | str | `MM/dd/yyyy` | Date format for photo dates | +| `ShowImageDesc` | bool | true | Display image description | +| `ShowPeopleDesc` | bool | true | Display people names | +| `ShowTagsDesc` | bool | true | Display tag names | +| `ShowAlbumName` | bool | true | Display album names | +| `ShowImageLocation` | bool | true | Display image location | +| `ShowWeatherDescription` | bool | true | Display weather description | +| `ImageZoom` | bool | true | Zoom into images for a touch of life | +| `ImagePan` | bool | false | Pan images in a random direction | +| `ImageFill` | bool | false | Fill available space (may crop) | +| `PlayAudio` | bool | false | Play audio for videos with audio tracks | +| `PrimaryColor` | str | `#f5deb3` | Primary UI color (hex) | +| `SecondaryColor` | str | `#000000` | Secondary UI color (hex) | +| `Style` | str | `none` | Background style: `none`, `solid`, `transition`, `blur` | +| `Layout` | str | `splitview` | Layout: `single` or `splitview` | +| `BaseFontSize` | str | `17px` | Base font size (CSS format) | +| `Language` | str | `en` | 2-digit ISO language code | +| `WeatherApiKey` | str | | OpenWeatherMap API key | +| `UnitSystem` | str | `imperial` | `imperial` or `metric` | +| `WeatherLatLong` | str | | Weather location as `lat,lon` | +| `ImageLocationFormat` | str | `City,State,Country` | Location display format | +| `DownloadImages` | bool | false | Download images to server | +| `RenewImagesDuration` | int | 30 | Re-download images after this many days | +| `RefreshAlbumPeopleInterval` | int | 12 | Hours between album/people refresh | + +#### Per-Account Options + +These options can be set within each `Accounts` entry to control which images are shown: + +| Option | Type | Description | +|--------|------|-------------| +| `Albums` | str | Comma-separated album UUIDs | +| `ExcludedAlbums` | str | Comma-separated excluded album UUIDs | +| `People` | str | Comma-separated people UUIDs | +| `Tags` | str | Comma-separated tag paths (e.g., `Vacation,Travel/Europe`) | +| `ShowFavorites` | bool | Show favorite images | +| `ShowMemories` | bool | Show memory images | +| `ShowArchived` | bool | Show archived images | +| `ShowVideos` | bool | Include video assets | +| `ImagesFromDays` | int | Show images from the last X days | +| `ImagesFromDate` | str | Show images after this date | +| `ImagesUntilDate` | str | Show images before this date | +| `Rating` | int | Filter by star rating (-1 to 5) | ### Single Account Example @@ -52,6 +111,9 @@ Webui can be found at `:8171`. ImmichServerUrl: "http://homeassistant:3001" ApiKey: "your-immich-api-key-here" TZ: "Europe/London" +ShowClock: false +Interval: 30 +PhotoDateFormat: "dd/MM/yyyy" ``` ### Multi-Account Example @@ -62,8 +124,13 @@ To display photos from multiple Immich accounts (e.g., you and your partner), us Accounts: - ImmichServerUrl: "http://homeassistant:3001" ApiKey: "api-key-for-user-1" + Albums: "album-uuid-1,album-uuid-2" + ShowFavorites: true - ImmichServerUrl: "http://homeassistant:3001" ApiKey: "api-key-for-user-2" + People: "person-uuid-1,person-uuid-2" +ShowClock: false +Interval: 40 TZ: "Europe/London" ``` @@ -84,7 +151,16 @@ For more configuration options, see the [ImmichFrame documentation](https://immi This addon supports custom scripts and environment variables through the `addon_config` mapping: - **Custom scripts**: See [Running Custom Scripts in Addons](https://github.com/alexbelgium/hassio-addons/wiki/Running-custom-scripts-in-Addons) -- **env_vars option**: Use the add-on `env_vars` option to pass extra environment variables (uppercase or lowercase names). See https://github.com/alexbelgium/hassio-addons/wiki/Add-Environment-variables-to-your-Addon-2 for details. +- **env_vars option**: Use the add-on `env_vars` option to pass extra ImmichFrame settings not available in the addon UI. Environment variables are automatically classified as General or Account-level settings and written to `Settings.yaml`. See https://github.com/alexbelgium/hassio-addons/wiki/Add-Environment-variables-to-your-Addon-2 for details. + +**env_vars example** (for settings not in the UI): +```yaml +env_vars: + - name: AuthenticationSecret + value: "my-secret" + - name: Webhook + value: "http://example.com/notify" +``` ## Installation diff --git a/immich_frame/config.yaml b/immich_frame/config.yaml index 21ad5d1c3..59a60c113 100644 --- a/immich_frame/config.yaml +++ b/immich_frame/config.yaml @@ -19,13 +19,56 @@ schema: Accounts: - ImmichServerUrl: str ApiKey: str + Albums: str? + ExcludedAlbums: str? + People: str? + Tags: str? + ShowFavorites: bool? + ShowMemories: bool? + ShowArchived: bool? + ShowVideos: bool? + ImagesFromDays: int? + ImagesFromDate: str? + ImagesUntilDate: str? + Rating: int? env_vars: - name: match(^[A-Za-z0-9_]+$) value: str? ApiKey: str? ImmichServerUrl: str? TZ: str? + Interval: int? + TransitionDuration: float? + ShowClock: bool? + ClockFormat: str? + ClockDateFormat: str? + ShowProgressBar: bool? + ShowPhotoDate: bool? + PhotoDateFormat: str? + ShowImageDesc: bool? + ShowPeopleDesc: bool? + ShowTagsDesc: bool? + ShowAlbumName: bool? + ShowImageLocation: bool? + ShowWeatherDescription: bool? + ImageZoom: bool? + ImagePan: bool? + ImageFill: bool? + PlayAudio: bool? + PrimaryColor: str? + SecondaryColor: str? + Style: str? + Layout: str? + BaseFontSize: str? + Language: str? + WeatherApiKey: str? + UnitSystem: str? + WeatherLatLong: str? + ImageLocationFormat: str? + DownloadImages: bool? + RenewImagesDuration: int? + RefreshAlbumPeopleInterval: int? slug: immich_frame url: https://github.com/alexbelgium/hassio-addons -version: "1.0.32.0-3" +version: "1.0.32.0-4" webui: http://[HOST]:[PORT:8080] diff --git a/immich_frame/rootfs/etc/cont-init.d/99-run.sh b/immich_frame/rootfs/etc/cont-init.d/99-run.sh index 6141be3d2..6abd7bae2 100755 --- a/immich_frame/rootfs/etc/cont-init.d/99-run.sh +++ b/immich_frame/rootfs/etc/cont-init.d/99-run.sh @@ -22,45 +22,186 @@ if [ ! -e /app/Config ]; then ln -sf /config/Config /app/Config fi -# Generate Settings.yaml from addon options for multi-account support +# ---- Settings.yaml generation ---- SETTINGS_FILE="/config/Config/Settings.yaml" -ACCOUNT_COUNT=$(jq '.Accounts // [] | length' /data/options.json 2>/dev/null || echo 0) -if [ "$ACCOUNT_COUNT" -gt 0 ]; then - bashio::log.info "Configuring ${ACCOUNT_COUNT} account(s) from Accounts list" - { +# Known account-level setting names (ImmichFrame v2 config) +ACCOUNT_KEYS=" ImmichServerUrl ApiKey ApiKeyFile Albums ExcludedAlbums People Tags ShowFavorites ShowMemories ShowArchived ShowVideos ImagesFromDays ImagesFromDate ImagesUntilDate Rating " +# Settings that accept comma-separated values and should become YAML lists +LIST_KEYS=" Albums ExcludedAlbums People Tags Webcalendars " + +# Helper: check if word is in a space-padded list +in_list() { [[ "$2" == *" $1 "* ]]; } + +# Helper: read a value from options.json handling booleans and nulls correctly +config_val() { + jq -r "($1) as \$v | if \$v == null then \"\" else (\$v | tostring) end" /data/options.json 2>/dev/null +} +config_has() { + jq -e "($1) != null" /data/options.json >/dev/null 2>&1 +} + +# Helper: write a YAML key-value pair with proper formatting +yaml_kv() { + local indent="$1" key="$2" value="$3" + + # List-type settings -> YAML array + if in_list "$key" "$LIST_KEYS"; then + echo "${indent}${key}:" + IFS=',' read -ra ITEMS <<< "$value" + for item in "${ITEMS[@]}"; do + item="$(echo "$item" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" + [ -n "$item" ] && echo "${indent} - '${item//\'/\'\'}'" + done + return + fi + + # Boolean + if [ "$value" = "true" ] || [ "$value" = "false" ]; then + echo "${indent}${key}: ${value}" + return + fi + + # Integer + if [[ "$value" =~ ^-?[0-9]+$ ]]; then + echo "${indent}${key}: ${value}" + return + fi + + # Float + if [[ "$value" =~ ^-?[0-9]+\.[0-9]+$ ]]; then + echo "${indent}${key}: ${value}" + return + fi + + # String (single-quoted with escaping) + echo "${indent}${key}: '${value//\'/\'\'}'" +} + +# ---- Classify env_vars into general vs account settings ---- +declare -A GENERAL_ENVS +declare -A ACCOUNT_ENVS + +ENV_COUNT=$(jq '.env_vars // [] | length' /data/options.json 2>/dev/null || echo 0) +if [ "$ENV_COUNT" -gt 0 ]; then + bashio::log.info "Processing ${ENV_COUNT} env_var(s) for Settings.yaml" +fi +for idx in $(seq 0 $((ENV_COUNT - 1))); do + ENAME=$(jq -r ".env_vars[${idx}].name" /data/options.json) + EVALUE=$(jq -r ".env_vars[${idx}].value // \"\"" /data/options.json) + [ -z "$ENAME" ] && continue + [ -z "$EVALUE" ] && continue + [ "$ENAME" = "TZ" ] && continue # TZ is a system env var, not an ImmichFrame setting + + if in_list "$ENAME" "$ACCOUNT_KEYS"; then + ACCOUNT_ENVS["$ENAME"]="$EVALUE" + bashio::log.info " env_var ${ENAME} -> Account setting" + else + GENERAL_ENVS["$ENAME"]="$EVALUE" + bashio::log.info " env_var ${ENAME} -> General setting" + fi +done + +# General options from the addon schema +GENERAL_SCHEMA_OPTS="Interval TransitionDuration ShowClock ClockFormat ClockDateFormat + ShowProgressBar ShowPhotoDate PhotoDateFormat ShowImageDesc ShowPeopleDesc + ShowTagsDesc ShowAlbumName ShowImageLocation ShowWeatherDescription + ImageZoom ImagePan ImageFill PlayAudio PrimaryColor SecondaryColor Style + Layout BaseFontSize Language WeatherApiKey UnitSystem WeatherLatLong + ImageLocationFormat DownloadImages RenewImagesDuration RefreshAlbumPeopleInterval" + +# Per-account options from the addon schema (besides ImmichServerUrl/ApiKey) +ACCOUNT_SCHEMA_OPTS="Albums ExcludedAlbums People Tags ShowFavorites ShowMemories + ShowArchived ShowVideos ImagesFromDays ImagesFromDate ImagesUntilDate Rating" + +# ---- Build Settings.yaml ---- +{ + # -- General section -- + GENERAL_STARTED=false + + for opt in $GENERAL_SCHEMA_OPTS; do + if config_has ".$opt"; then + $GENERAL_STARTED || { echo "General:"; GENERAL_STARTED=true; } + yaml_kv " " "$opt" "$(config_val ".$opt")" + fi + done + + # Add general env_vars (skip if already set via schema option) + for key in "${!GENERAL_ENVS[@]}"; do + if ! config_has ".$key"; then + $GENERAL_STARTED || { echo "General:"; GENERAL_STARTED=true; } + yaml_kv " " "$key" "${GENERAL_ENVS[$key]}" + fi + done + + # -- Accounts section -- + ACCOUNT_COUNT=$(jq '.Accounts // [] | length' /data/options.json 2>/dev/null || echo 0) + + if [ "$ACCOUNT_COUNT" -gt 0 ]; then + bashio::log.info "Configuring ${ACCOUNT_COUNT} account(s) from Accounts list" echo "Accounts:" for i in $(seq 0 $((ACCOUNT_COUNT - 1))); do - SERVER_URL=$(jq -r ".Accounts[${i}].ImmichServerUrl" /data/options.json) - API_KEY=$(jq -r ".Accounts[${i}].ApiKey" /data/options.json) - # Escape single quotes for YAML single-quoted strings - SERVER_URL="${SERVER_URL//\'/\'\'}" - API_KEY="${API_KEY//\'/\'\'}" - echo " - ImmichServerUrl: '${SERVER_URL}'" - echo " ApiKey: '${API_KEY}'" - bashio::log.info " Account $((i + 1)): ${SERVER_URL}" + SRV="$(config_val ".Accounts[${i}].ImmichServerUrl")" + KEY="$(config_val ".Accounts[${i}].ApiKey")" + echo " - ImmichServerUrl: '${SRV//\'/\'\'}'" + echo " ApiKey: '${KEY//\'/\'\'}'" + + for opt in $ACCOUNT_SCHEMA_OPTS; do + if config_has ".Accounts[${i}].${opt}"; then + yaml_kv " " "$opt" "$(config_val ".Accounts[${i}].${opt}")" + fi + done + + # Apply account-level env_vars (only if not already set in this account's schema) + for key in "${!ACCOUNT_ENVS[@]}"; do + in_list "$key" " ImmichServerUrl ApiKey " && continue + if ! config_has ".Accounts[${i}].${key}"; then + yaml_kv " " "$key" "${ACCOUNT_ENVS[$key]}" + fi + done + + bashio::log.info " Account $((i + 1)): ${SRV}" done - } > "${SETTINGS_FILE}" - chmod 600 "${SETTINGS_FILE}" - bashio::log.info "Settings.yaml generated at ${SETTINGS_FILE}" -elif bashio::config.has_value 'ApiKey' && bashio::config.has_value 'ImmichServerUrl'; then - bashio::log.info "Using single account configuration" - SERVER_URL=$(bashio::config 'ImmichServerUrl') - API_KEY=$(bashio::config 'ApiKey') - # Escape single quotes for YAML single-quoted strings - SERVER_URL="${SERVER_URL//\'/\'\'}" - API_KEY="${API_KEY//\'/\'\'}" - { + + elif config_has '.ApiKey' && config_has '.ImmichServerUrl'; then + bashio::log.info "Using single account configuration" + SRV="$(config_val '.ImmichServerUrl')" + KEY="$(config_val '.ApiKey')" echo "Accounts:" - echo " - ImmichServerUrl: '${SERVER_URL}'" - echo " ApiKey: '${API_KEY}'" - } > "${SETTINGS_FILE}" - chmod 600 "${SETTINGS_FILE}" - bashio::log.info "Settings.yaml generated at ${SETTINGS_FILE}" -else - bashio::log.fatal "No accounts configured! Set either 'Accounts' list or both 'ApiKey' and 'ImmichServerUrl'" - exit 1 -fi + echo " - ImmichServerUrl: '${SRV//\'/\'\'}'" + echo " ApiKey: '${KEY//\'/\'\'}'" + + # Apply account-level env_vars to the single account + for key in "${!ACCOUNT_ENVS[@]}"; do + in_list "$key" " ImmichServerUrl ApiKey " && continue + yaml_kv " " "$key" "${ACCOUNT_ENVS[$key]}" + done + + elif [ -n "${ACCOUNT_ENVS[ImmichServerUrl]:-}" ] && [ -n "${ACCOUNT_ENVS[ApiKey]:-}" ]; then + bashio::log.info "Using account configuration from env_vars" + echo "Accounts:" + echo " - ImmichServerUrl: '${ACCOUNT_ENVS[ImmichServerUrl]//\'/\'\'}'" + echo " ApiKey: '${ACCOUNT_ENVS[ApiKey]//\'/\'\'}'" + + for key in "${!ACCOUNT_ENVS[@]}"; do + in_list "$key" " ImmichServerUrl ApiKey " && continue + yaml_kv " " "$key" "${ACCOUNT_ENVS[$key]}" + done + else + bashio::log.fatal "No accounts configured! Set either 'Accounts' list or both 'ApiKey' and 'ImmichServerUrl'" + exit 1 + fi + +} > "${SETTINGS_FILE}" +chmod 600 "${SETTINGS_FILE}" +bashio::log.info "Settings.yaml generated at ${SETTINGS_FILE}" + +# Log contents (mask sensitive values) +bashio::log.info "--- Generated Settings.yaml ---" +sed -E 's/(ApiKey:).*/\1 *****/;s/(AuthenticationSecret:).*/\1 *****/' "${SETTINGS_FILE}" | while IFS= read -r line; do + bashio::log.info "$line" +done +bashio::log.info "-------------------------------" export IMMICHFRAME_CONFIG_PATH=/config/Config exec dotnet ImmichFrame.WebApi.dll From 75750d46580066fe9faa9d6ca9a1672276037ac5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 09:04:46 +0000 Subject: [PATCH 3/3] Mask WeatherApiKey in log output Co-authored-by: alexbelgium <44178713+alexbelgium@users.noreply.github.com> --- immich_frame/rootfs/etc/cont-init.d/99-run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/immich_frame/rootfs/etc/cont-init.d/99-run.sh b/immich_frame/rootfs/etc/cont-init.d/99-run.sh index 6abd7bae2..f1583ef9e 100755 --- a/immich_frame/rootfs/etc/cont-init.d/99-run.sh +++ b/immich_frame/rootfs/etc/cont-init.d/99-run.sh @@ -198,7 +198,7 @@ bashio::log.info "Settings.yaml generated at ${SETTINGS_FILE}" # Log contents (mask sensitive values) bashio::log.info "--- Generated Settings.yaml ---" -sed -E 's/(ApiKey:).*/\1 *****/;s/(AuthenticationSecret:).*/\1 *****/' "${SETTINGS_FILE}" | while IFS= read -r line; do +sed -E 's/(ApiKey:).*/\1 *****/;s/(AuthenticationSecret:).*/\1 *****/;s/(WeatherApiKey:).*/\1 *****/' "${SETTINGS_FILE}" | while IFS= read -r line; do bashio::log.info "$line" done bashio::log.info "-------------------------------"