python-aidot-cameras 1.0.0rc15__tar.gz → 1.0.0rc17__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.0rc15/python_aidot_cameras.egg-info → python_aidot_cameras-1.0.0rc17}/PKG-INFO +1 -1
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/controls.py +74 -1
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/models.py +78 -1
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/pyproject.toml +1 -1
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17/python_aidot_cameras.egg-info}/PKG-INFO +1 -1
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/python_aidot_cameras.egg-info/SOURCES.txt +2 -0
- python_aidot_cameras-1.0.0rc17/tests/test_auto_light_settings.py +143 -0
- python_aidot_cameras-1.0.0rc17/tests/test_declared_properties.py +85 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/LICENSE +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/MANIFEST.in +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/README.md +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/__init__.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/__main__.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_upstream.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/__init__.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/LICENSE +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/__init__.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/clock.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/codecs/__init__.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/codecs/base.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/codecs/g711.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/codecs/g722.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/codecs/h264.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/codecs/opus.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/codecs/vpx.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/contrib/__init__.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/contrib/media.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/contrib/signaling.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/events.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/exceptions.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/jitterbuffer.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/mediastreams.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rate.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rtcconfiguration.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rtcdatachannel.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rtcdtlstransport.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rtcicetransport.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rtcpeerconnection.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rtcrtpparameters.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rtcrtpreceiver.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rtcrtpsender.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rtcrtptransceiver.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rtcsctptransport.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rtcsessiondescription.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/rtp.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/sdp.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/stats.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_vendor/aiortc/utils.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/ble_gateway.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/__init__.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/client.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/constants.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/go2rtc.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/hwaccel.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/lan_control.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/playback.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/protocol.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/sd_events.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/sd_listing.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/sd_playback.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/sdes.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/sdes_open.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/tutk.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/webrtc.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/webrtc_open.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/client.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/cloud_auth.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/const.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/credentials.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/crypto.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/device_client.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/discover.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/exceptions.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/g711.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/lan_retry.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/py.typed +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/python_aidot_cameras.egg-info/dependency_links.txt +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/python_aidot_cameras.egg-info/entry_points.txt +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/python_aidot_cameras.egg-info/requires.txt +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/python_aidot_cameras.egg-info/top_level.txt +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/setup.cfg +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_abandoned_attempt_skips_the_doomed_serve.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_abandoned_wait_still_maps_audio.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_aioice_compat.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_alarm_event.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_answer_pt_kinds.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_answer_sdp_video_pt.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_answer_section_selection.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_audio_mux_offline.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_auto_alarm.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_avio_ctrl_uses_the_session_dseq.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_avio_request.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_avio_response_parse.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_avio_response_router.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_backoff.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_battery_detection.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_battery_evidence_corroboration.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_battery_skip_turn.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_battery_stale_offer_is_abandoned_early.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_battery_wake_gate_app_parity.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_bitrate_campaign_arms.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_ble_gateway.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_bridge_state_sentinels.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_busy_backoff.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_camera_properties.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_canary_log_names_the_camera.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_cloud_count.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_cloud_plan.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_constants_imports_resolve.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_control_verdicts.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_deferred_hardening.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_detection_types.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_device_cmd_seq_match.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_device_login_guard.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_device_user_info_cache.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_direct_ts_keyframe_signal.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_direct_ts_serve.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_dormant_camera_cleanup.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_drain_is_cancellable.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_dtls_mux_terminal_write.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_dtls_not_ready_burst.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_dtls_pinning.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_dtls_resolution_ack.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_dtls_serve_decode_skip.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_dtls_serve_ice_wait.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_dtls_serve_open_timeout.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_dtls_skip_signaling_wait.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_dtls_slow_probe.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_dtls_stdout_producer.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_dtls_video_presence.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_egress_guard.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_env_seams.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_feature_probe_needs_a_live_session.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_ffmpeg_signal_exit_is_not_a_crash.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_futile_keepalive.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_gate_requires_a_decoded_frame.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_go2rtc.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_go2rtc_cli.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_h264_decode_rate_limit.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_highport_nomination.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_hwaccel_probe.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_ice_config_cache.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_ice_credentials_match_the_app.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_ice_peer_reflexive.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_idle_release_both_loops.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_keepalive_renew.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_key_restart_sdp.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_keyframe_prompter.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_lan_control.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_lan_retry_reaches_the_storming_devices.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_late_repeats_are_dropped.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_light_active_color_mode.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_live_session_carries_talk.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_live_stream_param.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_live_validate_policy.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_live_video_canary.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_liveplay_echo_timeout.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_local_only_controls.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_logging_caps.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_login_connect_deadline.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_login_reconnect_backoff.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_media_counted_only_when_plaintext.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_media_path_is_reported.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_media_socket_rcvbuf.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_media_wait_floor.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_motion_poll.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_mqtt_client_id_is_exact.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_mqtt_credential_refetch.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_mqtt_credential_self_heal.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_mqtt_password_not_persisted.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_mqtt_session_reconnect.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_mqtt_thread_not_orphaned.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_nack.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_nack_cannot_add_load_in_a_burst.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_narrow_pc_ice.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_no_key_material_in_logs.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_no_self_referential_go2rtc.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_no_unbounded_hex_dumps.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_no_undefined_names.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_nominates_as_soon_as_the_answer_lands.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_off_subnet_candidate.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_offer_bandwidth_line.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_offer_keeps_rtcp_feedback.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_offline_keepalive_pause.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_on_frame_is_an_av_frame.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_open_fail_logger.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_open_gate_delay.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_peer_id_class_env.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_peer_id_client_type.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_peerid_reuse.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_persistent_mqtt.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_persistent_mqtt_robustness.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_playback_tls.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_post_merge_hardening.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_ptz_probe_reads_what_it_is_told.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_public_signature_matches_impl.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_publish_target_readiness.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_quality_campaign_in_session.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_reboot_action.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_recent_recordings.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_remb.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_remb_goes_out_the_relay_aware_sender.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_resent_frames_are_dropped.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_resolution_persists.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_restart_cycle.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_retry_policy.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_scripts_importable.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sctp_init_ack_tsn.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sctp_reassembly.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sctp_sack.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sd_card_presence.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sd_event_decode.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sd_event_probe_payloads.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sd_listing.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sd_playback.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_adaptive.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_avio_dispatch.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_bridge_break_gate.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_connection_mode.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_echo_wait_is_not_dead_time.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_fast_liveplay.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_ffmpeg_stderr_log.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_first_media_stall_report.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_ice_teardown.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_idle_release.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_nack_bridge.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_offer_video_order.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_offer_video_pin.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_open_cleanup.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_sctp_abort.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_serve_audio.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_serve_cmd.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_serve_stderr_drain.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_session_cap.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_sprop.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_talk.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_teardown_exit_log.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_terminal_ack.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_terminal_ack_latency.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_tmmbr_bridge.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdes_watchdog.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdp_narrowing_properties.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sdp_payload_type_narrowing.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_security_hardening.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_self_peer_is_an_address_not_an_ip.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_serializable_login_info.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_serve_input_timeout.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_serve_loss_tolerance.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_serve_pt_respects_the_pin.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_serve_relay.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_serve_stderr_keeps_the_reason.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_session_stats.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_shared_house_seam.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_signalling_is_attributable_to_a_camera.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_snapshot_cancellation.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_speak.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sprop_capture_gate.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sprop_staleness.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sprop_unstable_public.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_sprop_write_is_reported_truthfully.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_srtp_rx_rekey.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_srtp_tx_key_note.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_stable_terminal_id.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_stall_nudge_policy.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_stall_report_says_what_the_answer_carried.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_stall_report_says_whether_the_trigger_was_answered.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_stall_report_separates_silence_from_undecryptable.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_stall_report_survives_a_caller_giving_up.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_stall_reports_reach_the_artifact.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_status_and_trace_channels.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_stream_cap.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_stream_drain_ownership.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_stream_idle.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_stream_slot_cap.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_stream_teardown.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_talk.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_talk_reports_a_speaker_that_never_opened.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_tap_to_mux_end_to_end.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_terminal_ack.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_tmmbr.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_token_file_robustness.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_token_refresh.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_transport_failure_reporting.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_turn_create_permission.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_tutk_path_is_out_of_reach.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_unbuildable_summary_repeats_at_debug.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_upstream_compat.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_validator_shares_one_token.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_video_profile_log.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_video_pts_dts.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_video_ts_unwrap.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_viewer_check_throttle.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_viewer_presence.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_viewer_present_push_mode.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_wait_or_event.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_wake_gate_camera_evidence.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/tests/test_wake_readiness_retry.py +0 -0
- {python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/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.0rc17
|
|
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.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/controls.py
RENAMED
|
@@ -15,6 +15,24 @@ from .constants import _PTZ_DIR_CODES, _STREAM_QUALITY, SETSTREAMCTRL_CMD
|
|
|
15
15
|
|
|
16
16
|
_LOGGER = logging.getLogger(__name__)
|
|
17
17
|
|
|
18
|
+
#: How the floodlight behaves when the camera sees someone, by the name the
|
|
19
|
+
#: vendor app prints against each value. From the cloud device profile
|
|
20
|
+
#: (LK.IPC.A001513): ``lightBehavior`` is an enum with allowedValues
|
|
21
|
+
#: ``Constant=0, Flash=1``, default 0.
|
|
22
|
+
LIGHT_BEHAVIORS = {"constant": 0, "flash": 1}
|
|
23
|
+
|
|
24
|
+
#: How long the floodlight stays on after it triggers, in seconds. The camera
|
|
25
|
+
#: enumerates exactly these four (``LingerDuration``, default 30) -- it is not a
|
|
26
|
+
#: free range, and a value outside the set is accepted on the wire and ignored,
|
|
27
|
+
#: which would leave a control reporting a setting the camera never applied.
|
|
28
|
+
LIGHT_LINGER_DURATIONS = (20, 30, 40, 50)
|
|
29
|
+
|
|
30
|
+
#: The floodlight level the camera accepts (``Dimming``: min 10, max 100).
|
|
31
|
+
#: The floor is 10, not 0: 0 is out of range rather than "off", and off is
|
|
32
|
+
#: LightOnOff.
|
|
33
|
+
LIGHT_BRIGHTNESS_MIN = 10
|
|
34
|
+
LIGHT_BRIGHTNESS_MAX = 100
|
|
35
|
+
|
|
18
36
|
#: The camera's ack for SETSTREAMCTRL (800).
|
|
19
37
|
SETSTREAMCTRL_RESP_CMD = 801
|
|
20
38
|
#: Measured 0.01-0.03s; short so a control call never holds up a view.
|
|
@@ -56,10 +74,65 @@ class _CameraControlsMixin:
|
|
|
56
74
|
return await self.async_set_device_attribute("OSDEnable", 1 if enabled else 0)
|
|
57
75
|
|
|
58
76
|
async def async_set_auto_light(self, enabled: bool) -> bool:
|
|
59
|
-
"""
|
|
77
|
+
"""Turn the light-up-when-someone-appears automation on or off.
|
|
78
|
+
|
|
79
|
+
This is ``autoLightEnable``, which the vendor app labels "When Someone
|
|
80
|
+
Appears" (or "When Someone Appears at Night" on some models -- that is
|
|
81
|
+
which label the app prints for the model, not a separate setting).
|
|
82
|
+
"""
|
|
60
83
|
return await self.async_set_device_attribute(
|
|
61
84
|
"autoLightEnable", 1 if enabled else 0)
|
|
62
85
|
|
|
86
|
+
async def async_set_light_behavior(self, behavior: str) -> bool:
|
|
87
|
+
"""Set how the light behaves when it triggers: "constant" or "flash".
|
|
88
|
+
|
|
89
|
+
**This write acks and does not land.** Measured 2026-09-07 against an
|
|
90
|
+
A001513 (which declares ``lightBehavior`` in its profile) and an
|
|
91
|
+
A000088 (which does not): six attempts across both models, as int 1 and
|
|
92
|
+
as string "1", every one returning True and every read-back over the
|
|
93
|
+
following 20 s still reporting the old value. On the same camera in the
|
|
94
|
+
same session ``LingerDuration`` and ``Dimming`` both landed within 4 s,
|
|
95
|
+
so this is the attribute, not the camera being asleep or the cloud
|
|
96
|
+
lagging. The one hypothesis left untested is that the camera takes it
|
|
97
|
+
only while ``autoLightEnable`` is 1 -- untested because arming it makes
|
|
98
|
+
a real floodlight come on in someone's house.
|
|
99
|
+
|
|
100
|
+
The method is kept because the command is well-formed and a future
|
|
101
|
+
firmware may honour it, but **nothing should build a control on it**
|
|
102
|
+
until a read-back confirms one: a setting that reports a value the
|
|
103
|
+
camera never applied is worse than no setting. Same call as
|
|
104
|
+
``async_set_resolution``.
|
|
105
|
+
|
|
106
|
+
Refuses a name the camera does not offer rather than sending it: the
|
|
107
|
+
attribute is an enum, and an out-of-range write is accepted silently.
|
|
108
|
+
"""
|
|
109
|
+
try:
|
|
110
|
+
value = LIGHT_BEHAVIORS[behavior]
|
|
111
|
+
except KeyError:
|
|
112
|
+
raise ValueError(
|
|
113
|
+
f"unknown light behavior {behavior!r}; "
|
|
114
|
+
f"expected one of {sorted(LIGHT_BEHAVIORS)}") from None
|
|
115
|
+
return await self.async_set_device_attribute("lightBehavior", value)
|
|
116
|
+
|
|
117
|
+
async def async_set_light_linger_duration(self, seconds: int) -> bool:
|
|
118
|
+
"""Set how long the light stays on after it triggers, in seconds."""
|
|
119
|
+
if seconds not in LIGHT_LINGER_DURATIONS:
|
|
120
|
+
raise ValueError(
|
|
121
|
+
f"unsupported linger duration {seconds!r}; "
|
|
122
|
+
f"the camera offers {list(LIGHT_LINGER_DURATIONS)}")
|
|
123
|
+
return await self.async_set_device_attribute("LingerDuration", seconds)
|
|
124
|
+
|
|
125
|
+
async def async_set_light_brightness(self, level: int) -> bool:
|
|
126
|
+
"""Set the floodlight level (10-100), clamped to what the camera takes.
|
|
127
|
+
|
|
128
|
+
Separate from :meth:`async_set_floodlight`'s optional brightness, which
|
|
129
|
+
is part of turning the light on by hand; this is the level the camera
|
|
130
|
+
uses when its own automation fires.
|
|
131
|
+
"""
|
|
132
|
+
return await self.async_set_device_attribute(
|
|
133
|
+
"Dimming",
|
|
134
|
+
max(LIGHT_BRIGHTNESS_MIN, min(LIGHT_BRIGHTNESS_MAX, int(level))))
|
|
135
|
+
|
|
63
136
|
async def async_set_voice_prompts(self, enabled: bool) -> bool:
|
|
64
137
|
"""Enable or disable spoken prompts from the camera speaker (voiceEnable)."""
|
|
65
138
|
return await self.async_set_device_attribute(
|
{python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/models.py
RENAMED
|
@@ -97,8 +97,18 @@ class CameraStatusData(DeviceStatusData):
|
|
|
97
97
|
status_led: Optional[bool] = None
|
|
98
98
|
#: Timestamp/OSD overlay burned into the video (OSDEnable).
|
|
99
99
|
osd_timestamp: Optional[bool] = None
|
|
100
|
-
#: Floodlight automation (autoLightEnable).
|
|
100
|
+
#: Floodlight automation (autoLightEnable). The vendor app labels this
|
|
101
|
+
#: "When Someone Appears" -- it is the person-triggered light, not a
|
|
102
|
+
#: generic schedule, and the three fields below are its settings.
|
|
101
103
|
auto_light: Optional[bool] = None
|
|
104
|
+
#: How the light behaves when it triggers: "constant" or "flash"
|
|
105
|
+
#: (lightBehavior 0/1). An unmodelled number is carried through as itself.
|
|
106
|
+
light_behavior: Optional[str] = None
|
|
107
|
+
#: How long the light stays on after a trigger, in seconds (LingerDuration).
|
|
108
|
+
light_linger_duration: Optional[int] = None
|
|
109
|
+
#: Floodlight level 10-100 (Dimming). Kept apart from `dimming`, which is
|
|
110
|
+
#: the light platform's brightness -- see _LIGHT_ONLY_ATTR_KEYS.
|
|
111
|
+
light_brightness: Optional[int] = None
|
|
102
112
|
#: Spoken prompts from the camera speaker (voiceEnable).
|
|
103
113
|
voice_prompts: Optional[bool] = None
|
|
104
114
|
#: HDR (HDRStatus).
|
|
@@ -153,6 +163,16 @@ class CameraStatusData(DeviceStatusData):
|
|
|
153
163
|
self.osd_timestamp = b
|
|
154
164
|
if (b := _as_bool(attr.get("autoLightEnable"))) is not None:
|
|
155
165
|
self.auto_light = b
|
|
166
|
+
if (v := attr.get("lightBehavior")) is not None:
|
|
167
|
+
lb = _as_int(v)
|
|
168
|
+
# Same shape as nightVisionMode: name the values we know and carry
|
|
169
|
+
# anything else through, so a firmware that grows a third mode
|
|
170
|
+
# cannot be reported as one of the two we do know.
|
|
171
|
+
self.light_behavior = (
|
|
172
|
+
{0: "constant", 1: "flash"}.get(lb, str(lb))
|
|
173
|
+
if lb is not None else str(v))
|
|
174
|
+
if (i := _as_int(attr.get("LingerDuration"))) is not None:
|
|
175
|
+
self.light_linger_duration = i
|
|
156
176
|
if (b := _as_bool(attr.get("voiceEnable"))) is not None:
|
|
157
177
|
self.voice_prompts = b
|
|
158
178
|
if (b := _as_bool(attr.get("HDRStatus"))) is not None:
|
|
@@ -201,6 +221,13 @@ class CameraStatusData(DeviceStatusData):
|
|
|
201
221
|
``properties`` dict - both share the same camera attribute keys
|
|
202
222
|
(Battery_remaining, Occupancy, SDcardStatus, MotionDetection_*, ...).
|
|
203
223
|
"""
|
|
224
|
+
# Dimming is filtered out below because on a LIGHT it is the entity's
|
|
225
|
+
# brightness, and a camera's must never be read as one. On a camera it
|
|
226
|
+
# is nonetheless a real setting -- the floodlight's level, 10-100 in the
|
|
227
|
+
# device profile, and what the app's Brightness page writes -- so read
|
|
228
|
+
# it here into its own field rather than letting the filter lose it.
|
|
229
|
+
if (i := _as_int(attrs.get(CONF_DIMMING))) is not None:
|
|
230
|
+
self.light_brightness = i
|
|
204
231
|
self.update({
|
|
205
232
|
k: v for k, v in attrs.items()
|
|
206
233
|
if k not in self._LIGHT_ONLY_ATTR_KEYS
|
|
@@ -220,6 +247,55 @@ class CameraDeviceInformation(DeviceInformation):
|
|
|
220
247
|
# for a pan-only camera, [1,2,3,6] = full PTZ). Empty means unknown - callers
|
|
221
248
|
# should treat unknown as "show all" for backward compatibility.
|
|
222
249
|
ptz_directions: list
|
|
250
|
+
# Every property identity this MODEL declares in its cloud profile - the
|
|
251
|
+
# same list the vendor app gates its settings pages on. Distinct from the
|
|
252
|
+
# device's `properties` dict, which is what the cloud currently REPORTS and
|
|
253
|
+
# carries defaults for keys the model never implemented: an A000088 reports
|
|
254
|
+
# LingerDuration = "30" and, asked directly over LAN, has no such attribute
|
|
255
|
+
# and ignores writes to it. Gate a control on this, not on a reported
|
|
256
|
+
# value, whenever offering it on the wrong model would mean a control that
|
|
257
|
+
# acknowledges and does nothing.
|
|
258
|
+
#
|
|
259
|
+
# Empty means the profile was absent or unreadable, which is UNKNOWN rather
|
|
260
|
+
# than "declares nothing" - so a caller that has other evidence a setting
|
|
261
|
+
# works (`Dimming` is real on the A000088 and verified by read-back there,
|
|
262
|
+
# though the profile omits it) should keep using that evidence instead.
|
|
263
|
+
declared_properties: frozenset
|
|
264
|
+
|
|
265
|
+
@staticmethod
|
|
266
|
+
def _collect_declared_properties(device: dict) -> frozenset:
|
|
267
|
+
"""Property identities from the model profile, or empty if unreadable.
|
|
268
|
+
|
|
269
|
+
Keyed on ``identity``/``code``, never on ``name``: the profile's display
|
|
270
|
+
name is often a placeholder like
|
|
271
|
+
``propertyName_lightBehavior_1679075889207132162``, and a name-keyed
|
|
272
|
+
search reported that no model declared a property every model declared.
|
|
273
|
+
|
|
274
|
+
The profile is remote data of no guaranteed shape, so every level is
|
|
275
|
+
type-checked rather than trusted; a malformed one yields an empty set
|
|
276
|
+
instead of killing device setup.
|
|
277
|
+
"""
|
|
278
|
+
out = set()
|
|
279
|
+
try:
|
|
280
|
+
product = device.get(CONF_PRODUCT)
|
|
281
|
+
modules = product.get(CONF_SERVICE_MODULES) if isinstance(product, dict) else None
|
|
282
|
+
if not isinstance(modules, list):
|
|
283
|
+
return frozenset()
|
|
284
|
+
for service in modules:
|
|
285
|
+
if not isinstance(service, dict):
|
|
286
|
+
continue
|
|
287
|
+
props = service.get(CONF_PROPERTIES)
|
|
288
|
+
if not isinstance(props, list):
|
|
289
|
+
continue
|
|
290
|
+
for prop in props:
|
|
291
|
+
if not isinstance(prop, dict):
|
|
292
|
+
continue
|
|
293
|
+
ident = prop.get("identity") or prop.get("code")
|
|
294
|
+
if isinstance(ident, str) and ident:
|
|
295
|
+
out.add(ident)
|
|
296
|
+
except Exception:
|
|
297
|
+
return frozenset()
|
|
298
|
+
return frozenset(out)
|
|
223
299
|
|
|
224
300
|
def __init__(self, device: dict[str, Any]) -> None:
|
|
225
301
|
super().__init__(device)
|
|
@@ -229,6 +305,7 @@ class CameraDeviceInformation(DeviceInformation):
|
|
|
229
305
|
str(_aes) if _aes else "")
|
|
230
306
|
self.device_password = device.get("password") or ""
|
|
231
307
|
self.ptz_directions = []
|
|
308
|
+
self.declared_properties = self._collect_declared_properties(device)
|
|
232
309
|
if CONF_PRODUCT in device and CONF_SERVICE_MODULES in device[CONF_PRODUCT]:
|
|
233
310
|
for service in device[CONF_PRODUCT][CONF_SERVICE_MODULES]:
|
|
234
311
|
for prop in service.get(CONF_PROPERTIES) or []:
|
|
@@ -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.0rc17"
|
|
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.0rc17
|
|
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
|
|
@@ -84,6 +84,7 @@ tests/test_answer_sdp_video_pt.py
|
|
|
84
84
|
tests/test_answer_section_selection.py
|
|
85
85
|
tests/test_audio_mux_offline.py
|
|
86
86
|
tests/test_auto_alarm.py
|
|
87
|
+
tests/test_auto_light_settings.py
|
|
87
88
|
tests/test_avio_ctrl_uses_the_session_dseq.py
|
|
88
89
|
tests/test_avio_request.py
|
|
89
90
|
tests/test_avio_response_parse.py
|
|
@@ -104,6 +105,7 @@ tests/test_cloud_count.py
|
|
|
104
105
|
tests/test_cloud_plan.py
|
|
105
106
|
tests/test_constants_imports_resolve.py
|
|
106
107
|
tests/test_control_verdicts.py
|
|
108
|
+
tests/test_declared_properties.py
|
|
107
109
|
tests/test_deferred_hardening.py
|
|
108
110
|
tests/test_detection_types.py
|
|
109
111
|
tests/test_device_cmd_seq_match.py
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""The camera's "light up when someone appears" settings.
|
|
2
|
+
|
|
3
|
+
The vendor app calls the master toggle "When Someone Appears" and it is the
|
|
4
|
+
`autoLightEnable` attribute this library already exposes. The three settings
|
|
5
|
+
that sit UNDER it in the app were missing:
|
|
6
|
+
|
|
7
|
+
lightBehavior enum Constant=0, Flash=1 (default 0)
|
|
8
|
+
LingerDuration enum 20 / 30 / 40 / 50 seconds (default 30)
|
|
9
|
+
Dimming int 10..100 step 1 (default 100)
|
|
10
|
+
|
|
11
|
+
Read from the cloud device profile 2026-09-07 (LK.IPC.A001513, the L2), and
|
|
12
|
+
every one of the three is carried as a live property value by the L2 and by the
|
|
13
|
+
A000088 alike. Of the three, `LingerDuration` and `Dimming` were confirmed on
|
|
14
|
+
hardware to land (written, read back changed, restored); `lightBehavior` acks
|
|
15
|
+
and does NOT land on either model, so the library keeps the setter and Home
|
|
16
|
+
Assistant deliberately ships no control for it -- see
|
|
17
|
+
`async_set_light_behavior`. The A001064 has neither `lightBehavior` nor `LingerDuration` in
|
|
18
|
+
its profile or its properties, so those controls must stay absent there rather
|
|
19
|
+
than appear reading "unknown" -- which is why every getter here answers None
|
|
20
|
+
for a camera that never reported the key.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import asyncio
|
|
24
|
+
|
|
25
|
+
import pytest
|
|
26
|
+
|
|
27
|
+
from aidot_cameras.camera.controls import (
|
|
28
|
+
LIGHT_BEHAVIORS,
|
|
29
|
+
LIGHT_LINGER_DURATIONS,
|
|
30
|
+
_CameraControlsMixin,
|
|
31
|
+
)
|
|
32
|
+
from aidot_cameras.camera.models import CameraStatusData
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class _Cam(_CameraControlsMixin):
|
|
36
|
+
device_id = "cam1"
|
|
37
|
+
|
|
38
|
+
def __init__(self):
|
|
39
|
+
self.attrs: list = []
|
|
40
|
+
|
|
41
|
+
async def async_set_device_attribute(self, attr, value, **kw):
|
|
42
|
+
self.attrs.append((attr, value))
|
|
43
|
+
return True
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# --- reading -----------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
def test_light_behavior_reads_as_a_name_not_a_number():
|
|
49
|
+
s = CameraStatusData()
|
|
50
|
+
s.update_from_camera_attributes({"lightBehavior": "1"})
|
|
51
|
+
assert s.light_behavior == "flash"
|
|
52
|
+
s.update_from_camera_attributes({"lightBehavior": "0"})
|
|
53
|
+
assert s.light_behavior == "constant"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_an_unmodelled_behavior_value_is_carried_through_not_dropped():
|
|
57
|
+
"""A firmware that grows a third mode must not read as one we know."""
|
|
58
|
+
s = CameraStatusData()
|
|
59
|
+
s.update_from_camera_attributes({"lightBehavior": "7"})
|
|
60
|
+
assert s.light_behavior == "7"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_linger_duration_reads_as_seconds():
|
|
64
|
+
s = CameraStatusData()
|
|
65
|
+
s.update_from_camera_attributes({"LingerDuration": "40"})
|
|
66
|
+
assert s.light_linger_duration == 40
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_trigger_brightness_reads_from_dimming():
|
|
70
|
+
s = CameraStatusData()
|
|
71
|
+
s.update_from_camera_attributes({"Dimming": "80"})
|
|
72
|
+
assert s.light_brightness == 80
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_dimming_still_does_not_become_the_light_entity_brightness():
|
|
76
|
+
"""The camera's Dimming is its floodlight level, and it has its own field.
|
|
77
|
+
|
|
78
|
+
It must not leak into `dimming`, which is the LIGHT platform's brightness
|
|
79
|
+
and is why _LIGHT_ONLY_ATTR_KEYS filters the key in the first place.
|
|
80
|
+
"""
|
|
81
|
+
s = CameraStatusData()
|
|
82
|
+
s.update_from_camera_attributes({"Dimming": "80"})
|
|
83
|
+
assert s.dimming is None
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_a_camera_that_never_reported_them_is_unknown_not_defaulted():
|
|
87
|
+
s = CameraStatusData()
|
|
88
|
+
s.update_from_camera_attributes({"MotionDetection_Enable": "1"})
|
|
89
|
+
assert s.light_behavior is None
|
|
90
|
+
assert s.light_linger_duration is None
|
|
91
|
+
assert s.light_brightness is None
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def test_a_partial_push_does_not_clear_what_a_full_poll_established():
|
|
95
|
+
s = CameraStatusData()
|
|
96
|
+
s.update_from_camera_attributes(
|
|
97
|
+
{"lightBehavior": "1", "LingerDuration": "50", "Dimming": "60"})
|
|
98
|
+
s.update_from_camera_attributes({"Occupancy": "1"})
|
|
99
|
+
assert (s.light_behavior, s.light_linger_duration, s.light_brightness) == (
|
|
100
|
+
"flash", 50, 60)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# --- writing -----------------------------------------------------------------
|
|
104
|
+
|
|
105
|
+
def test_setting_the_behavior_sends_the_camera_its_own_number():
|
|
106
|
+
cam = _Cam()
|
|
107
|
+
assert asyncio.run(cam.async_set_light_behavior("flash")) is True
|
|
108
|
+
assert cam.attrs == [("lightBehavior", 1)]
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_an_unknown_behavior_is_refused_and_nothing_is_sent():
|
|
112
|
+
cam = _Cam()
|
|
113
|
+
with pytest.raises(ValueError):
|
|
114
|
+
asyncio.run(cam.async_set_light_behavior("strobe"))
|
|
115
|
+
assert cam.attrs == []
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def test_setting_the_linger_duration_sends_seconds():
|
|
119
|
+
cam = _Cam()
|
|
120
|
+
assert asyncio.run(cam.async_set_light_linger_duration(20)) is True
|
|
121
|
+
assert cam.attrs == [("LingerDuration", 20)]
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def test_a_duration_the_camera_does_not_offer_is_refused():
|
|
125
|
+
"""The camera enumerates exactly four; 35 would be accepted and ignored."""
|
|
126
|
+
cam = _Cam()
|
|
127
|
+
with pytest.raises(ValueError):
|
|
128
|
+
asyncio.run(cam.async_set_light_linger_duration(35))
|
|
129
|
+
assert cam.attrs == []
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def test_trigger_brightness_is_clamped_to_the_range_the_camera_declares():
|
|
133
|
+
"""The profile's minimum is 10, not 0 -- 0 is not "off", it is out of range."""
|
|
134
|
+
cam = _Cam()
|
|
135
|
+
asyncio.run(cam.async_set_light_brightness(0))
|
|
136
|
+
asyncio.run(cam.async_set_light_brightness(150))
|
|
137
|
+
asyncio.run(cam.async_set_light_brightness(55))
|
|
138
|
+
assert cam.attrs == [("Dimming", 10), ("Dimming", 100), ("Dimming", 55)]
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def test_the_offered_values_match_what_the_camera_profile_declares():
|
|
142
|
+
assert LIGHT_BEHAVIORS == {"constant": 0, "flash": 1}
|
|
143
|
+
assert LIGHT_LINGER_DURATIONS == (20, 30, 40, 50)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""What the MODEL declares it has, as distinct from what the cloud reports.
|
|
2
|
+
|
|
3
|
+
These are not the same thing, and the difference is what makes a control lie.
|
|
4
|
+
Measured 2026-09-08 on an A000088:
|
|
5
|
+
|
|
6
|
+
cloud property list LingerDuration = "30"
|
|
7
|
+
the camera, asked
|
|
8
|
+
directly over LAN LingerDuration = absent
|
|
9
|
+
writing it acked, and nothing changed anywhere
|
|
10
|
+
|
|
11
|
+
The cloud carries a DEFAULT for a key that model has never implemented. Gating
|
|
12
|
+
a control on "the cloud reported a value" therefore offers it on cameras where
|
|
13
|
+
it cannot work -- which is exactly what shipped in 2.23.0 and had to be pulled
|
|
14
|
+
back. The vendor app gates on the product profile instead, and so must we.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from aidot_cameras.camera.models import CameraDeviceInformation
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _device(props, **extra):
|
|
21
|
+
d = {"id": "cam1", "aesKey": ["k" * 16], "password": "p",
|
|
22
|
+
"product": {"serviceModules": [{"identity": "IPC/common attr",
|
|
23
|
+
"properties": props}]}}
|
|
24
|
+
d.update(extra)
|
|
25
|
+
return d
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_declared_properties_collects_what_the_model_offers():
|
|
29
|
+
info = CameraDeviceInformation(_device([
|
|
30
|
+
{"identity": "LingerDuration", "code": "LingerDuration"},
|
|
31
|
+
{"identity": "Dimming", "code": "Dimming"},
|
|
32
|
+
]))
|
|
33
|
+
assert "LingerDuration" in info.declared_properties
|
|
34
|
+
assert "Dimming" in info.declared_properties
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_a_placeholder_display_name_does_not_hide_a_property():
|
|
38
|
+
"""The profile's `name` is often `propertyName_<x>_<productId>`.
|
|
39
|
+
|
|
40
|
+
Keying on it instead of `identity` reported "no model declares
|
|
41
|
+
lightBehavior" when every model did.
|
|
42
|
+
"""
|
|
43
|
+
info = CameraDeviceInformation(_device([
|
|
44
|
+
{"identity": "lightBehavior", "code": "lightBehavior",
|
|
45
|
+
"name": "propertyName_lightBehavior_1679075889207132162"},
|
|
46
|
+
]))
|
|
47
|
+
assert "lightBehavior" in info.declared_properties
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_a_property_with_only_a_code_is_still_collected():
|
|
51
|
+
info = CameraDeviceInformation(_device([{"code": "Dimming"}]))
|
|
52
|
+
assert "Dimming" in info.declared_properties
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_a_device_with_no_profile_declares_nothing_and_does_not_raise():
|
|
56
|
+
info = CameraDeviceInformation({"id": "c", "aesKey": ["k" * 16]})
|
|
57
|
+
assert info.declared_properties == frozenset()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_a_malformed_profile_yields_nothing_rather_than_raising():
|
|
61
|
+
"""The profile is remote data of no guaranteed shape.
|
|
62
|
+
|
|
63
|
+
Asserted against the collector directly, not through the constructor: the
|
|
64
|
+
UPSTREAM base class parses the same profile first and indexes it without
|
|
65
|
+
type checks, so a string where a list belongs raises there before this code
|
|
66
|
+
runs. That fragility is pre-existing and shared with the light path, so it
|
|
67
|
+
is noted rather than widened into here.
|
|
68
|
+
"""
|
|
69
|
+
collect = CameraDeviceInformation._collect_declared_properties
|
|
70
|
+
for bad in ({"product": {"serviceModules": "nope"}},
|
|
71
|
+
{"product": {"serviceModules": [None]}},
|
|
72
|
+
{"product": {"serviceModules": [{"properties": "nope"}]}},
|
|
73
|
+
{"product": {"serviceModules": [{"properties": ["str"]}]}},
|
|
74
|
+
{"product": "nope"},
|
|
75
|
+
{}):
|
|
76
|
+
assert collect(bad) == frozenset()
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_declaring_nothing_is_not_the_same_as_reporting_nothing():
|
|
80
|
+
"""The A000088 case, locked in: the cloud reports it, the model does not.
|
|
81
|
+
|
|
82
|
+
A gate built on the reported value would say yes here. That is the bug.
|
|
83
|
+
"""
|
|
84
|
+
info = CameraDeviceInformation(_device([{"identity": "Dimming"}]))
|
|
85
|
+
assert "LingerDuration" not in info.declared_properties
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/_upstream.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/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.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/ble_gateway.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/__init__.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/client.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/constants.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/go2rtc.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/hwaccel.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/playback.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/protocol.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/sd_events.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/sd_listing.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/sdes.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/sdes_open.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/tutk.py
RENAMED
|
File without changes
|
{python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/camera/webrtc.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/cloud_auth.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/credentials.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-1.0.0rc15 → python_aidot_cameras-1.0.0rc17}/aidot_cameras/device_client.py
RENAMED
|
File without changes
|