mirror of
https://github.com/tbnobody/OpenDTU.git
synced 2025-12-11 09:20:34 +01:00
webapp: show precise data age for inverters and introduce and use DataAgeDisplay component
thanks to @schlimmchen
This commit is contained in:
@@ -61,6 +61,10 @@
|
||||
"Light": "Φωτεινό",
|
||||
"Auto": "Αυτόματο"
|
||||
},
|
||||
"dataagedisplay": {
|
||||
"DataAge": "Τελευταία ενημέρωση πρίν από",
|
||||
"SecondsSince": "0 δευτερόλεπτα | 1 δευτερόλεπτα | {n} δευτερόλεπτα"
|
||||
},
|
||||
"apiresponse": {
|
||||
"1001": "Οι ρυθμίσεις αποθηκεύτηκαν!",
|
||||
"1002": "Δεν βρέθηκαν τιμές!",
|
||||
@@ -134,8 +138,6 @@
|
||||
"LiveData": "Τηλεμετρία",
|
||||
"SerialNumber": "Σειριακός Αριθμός: ",
|
||||
"CurrentLimit": "Τρέχον Όριο: ",
|
||||
"DataAge": "Τελευταία ενημέρωση πρίν από: ",
|
||||
"Seconds": "{val} δευτερόλεπτα",
|
||||
"ShowSetInverterLimit": "Εμφάνιση/Ρύθμιση ορίου μετατροπέα",
|
||||
"TurnOnOff": "Ενεργοποίηση/απενεργοποίηση του μετατροπέα",
|
||||
"ShowInverterInfo": "Εμφάνιση πληροφοριών του μετατροπέα",
|
||||
|
||||
@@ -61,6 +61,10 @@
|
||||
"Light": "Claro",
|
||||
"Auto": "Automático"
|
||||
},
|
||||
"dataagedisplay": {
|
||||
"DataAge": "Edad de los Datos",
|
||||
"SecondsSince": "0 segundos | 1 segundos | {n} segundos"
|
||||
},
|
||||
"apiresponse": {
|
||||
"1001": "¡Opciones guardadas!",
|
||||
"1002": "No se encontraron valores",
|
||||
@@ -134,8 +138,6 @@
|
||||
"LiveData": "Datos en Vivo",
|
||||
"SerialNumber": "Número de Serie: ",
|
||||
"CurrentLimit": "Límite de Corriente: ",
|
||||
"DataAge": "Edad de los Datos: ",
|
||||
"Seconds": "{val} segundos",
|
||||
"ShowSetInverterLimit": "Ver / Establecer Límite del Inversor",
|
||||
"TurnOnOff": "Encender/Apagar el Inversor",
|
||||
"ShowInverterInfo": "Ver Información del Inversor",
|
||||
|
||||
@@ -61,6 +61,10 @@
|
||||
"Light": "Chiaro",
|
||||
"Auto": "Automatico"
|
||||
},
|
||||
"dataagedisplay": {
|
||||
"DataAge": "Aggiornamento Dati",
|
||||
"SecondsSince": "0 secondi | 1 secondi | {n} secondi"
|
||||
},
|
||||
"apiresponse": {
|
||||
"1001": "Settings saved!",
|
||||
"1002": "No values found!",
|
||||
@@ -134,8 +138,6 @@
|
||||
"LiveData": "Dati in tempo reale",
|
||||
"SerialNumber": "Numero seriale: ",
|
||||
"CurrentLimit": "Limite attuale: ",
|
||||
"DataAge": "Aggiornamento Dati: ",
|
||||
"Seconds": "{val} secondi",
|
||||
"ShowSetInverterLimit": "Mostra / Imposta Limite di Potenza",
|
||||
"TurnOnOff": "Accendi/Spegni Inverter",
|
||||
"ShowInverterInfo": "Mostra info Inverter",
|
||||
|
||||
@@ -61,6 +61,10 @@
|
||||
"Light": "Jasny",
|
||||
"Auto": "Automatyczny"
|
||||
},
|
||||
"dataagedisplay": {
|
||||
"DataAge": "Aktualizacja danych",
|
||||
"SecondsSince": "0 sekund | 1 sekund | {n} sekund"
|
||||
},
|
||||
"apiresponse": {
|
||||
"1001": "Ustawienia zapisane!",
|
||||
"1002": "Nie znaleziono żadnych wartości!",
|
||||
@@ -134,8 +138,6 @@
|
||||
"LiveData": "Dane na żywo",
|
||||
"SerialNumber": "Numer seryjny: ",
|
||||
"CurrentLimit": "Aktualny limit: ",
|
||||
"DataAge": "Aktualizacja danych: ",
|
||||
"Seconds": "{val} sekund",
|
||||
"ShowSetInverterLimit": "Pokaż / ustaw limit mocy",
|
||||
"TurnOnOff": "Włącz /wyłącz falownik",
|
||||
"ShowInverterInfo": "Pokaż informacje o falowniku",
|
||||
|
||||
@@ -146,6 +146,7 @@ void WebApiWsLiveClass::generateInverterCommonJsonResponse(JsonObject& root, std
|
||||
root["name"] = inv->name();
|
||||
root["order"] = inv_cfg->Order;
|
||||
root["data_age"] = (millis() - inv->Statistics()->getLastUpdate()) / 1000;
|
||||
root["data_age_ms"] = millis() - inv->Statistics()->getLastUpdate();
|
||||
root["poll_enabled"] = inv->getEnablePolling();
|
||||
root["reachable"] = inv->isReachable();
|
||||
root["producing"] = inv->isProducing();
|
||||
|
||||
34
webapp/src/components/DataAgeDisplay.vue
Normal file
34
webapp/src/components/DataAgeDisplay.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div style="padding-right: 2em">
|
||||
{{ $t('dataagedisplay.DataAge') }}:
|
||||
{{ $t('dataagedisplay.SecondsSince', { n: dataAgeSeconds }) }}
|
||||
<template v-if="dataAgeMs > thresholdMs"> ({{ calculateAbsoluteTime(dataAgeMs) }}) </template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'DataAgeDisplay',
|
||||
props: {
|
||||
dataAgeMs: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
thresholdMs: {
|
||||
type: Number,
|
||||
default: 300000,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
calculateAbsoluteTime(lastTime: number): string {
|
||||
const date = new Date(Date.now() - lastTime);
|
||||
return this.$d(date, 'datetime');
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
dataAgeSeconds() {
|
||||
return Math.floor(this.dataAgeMs / 1000);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -46,6 +46,10 @@
|
||||
"Light": "Hell",
|
||||
"Auto": "Automatisch"
|
||||
},
|
||||
"dataagedisplay": {
|
||||
"DataAge": "Letzte Aktualisierung",
|
||||
"SecondsSince": "vor 0 Sekunden | vor 1 Sekunde | vor {n} Sekunden"
|
||||
},
|
||||
"apiresponse": {
|
||||
"1001": "Einstellungen gespeichert!",
|
||||
"1002": "Keine Werte gefunden!",
|
||||
@@ -119,8 +123,6 @@
|
||||
"LiveData": "Live-Daten",
|
||||
"SerialNumber": "Seriennummer: ",
|
||||
"CurrentLimit": "Aktuelles Limit: ",
|
||||
"DataAge": "Letzte Aktualisierung: ",
|
||||
"Seconds": "vor {val} Sekunden",
|
||||
"ShowSetInverterLimit": "Zeige / Setze Wechselrichterlimit",
|
||||
"TurnOnOff": "Schalte Wechselrichter ein oder aus",
|
||||
"ShowInverterInfo": "Zeige Wechselrichter-Informationen",
|
||||
|
||||
@@ -46,6 +46,10 @@
|
||||
"Light": "Light",
|
||||
"Auto": "Auto"
|
||||
},
|
||||
"dataagedisplay": {
|
||||
"DataAge": "Data Age",
|
||||
"SecondsSince": "0 seconds | 1 second | {n} seconds"
|
||||
},
|
||||
"apiresponse": {
|
||||
"1001": "Settings saved!",
|
||||
"1002": "No values found!",
|
||||
@@ -119,8 +123,6 @@
|
||||
"LiveData": "Live Data",
|
||||
"SerialNumber": "Serial Number: ",
|
||||
"CurrentLimit": "Current Limit: ",
|
||||
"DataAge": "Data Age: ",
|
||||
"Seconds": "{val} seconds",
|
||||
"ShowSetInverterLimit": "Show / Set Inverter Limit",
|
||||
"TurnOnOff": "Turn Inverter on/off",
|
||||
"ShowInverterInfo": "Show Inverter Info",
|
||||
|
||||
@@ -46,6 +46,10 @@
|
||||
"Light": "Clair",
|
||||
"Auto": "Auto"
|
||||
},
|
||||
"dataagedisplay": {
|
||||
"DataAge": "Âge des données",
|
||||
"SecondsSince": "0 secondes | 1 seconde | {n} secondes"
|
||||
},
|
||||
"apiresponse": {
|
||||
"1001": "Paramètres enregistrés !",
|
||||
"1002": "Aucune valeur trouvée !",
|
||||
@@ -119,8 +123,6 @@
|
||||
"LiveData": "Données en direct",
|
||||
"SerialNumber": "Numéro de série : ",
|
||||
"CurrentLimit": "Limite de courant : ",
|
||||
"DataAge": "Âge des données : ",
|
||||
"Seconds": "{val} secondes",
|
||||
"ShowSetInverterLimit": "Afficher / Régler la limite de l'onduleur",
|
||||
"TurnOnOff": "Allumer / Eteindre l'onduleur",
|
||||
"ShowInverterInfo": "Afficher les informations sur l'onduleur",
|
||||
|
||||
@@ -36,6 +36,7 @@ export interface Inverter {
|
||||
name: string;
|
||||
order: number;
|
||||
data_age: number;
|
||||
data_age_ms: number;
|
||||
poll_enabled: boolean;
|
||||
reachable: boolean;
|
||||
producing: boolean;
|
||||
|
||||
@@ -98,11 +98,7 @@
|
||||
>{{ $n(inverter.limit_relative / 100, 'percentOneDigit') }}
|
||||
</div>
|
||||
<div style="padding-right: 2em">
|
||||
{{ $t('home.DataAge') }}
|
||||
{{ $t('home.Seconds', { val: $n(inverter.data_age) }) }}
|
||||
<template v-if="inverter.data_age > 300">
|
||||
/ {{ calculateAbsoluteTime(inverter.data_age) }}
|
||||
</template>
|
||||
<DataAgeDisplay :data-age-ms="inverter.data_age_ms" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -504,6 +500,7 @@
|
||||
<script lang="ts">
|
||||
import BasePage from '@/components/BasePage.vue';
|
||||
import BootstrapAlert from '@/components/BootstrapAlert.vue';
|
||||
import DataAgeDisplay from '@/components/DataAgeDisplay.vue';
|
||||
import DevInfo from '@/components/DevInfo.vue';
|
||||
import EventLog from '@/components/EventLog.vue';
|
||||
import GridProfile from '@/components/GridProfile.vue';
|
||||
@@ -513,8 +510,8 @@ import InverterTotalInfo from '@/components/InverterTotalInfo.vue';
|
||||
import ModalDialog from '@/components/ModalDialog.vue';
|
||||
import type { DevInfoStatus } from '@/types/DevInfoStatus';
|
||||
import type { EventlogItems } from '@/types/EventlogStatus';
|
||||
import type { GridProfileStatus } from '@/types/GridProfileStatus';
|
||||
import type { GridProfileRawdata } from '@/types/GridProfileRawdata';
|
||||
import type { GridProfileStatus } from '@/types/GridProfileStatus';
|
||||
import type { LimitConfig } from '@/types/LimitConfig';
|
||||
import type { LimitStatus } from '@/types/LimitStatus';
|
||||
import type { Inverter, LiveData } from '@/types/LiveDataStatus';
|
||||
@@ -538,6 +535,7 @@ export default defineComponent({
|
||||
components: {
|
||||
BasePage,
|
||||
BootstrapAlert,
|
||||
DataAgeDisplay,
|
||||
DevInfo,
|
||||
EventLog,
|
||||
GridProfile,
|
||||
@@ -562,7 +560,7 @@ export default defineComponent({
|
||||
|
||||
socket: {} as WebSocket,
|
||||
heartInterval: 0,
|
||||
dataAgeInterval: 0,
|
||||
dataAgeTimers: {} as Record<string, number>,
|
||||
dataLoading: true,
|
||||
liveData: {} as LiveData,
|
||||
isFirstFetchAfterConnect: true,
|
||||
@@ -607,7 +605,6 @@ export default defineComponent({
|
||||
created() {
|
||||
this.getInitialData();
|
||||
this.initSocket();
|
||||
this.initDataAgeing();
|
||||
this.$emitter.on('logged-in', () => {
|
||||
this.isLogged = this.isLoggedIn();
|
||||
});
|
||||
@@ -706,8 +703,10 @@ export default defineComponent({
|
||||
);
|
||||
if (foundIdx == -1) {
|
||||
Object.assign(this.liveData.inverters, newData.inverters);
|
||||
this.liveData.inverters.forEach((inv) => this.resetDataAging(inv));
|
||||
} else {
|
||||
Object.assign(this.liveData.inverters[foundIdx], newData.inverters[0]);
|
||||
this.resetDataAging(this.liveData.inverters[foundIdx]);
|
||||
}
|
||||
this.dataLoading = false;
|
||||
this.heartCheck(); // Reset heartbeat detection
|
||||
@@ -734,13 +733,26 @@ export default defineComponent({
|
||||
this.closeSocket();
|
||||
};
|
||||
},
|
||||
initDataAgeing() {
|
||||
this.dataAgeInterval = setInterval(() => {
|
||||
if (this.inverterData) {
|
||||
this.inverterData.forEach((element) => {
|
||||
element.data_age++;
|
||||
});
|
||||
}
|
||||
resetDataAging(inv: Inverter) {
|
||||
if (this.dataAgeTimers[inv.serial] !== undefined) {
|
||||
clearTimeout(this.dataAgeTimers[inv.serial]);
|
||||
}
|
||||
|
||||
const nextMs = 1000 - (inv.data_age_ms % 1000);
|
||||
this.dataAgeTimers[inv.serial] = setTimeout(() => {
|
||||
this.doDataAging(inv.serial);
|
||||
}, nextMs);
|
||||
},
|
||||
doDataAging(serial: string) {
|
||||
const inv = this.liveData?.inverters?.find((inv) => inv.serial === serial);
|
||||
if (inv === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
inv.data_age_ms += 1000;
|
||||
|
||||
this.dataAgeTimers[serial] = setTimeout(() => {
|
||||
this.doDataAging(serial);
|
||||
}, 1000);
|
||||
},
|
||||
// Send heartbeat packets regularly * 59s Send a heartbeat
|
||||
@@ -918,10 +930,6 @@ export default defineComponent({
|
||||
}
|
||||
});
|
||||
},
|
||||
calculateAbsoluteTime(lastTime: number): string {
|
||||
const date = new Date(Date.now() - lastTime * 1000);
|
||||
return this.$d(date, 'datetime');
|
||||
},
|
||||
getSumIrridiation(inv: Inverter): number {
|
||||
let total = 0;
|
||||
Object.keys(inv.DC).forEach((key) => {
|
||||
|
||||
Reference in New Issue
Block a user