Added small dialog to show inverter info (firmware version etc.)
This commit is contained in:
parent
991838ae2c
commit
217fddf405
@ -1,6 +1,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "WebApi_devinfo.h"
|
||||||
#include "WebApi_dtu.h"
|
#include "WebApi_dtu.h"
|
||||||
#include "WebApi_eventlog.h"
|
#include "WebApi_eventlog.h"
|
||||||
#include "WebApi_firmware.h"
|
#include "WebApi_firmware.h"
|
||||||
@ -23,6 +24,7 @@ private:
|
|||||||
AsyncWebServer _server;
|
AsyncWebServer _server;
|
||||||
AsyncEventSource _events;
|
AsyncEventSource _events;
|
||||||
|
|
||||||
|
WebApiDevInfoClass _webApiDevInfo;
|
||||||
WebApiDtuClass _webApiDtu;
|
WebApiDtuClass _webApiDtu;
|
||||||
WebApiEventlogClass _webApiEventlog;
|
WebApiEventlogClass _webApiEventlog;
|
||||||
WebApiFirmwareClass _webApiFirmware;
|
WebApiFirmwareClass _webApiFirmware;
|
||||||
|
|||||||
15
include/WebApi_devinfo.h
Normal file
15
include/WebApi_devinfo.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
|
||||||
|
class WebApiDevInfoClass {
|
||||||
|
public:
|
||||||
|
void init(AsyncWebServer* server);
|
||||||
|
void loop();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void onDevInfoStatus(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
|
AsyncWebServer* _server;
|
||||||
|
};
|
||||||
@ -19,6 +19,7 @@ void WebApiClass::init()
|
|||||||
|
|
||||||
_server.addHandler(&_events);
|
_server.addHandler(&_events);
|
||||||
|
|
||||||
|
_webApiDevInfo.init(&_server);
|
||||||
_webApiDtu.init(&_server);
|
_webApiDtu.init(&_server);
|
||||||
_webApiEventlog.init(&_server);
|
_webApiEventlog.init(&_server);
|
||||||
_webApiFirmware.init(&_server);
|
_webApiFirmware.init(&_server);
|
||||||
@ -35,6 +36,7 @@ void WebApiClass::init()
|
|||||||
|
|
||||||
void WebApiClass::loop()
|
void WebApiClass::loop()
|
||||||
{
|
{
|
||||||
|
_webApiDevInfo.loop();
|
||||||
_webApiDtu.loop();
|
_webApiDtu.loop();
|
||||||
_webApiEventlog.loop();
|
_webApiEventlog.loop();
|
||||||
_webApiFirmware.loop();
|
_webApiFirmware.loop();
|
||||||
|
|||||||
52
src/WebApi_devinfo.cpp
Normal file
52
src/WebApi_devinfo.cpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Thomas Basler and others
|
||||||
|
*/
|
||||||
|
#include "WebApi_devinfo.h"
|
||||||
|
#include "ArduinoJson.h"
|
||||||
|
#include "AsyncJson.h"
|
||||||
|
#include "Hoymiles.h"
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
void WebApiDevInfoClass::init(AsyncWebServer* server)
|
||||||
|
{
|
||||||
|
using namespace std::placeholders;
|
||||||
|
|
||||||
|
_server = server;
|
||||||
|
|
||||||
|
_server->on("/api/devinfo/status", HTTP_GET, std::bind(&WebApiDevInfoClass::onDevInfoStatus, this, _1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebApiDevInfoClass::loop()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebApiDevInfoClass::onDevInfoStatus(AsyncWebServerRequest* request)
|
||||||
|
{
|
||||||
|
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||||
|
JsonObject root = response->getRoot();
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
|
||||||
|
auto inv = Hoymiles.getInverterByPos(i);
|
||||||
|
|
||||||
|
// Inverter Serial is read as HEX
|
||||||
|
char buffer[sizeof(uint64_t) * 8 + 1];
|
||||||
|
sprintf(buffer, "%0lx%08lx",
|
||||||
|
((uint32_t)((inv->serial() >> 32) & 0xFFFFFFFF)),
|
||||||
|
((uint32_t)(inv->serial() & 0xFFFFFFFF)));
|
||||||
|
|
||||||
|
JsonObject devInfoObj = root[buffer].createNestedObject();
|
||||||
|
devInfoObj[F("fw_bootloader_version")] = inv->DevInfo()->getFwBootloaderVersion();
|
||||||
|
devInfoObj[F("fw_build_version")] = inv->DevInfo()->getFwBuildVersion();
|
||||||
|
devInfoObj[F("hw_part_number")] = inv->DevInfo()->getHwPartNumber();
|
||||||
|
devInfoObj[F("hw_version")] = inv->DevInfo()->getHwVersion();
|
||||||
|
|
||||||
|
char timebuffer[32];
|
||||||
|
const time_t t = inv->DevInfo()->getFwBuildDateTime();
|
||||||
|
std::strftime(timebuffer, sizeof(timebuffer), "%Y-%m-%d %H:%M:%S", gmtime(&t));
|
||||||
|
devInfoObj[F("fw_build_datetime")] = String(timebuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
response->setLength();
|
||||||
|
request->send(response);
|
||||||
|
}
|
||||||
@ -38,24 +38,34 @@
|
|||||||
{{ inverter.serial }}) (Data Age:
|
{{ inverter.serial }}) (Data Age:
|
||||||
{{ inverter.data_age }} seconds)
|
{{ inverter.data_age }} seconds)
|
||||||
|
|
||||||
<button v-if="inverter.events >= 0" type="button"
|
<div class="btn-toolbar" role="toolbar">
|
||||||
class="btn btn-sm btn-secondary position-relative"
|
<div class="btn-group me-2" role="group">
|
||||||
@click="onShowEventlog(inverter.serial)"
|
<button type="button" class="btn btn-sm btn-info"
|
||||||
title="Show Eventlog">
|
@click="onShowDevInfo(inverter.serial)" title="Show Inverter Info">
|
||||||
<BIconJournalText style="font-size:24px;" />
|
<BIconCpu style="font-size:24px;" />
|
||||||
<span
|
|
||||||
class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
|
</button>
|
||||||
{{ inverter.events }}
|
</div>
|
||||||
<span class="visually-hidden">unread messages</span>
|
|
||||||
</span>
|
<div class="btn-group" role="group">
|
||||||
</button>
|
<button v-if="inverter.events >= 0" type="button"
|
||||||
|
class="btn btn-sm btn-secondary position-relative"
|
||||||
|
@click="onShowEventlog(inverter.serial)" title="Show Eventlog">
|
||||||
|
<BIconJournalText style="font-size:24px;" />
|
||||||
|
<span
|
||||||
|
class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
|
||||||
|
{{ inverter.events }}
|
||||||
|
<span class="visually-hidden">unread messages</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="row flex-row-reverse flex-wrap-reverse align-items-end g-3">
|
<div class="row flex-row-reverse flex-wrap-reverse align-items-end g-3">
|
||||||
<template v-for="channel in 5" :key="channel">
|
<template v-for="channel in 5" :key="channel">
|
||||||
<div v-if="inverter[channel - 1]" :class="`col order-${5 - channel}`">
|
<div v-if="inverter[channel - 1]" :class="`col order-${5 - channel}`">
|
||||||
<InverterChannelInfo
|
<InverterChannelInfo :channelData="inverter[channel - 1]"
|
||||||
:channelData="inverter[channel - 1]"
|
|
||||||
:channelNumber="channel - 1" />
|
:channelNumber="channel - 1" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -93,6 +103,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="modal" id="devInfoView" tabindex="-1">
|
||||||
|
<div class="modal-dialog modal-lg">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Inverter Info</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="text-center" v-if="devInfoLoading">
|
||||||
|
<div class="spinner-border" role="status">
|
||||||
|
<span class="visually-hidden">Loading...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DevInfo v-if="!devInfoLoading" :devInfoList="devInfoList" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" @click="onHideDevInfo"
|
||||||
|
data-bs-dismiss="modal">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -101,6 +136,7 @@ import { defineComponent } from 'vue';
|
|||||||
import InverterChannelInfo from "@/components/partials/InverterChannelInfo.vue";
|
import InverterChannelInfo from "@/components/partials/InverterChannelInfo.vue";
|
||||||
import * as bootstrap from 'bootstrap';
|
import * as bootstrap from 'bootstrap';
|
||||||
import EventLog from '@/components/partials/EventLog.vue';
|
import EventLog from '@/components/partials/EventLog.vue';
|
||||||
|
import DevInfo from '@/components/partials/DevInfo.vue';
|
||||||
|
|
||||||
declare interface Inverter {
|
declare interface Inverter {
|
||||||
serial: number,
|
serial: number,
|
||||||
@ -113,7 +149,8 @@ declare interface Inverter {
|
|||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
InverterChannelInfo,
|
InverterChannelInfo,
|
||||||
EventLog
|
EventLog,
|
||||||
|
DevInfo
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@ -125,7 +162,10 @@ export default defineComponent({
|
|||||||
isFirstFetchAfterConnect: true,
|
isFirstFetchAfterConnect: true,
|
||||||
eventLogView: {} as bootstrap.Modal,
|
eventLogView: {} as bootstrap.Modal,
|
||||||
eventLogList: {},
|
eventLogList: {},
|
||||||
eventLogLoading: true
|
eventLogLoading: true,
|
||||||
|
devInfoView: {} as bootstrap.Modal,
|
||||||
|
devInfoList: {},
|
||||||
|
devInfoLoading: true
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@ -135,6 +175,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.eventLogView = new bootstrap.Modal('#eventView');
|
this.eventLogView = new bootstrap.Modal('#eventView');
|
||||||
|
this.devInfoView = new bootstrap.Modal('#devInfoView');
|
||||||
},
|
},
|
||||||
unmounted() {
|
unmounted() {
|
||||||
this.closeSocket();
|
this.closeSocket();
|
||||||
@ -228,6 +269,20 @@ export default defineComponent({
|
|||||||
|
|
||||||
this.eventLogView.show();
|
this.eventLogView.show();
|
||||||
},
|
},
|
||||||
|
onHideDevInfo() {
|
||||||
|
this.devInfoView.hide();
|
||||||
|
},
|
||||||
|
onShowDevInfo(serial: number) {
|
||||||
|
this.devInfoLoading = true;
|
||||||
|
fetch("/api/devinfo/status")
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((data) => {
|
||||||
|
this.devInfoList = data[serial][0];
|
||||||
|
this.devInfoLoading = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.devInfoView.show();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
54
webapp/src/components/partials/DevInfo.vue
Normal file
54
webapp/src/components/partials/DevInfo.vue
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<template>
|
||||||
|
<table class="table table-hover">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Bootloader Version</td>
|
||||||
|
<td>{{ formatVersion(devInfoList.fw_bootloader_version) }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Firmware Version</td>
|
||||||
|
<td>{{ formatVersion(devInfoList.fw_build_version) }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Firmware Build Date</td>
|
||||||
|
<td>{{ devInfoList.fw_build_datetime }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Hardware Part Number</td>
|
||||||
|
<td>{{ devInfoList.hw_part_number }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Hardware Version</td>
|
||||||
|
<td>{{ devInfoList.hw_version }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
|
declare interface DevInfoData {
|
||||||
|
fw_bootloader_version: number,
|
||||||
|
fw_build_version: number,
|
||||||
|
fw_build_datetime: Date,
|
||||||
|
hw_part_number: number,
|
||||||
|
hw_version: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
devInfoList: { type: Object as () => DevInfoData, required: true },
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
formatVersion() {
|
||||||
|
return (value: number) => {
|
||||||
|
const version_major = Math.floor(value / 10000);
|
||||||
|
const version_minor = Math.floor((value - version_major * 10000) / 100);
|
||||||
|
const version_patch = Math.floor((value - version_major * 10000 - version_minor * 100));
|
||||||
|
return version_major + "." + version_minor + "." + version_patch;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
Binary file not shown.
Loading…
Reference in New Issue
Block a user