From cf88935b5245daea51d2b513709b61a0e43483d6 Mon Sep 17 00:00:00 2001 From: stuebinm Date: Tue, 8 Feb 2022 16:55:51 +0100 Subject: S-Bahns are weird (added a simple zugportal.de api client, but can't do auto-checkin yet) --- src/lib.rs | 1 + src/main.rs | 26 ++++++++++++++- src/zugportal.rs | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 src/zugportal.rs diff --git a/src/lib.rs b/src/lib.rs index 4172b8a..4107efe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,3 +2,4 @@ pub mod iceportal; pub(crate) mod serde; pub mod travelynx; pub mod types; +pub mod zugportal; diff --git a/src/main.rs b/src/main.rs index 1782953..7e1c85a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -41,7 +41,8 @@ enum Command { /// Undo the last checkin (if any). Undo, /// Query iceportal.de (for testing) - ICEPortal + ICEPortal, + Zugportal } #[derive(Deserialize)] @@ -213,6 +214,29 @@ fn main() -> Result<(), ureq::Error> { } } } + Command::Zugportal => { + match get_request::( + // "https://iceportal.de/api1/rs/tripInfo/trip" + "https://zugportal.de/prd/zupo-travel-information/api/public/ri/journey" + ) { + Ok(resp) => { + println!( + "{}: Currently in {}\n", + traveltext, + resp.get_train_ref().to_string().green() + ); + // println!("guessing last stop was: {:?}\n", resp.guess_last_station()); + println!("Stops:\n{}", resp.trip()) + } + Err(err) => { + if cli.debug { + eprintln!("{:?}", err); + } + println!("either this tool or the zugportal broke or you're not actually on an ICE\n\ + (get a response but couldn't parse it)"); + } + } + } } Ok(()) } diff --git a/src/zugportal.rs b/src/zugportal.rs new file mode 100644 index 0000000..f00cd04 --- /dev/null +++ b/src/zugportal.rs @@ -0,0 +1,97 @@ +/// implementation of traits to query zugportal.de +/// (available at least in the Munich S-Bahn, maybe other trains) + +use chrono::{DateTime, Utc}; +use serde::Deserialize; +use serde_json::Value; + +use crate::{serde::*, travelynx::TrainRef, types::IsStation}; + +#[derive(Deserialize, Debug)] +#[serde(rename_all="camelCase")] +pub struct Journey { + name: String, // the line's name, e.g. S 8 + no: i64, + stops: Vec +} + + +#[derive(Deserialize, Debug)] +#[serde(rename_all="camelCase")] +pub struct Stop { + station: Station, + status: String, // one of "Normal", ...? + track: Track, + messages: Vec, + arrival_time: Option, + departure_time: Option +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all="camelCase")] +struct Station { + eva_no: String, + name: String +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all="camelCase")] +struct Track { + target: String, + prediction: String +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all="camelCase")] +struct DepartureTime { + target: DateTime, + predicted: DateTime, + time_type: String, // one of REAL, PREVIEW, ..? + diff: i64, // diff in minutes? + // NOTE: also sends predictedTimeInMs and targetTimeInMs; these might be unix times +} + +impl IsStation for Stop { + fn name(&self) -> &str { + &self.station.name + } + + fn scheduled_arrival(&self) -> Option<&chrono::DateTime> { + self.arrival_time.as_ref().map(|t| &t.target) + } + + fn real_arrival(&self) -> Option<&chrono::DateTime> { + self.arrival_time.as_ref().map(|t| &t.predicted) + } + + fn ds100(&self) -> &str { + "??" + } +} + +impl Journey { + pub fn guess_last_station(&self) -> Option { + todo!() + // let current_pos = self.trip.actual_position; + // self + // .trip + // .stops + // .iter() + // .rev() + // .map(|stop| (stop.info.distance_from_start, stop)) + // .filter(|(dist, _)| dist <= ¤t_pos) + // .next() + // .map(|(_, stop)| stop.station.name.clone()) + } + + pub fn get_train_ref(&self) -> TrainRef { + TrainRef { + _type: self.name.clone(), + no: self.no.to_string().clone() + } + } + + pub fn trip(&self) -> crate::types::Trip<'_, Stop> { + crate::types::Trip(&self.stops) + } +} -- cgit v1.2.3