Compare commits

..

No commits in common. "ollo-dev" and "master" have entirely different histories.

9 changed files with 90 additions and 665 deletions

1
.gitignore vendored
View File

@ -4,4 +4,3 @@
.vscode/launch.json .vscode/launch.json
.vscode/ipch .vscode/ipch
todo.txt todo.txt
*.ini

View File

@ -25,17 +25,3 @@ stored in folder **/client**
go to **/client** go to **/client**
* cargo build * cargo build
* cargo run * cargo run
### Deamon
Requires ''systemd''
Install by creating a link to this project
```
/etc/systemd/system# ln -s /home/c3ma/led-board/client/ledBoard.service ledBoard.service
systemctl daemon-reload
systemctl enable ledBoard.service
```
Start deamon with
```
systemctl start ledBoard.service
```

View File

@ -1,51 +0,0 @@
# MQTT Configuration
This project can publish weather and public transport data to an MQTT broker.
To enable MQTT, follow these steps:
## 1. Install dependencies
Ensure you have Rust and Cargo installed. The MQTT support uses the Paho MQTT client crate.
Run:
```bash
cargo update
```
## 2. Set the MQTT_BROKER environment variable
Before running the client, define `MQTT_BROKER` to your broker address.
- Without URI scheme (defaults to TCP):
```bash
export MQTT_BROKER=localhost:1883
```
- With URI scheme:
```bash
export MQTT_BROKER=tcp://broker.example.com:1883
```
## 3. Run the LED board client
Pass the LED board IP address as the only argument:
```bash
export MQTT_BROKER=localhost:1883
cargo run --bin ledboard_client -- 192.168.1.50
```
## Topics and Payloads
The client publishes two topics:
### weather
JSON payload with fields:
- `dt`: timestamp (Unix seconds)
- `temp`: temperature in °C
- `weather`: object with `main`, `description`, `icon`
- `rain`: rain volume in last 3h (optional)
- `pop`: probability of precipitation
- `wind`: object with `speed`, `deg`, `gust`
### straba
JSON payload with fields:
- `outbound_station`: name of outbound station
- `outbound_diff`: seconds until outbound departure
- `inbound_station`: name of inbound station
- `inbound_diff`: seconds until inbound departure
## Customization
You can adjust MQTT topics, QoS, and message formats in `client/bin/src/main.rs` under the `publish_to_mqtt` function.

View File

@ -22,9 +22,3 @@ serde_derive = "1.0"
serde_json = "1.0" serde_json = "1.0"
# end of web stuff # end of web stuff
ping = "0.4.1" ping = "0.4.1"
paho-mqtt = "0.13.2"
async-trait = "0.1"
# Ini File parser
rust-ini = "0.21"
lazy_static = "1.4"
futures = "0.3"

View File

@ -1,78 +0,0 @@
// config.rs
/// @file
/// @brief Configuration settings for the application.
///
/// This file defines the configuration settings that are used throughout the application.
use lazy_static::lazy_static;
use std::sync::Mutex;
use ini::Ini;
use std::path::Path;
const DEFAULT_REFRESH_INTERVAL: u32 = 50;
// Define a struct to hold the INI configuration
#[derive(Debug)]
pub struct Config {
pub mqttPrefix: String,
pub mqttIPAddress: String,
pub panelIPAddress: String,
pub refreshInterval: u32
}
impl Config {
// Constructor for Config
pub fn new(mqtt_prefix: &str, mqtt_ip_address: &str, panel_ip_address: &str, interval: u32) -> Self {
Config {
mqttPrefix: mqtt_prefix.to_string(),
mqttIPAddress: mqtt_ip_address.to_string(),
panelIPAddress: panel_ip_address.to_string(),
refreshInterval: interval
}
}
pub fn newDefault() -> Self {
Config {
mqttPrefix: "".to_string(),
mqttIPAddress: "".to_string(),
panelIPAddress: "".to_string(),
refreshInterval: DEFAULT_REFRESH_INTERVAL
}
}
}
// Function to read the INI file
pub fn read_ini_file(filename: String) -> Config {
let mut config = Config::newDefault();
let i = Ini::load_from_file(filename).unwrap();
for (sec, prop) in i.iter() {
for (k, v) in prop.iter()
{
println!("{:?} {}:{}", sec, k, v);
if (sec.is_some()) && (sec.unwrap() == "mqtt")
{
if k == "path"
{
config.mqttPrefix = v.trim().to_string();
}
else if k == "server"
{
config.mqttIPAddress = v.trim().to_string();
}
}
else if (sec.is_some()) && (sec.unwrap() == "panel")
{
if k == "ip"
{
config.panelIPAddress = v.trim().to_string();
}
}
}
}
return config;
}

