python-aidot-cameras 0.7.28__tar.gz → 0.7.29__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.28/src/python_aidot_cameras.egg-info → python_aidot_cameras-0.7.29}/PKG-INFO +1 -1
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/pyproject.toml +1 -1
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/camera/client.py +34 -10
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/camera/protocol.py +45 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29/src/python_aidot_cameras.egg-info}/PKG-INFO +1 -1
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/LICENSE +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/MANIFEST.in +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/README.md +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/setup.cfg +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/__init__.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/aes_utils.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/camera/__init__.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/camera/constants.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/camera/controls.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/camera/go2rtc.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/camera/lan_control.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/camera/models.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/camera/playback.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/camera/sdes.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/camera/tutk.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/camera/webrtc.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/client.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/const.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/credentials.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/device_client.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/discover.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/exceptions.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/g711.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/login_const.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/models/__init__.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/models/device_client_model.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/models/device_model.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/models/discover_model.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/py.typed +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/python_aidot_cameras.egg-info/SOURCES.txt +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/python_aidot_cameras.egg-info/dependency_links.txt +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/python_aidot_cameras.egg-info/requires.txt +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/python_aidot_cameras.egg-info/top_level.txt +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/tests/test_alarm_event.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/tests/test_backoff.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/tests/test_go2rtc.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/tests/test_highport_nomination.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/tests/test_lan_control.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/tests/test_live_stream_param.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/tests/test_motion_poll.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/tests/test_no_undefined_names.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/tests/test_persistent_mqtt.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/tests/test_post_merge_hardening.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/tests/test_sdes_adaptive.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/tests/test_sdes_fast_liveplay.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/tests/test_sdes_idle_release.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/tests/test_sdes_sprop.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/tests/test_sdes_talk.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/tests/test_sdes_watchdog.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/tests/test_serve_relay.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/tests/test_speak.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/tests/test_stream_cap.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/tests/test_stream_idle.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/tests/test_talk.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/tests/test_terminal_ack.py +0 -0
- {python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/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.29
|
|
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.29"
|
|
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"
|
|
@@ -4093,16 +4093,40 @@ class CameraMixin(_CameraControlsMixin):
|
|
|
4093
4093
|
)
|
|
4094
4094
|
)
|
|
4095
4095
|
|
|
4096
|
-
#
|
|
4097
|
-
#
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4096
|
+
# MQTT transport for the stream signaling. Default: a dedicated
|
|
4097
|
+
# connect-per-stream session in an executor (stopped via outgoing_q
|
|
4098
|
+
# sentinel on WebRTCSession.stop()). AIDOT_PERSISTENT_MQTT (Phase 2):
|
|
4099
|
+
# ride the SAME account-level persistent connection commands/attrs use
|
|
4100
|
+
# (the stream's mqtt_cid IS the authorized mqttClientId, so it's the same
|
|
4101
|
+
# connection) - subscribe + register a handler + drain outgoing_q through
|
|
4102
|
+
# it, and DON'T tear the connection down on stop (matching the app).
|
|
4103
|
+
_pm_stream = (await self._get_persistent_mqtt()
|
|
4104
|
+
if self._resolve_persistent_mqtt() else None)
|
|
4105
|
+
if _pm_stream is not None:
|
|
4106
|
+
await _pm_stream.subscribe(sub_topics)
|
|
4107
|
+
_pm_stream.add_handler(_on_mqtt_message)
|
|
4108
|
+
|
|
4109
|
+
async def _pm_stream_drain():
|
|
4110
|
+
try:
|
|
4111
|
+
while True:
|
|
4112
|
+
out = await loop.run_in_executor(None, outgoing_q.get)
|
|
4113
|
+
if out is None: # stop sentinel from WebRTCSession.stop()
|
|
4114
|
+
return
|
|
4115
|
+
await _pm_stream.publish(out[0], out[1])
|
|
4116
|
+
finally:
|
|
4117
|
+
_pm_stream.remove_handler(_on_mqtt_message)
|
|
4118
|
+
|
|
4119
|
+
mqtt_fut = asyncio.ensure_future(_pm_stream_drain())
|
|
4120
|
+
_on_mqtt_ready({"connected": True, "rc": 0, "rc_str": "persistent"})
|
|
4121
|
+
else:
|
|
4122
|
+
mqtt_fut = loop.run_in_executor(
|
|
4123
|
+
None,
|
|
4124
|
+
lambda: _mqtt_session_sync(
|
|
4125
|
+
mqtt_url, mqtt_user, mqtt_pwd, mqtt_cid,
|
|
4126
|
+
sub_topics, [], 3600.0, _on_mqtt_message,
|
|
4127
|
+
"/mqtt", _on_mqtt_ready, outgoing_q,
|
|
4128
|
+
),
|
|
4129
|
+
)
|
|
4106
4130
|
|
|
4107
4131
|
# Wait for MQTT to be connected and subscribed before proceeding.
|
|
4108
4132
|
# threading.Event.wait(timeout) returns True if set, False on timeout.
|
|
@@ -1270,6 +1270,7 @@ class _PersistentMqtt:
|
|
|
1270
1270
|
self._lock = threading.Lock()
|
|
1271
1271
|
self._subs = set() # topics to (re)subscribe on connect
|
|
1272
1272
|
self._collectors = [] # transient queues, each receives every msg
|
|
1273
|
+
self._handlers = [] # persistent on_message callbacks (e.g. a stream)
|
|
1273
1274
|
self._started = False
|
|
1274
1275
|
self.connects = 0 # observability: how many times we connected
|
|
1275
1276
|
|
|
@@ -1330,8 +1331,14 @@ class _PersistentMqtt:
|
|
|
1330
1331
|
item = (msg.topic, payload)
|
|
1331
1332
|
with self._lock:
|
|
1332
1333
|
cols = list(self._collectors)
|
|
1334
|
+
handlers = list(self._handlers)
|
|
1333
1335
|
for q in cols:
|
|
1334
1336
|
q.put(item)
|
|
1337
|
+
for h in handlers: # persistent subscribers (stream, etc.)
|
|
1338
|
+
try:
|
|
1339
|
+
h(msg.topic, payload)
|
|
1340
|
+
except Exception:
|
|
1341
|
+
_LOGGER.debug("persistent mqtt: handler raised", exc_info=True)
|
|
1335
1342
|
|
|
1336
1343
|
def _ensure_started_sync(self, timeout=15.0):
|
|
1337
1344
|
with self._lock:
|
|
@@ -1409,6 +1416,44 @@ class _PersistentMqtt:
|
|
|
1409
1416
|
return await loop.run_in_executor(
|
|
1410
1417
|
None, functools.partial(self._ensure_started_sync, timeout))
|
|
1411
1418
|
|
|
1419
|
+
# --- persistent subscriber API (for the stream signaling, Phase 2) -------- #
|
|
1420
|
+
def add_handler(self, callback):
|
|
1421
|
+
"""Register a persistent on_message callback ``callback(topic, payload)``
|
|
1422
|
+
that receives every message for the connection's lifetime (until removed).
|
|
1423
|
+
Use for a long-lived consumer like an open stream's signaling handler."""
|
|
1424
|
+
with self._lock:
|
|
1425
|
+
self._handlers.append(callback)
|
|
1426
|
+
return callback
|
|
1427
|
+
|
|
1428
|
+
def remove_handler(self, callback):
|
|
1429
|
+
with self._lock:
|
|
1430
|
+
try:
|
|
1431
|
+
self._handlers.remove(callback)
|
|
1432
|
+
except ValueError:
|
|
1433
|
+
pass
|
|
1434
|
+
|
|
1435
|
+
async def subscribe(self, topics):
|
|
1436
|
+
"""Ensure the connection is up and subscribe ``topics`` (tracked for replay)."""
|
|
1437
|
+
import functools
|
|
1438
|
+
loop = asyncio.get_running_loop()
|
|
1439
|
+
await loop.run_in_executor(None, self._ensure_started_sync, 15.0)
|
|
1440
|
+
await loop.run_in_executor(None, functools.partial(self._subscribe_sync, topics))
|
|
1441
|
+
|
|
1442
|
+
async def publish(self, topic, payload):
|
|
1443
|
+
"""Publish on the shared connection (ensures it's up first)."""
|
|
1444
|
+
import functools
|
|
1445
|
+
|
|
1446
|
+
def _pub():
|
|
1447
|
+
if not self._ensure_started_sync():
|
|
1448
|
+
return False
|
|
1449
|
+
try:
|
|
1450
|
+
self._client.publish(topic, payload)
|
|
1451
|
+
return True
|
|
1452
|
+
except Exception:
|
|
1453
|
+
return False
|
|
1454
|
+
loop = asyncio.get_running_loop()
|
|
1455
|
+
return await loop.run_in_executor(None, functools.partial(_pub))
|
|
1456
|
+
|
|
1412
1457
|
def close(self):
|
|
1413
1458
|
c = self._client
|
|
1414
1459
|
self._client = None
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-aidot-cameras
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.29
|
|
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
|
{python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/models/device_client_model.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/src/aidot/models/device_model.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/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.28 → python_aidot_cameras-0.7.29}/tests/test_highport_nomination.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/tests/test_no_undefined_names.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/tests/test_post_merge_hardening.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.7.28 → python_aidot_cameras-0.7.29}/tests/test_sdes_fast_liveplay.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
|