Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development

This commit is contained in:
helgeerbe 2024-01-25 23:36:32 +01:00
commit ebacc2f25f
109 changed files with 1171 additions and 1274 deletions

View File

@ -0,0 +1,47 @@
[
{
"name": "Olimex ESP32-Gateway",
"nrf24": {
"miso": 14,
"mosi": 13,
"clk": 12,
"irq": 15,
"en": 2,
"cs": 4
},
"eth": {
"enabled": true,
"phy_addr": 0,
"power": 12,
"mdc": 23,
"mdio": 18,
"type": 0,
"clk_mode": 3
}
},
{
"name": "Olimex ESP32-Gateway with SSH1106",
"nrf24": {
"miso": 14,
"mosi": 13,
"clk": 12,
"irq": 15,
"en": 2,
"cs": 4
},
"eth": {
"enabled": true,
"phy_addr": 0,
"power": 12,
"mdc": 23,
"mdio": 18,
"type": 0,
"clk_mode": 3
},
"display": {
"type": 3,
"data": 32,
"clk": 16
}
}
]

View File

@ -6,6 +6,7 @@
class DatastoreClass { class DatastoreClass {
public: public:
DatastoreClass();
void init(Scheduler& scheduler); void init(Scheduler& scheduler);
// Sum of yield total of all enabled inverters, a inverter which is just disabled at night is also included // Sum of yield total of all enabled inverters, a inverter which is just disabled at night is also included
@ -81,4 +82,4 @@ private:
bool _isAtLeastOnePollEnabled = false; bool _isAtLeastOnePollEnabled = false;
}; };
extern DatastoreClass Datastore; extern DatastoreClass Datastore;

View File

@ -8,6 +8,7 @@
class InverterSettingsClass { class InverterSettingsClass {
public: public:
InverterSettingsClass();
void init(Scheduler& scheduler); void init(Scheduler& scheduler);
private: private:

View File

@ -12,6 +12,7 @@
class MessageOutputClass : public Print { class MessageOutputClass : public Print {
public: public:
MessageOutputClass();
void init(Scheduler& scheduler); void init(Scheduler& scheduler);
size_t write(uint8_t c) override; size_t write(uint8_t c) override;
size_t write(const uint8_t* buffer, size_t size) override; size_t write(const uint8_t* buffer, size_t size) override;
@ -37,4 +38,4 @@ private:
void serialWrite(message_t const& m); void serialWrite(message_t const& m);
}; };
extern MessageOutputClass MessageOutput; extern MessageOutputClass MessageOutput;

View File

@ -6,6 +6,7 @@
class MqttHandleDtuClass { class MqttHandleDtuClass {
public: public:
MqttHandleDtuClass();
void init(Scheduler& scheduler); void init(Scheduler& scheduler);
private: private:
@ -14,4 +15,4 @@ private:
Task _loopTask; Task _loopTask;
}; };
extern MqttHandleDtuClass MqttHandleDtu; extern MqttHandleDtuClass MqttHandleDtu;

View File

@ -51,6 +51,7 @@ const byteAssign_fieldDeviceClass_t deviceFieldAssignment[] = {
class MqttHandleHassClass { class MqttHandleHassClass {
public: public:
MqttHandleHassClass();
void init(Scheduler& scheduler); void init(Scheduler& scheduler);
void publishConfig(); void publishConfig();
void forceUpdate(); void forceUpdate();
@ -79,4 +80,4 @@ private:
bool _updateForced = false; bool _updateForced = false;
}; };
extern MqttHandleHassClass MqttHandleHass; extern MqttHandleHassClass MqttHandleHass;

View File

@ -8,6 +8,7 @@
class MqttHandleInverterClass { class MqttHandleInverterClass {
public: public:
MqttHandleInverterClass();
void init(Scheduler& scheduler); void init(Scheduler& scheduler);
static String getTopic(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId); static String getTopic(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId);
@ -39,4 +40,4 @@ private:
}; };
}; };
extern MqttHandleInverterClass MqttHandleInverter; extern MqttHandleInverterClass MqttHandleInverter;

View File

@ -5,6 +5,7 @@
class MqttHandleInverterTotalClass { class MqttHandleInverterTotalClass {
public: public:
MqttHandleInverterTotalClass();
void init(Scheduler& scheduler); void init(Scheduler& scheduler);
private: private:
@ -13,4 +14,4 @@ private:
Task _loopTask; Task _loopTask;
}; };
extern MqttHandleInverterTotalClass MqttHandleInverterTotal; extern MqttHandleInverterTotalClass MqttHandleInverterTotal;

View File

@ -46,10 +46,6 @@ public:
static void writeConfig(JsonVariant& retMsg, const WebApiError code = WebApiError::GenericSuccess, const String& message = "Settings saved!"); static void writeConfig(JsonVariant& retMsg, const WebApiError code = WebApiError::GenericSuccess, const String& message = "Settings saved!");
private: private:
void loop();
Task _loopTask;
AsyncWebServer _server; AsyncWebServer _server;
WebApiBatteryClass _webApiBattery; WebApiBatteryClass _webApiBattery;
@ -82,4 +78,4 @@ private:
WebApiWsBatteryLiveClass _webApiWsBatteryLive; WebApiWsBatteryLiveClass _webApiWsBatteryLive;
}; };
extern WebApiClass WebApi; extern WebApiClass WebApi;

View File

@ -2,12 +2,12 @@
#pragma once #pragma once
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
#include <AsyncJson.h> #include <AsyncJson.h>
class WebApiHuaweiClass { class WebApiHuaweiClass {
public: public:
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
void getJsonData(JsonVariant& root); void getJsonData(JsonVariant& root);
private: private:
void onStatus(AsyncWebServerRequest* request); void onStatus(AsyncWebServerRequest* request);

View File

@ -2,12 +2,11 @@
#pragma once #pragma once
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiBatteryClass { class WebApiBatteryClass {
public: public:
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
private: private:
void onStatus(AsyncWebServerRequest* request); void onStatus(AsyncWebServerRequest* request);

View File

@ -2,11 +2,11 @@
#pragma once #pragma once
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiConfigClass { class WebApiConfigClass {
public: public:
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
private: private:
void onConfigGet(AsyncWebServerRequest* request); void onConfigGet(AsyncWebServerRequest* request);
@ -16,4 +16,4 @@ private:
void onConfigUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final); void onConfigUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final);
AsyncWebServer* _server; AsyncWebServer* _server;
}; };

View File

@ -2,15 +2,15 @@
#pragma once #pragma once
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiDeviceClass { class WebApiDeviceClass {
public: public:
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
private: private:
void onDeviceAdminGet(AsyncWebServerRequest* request); void onDeviceAdminGet(AsyncWebServerRequest* request);
void onDeviceAdminPost(AsyncWebServerRequest* request); void onDeviceAdminPost(AsyncWebServerRequest* request);
AsyncWebServer* _server; AsyncWebServer* _server;
}; };

View File

@ -2,14 +2,14 @@
#pragma once #pragma once
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiDevInfoClass { class WebApiDevInfoClass {
public: public:
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
private: private:
void onDevInfoStatus(AsyncWebServerRequest* request); void onDevInfoStatus(AsyncWebServerRequest* request);
AsyncWebServer* _server; AsyncWebServer* _server;
}; };

View File

@ -2,16 +2,19 @@
#pragma once #pragma once
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiDtuClass { class WebApiDtuClass {
public: public:
void init(AsyncWebServer& server); WebApiDtuClass();
void loop(); void init(AsyncWebServer& server, Scheduler& scheduler);
private: private:
void onDtuAdminGet(AsyncWebServerRequest* request); void onDtuAdminGet(AsyncWebServerRequest* request);
void onDtuAdminPost(AsyncWebServerRequest* request); void onDtuAdminPost(AsyncWebServerRequest* request);
AsyncWebServer* _server; AsyncWebServer* _server;
bool _performReload = false;
Task _applyDataTask;
void applyDataTaskCb();
}; };

View File

@ -2,14 +2,14 @@
#pragma once #pragma once
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiEventlogClass { class WebApiEventlogClass {
public: public:
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
private: private:
void onEventlogStatus(AsyncWebServerRequest* request); void onEventlogStatus(AsyncWebServerRequest* request);
AsyncWebServer* _server; AsyncWebServer* _server;
}; };

View File

@ -2,15 +2,15 @@
#pragma once #pragma once
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiFirmwareClass { class WebApiFirmwareClass {
public: public:
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
private: private:
void onFirmwareUpdateFinish(AsyncWebServerRequest* request); void onFirmwareUpdateFinish(AsyncWebServerRequest* request);
void onFirmwareUpdateUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final); void onFirmwareUpdateUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final);
AsyncWebServer* _server; AsyncWebServer* _server;
}; };

View File

@ -2,15 +2,15 @@
#pragma once #pragma once
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiGridProfileClass { class WebApiGridProfileClass {
public: public:
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
private: private:
void onGridProfileStatus(AsyncWebServerRequest* request); void onGridProfileStatus(AsyncWebServerRequest* request);
void onGridProfileRawdata(AsyncWebServerRequest* request); void onGridProfileRawdata(AsyncWebServerRequest* request);
AsyncWebServer* _server; AsyncWebServer* _server;
}; };

View File

@ -2,11 +2,11 @@
#pragma once #pragma once
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiInverterClass { class WebApiInverterClass {
public: public:
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
private: private:
void onInverterList(AsyncWebServerRequest* request); void onInverterList(AsyncWebServerRequest* request);
@ -16,4 +16,4 @@ private:
void onInverterOrder(AsyncWebServerRequest* request); void onInverterOrder(AsyncWebServerRequest* request);
AsyncWebServer* _server; AsyncWebServer* _server;
}; };

View File

@ -2,15 +2,15 @@
#pragma once #pragma once
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiLimitClass { class WebApiLimitClass {
public: public:
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
private: private:
void onLimitStatus(AsyncWebServerRequest* request); void onLimitStatus(AsyncWebServerRequest* request);
void onLimitPost(AsyncWebServerRequest* request); void onLimitPost(AsyncWebServerRequest* request);
AsyncWebServer* _server; AsyncWebServer* _server;
}; };

View File

@ -2,14 +2,14 @@
#pragma once #pragma once
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiMaintenanceClass { class WebApiMaintenanceClass {
public: public:
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
private: private:
void onRebootPost(AsyncWebServerRequest* request); void onRebootPost(AsyncWebServerRequest* request);
AsyncWebServer* _server; AsyncWebServer* _server;
}; };

View File

@ -2,13 +2,13 @@
#pragma once #pragma once
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
#define MQTT_JSON_DOC_SIZE 10240 #define MQTT_JSON_DOC_SIZE 10240
class WebApiMqttClass { class WebApiMqttClass {
public: public:
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
private: private:
void onMqttStatus(AsyncWebServerRequest* request); void onMqttStatus(AsyncWebServerRequest* request);
@ -17,4 +17,4 @@ private:
String getTlsCertInfo(const char* cert); String getTlsCertInfo(const char* cert);
AsyncWebServer* _server; AsyncWebServer* _server;
}; };

View File

@ -2,11 +2,11 @@
#pragma once #pragma once
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiNetworkClass { class WebApiNetworkClass {
public: public:
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
private: private:
void onNetworkStatus(AsyncWebServerRequest* request); void onNetworkStatus(AsyncWebServerRequest* request);
@ -14,4 +14,4 @@ private:
void onNetworkAdminPost(AsyncWebServerRequest* request); void onNetworkAdminPost(AsyncWebServerRequest* request);
AsyncWebServer* _server; AsyncWebServer* _server;
}; };

View File

@ -2,11 +2,11 @@
#pragma once #pragma once
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiNtpClass { class WebApiNtpClass {
public: public:
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
private: private:
void onNtpStatus(AsyncWebServerRequest* request); void onNtpStatus(AsyncWebServerRequest* request);
@ -16,4 +16,4 @@ private:
void onNtpTimePost(AsyncWebServerRequest* request); void onNtpTimePost(AsyncWebServerRequest* request);
AsyncWebServer* _server; AsyncWebServer* _server;
}; };

View File

@ -2,15 +2,15 @@
#pragma once #pragma once
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiPowerClass { class WebApiPowerClass {
public: public:
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
private: private:
void onPowerStatus(AsyncWebServerRequest* request); void onPowerStatus(AsyncWebServerRequest* request);
void onPowerPost(AsyncWebServerRequest* request); void onPowerPost(AsyncWebServerRequest* request);
AsyncWebServer* _server; AsyncWebServer* _server;
}; };

View File

