python-aidot-cameras 0.17.2__tar.gz → 0.17.3b2__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-0.17.2/python_aidot_cameras.egg-info → python_aidot_cameras-0.17.3b2}/PKG-INFO +35 -1
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/README.md +34 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/client.py +24 -3
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/lan_control.py +26 -2
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/sdes_open.py +424 -128
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/webrtc_open.py +51 -16
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/pyproject.toml +1 -1
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2/python_aidot_cameras.egg-info}/PKG-INFO +35 -1
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/python_aidot_cameras.egg-info/SOURCES.txt +8 -1
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_futile_keepalive.py +35 -4
- python_aidot_cameras-0.17.3b2/tests/test_key_restart_sdp.py +195 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_lan_control.py +61 -0
- python_aidot_cameras-0.17.3b2/tests/test_media_counted_only_when_plaintext.py +237 -0
- python_aidot_cameras-0.17.3b2/tests/test_no_key_material_in_logs.py +140 -0
- python_aidot_cameras-0.17.3b2/tests/test_sctp_init_ack_tsn.py +94 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_bridge_break_gate.py +74 -10
- python_aidot_cameras-0.17.3b2/tests/test_snapshot_cancellation.py +105 -0
- python_aidot_cameras-0.17.3b2/tests/test_srtp_rx_rekey.py +183 -0
- python_aidot_cameras-0.17.3b2/tests/test_srtp_tx_key_note.py +101 -0
- python_aidot_cameras-0.17.3b2/tests/test_webrtc_answer_race.py +101 -0
- python_aidot_cameras-0.17.2/tests/test_key_restart_sdp.py +0 -61
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/LICENSE +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/MANIFEST.in +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/__init__.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/__main__.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_upstream.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/__init__.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/__init__.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/clock.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/codecs/__init__.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/codecs/base.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/codecs/g711.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/codecs/g722.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/codecs/h264.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/codecs/opus.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/codecs/vpx.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/contrib/__init__.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/contrib/media.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/contrib/signaling.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/events.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/exceptions.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/jitterbuffer.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/mediastreams.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rate.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rtcconfiguration.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rtcdatachannel.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rtcdtlstransport.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rtcicetransport.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rtcpeerconnection.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rtcrtpparameters.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rtcrtpreceiver.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rtcrtpsender.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rtcrtptransceiver.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rtcsctptransport.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rtcsessiondescription.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/rtp.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/sdp.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/stats.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/_vendor/aiortc/utils.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/ble_gateway.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/__init__.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/constants.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/controls.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/go2rtc.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/hwaccel.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/models.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/playback.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/protocol.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/sdes.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/tutk.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/webrtc.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/client.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/const.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/credentials.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/crypto.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/device_client.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/discover.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/exceptions.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/g711.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/lan_retry.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/py.typed +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/python_aidot_cameras.egg-info/dependency_links.txt +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/python_aidot_cameras.egg-info/entry_points.txt +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/python_aidot_cameras.egg-info/requires.txt +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/python_aidot_cameras.egg-info/top_level.txt +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/setup.cfg +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_aioice_compat.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_alarm_event.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_answer_sdp_video_pt.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_audio_mux_offline.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_avio_request.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_avio_response_parse.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_avio_response_router.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_backoff.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_battery_detection.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_battery_evidence_corroboration.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_battery_skip_turn.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_ble_gateway.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_bridge_state_sentinels.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_busy_backoff.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_constants_imports_resolve.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_control_verdicts.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_deferred_hardening.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_device_cmd_seq_match.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_device_login_guard.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_device_user_info_cache.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_dormant_camera_cleanup.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_dtls_mux_terminal_write.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_dtls_not_ready_burst.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_dtls_pinning.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_dtls_serve_decode_skip.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_dtls_serve_open_timeout.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_dtls_skip_signaling_wait.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_dtls_slow_probe.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_dtls_stdout_producer.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_egress_guard.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_env_seams.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_go2rtc.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_go2rtc_cli.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_h264_decode_rate_limit.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_highport_nomination.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_hwaccel_probe.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_ice_config_cache.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_ice_peer_reflexive.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_idle_release_both_loops.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_keepalive_renew.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_keyframe_prompter.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_lan_retry_reaches_the_storming_devices.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_light_active_color_mode.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_live_stream_param.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_live_validate_policy.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_logging_caps.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_login_connect_deadline.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_login_reconnect_backoff.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_media_socket_rcvbuf.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_media_wait_floor.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_motion_poll.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_mqtt_credential_refetch.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_mqtt_credential_self_heal.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_mqtt_password_not_persisted.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_mqtt_session_reconnect.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_narrow_pc_ice.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_no_self_referential_go2rtc.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_no_undefined_names.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_offer_keeps_rtcp_feedback.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_offline_keepalive_pause.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_open_fail_logger.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_open_gate_delay.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_peerid_reuse.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_persistent_mqtt.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_persistent_mqtt_robustness.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_playback_tls.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_post_merge_hardening.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_remb.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_resolution_persists.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_restart_cycle.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_retry_policy.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_scripts_importable.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_adaptive.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_avio_dispatch.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_echo_wait_timeout.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_fast_liveplay.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_ffmpeg_stderr_log.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_idle_release.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_offer_video_pin.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_open_cleanup.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_sctp_abort.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_serve_audio.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_serve_cmd.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_serve_stderr_drain.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_sprop.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_talk.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_teardown_exit_log.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_terminal_ack.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_terminal_ack_latency.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdes_watchdog.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdp_narrowing_properties.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sdp_payload_type_narrowing.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_security_hardening.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_serializable_login_info.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_serve_loss_tolerance.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_serve_relay.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_session_stats.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_shared_house_seam.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_speak.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sprop_capture_gate.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_sprop_staleness.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_stream_cap.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_stream_drain_ownership.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_stream_idle.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_stream_slot_cap.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_stream_teardown.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_talk.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_terminal_ack.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_token_file_robustness.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_token_refresh.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_turn_create_permission.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_upstream_compat.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_video_profile_log.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_viewer_check_throttle.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_viewer_presence.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_viewer_present_push_mode.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_wait_or_event.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_wake_gate_camera_evidence.py +0 -0
- {python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/tests/test_wake_readiness_retry.py +0 -0
{python_aidot_cameras-0.17.2/python_aidot_cameras.egg-info → python_aidot_cameras-0.17.3b2}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-aidot-cameras
|
|
3
|
-
Version: 0.17.
|
|
3
|
+
Version: 0.17.3b2
|
|
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
|
|
@@ -66,6 +66,40 @@ Other battery models (A001108, A001360) are recognized in code with the same
|
|
|
66
66
|
battery handling. See [`docs/CAMERAS.md`](docs/CAMERAS.md#supported-cameras) for
|
|
67
67
|
the authoritative table and per-model notes.
|
|
68
68
|
|
|
69
|
+
## Local (LAN) control and account ownership
|
|
70
|
+
|
|
71
|
+
Local control over the devices' TCP:10000 channel - both the light/plug protocol
|
|
72
|
+
and this package's `CameraLanClient` - is accepted **only for the account that
|
|
73
|
+
owns the devices**. A member of a shared home is rejected at the device, even
|
|
74
|
+
though the cloud happily hands that member a full device list, complete with the
|
|
75
|
+
per-device `password` and `aesKey`.
|
|
76
|
+
|
|
77
|
+
The rejection is easy to misread, because the device answers with a code that
|
|
78
|
+
varies by firmware family and a message that blames the wrong thing:
|
|
79
|
+
|
|
80
|
+
| Model | Ack |
|
|
81
|
+
| --- | --- |
|
|
82
|
+
| `LK.light.A001493` | 400 `not equal abort user id or password` |
|
|
83
|
+
| `LK.light.A001497`, `LK.plug.A001535` | 4354 `fail` |
|
|
84
|
+
| `LK.IPC.A000088` | 4352 `fail` |
|
|
85
|
+
|
|
86
|
+
The A001493's message is the honest one: it is the **user id** that does not
|
|
87
|
+
match, not the password. Every credential in the request can be correct and the
|
|
88
|
+
login will still be refused if the `userId` is not the owner's.
|
|
89
|
+
|
|
90
|
+
Verified 2026-08-08 against ten devices across four model families: every one
|
|
91
|
+
returns 200 for the owning account and one of the codes above for a shared-home
|
|
92
|
+
member.
|
|
93
|
+
|
|
94
|
+
**If local control never engages, check which account the integration is signed
|
|
95
|
+
in as before looking anywhere else.** A dedicated or secondary login - the sort
|
|
96
|
+
of thing you might use to avoid contending with a phone app over a rotating
|
|
97
|
+
refresh token - will control everything fine through the cloud and never once
|
|
98
|
+
log in locally.
|
|
99
|
+
|
|
100
|
+
Cameras' live video is unaffected either way: it is WebRTC signaled over cloud
|
|
101
|
+
MQTT and does not use this path.
|
|
102
|
+
|
|
69
103
|
## Library install
|
|
70
104
|
|
|
71
105
|
Install from PyPI (the simplest, recommended method):
|
|
@@ -35,6 +35,40 @@ Other battery models (A001108, A001360) are recognized in code with the same
|
|
|
35
35
|
battery handling. See [`docs/CAMERAS.md`](docs/CAMERAS.md#supported-cameras) for
|
|
36
36
|
the authoritative table and per-model notes.
|
|
37
37
|
|
|
38
|
+
## Local (LAN) control and account ownership
|
|
39
|
+
|
|
40
|
+
Local control over the devices' TCP:10000 channel - both the light/plug protocol
|
|
41
|
+
and this package's `CameraLanClient` - is accepted **only for the account that
|
|
42
|
+
owns the devices**. A member of a shared home is rejected at the device, even
|
|
43
|
+
though the cloud happily hands that member a full device list, complete with the
|
|
44
|
+
per-device `password` and `aesKey`.
|
|
45
|
+
|
|
46
|
+
The rejection is easy to misread, because the device answers with a code that
|
|
47
|
+
varies by firmware family and a message that blames the wrong thing:
|
|
48
|
+
|
|
49
|
+
| Model | Ack |
|
|
50
|
+
| --- | --- |
|
|
51
|
+
| `LK.light.A001493` | 400 `not equal abort user id or password` |
|
|
52
|
+
| `LK.light.A001497`, `LK.plug.A001535` | 4354 `fail` |
|
|
53
|
+
| `LK.IPC.A000088` | 4352 `fail` |
|
|
54
|
+
|
|
55
|
+
The A001493's message is the honest one: it is the **user id** that does not
|
|
56
|
+
match, not the password. Every credential in the request can be correct and the
|
|
57
|
+
login will still be refused if the `userId` is not the owner's.
|
|
58
|
+
|
|
59
|
+
Verified 2026-08-08 against ten devices across four model families: every one
|
|
60
|
+
returns 200 for the owning account and one of the codes above for a shared-home
|
|
61
|
+
member.
|
|
62
|
+
|
|
63
|
+
**If local control never engages, check which account the integration is signed
|
|
64
|
+
in as before looking anywhere else.** A dedicated or secondary login - the sort
|
|
65
|
+
of thing you might use to avoid contending with a phone app over a rotating
|
|
66
|
+
refresh token - will control everything fine through the cloud and never once
|
|
67
|
+
log in locally.
|
|
68
|
+
|
|
69
|
+
Cameras' live video is unaffected either way: it is WebRTC signaled over cloud
|
|
70
|
+
MQTT and does not use this path.
|
|
71
|
+
|
|
38
72
|
## Library install
|
|
39
73
|
|
|
40
74
|
Install from PyPI (the simplest, recommended method):
|
{python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/client.py
RENAMED
|
@@ -398,6 +398,21 @@ _BUSY_BACKOFF_S = float(os.environ.get("AIDOT_BUSY_BACKOFF_S", "20"))
|
|
|
398
398
|
_FUTILE_KEEPALIVE_LIMIT = int(os.environ.get("AIDOT_FUTILE_KEEPALIVE_LIMIT", "5"))
|
|
399
399
|
|
|
400
400
|
|
|
401
|
+
def _next_no_media_streak(no_media_streak: int, healthy: bool) -> int:
|
|
402
|
+
"""The streak after a keepalive session that was (or was not) healthy.
|
|
403
|
+
|
|
404
|
+
The ceiling counts CONSECUTIVE failures, so a session that delivered media
|
|
405
|
+
resets it to zero. Without that reset the count is cumulative and a camera
|
|
406
|
+
that merely fails occasionally - five scattered no-media sessions spread
|
|
407
|
+
across a day, each one separated by sessions that worked - eventually trips
|
|
408
|
+
a limit meant for cameras that never deliver anything at all, and has its
|
|
409
|
+
background keepalive stopped for good.
|
|
410
|
+
"""
|
|
411
|
+
if healthy:
|
|
412
|
+
return 0
|
|
413
|
+
return no_media_streak + 1
|
|
414
|
+
|
|
415
|
+
|
|
401
416
|
def _should_abandon_keepalive(no_media_streak: int, *, is_battery: bool,
|
|
402
417
|
limit: int = _FUTILE_KEEPALIVE_LIMIT) -> bool:
|
|
403
418
|
"""Should the background keepalive stop reopening this camera?
|
|
@@ -2622,7 +2637,14 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
|
|
|
2622
2637
|
_session.wait_done(),
|
|
2623
2638
|
timeout=_sdes_snap_seconds + 80,
|
|
2624
2639
|
)
|
|
2625
|
-
except
|
|
2640
|
+
except _asyncio.CancelledError:
|
|
2641
|
+
# Not the same event as a timeout. A timeout means the
|
|
2642
|
+
# camera was slow and whatever landed in the temp file is
|
|
2643
|
+
# worth salvaging; a cancellation means the caller is gone,
|
|
2644
|
+
# so salvaging it (another ffmpeg, up to 15s) is work nobody
|
|
2645
|
+
# is waiting for and it stops shutdown from completing.
|
|
2646
|
+
raise
|
|
2647
|
+
except TimeoutError:
|
|
2626
2648
|
pass
|
|
2627
2649
|
finally:
|
|
2628
2650
|
await _session.stop()
|
|
@@ -3599,6 +3621,7 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
|
|
|
3599
3621
|
# Adaptive bookkeeping: a fast attempt that never delivered media
|
|
3600
3622
|
# latches the loop onto the full relay path for its remaining opens.
|
|
3601
3623
|
_healthy = session.last_media_monotonic > 0.0
|
|
3624
|
+
_no_media_streak = _next_no_media_streak(_no_media_streak, _healthy)
|
|
3602
3625
|
if _healthy:
|
|
3603
3626
|
# A session that delivered media is a real, cleanly-used session;
|
|
3604
3627
|
# the next open should be a fresh one rather than re-offering on a
|
|
@@ -3606,9 +3629,7 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
|
|
|
3606
3629
|
_loop_peer_id = self.generate_webrtc_peer_id(
|
|
3607
3630
|
live_type=2, stream_id=0, sdes=True)
|
|
3608
3631
|
_peer_reuses = 0
|
|
3609
|
-
_no_media_streak = 0
|
|
3610
3632
|
else:
|
|
3611
|
-
_no_media_streak += 1
|
|
3612
3633
|
if _should_abandon_keepalive(
|
|
3613
3634
|
_no_media_streak, is_battery=self.is_battery_camera):
|
|
3614
3635
|
# Every open so far has cost the camera a wake and returned
|
{python_aidot_cameras-0.17.2 → python_aidot_cameras-0.17.3b2}/aidot_cameras/camera/lan_control.py
RENAMED
|
@@ -164,6 +164,26 @@ class CameraLanError(Exception):
|
|
|
164
164
|
"""A local control operation failed (connect, login, or protocol error)."""
|
|
165
165
|
|
|
166
166
|
|
|
167
|
+
class CameraLanLoginRejected(CameraLanError):
|
|
168
|
+
"""The device advertised local control and then refused our login.
|
|
169
|
+
|
|
170
|
+
Separate from its parent because the two mean opposite things to a caller.
|
|
171
|
+
A device that does not offer local control is ordinary and should be quiet;
|
|
172
|
+
a device that offers it, accepts the connection, decrypts our request with
|
|
173
|
+
its own key, and STILL answers with a non-200 ack is a defect - either in
|
|
174
|
+
what we send or in the device - and the caller should be able to say so out
|
|
175
|
+
loud rather than logging it at the same level as "not supported here".
|
|
176
|
+
|
|
177
|
+
``ack`` is the device's own code, because the refusals differ by family and
|
|
178
|
+
the number is the only handle on which one happened: A000088 cameras answer
|
|
179
|
+
4352, A001497 lights and A001535 plugs 4354, A001493 lights 400.
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
def __init__(self, message: str, ack: Any = None) -> None:
|
|
183
|
+
super().__init__(message)
|
|
184
|
+
self.ack = ack
|
|
185
|
+
|
|
186
|
+
|
|
167
187
|
class CameraLanClient:
|
|
168
188
|
"""Short-lived local device-control client for one camera.
|
|
169
189
|
|
|
@@ -291,9 +311,13 @@ class CameraLanClient:
|
|
|
291
311
|
) from exc
|
|
292
312
|
if ack != 200:
|
|
293
313
|
# The real device rejected our key-authenticated login; stop using
|
|
294
|
-
# the LAN path and revert to cloud.
|
|
314
|
+
# the LAN path and revert to cloud. Raised as the specific type so
|
|
315
|
+
# a caller can tell this apart from a device that never offered
|
|
316
|
+
# local control - see CameraLanLoginRejected.
|
|
295
317
|
self._eligible = False
|
|
296
|
-
raise
|
|
318
|
+
raise CameraLanLoginRejected(
|
|
319
|
+
f"{self.device_id}: login rejected ack={ack}", ack=ack
|
|
320
|
+
)
|
|
297
321
|
return (resp.get("payload") or {}).get("ascNumber", 1)
|
|
298
322
|
|
|
299
323
|
# -- public control ---------------------------------------------------- #
|