Skip to main content

dvrip_rs/commands/
alarm.rs

1use crate::error::Result;
2use async_trait::async_trait;
3use serde_json::Value;
4
5use crate::constants::{OK_CODES, QCODES};
6use crate::dvrip::DVRIPCam;
7use std::sync::atomic::Ordering;
8
9pub type AlarmCallback = Box<dyn Fn(Value, u32) + Send + Sync>;
10
11#[async_trait]
12pub trait Alarm: Send + Sync {
13    /// Set the alarm callback function
14    fn set_alarm_callback(&mut self, callback: Option<AlarmCallback>);
15
16    /// Clear the alarm callback
17    fn clear_alarm_callback(&mut self);
18
19    /// Start alarm monitoring
20    async fn start_alarm_monitoring(&mut self) -> Result<()>;
21
22    /// Stop alarm monitoring
23    async fn stop_alarm_monitoring(&mut self) -> Result<()>;
24
25    /// Set remote alarm
26    async fn set_remote_alarm(&mut self, state: bool) -> Result<bool>;
27
28    /// Check if monitoring alarms
29    fn is_alarm_monitoring(&self) -> bool;
30}
31
32#[async_trait]
33impl Alarm for DVRIPCam {
34    fn set_alarm_callback(&mut self, callback: Option<AlarmCallback>) {
35        let alarm_cb = self.alarm_callback.clone();
36        if let Ok(handle) = tokio::runtime::Handle::try_current() {
37            handle.spawn(async move {
38                *alarm_cb.lock().await = callback;
39            });
40        } else {
41            // If not in an async context, create a temporary runtime
42            tokio::spawn(async move {
43                *alarm_cb.lock().await = callback;
44            });
45        }
46    }
47
48    fn clear_alarm_callback(&mut self) {
49        let alarm_cb = self.alarm_callback.clone();
50        if let Ok(handle) = tokio::runtime::Handle::try_current() {
51            handle.spawn(async move {
52                *alarm_cb.lock().await = None;
53            });
54        } else {
55            tokio::spawn(async move {
56                *alarm_cb.lock().await = None;
57            });
58        }
59    }
60
61    async fn start_alarm_monitoring(&mut self) -> Result<()> {
62        let reply = self
63            .get_command(
64                "",
65                Some(QCODES.get("AlarmSet").copied().unwrap_or(1500) as u32),
66            )
67            .await?;
68
69        if let Some(ret) = reply.get("Ret").and_then(|r| r.as_u64())
70            && !OK_CODES.contains(&(ret as u32))
71        {
72            return Err(crate::error::DVRIPError::ProtocolError(
73                "Failed to start alarm monitoring".to_string(),
74            ));
75        }
76
77        self.alarm_monitoring.store(true, Ordering::Release);
78        self.start_alarm_worker().await;
79
80        Ok(())
81    }
82
83    async fn stop_alarm_monitoring(&mut self) -> Result<()> {
84        self.alarm_monitoring.store(false, Ordering::Release);
85
86        if let Some(handle) = self.alarm_handle.lock().await.take() {
87            handle.abort();
88        }
89
90        Ok(())
91    }
92
93    async fn set_remote_alarm(&mut self, state: bool) -> Result<bool> {
94        let data = serde_json::json!({
95            "Event": 0,
96            "State": state,
97        });
98
99        let reply = self.set_command("OPNetAlarm", data, None).await?;
100        if let Some(ret) = reply.get("Ret").and_then(|r| r.as_u64()) {
101            return Ok(OK_CODES.contains(&(ret as u32)));
102        }
103        Ok(false)
104    }
105
106    fn is_alarm_monitoring(&self) -> bool {
107        self.alarm_monitoring.load(Ordering::Acquire)
108    }
109}