initial mvc concept

This commit is contained in:
2024-12-11 21:01:11 +01:00
parent 4a8e0188b3
commit 8bd2cb72d0
14 changed files with 922 additions and 808 deletions

View File

@@ -0,0 +1,54 @@
interface PlantControllerConfig {
ap_ssid: string,
ssid: string,
password: string,
mqtt_url: string,
base_topic: string,
tank_sensor_enabled: boolean,
tank_allow_pumping_if_sensor_error: boolean,
tank_useable_ml: number,
tank_warn_percent: number,
tank_empty_percent: number,
tank_full_percent: number,
night_lamp_hour_start: number,
night_lamp_hour_end: number,
night_lamp_only_when_dark: boolean,
max_consecutive_pump_count: number,
plants: PlantConfig[]
}
interface PlantConfig{
mode: string,
target_moisture: number,
pump_time_s: number,
pump_cooldown_min: number,
pump_hour_start: number,
pump_hour_end: number,
sensor_b: boolean
}
interface SSIDList {
ssids: [string]
}
interface TestPump {
pump: number
}
interface SetTime {
time: string
}
interface GetData {
rtc: string,
native: string,
moisture_a: [number],
moisture_b: [number],
}
interface VersionInfo {
git_hash: string,
build_time: string
}

View File

@@ -1,44 +0,0 @@
declare var PUBLIC_URL: string;
let battery_flash_button = document.getElementById("battery_flash_button") as HTMLButtonElement;
let battery_flash_file = document.getElementById("battery_flash_file") as HTMLInputElement;
let battery_flash_message = document.getElementById("battery_flash_message") as HTMLElement;
let battery_flash_progressBar = document.getElementById("battery_flash_progressBar") as HTMLProgressElement;
let battery_flash_loaded_n_total = document.getElementById("battery_flash_loaded_n_total") as HTMLElement;
let battery_flash_status = document.getElementById("battery_flash_status") as HTMLElement;
var ajax = new XMLHttpRequest();
ajax.upload.addEventListener("progress", event => {
battery_flash_loaded_n_total.innerHTML = "Uploaded " + event.loaded + " bytes of " + event.total;
var percent = (event.loaded / event.total) * 100;
battery_flash_progressBar.value = Math.round(percent);
battery_flash_status.innerHTML = Math.round(percent) + "%";
battery_flash_message.innerHTML = "in progress";
}, false);
ajax.addEventListener("load", () => {
battery_flash_status.innerHTML = ajax.responseText;
battery_flash_message.innerHTML = "finished";
battery_flash_progressBar.value = 0;
}, false);
ajax.addEventListener("error", () => {
battery_flash_status.innerHTML = ajax.responseText;
battery_flash_message.innerHTML = "failed";
}, false);
ajax.addEventListener("abort", () => {
battery_flash_status.innerHTML = ajax.responseText;
battery_flash_message.innerHTML = "aborted";
}, false);
battery_flash_button.onclick = async function (){
//ajax.open("POST", "/flashbattery");
//ajax.send(battery_flash_file.files[0]);
ajax.open("POST", PUBLIC_URL + "/flashbattery?flash=true");
ajax.send(battery_flash_file.files[0]);
};

View File

