islabtech_upw_sensor_v1/device/
network.rs1use serde::Deserialize;
3
4use crate::{
5 measurements::{Measurement, SuccessfulMeasurement},
6 SystemStatus,
7};
8
9pub struct NetworkConnectedDevice {
11 ip_address: std::net::IpAddr,
12 port: u16,
13 use_tls: bool,
14}
15
16pub struct TcpPort(u16);
18impl From<u16> for TcpPort {
19 fn from(value: u16) -> Self {
20 TcpPort(value)
21 }
22}
23impl Default for TcpPort {
24 fn default() -> Self {
25 80.into()
26 }
27}
28
29pub enum UseTls {
31 Off,
33 On,
35 Auto,
37}
38impl From<bool> for UseTls {
39 fn from(value: bool) -> Self {
40 if value {
41 return UseTls::On;
42 } else {
43 return UseTls::Off;
44 }
45 }
46}
47impl Default for UseTls {
48 fn default() -> Self {
49 UseTls::Auto
50 }
51}
52
53pub fn connect_via_network(
55 ip_address: std::net::IpAddr,
56 use_tls: UseTls,
57) -> NetworkConnectedDevice {
58 connect_via_network_on_port(ip_address, 80.into(), use_tls)
59}
60pub fn connect_via_network_on_port(
61 ip_address: std::net::IpAddr,
62 port: TcpPort,
63 use_tls: UseTls,
64) -> NetworkConnectedDevice {
65 NetworkConnectedDevice {
66 ip_address,
67 port: port.0,
68 use_tls: match use_tls {
69 UseTls::Off => false,
70 UseTls::On => true,
71 UseTls::Auto => port.0 == 443,
72 },
73 }
74}
75
76impl NetworkConnectedDevice {
78 fn base_url(&self) -> String {
80 format!(
81 "{proto}://{addr}:{port}",
82 proto = if self.use_tls { "https" } else { "http" },
83 addr = self.ip_address.to_string(),
84 port = self.port,
85 )
86 }
87}
88async fn fetch<T: serde::de::DeserializeOwned>(
102 device: &NetworkConnectedDevice,
103 url: &str,
104) -> Result<T, crate::Error> {
105 let client = reqwest::ClientBuilder::new()
106 .connect_timeout(std::time::Duration::from_secs(5))
107 .build()
108 .map_err(|err| crate::Error::Network(err))?;
109
110 let request = client
111 .request(reqwest::Method::GET, device.base_url() + url)
112 .build()
113 .map_err(|err| crate::Error::Network(err))?;
114 let response = client
115 .execute(request)
116 .await
117 .map_err(|err| crate::Error::Network(err))?;
118 let response = response
119 .text()
120 .await
121 .map_err(|err| crate::Error::Network(err))?;
122 serde_json::from_str::<T>(response.as_str())
123 .map_err(|err| crate::Error::InvalidJsonResponse(err))
124}
125impl super::Device for NetworkConnectedDevice {
126 async fn latest_measurement(
127 &self,
128 ) -> Result<Option<crate::measurements::Measurement>, crate::Error> {
129 fetch::<Option<Measurement>>(&self, "/api/v1/measurements/latest").await
130 }
131 async fn latest_successful_measurement(
132 &self,
133 ) -> Result<Option<crate::measurements::SuccessfulMeasurement>, crate::Error> {
134 fetch::<Option<SuccessfulMeasurement>>(&self, "/api/v1/measurements/latest_successful")
135 .await
136 }
137 async fn measurement_history(&self) -> Result<Vec<Measurement>, crate::Error> {
138 #[derive(Debug, Deserialize)]
139 struct MeasurementList {
140 pub values: Vec<Measurement>,
141 }
142 fetch::<MeasurementList>(&self, "/api/v1/measurements/history")
143 .await
144 .map(|l| l.values)
145 }
146 async fn system_status(&self) -> Result<SystemStatus, crate::Error> {
147 fetch::<SystemStatus>(&self, "/api/v1/system/status").await
148 }
149}