From bd3674accfba8bb89e8e78d923d321e04a861964 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Tue, 10 Sep 2024 21:37:18 +0200 Subject: [PATCH 1/3] continue working on traveldings (get live checkin thing working maybe??) --- Cargo.lock | 1 + programs/traveldings/Cargo.toml | 1 + .../src/commands/current_journey.rs | 177 +++++++++++++++++- programs/traveldings/src/traewelling.rs | 45 ++++- programs/traveldings/src/traewelling/model.rs | 80 +++++--- 5 files changed, 274 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7e5d422..83b54a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1722,6 +1722,7 @@ dependencies = [ "reqwest", "serde", "serde_json", + "thiserror", "tokio", ] diff --git a/programs/traveldings/Cargo.toml b/programs/traveldings/Cargo.toml index 3b8ad29..c21a6d1 100644 --- a/programs/traveldings/Cargo.toml +++ b/programs/traveldings/Cargo.toml @@ -8,6 +8,7 @@ serde = { version = "1.0.209", features = ["derive"] } serde_json = "1.0.128" reqwest = {version = "0.12.7", default-features = false, features = ["rustls-tls", "charset", "http2"]} tokio = { version = "1", features = ["full"] } +thiserror = "1" anyhow = "1" chrono = { version = "0.4", features = ["serde"]} clap = { version = "4.5", features = ["derive"]} diff --git a/programs/traveldings/src/commands/current_journey.rs b/programs/traveldings/src/commands/current_journey.rs index ee57973..c7a8792 100644 --- a/programs/traveldings/src/commands/current_journey.rs +++ b/programs/traveldings/src/commands/current_journey.rs @@ -1,9 +1,182 @@ -use crate::traewelling::TraewellingClient; +use std::time::Duration; + +use chrono::Local; +use reqwest::StatusCode; +use serde::Serialize; +use tokio::time::sleep; + +use crate::traewelling::{ + model::{JsonableData, Status, StopJourneyPart}, + RequestErr, TraewellingClient, +}; pub async fn get_current_journey() -> anyhow::Result<()> { let client = TraewellingClient::new()?; - println!("active: {:#?}", client.get_active_checkin().await?); + let mut state; + let mut cur_active_checkin = None; + + loop { + match client.get_active_checkin().await { + Ok(status) => { + cur_active_checkin = Some(status); + state = State::Live; + } + Err(err) => { + if err == RequestErr::WithStatus(StatusCode::NOT_FOUND) { + state = State::NoCheckin; + cur_active_checkin = None; + } else { + state = State::NoConnectionOrSomethingElseDoesntWork; + } + } + }; + + match (state, &cur_active_checkin) { + (State::Live | State::NoConnectionOrSomethingElseDoesntWork, Some(status)) => { + let live = state == State::Live; + let out = CurrentJourneyOutput::new(&status, live); + + println!( + "{}", + serde_json::to_string(&out) + .expect("serde should not make you sad but it does because it's serde") + ); + sleep(Duration::from_secs(20)).await; + } + (_, None) | (State::NoCheckin, Some(_)) => { + println!("null"); + sleep(Duration::from_secs(60)).await; + } + } + } Ok(()) } + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +enum State { + Live, + NoConnectionOrSomethingElseDoesntWork, + NoCheckin, +} + +#[derive(Serialize)] +struct CurrentJourneyOutput { + live: bool, + // Journey progress, 0.0-1.0 + progress: Option, + time_left: Option, + icon: String, + + // Invalid data received? + departure_err: bool, + departure_planned: Option, + departure_real: Option, + departure_station: String, + departure_ril100: Option, + departure_platform_data_available: bool, + departure_platform_planned: Option, + departure_platform_real: Option, + + // Invalid data received? + arrival_err: bool, + arrival_planned: Option, + arrival_real: Option, + arrival_station: String, + arrival_ril100: Option, + arrival_platform_data_available: bool, + arrival_platform_planned: Option, + arrival_platform_real: Option, +} + +impl CurrentJourneyOutput { + fn new(checkin: &Status, live: bool) -> Self { + let JsonableData { + time_err: departure_err, + time_planned: departure_planned, + time_real: departure_real, + station: departure_station, + ril100: departure_ril100, + platform_data_available: departure_platform_data_available, + platform_planned: departure_platform_planned, + platform_real: departure_platform_real, + } = checkin.train.origin.get_time_data(StopJourneyPart::Origin); + let JsonableData { + time_err: arrival_err, + time_planned: arrival_planned, + time_real: arrival_real, + station: arrival_station, + ril100: arrival_ril100, + platform_data_available: arrival_platform_data_available, + platform_planned: arrival_platform_planned, + platform_real: arrival_platform_real, + } = checkin + .train + .destination + .get_time_data(StopJourneyPart::Destination); + + let (progress, time_left) = if !departure_err && !arrival_err { + let departure = departure_real.unwrap_or(departure_planned.unwrap()); + let arrival = arrival_real.unwrap_or(arrival_planned.unwrap()); + let dur = arrival - departure; + + let now = Local::now().timestamp(); + + let progress = ((now - departure) as f32) / dur as f32; + let time_left = arrival - now; + + (Some(progress), Some(time_left)) + } else { + (None, None) + }; + + let icon = match checkin.train.category.as_str() { + "nationalExpress" | "national" => "longDistanceTrans", + "regionalExp" | "regional" => "regionalTrans", + "suburban" => "localTrans", + "subway" => "subTrans", + "bus" => "bus", + "tram" => "tram", + "ferry" => "ferry", + _ => "other", + } + .to_string(); + + CurrentJourneyOutput { + live, + progress, + time_left, + icon, + departure_err, + departure_planned, + departure_real, + departure_station, + departure_ril100, + departure_platform_data_available, + departure_platform_planned, + departure_platform_real, + arrival_err, + arrival_planned, + arrival_real, + arrival_station, + arrival_ril100, + arrival_platform_data_available, + arrival_platform_planned, + arrival_platform_real, + } + } +} + +enum TransportType { + // FV, ob jetzt NJ, IC, ICE... egal + LongDistanceTrans, + RegionalTrans, + // S-bahn... + LocalTrans, + // U-bahn + SubTrans, + Bus, + Tram, + Ferry, +} diff --git a/programs/traveldings/src/traewelling.rs b/programs/traveldings/src/traewelling.rs index 3f55f5a..e1537e8 100644 --- a/programs/traveldings/src/traewelling.rs +++ b/programs/traveldings/src/traewelling.rs @@ -3,7 +3,7 @@ use std::{fmt, fs}; use model::{Container, Status}; use reqwest::{ header::{self, HeaderMap}, - Client, ClientBuilder, + Client, ClientBuilder, StatusCode, }; const KEY_PATH: &str = "/home/jade/Docs/traveldings-key"; @@ -19,7 +19,6 @@ impl TraewellingClient { let mut headers = HeaderMap::new(); let token = fs::read_to_string(KEY_PATH)?; let key = header::HeaderValue::from_str(&format!("Bearer {token}"))?; - println!("meow"); headers.insert("Authorization", key); headers.insert( header::ACCEPT, @@ -33,16 +32,17 @@ impl TraewellingClient { }) } - pub async fn get_active_checkin(&self) -> anyhow::Result { - let txt = self + pub async fn get_active_checkin(&self) -> Result { + let res = self .client .get(Self::fmt_url("user/statuses/active")) .send() - .await? - .text() .await?; + if res.status() != StatusCode::OK { + return Err(RequestErr::WithStatus(res.status())); + } - println!("{txt}"); + let txt = res.text().await?; let res: Container = serde_json::de::from_str(&txt)?; Ok(res.data) @@ -53,4 +53,35 @@ impl TraewellingClient { } } +#[derive(thiserror::Error, Debug, PartialEq, Eq)] +pub enum RequestErr { + #[error("Couldn't deserialize the json :(")] + DeserializationError, + #[error("an error related to connect happened!!")] + RelatedToConnect, + #[error("error haz status: {0}")] + WithStatus(StatusCode), + #[error("fuck if i know what went wrong :333 am silly ")] + Other, +} + +impl From for RequestErr { + fn from(value: serde_json::Error) -> Self { + eprintln!("serde error: {value:?}"); + Self::DeserializationError + } +} + +impl From for RequestErr { + fn from(value: reqwest::Error) -> Self { + if let Some(status) = value.status() { + Self::WithStatus(status) + } else if value.is_connect() { + Self::RelatedToConnect + } else { + Self::Other + } + } +} + pub mod model; diff --git a/programs/traveldings/src/traewelling/model.rs b/programs/traveldings/src/traewelling/model.rs index b40554a..4b54c7b 100644 --- a/programs/traveldings/src/traewelling/model.rs +++ b/programs/traveldings/src/traewelling/model.rs @@ -1,4 +1,4 @@ -use chrono::{DateTime, FixedOffset}; +use chrono::{DateTime, FixedOffset, Timelike}; use serde::Deserialize; #[derive(Deserialize, Debug)] @@ -9,39 +9,77 @@ pub struct Container { #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct Status { - train: TransportResource, + pub train: TransportResource, } #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct TransportResource { - category: String, - line_name: String, - distance: u32, - duration: u32, - operator: OperatorResource, - origin: StopOverResource, - destination: StopOverResource, + pub category: String, + pub line_name: String, + pub distance: u32, + pub duration: u32, + pub operator: Option, + pub origin: StopOverResource, + pub destination: StopOverResource, } #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct StopOverResource { - name: String, - ril_identifier: Option, - arrival: Option>, - arrival_planned: Option>, - arrival_real: Option>, - departure: Option>, - departure_planned: Option>, - departure_real: Option>, - platform: Option, - departure_platform_planned: Option, - departure_platform_real: Option, + pub name: String, + pub ril_identifier: Option, + pub arrival_planned: Option>, + pub arrival_real: Option>, + pub departure_planned: Option>, + pub departure_real: Option>, + pub platform: Option, + pub departure_platform_planned: Option, + pub departure_platform_real: Option, +} + +// ???? +pub struct JsonableData { + pub time_err: bool, + pub time_planned: Option, + pub time_real: Option, + pub station: String, + pub ril100: Option, + pub platform_data_available: bool, + pub platform_planned: Option, + pub platform_real: Option, +} + +// What the meaning of the stop in the journey is +pub enum StopJourneyPart { + Origin, + Destination, +} +impl StopOverResource { + pub fn get_time_data(&self, journey_part: StopJourneyPart) -> JsonableData { + let (time_planned, time_real) = match journey_part { + StopJourneyPart::Origin => (self.departure_planned, self.departure_real), + StopJourneyPart::Destination => (self.arrival_planned, self.arrival_real), + }; + + let time_err = time_planned == None; + + JsonableData { + time_err, + time_planned: time_planned.map(|ts| ts.timestamp()), + time_real: time_real.map(|ts| ts.timestamp()), + station: self.name.clone(), + ril100: self.ril_identifier.clone(), + platform_data_available: self.departure_platform_planned.is_none() + || self.departure_platform_real.is_none(), + platform_planned: self.departure_platform_planned.clone(), + platform_real: self.departure_platform_real.clone(), + } + } } #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct OperatorResource { - name: String, + pub name: String, } From 54a1e34fa602a3538e7b3cc051163e214e82c0cf Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Tue, 10 Sep 2024 22:07:27 +0200 Subject: [PATCH 2/3] refactor bottomBar smol bit --- .../eww/configDir/bottomBar/bottomBar.yuck | 20 ++----------------- .../eww/configDir/bottomBar/workspaces.yuck | 17 ++++++++++++++++ 2 files changed, 19 insertions(+), 18 deletions(-) create mode 100644 modules/desktop-environment/home/panels/eww/configDir/bottomBar/workspaces.yuck diff --git a/modules/desktop-environment/home/panels/eww/configDir/bottomBar/bottomBar.yuck b/modules/desktop-environment/home/panels/eww/configDir/bottomBar/bottomBar.yuck index 6c31965..f3bbb94 100644 --- a/modules/desktop-environment/home/panels/eww/configDir/bottomBar/bottomBar.yuck +++ b/modules/desktop-environment/home/panels/eww/configDir/bottomBar/bottomBar.yuck @@ -1,3 +1,5 @@ +(include "bottomBar/workspaces.yuck") + (defwindow bottomBar :monitor 0 :stacking "fg" @@ -39,21 +41,3 @@ ) ) ) - -(defwidget workspaceWidget [] - (box - :class "workspaces" - (for workspace in workspaces - (button - :class "${workspace.urgent ? "urgent" : ""} ${workspace.focused ? "focused" : 0}" - :onclick "swaymsg workspace ${workspace.name}" - (label :text "${workspace.name}") - ) - ) - ) -) - -(deflisten workspaces - :initial "[]" - "bar-ws-monitor" -) diff --git a/modules/desktop-environment/home/panels/eww/configDir/bottomBar/workspaces.yuck b/modules/desktop-environment/home/panels/eww/configDir/bottomBar/workspaces.yuck new file mode 100644 index 0000000..4c5495f --- /dev/null +++ b/modules/desktop-environment/home/panels/eww/configDir/bottomBar/workspaces.yuck @@ -0,0 +1,17 @@ +(defwidget workspaceWidget [] + (box + :class "workspaces" + (for workspace in workspaces + (button + :class "${workspace.urgent ? "urgent" : ""} ${workspace.focused ? "focused" : 0}" + :onclick "swaymsg workspace ${workspace.name}" + (label :text "${workspace.name}") + ) + ) + ) +) + +(deflisten workspaces + :initial "[]" + "bar-ws-monitor" +) From b4dfef1d6de0245029ad725ac6a5af7fa5fb75f3 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Wed, 11 Sep 2024 02:09:08 +0200 Subject: [PATCH 3/3] traveldings stuff! --- .../eww/configDir/bottomBar/bottomBar.yuck | 3 +- .../eww/configDir/bottomBar/traveldings.yuck | 50 +++++++++++++++++++ .../home/panels/eww/configDir/eww.css | 17 +++++++ .../src/commands/current_journey.rs | 2 + programs/traveldings/src/traewelling/model.rs | 25 +++++++--- 5 files changed, 89 insertions(+), 8 deletions(-) create mode 100644 modules/desktop-environment/home/panels/eww/configDir/bottomBar/traveldings.yuck diff --git a/modules/desktop-environment/home/panels/eww/configDir/bottomBar/bottomBar.yuck b/modules/desktop-environment/home/panels/eww/configDir/bottomBar/bottomBar.yuck index f3bbb94..2034740 100644 --- a/modules/desktop-environment/home/panels/eww/configDir/bottomBar/bottomBar.yuck +++ b/modules/desktop-environment/home/panels/eww/configDir/bottomBar/bottomBar.yuck @@ -1,4 +1,5 @@ (include "bottomBar/workspaces.yuck") +(include "bottomBar/traveldings.yuck") (defwindow bottomBar :monitor 0 @@ -21,7 +22,7 @@ ) (box :halign "center" - (label :text "mid") + (traveldings) ) (box :halign "end" diff --git a/modules/desktop-environment/home/panels/eww/configDir/bottomBar/traveldings.yuck b/modules/desktop-environment/home/panels/eww/configDir/bottomBar/traveldings.yuck new file mode 100644 index 0000000..0e13b28 --- /dev/null +++ b/modules/desktop-environment/home/panels/eww/configDir/bottomBar/traveldings.yuck @@ -0,0 +1,50 @@ +(defwidget traveldings [] + (revealer + :class "traveldings" + :transition "crossfade" + :reveal { traveldings_data != "null" } + (traveldingsBarWidget) + ) +) + +(defwidget traveldingsBarWidget [] + (overlay + (box + :width 640 + (label + :halign "start" + :text "${traveldings_data.line} -> ${strlength(traveldings_data.arrival_station) > 24 ? "${substring(traveldings_data.arrival_station, 0, 24)}…" : traveldings_data.arrival_station}${traveldings_data.arrival_platform_data_available ? " (Gl. ${traveldings_data.arrival_platform_real})" : ""}" + ) + (label + :halign "end" + :text { traveldings_data.time_left >= 3600 ? formattime(traveldings_data.time_left, "noch %-Hh %-Mmin", "Etc/UTC") : formattime(traveldings_data.time_left, "noch %-Mmin", "Etc/UTC") } + ) + ) + (box + (progress + :value {traveldings_data.progress * 100} + :orientation "horizontal" + ) + ) + ) +) + +(defwindow traveldingsWindow + :monitor 0 + :stacking "overlay" + :exclusive "false" + :geometry (geometry + :x "0%" + :y "6.6%" + :width "30%" + :height "20%" + :anchor "bottom center") + (box + :class "traveldingsWindow" + (label :text "TODO")) +) + +(deflisten traveldings_data + :initial "null" + "traveldings current" +) diff --git a/modules/desktop-environment/home/panels/eww/configDir/eww.css b/modules/desktop-environment/home/panels/eww/configDir/eww.css index 848f77a..f512917 100644 --- a/modules/desktop-environment/home/panels/eww/configDir/eww.css +++ b/modules/desktop-environment/home/panels/eww/configDir/eww.css @@ -13,4 +13,21 @@ label { .workspaces button.focused { background-color: #504935; +} + +.traveldings progressbar trough { + border: none; + background-color: #3c3836; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.traveldings progressbar progress { + background-color: #79740e; + border-bottom-left-radius: 0; +} + + +.traveldingsWindow { + border-radius: 15px; } \ No newline at end of file diff --git a/programs/traveldings/src/commands/current_journey.rs b/programs/traveldings/src/commands/current_journey.rs index c7a8792..b0864bf 100644 --- a/programs/traveldings/src/commands/current_journey.rs +++ b/programs/traveldings/src/commands/current_journey.rs @@ -68,6 +68,7 @@ struct CurrentJourneyOutput { progress: Option, time_left: Option, icon: String, + line: String, // Invalid data received? departure_err: bool, @@ -148,6 +149,7 @@ impl CurrentJourneyOutput { progress, time_left, icon, + line: checkin.train.line_name.clone(), departure_err, departure_planned, departure_real, diff --git a/programs/traveldings/src/traewelling/model.rs b/programs/traveldings/src/traewelling/model.rs index 4b54c7b..090d36f 100644 --- a/programs/traveldings/src/traewelling/model.rs +++ b/programs/traveldings/src/traewelling/model.rs @@ -36,6 +36,8 @@ pub struct StopOverResource { pub platform: Option, pub departure_platform_planned: Option, pub departure_platform_real: Option, + pub arrival_platform_planned: Option, + pub arrival_platform_real: Option, } // ???? @@ -57,9 +59,19 @@ pub enum StopJourneyPart { } impl StopOverResource { pub fn get_time_data(&self, journey_part: StopJourneyPart) -> JsonableData { - let (time_planned, time_real) = match journey_part { - StopJourneyPart::Origin => (self.departure_planned, self.departure_real), - StopJourneyPart::Destination => (self.arrival_planned, self.arrival_real), + let (time_planned, time_real, platform_planned, platform_real) = match journey_part { + StopJourneyPart::Origin => ( + self.departure_planned, + self.departure_real, + self.departure_platform_planned.clone(), + self.departure_platform_real.clone(), + ), + StopJourneyPart::Destination => ( + self.arrival_planned, + self.arrival_real, + self.arrival_platform_planned.clone(), + self.arrival_platform_real.clone(), + ), }; let time_err = time_planned == None; @@ -70,10 +82,9 @@ impl StopOverResource { time_real: time_real.map(|ts| ts.timestamp()), station: self.name.clone(), ril100: self.ril_identifier.clone(), - platform_data_available: self.departure_platform_planned.is_none() - || self.departure_platform_real.is_none(), - platform_planned: self.departure_platform_planned.clone(), - platform_real: self.departure_platform_real.clone(), + platform_data_available: platform_planned.is_some() || platform_real.is_some(), + platform_planned, + platform_real, } } }