@ -2,12 +2,12 @@
#pragma once #pragma once
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiPowerLimiterClass { class WebApiPowerLimiterClass {
public: public:
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
private: private:
void onStatus(AsyncWebServerRequest* request); void onStatus(AsyncWebServerRequest* request);

View File

@ -2,12 +2,12 @@
#pragma once #pragma once
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiPowerMeterClass { class WebApiPowerMeterClass {
public: public:
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
private: private:
void onStatus(AsyncWebServerRequest* request); void onStatus(AsyncWebServerRequest* request);

View File

@ -3,12 +3,12 @@
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <Hoymiles.h> #include <Hoymiles.h>
#include <TaskSchedulerDeclarations.h>
#include <map> #include <map>
class WebApiPrometheusClass { class WebApiPrometheusClass {
public: public:
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
private: private:
void onPrometheusMetricsGet(AsyncWebServerRequest* request); void onPrometheusMetricsGet(AsyncWebServerRequest* request);
@ -47,4 +47,4 @@ private:
{ FLD_EFF, MetricType_t::GAUGE }, { FLD_EFF, MetricType_t::GAUGE },
{ FLD_IRR, MetricType_t::GAUGE }, { FLD_IRR, MetricType_t::GAUGE },
}; };
}; };

View File

@ -2,11 +2,11 @@
#pragma once #pragma once
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiSecurityClass { class WebApiSecurityClass {
public: public:
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
private: private:
void onSecurityGet(AsyncWebServerRequest* request); void onSecurityGet(AsyncWebServerRequest* request);
@ -15,4 +15,4 @@ private:
void onAuthenticateGet(AsyncWebServerRequest* request); void onAuthenticateGet(AsyncWebServerRequest* request);
AsyncWebServer* _server; AsyncWebServer* _server;
}; };

View File

@ -2,14 +2,14 @@
#pragma once #pragma once
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiSysstatusClass { class WebApiSysstatusClass {
public: public:
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
private: private:
void onSystemStatus(AsyncWebServerRequest* request); void onSystemStatus(AsyncWebServerRequest* request);
AsyncWebServer* _server; AsyncWebServer* _server;
}; };

View File

@ -2,12 +2,12 @@
#pragma once #pragma once
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiVedirectClass { class WebApiVedirectClass {
public: public:
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
private: private:
void onVedirectStatus(AsyncWebServerRequest* request); void onVedirectStatus(AsyncWebServerRequest* request);

View File

@ -2,12 +2,12 @@
#pragma once #pragma once
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiWebappClass { class WebApiWebappClass {
public: public:
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
private: private:
AsyncWebServer* _server; AsyncWebServer* _server;
}; };

View File

@ -3,13 +3,13 @@
#include "ArduinoJson.h" #include "ArduinoJson.h"
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
#include <mutex> #include <mutex>
class WebApiWsHuaweiLiveClass { class WebApiWsHuaweiLiveClass {
public: public:
WebApiWsHuaweiLiveClass(); WebApiWsHuaweiLiveClass();
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
private: private:
void generateJsonResponse(JsonVariant& root); void generateJsonResponse(JsonVariant& root);
@ -19,8 +19,11 @@ private:
AsyncWebServer* _server; AsyncWebServer* _server;
AsyncWebSocket _ws; AsyncWebSocket _ws;
uint32_t _lastWsCleanup = 0;
uint32_t _lastUpdateCheck = 0;
std::mutex _mutex; std::mutex _mutex;
Task _wsCleanupTask;
void wsCleanupTaskCb();
Task _sendDataTask;
void sendDataTaskCb();
}; };

View File

@ -3,13 +3,13 @@
#include "ArduinoJson.h" #include "ArduinoJson.h"
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
#include <mutex> #include <mutex>
class WebApiWsBatteryLiveClass { class WebApiWsBatteryLiveClass {
public: public:
WebApiWsBatteryLiveClass(); WebApiWsBatteryLiveClass();
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
private: private:
void generateJsonResponse(JsonVariant& root); void generateJsonResponse(JsonVariant& root);
@ -19,9 +19,14 @@ private:
AsyncWebServer* _server; AsyncWebServer* _server;
AsyncWebSocket _ws; AsyncWebSocket _ws;
uint32_t _lastWsCleanup = 0;
uint32_t _lastUpdateCheck = 0; uint32_t _lastUpdateCheck = 0;
static constexpr uint16_t _responseSize = 1024 + 512; static constexpr uint16_t _responseSize = 1024 + 512;
std::mutex _mutex; std::mutex _mutex;
Task _wsCleanupTask;
void wsCleanupTaskCb();
Task _sendDataTask;
void sendDataTaskCb();
}; };

View File

@ -1,17 +1,18 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiWsConsoleClass {
public: class WebApiWsConsoleClass {
WebApiWsConsoleClass(); public:
void init(AsyncWebServer& server); WebApiWsConsoleClass();
void loop(); void init(AsyncWebServer& server, Scheduler& scheduler);
private: private:
AsyncWebServer* _server; AsyncWebServer* _server;
AsyncWebSocket _ws; AsyncWebSocket _ws;
uint32_t _lastWsCleanup = 0; Task _wsCleanupTask;
}; void wsCleanupTaskCb();
};

View File

@ -4,16 +4,16 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <Hoymiles.h> #include <Hoymiles.h>
#include <TaskSchedulerDeclarations.h>
class WebApiWsLiveClass { class WebApiWsLiveClass {
public: public:
WebApiWsLiveClass(); WebApiWsLiveClass();
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
private: private:
void generateJsonResponse(JsonVariant& root); void generateJsonResponse(JsonVariant& root);
void addField(JsonObject& root, uint8_t idx, std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId, String topic = ""); void addField(JsonObject& root, std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId, String topic = "");
void addTotalField(JsonObject& root, const String& name, const float value, const String& unit, const uint8_t digits); void addTotalField(JsonObject& root, const String& name, const float value, const String& unit, const uint8_t digits);
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);
@ -22,9 +22,13 @@ private:
AsyncWebSocket _ws; AsyncWebSocket _ws;
uint32_t _lastWsPublish = 0; uint32_t _lastWsPublish = 0;
uint32_t _lastInvUpdateCheck = 0;
uint32_t _lastWsCleanup = 0;
uint32_t _newestInverterTimestamp = 0; uint32_t _newestInverterTimestamp = 0;
std::mutex _mutex; std::mutex _mutex;
};
Task _wsCleanupTask;
void wsCleanupTaskCb();
Task _sendDataTask;
void sendDataTaskCb();
};

View File

@ -3,14 +3,14 @@
#include "ArduinoJson.h" #include "ArduinoJson.h"
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
#include <VeDirectMpptController.h> #include <VeDirectMpptController.h>
#include <mutex> #include <mutex>
class WebApiWsVedirectLiveClass { class WebApiWsVedirectLiveClass {
public: public:
WebApiWsVedirectLiveClass(); WebApiWsVedirectLiveClass();
void init(AsyncWebServer& server); void init(AsyncWebServer& server, Scheduler& scheduler);
void loop();
private: private:
void generateJsonResponse(JsonVariant& root); void generateJsonResponse(JsonVariant& root);
@ -21,9 +21,14 @@ private:
AsyncWebSocket _ws; AsyncWebSocket _ws;
uint32_t _lastWsPublish = 0; uint32_t _lastWsPublish = 0;
uint32_t _lastWsCleanup = 0;
uint32_t _dataAgeMillis = 0; uint32_t _dataAgeMillis = 0;
static constexpr uint16_t _responseSize = 1024 + 128; static constexpr uint16_t _responseSize = 1024 + 128;
std::mutex _mutex; std::mutex _mutex;
Task _wsCleanupTask;
void wsCleanupTaskCb();
Task _sendDataTask;
void sendDataTaskCb();
}; };

View File

@ -137,6 +137,14 @@ time_t DevInfoParser::getFwBuildDateTime() const
return timegm(&timeinfo); return timegm(&timeinfo);
} }
String DevInfoParser::getFwBuildDateTimeStr() const
{
char timebuffer[32];
const time_t t = getFwBuildDateTime();
std::strftime(timebuffer, sizeof(timebuffer), "%Y-%m-%d %H:%M:%S", gmtime(&t));
return timebuffer;
}
uint16_t DevInfoParser::getFwBootloaderVersion() const uint16_t DevInfoParser::getFwBootloaderVersion() const
{ {
HOY_SEMAPHORE_TAKE(); HOY_SEMAPHORE_TAKE();
@ -254,4 +262,4 @@ time_t DevInfoParser::timegm(const struct tm* t)
result -= 3600; result -= 3600;
/*@ -matchanyintegral @*/ /*@ -matchanyintegral @*/
return (result); return (result);
} }

View File

@ -21,6 +21,7 @@ public:
uint16_t getFwBuildVersion() const; uint16_t getFwBuildVersion() const;
time_t getFwBuildDateTime() const; time_t getFwBuildDateTime() const;
String getFwBuildDateTimeStr() const;
uint16_t getFwBootloaderVersion() const; uint16_t getFwBootloaderVersion() const;
uint32_t getHwPartNumber() const; uint32_t getHwPartNumber() const;
@ -43,4 +44,4 @@ private:
uint8_t _payloadDevInfoSimple[DEV_INFO_SIZE] = {}; uint8_t _payloadDevInfoSimple[DEV_INFO_SIZE] = {};
uint8_t _devInfoSimpleLength = 0; uint8_t _devInfoSimpleLength = 0;
}; };

View File

