Reduce idle timeout of HLS stream to conserve camera battery life (#147728)

* Reduce IDLE timeout of HLS stream to conserve camera battery life

* adjust tests
This commit is contained in:
starkillerOG 2025-06-28 22:20:47 +02:00 committed by Paulus Schoutsen
parent 81e712ea49
commit 33e1c6de68
4 changed files with 17 additions and 10 deletions

View File

@ -55,6 +55,7 @@ from .const import (
MAX_SEGMENTS, MAX_SEGMENTS,
OUTPUT_FORMATS, OUTPUT_FORMATS,
OUTPUT_IDLE_TIMEOUT, OUTPUT_IDLE_TIMEOUT,
OUTPUT_STARTUP_TIMEOUT,
RECORDER_PROVIDER, RECORDER_PROVIDER,
RTSP_TRANSPORTS, RTSP_TRANSPORTS,
SEGMENT_DURATION_ADJUSTER, SEGMENT_DURATION_ADJUSTER,
@ -363,11 +364,14 @@ class Stream:
# without concern about self._outputs being modified from another thread. # without concern about self._outputs being modified from another thread.
return MappingProxyType(self._outputs.copy()) return MappingProxyType(self._outputs.copy())
def add_provider( def add_provider(self, fmt: str, timeout: int | None = None) -> StreamOutput:
self, fmt: str, timeout: int = OUTPUT_IDLE_TIMEOUT
) -> StreamOutput:
"""Add provider output stream.""" """Add provider output stream."""
if not (provider := self._outputs.get(fmt)): if not (provider := self._outputs.get(fmt)):
startup_timeout = OUTPUT_STARTUP_TIMEOUT
if timeout is None:
timeout = OUTPUT_IDLE_TIMEOUT
else:
startup_timeout = timeout
async def idle_callback() -> None: async def idle_callback() -> None:
if ( if (
@ -379,7 +383,7 @@ class Stream:
provider = PROVIDERS[fmt]( provider = PROVIDERS[fmt](
self.hass, self.hass,
IdleTimer(self.hass, timeout, idle_callback), IdleTimer(self.hass, timeout, idle_callback, startup_timeout),
self._stream_settings, self._stream_settings,
self.dynamic_stream_settings, self.dynamic_stream_settings,
) )

View File

@ -22,7 +22,8 @@ AUDIO_CODECS = {"aac", "mp3"}
FORMAT_CONTENT_TYPE = {HLS_PROVIDER: "application/vnd.apple.mpegurl"} FORMAT_CONTENT_TYPE = {HLS_PROVIDER: "application/vnd.apple.mpegurl"}
OUTPUT_IDLE_TIMEOUT = 300 # Idle timeout due to inactivity OUTPUT_STARTUP_TIMEOUT = 60 # timeout due to no startup
OUTPUT_IDLE_TIMEOUT = 30 # Idle timeout due to inactivity
NUM_PLAYLIST_SEGMENTS = 3 # Number of segments to use in HLS playlist NUM_PLAYLIST_SEGMENTS = 3 # Number of segments to use in HLS playlist
MAX_SEGMENTS = 5 # Max number of segments to keep around MAX_SEGMENTS = 5 # Max number of segments to keep around

View File

@ -234,10 +234,12 @@ class IdleTimer:
hass: HomeAssistant, hass: HomeAssistant,
timeout: int, timeout: int,
idle_callback: Callable[[], Coroutine[Any, Any, None]], idle_callback: Callable[[], Coroutine[Any, Any, None]],
startup_timeout: int | None = None,
) -> None: ) -> None:
"""Initialize IdleTimer.""" """Initialize IdleTimer."""
self._hass = hass self._hass = hass
self._timeout = timeout self._timeout = timeout
self._startup_timeout = startup_timeout or timeout
self._callback = idle_callback self._callback = idle_callback
self._unsub: CALLBACK_TYPE | None = None self._unsub: CALLBACK_TYPE | None = None
self.idle = False self.idle = False
@ -246,7 +248,7 @@ class IdleTimer:
"""Start the idle timer if not already started.""" """Start the idle timer if not already started."""
self.idle = False self.idle = False
if self._unsub is None: if self._unsub is None:
self._unsub = async_call_later(self._hass, self._timeout, self.fire) self._unsub = async_call_later(self._hass, self._startup_timeout, self.fire)
def awake(self) -> None: def awake(self) -> None:
"""Keep the idle time alive by resetting the timeout.""" """Keep the idle time alive by resetting the timeout."""

View File

@ -230,8 +230,8 @@ async def test_stream_timeout(
playlist_response = await http_client.get(parsed_url.path) playlist_response = await http_client.get(parsed_url.path)
assert playlist_response.status == HTTPStatus.OK assert playlist_response.status == HTTPStatus.OK
# Wait a minute # Wait 40 seconds
future = dt_util.utcnow() + timedelta(minutes=1) future = dt_util.utcnow() + timedelta(seconds=40)
async_fire_time_changed(hass, future) async_fire_time_changed(hass, future)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -241,8 +241,8 @@ async def test_stream_timeout(
stream_worker_sync.resume() stream_worker_sync.resume()
# Wait 5 minutes # Wait 2 minutes
future = dt_util.utcnow() + timedelta(minutes=5) future = dt_util.utcnow() + timedelta(minutes=2)
async_fire_time_changed(hass, future) async_fire_time_changed(hass, future)
await hass.async_block_till_done() await hass.async_block_till_done()