python-aidot-cameras 0.17.2__tar.gz → 0.17.3b2__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.
Files changed (205) hide show
  1. {python_aidot_cameras-0.17.2/python_aidot_cameras.egg-info → python_aidot_cameras-0.17.3b2}/PKG-INFO +35 -1
  2. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/README.md +34 -0
  3. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/client.py +24 -3
  4. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/lan_control.py +26 -2
  5. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/sdes_open.py +424 -128
  6. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/webrtc_open.py +51 -16
  7. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/pyproject.toml +1 -1
  8. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2/python_aidot_cameras.egg-info}/PKG-INFO +35 -1
  9. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/python_aidot_cameras.egg-info/SOURCES.txt +8 -1
  10. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_futile_keepalive.py +35 -4
  11. python_aidot_cameras-0.17.3b2/tests/test_key_restart_sdp.py +195 -0
  12. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_lan_control.py +61 -0
  13. python_aidot_cameras-0.17.3b2/tests/test_media_counted_only_when_plaintext.py +237 -0
  14. python_aidot_cameras-0.17.3b2/tests/test_no_key_material_in_logs.py +140 -0
  15. python_aidot_cameras-0.17.3b2/tests/test_sctp_init_ack_tsn.py +94 -0
  16. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_bridge_break_gate.py +74 -10
  17. python_aidot_cameras-0.17.3b2/tests/test_snapshot_cancellation.py +105 -0
  18. python_aidot_cameras-0.17.3b2/tests/test_srtp_rx_rekey.py +183 -0
  19. python_aidot_cameras-0.17.3b2/tests/test_srtp_tx_key_note.py +101 -0
  20. python_aidot_cameras-0.17.3b2/tests/test_webrtc_answer_race.py +101 -0
  21. python_aidot_cameras-0.17.2/tests/test_key_restart_sdp.py +0 -61
  22. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/LICENSE +0 -0
  23. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/MANIFEST.in +0 -0
  24. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/__init__.py +0 -0
  25. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/__main__.py +0 -0
  26. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_upstream.py +0 -0
  27. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/__init__.py +0 -0
  28. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/__init__.py +0 -0
  29. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/clock.py +0 -0
  30. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/codecs/__init__.py +0 -0
  31. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/codecs/base.py +0 -0
  32. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/codecs/g711.py +0 -0
  33. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/codecs/g722.py +0 -0
  34. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/codecs/h264.py +0 -0
  35. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/codecs/opus.py +0 -0
  36. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/codecs/vpx.py +0 -0
  37. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/contrib/__init__.py +0 -0
  38. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/contrib/media.py +0 -0
  39. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/contrib/signaling.py +0 -0
  40. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/events.py +0 -0
  41. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/exceptions.py +0 -0
  42. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/jitterbuffer.py +0 -0
  43. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/mediastreams.py +0 -0
  44. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rate.py +0 -0
  45. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rtcconfiguration.py +0 -0
  46. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rtcdatachannel.py +0 -0
  47. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rtcdtlstransport.py +0 -0
  48. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rtcicetransport.py +0 -0
  49. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rtcpeerconnection.py +0 -0
  50. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rtcrtpparameters.py +0 -0
  51. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rtcrtpreceiver.py +0 -0
  52. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rtcrtpsender.py +0 -0
  53. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rtcrtptransceiver.py +0 -0
  54. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rtcsctptransport.py +0 -0
  55. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rtcsessiondescription.py +0 -0
  56. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rtp.py +0 -0
  57. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/sdp.py +0 -0
  58. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/stats.py +0 -0
  59. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/utils.py +0 -0
  60. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/ble_gateway.py +0 -0
  61. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/__init__.py +0 -0
  62. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/constants.py +0 -0
  63. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/controls.py +0 -0
  64. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/go2rtc.py +0 -0
  65. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/hwaccel.py +0 -0
  66. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/models.py +0 -0
  67. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/playback.py +0 -0
  68. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/protocol.py +0 -0
  69. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/sdes.py +0 -0
  70. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/tutk.py +0 -0
  71. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/webrtc.py +0 -0
  72. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/client.py +0 -0
  73. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/const.py +0 -0
  74. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/credentials.py +0 -0
  75. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/crypto.py +0 -0
  76. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/device_client.py +0 -0
  77. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/discover.py +0 -0
  78. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/exceptions.py +0 -0
  79. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/g711.py +0 -0
  80. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/lan_retry.py +0 -0
  81. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/py.typed +0 -0
  82. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/python_aidot_cameras.egg-info/dependency_links.txt +0 -0
  83. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/python_aidot_cameras.egg-info/entry_points.txt +0 -0
  84. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/python_aidot_cameras.egg-info/requires.txt +0 -0
  85. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/python_aidot_cameras.egg-info/top_level.txt +0 -0
  86. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/setup.cfg +0 -0
  87. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_aioice_compat.py +0 -0
  88. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_alarm_event.py +0 -0
  89. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_answer_sdp_video_pt.py +0 -0
  90. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_audio_mux_offline.py +0 -0
  91. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_avio_request.py +0 -0
  92. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_avio_response_parse.py +0 -0
  93. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_avio_response_router.py +0 -0
  94. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_backoff.py +0 -0
  95. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_battery_detection.py +0 -0
  96. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_battery_evidence_corroboration.py +0 -0
  97. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_battery_skip_turn.py +0 -0
  98. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_ble_gateway.py +0 -0
  99. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_bridge_state_sentinels.py +0 -0
  100. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_busy_backoff.py +0 -0
  101. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_constants_imports_resolve.py +0 -0
  102. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_control_verdicts.py +0 -0
  103. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_deferred_hardening.py +0 -0
  104. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_device_cmd_seq_match.py +0 -0
  105. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_device_login_guard.py +0 -0
  106. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_device_user_info_cache.py +0 -0
  107. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_dormant_camera_cleanup.py +0 -0
  108. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_dtls_mux_terminal_write.py +0 -0
  109. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_dtls_not_ready_burst.py +0 -0
  110. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_dtls_pinning.py +0 -0
  111. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_dtls_serve_decode_skip.py +0 -0
  112. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_dtls_serve_open_timeout.py +0 -0
  113. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_dtls_skip_signaling_wait.py +0 -0
  114. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_dtls_slow_probe.py +0 -0
  115. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_dtls_stdout_producer.py +0 -0
  116. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_egress_guard.py +0 -0
  117. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_env_seams.py +0 -0
  118. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_go2rtc.py +0 -0
  119. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_go2rtc_cli.py +0 -0
  120. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_h264_decode_rate_limit.py +0 -0
  121. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_highport_nomination.py +0 -0
  122. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_hwaccel_probe.py +0 -0
  123. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_ice_config_cache.py +0 -0
  124. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_ice_peer_reflexive.py +0 -0
  125. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_idle_release_both_loops.py +0 -0
  126. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_keepalive_renew.py +0 -0
  127. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_keyframe_prompter.py +0 -0
  128. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_lan_retry_reaches_the_storming_devices.py +0 -0
  129. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_light_active_color_mode.py +0 -0
  130. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_live_stream_param.py +0 -0
  131. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_live_validate_policy.py +0 -0
  132. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_logging_caps.py +0 -0
  133. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_login_connect_deadline.py +0 -0
  134. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_login_reconnect_backoff.py +0 -0
  135. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_media_socket_rcvbuf.py +0 -0
  136. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_media_wait_floor.py +0 -0
  137. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_motion_poll.py +0 -0
  138. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_mqtt_credential_refetch.py +0 -0
  139. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_mqtt_credential_self_heal.py +0 -0
  140. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_mqtt_password_not_persisted.py +0 -0
  141. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_mqtt_session_reconnect.py +0 -0
  142. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_narrow_pc_ice.py +0 -0
  143. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_no_self_referential_go2rtc.py +0 -0
  144. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_no_undefined_names.py +0 -0
  145. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_offer_keeps_rtcp_feedback.py +0 -0
  146. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_offline_keepalive_pause.py +0 -0
  147. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_open_fail_logger.py +0 -0
  148. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_open_gate_delay.py +0 -0
  149. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_peerid_reuse.py +0 -0
  150. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_persistent_mqtt.py +0 -0
  151. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_persistent_mqtt_robustness.py +0 -0
  152. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_playback_tls.py +0 -0
  153. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_post_merge_hardening.py +0 -0
  154. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_remb.py +0 -0
  155. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_resolution_persists.py +0 -0
  156. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_restart_cycle.py +0 -0
  157. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_retry_policy.py +0 -0
  158. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_scripts_importable.py +0 -0
  159. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_adaptive.py +0 -0
  160. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_avio_dispatch.py +0 -0
  161. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_echo_wait_timeout.py +0 -0
  162. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_fast_liveplay.py +0 -0
  163. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_ffmpeg_stderr_log.py +0 -0
  164. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_idle_release.py +0 -0
  165. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_offer_video_pin.py +0 -0
  166. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_open_cleanup.py +0 -0
  167. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_sctp_abort.py +0 -0
  168. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_serve_audio.py +0 -0
  169. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_serve_cmd.py +0 -0
  170. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_serve_stderr_drain.py +0 -0
  171. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_sprop.py +0 -0
  172. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_talk.py +0 -0
  173. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_teardown_exit_log.py +0 -0
  174. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_terminal_ack.py +0 -0
  175. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_terminal_ack_latency.py +0 -0
  176. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_watchdog.py +0 -0
  177. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdp_narrowing_properties.py +0 -0
  178. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdp_payload_type_narrowing.py +0 -0
  179. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_security_hardening.py +0 -0
  180. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_serializable_login_info.py +0 -0
  181. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_serve_loss_tolerance.py +0 -0
  182. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_serve_relay.py +0 -0
  183. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_session_stats.py +0 -0
  184. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_shared_house_seam.py +0 -0
  185. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_speak.py +0 -0
  186. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sprop_capture_gate.py +0 -0
  187. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sprop_staleness.py +0 -0
  188. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_stream_cap.py +0 -0
  189. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_stream_drain_ownership.py +0 -0
  190. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_stream_idle.py +0 -0
  191. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_stream_slot_cap.py +0 -0
  192. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_stream_teardown.py +0 -0
  193. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_talk.py +0 -0
  194. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_terminal_ack.py +0 -0
  195. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_token_file_robustness.py +0 -0
  196. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_token_refresh.py +0 -0
  197. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_turn_create_permission.py +0 -0
  198. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_upstream_compat.py +0 -0
  199. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_video_profile_log.py +0 -0
  200. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_viewer_check_throttle.py +0 -0
  201. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_viewer_presence.py +0 -0
  202. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_viewer_present_push_mode.py +0 -0
  203. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_wait_or_event.py +0 -0
  204. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_wake_gate_camera_evidence.py +0 -0
  205. {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_wake_readiness_retry.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-aidot-cameras
3
- Version: 0.17.2
3
+ Version: 0.17.3b2
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
@@ -66,6 +66,40 @@ Other battery models (A001108, A001360) are recognized in code with the same
66
66
  battery handling. See [`docs/CAMERAS.md`](docs/CAMERAS.md#supported-cameras) for
67
67
  the authoritative table and per-model notes.
68
68
 
69
+ ## Local (LAN) control and account ownership
70
+
71
+ Local control over the devices' TCP:10000 channel - both the light/plug protocol
72
+ and this package's `CameraLanClient` - is accepted **only for the account that
73
+ owns the devices**. A member of a shared home is rejected at the device, even
74
+ though the cloud happily hands that member a full device list, complete with the
75
+ per-device `password` and `aesKey`.
76
+
77
+ The rejection is easy to misread, because the device answers with a code that
78
+ varies by firmware family and a message that blames the wrong thing:
79
+
80
+ | Model | Ack |
81
+ | --- | --- |
82
+ | `LK.light.A001493` | 400 `not equal abort user id or password` |
83
+ | `LK.light.A001497`, `LK.plug.A001535` | 4354 `fail` |
84
+ | `LK.IPC.A000088` | 4352 `fail` |
85
+
86
+ The A001493's message is the honest one: it is the **user id** that does not
87
+ match, not the password. Every credential in the request can be correct and the
88
+ login will still be refused if the `userId` is not the owner's.
89
+
90
+ Verified 2026-08-08 against ten devices across four model families: every one
91
+ returns 200 for the owning account and one of the codes above for a shared-home
92
+ member.
93
+
94
+ **If local control never engages, check which account the integration is signed
95
+ in as before looking anywhere else.** A dedicated or secondary login - the sort
96
+ of thing you might use to avoid contending with a phone app over a rotating
97
+ refresh token - will control everything fine through the cloud and never once
98
+ log in locally.
99
+
100
+ Cameras' live video is unaffected either way: it is WebRTC signaled over cloud
101
+ MQTT and does not use this path.
102
+
69
103
  ## Library install
70
104
 
71
105
  Install from PyPI (the simplest, recommended method):
@@ -35,6 +35,40 @@ Other battery models (A001108, A001360) are recognized in code with the same
35
35
  battery handling. See [`docs/CAMERAS.md`](docs/CAMERAS.md#supported-cameras) for
36
36
  the authoritative table and per-model notes.
37
37
 
38
+ ## Local (LAN) control and account ownership
39
+
40
+ Local control over the devices' TCP:10000 channel - both the light/plug protocol
41
+ and this package's `CameraLanClient` - is accepted **only for the account that
42
+ owns the devices**. A member of a shared home is rejected at the device, even
43
+ though the cloud happily hands that member a full device list, complete with the
44
+ per-device `password` and `aesKey`.
45
+
46
+ The rejection is easy to misread, because the device answers with a code that
47
+ varies by firmware family and a message that blames the wrong thing:
48
+
49
+ | Model | Ack |
50
+ | --- | --- |
51
+ | `LK.light.A001493` | 400 `not equal abort user id or password` |
52
+ | `LK.light.A001497`, `LK.plug.A001535` | 4354 `fail` |
53
+ | `LK.IPC.A000088` | 4352 `fail` |
54
+
55
+ The A001493's message is the honest one: it is the **user id** that does not
56
+ match, not the password. Every credential in the request can be correct and the
57
+ login will still be refused if the `userId` is not the owner's.
58
+
59
+ Verified 2026-08-08 against ten devices across four model families: every one
60
+ returns 200 for the owning account and one of the codes above for a shared-home
61
+ member.
62
+
63
+ **If local control never engages, check which account the integration is signed
64
+ in as before looking anywhere else.** A dedicated or secondary login - the sort
65
+ of thing you might use to avoid contending with a phone app over a rotating
66
+ refresh token - will control everything fine through the cloud and never once
67
+ log in locally.
68
+
69
+ Cameras' live video is unaffected either way: it is WebRTC signaled over cloud
70
+ MQTT and does not use this path.
71
+
38
72
  ## Library install
39
73
 
40
74
  Install from PyPI (the simplest, recommended method):
@@ -398,6 +398,21 @@ _BUSY_BACKOFF_S = float(os.environ.get("AIDOT_BUSY_BACKOFF_S", "20"))
398
398
  _FUTILE_KEEPALIVE_LIMIT = int(os.environ.get("AIDOT_FUTILE_KEEPALIVE_LIMIT", "5"))
399
399
 
400
400
 
401
+ def _next_no_media_streak(no_media_streak: int, healthy: bool) -> int:
402
+ """The streak after a keepalive session that was (or was not) healthy.
403
+
404
+ The ceiling counts CONSECUTIVE failures, so a session that delivered media
405
+ resets it to zero. Without that reset the count is cumulative and a camera
406
+ that merely fails occasionally - five scattered no-media sessions spread
407
+ across a day, each one separated by sessions that worked - eventually trips
408
+ a limit meant for cameras that never deliver anything at all, and has its
409
+ background keepalive stopped for good.
410
+ """
411
+ if healthy:
412
+ return 0
413
+ return no_media_streak + 1
414
+
415
+
401
416
  def _should_abandon_keepalive(no_media_streak: int, *, is_battery: bool,
402
417
  limit: int = _FUTILE_KEEPALIVE_LIMIT) -> bool:
403
418
  """Should the background keepalive stop reopening this camera?
@@ -2622,7 +2637,14 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
2622
2637
  _session.wait_done(),
2623
2638
  timeout=_sdes_snap_seconds + 80,
2624
2639
  )
2625
- except (TimeoutError, _asyncio.CancelledError):
2640
+ except _asyncio.CancelledError:
2641
+ # Not the same event as a timeout. A timeout means the
2642
+ # camera was slow and whatever landed in the temp file is
2643
+ # worth salvaging; a cancellation means the caller is gone,
2644
+ # so salvaging it (another ffmpeg, up to 15s) is work nobody
2645
+ # is waiting for and it stops shutdown from completing.
2646
+ raise
2647
+ except TimeoutError:
2626
2648
  pass
2627
2649
  finally:
2628
2650
  await _session.stop()
@@ -3599,6 +3621,7 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
3599
3621
  # Adaptive bookkeeping: a fast attempt that never delivered media
3600
3622
  # latches the loop onto the full relay path for its remaining opens.
3601
3623
  _healthy = session.last_media_monotonic > 0.0
3624
+ _no_media_streak = _next_no_media_streak(_no_media_streak, _healthy)
3602
3625
  if _healthy:
3603
3626
  # A session that delivered media is a real, cleanly-used session;
3604
3627
  # the next open should be a fresh one rather than re-offering on a
@@ -3606,9 +3629,7 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
3606
3629
  _loop_peer_id = self.generate_webrtc_peer_id(
3607
3630
  live_type=2, stream_id=0, sdes=True)
3608
3631
  _peer_reuses = 0
3609
- _no_media_streak = 0
3610
3632
  else:
3611
- _no_media_streak += 1
3612
3633
  if _should_abandon_keepalive(
3613
3634
  _no_media_streak, is_battery=self.is_battery_camera):
3614
3635
  # Every open so far has cost the camera a wake and returned
@@ -164,6 +164,26 @@ class CameraLanError(Exception):
164
164
  """A local control operation failed (connect, login, or protocol error)."""
165
165
 
166
166
 
167
+ class CameraLanLoginRejected(CameraLanError):
168
+ """The device advertised local control and then refused our login.
169
+
170
+ Separate from its parent because the two mean opposite things to a caller.
171
+ A device that does not offer local control is ordinary and should be quiet;
172
+ a device that offers it, accepts the connection, decrypts our request with
173
+ its own key, and STILL answers with a non-200 ack is a defect - either in
174
+ what we send or in the device - and the caller should be able to say so out
175
+ loud rather than logging it at the same level as "not supported here".
176
+
177
+ ``ack`` is the device's own code, because the refusals differ by family and
178
+ the number is the only handle on which one happened: A000088 cameras answer
179
+ 4352, A001497 lights and A001535 plugs 4354, A001493 lights 400.
180
+ """
181
+
182
+ def __init__(self, message: str, ack: Any = None) -> None:
183
+ super().__init__(message)
184
+ self.ack = ack
185
+
186
+
167
187
  class CameraLanClient:
168
188
  """Short-lived local device-control client for one camera.
169
189
 
@@ -291,9 +311,13 @@ class CameraLanClient:
291
311
  ) from exc
292
312
  if ack != 200:
293
313
  # The real device rejected our key-authenticated login; stop using
294
- # the LAN path and revert to cloud.
314
+ # the LAN path and revert to cloud. Raised as the specific type so
315
+ # a caller can tell this apart from a device that never offered
316
+ # local control - see CameraLanLoginRejected.
295
317
  self._eligible = False
296
- raise CameraLanError(f"{self.device_id}: login rejected ack={ack}")
318
+ raise CameraLanLoginRejected(
319
+ f"{self.device_id}: login rejected ack={ack}", ack=ack
320
+ )
297
321
  return (resp.get("payload") or {}).get("ascNumber", 1)
298
322
 
299
323
  # -- public control ---------------------------------------------------- #