A small rust lib for getting live heartrate data
1use std::time::Duration;
2
3use anyhow::bail;
4use futures_util::TryStreamExt;
5use reqwest::Client;
6use reqwest_websocket::{Message, RequestBuilderExt, WebSocket};
7use serde::Deserialize;
8use serde_json::json;
9use tokio::time::timeout;
10
11#[derive(Deserialize)]
12struct PulsoidResponseError{
13 message: String
14}
15
16#[derive(Deserialize)]
17struct PulsoidResponseResult{
18 #[serde(rename = "ramielUrl")]
19 ramiel_url: String
20}
21
22#[derive(Deserialize)]
23struct PulsoidResponse{
24 error: Option<PulsoidResponseError>,
25 result: Option<PulsoidResponseResult>,
26}
27
28#[derive(Deserialize)]
29struct PulsoidHRUpdateData{
30 #[serde(rename = "heartRate")]
31 hr: usize
32}
33
34#[derive(Deserialize)]
35struct PulsoidHRUpdate{
36 data: PulsoidHRUpdateData
37}
38
39pub struct Pulsoid(WebSocket);
40
41impl Pulsoid{
42 pub async fn poll( &mut self ) -> anyhow::Result<usize>{
43 let Some(message) = self.0.try_next().await? else { bail!("Could not get message from pulsoid") };
44
45 if let Message::Text(message) = message{
46 let json: PulsoidHRUpdate = serde_json::from_str(&message).unwrap();
47 Ok(json.data.hr)
48 } else{
49 bail!("Message was not formatted correctly")
50 }
51 }
52
53 pub async fn poll_timeout( &mut self, dur: Duration ) -> anyhow::Result<usize>{
54 let future = timeout(dur, self.0.try_next()).await?;
55 let Some(message) = future? else { bail!("Could not get message from pulsoid") };
56
57 if let Message::Text(message) = message{
58 let json: PulsoidHRUpdate = serde_json::from_str(&message).unwrap();
59 Ok(json.data.hr)
60 } else{
61 bail!("Message was not formatted correctly")
62 }
63 }
64}
65
66pub async fn pulsoid( widget_id: String ) -> anyhow::Result<Pulsoid>{
67 let client = Client::default();
68
69 let res = client.post("https://pulsoid.net/v1/api/public/rpc")
70 .header("Content-Type", "application/json")
71 .body(serde_json::to_string(&json!({
72 "id": "1",
73 "jsonrpc": "2.0",
74 "method": "getWidget",
75 "params": { "widgetId": widget_id }
76 }))?).send().await?;
77
78 if res.status() != 200{ bail!("Request Failed.") }
79 let res: PulsoidResponse = serde_json::from_str(&res.text().await?).unwrap();
80
81 if let Some(error) = res.error{ bail!(error.message) };
82 let Some(result) = res.result else { bail!("Failed to get result") };
83
84 let ws_url = result.ramiel_url;
85 let ws = client.get(ws_url).upgrade().send().await?.into_websocket().await?;
86
87 Ok(Pulsoid(ws))
88}