added solar ina handling, adjusted website

This commit is contained in:
Empire 2025-06-20 23:29:44 +02:00
parent 34b20b1f8f
commit 04849162cd
16 changed files with 301 additions and 42 deletions

View File

@ -7,6 +7,7 @@ use chrono::{DateTime, Utc};
use embedded_hal::digital::OutputPin; use embedded_hal::digital::OutputPin;
use esp_idf_hal::gpio::{IOPin, Pull}; use esp_idf_hal::gpio::{IOPin, Pull};
use esp_idf_hal::gpio::{InputOutput, PinDriver}; use esp_idf_hal::gpio::{InputOutput, PinDriver};
use measurements::{Current, Voltage};
pub struct Initial<'a> { pub struct Initial<'a> {
pub(crate) general_fault: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, pub(crate) general_fault: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
@ -123,4 +124,12 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
self.esp.save_config(&self.config)?; self.esp.save_config(&self.config)?;
anyhow::Ok(()) anyhow::Ok(())
} }
fn get_mptt_voltage(&mut self) -> Result<Voltage> {
bail!("Please configure board revision")
}
fn get_mptt_current(&mut self) -> Result<Current> {
bail!("Please configure board revision")
}
} }

View File

@ -50,6 +50,7 @@ use esp_idf_hal::gpio::{
use esp_idf_hal::pcnt::PCNT0; use esp_idf_hal::pcnt::PCNT0;
use esp_idf_hal::prelude::Peripherals; use esp_idf_hal::prelude::Peripherals;
use esp_idf_hal::reset::ResetReason; use esp_idf_hal::reset::ResetReason;
use measurements::{Current, Voltage};
use pca9535::StandardExpanderInterface; use pca9535::StandardExpanderInterface;
//Only support for 8 right now! //Only support for 8 right now!
@ -115,15 +116,18 @@ impl Default for BackupHeader {
} }
pub trait BoardInteraction<'a> { pub trait BoardInteraction<'a> {
fn set_charge_indicator(&mut self, charging: bool) -> Result<()>;
fn is_day(&self) -> bool;
fn get_mptt_voltage(&mut self) -> Result<Voltage>;
fn get_mptt_current(&mut self) -> Result<Current>;
fn get_esp(&mut self) -> &mut ESP<'a>; fn get_esp(&mut self) -> &mut ESP<'a>;
fn get_config(&mut self) -> &PlantControllerConfig; fn get_config(&mut self) -> &PlantControllerConfig;
fn get_battery_monitor(&mut self) -> &mut Box<dyn BatteryInteraction + Send>; fn get_battery_monitor(&mut self) -> &mut Box<dyn BatteryInteraction + Send>;
fn set_charge_indicator(&mut self, charging: bool) -> Result<()>;
fn deep_sleep(&mut self, duration_in_ms: u64) -> !; fn deep_sleep(&mut self, duration_in_ms: u64) -> !;
fn get_backup_info(&mut self) -> Result<BackupHeader>; fn get_backup_info(&mut self) -> Result<BackupHeader>;
fn get_backup_config(&mut self) -> Result<Vec<u8>>; fn get_backup_config(&mut self) -> Result<Vec<u8>>;
fn backup_config(&mut self, bytes: &[u8]) -> Result<()>; fn backup_config(&mut self, bytes: &[u8]) -> Result<()>;
fn is_day(&self) -> bool;
//should be multsampled //should be multsampled
fn water_temperature_c(&mut self) -> Result<f32>; fn water_temperature_c(&mut self) -> Result<f32>;
/// return median tank sensor value in milli volt /// return median tank sensor value in milli volt

View File

@ -23,6 +23,7 @@ use esp_idf_hal::pcnt::{
PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex, PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex,
}; };
use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, vTaskDelay, EspError}; use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, vTaskDelay, EspError};
use measurements::{Current, Voltage};
use one_wire_bus::OneWire; use one_wire_bus::OneWire;
use plant_ctrl2::sipo::ShiftRegister40; use plant_ctrl2::sipo::ShiftRegister40;
use std::result::Result::Ok as OkStd; use std::result::Result::Ok as OkStd;
@ -247,6 +248,26 @@ pub(crate) fn create_v3(
} }
impl<'a> BoardInteraction<'a> for V3<'a> { impl<'a> BoardInteraction<'a> for V3<'a> {
fn set_charge_indicator(&mut self, charging: bool) -> Result<()> {
Ok(self.shift_register.decompose()[CHARGING].set_state(charging.into())?)
}
fn is_day(&self) -> bool {
self.solar_is_day.get_level().into()
}
fn get_mptt_voltage(&mut self) -> Result<Voltage> {
//if working this is the hardware set mppt voltage
if self.is_day() {
Ok(Voltage::from_volts(15_f64))
} else {
Ok(Voltage::from_volts(0_f64))
}
}
fn get_mptt_current(&mut self) -> Result<Current> {
bail!("Board does not have current sensor")
}
fn get_esp(&mut self) -> &mut ESP<'a> { fn get_esp(&mut self) -> &mut ESP<'a> {
&mut self.esp &mut self.esp
} }
@ -259,10 +280,6 @@ impl<'a> BoardInteraction<'a> for V3<'a> {
&mut self.battery_monitor &mut self.battery_monitor
} }
fn set_charge_indicator(&mut self, charging: bool) -> Result<()> {
Ok(self.shift_register.decompose()[CHARGING].set_state(charging.into())?)
}
fn deep_sleep(&mut self, duration_in_ms: u64) -> ! { fn deep_sleep(&mut self, duration_in_ms: u64) -> ! {
let _ = self.shift_register.decompose()[AWAKE].set_low(); let _ = self.shift_register.decompose()[AWAKE].set_low();
deep_sleep(duration_in_ms) deep_sleep(duration_in_ms)
@ -364,10 +381,6 @@ impl<'a> BoardInteraction<'a> for V3<'a> {
Ok(()) Ok(())
} }
fn is_day(&self) -> bool {
self.solar_is_day.get_level().into()
}
fn water_temperature_c(&mut self) -> Result<f32> { fn water_temperature_c(&mut self) -> Result<f32> {
self.one_wire_bus self.one_wire_bus
.reset(&mut self.esp.delay) .reset(&mut self.esp.delay)

View File

@ -23,12 +23,14 @@ use esp_idf_hal::pcnt::{
PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex, PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex,
}; };
use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, vTaskDelay, EspError}; use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, vTaskDelay, EspError};
use ina219::address::Address; use ina219::address::{Address, Pin};
use ina219::calibration::{Calibration, UnCalibrated}; use ina219::calibration::{Calibration, UnCalibrated};
use ina219::SyncIna219; use ina219::SyncIna219;
use measurements::{Current, Resistance, Voltage};
use one_wire_bus::OneWire; use one_wire_bus::OneWire;
use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface}; use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface};
use std::result::Result::Ok as OkStd; use std::result::Result::Ok as OkStd;
use ina219::configuration::{Configuration, OperatingMode};
const MS0: u8 = 1_u8; const MS0: u8 = 1_u8;
const MS1: u8 = 0_u8; const MS1: u8 = 0_u8;
@ -37,15 +39,78 @@ const MS3: u8 = 4_u8;
const MS4: u8 = 2_u8; const MS4: u8 = 2_u8;
const SENSOR_ON: u8 = 5_u8; const SENSOR_ON: u8 = 5_u8;
pub struct V4<'a> { pub enum Charger<'a> {
SolarMpptV1 {
mppt_ina: SyncIna219<MutexDevice<'a, I2cDriver<'a>>, UnCalibrated>, mppt_ina: SyncIna219<MutexDevice<'a, I2cDriver<'a>>, UnCalibrated>,
solar_is_day: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>,
charge_indicator: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
},
}
impl<'a> Charger<'a> {
pub(crate) fn powersave(&mut self) {
match self { Charger::SolarMpptV1 { mppt_ina, .. } => {
let _ = mppt_ina.set_configuration(Configuration {
reset: Default::default(),
bus_voltage_range: Default::default(),
shunt_voltage_range: Default::default(),
bus_resolution: Default::default(),
shunt_resolution: Default::default(),
operating_mode: OperatingMode::PowerDown,
}).map_err(|e| {
println!(
"Error setting ina mppt configuration during deepsleep preparation{:?}",
e
);
});
} }
}
fn set_charge_indicator(&mut self, charging: bool) -> anyhow::Result<()> {
match self {
Self::SolarMpptV1 {
charge_indicator, ..
} => {
charge_indicator.set_state(charging.into())?;
}
}
Ok(())
}
fn is_day(&self) -> bool {
match self {
Charger::SolarMpptV1 { solar_is_day, .. } => solar_is_day.get_level().into(),
}
}
fn get_mptt_voltage(&mut self) -> anyhow::Result<Voltage> {
let voltage = match self {
Charger::SolarMpptV1 { mppt_ina, .. } => mppt_ina
.bus_voltage()
.map(|v| Voltage::from_millivolts(v.voltage_mv() as f64))?,
};
Ok(voltage)
}
fn get_mptt_current(&mut self) -> anyhow::Result<Current> {
let current = match self {
Charger::SolarMpptV1 { mppt_ina, .. } => mppt_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(current)
}
}
pub struct V4<'a> {
esp: ESP<'a>, esp: ESP<'a>,
charger: Charger<'a>,
battery_monitor: Box<dyn BatteryInteraction + Send>, battery_monitor: Box<dyn BatteryInteraction + Send>,
config: PlantControllerConfig, config: PlantControllerConfig,
tank_channel: AdcChannelDriver<'a, Gpio5, AdcDriver<'a, esp_idf_hal::adc::ADC1>>, tank_channel: AdcChannelDriver<'a, Gpio5, AdcDriver<'a, esp_idf_hal::adc::ADC1>>,
solar_is_day: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>,
signal_counter: PcntDriver<'a>, signal_counter: PcntDriver<'a>,
charge_indicator: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
awake: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, Output>, awake: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, Output>,
light: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, light: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
tank_power: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, tank_power: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
@ -173,7 +238,21 @@ pub(crate) fn create_v4(
let _ = sensor_expander.pin_set_low(GPIOBank::Bank1, pin); let _ = sensor_expander.pin_set_low(GPIOBank::Bank1, pin);
} }
let mut mppt_ina = SyncIna219::new(MutexDevice::new(&I2C_DRIVER), Address::from_byte(68)?)?; //TODO error handling is not done nicely here, should not break if ina is not responsive
let mut mppt_ina = SyncIna219::new(
MutexDevice::new(&I2C_DRIVER),
Address::from_pins(Pin::Vcc, Pin::Gnd),
)?;
mppt_ina.set_configuration(Configuration{
reset: Default::default(),
bus_voltage_range: Default::default(),
shunt_voltage_range: Default::default(),
bus_resolution: Default::default(),
shunt_resolution: ina219::configuration::Resolution::Avg128,
operating_mode: Default::default(),
})?;
//TODO this is probably laready done untill we are ready first time?, maybee add startup time comparison on access?
esp.delay.delay_ms( esp.delay.delay_ms(
mppt_ina mppt_ina
.configuration()? .configuration()?
@ -181,18 +260,17 @@ pub(crate) fn create_v4(
.unwrap() .unwrap()
.as_millis() as u32, .as_millis() as u32,
); );
println!("Bus Voltage: {}", mppt_ina.bus_voltage()?);
println!("Shunt Voltage: {}", mppt_ina.shunt_voltage()?); let charger = Charger::SolarMpptV1 {
let volt = (mppt_ina.shunt_voltage()?.shunt_voltage_mv()) as f32 / 1000_f32; mppt_ina,
let current = volt / 0.05; solar_is_day,
println!("Shunt Current: {}", current); charge_indicator,
};
let v = V4 { let v = V4 {
mppt_ina,
esp, esp,
awake, awake,
tank_channel, tank_channel,
solar_is_day,
signal_counter, signal_counter,
light, light,
tank_power, tank_power,
@ -202,9 +280,9 @@ pub(crate) fn create_v4(
general_fault, general_fault,
pump_expander, pump_expander,
sensor_expander, sensor_expander,
charge_indicator,
config, config,
battery_monitor, battery_monitor,
charger,
}; };
Ok(Box::new(v)) Ok(Box::new(v))
} }
@ -223,14 +301,12 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
} }
fn set_charge_indicator(&mut self, charging: bool) -> anyhow::Result<()> { fn set_charge_indicator(&mut self, charging: bool) -> anyhow::Result<()> {
self.charge_indicator self.charger.set_charge_indicator(charging)
.set_state(charging.into())
.expect("cannot fail");
Ok(())
} }
fn deep_sleep(&mut self, duration_in_ms: u64) -> ! { fn deep_sleep(&mut self, duration_in_ms: u64) -> ! {
self.awake.set_low().unwrap(); self.awake.set_low().unwrap();
self.charger.powersave();
deep_sleep(duration_in_ms); deep_sleep(duration_in_ms);
} }
@ -331,7 +407,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
} }
fn is_day(&self) -> bool { fn is_day(&self) -> bool {
self.solar_is_day.get_level().into() self.charger.is_day()
} }
fn water_temperature_c(&mut self) -> anyhow::Result<f32> { fn water_temperature_c(&mut self) -> anyhow::Result<f32> {
@ -568,4 +644,12 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
self.esp.save_config(&self.config)?; self.esp.save_config(&self.config)?;
anyhow::Ok(()) anyhow::Ok(())
} }
fn get_mptt_voltage(&mut self) -> anyhow::Result<Voltage> {
self.charger.get_mptt_voltage()
}
fn get_mptt_current(&mut self) -> anyhow::Result<Current> {
self.charger.get_mptt_current()
}
} }

