diff --git a/custom_components/immich_album_watcher/__init__.py b/custom_components/immich_album_watcher/__init__.py index fe5f713..61c1ef7 100644 --- a/custom_components/immich_album_watcher/__init__.py +++ b/custom_components/immich_album_watcher/__init__.py @@ -4,12 +4,12 @@ from __future__ import annotations import logging from dataclasses import dataclass -from datetime import datetime +from datetime import datetime, time as dt_time from homeassistant.config_entries import ConfigEntry, ConfigSubentry from homeassistant.core import HomeAssistant - from homeassistant.helpers.event import async_track_time_change +from homeassistant.util import dt as dt_util from .const import ( CONF_ALBUM_ID, @@ -17,8 +17,6 @@ from .const import ( CONF_API_KEY, CONF_HUB_NAME, CONF_IMMICH_URL, - CONF_QUIET_HOURS_END, - CONF_QUIET_HOURS_START, CONF_SCAN_INTERVAL, CONF_TELEGRAM_CACHE_TTL, DEFAULT_SCAN_INTERVAL, @@ -41,8 +39,6 @@ class ImmichHubData: api_key: str scan_interval: int telegram_cache_ttl: int - quiet_hours_start: str - quiet_hours_end: str @dataclass @@ -66,8 +62,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ImmichConfigEntry) -> bo api_key = entry.data[CONF_API_KEY] scan_interval = entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL) telegram_cache_ttl = entry.options.get(CONF_TELEGRAM_CACHE_TTL, DEFAULT_TELEGRAM_CACHE_TTL) - quiet_hours_start = entry.options.get(CONF_QUIET_HOURS_START, "") - quiet_hours_end = entry.options.get(CONF_QUIET_HOURS_END, "") # Store hub data entry.runtime_data = ImmichHubData( @@ -76,8 +70,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ImmichConfigEntry) -> bo api_key=api_key, scan_interval=scan_interval, telegram_cache_ttl=telegram_cache_ttl, - quiet_hours_start=quiet_hours_start, - quiet_hours_end=quiet_hours_end, ) # Create storage for persisting album state across restarts @@ -108,6 +100,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ImmichConfigEntry) -> bo "telegram_cache": telegram_cache, "telegram_asset_cache": telegram_asset_cache, "notification_queue": notification_queue, + "quiet_hours_unsubs": {}, # keyed by "HH:MM" end time } # Track loaded subentries to detect changes @@ -120,12 +113,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ImmichConfigEntry) -> bo # Forward platform setup once - platforms will iterate through subentries await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) - # Register quiet hours end timer - _register_quiet_hours_timer(hass, entry) - - # Check if there are queued notifications from before restart (outside quiet hours) - if notification_queue.has_pending() and not _is_quiet_hours(quiet_hours_start, quiet_hours_end): - hass.async_create_task(_process_notification_queue(hass, entry)) + # Check if there are queued notifications from before restart + if notification_queue.has_pending(): + _register_queue_timers(hass, entry) + # Process any items whose quiet hours have already ended + hass.async_create_task(_process_ready_notifications(hass, entry)) # Register update listener for options and subentry changes entry.async_on_unload(entry.add_update_listener(_async_update_listener)) @@ -182,11 +174,8 @@ async def _async_setup_subentry_coordinator( _LOGGER.info("Coordinator for album '%s' set up successfully", album_name) -def _is_quiet_hours(start_str: str, end_str: str, hass: HomeAssistant | None = None) -> bool: +def _is_quiet_hours(start_str: str, end_str: str) -> bool: """Check if current time is within quiet hours.""" - from datetime import time as dt_time - from homeassistant.util import dt as dt_util - if not start_str or not end_str: return False @@ -204,50 +193,64 @@ def _is_quiet_hours(start_str: str, end_str: str, hass: HomeAssistant | None = N return now >= start_time or now < end_time -def _register_quiet_hours_timer(hass: HomeAssistant, entry: ImmichConfigEntry) -> None: - """Register a timer to process the notification queue when quiet hours end.""" +def _register_queue_timers(hass: HomeAssistant, entry: ImmichConfigEntry) -> None: + """Register timers for each unique quiet_hours_end in the queue.""" entry_data = hass.data[DOMAIN][entry.entry_id] + queue: NotificationQueue = entry_data["notification_queue"] + unsubs: dict[str, list] = entry_data["quiet_hours_unsubs"] - # Cancel existing timer if any - unsub = entry_data.pop("quiet_hours_unsub", None) + # Collect unique end times from queued items + end_times: set[str] = set() + for item in queue.get_all(): + end_str = item.get("params", {}).get("quiet_hours_end", "") + if end_str: + end_times.add(end_str) + + for end_str in end_times: + if end_str in unsubs: + continue # Timer already registered for this end time + + try: + end_time = dt_time.fromisoformat(end_str) + except ValueError: + _LOGGER.warning("Invalid quiet hours end time in queue: %s", end_str) + continue + + async def _on_quiet_hours_end(_now: datetime, _end_str: str = end_str) -> None: + """Handle quiet hours end — process matching queued notifications.""" + _LOGGER.info("Quiet hours ended (%s), processing queued notifications", _end_str) + await _process_notifications_for_end_time(hass, entry, _end_str) + + unsub = async_track_time_change( + hass, _on_quiet_hours_end, hour=end_time.hour, minute=end_time.minute, second=0 + ) + unsubs[end_str] = unsub + entry.async_on_unload(unsub) + + _LOGGER.debug("Registered quiet hours timer for %s", end_str) + + +def _unregister_queue_timer(hass: HomeAssistant, entry: ImmichConfigEntry, end_str: str) -> None: + """Unregister a quiet hours timer if no more items need it.""" + entry_data = hass.data[DOMAIN][entry.entry_id] + queue: NotificationQueue = entry_data["notification_queue"] + unsubs: dict[str, list] = entry_data["quiet_hours_unsubs"] + + # Check if any remaining items still use this end time + for item in queue.get_all(): + if item.get("params", {}).get("quiet_hours_end", "") == end_str: + return # Still needed + + unsub = unsubs.pop(end_str, None) if unsub: unsub() - - end_str = entry.options.get(CONF_QUIET_HOURS_END, "") - start_str = entry.options.get(CONF_QUIET_HOURS_START, "") - if not end_str or not start_str: - return - - try: - from datetime import time as dt_time - end_time = dt_time.fromisoformat(end_str) - except ValueError: - _LOGGER.warning("Invalid quiet hours end time: %s", end_str) - return - - async def _on_quiet_hours_end(_now: datetime) -> None: - """Handle quiet hours end — process queued notifications.""" - queue: NotificationQueue = entry_data["notification_queue"] - if queue.has_pending(): - _LOGGER.info("Quiet hours ended, processing queued notifications") - await _process_notification_queue(hass, entry) - - unsub = async_track_time_change( - hass, _on_quiet_hours_end, hour=end_time.hour, minute=end_time.minute, second=0 - ) - entry_data["quiet_hours_unsub"] = unsub - entry.async_on_unload(unsub) - - _LOGGER.debug("Registered quiet hours timer for %s", end_str) + _LOGGER.debug("Unregistered quiet hours timer for %s (no more items)", end_str) -async def _process_notification_queue( +async def _process_ready_notifications( hass: HomeAssistant, entry: ImmichConfigEntry ) -> None: - """Process all queued notifications via the HA service call.""" - import asyncio - from homeassistant.helpers import entity_registry as er - + """Process queued notifications whose quiet hours have already ended.""" entry_data = hass.data[DOMAIN].get(entry.entry_id) if not entry_data: return @@ -257,7 +260,68 @@ async def _process_notification_queue( if not items: return - # Find a fallback sensor entity for items that don't have entity_id stored + # Find items whose quiet hours have ended + ready_indices = [] + for i, item in enumerate(items): + params = item.get("params", {}) + start_str = params.get("quiet_hours_start", "") + end_str = params.get("quiet_hours_end", "") + if not _is_quiet_hours(start_str, end_str): + ready_indices.append(i) + + if not ready_indices: + return + + _LOGGER.info("Found %d queued notifications ready to send (quiet hours ended)", len(ready_indices)) + await _send_queued_items(hass, entry, ready_indices) + + +async def _process_notifications_for_end_time( + hass: HomeAssistant, entry: ImmichConfigEntry, end_str: str +) -> None: + """Process queued notifications matching a specific quiet_hours_end time.""" + entry_data = hass.data[DOMAIN].get(entry.entry_id) + if not entry_data: + return + + queue: NotificationQueue = entry_data["notification_queue"] + items = queue.get_all() + if not items: + return + + # Find items matching this end time that are no longer in quiet hours + matching_indices = [] + for i, item in enumerate(items): + params = item.get("params", {}) + if params.get("quiet_hours_end", "") == end_str: + start_str = params.get("quiet_hours_start", "") + if not _is_quiet_hours(start_str, end_str): + matching_indices.append(i) + + if not matching_indices: + return + + _LOGGER.info("Processing %d queued notifications for quiet hours end %s", len(matching_indices), end_str) + await _send_queued_items(hass, entry, matching_indices) + + # Clean up timer if no more items need it + _unregister_queue_timer(hass, entry, end_str) + + +async def _send_queued_items( + hass: HomeAssistant, entry: ImmichConfigEntry, indices: list[int] +) -> None: + """Send specific queued notifications by index and remove them from the queue.""" + import asyncio + from homeassistant.helpers import entity_registry as er + + entry_data = hass.data[DOMAIN].get(entry.entry_id) + if not entry_data: + return + + queue: NotificationQueue = entry_data["notification_queue"] + + # Find a fallback sensor entity ent_reg = er.async_get(hass) fallback_entity_id = None for ent in er.async_entries_for_config_entry(ent_reg, entry.entry_id): @@ -269,29 +333,34 @@ async def _process_notification_queue( _LOGGER.warning("No sensor entity found to process notification queue") return - _LOGGER.info("Processing %d queued notifications", len(items)) - for i, item in enumerate(items): - params = item.get("params", {}) + items = queue.get_all() + sent_count = 0 + for i in indices: + if i >= len(items): + continue + params = dict(items[i].get("params", {})) try: - # Use stored entity_id from the original call, fall back to discovered one target_entity_id = params.pop("entity_id", None) or fallback_entity_id - # Call the service with ignore_quiet_hours=True to prevent re-queuing + # Remove quiet hours params so the replay doesn't re-queue + params.pop("quiet_hours_start", None) + params.pop("quiet_hours_end", None) await hass.services.async_call( DOMAIN, "send_telegram_notification", - {**params, "ignore_quiet_hours": True}, + params, target={"entity_id": target_entity_id}, blocking=True, ) + sent_count += 1 except Exception: - _LOGGER.exception("Failed to send queued notification %d/%d", i + 1, len(items)) + _LOGGER.exception("Failed to send queued notification %d", i + 1) # Small delay between notifications to avoid rate limiting - if i < len(items) - 1: - await asyncio.sleep(1) + await asyncio.sleep(1) - await queue.async_clear() - _LOGGER.info("Processed %d queued notifications", len(items)) + # Remove sent items from queue (in reverse order to preserve indices) + await queue.async_remove_indices(sorted(indices, reverse=True)) + _LOGGER.info("Sent %d/%d queued notifications", sent_count, len(indices)) async def _async_update_listener( @@ -314,28 +383,25 @@ async def _async_update_listener( # Handle options-only update new_interval = entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL) - new_quiet_start = entry.options.get(CONF_QUIET_HOURS_START, "") - new_quiet_end = entry.options.get(CONF_QUIET_HOURS_END, "") # Update hub data entry.runtime_data.scan_interval = new_interval - entry.runtime_data.quiet_hours_start = new_quiet_start - entry.runtime_data.quiet_hours_end = new_quiet_end # Update all subentry coordinators subentries_data = entry_data["subentries"] for subentry_data in subentries_data.values(): subentry_data.coordinator.update_scan_interval(new_interval) - # Re-register quiet hours timer - _register_quiet_hours_timer(hass, entry) - - _LOGGER.info("Updated hub options (scan_interval=%d, quiet_hours=%s-%s)", - new_interval, new_quiet_start or "disabled", new_quiet_end or "disabled") + _LOGGER.info("Updated hub options (scan_interval=%d)", new_interval) async def async_unload_entry(hass: HomeAssistant, entry: ImmichConfigEntry) -> bool: """Unload a config entry.""" + # Cancel all quiet hours timers + entry_data = hass.data[DOMAIN].get(entry.entry_id, {}) + for unsub in entry_data.get("quiet_hours_unsubs", {}).values(): + unsub() + # Unload all platforms unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) diff --git a/custom_components/immich_album_watcher/config_flow.py b/custom_components/immich_album_watcher/config_flow.py index cc08630..743dbf7 100644 --- a/custom_components/immich_album_watcher/config_flow.py +++ b/custom_components/immich_album_watcher/config_flow.py @@ -25,8 +25,6 @@ from .const import ( CONF_API_KEY, CONF_HUB_NAME, CONF_IMMICH_URL, - CONF_QUIET_HOURS_END, - CONF_QUIET_HOURS_START, CONF_SCAN_INTERVAL, CONF_TELEGRAM_BOT_TOKEN, CONF_TELEGRAM_CACHE_TTL, @@ -247,26 +245,6 @@ class ImmichAlbumWatcherOptionsFlow(OptionsFlow): ) -> ConfigFlowResult: """Manage the options.""" if user_input is not None: - errors: dict[str, str] = {} - quiet_start = user_input.get(CONF_QUIET_HOURS_START, "") - quiet_end = user_input.get(CONF_QUIET_HOURS_END, "") - - import re - time_pattern = re.compile(r"^(\d{2}:\d{2})?$") - if quiet_start and not time_pattern.match(quiet_start): - errors[CONF_QUIET_HOURS_START] = "invalid_time_format" - if quiet_end and not time_pattern.match(quiet_end): - errors[CONF_QUIET_HOURS_END] = "invalid_time_format" - if (quiet_start and not quiet_end) or (quiet_end and not quiet_start): - errors["base"] = "quiet_hours_incomplete" - - if errors: - return self.async_show_form( - step_id="init", - data_schema=self._build_options_schema(), - errors=errors, - ) - return self.async_create_entry( title="", data={ @@ -279,8 +257,6 @@ class ImmichAlbumWatcherOptionsFlow(OptionsFlow): CONF_TELEGRAM_CACHE_TTL: user_input.get( CONF_TELEGRAM_CACHE_TTL, DEFAULT_TELEGRAM_CACHE_TTL ), - CONF_QUIET_HOURS_START: quiet_start, - CONF_QUIET_HOURS_END: quiet_end, }, ) @@ -300,12 +276,6 @@ class ImmichAlbumWatcherOptionsFlow(OptionsFlow): current_cache_ttl = self._config_entry.options.get( CONF_TELEGRAM_CACHE_TTL, DEFAULT_TELEGRAM_CACHE_TTL ) - current_quiet_start = self._config_entry.options.get( - CONF_QUIET_HOURS_START, "" - ) - current_quiet_end = self._config_entry.options.get( - CONF_QUIET_HOURS_END, "" - ) return vol.Schema( { @@ -318,12 +288,6 @@ class ImmichAlbumWatcherOptionsFlow(OptionsFlow): vol.Optional( CONF_TELEGRAM_CACHE_TTL, default=current_cache_ttl ): vol.All(vol.Coerce(int), vol.Range(min=1, max=168)), - vol.Optional( - CONF_QUIET_HOURS_START, default=current_quiet_start - ): str, - vol.Optional( - CONF_QUIET_HOURS_END, default=current_quiet_end - ): str, } ) diff --git a/custom_components/immich_album_watcher/const.py b/custom_components/immich_album_watcher/const.py index 9d85f31..8a9caca 100644 --- a/custom_components/immich_album_watcher/const.py +++ b/custom_components/immich_album_watcher/const.py @@ -15,8 +15,6 @@ CONF_ALBUM_NAME: Final = "album_name" CONF_SCAN_INTERVAL: Final = "scan_interval" CONF_TELEGRAM_BOT_TOKEN: Final = "telegram_bot_token" CONF_TELEGRAM_CACHE_TTL: Final = "telegram_cache_ttl" -CONF_QUIET_HOURS_START: Final = "quiet_hours_start" -CONF_QUIET_HOURS_END: Final = "quiet_hours_end" # Subentry type SUBENTRY_TYPE_ALBUM: Final = "album" diff --git a/custom_components/immich_album_watcher/sensor.py b/custom_components/immich_album_watcher/sensor.py index 3c1d28c..3d84466 100644 --- a/custom_components/immich_album_watcher/sensor.py +++ b/custom_components/immich_album_watcher/sensor.py @@ -41,8 +41,6 @@ from .const import ( CONF_ALBUM_ID, CONF_ALBUM_NAME, CONF_HUB_NAME, - CONF_QUIET_HOURS_END, - CONF_QUIET_HOURS_START, CONF_TELEGRAM_BOT_TOKEN, DOMAIN, SERVICE_GET_ASSETS, @@ -246,7 +244,8 @@ async def async_setup_entry( vol.Optional("chat_action", default="typing"): vol.Any( None, vol.In(["", "typing", "upload_photo", "upload_video", "upload_document"]) ), - vol.Optional("ignore_quiet_hours", default=False): bool, + vol.Optional("quiet_hours_start"): vol.Match(r"^\d{2}:\d{2}$"), + vol.Optional("quiet_hours_end"): vol.Match(r"^\d{2}:\d{2}$"), }, "async_send_telegram_notification", supports_response=SupportsResponse.OPTIONAL, @@ -337,13 +336,12 @@ class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], Se ) return {"assets": assets} - def _is_quiet_hours(self) -> bool: - """Check if current time is within configured quiet hours.""" + @staticmethod + def _is_quiet_hours(start_str: str | None, end_str: str | None) -> bool: + """Check if current time is within quiet hours.""" from datetime import time as dt_time from homeassistant.util import dt as dt_util - start_str = self._entry.options.get(CONF_QUIET_HOURS_START, "") - end_str = self._entry.options.get(CONF_QUIET_HOURS_END, "") if not start_str or not end_str: return False @@ -375,7 +373,8 @@ class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], Se max_asset_data_size: int | None = None, send_large_photos_as_documents: bool = False, chat_action: str | None = "typing", - ignore_quiet_hours: bool = False, + quiet_hours_start: str | None = None, + quiet_hours_end: str | None = None, ) -> ServiceResponse: """Send notification to Telegram. @@ -393,7 +392,8 @@ class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], Se and the service will return immediately. """ # Check quiet hours — queue notification if active - if not ignore_quiet_hours and self._is_quiet_hours(): + if self._is_quiet_hours(quiet_hours_start, quiet_hours_end): + from . import _register_queue_timers, ImmichConfigEntry queue: NotificationQueue = self.hass.data[DOMAIN][self._entry.entry_id]["notification_queue"] await queue.async_enqueue({ "entity_id": self.entity_id, @@ -409,7 +409,11 @@ class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], Se "max_asset_data_size": max_asset_data_size, "send_large_photos_as_documents": send_large_photos_as_documents, "chat_action": chat_action, + "quiet_hours_start": quiet_hours_start, + "quiet_hours_end": quiet_hours_end, }) + # Register timer for this end time if not already registered + _register_queue_timers(self.hass, self._entry) return {"success": True, "status": "queued_quiet_hours"} # If non-blocking mode, create a background task and return immediately diff --git a/custom_components/immich_album_watcher/services.yaml b/custom_components/immich_album_watcher/services.yaml index ce77321..3ac596b 100644 --- a/custom_components/immich_album_watcher/services.yaml +++ b/custom_components/immich_album_watcher/services.yaml @@ -256,10 +256,15 @@ send_telegram_notification: value: "upload_document" - label: "Disabled" value: "" - ignore_quiet_hours: - name: Ignore Quiet Hours - description: Send notification immediately even during quiet hours. By default, notifications are queued during quiet hours and sent when they end. + quiet_hours_start: + name: Quiet Hours Start + description: "Start time for quiet hours (HH:MM format, e.g. 22:00). When set along with quiet_hours_end, notifications during this period are queued and sent when quiet hours end. Omit to send immediately." required: false - default: false selector: - boolean: + text: + quiet_hours_end: + name: Quiet Hours End + description: "End time for quiet hours (HH:MM format, e.g. 08:00). Queued notifications will be sent at this time." + required: false + selector: + text: diff --git a/custom_components/immich_album_watcher/storage.py b/custom_components/immich_album_watcher/storage.py index 1a4f7dc..f19eb8b 100644 --- a/custom_components/immich_album_watcher/storage.py +++ b/custom_components/immich_album_watcher/storage.py @@ -306,6 +306,15 @@ class NotificationQueue: """Check if there are pending notifications.""" return bool(self._data and self._data.get("queue")) + async def async_remove_indices(self, indices: list[int]) -> None: + """Remove specific items by index (indices must be in descending order).""" + if not self._data or not indices: + return + for idx in indices: + if 0 <= idx < len(self._data["queue"]): + del self._data["queue"][idx] + await self._store.async_save(self._data) + async def async_clear(self) -> None: """Clear all queued notifications.""" if self._data: diff --git a/custom_components/immich_album_watcher/translations/en.json b/custom_components/immich_album_watcher/translations/en.json index 364c877..ed5893e 100644 --- a/custom_components/immich_album_watcher/translations/en.json +++ b/custom_components/immich_album_watcher/translations/en.json @@ -113,10 +113,6 @@ } }, "options": { - "error": { - "invalid_time_format": "Invalid time format. Use HH:MM (e.g. 22:00)", - "quiet_hours_incomplete": "Both start and end times must be set for quiet hours" - }, "step": { "init": { "title": "Immich Album Watcher Options", @@ -124,16 +120,12 @@ "data": { "scan_interval": "Scan interval (seconds)", "telegram_bot_token": "Telegram Bot Token", - "telegram_cache_ttl": "Telegram Cache TTL (hours)", - "quiet_hours_start": "Quiet Hours Start", - "quiet_hours_end": "Quiet Hours End" + "telegram_cache_ttl": "Telegram Cache TTL (hours)" }, "data_description": { "scan_interval": "How often to check for album changes (10-3600 seconds)", "telegram_bot_token": "Bot token for sending notifications to Telegram", - "telegram_cache_ttl": "How long to cache uploaded file IDs to avoid re-uploading (1-168 hours, default: 48)", - "quiet_hours_start": "Start time for quiet hours (HH:MM format, e.g. 22:00). Notifications will be queued during this period. Leave empty to disable.", - "quiet_hours_end": "End time for quiet hours (HH:MM format, e.g. 08:00). Queued notifications will be sent at this time." + "telegram_cache_ttl": "How long to cache uploaded file IDs to avoid re-uploading (1-168 hours, default: 48)" } } } @@ -257,9 +249,13 @@ "name": "Chat Action", "description": "Chat action to display while processing (typing, upload_photo, upload_video, upload_document). Set to empty to disable." }, - "ignore_quiet_hours": { - "name": "Ignore Quiet Hours", - "description": "Send notification immediately even during quiet hours. By default, notifications are queued during quiet hours and sent when they end." + "quiet_hours_start": { + "name": "Quiet Hours Start", + "description": "Start time for quiet hours (HH:MM format, e.g. 22:00). Notifications during this period are queued and sent when quiet hours end. Omit to send immediately." + }, + "quiet_hours_end": { + "name": "Quiet Hours End", + "description": "End time for quiet hours (HH:MM format, e.g. 08:00). Queued notifications will be sent at this time." } } } diff --git a/custom_components/immich_album_watcher/translations/ru.json b/custom_components/immich_album_watcher/translations/ru.json index b6a575b..8130c84 100644 --- a/custom_components/immich_album_watcher/translations/ru.json +++ b/custom_components/immich_album_watcher/translations/ru.json @@ -113,10 +113,6 @@ } }, "options": { - "error": { - "invalid_time_format": "Неверный формат времени. Используйте ЧЧ:ММ (например 22:00)", - "quiet_hours_incomplete": "Необходимо указать и начало, и конец тихих часов" - }, "step": { "init": { "title": "Настройки Immich Album Watcher", @@ -124,16 +120,12 @@ "data": { "scan_interval": "Интервал сканирования (секунды)", "telegram_bot_token": "Токен Telegram бота", - "telegram_cache_ttl": "Время жизни кэша Telegram (часы)", - "quiet_hours_start": "Начало тихих часов", - "quiet_hours_end": "Конец тихих часов" + "telegram_cache_ttl": "Время жизни кэша Telegram (часы)" }, "data_description": { "scan_interval": "Как часто проверять изменения в альбомах (10-3600 секунд)", "telegram_bot_token": "Токен бота для отправки уведомлений в Telegram", - "telegram_cache_ttl": "Сколько хранить ID загруженных файлов для повторной отправки без загрузки (1-168 часов, по умолчанию: 48)", - "quiet_hours_start": "Время начала тихих часов (формат ЧЧ:ММ, например 22:00). Уведомления будут поставлены в очередь. Оставьте пустым для отключения.", - "quiet_hours_end": "Время окончания тихих часов (формат ЧЧ:ММ, например 08:00). Уведомления из очереди будут отправлены в это время." + "telegram_cache_ttl": "Сколько хранить ID загруженных файлов для повторной отправки без загрузки (1-168 часов, по умолчанию: 48)" } } } @@ -257,9 +249,13 @@ "name": "Действие в чате", "description": "Действие для отображения во время обработки (typing, upload_photo, upload_video, upload_document). Оставьте пустым для отключения." }, - "ignore_quiet_hours": { - "name": "Игнорировать тихие часы", - "description": "Отправить уведомление немедленно, даже во время тихих часов. По умолчанию уведомления ставятся в очередь и отправляются по окончании тихих часов." + "quiet_hours_start": { + "name": "Начало тихих часов", + "description": "Время начала тихих часов (формат ЧЧ:ММ, например 22:00). Уведомления в этот период ставятся в очередь и отправляются по окончании. Не указывайте для немедленной отправки." + }, + "quiet_hours_end": { + "name": "Конец тихих часов", + "description": "Время окончания тихих часов (формат ЧЧ:ММ, например 08:00). Уведомления из очереди будут отправлены в это время." } } }