made ui more modular

This commit is contained in:
Empire Phoenix 2024-12-16 00:31:59 +01:00
parent 92299665b6
commit c89a617d9d
7 changed files with 250 additions and 236 deletions

View File

@ -1,7 +1,9 @@
<html>
<head>
</head>
<head>
</head>
<body>
<input type="button" id="test" value="Test">
<h2>Current Firmware</h2>
@ -10,12 +12,12 @@
<div id="firmware_githash">Build githash loading</div>
</div>
<h2>Time</h2>
<div>
<div id="esp_time">Esp time</div>
<div id="rtc_time">Rtc time</div>
<div id="browser_time">Rtc time</div>
<div id="timeview">
<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>Store Browser time into esp and rtc</div>
<input type="button" id="time_upload" value="write">
<input type="button" id="timeview_time_upload" value="write">
</div>
<h2>firmeware OTA v3</h2>
@ -38,13 +40,13 @@
<label for="ssid">SSID:</label>
<input type="text" id="ssid" list="ssidlist">
<datalist id="ssidlist">
<option value="Not scanned yet">
<option value="Not scanned yet">
</datalist>
<label for="ssid">Password:</label>
<input type="text" id="password">
</div>
</div>
<h2>config</h2>
<div id="configform">
@ -69,7 +71,7 @@
Allow Pumping if Sensor Error
</div>
<div>
<input type="number" min="2" max="500000" id="tank_useable_ml">
Tank Size mL
@ -108,9 +110,9 @@
<input type="button" name="battery_flash_button" id="battery_flash_button"><br>
<h3 id="battery_flash_status"></h3>
<p id="battery_flash_loaded_n_total"></p>
<div style="height: 100px; display: block; overflow-y: auto;" id = "battery_flash_message"></div>
<div style="height: 100px; display: block; overflow-y: auto;" id="battery_flash_message"></div>
</form>
<h3>Plants:</h3>
@ -118,7 +120,7 @@
<input type="number" min="2" max="100" id="max_consecutive_pump_count">
Max consecutive pump count:
</div>
<div id="plants"></div>
</div>
<button id="submit">Submit</button>
@ -128,4 +130,4 @@
<script src="bundle.js"></script>
</body>
</html>
</html>

View File

