add pump current configuration
This commit is contained in:
		| @@ -118,6 +118,9 @@ pub struct PlantConfig { | ||||
|     pub max_consecutive_pump_count: u8, | ||||
|     pub moisture_sensor_min_frequency: Option<f32>, // Optional min frequency | ||||
|     pub moisture_sensor_max_frequency: Option<f32>, // Optional max frequency | ||||
|     pub min_pump_current_ma: u16, | ||||
|     pub max_pump_current_ma: u16, | ||||
|     pub ignore_current_error: bool, | ||||
| } | ||||
|  | ||||
| impl Default for PlantConfig { | ||||
| @@ -134,6 +137,9 @@ impl Default for PlantConfig { | ||||
|             max_consecutive_pump_count: 10, | ||||
|             moisture_sensor_min_frequency: None, // No override by default | ||||
|             moisture_sensor_max_frequency: None, // No override by default | ||||
|             min_pump_current_ma: 10, | ||||
|             max_pump_current_ma: 3000, | ||||
|             ignore_current_error: true, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -107,6 +107,10 @@ impl<'a> BoardInteraction<'a> for Initial<'a> { | ||||
|         bail!("Please configure board revision") | ||||
|     } | ||||
|  | ||||
|     fn pump_current(&mut self, _plant: usize) -> Result<Current> { | ||||
|         bail!("Please configure board revision") | ||||
|     } | ||||
|  | ||||
|     fn fault(&mut self, _plant: usize, _enable: bool) -> Result<()> { | ||||
|         bail!("Please configure board revision") | ||||
|     } | ||||
| @@ -119,10 +123,6 @@ impl<'a> BoardInteraction<'a> for Initial<'a> { | ||||
|         let _ = self.general_fault.set_state(enable.into()); | ||||
|     } | ||||
|  | ||||
|     fn test_pump(&mut self, _plant: usize) -> Result<()> { | ||||
|         bail!("Please configure board revision") | ||||
|     } | ||||
|  | ||||
|     fn test(&mut self) -> Result<()> { | ||||
|         bail!("Please configure board revision") | ||||
|     } | ||||
|   | ||||
| @@ -99,11 +99,10 @@ pub trait BoardInteraction<'a> { | ||||
|     //should be multsampled | ||||
|     fn light(&mut self, enable: bool) -> Result<()>; | ||||
|     fn pump(&mut self, plant: usize, enable: bool) -> Result<()>; | ||||
|     fn pump_current(&mut self, plant: usize) -> Result<Current>; | ||||
|     fn fault(&mut self, plant: usize, enable: bool) -> Result<()>; | ||||
|     fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> Result<f32>; | ||||
|     fn general_fault(&mut self, enable: bool); | ||||
|  | ||||
|     fn test_pump(&mut self, plant: usize) -> Result<()>; | ||||
|     fn test(&mut self) -> Result<()>; | ||||
|     fn set_config(&mut self, config: PlantControllerConfig) -> Result<()>; | ||||
|     fn get_mptt_voltage(&mut self) -> anyhow::Result<Voltage>; | ||||
|   | ||||
| @@ -14,7 +14,7 @@ use esp_idf_hal::{ | ||||
|     gpio::{AnyInputPin, IOPin, InputOutput, PinDriver, Pull}, | ||||
|     pcnt::{PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex}, | ||||
| }; | ||||
| use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, vTaskDelay}; | ||||
| use esp_idf_sys::{gpio_hold_dis, gpio_hold_en}; | ||||
| use measurements::{Current, Voltage}; | ||||
| use plant_ctrl2::sipo::ShiftRegister40; | ||||
| use std::result::Result::Ok as OkStd; | ||||
| @@ -252,6 +252,10 @@ impl<'a> BoardInteraction<'a> for V3<'a> { | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     fn pump_current(&mut self, _plant: usize) -> Result<Current> { | ||||
|         bail!("Not implemented in v3") | ||||
|     } | ||||
|  | ||||
|     fn fault(&mut self, plant: usize, enable: bool) -> Result<()> { | ||||
|         let index = match plant { | ||||
|             0 => FAULT_1, | ||||
| @@ -365,13 +369,6 @@ impl<'a> BoardInteraction<'a> for V3<'a> { | ||||
|         unsafe { gpio_hold_en(self.general_fault.pin()) }; | ||||
|     } | ||||
|  | ||||
|     fn test_pump(&mut self, plant: usize) -> Result<()> { | ||||
|         self.pump(plant, true)?; | ||||
|         unsafe { vTaskDelay(30000) }; | ||||
|         self.pump(plant, false)?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     fn test(&mut self) -> Result<()> { | ||||
|         self.general_fault(true); | ||||
|         self.esp.delay.delay_ms(100); | ||||
|   | ||||
| @@ -13,7 +13,7 @@ use embedded_hal::digital::OutputPin; | ||||
| use embedded_hal_bus::i2c::MutexDevice; | ||||
| use esp_idf_hal::delay::Delay; | ||||
| use esp_idf_hal::gpio::{AnyInputPin, IOPin, InputOutput, Output, PinDriver, Pull}; | ||||
| use esp_idf_hal::i2c::{I2cDriver, I2cError}; | ||||
| use esp_idf_hal::i2c::I2cDriver; | ||||
| use esp_idf_hal::pcnt::{ | ||||
|     PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex, | ||||
| }; | ||||
| @@ -21,7 +21,6 @@ use esp_idf_sys::{gpio_hold_dis, gpio_hold_en}; | ||||
| use ina219::address::{Address, Pin}; | ||||
| use ina219::calibration::UnCalibrated; | ||||
| use ina219::configuration::{Configuration, OperatingMode}; | ||||
| use ina219::errors::InitializationError; | ||||
| use ina219::SyncIna219; | ||||
| use measurements::{Current, Resistance, Voltage}; | ||||
| use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface}; | ||||
| @@ -125,6 +124,7 @@ pub struct V4<'a> { | ||||
|     light: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, | ||||
|     general_fault: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, | ||||
|     pump_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>, | ||||
|     pump_ina: Option<SyncIna219<MutexDevice<'a, I2cDriver<'a>>, UnCalibrated>>, | ||||
|     sensor_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>, | ||||
|     extra1: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, Output>, | ||||
|     extra2: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, Output>, | ||||
| @@ -208,7 +208,7 @@ pub(crate) fn create_v4( | ||||
|         let _ = sensor_expander.pin_set_low(GPIOBank::Bank1, pin); | ||||
|     } | ||||
|  | ||||
|     let mut mppt_ina = SyncIna219::new( | ||||
|     let mppt_ina = SyncIna219::new( | ||||
|         MutexDevice::new(&I2C_DRIVER), | ||||
|         Address::from_pins(Pin::Vcc, Pin::Gnd), | ||||
|     ); | ||||
| @@ -224,15 +224,6 @@ pub(crate) fn create_v4( | ||||
|                 operating_mode: Default::default(), | ||||
|             })?; | ||||
|  | ||||
|             //TODO this is probably already done until we are ready first time?, maybe add startup time comparison on access? | ||||
|             esp.delay.delay_ms( | ||||
|                 mppt_ina | ||||
|                     .configuration()? | ||||
|                     .conversion_time() | ||||
|                     .unwrap() | ||||
|                     .as_millis() as u32, | ||||
|             ); | ||||
|  | ||||
|             Charger::SolarMpptV1 { | ||||
|                 mppt_ina, | ||||
|                 solar_is_day, | ||||
| @@ -242,6 +233,17 @@ pub(crate) fn create_v4( | ||||
|         Err(_) => Charger::ErrorInit {}, | ||||
|     }; | ||||
|  | ||||
|     let pump_ina = match SyncIna219::new( | ||||
|         MutexDevice::new(&I2C_DRIVER), | ||||
|         Address::from_pins(Pin::Gnd, Pin::Sda), | ||||
|     ) { | ||||
|         Ok(pump_ina) => Some(pump_ina), | ||||
|         Err(err) => { | ||||
|             println!("Error creating pump ina: {:?}", err); | ||||
|             None | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let v = V4 { | ||||
|         rtc_module, | ||||
|         esp, | ||||
| @@ -250,6 +252,7 @@ pub(crate) fn create_v4( | ||||
|         signal_counter, | ||||
|         light, | ||||
|         general_fault, | ||||
|         pump_ina, | ||||
|         pump_expander, | ||||
|         sensor_expander, | ||||
|         config, | ||||
| @@ -314,6 +317,24 @@ impl<'a> BoardInteraction<'a> for V4<'a> { | ||||
|         anyhow::Ok(()) | ||||
|     } | ||||
|  | ||||
|     fn pump_current(&mut self, _plant: usize) -> anyhow::Result<Current> { | ||||
|         //sensore is shared for all pumps, ignore plant id | ||||
|         match self.pump_ina.as_mut() { | ||||
|             None => { | ||||
|                 bail!("pump current sensor not available"); | ||||
|             } | ||||
|             Some(pump_ina) => { | ||||
|                 let v = pump_ina.shunt_voltage().map(|v| { | ||||
|                     let shunt_voltage = Voltage::from_microvolts(v.shunt_voltage_uv().abs() as f64); | ||||
|                     let shut_value = Resistance::from_ohms(0.05_f64); | ||||
|                     let current = shunt_voltage.as_volts() / shut_value.as_ohms(); | ||||
|                     Current::from_amperes(current) | ||||
|                 })?; | ||||
|                 Ok(v) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn fault(&mut self, plant: usize, enable: bool) -> anyhow::Result<()> { | ||||
|         if enable { | ||||
|             self.pump_expander | ||||
| @@ -406,13 +427,6 @@ impl<'a> BoardInteraction<'a> for V4<'a> { | ||||
|         unsafe { gpio_hold_en(self.general_fault.pin()) }; | ||||
|     } | ||||
|  | ||||
|     fn test_pump(&mut self, plant: usize) -> anyhow::Result<()> { | ||||
|         self.pump(plant, true)?; | ||||
|         self.esp.delay.delay_ms(30000); | ||||
|         self.pump(plant, false)?; | ||||
|         anyhow::Ok(()) | ||||
|     } | ||||
|  | ||||
|     fn test(&mut self) -> anyhow::Result<()> { | ||||
|         self.general_fault(true); | ||||
|         self.esp.delay.delay_ms(100); | ||||
|   | ||||
| @@ -93,6 +93,7 @@ pub fn log(message_key: LogMessage, number_a: u32, number_b: u32, txt_short: &st | ||||
|     let serial_entry = template.fill_in(&values); | ||||
|  | ||||
|     println!("{serial_entry}"); | ||||
|     //TODO push to mqtt? | ||||
|  | ||||
|     let entry = LogEntry { | ||||
|         timestamp: time, | ||||
| @@ -193,6 +194,16 @@ pub enum LogMessage { | ||||
|         serialize = "Pumped multiple times, but plant is still to try attempt: ${number_a} limit :: ${number_b} plant: ${txt_short}" | ||||
|     )] | ||||
|     ConsecutivePumpCountLimit, | ||||
|     #[strum( | ||||
|         serialize = "Pump Overcurrent error, pump: ${number_a} tripped overcurrent ${number_b} limit was ${txt_short} @s ${txt_long}" | ||||
|     )] | ||||
|     PumpOverCurrent, | ||||
|     #[strum( | ||||
|         serialize = "Pump Open loop error, pump: ${number_a} is low,  ${number_b} limit was ${txt_short} @s ${txt_long}" | ||||
|     )] | ||||
|     PumpOpenLoopCurrent, | ||||
|     #[strum(serialize = "Pump Open current sensor required but did not work: ${number_a}")] | ||||
|     PumpMissingSensorCurrent, | ||||
| } | ||||
|  | ||||
| #[derive(Serialize)] | ||||
|   | ||||
							
								
								
									
										127
									
								
								rust/src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										127
									
								
								rust/src/main.rs
									
									
									
									
									
								
							| @@ -1,3 +1,4 @@ | ||||
| use crate::config::PlantConfig; | ||||
| use crate::{ | ||||
|     config::BoardVersion::INITIAL, | ||||
|     hal::{PlantHal, HAL, PLANT_COUNT}, | ||||
| @@ -76,6 +77,17 @@ struct LightState { | ||||
| struct PumpInfo { | ||||
|     enabled: bool, | ||||
|     pump_ineffective: bool, | ||||
|     median_current_ma: u16, | ||||
|     max_current_ma: u16, | ||||
|     min_current_ma: u16, | ||||
| } | ||||
|  | ||||
| #[derive(Serialize)] | ||||
| pub struct PumpResult { | ||||
|     median_current_ma: u16, | ||||
|     max_current_ma: u16, | ||||
|     min_current_ma: u16, | ||||
|     error: bool, | ||||
| } | ||||
|  | ||||
| #[derive(Serialize, Deserialize, Debug, PartialEq)] | ||||
| @@ -225,6 +237,8 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|         try_connect_wifi_sntp_mqtt(&mut board) | ||||
|     } else { | ||||
|         println!("No wifi configured"); | ||||
|         //the current sensors require this amount to stabilize, in case of wifi this is already handles for sure; | ||||
|         board.board_hal.get_esp().delay.delay_ms(100); | ||||
|         NetworkMode::OFFLINE | ||||
|     }; | ||||
|  | ||||
| @@ -395,14 +409,20 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|                 board.board_hal.get_esp().last_pump_time(plant_id); | ||||
|                 //state.active = true; | ||||
|  | ||||
|                 pump_info(&mut board, plant_id, true, pump_ineffective); | ||||
|                 pump_info(&mut board, plant_id, true, pump_ineffective, 0, 0, 0, false); | ||||
|  | ||||
|                 if !dry_run { | ||||
|                     board.board_hal.pump(plant_id, true)?; | ||||
|                     Delay::new_default().delay_ms(1000 * plant_config.pump_time_s as u32); | ||||
|                     board.board_hal.pump(plant_id, false)?; | ||||
|                 } | ||||
|                 pump_info(&mut board, plant_id, false, pump_ineffective); | ||||
|                 let result = do_secure_pump(&mut board, plant_id, plant_config, dry_run)?; | ||||
|                 board.board_hal.pump(plant_id, false)?; | ||||
|                 pump_info( | ||||
|                     &mut board, | ||||
|                     plant_id, | ||||
|                     false, | ||||
|                     pump_ineffective, | ||||
|                     result.median_current_ma, | ||||
|                     result.max_current_ma, | ||||
|                     result.min_current_ma, | ||||
|                     result.error, | ||||
|                 ); | ||||
|             } else if !state.pump_in_timeout(plant_config, &timezone_time) { | ||||
|                 // plant does not need to be watered and is not in timeout | ||||
|                 // -> reset consecutive pump count | ||||
| @@ -556,6 +576,92 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|         .deep_sleep(1000 * 1000 * 60 * deep_sleep_duration_minutes as u64); | ||||
| } | ||||
|  | ||||
| pub fn do_secure_pump( | ||||
|     board: &mut MutexGuard<HAL>, | ||||
|     plant_id: usize, | ||||
|     plant_config: &PlantConfig, | ||||
|     dry_run: bool, | ||||
| ) -> anyhow::Result<PumpResult> { | ||||
|     let mut current_collector = vec![0_u16; plant_config.pump_time_s.into()]; | ||||
|     let mut error = false; | ||||
|     let mut first_error = true; | ||||
|     if !dry_run { | ||||
|         board.board_hal.pump(plant_id, true)?; | ||||
|         Delay::new_default().delay_ms(2); | ||||
|         for step in 0..plant_config.pump_time_s as usize { | ||||
|             let current = board.board_hal.pump_current(plant_id); | ||||
|             match current { | ||||
|                 Ok(current) => { | ||||
|                     let current_ma = current.as_milliamperes() as u16; | ||||
|                     current_collector[step] = current_ma; | ||||
|                     let high_current = current_ma > plant_config.max_pump_current_ma; | ||||
|                     if high_current { | ||||
|                         if first_error { | ||||
|                             log( | ||||
|                                 LogMessage::PumpOverCurrent, | ||||
|                                 plant_id as u32 + 1, | ||||
|                                 current_ma as u32, | ||||
|                                 plant_config.max_pump_current_ma.to_string().as_str(), | ||||
|                                 step.to_string().as_str(), | ||||
|                             ); | ||||
|                             board.board_hal.general_fault(true); | ||||
|                             board.board_hal.fault(plant_id, true)?; | ||||
|                             if !plant_config.ignore_current_error { | ||||
|                                 error = true; | ||||
|                                 break; | ||||
|                             } | ||||
|                             first_error = false; | ||||
|                         } | ||||
|                     } | ||||
|                     let low_current = current_ma < plant_config.min_pump_current_ma; | ||||
|                     if low_current { | ||||
|                         if first_error { | ||||
|                             log( | ||||
|                                 LogMessage::PumpOpenLoopCurrent, | ||||
|                                 plant_id as u32 + 1, | ||||
|                                 current_ma as u32, | ||||
|                                 plant_config.min_pump_current_ma.to_string().as_str(), | ||||
|                                 step.to_string().as_str(), | ||||
|                             ); | ||||
|                             board.board_hal.general_fault(true); | ||||
|                             board.board_hal.fault(plant_id, true)?; | ||||
|                             if !plant_config.ignore_current_error { | ||||
|                                 error = true; | ||||
|                                 break; | ||||
|                             } | ||||
|                             first_error = false; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 Err(err) => { | ||||
|                     if !plant_config.ignore_current_error { | ||||
|                         println!("Error getting pump current: {}", err); | ||||
|                         log( | ||||
|                             LogMessage::PumpMissingSensorCurrent, | ||||
|                             plant_id as u32, | ||||
|                             0, | ||||
|                             "", | ||||
|                             "", | ||||
|                         ); | ||||
|                         error = true; | ||||
|                         break; | ||||
|                     } else { | ||||
|                         //eg v3 without a sensor ends here, do not spam | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             Delay::new_default().delay_ms(1000); | ||||
|         } | ||||
|     } | ||||
|     current_collector.sort(); | ||||
|     Ok(PumpResult { | ||||
|         median_current_ma: current_collector[current_collector.len() / 2], | ||||
|         max_current_ma: current_collector[current_collector.len() - 1], | ||||
|         min_current_ma: current_collector[0], | ||||
|         error, | ||||
|     }) | ||||
| } | ||||
|  | ||||
| fn update_charge_indicator(board: &mut MutexGuard<HAL>) { | ||||
|     //we have mppt controller, ask it for charging current | ||||
|     if let Ok(current) = board.board_hal.get_mptt_current() { | ||||
| @@ -711,10 +817,17 @@ fn pump_info( | ||||
|     plant_id: usize, | ||||
|     pump_active: bool, | ||||
|     pump_ineffective: bool, | ||||
|     median_current_ma: u16, | ||||
|     max_current_ma: u16, | ||||
|     min_current_ma: u16, | ||||
|     error: bool, | ||||
| ) { | ||||
|     let pump_info = PumpInfo { | ||||
|         enabled: pump_active, | ||||
|         pump_ineffective, | ||||
|         median_current_ma: median_current_ma, | ||||
|         max_current_ma: max_current_ma, | ||||
|         min_current_ma: min_current_ma, | ||||
|     }; | ||||
|     let pump_topic = format!("/pump{}", plant_id + 1); | ||||
|     match serde_json::to_string(&pump_info) { | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|  | ||||
| use crate::{ | ||||
|     config::PlantControllerConfig, | ||||
|     determine_tank_state, get_version, | ||||
|     determine_tank_state, do_secure_pump, get_version, | ||||
|     hal::PLANT_COUNT, | ||||
|     log::LogMessage, | ||||
|     plant_state::{MoistureSensorState, PlantState}, | ||||
| @@ -222,7 +222,7 @@ fn backup_info( | ||||
| fn set_config( | ||||
|     request: &mut Request<&mut EspHttpConnection>, | ||||
| ) -> Result<Option<std::string::String>, anyhow::Error> { | ||||
|     let all = read_up_to_bytes_from_request(request, Some(3072))?; | ||||
|     let all = read_up_to_bytes_from_request(request, Some(4096))?; | ||||
|     let config: PlantControllerConfig = serde_json::from_slice(&all)?; | ||||
|  | ||||
|     let mut board = BOARD_ACCESS.lock().expect("board access"); | ||||
| @@ -275,8 +275,11 @@ fn pump_test( | ||||
|     let actual_data = read_up_to_bytes_from_request(request, None)?; | ||||
|     let pump_test: TestPump = serde_json::from_slice(&actual_data)?; | ||||
|     let mut board = BOARD_ACCESS.lock().unwrap(); | ||||
|     board.board_hal.test_pump(pump_test.pump)?; | ||||
|     anyhow::Ok(None) | ||||
|  | ||||
|     let config = &board.board_hal.get_config().plants[pump_test.pump].clone(); | ||||
|     let pump_result = do_secure_pump(&mut board, pump_test.pump, config, false)?; | ||||
|     board.board_hal.pump(pump_test.pump, false)?; | ||||
|     anyhow::Ok(Some(serde_json::to_string(&pump_result)?)) | ||||
| } | ||||
|  | ||||
| fn tank_info( | ||||
|   | ||||
| @@ -1,174 +1,185 @@ | ||||
| export interface LogArray  extends Array<LogEntry>{} | ||||
|  | ||||
| export interface LogEntry { | ||||
|   timestamp: string, | ||||
|   message_id: number, | ||||
|   a: number, | ||||
|   b: number, | ||||
|   txt_short: string, | ||||
|   txt_long: string | ||||
| export interface LogArray extends Array<LogEntry> { | ||||
| } | ||||
|  | ||||
| export interface LogLocalisation extends Array<LogLocalisationEntry>{} | ||||
| export interface LogEntry { | ||||
|     timestamp: string, | ||||
|     message_id: number, | ||||
|     a: number, | ||||
|     b: number, | ||||
|     txt_short: string, | ||||
|     txt_long: string | ||||
| } | ||||
|  | ||||
| export interface LogLocalisation extends Array<LogLocalisationEntry> { | ||||
| } | ||||
|  | ||||
| export interface LogLocalisationEntry { | ||||
|   msg_type: string, | ||||
|   message: string | ||||
|     msg_type: string, | ||||
|     message: string | ||||
| } | ||||
|  | ||||
| export interface BackupHeader { | ||||
|   timestamp: string, | ||||
|   size: number | ||||
|     timestamp: string, | ||||
|     size: number | ||||
| } | ||||
|  | ||||
| export interface NetworkConfig { | ||||
|   ap_ssid: string, | ||||
|   ssid: string, | ||||
|   password: string, | ||||
|   mqtt_url: string, | ||||
|   base_topic: string, | ||||
|   max_wait: number | ||||
|     ap_ssid: string, | ||||
|     ssid: string, | ||||
|     password: string, | ||||
|     mqtt_url: string, | ||||
|     base_topic: string, | ||||
|     max_wait: number | ||||
| } | ||||
|  | ||||
| export interface FileList { | ||||
|   total: number, | ||||
|   used: number, | ||||
|   files: FileInfo[], | ||||
|   file_system_corrupt: string, | ||||
|   iter_error: string, | ||||
|     total: number, | ||||
|     used: number, | ||||
|     files: FileInfo[], | ||||
|     file_system_corrupt: string, | ||||
|     iter_error: string, | ||||
| } | ||||
|  | ||||
| export interface SolarState{ | ||||
|   mppt_voltage: number, | ||||
|   mppt_current: number, | ||||
|   is_day: boolean | ||||
| export interface SolarState { | ||||
|     mppt_voltage: number, | ||||
|     mppt_current: number, | ||||
|     is_day: boolean | ||||
| } | ||||
|  | ||||
| export interface FileInfo{ | ||||
|   filename: string, | ||||
|   size: number, | ||||
| export interface FileInfo { | ||||
|     filename: string, | ||||
|     size: number, | ||||
| } | ||||
|  | ||||
| export interface NightLampConfig { | ||||
|   enabled: boolean, | ||||
|   night_lamp_hour_start: number, | ||||
|   night_lamp_hour_end: number, | ||||
|   night_lamp_only_when_dark: boolean, | ||||
|   low_soc_cutoff: number, | ||||
|   low_soc_restore: number | ||||
|     enabled: boolean, | ||||
|     night_lamp_hour_start: number, | ||||
|     night_lamp_hour_end: number, | ||||
|     night_lamp_only_when_dark: boolean, | ||||
|     low_soc_cutoff: number, | ||||
|     low_soc_restore: number | ||||
| } | ||||
|  | ||||
| export interface NightLampCommand { | ||||
|   active: boolean | ||||
|     active: boolean | ||||
| } | ||||
|  | ||||
| export interface TankConfig { | ||||
|   tank_sensor_enabled: boolean, | ||||
|   tank_allow_pumping_if_sensor_error: boolean, | ||||
|   tank_useable_ml: number, | ||||
|   tank_warn_percent: number, | ||||
|   tank_empty_percent: number, | ||||
|   tank_full_percent: number, | ||||
|     tank_sensor_enabled: boolean, | ||||
|     tank_allow_pumping_if_sensor_error: boolean, | ||||
|     tank_useable_ml: number, | ||||
|     tank_warn_percent: number, | ||||
|     tank_empty_percent: number, | ||||
|     tank_full_percent: number, | ||||
| } | ||||
|  | ||||
|  | ||||
| export enum BatteryBoardVersion { | ||||
|   Disabled = "Disabled", | ||||
|   BQ34Z100G1 = "BQ34Z100G1", | ||||
|   WchI2cSlave = "WchI2cSlave" | ||||
|     Disabled = "Disabled", | ||||
|     BQ34Z100G1 = "BQ34Z100G1", | ||||
|     WchI2cSlave = "WchI2cSlave" | ||||
| } | ||||
| export enum BoardVersion{ | ||||
|  | ||||
| export enum BoardVersion { | ||||
|     INITIAL = "INITIAL", | ||||
|     V3 = "V3", | ||||
|     V4 = "V4" | ||||
| } | ||||
|  | ||||
| export interface BoardHardware { | ||||
|   board: BoardVersion, | ||||
|   battery: BatteryBoardVersion, | ||||
|     board: BoardVersion, | ||||
|     battery: BatteryBoardVersion, | ||||
| } | ||||
|  | ||||
| export interface PlantControllerConfig { | ||||
|   hardware: BoardHardware, | ||||
|     hardware: BoardHardware, | ||||
|  | ||||
|   network: NetworkConfig, | ||||
|   tank: TankConfig, | ||||
|   night_lamp: NightLampConfig, | ||||
|   plants: PlantConfig[] | ||||
|   timezone?: string, | ||||
|     network: NetworkConfig, | ||||
|     tank: TankConfig, | ||||
|     night_lamp: NightLampConfig, | ||||
|     plants: PlantConfig[] | ||||
|     timezone?: string, | ||||
| } | ||||
|  | ||||
| export interface PlantConfig { | ||||
|   mode: string, | ||||
|   target_moisture: number, | ||||
|   pump_time_s: number, | ||||
|   pump_cooldown_min: number, | ||||
|   pump_hour_start: number, | ||||
|   pump_hour_end: number, | ||||
|   sensor_a: boolean, | ||||
|   sensor_b: boolean, | ||||
|   max_consecutive_pump_count: number, | ||||
|   moisture_sensor_min_frequency: number | null; | ||||
|   moisture_sensor_max_frequency: number | null; | ||||
|  | ||||
|     mode: string, | ||||
|     target_moisture: number, | ||||
|     pump_time_s: number, | ||||
|     pump_cooldown_min: number, | ||||
|     pump_hour_start: number, | ||||
|     pump_hour_end: number, | ||||
|     sensor_a: boolean, | ||||
|     sensor_b: boolean, | ||||
|     max_consecutive_pump_count: number, | ||||
|     moisture_sensor_min_frequency: number | null; | ||||
|     moisture_sensor_max_frequency: number | null; | ||||
|     min_pump_current_ma: number, | ||||
|     max_pump_current_ma: number, | ||||
|     ignore_current_error: boolean, | ||||
| } | ||||
|  | ||||
| export interface PumpTestResult { | ||||
|     median_current_ma: number, | ||||
|     max_current_ma: number, | ||||
|     min_current_ma: number, | ||||
|     error: boolean, | ||||
| } | ||||
|  | ||||
| export interface SSIDList { | ||||
|   ssids: [string] | ||||
|     ssids: [string] | ||||
| } | ||||
|  | ||||
| export interface TestPump { | ||||
|   pump: number | ||||
|     pump: number | ||||
| } | ||||
|  | ||||
| export interface SetTime { | ||||
|   time: string | ||||
|     time: string | ||||
| } | ||||
|  | ||||
| export interface GetTime { | ||||
|   rtc: string, | ||||
|   native: string | ||||
|     rtc: string, | ||||
|     native: string | ||||
| } | ||||
|  | ||||
| export interface Moistures { | ||||
|   moisture_a: [string], | ||||
|   moisture_b: [string], | ||||
|     moisture_a: [string], | ||||
|     moisture_b: [string], | ||||
| } | ||||
|  | ||||
| export interface VersionInfo { | ||||
|   git_hash: string, | ||||
|   build_time: string, | ||||
|   partition: string | ||||
|     git_hash: string, | ||||
|     build_time: string, | ||||
|     partition: string | ||||
| } | ||||
|  | ||||
| export interface BatteryState { | ||||
|   temperature: string | ||||
|   voltage_milli_volt: string, | ||||
|   current_milli_ampere: string, | ||||
|   cycle_count: string, | ||||
|   design_milli_ampere: string, | ||||
|   remaining_milli_ampere: string, | ||||
|   state_of_charge: string, | ||||
|   state_of_health: string | ||||
|     temperature: string | ||||
|     voltage_milli_volt: string, | ||||
|     current_milli_ampere: string, | ||||
|     cycle_count: string, | ||||
|     design_milli_ampere: string, | ||||
|     remaining_milli_ampere: string, | ||||
|     state_of_charge: string, | ||||
|     state_of_health: string | ||||
| } | ||||
|  | ||||
| export interface TankInfo { | ||||
|   /// is there enough water in the tank | ||||
|   enough_water: boolean, | ||||
|   /// warning that water needs to be refilled soon | ||||
|   warn_level: boolean, | ||||
|   /// estimation how many ml are still in tank | ||||
|   left_ml: number | null, | ||||
|   /// if there is was an issue with the water level sensor | ||||
|   sensor_error: string | null, | ||||
|   /// raw water sensor value | ||||
|   raw: number | null, | ||||
|   /// percent value | ||||
|   percent: number | null, | ||||
|   /// water in tank might be frozen | ||||
|   water_frozen: boolean, | ||||
|   /// water temperature | ||||
|   water_temp: number | null, | ||||
|   temp_sensor_error: string | null | ||||
|     /// is there enough water in the tank | ||||
|     enough_water: boolean, | ||||
|     /// warning that water needs to be refilled soon | ||||
|     warn_level: boolean, | ||||
|     /// estimation how many ml are still in tank | ||||
|     left_ml: number | null, | ||||
|     /// if there is was an issue with the water level sensor | ||||
|     sensor_error: string | null, | ||||
|     /// raw water sensor value | ||||
|     raw: number | null, | ||||
|     /// percent value | ||||
|     percent: number | null, | ||||
|     /// water in tank might be frozen | ||||
|     water_frozen: boolean, | ||||
|     /// water temperature | ||||
|     water_temp: number | null, | ||||
|     temp_sensor_error: string | null | ||||
| } | ||||
|   | ||||
| @@ -28,7 +28,7 @@ import { | ||||
|     SetTime, SSIDList, TankInfo, | ||||
|     TestPump, | ||||
|     VersionInfo, | ||||
|     FileList, SolarState | ||||
|     FileList, SolarState, PumpTestResult | ||||
| } from "./api"; | ||||
| import {SolarView} from "./solarview"; | ||||
|  | ||||
| @@ -345,9 +345,10 @@ export class Controller { | ||||
|             method: "POST", | ||||
|             body: pretty | ||||
|         }) | ||||
|             .then(response => response.text()) | ||||
|             .then(response => response.json() as Promise<PumpTestResult>) | ||||
|             .then( | ||||
|                 _ => { | ||||
|                 response => { | ||||
|                     controller.plantViews.setPumpTestCurrent(plantId, response); | ||||
|                     clearTimeout(timerId); | ||||
|                     controller.progressview.removeProgress("test_pump"); | ||||
|                 } | ||||
|   | ||||
| @@ -1,27 +1,30 @@ | ||||
| <style> | ||||
|   .plantsensorkey{ | ||||
|     min-width: 100px; | ||||
|   } | ||||
|   .plantsensorvalue{ | ||||
|     flex-grow: 1; | ||||
|   } | ||||
|     .plantsensorkey { | ||||
|         min-width: 100px; | ||||
|     } | ||||
|  | ||||
|   .plantkey{ | ||||
|     min-width: 175px; | ||||
|   } | ||||
|   .plantvalue{ | ||||
|     flex-grow: 1; | ||||
|   } | ||||
|   .plantcheckbox{ | ||||
|     min-width: 20px; | ||||
|     margin: 0; | ||||
|   } | ||||
|     .plantsensorvalue { | ||||
|         flex-grow: 1; | ||||
|     } | ||||
|  | ||||
|     .plantkey { | ||||
|         min-width: 175px; | ||||
|     } | ||||
|  | ||||
|     .plantvalue { | ||||
|         flex-grow: 1; | ||||
|     } | ||||
|  | ||||
|     .plantcheckbox { | ||||
|         min-width: 20px; | ||||
|         margin: 0; | ||||
|     } | ||||
| </style> | ||||
|  | ||||
|  | ||||
| <div> | ||||
|     <div class="subtitle" | ||||
|         id="plant_${plantId}_header"> | ||||
|          id="plant_${plantId}_header"> | ||||
|         Plant ${plantId} | ||||
|     </div> | ||||
|  | ||||
| @@ -47,7 +50,8 @@ | ||||
|  | ||||
|     <div class="flexcontainer"> | ||||
|         <div class="plantkey">Pump Cooldown (m):</div> | ||||
|         <input class="plantvalue" id="plant_${plantId}_pump_cooldown_min" type="number" min="0" max="600" placeholder="30"> | ||||
|         <input class="plantvalue" id="plant_${plantId}_pump_cooldown_min" type="number" min="0" max="600" | ||||
|                placeholder="30"> | ||||
|     </div> | ||||
|     <div class="flexcontainer"> | ||||
|         <div class="plantkey">"Pump Hour Start":</div> | ||||
| @@ -59,8 +63,8 @@ | ||||
|     </div> | ||||
|     <div class="flexcontainer"> | ||||
|         <div class="plantkey">Warn Pump Count:</div> | ||||
|         <input class="plantvalue" id="plant_${plantId}_max_consecutive_pump_count" type="number" min="1"  max="50"  | ||||
|             placeholder="10"> | ||||
|         <input class="plantvalue" id="plant_${plantId}_max_consecutive_pump_count" type="number" min="1" max="50" | ||||
|                placeholder="10"> | ||||
|     </div> | ||||
|     <div class="flexcontainer"> | ||||
|         <div class="plantkey">Min Frequency Override</div> | ||||
| @@ -68,7 +72,7 @@ | ||||
|     </div> | ||||
|     <div class="flexcontainer"> | ||||
|         <div class="plantkey">Max Frequency Override</div> | ||||
|         <input class="plantvalue" id="plant_${plantId}_max_frequency" type="number" min="1000" max="25000" > | ||||
|         <input class="plantvalue" id="plant_${plantId}_max_frequency" type="number" min="1000" max="25000"> | ||||
|     </div> | ||||
|  | ||||
|     <div class="flexcontainer"> | ||||
| @@ -79,6 +83,22 @@ | ||||
|         <div class="plantkey">Sensor B installed:</div> | ||||
|         <input class="plantcheckbox" id="plant_${plantId}_sensor_b" type="checkbox"> | ||||
|     </div> | ||||
|     <div class="flexcontainer"> | ||||
|         <h2 class="plantkey">Current config:</h2> | ||||
|     </div> | ||||
|     <div class="flexcontainer"> | ||||
|         <div class="plantkey">Min current</div> | ||||
|         <input class="plantvalue" id="plant_${plantId}_min_pump_current_ma" type="number" min="0" max="4500"> | ||||
|     </div> | ||||
|     <div class="flexcontainer"> | ||||
|         <div class="plantkey">Max current</div> | ||||
|         <input class="plantvalue" id="plant_${plantId}_max_pump_current_ma" type="number" min="0" max="4500"> | ||||
|     </div> | ||||
|     <div class="flexcontainer"> | ||||
|         <div class="plantkey">Ignore current sensor error</div> | ||||
|         <input class="plantcheckbox" id="plant_${plantId}_ignore_current_error" type="checkbox"> | ||||
|     </div> | ||||
|  | ||||
|  | ||||
|     <div class="flexcontainer"> | ||||
|         <button class="subtitle" id="plant_${plantId}_test">Test Pump</button> | ||||
| @@ -89,11 +109,15 @@ | ||||
|     </div> | ||||
|     <div class="flexcontainer"> | ||||
|         <span class="plantsensorkey">Sensor A:</span> | ||||
|         <span class="plantsensorvalue" id="plant_${plantId}_moisture_a">loading</span> | ||||
|         <span class="plantsensorvalue" id="plant_${plantId}_moisture_a">not measured</span> | ||||
|     </div> | ||||
|     <div class="flexcontainer"> | ||||
|         <div class="plantsensorkey">Sensor B:</div> | ||||
|         <span class="plantsensorvalue" id="plant_${plantId}_moisture_b">loading</span> | ||||
|         <span class="plantsensorvalue" id="plant_${plantId}_moisture_b">not measured</span> | ||||
|     </div> | ||||
|     <div class="flexcontainer"> | ||||
|         <div class="plantsensorkey">Test Current</div> | ||||
|         <span class="plantsensorvalue" id="plant_${plantId}_pump_current_result">not_tested</span> | ||||
|     </div> | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import {PlantConfig} from "./api"; | ||||
| import {PlantConfig, PumpTestResult} from "./api"; | ||||
|  | ||||
| const PLANT_COUNT = 8; | ||||
|  | ||||
| @@ -9,39 +9,45 @@ export class PlantViews { | ||||
|     private readonly measure_moisture: HTMLButtonElement; | ||||
|     private readonly plants: PlantView[] = [] | ||||
|     private readonly plantsDiv: HTMLDivElement | ||||
|    | ||||
|     constructor(syncConfig:Controller) { | ||||
|       this.measure_moisture = document.getElementById("measure_moisture") as HTMLButtonElement | ||||
|       this.measure_moisture.onclick = syncConfig.measure_moisture | ||||
|       this.plantsDiv = document.getElementById("plants") as HTMLDivElement; | ||||
|       for (let plantId = 0; plantId < PLANT_COUNT; plantId++) { | ||||
|         this.plants[plantId] = new PlantView(plantId, this.plantsDiv, syncConfig); | ||||
|       } | ||||
|  | ||||
|     constructor(syncConfig: Controller) { | ||||
|         this.measure_moisture = document.getElementById("measure_moisture") as HTMLButtonElement | ||||
|         this.measure_moisture.onclick = syncConfig.measure_moisture | ||||
|         this.plantsDiv = document.getElementById("plants") as HTMLDivElement; | ||||
|         for (let plantId = 0; plantId < PLANT_COUNT; plantId++) { | ||||
|             this.plants[plantId] = new PlantView(plantId, this.plantsDiv, syncConfig); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     getConfig(): PlantConfig[] { | ||||
|       const rv: PlantConfig[] = []; | ||||
|       for (let i = 0; i < PLANT_COUNT; i++) { | ||||
|         rv[i] = this.plants[i].getConfig(); | ||||
|       } | ||||
|       return rv | ||||
|         const rv: PlantConfig[] = []; | ||||
|         for (let i = 0; i < PLANT_COUNT; i++) { | ||||
|             rv[i] = this.plants[i].getConfig(); | ||||
|         } | ||||
|         return rv | ||||
|     } | ||||
|  | ||||
|     update(moisture_a: [string], moisture_b: [string]) { | ||||
|       for (let plantId = 0; plantId < PLANT_COUNT; plantId++) { | ||||
|         const a = moisture_a[plantId] | ||||
|         const b = moisture_b[plantId] | ||||
|         this.plants[plantId].update(a,b) | ||||
|       } | ||||
|         for (let plantId = 0; plantId < PLANT_COUNT; plantId++) { | ||||
|             const a = moisture_a[plantId] | ||||
|             const b = moisture_b[plantId] | ||||
|             this.plants[plantId].setMeasurementResult(a, b) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     setConfig(plants: PlantConfig[]) { | ||||
|       for (let plantId = 0; plantId < PLANT_COUNT; plantId++) { | ||||
|         const plantConfig = plants[plantId]; | ||||
|         const plantView = this.plants[plantId]; | ||||
|         plantView.setConfig(plantConfig) | ||||
|       } | ||||
|         for (let plantId = 0; plantId < PLANT_COUNT; plantId++) { | ||||
|             const plantConfig = plants[plantId]; | ||||
|             const plantView = this.plants[plantId]; | ||||
|             plantView.setConfig(plantConfig) | ||||
|         } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|     setPumpTestCurrent(plantId: number, response: PumpTestResult) { | ||||
|         const plantView = this.plants[plantId]; | ||||
|         plantView.setTestResult(response) | ||||
|     } | ||||
| } | ||||
|  | ||||
| export class PlantView { | ||||
|     private readonly moistureSensorMinFrequency: HTMLInputElement; | ||||
| @@ -60,109 +66,135 @@ export class PlantView { | ||||
|     private readonly mode: HTMLSelectElement; | ||||
|     private readonly moistureA: HTMLElement; | ||||
|     private readonly moistureB: HTMLElement; | ||||
|     private readonly pump_current_result: HTMLElement | ||||
|     private readonly maxConsecutivePumpCount: HTMLInputElement; | ||||
|    | ||||
|    | ||||
|     constructor(plantId: number, parent:HTMLDivElement, controller:Controller) { | ||||
|     private readonly minPumpCurrentMa: HTMLInputElement; | ||||
|     private readonly maxPumpCurrentMa: HTMLInputElement; | ||||
|     private readonly ignoreCurrentError: HTMLInputElement; | ||||
|  | ||||
|  | ||||
|     constructor(plantId: number, parent: HTMLDivElement, controller: Controller) { | ||||
|         this.plantId = plantId; | ||||
|       this.plantDiv = document.createElement("div")! as HTMLDivElement | ||||
|       const template = require('./plant.html') as string; | ||||
|         this.plantDiv = document.createElement("div")! as HTMLDivElement | ||||
|         const template = require('./plant.html') as string; | ||||
|         this.plantDiv.innerHTML = template.replaceAll("${plantId}", String(plantId)) | ||||
|        | ||||
|       this.plantDiv.classList.add("plantcontainer") | ||||
|       parent.appendChild(this.plantDiv) | ||||
|    | ||||
|       this.header = document.getElementById("plant_"+plantId+"_header")! | ||||
|       this.header.innerText = "Plant "+ (this.plantId+1) | ||||
|    | ||||
|       this.moistureA = document.getElementById("plant_"+plantId+"_moisture_a")! as HTMLElement; | ||||
|       this.moistureB = document.getElementById("plant_"+plantId+"_moisture_b")! as HTMLElement; | ||||
|  | ||||
|       this.testButton = document.getElementById("plant_"+plantId+"_test")! as HTMLButtonElement; | ||||
|       this.testButton.onclick = function(){ | ||||
|         controller.testPlant(plantId) | ||||
|       } | ||||
|         this.plantDiv.classList.add("plantcontainer") | ||||
|         parent.appendChild(this.plantDiv) | ||||
|  | ||||
|       this.mode = document.getElementById("plant_"+plantId+"_mode") as HTMLSelectElement | ||||
|       this.mode.onchange = function(){ | ||||
|         controller.configChanged() | ||||
|       } | ||||
|         this.header = document.getElementById("plant_" + plantId + "_header")! | ||||
|         this.header.innerText = "Plant " + (this.plantId + 1) | ||||
|  | ||||
|       this.targetMoisture = document.getElementById("plant_"+plantId+"_target_moisture")! as HTMLInputElement; | ||||
|       this.targetMoisture.onchange = function(){ | ||||
|         controller.configChanged() | ||||
|       } | ||||
|         this.moistureA = document.getElementById("plant_" + plantId + "_moisture_a")! as HTMLElement; | ||||
|         this.moistureB = document.getElementById("plant_" + plantId + "_moisture_b")! as HTMLElement; | ||||
|         this.pump_current_result = document.getElementById("plant_" + plantId + "_pump_current_result")! as HTMLElement; | ||||
|  | ||||
|       this.pumpTimeS = document.getElementById("plant_"+plantId+"_pump_time_s") as HTMLInputElement; | ||||
|       this.pumpTimeS.onchange = function(){ | ||||
|         controller.configChanged() | ||||
|       } | ||||
|  | ||||
|       this.pumpCooldown = document.getElementById("plant_"+plantId+"_pump_cooldown_min") as HTMLInputElement; | ||||
|       this.pumpCooldown.onchange = function(){ | ||||
|         controller.configChanged() | ||||
|       } | ||||
|  | ||||
|       this.pumpHourStart = document.getElementById("plant_"+plantId+"_pump_hour_start") as HTMLSelectElement; | ||||
|       this.pumpHourStart.onchange = function(){ | ||||
|         controller.configChanged() | ||||
|       } | ||||
|       for (let i = 0; i < 24; i++) { | ||||
|         let option = document.createElement("option"); | ||||
|         if (i == 10){ | ||||
|             option.selected = true | ||||
|         this.testButton = document.getElementById("plant_" + plantId + "_test")! as HTMLButtonElement; | ||||
|         this.testButton.onclick = function () { | ||||
|             controller.testPlant(plantId) | ||||
|         } | ||||
|         option.innerText = i.toString(); | ||||
|         this.pumpHourStart.appendChild(option); | ||||
|       } | ||||
|  | ||||
|       this.pumpHourEnd = document.getElementById("plant_"+plantId+"_pump_hour_end") as HTMLSelectElement; | ||||
|       this.pumpHourEnd.onchange = function(){ | ||||
|         controller.configChanged() | ||||
|       } | ||||
|       for (let i = 0; i < 24; i++) { | ||||
|         let option = document.createElement("option"); | ||||
|         if (i == 19){ | ||||
|             option.selected = true | ||||
|         this.mode = document.getElementById("plant_" + plantId + "_mode") as HTMLSelectElement | ||||
|         this.mode.onchange = function () { | ||||
|             controller.configChanged() | ||||
|         } | ||||
|         option.innerText = i.toString(); | ||||
|         this.pumpHourEnd.appendChild(option); | ||||
|       } | ||||
|  | ||||
|       this.sensorAInstalled = document.getElementById("plant_"+plantId+"_sensor_a") as HTMLInputElement; | ||||
|       this.sensorAInstalled.onchange = function(){ | ||||
|         controller.configChanged() | ||||
|       } | ||||
|         this.targetMoisture = document.getElementById("plant_" + plantId + "_target_moisture")! as HTMLInputElement; | ||||
|         this.targetMoisture.onchange = function () { | ||||
|             controller.configChanged() | ||||
|         } | ||||
|  | ||||
|       this.sensorBInstalled = document.getElementById("plant_"+plantId+"_sensor_b") as HTMLInputElement; | ||||
|       this.sensorBInstalled.onchange = function(){ | ||||
|         controller.configChanged() | ||||
|       } | ||||
|         this.pumpTimeS = document.getElementById("plant_" + plantId + "_pump_time_s") as HTMLInputElement; | ||||
|         this.pumpTimeS.onchange = function () { | ||||
|             controller.configChanged() | ||||
|         } | ||||
|  | ||||
|       this.maxConsecutivePumpCount = document.getElementById("plant_"+plantId+"_max_consecutive_pump_count") as HTMLInputElement; | ||||
|       this.maxConsecutivePumpCount.onchange = function(){ | ||||
|         controller.configChanged() | ||||
|       } | ||||
|         this.pumpCooldown = document.getElementById("plant_" + plantId + "_pump_cooldown_min") as HTMLInputElement; | ||||
|         this.pumpCooldown.onchange = function () { | ||||
|             controller.configChanged() | ||||
|         } | ||||
|  | ||||
|         this.moistureSensorMinFrequency = document.getElementById("plant_"+plantId+"_min_frequency") as HTMLInputElement; | ||||
|         this.moistureSensorMinFrequency.onchange = function(){ | ||||
|         this.pumpHourStart = document.getElementById("plant_" + plantId + "_pump_hour_start") as HTMLSelectElement; | ||||
|         this.pumpHourStart.onchange = function () { | ||||
|             controller.configChanged() | ||||
|         } | ||||
|         for (let i = 0; i < 24; i++) { | ||||
|             let option = document.createElement("option"); | ||||
|             if (i == 10) { | ||||
|                 option.selected = true | ||||
|             } | ||||
|             option.innerText = i.toString(); | ||||
|             this.pumpHourStart.appendChild(option); | ||||
|         } | ||||
|  | ||||
|         this.pumpHourEnd = document.getElementById("plant_" + plantId + "_pump_hour_end") as HTMLSelectElement; | ||||
|         this.pumpHourEnd.onchange = function () { | ||||
|             controller.configChanged() | ||||
|         } | ||||
|         for (let i = 0; i < 24; i++) { | ||||
|             let option = document.createElement("option"); | ||||
|             if (i == 19) { | ||||
|                 option.selected = true | ||||
|             } | ||||
|             option.innerText = i.toString(); | ||||
|             this.pumpHourEnd.appendChild(option); | ||||
|         } | ||||
|  | ||||
|         this.sensorAInstalled = document.getElementById("plant_" + plantId + "_sensor_a") as HTMLInputElement; | ||||
|         this.sensorAInstalled.onchange = function () { | ||||
|             controller.configChanged() | ||||
|         } | ||||
|  | ||||
|         this.sensorBInstalled = document.getElementById("plant_" + plantId + "_sensor_b") as HTMLInputElement; | ||||
|         this.sensorBInstalled.onchange = function () { | ||||
|             controller.configChanged() | ||||
|         } | ||||
|  | ||||
|         this.minPumpCurrentMa = document.getElementById("plant_" + plantId + "_min_pump_current_ma") as HTMLInputElement; | ||||
|         this.minPumpCurrentMa.onchange = function () { | ||||
|             controller.configChanged() | ||||
|         } | ||||
|  | ||||
|         this.maxPumpCurrentMa = document.getElementById("plant_" + plantId + "_max_pump_current_ma") as HTMLInputElement; | ||||
|         this.maxPumpCurrentMa.onchange = function () { | ||||
|             controller.configChanged() | ||||
|         } | ||||
|  | ||||
|         this.ignoreCurrentError = document.getElementById("plant_" + plantId + "_ignore_current_error") as HTMLInputElement; | ||||
|         this.ignoreCurrentError.onchange = function () { | ||||
|             controller.configChanged() | ||||
|         } | ||||
|  | ||||
|  | ||||
|         this.maxConsecutivePumpCount = document.getElementById("plant_" + plantId + "_max_consecutive_pump_count") as HTMLInputElement; | ||||
|         this.maxConsecutivePumpCount.onchange = function () { | ||||
|             controller.configChanged() | ||||
|         } | ||||
|  | ||||
|         this.moistureSensorMinFrequency = document.getElementById("plant_" + plantId + "_min_frequency") as HTMLInputElement; | ||||
|         this.moistureSensorMinFrequency.onchange = function () { | ||||
|             controller.configChanged() | ||||
|         } | ||||
|         this.moistureSensorMinFrequency.onchange = () => { | ||||
|             controller.configChanged(); | ||||
|         }; | ||||
|  | ||||
|         this.moistureSensorMaxFrequency = document.getElementById("plant_"+plantId+"_max_frequency") as HTMLInputElement; | ||||
|         this.moistureSensorMaxFrequency = document.getElementById("plant_" + plantId + "_max_frequency") as HTMLInputElement; | ||||
|         this.moistureSensorMaxFrequency.onchange = () => { | ||||
|             controller.configChanged(); | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     update(a: string, b: string) { | ||||
|     setTestResult(result: PumpTestResult) { | ||||
|         this.pump_current_result.innerText = "Did abort " + result.error + " median current " + result.median_current_ma + " max current " + result.max_current_ma + " min current " + result.min_current_ma | ||||
|     } | ||||
|  | ||||
|     setMeasurementResult(a: string, b: string) { | ||||
|         this.moistureA.innerText = a | ||||
|         this.moistureB.innerText = b | ||||
|     } | ||||
|    | ||||
|  | ||||
|     setConfig(plantConfig: PlantConfig) { | ||||
|         this.mode.value = plantConfig.mode; | ||||
|         this.targetMoisture.value = plantConfig.target_moisture.toString(); | ||||
| @@ -173,6 +205,9 @@ export class PlantView { | ||||
|         this.sensorBInstalled.checked = plantConfig.sensor_b; | ||||
|         this.sensorAInstalled.checked = plantConfig.sensor_a; | ||||
|         this.maxConsecutivePumpCount.value = plantConfig.max_consecutive_pump_count.toString(); | ||||
|         this.minPumpCurrentMa.value = plantConfig.min_pump_current_ma.toString(); | ||||
|         this.maxPumpCurrentMa.value = plantConfig.max_pump_current_ma.toString(); | ||||
|         this.ignoreCurrentError.checked = plantConfig.ignore_current_error; | ||||
|  | ||||
|         // Set new fields | ||||
|         this.moistureSensorMinFrequency.value = | ||||
| @@ -193,7 +228,10 @@ export class PlantView { | ||||
|             sensor_a: this.sensorAInstalled.checked, | ||||
|             max_consecutive_pump_count: this.maxConsecutivePumpCount.valueAsNumber, | ||||
|             moisture_sensor_min_frequency: this.moistureSensorMinFrequency.valueAsNumber || null, | ||||
|             moisture_sensor_max_frequency: this.moistureSensorMaxFrequency.valueAsNumber || null | ||||
|             moisture_sensor_max_frequency: this.moistureSensorMaxFrequency.valueAsNumber || null, | ||||
|             min_pump_current_ma: this.minPumpCurrentMa.valueAsNumber, | ||||
|             max_pump_current_ma: this.maxPumpCurrentMa.valueAsNumber, | ||||
|             ignore_current_error: this.ignoreCurrentError.checked, | ||||
|         }; | ||||
|     } | ||||
|   } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user