1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// Legacy API device endpoints
//
// Device management via stat/device (read) and cmd/devmgr (commands).
// Covers listing, adoption, restart, firmware upgrade, and LED locate.
use serde_json::json;
use tracing::debug;
use crate::error::Error;
use crate::legacy::client::LegacyClient;
use crate::legacy::models::LegacyDevice;
impl LegacyClient {
/// List all devices with full statistics.
///
/// `GET /api/s/{site}/stat/device`
pub async fn list_devices(&self) -> Result<Vec<LegacyDevice>, Error> {
let url = self.site_url("stat/device");
debug!("listing devices");
self.get(url).await
}
/// Get a single device by MAC address.
///
/// Filters the device list by MAC. Returns `None` if no device matches.
pub async fn get_device(&self, mac: &str) -> Result<Option<LegacyDevice>, Error> {
let url = self.site_url("stat/device");
let body = json!({ "macs": [mac.to_lowercase()] });
let devices: Vec<LegacyDevice> = self.post(url, &body).await?;
Ok(devices.into_iter().next())
}
/// Adopt a pending device.
///
/// `POST /api/s/{site}/cmd/devmgr` with `{"cmd": "adopt", "mac": "..."}`
pub async fn adopt_device(&self, mac: &str) -> Result<(), Error> {
let url = self.site_url("cmd/devmgr");
debug!(mac, "adopting device");
let _: Vec<serde_json::Value> = self
.post(
url,
&json!({
"cmd": "adopt",
"mac": mac,
}),
)
.await?;
Ok(())
}
/// Restart a device.
///
/// `POST /api/s/{site}/cmd/devmgr` with `{"cmd": "restart", "mac": "..."}`
pub async fn restart_device(&self, mac: &str) -> Result<(), Error> {
let url = self.site_url("cmd/devmgr");
debug!(mac, "restarting device");
let _: Vec<serde_json::Value> = self
.post(
url,
&json!({
"cmd": "restart",
"mac": mac,
}),
)
.await?;
Ok(())
}
/// Upgrade device firmware.
///
/// If `url` is `Some`, upgrades from that URL (`cmd: "upgrade-external"`).
/// Otherwise upgrades from Ubiquiti's cloud (`cmd: "upgrade"`).
pub async fn upgrade_device(&self, mac: &str, firmware_url: Option<&str>) -> Result<(), Error> {
let api_url = self.site_url("cmd/devmgr");
debug!(mac, ?firmware_url, "upgrading device firmware");
let body = match firmware_url {
Some(fw_url) => json!({
"cmd": "upgrade-external",
"mac": mac,
"url": fw_url,
}),
None => json!({
"cmd": "upgrade",
"mac": mac,
}),
};
let _: Vec<serde_json::Value> = self.post(api_url, &body).await?;
Ok(())
}
/// Force re-provision a device configuration.
///
/// `POST /api/s/{site}/cmd/devmgr` with `{"cmd": "force-provision", "mac": "..."}`
pub async fn provision_device(&self, mac: &str) -> Result<(), Error> {
let url = self.site_url("cmd/devmgr");
debug!(mac, "force-provisioning device");
let _: Vec<serde_json::Value> = self
.post(
url,
&json!({
"cmd": "force-provision",
"mac": mac,
}),
)
.await?;
Ok(())
}
/// Trigger a site speed test (gateway).
///
/// `POST /api/s/{site}/cmd/devmgr` with `{"cmd": "speedtest"}`
pub async fn speedtest(&self) -> Result<(), Error> {
let url = self.site_url("cmd/devmgr");
debug!("starting speed test");
let _: Vec<serde_json::Value> = self.post(url, &json!({ "cmd": "speedtest" })).await?;
Ok(())
}
/// Toggle the LED locator on a device.
///
/// `enable: true` sends `set-locate`, `false` sends `unset-locate`.
pub async fn locate_device(&self, mac: &str, enable: bool) -> Result<(), Error> {
let url = self.site_url("cmd/devmgr");
let cmd = if enable { "set-locate" } else { "unset-locate" };
debug!(mac, cmd, "toggling device locate LED");
let _: Vec<serde_json::Value> = self
.post(
url,
&json!({
"cmd": cmd,
"mac": mac,
}),
)
.await?;
Ok(())
}
}