@@ -1,484 +0,0 @@
declare var PUBLIC_URL: string;
console.log("Url is " + PUBLIC_URL);
interface PlantConfig {
ap_ssid: string,
ssid: string,
password: string,
mqtt_url: string,
base_topic: string,
tank_sensor_enabled: boolean,
tank_allow_pumping_if_sensor_error: boolean,
tank_useable_ml: number,
tank_warn_percent: number,
tank_empty_percent: number,
tank_full_percent: number,
night_lamp_hour_start: number,
night_lamp_hour_end: number,
night_lamp_only_when_dark: boolean,
max_consecutive_pump_count: number,
plants: {
mode: string,
target_moisture: number,
pump_time_s: number,
pump_cooldown_min: number,
pump_hour_start: number,
pump_hour_end: number,
sensor_b: boolean,
sensor_p: boolean
}[]
}
interface SSIDList {
ssids : [string]
}
interface TestPump{
pump: number
}
interface SetTime{
time: string
}
interface GetData{
rtc: string,
native: string,
moisture_a: [number],
moisture_b: [number],
}
let plants = document.getElementById("plants") as HTMLInputElement;
let scanWifiBtn = document.getElementById("scan") as HTMLButtonElement;
if(scanWifiBtn){
scanWifiBtn.onclick = scanWifi;
}
let esp_time = document.getElementById("esp_time") as HTMLDivElement;
let rtc_time = document.getElementById("rtc_time") as HTMLDivElement;
let browser_time = document.getElementById("browser_time") as HTMLDivElement;
let sync = document.getElementById("time_upload") as HTMLButtonElement;
sync.onclick = setTime
function setTime(){
var value: SetTime = {
time : new Date().toISOString()
}
var pretty = JSON.stringify(value, undefined, 1);
fetch(PUBLIC_URL + "/time", {
method :"POST",
body: pretty
})
}
function updateTime(){
fetch(PUBLIC_URL +"/data")
.then(response => response.json())
.then(json => json as GetData)
.then(time => {
esp_time.innerText = time.native;
rtc_time.innerText = time.rtc;
var date = new Date();
browser_time.innerText = date.toISOString();
time.moisture_a.forEach((a, index) => {
var id = "plant_" + index + "_moisture_a";
console.log("id is " + id + "index is " + index)
var target = document.getElementById(id) as HTMLDivElement;
target.innerText = a+"";
})
time.moisture_b.forEach((b, index) => {
var id = "plant_" + index + "_moisture_b";
var target = document.getElementById(id) as HTMLDivElement;
target.innerText = b+"";
})
setTimeout(updateTime,1000);
})
.catch(error => {
console.log(error);
setTimeout(updateTime,10000);
});
}
setTimeout(updateTime,1000);
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")
ssidlist.innerHTML = ''
for (var ssid of data.ssids) {
var wi = document.createElement("option");
wi.value = ssid;
ssidlist.appendChild(wi);
}
}
let fromWrapper = (() => {
let plantcount = 0;
function addTimeOptions(select: HTMLSelectElement) {
for (let i = 0; i < 24; i++) {
let option = document.createElement("option");
option.innerText = i.toString();
select.appendChild(option);
}
}
var ap_ssid = (document.getElementById("ap_ssid") as HTMLInputElement);
ap_ssid.onchange = updateJson
var ssid = (document.getElementById("ssid") as HTMLInputElement);
ssid.onchange = updateJson
var password = (document.getElementById("password") as HTMLInputElement);
password.onchange = updateJson
let mqtt_url = document.getElementById("mqtt_url") as HTMLInputElement;
mqtt_url.onchange = updateJson
let base_topic = document.getElementById("base_topic") as HTMLInputElement;
base_topic.onchange = updateJson
let max_consecutive_pump_count = document.getElementById("max_consecutive_pump_count") as HTMLInputElement;
max_consecutive_pump_count.onchange = updateJson
let tank_useable_ml = document.getElementById("tank_useable_ml") as HTMLInputElement;
tank_useable_ml.onchange = updateJson
let tank_empty_percent = document.getElementById("tank_empty_percent") as HTMLInputElement;
tank_empty_percent.onchange = updateJson
let tank_full_percent = document.getElementById("tank_full_percent") as HTMLInputElement;
tank_full_percent.onchange = updateJson
let tank_warn_percent = document.getElementById("tank_warn_percent") as HTMLInputElement;
tank_warn_percent.onchange = updateJson
let tank_sensor_enabled = document.getElementById("tank_sensor_enabled") as HTMLInputElement;
tank_sensor_enabled.onchange = updateJson
let tank_allow_pumping_if_sensor_error = document.getElementById("tank_allow_pumping_if_sensor_error") as HTMLInputElement;
tank_allow_pumping_if_sensor_error.onchange = updateJson
let night_lamp_only_when_dark = document.getElementById("night_lamp_only_when_dark") as HTMLInputElement;
night_lamp_only_when_dark.onchange = updateJson
let night_lamp_time_start = document.getElementById("night_lamp_time_start") as HTMLSelectElement;
night_lamp_time_start.onchange = updateJson
addTimeOptions(night_lamp_time_start);
let night_lamp_time_end = document.getElementById("night_lamp_time_end") as HTMLSelectElement;
night_lamp_time_end.onchange = updateJson
addTimeOptions(night_lamp_time_end);
let json = document.getElementById('json') as HTMLInputElement
function createForm(current: PlantConfig) {
for (let i = 0; i < current.plants.length; i++) {
let plant = document.createElement("div");
plants.appendChild(plant);
let header = document.createElement("h4");
header.textContent = "Plant " + (i + 1);
plant.appendChild(header);
{
let holder = document.createElement("div");
plant.appendChild(holder);
let testButton = document.createElement("button");
testButton.innerText = "Test";
testButton.id = "plant_" + i + "_test";
testButton.onclick = function (){
var body:TestPump = {
pump: i
}
var pretty = JSON.stringify(body, undefined, 1);
fetch("/pumptest", {
method :"POST",
body: pretty
})
.then(response => response.text())
.then(text => submit_status.innerText = text)
};
holder.appendChild(testButton);
let moisture_a = document.createElement("div");
moisture_a.innerText = "N/A";
moisture_a.id = "plant_" + i + "_moisture_a";
holder.appendChild(moisture_a);
let moisture_b = document.createElement("div");
moisture_b.innerText = "N/A";
moisture_b.id = "plant_" + i + "_moisture_b";
holder.appendChild(moisture_b);
let br = document.createElement("br");
holder.appendChild(br);
let inputf = document.createElement("select");
inputf.id = "plant_" + i + "_mode";
inputf.onchange = updateJson;
holder.appendChild(inputf)
let optionOff = document.createElement("option");
optionOff.value = "OFF";
optionOff.innerText = "Off";
inputf.appendChild(optionOff);
let optionTargetMoisture = document.createElement("option");
optionTargetMoisture.value = "TargetMoisture";
optionTargetMoisture.innerText = "Target Moisture";
inputf.appendChild(optionTargetMoisture);
let optionTimerOnly = document.createElement("option");
optionTimerOnly.value = "TimerOnly";
optionTimerOnly.innerText = "Timer";
inputf.appendChild(optionTimerOnly);
let text = document.createElement("span");
holder.appendChild(text)
text.innerHTML += "Mode"
}
{
let holder = document.createElement("div");
plant.appendChild(holder);
let inputf = document.createElement("input");
inputf.id = "plant_" + i + "_target_moisture";
inputf.onchange = updateJson;
inputf.type = "number";
inputf.min = "0";
inputf.max = "100";
holder.appendChild(inputf)
let text = document.createElement("span");
holder.appendChild(text)
text.innerHTML += "Target Moisture"
}
{
let holder = document.createElement("div");
plant.appendChild(holder);
let input = document.createElement("input");
input.id = "plant_" + i + "_pump_time_s";
input.onchange = updateJson;
input.type = "number";
input.min = "0";
input.max = "600";
holder.appendChild(input)
let text = document.createElement("span");
holder.appendChild(text)
text.innerHTML += "Pump Time (s)"
}
{
let holder = document.createElement("div");
plant.appendChild(holder);
let input = document.createElement("input");
input.id = "plant_" + i + "_pump_cooldown_min";
input.onchange = updateJson;
input.type = "number";
input.min = "0";
input.max = "600";
holder.appendChild(input)
let text = document.createElement("span");
holder.appendChild(text)
text.innerHTML += "Pump Cooldown (m)"
}
{
let holder = document.createElement("div");
plant.appendChild(holder);
let input = document.createElement("select");
input.id = "plant_" + i + "_pump_hour_start";
addTimeOptions(input);
input.onchange = updateJson;
holder.appendChild(input)
let text = document.createElement("span");
holder.appendChild(text)
text.innerHTML += "Pump Hour Start"
}
{
let holder = document.createElement("div");
plant.appendChild(holder);
let input = document.createElement("select");
input.id = "plant_" + i + "_pump_hour_end";
addTimeOptions(input);
input.onchange = updateJson;
holder.appendChild(input)
let text = document.createElement("span");
holder.appendChild(text)
text.innerHTML += "Pump Hour End"
}
{
let holder = document.createElement("div");
plant.appendChild(holder);
let input = document.createElement("input");
input.id = "plant_" + i + "_sensor_b";
input.type = "checkbox";
input.onchange = updateJson;
holder.appendChild(input)
let text = document.createElement("span");
holder.appendChild(text)
text.innerHTML += "Sensor B installed"
}
{
let holder = document.createElement("div");
plant.appendChild(holder);
let input = document.createElement("input");
input.id = "plant_" + i + "_sensor_p";
input.type = "checkbox";
input.onchange = updateJson;
holder.appendChild(input)
let text = document.createElement("span");
holder.appendChild(text)
text.innerHTML += "Sensor P installed"
}
}
sync(current);
}
function sync(current: PlantConfig) {
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++) {
let plant_mode = document.getElementById("plant_" + i + "_mode") as HTMLSelectElement;
plant_mode.value = current.plants[i].mode;
let plant_target_moisture = document.getElementById("plant_" + i + "_target_moisture") as HTMLInputElement;
plant_target_moisture.value = current.plants[i].target_moisture.toString();
let plant_pump_time_s = document.getElementById("plant_" + i + "_pump_time_s") as HTMLInputElement;
plant_pump_time_s.value = current.plants[i].pump_time_s.toString();
let plant_pump_cooldown_min = document.getElementById("plant_" + i + "_pump_cooldown_min") as HTMLInputElement;
plant_pump_cooldown_min.value = current.plants[i].pump_cooldown_min.toString();
let plant_pump_hour_start = document.getElementById("plant_" + i + "_pump_hour_start") as HTMLInputElement;
plant_pump_hour_start.value = current.plants[i].pump_hour_start.toString();
let plant_pump_hour_end = document.getElementById("plant_" + i + "_pump_hour_end") as HTMLInputElement;
plant_pump_hour_end.value = current.plants[i].pump_hour_end.toString();
let plant_sensor_b = document.getElementById("plant_" + i + "_sensor_b") as HTMLInputElement;
plant_sensor_b.checked = current.plants[i].sensor_b;
let plant_sensor_p = document.getElementById("plant_" + i + "_sensor_p") as HTMLInputElement;
plant_sensor_p.checked = current.plants[i].sensor_p;
}
}
function updateJson() {
var current: PlantConfig = {
ap_ssid: ap_ssid.value,
ssid: ssid.value,
password: password.value,
max_consecutive_pump_count: +max_consecutive_pump_count.value,
mqtt_url: mqtt_url.value,
base_topic: base_topic.value,
tank_allow_pumping_if_sensor_error: tank_allow_pumping_if_sensor_error.checked,
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: []
}
for (let i = 0; i < plantcount; i++) {
console.log("Adding plant " + i)
let plant_mode = document.getElementById("plant_" + i + "_mode") as HTMLSelectElement;
let plant_target_moisture = document.getElementById("plant_" + i + "_target_moisture") as HTMLInputElement;
let plant_pump_time_s = document.getElementById("plant_" + i + "_pump_time_s") as HTMLInputElement;
let plant_pump_cooldown_min = document.getElementById("plant_" + i + "_pump_cooldown_min") as HTMLInputElement;
let plant_pump_hour_start = document.getElementById("plant_" + i + "_pump_hour_start") as HTMLInputElement;
let plant_pump_hour_end = document.getElementById("plant_" + i + "_pump_hour_end") as HTMLInputElement;
let plant_sensor_b = document.getElementById("plant_" + i + "_sensor_b") as HTMLInputElement;
let plant_sensor_p = document.getElementById("plant_" + i + "_sensor_p") as HTMLInputElement;
current.plants[i] = {
mode: plant_mode.value,
target_moisture: +plant_target_moisture.value,
pump_time_s: +plant_pump_time_s.value,
pump_cooldown_min: +plant_pump_cooldown_min.value,
pump_hour_start: +plant_pump_hour_start.value,
pump_hour_end: +plant_pump_hour_end.value,
sensor_b: plant_sensor_b.checked,
sensor_p: plant_sensor_p.checked
}
}
sync(current);
console.log(current);
var pretty = JSON.stringify(current, undefined, 1);
json.value = pretty;
}
let submitFormBtn = document.getElementById("submit") as HTMLButtonElement
let submit_status = document.getElementById("submit_status")
if (submitFormBtn) {
submitFormBtn.onclick = function (){
updateJson()
fetch(PUBLIC_URL+"/set_config", {
method :"POST",
body: json.value,
})
.then(response => response.text())
.then(text => submit_status.innerText = text)
};
}
fetch(PUBLIC_URL+"/get_config")
.then(response => response.json())
.then(loaded => {
var currentConfig = loaded as PlantConfig;
createForm(currentConfig);
var pretty = JSON.stringify(currentConfig, undefined, 1);
json.value = pretty;
}
)
})
if (plants) {
fromWrapper()
}

