allow parsing config with missing keys, added nightlamp config stuff, added nightlamp testing,
This commit is contained in:
parent
a4fd4acaa3
commit
d11dc523f0
@ -5,8 +5,8 @@ target = "riscv32imac-esp-espidf"
|
||||
[target.riscv32imac-esp-espidf]
|
||||
linker = "ldproxy"
|
||||
#runner = "espflash flash --monitor --baud 921600 --partition-table partitions.csv -b no-reset" # Select this runner in case of usb ttl
|
||||
runner = "espflash flash --monitor --baud 921600 --flash-size 16mb --partition-table partitions.csv"
|
||||
#runner = "cargo runner"
|
||||
#runner = "espflash flash --monitor --baud 921600 --flash-size 16mb --partition-table partitions.csv"
|
||||
runner = "cargo runner"
|
||||
|
||||
|
||||
#runner = "espflash flash --monitor --partition-table partitions.csv -b no-reset" # create upgrade image file for webupload
|
||||
|
@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize};
|
||||
use crate::PLANT_COUNT;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
#[serde(default)]
|
||||
pub struct NetworkConfig {
|
||||
pub ap_ssid: heapless::String<32>,
|
||||
pub ssid: Option<heapless::String<32>>,
|
||||
@ -25,22 +26,30 @@ impl Default for NetworkConfig {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
#[serde(default)]
|
||||
pub struct NightLampConfig {
|
||||
pub enabled: bool,
|
||||
pub night_lamp_hour_start: u8,
|
||||
pub night_lamp_hour_end: u8,
|
||||
pub night_lamp_only_when_dark: bool,
|
||||
pub low_soc_cutoff: u8,
|
||||
pub low_soc_restore: u8
|
||||
}
|
||||
impl Default for NightLampConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: true,
|
||||
night_lamp_hour_start: 19,
|
||||
night_lamp_hour_end: 2,
|
||||
night_lamp_only_when_dark: true,
|
||||
low_soc_cutoff: 30,
|
||||
low_soc_restore: 50
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
#[serde(default)]
|
||||
pub struct TankConfig {
|
||||
pub tank_sensor_enabled: bool,
|
||||
pub tank_allow_pumping_if_sensor_error: bool,
|
||||
@ -63,6 +72,7 @@ impl Default for TankConfig {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)]
|
||||
#[serde(default)]
|
||||
pub struct PlantControllerConfig {
|
||||
pub network: NetworkConfig,
|
||||
pub tank: TankConfig,
|
||||
@ -71,6 +81,7 @@ pub struct PlantControllerConfig {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
#[serde(default)]
|
||||
pub struct PlantConfig {
|
||||
pub mode: Mode,
|
||||
pub target_moisture: u8,
|
||||
|
@ -39,8 +39,8 @@ pub mod plant_hal;
|
||||
|
||||
const TIME_ZONE: Tz = Berlin;
|
||||
|
||||
const MOIST_SENSOR_MAX_FREQUENCY: u32 = 250000; // 60kHz (500Hz margin)
|
||||
const MOIST_SENSOR_MIN_FREQUENCY: u32 = 300; // this is really really dry, think like cactus levels
|
||||
const MOIST_SENSOR_MAX_FREQUENCY: u32 = 5000; // 60kHz (500Hz margin)
|
||||
const MOIST_SENSOR_MIN_FREQUENCY: u32 = 150; // this is really really dry, think like cactus levels
|
||||
|
||||
const FROM: (f32, f32) = (
|
||||
MOIST_SENSOR_MIN_FREQUENCY as f32,
|
||||
@ -76,6 +76,8 @@ impl WaitType {
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Default)]
|
||||
/// Light State tracking data for mqtt
|
||||
struct LightState {
|
||||
/// is enabled in config
|
||||
enabled: bool,
|
||||
/// led is on
|
||||
active: bool,
|
||||
/// led should not be on at this time of day
|
||||
@ -510,28 +512,39 @@ fn safe_main() -> anyhow::Result<()> {
|
||||
}
|
||||
update_plant_state(&mut plantstate, &mut board, &config);
|
||||
|
||||
let is_day = board.is_day();
|
||||
let state_of_charge = board.state_charge_percent().unwrap_or(0);
|
||||
|
||||
let mut light_state = LightState {
|
||||
enabled: config.night_lamp.enabled,
|
||||
..Default::default()
|
||||
};
|
||||
let is_day = board.is_day();
|
||||
light_state.is_day = is_day;
|
||||
light_state.out_of_work_hour = !in_time_range(
|
||||
&timezone_time,
|
||||
config.night_lamp.night_lamp_hour_start,
|
||||
config.night_lamp.night_lamp_hour_end,
|
||||
);
|
||||
|
||||
let state_of_charge = board.state_charge_percent().unwrap_or(0);
|
||||
if state_of_charge < 30 {
|
||||
board.set_low_voltage_in_cycle();
|
||||
} else if state_of_charge > 50 {
|
||||
board.clear_low_voltage_in_cycle();
|
||||
}
|
||||
light_state.battery_low = board.low_voltage_in_cycle();
|
||||
|
||||
if !light_state.out_of_work_hour {
|
||||
if config.night_lamp.night_lamp_only_when_dark {
|
||||
if !light_state.is_day {
|
||||
if light_state.enabled {
|
||||
light_state.is_day = is_day;
|
||||
light_state.out_of_work_hour = !in_time_range(
|
||||
&timezone_time,
|
||||
config.night_lamp.night_lamp_hour_start,
|
||||
config.night_lamp.night_lamp_hour_end,
|
||||
);
|
||||
|
||||
if state_of_charge < config.night_lamp.low_soc_cutoff {
|
||||
board.set_low_voltage_in_cycle();
|
||||
} else if state_of_charge > config.night_lamp.low_soc_restore {
|
||||
board.clear_low_voltage_in_cycle();
|
||||
}
|
||||
light_state.battery_low = board.low_voltage_in_cycle();
|
||||
|
||||
if !light_state.out_of_work_hour {
|
||||
if config.night_lamp.night_lamp_only_when_dark {
|
||||
if !light_state.is_day {
|
||||
if light_state.battery_low {
|
||||
board.light(false).unwrap();
|
||||
} else {
|
||||
light_state.active = true;
|
||||
board.light(true).unwrap();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if light_state.battery_low {
|
||||
board.light(false).unwrap();
|
||||
} else {
|
||||
@ -540,20 +553,13 @@ fn safe_main() -> anyhow::Result<()> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if light_state.battery_low {
|
||||
board.light(false).unwrap();
|
||||
} else {
|
||||
light_state.active = true;
|
||||
board.light(true).unwrap();
|
||||
}
|
||||
light_state.active = false;
|
||||
board.light(false).unwrap();
|
||||
}
|
||||
} else {
|
||||
light_state.active = false;
|
||||
board.light(false).unwrap();
|
||||
|
||||
println!("Lightstate is {:?}", light_state);
|
||||
}
|
||||
|
||||
println!("Lightstate is {:?}", light_state);
|
||||
|
||||
match serde_json::to_string(&light_state) {
|
||||
Ok(state) => {
|
||||
let _ = board.mqtt_publish(&config, "/light", state.as_bytes());
|
||||
|
@ -54,6 +54,11 @@ pub struct WebBackupHeader{
|
||||
size: usize
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct NightLampCommand {
|
||||
active: bool
|
||||
}
|
||||
|
||||
fn write_time(
|
||||
request: &mut Request<&mut EspHttpConnection>,
|
||||
) -> Result<Option<std::string::String>, anyhow::Error> {
|
||||
@ -236,6 +241,16 @@ fn pump_test(
|
||||
anyhow::Ok(None)
|
||||
}
|
||||
|
||||
fn night_lamp_test(
|
||||
request: &mut Request<&mut EspHttpConnection>,
|
||||
) -> Result<Option<std::string::String>, anyhow::Error> {
|
||||
let actual_data = read_up_to_bytes_from_request(request, None)?;
|
||||
let light_command: NightLampCommand = serde_json::from_slice(&actual_data)?;
|
||||
let mut board = BOARD_ACCESS.lock().unwrap();
|
||||
board.light(light_command.active)?;
|
||||
anyhow::Ok(None)
|
||||
}
|
||||
|
||||
fn wifi_scan(
|
||||
_request: &mut Request<&mut EspHttpConnection>,
|
||||
) -> Result<Option<std::string::String>, anyhow::Error> {
|
||||
@ -399,6 +414,11 @@ pub fn httpd(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
||||
handle_error_to500(request, pump_test)
|
||||
})
|
||||
.unwrap();
|
||||
server
|
||||
.fn_handler("/lamptest", Method::Post, |request| {
|
||||
handle_error_to500(request, night_lamp_test)
|
||||
})
|
||||
.unwrap();
|
||||
server
|
||||
.fn_handler("/boardtest", Method::Post, move |_| {
|
||||
BOARD_ACCESS.lock().unwrap().test()
|
||||
|
@ -25,9 +25,16 @@ interface FileInfo{
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
interface NightLampCommand {
|
||||
active: boolean
|
||||
}
|
||||
|
||||
interface TankConfig {
|
||||
|
@ -225,7 +225,16 @@ export class Controller {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
testNightLamp(active: boolean){
|
||||
var body: NightLampCommand = {
|
||||
active: active
|
||||
}
|
||||
var pretty = JSON.stringify(body, undefined, 1);
|
||||
fetch(PUBLIC_URL + "/lamptest", {
|
||||
method: "POST",
|
||||
body: pretty
|
||||
})
|
||||
}
|
||||
|
||||
testPlant(plantId: number) {
|
||||
let counter = 0
|
||||
@ -349,11 +358,19 @@ export class Controller {
|
||||
waitForReboot() {
|
||||
console.log("Check if controller online again")
|
||||
fetch(PUBLIC_URL + "/version", {
|
||||
method: "POST",
|
||||
method: "GET",
|
||||
signal: AbortSignal.timeout(5000)
|
||||
}).then(response => {
|
||||
console.log("Reached controller, reloading")
|
||||
window.location.reload();
|
||||
if (response.status != 200){
|
||||
console.log("Not reached yet, retrying")
|
||||
setTimeout(controller.waitForReboot, 1000)
|
||||
} else {
|
||||
console.log("Reached controller, reloading")
|
||||
controller.progressview.addIndeterminate("rebooting", "Reached Controller, reloading")
|
||||
setTimeout(function(){
|
||||
window.location.reload()
|
||||
}, 2000);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.log("Not reached yet, retrying")
|
||||
|
@ -16,9 +16,13 @@
|
||||
</style>
|
||||
|
||||
<div class="subtitle">Light:</div>
|
||||
<div class="flexcontainer">
|
||||
<div class="lightkey">Test Nightlight</div>
|
||||
<input class="lightcheckbox" type="checkbox" id="night_lamp_test">
|
||||
</div>
|
||||
<div class="flexcontainer" style="text-decoration-line: line-through;">
|
||||
<div class="lightkey">Enable Nightlight</div>
|
||||
<input class="lightcheckbox" type="checkbox" id="night_lamp_enabled" checked="false">
|
||||
<input class="lightcheckbox" type="checkbox" id="night_lamp_enabled">
|
||||
</div>
|
||||
<div class="flexcontainer">
|
||||
<div class="lightkey">Light only when dark</div>
|
||||
@ -33,4 +37,12 @@
|
||||
<div class="lightkey">Stop</div>
|
||||
<select class="lightnumberbox" type="time" id="night_lamp_time_end">
|
||||
</select>
|
||||
</div>
|
||||
<div class="flexcontainer">
|
||||
<div class="lightkey">Disable if Battery below %</div>
|
||||
<input class="lightcheckbox" type="number" id="night_lamp_soc_low" min="0" max="100">
|
||||
</div>
|
||||
<div class="flexcontainer">
|
||||
<div class="lightkey">Reenable if Battery higher %</div>
|
||||
<input class="lightcheckbox" type="number" id="night_lamp_soc_restore" min="0" max="100">
|
||||
</div>
|
@ -4,12 +4,26 @@ export class NightLampView {
|
||||
private readonly night_lamp_only_when_dark: HTMLInputElement;
|
||||
private readonly night_lamp_time_start: HTMLSelectElement;
|
||||
private readonly night_lamp_time_end: HTMLSelectElement;
|
||||
private readonly night_lamp_test: HTMLInputElement;
|
||||
private readonly night_lamp_enabled: HTMLInputElement;
|
||||
private readonly night_lamp_soc_low: HTMLInputElement;
|
||||
private readonly night_lamp_soc_restore: HTMLInputElement;
|
||||
|
||||
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_enabled = document.getElementById("night_lamp_enabled") as HTMLInputElement;
|
||||
this.night_lamp_enabled.onchange = controller.configChanged
|
||||
|
||||
this.night_lamp_soc_low = document.getElementById("night_lamp_soc_low") as HTMLInputElement;
|
||||
this.night_lamp_soc_low.onchange = controller.configChanged
|
||||
|
||||
this.night_lamp_soc_restore = document.getElementById("night_lamp_soc_restore") as HTMLInputElement;
|
||||
this.night_lamp_soc_restore.onchange = controller.configChanged
|
||||
|
||||
this.night_lamp_time_start = document.getElementById("night_lamp_time_start") as HTMLSelectElement;
|
||||
this.night_lamp_time_start.onchange = controller.configChanged
|
||||
for (let i = 0; i < 24; i++) {
|
||||
@ -31,12 +45,21 @@ export class NightLampView {
|
||||
option.innerText = i.toString();
|
||||
this.night_lamp_time_end.appendChild(option);
|
||||
}
|
||||
|
||||
let night_lamp_test = document.getElementById("night_lamp_test") as HTMLInputElement;
|
||||
this.night_lamp_test = night_lamp_test
|
||||
this.night_lamp_test.onchange = () => {
|
||||
controller.testNightLamp(night_lamp_test.checked)
|
||||
}
|
||||
}
|
||||
|
||||
setConfig(nightLamp: NightLampConfig) {
|
||||
this.night_lamp_only_when_dark.checked = nightLamp.night_lamp_only_when_dark
|
||||
this.night_lamp_time_start.value = nightLamp.night_lamp_hour_start.toString();
|
||||
this.night_lamp_time_end.value = nightLamp.night_lamp_hour_end.toString();
|
||||
this.night_lamp_enabled.checked = nightLamp.enabled;
|
||||
this.night_lamp_soc_low.value = nightLamp.low_soc_cutoff.toString();
|
||||
this.night_lamp_soc_restore.value = nightLamp.low_soc_restore.toString();
|
||||
}
|
||||
|
||||
getConfig(): NightLampConfig {
|
||||
@ -44,6 +67,9 @@ export class NightLampView {
|
||||
night_lamp_hour_start: +this.night_lamp_time_start.value,
|
||||
night_lamp_hour_end: +this.night_lamp_time_end.value,
|
||||
night_lamp_only_when_dark: this.night_lamp_only_when_dark.checked,
|
||||
enabled: this.night_lamp_enabled.checked,
|
||||
low_soc_cutoff: +this.night_lamp_soc_low.value,
|
||||
low_soc_restore: +this.night_lamp_soc_restore.value
|
||||
}
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@
|
||||
<div class="flexcontainer">
|
||||
<div class="subtitle">Tank:</div>
|
||||
</div>
|
||||
<div class="flexcontainer" style="text-decoration-line: line-through;">
|
||||
<div class="flexcontainer">
|
||||
<span class="tankkey">Enable Tank Sensor</span>
|
||||
<input class="tankcheckbox" type="checkbox" id="tank_sensor_enabled">
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user