- New Modal.svelte component: overlay with backdrop click to close,
title bar, reusable via children snippet
- Layout: password change moved from inline sidebar form to modal
dialog. Clean UX with current + new password fields.
- Users page: 🔑 button per user opens modal for admin to set a
new password (no current password required for admin reset)
- Backend: PUT /api/users/{id}/password (admin only, min 6 chars)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 features implemented:
1. Server health indicator: green/red/yellow dot on each server card.
Pings Immich in background on page load. New GET /api/servers/{id}/ping.
2. Album selector filter: search input above album list in tracker form.
Filters by name as you type (case-insensitive). Shows total count.
3. Album last update time: each album in the selector shows its
updatedAt date. Backend now returns updatedAt from Immich API.
4. Full Jinja2 template engine in notifier:
- build_full_context() assembles all ~40 variables from blueprint
- Common date/location detection across assets
- Per-asset date/location when they differ
- Favorite indicator, people formatting, asset list with truncation
- Video warning for Telegram
- All template slots from TemplateConfig used contextually
5. Password change: PUT /api/auth/password endpoint (validates current
password, min 6 chars). UI in sidebar footer with inline form.
Also: Phase 9 plan (Telegram bot management) added.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Core library:
- New combine_album_assets() in asset_utils.py: smart redistribution
of unused quota when albums return fewer assets than their share.
Two-pass algorithm: even split then redistribute remainder.
- 6 new tests (56 total passing).
Frontend:
- Fix "leave empty to keep current" not localized in server edit.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New pages:
- /tracking-configs: Full CRUD with event tracking, asset display,
periodic summary, scheduled assets, and memory mode sections.
Collapsible sub-sections that show/hide based on enabled state.
- /template-configs: Full CRUD with all 21 template slots organized
into 5 fieldsets (event messages, asset formatting, date/location,
scheduled messages, telegram). Preview support per slot.
Updated pages:
- Targets: added tracking_config_id + template_config_id selectors
(dropdowns populated from configs). Configs are reusable.
- Trackers: simplified to album selection + scan interval + targets.
Added Test, Test Periodic, Test Memory buttons per tracker.
- Nav: replaced Templates with Tracking + Templates config links
Other fixes:
- Language button: now triggers window.location.reload() to force
all child pages to re-evaluate t() calls
- Dark theme buttons: changed primary color to dark gray in dark mode
- Removed old /templates page (replaced by /template-configs)
- Added .gitignore for __pycache__ in server package
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Major model restructuring for clean separation of concerns:
New entities:
- TrackingConfig: What to react to (event types, asset filters,
periodic/scheduled/memory mode config) - reusable across targets
- TemplateConfig: All ~15 template slots from blueprint (event
messages, asset formatting, date/location, scheduled messages)
with full defaults - separate entities per locale
Changed entities:
- AlbumTracker: Simplified to album selection + polling + target_ids
(removed event_types, template_id, all filter fields)
- NotificationTarget: Extended with tracking_config_id and
template_config_id FKs (many-to-one, reusable configs)
Removed entities:
- MessageTemplate (replaced by TemplateConfig)
- ScheduledJob (absorbed into TrackingConfig)
Updated services:
- watcher.py: Each target checked against its own tracking_config
for event filtering before sending notification
- notifier.py: Uses target's template_config to select the right
template slot based on event type
New API routes:
- /api/tracking-configs/* (CRUD)
- /api/template-configs/* (CRUD + per-slot preview)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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 7 issues identified by code-reviewer agent:
1. (Critical) Webhook endpoint now validates X-Telegram-Bot-Api-
Secret-Token header against configured secret, and verifies
bot_token matches a stored NotificationTarget
2. (Critical) register/unregister webhook endpoints now require
JWT auth via Depends(get_current_user); register passes
secret_token to Telegram setWebhook
3. (Critical) Conversation dict now uses OrderedDict with LRU
eviction (max 100 chats); trim happens BEFORE API call
4. (Important) Tool-use responses no longer stored in conversation
history (avoids corrupted multi-turn state)
5. (Important) Singleton AsyncAnthropic client (module-level,
lazily initialized once) - no more connection pool leaks
6. (Important) Markdown retry now uses payload.pop("parse_mode")
instead of setting empty string
7. (Important) All user-controlled data wrapped in <data> tags
with _sanitize() helper (truncation + newline stripping)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Integrate Claude AI into the notification system for intelligent
conversational interactions and AI-powered captions.
New modules:
- ai/service.py: Claude API client with conversation history,
caption generation, and album activity summarization
- ai/telegram_webhook.py: Telegram webhook handler for incoming
bot messages, routes to AI service for responses
Features:
- Conversational bot: users chat with the bot about albums
- AI captions: intelligent notification messages based on album
context (people, locations, dates) - enabled per target via
"ai_captions" config flag
- Album summaries: "what's new?" triggers AI-generated overview
- /start command with welcome message
- Webhook register/unregister endpoints
Architecture:
- Per-chat conversation history (in-memory, capped at 20 messages)
- Graceful degradation: AI features completely disabled without
IMMICH_WATCHER_ANTHROPIC_API_KEY env var (zero impact)
- AI caption failure falls back to Jinja2 template rendering
- Health endpoint reports ai_enabled status
Config: IMMICH_WATCHER_ANTHROPIC_API_KEY, IMMICH_WATCHER_AI_MODEL,
IMMICH_WATCHER_AI_MAX_TOKENS
Server now has 45 API routes (was 42 after Phase 5).
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>
Extract HA-independent logic from the integration into packages/core/
as a standalone Python library (immich-watcher-core). This is the first
phase of restructuring the project to support a standalone web app
alongside the existing HAOS integration.
Core library modules:
- models: SharedLinkInfo, AssetInfo, AlbumData, AlbumChange dataclasses
- immich_client: Async Immich API client (aiohttp, session-injected)
- change_detector: Pure function for album change detection
- asset_utils: Filtering, sorting, URL building utilities
- telegram/client: Full Telegram Bot API (text, photo, video, media groups)
- telegram/cache: File ID cache with pluggable storage backend
- telegram/media: Media size checks, URL extraction, group splitting
- notifications/queue: Persistent notification queue
- storage: StorageBackend protocol + JSON file implementation
All modules have zero Home Assistant imports. 50 unit tests passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>