@ -390,7 +390,7 @@ std::vector<uint8_t> GridProfileParser::getRawData() const
{ {
std::vector<uint8_t> ret; std::vector<uint8_t> ret;
HOY_SEMAPHORE_TAKE(); HOY_SEMAPHORE_TAKE();
for (uint8_t i = 0; i < GRID_PROFILE_SIZE; i++) { for (uint8_t i = 0; i < _gridProfileLength; i++) {
ret.push_back(_payloadGridProfile[i]); ret.push_back(_payloadGridProfile[i]);
} }
HOY_SEMAPHORE_GIVE(); HOY_SEMAPHORE_GIVE();

View File

@ -37,13 +37,12 @@ build_unflags =
lib_deps = lib_deps =
https://github.com/yubox-node-org/ESPAsyncWebServer https://github.com/yubox-node-org/ESPAsyncWebServer
bblanchon/ArduinoJson @ ^6.21.4 bblanchon/ArduinoJson @ ^6.21.5
https://github.com/bertmelis/espMqttClient.git#v1.5.0 https://github.com/bertmelis/espMqttClient.git#v1.5.0
nrf24/RF24 @ ^1.4.8 nrf24/RF24 @ ^1.4.8
olikraus/U8g2 @ ^2.35.9 olikraus/U8g2 @ ^2.35.9
buelowp/sunset @ ^1.1.7 buelowp/sunset @ ^1.1.7
https://github.com/arkhipenko/TaskScheduler#testing https://github.com/arkhipenko/TaskScheduler#testing
https://github.com/arkhipenko/TaskScheduler#testing
https://github.com/coryjfowler/MCP_CAN_lib https://github.com/coryjfowler/MCP_CAN_lib
plerup/EspSoftwareSerial @ ^8.0.1 plerup/EspSoftwareSerial @ ^8.0.1
https://github.com/dok-net/ghostl @ ^1.0.1 https://github.com/dok-net/ghostl @ ^1.0.1

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2023 Thomas Basler and others * Copyright (C) 2023-2024 Thomas Basler and others
*/ */
#include "Datastore.h" #include "Datastore.h"
#include "Configuration.h" #include "Configuration.h"
@ -8,12 +8,14 @@
DatastoreClass Datastore; DatastoreClass Datastore;
DatastoreClass::DatastoreClass()
: _loopTask(1 * TASK_SECOND, TASK_FOREVER, std::bind(&DatastoreClass::loop, this))
{
}
void DatastoreClass::init(Scheduler& scheduler) void DatastoreClass::init(Scheduler& scheduler)
{ {
scheduler.addTask(_loopTask); scheduler.addTask(_loopTask);
_loopTask.setCallback(std::bind(&DatastoreClass::loop, this));
_loopTask.setIterations(TASK_FOREVER);
_loopTask.setInterval(1 * TASK_SECOND);
_loopTask.enable(); _loopTask.enable();
} }

View File

@ -35,6 +35,7 @@ static const char* const i18n_yield_total_kwh[] = { "total: %.1f kWh", "Ges.: %.
static const char* const i18n_date_format[] = { "%m/%d/%Y %H:%M", "%d.%m.%Y %H:%M", "%d/%m/%Y %H:%M" }; static const char* const i18n_date_format[] = { "%m/%d/%Y %H:%M", "%d.%m.%Y %H:%M", "%d/%m/%Y %H:%M" };
DisplayGraphicClass::DisplayGraphicClass() DisplayGraphicClass::DisplayGraphicClass()
: _loopTask(TASK_IMMEDIATE, TASK_FOREVER, std::bind(&DisplayGraphicClass::loop, this))
{ {
} }
@ -55,8 +56,6 @@ void DisplayGraphicClass::init(Scheduler& scheduler, const DisplayType_t type, c
_diagram.init(scheduler, _display); _diagram.init(scheduler, _display);
scheduler.addTask(_loopTask); scheduler.addTask(_loopTask);
_loopTask.setCallback(std::bind(&DisplayGraphicClass::loop, this));
_loopTask.setIterations(TASK_FOREVER);
_loopTask.setInterval(_period); _loopTask.setInterval(_period);
_loopTask.enable(); _loopTask.enable();
} }

View File

@ -8,6 +8,8 @@
#include <algorithm> #include <algorithm>
DisplayGraphicDiagramClass::DisplayGraphicDiagramClass() DisplayGraphicDiagramClass::DisplayGraphicDiagramClass()
: _averageTask(1 * TASK_SECOND, TASK_FOREVER, std::bind(&DisplayGraphicDiagramClass::averageLoop, this))
, _dataPointTask(TASK_IMMEDIATE, TASK_FOREVER, std::bind(&DisplayGraphicDiagramClass::dataPointLoop, this))
{ {
} }
@ -16,14 +18,9 @@ void DisplayGraphicDiagramClass::init(Scheduler& scheduler, U8G2* display)
_display = display; _display = display;
scheduler.addTask(_averageTask); scheduler.addTask(_averageTask);
_averageTask.setCallback(std::bind(&DisplayGraphicDiagramClass::averageLoop, this));
_averageTask.setIterations(TASK_FOREVER);
_averageTask.setInterval(1 * TASK_SECOND);
_averageTask.enable(); _averageTask.enable();
scheduler.addTask(_dataPointTask); scheduler.addTask(_dataPointTask);
_dataPointTask.setCallback(std::bind(&DisplayGraphicDiagramClass::dataPointLoop, this));
_dataPointTask.setIterations(TASK_FOREVER);
updatePeriod(); updatePeriod();
_dataPointTask.enable(); _dataPointTask.enable();
} }
@ -58,7 +55,7 @@ uint32_t DisplayGraphicDiagramClass::getSecondsPerDot()
void DisplayGraphicDiagramClass::updatePeriod() void DisplayGraphicDiagramClass::updatePeriod()
{ {
// Calculate seconds per datapoint // Calculate seconds per datapoint
_dataPointTask.setInterval(Configuration.get().Display.Diagram.Duration * TASK_SECOND / MAX_DATAPOINTS ); _dataPointTask.setInterval(Configuration.get().Display.Diagram.Duration * TASK_SECOND / MAX_DATAPOINTS);
} }
void DisplayGraphicDiagramClass::redraw(uint8_t screenSaverOffsetX, uint8_t xPos, uint8_t yPos, uint8_t width, uint8_t height, bool isFullscreen) void DisplayGraphicDiagramClass::redraw(uint8_t screenSaverOffsetX, uint8_t xPos, uint8_t yPos, uint8_t width, uint8_t height, bool isFullscreen)
@ -110,7 +107,9 @@ void DisplayGraphicDiagramClass::redraw(uint8_t screenSaverOffsetX, uint8_t xPos
if (maxWatts > 0 && isFullscreen) { if (maxWatts > 0 && isFullscreen) {
// draw y axis ticks // draw y axis ticks
const uint16_t yAxisWattPerTick = maxWatts <= 100 ? 10 : maxWatts <= 1000 ? 100 : maxWatts < 5000 ? 500 : 1000; const uint16_t yAxisWattPerTick = maxWatts <= 100 ? 10 : maxWatts <= 1000 ? 100
: maxWatts < 5000 ? 500
: 1000;
const uint8_t yAxisTickSizePixel = height / (maxWatts / yAxisWattPerTick); const uint8_t yAxisTickSizePixel = height / (maxWatts / yAxisWattPerTick);
for (int16_t tickYPos = graphPosY + height; tickYPos > graphPosY - arrow_size; tickYPos -= yAxisTickSizePixel) { for (int16_t tickYPos = graphPosY + height; tickYPos > graphPosY - arrow_size; tickYPos -= yAxisTickSizePixel) {

View File

@ -25,6 +25,12 @@
InverterSettingsClass InverterSettings; InverterSettingsClass InverterSettings;
InverterSettingsClass::InverterSettingsClass()
: _settingsTask(INVERTER_UPDATE_SETTINGS_INTERVAL, TASK_FOREVER, std::bind(&InverterSettingsClass::settingsLoop, this))
, _hoyTask(TASK_IMMEDIATE, TASK_FOREVER, std::bind(&InverterSettingsClass::hoyLoop, this))
{
}
void InverterSettingsClass::init(Scheduler& scheduler) void InverterSettingsClass::init(Scheduler& scheduler)
{ {
const CONFIG_T& config = Configuration.get(); const CONFIG_T& config = Configuration.get();
@ -94,14 +100,9 @@ void InverterSettingsClass::init(Scheduler& scheduler)
} }
scheduler.addTask(_hoyTask); scheduler.addTask(_hoyTask);
_hoyTask.setCallback(std::bind(&InverterSettingsClass::hoyLoop, this));
_hoyTask.setIterations(TASK_FOREVER);
_hoyTask.enable(); _hoyTask.enable();
scheduler.addTask(_settingsTask); scheduler.addTask(_settingsTask);
_settingsTask.setCallback(std::bind(&InverterSettingsClass::settingsLoop, this));
_settingsTask.setIterations(TASK_FOREVER);
_settingsTask.setInterval(INVERTER_UPDATE_SETTINGS_INTERVAL);
_settingsTask.enable(); _settingsTask.enable();
} }
@ -122,7 +123,7 @@ void InverterSettingsClass::settingsLoop()
inv->setEnablePolling(inv_cfg.Poll_Enable && (SunPosition.isDayPeriod() || inv_cfg.Poll_Enable_Night)); inv->setEnablePolling(inv_cfg.Poll_Enable && (SunPosition.isDayPeriod() || inv_cfg.Poll_Enable_Night));
inv->setEnableCommands(inv_cfg.Command_Enable && (SunPosition.isDayPeriod() || inv_cfg.Command_Enable_Night)); inv->setEnableCommands(inv_cfg.Command_Enable && (SunPosition.isDayPeriod() || inv_cfg.Command_Enable_Night));
} }
} }
void InverterSettingsClass::hoyLoop() void InverterSettingsClass::hoyLoop()
{ {

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2023 Thomas Basler and others * Copyright (C) 2023-2024 Thomas Basler and others
*/ */
#include "Led_Single.h" #include "Led_Single.h"
#include "Configuration.h" #include "Configuration.h"
@ -38,6 +38,8 @@ const uint8_t pwmTable[] = {
#define LED_OFF 0 #define LED_OFF 0
LedSingleClass::LedSingleClass() LedSingleClass::LedSingleClass()
: _setTask(LEDSINGLE_UPDATE_INTERVAL * TASK_MILLISECOND, TASK_FOREVER, std::bind(&LedSingleClass::setLoop, this))
, _outputTask(TASK_IMMEDIATE, TASK_FOREVER, std::bind(&LedSingleClass::outputLoop, this))
{ {
} }
@ -62,14 +64,9 @@ void LedSingleClass::init(Scheduler& scheduler)
if (ledActive) { if (ledActive) {
scheduler.addTask(_outputTask); scheduler.addTask(_outputTask);
_outputTask.setCallback(std::bind(&LedSingleClass::outputLoop, this));
_outputTask.setIterations(TASK_FOREVER);
_outputTask.enable(); _outputTask.enable();
scheduler.addTask(_setTask); scheduler.addTask(_setTask);
_setTask.setCallback(std::bind(&LedSingleClass::setLoop, this));
_setTask.setInterval(LEDSINGLE_UPDATE_INTERVAL * TASK_MILLISECOND);
_setTask.setIterations(TASK_FOREVER);
_setTask.enable(); _setTask.enable();
} }
} }

View File

@ -1,17 +1,20 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include <HardwareSerial.h> #include <HardwareSerial.h>
#include "MessageOutput.h" #include "MessageOutput.h"
MessageOutputClass MessageOutput; MessageOutputClass MessageOutput;
MessageOutputClass::MessageOutputClass()
: _loopTask(TASK_IMMEDIATE, TASK_FOREVER, std::bind(&MessageOutputClass::loop, this))
{
}
void MessageOutputClass::init(Scheduler& scheduler) void MessageOutputClass::init(Scheduler& scheduler)
{ {
scheduler.addTask(_loopTask); scheduler.addTask(_loopTask);
_loopTask.setCallback(std::bind(&MessageOutputClass::loop, this));
_loopTask.setIterations(TASK_FOREVER);
_loopTask.enable(); _loopTask.enable();
} }
@ -110,4 +113,4 @@ void MessageOutputClass::loop()
_ws->textAll(std::make_shared<message_t>(std::move(_lines.front()))); _ws->textAll(std::make_shared<message_t>(std::move(_lines.front())));
_lines.pop(); _lines.pop();
} }
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "MqttHandleDtu.h" #include "MqttHandleDtu.h"
#include "Configuration.h" #include "Configuration.h"
@ -10,11 +10,14 @@
MqttHandleDtuClass MqttHandleDtu; MqttHandleDtuClass MqttHandleDtu;
MqttHandleDtuClass::MqttHandleDtuClass()
: _loopTask(TASK_IMMEDIATE, TASK_FOREVER, std::bind(&MqttHandleDtuClass::loop, this))
{
}
void MqttHandleDtuClass::init(Scheduler& scheduler) void MqttHandleDtuClass::init(Scheduler& scheduler)
{ {
scheduler.addTask(_loopTask); scheduler.addTask(_loopTask);
_loopTask.setCallback(std::bind(&MqttHandleDtuClass::loop, this));
_loopTask.setIterations(TASK_FOREVER);
_loopTask.setInterval(Configuration.get().Mqtt.PublishInterval * TASK_SECOND); _loopTask.setInterval(Configuration.get().Mqtt.PublishInterval * TASK_SECOND);
_loopTask.enable(); _loopTask.enable();
} }
@ -35,4 +38,4 @@ void MqttHandleDtuClass::loop()
MqttSettings.publish("dtu/rssi", String(WiFi.RSSI())); MqttSettings.publish("dtu/rssi", String(WiFi.RSSI()));
MqttSettings.publish("dtu/bssid", WiFi.BSSIDstr()); MqttSettings.publish("dtu/bssid", WiFi.BSSIDstr());
} }
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "MqttHandleHass.h" #include "MqttHandleHass.h"
#include "MqttHandleInverter.h" #include "MqttHandleInverter.h"
@ -11,11 +11,14 @@
MqttHandleHassClass MqttHandleHass; MqttHandleHassClass MqttHandleHass;
MqttHandleHassClass::MqttHandleHassClass()
: _loopTask(TASK_IMMEDIATE, TASK_FOREVER, std::bind(&MqttHandleHassClass::loop, this))
{
}
void MqttHandleHassClass::init(Scheduler& scheduler) void MqttHandleHassClass::init(Scheduler& scheduler)
{ {
scheduler.addTask(_loopTask); scheduler.addTask(_loopTask);
_loopTask.setCallback(std::bind(&MqttHandleHassClass::loop, this));
_loopTask.setIterations(TASK_FOREVER);
_loopTask.enable(); _loopTask.enable();
} }
@ -53,7 +56,7 @@ void MqttHandleHassClass::publishConfig()
const CONFIG_T& config = Configuration.get(); const CONFIG_T& config = Configuration.get();
// publish DTU sensors // publish DTU sensors
publishDtuSensor("IP", "", "diagnostic", "mdi:network-outline", "", ""); publishDtuSensor("IP", "", "diagnostic", "mdi:network-outline", "", "");
publishDtuSensor("WiFi Signal", "signal_strength", "diagnostic", "", "dBm", "rssi"); publishDtuSensor("WiFi Signal", "signal_strength", "diagnostic", "", "dBm", "rssi");
publishDtuSensor("Uptime", "duration", "diagnostic", "", "s", ""); publishDtuSensor("Uptime", "duration", "diagnostic", "", "s", "");
@ -421,5 +424,5 @@ void MqttHandleHassClass::publish(const String& subtopic, const String& payload)
{ {
String topic = Configuration.get().Mqtt.Hass.Topic; String topic = Configuration.get().Mqtt.Hass.Topic;
topic += subtopic; topic += subtopic;
MqttSettings.publishGeneric(topic.c_str(), payload.c_str(), Configuration.get().Mqtt.Hass.Retain); MqttSettings.publishGeneric(topic, payload, Configuration.get().Mqtt.Hass.Retain);
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "MqttHandleInverter.h" #include "MqttHandleInverter.h"
#include "MessageOutput.h" #include "MessageOutput.h"
@ -18,6 +18,11 @@
MqttHandleInverterClass MqttHandleInverter; MqttHandleInverterClass MqttHandleInverter;
MqttHandleInverterClass::MqttHandleInverterClass()
: _loopTask(TASK_IMMEDIATE, TASK_FOREVER, std::bind(&MqttHandleInverterClass::loop, this))
{
}
void MqttHandleInverterClass::init(Scheduler& scheduler) void MqttHandleInverterClass::init(Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
@ -28,16 +33,14 @@ void MqttHandleInverterClass::init(Scheduler& scheduler)
using std::placeholders::_6; using std::placeholders::_6;
const String topic = MqttSettings.getPrefix(); const String topic = MqttSettings.getPrefix();
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_RELATIVE).c_str(), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_RELATIVE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_ABSOLUTE).c_str(), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_ABSOLUTE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_RELATIVE).c_str(), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_RELATIVE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_ABSOLUTE).c_str(), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_ABSOLUTE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_POWER).c_str(), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_POWER), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_RESTART).c_str(), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_RESTART), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
scheduler.addTask(_loopTask); scheduler.addTask(_loopTask);
_loopTask.setCallback(std::bind(&MqttHandleInverterClass::loop, this));
_loopTask.setIterations(TASK_FOREVER);
_loopTask.setInterval(Configuration.get().Mqtt.PublishInterval * TASK_SECOND); _loopTask.setInterval(Configuration.get().Mqtt.PublishInterval * TASK_SECOND);
_loopTask.enable(); _loopTask.enable();
} }
@ -68,10 +71,7 @@ void MqttHandleInverterClass::loop()
MqttSettings.publish(subtopic + "/device/fwbuildversion", String(inv->DevInfo()->getFwBuildVersion())); MqttSettings.publish(subtopic + "/device/fwbuildversion", String(inv->DevInfo()->getFwBuildVersion()));
// Firmware Build DateTime // Firmware Build DateTime
char timebuffer[32]; MqttSettings.publish(subtopic + "/device/fwbuilddatetime", inv->DevInfo()->getFwBuildDateTimeStr());
const time_t t = inv->DevInfo()->getFwBuildDateTime();
std::strftime(timebuffer, sizeof(timebuffer), "%Y-%m-%d %H:%M:%S", gmtime(&t));
MqttSettings.publish(subtopic + "/device/fwbuilddatetime", String(timebuffer));
// Hardware part number // Hardware part number
MqttSettings.publish(subtopic + "/device/hwpartnumber", String(inv->DevInfo()->getHwPartNumber())); MqttSettings.publish(subtopic + "/device/hwpartnumber", String(inv->DevInfo()->getHwPartNumber()));
@ -246,4 +246,4 @@ void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessagePro
MessageOutput.println("Ignored because retained"); MessageOutput.println("Ignored because retained");
} }
} }
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2023 Thomas Basler and others * Copyright (C) 2023-2024 Thomas Basler and others
*/ */
#include "MqttHandleInverterTotal.h" #include "MqttHandleInverterTotal.h"
#include "Configuration.h" #include "Configuration.h"
@ -10,11 +10,14 @@
MqttHandleInverterTotalClass MqttHandleInverterTotal; MqttHandleInverterTotalClass MqttHandleInverterTotal;
MqttHandleInverterTotalClass::MqttHandleInverterTotalClass()
: _loopTask(TASK_IMMEDIATE, TASK_FOREVER, std::bind(&MqttHandleInverterTotalClass::loop, this))
{
}
void MqttHandleInverterTotalClass::init(Scheduler& scheduler) void MqttHandleInverterTotalClass::init(Scheduler& scheduler)
{ {
scheduler.addTask(_loopTask); scheduler.addTask(_loopTask);
_loopTask.setCallback(std::bind(&MqttHandleInverterTotalClass::loop, this));
_loopTask.setIterations(TASK_FOREVER);
_loopTask.setInterval(Configuration.get().Mqtt.PublishInterval * TASK_SECOND); _loopTask.setInterval(Configuration.get().Mqtt.PublishInterval * TASK_SECOND);
_loopTask.enable(); _loopTask.enable();
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "NetworkSettings.h" #include "NetworkSettings.h"
#include "Configuration.h" #include "Configuration.h"
@ -12,7 +12,8 @@
#include <ETH.h> #include <ETH.h>
NetworkSettingsClass::NetworkSettingsClass() NetworkSettingsClass::NetworkSettingsClass()
: _apIp(192, 168, 4, 1) : _loopTask(TASK_IMMEDIATE, TASK_FOREVER, std::bind(&NetworkSettingsClass::loop, this))
, _apIp(192, 168, 4, 1)
, _apNetmask(255, 255, 255, 0) , _apNetmask(255, 255, 255, 0)
{ {
_dnsServer.reset(new DNSServer()); _dnsServer.reset(new DNSServer());
@ -29,8 +30,6 @@ void NetworkSettingsClass::init(Scheduler& scheduler)
setupMode(); setupMode();
scheduler.addTask(_loopTask); scheduler.addTask(_loopTask);
_loopTask.setCallback(std::bind(&NetworkSettingsClass::loop, this));
_loopTask.setIterations(TASK_FOREVER);
_loopTask.enable(); _loopTask.enable();
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2023 Thomas Basler and others * Copyright (C) 2023-2024 Thomas Basler and others
*/ */
#include "SunPosition.h" #include "SunPosition.h"
#include "Configuration.h" #include "Configuration.h"
@ -10,15 +10,13 @@
SunPositionClass SunPosition; SunPositionClass SunPosition;
SunPositionClass::SunPositionClass() SunPositionClass::SunPositionClass()
: _loopTask(5 * TASK_SECOND, TASK_FOREVER, std::bind(&SunPositionClass::loop, this))
{ {
} }
void SunPositionClass::init(Scheduler& scheduler) void SunPositionClass::init(Scheduler& scheduler)
{ {
scheduler.addTask(_loopTask); scheduler.addTask(_loopTask);
_loopTask.setCallback(std::bind(&SunPositionClass::loop, this));
_loopTask.setIterations(TASK_FOREVER);
_loopTask.setInterval(5 * TASK_SECOND);
_loopTask.enable(); _loopTask.enable();
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi.h" #include "WebApi.h"
#include "Configuration.h" #include "Configuration.h"
@ -14,72 +14,36 @@ WebApiClass::WebApiClass()
void WebApiClass::init(Scheduler& scheduler) void WebApiClass::init(Scheduler& scheduler)
{ {
_webApiConfig.init(_server); _webApiConfig.init(_server, scheduler);
_webApiDevice.init(_server); _webApiDevice.init(_server, scheduler);
_webApiDevInfo.init(_server); _webApiDevInfo.init(_server, scheduler);
_webApiDtu.init(_server); _webApiDtu.init(_server, scheduler);
_webApiEventlog.init(_server); _webApiEventlog.init(_server, scheduler);
_webApiFirmware.init(_server); _webApiFirmware.init(_server, scheduler);
_webApiGridprofile.init(_server); _webApiGridprofile.init(_server, scheduler);
_webApiInverter.init(_server); _webApiInverter.init(_server, scheduler);
_webApiLimit.init(_server); _webApiLimit.init(_server, scheduler);
_webApiMaintenance.init(_server); _webApiMaintenance.init(_server, scheduler);
_webApiMqtt.init(_server); _webApiMqtt.init(_server, scheduler);
_webApiNetwork.init(_server); _webApiNetwork.init(_server, scheduler);
_webApiNtp.init(_server); _webApiNtp.init(_server, scheduler);
_webApiPower.init(_server); _webApiPower.init(_server, scheduler);
_webApiPrometheus.init(_server); _webApiPrometheus.init(_server, scheduler);
_webApiSecurity.init(_server); _webApiSecurity.init(_server, scheduler);
_webApiSysstatus.init(_server); _webApiSysstatus.init(_server, scheduler);
_webApiWebapp.init(_server); _webApiWebapp.init(_server, scheduler);
_webApiWsConsole.init(_server); _webApiWsConsole.init(_server, scheduler);
_webApiWsLive.init(_server); _webApiWsLive.init(_server, scheduler);
_webApiBattery.init(_server); _webApiBattery.init(_server, scheduler);
_webApiPowerMeter.init(_server); _webApiPowerMeter.init(_server, scheduler);
_webApiPowerLimiter.init(_server); _webApiPowerLimiter.init(_server, scheduler);
_webApiWsVedirectLive.init(_server); _webApiWsVedirectLive.init(_server, scheduler);
_webApiVedirect.init(_server); _webApiVedirect.init(_server, scheduler);
_webApiWsHuaweiLive.init(_server); _webApiWsHuaweiLive.init(_server, scheduler);
_webApiHuaweiClass.init(_server); _webApiHuaweiClass.init(_server, scheduler);
_webApiWsBatteryLive.init(_server); _webApiWsBatteryLive.init(_server, scheduler);
_server.begin(); _server.begin();
scheduler.addTask(_loopTask);
_loopTask.setCallback(std::bind(&WebApiClass::loop, this));
_loopTask.setIterations(TASK_FOREVER);
_loopTask.enable();
}
void WebApiClass::loop()
{
_webApiBattery.loop();
_webApiConfig.loop();
_webApiDevice.loop();
_webApiDevInfo.loop();
_webApiDtu.loop();
_webApiEventlog.loop();
_webApiFirmware.loop();
_webApiGridprofile.loop();
_webApiInverter.loop();
_webApiLimit.loop();
_webApiMaintenance.loop();
_webApiMqtt.loop();
_webApiNetwork.loop();
_webApiNtp.loop();
_webApiPower.loop();
_webApiPowerMeter.loop();
_webApiPowerLimiter.loop();
_webApiSecurity.loop();
_webApiSysstatus.loop();
_webApiWebapp.loop();
_webApiWsConsole.loop();
_webApiWsLive.loop();
_webApiWsVedirectLive.loop();
_webApiVedirect.loop();
_webApiWsHuaweiLive.loop();
_webApiHuaweiClass.loop();
_webApiWsBatteryLive.loop();
} }
bool WebApiClass::checkCredentials(AsyncWebServerRequest* request) bool WebApiClass::checkCredentials(AsyncWebServerRequest* request)
@ -129,4 +93,4 @@ void WebApiClass::writeConfig(JsonVariant& retMsg, const WebApiError code, const
} }
} }
WebApiClass WebApi; WebApiClass WebApi;

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_Huawei.h" #include "WebApi_Huawei.h"
#include "Huawei_can.h" #include "Huawei_can.h"
@ -12,7 +12,7 @@
#include <AsyncJson.h> #include <AsyncJson.h>
#include <Hoymiles.h> #include <Hoymiles.h>
void WebApiHuaweiClass::init(AsyncWebServer& server) void WebApiHuaweiClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
@ -24,10 +24,6 @@ void WebApiHuaweiClass::init(AsyncWebServer& server)
_server->on("/api/huawei/limit/config", HTTP_POST, std::bind(&WebApiHuaweiClass::onPost, this, _1)); _server->on("/api/huawei/limit/config", HTTP_POST, std::bind(&WebApiHuaweiClass::onPost, this, _1));
} }
void WebApiHuaweiClass::loop()
{
}
void WebApiHuaweiClass::getJsonData(JsonVariant& root) { void WebApiHuaweiClass::getJsonData(JsonVariant& root) {
const RectifierParameters_t * rp = HuaweiCan.get(); const RectifierParameters_t * rp = HuaweiCan.get();

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "ArduinoJson.h" #include "ArduinoJson.h"
@ -13,7 +13,7 @@
#include "WebApi_errors.h" #include "WebApi_errors.h"
#include "helper.h" #include "helper.h"
void WebApiBatteryClass::init(AsyncWebServer& server) void WebApiBatteryClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
@ -24,10 +24,6 @@ void WebApiBatteryClass::init(AsyncWebServer& server)
_server->on("/api/battery/config", HTTP_POST, std::bind(&WebApiBatteryClass::onAdminPost, this, _1)); _server->on("/api/battery/config", HTTP_POST, std::bind(&WebApiBatteryClass::onAdminPost, this, _1));
} }
void WebApiBatteryClass::loop()
{
}
void WebApiBatteryClass::onStatus(AsyncWebServerRequest* request) void WebApiBatteryClass::onStatus(AsyncWebServerRequest* request)
{ {
if (!WebApi.checkCredentialsReadonly(request)) { if (!WebApi.checkCredentialsReadonly(request)) {

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_config.h" #include "WebApi_config.h"
#include "Configuration.h" #include "Configuration.h"
@ -10,7 +10,7 @@
#include <AsyncJson.h> #include <AsyncJson.h>
#include <LittleFS.h> #include <LittleFS.h>
void WebApiConfigClass::init(AsyncWebServer& server) void WebApiConfigClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
using std::placeholders::_2; using std::placeholders::_2;
@ -29,10 +29,6 @@ void WebApiConfigClass::init(AsyncWebServer& server)
std::bind(&WebApiConfigClass::onConfigUpload, this, _1, _2, _3, _4, _5, _6)); std::bind(&WebApiConfigClass::onConfigUpload, this, _1, _2, _3, _4, _5, _6));
} }
void WebApiConfigClass::loop()
{
}
void WebApiConfigClass::onConfigGet(AsyncWebServerRequest* request) void WebApiConfigClass::onConfigGet(AsyncWebServerRequest* request)
{ {
if (!WebApi.checkCredentials(request)) { if (!WebApi.checkCredentials(request)) {
@ -186,4 +182,4 @@ void WebApiConfigClass::onConfigUpload(AsyncWebServerRequest* request, String fi
// close the file handle as the upload is now done // close the file handle as the upload is now done
request->_tempFile.close(); request->_tempFile.close();
} }
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_device.h" #include "WebApi_device.h"
#include "Configuration.h" #include "Configuration.h"
@ -12,7 +12,7 @@
#include "helper.h" #include "helper.h"
#include <AsyncJson.h> #include <AsyncJson.h>
void WebApiDeviceClass::init(AsyncWebServer& server) void WebApiDeviceClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
@ -22,10 +22,6 @@ void WebApiDeviceClass::init(AsyncWebServer& server)
_server->on("/api/device/config", HTTP_POST, std::bind(&WebApiDeviceClass::onDeviceAdminPost, this, _1)); _server->on("/api/device/config", HTTP_POST, std::bind(&WebApiDeviceClass::onDeviceAdminPost, this, _1));
} }
void WebApiDeviceClass::loop()
{
}
void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request) void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request)
{ {
if (!WebApi.checkCredentials(request)) { if (!WebApi.checkCredentials(request)) {

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_devinfo.h" #include "WebApi_devinfo.h"
#include "WebApi.h" #include "WebApi.h"
@ -8,7 +8,7 @@
#include <Hoymiles.h> #include <Hoymiles.h>
#include <ctime> #include <ctime>
void WebApiDevInfoClass::init(AsyncWebServer& server) void WebApiDevInfoClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
@ -17,10 +17,6 @@ void WebApiDevInfoClass::init(AsyncWebServer& server)
_server->on("/api/devinfo/status", HTTP_GET, std::bind(&WebApiDevInfoClass::onDevInfoStatus, this, _1)); _server->on("/api/devinfo/status", HTTP_GET, std::bind(&WebApiDevInfoClass::onDevInfoStatus, this, _1));
} }
void WebApiDevInfoClass::loop()
{
}
void WebApiDevInfoClass::onDevInfoStatus(AsyncWebServerRequest* request) void WebApiDevInfoClass::onDevInfoStatus(AsyncWebServerRequest* request)
{ {
if (!WebApi.checkCredentialsReadonly(request)) { if (!WebApi.checkCredentialsReadonly(request)) {
@ -46,13 +42,9 @@ void WebApiDevInfoClass::onDevInfoStatus(AsyncWebServerRequest* request)
root["hw_version"] = inv->DevInfo()->getHwVersion(); root["hw_version"] = inv->DevInfo()->getHwVersion();
root["hw_model_name"] = inv->DevInfo()->getHwModelName(); root["hw_model_name"] = inv->DevInfo()->getHwModelName();
root["max_power"] = inv->DevInfo()->getMaxPower(); root["max_power"] = inv->DevInfo()->getMaxPower();
root["fw_build_datetime"] = inv->DevInfo()->getFwBuildDateTimeStr();
char timebuffer[32];
const time_t t = inv->DevInfo()->getFwBuildDateTime();
std::strftime(timebuffer, sizeof(timebuffer), "%Y-%m-%d %H:%M:%S", gmtime(&t));
root["fw_build_datetime"] = String(timebuffer);
} }
response->setLength(); response->setLength();
request->send(response); request->send(response);
} }

View File

@ -9,7 +9,12 @@
#include <AsyncJson.h> #include <AsyncJson.h>
#include <Hoymiles.h> #include <Hoymiles.h>
void WebApiDtuClass::init(AsyncWebServer& server) WebApiDtuClass::WebApiDtuClass()
: _applyDataTask(TASK_IMMEDIATE, TASK_ONCE, std::bind(&WebApiDtuClass::applyDataTaskCb, this))
{
}
void WebApiDtuClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
@ -17,22 +22,21 @@ void WebApiDtuClass::init(AsyncWebServer& server)
_server->on("/api/dtu/config", HTTP_GET, std::bind(&WebApiDtuClass::onDtuAdminGet, this, _1)); _server->on("/api/dtu/config", HTTP_GET, std::bind(&WebApiDtuClass::onDtuAdminGet, this, _1));
_server->on("/api/dtu/config", HTTP_POST, std::bind(&WebApiDtuClass::onDtuAdminPost, this, _1)); _server->on("/api/dtu/config", HTTP_POST, std::bind(&WebApiDtuClass::onDtuAdminPost, this, _1));
scheduler.addTask(_applyDataTask);
} }
void WebApiDtuClass::loop() void WebApiDtuClass::applyDataTaskCb()
{ {
if (_performReload) { // Execute stuff in main thread to avoid busy SPI bus
// Execute stuff in main thread to avoid busy SPI bus CONFIG_T& config = Configuration.get();
CONFIG_T& config = Configuration.get(); Hoymiles.getRadioNrf()->setPALevel((rf24_pa_dbm_e)config.Dtu.Nrf.PaLevel);
Hoymiles.getRadioNrf()->setPALevel((rf24_pa_dbm_e)config.Dtu.Nrf.PaLevel); Hoymiles.getRadioCmt()->setPALevel(config.Dtu.Cmt.PaLevel);
Hoymiles.getRadioCmt()->setPALevel(config.Dtu.Cmt.PaLevel); Hoymiles.getRadioNrf()->setDtuSerial(config.Dtu.Serial);
Hoymiles.getRadioNrf()->setDtuSerial(config.Dtu.Serial); Hoymiles.getRadioCmt()->setDtuSerial(config.Dtu.Serial);
Hoymiles.getRadioCmt()->setDtuSerial(config.Dtu.Serial); Hoymiles.getRadioCmt()->setCountryMode(static_cast<CountryModeId_t>(config.Dtu.Cmt.CountryMode));
Hoymiles.getRadioCmt()->setCountryMode(static_cast<CountryModeId_t>(config.Dtu.Cmt.CountryMode)); Hoymiles.getRadioCmt()->setInverterTargetFrequency(config.Dtu.Cmt.Frequency);
Hoymiles.getRadioCmt()->setInverterTargetFrequency(config.Dtu.Cmt.Frequency); Hoymiles.setPollInterval(config.Dtu.PollInterval);
Hoymiles.setPollInterval(config.Dtu.PollInterval);
_performReload = false;
}
} }
void WebApiDtuClass::onDtuAdminGet(AsyncWebServerRequest* request) void WebApiDtuClass::onDtuAdminGet(AsyncWebServerRequest* request)
@ -199,5 +203,5 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request)
response->setLength(); response->setLength();
request->send(response); request->send(response);
_performReload = true; _applyDataTask.enable();
} }

