# Immich Watcher: Standalone Web App + Shared Core Architecture **Status**: Planning **Created**: 2026-03-19 --- ## Context The current `immich_album_watcher` HA integration contains ~3,600 lines of tightly coupled code: Immich API client, change detection, Telegram notifications, and HA entities/services. A separate HA Blueprint (~2,000 lines) adds message templating, filtering, scheduled notifications, and memory mode. **Goal**: Enable Immich album change notifications **without Home Assistant** via a standalone web application, while keeping the HAOS integration functional and sharing as much logic as possible. --- ## Decisions | Decision | Choice | Rationale | |---|---|---| | Architecture | Hybrid (Option C) | HAOS standalone with shared core lib, optional server sync. No breaking changes for HA-only users. | | Frontend | SvelteKit + Shadcn-svelte | Small bundle, fast, calm UI aesthetic. Good fit for self-hosted. | | Notifications | Telegram + Generic webhook | Telegram from existing code. Webhook enables Discord/Slack/ntfy/custom. | | Auth | Multi-user (admin/user roles) | Supports shared Immich servers. Admin manages servers/users, users manage own trackers. | | Backend | FastAPI + SQLite + APScheduler | Async-native Python, zero external DB deps, proven scheduler. | --- ## Repository Structure ``` immich-watcher/ packages/ core/ # Shared Python library pyproject.toml src/immich_watcher_core/ immich_client.py # Immich API (from coordinator.py) models.py # AssetInfo, AlbumData, AlbumChange, SharedLinkInfo change_detector.py # Change detection (from coordinator.py) telegram/ client.py # Telegram Bot API (from sensor.py) cache.py # File cache with pluggable backend media.py # Media download, size checks, group splitting webhook/ client.py # Generic webhook notification provider templates.py # Jinja2 template engine storage.py # Abstract storage protocol + SQLite impl constants.py # Shared constants (from const.py) tests/ server/ # Standalone FastAPI app pyproject.toml src/immich_watcher_server/ main.py # FastAPI entry point database/models.py # SQLModel ORM database/migrations/ # Alembic api/ # REST endpoints services/ scheduler.py # APScheduler background polling watcher.py # Album polling orchestrator notifier.py # Notification dispatch Dockerfile docker-compose.yml haos/ # Home Assistant integration (moved) custom_components/immich_album_watcher/ ... (refactored to use core library) hacs.json frontend/ # SvelteKit web UI source package.json src/ dist/ # Built static files served by FastAPI plans/ # This folder README.md LICENSE ``` --- ## Shared Core Library Extractions | Core Module | Source File | What Gets Extracted | |---|---|---| | `immich_client.py` | coordinator.py | All `/api/` calls, session injection via constructor | | `models.py` | coordinator.py L66-326 | Dataclasses (already HA-independent) | | `change_detector.py` | coordinator.py L979-1066 | `detect_album_changes()` pure function | | `telegram/client.py` | sensor.py (~1200 lines) | Full Telegram Bot API: send_message/photo/video/media_group | | `telegram/cache.py` | storage.py | TelegramFileCache with `CacheBackend` protocol | | `templates.py` | NEW (from blueprint logic) | Jinja2 renderer with ~40 variables matching blueprint | | `storage.py` | storage.py | Abstract protocol + SQLite implementation | | `webhook/client.py` | NEW | Generic webhook POST JSON with event data | The `ImmichClient` accepts an `aiohttp.ClientSession` in constructor -- HA provides its managed session, standalone creates its own. --- ## Standalone Server Design ### Backend: FastAPI + SQLite + APScheduler **Database tables**: `users`, `immich_servers`, `notification_targets`, `message_templates`, `album_trackers`, `album_states`, `telegram_cache`, `notification_queue` **Key API endpoints**: - `POST /api/auth/setup` / `POST /api/auth/login` -- JWT auth - `CRUD /api/servers` -- Immich server connections - `GET /api/servers/{id}/albums` -- Fetch album list from Immich - `CRUD /api/trackers` -- Album trackers (album selection, event types, template overrides, targets) - `CRUD /api/templates` -- Message templates with preview - `CRUD /api/targets` -- Notification targets (Telegram chats, webhooks) - `CRUD /api/users` -- User management (admin only) - `GET /api/status` -- Dashboard data **Background**: APScheduler runs one job per tracker at its scan interval. Each job: fetch album -> detect changes -> render template -> dispatch notification. ### Frontend: SvelteKit + Shadcn-svelte **Pages**: 1. **Setup wizard** -- First-run: create admin account, connect Immich server 2. **Login** -- Username/password 3. **Dashboard** -- Active trackers overview, recent events timeline, server status 4. **Servers** -- Add/edit Immich server connections (URL + API key validation) 5. **Trackers** -- Create/edit album trackers: - Album picker (multi-select, fetched from Immich) - Event type toggles (assets added/removed, renamed, sharing changed, deleted) - Notification target selection - Template selection or per-tracker override - Scan interval, quiet hours 6. **Templates** -- Jinja2 template editor: - CodeMirror with Jinja2 syntax highlighting - Live preview with sample album data - Variable reference sidebar - Default templates for common use cases 7. **Targets** -- Manage notification destinations (Telegram chats, webhooks) 8. **Users** -- User management (admin only) 9. **Settings** -- Global defaults ### Auth: Multi-user, bcrypt + JWT - Multiple user accounts with admin/user roles - Admin: full access (user management, server configuration) - User: manage own trackers, templates, and targets - First-run setup creates initial admin account ### Deployment: Single Docker container, SQLite in mounted volume --- ## HAOS Integration Changes The integration gets refactored to delegate to core: ```python # coordinator.py becomes thin wrapper class ImmichAlbumWatcherCoordinator(DataUpdateCoordinator): def __init__(self, ...): self._client = ImmichClient(session, url, api_key) # from core async def _async_update_data(self): album = await self._client.get_album(self._album_id) change = detect_album_changes(old, album, pending) # from core if change: self._fire_events(change, album) # HA-specific return album # sensor.py Telegram methods delegate to core async def _execute_telegram_notification(self, ...): telegram = TelegramClient(session, token, cache) # from core return await telegram.send_notification(...) ``` --- ## Phases > **Rule 1**: Before starting work on any phase, create a detailed trackable subplan at `plans/phase-N-.md` with granular tasks, specific files to create/modify, and acceptance criteria. Do not begin implementation until the subplan is reviewed. > > **Rule 2**: After completing each phase, perform a detailed code review of all changes. Use the `code-reviewer` agent to check for bugs, logic errors, security vulnerabilities, code quality issues, and adherence to project conventions. Document review findings in the phase subplan under a "## Review" section and fix any issues before committing. ### Phase 1: Extract Core Library `[x]` - Extract models, Immich client, change detection, Telegram client, cache into `packages/core/` - Write unit tests for all extracted modules - **Subplan**: `plans/phase-1-core-library.md` ### Phase 2: Wire Core into HAOS Integration `[x]` - Move integration to `packages/haos/` - Refactor coordinator.py, sensor.py, storage.py to use core library - Update manifest.json, hacs.json for new structure - Verify identical behavior with real Immich server - **Subplan**: `plans/phase-2-haos-refactor.md` ### Phase 3: Build Server Backend `[x]` - FastAPI app with database, scheduler, API endpoints - Telegram + webhook notification providers - Jinja2 template engine with variable system matching blueprint - **Subplan**: `plans/phase-3-server-backend.md` ### Phase 4: Build Frontend `[x]` - SvelteKit app with all pages (setup, dashboard, trackers, templates, targets, users) - Template editor with live preview - Album picker connected to Immich API - **Subplan**: `plans/phase-4-frontend.md` ### Phase 5: HAOS-Server Sync (Optional) `[x]` - Add optional server URL to HA config flow - Implement tracker/template config sync - **Subplan**: `plans/phase-5-haos-server-sync.md` ### Phase 6: Claude AI Telegram Bot Enhancement (Optional) `[x]` - Conversational bot, AI captions, album summaries - **Subplan**: `plans/phase-6-claude-ai-bot.md` ### Phase 7: Production UI & Full Blueprint Parity `[ ]` - i18n (RU/EN), dark/light themes, OAuth via Immich - 4 notification modes: event-driven, periodic summary, scheduled assets, memory - Enhanced tracker config (filtering, sorting, telegram media) - Full template variable system (40+ vars, per-event templates) - **Subplan**: `plans/phase-7-production-ui.md` - Integrate Claude AI to enhance the Telegram notification bot - Enable conversational interactions: users can ask questions about their albums, get summaries, request specific photos - AI-powered message formatting: intelligent caption generation, album descriptions - Smart notification filtering: AI decides notification importance/relevance - Natural language tracker configuration via Telegram chat - **Subplan**: `plans/phase-6-claude-ai-bot.md` --- ## Verification 1. **Core library**: Unit tests for Immich client (mocked HTTP), change detection (pure function), Telegram client (mocked API), template rendering 2. **HAOS integration**: After refactoring, verify all existing entities, services, and events work identically with a real Immich server 3. **Server backend**: API integration tests with SQLite in-memory DB, scheduler tests 4. **Frontend**: Manual testing of all pages, template editor preview, album picker 5. **End-to-end**: Docker Compose up -> create account -> connect Immich -> create tracker -> trigger album change -> verify notification received