diff --git a/include/WebApi.h b/include/WebApi.h index e2b85e7..9c11a5e 100644 --- a/include/WebApi.h +++ b/include/WebApi.h @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include "WebApi_devinfo.h" #include "WebApi_dtu.h" #include "WebApi_eventlog.h" #include "WebApi_firmware.h" @@ -23,6 +24,7 @@ private: AsyncWebServer _server; AsyncEventSource _events; + WebApiDevInfoClass _webApiDevInfo; WebApiDtuClass _webApiDtu; WebApiEventlogClass _webApiEventlog; WebApiFirmwareClass _webApiFirmware; diff --git a/include/WebApi_devinfo.h b/include/WebApi_devinfo.h new file mode 100644 index 0000000..0b8471e --- /dev/null +++ b/include/WebApi_devinfo.h @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +#include + +class WebApiDevInfoClass { +public: + void init(AsyncWebServer* server); + void loop(); + +private: + void onDevInfoStatus(AsyncWebServerRequest* request); + + AsyncWebServer* _server; +}; \ No newline at end of file diff --git a/src/WebApi.cpp b/src/WebApi.cpp index 1131d3c..3f90704 100644 --- a/src/WebApi.cpp +++ b/src/WebApi.cpp @@ -19,6 +19,7 @@ void WebApiClass::init() _server.addHandler(&_events); + _webApiDevInfo.init(&_server); _webApiDtu.init(&_server); _webApiEventlog.init(&_server); _webApiFirmware.init(&_server); @@ -35,6 +36,7 @@ void WebApiClass::init() void WebApiClass::loop() { + _webApiDevInfo.loop(); _webApiDtu.loop(); _webApiEventlog.loop(); _webApiFirmware.loop(); diff --git a/src/WebApi_devinfo.cpp b/src/WebApi_devinfo.cpp new file mode 100644 index 0000000..8022ecb --- /dev/null +++ b/src/WebApi_devinfo.cpp @@ -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 + +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); +} \ No newline at end of file diff --git a/webapp/src/components/HomeView.vue b/webapp/src/components/HomeView.vue index f076967..3614e09 100644 --- a/webapp/src/components/HomeView.vue +++ b/webapp/src/components/HomeView.vue @@ -38,24 +38,34 @@ {{ inverter.serial }}) (Data Age: {{ inverter.data_age }} seconds) - +
@@ -93,6 +103,31 @@
+ + @@ -101,6 +136,7 @@ import { defineComponent } from 'vue'; import InverterChannelInfo from "@/components/partials/InverterChannelInfo.vue"; import * as bootstrap from 'bootstrap'; import EventLog from '@/components/partials/EventLog.vue'; +import DevInfo from '@/components/partials/DevInfo.vue'; declare interface Inverter { serial: number, @@ -113,7 +149,8 @@ declare interface Inverter { export default defineComponent({ components: { InverterChannelInfo, - EventLog + EventLog, + DevInfo }, data() { return { @@ -125,7 +162,10 @@ export default defineComponent({ isFirstFetchAfterConnect: true, eventLogView: {} as bootstrap.Modal, eventLogList: {}, - eventLogLoading: true + eventLogLoading: true, + devInfoView: {} as bootstrap.Modal, + devInfoList: {}, + devInfoLoading: true }; }, created() { @@ -135,6 +175,7 @@ export default defineComponent({ }, mounted() { this.eventLogView = new bootstrap.Modal('#eventView'); + this.devInfoView = new bootstrap.Modal('#devInfoView'); }, unmounted() { this.closeSocket(); @@ -228,6 +269,20 @@ export default defineComponent({ 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(); + }, }, }); \ No newline at end of file diff --git a/webapp/src/components/partials/DevInfo.vue b/webapp/src/components/partials/DevInfo.vue new file mode 100644 index 0000000..e8e35a9 --- /dev/null +++ b/webapp/src/components/partials/DevInfo.vue @@ -0,0 +1,54 @@ + + + \ No newline at end of file diff --git a/webapp_dist/js/app.js.gz b/webapp_dist/js/app.js.gz index ce8fcc6..7509f00 100644 Binary files a/webapp_dist/js/app.js.gz and b/webapp_dist/js/app.js.gz differ