Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
33c170ca15 | ||
|
|
2c473b157c | ||
|
|
a1b63d2ee1 | ||
|
|
92ecd51e2e | ||
|
|
e60a47ef48 | ||
|
|
ab3de76de5 | ||
|
|
84127a1229 | ||
|
|
57d2441d3b | ||
|
|
ea1b6e3916 | ||
|
|
792a185204 | ||
|
|
015e3221df | ||
|
|
02cb16325c |
@@ -19,6 +19,8 @@ English | [Русский](README.ru.md)
|
|||||||
- **Force stop sensor** - Stop recording based on any entity state
|
- **Force stop sensor** - Stop recording based on any entity state
|
||||||
- **Enable/disable switch** - Control recording with a toggle
|
- **Enable/disable switch** - Control recording with a toggle
|
||||||
- **Status sensor** - Monitor recording state with beautiful icons
|
- **Status sensor** - Monitor recording state with beautiful icons
|
||||||
|
- **Smart device control** - Automatically turn on/off lights or switches based on recording state
|
||||||
|
- **Auto-cleanup** - Delete old recordings by retention policy (daily at 03:00)
|
||||||
- **Multi-language support** - English and Russian translations
|
- **Multi-language support** - English and Russian translations
|
||||||
- **Hot reload** - Settings apply immediately without restart
|
- **Hot reload** - Settings apply immediately without restart
|
||||||
|
|
||||||
@@ -66,6 +68,8 @@ When adding the integration, you'll configure:
|
|||||||
| **Force Stop Sensor** | Entity to force stop recording | - | - |
|
| **Force Stop Sensor** | Entity to force stop recording | - | - |
|
||||||
| **Force Stop State** | State value to trigger stop | `off` | - |
|
| **Force Stop State** | State value to trigger stop | `off` | - |
|
||||||
| **Retention Days** | Auto-delete recordings older than | 7 days | 0-365 days |
|
| **Retention Days** | Auto-delete recordings older than | 7 days | 0-365 days |
|
||||||
|
| **Controlled Devices** | Lights/switches to auto-control | - | - |
|
||||||
|
| **Control States** | States when devices should be ON | `recording` | Multi-select |
|
||||||
| **Log Level** | Logging verbosity | INFO | - |
|
| **Log Level** | Logging verbosity | INFO | - |
|
||||||
|
|
||||||
### Options (Post-Setup)
|
### Options (Post-Setup)
|
||||||
@@ -77,6 +81,8 @@ After initial setup, you can modify these settings via **Settings → Devices &
|
|||||||
- Motion filter
|
- Motion filter
|
||||||
- Off delay
|
- Off delay
|
||||||
- Retention days
|
- Retention days
|
||||||
|
- Controlled Devices
|
||||||
|
- Control States
|
||||||
- Log level
|
- Log level
|
||||||
|
|
||||||
Changes apply immediately without restart.
|
Changes apply immediately without restart.
|
||||||
@@ -217,6 +223,29 @@ Files older than the retention period are automatically deleted daily at 03:00.
|
|||||||
|
|
||||||
Cleanup runs in the background and doesn't affect recording performance.
|
Cleanup runs in the background and doesn't affect recording performance.
|
||||||
|
|
||||||
|
## Smart Device Control
|
||||||
|
|
||||||
|
Motion Recorder can automatically control lights and switches based on its current state.
|
||||||
|
|
||||||
|
### How It Works
|
||||||
|
|
||||||
|
1. Select one or more `light` or `switch` entities in settings
|
||||||
|
2. Choose which states should trigger the devices (e.g., `recording`, `delaying`)
|
||||||
|
3. When the integration enters a selected state → devices turn ON
|
||||||
|
4. When it leaves that state → devices turn OFF (only if integration turned them on)
|
||||||
|
|
||||||
|
### Safety Features
|
||||||
|
|
||||||
|
- **Non-intrusive**: If you manually turn on a light, the integration won't turn it off
|
||||||
|
- **Manual override respected**: If you turn off a controlled light during recording, it stays off
|
||||||
|
- **Shutdown cleanup**: All integration-controlled devices are turned off when HA restarts or integration reloads
|
||||||
|
|
||||||
|
### Example Use Cases
|
||||||
|
|
||||||
|
- Turn on porch light only while recording is active
|
||||||
|
- Activate alarm indicator LED during motion detection
|
||||||
|
- Turn on indoor lighting when camera detects movement at night
|
||||||
|
|
||||||
## Advanced Features
|
## Advanced Features
|
||||||
|
|
||||||
### Multiple Motion Sensors
|
### Multiple Motion Sensors
|
||||||
@@ -308,6 +337,6 @@ This project is licensed under the MIT License - see the LICENSE file for detail
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Version**: 1.0.0
|
**Version**: 1.1.0
|
||||||
**Last Updated**: 2026-07-03
|
**Last Updated**: 2026-07-05
|
||||||
**Compatible with**: Home Assistant 2024.6+
|
**Compatible with**: Home Assistant 2024.6+
|
||||||
|
|||||||
+31
-2
@@ -19,6 +19,8 @@
|
|||||||
- 🔌 **Датчик принудительной остановки** - Остановка записи по состоянию любой сущности
|
- 🔌 **Датчик принудительной остановки** - Остановка записи по состоянию любой сущности
|
||||||
- 🎛️ **Переключатель включения/выключения** - Управление записью одним кликом
|
- 🎛️ **Переключатель включения/выключения** - Управление записью одним кликом
|
||||||
- **Сенсор статуса** - Мониторинг состояния записи с красивыми иконками
|
- **Сенсор статуса** - Мониторинг состояния записи с красивыми иконками
|
||||||
|
- 💡 **Умное управление устройствами** - Автоматическое включение/выключение света и переключателей по состоянию записи
|
||||||
|
- 🗑️ **Автоочистка** - Удаление старых записей по политике хранения (ежедневно в 03:00)
|
||||||
- 🌍 **Многоязычность** - Переводы на русский и английский языки
|
- 🌍 **Многоязычность** - Переводы на русский и английский языки
|
||||||
- **Горячая перезагрузка** - Настройки применяются сразу без перезапуска
|
- **Горячая перезагрузка** - Настройки применяются сразу без перезапуска
|
||||||
|
|
||||||
@@ -66,6 +68,8 @@
|
|||||||
| **Датчик принуд. остановки** | Сущность для принудительной остановки | - | - |
|
| **Датчик принуд. остановки** | Сущность для принудительной остановки | - | - |
|
||||||
| **Состояние остановки** | Значение состояния для запуска остановки | `off` | - |
|
| **Состояние остановки** | Значение состояния для запуска остановки | `off` | - |
|
||||||
| **Дни хранения** | Автоудаление записей старше | 7 дней | 0-365 дней |
|
| **Дни хранения** | Автоудаление записей старше | 7 дней | 0-365 дней |
|
||||||
|
| **Управляемые устройства** | Светильники/переключатели для автоуправления | - | - |
|
||||||
|
| **Состояния включения** | Состояния, при которых устройства включаются | `recording` | Множественный выбор |
|
||||||
| **Уровень логирования** | Детализация логов | INFO | - |
|
| **Уровень логирования** | Детализация логов | INFO | - |
|
||||||
|
|
||||||
### Опции (после настройки)
|
### Опции (после настройки)
|
||||||
@@ -77,6 +81,8 @@
|
|||||||
- Фильтр движения
|
- Фильтр движения
|
||||||
- Задержка остановки
|
- Задержка остановки
|
||||||
- Дни хранения
|
- Дни хранения
|
||||||
|
- Управляемые устройства
|
||||||
|
- Состояния включения
|
||||||
- Уровень логирования
|
- Уровень логирования
|
||||||
|
|
||||||
Изменения применяются сразу без перезапуска.
|
Изменения применяются сразу без перезапуска.
|
||||||
@@ -217,6 +223,29 @@ automation:
|
|||||||
|
|
||||||
Очистка работает в фоновом режиме и не влияет на производительность записи.
|
Очистка работает в фоновом режиме и не влияет на производительность записи.
|
||||||
|
|
||||||
|
## Умное управление устройствами
|
||||||
|
|
||||||
|
Motion Recorder может автоматически управлять светильниками и переключателями в зависимости от текущего состояния.
|
||||||
|
|
||||||
|
### Как это работает
|
||||||
|
|
||||||
|
1. Выберите одну или несколько сущностей `light` или `switch` в настройках
|
||||||
|
2. Отметьте состояния, при которых устройства должны включаться (например, `recording`, `delaying`)
|
||||||
|
3. При переходе в выбранное состояние → устройства включаются
|
||||||
|
4. При выходе из состояния → устройства выключаются (только если интеграция их включала)
|
||||||
|
|
||||||
|
### Безопасность
|
||||||
|
|
||||||
|
- **Не мешает пользователю**: Если вы вручную включили свет, интеграция его не выключит
|
||||||
|
- **Ручное управление приоритетно**: Если вы выключили управляемый свет во время записи, он останется выключенным
|
||||||
|
- **Очистка при остановке**: Все устройства, включённые интеграцией, выключаются при перезагрузке HA или перезагрузке интеграции
|
||||||
|
|
||||||
|
### Примеры использования
|
||||||
|
|
||||||
|
- Включать уличный фонарь только во время активной записи
|
||||||
|
- Активировать индикатор тревоги при обнаружении движения
|
||||||
|
- Включать освещение в помещении при детекции движения камерой ночью
|
||||||
|
|
||||||
## Продвинутые возможности
|
## Продвинутые возможности
|
||||||
|
|
||||||
### Несколько датчиков движения
|
### Несколько датчиков движения
|
||||||
@@ -308,6 +337,6 @@ logger:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Версия**: 1.0.0
|
**Версия**: 1.1.0
|
||||||
**Последнее обновление**: 2026-07-03
|
**Последнее обновление**: 2026-07-05
|
||||||
**Совместимо с**: Home Assistant 2024.6+
|
**Совместимо с**: Home Assistant 2024.6+
|
||||||
@@ -11,25 +11,28 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
PLATFORMS = ["sensor", "switch"]
|
PLATFORMS = ["sensor", "switch"]
|
||||||
|
|
||||||
|
|
||||||
|
def _get_config_value(entry: ConfigEntry, key: str, default=None):
|
||||||
|
"""Get config value from options first, then from data, then default."""
|
||||||
|
return entry.options.get(key, entry.data.get(key, default))
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Set up Motion Recorder from a config entry."""
|
"""Set up Motion Recorder from a config entry."""
|
||||||
# Устанавливаем уровень логирования для всего домена
|
|
||||||
log_level = _get_config_value(entry, CONF_LOG_LEVEL, DEFAULT_LOG_LEVEL)
|
log_level = _get_config_value(entry, CONF_LOG_LEVEL, DEFAULT_LOG_LEVEL)
|
||||||
logging.getLogger(f"custom_components.{DOMAIN}").setLevel(getattr(logging, log_level, logging.INFO))
|
logging.getLogger(f"custom_components.{DOMAIN}").setLevel(getattr(logging, log_level, logging.INFO))
|
||||||
_LOGGER.info("Motion Recorder integration loaded with log level: %s", log_level)
|
_LOGGER.info("Motion Recorder integration loaded with log level: %s", log_level)
|
||||||
|
|
||||||
hass.data.setdefault(DOMAIN, {})
|
hass.data.setdefault(DOMAIN, {})
|
||||||
|
|
||||||
coordinator = MotionRecorderCoordinator(hass, entry)
|
coordinator = MotionRecorderCoordinator(hass, entry)
|
||||||
await coordinator.async_setup()
|
await coordinator.async_setup()
|
||||||
|
|
||||||
hass.data[DOMAIN][entry.entry_id] = coordinator
|
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||||
|
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
|
||||||
# Добавляем listener для автоматической перезагрузки при изменении опций
|
|
||||||
entry.async_on_unload(entry.add_update_listener(_async_update_listener))
|
entry.async_on_unload(entry.add_update_listener(_async_update_listener))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@@ -37,21 +40,16 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
coordinator = hass.data[DOMAIN][entry.entry_id]
|
coordinator = hass.data[DOMAIN][entry.entry_id]
|
||||||
await coordinator.async_shutdown()
|
await coordinator.async_shutdown()
|
||||||
|
|
||||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
|
|
||||||
if unload_ok:
|
if unload_ok:
|
||||||
hass.data[DOMAIN].pop(entry.entry_id)
|
hass.data[DOMAIN].pop(entry.entry_id)
|
||||||
|
|
||||||
return unload_ok
|
return unload_ok
|
||||||
|
|
||||||
|
|
||||||
async def _async_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
async def _async_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||||
"""Handle options update - reload the integration."""
|
"""Handle options update - reload the integration."""
|
||||||
_LOGGER.info("Options updated, reloading Motion Recorder integration")
|
_LOGGER.info("Options updated, reloading Motion Recorder integration")
|
||||||
await hass.config_entries.async_reload(entry.entry_id)
|
await hass.config_entries.async_reload(entry.entry_id)
|
||||||
|
|
||||||
|
|
||||||
def _get_config_value(entry: ConfigEntry, key: str, default=None):
|
|
||||||
"""Get config value from options first, then from data, then default."""
|
|
||||||
return entry.options.get(key, entry.data.get(key, default))
|
|
||||||
@@ -1,235 +1,291 @@
|
|||||||
"""Config flow for Motion Recorder."""
|
"""Config flow for Motion Recorder."""
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from homeassistant.helpers import selector
|
from homeassistant.helpers import selector
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
CONF_CAMERA_ENTITY,
|
CONF_CAMERA_ENTITY,
|
||||||
CONF_SAVE_PATH,
|
CONF_SAVE_PATH,
|
||||||
CONF_FILENAME_TEMPLATE,
|
CONF_FILENAME_TEMPLATE,
|
||||||
CONF_MAX_DURATION,
|
CONF_MAX_DURATION,
|
||||||
CONF_PREBUFFER,
|
CONF_PREBUFFER,
|
||||||
CONF_MOTION_SENSORS,
|
CONF_MOTION_SENSORS,
|
||||||
CONF_MOTION_FILTER,
|
CONF_MOTION_FILTER,
|
||||||
CONF_OFF_DELAY,
|
CONF_OFF_DELAY,
|
||||||
CONF_FORCE_STOP_SENSOR,
|
CONF_FORCE_STOP_SENSOR,
|
||||||
CONF_FORCE_STOP_STATE,
|
CONF_FORCE_STOP_STATE,
|
||||||
CONF_LOG_LEVEL,
|
CONF_LOG_LEVEL,
|
||||||
CONF_RETENTION_DAYS,
|
CONF_RETENTION_DAYS,
|
||||||
DEFAULT_NAME,
|
CONF_CONTROLLED_ENTITIES,
|
||||||
DEFAULT_SAVE_PATH,
|
CONF_CONTROL_STATES,
|
||||||
DEFAULT_FILENAME_TEMPLATE,
|
DEFAULT_NAME,
|
||||||
DEFAULT_MAX_DURATION,
|
DEFAULT_SAVE_PATH,
|
||||||
DEFAULT_PREBUFFER,
|
DEFAULT_FILENAME_TEMPLATE,
|
||||||
DEFAULT_MOTION_FILTER,
|
DEFAULT_MAX_DURATION,
|
||||||
DEFAULT_OFF_DELAY,
|
DEFAULT_PREBUFFER,
|
||||||
DEFAULT_FORCE_STOP_STATE,
|
DEFAULT_MOTION_FILTER,
|
||||||
DEFAULT_LOG_LEVEL,
|
DEFAULT_OFF_DELAY,
|
||||||
DEFAULT_RETENTION_DAYS,
|
DEFAULT_FORCE_STOP_STATE,
|
||||||
)
|
DEFAULT_LOG_LEVEL,
|
||||||
|
DEFAULT_RETENTION_DAYS,
|
||||||
|
DEFAULT_CONTROL_STATES,
|
||||||
# Список шаблонов имени файла (все с секундами для уникальности)
|
STATE_IDLE,
|
||||||
FILENAME_TEMPLATE_OPTIONS = [
|
STATE_DETECTING,
|
||||||
selector.SelectOptionDict(value="%d-%m-%Y_%H-%M-%S", label="ДД-ММ-ГГГГ_ЧЧ-ММ-СС (02-07-2026_20-30-45)"),
|
STATE_RECORDING,
|
||||||
selector.SelectOptionDict(value="%Y-%m-%d_%H-%M-%S", label="ГГГГ-ММ-ДД_ЧЧ-ММ-СС (2026-07-02_20-30-45)"),
|
STATE_DELAYING,
|
||||||
selector.SelectOptionDict(value="%d.%m.%Y_%H-%M-%S", label="ДД.ММ.ГГГГ_ЧЧ-ММ-СС (02.07.2026_20-30-45)"),
|
STATE_FINALIZING,
|
||||||
selector.SelectOptionDict(value="%Y%m%d_%H%M%S", label="ГГГГММДД_ЧЧММСС (20260702_203045)"),
|
STATE_ERROR,
|
||||||
selector.SelectOptionDict(value="%d.%m.%Y_%H.%M.%S", label="ДД.ММ.ГГГГ_ЧЧ.ММ.СС (все точки)"),
|
STATE_DISABLED,
|
||||||
]
|
)
|
||||||
|
|
||||||
|
FILENAME_TEMPLATE_OPTIONS = [
|
||||||
class MotionRecorderConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
selector.SelectOptionDict(value="%d-%m-%Y_%H-%M-%S", label="ДД-ММ-ГГГГ_ЧЧ-ММ-СС (02-07-2026_20-30-45)"),
|
||||||
"""Handle a config flow for Motion Recorder."""
|
selector.SelectOptionDict(value="%Y-%m-%d_%H-%M-%S", label="ГГГГ-ММ-ДД_ЧЧ-ММ-СС (2026-07-02_20-30-45)"),
|
||||||
|
selector.SelectOptionDict(value="%d.%m.%Y_%H-%M-%S", label="ДД.ММ.ГГГГ_ЧЧ-ММ-СС (02.07.2026_20-30-45)"),
|
||||||
VERSION = 1
|
selector.SelectOptionDict(value="%Y%m%d_%H%M%S", label="ГГГГММДД_ЧЧММСС (20260702_203045)"),
|
||||||
|
selector.SelectOptionDict(value="%d.%m.%Y_%H.%M.%S", label="ДД.ММ.ГГГГ_ЧЧ.ММ.СС (все точки)"),
|
||||||
async def async_step_user(self, user_input=None):
|
]
|
||||||
"""Handle the initial step."""
|
|
||||||
errors = {}
|
CONTROL_STATE_OPTIONS = [
|
||||||
|
selector.SelectOptionDict(value=STATE_IDLE, label="Ожидание (idle)"),
|
||||||
if user_input is not None:
|
selector.SelectOptionDict(value=STATE_DETECTING, label="Обнаружение (detecting)"),
|
||||||
await self.async_set_unique_id(user_input[CONF_CAMERA_ENTITY])
|
selector.SelectOptionDict(value=STATE_RECORDING, label="Запись (recording)"),
|
||||||
self._abort_if_unique_id_configured()
|
selector.SelectOptionDict(value=STATE_DELAYING, label="Задержка (delaying)"),
|
||||||
|
selector.SelectOptionDict(value=STATE_FINALIZING, label="Завершение (finalizing)"),
|
||||||
return self.async_create_entry(
|
selector.SelectOptionDict(value=STATE_ERROR, label="Ошибка (error)"),
|
||||||
title=user_input.get("name", DEFAULT_NAME),
|
selector.SelectOptionDict(value=STATE_DISABLED, label="Отключено (disabled)"),
|
||||||
data=user_input,
|
]
|
||||||
)
|
|
||||||
|
|
||||||
return self.async_show_form(
|
class MotionRecorderConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
step_id="user",
|
"""Handle a config flow for Motion Recorder."""
|
||||||
data_schema=vol.Schema({
|
|
||||||
vol.Required("name", default=DEFAULT_NAME): str,
|
VERSION = 1
|
||||||
vol.Required(CONF_CAMERA_ENTITY): selector.EntitySelector(
|
|
||||||
selector.EntitySelectorConfig(domain="camera")
|
async def async_step_user(self, user_input=None):
|
||||||
),
|
"""Handle the initial step."""
|
||||||
vol.Required(CONF_SAVE_PATH, default=DEFAULT_SAVE_PATH): str,
|
errors = {}
|
||||||
vol.Required(CONF_FILENAME_TEMPLATE, default=DEFAULT_FILENAME_TEMPLATE): selector.SelectSelector(
|
|
||||||
selector.SelectSelectorConfig(
|
if user_input is not None:
|
||||||
options=FILENAME_TEMPLATE_OPTIONS,
|
await self.async_set_unique_id(user_input[CONF_CAMERA_ENTITY])
|
||||||
mode=selector.SelectSelectorMode.DROPDOWN,
|
self._abort_if_unique_id_configured()
|
||||||
)
|
|
||||||
),
|
return self.async_create_entry(
|
||||||
vol.Required(CONF_MAX_DURATION, default=DEFAULT_MAX_DURATION): selector.NumberSelector(
|
title=user_input.get("name", DEFAULT_NAME),
|
||||||
selector.NumberSelectorConfig(
|
data=user_input,
|
||||||
min=10, max=3600, step=1,
|
)
|
||||||
mode=selector.NumberSelectorMode.SLIDER,
|
|
||||||
unit_of_measurement="сек",
|
return self.async_show_form(
|
||||||
)
|
step_id="user",
|
||||||
),
|
data_schema=vol.Schema({
|
||||||
vol.Required(CONF_PREBUFFER, default=DEFAULT_PREBUFFER): selector.NumberSelector(
|
vol.Required("name", default=DEFAULT_NAME): str,
|
||||||
selector.NumberSelectorConfig(
|
vol.Required(CONF_CAMERA_ENTITY): selector.EntitySelector(
|
||||||
min=0, max=60, step=1,
|
selector.EntitySelectorConfig(domain="camera")
|
||||||
mode=selector.NumberSelectorMode.SLIDER,
|
),
|
||||||
unit_of_measurement="сек",
|
vol.Required(CONF_SAVE_PATH, default=DEFAULT_SAVE_PATH): str,
|
||||||
)
|
vol.Required(CONF_FILENAME_TEMPLATE, default=DEFAULT_FILENAME_TEMPLATE): selector.SelectSelector(
|
||||||
),
|
selector.SelectSelectorConfig(
|
||||||
vol.Required(CONF_MOTION_SENSORS): selector.EntitySelector(
|
options=FILENAME_TEMPLATE_OPTIONS,
|
||||||
selector.EntitySelectorConfig(domain="binary_sensor", multiple=True)
|
mode=selector.SelectSelectorMode.DROPDOWN,
|
||||||
),
|
)
|
||||||
vol.Required(CONF_MOTION_FILTER, default=DEFAULT_MOTION_FILTER): selector.NumberSelector(
|
),
|
||||||
selector.NumberSelectorConfig(
|
vol.Required(CONF_MAX_DURATION, default=DEFAULT_MAX_DURATION): selector.NumberSelector(
|
||||||
min=0, max=10, step=1,
|
selector.NumberSelectorConfig(
|
||||||
mode=selector.NumberSelectorMode.SLIDER,
|
min=10, max=3600, step=1,
|
||||||
unit_of_measurement="сек",
|
mode=selector.NumberSelectorMode.SLIDER,
|
||||||
)
|
unit_of_measurement="сек",
|
||||||
),
|
)
|
||||||
vol.Required(CONF_OFF_DELAY, default=DEFAULT_OFF_DELAY): selector.NumberSelector(
|
),
|
||||||
selector.NumberSelectorConfig(
|
vol.Required(CONF_PREBUFFER, default=DEFAULT_PREBUFFER): selector.NumberSelector(
|
||||||
min=0, max=60, step=1,
|
selector.NumberSelectorConfig(
|
||||||
mode=selector.NumberSelectorMode.SLIDER,
|
min=0, max=60, step=1,
|
||||||
unit_of_measurement="сек",
|
mode=selector.NumberSelectorMode.SLIDER,
|
||||||
)
|
unit_of_measurement="сек",
|
||||||
),
|
)
|
||||||
vol.Required(CONF_RETENTION_DAYS, default=DEFAULT_RETENTION_DAYS): selector.NumberSelector(
|
),
|
||||||
selector.NumberSelectorConfig(
|
vol.Required(CONF_MOTION_SENSORS): selector.EntitySelector(
|
||||||
min=0, max=365, step=1,
|
selector.EntitySelectorConfig(domain="binary_sensor", multiple=True)
|
||||||
mode=selector.NumberSelectorMode.SLIDER,
|
),
|
||||||
unit_of_measurement="дн",
|
vol.Required(CONF_MOTION_FILTER, default=DEFAULT_MOTION_FILTER): selector.NumberSelector(
|
||||||
)
|
selector.NumberSelectorConfig(
|
||||||
),
|
min=0, max=10, step=1,
|
||||||
vol.Optional(CONF_FORCE_STOP_SENSOR): selector.EntitySelector(
|
mode=selector.NumberSelectorMode.SLIDER,
|
||||||
selector.EntitySelectorConfig(
|
unit_of_measurement="сек",
|
||||||
domain=["binary_sensor", "switch", "input_boolean"],
|
)
|
||||||
multiple=False,
|
),
|
||||||
)
|
vol.Required(CONF_OFF_DELAY, default=DEFAULT_OFF_DELAY): selector.NumberSelector(
|
||||||
),
|
selector.NumberSelectorConfig(
|
||||||
vol.Required(CONF_FORCE_STOP_STATE, default=DEFAULT_FORCE_STOP_STATE): str,
|
min=0, max=60, step=1,
|
||||||
vol.Required(CONF_LOG_LEVEL, default=DEFAULT_LOG_LEVEL): selector.SelectSelector(
|
mode=selector.NumberSelectorMode.SLIDER,
|
||||||
selector.SelectSelectorConfig(
|
unit_of_measurement="сек",
|
||||||
options=[
|
)
|
||||||
selector.SelectOptionDict(value="DEBUG", label="DEBUG (подробно)"),
|
),
|
||||||
selector.SelectOptionDict(value="INFO", label="INFO (обычный)"),
|
vol.Optional(CONF_FORCE_STOP_SENSOR): selector.EntitySelector(
|
||||||
selector.SelectOptionDict(value="WARNING", label="WARNING (только предупреждения)"),
|
selector.EntitySelectorConfig(
|
||||||
selector.SelectOptionDict(value="ERROR", label="ERROR (только ошибки)"),
|
domain=["binary_sensor", "switch", "input_boolean"],
|
||||||
],
|
multiple=False,
|
||||||
mode=selector.SelectSelectorMode.DROPDOWN,
|
)
|
||||||
)
|
),
|
||||||
),
|
vol.Required(CONF_FORCE_STOP_STATE, default=DEFAULT_FORCE_STOP_STATE): str,
|
||||||
}),
|
vol.Required(CONF_RETENTION_DAYS, default=DEFAULT_RETENTION_DAYS): selector.NumberSelector(
|
||||||
errors=errors,
|
selector.NumberSelectorConfig(
|
||||||
)
|
min=0, max=365, step=1,
|
||||||
|
mode=selector.NumberSelectorMode.SLIDER,
|
||||||
@staticmethod
|
unit_of_measurement="дн",
|
||||||
@callback
|
)
|
||||||
def async_get_options_flow(config_entry):
|
),
|
||||||
"""Get the options flow for this handler."""
|
vol.Optional(CONF_CONTROLLED_ENTITIES, default=[]): selector.EntitySelector(
|
||||||
return MotionRecorderOptionsFlow()
|
selector.EntitySelectorConfig(
|
||||||
|
domain=["light", "switch"],
|
||||||
|
multiple=True,
|
||||||
class MotionRecorderOptionsFlow(config_entries.OptionsFlow):
|
)
|
||||||
"""Handle options flow for Motion Recorder."""
|
),
|
||||||
|
vol.Optional(CONF_CONTROL_STATES, default=DEFAULT_CONTROL_STATES): selector.SelectSelector(
|
||||||
async def async_step_init(self, user_input=None):
|
selector.SelectSelectorConfig(
|
||||||
"""Manage the options."""
|
options=CONTROL_STATE_OPTIONS,
|
||||||
if user_input is not None:
|
multiple=True,
|
||||||
return self.async_create_entry(title="", data=user_input)
|
mode=selector.SelectSelectorMode.LIST,
|
||||||
|
)
|
||||||
return self.async_show_form(
|
),
|
||||||
step_id="init",
|
vol.Required(CONF_LOG_LEVEL, default=DEFAULT_LOG_LEVEL): selector.SelectSelector(
|
||||||
data_schema=vol.Schema({
|
selector.SelectSelectorConfig(
|
||||||
vol.Required(
|
options=[
|
||||||
CONF_FILENAME_TEMPLATE,
|
selector.SelectOptionDict(value="DEBUG", label="DEBUG (подробно)"),
|
||||||
default=self.config_entry.options.get(CONF_FILENAME_TEMPLATE,
|
selector.SelectOptionDict(value="INFO", label="INFO (обычный)"),
|
||||||
self.config_entry.data.get(CONF_FILENAME_TEMPLATE, DEFAULT_FILENAME_TEMPLATE))
|
selector.SelectOptionDict(value="WARNING", label="WARNING (только предупреждения)"),
|
||||||
): selector.SelectSelector(
|
selector.SelectOptionDict(value="ERROR", label="ERROR (только ошибки)"),
|
||||||
selector.SelectSelectorConfig(
|
],
|
||||||
options=FILENAME_TEMPLATE_OPTIONS,
|
mode=selector.SelectSelectorMode.DROPDOWN,
|
||||||
mode=selector.SelectSelectorMode.DROPDOWN,
|
)
|
||||||
)
|
),
|
||||||
),
|
}),
|
||||||
vol.Required(
|
errors=errors,
|
||||||
CONF_MAX_DURATION,
|
)
|
||||||
default=self.config_entry.options.get(CONF_MAX_DURATION,
|
|
||||||
self.config_entry.data.get(CONF_MAX_DURATION, DEFAULT_MAX_DURATION))
|
@staticmethod
|
||||||
): selector.NumberSelector(
|
@callback
|
||||||
selector.NumberSelectorConfig(
|
def async_get_options_flow(config_entry):
|
||||||
min=10, max=3600, step=1,
|
"""Get the options flow for this handler."""
|
||||||
mode=selector.NumberSelectorMode.SLIDER,
|
return MotionRecorderOptionsFlow()
|
||||||
unit_of_measurement="сек",
|
|
||||||
)
|
|
||||||
),
|
class MotionRecorderOptionsFlow(config_entries.OptionsFlow):
|
||||||
vol.Required(
|
"""Handle options flow for Motion Recorder."""
|
||||||
CONF_PREBUFFER,
|
|
||||||
default=self.config_entry.options.get(CONF_PREBUFFER,
|
async def async_step_init(self, user_input=None):
|
||||||
self.config_entry.data.get(CONF_PREBUFFER, DEFAULT_PREBUFFER))
|
"""Manage the options."""
|
||||||
): selector.NumberSelector(
|
if user_input is not None:
|
||||||
selector.NumberSelectorConfig(
|
return self.async_create_entry(title="", data=user_input)
|
||||||
min=0, max=60, step=1,
|
|
||||||
mode=selector.NumberSelectorMode.SLIDER,
|
return self.async_show_form(
|
||||||
unit_of_measurement="сек",
|
step_id="init",
|
||||||
)
|
data_schema=vol.Schema({
|
||||||
),
|
vol.Required(
|
||||||
vol.Required(
|
CONF_FILENAME_TEMPLATE,
|
||||||
CONF_MOTION_FILTER,
|
default=self.config_entry.options.get(CONF_FILENAME_TEMPLATE,
|
||||||
default=self.config_entry.options.get(CONF_MOTION_FILTER,
|
self.config_entry.data.get(CONF_FILENAME_TEMPLATE, DEFAULT_FILENAME_TEMPLATE))
|
||||||
self.config_entry.data.get(CONF_MOTION_FILTER, DEFAULT_MOTION_FILTER))
|
): selector.SelectSelector(
|
||||||
): selector.NumberSelector(
|
selector.SelectSelectorConfig(
|
||||||
selector.NumberSelectorConfig(
|
options=FILENAME_TEMPLATE_OPTIONS,
|
||||||
min=0, max=10, step=1,
|
mode=selector.SelectSelectorMode.DROPDOWN,
|
||||||
mode=selector.NumberSelectorMode.SLIDER,
|
)
|
||||||
unit_of_measurement="сек",
|
),
|
||||||
)
|
vol.Required(
|
||||||
),
|
CONF_MAX_DURATION,
|
||||||
vol.Required(
|
default=self.config_entry.options.get(CONF_MAX_DURATION,
|
||||||
CONF_OFF_DELAY,
|
self.config_entry.data.get(CONF_MAX_DURATION, DEFAULT_MAX_DURATION))
|
||||||
default=self.config_entry.options.get(CONF_OFF_DELAY,
|
): selector.NumberSelector(
|
||||||
self.config_entry.data.get(CONF_OFF_DELAY, DEFAULT_OFF_DELAY))
|
selector.NumberSelectorConfig(
|
||||||
): selector.NumberSelector(
|
min=10, max=3600, step=1,
|
||||||
selector.NumberSelectorConfig(
|
mode=selector.NumberSelectorMode.SLIDER,
|
||||||
min=0, max=60, step=1,
|
unit_of_measurement="сек",
|
||||||
mode=selector.NumberSelectorMode.SLIDER,
|
)
|
||||||
unit_of_measurement="сек",
|
),
|
||||||
)
|
vol.Required(
|
||||||
),
|
CONF_PREBUFFER,
|
||||||
vol.Required(
|
default=self.config_entry.options.get(CONF_PREBUFFER,
|
||||||
CONF_RETENTION_DAYS,
|
self.config_entry.data.get(CONF_PREBUFFER, DEFAULT_PREBUFFER))
|
||||||
default=self.config_entry.options.get(CONF_RETENTION_DAYS,
|
): selector.NumberSelector(
|
||||||
self.config_entry.data.get(CONF_RETENTION_DAYS, DEFAULT_RETENTION_DAYS))
|
selector.NumberSelectorConfig(
|
||||||
): selector.NumberSelector(
|
min=0, max=60, step=1,
|
||||||
selector.NumberSelectorConfig(
|
mode=selector.NumberSelectorMode.SLIDER,
|
||||||
min=0, max=365, step=1,
|
unit_of_measurement="сек",
|
||||||
mode=selector.NumberSelectorMode.SLIDER,
|
)
|
||||||
unit_of_measurement="дн",
|
),
|
||||||
)
|
vol.Required(
|
||||||
),
|
CONF_MOTION_FILTER,
|
||||||
vol.Required(
|
default=self.config_entry.options.get(CONF_MOTION_FILTER,
|
||||||
CONF_LOG_LEVEL,
|
self.config_entry.data.get(CONF_MOTION_FILTER, DEFAULT_MOTION_FILTER))
|
||||||
default=self.config_entry.options.get(CONF_LOG_LEVEL,
|
): selector.NumberSelector(
|
||||||
self.config_entry.data.get(CONF_LOG_LEVEL, DEFAULT_LOG_LEVEL))
|
selector.NumberSelectorConfig(
|
||||||
): selector.SelectSelector(
|
min=0, max=10, step=1,
|
||||||
selector.SelectSelectorConfig(
|
mode=selector.NumberSelectorMode.SLIDER,
|
||||||
options=[
|
unit_of_measurement="сек",
|
||||||
selector.SelectOptionDict(value="DEBUG", label="DEBUG (подробно)"),
|
)
|
||||||
selector.SelectOptionDict(value="INFO", label="INFO (обычный)"),
|
),
|
||||||
selector.SelectOptionDict(value="WARNING", label="WARNING (только предупреждения)"),
|
vol.Required(
|
||||||
selector.SelectOptionDict(value="ERROR", label="ERROR (только ошибки)"),
|
CONF_OFF_DELAY,
|
||||||
],
|
default=self.config_entry.options.get(CONF_OFF_DELAY,
|
||||||
mode=selector.SelectSelectorMode.DROPDOWN,
|
self.config_entry.data.get(CONF_OFF_DELAY, DEFAULT_OFF_DELAY))
|
||||||
)
|
): selector.NumberSelector(
|
||||||
),
|
selector.NumberSelectorConfig(
|
||||||
}),
|
min=0, max=60, step=1,
|
||||||
|
mode=selector.NumberSelectorMode.SLIDER,
|
||||||
|
unit_of_measurement="сек",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
vol.Required(
|
||||||
|
CONF_RETENTION_DAYS,
|
||||||
|
default=self.config_entry.options.get(CONF_RETENTION_DAYS,
|
||||||
|
self.config_entry.data.get(CONF_RETENTION_DAYS, DEFAULT_RETENTION_DAYS))
|
||||||
|
): selector.NumberSelector(
|
||||||
|
selector.NumberSelectorConfig(
|
||||||
|
min=0, max=365, step=1,
|
||||||
|
mode=selector.NumberSelectorMode.SLIDER,
|
||||||
|
unit_of_measurement="дн",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
vol.Optional(
|
||||||
|
CONF_CONTROLLED_ENTITIES,
|
||||||
|
default=self.config_entry.options.get(
|
||||||
|
CONF_CONTROLLED_ENTITIES,
|
||||||
|
self.config_entry.data.get(CONF_CONTROLLED_ENTITIES, [])
|
||||||
|
)
|
||||||
|
): selector.EntitySelector(
|
||||||
|
selector.EntitySelectorConfig(
|
||||||
|
domain=["light", "switch"],
|
||||||
|
multiple=True,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
vol.Optional(
|
||||||
|
CONF_CONTROL_STATES,
|
||||||
|
default=self.config_entry.options.get(
|
||||||
|
CONF_CONTROL_STATES,
|
||||||
|
self.config_entry.data.get(CONF_CONTROL_STATES, DEFAULT_CONTROL_STATES)
|
||||||
|
)
|
||||||
|
): selector.SelectSelector(
|
||||||
|
selector.SelectSelectorConfig(
|
||||||
|
options=CONTROL_STATE_OPTIONS,
|
||||||
|
multiple=True,
|
||||||
|
mode=selector.SelectSelectorMode.LIST,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
vol.Required(
|
||||||
|
CONF_LOG_LEVEL,
|
||||||
|
default=self.config_entry.options.get(CONF_LOG_LEVEL,
|
||||||
|
self.config_entry.data.get(CONF_LOG_LEVEL, DEFAULT_LOG_LEVEL))
|
||||||
|
): selector.SelectSelector(
|
||||||
|
selector.SelectSelectorConfig(
|
||||||
|
options=[
|
||||||
|
selector.SelectOptionDict(value="DEBUG", label="DEBUG (подробно)"),
|
||||||
|
selector.SelectOptionDict(value="INFO", label="INFO (обычный)"),
|
||||||
|
selector.SelectOptionDict(value="WARNING", label="WARNING (только предупреждения)"),
|
||||||
|
selector.SelectOptionDict(value="ERROR", label="ERROR (только ошибки)"),
|
||||||
|
],
|
||||||
|
mode=selector.SelectSelectorMode.DROPDOWN,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
@@ -1,38 +1,50 @@
|
|||||||
"""Constants for Motion Recorder integration."""
|
"""Constants for Motion Recorder integration."""
|
||||||
DOMAIN = "motion_recorder"
|
DOMAIN = "motion_recorder"
|
||||||
|
|
||||||
# Configuration keys
|
# Configuration keys
|
||||||
CONF_CAMERA_ENTITY = "camera_entity"
|
CONF_CAMERA_ENTITY = "camera_entity"
|
||||||
CONF_SAVE_PATH = "save_path"
|
CONF_SAVE_PATH = "save_path"
|
||||||
CONF_FILENAME_TEMPLATE = "filename_template"
|
CONF_FILENAME_TEMPLATE = "filename_template"
|
||||||
CONF_MAX_DURATION = "max_duration"
|
CONF_MAX_DURATION = "max_duration"
|
||||||
CONF_PREBUFFER = "prebuffer"
|
CONF_PREBUFFER = "prebuffer"
|
||||||
CONF_MOTION_SENSORS = "motion_sensors"
|
CONF_MOTION_SENSORS = "motion_sensors"
|
||||||
CONF_MOTION_FILTER = "motion_filter"
|
CONF_MOTION_FILTER = "motion_filter"
|
||||||
CONF_OFF_DELAY = "off_delay"
|
CONF_OFF_DELAY = "off_delay"
|
||||||
CONF_FORCE_STOP_SENSOR = "force_stop_sensor"
|
CONF_FORCE_STOP_SENSOR = "force_stop_sensor"
|
||||||
CONF_FORCE_STOP_STATE = "force_stop_state"
|
CONF_FORCE_STOP_STATE = "force_stop_state"
|
||||||
CONF_LOG_LEVEL = "log_level"
|
CONF_LOG_LEVEL = "log_level"
|
||||||
CONF_RETENTION_DAYS = "retention_days"
|
CONF_RETENTION_DAYS = "retention_days"
|
||||||
|
CONF_CONTROLLED_ENTITIES = "controlled_entities"
|
||||||
# Defaults
|
CONF_CONTROL_STATES = "control_states"
|
||||||
DEFAULT_NAME = "Motion Recorder"
|
|
||||||
DEFAULT_SAVE_PATH = "{camera_name}"
|
# Defaults
|
||||||
DEFAULT_FILENAME_TEMPLATE = "%d-%m-%Y_%H-%M-%S"
|
DEFAULT_NAME = "Motion Recorder"
|
||||||
DEFAULT_MAX_DURATION = 60
|
DEFAULT_SAVE_PATH = "{camera_name}"
|
||||||
DEFAULT_PREBUFFER = 0
|
DEFAULT_FILENAME_TEMPLATE = "%d-%m-%Y_%H-%M-%S"
|
||||||
DEFAULT_MOTION_FILTER = 0
|
DEFAULT_MAX_DURATION = 60
|
||||||
DEFAULT_OFF_DELAY = 10
|
DEFAULT_PREBUFFER = 0
|
||||||
DEFAULT_FORCE_STOP_STATE = "off"
|
DEFAULT_MOTION_FILTER = 0
|
||||||
DEFAULT_LOG_LEVEL = "INFO"
|
DEFAULT_OFF_DELAY = 10
|
||||||
DEFAULT_RETENTION_DAYS = 7
|
DEFAULT_FORCE_STOP_STATE = "off"
|
||||||
|
DEFAULT_LOG_LEVEL = "INFO"
|
||||||
# Sensor states
|
DEFAULT_RETENTION_DAYS = 7
|
||||||
STATE_IDLE = "idle"
|
DEFAULT_CONTROL_STATES = ["recording"]
|
||||||
STATE_DETECTING = "detecting"
|
|
||||||
STATE_RECORDING = "recording"
|
# Sensor states
|
||||||
STATE_DELAYING = "delaying"
|
STATE_IDLE = "idle"
|
||||||
STATE_FINALIZING = "finalizing"
|
STATE_DETECTING = "detecting"
|
||||||
STATE_COMPLETED = "completed"
|
STATE_RECORDING = "recording"
|
||||||
STATE_ERROR = "error"
|
STATE_DELAYING = "delaying"
|
||||||
STATE_DISABLED = "disabled"
|
STATE_FINALIZING = "finalizing"
|
||||||
|
STATE_ERROR = "error"
|
||||||
|
STATE_DISABLED = "disabled"
|
||||||
|
|
||||||
|
ALL_STATES = [
|
||||||
|
STATE_IDLE,
|
||||||
|
STATE_DETECTING,
|
||||||
|
STATE_RECORDING,
|
||||||
|
STATE_DELAYING,
|
||||||
|
STATE_FINALIZING,
|
||||||
|
STATE_ERROR,
|
||||||
|
STATE_DISABLED,
|
||||||
|
]
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"domain": "motion_recorder",
|
"domain": "motion_recorder",
|
||||||
"name": "Motion Recorder",
|
"name": "Motion Recorder",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"documentation": "https://github.com/sirmrak/motion_recorder",
|
"documentation": "https://github.com/sirmrak/motion_recorder",
|
||||||
"issue_tracker": "https://github.com/sirmrak/motion_recorder/issues",
|
"issue_tracker": "https://github.com/sirmrak/motion_recorder/issues",
|
||||||
"dependencies": ["camera", "stream"],
|
"dependencies": ["camera", "stream"],
|
||||||
"codeowners": ["@sirmrak"],
|
"codeowners": ["@sirmrak"],
|
||||||
"requirements": [],
|
"requirements": [],
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"config_flow": true
|
"config_flow": true
|
||||||
}
|
}
|
||||||
@@ -1,70 +1,67 @@
|
|||||||
"""Sensor platform for Motion Recorder."""
|
"""Sensor platform for Motion Recorder."""
|
||||||
from homeassistant.components.sensor import SensorEntity
|
from homeassistant.components.sensor import SensorEntity
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
from homeassistant.helpers.device_registry import DeviceInfo
|
from homeassistant.helpers.device_registry import DeviceInfo
|
||||||
|
|
||||||
from .const import DOMAIN, CONF_CAMERA_ENTITY
|
from .const import DOMAIN
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entry: ConfigEntry,
|
entry: ConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up Motion Recorder sensor."""
|
"""Set up Motion Recorder sensor."""
|
||||||
coordinator = hass.data[DOMAIN][entry.entry_id]
|
coordinator = hass.data[DOMAIN][entry.entry_id]
|
||||||
async_add_entities([MotionRecorderStatusSensor(coordinator, entry)])
|
async_add_entities([MotionRecorderStatusSensor(coordinator, entry)])
|
||||||
|
|
||||||
|
|
||||||
class MotionRecorderStatusSensor(CoordinatorEntity, SensorEntity):
|
class MotionRecorderStatusSensor(CoordinatorEntity, SensorEntity):
|
||||||
"""Representation of a Motion Recorder status sensor."""
|
"""Representation of a Motion Recorder status sensor."""
|
||||||
|
|
||||||
def __init__(self, coordinator, entry):
|
def __init__(self, coordinator, entry):
|
||||||
"""Initialize the sensor."""
|
"""Initialize the sensor."""
|
||||||
super().__init__(coordinator)
|
super().__init__(coordinator)
|
||||||
self.entry = entry
|
self.entry = entry
|
||||||
self._attr_unique_id = f"{entry.entry_id}_status"
|
self._attr_unique_id = f"{entry.entry_id}_status"
|
||||||
self._attr_name = f"{entry.title} Статус"
|
self._attr_name = f"{entry.title} Статус"
|
||||||
self._attr_translation_key = "motion_recorder_status"
|
self._attr_translation_key = "motion_recorder_status"
|
||||||
|
|
||||||
# Добавляем информацию об устройстве
|
self._attr_device_info = DeviceInfo(
|
||||||
self._attr_device_info = DeviceInfo(
|
identifiers={(DOMAIN, entry.entry_id)},
|
||||||
identifiers={(DOMAIN, entry.entry_id)},
|
name=entry.title,
|
||||||
name=entry.title,
|
manufacturer="Motion Recorder",
|
||||||
manufacturer="Motion Recorder",
|
model="Camera Recording Integration",
|
||||||
model="Camera Recording Integration",
|
)
|
||||||
)
|
|
||||||
|
self._icon_map = {
|
||||||
# Icon mapping
|
"idle": "mdi:sleep",
|
||||||
self._icon_map = {
|
"detecting": "mdi:motion-sensor",
|
||||||
"idle": "mdi:sleep",
|
"recording": "mdi:record-rec",
|
||||||
"detecting": "mdi:motion-sensor",
|
"delaying": "mdi:timer-sand",
|
||||||
"recording": "mdi:record-rec",
|
"finalizing": "mdi:file-check",
|
||||||
"delaying": "mdi:timer-sand",
|
"error": "mdi:alert-circle",
|
||||||
"finalizing": "mdi:file-check",
|
"disabled": "mdi:video-off",
|
||||||
"completed": "mdi:check-circle",
|
}
|
||||||
"error": "mdi:alert-circle",
|
|
||||||
"disabled": "mdi:video-off",
|
@property
|
||||||
}
|
def native_value(self):
|
||||||
|
"""Return the state of the sensor."""
|
||||||
@property
|
return self.coordinator.state
|
||||||
def native_value(self):
|
|
||||||
"""Return the state of the sensor."""
|
@property
|
||||||
return self.coordinator.state
|
def extra_state_attributes(self):
|
||||||
|
"""Return the state attributes."""
|
||||||
@property
|
return self.coordinator.attributes
|
||||||
def extra_state_attributes(self):
|
|
||||||
"""Return the state attributes."""
|
@property
|
||||||
return self.coordinator.attributes
|
def icon(self):
|
||||||
|
"""Return the icon."""
|
||||||
@property
|
return self._icon_map.get(self.coordinator.state, "mdi:camera")
|
||||||
def icon(self):
|
|
||||||
"""Return the icon."""
|
def _handle_coordinator_update(self) -> None:
|
||||||
return self._icon_map.get(self.coordinator.state, "mdi:camera")
|
"""Handle updated data from the coordinator."""
|
||||||
|
|
||||||
def _handle_coordinator_update(self) -> None:
|
|
||||||
"""Handle updated data from the coordinator."""
|
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
@@ -1,91 +1,99 @@
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"step": {
|
"step": {
|
||||||
"user": {
|
"user": {
|
||||||
"title": "Настройки Motion Recorder",
|
"title": "Настройки Motion Recorder",
|
||||||
"description": "Настройка интеграции записи камер по движению",
|
"description": "Настройка интеграции записи камер по движению",
|
||||||
"data": {
|
"data": {
|
||||||
"name": "Имя интеграции",
|
"name": "Имя интеграции",
|
||||||
"camera_entity": "Камера для записи",
|
"camera_entity": "Камера для записи",
|
||||||
"save_path": "Путь сохранения (относительно media)",
|
"save_path": "Путь сохранения (относительно media)",
|
||||||
"filename_template": "Формат имени файла",
|
"filename_template": "Формат имени файла",
|
||||||
"max_duration": "Максимальная длительность записи (сек)",
|
"max_duration": "Максимальная длительность записи (сек)",
|
||||||
"prebuffer": "Предбуфер (сек, 0 = отключено)",
|
"prebuffer": "Предбуфер (сек, 0 = отключено)",
|
||||||
"motion_sensors": "Датчики движения",
|
"motion_sensors": "Датчики движения",
|
||||||
"motion_filter": "Фильтр обнаружения движения (сек, 0 = без фильтрации)",
|
"motion_filter": "Фильтр обнаружения движения (сек, 0 = без фильтрации)",
|
||||||
"off_delay": "Задержка после пропадания движения (сек, 0 = без задержки)",
|
"off_delay": "Задержка после пропадания движения (сек, 0 = без задержки)",
|
||||||
"retention_days": "Хранить записи (дней, 0 = не удалять)",
|
"force_stop_sensor": "Датчик принудительной остановки (binary_sensor, switch, input_boolean)",
|
||||||
"force_stop_sensor": "Датчик принудительной остановки (binary_sensor, switch, input_boolean)",
|
"force_stop_state": "Состояние для остановки",
|
||||||
"force_stop_state": "Состояние для остановки",
|
"retention_days": "Хранить записи (дней, 0 = не удалять)",
|
||||||
"log_level": "Уровень логирования"
|
"controlled_entities": "Управляемые устройства (light/switch)",
|
||||||
},
|
"control_states": "Включать при состояниях",
|
||||||
"data_description": {
|
"log_level": "Уровень логирования"
|
||||||
"name": "Имя, которое будет отображаться в интеграции",
|
},
|
||||||
"camera_entity": "Выберите камеру из списка доступных",
|
"data_description": {
|
||||||
"save_path": "Папка для сохранения записей относительно /media. Поддерживаются переменные, например camera_name",
|
"name": "Имя, которое будет отображаться в интеграции",
|
||||||
"filename_template": "Выберите формат даты и времени для имён файлов записей. Все форматы содержат секунды, чтобы избежать перезаписи файлов при нескольких срабатываниях в одну минуту",
|
"camera_entity": "Выберите камеру из списка доступных",
|
||||||
"max_duration": "Максимальное время одной записии (10-3600 секунд). Если движение не прекратится, запись остановится по таймауту",
|
"save_path": "Папка для сохранения записей относительно /media. Поддерживаются переменные, например camera_name",
|
||||||
"prebuffer": "Сколько секунд до обнаружения движения включить в запись. Работает только если стрим уже активен",
|
"filename_template": "Выберите формат даты и времени для имён файлов записей. Все форматы содержат секунды, чтобы избежать перезаписи файлов при нескольких срабатываниях в одну минуту",
|
||||||
"motion_sensors": "Можно выбрать несколько датчиков. Запись начнётся при сработке любого из них",
|
"max_duration": "Максимальное время одной записи (10-3600 секунд). Если движение не прекратится, запись остановится по таймауту",
|
||||||
"motion_filter": "Задержка перед началом записи. Если движение пропадёт раньше — запись не начнётся (защита от ложных срабатываний)",
|
"prebuffer": "Сколько секунд до обнаружения движения включить в запись. Работает только если стрим уже активен",
|
||||||
"off_delay": "Сколько секунд ждать после пропадания движения перед остановкой записи. Если движение вернётся — таймер сбросится",
|
"motion_sensors": "Можно выбрать несколько датчиков. Запись начнётся при сработке любого из них",
|
||||||
"retention_days": "Автоматически удалять записи старше указанного количества дней. 0 = не удалять никогда. Очистка выполняется ежедневно в 3:00",
|
"motion_filter": "Задержка перед началом записи. Если движение пропадёт раньше — запись не начнётся (защита от ложных срабатываний)",
|
||||||
"force_stop_sensor": "Необязательно. Сущность, при переходе в указанное состояние запись немедленно остановится",
|
"off_delay": "Сколько секунд ждать после пропадания движения перед остановкой записи. Если движение вернётся — таймер сбросится",
|
||||||
"force_stop_state": "Значение состояния датчика принудительной остановки (обычно 'off' или 'on')",
|
"force_stop_sensor": "Необязательно. Сущность, при переходе в указанное состояние запись немедленно остановится",
|
||||||
"log_level": "Уровень детализации логов. DEBUG — для отладки, INFO — обычный режим"
|
"force_stop_state": "Значение состояния датчика принудительной остановки (обычно 'off' или 'on')",
|
||||||
}
|
"retention_days": "Автоматически удалять записи старше указанного количества дней. 0 = не удалять никогда. Очистка выполняется ежедневно в 3:00",
|
||||||
}
|
"controlled_entities": "Список светильников или переключателей, которые будут автоматически включаться/выключаться в зависимости от состояния интеграции",
|
||||||
},
|
"control_states": "Отметьте состояния, при которых управляемые устройства должны быть включены. В остальных состояниях они будут выключены (только если были включены интеграцией)",
|
||||||
"abort": {
|
"log_level": "Уровень детализации логов. DEBUG — для отладки, INFO — обычный режим"
|
||||||
"single_instance_allowed": "Разрешена только одна конфигурация на камеру"
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"abort": {
|
||||||
"step": {
|
"single_instance_allowed": "Разрешена только одна конфигурация на камеру"
|
||||||
"init": {
|
}
|
||||||
"title": "Настройки Motion Recorder",
|
},
|
||||||
"description": "Изменение параметров записи",
|
"options": {
|
||||||
"data": {
|
"step": {
|
||||||
"filename_template": "Формат имени файла",
|
"init": {
|
||||||
"max_duration": "Максимальная длительность записи (сек)",
|
"title": "Настройки Motion Recorder",
|
||||||
"prebuffer": "Предбуфер (сек, 0 = отключено)",
|
"description": "Изменение параметров записи",
|
||||||
"motion_filter": "Фильтр обнаружения движения (сек, 0 = без фильтрации)",
|
"data": {
|
||||||
"off_delay": "Задержка после пропадания движения (сек, 0 = без задержки)",
|
"filename_template": "Формат имени файла",
|
||||||
"retention_days": "Хранить записи (дней, 0 = не удалять)",
|
"max_duration": "Максимальная длительность записи (сек)",
|
||||||
"log_level": "Уровень логирования"
|
"prebuffer": "Предбуфер (сек, 0 = отключено)",
|
||||||
},
|
"motion_filter": "Фильтр обнаружения движения (сек, 0 = без фильтрации)",
|
||||||
"data_description": {
|
"off_delay": "Задержка после пропадания движения (сек, 0 = без задержки)",
|
||||||
"filename_template": "Выберите формат даты и времени для имён файлов записей",
|
"retention_days": "Хранить записи (дней, 0 = не удалять)",
|
||||||
"max_duration": "Максимальное время одной записии (10-3600 секунд)",
|
"controlled_entities": "Управляемые устройства (light/switch)",
|
||||||
"prebuffer": "Сколько секунд до обнаружения движения включить в запись",
|
"control_states": "Включать при состояниях",
|
||||||
"motion_filter": "Задержка перед началом записи для защиты от ложных срабатываний",
|
"log_level": "Уровень логирования"
|
||||||
"off_delay": "Сколько секунд ждать после пропадания движения перед остановкой записи",
|
},
|
||||||
"retention_days": "Автоматически удалять записи старше указанного количества дней. 0 = не удалять никогда",
|
"data_description": {
|
||||||
"log_level": "Уровень детализации логов"
|
"filename_template": "Выберите формат даты и времени для имён файлов записей",
|
||||||
}
|
"max_duration": "Максимальное время одной записи (10-3600 секунд)",
|
||||||
}
|
"prebuffer": "Сколько секунд до обнаружения движения включить в запись",
|
||||||
}
|
"motion_filter": "Задержка перед началом записи для защиты от ложных срабатываний",
|
||||||
},
|
"off_delay": "Сколько секунд ждать после пропадания движения перед остановкой записи",
|
||||||
"entity": {
|
"retention_days": "Автоматически удалять записи старше указанного количества дней. 0 = не удалять никогда",
|
||||||
"sensor": {
|
"controlled_entities": "Список светильников или переключателей для автоматического управления",
|
||||||
"motion_recorder_status": {
|
"control_states": "Состояния, при которых устройства должны быть включены",
|
||||||
"name": "Статус",
|
"log_level": "Уровень детализации логов"
|
||||||
"state": {
|
}
|
||||||
"idle": "Ожидание",
|
}
|
||||||
"detecting": "Обнаружение",
|
}
|
||||||
"recording": "Запись",
|
},
|
||||||
"delaying": "Задержка",
|
"entity": {
|
||||||
"finalizing": "Завершение",
|
"sensor": {
|
||||||
"completed": "Завершено",
|
"motion_recorder_status": {
|
||||||
"error": "Ошибка",
|
"name": "Статус",
|
||||||
"disabled": "Отключено"
|
"state": {
|
||||||
}
|
"idle": "Ожидание",
|
||||||
}
|
"detecting": "Обнаружение",
|
||||||
},
|
"recording": "Запись",
|
||||||
"switch": {
|
"delaying": "Задержка",
|
||||||
"motion_recorder_enabled": {
|
"finalizing": "Завершение",
|
||||||
"name": "Запись включена"
|
"completed": "Завершено",
|
||||||
}
|
"error": "Ошибка",
|
||||||
}
|
"disabled": "Отключено"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"switch": {
|
||||||
|
"motion_recorder_enabled": {
|
||||||
|
"name": "Запись включена"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,91 +1,98 @@
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"step": {
|
"step": {
|
||||||
"user": {
|
"user": {
|
||||||
"title": "Motion Recorder Settings",
|
"title": "Motion Recorder Settings",
|
||||||
"description": "Configure motion-based camera recording integration",
|
"description": "Configure motion-based camera recording integration",
|
||||||
"data": {
|
"data": {
|
||||||
"name": "Integration Name",
|
"name": "Integration Name",
|
||||||
"camera_entity": "Camera to record",
|
"camera_entity": "Camera to record",
|
||||||
"save_path": "Save path (relative to media)",
|
"save_path": "Save path (relative to media)",
|
||||||
"filename_template": "Filename format",
|
"filename_template": "Filename format",
|
||||||
"max_duration": "Maximum recording duration (seconds)",
|
"max_duration": "Maximum recording duration (seconds)",
|
||||||
"prebuffer": "Prebuffer (seconds, 0 = disabled)",
|
"prebuffer": "Prebuffer (seconds, 0 = disabled)",
|
||||||
"motion_sensors": "Motion sensors",
|
"motion_sensors": "Motion sensors",
|
||||||
"motion_filter": "Motion detection filter (seconds, 0 = no filter)",
|
"motion_filter": "Motion detection filter (seconds, 0 = no filter)",
|
||||||
"off_delay": "Delay after motion stops (seconds, 0 = no delay)",
|
"off_delay": "Delay after motion stops (seconds, 0 = no delay)",
|
||||||
"retention_days": "Keep recordings (days, 0 = never delete)",
|
"force_stop_sensor": "Force stop sensor (binary_sensor, switch, input_boolean)",
|
||||||
"force_stop_sensor": "Force stop sensor (binary_sensor, switch, input_boolean)",
|
"force_stop_state": "Stop state value",
|
||||||
"force_stop_state": "Stop state value",
|
"retention_days": "Keep recordings (days, 0 = never delete)",
|
||||||
"log_level": "Log level"
|
"controlled_entities": "Controlled devices (light/switch)",
|
||||||
},
|
"control_states": "Turn on at states",
|
||||||
"data_description": {
|
"log_level": "Log level"
|
||||||
"name": "Name that will be displayed in the integration",
|
},
|
||||||
"camera_entity": "Select a camera from the list of available",
|
"data_description": {
|
||||||
"save_path": "Folder for saving recordings relative to /media. Variables are supported, e.g. camera_name",
|
"name": "Name that will be displayed in the integration",
|
||||||
"filename_template": "Select date and time format for recording filenames. All formats include seconds to avoid file overwriting when multiple triggers occur in the same minute",
|
"camera_entity": "Select a camera from the list of available",
|
||||||
"max_duration": "Maximum time of one recording (10-3600 seconds). If motion does not stop, recording will stop by timeout",
|
"save_path": "Folder for saving recordings relative to /media. Variables are supported, e.g. camera_name",
|
||||||
"prebuffer": "How many seconds before motion detection to include in recording. Works only if stream is already active",
|
"filename_template": "Select date and time format for recording filenames. All formats include seconds to avoid file overwriting when multiple triggers occur in the same minute",
|
||||||
"motion_sensors": "You can select multiple sensors. Recording will start when any of them triggers",
|
"max_duration": "Maximum time of one recording (10-3600 seconds). If motion does not stop, recording will stop by timeout",
|
||||||
"motion_filter": "Delay before starting recording. If motion stops earlier — recording will not start (false trigger protection)",
|
"prebuffer": "How many seconds before motion detection to include in recording. Works only if stream is already active",
|
||||||
"off_delay": "How many seconds to wait after motion stops before stopping recording. If motion returns — timer resets",
|
"motion_sensors": "You can select multiple sensors. Recording will start when any of them triggers",
|
||||||
"retention_days": "Automatically delete recordings older than specified days. 0 = never delete. Cleanup runs daily at 03:00",
|
"motion_filter": "Delay before starting recording. If motion stops earlier — recording will not start (false trigger protection)",
|
||||||
"force_stop_sensor": "Optional. Entity that will immediately stop recording when it reaches the specified state",
|
"off_delay": "How many seconds to wait after motion stops before stopping recording. If motion returns — timer resets",
|
||||||
"force_stop_state": "State value of the force stop sensor (usually 'off' or 'on')",
|
"force_stop_sensor": "Optional. Entity that will immediately stop recording when it reaches the specified state",
|
||||||
"log_level": "Log detail level. DEBUG — for debugging, INFO — normal mode"
|
"force_stop_state": "State value of the force stop sensor (usually 'off' or 'on')",
|
||||||
}
|
"retention_days": "Automatically delete recordings older than specified days. 0 = never delete. Cleanup runs daily at 03:00",
|
||||||
}
|
"controlled_entities": "List of lights or switches that will be automatically turned on/off depending on the integration state",
|
||||||
},
|
"control_states": "Select states when controlled devices should be turned on. In other states they will be turned off (only if they were turned on by the integration)",
|
||||||
"abort": {
|
"log_level": "Log detail level. DEBUG — for debugging, INFO — normal mode"
|
||||||
"single_instance_allowed": "Only one configuration per camera is allowed"
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"abort": {
|
||||||
"step": {
|
"single_instance_allowed": "Only one configuration per camera is allowed"
|
||||||
"init": {
|
}
|
||||||
"title": "Motion Recorder Settings",
|
},
|
||||||
"description": "Change recording parameters",
|
"options": {
|
||||||
"data": {
|
"step": {
|
||||||
"filename_template": "Filename format",
|
"init": {
|
||||||
"max_duration": "Maximum recording duration (seconds)",
|
"title": "Motion Recorder Settings",
|
||||||
"prebuffer": "Prebuffer (seconds, 0 = disabled)",
|
"description": "Change recording parameters",
|
||||||
"motion_filter": "Motion detection filter (seconds, 0 = no filter)",
|
"data": {
|
||||||
"off_delay": "Delay after motion stops (seconds, 0 = no delay)",
|
"filename_template": "Filename format",
|
||||||
"retention_days": "Keep recordings (days, 0 = never delete)",
|
"max_duration": "Maximum recording duration (seconds)",
|
||||||
"log_level": "Log level"
|
"prebuffer": "Prebuffer (seconds, 0 = disabled)",
|
||||||
},
|
"motion_filter": "Motion detection filter (seconds, 0 = no filter)",
|
||||||
"data_description": {
|
"off_delay": "Delay after motion stops (seconds, 0 = no delay)",
|
||||||
"filename_template": "Select date and time format for recording filenames",
|
"retention_days": "Keep recordings (days, 0 = never delete)",
|
||||||
"max_duration": "Maximum time of one recording (10-3600 seconds)",
|
"controlled_entities": "Controlled devices (light/switch)",
|
||||||
"prebuffer": "How many seconds before motion detection to include in recording",
|
"control_states": "Turn on at states",
|
||||||
"motion_filter": "Delay before starting recording for false trigger protection",
|
"log_level": "Log level"
|
||||||
"off_delay": "How many seconds to wait after motion stops before stopping recording",
|
},
|
||||||
"retention_days": "Automatically delete recordings older than specified days. 0 = never delete",
|
"data_description": {
|
||||||
"log_level": "Log detail level"
|
"filename_template": "Select date and time format for recording filenames",
|
||||||
}
|
"max_duration": "Maximum time of one recording (10-3600 seconds)",
|
||||||
}
|
"prebuffer": "How many seconds before motion detection to include in recording",
|
||||||
}
|
"motion_filter": "Delay before starting recording for false trigger protection",
|
||||||
},
|
"off_delay": "How many seconds to wait after motion stops before stopping recording",
|
||||||
"entity": {
|
"retention_days": "Automatically delete recordings older than specified days. 0 = never delete",
|
||||||
"sensor": {
|
"controlled_entities": "List of lights or switches for automatic control",
|
||||||
"motion_recorder_status": {
|
"control_states": "States when devices should be turned on",
|
||||||
"name": "Status",
|
"log_level": "Log detail level"
|
||||||
"state": {
|
}
|
||||||
"idle": "Idle",
|
}
|
||||||
"detecting": "Detecting",
|
}
|
||||||
"recording": "Recording",
|
},
|
||||||
"delaying": "Delaying",
|
"entity": {
|
||||||
"finalizing": "Finalizing",
|
"sensor": {
|
||||||
"completed": "Completed",
|
"motion_recorder_status": {
|
||||||
"error": "Error",
|
"name": "Status",
|
||||||
"disabled": "Disabled"
|
"state": {
|
||||||
}
|
"idle": "Idle",
|
||||||
}
|
"detecting": "Detecting",
|
||||||
},
|
"recording": "Recording",
|
||||||
"switch": {
|
"delaying": "Delaying",
|
||||||
"motion_recorder_enabled": {
|
"finalizing": "Finalizing",
|
||||||
"name": "Recording Enabled"
|
"error": "Error",
|
||||||
}
|
"disabled": "Disabled"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"switch": {
|
||||||
|
"motion_recorder_enabled": {
|
||||||
|
"name": "Recording Enabled"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,91 +1,98 @@
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"step": {
|
"step": {
|
||||||
"user": {
|
"user": {
|
||||||
"title": "Настройки Motion Recorder",
|
"title": "Настройки Motion Recorder",
|
||||||
"description": "Настройка интеграции записи камер по движению",
|
"description": "Настройка интеграции записи камер по движению",
|
||||||
"data": {
|
"data": {
|
||||||
"name": "Имя интеграции",
|
"name": "Имя интеграции",
|
||||||
"camera_entity": "Камера для записи",
|
"camera_entity": "Камера для записи",
|
||||||
"save_path": "Путь сохранения (относительно media)",
|
"save_path": "Путь сохранения (относительно media)",
|
||||||
"filename_template": "Формат имени файла",
|
"filename_template": "Формат имени файла",
|
||||||
"max_duration": "Максимальная длительность записи (сек)",
|
"max_duration": "Максимальная длительность записи (сек)",
|
||||||
"prebuffer": "Предбуфер (сек, 0 = отключено)",
|
"prebuffer": "Предбуфер (сек, 0 = отключено)",
|
||||||
"motion_sensors": "Датчики движения",
|
"motion_sensors": "Датчики движения",
|
||||||
"motion_filter": "Фильтр обнаружения движения (сек, 0 = без фильтрации)",
|
"motion_filter": "Фильтр обнаружения движения (сек, 0 = без фильтрации)",
|
||||||
"off_delay": "Задержка после пропадания движения (сек, 0 = без задержки)",
|
"off_delay": "Задержка после пропадания движения (сек, 0 = без задержки)",
|
||||||
"retention_days": "Хранить записи (дней, 0 = не удалять)",
|
"force_stop_sensor": "Датчик принудительной остановки (binary_sensor, switch, input_boolean)",
|
||||||
"force_stop_sensor": "Датчик принудительной остановки (binary_sensor, switch, input_boolean)",
|
"force_stop_state": "Состояние для остановки",
|
||||||
"force_stop_state": "Состояние для остановки",
|
"retention_days": "Хранить записи (дней, 0 = не удалять)",
|
||||||
"log_level": "Уровень логирования"
|
"controlled_entities": "Управляемые устройства (light/switch)",
|
||||||
},
|
"control_states": "Включать при состояниях",
|
||||||
"data_description": {
|
"log_level": "Уровень логирования"
|
||||||
"name": "Имя, которое будет отображаться в интеграции",
|
},
|
||||||
"camera_entity": "Выберите камеру из списка доступных",
|
"data_description": {
|
||||||
"save_path": "Папка для сохранения записей относительно /media. Поддерживаются переменные, например camera_name",
|
"name": "Имя, которое будет отображаться в интеграции",
|
||||||
"filename_template": "Выберите формат даты и времени для имён файлов записей. Все форматы содержат секунды, чтобы избежать перезаписи файлов при нескольких срабатываниях в одну минуту",
|
"camera_entity": "Выберите камеру из списка доступных",
|
||||||
"max_duration": "Максимальное время одной записии (10-3600 секунд). Если движение не прекратится, запись остановится по таймауту",
|
"save_path": "Папка для сохранения записей относительно /media. Поддерживаются переменные, например camera_name",
|
||||||
"prebuffer": "Сколько секунд до обнаружения движения включить в запись. Работает только если стрим уже активен",
|
"filename_template": "Выберите формат даты и времени для имён файлов записей. Все форматы содержат секунды, чтобы избежать перезаписи файлов при нескольких срабатываниях в одну минуту",
|
||||||
"motion_sensors": "Можно выбрать несколько датчиков. Запись начнётся при сработке любого из них",
|
"max_duration": "Максимальное время одной записи (10-3600 секунд). Если движение не прекратится, запись остановится по таймауту",
|
||||||
"motion_filter": "Задержка перед началом записи. Если движение пропадёт раньше — запись не начнётся (защита от ложных срабатываний)",
|
"prebuffer": "Сколько секунд до обнаружения движения включить в запись. Работает только если стрим уже активен",
|
||||||
"off_delay": "Сколько секунд ждать после пропадания движения перед остановкой записи. Если движение вернётся — таймер сбросится",
|
"motion_sensors": "Можно выбрать несколько датчиков. Запись начнётся при сработке любого из них",
|
||||||
"retention_days": "Автоматически удалять записии старше указанного количества дней. 0 = не удалять никогда. Очистка выполняется ежедневно в 3:00",
|
"motion_filter": "Задержка перед началом записи. Если движение пропадёт раньше — запись не начнётся (защита от ложных срабатываний)",
|
||||||
"force_stop_sensor": "Необязательно. Сущность, при переходе в указанное состояние запись немедленно остановится",
|
"off_delay": "Сколько секунд ждать после пропадания движения перед остановкой записи. Если движение вернётся — таймер сбросится",
|
||||||
"force_stop_state": "Значение состояния датчика принудительной остановки (обычно 'off' или 'on')",
|
"force_stop_sensor": "Необязательно. Сущность, при переходе в указанное состояние запись немедленно остановится",
|
||||||
"log_level": "Уровень детализации логов. DEBUG — для отладки, INFO — обычный режим"
|
"force_stop_state": "Значение состояния датчика принудительной остановки (обычно 'off' или 'on')",
|
||||||
}
|
"retention_days": "Автоматически удалять записи старше указанного количества дней. 0 = не удалять никогда. Очистка выполняется ежедневно в 3:00",
|
||||||
}
|
"controlled_entities": "Список светильников или переключателей, которые будут автоматически включаться/выключаться в зависимости от состояния интеграции",
|
||||||
},
|
"control_states": "Отметьте состояния, при которых управляемые устройства должны быть включены. В остальных состояниях они будут выключены (только если были включены интеграцией)",
|
||||||
"abort": {
|
"log_level": "Уровень детализации логов. DEBUG — для отладки, INFO — обычный режим"
|
||||||
"single_instance_allowed": "Разрешена только одна конфигурация на камеру"
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"abort": {
|
||||||
"step": {
|
"single_instance_allowed": "Разрешена только одна конфигурация на камеру"
|
||||||
"init": {
|
}
|
||||||
"title": "Настройки Motion Recorder",
|
},
|
||||||
"description": "Изменение параметров записи",
|
"options": {
|
||||||
"data": {
|
"step": {
|
||||||
"filename_template": "Формат имени файла",
|
"init": {
|
||||||
"max_duration": "Максимальная длительность записи (сек)",
|
"title": "Настройки Motion Recorder",
|
||||||
"prebuffer": "Предбуфер (сек, 0 = отключено)",
|
"description": "Изменение параметров записи",
|
||||||
"motion_filter": "Фильтр обнаружения движения (сек, 0 = без фильтрации)",
|
"data": {
|
||||||
"off_delay": "Задержка после пропадания движения (сек, 0 = без задержки)",
|
"filename_template": "Формат имени файла",
|
||||||
"retention_days": "Хранить записии (дней, 0 = не удалять)",
|
"max_duration": "Максимальная длительность записи (сек)",
|
||||||
"log_level": "Уровень логирования"
|
"prebuffer": "Предбуфер (сек, 0 = отключено)",
|
||||||
},
|
"motion_filter": "Фильтр обнаружения движения (сек, 0 = без фильтрации)",
|
||||||
"data_description": {
|
"off_delay": "Задержка после пропадания движения (сек, 0 = без задержки)",
|
||||||
"filename_template": "Выберите формат даты и времени для имён файлов записей",
|
"retention_days": "Хранить записи (дней, 0 = не удалять)",
|
||||||
"max_duration": "Максимальное время одной записии (10-3600 секунд)",
|
"controlled_entities": "Управляемые устройства (light/switch)",
|
||||||
"prebuffer": "Сколько секунд до обнаружения движения включить в запись",
|
"control_states": "Включать при состояниях",
|
||||||
"motion_filter": "Задержка перед началом записии для защиты от ложных срабатываний",
|
"log_level": "Уровень логирования"
|
||||||
"off_delay": "Сколько секунд ждать после пропадания движения перед остановкой записии",
|
},
|
||||||
"retention_days": "Автоматически удалять записии старше указанного количества дней. 0 = не удалять никогда",
|
"data_description": {
|
||||||
"log_level": "Уровень детализации логов"
|
"filename_template": "Выберите формат даты и времени для имён файлов записей",
|
||||||
}
|
"max_duration": "Максимальное время одной записи (10-3600 секунд)",
|
||||||
}
|
"prebuffer": "Сколько секунд до обнаружения движения включить в запись",
|
||||||
}
|
"motion_filter": "Задержка перед началом записи для защиты от ложных срабатываний",
|
||||||
},
|
"off_delay": "Сколько секунд ждать после пропадания движения перед остановкой записи",
|
||||||
"entity": {
|
"retention_days": "Автоматически удалять записи старше указанного количества дней. 0 = не удалять никогда",
|
||||||
"sensor": {
|
"controlled_entities": "Список светильников или переключателей для автоматического управления",
|
||||||
"motion_recorder_status": {
|
"control_states": "Состояния, при которых устройства должны быть включены",
|
||||||
"name": "Статус",
|
"log_level": "Уровень детализации логов"
|
||||||
"state": {
|
}
|
||||||
"idle": "Ожидание",
|
}
|
||||||
"detecting": "Обнаружение",
|
}
|
||||||
"recording": "Запись",
|
},
|
||||||
"delaying": "Задержка",
|
"entity": {
|
||||||
"finalizing": "Завершение",
|
"sensor": {
|
||||||
"completed": "Завершено",
|
"motion_recorder_status": {
|
||||||
"error": "Ошибка",
|
"name": "Статус",
|
||||||
"disabled": "Отключено"
|
"state": {
|
||||||
}
|
"idle": "Ожидание",
|
||||||
}
|
"detecting": "Обнаружение",
|
||||||
},
|
"recording": "Запись",
|
||||||
"switch": {
|
"delaying": "Задержка",
|
||||||
"motion_recorder_enabled": {
|
"finalizing": "Завершение",
|
||||||
"name": "Запись включена"
|
"error": "Ошибка",
|
||||||
}
|
"disabled": "Отключено"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"switch": {
|
||||||
|
"motion_recorder_enabled": {
|
||||||
|
"name": "Запись включена"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user