python-aidot-cameras 1.0.0rc15__tar.gz → 1.0.0rc17__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 (298) hide show
  1. {python_aidot_cameras-1.0.0rc15/python_aidot_cameras.egg-info → python_aidot_cameras-1.0.0rc17}/PKG-INFO +1 -1
  2. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/controls.py +74 -1
  3. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/models.py +78 -1
  4. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/pyproject.toml +1 -1
  5. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17/python_aidot_cameras.egg-info}/PKG-INFO +1 -1
  6. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/python_aidot_cameras.egg-info/SOURCES.txt +2 -0
  7. python_aidot_cameras-1.0.0rc17/tests/test_auto_light_settings.py +143 -0
  8. python_aidot_cameras-1.0.0rc17/tests/test_declared_properties.py +85 -0
  9. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/LICENSE +0 -0
  10. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/MANIFEST.in +0 -0
  11. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/README.md +0 -0
  12. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/__init__.py +0 -0
  13. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/__main__.py +0 -0
  14. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_upstream.py +0 -0
  15. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/__init__.py +0 -0
  16. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/LICENSE +0 -0
  17. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/__init__.py +0 -0
  18. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/clock.py +0 -0
  19. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/codecs/__init__.py +0 -0
  20. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/codecs/base.py +0 -0
  21. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/codecs/g711.py +0 -0
  22. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/codecs/g722.py +0 -0
  23. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/codecs/h264.py +0 -0
  24. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/codecs/opus.py +0 -0
  25. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/codecs/vpx.py +0 -0
  26. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/contrib/__init__.py +0 -0
  27. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/contrib/media.py +0 -0
  28. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/contrib/signaling.py +0 -0
  29. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/events.py +0 -0
  30. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/exceptions.py +0 -0
  31. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/jitterbuffer.py +0 -0
  32. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/mediastreams.py +0 -0
  33. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rate.py +0 -0
  34. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rtcconfiguration.py +0 -0
  35. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rtcdatachannel.py +0 -0
  36. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rtcdtlstransport.py +0 -0
  37. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rtcicetransport.py +0 -0
  38. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rtcpeerconnection.py +0 -0
  39. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rtcrtpparameters.py +0 -0
  40. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rtcrtpreceiver.py +0 -0
  41. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rtcrtpsender.py +0 -0
  42. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rtcrtptransceiver.py +0 -0
  43. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rtcsctptransport.py +0 -0
  44. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rtcsessiondescription.py +0 -0
  45. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rtp.py +0 -0
  46. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/sdp.py +0 -0
  47. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/stats.py +0 -0
  48. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/utils.py +0 -0
  49. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/ble_gateway.py +0 -0
  50. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/__init__.py +0 -0
  51. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/client.py +0 -0
  52. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/constants.py +0 -0
  53. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/go2rtc.py +0 -0
  54. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/hwaccel.py +0 -0
  55. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/lan_control.py +0 -0
  56. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/playback.py +0 -0
  57. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/protocol.py +0 -0
  58. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/sd_events.py +0 -0
  59. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/sd_listing.py +0 -0
  60. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/sd_playback.py +0 -0
  61. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/sdes.py +0 -0
  62. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/sdes_open.py +0 -0
  63. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/tutk.py +0 -0
  64. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/webrtc.py +0 -0
  65. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/webrtc_open.py +0 -0
  66. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/client.py +0 -0
  67. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/cloud_auth.py +0 -0
  68. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/const.py +0 -0
  69. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/credentials.py +0 -0
  70. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/crypto.py +0 -0
  71. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/device_client.py +0 -0
  72. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/discover.py +0 -0
  73. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/exceptions.py +0 -0
  74. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/g711.py +0 -0
  75. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/lan_retry.py +0 -0
  76. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/py.typed +0 -0
  77. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/python_aidot_cameras.egg-info/dependency_links.txt +0 -0
  78. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/python_aidot_cameras.egg-info/entry_points.txt +0 -0
  79. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/python_aidot_cameras.egg-info/requires.txt +0 -0
  80. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/python_aidot_cameras.egg-info/top_level.txt +0 -0
  81. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/setup.cfg +0 -0
  82. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_abandoned_attempt_skips_the_doomed_serve.py +0 -0
  83. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_abandoned_wait_still_maps_audio.py +0 -0
  84. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_aioice_compat.py +0 -0
  85. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_alarm_event.py +0 -0
  86. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_answer_pt_kinds.py +0 -0
  87. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_answer_sdp_video_pt.py +0 -0
  88. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_answer_section_selection.py +0 -0
  89. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_audio_mux_offline.py +0 -0
  90. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_auto_alarm.py +0 -0
  91. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_avio_ctrl_uses_the_session_dseq.py +0 -0
  92. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_avio_request.py +0 -0
  93. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_avio_response_parse.py +0 -0
  94. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_avio_response_router.py +0 -0
  95. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_backoff.py +0 -0
  96. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_battery_detection.py +0 -0
  97. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_battery_evidence_corroboration.py +0 -0
  98. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_battery_skip_turn.py +0 -0
  99. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_battery_stale_offer_is_abandoned_early.py +0 -0
  100. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_battery_wake_gate_app_parity.py +0 -0
  101. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_bitrate_campaign_arms.py +0 -0
  102. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_ble_gateway.py +0 -0
  103. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_bridge_state_sentinels.py +0 -0
  104. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_busy_backoff.py +0 -0
  105. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_camera_properties.py +0 -0
  106. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_canary_log_names_the_camera.py +0 -0
  107. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_cloud_count.py +0 -0
  108. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_cloud_plan.py +0 -0
  109. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_constants_imports_resolve.py +0 -0
  110. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_control_verdicts.py +0 -0
  111. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_deferred_hardening.py +0 -0
  112. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_detection_types.py +0 -0
  113. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_device_cmd_seq_match.py +0 -0
  114. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_device_login_guard.py +0 -0
  115. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_device_user_info_cache.py +0 -0
  116. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_direct_ts_keyframe_signal.py +0 -0
  117. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_direct_ts_serve.py +0 -0
  118. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_dormant_camera_cleanup.py +0 -0
  119. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_drain_is_cancellable.py +0 -0
  120. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_dtls_mux_terminal_write.py +0 -0
  121. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_dtls_not_ready_burst.py +0 -0
  122. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_dtls_pinning.py +0 -0
  123. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_dtls_resolution_ack.py +0 -0
  124. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_dtls_serve_decode_skip.py +0 -0
  125. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_dtls_serve_ice_wait.py +0 -0
  126. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_dtls_serve_open_timeout.py +0 -0
  127. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_dtls_skip_signaling_wait.py +0 -0
  128. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_dtls_slow_probe.py +0 -0
  129. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_dtls_stdout_producer.py +0 -0
  130. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_dtls_video_presence.py +0 -0
  131. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_egress_guard.py +0 -0
  132. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_env_seams.py +0 -0
  133. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_feature_probe_needs_a_live_session.py +0 -0
  134. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_ffmpeg_signal_exit_is_not_a_crash.py +0 -0
  135. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_futile_keepalive.py +0 -0
  136. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_gate_requires_a_decoded_frame.py +0 -0
  137. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_go2rtc.py +0 -0
  138. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_go2rtc_cli.py +0 -0
  139. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_h264_decode_rate_limit.py +0 -0
  140. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_highport_nomination.py +0 -0
  141. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_hwaccel_probe.py +0 -0
  142. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_ice_config_cache.py +0 -0
  143. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_ice_credentials_match_the_app.py +0 -0
  144. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_ice_peer_reflexive.py +0 -0
  145. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_idle_release_both_loops.py +0 -0
  146. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_keepalive_renew.py +0 -0
  147. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_key_restart_sdp.py +0 -0
  148. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_keyframe_prompter.py +0 -0
  149. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_lan_control.py +0 -0
  150. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_lan_retry_reaches_the_storming_devices.py +0 -0
  151. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_late_repeats_are_dropped.py +0 -0
  152. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_light_active_color_mode.py +0 -0
  153. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_live_session_carries_talk.py +0 -0
  154. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_live_stream_param.py +0 -0
  155. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_live_validate_policy.py +0 -0
  156. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_live_video_canary.py +0 -0
  157. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_liveplay_echo_timeout.py +0 -0
  158. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_local_only_controls.py +0 -0
  159. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_logging_caps.py +0 -0
  160. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_login_connect_deadline.py +0 -0
  161. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_login_reconnect_backoff.py +0 -0
  162. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_media_counted_only_when_plaintext.py +0 -0
  163. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_media_path_is_reported.py +0 -0
  164. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_media_socket_rcvbuf.py +0 -0
  165. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_media_wait_floor.py +0 -0
  166. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_motion_poll.py +0 -0
  167. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_mqtt_client_id_is_exact.py +0 -0
  168. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_mqtt_credential_refetch.py +0 -0
  169. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_mqtt_credential_self_heal.py +0 -0
  170. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_mqtt_password_not_persisted.py +0 -0
  171. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_mqtt_session_reconnect.py +0 -0
  172. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_mqtt_thread_not_orphaned.py +0 -0
  173. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_nack.py +0 -0
  174. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_nack_cannot_add_load_in_a_burst.py +0 -0
  175. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_narrow_pc_ice.py +0 -0
  176. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_no_key_material_in_logs.py +0 -0
  177. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_no_self_referential_go2rtc.py +0 -0
  178. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_no_unbounded_hex_dumps.py +0 -0
  179. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_no_undefined_names.py +0 -0
  180. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_nominates_as_soon_as_the_answer_lands.py +0 -0
  181. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_off_subnet_candidate.py +0 -0
  182. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_offer_bandwidth_line.py +0 -0
  183. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_offer_keeps_rtcp_feedback.py +0 -0
  184. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_offline_keepalive_pause.py +0 -0
  185. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_on_frame_is_an_av_frame.py +0 -0
  186. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_open_fail_logger.py +0 -0
  187. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_open_gate_delay.py +0 -0
  188. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_peer_id_class_env.py +0 -0
  189. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_peer_id_client_type.py +0 -0
  190. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_peerid_reuse.py +0 -0
  191. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_persistent_mqtt.py +0 -0
  192. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_persistent_mqtt_robustness.py +0 -0
  193. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_playback_tls.py +0 -0
  194. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_post_merge_hardening.py +0 -0
  195. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_ptz_probe_reads_what_it_is_told.py +0 -0
  196. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_public_signature_matches_impl.py +0 -0
  197. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_publish_target_readiness.py +0 -0
  198. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_quality_campaign_in_session.py +0 -0
  199. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_reboot_action.py +0 -0
  200. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_recent_recordings.py +0 -0
  201. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_remb.py +0 -0
  202. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_remb_goes_out_the_relay_aware_sender.py +0 -0
  203. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_resent_frames_are_dropped.py +0 -0
  204. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_resolution_persists.py +0 -0
  205. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_restart_cycle.py +0 -0
  206. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_retry_policy.py +0 -0
  207. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_scripts_importable.py +0 -0
  208. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sctp_init_ack_tsn.py +0 -0
  209. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sctp_reassembly.py +0 -0
  210. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sctp_sack.py +0 -0
  211. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sd_card_presence.py +0 -0
  212. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sd_event_decode.py +0 -0
  213. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sd_event_probe_payloads.py +0 -0
  214. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sd_listing.py +0 -0
  215. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sd_playback.py +0 -0
  216. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_adaptive.py +0 -0
  217. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_avio_dispatch.py +0 -0
  218. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_bridge_break_gate.py +0 -0
  219. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_connection_mode.py +0 -0
  220. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_echo_wait_is_not_dead_time.py +0 -0
  221. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_fast_liveplay.py +0 -0
  222. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_ffmpeg_stderr_log.py +0 -0
  223. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_first_media_stall_report.py +0 -0
  224. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_ice_teardown.py +0 -0
  225. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_idle_release.py +0 -0
  226. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_nack_bridge.py +0 -0
  227. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_offer_video_order.py +0 -0
  228. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_offer_video_pin.py +0 -0
  229. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_open_cleanup.py +0 -0
  230. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_sctp_abort.py +0 -0
  231. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_serve_audio.py +0 -0
  232. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_serve_cmd.py +0 -0
  233. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_serve_stderr_drain.py +0 -0
  234. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_session_cap.py +0 -0
  235. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_sprop.py +0 -0
  236. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_talk.py +0 -0
  237. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_teardown_exit_log.py +0 -0
  238. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_terminal_ack.py +0 -0
  239. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_terminal_ack_latency.py +0 -0
  240. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_tmmbr_bridge.py +0 -0
  241. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_watchdog.py +0 -0
  242. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdp_narrowing_properties.py +0 -0
  243. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdp_payload_type_narrowing.py +0 -0
  244. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_security_hardening.py +0 -0
  245. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_self_peer_is_an_address_not_an_ip.py +0 -0
  246. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_serializable_login_info.py +0 -0
  247. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_serve_input_timeout.py +0 -0
  248. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_serve_loss_tolerance.py +0 -0
  249. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_serve_pt_respects_the_pin.py +0 -0
  250. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_serve_relay.py +0 -0
  251. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_serve_stderr_keeps_the_reason.py +0 -0
  252. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_session_stats.py +0 -0
  253. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_shared_house_seam.py +0 -0
  254. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_signalling_is_attributable_to_a_camera.py +0 -0
  255. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_snapshot_cancellation.py +0 -0
  256. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_speak.py +0 -0
  257. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sprop_capture_gate.py +0 -0
  258. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sprop_staleness.py +0 -0
  259. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sprop_unstable_public.py +0 -0
  260. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sprop_write_is_reported_truthfully.py +0 -0
  261. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_srtp_rx_rekey.py +0 -0
  262. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_srtp_tx_key_note.py +0 -0
  263. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_stable_terminal_id.py +0 -0
  264. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_stall_nudge_policy.py +0 -0
  265. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_stall_report_says_what_the_answer_carried.py +0 -0
  266. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_stall_report_says_whether_the_trigger_was_answered.py +0 -0
  267. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_stall_report_separates_silence_from_undecryptable.py +0 -0
  268. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_stall_report_survives_a_caller_giving_up.py +0 -0
  269. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_stall_reports_reach_the_artifact.py +0 -0
  270. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_status_and_trace_channels.py +0 -0
  271. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_stream_cap.py +0 -0
  272. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_stream_drain_ownership.py +0 -0
  273. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_stream_idle.py +0 -0
  274. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_stream_slot_cap.py +0 -0
  275. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_stream_teardown.py +0 -0
  276. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_talk.py +0 -0
  277. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_talk_reports_a_speaker_that_never_opened.py +0 -0
  278. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_tap_to_mux_end_to_end.py +0 -0
  279. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_terminal_ack.py +0 -0
  280. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_tmmbr.py +0 -0
  281. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_token_file_robustness.py +0 -0
  282. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_token_refresh.py +0 -0
  283. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_transport_failure_reporting.py +0 -0
  284. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_turn_create_permission.py +0 -0
  285. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_tutk_path_is_out_of_reach.py +0 -0
  286. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_unbuildable_summary_repeats_at_debug.py +0 -0
  287. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_upstream_compat.py +0 -0
  288. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_validator_shares_one_token.py +0 -0
  289. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_video_profile_log.py +0 -0
  290. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_video_pts_dts.py +0 -0
  291. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_video_ts_unwrap.py +0 -0
  292. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_viewer_check_throttle.py +0 -0
  293. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_viewer_presence.py +0 -0
  294. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_viewer_present_push_mode.py +0 -0
  295. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_wait_or_event.py +0 -0
  296. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_wake_gate_camera_evidence.py +0 -0
  297. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_wake_readiness_retry.py +0 -0
  298. {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_webrtc_answer_race.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-aidot-cameras
3
- Version: 1.0.0rc15
3
+ Version: 1.0.0rc17
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
@@ -15,6 +15,24 @@ from .constants import _PTZ_DIR_CODES, _STREAM_QUALITY, SETSTREAMCTRL_CMD
15
15
 
16
16
  _LOGGER = logging.getLogger(__name__)
17
17
 
18
+ #: How the floodlight behaves when the camera sees someone, by the name the
19
+ #: vendor app prints against each value. From the cloud device profile
20
+ #: (LK.IPC.A001513): ``lightBehavior`` is an enum with allowedValues
21
+ #: ``Constant=0, Flash=1``, default 0.
22
+ LIGHT_BEHAVIORS = {"constant": 0, "flash": 1}
23
+
24
+ #: How long the floodlight stays on after it triggers, in seconds. The camera
25
+ #: enumerates exactly these four (``LingerDuration``, default 30) -- it is not a
26
+ #: free range, and a value outside the set is accepted on the wire and ignored,
27
+ #: which would leave a control reporting a setting the camera never applied.
28
+ LIGHT_LINGER_DURATIONS = (20, 30, 40, 50)
29
+
30
+ #: The floodlight level the camera accepts (``Dimming``: min 10, max 100).
31
+ #: The floor is 10, not 0: 0 is out of range rather than "off", and off is
32
+ #: LightOnOff.
33
+ LIGHT_BRIGHTNESS_MIN = 10
34
+ LIGHT_BRIGHTNESS_MAX = 100
35
+
18
36
  #: The camera's ack for SETSTREAMCTRL (800).
19
37
  SETSTREAMCTRL_RESP_CMD = 801
20
38
  #: Measured 0.01-0.03s; short so a control call never holds up a view.
@@ -56,10 +74,65 @@ class _CameraControlsMixin:
56
74
  return await self.async_set_device_attribute("OSDEnable", 1 if enabled else 0)
57
75
 
58
76
  async def async_set_auto_light(self, enabled: bool) -> bool:
59
- """Enable or disable floodlight automation (autoLightEnable)."""
77
+ """Turn the light-up-when-someone-appears automation on or off.
78
+
79
+ This is ``autoLightEnable``, which the vendor app labels "When Someone
80
+ Appears" (or "When Someone Appears at Night" on some models -- that is
81
+ which label the app prints for the model, not a separate setting).
82
+ """
60
83
  return await self.async_set_device_attribute(
61
84
  "autoLightEnable", 1 if enabled else 0)
62
85
 
86
+ async def async_set_light_behavior(self, behavior: str) -> bool:
87
+ """Set how the light behaves when it triggers: "constant" or "flash".
88
+
89
+ **This write acks and does not land.** Measured 2026-09-07 against an
90
+ A001513 (which declares ``lightBehavior`` in its profile) and an
91
+ A000088 (which does not): six attempts across both models, as int 1 and
92
+ as string "1", every one returning True and every read-back over the
93
+ following 20 s still reporting the old value. On the same camera in the
94
+ same session ``LingerDuration`` and ``Dimming`` both landed within 4 s,
95
+ so this is the attribute, not the camera being asleep or the cloud
96
+ lagging. The one hypothesis left untested is that the camera takes it
97
+ only while ``autoLightEnable`` is 1 -- untested because arming it makes
98
+ a real floodlight come on in someone's house.
99
+
100
+ The method is kept because the command is well-formed and a future
101
+ firmware may honour it, but **nothing should build a control on it**
102
+ until a read-back confirms one: a setting that reports a value the
103
+ camera never applied is worse than no setting. Same call as
104
+ ``async_set_resolution``.
105
+
106
+ Refuses a name the camera does not offer rather than sending it: the
107
+ attribute is an enum, and an out-of-range write is accepted silently.
108
+ """
109
+ try:
110
+ value = LIGHT_BEHAVIORS[behavior]
111
+ except KeyError:
112
+ raise ValueError(
113
+ f"unknown light behavior {behavior!r}; "
114
+ f"expected one of {sorted(LIGHT_BEHAVIORS)}") from None
115
+ return await self.async_set_device_attribute("lightBehavior", value)
116
+
117
+ async def async_set_light_linger_duration(self, seconds: int) -> bool:
118
+ """Set how long the light stays on after it triggers, in seconds."""
119
+ if seconds not in LIGHT_LINGER_DURATIONS:
120
+ raise ValueError(
121
+ f"unsupported linger duration {seconds!r}; "
122
+ f"the camera offers {list(LIGHT_LINGER_DURATIONS)}")
123
+ return await self.async_set_device_attribute("LingerDuration", seconds)
124
+
125
+ async def async_set_light_brightness(self, level: int) -> bool:
126
+ """Set the floodlight level (10-100), clamped to what the camera takes.
127
+
128
+ Separate from :meth:`async_set_floodlight`'s optional brightness, which
129
+ is part of turning the light on by hand; this is the level the camera
130
+ uses when its own automation fires.
131
+ """
132
+ return await self.async_set_device_attribute(
133
+ "Dimming",
134
+ max(LIGHT_BRIGHTNESS_MIN, min(LIGHT_BRIGHTNESS_MAX, int(level))))
135
+
63
136
  async def async_set_voice_prompts(self, enabled: bool) -> bool:
64
137
  """Enable or disable spoken prompts from the camera speaker (voiceEnable)."""
65
138
  return await self.async_set_device_attribute(
@@ -97,8 +97,18 @@ class CameraStatusData(DeviceStatusData):
97
97
  status_led: Optional[bool] = None
98
98
  #: Timestamp/OSD overlay burned into the video (OSDEnable).
99
99
  osd_timestamp: Optional[bool] = None
100
- #: Floodlight automation (autoLightEnable).
100
+ #: Floodlight automation (autoLightEnable). The vendor app labels this
101
+ #: "When Someone Appears" -- it is the person-triggered light, not a
102
+ #: generic schedule, and the three fields below are its settings.
101
103
  auto_light: Optional[bool] = None
104
+ #: How the light behaves when it triggers: "constant" or "flash"
105
+ #: (lightBehavior 0/1). An unmodelled number is carried through as itself.
106
+ light_behavior: Optional[str] = None
107
+ #: How long the light stays on after a trigger, in seconds (LingerDuration).
108
+ light_linger_duration: Optional[int] = None
109
+ #: Floodlight level 10-100 (Dimming). Kept apart from `dimming`, which is
110
+ #: the light platform's brightness -- see _LIGHT_ONLY_ATTR_KEYS.
111
+ light_brightness: Optional[int] = None
102
112
  #: Spoken prompts from the camera speaker (voiceEnable).
103
113
  voice_prompts: Optional[bool] = None
104
114
  #: HDR (HDRStatus).
@@ -153,6 +163,16 @@ class CameraStatusData(DeviceStatusData):
153
163
  self.osd_timestamp = b
154
164
  if (b := _as_bool(attr.get("autoLightEnable"))) is not None:
155
165
  self.auto_light = b
166
+ if (v := attr.get("lightBehavior")) is not None:
167
+ lb = _as_int(v)
168
+ # Same shape as nightVisionMode: name the values we know and carry
169
+ # anything else through, so a firmware that grows a third mode
170
+ # cannot be reported as one of the two we do know.
171
+ self.light_behavior = (
172
+ {0: "constant", 1: "flash"}.get(lb, str(lb))
173
+ if lb is not None else str(v))
174
+ if (i := _as_int(attr.get("LingerDuration"))) is not None:
175
+ self.light_linger_duration = i
156
176
  if (b := _as_bool(attr.get("voiceEnable"))) is not None:
157
177
  self.voice_prompts = b
158
178
  if (b := _as_bool(attr.get("HDRStatus"))) is not None:
@@ -201,6 +221,13 @@ class CameraStatusData(DeviceStatusData):
201
221
  ``properties`` dict - both share the same camera attribute keys
202
222
  (Battery_remaining, Occupancy, SDcardStatus, MotionDetection_*, ...).
203
223
  """
224
+ # Dimming is filtered out below because on a LIGHT it is the entity's
225
+ # brightness, and a camera's must never be read as one. On a camera it
226
+ # is nonetheless a real setting -- the floodlight's level, 10-100 in the
227
+ # device profile, and what the app's Brightness page writes -- so read
228
+ # it here into its own field rather than letting the filter lose it.
229
+ if (i := _as_int(attrs.get(CONF_DIMMING))) is not None:
230
+ self.light_brightness = i
204
231
  self.update({
205
232
  k: v for k, v in attrs.items()
206
233
  if k not in self._LIGHT_ONLY_ATTR_KEYS
@@ -220,6 +247,55 @@ class CameraDeviceInformation(DeviceInformation):
220
247
  # for a pan-only camera, [1,2,3,6] = full PTZ). Empty means unknown - callers
221
248
  # should treat unknown as "show all" for backward compatibility.
222
249
  ptz_directions: list
250
+ # Every property identity this MODEL declares in its cloud profile - the
251
+ # same list the vendor app gates its settings pages on. Distinct from the
252
+ # device's `properties` dict, which is what the cloud currently REPORTS and
253
+ # carries defaults for keys the model never implemented: an A000088 reports
254
+ # LingerDuration = "30" and, asked directly over LAN, has no such attribute
255
+ # and ignores writes to it. Gate a control on this, not on a reported
256
+ # value, whenever offering it on the wrong model would mean a control that
257
+ # acknowledges and does nothing.
258
+ #
259
+ # Empty means the profile was absent or unreadable, which is UNKNOWN rather
260
+ # than "declares nothing" - so a caller that has other evidence a setting
261
+ # works (`Dimming` is real on the A000088 and verified by read-back there,
262
+ # though the profile omits it) should keep using that evidence instead.
263
+ declared_properties: frozenset
264
+
265
+ @staticmethod
266
+ def _collect_declared_properties(device: dict) -> frozenset:
267
+ """Property identities from the model profile, or empty if unreadable.
268
+
269
+ Keyed on ``identity``/``code``, never on ``name``: the profile's display
270
+ name is often a placeholder like
271
+ ``propertyName_lightBehavior_1679075889207132162``, and a name-keyed
272
+ search reported that no model declared a property every model declared.
273
+
274
+ The profile is remote data of no guaranteed shape, so every level is
275
+ type-checked rather than trusted; a malformed one yields an empty set
276
+ instead of killing device setup.
277
+ """
278
+ out = set()
279
+ try:
280
+ product = device.get(CONF_PRODUCT)
281
+ modules = product.get(CONF_SERVICE_MODULES) if isinstance(product, dict) else None
282
+ if not isinstance(modules, list):
283
+ return frozenset()
284
+ for service in modules:
285
+ if not isinstance(service, dict):
286
+ continue
287
+ props = service.get(CONF_PROPERTIES)
288
+ if not isinstance(props, list):
289
+ continue
290
+ for prop in props:
291
+ if not isinstance(prop, dict):
292
+ continue
293
+ ident = prop.get("identity") or prop.get("code")
294
+ if isinstance(ident, str) and ident:
295
+ out.add(ident)
296
+ except Exception:
297
+ return frozenset()
298
+ return frozenset(out)
223
299
 
224
300
  def __init__(self, device: dict[str, Any]) -> None:
225
301
  super().__init__(device)
@@ -229,6 +305,7 @@ class CameraDeviceInformation(DeviceInformation):
229
305
  str(_aes) if _aes else "")
230
306
  self.device_password = device.get("password") or ""
231
307
  self.ptz_directions = []
308
+ self.declared_properties = self._collect_declared_properties(device)
232
309
  if CONF_PRODUCT in device and CONF_SERVICE_MODULES in device[CONF_PRODUCT]:
233
310
  for service in device[CONF_PRODUCT][CONF_SERVICE_MODULES]:
234
311
  for prop in service.get(CONF_PROPERTIES) or []:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-aidot-cameras"
7
- version = "1.0.0rc15"
7
+ version = "1.0.0rc17"
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"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-aidot-cameras
3
- Version: 1.0.0rc15
3
+ Version: 1.0.0rc17
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
@@ -84,6 +84,7 @@ tests/test_answer_sdp_video_pt.py
84
84
  tests/test_answer_section_selection.py
85
85
  tests/test_audio_mux_offline.py
86
86
  tests/test_auto_alarm.py
87
+ tests/test_auto_light_settings.py
87
88
  tests/test_avio_ctrl_uses_the_session_dseq.py
88
89
  tests/test_avio_request.py
89
90
  tests/test_avio_response_parse.py
@@ -104,6 +105,7 @@ tests/test_cloud_count.py
104
105
  tests/test_cloud_plan.py
105
106
  tests/test_constants_imports_resolve.py
106
107
  tests/test_control_verdicts.py
108
+ tests/test_declared_properties.py
107
109
  tests/test_deferred_hardening.py
108
110
  tests/test_detection_types.py
109
111
  tests/test_device_cmd_seq_match.py
@@ -0,0 +1,143 @@
1
+ """The camera's "light up when someone appears" settings.
2
+
3
+ The vendor app calls the master toggle "When Someone Appears" and it is the
4
+ `autoLightEnable` attribute this library already exposes. The three settings
5
+ that sit UNDER it in the app were missing:
6
+
7
+ lightBehavior enum Constant=0, Flash=1 (default 0)
8
+ LingerDuration enum 20 / 30 / 40 / 50 seconds (default 30)
9
+ Dimming int 10..100 step 1 (default 100)
10
+
11
+ Read from the cloud device profile 2026-09-07 (LK.IPC.A001513, the L2), and
12
+ every one of the three is carried as a live property value by the L2 and by the
13
+ A000088 alike. Of the three, `LingerDuration` and `Dimming` were confirmed on
14
+ hardware to land (written, read back changed, restored); `lightBehavior` acks
15
+ and does NOT land on either model, so the library keeps the setter and Home
16
+ Assistant deliberately ships no control for it -- see
17
+ `async_set_light_behavior`. The A001064 has neither `lightBehavior` nor `LingerDuration` in
18
+ its profile or its properties, so those controls must stay absent there rather
19
+ than appear reading "unknown" -- which is why every getter here answers None
20
+ for a camera that never reported the key.
21
+ """
22
+
23
+ import asyncio
24
+
25
+ import pytest
26
+
27
+ from aidot_cameras.camera.controls import (
28
+ LIGHT_BEHAVIORS,
29
+ LIGHT_LINGER_DURATIONS,
30
+ _CameraControlsMixin,
31
+ )
32
+ from aidot_cameras.camera.models import CameraStatusData
33
+
34
+
35
+ class _Cam(_CameraControlsMixin):
36
+ device_id = "cam1"
37
+
38
+ def __init__(self):
39
+ self.attrs: list = []
40
+
41
+ async def async_set_device_attribute(self, attr, value, **kw):
42
+ self.attrs.append((attr, value))
43
+ return True
44
+
45
+
46
+ # --- reading -----------------------------------------------------------------
47
+
48
+ def test_light_behavior_reads_as_a_name_not_a_number():
49
+ s = CameraStatusData()
50
+ s.update_from_camera_attributes({"lightBehavior": "1"})
51
+ assert s.light_behavior == "flash"
52
+ s.update_from_camera_attributes({"lightBehavior": "0"})
53
+ assert s.light_behavior == "constant"
54
+
55
+
56
+ def test_an_unmodelled_behavior_value_is_carried_through_not_dropped():
57
+ """A firmware that grows a third mode must not read as one we know."""
58
+ s = CameraStatusData()
59
+ s.update_from_camera_attributes({"lightBehavior": "7"})
60
+ assert s.light_behavior == "7"
61
+
62
+
63
+ def test_linger_duration_reads_as_seconds():
64
+ s = CameraStatusData()
65
+ s.update_from_camera_attributes({"LingerDuration": "40"})
66
+ assert s.light_linger_duration == 40
67
+
68
+
69
+ def test_trigger_brightness_reads_from_dimming():
70
+ s = CameraStatusData()
71
+ s.update_from_camera_attributes({"Dimming": "80"})
72
+ assert s.light_brightness == 80
73
+
74
+
75
+ def test_dimming_still_does_not_become_the_light_entity_brightness():
76
+ """The camera's Dimming is its floodlight level, and it has its own field.
77
+
78
+ It must not leak into `dimming`, which is the LIGHT platform's brightness
79
+ and is why _LIGHT_ONLY_ATTR_KEYS filters the key in the first place.
80
+ """
81
+ s = CameraStatusData()
82
+ s.update_from_camera_attributes({"Dimming": "80"})
83
+ assert s.dimming is None
84
+
85
+
86
+ def test_a_camera_that_never_reported_them_is_unknown_not_defaulted():
87
+ s = CameraStatusData()
88
+ s.update_from_camera_attributes({"MotionDetection_Enable": "1"})
89
+ assert s.light_behavior is None
90
+ assert s.light_linger_duration is None
91
+ assert s.light_brightness is None
92
+
93
+
94
+ def test_a_partial_push_does_not_clear_what_a_full_poll_established():
95
+ s = CameraStatusData()
96
+ s.update_from_camera_attributes(
97
+ {"lightBehavior": "1", "LingerDuration": "50", "Dimming": "60"})
98
+ s.update_from_camera_attributes({"Occupancy": "1"})
99
+ assert (s.light_behavior, s.light_linger_duration, s.light_brightness) == (
100
+ "flash", 50, 60)
101
+
102
+
103
+ # --- writing -----------------------------------------------------------------
104
+
105
+ def test_setting_the_behavior_sends_the_camera_its_own_number():
106
+ cam = _Cam()
107
+ assert asyncio.run(cam.async_set_light_behavior("flash")) is True
108
+ assert cam.attrs == [("lightBehavior", 1)]
109
+
110
+
111
+ def test_an_unknown_behavior_is_refused_and_nothing_is_sent():
112
+ cam = _Cam()
113
+ with pytest.raises(ValueError):
114
+ asyncio.run(cam.async_set_light_behavior("strobe"))
115
+ assert cam.attrs == []
116
+
117
+
118
+ def test_setting_the_linger_duration_sends_seconds():
119
+ cam = _Cam()
120
+ assert asyncio.run(cam.async_set_light_linger_duration(20)) is True
121
+ assert cam.attrs == [("LingerDuration", 20)]
122
+
123
+
124
+ def test_a_duration_the_camera_does_not_offer_is_refused():
125
+ """The camera enumerates exactly four; 35 would be accepted and ignored."""
126
+ cam = _Cam()
127
+ with pytest.raises(ValueError):
128
+ asyncio.run(cam.async_set_light_linger_duration(35))
129
+ assert cam.attrs == []
130
+
131
+
132
+ def test_trigger_brightness_is_clamped_to_the_range_the_camera_declares():
133
+ """The profile's minimum is 10, not 0 -- 0 is not "off", it is out of range."""
134
+ cam = _Cam()
135
+ asyncio.run(cam.async_set_light_brightness(0))
136
+ asyncio.run(cam.async_set_light_brightness(150))
137
+ asyncio.run(cam.async_set_light_brightness(55))
138
+ assert cam.attrs == [("Dimming", 10), ("Dimming", 100), ("Dimming", 55)]
139
+
140
+
141
+ def test_the_offered_values_match_what_the_camera_profile_declares():
142
+ assert LIGHT_BEHAVIORS == {"constant": 0, "flash": 1}
143
+ assert LIGHT_LINGER_DURATIONS == (20, 30, 40, 50)
@@ -0,0 +1,85 @@
1
+ """What the MODEL declares it has, as distinct from what the cloud reports.
2
+
3
+ These are not the same thing, and the difference is what makes a control lie.
4
+ Measured 2026-09-08 on an A000088:
5
+
6
+ cloud property list LingerDuration = "30"
7
+ the camera, asked
8
+ directly over LAN LingerDuration = absent
9
+ writing it acked, and nothing changed anywhere
10
+
11
+ The cloud carries a DEFAULT for a key that model has never implemented. Gating
12
+ a control on "the cloud reported a value" therefore offers it on cameras where
13
+ it cannot work -- which is exactly what shipped in 2.23.0 and had to be pulled
14
+ back. The vendor app gates on the product profile instead, and so must we.
15
+ """
16
+
17
+ from aidot_cameras.camera.models import CameraDeviceInformation
18
+
19
+
20
+ def _device(props, **extra):
21
+ d = {"id": "cam1", "aesKey": ["k" * 16], "password": "p",
22
+ "product": {"serviceModules": [{"identity": "IPC/common attr",
23
+ "properties": props}]}}
24
+ d.update(extra)
25
+ return d
26
+
27
+
28
+ def test_declared_properties_collects_what_the_model_offers():
29
+ info = CameraDeviceInformation(_device([
30
+ {"identity": "LingerDuration", "code": "LingerDuration"},
31
+ {"identity": "Dimming", "code": "Dimming"},
32
+ ]))
33
+ assert "LingerDuration" in info.declared_properties
34
+ assert "Dimming" in info.declared_properties
35
+
36
+
37
+ def test_a_placeholder_display_name_does_not_hide_a_property():
38
+ """The profile's `name` is often `propertyName_<x>_<productId>`.
39
+
40
+ Keying on it instead of `identity` reported "no model declares
41
+ lightBehavior" when every model did.
42
+ """
43
+ info = CameraDeviceInformation(_device([
44
+ {"identity": "lightBehavior", "code": "lightBehavior",
45
+ "name": "propertyName_lightBehavior_1679075889207132162"},
46
+ ]))
47
+ assert "lightBehavior" in info.declared_properties
48
+
49
+
50
+ def test_a_property_with_only_a_code_is_still_collected():
51
+ info = CameraDeviceInformation(_device([{"code": "Dimming"}]))
52
+ assert "Dimming" in info.declared_properties
53
+
54
+
55
+ def test_a_device_with_no_profile_declares_nothing_and_does_not_raise():
56
+ info = CameraDeviceInformation({"id": "c", "aesKey": ["k" * 16]})
57
+ assert info.declared_properties == frozenset()
58
+
59
+
60
+ def test_a_malformed_profile_yields_nothing_rather_than_raising():
61
+ """The profile is remote data of no guaranteed shape.
62
+
63
+ Asserted against the collector directly, not through the constructor: the
64
+ UPSTREAM base class parses the same profile first and indexes it without
65
+ type checks, so a string where a list belongs raises there before this code
66
+ runs. That fragility is pre-existing and shared with the light path, so it
67
+ is noted rather than widened into here.
68
+ """
69
+ collect = CameraDeviceInformation._collect_declared_properties
70
+ for bad in ({"product": {"serviceModules": "nope"}},
71
+ {"product": {"serviceModules": [None]}},
72
+ {"product": {"serviceModules": [{"properties": "nope"}]}},
73
+ {"product": {"serviceModules": [{"properties": ["str"]}]}},
74
+ {"product": "nope"},
75
+ {}):
76
+ assert collect(bad) == frozenset()
77
+
78
+
79
+ def test_declaring_nothing_is_not_the_same_as_reporting_nothing():
80
+ """The A000088 case, locked in: the cloud reports it, the model does not.
81
+
82
+ A gate built on the reported value would say yes here. That is the bug.
83
+ """
84
+ info = CameraDeviceInformation(_device([{"identity": "Dimming"}]))
85
+ assert "LingerDuration" not in info.declared_properties