python-aidot-cameras 0.5.5__tar.gz → 0.5.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_aidot_cameras-0.5.5/src/python_aidot_cameras.egg-info → python_aidot_cameras-0.5.7}/PKG-INFO +1 -1
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/pyproject.toml +1 -1
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/src/aidot/device_client.py +60 -19
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7/src/python_aidot_cameras.egg-info}/PKG-INFO +1 -1
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/LICENSE +0 -0
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/README.md +0 -0
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/setup.cfg +0 -0
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/src/aidot/__init__.py +0 -0
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/src/aidot/aes_utils.py +0 -0
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/src/aidot/client.py +0 -0
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/src/aidot/const.py +0 -0
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/src/aidot/credentials.py +0 -0
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/src/aidot/discover.py +0 -0
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/src/aidot/exceptions.py +0 -0
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/src/aidot/g711.py +0 -0
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/src/python_aidot_cameras.egg-info/SOURCES.txt +0 -0
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/src/python_aidot_cameras.egg-info/dependency_links.txt +0 -0
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/src/python_aidot_cameras.egg-info/requires.txt +0 -0
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/src/python_aidot_cameras.egg-info/top_level.txt +0 -0
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/tests/test_alarm_event.py +0 -0
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/tests/test_highport_nomination.py +0 -0
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/tests/test_motion_poll.py +0 -0
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/tests/test_sdes_talk.py +0 -0
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/tests/test_sdes_watchdog.py +0 -0
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/tests/test_speak.py +0 -0
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/tests/test_stream_cap.py +0 -0
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/tests/test_talk.py +0 -0
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/tests/test_terminal_ack.py +0 -0
- {python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7}/tests/test_token_refresh.py +0 -0
{python_aidot_cameras-0.5.5/src/python_aidot_cameras.egg-info → python_aidot_cameras-0.5.7}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-aidot-cameras
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.7
|
|
4
4
|
Summary: Control AiDot/Leedarson WiFi lights and cameras (WebRTC streaming, two-way audio, PTZ, controls)
|
|
5
5
|
Author-email: cbrightly <chris.brightly@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-aidot-cameras"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.7"
|
|
8
8
|
description = "Control AiDot/Leedarson WiFi lights and cameras (WebRTC streaming, two-way audio, PTZ, controls)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -4042,7 +4042,11 @@ class DeviceClient(object):
|
|
|
4042
4042
|
except asyncio.CancelledError:
|
|
4043
4043
|
return
|
|
4044
4044
|
|
|
4045
|
-
async def start_keepalive(
|
|
4045
|
+
async def start_keepalive(
|
|
4046
|
+
self,
|
|
4047
|
+
rtsp_push_url: Optional[str] = None,
|
|
4048
|
+
fast_connect: Optional[bool] = None,
|
|
4049
|
+
) -> None:
|
|
4046
4050
|
"""Start a persistent stream that keeps the camera session alive.
|
|
4047
4051
|
|
|
4048
4052
|
For SDES cameras (A001064/A001513): opens an indefinite ffmpeg stream and
|
|
@@ -4054,8 +4058,15 @@ class DeviceClient(object):
|
|
|
4054
4058
|
pulls (real play/pause video, no decode/re-encode). Without a serve URL
|
|
4055
4059
|
it falls back to the on_frame keepalive loop (latest_jpeg / MJPEG).
|
|
4056
4060
|
|
|
4061
|
+
``fast_connect`` enables LAN-direct mode (skip the ~2.5s livePlay/ICE-config
|
|
4062
|
+
waits + TURN relay; see _async_open_webrtc_stream_impl). ``None`` leaves it
|
|
4063
|
+
to the ``AIDOT_FAST_CONNECT`` env var; ``True``/``False`` override it (e.g.
|
|
4064
|
+
from a Home Assistant config-entry option, since HA OS can't set env vars).
|
|
4065
|
+
|
|
4057
4066
|
Safe to call multiple times - does nothing if already running.
|
|
4058
4067
|
"""
|
|
4068
|
+
if fast_connect is not None:
|
|
4069
|
+
self._fast_connect_opt = fast_connect
|
|
4059
4070
|
if self._stream_task is not None and not self._stream_task.done():
|
|
4060
4071
|
return
|
|
4061
4072
|
self._keepalive_rtsp_url = rtsp_push_url
|
|
@@ -5164,9 +5175,13 @@ class DeviceClient(object):
|
|
|
5164
5175
|
# LAN host candidate connects in ~1 s. TRADE-OFF: no relay means cameras
|
|
5165
5176
|
# on a different network segment / behind strict NAT cannot connect, so
|
|
5166
5177
|
# this is opt-in and off by default.
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
)
|
|
5178
|
+
# Prefer an explicit per-camera setting (e.g. HA config-entry option via
|
|
5179
|
+
# start_keepalive(fast_connect=...)); fall back to the env var otherwise.
|
|
5180
|
+
_fast_connect = getattr(self, "_fast_connect_opt", None)
|
|
5181
|
+
if _fast_connect is None:
|
|
5182
|
+
_fast_connect = os.environ.get("AIDOT_FAST_CONNECT", "").strip().lower() in (
|
|
5183
|
+
"1", "true", "yes", "on",
|
|
5184
|
+
)
|
|
5170
5185
|
|
|
5171
5186
|
# Wake battery cameras via the cloud HTTP low-power endpoint before the
|
|
5172
5187
|
# handshake (matches the app, which fires the HTTP wake so a sleeping camera
|
|
@@ -5938,25 +5953,40 @@ class DeviceClient(object):
|
|
|
5938
5953
|
_status(f"livePlayReq sent peerid={peer_id}")
|
|
5939
5954
|
# Wait for the broker to echo livePlayReq back (confirms delivery).
|
|
5940
5955
|
# Proceed as soon as the echo arrives; fall through after 0.5 s.
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5956
|
+
# AIDOT_FAST_CONNECT skips this too (the echo often just times out,
|
|
5957
|
+
# costing a flat 0.5 s); we proceed straight to SDP/ICE.
|
|
5958
|
+
if not _fast_connect:
|
|
5959
|
+
try:
|
|
5960
|
+
await asyncio.wait_for(liveplay_echo_ev.wait(), timeout=0.5)
|
|
5961
|
+
except asyncio.TimeoutError:
|
|
5962
|
+
pass
|
|
5945
5963
|
# livePlayResp carries camera-side accept/reject. If the camera
|
|
5946
5964
|
# rejects start-play, continuing to SDP/ICE causes large STUN churn
|
|
5947
5965
|
# but no media. Fail fast with the concrete code.
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5966
|
+
# MEASURED (2026-06-07, live A/B): this up-to-2 s wait is the DOMINANT
|
|
5967
|
+
# cold-start cost (camera-awake → ICE-servers ≈ 2.5 s = 0.5 s echo +
|
|
5968
|
+
# 2.0 s here). The official app does NOT wait for/parse livePlayResp
|
|
5969
|
+
# (parity-confirmed). AIDOT_FAST_CONNECT skips it and proceeds to
|
|
5970
|
+
# SDP/ICE immediately — losing fast-fail on rejection (rare; ICE then
|
|
5971
|
+
# fails instead) in exchange for ~2 s off every LAN connect.
|
|
5972
|
+
if not _fast_connect:
|
|
5973
|
+
try:
|
|
5974
|
+
_lp_resp = await asyncio.wait_for(
|
|
5975
|
+
asyncio.shield(liveplay_resp_fut), timeout=2.0
|
|
5957
5976
|
)
|
|
5958
|
-
|
|
5959
|
-
|
|
5977
|
+
_lp_code = int(_lp_resp.get("code", 200))
|
|
5978
|
+
_lp_on = int(_lp_resp.get("livePlay", 1))
|
|
5979
|
+
if _lp_code not in (0, 200) or _lp_on == 0:
|
|
5980
|
+
raise RuntimeError(
|
|
5981
|
+
f"livePlay rejected by camera (code={_lp_code}, livePlay={_lp_on})"
|
|
5982
|
+
)
|
|
5983
|
+
except asyncio.TimeoutError:
|
|
5984
|
+
pass
|
|
5985
|
+
else:
|
|
5986
|
+
_status(
|
|
5987
|
+
"AIDOT_FAST_CONNECT: skipping livePlayResp wait (~2s) -"
|
|
5988
|
+
" proceeding to SDP/ICE (app-parity, no fast-fail on reject)"
|
|
5989
|
+
)
|
|
5960
5990
|
# Short extra wait for getIceConfigResp - the server may only respond
|
|
5961
5991
|
# once a live camera session is active (i.e. after livePlayReq).
|
|
5962
5992
|
# Waiting here ensures TURN credentials arrive before RTCPeerConnection
|
|
@@ -8614,6 +8644,17 @@ class DeviceClient(object):
|
|
|
8614
8644
|
import tempfile
|
|
8615
8645
|
import json
|
|
8616
8646
|
|
|
8647
|
+
# _open_sdes_stream runs in its own scope, so recompute the fast-connect
|
|
8648
|
+
# flag here (same precedence as the parent open path: explicit per-camera
|
|
8649
|
+
# option set via start_keepalive, else the AIDOT_FAST_CONNECT env var).
|
|
8650
|
+
# Without this the SDES path NameErrors on every open (regression fixed
|
|
8651
|
+
# 2026-06-07: the SDES TURN-skip gates reference _fast_connect).
|
|
8652
|
+
_fast_connect = getattr(self, "_fast_connect_opt", None)
|
|
8653
|
+
if _fast_connect is None:
|
|
8654
|
+
_fast_connect = os.environ.get("AIDOT_FAST_CONNECT", "").strip().lower() in (
|
|
8655
|
+
"1", "true", "yes", "on",
|
|
8656
|
+
)
|
|
8657
|
+
|
|
8617
8658
|
user_id = user_id or str(self.user_id)
|
|
8618
8659
|
|
|
8619
8660
|
# Models confirmed (2026-05-02) to send TUTK-framed data instead of
|
{python_aidot_cameras-0.5.5 → python_aidot_cameras-0.5.7/src/python_aidot_cameras.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-aidot-cameras
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.7
|
|
4
4
|
Summary: Control AiDot/Leedarson WiFi lights and cameras (WebRTC streaming, two-way audio, PTZ, controls)
|
|
5
5
|
Author-email: cbrightly <chris.brightly@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|