193 lines
5.9 KiB
Rust
193 lines
5.9 KiB
Rust
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<String> = 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::<i32>() {
|
|
Ok(v) => v,
|
|
Err(_) => return true
|
|
};
|
|
voltage = raw_value as f32 / 1000.0;
|
|
true
|
|
});
|
|
return voltage;
|
|
}
|