import { deepEqual } from 'fast-equals'; declare var PUBLIC_URL: string; console.log("Url is " + PUBLIC_URL); document.body.innerHTML = require('./main.html') as string; import { TimeView } from "./timeview"; import { PlantView, PlantViews } from "./plant"; import { NetworkConfigView } from "./network"; import { NightLampView } from "./nightlightview"; import { TankConfigView } from "./tankview"; import { SubmitView } from "./submitView"; import { ProgressView } from "./progress"; import { OTAView } from "./ota"; import { BatteryView } from "./batteryview"; import { FileView } from './fileview'; export class Controller { getBackupInfo() { fetch(PUBLIC_URL + "/backup_info") .then(response => response.json()) .then(json => json as BackupHeader) .then(header => { controller.submitView.setBackupInfo(header) }) .catch(error => { console.log(error); }); } updateFileList() { fetch(PUBLIC_URL + "/files") .then(response => response.json()) .then(json => json as FileList) .then(filelist => { controller.fileview.setFileList(filelist, PUBLIC_URL) }) .catch(error => { console.log(error); }); } uploadFile(file: File, name:string) { var current = 0; var max = 100; controller.progressview.addProgress("file_upload", (current / max) * 100, "Uploading File " + name + "(" + current + "/" + max + ")") var ajax = new XMLHttpRequest(); ajax.upload.addEventListener("progress", event => { current = event.loaded / 1000; max = event.total / 1000; controller.progressview.addProgress("file_upload", (current / max) * 100, "Uploading File " + name + "(" + current + "/" + max + ")") }, false); ajax.addEventListener("load", () => { controller.progressview.removeProgress("file_upload") controller.updateFileList() }, false); ajax.addEventListener("error", () => { alert("Error upload") controller.progressview.removeProgress("file_upload") controller.updateFileList() }, false); ajax.addEventListener("abort", () => { alert("abort upload") controller.progressview.removeProgress("file_upload") controller.updateFileList() }, false); ajax.open("POST", PUBLIC_URL + "/file?filename="+name); ajax.send(file); } deleteFile(name:string) { controller.progressview.addIndeterminate("file_delete", "Deleting " + name); var ajax = new XMLHttpRequest(); ajax.open("DELETE", PUBLIC_URL + "/file?filename="+name); ajax.send(); ajax.addEventListener("error", () => { controller.progressview.removeProgress("file_delete") alert("Error delete") controller.updateFileList() }, false); ajax.addEventListener("abort", () => { controller.progressview.removeProgress("file_delete") alert("Error upload") controller.updateFileList() }, false); ajax.addEventListener("load", () => { controller.progressview.removeProgress("file_delete") controller.updateFileList() }, false); controller.updateFileList() } updateRTCData() { fetch(PUBLIC_URL + "/time") .then(response => response.json()) .then(json => json as GetTime) .then(time => { controller.timeView.update(time.native, time.rtc) }) .catch(error => { controller.timeView.update("n/a", "n/a") console.log(error); }); } updateBatteryData() { fetch(PUBLIC_URL + "/battery") .then(response => response.json()) .then(json => json as BatteryState) .then(battery => { controller.batteryView.update(battery) }) .catch(error => { controller.batteryView.update(null) console.log(error); }); } uploadNewFirmware(file: File) { var current = 0; var max = 100; controller.progressview.addProgress("ota_upload", (current / max) * 100, "Uploading firmeware (" + current + "/" + max + ")") var ajax = new XMLHttpRequest(); ajax.upload.addEventListener("progress", event => { current = event.loaded / 1000; max = event.total / 1000; controller.progressview.addProgress("ota_upload", (current / max) * 100, "Uploading firmeware (" + current + "/" + max + ")") }, false); ajax.addEventListener("load", () => { //TODO wait for reboot here! controller.progressview.removeProgress("ota_upload") }, false); ajax.addEventListener("error", () => { alert("Error ota") controller.progressview.removeProgress("ota_upload") }, false); ajax.addEventListener("abort", () => { alert("abort ota") controller.progressview.removeProgress("ota_upload") }, false); ajax.open("POST", PUBLIC_URL + "/ota"); ajax.send(file); } version() { controller.progressview.addIndeterminate("version", "Getting buildVersion") fetch(PUBLIC_URL + "/version") .then(response => response.json()) .then(json => json as VersionInfo) .then(versionInfo => { controller.progressview.removeProgress("version") controller.firmWareView.setVersion(versionInfo); }) } getBackupConfig() { controller.progressview.addIndeterminate("get_backup_config", "Downloading Backup") fetch(PUBLIC_URL + "/get_backup_config") .then(response => response.text()) .then(loaded => { controller.progressview.removeProgress("get_backup_config") controller.submitView.setBackupJson(loaded); }) } downloadConfig() { controller.progressview.addIndeterminate("get_config", "Downloading Config") fetch(PUBLIC_URL + "/get_config") .then(response => response.json()) .then(loaded => { var currentConfig = loaded as PlantControllerConfig; controller.setInitialConfig(currentConfig); controller.setConfig(currentConfig); //sync json view initially this.configChanged(); controller.progressview.removeProgress("get_config") }) } setInitialConfig(currentConfig: PlantControllerConfig) { this.initialConfig = currentConfig } uploadConfig(json: string, statusCallback: (status: string) => void) { controller.progressview.addIndeterminate("set_config", "Uploading Config") fetch(PUBLIC_URL + "/set_config", { method: "POST", body: json, }) .then(response => response.text()) .then(text => statusCallback(text)) controller.progressview.removeProgress("set_config") //load from remote to be clean controller.downloadConfig() } backupConfig(json: string, statusCallback: (status: string) => void) { controller.progressview.addIndeterminate("backup_config", "Backingup Config") fetch(PUBLIC_URL + "/backup_config", { method: "POST", body: json, }) .then(response => response.text()) .then(text => statusCallback(text)) controller.progressview.removeProgress("backup_config") } syncRTCFromBrowser() { controller.progressview.addIndeterminate("write_rtc", "Writing RTC") var value: SetTime = { time: new Date().toISOString() } var pretty = JSON.stringify(value, undefined, 1); fetch(PUBLIC_URL + "/time", { method: "POST", body: pretty }).then( _ => controller.progressview.removeProgress("write_rtc") ) } configChanged() { const current = controller.getConfig(); var pretty = JSON.stringify(current, undefined, 0); var initial = JSON.stringify(this.initialConfig, undefined, 0); controller.submitView.setJson(pretty); if (deepEqual(current, controller.initialConfig)) { document.title = "PlantCtrl" } else { document.title = "*PlantCtrl" } } testPlant(plantId: number) { let counter = 0 let limit = 30 controller.progressview.addProgress("test_pump", counter / limit * 100, "Testing pump " + (plantId + 1) + " for " + (limit - counter) + "s") let timerId: string | number | NodeJS.Timeout | undefined function updateProgress() { counter++; controller.progressview.addProgress("test_pump", counter / limit * 100, "Testing pump " + (plantId + 1) + " for " + (limit - counter) + "s") timerId = setTimeout(updateProgress, 1000); } timerId = setTimeout(updateProgress, 1000); 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 => { clearTimeout(timerId); controller.progressview.removeProgress("test_pump"); } ) } getConfig(): PlantControllerConfig { return { network: controller.networkView.getConfig(), tank: controller.tankView.getConfig(), night_lamp: controller.nightLampView.getConfig(), plants: controller.plantViews.getConfig() } } scanWifi() { let counter = 0 let limit = 5 controller.progressview.addProgress("scan_ssid", counter / limit * 100, "Scanning for SSIDs for " + (limit - counter) + "s") let timerId: string | number | NodeJS.Timeout | undefined function updateProgress() { counter++; controller.progressview.addProgress("scan_ssid", counter / limit * 100, "Scanning for SSIDs for " + (limit - counter) + "s") timerId = setTimeout(updateProgress, 1000); } timerId = setTimeout(updateProgress, 1000); var ajax = new XMLHttpRequest(); ajax.responseType = 'json'; ajax.onreadystatechange = () => { if (ajax.readyState === 4) { clearTimeout(timerId); controller.progressview.removeProgress("scan_ssid"); this.networkView.setScanResult(ajax.response as SSIDList) } }; ajax.onerror = (evt) => { clearTimeout(timerId); controller.progressview.removeProgress("scan_ssid"); 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.night_lamp); this.plantViews.setConfig(current.plants); } measure_moisture() { let counter = 0 let limit = 2 controller.progressview.addProgress("measure_moisture", counter / limit * 100, "Measure Moisture " + (limit - counter) + "s") let timerId: string | number | NodeJS.Timeout | undefined function updateProgress() { counter++; controller.progressview.addProgress("measure_moisture", counter / limit * 100, "Measure Moisture " + (limit - counter) + "s") timerId = setTimeout(updateProgress, 1000); } timerId = setTimeout(updateProgress, 1000); fetch(PUBLIC_URL + "/moisture") .then(response => response.json()) .then(json => json as Moistures) .then(time => { controller.plantViews.update(time.moisture_a, time.moisture_b) clearTimeout(timerId); controller.progressview.removeProgress("measure_moisture"); }) .catch(error => { clearTimeout(timerId); controller.progressview.removeProgress("measure_moisture"); console.log(error); }); } exit() { fetch(PUBLIC_URL + "/exit", { method: "POST", }) controller.progressview.addIndeterminate("rebooting", "Returned to normal mode, you can close this site now") } waitForReboot() { console.log("Check if controller online again") fetch(PUBLIC_URL + "/version", { method: "POST", signal: AbortSignal.timeout(5000) }).then(response => { console.log("Reached controller, reloading") window.location.reload(); }) .catch(err => { console.log("Not reached yet, retrying") setTimeout(controller.waitForReboot, 1000) }) } reboot() { fetch(PUBLIC_URL + "/reboot", { method: "POST", }) controller.progressview.addIndeterminate("rebooting", "Rebooting") setTimeout(this.waitForReboot, 1000) } initialConfig: PlantControllerConfig | null = null readonly rebootBtn: HTMLButtonElement readonly exitBtn: HTMLButtonElement readonly timeView: TimeView; readonly plantViews: PlantViews; readonly networkView: NetworkConfigView; readonly tankView: TankConfigView; readonly nightLampView: NightLampView; readonly submitView: SubmitView; readonly firmWareView: OTAView; readonly progressview: ProgressView; readonly batteryView: BatteryView; readonly fileview: FileView; constructor() { this.timeView = new TimeView(this) this.plantViews = new PlantViews(this) this.networkView = new NetworkConfigView(this, PUBLIC_URL) this.tankView = new TankConfigView(this) this.batteryView = new BatteryView(this) this.nightLampView = new NightLampView(this) this.submitView = new SubmitView(this) this.firmWareView = new OTAView(this) this.progressview = new ProgressView(this) this.fileview = new FileView(this) this.rebootBtn = document.getElementById("reboot") as HTMLButtonElement this.rebootBtn.onclick = () => { controller.reboot(); } this.exitBtn = document.getElementById("exit") as HTMLButtonElement this.exitBtn.onclick = () => { controller.exit(); } } } const controller = new Controller(); controller.updateRTCData(); controller.updateBatteryData(); controller.downloadConfig(); //controller.measure_moisture(); controller.version(); controller.updateFileList(); controller.getBackupInfo(); controller.progressview.removeProgress("rebooting");