use rppal::gpio::{Gpio, OutputPin}; use serde::{Deserialize}; use serde_json; use sqlite; use std::env; use std::fs::File; use std::path::Path; use std::thread; use std::time::Duration; use chrono; // Gpio uses BCM pin numbering. BCM GPIO 23 is tied to physical pin 16. const GPIO_SWITCH: u8 = 24; // set duration of pin switching test #[derive(Deserialize)] struct Config{ database: String, device_name_voltage: String, mode: String, test_duration_seconds: u64, time_on: String, time_off: String, time_use: bool, voltage_off: f32, voltage_recover: f32, } #[derive(Deserialize)] #[allow(non_snake_case)] struct DbData{ V: String, } //fn main() { fn main () { let home_dir = env::home_dir().expect("Could not determine home directory"); let mut config_file_path: &str = &(home_dir.to_string_lossy() + "/.isolated-switches/config.json"); // read and parse command line parameters let args: Vec = env::args().collect(); for n in 1..args.len() { match args[n].as_str() { "on" => { pin_switch_on(); return; }, "off" => { pin_switch_off(); return; }, "status" => { println!("Inverter status: {}" , if pin_status() == 0 { "off" } else { "on" }); return; }, "test" => { pin_test(config_file_path); return; }, "config" => { if args.len() > n + 1 { config_file_path = args[n + 1].as_str(); } }, _ => () } } // read and parse configuration file let config: Config = read_config(&config_file_path); match config.mode.as_str() { "auto" => { println!("Running in automatic mode"); let pin_status_cur = pin_status(); let voltage = get_current_voltage(&config); // turn inverter off based on time of day settings if config.time_use { let time_off = chrono::NaiveTime::parse_from_str(&config.time_off, "%H:%M").unwrap(); let time_on = chrono::NaiveTime::parse_from_str(&config.time_on, "%H:%M").unwrap(); let time_now = chrono::Local::now().time(); if time_off > time_on { // switch off at the same day if time_now < time_on || time_now > time_off { if pin_status() > 0 { println!("Off-time!"); pin_switch_off(); } return; } } else if time_off < time_on { // switch off the next day if time_now < time_on && time_now > time_off { if pin_status() > 0 { println!("Off-time!"); pin_switch_off(); } return; } } } if voltage < config.voltage_off && pin_status_cur > 0 { println!("Low voltage!"); pin_switch_off(); } else if voltage >= config.voltage_recover && pin_status_cur == 0 { println!("Voltage recovered!"); pin_switch_on(); } else { // no change in status needed let pin_status_text = if pin_status_cur == 0 { "off" } else { "on" }; println!("No change in inverter status. Current status: {}", pin_status_text); } }, "on" => { println!("Running in manual mode ON"); if pin_status() == 0 { pin_switch_on(); } return; }, "off" => { println!("Running in manual mode OFF"); if pin_status() > 0 { pin_switch_off(); } return; }, _ => () } //end program with OK status } // read and parse configuration file fn read_config(config_file_path: &str) -> Config { let config_file = File::open(config_file_path) .expect(&("Could not read config file ".to_owned() + config_file_path )); let config: Config = serde_json::from_reader(config_file) .expect("file should be proper JSON"); config } fn pin_status () -> u8 { let pin_level: u8 = Gpio::new().unwrap().get(GPIO_SWITCH).unwrap().read() as u8; return pin_level; } fn pin_switch_on () { let mut pin: OutputPin = Gpio::new().unwrap().get(GPIO_SWITCH).unwrap().into_output(); pin.set_reset_on_drop(false); pin.set_high(); println!("Inverter switched on."); } fn pin_switch_off () { let mut pin: OutputPin = Gpio::new().unwrap().get(GPIO_SWITCH).unwrap().into_output(); pin.set_reset_on_drop(false); pin.set_low(); println!("Inverter switched off."); } fn pin_test (config_path: &str) { let config: Config = read_config(&config_path); pin_switch_on(); thread::sleep(Duration::from_millis(config.test_duration_seconds * 1000)); pin_switch_off(); } fn get_current_voltage (config: &Config) -> f32 { let mut voltage: f32 = 0.0; if !Path::new(&config.database).exists() { return voltage; } let db_connection = sqlite::open(&config.database).unwrap(); let query = format!("SELECT data FROM '{device}' ORDER BY date DESC, time DESC LIMIT 1;", device = config.device_name_voltage); let _ = db_connection.iterate(query, |pairs | { let data = pairs.get(0).unwrap().1.unwrap(); let test_parse: DbData = serde_json::from_str(data).expect("no proper JSON data found on database"); let raw_value = match test_parse.V.parse::() { Ok(v) => v, Err(_) => return true }; voltage = raw_value as f32 / 1000.0; true }); return voltage; }