@ -6,16 +6,33 @@ console.log("Url is " + PUBLIC_URL);
import { TimeView } from "./timeview";
import { PlantView, PlantViews } from "./plant";
import { NetworkConfigView } from "./network";
import { NightLampView } from "./nightmode";
import { TankConfigView } from "./tanks";
export class Controller {
readonly timeView: TimeView
readonly plantViews: PlantViews
readonly timeView: TimeView;
readonly plantViews: PlantViews;
readonly networkView: NetworkConfigView;
readonly tankView: TankConfigView;
readonly nightLampView: NightLampView;
constructor() {
this.timeView = new TimeView()
this.timeView = new TimeView(this)
this.plantViews = new PlantViews(this)
this.networkView = new NetworkConfigView(this)
this.tankView = new TankConfigView(this)
this.nightLampView = new NightLampView(this)
}
syncRTCFromBrowser(){
var value: SetTime = {
time: new Date().toISOString()
}
var pretty = JSON.stringify(value, undefined, 1);
fetch(PUBLIC_URL + "/time", {
method: "POST",
body: pretty
})
}
configChanged() {
@ -40,15 +57,9 @@ export class Controller {
.then(response => response.json())
.then(json => json as GetData)
.then(time => {
controller.timeView.update(time.native, time.rtc)
time.moisture_a.forEach((a, index) => {
controller.plantViews.getPlant(index).setMoistureA(a);
})
time.moisture_b.forEach((b, index) => {
controller.plantViews.getPlant(index).setMoistureB(b);
})
controller.plantViews.update(time.moisture_a, time.moisture_b)
setTimeout(controller.updateRealTimeData, 1000);
})
.catch(error => {
@ -57,6 +68,36 @@ export class Controller {
});
}
getConfig(): PlantControllerConfig{
return {
network: controller.networkView.getConfig(),
tank: controller.tankView.getConfig(),
nightLamp: controller.nightLampView.getConfig(),
plants: controller.plantViews.getConfig()
}
}
scanWifi() {
var ajax = new XMLHttpRequest();
ajax.responseType = 'json';
ajax.onreadystatechange = () => {
if (ajax.readyState === 4) {
this.networkView.setScanResult(ajax.response as SSIDList)
}
};
ajax.onerror = (evt) => {
alert("Failed to start see console")
}
ajax.open("POST", PUBLIC_URL + "/wifiscan");
ajax.send();
}
setConfig(current: PlantControllerConfig) {
this.tankView.setConfig(current.tank);
this.networkView.setConfig(current.network);
this.nightLampView.setConfig(current.nightLamp);
this.plantViews.setConfig(current.plants);
}
}
const controller = new Controller();
@ -66,178 +107,18 @@ setTimeout(controller.updateRealTimeData, 1000);
let scanWifiBtn = document.getElementById("scan") as HTMLButtonElement;
if (scanWifiBtn) {
scanWifiBtn.onclick = scanWifi;
}
export function scanWifi() {
var scanButton = (document.getElementById("scan") as HTMLButtonElement);
scanButton.disabled = true;
var ajax = new XMLHttpRequest();
ajax.responseType = 'json';
ajax.onreadystatechange = () => {
if (ajax.readyState === 4) {
callback(ajax.response);
}
};
ajax.onerror = (evt) => {
console.log(evt)
scanButton.disabled = false;
alert("Failed to start see console")
}
ajax.open("POST", PUBLIC_URL + "/wifiscan");
ajax.send();
}
function callback(data: SSIDList) {
var ssidlist = document.getElementById("ssidlist") as HTMLElement
ssidlist.innerHTML = ''
for (var ssid of data.ssids) {
var wi = document.createElement("option");
wi.value = ssid;
ssidlist.appendChild(wi);
}
}
function addTimeOptions(select: HTMLSelectElement) {
for (let i = 0; i < 24; i++) {
let option = document.createElement("option");
option.innerText = i.toString();
select.appendChild(option);
}
}
class TankConfigView {
max_consecutive_pump_count: HTMLInputElement;
tank_useable_ml: HTMLInputElement;
tank_empty_percent: HTMLInputElement;
tank_full_percent: HTMLInputElement;
tank_warn_percent: HTMLInputElement;
tank_sensor_enabled: HTMLInputElement;
tank_allow_pumping_if_sensor_error: HTMLInputElement;
constructor(controller:Controller){
this.max_consecutive_pump_count = document.getElementById("max_consecutive_pump_count") as HTMLInputElement;
this.max_consecutive_pump_count.onchange = controller.configChanged
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;
this.tank_empty_percent.onchange = controller.configChanged
this.tank_full_percent = document.getElementById("tank_full_percent") as HTMLInputElement;
this.tank_full_percent.onchange = controller.configChanged
this.tank_warn_percent = document.getElementById("tank_warn_percent") as HTMLInputElement;
this.tank_warn_percent.onchange = controller.configChanged
this.tank_sensor_enabled = document.getElementById("tank_sensor_enabled") as HTMLInputElement;
this.tank_sensor_enabled.onchange = controller.configChanged
this.tank_allow_pumping_if_sensor_error = document.getElementById("tank_allow_pumping_if_sensor_error") as HTMLInputElement;
this.tank_allow_pumping_if_sensor_error.onchange = controller.configChanged
}
}
class NightLampView {
night_lamp_only_when_dark: HTMLInputElement;
night_lamp_time_start: HTMLSelectElement;
night_lamp_time_end: HTMLSelectElement;
constructor(){
this.night_lamp_only_when_dark = document.getElementById("night_lamp_only_when_dark") as HTMLInputElement;
this.night_lamp_only_when_dark.onchange = updateJson
this.night_lamp_time_start = document.getElementById("night_lamp_time_start") as HTMLSelectElement;
this.night_lamp_time_start.onchange = updateJson
for (let i = 0; i < 24; i++) {
let option = document.createElement("option");
if (i == 20){
option.selected = true
}
option.innerText = i.toString();
this.night_lamp_time_start.appendChild(option);
}
this.night_lamp_time_end = document.getElementById("night_lamp_time_end") as HTMLSelectElement;
this.night_lamp_time_end.onchange = updateJson
for (let i = 0; i < 24; i++) {
let option = document.createElement("option");
if (i == 1){
option.selected = true
}
option.innerText = i.toString();
this.night_lamp_time_end.appendChild(option);
}
}
}
function updateJson() {
var current: PlantControllerConfig = {
network: controller.networkView.getConfig(),
tank_sensor_enabled: tank_sensor_enabled.checked,
tank_useable_ml: +tank_useable_ml.value,
tank_warn_percent: +tank_warn_percent.value,
tank_empty_percent: +tank_empty_percent.value,
tank_full_percent: +tank_full_percent.value,
night_lamp_hour_start: +night_lamp_time_start.value,
night_lamp_hour_end: +night_lamp_time_end.value,
night_lamp_only_when_dark: night_lamp_only_when_dark.checked,
plants: []
}
const current = controller.getConfig();
for (let i = 0; i < 8; i++) {
current.plants[i] = controller.plantViews.getPlant(i).getConfig();
}
//sync(current);
console.log(current);
var pretty = JSON.stringify(current, undefined, 1);
console.log(pretty)
//json.value = pretty;
}
let fromWrapper = (() => {
let plantcount = 0;
let json = document.getElementById('json') as HTMLInputElement
function sync(current: PlantControllerConfig) {
plantcount = current.plants.length
ap_ssid.value = current.ap_ssid;
ssid.value = current.ssid;
password.value = current.password;
mqtt_url.value = current.mqtt_url;
base_topic.value = current.base_topic;
max_consecutive_pump_count.value = current.max_consecutive_pump_count.toString();
tank_useable_ml.disabled = !current.tank_sensor_enabled;
tank_warn_percent.disabled = !current.tank_sensor_enabled;
tank_sensor_enabled.checked = current.tank_sensor_enabled;
tank_allow_pumping_if_sensor_error.checked = current.tank_allow_pumping_if_sensor_error;
tank_useable_ml.value = current.tank_useable_ml.toString();
tank_warn_percent.value = current.tank_warn_percent.toString();
tank_empty_percent.value = current.tank_empty_percent.toString();
tank_full_percent.value = current.tank_full_percent.toString();
night_lamp_time_start.value = current.night_lamp_hour_start.toString();
night_lamp_time_end.value = current.night_lamp_hour_end.toString();
for (let i = 0; i < current.plants.length; i++) {
const plantConfig = current.plants[i];
const plantView = controller.plantViews.getPlant(i);
plantView.setConfig(plantConfig)
}
}
let submitFormBtn = document.getElementById("submit") as HTMLButtonElement
let submit_status = document.getElementById("submit_status") as HTMLElement
@ -261,7 +142,7 @@ let fromWrapper = (() => {
.then(response => response.json())
.then(loaded => {
var currentConfig = loaded as PlantControllerConfig;
sync(currentConfig);
controller.setConfig(currentConfig);
var pretty = JSON.stringify(currentConfig, undefined, 1);
json.value = pretty;
}

View File

@ -1,13 +1,12 @@
import { Controller } from ".";
export class NetworkConfigView {
getConfig(): NetworkConfig {
return {
ap_ssid: this.ap_ssid.value,
ssid: this.ssid.value,
password: this.password.value,
mqtt_url: this.mqtt_url.value,
base_topic: this.base_topic.value
setScanResult(ssidList: SSIDList) {
this.ssidlist.innerHTML = ''
for (var ssid of ssidList.ssids) {
var wi = document.createElement("option");
wi.value = ssid;
this.ssidlist.appendChild(wi);
}
}
private readonly ap_ssid: HTMLInputElement;
@ -15,7 +14,8 @@ export class NetworkConfigView {
private readonly password: HTMLInputElement;
private readonly mqtt_url: HTMLInputElement;
private readonly base_topic: HTMLInputElement;
private readonly ssidlist: HTMLElement;
constructor(controller: Controller) {
this.ap_ssid = (document.getElementById("ap_ssid") as HTMLInputElement);
this.ap_ssid.onchange = controller.configChanged
@ -28,5 +28,30 @@ export class NetworkConfigView {
this.mqtt_url.onchange = controller.configChanged
this.base_topic = document.getElementById("base_topic") as HTMLInputElement;
this.base_topic.onchange = controller.configChanged
this.ssidlist = document.getElementById("ssidlist") as HTMLElement
let scanWifiBtn = document.getElementById("scan") as HTMLButtonElement;
scanWifiBtn.onclick = function (){
controller.scanWifi();
}
}
setConfig(network: NetworkConfig) {
this.ap_ssid.value = network.ap_ssid;
this.ssid.value = network.ssid;
this.password.value = network.password;
this.mqtt_url.value = network.mqtt_url;
this.base_topic.value = network.base_topic;
}
getConfig(): NetworkConfig {
return {
ap_ssid: this.ap_ssid.value,
ssid: this.ssid.value,
password: this.password.value,
mqtt_url: this.mqtt_url.value,
base_topic: this.base_topic.value
}
}
}

View File

@ -0,0 +1,46 @@
import { Controller } from ".";
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;
constructor(controller:Controller){
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;
this.night_lamp_time_start.onchange = controller.configChanged
for (let i = 0; i < 24; i++) {
let option = document.createElement("option");
if (i == 20){
option.selected = true
}
option.innerText = i.toString();
this.night_lamp_time_start.appendChild(option);
}
this.night_lamp_time_end = document.getElementById("night_lamp_time_end") as HTMLSelectElement;
this.night_lamp_time_end.onchange = controller.configChanged
for (let i = 0; i < 24; i++) {
let option = document.createElement("option");
if (i == 1){
option.selected = true
}
option.innerText = i.toString();
this.night_lamp_time_end.appendChild(option);
}
}
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();
}
getConfig(): NightLampConfig {
return {
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,
}
}
}

View File

@ -1,22 +1,41 @@
declare var PUBLIC_URL: string;
declare const PLANT_COUNT = 8;
import { Controller } from ".";
export class PlantViews {
getConfig(): PlantConfig[] {
const rv: PlantConfig[] = [];
for (let i = 0; i < PLANT_COUNT; i++) {
rv[i] = this.plants[i].getConfig();
}
return rv
}
update(moisture_a: [number], moisture_b: [number]) {
for (let plantId = 0; plantId < PLANT_COUNT; plantId++) {
const a = moisture_a[plantId]
const b = moisture_b[plantId]
this.plants[plantId].update(a,b)
}
}
setConfig(plants: PlantConfig[]) {
for (let plantId = 0; plantId < PLANT_COUNT; plantId++) {
const plantConfig = plants[plantId];
const plantView = this.plants[plantId];
plantView.setConfig(plantConfig)
}
}
private readonly plants: PlantView[] = []
private readonly plantsDiv: HTMLDivElement
constructor(syncConfig:Controller) {
this.plantsDiv = document.getElementById("plants") as HTMLDivElement;
for (let plantId = 0; plantId < 8; plantId++) {
for (let plantId = 0; plantId < PLANT_COUNT; plantId++) {
this.plants[plantId] = new PlantView(plantId, this.plantsDiv, syncConfig);
}
}
getPlant(plantId: number) {
return this.plants[plantId]
}
}
export class PlantView {
@ -113,6 +132,11 @@ export class PlantView {
}
console.log(this)
}
update(a: number, b: number) {
this.moistureA.innerText = String(a)
this.moistureB.innerText = String(b)
}
setConfig(plantConfig: PlantConfig) {
console.log("apply config to ui plant " + this.plantId + " config: " + JSON.stringify(plantConfig))

View File

@ -0,0 +1,47 @@
import { Controller } from ".";
export class TankConfigView {
private readonly max_consecutive_pump_count: HTMLInputElement;
private readonly tank_useable_ml: HTMLInputElement;
private readonly tank_empty_percent: HTMLInputElement;
private readonly tank_full_percent: HTMLInputElement;
private readonly tank_warn_percent: HTMLInputElement;
private readonly tank_sensor_enabled: HTMLInputElement;
private readonly tank_allow_pumping_if_sensor_error: HTMLInputElement;
constructor(controller:Controller){
this.max_consecutive_pump_count = document.getElementById("max_consecutive_pump_count") as HTMLInputElement;
this.max_consecutive_pump_count.onchange = controller.configChanged
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;
this.tank_empty_percent.onchange = controller.configChanged
this.tank_full_percent = document.getElementById("tank_full_percent") as HTMLInputElement;
this.tank_full_percent.onchange = controller.configChanged
this.tank_warn_percent = document.getElementById("tank_warn_percent") as HTMLInputElement;
this.tank_warn_percent.onchange = controller.configChanged
this.tank_sensor_enabled = document.getElementById("tank_sensor_enabled") as HTMLInputElement;
this.tank_sensor_enabled.onchange = controller.configChanged
this.tank_allow_pumping_if_sensor_error = document.getElementById("tank_allow_pumping_if_sensor_error") as HTMLInputElement;
this.tank_allow_pumping_if_sensor_error.onchange = controller.configChanged
}
setConfig(tank: TankConfig) {
this.tank_allow_pumping_if_sensor_error.checked = tank.tank_allow_pumping_if_sensor_error;
this.tank_empty_percent.value = String(tank.tank_empty_percent)
this.tank_warn_percent.value = String(tank.tank_warn_percent)
this.tank_full_percent.value = String(tank.tank_full_percent)
this.tank_sensor_enabled.checked = tank.tank_sensor_enabled
this.tank_useable_ml.value = String(tank.tank_useable_ml)
}
getConfig(): TankConfig {
return {
tank_allow_pumping_if_sensor_error: this.tank_allow_pumping_if_sensor_error.checked,
tank_empty_percent : this.tank_empty_percent.valueAsNumber,
tank_full_percent: this.tank_full_percent.valueAsNumber,
tank_sensor_enabled: this.tank_sensor_enabled.checked,
tank_useable_ml: this.tank_useable_ml.valueAsNumber,
tank_warn_percent: this.tank_warn_percent.valueAsNumber
}
}
}

View File

@ -1,34 +1,23 @@
declare var PUBLIC_URL: string;
import { Controller } from ".";
export class TimeView {
esp_time: HTMLDivElement
rtc_time: HTMLDivElement
browser_time: HTMLDivElement
sync: HTMLButtonElement
constructor() {
this.esp_time = document.getElementById("esp_time") as HTMLDivElement;
this.rtc_time = document.getElementById("rtc_time") as HTMLDivElement;
this.browser_time = document.getElementById("browser_time") as HTMLDivElement;
this.sync = document.getElementById("time_upload") as HTMLButtonElement;
this.sync.onclick = this.syncTimeFromBrowser;
}
update(native: string, rtc: string) {
this.esp_time.innerText = native;
this.rtc_time.innerText = rtc;
var date = new Date();
this.browser_time.innerText = date.toISOString();
}
syncTimeFromBrowser() {
var value: SetTime = {
time: new Date().toISOString()
}
var pretty = JSON.stringify(value, undefined, 1);
fetch(PUBLIC_URL + "/time", {
method: "POST",
body: pretty
})
}
}
export class TimeView {
esp_time: HTMLDivElement
rtc_time: HTMLDivElement
browser_time: HTMLDivElement
sync: HTMLButtonElement
constructor(controller:Controller) {
this.esp_time = document.getElementById("timeview_esp_time") as HTMLDivElement;
this.rtc_time = document.getElementById("timeview_rtc_time") as HTMLDivElement;
this.browser_time = document.getElementById("timeview_browser_time") as HTMLDivElement;
this.sync = document.getElementById("timeview_time_upload") as HTMLButtonElement;
this.sync.onclick = controller.syncRTCFromBrowser;
}
update(native: string, rtc: string) {
this.esp_time.innerText = native;
this.rtc_time.innerText = rtc;
var date = new Date();
this.browser_time.innerText = date.toISOString();
}
}