View File

@@ -0,0 +1,348 @@
declare var PUBLIC_URL: string;
console.log("Url is " + PUBLIC_URL);
import { TimeView } from "./timeview";
import { PlantView, PlantViews } from "./plant";
export class Controller{
private readonly timeView: TimeView
readonly plantViews:PlantViews
constructor(){
this.timeView = new TimeView()
this.plantViews = new PlantViews(this)
}
configChanged() {
updateJson()
}
testPlant(plantId: number) {
var body: TestPump = {
pump: plantId
}
var pretty = JSON.stringify(body, undefined, 1);
fetch(PUBLIC_URL + "/pumptest", {
method: "POST",
body: pretty
})
.then(response => response.text())
.then(text => console.log(text))
}
updateRealTimeData() {
fetch(PUBLIC_URL + "/data")
.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);
})
setTimeout(controller.updateRealTimeData, 1000);
})
.catch(error => {
console.log(error);
setTimeout(controller.updateRealTimeData, 10000);
});
}
}
const controller = new Controller();
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);
}
}
var ap_ssid = (document.getElementById("ap_ssid") as HTMLInputElement);
ap_ssid.onchange = updateJson
var ssid = (document.getElementById("ssid") as HTMLInputElement);
ssid.onchange = updateJson
var password = (document.getElementById("password") as HTMLInputElement);
password.onchange = updateJson
let mqtt_url = document.getElementById("mqtt_url") as HTMLInputElement;
mqtt_url.onchange = updateJson
let base_topic = document.getElementById("base_topic") as HTMLInputElement;
base_topic.onchange = updateJson
let max_consecutive_pump_count = document.getElementById("max_consecutive_pump_count") as HTMLInputElement;
max_consecutive_pump_count.onchange = updateJson
let tank_useable_ml = document.getElementById("tank_useable_ml") as HTMLInputElement;
tank_useable_ml.onchange = updateJson
let tank_empty_percent = document.getElementById("tank_empty_percent") as HTMLInputElement;
tank_empty_percent.onchange = updateJson
let tank_full_percent = document.getElementById("tank_full_percent") as HTMLInputElement;
tank_full_percent.onchange = updateJson
let tank_warn_percent = document.getElementById("tank_warn_percent") as HTMLInputElement;
tank_warn_percent.onchange = updateJson
let tank_sensor_enabled = document.getElementById("tank_sensor_enabled") as HTMLInputElement;
tank_sensor_enabled.onchange = updateJson
let tank_allow_pumping_if_sensor_error = document.getElementById("tank_allow_pumping_if_sensor_error") as HTMLInputElement;
tank_allow_pumping_if_sensor_error.onchange = updateJson
let night_lamp_only_when_dark = document.getElementById("night_lamp_only_when_dark") as HTMLInputElement;
night_lamp_only_when_dark.onchange = updateJson
let night_lamp_time_start = document.getElementById("night_lamp_time_start") as HTMLSelectElement;
night_lamp_time_start.onchange = updateJson
addTimeOptions(night_lamp_time_start);
let night_lamp_time_end = document.getElementById("night_lamp_time_end") as HTMLSelectElement;
night_lamp_time_end.onchange = updateJson
addTimeOptions(night_lamp_time_end);
function updateJson() {
var current: PlantControllerConfig = {
ap_ssid: ap_ssid.value,
ssid: ssid.value,
password: password.value,
max_consecutive_pump_count: +max_consecutive_pump_count.value,
mqtt_url: mqtt_url.value,
base_topic: base_topic.value,
tank_allow_pumping_if_sensor_error: tank_allow_pumping_if_sensor_error.checked,
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: []
}
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
if (submitFormBtn) {
submitFormBtn.onclick = function () {
updateJson()
fetch(PUBLIC_URL + "/set_config", {
method: "POST",
body: json.value,
})
.then(response => response.text())
.then(text => submit_status.innerText = text)
};
}
fetch(PUBLIC_URL + "/get_config")
.then(response => response.json())
.then(loaded => {
var currentConfig = loaded as PlantControllerConfig;
sync(currentConfig);
var pretty = JSON.stringify(currentConfig, undefined, 1);
json.value = pretty;
}
)
})
fromWrapper()
export function uploadFile() {
var file1 = document.getElementById("file1") as HTMLInputElement;
var loaded_n_total = document.getElementById("loaded_n_total") as HTMLElement;
var progressBar = document.getElementById("progressBar") as HTMLProgressElement;
var status = document.getElementById("status") as HTMLElement;
var answer = document.getElementById("answer") as HTMLElement;
if (file1.files == null){
//TODO error dialog here
return
}
var file = file1.files[0];
var ajax = new XMLHttpRequest();
ajax.upload.addEventListener("progress", event => {
loaded_n_total.innerHTML = "Uploaded " + event.loaded + " bytes of " + event.total;
var percent = (event.loaded / event.total) * 100;
progressBar.value = Math.round(percent);
status.innerHTML = Math.round(percent) + "%";
answer.innerHTML = "in progress";
}, false);
ajax.addEventListener("load", () => {
status.innerHTML = ajax.responseText;
answer.innerHTML = "finished";
progressBar.value = 0;
}, false);
ajax.addEventListener("error", () => {
status.innerHTML = ajax.responseText;
answer.innerHTML = "failed";
}, false);
ajax.addEventListener("abort", () => {
status.innerHTML = ajax.responseText;
answer.innerHTML = "aborted";
}, false);
ajax.open("POST", PUBLIC_URL + "/ota");
ajax.send(file);
}
let file1Upload = document.getElementById("file1") as HTMLInputElement;
file1Upload.onchange = uploadFile;
let firmware_buildtime = document.getElementById("firmware_buildtime") as HTMLDivElement;
let firmware_githash = document.getElementById("firmware_githash") as HTMLDivElement;
document.addEventListener('DOMContentLoaded', function () {
fetch(PUBLIC_URL + "/version")
.then(response => response.json())
.then(json => json as VersionInfo)
.then(versionInfo => {
firmware_buildtime.innerText = versionInfo.build_time;
firmware_githash.innerText = versionInfo.git_hash;
})
}, false);
let battery_flash_button = document.getElementById("battery_flash_button") as HTMLButtonElement;
let battery_flash_file = document.getElementById("battery_flash_file") as HTMLInputElement;
let battery_flash_message = document.getElementById("battery_flash_message") as HTMLElement;
let battery_flash_progressBar = document.getElementById("battery_flash_progressBar") as HTMLProgressElement;
let battery_flash_loaded_n_total = document.getElementById("battery_flash_loaded_n_total") as HTMLElement;
let battery_flash_status = document.getElementById("battery_flash_status") as HTMLElement;
var ajax = new XMLHttpRequest();
ajax.upload.addEventListener("progress", event => {
battery_flash_loaded_n_total.innerHTML = "Uploaded " + event.loaded + " bytes of " + event.total;
var percent = (event.loaded / event.total) * 100;
battery_flash_progressBar.value = Math.round(percent);
battery_flash_status.innerHTML = Math.round(percent) + "%";
battery_flash_message.innerHTML = "in progress";
}, false);
ajax.addEventListener("load", () => {
battery_flash_status.innerHTML = ajax.responseText;
battery_flash_message.innerHTML = "finished";
battery_flash_progressBar.value = 0;
}, false);
ajax.addEventListener("error", () => {
battery_flash_status.innerHTML = ajax.responseText;
battery_flash_message.innerHTML = "failed";
}, false);
ajax.addEventListener("abort", () => {
battery_flash_status.innerHTML = ajax.responseText;
battery_flash_message.innerHTML = "aborted";
}, false);
battery_flash_button.onclick = async function () {
//ajax.open("POST", "/flashbattery");
//ajax.send(battery_flash_file.files[0]);
ajax.open("POST", PUBLIC_URL + "/flashbattery?flash=true");
ajax.send(battery_flash_file.files![0]);
};

