Длительность архива = archive_duration, Camera без STREAM для архивов

— duration = archive_duration (не сумма lag+pre+archive)
— RTKeyEventCamera: убран STREAM (HLS обрезает видео + reconnect loop)
— RTKeyEventCamera: async_camera_image() для thumbnail (fix NotImplementedError)
— Убраны эмодзи из статусов, убран CameraEntityFeature импорт из EventCamera
This commit is contained in:
2026-06-28 22:33:17 +03:00
parent 8472b91cd1
commit 0e07bc8c9d
2 changed files with 16 additions and 23 deletions
+1 -1
View File
@@ -304,7 +304,7 @@ class RTKeyCamerasApi:
filename = dt.strftime("%Y-%m-%d_%H-%M-%S.mp4") filename = dt.strftime("%Y-%m-%d_%H-%M-%S.mp4")
filepath = archive_dir / filename filepath = archive_dir / filename
duration = self.pre_event_seconds + self.event_time_lag + self.archive_duration duration = self.archive_duration
_LOGGER.info(f"Скачиваем архив для {intercom_id}: {duration} сек, файл: {filename}") _LOGGER.info(f"Скачиваем архив для {intercom_id}: {duration} сек, файл: {filename}")
+12 -19
View File
@@ -1,12 +1,13 @@
import datetime import datetime
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
from homeassistant.components.camera import Camera, CameraEntityFeature
from homeassistant.components.camera import Camera
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity import EntityCategory
from . import _LOGGER, DOMAIN, RTKeyCamerasApi, TOKEN_REFRESH_BUFFER from . import _LOGGER, DOMAIN, RTKeyCamerasApi, TOKEN_REFRESH_BUFFER
@@ -23,7 +24,6 @@ async def async_setup_entry(
for camera_info in cameras_info["data"]["items"] for camera_info in cameras_info["data"]["items"]
] ]
# 🆕 Добавляем архивные камеры только если archive_copies > 0
if cameras_api.archive_copies > 0: if cameras_api.archive_copies > 0:
intercoms_info = await cameras_api.get_intercoms_info() intercoms_info = await cameras_api.get_intercoms_info()
for intercom_info in intercoms_info.get("data", {}).get("devices", []): for intercom_info in intercoms_info.get("data", {}).get("devices", []):
@@ -63,7 +63,6 @@ class RTKeyCamera(Camera):
self._attr_unique_id = f"camera_{self.camera_id}" self._attr_unique_id = f"camera_{self.camera_id}"
self._attr_name = self.device_name self._attr_name = self.device_name
self._attr_supported_features = CameraEntityFeature.STREAM
self._unsub_stream_refresh: Any = None self._unsub_stream_refresh: Any = None
@@ -113,7 +112,7 @@ class RTKeyCamera(Camera):
if self.cameras_api.is_camera_available(self.camera_id): if self.cameras_api.is_camera_available(self.camera_id):
status = self._device_status_title status = self._device_status_title
else: else:
status = "❌ Нет связи" status = "Нет связи"
attrs = {"Статус": status} attrs = {"Статус": status}
if self._device_model: if self._device_model:
@@ -141,7 +140,7 @@ class RTKeyCamera(Camera):
class RTKeyEventCamera(Camera): class RTKeyEventCamera(Camera):
"""Камера для просмотра архивного видео на момент последнего события (локальный файл).""" """Архив события: thumbnail-скриншот + ссылка на видео-файл."""
_attr_should_poll = False _attr_should_poll = False
_attr_entity_category = EntityCategory.DIAGNOSTIC _attr_entity_category = EntityCategory.DIAGNOSTIC
@@ -168,7 +167,6 @@ class RTKeyEventCamera(Camera):
self._attr_unique_id = f"event_camera_{self.intercom_id}" self._attr_unique_id = f"event_camera_{self.intercom_id}"
self._attr_name = "Архив события" self._attr_name = "Архив события"
self._attr_supported_features = CameraEntityFeature.STREAM
self._event_description = None self._event_description = None
self._event_time = None self._event_time = None
@@ -204,27 +202,22 @@ class RTKeyEventCamera(Camera):
self._archive_error = None self._archive_error = None
self._event_description = event_info.get("description") self._event_description = event_info.get("description")
self._event_time = event_info.get("local_time") self._event_time = event_info.get("local_time")
if self.stream:
_LOGGER.debug(f"Обновляем путь архива для {self.device_name}")
# Для локального файла stream не обновляем через update_source
# stream_source() будет вызван при запросе
self.async_write_ha_state() self.async_write_ha_state()
elif archive_error: elif archive_error:
self._archive_error = archive_error self._archive_error = archive_error
self.async_write_ha_state() self.async_write_ha_state()
async def stream_source(self) -> str | None: async def async_camera_image(
"""Возвращаем путь к локальному файлу.""" self, width: int | None = None, height: int | None = None
if self._archive_path and Path(self._archive_path).exists(): ) -> bytes | None:
return f"file://{self._archive_path}" """Скриншот камеры как thumbnail для карточки."""
if self.camera_id:
return await self.cameras_api.get_camera_image(self.camera_id)
return None return None
@property @property
def available(self) -> bool: def available(self) -> bool:
"""Доступна, если есть локальный файл.""" return self.camera_id is not None and self.cameras_api.is_camera_available(self.camera_id)
return self._archive_path is not None and Path(self._archive_path).exists()
@property @property
def extra_state_attributes(self) -> dict[str, Any]: def extra_state_attributes(self) -> dict[str, Any]: