python-aidot-cameras 1.0.0b6__tar.gz → 1.0.0b7__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 (231) hide show
  1. {python_aidot_cameras-1.0.0b6/python_aidot_cameras.egg-info → python_aidot_cameras-1.0.0b7}/PKG-INFO +1 -1
  2. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/models.py +42 -0
  3. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/pyproject.toml +1 -1
  4. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7/python_aidot_cameras.egg-info}/PKG-INFO +1 -1
  5. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/python_aidot_cameras.egg-info/SOURCES.txt +1 -0
  6. python_aidot_cameras-1.0.0b7/tests/test_sd_card_presence.py +94 -0
  7. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/LICENSE +0 -0
  8. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/MANIFEST.in +0 -0
  9. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/README.md +0 -0
  10. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/__init__.py +0 -0
  11. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/__main__.py +0 -0
  12. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_upstream.py +0 -0
  13. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/__init__.py +0 -0
  14. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/__init__.py +0 -0
  15. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/clock.py +0 -0
  16. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/codecs/__init__.py +0 -0
  17. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/codecs/base.py +0 -0
  18. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/codecs/g711.py +0 -0
  19. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/codecs/g722.py +0 -0
  20. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/codecs/h264.py +0 -0
  21. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/codecs/opus.py +0 -0
  22. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/codecs/vpx.py +0 -0
  23. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/contrib/__init__.py +0 -0
  24. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/contrib/media.py +0 -0
  25. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/contrib/signaling.py +0 -0
  26. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/events.py +0 -0
  27. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/exceptions.py +0 -0
  28. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/jitterbuffer.py +0 -0
  29. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/mediastreams.py +0 -0
  30. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rate.py +0 -0
  31. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rtcconfiguration.py +0 -0
  32. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rtcdatachannel.py +0 -0
  33. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rtcdtlstransport.py +0 -0
  34. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rtcicetransport.py +0 -0
  35. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rtcpeerconnection.py +0 -0
  36. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rtcrtpparameters.py +0 -0
  37. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rtcrtpreceiver.py +0 -0
  38. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rtcrtpsender.py +0 -0
  39. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rtcrtptransceiver.py +0 -0
  40. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rtcsctptransport.py +0 -0
  41. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rtcsessiondescription.py +0 -0
  42. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rtp.py +0 -0
  43. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/sdp.py +0 -0
  44. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/stats.py +0 -0
  45. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/utils.py +0 -0
  46. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/ble_gateway.py +0 -0
  47. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/__init__.py +0 -0
  48. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/client.py +0 -0
  49. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/constants.py +0 -0
  50. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/controls.py +0 -0
  51. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/go2rtc.py +0 -0
  52. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/hwaccel.py +0 -0
  53. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/lan_control.py +0 -0
  54. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/playback.py +0 -0
  55. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/protocol.py +0 -0
  56. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/sd_events.py +0 -0
  57. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/sd_listing.py +0 -0
  58. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/sdes.py +0 -0
  59. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/sdes_open.py +0 -0
  60. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/tutk.py +0 -0
  61. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/webrtc.py +0 -0
  62. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/webrtc_open.py +0 -0
  63. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/client.py +0 -0
  64. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/const.py +0 -0
  65. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/credentials.py +0 -0
  66. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/crypto.py +0 -0
  67. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/device_client.py +0 -0
  68. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/discover.py +0 -0
  69. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/exceptions.py +0 -0
  70. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/g711.py +0 -0
  71. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/lan_retry.py +0 -0
  72. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/aidot_cameras/py.typed +0 -0
  73. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/python_aidot_cameras.egg-info/dependency_links.txt +0 -0
  74. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/python_aidot_cameras.egg-info/entry_points.txt +0 -0
  75. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/python_aidot_cameras.egg-info/requires.txt +0 -0
  76. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/python_aidot_cameras.egg-info/top_level.txt +0 -0
  77. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/setup.cfg +0 -0
  78. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_aioice_compat.py +0 -0
  79. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_alarm_event.py +0 -0
  80. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_answer_sdp_video_pt.py +0 -0
  81. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_audio_mux_offline.py +0 -0
  82. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_avio_request.py +0 -0
  83. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_avio_response_parse.py +0 -0
  84. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_avio_response_router.py +0 -0
  85. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_backoff.py +0 -0
  86. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_battery_detection.py +0 -0
  87. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_battery_evidence_corroboration.py +0 -0
  88. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_battery_skip_turn.py +0 -0
  89. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_bitrate_campaign_arms.py +0 -0
  90. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_ble_gateway.py +0 -0
  91. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_bridge_state_sentinels.py +0 -0
  92. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_busy_backoff.py +0 -0
  93. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_cloud_count.py +0 -0
  94. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_cloud_plan.py +0 -0
  95. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_constants_imports_resolve.py +0 -0
  96. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_control_verdicts.py +0 -0
  97. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_deferred_hardening.py +0 -0
  98. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_device_cmd_seq_match.py +0 -0
  99. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_device_login_guard.py +0 -0
  100. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_device_user_info_cache.py +0 -0
  101. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_dormant_camera_cleanup.py +0 -0
  102. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_dtls_mux_terminal_write.py +0 -0
  103. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_dtls_not_ready_burst.py +0 -0
  104. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_dtls_pinning.py +0 -0
  105. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_dtls_resolution_ack.py +0 -0
  106. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_dtls_serve_decode_skip.py +0 -0
  107. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_dtls_serve_open_timeout.py +0 -0
  108. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_dtls_skip_signaling_wait.py +0 -0
  109. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_dtls_slow_probe.py +0 -0
  110. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_dtls_stdout_producer.py +0 -0
  111. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_egress_guard.py +0 -0
  112. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_env_seams.py +0 -0
  113. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_feature_probe_needs_a_live_session.py +0 -0
  114. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_futile_keepalive.py +0 -0
  115. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_gate_requires_a_decoded_frame.py +0 -0
  116. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_go2rtc.py +0 -0
  117. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_go2rtc_cli.py +0 -0
  118. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_h264_decode_rate_limit.py +0 -0
  119. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_highport_nomination.py +0 -0
  120. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_hwaccel_probe.py +0 -0
  121. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_ice_config_cache.py +0 -0
  122. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_ice_peer_reflexive.py +0 -0
  123. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_idle_release_both_loops.py +0 -0
  124. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_keepalive_renew.py +0 -0
  125. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_key_restart_sdp.py +0 -0
  126. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_keyframe_prompter.py +0 -0
  127. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_lan_control.py +0 -0
  128. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_lan_retry_reaches_the_storming_devices.py +0 -0
  129. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_light_active_color_mode.py +0 -0
  130. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_live_session_carries_talk.py +0 -0
  131. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_live_stream_param.py +0 -0
  132. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_live_validate_policy.py +0 -0
  133. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_logging_caps.py +0 -0
  134. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_login_connect_deadline.py +0 -0
  135. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_login_reconnect_backoff.py +0 -0
  136. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_media_counted_only_when_plaintext.py +0 -0
  137. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_media_socket_rcvbuf.py +0 -0
  138. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_media_wait_floor.py +0 -0
  139. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_motion_poll.py +0 -0
  140. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_mqtt_credential_refetch.py +0 -0
  141. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_mqtt_credential_self_heal.py +0 -0
  142. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_mqtt_password_not_persisted.py +0 -0
  143. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_mqtt_session_reconnect.py +0 -0
  144. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_narrow_pc_ice.py +0 -0
  145. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_no_key_material_in_logs.py +0 -0
  146. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_no_self_referential_go2rtc.py +0 -0
  147. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_no_undefined_names.py +0 -0
  148. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_offer_keeps_rtcp_feedback.py +0 -0
  149. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_offline_keepalive_pause.py +0 -0
  150. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_open_fail_logger.py +0 -0
  151. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_open_gate_delay.py +0 -0
  152. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_peerid_reuse.py +0 -0
  153. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_persistent_mqtt.py +0 -0
  154. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_persistent_mqtt_robustness.py +0 -0
  155. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_playback_tls.py +0 -0
  156. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_post_merge_hardening.py +0 -0
  157. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_ptz_probe_reads_what_it_is_told.py +0 -0
  158. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_quality_campaign_in_session.py +0 -0
  159. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_recent_recordings.py +0 -0
  160. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_remb.py +0 -0
  161. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_resolution_persists.py +0 -0
  162. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_restart_cycle.py +0 -0
  163. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_retry_policy.py +0 -0
  164. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_scripts_importable.py +0 -0
  165. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sctp_init_ack_tsn.py +0 -0
  166. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sd_event_decode.py +0 -0
  167. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sd_event_probe_payloads.py +0 -0
  168. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sd_listing.py +0 -0
  169. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_adaptive.py +0 -0
  170. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_avio_dispatch.py +0 -0
  171. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_bridge_break_gate.py +0 -0
  172. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_echo_wait_timeout.py +0 -0
  173. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_fast_liveplay.py +0 -0
  174. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_ffmpeg_stderr_log.py +0 -0
  175. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_first_media_stall_report.py +0 -0
  176. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_idle_release.py +0 -0
  177. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_offer_video_order.py +0 -0
  178. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_offer_video_pin.py +0 -0
  179. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_open_cleanup.py +0 -0
  180. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_sctp_abort.py +0 -0
  181. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_serve_audio.py +0 -0
  182. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_serve_cmd.py +0 -0
  183. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_serve_stderr_drain.py +0 -0
  184. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_sprop.py +0 -0
  185. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_talk.py +0 -0
  186. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_teardown_exit_log.py +0 -0
  187. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_terminal_ack.py +0 -0
  188. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_terminal_ack_latency.py +0 -0
  189. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_watchdog.py +0 -0
  190. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sdp_narrowing_properties.py +0 -0
  191. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sdp_payload_type_narrowing.py +0 -0
  192. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_security_hardening.py +0 -0
  193. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_self_peer_is_an_address_not_an_ip.py +0 -0
  194. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_serializable_login_info.py +0 -0
  195. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_serve_loss_tolerance.py +0 -0
  196. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_serve_relay.py +0 -0
  197. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_session_stats.py +0 -0
  198. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_shared_house_seam.py +0 -0
  199. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_snapshot_cancellation.py +0 -0
  200. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_speak.py +0 -0
  201. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sprop_capture_gate.py +0 -0
  202. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sprop_staleness.py +0 -0
  203. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_sprop_write_is_reported_truthfully.py +0 -0
  204. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_srtp_rx_rekey.py +0 -0
  205. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_srtp_tx_key_note.py +0 -0
  206. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_stall_report_says_what_the_answer_carried.py +0 -0
  207. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_stall_report_says_whether_the_trigger_was_answered.py +0 -0
  208. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_stall_report_separates_silence_from_undecryptable.py +0 -0
  209. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_stall_report_survives_a_caller_giving_up.py +0 -0
  210. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_stall_reports_reach_the_artifact.py +0 -0
  211. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_stream_cap.py +0 -0
  212. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_stream_drain_ownership.py +0 -0
  213. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_stream_idle.py +0 -0
  214. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_stream_slot_cap.py +0 -0
  215. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_stream_teardown.py +0 -0
  216. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_talk.py +0 -0
  217. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_talk_reports_a_speaker_that_never_opened.py +0 -0
  218. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_terminal_ack.py +0 -0
  219. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_token_file_robustness.py +0 -0
  220. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_token_refresh.py +0 -0
  221. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_turn_create_permission.py +0 -0
  222. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_tutk_path_is_out_of_reach.py +0 -0
  223. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_upstream_compat.py +0 -0
  224. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_video_profile_log.py +0 -0
  225. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_viewer_check_throttle.py +0 -0
  226. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_viewer_presence.py +0 -0
  227. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_viewer_present_push_mode.py +0 -0
  228. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_wait_or_event.py +0 -0
  229. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_wake_gate_camera_evidence.py +0 -0
  230. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/tests/test_wake_readiness_retry.py +0 -0
  231. {python_aidot_cameras-1.0.0b6 → python_aidot_cameras-1.0.0b7}/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.0b6
