From da7c05ae84d2ba956d34e6f0d788ae351d1ae3c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Bo=CC=88hm?= Date: Sun, 14 Sep 2025 18:32:35 +0200 Subject: [PATCH 1/4] Implement 3-phase support for HMT inverters in live view --- src/WebApi_ws_live.cpp | 12 ++ webapp/src/components/InverterChannelInfo.vue | 13 +- webapp/src/locales/en.json | 1 + webapp/src/types/LiveDataStatus.ts | 7 + webapp/src/views/HomeView.vue | 169 ++++++++++++++++-- 5 files changed, 183 insertions(+), 19 deletions(-) diff --git a/src/WebApi_ws_live.cpp b/src/WebApi_ws_live.cpp index e30ec313b..7af7aca93 100644 --- a/src/WebApi_ws_live.cpp +++ b/src/WebApi_ws_live.cpp @@ -302,6 +302,18 @@ void WebApiWsLiveClass::generateInverterChannelJsonResponse(JsonObject& root, st addField(chanTypeObj, inv, t, c, FLD_PAC); addField(chanTypeObj, inv, t, c, FLD_UAC); addField(chanTypeObj, inv, t, c, FLD_IAC); + // Add HMT-specific 3-phase fields if available + if (t == TYPE_AC) { + addField(chanTypeObj, inv, t, c, FLD_UAC_1N); + addField(chanTypeObj, inv, t, c, FLD_UAC_2N); + addField(chanTypeObj, inv, t, c, FLD_UAC_3N); + addField(chanTypeObj, inv, t, c, FLD_UAC_12); + addField(chanTypeObj, inv, t, c, FLD_UAC_23); + addField(chanTypeObj, inv, t, c, FLD_UAC_31); + addField(chanTypeObj, inv, t, c, FLD_IAC_1); + addField(chanTypeObj, inv, t, c, FLD_IAC_2); + addField(chanTypeObj, inv, t, c, FLD_IAC_3); + } if (t == TYPE_INV) { addField(chanTypeObj, inv, t, c, FLD_PDC, "Power DC"); } else { diff --git a/webapp/src/components/InverterChannelInfo.vue b/webapp/src/components/InverterChannelInfo.vue index b7ebc27ba..2f8a9a091 100644 --- a/webapp/src/components/InverterChannelInfo.vue +++ b/webapp/src/components/InverterChannelInfo.vue @@ -11,12 +11,17 @@
- +
- {{ $t('inverterchannelinfo.Phase', { num: channelNumber + 1 }) }} + +
@@ -48,9 +53,11 @@ import { defineComponent, type PropType } from 'vue'; export default defineComponent({ props: { - channelData: { type: Object as PropType, required: true }, + channelData: { type: Object as PropType>, required: true }, channelType: { type: String, required: true }, channelNumber: { type: Number, required: true }, + phaseNumber: { type: Number, required: false }, + isThreePhaseInverter: { type: Boolean, required: false, default: false }, }, }); diff --git a/webapp/src/locales/en.json b/webapp/src/locales/en.json index 44de8cc57..6b154c988 100644 --- a/webapp/src/locales/en.json +++ b/webapp/src/locales/en.json @@ -446,6 +446,7 @@ "inverterchannelinfo": { "String": "String {num}", "Phase": "Phase {num}", + "AC": "AC", "General": "General" }, "invertertotalinfo": { diff --git a/webapp/src/types/LiveDataStatus.ts b/webapp/src/types/LiveDataStatus.ts index 71da175a8..f702e05a2 100644 --- a/webapp/src/types/LiveDataStatus.ts +++ b/webapp/src/types/LiveDataStatus.ts @@ -19,6 +19,13 @@ export interface InverterStatistics { ReactivePower?: ValueObject; Efficiency?: ValueObject; Irradiation?: ValueObject; + // 3-phase fields for HMT inverters + 'Voltage Ph1-N'?: ValueObject; + 'Voltage Ph2-N'?: ValueObject; + 'Voltage Ph3-N'?: ValueObject; + 'Current Ph1'?: ValueObject; + 'Current Ph2'?: ValueObject; + 'Current Ph3'?: ValueObject; } export interface RadioStatistics { diff --git a/webapp/src/views/HomeView.vue b/webapp/src/views/HomeView.vue index 69545f06e..f8715d3ac 100644 --- a/webapp/src/views/HomeView.vue +++ b/webapp/src/views/HomeView.vue @@ -190,32 +190,73 @@ ].reverse()" >
@@ -528,7 +569,7 @@ 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'; +import type { Inverter, LiveData, InverterStatistics } from '@/types/LiveDataStatus'; import { authHeader, authUrl, handleResponse, isLoggedIn } from '@/utils/authentication'; import * as bootstrap from 'bootstrap'; import { @@ -992,6 +1033,102 @@ export default defineComponent({ } return this.$n(val_small / val_large, 'percent'); }, + isThreePhaseInverter(inverter: Inverter): boolean { + // Check if the inverter has 3-phase specific fields + if (!inverter.AC || !inverter.AC[0]) { + return false; + } + + const acData = inverter.AC[0]; + // Look for 3-phase specific field names + const hasPhase1Voltage = !!acData['Voltage Ph1-N']; + const hasPhase2Voltage = !!acData['Voltage Ph2-N']; + const hasPhase3Voltage = !!acData['Voltage Ph3-N']; + + return hasPhase1Voltage && hasPhase2Voltage && hasPhase3Voltage; + }, + getPhaseData(acData: InverterStatistics, phase: number): Partial { + // Extract data for a specific phase from the AC data + const phaseData: Partial = {}; + + // Copy phase-specific voltage field + const voltageField = `Voltage Ph${phase}-N` as keyof InverterStatistics; + if (acData[voltageField]) { + phaseData.Voltage = acData[voltageField]; + } + + // Copy phase-specific current field + const currentField = `Current Ph${phase}` as keyof InverterStatistics; + if (acData[currentField]) { + phaseData.Current = acData[currentField]; + } + + // Add common fields that apply to all phases (frequency, power factor, etc.) + if (acData.Frequency) { + phaseData.Frequency = acData.Frequency; + } + if (acData.PowerFactor) { + phaseData.PowerFactor = acData.PowerFactor; + } + if (acData.Temperature) { + phaseData.Temperature = acData.Temperature; + } + + // For phase 1, also include total power and reactive power + if (phase === 1) { + if (acData.Power) { + phaseData.Power = acData.Power; + } + if (acData.ReactivePower) { + phaseData.ReactivePower = acData.ReactivePower; + } + } + + return phaseData; + }, + getPhaseDataWithPower(acData: InverterStatistics, phase: number): Partial { + // Extract data for a specific phase from the AC data with calculated power + const phaseData: Partial = {}; + + // Copy phase-specific voltage field + const voltageField = `Voltage Ph${phase}-N` as keyof InverterStatistics; + if (acData[voltageField]) { + phaseData.Voltage = acData[voltageField]; + } + + // Copy phase-specific current field + const currentField = `Current Ph${phase}` as keyof InverterStatistics; + if (acData[currentField]) { + phaseData.Current = acData[currentField]; + } + + // Add common fields that apply to all phases + if (acData.Frequency) { + phaseData.Frequency = acData.Frequency; + } + if (acData.PowerFactor) { + phaseData.PowerFactor = acData.PowerFactor; + } + if (acData.Temperature) { + phaseData.Temperature = acData.Temperature; + } + + // Calculate power for this phase: P = V * I * PowerFactor + const voltage = acData[voltageField]; + const current = acData[currentField]; + const powerFactor = acData.PowerFactor; + + if (voltage && current && powerFactor) { + phaseData.Power = { + v: voltage.v * current.v * powerFactor.v, + u: 'W', + d: 2, + max: 0, + }; + } + + return phaseData; + }, }, }); From 24649c538ee6e94ddaf8018935d7cb6843319426 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Bo=CC=88hm?= Date: Thu, 9 Oct 2025 20:09:24 +0200 Subject: [PATCH 2/4] remove unused fields --- src/WebApi_ws_live.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/WebApi_ws_live.cpp b/src/WebApi_ws_live.cpp index 7af7aca93..6ee77bfdb 100644 --- a/src/WebApi_ws_live.cpp +++ b/src/WebApi_ws_live.cpp @@ -307,9 +307,6 @@ void WebApiWsLiveClass::generateInverterChannelJsonResponse(JsonObject& root, st addField(chanTypeObj, inv, t, c, FLD_UAC_1N); addField(chanTypeObj, inv, t, c, FLD_UAC_2N); addField(chanTypeObj, inv, t, c, FLD_UAC_3N); - addField(chanTypeObj, inv, t, c, FLD_UAC_12); - addField(chanTypeObj, inv, t, c, FLD_UAC_23); - addField(chanTypeObj, inv, t, c, FLD_UAC_31); addField(chanTypeObj, inv, t, c, FLD_IAC_1); addField(chanTypeObj, inv, t, c, FLD_IAC_2); addField(chanTypeObj, inv, t, c, FLD_IAC_3); From a5e3b623cbf28c0ae3d78a5b070b4f20ea52cf67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Bo=CC=88hm?= Date: Thu, 9 Oct 2025 20:24:00 +0200 Subject: [PATCH 3/4] remove unused prop --- webapp/src/components/InverterChannelInfo.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/webapp/src/components/InverterChannelInfo.vue b/webapp/src/components/InverterChannelInfo.vue index 2f8a9a091..36d00342a 100644 --- a/webapp/src/components/InverterChannelInfo.vue +++ b/webapp/src/components/InverterChannelInfo.vue @@ -57,7 +57,6 @@ export default defineComponent({ channelType: { type: String, required: true }, channelNumber: { type: Number, required: true }, phaseNumber: { type: Number, required: false }, - isThreePhaseInverter: { type: Boolean, required: false, default: false }, }, }); From b9639c2bf0b3a3860bacc1394815b2ceb6dce65a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Bo=CC=88hm?= Date: Thu, 9 Oct 2025 20:27:03 +0200 Subject: [PATCH 4/4] cleanup homeview --- webapp/src/views/HomeView.vue | 94 ++++++++--------------------------- 1 file changed, 20 insertions(+), 74 deletions(-) diff --git a/webapp/src/views/HomeView.vue b/webapp/src/views/HomeView.vue index f8715d3ac..61dc9610a 100644 --- a/webapp/src/views/HomeView.vue +++ b/webapp/src/views/HomeView.vue @@ -192,22 +192,6 @@