View File

@ -1,13 +1,10 @@
use std::{time::Duration, fmt::format}; use std::{time::Duration, fmt::format};
use std::sync::{RwLock, Arc};
use async_trait::async_trait;
use paho_mqtt::{CreateOptionsBuilder, ConnectOptionsBuilder, Client ,Message as MqttMessage};
use str; use str;
use bit::BitIndex; use bit::BitIndex;
use chrono_tz::Europe::Berlin; use chrono_tz::Europe::Berlin;
use chrono::{DateTime, NaiveDateTime, Utc, Timelike}; use chrono::{DateTime, NaiveDateTime, Utc, Timelike};
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use openweathermap::{forecast::Weather, Receiver}; use openweathermap::forecast::Weather;
use substring::Substring; use substring::Substring;
use tinybmp::Bmp; use tinybmp::Bmp;
use core::time; use core::time;
@ -21,23 +18,15 @@ use embedded_graphics::{
use std::net::UdpSocket; use std::net::UdpSocket;
use std::{env, thread}; use std::{env, thread};
use std::path::Path;
use std::io; use std::io;
use std::process::ExitCode; use std::process::ExitCode;
use openweathermap::forecast::Forecast; use openweathermap::forecast::Forecast;
use straba::NextDeparture; use straba::NextDeparture;
// This declaration will look for a file named `straba.rs` and will // This declaration will look for a file named `straba.rs` and will
// insert its contents inside a module named `straba` under this scope // insert its contents inside a module named `straba` under this scope
mod straba; mod straba;
use std::sync::Mutex;
use lazy_static::lazy_static;
use futures::executor::block_on;
// Load INI-File handling module
mod config;
use config::Config;
use crate::config::read_ini_file;
const IMAGE_SIZE_BYTE: usize = (IMAGE_WIDTH_BYTE * IMAGE_HEIGHT) as usize; /* one byte contains 8 LEDs, one in each bit */ const IMAGE_SIZE_BYTE: usize = (IMAGE_WIDTH_BYTE * IMAGE_HEIGHT) as usize; /* one byte contains 8 LEDs, one in each bit */
const IMAGE_WIDTH: u32 = 5 * 32; const IMAGE_WIDTH: u32 = 5 * 32;
const IMAGE_WIDTH_BYTE: u32 = IMAGE_WIDTH / 8; /* one byte contains 8 LEDs, one in each bit */ const IMAGE_WIDTH_BYTE: u32 = IMAGE_WIDTH / 8; /* one byte contains 8 LEDs, one in each bit */
@ -47,37 +36,6 @@ const IMAGE_HEIGHT_BYTE: u32 = 40;
const IMAGE_LENGTH: usize = (IMAGE_WIDTH_BYTE * IMAGE_HEIGHT_BYTE) as usize; const IMAGE_LENGTH: usize = (IMAGE_WIDTH_BYTE * IMAGE_HEIGHT_BYTE) as usize;
const PACKAGE_LENGTH: usize = (IMAGE_LENGTH + 1) as usize; const PACKAGE_LENGTH: usize = (IMAGE_LENGTH + 1) as usize;
/// @struct MqttClient
/// @brief A struct to hold application configuration settings.
///
/// The `MqttClient` struct contains various configuration parameters that control the behavior of the application.
pub struct MqttClient {
/// MQTT client option
pub mqtt_client: Option<paho_mqtt::Client>,
}
impl MqttClient {
/// Creates a new instance of MqttClient with mqtt_client initialized to None.
pub fn new() -> Self {
MqttClient {
mqtt_client: None,
}
}
/// Setter for mqtt_client
pub fn set_mqtt_client(&mut self, client: Option<paho_mqtt::Client>) {
self.mqtt_client = client;
}
/// Getter for mqtt_client
pub fn get_mqtt_client(&self) -> &Option<paho_mqtt::Client> {
&self.mqtt_client
}
}
lazy_static! {
static ref MQTTCLIENT: Mutex<MqttClient> = Mutex::new(MqttClient::new());
}
struct UdpDisplay { struct UdpDisplay {
image: [u8; IMAGE_SIZE_BYTE], image: [u8; IMAGE_SIZE_BYTE],
@ -285,13 +243,6 @@ fn render_clock(display: &mut UdpDisplay){
.unwrap(); .unwrap();
} }
fn render_mqtt_message(display: &mut UdpDisplay, mqtt_message: String){
let text_style = MonoTextStyle::new(&FONT_6X10, BinaryColor::On);
Text::new(&mqtt_message, Point::new((1) as i32, 37), text_style)
.draw(display)
.unwrap();
}
fn render_strab_partial(display: &mut UdpDisplay, station: &String, diff: i64, height: i32) { fn render_strab_partial(display: &mut UdpDisplay, station: &String, diff: i64, height: i32) {
let text_style = MonoTextStyle::new(&FONT_6X10, BinaryColor::On); let text_style = MonoTextStyle::new(&FONT_6X10, BinaryColor::On);
let mut diff_str = format!("{}min", (diff / 60)); let mut diff_str = format!("{}min", (diff / 60));
@ -325,15 +276,13 @@ fn render_strab_partial(display: &mut UdpDisplay, station: &String, diff: i64, h
} }
fn render_strab(display: &mut UdpDisplay, straba_res: &NextDeparture) { fn render_strab(display: &mut UdpDisplay, straba_res: &NextDeparture) {
render_strab_partial(display, &straba_res.outbound_station, straba_res.outbound_diff, 17); render_strab_partial(display, &straba_res.outbound_station, straba_res.outbound_diff, 15);
render_strab_partial(display, &straba_res.inbound_station, straba_res.inbound_diff, 27); render_strab_partial(display, &straba_res.inbound_station, straba_res.inbound_diff, 25);
} }
/////////////////////////////////////////////////////////////////////////////
fn send_package(ipaddress: String, fn send_package(ipaddress: String,
data: &Option<Result<Forecast, String>>, data: &Option<Result<Forecast, String>>,
straba_res: &NextDeparture, straba_res: &NextDeparture) {
mqtt_message: Option<String>) {
let mut package: [u8; PACKAGE_LENGTH] = [0; PACKAGE_LENGTH]; let mut package: [u8; PACKAGE_LENGTH] = [0; PACKAGE_LENGTH];
// Brightness // Brightness
@ -351,17 +300,14 @@ fn send_package(ipaddress: String,
render_strab(&mut display, straba_res); render_strab(&mut display, straba_res);
} }
if mqtt_message.is_some() {
render_mqtt_message(&mut display, mqtt_message.unwrap());
}
render_clock(&mut display); render_clock(&mut display);
package[1..PACKAGE_LENGTH].copy_from_slice(&display.image); package[1..PACKAGE_LENGTH].copy_from_slice(&display.image);
let target = format!("{}:4242", ipaddress); // client need to bind to client port (1 before 4242)
let socket = UdpSocket::bind("0.0.0.0:0").expect("couldn't bind to address"); let socket = UdpSocket::bind("0.0.0.0:14242").expect("couldn't bind to address");
socket socket
.send_to(&package, &target) .send_to(&package, ipaddress + ":4242")
.expect("couldn't send data"); .expect("couldn't send data");
} }
@ -372,307 +318,56 @@ LEDboardClient <ip address>"
); );
println!("one argument necessary!"); println!("one argument necessary!");
println!("<ip address>"); println!("<ip address>");
println!("second argument is optional:");
println!("<ip of mqtt server>");
println!("");
println!("Config mode");
println!("--config <file.ini>");
} }
fn check_connection(ipaddress: String) -> bool { fn check_connection(ipaddress: String) -> bool {
let device_online; let device_online;
// generate a faulty package length
let mut package: [u8; PACKAGE_LENGTH/2] = [0; PACKAGE_LENGTH/2]; let mut package: [u8; PACKAGE_LENGTH/2] = [0; PACKAGE_LENGTH/2];
// client need to bind to client port (1 before 4242)
let socket = UdpSocket::bind("0.0.0.0:14242").expect("couldn't bind to address");
socket.set_read_timeout(Some(Duration::from_secs(10))).unwrap(); /* 10 seconds timeout */
socket
.send_to(&package, ipaddress + ":4242")
.expect("couldn't send data");
// Use a random local port instead of hardcoding // self.recv_buff is a [u8; 8092]
let socket = UdpSocket::bind("0.0.0.0:0").expect("couldn't bind to address"); let answer = socket.recv_from(&mut package);
socket.set_read_timeout(Some(Duration::from_secs(10))).unwrap();
let target = format!("{}:4242", ipaddress);
match socket.send_to(&package, &target) {
Ok(_) => {
// Continue with receive
match socket.recv_from(&mut package) {
Ok((_n, _addr)) => device_online = true,
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => device_online = false,
Err(_) => device_online = false,
}
},
Err(_) => device_online = false,
}
device_online
}
/// Publishes weather and transit data to MQTT broker
fn publish_to_mqtt(client: &Client, data: &Option<Result<Forecast, String>>, straba_res: &NextDeparture) {
let payload = if let Some(Ok(forecast)) = data {
if let Some(f) = forecast.list.first() {
let temp = f.main.temp;
let weather = f.weather.get(0).map(|w| w.main.clone()).unwrap_or_default();
format!("temp:{:.1}C,weather:{}",
temp,
weather)
} else {
"no_forecast".to_string()
}
} else {
"no_data".to_string()
};
let msg = MqttMessage::new("ledboard/forecast", payload, 1);
if let Err(e) = client.publish(msg) {
eprintln!("Error publishing MQTT message: {}", e);
}
let payloadPT = {
format!("out:{}min,in:{}min",
straba_res.outbound_diff / 60,
straba_res.inbound_diff / 60)
};
let ptmsg = MqttMessage::new("ledboard/public_transportation", payloadPT, 1);
if let Err(e) = client.publish(ptmsg) {
eprintln!("Error publishing MQTT message: {}", e);
}
}
struct Message {
string: Option<String>
}
// Lazy static to load the config file content
lazy_static! {
pub static ref GlobalConfiguration: Mutex<Config> = Mutex::new({
Config::newDefault()
});
}
/// Asynchronously publishes a message to the specified topic.
///
/// @author Gwen2.5
///
/// # Arguments
/// * `topic` - The MQTT topic to which the message will be published.
/// * `message` - The message to be published.
///
/// # Returns
/// True if the message was successfully published, false otherwise.
fn publish_message(topic: &str, message: &str) {
let msg = MqttMessage::new(topic, message, 0);
let mqtt_client = <std::option::Option<Client> as Clone>::clone(&(MQTTCLIENT.lock().unwrap().get_mqtt_client())).unwrap();
// Publish the message and ensure it completes without error
let result = mqtt_client.publish(msg);
match result {
Ok(_) => (),
Err(error) => {
println!("Error publishing {error:?}");
}
}
}
fn main_function(parameter1: String, parameter2: Option<String>) -> ExitCode {
// Read configuration file
if (parameter1 == "--config") && (parameter2.is_some())
{
let configOrMqttAddress: String = parameter2.unwrap();
if Path::new(&configOrMqttAddress).exists()
{
let mut gc = GlobalConfiguration.lock().unwrap();
let c = read_ini_file(configOrMqttAddress);
//update configuration
gc.mqttIPAddress = c.mqttIPAddress;
gc.panelIPAddress = c.panelIPAddress;
gc.mqttPrefix = c.mqttPrefix;
println!("Read INI {:} @ {:}", gc.mqttPrefix, gc.mqttIPAddress);
}
else
{
/* Panel and MQTT Configured*/
println!("INI file not found");
return ExitCode::FAILURE;
}
}
else
{
let mut gc = GlobalConfiguration.lock().unwrap();
gc.panelIPAddress = parameter1;
gc.mqttIPAddress = parameter2.unwrap();
}
let mut device_online = false;
if GlobalConfiguration.lock().unwrap().panelIPAddress.len() > 0
{
device_online = check_connection(GlobalConfiguration.lock().unwrap().panelIPAddress.clone());
if !device_online {
println!("{:} not online", &GlobalConfiguration.lock().unwrap().panelIPAddress);
return ExitCode::FAILURE;
}
}
let mut gmc = MQTTCLIENT.lock().unwrap();
let mqtt_message: Arc<Mutex<Message>> = Arc::new(Mutex::new(Message{ string: None }));
if GlobalConfiguration.lock().is_ok() && (GlobalConfiguration.lock().unwrap().mqttIPAddress.len() > 0)
{
// Define the set of options for the create.
// Use an ID for a persistent session.
let create_opts = paho_mqtt::CreateOptionsBuilder::new()
.server_uri(GlobalConfiguration.lock().unwrap().mqttIPAddress.clone())
.client_id("ledboard")
.finalize();
// Create a client.
let local_mqtt = paho_mqtt::Client::new(create_opts).unwrap();
println!("MQTT | Connecting to {:} MQTT server...", GlobalConfiguration.lock().unwrap().mqttIPAddress);
let lwt = paho_mqtt::Message::new(&format!("{}/lwt", GlobalConfiguration.lock().unwrap().mqttPrefix), "lost connection", 1);
// The connect options. Defaults to an MQTT v3.x connection.
let conn_opts = paho_mqtt::ConnectOptionsBuilder::new()
.keep_alive_interval(Duration::from_secs(20))
.will_message(lwt)
.finalize();
// Make the connection to the broker
println!("MQTT | Connecting to the MQTT server...");
let result = local_mqtt.connect(conn_opts);
match result {
Ok(_) => {
println!("MQTT | Server connected");
},
Err(error) => {
println!("MQTT | Server connecting {error:?}");
},
}
// Subscribe to the desired topic(s).
local_mqtt.subscribe("room/ledboard", paho_mqtt::QOS_0);
// Starts the client receiving messages
let rx_queue = local_mqtt.start_consuming();
// Attach a closure to the client to receive callback
// on incoming messages.
let mqtt_message_for_callback = mqtt_message.clone();
// Create a thread that stays pending over incoming messages.
let handle = thread::spawn(move || {
for mqttmsg in rx_queue.iter() {
if let Some(mqttmsg) = mqttmsg {
let topic = mqttmsg.topic();
let payload_str = mqttmsg.payload_str();
//println!("MQTT | {} - {}", topic, payload_str);
let mut lock = mqtt_message_for_callback.lock().unwrap();
lock.string = Some(payload_str.to_string());
println!("Received: -> {}", mqttmsg.payload_str());
} else {
println!("Unsubscribe: connection closed");
break;
}
}
});
// Define the set of options for the connection
// move local instance to global scope
gmc.set_mqtt_client(Some(local_mqtt));
}
let receiver = openweathermap::init_forecast("Mannheim",
"metric",
"de",
"978882ab9dd05e7122ff2b0aef2d3e55",
60,1);
let mut last_data = Option::None;
// Test Webcrawler for public transportataion
let mut straba_res = straba::fetch_data(Some(true));
println!("{:?} {:?}s", straba_res.outbound_station, straba_res.outbound_diff);
println!("{:?} {:?}s", straba_res.inbound_station , straba_res.inbound_diff);
if GlobalConfiguration.lock().is_ok() && (GlobalConfiguration.lock().unwrap().panelIPAddress.len() > 0)
{
// Render start
send_package(GlobalConfiguration.lock().unwrap().panelIPAddress.clone(), &last_data, &straba_res, Some("MQTT: room/ledboard".to_string()));
}
loop {
let st_now = SystemTime::now();
let seconds = st_now.duration_since(UNIX_EPOCH).unwrap().as_secs();
let delay = time::Duration::from_millis(500);
thread::sleep(delay);
// Only request, if the device is present
if device_online == true {
let answer = openweathermap::update_forecast(&receiver);
match answer { match answer {
Some(_) => { Ok((_n, _addr)) => {
last_data = answer; //println!("{} bytes response from {:?} {:?}", n, addr, &package[..n]);
device_online = true;
} }
None => { Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
device_online = false;
}
Err(_e) => {
device_online = false;
} }
} }
return device_online;
} }
if (straba_res.request_time + (GlobalConfiguration.lock().unwrap().refreshInterval as i64)) < seconds as i64 { fn main() -> ExitCode {
if GlobalConfiguration.lock().is_ok() && GlobalConfiguration.lock().unwrap().mqttIPAddress.len() > 0 let args: Vec<String> = env::args().collect();
{ match args.len() {
device_online = check_connection(GlobalConfiguration.lock().unwrap().mqttIPAddress.clone()); // no arguments passed
1 => {
// show a help message
help();
return ExitCode::SUCCESS;
} }
// one argument passed
if GlobalConfiguration.lock().is_ok() && GlobalConfiguration.lock().unwrap().mqttPrefix.len() > 0 2 => {
{ let ip = &args[1];
//FIXME if mqtt_client.is_some()
fun_publishinfoviamqtt(&straba_res);
}
// request once a minute new data
straba_res = straba::fetch_data(None);
println!("Update {:?} {:?}s", straba_res.outbound_station, straba_res.outbound_diff);
println!("Update {:?} {:?}s", straba_res.inbound_station , straba_res.inbound_diff);
}
let lock = mqtt_message.lock().unwrap();
let mqtt_message: Option<String>;
if lock.string.is_some() {
mqtt_message = lock.string.clone();
} else {
mqtt_message = None;
}
if (GlobalConfiguration.lock().is_ok()) && (GlobalConfiguration.lock().unwrap().mqttIPAddress.len() > 0) && (device_online == true) {
// Render new image
send_package(GlobalConfiguration.lock().unwrap().mqttIPAddress.clone(), &last_data, &straba_res, mqtt_message);
}
// Handle MQTT messages
}
}
fn fun_publishinfoviamqtt(straba_res: &NextDeparture) {
let topic_in_station: String = format!("{}{}", GlobalConfiguration.lock().unwrap().mqttPrefix, "/inbound/station");
let station_name: String = format!("{}", straba_res.inbound_station);
// Execute async publish synchronously
let _ = publish_message(topic_in_station.as_str(), station_name.as_str());
println!("MQTT published {:?} = {:?}s", topic_in_station, straba_res.outbound_station);
}
fn main_function2(ip: String) -> ExitCode
{
let mut device_online = check_connection(ip.to_string()); let mut device_online = check_connection(ip.to_string());
if !device_online { if !device_online {
println!("{} not online", ip); println!("{} not online", ip);
// return ExitCode::FAILURE; return ExitCode::FAILURE;
} }
let receiver = openweathermap::init_forecast("Mannheim", let receiver = openweathermap::init_forecast("Mannheim",
"metric", "metric",
"de", "de",
@ -686,47 +381,8 @@ fn main_function2(ip: String) -> ExitCode
println!("{:?} {:?}s", straba_res.outbound_station, straba_res.outbound_diff); println!("{:?} {:?}s", straba_res.outbound_station, straba_res.outbound_diff);
println!("{:?} {:?}s", straba_res.inbound_station , straba_res.inbound_diff); println!("{:?} {:?}s", straba_res.inbound_station , straba_res.inbound_diff);
// Initialize MQTT client from MQTT_BROKER env var (else disabled)
let mqtt_client: Option<Client> = {
// Read broker URL from environment
let broker = match std::env::var("MQTT_BROKER") {
Ok(val) if !val.is_empty() => val,
_ => {
eprintln!("Environment variable MQTT_BROKER not set or empty, MQTT disabled");
String::new()
}
};
if broker.is_empty() {
None
} else {
let create_opts = CreateOptionsBuilder::new()
.server_uri(&broker)
.client_id("ledboard_client")
.finalize();
match Client::new(create_opts) {
Ok(cli) => {
let conn_opts = ConnectOptionsBuilder::new()
.keep_alive_interval(Duration::from_secs(20))
.clean_session(true)
.finalize();
match cli.connect(conn_opts) {
Ok(_) => Some(cli),
Err(e) => {
eprintln!("Failed to connect to MQTT broker '{}': {}", broker, e);
None
}
}
}
Err(e) => {
eprintln!("Failed to create MQTT client for '{}': {}", broker, e);
None
}
}
}
};
// Render start // Render start
send_package(ip.to_string(), &last_data, &straba_res, None); send_package(ip.to_string(), &last_data, &straba_res);
loop { loop {
let st_now = SystemTime::now(); let st_now = SystemTime::now();
let seconds = st_now.duration_since(UNIX_EPOCH).unwrap().as_secs(); let seconds = st_now.duration_since(UNIX_EPOCH).unwrap().as_secs();
@ -757,38 +413,9 @@ fn main_function2(ip: String) -> ExitCode
if device_online == true { if device_online == true {
// Render new image // Render new image
send_package(ip.to_string(), &last_data, &straba_res, None); send_package(ip.to_string(), &last_data, &straba_res);
// Publish data to MQTT
}
if let Some(ref client) = mqtt_client {
publish_to_mqtt(client, &last_data, &straba_res);
}
} }
} }
fn main() -> ExitCode {
let args: Vec<String> = env::args().collect();
match args.len() {
// no arguments passed
1 => {
// show a help message
help();
return ExitCode::SUCCESS;
}
// one argument passed
2 => {
let ip = &args[1];
// Only one parameter uses the logic, generated in o4mini-llm
return main_function2(ip.to_string());
}
// two argument passed
3 => {
let ip = &args[1];
let mqtt = &args[2];
return main_function( ip.to_string(),
Some(mqtt.to_string())
);
} }
// all the other cases // all the other cases
_ => { _ => {

View File

@ -1,6 +1,6 @@
use chrono::DateTime; use chrono::DateTime;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use chrono::Local;
use serde::Deserialize; use serde::Deserialize;
const STATION_URL:&str = "https://www.rnv-online.de/rest/departure/2494"; const STATION_URL:&str = "https://www.rnv-online.de/rest/departure/2494";
@ -79,11 +79,10 @@ pub struct NextDeparture {
} }
pub fn fetch_data(debug_print : Option<bool>) -> NextDeparture { pub fn fetch_data(debug_print : Option<bool>) -> NextDeparture {
let date = Local::now();
let st_now = SystemTime::now(); let st_now = SystemTime::now();
let seconds = st_now.duration_since(UNIX_EPOCH).unwrap().as_secs(); let seconds = st_now.duration_since(UNIX_EPOCH).unwrap().as_secs();
let timeString = date.format("%Y-%m-%d %H:%M:%S"); let url = &format!("{}?datetime={}", STATION_URL, seconds);
let url = &format!("{}?datetime={}", STATION_URL, timeString);
let result = reqwest::blocking::get(url); let result = reqwest::blocking::get(url);
let mut return_value = NextDeparture { let mut return_value = NextDeparture {
@ -119,24 +118,10 @@ pub fn fetch_data(debug_print : Option<bool>) -> NextDeparture {
return return_value; return return_value;
} }
if debug_print.is_some() && debug_print.unwrap() == true {
println!("----------- Seconds {:} {:} requesting ... -----------", seconds, timeString);
}
// parse JSON result.. search of both directions // parse JSON result.. search of both directions
let json = body.unwrap(); let json = body.unwrap();
if debug_print.is_some() && debug_print.unwrap() == true {
println!("Requesting {:}", json.graph_ql.response.name);
println!("Elements {:}", json.graph_ql.response.journeys.elements.len() );
//println!("------------------------- %< ----------------------------");
//println!("{}", &raw_text);
//println!("------------------------- %< ----------------------------");
}
for el in json.graph_ql.response.journeys.elements { for el in json.graph_ql.response.journeys.elements {
if debug_print.is_some() && debug_print.unwrap() == true { if debug_print.is_some() && debug_print.unwrap() == true {
println!("Requesting {:}", json.graph_ql.response.name);
println!("Line {:}", el.line.line_group.label); println!("Line {:}", el.line.line_group.label);
} }
for stop in el.stops { for stop in el.stops {
@ -155,8 +140,6 @@ pub fn fetch_data(debug_print : Option<bool>) -> NextDeparture {
if diff < return_value.outbound_diff { if diff < return_value.outbound_diff {
return_value.outbound_station = stop.destination_label; return_value.outbound_station = stop.destination_label;
return_value.outbound_diff = diff; return_value.outbound_diff = diff;
} else if debug_print.is_some() && debug_print.unwrap() == true {
println!("Unkown diff Stop {:} {:} (in {:} seconds)", stop.destination_label, txt_departure, diff );
} }
} else if stop.destination_label.contains("Hochschule") || } else if stop.destination_label.contains("Hochschule") ||
stop.destination_label.contains("Hauptbahnhof") || stop.destination_label.contains("Hauptbahnhof") ||
@ -164,19 +147,13 @@ pub fn fetch_data(debug_print : Option<bool>) -> NextDeparture {
if diff < return_value.inbound_diff { if diff < return_value.inbound_diff {
return_value.inbound_station = stop.destination_label; return_value.inbound_station = stop.destination_label;
return_value.inbound_diff = diff; return_value.inbound_diff = diff;
} else if debug_print.is_some() && debug_print.unwrap() == true {
println!("Unkown diff Stop {:} {:} (in {:} seconds)", stop.destination_label, txt_departure, diff );
} }
} else if debug_print.is_some() && debug_print.unwrap() == true {
println!("Unkown Stop {:} {:} (in {:} seconds)", stop.destination_label, txt_departure, diff );
} }
} else { } else {
println!("Planned {:} {:?}", stop.destination_label, stop.planned_departure.iso_string) println!("Planned {:} {:?}", stop.destination_label, stop.planned_departure.iso_string)
} }
} }
} }
if debug_print.is_some() && debug_print.unwrap() == true {
println!("----------- end of straba.rs -----------");
}
return_value return_value
} }

View File

@ -1,17 +0,0 @@
[Unit]
Description=Log uptime in scoreboard
DefaultDependencies=no
[Service]
Type=simple
Restart=on-failure
User=c3ma
# Specify users home as working directory
WorkingDirectory=/home/c3ma/
# Define wrapper to update and start project
ExecStart=/usr/bin/bash <project home>/client/ledboard.sh
TimeoutStartSec=0
[Install]
WantedBy=network.target

View File

@ -1,12 +0,0 @@
#!/bin/bash
# Wrapper script to update project and build project
#
#Set target IP address
IP=
# Path to this project
HOSTCLIENT=
cd $HOSTCLIENT
/usr/bin/pkill LEDboardClient
git pull
cargo build
$HOSTCLIENT/target/debug/LEDboardClient $IP