fix: FST
Update Version / Update Version (push) Successful in 8s

При выключении switch запись полностью останавливается и не продолжается
При активации force_stop запись мгновенно останавливается и блокируется до деактивации
При деактивации force_stop система возвращается в нормальный режим
This commit is contained in:
2026-07-11 18:33:15 +03:00
parent 495fbe5dce
commit dc85e69e28
@@ -92,6 +92,8 @@ class MotionRecorderCoordinator(DataUpdateCoordinator):
# Enabled state
self._enabled = True
self._force_stopped = False
# Listeners
self._unsub_motion_listeners = []
self._unsub_force_stop_listener = None
@@ -146,6 +148,7 @@ class MotionRecorderCoordinator(DataUpdateCoordinator):
attrs["recordings_count"] = self._recordings_count
attrs["total_duration"] = self._total_duration
attrs["total_size"] = self._total_size
attrs["force_stopped"] = self._force_stopped
return attrs
@property
@@ -159,17 +162,39 @@ class MotionRecorderCoordinator(DataUpdateCoordinator):
_LOGGER.info("Recording %s", "enabled" if enabled else "disabled")
if not enabled:
await self._cancel_all_timers()
# Останавливаем запись если идёт
if self._state == STATE_RECORDING:
await self._stop_recording()
# Переходим в disabled
await self._update_state(STATE_DISABLED)
else:
await self._update_state(STATE_IDLE)
# Включаем только если не заблокированы force_stop
if self._force_stopped:
_LOGGER.info("Cannot enable: force stop is active")
await self._update_state(STATE_DISABLED)
else:
await self._update_state(STATE_IDLE)
self.async_set_updated_data({
"state": self._state,
"attributes": self.attributes
})
async def _cancel_all_timers(self) -> None:
"""Отменить все активные таймеры."""
if self._motion_detect_timer:
_LOGGER.debug("Cancelling motion detect timer")
self._motion_detect_timer()
self._motion_detect_timer = None
if self._off_delay_timer:
_LOGGER.debug("Cancelling off-delay timer")
self._off_delay_timer()
self._off_delay_timer = None
async def async_setup(self):
"""Set up the coordinator."""
_LOGGER.info("Setting up Motion Recorder coordinator for %s", self.camera_entity)
@@ -364,9 +389,43 @@ class MotionRecorderCoordinator(DataUpdateCoordinator):
if not new_state:
return
if new_state.state == self.force_stop_state:
_LOGGER.info("Force stop triggered by %s", event.data.get("entity_id"))
self.hass.async_create_task(self._force_stop_recording())
new_s = new_state.state
sensor_entity = event.data.get("entity_id")
_LOGGER.debug("Force stop sensor %s changed to: %s", sensor_entity, new_s)
if new_s == self.force_stop_state:
_LOGGER.info("Force stop ACTIVATED by %s", sensor_entity)
self.hass.async_create_task(self._activate_force_stop())
else:
_LOGGER.info("Force stop DEACTIVATED by %s", sensor_entity)
self.hass.async_create_task(self._deactivate_force_stop())
async def _activate_force_stop(self) -> None:
"""Активировать force stop блокировку."""
self._force_stopped = True
_LOGGER.info("Force stop blocking activated")
# Отменяем все таймеры
await self._cancel_all_timers()
# Останавливаем запись если идёт
if self._state == STATE_RECORDING:
await self._stop_recording()
# Переходим в disabled
await self._update_state(STATE_DISABLED)
async def _deactivate_force_stop(self) -> None:
"""Деактивировать force stop блокировку."""
self._force_stopped = False
_LOGGER.info("Force stop blocking deactivated")
# Возвращаемся в idle только если запись включена
if self._enabled:
await self._update_state(STATE_IDLE)
else:
await self._update_state(STATE_DISABLED)
def _is_any_motion_active(self) -> bool:
"""Check if any motion sensor is currently in 'on' state."""
@@ -382,6 +441,10 @@ class MotionRecorderCoordinator(DataUpdateCoordinator):
_LOGGER.debug("Recording disabled, ignoring motion")
return
if self._force_stopped:
_LOGGER.debug("Force stop active, ignoring motion")
return
_LOGGER.debug("Motion detected by %s, current state: %s", triggered_by, self._state)
if self._state == STATE_RECORDING:
@@ -423,6 +486,12 @@ class MotionRecorderCoordinator(DataUpdateCoordinator):
async def _start_recording_after_filter(self, _=None):
"""Start recording after motion filter."""
if not self._enabled or self._force_stopped:
_LOGGER.debug("Cannot start recording: enabled=%s, force_stopped=%s",
self._enabled, self._force_stopped)
await self._update_state(STATE_IDLE if self._enabled else STATE_DISABLED)
return
_LOGGER.debug("Starting recording after filter")
self._motion_detect_timer = None
triggered_by = self._attributes.get("triggered_by")
@@ -430,6 +499,10 @@ class MotionRecorderCoordinator(DataUpdateCoordinator):
async def _handle_motion_stopped(self):
"""Handle motion stopped with off-delay."""
if self._force_stopped:
_LOGGER.debug("Force stop active, ignoring motion stopped")
return
_LOGGER.debug("Motion stopped, current state: %s", self._state)
if self._is_any_motion_active():
@@ -465,10 +538,19 @@ class MotionRecorderCoordinator(DataUpdateCoordinator):
_LOGGER.debug("Cancelling motion filter timer")
self._motion_detect_timer()
self._motion_detect_timer = None
await self._update_state(STATE_IDLE)
if not self._enabled:
await self._update_state(STATE_DISABLED)
else:
await self._update_state(STATE_IDLE)
async def _stop_recording_after_delay(self, _=None):
"""Stop recording after off-delay."""
if self._force_stopped or not self._enabled:
_LOGGER.debug("Cannot stop recording: enabled=%s, force_stopped=%s",
self._enabled, self._force_stopped)
return
_LOGGER.debug("Off-delay timer expired")
self._off_delay_timer = None
@@ -482,6 +564,12 @@ class MotionRecorderCoordinator(DataUpdateCoordinator):
async def _start_recording(self, triggered_by):
"""Start recording the camera."""
if not self._enabled or self._force_stopped:
_LOGGER.warning("Cannot start recording: enabled=%s, force_stopped=%s",
self._enabled, self._force_stopped)
await self._update_state(STATE_IDLE if self._enabled else STATE_DISABLED)
return
try:
if self._current_stream:
recorder_output = self._current_stream.outputs().get(_RECORDER_PROVIDER)
@@ -578,13 +666,13 @@ class MotionRecorderCoordinator(DataUpdateCoordinator):
_LOGGER.error("Error stopping recording: %s", err, exc_info=True)
await self._update_state(STATE_ERROR, error_message=str(err))
await asyncio.sleep(5)
if not self._enabled:
if not self._enabled or self._force_stopped:
await self._update_state(STATE_DISABLED)
else:
await self._update_state(STATE_IDLE)
async def _force_stop_recording(self):
"""Force stop recording."""
"""Force stop recording (вызывается при активации force_stop)."""
_LOGGER.info("Force stop recording called")
await self._stop_recording()
@@ -628,9 +716,8 @@ class MotionRecorderCoordinator(DataUpdateCoordinator):
self._recording_start_time = None
self._current_filename = None
# Проверяем, не был ли выключен переключатель во время записи
if not self._enabled:
_LOGGER.debug("Recording disabled during finalization, transitioning to disabled")
if not self._enabled or self._force_stopped:
_LOGGER.debug("Recording disabled or force stopped during finalization, transitioning to disabled")
await self._update_state(STATE_DISABLED)
else:
_LOGGER.debug("Transitioning to idle")