Изменения в потоке камер

This commit is contained in:
2026-06-30 20:45:31 +03:00
parent e1f1ff89d8
commit 6a44a996b1
3 changed files with 19 additions and 30 deletions
+2 -23
View File
@@ -697,8 +697,8 @@ class RTKeyCamerasApi:
url = url.update_query({"token": streamer_token}) url = url.update_query({"token": streamer_token})
return str(url) return str(url)
async def get_camera_stream_url(self, camera_id: str, retry: bool = False) -> str | None: async def get_camera_stream_url(self, camera_id: str) -> str | None:
if self._tokens_invalid and not retry: if self._tokens_invalid:
_LOGGER.debug(f"Токены протухшие, обновляем перед запросом стрима для {camera_id}") _LOGGER.debug(f"Токены протухшие, обновляем перед запросом стрима для {camera_id}")
await self.refresh_tokens() await self.refresh_tokens()
@@ -712,27 +712,6 @@ class RTKeyCamerasApi:
}) })
if streamer_token: if streamer_token:
url = url.update_query({"token": streamer_token}) url = url.update_query({"token": streamer_token})
r = await self._api_get(url, _DEFAULT_HEADERS, timeout=10)
if r is None:
return str(url)
if r.status == 403 and not retry:
camera_name = self.get_camera_name(camera_id)
_LOGGER.warning(f"Токен стрима для {camera_name} ({camera_id}) протух (403), запускаем обновление...")
self._tokens_invalid = True
await self.refresh_tokens()
return await self.get_camera_stream_url(camera_id, retry=True)
if r.status == 403 and retry:
camera_name = self.get_camera_name(camera_id)
_LOGGER.error(f"Повторный 403 для стрима {camera_name} ({camera_id}) — проблема с авторизацией")
return None
if r.status == 200:
return str(url)
_LOGGER.warning(f"Стрим для {camera_id}: статус {r.status}")
return str(url) return str(url)
# === Домофон: открытие === # === Домофон: открытие ===
+16 -6
View File
@@ -1,7 +1,7 @@
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
from homeassistant.components.camera import Camera from homeassistant.components.camera import Camera, CameraEntityFeature
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
@@ -38,6 +38,8 @@ async def async_setup_entry(
class RTKeyCamera(Camera): class RTKeyCamera(Camera):
"""Live-камера (для всех устройств — и камер, и домофонов).""" """Live-камера (для всех устройств — и камер, и домофонов)."""
_attr_supported_features = CameraEntityFeature.STREAM
def __init__( def __init__(
self, self,
hass: HomeAssistant, hass: HomeAssistant,
@@ -108,10 +110,11 @@ 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
_attr_supported_features = CameraEntityFeature.STREAM
def __init__( def __init__(
self, self,
@@ -175,17 +178,24 @@ class RTKeyEventCamera(Camera):
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:
if self._archive_path and Path(self._archive_path).exists():
return f"file://{self._archive_path}"
return None
async def async_camera_image( async def async_camera_image(
self, width: int | None = None, height: int | None = None self, width: int | None = None, height: int | None = None
) -> bytes | None: ) -> bytes | None:
"""Скриншот камеры как thumbnail для карточки.""" event_info = self.cameras_api.last_events.get(self.intercom_id)
if self.camera_id: if event_info and event_info.get("screenshot_path"):
return await self.cameras_api.get_camera_image(self.camera_id) path = Path(event_info["screenshot_path"])
if path.exists():
return await self.hass.async_add_executor_job(path.read_bytes)
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]:
+1 -1
View File
@@ -4,7 +4,7 @@
"documentation": "https://github.com/sirmrak/RTKey", "documentation": "https://github.com/sirmrak/RTKey",
"issue_tracker": "https://github.com/sirmrak/RTKey/issues", "issue_tracker": "https://github.com/sirmrak/RTKey/issues",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"version": "1.2.2", "version": "1.2.3",
"requirements": [], "requirements": [],
"config_flow": true, "config_flow": true,
"codeowners": ["@sirmrak"] "codeowners": ["@sirmrak"]