webapp: Added device manager interface
Allows selecting a device profile which changes the pin assignment
This commit is contained in:
parent
5f699f4927
commit
e81a435ed9
@ -48,6 +48,9 @@
|
||||
<li>
|
||||
<router-link @click="onClick" class="dropdown-item" to="/settings/dtu">{{ $t('menu.DTUSettings') }}</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link @click="onClick" class="dropdown-item" to="/settings/device">{{ $t('menu.DeviceManager') }}</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<hr class="dropdown-divider" />
|
||||
</li>
|
||||
|
||||
58
webapp/src/components/PinInfo.vue
Normal file
58
webapp/src/components/PinInfo.vue
Normal file
@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<CardElement :text="$t('pininfo.PinOverview')" textVariant="text-bg-primary">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $t('pininfo.Category') }}</th>
|
||||
<th>{{ $t('pininfo.Name') }}</th>
|
||||
<th>{{ $t('pininfo.Number') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="6">NRF24</td>
|
||||
<td>MISO</td>
|
||||
<td>{{ pinAssignment?.nrf24?.miso }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MOSI</td>
|
||||
<td>{{ pinAssignment?.nrf24?.mosi }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CLK</td>
|
||||
<td>{{ pinAssignment?.nrf24?.clk }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IRQ</td>
|
||||
<td>{{ pinAssignment?.nrf24?.irq }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>EN</td>
|
||||
<td>{{ pinAssignment?.nrf24?.en }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CS</td>
|
||||
<td>{{ pinAssignment?.nrf24?.cs }}</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</CardElement>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import CardElement from '@/components/CardElement.vue';
|
||||
import type { Device } from '@/types/PinMapping';
|
||||
import { defineComponent, type PropType } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
CardElement,
|
||||
},
|
||||
props: {
|
||||
pinAssignment: { type: Object as PropType<Device | undefined>, required: true },
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@ -8,6 +8,7 @@
|
||||
"InverterSettings": "Wechselrichter Einstellungen",
|
||||
"SecuritySettings": "Sicherheitseinstellungen",
|
||||
"DTUSettings": "DTU Einstellungen",
|
||||
"DeviceManager": "Geräte-Manager",
|
||||
"ConfigManagement": "Konfigurationsverwaltung",
|
||||
"FirmwareUpgrade": "Firmware Aktualisierung",
|
||||
"DeviceReboot": "Geräteneustart",
|
||||
@ -82,7 +83,8 @@
|
||||
"10001": "Das Passwort muss zwischen 8 und {max} Zeichen lang sein!",
|
||||
"10002": "Authentifizierung erfolgreich!",
|
||||
"11001": "@:apiresponse.2001",
|
||||
"11002": "@:apiresponse:5004"
|
||||
"11002": "@:apiresponse:5004",
|
||||
"12001": "Profil muss zwischen 1 und {max} Zeichen lang sein!"
|
||||
},
|
||||
"home": {
|
||||
"LiveData": "Live Daten",
|
||||
@ -466,5 +468,19 @@
|
||||
"TimeSyncLink": "Bitte überprüfen Sie Ihre Zeiteinstellungen.",
|
||||
"DefaultPassword": "Sie verwenden das Standardpasswort für die Weboberfläche und den Notfall Access Point. Dies ist potenziell unsicher.",
|
||||
"DefaultPasswordLink": "Bitte ändern Sie das Passwort."
|
||||
},
|
||||
"deviceadmin": {
|
||||
"DeviceManager": "Geräte-Manager",
|
||||
"PinAssignment": "Anschlusseinstellungen",
|
||||
"SelectedProfile": "Ausgewähltes Profil:",
|
||||
"DefaultProfile": "(Standard Einstellungen)",
|
||||
"ProfileHint": "Ihr Gerät reagiert möglicherweise nicht mehr, wenn Sie ein inkompatibles Profil wählen. In diesem Fall müssen Sie eine Löschung über das serielle Interface durchführen.",
|
||||
"Save": "@:dtuadmin.Save"
|
||||
},
|
||||
"pininfo": {
|
||||
"PinOverview": "Anschlussübersicht",
|
||||
"Category": "Kategorie",
|
||||
"Name": "Name",
|
||||
"Number": "Nummer"
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,7 @@
|
||||
"InverterSettings": "Inverter Settings",
|
||||
"SecuritySettings": "Security Settings",
|
||||
"DTUSettings": "DTU Settings",
|
||||
"DeviceManager": "Device-Manager",
|
||||
"ConfigManagement": "Config Management",
|
||||
"FirmwareUpgrade": "Firmware Upgrade",
|
||||
"DeviceReboot": "Device Reboot",
|
||||
@ -82,7 +83,8 @@
|
||||
"10001": "Password must between 8 and {max} characters long!",
|
||||
"10002": "Authentication successfull!",
|
||||
"11001": "@:apiresponse.2001",
|
||||
"11002": "@:apiresponse:5004"
|
||||
"11002": "@:apiresponse:5004",
|
||||
"12001": "Profil must between 1 and {max} characters long!"
|
||||
},
|
||||
"home": {
|
||||
"LiveData": "Live Data",
|
||||
@ -466,5 +468,19 @@
|
||||
"TimeSyncLink": "Please check your time settings.",
|
||||
"DefaultPassword": "You are using the default password for the web interface and the emergency access point. This is potentially insecure.",
|
||||
"DefaultPasswordLink": "Please change the password."
|
||||
},
|
||||
"deviceadmin": {
|
||||
"DeviceManager": "Device-Manager",
|
||||
"PinAssignment": "Connection settings",
|
||||
"SelectedProfile": "Selected profile:",
|
||||
"DefaultProfile": "(Default settings)",
|
||||
"ProfileHint": "Your device may stop responding if you select an incompatible profile. In this case, you must perform a deletion via the serial interface.",
|
||||
"Save": "@:dtuadmin.Save"
|
||||
},
|
||||
"pininfo": {
|
||||
"PinOverview": "Connection overview",
|
||||
"Category": "Category",
|
||||
"Name": "Name",
|
||||
"Number": "Number"
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,7 @@
|
||||
"InverterSettings": "Paramètres des onduleurs",
|
||||
"SecuritySettings": "Paramètres de sécurité",
|
||||
"DTUSettings": "Paramètres DTU",
|
||||
"DeviceManager": "Device-Manager",
|
||||
"ConfigManagement": "Gestion de la configuration",
|
||||
"FirmwareUpgrade": "Mise à jour du firmware",
|
||||
"DeviceReboot": "Redémarrage de l'appareil",
|
||||
@ -82,7 +83,8 @@
|
||||
"10001": "Le mot de passe doit comporter entre 8 et {max} caractères !",
|
||||
"10002": "Authentification réussie !",
|
||||
"11001": "@:apiresponse.2001",
|
||||
"11002": "@:apiresponse:5004"
|
||||
"11002": "@:apiresponse:5004",
|
||||
"12001": "Profil must between 1 and {max} characters long!"
|
||||
},
|
||||
"home": {
|
||||
"LiveData": "Données en direct",
|
||||
@ -466,5 +468,19 @@
|
||||
"TimeSyncLink": "Veuillez vérifier vos paramètres horaires.",
|
||||
"DefaultPassword": "Vous utilisez le mot de passe par défaut pour l'interface Web et le point d'accès d'urgence. Ceci est potentiellement non sécurisé.",
|
||||
"DefaultPasswordLink": "Merci de changer le mot de passe."
|
||||
},
|
||||
"deviceadmin": {
|
||||
"DeviceManager": "Device-Manager",
|
||||
"PinAssignment": "Connection settings",
|
||||
"SelectedProfile": "Selected profile:",
|
||||
"DefaultProfile": "(Default settings)",
|
||||
"ProfileHint": "Your device may stop responding if you select an incompatible profile. In this case, you must perform a deletion via the serial interface.",
|
||||
"Save": "@:dtuadmin.Save"
|
||||
},
|
||||
"pininfo": {
|
||||
"PinOverview": "Connection overview",
|
||||
"Category": "Category",
|
||||
"Name": "Name",
|
||||
"Number": "Number"
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
import AboutView from '@/views/AboutView.vue';
|
||||
import ConfigAdminView from '@/views/ConfigAdminView.vue';
|
||||
import ConsoleInfoView from '@/views/ConsoleInfoView.vue';
|
||||
import DeviceAdminView from '@/views/DeviceAdminView.vue'
|
||||
import DtuAdminView from '@/views/DtuAdminView.vue';
|
||||
import FirmwareUpgradeView from '@/views/FirmwareUpgradeView.vue';
|
||||
import HomeView from '@/views/HomeView.vue';
|
||||
@ -86,6 +87,11 @@ const router = createRouter({
|
||||
name: 'DTU Settings',
|
||||
component: DtuAdminView
|
||||
},
|
||||
{
|
||||
path: '/settings/device',
|
||||
name: 'Device Manager',
|
||||
component: DeviceAdminView
|
||||
},
|
||||
{
|
||||
path: '/firmware/upgrade',
|
||||
name: 'Firmware Upgrade',
|
||||
|
||||
3
webapp/src/types/DeviceConfig.ts
Normal file
3
webapp/src/types/DeviceConfig.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export interface DeviceConfig {
|
||||
dev_pinmapping: string;
|
||||
}
|
||||
15
webapp/src/types/PinMapping.ts
Normal file
15
webapp/src/types/PinMapping.ts
Normal file
@ -0,0 +1,15 @@
|
||||
export interface Nrf24 {
|
||||
miso: number;
|
||||
mosi: number;
|
||||
clk: number;
|
||||
irq: number;
|
||||
en: number;
|
||||
cs: number;
|
||||
}
|
||||
|
||||
export interface Device {
|
||||
name: string;
|
||||
nrf24: Nrf24;
|
||||
}
|
||||
|
||||
export interface PinMapping extends Array<Device>{}
|
||||
134
webapp/src/views/DeviceAdminView.vue
Normal file
134
webapp/src/views/DeviceAdminView.vue
Normal file
@ -0,0 +1,134 @@
|
||||
<template>
|
||||
<BasePage :title="$t('deviceadmin.DeviceManager')" :isLoading="dataLoading || pinMappingLoading">
|
||||
<BootstrapAlert v-model="showAlert" dismissible :variant="alertType">
|
||||
{{ alertMessage }}
|
||||
</BootstrapAlert>
|
||||
|
||||
<form @submit="saveDtuConfig">
|
||||
|
||||
<nav>
|
||||
<div class="nav nav-tabs" id="nav-tab" role="tablist">
|
||||
<button class="nav-link active" id="nav-pin-tab" data-bs-toggle="tab" data-bs-target="#nav-pin"
|
||||
type="button" role="tab" aria-controls="nav-pin" aria-selected="true">{{
|
||||
$t('deviceadmin.PinAssignment')
|
||||
}}</button>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="tab-content" id="nav-tabContent">
|
||||
<div class="tab-pane fade show active" id="nav-pin" role="tabpanel" aria-labelledby="nav-pin-tab"
|
||||
tabindex="0">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
|
||||
<div class="row mb-3">
|
||||
<label for="inputPinProfile" class="col-sm-2 col-form-label">{{
|
||||
$t('deviceadmin.SelectedProfile')
|
||||
}}</label>
|
||||
<div class="col-sm-10">
|
||||
<select class="form-select" id="inputPinProfile" v-model="deviceConfigList.dev_pinmapping">
|
||||
<option v-for="device in pinMappingList" :value="device.name">
|
||||
{{ device.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-danger mt-3" role="alert" v-html="$t('deviceadmin.ProfileHint')"></div>
|
||||
|
||||
<PinInfo :pinAssignment="pinMappingList.find(i => i.name === deviceConfigList.dev_pinmapping)" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary mb-3">{{ $t('deviceadmin.Save') }}</button>
|
||||
</form>
|
||||
</BasePage>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import BasePage from '@/components/BasePage.vue';
|
||||
import BootstrapAlert from "@/components/BootstrapAlert.vue";
|
||||
import InputElement from '@/components/InputElement.vue';
|
||||
import PinInfo from '@/components/PinInfo.vue';
|
||||
import type { DeviceConfig } from "@/types/DeviceConfig";
|
||||
import type { PinMapping, Device } from "@/types/PinMapping";
|
||||
import { authHeader, handleResponse } from '@/utils/authentication';
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
BasePage,
|
||||
BootstrapAlert,
|
||||
InputElement,
|
||||
PinInfo,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dataLoading: true,
|
||||
pinMappingLoading: true,
|
||||
deviceConfigList: {} as DeviceConfig,
|
||||
pinMappingList: {} as PinMapping,
|
||||
alertMessage: "",
|
||||
alertType: "info",
|
||||
showAlert: false,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getDeviceConfig();
|
||||
this.getPinMappingList();
|
||||
},
|
||||
methods: {
|
||||
getPinMappingList() {
|
||||
this.pinMappingLoading = true;
|
||||
fetch("/api/config/get?file=pin_mapping.json", { headers: authHeader() })
|
||||
.then((response) => handleResponse(response, this.$emitter, this.$router))
|
||||
.then(
|
||||
(data) => {
|
||||
this.pinMappingList = data;
|
||||
this.pinMappingList.push({
|
||||
"name": this.$t('deviceadmin.DefaultProfile')
|
||||
} as Device);
|
||||
this.pinMappingList.sort((a, b) => (a.name < b.name) ? -1 : 1);
|
||||
this.pinMappingLoading = false;
|
||||
}
|
||||
)
|
||||
.catch(() => {
|
||||
this.pinMappingLoading = false;
|
||||
});
|
||||
},
|
||||
getDeviceConfig() {
|
||||
this.dataLoading = true;
|
||||
fetch("/api/device/config", { headers: authHeader() })
|
||||
.then((response) => handleResponse(response, this.$emitter, this.$router))
|
||||
.then(
|
||||
(data) => {
|
||||
this.deviceConfigList = data;
|
||||
this.dataLoading = false;
|
||||
}
|
||||
);
|
||||
},
|
||||
saveDtuConfig(e: Event) {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("data", JSON.stringify(this.deviceConfigList));
|
||||
|
||||
fetch("/api/device/config", {
|
||||
method: "POST",
|
||||
headers: authHeader(),
|
||||
body: formData,
|
||||
})
|
||||
.then((response) => handleResponse(response, this.$emitter, this.$router))
|
||||
.then(
|
||||
(response) => {
|
||||
this.alertMessage = this.$t('apiresponse.' + response.code, response.param);
|
||||
this.alertType = response.type;
|
||||
this.showAlert = true;
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
Loading…
Reference in New Issue
Block a user