View File

@ -756,13 +756,24 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
loop { loop {
unsafe { unsafe {
let mut board = BOARD_ACCESS.lock().unwrap(); let mut board = BOARD_ACCESS.lock().unwrap();
if let Ok(charging) = board
//we have mppt controller, ask it for charging current
if let Ok(current) = board.board_hal.get_mptt_current() {
let _ = board.board_hal.set_charge_indicator(current.as_milliamperes() > 20_f64);
}
//fallback to battery controller and ask it instead
else if let Ok(charging) = board
.board_hal .board_hal
.get_battery_monitor() .get_battery_monitor()
.average_current_milli_ampere() .average_current_milli_ampere()
{ {
let _ = board.board_hal.set_charge_indicator(charging > 20); let _ = board.board_hal.set_charge_indicator(charging > 20);
} }
else {
//who knows
let _ = board.board_hal.set_charge_indicator(false);
}
match wait_type { match wait_type {
WaitType::MissingConfig => { WaitType::MissingConfig => {
// Keep existing behavior: circular filling pattern // Keep existing behavior: circular filling pattern

View File

@ -40,6 +40,13 @@ struct Moistures {
moisture_b: Vec<std::string::String>, moisture_b: Vec<std::string::String>,
} }
#[derive(Serialize, Debug)]
struct SolarState {
mppt_voltage: f32,
mppt_current: f32,
is_day: bool,
}
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
struct SetTime<'a> { struct SetTime<'a> {
time: &'a str, time: &'a str,
@ -218,6 +225,18 @@ fn set_config(
anyhow::Ok(Some("saved".to_owned())) anyhow::Ok(Some("saved".to_owned()))
} }
fn get_solar_state(
_request: &mut Request<&mut EspHttpConnection>,
) -> Result<Option<std::string::String>, anyhow::Error> {
let mut board = BOARD_ACCESS.lock().expect("board access");
let state = SolarState {
mppt_voltage: board.board_hal.get_mptt_voltage()?.as_volts() as f32,
mppt_current: board.board_hal.get_mptt_current()?.as_amperes() as f32,
is_day: board.board_hal.is_day(),
};
anyhow::Ok(Some(serde_json::to_string(&state)?))
}
fn get_battery_state( fn get_battery_state(
_request: &mut Request<&mut EspHttpConnection>, _request: &mut Request<&mut EspHttpConnection>,
) -> Result<Option<std::string::String>, anyhow::Error> { ) -> Result<Option<std::string::String>, anyhow::Error> {
@ -383,6 +402,11 @@ pub fn httpd(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
handle_error_to500(request, get_battery_state) handle_error_to500(request, get_battery_state)
}) })
.unwrap(); .unwrap();
server
.fn_handler("/solar", Method::Get, |request| {
handle_error_to500(request, get_solar_state)
})
.unwrap();
server server
.fn_handler("/time", Method::Get, |request| { .fn_handler("/time", Method::Get, |request| {
handle_error_to500(request, get_time) handle_error_to500(request, get_time)

View File

@ -38,6 +38,12 @@ export interface FileList {
iter_error: string, iter_error: string,
} }
export interface SolarState{
mppt_voltage: number,
mppt_current: number,
is_day: boolean
}
export interface FileInfo{ export interface FileInfo{
filename: string, filename: string,
size: number, size: number,

View File

@ -13,7 +13,7 @@
<select class="boardvalue" id="hardware_board_value"> <select class="boardvalue" id="hardware_board_value">
</select> </select>
</div> </div>
<div class="flexcontainer" style="text-decoration-line: line-through;"> <div class="flexcontainer">
<div class="boardkey">BatteryMonitor</div> <div class="boardkey">BatteryMonitor</div>
<select class="boardvalue" id="hardware_battery_value"> <select class="boardvalue" id="hardware_battery_value">
</select> </select>

View File

@ -149,6 +149,8 @@
</div> </div>
<div id="batteryview" class="subcontainer"> <div id="batteryview" class="subcontainer">
</div> </div>
<div id="solarview" class="subcontainer">
</div>
</div> </div>
<div class="flexcontainer"> <div class="flexcontainer">

View File

@ -28,8 +28,9 @@ import {
SetTime, SSIDList, TankInfo, SetTime, SSIDList, TankInfo,
TestPump, TestPump,
VersionInfo, VersionInfo,
FileList FileList, SolarState
} from "./api"; } from "./api";
import {SolarView} from "./solarview";
export class Controller { export class Controller {
loadTankInfo() : Promise<void> { loadTankInfo() : Promise<void> {
@ -160,7 +161,7 @@ export class Controller {
console.log(error); console.log(error);
}); });
} }
updateBatteryData(): Promise<void> { updateBatteryData() {
return fetch(PUBLIC_URL + "/battery") return fetch(PUBLIC_URL + "/battery")
.then(response => response.json()) .then(response => response.json())
.then(json => json as BatteryState) .then(json => json as BatteryState)
@ -172,6 +173,18 @@ export class Controller {
console.log(error); console.log(error);
}) })
} }
updateSolarData() {
return fetch(PUBLIC_URL + "/solar")
.then(response => response.json())
.then(json => json as SolarState)
.then(solar => {
controller.solarView.update(solar)
})
.catch(error => {
controller.solarView.update(null)
console.log(error);
})
}
uploadNewFirmware(file: File) { uploadNewFirmware(file: File) {
var current = 0; var current = 0;
var max = 100; var max = 100;
@ -244,6 +257,7 @@ export class Controller {
//load from remote to be clean //load from remote to be clean
controller.downloadConfig() controller.downloadConfig()
} }
backupConfig(json: string, statusCallback: (status: string) => void) { backupConfig(json: string, statusCallback: (status: string) => void) {
controller.progressview.addIndeterminate("backup_config", "Backingup Config") controller.progressview.addIndeterminate("backup_config", "Backingup Config")
fetch(PUBLIC_URL + "/backup_config", { fetch(PUBLIC_URL + "/backup_config", {
@ -465,6 +479,7 @@ export class Controller {
readonly firmWareView: OTAView; readonly firmWareView: OTAView;
readonly progressview: ProgressView; readonly progressview: ProgressView;
readonly batteryView: BatteryView; readonly batteryView: BatteryView;
readonly solarView: SolarView;
readonly fileview: FileView; readonly fileview: FileView;
readonly logView: LogView readonly logView: LogView
constructor() { constructor() {
@ -473,6 +488,7 @@ export class Controller {
this.networkView = new NetworkConfigView(this, PUBLIC_URL) this.networkView = new NetworkConfigView(this, PUBLIC_URL)
this.tankView = new TankConfigView(this) this.tankView = new TankConfigView(this)
this.batteryView = new BatteryView(this) this.batteryView = new BatteryView(this)
this.solarView = new SolarView(this)
this.nightLampView = new NightLampView(this) this.nightLampView = new NightLampView(this)
this.submitView = new SubmitView(this) this.submitView = new SubmitView(this)
this.firmWareView = new OTAView(this) this.firmWareView = new OTAView(this)
@ -489,21 +505,16 @@ export class Controller {
controller.exit(); controller.exit();
} }
} }
selftest() {
}
} }
const controller = new Controller(); const controller = new Controller();
controller.progressview.removeProgress("rebooting"); controller.progressview.removeProgress("rebooting");
const tasks = [ const tasks = [
{ task: controller.populateTimezones, displayString: "Populating Timezones" }, { task: controller.populateTimezones, displayString: "Populating Timezones" },
{ task: controller.updateRTCData, displayString: "Updating RTC Data" }, { task: controller.updateRTCData, displayString: "Updating RTC Data" },
{ task: controller.updateBatteryData, displayString: "Updating Battery Data" }, { task: controller.updateBatteryData, displayString: "Updating Battery Data" },
{ task: controller.updateSolarData, displayString: "Updating Solar Data" },
{ task: controller.downloadConfig, displayString: "Downloading Configuration" }, { task: controller.downloadConfig, displayString: "Downloading Configuration" },
{ task: controller.version, displayString: "Fetching Version Information" }, { task: controller.version, displayString: "Fetching Version Information" },
{ task: controller.updateFileList, displayString: "Updating File List" }, { task: controller.updateFileList, displayString: "Updating File List" },

View File

@ -20,7 +20,7 @@
<div class="lightkey">Test Nightlight</div> <div class="lightkey">Test Nightlight</div>
<input class="lightcheckbox" type="checkbox" id="night_lamp_test"> <input class="lightcheckbox" type="checkbox" id="night_lamp_test">
</div> </div>
<div class="flexcontainer" style="text-decoration-line: line-through;"> <div class="flexcontainer">
<div class="lightkey">Enable Nightlight</div> <div class="lightkey">Enable Nightlight</div>
<input class="lightcheckbox" type="checkbox" id="night_lamp_enabled"> <input class="lightcheckbox" type="checkbox" id="night_lamp_enabled">
</div> </div>

View File

@ -29,7 +29,7 @@ export class OTAView {
}; };
test.onclick = () => { test.onclick = () => {
controller.selftest(); controller.selfTest();
} }
} }

View File

@ -0,0 +1,29 @@
<style>
.solarflexkey {
min-width: 150px;
}
.solarflexvalue {
text-wrap: nowrap;
flex-grow: 1;
}
</style>
<div class="flexcontainer">
<div class="subtitle">
Mppt:
</div>
<input id="solar_auto_refresh" type="checkbox">
</div>
<div class="flexcontainer">
<span class="solarflexkey">Mppt mV:</span>
<span class="solarflexvalue" id="solar_voltage_milli_volt"></span>
</div>
<div class="flexcontainer">
<span class="solarflexkey">Mppt mA:</span>
<span class="solarflexvalue" id="solar_current_milli_ampere" ></span>
</div>
<div class="flexcontainer">
<span class="solarflexkey">is Day:</span>
<span class="solarflexvalue" id="solar_is_day" ></span>
</div>

View File

@ -0,0 +1,49 @@
import { Controller } from "./main";
import {BatteryState, SolarState} from "./api";
export class SolarView{
solar_voltage_milli_volt: HTMLSpanElement;
solar_current_milli_ampere: HTMLSpanElement;
solar_is_day: HTMLSpanElement;
solar_auto_refresh: HTMLInputElement;
timer: NodeJS.Timeout | undefined;
controller: Controller;
constructor (controller:Controller) {
(document.getElementById("solarview") as HTMLElement).innerHTML = require("./solarview.html")
this.solar_voltage_milli_volt = document.getElementById("solar_voltage_milli_volt") as HTMLSpanElement;
this.solar_current_milli_ampere = document.getElementById("solar_current_milli_ampere") as HTMLSpanElement;
this.solar_is_day = document.getElementById("solar_is_day") as HTMLSpanElement;
this.solar_auto_refresh = document.getElementById("solar_auto_refresh") as HTMLInputElement;
this.controller = controller
this.solar_auto_refresh.onchange = () => {
if(this.timer){
clearTimeout(this.timer)
}
if(this.solar_auto_refresh.checked){
controller.updateSolarData()
}
}
}
update(solarState: SolarState|null){
if (solarState == null) {
this.solar_voltage_milli_volt.innerText = "N/A"
this.solar_current_milli_ampere.innerText = "N/A"
this.solar_is_day.innerText = "N/A"
} else {
this.solar_voltage_milli_volt.innerText = solarState.mppt_voltage.toFixed(2)
this.solar_current_milli_ampere.innerText = String(+solarState.mppt_current)
this.solar_is_day.innerText = solarState.is_day?"🌞":"🌙"
}
if(this.solar_auto_refresh.checked){
this.timer = setTimeout(this.controller.updateSolarData, 1000);
} else {
if(this.timer){
clearTimeout(this.timer)
}
}
}
}

View File

@ -7,14 +7,31 @@
word-wrap: break-word; word-wrap: break-word;
overflow: scroll; overflow: scroll;
} }
.submitbutton{
padding: 1em 1em;
background: #667eea;
color: white;
border: none;
border-radius: 8px;
font-size: 1.1em;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
letter-spacing: 1px;
margin: 1em 0;
}
.submitbutton:hover {
background: #1c4e63;
}
</style> </style>
<button class="submitbutton" id="submit">Submit</button>
<br>
<button id="showJson">Show Json</button> <button id="showJson">Show Json</button>
<div id="rawdata" class="flexcontainer" style="display: none;"> <div id="rawdata" class="flexcontainer" style="display: none;">
<div class="submitarea" id="json" contenteditable="true"></div> <div class="submitarea" id="json" contenteditable="true"></div>
<div class="submitarea" id="backupjson">backup will be here</div> <div class="submitarea" id="backupjson">backup will be here</div>
</div> </div>
<button id="submit">Submit</button>
<div>BackupStatus:</div> <div>BackupStatus:</div>
<div id="backuptimestamp"></div> <div id="backuptimestamp"></div>
<div id="backupsize"></div> <div id="backupsize"></div>

View File

@ -9,7 +9,7 @@ console.log("Dev server is " + isDevServer);
var host; var host;
if (isDevServer){ if (isDevServer){
//ensure no trailing / //ensure no trailing /
host = 'http://192.168.71.1'; host = 'http://10.23.44.186';
} else { } else {
host = ''; host = '';
} }