ring_client/lib.rs
1#![deny(missing_docs)]
2#![deny(clippy::all)]
3#![warn(clippy::pedantic)]
4#![warn(clippy::nursery)]
5#![warn(missing_debug_implementations, rust_2018_idioms, rustdoc::all)]
6#![allow(rustdoc::private_doc_tests)]
7#![forbid(unsafe_code)]
8#![deny(clippy::clone_on_ref_ptr)]
9
10//! # Ring Client
11//!
12//! The Ring Client crate provides a client for interfacing with [Ring](https://www.ring.com/)
13//! home security devices.
14//!
15//! ## Usage
16//! ```toml
17//! [dependencies]
18//! ring-client = "0.1.1"
19//! ```
20//!
21//! ## Capabilities
22//!
23//! 1. Authenticate with Ring - either via Username and Password, or Refresh Tokens.
24//! 2. Interact with Ring locations - including listening for events (such as motion detectors) in
25//! real-time, as well as changing the states of devices (such as enabling or disabling an Alarm system).
26//! 3. Retrieve profile information.
27//!
28//! ## Examples
29//!
30//! More in-depth examples can be found in documentation comments on the Client methods.
31//!
32//! ### Listening for Events
33//!
34//! Perhaps one of the most useful features of the crate is the ability to listen and respond to
35//! events which occur in a location in real-time.
36//!
37//! This is done using the [`location::Listener`] method.
38//!
39//! ```no_run
40//! use ring_client::Client;
41//!
42//! use ring_client::authentication::Credentials;
43//! use ring_client::OperatingSystem;
44//!
45//! # tokio_test::block_on(async {
46//! let client = Client::new("Home Automation", "mock-system-id", OperatingSystem::Ios);
47//!
48//! // For brevity, a Refresh Token is being used here. However, the client can also
49//! // be authenticated using a username and password.
50//! //
51//! // See `Client::login` for more information.
52//! let refresh_token = Credentials::RefreshToken("".to_string());
53//!
54//! client.login(refresh_token)
55//! .await
56//! .expect("Logging in with a valid refresh token should not fail");
57//!
58//! let locations = client.get_locations()
59//! .await
60//! .expect("Getting locations should not fail");
61//!
62//! let location = locations
63//! .first()
64//! .expect("There should be at least one location");
65//!
66//! let mut listener = location.get_listener()
67//! .await
68//! .expect("Creating a listener should not fail");
69//!
70//! // Listen for events in the location and react to them using the provided closure.
71//! let result = listener.listen::<_, _, ()>(|event, _, _| async move {
72//! // Connection can be used to send commands to the Ring API.
73//! println!("New event: {:#?}", event);
74//!
75//! // The connection argument can be used to send events back to Ring in
76//! // response to the event.
77//!
78//! // Return true or false to indicate whether the listener should continue listening, or
79//! // whether the promise should be resolved.
80//! Ok(true)
81//! })
82//! .await;
83//! # });
84//!```
85//!
86//! ### Sending Events
87//!
88//! The [`location::Listener`] can also be used to send events to the Ring API, such as arming or disarming an alarm
89//! system.
90//!
91//! ```no_run
92//! use serde_json::json;
93//! use ring_client::Client;
94//!
95//! use ring_client::authentication::Credentials;
96//! use ring_client::location::{Event, Message};
97//! use ring_client::OperatingSystem;
98//!
99//! # tokio_test::block_on(async {
100//! let client = Client::new("Home Automation", "mock-system-id", OperatingSystem::Ios);
101//!
102//! // For brevity, a Refresh Token is being used here. However, the client can also
103//! // be authenticated using a username and password.
104//! //
105//! // See `Client::login` for more information.
106//! let refresh_token = Credentials::RefreshToken("".to_string());
107//!
108//! client.login(refresh_token)
109//! .await
110//! .expect("Logging in with a valid refresh token should not fail");
111//!
112//! let locations = client.get_locations()
113//! .await
114//! .expect("Getting locations should not fail");
115//!
116//! let location = locations
117//! .first()
118//! .expect("There should be at least one location");
119//!
120//! location.get_listener()
121//! .await
122//! .expect("Creating a listener should not fail")
123//! .send(
124//! Event::new(
125//! Message::DataUpdate(json!({}))
126//! )
127//! )
128//! .await
129//! .expect("Sending an event should not fail");
130//! # });
131//!```
132//!
133//! ### Listing Devices
134//!
135//! ```no_run
136//! use ring_client::Client;
137//!
138//! use ring_client::authentication::Credentials;
139//! use ring_client::OperatingSystem;
140//!
141//! # tokio_test::block_on(async {
142//! let client = Client::new("Home Automation", "mock-system-id", OperatingSystem::Ios);
143//!
144//! // For brevity, a Refresh Token is being used here. However, the client can also
145//! // be authenticated using a username and password.
146//! //
147//! // See `Client::login` for more information.
148//! let refresh_token = Credentials::RefreshToken("".to_string());
149//!
150//! client.login(refresh_token)
151//! .await
152//! .expect("Logging in with a valid refresh token should not fail");
153//!
154//! let devices = client.get_devices()
155//! .await
156//! .expect("Getting devices not fail");
157//!
158//! println!("{:#?}", devices);
159//! # });
160//!```
161//!
162//! ## Contributing
163//!
164//! There are _tons_ of features which could be added to the crate. If you'd like to contribute, please
165//! feel free to open an issue or a pull request.
166//!
167//! Examples of features which could be added:
168//! 1. Better parity between the Ring API and the structs.
169//! 2. Support for streaming video from Ring cameras and doorbells.
170//!
171//! ### Testing
172//!
173//! Many of the tests require a valid Ring account before they can be run, which can be provided
174//! via a Refresh Token being set in the `.env` file.
175//!
176//! The `.env` file can be created by using `.env.example` as a template:
177//! ```sh
178//! cp .env.example .env
179//! ```
180
181//! #### Running tests
182
183//! The tests can be run with:
184//! ```sh
185//! cargo test
186//! ```
187
188mod client;
189mod constant;
190mod helper;
191
192pub use client::*;
193
194#[doc(hidden)]
195pub use helper::OperatingSystem;