Skip to main content

alarm_dot_com/
lib.rs

1//! # alarm-dot-com
2//!
3//! A Rust library for interfacing with Alarm.com security panels.
4//!
5//! This library reverse-engineers the Alarm.com web portal's AJAX API to provide
6//! programmatic access to your alarm panel, sensors, locks, garage doors, lights,
7//! and thermostats.
8//!
9//! ## Quick Start
10//!
11//! ```no_run
12//! use alarm_dot_com::AlarmDotCom;
13//!
14//! #[tokio::main]
15//! async fn main() -> alarm_dot_com::error::Result<()> {
16//!     let mut alarm = AlarmDotCom::new("user@example.com", "password");
17//!
18//!     // For accounts with 2FA, set the trusted device cookie from your browser:
19//!     // alarm.set_trusted_device_cookie("cookie-value-from-browser");
20//!
21//!     alarm.login().await?;
22//!
23//!     let status = alarm.fetch_status().await?;
24//!     for partition in &status.partitions {
25//!         println!("{}: {}", partition.name, partition.state);
26//!     }
27//!     Ok(())
28//! }
29//! ```
30
31pub mod auth;
32pub mod client;
33pub mod controllers;
34pub mod error;
35pub mod models;
36
37use client::AlarmClient;
38use error::Result;
39use models::garage_door::GarageDoor;
40use models::light::Light;
41use models::lock::Lock;
42use models::partition::{ArmingOption, Partition};
43use models::sensor::Sensor;
44use models::system::System;
45use models::thermostat::Thermostat;
46
47/// A snapshot of the full system state.
48#[derive(Debug, Clone)]
49pub struct SystemStatus {
50    pub system: System,
51    pub partitions: Vec<Partition>,
52    pub sensors: Vec<Sensor>,
53    pub locks: Vec<Lock>,
54    pub garage_doors: Vec<GarageDoor>,
55    pub lights: Vec<Light>,
56    pub thermostats: Vec<Thermostat>,
57}
58
59/// High-level API entry point for Alarm.com.
60///
61/// Wraps authentication, session management, and all device controllers
62/// behind a single ergonomic interface.
63pub struct AlarmDotCom {
64    client: AlarmClient,
65}
66
67impl AlarmDotCom {
68    /// Create a new `AlarmDotCom` instance with the given credentials.
69    pub fn new(username: impl Into<String>, password: impl Into<String>) -> Self {
70        AlarmDotCom {
71            client: AlarmClient::new(username, password),
72        }
73    }
74
75    /// Set a trusted device cookie to bypass 2FA on login.
76    ///
77    /// This is the `twoFactorAuthenticationId` cookie value from your browser's
78    /// cookies for `www.alarm.com` after completing 2FA manually. With this set,
79    /// the server recognizes the client as a trusted device and skips 2FA.
80    pub fn set_trusted_device_cookie(&mut self, cookie: impl Into<String>) {
81        self.client.set_trusted_device_cookie(cookie);
82    }
83
84    /// Get the current trusted device cookie value, if available.
85    pub fn trusted_device_cookie(&self) -> Option<&str> {
86        self.client.trusted_device_cookie()
87    }
88
89    /// Perform the login flow.
90    ///
91    /// Returns `Err(AlarmError::TwoFactorRequired)` if 2FA is needed and no
92    /// trusted device cookie was provided via [`set_trusted_device_cookie`](Self::set_trusted_device_cookie).
93    pub async fn login(&mut self) -> Result<()> {
94        self.client.login().await
95    }
96
97    /// Whether the client is currently authenticated.
98    pub fn is_logged_in(&self) -> bool {
99        self.client.is_logged_in()
100    }
101
102    /// Send a keep-alive signal to prevent session timeout.
103    /// Returns `false` if the session has expired (re-login needed).
104    pub async fn keep_alive(&mut self) -> Result<bool> {
105        self.client.keep_alive().await
106    }
107
108    // ---- System ----
109
110    /// Fetch the primary system and all its devices in one call.
111    pub async fn fetch_status(&mut self) -> Result<SystemStatus> {
112        let system = controllers::systems::fetch_primary_system(&mut self.client).await?;
113
114        let partitions = controllers::partitions::fetch_partitions(&mut self.client)
115            .await
116            .unwrap_or_default();
117        let sensors = controllers::sensors::fetch_sensors(&mut self.client)
118            .await
119            .unwrap_or_default();
120        let locks = controllers::locks::fetch_locks(&mut self.client)
121            .await
122            .unwrap_or_default();
123        let garage_doors = controllers::garage_doors::fetch_garage_doors(&mut self.client)
124            .await
125            .unwrap_or_default();
126        let lights = controllers::lights::fetch_lights(&mut self.client)
127            .await
128            .unwrap_or_default();
129        let thermostats = controllers::thermostats::fetch_thermostats(&mut self.client)
130            .await
131            .unwrap_or_default();
132
133        Ok(SystemStatus {
134            system,
135            partitions,
136            sensors,
137            locks,
138            garage_doors,
139            lights,
140            thermostats,
141        })
142    }
143
144    /// Fetch the primary system info.
145    pub async fn fetch_system(&mut self) -> Result<System> {
146        controllers::systems::fetch_primary_system(&mut self.client).await
147    }
148
149    // ---- Partitions ----
150
151    /// Fetch all partitions (alarm panels).
152    pub async fn fetch_partitions(&mut self) -> Result<Vec<Partition>> {
153        controllers::partitions::fetch_partitions(&mut self.client).await
154    }
155
156    /// Arm a partition in Stay mode.
157    pub async fn arm_stay(&mut self, partition_id: &str, options: &[ArmingOption]) -> Result<()> {
158        controllers::partitions::arm_stay(&mut self.client, partition_id, options).await
159    }
160
161    /// Arm a partition in Away mode.
162    pub async fn arm_away(&mut self, partition_id: &str, options: &[ArmingOption]) -> Result<()> {
163        controllers::partitions::arm_away(&mut self.client, partition_id, options).await
164    }
165
166    /// Arm a partition in Night mode.
167    pub async fn arm_night(&mut self, partition_id: &str, options: &[ArmingOption]) -> Result<()> {
168        controllers::partitions::arm_night(&mut self.client, partition_id, options).await
169    }
170
171    /// Disarm a partition.
172    pub async fn disarm(&mut self, partition_id: &str) -> Result<()> {
173        controllers::partitions::disarm(&mut self.client, partition_id).await
174    }
175
176    /// Clear faults on a partition.
177    pub async fn clear_faults(&mut self, partition_id: &str) -> Result<()> {
178        controllers::partitions::clear_faults(&mut self.client, partition_id).await
179    }
180
181    // ---- Sensors ----
182
183    /// Fetch all sensors.
184    pub async fn fetch_sensors(&mut self) -> Result<Vec<Sensor>> {
185        controllers::sensors::fetch_sensors(&mut self.client).await
186    }
187
188    // ---- Locks ----
189
190    /// Fetch all locks.
191    pub async fn fetch_locks(&mut self) -> Result<Vec<Lock>> {
192        controllers::locks::fetch_locks(&mut self.client).await
193    }
194
195    /// Lock a lock.
196    pub async fn lock(&mut self, lock_id: &str) -> Result<()> {
197        controllers::locks::lock(&mut self.client, lock_id).await
198    }
199
200    /// Unlock a lock.
201    pub async fn unlock(&mut self, lock_id: &str) -> Result<()> {
202        controllers::locks::unlock(&mut self.client, lock_id).await
203    }
204
205    // ---- Garage Doors ----
206
207    /// Fetch all garage doors.
208    pub async fn fetch_garage_doors(&mut self) -> Result<Vec<GarageDoor>> {
209        controllers::garage_doors::fetch_garage_doors(&mut self.client).await
210    }
211
212    /// Open a garage door.
213    pub async fn open_garage_door(&mut self, id: &str) -> Result<()> {
214        controllers::garage_doors::open(&mut self.client, id).await
215    }
216
217    /// Close a garage door.
218    pub async fn close_garage_door(&mut self, id: &str) -> Result<()> {
219        controllers::garage_doors::close(&mut self.client, id).await
220    }
221
222    // ---- Lights ----
223
224    /// Fetch all lights.
225    pub async fn fetch_lights(&mut self) -> Result<Vec<Light>> {
226        controllers::lights::fetch_lights(&mut self.client).await
227    }
228
229    /// Turn a light on.
230    pub async fn turn_on_light(&mut self, id: &str) -> Result<()> {
231        controllers::lights::turn_on(&mut self.client, id).await
232    }
233
234    /// Turn a light off.
235    pub async fn turn_off_light(&mut self, id: &str) -> Result<()> {
236        controllers::lights::turn_off(&mut self.client, id).await
237    }
238
239    /// Set a light's brightness (0-100).
240    pub async fn set_light_brightness(&mut self, id: &str, level: u32) -> Result<()> {
241        controllers::lights::set_brightness(&mut self.client, id, level).await
242    }
243
244    // ---- Thermostats ----
245
246    /// Fetch all thermostats.
247    pub async fn fetch_thermostats(&mut self) -> Result<Vec<Thermostat>> {
248        controllers::thermostats::fetch_thermostats(&mut self.client).await
249    }
250
251    /// Set thermostat mode.
252    pub async fn set_thermostat_mode(
253        &mut self,
254        id: &str,
255        mode: models::thermostat::ThermostatMode,
256    ) -> Result<()> {
257        controllers::thermostats::set_mode(&mut self.client, id, mode).await
258    }
259
260    /// Set thermostat heat setpoint.
261    pub async fn set_heat_setpoint(&mut self, id: &str, temp: f64) -> Result<()> {
262        controllers::thermostats::set_heat_setpoint(&mut self.client, id, temp).await
263    }
264
265    /// Set thermostat cool setpoint.
266    pub async fn set_cool_setpoint(&mut self, id: &str, temp: f64) -> Result<()> {
267        controllers::thermostats::set_cool_setpoint(&mut self.client, id, temp).await
268    }
269}