BREAKING CHANGE: Web API!

In order to support multiple AC channels in future the WebAPI had to be changed. AC and DC channels are now grouped in a sub object containing the channels beginning with 0.
This commit is contained in:
Thomas Basler 2023-02-13 18:37:55 +01:00
parent ceaf08c1a0
commit 3b7aef63f8
8 changed files with 394 additions and 284 deletions

View File

@ -56,7 +56,7 @@ You can "talk" to the OpenDTU with a command line tool like `curl`. The output i
```
~$ curl http://192.168.10.10/api/livedata/status
{"inverters":[{"serial":"11418186xxxx","name":"HM600","data_age":4,"reachable":true,"producing":true,"limit_relative":100,"limit_absolute":600,"0":{"Power":{"v":70.69999695,"u":"W","d":1},"Voltage":{"v":233,"u":"V","d":1},"Current":{"v":0.300000012,"u":"A","d":2},"Power DC":{"v":74,"u":"W","d":2},"YieldDay":{"v":23,"u":"Wh","d":2},"YieldTotal":{"v":150.5050049,"u":"kWh","d":2},"Frequency":{"v":50.02000046,"u":"Hz","d":2},"Temperature":{"v":8.300000191,"u":"°C","d":1},"PowerFactor":{"v":1,"u":"","d":3},"ReactivePower":{"v":0.100000001,"u":"var","d":1},"Efficiency":{"v":95.54053497,"u":"%","d":2}},"1":{"Power":{"v":0,"u":"W","d":1},"Voltage":{"v":1,"u":"V","d":1},"Current":{"v":0.02,"u":"A","d":2},"YieldDay":{"v":0,"u":"Wh","d":0},"YieldTotal":{"v":49.0320015,"u":"kWh","d":3},"Irradiation":{"v":0,"u":"%","d":2}},"2":{"Power":{"v":74,"u":"W","d":1},"Voltage":{"v":42.40000153,"u":"V","d":1},"Current":{"v":1.74000001,"u":"A","d":2},"YieldDay":{"v":23,"u":"Wh","d":0},"YieldTotal":{"v":101.4729996,"u":"kWh","d":3},"Irradiation":{"v":18.04878044,"u":"%","d":2}},"events":3},{"serial":"11418180xxxx","name":"HM800","data_age":11,"reachable":true,"producing":true,"limit_relative":100,"limit_absolute":800,"0":{"Power":{"v":70.09999847,"u":"W","d":1},"Voltage":{"v":233.1000061,"u":"V","d":1},"Current":{"v":0.300000012,"u":"A","d":2},"Power DC":{"v":73.59999847,"u":"W","d":2},"YieldDay":{"v":48,"u":"Wh","d":2},"YieldTotal":{"v":48.5399971,"u":"kWh","d":2},"Frequency":{"v":50.02000046,"u":"Hz","d":2},"Temperature":{"v":11.39999962,"u":"°C","d":1},"PowerFactor":{"v":1,"u":"","d":3},"ReactivePower":{"v":0.100000001,"u":"var","d":1},"Efficiency":{"v":95.24456024,"u":"%","d":2}},"1":{"Power":{"v":36.5,"u":"W","d":1},"Voltage":{"v":39.09999847,"u":"V","d":1},"Current":{"v":0.930000007,"u":"A","d":2},"YieldDay":{"v":31,"u":"Wh","d":0},"YieldTotal":{"v":4.301000118,"u":"kWh","d":3},"Irradiation":{"v":8.902439117,"u":"%","d":2}},"2":{"Power":{"v":37.09999847,"u":"W","d":1},"Voltage":{"v":40.79999924,"u":"V","d":1},"Current":{"v":0.910000026,"u":"A","d":2},"YieldDay":{"v":17,"u":"Wh","d":0},"YieldTotal":{"v":44.23899841,"u":"kWh","d":3},"Irradiation":{"v":9.048780441,"u":"%","d":2}},"events":1}],"total":{"Power":{"v":140.7999878,"u":"W","d":1},"YieldDay":{"v":71,"u":"Wh","d":0},"YieldTotal":{"v":199.0449982,"u":"kWh","d":2}}}
{"inverters":[{"serial":"11617160xxxx","name":"Meine Solaranlage","data_age":6983,"reachable":false,"producing":false,"limit_relative":0,"limit_absolute":-1,"AC":{"0":{"Power":{"v":0,"u":"W","d":1},"Voltage":{"v":0,"u":"V","d":1},"Current":{"v":0,"u":"A","d":2},"Power DC":{"v":0,"u":"W","d":1},"YieldDay":{"v":0,"u":"Wh","d":0},"YieldTotal":{"v":0,"u":"kWh","d":3},"Frequency":{"v":0,"u":"Hz","d":2},"PowerFactor":{"v":0,"u":"","d":3},"ReactivePower":{"v":0,"u":"var","d":1},"Efficiency":{"v":0,"u":"%","d":3}}},"DC":{"0":{"name":{"u":""},"Power":{"v":0,"u":"W","d":1},"Voltage":{"v":0,"u":"V","d":1},"Current":{"v":0,"u":"A","d":2},"YieldDay":{"v":0,"u":"Wh","d":0},"YieldTotal":{"v":0,"u":"kWh","d":3},"Irradiation":{"v":0,"u":"%","d":3}},"1":{"name":{"u":""},"Power":{"v":0,"u":"W","d":1},"Voltage":{"v":0,"u":"V","d":1},"Current":{"v":0,"u":"A","d":2},"YieldDay":{"v":0,"u":"Wh","d":0},"YieldTotal":{"v":0,"u":"kWh","d":3},"Irradiation":{"v":0,"u":"%","d":3}},"2":{"name":{"u":""},"Power":{"v":0,"u":"W","d":1},"Voltage":{"v":0,"u":"V","d":1},"Current":{"v":0,"u":"A","d":2},"YieldDay":{"v":0,"u":"Wh","d":0},"YieldTotal":{"v":0,"u":"kWh","d":3},"Irradiation":{"v":0,"u":"%","d":3}},"3":{"name":{"u":""},"Power":{"v":0,"u":"W","d":1},"Voltage":{"v":0,"u":"V","d":1},"Current":{"v":0,"u":"A","d":2},"YieldDay":{"v":0,"u":"Wh","d":0},"YieldTotal":{"v":0,"u":"kWh","d":3}}},"INV":{"0":{"Temperature":{"v":0,"u":"°C","d":1}}},"events":0},{"serial":"11417160xxxx","name":"test","data_age":6983,"reachable":false,"producing":false,"limit_relative":0,"limit_absolute":-1,"AC":{"0":{"Power":{"v":0,"u":"W","d":1},"Voltage":{"v":0,"u":"V","d":1},"Current":{"v":0,"u":"A","d":2},"Power DC":{"v":0,"u":"W","d":1},"YieldDay":{"v":0,"u":"Wh","d":0},"YieldTotal":{"v":0,"u":"kWh","d":3},"Frequency":{"v":0,"u":"Hz","d":2},"PowerFactor":{"v":0,"u":"","d":3},"ReactivePower":{"v":0,"u":"var","d":1},"Efficiency":{"v":0,"u":"%","d":3}}},"DC":{"0":{"name":{"u":"test 1"},"Power":{"v":0,"u":"W","d":1},"Voltage":{"v":0,"u":"V","d":1},"Current":{"v":0,"u":"A","d":2},"YieldDay":{"v":0,"u":"Wh","d":0},"YieldTotal":{"v":0,"u":"kWh","d":3},"Irradiation":{"v":0,"u":"%","d":3}},"1":{"name":{"u":"test 2"},"Power":{"v":0,"u":"W","d":1},"Voltage":{"v":0,"u":"V","d":1},"Current":{"v":0,"u":"A","d":2},"YieldDay":{"v":0,"u":"Wh","d":0},"YieldTotal":{"v":0,"u":"kWh","d":3},"Irradiation":{"v":0,"u":"%","d":3}}},"INV":{"0":{"Temperature":{"v":0,"u":"°C","d":1}}},"events":0}],"total":{"Power":{"v":0,"u":"W","d":1},"YieldDay":{"v":0,"u":"Wh","d":0},"YieldTotal":{"v":0,"u":"kWh","d":2}},"hints":{"time_sync":false,"radio_problem":false,"default_password":false}}
```
@ -67,284 +67,382 @@ To enhance readability (and filter information) use the JSON command line proces
{
"inverters": [
{
"serial": "11418186xxxx",
"name": "HM600",
"data_age": 4,
"reachable": true,
"producing": true,
"limit_relative": 100,
"limit_absolute": 600,
"0": {
"Power": {
"v": 70.69999695,
"u": "W",
"d": 1
},
"Voltage": {
"v": 233,
"u": "V",
"d": 1
},
"Current": {
"v": 0.300000012,
"u": "A",
"d": 2
},
"Power DC": {
"v": 74,
"u": "W",
"d": 2
},
"YieldDay": {
"v": 23,
"u": "Wh",
"d": 2
},
"YieldTotal": {
"v": 150.5050049,
"u": "kWh",
"d": 2
},
"Frequency": {
"v": 50.02000046,
"u": "Hz",
"d": 2
},
"Temperature": {
"v": 8.300000191,
"u": "°C",
"d": 1
},
"PowerFactor": {
"v": 1,
"u": "",
"d": 3
},
"ReactivePower": {
"v": 0.100000001,
"u": "var",
"d": 1
},
"Efficiency": {
"v": 95.54053497,
"u": "%",
"d": 2
"serial": "116171603546",
"name": "Meine Solaranlage",
"data_age": 7038,
"reachable": false,
"producing": false,
"limit_relative": 0,
"limit_absolute": -1,
"AC": {
"0": {
"Power": {
"v": 0,
"u": "W",
"d": 1
},
"Voltage": {
"v": 0,
"u": "V",
"d": 1
},
"Current": {
"v": 0,
"u": "A",
"d": 2
},
"Power DC": {
"v": 0,
"u": "W",
"d": 1
},
"YieldDay": {
"v": 0,
"u": "Wh",
"d": 0
},
"YieldTotal": {
"v": 0,
"u": "kWh",
"d": 3
},
"Frequency": {
"v": 0,
"u": "Hz",
"d": 2
},
"PowerFactor": {
"v": 0,
"u": "",
"d": 3
},
"ReactivePower": {
"v": 0,
"u": "var",
"d": 1
},
"Efficiency": {
"v": 0,
"u": "%",
"d": 3
}
}
},
"1": {
"Power": {
"v": 0,
"u": "W",
"d": 1
"DC": {
"0": {
"name": {
"u": ""
},
"Power": {
"v": 0,
"u": "W",
"d": 1
},
"Voltage": {
"v": 0,
"u": "V",
"d": 1
},
"Current": {
"v": 0,
"u": "A",
"d": 2
},
"YieldDay": {
"v": 0,
"u": "Wh",
"d": 0
},
"YieldTotal": {
"v": 0,
"u": "kWh",
"d": 3
},
"Irradiation": {
"v": 0,
"u": "%",
"d": 3
}
},
"Voltage": {
"v": 1,
"u": "V",
"d": 1
"1": {
"name": {
"u": ""
},
"Power": {
"v": 0,
"u": "W",
"d": 1
},
"Voltage": {
"v": 0,
"u": "V",
"d": 1
},
"Current": {
"v": 0,
"u": "A",
"d": 2
},
"YieldDay": {
"v": 0,
"u": "Wh",
"d": 0
},
"YieldTotal": {
"v": 0,
"u": "kWh",
"d": 3
},
"Irradiation": {
"v": 0,
"u": "%",
"d": 3
}
},
"Current": {
"v": 0.02,
"u": "A",
"d": 2
"2": {
"name": {
"u": ""
},
"Power": {
"v": 0,
"u": "W",
"d": 1
},
"Voltage": {
"v": 0,
"u": "V",
"d": 1
},
"Current": {
"v": 0,
"u": "A",
"d": 2
},
"YieldDay": {
"v": 0,
"u": "Wh",
"d": 0
},
"YieldTotal": {
"v": 0,
"u": "kWh",
"d": 3
},
"Irradiation": {
"v": 0,
"u": "%",
"d": 3
}
},
"YieldDay": {
"v": 0,
"u": "Wh",
"d": 0
},
"YieldTotal": {
"v": 49.0320015,
"u": "kWh",
"d": 3
},
"Irradiation": {
"v": 0,
"u": "%",
"d": 2
"3": {
"name": {
"u": ""
},
"Power": {
"v": 0,
"u": "W",
"d": 1
},
"Voltage": {
"v": 0,
"u": "V",
"d": 1
},
"Current": {
"v": 0,
"u": "A",
"d": 2
},
"YieldDay": {
"v": 0,
"u": "Wh",
"d": 0
},
"YieldTotal": {
"v": 0,
"u": "kWh",
"d": 3
}
}
},
"2": {
"Power": {
"v": 74,
"u": "W",
"d": 1
},
"Voltage": {
"v": 42.40000153,
"u": "V",
"d": 1
},
"Current": {
"v": 1.74000001,
"u": "A",
"d": 2
},
"YieldDay": {
"v": 23,
"u": "Wh",
"d": 0
},
"YieldTotal": {
"v": 101.4729996,
"u": "kWh",
"d": 3
},
"Irradiation": {
"v": 18.04878044,
"u": "%",
"d": 2
"INV": {
"0": {
"Temperature": {
"v": 0,
"u": "°C",
"d": 1
}
}
},
"events": 3
"events": 0
},
{
"serial": "11418180xxxx",
"name": "HM800",
"data_age": 11,
"reachable": true,
"producing": true,
"limit_relative": 100,
"limit_absolute": 800,
"0": {
"Power": {
"v": 70.09999847,
"u": "W",
"d": 1
},
"Voltage": {
"v": 233.1000061,
"u": "V",
"d": 1
},
"Current": {
"v": 0.300000012,
"u": "A",
"d": 2
},
"Power DC": {
"v": 73.59999847,
"u": "W",
"d": 2
},
"YieldDay": {
"v": 48,
"u": "Wh",
"d": 2
},
"YieldTotal": {
"v": 48.5399971,
"u": "kWh",
"d": 2
},
"Frequency": {
"v": 50.02000046,
"u": "Hz",
"d": 2
},
"Temperature": {
"v": 11.39999962,
"u": "°C",
"d": 1
},
"PowerFactor": {
"v": 1,
"u": "",
"d": 3
},
"ReactivePower": {
"v": 0.100000001,
"u": "var",
"d": 1
},
"Efficiency": {
"v": 95.24456024,
"u": "%",
"d": 2
"serial": "114171603548",
"name": "test",
"data_age": 7038,
"reachable": false,
"producing": false,
"limit_relative": 0,
"limit_absolute": -1,
"AC": {
"0": {
"Power": {
"v": 0,
"u": "W",
"d": 1
},
"Voltage": {
"v": 0,
"u": "V",
"d": 1
},
"Current": {
"v": 0,
"u": "A",
"d": 2
},
"Power DC": {
"v": 0,
"u": "W",
"d": 1
},
"YieldDay": {
"v": 0,
"u": "Wh",
"d": 0
},
"YieldTotal": {
"v": 0,
"u": "kWh",
"d": 3
},
"Frequency": {
"v": 0,
"u": "Hz",
"d": 2
},
"PowerFactor": {
"v": 0,
"u": "",
"d": 3
},
"ReactivePower": {
"v": 0,
"u": "var",
"d": 1
},
"Efficiency": {
"v": 0,
"u": "%",
"d": 3
}
}
},
"1": {
"Power": {
"v": 36.5,
"u": "W",
"d": 1
"DC": {
"0": {
"name": {
"u": "test 1"
},
"Power": {
"v": 0,
"u": "W",
"d": 1
},
"Voltage": {
"v": 0,
"u": "V",
"d": 1
},
"Current": {
"v": 0,
"u": "A",
"d": 2
},
"YieldDay": {
"v": 0,
"u": "Wh",
"d": 0
},
"YieldTotal": {
"v": 0,
"u": "kWh",
"d": 3
},
"Irradiation": {
"v": 0,
"u": "%",
"d": 3
}
},
"Voltage": {
"v": 39.09999847,
"u": "V",
"d": 1
},
"Current": {
"v": 0.930000007,
"u": "A",
"d": 2
},
"YieldDay": {
"v": 31,
"u": "Wh",
"d": 0
},
"YieldTotal": {
"v": 4.301000118,
"u": "kWh",
"d": 3
},
"Irradiation": {
"v": 8.902439117,
"u": "%",
"d": 2
"1": {
"name": {
"u": "test 2"
},
"Power": {
"v": 0,
"u": "W",
"d": 1
},
"Voltage": {
"v": 0,
"u": "V",
"d": 1
},
"Current": {
"v": 0,
"u": "A",
"d": 2
},
"YieldDay": {
"v": 0,
"u": "Wh",
"d": 0
},
"YieldTotal": {
"v": 0,
"u": "kWh",
"d": 3
},
"Irradiation": {
"v": 0,
"u": "%",
"d": 3
}
}
},
"2": {
"Power": {
"v": 37.09999847,
"u": "W",
"d": 1
},
"Voltage": {
"v": 40.79999924,
"u": "V",
"d": 1
},
"Current": {
"v": 0.910000026,
"u": "A",
"d": 2
},
"YieldDay": {
"v": 17,
"u": "Wh",
"d": 0
},
"YieldTotal": {
"v": 44.23899841,
"u": "kWh",
"d": 3
},
"Irradiation": {
"v": 9.048780441,
"u": "%",
"d": 2
"INV": {
"0": {
"Temperature": {
"v": 0,
"u": "°C",
"d": 1
}
}
},
"events": 1
"events": 0
}
],
"total": {
"Power": {
"v": 140.7999878,
"v": 0,
"u": "W",
"d": 1
},
"YieldDay": {
"v": 71,
"v": 0,
"u": "Wh",
"d": 0
},
"YieldTotal": {
"v": 199.0449982,
"v": 0,
"u": "kWh",
"d": 2
}
},
"hints": {
"time_sync": false,
"radio_problem": false,
"default_password": false
}
}
```

View File

@ -112,33 +112,33 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
// Loop all channels
for (auto& t : inv->Statistics()->getChannelTypes()) {
JsonObject chanTypeObj = invObject.createNestedObject(inv->Statistics()->getChannelTypeName(t));
for (auto& c : inv->Statistics()->getChannelsByType(t)) {
if (t == TYPE_DC) {
INVERTER_CONFIG_T* inv_cfg = Configuration.getInverterConfig(inv->serial());
if (inv_cfg != nullptr) {
// TODO(tbnobody)
invObject[String(static_cast<uint8_t>(c) + 1)][F("name")]["u"] = inv_cfg->channel[c].Name;
chanTypeObj[String(static_cast<uint8_t>(c))][F("name")]["u"] = inv_cfg->channel[c].Name;
}
}
addField(invObject, i, inv, t, c, FLD_PAC);
addField(invObject, i, inv, t, c, FLD_UAC);
addField(invObject, i, inv, t, c, FLD_IAC);
addField(chanTypeObj, i, inv, t, c, FLD_PAC);
addField(chanTypeObj, i, inv, t, c, FLD_UAC);
addField(chanTypeObj, i, inv, t, c, FLD_IAC);
if (t == TYPE_AC) {
addField(invObject, i, inv, t, c, FLD_PDC, F("Power DC"));
addField(chanTypeObj, i, inv, t, c, FLD_PDC, F("Power DC"));
} else {
addField(invObject, i, inv, t, c, FLD_PDC);
addField(chanTypeObj, i, inv, t, c, FLD_PDC);
}
addField(invObject, i, inv, t, c, FLD_UDC);
addField(invObject, i, inv, t, c, FLD_IDC);
addField(invObject, i, inv, t, c, FLD_YD);
addField(invObject, i, inv, t, c, FLD_YT);
addField(invObject, i, inv, t, c, FLD_F);
addField(invObject, i, inv, t, c, FLD_T);
addField(invObject, i, inv, t, c, FLD_PF);
addField(invObject, i, inv, t, c, FLD_PRA);
addField(invObject, i, inv, t, c, FLD_EFF);
addField(chanTypeObj, i, inv, t, c, FLD_UDC);
addField(chanTypeObj, i, inv, t, c, FLD_IDC);
addField(chanTypeObj, i, inv, t, c, FLD_YD);
addField(chanTypeObj, i, inv, t, c, FLD_YT);
addField(chanTypeObj, i, inv, t, c, FLD_F);
addField(chanTypeObj, i, inv, t, c, FLD_T);
addField(chanTypeObj, i, inv, t, c, FLD_PF);
addField(chanTypeObj, i, inv, t, c, FLD_PRA);
addField(chanTypeObj, i, inv, t, c, FLD_EFF);
if (t == TYPE_DC && inv->Statistics()->getStringMaxPower(c) > 0) {
addField(invObject, i, inv, t, c, FLD_IRR);
addField(chanTypeObj, i, inv, t, c, FLD_IRR);
}
}
}
@ -187,12 +187,7 @@ void WebApiWsLiveClass::addField(JsonObject& root, uint8_t idx, std::shared_ptr<
chanName = topic;
}
String chanNum;
if (type == TYPE_DC) {
// TODO(tbnobody)
chanNum = static_cast<uint8_t>(channel) + 1;
} else {
chanNum = channel;
}
chanNum = channel;
root[chanNum][chanName]["v"] = inv->Statistics()->getChannelFieldValue(type, channel, fieldId);
root[chanNum][chanName]["u"] = inv->Statistics()->getChannelFieldUnit(type, channel, fieldId);
root[chanNum][chanName]["d"] = inv->Statistics()->getChannelFieldDigits(type, channel, fieldId);

View File

@ -1,11 +1,21 @@
<template>
<div class="card" :class="{ 'border-info': channelNumber == 0 }">
<div v-if="channelNumber >= 1" class="card-header">
<template v-if="channelData.name.u != ''">{{ channelData.name.u }}</template>
<template v-else>{{ $t('inverterchannelinfo.String', { num: channelNumber }) }}</template>
<div class="card" :class="{
'border-info': channelType == 'AC',
'border-secondary': channelType == 'INV'
}">
<div v-if="channelType == 'INV'" class="card-header text-bg-secondary">
{{ $t('inverterchannelinfo.General') }}
</div>
<div v-if="channelNumber == 0" class="card-header text-bg-info">
{{ $t('inverterchannelinfo.Phase', { num: channelNumber + 1 }) }}</div>
<div v-if="channelType == 'DC'" class="card-header">
<template v-if="channelData.name.u != ''">{{ channelData.name.u }}</template>
<template v-else>{{ $t('inverterchannelinfo.String', { num: channelNumber + 1 }) }}</template>
</div>
<div v-if="channelType == 'AC'" class="card-header text-bg-info">
{{ $t('inverterchannelinfo.Phase', { num: channelNumber + 1 }) }}
</div>
<div class="card-body">
<table class="table table-striped table-hover">
<thead>
@ -21,10 +31,10 @@
<th scope="row">{{ $t('inverterchannelproperty.' + key) }}</th>
<td style="text-align: right">
{{ $n(property.v, 'decimal', {
minimumFractionDigits: property.d,
maximumFractionDigits: property.d})
minimumFractionDigits: property.d,
maximumFractionDigits: property.d})
}}
</td>
</td>
<td>{{ property.u }}</td>
</template>
</tr>
@ -41,6 +51,7 @@ import { defineComponent, type PropType } from 'vue';
export default defineComponent({
props: {
channelData: { type: Object as PropType<InverterStatistics>, required: true },
channelType: { type: String, required: true },
channelNumber: { type: Number, required: true },
},
});

View File

@ -263,6 +263,7 @@
"inverterchannelinfo": {
"String": "String {num}",
"Phase": "Phase {num}",
"General": "Allgemein",
"Property": "Eigenschaft",
"Value": "Wert",
"Unit": "Einheit"

View File

@ -263,6 +263,7 @@
"inverterchannelinfo": {
"String": "String {num}",
"Phase": "Phase {num}",
"General": "General",
"Property": "Property",
"Value": "Value",
"Unit": "Unit"

View File

@ -263,6 +263,7 @@
"inverterchannelinfo": {
"String": "Ligne {num}",
"Phase": "Phase {num}",
"General": "General",
"Property": "Propriété",
"Value": "Valeur",
"Unit": "Unité"

View File

@ -29,7 +29,9 @@ export interface Inverter {
limit_relative: number;
limit_absolute: number;
events: number;
[key: number]: InverterStatistics;
AC: InverterStatistics[];
DC: InverterStatistics[];
INV: InverterStatistics[];
}
export interface Total {

View File

@ -92,11 +92,12 @@
</div>
</div>
<div class="card-body">
<div class="row flex-row-reverse flex-wrap-reverse align-items-end g-3">
<template v-for="channel in 5" :key="channel">
<div v-if="inverter[channel - 1]" :class="`col order-${5 - channel}`">
<InverterChannelInfo :channelData="inverter[channel - 1]"
:channelNumber="channel - 1" />
<div class="row flex-row-reverse flex-wrap-reverse g-3">
<template v-for="chanType in [{obj: inverter.INV, name: 'INV'}, {obj: inverter.AC, name: 'AC'}, {obj: inverter.DC, name: 'DC'}].reverse()">
<div v-for="channel in Object.keys(chanType.obj).sort().reverse().map(x=>+x)" :key="channel" class="col">
<InverterChannelInfo :channelData="chanType.obj[channel]"
:channelType="chanType.name"
:channelNumber="channel" />
</div>
</template>
</div>