webapp: Use volar formatter
This commit is contained in:
parent
cd35261570
commit
5100c44c23
@ -1,25 +1,25 @@
|
|||||||
<template>
|
<template>
|
||||||
<NavBar />
|
<NavBar />
|
||||||
<main class="container-fluid">
|
<main class="container-fluid">
|
||||||
<router-view />
|
<router-view />
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import NavBar from "./components/NavBar.vue";
|
import NavBar from "./components/NavBar.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "App",
|
name: "App",
|
||||||
components: {
|
components: {
|
||||||
NavBar,
|
NavBar,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* Show it is fixed to the top */
|
/* Show it is fixed to the top */
|
||||||
body {
|
body {
|
||||||
min-height: 75rem;
|
min-height: 75rem;
|
||||||
padding-top: 4.5rem;
|
padding-top: 4.5rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container" role="main">
|
<div class="container" role="main">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1>About</h1>
|
<h1>About</h1>
|
||||||
This project was started from
|
This project was started from
|
||||||
<a href="https://www.mikrocontroller.net/topic/525778" target="_blank"
|
<a href="https://www.mikrocontroller.net/topic/525778" target="_blank">this discussion.
|
||||||
>this discussion. (Mikrocontroller.net)</a
|
(Mikrocontroller.net)</a>
|
||||||
>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
@ -1,76 +1,50 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container" role="main">
|
<div class="container" role="main">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1>DTU Settings</h1>
|
<h1>DTU Settings</h1>
|
||||||
</div>
|
|
||||||
<BootstrapAlert v-model="showAlert" dismissible :variant="alertType">
|
|
||||||
{{ this.alertMessage }}
|
|
||||||
</BootstrapAlert>
|
|
||||||
<form @submit="saveDtuConfig">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header text-white bg-primary">DTU Configuration</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="inputDtuSerial" class="col-sm-2 col-form-label"
|
|
||||||
>Serial:</label
|
|
||||||
>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
class="form-control"
|
|
||||||
id="inputDtuSerial"
|
|
||||||
min="1"
|
|
||||||
max="99999999999"
|
|
||||||
placeholder="DTU Serial"
|
|
||||||
v-model="dtuConfigList.dtu_serial"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="inputPollInterval" class="col-sm-2 col-form-label"
|
|
||||||
>Poll Interval:</label
|
|
||||||
>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<div class="input-group">
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
class="form-control"
|
|
||||||
id="inputPollInterval"
|
|
||||||
min="1"
|
|
||||||
max="86400"
|
|
||||||
placeholder="Poll Interval in Seconds"
|
|
||||||
v-model="dtuConfigList.dtu_pollinterval"
|
|
||||||
aria-describedby="pollIntervalDescription"
|
|
||||||
/>
|
|
||||||
<span class="input-group-text" id="pollIntervalDescription"
|
|
||||||
>seconds</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="inputTimezone" class="col-sm-2 col-form-label"
|
|
||||||
>PA Level:</label
|
|
||||||
>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<select class="form-select" v-model="dtuConfigList.dtu_palevel">
|
|
||||||
<option
|
|
||||||
v-for="palevel in palevelList"
|
|
||||||
:key="palevel.key"
|
|
||||||
:value="palevel.key"
|
|
||||||
>
|
|
||||||
{{ palevel.value }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<BootstrapAlert v-model="showAlert" dismissible :variant="alertType">
|
||||||
<button type="submit" class="btn btn-primary mb-3">Save</button>
|
{{ this.alertMessage }}
|
||||||
</form>
|
</BootstrapAlert>
|
||||||
</div>
|
<form @submit="saveDtuConfig">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header text-white bg-primary">DTU Configuration</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="inputDtuSerial" class="col-sm-2 col-form-label">Serial:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="number" class="form-control" id="inputDtuSerial" min="1" max="99999999999"
|
||||||
|
placeholder="DTU Serial" v-model="dtuConfigList.dtu_serial" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="inputPollInterval" class="col-sm-2 col-form-label">Poll Interval:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="number" class="form-control" id="inputPollInterval" min="1" max="86400"
|
||||||
|
placeholder="Poll Interval in Seconds" v-model="dtuConfigList.dtu_pollinterval"
|
||||||
|
aria-describedby="pollIntervalDescription" />
|
||||||
|
<span class="input-group-text" id="pollIntervalDescription">seconds</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="inputTimezone" class="col-sm-2 col-form-label">PA Level:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<select class="form-select" v-model="dtuConfigList.dtu_palevel">
|
||||||
|
<option v-for="palevel in palevelList" :key="palevel.key" :value="palevel.key">
|
||||||
|
{{ palevel.value }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary mb-3">Save</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -78,61 +52,61 @@ import { defineComponent } from 'vue';
|
|||||||
import BootstrapAlert from "@/components/partials/BootstrapAlert.vue";
|
import BootstrapAlert from "@/components/partials/BootstrapAlert.vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
BootstrapAlert,
|
BootstrapAlert,
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
dtuConfigList: [],
|
|
||||||
palevelList: [
|
|
||||||
{ key: 0, value: "Minimum (-18 dBm)" },
|
|
||||||
{ key: 1, value: "Low (-12 dBm)" },
|
|
||||||
{ key: 2, value: "High (-6 dBm)" },
|
|
||||||
{ key: 3, value: "Maximum (0 dBm)" },
|
|
||||||
],
|
|
||||||
alertMessage: "",
|
|
||||||
alertType: "info",
|
|
||||||
showAlert: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.getDtuConfig();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getDtuConfig() {
|
|
||||||
fetch("/api/dtu/config")
|
|
||||||
.then((response) => response.json())
|
|
||||||
.then(
|
|
||||||
function (data) {
|
|
||||||
this.dtuConfigList = data;
|
|
||||||
}.bind(this)
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
saveDtuConfig(e) {
|
data() {
|
||||||
e.preventDefault();
|
return {
|
||||||
|
dtuConfigList: [],
|
||||||
const formData = new FormData();
|
palevelList: [
|
||||||
formData.append("data", JSON.stringify(this.dtuConfigList));
|
{ key: 0, value: "Minimum (-18 dBm)" },
|
||||||
|
{ key: 1, value: "Low (-12 dBm)" },
|
||||||
fetch("/api/dtu/config", {
|
{ key: 2, value: "High (-6 dBm)" },
|
||||||
method: "POST",
|
{ key: 3, value: "Maximum (0 dBm)" },
|
||||||
body: formData,
|
],
|
||||||
})
|
alertMessage: "",
|
||||||
.then(function (response) {
|
alertType: "info",
|
||||||
if (response.status != 200) {
|
showAlert: false,
|
||||||
throw response.status;
|
};
|
||||||
} else {
|
},
|
||||||
return response.json();
|
created() {
|
||||||
}
|
this.getDtuConfig();
|
||||||
})
|
},
|
||||||
.then(
|
methods: {
|
||||||
function (response) {
|
getDtuConfig() {
|
||||||
this.alertMessage = response.message;
|
fetch("/api/dtu/config")
|
||||||
this.alertType = response.type;
|
.then((response) => response.json())
|
||||||
this.showAlert = true;
|
.then(
|
||||||
}.bind(this)
|
function (data) {
|
||||||
);
|
this.dtuConfigList = data;
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
saveDtuConfig(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("data", JSON.stringify(this.dtuConfigList));
|
||||||
|
|
||||||
|
fetch("/api/dtu/config", {
|
||||||
|
method: "POST",
|
||||||
|
body: formData,
|
||||||
|
})
|
||||||
|
.then(function (response) {
|
||||||
|
if (response.status != 200) {
|
||||||
|
throw response.status;
|
||||||
|
} else {
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(
|
||||||
|
function (response) {
|
||||||
|
this.alertMessage = response.message;
|
||||||
|
this.alertType = response.type;
|
||||||
|
this.showAlert = true;
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -1,82 +1,74 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container" role="main">
|
<div class="container" role="main">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1>Firmware Upgrade</h1>
|
<h1>Firmware Upgrade</h1>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="position-relative" v-if="loading">
|
|
||||||
<div class="position-absolute top-50 start-50 translate-middle">
|
|
||||||
<div class="spinner-border" role="status">
|
|
||||||
<span class="visually-hidden">Loading...</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="!loading && !uploading && OTAError !== null" class="card">
|
<div class="position-relative" v-if="loading">
|
||||||
<div class="card-header text-white bg-danger">OTA Error</div>
|
<div class="position-absolute top-50 start-50 translate-middle">
|
||||||
<div class="card-body text-center">
|
<div class="spinner-border" role="status">
|
||||||
<p class="h1 mb-2"><BIconExclamationCircleFill /></p>
|
<span class="visually-hidden">Loading...</span>
|
||||||
|
</div>
|
||||||
<span style="vertical-align: middle" class="ml-2">
|
</div>
|
||||||
{{ OTAError }}
|
|
||||||
</span>
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<button class="btn btn-light" @click="clear">
|
|
||||||
<BIconArrowLeft /> Back
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-primary" @click="retryOTA">
|
|
||||||
<BIconArrowRepeat /> Retry
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-else-if="!loading && !uploading && OTASuccess" class="card">
|
|
||||||
<div class="card-header text-white bg-success">OTA Status</div>
|
|
||||||
<div class="card-body text-center">
|
|
||||||
<span class="h1 mb-2"><BIconCheckCircle /></span>
|
|
||||||
<span> OTA Success </span>
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<button class="btn btn-primary" @click="clear">
|
|
||||||
<BIconArrowLeft /> Back
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-else-if="!loading && !uploading" class="card">
|
|
||||||
<div class="card-header text-white bg-primary">Firmware Upload</div>
|
|
||||||
<div class="card-body text-center">
|
|
||||||
<div class="form-group pt-2 mt-3">
|
|
||||||
<input
|
|
||||||
class="form-control"
|
|
||||||
type="file"
|
|
||||||
ref="file"
|
|
||||||
accept=".bin,.bin.gz"
|
|
||||||
@change="uploadOTA"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-else-if="!loading && uploading" class="card">
|
<div v-if="!loading && !uploading && OTAError !== null" class="card">
|
||||||
<div class="card-header text-white bg-primary">Upload Progress</div>
|
<div class="card-header text-white bg-danger">OTA Error</div>
|
||||||
<div class="card-body text-center">
|
<div class="card-body text-center">
|
||||||
<div class="progress">
|
<p class="h1 mb-2">
|
||||||
<div
|
<BIconExclamationCircleFill />
|
||||||
class="progress-bar"
|
</p>
|
||||||
role="progressbar"
|
|
||||||
:style="{ width: this.progress + '%' }"
|
<span style="vertical-align: middle" class="ml-2">
|
||||||
v-bind:aria-valuenow="this.progress"
|
{{ OTAError }}
|
||||||
aria-valuemin="0"
|
</span>
|
||||||
aria-valuemax="100"
|
<br />
|
||||||
>
|
<br />
|
||||||
{{ progress }}%
|
<button class="btn btn-light" @click="clear">
|
||||||
</div>
|
<BIconArrowLeft /> Back
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-primary" @click="retryOTA">
|
||||||
|
<BIconArrowRepeat /> Retry
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="!loading && !uploading && OTASuccess" class="card">
|
||||||
|
<div class="card-header text-white bg-success">OTA Status</div>
|
||||||
|
<div class="card-body text-center">
|
||||||
|
<span class="h1 mb-2">
|
||||||
|
<BIconCheckCircle />
|
||||||
|
</span>
|
||||||
|
<span> OTA Success </span>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<button class="btn btn-primary" @click="clear">
|
||||||
|
<BIconArrowLeft /> Back
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="!loading && !uploading" class="card">
|
||||||
|
<div class="card-header text-white bg-primary">Firmware Upload</div>
|
||||||
|
<div class="card-body text-center">
|
||||||
|
<div class="form-group pt-2 mt-3">
|
||||||
|
<input class="form-control" type="file" ref="file" accept=".bin,.bin.gz" @change="uploadOTA" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="!loading && uploading" class="card">
|
||||||
|
<div class="card-header text-white bg-primary">Upload Progress</div>
|
||||||
|
<div class="card-body text-center">
|
||||||
|
<div class="progress">
|
||||||
|
<div class="progress-bar" role="progressbar" :style="{ width: this.progress + '%' }"
|
||||||
|
v-bind:aria-valuenow="this.progress" aria-valuemin="0" aria-valuemax="100">
|
||||||
|
{{ progress }}%
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -84,101 +76,101 @@ import { defineComponent } from 'vue';
|
|||||||
import SparkMD5 from "spark-md5";
|
import SparkMD5 from "spark-md5";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: true,
|
loading: true,
|
||||||
uploading: false,
|
uploading: false,
|
||||||
progress: 0,
|
progress: 0,
|
||||||
OTAError: null,
|
OTAError: null,
|
||||||
OTASuccess: false,
|
OTASuccess: false,
|
||||||
type: "firmware",
|
type: "firmware",
|
||||||
file: null,
|
file: null,
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
fileMD5(file) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const blobSlice =
|
|
||||||
File.prototype.slice ||
|
|
||||||
File.prototype.mozSlice ||
|
|
||||||
File.prototype.webkitSlice;
|
|
||||||
const chunkSize = 2097152; // Read in chunks of 2MB
|
|
||||||
const chunks = Math.ceil(file.size / chunkSize);
|
|
||||||
const spark = new SparkMD5.ArrayBuffer();
|
|
||||||
const fileReader = new FileReader();
|
|
||||||
let currentChunk = 0;
|
|
||||||
fileReader.onload = (e) => {
|
|
||||||
spark.append(e.target.result); // Append array buffer
|
|
||||||
currentChunk += 1;
|
|
||||||
if (currentChunk < chunks) {
|
|
||||||
loadNext();
|
|
||||||
} else {
|
|
||||||
const md5 = spark.end();
|
|
||||||
resolve(md5);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
fileReader.onerror = (e) => {
|
|
||||||
reject(e);
|
|
||||||
};
|
|
||||||
const loadNext = () => {
|
|
||||||
const start = currentChunk * chunkSize;
|
|
||||||
const end =
|
|
||||||
start + chunkSize >= file.size ? file.size : start + chunkSize;
|
|
||||||
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
|
|
||||||
};
|
|
||||||
loadNext();
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
uploadOTA(event) {
|
methods: {
|
||||||
this.uploading = true;
|
fileMD5(file) {
|
||||||
const formData = new FormData();
|
return new Promise((resolve, reject) => {
|
||||||
if (event !== null) {
|
const blobSlice =
|
||||||
[this.file] = event.target.files;
|
File.prototype.slice ||
|
||||||
}
|
File.prototype.mozSlice ||
|
||||||
const request = new XMLHttpRequest();
|
File.prototype.webkitSlice;
|
||||||
request.addEventListener("load", () => {
|
const chunkSize = 2097152; // Read in chunks of 2MB
|
||||||
// request.response will hold the response from the server
|
const chunks = Math.ceil(file.size / chunkSize);
|
||||||
if (request.status === 200) {
|
const spark = new SparkMD5.ArrayBuffer();
|
||||||
this.OTASuccess = true;
|
const fileReader = new FileReader();
|
||||||
} else if (request.status !== 500) {
|
let currentChunk = 0;
|
||||||
this.OTAError = `[HTTP ERROR] ${request.statusText}`;
|
fileReader.onload = (e) => {
|
||||||
} else {
|
spark.append(e.target.result); // Append array buffer
|
||||||
this.OTAError = request.responseText;
|
currentChunk += 1;
|
||||||
}
|
if (currentChunk < chunks) {
|
||||||
this.uploading = false;
|
loadNext();
|
||||||
this.progress = 0;
|
} else {
|
||||||
});
|
const md5 = spark.end();
|
||||||
// Upload progress
|
resolve(md5);
|
||||||
request.upload.addEventListener("progress", (e) => {
|
}
|
||||||
this.progress = Math.trunc((e.loaded / e.total) * 100);
|
};
|
||||||
});
|
fileReader.onerror = (e) => {
|
||||||
request.withCredentials = true;
|
reject(e);
|
||||||
this.fileMD5(this.file)
|
};
|
||||||
.then((md5) => {
|
const loadNext = () => {
|
||||||
formData.append("MD5", md5);
|
const start = currentChunk * chunkSize;
|
||||||
formData.append("firmware", this.file, "firmware");
|
const end =
|
||||||
request.open("post", "/api/firmware/update");
|
start + chunkSize >= file.size ? file.size : start + chunkSize;
|
||||||
request.send(formData);
|
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
|
||||||
})
|
};
|
||||||
.catch(() => {
|
loadNext();
|
||||||
this.OTAError =
|
});
|
||||||
"Unknown error while upload, check the console for details.";
|
},
|
||||||
this.uploading = false;
|
uploadOTA(event) {
|
||||||
this.progress = 0;
|
this.uploading = true;
|
||||||
});
|
const formData = new FormData();
|
||||||
|
if (event !== null) {
|
||||||
|
[this.file] = event.target.files;
|
||||||
|
}
|
||||||
|
const request = new XMLHttpRequest();
|
||||||
|
request.addEventListener("load", () => {
|
||||||
|
// request.response will hold the response from the server
|
||||||
|
if (request.status === 200) {
|
||||||
|
this.OTASuccess = true;
|
||||||
|
} else if (request.status !== 500) {
|
||||||
|
this.OTAError = `[HTTP ERROR] ${request.statusText}`;
|
||||||
|
} else {
|
||||||
|
this.OTAError = request.responseText;
|
||||||
|
}
|
||||||
|
this.uploading = false;
|
||||||
|
this.progress = 0;
|
||||||
|
});
|
||||||
|
// Upload progress
|
||||||
|
request.upload.addEventListener("progress", (e) => {
|
||||||
|
this.progress = Math.trunc((e.loaded / e.total) * 100);
|
||||||
|
});
|
||||||
|
request.withCredentials = true;
|
||||||
|
this.fileMD5(this.file)
|
||||||
|
.then((md5) => {
|
||||||
|
formData.append("MD5", md5);
|
||||||
|
formData.append("firmware", this.file, "firmware");
|
||||||
|
request.open("post", "/api/firmware/update");
|
||||||
|
request.send(formData);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.OTAError =
|
||||||
|
"Unknown error while upload, check the console for details.";
|
||||||
|
this.uploading = false;
|
||||||
|
this.progress = 0;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
retryOTA() {
|
||||||
|
this.OTAError = null;
|
||||||
|
this.OTASuccess = false;
|
||||||
|
this.uploadOTA(null);
|
||||||
|
},
|
||||||
|
clear() {
|
||||||
|
this.OTAError = null;
|
||||||
|
this.OTASuccess = false;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
retryOTA() {
|
mounted() {
|
||||||
this.OTAError = null;
|
this.loading = false;
|
||||||
this.OTASuccess = false;
|
|
||||||
this.uploadOTA(null);
|
|
||||||
},
|
},
|
||||||
clear() {
|
|
||||||
this.OTAError = null;
|
|
||||||
this.OTASuccess = false;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.loading = false;
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -1,72 +1,48 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container" role="main">
|
<div class="container" role="main">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1>Live Data</h1>
|
<h1>Live Data</h1>
|
||||||
<template v-if="waitForData == true">Waiting for data... </template>
|
<template v-if="waitForData == true">Waiting for data... </template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="d-flex align-items-start">
|
<div class="d-flex align-items-start">
|
||||||
<div
|
<div class="nav flex-column nav-pills me-3" id="v-pills-tab" role="tablist"
|
||||||
class="nav flex-column nav-pills me-3"
|
aria-orientation="vertical">
|
||||||
id="v-pills-tab"
|
<button v-for="inverter in inverterData" :key="inverter.serial" class="nav-link"
|
||||||
role="tablist"
|
:id="'v-pills-' + inverter.serial + '-tab'" data-bs-toggle="pill"
|
||||||
aria-orientation="vertical"
|
:data-bs-target="'#v-pills-' + inverter.serial" type="button" role="tab"
|
||||||
>
|
aria-controls="'v-pills-' + inverter.serial" aria-selected="true">
|
||||||
<button
|
{{ inverter.name }}
|
||||||
v-for="inverter in inverterData"
|
</button>
|
||||||
:key="inverter.serial"
|
</div>
|
||||||
class="nav-link"
|
|
||||||
:id="'v-pills-' + inverter.serial + '-tab'"
|
<div class="tab-content" id="v-pills-tabContent">
|
||||||
data-bs-toggle="pill"
|
<div v-for="inverter in inverterData" :key="inverter.serial" class="tab-pane fade show"
|
||||||
:data-bs-target="'#v-pills-' + inverter.serial"
|
:id="'v-pills-' + inverter.serial" role="tabpanel"
|
||||||
type="button"
|
:aria-labelledby="'v-pills-' + inverter.serial + '-tab'" tabindex="0">
|
||||||
role="tab"
|
<div class="card">
|
||||||
aria-controls="'v-pills-' + inverter.serial"
|
<div class="card-header text-white bg-primary" :class="{
|
||||||
aria-selected="true"
|
'bg-danger': inverter.age_critical,
|
||||||
>
|
'bg-primary': !inverter.age_critical,
|
||||||
{{ inverter.name }}
|
}">
|
||||||
</button>
|
{{ inverter.name }} (Inverter Serial Number:
|
||||||
</div>
|
{{ inverter.serial }}) (Data Age:
|
||||||
|
{{ inverter.data_age }} seconds)
|
||||||
<div class="tab-content" id="v-pills-tabContent">
|
</div>
|
||||||
<div
|
<div class="card-body">
|
||||||
v-for="inverter in inverterData"
|
<div class="row row-cols-1 row-cols-md-3 g-4">
|
||||||
:key="inverter.serial"
|
<div v-for="channel in 5" :key="channel">
|
||||||
class="tab-pane fade show"
|
<InverterChannelInfo v-if="inverter[channel - 1]"
|
||||||
:id="'v-pills-' + inverter.serial"
|
:channelData="inverter[channel - 1]" :channelNumber="channel - 1" />
|
||||||
role="tabpanel"
|
</div>
|
||||||
:aria-labelledby="'v-pills-' + inverter.serial + '-tab'"
|
</div>
|
||||||
tabindex="0"
|
</div>
|
||||||
>
|
</div>
|
||||||
<div class="card">
|
</div>
|
||||||
<div
|
|
||||||
class="card-header text-white bg-primary"
|
|
||||||
:class="{
|
|
||||||
'bg-danger': inverter.age_critical,
|
|
||||||
'bg-primary': !inverter.age_critical,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
{{ inverter.name }} (Inverter Serial Number:
|
|
||||||
{{ inverter.serial }}) (Data Age:
|
|
||||||
{{ inverter.data_age }} seconds)
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="row row-cols-1 row-cols-md-3 g-4">
|
|
||||||
<div v-for="channel in 5" :key="channel">
|
|
||||||
<InverterChannelInfo
|
|
||||||
v-if="inverter[channel - 1]"
|
|
||||||
:channelData="inverter[channel - 1]"
|
|
||||||
:channelNumber="channel - 1"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -75,84 +51,83 @@ import InverterChannelInfo from "@/components/partials/InverterChannelInfo";
|
|||||||
import bootstrap from "bootstrap/dist/js/bootstrap.js";
|
import bootstrap from "bootstrap/dist/js/bootstrap.js";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
InverterChannelInfo,
|
InverterChannelInfo,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
socket: null,
|
socket: null,
|
||||||
heartInterval: null,
|
heartInterval: null,
|
||||||
waitForData: true,
|
waitForData: true,
|
||||||
inverterData: [],
|
inverterData: [],
|
||||||
isFirstFetchAfterConnect: true,
|
isFirstFetchAfterConnect: true,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.initSocket();
|
this.initSocket();
|
||||||
},
|
},
|
||||||
unmounted() {
|
unmounted() {
|
||||||
this.closeSocket();
|
|
||||||
},
|
|
||||||
updated() {
|
|
||||||
// Select first tab
|
|
||||||
if (this.isFirstFetchAfterConnect) {
|
|
||||||
this.isFirstFetchAfterConnect = false;
|
|
||||||
|
|
||||||
const firstTabEl = this.$el.querySelector(
|
|
||||||
"#v-pills-tab:first-child button"
|
|
||||||
);
|
|
||||||
if (firstTabEl != null) {
|
|
||||||
const firstTab = new bootstrap.Tab(firstTabEl);
|
|
||||||
firstTab.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
initSocket() {
|
|
||||||
console.log("Starting connection to WebSocket Server");
|
|
||||||
|
|
||||||
const { protocol, host } = location;
|
|
||||||
const webSocketUrl = `${
|
|
||||||
protocol === "https" ? "wss" : "ws"
|
|
||||||
}://${host}/livedata`;
|
|
||||||
|
|
||||||
this.socket = new WebSocket(webSocketUrl);
|
|
||||||
|
|
||||||
this.socket.onmessage = function (event) {
|
|
||||||
console.log(event);
|
|
||||||
this.inverterData = JSON.parse(event.data);
|
|
||||||
this.waitForData = false;
|
|
||||||
this.heartCheck(); // Reset heartbeat detection
|
|
||||||
}.bind(this);
|
|
||||||
|
|
||||||
this.socket.onopen = function (event) {
|
|
||||||
console.log(event);
|
|
||||||
console.log("Successfully connected to the echo websocket server...");
|
|
||||||
};
|
|
||||||
|
|
||||||
// Listen to window events , When the window closes , Take the initiative to disconnect websocket Connect
|
|
||||||
window.onbeforeunload = () => {
|
|
||||||
this.closeSocket();
|
this.closeSocket();
|
||||||
};
|
|
||||||
},
|
},
|
||||||
// Send heartbeat packets regularly * 59s Send a heartbeat
|
updated() {
|
||||||
heartCheck() {
|
// Select first tab
|
||||||
this.heartInterval && clearTimeout(this.heartInterval);
|
if (this.isFirstFetchAfterConnect) {
|
||||||
this.heartInterval = setInterval(() => {
|
this.isFirstFetchAfterConnect = false;
|
||||||
if (this.socket.readyState === 1) {
|
|
||||||
// Connection status
|
const firstTabEl = this.$el.querySelector(
|
||||||
this.socket.send("ping");
|
"#v-pills-tab:first-child button"
|
||||||
} else {
|
);
|
||||||
this.initSocket(); // Breakpoint reconnection 5 Time
|
if (firstTabEl != null) {
|
||||||
|
const firstTab = new bootstrap.Tab(firstTabEl);
|
||||||
|
firstTab.show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, 59 * 1000);
|
|
||||||
},
|
},
|
||||||
/** To break off websocket Connect */
|
methods: {
|
||||||
closeSocket() {
|
initSocket() {
|
||||||
this.socket.close();
|
console.log("Starting connection to WebSocket Server");
|
||||||
this.heartInterval && clearTimeout(this.heartInterval);
|
|
||||||
this.isFirstFetchAfterConnect = true;
|
const { protocol, host } = location;
|
||||||
|
const webSocketUrl = `${protocol === "https" ? "wss" : "ws"
|
||||||
|
}://${host}/livedata`;
|
||||||
|
|
||||||
|
this.socket = new WebSocket(webSocketUrl);
|
||||||
|
|
||||||
|
this.socket.onmessage = function (event) {
|
||||||
|
console.log(event);
|
||||||
|
this.inverterData = JSON.parse(event.data);
|
||||||
|
this.waitForData = false;
|
||||||
|
this.heartCheck(); // Reset heartbeat detection
|
||||||
|
}.bind(this);
|
||||||
|
|
||||||
|
this.socket.onopen = function (event) {
|
||||||
|
console.log(event);
|
||||||
|
console.log("Successfully connected to the echo websocket server...");
|
||||||
|
};
|
||||||
|
|
||||||
|
// Listen to window events , When the window closes , Take the initiative to disconnect websocket Connect
|
||||||
|
window.onbeforeunload = () => {
|
||||||
|
this.closeSocket();
|
||||||
|
};
|
||||||
|
},
|
||||||
|
// Send heartbeat packets regularly * 59s Send a heartbeat
|
||||||
|
heartCheck() {
|
||||||
|
this.heartInterval && clearTimeout(this.heartInterval);
|
||||||
|
this.heartInterval = setInterval(() => {
|
||||||
|
if (this.socket.readyState === 1) {
|
||||||
|
// Connection status
|
||||||
|
this.socket.send("ping");
|
||||||
|
} else {
|
||||||
|
this.initSocket(); // Breakpoint reconnection 5 Time
|
||||||
|
}
|
||||||
|
}, 59 * 1000);
|
||||||
|
},
|
||||||
|
/** To break off websocket Connect */
|
||||||
|
closeSocket() {
|
||||||
|
this.socket.close();
|
||||||
|
this.heartInterval && clearTimeout(this.heartInterval);
|
||||||
|
this.isFirstFetchAfterConnect = true;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -1,112 +1,95 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container" role="main">
|
<div class="container" role="main">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1>Inverter Settings</h1>
|
<h1>Inverter Settings</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<BootstrapAlert v-model="showAlert" dismissible :variant="alertType">
|
<BootstrapAlert v-model="showAlert" dismissible :variant="alertType">
|
||||||
{{ this.alertMessage }}
|
{{ this.alertMessage }}
|
||||||
</BootstrapAlert>
|
</BootstrapAlert>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header text-white bg-primary">Add a new Inverter</div>
|
<div class="card-header text-white bg-primary">Add a new Inverter</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form class="form-inline" v-on:submit.prevent="onSubmit">
|
<form class="form-inline" v-on:submit.prevent="onSubmit">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Serial</label>
|
<label>Serial</label>
|
||||||
<input
|
<input v-model="inverterData.serial" type="number" class="form-control ml-sm-2 mr-sm-4 my-2"
|
||||||
v-model="inverterData.serial"
|
required />
|
||||||
type="number"
|
</div>
|
||||||
class="form-control ml-sm-2 mr-sm-4 my-2"
|
<div class="form-group">
|
||||||
required
|
<label>Name</label>
|
||||||
/>
|
<input v-model="inverterData.name" type="text" class="form-control ml-sm-2 mr-sm-4 my-2"
|
||||||
</div>
|
maxlength="31" required />
|
||||||
<div class="form-group">
|
</div>
|
||||||
<label>Name</label>
|
<div class="ml-auto text-right">
|
||||||
<input
|
<button type="submit" class="btn btn-primary my-2">Add</button>
|
||||||
v-model="inverterData.name"
|
</div>
|
||||||
type="text"
|
</form>
|
||||||
class="form-control ml-sm-2 mr-sm-4 my-2"
|
</div>
|
||||||
maxlength="31"
|
</div>
|
||||||
required
|
|
||||||
/>
|
<div class="card mt-5">
|
||||||
</div>
|
<div class="card-header text-white bg-primary">Inverter List</div>
|
||||||
<div class="ml-auto text-right">
|
<div class="card-body">
|
||||||
<button type="submit" class="btn btn-primary my-2">Add</button>
|
<div class="table-responsive">
|
||||||
</div>
|
<table class="table">
|
||||||
</form>
|
<thead>
|
||||||
</div>
|
<tr>
|
||||||
</div>
|
<th scope="col">Serial</th>
|
||||||
|
<th>Name</th>
|
||||||
<div class="card mt-5">
|
<th>Type</th>
|
||||||
<div class="card-header text-white bg-primary">Inverter List</div>
|
<th>Action</th>
|
||||||
<div class="card-body">
|
</tr>
|
||||||
<div class="table-responsive">
|
</thead>
|
||||||
<table class="table">
|
<tbody>
|
||||||
<thead>
|
<tr v-for="inverter in sortedInverters" v-bind:key="inverter.id">
|
||||||
<tr>
|
<template v-if="editId == inverter.id">
|
||||||
<th scope="col">Serial</th>
|
<td>
|
||||||
<th>Name</th>
|
<input v-model="editInverterData.serial" type="number" class="form-control" />
|
||||||
<th>Type</th>
|
</td>
|
||||||
<th>Action</th>
|
<td>
|
||||||
</tr>
|
<input v-model="editInverterData.name" type="text" class="form-control"
|
||||||
</thead>
|
maxlength="31" />
|
||||||
<tbody>
|
</td>
|
||||||
<tr v-for="inverter in sortedInverters" v-bind:key="inverter.id">
|
<td>
|
||||||
<template v-if="editId == inverter.id">
|
{{ editInverterData.type }}
|
||||||
<td>
|
</td>
|
||||||
<input
|
<td>
|
||||||
v-model="editInverterData.serial"
|
<a href="#" class="icon">
|
||||||
type="number"
|
<BIconCheck v-on:click="onEditSubmit(inverter.id)" />
|
||||||
class="form-control"
|
</a>
|
||||||
/>
|
<a href="#" class="icon">
|
||||||
</td>
|
<BIconX v-on:click="onCancel" />
|
||||||
<td>
|
</a>
|
||||||
<input
|
</td>
|
||||||
v-model="editInverterData.name"
|
</template>
|
||||||
type="text"
|
<template v-else>
|
||||||
class="form-control"
|
<td>
|
||||||
maxlength="31"
|
{{ inverter.serial }}
|
||||||
/>
|
</td>
|
||||||
</td>
|
<td>
|
||||||
<td>
|
{{ inverter.name }}
|
||||||
{{ editInverterData.type }}
|
</td>
|
||||||
</td>
|
<td>
|
||||||
<td>
|
{{ inverter.type }}
|
||||||
<a href="#" class="icon">
|
</td>
|
||||||
<BIconCheck v-on:click="onEditSubmit(inverter.id)" />
|
<td>
|
||||||
</a>
|
<a href="#" class="icon">
|
||||||
<a href="#" class="icon">
|
<BIconTrash v-on:click="onDelete(inverter.id)" />
|
||||||
<BIconX v-on:click="onCancel" />
|
</a>
|
||||||
</a>
|
<a href="#" class="icon">
|
||||||
</td>
|
<BIconPencil v-on:click="onEdit(inverter)" />
|
||||||
</template>
|
</a>
|
||||||
<template v-else>
|
</td>
|
||||||
<td>
|
</template>
|
||||||
{{ inverter.serial }}
|
</tr>
|
||||||
</td>
|
</tbody>
|
||||||
<td>
|
</table>
|
||||||
{{ inverter.name }}
|
</div>
|
||||||
</td>
|
</div>
|
||||||
<td>
|
|
||||||
{{ inverter.type }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a href="#" class="icon">
|
|
||||||
<BIconTrash v-on:click="onDelete(inverter.id)" />
|
|
||||||
</a>
|
|
||||||
<a href="#" class="icon">
|
|
||||||
<BIconPencil v-on:click="onEdit(inverter)" />
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
</template>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -114,137 +97,137 @@ import { defineComponent } from 'vue';
|
|||||||
import BootstrapAlert from "@/components/partials/BootstrapAlert.vue";
|
import BootstrapAlert from "@/components/partials/BootstrapAlert.vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
BootstrapAlert,
|
BootstrapAlert,
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
editId: "-1",
|
|
||||||
inverterData: {
|
|
||||||
id: "",
|
|
||||||
serial: "",
|
|
||||||
name: "",
|
|
||||||
},
|
|
||||||
editInverterData: {
|
|
||||||
id: "",
|
|
||||||
serial: "",
|
|
||||||
name: "",
|
|
||||||
type: "",
|
|
||||||
},
|
|
||||||
inverters: [],
|
|
||||||
alertMessage: "",
|
|
||||||
alertType: "info",
|
|
||||||
showAlert: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.getInverters();
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
sortedInverters() {
|
|
||||||
return this.inverters.slice().sort((a, b) => {
|
|
||||||
return a.serial - b.serial;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
data() {
|
||||||
methods: {
|
return {
|
||||||
getInverters() {
|
editId: "-1",
|
||||||
fetch("/api/inverter/list")
|
inverterData: {
|
||||||
.then((response) => response.json())
|
id: "",
|
||||||
.then((data) => (this.inverters = data.inverter));
|
serial: "",
|
||||||
|
name: "",
|
||||||
|
},
|
||||||
|
editInverterData: {
|
||||||
|
id: "",
|
||||||
|
serial: "",
|
||||||
|
name: "",
|
||||||
|
type: "",
|
||||||
|
},
|
||||||
|
inverters: [],
|
||||||
|
alertMessage: "",
|
||||||
|
alertType: "info",
|
||||||
|
showAlert: false,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
onSubmit() {
|
created() {
|
||||||
const formData = new FormData();
|
this.getInverters();
|
||||||
formData.append("data", JSON.stringify(this.inverterData));
|
},
|
||||||
|
computed: {
|
||||||
|
sortedInverters() {
|
||||||
|
return this.inverters.slice().sort((a, b) => {
|
||||||
|
return a.serial - b.serial;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getInverters() {
|
||||||
|
fetch("/api/inverter/list")
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((data) => (this.inverters = data.inverter));
|
||||||
|
},
|
||||||
|
onSubmit() {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("data", JSON.stringify(this.inverterData));
|
||||||
|
|
||||||
fetch("/api/inverter/add", {
|
fetch("/api/inverter/add", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: formData,
|
body: formData,
|
||||||
})
|
})
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
if (response.status != 200) {
|
if (response.status != 200) {
|
||||||
throw response.status;
|
throw response.status;
|
||||||
} else {
|
} else {
|
||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(
|
.then(
|
||||||
function (response) {
|
function (response) {
|
||||||
this.alertMessage = response.message;
|
this.alertMessage = response.message;
|
||||||
this.alertType = response.type;
|
this.alertType = response.type;
|
||||||
this.showAlert = true;
|
this.showAlert = true;
|
||||||
}.bind(this)
|
}.bind(this)
|
||||||
)
|
)
|
||||||
.then(this.getInverters());
|
.then(this.getInverters());
|
||||||
|
|
||||||
this.inverterData.serial = "";
|
this.inverterData.serial = "";
|
||||||
this.inverterData.name = "";
|
this.inverterData.name = "";
|
||||||
},
|
},
|
||||||
onDelete(id) {
|
onDelete(id) {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("data", JSON.stringify({ id: id }));
|
formData.append("data", JSON.stringify({ id: id }));
|
||||||
|
|
||||||
fetch("/api/inverter/del", {
|
fetch("/api/inverter/del", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: formData,
|
body: formData,
|
||||||
})
|
})
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
if (response.status != 200) {
|
if (response.status != 200) {
|
||||||
throw response.status;
|
throw response.status;
|
||||||
} else {
|
} else {
|
||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(
|
.then(
|
||||||
function (response) {
|
function (response) {
|
||||||
this.alertMessage = response.message;
|
this.alertMessage = response.message;
|
||||||
this.alertType = response.type;
|
this.alertType = response.type;
|
||||||
this.showAlert = true;
|
this.showAlert = true;
|
||||||
}.bind(this)
|
}.bind(this)
|
||||||
)
|
)
|
||||||
.then(this.getInverters());
|
.then(this.getInverters());
|
||||||
},
|
},
|
||||||
onEdit(inverter) {
|
onEdit(inverter) {
|
||||||
this.editId = inverter.id;
|
this.editId = inverter.id;
|
||||||
this.editInverterData.serial = inverter.serial;
|
this.editInverterData.serial = inverter.serial;
|
||||||
this.editInverterData.name = inverter.name;
|
this.editInverterData.name = inverter.name;
|
||||||
this.editInverterData.type = inverter.type;
|
this.editInverterData.type = inverter.type;
|
||||||
},
|
},
|
||||||
onCancel() {
|
onCancel() {
|
||||||
this.editId = "-1";
|
this.editId = "-1";
|
||||||
this.editInverterData.serial = "";
|
this.editInverterData.serial = "";
|
||||||
this.editInverterData.name = "";
|
this.editInverterData.name = "";
|
||||||
},
|
},
|
||||||
onEditSubmit(id) {
|
onEditSubmit(id) {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
this.editInverterData.id = id;
|
this.editInverterData.id = id;
|
||||||
formData.append("data", JSON.stringify(this.editInverterData));
|
formData.append("data", JSON.stringify(this.editInverterData));
|
||||||
|
|
||||||
fetch("/api/inverter/edit", {
|
fetch("/api/inverter/edit", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: formData,
|
body: formData,
|
||||||
})
|
})
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
if (response.status != 200) {
|
if (response.status != 200) {
|
||||||
throw response.status;
|
throw response.status;
|
||||||
} else {
|
} else {
|
||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(
|
.then(
|
||||||
function (response) {
|
function (response) {
|
||||||
this.alertMessage = response.message;
|
this.alertMessage = response.message;
|
||||||
this.alertType = response.type;
|
this.alertType = response.type;
|
||||||
this.showAlert = true;
|
this.showAlert = true;
|
||||||
}.bind(this)
|
}.bind(this)
|
||||||
)
|
)
|
||||||
.then(this.getInverters());
|
.then(this.getInverters());
|
||||||
|
|
||||||
this.editId = "-1";
|
this.editId = "-1";
|
||||||
this.editInverterData.serial = "";
|
this.editInverterData.serial = "";
|
||||||
this.editInverterData.name = "";
|
this.editInverterData.name = "";
|
||||||
this.editInverterData.type = "";
|
this.editInverterData.type = "";
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -1,213 +1,135 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container" role="main">
|
<div class="container" role="main">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1>MqTT Settings</h1>
|
<h1>MqTT Settings</h1>
|
||||||
|
</div>
|
||||||
|
<BootstrapAlert v-model="showAlert" dismissible :variant="alertType">
|
||||||
|
{{ this.alertMessage }}
|
||||||
|
</BootstrapAlert>
|
||||||
|
<form @submit="saveMqttConfig">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header text-white bg-primary">MqTT Configuration</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<input class="form-check-input" type="checkbox" id="inputMqtt"
|
||||||
|
v-model="mqttConfigList.mqtt_enabled" />
|
||||||
|
<label class="form-check-label" for="inputMqtt">Enable MqTT</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card mt-5" v-show="mqttConfigList.mqtt_enabled">
|
||||||
|
<div class="card-header text-white bg-primary">
|
||||||
|
MqTT Broker Parameter
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="inputHostname" class="col-sm-2 col-form-label">Hostname:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="text" class="form-control" id="inputHostname" maxlength="32"
|
||||||
|
placeholder="Hostname or IP address" v-model="mqttConfigList.mqtt_hostname" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="inputPort" class="col-sm-2 col-form-label">Port:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="number" class="form-control" id="inputPort" min="1" max="65535"
|
||||||
|
placeholder="Port number" v-model="mqttConfigList.mqtt_port" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="inputUsername" class="col-sm-2 col-form-label">Username:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="text" class="form-control" id="inputUsername" maxlength="32"
|
||||||
|
placeholder="Username, leave empty for anonymous connection"
|
||||||
|
v-model="mqttConfigList.mqtt_username" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="inputPassword" class="col-sm-2 col-form-label">Password:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="text" class="form-control" id="inputPassword" maxlength="32"
|
||||||
|
placeholder="Password, leave empty for anonymous connection"
|
||||||
|
v-model="mqttConfigList.mqtt_password" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="inputTopic" class="col-sm-2 col-form-label">Base Topic:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="text" class="form-control" id="inputTopic" maxlength="32"
|
||||||
|
placeholder="Base topic, will be prepend to all published topics (e.g. inverter/)"
|
||||||
|
v-model="mqttConfigList.mqtt_topic" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="inputPublishInterval" class="col-sm-2 col-form-label">Publish Interval:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="number" class="form-control" id="inputPublishInterval" min="5" max="86400"
|
||||||
|
placeholder="Publish Interval in Seconds"
|
||||||
|
v-model="mqttConfigList.mqtt_publish_interval"
|
||||||
|
aria-describedby="publishIntervalDescription" />
|
||||||
|
<span class="input-group-text" id="publishIntervalDescription">seconds</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="col-sm-2 form-check-label" for="inputRetain">Enable Retain Flag</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<input class="form-check-input" type="checkbox" id="inputRetain"
|
||||||
|
v-model="mqttConfigList.mqtt_retain" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card mt-5" v-show="mqttConfigList.mqtt_enabled">
|
||||||
|
<div class="card-header text-white bg-primary">LWT Parameters</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="inputLwtTopic" class="col-sm-2 col-form-label">LWT Topic:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-text" id="basic-addon3">{{
|
||||||
|
mqttConfigList.mqtt_topic
|
||||||
|
}}</span>
|
||||||
|
<input type="text" class="form-control" id="inputLwtTopic" maxlength="32"
|
||||||
|
placeholder="LWT topic, will be append base topic"
|
||||||
|
v-model="mqttConfigList.mqtt_lwt_topic" aria-describedby="basic-addon3" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="inputLwtOnline" class="col-sm-2 col-form-label">LWT Online message:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="text" class="form-control" id="inputLwtOnline" maxlength="20"
|
||||||
|
placeholder="Message that will be published to LWT topic when online"
|
||||||
|
v-model="mqttConfigList.mqtt_lwt_online" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="inputLwtOffline" class="col-sm-2 col-form-label">LWT Offline message:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="text" class="form-control" id="inputLwtOffline" maxlength="20"
|
||||||
|
placeholder="Message that will be published to LWT topic when offline"
|
||||||
|
v-model="mqttConfigList.mqtt_lwt_offline" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary mb-3">Save</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<BootstrapAlert v-model="showAlert" dismissible :variant="alertType">
|
|
||||||
{{ this.alertMessage }}
|
|
||||||
</BootstrapAlert>
|
|
||||||
<form @submit="saveMqttConfig">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header text-white bg-primary">MqTT Configuration</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="form-check form-switch">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="checkbox"
|
|
||||||
id="inputMqtt"
|
|
||||||
v-model="mqttConfigList.mqtt_enabled"
|
|
||||||
/>
|
|
||||||
<label class="form-check-label" for="inputMqtt">Enable MqTT</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card mt-5" v-show="mqttConfigList.mqtt_enabled">
|
|
||||||
<div class="card-header text-white bg-primary">
|
|
||||||
MqTT Broker Parameter
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="inputHostname" class="col-sm-2 col-form-label"
|
|
||||||
>Hostname:</label
|
|
||||||
>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
id="inputHostname"
|
|
||||||
maxlength="32"
|
|
||||||
placeholder="Hostname or IP address"
|
|
||||||
v-model="mqttConfigList.mqtt_hostname"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="inputPort" class="col-sm-2 col-form-label">Port:</label>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
class="form-control"
|
|
||||||
id="inputPort"
|
|
||||||
min="1"
|
|
||||||
max="65535"
|
|
||||||
placeholder="Port number"
|
|
||||||
v-model="mqttConfigList.mqtt_port"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="inputUsername" class="col-sm-2 col-form-label"
|
|
||||||
>Username:</label
|
|
||||||
>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
id="inputUsername"
|
|
||||||
maxlength="32"
|
|
||||||
placeholder="Username, leave empty for anonymous connection"
|
|
||||||
v-model="mqttConfigList.mqtt_username"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="inputPassword" class="col-sm-2 col-form-label"
|
|
||||||
>Password:</label
|
|
||||||
>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
id="inputPassword"
|
|
||||||
maxlength="32"
|
|
||||||
placeholder="Password, leave empty for anonymous connection"
|
|
||||||
v-model="mqttConfigList.mqtt_password"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="inputTopic" class="col-sm-2 col-form-label"
|
|
||||||
>Base Topic:</label
|
|
||||||
>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
id="inputTopic"
|
|
||||||
maxlength="32"
|
|
||||||
placeholder="Base topic, will be prepend to all published topics (e.g. inverter/)"
|
|
||||||
v-model="mqttConfigList.mqtt_topic"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="inputPublishInterval" class="col-sm-2 col-form-label"
|
|
||||||
>Publish Interval:</label
|
|
||||||
>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<div class="input-group">
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
class="form-control"
|
|
||||||
id="inputPublishInterval"
|
|
||||||
min="5"
|
|
||||||
max="86400"
|
|
||||||
placeholder="Publish Interval in Seconds"
|
|
||||||
v-model="mqttConfigList.mqtt_publish_interval"
|
|
||||||
aria-describedby="publishIntervalDescription"
|
|
||||||
/>
|
|
||||||
<span class="input-group-text" id="publishIntervalDescription"
|
|
||||||
>seconds</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label class="col-sm-2 form-check-label" for="inputRetain"
|
|
||||||
>Enable Retain Flag</label
|
|
||||||
>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<div class="form-check form-switch">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="checkbox"
|
|
||||||
id="inputRetain"
|
|
||||||
v-model="mqttConfigList.mqtt_retain"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card mt-5" v-show="mqttConfigList.mqtt_enabled">
|
|
||||||
<div class="card-header text-white bg-primary">LWT Parameters</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="inputLwtTopic" class="col-sm-2 col-form-label"
|
|
||||||
>LWT Topic:</label
|
|
||||||
>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<div class="input-group">
|
|
||||||
<span class="input-group-text" id="basic-addon3">{{
|
|
||||||
mqttConfigList.mqtt_topic
|
|
||||||
}}</span>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
id="inputLwtTopic"
|
|
||||||
maxlength="32"
|
|
||||||
placeholder="LWT topic, will be append base topic"
|
|
||||||
v-model="mqttConfigList.mqtt_lwt_topic"
|
|
||||||
aria-describedby="basic-addon3"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="inputLwtOnline" class="col-sm-2 col-form-label"
|
|
||||||
>LWT Online message:</label
|
|
||||||
>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
id="inputLwtOnline"
|
|
||||||
maxlength="20"
|
|
||||||
placeholder="Message that will be published to LWT topic when online"
|
|
||||||
v-model="mqttConfigList.mqtt_lwt_online"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="inputLwtOffline" class="col-sm-2 col-form-label"
|
|
||||||
>LWT Offline message:</label
|
|
||||||
>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
id="inputLwtOffline"
|
|
||||||
maxlength="20"
|
|
||||||
placeholder="Message that will be published to LWT topic when offline"
|
|
||||||
v-model="mqttConfigList.mqtt_lwt_offline"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="btn btn-primary mb-3">Save</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -215,51 +137,51 @@ import { defineComponent } from 'vue';
|
|||||||
import BootstrapAlert from "@/components/partials/BootstrapAlert.vue";
|
import BootstrapAlert from "@/components/partials/BootstrapAlert.vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
BootstrapAlert,
|
BootstrapAlert,
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
mqttConfigList: [],
|
|
||||||
alertMessage: "",
|
|
||||||
alertType: "info",
|
|
||||||
showAlert: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.getMqttConfig();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getMqttConfig() {
|
|
||||||
fetch("/api/mqtt/config")
|
|
||||||
.then((response) => response.json())
|
|
||||||
.then((data) => (this.mqttConfigList = data));
|
|
||||||
},
|
},
|
||||||
saveMqttConfig(e) {
|
data() {
|
||||||
e.preventDefault();
|
return {
|
||||||
|
mqttConfigList: [],
|
||||||
const formData = new FormData();
|
alertMessage: "",
|
||||||
formData.append("data", JSON.stringify(this.mqttConfigList));
|
alertType: "info",
|
||||||
|
showAlert: false,
|
||||||
fetch("/api/mqtt/config", {
|
};
|
||||||
method: "POST",
|
},
|
||||||
body: formData,
|
created() {
|
||||||
})
|
this.getMqttConfig();
|
||||||
.then(function (response) {
|
},
|
||||||
if (response.status != 200) {
|
methods: {
|
||||||
throw response.status;
|
getMqttConfig() {
|
||||||
} else {
|
fetch("/api/mqtt/config")
|
||||||
return response.json();
|
.then((response) => response.json())
|
||||||
}
|
.then((data) => (this.mqttConfigList = data));
|
||||||
})
|
},
|
||||||
.then(
|
saveMqttConfig(e) {
|
||||||
function (response) {
|
e.preventDefault();
|
||||||
this.alertMessage = response.message;
|
|
||||||
this.alertType = response.type;
|
const formData = new FormData();
|
||||||
this.showAlert = true;
|
formData.append("data", JSON.stringify(this.mqttConfigList));
|
||||||
}.bind(this)
|
|
||||||
);
|
fetch("/api/mqtt/config", {
|
||||||
|
method: "POST",
|
||||||
|
body: formData,
|
||||||
|
})
|
||||||
|
.then(function (response) {
|
||||||
|
if (response.status != 200) {
|
||||||
|
throw response.status;
|
||||||
|
} else {
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(
|
||||||
|
function (response) {
|
||||||
|
this.alertMessage = response.message;
|
||||||
|
this.alertType = response.type;
|
||||||
|
this.showAlert = true;
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -1,112 +1,103 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container" role="main">
|
<div class="container" role="main">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1>MqTT Info</h1>
|
<h1>MqTT Info</h1>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header text-white bg-primary">Configuration Summary</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-hover table-condensed">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th>Status</th>
|
|
||||||
<td
|
|
||||||
class="badge"
|
|
||||||
:class="{
|
|
||||||
'bg-danger': !mqttDataList.mqtt_enabled,
|
|
||||||
'bg-success': mqttDataList.mqtt_enabled,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<span v-if="mqttDataList.mqtt_enabled">enabled</span>
|
|
||||||
<span v-else>disabled</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Server</th>
|
|
||||||
<td>{{ mqttDataList.mqtt_hostname }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Port</th>
|
|
||||||
<td>{{ mqttDataList.mqtt_port }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Username</th>
|
|
||||||
<td>{{ mqttDataList.mqtt_username }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Base Topic</th>
|
|
||||||
<td>{{ mqttDataList.mqtt_topic }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Publish Tnterval</th>
|
|
||||||
<td>{{ mqttDataList.mqtt_publish_interval }} seconds</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Retain</th>
|
|
||||||
<td
|
|
||||||
class="badge"
|
|
||||||
:class="{
|
|
||||||
'bg-danger': !mqttDataList.mqtt_retain,
|
|
||||||
'bg-success': mqttDataList.mqtt_retain,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<span v-if="mqttDataList.mqtt_retain">enabled</span>
|
|
||||||
<span v-else>disabled</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card mt-5">
|
<div class="card">
|
||||||
<div class="card-header text-white bg-primary">Runtime Summary</div>
|
<div class="card-header text-white bg-primary">Configuration Summary</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-hover table-condensed">
|
<table class="table table-hover table-condensed">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Connection Status</th>
|
<th>Status</th>
|
||||||
<td
|
<td class="badge" :class="{
|
||||||
class="badge"
|
'bg-danger': !mqttDataList.mqtt_enabled,
|
||||||
:class="{
|
'bg-success': mqttDataList.mqtt_enabled,
|
||||||
'bg-danger': !mqttDataList.mqtt_connected,
|
}">
|
||||||
'bg-success': mqttDataList.mqtt_connected,
|
<span v-if="mqttDataList.mqtt_enabled">enabled</span>
|
||||||
}"
|
<span v-else>disabled</span>
|
||||||
>
|
</td>
|
||||||
<span v-if="mqttDataList.mqtt_connected">connected</span>
|
</tr>
|
||||||
<span v-else>disconnected</span>
|
<tr>
|
||||||
</td>
|
<th>Server</th>
|
||||||
</tr>
|
<td>{{ mqttDataList.mqtt_hostname }}</td>
|
||||||
</tbody>
|
</tr>
|
||||||
</table>
|
<tr>
|
||||||
|
<th>Port</th>
|
||||||
|
<td>{{ mqttDataList.mqtt_port }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Username</th>
|
||||||
|
<td>{{ mqttDataList.mqtt_username }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Base Topic</th>
|
||||||
|
<td>{{ mqttDataList.mqtt_topic }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Publish Tnterval</th>
|
||||||
|
<td>{{ mqttDataList.mqtt_publish_interval }} seconds</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Retain</th>
|
||||||
|
<td class="badge" :class="{
|
||||||
|
'bg-danger': !mqttDataList.mqtt_retain,
|
||||||
|
'bg-success': mqttDataList.mqtt_retain,
|
||||||
|
}">
|
||||||
|
<span v-if="mqttDataList.mqtt_retain">enabled</span>
|
||||||
|
<span v-else>disabled</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card mt-5">
|
||||||
|
<div class="card-header text-white bg-primary">Runtime Summary</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover table-condensed">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>Connection Status</th>
|
||||||
|
<td class="badge" :class="{
|
||||||
|
'bg-danger': !mqttDataList.mqtt_connected,
|
||||||
|
'bg-success': mqttDataList.mqtt_connected,
|
||||||
|
}">
|
||||||
|
<span v-if="mqttDataList.mqtt_connected">connected</span>
|
||||||
|
<span v-else>disconnected</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
mqttDataList: [],
|
mqttDataList: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.getNtpInfo();
|
this.getNtpInfo();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getNtpInfo() {
|
getNtpInfo() {
|
||||||
fetch("/api/mqtt/status")
|
fetch("/api/mqtt/status")
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
.then((data) => (this.mqttDataList = data));
|
.then((data) => (this.mqttDataList = data));
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -1,109 +1,73 @@
|
|||||||
<template>
|
<template>
|
||||||
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
|
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<a class="navbar-brand" href="#">OpenDTU</a>
|
<a class="navbar-brand" href="#">OpenDTU</a>
|
||||||
<button
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup"
|
||||||
class="navbar-toggler"
|
aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
type="button"
|
<span class="navbar-toggler-icon"></span>
|
||||||
data-bs-toggle="collapse"
|
</button>
|
||||||
data-bs-target="#navbarNavAltMarkup"
|
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
|
||||||
aria-controls="navbarNavAltMarkup"
|
<div class="navbar-nav">
|
||||||
aria-expanded="false"
|
<li class="nav-item">
|
||||||
aria-label="Toggle navigation"
|
<router-link class="nav-link" to="/">Live Data</router-link>
|
||||||
>
|
</li>
|
||||||
<span class="navbar-toggler-icon"></span>
|
<li class="nav-item dropdown">
|
||||||
</button>
|
<a class="nav-link dropdown-toggle" href="#" id="navbarScrollingDropdown" role="button"
|
||||||
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
|
data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
<div class="navbar-nav">
|
Settings
|
||||||
<li class="nav-item">
|
</a>
|
||||||
<router-link class="nav-link" to="/">Live Data</router-link>
|
<ul class="dropdown-menu" aria-labelledby="navbarScrollingDropdown">
|
||||||
</li>
|
<li>
|
||||||
<li class="nav-item dropdown">
|
<router-link class="dropdown-item" to="/settings/network">Network Settings</router-link>
|
||||||
<a
|
</li>
|
||||||
class="nav-link dropdown-toggle"
|
<li>
|
||||||
href="#"
|
<router-link class="dropdown-item" to="/settings/ntp">NTP Settings</router-link>
|
||||||
id="navbarScrollingDropdown"
|
</li>
|
||||||
role="button"
|
<li>
|
||||||
data-bs-toggle="dropdown"
|
<router-link class="dropdown-item" to="/settings/mqtt">MqTT Settings</router-link>
|
||||||
aria-expanded="false"
|
</li>
|
||||||
>
|
<li>
|
||||||
Settings
|
<router-link class="dropdown-item" to="/settings/inverter">Inverter Settings
|
||||||
</a>
|
</router-link>
|
||||||
<ul class="dropdown-menu" aria-labelledby="navbarScrollingDropdown">
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<router-link class="dropdown-item" to="/settings/network"
|
<router-link class="dropdown-item" to="/settings/dtu">DTU Settings</router-link>
|
||||||
>Network Settings</router-link
|
</li>
|
||||||
>
|
<li>
|
||||||
</li>
|
<hr class="dropdown-divider" />
|
||||||
<li>
|
</li>
|
||||||
<router-link class="dropdown-item" to="/settings/ntp"
|
<li>
|
||||||
>NTP Settings</router-link
|
<router-link class="dropdown-item" to="/firmware/upgrade">Firmware Upgrade</router-link>
|
||||||
>
|
</li>
|
||||||
</li>
|
</ul>
|
||||||
<li>
|
</li>
|
||||||
<router-link class="dropdown-item" to="/settings/mqtt"
|
<li class="nav-item dropdown">
|
||||||
>MqTT Settings</router-link
|
<a class="nav-link dropdown-toggle" href="#" id="navbarScrollingDropdown" role="button"
|
||||||
>
|
data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
</li>
|
Info
|
||||||
<li>
|
</a>
|
||||||
<router-link class="dropdown-item" to="/settings/inverter"
|
<ul class="dropdown-menu" aria-labelledby="navbarScrollingDropdown">
|
||||||
>Inverter Settings</router-link
|
<li>
|
||||||
>
|
<router-link class="dropdown-item" to="/info/system">System</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<router-link class="dropdown-item" to="/settings/dtu"
|
<router-link class="dropdown-item" to="/info/network">Network</router-link>
|
||||||
>DTU Settings</router-link
|
</li>
|
||||||
>
|
<li>
|
||||||
</li>
|
<router-link class="dropdown-item" to="/info/ntp">NTP</router-link>
|
||||||
<li><hr class="dropdown-divider" /></li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<router-link class="dropdown-item" to="/firmware/upgrade"
|
<router-link class="dropdown-item" to="/info/mqtt">MqTT</router-link>
|
||||||
>Firmware Upgrade</router-link
|
</li>
|
||||||
>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
<li class="nav-item">
|
||||||
</li>
|
<router-link class="nav-link" to="/about">About</router-link>
|
||||||
<li class="nav-item dropdown">
|
</li>
|
||||||
<a
|
</div>
|
||||||
class="nav-link dropdown-toggle"
|
</div>
|
||||||
href="#"
|
|
||||||
id="navbarScrollingDropdown"
|
|
||||||
role="button"
|
|
||||||
data-bs-toggle="dropdown"
|
|
||||||
aria-expanded="false"
|
|
||||||
>
|
|
||||||
Info
|
|
||||||
</a>
|
|
||||||
<ul class="dropdown-menu" aria-labelledby="navbarScrollingDropdown">
|
|
||||||
<li>
|
|
||||||
<router-link class="dropdown-item" to="/info/system"
|
|
||||||
>System</router-link
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<router-link class="dropdown-item" to="/info/network"
|
|
||||||
>Network</router-link
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<router-link class="dropdown-item" to="/info/ntp"
|
|
||||||
>NTP</router-link
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<router-link class="dropdown-item" to="/info/mqtt"
|
|
||||||
>MqTT</router-link
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<router-link class="nav-link" to="/about">About</router-link>
|
|
||||||
</li>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</nav>
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@ -1,170 +1,100 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container" role="main">
|
<div class="container" role="main">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1>Network Settings</h1>
|
<h1>Network Settings</h1>
|
||||||
|
</div>
|
||||||
|
<BootstrapAlert v-model="showAlert" dismissible :variant="alertType">
|
||||||
|
{{ this.alertMessage }}
|
||||||
|
</BootstrapAlert>
|
||||||
|
<form @submit="saveNetworkConfig">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header text-white bg-primary">WiFi Configuration</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="inputSSID" class="col-sm-2 col-form-label">WiFi SSID:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="text" class="form-control" id="inputSSID" maxlength="32" placeholder="SSID"
|
||||||
|
v-model="networkConfigList.ssid" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="inputPassword" class="col-sm-2 col-form-label">WiFi Password:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="password" class="form-control" id="inputPassword" maxlength="32"
|
||||||
|
placeholder="PSK" v-model="networkConfigList.password" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="inputHostname" class="col-sm-2 col-form-label">Hostname:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="text" class="form-control" id="inputHostname" maxlength="32"
|
||||||
|
placeholder="Hostname" v-model="networkConfigList.hostname" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="col-sm-2 form-check-label" for="inputDHCP">Enable DHCP</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<input class="form-check-input" type="checkbox" id="inputDHCP"
|
||||||
|
v-model="networkConfigList.dhcp" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card" v-show="!networkConfigList.dhcp">
|
||||||
|
<div class="card-header text-white bg-primary">
|
||||||
|
Static IP Configuration
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="inputIP" class="col-sm-2 col-form-label">IP Address:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="text" class="form-control" id="inputIP" maxlength="32" placeholder="IP address"
|
||||||
|
v-model="networkConfigList.ipaddress" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="inputNetmask" class="col-sm-2 col-form-label">Netmask:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="text" class="form-control" id="inputNetmask" maxlength="32"
|
||||||
|
placeholder="Netmask" v-model="networkConfigList.netmask" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="inputGateway" class="col-sm-2 col-form-label">Default Gateway:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="text" class="form-control" id="inputGateway" maxlength="32"
|
||||||
|
placeholder="Default Gateway" v-model="networkConfigList.gateway" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="inputDNS1" class="col-sm-2 col-form-label">DNS Server 1:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="text" class="form-control" id="inputDNS1" maxlength="32"
|
||||||
|
placeholder="DNS Server 1" v-model="networkConfigList.dns1" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="inputDNS2" class="col-sm-2 col-form-label">DNS Server 2:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="text" class="form-control" id="inputDNS2" maxlength="32"
|
||||||
|
placeholder="DNS Server 2" v-model="networkConfigList.dns2" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary mb-3">Save</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<BootstrapAlert v-model="showAlert" dismissible :variant="alertType">
|
|
||||||
{{ this.alertMessage }}
|
|
||||||
</BootstrapAlert>
|
|
||||||
<form @submit="saveNetworkConfig">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header text-white bg-primary">WiFi Configuration</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="inputSSID" class="col-sm-2 col-form-label"
|
|
||||||
>WiFi SSID:</label
|
|
||||||
>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
id="inputSSID"
|
|
||||||
maxlength="32"
|
|
||||||
placeholder="SSID"
|
|
||||||
v-model="networkConfigList.ssid"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="inputPassword" class="col-sm-2 col-form-label"
|
|
||||||
>WiFi Password:</label
|
|
||||||
>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
class="form-control"
|
|
||||||
id="inputPassword"
|
|
||||||
maxlength="32"
|
|
||||||
placeholder="PSK"
|
|
||||||
v-model="networkConfigList.password"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="inputHostname" class="col-sm-2 col-form-label"
|
|
||||||
>Hostname:</label
|
|
||||||
>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
id="inputHostname"
|
|
||||||
maxlength="32"
|
|
||||||
placeholder="Hostname"
|
|
||||||
v-model="networkConfigList.hostname"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label class="col-sm-2 form-check-label" for="inputDHCP"
|
|
||||||
>Enable DHCP</label
|
|
||||||
>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<div class="form-check form-switch">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="checkbox"
|
|
||||||
id="inputDHCP"
|
|
||||||
v-model="networkConfigList.dhcp"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card" v-show="!networkConfigList.dhcp">
|
|
||||||
<div class="card-header text-white bg-primary">
|
|
||||||
Static IP Configuration
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="inputIP" class="col-sm-2 col-form-label"
|
|
||||||
>IP Address:</label
|
|
||||||
>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
id="inputIP"
|
|
||||||
maxlength="32"
|
|
||||||
placeholder="IP address"
|
|
||||||
v-model="networkConfigList.ipaddress"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="inputNetmask" class="col-sm-2 col-form-label"
|
|
||||||
>Netmask:</label
|
|
||||||
>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
id="inputNetmask"
|
|
||||||
maxlength="32"
|
|
||||||
placeholder="Netmask"
|
|
||||||
v-model="networkConfigList.netmask"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="inputGateway" class="col-sm-2 col-form-label"
|
|
||||||
>Default Gateway:</label
|
|
||||||
>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
id="inputGateway"
|
|
||||||
maxlength="32"
|
|
||||||
placeholder="Default Gateway"
|
|
||||||
v-model="networkConfigList.gateway"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="inputDNS1" class="col-sm-2 col-form-label"
|
|
||||||
>DNS Server 1:</label
|
|
||||||
>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
id="inputDNS1"
|
|
||||||
maxlength="32"
|
|
||||||
placeholder="DNS Server 1"
|
|
||||||
v-model="networkConfigList.dns1"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="inputDNS2" class="col-sm-2 col-form-label"
|
|
||||||
>DNS Server 2:</label
|
|
||||||
>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
id="inputDNS2"
|
|
||||||
maxlength="32"
|
|
||||||
placeholder="DNS Server 2"
|
|
||||||
v-model="networkConfigList.dns2"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="btn btn-primary mb-3">Save</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -172,51 +102,51 @@ import { defineComponent } from 'vue';
|
|||||||
import BootstrapAlert from "@/components/partials/BootstrapAlert.vue";
|
import BootstrapAlert from "@/components/partials/BootstrapAlert.vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
BootstrapAlert,
|
BootstrapAlert,
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
networkConfigList: [],
|
|
||||||
alertMessage: "",
|
|
||||||
alertType: "info",
|
|
||||||
showAlert: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.getNetworkConfig();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getNetworkConfig() {
|
|
||||||
fetch("/api/network/config")
|
|
||||||
.then((response) => response.json())
|
|
||||||
.then((data) => (this.networkConfigList = data));
|
|
||||||
},
|
},
|
||||||
saveNetworkConfig(e) {
|
data() {
|
||||||
e.preventDefault();
|
return {
|
||||||
|
networkConfigList: [],
|
||||||
const formData = new FormData();
|
alertMessage: "",
|
||||||
formData.append("data", JSON.stringify(this.networkConfigList));
|
alertType: "info",
|
||||||
|
showAlert: false,
|
||||||
fetch("/api/network/config", {
|
};
|
||||||
method: "POST",
|
},
|
||||||
body: formData,
|
created() {
|
||||||
})
|
this.getNetworkConfig();
|
||||||
.then(function (response) {
|
},
|
||||||
if (response.status != 200) {
|
methods: {
|
||||||
throw response.status;
|
getNetworkConfig() {
|
||||||
} else {
|
fetch("/api/network/config")
|
||||||
return response.json();
|
.then((response) => response.json())
|
||||||
}
|
.then((data) => (this.networkConfigList = data));
|
||||||
})
|
},
|
||||||
.then(
|
saveNetworkConfig(e) {
|
||||||
function (response) {
|
e.preventDefault();
|
||||||
this.alertMessage = response.message;
|
|
||||||
this.alertType = response.type;
|
const formData = new FormData();
|
||||||
this.showAlert = true;
|
formData.append("data", JSON.stringify(this.networkConfigList));
|
||||||
}.bind(this)
|
|
||||||
);
|
fetch("/api/network/config", {
|
||||||
|
method: "POST",
|
||||||
|
body: formData,
|
||||||
|
})
|
||||||
|
.then(function (response) {
|
||||||
|
if (response.status != 200) {
|
||||||
|
throw response.status;
|
||||||
|
} else {
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(
|
||||||
|
function (response) {
|
||||||
|
this.alertMessage = response.message;
|
||||||
|
this.alertType = response.type;
|
||||||
|
this.showAlert = true;
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -1,17 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container" role="main">
|
<div class="container" role="main">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1>Network Info</h1>
|
<h1>Network Info</h1>
|
||||||
|
</div>
|
||||||
|
<WifiStationInfo />
|
||||||
|
<div class="mt-5"></div>
|
||||||
|
<WifiApInfo />
|
||||||
|
<div class="mt-5"></div>
|
||||||
|
<InterfaceStationInfo />
|
||||||
|
<div class="mt-5"></div>
|
||||||
|
<InterfaceApInfo />
|
||||||
|
<div class="mt-5"></div>
|
||||||
</div>
|
</div>
|
||||||
<WifiStationInfo />
|
|
||||||
<div class="mt-5"></div>
|
|
||||||
<WifiApInfo />
|
|
||||||
<div class="mt-5"></div>
|
|
||||||
<InterfaceStationInfo />
|
|
||||||
<div class="mt-5"></div>
|
|
||||||
<InterfaceApInfo />
|
|
||||||
<div class="mt-5"></div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -22,11 +22,11 @@ import InterfaceStationInfo from "./partials/InterfaceStationInfo.vue";
|
|||||||
import InterfaceApInfo from "./partials/InterfaceApInfo.vue";
|
import InterfaceApInfo from "./partials/InterfaceApInfo.vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
WifiStationInfo,
|
WifiStationInfo,
|
||||||
WifiApInfo,
|
WifiApInfo,
|
||||||
InterfaceStationInfo,
|
InterfaceStationInfo,
|
||||||
InterfaceApInfo,
|
InterfaceApInfo,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -1,69 +1,47 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container" role="main">
|
<div class="container" role="main">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1>NTP Settings</h1>
|
<h1>NTP Settings</h1>
|
||||||
</div>
|
|
||||||
<BootstrapAlert v-model="showAlert" dismissible :variant="alertType">
|
|
||||||
{{ this.alertMessage }}
|
|
||||||
</BootstrapAlert>
|
|
||||||
<form @submit="saveNtpConfig">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header text-white bg-primary">NTP Configuration</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="inputNtpServer" class="col-sm-2 col-form-label"
|
|
||||||
>Time Server:</label
|
|
||||||
>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
id="inputNtpServer"
|
|
||||||
maxlength="32"
|
|
||||||
placeholder="Time Server"
|
|
||||||
v-model="ntpConfigList.ntp_server"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="inputTimezone" class="col-sm-2 col-form-label"
|
|
||||||
>Timezone:</label
|
|
||||||
>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<select class="form-select" v-model="timezoneSelect">
|
|
||||||
<option
|
|
||||||
v-for="(config, name) in timezoneList"
|
|
||||||
:key="name + '---' + config"
|
|
||||||
:value="name + '---' + config"
|
|
||||||
>
|
|
||||||
{{ name }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="inputTimezoneConfig" class="col-sm-2 col-form-label"
|
|
||||||
>Timezone Config:</label
|
|
||||||
>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
id="inputTimezoneConfig"
|
|
||||||
maxlength="32"
|
|
||||||
placeholder="Timezone"
|
|
||||||
v-model="ntpConfigList.ntp_timezone"
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<BootstrapAlert v-model="showAlert" dismissible :variant="alertType">
|
||||||
<button type="submit" class="btn btn-primary mb-3">Save</button>
|
{{ this.alertMessage }}
|
||||||
</form>
|
</BootstrapAlert>
|
||||||
</div>
|
<form @submit="saveNtpConfig">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header text-white bg-primary">NTP Configuration</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="inputNtpServer" class="col-sm-2 col-form-label">Time Server:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="text" class="form-control" id="inputNtpServer" maxlength="32"
|
||||||
|
placeholder="Time Server" v-model="ntpConfigList.ntp_server" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="inputTimezone" class="col-sm-2 col-form-label">Timezone:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<select class="form-select" v-model="timezoneSelect">
|
||||||
|
<option v-for="(config, name) in timezoneList" :key="name + '---' + config"
|
||||||
|
:value="name + '---' + config">
|
||||||
|
{{ name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="inputTimezoneConfig" class="col-sm-2 col-form-label">Timezone Config:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="text" class="form-control" id="inputTimezoneConfig" maxlength="32"
|
||||||
|
placeholder="Timezone" v-model="ntpConfigList.ntp_timezone" disabled />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary mb-3">Save</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -71,73 +49,73 @@ import { defineComponent } from 'vue';
|
|||||||
import BootstrapAlert from "@/components/partials/BootstrapAlert.vue";
|
import BootstrapAlert from "@/components/partials/BootstrapAlert.vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
BootstrapAlert,
|
BootstrapAlert,
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
ntpConfigList: [],
|
|
||||||
timezoneList: {},
|
|
||||||
timezoneSelect: "",
|
|
||||||
alertMessage: "",
|
|
||||||
alertType: "info",
|
|
||||||
showAlert: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
timezoneSelect: function (newValue) {
|
|
||||||
this.ntpConfigList.ntp_timezone = newValue.split("---")[1];
|
|
||||||
this.ntpConfigList.ntp_timezone_descr = newValue.split("---")[0];
|
|
||||||
},
|
},
|
||||||
},
|
data() {
|
||||||
created() {
|
return {
|
||||||
this.getTimezoneList();
|
ntpConfigList: [],
|
||||||
this.getNtpConfig();
|
timezoneList: {},
|
||||||
},
|
timezoneSelect: "",
|
||||||
methods: {
|
alertMessage: "",
|
||||||
getTimezoneList() {
|
alertType: "info",
|
||||||
fetch("/zones.json")
|
showAlert: false,
|
||||||
.then((response) => response.json())
|
};
|
||||||
.then((data) => (this.timezoneList = data));
|
|
||||||
},
|
},
|
||||||
getNtpConfig() {
|
watch: {
|
||||||
fetch("/api/ntp/config")
|
timezoneSelect: function (newValue) {
|
||||||
.then((response) => response.json())
|
this.ntpConfigList.ntp_timezone = newValue.split("---")[1];
|
||||||
.then(
|
this.ntpConfigList.ntp_timezone_descr = newValue.split("---")[0];
|
||||||
function (data) {
|
},
|
||||||
this.ntpConfigList = data;
|
|
||||||
this.timezoneSelect =
|
|
||||||
this.ntpConfigList.ntp_timezone_descr +
|
|
||||||
"---" +
|
|
||||||
this.ntpConfigList.ntp_timezone;
|
|
||||||
}.bind(this)
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
saveNtpConfig(e) {
|
created() {
|
||||||
e.preventDefault();
|
this.getTimezoneList();
|
||||||
|
this.getNtpConfig();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getTimezoneList() {
|
||||||
|
fetch("/zones.json")
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((data) => (this.timezoneList = data));
|
||||||
|
},
|
||||||
|
getNtpConfig() {
|
||||||
|
fetch("/api/ntp/config")
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then(
|
||||||
|
function (data) {
|
||||||
|
this.ntpConfigList = data;
|
||||||
|
this.timezoneSelect =
|
||||||
|
this.ntpConfigList.ntp_timezone_descr +
|
||||||
|
"---" +
|
||||||
|
this.ntpConfigList.ntp_timezone;
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
saveNtpConfig(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("data", JSON.stringify(this.ntpConfigList));
|
formData.append("data", JSON.stringify(this.ntpConfigList));
|
||||||
|
|
||||||
fetch("/api/ntp/config", {
|
fetch("/api/ntp/config", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: formData,
|
body: formData,
|
||||||
})
|
})
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
if (response.status != 200) {
|
if (response.status != 200) {
|
||||||
throw response.status;
|
throw response.status;
|
||||||
} else {
|
} else {
|
||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(
|
.then(
|
||||||
function (response) {
|
function (response) {
|
||||||
this.alertMessage = response.message;
|
this.alertMessage = response.message;
|
||||||
this.alertType = response.type;
|
this.alertType = response.type;
|
||||||
this.showAlert = true;
|
this.showAlert = true;
|
||||||
}.bind(this)
|
}.bind(this)
|
||||||
);
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -1,82 +1,79 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container" role="main">
|
<div class="container" role="main">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1>NTP Info</h1>
|
<h1>NTP Info</h1>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header text-white bg-primary">Configuration Summary</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-hover table-condensed">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th>Server</th>
|
|
||||||
<td>{{ ntpDataList.ntp_server }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Timezone</th>
|
|
||||||
<td>{{ ntpDataList.ntp_timezone }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Timezone Description</th>
|
|
||||||
<td>{{ ntpDataList.ntp_timezone_descr }}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card mt-5">
|
<div class="card">
|
||||||
<div class="card-header text-white bg-primary">Current Time</div>
|
<div class="card-header text-white bg-primary">Configuration Summary</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-hover table-condensed">
|
<table class="table table-hover table-condensed">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Status</th>
|
<th>Server</th>
|
||||||
<td
|
<td>{{ ntpDataList.ntp_server }}</td>
|
||||||
class="badge"
|
</tr>
|
||||||
:class="{
|
<tr>
|
||||||
'bg-danger': !ntpDataList.ntp_status,
|
<th>Timezone</th>
|
||||||
'bg-success': ntpDataList.ntp_status,
|
<td>{{ ntpDataList.ntp_timezone }}</td>
|
||||||
}"
|
</tr>
|
||||||
>
|
<tr>
|
||||||
<span v-if="ntpDataList.ntp_status">synced</span>
|
<th>Timezone Description</th>
|
||||||
<span v-else>not synced</span>
|
<td>{{ ntpDataList.ntp_timezone_descr }}</td>
|
||||||
</td>
|
</tr>
|
||||||
</tr>
|
</tbody>
|
||||||
<tr>
|
</table>
|
||||||
<th>Local Time</th>
|
</div>
|
||||||
<td>{{ ntpDataList.ntp_localtime }}</td>
|
</div>
|
||||||
</tr>
|
</div>
|
||||||
</tbody>
|
|
||||||
</table>
|
<div class="card mt-5">
|
||||||
|
<div class="card-header text-white bg-primary">Current Time</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover table-condensed">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>Status</th>
|
||||||
|
<td class="badge" :class="{
|
||||||
|
'bg-danger': !ntpDataList.ntp_status,
|
||||||
|
'bg-success': ntpDataList.ntp_status,
|
||||||
|
}">
|
||||||
|
<span v-if="ntpDataList.ntp_status">synced</span>
|
||||||
|
<span v-else>not synced</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Local Time</th>
|
||||||
|
<td>{{ ntpDataList.ntp_localtime }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
ntpDataList: [],
|
ntpDataList: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.getNtpInfo();
|
this.getNtpInfo();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getNtpInfo() {
|
getNtpInfo() {
|
||||||
fetch("/api/ntp/status")
|
fetch("/api/ntp/status")
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
.then((data) => (this.ntpDataList = data));
|
.then((data) => (this.ntpDataList = data));
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -1,15 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container" role="main">
|
<div class="container" role="main">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1>System Info</h1>
|
<h1>System Info</h1>
|
||||||
|
</div>
|
||||||
|
<FirmwareInfo />
|
||||||
|
<div class="mt-5"></div>
|
||||||
|
<HardwareInfo />
|
||||||
|
<div class="mt-5"></div>
|
||||||
|
<MemoryInfo />
|
||||||
|
<div class="mt-5"></div>
|
||||||
</div>
|
</div>
|
||||||
<FirmwareInfo />
|
|
||||||
<div class="mt-5"></div>
|
|
||||||
<HardwareInfo />
|
|
||||||
<div class="mt-5"></div>
|
|
||||||
<MemoryInfo />
|
|
||||||
<div class="mt-5"></div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -19,10 +19,10 @@ import FirmwareInfo from "@/components/partials/FirmwareInfo.vue";
|
|||||||
import MemoryInfo from "@/components/partials/MemoryInfo.vue";
|
import MemoryInfo from "@/components/partials/MemoryInfo.vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
HardwareInfo,
|
HardwareInfo,
|
||||||
FirmwareInfo,
|
FirmwareInfo,
|
||||||
MemoryInfo,
|
MemoryInfo,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,21 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div v-if="isAlertVisible" ref="element" class="alert" role="alert" :class="classes">
|
||||||
v-if="isAlertVisible"
|
<slot />
|
||||||
ref="element"
|
<button v-if="dismissible" type="button" class="btn-close" data-bs-dismiss="alert" :aria-label="dismissLabel"
|
||||||
class="alert"
|
@click="dismissClicked" />
|
||||||
role="alert"
|
</div>
|
||||||
:class="classes"
|
|
||||||
>
|
|
||||||
<slot />
|
|
||||||
<button
|
|
||||||
v-if="dismissible"
|
|
||||||
type="button"
|
|
||||||
class="btn-close"
|
|
||||||
data-bs-dismiss="alert"
|
|
||||||
:aria-label="dismissLabel"
|
|
||||||
@click="dismissClicked"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -23,105 +11,105 @@ import { computed, defineComponent, onBeforeUnmount, ref, watch } from "vue";
|
|||||||
import Alert from "bootstrap/js/dist/alert";
|
import Alert from "bootstrap/js/dist/alert";
|
||||||
|
|
||||||
export const toInteger = (value, defaultValue = NaN) => {
|
export const toInteger = (value, defaultValue = NaN) => {
|
||||||
return Number.isInteger(value) ? value : defaultValue;
|
return Number.isInteger(value) ? value : defaultValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "BAlert",
|
name: "BAlert",
|
||||||
props: {
|
props: {
|
||||||
dismissLabel: { type: String, default: "Close" },
|
dismissLabel: { type: String, default: "Close" },
|
||||||
dismissible: { type: Boolean, default: false },
|
dismissible: { type: Boolean, default: false },
|
||||||
fade: { type: Boolean, default: false },
|
fade: { type: Boolean, default: false },
|
||||||
modelValue: { type: [Boolean, Number], default: false },
|
modelValue: { type: [Boolean, Number], default: false },
|
||||||
show: { type: Boolean, default: false },
|
show: { type: Boolean, default: false },
|
||||||
variant: { type: String, default: "info" },
|
variant: { type: String, default: "info" },
|
||||||
},
|
},
|
||||||
emits: ["dismissed", "dismiss-count-down", "update:modelValue"],
|
emits: ["dismissed", "dismiss-count-down", "update:modelValue"],
|
||||||
setup(props, { emit }) {
|
setup(props, { emit }) {
|
||||||
const element = undefined;
|
const element = undefined;
|
||||||
let instance = undefined;
|
let instance = undefined;
|
||||||
const classes = computed(() => ({
|
const classes = computed(() => ({
|
||||||
[`alert-${props.variant}`]: props.variant,
|
[`alert-${props.variant}`]: props.variant,
|
||||||
show: props.modelValue,
|
show: props.modelValue,
|
||||||
"alert-dismissible": props.dismissible,
|
"alert-dismissible": props.dismissible,
|
||||||
fade: props.modelValue,
|
fade: props.modelValue,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let _countDownTimeout = 0;
|
let _countDownTimeout = 0;
|
||||||
|
|
||||||
const parseCountDown = (value) => {
|
const parseCountDown = (value) => {
|
||||||
if (typeof value === "boolean") {
|
if (typeof value === "boolean") {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const numberValue = toInteger(value, 0);
|
const numberValue = toInteger(value, 0);
|
||||||
return numberValue > 0 ? numberValue : 0;
|
return numberValue > 0 ? numberValue : 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearCountDownInterval = () => {
|
const clearCountDownInterval = () => {
|
||||||
if (_countDownTimeout === undefined) return;
|
if (_countDownTimeout === undefined) return;
|
||||||
clearTimeout(_countDownTimeout);
|
clearTimeout(_countDownTimeout);
|
||||||
_countDownTimeout = undefined;
|
_countDownTimeout = undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const countDown = ref(parseCountDown(props.modelValue));
|
const countDown = ref(parseCountDown(props.modelValue));
|
||||||
const isAlertVisible = computed(() => props.modelValue || props.show);
|
const isAlertVisible = computed(() => props.modelValue || props.show);
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
clearCountDownInterval();
|
clearCountDownInterval();
|
||||||
instance?.dispose();
|
instance?.dispose();
|
||||||
instance = undefined;
|
instance = undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
const parsedModelValue = computed(() => {
|
const parsedModelValue = computed(() => {
|
||||||
if (props.modelValue === true) {
|
if (props.modelValue === true) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (props.modelValue === false) return false;
|
if (props.modelValue === false) return false;
|
||||||
|
|
||||||
if (toInteger(props.modelValue, 0) < 1) {
|
if (toInteger(props.modelValue, 0) < 1) {
|
||||||
// Boolean will always return false for the above comparison
|
// Boolean will always return false for the above comparison
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return !!props.modelValue;
|
return !!props.modelValue;
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleShowAndModelChanged = () => {
|
const handleShowAndModelChanged = () => {
|
||||||
countDown.value = parseCountDown(props.modelValue);
|
countDown.value = parseCountDown(props.modelValue);
|
||||||
if ((parsedModelValue.value || props.show) && !instance)
|
if ((parsedModelValue.value || props.show) && !instance)
|
||||||
instance = new Alert(element);
|
instance = new Alert(element);
|
||||||
};
|
};
|
||||||
|
|
||||||
const dismissClicked = () => {
|
const dismissClicked = () => {
|
||||||
if (typeof props.modelValue === "boolean") {
|
if (typeof props.modelValue === "boolean") {
|
||||||
emit("update:modelValue", false);
|
emit("update:modelValue", false);
|
||||||
} else {
|
} else {
|
||||||
emit("update:modelValue", 0);
|
emit("update:modelValue", 0);
|
||||||
}
|
}
|
||||||
emit("dismissed");
|
emit("dismissed");
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(() => props.modelValue, handleShowAndModelChanged);
|
watch(() => props.modelValue, handleShowAndModelChanged);
|
||||||
watch(() => props.show, handleShowAndModelChanged);
|
watch(() => props.show, handleShowAndModelChanged);
|
||||||
|
|
||||||
watch(countDown, (newValue) => {
|
watch(countDown, (newValue) => {
|
||||||
clearCountDownInterval();
|
clearCountDownInterval();
|
||||||
if (typeof props.modelValue === "boolean") return;
|
if (typeof props.modelValue === "boolean") return;
|
||||||
emit("dismiss-count-down", newValue);
|
emit("dismiss-count-down", newValue);
|
||||||
if (newValue === 0 && props.modelValue > 0) emit("dismissed");
|
if (newValue === 0 && props.modelValue > 0) emit("dismissed");
|
||||||
if (props.modelValue !== newValue) emit("update:modelValue", newValue);
|
if (props.modelValue !== newValue) emit("update:modelValue", newValue);
|
||||||
if (newValue > 0) {
|
if (newValue > 0) {
|
||||||
_countDownTimeout = setTimeout(() => {
|
_countDownTimeout = setTimeout(() => {
|
||||||
countDown.value--;
|
countDown.value--;
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
dismissClicked,
|
dismissClicked,
|
||||||
isAlertVisible,
|
isAlertVisible,
|
||||||
element,
|
element,
|
||||||
classes,
|
classes,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -1,85 +1,85 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header text-white bg-primary">
|
<div class="card-header text-white bg-primary">
|
||||||
Firmware Information
|
Firmware Information
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover table-condensed">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>Hostname</th>
|
||||||
|
<td>{{ systemDataList.hostname }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>SDK Version</th>
|
||||||
|
<td>{{ systemDataList.sdkversion }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Firmware Version</th>
|
||||||
|
<td>{{ systemDataList.firmware_version }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Git Hash</th>
|
||||||
|
<td>{{ systemDataList.git_hash }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Reset Reason CPU 0</th>
|
||||||
|
<td>{{ systemDataList.resetreason_0 }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Reset Reason CPU 1</th>
|
||||||
|
<td>{{ systemDataList.resetreason_1 }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Config save count</th>
|
||||||
|
<td>{{ systemDataList.cfgsavecount }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Uptime</th>
|
||||||
|
<td>{{ timeInHours(systemDataList.uptime) }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-hover table-condensed">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th>Hostname</th>
|
|
||||||
<td>{{ systemDataList.hostname }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>SDK Version</th>
|
|
||||||
<td>{{ systemDataList.sdkversion }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Firmware Version</th>
|
|
||||||
<td>{{ systemDataList.firmware_version }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Git Hash</th>
|
|
||||||
<td>{{ systemDataList.git_hash }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Reset Reason CPU 0</th>
|
|
||||||
<td>{{ systemDataList.resetreason_0 }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Reset Reason CPU 1</th>
|
|
||||||
<td>{{ systemDataList.resetreason_1 }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Config save count</th>
|
|
||||||
<td>{{ systemDataList.cfgsavecount }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Uptime</th>
|
|
||||||
<td>{{ timeInHours(systemDataList.uptime) }}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
systemDataList: [],
|
systemDataList: [],
|
||||||
};
|
};
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.getSystemInfo();
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
timeInHours() {
|
|
||||||
return (value) => {
|
|
||||||
const days = parseInt(Math.floor(value / 3600 / 24));
|
|
||||||
const hours = parseInt(Math.floor((value - days * 3600 * 24) / 3600));
|
|
||||||
const minutes = parseInt(Math.floor((value - days * 3600 * 24 - hours * 3600) / 60));
|
|
||||||
const seconds = parseInt((value - days * 3600 * 24 - hours * 3600 + minutes * 60) % 60);
|
|
||||||
|
|
||||||
const dHours = hours > 9 ? hours : "0" + hours;
|
|
||||||
const dMins = minutes > 9 ? minutes : "0" + minutes;
|
|
||||||
const dSecs = seconds > 9 ? seconds : "0" + seconds;
|
|
||||||
|
|
||||||
return days + " days " + dHours + ":" + dMins + ":" + dSecs;
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
},
|
created() {
|
||||||
methods: {
|
this.getSystemInfo();
|
||||||
getSystemInfo() {
|
},
|
||||||
fetch("/api/system/status")
|
computed: {
|
||||||
.then((response) => response.json())
|
timeInHours() {
|
||||||
.then((data) => (this.systemDataList = data));
|
return (value) => {
|
||||||
|
const days = parseInt(Math.floor(value / 3600 / 24));
|
||||||
|
const hours = parseInt(Math.floor((value - days * 3600 * 24) / 3600));
|
||||||
|
const minutes = parseInt(Math.floor((value - days * 3600 * 24 - hours * 3600) / 60));
|
||||||
|
const seconds = parseInt((value - days * 3600 * 24 - hours * 3600 + minutes * 60) % 60);
|
||||||
|
|
||||||
|
const dHours = hours > 9 ? hours : "0" + hours;
|
||||||
|
const dMins = minutes > 9 ? minutes : "0" + minutes;
|
||||||
|
const dSecs = seconds > 9 ? seconds : "0" + seconds;
|
||||||
|
|
||||||
|
return days + " days " + dHours + ":" + dMins + ":" + dSecs;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getSystemInfo() {
|
||||||
|
fetch("/api/system/status")
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((data) => (this.systemDataList = data));
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,42 +1,36 @@
|
|||||||
<template>
|
<template>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ name }}</th>
|
<th>{{ name }}</th>
|
||||||
<td>
|
<td>
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<div
|
<div class="progress-bar" role="progressbar" :style="{ width: this.getPercent() + '%' }"
|
||||||
class="progress-bar"
|
v-bind:aria-valuenow="this.getPercent()" aria-valuemin="0" aria-valuemax="100">
|
||||||
role="progressbar"
|
{{ this.getPercent() }}%
|
||||||
:style="{ width: this.getPercent() + '%' }"
|
</div>
|
||||||
v-bind:aria-valuenow="this.getPercent()"
|
</div>
|
||||||
aria-valuemin="0"
|
</td>
|
||||||
aria-valuemax="100"
|
<td class="rightCell">
|
||||||
>
|
{{ Math.round((total - used) / 1024) }}
|
||||||
{{ this.getPercent() }}%
|
KByte
|
||||||
</div>
|
</td>
|
||||||
</div>
|
<td class="rightCell">{{ Math.round(used / 1024) }} KByte</td>
|
||||||
</td>
|
<td class="rightCell">{{ Math.round(total / 1024) }} KByte</td>
|
||||||
<td class="rightCell">
|
</tr>
|
||||||
{{ Math.round((total - used) / 1024) }}
|
|
||||||
KByte
|
|
||||||
</td>
|
|
||||||
<td class="rightCell">{{ Math.round(used / 1024) }} KByte</td>
|
|
||||||
<td class="rightCell">{{ Math.round(total / 1024) }} KByte</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
name: String,
|
name: String,
|
||||||
total: Number,
|
total: Number,
|
||||||
used: Number,
|
used: Number,
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getPercent() {
|
getPercent() {
|
||||||
return Math.round((this.used / this.total) * 100);
|
return Math.round((this.used / this.total) * 100);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,53 +1,53 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header text-white bg-primary">
|
<div class="card-header text-white bg-primary">
|
||||||
Hardware Information
|
Hardware Information
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover table-condensed">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>Chip Model</th>
|
||||||
|
<td>{{ systemDataList.chipmodel }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Chip Revision</th>
|
||||||
|
<td>{{ systemDataList.chiprevision }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Chip Cores</th>
|
||||||
|
<td>{{ systemDataList.chipcores }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>CPU Frequency</th>
|
||||||
|
<td>{{ systemDataList.cpufreq }} MHz</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-hover table-condensed">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th>Chip Model</th>
|
|
||||||
<td>{{ systemDataList.chipmodel }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Chip Revision</th>
|
|
||||||
<td>{{ systemDataList.chiprevision }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Chip Cores</th>
|
|
||||||
<td>{{ systemDataList.chipcores }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>CPU Frequency</th>
|
|
||||||
<td>{{ systemDataList.cpufreq }} MHz</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
systemDataList: [],
|
systemDataList: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.getSystemInfo();
|
this.getSystemInfo();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getSystemInfo() {
|
getSystemInfo() {
|
||||||
fetch("/api/system/status")
|
fetch("/api/system/status")
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
.then((data) => (this.systemDataList = data));
|
.then((data) => (this.systemDataList = data));
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,45 +1,45 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header text-white bg-primary">
|
<div class="card-header text-white bg-primary">
|
||||||
Network Interface (Access Point)
|
Network Interface (Access Point)
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover table-condensed">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>IP Address</th>
|
||||||
|
<td>{{ networkDataList.ap_ip }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>MAC Address</th>
|
||||||
|
<td>{{ networkDataList.ap_mac }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-hover table-condensed">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th>IP Address</th>
|
|
||||||
<td>{{ networkDataList.ap_ip }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>MAC Address</th>
|
|
||||||
<td>{{ networkDataList.ap_mac }}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
networkDataList: [],
|
networkDataList: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.getNetworkInfo();
|
this.getNetworkInfo();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getNetworkInfo() {
|
getNetworkInfo() {
|
||||||
fetch("/api/network/status")
|
fetch("/api/network/status")
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
.then((data) => (this.networkDataList = data));
|
.then((data) => (this.networkDataList = data));
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,61 +1,61 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header text-white bg-primary">
|
<div class="card-header text-white bg-primary">
|
||||||
Network Interface (Station)
|
Network Interface (Station)
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover table-condensed">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>IP Address</th>
|
||||||
|
<td>{{ networkDataList.sta_ip }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Netmask</th>
|
||||||
|
<td>{{ networkDataList.sta_netmask }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Default Gateway</th>
|
||||||
|
<td>{{ networkDataList.sta_gateway }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>DNS 1</th>
|
||||||
|
<td>{{ networkDataList.sta_dns1 }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>DNS 2</th>
|
||||||
|
<td>{{ networkDataList.sta_dns2 }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>MAC Address</th>
|
||||||
|
<td>{{ networkDataList.sta_mac }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-hover table-condensed">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th>IP Address</th>
|
|
||||||
<td>{{ networkDataList.sta_ip }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Netmask</th>
|
|
||||||
<td>{{ networkDataList.sta_netmask }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Default Gateway</th>
|
|
||||||
<td>{{ networkDataList.sta_gateway }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>DNS 1</th>
|
|
||||||
<td>{{ networkDataList.sta_dns1 }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>DNS 2</th>
|
|
||||||
<td>{{ networkDataList.sta_dns2 }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>MAC Address</th>
|
|
||||||
<td>{{ networkDataList.sta_mac }}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
networkDataList: [],
|
networkDataList: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.getNetworkInfo();
|
this.getNetworkInfo();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getNetworkInfo() {
|
getNetworkInfo() {
|
||||||
fetch("/api/network/status")
|
fetch("/api/network/status")
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
.then((data) => (this.networkDataList = data));
|
.then((data) => (this.networkDataList = data));
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,41 +1,41 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">Channel {{ channelNumber }}</div>
|
<div class="card-header">Channel {{ channelNumber }}</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<table class="table table-striped table-hover">
|
<table class="table table-striped table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">Property</th>
|
<th scope="col">Property</th>
|
||||||
<th scope="col">Value</th>
|
<th scope="col">Value</th>
|
||||||
<th scope="col">Unit</th>
|
<th scope="col">Unit</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="(property, key) in channelData" :key="`prop-${key}`">
|
<tr v-for="(property, key) in channelData" :key="`prop-${key}`">
|
||||||
<th scope="row">{{ key }}</th>
|
<th scope="row">{{ key }}</th>
|
||||||
<td style="text-align: right">{{ formatNumber(property.v) }}</td>
|
<td style="text-align: right">{{ formatNumber(property.v) }}</td>
|
||||||
<td>{{ property.u }}</td>
|
<td>{{ property.u }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
channelData: Object,
|
channelData: Object,
|
||||||
channelNumber: Number,
|
channelNumber: Number,
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
formatNumber(num) {
|
formatNumber(num) {
|
||||||
return parseFloat(num).toFixed(2);
|
return parseFloat(num).toFixed(2);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,27 +1,28 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header text-white bg-primary">Memory Information</div>
|
<div class="card-header text-white bg-primary">Memory Information</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-hover table-condensed">
|
<table class="table table-hover table-condensed">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Type</th>
|
<th>Type</th>
|
||||||
<th>Usage</th>
|
<th>Usage</th>
|
||||||
<th class="rightCell">Free</th>
|
<th class="rightCell">Free</th>
|
||||||
<th class="rightCell">Used</th>
|
<th class="rightCell">Used</th>
|
||||||
<th class="rightCell">Size</th>
|
<th class="rightCell">Size</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<FsInfo name="Heap" :total="systemDataList.heap_total" :used="systemDataList.heap_used" />
|
<FsInfo name="Heap" :total="systemDataList.heap_total" :used="systemDataList.heap_used" />
|
||||||
<FsInfo name="LittleFs" :total="systemDataList.littlefs_total" :used="systemDataList.littlefs_used" />
|
<FsInfo name="LittleFs" :total="systemDataList.littlefs_total"
|
||||||
<FsInfo name="Sketch" :total="systemDataList.sketch_total" :used="systemDataList.sketch_used" />
|
:used="systemDataList.littlefs_used" />
|
||||||
</tbody>
|
<FsInfo name="Sketch" :total="systemDataList.sketch_total" :used="systemDataList.sketch_used" />
|
||||||
</table>
|
</tbody>
|
||||||
</div>
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -29,23 +30,23 @@ import { defineComponent } from 'vue';
|
|||||||
import FsInfo from "@/components/partials/FsInfo.vue";
|
import FsInfo from "@/components/partials/FsInfo.vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
FsInfo,
|
FsInfo,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
systemDataList: [],
|
systemDataList: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.getSystemInfo();
|
this.getSystemInfo();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getSystemInfo() {
|
getSystemInfo() {
|
||||||
fetch("/api/system/status")
|
fetch("/api/system/status")
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
.then((data) => (this.systemDataList = data));
|
.then((data) => (this.systemDataList = data));
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,58 +1,55 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header text-white bg-primary">
|
<div class="card-header text-white bg-primary">
|
||||||
WiFi Information (Access Point)
|
WiFi Information (Access Point)
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover table-condensed">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>Status</th>
|
||||||
|
<td class="badge" :class="{
|
||||||
|
'bg-danger': !networkDataList.ap_status,
|
||||||
|
'bg-success': networkDataList.ap_status,
|
||||||
|
}">
|
||||||
|
<span v-if="networkDataList.ap_status">enabled</span>
|
||||||
|
<span v-else>disabled</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>SSID</th>
|
||||||
|
<td>{{ networkDataList.ap_ssid }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th># Stations</th>
|
||||||
|
<td>{{ networkDataList.ap_stationnum }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-hover table-condensed">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th>Status</th>
|
|
||||||
<td
|
|
||||||
class="badge"
|
|
||||||
:class="{
|
|
||||||
'bg-danger': !networkDataList.ap_status,
|
|
||||||
'bg-success': networkDataList.ap_status,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<span v-if="networkDataList.ap_status">enabled</span>
|
|
||||||
<span v-else>disabled</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>SSID</th>
|
|
||||||
<td>{{ networkDataList.ap_ssid }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th># Stations</th>
|
|
||||||
<td>{{ networkDataList.ap_stationnum }}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
networkDataList: [],
|
networkDataList: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.getNetworkInfo();
|
this.getNetworkInfo();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getNetworkInfo() {
|
getNetworkInfo() {
|
||||||
fetch("/api/network/status")
|
fetch("/api/network/status")
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
.then((data) => (this.networkDataList = data));
|
.then((data) => (this.networkDataList = data));
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,75 +1,72 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header text-white bg-primary">
|
<div class="card-header text-white bg-primary">
|
||||||
WiFi Information (Station)
|
WiFi Information (Station)
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover table-condensed">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>Status</th>
|
||||||
|
<td class="badge" :class="{
|
||||||
|
'bg-danger': !networkDataList.sta_status,
|
||||||
|
'bg-success': networkDataList.sta_status,
|
||||||
|
}">
|
||||||
|
<span v-if="networkDataList.sta_status">enabled</span>
|
||||||
|
<span v-else>disabled</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>SSID</th>
|
||||||
|
<td>{{ networkDataList.sta_ssid }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Quality</th>
|
||||||
|
<td>{{ this.getRSSIasQuality(networkDataList.sta_rssi) }} %</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>RSSI</th>
|
||||||
|
<td>{{ networkDataList.sta_rssi }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-hover table-condensed">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th>Status</th>
|
|
||||||
<td
|
|
||||||
class="badge"
|
|
||||||
:class="{
|
|
||||||
'bg-danger': !networkDataList.sta_status,
|
|
||||||
'bg-success': networkDataList.sta_status,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<span v-if="networkDataList.sta_status">enabled</span>
|
|
||||||
<span v-else>disabled</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>SSID</th>
|
|
||||||
<td>{{ networkDataList.sta_ssid }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Quality</th>
|
|
||||||
<td>{{ this.getRSSIasQuality(networkDataList.sta_rssi) }} %</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>RSSI</th>
|
|
||||||
<td>{{ networkDataList.sta_rssi }}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
networkDataList: [],
|
networkDataList: [],
|
||||||
};
|
};
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.getNetworkInfo();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getNetworkInfo() {
|
|
||||||
fetch("/api/network/status")
|
|
||||||
.then((response) => response.json())
|
|
||||||
.then((data) => (this.networkDataList = data));
|
|
||||||
},
|
},
|
||||||
getRSSIasQuality(rssi) {
|
created() {
|
||||||
let quality = 0;
|
this.getNetworkInfo();
|
||||||
|
},
|
||||||
if (rssi <= -100) {
|
methods: {
|
||||||
quality = 0;
|
getNetworkInfo() {
|
||||||
} else if (rssi >= -50) {
|
fetch("/api/network/status")
|
||||||
quality = 100;
|
.then((response) => response.json())
|
||||||
} else {
|
.then((data) => (this.networkDataList = data));
|
||||||
quality = 2 * (rssi + 100);
|
},
|
||||||
}
|
getRSSIasQuality(rssi) {
|
||||||
|
let quality = 0;
|
||||||
return quality;
|
|
||||||
|
if (rssi <= -100) {
|
||||||
|
quality = 0;
|
||||||
|
} else if (rssi >= -50) {
|
||||||
|
quality = 100;
|
||||||
|
} else {
|
||||||
|
quality = 2 * (rssi + 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
return quality;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user