python-aidot-cameras 1.0.0rc4__tar.gz → 1.0.0rc5__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.0rc4/python_aidot_cameras.egg-info → python_aidot_cameras-1.0.0rc5}/PKG-INFO +1 -1
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/client.py +85 -8
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/pyproject.toml +1 -1
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5/python_aidot_cameras.egg-info}/PKG-INFO +1 -1
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_camera_properties.py +64 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/LICENSE +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/MANIFEST.in +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/README.md +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/__init__.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/__main__.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_upstream.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/__init__.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/LICENSE +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/__init__.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/clock.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/codecs/__init__.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/codecs/base.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/codecs/g711.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/codecs/g722.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/codecs/h264.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/codecs/opus.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/codecs/vpx.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/contrib/__init__.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/contrib/media.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/contrib/signaling.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/events.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/exceptions.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/jitterbuffer.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/mediastreams.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/rate.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/rtcconfiguration.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/rtcdatachannel.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/rtcdtlstransport.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/rtcicetransport.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/rtcpeerconnection.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/rtcrtpparameters.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/rtcrtpreceiver.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/rtcrtpsender.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/rtcrtptransceiver.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/rtcsctptransport.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/rtcsessiondescription.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/rtp.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/sdp.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/stats.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/utils.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/ble_gateway.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/__init__.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/constants.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/controls.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/go2rtc.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/hwaccel.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/lan_control.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/models.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/playback.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/protocol.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/sd_events.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/sd_listing.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/sd_playback.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/sdes.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/sdes_open.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/tutk.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/webrtc.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/webrtc_open.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/client.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/cloud_auth.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/const.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/credentials.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/crypto.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/device_client.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/discover.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/exceptions.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/g711.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/lan_retry.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/py.typed +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/python_aidot_cameras.egg-info/SOURCES.txt +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/python_aidot_cameras.egg-info/dependency_links.txt +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/python_aidot_cameras.egg-info/entry_points.txt +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/python_aidot_cameras.egg-info/requires.txt +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/python_aidot_cameras.egg-info/top_level.txt +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/setup.cfg +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_aioice_compat.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_alarm_event.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_answer_pt_kinds.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_answer_sdp_video_pt.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_answer_section_selection.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_audio_mux_offline.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_avio_ctrl_uses_the_session_dseq.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_avio_request.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_avio_response_parse.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_avio_response_router.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_backoff.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_battery_detection.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_battery_evidence_corroboration.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_battery_skip_turn.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_bitrate_campaign_arms.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_ble_gateway.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_bridge_state_sentinels.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_busy_backoff.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_canary_log_names_the_camera.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_cloud_count.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_cloud_plan.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_constants_imports_resolve.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_control_verdicts.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_deferred_hardening.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_device_cmd_seq_match.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_device_login_guard.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_device_user_info_cache.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_direct_ts_keyframe_signal.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_direct_ts_serve.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_dormant_camera_cleanup.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_drain_is_cancellable.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_dtls_mux_terminal_write.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_dtls_not_ready_burst.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_dtls_pinning.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_dtls_resolution_ack.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_dtls_serve_decode_skip.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_dtls_serve_ice_wait.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_dtls_serve_open_timeout.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_dtls_skip_signaling_wait.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_dtls_slow_probe.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_dtls_stdout_producer.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_dtls_video_presence.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_egress_guard.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_env_seams.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_feature_probe_needs_a_live_session.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_ffmpeg_signal_exit_is_not_a_crash.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_futile_keepalive.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_gate_requires_a_decoded_frame.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_go2rtc.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_go2rtc_cli.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_h264_decode_rate_limit.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_highport_nomination.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_hwaccel_probe.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_ice_config_cache.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_ice_peer_reflexive.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_idle_release_both_loops.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_keepalive_renew.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_key_restart_sdp.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_keyframe_prompter.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_lan_control.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_lan_retry_reaches_the_storming_devices.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_late_repeats_are_dropped.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_light_active_color_mode.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_live_session_carries_talk.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_live_stream_param.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_live_validate_policy.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_live_video_canary.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_liveplay_echo_timeout.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_local_only_controls.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_logging_caps.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_login_connect_deadline.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_login_reconnect_backoff.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_media_counted_only_when_plaintext.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_media_path_is_reported.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_media_socket_rcvbuf.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_media_wait_floor.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_motion_poll.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_mqtt_client_id_is_exact.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_mqtt_credential_refetch.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_mqtt_credential_self_heal.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_mqtt_password_not_persisted.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_mqtt_session_reconnect.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_mqtt_thread_not_orphaned.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_nack.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_nack_cannot_add_load_in_a_burst.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_narrow_pc_ice.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_no_key_material_in_logs.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_no_self_referential_go2rtc.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_no_undefined_names.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_offer_bandwidth_line.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_offer_keeps_rtcp_feedback.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_offline_keepalive_pause.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_on_frame_is_an_av_frame.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_open_fail_logger.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_open_gate_delay.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_peer_id_client_type.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_peerid_reuse.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_persistent_mqtt.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_persistent_mqtt_robustness.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_playback_tls.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_post_merge_hardening.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_ptz_probe_reads_what_it_is_told.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_public_signature_matches_impl.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_publish_target_readiness.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_quality_campaign_in_session.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_reboot_action.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_recent_recordings.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_remb.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_remb_goes_out_the_relay_aware_sender.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_resent_frames_are_dropped.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_resolution_persists.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_restart_cycle.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_retry_policy.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_scripts_importable.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sctp_init_ack_tsn.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sctp_reassembly.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sctp_sack.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sd_card_presence.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sd_event_decode.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sd_event_probe_payloads.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sd_listing.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sd_playback.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_adaptive.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_avio_dispatch.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_bridge_break_gate.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_connection_mode.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_echo_wait_timeout.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_fast_liveplay.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_ffmpeg_stderr_log.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_first_media_stall_report.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_ice_teardown.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_idle_release.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_nack_bridge.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_offer_video_order.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_offer_video_pin.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_open_cleanup.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_sctp_abort.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_serve_audio.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_serve_cmd.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_serve_stderr_drain.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_session_cap.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_sprop.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_talk.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_teardown_exit_log.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_terminal_ack.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_terminal_ack_latency.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_tmmbr_bridge.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdes_watchdog.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdp_narrowing_properties.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sdp_payload_type_narrowing.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_security_hardening.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_self_peer_is_an_address_not_an_ip.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_serializable_login_info.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_serve_input_timeout.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_serve_loss_tolerance.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_serve_pt_respects_the_pin.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_serve_relay.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_serve_stderr_keeps_the_reason.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_session_stats.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_shared_house_seam.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_snapshot_cancellation.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_speak.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sprop_capture_gate.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sprop_staleness.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sprop_unstable_public.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_sprop_write_is_reported_truthfully.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_srtp_rx_rekey.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_srtp_tx_key_note.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_stable_terminal_id.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_stall_nudge_policy.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_stall_report_says_what_the_answer_carried.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_stall_report_says_whether_the_trigger_was_answered.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_stall_report_separates_silence_from_undecryptable.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_stall_report_survives_a_caller_giving_up.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_stall_reports_reach_the_artifact.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_stream_cap.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_stream_drain_ownership.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_stream_idle.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_stream_slot_cap.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_stream_teardown.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_talk.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_talk_reports_a_speaker_that_never_opened.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_tap_to_mux_end_to_end.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_terminal_ack.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_tmmbr.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_token_file_robustness.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_token_refresh.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_turn_create_permission.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_tutk_path_is_out_of_reach.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_upstream_compat.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_validator_shares_one_token.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_video_profile_log.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_video_pts_dts.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_video_ts_unwrap.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_viewer_check_throttle.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_viewer_presence.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_viewer_present_push_mode.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_wait_or_event.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_wake_gate_camera_evidence.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_wake_readiness_retry.py +0 -0
- {python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/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.0rc5
|
|
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.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/client.py
RENAMED
|
@@ -2675,12 +2675,53 @@ class CameraMixin(_CameraControlsMixin, _CameraSdMixin, _WebRTCOpenMixin, _SdesO
|
|
|
2675
2675
|
publish_items = [
|
|
2676
2676
|
(f"iot/v1/s/{user_id}/device/devActionReq", payload),
|
|
2677
2677
|
]
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2678
|
+
sub_topics = [f"iot/v1/c/{user_id}/#", f"iot/v1/cb/{user_id}/#"]
|
|
2679
|
+
|
|
2680
|
+
# Use the PERSISTENT connection when there is one, exactly as
|
|
2681
|
+
# `_mqtt_device_cmd` does. Opening a fresh session here instead looked
|
|
2682
|
+
# harmless and is not: it connects with the same `mqttClientId` the
|
|
2683
|
+
# long-lived connection is already using, and a broker evicts the older
|
|
2684
|
+
# session on a duplicate client id. Inside Home Assistant that means the
|
|
2685
|
+
# query and the integration's own connection knock each other off, and
|
|
2686
|
+
# the reply never arrives -- the request appears to work and simply
|
|
2687
|
+
# returns nothing. It only "worked" when tested with the integration
|
|
2688
|
+
# stopped, which is the one condition that hides the conflict.
|
|
2689
|
+
messages = []
|
|
2690
|
+
pm = await self._get_persistent_mqtt() if self._resolve_persistent_mqtt() else None
|
|
2691
|
+
if pm is not None:
|
|
2692
|
+
messages, _st = await pm.request(
|
|
2693
|
+
publish_items=publish_items,
|
|
2694
|
+
subscribe_topics=sub_topics,
|
|
2695
|
+
timeout=timeout,
|
|
2696
|
+
)
|
|
2697
|
+
if _st and _st.get("error"):
|
|
2698
|
+
_LOGGER.debug(
|
|
2699
|
+
"async_query_device_action: persistent MQTT failed (%s); "
|
|
2700
|
+
"falling back for %s", _st.get("error"), device_id)
|
|
2701
|
+
pm = None
|
|
2702
|
+
if pm is None:
|
|
2703
|
+
messages, _st = await _mqtt_session_with_status(
|
|
2704
|
+
mqtt_url, mqtt_user, mqtt_pwd, client_id,
|
|
2705
|
+
subscribe_topics=sub_topics,
|
|
2706
|
+
publish_items=publish_items,
|
|
2707
|
+
duration=timeout,
|
|
2708
|
+
)
|
|
2709
|
+
# Several devAction messages can be in flight on a shared connection,
|
|
2710
|
+
# and some carry no `out` at all (a bare acknowledgement). Taking the
|
|
2711
|
+
# first match therefore loses data: a camera reporting six sound
|
|
2712
|
+
# detectors surfaced only the two from whichever reply arrived first.
|
|
2713
|
+
# So prefer a reply that actually carries a payload, and only fall back
|
|
2714
|
+
# to an empty one when nothing better arrived.
|
|
2715
|
+
# Two passes' worth of preference, resolved in one:
|
|
2716
|
+
# best - right device, right action, our seq, and a payload
|
|
2717
|
+
# good - right device, right action, a payload
|
|
2718
|
+
# `seq` is a PREFERENCE, never a filter. Requiring it looked tidy and
|
|
2719
|
+
# was wrong: a camera that does not echo our seq has every one of its
|
|
2720
|
+
# replies discarded, and the call then reports "no reply" for a camera
|
|
2721
|
+
# that answered. Observed live as `no reply from <device> (4 messages
|
|
2722
|
+
# seen)` -- four candidates rejected on the seq alone.
|
|
2723
|
+
best = good = None
|
|
2724
|
+
_empty_seen = False
|
|
2684
2725
|
for topic, raw in messages:
|
|
2685
2726
|
if "devAction" not in topic:
|
|
2686
2727
|
continue
|
|
@@ -2691,8 +2732,44 @@ class CameraMixin(_CameraControlsMixin, _CameraSdMixin, _WebRTCOpenMixin, _SdesO
|
|
|
2691
2732
|
body = msg.get("payload") or {}
|
|
2692
2733
|
if body.get("action") != action:
|
|
2693
2734
|
continue
|
|
2694
|
-
|
|
2695
|
-
|
|
2735
|
+
# Match the DEVICE too. The persistent connection is subscribed to
|
|
2736
|
+
# the whole account (`iot/v1/c/{userId}/#`), so replies for every
|
|
2737
|
+
# camera arrive here -- matching on the action alone lets one
|
|
2738
|
+
# camera read another's answer. Observed live: four cameras all
|
|
2739
|
+
# reported the same SD figures, which belonged to whichever camera
|
|
2740
|
+
# answered first.
|
|
2741
|
+
# The device match IS a filter: the persistent connection carries
|
|
2742
|
+
# the whole account, and without this one camera reads another's
|
|
2743
|
+
# answer (observed live as four cameras reporting identical SD
|
|
2744
|
+
# figures).
|
|
2745
|
+
_rid = body.get("devId") or msg.get("id")
|
|
2746
|
+
if _rid and _rid != device_id:
|
|
2747
|
+
continue
|
|
2748
|
+
out = body.get("out")
|
|
2749
|
+
if out is None:
|
|
2750
|
+
_empty_seen = True
|
|
2751
|
+
continue
|
|
2752
|
+
if msg.get("seq") == seq:
|
|
2753
|
+
best = out
|
|
2754
|
+
break
|
|
2755
|
+
if good is None:
|
|
2756
|
+
good = out
|
|
2757
|
+
if best is not None:
|
|
2758
|
+
return best
|
|
2759
|
+
if good is not None:
|
|
2760
|
+
_LOGGER.debug(
|
|
2761
|
+
"devActionReq %s: accepted a reply for %s without a seq match",
|
|
2762
|
+
action, device_id)
|
|
2763
|
+
return good
|
|
2764
|
+
if _empty_seen:
|
|
2765
|
+
_LOGGER.debug("devActionReq %s: only empty replies from %s",
|
|
2766
|
+
action, device_id)
|
|
2767
|
+
return None
|
|
2768
|
+
# Never return None silently: a caller cannot tell "unsupported" from
|
|
2769
|
+
# "the reply never came back", and a silent None is what made this
|
|
2770
|
+
# failure invisible for an entire debugging session.
|
|
2771
|
+
_LOGGER.info("devActionReq %s: no reply from %s (%d messages seen)",
|
|
2772
|
+
action, device_id, len(messages))
|
|
2696
2773
|
return None
|
|
2697
2774
|
|
|
2698
2775
|
async def async_trigger_device_action(
|
|
@@ -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.0rc5"
|
|
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.0rc5
|
|
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.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_camera_properties.py
RENAMED
|
@@ -104,3 +104,67 @@ class TestSdCardInfo:
|
|
|
104
104
|
|
|
105
105
|
def test_no_reply_is_none(self):
|
|
106
106
|
assert asyncio.run(_Cam(None).async_get_sd_card_info()) is None
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class TestItDoesNotFightTheIntegrationsConnection:
|
|
110
|
+
"""The query must reuse the persistent MQTT connection when there is one.
|
|
111
|
+
|
|
112
|
+
Opening a fresh session uses the same `mqttClientId` the long-lived
|
|
113
|
+
connection already holds, and a broker evicts the older session on a
|
|
114
|
+
duplicate client id. Inside Home Assistant that means the query and the
|
|
115
|
+
integration knock each other off and the reply never arrives -- which looks
|
|
116
|
+
exactly like "this camera does not support the action".
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
def test_the_query_prefers_the_persistent_connection(self):
|
|
120
|
+
import inspect
|
|
121
|
+
from aidot_cameras.camera import client as client_mod
|
|
122
|
+
src = inspect.getsource(client_mod.CameraMixin.async_query_device_action)
|
|
123
|
+
assert "_get_persistent_mqtt" in src, (
|
|
124
|
+
"the query opens its own MQTT session again - that duplicates the "
|
|
125
|
+
"client id and the reply is lost inside Home Assistant")
|
|
126
|
+
assert "_resolve_persistent_mqtt" in src
|
|
127
|
+
|
|
128
|
+
def test_a_missing_reply_is_logged_not_silent(self):
|
|
129
|
+
import inspect
|
|
130
|
+
from aidot_cameras.camera import client as client_mod
|
|
131
|
+
src = inspect.getsource(client_mod.CameraMixin.async_query_device_action)
|
|
132
|
+
assert "no reply from" in src
|
|
133
|
+
# INFO, not debug: a silent None is what hid this failure. Find the
|
|
134
|
+
# logging call that owns the message rather than guessing at a
|
|
135
|
+
# character window near it.
|
|
136
|
+
import re
|
|
137
|
+
call = re.search(r'_LOGGER\.(\w+)\(\s*\n?\s*"[^"]*no reply from', src)
|
|
138
|
+
assert call is not None, "the no-reply message is not a logging call"
|
|
139
|
+
assert call.group(1) == "info", (
|
|
140
|
+
f"the no-reply message logs at {call.group(1)}, not info")
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class TestRepliesAreMatchedToTheRightCamera:
|
|
144
|
+
"""The persistent connection carries the whole account's traffic.
|
|
145
|
+
|
|
146
|
+
Observed live: all four cameras reported identical SD figures, because the
|
|
147
|
+
matcher keyed on the action alone and each camera accepted whichever reply
|
|
148
|
+
arrived first.
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
def test_it_filters_on_the_device_id(self):
|
|
152
|
+
import inspect
|
|
153
|
+
from aidot_cameras.camera import client as client_mod
|
|
154
|
+
src = inspect.getsource(client_mod.CameraMixin.async_query_device_action)
|
|
155
|
+
assert 'body.get("devId")' in src
|
|
156
|
+
assert "!= device_id" in src
|
|
157
|
+
|
|
158
|
+
def test_seq_is_a_preference_not_a_filter(self):
|
|
159
|
+
"""Requiring the seq discards every reply from a camera that does not
|
|
160
|
+
echo it -- seen live as "no reply (4 messages seen)" on a camera that
|
|
161
|
+
had in fact answered four times."""
|
|
162
|
+
import inspect
|
|
163
|
+
from aidot_cameras.camera import client as client_mod
|
|
164
|
+
src = inspect.getsource(client_mod.CameraMixin.async_query_device_action)
|
|
165
|
+
assert 'msg.get("seq") == seq' in src, "seq should still be preferred"
|
|
166
|
+
# The rejecting form must be gone.
|
|
167
|
+
assert "_rseq != seq" not in src
|
|
168
|
+
assert "if _rseq and" not in src
|
|
169
|
+
# A reply without a matching seq must still be usable.
|
|
170
|
+
assert "good" in src
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/__init__.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/LICENSE
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
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/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.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/rtp.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/_vendor/aiortc/sdp.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/ble_gateway.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/__init__.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/constants.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/controls.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/go2rtc.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/hwaccel.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/lan_control.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/models.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/playback.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/protocol.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/sd_events.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/sd_listing.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/sd_playback.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/sdes.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/sdes_open.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/tutk.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/webrtc.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/camera/webrtc_open.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/aidot_cameras/credentials.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/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
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_answer_pt_kinds.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_answer_sdp_video_pt.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_audio_mux_offline.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_avio_response_parse.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_avio_response_router.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_battery_detection.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_battery_skip_turn.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_bitrate_campaign_arms.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_bridge_state_sentinels.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_control_verdicts.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_deferred_hardening.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_device_cmd_seq_match.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_device_login_guard.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_device_user_info_cache.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_direct_ts_serve.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_dormant_camera_cleanup.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_drain_is_cancellable.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_dtls_not_ready_burst.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_dtls_resolution_ack.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_dtls_serve_decode_skip.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_dtls_serve_ice_wait.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_dtls_slow_probe.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_dtls_stdout_producer.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_dtls_video_presence.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_futile_keepalive.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_h264_decode_rate_limit.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_highport_nomination.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_ice_config_cache.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_ice_peer_reflexive.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_keepalive_renew.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_key_restart_sdp.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_keyframe_prompter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_live_stream_param.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_live_validate_policy.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_live_video_canary.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_liveplay_echo_timeout.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_local_only_controls.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_login_connect_deadline.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_media_path_is_reported.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_media_socket_rcvbuf.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc4 → python_aidot_cameras-1.0.0rc5}/tests/test_media_wait_floor.py
RENAMED
|
File without changes
|