use std::sync::Arc;
use crate::client::OnvifClient;
use crate::error::OnvifError;
use crate::soap::SoapError;
use crate::transport::Transport;
use crate::types::{
AudioEncoderConfiguration, AudioEncoderConfigurationOptions, AudioSource,
AudioSourceConfiguration, Capabilities, DeviceInfo, DnsInformation, EventProperties,
FindRecordingResults, FocusMove, Hostname, ImagingMoveOptions, ImagingOptions, ImagingSettings,
ImagingStatus, MediaProfile, MediaProfile2, NetworkGateway, NetworkInterface, NetworkProtocol,
NotificationMessage, NtpInfo, OnvifService, OsdConfiguration, OsdOptions, PtzConfiguration,
PtzConfigurationOptions, PtzNode, PtzPreset, PtzStatus, PullPointSubscription, RecordingItem,
RelayOutput, SnapshotUri, StreamUri, SystemDateTime, SystemLog, User,
VideoEncoderConfiguration, VideoEncoderConfiguration2, VideoEncoderConfigurationOptions,
VideoEncoderConfigurationOptions2, VideoEncoderInstances, VideoSource,
VideoSourceConfiguration, VideoSourceConfigurationOptions,
};
pub struct OnvifSessionBuilder {
device_url: String,
credentials: Option<(String, String)>,
sync_clock: bool,
transport: Option<Arc<dyn Transport>>,
}
impl OnvifSessionBuilder {
fn new(device_url: impl Into<String>) -> Self {
Self {
device_url: device_url.into(),
credentials: None,
sync_clock: false,
transport: None,
}
}
pub fn with_credentials(
mut self,
username: impl Into<String>,
password: impl Into<String>,
) -> Self {
self.credentials = Some((username.into(), password.into()));
self
}
pub fn with_clock_sync(mut self) -> Self {
self.sync_clock = true;
self
}
pub fn with_transport(mut self, transport: Arc<dyn Transport>) -> Self {
self.transport = Some(transport);
self
}
pub async fn build(self) -> Result<OnvifSession, OnvifError> {
let mut client = OnvifClient::new(&self.device_url);
if let Some((user, pass)) = self.credentials {
client = client.with_credentials(user, pass);
}
if let Some(transport) = self.transport {
client = client.with_transport(transport);
}
if self.sync_clock {
let dt = client.get_system_date_and_time().await?;
client = client.with_utc_offset(dt.utc_offset_secs());
}
let caps = client.get_capabilities().await?;
Ok(OnvifSession { client, caps })
}
}
#[derive(Clone)]
pub struct OnvifSession {
client: OnvifClient,
caps: Capabilities,
}
impl OnvifSession {
pub fn builder(device_url: impl Into<String>) -> OnvifSessionBuilder {
OnvifSessionBuilder::new(device_url)
}
pub fn client(&self) -> &OnvifClient {
&self.client
}
pub fn capabilities(&self) -> &Capabilities {
&self.caps
}
fn media_url(&self) -> Result<&str, OnvifError> {
self.caps
.media
.url
.as_deref()
.ok_or_else(|| SoapError::missing("Media service URL").into())
}
fn media2_url(&self) -> Result<&str, OnvifError> {
self.caps
.media2_url
.as_deref()
.ok_or_else(|| SoapError::missing("Media2 service URL").into())
}
fn ptz_url(&self) -> Result<&str, OnvifError> {
self.caps
.ptz_url
.as_deref()
.ok_or_else(|| SoapError::missing("PTZ service URL").into())
}
fn imaging_url(&self) -> Result<&str, OnvifError> {
self.caps
.imaging_url
.as_deref()
.ok_or_else(|| SoapError::missing("Imaging service URL").into())
}
fn events_url(&self) -> Result<&str, OnvifError> {
self.caps
.events
.url
.as_deref()
.ok_or_else(|| SoapError::missing("Events service URL").into())
}
fn recording_url(&self) -> Result<&str, OnvifError> {
self.caps
.recording_url
.as_deref()
.ok_or_else(|| SoapError::missing("Recording service URL").into())
}
fn search_url(&self) -> Result<&str, OnvifError> {
self.caps
.search_url
.as_deref()
.ok_or_else(|| SoapError::missing("Search service URL").into())
}
fn replay_url(&self) -> Result<&str, OnvifError> {
self.caps
.replay_url
.as_deref()
.ok_or_else(|| SoapError::missing("Replay service URL").into())
}
pub async fn get_services(&self) -> Result<Vec<OnvifService>, OnvifError> {
self.client.get_services().await
}
pub async fn get_system_date_and_time(&self) -> Result<SystemDateTime, OnvifError> {
self.client.get_system_date_and_time().await
}
pub async fn get_device_info(&self) -> Result<DeviceInfo, OnvifError> {
self.client.get_device_info().await
}
pub async fn get_hostname(&self) -> Result<Hostname, OnvifError> {
self.client.get_hostname().await
}
pub async fn set_hostname(&self, name: &str) -> Result<(), OnvifError> {
self.client.set_hostname(name).await
}
pub async fn get_ntp(&self) -> Result<NtpInfo, OnvifError> {
self.client.get_ntp().await
}
pub async fn set_ntp(&self, from_dhcp: bool, servers: &[&str]) -> Result<(), OnvifError> {
self.client.set_ntp(from_dhcp, servers).await
}
pub async fn system_reboot(&self) -> Result<String, OnvifError> {
self.client.system_reboot().await
}
pub async fn get_scopes(&self) -> Result<Vec<String>, OnvifError> {
self.client.get_scopes().await
}
pub async fn get_users(&self) -> Result<Vec<User>, OnvifError> {
self.client.get_users().await
}
pub async fn create_users(&self, users: &[(&str, &str, &str)]) -> Result<(), OnvifError> {
self.client.create_users(users).await
}
pub async fn delete_users(&self, usernames: &[&str]) -> Result<(), OnvifError> {
self.client.delete_users(usernames).await
}
pub async fn set_user(
&self,
username: &str,
password: Option<&str>,
user_level: &str,
) -> Result<(), OnvifError> {
self.client.set_user(username, password, user_level).await
}
pub async fn get_network_interfaces(&self) -> Result<Vec<NetworkInterface>, OnvifError> {
self.client.get_network_interfaces().await
}
pub async fn set_network_interfaces(
&self,
token: &str,
enabled: bool,
ipv4_address: &str,
prefix_length: u32,
from_dhcp: bool,
) -> Result<bool, OnvifError> {
self.client
.set_network_interfaces(token, enabled, ipv4_address, prefix_length, from_dhcp)
.await
}
pub async fn get_network_protocols(&self) -> Result<Vec<NetworkProtocol>, OnvifError> {
self.client.get_network_protocols().await
}
pub async fn get_dns(&self) -> Result<DnsInformation, OnvifError> {
self.client.get_dns().await
}
pub async fn set_dns(&self, from_dhcp: bool, servers: &[&str]) -> Result<(), OnvifError> {
self.client.set_dns(from_dhcp, servers).await
}
pub async fn get_network_default_gateway(&self) -> Result<NetworkGateway, OnvifError> {
self.client.get_network_default_gateway().await
}
pub async fn get_system_log(&self, log_type: &str) -> Result<SystemLog, OnvifError> {
self.client.get_system_log(log_type).await
}
pub async fn get_relay_outputs(&self) -> Result<Vec<RelayOutput>, OnvifError> {
self.client.get_relay_outputs().await
}
pub async fn set_relay_output_state(
&self,
relay_token: &str,
state: &str,
) -> Result<(), OnvifError> {
self.client.set_relay_output_state(relay_token, state).await
}
pub async fn get_profiles(&self) -> Result<Vec<MediaProfile>, OnvifError> {
self.client.get_profiles(self.media_url()?).await
}
pub async fn get_profile(&self, profile_token: &str) -> Result<MediaProfile, OnvifError> {
self.client
.get_profile(self.media_url()?, profile_token)
.await
}
pub async fn create_profile(
&self,
name: &str,
token: Option<&str>,
) -> Result<MediaProfile, OnvifError> {
self.client
.create_profile(self.media_url()?, name, token)
.await
}
pub async fn delete_profile(&self, profile_token: &str) -> Result<(), OnvifError> {
self.client
.delete_profile(self.media_url()?, profile_token)
.await
}
pub async fn get_stream_uri(&self, profile_token: &str) -> Result<StreamUri, OnvifError> {
self.client
.get_stream_uri(self.media_url()?, profile_token)
.await
}
pub async fn get_snapshot_uri(&self, profile_token: &str) -> Result<SnapshotUri, OnvifError> {
self.client
.get_snapshot_uri(self.media_url()?, profile_token)
.await
}
pub async fn add_video_encoder_configuration(
&self,
profile_token: &str,
config_token: &str,
) -> Result<(), OnvifError> {
self.client
.add_video_encoder_configuration(self.media_url()?, profile_token, config_token)
.await
}
pub async fn remove_video_encoder_configuration(
&self,
profile_token: &str,
) -> Result<(), OnvifError> {
self.client
.remove_video_encoder_configuration(self.media_url()?, profile_token)
.await
}
pub async fn add_video_source_configuration(
&self,
profile_token: &str,
config_token: &str,
) -> Result<(), OnvifError> {
self.client
.add_video_source_configuration(self.media_url()?, profile_token, config_token)
.await
}
pub async fn remove_video_source_configuration(
&self,
profile_token: &str,
) -> Result<(), OnvifError> {
self.client
.remove_video_source_configuration(self.media_url()?, profile_token)
.await
}
pub async fn get_video_sources(&self) -> Result<Vec<VideoSource>, OnvifError> {
self.client.get_video_sources(self.media_url()?).await
}
pub async fn get_video_source_configurations(
&self,
) -> Result<Vec<VideoSourceConfiguration>, OnvifError> {
self.client
.get_video_source_configurations(self.media_url()?)
.await
}
pub async fn get_video_source_configuration(
&self,
token: &str,
) -> Result<VideoSourceConfiguration, OnvifError> {
self.client
.get_video_source_configuration(self.media_url()?, token)
.await
}
pub async fn set_video_source_configuration(
&self,
config: &VideoSourceConfiguration,
) -> Result<(), OnvifError> {
self.client
.set_video_source_configuration(self.media_url()?, config)
.await
}
pub async fn get_video_source_configuration_options(
&self,
config_token: Option<&str>,
) -> Result<VideoSourceConfigurationOptions, OnvifError> {
self.client
.get_video_source_configuration_options(self.media_url()?, config_token)
.await
}
pub async fn get_video_encoder_configurations(
&self,
) -> Result<Vec<VideoEncoderConfiguration>, OnvifError> {
self.client
.get_video_encoder_configurations(self.media_url()?)
.await
}
pub async fn get_video_encoder_configuration(
&self,
token: &str,
) -> Result<VideoEncoderConfiguration, OnvifError> {
self.client
.get_video_encoder_configuration(self.media_url()?, token)
.await
}
pub async fn set_video_encoder_configuration(
&self,
config: &VideoEncoderConfiguration,
) -> Result<(), OnvifError> {
self.client
.set_video_encoder_configuration(self.media_url()?, config)
.await
}
pub async fn get_video_encoder_configuration_options(
&self,
config_token: Option<&str>,
) -> Result<VideoEncoderConfigurationOptions, OnvifError> {
self.client
.get_video_encoder_configuration_options(self.media_url()?, config_token)
.await
}
pub async fn get_osds(
&self,
config_token: Option<&str>,
) -> Result<Vec<OsdConfiguration>, OnvifError> {
self.client.get_osds(self.media_url()?, config_token).await
}
pub async fn get_osd(&self, osd_token: &str) -> Result<OsdConfiguration, OnvifError> {
self.client.get_osd(self.media_url()?, osd_token).await
}
pub async fn set_osd(&self, osd: &OsdConfiguration) -> Result<(), OnvifError> {
self.client.set_osd(self.media_url()?, osd).await
}
pub async fn create_osd(&self, osd: &OsdConfiguration) -> Result<String, OnvifError> {
self.client.create_osd(self.media_url()?, osd).await
}
pub async fn delete_osd(&self, osd_token: &str) -> Result<(), OnvifError> {
self.client.delete_osd(self.media_url()?, osd_token).await
}
pub async fn get_osd_options(&self, config_token: &str) -> Result<OsdOptions, OnvifError> {
self.client
.get_osd_options(self.media_url()?, config_token)
.await
}
pub async fn get_audio_sources(&self) -> Result<Vec<AudioSource>, OnvifError> {
self.client.get_audio_sources(self.media_url()?).await
}
pub async fn get_audio_source_configurations(
&self,
) -> Result<Vec<AudioSourceConfiguration>, OnvifError> {
self.client
.get_audio_source_configurations(self.media_url()?)
.await
}
pub async fn get_audio_encoder_configurations(
&self,
) -> Result<Vec<AudioEncoderConfiguration>, OnvifError> {
self.client
.get_audio_encoder_configurations(self.media_url()?)
.await
}
pub async fn get_audio_encoder_configuration(
&self,
config_token: &str,
) -> Result<AudioEncoderConfiguration, OnvifError> {
self.client
.get_audio_encoder_configuration(self.media_url()?, config_token)
.await
}
pub async fn set_audio_encoder_configuration(
&self,
config: &AudioEncoderConfiguration,
) -> Result<(), OnvifError> {
self.client
.set_audio_encoder_configuration(self.media_url()?, config)
.await
}
pub async fn get_audio_encoder_configuration_options(
&self,
config_token: &str,
) -> Result<AudioEncoderConfigurationOptions, OnvifError> {
self.client
.get_audio_encoder_configuration_options(self.media_url()?, config_token)
.await
}
pub async fn get_profiles_media2(&self) -> Result<Vec<MediaProfile2>, OnvifError> {
self.client.get_profiles_media2(self.media2_url()?).await
}
pub async fn get_stream_uri_media2(&self, profile_token: &str) -> Result<String, OnvifError> {
self.client
.get_stream_uri_media2(self.media2_url()?, profile_token)
.await
}
pub async fn get_snapshot_uri_media2(&self, profile_token: &str) -> Result<String, OnvifError> {
self.client
.get_snapshot_uri_media2(self.media2_url()?, profile_token)
.await
}
pub async fn get_video_source_configurations_media2(
&self,
) -> Result<Vec<VideoSourceConfiguration>, OnvifError> {
self.client
.get_video_source_configurations_media2(self.media2_url()?)
.await
}
pub async fn set_video_source_configuration_media2(
&self,
config: &VideoSourceConfiguration,
) -> Result<(), OnvifError> {
self.client
.set_video_source_configuration_media2(self.media2_url()?, config)
.await
}
pub async fn get_video_source_configuration_options_media2(
&self,
config_token: Option<&str>,
) -> Result<VideoSourceConfigurationOptions, OnvifError> {
self.client
.get_video_source_configuration_options_media2(self.media2_url()?, config_token)
.await
}
pub async fn get_video_encoder_configurations_media2(
&self,
) -> Result<Vec<VideoEncoderConfiguration2>, OnvifError> {
self.client
.get_video_encoder_configurations_media2(self.media2_url()?)
.await
}
pub async fn get_video_encoder_configuration_media2(
&self,
token: &str,
) -> Result<VideoEncoderConfiguration2, OnvifError> {
self.client
.get_video_encoder_configuration_media2(self.media2_url()?, token)
.await
}
pub async fn set_video_encoder_configuration_media2(
&self,
config: &VideoEncoderConfiguration2,
) -> Result<(), OnvifError> {
self.client
.set_video_encoder_configuration_media2(self.media2_url()?, config)
.await
}
pub async fn get_video_encoder_configuration_options_media2(
&self,
config_token: Option<&str>,
) -> Result<VideoEncoderConfigurationOptions2, OnvifError> {
self.client
.get_video_encoder_configuration_options_media2(self.media2_url()?, config_token)
.await
}
pub async fn get_video_encoder_instances_media2(
&self,
config_token: &str,
) -> Result<VideoEncoderInstances, OnvifError> {
self.client
.get_video_encoder_instances_media2(self.media2_url()?, config_token)
.await
}
pub async fn create_profile_media2(&self, name: &str) -> Result<String, OnvifError> {
self.client
.create_profile_media2(self.media2_url()?, name)
.await
}
pub async fn delete_profile_media2(&self, token: &str) -> Result<(), OnvifError> {
self.client
.delete_profile_media2(self.media2_url()?, token)
.await
}
pub async fn ptz_absolute_move(
&self,
profile_token: &str,
pan: f32,
tilt: f32,
zoom: f32,
) -> Result<(), OnvifError> {
self.client
.ptz_absolute_move(self.ptz_url()?, profile_token, pan, tilt, zoom)
.await
}
pub async fn ptz_relative_move(
&self,
profile_token: &str,
pan: f32,
tilt: f32,
zoom: f32,
) -> Result<(), OnvifError> {
self.client
.ptz_relative_move(self.ptz_url()?, profile_token, pan, tilt, zoom)
.await
}
pub async fn ptz_continuous_move(
&self,
profile_token: &str,
pan: f32,
tilt: f32,
zoom: f32,
) -> Result<(), OnvifError> {
self.client
.ptz_continuous_move(self.ptz_url()?, profile_token, pan, tilt, zoom)
.await
}
pub async fn ptz_stop(&self, profile_token: &str) -> Result<(), OnvifError> {
self.client.ptz_stop(self.ptz_url()?, profile_token).await
}
pub async fn ptz_get_presets(&self, profile_token: &str) -> Result<Vec<PtzPreset>, OnvifError> {
self.client
.ptz_get_presets(self.ptz_url()?, profile_token)
.await
}
pub async fn ptz_goto_preset(
&self,
profile_token: &str,
preset_token: &str,
) -> Result<(), OnvifError> {
self.client
.ptz_goto_preset(self.ptz_url()?, profile_token, preset_token)
.await
}
pub async fn ptz_set_preset(
&self,
profile_token: &str,
preset_name: Option<&str>,
preset_token: Option<&str>,
) -> Result<String, OnvifError> {
self.client
.ptz_set_preset(self.ptz_url()?, profile_token, preset_name, preset_token)
.await
}
pub async fn ptz_remove_preset(
&self,
profile_token: &str,
preset_token: &str,
) -> Result<(), OnvifError> {
self.client
.ptz_remove_preset(self.ptz_url()?, profile_token, preset_token)
.await
}
pub async fn ptz_get_status(&self, profile_token: &str) -> Result<PtzStatus, OnvifError> {
self.client
.ptz_get_status(self.ptz_url()?, profile_token)
.await
}
pub async fn ptz_goto_home_position(
&self,
profile_token: &str,
speed: Option<f32>,
) -> Result<(), OnvifError> {
self.client
.ptz_goto_home_position(self.ptz_url()?, profile_token, speed)
.await
}
pub async fn ptz_set_home_position(&self, profile_token: &str) -> Result<(), OnvifError> {
self.client
.ptz_set_home_position(self.ptz_url()?, profile_token)
.await
}
pub async fn ptz_get_configurations(&self) -> Result<Vec<PtzConfiguration>, OnvifError> {
self.client.ptz_get_configurations(self.ptz_url()?).await
}
pub async fn ptz_get_configuration(
&self,
config_token: &str,
) -> Result<PtzConfiguration, OnvifError> {
self.client
.ptz_get_configuration(self.ptz_url()?, config_token)
.await
}
pub async fn ptz_set_configuration(
&self,
config: &PtzConfiguration,
force_persist: bool,
) -> Result<(), OnvifError> {
self.client
.ptz_set_configuration(self.ptz_url()?, config, force_persist)
.await
}
pub async fn ptz_get_configuration_options(
&self,
config_token: &str,
) -> Result<PtzConfigurationOptions, OnvifError> {
self.client
.ptz_get_configuration_options(self.ptz_url()?, config_token)
.await
}
pub async fn ptz_get_nodes(&self) -> Result<Vec<PtzNode>, OnvifError> {
self.client.ptz_get_nodes(self.ptz_url()?).await
}
pub async fn get_imaging_settings(
&self,
video_source_token: &str,
) -> Result<ImagingSettings, OnvifError> {
self.client
.get_imaging_settings(self.imaging_url()?, video_source_token)
.await
}
pub async fn set_imaging_settings(
&self,
video_source_token: &str,
settings: &ImagingSettings,
) -> Result<(), OnvifError> {
self.client
.set_imaging_settings(self.imaging_url()?, video_source_token, settings)
.await
}
pub async fn get_imaging_options(
&self,
video_source_token: &str,
) -> Result<ImagingOptions, OnvifError> {
self.client
.get_imaging_options(self.imaging_url()?, video_source_token)
.await
}
pub async fn imaging_move(
&self,
video_source_token: &str,
focus: &FocusMove,
) -> Result<(), OnvifError> {
self.client
.imaging_move(self.imaging_url()?, video_source_token, focus)
.await
}
pub async fn imaging_stop(&self, video_source_token: &str) -> Result<(), OnvifError> {
self.client
.imaging_stop(self.imaging_url()?, video_source_token)
.await
}
pub async fn imaging_get_move_options(
&self,
video_source_token: &str,
) -> Result<ImagingMoveOptions, OnvifError> {
self.client
.imaging_get_move_options(self.imaging_url()?, video_source_token)
.await
}
pub async fn imaging_get_status(
&self,
video_source_token: &str,
) -> Result<ImagingStatus, OnvifError> {
self.client
.imaging_get_status(self.imaging_url()?, video_source_token)
.await
}
pub async fn get_event_properties(&self) -> Result<EventProperties, OnvifError> {
self.client.get_event_properties(self.events_url()?).await
}
pub async fn create_pull_point_subscription(
&self,
filter: Option<&str>,
initial_termination_time: Option<&str>,
) -> Result<PullPointSubscription, OnvifError> {
self.client
.create_pull_point_subscription(self.events_url()?, filter, initial_termination_time)
.await
}
pub async fn pull_messages(
&self,
subscription_url: &str,
timeout: &str,
max_messages: u32,
) -> Result<Vec<NotificationMessage>, OnvifError> {
self.client
.pull_messages(subscription_url, timeout, max_messages)
.await
}
pub async fn renew_subscription(
&self,
subscription_url: &str,
termination_time: &str,
) -> Result<String, OnvifError> {
self.client
.renew_subscription(subscription_url, termination_time)
.await
}
pub async fn unsubscribe(&self, subscription_url: &str) -> Result<(), OnvifError> {
self.client.unsubscribe(subscription_url).await
}
pub async fn get_recordings(&self) -> Result<Vec<RecordingItem>, OnvifError> {
self.client.get_recordings(self.recording_url()?).await
}
pub async fn find_recordings(
&self,
max_matches: Option<u32>,
keep_alive_timeout: &str,
) -> Result<String, OnvifError> {
self.client
.find_recordings(self.search_url()?, max_matches, keep_alive_timeout)
.await
}
pub async fn get_recording_search_results(
&self,
search_token: &str,
max_results: u32,
wait_time: &str,
) -> Result<FindRecordingResults, OnvifError> {
self.client
.get_recording_search_results(self.search_url()?, search_token, max_results, wait_time)
.await
}
pub async fn end_search(&self, search_token: &str) -> Result<(), OnvifError> {
self.client
.end_search(self.search_url()?, search_token)
.await
}
pub async fn get_replay_uri(
&self,
recording_token: &str,
stream_type: &str,
protocol: &str,
) -> Result<String, OnvifError> {
self.client
.get_replay_uri(self.replay_url()?, recording_token, stream_type, protocol)
.await
}
}
#[cfg(test)]
#[path = "tests/session_tests.rs"]
mod tests;