python-aidot-cameras 1.0.0rc13__tar.gz → 1.0.0rc15__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.0rc13/python_aidot_cameras.egg-info → python_aidot_cameras-1.0.0rc15}/PKG-INFO +1 -1
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/client.py +82 -13
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/controls.py +43 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/pyproject.toml +1 -1
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15/python_aidot_cameras.egg-info}/PKG-INFO +1 -1
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/python_aidot_cameras.egg-info/SOURCES.txt +2 -0
- python_aidot_cameras-1.0.0rc15/tests/test_auto_alarm.py +70 -0
- python_aidot_cameras-1.0.0rc15/tests/test_peer_id_class_env.py +62 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_peer_id_client_type.py +37 -36
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/LICENSE +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/MANIFEST.in +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/README.md +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/__init__.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/__main__.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_upstream.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/__init__.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/LICENSE +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/__init__.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/clock.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/codecs/__init__.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/codecs/base.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/codecs/g711.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/codecs/g722.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/codecs/h264.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/codecs/opus.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/codecs/vpx.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/contrib/__init__.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/contrib/media.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/contrib/signaling.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/events.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/exceptions.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/jitterbuffer.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/mediastreams.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/rate.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/rtcconfiguration.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/rtcdatachannel.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/rtcdtlstransport.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/rtcicetransport.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/rtcpeerconnection.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/rtcrtpparameters.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/rtcrtpreceiver.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/rtcrtpsender.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/rtcrtptransceiver.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/rtcsctptransport.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/rtcsessiondescription.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/rtp.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/sdp.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/stats.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/aiortc/utils.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/ble_gateway.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/__init__.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/constants.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/go2rtc.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/hwaccel.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/lan_control.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/models.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/playback.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/protocol.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/sd_events.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/sd_listing.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/sd_playback.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/sdes.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/sdes_open.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/tutk.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/webrtc.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/webrtc_open.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/client.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/cloud_auth.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/const.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/credentials.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/crypto.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/device_client.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/discover.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/exceptions.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/g711.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/lan_retry.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/py.typed +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/python_aidot_cameras.egg-info/dependency_links.txt +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/python_aidot_cameras.egg-info/entry_points.txt +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/python_aidot_cameras.egg-info/requires.txt +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/python_aidot_cameras.egg-info/top_level.txt +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/setup.cfg +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_abandoned_attempt_skips_the_doomed_serve.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_abandoned_wait_still_maps_audio.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_aioice_compat.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_alarm_event.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_answer_pt_kinds.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_answer_sdp_video_pt.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_answer_section_selection.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_audio_mux_offline.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_avio_ctrl_uses_the_session_dseq.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_avio_request.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_avio_response_parse.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_avio_response_router.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_backoff.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_battery_detection.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_battery_evidence_corroboration.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_battery_skip_turn.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_battery_stale_offer_is_abandoned_early.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_battery_wake_gate_app_parity.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_bitrate_campaign_arms.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_ble_gateway.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_bridge_state_sentinels.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_busy_backoff.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_camera_properties.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_canary_log_names_the_camera.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_cloud_count.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_cloud_plan.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_constants_imports_resolve.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_control_verdicts.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_deferred_hardening.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_detection_types.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_device_cmd_seq_match.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_device_login_guard.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_device_user_info_cache.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_direct_ts_keyframe_signal.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_direct_ts_serve.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_dormant_camera_cleanup.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_drain_is_cancellable.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_dtls_mux_terminal_write.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_dtls_not_ready_burst.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_dtls_pinning.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_dtls_resolution_ack.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_dtls_serve_decode_skip.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_dtls_serve_ice_wait.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_dtls_serve_open_timeout.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_dtls_skip_signaling_wait.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_dtls_slow_probe.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_dtls_stdout_producer.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_dtls_video_presence.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_egress_guard.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_env_seams.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_feature_probe_needs_a_live_session.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_ffmpeg_signal_exit_is_not_a_crash.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_futile_keepalive.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_gate_requires_a_decoded_frame.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_go2rtc.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_go2rtc_cli.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_h264_decode_rate_limit.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_highport_nomination.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_hwaccel_probe.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_ice_config_cache.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_ice_credentials_match_the_app.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_ice_peer_reflexive.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_idle_release_both_loops.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_keepalive_renew.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_key_restart_sdp.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_keyframe_prompter.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_lan_control.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_lan_retry_reaches_the_storming_devices.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_late_repeats_are_dropped.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_light_active_color_mode.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_live_session_carries_talk.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_live_stream_param.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_live_validate_policy.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_live_video_canary.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_liveplay_echo_timeout.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_local_only_controls.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_logging_caps.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_login_connect_deadline.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_login_reconnect_backoff.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_media_counted_only_when_plaintext.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_media_path_is_reported.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_media_socket_rcvbuf.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_media_wait_floor.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_motion_poll.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_mqtt_client_id_is_exact.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_mqtt_credential_refetch.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_mqtt_credential_self_heal.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_mqtt_password_not_persisted.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_mqtt_session_reconnect.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_mqtt_thread_not_orphaned.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_nack.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_nack_cannot_add_load_in_a_burst.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_narrow_pc_ice.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_no_key_material_in_logs.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_no_self_referential_go2rtc.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_no_unbounded_hex_dumps.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_no_undefined_names.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_nominates_as_soon_as_the_answer_lands.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_off_subnet_candidate.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_offer_bandwidth_line.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_offer_keeps_rtcp_feedback.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_offline_keepalive_pause.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_on_frame_is_an_av_frame.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_open_fail_logger.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_open_gate_delay.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_peerid_reuse.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_persistent_mqtt.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_persistent_mqtt_robustness.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_playback_tls.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_post_merge_hardening.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_ptz_probe_reads_what_it_is_told.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_public_signature_matches_impl.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_publish_target_readiness.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_quality_campaign_in_session.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_reboot_action.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_recent_recordings.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_remb.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_remb_goes_out_the_relay_aware_sender.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_resent_frames_are_dropped.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_resolution_persists.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_restart_cycle.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_retry_policy.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_scripts_importable.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sctp_init_ack_tsn.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sctp_reassembly.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sctp_sack.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sd_card_presence.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sd_event_decode.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sd_event_probe_payloads.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sd_listing.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sd_playback.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_adaptive.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_avio_dispatch.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_bridge_break_gate.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_connection_mode.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_echo_wait_is_not_dead_time.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_fast_liveplay.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_ffmpeg_stderr_log.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_first_media_stall_report.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_ice_teardown.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_idle_release.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_nack_bridge.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_offer_video_order.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_offer_video_pin.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_open_cleanup.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_sctp_abort.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_serve_audio.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_serve_cmd.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_serve_stderr_drain.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_session_cap.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_sprop.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_talk.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_teardown_exit_log.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_terminal_ack.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_terminal_ack_latency.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_tmmbr_bridge.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdes_watchdog.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdp_narrowing_properties.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sdp_payload_type_narrowing.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_security_hardening.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_self_peer_is_an_address_not_an_ip.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_serializable_login_info.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_serve_input_timeout.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_serve_loss_tolerance.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_serve_pt_respects_the_pin.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_serve_relay.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_serve_stderr_keeps_the_reason.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_session_stats.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_shared_house_seam.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_signalling_is_attributable_to_a_camera.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_snapshot_cancellation.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_speak.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sprop_capture_gate.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sprop_staleness.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sprop_unstable_public.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_sprop_write_is_reported_truthfully.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_srtp_rx_rekey.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_srtp_tx_key_note.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_stable_terminal_id.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_stall_nudge_policy.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_stall_report_says_what_the_answer_carried.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_stall_report_says_whether_the_trigger_was_answered.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_stall_report_separates_silence_from_undecryptable.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_stall_report_survives_a_caller_giving_up.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_stall_reports_reach_the_artifact.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_status_and_trace_channels.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_stream_cap.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_stream_drain_ownership.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_stream_idle.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_stream_slot_cap.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_stream_teardown.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_talk.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_talk_reports_a_speaker_that_never_opened.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_tap_to_mux_end_to_end.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_terminal_ack.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_tmmbr.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_token_file_robustness.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_token_refresh.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_transport_failure_reporting.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_turn_create_permission.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_tutk_path_is_out_of_reach.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_unbuildable_summary_repeats_at_debug.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_upstream_compat.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_validator_shares_one_token.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_video_profile_log.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_video_pts_dts.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_video_ts_unwrap.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_viewer_check_throttle.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_viewer_presence.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_viewer_present_push_mode.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_wait_or_event.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_wake_gate_camera_evidence.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_wake_readiness_retry.py +0 -0
- {python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/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.
|
|
3
|
+
Version: 1.0.0rc15
|
|
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.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/client.py
RENAMED
|
@@ -1163,6 +1163,37 @@ def _stable_terminal_id(seed: "Optional[str]" = None, width: int = 6) -> str:
|
|
|
1163
1163
|
EXPT_PEERID_FILE = os.environ.get("AIDOT_EXPT_PEERID_FILE")
|
|
1164
1164
|
|
|
1165
1165
|
|
|
1166
|
+
#: Screening knob for the peer id's CLIENT CLASS only, off unless set.
|
|
1167
|
+
#: `_expt_peer_id_fields` covers the same ground but reads a file, and the
|
|
1168
|
+
#: live-validation harness can only pass environment variables - so an arm that
|
|
1169
|
+
#: has to run there needs this route.
|
|
1170
|
+
EXPT_PEERID_CLASS = "AIDOT_EXPT_PEERID_CLASS"
|
|
1171
|
+
|
|
1172
|
+
|
|
1173
|
+
def _expt_peer_id_class():
|
|
1174
|
+
"""One hex character to pin field 2's first character, or None.
|
|
1175
|
+
|
|
1176
|
+
The camera takes its client class from that character
|
|
1177
|
+
(``client_type = field2[0] - '0'``). Pinning it to ``'0'`` (APP_ANDROID)
|
|
1178
|
+
was tried and reverted - it breaks the A000088, which accepts the session
|
|
1179
|
+
and then sends nothing. The vendor WEB app sends ``'2'``, and that arm has
|
|
1180
|
+
never been screened; this exists to screen it.
|
|
1181
|
+
|
|
1182
|
+
**Pins the class character and nothing else.** The three trailing integers
|
|
1183
|
+
encode transport, and an SDES tail pushed at a DTLS camera is silently
|
|
1184
|
+
discarded - the failure mode an earlier unscoped knob produced. Those stay
|
|
1185
|
+
exactly as computed.
|
|
1186
|
+
|
|
1187
|
+
Fails closed: anything that is not a single hex digit is ignored rather
|
|
1188
|
+
than applied, because a peer id of the wrong shape is rejected outright and
|
|
1189
|
+
would break every open rather than one arm of an experiment.
|
|
1190
|
+
"""
|
|
1191
|
+
raw = (os.environ.get(EXPT_PEERID_CLASS) or "").strip()
|
|
1192
|
+
if len(raw) != 1 or raw not in "0123456789abcdef":
|
|
1193
|
+
return None
|
|
1194
|
+
return raw
|
|
1195
|
+
|
|
1196
|
+
|
|
1166
1197
|
def _expt_peer_id_fields(device_id=None, path=None):
|
|
1167
1198
|
"""Experiment override for one device's peer-id fields, or None.
|
|
1168
1199
|
|
|
@@ -6518,8 +6549,11 @@ class CameraMixin(_CameraControlsMixin, _CameraSdMixin, _WebRTCOpenMixin, _SdesO
|
|
|
6518
6549
|
# the vendor Android app hard-codes '0' there and the vendor web app
|
|
6519
6550
|
# sends '2'. That is all verified.
|
|
6520
6551
|
#
|
|
6521
|
-
# **
|
|
6522
|
-
#
|
|
6552
|
+
# **Pinning it to '0' breaks the A000088. Pinning it to '2' does not.**
|
|
6553
|
+
# The two are not interchangeable and the earlier note here said only
|
|
6554
|
+
# "pinning it breaks the A000088", which over-generalised one arm.
|
|
6555
|
+
#
|
|
6556
|
+
# Setting this character to '0' (APP_ANDROID) for every camera was
|
|
6523
6557
|
# tried and reverted on 2026-08-31. Against a drained fleet with Home
|
|
6524
6558
|
# Assistant stopped, three A000088 cameras went 0 for 3 over nine
|
|
6525
6559
|
# attempts - each one completing its DTLS handshake in under two seconds
|
|
@@ -6527,23 +6561,58 @@ class CameraMixin(_CameraControlsMixin, _CameraSdMixin, _WebRTCOpenMixin, _SdesO
|
|
|
6527
6561
|
# A001064 on the first attempt. Reverting THIS LINE ALONE, with every
|
|
6528
6562
|
# other change of that release left in place, restored 3 of 3.
|
|
6529
6563
|
#
|
|
6530
|
-
# The A000088 is a different SoC (Realtek
|
|
6531
|
-
# image
|
|
6532
|
-
#
|
|
6533
|
-
#
|
|
6534
|
-
#
|
|
6564
|
+
# The A000088 is a different SoC (Realtek RTL8735B / AmebaPro2). Its
|
|
6565
|
+
# image WAS disassembled on 2026-09-07, and it has **no client class at
|
|
6566
|
+
# all**: `lds_process_peerid` in rtc_mqtt.c parses
|
|
6567
|
+
# `%[^_]_%[^_]_%02hhd_%02hhd_%02hhd`, keeps field 2 as an opaque string
|
|
6568
|
+
# at buf+0x32, and never derives a class from its first character. It
|
|
6569
|
+
# also treats a parse failure as NON-fatal - it substitutes the literal
|
|
6570
|
+
# "userid" for field 2 and continues. Only field 1 is compared, against
|
|
6571
|
+
# "google". So `client_type = field2[0]-'0'` and "a malformed peer id is
|
|
6572
|
+
# rejected outright" are BOTH A001064-only; do not generalise either.
|
|
6573
|
+
#
|
|
6574
|
+
# Which leaves the class-0 result real but unexplained: 0 of 9 is not
|
|
6575
|
+
# luck against a 29-of-30 baseline, yet the firmware has nothing there
|
|
6576
|
+
# to read. Do not repeat "class 0 breaks it because it announces
|
|
6577
|
+
# APP_ANDROID" - that story is not supported by the image.
|
|
6578
|
+
#
|
|
6579
|
+
# Class '2' (WEB) - what the vendor web app sends - was screened on
|
|
6580
|
+
# 2026-09-07 and does NOT break it: 6 of 6 A000088 cameras PASS across
|
|
6581
|
+
# two independent live-validation runs, nearly all on the first
|
|
6582
|
+
# attempt, against 0 of 9 attempts for class '0'. For reference the
|
|
6583
|
+
# random character's own baseline is 29/30 over ten runs, so the 0/9
|
|
6584
|
+
# was never background flakiness.
|
|
6585
|
+
#
|
|
6586
|
+
# SHIPPED as '2' on 2026-09-07, after the arm was also run under the
|
|
6587
|
+
# exact condition the class-0 failure was found in: Home Assistant's
|
|
6588
|
+
# config entry disabled so nothing held a session, fleet drained, opens
|
|
6589
|
+
# driven from the LAN runner. Class 2 took 3 of 3 A000088 cameras on
|
|
6590
|
+
# the first attempt there, against 0 of 9 attempts for class 0. Nine of
|
|
6591
|
+
# nine across all screening runs.
|
|
6535
6592
|
#
|
|
6536
|
-
#
|
|
6537
|
-
# was
|
|
6538
|
-
#
|
|
6539
|
-
#
|
|
6540
|
-
#
|
|
6541
|
-
|
|
6593
|
+
# Announcing a truthful class beats announcing a random hex digit that
|
|
6594
|
+
# was out of range 11 times in 16. Note class 2 is NOT one of the
|
|
6595
|
+
# smart-home classes (3, 4) that skip the camera's keepalive watchdog,
|
|
6596
|
+
# so this changes what we claim to be and nothing about how the session
|
|
6597
|
+
# is policed. Screen any future change with `_expt_peer_id_class` (env)
|
|
6598
|
+
# or `_expt_peer_id_fields` (file, per device), and get evidence from
|
|
6599
|
+
# EACH model - the A000088's firmware has never been disassembled.
|
|
6600
|
+
# Field 2 = client class + 5 random. '2' is EN_WEBRTC_CLIENT_TYPE_WEB,
|
|
6601
|
+
# what the vendor web app announces. See the note above for why this
|
|
6602
|
+
# is '2' and never '0'. The tail stays random: pinning all six would
|
|
6603
|
+
# make every peer id identical across opens, which is the cross-session
|
|
6604
|
+
# reuse the camera dedups on.
|
|
6605
|
+
rand6 = "2" + os.urandom(3).hex()[1:]
|
|
6542
6606
|
version = 1 if sdes else 2
|
|
6543
6607
|
# Experiment override (off by default, fails closed, scoped to one
|
|
6544
6608
|
# device): the camera reads its client class from field 2's first
|
|
6545
6609
|
# character - see _expt_peer_id_fields. Without a device id the
|
|
6546
6610
|
# override never applies.
|
|
6611
|
+
# Class-only screening knob (env), applied before the file override so
|
|
6612
|
+
# a file naming this device still wins.
|
|
6613
|
+
_cls = _expt_peer_id_class()
|
|
6614
|
+
if _cls:
|
|
6615
|
+
rand6 = _cls + rand6[1:]
|
|
6547
6616
|
_fields = _expt_peer_id_fields(device_id)
|
|
6548
6617
|
if _fields is not None:
|
|
6549
6618
|
_term, live_type, stream_id, version = _fields
|
{python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/controls.py
RENAMED
|
@@ -190,6 +190,49 @@ class _CameraControlsMixin:
|
|
|
190
190
|
return await self.async_trigger_device_action(
|
|
191
191
|
"setRoiHuman", {"roi": [payload]})
|
|
192
192
|
|
|
193
|
+
#: The siren's automatic trigger. ``autoAlarm`` is the master; the other
|
|
194
|
+
#: two say what sets it off. Probed 2026-09-07: NOT a mirror of anything
|
|
195
|
+
#: else we expose -- on an A000088 ``motionDetection`` read 0 while the
|
|
196
|
+
#: camera's own motion-detection setting read True, and on an A001064
|
|
197
|
+
#: ``humanDetect`` read 0 while ``getRoiHuman``'s read 1.
|
|
198
|
+
AUTO_ALARM_KEYS = ("autoAlarm", "motionDetection", "humanDetect")
|
|
199
|
+
|
|
200
|
+
async def async_get_auto_alarm(self) -> "Optional[dict]":
|
|
201
|
+
"""Siren auto-trigger settings as ``{key: bool}``.
|
|
202
|
+
|
|
203
|
+
``None`` means the camera did not answer -- unknown, NOT all-off.
|
|
204
|
+
Reporting an unanswered camera as three disarmed switches would claim a
|
|
205
|
+
state it never gave, and this is the setting that decides whether a
|
|
206
|
+
siren goes off in someone's house.
|
|
207
|
+
"""
|
|
208
|
+
out = await self.async_query_device_action("getAutoAlarm")
|
|
209
|
+
if not isinstance(out, list) or not out or not isinstance(out[0], dict):
|
|
210
|
+
return None
|
|
211
|
+
return {k: bool(v) for k, v in out[0].items()}
|
|
212
|
+
|
|
213
|
+
async def async_set_auto_alarm(self, key: str, enabled: bool) -> bool:
|
|
214
|
+
"""Arm or disarm the siren's automatic trigger, or one of its causes.
|
|
215
|
+
|
|
216
|
+
Read-modify-write against the camera's own object. That matters more
|
|
217
|
+
here than anywhere else: rebuilding the payload from our own key list
|
|
218
|
+
could arm ``autoAlarm`` as a side effect of changing a trigger, which
|
|
219
|
+
would make a camera start sounding a siren nobody asked it to.
|
|
220
|
+
"""
|
|
221
|
+
out = await self.async_query_device_action("getAutoAlarm")
|
|
222
|
+
if not isinstance(out, list) or not out or not isinstance(out[0], dict):
|
|
223
|
+
_LOGGER.warning(
|
|
224
|
+
"auto alarm: %s did not report its settings; not writing",
|
|
225
|
+
self.device_id)
|
|
226
|
+
return False
|
|
227
|
+
current = out[0]
|
|
228
|
+
if key not in current:
|
|
229
|
+
_LOGGER.warning("auto alarm: %s does not report %r",
|
|
230
|
+
self.device_id, key)
|
|
231
|
+
return False
|
|
232
|
+
payload = dict(current)
|
|
233
|
+
payload[key] = 1 if enabled else 0
|
|
234
|
+
return await self.async_trigger_device_action("setAutoAlarm", [payload])
|
|
235
|
+
|
|
193
236
|
async def async_get_wifi_info(self) -> "Optional[dict]":
|
|
194
237
|
"""``{"ssid": str, "rssi": int}`` for the camera's own WiFi link.
|
|
195
238
|
|
|
@@ -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.0rc15"
|
|
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.
|
|
3
|
+
Version: 1.0.0rc15
|
|
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
|
|
@@ -83,6 +83,7 @@ tests/test_answer_pt_kinds.py
|
|
|
83
83
|
tests/test_answer_sdp_video_pt.py
|
|
84
84
|
tests/test_answer_section_selection.py
|
|
85
85
|
tests/test_audio_mux_offline.py
|
|
86
|
+
tests/test_auto_alarm.py
|
|
86
87
|
tests/test_avio_ctrl_uses_the_session_dseq.py
|
|
87
88
|
tests/test_avio_request.py
|
|
88
89
|
tests/test_avio_response_parse.py
|
|
@@ -180,6 +181,7 @@ tests/test_offline_keepalive_pause.py
|
|
|
180
181
|
tests/test_on_frame_is_an_av_frame.py
|
|
181
182
|
tests/test_open_fail_logger.py
|
|
182
183
|
tests/test_open_gate_delay.py
|
|
184
|
+
tests/test_peer_id_class_env.py
|
|
183
185
|
tests/test_peer_id_client_type.py
|
|
184
186
|
tests/test_peerid_reuse.py
|
|
185
187
|
tests/test_persistent_mqtt.py
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""The siren's automatic trigger: on/off, and what sets it off.
|
|
2
|
+
|
|
3
|
+
`getAutoAlarm` returns `[{autoAlarm, motionDetection, humanDetect}]` -- a master
|
|
4
|
+
switch plus which events fire it. Probed on real hardware 2026-09-07, and it is
|
|
5
|
+
NOT a mirror of anything already exposed: on an A000088 its `motionDetection`
|
|
6
|
+
read 0 while the camera's own motion-detection setting read True, and on an
|
|
7
|
+
A001064 its `humanDetect` read 0 while `getRoiHuman`'s read 1. Two independent
|
|
8
|
+
settings that happen to share field names.
|
|
9
|
+
|
|
10
|
+
Same discipline as the detection types: read-modify-write against the camera's
|
|
11
|
+
own object so a flag we do not model cannot be reset, and a camera that does not
|
|
12
|
+
answer is unknown rather than all-off.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import asyncio
|
|
16
|
+
|
|
17
|
+
from aidot_cameras.camera.controls import _CameraControlsMixin
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class _Cam(_CameraControlsMixin):
|
|
21
|
+
device_id = "cam1"
|
|
22
|
+
|
|
23
|
+
def __init__(self, out):
|
|
24
|
+
self._out = out
|
|
25
|
+
self.triggered: list = []
|
|
26
|
+
|
|
27
|
+
async def async_query_device_action(self, action, params=None, **kw):
|
|
28
|
+
return self._out
|
|
29
|
+
|
|
30
|
+
async def async_trigger_device_action(self, action, params=None, **kw):
|
|
31
|
+
self.triggered.append((action, params))
|
|
32
|
+
return True
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
_LIVE = [{"autoAlarm": 0, "motionDetection": 0, "humanDetect": 1}]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_reads_the_master_and_both_triggers():
|
|
39
|
+
got = asyncio.run(_Cam(_LIVE).async_get_auto_alarm())
|
|
40
|
+
assert got == {"autoAlarm": False, "motionDetection": False,
|
|
41
|
+
"humanDetect": True}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_a_camera_that_does_not_answer_is_unknown_not_all_off():
|
|
45
|
+
assert asyncio.run(_Cam(None).async_get_auto_alarm()) is None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_setting_one_flag_preserves_the_others():
|
|
49
|
+
"""Turning a trigger on must not silently disarm the master, or arm it."""
|
|
50
|
+
cam = _Cam(_LIVE)
|
|
51
|
+
assert asyncio.run(cam.async_set_auto_alarm("motionDetection", True)) is True
|
|
52
|
+
action, payload = cam.triggered[0]
|
|
53
|
+
assert action == "setAutoAlarm"
|
|
54
|
+
row = payload[0]
|
|
55
|
+
assert row["motionDetection"] == 1 # the one we changed
|
|
56
|
+
assert row["autoAlarm"] == 0 # master untouched - still disarmed
|
|
57
|
+
assert row["humanDetect"] == 1 # other trigger untouched
|
|
58
|
+
assert set(row) == set(_LIVE[0])
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_a_key_the_camera_does_not_report_is_refused():
|
|
62
|
+
cam = _Cam(_LIVE)
|
|
63
|
+
assert asyncio.run(cam.async_set_auto_alarm("sirenDetect", True)) is False
|
|
64
|
+
assert cam.triggered == []
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_no_write_when_the_camera_will_not_say_what_it_has():
|
|
68
|
+
cam = _Cam(None)
|
|
69
|
+
assert asyncio.run(cam.async_set_auto_alarm("autoAlarm", True)) is False
|
|
70
|
+
assert cam.triggered == []
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""An env-var route to pinning the peer id's client class, for screening only.
|
|
2
|
+
|
|
3
|
+
The camera reads its client class from field 2's first character. Pinning it to
|
|
4
|
+
'0' (APP_ANDROID) was tried and reverted: three A000088 cameras went 0 for 3
|
|
5
|
+
over nine attempts, each completing DTLS and then receiving no media. That
|
|
6
|
+
finding is real - checked against ten live-validation runs, the A000088 is
|
|
7
|
+
29/30 PASS with the random character, so a 3% baseline cannot explain 0/9.
|
|
8
|
+
|
|
9
|
+
But it only ever tested '0'. The vendor WEB app sends '2' (WEB), and that arm
|
|
10
|
+
has never been run. `_expt_peer_id_fields` exists to screen this, and it reads
|
|
11
|
+
a FILE - which the live-validation harness cannot write, it only passes env
|
|
12
|
+
vars. This is that route and nothing more.
|
|
13
|
+
|
|
14
|
+
It pins ONLY the class character. It deliberately cannot touch the three
|
|
15
|
+
trailing integers: those encode transport, and an SDES tail pushed at a DTLS
|
|
16
|
+
camera is silently discarded, which is how an earlier unscoped knob forced a
|
|
17
|
+
battery camera to ~30x its wake rate.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from aidot_cameras.camera.client import _expt_peer_id_class
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_unset_means_no_override(monkeypatch):
|
|
24
|
+
monkeypatch.delenv("AIDOT_EXPT_PEERID_CLASS", raising=False)
|
|
25
|
+
assert _expt_peer_id_class() is None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_a_single_hex_digit_is_accepted(monkeypatch):
|
|
29
|
+
monkeypatch.setenv("AIDOT_EXPT_PEERID_CLASS", "2")
|
|
30
|
+
assert _expt_peer_id_class() == "2"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_whitespace_is_tolerated(monkeypatch):
|
|
34
|
+
monkeypatch.setenv("AIDOT_EXPT_PEERID_CLASS", " 2 ")
|
|
35
|
+
assert _expt_peer_id_class() == "2"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_anything_that_would_change_the_id_shape_is_refused(monkeypatch):
|
|
39
|
+
"""A wrong width changes the peer id's length and the camera rejects a
|
|
40
|
+
malformed id outright - fail closed rather than break every open."""
|
|
41
|
+
for bad in ("22", "", "zz", "0x2", "-1"):
|
|
42
|
+
monkeypatch.setenv("AIDOT_EXPT_PEERID_CLASS", bad)
|
|
43
|
+
assert _expt_peer_id_class() is None, bad
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_it_pins_only_the_first_character(monkeypatch):
|
|
47
|
+
"""The other five stay random: pinning all six would make every peer id
|
|
48
|
+
identical across opens, which is cross-session REUSE and would confound the
|
|
49
|
+
class with the camera's session-dedup path."""
|
|
50
|
+
from aidot_cameras.camera.client import CameraMixin
|
|
51
|
+
monkeypatch.setenv("AIDOT_EXPT_PEERID_CLASS", "2")
|
|
52
|
+
monkeypatch.delenv("AIDOT_EXPT_PEERID_FILE", raising=False)
|
|
53
|
+
|
|
54
|
+
# staticmethod: no instance, and device_id left unset so only the env knob
|
|
55
|
+
# can act (the file override needs a device id and must not interfere).
|
|
56
|
+
ids = {CameraMixin.generate_webrtc_peer_id(live_type=2, stream_id=0)
|
|
57
|
+
for _ in range(12)}
|
|
58
|
+
fields = [i.split("_") for i in ids]
|
|
59
|
+
assert all(f[1][0] == "2" for f in fields), "class not pinned"
|
|
60
|
+
assert len({f[1][1:] for f in fields}) > 1, "tail stopped being random"
|
|
61
|
+
# and the transport digits are untouched by this knob
|
|
62
|
+
assert all(f[2:] == ["2", "0", "2"] for f in fields), fields[0]
|
{python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/tests/test_peer_id_client_type.py
RENAMED
|
@@ -176,31 +176,50 @@ class TestEveryCallSitePassesADeviceId:
|
|
|
176
176
|
)
|
|
177
177
|
|
|
178
178
|
|
|
179
|
-
class
|
|
180
|
-
"""
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
The
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
179
|
+
class TestTheClassCharacterIsWeb:
|
|
180
|
+
"""Field 2's first character announces the client class, and we announce
|
|
181
|
+
'2' (WEB).
|
|
182
|
+
|
|
183
|
+
The history matters, because the obvious reading of it is wrong. Pinning
|
|
184
|
+
this character to '0' (APP_ANDROID) was tried on 2026-08-31 and reverted:
|
|
185
|
+
against a drained fleet with Home Assistant stopped, three A000088 cameras
|
|
186
|
+
went 0 for 3 over nine attempts, each completing DTLS in under two seconds
|
|
187
|
+
and then receiving no media. That is real - across ten live-validation runs
|
|
188
|
+
the A000088 is 29 of 30 with a random character, so a 3% baseline cannot
|
|
189
|
+
explain 0 of 9.
|
|
190
|
+
|
|
191
|
+
But it was recorded as "pinning the class breaks the A000088", which is one
|
|
192
|
+
arm generalised into a rule. The vendor WEB app sends '2', and that arm was
|
|
193
|
+
screened on 2026-09-07 through live-validate - same sha, only the
|
|
194
|
+
environment differing - at **6 of 6 A000088 cameras across two independent
|
|
195
|
+
runs**, nearly all on the first attempt.
|
|
196
|
+
|
|
197
|
+
So the class is pinned to WEB, which is what a known-good vendor client
|
|
198
|
+
announces, instead of a random hex digit that was out of range 11 times in
|
|
199
|
+
16. Class 2 is NOT one of the smart-home classes (3 and 4) that skip the
|
|
200
|
+
camera's keepalive watchdog, so this changes what we claim to be and
|
|
201
|
+
nothing about how the session is policed.
|
|
193
202
|
"""
|
|
194
203
|
|
|
195
|
-
def
|
|
204
|
+
def test_field2_announces_client_class_2(self, tmp_path, monkeypatch):
|
|
196
205
|
monkeypatch.setattr(client_mod, "EXPT_PEERID_FILE", None)
|
|
206
|
+
monkeypatch.delenv("AIDOT_EXPT_PEERID_CLASS", raising=False)
|
|
197
207
|
firsts = {_gen(device_id=DEV).split("_")[1][0] for _ in range(120)}
|
|
198
|
-
assert
|
|
199
|
-
"field 2's first character is
|
|
200
|
-
"
|
|
208
|
+
assert firsts == {"2"}, (
|
|
209
|
+
"field 2's first character is the client class; it must announce "
|
|
210
|
+
f"WEB ('2'), got {sorted(firsts)}")
|
|
211
|
+
|
|
212
|
+
def test_the_rest_of_field2_stays_random(self, tmp_path, monkeypatch):
|
|
213
|
+
"""Pinning all six would make every peer id identical across opens -
|
|
214
|
+
cross-session reuse, which the camera dedups on."""
|
|
215
|
+
monkeypatch.setattr(client_mod, "EXPT_PEERID_FILE", None)
|
|
216
|
+
monkeypatch.delenv("AIDOT_EXPT_PEERID_CLASS", raising=False)
|
|
217
|
+
tails = {_gen(device_id=DEV).split("_")[1][1:] for _ in range(40)}
|
|
218
|
+
assert len(tails) > 1, "field 2 is fully pinned - that is peer-id reuse"
|
|
201
219
|
|
|
202
220
|
def test_field2_is_still_six_hex(self, tmp_path, monkeypatch):
|
|
203
221
|
monkeypatch.setattr(client_mod, "EXPT_PEERID_FILE", None)
|
|
222
|
+
monkeypatch.delenv("AIDOT_EXPT_PEERID_CLASS", raising=False)
|
|
204
223
|
for _ in range(20):
|
|
205
224
|
f2 = _gen(device_id=DEV).split("_")[1]
|
|
206
225
|
assert len(f2) == 6 and all(c in "0123456789abcdef" for c in f2)
|
|
@@ -211,21 +230,3 @@ class TestTheClassCharacterMustStayRandom:
|
|
|
211
230
|
f = tmp_path / "pid"; f.write_text(f"{DEV}:4_2_0_1")
|
|
212
231
|
monkeypatch.setattr(client_mod, "EXPT_PEERID_FILE", str(f))
|
|
213
232
|
assert _gen(device_id=DEV).split("_")[1][0] == "4"
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
class TestTheOverrideIsOffInAPublishedLibrary:
|
|
217
|
-
"""Same rule as the session cap: no env var, no file I/O."""
|
|
218
|
-
|
|
219
|
-
def test_no_file_is_opened_when_the_knob_is_unset(self, monkeypatch):
|
|
220
|
-
monkeypatch.setattr(client_mod, "EXPT_PEERID_FILE", None)
|
|
221
|
-
opened = []
|
|
222
|
-
real_open = open
|
|
223
|
-
|
|
224
|
-
def _spy(*a, **k):
|
|
225
|
-
opened.append(a[0] if a else None)
|
|
226
|
-
return real_open(*a, **k)
|
|
227
|
-
|
|
228
|
-
monkeypatch.setattr("builtins.open", _spy)
|
|
229
|
-
pid = _gen(device_id=DEV)
|
|
230
|
-
assert SHAPE.match(pid).groups() == ("2", "0", "1")
|
|
231
|
-
assert opened == [], f"opened {opened!r} with the knob off"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_upstream.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/_vendor/__init__.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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/ble_gateway.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/__init__.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/constants.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/go2rtc.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/hwaccel.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/models.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/playback.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/protocol.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/sd_events.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/sd_listing.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/sdes.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/sdes_open.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/tutk.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/camera/webrtc.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/cloud_auth.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/credentials.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/device_client.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/exceptions.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc13 → python_aidot_cameras-1.0.0rc15}/aidot_cameras/lan_retry.py
RENAMED
|
File without changes
|
|
File without changes
|