3
+ Version: 1.0.0b7
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
@@ -48,6 +48,38 @@ def _as_bool(v: Any) -> Optional[bool]:
48
48
  return None if i is None else bool(i)
49
49
 
50
50
 
51
+ def _card_present(attr: dict) -> Optional[bool]:
52
+ """Is an SD card in the slot? None means nobody said.
53
+
54
+ Three states, and the third is the point. Of seven cameras measured
55
+ 2026-08-12, one reported ``SDcardExistFlag: true``, two reported ``false``,
56
+ and four carried neither key - including an A000088, the same model as all
57
+ three that did report. So a missing key cannot mean "this model cannot say";
58
+ it means we do not know, and anything that reads it as "no card" would tell
59
+ someone with a working camera that their slot is empty.
60
+
61
+ ``SDcardExistFlag`` wins where it exists. ``SDcardBaseInfo`` is the fallback
62
+ and arrives as a STRING holding a JSON array - element 0 agreed with the
63
+ flag on every camera carrying both. The other four positions are not
64
+ decoded: ``30432`` is plausibly a 32 GB card's total MB and plausibly
65
+ something else, and a guess does not belong in a public data model.
66
+ """
67
+ if "SDcardExistFlag" in attr:
68
+ flag = _as_bool(attr.get("SDcardExistFlag"))
69
+ if flag is not None:
70
+ return flag
71
+
72
+ raw = attr.get("SDcardBaseInfo")
73
+ if isinstance(raw, str):
74
+ try:
75
+ raw = json.loads(raw)
76
+ except ValueError:
77
+ return None
78
+ if isinstance(raw, (list, tuple)) and raw:
79
+ return _as_bool(raw[0])
80
+ return None
81
+
82
+
51
83
  class CameraStatusData(DeviceStatusData):
