webapp: Implement BasePage component for default views

This commit is contained in:
Thomas Basler 2022-10-18 18:25:50 +02:00
parent 93512e2e0c
commit 03066af1c4
14 changed files with 795 additions and 864 deletions

View File

@ -0,0 +1,28 @@
<template>
<div class="container-xxl" role="main">
<div class="page-header">
<h1>{{ title }}</h1>
</div>
<div class="text-center" v-if="isLoading">
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
<template v-if="!isLoading">
<slot />
</template>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
props: {
title: { type: String, required: true },
isLoading: { type: Boolean, required: false, default: false }
},
});
</script>

View File

@ -1,10 +1,5 @@
<template>
<div class="container-xxl" role="main">
<div class="page-header">
<h1>About OpenDTU</h1>
</div>
<BasePage :title="'About OpenDTU'">
<div class="accordion" id="accordionExample">
<div class="accordion-item">
<h2 class="accordion-header" id="headingOne">
@ -96,12 +91,13 @@
</div>
</div>
</BasePage>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import BasePage from '@/components/BasePage.vue';
import {
BIconInfoCircle,
BIconActivity,
@ -111,6 +107,7 @@ import {
export default defineComponent({
components: {
BasePage,
BIconInfoCircle,
BIconActivity,
BIconBug,

View File

@ -1,121 +1,110 @@
<template>
<div class="container-xxl" role="main">
<div class="page-header">
<h1>Config Management</h1>
</div>
<BasePage :title="'Config Management'" :isLoading="loading">
<BootstrapAlert v-model="showAlert" dismissible :variant="alertType">
{{ alertMessage }}
</BootstrapAlert>
<div class="text-center" v-if="loading">
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
<div class="card">
<div class="card-header text-white bg-primary">Backup: Configuration File Backup</div>
<div class="card-body text-center">
Backup the configuration file
<button class="btn btn-primary" @click="downloadConfig">Backup
</button>
</div>
</div>
<template v-if="!loading">
<div class="card">
<div class="card-header text-white bg-primary">Backup: Configuration File Backup</div>
<div class="card-body text-center">
Backup the configuration file
<button class="btn btn-primary" @click="downloadConfig">Backup
<div class="card mt-5">
<div class="card-header text-white bg-primary">Restore: Restore the Configuration File</div>
<div class="card-body text-center">
<div v-if="!uploading && UploadError != ''">
<p class="h1 mb-2">
<BIconExclamationCircleFill />
</p>
<span style="vertical-align: middle" class="ml-2">
{{ UploadError }}
</span>
<br />
<br />
<button class="btn btn-light" @click="clear">
<BIconArrowLeft /> Back
</button>
</div>
</div>
<div class="card mt-5">
<div class="card-header text-white bg-primary">Restore: Restore the Configuration File</div>
<div class="card-body text-center">
<div v-if="!uploading && UploadError != ''">
<p class="h1 mb-2">
<BIconExclamationCircleFill />
</p>
<span style="vertical-align: middle" class="ml-2">
{{ UploadError }}
</span>
<br />
<br />
<button class="btn btn-light" @click="clear">
<BIconArrowLeft /> Back
</button>
</div>
<div v-else-if="!uploading && UploadSuccess">
<span class="h1 mb-2">
<BIconCheckCircle />
</span>
<span> Upload Success </span>
<br />
<br />
<button class="btn btn-primary" @click="clear">
<BIconArrowLeft /> Back
</button>
</div>
<div v-else-if="!uploading">
<div class="form-group pt-2 mt-3">
<input class="form-control" type="file" ref="file" accept=".json" @change="uploadConfig" />
</div>
</div>
<div v-else-if="uploading">
<div class="progress">
<div class="progress-bar" role="progressbar" :style="{ width: progress + '%' }"
v-bind:aria-valuenow="progress" aria-valuemin="0" aria-valuemax="100">
{{ progress }}%
</div>
</div>
</div>
<div class="alert alert-danger mt-3" role="alert">
<b>Note:</b> This operation replaces the configuration file with the restored configuration and
restarts OpenDTU to apply all settings.
</div>
</div>
</div>
<div class="card mt-5">
<div class="card-header text-white bg-primary">Initialize: Perform Factory Reset</div>
<div class="card-body text-center">
<button class="btn btn-danger" @click="onFactoryResetModal">Restore Factory-Default Settings
<div v-else-if="!uploading && UploadSuccess">
<span class="h1 mb-2">
<BIconCheckCircle />
</span>
<span> Upload Success </span>
<br />
<br />
<button class="btn btn-primary" @click="clear">
<BIconArrowLeft /> Back
</button>
</div>
<div class="alert alert-danger mt-3" role="alert">
<b>Note:</b> Click Restore Factory-Default Settings to restore and initialize the
factory-default settings and reboot.
<div v-else-if="!uploading">
<div class="form-group pt-2 mt-3">
<input class="form-control" type="file" ref="file" accept=".json" @change="uploadConfig" />
</div>
</div>
</div>
</template>
<div class="modal" id="factoryReset" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Factory Reset</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Are you sure you want to delete the current configuration and reset all settings to their
factory defaults?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @click="onFactoryResetCancel"
data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger" @click="onFactoryResetPerform">Factory
Reset!</button>
<div v-else-if="uploading">
<div class="progress">
<div class="progress-bar" role="progressbar" :style="{ width: progress + '%' }"
v-bind:aria-valuenow="progress" aria-valuemin="0" aria-valuemax="100">
{{ progress }}%
</div>
</div>
</div>
<div class="alert alert-danger mt-3" role="alert">
<b>Note:</b> This operation replaces the configuration file with the restored configuration and
restarts OpenDTU to apply all settings.
</div>
</div>
</div>
<div class="card mt-5">
<div class="card-header text-white bg-primary">Initialize: Perform Factory Reset</div>
<div class="card-body text-center">
<button class="btn btn-danger" @click="onFactoryResetModal">Restore Factory-Default Settings
</button>
<div class="alert alert-danger mt-3" role="alert">
<b>Note:</b> Click Restore Factory-Default Settings to restore and initialize the
factory-default settings and reboot.
</div>
</div>
</div>
</BasePage>
<div class="modal" id="factoryReset" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Factory Reset</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Are you sure you want to delete the current configuration and reset all settings to their
factory defaults?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @click="onFactoryResetCancel"
data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger" @click="onFactoryResetPerform">Factory
Reset!</button>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import BasePage from '@/components/BasePage.vue';
import {
BIconExclamationCircleFill,
BIconArrowLeft,
@ -126,6 +115,7 @@ import BootstrapAlert from "@/components/BootstrapAlert.vue";
export default defineComponent({
components: {
BasePage,
BIconExclamationCircleFill,
BIconArrowLeft,
BIconCheckCircle,

View File

@ -1,68 +1,59 @@
<template>
<div class="container-xxl" role="main">
<div class="page-header">
<h1>DTU Settings</h1>
</div>
<BasePage :title="'DTU Settings'" :isLoading="dataLoading">
<BootstrapAlert v-model="showAlert" dismissible :variant="alertType">
{{ alertMessage }}
</BootstrapAlert>
<div class="text-center" v-if="dataLoading">
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
<template v-if="!dataLoading">
<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>
<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 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>
<button type="submit" class="btn btn-primary mb-3">Save</button>
</form>
</template>
</div>
</div>
<button type="submit" class="btn btn-primary mb-3">Save</button>
</form>
</BasePage>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import BasePage from '@/components/BasePage.vue';
import BootstrapAlert from "@/components/BootstrapAlert.vue";
import type { DtuConfig } from "@/types/DtuConfig";
export default defineComponent({
components: {
BasePage,
BootstrapAlert,
},
data() {

View File

@ -1,9 +1,5 @@
<template>
<div class="container-xxl" role="main">
<div class="page-header">
<h1>Firmware Upgrade</h1>
</div>
<BasePage :title="'Firmware Upgrade'">
<div class="position-relative" v-if="loading">
<div class="position-absolute top-50 start-50 translate-middle">
<div class="spinner-border" role="status">
@ -68,11 +64,12 @@
</div>
</div>
</div>
</div>
</BasePage>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import BasePage from '@/components/BasePage.vue';
import SparkMD5 from "spark-md5";
import {
BIconExclamationCircleFill,
@ -83,6 +80,7 @@ import {
export default defineComponent({
components: {
BasePage,
BIconExclamationCircleFill,
BIconArrowLeft,
BIconArrowRepeat,

View File

@ -1,9 +1,5 @@
<template>
<div class="container-xxl" role="main">
<div class="page-header">
<h1>Inverter Settings</h1>
</div>
<BasePage :title="'Inverter Settings'" :isLoading="dataLoading">
<BootstrapAlert v-model="showAlert" dismissible :variant="alertType">
{{ alertMessage }}
</BootstrapAlert>
@ -72,80 +68,79 @@
</div>
</div>
</div>
</BasePage>
<div class="modal" id="inverterEdit" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Edit Inverter</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="modal" id="inverterEdit" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Edit Inverter</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form>
<div class="mb-3">
<label for="inverter-serial" class="col-form-label">Serial:</label>
<input v-model="editInverterData.serial" type="number" id="inverter-serial"
class="form-control" />
</div>
<div class="mb-3">
<label for="inverter-name" class="col-form-label">Name:</label>
<input v-model="editInverterData.name" type="text" id="inverter-name"
class="form-control" maxlength="31" />
<form>
<div class="mb-3">
<label for="inverter-serial" class="col-form-label">Serial:</label>
<input v-model="editInverterData.serial" type="number" id="inverter-serial"
class="form-control" />
</div>
<div class="mb-3">
<label for="inverter-name" class="col-form-label">Name:</label>
<input v-model="editInverterData.name" type="text" id="inverter-name" class="form-control"
maxlength="31" />
</div>
<div class="mb-3" v-for="(max, index) in editInverterData.max_power" :key="`${index}`">
<label :for="`inverter-max_${index}`" class="col-form-label">Max power string {{ index +
1
}}:</label>
<div class="input-group">
<input type="number" class="form-control" :id="`inverter-max_${index}`" min="0"
v-model="editInverterData.max_power[index]"
:aria-describedby="`inverter-maxDescription_${index} inverter-maxHelpText_${index}`" />
<span class="input-group-text" :id="`inverter-maxDescription_${index}`">W</span>
</div>
<div :id="`inverter-maxHelpText_${index}`" class="form-text">This value is used to
calculate the Irradiation.</div>
</div>
</form>
<div class="mb-3" v-for="(max, index) in editInverterData.max_power" :key="`${index}`">
<label :for="`inverter-max_${index}`" class="col-form-label">Max power string {{ index +
1
}}:</label>
<div class="input-group">
<input type="number" class="form-control" :id="`inverter-max_${index}`" min="0"
v-model="editInverterData.max_power[index]"
:aria-describedby="`inverter-maxDescription_${index} inverter-maxHelpText_${index}`" />
<span class="input-group-text" :id="`inverter-maxDescription_${index}`">W</span>
</div>
<div :id="`inverter-maxHelpText_${index}`" class="form-text">This value is used to
calculate the Irradiation.</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @click="onCancel"
data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" @click="onEditSubmit(editId)">Save
changes</button>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @click="onCancel"
data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" @click="onEditSubmit(editId)">Save
changes</button>
</div>
</div>
</div>
</div>
<div class="modal" id="inverterDelete" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Delete Inverter</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Are you sure you want to delete the inverter "{{ deleteInverterData.name }}" with serial number
{{ deleteInverterData.serial }}?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @click="onDeleteCancel"
data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger"
@click="onDelete(deleteId.toString())">Delete</button>
</div>
<div class="modal" id="inverterDelete" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Delete Inverter</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Are you sure you want to delete the inverter "{{ deleteInverterData.name }}" with serial number
{{ deleteInverterData.serial }}?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @click="onDeleteCancel"
data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger" @click="onDelete(deleteId.toString())">Delete</button>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import BasePage from '@/components/BasePage.vue';
import {
BIconTrash,
BIconPencil
@ -163,6 +158,7 @@ declare interface Inverter {
export default defineComponent({
components: {
BasePage,
BootstrapAlert,
BIconTrash,
BIconPencil,
@ -177,6 +173,7 @@ export default defineComponent({
editInverterData: {} as Inverter,
deleteInverterData: {} as Inverter,
inverters: [] as Inverter[],
dataLoading: true,
alertMessage: "",
alertType: "info",
showAlert: false,
@ -198,9 +195,13 @@ export default defineComponent({
},
methods: {
getInverters() {
this.dataLoading = true;
fetch("/api/inverter/list")
.then((response) => response.json())
.then((data) => (this.inverters = data.inverter));
.then((data) => {
this.inverters = data.inverter;
this.dataLoading = false;
});
},
onSubmit() {
const formData = new FormData();

View File

@ -1,237 +1,228 @@
<template>
<div class="container-xxl" role="main">
<div class="page-header">
<h1>MqTT Settings</h1>
</div>
<BasePage :title="'MqTT Settings'" :isLoading="dataLoading">
<BootstrapAlert v-model="showAlert" dismissible :variant="alertType">
{{ alertMessage }}
</BootstrapAlert>
<div class="text-center" v-if="dataLoading">
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
<form @submit="saveMqttConfig">
<div class="card">
<div class="card-header text-white bg-primary">MqTT Configuration</div>
<div class="card-body">
<div class="row mb-3">
<label class="col-sm-4 form-check-label" for="inputMqtt">Enable MqTT</label>
<div class="col-sm-8">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="inputMqtt"
v-model="mqttConfigList.mqtt_enabled" />
</div>
</div>
</div>
<div class="row mb-3" v-show="mqttConfigList.mqtt_enabled">
<label class="col-sm-4 form-check-label" for="inputMqttHass">Enable Home Assistant MQTT Auto
Discovery</label>
<div class="col-sm-8">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="inputMqttHass"
v-model="mqttConfigList.mqtt_hass_enabled" />
</div>
</div>
</div>
</div>
</div>
</div>
<template v-if="!dataLoading">
<form @submit="saveMqttConfig">
<div class="card">
<div class="card-header text-white bg-primary">MqTT Configuration</div>
<div class="card-body">
<div class="row mb-3">
<label class="col-sm-4 form-check-label" for="inputMqtt">Enable MqTT</label>
<div class="col-sm-8">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="inputMqtt"
v-model="mqttConfigList.mqtt_enabled" />
</div>
</div>
</div>
<div class="row mb-3" v-show="mqttConfigList.mqtt_enabled">
<label class="col-sm-4 form-check-label" for="inputMqttHass">Enable Home Assistant MQTT Auto
Discovery</label>
<div class="col-sm-8">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="inputMqttHass"
v-model="mqttConfigList.mqtt_hass_enabled" />
</div>
</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 mt-5" v-show="mqttConfigList.mqtt_enabled">
<div class="card-header text-white bg-primary">
MqTT Broker Parameter
<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="128"
placeholder="Hostname or IP address" v-model="mqttConfigList.mqtt_hostname" />
</div>
</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="128"
placeholder="Hostname or IP address" v-model="mqttConfigList.mqtt_hostname" />
<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="password" 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 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 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 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 class="row mb-3">
<label class="col-sm-2 form-check-label" for="inputTls">Enable TLS</label>
<div class="col-sm-10">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="inputTls"
v-model="mqttConfigList.mqtt_tls" />
</div>
</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="password" 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 class="row mb-3">
<label class="col-sm-2 form-check-label" for="inputTls">Enable TLS</label>
<div class="col-sm-10">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="inputTls"
v-model="mqttConfigList.mqtt_tls" />
</div>
</div>
</div>
<div class="row mb-3" v-show="mqttConfigList.mqtt_tls">
<label for="inputCert" class="col-sm-2 col-form-label">CA-Root-Certificate (default
Letsencrypt):</label>
<div class="col-sm-10">
<textarea class="form-control" id="inputCert" maxlength="2048" rows="10"
placeholder="Root CA Certificate from Letsencrypt"
v-model="mqttConfigList.mqtt_root_ca_cert">
<div class="row mb-3" v-show="mqttConfigList.mqtt_tls">
<label for="inputCert" class="col-sm-2 col-form-label">CA-Root-Certificate (default
Letsencrypt):</label>
<div class="col-sm-10">
<textarea class="form-control" id="inputCert" maxlength="2048" rows="10"
placeholder="Root CA Certificate from Letsencrypt"
v-model="mqttConfigList.mqtt_root_ca_cert">
</textarea>
</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 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>
<div class="card mt-5" v-show="mqttConfigList.mqtt_enabled && mqttConfigList.mqtt_hass_enabled">
<div class="card-header text-white bg-primary">Home Assistant MQTT Auto Discovery Parameters</div>
<div class="card-body">
<div class="row mb-3">
<label for="inputHassTopic" class="col-sm-2 col-form-label">Prefix Topic:</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="inputHassTopic" maxlength="32"
placeholder="The prefix for the discovery topic"
v-model="mqttConfigList.mqtt_hass_topic" />
</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 class="col-sm-2 form-check-label" for="inputHassRetain">Enable Retain Flag</label>
<div class="col-sm-10">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="inputHassRetain"
v-model="mqttConfigList.mqtt_hass_retain" />
</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 class="row mb-3">
<label class="col-sm-2 form-check-label" for="inputHassExpire">Enable Expiration</label>
<div class="col-sm-10">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="inputHassExpire"
v-model="mqttConfigList.mqtt_hass_expire" />
</div>
</div>
</div>
<div class="row mb-3">
<label class="col-sm-2 form-check-label" for="inputIndividualPanels">Individual
Panels:</label>
<div class="col-sm-10">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="inputIndividualPanels"
v-model="mqttConfigList.mqtt_hass_individualpanels" />
</div>
</div>
</div>
</div>
</div>
</div>
<button type="submit" class="btn btn-primary mb-3">Save</button>
</form>
</template>
</div>
<div class="card mt-5" v-show="mqttConfigList.mqtt_enabled && mqttConfigList.mqtt_hass_enabled">
<div class="card-header text-white bg-primary">Home Assistant MQTT Auto Discovery Parameters</div>
<div class="card-body">
<div class="row mb-3">
<label for="inputHassTopic" class="col-sm-2 col-form-label">Prefix Topic:</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="inputHassTopic" maxlength="32"
placeholder="The prefix for the discovery topic"
v-model="mqttConfigList.mqtt_hass_topic" />
</div>
</div>
<div class="row mb-3">
<label class="col-sm-2 form-check-label" for="inputHassRetain">Enable Retain Flag</label>
<div class="col-sm-10">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="inputHassRetain"
v-model="mqttConfigList.mqtt_hass_retain" />
</div>
</div>
</div>
<div class="row mb-3">
<label class="col-sm-2 form-check-label" for="inputHassExpire">Enable Expiration</label>
<div class="col-sm-10">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="inputHassExpire"
v-model="mqttConfigList.mqtt_hass_expire" />
</div>
</div>
</div>
<div class="row mb-3">
<label class="col-sm-2 form-check-label" for="inputIndividualPanels">Individual
Panels:</label>
<div class="col-sm-10">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="inputIndividualPanels"
v-model="mqttConfigList.mqtt_hass_individualpanels" />
</div>
</div>
</div>
</div>
</div>
<button type="submit" class="btn btn-primary mb-3">Save</button>
</form>
</BasePage>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import BasePage from '@/components/BasePage.vue';
import BootstrapAlert from "@/components/BootstrapAlert.vue";
import type { MqttConfig } from "@/types/MqttConfig";
export default defineComponent({
components: {
BasePage,
BootstrapAlert,
},
data() {

View File

@ -1,168 +1,161 @@
<template>
<div class="container-xxl" role="main">
<div class="page-header">
<h1>MqTT Info</h1>
</div>
<div class="text-center" v-if="dataLoading">
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
<BasePage :title="'MqTT Info'" :isLoading="dataLoading">
<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 Interval</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>
<tr>
<th>TLS</th>
<td class="badge" :class="{
'bg-danger': !mqttDataList.mqtt_tls,
'bg-success': mqttDataList.mqtt_tls,
}">
<span v-if="mqttDataList.mqtt_tls">enabled</span>
<span v-else>disabled</span>
</td>
</tr>
<tr v-show="mqttDataList.mqtt_tls">
<th>Root CA Certifcate Info</th>
<td>{{ mqttDataList.mqtt_root_ca_cert_info }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<template v-if="!dataLoading">
<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 Interval</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>
<tr>
<th>TLS</th>
<td class="badge" :class="{
'bg-danger': !mqttDataList.mqtt_tls,
'bg-success': mqttDataList.mqtt_tls,
}">
<span v-if="mqttDataList.mqtt_tls">enabled</span>
<span v-else>disabled</span>
</td>
</tr>
<tr v-show="mqttDataList.mqtt_tls">
<th>Root CA Certifcate Info</th>
<td>{{ mqttDataList.mqtt_root_ca_cert_info }}</td>
</tr>
</tbody>
</table>
</div>
<div class="card mt-5">
<div class="card-header text-white bg-primary">Home Assistant MQTT Auto Discovery 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_hass_enabled,
'bg-success': mqttDataList.mqtt_hass_enabled,
}">
<span v-if="mqttDataList.mqtt_hass_enabled">enabled</span>
<span v-else>disabled</span>
</td>
</tr>
<tr>
<th>Base Topic</th>
<td>{{ mqttDataList.mqtt_hass_topic }}</td>
</tr>
<tr>
<th>Retain</th>
<td class="badge" :class="{
'bg-danger': !mqttDataList.mqtt_hass_retain,
'bg-success': mqttDataList.mqtt_hass_retain,
}">
<span v-if="mqttDataList.mqtt_hass_retain">enabled</span>
<span v-else>disabled</span>
</td>
</tr>
<tr>
<th>Expire</th>
<td class="badge" :class="{
'bg-danger': !mqttDataList.mqtt_hass_expire,
'bg-success': mqttDataList.mqtt_hass_expire,
}">
<span v-if="mqttDataList.mqtt_hass_expire">enabled</span>
<span v-else>disabled</span>
</td>
</tr>
<tr>
<th>Individual Panels</th>
<td class="badge" :class="{
'bg-danger': !mqttDataList.mqtt_hass_individualpanels,
'bg-success': mqttDataList.mqtt_hass_individualpanels,
}">
<span v-if="mqttDataList.mqtt_hass_individualpanels">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">Home Assistant MQTT Auto Discovery 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_hass_enabled,
'bg-success': mqttDataList.mqtt_hass_enabled,
}">
<span v-if="mqttDataList.mqtt_hass_enabled">enabled</span>
<span v-else>disabled</span>
</td>
</tr>
<tr>
<th>Base Topic</th>
<td>{{ mqttDataList.mqtt_hass_topic }}</td>
</tr>
<tr>
<th>Retain</th>
<td class="badge" :class="{
'bg-danger': !mqttDataList.mqtt_hass_retain,
'bg-success': mqttDataList.mqtt_hass_retain,
}">
<span v-if="mqttDataList.mqtt_hass_retain">enabled</span>
<span v-else>disabled</span>
</td>
</tr>
<tr>
<th>Expire</th>
<td class="badge" :class="{
'bg-danger': !mqttDataList.mqtt_hass_expire,
'bg-success': mqttDataList.mqtt_hass_expire,
}">
<span v-if="mqttDataList.mqtt_hass_expire">enabled</span>
<span v-else>disabled</span>
</td>
</tr>
<tr>
<th>Individual Panels</th>
<td class="badge" :class="{
'bg-danger': !mqttDataList.mqtt_hass_individualpanels,
'bg-success': mqttDataList.mqtt_hass_individualpanels,
}">
<span v-if="mqttDataList.mqtt_hass_individualpanels">enabled</span>
<span v-else>disabled</span>
</td>
</tr>
</tbody>
</table>
</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 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>
</template>
</div>
</div>
</BasePage>
</template>
<script lang="ts">
import type { MqttStatus } from '@/types/MqttStatus';
import { defineComponent } from 'vue';
import BasePage from '@/components/BasePage.vue';
import type { MqttStatus } from '@/types/MqttStatus';
export default defineComponent({
components: {
BasePage,
},
data() {
return {
dataLoading: true,

View File

@ -1,123 +1,114 @@
<template>
<div class="container-xxl" role="main">
<div class="page-header">
<h1>Network Settings</h1>
</div>
<BasePage :title="'Network Settings'" :isLoading="dataLoading">
<BootstrapAlert v-model="showAlert" dismissible :variant="alertType">
{{ alertMessage }}
</BootstrapAlert>
<div class="text-center" v-if="dataLoading">
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
<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="64"
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 class="alert alert-secondary" role="alert">
<b>Hint:</b> The text <span class="font-monospace">%06X</span> will be replaced
with the last 6 digits of the ESP ChipID in hex format.
</div>
</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>
<template v-if="!dataLoading">
<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 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="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="64"
placeholder="PSK" v-model="networkConfigList.password" />
</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="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 class="alert alert-secondary" role="alert">
<b>Hint:</b> The text <span class="font-monospace">%06X</span> will be replaced
with the last 6 digits of the ESP ChipID in hex format.
</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 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 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 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>
</template>
</div>
</div>
<button type="submit" class="btn btn-primary mb-3">Save</button>
</form>
</BasePage>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import BasePage from '@/components/BasePage.vue';
import BootstrapAlert from "@/components/BootstrapAlert.vue";
import type { NetworkConfig } from "@/types/NetworkkConfig";
export default defineComponent({
components: {
BasePage,
BootstrapAlert,
},
data() {

View File

@ -1,29 +1,19 @@
<template>
<div class="container-xxl" role="main">
<div class="page-header">
<h1>Network Info</h1>
</div>
<div class="text-center" v-if="dataLoading">
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
<template v-if="!dataLoading">
<WifiStationInfo :networkStatus="networkDataList" />
<div class="mt-5"></div>
<WifiApInfo :networkStatus="networkDataList" />
<div class="mt-5"></div>
<InterfaceNetworkInfo :networkStatus="networkDataList" />
<div class="mt-5"></div>
<InterfaceApInfo :networkStatus="networkDataList" />
<div class="mt-5"></div>
</template>
</div>
<BasePage :title="'Network Info'" :isLoading="dataLoading">
<WifiStationInfo :networkStatus="networkDataList" />
<div class="mt-5"></div>
<WifiApInfo :networkStatus="networkDataList" />
<div class="mt-5"></div>
<InterfaceNetworkInfo :networkStatus="networkDataList" />
<div class="mt-5"></div>
<InterfaceApInfo :networkStatus="networkDataList" />
<div class="mt-5"></div>
</BasePage>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import BasePage from '@/components/BasePage.vue';
import WifiStationInfo from "@/components/WifiStationInfo.vue";
import WifiApInfo from "@/components/WifiApInfo.vue";
import InterfaceNetworkInfo from "@/components/InterfaceNetworkInfo.vue";
@ -32,6 +22,7 @@ import type { NetworkStatus } from '@/types/NetworkStatus';
export default defineComponent({
components: {
BasePage,
WifiStationInfo,
WifiApInfo,
InterfaceNetworkInfo,

View File

@ -1,97 +1,85 @@
<template>
<div class="container-xxl" role="main">
<div class="page-header">
<h1>NTP Settings</h1>
</div>
<BasePage :title="'NTP Settings'" :isLoading="dataLoading || timezoneLoading">
<BootstrapAlert v-model="showAlert" dismissible :variant="alertType">
{{ alertMessage }}
</BootstrapAlert>
<div class="text-center" v-if="dataLoading || timezoneLoading">
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
<template v-if="!dataLoading && !timezoneLoading">
<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>
</template>
<template v-if="!dataLoading && !timezoneLoading">
<form @submit="saveNtpConfig">
<div class="card">
<div class="card-header text-white bg-primary">Manual Time Synchronization</div>
<div class="card-header text-white bg-primary">NTP Configuration</div>
<div class="card-body">
<div class="row mb-3">
<label for="currentMcuTime" class="col-sm-2 col-form-label">Current OpenDTU Time:</label>
<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="currentMcuTime" v-model="mcuTime" disabled />
<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="currentLocalTime" class="col-sm-2 col-form-label">Current Local Time:</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="currentLocalTime" v-model="localTime"
disabled />
</div>
</div>
<div class="text-center mb-3">
<button type="button" class="btn btn-danger" @click="setCurrentTime()"
title="Synchronize Time">Synchronize Time
</button>
</div>
<div class="alert alert-secondary" role="alert">
<b>Hint:</b> You can use the manual time synchronization to set the current time of OpenDTU if
no NTP server is available. But be aware, that in case of power cycle the time gets lost. Also
the time accurancy can be very bad as it is not resynchronised regularly.
</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>
</template>
</div>
<button type="submit" class="btn btn-primary mb-3">Save</button>
</form>
<div class="card">
<div class="card-header text-white bg-primary">Manual Time Synchronization</div>
<div class="card-body">
<div class="row mb-3">
<label for="currentMcuTime" class="col-sm-2 col-form-label">Current OpenDTU Time:</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="currentMcuTime" v-model="mcuTime" disabled />
</div>
</div>
<div class="row mb-3">
<label for="currentLocalTime" class="col-sm-2 col-form-label">Current Local Time:</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="currentLocalTime" v-model="localTime" disabled />
</div>
</div>
<div class="text-center mb-3">
<button type="button" class="btn btn-danger" @click="setCurrentTime()"
title="Synchronize Time">Synchronize Time
</button>
</div>
<div class="alert alert-secondary" role="alert">
<b>Hint:</b> You can use the manual time synchronization to set the current time of OpenDTU if
no NTP server is available. But be aware, that in case of power cycle the time gets lost. Also
the time accurancy can be very bad as it is not resynchronised regularly.
</div>
</div>
</div>
</BasePage>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import BasePage from '@/components/BasePage.vue';
import BootstrapAlert from "@/components/BootstrapAlert.vue";
import type { NtpConfig } from "@/types/NtpConfig";
export default defineComponent({
components: {
BasePage,
BootstrapAlert,
},
data() {

View File

@ -1,75 +1,66 @@
<template>
<div class="container-xxl" role="main">
<div class="page-header">
<h1>NTP Info</h1>
</div>
<div class="text-center" v-if="dataLoading">
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
<BasePage :title="'NTP Info'" :isLoading="dataLoading">
<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>
<template v-if="!dataLoading">
<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 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 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>
</template>
</div>
</div>
</BasePage>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import BasePage from '@/components/BasePage.vue';
import type { NtpStatus } from "@/types/NtpStatus";
export default defineComponent({
components: {
BasePage,
},
data() {
return {
dataLoading: true,

View File

@ -1,60 +1,51 @@
<template>
<div class="container-xxl" role="main">
<div class="page-header">
<h1>Security Settings</h1>
</div>
<BasePage :title="'Security Settings'" :isLoading="dataLoading">
<BootstrapAlert v-model="showAlert" dismissible :variant="alertType">
{{ alertMessage }}
</BootstrapAlert>
<div class="text-center" v-if="dataLoading">
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
<template v-if="!dataLoading">
<form @submit="savePasswordConfig">
<div class="card">
<div class="card-header text-white bg-primary">Admin password</div>
<div class="card-body">
<div class="row mb-3">
<label for="inputPassword" class="col-sm-2 col-form-label">Password:</label>
<div class="col-sm-10">
<input type="password" class="form-control" id="inputPassword" maxlength="64"
placeholder="Password" v-model="securityConfigList.password" />
</div>
<form @submit="savePasswordConfig">
<div class="card">
<div class="card-header text-white bg-primary">Admin password</div>
<div class="card-body">
<div class="row mb-3">
<label for="inputPassword" class="col-sm-2 col-form-label">Password:</label>
<div class="col-sm-10">
<input type="password" class="form-control" id="inputPassword" maxlength="64"
placeholder="Password" v-model="securityConfigList.password" />
</div>
<div class="row mb-3">
<label for="inputPasswordRepeat" class="col-sm-2 col-form-label">Repeat Password:</label>
<div class="col-sm-10">
<input type="password" class="form-control" id="inputPasswordRepeat" maxlength="64"
placeholder="Password" v-model="passwordRepeat" />
</div>
</div>
<div class="alert alert-secondary" role="alert">
<b>Hint:</b>
The administrator password is used to connect to the device when in AP mode.
It must be 8..64 characters.
</div>
</div>
<div class="row mb-3">
<label for="inputPasswordRepeat" class="col-sm-2 col-form-label">Repeat Password:</label>
<div class="col-sm-10">
<input type="password" class="form-control" id="inputPasswordRepeat" maxlength="64"
placeholder="Password" v-model="passwordRepeat" />
</div>
</div>
<div class="alert alert-secondary" role="alert">
<b>Hint:</b>
The administrator password is used to connect to the device when in AP mode.
It must be 8..64 characters.
</div>
</div>
<button type="submit" class="btn btn-primary mb-3">Save</button>
</form>
</template>
</div>
</div>
<button type="submit" class="btn btn-primary mb-3">Save</button>
</form>
</BasePage>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import BasePage from '@/components/BasePage.vue';
import BootstrapAlert from "@/components/BootstrapAlert.vue";
import type { SecurityConfig } from '@/types/SecurityConfig';
export default defineComponent({
components: {
BasePage,
BootstrapAlert,
},
data() {

View File

@ -1,30 +1,19 @@
<template>
<div class="container-xxl" role="main">
<div class="page-header">
<h1>System Info</h1>
</div>
<div class="text-center" v-if="dataLoading">
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
<template v-if="!dataLoading">
<FirmwareInfo :systemStatus="systemDataList" />
<div class="mt-5"></div>
<HardwareInfo :systemStatus="systemDataList" />
<div class="mt-5"></div>
<MemoryInfo :systemStatus="systemDataList" />
<div class="mt-5"></div>
<RadioInfo :systemStatus="systemDataList" />
<div class="mt-5"></div>
</template>
</div>
<BasePage :title="'System Info'" :isLoading="dataLoading">
<FirmwareInfo :systemStatus="systemDataList" />
<div class="mt-5"></div>
<HardwareInfo :systemStatus="systemDataList" />
<div class="mt-5"></div>
<MemoryInfo :systemStatus="systemDataList" />
<div class="mt-5"></div>
<RadioInfo :systemStatus="systemDataList" />
<div class="mt-5"></div>
</BasePage>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import BasePage from '@/components/BasePage.vue';
import HardwareInfo from "@/components/HardwareInfo.vue";
import FirmwareInfo from "@/components/FirmwareInfo.vue";
import MemoryInfo from "@/components/MemoryInfo.vue";
@ -33,6 +22,7 @@ import type { SystemStatus } from '@/types/SystemStatus';
export default defineComponent({
components: {
BasePage,
HardwareInfo,
FirmwareInfo,
MemoryInfo,