View File

@@ -1,58 +0,0 @@
declare var PUBLIC_URL: string;
export function uploadFile() {
var file1 = document.getElementById("file1") as HTMLInputElement;
var loaded_n_total = document.getElementById("loaded_n_total");
var progressBar = document.getElementById("progressBar") as HTMLProgressElement;
var status = document.getElementById("status");
var answer = document.getElementById("answer");
var file = file1.files[0];
var ajax = new XMLHttpRequest();
ajax.upload.addEventListener("progress", event => {
loaded_n_total.innerHTML = "Uploaded " + event.loaded + " bytes of " + event.total;
var percent = (event.loaded / event.total) * 100;
progressBar.value = Math.round(percent);
status.innerHTML = Math.round(percent) + "%";
answer.innerHTML = "in progress";
}, false);
ajax.addEventListener("load", () => {
status.innerHTML = ajax.responseText;
answer.innerHTML = "finished";
progressBar.value = 0;
}, false);
ajax.addEventListener("error", () => {
status.innerHTML = ajax.responseText;
answer.innerHTML = "failed";
}, false);
ajax.addEventListener("abort", () => {
status.innerHTML = ajax.responseText;
answer.innerHTML = "aborted";
}, false);
ajax.open("POST", PUBLIC_URL+"/ota");
ajax.send(file);
}
interface VersionInfo{
git_hash:string,
build_time: string
}
let file1Upload = document.getElementById("file1") as HTMLInputElement;
file1Upload.onchange = uploadFile;
let firmware_buildtime = document.getElementById("firmware_buildtime") as HTMLDivElement;
let firmware_githash = document.getElementById("firmware_githash") as HTMLDivElement;
document.addEventListener('DOMContentLoaded', function() {
fetch(PUBLIC_URL+"/version")
.then(response => response.json())
.then(json => json as VersionInfo)
.then(versionInfo => {
firmware_buildtime.innerText = versionInfo.build_time;
firmware_githash.innerText = versionInfo.git_hash;
})
}, false);

