python-aidot-cameras 1.0.0b5__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.
- {python_aidot_cameras-1.0.0b5/python_aidot_cameras.egg-info → python_aidot_cameras-1.0.0b7}/PKG-INFO +1 -1
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/models.py +42 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/sd_listing.py +38 -8
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/pyproject.toml +1 -1
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7/python_aidot_cameras.egg-info}/PKG-INFO +1 -1
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/python_aidot_cameras.egg-info/SOURCES.txt +1 -0
- python_aidot_cameras-1.0.0b7/tests/test_sd_card_presence.py +94 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sd_listing.py +33 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/LICENSE +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/MANIFEST.in +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/README.md +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/__init__.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/__main__.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_upstream.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/__init__.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/__init__.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/clock.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/codecs/__init__.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/codecs/base.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/codecs/g711.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/codecs/g722.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/codecs/h264.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/codecs/opus.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/codecs/vpx.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/contrib/__init__.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/contrib/media.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/contrib/signaling.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/events.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/exceptions.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/jitterbuffer.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/mediastreams.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rate.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rtcconfiguration.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rtcdatachannel.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rtcdtlstransport.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rtcicetransport.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rtcpeerconnection.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rtcrtpparameters.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rtcrtpreceiver.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rtcrtpsender.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rtcrtptransceiver.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rtcsctptransport.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rtcsessiondescription.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rtp.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/sdp.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/stats.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/utils.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/ble_gateway.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/__init__.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/client.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/constants.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/controls.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/go2rtc.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/hwaccel.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/lan_control.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/playback.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/protocol.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/sd_events.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/sdes.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/sdes_open.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/tutk.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/webrtc.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/webrtc_open.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/client.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/const.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/credentials.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/crypto.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/device_client.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/discover.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/exceptions.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/g711.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/lan_retry.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/py.typed +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/python_aidot_cameras.egg-info/dependency_links.txt +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/python_aidot_cameras.egg-info/entry_points.txt +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/python_aidot_cameras.egg-info/requires.txt +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/python_aidot_cameras.egg-info/top_level.txt +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/setup.cfg +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_aioice_compat.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_alarm_event.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_answer_sdp_video_pt.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_audio_mux_offline.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_avio_request.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_avio_response_parse.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_avio_response_router.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_backoff.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_battery_detection.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_battery_evidence_corroboration.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_battery_skip_turn.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_bitrate_campaign_arms.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_ble_gateway.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_bridge_state_sentinels.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_busy_backoff.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_cloud_count.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_cloud_plan.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_constants_imports_resolve.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_control_verdicts.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_deferred_hardening.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_device_cmd_seq_match.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_device_login_guard.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_device_user_info_cache.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_dormant_camera_cleanup.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_dtls_mux_terminal_write.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_dtls_not_ready_burst.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_dtls_pinning.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_dtls_resolution_ack.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_dtls_serve_decode_skip.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_dtls_serve_open_timeout.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_dtls_skip_signaling_wait.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_dtls_slow_probe.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_dtls_stdout_producer.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_egress_guard.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_env_seams.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_feature_probe_needs_a_live_session.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_futile_keepalive.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_gate_requires_a_decoded_frame.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_go2rtc.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_go2rtc_cli.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_h264_decode_rate_limit.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_highport_nomination.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_hwaccel_probe.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_ice_config_cache.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_ice_peer_reflexive.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_idle_release_both_loops.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_keepalive_renew.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_key_restart_sdp.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_keyframe_prompter.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_lan_control.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_lan_retry_reaches_the_storming_devices.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_light_active_color_mode.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_live_session_carries_talk.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_live_stream_param.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_live_validate_policy.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_logging_caps.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_login_connect_deadline.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_login_reconnect_backoff.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_media_counted_only_when_plaintext.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_media_socket_rcvbuf.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_media_wait_floor.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_motion_poll.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_mqtt_credential_refetch.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_mqtt_credential_self_heal.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_mqtt_password_not_persisted.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_mqtt_session_reconnect.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_narrow_pc_ice.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_no_key_material_in_logs.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_no_self_referential_go2rtc.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_no_undefined_names.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_offer_keeps_rtcp_feedback.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_offline_keepalive_pause.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_open_fail_logger.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_open_gate_delay.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_peerid_reuse.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_persistent_mqtt.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_persistent_mqtt_robustness.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_playback_tls.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_post_merge_hardening.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_ptz_probe_reads_what_it_is_told.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_quality_campaign_in_session.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_recent_recordings.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_remb.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_resolution_persists.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_restart_cycle.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_retry_policy.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_scripts_importable.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sctp_init_ack_tsn.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sd_event_decode.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sd_event_probe_payloads.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_adaptive.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_avio_dispatch.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_bridge_break_gate.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_echo_wait_timeout.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_fast_liveplay.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_ffmpeg_stderr_log.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_first_media_stall_report.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_idle_release.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_offer_video_order.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_offer_video_pin.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_open_cleanup.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_sctp_abort.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_serve_audio.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_serve_cmd.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_serve_stderr_drain.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_sprop.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_talk.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_teardown_exit_log.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_terminal_ack.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_terminal_ack_latency.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sdes_watchdog.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sdp_narrowing_properties.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sdp_payload_type_narrowing.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_security_hardening.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_self_peer_is_an_address_not_an_ip.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_serializable_login_info.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_serve_loss_tolerance.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_serve_relay.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_session_stats.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_shared_house_seam.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_snapshot_cancellation.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_speak.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sprop_capture_gate.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sprop_staleness.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_sprop_write_is_reported_truthfully.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_srtp_rx_rekey.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_srtp_tx_key_note.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_stall_report_says_what_the_answer_carried.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_stall_report_says_whether_the_trigger_was_answered.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_stall_report_separates_silence_from_undecryptable.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_stall_report_survives_a_caller_giving_up.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_stall_reports_reach_the_artifact.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_stream_cap.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_stream_drain_ownership.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_stream_idle.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_stream_slot_cap.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_stream_teardown.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_talk.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_talk_reports_a_speaker_that_never_opened.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_terminal_ack.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_token_file_robustness.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_token_refresh.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_turn_create_permission.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_tutk_path_is_out_of_reach.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_upstream_compat.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_video_profile_log.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_viewer_check_throttle.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_viewer_presence.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_viewer_present_push_mode.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_wait_or_event.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_wake_gate_camera_evidence.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_wake_readiness_retry.py +0 -0
- {python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_webrtc_answer_race.py +0 -0
{python_aidot_cameras-1.0.0b5/python_aidot_cameras.egg-info → python_aidot_cameras-1.0.0b7}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-aidot-cameras
|
|
3
|
-
Version: 1.0.
|
|
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
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/models.py
RENAMED
|
@@ -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
|
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/sd_listing.py
RENAMED
|
@@ -35,9 +35,42 @@ _LOGGER = logging.getLogger(__name__)
|
|
|
35
35
|
_SD_LIST_TIMEOUT_S = 8.0
|
|
36
36
|
|
|
37
37
|
|
|
38
|
+
def _session_is_live(session: Any) -> bool:
|
|
39
|
+
"""Is this session object one that could still carry a request?
|
|
40
|
+
|
|
41
|
+
Only SDES sessions publish ``is_alive``, so a session already torn down is
|
|
42
|
+
caught on that transport and not on DTLS. That asymmetry is fine here and
|
|
43
|
+
must stay fine at every caller: this is a cheap early-out, never the thing
|
|
44
|
+
that keeps a dead channel from being reported as an empty card. ``answered``
|
|
45
|
+
does that, on every transport.
|
|
46
|
+
"""
|
|
47
|
+
if session is None:
|
|
48
|
+
return False
|
|
49
|
+
return getattr(session, "is_alive", True) is not False
|
|
50
|
+
|
|
51
|
+
|
|
38
52
|
class _CameraSdMixin:
|
|
39
53
|
"""The on-device recording query, mixed into CameraMixin."""
|
|
40
54
|
|
|
55
|
+
@property
|
|
56
|
+
def has_live_session(self) -> bool:
|
|
57
|
+
"""Is a stream session up right now?
|
|
58
|
+
|
|
59
|
+
Public because callers legitimately need to WAIT for a session without
|
|
60
|
+
asking it anything. ``start_keepalive`` returns before the handshake it
|
|
61
|
+
schedules has finished, so a caller that wants to ride the session it
|
|
62
|
+
just asked for has no way to know when that session has arrived.
|
|
63
|
+
Probing with a real request is not a substitute: once the session is up,
|
|
64
|
+
the probe IS the request, and a caller that wanted to send one request
|
|
65
|
+
has then sent two.
|
|
66
|
+
|
|
67
|
+
Best-effort, in the direction that keeps callers honest: a torn-down
|
|
68
|
+
session reads as live on a transport that does not say otherwise, so a
|
|
69
|
+
listing taken on the strength of this can still fail and every caller
|
|
70
|
+
must still handle that.
|
|
71
|
+
"""
|
|
72
|
+
return _session_is_live(getattr(self, "_stream_session", None))
|
|
73
|
+
|
|
41
74
|
async def _sd_ask(self, session: Any, cmd: int, payload: bytes,
|
|
42
75
|
response_cmd: int, timeout: float) -> Optional[Any]:
|
|
43
76
|
"""One request, with every failure flattened to None."""
|
|
@@ -84,14 +117,11 @@ class _CameraSdMixin:
|
|
|
84
117
|
"""
|
|
85
118
|
session = session if session is not None else getattr(
|
|
86
119
|
self, "_stream_session", None)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
-
|
|
92
|
-
# is fine, because it is NOT what keeps a dead session from being
|
|
93
|
-
# reported as an empty card. `answered` does that, on every transport.
|
|
94
|
-
if getattr(session, "is_alive", True) is False:
|
|
120
|
+
# No session is "could not ask", and so is a session already known to
|
|
121
|
+
# be torn down: there is no point spending two timeouts on one. Shared
|
|
122
|
+
# with `has_live_session` so a caller waiting for a session to exist and
|
|
123
|
+
# this call deciding it has one cannot come to different answers.
|
|
124
|
+
if not _session_is_live(session):
|
|
95
125
|
return None
|
|
96
126
|
|
|
97
127
|
end_ts = time.time()
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-aidot-cameras"
|
|
7
|
-
version = "1.0.
|
|
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"
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7/python_aidot_cameras.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-aidot-cameras
|
|
3
|
-
Version: 1.0.
|
|
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
|
|
@@ -58,6 +58,39 @@ class _Camera(_CameraSdMixin):
|
|
|
58
58
|
self.device_id = "dev-1"
|
|
59
59
|
|
|
60
60
|
|
|
61
|
+
def test_no_session_is_not_a_live_session():
|
|
62
|
+
# The point of the predicate: a caller can wait for a session to exist
|
|
63
|
+
# without sending anything to find out whether it does.
|
|
64
|
+
assert _Camera().has_live_session is False
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_a_session_that_exists_is_a_live_session():
|
|
68
|
+
assert _Camera(_Session({})).has_live_session is True
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def test_a_torn_down_session_is_not_a_live_session():
|
|
72
|
+
# Only SDES sessions say so, which is why a caller that acts on this still
|
|
73
|
+
# has to handle a listing that fails anyway.
|
|
74
|
+
assert _Camera(_Session({}, alive=False)).has_live_session is False
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_the_predicate_reaches_the_client_a_caller_actually_holds():
|
|
78
|
+
# Every test above builds the mixin directly, which proves the rule and not
|
|
79
|
+
# the wiring. A caller reads this off the client it was handed, and reads it
|
|
80
|
+
# through hasattr - so a name shadowed anywhere in that MRO would not raise,
|
|
81
|
+
# it would quietly answer "cannot ask" and put the caller straight back to
|
|
82
|
+
# the behaviour this predicate exists to end.
|
|
83
|
+
from aidot_cameras.device_client import CameraDeviceClient, LightDeviceClient
|
|
84
|
+
|
|
85
|
+
client = object.__new__(CameraDeviceClient)
|
|
86
|
+
assert client.has_live_session is False
|
|
87
|
+
client._stream_session = _Session({})
|
|
88
|
+
assert client.has_live_session is True
|
|
89
|
+
# And it stays off the client a light gets, like the rest of the camera
|
|
90
|
+
# surface: that separation is enforced by construction, not by convention.
|
|
91
|
+
assert not hasattr(LightDeviceClient, "has_live_session")
|
|
92
|
+
|
|
93
|
+
|
|
61
94
|
@pytest.mark.asyncio
|
|
62
95
|
async def test_no_session_is_not_an_empty_card():
|
|
63
96
|
# None, not []. A caller cannot tell "could not ask" from "nothing there"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/clock.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/events.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rate.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/rtp.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/sdp.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/stats.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/_vendor/aiortc/utils.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/__init__.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/client.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/constants.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/controls.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/go2rtc.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/hwaccel.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/lan_control.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/playback.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/protocol.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/sd_events.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/sdes_open.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/webrtc.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/camera/webrtc_open.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/aidot_cameras/device_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_answer_sdp_video_pt.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_audio_mux_offline.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_avio_response_parse.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_avio_response_router.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_battery_detection.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_battery_skip_turn.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_bitrate_campaign_arms.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_bridge_state_sentinels.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_control_verdicts.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_deferred_hardening.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_device_cmd_seq_match.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_device_login_guard.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_device_user_info_cache.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_dormant_camera_cleanup.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_dtls_mux_terminal_write.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_dtls_not_ready_burst.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_dtls_resolution_ack.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_dtls_serve_decode_skip.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_dtls_serve_open_timeout.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_dtls_skip_signaling_wait.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_dtls_stdout_producer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_futile_keepalive.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_h264_decode_rate_limit.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_highport_nomination.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_ice_config_cache.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_ice_peer_reflexive.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_idle_release_both_loops.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0b5 → python_aidot_cameras-1.0.0b7}/tests/test_keyframe_prompter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|