52
84
  """Core status plus camera fields; accepts both typed-model and dict updates."""
53
85
 
@@ -74,6 +106,11 @@ class CameraStatusData(DeviceStatusData):
74
106
  battery_remaining: Optional[int] = None # Battery_remaining 0-100 (%)
75
107
  occupancy: Optional[bool] = None # Occupancy live presence
76
108
  sd_card_status: Optional[str] = None # SDcardStatus
109
+ # Tri-state: True in the slot, False slot empty, None nobody said. Reads
110
+ # INVERTED on A000088 relative to sd_card_status ("0" with a card, "1"
111
+ # without, 3/3) - which is why sd_card_status is not used for this and the
112
+ # two are kept apart. That inversion has no corroboration off A000088.
113
+ sd_card_present: Optional[bool] = None # SDcardExistFlag/SDcardBaseInfo[0]
77
114
  wifi_rssi: Optional[int] = None # networkRssi (dBm), cloud property
78
115
 
79
116
  def update(self, attr) -> None:
@@ -125,6 +162,11 @@ class CameraStatusData(DeviceStatusData):
125
162
  self.occupancy = b
126
163
  if (v := attr.get("SDcardStatus")) is not None:
127
164
  self.sd_card_status = str(v)
165
+ # Only overwrite on an actual reading. Attribute pushes are partial - a
166
+ # motion notif carries no card keys - so a silent update must leave what
167
+ # the last full poll established alone.
168
+ if (b := _card_present(attr)) is not None:
169
+ self.sd_card_present = b
128
170
  if (i := _as_int(attr.get("networkRssi"))) is not None:
129
171
  self.wifi_rssi = i
130
172
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-aidot-cameras"
7
- version = "1.0.0b6"
7
+ version = "1.0.0b7"
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.0b6
3
+ Version: 1.0.0b7
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
@@ -160,6 +160,7 @@ tests/test_restart_cycle.py
160
160
  tests/test_retry_policy.py
161
161
  tests/test_scripts_importable.py
162
162
  tests/test_sctp_init_ack_tsn.py
163
+ tests/test_sd_card_presence.py
163
164
  tests/test_sd_event_decode.py
164
165
  tests/test_sd_event_probe_payloads.py
165
166
  tests/test_sd_listing.py
@@ -0,0 +1,94 @@
1
+ """Is there a card in the slot, and do we actually know?
2
+
3
+ A camera with no SD card and a camera that could not be asked look identical to
4
+ every listing call: both return nothing. The cloud has been carrying the answer
5
+ all along in `SDcardExistFlag` / `SDcardBaseInfo`, and nothing read it - so a
6
+ user with an empty slot was told "the camera did not answer", which is false and
7
+ not actionable.
8
+
9
+ The whole value of the field is in the THIRD state. Two of the fleet's seven
10
+ cameras report the flag as false, one reports true, and four carry neither key -
11
+ including an A000088, the same model as all three that do report. So absence
12
+ cannot mean "this model cannot report it", and anything that collapses unknown
13
+ into "no card" would tell someone with a working camera it has no card.
14
+ """
15
+ from aidot_cameras.camera.models import CameraStatusData
16
+
17
+
18
+ def test_unknown_by_default():
19
+ assert CameraStatusData().sd_card_present is None
20
+
21
+
22
+ def test_a_card_that_is_there():
23
+ s = CameraStatusData()
24
+ s.update({"SDcardExistFlag": True})
25
+ assert s.sd_card_present is True
26
+
27
+
28
+ def test_a_slot_that_is_empty():
29
+ s = CameraStatusData()
30
+ s.update({"SDcardExistFlag": False})
31
+ assert s.sd_card_present is False
32
+
33
+
34
+ def test_a_camera_that_says_nothing_stays_unknown():
35
+ # The regression that matters. Four of seven cameras look like this, and
36
+ # `False` here would put "there is no SD card in this camera" under a
37
+ # camera nobody has any evidence about.
38
+ s = CameraStatusData()
39
+ s.update({"Battery_remaining": "80", "SDcardStatus": "1"})
40
+ assert s.sd_card_present is None
41
+
42
+
43
+ def test_the_base_info_answers_when_the_flag_is_absent():
44
+ # Measured shape: a STRING holding a JSON array, not an array. Element 0
45
+ # agreed with the flag on every camera that carried both.
46
+ s = CameraStatusData()
47
+ s.update({"SDcardBaseInfo": "[true,30432,0,1,0]"})
48
+ assert s.sd_card_present is True
49
+ s2 = CameraStatusData()
50
+ s2.update({"SDcardBaseInfo": "[false,0,0,0,0]"})
51
+ assert s2.sd_card_present is False
52
+
53
+
54
+ def test_the_base_info_is_also_accepted_as_a_real_list():
55
+ # The same keys arrive in different shapes across cameras - the neighbouring
56
+ # SdcardRecord_Enable is "1" on five and true on one - so shape is not
57
+ # something to rely on.
58
+ s = CameraStatusData()
59
+ s.update({"SDcardBaseInfo": [True, 30432, 0, 1, 0]})
60
+ assert s.sd_card_present is True
61
+
62
+
63
+ def test_the_flag_wins_over_the_base_info():
64
+ s = CameraStatusData()
65
+ s.update({"SDcardExistFlag": False, "SDcardBaseInfo": "[true,30432,0,1,0]"})
66
+ assert s.sd_card_present is False
67
+
68
+
69
+ def test_a_reply_that_cannot_be_read_is_unknown_not_absent():
70
+ # A decode failure is not evidence about the slot. Silence about a card and
71
+ # a card that is not there are the two answers this whole subsystem exists
72
+ # to keep apart.
73
+ for junk in ("", "[", "not json", "[]", None, 5, {}):
74
+ s = CameraStatusData()
75
+ s.update({"SDcardBaseInfo": junk})
76
+ assert s.sd_card_present is None, junk
77
+
78
+
79
+ def test_a_later_silent_update_does_not_clobber_a_known_reading():
80
+ # Camera attribute pushes are partial: a notif about motion carries no card
81
+ # keys, and must not erase what the last full poll established.
82
+ s = CameraStatusData()
83
+ s.update({"SDcardExistFlag": True})
84
+ s.update({"Occupancy": True})
85
+ assert s.sd_card_present is True
86
+
87
+
88
+ def test_it_arrives_through_the_cloud_properties_path_too():
89
+ # Both shapes reach the same parser: setDevAttrNotif attrs and the cloud
90
+ # device record's `properties`. The probe read it off the latter.
91
+ s = CameraStatusData()
92
+ s.update_from_camera_attributes(
93
+ {"SDcardExistFlag": False, "SDcardBaseInfo": "[false,0,0,0,0]"})
94
+ assert s.sd_card_present is False