View File

@@ -0,0 +1,31 @@
<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>
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">
</div>

View File

@@ -0,0 +1,142 @@
declare var PUBLIC_URL: string;
import { Controller } from ".";
export class PlantViews {
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++) {
this.plants[plantId] = new PlantView(plantId, this.plantsDiv, syncConfig);
}
}
getPlant(plantId: number) {
return this.plants[plantId]
}
}
export class PlantView {
private readonly plantId: number;
private readonly plantDiv: HTMLDivElement;
private readonly header: HTMLElement;
private readonly testButton: HTMLButtonElement;
private readonly targetMoisture: HTMLInputElement;
private readonly pumpTimeS: HTMLInputElement;
private readonly pumpCooldown: HTMLInputElement;
private readonly pumpHourStart: HTMLSelectElement;
private readonly pumpHourEnd: HTMLSelectElement;
private readonly sensorBInstalled: HTMLInputElement;
private readonly mode: HTMLSelectElement;
private readonly moistureA: HTMLElement;
private readonly moistureB: HTMLElement;
constructor(plantId: number, parent:HTMLDivElement, controller:Controller) {
const dummy = this;
this.plantId = plantId;
this.plantDiv = document.createElement("div")! as HTMLDivElement
const template = require('./plant.html') as string;
const plantRaw = template.replaceAll("${plantId}", String(plantId));
this.plantDiv.innerHTML = plantRaw
parent.appendChild(this.plantDiv)
this.header = document.getElementById("plant_"+plantId+"_header")!
this.header.innerText = "Plant "+ (this.plantId+1)
this.moistureA = document.getElementById("plant_"+plantId+"_moisture_a")! as HTMLElement;
this.moistureB = document.getElementById("plant_"+plantId+"_moisture_b")! as HTMLElement;
this.testButton = document.getElementById("plant_"+plantId+"_test")! as HTMLButtonElement;
this.testButton.onclick = function(){
controller.testPlant(plantId)
}
this.mode = document.getElementById("plant_"+plantId+"_mode") as HTMLSelectElement
this.mode.onchange = function(){
controller.configChanged()
}
this.targetMoisture = document.getElementById("plant_"+plantId+"_target_moisture")! as HTMLInputElement;
this.targetMoisture.onchange = function(){
controller.configChanged()
}
this.pumpTimeS = document.getElementById("plant_"+plantId+"_pump_time_s") as HTMLInputElement;
this.pumpTimeS.onchange = function(){
controller.configChanged()
}
this.pumpCooldown = document.getElementById("plant_"+plantId+"_pump_cooldown_min") as HTMLInputElement;
this.pumpCooldown.onchange = function(){
controller.configChanged()
}
this.pumpHourStart = document.getElementById("plant_"+plantId+"_pump_hour_start") as HTMLSelectElement;
this.pumpHourStart.onchange = function(){
controller.configChanged()
}
for (let i = 0; i < 24; i++) {
let option = document.createElement("option");
if (i == 10){
option.selected = true
}
option.innerText = i.toString();
this.pumpHourStart.appendChild(option);
}
this.pumpHourEnd = document.getElementById("plant_"+plantId+"_pump_hour_end") as HTMLSelectElement;
this.pumpHourEnd.onchange = function(){
controller.configChanged()
}
for (let i = 0; i < 24; i++) {
let option = document.createElement("option");
if (i == 19){
option.selected = true
}
option.innerText = i.toString();
this.pumpHourEnd.appendChild(option);
}
this.sensorBInstalled = document.getElementById("plant_"+plantId+"_sensor_b") as HTMLInputElement;
this.sensorBInstalled.onchange = function(){
controller.configChanged()
}
console.log(this)
}
setConfig(plantConfig: PlantConfig) {
console.log("apply config to ui plant " + this.plantId + " config: " + JSON.stringify(plantConfig))
this.mode.value = plantConfig.mode;
this.targetMoisture.value = plantConfig.target_moisture.toString();
this.pumpTimeS.value = plantConfig.pump_time_s.toString();
this.pumpCooldown.value = plantConfig.pump_cooldown_min.toString();
this.pumpHourStart.value = plantConfig.pump_hour_start.toString();
this.pumpHourEnd.value = plantConfig.pump_hour_end.toString();
this.sensorBInstalled.checked = plantConfig.sensor_b
}
getConfig() :PlantConfig {
const rv:PlantConfig = {
mode: this.mode.value,
target_moisture: +this.targetMoisture.value,
pump_time_s: +this.pumpTimeS.value,
pump_cooldown_min: +this.pumpCooldown.value,
pump_hour_start: +this.pumpHourStart.value,
pump_hour_end: +this.pumpHourEnd.value,
sensor_b: this.sensorBInstalled.checked,
}
return rv
}
setMoistureA(a: number) {
this.moistureA.innerText = String(a);
}
setMoistureB(b: number) {
this.moistureB.innerText = String(b);
}
}

View File

@@ -0,0 +1,34 @@
declare var PUBLIC_URL: string;
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
})
}
}