python-aidot-cameras 0.7.23__tar.gz → 0.7.25__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.7.23/src/python_aidot_cameras.egg-info → python_aidot_cameras-0.7.25}/PKG-INFO +1 -1
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/pyproject.toml +1 -1
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/camera/client.py +55 -9
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25/src/python_aidot_cameras.egg-info}/PKG-INFO +1 -1
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/tests/test_sdes_fast_liveplay.py +16 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/LICENSE +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/MANIFEST.in +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/README.md +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/setup.cfg +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/__init__.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/aes_utils.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/camera/__init__.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/camera/constants.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/camera/controls.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/camera/go2rtc.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/camera/lan_control.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/camera/models.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/camera/playback.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/camera/protocol.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/camera/sdes.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/camera/tutk.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/camera/webrtc.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/client.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/const.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/credentials.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/device_client.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/discover.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/exceptions.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/g711.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/login_const.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/models/__init__.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/models/device_client_model.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/models/device_model.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/models/discover_model.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/py.typed +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/python_aidot_cameras.egg-info/SOURCES.txt +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/python_aidot_cameras.egg-info/dependency_links.txt +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/python_aidot_cameras.egg-info/requires.txt +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/python_aidot_cameras.egg-info/top_level.txt +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/tests/test_alarm_event.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/tests/test_backoff.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/tests/test_go2rtc.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/tests/test_highport_nomination.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/tests/test_lan_control.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/tests/test_live_stream_param.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/tests/test_motion_poll.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/tests/test_no_undefined_names.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/tests/test_post_merge_hardening.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/tests/test_sdes_idle_release.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/tests/test_sdes_sprop.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/tests/test_sdes_talk.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/tests/test_sdes_watchdog.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/tests/test_serve_relay.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/tests/test_speak.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/tests/test_stream_cap.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/tests/test_stream_idle.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/tests/test_talk.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/tests/test_terminal_ack.py +0 -0
- {python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/tests/test_token_refresh.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-aidot-cameras
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.25
|
|
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.7.
|
|
7
|
+
version = "0.7.25"
|
|
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"
|
|
@@ -543,6 +543,14 @@ class CameraMixin(_CameraControlsMixin):
|
|
|
543
543
|
# data-channel-less; it's parked on the SDES/TUTK side anyway.
|
|
544
544
|
_NO_DATACHANNEL_MODELS: frozenset = frozenset({"LK.IPC.A001064"})
|
|
545
545
|
|
|
546
|
+
# A001064's role-reversal handshake (the camera echoes our offer back as its
|
|
547
|
+
# own webrtcReq before doing ICE, and won't stream until it gets our
|
|
548
|
+
# webrtcResp) needs the camera armed BEFORE our webrtcReq. sdes_fast_liveplay
|
|
549
|
+
# sends webrtcReq ~4.5 s earlier, which degrades A001064 media reliability
|
|
550
|
+
# (2/2 -> 1/2 media in a live A/B; the flag was soak-validated only on the
|
|
551
|
+
# A001513 battery cameras). Exclude role-reversal models from the flag.
|
|
552
|
+
_NO_FAST_LIVEPLAY_MODELS: frozenset = frozenset({"LK.IPC.A001064"})
|
|
553
|
+
|
|
546
554
|
@property
|
|
547
555
|
def _offer_should_include_datachannel(self) -> bool:
|
|
548
556
|
model = getattr(getattr(self, "info", None), "model_id", None)
|
|
@@ -2537,7 +2545,14 @@ class CameraMixin(_CameraControlsMixin):
|
|
|
2537
2545
|
|
|
2538
2546
|
Per-camera ``sdes_fast_liveplay`` (set via start_keepalive) wins; else the
|
|
2539
2547
|
``AIDOT_SDES_FAST_LIVEPLAY`` env (truthy = 1/true/yes/on), default off.
|
|
2540
|
-
Only consulted for SDES cameras (the caller gates on is_sdes_camera).
|
|
2548
|
+
Only consulted for SDES cameras (the caller gates on is_sdes_camera).
|
|
2549
|
+
|
|
2550
|
+
Role-reversal models (``_NO_FAST_LIVEPLAY_MODELS``, e.g. A001064) are always
|
|
2551
|
+
excluded - their handshake needs the camera armed before our webrtcReq, so
|
|
2552
|
+
the flag's early webrtcReq degrades their media reliability."""
|
|
2553
|
+
model = getattr(getattr(self, "info", None), "model_id", None)
|
|
2554
|
+
if model in self._NO_FAST_LIVEPLAY_MODELS:
|
|
2555
|
+
return False
|
|
2541
2556
|
opt = getattr(self, "_sdes_fast_liveplay_opt", None)
|
|
2542
2557
|
if opt is not None:
|
|
2543
2558
|
return bool(opt)
|
|
@@ -3705,7 +3720,14 @@ class CameraMixin(_CameraControlsMixin):
|
|
|
3705
3720
|
or msg.get("devId") == device_id):
|
|
3706
3721
|
loop.call_soon_threadsafe(camera_ready_ev.set)
|
|
3707
3722
|
# livePlayResp: explicit camera ack/nack for start-play command.
|
|
3708
|
-
|
|
3723
|
+
# The camera echoes our peer_id (verified live); its payload has NO
|
|
3724
|
+
# devId, so the old devId-only match never fired and this future never
|
|
3725
|
+
# resolved (the wait always timed out). Match on the echoed peer_id
|
|
3726
|
+
# (per-open, precise); keep devId as a fallback for any camera that
|
|
3727
|
+
# does send it. dstAddr is the account-wide userId - too loose to use.
|
|
3728
|
+
if method == "livePlayResp" and (
|
|
3729
|
+
inner.get("peerid") == peer_id
|
|
3730
|
+
or inner.get("devId") == device_id):
|
|
3709
3731
|
if not liveplay_resp_fut.done():
|
|
3710
3732
|
loop.call_soon_threadsafe(liveplay_resp_fut.set_result, inner)
|
|
3711
3733
|
# livePlayReq echo: broker/camera confirmed delivery of our livePlayReq.
|
|
@@ -4171,9 +4193,19 @@ class CameraMixin(_CameraControlsMixin):
|
|
|
4171
4193
|
_lp_arrived = True
|
|
4172
4194
|
_lp_code = int(_lp_resp.get("code", 200))
|
|
4173
4195
|
_lp_on = int(_lp_resp.get("livePlay", 1))
|
|
4174
|
-
|
|
4196
|
+
# Fast-fail only on an unambiguous refusal (livePlay=0).
|
|
4197
|
+
# Other non-OK codes (incl. -50019 "not ready") are
|
|
4198
|
+
# transient on battery cameras and recover via ICE; abort
|
|
4199
|
+
# there would spuriously kill otherwise-good streams.
|
|
4200
|
+
if _lp_on == 0:
|
|
4175
4201
|
raise RuntimeError(
|
|
4176
|
-
f"livePlay
|
|
4202
|
+
f"livePlay refused by camera (livePlay=0, code={_lp_code})"
|
|
4203
|
+
)
|
|
4204
|
+
elif _lp_code not in (0, 200):
|
|
4205
|
+
_status(
|
|
4206
|
+
f"livePlayResp: non-OK code {_lp_code}"
|
|
4207
|
+
f"{' (not ready, transient)' if _lp_code == -50019 else ''}"
|
|
4208
|
+
" - proceeding"
|
|
4177
4209
|
)
|
|
4178
4210
|
except TimeoutError:
|
|
4179
4211
|
pass
|
|
@@ -4216,9 +4248,15 @@ class CameraMixin(_CameraControlsMixin):
|
|
|
4216
4248
|
_lp_resp2 = liveplay_resp_fut.result()
|
|
4217
4249
|
_lp_code2 = int(_lp_resp2.get("code", 200))
|
|
4218
4250
|
_lp_on2 = int(_lp_resp2.get("livePlay", 1))
|
|
4219
|
-
if
|
|
4251
|
+
if _lp_on2 == 0:
|
|
4220
4252
|
raise RuntimeError(
|
|
4221
|
-
f"livePlay
|
|
4253
|
+
f"livePlay refused by camera (livePlay=0, code={_lp_code2})"
|
|
4254
|
+
)
|
|
4255
|
+
elif _lp_code2 not in (0, 200):
|
|
4256
|
+
_status(
|
|
4257
|
+
f"livePlayResp: non-OK code {_lp_code2}"
|
|
4258
|
+
f"{' (not ready, transient)' if _lp_code2 == -50019 else ''}"
|
|
4259
|
+
" - proceeding"
|
|
4222
4260
|
)
|
|
4223
4261
|
except RuntimeError:
|
|
4224
4262
|
raise
|
|
@@ -7085,10 +7123,18 @@ class CameraMixin(_CameraControlsMixin):
|
|
|
7085
7123
|
_lp_arrived = True
|
|
7086
7124
|
_lp_code_sdes = int(_lp_resp_sdes.get("code", 200))
|
|
7087
7125
|
_lp_on_sdes = int(_lp_resp_sdes.get("livePlay", 1))
|
|
7088
|
-
|
|
7126
|
+
# Only an explicit livePlay=0 is an unambiguous refusal (fast-fail).
|
|
7127
|
+
# Numeric codes (e.g. -50019 "not ready" on a waking battery cam)
|
|
7128
|
+
# are transient - the camera recovers and streams - so log and
|
|
7129
|
+
# proceed rather than abort on a code we can't classify as terminal
|
|
7130
|
+
# (genuine terminal rejects are still caught on the webrtcResp ack).
|
|
7131
|
+
if _lp_on_sdes == 0:
|
|
7089
7132
|
raise RuntimeError(
|
|
7090
|
-
f"livePlay
|
|
7091
|
-
|
|
7133
|
+
f"livePlay refused by camera (livePlay=0, code={_lp_code_sdes})")
|
|
7134
|
+
elif _lp_code_sdes not in (0, 200):
|
|
7135
|
+
_status(f"livePlayResp: non-OK code {_lp_code_sdes}"
|
|
7136
|
+
f"{' (not ready, transient)' if _lp_code_sdes == -50019 else ''}"
|
|
7137
|
+
" - proceeding")
|
|
7092
7138
|
except TimeoutError:
|
|
7093
7139
|
pass
|
|
7094
7140
|
_LOGGER.info(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-aidot-cameras
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.25
|
|
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
|
{python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/tests/test_sdes_fast_liveplay.py
RENAMED
|
@@ -38,6 +38,22 @@ def test_env_falsey_values(monkeypatch):
|
|
|
38
38
|
assert _cam()._resolve_sdes_fast_liveplay() is False, val
|
|
39
39
|
|
|
40
40
|
|
|
41
|
+
def test_role_reversal_model_always_excluded(monkeypatch):
|
|
42
|
+
# A001064 (role-reversal) must NOT use the flag even when opt/env force it on -
|
|
43
|
+
# the early webrtcReq degrades its media reliability.
|
|
44
|
+
from types import SimpleNamespace
|
|
45
|
+
monkeypatch.setenv("AIDOT_SDES_FAST_LIVEPLAY", "1")
|
|
46
|
+
cam = _cam()
|
|
47
|
+
cam.info = SimpleNamespace(model_id="LK.IPC.A001064")
|
|
48
|
+
cam._sdes_fast_liveplay_opt = True
|
|
49
|
+
assert cam._resolve_sdes_fast_liveplay() is False
|
|
50
|
+
# a non-excluded SDES model still honours the flag
|
|
51
|
+
cam2 = _cam()
|
|
52
|
+
cam2.info = SimpleNamespace(model_id="LK.IPC.A001513")
|
|
53
|
+
cam2._sdes_fast_liveplay_opt = True
|
|
54
|
+
assert cam2._resolve_sdes_fast_liveplay() is True
|
|
55
|
+
|
|
56
|
+
|
|
41
57
|
def test_kwarg_option_wins_over_env(monkeypatch):
|
|
42
58
|
monkeypatch.setenv("AIDOT_SDES_FAST_LIVEPLAY", "1")
|
|
43
59
|
cam = _cam()
|
|
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
|
|
File without changes
|
{python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/models/device_client_model.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/models/device_model.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/src/aidot/models/discover_model.py
RENAMED
|
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
|
{python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/tests/test_highport_nomination.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/tests/test_no_undefined_names.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.7.23 → python_aidot_cameras-0.7.25}/tests/test_post_merge_hardening.py
RENAMED
|
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
|