Complete config Immich/Powertools and Kiosk

For ML i’m using a second machine with GPU.

name: immich

services:
  immich-server:
    container_name: immich_server
    image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
    # extends:
    #   file: hwaccel.transcoding.yml
    #   service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding
    volumes:
      # Do not edit the next line. If you want to change the media storage location on your system, edit the value of UPLOAD_LOCATION in the .env file
      - ${UPLOAD_LOCATION}:/data
      - /etc/localtime:/etc/localtime:ro
    env_file:
      - .env
    ports:
      - '2283:2283'
    depends_on:
      - redis
      - database
    restart: always
    healthcheck:
      disable: false

  immich-machine-learning:
    container_name: immich_machine_learning
    # For hardware acceleration, add one of -[armnn, cuda, rocm, openvino, rknn] to the image tag.
    # Example tag: ${IMMICH_VERSION:-release}-cuda
    image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
    # extends: # uncomment this section for hardware acceleration - see https://docs.immich.app/features/ml-hardware-acceleration
    #   file: hwaccel.ml.yml
    #   service: cpu # set to one of [armnn, cuda, rocm, openvino, openvino-wsl, rknn] for accelerated inference - use the `-wsl` version for WSL2 where applicable
    volumes:
      - model-cache:/cache
    env_file:
      - .env
    restart: always
    healthcheck:
      disable: false

  immich-kiosk:
    image: ghcr.io/damongolding/immich-kiosk:latest
    container_name: immich-kiosk
    environment:
        LANG: "en_GB"
        TZ: "Europe/Amsterdam"
        # Required settings
        KIOSK_IMMICH_API_KEY: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx"
        KIOSK_IMMICH_URL: "https://mypix.henriaanstoot.nl/"
        # External url for image links/QR codes
        KIOSK_IMMICH_EXTERNAL_URL: ""
        # Clock
        KIOSK_SHOW_TIME: true
        KIOSK_TIME_FORMAT: 24
        KIOSK_SHOW_DATE: false
        KIOSK_DATE_FORMAT: YYYY/MM/DD
        KIOSK_CLOCK_SOURCE: client
        # Kiosk behaviour
        KIOSK_DURATION: 60
        KIOSK_DISABLE_SCREENSAVER: false
        KIOSK_OPTIMIZE_IMAGES: false
        KIOSK_USE_GPU: true
        KIOSK_BURN_IN_INTERVAL: 0
        KIOSK_BURN_IN_DURATION: 30
        KIOSK_BURN_IN_OPACITY: 70
        # Asset sources
        KIOSK_SHOW_ARCHIVED: false
        KIOSK_ALBUMS: ""
        KIOSK_ALBUM_ORDER: random
        KIOSK_EXCLUDED_ALBUMS: "ALBUM_ID,ALBUM_ID,ALBUM_ID"
        KIOSK_PEOPLE: ""
        KIOSK_REQUIRE_ALL_PEOPLE: false
        KIOSK_EXCLUDED_PEOPLE: "PERSON_ID,PERSON_ID,PERSON_ID"
        KIOSK_DATES: ""
        KIOSK_TAGS: ""
        KIOSK_EXCLUDED_TAGS: "TAG_VALUE,TAG_VALUE,TAG_VALUE"
        KIOSK_RATING: -1
        KIOSK_EXCLUDED_PARTNERS: "PARTNER_ID"
        KIOSK_MEMORIES: false
        KIOSK_BLACKLIST: "ASSET_ID,ASSET_ID,ASSET_ID"
        # FILTER
        KIOSK_FILTER_DATE: ""
        KIOSK_FILTER_NEWEST: 0
        KIOSK_FILTER_EXCLUDE_FACES: false
        KIOSK_FILTER_FAVORITES: false
        # UI
        KIOSK_SHOW_PROGRESS_BAR: false
        KIOSK_DISABLE_NAVIGATION: false
        KIOSK_DISABLE_UI: false
        KIOSK_FRAMELESS: false
        KIOSK_HIDE_CURSOR: false
        KIOSK_FONT_SIZE: 100
        KIOSK_BACKGROUND_BLUR: true
        KIOSK_BACKGROUND_BLUR_AMOUNT: 10
        KIOSK_THEME: fade
        KIOSK_LAYOUT: single
        KIOSK_SHOW_USER: false
        # Sleep mode
        # KIOSK_SLEEP_START: 22
        # KIOSK_SLEEP_END: 7
        # KIOSK_SLEEP_DIM_SCREEN: false
        # Transistion options
        KIOSK_TRANSITION: none
        KIOSK_FADE_TRANSITION_DURATION: 1
        KIOSK_CROSS_FADE_TRANSITION_DURATION: 1
        # Image display settings
        KIOSK_IMAGE_FIT: contain
        KIOSK_IMAGE_EFFECT: zoom
        KIOSK_IMAGE_EFFECT_AMOUNT: 120
        KIOSK_USE_ORIGINAL_IMAGE: false
        # Video
        KIOSK_SHOW_VIDEOS: true
        KIOSK_LIVE_PHOTOS: false
        KIOSK_LIVE_PHOTO_LOOP_DELAY: 0
        KIOSK_SHOW_ANIMATED_GIFS: false
        # Image metadata
        KIOSK_SHOW_IMAGE_RATING: true
        KIOSK_SHOW_OWNER: false
        KIOSK_SHOW_ALBUM_NAME: true
        KIOSK_SHOW_PERSON_NAME: false
        KIOSK_SHOW_PERSON_AGE: false
        KIOSK_SHOW_IMAGE_TIME: true
        KIOSK_IMAGE_TIME_FORMAT: 24
        KIOSK_SHOW_IMAGE_DATE: true
        KIOSK_IMAGE_DATE_FORMAT: YYYY-MM-DD
        KIOSK_SHOW_IMAGE_DESCRIPTION: true
        KIOSK_SHOW_IMAGE_CAMERA: false
        KIOSK_SHOW_IMAGE_EXIF: true
        KIOSK_SHOW_IMAGE_LOCATION: true
        KIOSK_HIDE_COUNTRIES: "HIDDEN_COUNTRY,HIDDEN_COUNTRY"
        KIOSK_SHOW_IMAGE_ID: false
        KIOSK_SHOW_IMAGE_QR: false
        KIOSK_SHOW_MORE_INFO: true
        KIOSK_SHOW_MORE_INFO_IMAGE_LINK: true
        KIOSK_SHOW_MORE_INFO_QR_CODE: true
        # More info actions
        KIOSK_LIKE_BUTTON_ACTION: favorite
        KIOSK_HIDE_BUTTON_ACTION: tag
        # Kiosk settings
        KIOSK_PORT: 3000
        KIOSK_BEHIND_PROXY: false
        KIOSK_DISABLE_URL_QUERIES: false
        KIOSK_DISABLE_CONFIG_ENDPOINT: false
        KIOSK_ENABLE_URL_BUILDER: false
        KIOSK_WATCH_CONFIG: false
        KIOSK_FETCHED_ASSETS_SIZE: 1000
        KIOSK_HTTP_TIMEOUT: 20
        KIOSK_PASSWORD: ""
        KIOSK_CACHE: true
        KIOSK_PREFETCH: true
        KIOSK_ASSET_WEIGHTING: true
    ports:
      - 3000:3000
    restart: always
    healthcheck:
      test: ["CMD", "/kiosk", "--healthcheck"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s


  redis:
    container_name: immich_redis
    image: docker.io/valkey/valkey:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    healthcheck:
      test: redis-cli ping || exit 1
    restart: always

  database:
    container_name: immich_postgres
    image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_USER: ${DB_USERNAME}
      POSTGRES_DB: ${DB_DATABASE_NAME}
      POSTGRES_INITDB_ARGS: '--data-checksums'
      # Uncomment the DB_STORAGE_TYPE: 'HDD' var if your database isn't stored on SSDs
      # DB_STORAGE_TYPE: 'HDD'
    volumes:
      # Do not edit the next line. If you want to change the database storage location on your system, edit the value of DB_DATA_LOCATION in the .env file
      - ${DB_DATA_LOCATION}:/var/lib/postgresql/data
    shm_size: 128mb
    restart: always
  power-tools:
    container_name: immich_power_tools
    image: ghcr.io/immich-power-tools/immich-power-tools:latest
    volumes:
      - immich-power-tools-data:/app/data
    ports:
      - "8001:3000"
    env_file:
      - .env
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/api/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

volumes:
  model-cache:
  immich-power-tools-data:

Onkyo old to new

Our Onkyo was giving problems, some channels are breaking up.
To have some channels back, we needed to crank up the volume for a few seconds.

So I thought .. preamps!

So I opened the Onkyo and started investigating.

We used it as a 5.1 surround sound system. So I’ve got PreAmp channels to spare, so lets switch the boards!


Well, that didn’t work.
Reading forums, it COULD be the sound chip, on the main board.
Because of the heat over the years, it could losing contact with the main PCB.

So I put my heatgun to work, but to no avail.
One of the square chips on the right in first photo.

The AMP being 15+ years old, we decided to get a new one.
Researching best amp’s in 2026 … in our price range.

Onkyo TX-RZ50

Amazing …

Using the DIRAC sound setup, perfect again.

Using the same IP, our Home Assistant setup mostly worked without changing much.

Cheap yellow display

I’ve posted about these a while ago. (OpenHASP)

I bought some new ones.
These will be used for serial logging, plotting data and terminal emulation for a 6502 system.

I’ve got some M5Stack credit card sized keyboards, so next to do is implement these instead of using the onscreen keyboard.

Immich Workflow Filters

UPDATE 20260729 : There are EXIF filters in workflows now!

Put your media in the correct directories using workflows.

Below the regex for a Samsung phone

Image
^\d{8}_\d{6}\.(jpg|jpeg|heic|png)$

Video
^\d{8}_\d{6}\.(mp4|mov|mkv)$

WhatsApp Image
^IMG-\d{8}-WA\d+\.(jpg|jpeg|png|webp)$

WhatsApp Video
^VID-\d{8}-WA\d+\.(mp4|3gp)$

Signal Image
^(signal-|Signal-).*\.(jpg|jpeg|png|webp)$

Signal Video
^(signal-|Signal-).*\.(mp4|mov)$

Flashing a new ROM on an Android tablet.

It has been a long time, since I flashed the last one.
Some things have changed.

I’ve flashed tablets and phones, now a tablet again.

The small tablet was really slow, and could not be upgraded to support a new webkit version.

So I installed a minimal Lineage OS on it.

Now it can be used as a immich web kiosk.

Some notes for myself

adb reboot bootloader
adb fastboot oem unlock
fastboot flash recovery twrp-3.7.0_9-1-flo_followmsi.img # this didn´t work, needed an older version
twrp-3.3.0-0-flo.img worked
adb push lanchon-repit-20210221-system=max-cache=16M+wipe-flo.zip /tmp/lanchon-repit-20210221-system=max-cache=16M+wipe-flo.zip
also lineage-20.0-20230830-UNOFFICIAL-flo.zip
adb install -r F-Droid.apk
adb install -r Rotation+Control_1.1_APKPure.apk # This because trebuchet rotate is broken.

While waiting for flashing, I installed nerdfonts, and patched them with cool icons. Then I installed superfile, a terminal file manager. Nice!

Dual PICO UART test with CirquitPython

Test for UART serial communication for my whack-a-mole game

CODE for Client

import board
import busio
import time

uart = busio.UART(
    tx=board.GP16,
    rx=board.GP17,
    baudrate=115200,
    timeout=1,
)

time.sleep(2)

commands = ["PING", "HELLO", "TEST"]
while True:
    for cmd in commands:
        print("Sending:", cmd)
        uart.write((cmd + "\n").encode())

        response = uart.readline()

        if response:
            print("Response:", response.decode().strip())
        else:
            print("No response")

        time.sleep(1)

CODE for server

import board
import busio
import digitalio
import time

# UART pins
uart = busio.UART(
    tx=board.GP0,
    rx=board.GP1,
    baudrate=115200,
    timeout=0.1
)

# Onboard LED
led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT

print("UART LED server ready")

while True:
    data = uart.readline()

    if data:
        command = data.decode("utf-8").strip()
        print("Received:", command)

        if command == "PING":
            # Blink LED
            led.value = True
            time.sleep(0.2)
            led.value = False

            # Reply
            uart.write(b"PONG\n")

        else:
            uart.write(b"UNKNOWN\n")

    time.sleep(0.01)

New CO2 sensor

Today I got, amongst other goodies a new CO2 sensor in the mail.

This is a MH-Z19C CO2/Temperature sensor.
(Easy to implement with ESPHome)

Calibrate using HD 7 seconds to GND
(Do this in a well ventilated room)

YAML Code

# Board: Wemos D1 Mini (ESP8266) (Wemos)
# Definition: definitions/boards/d1_mini/manifest.yaml

esphome:
  name: newco2
  friendly_name: NewCO2

esp8266:
  board: d1_mini

logger:

api:
  encryption:
    key: "c/vSFcxi1YgYlXtvsjXXXXXXXXXXXXXXXXXXXXXX="

ota:
  - platform: esphome

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  ap:
    ssid: NewCO2 Fallback Hotspot
    password: "tepe9XXXXXXXXXXX"

captive_portal:

uart:
  - baud_rate: 9600
    rx_pin: 4
    tx_pin: 5
    id: uart_1

sensor:
  - platform: mhz19
    uart_id: uart_1
    co2:
      id: sensor_mhz19_1_co2
      name: CO2
    temperature:
      id: sensor_mhz19_1_temperature
      name: Temperature
    id: sensor_mhz19_1
    automatic_baseline_calibration: true
    detection_range: "5000ppm"
    
switch:
  - platform: template
    name: "MH-Z19 ABC"
    optimistic: true
    on_turn_on:
      mhz19.abc_enable: sensor_mhz19_1
    on_turn_off:
      mhz19.abc_disable: sensor_mhz19_1

light:
  - platform: status_led
    name: status
    id: light_status_led_1
    pin: GPIO12

Blender plugin for 8 potmeter input

This weekend I saw a corridor crew video about controlling CGI objects using input devices.

This made me think of a half finished project I did a while ago.
So lets finish it.

(I know, dirty screen .. too much outside hacking)

8Angle M5 thingy using I2C (Pin D1 and D2)

CODE for wemos

Very simple code to read pots using I2C and printing values on the serial output using the wemos.
Only needs 5V,GND,SDA,SCL (D1 D2)

279,206,520,1023,1023,60,300,985,0
Output example 8 potmeters 0-1023 value and last value 0 or 1 for the switch

Serial plotter example below

#include "m5angle8.h"

M5ANGLE8 MM;

void setup()
{
  Serial.begin(115200);

  Serial.println();
  delay(100);

  Wire.begin();
  MM.begin();
}


void loop()
{
  for (int ch = 0; ch < 8; ch++)
  {
    Serial.print(MM.analogRead(ch, 10));
    Serial.print(",");
    delay(1);
  }
  Serial.print(MM.inputSwitch());
  Serial.print("\n");
  delay(100);
}

Plugin for Blender

filename unit8angle_blender/__init__.py

made into a zip you can install using

zip -r myplugin.zip unit8angle_blender

Blender edit > preferences > add-ons > Install from disk

import bpy
import serial
import serial.tools.list_ports

ser = None


def open_serial():
    global ser

    if ser:
        return

    # Change this to your COM port
    ser = serial.Serial("/dev/ttyUSB0",115200,timeout=0)


class SERIAL_OT_start(bpy.types.Operator):
    bl_idname = "wm.unit8_start"
    bl_label = "Start Unit8"

    _timer = None

    def modal(self, context, event):

        if event.type == 'TIMER':

            global ser

            if ser and ser.in_waiting:

                line = ser.readline().decode(errors="ignore").strip()

                try:

                    values = list(map(int,line.split(",")))

                    if len(values) >= 8:

                        obj = context.active_object

                        if obj:

                            obj.location.x = values[0] / 100.0
                            obj.location.y = values[1] / 100.0
                            obj.location.z = values[2] / 100.0

#                            obj.location.x = values[0] / 1023.0
#                            obj.location.y = values[1] / 1023.0
#                            obj.location.z = values[2] / 1023.0

                            obj.rotation_euler.x = values[3] / 1023.0 * 6.28318
                            obj.rotation_euler.y = values[4] / 1023.0 * 6.28318
                            obj.rotation_euler.z = values[5] / 1023.0 * 6.28318

                            s = 0.1 + values[6] / 1023.0 * 3.0
                            obj.scale = (s,s,s)

                except Exception:
                    pass

        return {'PASS_THROUGH'}

    def execute(self, context):

        open_serial()

        wm = context.window_manager

        self._timer = wm.event_timer_add(0.01, window=context.window)

        wm.modal_handler_add(self)

        return {'RUNNING_MODAL'}


class SERIAL_PT_panel(bpy.types.Panel):
    bl_label = "Unit8Angle"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = 'Unit8'

    def draw(self, context):
        self.layout.operator("wm.unit8_start")


classes = (
    SERIAL_OT_start,
    SERIAL_PT_panel,
)


def register():
    for c in classes:
        bpy.utils.register_class(c)


def unregister():
    for c in reversed(classes):
        bpy.utils.unregister_class(c)

Yesterday I saw some Art I recognized .. Chris Foss

While browsing the Internet, something popped up.
An image of a spaceship, and I instantly remembered where I’ve seen it before.

When I was a teenager I got hold of a book from Chris Foss, it was a large book with only around 8-10 pages.

But I loved the images.
(I shamefully admit, that I traded the book for something else a long time ago)
46x29cm printed in 1977
(See more at end of post)

At that time I had several artists that really peaked my interest in graphical arts.
I still love to paint and draw.

List of artists

Maurits Cornelis Escher https://mcescher.com/

A dutch artist know for impossible drawings.
He used woodcut, lithograph and mezzotint.
(I tried to do a mezzotint using a copper pcb plate)

H.G Giger https://www.hrgigermuseum.com/

A Swiss artist best known for his airbrushed images that blended human physiques with machines, an art style known as “biomechanical”.
He designed for several movies : Alien, Species and Dune

Banksy https://www.banksy.co.uk/

Makes statues, installations and stencil art.
The latter I really love. Tried to make some myself.

Hajime Sorayama

Airbrush master

Nico Vrielink https://nicovrielink.eu/

Mostly asian ladies (his wife). I like the long linestrokes.

Back to Chris Foss. https://chrisfossart.com/

Made many Science fiction book covers, but also a great influence of spaceships in multiple movies.

The images I recognized from 40+ years ago:

A more recent artist I discovered (10-ish years ago) is Zdzisław Beksiński.
https://www.wikiart.org/en/zdzislaw-beksinski
He was a Polish painter, photographer, and sculptor.

Reminds me of : Bag of Bones a 1998 horror novel by Stephen King.