View File

@ -1,13 +1,13 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_eventlog.h" #include "WebApi_eventlog.h"
#include "WebApi.h" #include "WebApi.h"
#include <AsyncJson.h> #include <AsyncJson.h>
#include <Hoymiles.h> #include <Hoymiles.h>
void WebApiEventlogClass::init(AsyncWebServer& server) void WebApiEventlogClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
@ -16,10 +16,6 @@ void WebApiEventlogClass::init(AsyncWebServer& server)
_server->on("/api/eventlog/status", HTTP_GET, std::bind(&WebApiEventlogClass::onEventlogStatus, this, _1)); _server->on("/api/eventlog/status", HTTP_GET, std::bind(&WebApiEventlogClass::onEventlogStatus, this, _1));
} }
void WebApiEventlogClass::loop()
{
}
void WebApiEventlogClass::onEventlogStatus(AsyncWebServerRequest* request) void WebApiEventlogClass::onEventlogStatus(AsyncWebServerRequest* request)
{ {
if (!WebApi.checkCredentialsReadonly(request)) { if (!WebApi.checkCredentialsReadonly(request)) {
@ -70,4 +66,4 @@ void WebApiEventlogClass::onEventlogStatus(AsyncWebServerRequest* request)
response->setLength(); response->setLength();
request->send(response); request->send(response);
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_firmware.h" #include "WebApi_firmware.h"
#include "Configuration.h" #include "Configuration.h"
@ -10,7 +10,7 @@
#include "helper.h" #include "helper.h"
#include <AsyncJson.h> #include <AsyncJson.h>
void WebApiFirmwareClass::init(AsyncWebServer& server) void WebApiFirmwareClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
using std::placeholders::_2; using std::placeholders::_2;
@ -26,10 +26,6 @@ void WebApiFirmwareClass::init(AsyncWebServer& server)
std::bind(&WebApiFirmwareClass::onFirmwareUpdateUpload, this, _1, _2, _3, _4, _5, _6)); std::bind(&WebApiFirmwareClass::onFirmwareUpdateUpload, this, _1, _2, _3, _4, _5, _6));
} }
void WebApiFirmwareClass::loop()
{
}
void WebApiFirmwareClass::onFirmwareUpdateFinish(AsyncWebServerRequest* request) void WebApiFirmwareClass::onFirmwareUpdateFinish(AsyncWebServerRequest* request)
{ {
if (!WebApi.checkCredentials(request)) { if (!WebApi.checkCredentials(request)) {
@ -83,4 +79,4 @@ void WebApiFirmwareClass::onFirmwareUpdateUpload(AsyncWebServerRequest* request,
} else { } else {
return; return;
} }
} }

View File

@ -1,13 +1,13 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_gridprofile.h" #include "WebApi_gridprofile.h"
#include "WebApi.h" #include "WebApi.h"
#include <AsyncJson.h> #include <AsyncJson.h>
#include <Hoymiles.h> #include <Hoymiles.h>
void WebApiGridProfileClass::init(AsyncWebServer& server) void WebApiGridProfileClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
@ -17,10 +17,6 @@ void WebApiGridProfileClass::init(AsyncWebServer& server)
_server->on("/api/gridprofile/rawdata", HTTP_GET, std::bind(&WebApiGridProfileClass::onGridProfileRawdata, this, _1)); _server->on("/api/gridprofile/rawdata", HTTP_GET, std::bind(&WebApiGridProfileClass::onGridProfileRawdata, this, _1));
} }
void WebApiGridProfileClass::loop()
{
}
void WebApiGridProfileClass::onGridProfileStatus(AsyncWebServerRequest* request) void WebApiGridProfileClass::onGridProfileStatus(AsyncWebServerRequest* request)
{ {
if (!WebApi.checkCredentialsReadonly(request)) { if (!WebApi.checkCredentialsReadonly(request)) {
@ -91,4 +87,4 @@ void WebApiGridProfileClass::onGridProfileRawdata(AsyncWebServerRequest* request
response->setLength(); response->setLength();
request->send(response); request->send(response);
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_inverter.h" #include "WebApi_inverter.h"
#include "Configuration.h" #include "Configuration.h"
@ -12,7 +12,7 @@
#include <AsyncJson.h> #include <AsyncJson.h>
#include <Hoymiles.h> #include <Hoymiles.h>
void WebApiInverterClass::init(AsyncWebServer& server) void WebApiInverterClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
@ -25,10 +25,6 @@ void WebApiInverterClass::init(AsyncWebServer& server)
_server->on("/api/inverter/order", HTTP_POST, std::bind(&WebApiInverterClass::onInverterOrder, this, _1)); _server->on("/api/inverter/order", HTTP_POST, std::bind(&WebApiInverterClass::onInverterOrder, this, _1));
} }
void WebApiInverterClass::loop()
{
}
void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request) void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request)
{ {
if (!WebApi.checkCredentials(request)) { if (!WebApi.checkCredentials(request)) {
@ -460,4 +456,4 @@ void WebApiInverterClass::onInverterOrder(AsyncWebServerRequest* request)
response->setLength(); response->setLength();
request->send(response); request->send(response);
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_limit.h" #include "WebApi_limit.h"
#include "WebApi.h" #include "WebApi.h"
@ -10,7 +10,7 @@
#include <AsyncJson.h> #include <AsyncJson.h>
#include <Hoymiles.h> #include <Hoymiles.h>
void WebApiLimitClass::init(AsyncWebServer& server) void WebApiLimitClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
@ -20,10 +20,6 @@ void WebApiLimitClass::init(AsyncWebServer& server)
_server->on("/api/limit/config", HTTP_POST, std::bind(&WebApiLimitClass::onLimitPost, this, _1)); _server->on("/api/limit/config", HTTP_POST, std::bind(&WebApiLimitClass::onLimitPost, this, _1));
} }
void WebApiLimitClass::loop()
{
}
void WebApiLimitClass::onLimitStatus(AsyncWebServerRequest* request) void WebApiLimitClass::onLimitStatus(AsyncWebServerRequest* request)
{ {
if (!WebApi.checkCredentialsReadonly(request)) { if (!WebApi.checkCredentialsReadonly(request)) {
@ -156,4 +152,4 @@ void WebApiLimitClass::onLimitPost(AsyncWebServerRequest* request)
response->setLength(); response->setLength();
request->send(response); request->send(response);
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_maintenance.h" #include "WebApi_maintenance.h"
@ -9,7 +9,7 @@
#include "WebApi_errors.h" #include "WebApi_errors.h"
#include <AsyncJson.h> #include <AsyncJson.h>
void WebApiMaintenanceClass::init(AsyncWebServer& server) void WebApiMaintenanceClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
@ -18,10 +18,6 @@ void WebApiMaintenanceClass::init(AsyncWebServer& server)
_server->on("/api/maintenance/reboot", HTTP_POST, std::bind(&WebApiMaintenanceClass::onRebootPost, this, _1)); _server->on("/api/maintenance/reboot", HTTP_POST, std::bind(&WebApiMaintenanceClass::onRebootPost, this, _1));
} }
void WebApiMaintenanceClass::loop()
{
}
void WebApiMaintenanceClass::onRebootPost(AsyncWebServerRequest* request) void WebApiMaintenanceClass::onRebootPost(AsyncWebServerRequest* request)
{ {
if (!WebApi.checkCredentials(request)) { if (!WebApi.checkCredentials(request)) {
@ -84,4 +80,4 @@ void WebApiMaintenanceClass::onRebootPost(AsyncWebServerRequest* request)
response->setLength(); response->setLength();
request->send(response); request->send(response);
} }
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_mqtt.h" #include "WebApi_mqtt.h"
#include "Configuration.h" #include "Configuration.h"
@ -15,7 +15,7 @@
#include "PowerMeter.h" #include "PowerMeter.h"
#include <AsyncJson.h> #include <AsyncJson.h>
void WebApiMqttClass::init(AsyncWebServer& server) void WebApiMqttClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
@ -26,10 +26,6 @@ void WebApiMqttClass::init(AsyncWebServer& server)
_server->on("/api/mqtt/config", HTTP_POST, std::bind(&WebApiMqttClass::onMqttAdminPost, this, _1)); _server->on("/api/mqtt/config", HTTP_POST, std::bind(&WebApiMqttClass::onMqttAdminPost, this, _1));
} }
void WebApiMqttClass::loop()
{
}
void WebApiMqttClass::onMqttStatus(AsyncWebServerRequest* request) void WebApiMqttClass::onMqttStatus(AsyncWebServerRequest* request)
{ {
if (!WebApi.checkCredentialsReadonly(request)) { if (!WebApi.checkCredentialsReadonly(request)) {

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_network.h" #include "WebApi_network.h"
#include "Configuration.h" #include "Configuration.h"
@ -10,7 +10,7 @@
#include "helper.h" #include "helper.h"
#include <AsyncJson.h> #include <AsyncJson.h>
void WebApiNetworkClass::init(AsyncWebServer& server) void WebApiNetworkClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
@ -21,10 +21,6 @@ void WebApiNetworkClass::init(AsyncWebServer& server)
_server->on("/api/network/config", HTTP_POST, std::bind(&WebApiNetworkClass::onNetworkAdminPost, this, _1)); _server->on("/api/network/config", HTTP_POST, std::bind(&WebApiNetworkClass::onNetworkAdminPost, this, _1));
} }
void WebApiNetworkClass::loop()
{
}
void WebApiNetworkClass::onNetworkStatus(AsyncWebServerRequest* request) void WebApiNetworkClass::onNetworkStatus(AsyncWebServerRequest* request)
{ {
if (!WebApi.checkCredentialsReadonly(request)) { if (!WebApi.checkCredentialsReadonly(request)) {
@ -246,4 +242,4 @@ void WebApiNetworkClass::onNetworkAdminPost(AsyncWebServerRequest* request)
NetworkSettings.enableAdminMode(); NetworkSettings.enableAdminMode();
NetworkSettings.applyConfig(); NetworkSettings.applyConfig();
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_ntp.h" #include "WebApi_ntp.h"
#include "Configuration.h" #include "Configuration.h"
@ -11,7 +11,7 @@
#include "helper.h" #include "helper.h"
#include <AsyncJson.h> #include <AsyncJson.h>
void WebApiNtpClass::init(AsyncWebServer& server) void WebApiNtpClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
@ -24,10 +24,6 @@ void WebApiNtpClass::init(AsyncWebServer& server)
_server->on("/api/ntp/time", HTTP_POST, std::bind(&WebApiNtpClass::onNtpTimePost, this, _1)); _server->on("/api/ntp/time", HTTP_POST, std::bind(&WebApiNtpClass::onNtpTimePost, this, _1));
} }
void WebApiNtpClass::loop()
{
}
void WebApiNtpClass::onNtpStatus(AsyncWebServerRequest* request) void WebApiNtpClass::onNtpStatus(AsyncWebServerRequest* request)
{ {
if (!WebApi.checkCredentialsReadonly(request)) { if (!WebApi.checkCredentialsReadonly(request)) {

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_power.h" #include "WebApi_power.h"
#include "WebApi.h" #include "WebApi.h"
@ -8,7 +8,7 @@
#include <AsyncJson.h> #include <AsyncJson.h>
#include <Hoymiles.h> #include <Hoymiles.h>
void WebApiPowerClass::init(AsyncWebServer& server) void WebApiPowerClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
@ -18,10 +18,6 @@ void WebApiPowerClass::init(AsyncWebServer& server)
_server->on("/api/power/config", HTTP_POST, std::bind(&WebApiPowerClass::onPowerPost, this, _1)); _server->on("/api/power/config", HTTP_POST, std::bind(&WebApiPowerClass::onPowerPost, this, _1));
} }
void WebApiPowerClass::loop()
{
}
void WebApiPowerClass::onPowerStatus(AsyncWebServerRequest* request) void WebApiPowerClass::onPowerStatus(AsyncWebServerRequest* request)
{ {
if (!WebApi.checkCredentialsReadonly(request)) { if (!WebApi.checkCredentialsReadonly(request)) {
@ -132,4 +128,4 @@ void WebApiPowerClass::onPowerPost(AsyncWebServerRequest* request)
response->setLength(); response->setLength();
request->send(response); request->send(response);
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_powerlimiter.h" #include "WebApi_powerlimiter.h"
#include "VeDirectFrameHandler.h" #include "VeDirectFrameHandler.h"
@ -16,7 +16,7 @@
#include "helper.h" #include "helper.h"
#include "WebApi_errors.h" #include "WebApi_errors.h"
void WebApiPowerLimiterClass::init(AsyncWebServer& server) void WebApiPowerLimiterClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
@ -27,10 +27,6 @@ void WebApiPowerLimiterClass::init(AsyncWebServer& server)
_server->on("/api/powerlimiter/config", HTTP_POST, std::bind(&WebApiPowerLimiterClass::onAdminPost, this, _1)); _server->on("/api/powerlimiter/config", HTTP_POST, std::bind(&WebApiPowerLimiterClass::onAdminPost, this, _1));
} }
void WebApiPowerLimiterClass::loop()
{
}
void WebApiPowerLimiterClass::onStatus(AsyncWebServerRequest* request) void WebApiPowerLimiterClass::onStatus(AsyncWebServerRequest* request)
{ {
AsyncJsonResponse* response = new AsyncJsonResponse(); AsyncJsonResponse* response = new AsyncJsonResponse();

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_powermeter.h" #include "WebApi_powermeter.h"
#include "VeDirectFrameHandler.h" #include "VeDirectFrameHandler.h"
@ -16,7 +16,7 @@
#include "WebApi.h" #include "WebApi.h"
#include "helper.h" #include "helper.h"
void WebApiPowerMeterClass::init(AsyncWebServer& server) void WebApiPowerMeterClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
@ -28,10 +28,6 @@ void WebApiPowerMeterClass::init(AsyncWebServer& server)
_server->on("/api/powermeter/testhttprequest", HTTP_POST, std::bind(&WebApiPowerMeterClass::onTestHttpRequest, this, _1)); _server->on("/api/powermeter/testhttprequest", HTTP_POST, std::bind(&WebApiPowerMeterClass::onTestHttpRequest, this, _1));
} }
void WebApiPowerMeterClass::loop()
{
}
void WebApiPowerMeterClass::onStatus(AsyncWebServerRequest* request) void WebApiPowerMeterClass::onStatus(AsyncWebServerRequest* request)
{ {
AsyncJsonResponse* response = new AsyncJsonResponse(false, 2048); AsyncJsonResponse* response = new AsyncJsonResponse(false, 2048);

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_prometheus.h" #include "WebApi_prometheus.h"
#include "Configuration.h" #include "Configuration.h"
@ -11,7 +11,7 @@
#include <Hoymiles.h> #include <Hoymiles.h>
#include "MessageOutput.h" #include "MessageOutput.h"
void WebApiPrometheusClass::init(AsyncWebServer& server) void WebApiPrometheusClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
@ -20,10 +20,6 @@ void WebApiPrometheusClass::init(AsyncWebServer& server)
_server->on("/api/prometheus/metrics", HTTP_GET, std::bind(&WebApiPrometheusClass::onPrometheusMetricsGet, this, _1)); _server->on("/api/prometheus/metrics", HTTP_GET, std::bind(&WebApiPrometheusClass::onPrometheusMetricsGet, this, _1));
} }
void WebApiPrometheusClass::loop()
{
}
void WebApiPrometheusClass::onPrometheusMetricsGet(AsyncWebServerRequest* request) void WebApiPrometheusClass::onPrometheusMetricsGet(AsyncWebServerRequest* request)
{ {
if (!WebApi.checkCredentialsReadonly(request)) { if (!WebApi.checkCredentialsReadonly(request)) {

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_security.h" #include "WebApi_security.h"
#include "Configuration.h" #include "Configuration.h"
@ -9,7 +9,7 @@
#include "helper.h" #include "helper.h"
#include <AsyncJson.h> #include <AsyncJson.h>
void WebApiSecurityClass::init(AsyncWebServer& server) void WebApiSecurityClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
@ -20,10 +20,6 @@ void WebApiSecurityClass::init(AsyncWebServer& server)
_server->on("/api/security/authenticate", HTTP_GET, std::bind(&WebApiSecurityClass::onAuthenticateGet, this, _1)); _server->on("/api/security/authenticate", HTTP_GET, std::bind(&WebApiSecurityClass::onAuthenticateGet, this, _1));
} }
void WebApiSecurityClass::loop()
{
}
void WebApiSecurityClass::onSecurityGet(AsyncWebServerRequest* request) void WebApiSecurityClass::onSecurityGet(AsyncWebServerRequest* request)
{ {
if (!WebApi.checkCredentials(request)) { if (!WebApi.checkCredentials(request)) {
@ -122,4 +118,4 @@ void WebApiSecurityClass::onAuthenticateGet(AsyncWebServerRequest* request)
response->setLength(); response->setLength();
request->send(response); request->send(response);
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_sysstatus.h" #include "WebApi_sysstatus.h"
#include "Configuration.h" #include "Configuration.h"
@ -20,7 +20,7 @@
#define AUTO_GIT_BRANCH "" #define AUTO_GIT_BRANCH ""
#endif #endif
void WebApiSysstatusClass::init(AsyncWebServer& server) void WebApiSysstatusClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
@ -29,10 +29,6 @@ void WebApiSysstatusClass::init(AsyncWebServer& server)
_server->on("/api/system/status", HTTP_GET, std::bind(&WebApiSysstatusClass::onSystemStatus, this, _1)); _server->on("/api/system/status", HTTP_GET, std::bind(&WebApiSysstatusClass::onSystemStatus, this, _1));
} }
void WebApiSysstatusClass::loop()
{
}
void WebApiSysstatusClass::onSystemStatus(AsyncWebServerRequest* request) void WebApiSysstatusClass::onSystemStatus(AsyncWebServerRequest* request)
{ {
if (!WebApi.checkCredentialsReadonly(request)) { if (!WebApi.checkCredentialsReadonly(request)) {
@ -87,4 +83,4 @@ void WebApiSysstatusClass::onSystemStatus(AsyncWebServerRequest* request)
response->setLength(); response->setLength();
request->send(response); request->send(response);
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_vedirect.h" #include "WebApi_vedirect.h"
#include "VictronMppt.h" #include "VictronMppt.h"
@ -11,7 +11,7 @@
#include "WebApi_errors.h" #include "WebApi_errors.h"
#include "helper.h" #include "helper.h"
void WebApiVedirectClass::init(AsyncWebServer& server) void WebApiVedirectClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
@ -22,10 +22,6 @@ void WebApiVedirectClass::init(AsyncWebServer& server)
_server->on("/api/vedirect/config", HTTP_POST, std::bind(&WebApiVedirectClass::onVedirectAdminPost, this, _1)); _server->on("/api/vedirect/config", HTTP_POST, std::bind(&WebApiVedirectClass::onVedirectAdminPost, this, _1));
} }
void WebApiVedirectClass::loop()
{
}
void WebApiVedirectClass::onVedirectStatus(AsyncWebServerRequest* request) void WebApiVedirectClass::onVedirectStatus(AsyncWebServerRequest* request)
{ {
if (!WebApi.checkCredentialsReadonly(request)) { if (!WebApi.checkCredentialsReadonly(request)) {

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_webapp.h" #include "WebApi_webapp.h"
@ -22,7 +22,7 @@ extern const uint8_t file_site_webmanifest_end[] asm("_binary_webapp_dist_site_w
#define ETAG_HTTP_HEADER_VAL "\"" AUTO_GIT_HASH "\"" // ETag value must be between quotes #define ETAG_HTTP_HEADER_VAL "\"" AUTO_GIT_HASH "\"" // ETag value must be between quotes
#endif #endif
void WebApiWebappClass::init(AsyncWebServer& server) void WebApiWebappClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
_server = &server; _server = &server;
@ -94,7 +94,3 @@ void WebApiWebappClass::init(AsyncWebServer& server)
request->send(response); request->send(response);
}); });
} }
void WebApiWebappClass::loop()
{
}

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_ws_Huawei.h" #include "WebApi_ws_Huawei.h"
#include "AsyncJson.h" #include "AsyncJson.h"
@ -16,7 +16,7 @@ WebApiWsHuaweiLiveClass::WebApiWsHuaweiLiveClass()
{ {
} }
void WebApiWsHuaweiLiveClass::init(AsyncWebServer& server) void WebApiWsHuaweiLiveClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
using std::placeholders::_2; using std::placeholders::_2;
@ -30,26 +30,33 @@ void WebApiWsHuaweiLiveClass::init(AsyncWebServer& server)
_server->addHandler(&_ws); _server->addHandler(&_ws);
_ws.onEvent(std::bind(&WebApiWsHuaweiLiveClass::onWebsocketEvent, this, _1, _2, _3, _4, _5, _6)); _ws.onEvent(std::bind(&WebApiWsHuaweiLiveClass::onWebsocketEvent, this, _1, _2, _3, _4, _5, _6));
scheduler.addTask(_wsCleanupTask);
_wsCleanupTask.setCallback(std::bind(&WebApiWsHuaweiLiveClass::wsCleanupTaskCb, this));
_wsCleanupTask.setIterations(TASK_FOREVER);
_wsCleanupTask.setInterval(1 * TASK_SECOND);
_wsCleanupTask.enable();
scheduler.addTask(_sendDataTask);
_sendDataTask.setCallback(std::bind(&WebApiWsHuaweiLiveClass::sendDataTaskCb, this));
_sendDataTask.setIterations(TASK_FOREVER);
_sendDataTask.setInterval(1 * TASK_SECOND);
_sendDataTask.enable();
} }
void WebApiWsHuaweiLiveClass::loop() void WebApiWsHuaweiLiveClass::wsCleanupTaskCb()
{ {
// see: https://github.com/me-no-dev/ESPAsyncWebServer#limiting-the-number-of-web-socket-clients // see: https://github.com/me-no-dev/ESPAsyncWebServer#limiting-the-number-of-web-socket-clients
if (millis() - _lastWsCleanup > 1000) { _ws.cleanupClients();
_ws.cleanupClients(); }
_lastWsCleanup = millis();
}
void WebApiWsHuaweiLiveClass::sendDataTaskCb()
{
// do nothing if no WS client is connected // do nothing if no WS client is connected
if (_ws.count() == 0) { if (_ws.count() == 0) {
return; return;
} }
if (millis() - _lastUpdateCheck < 1000) {
return;
}
_lastUpdateCheck = millis();
try { try {
std::lock_guard<std::mutex> lock(_mutex); std::lock_guard<std::mutex> lock(_mutex);
DynamicJsonDocument root(1024); DynamicJsonDocument root(1024);

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_ws_battery.h" #include "WebApi_ws_battery.h"
#include "AsyncJson.h" #include "AsyncJson.h"
@ -16,7 +16,7 @@ WebApiWsBatteryLiveClass::WebApiWsBatteryLiveClass()
{ {
} }
void WebApiWsBatteryLiveClass::init(AsyncWebServer& server) void WebApiWsBatteryLiveClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
using std::placeholders::_2; using std::placeholders::_2;
@ -30,16 +30,28 @@ void WebApiWsBatteryLiveClass::init(AsyncWebServer& server)
_server->addHandler(&_ws); _server->addHandler(&_ws);
_ws.onEvent(std::bind(&WebApiWsBatteryLiveClass::onWebsocketEvent, this, _1, _2, _3, _4, _5, _6)); _ws.onEvent(std::bind(&WebApiWsBatteryLiveClass::onWebsocketEvent, this, _1, _2, _3, _4, _5, _6));
scheduler.addTask(_wsCleanupTask);
_wsCleanupTask.setCallback(std::bind(&WebApiWsBatteryLiveClass::wsCleanupTaskCb, this));
_wsCleanupTask.setIterations(TASK_FOREVER);
_wsCleanupTask.setInterval(1 * TASK_SECOND);
_wsCleanupTask.enable();
scheduler.addTask(_sendDataTask);
_sendDataTask.setCallback(std::bind(&WebApiWsBatteryLiveClass::sendDataTaskCb, this));
_sendDataTask.setIterations(TASK_FOREVER);
_sendDataTask.setInterval(1 * TASK_SECOND);
_sendDataTask.enable();
} }
void WebApiWsBatteryLiveClass::loop() void WebApiWsBatteryLiveClass::wsCleanupTaskCb()
{ {
// see: https://github.com/me-no-dev/ESPAsyncWebServer#limiting-the-number-of-web-socket-clients // see: https://github.com/me-no-dev/ESPAsyncWebServer#limiting-the-number-of-web-socket-clients
if (millis() - _lastWsCleanup > 1000) { _ws.cleanupClients();
_ws.cleanupClients(); }
_lastWsCleanup = millis();
}
void WebApiWsBatteryLiveClass::sendDataTaskCb()
{
// do nothing if no WS client is connected // do nothing if no WS client is connected
if (_ws.count() == 0) { if (_ws.count() == 0) {
return; return;

View File

@ -1,37 +1,37 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_ws_console.h" #include "WebApi_ws_console.h"
#include "Configuration.h" #include "Configuration.h"
#include "MessageOutput.h" #include "MessageOutput.h"
#include "WebApi.h" #include "WebApi.h"
#include "defaults.h" #include "defaults.h"
WebApiWsConsoleClass::WebApiWsConsoleClass() WebApiWsConsoleClass::WebApiWsConsoleClass()
: _ws("/console") : _ws("/console")
{ , _wsCleanupTask(1 * TASK_SECOND, TASK_FOREVER, std::bind(&WebApiWsConsoleClass::wsCleanupTaskCb, this))
} {
}
void WebApiWsConsoleClass::init(AsyncWebServer& server)
{ void WebApiWsConsoleClass::init(AsyncWebServer& server, Scheduler& scheduler)
_server = &server; {
_server->addHandler(&_ws); _server = &server;
MessageOutput.register_ws_output(&_ws); _server->addHandler(&_ws);
} MessageOutput.register_ws_output(&_ws);
void WebApiWsConsoleClass::loop() scheduler.addTask(_wsCleanupTask);
{ _wsCleanupTask.enable();
// see: https://github.com/me-no-dev/ESPAsyncWebServer#limiting-the-number-of-web-socket-clients }
if (millis() - _lastWsCleanup > 1000) {
_ws.cleanupClients(); void WebApiWsConsoleClass::wsCleanupTaskCb()
{
if (Configuration.get().Security.AllowReadonly) { // see: https://github.com/me-no-dev/ESPAsyncWebServer#limiting-the-number-of-web-socket-clients
_ws.setAuthentication("", ""); _ws.cleanupClients();
} else {
_ws.setAuthentication(AUTH_USERNAME, Configuration.get().Security.Password); if (Configuration.get().Security.AllowReadonly) {
} _ws.setAuthentication("", "");
} else {
_lastWsCleanup = millis(); _ws.setAuthentication(AUTH_USERNAME, Configuration.get().Security.Password);
} }
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_ws_live.h" #include "WebApi_ws_live.h"
#include "Configuration.h" #include "Configuration.h"
@ -17,10 +17,12 @@
WebApiWsLiveClass::WebApiWsLiveClass() WebApiWsLiveClass::WebApiWsLiveClass()
: _ws("/livedata") : _ws("/livedata")
, _wsCleanupTask(1 * TASK_SECOND, TASK_FOREVER, std::bind(&WebApiWsLiveClass::wsCleanupTaskCb, this))
, _sendDataTask(1 * TASK_SECOND, TASK_FOREVER, std::bind(&WebApiWsLiveClass::sendDataTaskCb, this))
{ {
} }
void WebApiWsLiveClass::init(AsyncWebServer& server) void WebApiWsLiveClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
using std::placeholders::_2; using std::placeholders::_2;
@ -34,33 +36,37 @@ void WebApiWsLiveClass::init(AsyncWebServer& server)
_server->addHandler(&_ws); _server->addHandler(&_ws);
_ws.onEvent(std::bind(&WebApiWsLiveClass::onWebsocketEvent, this, _1, _2, _3, _4, _5, _6)); _ws.onEvent(std::bind(&WebApiWsLiveClass::onWebsocketEvent, this, _1, _2, _3, _4, _5, _6));
scheduler.addTask(_wsCleanupTask);
_wsCleanupTask.enable();
scheduler.addTask(_sendDataTask);
_sendDataTask.enable();
} }
void WebApiWsLiveClass::loop() void WebApiWsLiveClass::wsCleanupTaskCb()
{ {
// see: https://github.com/me-no-dev/ESPAsyncWebServer#limiting-the-number-of-web-socket-clients // see: https://github.com/me-no-dev/ESPAsyncWebServer#limiting-the-number-of-web-socket-clients
if (millis() - _lastWsCleanup > 1000) { _ws.cleanupClients();
_ws.cleanupClients();
_lastWsCleanup = millis();
}
if (Configuration.get().Security.AllowReadonly) {
_ws.setAuthentication("", "");
} else {
_ws.setAuthentication(AUTH_USERNAME, Configuration.get().Security.Password);
}
}
void WebApiWsLiveClass::sendDataTaskCb()
{
// do nothing if no WS client is connected // do nothing if no WS client is connected
if (_ws.count() == 0) { if (_ws.count() == 0) {
return; return;
} }
if (millis() - _lastInvUpdateCheck < 1000) {
return;
}
_lastInvUpdateCheck = millis();
uint32_t maxTimeStamp = 0; uint32_t maxTimeStamp = 0;
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);
maxTimeStamp = std::max<uint32_t>(maxTimeStamp, inv->Statistics()->getLastUpdate());
if (inv->Statistics()->getLastUpdate() > maxTimeStamp) {
maxTimeStamp = inv->Statistics()->getLastUpdate();
}
} }
// Update on every inverter change or at least after 10 seconds // Update on every inverter change or at least after 10 seconds
@ -76,13 +82,8 @@ void WebApiWsLiveClass::loop()
String buffer; String buffer;
serializeJson(root, buffer); serializeJson(root, buffer);
if (Configuration.get().Security.AllowReadonly) {
_ws.setAuthentication("", "");
} else {
_ws.setAuthentication(AUTH_USERNAME, Configuration.get().Security.Password);
}
_ws.textAll(buffer); _ws.textAll(buffer);
_newestInverterTimestamp = maxTimeStamp;
} }
} catch (const std::bad_alloc& bad_alloc) { } catch (const std::bad_alloc& bad_alloc) {
@ -133,25 +134,25 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
if (t == TYPE_DC) { if (t == TYPE_DC) {
chanTypeObj[String(static_cast<uint8_t>(c))]["name"]["u"] = inv_cfg->channel[c].Name; chanTypeObj[String(static_cast<uint8_t>(c))]["name"]["u"] = inv_cfg->channel[c].Name;
} }
addField(chanTypeObj, i, inv, t, c, FLD_PAC); addField(chanTypeObj, inv, t, c, FLD_PAC);
addField(chanTypeObj, i, inv, t, c, FLD_UAC); addField(chanTypeObj, inv, t, c, FLD_UAC);
addField(chanTypeObj, i, inv, t, c, FLD_IAC); addField(chanTypeObj, inv, t, c, FLD_IAC);
if (t == TYPE_AC) { if (t == TYPE_AC) {
addField(chanTypeObj, i, inv, t, c, FLD_PDC, "Power DC"); addField(chanTypeObj, inv, t, c, FLD_PDC, "Power DC");
} else { } else {
addField(chanTypeObj, i, inv, t, c, FLD_PDC); addField(chanTypeObj, inv, t, c, FLD_PDC);
} }
addField(chanTypeObj, i, inv, t, c, FLD_UDC); addField(chanTypeObj, inv, t, c, FLD_UDC);
addField(chanTypeObj, i, inv, t, c, FLD_IDC); addField(chanTypeObj, inv, t, c, FLD_IDC);
addField(chanTypeObj, i, inv, t, c, FLD_YD); addField(chanTypeObj, inv, t, c, FLD_YD);
addField(chanTypeObj, i, inv, t, c, FLD_YT); addField(chanTypeObj, inv, t, c, FLD_YT);
addField(chanTypeObj, i, inv, t, c, FLD_F); addField(chanTypeObj, inv, t, c, FLD_F);
addField(chanTypeObj, i, inv, t, c, FLD_T); addField(chanTypeObj, inv, t, c, FLD_T);
addField(chanTypeObj, i, inv, t, c, FLD_PF); addField(chanTypeObj, inv, t, c, FLD_PF);
addField(chanTypeObj, i, inv, t, c, FLD_Q); addField(chanTypeObj, inv, t, c, FLD_Q);
addField(chanTypeObj, i, inv, t, c, FLD_EFF); addField(chanTypeObj, inv, t, c, FLD_EFF);
if (t == TYPE_DC && inv->Statistics()->getStringMaxPower(c) > 0) { if (t == TYPE_DC && inv->Statistics()->getStringMaxPower(c) > 0) {
addField(chanTypeObj, i, inv, t, c, FLD_IRR); addField(chanTypeObj, inv, t, c, FLD_IRR);
chanTypeObj[String(c)][inv->Statistics()->getChannelFieldName(t, c, FLD_IRR)]["max"] = inv->Statistics()->getStringMaxPower(c); chanTypeObj[String(c)][inv->Statistics()->getChannelFieldName(t, c, FLD_IRR)]["max"] = inv->Statistics()->getStringMaxPower(c);
} }
} }
@ -162,10 +163,6 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
} else { } else {
invObject["events"] = -1; invObject["events"] = -1;
} }
if (inv->Statistics()->getLastUpdate() > _newestInverterTimestamp) {
_newestInverterTimestamp = inv->Statistics()->getLastUpdate();
}
} }
JsonObject totalObj = root.createNestedObject("total"); JsonObject totalObj = root.createNestedObject("total");
@ -206,7 +203,7 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
} }
void WebApiWsLiveClass::addField(JsonObject& root, uint8_t idx, std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId, String topic) void WebApiWsLiveClass::addField(JsonObject& root, std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId, String topic)
{ {
if (inv->Statistics()->hasChannelFieldValue(type, channel, fieldId)) { if (inv->Statistics()->hasChannelFieldValue(type, channel, fieldId)) {
String chanName; String chanName;
@ -262,4 +259,4 @@ void WebApiWsLiveClass::onLivedataStatus(AsyncWebServerRequest* request)
MessageOutput.printf("Unknown exception in /api/livedata/status. Reason: \"%s\".\r\n", exc.what()); MessageOutput.printf("Unknown exception in /api/livedata/status. Reason: \"%s\".\r\n", exc.what());
WebApi.sendTooManyRequests(request); WebApi.sendTooManyRequests(request);
} }
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_ws_vedirect_live.h" #include "WebApi_ws_vedirect_live.h"
#include "AsyncJson.h" #include "AsyncJson.h"
@ -17,7 +17,7 @@ WebApiWsVedirectLiveClass::WebApiWsVedirectLiveClass()
{ {
} }
void WebApiWsVedirectLiveClass::init(AsyncWebServer& server) void WebApiWsVedirectLiveClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; using std::placeholders::_1;
using std::placeholders::_2; using std::placeholders::_2;
@ -31,16 +31,29 @@ void WebApiWsVedirectLiveClass::init(AsyncWebServer& server)
_server->addHandler(&_ws); _server->addHandler(&_ws);
_ws.onEvent(std::bind(&WebApiWsVedirectLiveClass::onWebsocketEvent, this, _1, _2, _3, _4, _5, _6)); _ws.onEvent(std::bind(&WebApiWsVedirectLiveClass::onWebsocketEvent, this, _1, _2, _3, _4, _5, _6));
scheduler.addTask(_wsCleanupTask);
_wsCleanupTask.setCallback(std::bind(&WebApiWsVedirectLiveClass::wsCleanupTaskCb, this));
_wsCleanupTask.setIterations(TASK_FOREVER);
_wsCleanupTask.setInterval(1 * TASK_SECOND);
_wsCleanupTask.enable();
scheduler.addTask(_sendDataTask);
_sendDataTask.setCallback(std::bind(&WebApiWsVedirectLiveClass::sendDataTaskCb, this));
_sendDataTask.setIterations(TASK_FOREVER);
_sendDataTask.setInterval(1 * TASK_SECOND);
_sendDataTask.enable();
} }
void WebApiWsVedirectLiveClass::loop() void WebApiWsVedirectLiveClass::wsCleanupTaskCb()
{ {
// see: https://github.com/me-no-dev/ESPAsyncWebServer#limiting-the-number-of-web-socket-clients // see: https://github.com/me-no-dev/ESPAsyncWebServer#limiting-the-number-of-web-socket-clients
if (millis() - _lastWsCleanup > 1000) { _ws.cleanupClients();
_ws.cleanupClients(); }
_lastWsCleanup = millis();
}
void WebApiWsVedirectLiveClass::sendDataTaskCb()
{
// do nothing if no WS client is connected // do nothing if no WS client is connected
if (_ws.count() == 0) { if (_ws.count() == 0) {
return; return;

View File

@ -16,18 +16,18 @@
"bootstrap": "^5.3.2", "bootstrap": "^5.3.2",
"bootstrap-icons-vue": "^1.11.1", "bootstrap-icons-vue": "^1.11.1",
"mitt": "^3.0.1", "mitt": "^3.0.1",
"sortablejs": "^1.15.1", "sortablejs": "^1.15.2",
"spark-md5": "^3.0.2", "spark-md5": "^3.0.2",
"vue": "^3.4.13", "vue": "^3.4.15",
"vue-i18n": "^9.9.0", "vue-i18n": "^9.9.0",
"vue-router": "^4.2.5" "vue-router": "^4.2.5"
}, },
"devDependencies": { "devDependencies": {
"@intlify/unplugin-vue-i18n": "^2.0.0", "@intlify/unplugin-vue-i18n": "^2.0.0",
"@rushstack/eslint-patch": "^1.6.1", "@rushstack/eslint-patch": "^1.7.0",
"@tsconfig/node18": "^18.2.2", "@tsconfig/node18": "^18.2.2",
"@types/bootstrap": "^5.2.10", "@types/bootstrap": "^5.2.10",
"@types/node": "^20.11.0", "@types/node": "^20.11.5",
"@types/pulltorefreshjs": "^0.1.7", "@types/pulltorefreshjs": "^0.1.7",
"@types/sortablejs": "^1.15.7", "@types/sortablejs": "^1.15.7",
"@types/spark-md5": "^3.0.4", "@types/spark-md5": "^3.0.4",
@ -38,10 +38,10 @@
"eslint-plugin-vue": "^9.20.1", "eslint-plugin-vue": "^9.20.1",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"pulltorefreshjs": "^0.1.22", "pulltorefreshjs": "^0.1.22",
"sass": "^1.69.7", "sass": "^1.70.0",
"terser": "^5.26.0", "terser": "^5.27.0",
"typescript": "^5.3.3", "typescript": "^5.3.3",
"vite": "^5.0.11", "vite": "^5.0.12",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-css-injected-by-js": "^3.3.1", "vite-plugin-css-injected-by-js": "^3.3.1",
"vue-tsc": "^1.8.27" "vue-tsc": "^1.8.27"

View File

@ -1,8 +1,6 @@
<template> <template>
<NavBar /> <NavBar />
<main class="container-fluid"> <router-view />
<router-view />
</main>
</template> </template>
<script lang="ts"> <script lang="ts">

View File

@ -51,7 +51,7 @@ export default defineComponent({
var self = this; var self = this;
console.log("init"); console.log("init");
PullToRefresh.init({ PullToRefresh.init({
mainElement: 'main', // above which element? mainElement: 'body', // above which element?
instructionsPullToRefresh: this.$t('base.Pull'), instructionsPullToRefresh: this.$t('base.Pull'),
instructionsReleaseToRefresh: this.$t('base.Release'), instructionsReleaseToRefresh: this.$t('base.Release'),
instructionsRefreshing: this.$t('base.Refreshing'), instructionsRefreshing: this.$t('base.Refreshing'),
@ -120,4 +120,4 @@ export default defineComponent({
opacity: 0; opacity: 0;
} }
} }
</style> </style>

View File

@ -1,7 +1,7 @@
<template> <template>
<div :class="['card', addSpace ? 'mt-5' : '' ]"> <div :class="['card', addSpace ? 'mt-5' : '' ]">
<div :class="['card-header', textVariant]">{{ text }}</div> <div :class="['card-header', textVariant]">{{ text }}</div>
<div :class="['card-body', centerContent ? 'text-center' : '']"> <div :class="['card-body', 'card-text', centerContent ? 'text-center' : '']">
<slot /> <slot />
</div> </div>
</div> </div>
@ -18,4 +18,4 @@ export default defineComponent({
'centerContent': Boolean, 'centerContent': Boolean,
}, },
}); });
</script> </script>

View File

@ -31,7 +31,7 @@ export default defineComponent({
computed: { computed: {
timeInHours() { timeInHours() {
return (value: number) => { return (value: number) => {
return timestampToString(value); return timestampToString(this.$i18n.locale, value)[0];
}; };
}, },
}, },

View File

@ -32,12 +32,19 @@
</tr> </tr>
<tr> <tr>
<th>{{ $t('firmwareinfo.FirmwareUpdate') }}</th> <th>{{ $t('firmwareinfo.FirmwareUpdate') }}</th>
<td><a :href="systemStatus.update_url" target="_blank" v-tooltip <td v-if="modelAllowVersionInfo">
<a :href="systemStatus.update_url" target="_blank" v-tooltip
:title="$t('firmwareinfo.FirmwareUpdateHint')"> :title="$t('firmwareinfo.FirmwareUpdateHint')">
<span class="badge" :class="systemStatus.update_status"> <span class="badge" :class="systemStatus.update_status">
{{ systemStatus.update_text }} {{ systemStatus.update_text }}
</span> </span>
</a></td> </a>
</td>
<td v-else>
<div class="form-check form-switch">
<input v-model="modelAllowVersionInfo" class="form-check-input" type="checkbox" role="switch" v-tooltip :title="$t('firmwareinfo.FrmwareUpdateAllow')" />
</div>
</td>
</tr> </tr>
<tr> <tr>
<th>{{ $t('firmwareinfo.ResetReason0') }}</th> <th>{{ $t('firmwareinfo.ResetReason0') }}</th>
@ -53,7 +60,7 @@
</tr> </tr>
<tr> <tr>
<th>{{ $t('firmwareinfo.Uptime') }}</th> <th>{{ $t('firmwareinfo.Uptime') }}</th>
<td>{{ timeInHours(systemStatus.uptime) }}</td> <td>{{ $t('firmwareinfo.UptimeValue', timeInHours(systemStatus.uptime)) }}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -73,11 +80,21 @@ export default defineComponent({
}, },
props: { props: {
systemStatus: { type: Object as PropType<SystemStatus>, required: true }, systemStatus: { type: Object as PropType<SystemStatus>, required: true },
allowVersionInfo: Boolean,
}, },
computed: { computed: {
modelAllowVersionInfo: {
get(): any {
return !!this.allowVersionInfo;
},
set(value: any) {
this.$emit('update:allowVersionInfo', value);
},
},
timeInHours() { timeInHours() {
return (value: number) => { return (value: number) => {
return timestampToString(value, true); const [count, time] = timestampToString(this.$i18n.locale, value, true);
return {count, time};
}; };
}, },
versionInfoUrl(): string { versionInfoUrl(): string {

View File

@ -33,9 +33,9 @@
<tr v-for="value in section.items" :key="value.n"> <tr v-for="value in section.items" :key="value.n">
<th>{{ value.n }}</th> <th>{{ value.n }}</th>
<td> <td>
<tempplate v-if="value.u!='bool'"> <template v-if="value.u!='bool'">
{{ $n(value.v, 'decimal') }} {{ value.u }} {{ $n(value.v, 'decimal') }} {{ value.u }}
</tempplate> </template>
<template v-else> <template v-else>
<StatusBadge :status="value.v==1" true_text="gridprofile.Enabled" false_text="gridprofile.Disabled"/> <StatusBadge :status="value.v==1" true_text="gridprofile.Enabled" false_text="gridprofile.Disabled"/>
</template> </template>
@ -109,4 +109,4 @@ export default defineComponent({
}, },
}, },
}); });
</script> </script>

View File

@ -2,7 +2,7 @@
<div class="card" :class="{ <div class="card" :class="{
'border-info': channelType == 'AC', 'border-info': channelType == 'AC',
'border-secondary': channelType == 'INV' 'border-secondary': channelType == 'INV'
}"> }" style="overflow: hidden">
<div v-if="channelType == 'INV'" class="card-header text-bg-secondary"> <div v-if="channelType == 'INV'" class="card-header text-bg-secondary">
{{ $t('inverterchannelinfo.General') }} {{ $t('inverterchannelinfo.General') }}
</div> </div>
@ -16,32 +16,24 @@
{{ $t('inverterchannelinfo.Phase', { num: channelNumber + 1 }) }} {{ $t('inverterchannelinfo.Phase', { num: channelNumber + 1 }) }}
</div> </div>
<div class="card-body"> <div class="table-responsive">
<div class="table-responsive"> <table class="table table-striped table-hover" style="margin: 0">
<table class="table table-striped table-hover"> <tbody>
<thead> <tr v-for="(property, key) in channelData" :key="`prop-${key}`">
<tr> <template v-if="key != 'name' && property">
<th scope="col">{{ $t('inverterchannelinfo.Property') }}</th> <th scope="row">{{ $t('inverterchannelproperty.' + key) }}</th>
<th style="text-align: right" scope="col">{{ $t('inverterchannelinfo.Value') }}</th> <td style="text-align: right; padding-right: 0;">
<th scope="col">{{ $t('inverterchannelinfo.Unit') }}</th> {{ $n(property.v, 'decimal', {
</tr> minimumFractionDigits: property.d,
</thead> maximumFractionDigits: property.d
<tbody> })
<tr v-for="(property, key) in channelData" :key="`prop-${key}`"> }}
<template v-if="key != 'name' && property"> </td>
<th scope="row">{{ $t('inverterchannelproperty.' + key) }}</th> <td>{{ property.u }}</td>
<td style="text-align: right"> </template>
{{ $n(property.v, 'decimal', { </tr>
minimumFractionDigits: property.d, </tbody>
maximumFractionDigits: property.d}) </table>
}}
</td>
<td>{{ property.u }}</td>
</template>
</tr>
</tbody>
</table>
</div>
</div> </div>
</div> </div>
</template> </template>

View File

@ -1,137 +1,110 @@
<template> <template>
<div v-show="totalVeData.enabled"> <div v-show="totalVeData.enabled">
<div class="row row-cols-1 row-cols-md-3 g-3"> <div class="row row-cols-1 row-cols-md-3 g-3">
<div class="col"> <div class="col">
<div class="card"> <CardElement centerContent textVariant="text-bg-success" :text="$t('invertertotalinfo.MpptTotalYieldTotal')">
<div class="card-header text-bg-success">{{ $t('invertertotalinfo.MpptTotalYieldTotal') }}</div> <h2>
<div class="card-body card-text text-center"> {{ $n(totalVeData.total.YieldTotal.v, 'decimal', {
<h2> minimumFractionDigits: totalVeData.total.YieldTotal.d,
{{ $n(totalVeData.total.YieldTotal.v, 'decimal', { maximumFractionDigits:totalVeData.total.YieldTotal.d
minimumFractionDigits: totalVeData.total.YieldTotal.d, }) }}
maximumFractionDigits: totalVeData.total.YieldTotal.d <small class="text-muted">{{ totalVeData.total.YieldTotal.u }}</small>
}) }} </h2>
<small class="text-muted">{{ totalVeData.total.YieldTotal.u }}</small> </CardElement>
</h2>
</div>
</div> </div>
</div> <div class="col">
<div class="col"> <CardElement centerContent textVariant="text-bg-success" :text="$t('invertertotalinfo.MpptTotalYieldDay')">
<div class="card"> <h2>
<div class="card-header text-bg-success">{{ $t('invertertotalinfo.MpptTotalYieldDay') }}</div> {{ $n(totalVeData.total.YieldDay.v, 'decimal', {
<div class="card-body card-text text-center"> minimumFractionDigits: totalVeData.total.YieldDay.d,
<h2> maximumFractionDigits: totalVeData.total.YieldDay.d
{{ $n(totalVeData.total.YieldDay.v, 'decimal', { }) }}
minimumFractionDigits: totalVeData.total.YieldDay.d, <small class="text-muted">{{ totalVeData.total.YieldDay.u }}</small>
maximumFractionDigits: totalVeData.total.YieldDay.d </h2>
}) }} </CardElement>
<small class="text-muted">{{ totalVeData.total.YieldDay.u }}</small>
</h2>
</div>
</div> </div>
</div> <div class="col">
<div class="col"> <CardElement centerContent textVariant="text-bg-success" :text="$t('invertertotalinfo.MpptTotalPower')">
<div class="card"> <h2>
<div class="card-header text-bg-success">{{ $t('invertertotalinfo.MpptTotalPower') }}</div> {{ $n(totalVeData.total.Power.v, 'decimal', {
<div class="card-body card-text text-center"> minimumFractionDigits: totalVeData.total.Power.d,
<h2> maximumFractionDigits: totalVeData.total.Power.d
{{ $n(totalVeData.total.Power.v, 'decimal', { }) }}
minimumFractionDigits: totalVeData.total.Power.d, <small class="text-muted">{{ totalVeData.total.Power.u }}</small>
maximumFractionDigits: totalVeData.total.Power.d </h2>
}) }} </CardElement>
<small class="text-muted">{{ totalVeData.total.Power.u }}</small>
</h2>
</div>
</div> </div>
</div>
</div> </div>
</div> </div>
<div class="row row-cols-1 row-cols-md-3 g-3"> <div class="row row-cols-1 row-cols-md-3 g-3">
<div class="col"> <div class="col">
<div class="card"> <CardElement centerContent textVariant="text-bg-success" :text="$t('invertertotalinfo.TotalYieldTotal')">
<div class="card-header text-bg-success">{{ $t('invertertotalinfo.InverterTotalYieldTotal') }}</div> <h2>
<div class="card-body card-text text-center"> {{ $n(totalData.YieldTotal.v, 'decimal', {
<h2> minimumFractionDigits: totalData.YieldTotal.d,
{{ $n(totalData.YieldTotal.v, 'decimal', { maximumFractionDigits: totalData.YieldTotal.d
minimumFractionDigits: totalData.YieldTotal.d, }) }}
maximumFractionDigits: totalData.YieldTotal.d <small class="text-muted">{{ totalData.YieldTotal.u }}</small>
}) }} </h2>
<small class="text-muted">{{ totalData.YieldTotal.u }}</small> </CardElement>
</h2>
</div> </div>
</div> <div class="col">
</div> <CardElement centerContent textVariant="text-bg-success" :text="$t('invertertotalinfo.TotalYieldDay')">
<div class="col"> <h2>
<div class="card"> {{ $n(totalData.YieldDay.v, 'decimal', {
<div class="card-header text-bg-success">{{ $t('invertertotalinfo.InverterTotalYieldDay') }}</div> minimumFractionDigits: totalData.YieldDay.d,
<div class="card-body card-text text-center"> maximumFractionDigits: totalData.YieldDay.d
<h2> }) }}
{{ $n(totalData.YieldDay.v, 'decimal', { <small class="text-muted">{{ totalData.YieldDay.u }}</small>
minimumFractionDigits: totalData.YieldDay.d, </h2>
maximumFractionDigits: totalData.YieldDay.d </CardElement>
}) }}
<small class="text-muted">{{ totalData.YieldDay.u }}</small>
</h2>
</div> </div>
</div> <div class="col">
</div> <CardElement centerContent textVariant="text-bg-success" :text="$t('invertertotalinfo.TotalPower')">
<div class="col"> <h2>
<div class="card"> {{ $n(totalData.Power.v, 'decimal', {
<div class="card-header text-bg-success">{{ $t('invertertotalinfo.InverterTotalPower') }}</div> minimumFractionDigits: totalData.Power.d,
<div class="card-body card-text text-center"> maximumFractionDigits: totalData.Power.d
<h2> }) }}
{{ $n(totalData.Power.v, 'decimal', { <small class="text-muted">{{ totalData.Power.u }}</small>
minimumFractionDigits: totalData.Power.d, </h2>
maximumFractionDigits: totalData.Power.d </CardElement>
}) }}
<small class="text-muted">{{ totalData.Power.u }}</small>
</h2>
</div> </div>
</div>
</div>
</div> </div>
<div v-show="totalBattData.enabled || powerMeterData.enabled || huaweiData.enabled"> <div v-show="totalBattData.enabled || powerMeterData.enabled || huaweiData.enabled">
<div class="row row-cols-1 row-cols-md-3 g-3"> <div class="row row-cols-1 row-cols-md-3 g-3">
<div class="col" v-show="totalBattData.enabled"> <div class="col" v-show="totalBattData.enabled">
<div class="card"> <CardElement centerContent textVariant="text-bg-success" :text="$t('invertertotalinfo.BatterySoc')">
<div class="card-header text-bg-success">{{ $t('invertertotalinfo.BatterySoc') }}</div> <h2>
<div class="card-body card-text text-center"> {{ $n(totalBattData.soc.v, 'decimal', {
<h2> minimumFractionDigits: totalBattData.soc.d,
{{ $n(totalBattData.soc.v, 'decimal', { maximumFractionDigits: totalBattData.soc.d
minimumFractionDigits: totalBattData.soc.d, }) }}
maximumFractionDigits: totalBattData.soc.d <small class="text-muted">{{ totalBattData.soc.u }}</small>
}) }} </h2>
<small class="text-muted">{{ totalBattData.soc.u }}</small> </CardElement>
</h2>
</div>
</div>
</div> </div>
<div class="col" v-show="powerMeterData.enabled"> <div class="col" v-show="powerMeterData.enabled">
<div class="card"> <CardElement centerContent textVariant="text-bg-success" :text="$t('invertertotalinfo.HomePower')">
<div class="card-header text-bg-success">{{ $t('invertertotalinfo.HomePower') }}</div> <h2>
<div class="card-body card-text text-center"> {{ $n(powerMeterData.Power.v, 'decimal', {
<h2> minimumFractionDigits: powerMeterData.Power.d,
{{ $n(powerMeterData.Power.v, 'decimal', { maximumFractionDigits: powerMeterData.Power.d
minimumFractionDigits: powerMeterData.Power.d, }) }}
maximumFractionDigits: powerMeterData.Power.d <small class="text-muted">{{powerMeterData.Power.u }}</small>
}) }} </h2>
<small class="text-muted">{{ powerMeterData.Power.u }}</small> </CardElement>
</h2>
</div>
</div>
</div> </div>
<div class="col" v-show="huaweiData.enabled"> <div class="col" v-show="huaweiData.enabled">
<div class="card"> <CardElement centerContent textVariant="text-bg-success" :text="$t('invertertotalinfo.HuaweiPower')">
<div class="card-header text-bg-success">{{ $t('invertertotalinfo.HuaweiPower') }}</div> <h2>
<div class="card-body card-text text-center"> {{ $n(huaweiData.Power.v, 'decimal', {
<h2> minimumFractionDigits: huaweiData.Power.d,
{{ $n(huaweiData.Power.v, 'decimal', { maximumFractionDigits: huaweiData.Power.d
minimumFractionDigits: huaweiData.Power.d, }) }}
maximumFractionDigits: huaweiData.Power.d <small class="text-muted">{{huaweiData.Power.u }}</small>
}) }} </h2>
<small class="text-muted">{{ huaweiData.Power.u }}</small> </CardElement>
</h2>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -139,9 +112,13 @@
<script lang="ts"> <script lang="ts">
import type { Battery, Total, Vedirect, Huawei, PowerMeter } from '@/types/LiveDataStatus'; import type { Battery, Total, Vedirect, Huawei, PowerMeter } from '@/types/LiveDataStatus';
import CardElement from './CardElement.vue';
import { defineComponent, type PropType } from 'vue'; import { defineComponent, type PropType } from 'vue';
export default defineComponent({ export default defineComponent({
components: {
CardElement,
},
props: { props: {
totalData: { type: Object as PropType<Total>, required: true }, totalData: { type: Object as PropType<Total>, required: true },
totalVeData: { type: Object as PropType<Vedirect>, required: true }, totalVeData: { type: Object as PropType<Vedirect>, required: true },
@ -150,4 +127,4 @@ export default defineComponent({
huaweiData: { type: Object as PropType<PowerMeter>, required: true }, huaweiData: { type: Object as PropType<PowerMeter>, required: true },
}, },
}); });
</script> </script>

View File

@ -0,0 +1,52 @@
<template>
<div class="modal" :id="modalId" tabindex="-1">
<div class="modal-dialog" :class="[small ? '' : 'modal-lg']">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{ title }}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" :aria-label="getCloseText"
@click="close"></button>
</div>
<div class="modal-body">
<div class="text-center" v-if="loading">
<div class="spinner-border" role="status">
<span class="visually-hidden">{{ $t('home.Loading') }}</span>
</div>
</div>
<slot v-else>
</slot>
</div>
<div class="modal-footer">
<slot name="footer">
</slot>
<button type="button" class="btn btn-secondary" @click="close" data-bs-dismiss="modal">{{
getCloseText }}</button>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
props: {
'modalId': { type: String, required: true },
'title': { type: String, required: true },
'closeText': { type: String, required: false, default: '' },
'small': Boolean,
'loading': Boolean,
},
computed: {
getCloseText() {
return this.closeText == '' ? this.$t('base.Close') : this.closeText;
}
},
methods: {
close() {
this.$emit('close');
},
},
});
</script>

View File

@ -1,17 +1,16 @@
<template> <template>
<nav class="navbar navbar-expand-md fixed-top bg-body-tertiary" data-bs-theme="dark"> <nav class="navbar navbar-expand-md fixed-top bg-body-tertiary" data-bs-theme="dark">
<div class="container-fluid"> <div class="container-fluid">
<router-link @click="onClick" class="navbar-brand" to="/"> <router-link @click="onClick" class="navbar-brand" to="/" style="display: flex; height: 30px; padding: 0;">
<span v-if="isXmas" class="text-success"> <BIconTree v-if="isXmas" width="30" height="30" class="d-inline-block align-text-top text-success" />
<BIconTree width="30" height="30" class="d-inline-block align-text-top" />
<BIconEgg v-else-if="isEaster" width="30" height="30" class="d-inline-block align-text-top text-info" />
<BIconSun v-else width="30" height="30" class="d-inline-block align-text-top text-warning" />
<span style="margin-left: .5rem">
OpenDTU-onBattery
</span> </span>
<span v-else-if="isEaster" class="text-info">
<BIconEgg width="30" height="30" class="d-inline-block align-text-top" />
</span>
<span v-else class="text-warning">
<BIconSun width="30" height="30" class="d-inline-block align-text-top" />
</span>
OpenDTU-onBattery
<span class="text-info"> <span class="text-info">
<BIconBatteryCharging width="20" height="20" class="d-inline-block align-text-center" /> <BIconBatteryCharging width="20" height="20" class="d-inline-block align-text-center" />
</span> </span>

View File

@ -38,7 +38,8 @@
"Save": "Speichern", "Save": "Speichern",
"Refreshing": "Aktualisieren", "Refreshing": "Aktualisieren",
"Pull": "Zum Aktualisieren nach unten ziehen", "Pull": "Zum Aktualisieren nach unten ziehen",
"Release": "Loslassen zum Aktualisieren" "Release": "Loslassen zum Aktualisieren",
"Close": "Schließen"
}, },
"localeswitcher": { "localeswitcher": {
"Dark": "Dunkel", "Dark": "Dunkel",
@ -125,7 +126,6 @@
"UnreadMessages": "Ungelesene Meldungen", "UnreadMessages": "Ungelesene Meldungen",
"Loading": "@:base.Loading", "Loading": "@:base.Loading",
"EventLog": "Ereignisanzeige", "EventLog": "Ereignisanzeige",
"Close": "Schließen",
"InverterInfo": "Wechselrichter-Informationen", "InverterInfo": "Wechselrichter-Informationen",
"LimitSettings": "Limit-Einstellungen", "LimitSettings": "Limit-Einstellungen",
"LastLimitSetStatus": "Letzter Übertragungsstatus:", "LastLimitSetStatus": "Letzter Übertragungsstatus:",
@ -229,10 +229,12 @@
"FirmwareVersionHint": "Klicken Sie hier, um Informationen über Ihre aktuelle Version anzuzeigen", "FirmwareVersionHint": "Klicken Sie hier, um Informationen über Ihre aktuelle Version anzuzeigen",
"FirmwareUpdate": "Firmware-Aktualisierung", "FirmwareUpdate": "Firmware-Aktualisierung",
"FirmwareUpdateHint": "Klicken Sie hier, um die Änderungen zwischen Ihrer Version und der neuesten Version anzuzeigen", "FirmwareUpdateHint": "Klicken Sie hier, um die Änderungen zwischen Ihrer Version und der neuesten Version anzuzeigen",
"FrmwareUpdateAllow": "Durch Aktivieren der Update Prüfung wird bei jedem Seitenaufruf eine Anfrage an GitHub.com gesendet um die aktuell verfügbare Version abzurufen. Wenn du damit nicht einverstanden bist, lasse diese Funktion deaktiviert.",
"ResetReason0": "Reset Grund CPU 0", "ResetReason0": "Reset Grund CPU 0",
"ResetReason1": "Reset Grund CPU 1", "ResetReason1": "Reset Grund CPU 1",
"ConfigSaveCount": "Anzahl der Konfigurationsspeicherungen", "ConfigSaveCount": "Anzahl der Konfigurationsspeicherungen",
"Uptime": "Betriebszeit" "Uptime": "Betriebszeit",
"UptimeValue": "0 Tage {time} | 1 Tag {time} | {count} Tage {time}"
}, },
"hardwareinfo": { "hardwareinfo": {
"HardwareInformation": "Hardwareinformationen", "HardwareInformation": "Hardwareinformationen",
@ -371,10 +373,7 @@
"inverterchannelinfo": { "inverterchannelinfo": {
"String": "String {num}", "String": "String {num}",
"Phase": "Phase {num}", "Phase": "Phase {num}",
"General": "Allgemein", "General": "Allgemein"
"Property": "Eigenschaft",
"Value": "Wert",
"Unit": "Einheit"
}, },
"invertertotalinfo": { "invertertotalinfo": {
"InverterTotalYieldTotal": "Inverter Gesamtertrag Insgesamt", "InverterTotalYieldTotal": "Inverter Gesamtertrag Insgesamt",
@ -422,7 +421,7 @@
"CmtPaLevel": "CMT2300A Sendeleistung:", "CmtPaLevel": "CMT2300A Sendeleistung:",
"NrfPaLevelHint": "Verwendet für HM-Wechselrichter. Stellen Sie sicher, dass Ihre Stromversorgung stabil genug ist, bevor Sie die Sendeleistung erhöhen.", "NrfPaLevelHint": "Verwendet für HM-Wechselrichter. Stellen Sie sicher, dass Ihre Stromversorgung stabil genug ist, bevor Sie die Sendeleistung erhöhen.",
"CmtPaLevelHint": "Verwendet für HMS/HMT-Wechselrichter. Stellen Sie sicher, dass Ihre Stromversorgung stabil genug ist, bevor Sie die Sendeleistung erhöhen.", "CmtPaLevelHint": "Verwendet für HMS/HMT-Wechselrichter. Stellen Sie sicher, dass Ihre Stromversorgung stabil genug ist, bevor Sie die Sendeleistung erhöhen.",
"CmtCountry": "Region/Land:", "CmtCountry": "CMT2300A Region/Land:",
"CmtCountryHint": "Jedes Land hat unterschiedliche Frequenzzuteilungen.", "CmtCountryHint": "Jedes Land hat unterschiedliche Frequenzzuteilungen.",
"country_0": "Europa ({min}MHz - {max}MHz)", "country_0": "Europa ({min}MHz - {max}MHz)",
"country_1": "Nordamerika ({min}MHz - {max}MHz)", "country_1": "Nordamerika ({min}MHz - {max}MHz)",
@ -716,7 +715,7 @@
"Back": "Zurück", "Back": "Zurück",
"Retry": "Wiederholen", "Retry": "Wiederholen",
"OtaStatus": "OTA-Status", "OtaStatus": "OTA-Status",
"OtaSuccess": "OTA erfolgreich. Das Gerät wurde automatisch neu gestartet und wird in wenigen Augenblicken wieder zur Verfügung stehen. Bitte nicht vergessen, die Weboberfläche neu zu laden!", "OtaSuccess": "Das Hochladen der Firmware war erfolgreich. Das Gerät wurde automatisch neu gestartet. Wenn das Gerät wieder erreichbar ist wird die automatisch Oberfläche neu geladen.",
"FirmwareUpload": "Firmware hochladen", "FirmwareUpload": "Firmware hochladen",
"UploadProgress": "Hochlade-Fortschritt" "UploadProgress": "Hochlade-Fortschritt"
}, },

View File

@ -38,7 +38,8 @@
"Save": "Save", "Save": "Save",
"Refreshing": "Refreshing", "Refreshing": "Refreshing",
"Pull": "Pull down to refresh", "Pull": "Pull down to refresh",
"Release": "Release to refresh" "Release": "Release to refresh",
"Close": "Close"
}, },
"localeswitcher": { "localeswitcher": {
"Dark": "Dark", "Dark": "Dark",
@ -125,7 +126,6 @@
"UnreadMessages": "unread messages", "UnreadMessages": "unread messages",
"Loading": "@:base.Loading", "Loading": "@:base.Loading",
"EventLog": "Event Log", "EventLog": "Event Log",
"Close": "Close",
"InverterInfo": "Inverter Info", "InverterInfo": "Inverter Info",
"LimitSettings": "Limit Settings", "LimitSettings": "Limit Settings",
"LastLimitSetStatus": "Last Limit Set Status:", "LastLimitSetStatus": "Last Limit Set Status:",
@ -230,10 +230,12 @@
"FirmwareVersionHint": "Click here to show information about your current version", "FirmwareVersionHint": "Click here to show information about your current version",
"FirmwareUpdate": "Firmware Update", "FirmwareUpdate": "Firmware Update",
"FirmwareUpdateHint": "Click here to view the changes between your version and the latest version", "FirmwareUpdateHint": "Click here to view the changes between your version and the latest version",
"FrmwareUpdateAllow": "By activating the update check, a request is sent to GitHub.com each time the page is called up to retrieve the currently available version. If you do not agree with this, leave this function deactivated.",
"ResetReason0": "Reset Reason CPU 0", "ResetReason0": "Reset Reason CPU 0",
"ResetReason1": "Reset Reason CPU 1", "ResetReason1": "Reset Reason CPU 1",
"ConfigSaveCount": "Config save count", "ConfigSaveCount": "Config save count",
"Uptime": "Uptime" "Uptime": "Uptime",
"UptimeValue": "0 days {time} | 1 day {time} | {count} days {time}"
}, },
"hardwareinfo": { "hardwareinfo": {
"HardwareInformation": "Hardware Information", "HardwareInformation": "Hardware Information",
@ -373,10 +375,7 @@
"inverterchannelinfo": { "inverterchannelinfo": {
"String": "String {num}", "String": "String {num}",
"Phase": "Phase {num}", "Phase": "Phase {num}",
"General": "General", "General": "General"
"Property": "Property",
"Value": "Value",
"Unit": "Unit"
}, },
"invertertotalinfo": { "invertertotalinfo": {
"InverterTotalYieldTotal": "Inverter Total Yield Total", "InverterTotalYieldTotal": "Inverter Total Yield Total",
@ -424,7 +423,7 @@
"CmtPaLevel": "CMT2300A Transmitting power:", "CmtPaLevel": "CMT2300A Transmitting power:",
"NrfPaLevelHint": "Used for HM-Inverters. Make sure your power supply is stable enough before increasing the transmit power.", "NrfPaLevelHint": "Used for HM-Inverters. Make sure your power supply is stable enough before increasing the transmit power.",
"CmtPaLevelHint": "Used for HMS/HMT-Inverters. Make sure your power supply is stable enough before increasing the transmit power.", "CmtPaLevelHint": "Used for HMS/HMT-Inverters. Make sure your power supply is stable enough before increasing the transmit power.",
"CmtCountry": "Region/Country:", "CmtCountry": "CMT2300A Region/Country:",
"CmtCountryHint": "Each country has different frequency allocations.", "CmtCountryHint": "Each country has different frequency allocations.",
"country_0": "Europe ({min}MHz - {max}MHz)", "country_0": "Europe ({min}MHz - {max}MHz)",
"country_1": "North America ({min}MHz - {max}MHz)", "country_1": "North America ({min}MHz - {max}MHz)",
@ -725,7 +724,7 @@
"Back": "Back", "Back": "Back",
"Retry": "Retry", "Retry": "Retry",
"OtaStatus": "OTA Status", "OtaStatus": "OTA Status",
"OtaSuccess": "OTA Success. The unit has been automatically restarted and will be available again in a few moments. Please do not forget to reload the web interface!", "OtaSuccess": "The firmware upload was successful. The device was restarted automatically. When the device is accessible again, the interface is automatically reloaded.",
"FirmwareUpload": "Firmware Upload", "FirmwareUpload": "Firmware Upload",
"UploadProgress": "Upload Progress" "UploadProgress": "Upload Progress"
}, },

View File

@ -38,7 +38,8 @@
"Save": "Sauvegarder", "Save": "Sauvegarder",
"Refreshing": "Refreshing", "Refreshing": "Refreshing",
"Pull": "Pull down to refresh", "Pull": "Pull down to refresh",
"Release": "Release to refresh" "Release": "Release to refresh",
"Close": "Fermer"
}, },
"localeswitcher": { "localeswitcher": {
"Dark": "Sombre", "Dark": "Sombre",
@ -125,7 +126,6 @@
"UnreadMessages": "messages non lus", "UnreadMessages": "messages non lus",
"Loading": "@:base.Loading", "Loading": "@:base.Loading",
"EventLog": "Journal des événements", "EventLog": "Journal des événements",
"Close": "Fermer",
"InverterInfo": "Informations sur l'onduleur", "InverterInfo": "Informations sur l'onduleur",
"LimitSettings": "Paramètres de la limite", "LimitSettings": "Paramètres de la limite",
"LastLimitSetStatus": "Statut de la dernière limite fixée", "LastLimitSetStatus": "Statut de la dernière limite fixée",
@ -229,10 +229,12 @@
"FirmwareVersionHint": "Cliquez ici pour afficher des informations sur votre version actuelle", "FirmwareVersionHint": "Cliquez ici pour afficher des informations sur votre version actuelle",
"FirmwareUpdate": "Mise à jour du firmware", "FirmwareUpdate": "Mise à jour du firmware",
"FirmwareUpdateHint": "Cliquez ici pour voir les changements entre votre version et la dernière version", "FirmwareUpdateHint": "Cliquez ici pour voir les changements entre votre version et la dernière version",
"FrmwareUpdateAllow": "En activant le contrôle de mise à jour, une demande est envoyée à GitHub.com à chaque fois que la page est consultée afin de récupérer la dernière version disponible. Si tu n'es pas d'accord, laisse cette fonction désactivée.",
"ResetReason0": "Raison de la réinitialisation CPU 0", "ResetReason0": "Raison de la réinitialisation CPU 0",
"ResetReason1": "Raison de la réinitialisation CPU 1", "ResetReason1": "Raison de la réinitialisation CPU 1",
"ConfigSaveCount": "Nombre d'enregistrements de la configuration", "ConfigSaveCount": "Nombre d'enregistrements de la configuration",
"Uptime": "Temps de fonctionnement" "Uptime": "Durée de fonctionnement",
"UptimeValue": "0 jour {time} | 1 jour {time} | {count} jours {time}"
}, },
"hardwareinfo": { "hardwareinfo": {
"HardwareInformation": "Informations sur le matériel", "HardwareInformation": "Informations sur le matériel",
@ -371,10 +373,7 @@
"inverterchannelinfo": { "inverterchannelinfo": {
"String": "Ligne {num}", "String": "Ligne {num}",
"Phase": "Phase {num}", "Phase": "Phase {num}",
"General": "General", "General": "General"
"Property": "Propriété",
"Value": "Valeur",
"Unit": "Unité"
}, },
"invertertotalinfo": { "invertertotalinfo": {
"InverterTotalYieldTotal": "Onduleurs rendement total", "InverterTotalYieldTotal": "Onduleurs rendement total",
@ -422,7 +421,7 @@
"CmtPaLevel": "CMT2300A Niveau de puissance d'émission", "CmtPaLevel": "CMT2300A Niveau de puissance d'émission",
"NrfPaLevelHint": "Used for HM-Inverters. Assurez-vous que votre alimentation est suffisamment stable avant d'augmenter la puissance d'émission.", "NrfPaLevelHint": "Used for HM-Inverters. Assurez-vous que votre alimentation est suffisamment stable avant d'augmenter la puissance d'émission.",
"CmtPaLevelHint": "Used for HMS/HMT-Inverters. Assurez-vous que votre alimentation est suffisamment stable avant d'augmenter la puissance d'émission.", "CmtPaLevelHint": "Used for HMS/HMT-Inverters. Assurez-vous que votre alimentation est suffisamment stable avant d'augmenter la puissance d'émission.",
"CmtCountry": "Region/Country:", "CmtCountry": "CMT2300A Region/Country:",
"CmtCountryHint": "Each country has different frequency allocations.", "CmtCountryHint": "Each country has different frequency allocations.",
"country_0": "Europe ({min}MHz - {max}MHz)", "country_0": "Europe ({min}MHz - {max}MHz)",
"country_1": "North America ({min}MHz - {max}MHz)", "country_1": "North America ({min}MHz - {max}MHz)",
@ -683,7 +682,7 @@
"Back": "Retour", "Back": "Retour",
"Retry": "Réessayer", "Retry": "Réessayer",
"OtaStatus": "Statut OTA", "OtaStatus": "Statut OTA",
"OtaSuccess": "Succès de l'OTA. L'unité a été automatiquement redémarrée et sera à nouveau disponible dans quelques instants. N'oubliez pas de recharger l'interface web !", "OtaSuccess": "Le téléchargement du firmware a réussi. L'appareil a été redémarré automatiquement. Lorsque l'appareil est à nouveau accessible, l'interface est automatiquement rechargée.",
"FirmwareUpload": "Téléversement du firmware", "FirmwareUpload": "Téléversement du firmware",
"UploadProgress": "Progression du téléversement" "UploadProgress": "Progression du téléversement"
}, },

View File

@ -1,6 +1,6 @@
// Import all of Bootstrap's CSS // Import all of Bootstrap's CSS
@import "~bootstrap/scss/bootstrap"; @import "~bootstrap/scss/bootstrap";
main { .container-fluid .row {
font-feature-settings: "tnum"; font-feature-settings: "tnum";
} }

View File

@ -0,0 +1,22 @@
export interface InverterChannel {
name: string;
max_power: number;
yield_total_offset: number;
}
export interface Inverter {
id: string;
serial: number;
name: string;
type: string;
order: number;
poll_enable: boolean;
poll_enable_night: boolean;
command_enable: boolean;
command_enable_night: boolean;
reachable_threshold: number;
zero_runtime: boolean;
zero_day: boolean;
yieldday_correction: boolean;
channel: Array<InverterChannel>;
}

View File

@ -1,8 +1,10 @@
export const timestampToString = (timestampSeconds: number, includeDays = false): string => { export function timestampToString(locale: string, timestampSeconds: number, includeDays: true): [number, string];
const timeString = new Date(timestampSeconds * 1000).toLocaleTimeString([], { timeZone: "UTC" }); export function timestampToString(locale: string, timestampSeconds: number, includeDays?: false): [string];
if (!includeDays) return timeString; export function timestampToString(locale: string, timestampSeconds: number, includeDays = false): [number, string] | [string] {
const timeString = new Date(timestampSeconds * 1000).toLocaleTimeString(locale, { timeZone: "UTC", hour12: false });
if (!includeDays) return [timeString];
const secondsPerDay = 60 * 60 * 24; const secondsPerDay = 60 * 60 * 24;
const days = Math.floor(timestampSeconds / secondsPerDay); const days = Math.floor(timestampSeconds / secondsPerDay);
return new Intl.RelativeTimeFormat().format(-days, "day") + " " + timeString; return [days, timeString];
} }

View File

@ -88,32 +88,21 @@
</CardElement> </CardElement>
</BasePage> </BasePage>
<div class="modal" id="factoryReset" tabindex="-1"> <ModalDialog modalId="factoryReset" small :title="$t('configadmin.FactoryReset')" :closeText="$t('configadmin.Cancel')">
<div class="modal-dialog"> {{ $t('configadmin.ResetMsg') }}
<div class="modal-content"> <template #footer>
<div class="modal-header"> <button type="button" class="btn btn-danger" @click="onFactoryResetPerform">
<h5 class="modal-title">{{ $t('configadmin.FactoryReset') }}</h5> {{ $t('configadmin.ResetConfirm') }}
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </button>
</div> </template>
<div class="modal-body"> </ModalDialog>
{{ $t('configadmin.ResetMsg') }}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @click="onFactoryResetCancel"
data-bs-dismiss="modal">{{ $t('configadmin.Cancel') }}</button>
<button type="button" class="btn btn-danger" @click="onFactoryResetPerform">
{{ $t('configadmin.ResetConfirm') }}
</button>
</div>
</div>
</div>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
import BasePage from '@/components/BasePage.vue'; import BasePage from '@/components/BasePage.vue';
import BootstrapAlert from "@/components/BootstrapAlert.vue"; import BootstrapAlert from "@/components/BootstrapAlert.vue";
import CardElement from '@/components/CardElement.vue'; import CardElement from '@/components/CardElement.vue';
import ModalDialog from '@/components/ModalDialog.vue';
import type { ConfigFileList } from '@/types/Config'; import type { ConfigFileList } from '@/types/Config';
import { authHeader, handleResponse } from '@/utils/authentication'; import { authHeader, handleResponse } from '@/utils/authentication';
import * as bootstrap from 'bootstrap'; import * as bootstrap from 'bootstrap';
@ -129,6 +118,7 @@ export default defineComponent({
BasePage, BasePage,
BootstrapAlert, BootstrapAlert,
CardElement, CardElement,
ModalDialog,
BIconArrowLeft, BIconArrowLeft,
BIconCheckCircle, BIconCheckCircle,
BIconExclamationCircleFill, BIconExclamationCircleFill,
@ -252,4 +242,4 @@ export default defineComponent({
}, },
}, },
}); });
</script> </script>

Some files were not shown because too many files have changed in this diff Show More