Длительность архива = 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:
@@ -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}")
|
||||||
|
|
||||||
|
|||||||
@@ -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]:
|
||||||
|
|||||||
Reference in New Issue
Block a user