Compare commits

..

No commits in common. "0f77ac163aef80b1af7daecf17cf81d484bc6748" and "58b63fc8ee9030beccbb16d65d7918023c77707d" have entirely different histories.

24 changed files with 14075 additions and 16949 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"board": {
"active_layer": 0,
"active_layer": 1,
"active_layer_preset": "",
"auto_track_width": false,
"hidden_netclasses": [],
@ -68,7 +68,7 @@
39,
40
],
"visible_layers": "ffc35ba_ffffffff",
"visible_layers": "ffc7fff_ffffffff",
"zone_display_mode": 1
},
"git": {

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,11 @@ use chrono_tz::{Europe::Berlin, Tz};
use config::Mode;
use esp_idf_hal::delay::Delay;
use esp_idf_sys::{
esp_deep_sleep, esp_ota_get_app_partition_count, esp_ota_get_running_partition, esp_ota_get_state_partition, esp_ota_img_states_t, esp_ota_img_states_t_ESP_OTA_IMG_ABORTED, esp_ota_img_states_t_ESP_OTA_IMG_INVALID, esp_ota_img_states_t_ESP_OTA_IMG_NEW, esp_ota_img_states_t_ESP_OTA_IMG_PENDING_VERIFY, esp_ota_img_states_t_ESP_OTA_IMG_UNDEFINED, esp_ota_img_states_t_ESP_OTA_IMG_VALID, esp_restart, esp_sleep_enable_ext1_wakeup, esp_sleep_ext1_wakeup_mode_t_ESP_EXT1_WAKEUP_ANY_LOW, vTaskDelay, CONFIG_FREERTOS_HZ
esp_deep_sleep, esp_ota_get_app_partition_count, esp_ota_get_running_partition,
esp_ota_get_state_partition, esp_ota_img_states_t, esp_ota_img_states_t_ESP_OTA_IMG_ABORTED,
esp_ota_img_states_t_ESP_OTA_IMG_INVALID, esp_ota_img_states_t_ESP_OTA_IMG_NEW,
esp_ota_img_states_t_ESP_OTA_IMG_PENDING_VERIFY, esp_ota_img_states_t_ESP_OTA_IMG_UNDEFINED,
esp_ota_img_states_t_ESP_OTA_IMG_VALID, esp_restart, vTaskDelay, CONFIG_FREERTOS_HZ,
};
use log::error;
use once_cell::sync::Lazy;
@ -122,6 +126,16 @@ struct PlantStateMQTT<'a> {
last_pump: &'a str,
next_pump: &'a str,
}
#[derive(Serialize)]
struct BatteryState<'a> {
voltage_milli_volt: &'a str,
current_milli_ampere: &'a str,
cycle_count: &'a str,
design_milli_ampere: &'a str,
remaining_milli_ampere: &'a str,
state_of_charge: &'a str,
state_of_health: &'a str,
}
fn safe_main() -> anyhow::Result<()> {
// It is necessary to call this function once. Otherwise some patches to the runtime
@ -141,16 +155,15 @@ fn safe_main() -> anyhow::Result<()> {
log::info!("Startup Rust");
let mut to_config = false;
let version = get_version();
let git_hash = env!("VERGEN_GIT_DESCRIBE");
let build_timestamp = env!("VERGEN_BUILD_TIMESTAMP");
println!(
"Version useing git has {} build on {}",
version.git_hash, version.build_time
git_hash, build_timestamp
);
let count = unsafe { esp_ota_get_app_partition_count() };
println!("Partition count is {}", count);
println!("Partit ion count is {}", count);
let mut ota_state: esp_ota_img_states_t = 0;
let running_partition = unsafe { esp_ota_get_running_partition() };
let address = unsafe { (*running_partition).address };
@ -179,7 +192,6 @@ fn safe_main() -> anyhow::Result<()> {
println!("Board hal init");
let mut board: std::sync::MutexGuard<'_, PlantCtrlBoard<'_>> = BOARD_ACCESS.lock().unwrap();
board.general_fault(false);
println!("Mounting filesystem");
board.mount_file_system()?;
let free_space = board.file_system_size()?;
@ -220,18 +232,8 @@ fn safe_main() -> anyhow::Result<()> {
println!("cur is {}", cur);
if board.get_restart_to_conf() {
println!("config mode software override");
for _i in 0..2 {
board.general_fault(true);
Delay::new_default().delay_ms(100);
board.general_fault(false);
Delay::new_default().delay_ms(100);
}
to_config = true;
board.general_fault(true);
board.set_restart_to_conf(false);
} else if board.is_mode_override() {
let mut to_config = false;
if board.is_mode_override() {
board.general_fault(true);
println!("config mode override is pressed, waiting 5s");
for _i in 0..5 {
@ -242,15 +244,11 @@ fn safe_main() -> anyhow::Result<()> {
}
if board.is_mode_override() {
board.general_fault(true);
to_config = true;
} else {
board.general_fault(false);
}
} else {
println!("No config override start detected");
}
let config: PlantControllerConfig;
match board.get_config() {
@ -295,7 +293,7 @@ fn safe_main() -> anyhow::Result<()> {
board.general_fault(true);
}
}
if config.network.mqtt_url.is_some() {
if (config.network.mqtt_url.is_some()) {
match board.mqtt(&config) {
Ok(_) => {
println!("Mqtt connection ready");
@ -340,12 +338,8 @@ fn safe_main() -> anyhow::Result<()> {
if mqtt {
let ip_string = ip_address.unwrap_or("N/A".to_owned());
let _ = board.mqtt_publish(&config, "/firmware/address", ip_string.as_bytes());
let _ = board.mqtt_publish(&config, "/firmware/githash", version.git_hash.as_bytes());
let _ = board.mqtt_publish(
&config,
"/firmware/buildtime",
version.build_time.as_bytes(),
);
let _ = board.mqtt_publish(&config, "/firmware/githash", git_hash.as_bytes());
let _ = board.mqtt_publish(&config, "/firmware/buildtime", build_timestamp.as_bytes());
let _ = board.mqtt_publish(
&config,
"/firmware/last_online",
@ -431,7 +425,6 @@ fn safe_main() -> anyhow::Result<()> {
);
let stay_alive_mqtt = STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed);
let stay_alive = stay_alive_mqtt;
println!("Check stay alive, current state is {}", stay_alive);
@ -557,17 +550,23 @@ fn safe_main() -> anyhow::Result<()> {
let _webserver = httpd(reboot_now.clone());
wait_infinity(WaitType::MqttConfig, reboot_now.clone());
}
unsafe {
esp_sleep_enable_ext1_wakeup(0b10u64, esp_sleep_ext1_wakeup_mode_t_ESP_EXT1_WAKEUP_ANY_LOW);
esp_deep_sleep(1000 * 1000 * 60 * deep_sleep_duration_minutes as u64)
};
unsafe { esp_deep_sleep(1000 * 1000 * 60 * deep_sleep_duration_minutes as u64) };
}
fn publish_battery_state(
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
config: &PlantControllerConfig,
) {
let bat = board.get_battery_state();
let bat = BatteryState {
voltage_milli_volt: &to_string(&board.voltage_milli_volt()),
current_milli_ampere: &to_string(&board.average_current_milli_ampere()),
cycle_count: &to_string(&board.cycle_count()),
design_milli_ampere: &to_string(&board.design_milli_ampere_hour()),
remaining_milli_ampere: &to_string(&board.remaining_milli_ampere_hour()),
state_of_charge: &to_string(&board.state_charge_percent()),
state_of_health: &to_string(&board.state_health_percent()),
};
match serde_json::to_string(&bat) {
Ok(state) => {
let _ = board.mqtt_publish(&config, "/battery", state.as_bytes());
@ -1006,7 +1005,7 @@ fn sensor_to_string(value: &Option<u8>, error: &Option<SensorError>, enabled: bo
};
}
fn to_string<T: Display>(value: Result<T>) -> String {
fn to_string<T: Display>(value: &Result<T>) -> String {
return match value {
Ok(v) => v.to_string(),
Err(err) => {
@ -1055,19 +1054,3 @@ fn in_time_range(cur: &DateTime<Tz>, start: u8, end: u8) -> bool {
return curhour > start || curhour < end;
}
}
fn get_version() -> VersionInfo {
let branch = env!("VERGEN_GIT_BRANCH").to_owned();
let hash = &env!("VERGEN_GIT_SHA")[0..8];
return VersionInfo {
git_hash: (branch + "@" + hash),
build_time: env!("VERGEN_BUILD_TIMESTAMP").to_owned(),
};
}
#[derive(Serialize, Debug)]
struct VersionInfo {
git_hash: String,
build_time: String,
}

View File

@ -53,7 +53,7 @@ use esp_idf_sys::{esp, esp_spiffs_check, gpio_hold_dis, gpio_hold_en, vTaskDelay
use one_wire_bus::OneWire;
use crate::config::{self, PlantControllerConfig};
use crate::{plant_hal, to_string, STAY_ALIVE};
use crate::{plant_hal, STAY_ALIVE};
//Only support for 8 right now!
pub const PLANT_COUNT: usize = 8;
@ -117,9 +117,6 @@ static mut CONSECUTIVE_WATERING_PLANT: [u32; PLANT_COUNT] = [0; PLANT_COUNT];
#[link_section = ".rtc.data"]
static mut LOW_VOLTAGE_DETECTED: bool = false;
#[link_section = ".rtc.data"]
static mut RESTART_TO_CONF: bool = false;
pub struct FileSystemSizeInfo {
pub total_size: usize,
pub used_size: usize,
@ -183,33 +180,7 @@ pub struct FileList {
iter_error: Option<String>,
}
#[derive(Serialize)]
pub struct BatteryState {
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,
}
impl PlantCtrlBoard<'_> {
pub fn get_battery_state(&mut self) -> BatteryState {
let bat = BatteryState {
voltage_milli_volt: to_string(self.voltage_milli_volt()),
current_milli_ampere: to_string(self.average_current_milli_ampere()),
cycle_count: to_string(self.cycle_count()),
design_milli_ampere: to_string(self.design_milli_ampere_hour()),
remaining_milli_ampere: to_string(self.remaining_milli_ampere_hour()),
state_of_charge: to_string(self.state_charge_percent()),
state_of_health: to_string(self.state_health_percent()),
temperature: to_string(self.bat_temperature()),
};
return bat;
}
pub fn list_files(&self, filename: &str) -> FileList {
let storage = CString::new(SPIFFS_PARTITION_NAME).unwrap();
let error = unsafe {
@ -985,16 +956,6 @@ impl PlantCtrlBoard<'_> {
};
}
pub fn get_restart_to_conf(&mut self) -> bool {
return unsafe { RESTART_TO_CONF };
}
pub fn set_restart_to_conf(&mut self, to_conf: bool) {
unsafe {
RESTART_TO_CONF = to_conf;
}
}
pub fn state_charge_percent(&mut self) -> Result<u8> {
match self.battery_driver.state_of_charge() {
OkStd(r) => Ok(r),
@ -1051,13 +1012,6 @@ impl PlantCtrlBoard<'_> {
}
}
pub fn bat_temperature(&mut self) -> Result<u16> {
match self.battery_driver.temperature() {
OkStd(r) => Ok(r as u16),
Err(err) => bail!("Error reading Temperature {:?}", err),
}
}
pub fn flash_bq34_z100(&mut self, line: &str, dryrun: bool) -> Result<()> {
match self.battery_driver.write_flash_stream_i2c(line, dryrun) {
OkStd(r) => Ok(r),
@ -1239,60 +1193,32 @@ impl PlantHal {
ms4.set_high()?;
//init,reset rtc memory depending on cause
let mut init_rtc_store: bool = false;
let mut to_config_mode: bool = false;
let reasons = ResetReason::get();
match reasons {
ResetReason::Software => {},
ResetReason::ExternalPin => {},
ResetReason::Watchdog => {
init_rtc_store = true;
},
ResetReason::Sdio => {
init_rtc_store = true
},
ResetReason::Panic => {
init_rtc_store = true
},
ResetReason::InterruptWatchdog => {
init_rtc_store = true
},
ResetReason::PowerOn => {
init_rtc_store = true
},
ResetReason::Unknown => {
init_rtc_store = true
},
ResetReason::Brownout => {
init_rtc_store = true
},
ResetReason::TaskWatchdog => {
init_rtc_store = true
},
ResetReason::DeepSleep => {},
ResetReason::USBPeripheral => {
init_rtc_store = true;
to_config_mode = true;
},
ResetReason::JTAG => {
init_rtc_store = true
},
let reset_store = match reasons {
ResetReason::Software => false,
ResetReason::ExternalPin => false,
ResetReason::Watchdog => true,
ResetReason::Sdio => true,
ResetReason::Panic => true,
ResetReason::InterruptWatchdog => true,
ResetReason::PowerOn => true,
ResetReason::Unknown => true,
ResetReason::Brownout => true,
ResetReason::TaskWatchdog => true,
ResetReason::DeepSleep => false,
ResetReason::USBPeripheral => true,
ResetReason::JTAG => true,
};
println!("Reset due to {:?} requires rtc clear {} and force config mode {}", reasons, init_rtc_store, to_config_mode);
if init_rtc_store {
if reset_store {
println!("Clear and reinit RTC store");
unsafe {
LAST_WATERING_TIMESTAMP = [0; PLANT_COUNT];
CONSECUTIVE_WATERING_PLANT = [0; PLANT_COUNT];
LOW_VOLTAGE_DETECTED = false;
RESTART_TO_CONF = to_config_mode;
};
} else {
println!("Keeping RTC store");
unsafe {
if to_config_mode{
RESTART_TO_CONF = true;
}
println!("Current restart to conf mode{:?}", RESTART_TO_CONF);
println!(
"Current low voltage detection is {:?}",
LOW_VOLTAGE_DETECTED

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,12 @@
//offer ota and config mode
use std::{
io::{BufRead, BufReader, Read, Write},
str::from_utf8,
sync::{atomic::AtomicBool, Arc},
};
use esp_idf_svc::io::BufRead;
use crate::{
espota::OtaUpdate, get_version, map_range_moisture, plant_hal::FileInfo, BOARD_ACCESS,
};
use crate::{espota::OtaUpdate, map_range_moisture, plant_hal::FileInfo, BOARD_ACCESS};
use anyhow::bail;
use chrono::DateTime;
use core::result::Result::Ok;
@ -31,6 +29,12 @@ struct FileList {
file: Vec<FileInfo>,
}
#[derive(Serialize, Debug)]
struct VersionInfo<'a> {
git_hash: &'a str,
build_time: &'a str,
}
#[derive(Serialize, Debug)]
struct LoadData<'a> {
rtc: &'a str,
@ -154,19 +158,17 @@ fn set_config(
anyhow::Ok(Some("saved".to_owned()))
}
fn get_battery_state(
fn get_version(
_request: &mut Request<&mut EspHttpConnection>,
) -> Result<Option<std::string::String>, anyhow::Error> {
let mut board = BOARD_ACCESS.lock().unwrap();
let battery_state = board.get_battery_state();
let battery_json = serde_json::to_string(&battery_state)?;
anyhow::Ok(Some(battery_json))
}
fn get_version_web(
_request: &mut Request<&mut EspHttpConnection>,
) -> Result<Option<std::string::String>, anyhow::Error> {
anyhow::Ok(Some(serde_json::to_string(&get_version())?))
let branch = env!("VERGEN_GIT_BRANCH").to_owned();
let hash = &env!("VERGEN_GIT_SHA")[0..8];
let version_info: VersionInfo<'_> = VersionInfo {
git_hash: &(branch + "@" + hash),
build_time: env!("VERGEN_BUILD_TIMESTAMP"),
};
anyhow::Ok(Some(serde_json::to_string(&version_info)?))
}
fn pump_test(
@ -242,7 +244,7 @@ fn flash_bq(filename: &str, dryrun: bool) -> anyhow::Result<()> {
let file_handle = board.get_file_handle(filename, false)?;
let mut reader = std::io::BufRead::lines(std::io::BufReader::with_capacity(512, file_handle));
let mut reader = BufReader::with_capacity(512, file_handle).lines();
let mut line = 0;
loop {
board.general_fault(toggle);
@ -294,14 +296,10 @@ pub fn httpd(_reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
Box::new(EspHttpServer::new(&server_config).unwrap());
server
.fn_handler("/version", Method::Get, |request| {
handle_error_to500(request, get_version_web)
})
.unwrap();
server
.fn_handler("/battery", Method::Get, |request| {
handle_error_to500(request, get_battery_state)
handle_error_to500(request, get_version)
})
.unwrap();
server
.fn_handler("/time", Method::Get, |request| {
handle_error_to500(request, get_data)
@ -373,7 +371,7 @@ pub fn httpd(_reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
let mut total_read: usize = 0;
loop {
let read = std::io::Read::read(&mut file_handle, &mut buffer)?;
let read = file_handle.read(&mut buffer)?;
total_read += read;
println!(
"sending {read} bytes of {total_read} for file {}",
@ -416,7 +414,7 @@ pub fn httpd(_reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
total_read += read;
println!("sending {read} bytes of {total_read} for upload {filename}");
let to_write = &buffer[0..read];
std::io::Write::write(&mut file_handle, to_write)?;
file_handle.write(to_write)?;
println!("wrote {read} bytes of {total_read} for upload {filename}");
if read == 0 {
break;
@ -507,16 +505,13 @@ pub fn httpd(_reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
})
.unwrap();
server
.fn_handler("/bootstrap-grid.css", Method::Get, |request| {
let headers = [
("Access-Control-Allow-Origin", "*"),
("Access-Control-Allow-Headers", "*"),
("Content-Type", "text/css")
];
request.into_response(200, None, &headers)?.write(include_bytes!("bootstrap-grid.css"))?;
anyhow::Ok(())
})
.unwrap();
.fn_handler("/bootstrap-grid.css", Method::Get, |request| {
request
.into_ok_response()?
.write(include_bytes!("bootstrap-grid.css"))?;
anyhow::Ok(())
})
.unwrap();
server
}
@ -527,7 +522,7 @@ fn cors_response(
) -> Result<(), anyhow::Error> {
let headers = [
("Access-Control-Allow-Origin", "*"),
("Access-Control-Allow-Headers", "*"),
("Access-Control-Allow-Headers", "*")
];
let mut response = request.into_response(status, None, &headers)?;
response.write(body.as_bytes())?;

View File

@ -5,10 +5,11 @@
"packages": {
"": {
"dependencies": {
"copy-webpack-plugin": "^12.0.2",
"source-map-loader": "^4.0.1"
},
"devDependencies": {
"copy-webpack-plugin": "^12.0.2",
"css-loader": "^7.1.2",
"html-webpack-harddisk-plugin": "^2.0.0",
"html-webpack-plugin": "^5.6.3",
"raw-loader": "^4.0.2",
@ -154,6 +155,7 @@
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@nodelib/fs.stat": "2.0.5",
@ -167,6 +169,7 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 8"
@ -176,6 +179,7 @@
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@nodelib/fs.scandir": "2.1.5",
@ -189,6 +193,7 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz",
"integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
@ -693,6 +698,7 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ajv": "^8.0.0"
@ -710,6 +716,7 @@
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true,
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
@ -726,6 +733,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true,
"license": "MIT"
},
"node_modules/ajv-keywords": {
@ -886,6 +894,7 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dev": true,
"dependencies": {
"fill-range": "^7.0.1"
},
@ -1213,6 +1222,7 @@
"version": "12.0.2",
"resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz",
"integrity": "sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA==",
"dev": true,
"license": "MIT",
"dependencies": {
"fast-glob": "^3.3.2",
@ -1237,6 +1247,7 @@
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true,
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
@ -1253,6 +1264,7 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
"integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
"dev": true,
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3"
@ -1265,6 +1277,7 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
"dev": true,
"license": "ISC",
"dependencies": {
"is-glob": "^4.0.3"
@ -1277,12 +1290,14 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true,
"license": "MIT"
},
"node_modules/copy-webpack-plugin/node_modules/schema-utils": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz",
"integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==",
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz",
"integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/json-schema": "^7.0.9",
@ -1291,7 +1306,7 @@
"ajv-keywords": "^5.1.0"
},
"engines": {
"node": ">= 10.13.0"
"node": ">= 12.13.0"
},
"funding": {
"type": "opencollective",
@ -1319,6 +1334,42 @@
"node": ">= 8"
}
},
"node_modules/css-loader": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz",
"integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==",
"dev": true,
"license": "MIT",
"dependencies": {
"icss-utils": "^5.1.0",
"postcss": "^8.4.33",
"postcss-modules-extract-imports": "^3.1.0",
"postcss-modules-local-by-default": "^4.0.5",
"postcss-modules-scope": "^3.2.0",
"postcss-modules-values": "^4.0.0",
"postcss-value-parser": "^4.2.0",
"semver": "^7.5.4"
},
"engines": {
"node": ">= 18.12.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
},
"peerDependencies": {
"@rspack/core": "0.x || 1.x",
"webpack": "^5.27.0"
},
"peerDependenciesMeta": {
"@rspack/core": {
"optional": true
},
"webpack": {
"optional": true
}
}
},
"node_modules/css-select": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
@ -1349,6 +1400,19 @@
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
"dev": true,
"license": "MIT",
"bin": {
"cssesc": "bin/cssesc"
},
"engines": {
"node": ">=4"
}
},
"node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@ -1769,6 +1833,7 @@
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
"integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
"dev": true,
"license": "MIT",
"dependencies": {
"@nodelib/fs.stat": "^2.0.2",
@ -1790,6 +1855,7 @@
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz",
"integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==",
"dev": true,
"license": "BSD-3-Clause"
},
"node_modules/fastest-levenshtein": {
@ -1805,6 +1871,7 @@
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
"integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
"dev": true,
"license": "ISC",
"dependencies": {
"reusify": "^1.0.4"
@ -1827,6 +1894,7 @@
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dev": true,
"dependencies": {
"to-regex-range": "^5.0.1"
},
@ -1949,6 +2017,7 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"license": "ISC",
"dependencies": {
"is-glob": "^4.0.1"
@ -1967,6 +2036,7 @@
"version": "14.0.2",
"resolved": "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz",
"integrity": "sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@sindresorhus/merge-streams": "^2.1.0",
@ -2334,10 +2404,24 @@
"node": ">=0.10.0"
}
},
"node_modules/icss-utils": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
"integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
"dev": true,
"license": "ISC",
"engines": {
"node": "^10 || ^12 || >= 14"
},
"peerDependencies": {
"postcss": "^8.1.0"
}
},
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4"
@ -2433,6 +2517,7 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@ -2442,6 +2527,7 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-extglob": "^2.1.1"
@ -2486,6 +2572,7 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"engines": {
"node": ">=0.12.0"
}
@ -2727,6 +2814,7 @@
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 8"
@ -2746,6 +2834,7 @@
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
"dev": true,
"dependencies": {
"braces": "^3.0.2",
"picomatch": "^2.3.1"
@ -2814,6 +2903,25 @@
"multicast-dns": "cli.js"
}
},
"node_modules/nanoid": {
"version": "3.3.8",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/negotiator": {
"version": "0.6.4",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz",
@ -2860,6 +2968,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@ -3061,6 +3170,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz",
"integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
@ -3079,6 +3189,7 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"engines": {
"node": ">=8.6"
},
@ -3098,6 +3209,119 @@
"node": ">=8"
}
},
"node_modules/postcss": {
"version": "8.4.49",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
"integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.7",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/postcss-modules-extract-imports": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz",
"integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==",
"dev": true,
"license": "ISC",
"engines": {
"node": "^10 || ^12 || >= 14"
},
"peerDependencies": {
"postcss": "^8.1.0"
}
},
"node_modules/postcss-modules-local-by-default": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz",
"integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==",
"dev": true,
"license": "MIT",
"dependencies": {
"icss-utils": "^5.0.0",
"postcss-selector-parser": "^7.0.0",
"postcss-value-parser": "^4.1.0"
},
"engines": {
"node": "^10 || ^12 || >= 14"
},
"peerDependencies": {
"postcss": "^8.1.0"
}
},
"node_modules/postcss-modules-scope": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz",
"integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==",
"dev": true,
"license": "ISC",
"dependencies": {
"postcss-selector-parser": "^7.0.0"
},
"engines": {
"node": "^10 || ^12 || >= 14"
},
"peerDependencies": {
"postcss": "^8.1.0"
}
},
"node_modules/postcss-modules-values": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz",
"integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==",
"dev": true,
"license": "ISC",
"dependencies": {
"icss-utils": "^5.0.0"
},
"engines": {
"node": "^10 || ^12 || >= 14"
},
"peerDependencies": {
"postcss": "^8.1.0"
}
},
"node_modules/postcss-selector-parser": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz",
"integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
},
"engines": {
"node": ">=4"
}
},
"node_modules/postcss-value-parser": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"dev": true,
"license": "MIT"
},
"node_modules/pretty-error": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz",
@ -3168,6 +3392,7 @@
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
"dev": true,
"funding": [
{
"type": "github",
@ -3320,6 +3545,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@ -3384,6 +3610,7 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
"dev": true,
"license": "MIT",
"engines": {
"iojs": ">=1.0.0",
@ -3407,6 +3634,7 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
"dev": true,
"funding": [
{
"type": "github",
@ -3731,6 +3959,7 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz",
"integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=14.16"
@ -4033,6 +4262,7 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"dependencies": {
"is-number": "^7.0.0"
},
@ -4131,6 +4361,7 @@
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz",
"integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"

View File

@ -10,7 +10,6 @@
"webpack-dev-server": "^5.1.0"
},
"dependencies": {
"copy-webpack-plugin": "^12.0.2",
"source-map-loader": "^4.0.1"
}
}

View File

@ -65,15 +65,4 @@ interface Moistures {
interface VersionInfo {
git_hash: string,
build_time: string
}
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
}

View File

@ -1,42 +0,0 @@
<div class="row">
<div class="col-12" style="text-align: center; font-weight: bold;">
Battery:
</div>
<div style="display: block; right: 8px; position: absolute;">
<input id="battery_auto_refresh" type="checkbox">
</div>
</div>
<div class="row">
<span class="col-7">V:</span>
<div class="col-5" id="battery_voltage_milli_volt" style="text-wrap: nowrap"></div>
</div>
<div class="row">
<span class="col-7">mA:</span>
<div class="col-5" id="battery_current_milli_ampere" style="text-wrap: nowrap"></div>
</div>
<div class="row">
<span class="col-7">Cycles:</span>
<div class="col-5" id="battery_cycle_count" style="text-wrap: nowrap"></div>
</div>
<div class="row">
<span class="col-7">design mA:</span>
<div class="col-5" id="battery_design_milli_ampere" style="text-wrap: nowrap"></div>
</div>
<div class="row">
<span class="col-7">remaining mA:</span>
<div class="col-5" id="battery_remaining_milli_ampere" style="text-wrap: nowrap"></div>
</div>
<div class="row">
<span class="col-7">charge %:</span>
<div class="col-5" id="battery_state_of_charge" style="text-wrap: nowrap"></div>
</div>
<div class="row">
<span class="col-7">health %:</span>
<div class="col-5" id="battery_state_of_health" style="text-wrap: nowrap"></div>
</div>
<div class="row">
<span class="col-7">Temp °C:</span>
<div class="col-5" id="battery_temperature" style="text-wrap: nowrap"></div>
</div>

View File

@ -1,69 +0,0 @@
import { Controller } from "./main";
export class BatteryView{
voltage_milli_volt: HTMLSpanElement;
current_milli_ampere: HTMLSpanElement;
cycle_count: HTMLSpanElement;
design_milli_ampere: HTMLSpanElement;
remaining_milli_ampere: HTMLSpanElement;
state_of_charge: HTMLSpanElement;
state_of_health: HTMLSpanElement;
temperature: HTMLSpanElement;
auto_refresh: HTMLInputElement;
timer: NodeJS.Timeout | undefined;
controller: Controller;
constructor (controller:Controller) {
(document.getElementById("batteryview") as HTMLElement).innerHTML = require("./batteryview.html")
this.voltage_milli_volt = document.getElementById("battery_voltage_milli_volt") as HTMLSpanElement;
this.current_milli_ampere = document.getElementById("battery_current_milli_ampere") as HTMLSpanElement;
this.cycle_count = document.getElementById("battery_cycle_count") as HTMLSpanElement;
this.design_milli_ampere = document.getElementById("battery_design_milli_ampere") as HTMLSpanElement;
this.remaining_milli_ampere = document.getElementById("battery_remaining_milli_ampere") as HTMLSpanElement;
this.state_of_charge = document.getElementById("battery_state_of_charge") as HTMLSpanElement;
this.state_of_health = document.getElementById("battery_state_of_health") as HTMLSpanElement;
this.temperature = document.getElementById("battery_temperature") as HTMLSpanElement;
this.auto_refresh = document.getElementById("battery_auto_refresh") as HTMLInputElement;
this.controller = controller
this.auto_refresh.onchange = () => {
if(this.timer){
clearTimeout(this.timer)
}
if(this.auto_refresh.checked){
controller.updateBatteryData()
}
}
}
update(batterystate: BatteryState|null){
if (batterystate == null) {
this.voltage_milli_volt.innerText = "N/A"
this.current_milli_ampere.innerText = "N/A"
this.cycle_count.innerText = "N/A"
this.design_milli_ampere.innerText = "N/A"
this.remaining_milli_ampere.innerText = "N/A"
this.state_of_charge.innerText = "N/A"
this.state_of_health.innerText = "N/A"
this.temperature.innerText = "N/A"
} else {
this.voltage_milli_volt.innerText = String(+batterystate.voltage_milli_volt/1000)
this.current_milli_ampere.innerText = batterystate.current_milli_ampere
this.cycle_count.innerText = batterystate.cycle_count
this.design_milli_ampere.innerText = batterystate.design_milli_ampere
this.remaining_milli_ampere.innerText = batterystate.remaining_milli_ampere
this.state_of_charge.innerText = batterystate.state_of_charge
this.state_of_health.innerText = batterystate.state_of_health
this.temperature.innerText = String(+batterystate.temperature / 100)
}
if(this.auto_refresh.checked){
this.timer = setTimeout(this.controller.updateBatteryData, 1000);
} else {
if(this.timer){
clearTimeout(this.timer)
}
}
}
}

View File

@ -1,127 +1,157 @@
<link rel="stylesheet" href="bootstrap-grid.css">
<style>
.progressPane {
display: block;
position: fixed;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
top: 0;
left: 0;
background-color: lightgrey;
opacity: 0.8;
}
.progressPaneCenter {
display: inline-block;
margin-top: 48%;
position: absolute;
height: 4%;
width: 50%;
margin-left: 25%;
margin-right: 25%;
}
.progress {
height: 1.5em;
width: 100%;
background-color: #c9c9c9;
position: relative;
}
.progress:after {
content: attr(data-label);
font-size: 0.8em;
position: absolute;
text-align: center;
top: 5px;
left: 0;
right: 0;
}
.progress .value {
background-color: #7cc4ff;
display: inline-block;
height: 100%;
}
.progress .valueIndeterminate {
background-color: #7cc4ff;
display: inline-block;
height: 100%;
animation: indeterminateAnimation 1s infinite linear;
transform-origin: 0% 50%;
}
@keyframes indeterminateAnimation {
0% {
transform: translateX(0%) scaleX(0.5);
<div class="container-xxl">
<link rel="stylesheet" href="bootstrap-grid.css">
<style>
.progressPane{
display: block;
position: fixed;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
top: 0;
left: 0;
background-color: lightgrey;
opacity: 0.8;
}
.progressPaneCenter{
display: inline-block;
margin-top: 48%;
position: absolute;
height: 4%;
width: 50%;
margin-left: 25%;
margin-right: 25%;
}
.progress {
height: 1.5em;
width: 100%;
background-color: #c9c9c9;
position: relative;
}
.progress:after {
content: attr(data-label);
font-size: 0.8em;
position: absolute;
text-align: center;
top: 5px;
left: 0;
right: 0;
}
.progress .value {
background-color: #7cc4ff;
display: inline-block;
height: 100%;
}
50% {
transform: translateX(50%) scaleX(0.5);
.progress .valueIndeterminate {
background-color: #7cc4ff;
display: inline-block;
height: 100%;
animation: indeterminateAnimation 1s infinite linear;
transform-origin: 0% 50%;
}
100% {
transform: translateX(0%) scaleX(0.5);
@keyframes indeterminateAnimation {
0% {
transform: translateX(0%) scaleX(0.5);
}
50% {
transform: translateX(50%) scaleX(0.5);
}
100% {
transform: translateX(0%) scaleX(0.5);
}
}
}
</style>
</style>
<div class="container-xl">
<div class="row">
<div id="firmwareview" class="container col-12 col-lg-4"
style="border-width: 1px; border-style: solid; padding: 8px;">
</div>
<div id="timeview" class="container col-12 col-lg-4" style="border-style: solid; border-width: 1px; padding: 8px;">
</div>
<div id="batteryview" class="container col-12 col-lg-4"
style="border-style: solid; border-width: 1px; padding: 8px;">
<div id="progressPane" class="progressPane">
<div class="progressPaneCenter">
<div id="progressPaneBar" class="progress" data-label="50% Complete">
<span id="progressPaneSpan" class="value" style="width:100%;"></span>
</div>
</div>
</div>
<div class="row">
<div id="network_view" class="container col-12 col-sm-6 col-lg-4"
style="border-style: solid; border-width: 1px; padding: 8px;">
</div>
<div id="lightview" class="container col-12 col-sm-6 col-lg-4"
style="border-style: solid; border-width: 1px; padding: 16px;">
</div>
<div id="tankview" class="container col-12 col-lg-4" style="border-style: solid; border-width: 1px; padding: 16px;">
</div>
<input type="button" id="test" value="Test">
<h2>Current Firmware</h2>
<div>
<div id="firmware_buildtime">Buildtime loading</div>
<div id="firmware_githash">Build githash loading</div>
</div>
<h2>firmeware OTA v3</h2>
<form id="upload_form" method="post">
<input type="file" name="file1" id="firmware_file"><br>
<progress id="firmware_progressBar" value="0" max="100" style="width:300px;"></progress>
<h3 id="firmware_status"></h3>
<h3 id="firmware_answer"></h3>
<p id="firmware_loaded_n_total"></p>
</form>
<div id="timeview">
</div>
<div id="network_view">
</div>
<h2>config</h2>
<h3>Plants:</h3>
<button id="measure_moisture">Measure Moisture</button>
<div id="plants" class="row"></div>
<div id="configform">
<h3>Tank:</h3>
<div>
<input type="checkbox" id="tank_sensor_enabled">
Enable Tank Sensor
</div>
<div>
<input type="checkbox" id="tank_allow_pumping_if_sensor_error">
Allow Pumping if Sensor Error
</div>
<div>
<input type="number" min="2" max="500000" id="tank_useable_ml">
Tank Size mL
</div>
<div>
<input type="number" min="1" max="500000" id="tank_warn_percent">
Tank Warn Percent (mapped in relation to empty and full)
</div>
<div>
<input type="number" min="0" max="100" id="tank_empty_percent">
Tank Empty Percent (% max move)
</div>
<div>
<input type="number" min="0" max="100" id="tank_full_percent">
Tank Full Percent (% max move)
</div>
<h3>Light:</h3>
<input type="checkbox" id="night_lamp_enabled" checked="false"> Enable Nightlight
<div>
Start
<select type="time" id="night_lamp_time_start">
</select>
Stop
<select type="time" id="night_lamp_time_end">
</select>
</div>
<div>
<input type="checkbox" id="night_lamp_only_when_dark">
Light only when dark
</div>
<h3>Plants:</h3>
<button id="measure_moisture">Measure Moisture</button>
<div id="plants" class="row"></div>
</div>
<button id="submit">Submit</button>
<div id="submit_status"></div>
<br>
<textarea id="json" cols=50 rows=10></textarea>
<script src="bundle.js"></script>
</div>
<div id="progressPane" class="progressPane">
<div class="progressPaneCenter">
<div id="progressPaneBar" class="progress" data-label="50% Complete">
<span id="progressPaneSpan" class="value" style="width:100%;"></span>
</div>
</div>
</div>

View File

@ -8,12 +8,11 @@ document.body.innerHTML = require('./main.html') as string;
import { TimeView } from "./timeview";
import { PlantView, PlantViews } from "./plant";
import { NetworkConfigView } from "./network";
import { NightLampView } from "./nightlightview";
import { TankConfigView } from "./tankview";
import { NightLampView } from "./nightmode";
import { TankConfigView } from "./tanks";
import { SubmitView } from "./submitView";
import { ProgressView } from "./progress";
import { OTAView } from "./ota";
import { BatteryView } from "./batteryview";
export class Controller {
updateRTCData() {
@ -28,18 +27,6 @@ export class Controller {
console.log(error);
});
}
updateBatteryData() {
fetch(PUBLIC_URL + "/battery")
.then(response => response.json())
.then(json => json as BatteryState)
.then(battery => {
controller.batteryView.update(battery)
})
.catch(error => {
controller.batteryView.update(null)
console.log(error);
});
}
uploadNewFirmware(file: File) {
var current = 0;
var max = 100;
@ -239,13 +226,11 @@ export class Controller {
readonly submitView: SubmitView;
readonly firmWareView : OTAView;
readonly progressview: ProgressView;
readonly batteryView: BatteryView;
constructor() {
this.timeView = new TimeView(this)
this.plantViews = new PlantViews(this)
this.networkView = new NetworkConfigView(this, PUBLIC_URL)
this.tankView = new TankConfigView(this)
this.batteryView = new BatteryView(this)
this.nightLampView = new NightLampView(this)
this.submitView = new SubmitView(this)
this.firmWareView = new OTAView(this)
@ -254,8 +239,6 @@ export class Controller {
}
const controller = new Controller();
controller.updateRTCData();
controller.updateBatteryData();
controller.downloadConfig();
//controller.measure_moisture();
controller.measure_moisture();
controller.version();

View File

@ -1,14 +0,0 @@
<h3>Light:</h3>
<input type="checkbox" id="night_lamp_enabled" checked="false"> Enable Nightlight
<div>
Start
<select type="time" id="night_lamp_time_start">
</select>
Stop
<select type="time" id="night_lamp_time_end">
</select>
</div>
<div>
<input type="checkbox" id="night_lamp_only_when_dark">
Light only when dark
</div>

View File

@ -5,9 +5,6 @@ export class NightLampView {
private readonly night_lamp_time_start: HTMLSelectElement;
private readonly night_lamp_time_end: HTMLSelectElement;
constructor(controller:Controller){
(document.getElementById("lightview") as HTMLElement).innerHTML = require('./nightlightview.html') as string;
this.night_lamp_only_when_dark = document.getElementById("night_lamp_only_when_dark") as HTMLInputElement;
this.night_lamp_only_when_dark.onchange = controller.configChanged
this.night_lamp_time_start = document.getElementById("night_lamp_time_start") as HTMLSelectElement;

View File

@ -1,25 +0,0 @@
<div class="row">
<div class="col-12" style="text-align: center; font-weight: bold;">
Current Firmware
</div>
</div>
<div class="row">
<div class="col-6">
<span>Buildtime:</span>
<span id="firmware_buildtime" style="text-wrap: nowrap"></span>
</div>
</div>
<div class="row">
<div class="col-md-6">
<span>Buildhash:</span>
<span id="firmware_githash" style="text-wrap: nowrap"></span>
</div>
</div>
<div class="row">
<form class="col-12" id="upload_form" method="post">
<input type="file" name="file1" id="firmware_file"><br>
</form>
</div>
<div class="row">
<input style="margin-left: 16px; margin-top: 8px;" class="col-6" type="button" id="test" value="Self-Test">
</div>

View File

@ -6,8 +6,6 @@ export class OTAView {
firmware_githash: HTMLDivElement;
constructor(controller: Controller) {
(document.getElementById("firmwareview") as HTMLElement).innerHTML = require("./ota.html")
this.firmware_buildtime = document.getElementById("firmware_buildtime") as HTMLDivElement;
this.firmware_githash = document.getElementById("firmware_githash") as HTMLDivElement;

View File

@ -1,61 +1,35 @@
<div class="container" style="border-style: solid; border-width: 1px; padding: 8px;">
<span class="row col-12" style="font-weight: bold; display: block; text-align: center;"
id="plant_${plantId}_header">Plant ${plantId}</span>
<h4 id="plant_${plantId}_header">Plant ${plantId}</h4>
<div>
<button id="plant_${plantId}_test">Test</button>
</div>
<div>
Current:
<br>
Sensor A:<span id="plant_${plantId}_moisture_a">loading</span>
<br>
Sensor b:<span id="plant_${plantId}_moisture_b">loading</span>
</div>
Mode: <select id="plant_${plantId}_mode">
<option value="OFF">Off</option>
<option value="TargetMoisture">Target Moisture</option>
<option value="TimerOnly">Timer Only</option>
</select>
<div class="row">
<div class="col-1"></div>
<button class="col-10" id="plant_${plantId}_test">Test</button>
<div class="col-1"></div>
</div>
<div class="row">
<div class="col-12">Live:</div>
</div>
<div class="row">
<div class="col-7">Sensor A:</div>
<span class="col-4" id="plant_${plantId}_moisture_a">loading</span>
<div class="col-7">Sensor B:</div>
<span class="col-4" id="plant_${plantId}_moisture_b">loading</span>
</div>
<div class="row">
<div class="col-7">
Mode:
</div>
<select class="col-4" id="plant_${plantId}_mode">
<option value="OFF">Off</option>
<option value="TargetMoisture">Target</option>
<option value="TimerOnly">Timer</option>
</select>
<div>
Target Moisture: <input id="plant_${plantId}_target_moisture" type="number" min="0" max="100" placeholder="0">
<br>
Pump Time (s): <input id="plant_${plantId}_pump_time_s" type="number" min="0" max="600" placeholder="30">
<br>
Pump Cooldown (m): <input id="plant_${plantId}_pump_cooldown_min" type="number" min="0" max="600" placeholder="30">
<br>
"Pump Hour Start": <select id="plant_${plantId}_pump_hour_start">10</select>
<br>
"Pump Hour End": <select id="plant_${plantId}_pump_hour_end">19</select>
<br>
Sensor B installed: <input id="plant_${plantId}_sensor_b" type="checkbox">
<br>
Max Consecutive Pump Count: <input id="plant_${plantId}_max_consecutive_pump_count" type="number" min="1", max="50", placeholder="10">
</div>
<div class="row">
<div class="col-7">Target Moisture:</div>
<input class="col-4" id="plant_${plantId}_target_moisture" type="number" min="0" max="100" placeholder="0">
</div>
<div class="row">
<div class="col-7">Pump Time (s):</div>
<input class="col-4" id="plant_${plantId}_pump_time_s" type="number" min="0" max="600" placeholder="30">
</div>
<div class="row">
<div class="col-7">Pump Cooldown (m):</div>
<input class="col-4" id="plant_${plantId}_pump_cooldown_min" type="number" min="0" max="600" placeholder="30">
</div>
<div class="row">
<div class="col-7">"Pump Hour Start":</div>
<select class="col-4" id="plant_${plantId}_pump_hour_start">10</select>
</div>
<div class="row">
<div class="col-7">"Pump Hour End":</div>
<select class="col-4" id="plant_${plantId}_pump_hour_end">19</select>
</div>
<div class="row">
<div class="col-7">Sensor B installed:</div>
<input class="col-4" id="plant_${plantId}_sensor_b" type="checkbox">
</div>
<div class="row">
<div class="col-7">Max Consecutive Pump Count:</div>
<input class="col-4" id="plant_${plantId}_max_consecutive_pump_count" type="number" min="1" , max="50" ,
placeholder="10">
</div>
</div>

View File

@ -66,10 +66,7 @@ export class PlantView {
const template = require('./plant.html') as string;
const plantRaw = template.replaceAll("${plantId}", String(plantId));
this.plantDiv.innerHTML = plantRaw
this.plantDiv.classList.add("col-12" )
this.plantDiv.classList.add("col-sm-6" )
this.plantDiv.classList.add("col-md-auto" )
this.plantDiv.classList.add("col-auto" )
parent.appendChild(this.plantDiv)
this.header = document.getElementById("plant_"+plantId+"_header")!
@ -138,6 +135,7 @@ export class PlantView {
this.maxConsecutivePumpCount.onchange = function(){
controller.configChanged()
}
console.log(this)
}
update(a: number, b: number) {

View File

@ -9,9 +9,6 @@ export class TankConfigView {
private readonly tank_allow_pumping_if_sensor_error: HTMLInputElement;
constructor(controller:Controller){
(document.getElementById("tankview") as HTMLElement).innerHTML = require("./tankview.html")
this.tank_useable_ml = document.getElementById("tank_useable_ml") as HTMLInputElement;
this.tank_useable_ml.onchange = controller.configChanged
this.tank_empty_percent = document.getElementById("tank_empty_percent") as HTMLInputElement;

View File

@ -1,29 +0,0 @@
<div class="row">
<div class="col-12" style="font-weight: bold; text-align: center;">Tank:</div>
</div>
<div class="row">
<input class="col-4" type="checkbox" id="tank_sensor_enabled">
<div class="col-7">Enable Tank Sensor</div>
</div>
<div class="row">
<input class="col-4" type="checkbox" id="tank_allow_pumping_if_sensor_error">
<div class="col-7">Ignore Sensor Error</div>
</div>
<div class="row">
<input class="col-4" type="number" min="2" max="500000" id="tank_useable_ml">
<div class="col-7">Tank Size mL</div>
</div>
<div class="row">
<input class="col-4" type="number" min="1" max="500000" id="tank_warn_percent">
<div class="col-7">Tank Warn Percent (mapped in relation to empty and full)</div>
</div>
<div class="row">
<input class="col-4" type="number" min="0" max="100" id="tank_empty_percent">
<div class="col-7">Tank Empty Percent (% max move)</div>
</div>
<div class="row">
<input class="col-4" type="number" min="0" max="100" id="tank_full_percent">
<div class="col-7">Tank Full Percent (% max move)</div>
</div>

View File

@ -1,24 +1,7 @@
<div class="row">
<div class="col-12" style="text-align: center; font-weight: bold;">
Time:
</div>
<div style="display: block; right: 8px; position: absolute;">
<input id="timeview_auto_refresh" type="checkbox">
</div>
</div>
<div class="row">
<span class="col-2">MCU:</span>
<div class="col-9" id="timeview_esp_time" style="text-wrap: nowrap">Esp time</div>
</div>
<div class="row">
<span class="col-2">RTC:</span>
<div class="col-9" id="timeview_rtc_time" style="text-wrap: nowrap">Rtc time</div>
</div>
<div class="row">
<span class="col-2">Local:</span>
<div class="col-9" id="timeview_browser_time" style="text-wrap: nowrap">Local time</div>
</div>
<button id="timeview_time_upload">Store Browser time into esp and rtc</button>
<h2>Time</h2>
AutoRefresh:<input id="timeview_auto_refresh" type="checkbox">
<div id="timeview_esp_time">Esp time</div>
<div id="timeview_rtc_time">Rtc time</div>
<div id="timeview_browser_time">Rtc time</div>
<div></div>
<button id="timeview_time_upload">Store Browser time into esp and rtc</button>

View File

@ -8,7 +8,7 @@ const isDevServer = process.env.WEBPACK_SERVE;
console.log("Dev server is " + isDevServer);
var host;
if (isDevServer){
host = 'http://192.168.0.105';
host = 'http://192.168.1.172';
} else {
host = '';
}