Backend changes for full blueprint feature parity:
Database models:
- MessageTemplate: add body_ru (locale variant), event_type field
- AlbumTracker: add track_images, track_videos, notify_favorites_only,
include_people, include_asset_details, max_assets_to_show,
assets_order_by, assets_order fields
- New ScheduledJob model: supports periodic_summary, scheduled_assets,
and memory (On This Day) notification modes with full config
(times, interval, album_mode, limit, filters, sorting)
API:
- New /api/scheduled/* CRUD endpoints for scheduled jobs
- Tracker create/update accept all new filtering fields
- Tracker response includes new fields
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix jinja2.sandbox import: use `from jinja2.sandbox import
SandboxedEnvironment` (dotted attribute access doesn't work)
in templates.py, sync.py, and notifier.py
- Fix greenlet crash in tracker trigger: SQLAlchemy async sessions
can't survive across aiohttp.ClientSession context managers.
Eagerly load all tracker/server data before entering HTTP context.
Split check_tracker into check_tracker (scheduler, own session)
and check_tracker_with_session (API, reuses route session).
- Fix _check_album to accept pre-loaded params instead of tracker
object (avoids lazy-load access after greenlet context break)
Tested end-to-end against live Immich server:
- Server connection + album browsing: OK (39 albums)
- Template creation + preview: OK
- Webhook target creation: OK
- Tracker creation + trigger: OK (initialized 4 assets)
- Second trigger: OK (no_changes detected)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fixes 5 issues identified by code-reviewer agent:
1. (Critical) EventLog.tracker_id now nullable - use None instead
of 0 when tracker name doesn't match, avoiding FK constraint
violations on PostgreSQL
2. (Critical) Replace jinja2.Environment with SandboxedEnvironment
in all 3 server template rendering locations to prevent SSTI
3. (Important) Rebuild sync_client in _async_update_listener when
server URL/key options change, propagate to all coordinators
4. (Important) Validate partial server config - require both URL
and API key or neither, with clear error message
5. (Important) Name fire-and-forget sync task for debugging
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Enable the HAOS integration to optionally connect to the standalone
Immich Watcher server for config sync and event reporting.
Server-side:
- New /api/sync/* endpoints: GET trackers, POST template render,
POST event report
- API key auth via X-API-Key header (accepts JWT access tokens)
Integration-side:
- New sync.py: ServerSyncClient with graceful error handling
(all methods return defaults on connection failure)
- Options flow: optional server_url and server_api_key fields
with connection validation
- Coordinator: fire-and-forget event reporting to server when
album changes are detected
- Translations: en.json and ru.json updated with new fields
The connection is fully additive -- the integration works identically
without a server URL configured. Server failures never break HA.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Build a complete standalone web server for Immich album change
notifications, independent of Home Assistant. Uses the shared
core library from Phase 1.
Server features:
- FastAPI with async SQLite (SQLModel + aiosqlite)
- Multi-user auth with JWT (admin/user roles, setup wizard)
- CRUD APIs: Immich servers, album trackers, message templates,
notification targets (Telegram + webhook), user management
- APScheduler background polling per tracker
- Jinja2 template rendering with live preview
- Album browser proxied from Immich API
- Event logging and dashboard status endpoint
- Docker deployment (single container, SQLite in volume)
39 API routes, 14 integration tests passing.
Also adds Phase 6 (Claude AI Telegram bot enhancement) to the
primary plan as an optional future phase.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>