webserver improvements
This commit is contained in:
parent
1e40e2e3ba
commit
4c02b99ea7
@ -75,6 +75,7 @@ anyhow = { version = "1.0.75", features = ["std", "backtrace"] }
|
|||||||
schemars = "0.8.16"
|
schemars = "0.8.16"
|
||||||
heapless = { version = "0.7", features = ["serde"] }
|
heapless = { version = "0.7", features = ["serde"] }
|
||||||
serde_json = "1.0.108"
|
serde_json = "1.0.108"
|
||||||
|
strum = { version = "0.25.0", features = ["derive"] }
|
||||||
#?bq34z100 required
|
#?bq34z100 required
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
nvs, data, nvs, , 16k,
|
nvs, data, nvs, , 16k,
|
||||||
otadata, data, ota, , 8k,
|
otadata, data, ota, , 8k,
|
||||||
phy_init, data, phy, , 4k,
|
phy_init, data, phy, , 4k,
|
||||||
factory, app, ota_0, , 1792K,
|
ota_0, app, ota_0, , 1792K,
|
||||||
ota_1, app, ota_1, , 1792K,
|
ota_1, app, ota_1, , 1792K,
|
||||||
storage, data, spiffs, , 400K,
|
storage, data, spiffs, , 400K,
|
|
@ -6,28 +6,47 @@ use crate::PLANT_COUNT;
|
|||||||
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
tank_sensor_enabled: bool,
|
tank_sensor_enabled: bool,
|
||||||
tank_full_ml: u32,
|
tank_full_ml: u32,
|
||||||
tank_warn_percent: u8,
|
tank_warn_percent: u8,
|
||||||
|
|
||||||
plantcount: u16,
|
|
||||||
|
|
||||||
night_lamp_hour_start: u8,
|
night_lamp_hour_start: u8,
|
||||||
night_lamp_hour_end: u8,
|
night_lamp_hour_end: u8,
|
||||||
night_lamp_only_when_dark: u8,
|
night_lamp_only_when_dark: bool,
|
||||||
|
|
||||||
plants: [Plant;PLANT_COUNT]
|
plants: [Plant;PLANT_COUNT]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { tank_sensor_enabled: true,
|
||||||
|
tank_full_ml: 5000,
|
||||||
|
tank_warn_percent: 50,
|
||||||
|
night_lamp_hour_start: 21,
|
||||||
|
night_lamp_hour_end: 2,
|
||||||
|
night_lamp_only_when_dark: true,
|
||||||
|
plants: [Plant::default();PLANT_COUNT]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Copy, Clone)]
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Plant{
|
pub struct Plant{
|
||||||
target_moisture: u8,
|
target_moisture: u8,
|
||||||
pump_time_s: u16,
|
pump_time_s: u16,
|
||||||
pump_cooldown_min: u16,
|
pump_cooldown_min: u16,
|
||||||
pump_hour_start: heapless::String<5>,
|
pump_hour_start: u8,
|
||||||
pump_hour_end: heapless::String<5>
|
pump_hour_end: u8
|
||||||
}
|
}
|
||||||
|
impl Default for Plant {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { target_moisture: 40, pump_time_s: 60, pump_cooldown_min: 60, pump_hour_start: 8, pump_hour_end: 20 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct WifiConfig {
|
pub struct WifiConfig {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::{sync::{Arc, Mutex}, env};
|
use std::{sync::{Arc, Mutex, atomic::AtomicBool}, env};
|
||||||
|
|
||||||
use chrono::{Datelike, NaiveDateTime, Timelike};
|
use chrono::{Datelike, NaiveDateTime, Timelike};
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ use esp_idf_sys::{esp_restart, vTaskDelay};
|
|||||||
use plant_hal::{CreatePlantHal, PlantCtrlBoard, PlantCtrlBoardInteraction, PlantHal, PLANT_COUNT};
|
use plant_hal::{CreatePlantHal, PlantCtrlBoard, PlantCtrlBoardInteraction, PlantHal, PLANT_COUNT};
|
||||||
|
|
||||||
|
|
||||||
use crate::{config::{Config, WifiConfig}, webserver::webserver::httpd_initial};
|
use crate::{config::{Config, WifiConfig}, webserver::webserver::{httpd_initial, httpd}};
|
||||||
mod config;
|
mod config;
|
||||||
pub mod plant_hal;
|
pub mod plant_hal;
|
||||||
mod webserver {
|
mod webserver {
|
||||||
@ -27,32 +27,47 @@ enum OnlineMode {
|
|||||||
|
|
||||||
enum WaitType{
|
enum WaitType{
|
||||||
InitialConfig,
|
InitialConfig,
|
||||||
FlashError
|
FlashError,
|
||||||
|
NormalConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait_infinity(board_access: Arc<Mutex<PlantCtrlBoard<'_>>>, wait_type:WaitType) -> !{
|
fn wait_infinity(wait_type:WaitType, reboot_now:Arc<AtomicBool>) -> !{
|
||||||
let delay = match wait_type {
|
let delay = match wait_type {
|
||||||
WaitType::InitialConfig => 250_u32,
|
WaitType::InitialConfig => 250_u32,
|
||||||
WaitType::FlashError => 100_u32,
|
WaitType::FlashError => 100_u32,
|
||||||
|
WaitType::NormalConfig => 500_u32
|
||||||
};
|
};
|
||||||
board_access.lock().unwrap().light(true).unwrap();
|
let led_count = match wait_type {
|
||||||
|
WaitType::InitialConfig => 8,
|
||||||
|
WaitType::FlashError => 8,
|
||||||
|
WaitType::NormalConfig => 4
|
||||||
|
};
|
||||||
|
BOARD_ACCESS.lock().unwrap().light(true).unwrap();
|
||||||
loop {
|
loop {
|
||||||
unsafe {
|
unsafe {
|
||||||
//do not trigger watchdog
|
//do not trigger watchdog
|
||||||
for i in 0..8 {
|
for i in 0..8 {
|
||||||
board_access.lock().unwrap().fault(i, true);
|
BOARD_ACCESS.lock().unwrap().fault(i, i <led_count);
|
||||||
}
|
}
|
||||||
board_access.lock().unwrap().general_fault(true);
|
BOARD_ACCESS.lock().unwrap().general_fault(true);
|
||||||
vTaskDelay(delay);
|
vTaskDelay(delay);
|
||||||
board_access.lock().unwrap().general_fault(false);
|
BOARD_ACCESS.lock().unwrap().general_fault(false);
|
||||||
for i in 0..8 {
|
for i in 0..8 {
|
||||||
board_access.lock().unwrap().fault(i, false);
|
BOARD_ACCESS.lock().unwrap().fault(i, false);
|
||||||
}
|
}
|
||||||
vTaskDelay(delay);
|
vTaskDelay(delay);
|
||||||
|
if reboot_now.load(std::sync::atomic::Ordering::Relaxed) {
|
||||||
|
println!("Rebooting");
|
||||||
|
esp_restart();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static BOARD_ACCESS: Lazy<PlantCtrlBoard> = Lazy::new(|| {
|
||||||
|
PlantHal::create()?;
|
||||||
|
});
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
// It is necessary to call this function once. Otherwise some patches to the runtime
|
// It is necessary to call this function once. Otherwise some patches to the runtime
|
||||||
// implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71
|
// implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71
|
||||||
@ -67,8 +82,7 @@ fn main() -> Result<()> {
|
|||||||
println!("Version useing git has {}", git_hash);
|
println!("Version useing git has {}", git_hash);
|
||||||
|
|
||||||
println!("Board hal init");
|
println!("Board hal init");
|
||||||
let board_access = PlantHal::create()?;
|
let mut board = BOARD_ACCESS.lock().unwrap();
|
||||||
let mut board = board_access.lock().unwrap();
|
|
||||||
println!("Mounting filesystem");
|
println!("Mounting filesystem");
|
||||||
board.mountFileSystem()?;
|
board.mountFileSystem()?;
|
||||||
let free_space = board.fileSystemSize()?;
|
let free_space = board.fileSystemSize()?;
|
||||||
@ -103,16 +117,14 @@ fn main() -> Result<()> {
|
|||||||
if board.is_config_reset() {
|
if board.is_config_reset() {
|
||||||
println!("Reset config is still pressed, deleting configs and reboot");
|
println!("Reset config is still pressed, deleting configs and reboot");
|
||||||
match board.remove_configs() {
|
match board.remove_configs() {
|
||||||
Ok(_) => {
|
Ok(case) => {
|
||||||
println!("Removed config files, restarting");
|
println!("Succeeded in deleting config {}", case);
|
||||||
unsafe {
|
|
||||||
esp_restart();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
println!("Could not remove config files, system borked {}", err);
|
println!("Could not remove config files, system borked {}", err);
|
||||||
//terminate main app and freeze
|
//terminate main app and freeze
|
||||||
wait_infinity( board_access.clone(), WaitType::FlashError);
|
|
||||||
|
wait_infinity(WaitType::FlashError, Arc::new(AtomicBool::new(false)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -130,14 +142,13 @@ fn main() -> Result<()> {
|
|||||||
board.wifi_ap().unwrap();
|
board.wifi_ap().unwrap();
|
||||||
//config upload will trigger reboot!
|
//config upload will trigger reboot!
|
||||||
drop(board);
|
drop(board);
|
||||||
let _webserver = httpd_initial(board_access.clone());
|
let reboot_now = Arc::new(AtomicBool::new(false));
|
||||||
wait_infinity(board_access.clone(), WaitType::InitialConfig);
|
let _webserver = httpd_initial(BOARD_ACCESS.clone(), reboot_now.clone());
|
||||||
|
wait_infinity(WaitType::InitialConfig, reboot_now.clone());
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// let proceed = config.unwrap();
|
|
||||||
|
|
||||||
//check if we have a config file
|
//check if we have a config file
|
||||||
// if not found or parsing error -> error very fast blink general fault
|
// if not found or parsing error -> error very fast blink general fault
|
||||||
//if this happens after a firmeware upgrade (check image state), mark as invalid
|
//if this happens after a firmeware upgrade (check image state), mark as invalid
|
||||||
@ -203,6 +214,21 @@ fn main() -> Result<()> {
|
|||||||
println!("Running logic at europe/berlin {}", europe_time);
|
println!("Running logic at europe/berlin {}", europe_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let config:Config;
|
||||||
|
match (board.get_config()){
|
||||||
|
Ok(valid) => {
|
||||||
|
config = valid;
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
println!("Missing normal config, entering config mode {}", err);
|
||||||
|
//config upload will trigger reboot!
|
||||||
|
drop(board);
|
||||||
|
let reboot_now = Arc::new(AtomicBool::new(false));
|
||||||
|
let _webserver = httpd(BOARD_ACCESS.clone(),reboot_now.clone());
|
||||||
|
wait_infinity(BOARD_ACCESS.clone(), WaitType::NormalConfig, reboot_now.clone());
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
if online_mode == OnlineMode::SnTp {
|
if online_mode == OnlineMode::SnTp {
|
||||||
//mqtt here
|
//mqtt here
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ use plant_ctrl2::sipo::ShiftRegister40;
|
|||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use anyhow::{bail, Ok, Result};
|
use anyhow::{bail, Ok, Result};
|
||||||
|
use strum::EnumString;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
@ -23,7 +24,6 @@ use chrono::{DateTime, NaiveDateTime, Utc};
|
|||||||
use ds18b20::Ds18b20;
|
use ds18b20::Ds18b20;
|
||||||
|
|
||||||
use embedded_hal::digital::v2::OutputPin;
|
use embedded_hal::digital::v2::OutputPin;
|
||||||
use esp_idf_hal::adc::config::Config;
|
|
||||||
use esp_idf_hal::adc::{attenuation, AdcChannelDriver, AdcDriver};
|
use esp_idf_hal::adc::{attenuation, AdcChannelDriver, AdcDriver};
|
||||||
use esp_idf_hal::delay::Delay;
|
use esp_idf_hal::delay::Delay;
|
||||||
use esp_idf_hal::gpio::{AnyInputPin, Gpio39, Gpio4, Level, PinDriver};
|
use esp_idf_hal::gpio::{AnyInputPin, Gpio39, Gpio4, Level, PinDriver};
|
||||||
@ -37,7 +37,7 @@ use esp_idf_svc::systime::EspSystemTime;
|
|||||||
use esp_idf_sys::{EspError, vTaskDelay};
|
use esp_idf_sys::{EspError, vTaskDelay};
|
||||||
use one_wire_bus::OneWire;
|
use one_wire_bus::OneWire;
|
||||||
|
|
||||||
use crate::config::{self, WifiConfig};
|
use crate::config::{self, WifiConfig, Config};
|
||||||
|
|
||||||
pub const PLANT_COUNT: usize = 8;
|
pub const PLANT_COUNT: usize = 8;
|
||||||
const PINS_PER_PLANT: usize = 5;
|
const PINS_PER_PLANT: usize = 5;
|
||||||
@ -80,6 +80,14 @@ pub struct FileSystemSizeInfo {
|
|||||||
pub free_size: usize,
|
pub free_size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(strum::Display)]
|
||||||
|
pub enum ClearConfigType {
|
||||||
|
WifiConfig,
|
||||||
|
Config,
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Sensor {
|
pub enum Sensor {
|
||||||
A,
|
A,
|
||||||
@ -121,8 +129,9 @@ pub trait PlantCtrlBoardInteraction {
|
|||||||
|
|
||||||
//config
|
//config
|
||||||
fn is_config_reset(&mut self) -> bool;
|
fn is_config_reset(&mut self) -> bool;
|
||||||
fn remove_configs(&mut self) -> Result<()>;
|
fn remove_configs(&mut self) -> Result<ClearConfigType>;
|
||||||
fn get_config(&mut self) -> Result<config::Config>;
|
fn get_config(&mut self) -> Result<config::Config>;
|
||||||
|
fn set_config(&mut self, wifi: &Config) -> Result<()>;
|
||||||
fn get_wifi(&mut self) -> Result<config::WifiConfig>;
|
fn get_wifi(&mut self) -> Result<config::WifiConfig>;
|
||||||
fn set_wifi(&mut self, wifi: &WifiConfig) -> Result<()>;
|
fn set_wifi(&mut self, wifi: &WifiConfig) -> Result<()>;
|
||||||
fn wifi_ap(&mut self) -> Result<()>;
|
fn wifi_ap(&mut self) -> Result<()>;
|
||||||
@ -137,7 +146,7 @@ pub trait CreatePlantHal<'a> {
|
|||||||
pub struct PlantHal {}
|
pub struct PlantHal {}
|
||||||
|
|
||||||
impl CreatePlantHal<'_> for PlantHal {
|
impl CreatePlantHal<'_> for PlantHal {
|
||||||
fn create() -> Result<Arc<Mutex<PlantCtrlBoard<'static>>>> {
|
fn create() -> Result<Mutex<PlantCtrlBoard<'static>>> {
|
||||||
let peripherals = Peripherals::take()?;
|
let peripherals = Peripherals::take()?;
|
||||||
|
|
||||||
let mut clock = PinDriver::output(peripherals.pins.gpio21)?;
|
let mut clock = PinDriver::output(peripherals.pins.gpio21)?;
|
||||||
@ -213,7 +222,7 @@ impl CreatePlantHal<'_> for PlantHal {
|
|||||||
let last_watering_timestamp = Mutex::new(unsafe { LAST_WATERING_TIMESTAMP });
|
let last_watering_timestamp = Mutex::new(unsafe { LAST_WATERING_TIMESTAMP });
|
||||||
let consecutive_watering_plant = Mutex::new(unsafe { CONSECUTIVE_WATERING_PLANT });
|
let consecutive_watering_plant = Mutex::new(unsafe { CONSECUTIVE_WATERING_PLANT });
|
||||||
let low_voltage_detected = Mutex::new(unsafe { LOW_VOLTAGE_DETECTED });
|
let low_voltage_detected = Mutex::new(unsafe { LOW_VOLTAGE_DETECTED });
|
||||||
let tank_driver = AdcDriver::new(peripherals.adc1, &Config::new())?;
|
let tank_driver = AdcDriver::new(peripherals.adc1, &esp_idf_hal::adc::config::Config::new())?;
|
||||||
let tank_channel: AdcChannelDriver<'_, { attenuation::DB_11 }, Gpio39> =
|
let tank_channel: AdcChannelDriver<'_, { attenuation::DB_11 }, Gpio39> =
|
||||||
AdcChannelDriver::new(peripherals.pins.gpio39)?;
|
AdcChannelDriver::new(peripherals.pins.gpio39)?;
|
||||||
let solar_is_day = PinDriver::input(peripherals.pins.gpio25)?;
|
let solar_is_day = PinDriver::input(peripherals.pins.gpio25)?;
|
||||||
@ -228,7 +237,7 @@ impl CreatePlantHal<'_> for PlantHal {
|
|||||||
println!("After stuff");
|
println!("After stuff");
|
||||||
|
|
||||||
|
|
||||||
let rv = Arc::new(Mutex::new(PlantCtrlBoard {
|
let rv = Mutex::new(PlantCtrlBoard {
|
||||||
shift_register : shift_register,
|
shift_register : shift_register,
|
||||||
last_watering_timestamp: last_watering_timestamp,
|
last_watering_timestamp: last_watering_timestamp,
|
||||||
consecutive_watering_plant: consecutive_watering_plant,
|
consecutive_watering_plant: consecutive_watering_plant,
|
||||||
@ -244,7 +253,7 @@ impl CreatePlantHal<'_> for PlantHal {
|
|||||||
one_wire_bus: one_wire_bus,
|
one_wire_bus: one_wire_bus,
|
||||||
signal_counter: counter_unit1,
|
signal_counter: counter_unit1,
|
||||||
wifi_driver: wifi_driver,
|
wifi_driver: wifi_driver,
|
||||||
}));
|
});
|
||||||
return Ok(rv);
|
return Ok(rv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -534,19 +543,26 @@ impl PlantCtrlBoardInteraction for PlantCtrlBoard<'_> {
|
|||||||
return self.boot_button.get_level() == Level::Low;
|
return self.boot_button.get_level() == Level::Low;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_configs(&mut self) -> Result<()> {
|
|
||||||
let wifi_config = Path::new(WIFI_CONFIG_FILE);
|
|
||||||
if wifi_config.exists() {
|
|
||||||
println!("Removing wifi config");
|
|
||||||
std::fs::remove_file(wifi_config)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
fn remove_configs(&mut self) -> Result<ClearConfigType> {
|
||||||
let config = Path::new(CONFIG_FILE);
|
let config = Path::new(CONFIG_FILE);
|
||||||
if config.exists() {
|
if config.exists() {
|
||||||
println!("Removing config");
|
println!("Removing config");
|
||||||
std::fs::remove_file(config)?;
|
std::fs::remove_file(config)?;
|
||||||
|
return Ok(ClearConfigType::Config);
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
|
let wifi_config = Path::new(WIFI_CONFIG_FILE);
|
||||||
|
if wifi_config.exists() {
|
||||||
|
println!("Removing wifi config");
|
||||||
|
std::fs::remove_file(wifi_config)?;
|
||||||
|
Ok(ClearConfigType::WifiConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((ClearConfigType::None));
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_wifi(&mut self) -> Result<config::WifiConfig> {
|
fn get_wifi(&mut self) -> Result<config::WifiConfig> {
|
||||||
@ -563,12 +579,16 @@ impl PlantCtrlBoardInteraction for PlantCtrlBoard<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_config(&mut self) -> Result<config::Config> {
|
fn get_config(&mut self) -> Result<config::Config> {
|
||||||
let mut cfg = File::open(CONFIG_FILE)?;
|
let cfg = File::open(CONFIG_FILE)?;
|
||||||
let mut data: [u8; 512] = [0; 512];
|
let config: Config = serde_json::from_reader(cfg)?;
|
||||||
let read = cfg.read(&mut data)?;
|
return Ok(config);
|
||||||
println!("Read file {}", from_utf8(&data[0..read])?);
|
}
|
||||||
|
|
||||||
bail!("todo")
|
fn set_config(&mut self, config: &Config) -> Result<()> {
|
||||||
|
let mut cfg = File::create(CONFIG_FILE)?;
|
||||||
|
serde_json::to_writer(&mut cfg, &config)?;
|
||||||
|
println!("Wrote config config {:?}", config);
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wifi_scan(&mut self) -> Result<Vec<AccessPointInfo>> {
|
fn wifi_scan(&mut self) -> Result<Vec<AccessPointInfo>> {
|
||||||
|
@ -32,9 +32,11 @@
|
|||||||
<h3>Light:</h3>
|
<h3>Light:</h3>
|
||||||
<div>
|
<div>
|
||||||
Start
|
Start
|
||||||
<input type="time" id="night_lamp_time_start">
|
<select type="time" id="night_lamp_time_start">
|
||||||
|
</select>
|
||||||
Stop
|
Stop
|
||||||
<input type="time" id="night_lamp_time_end">
|
<select type="time" id="night_lamp_time_end">
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input type="checkbox" id="night_lamp_only_when_dark">
|
<input type="checkbox" id="night_lamp_only_when_dark">
|
||||||
|
BIN
rust/src/webserver/favicon.ico
Normal file
BIN
rust/src/webserver/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
@ -23,7 +23,7 @@
|
|||||||
<option value="Not scanned yet">
|
<option value="Not scanned yet">
|
||||||
</datalist>
|
</datalist>
|
||||||
<label for="ssid">Password:</label>
|
<label for="ssid">Password:</label>
|
||||||
<input type="text" id="password" list="ssidlist">
|
<input type="text" id="password">
|
||||||
<input type="button" id="save" value="Save & Restart">
|
<input type="button" id="save" value="Save & Restart">
|
||||||
<div id="wifistatus"></div>
|
<div id="wifistatus"></div>
|
||||||
<br>
|
<br>
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
//offer ota and config mode
|
//offer ota and config mode
|
||||||
|
|
||||||
use std::sync::{Mutex, Arc};
|
use std::{sync::{Mutex, Arc, atomic::AtomicBool}, str::from_utf8};
|
||||||
|
|
||||||
use embedded_svc::http::Method;
|
use embedded_svc::http::{Method, Headers};
|
||||||
use esp_idf_svc::http::server::EspHttpServer;
|
use esp_idf_svc::http::server::EspHttpServer;
|
||||||
use esp_ota::OtaUpdate;
|
use esp_ota::OtaUpdate;
|
||||||
use heapless::String;
|
use heapless::String;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::{plant_hal::{PlantCtrlBoard, PlantCtrlBoardInteraction}, config::WifiConfig};
|
use crate::{plant_hal::{PlantCtrlBoard, PlantCtrlBoardInteraction, PLANT_COUNT}, config::{WifiConfig, Config, Plant}};
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -16,7 +16,7 @@ struct SSIDList<'a> {
|
|||||||
ssids: Vec<&'a String<32>>
|
ssids: Vec<&'a String<32>>
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn httpd_initial(board_access:Arc<Mutex<PlantCtrlBoard<'static>>>) -> Box<EspHttpServer<'static>> {
|
pub fn httpd_initial(board_access:Arc<Mutex<PlantCtrlBoard<'static>>>, reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
||||||
let mut server = shared();
|
let mut server = shared();
|
||||||
server.fn_handler("/",Method::Get, move |request| {
|
server.fn_handler("/",Method::Get, move |request| {
|
||||||
let mut response = request.into_ok_response()?;
|
let mut response = request.into_ok_response()?;
|
||||||
@ -37,9 +37,9 @@ pub fn httpd_initial(board_access:Arc<Mutex<PlantCtrlBoard<'static>>>) -> Box<Es
|
|||||||
scan_result.iter().for_each(|s|
|
scan_result.iter().for_each(|s|
|
||||||
ssids.push(&s.ssid)
|
ssids.push(&s.ssid)
|
||||||
);
|
);
|
||||||
response.write( serde_json::to_string(
|
let ssid_json = serde_json::to_string( &SSIDList{ssids})?;
|
||||||
&SSIDList{ssids}
|
println!("Sending ssid list {}", &ssid_json);
|
||||||
)?.as_bytes())?;
|
response.write( &ssid_json.as_bytes())?;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return Ok(())
|
return Ok(())
|
||||||
@ -48,6 +48,7 @@ pub fn httpd_initial(board_access:Arc<Mutex<PlantCtrlBoard<'static>>>) -> Box<Es
|
|||||||
|
|
||||||
let board_access_for_save = board_access.clone();
|
let board_access_for_save = board_access.clone();
|
||||||
server.fn_handler("/wifisave",Method::Post, move |mut request| {
|
server.fn_handler("/wifisave",Method::Post, move |mut request| {
|
||||||
|
|
||||||
let mut buf = [0_u8;2048];
|
let mut buf = [0_u8;2048];
|
||||||
let read = request.read(&mut buf);
|
let read = request.read(&mut buf);
|
||||||
if read.is_err(){
|
if read.is_err(){
|
||||||
@ -57,6 +58,8 @@ pub fn httpd_initial(board_access:Arc<Mutex<PlantCtrlBoard<'static>>>) -> Box<Es
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let actual_data = &buf[0..read.unwrap()];
|
let actual_data = &buf[0..read.unwrap()];
|
||||||
|
println!("raw {:?}", actual_data);
|
||||||
|
println!("Raw data {}", from_utf8(actual_data).unwrap());
|
||||||
let wifi_config: Result<WifiConfig, serde_json::Error> = serde_json::from_slice(actual_data);
|
let wifi_config: Result<WifiConfig, serde_json::Error> = serde_json::from_slice(actual_data);
|
||||||
if wifi_config.is_err(){
|
if wifi_config.is_err(){
|
||||||
let error_text = wifi_config.unwrap_err().to_string();
|
let error_text = wifi_config.unwrap_err().to_string();
|
||||||
@ -68,6 +71,7 @@ pub fn httpd_initial(board_access:Arc<Mutex<PlantCtrlBoard<'static>>>) -> Box<Es
|
|||||||
board.set_wifi(&wifi_config.unwrap())?;
|
board.set_wifi(&wifi_config.unwrap())?;
|
||||||
let mut response = request.into_status_response(202)?;
|
let mut response = request.into_status_response(202)?;
|
||||||
response.write("saved".as_bytes())?;
|
response.write("saved".as_bytes())?;
|
||||||
|
reboot_now.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
return Ok(())
|
return Ok(())
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
|
|
||||||
@ -81,7 +85,7 @@ pub fn httpd_initial(board_access:Arc<Mutex<PlantCtrlBoard<'static>>>) -> Box<Es
|
|||||||
return server
|
return server
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn httpd(board:&mut Box<PlantCtrlBoard<'static>>) -> Box<EspHttpServer<'static>> {
|
pub fn httpd(board_access:Arc<Mutex<PlantCtrlBoard<'static>>>, reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
||||||
let mut server = shared();
|
let mut server = shared();
|
||||||
|
|
||||||
server
|
server
|
||||||
@ -91,12 +95,57 @@ pub fn httpd(board:&mut Box<PlantCtrlBoard<'static>>) -> Box<EspHttpServer<'stat
|
|||||||
return Ok(())
|
return Ok(())
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
|
|
||||||
|
let board_access_for_get_config= board_access.clone();
|
||||||
|
server
|
||||||
|
.fn_handler("/get_config",Method::Get, move |request| {
|
||||||
|
let mut response = request.into_ok_response()?;
|
||||||
|
let mut board = board_access_for_get_config.lock()?;
|
||||||
|
match (board.get_config()) {
|
||||||
|
Ok(config) => {
|
||||||
|
let config_json = serde_json::to_string(&config)?;
|
||||||
|
response.write(config_json.as_bytes())?;
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
let config_json = serde_json::to_string(&Config::default())?;
|
||||||
|
response.write(config_json.as_bytes())?;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return Ok(())
|
||||||
|
}).unwrap();
|
||||||
|
|
||||||
|
let board_access_for_set_config= board_access.clone();
|
||||||
|
server.fn_handler("/set_config",Method::Post, move |mut request| {
|
||||||
|
let mut buf = [0_u8;2048];
|
||||||
|
let read = request.read(&mut buf);
|
||||||
|
if read.is_err(){
|
||||||
|
let error_text = read.unwrap_err().to_string();
|
||||||
|
println!("Could not parse wificonfig {}", error_text);
|
||||||
|
request.into_status_response(500)?.write(error_text.as_bytes())?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let actual_data = &buf[0..read.unwrap()];
|
||||||
|
println!("raw {:?}", actual_data);
|
||||||
|
println!("Raw data {}", from_utf8(actual_data).unwrap());
|
||||||
|
let config: Result<Config, serde_json::Error> = serde_json::from_slice(actual_data);
|
||||||
|
if config.is_err(){
|
||||||
|
let error_text = config.unwrap_err().to_string();
|
||||||
|
println!("Could not parse wificonfig {}", error_text);
|
||||||
|
request.into_status_response(500)?.write(error_text.as_bytes())?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let mut board = board_access_for_set_config.lock().unwrap();
|
||||||
|
board.set_config(&config.unwrap())?;
|
||||||
|
let mut response = request.into_status_response(202)?;
|
||||||
|
response.write("saved".as_bytes())?;
|
||||||
|
reboot_now.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
return Ok(())
|
||||||
|
}).unwrap();
|
||||||
return server;
|
return server;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shared() -> Box<EspHttpServer<'static>> {
|
pub fn shared() -> Box<EspHttpServer<'static>> {
|
||||||
let mut server = Box::new(EspHttpServer::new(&Default::default()).unwrap());
|
let mut server: Box<EspHttpServer<'static>> = Box::new(EspHttpServer::new(&Default::default()).unwrap());
|
||||||
|
|
||||||
server
|
server
|
||||||
.fn_handler("/version",Method::Get, |request| {
|
.fn_handler("/version",Method::Get, |request| {
|
||||||
@ -110,6 +159,12 @@ pub fn shared() -> Box<EspHttpServer<'static>> {
|
|||||||
response.write(include_bytes!("bundle.js"))?;
|
response.write(include_bytes!("bundle.js"))?;
|
||||||
return Ok(())
|
return Ok(())
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
|
server
|
||||||
|
.fn_handler("/favicon.ico",Method::Get, |request| {
|
||||||
|
let mut response = request.into_ok_response()?;
|
||||||
|
response.write(include_bytes!("favicon.ico"))?;
|
||||||
|
return Ok(())
|
||||||
|
}).unwrap();
|
||||||
server
|
server
|
||||||
.fn_handler("/ota", Method::Post, |mut request| {
|
.fn_handler("/ota", Method::Post, |mut request| {
|
||||||
let ota = OtaUpdate::begin();
|
let ota = OtaUpdate::begin();
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
interface PlantConfig {
|
interface PlantConfig {
|
||||||
tank_sensor_enabled:boolean,
|
tank_sensor_enabled: boolean,
|
||||||
tank_full_ml:number,
|
tank_full_ml: number,
|
||||||
tank_warn_percent: number,
|
tank_warn_percent: number,
|
||||||
night_lamp_time_start: string,
|
night_lamp_hour_start: number,
|
||||||
night_lamp_time_end: string,
|
night_lamp_hour_end: number,
|
||||||
night_lamp_only_when_dark: boolean,
|
night_lamp_only_when_dark: boolean,
|
||||||
plants: {
|
plants: {
|
||||||
target_moisture: number,
|
target_moisture: number,
|
||||||
pump_time_s: number,
|
pump_time_s: number,
|
||||||
pump_cooldown_min: number,
|
pump_cooldown_min: number,
|
||||||
pump_hour_start: string,
|
pump_hour_start: number,
|
||||||
pump_hour_end: string
|
pump_hour_end: number
|
||||||
}[]
|
}[]
|
||||||
}
|
}
|
||||||
|
|
||||||
let plants = document.getElementById("plants") as HTMLInputElement;
|
let plants = document.getElementById("plants") as HTMLInputElement;
|
||||||
@ -20,156 +20,192 @@ let fromWrapper = (() => {
|
|||||||
|
|
||||||
let plantcount = 0;
|
let plantcount = 0;
|
||||||
|
|
||||||
|
function addTimeOptions(select: HTMLSelectElement) {
|
||||||
|
for (let i = 0; i < 24; i++) {
|
||||||
|
let option = document.createElement("option");
|
||||||
|
option.innerText = i.toString();
|
||||||
|
select.appendChild(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let tank_full_ml = document.getElementById("tank_full_ml") as HTMLInputElement;
|
let tank_full_ml = document.getElementById("tank_full_ml") as HTMLInputElement;
|
||||||
tank_full_ml.onchange = submitForm
|
tank_full_ml.onchange = updateJson
|
||||||
let tank_warn_percent = document.getElementById("tank_warn_percent") as HTMLInputElement;
|
let tank_warn_percent = document.getElementById("tank_warn_percent") as HTMLInputElement;
|
||||||
tank_warn_percent.onchange = submitForm
|
tank_warn_percent.onchange = updateJson
|
||||||
let tank_sensor_enabled = document.getElementById("tank_sensor_enabled") as HTMLInputElement;
|
let tank_sensor_enabled = document.getElementById("tank_sensor_enabled") as HTMLInputElement;
|
||||||
tank_sensor_enabled.onchange = submitForm
|
tank_sensor_enabled.onchange = updateJson
|
||||||
let night_lamp_only_when_dark = document.getElementById("night_lamp_only_when_dark") as HTMLInputElement;
|
let night_lamp_only_when_dark = document.getElementById("night_lamp_only_when_dark") as HTMLInputElement;
|
||||||
night_lamp_only_when_dark.onchange = submitForm
|
night_lamp_only_when_dark.onchange = updateJson
|
||||||
let night_lamp_time_start = document.getElementById("night_lamp_time_start") as HTMLInputElement;
|
let night_lamp_time_start = document.getElementById("night_lamp_time_start") as HTMLSelectElement;
|
||||||
night_lamp_time_start.onchange = submitForm
|
night_lamp_time_start.onchange = updateJson
|
||||||
let night_lamp_time_end = document.getElementById("night_lamp_time_end") as HTMLInputElement;
|
addTimeOptions(night_lamp_time_start);
|
||||||
night_lamp_time_end.onchange = submitForm
|
let night_lamp_time_end = document.getElementById("night_lamp_time_end") as HTMLSelectElement;
|
||||||
|
night_lamp_time_end.onchange = updateJson
|
||||||
|
addTimeOptions(night_lamp_time_end);
|
||||||
|
|
||||||
let json = document.getElementById('json') as HTMLInputElement
|
let json = document.getElementById('json') as HTMLInputElement
|
||||||
|
|
||||||
|
function createForm(current: PlantConfig) {
|
||||||
|
for (let i = 0; i < current.plants.length; i++) {
|
||||||
|
let plant = document.createElement("div");
|
||||||
|
plants.appendChild(plant);
|
||||||
|
let header = document.createElement("h4");
|
||||||
|
header.textContent = "Plant " + (i + 1);
|
||||||
|
plant.appendChild(header);
|
||||||
|
|
||||||
function createForm(){
|
|
||||||
var current:PlantConfig = {
|
|
||||||
tank_sensor_enabled:true,
|
|
||||||
tank_full_ml:400,
|
|
||||||
tank_warn_percent:50,
|
|
||||||
night_lamp_time_start : "18:00",
|
|
||||||
night_lamp_time_end : "02:00",
|
|
||||||
night_lamp_only_when_dark: true,
|
|
||||||
plants :[
|
|
||||||
{
|
{
|
||||||
target_moisture: 40,
|
let holder = document.createElement("div");
|
||||||
pump_time_s: 60,
|
plant.appendChild(holder);
|
||||||
pump_cooldown_min: 60,
|
let inputf = document.createElement("input");
|
||||||
pump_hour_start: "10:00",
|
inputf.id = "plant_" + i + "_target_moisture";
|
||||||
pump_hour_end: "18:00"
|
inputf.onchange = updateJson;
|
||||||
|
inputf.type = "number";
|
||||||
|
inputf.min = "0";
|
||||||
|
inputf.max = "100";
|
||||||
|
holder.appendChild(inputf)
|
||||||
|
|
||||||
|
let text = document.createElement("span");
|
||||||
|
holder.appendChild(text)
|
||||||
|
text.innerHTML += "Target Moisture"
|
||||||
}
|
}
|
||||||
]
|
{
|
||||||
|
let holder = document.createElement("div");
|
||||||
|
plant.appendChild(holder);
|
||||||
|
let input = document.createElement("input");
|
||||||
|
input.id = "plant_" + i + "_pump_time_s";
|
||||||
|
input.onchange = updateJson;
|
||||||
|
input.type = "number";
|
||||||
|
input.min = "0";
|
||||||
|
input.max = "600";
|
||||||
|
holder.appendChild(input)
|
||||||
|
|
||||||
|
let text = document.createElement("span");
|
||||||
|
holder.appendChild(text)
|
||||||
|
text.innerHTML += "Pump Time (s)"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let holder = document.createElement("div");
|
||||||
|
plant.appendChild(holder);
|
||||||
|
let input = document.createElement("input");
|
||||||
|
input.id = "plant_" + i + "_pump_cooldown_min";
|
||||||
|
input.onchange = updateJson;
|
||||||
|
input.type = "number";
|
||||||
|
input.min = "0";
|
||||||
|
input.max = "600";
|
||||||
|
holder.appendChild(input)
|
||||||
|
|
||||||
|
let text = document.createElement("span");
|
||||||
|
holder.appendChild(text)
|
||||||
|
text.innerHTML += "Pump Cooldown (m)"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let holder = document.createElement("div");
|
||||||
|
plant.appendChild(holder);
|
||||||
|
let input = document.createElement("select");
|
||||||
|
input.id = "plant_" + i + "_pump_hour_start";
|
||||||
|
addTimeOptions(input);
|
||||||
|
input.onchange = updateJson;
|
||||||
|
holder.appendChild(input)
|
||||||
|
|
||||||
|
let text = document.createElement("span");
|
||||||
|
holder.appendChild(text)
|
||||||
|
text.innerHTML += "Pump Hour Start"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let holder = document.createElement("div");
|
||||||
|
plant.appendChild(holder);
|
||||||
|
let input = document.createElement("select");
|
||||||
|
input.id = "plant_" + i + "_pump_hour_end";
|
||||||
|
addTimeOptions(input);
|
||||||
|
input.onchange = updateJson;
|
||||||
|
holder.appendChild(input)
|
||||||
|
|
||||||
|
let text = document.createElement("span");
|
||||||
|
holder.appendChild(text)
|
||||||
|
text.innerHTML += "Pump Hour End"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sync(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sync(current: PlantConfig) {
|
||||||
for(let i=0;i<current.plants.length;i++){
|
plantcount = current.plants.length
|
||||||
var plant = document.createElement("div");
|
tank_full_ml.disabled = !current.tank_sensor_enabled;
|
||||||
plants.appendChild(plant);
|
tank_warn_percent.disabled = !current.tank_sensor_enabled;
|
||||||
var header = document.createElement("h4");
|
|
||||||
header.textContent = "Plant " + (i+1);
|
|
||||||
plant.appendChild(header);
|
|
||||||
|
|
||||||
{
|
tank_sensor_enabled.checked = current.tank_sensor_enabled;
|
||||||
var holder = document.createElement("div");
|
tank_full_ml.value = current.tank_full_ml.toString();
|
||||||
plant.appendChild(holder);
|
tank_warn_percent.value = current.tank_warn_percent.toString();
|
||||||
var inputf = document.createElement("input");
|
night_lamp_time_start.value = current.night_lamp_hour_start.toString();
|
||||||
inputf.id = "plant_"+i+"_target_moisture";
|
night_lamp_time_end.value = current.night_lamp_hour_end.toString();
|
||||||
inputf.onchange = submitForm;
|
|
||||||
inputf.type = "number";
|
|
||||||
inputf.min = "0";
|
|
||||||
inputf.max = "100";
|
|
||||||
holder.appendChild(inputf)
|
|
||||||
|
|
||||||
var text = document.createElement("span");
|
for (let i = 0; i < current.plants.length; i++) {
|
||||||
holder.appendChild(text)
|
let plant_target_moisture = document.getElementById("plant_" + i + "_target_moisture") as HTMLInputElement;
|
||||||
text.innerHTML += "Target Moisture"
|
plant_target_moisture.value = current.plants[i].target_moisture.toString();
|
||||||
}
|
let plant_pump_time_s = document.getElementById("plant_" + i + "_pump_time_s") as HTMLInputElement;
|
||||||
{
|
plant_pump_time_s.value = current.plants[i].pump_time_s.toString();
|
||||||
var holder = document.createElement("div");
|
let plant_pump_cooldown_min = document.getElementById("plant_" + i + "_pump_cooldown_min") as HTMLInputElement;
|
||||||
plant.appendChild(holder);
|
plant_pump_cooldown_min.value = current.plants[i].pump_cooldown_min.toString();
|
||||||
var input = document.createElement("input");
|
let plant_pump_hour_start = document.getElementById("plant_" + i + "_pump_hour_start") as HTMLInputElement;
|
||||||
input.id = "plant_"+i+"_pump_time_s";
|
plant_pump_hour_start.value = current.plants[i].pump_hour_start.toString();
|
||||||
input.onchange = submitForm;
|
let plant_pump_hour_end = document.getElementById("plant_" + i + "_pump_hour_end") as HTMLInputElement;
|
||||||
input.type = "number";
|
plant_pump_hour_end.value = current.plants[i].pump_hour_end.toString();
|
||||||
input.min = "0";
|
|
||||||
input.max = "600";
|
|
||||||
holder.appendChild(input)
|
|
||||||
|
|
||||||
var text = document.createElement("span");
|
|
||||||
holder.appendChild(text)
|
|
||||||
text.innerHTML += "Pump Time (s)"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sync(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sync(current:PlantConfig){
|
|
||||||
plantcount = current.plants.length
|
|
||||||
tank_full_ml.disabled = !current.tank_sensor_enabled;
|
|
||||||
tank_warn_percent.disabled = !current.tank_sensor_enabled;
|
|
||||||
|
|
||||||
tank_sensor_enabled.checked = current.tank_sensor_enabled;
|
|
||||||
tank_full_ml.value = current.tank_full_ml.toString();
|
|
||||||
tank_warn_percent.value = current.tank_warn_percent.toString();
|
|
||||||
night_lamp_time_start.value = current.night_lamp_time_start;
|
|
||||||
night_lamp_time_end.value = current.night_lamp_time_end;
|
|
||||||
|
|
||||||
for(let i=0;i<current.plants.length;i++){
|
|
||||||
let plant_target_moisture = document.getElementById("plant_"+i+"_target_moisture") as HTMLInputElement;
|
|
||||||
plant_target_moisture.value = current.plants[i].target_moisture.toString();
|
|
||||||
let plant_pump_time_s = document.getElementById("plant_"+i+"_pump_time_s") as HTMLInputElement;
|
|
||||||
plant_pump_time_s.value = current.plants[i].pump_time_s.toString();
|
|
||||||
let plant_pump_cooldown_min = document.getElementById("plant_"+i+"_pump_cooldown_min") as HTMLInputElement;
|
|
||||||
plant_pump_cooldown_min.value = current.plants[i].pump_cooldown_min.toString();
|
|
||||||
let plant_pump_hour_start = document.getElementById("plant_"+i+"_pump_hour_start") as HTMLInputElement;
|
|
||||||
plant_pump_hour_start.value = current.plants[i].pump_hour_start;
|
|
||||||
let plant_pump_hour_end = document.getElementById("plant_"+i+"_pump_hour_end") as HTMLInputElement;
|
|
||||||
plant_pump_hour_end.value = current.plants[i].pump_hour_end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function submitForm() {
|
|
||||||
var current: PlantConfig = {
|
|
||||||
tank_sensor_enabled: tank_sensor_enabled.checked,
|
|
||||||
tank_full_ml: +tank_full_ml.value,
|
|
||||||
tank_warn_percent: +tank_warn_percent.value,
|
|
||||||
night_lamp_time_start: night_lamp_time_start.value,
|
|
||||||
night_lamp_time_end: night_lamp_time_end.value,
|
|
||||||
night_lamp_only_when_dark: night_lamp_only_when_dark.checked,
|
|
||||||
plants: []
|
|
||||||
}
|
|
||||||
|
|
||||||
for(let i=0;i<plantcount;i++){
|
|
||||||
console.log("Adding plant " + i)
|
|
||||||
let plant_target_moisture = document.getElementById("plant_"+i+"_target_moisture") as HTMLInputElement;
|
|
||||||
let plant_pump_time_s = document.getElementById("plant_"+i+"_pump_time_s") as HTMLInputElement;
|
|
||||||
let plant_pump_cooldown_min = document.getElementById("plant_"+i+"_pump_cooldown_min") as HTMLInputElement;
|
|
||||||
let plant_pump_hour_start = document.getElementById("plant_"+i+"_pump_hour_start") as HTMLInputElement;
|
|
||||||
let plant_pump_hour_end = document.getElementById("plant_"+i+"_pump_hour_end") as HTMLInputElement;
|
|
||||||
|
|
||||||
current.plants[i] = {
|
|
||||||
target_moisture : +plant_target_moisture.value,
|
|
||||||
pump_time_s: +plant_pump_time_s.value,
|
|
||||||
pump_cooldown_min: +plant_pump_cooldown_min.value,
|
|
||||||
pump_hour_start: plant_pump_hour_start.value,
|
|
||||||
pump_hour_end: plant_pump_hour_end.value
|
|
||||||
|
|
||||||
|
function updateJson() {
|
||||||
|
var current: PlantConfig = {
|
||||||
|
tank_sensor_enabled: tank_sensor_enabled.checked,
|
||||||
|
tank_full_ml: +tank_full_ml.value,
|
||||||
|
tank_warn_percent: +tank_warn_percent.value,
|
||||||
|
night_lamp_hour_start: +night_lamp_time_start.value,
|
||||||
|
night_lamp_hour_end: +night_lamp_time_end.value,
|
||||||
|
night_lamp_only_when_dark: night_lamp_only_when_dark.checked,
|
||||||
|
plants: []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < plantcount; i++) {
|
||||||
|
console.log("Adding plant " + i)
|
||||||
|
let plant_target_moisture = document.getElementById("plant_" + i + "_target_moisture") as HTMLInputElement;
|
||||||
|
let plant_pump_time_s = document.getElementById("plant_" + i + "_pump_time_s") as HTMLInputElement;
|
||||||
|
let plant_pump_cooldown_min = document.getElementById("plant_" + i + "_pump_cooldown_min") as HTMLInputElement;
|
||||||
|
let plant_pump_hour_start = document.getElementById("plant_" + i + "_pump_hour_start") as HTMLInputElement;
|
||||||
|
let plant_pump_hour_end = document.getElementById("plant_" + i + "_pump_hour_end") as HTMLInputElement;
|
||||||
|
|
||||||
|
current.plants[i] = {
|
||||||
|
target_moisture: +plant_target_moisture.value,
|
||||||
|
pump_time_s: +plant_pump_time_s.value,
|
||||||
|
pump_cooldown_min: +plant_pump_cooldown_min.value,
|
||||||
|
pump_hour_start: +plant_pump_hour_start.value,
|
||||||
|
pump_hour_end: +plant_pump_hour_end.value
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sync(current);
|
||||||
|
console.log(current);
|
||||||
|
|
||||||
|
var pretty = JSON.stringify(current, undefined, 4);
|
||||||
|
json.value = pretty;
|
||||||
}
|
}
|
||||||
sync(current);
|
|
||||||
console.log(current);
|
|
||||||
|
|
||||||
var pretty = JSON.stringify(current, undefined, 4);
|
|
||||||
json.value = pretty;
|
|
||||||
}
|
|
||||||
|
|
||||||
let createDocumentBtn = document.getElementById("create") as HTMLButtonElement
|
|
||||||
if(createDocumentBtn){
|
|
||||||
createDocumentBtn.onclick = createForm;
|
|
||||||
}
|
|
||||||
let submitFormBtn = document.getElementById("submit") as HTMLButtonElement
|
|
||||||
if(submitFormBtn){
|
|
||||||
submitFormBtn.onclick = submitForm;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
let submitFormBtn = document.getElementById("submit") as HTMLButtonElement
|
||||||
|
if (submitFormBtn) {
|
||||||
|
submitFormBtn.onclick = function (){
|
||||||
|
updateJson()
|
||||||
|
fetch("/set_config", {
|
||||||
|
method :"POST",
|
||||||
|
body: json.value
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch("/get_config")
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(json => { createForm(json as PlantConfig); }
|
||||||
|
)
|
||||||
})
|
})
|
||||||
if(plants){
|
if (plants) {
|
||||||
fromWrapper()
|
fromWrapper()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ export function saveWifi(){
|
|||||||
alert("Failed to save config see console")
|
alert("Failed to save config see console")
|
||||||
}
|
}
|
||||||
ajax.open("POST", "/wifisave");
|
ajax.open("POST", "/wifisave");
|
||||||
ajax.send();
|
ajax.send(pretty);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function scanWifi(){
|
export function scanWifi(){
|
||||||
|
Loading…
Reference in New Issue
Block a user