BREAKING CHANGE: Structure WebAPI /api/livedata/status changed

The different inverter data are now in a subobject called "inverters".
This is required to extend the API by other data then the inverter values
This commit is contained in:
Thomas Basler 2022-11-01 15:04:05 +01:00
parent f854cdff03
commit 69b675bc64
4 changed files with 45 additions and 37 deletions

View File

@ -13,7 +13,7 @@ public:
private: private:
void generateJsonResponse(JsonVariant& root); void generateJsonResponse(JsonVariant& root);
void addField(JsonVariant& root, uint8_t idx, std::shared_ptr<InverterAbstract> inv, uint8_t channel, uint8_t fieldId, String topic = ""); void addField(JsonObject& root, uint8_t idx, std::shared_ptr<InverterAbstract> inv, uint8_t channel, uint8_t fieldId, String topic = "");
void onLivedataStatus(AsyncWebServerRequest* request); void onLivedataStatus(AsyncWebServerRequest* request);
void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len); void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len);

View File

@ -73,48 +73,51 @@ void WebApiWsLiveClass::loop()
void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root) void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
{ {
JsonArray invArray = root.createNestedArray("inverters");
// Loop all inverters // Loop all inverters
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) { for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
auto inv = Hoymiles.getInverterByPos(i); auto inv = Hoymiles.getInverterByPos(i);
root[i][F("serial")] = inv->serialString(); JsonObject invObject = invArray.createNestedObject();
root[i][F("name")] = inv->name();
root[i][F("data_age")] = (millis() - inv->Statistics()->getLastUpdate()) / 1000; invObject[F("serial")] = inv->serialString();
root[i][F("reachable")] = inv->isReachable(); invObject[F("name")] = inv->name();
root[i][F("producing")] = inv->isProducing(); invObject[F("data_age")] = (millis() - inv->Statistics()->getLastUpdate()) / 1000;
root[i][F("limit_relative")] = inv->SystemConfigPara()->getLimitPercent(); invObject[F("reachable")] = inv->isReachable();
invObject[F("producing")] = inv->isProducing();
invObject[F("limit_relative")] = inv->SystemConfigPara()->getLimitPercent();
if (inv->DevInfo()->getMaxPower() > 0) { if (inv->DevInfo()->getMaxPower() > 0) {
root[i][F("limit_absolute")] = inv->SystemConfigPara()->getLimitPercent() * inv->DevInfo()->getMaxPower() / 100.0; invObject[F("limit_absolute")] = inv->SystemConfigPara()->getLimitPercent() * inv->DevInfo()->getMaxPower() / 100.0;
} else { } else {
root[i][F("limit_absolute")] = -1; invObject[F("limit_absolute")] = -1;
} }
// Loop all channels // Loop all channels
for (uint8_t c = 0; c <= inv->Statistics()->getChannelCount(); c++) { for (uint8_t c = 0; c <= inv->Statistics()->getChannelCount(); c++) {
addField(root, i, inv, c, FLD_PAC); addField(invObject, i, inv, c, FLD_PAC);
addField(root, i, inv, c, FLD_UAC); addField(invObject, i, inv, c, FLD_UAC);
addField(root, i, inv, c, FLD_IAC); addField(invObject, i, inv, c, FLD_IAC);
if (c == 0) { if (c == 0) {
addField(root, i, inv, c, FLD_PDC, F("Power DC")); addField(invObject, i, inv, c, FLD_PDC, F("Power DC"));
} else { } else {
addField(root, i, inv, c, FLD_PDC); addField(invObject, i, inv, c, FLD_PDC);
} }
addField(root, i, inv, c, FLD_UDC); addField(invObject, i, inv, c, FLD_UDC);
addField(root, i, inv, c, FLD_IDC); addField(invObject, i, inv, c, FLD_IDC);
addField(root, i, inv, c, FLD_YD); addField(invObject, i, inv, c, FLD_YD);
addField(root, i, inv, c, FLD_YT); addField(invObject, i, inv, c, FLD_YT);
addField(root, i, inv, c, FLD_F); addField(invObject, i, inv, c, FLD_F);
addField(root, i, inv, c, FLD_T); addField(invObject, i, inv, c, FLD_T);
addField(root, i, inv, c, FLD_PF); addField(invObject, i, inv, c, FLD_PF);
addField(root, i, inv, c, FLD_PRA); addField(invObject, i, inv, c, FLD_PRA);
addField(root, i, inv, c, FLD_EFF); addField(invObject, i, inv, c, FLD_EFF);
addField(root, i, inv, c, FLD_IRR); addField(invObject, i, inv, c, FLD_IRR);
} }
if (inv->Statistics()->hasChannelFieldValue(CH0, FLD_EVT_LOG)) { if (inv->Statistics()->hasChannelFieldValue(CH0, FLD_EVT_LOG)) {
root[i][F("events")] = inv->EventLog()->getEntryCount(); invObject[F("events")] = inv->EventLog()->getEntryCount();
} else { } else {
root[i][F("events")] = -1; invObject[F("events")] = -1;
} }
if (inv->Statistics()->getLastUpdate() > _newestInverterTimestamp) { if (inv->Statistics()->getLastUpdate() > _newestInverterTimestamp) {
@ -123,7 +126,7 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
} }
} }
void WebApiWsLiveClass::addField(JsonVariant& root, uint8_t idx, std::shared_ptr<InverterAbstract> inv, uint8_t channel, uint8_t fieldId, String topic) void WebApiWsLiveClass::addField(JsonObject& root, uint8_t idx, std::shared_ptr<InverterAbstract> inv, uint8_t channel, uint8_t fieldId, String topic)
{ {
if (inv->Statistics()->hasChannelFieldValue(channel, fieldId)) { if (inv->Statistics()->hasChannelFieldValue(channel, fieldId)) {
String chanName; String chanName;
@ -132,9 +135,9 @@ void WebApiWsLiveClass::addField(JsonVariant& root, uint8_t idx, std::shared_ptr
} else { } else {
chanName = topic; chanName = topic;
} }
root[idx][String(channel)][chanName]["v"] = inv->Statistics()->getChannelFieldValue(channel, fieldId); root[String(channel)][chanName]["v"] = inv->Statistics()->getChannelFieldValue(channel, fieldId);
root[idx][String(channel)][chanName]["u"] = inv->Statistics()->getChannelFieldUnit(channel, fieldId); root[String(channel)][chanName]["u"] = inv->Statistics()->getChannelFieldUnit(channel, fieldId);
root[idx][String(channel)][chanName]["d"] = inv->Statistics()->getChannelFieldDigits(channel, fieldId); root[String(channel)][chanName]["d"] = inv->Statistics()->getChannelFieldDigits(channel, fieldId);
} }
} }
@ -153,8 +156,8 @@ void WebApiWsLiveClass::onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketC
void WebApiWsLiveClass::onLivedataStatus(AsyncWebServerRequest* request) void WebApiWsLiveClass::onLivedataStatus(AsyncWebServerRequest* request)
{ {
AsyncJsonResponse* response = new AsyncJsonResponse(true, 40960U); AsyncJsonResponse* response = new AsyncJsonResponse(false, 40960U);
JsonVariant root = response->getRoot().as<JsonVariant>(); JsonVariant root = response->getRoot();
generateJsonResponse(root); generateJsonResponse(root);

View File

@ -31,4 +31,6 @@ export interface Inverter {
[key: number]: InverterStatistics, [key: number]: InverterStatistics,
}; };
export interface Inverters extends Array<Inverter>{}; export interface LiveData {
inverters: Inverter[],
}

View File

@ -332,7 +332,7 @@ import BootstrapAlert from '@/components/BootstrapAlert.vue';
import InverterChannelInfo from "@/components/InverterChannelInfo.vue"; import InverterChannelInfo from "@/components/InverterChannelInfo.vue";
import type { DevInfoStatus } from '@/types/DevInfoStatus'; import type { DevInfoStatus } from '@/types/DevInfoStatus';
import type { EventlogItems } from '@/types/EventlogStatus'; import type { EventlogItems } from '@/types/EventlogStatus';
import type { Inverters } from '@/types/LiveDataStatus'; import type { LiveData, Inverter } from '@/types/LiveDataStatus';
import type { LimitStatus } from '@/types/LimitStatus'; import type { LimitStatus } from '@/types/LimitStatus';
import type { LimitConfig } from '@/types/LimitConfig'; import type { LimitConfig } from '@/types/LimitConfig';
import { formatNumber } from '@/utils'; import { formatNumber } from '@/utils';
@ -361,7 +361,7 @@ export default defineComponent({
heartInterval: 0, heartInterval: 0,
dataAgeInterval: 0, dataAgeInterval: 0,
dataLoading: true, dataLoading: true,
inverterData: [] as Inverters, liveData: {} as LiveData,
isFirstFetchAfterConnect: true, isFirstFetchAfterConnect: true,
eventLogView: {} as bootstrap.Modal, eventLogView: {} as bootstrap.Modal,
eventLogList: {} as EventlogItems, eventLogList: {} as EventlogItems,
@ -435,6 +435,9 @@ export default defineComponent({
}, },
currentLimitRelative(): string { currentLimitRelative(): string {
return formatNumber(this.currentLimitList.limit_relative, 2); return formatNumber(this.currentLimitList.limit_relative, 2);
},
inverterData(): Inverter[] {
return this.liveData.inverters;
} }
}, },
methods: { methods: {
@ -444,7 +447,7 @@ export default defineComponent({
fetch("/api/livedata/status") fetch("/api/livedata/status")
.then((response) => response.json()) .then((response) => response.json())
.then((data) => { .then((data) => {
this.inverterData = data; this.liveData = data;
this.dataLoading = false; this.dataLoading = false;
}); });
}, },
@ -459,7 +462,7 @@ export default defineComponent({
this.socket.onmessage = (event) => { this.socket.onmessage = (event) => {
console.log(event); console.log(event);
this.inverterData = JSON.parse(event.data); this.liveData = JSON.parse(event.data);
this.dataLoading = false; this.dataLoading = false;
this.heartCheck(); // Reset heartbeat detection this.heartCheck(); // Reset heartbeat detection
}; };