Merge branch 'development'
This commit is contained in:
commit
f0f8702b4b
10
.github/workflows/build.yml
vendored
10
.github/workflows/build.yml
vendored
@ -23,14 +23,14 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Cache pip
|
- name: Cache pip
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/pip
|
path: ~/.cache/pip
|
||||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-pip-
|
${{ runner.os }}-pip-
|
||||||
|
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ jobs:
|
|||||||
run: git fetch --force --tags origin
|
run: git fetch --force --tags origin
|
||||||
|
|
||||||
- name: Cache pip
|
- name: Cache pip
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/pip
|
path: ~/.cache/pip
|
||||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||||
@ -69,13 +69,13 @@ jobs:
|
|||||||
${{ runner.os }}-pip-
|
${{ runner.os }}-pip-
|
||||||
|
|
||||||
- name: Cache PlatformIO
|
- name: Cache PlatformIO
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ~/.platformio
|
path: ~/.platformio
|
||||||
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
|
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/cpplint.yml
vendored
2
.github/workflows/cpplint.yml
vendored
@ -9,7 +9,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
|
|||||||
76
docs/DeviceProfiles/AhoyDTU-ESP32.json
Normal file
76
docs/DeviceProfiles/AhoyDTU-ESP32.json
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "AhoyDTU ESP32 Display LED",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://ahoydtu.de/getting_started/"}
|
||||||
|
],
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 19,
|
||||||
|
"mosi": 23,
|
||||||
|
"clk": 18,
|
||||||
|
"irq": 16,
|
||||||
|
"en": 4,
|
||||||
|
"cs": 5
|
||||||
|
},
|
||||||
|
"led": {
|
||||||
|
"led0": 25,
|
||||||
|
"led1": 26
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"type": 2,
|
||||||
|
"data": 21,
|
||||||
|
"clk": 22
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AhoyDTU ESP32 Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://ahoydtu.de/getting_started/"}
|
||||||
|
],
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 19,
|
||||||
|
"mosi": 23,
|
||||||
|
"clk": 18,
|
||||||
|
"irq": 16,
|
||||||
|
"en": 4,
|
||||||
|
"cs": 5
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"type": 2,
|
||||||
|
"data": 21,
|
||||||
|
"clk": 22
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AhoyDTU ESP32 LED",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://ahoydtu.de/getting_started/"}
|
||||||
|
],
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 19,
|
||||||
|
"mosi": 23,
|
||||||
|
"clk": 18,
|
||||||
|
"irq": 16,
|
||||||
|
"en": 4,
|
||||||
|
"cs": 5
|
||||||
|
},
|
||||||
|
"led": {
|
||||||
|
"led0": 25,
|
||||||
|
"led1": 26
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AhoyDTU ESP32",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://ahoydtu.de/getting_started/"}
|
||||||
|
],
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 19,
|
||||||
|
"mosi": 23,
|
||||||
|
"clk": 18,
|
||||||
|
"irq": 16,
|
||||||
|
"en": 4,
|
||||||
|
"cs": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
74
docs/DeviceProfiles/liligo_t-eth-lite_poe.json
Normal file
74
docs/DeviceProfiles/liligo_t-eth-lite_poe.json
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "LILYGO T-ETH-Lite-POE CMT",
|
||||||
|
"links": [
|
||||||
|
{"name": "Datasheet", "url": "https://www.lilygo.cc/products/t-eth-lite"}
|
||||||
|
],
|
||||||
|
"eth": {
|
||||||
|
"enabled": true,
|
||||||
|
"phy_addr": 0,
|
||||||
|
"power": 12,
|
||||||
|
"mdc": 23,
|
||||||
|
"mdio": 18,
|
||||||
|
"type": 2,
|
||||||
|
"clk_mode": 0
|
||||||
|
},
|
||||||
|
"cmt": {
|
||||||
|
"clk": 15,
|
||||||
|
"cs": 32,
|
||||||
|
"fcs": 33,
|
||||||
|
"sdio": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LILYGO T-ETH-Lite-POE NRF24",
|
||||||
|
"links": [
|
||||||
|
{"name": "Datasheet", "url": "https://www.lilygo.cc/products/t-eth-lite"}
|
||||||
|
],
|
||||||
|
"eth": {
|
||||||
|
"enabled": true,
|
||||||
|
"phy_addr": 0,
|
||||||
|
"power": 12,
|
||||||
|
"mdc": 23,
|
||||||
|
"mdio": 18,
|
||||||
|
"type": 2,
|
||||||
|
"clk_mode": 0
|
||||||
|
},
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 34,
|
||||||
|
"mosi": 13,
|
||||||
|
"clk": 14,
|
||||||
|
"irq": 35,
|
||||||
|
"en": 4,
|
||||||
|
"cs": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LILYGO T-ETH-Lite-POE NRF24 + Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Datasheet", "url": "https://www.lilygo.cc/products/t-eth-lite"}
|
||||||
|
],
|
||||||
|
"eth": {
|
||||||
|
"enabled": true,
|
||||||
|
"phy_addr": 0,
|
||||||
|
"power": 12,
|
||||||
|
"mdc": 23,
|
||||||
|
"mdio": 18,
|
||||||
|
"type": 2,
|
||||||
|
"clk_mode": 0
|
||||||
|
},
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 34,
|
||||||
|
"mosi": 13,
|
||||||
|
"clk": 14,
|
||||||
|
"irq": 35,
|
||||||
|
"en": 4,
|
||||||
|
"cs": 2
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"type": 3,
|
||||||
|
"data": 32,
|
||||||
|
"clk": 33
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@ -17,9 +17,12 @@ class BatteryStats {
|
|||||||
uint32_t getAgeSeconds() const { return (millis() - _lastUpdate) / 1000; }
|
uint32_t getAgeSeconds() const { return (millis() - _lastUpdate) / 1000; }
|
||||||
bool updateAvailable(uint32_t since) const { return _lastUpdate > since; }
|
bool updateAvailable(uint32_t since) const { return _lastUpdate > since; }
|
||||||
|
|
||||||
uint8_t getSoC() const { return _SoC; }
|
uint8_t getSoC() const { return _soc; }
|
||||||
uint32_t getSoCAgeSeconds() const { return (millis() - _lastUpdateSoC) / 1000; }
|
uint32_t getSoCAgeSeconds() const { return (millis() - _lastUpdateSoC) / 1000; }
|
||||||
|
|
||||||
|
float getVoltage() const { return _voltage; }
|
||||||
|
uint32_t getVoltageAgeSeconds() const { return (millis() - _lastUpdateVoltage) / 1000; }
|
||||||
|
|
||||||
// convert stats to JSON for web application live view
|
// convert stats to JSON for web application live view
|
||||||
virtual void getLiveViewData(JsonVariant& root) const;
|
virtual void getLiveViewData(JsonVariant& root) const;
|
||||||
|
|
||||||
@ -29,18 +32,33 @@ class BatteryStats {
|
|||||||
// if they did not change. used to calculate Home Assistent expiration.
|
// if they did not change. used to calculate Home Assistent expiration.
|
||||||
virtual uint32_t getMqttFullPublishIntervalMs() const;
|
virtual uint32_t getMqttFullPublishIntervalMs() const;
|
||||||
|
|
||||||
bool isValid() const { return _lastUpdateSoC > 0 && _lastUpdate > 0; }
|
bool isSoCValid() const { return _lastUpdateSoC > 0; }
|
||||||
|
bool isVoltageValid() const { return _lastUpdateVoltage > 0; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void mqttPublish() const;
|
virtual void mqttPublish() const;
|
||||||
|
|
||||||
|
void setSoC(float soc, uint8_t precision, uint32_t timestamp) {
|
||||||
|
_soc = soc;
|
||||||
|
_socPrecision = precision;
|
||||||
|
_lastUpdateSoC = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setVoltage(float voltage, uint32_t timestamp) {
|
||||||
|
_voltage = voltage;
|
||||||
|
_lastUpdateVoltage = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
String _manufacturer = "unknown";
|
String _manufacturer = "unknown";
|
||||||
uint8_t _SoC = 0;
|
|
||||||
uint32_t _lastUpdateSoC = 0;
|
|
||||||
uint32_t _lastUpdate = 0;
|
uint32_t _lastUpdate = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t _lastMqttPublish = 0;
|
uint32_t _lastMqttPublish = 0;
|
||||||
|
float _soc = 0;
|
||||||
|
uint8_t _socPrecision = 0; // decimal places
|
||||||
|
uint32_t _lastUpdateSoC = 0;
|
||||||
|
float _voltage = 0; // total battery pack voltage
|
||||||
|
uint32_t _lastUpdateVoltage = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PylontechBatteryStats : public BatteryStats {
|
class PylontechBatteryStats : public BatteryStats {
|
||||||
@ -52,14 +70,12 @@ class PylontechBatteryStats : public BatteryStats {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void setManufacturer(String&& m) { _manufacturer = std::move(m); }
|
void setManufacturer(String&& m) { _manufacturer = std::move(m); }
|
||||||
void setSoC(uint8_t SoC) { _SoC = SoC; _lastUpdateSoC = millis(); }
|
|
||||||
void setLastUpdate(uint32_t ts) { _lastUpdate = ts; }
|
void setLastUpdate(uint32_t ts) { _lastUpdate = ts; }
|
||||||
|
|
||||||
float _chargeVoltage;
|
float _chargeVoltage;
|
||||||
float _chargeCurrentLimitation;
|
float _chargeCurrentLimitation;
|
||||||
float _dischargeCurrentLimitation;
|
float _dischargeCurrentLimitation;
|
||||||
uint16_t _stateOfHealth;
|
uint16_t _stateOfHealth;
|
||||||
float _voltage; // total voltage of the battery pack
|
|
||||||
// total current into (positive) or from (negative)
|
// total current into (positive) or from (negative)
|
||||||
// the battery, i.e., the charging current
|
// the battery, i.e., the charging current
|
||||||
float _current;
|
float _current;
|
||||||
@ -123,7 +139,6 @@ class VictronSmartShuntStats : public BatteryStats {
|
|||||||
void updateFrom(VeDirectShuntController::veShuntStruct const& shuntData);
|
void updateFrom(VeDirectShuntController::veShuntStruct const& shuntData);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float _voltage;
|
|
||||||
float _current;
|
float _current;
|
||||||
float _temperature;
|
float _temperature;
|
||||||
bool _tempPresent;
|
bool _tempPresent;
|
||||||
@ -141,14 +156,14 @@ class VictronSmartShuntStats : public BatteryStats {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class MqttBatteryStats : public BatteryStats {
|
class MqttBatteryStats : public BatteryStats {
|
||||||
|
friend class MqttBattery;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// since the source of information was MQTT in the first place,
|
// since the source of information was MQTT in the first place,
|
||||||
// we do NOT publish the same data under a different topic.
|
// we do NOT publish the same data under a different topic.
|
||||||
void mqttPublish() const final { }
|
void mqttPublish() const final { }
|
||||||
|
|
||||||
// the SoC is the only interesting value in this case, which is already
|
// if the voltage is subscribed to at all, it alone does not warrant a
|
||||||
// displayed at the top of the live view. do not generate a card.
|
// card in the live view, since the SoC is already displayed at the top
|
||||||
void getLiveViewData(JsonVariant& root) const final { }
|
void getLiveViewData(JsonVariant& root) const final { }
|
||||||
|
|
||||||
void setSoC(uint8_t SoC) { _SoC = SoC; _lastUpdateSoC = _lastUpdate = millis(); }
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -213,6 +213,7 @@ struct CONFIG_T {
|
|||||||
int32_t TargetPowerConsumptionHysteresis;
|
int32_t TargetPowerConsumptionHysteresis;
|
||||||
int32_t LowerPowerLimit;
|
int32_t LowerPowerLimit;
|
||||||
int32_t UpperPowerLimit;
|
int32_t UpperPowerLimit;
|
||||||
|
bool IgnoreSoc;
|
||||||
uint32_t BatterySocStartThreshold;
|
uint32_t BatterySocStartThreshold;
|
||||||
uint32_t BatterySocStopThreshold;
|
uint32_t BatterySocStopThreshold;
|
||||||
float VoltageStartThreshold;
|
float VoltageStartThreshold;
|
||||||
@ -230,7 +231,8 @@ struct CONFIG_T {
|
|||||||
uint8_t Provider;
|
uint8_t Provider;
|
||||||
uint8_t JkBmsInterface;
|
uint8_t JkBmsInterface;
|
||||||
uint8_t JkBmsPollingInterval;
|
uint8_t JkBmsPollingInterval;
|
||||||
char MqttTopic[MQTT_MAX_TOPIC_STRLEN + 1];
|
char MqttSocTopic[MQTT_MAX_TOPIC_STRLEN + 1];
|
||||||
|
char MqttVoltageTopic[MQTT_MAX_TOPIC_STRLEN + 1];
|
||||||
} Battery;
|
} Battery;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include "Battery.h"
|
#include "Battery.h"
|
||||||
#include <espMqttClient.h>
|
#include <espMqttClient.h>
|
||||||
|
|
||||||
@ -15,8 +16,12 @@ class MqttBattery : public BatteryProvider {
|
|||||||
private:
|
private:
|
||||||
bool _verboseLogging = false;
|
bool _verboseLogging = false;
|
||||||
String _socTopic;
|
String _socTopic;
|
||||||
|
String _voltageTopic;
|
||||||
std::shared_ptr<MqttBatteryStats> _stats = std::make_shared<MqttBatteryStats>();
|
std::shared_ptr<MqttBatteryStats> _stats = std::make_shared<MqttBatteryStats>();
|
||||||
|
|
||||||
void onMqttMessage(espMqttClientTypes::MessageProperties const& properties,
|
std::optional<float> getFloat(std::string const& src, char const* topic);
|
||||||
|
void onMqttMessageSoC(espMqttClientTypes::MessageProperties const& properties,
|
||||||
|
char const* topic, uint8_t const* payload, size_t len, size_t index, size_t total);
|
||||||
|
void onMqttMessageVoltage(espMqttClientTypes::MessageProperties const& properties,
|
||||||
char const* topic, uint8_t const* payload, size_t len, size_t index, size_t total);
|
char const* topic, uint8_t const* payload, size_t len, size_t index, size_t total);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -88,6 +88,7 @@ private:
|
|||||||
void announceStatus(Status status);
|
void announceStatus(Status status);
|
||||||
bool shutdown(Status status);
|
bool shutdown(Status status);
|
||||||
bool shutdown() { return shutdown(_lastStatus); }
|
bool shutdown() { return shutdown(_lastStatus); }
|
||||||
|
float getBatteryVoltage(bool log = false);
|
||||||
int32_t inverterPowerDcToAc(std::shared_ptr<InverterAbstract> inverter, int32_t dcPower);
|
int32_t inverterPowerDcToAc(std::shared_ptr<InverterAbstract> inverter, int32_t dcPower);
|
||||||
void unconditionalSolarPassthrough(std::shared_ptr<InverterAbstract> inverter);
|
void unconditionalSolarPassthrough(std::shared_ptr<InverterAbstract> inverter);
|
||||||
bool canUseDirectSolarPower();
|
bool canUseDirectSolarPower();
|
||||||
|
|||||||
@ -11,4 +11,5 @@ public:
|
|||||||
static int getTimezoneOffset();
|
static int getTimezoneOffset();
|
||||||
static void restartDtu();
|
static void restartDtu();
|
||||||
static bool checkJsonAlloc(const DynamicJsonDocument& doc, const char* function, const uint16_t line);
|
static bool checkJsonAlloc(const DynamicJsonDocument& doc, const char* function, const uint16_t line);
|
||||||
|
static void removeAllFiles();
|
||||||
};
|
};
|
||||||
|
|||||||
@ -35,6 +35,9 @@ public:
|
|||||||
// sum of today's yield of all MPPT charge controllers in kWh
|
// sum of today's yield of all MPPT charge controllers in kWh
|
||||||
double getYieldDay() const;
|
double getYieldDay() const;
|
||||||
|
|
||||||
|
// minimum of all MPPT charge controllers' output voltages in V
|
||||||
|
double getOutputVoltage() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void loop();
|
void loop();
|
||||||
VictronMpptClass(VictronMpptClass const& other) = delete;
|
VictronMpptClass(VictronMpptClass const& other) = delete;
|
||||||
|
|||||||
@ -14,6 +14,4 @@ private:
|
|||||||
void onConfigListGet(AsyncWebServerRequest* request);
|
void onConfigListGet(AsyncWebServerRequest* request);
|
||||||
void onConfigUploadFinish(AsyncWebServerRequest* request);
|
void onConfigUploadFinish(AsyncWebServerRequest* request);
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,6 +11,4 @@ public:
|
|||||||
private:
|
private:
|
||||||
void onDeviceAdminGet(AsyncWebServerRequest* request);
|
void onDeviceAdminGet(AsyncWebServerRequest* request);
|
||||||
void onDeviceAdminPost(AsyncWebServerRequest* request);
|
void onDeviceAdminPost(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -10,6 +10,4 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void onDevInfoStatus(AsyncWebServerRequest* request);
|
void onDevInfoStatus(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -13,8 +13,6 @@ private:
|
|||||||
void onDtuAdminGet(AsyncWebServerRequest* request);
|
void onDtuAdminGet(AsyncWebServerRequest* request);
|
||||||
void onDtuAdminPost(AsyncWebServerRequest* request);
|
void onDtuAdminPost(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
|
|
||||||
Task _applyDataTask;
|
Task _applyDataTask;
|
||||||
void applyDataTaskCb();
|
void applyDataTaskCb();
|
||||||
};
|
};
|
||||||
|
|||||||
@ -10,6 +10,4 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void onEventlogStatus(AsyncWebServerRequest* request);
|
void onEventlogStatus(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,6 +11,4 @@ public:
|
|||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,6 +11,4 @@ public:
|
|||||||
private:
|
private:
|
||||||
void onGridProfileStatus(AsyncWebServerRequest* request);
|
void onGridProfileStatus(AsyncWebServerRequest* request);
|
||||||
void onGridProfileRawdata(AsyncWebServerRequest* request);
|
void onGridProfileRawdata(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -14,6 +14,4 @@ private:
|
|||||||
void onInverterEdit(AsyncWebServerRequest* request);
|
void onInverterEdit(AsyncWebServerRequest* request);
|
||||||
void onInverterDelete(AsyncWebServerRequest* request);
|
void onInverterDelete(AsyncWebServerRequest* request);
|
||||||
void onInverterOrder(AsyncWebServerRequest* request);
|
void onInverterOrder(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,6 +11,4 @@ public:
|
|||||||
private:
|
private:
|
||||||
void onLimitStatus(AsyncWebServerRequest* request);
|
void onLimitStatus(AsyncWebServerRequest* request);
|
||||||
void onLimitPost(AsyncWebServerRequest* request);
|
void onLimitPost(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -10,6 +10,4 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void onRebootPost(AsyncWebServerRequest* request);
|
void onRebootPost(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -15,6 +15,4 @@ private:
|
|||||||
void onMqttAdminGet(AsyncWebServerRequest* request);
|
void onMqttAdminGet(AsyncWebServerRequest* request);
|
||||||
void onMqttAdminPost(AsyncWebServerRequest* request);
|
void onMqttAdminPost(AsyncWebServerRequest* request);
|
||||||
String getTlsCertInfo(const char* cert);
|
String getTlsCertInfo(const char* cert);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -12,6 +12,4 @@ private:
|
|||||||
void onNetworkStatus(AsyncWebServerRequest* request);
|
void onNetworkStatus(AsyncWebServerRequest* request);
|
||||||
void onNetworkAdminGet(AsyncWebServerRequest* request);
|
void onNetworkAdminGet(AsyncWebServerRequest* request);
|
||||||
void onNetworkAdminPost(AsyncWebServerRequest* request);
|
void onNetworkAdminPost(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -14,6 +14,4 @@ private:
|
|||||||
void onNtpAdminPost(AsyncWebServerRequest* request);
|
void onNtpAdminPost(AsyncWebServerRequest* request);
|
||||||
void onNtpTimeGet(AsyncWebServerRequest* request);
|
void onNtpTimeGet(AsyncWebServerRequest* request);
|
||||||
void onNtpTimePost(AsyncWebServerRequest* request);
|
void onNtpTimePost(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,6 +11,4 @@ public:
|
|||||||
private:
|
private:
|
||||||
void onPowerStatus(AsyncWebServerRequest* request);
|
void onPowerStatus(AsyncWebServerRequest* request);
|
||||||
void onPowerPost(AsyncWebServerRequest* request);
|
void onPowerPost(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -17,8 +17,6 @@ private:
|
|||||||
|
|
||||||
void addPanelInfo(AsyncResponseStream* stream, const String& serial, const uint8_t idx, std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel);
|
void addPanelInfo(AsyncResponseStream* stream, const String& serial, const uint8_t idx, std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
|
|
||||||
enum MetricType_t {
|
enum MetricType_t {
|
||||||
NONE = 0,
|
NONE = 0,
|
||||||
GAUGE,
|
GAUGE,
|
||||||
|
|||||||
@ -13,6 +13,4 @@ private:
|
|||||||
void onSecurityPost(AsyncWebServerRequest* request);
|
void onSecurityPost(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
void onAuthenticateGet(AsyncWebServerRequest* request);
|
void onAuthenticateGet(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -10,6 +10,4 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void onSystemStatus(AsyncWebServerRequest* request);
|
void onSystemStatus(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -9,5 +9,5 @@ public:
|
|||||||
void init(AsyncWebServer& server, Scheduler& scheduler);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AsyncWebServer* _server;
|
void responseBinaryDataWithETagCache(AsyncWebServerRequest* request, const String &contentType, const String &contentEncoding, const uint8_t *content, size_t len);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -10,7 +10,6 @@ public:
|
|||||||
void init(AsyncWebServer& server, Scheduler& scheduler);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AsyncWebServer* _server;
|
|
||||||
AsyncWebSocket _ws;
|
AsyncWebSocket _ws;
|
||||||
|
|
||||||
Task _wsCleanupTask;
|
Task _wsCleanupTask;
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Configuration.h"
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
#include <Hoymiles.h>
|
#include <Hoymiles.h>
|
||||||
@ -12,17 +13,19 @@ public:
|
|||||||
void init(AsyncWebServer& server, Scheduler& scheduler);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void generateJsonResponse(JsonVariant& root);
|
static void generateInverterCommonJsonResponse(JsonObject& root, std::shared_ptr<InverterAbstract> inv);
|
||||||
void addField(JsonObject& root, std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId, String topic = "");
|
static void generateInverterChannelJsonResponse(JsonObject& root, std::shared_ptr<InverterAbstract> inv);
|
||||||
void addTotalField(JsonObject& root, const String& name, const float value, const String& unit, const uint8_t digits);
|
static void generateCommonJsonResponse(JsonVariant& root);
|
||||||
|
|
||||||
|
static void addField(JsonObject& root, std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId, String topic = "");
|
||||||
|
static 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);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
AsyncWebSocket _ws;
|
AsyncWebSocket _ws;
|
||||||
|
|
||||||
uint32_t _lastWsPublish = 0;
|
uint32_t _lastPublishStats[INV_MAX_COUNT] = { 0 };
|
||||||
uint32_t _newestInverterTimestamp = 0;
|
|
||||||
|
|
||||||
std::mutex _mutex;
|
std::mutex _mutex;
|
||||||
|
|
||||||
|
|||||||
@ -128,6 +128,7 @@
|
|||||||
#define POWERLIMITER_TARGET_POWER_CONSUMPTION_HYSTERESIS 0
|
#define POWERLIMITER_TARGET_POWER_CONSUMPTION_HYSTERESIS 0
|
||||||
#define POWERLIMITER_LOWER_POWER_LIMIT 10
|
#define POWERLIMITER_LOWER_POWER_LIMIT 10
|
||||||
#define POWERLIMITER_UPPER_POWER_LIMIT 800
|
#define POWERLIMITER_UPPER_POWER_LIMIT 800
|
||||||
|
#define POWERLIMITER_IGNORE_SOC false
|
||||||
#define POWERLIMITER_BATTERY_SOC_START_THRESHOLD 80
|
#define POWERLIMITER_BATTERY_SOC_START_THRESHOLD 80
|
||||||
#define POWERLIMITER_BATTERY_SOC_STOP_THRESHOLD 20
|
#define POWERLIMITER_BATTERY_SOC_STOP_THRESHOLD 20
|
||||||
#define POWERLIMITER_VOLTAGE_START_THRESHOLD 50.0
|
#define POWERLIMITER_VOLTAGE_START_THRESHOLD 50.0
|
||||||
|
|||||||
@ -239,8 +239,11 @@ CountryModeId_t HoymilesRadio_CMT::getCountryMode() const
|
|||||||
|
|
||||||
void HoymilesRadio_CMT::setCountryMode(const CountryModeId_t mode)
|
void HoymilesRadio_CMT::setCountryMode(const CountryModeId_t mode)
|
||||||
{
|
{
|
||||||
_radio->setFrequencyBand(countryDefinition.at(mode).Band);
|
|
||||||
_countryMode = mode;
|
_countryMode = mode;
|
||||||
|
if (!_isInitialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_radio->setFrequencyBand(countryDefinition.at(mode).Band);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t HoymilesRadio_CMT::getInvBootFrequency() const
|
uint32_t HoymilesRadio_CMT::getInvBootFrequency() const
|
||||||
|
|||||||
@ -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 "HMS_1CH.h"
|
#include "HMS_1CH.h"
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ static const byteAssign_t byteAssignment[] = {
|
|||||||
{ TYPE_DC, CH0, FLD_PDC, UNIT_W, 6, 2, 10, false, 1 },
|
{ TYPE_DC, CH0, FLD_PDC, UNIT_W, 6, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH0, FLD_YD, UNIT_WH, 12, 2, 1, false, 0 },
|
{ TYPE_DC, CH0, FLD_YD, UNIT_WH, 12, 2, 1, false, 0 },
|
||||||
{ TYPE_DC, CH0, FLD_YT, UNIT_KWH, 8, 4, 1000, false, 3 },
|
{ TYPE_DC, CH0, FLD_YT, UNIT_KWH, 8, 4, 1000, false, 3 },
|
||||||
{ TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH0, CMD_CALC, false, 3 },
|
{ TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH0, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
{ TYPE_AC, CH0, FLD_UAC, UNIT_V, 14, 2, 10, false, 1 },
|
{ TYPE_AC, CH0, FLD_UAC, UNIT_V, 14, 2, 10, false, 1 },
|
||||||
{ TYPE_AC, CH0, FLD_IAC, UNIT_A, 22, 2, 100, false, 2 },
|
{ TYPE_AC, CH0, FLD_IAC, UNIT_A, 22, 2, 100, false, 2 },
|
||||||
@ -22,10 +22,10 @@ static const byteAssign_t byteAssignment[] = {
|
|||||||
{ TYPE_INV, CH0, FLD_T, UNIT_C, 26, 2, 10, true, 1 },
|
{ TYPE_INV, CH0, FLD_T, UNIT_C, 26, 2, 10, true, 1 },
|
||||||
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 28, 2, 1, false, 0 },
|
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 28, 2, 1, false, 0 },
|
||||||
|
|
||||||
{ TYPE_AC, CH0, FLD_YD, UNIT_WH, CALC_YD_CH0, 0, CMD_CALC, false, 0 },
|
{ TYPE_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 },
|
||||||
{ TYPE_AC, CH0, FLD_YT, UNIT_KWH, CALC_YT_CH0, 0, CMD_CALC, false, 3 },
|
{ TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 },
|
||||||
{ TYPE_AC, CH0, FLD_PDC, UNIT_W, CALC_PDC_CH0, 0, CMD_CALC, false, 1 },
|
{ TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 },
|
||||||
{ TYPE_AC, CH0, FLD_EFF, UNIT_PCT, CALC_EFF_CH0, 0, CMD_CALC, false, 3 }
|
{ TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 }
|
||||||
};
|
};
|
||||||
|
|
||||||
HMS_1CH::HMS_1CH(HoymilesRadio* radio, const uint64_t serial)
|
HMS_1CH::HMS_1CH(HoymilesRadio* radio, const uint64_t serial)
|
||||||
|
|||||||
@ -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 "HMS_1CHv2.h"
|
#include "HMS_1CHv2.h"
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ static const byteAssign_t byteAssignment[] = {
|
|||||||
{ TYPE_DC, CH0, FLD_PDC, UNIT_W, 10, 2, 10, false, 1 },
|
{ TYPE_DC, CH0, FLD_PDC, UNIT_W, 10, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH0, FLD_YD, UNIT_WH, 22, 2, 1, false, 0 },
|
{ TYPE_DC, CH0, FLD_YD, UNIT_WH, 22, 2, 1, false, 0 },
|
||||||
{ TYPE_DC, CH0, FLD_YT, UNIT_KWH, 14, 4, 1000, false, 3 },
|
{ TYPE_DC, CH0, FLD_YT, UNIT_KWH, 14, 4, 1000, false, 3 },
|
||||||
{ TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH0, CMD_CALC, false, 3 },
|
{ TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH0, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
{ TYPE_AC, CH0, FLD_UAC, UNIT_V, 26, 2, 10, false, 1 },
|
{ TYPE_AC, CH0, FLD_UAC, UNIT_V, 26, 2, 10, false, 1 },
|
||||||
{ TYPE_AC, CH0, FLD_IAC, UNIT_A, 34, 2, 100, false, 2 },
|
{ TYPE_AC, CH0, FLD_IAC, UNIT_A, 34, 2, 100, false, 2 },
|
||||||
@ -22,10 +22,10 @@ static const byteAssign_t byteAssignment[] = {
|
|||||||
{ TYPE_INV, CH0, FLD_T, UNIT_C, 38, 2, 10, true, 1 },
|
{ TYPE_INV, CH0, FLD_T, UNIT_C, 38, 2, 10, true, 1 },
|
||||||
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 18, 2, 1, false, 0 },
|
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 18, 2, 1, false, 0 },
|
||||||
|
|
||||||
{ TYPE_AC, CH0, FLD_YD, UNIT_WH, CALC_YD_CH0, 0, CMD_CALC, false, 0 },
|
{ TYPE_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 },
|
||||||
{ TYPE_AC, CH0, FLD_YT, UNIT_KWH, CALC_YT_CH0, 0, CMD_CALC, false, 3 },
|
{ TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 },
|
||||||
{ TYPE_AC, CH0, FLD_PDC, UNIT_W, CALC_PDC_CH0, 0, CMD_CALC, false, 1 },
|
{ TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 },
|
||||||
{ TYPE_AC, CH0, FLD_EFF, UNIT_PCT, CALC_EFF_CH0, 0, CMD_CALC, false, 3 }
|
{ TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 }
|
||||||
};
|
};
|
||||||
|
|
||||||
HMS_1CHv2::HMS_1CHv2(HoymilesRadio* radio, const uint64_t serial)
|
HMS_1CHv2::HMS_1CHv2(HoymilesRadio* radio, const uint64_t serial)
|
||||||
|
|||||||
@ -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 "HMS_2CH.h"
|
#include "HMS_2CH.h"
|
||||||
|
|
||||||
@ -10,14 +10,14 @@ static const byteAssign_t byteAssignment[] = {
|
|||||||
{ TYPE_DC, CH0, FLD_PDC, UNIT_W, 10, 2, 10, false, 1 },
|
{ TYPE_DC, CH0, FLD_PDC, UNIT_W, 10, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH0, FLD_YT, UNIT_KWH, 14, 4, 1000, false, 3 },
|
{ TYPE_DC, CH0, FLD_YT, UNIT_KWH, 14, 4, 1000, false, 3 },
|
||||||
{ TYPE_DC, CH0, FLD_YD, UNIT_WH, 22, 2, 1, false, 0 },
|
{ TYPE_DC, CH0, FLD_YD, UNIT_WH, 22, 2, 1, false, 0 },
|
||||||
{ TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH0, CMD_CALC, false, 3 },
|
{ TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH0, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
{ TYPE_DC, CH1, FLD_UDC, UNIT_V, 4, 2, 10, false, 1 },
|
{ TYPE_DC, CH1, FLD_UDC, UNIT_V, 4, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH1, FLD_IDC, UNIT_A, 8, 2, 100, false, 2 },
|
{ TYPE_DC, CH1, FLD_IDC, UNIT_A, 8, 2, 100, false, 2 },
|
||||||
{ TYPE_DC, CH1, FLD_PDC, UNIT_W, 12, 2, 10, false, 1 },
|
{ TYPE_DC, CH1, FLD_PDC, UNIT_W, 12, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH1, FLD_YT, UNIT_KWH, 18, 4, 1000, false, 3 },
|
{ TYPE_DC, CH1, FLD_YT, UNIT_KWH, 18, 4, 1000, false, 3 },
|
||||||
{ TYPE_DC, CH1, FLD_YD, UNIT_WH, 24, 2, 1, false, 0 },
|
{ TYPE_DC, CH1, FLD_YD, UNIT_WH, 24, 2, 1, false, 0 },
|
||||||
{ TYPE_DC, CH1, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH1, CMD_CALC, false, 3 },
|
{ TYPE_DC, CH1, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH1, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
{ TYPE_AC, CH0, FLD_UAC, UNIT_V, 26, 2, 10, false, 1 },
|
{ TYPE_AC, CH0, FLD_UAC, UNIT_V, 26, 2, 10, false, 1 },
|
||||||
{ TYPE_AC, CH0, FLD_IAC, UNIT_A, 34, 2, 100, false, 2 },
|
{ TYPE_AC, CH0, FLD_IAC, UNIT_A, 34, 2, 100, false, 2 },
|
||||||
@ -29,10 +29,10 @@ static const byteAssign_t byteAssignment[] = {
|
|||||||
{ TYPE_INV, CH0, FLD_T, UNIT_C, 38, 2, 10, true, 1 },
|
{ TYPE_INV, CH0, FLD_T, UNIT_C, 38, 2, 10, true, 1 },
|
||||||
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 40, 2, 1, false, 0 },
|
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 40, 2, 1, false, 0 },
|
||||||
|
|
||||||
{ TYPE_AC, CH0, FLD_YD, UNIT_WH, CALC_YD_CH0, 0, CMD_CALC, false, 0 },
|
{ TYPE_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 },
|
||||||
{ TYPE_AC, CH0, FLD_YT, UNIT_KWH, CALC_YT_CH0, 0, CMD_CALC, false, 3 },
|
{ TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 },
|
||||||
{ TYPE_AC, CH0, FLD_PDC, UNIT_W, CALC_PDC_CH0, 0, CMD_CALC, false, 1 },
|
{ TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 },
|
||||||
{ TYPE_AC, CH0, FLD_EFF, UNIT_PCT, CALC_EFF_CH0, 0, CMD_CALC, false, 3 }
|
{ TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 }
|
||||||
};
|
};
|
||||||
|
|
||||||
HMS_2CH::HMS_2CH(HoymilesRadio* radio, const uint64_t serial)
|
HMS_2CH::HMS_2CH(HoymilesRadio* radio, const uint64_t serial)
|
||||||
|
|||||||
@ -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 "HMS_4CH.h"
|
#include "HMS_4CH.h"
|
||||||
|
|
||||||
@ -10,28 +10,28 @@ static const byteAssign_t byteAssignment[] = {
|
|||||||
{ TYPE_DC, CH0, FLD_PDC, UNIT_W, 10, 2, 10, false, 1 },
|
{ TYPE_DC, CH0, FLD_PDC, UNIT_W, 10, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH0, FLD_YD, UNIT_WH, 22, 2, 1, false, 0 },
|
{ TYPE_DC, CH0, FLD_YD, UNIT_WH, 22, 2, 1, false, 0 },
|
||||||
{ TYPE_DC, CH0, FLD_YT, UNIT_KWH, 14, 4, 1000, false, 3 },
|
{ TYPE_DC, CH0, FLD_YT, UNIT_KWH, 14, 4, 1000, false, 3 },
|
||||||
{ TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH0, CMD_CALC, false, 3 },
|
{ TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH0, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
{ TYPE_DC, CH1, FLD_UDC, UNIT_V, 4, 2, 10, false, 1 },
|
{ TYPE_DC, CH1, FLD_UDC, UNIT_V, 4, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH1, FLD_IDC, UNIT_A, 8, 2, 100, false, 2 },
|
{ TYPE_DC, CH1, FLD_IDC, UNIT_A, 8, 2, 100, false, 2 },
|
||||||
{ TYPE_DC, CH1, FLD_PDC, UNIT_W, 12, 2, 10, false, 1 },
|
{ TYPE_DC, CH1, FLD_PDC, UNIT_W, 12, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH1, FLD_YD, UNIT_WH, 24, 2, 1, false, 0 },
|
{ TYPE_DC, CH1, FLD_YD, UNIT_WH, 24, 2, 1, false, 0 },
|
||||||
{ TYPE_DC, CH1, FLD_YT, UNIT_KWH, 18, 4, 1000, false, 3 },
|
{ TYPE_DC, CH1, FLD_YT, UNIT_KWH, 18, 4, 1000, false, 3 },
|
||||||
{ TYPE_DC, CH1, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH1, CMD_CALC, false, 3 },
|
{ TYPE_DC, CH1, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH1, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
{ TYPE_DC, CH2, FLD_UDC, UNIT_V, 26, 2, 10, false, 1 },
|
{ TYPE_DC, CH2, FLD_UDC, UNIT_V, 26, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH2, FLD_IDC, UNIT_A, 30, 2, 100, false, 2 },
|
{ TYPE_DC, CH2, FLD_IDC, UNIT_A, 30, 2, 100, false, 2 },
|
||||||
{ TYPE_DC, CH2, FLD_PDC, UNIT_W, 34, 2, 10, false, 1 },
|
{ TYPE_DC, CH2, FLD_PDC, UNIT_W, 34, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH2, FLD_YD, UNIT_WH, 46, 2, 1, false, 0 },
|
{ TYPE_DC, CH2, FLD_YD, UNIT_WH, 46, 2, 1, false, 0 },
|
||||||
{ TYPE_DC, CH2, FLD_YT, UNIT_KWH, 38, 4, 1000, false, 3 },
|
{ TYPE_DC, CH2, FLD_YT, UNIT_KWH, 38, 4, 1000, false, 3 },
|
||||||
{ TYPE_DC, CH2, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH2, CMD_CALC, false, 3 },
|
{ TYPE_DC, CH2, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH2, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
{ TYPE_DC, CH3, FLD_UDC, UNIT_V, 28, 2, 10, false, 1 },
|
{ TYPE_DC, CH3, FLD_UDC, UNIT_V, 28, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH3, FLD_IDC, UNIT_A, 32, 2, 100, false, 2 },
|
{ TYPE_DC, CH3, FLD_IDC, UNIT_A, 32, 2, 100, false, 2 },
|
||||||
{ TYPE_DC, CH3, FLD_PDC, UNIT_W, 36, 2, 10, false, 1 },
|
{ TYPE_DC, CH3, FLD_PDC, UNIT_W, 36, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH3, FLD_YD, UNIT_WH, 48, 2, 1, false, 0 },
|
{ TYPE_DC, CH3, FLD_YD, UNIT_WH, 48, 2, 1, false, 0 },
|
||||||
{ TYPE_DC, CH3, FLD_YT, UNIT_KWH, 42, 4, 1000, false, 3 },
|
{ TYPE_DC, CH3, FLD_YT, UNIT_KWH, 42, 4, 1000, false, 3 },
|
||||||
{ TYPE_DC, CH3, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH3, CMD_CALC, false, 3 },
|
{ TYPE_DC, CH3, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH3, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
{ TYPE_AC, CH0, FLD_UAC, UNIT_V, 50, 2, 10, false, 1 },
|
{ TYPE_AC, CH0, FLD_UAC, UNIT_V, 50, 2, 10, false, 1 },
|
||||||
{ TYPE_AC, CH0, FLD_IAC, UNIT_A, 58, 2, 100, false, 2 },
|
{ TYPE_AC, CH0, FLD_IAC, UNIT_A, 58, 2, 100, false, 2 },
|
||||||
@ -43,10 +43,10 @@ static const byteAssign_t byteAssignment[] = {
|
|||||||
{ TYPE_INV, CH0, FLD_T, UNIT_C, 62, 2, 10, true, 1 },
|
{ TYPE_INV, CH0, FLD_T, UNIT_C, 62, 2, 10, true, 1 },
|
||||||
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 64, 2, 1, false, 0 },
|
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 64, 2, 1, false, 0 },
|
||||||
|
|
||||||
{ TYPE_AC, CH0, FLD_YD, UNIT_WH, CALC_YD_CH0, 0, CMD_CALC, false, 0 },
|
{ TYPE_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 },
|
||||||
{ TYPE_AC, CH0, FLD_YT, UNIT_KWH, CALC_YT_CH0, 0, CMD_CALC, false, 3 },
|
{ TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 },
|
||||||
{ TYPE_AC, CH0, FLD_PDC, UNIT_W, CALC_PDC_CH0, 0, CMD_CALC, false, 1 },
|
{ TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 },
|
||||||
{ TYPE_AC, CH0, FLD_EFF, UNIT_PCT, CALC_EFF_CH0, 0, CMD_CALC, false, 3 }
|
{ TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 }
|
||||||
};
|
};
|
||||||
|
|
||||||
HMS_4CH::HMS_4CH(HoymilesRadio* radio, const uint64_t serial)
|
HMS_4CH::HMS_4CH(HoymilesRadio* radio, const uint64_t serial)
|
||||||
|
|||||||
@ -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 "HMT_4CH.h"
|
#include "HMT_4CH.h"
|
||||||
|
|
||||||
@ -10,28 +10,28 @@ static const byteAssign_t byteAssignment[] = {
|
|||||||
{ TYPE_DC, CH0, FLD_PDC, UNIT_W, 8, 2, 10, false, 1 },
|
{ TYPE_DC, CH0, FLD_PDC, UNIT_W, 8, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH0, FLD_YT, UNIT_KWH, 12, 4, 1000, false, 3 },
|
{ TYPE_DC, CH0, FLD_YT, UNIT_KWH, 12, 4, 1000, false, 3 },
|
||||||
{ TYPE_DC, CH0, FLD_YD, UNIT_WH, 20, 2, 1, false, 0 },
|
{ TYPE_DC, CH0, FLD_YD, UNIT_WH, 20, 2, 1, false, 0 },
|
||||||
{ TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH0, CMD_CALC, false, 3 },
|
{ TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH0, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
{ TYPE_DC, CH1, FLD_UDC, UNIT_V, 2, 2, 10, false, 1 },
|
{ TYPE_DC, CH1, FLD_UDC, UNIT_V, 2, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH1, FLD_IDC, UNIT_A, 6, 2, 100, false, 2 },
|
{ TYPE_DC, CH1, FLD_IDC, UNIT_A, 6, 2, 100, false, 2 },
|
||||||
{ TYPE_DC, CH1, FLD_PDC, UNIT_W, 10, 2, 10, false, 1 },
|
{ TYPE_DC, CH1, FLD_PDC, UNIT_W, 10, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH1, FLD_YT, UNIT_KWH, 16, 4, 1000, false, 3 },
|
{ TYPE_DC, CH1, FLD_YT, UNIT_KWH, 16, 4, 1000, false, 3 },
|
||||||
{ TYPE_DC, CH1, FLD_YD, UNIT_WH, 22, 2, 1, false, 0 },
|
{ TYPE_DC, CH1, FLD_YD, UNIT_WH, 22, 2, 1, false, 0 },
|
||||||
{ TYPE_DC, CH1, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH1, CMD_CALC, false, 3 },
|
{ TYPE_DC, CH1, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH1, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
{ TYPE_DC, CH2, FLD_UDC, UNIT_V, 24, 2, 10, false, 1 },
|
{ TYPE_DC, CH2, FLD_UDC, UNIT_V, 24, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH2, FLD_IDC, UNIT_A, 26, 2, 100, false, 2 },
|
{ TYPE_DC, CH2, FLD_IDC, UNIT_A, 26, 2, 100, false, 2 },
|
||||||
{ TYPE_DC, CH2, FLD_PDC, UNIT_W, 30, 2, 10, false, 1 },
|
{ TYPE_DC, CH2, FLD_PDC, UNIT_W, 30, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH2, FLD_YT, UNIT_KWH, 34, 4, 1000, false, 3 },
|
{ TYPE_DC, CH2, FLD_YT, UNIT_KWH, 34, 4, 1000, false, 3 },
|
||||||
{ TYPE_DC, CH2, FLD_YD, UNIT_WH, 42, 2, 1, false, 0 },
|
{ TYPE_DC, CH2, FLD_YD, UNIT_WH, 42, 2, 1, false, 0 },
|
||||||
{ TYPE_DC, CH2, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH2, CMD_CALC, false, 3 },
|
{ TYPE_DC, CH2, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH2, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
{ TYPE_DC, CH3, FLD_UDC, UNIT_V, 24, 2, 10, false, 1 },
|
{ TYPE_DC, CH3, FLD_UDC, UNIT_V, 24, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH3, FLD_IDC, UNIT_A, 28, 2, 100, false, 2 },
|
{ TYPE_DC, CH3, FLD_IDC, UNIT_A, 28, 2, 100, false, 2 },
|
||||||
{ TYPE_DC, CH3, FLD_PDC, UNIT_W, 32, 2, 10, false, 1 },
|
{ TYPE_DC, CH3, FLD_PDC, UNIT_W, 32, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH3, FLD_YT, UNIT_KWH, 38, 4, 1000, false, 3 },
|
{ TYPE_DC, CH3, FLD_YT, UNIT_KWH, 38, 4, 1000, false, 3 },
|
||||||
{ TYPE_DC, CH3, FLD_YD, UNIT_WH, 44, 2, 1, false, 0 },
|
{ TYPE_DC, CH3, FLD_YD, UNIT_WH, 44, 2, 1, false, 0 },
|
||||||
{ TYPE_DC, CH3, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH3, CMD_CALC, false, 3 },
|
{ TYPE_DC, CH3, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH3, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
{ TYPE_AC, CH0, FLD_UAC, UNIT_V, 74, 2, 10, false, 1 }, // dummy
|
{ TYPE_AC, CH0, FLD_UAC, UNIT_V, 74, 2, 10, false, 1 }, // dummy
|
||||||
{ TYPE_AC, CH0, FLD_UAC_1N, UNIT_V, 68, 2, 10, false, 1 },
|
{ TYPE_AC, CH0, FLD_UAC_1N, UNIT_V, 68, 2, 10, false, 1 },
|
||||||
@ -52,10 +52,10 @@ static const byteAssign_t byteAssignment[] = {
|
|||||||
{ TYPE_INV, CH0, FLD_T, UNIT_C, 94, 2, 10, true, 1 },
|
{ TYPE_INV, CH0, FLD_T, UNIT_C, 94, 2, 10, true, 1 },
|
||||||
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 96, 2, 1, false, 0 },
|
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 96, 2, 1, false, 0 },
|
||||||
|
|
||||||
{ TYPE_AC, CH0, FLD_YD, UNIT_WH, CALC_YD_CH0, 0, CMD_CALC, false, 0 },
|
{ TYPE_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 },
|
||||||
{ TYPE_AC, CH0, FLD_YT, UNIT_KWH, CALC_YT_CH0, 0, CMD_CALC, false, 3 },
|
{ TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 },
|
||||||
{ TYPE_AC, CH0, FLD_PDC, UNIT_W, CALC_PDC_CH0, 0, CMD_CALC, false, 1 },
|
{ TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 },
|
||||||
{ TYPE_AC, CH0, FLD_EFF, UNIT_PCT, CALC_EFF_CH0, 0, CMD_CALC, false, 3 }
|
{ TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 }
|
||||||
};
|
};
|
||||||
|
|
||||||
HMT_4CH::HMT_4CH(HoymilesRadio* radio, const uint64_t serial)
|
HMT_4CH::HMT_4CH(HoymilesRadio* radio, const uint64_t serial)
|
||||||
|
|||||||
@ -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 "HMT_6CH.h"
|
#include "HMT_6CH.h"
|
||||||
|
|
||||||
@ -10,42 +10,42 @@ static const byteAssign_t byteAssignment[] = {
|
|||||||
{ TYPE_DC, CH0, FLD_PDC, UNIT_W, 8, 2, 10, false, 1 },
|
{ TYPE_DC, CH0, FLD_PDC, UNIT_W, 8, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH0, FLD_YT, UNIT_KWH, 12, 4, 1000, false, 3 },
|
{ TYPE_DC, CH0, FLD_YT, UNIT_KWH, 12, 4, 1000, false, 3 },
|
||||||
{ TYPE_DC, CH0, FLD_YD, UNIT_WH, 20, 2, 1, false, 0 },
|
{ TYPE_DC, CH0, FLD_YD, UNIT_WH, 20, 2, 1, false, 0 },
|
||||||
{ TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH0, CMD_CALC, false, 3 },
|
{ TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH0, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
{ TYPE_DC, CH1, FLD_UDC, UNIT_V, 2, 2, 10, false, 1 },
|
{ TYPE_DC, CH1, FLD_UDC, UNIT_V, 2, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH1, FLD_IDC, UNIT_A, 6, 2, 100, false, 2 },
|
{ TYPE_DC, CH1, FLD_IDC, UNIT_A, 6, 2, 100, false, 2 },
|
||||||
{ TYPE_DC, CH1, FLD_PDC, UNIT_W, 10, 2, 10, false, 1 },
|
{ TYPE_DC, CH1, FLD_PDC, UNIT_W, 10, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH1, FLD_YT, UNIT_KWH, 16, 4, 1000, false, 3 },
|
{ TYPE_DC, CH1, FLD_YT, UNIT_KWH, 16, 4, 1000, false, 3 },
|
||||||
{ TYPE_DC, CH1, FLD_YD, UNIT_WH, 22, 2, 1, false, 0 },
|
{ TYPE_DC, CH1, FLD_YD, UNIT_WH, 22, 2, 1, false, 0 },
|
||||||
{ TYPE_DC, CH1, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH1, CMD_CALC, false, 3 },
|
{ TYPE_DC, CH1, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH1, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
{ TYPE_DC, CH2, FLD_UDC, UNIT_V, 24, 2, 10, false, 1 },
|
{ TYPE_DC, CH2, FLD_UDC, UNIT_V, 24, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH2, FLD_IDC, UNIT_A, 26, 2, 100, false, 2 },
|
{ TYPE_DC, CH2, FLD_IDC, UNIT_A, 26, 2, 100, false, 2 },
|
||||||
{ TYPE_DC, CH2, FLD_PDC, UNIT_W, 30, 2, 10, false, 1 },
|
{ TYPE_DC, CH2, FLD_PDC, UNIT_W, 30, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH2, FLD_YT, UNIT_KWH, 34, 4, 1000, false, 3 },
|
{ TYPE_DC, CH2, FLD_YT, UNIT_KWH, 34, 4, 1000, false, 3 },
|
||||||
{ TYPE_DC, CH2, FLD_YD, UNIT_WH, 42, 2, 1, false, 0 },
|
{ TYPE_DC, CH2, FLD_YD, UNIT_WH, 42, 2, 1, false, 0 },
|
||||||
{ TYPE_DC, CH2, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH2, CMD_CALC, false, 3 },
|
{ TYPE_DC, CH2, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH2, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
{ TYPE_DC, CH3, FLD_UDC, UNIT_V, 24, 2, 10, false, 1 },
|
{ TYPE_DC, CH3, FLD_UDC, UNIT_V, 24, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH3, FLD_IDC, UNIT_A, 28, 2, 100, false, 2 },
|
{ TYPE_DC, CH3, FLD_IDC, UNIT_A, 28, 2, 100, false, 2 },
|
||||||
{ TYPE_DC, CH3, FLD_PDC, UNIT_W, 32, 2, 10, false, 1 },
|
{ TYPE_DC, CH3, FLD_PDC, UNIT_W, 32, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH3, FLD_YT, UNIT_KWH, 38, 4, 1000, false, 3 },
|
{ TYPE_DC, CH3, FLD_YT, UNIT_KWH, 38, 4, 1000, false, 3 },
|
||||||
{ TYPE_DC, CH3, FLD_YD, UNIT_WH, 44, 2, 1, false, 0 },
|
{ TYPE_DC, CH3, FLD_YD, UNIT_WH, 44, 2, 1, false, 0 },
|
||||||
{ TYPE_DC, CH3, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH3, CMD_CALC, false, 3 },
|
{ TYPE_DC, CH3, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH3, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
{ TYPE_DC, CH4, FLD_UDC, UNIT_V, 46, 2, 10, false, 1 },
|
{ TYPE_DC, CH4, FLD_UDC, UNIT_V, 46, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH4, FLD_IDC, UNIT_A, 48, 2, 100, false, 2 },
|
{ TYPE_DC, CH4, FLD_IDC, UNIT_A, 48, 2, 100, false, 2 },
|
||||||
{ TYPE_DC, CH4, FLD_PDC, UNIT_W, 52, 2, 10, false, 1 },
|
{ TYPE_DC, CH4, FLD_PDC, UNIT_W, 52, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH4, FLD_YT, UNIT_KWH, 56, 4, 1000, false, 3 },
|
{ TYPE_DC, CH4, FLD_YT, UNIT_KWH, 56, 4, 1000, false, 3 },
|
||||||
{ TYPE_DC, CH4, FLD_YD, UNIT_WH, 64, 2, 1, false, 0 },
|
{ TYPE_DC, CH4, FLD_YD, UNIT_WH, 64, 2, 1, false, 0 },
|
||||||
{ TYPE_DC, CH4, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH4, CMD_CALC, false, 3 },
|
{ TYPE_DC, CH4, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH4, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
{ TYPE_DC, CH5, FLD_UDC, UNIT_V, 46, 2, 10, false, 1 },
|
{ TYPE_DC, CH5, FLD_UDC, UNIT_V, 46, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH5, FLD_IDC, UNIT_A, 50, 2, 100, false, 2 },
|
{ TYPE_DC, CH5, FLD_IDC, UNIT_A, 50, 2, 100, false, 2 },
|
||||||
{ TYPE_DC, CH5, FLD_PDC, UNIT_W, 54, 2, 10, false, 1 },
|
{ TYPE_DC, CH5, FLD_PDC, UNIT_W, 54, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH5, FLD_YT, UNIT_KWH, 60, 4, 1000, false, 3 },
|
{ TYPE_DC, CH5, FLD_YT, UNIT_KWH, 60, 4, 1000, false, 3 },
|
||||||
{ TYPE_DC, CH5, FLD_YD, UNIT_WH, 66, 2, 1, false, 0 },
|
{ TYPE_DC, CH5, FLD_YD, UNIT_WH, 66, 2, 1, false, 0 },
|
||||||
{ TYPE_DC, CH5, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH5, CMD_CALC, false, 3 },
|
{ TYPE_DC, CH5, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH5, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
{ TYPE_AC, CH0, FLD_UAC, UNIT_V, 74, 2, 10, false, 1 }, // dummy
|
{ TYPE_AC, CH0, FLD_UAC, UNIT_V, 74, 2, 10, false, 1 }, // dummy
|
||||||
{ TYPE_AC, CH0, FLD_UAC_1N, UNIT_V, 68, 2, 10, false, 1 },
|
{ TYPE_AC, CH0, FLD_UAC_1N, UNIT_V, 68, 2, 10, false, 1 },
|
||||||
@ -57,7 +57,7 @@ static const byteAssign_t byteAssignment[] = {
|
|||||||
{ TYPE_AC, CH0, FLD_F, UNIT_HZ, 80, 2, 100, false, 2 },
|
{ TYPE_AC, CH0, FLD_F, UNIT_HZ, 80, 2, 100, false, 2 },
|
||||||
{ TYPE_AC, CH0, FLD_PAC, UNIT_W, 82, 2, 10, false, 1 },
|
{ TYPE_AC, CH0, FLD_PAC, UNIT_W, 82, 2, 10, false, 1 },
|
||||||
{ TYPE_AC, CH0, FLD_Q, UNIT_VAR, 84, 2, 10, true, 1 },
|
{ TYPE_AC, CH0, FLD_Q, UNIT_VAR, 84, 2, 10, true, 1 },
|
||||||
{ TYPE_AC, CH0, FLD_IAC, UNIT_A, 86, 2, 100, false, 2 }, // dummy
|
{ TYPE_AC, CH0, FLD_IAC, UNIT_A, CALC_TOTAL_IAC, 0, CMD_CALC, false, 2 },
|
||||||
{ TYPE_AC, CH0, FLD_IAC_1, UNIT_A, 86, 2, 100, false, 2 },
|
{ TYPE_AC, CH0, FLD_IAC_1, UNIT_A, 86, 2, 100, false, 2 },
|
||||||
{ TYPE_AC, CH0, FLD_IAC_2, UNIT_A, 88, 2, 100, false, 2 },
|
{ TYPE_AC, CH0, FLD_IAC_2, UNIT_A, 88, 2, 100, false, 2 },
|
||||||
{ TYPE_AC, CH0, FLD_IAC_3, UNIT_A, 90, 2, 100, false, 2 },
|
{ TYPE_AC, CH0, FLD_IAC_3, UNIT_A, 90, 2, 100, false, 2 },
|
||||||
@ -66,10 +66,10 @@ static const byteAssign_t byteAssignment[] = {
|
|||||||
{ TYPE_INV, CH0, FLD_T, UNIT_C, 94, 2, 10, true, 1 },
|
{ TYPE_INV, CH0, FLD_T, UNIT_C, 94, 2, 10, true, 1 },
|
||||||
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 96, 2, 1, false, 0 },
|
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 96, 2, 1, false, 0 },
|
||||||
|
|
||||||
{ TYPE_AC, CH0, FLD_YD, UNIT_WH, CALC_YD_CH0, 0, CMD_CALC, false, 0 },
|
{ TYPE_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 },
|
||||||
{ TYPE_AC, CH0, FLD_YT, UNIT_KWH, CALC_YT_CH0, 0, CMD_CALC, false, 3 },
|
{ TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 },
|
||||||
{ TYPE_AC, CH0, FLD_PDC, UNIT_W, CALC_PDC_CH0, 0, CMD_CALC, false, 1 },
|
{ TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 },
|
||||||
{ TYPE_AC, CH0, FLD_EFF, UNIT_PCT, CALC_EFF_CH0, 0, CMD_CALC, false, 3 }
|
{ TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 }
|
||||||
};
|
};
|
||||||
|
|
||||||
HMT_6CH::HMT_6CH(HoymilesRadio* radio, const uint64_t serial)
|
HMT_6CH::HMT_6CH(HoymilesRadio* radio, const uint64_t serial)
|
||||||
|
|||||||
@ -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 "HM_1CH.h"
|
#include "HM_1CH.h"
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ static const byteAssign_t byteAssignment[] = {
|
|||||||
{ TYPE_DC, CH0, FLD_PDC, UNIT_W, 6, 2, 10, false, 1 },
|
{ TYPE_DC, CH0, FLD_PDC, UNIT_W, 6, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH0, FLD_YD, UNIT_WH, 12, 2, 1, false, 0 },
|
{ TYPE_DC, CH0, FLD_YD, UNIT_WH, 12, 2, 1, false, 0 },
|
||||||
{ TYPE_DC, CH0, FLD_YT, UNIT_KWH, 8, 4, 1000, false, 3 },
|
{ TYPE_DC, CH0, FLD_YT, UNIT_KWH, 8, 4, 1000, false, 3 },
|
||||||
{ TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH0, CMD_CALC, false, 3 },
|
{ TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH0, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
{ TYPE_AC, CH0, FLD_UAC, UNIT_V, 14, 2, 10, false, 1 },
|
{ TYPE_AC, CH0, FLD_UAC, UNIT_V, 14, 2, 10, false, 1 },
|
||||||
{ TYPE_AC, CH0, FLD_IAC, UNIT_A, 22, 2, 100, false, 2 },
|
{ TYPE_AC, CH0, FLD_IAC, UNIT_A, 22, 2, 100, false, 2 },
|
||||||
@ -22,10 +22,10 @@ static const byteAssign_t byteAssignment[] = {
|
|||||||
{ TYPE_INV, CH0, FLD_T, UNIT_C, 26, 2, 10, true, 1 },
|
{ TYPE_INV, CH0, FLD_T, UNIT_C, 26, 2, 10, true, 1 },
|
||||||
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 28, 2, 1, false, 0 },
|
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 28, 2, 1, false, 0 },
|
||||||
|
|
||||||
{ TYPE_AC, CH0, FLD_YD, UNIT_WH, CALC_YD_CH0, 0, CMD_CALC, false, 0 },
|
{ TYPE_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 },
|
||||||
{ TYPE_AC, CH0, FLD_YT, UNIT_KWH, CALC_YT_CH0, 0, CMD_CALC, false, 3 },
|
{ TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 },
|
||||||
{ TYPE_AC, CH0, FLD_PDC, UNIT_W, CALC_PDC_CH0, 0, CMD_CALC, false, 1 },
|
{ TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 },
|
||||||
{ TYPE_AC, CH0, FLD_EFF, UNIT_PCT, CALC_EFF_CH0, 0, CMD_CALC, false, 3 }
|
{ TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 }
|
||||||
};
|
};
|
||||||
|
|
||||||
HM_1CH::HM_1CH(HoymilesRadio* radio, const uint64_t serial)
|
HM_1CH::HM_1CH(HoymilesRadio* radio, const uint64_t serial)
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
// 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 "HM_2CH.h"
|
#include "HM_2CH.h"
|
||||||
|
|
||||||
@ -11,14 +11,14 @@ static const byteAssign_t byteAssignment[] = {
|
|||||||
{ TYPE_DC, CH0, FLD_PDC, UNIT_W, 6, 2, 10, false, 1 },
|
{ TYPE_DC, CH0, FLD_PDC, UNIT_W, 6, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH0, FLD_YD, UNIT_WH, 22, 2, 1, false, 0 },
|
{ TYPE_DC, CH0, FLD_YD, UNIT_WH, 22, 2, 1, false, 0 },
|
||||||
{ TYPE_DC, CH0, FLD_YT, UNIT_KWH, 14, 4, 1000, false, 3 },
|
{ TYPE_DC, CH0, FLD_YT, UNIT_KWH, 14, 4, 1000, false, 3 },
|
||||||
{ TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH0, CMD_CALC, false, 3 },
|
{ TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH0, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
{ TYPE_DC, CH1, FLD_UDC, UNIT_V, 8, 2, 10, false, 1 },
|
{ TYPE_DC, CH1, FLD_UDC, UNIT_V, 8, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH1, FLD_IDC, UNIT_A, 10, 2, 100, false, 2 },
|
{ TYPE_DC, CH1, FLD_IDC, UNIT_A, 10, 2, 100, false, 2 },
|
||||||
{ TYPE_DC, CH1, FLD_PDC, UNIT_W, 12, 2, 10, false, 1 },
|
{ TYPE_DC, CH1, FLD_PDC, UNIT_W, 12, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH1, FLD_YD, UNIT_WH, 24, 2, 1, false, 0 },
|
{ TYPE_DC, CH1, FLD_YD, UNIT_WH, 24, 2, 1, false, 0 },
|
||||||
{ TYPE_DC, CH1, FLD_YT, UNIT_KWH, 18, 4, 1000, false, 3 },
|
{ TYPE_DC, CH1, FLD_YT, UNIT_KWH, 18, 4, 1000, false, 3 },
|
||||||
{ TYPE_DC, CH1, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH1, CMD_CALC, false, 3 },
|
{ TYPE_DC, CH1, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH1, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
{ TYPE_AC, CH0, FLD_UAC, UNIT_V, 26, 2, 10, false, 1 },
|
{ TYPE_AC, CH0, FLD_UAC, UNIT_V, 26, 2, 10, false, 1 },
|
||||||
{ TYPE_AC, CH0, FLD_IAC, UNIT_A, 34, 2, 100, false, 2 },
|
{ TYPE_AC, CH0, FLD_IAC, UNIT_A, 34, 2, 100, false, 2 },
|
||||||
@ -30,10 +30,10 @@ static const byteAssign_t byteAssignment[] = {
|
|||||||
{ TYPE_INV, CH0, FLD_T, UNIT_C, 38, 2, 10, true, 1 },
|
{ TYPE_INV, CH0, FLD_T, UNIT_C, 38, 2, 10, true, 1 },
|
||||||
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 40, 2, 1, false, 0 },
|
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 40, 2, 1, false, 0 },
|
||||||
|
|
||||||
{ TYPE_AC, CH0, FLD_YD, UNIT_WH, CALC_YD_CH0, 0, CMD_CALC, false, 0 },
|
{ TYPE_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 },
|
||||||
{ TYPE_AC, CH0, FLD_YT, UNIT_KWH, CALC_YT_CH0, 0, CMD_CALC, false, 3 },
|
{ TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 },
|
||||||
{ TYPE_AC, CH0, FLD_PDC, UNIT_W, CALC_PDC_CH0, 0, CMD_CALC, false, 1 },
|
{ TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 },
|
||||||
{ TYPE_AC, CH0, FLD_EFF, UNIT_PCT, CALC_EFF_CH0, 0, CMD_CALC, false, 3 }
|
{ TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 }
|
||||||
};
|
};
|
||||||
|
|
||||||
HM_2CH::HM_2CH(HoymilesRadio* radio, const uint64_t serial)
|
HM_2CH::HM_2CH(HoymilesRadio* radio, const uint64_t serial)
|
||||||
|
|||||||
@ -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 "HM_4CH.h"
|
#include "HM_4CH.h"
|
||||||
|
|
||||||
@ -10,28 +10,28 @@ static const byteAssign_t byteAssignment[] = {
|
|||||||
{ TYPE_DC, CH0, FLD_PDC, UNIT_W, 8, 2, 10, false, 1 },
|
{ TYPE_DC, CH0, FLD_PDC, UNIT_W, 8, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH0, FLD_YD, UNIT_WH, 20, 2, 1, false, 0 },
|
{ TYPE_DC, CH0, FLD_YD, UNIT_WH, 20, 2, 1, false, 0 },
|
||||||
{ TYPE_DC, CH0, FLD_YT, UNIT_KWH, 12, 4, 1000, false, 3 },
|
{ TYPE_DC, CH0, FLD_YT, UNIT_KWH, 12, 4, 1000, false, 3 },
|
||||||
{ TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH0, CMD_CALC, false, 3 },
|
{ TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH0, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
{ TYPE_DC, CH1, FLD_UDC, UNIT_V, CALC_UDC_CH, CH0, CMD_CALC, false, 1 },
|
{ TYPE_DC, CH1, FLD_UDC, UNIT_V, CALC_CH_UDC, CH0, CMD_CALC, false, 1 },
|
||||||
{ TYPE_DC, CH1, FLD_IDC, UNIT_A, 6, 2, 100, false, 2 },
|
{ TYPE_DC, CH1, FLD_IDC, UNIT_A, 6, 2, 100, false, 2 },
|
||||||
{ TYPE_DC, CH1, FLD_PDC, UNIT_W, 10, 2, 10, false, 1 },
|
{ TYPE_DC, CH1, FLD_PDC, UNIT_W, 10, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH1, FLD_YD, UNIT_WH, 22, 2, 1, false, 0 },
|
{ TYPE_DC, CH1, FLD_YD, UNIT_WH, 22, 2, 1, false, 0 },
|
||||||
{ TYPE_DC, CH1, FLD_YT, UNIT_KWH, 16, 4, 1000, false, 3 },
|
{ TYPE_DC, CH1, FLD_YT, UNIT_KWH, 16, 4, 1000, false, 3 },
|
||||||
{ TYPE_DC, CH1, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH1, CMD_CALC, false, 3 },
|
{ TYPE_DC, CH1, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH1, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
{ TYPE_DC, CH2, FLD_UDC, UNIT_V, 24, 2, 10, false, 1 },
|
{ TYPE_DC, CH2, FLD_UDC, UNIT_V, 24, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH2, FLD_IDC, UNIT_A, 26, 2, 100, false, 2 },
|
{ TYPE_DC, CH2, FLD_IDC, UNIT_A, 26, 2, 100, false, 2 },
|
||||||
{ TYPE_DC, CH2, FLD_PDC, UNIT_W, 30, 2, 10, false, 1 },
|
{ TYPE_DC, CH2, FLD_PDC, UNIT_W, 30, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH2, FLD_YD, UNIT_WH, 42, 2, 1, false, 0 },
|
{ TYPE_DC, CH2, FLD_YD, UNIT_WH, 42, 2, 1, false, 0 },
|
||||||
{ TYPE_DC, CH2, FLD_YT, UNIT_KWH, 34, 4, 1000, false, 3 },
|
{ TYPE_DC, CH2, FLD_YT, UNIT_KWH, 34, 4, 1000, false, 3 },
|
||||||
{ TYPE_DC, CH2, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH2, CMD_CALC, false, 3 },
|
{ TYPE_DC, CH2, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH2, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
{ TYPE_DC, CH3, FLD_UDC, UNIT_V, CALC_UDC_CH, CH2, CMD_CALC, false, 1 },
|
{ TYPE_DC, CH3, FLD_UDC, UNIT_V, CALC_CH_UDC, CH2, CMD_CALC, false, 1 },
|
||||||
{ TYPE_DC, CH3, FLD_IDC, UNIT_A, 28, 2, 100, false, 2 },
|
{ TYPE_DC, CH3, FLD_IDC, UNIT_A, 28, 2, 100, false, 2 },
|
||||||
{ TYPE_DC, CH3, FLD_PDC, UNIT_W, 32, 2, 10, false, 1 },
|
{ TYPE_DC, CH3, FLD_PDC, UNIT_W, 32, 2, 10, false, 1 },
|
||||||
{ TYPE_DC, CH3, FLD_YD, UNIT_WH, 44, 2, 1, false, 0 },
|
{ TYPE_DC, CH3, FLD_YD, UNIT_WH, 44, 2, 1, false, 0 },
|
||||||
{ TYPE_DC, CH3, FLD_YT, UNIT_KWH, 38, 4, 1000, false, 3 },
|
{ TYPE_DC, CH3, FLD_YT, UNIT_KWH, 38, 4, 1000, false, 3 },
|
||||||
{ TYPE_DC, CH3, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH3, CMD_CALC, false, 3 },
|
{ TYPE_DC, CH3, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH3, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
{ TYPE_AC, CH0, FLD_UAC, UNIT_V, 46, 2, 10, false, 1 },
|
{ TYPE_AC, CH0, FLD_UAC, UNIT_V, 46, 2, 10, false, 1 },
|
||||||
{ TYPE_AC, CH0, FLD_IAC, UNIT_A, 54, 2, 100, false, 2 },
|
{ TYPE_AC, CH0, FLD_IAC, UNIT_A, 54, 2, 100, false, 2 },
|
||||||
@ -43,10 +43,10 @@ static const byteAssign_t byteAssignment[] = {
|
|||||||
{ TYPE_INV, CH0, FLD_T, UNIT_C, 58, 2, 10, true, 1 },
|
{ TYPE_INV, CH0, FLD_T, UNIT_C, 58, 2, 10, true, 1 },
|
||||||
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 60, 2, 1, false, 0 },
|
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 60, 2, 1, false, 0 },
|
||||||
|
|
||||||
{ TYPE_AC, CH0, FLD_YD, UNIT_WH, CALC_YD_CH0, 0, CMD_CALC, false, 0 },
|
{ TYPE_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 },
|
||||||
{ TYPE_AC, CH0, FLD_YT, UNIT_KWH, CALC_YT_CH0, 0, CMD_CALC, false, 3 },
|
{ TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 },
|
||||||
{ TYPE_AC, CH0, FLD_PDC, UNIT_W, CALC_PDC_CH0, 0, CMD_CALC, false, 1 },
|
{ TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 },
|
||||||
{ TYPE_AC, CH0, FLD_EFF, UNIT_PCT, CALC_EFF_CH0, 0, CMD_CALC, false, 3 }
|
{ TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 }
|
||||||
};
|
};
|
||||||
|
|
||||||
HM_4CH::HM_4CH(HoymilesRadio* radio, const uint64_t serial)
|
HM_4CH::HM_4CH(HoymilesRadio* radio, const uint64_t serial)
|
||||||
|
|||||||
@ -28,9 +28,11 @@ const devInfo_t devInfo[] = {
|
|||||||
{ { 0x10, 0x12, 0x30, ALL }, 1500, "HM-1500-4T" },
|
{ { 0x10, 0x12, 0x30, ALL }, 1500, "HM-1500-4T" },
|
||||||
{ { 0x10, 0x10, 0x10, 0x15 }, static_cast<uint16_t>(300 * 0.7), "HM-300-1T" }, // HM-300 factory limitted to 70%
|
{ { 0x10, 0x10, 0x10, 0x15 }, static_cast<uint16_t>(300 * 0.7), "HM-300-1T" }, // HM-300 factory limitted to 70%
|
||||||
|
|
||||||
|
{ { 0x10, 0x20, 0x11, ALL }, 300, "HMS-300-1T" }, // 00
|
||||||
{ { 0x10, 0x20, 0x21, ALL }, 350, "HMS-350-1T" }, // 00
|
{ { 0x10, 0x20, 0x21, ALL }, 350, "HMS-350-1T" }, // 00
|
||||||
{ { 0x10, 0x20, 0x41, ALL }, 400, "HMS-400-1T" }, // 00
|
{ { 0x10, 0x20, 0x41, ALL }, 400, "HMS-400-1T" }, // 00
|
||||||
{ { 0x10, 0x10, 0x51, ALL }, 450, "HMS-450-1T" }, // 01
|
{ { 0x10, 0x10, 0x51, ALL }, 450, "HMS-450-1T" }, // 01
|
||||||
|
{ { 0x10, 0x20, 0x51, ALL }, 450, "HMS-450-1T" }, // 03
|
||||||
{ { 0x10, 0x10, 0x71, ALL }, 500, "HMS-500-1T" }, // 02
|
{ { 0x10, 0x10, 0x71, ALL }, 500, "HMS-500-1T" }, // 02
|
||||||
{ { 0x10, 0x20, 0x71, ALL }, 500, "HMS-500-1T v2" }, // 02
|
{ { 0x10, 0x20, 0x71, ALL }, 500, "HMS-500-1T v2" }, // 02
|
||||||
{ { 0x10, 0x21, 0x11, ALL }, 600, "HMS-600-2T" }, // 01
|
{ { 0x10, 0x21, 0x11, ALL }, 600, "HMS-600-2T" }, // 01
|
||||||
@ -47,6 +49,7 @@ const devInfo_t devInfo[] = {
|
|||||||
|
|
||||||
{ { 0x10, 0x32, 0x41, ALL }, 1600, "HMT-1600-4T" }, // 00
|
{ { 0x10, 0x32, 0x41, ALL }, 1600, "HMT-1600-4T" }, // 00
|
||||||
{ { 0x10, 0x32, 0x51, ALL }, 1800, "HMT-1800-4T" }, // 00
|
{ { 0x10, 0x32, 0x51, ALL }, 1800, "HMT-1800-4T" }, // 00
|
||||||
|
{ { 0x10, 0x32, 0x71, ALL }, 2000, "HMT-2000-4T" }, // 0
|
||||||
|
|
||||||
{ { 0x10, 0x33, 0x11, ALL }, 1800, "HMT-1800-6T" }, // 01
|
{ { 0x10, 0x33, 0x11, ALL }, 1800, "HMT-1800-6T" }, // 01
|
||||||
{ { 0x10, 0x33, 0x31, ALL }, 2250, "HMT-2250-6T" } // 01
|
{ { 0x10, 0x33, 0x31, ALL }, 2250, "HMT-2250-6T" } // 01
|
||||||
|
|||||||
@ -5,12 +5,13 @@
|
|||||||
#include "StatisticsParser.h"
|
#include "StatisticsParser.h"
|
||||||
#include "../Hoymiles.h"
|
#include "../Hoymiles.h"
|
||||||
|
|
||||||
static float calcYieldTotalCh0(StatisticsParser* iv, uint8_t arg0);
|
static float calcTotalYieldTotal(StatisticsParser* iv, uint8_t arg0);
|
||||||
static float calcYieldDayCh0(StatisticsParser* iv, uint8_t arg0);
|
static float calcTotalYieldDay(StatisticsParser* iv, uint8_t arg0);
|
||||||
static float calcUdcCh(StatisticsParser* iv, uint8_t arg0);
|
static float calcChUdc(StatisticsParser* iv, uint8_t arg0);
|
||||||
static float calcPowerDcCh0(StatisticsParser* iv, uint8_t arg0);
|
static float calcTotalPowerDc(StatisticsParser* iv, uint8_t arg0);
|
||||||
static float calcEffiencyCh0(StatisticsParser* iv, uint8_t arg0);
|
static float calcTotalEffiency(StatisticsParser* iv, uint8_t arg0);
|
||||||
static float calcIrradiation(StatisticsParser* iv, uint8_t arg0);
|
static float calcChIrradiation(StatisticsParser* iv, uint8_t arg0);
|
||||||
|
static float calcTotalCurrentAc(StatisticsParser* iv, uint8_t arg0);
|
||||||
|
|
||||||
using func_t = float(StatisticsParser*, uint8_t);
|
using func_t = float(StatisticsParser*, uint8_t);
|
||||||
|
|
||||||
@ -20,12 +21,13 @@ struct calcFunc_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const calcFunc_t calcFunctions[] = {
|
const calcFunc_t calcFunctions[] = {
|
||||||
{ CALC_YT_CH0, &calcYieldTotalCh0 },
|
{ CALC_TOTAL_YT, &calcTotalYieldTotal },
|
||||||
{ CALC_YD_CH0, &calcYieldDayCh0 },
|
{ CALC_TOTAL_YD, &calcTotalYieldDay },
|
||||||
{ CALC_UDC_CH, &calcUdcCh },
|
{ CALC_CH_UDC, &calcChUdc },
|
||||||
{ CALC_PDC_CH0, &calcPowerDcCh0 },
|
{ CALC_TOTAL_PDC, &calcTotalPowerDc },
|
||||||
{ CALC_EFF_CH0, &calcEffiencyCh0 },
|
{ CALC_TOTAL_EFF, &calcTotalEffiency },
|
||||||
{ CALC_IRR_CH, &calcIrradiation }
|
{ CALC_CH_IRR, &calcChIrradiation },
|
||||||
|
{ CALC_TOTAL_IAC, &calcTotalCurrentAc }
|
||||||
};
|
};
|
||||||
|
|
||||||
const FieldId_t runtimeFields[] = {
|
const FieldId_t runtimeFields[] = {
|
||||||
@ -386,7 +388,7 @@ void StatisticsParser::resetYieldDayCorrection()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static float calcYieldTotalCh0(StatisticsParser* iv, uint8_t arg0)
|
static float calcTotalYieldTotal(StatisticsParser* iv, uint8_t arg0)
|
||||||
{
|
{
|
||||||
float yield = 0;
|
float yield = 0;
|
||||||
for (auto& channel : iv->getChannelsByType(TYPE_DC)) {
|
for (auto& channel : iv->getChannelsByType(TYPE_DC)) {
|
||||||
@ -395,7 +397,7 @@ static float calcYieldTotalCh0(StatisticsParser* iv, uint8_t arg0)
|
|||||||
return yield;
|
return yield;
|
||||||
}
|
}
|
||||||
|
|
||||||
static float calcYieldDayCh0(StatisticsParser* iv, uint8_t arg0)
|
static float calcTotalYieldDay(StatisticsParser* iv, uint8_t arg0)
|
||||||
{
|
{
|
||||||
float yield = 0;
|
float yield = 0;
|
||||||
for (auto& channel : iv->getChannelsByType(TYPE_DC)) {
|
for (auto& channel : iv->getChannelsByType(TYPE_DC)) {
|
||||||
@ -405,12 +407,12 @@ static float calcYieldDayCh0(StatisticsParser* iv, uint8_t arg0)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// arg0 = channel of source
|
// arg0 = channel of source
|
||||||
static float calcUdcCh(StatisticsParser* iv, uint8_t arg0)
|
static float calcChUdc(StatisticsParser* iv, uint8_t arg0)
|
||||||
{
|
{
|
||||||
return iv->getChannelFieldValue(TYPE_DC, static_cast<ChannelNum_t>(arg0), FLD_UDC);
|
return iv->getChannelFieldValue(TYPE_DC, static_cast<ChannelNum_t>(arg0), FLD_UDC);
|
||||||
}
|
}
|
||||||
|
|
||||||
static float calcPowerDcCh0(StatisticsParser* iv, uint8_t arg0)
|
static float calcTotalPowerDc(StatisticsParser* iv, uint8_t arg0)
|
||||||
{
|
{
|
||||||
float dcPower = 0;
|
float dcPower = 0;
|
||||||
for (auto& channel : iv->getChannelsByType(TYPE_DC)) {
|
for (auto& channel : iv->getChannelsByType(TYPE_DC)) {
|
||||||
@ -419,8 +421,7 @@ static float calcPowerDcCh0(StatisticsParser* iv, uint8_t arg0)
|
|||||||
return dcPower;
|
return dcPower;
|
||||||
}
|
}
|
||||||
|
|
||||||
// arg0 = channel
|
static float calcTotalEffiency(StatisticsParser* iv, uint8_t arg0)
|
||||||
static float calcEffiencyCh0(StatisticsParser* iv, uint8_t arg0)
|
|
||||||
{
|
{
|
||||||
float acPower = 0;
|
float acPower = 0;
|
||||||
for (auto& channel : iv->getChannelsByType(TYPE_AC)) {
|
for (auto& channel : iv->getChannelsByType(TYPE_AC)) {
|
||||||
@ -439,7 +440,7 @@ static float calcEffiencyCh0(StatisticsParser* iv, uint8_t arg0)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// arg0 = channel
|
// arg0 = channel
|
||||||
static float calcIrradiation(StatisticsParser* iv, uint8_t arg0)
|
static float calcChIrradiation(StatisticsParser* iv, uint8_t arg0)
|
||||||
{
|
{
|
||||||
if (nullptr != iv) {
|
if (nullptr != iv) {
|
||||||
if (iv->getStringMaxPower(arg0) > 0)
|
if (iv->getStringMaxPower(arg0) > 0)
|
||||||
@ -447,3 +448,12 @@ static float calcIrradiation(StatisticsParser* iv, uint8_t arg0)
|
|||||||
}
|
}
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static float calcTotalCurrentAc(StatisticsParser* iv, uint8_t arg0)
|
||||||
|
{
|
||||||
|
float acCurrent = 0;
|
||||||
|
acCurrent += iv->getChannelFieldValue(TYPE_AC, CH0, FLD_IAC_1);
|
||||||
|
acCurrent += iv->getChannelFieldValue(TYPE_AC, CH0, FLD_IAC_2);
|
||||||
|
acCurrent += iv->getChannelFieldValue(TYPE_AC, CH0, FLD_IAC_3);
|
||||||
|
return acCurrent;
|
||||||
|
}
|
||||||
|
|||||||
@ -55,12 +55,13 @@ const char* const fields[] = { "Voltage", "Current", "Power", "YieldDay", "Yield
|
|||||||
|
|
||||||
// indices to calculation functions, defined in hmInverter.h
|
// indices to calculation functions, defined in hmInverter.h
|
||||||
enum {
|
enum {
|
||||||
CALC_YT_CH0 = 0,
|
CALC_TOTAL_YT = 0,
|
||||||
CALC_YD_CH0,
|
CALC_TOTAL_YD,
|
||||||
CALC_UDC_CH,
|
CALC_CH_UDC,
|
||||||
CALC_PDC_CH0,
|
CALC_TOTAL_PDC,
|
||||||
CALC_EFF_CH0,
|
CALC_TOTAL_EFF,
|
||||||
CALC_IRR_CH
|
CALC_CH_IRR,
|
||||||
|
CALC_TOTAL_IAC
|
||||||
};
|
};
|
||||||
enum { CMD_CALC = 0xffff };
|
enum { CMD_CALC = 0xffff };
|
||||||
|
|
||||||
|
|||||||
6
partitions_custom_16mb.csv
Normal file
6
partitions_custom_16mb.csv
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
|
nvs, data, nvs, 0x9000, 0x5000
|
||||||
|
otadata, data, ota, 0xE000, 0x2000
|
||||||
|
app0, app, ota_0, 0x10000, 0x7E0000
|
||||||
|
app1, app, ota_1, 0x7F0000, 0x7E0000
|
||||||
|
spiffs, data, spiffs, 0xFD0000, 0x30000
|
||||||
|
@ -36,9 +36,9 @@ build_unflags =
|
|||||||
-std=gnu++11
|
-std=gnu++11
|
||||||
|
|
||||||
lib_deps =
|
lib_deps =
|
||||||
https://github.com/yubox-node-org/ESPAsyncWebServer
|
mathieucarbou/ESP Async WebServer @ 2.7.0
|
||||||
bblanchon/ArduinoJson @ ^6.21.5
|
bblanchon/ArduinoJson @ ^6.21.5
|
||||||
https://github.com/bertmelis/espMqttClient.git#v1.5.0
|
https://github.com/bertmelis/espMqttClient.git#v1.6.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
|
||||||
@ -54,7 +54,7 @@ extra_scripts =
|
|||||||
pre:pio-scripts/patch_apply.py
|
pre:pio-scripts/patch_apply.py
|
||||||
post:pio-scripts/create_factory_bin.py
|
post:pio-scripts/create_factory_bin.py
|
||||||
|
|
||||||
board_build.partitions = partitions_custom.csv
|
board_build.partitions = partitions_custom_4mb.csv
|
||||||
board_build.filesystem = littlefs
|
board_build.filesystem = littlefs
|
||||||
board_build.embed_files =
|
board_build.embed_files =
|
||||||
webapp_dist/index.html.gz
|
webapp_dist/index.html.gz
|
||||||
@ -80,6 +80,16 @@ board = esp32dev
|
|||||||
build_flags = ${env.build_flags}
|
build_flags = ${env.build_flags}
|
||||||
|
|
||||||
|
|
||||||
|
[env:generic_esp32_16mb_psram]
|
||||||
|
board = esp32dev
|
||||||
|
board_build.flash_mode = qio
|
||||||
|
board_build.partitions = partitions_custom_16mb.csv
|
||||||
|
board_upload.flash_size = 16MB
|
||||||
|
build_flags = ${env.build_flags}
|
||||||
|
-DBOARD_HAS_PSRAM
|
||||||
|
-mfix-esp32-psram-cache-issue
|
||||||
|
|
||||||
|
|
||||||
[env:generic_esp32c3]
|
[env:generic_esp32c3]
|
||||||
board = esp32-c3-devkitc-02
|
board = esp32-c3-devkitc-02
|
||||||
custom_patches = ${env.custom_patches},esp32c3
|
custom_patches = ${env.custom_patches},esp32c3
|
||||||
|
|||||||
@ -56,7 +56,8 @@ void BatteryStats::getLiveViewData(JsonVariant& root) const
|
|||||||
root[F("manufacturer")] = _manufacturer;
|
root[F("manufacturer")] = _manufacturer;
|
||||||
root[F("data_age")] = getAgeSeconds();
|
root[F("data_age")] = getAgeSeconds();
|
||||||
|
|
||||||
addLiveViewValue(root, "SoC", _SoC, "%", 0);
|
addLiveViewValue(root, "SoC", _soc, "%", _socPrecision);
|
||||||
|
addLiveViewValue(root, "voltage", _voltage, "V", 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PylontechBatteryStats::getLiveViewData(JsonVariant& root) const
|
void PylontechBatteryStats::getLiveViewData(JsonVariant& root) const
|
||||||
@ -68,7 +69,6 @@ void PylontechBatteryStats::getLiveViewData(JsonVariant& root) const
|
|||||||
addLiveViewValue(root, "chargeCurrentLimitation", _chargeCurrentLimitation, "A", 1);
|
addLiveViewValue(root, "chargeCurrentLimitation", _chargeCurrentLimitation, "A", 1);
|
||||||
addLiveViewValue(root, "dischargeCurrentLimitation", _dischargeCurrentLimitation, "A", 1);
|
addLiveViewValue(root, "dischargeCurrentLimitation", _dischargeCurrentLimitation, "A", 1);
|
||||||
addLiveViewValue(root, "stateOfHealth", _stateOfHealth, "%", 0);
|
addLiveViewValue(root, "stateOfHealth", _stateOfHealth, "%", 0);
|
||||||
addLiveViewValue(root, "voltage", _voltage, "V", 2);
|
|
||||||
addLiveViewValue(root, "current", _current, "A", 1);
|
addLiveViewValue(root, "current", _current, "A", 1);
|
||||||
addLiveViewValue(root, "temperature", _temperature, "°C", 1);
|
addLiveViewValue(root, "temperature", _temperature, "°C", 1);
|
||||||
|
|
||||||
@ -105,18 +105,13 @@ void JkBmsBatteryStats::getJsonData(JsonVariant& root, bool verbose) const
|
|||||||
|
|
||||||
using Label = JkBms::DataPointLabel;
|
using Label = JkBms::DataPointLabel;
|
||||||
|
|
||||||
auto oVoltage = _dataPoints.get<Label::BatteryVoltageMilliVolt>();
|
|
||||||
if (oVoltage.has_value()) {
|
|
||||||
addLiveViewValue(root, "voltage",
|
|
||||||
static_cast<float>(*oVoltage) / 1000, "V", 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto oCurrent = _dataPoints.get<Label::BatteryCurrentMilliAmps>();
|
auto oCurrent = _dataPoints.get<Label::BatteryCurrentMilliAmps>();
|
||||||
if (oCurrent.has_value()) {
|
if (oCurrent.has_value()) {
|
||||||
addLiveViewValue(root, "current",
|
addLiveViewValue(root, "current",
|
||||||
static_cast<float>(*oCurrent) / 1000, "A", 2);
|
static_cast<float>(*oCurrent) / 1000, "A", 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto oVoltage = _dataPoints.get<Label::BatteryVoltageMilliVolt>();
|
||||||
if (oVoltage.has_value() && oCurrent.has_value()) {
|
if (oVoltage.has_value() && oCurrent.has_value()) {
|
||||||
auto current = static_cast<float>(*oCurrent) / 1000;
|
auto current = static_cast<float>(*oCurrent) / 1000;
|
||||||
auto voltage = static_cast<float>(*oVoltage) / 1000;
|
auto voltage = static_cast<float>(*oVoltage) / 1000;
|
||||||
@ -217,7 +212,8 @@ void BatteryStats::mqttPublish() const
|
|||||||
{
|
{
|
||||||
MqttSettings.publish(F("battery/manufacturer"), _manufacturer);
|
MqttSettings.publish(F("battery/manufacturer"), _manufacturer);
|
||||||
MqttSettings.publish(F("battery/dataAge"), String(getAgeSeconds()));
|
MqttSettings.publish(F("battery/dataAge"), String(getAgeSeconds()));
|
||||||
MqttSettings.publish(F("battery/stateOfCharge"), String(_SoC));
|
MqttSettings.publish(F("battery/stateOfCharge"), String(_soc));
|
||||||
|
MqttSettings.publish(F("battery/voltage"), String(_voltage));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PylontechBatteryStats::mqttPublish() const
|
void PylontechBatteryStats::mqttPublish() const
|
||||||
@ -228,7 +224,6 @@ void PylontechBatteryStats::mqttPublish() const
|
|||||||
MqttSettings.publish(F("battery/settings/chargeCurrentLimitation"), String(_chargeCurrentLimitation));
|
MqttSettings.publish(F("battery/settings/chargeCurrentLimitation"), String(_chargeCurrentLimitation));
|
||||||
MqttSettings.publish(F("battery/settings/dischargeCurrentLimitation"), String(_dischargeCurrentLimitation));
|
MqttSettings.publish(F("battery/settings/dischargeCurrentLimitation"), String(_dischargeCurrentLimitation));
|
||||||
MqttSettings.publish(F("battery/stateOfHealth"), String(_stateOfHealth));
|
MqttSettings.publish(F("battery/stateOfHealth"), String(_stateOfHealth));
|
||||||
MqttSettings.publish(F("battery/voltage"), String(_voltage));
|
|
||||||
MqttSettings.publish(F("battery/current"), String(_current));
|
MqttSettings.publish(F("battery/current"), String(_current));
|
||||||
MqttSettings.publish(F("battery/temperature"), String(_temperature));
|
MqttSettings.publish(F("battery/temperature"), String(_temperature));
|
||||||
MqttSettings.publish(F("battery/alarm/overCurrentDischarge"), String(_alarmOverCurrentDischarge));
|
MqttSettings.publish(F("battery/alarm/overCurrentDischarge"), String(_alarmOverCurrentDischarge));
|
||||||
@ -260,6 +255,10 @@ void JkBmsBatteryStats::mqttPublish() const
|
|||||||
Label::CellsMilliVolt, // complex data format
|
Label::CellsMilliVolt, // complex data format
|
||||||
Label::ModificationPassword, // sensitive data
|
Label::ModificationPassword, // sensitive data
|
||||||
Label::BatterySoCPercent // already published by base class
|
Label::BatterySoCPercent // already published by base class
|
||||||
|
// NOTE that voltage is also published by the base class, however, we
|
||||||
|
// previously published it only from here using the respective topic.
|
||||||
|
// to avoid a breaking change, we publish the value again using the
|
||||||
|
// "old" topic.
|
||||||
};
|
};
|
||||||
|
|
||||||
// regularly publish all topics regardless of whether or not their value changed
|
// regularly publish all topics regardless of whether or not their value changed
|
||||||
@ -335,9 +334,16 @@ void JkBmsBatteryStats::updateFrom(JkBms::DataPointContainer const& dp)
|
|||||||
|
|
||||||
auto oSoCValue = dp.get<Label::BatterySoCPercent>();
|
auto oSoCValue = dp.get<Label::BatterySoCPercent>();
|
||||||
if (oSoCValue.has_value()) {
|
if (oSoCValue.has_value()) {
|
||||||
_SoC = *oSoCValue;
|
|
||||||
auto oSoCDataPoint = dp.getDataPointFor<Label::BatterySoCPercent>();
|
auto oSoCDataPoint = dp.getDataPointFor<Label::BatterySoCPercent>();
|
||||||
_lastUpdateSoC = oSoCDataPoint->getTimestamp();
|
BatteryStats::setSoC(*oSoCValue, 0/*precision*/,
|
||||||
|
oSoCDataPoint->getTimestamp());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto oVoltage = dp.get<Label::BatteryVoltageMilliVolt>();
|
||||||
|
if (oVoltage.has_value()) {
|
||||||
|
auto oVoltageDataPoint = dp.getDataPointFor<Label::BatteryVoltageMilliVolt>();
|
||||||
|
BatteryStats::setVoltage(static_cast<float>(*oVoltage) / 1000,
|
||||||
|
oVoltageDataPoint->getTimestamp());
|
||||||
}
|
}
|
||||||
|
|
||||||
_dataPoints.updateFrom(dp);
|
_dataPoints.updateFrom(dp);
|
||||||
@ -360,8 +366,9 @@ void JkBmsBatteryStats::updateFrom(JkBms::DataPointContainer const& dp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VictronSmartShuntStats::updateFrom(VeDirectShuntController::veShuntStruct const& shuntData) {
|
void VictronSmartShuntStats::updateFrom(VeDirectShuntController::veShuntStruct const& shuntData) {
|
||||||
_SoC = shuntData.SOC / 10;
|
BatteryStats::setVoltage(shuntData.V, millis());
|
||||||
_voltage = shuntData.V;
|
BatteryStats::setSoC(static_cast<float>(shuntData.SOC) / 10, 1/*precision*/, millis());
|
||||||
|
|
||||||
_current = shuntData.I;
|
_current = shuntData.I;
|
||||||
_modelName = shuntData.getPidAsString().data();
|
_modelName = shuntData.getPidAsString().data();
|
||||||
_chargeCycles = shuntData.H4;
|
_chargeCycles = shuntData.H4;
|
||||||
@ -380,14 +387,12 @@ void VictronSmartShuntStats::updateFrom(VeDirectShuntController::veShuntStruct c
|
|||||||
_alarmHighTemperature = shuntData.AR & 64;
|
_alarmHighTemperature = shuntData.AR & 64;
|
||||||
|
|
||||||
_lastUpdate = VeDirectShunt.getLastUpdate();
|
_lastUpdate = VeDirectShunt.getLastUpdate();
|
||||||
_lastUpdateSoC = VeDirectShunt.getLastUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VictronSmartShuntStats::getLiveViewData(JsonVariant& root) const {
|
void VictronSmartShuntStats::getLiveViewData(JsonVariant& root) const {
|
||||||
BatteryStats::getLiveViewData(root);
|
BatteryStats::getLiveViewData(root);
|
||||||
|
|
||||||
// values go into the "Status" card of the web application
|
// values go into the "Status" card of the web application
|
||||||
addLiveViewValue(root, "voltage", _voltage, "V", 2);
|
|
||||||
addLiveViewValue(root, "current", _current, "A", 1);
|
addLiveViewValue(root, "current", _current, "A", 1);
|
||||||
addLiveViewValue(root, "chargeCycles", _chargeCycles, "", 0);
|
addLiveViewValue(root, "chargeCycles", _chargeCycles, "", 0);
|
||||||
addLiveViewValue(root, "chargedEnergy", _chargedEnergy, "KWh", 1);
|
addLiveViewValue(root, "chargedEnergy", _chargedEnergy, "KWh", 1);
|
||||||
@ -406,7 +411,6 @@ void VictronSmartShuntStats::getLiveViewData(JsonVariant& root) const {
|
|||||||
void VictronSmartShuntStats::mqttPublish() const {
|
void VictronSmartShuntStats::mqttPublish() const {
|
||||||
BatteryStats::mqttPublish();
|
BatteryStats::mqttPublish();
|
||||||
|
|
||||||
MqttSettings.publish(F("battery/voltage"), String(_voltage));
|
|
||||||
MqttSettings.publish(F("battery/current"), String(_current));
|
MqttSettings.publish(F("battery/current"), String(_current));
|
||||||
MqttSettings.publish(F("battery/chargeCycles"), String(_chargeCycles));
|
MqttSettings.publish(F("battery/chargeCycles"), String(_chargeCycles));
|
||||||
MqttSettings.publish(F("battery/chargedEnergy"), String(_chargedEnergy));
|
MqttSettings.publish(F("battery/chargedEnergy"), String(_chargedEnergy));
|
||||||
|
|||||||
@ -191,6 +191,7 @@ bool ConfigurationClass::write()
|
|||||||
powerlimiter["target_power_consumption_hysteresis"] = config.PowerLimiter.TargetPowerConsumptionHysteresis;
|
powerlimiter["target_power_consumption_hysteresis"] = config.PowerLimiter.TargetPowerConsumptionHysteresis;
|
||||||
powerlimiter["lower_power_limit"] = config.PowerLimiter.LowerPowerLimit;
|
powerlimiter["lower_power_limit"] = config.PowerLimiter.LowerPowerLimit;
|
||||||
powerlimiter["upper_power_limit"] = config.PowerLimiter.UpperPowerLimit;
|
powerlimiter["upper_power_limit"] = config.PowerLimiter.UpperPowerLimit;
|
||||||
|
powerlimiter["ignore_soc"] = config.PowerLimiter.IgnoreSoc;
|
||||||
powerlimiter["battery_soc_start_threshold"] = config.PowerLimiter.BatterySocStartThreshold;
|
powerlimiter["battery_soc_start_threshold"] = config.PowerLimiter.BatterySocStartThreshold;
|
||||||
powerlimiter["battery_soc_stop_threshold"] = config.PowerLimiter.BatterySocStopThreshold;
|
powerlimiter["battery_soc_stop_threshold"] = config.PowerLimiter.BatterySocStopThreshold;
|
||||||
powerlimiter["voltage_start_threshold"] = config.PowerLimiter.VoltageStartThreshold;
|
powerlimiter["voltage_start_threshold"] = config.PowerLimiter.VoltageStartThreshold;
|
||||||
@ -207,7 +208,8 @@ bool ConfigurationClass::write()
|
|||||||
battery["provider"] = config.Battery.Provider;
|
battery["provider"] = config.Battery.Provider;
|
||||||
battery["jkbms_interface"] = config.Battery.JkBmsInterface;
|
battery["jkbms_interface"] = config.Battery.JkBmsInterface;
|
||||||
battery["jkbms_polling_interval"] = config.Battery.JkBmsPollingInterval;
|
battery["jkbms_polling_interval"] = config.Battery.JkBmsPollingInterval;
|
||||||
battery["mqtt_topic"] = config.Battery.MqttTopic;
|
battery["mqtt_topic"] = config.Battery.MqttSocTopic;
|
||||||
|
battery["mqtt_voltage_topic"] = config.Battery.MqttVoltageTopic;
|
||||||
|
|
||||||
JsonObject huawei = doc.createNestedObject("huawei");
|
JsonObject huawei = doc.createNestedObject("huawei");
|
||||||
huawei["enabled"] = config.Huawei.Enabled;
|
huawei["enabled"] = config.Huawei.Enabled;
|
||||||
@ -435,6 +437,7 @@ bool ConfigurationClass::read()
|
|||||||
config.PowerLimiter.TargetPowerConsumptionHysteresis = powerlimiter["target_power_consumption_hysteresis"] | POWERLIMITER_TARGET_POWER_CONSUMPTION_HYSTERESIS;
|
config.PowerLimiter.TargetPowerConsumptionHysteresis = powerlimiter["target_power_consumption_hysteresis"] | POWERLIMITER_TARGET_POWER_CONSUMPTION_HYSTERESIS;
|
||||||
config.PowerLimiter.LowerPowerLimit = powerlimiter["lower_power_limit"] | POWERLIMITER_LOWER_POWER_LIMIT;
|
config.PowerLimiter.LowerPowerLimit = powerlimiter["lower_power_limit"] | POWERLIMITER_LOWER_POWER_LIMIT;
|
||||||
config.PowerLimiter.UpperPowerLimit = powerlimiter["upper_power_limit"] | POWERLIMITER_UPPER_POWER_LIMIT;
|
config.PowerLimiter.UpperPowerLimit = powerlimiter["upper_power_limit"] | POWERLIMITER_UPPER_POWER_LIMIT;
|
||||||
|
config.PowerLimiter.IgnoreSoc = powerlimiter["ignore_soc"] | POWERLIMITER_IGNORE_SOC;
|
||||||
config.PowerLimiter.BatterySocStartThreshold = powerlimiter["battery_soc_start_threshold"] | POWERLIMITER_BATTERY_SOC_START_THRESHOLD;
|
config.PowerLimiter.BatterySocStartThreshold = powerlimiter["battery_soc_start_threshold"] | POWERLIMITER_BATTERY_SOC_START_THRESHOLD;
|
||||||
config.PowerLimiter.BatterySocStopThreshold = powerlimiter["battery_soc_stop_threshold"] | POWERLIMITER_BATTERY_SOC_STOP_THRESHOLD;
|
config.PowerLimiter.BatterySocStopThreshold = powerlimiter["battery_soc_stop_threshold"] | POWERLIMITER_BATTERY_SOC_STOP_THRESHOLD;
|
||||||
config.PowerLimiter.VoltageStartThreshold = powerlimiter["voltage_start_threshold"] | POWERLIMITER_VOLTAGE_START_THRESHOLD;
|
config.PowerLimiter.VoltageStartThreshold = powerlimiter["voltage_start_threshold"] | POWERLIMITER_VOLTAGE_START_THRESHOLD;
|
||||||
@ -451,7 +454,8 @@ bool ConfigurationClass::read()
|
|||||||
config.Battery.Provider = battery["provider"] | BATTERY_PROVIDER;
|
config.Battery.Provider = battery["provider"] | BATTERY_PROVIDER;
|
||||||
config.Battery.JkBmsInterface = battery["jkbms_interface"] | BATTERY_JKBMS_INTERFACE;
|
config.Battery.JkBmsInterface = battery["jkbms_interface"] | BATTERY_JKBMS_INTERFACE;
|
||||||
config.Battery.JkBmsPollingInterval = battery["jkbms_polling_interval"] | BATTERY_JKBMS_POLLING_INTERVAL;
|
config.Battery.JkBmsPollingInterval = battery["jkbms_polling_interval"] | BATTERY_JKBMS_POLLING_INTERVAL;
|
||||||
strlcpy(config.Battery.MqttTopic, battery["mqtt_topic"] | "", sizeof(config.Battery.MqttTopic));
|
strlcpy(config.Battery.MqttSocTopic, battery["mqtt_topic"] | "", sizeof(config.Battery.MqttSocTopic));
|
||||||
|
strlcpy(config.Battery.MqttVoltageTopic, battery["mqtt_voltage_topic"] | "", sizeof(config.Battery.MqttVoltageTopic));
|
||||||
|
|
||||||
JsonObject huawei = doc["huawei"];
|
JsonObject huawei = doc["huawei"];
|
||||||
config.Huawei.Enabled = huawei["enabled"] | HUAWEI_ENABLED;
|
config.Huawei.Enabled = huawei["enabled"] | HUAWEI_ENABLED;
|
||||||
|
|||||||
@ -81,14 +81,17 @@ void DatastoreClass::loop()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& c : inv->Statistics()->getChannelsByType(TYPE_AC)) {
|
for (auto& c : inv->Statistics()->getChannelsByType(TYPE_INV)) {
|
||||||
if (cfg->Poll_Enable) {
|
if (cfg->Poll_Enable) {
|
||||||
_totalAcYieldTotalEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YT);
|
_totalAcYieldTotalEnabled += inv->Statistics()->getChannelFieldValue(TYPE_INV, c, FLD_YT);
|
||||||
_totalAcYieldDayEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YD);
|
_totalAcYieldDayEnabled += inv->Statistics()->getChannelFieldValue(TYPE_INV, c, FLD_YD);
|
||||||
|
|
||||||
_totalAcYieldTotalDigits = max<unsigned int>(_totalAcYieldTotalDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YT));
|
_totalAcYieldTotalDigits = max<unsigned int>(_totalAcYieldTotalDigits, inv->Statistics()->getChannelFieldDigits(TYPE_INV, c, FLD_YT));
|
||||||
_totalAcYieldDayDigits = max<unsigned int>(_totalAcYieldDayDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YD));
|
_totalAcYieldDayDigits = max<unsigned int>(_totalAcYieldDayDigits, inv->Statistics()->getChannelFieldDigits(TYPE_INV, c, FLD_YD));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& c : inv->Statistics()->getChannelsByType(TYPE_AC)) {
|
||||||
if (inv->getEnablePolling()) {
|
if (inv->getEnablePolling()) {
|
||||||
_totalAcPowerEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_PAC);
|
_totalAcPowerEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_PAC);
|
||||||
_totalAcPowerDigits = max<unsigned int>(_totalAcPowerDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_PAC));
|
_totalAcPowerDigits = max<unsigned int>(_totalAcPowerDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_PAC));
|
||||||
|
|||||||
@ -4,6 +4,8 @@
|
|||||||
*/
|
*/
|
||||||
#include "Display_Graphic.h"
|
#include "Display_Graphic.h"
|
||||||
#include "Datastore.h"
|
#include "Datastore.h"
|
||||||
|
#include "PowerMeter.h"
|
||||||
|
#include "Configuration.h"
|
||||||
#include <NetworkSettings.h>
|
#include <NetworkSettings.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
@ -31,8 +33,11 @@ const uint8_t languages[] = {
|
|||||||
static const char* const i18n_offline[] = { "Offline", "Offline", "Offline" };
|
static const char* const i18n_offline[] = { "Offline", "Offline", "Offline" };
|
||||||
static const char* const i18n_current_power_w[] = { "%.0f W", "%.0f W", "%.0f W" };
|
static const char* const i18n_current_power_w[] = { "%.0f W", "%.0f W", "%.0f W" };
|
||||||
static const char* const i18n_current_power_kw[] = { "%.1f kW", "%.1f kW", "%.1f kW" };
|
static const char* const i18n_current_power_kw[] = { "%.1f kW", "%.1f kW", "%.1f kW" };
|
||||||
|
static const char* const i18n_meter_power_w[] = { "grid: %.0f W", "Netz: %.0f W", "reseau: %.0f W" };
|
||||||
|
static const char* const i18n_meter_power_kw[] = { "grid: %.1f kW", "Netz: %.1f kW", "reseau: %.1f kW" };
|
||||||
static const char* const i18n_yield_today_wh[] = { "today: %4.0f Wh", "Heute: %4.0f Wh", "auj.: %4.0f Wh" };
|
static const char* const i18n_yield_today_wh[] = { "today: %4.0f Wh", "Heute: %4.0f Wh", "auj.: %4.0f Wh" };
|
||||||
static const char* const i18n_yield_total_kwh[] = { "total: %.1f kWh", "Ges.: %.1f kWh", "total: %.1f kWh" };
|
static const char* const i18n_yield_total_kwh[] = { "total: %.1f kWh", "Ges.: %.1f kWh", "total: %.1f kWh" };
|
||||||
|
static const char* const i18n_yield_total_mwh[] = { "total: %.0f kWh", "Ges.: %.0f kWh", "total: %.0f kWh" };
|
||||||
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()
|
||||||
@ -67,11 +72,19 @@ void DisplayGraphicClass::init(Scheduler& scheduler, const DisplayType_t type, c
|
|||||||
|
|
||||||
void DisplayGraphicClass::calcLineHeights()
|
void DisplayGraphicClass::calcLineHeights()
|
||||||
{
|
{
|
||||||
uint8_t yOff = 0;
|
bool diagram = (_isLarge && _diagram_mode == DiagramMode_t::Small);
|
||||||
|
// the diagram needs space. we need to keep
|
||||||
|
// away from the y-axis label in particular.
|
||||||
|
uint8_t yOff = (diagram ? 7 : 0);
|
||||||
for (uint8_t i = 0; i < 4; i++) {
|
for (uint8_t i = 0; i < 4; i++) {
|
||||||
setFont(i);
|
setFont(i);
|
||||||
yOff += (_display->getMaxCharHeight());
|
yOff += _display->getAscent();
|
||||||
_lineOffsets[i] = yOff;
|
_lineOffsets[i] = yOff;
|
||||||
|
yOff += ((!_isLarge || diagram) ? 2 : 3);
|
||||||
|
// the descent is a negative value and moves the *next* line's
|
||||||
|
// baseline. the first line never uses a letter with descent and
|
||||||
|
// we need that space when showing the small diagram.
|
||||||
|
yOff -= ((i == 0 && diagram) ? 0 : _display->getDescent());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,27 +116,23 @@ void DisplayGraphicClass::printText(const char* text, const uint8_t line)
|
|||||||
if (!_isLarge) {
|
if (!_isLarge) {
|
||||||
dispX = (line == 0) ? 5 : 0;
|
dispX = (line == 0) ? 5 : 0;
|
||||||
} else {
|
} else {
|
||||||
switch (line) {
|
if (line == 0 && _diagram_mode == DiagramMode_t::Small) {
|
||||||
case 0:
|
|
||||||
if (_diagram_mode == DiagramMode_t::Small) {
|
|
||||||
// Center between left border and diagram
|
// Center between left border and diagram
|
||||||
dispX = (CHART_POSX - _display->getStrWidth(text)) / 2;
|
dispX = (CHART_POSX - _display->getStrWidth(text)) / 2;
|
||||||
} else {
|
} else {
|
||||||
// Center on screen
|
// Center on screen
|
||||||
dispX = (_display->getDisplayWidth() - _display->getStrWidth(text)) / 2;
|
dispX = (_display->getDisplayWidth() - _display->getStrWidth(text)) / 2;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
// Center on screen
|
|
||||||
dispX = (_display->getDisplayWidth() - _display->getStrWidth(text)) / 2;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
dispX = 5;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dispX += enableScreensaver ? (_mExtra % 7) : 0;
|
if (enableScreensaver) {
|
||||||
|
unsigned maxOffset = (_isLarge ? 8 : 6);
|
||||||
|
unsigned period = 2 * maxOffset;
|
||||||
|
unsigned step = _mExtra % period;
|
||||||
|
int offset = (step <= maxOffset) ? step : (period - step);
|
||||||
|
offset -= (_isLarge ? 5 : 0); // oscillate around center on large screens
|
||||||
|
dispX += offset;
|
||||||
|
}
|
||||||
_display->drawStr(dispX, _lineOffsets[line], text);
|
_display->drawStr(dispX, _lineOffsets[line], text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,7 +245,9 @@ void DisplayGraphicClass::loop()
|
|||||||
snprintf(_fmtText, sizeof(_fmtText), i18n_yield_today_wh[_display_language], Datastore.getTotalAcYieldDayEnabled());
|
snprintf(_fmtText, sizeof(_fmtText), i18n_yield_today_wh[_display_language], Datastore.getTotalAcYieldDayEnabled());
|
||||||
printText(_fmtText, 1);
|
printText(_fmtText, 1);
|
||||||
|
|
||||||
snprintf(_fmtText, sizeof(_fmtText), i18n_yield_total_kwh[_display_language], Datastore.getTotalAcYieldTotalEnabled());
|
const float watts = Datastore.getTotalAcYieldTotalEnabled();
|
||||||
|
auto const format = (watts >= 1000) ? i18n_yield_total_mwh : i18n_yield_total_kwh;
|
||||||
|
snprintf(_fmtText, sizeof(_fmtText), format[_display_language], watts);
|
||||||
printText(_fmtText, 2);
|
printText(_fmtText, 2);
|
||||||
//<=======================
|
//<=======================
|
||||||
|
|
||||||
@ -252,6 +263,32 @@ void DisplayGraphicClass::loop()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the IP and time info in the third line use three-second slots. the
|
||||||
|
// timing for the power meter is chosen such that every third of those
|
||||||
|
// three-second slots is used to NOT overwrite the total inverter energy.
|
||||||
|
bool timing = (_mExtra % 9) >= 3;
|
||||||
|
|
||||||
|
if (showText && Configuration.get().PowerMeter.Enabled && timing && !displayPowerSave) {
|
||||||
|
// erase the third line and print the power meter value instead.
|
||||||
|
// we do it this way to touch as least upstream code as possible
|
||||||
|
// to make maintenance easier.
|
||||||
|
setFont(2);
|
||||||
|
auto lineHeight = _display->getAscent() - _display->getDescent();
|
||||||
|
auto y = _lineOffsets[2] - _display->getAscent();
|
||||||
|
_display->setDrawColor(0);
|
||||||
|
_display->drawBox(0, y, _display->getDisplayWidth(), lineHeight);
|
||||||
|
_display->setDrawColor(1);
|
||||||
|
|
||||||
|
auto acPower = PowerMeter.getPowerTotal(false);
|
||||||
|
if (acPower > 999) {
|
||||||
|
snprintf(_fmtText, sizeof(_fmtText), i18n_meter_power_kw[_display_language], (acPower / 1000));
|
||||||
|
} else {
|
||||||
|
snprintf(_fmtText, sizeof(_fmtText), i18n_meter_power_w[_display_language], acPower);
|
||||||
|
}
|
||||||
|
|
||||||
|
printText(_fmtText, 2);
|
||||||
|
}
|
||||||
|
|
||||||
_display->sendBuffer();
|
_display->sendBuffer();
|
||||||
|
|
||||||
_mExtra++;
|
_mExtra++;
|
||||||
|
|||||||
@ -10,56 +10,105 @@ bool MqttBattery::init(bool verboseLogging)
|
|||||||
_verboseLogging = verboseLogging;
|
_verboseLogging = verboseLogging;
|
||||||
|
|
||||||
auto const& config = Configuration.get();
|
auto const& config = Configuration.get();
|
||||||
_socTopic = config.Battery.MqttTopic;
|
|
||||||
|
|
||||||
if (_socTopic.isEmpty()) { return false; }
|
|
||||||
|
|
||||||
|
_socTopic = config.Battery.MqttSocTopic;
|
||||||
|
if (!_socTopic.isEmpty()) {
|
||||||
MqttSettings.subscribe(_socTopic, 0/*QoS*/,
|
MqttSettings.subscribe(_socTopic, 0/*QoS*/,
|
||||||
std::bind(&MqttBattery::onMqttMessage,
|
std::bind(&MqttBattery::onMqttMessageSoC,
|
||||||
this, std::placeholders::_1, std::placeholders::_2,
|
this, std::placeholders::_1, std::placeholders::_2,
|
||||||
std::placeholders::_3, std::placeholders::_4,
|
std::placeholders::_3, std::placeholders::_4,
|
||||||
std::placeholders::_5, std::placeholders::_6)
|
std::placeholders::_5, std::placeholders::_6)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (_verboseLogging) {
|
if (_verboseLogging) {
|
||||||
MessageOutput.printf("MqttBattery: Subscribed to '%s'\r\n",
|
MessageOutput.printf("MqttBattery: Subscribed to '%s' for SoC readings\r\n",
|
||||||
_socTopic.c_str());
|
_socTopic.c_str());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_voltageTopic = config.Battery.MqttVoltageTopic;
|
||||||
|
if (!_voltageTopic.isEmpty()) {
|
||||||
|
MqttSettings.subscribe(_voltageTopic, 0/*QoS*/,
|
||||||
|
std::bind(&MqttBattery::onMqttMessageVoltage,
|
||||||
|
this, std::placeholders::_1, std::placeholders::_2,
|
||||||
|
std::placeholders::_3, std::placeholders::_4,
|
||||||
|
std::placeholders::_5, std::placeholders::_6)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (_verboseLogging) {
|
||||||
|
MessageOutput.printf("MqttBattery: Subscribed to '%s' for voltage readings\r\n",
|
||||||
|
_voltageTopic.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttBattery::deinit()
|
void MqttBattery::deinit()
|
||||||
{
|
{
|
||||||
if (_socTopic.isEmpty()) { return; }
|
if (!_voltageTopic.isEmpty()) {
|
||||||
|
MqttSettings.unsubscribe(_voltageTopic);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_socTopic.isEmpty()) {
|
||||||
MqttSettings.unsubscribe(_socTopic);
|
MqttSettings.unsubscribe(_socTopic);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttBattery::onMqttMessage(espMqttClientTypes::MessageProperties const& properties,
|
std::optional<float> MqttBattery::getFloat(std::string const& src, char const* topic) {
|
||||||
char const* topic, uint8_t const* payload, size_t len, size_t index, size_t total)
|
float res = 0;
|
||||||
{
|
|
||||||
float soc = 0;
|
|
||||||
std::string value(reinterpret_cast<const char*>(payload), len);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
soc = std::stof(value);
|
res = std::stof(src);
|
||||||
}
|
}
|
||||||
catch(std::invalid_argument const& e) {
|
catch(std::invalid_argument const& e) {
|
||||||
MessageOutput.printf("MqttBattery: Cannot parse payload '%s' in topic '%s' as float\r\n",
|
MessageOutput.printf("MqttBattery: Cannot parse payload '%s' in topic '%s' as float\r\n",
|
||||||
value.c_str(), topic);
|
src.c_str(), topic);
|
||||||
return;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (soc < 0 || soc > 100) {
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttBattery::onMqttMessageSoC(espMqttClientTypes::MessageProperties const& properties,
|
||||||
|
char const* topic, uint8_t const* payload, size_t len, size_t index, size_t total)
|
||||||
|
{
|
||||||
|
auto soc = getFloat(std::string(reinterpret_cast<const char*>(payload), len), topic);
|
||||||
|
if (!soc.has_value()) { return; }
|
||||||
|
|
||||||
|
if (*soc < 0 || *soc > 100) {
|
||||||
MessageOutput.printf("MqttBattery: Implausible SoC '%.2f' in topic '%s'\r\n",
|
MessageOutput.printf("MqttBattery: Implausible SoC '%.2f' in topic '%s'\r\n",
|
||||||
soc, topic);
|
*soc, topic);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_stats->setSoC(static_cast<uint8_t>(soc));
|
_stats->setSoC(*soc, 0/*precision*/, millis());
|
||||||
|
|
||||||
if (_verboseLogging) {
|
if (_verboseLogging) {
|
||||||
MessageOutput.printf("MqttBattery: Updated SoC to %d from '%s'\r\n",
|
MessageOutput.printf("MqttBattery: Updated SoC to %d from '%s'\r\n",
|
||||||
static_cast<uint8_t>(soc), topic);
|
static_cast<uint8_t>(*soc), topic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttBattery::onMqttMessageVoltage(espMqttClientTypes::MessageProperties const& properties,
|
||||||
|
char const* topic, uint8_t const* payload, size_t len, size_t index, size_t total)
|
||||||
|
{
|
||||||
|
auto voltage = getFloat(std::string(reinterpret_cast<const char*>(payload), len), topic);
|
||||||
|
if (!voltage.has_value()) { return; }
|
||||||
|
|
||||||
|
// since this project is revolving around Hoymiles microinverters, which can
|
||||||
|
// only handle up to 65V of input voltage at best, it is safe to assume that
|
||||||
|
// an even higher voltage is implausible.
|
||||||
|
if (*voltage < 0 || *voltage > 65) {
|
||||||
|
MessageOutput.printf("MqttBattery: Implausible voltage '%.2f' in topic '%s'\r\n",
|
||||||
|
*voltage, topic);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_stats->setVoltage(*voltage, millis());
|
||||||
|
|
||||||
|
if (_verboseLogging) {
|
||||||
|
MessageOutput.printf("MqttBattery: Updated voltage to %.2f from '%s'\r\n",
|
||||||
|
*voltage, topic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -107,7 +107,7 @@ void MqttHandleHassClass::publishInverterField(std::shared_ptr<InverterAbstract>
|
|||||||
const String serial = inv->serialString();
|
const String serial = inv->serialString();
|
||||||
|
|
||||||
String fieldName;
|
String fieldName;
|
||||||
if (type == TYPE_AC && fieldType.fieldId == FLD_PDC) {
|
if (type == TYPE_INV && fieldType.fieldId == FLD_PDC) {
|
||||||
fieldName = "PowerDC";
|
fieldName = "PowerDC";
|
||||||
} else {
|
} else {
|
||||||
fieldName = inv->Statistics()->getChannelFieldName(type, channel, fieldType.fieldId);
|
fieldName = inv->Statistics()->getChannelFieldName(type, channel, fieldType.fieldId);
|
||||||
|
|||||||
@ -141,7 +141,7 @@ String MqttHandleInverterClass::getTopic(std::shared_ptr<InverterAbstract> inv,
|
|||||||
}
|
}
|
||||||
|
|
||||||
String chanName;
|
String chanName;
|
||||||
if (type == TYPE_AC && fieldId == FLD_PDC) {
|
if (type == TYPE_INV && fieldId == FLD_PDC) {
|
||||||
chanName = "powerdc";
|
chanName = "powerdc";
|
||||||
} else {
|
} else {
|
||||||
chanName = inv->Statistics()->getChannelFieldName(type, channel, fieldId);
|
chanName = inv->Statistics()->getChannelFieldName(type, channel, fieldId);
|
||||||
|
|||||||
@ -128,7 +128,7 @@ void MqttSettingsClass::performConnect()
|
|||||||
} else {
|
} else {
|
||||||
static_cast<espMqttClientSecure*>(_mqttClient)->setCredentials(config.Mqtt.Username, config.Mqtt.Password);
|
static_cast<espMqttClientSecure*>(_mqttClient)->setCredentials(config.Mqtt.Username, config.Mqtt.Password);
|
||||||
}
|
}
|
||||||
static_cast<espMqttClientSecure*>(_mqttClient)->setWill(willTopic.c_str(), 2, config.Mqtt.Retain, config.Mqtt.Lwt.Value_Offline);
|
static_cast<espMqttClientSecure*>(_mqttClient)->setWill(willTopic.c_str(), config.Mqtt.Lwt.Qos, config.Mqtt.Retain, config.Mqtt.Lwt.Value_Offline);
|
||||||
static_cast<espMqttClientSecure*>(_mqttClient)->setClientId(clientId.c_str());
|
static_cast<espMqttClientSecure*>(_mqttClient)->setClientId(clientId.c_str());
|
||||||
static_cast<espMqttClientSecure*>(_mqttClient)->setCleanSession(config.Mqtt.CleanSession);
|
static_cast<espMqttClientSecure*>(_mqttClient)->setCleanSession(config.Mqtt.CleanSession);
|
||||||
static_cast<espMqttClientSecure*>(_mqttClient)->onConnect(std::bind(&MqttSettingsClass::onMqttConnect, this, _1));
|
static_cast<espMqttClientSecure*>(_mqttClient)->onConnect(std::bind(&MqttSettingsClass::onMqttConnect, this, _1));
|
||||||
|
|||||||
@ -290,14 +290,15 @@ void PowerLimiterClass::loop()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_verboseLogging) {
|
if (_verboseLogging) {
|
||||||
MessageOutput.printf("[DPL::loop] battery interface %s, SoC: %d %%, StartTH: %d %%, StopTH: %d %%, SoC age: %d s\r\n",
|
MessageOutput.printf("[DPL::loop] battery interface %s, SoC: %d %%, StartTH: %d %%, StopTH: %d %%, SoC age: %d s, ignore: %s\r\n",
|
||||||
(config.Battery.Enabled?"enabled":"disabled"),
|
(config.Battery.Enabled?"enabled":"disabled"),
|
||||||
Battery.getStats()->getSoC(),
|
Battery.getStats()->getSoC(),
|
||||||
config.PowerLimiter.BatterySocStartThreshold,
|
config.PowerLimiter.BatterySocStartThreshold,
|
||||||
config.PowerLimiter.BatterySocStopThreshold,
|
config.PowerLimiter.BatterySocStopThreshold,
|
||||||
Battery.getStats()->getSoCAgeSeconds());
|
Battery.getStats()->getSoCAgeSeconds(),
|
||||||
|
(config.PowerLimiter.IgnoreSoc?"yes":"no"));
|
||||||
|
|
||||||
float dcVoltage = _inverter->Statistics()->getChannelFieldValue(TYPE_DC, (ChannelNum_t)config.PowerLimiter.InverterChannelId, FLD_UDC);
|
auto dcVoltage = getBatteryVoltage(true/*log voltages only once per DPL loop*/);
|
||||||
MessageOutput.printf("[DPL::loop] dcVoltage: %.2f V, loadCorrectedVoltage: %.2f V, StartTH: %.2f V, StopTH: %.2f V\r\n",
|
MessageOutput.printf("[DPL::loop] dcVoltage: %.2f V, loadCorrectedVoltage: %.2f V, StartTH: %.2f V, StopTH: %.2f V\r\n",
|
||||||
dcVoltage, getLoadCorrectedVoltage(),
|
dcVoltage, getLoadCorrectedVoltage(),
|
||||||
config.PowerLimiter.VoltageStartThreshold,
|
config.PowerLimiter.VoltageStartThreshold,
|
||||||
@ -339,6 +340,46 @@ void PowerLimiterClass::loop()
|
|||||||
_calculationBackoffMs = _calculationBackoffMsDefault;
|
_calculationBackoffMs = _calculationBackoffMsDefault;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* determines the battery's voltage, trying multiple data providers. the most
|
||||||
|
* accurate data is expected to be delivered by a BMS, if it's available. more
|
||||||
|
* accurate and more recent than the inverter's voltage reading is the volage
|
||||||
|
* at the charge controller's output, if it's available. only as a fallback
|
||||||
|
* the voltage reported by the inverter is used.
|
||||||
|
*/
|
||||||
|
float PowerLimiterClass::getBatteryVoltage(bool log) {
|
||||||
|
if (!_inverter) {
|
||||||
|
// there should be no need to call this method if no target inverter is known
|
||||||
|
MessageOutput.println("DPL getBatteryVoltage: no inverter (programmer error)");
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const& config = Configuration.get();
|
||||||
|
auto channel = static_cast<ChannelNum_t>(config.PowerLimiter.InverterChannelId);
|
||||||
|
float inverterVoltage = _inverter->Statistics()->getChannelFieldValue(TYPE_DC, channel, FLD_UDC);
|
||||||
|
float res = inverterVoltage;
|
||||||
|
|
||||||
|
float chargeControllerVoltage = -1;
|
||||||
|
if (VictronMppt.isDataValid()) {
|
||||||
|
res = chargeControllerVoltage = static_cast<float>(VictronMppt.getOutputVoltage());
|
||||||
|
}
|
||||||
|
|
||||||
|
float bmsVoltage = -1;
|
||||||
|
auto stats = Battery.getStats();
|
||||||
|
if (config.Battery.Enabled
|
||||||
|
&& stats->isVoltageValid()
|
||||||
|
&& stats->getVoltageAgeSeconds() < 60) {
|
||||||
|
res = bmsVoltage = stats->getVoltage();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log) {
|
||||||
|
MessageOutput.printf("[DPL::getBatteryVoltage] BMS: %.2f V, MPPT: %.2f V, inverter: %.2f V, returning: %.2fV\r\n",
|
||||||
|
bmsVoltage, chargeControllerVoltage, inverterVoltage, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* calculate the AC output power (limit) to set, such that the inverter uses
|
* calculate the AC output power (limit) to set, such that the inverter uses
|
||||||
* the given power on its DC side, i.e., adjust the power for the inverter's
|
* the given power on its DC side, i.e., adjust the power for the inverter's
|
||||||
@ -592,9 +633,8 @@ float PowerLimiterClass::getLoadCorrectedVoltage()
|
|||||||
|
|
||||||
CONFIG_T& config = Configuration.get();
|
CONFIG_T& config = Configuration.get();
|
||||||
|
|
||||||
auto channel = static_cast<ChannelNum_t>(config.PowerLimiter.InverterChannelId);
|
|
||||||
float acPower = _inverter->Statistics()->getChannelFieldValue(TYPE_AC, CH0, FLD_PAC);
|
float acPower = _inverter->Statistics()->getChannelFieldValue(TYPE_AC, CH0, FLD_PAC);
|
||||||
float dcVoltage = _inverter->Statistics()->getChannelFieldValue(TYPE_DC, channel, FLD_UDC);
|
float dcVoltage = getBatteryVoltage();
|
||||||
|
|
||||||
if (dcVoltage <= 0.0) {
|
if (dcVoltage <= 0.0) {
|
||||||
return 0.0;
|
return 0.0;
|
||||||
@ -608,11 +648,14 @@ bool PowerLimiterClass::testThreshold(float socThreshold, float voltThreshold,
|
|||||||
{
|
{
|
||||||
CONFIG_T& config = Configuration.get();
|
CONFIG_T& config = Configuration.get();
|
||||||
|
|
||||||
// prefer SoC provided through battery interface
|
// prefer SoC provided through battery interface, unless disabled by user
|
||||||
if (config.Battery.Enabled && socThreshold > 0.0
|
auto stats = Battery.getStats();
|
||||||
&& Battery.getStats()->isValid()
|
if (!config.PowerLimiter.IgnoreSoc
|
||||||
&& Battery.getStats()->getSoCAgeSeconds() < 60) {
|
&& config.Battery.Enabled
|
||||||
return compare(Battery.getStats()->getSoC(), socThreshold);
|
&& socThreshold > 0.0
|
||||||
|
&& stats->isSoCValid()
|
||||||
|
&& stats->getSoCAgeSeconds() < 60) {
|
||||||
|
return compare(stats->getSoC(), socThreshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
// use voltage threshold as fallback
|
// use voltage threshold as fallback
|
||||||
|
|||||||
@ -136,7 +136,7 @@ void PylontechCanReceiver::loop()
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 0x355: {
|
case 0x355: {
|
||||||
_stats->setSoC(static_cast<uint8_t>(this->readUnsignedInt16(rx_message.data)));
|
_stats->setSoC(static_cast<uint8_t>(this->readUnsignedInt16(rx_message.data)), 0/*precision*/, millis());
|
||||||
_stats->_stateOfHealth = this->readUnsignedInt16(rx_message.data + 2);
|
_stats->_stateOfHealth = this->readUnsignedInt16(rx_message.data + 2);
|
||||||
|
|
||||||
if (_verboseLogging) {
|
if (_verboseLogging) {
|
||||||
@ -147,13 +147,13 @@ void PylontechCanReceiver::loop()
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 0x356: {
|
case 0x356: {
|
||||||
_stats->_voltage = this->scaleValue(this->readSignedInt16(rx_message.data), 0.01);
|
_stats->setVoltage(this->scaleValue(this->readSignedInt16(rx_message.data), 0.01), millis());
|
||||||
_stats->_current = this->scaleValue(this->readSignedInt16(rx_message.data + 2), 0.1);
|
_stats->_current = this->scaleValue(this->readSignedInt16(rx_message.data + 2), 0.1);
|
||||||
_stats->_temperature = this->scaleValue(this->readSignedInt16(rx_message.data + 4), 0.1);
|
_stats->_temperature = this->scaleValue(this->readSignedInt16(rx_message.data + 4), 0.1);
|
||||||
|
|
||||||
if (_verboseLogging) {
|
if (_verboseLogging) {
|
||||||
MessageOutput.printf("[Pylontech] voltage: %f current: %f temperature: %f\n",
|
MessageOutput.printf("[Pylontech] voltage: %f current: %f temperature: %f\n",
|
||||||
_stats->_voltage, _stats->_current, _stats->_temperature);
|
_stats->getVoltage(), _stats->_current, _stats->_temperature);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -282,12 +282,12 @@ void PylontechCanReceiver::dummyData()
|
|||||||
};
|
};
|
||||||
|
|
||||||
_stats->setManufacturer("Pylontech US3000C");
|
_stats->setManufacturer("Pylontech US3000C");
|
||||||
_stats->setSoC(42);
|
_stats->setSoC(42, 0/*precision*/, millis());
|
||||||
_stats->_chargeVoltage = dummyFloat(50);
|
_stats->_chargeVoltage = dummyFloat(50);
|
||||||
_stats->_chargeCurrentLimitation = dummyFloat(33);
|
_stats->_chargeCurrentLimitation = dummyFloat(33);
|
||||||
_stats->_dischargeCurrentLimitation = dummyFloat(12);
|
_stats->_dischargeCurrentLimitation = dummyFloat(12);
|
||||||
_stats->_stateOfHealth = 99;
|
_stats->_stateOfHealth = 99;
|
||||||
_stats->_voltage = 48.67;
|
_stats->setVoltage(48.67, millis());
|
||||||
_stats->_current = dummyFloat(-1);
|
_stats->_current = dummyFloat(-1);
|
||||||
_stats->_temperature = dummyFloat(20);
|
_stats->_temperature = dummyFloat(20);
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,9 @@
|
|||||||
#include "Display_Graphic.h"
|
#include "Display_Graphic.h"
|
||||||
#include "Led_Single.h"
|
#include "Led_Single.h"
|
||||||
#include "MessageOutput.h"
|
#include "MessageOutput.h"
|
||||||
|
#include "PinMapping.h"
|
||||||
#include <Esp.h>
|
#include <Esp.h>
|
||||||
|
#include <LittleFS.h>
|
||||||
|
|
||||||
uint32_t Utils::getChipId()
|
uint32_t Utils::getChipId()
|
||||||
{
|
{
|
||||||
@ -76,3 +78,17 @@ bool Utils::checkJsonAlloc(const DynamicJsonDocument& doc, const char* function,
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Remove all files but the PINMAPPING_FILENAME
|
||||||
|
void Utils::removeAllFiles()
|
||||||
|
{
|
||||||
|
auto root = LittleFS.open("/");
|
||||||
|
auto file = root.getNextFileName();
|
||||||
|
|
||||||
|
while (file != "") {
|
||||||
|
if (file != PINMAPPING_FILENAME) {
|
||||||
|
LittleFS.remove(file);
|
||||||
|
}
|
||||||
|
file = root.getNextFileName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -137,3 +137,16 @@ double VictronMpptClass::getYieldDay() const
|
|||||||
|
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double VictronMpptClass::getOutputVoltage() const
|
||||||
|
{
|
||||||
|
double min = -1;
|
||||||
|
|
||||||
|
for (const auto& upController : _controllers) {
|
||||||
|
double volts = upController->getData()->V;
|
||||||
|
if (min == -1) { min = volts; }
|
||||||
|
min = std::min(min, volts);
|
||||||
|
}
|
||||||
|
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
|||||||
@ -39,7 +39,8 @@ void WebApiBatteryClass::onStatus(AsyncWebServerRequest* request)
|
|||||||
root["provider"] = config.Battery.Provider;
|
root["provider"] = config.Battery.Provider;
|
||||||
root["jkbms_interface"] = config.Battery.JkBmsInterface;
|
root["jkbms_interface"] = config.Battery.JkBmsInterface;
|
||||||
root["jkbms_polling_interval"] = config.Battery.JkBmsPollingInterval;
|
root["jkbms_polling_interval"] = config.Battery.JkBmsPollingInterval;
|
||||||
root["mqtt_topic"] = config.Battery.MqttTopic;
|
root["mqtt_soc_topic"] = config.Battery.MqttSocTopic;
|
||||||
|
root["mqtt_voltage_topic"] = config.Battery.MqttVoltageTopic;
|
||||||
|
|
||||||
response->setLength();
|
response->setLength();
|
||||||
request->send(response);
|
request->send(response);
|
||||||
@ -103,7 +104,8 @@ void WebApiBatteryClass::onAdminPost(AsyncWebServerRequest* request)
|
|||||||
config.Battery.Provider = root["provider"].as<uint8_t>();
|
config.Battery.Provider = root["provider"].as<uint8_t>();
|
||||||
config.Battery.JkBmsInterface = root["jkbms_interface"].as<uint8_t>();
|
config.Battery.JkBmsInterface = root["jkbms_interface"].as<uint8_t>();
|
||||||
config.Battery.JkBmsPollingInterval = root["jkbms_polling_interval"].as<uint8_t>();
|
config.Battery.JkBmsPollingInterval = root["jkbms_polling_interval"].as<uint8_t>();
|
||||||
strlcpy(config.Battery.MqttTopic, root["mqtt_topic"].as<String>().c_str(), sizeof(config.Battery.MqttTopic));
|
strlcpy(config.Battery.MqttSocTopic, root["mqtt_soc_topic"].as<String>().c_str(), sizeof(config.Battery.MqttSocTopic));
|
||||||
|
strlcpy(config.Battery.MqttVoltageTopic, root["mqtt_voltage_topic"].as<String>().c_str(), sizeof(config.Battery.MqttVoltageTopic));
|
||||||
|
|
||||||
WebApi.writeConfig(retMsg);
|
WebApi.writeConfig(retMsg);
|
||||||
|
|
||||||
|
|||||||
@ -19,12 +19,10 @@ void WebApiConfigClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
|||||||
using std::placeholders::_5;
|
using std::placeholders::_5;
|
||||||
using std::placeholders::_6;
|
using std::placeholders::_6;
|
||||||
|
|
||||||
_server = &server;
|
server.on("/api/config/get", HTTP_GET, std::bind(&WebApiConfigClass::onConfigGet, this, _1));
|
||||||
|
server.on("/api/config/delete", HTTP_POST, std::bind(&WebApiConfigClass::onConfigDelete, this, _1));
|
||||||
_server->on("/api/config/get", HTTP_GET, std::bind(&WebApiConfigClass::onConfigGet, this, _1));
|
server.on("/api/config/list", HTTP_GET, std::bind(&WebApiConfigClass::onConfigListGet, this, _1));
|
||||||
_server->on("/api/config/delete", HTTP_POST, std::bind(&WebApiConfigClass::onConfigDelete, this, _1));
|
server.on("/api/config/upload", HTTP_POST,
|
||||||
_server->on("/api/config/list", HTTP_GET, std::bind(&WebApiConfigClass::onConfigListGet, this, _1));
|
|
||||||
_server->on("/api/config/upload", HTTP_POST,
|
|
||||||
std::bind(&WebApiConfigClass::onConfigUploadFinish, this, _1),
|
std::bind(&WebApiConfigClass::onConfigUploadFinish, this, _1),
|
||||||
std::bind(&WebApiConfigClass::onConfigUpload, this, _1, _2, _3, _4, _5, _6));
|
std::bind(&WebApiConfigClass::onConfigUpload, this, _1, _2, _3, _4, _5, _6));
|
||||||
}
|
}
|
||||||
@ -110,7 +108,7 @@ void WebApiConfigClass::onConfigDelete(AsyncWebServerRequest* request)
|
|||||||
response->setLength();
|
response->setLength();
|
||||||
request->send(response);
|
request->send(response);
|
||||||
|
|
||||||
LittleFS.remove(CONFIG_FILENAME);
|
Utils::removeAllFiles();
|
||||||
Utils::restartDtu();
|
Utils::restartDtu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,10 +16,8 @@ void WebApiDeviceClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
|||||||
{
|
{
|
||||||
using std::placeholders::_1;
|
using std::placeholders::_1;
|
||||||
|
|
||||||
_server = &server;
|
server.on("/api/device/config", HTTP_GET, std::bind(&WebApiDeviceClass::onDeviceAdminGet, this, _1));
|
||||||
|
server.on("/api/device/config", HTTP_POST, std::bind(&WebApiDeviceClass::onDeviceAdminPost, this, _1));
|
||||||
_server->on("/api/device/config", HTTP_GET, std::bind(&WebApiDeviceClass::onDeviceAdminGet, this, _1));
|
|
||||||
_server->on("/api/device/config", HTTP_POST, std::bind(&WebApiDeviceClass::onDeviceAdminPost, this, _1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request)
|
void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request)
|
||||||
@ -184,12 +182,12 @@ void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request)
|
|||||||
config.Led_Single[i].Brightness = min<uint8_t>(100, config.Led_Single[i].Brightness);
|
config.Led_Single[i].Brightness = min<uint8_t>(100, config.Led_Single[i].Brightness);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Display.setDiagramMode(static_cast<DiagramMode_t>(config.Display.Diagram.Mode));
|
||||||
Display.setOrientation(config.Display.Rotation);
|
Display.setOrientation(config.Display.Rotation);
|
||||||
Display.enablePowerSafe = config.Display.PowerSafe;
|
Display.enablePowerSafe = config.Display.PowerSafe;
|
||||||
Display.enableScreensaver = config.Display.ScreenSaver;
|
Display.enableScreensaver = config.Display.ScreenSaver;
|
||||||
Display.setContrast(config.Display.Contrast);
|
Display.setContrast(config.Display.Contrast);
|
||||||
Display.setLanguage(config.Display.Language);
|
Display.setLanguage(config.Display.Language);
|
||||||
Display.setDiagramMode(static_cast<DiagramMode_t>(config.Display.Diagram.Mode));
|
|
||||||
Display.Diagram().updatePeriod();
|
Display.Diagram().updatePeriod();
|
||||||
|
|
||||||
WebApi.writeConfig(retMsg);
|
WebApi.writeConfig(retMsg);
|
||||||
|
|||||||
@ -12,9 +12,7 @@ void WebApiDevInfoClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
|||||||
{
|
{
|
||||||
using std::placeholders::_1;
|
using std::placeholders::_1;
|
||||||
|
|
||||||
_server = &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::onDevInfoStatus(AsyncWebServerRequest* request)
|
void WebApiDevInfoClass::onDevInfoStatus(AsyncWebServerRequest* request)
|
||||||
|
|||||||
@ -18,10 +18,8 @@ void WebApiDtuClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
|||||||
{
|
{
|
||||||
using std::placeholders::_1;
|
using std::placeholders::_1;
|
||||||
|
|
||||||
_server = &server;
|
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_GET, std::bind(&WebApiDtuClass::onDtuAdminGet, this, _1));
|
|
||||||
_server->on("/api/dtu/config", HTTP_POST, std::bind(&WebApiDtuClass::onDtuAdminPost, this, _1));
|
|
||||||
|
|
||||||
scheduler.addTask(_applyDataTask);
|
scheduler.addTask(_applyDataTask);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,9 +11,7 @@ void WebApiEventlogClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
|||||||
{
|
{
|
||||||
using std::placeholders::_1;
|
using std::placeholders::_1;
|
||||||
|
|
||||||
_server = &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::onEventlogStatus(AsyncWebServerRequest* request)
|
void WebApiEventlogClass::onEventlogStatus(AsyncWebServerRequest* request)
|
||||||
|
|||||||
@ -19,9 +19,7 @@ void WebApiFirmwareClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
|||||||
using std::placeholders::_5;
|
using std::placeholders::_5;
|
||||||
using std::placeholders::_6;
|
using std::placeholders::_6;
|
||||||
|
|
||||||
_server = &server;
|
server.on("/api/firmware/update", HTTP_POST,
|
||||||
|
|
||||||
_server->on("/api/firmware/update", HTTP_POST,
|
|
||||||
std::bind(&WebApiFirmwareClass::onFirmwareUpdateFinish, this, _1),
|
std::bind(&WebApiFirmwareClass::onFirmwareUpdateFinish, this, _1),
|
||||||
std::bind(&WebApiFirmwareClass::onFirmwareUpdateUpload, this, _1, _2, _3, _4, _5, _6));
|
std::bind(&WebApiFirmwareClass::onFirmwareUpdateUpload, this, _1, _2, _3, _4, _5, _6));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,10 +11,8 @@ void WebApiGridProfileClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
|||||||
{
|
{
|
||||||
using std::placeholders::_1;
|
using std::placeholders::_1;
|
||||||
|
|
||||||
_server = &server;
|
server.on("/api/gridprofile/status", HTTP_GET, std::bind(&WebApiGridProfileClass::onGridProfileStatus, this, _1));
|
||||||
|
server.on("/api/gridprofile/rawdata", HTTP_GET, std::bind(&WebApiGridProfileClass::onGridProfileRawdata, this, _1));
|
||||||
_server->on("/api/gridprofile/status", HTTP_GET, std::bind(&WebApiGridProfileClass::onGridProfileStatus, this, _1));
|
|
||||||
_server->on("/api/gridprofile/rawdata", HTTP_GET, std::bind(&WebApiGridProfileClass::onGridProfileRawdata, this, _1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebApiGridProfileClass::onGridProfileStatus(AsyncWebServerRequest* request)
|
void WebApiGridProfileClass::onGridProfileStatus(AsyncWebServerRequest* request)
|
||||||
|
|||||||
@ -16,13 +16,11 @@ void WebApiInverterClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
|||||||
{
|
{
|
||||||
using std::placeholders::_1;
|
using std::placeholders::_1;
|
||||||
|
|
||||||
_server = &server;
|
server.on("/api/inverter/list", HTTP_GET, std::bind(&WebApiInverterClass::onInverterList, this, _1));
|
||||||
|
server.on("/api/inverter/add", HTTP_POST, std::bind(&WebApiInverterClass::onInverterAdd, this, _1));
|
||||||
_server->on("/api/inverter/list", HTTP_GET, std::bind(&WebApiInverterClass::onInverterList, this, _1));
|
server.on("/api/inverter/edit", HTTP_POST, std::bind(&WebApiInverterClass::onInverterEdit, this, _1));
|
||||||
_server->on("/api/inverter/add", HTTP_POST, std::bind(&WebApiInverterClass::onInverterAdd, this, _1));
|
server.on("/api/inverter/del", HTTP_POST, std::bind(&WebApiInverterClass::onInverterDelete, this, _1));
|
||||||
_server->on("/api/inverter/edit", HTTP_POST, std::bind(&WebApiInverterClass::onInverterEdit, this, _1));
|
server.on("/api/inverter/order", HTTP_POST, std::bind(&WebApiInverterClass::onInverterOrder, this, _1));
|
||||||
_server->on("/api/inverter/del", HTTP_POST, std::bind(&WebApiInverterClass::onInverterDelete, this, _1));
|
|
||||||
_server->on("/api/inverter/order", HTTP_POST, std::bind(&WebApiInverterClass::onInverterOrder, this, _1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request)
|
void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request)
|
||||||
|
|||||||
@ -14,10 +14,8 @@ void WebApiLimitClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
|||||||
{
|
{
|
||||||
using std::placeholders::_1;
|
using std::placeholders::_1;
|
||||||
|
|
||||||
_server = &server;
|
server.on("/api/limit/status", HTTP_GET, std::bind(&WebApiLimitClass::onLimitStatus, this, _1));
|
||||||
|
server.on("/api/limit/config", HTTP_POST, std::bind(&WebApiLimitClass::onLimitPost, this, _1));
|
||||||
_server->on("/api/limit/status", HTTP_GET, std::bind(&WebApiLimitClass::onLimitStatus, this, _1));
|
|
||||||
_server->on("/api/limit/config", HTTP_POST, std::bind(&WebApiLimitClass::onLimitPost, this, _1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebApiLimitClass::onLimitStatus(AsyncWebServerRequest* request)
|
void WebApiLimitClass::onLimitStatus(AsyncWebServerRequest* request)
|
||||||
|
|||||||
@ -13,9 +13,7 @@ void WebApiMaintenanceClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
|||||||
{
|
{
|
||||||
using std::placeholders::_1;
|
using std::placeholders::_1;
|
||||||
|
|
||||||
_server = &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::onRebootPost(AsyncWebServerRequest* request)
|
void WebApiMaintenanceClass::onRebootPost(AsyncWebServerRequest* request)
|
||||||
|
|||||||
@ -19,11 +19,9 @@ void WebApiMqttClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
|||||||
{
|
{
|
||||||
using std::placeholders::_1;
|
using std::placeholders::_1;
|
||||||
|
|
||||||
_server = &server;
|
server.on("/api/mqtt/status", HTTP_GET, std::bind(&WebApiMqttClass::onMqttStatus, this, _1));
|
||||||
|
server.on("/api/mqtt/config", HTTP_GET, std::bind(&WebApiMqttClass::onMqttAdminGet, this, _1));
|
||||||
_server->on("/api/mqtt/status", HTTP_GET, std::bind(&WebApiMqttClass::onMqttStatus, this, _1));
|
server.on("/api/mqtt/config", HTTP_POST, std::bind(&WebApiMqttClass::onMqttAdminPost, this, _1));
|
||||||
_server->on("/api/mqtt/config", HTTP_GET, std::bind(&WebApiMqttClass::onMqttAdminGet, this, _1));
|
|
||||||
_server->on("/api/mqtt/config", HTTP_POST, std::bind(&WebApiMqttClass::onMqttAdminPost, this, _1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebApiMqttClass::onMqttStatus(AsyncWebServerRequest* request)
|
void WebApiMqttClass::onMqttStatus(AsyncWebServerRequest* request)
|
||||||
@ -85,7 +83,7 @@ void WebApiMqttClass::onMqttAdminGet(AsyncWebServerRequest* request)
|
|||||||
root["mqtt_client_cert"] = config.Mqtt.Tls.ClientCert;
|
root["mqtt_client_cert"] = config.Mqtt.Tls.ClientCert;
|
||||||
root["mqtt_client_key"] = config.Mqtt.Tls.ClientKey;
|
root["mqtt_client_key"] = config.Mqtt.Tls.ClientKey;
|
||||||
root["mqtt_lwt_topic"] = config.Mqtt.Lwt.Topic;
|
root["mqtt_lwt_topic"] = config.Mqtt.Lwt.Topic;
|
||||||
root["mqtt_lwt_online"] = config.Mqtt.CleanSession;
|
root["mqtt_lwt_online"] = config.Mqtt.Lwt.Value_Online;;
|
||||||
root["mqtt_lwt_offline"] = config.Mqtt.Lwt.Value_Offline;
|
root["mqtt_lwt_offline"] = config.Mqtt.Lwt.Value_Offline;
|
||||||
root["mqtt_lwt_qos"] = config.Mqtt.Lwt.Qos;
|
root["mqtt_lwt_qos"] = config.Mqtt.Lwt.Qos;
|
||||||
root["mqtt_publish_interval"] = config.Mqtt.PublishInterval;
|
root["mqtt_publish_interval"] = config.Mqtt.PublishInterval;
|
||||||
|
|||||||
@ -14,11 +14,9 @@ void WebApiNetworkClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
|||||||
{
|
{
|
||||||
using std::placeholders::_1;
|
using std::placeholders::_1;
|
||||||
|
|
||||||
_server = &server;
|
server.on("/api/network/status", HTTP_GET, std::bind(&WebApiNetworkClass::onNetworkStatus, this, _1));
|
||||||
|
server.on("/api/network/config", HTTP_GET, std::bind(&WebApiNetworkClass::onNetworkAdminGet, this, _1));
|
||||||
_server->on("/api/network/status", HTTP_GET, std::bind(&WebApiNetworkClass::onNetworkStatus, this, _1));
|
server.on("/api/network/config", HTTP_POST, std::bind(&WebApiNetworkClass::onNetworkAdminPost, this, _1));
|
||||||
_server->on("/api/network/config", HTTP_GET, std::bind(&WebApiNetworkClass::onNetworkAdminGet, this, _1));
|
|
||||||
_server->on("/api/network/config", HTTP_POST, std::bind(&WebApiNetworkClass::onNetworkAdminPost, this, _1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebApiNetworkClass::onNetworkStatus(AsyncWebServerRequest* request)
|
void WebApiNetworkClass::onNetworkStatus(AsyncWebServerRequest* request)
|
||||||
|
|||||||
@ -15,13 +15,11 @@ void WebApiNtpClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
|||||||
{
|
{
|
||||||
using std::placeholders::_1;
|
using std::placeholders::_1;
|
||||||
|
|
||||||
_server = &server;
|
server.on("/api/ntp/status", HTTP_GET, std::bind(&WebApiNtpClass::onNtpStatus, this, _1));
|
||||||
|
server.on("/api/ntp/config", HTTP_GET, std::bind(&WebApiNtpClass::onNtpAdminGet, this, _1));
|
||||||
_server->on("/api/ntp/status", HTTP_GET, std::bind(&WebApiNtpClass::onNtpStatus, this, _1));
|
server.on("/api/ntp/config", HTTP_POST, std::bind(&WebApiNtpClass::onNtpAdminPost, this, _1));
|
||||||
_server->on("/api/ntp/config", HTTP_GET, std::bind(&WebApiNtpClass::onNtpAdminGet, this, _1));
|
server.on("/api/ntp/time", HTTP_GET, std::bind(&WebApiNtpClass::onNtpTimeGet, this, _1));
|
||||||
_server->on("/api/ntp/config", HTTP_POST, std::bind(&WebApiNtpClass::onNtpAdminPost, this, _1));
|
server.on("/api/ntp/time", HTTP_POST, std::bind(&WebApiNtpClass::onNtpTimePost, this, _1));
|
||||||
_server->on("/api/ntp/time", HTTP_GET, std::bind(&WebApiNtpClass::onNtpTimeGet, this, _1));
|
|
||||||
_server->on("/api/ntp/time", HTTP_POST, std::bind(&WebApiNtpClass::onNtpTimePost, this, _1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebApiNtpClass::onNtpStatus(AsyncWebServerRequest* request)
|
void WebApiNtpClass::onNtpStatus(AsyncWebServerRequest* request)
|
||||||
|
|||||||
@ -12,10 +12,8 @@ void WebApiPowerClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
|||||||
{
|
{
|
||||||
using std::placeholders::_1;
|
using std::placeholders::_1;
|
||||||
|
|
||||||
_server = &server;
|
server.on("/api/power/status", HTTP_GET, std::bind(&WebApiPowerClass::onPowerStatus, this, _1));
|
||||||
|
server.on("/api/power/config", HTTP_POST, std::bind(&WebApiPowerClass::onPowerPost, this, _1));
|
||||||
_server->on("/api/power/status", HTTP_GET, std::bind(&WebApiPowerClass::onPowerStatus, this, _1));
|
|
||||||
_server->on("/api/power/config", HTTP_POST, std::bind(&WebApiPowerClass::onPowerPost, this, _1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebApiPowerClass::onPowerStatus(AsyncWebServerRequest* request)
|
void WebApiPowerClass::onPowerStatus(AsyncWebServerRequest* request)
|
||||||
|
|||||||
@ -45,6 +45,7 @@ void WebApiPowerLimiterClass::onStatus(AsyncWebServerRequest* request)
|
|||||||
root["target_power_consumption_hysteresis"] = config.PowerLimiter.TargetPowerConsumptionHysteresis;
|
root["target_power_consumption_hysteresis"] = config.PowerLimiter.TargetPowerConsumptionHysteresis;
|
||||||
root["lower_power_limit"] = config.PowerLimiter.LowerPowerLimit;
|
root["lower_power_limit"] = config.PowerLimiter.LowerPowerLimit;
|
||||||
root["upper_power_limit"] = config.PowerLimiter.UpperPowerLimit;
|
root["upper_power_limit"] = config.PowerLimiter.UpperPowerLimit;
|
||||||
|
root["ignore_soc"] = config.PowerLimiter.IgnoreSoc;
|
||||||
root["battery_soc_start_threshold"] = config.PowerLimiter.BatterySocStartThreshold;
|
root["battery_soc_start_threshold"] = config.PowerLimiter.BatterySocStartThreshold;
|
||||||
root["battery_soc_stop_threshold"] = config.PowerLimiter.BatterySocStopThreshold;
|
root["battery_soc_stop_threshold"] = config.PowerLimiter.BatterySocStopThreshold;
|
||||||
root["voltage_start_threshold"] = static_cast<int>(config.PowerLimiter.VoltageStartThreshold * 100 +0.5) / 100.0;
|
root["voltage_start_threshold"] = static_cast<int>(config.PowerLimiter.VoltageStartThreshold * 100 +0.5) / 100.0;
|
||||||
@ -133,6 +134,7 @@ void WebApiPowerLimiterClass::onAdminPost(AsyncWebServerRequest* request)
|
|||||||
config.PowerLimiter.TargetPowerConsumptionHysteresis = root["target_power_consumption_hysteresis"].as<int32_t>();
|
config.PowerLimiter.TargetPowerConsumptionHysteresis = root["target_power_consumption_hysteresis"].as<int32_t>();
|
||||||
config.PowerLimiter.LowerPowerLimit = root["lower_power_limit"].as<int32_t>();
|
config.PowerLimiter.LowerPowerLimit = root["lower_power_limit"].as<int32_t>();
|
||||||
config.PowerLimiter.UpperPowerLimit = root["upper_power_limit"].as<int32_t>();
|
config.PowerLimiter.UpperPowerLimit = root["upper_power_limit"].as<int32_t>();
|
||||||
|
config.PowerLimiter.IgnoreSoc = root["ignore_soc"].as<bool>();
|
||||||
config.PowerLimiter.BatterySocStartThreshold = root["battery_soc_start_threshold"].as<uint32_t>();
|
config.PowerLimiter.BatterySocStartThreshold = root["battery_soc_start_threshold"].as<uint32_t>();
|
||||||
config.PowerLimiter.BatterySocStopThreshold = root["battery_soc_stop_threshold"].as<uint32_t>();
|
config.PowerLimiter.BatterySocStopThreshold = root["battery_soc_stop_threshold"].as<uint32_t>();
|
||||||
config.PowerLimiter.VoltageStartThreshold = root["voltage_start_threshold"].as<float>();
|
config.PowerLimiter.VoltageStartThreshold = root["voltage_start_threshold"].as<float>();
|
||||||
|
|||||||
@ -15,9 +15,7 @@ void WebApiPrometheusClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
|||||||
{
|
{
|
||||||
using std::placeholders::_1;
|
using std::placeholders::_1;
|
||||||
|
|
||||||
_server = &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::onPrometheusMetricsGet(AsyncWebServerRequest* request)
|
void WebApiPrometheusClass::onPrometheusMetricsGet(AsyncWebServerRequest* request)
|
||||||
@ -100,7 +98,7 @@ void WebApiPrometheusClass::onPrometheusMetricsGet(AsyncWebServerRequest* reques
|
|||||||
for (auto& c : inv->Statistics()->getChannelsByType(t)) {
|
for (auto& c : inv->Statistics()->getChannelsByType(t)) {
|
||||||
addPanelInfo(stream, serial, i, inv, t, c);
|
addPanelInfo(stream, serial, i, inv, t, c);
|
||||||
for (uint8_t f = 0; f < sizeof(_publishFields) / sizeof(_publishFields[0]); f++) {
|
for (uint8_t f = 0; f < sizeof(_publishFields) / sizeof(_publishFields[0]); f++) {
|
||||||
if (t == TYPE_AC && _publishFields[f].field == FLD_PDC) {
|
if (t == TYPE_INV && _publishFields[f].field == FLD_PDC) {
|
||||||
addField(stream, serial, i, inv, t, c, _publishFields[f].field, _metricTypes[_publishFields[f].type], "PowerDC");
|
addField(stream, serial, i, inv, t, c, _publishFields[f].field, _metricTypes[_publishFields[f].type], "PowerDC");
|
||||||
} else {
|
} else {
|
||||||
addField(stream, serial, i, inv, t, c, _publishFields[f].field, _metricTypes[_publishFields[f].type]);
|
addField(stream, serial, i, inv, t, c, _publishFields[f].field, _metricTypes[_publishFields[f].type]);
|
||||||
|
|||||||
@ -13,11 +13,9 @@ void WebApiSecurityClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
|||||||
{
|
{
|
||||||
using std::placeholders::_1;
|
using std::placeholders::_1;
|
||||||
|
|
||||||
_server = &server;
|
server.on("/api/security/config", HTTP_GET, std::bind(&WebApiSecurityClass::onSecurityGet, this, _1));
|
||||||
|
server.on("/api/security/config", HTTP_POST, std::bind(&WebApiSecurityClass::onSecurityPost, this, _1));
|
||||||
_server->on("/api/security/config", HTTP_GET, std::bind(&WebApiSecurityClass::onSecurityGet, this, _1));
|
server.on("/api/security/authenticate", HTTP_GET, std::bind(&WebApiSecurityClass::onAuthenticateGet, this, _1));
|
||||||
_server->on("/api/security/config", HTTP_POST, std::bind(&WebApiSecurityClass::onSecurityPost, this, _1));
|
|
||||||
_server->on("/api/security/authenticate", HTTP_GET, std::bind(&WebApiSecurityClass::onAuthenticateGet, this, _1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebApiSecurityClass::onSecurityGet(AsyncWebServerRequest* request)
|
void WebApiSecurityClass::onSecurityGet(AsyncWebServerRequest* request)
|
||||||
|
|||||||
@ -24,9 +24,7 @@ void WebApiSysstatusClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
|||||||
{
|
{
|
||||||
using std::placeholders::_1;
|
using std::placeholders::_1;
|
||||||
|
|
||||||
_server = &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::onSystemStatus(AsyncWebServerRequest* request)
|
void WebApiSysstatusClass::onSystemStatus(AsyncWebServerRequest* request)
|
||||||
@ -47,6 +45,8 @@ void WebApiSysstatusClass::onSystemStatus(AsyncWebServerRequest* request)
|
|||||||
root["heap_used"] = ESP.getHeapSize() - ESP.getFreeHeap();
|
root["heap_used"] = ESP.getHeapSize() - ESP.getFreeHeap();
|
||||||
root["heap_max_block"] = ESP.getMaxAllocHeap();
|
root["heap_max_block"] = ESP.getMaxAllocHeap();
|
||||||
root["heap_min_free"] = ESP.getMinFreeHeap();
|
root["heap_min_free"] = ESP.getMinFreeHeap();
|
||||||
|
root["psram_total"] = ESP.getPsramSize();
|
||||||
|
root["psram_used"] = ESP.getPsramSize() - ESP.getFreePsram();
|
||||||
root["sketch_total"] = ESP.getFreeSketchSpace();
|
root["sketch_total"] = ESP.getFreeSketchSpace();
|
||||||
root["sketch_used"] = ESP.getSketchSize();
|
root["sketch_used"] = ESP.getSketchSize();
|
||||||
root["littlefs_total"] = LittleFS.totalBytes();
|
root["littlefs_total"] = LittleFS.totalBytes();
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
* Copyright (C) 2022-2024 Thomas Basler and others
|
* Copyright (C) 2022-2024 Thomas Basler and others
|
||||||
*/
|
*/
|
||||||
#include "WebApi_webapp.h"
|
#include "WebApi_webapp.h"
|
||||||
|
#include <MD5Builder.h>
|
||||||
|
|
||||||
extern const uint8_t file_index_html_start[] asm("_binary_webapp_dist_index_html_gz_start");
|
extern const uint8_t file_index_html_start[] asm("_binary_webapp_dist_index_html_gz_start");
|
||||||
extern const uint8_t file_favicon_ico_start[] asm("_binary_webapp_dist_favicon_ico_start");
|
extern const uint8_t file_favicon_ico_start[] asm("_binary_webapp_dist_favicon_ico_start");
|
||||||
@ -18,62 +19,22 @@ extern const uint8_t file_zones_json_end[] asm("_binary_webapp_dist_zones_json_g
|
|||||||
extern const uint8_t file_app_js_end[] asm("_binary_webapp_dist_js_app_js_gz_end");
|
extern const uint8_t file_app_js_end[] asm("_binary_webapp_dist_js_app_js_gz_end");
|
||||||
extern const uint8_t file_site_webmanifest_end[] asm("_binary_webapp_dist_site_webmanifest_end");
|
extern const uint8_t file_site_webmanifest_end[] asm("_binary_webapp_dist_site_webmanifest_end");
|
||||||
|
|
||||||
#ifdef AUTO_GIT_HASH
|
void WebApiWebappClass::responseBinaryDataWithETagCache(AsyncWebServerRequest *request, const String &contentType, const String &contentEncoding, const uint8_t *content, size_t len)
|
||||||
#define ETAG_HTTP_HEADER_VAL "\"" AUTO_GIT_HASH "\"" // ETag value must be between quotes
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void WebApiWebappClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
|
||||||
{
|
{
|
||||||
_server = &server;
|
auto md5 = MD5Builder();
|
||||||
|
md5.begin();
|
||||||
|
md5.add(const_cast<uint8_t *>(content), len);
|
||||||
|
md5.calculate();
|
||||||
|
|
||||||
_server->on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
|
String expectedEtag;
|
||||||
AsyncWebServerResponse* response = request->beginResponse_P(200, "text/html", file_index_html_start, file_index_html_end - file_index_html_start);
|
expectedEtag = "\"";
|
||||||
response->addHeader("Content-Encoding", "gzip");
|
expectedEtag += md5.toString();
|
||||||
request->send(response);
|
expectedEtag += "\"";
|
||||||
});
|
|
||||||
|
|
||||||
_server->onNotFound([](AsyncWebServerRequest* request) {
|
|
||||||
AsyncWebServerResponse* response = request->beginResponse_P(200, "text/html", file_index_html_start, file_index_html_end - file_index_html_start);
|
|
||||||
response->addHeader("Content-Encoding", "gzip");
|
|
||||||
request->send(response);
|
|
||||||
});
|
|
||||||
|
|
||||||
_server->on("/index.html", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
AsyncWebServerResponse* response = request->beginResponse_P(200, "text/html", file_index_html_start, file_index_html_end - file_index_html_start);
|
|
||||||
response->addHeader("Content-Encoding", "gzip");
|
|
||||||
request->send(response);
|
|
||||||
});
|
|
||||||
|
|
||||||
_server->on("/favicon.ico", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
AsyncWebServerResponse* response = request->beginResponse_P(200, "image/x-icon", file_favicon_ico_start, file_favicon_ico_end - file_favicon_ico_start);
|
|
||||||
request->send(response);
|
|
||||||
});
|
|
||||||
|
|
||||||
_server->on("/favicon.png", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
AsyncWebServerResponse* response = request->beginResponse_P(200, "image/png", file_favicon_png_start, file_favicon_png_end - file_favicon_png_start);
|
|
||||||
request->send(response);
|
|
||||||
});
|
|
||||||
|
|
||||||
_server->on("/zones.json", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
AsyncWebServerResponse* response = request->beginResponse_P(200, "application/json", file_zones_json_start, file_zones_json_end - file_zones_json_start);
|
|
||||||
response->addHeader("Content-Encoding", "gzip");
|
|
||||||
request->send(response);
|
|
||||||
});
|
|
||||||
|
|
||||||
_server->on("/site.webmanifest", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
AsyncWebServerResponse* response = request->beginResponse_P(200, "application/json", file_site_webmanifest_start, file_site_webmanifest_end - file_site_webmanifest_start);
|
|
||||||
request->send(response);
|
|
||||||
});
|
|
||||||
|
|
||||||
_server->on("/js/app.js", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
#ifdef ETAG_HTTP_HEADER_VAL
|
|
||||||
// check client If-None-Match header vs ETag/AUTO_GIT_HASH
|
|
||||||
bool eTagMatch = false;
|
bool eTagMatch = false;
|
||||||
if (request->hasHeader("If-None-Match")) {
|
if (request->hasHeader("If-None-Match")) {
|
||||||
const AsyncWebHeader* h = request->getHeader("If-None-Match");
|
const AsyncWebHeader* h = request->getHeader("If-None-Match");
|
||||||
if (strncmp(ETAG_HTTP_HEADER_VAL, h->value().c_str(), strlen(ETAG_HTTP_HEADER_VAL)) == 0) {
|
eTagMatch = h->value().equals(expectedEtag);
|
||||||
eTagMatch = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// begin response 200 or 304
|
// begin response 200 or 304
|
||||||
@ -81,16 +42,55 @@ void WebApiWebappClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
|||||||
if (eTagMatch) {
|
if (eTagMatch) {
|
||||||
response = request->beginResponse(304);
|
response = request->beginResponse(304);
|
||||||
} else {
|
} else {
|
||||||
response = request->beginResponse_P(200, "text/javascript", file_app_js_start, file_app_js_end - file_app_js_start);
|
response = request->beginResponse_P(200, contentType, content, len);
|
||||||
response->addHeader("Content-Encoding", "gzip");
|
if (contentEncoding.length() > 0) {
|
||||||
|
response->addHeader("Content-Encoding", contentEncoding);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// HTTP requires cache headers in 200 and 304 to be identical
|
// HTTP requires cache headers in 200 and 304 to be identical
|
||||||
response->addHeader("Cache-Control", "public, must-revalidate");
|
response->addHeader("Cache-Control", "public, must-revalidate");
|
||||||
response->addHeader("ETag", ETAG_HTTP_HEADER_VAL);
|
response->addHeader("ETag", expectedEtag);
|
||||||
#else
|
|
||||||
AsyncWebServerResponse* response = request->beginResponse_P(200, "text/javascript", file_app_js_start, file_app_js_end - file_app_js_start);
|
|
||||||
response->addHeader("Content-Encoding", "gzip");
|
|
||||||
#endif
|
|
||||||
request->send(response);
|
request->send(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebApiWebappClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
We don't validate the request header "Accept-Encoding" if gzip compression is supported!
|
||||||
|
We just have the gzipped data available - so we ship them!
|
||||||
|
*/
|
||||||
|
|
||||||
|
server.on("/", HTTP_GET, [&](AsyncWebServerRequest* request) {
|
||||||
|
responseBinaryDataWithETagCache(request, "text/html", "gzip", file_index_html_start, file_index_html_end - file_index_html_start);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.onNotFound([&](AsyncWebServerRequest* request) {
|
||||||
|
responseBinaryDataWithETagCache(request, "text/html", "gzip", file_index_html_start, file_index_html_end - file_index_html_start);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on("/index.html", HTTP_GET, [&](AsyncWebServerRequest* request) {
|
||||||
|
responseBinaryDataWithETagCache(request, "text/html", "gzip", file_index_html_start, file_index_html_end - file_index_html_start);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on("/favicon.ico", HTTP_GET, [&](AsyncWebServerRequest* request) {
|
||||||
|
responseBinaryDataWithETagCache(request, "image/x-icon", "", file_favicon_ico_start, file_favicon_ico_end - file_favicon_ico_start);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on("/favicon.png", HTTP_GET, [&](AsyncWebServerRequest* request) {
|
||||||
|
responseBinaryDataWithETagCache(request, "image/png", "", file_favicon_png_start, file_favicon_png_end - file_favicon_png_start);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on("/zones.json", HTTP_GET, [&](AsyncWebServerRequest* request) {
|
||||||
|
responseBinaryDataWithETagCache(request, "application/json", "gzip", file_zones_json_start, file_zones_json_end - file_zones_json_start);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on("/site.webmanifest", HTTP_GET, [&](AsyncWebServerRequest* request) {
|
||||||
|
responseBinaryDataWithETagCache(request, "application/json", "", file_site_webmanifest_start, file_site_webmanifest_end - file_site_webmanifest_start);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on("/js/app.js", HTTP_GET, [&](AsyncWebServerRequest* request) {
|
||||||
|
responseBinaryDataWithETagCache(request, "text/javascript", "gzip", file_app_js_start, file_app_js_end - file_app_js_start);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,8 +16,7 @@ WebApiWsConsoleClass::WebApiWsConsoleClass()
|
|||||||
|
|
||||||
void WebApiWsConsoleClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
void WebApiWsConsoleClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
||||||
{
|
{
|
||||||
_server = &server;
|
server.addHandler(&_ws);
|
||||||
_server->addHandler(&_ws);
|
|
||||||
MessageOutput.register_ws_output(&_ws);
|
MessageOutput.register_ws_output(&_ws);
|
||||||
|
|
||||||
scheduler.addTask(_wsCleanupTask);
|
scheduler.addTask(_wsCleanupTask);
|
||||||
|
|||||||
@ -3,7 +3,6 @@
|
|||||||
* Copyright (C) 2022-2024 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 "Datastore.h"
|
#include "Datastore.h"
|
||||||
#include "MessageOutput.h"
|
#include "MessageOutput.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
@ -31,10 +30,9 @@ void WebApiWsLiveClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
|||||||
using std::placeholders::_5;
|
using std::placeholders::_5;
|
||||||
using std::placeholders::_6;
|
using std::placeholders::_6;
|
||||||
|
|
||||||
_server = &server;
|
server.on("/api/livedata/status", HTTP_GET, std::bind(&WebApiWsLiveClass::onLivedataStatus, this, _1));
|
||||||
_server->on("/api/livedata/status", HTTP_GET, std::bind(&WebApiWsLiveClass::onLivedataStatus, this, _1));
|
|
||||||
|
|
||||||
_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);
|
scheduler.addTask(_wsCleanupTask);
|
||||||
@ -63,43 +61,6 @@ void WebApiWsLiveClass::sendDataTaskCb()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t maxTimeStamp = 0;
|
|
||||||
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
|
|
||||||
auto inv = Hoymiles.getInverterByPos(i);
|
|
||||||
maxTimeStamp = std::max<uint32_t>(maxTimeStamp, inv->Statistics()->getLastUpdate());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update on every inverter change or at least after 10 seconds
|
|
||||||
if (millis() - _lastWsPublish > (10 * 1000) || (maxTimeStamp != _newestInverterTimestamp)) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
std::lock_guard<std::mutex> lock(_mutex);
|
|
||||||
DynamicJsonDocument root(4200 * INV_MAX_COUNT);
|
|
||||||
if (Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
|
||||||
JsonVariant var = root;
|
|
||||||
generateJsonResponse(var);
|
|
||||||
|
|
||||||
String buffer;
|
|
||||||
serializeJson(root, buffer);
|
|
||||||
|
|
||||||
_ws.textAll(buffer);
|
|
||||||
_newestInverterTimestamp = maxTimeStamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (const std::bad_alloc& bad_alloc) {
|
|
||||||
MessageOutput.printf("Calling /api/livedata/status has temporarily run out of resources. Reason: \"%s\".\r\n", bad_alloc.what());
|
|
||||||
} catch (const std::exception& exc) {
|
|
||||||
MessageOutput.printf("Unknown exception in /api/livedata/status. Reason: \"%s\".\r\n", exc.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
_lastWsPublish = millis();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
|
|
||||||
{
|
|
||||||
JsonArray invArray = root.createNestedArray("inverters");
|
|
||||||
|
|
||||||
// Loop all inverters
|
// Loop all inverters
|
||||||
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
|
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
|
||||||
auto inv = Hoymiles.getInverterByPos(i);
|
auto inv = Hoymiles.getInverterByPos(i);
|
||||||
@ -107,64 +68,43 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonObject invObject = invArray.createNestedObject();
|
const uint32_t lastUpdateInternal = inv->Statistics()->getLastUpdateFromInternal();
|
||||||
INVERTER_CONFIG_T* inv_cfg = Configuration.getInverterConfig(inv->serial());
|
if (!((lastUpdateInternal > 0 && lastUpdateInternal > _lastPublishStats[i]) || (millis() - _lastPublishStats[i] > (10 * 1000)))) {
|
||||||
if (inv_cfg == nullptr) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
invObject["serial"] = inv->serialString();
|
_lastPublishStats[i] = millis();
|
||||||
invObject["name"] = inv->name();
|
|
||||||
invObject["order"] = inv_cfg->Order;
|
|
||||||
invObject["data_age"] = (millis() - inv->Statistics()->getLastUpdate()) / 1000;
|
|
||||||
invObject["poll_enabled"] = inv->getEnablePolling();
|
|
||||||
invObject["reachable"] = inv->isReachable();
|
|
||||||
invObject["producing"] = inv->isProducing();
|
|
||||||
invObject["limit_relative"] = inv->SystemConfigPara()->getLimitPercent();
|
|
||||||
if (inv->DevInfo()->getMaxPower() > 0) {
|
|
||||||
invObject["limit_absolute"] = inv->SystemConfigPara()->getLimitPercent() * inv->DevInfo()->getMaxPower() / 100.0;
|
|
||||||
} else {
|
|
||||||
invObject["limit_absolute"] = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop all channels
|
try {
|
||||||
for (auto& t : inv->Statistics()->getChannelTypes()) {
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
JsonObject chanTypeObj = invObject.createNestedObject(inv->Statistics()->getChannelTypeName(t));
|
DynamicJsonDocument root(4096);
|
||||||
for (auto& c : inv->Statistics()->getChannelsByType(t)) {
|
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
||||||
if (t == TYPE_DC) {
|
continue;
|
||||||
chanTypeObj[String(static_cast<uint8_t>(c))]["name"]["u"] = inv_cfg->channel[c].Name;
|
|
||||||
}
|
|
||||||
addField(chanTypeObj, inv, t, c, FLD_PAC);
|
|
||||||
addField(chanTypeObj, inv, t, c, FLD_UAC);
|
|
||||||
addField(chanTypeObj, inv, t, c, FLD_IAC);
|
|
||||||
if (t == TYPE_AC) {
|
|
||||||
addField(chanTypeObj, inv, t, c, FLD_PDC, "Power DC");
|
|
||||||
} else {
|
|
||||||
addField(chanTypeObj, inv, t, c, FLD_PDC);
|
|
||||||
}
|
|
||||||
addField(chanTypeObj, inv, t, c, FLD_UDC);
|
|
||||||
addField(chanTypeObj, inv, t, c, FLD_IDC);
|
|
||||||
addField(chanTypeObj, inv, t, c, FLD_YD);
|
|
||||||
addField(chanTypeObj, inv, t, c, FLD_YT);
|
|
||||||
addField(chanTypeObj, inv, t, c, FLD_F);
|
|
||||||
addField(chanTypeObj, inv, t, c, FLD_T);
|
|
||||||
addField(chanTypeObj, inv, t, c, FLD_PF);
|
|
||||||
addField(chanTypeObj, inv, t, c, FLD_Q);
|
|
||||||
addField(chanTypeObj, inv, t, c, FLD_EFF);
|
|
||||||
if (t == TYPE_DC && inv->Statistics()->getStringMaxPower(c) > 0) {
|
|
||||||
addField(chanTypeObj, inv, t, c, FLD_IRR);
|
|
||||||
chanTypeObj[String(c)][inv->Statistics()->getChannelFieldName(t, c, FLD_IRR)]["max"] = inv->Statistics()->getStringMaxPower(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
JsonVariant var = root;
|
||||||
|
|
||||||
if (inv->Statistics()->hasChannelFieldValue(TYPE_INV, CH0, FLD_EVT_LOG)) {
|
auto invArray = var.createNestedArray("inverters");
|
||||||
invObject["events"] = inv->EventLog()->getEntryCount();
|
auto invObject = invArray.createNestedObject();
|
||||||
} else {
|
|
||||||
invObject["events"] = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
generateCommonJsonResponse(var);
|
||||||
|
generateInverterCommonJsonResponse(invObject, inv);
|
||||||
|
generateInverterChannelJsonResponse(invObject, inv);
|
||||||
|
|
||||||
|
String buffer;
|
||||||
|
serializeJson(root, buffer);
|
||||||
|
|
||||||
|
_ws.textAll(buffer);
|
||||||
|
|
||||||
|
} catch (const std::bad_alloc& bad_alloc) {
|
||||||
|
MessageOutput.printf("Calling /api/livedata/status has temporarily run out of resources. Reason: \"%s\".\r\n", bad_alloc.what());
|
||||||
|
} catch (const std::exception& exc) {
|
||||||
|
MessageOutput.printf("Unknown exception in /api/livedata/status. Reason: \"%s\".\r\n", exc.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebApiWsLiveClass::generateCommonJsonResponse(JsonVariant& root)
|
||||||
|
{
|
||||||
JsonObject totalObj = root.createNestedObject("total");
|
JsonObject totalObj = root.createNestedObject("total");
|
||||||
addTotalField(totalObj, "Power", Datastore.getTotalAcPowerEnabled(), "W", Datastore.getTotalAcPowerDigits());
|
addTotalField(totalObj, "Power", Datastore.getTotalAcPowerEnabled(), "W", Datastore.getTotalAcPowerDigits());
|
||||||
addTotalField(totalObj, "YieldDay", Datastore.getTotalAcYieldDayEnabled(), "Wh", Datastore.getTotalAcYieldDayDigits());
|
addTotalField(totalObj, "YieldDay", Datastore.getTotalAcYieldDayEnabled(), "Wh", Datastore.getTotalAcYieldDayDigits());
|
||||||
@ -174,11 +114,7 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
|
|||||||
struct tm timeinfo;
|
struct tm timeinfo;
|
||||||
hintObj["time_sync"] = !getLocalTime(&timeinfo, 5);
|
hintObj["time_sync"] = !getLocalTime(&timeinfo, 5);
|
||||||
hintObj["radio_problem"] = (Hoymiles.getRadioNrf()->isInitialized() && (!Hoymiles.getRadioNrf()->isConnected() || !Hoymiles.getRadioNrf()->isPVariant())) || (Hoymiles.getRadioCmt()->isInitialized() && (!Hoymiles.getRadioCmt()->isConnected()));
|
hintObj["radio_problem"] = (Hoymiles.getRadioNrf()->isInitialized() && (!Hoymiles.getRadioNrf()->isConnected() || !Hoymiles.getRadioNrf()->isPVariant())) || (Hoymiles.getRadioCmt()->isInitialized() && (!Hoymiles.getRadioCmt()->isConnected()));
|
||||||
if (!strcmp(Configuration.get().Security.Password, ACCESS_POINT_PASSWORD)) {
|
hintObj["default_password"] = strcmp(Configuration.get().Security.Password, ACCESS_POINT_PASSWORD) == 0;
|
||||||
hintObj["default_password"] = true;
|
|
||||||
} else {
|
|
||||||
hintObj["default_password"] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonObject vedirectObj = root.createNestedObject("vedirect");
|
JsonObject vedirectObj = root.createNestedObject("vedirect");
|
||||||
vedirectObj["enabled"] = Configuration.get().Vedirect.Enabled;
|
vedirectObj["enabled"] = Configuration.get().Vedirect.Enabled;
|
||||||
@ -200,7 +136,73 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
|
|||||||
JsonObject powerMeterObj = root.createNestedObject("power_meter");
|
JsonObject powerMeterObj = root.createNestedObject("power_meter");
|
||||||
powerMeterObj["enabled"] = Configuration.get().PowerMeter.Enabled;
|
powerMeterObj["enabled"] = Configuration.get().PowerMeter.Enabled;
|
||||||
addTotalField(powerMeterObj, "Power", PowerMeter.getPowerTotal(false), "W", 1);
|
addTotalField(powerMeterObj, "Power", PowerMeter.getPowerTotal(false), "W", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebApiWsLiveClass::generateInverterCommonJsonResponse(JsonObject& root, std::shared_ptr<InverterAbstract> inv)
|
||||||
|
{
|
||||||
|
const INVERTER_CONFIG_T* inv_cfg = Configuration.getInverterConfig(inv->serial());
|
||||||
|
if (inv_cfg == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
root["serial"] = inv->serialString();
|
||||||
|
root["name"] = inv->name();
|
||||||
|
root["order"] = inv_cfg->Order;
|
||||||
|
root["data_age"] = (millis() - inv->Statistics()->getLastUpdate()) / 1000;
|
||||||
|
root["poll_enabled"] = inv->getEnablePolling();
|
||||||
|
root["reachable"] = inv->isReachable();
|
||||||
|
root["producing"] = inv->isProducing();
|
||||||
|
root["limit_relative"] = inv->SystemConfigPara()->getLimitPercent();
|
||||||
|
if (inv->DevInfo()->getMaxPower() > 0) {
|
||||||
|
root["limit_absolute"] = inv->SystemConfigPara()->getLimitPercent() * inv->DevInfo()->getMaxPower() / 100.0;
|
||||||
|
} else {
|
||||||
|
root["limit_absolute"] = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebApiWsLiveClass::generateInverterChannelJsonResponse(JsonObject& root, std::shared_ptr<InverterAbstract> inv)
|
||||||
|
{
|
||||||
|
const INVERTER_CONFIG_T* inv_cfg = Configuration.getInverterConfig(inv->serial());
|
||||||
|
if (inv_cfg == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop all channels
|
||||||
|
for (auto& t : inv->Statistics()->getChannelTypes()) {
|
||||||
|
JsonObject chanTypeObj = root.createNestedObject(inv->Statistics()->getChannelTypeName(t));
|
||||||
|
for (auto& c : inv->Statistics()->getChannelsByType(t)) {
|
||||||
|
if (t == TYPE_DC) {
|
||||||
|
chanTypeObj[String(static_cast<uint8_t>(c))]["name"]["u"] = inv_cfg->channel[c].Name;
|
||||||
|
}
|
||||||
|
addField(chanTypeObj, inv, t, c, FLD_PAC);
|
||||||
|
addField(chanTypeObj, inv, t, c, FLD_UAC);
|
||||||
|
addField(chanTypeObj, inv, t, c, FLD_IAC);
|
||||||
|
if (t == TYPE_INV) {
|
||||||
|
addField(chanTypeObj, inv, t, c, FLD_PDC, "Power DC");
|
||||||
|
} else {
|
||||||
|
addField(chanTypeObj, inv, t, c, FLD_PDC);
|
||||||
|
}
|
||||||
|
addField(chanTypeObj, inv, t, c, FLD_UDC);
|
||||||
|
addField(chanTypeObj, inv, t, c, FLD_IDC);
|
||||||
|
addField(chanTypeObj, inv, t, c, FLD_YD);
|
||||||
|
addField(chanTypeObj, inv, t, c, FLD_YT);
|
||||||
|
addField(chanTypeObj, inv, t, c, FLD_F);
|
||||||
|
addField(chanTypeObj, inv, t, c, FLD_T);
|
||||||
|
addField(chanTypeObj, inv, t, c, FLD_PF);
|
||||||
|
addField(chanTypeObj, inv, t, c, FLD_Q);
|
||||||
|
addField(chanTypeObj, inv, t, c, FLD_EFF);
|
||||||
|
if (t == TYPE_DC && inv->Statistics()->getStringMaxPower(c) > 0) {
|
||||||
|
addField(chanTypeObj, inv, t, c, FLD_IRR);
|
||||||
|
chanTypeObj[String(c)][inv->Statistics()->getChannelFieldName(t, c, FLD_IRR)]["max"] = inv->Statistics()->getStringMaxPower(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inv->Statistics()->hasChannelFieldValue(TYPE_INV, CH0, FLD_EVT_LOG)) {
|
||||||
|
root["events"] = inv->EventLog()->getEntryCount();
|
||||||
|
} else {
|
||||||
|
root["events"] = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebApiWsLiveClass::addField(JsonObject& root, 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)
|
||||||
@ -244,10 +246,38 @@ void WebApiWsLiveClass::onLivedataStatus(AsyncWebServerRequest* request)
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
std::lock_guard<std::mutex> lock(_mutex);
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
AsyncJsonResponse* response = new AsyncJsonResponse(false, 4200 * INV_MAX_COUNT);
|
AsyncJsonResponse* response = new AsyncJsonResponse(false, 4096);
|
||||||
auto& root = response->getRoot();
|
auto& root = response->getRoot();
|
||||||
|
|
||||||
generateJsonResponse(root);
|
JsonArray invArray = root.createNestedArray("inverters");
|
||||||
|
|
||||||
|
uint64_t serial = 0;
|
||||||
|
if (request->hasParam("inv")) {
|
||||||
|
String s = request->getParam("inv")->value();
|
||||||
|
serial = strtoll(s.c_str(), NULL, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serial > 0) {
|
||||||
|
auto inv = Hoymiles.getInverterBySerial(serial);
|
||||||
|
if (inv != nullptr) {
|
||||||
|
JsonObject invObject = invArray.createNestedObject();
|
||||||
|
generateInverterCommonJsonResponse(invObject, inv);
|
||||||
|
generateInverterChannelJsonResponse(invObject, inv);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Loop all inverters
|
||||||
|
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
|
||||||
|
auto inv = Hoymiles.getInverterByPos(i);
|
||||||
|
if (inv == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObject invObject = invArray.createNestedObject();
|
||||||
|
generateInverterCommonJsonResponse(invObject, inv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generateCommonJsonResponse(root);
|
||||||
|
|
||||||
response->setLength();
|
response->setLength();
|
||||||
request->send(response);
|
request->send(response);
|
||||||
|
|||||||
@ -34,9 +34,13 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <LittleFS.h>
|
#include <LittleFS.h>
|
||||||
#include <TaskScheduler.h>
|
#include <TaskScheduler.h>
|
||||||
|
#include <esp_heap_caps.h>
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
|
// Move all dynamic allocations >512byte to psram (if available)
|
||||||
|
heap_caps_malloc_extmem_enable(512);
|
||||||
|
|
||||||
// Initialize serial output
|
// Initialize serial output
|
||||||
Serial.begin(SERIAL_BAUDRATE);
|
Serial.begin(SERIAL_BAUDRATE);
|
||||||
#if ARDUINO_USB_CDC_ON_BOOT
|
#if ARDUINO_USB_CDC_ON_BOOT
|
||||||
@ -135,12 +139,12 @@ void setup()
|
|||||||
pin.display_clk,
|
pin.display_clk,
|
||||||
pin.display_cs,
|
pin.display_cs,
|
||||||
pin.display_reset);
|
pin.display_reset);
|
||||||
|
Display.setDiagramMode(static_cast<DiagramMode_t>(config.Display.Diagram.Mode));
|
||||||
Display.setOrientation(config.Display.Rotation);
|
Display.setOrientation(config.Display.Rotation);
|
||||||
Display.enablePowerSafe = config.Display.PowerSafe;
|
Display.enablePowerSafe = config.Display.PowerSafe;
|
||||||
Display.enableScreensaver = config.Display.ScreenSaver;
|
Display.enableScreensaver = config.Display.ScreenSaver;
|
||||||
Display.setContrast(config.Display.Contrast);
|
Display.setContrast(config.Display.Contrast);
|
||||||
Display.setLanguage(config.Display.Language);
|
Display.setLanguage(config.Display.Language);
|
||||||
Display.setDiagramMode(static_cast<DiagramMode_t>(config.Display.Diagram.Mode));
|
|
||||||
Display.setStartupDisplay();
|
Display.setStartupDisplay();
|
||||||
MessageOutput.println("done");
|
MessageOutput.println("done");
|
||||||
|
|
||||||
|
|||||||
@ -18,8 +18,8 @@
|
|||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"sortablejs": "^1.15.2",
|
"sortablejs": "^1.15.2",
|
||||||
"spark-md5": "^3.0.2",
|
"spark-md5": "^3.0.2",
|
||||||
"vue": "^3.4.15",
|
"vue": "^3.4.19",
|
||||||
"vue-i18n": "^9.9.0",
|
"vue-i18n": "^9.9.1",
|
||||||
"vue-router": "^4.2.5"
|
"vue-router": "^4.2.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -27,23 +27,23 @@
|
|||||||
"@rushstack/eslint-patch": "^1.7.2",
|
"@rushstack/eslint-patch": "^1.7.2",
|
||||||
"@tsconfig/node18": "^18.2.2",
|
"@tsconfig/node18": "^18.2.2",
|
||||||
"@types/bootstrap": "^5.2.10",
|
"@types/bootstrap": "^5.2.10",
|
||||||
"@types/node": "^20.11.7",
|
"@types/node": "^20.11.19",
|
||||||
"@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",
|
||||||
"@vitejs/plugin-vue": "^5.0.3",
|
"@vitejs/plugin-vue": "^5.0.4",
|
||||||
"@vue/eslint-config-typescript": "^12.0.0",
|
"@vue/eslint-config-typescript": "^12.0.0",
|
||||||
"@vue/tsconfig": "^0.5.1",
|
"@vue/tsconfig": "^0.5.1",
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^8.56.0",
|
||||||
"eslint-plugin-vue": "^9.20.1",
|
"eslint-plugin-vue": "^9.21.1",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"pulltorefreshjs": "^0.1.22",
|
"pulltorefreshjs": "^0.1.22",
|
||||||
"sass": "^1.70.0",
|
"sass": "^1.71.0",
|
||||||
"terser": "^5.27.0",
|
"terser": "^5.27.1",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
"vite": "^5.0.12",
|
"vite": "^5.1.3",
|
||||||
"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.4.0",
|
||||||
"vue-tsc": "^1.8.27"
|
"vue-tsc": "^1.8.27"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,6 +14,8 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<FsInfo :name="$t('memoryinfo.Heap')" :total="systemStatus.heap_total"
|
<FsInfo :name="$t('memoryinfo.Heap')" :total="systemStatus.heap_total"
|
||||||
:used="systemStatus.heap_used" />
|
:used="systemStatus.heap_used" />
|
||||||
|
<FsInfo :name="$t('memoryinfo.PsRam')" :total="systemStatus.psram_total"
|
||||||
|
:used="systemStatus.psram_used" />
|
||||||
<FsInfo :name="$t('memoryinfo.LittleFs')" :total="systemStatus.littlefs_total"
|
<FsInfo :name="$t('memoryinfo.LittleFs')" :total="systemStatus.littlefs_total"
|
||||||
:used="systemStatus.littlefs_used" />
|
:used="systemStatus.littlefs_used" />
|
||||||
<FsInfo :name="$t('memoryinfo.Sketch')" :total="systemStatus.sketch_total"
|
<FsInfo :name="$t('memoryinfo.Sketch')" :total="systemStatus.sketch_total"
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
<div class="collapse navbar-collapse" ref="navbarCollapse" id="navbarNavAltMarkup">
|
<div class="collapse navbar-collapse" ref="navbarCollapse" id="navbarNavAltMarkup">
|
||||||
<ul class="navbar-nav me-auto">
|
<ul class="navbar-nav navbar-nav-scroll d-flex me-auto flex-sm-fill">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<router-link @click="onClick" class="nav-link" to="/">{{ $t('menu.LiveView') }}</router-link>
|
<router-link @click="onClick" class="nav-link" to="/">{{ $t('menu.LiveView') }}</router-link>
|
||||||
</li>
|
</li>
|
||||||
@ -114,8 +114,7 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<router-link @click="onClick" class="nav-link" to="/about">{{ $t('menu.About') }}</router-link>
|
<router-link @click="onClick" class="nav-link" to="/about">{{ $t('menu.About') }}</router-link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
<li class="flex-sm-fill"></li>
|
||||||
<ul class="navbar-nav flex-row flex-wrap ms-md-auto">
|
|
||||||
<ThemeSwitcher class="me-2" />
|
<ThemeSwitcher class="me-2" />
|
||||||
<form class="d-flex" role="search">
|
<form class="d-flex" role="search">
|
||||||
<LocaleSwitcher class="me-2" />
|
<LocaleSwitcher class="me-2" />
|
||||||
|
|||||||
@ -50,13 +50,13 @@
|
|||||||
"1001": "Einstellungen gespeichert!",
|
"1001": "Einstellungen gespeichert!",
|
||||||
"1002": "Keine Werte gefunden!",
|
"1002": "Keine Werte gefunden!",
|
||||||
"1003": "Daten zu groß!",
|
"1003": "Daten zu groß!",
|
||||||
"1004": "Fehler beim interpretieren der Daten!",
|
"1004": "Fehler beim Interpretieren der Daten!",
|
||||||
"1005": "Benötigte Werte fehlen!",
|
"1005": "Benötigte Werte fehlen!",
|
||||||
"1006": "Schreiben fehlgeschlagen!",
|
"1006": "Schreiben fehlgeschlagen!",
|
||||||
"2001": "Die Seriennummer darf nicht 0 sein!",
|
"2001": "Die Seriennummer darf nicht 0 sein!",
|
||||||
"2002": "Das Abfraginterval muss größer als 0 sein!",
|
"2002": "Das Abfraginterval muss größer als 0 sein!",
|
||||||
"2003": "Ungültige Sendeleistung angegeben!",
|
"2003": "Ungültige Sendeleistung angegeben!",
|
||||||
"2004": "Die Frequenz muss zwischen {min} und {max} kHz liegen und ein vielfaches von 250kHz betragen!",
|
"2004": "Die Frequenz muss zwischen {min} und {max} kHz liegen und ein Vielfaches von 250kHz betragen!",
|
||||||
"2005": "Ungültige Landesauswahl!",
|
"2005": "Ungültige Landesauswahl!",
|
||||||
"3001": "Nichts gelöscht!",
|
"3001": "Nichts gelöscht!",
|
||||||
"3002": "Konfiguration zurückgesetzt. Starte jetzt neu...",
|
"3002": "Konfiguration zurückgesetzt. Starte jetzt neu...",
|
||||||
@ -145,7 +145,8 @@
|
|||||||
"Ok": "Ok",
|
"Ok": "Ok",
|
||||||
"Unknown": "Unbekannt",
|
"Unknown": "Unbekannt",
|
||||||
"ShowGridProfile": "Zeige Grid Profil",
|
"ShowGridProfile": "Zeige Grid Profil",
|
||||||
"GridProfile": "Grid Profil"
|
"GridProfile": "Grid Profil",
|
||||||
|
"LoadingInverter": "Warte auf Daten... (kann bis zu 10 Sekunden dauern)"
|
||||||
},
|
},
|
||||||
"vedirecthome": {
|
"vedirecthome": {
|
||||||
"SerialNumber": "Seriennummer: ",
|
"SerialNumber": "Seriennummer: ",
|
||||||
@ -214,7 +215,7 @@
|
|||||||
"GridprofileSupportLong": "Weitere Informationen sind <a href=\"https://github.com/tbnobody/OpenDTU/wiki/Grid-Profile-Parser\" target=\"_blank\">hier</a> zu finden."
|
"GridprofileSupportLong": "Weitere Informationen sind <a href=\"https://github.com/tbnobody/OpenDTU/wiki/Grid-Profile-Parser\" target=\"_blank\">hier</a> zu finden."
|
||||||
},
|
},
|
||||||
"systeminfo": {
|
"systeminfo": {
|
||||||
"SystemInfo": "System Informationen",
|
"SystemInfo": "Systeminformationen",
|
||||||
"VersionError": "Fehler beim Abrufen von Versionsinformationen",
|
"VersionError": "Fehler beim Abrufen von Versionsinformationen",
|
||||||
"VersionNew": "Neue Version verfügbar! Zeige Änderungen!",
|
"VersionNew": "Neue Version verfügbar! Zeige Änderungen!",
|
||||||
"VersionOk": "Aktuell!"
|
"VersionOk": "Aktuell!"
|
||||||
@ -252,6 +253,7 @@
|
|||||||
"Used": "Benutzt",
|
"Used": "Benutzt",
|
||||||
"Size": "Größe",
|
"Size": "Größe",
|
||||||
"Heap": "Heap",
|
"Heap": "Heap",
|
||||||
|
"PsRam": "PSRAM",
|
||||||
"LittleFs": "LittleFs",
|
"LittleFs": "LittleFs",
|
||||||
"Sketch": "Sketch"
|
"Sketch": "Sketch"
|
||||||
},
|
},
|
||||||
@ -266,7 +268,7 @@
|
|||||||
"RadioInformation": "Funkmodulinformationen",
|
"RadioInformation": "Funkmodulinformationen",
|
||||||
"Status": "{module} Status",
|
"Status": "{module} Status",
|
||||||
"ChipStatus": "{module} Chip-Status",
|
"ChipStatus": "{module} Chip-Status",
|
||||||
"ChipType": "{module} Chip-Type",
|
"ChipType": "{module} Chip-Typ",
|
||||||
"Connected": "verbunden",
|
"Connected": "verbunden",
|
||||||
"NotConnected": "nicht verbunden",
|
"NotConnected": "nicht verbunden",
|
||||||
"Configured": "konfiguriert",
|
"Configured": "konfiguriert",
|
||||||
@ -335,18 +337,18 @@
|
|||||||
"Server": "@:ntpinfo.Server",
|
"Server": "@:ntpinfo.Server",
|
||||||
"Port": "Port",
|
"Port": "Port",
|
||||||
"Username": "Benutzername",
|
"Username": "Benutzername",
|
||||||
"BaseTopic": "Basis Topic",
|
"BaseTopic": "Basis-Topic",
|
||||||
"PublishInterval": "Veröffentlichungsintervall",
|
"PublishInterval": "Veröffentlichungsintervall",
|
||||||
"Seconds": "{sec} Sekunden",
|
"Seconds": "{sec} Sekunden",
|
||||||
"CleanSession": "CleanSession Flag",
|
"CleanSession": "CleanSession Flag",
|
||||||
"Retain": "Retain",
|
"Retain": "Retain",
|
||||||
"Tls": "TLS",
|
"Tls": "TLS",
|
||||||
"RootCertifcateInfo": "Root CA-Zertifikat-Informationen",
|
"RootCertifcateInfo": "Root CA-Zertifikat-Informationen",
|
||||||
"TlsCertLogin": "Anmeldung mit TLS Zertifikat",
|
"TlsCertLogin": "Anmeldung mit TLS-Zertifikat",
|
||||||
"ClientCertifcateInfo": "Client Zertifikat-Informationen",
|
"ClientCertifcateInfo": "Client Zertifikat-Informationen",
|
||||||
"HassSummary": "Home Assistant MQTT-Auto-Discovery Konfigurationszusammenfassung",
|
"HassSummary": "Home Assistant MQTT-Auto-Discovery Konfigurationszusammenfassung",
|
||||||
"Expire": "Ablaufen",
|
"Expire": "Ablaufen",
|
||||||
"IndividualPanels": "Einzelne Paneele",
|
"IndividualPanels": "Einzelne Panels",
|
||||||
"RuntimeSummary": "Laufzeitzusammenfassung",
|
"RuntimeSummary": "Laufzeitzusammenfassung",
|
||||||
"ConnectionStatus": "Verbindungsstatus",
|
"ConnectionStatus": "Verbindungsstatus",
|
||||||
"Connected": "verbunden",
|
"Connected": "verbunden",
|
||||||
@ -367,7 +369,7 @@
|
|||||||
"Console": "Konsole",
|
"Console": "Konsole",
|
||||||
"VirtualDebugConsole": "Virtuelle Debug-Konsole",
|
"VirtualDebugConsole": "Virtuelle Debug-Konsole",
|
||||||
"EnableAutoScroll": "Automatisches Scrollen aktivieren",
|
"EnableAutoScroll": "Automatisches Scrollen aktivieren",
|
||||||
"ClearConsole": "Konsole löschen",
|
"ClearConsole": "Konsole leeren",
|
||||||
"CopyToClipboard": "In die Zwischenablage kopieren"
|
"CopyToClipboard": "In die Zwischenablage kopieren"
|
||||||
},
|
},
|
||||||
"inverterchannelinfo": {
|
"inverterchannelinfo": {
|
||||||
@ -405,7 +407,7 @@
|
|||||||
"PerformReboot": "Neustart durchführen",
|
"PerformReboot": "Neustart durchführen",
|
||||||
"Reboot": "Neustarten!",
|
"Reboot": "Neustarten!",
|
||||||
"Cancel": "@:base.Cancel",
|
"Cancel": "@:base.Cancel",
|
||||||
"RebootOpenDTU": "OpenDTU neustarten",
|
"RebootOpenDTU": "OpenDTU neu starten",
|
||||||
"RebootQuestion": "Möchten Sie das Gerät wirklich neu starten?",
|
"RebootQuestion": "Möchten Sie das Gerät wirklich neu starten?",
|
||||||
"RebootHint": "<b>Hinweis:</b> Ein manueller Neustart muss normalerweise nicht durchgeführt werden. OpenDTU führt jeden erforderlichen Neustart (z. B. nach einem Firmware-Update) automatisch durch. Einstellungen werden auch ohne Neustart übernommen. Wenn Sie aufgrund eines Fehlers einen Neustart durchführen müssen, denken Sie bitte daran, diesen unter <a href=\"https://github.com/helgeerbe/OpenDTU-OnBattery/issues\" class=\"alert-link\" target=\"_blank\">Github</a> zu melden."
|
"RebootHint": "<b>Hinweis:</b> Ein manueller Neustart muss normalerweise nicht durchgeführt werden. OpenDTU führt jeden erforderlichen Neustart (z. B. nach einem Firmware-Update) automatisch durch. Einstellungen werden auch ohne Neustart übernommen. Wenn Sie aufgrund eines Fehlers einen Neustart durchführen müssen, denken Sie bitte daran, diesen unter <a href=\"https://github.com/helgeerbe/OpenDTU-OnBattery/issues\" class=\"alert-link\" target=\"_blank\">Github</a> zu melden."
|
||||||
},
|
},
|
||||||
@ -427,7 +429,7 @@
|
|||||||
"country_1": "Nordamerika ({min}MHz - {max}MHz)",
|
"country_1": "Nordamerika ({min}MHz - {max}MHz)",
|
||||||
"country_2": "Brasilien ({min}MHz - {max}MHz)",
|
"country_2": "Brasilien ({min}MHz - {max}MHz)",
|
||||||
"CmtFrequency": "CMT2300A Frequenz:",
|
"CmtFrequency": "CMT2300A Frequenz:",
|
||||||
"CmtFrequencyHint": "Stelle sicher, dass du nur Frequenzen verwendet werden welche im entsprechenden Land erlaubt sind! Nach einer Frequenzänderung kann es bis zu 15min dauern bis eine Verbindung hergestellt wird.",
|
"CmtFrequencyHint": "Stelle sicher, dass nur Frequenzen verwendet werden, welche im entsprechenden Land erlaubt sind! Nach einer Frequenzänderung kann es bis zu 15min dauern bis eine Verbindung hergestellt wird.",
|
||||||
"CmtFrequencyWarning": "Die gewählte Frequenz liegt außerhalb des zulässigen Bereichs in der gewählten Region/dem Land. Vergewissere dich, dass mit dieser Auswahl keine lokalen Regularien verletzt werden.",
|
"CmtFrequencyWarning": "Die gewählte Frequenz liegt außerhalb des zulässigen Bereichs in der gewählten Region/dem Land. Vergewissere dich, dass mit dieser Auswahl keine lokalen Regularien verletzt werden.",
|
||||||
"MHz": "{mhz} MHz",
|
"MHz": "{mhz} MHz",
|
||||||
"dBm": "{dbm} dBm",
|
"dBm": "{dbm} dBm",
|
||||||
@ -461,7 +463,7 @@
|
|||||||
"NAUTICAL": "Nautische Dämmerung (102°)",
|
"NAUTICAL": "Nautische Dämmerung (102°)",
|
||||||
"CIVIL": "Bürgerliche Dämmerung (96°)",
|
"CIVIL": "Bürgerliche Dämmerung (96°)",
|
||||||
"ASTONOMICAL": "Astronomische Dämmerung (108°)",
|
"ASTONOMICAL": "Astronomische Dämmerung (108°)",
|
||||||
"ManualTimeSynchronization": "Manuelle Zeitsynchronization",
|
"ManualTimeSynchronization": "Manuelle Zeitsynchronisation",
|
||||||
"CurrentOpenDtuTime": "Aktuelle OpenDTU-Zeit:",
|
"CurrentOpenDtuTime": "Aktuelle OpenDTU-Zeit:",
|
||||||
"CurrentLocalTime": "Aktuelle lokale Zeit:",
|
"CurrentLocalTime": "Aktuelle lokale Zeit:",
|
||||||
"SynchronizeTime": "Zeit synchronisieren",
|
"SynchronizeTime": "Zeit synchronisieren",
|
||||||
@ -514,7 +516,7 @@
|
|||||||
"ClientKey": "TLS Client-Key:",
|
"ClientKey": "TLS Client-Key:",
|
||||||
"LwtParameters": "LWT-Parameter",
|
"LwtParameters": "LWT-Parameter",
|
||||||
"LwtTopic": "LWT-Topic:",
|
"LwtTopic": "LWT-Topic:",
|
||||||
"LwtTopicHint": "LWT-Topic, wird der Basis Topic angehängt",
|
"LwtTopicHint": "LWT-Topic, wird der Basis-Topic angehängt",
|
||||||
"LwtOnline": "LWT-Online-Nachricht:",
|
"LwtOnline": "LWT-Online-Nachricht:",
|
||||||
"LwtOnlineHint": "Nachricht, die im LWT-Topic veröffentlicht wird, wenn OpenDTU online ist",
|
"LwtOnlineHint": "Nachricht, die im LWT-Topic veröffentlicht wird, wenn OpenDTU online ist",
|
||||||
"LwtOffline": "LWT-Offline-Nachricht:",
|
"LwtOffline": "LWT-Offline-Nachricht:",
|
||||||
@ -528,7 +530,7 @@
|
|||||||
"HassPrefixTopicHint": "The prefix for the discovery topic",
|
"HassPrefixTopicHint": "The prefix for the discovery topic",
|
||||||
"HassRetain": "Retain Flag aktivieren",
|
"HassRetain": "Retain Flag aktivieren",
|
||||||
"HassExpire": "Ablauffunktion aktivieren",
|
"HassExpire": "Ablauffunktion aktivieren",
|
||||||
"HassIndividual": "Einzelne Paneele"
|
"HassIndividual": "Einzelne Panels"
|
||||||
},
|
},
|
||||||
"vedirectadmin": {
|
"vedirectadmin": {
|
||||||
"VedirectSettings": "VE.Direct Einstellungen",
|
"VedirectSettings": "VE.Direct Einstellungen",
|
||||||
@ -595,17 +597,18 @@
|
|||||||
"LowerPowerLimit": "Unteres Leistungslimit",
|
"LowerPowerLimit": "Unteres Leistungslimit",
|
||||||
"UpperPowerLimit": "Oberes Leistungslimit",
|
"UpperPowerLimit": "Oberes Leistungslimit",
|
||||||
"PowerMeters": "Leistungsmesser",
|
"PowerMeters": "Leistungsmesser",
|
||||||
|
"IgnoreSoc": "Batterie SoC ignorieren",
|
||||||
"BatterySocStartThreshold": "Akku SoC - Start",
|
"BatterySocStartThreshold": "Akku SoC - Start",
|
||||||
"BatterySocStopThreshold": "Akku SoC - Stop",
|
"BatterySocStopThreshold": "Akku SoC - Stop",
|
||||||
"BatterySocSolarPassthroughStartThreshold": "Akku SoC - Start solar passthrough",
|
"BatterySocSolarPassthroughStartThreshold": "Akku SoC - Start solar passthrough",
|
||||||
"BatterySocSolarPassthroughStartThresholdHint": "Wenn der Batterie SOC über diesem Limit ist wird die Inverter Leistung entsprechend der Victron MPPT Leistung gesetzt (abzüglich Effizienzkorrekturfaktor). Kann verwendet werden um überschüssige Solarleistung an das Netz zu liefern wenn die Batterie voll ist.",
|
"BatterySocSolarPassthroughStartThresholdHint": "Wenn der Batterie SoC über diesem Limit ist wird die Inverter Leistung entsprechend der Victron MPPT Leistung gesetzt (abzüglich Effizienzkorrekturfaktor). Kann verwendet werden um überschüssige Solarleistung an das Netz zu liefern wenn die Batterie voll ist.",
|
||||||
"VoltageStartThreshold": "DC Spannung - Start",
|
"VoltageStartThreshold": "DC Spannung - Start",
|
||||||
"VoltageStopThreshold": "DC Spannung - Stop",
|
"VoltageStopThreshold": "DC Spannung - Stop",
|
||||||
"VoltageSolarPassthroughStartThreshold": "DC Spannung - Start Solar-Passthrough",
|
"VoltageSolarPassthroughStartThreshold": "DC Spannung - Start Solar-Passthrough",
|
||||||
"VoltageSolarPassthroughStopThreshold": "DC Spannung - Stop Solar-Passthrough",
|
"VoltageSolarPassthroughStopThreshold": "DC Spannung - Stop Solar-Passthrough",
|
||||||
"VoltageSolarPassthroughStartThresholdHint": "Wenn der Batteriespannung über diesem Limit ist wird die Inverter Leistung entsprechend der Victron MPPT Leistung gesetzt (abzüglich Effizienzkorrekturfaktor). Kann verwendet werden um überschüssige Solarleistung an das Netz zu liefern wenn die Batterie voll ist. Dieser Mode wird aktiv wenn das Start Spannungslimit überschritten wird und inaktiv wenn das Stop Spannungslimit unterschritten wird.",
|
"VoltageSolarPassthroughStartThresholdHint": "Wenn der Batteriespannung über diesem Limit ist wird die Inverter Leistung entsprechend der Victron MPPT Leistung gesetzt (abzüglich Effizienzkorrekturfaktor). Kann verwendet werden um überschüssige Solarleistung an das Netz zu liefern wenn die Batterie voll ist. Dieser Mode wird aktiv wenn das Start Spannungslimit überschritten wird und inaktiv wenn das Stop Spannungslimit unterschritten wird.",
|
||||||
"VoltageLoadCorrectionFactor": "DC Spannung - Lastkorrekturfaktor",
|
"VoltageLoadCorrectionFactor": "DC Spannung - Lastkorrekturfaktor",
|
||||||
"BatterySocInfo": "<b>Hinweis:</b> Die Akku SoC (State of Charge) Werte können nur benutzt werden, wenn die Batterie-Kommunikationsschnittstelle aktiviert ist. Wenn die Batterie innerhalb der letzten Minute keine Werte geschickt hat, werden als Fallback-Option die Spannungseinstellungen verwendet.",
|
"BatterySocInfo": "<b>Hinweis:</b> Die Akku SoC (State of Charge) Werte werden nur benutzt, wenn die Batterie-Kommunikationsschnittstelle innerhalb der letzten Minute gültige Werte geschickt hat. Andernfalls werden als Fallback-Option die Spannungseinstellungen verwendet.",
|
||||||
"InverterIsBehindPowerMeter": "Welchselrichter ist hinter Leistungsmesser",
|
"InverterIsBehindPowerMeter": "Welchselrichter ist hinter Leistungsmesser",
|
||||||
"Battery": "DC / Akku",
|
"Battery": "DC / Akku",
|
||||||
"VoltageLoadCorrectionInfo": "<b>Hinweis:</b> Wenn Leistung von der Batterie abgegeben wird, bricht normalerweise die Spannung etwas ein. Damit nicht vorzeitig der Wechelrichter ausgeschaltet wird sobald der \"Stop\"-Schwellenwert erreicht wird, wird der hier angegebene Korrekturfaktor mit einberechnet. Korrigierte Spannung = DC Spannung + (Aktuelle Leistung (W) * Korrekturfaktor).",
|
"VoltageLoadCorrectionInfo": "<b>Hinweis:</b> Wenn Leistung von der Batterie abgegeben wird, bricht normalerweise die Spannung etwas ein. Damit nicht vorzeitig der Wechelrichter ausgeschaltet wird sobald der \"Stop\"-Schwellenwert erreicht wird, wird der hier angegebene Korrekturfaktor mit einberechnet. Korrigierte Spannung = DC Spannung + (Aktuelle Leistung (W) * Korrekturfaktor).",
|
||||||
@ -621,10 +624,11 @@
|
|||||||
"Provider": "Datenanbieter",
|
"Provider": "Datenanbieter",
|
||||||
"ProviderPylontechCan": "Pylontech per CAN-Bus",
|
"ProviderPylontechCan": "Pylontech per CAN-Bus",
|
||||||
"ProviderJkBmsSerial": "Jikong (JK) BMS per serieller Verbindung",
|
"ProviderJkBmsSerial": "Jikong (JK) BMS per serieller Verbindung",
|
||||||
"ProviderMqtt": "State of Charge (SoC) Wert aus MQTT Broker",
|
"ProviderMqtt": "Batteriewerte aus MQTT Broker",
|
||||||
"ProviderVictron": "Victron SmartShunt per VE.Direct Schnittstelle",
|
"ProviderVictron": "Victron SmartShunt per VE.Direct Schnittstelle",
|
||||||
"MqttConfiguration": "MQTT Einstellungen",
|
"MqttConfiguration": "MQTT Einstellungen",
|
||||||
"MqttTopic": "SoC-Wert Topic",
|
"MqttSocTopic": "Topic für Batterie-SoC",
|
||||||
|
"MqttVoltageTopic": "Topic für Batteriespannung",
|
||||||
"JkBmsConfiguration": "JK BMS Einstellungen",
|
"JkBmsConfiguration": "JK BMS Einstellungen",
|
||||||
"JkBmsInterface": "Schnittstellentyp",
|
"JkBmsInterface": "Schnittstellentyp",
|
||||||
"JkBmsInterfaceUart": "TTL-UART an der MCU",
|
"JkBmsInterfaceUart": "TTL-UART an der MCU",
|
||||||
@ -654,13 +658,13 @@
|
|||||||
"Advanced": "Erweitert",
|
"Advanced": "Erweitert",
|
||||||
"InverterSerial": "Wechselrichter Seriennummer:",
|
"InverterSerial": "Wechselrichter Seriennummer:",
|
||||||
"InverterName": "Wechselrichter Name:",
|
"InverterName": "Wechselrichter Name:",
|
||||||
"InverterNameHint": "Hier kann ein eigener Namen für den Wechselrichter angeben werden.",
|
"InverterNameHint": "Hier kann ein eigener Name für den Wechselrichter angeben werden.",
|
||||||
"InverterStatus": "Empfangen / senden",
|
"InverterStatus": "empfangen / senden",
|
||||||
"PollEnable": "Daten abrufen",
|
"PollEnable": "Daten abrufen",
|
||||||
"PollEnableNight": "Daten auch nachts abrufen",
|
"PollEnableNight": "Daten auch nachts abrufen",
|
||||||
"CommandEnable": "Befehle senden",
|
"CommandEnable": "Befehle senden",
|
||||||
"CommandEnableNight": "Befehle auch nachts senden",
|
"CommandEnableNight": "Befehle auch nachts senden",
|
||||||
"StringName": "Name String {num}:",
|
"StringName": "Stringname {num}:",
|
||||||
"StringNameHint": "Hier kann ein eigener Name für den entsprechenden Port des Wechselrichters angegeben werden.",
|
"StringNameHint": "Hier kann ein eigener Name für den entsprechenden Port des Wechselrichters angegeben werden.",
|
||||||
"StringMaxPower": "Max. Leistung String {num}:",
|
"StringMaxPower": "Max. Leistung String {num}:",
|
||||||
"StringMaxPowerHint": "Eingabe der maximalen Leistung der angeschlossenen Solarmodule.",
|
"StringMaxPowerHint": "Eingabe der maximalen Leistung der angeschlossenen Solarmodule.",
|
||||||
@ -715,12 +719,14 @@
|
|||||||
"Back": "Zurück",
|
"Back": "Zurück",
|
||||||
"Retry": "Wiederholen",
|
"Retry": "Wiederholen",
|
||||||
"OtaStatus": "OTA-Status",
|
"OtaStatus": "OTA-Status",
|
||||||
"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.",
|
"OtaSuccess": "Das Hochladen der Firmware war erfolgreich. Das Gerät wurde automatisch neu gestartet. Wenn das Gerät wieder erreichbar ist, wird die Oberfläche automatisch neu geladen.",
|
||||||
"FirmwareUpload": "Firmware hochladen",
|
"FirmwareUpload": "Firmware hochladen",
|
||||||
"UploadProgress": "Hochlade-Fortschritt"
|
"UploadProgress": "Hochlade-Fortschritt"
|
||||||
},
|
},
|
||||||
"about": {
|
"about": {
|
||||||
"AboutOpendtu": "Über OpenDTU",
|
"AboutOpendtu": "Über OpenDTU",
|
||||||
|
"Documentation": "Dokumentation",
|
||||||
|
"DocumentationBody": "Die Firmware- und Hardware-Dokumentation ist hier zu finden: <a href=\"https://www.opendtu.solar\" target=\"_blank\">https://www.opendtu.solar</a>",
|
||||||
"ProjectOrigin": "Projekt Ursprung",
|
"ProjectOrigin": "Projekt Ursprung",
|
||||||
"ProjectOriginBody1": "Das Projekt wurde aus <a href=\"https://www.mikrocontroller.net/topic/525778\" target=\"_blank\">dieser Diskussion (mikrocontroller.net)</a> heraus gestartet.",
|
"ProjectOriginBody1": "Das Projekt wurde aus <a href=\"https://www.mikrocontroller.net/topic/525778\" target=\"_blank\">dieser Diskussion (mikrocontroller.net)</a> heraus gestartet.",
|
||||||
"ProjectOriginBody2": "Das Hoymiles-Protokoll wurde durch die freiwilligen Bemühungen vieler Teilnehmer entschlüsselt. OpenDTU wurde unter anderem auf der Grundlage dieser Arbeit entwickelt. Das Projekt ist unter einer Open-Source-Lizenz lizenziert (<a href=\"https://www.gnu.de/documents/gpl-2.0.de.html\" target=\"_blank\">GNU General Public License version 2</a>).",
|
"ProjectOriginBody2": "Das Hoymiles-Protokoll wurde durch die freiwilligen Bemühungen vieler Teilnehmer entschlüsselt. OpenDTU wurde unter anderem auf der Grundlage dieser Arbeit entwickelt. Das Projekt ist unter einer Open-Source-Lizenz lizenziert (<a href=\"https://www.gnu.de/documents/gpl-2.0.de.html\" target=\"_blank\">GNU General Public License version 2</a>).",
|
||||||
|
|||||||
@ -145,7 +145,8 @@
|
|||||||
"Ok": "Ok",
|
"Ok": "Ok",
|
||||||
"Unknown": "Unknown",
|
"Unknown": "Unknown",
|
||||||
"ShowGridProfile": "Show Grid Profile",
|
"ShowGridProfile": "Show Grid Profile",
|
||||||
"GridProfile": "Grid Profile"
|
"GridProfile": "Grid Profile",
|
||||||
|
"LoadingInverter": "Waiting for data... (can take up to 10 seconds)"
|
||||||
},
|
},
|
||||||
"vedirecthome": {
|
"vedirecthome": {
|
||||||
"SerialNumber": "Serial Number: ",
|
"SerialNumber": "Serial Number: ",
|
||||||
@ -253,6 +254,7 @@
|
|||||||
"Used": "Used",
|
"Used": "Used",
|
||||||
"Size": "Size",
|
"Size": "Size",
|
||||||
"Heap": "Heap",
|
"Heap": "Heap",
|
||||||
|
"PsRam": "PSRAM",
|
||||||
"LittleFs": "LittleFs",
|
"LittleFs": "LittleFs",
|
||||||
"Sketch": "Sketch"
|
"Sketch": "Sketch"
|
||||||
},
|
},
|
||||||
@ -601,9 +603,7 @@
|
|||||||
"LowerPowerLimit": "Lower power limit",
|
"LowerPowerLimit": "Lower power limit",
|
||||||
"UpperPowerLimit": "Upper power limit",
|
"UpperPowerLimit": "Upper power limit",
|
||||||
"PowerMeters": "Power meter",
|
"PowerMeters": "Power meter",
|
||||||
"MqttTopicPowerMeter1": "MQTT topic - Power meter #1",
|
"IgnoreSoc": "Ignore Battery SoC",
|
||||||
"MqttTopicPowerMeter2": "MQTT topic - Power meter #2 (optional)",
|
|
||||||
"MqttTopicPowerMeter3": "MQTT topic - Power meter #3 (optional)",
|
|
||||||
"BatterySocStartThreshold": "Battery SoC - Start threshold",
|
"BatterySocStartThreshold": "Battery SoC - Start threshold",
|
||||||
"BatterySocStopThreshold": "Battery SoC - Stop threshold",
|
"BatterySocStopThreshold": "Battery SoC - Stop threshold",
|
||||||
"BatterySocSolarPassthroughStartThreshold": "Battery SoC - Start threshold for full solar passthrough",
|
"BatterySocSolarPassthroughStartThreshold": "Battery SoC - Start threshold for full solar passthrough",
|
||||||
@ -614,7 +614,7 @@
|
|||||||
"VoltageSolarPassthroughStopThreshold": "DC Voltage - Stop threshold for full solar passthrough",
|
"VoltageSolarPassthroughStopThreshold": "DC Voltage - Stop threshold for full solar passthrough",
|
||||||
"VoltageSolarPassthroughStartThresholdHint": "Inverter power is set according to Victron MPPT power (minus efficiency factors) when full solar passthrough is active. Use this if you like to supply excess power to the grid when battery is full. This is started when battery voltage goes over this limit and stopped if voltage drops below stop limit.",
|
"VoltageSolarPassthroughStartThresholdHint": "Inverter power is set according to Victron MPPT power (minus efficiency factors) when full solar passthrough is active. Use this if you like to supply excess power to the grid when battery is full. This is started when battery voltage goes over this limit and stopped if voltage drops below stop limit.",
|
||||||
"VoltageLoadCorrectionFactor": "DC Voltage - Load correction factor",
|
"VoltageLoadCorrectionFactor": "DC Voltage - Load correction factor",
|
||||||
"BatterySocInfo": "<b>Hint:</b> The battery SoC (State of Charge) values can only be used if the battery communication interface is enabled. If the battery has not reported any SoC updates in the last minute, the voltage thresholds will be used as fallback.",
|
"BatterySocInfo": "<b>Hint:</b> The battery SoC (State of Charge) values are only used if the battery communication interface reported SoC updates in the last minute. Otherwise the voltage thresholds will be used as fallback.",
|
||||||
"InverterIsBehindPowerMeter": "Inverter is behind Power meter",
|
"InverterIsBehindPowerMeter": "Inverter is behind Power meter",
|
||||||
"Battery": "DC / Battery",
|
"Battery": "DC / Battery",
|
||||||
"VoltageLoadCorrectionInfo": "<b>Hint:</b> When the power output is higher, the voltage is usually decreasing. In order to not stop the inverter too early (Stop treshold), a power factor can be specified here to correct this. Corrected voltage = DC Voltage + (Current power * correction factor).",
|
"VoltageLoadCorrectionInfo": "<b>Hint:</b> When the power output is higher, the voltage is usually decreasing. In order to not stop the inverter too early (Stop treshold), a power factor can be specified here to correct this. Corrected voltage = DC Voltage + (Current power * correction factor).",
|
||||||
@ -630,10 +630,11 @@
|
|||||||
"Provider": "Data Provider",
|
"Provider": "Data Provider",
|
||||||
"ProviderPylontechCan": "Pylontech using CAN bus",
|
"ProviderPylontechCan": "Pylontech using CAN bus",
|
||||||
"ProviderJkBmsSerial": "Jikong (JK) BMS using serial connection",
|
"ProviderJkBmsSerial": "Jikong (JK) BMS using serial connection",
|
||||||
"ProviderMqtt": "State of Charge (SoC) value from MQTT broker",
|
"ProviderMqtt": "Battery data from MQTT broker",
|
||||||
"ProviderVictron": "Victron SmartShunt using VE.Direct interface",
|
"ProviderVictron": "Victron SmartShunt using VE.Direct interface",
|
||||||
"MqttConfiguration": "MQTT Settings",
|
"MqttConfiguration": "MQTT Settings",
|
||||||
"MqttTopic": "SoC value topic",
|
"MqttSocTopic": "SoC value topic",
|
||||||
|
"MqttVoltageTopic": "Voltage value topic",
|
||||||
"JkBmsConfiguration": "JK BMS Settings",
|
"JkBmsConfiguration": "JK BMS Settings",
|
||||||
"JkBmsInterface": "Interface Type",
|
"JkBmsInterface": "Interface Type",
|
||||||
"JkBmsInterfaceUart": "TTL-UART on MCU",
|
"JkBmsInterfaceUart": "TTL-UART on MCU",
|
||||||
@ -730,6 +731,8 @@
|
|||||||
},
|
},
|
||||||
"about": {
|
"about": {
|
||||||
"AboutOpendtu": "About OpenDTU",
|
"AboutOpendtu": "About OpenDTU",
|
||||||
|
"Documentation": "Documentation",
|
||||||
|
"DocumentationBody": "The firmware and hardware documentation can be found here: <a href=\"https://www.opendtu.solar\" target=\"_blank\">https://www.opendtu.solar</a>",
|
||||||
"ProjectOrigin": "Project Origin",
|
"ProjectOrigin": "Project Origin",
|
||||||
"ProjectOriginBody1": "This project was started from <a href=\"https://www.mikrocontroller.net/topic/525778\" target=\"_blank\">this discussion. (Mikrocontroller.net)</a>",
|
"ProjectOriginBody1": "This project was started from <a href=\"https://www.mikrocontroller.net/topic/525778\" target=\"_blank\">this discussion. (Mikrocontroller.net)</a>",
|
||||||
"ProjectOriginBody2": "The Hoymiles protocol was decrypted through the voluntary efforts of many participants. OpenDTU, among others, was developed based on this work. The project is licensed under an Open Source License (<a href=\"https://www.gnu.de/documents/gpl-2.0.de.html\" target=\"_blank\">GNU General Public License version 2</a>).",
|
"ProjectOriginBody2": "The Hoymiles protocol was decrypted through the voluntary efforts of many participants. OpenDTU, among others, was developed based on this work. The project is licensed under an Open Source License (<a href=\"https://www.gnu.de/documents/gpl-2.0.de.html\" target=\"_blank\">GNU General Public License version 2</a>).",
|
||||||
|
|||||||
@ -145,7 +145,43 @@
|
|||||||
"Ok": "OK",
|
"Ok": "OK",
|
||||||
"Unknown": "Inconnu",
|
"Unknown": "Inconnu",
|
||||||
"ShowGridProfile": "Show Grid Profile",
|
"ShowGridProfile": "Show Grid Profile",
|
||||||
"GridProfile": "Grid Profile"
|
"GridProfile": "Grid Profile",
|
||||||
|
"LoadingInverter": "Waiting for data... (can take up to 10 seconds)"
|
||||||
|
},
|
||||||
|
"vedirecthome": {
|
||||||
|
"SerialNumber": "Serial Number: ",
|
||||||
|
"FirmwareNumber": "Firmware Number: ",
|
||||||
|
"DataAge": "Data Age: ",
|
||||||
|
"Seconds": "{val} seconds",
|
||||||
|
"DeviceInfo": "Device Info",
|
||||||
|
"Property": "Property",
|
||||||
|
"Value": "Value",
|
||||||
|
"Unit": "Unit",
|
||||||
|
"LoadOutputState": "Load output state",
|
||||||
|
"StateOfOperation": "State of operation",
|
||||||
|
"TrackerOperationMode": "Tracker operation mode",
|
||||||
|
"OffReason": "Off reason",
|
||||||
|
"ErrorCode": "Error code",
|
||||||
|
"DaySequenceNumber": "Day sequence number (0..364)",
|
||||||
|
"Battery": "Output (Battery)",
|
||||||
|
"output": {
|
||||||
|
"P": "Power (calculated)",
|
||||||
|
"V": "Voltage",
|
||||||
|
"I": "Current",
|
||||||
|
"E": "Efficiency (calculated)"
|
||||||
|
},
|
||||||
|
"Panel": "Input (Solar Panels)",
|
||||||
|
"input": {
|
||||||
|
"PPV": "Power",
|
||||||
|
"VPV": "Voltage",
|
||||||
|
"IPV": "Current (calculated)",
|
||||||
|
"YieldToday": "Yield today",
|
||||||
|
"YieldYesterday": "Yield yesterday",
|
||||||
|
"YieldTotal": "Yield total (user resettable counter)",
|
||||||
|
"MaximumPowerToday": "Maximum power today",
|
||||||
|
"MaximumPowerYesterday": "Maximum power yesterday"
|
||||||
|
},
|
||||||
|
"PowerLimiterState": "Power limiter state [off (charging), solar passthrough, on battery]"
|
||||||
},
|
},
|
||||||
"vedirecthome": {
|
"vedirecthome": {
|
||||||
"SerialNumber": "Serial Number: ",
|
"SerialNumber": "Serial Number: ",
|
||||||
@ -252,6 +288,7 @@
|
|||||||
"Used": "Utilisée",
|
"Used": "Utilisée",
|
||||||
"Size": "Taille",
|
"Size": "Taille",
|
||||||
"Heap": "Heap",
|
"Heap": "Heap",
|
||||||
|
"PsRam": "PSRAM",
|
||||||
"LittleFs": "LittleFs",
|
"LittleFs": "LittleFs",
|
||||||
"Sketch": "Sketch"
|
"Sketch": "Sketch"
|
||||||
},
|
},
|
||||||
@ -546,10 +583,11 @@
|
|||||||
"Provider": "Data Provider",
|
"Provider": "Data Provider",
|
||||||
"ProviderPylontechCan": "Pylontech using CAN bus",
|
"ProviderPylontechCan": "Pylontech using CAN bus",
|
||||||
"ProviderJkBmsSerial": "Jikong (JK) BMS using serial connection",
|
"ProviderJkBmsSerial": "Jikong (JK) BMS using serial connection",
|
||||||
"ProviderMqtt": "State of Charge (SoC) value from MQTT broker",
|
"ProviderMqtt": "Battery data from MQTT broker",
|
||||||
"ProviderVictron": "Victron SmartShunt using VE.Direct interface",
|
"ProviderVictron": "Victron SmartShunt using VE.Direct interface",
|
||||||
"MqttConfiguration": "MQTT Settings",
|
"MqttConfiguration": "MQTT Settings",
|
||||||
"MqttTopic": "SoC value topic",
|
"MqttSocTopic": "SoC value topic",
|
||||||
|
"MqttVoltageTopic": "Voltage value topic",
|
||||||
"JkBmsConfiguration": "JK BMS Settings",
|
"JkBmsConfiguration": "JK BMS Settings",
|
||||||
"JkBmsInterface": "Interface Type",
|
"JkBmsInterface": "Interface Type",
|
||||||
"JkBmsInterfaceUart": "TTL-UART on MCU",
|
"JkBmsInterfaceUart": "TTL-UART on MCU",
|
||||||
@ -648,9 +686,7 @@
|
|||||||
"LowerPowerLimit": "Lower power limit",
|
"LowerPowerLimit": "Lower power limit",
|
||||||
"UpperPowerLimit": "Upper power limit",
|
"UpperPowerLimit": "Upper power limit",
|
||||||
"PowerMeters": "Power meter",
|
"PowerMeters": "Power meter",
|
||||||
"MqttTopicPowerMeter1": "MQTT topic - Power meter #1",
|
"IgnoreSoc": "Ignore Battery SoC",
|
||||||
"MqttTopicPowerMeter2": "MQTT topic - Power meter #2 (optional)",
|
|
||||||
"MqttTopicPowerMeter3": "MQTT topic - Power meter #3 (optional)",
|
|
||||||
"BatterySocStartThreshold": "Battery SoC - Start threshold",
|
"BatterySocStartThreshold": "Battery SoC - Start threshold",
|
||||||
"BatterySocStopThreshold": "Battery SoC - Stop threshold",
|
"BatterySocStopThreshold": "Battery SoC - Stop threshold",
|
||||||
"BatterySocSolarPassthroughStartThreshold": "Battery SoC - Start threshold for full solar passthrough",
|
"BatterySocSolarPassthroughStartThreshold": "Battery SoC - Start threshold for full solar passthrough",
|
||||||
@ -661,7 +697,7 @@
|
|||||||
"VoltageSolarPassthroughStopThreshold": "DC Voltage - Stop threshold for full solar passthrough",
|
"VoltageSolarPassthroughStopThreshold": "DC Voltage - Stop threshold for full solar passthrough",
|
||||||
"VoltageSolarPassthroughStartThresholdHint": "Inverter power is set according to Victron MPPT power (minus efficiency factors) when full solar passthrough is active. Use this if you like to supply excess power to the grid when battery is full. This is started when battery voltage goes over this limit and stopped if voltage drops below stop limit.",
|
"VoltageSolarPassthroughStartThresholdHint": "Inverter power is set according to Victron MPPT power (minus efficiency factors) when full solar passthrough is active. Use this if you like to supply excess power to the grid when battery is full. This is started when battery voltage goes over this limit and stopped if voltage drops below stop limit.",
|
||||||
"VoltageLoadCorrectionFactor": "DC Voltage - Load correction factor",
|
"VoltageLoadCorrectionFactor": "DC Voltage - Load correction factor",
|
||||||
"BatterySocInfo": "<b>Hint:</b> The battery SoC (State of Charge) values can only be used if the battery communication interface is enabled. If the battery has not reported any SoC updates in the last minute, the voltage thresholds will be used as fallback.",
|
"BatterySocInfo": "<b>Hint:</b> The battery SoC (State of Charge) values are only used if the battery communication interface reported SoC updates in the last minute. Otherwise the voltage thresholds will be used as fallback.",
|
||||||
"InverterIsBehindPowerMeter": "Inverter is behind Power meter",
|
"InverterIsBehindPowerMeter": "Inverter is behind Power meter",
|
||||||
"Battery": "DC / Battery",
|
"Battery": "DC / Battery",
|
||||||
"VoltageLoadCorrectionInfo": "<b>Hint:</b> When the power output is higher, the voltage is usually decreasing. In order to not stop the inverter too early (Stop treshold), a power factor can be specified here to correct this. Corrected voltage = DC Voltage + (Current power * correction factor)."
|
"VoltageLoadCorrectionInfo": "<b>Hint:</b> When the power output is higher, the voltage is usually decreasing. In order to not stop the inverter too early (Stop treshold), a power factor can be specified here to correct this. Corrected voltage = DC Voltage + (Current power * correction factor)."
|
||||||
@ -688,6 +724,8 @@
|
|||||||
},
|
},
|
||||||
"about": {
|
"about": {
|
||||||
"AboutOpendtu": "À propos d'OpenDTU",
|
"AboutOpendtu": "À propos d'OpenDTU",
|
||||||
|
"Documentation": "Documentation",
|
||||||
|
"DocumentationBody": "The firmware and hardware documentation can be found here: <a href=\"https://www.opendtu.solar\" target=\"_blank\">https://www.opendtu.solar</a>",
|
||||||
"ProjectOrigin": "Origine du projet",
|
"ProjectOrigin": "Origine du projet",
|
||||||
"ProjectOriginBody1": "Ce projet a été démarré suite à cette discussion <a href=\"https://www.mikrocontroller.net/topic/525778\" target=\"_blank\">(Mikrocontroller.net)</a>.",
|
"ProjectOriginBody1": "Ce projet a été démarré suite à cette discussion <a href=\"https://www.mikrocontroller.net/topic/525778\" target=\"_blank\">(Mikrocontroller.net)</a>.",
|
||||||
"ProjectOriginBody2": "Le protocole Hoymiles a été décrypté grâce aux efforts volontaires de nombreux participants. OpenDTU, entre autres, a été développé sur la base de ce travail. Le projet est sous licence Open Source (<a href=\"https://www.gnu.de/documents/gpl-2.0.de.html\" target=\"_blank\">GNU General Public License version 2</a>).",
|
"ProjectOriginBody2": "Le protocole Hoymiles a été décrypté grâce aux efforts volontaires de nombreux participants. OpenDTU, entre autres, a été développé sur la base de ce travail. Le projet est sous licence Open Source (<a href=\"https://www.gnu.de/documents/gpl-2.0.de.html\" target=\"_blank\">GNU General Public License version 2</a>).",
|
||||||
|
|||||||
@ -4,5 +4,6 @@ export interface BatteryConfig {
|
|||||||
provider: number;
|
provider: number;
|
||||||
jkbms_interface: number;
|
jkbms_interface: number;
|
||||||
jkbms_polling_interval: number;
|
jkbms_polling_interval: number;
|
||||||
mqtt_topic: string;
|
mqtt_soc_topic: string;
|
||||||
|
mqtt_voltage_topic: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ export interface PowerLimiterConfig {
|
|||||||
target_power_consumption_hysteresis: number;
|
target_power_consumption_hysteresis: number;
|
||||||
lower_power_limit: number;
|
lower_power_limit: number;
|
||||||
upper_power_limit: number;
|
upper_power_limit: number;
|
||||||
|
ignore_soc: boolean;
|
||||||
battery_soc_start_threshold: number;
|
battery_soc_start_threshold: number;
|
||||||
battery_soc_stop_threshold: number;
|
battery_soc_stop_threshold: number;
|
||||||
voltage_start_threshold: number;
|
voltage_start_threshold: number;
|
||||||
|
|||||||
@ -26,6 +26,8 @@ export interface SystemStatus {
|
|||||||
heap_min_free: number;
|
heap_min_free: number;
|
||||||
littlefs_total: number;
|
littlefs_total: number;
|
||||||
littlefs_used: number;
|
littlefs_used: number;
|
||||||
|
psram_total: number;
|
||||||
|
psram_used: number;
|
||||||
sketch_total: number;
|
sketch_total: number;
|
||||||
sketch_used: number;
|
sketch_used: number;
|
||||||
// RadioInfo
|
// RadioInfo
|
||||||
|
|||||||
@ -2,15 +2,31 @@
|
|||||||
<BasePage :title="$t('about.AboutOpendtu')">
|
<BasePage :title="$t('about.AboutOpendtu')">
|
||||||
<div class="accordion" id="accordionExample">
|
<div class="accordion" id="accordionExample">
|
||||||
<div class="accordion-item">
|
<div class="accordion-item">
|
||||||
<h2 class="accordion-header" id="headingOne">
|
<h2 class="accordion-header" id="headingDocumentation">
|
||||||
<button class="accordion-button" type="button" data-bs-toggle="collapse"
|
<button class="accordion-button" type="button" data-bs-toggle="collapse"
|
||||||
data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
|
data-bs-target="#collapseDocumentation" aria-expanded="true" aria-controls="collapseOne">
|
||||||
|
<span class="badge text-bg-secondary">
|
||||||
|
<BIconInfoCircle class="fs-4" />
|
||||||
|
</span> {{ $t('about.Documentation') }}
|
||||||
|
</button>
|
||||||
|
</h2>
|
||||||
|
<div id="collapseDocumentation" class="accordion-collapse collapse show" aria-labelledby="headingDocumentation"
|
||||||
|
data-bs-parent="#accordionExample">
|
||||||
|
<div class="accordion-body">
|
||||||
|
<p class="fw-normal" v-html="$t('about.DocumentationBody')"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="accordion-item">
|
||||||
|
<h2 class="accordion-header" id="headingOne">
|
||||||
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
|
||||||
|
data-bs-target="#collapseOne" aria-expanded="false" aria-controls="collapseOne">
|
||||||
<span class="badge text-bg-secondary">
|
<span class="badge text-bg-secondary">
|
||||||
<BIconInfoCircle class="fs-4" />
|
<BIconInfoCircle class="fs-4" />
|
||||||
</span> {{ $t('about.ProjectOrigin') }}
|
</span> {{ $t('about.ProjectOrigin') }}
|
||||||
</button>
|
</button>
|
||||||
</h2>
|
</h2>
|
||||||
<div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne"
|
<div id="collapseOne" class="accordion-collapse collapse" aria-labelledby="headingOne"
|
||||||
data-bs-parent="#accordionExample">
|
data-bs-parent="#accordionExample">
|
||||||
<div class="accordion-body">
|
<div class="accordion-body">
|
||||||
<p class="fw-normal" v-html="$t('about.ProjectOriginBody1')"></p>
|
<p class="fw-normal" v-html="$t('about.ProjectOriginBody1')"></p>
|
||||||
|
|||||||
@ -53,11 +53,21 @@
|
|||||||
:text="$t('batteryadmin.MqttConfiguration')" textVariant="text-bg-primary" addSpace>
|
:text="$t('batteryadmin.MqttConfiguration')" textVariant="text-bg-primary" addSpace>
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<label class="col-sm-2 col-form-label">
|
<label class="col-sm-2 col-form-label">
|
||||||
{{ $t('batteryadmin.MqttTopic') }}
|
{{ $t('batteryadmin.MqttSocTopic') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" class="form-control" v-model="batteryConfigList.mqtt_topic" />
|
<input type="text" class="form-control" v-model="batteryConfigList.mqtt_soc_topic" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="col-sm-2 col-form-label">
|
||||||
|
{{ $t('batteryadmin.MqttVoltageTopic') }}
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control" v-model="batteryConfigList.mqtt_voltage_topic" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -103,6 +103,7 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="row flex-row-reverse flex-wrap-reverse g-3">
|
<div class="row flex-row-reverse flex-wrap-reverse g-3">
|
||||||
<template v-for="chanType in [{obj: inverter.INV, name: 'INV'}, {obj: inverter.AC, name: 'AC'}, {obj: inverter.DC, name: 'DC'}].reverse()">
|
<template v-for="chanType in [{obj: inverter.INV, name: 'INV'}, {obj: inverter.AC, name: 'AC'}, {obj: inverter.DC, name: 'DC'}].reverse()">
|
||||||
|
<template v-if="chanType.obj != null">
|
||||||
<template v-for="channel in Object.keys(chanType.obj).sort().reverse().map(x=>+x)" :key="channel">
|
<template v-for="channel in Object.keys(chanType.obj).sort().reverse().map(x=>+x)" :key="channel">
|
||||||
<template v-if="(chanType.name != 'DC') ||
|
<template v-if="(chanType.name != 'DC') ||
|
||||||
(chanType.name == 'DC' && getSumIrridiation(inverter) == 0) ||
|
(chanType.name == 'DC' && getSumIrridiation(inverter) == 0) ||
|
||||||
@ -116,7 +117,16 @@
|
|||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
<BootstrapAlert class="m-3" :show="!inverter.hasOwnProperty('INV')">
|
||||||
|
<div class="d-flex justify-content-center align-items-center">
|
||||||
|
<div class="spinner-border m-1" role="status">
|
||||||
|
<span class="visually-hidden">{{ $t('home.LoadingInverter') }}</span>
|
||||||
|
</div>
|
||||||
|
<span>{{ $t('home.LoadingInverter') }}</span>
|
||||||
|
</div>
|
||||||
|
</BootstrapAlert>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -450,7 +460,16 @@ export default defineComponent({
|
|||||||
this.socket.onmessage = (event) => {
|
this.socket.onmessage = (event) => {
|
||||||
console.log(event);
|
console.log(event);
|
||||||
if (event.data != "{}") {
|
if (event.data != "{}") {
|
||||||
this.liveData = JSON.parse(event.data);
|
const newData = JSON.parse(event.data);
|
||||||
|
Object.assign(this.liveData.total, newData.total);
|
||||||
|
Object.assign(this.liveData.hints, newData.hints);
|
||||||
|
|
||||||
|
const foundIdx = this.liveData.inverters.findIndex((element) => element.serial == newData.inverters[0].serial);
|
||||||
|
if (foundIdx == -1) {
|
||||||
|
Object.assign(this.liveData.inverters, newData.inverters);
|
||||||
|
} else {
|
||||||
|
Object.assign(this.liveData.inverters[foundIdx], newData.inverters[0]);
|
||||||
|
}
|
||||||
this.dataLoading = false;
|
this.dataLoading = false;
|
||||||
this.heartCheck(); // Reset heartbeat detection
|
this.heartCheck(); // Reset heartbeat detection
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -142,7 +142,12 @@
|
|||||||
<CardElement :text="$t('powerlimiteradmin.Battery')" textVariant="text-bg-primary" add-space
|
<CardElement :text="$t('powerlimiteradmin.Battery')" textVariant="text-bg-primary" add-space
|
||||||
v-show="powerLimiterConfigList.enabled"
|
v-show="powerLimiterConfigList.enabled"
|
||||||
>
|
>
|
||||||
<div class="row mb-3">
|
<InputElement
|
||||||
|
:label="$t('powerlimiteradmin.IgnoreSoc')"
|
||||||
|
v-model="powerLimiterConfigList.ignore_soc"
|
||||||
|
type="checkbox"/>
|
||||||
|
|
||||||
|
<div class="row mb-3" v-show="!powerLimiterConfigList.ignore_soc">
|
||||||
<label for="batterySocStartThreshold" class="col-sm-2 col-form-label">{{ $t('powerlimiteradmin.BatterySocStartThreshold') }}:</label>
|
<label for="batterySocStartThreshold" class="col-sm-2 col-form-label">{{ $t('powerlimiteradmin.BatterySocStartThreshold') }}:</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
@ -154,7 +159,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3" v-show="!powerLimiterConfigList.ignore_soc">
|
||||||
<label for="batterySocStopThreshold" class="col-sm-2 col-form-label">{{ $t('powerlimiteradmin.BatterySocStopThreshold') }}</label>
|
<label for="batterySocStopThreshold" class="col-sm-2 col-form-label">{{ $t('powerlimiteradmin.BatterySocStopThreshold') }}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
@ -166,7 +171,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mb-3" v-show="powerLimiterConfigList.solar_passthrough_enabled">
|
<div class="row mb-3" v-show="powerLimiterConfigList.solar_passthrough_enabled && !powerLimiterConfigList.ignore_soc">
|
||||||
<label for="batterySocSolarPassthroughStartThreshold" class="col-sm-2 col-form-label">{{ $t('powerlimiteradmin.BatterySocSolarPassthroughStartThreshold') }}
|
<label for="batterySocSolarPassthroughStartThreshold" class="col-sm-2 col-form-label">{{ $t('powerlimiteradmin.BatterySocSolarPassthroughStartThreshold') }}
|
||||||
<BIconInfoCircle v-tooltip :title="$t('powerlimiteradmin.BatterySocSolarPassthroughStartThresholdHint')" />
|
<BIconInfoCircle v-tooltip :title="$t('powerlimiteradmin.BatterySocSolarPassthroughStartThresholdHint')" />
|
||||||
</label>
|
</label>
|
||||||
@ -180,7 +185,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="alert alert-secondary" role="alert" v-html="$t('powerlimiteradmin.BatterySocInfo')"></div>
|
<div class="alert alert-secondary" role="alert" v-html="$t('powerlimiteradmin.BatterySocInfo')" v-show="!powerLimiterConfigList.ignore_soc"></div>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<label for="inputVoltageStartThreshold" class="col-sm-2 col-form-label">{{ $t('powerlimiteradmin.VoltageStartThreshold') }}:</label>
|
<label for="inputVoltageStartThreshold" class="col-sm-2 col-form-label">{{ $t('powerlimiteradmin.VoltageStartThreshold') }}:</label>
|
||||||
|
|||||||
270
webapp/yarn.lock
270
webapp/yarn.lock
@ -17,10 +17,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.8.tgz#642af7d0333eab9c0ad70b14ac5e76dbde7bfdf8"
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.8.tgz#642af7d0333eab9c0ad70b14ac5e76dbde7bfdf8"
|
||||||
integrity sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA==
|
integrity sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA==
|
||||||
|
|
||||||
"@babel/parser@^7.23.6":
|
"@babel/parser@^7.23.9":
|
||||||
version "7.23.6"
|
version "7.23.9"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.6.tgz#ba1c9e512bda72a47e285ae42aff9d2a635a9e3b"
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.9.tgz#7b903b6149b0f8fa7ad564af646c4c38a77fc44b"
|
||||||
integrity sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==
|
integrity sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==
|
||||||
|
|
||||||
"@esbuild/android-arm64@0.19.5":
|
"@esbuild/android-arm64@0.19.5":
|
||||||
version "0.19.5"
|
version "0.19.5"
|
||||||
@ -211,20 +211,20 @@
|
|||||||
source-map-js "^1.0.1"
|
source-map-js "^1.0.1"
|
||||||
yaml-eslint-parser "^1.2.2"
|
yaml-eslint-parser "^1.2.2"
|
||||||
|
|
||||||
"@intlify/core-base@9.9.0":
|
"@intlify/core-base@9.9.1":
|
||||||
version "9.9.0"
|
version "9.9.1"
|
||||||
resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-9.9.0.tgz#edc55a5e3dbbf8dbbbf656529ed27832c4c4f522"
|
resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-9.9.1.tgz#97ff0a98bf416c3f895e2a4fbcb0da353326b71a"
|
||||||
integrity sha512-C7UXPymDIOlMGSNjAhNLtKgzITc/8BjINK5gNKXg8GiWCTwL6n3MWr55czksxn8RM5wTMz0qcLOFT+adtaVQaA==
|
integrity sha512-qsV15dg7jNX2faBRyKMgZS8UcFJViWEUPLdzZ9UR0kQZpFVeIpc0AG7ZOfeP7pX2T9SQ5jSiorq/tii9nkkafA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@intlify/message-compiler" "9.9.0"
|
"@intlify/message-compiler" "9.9.1"
|
||||||
"@intlify/shared" "9.9.0"
|
"@intlify/shared" "9.9.1"
|
||||||
|
|
||||||
"@intlify/message-compiler@9.9.0":
|
"@intlify/message-compiler@9.9.1":
|
||||||
version "9.9.0"
|
version "9.9.1"
|
||||||
resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-9.9.0.tgz#7952759329e7af0388afbce7a984820bbeff82eb"
|
resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-9.9.1.tgz#4cd9c5a408be27784928e4cd57a77ea6ddb17e56"
|
||||||
integrity sha512-yDU/jdUm9KuhEzYfS+wuyja209yXgdl1XFhMlKtXEgSFTxz4COZQCRXXbbH8JrAjMsaJ7bdoPSLsKlY6mXG2iA==
|
integrity sha512-zTvP6X6HeumHOXuAE1CMMsV6tTX+opKMOxO1OHTCg5N5Sm/F7d8o2jdT6W6L5oHUsJ/vvkGefHIs7Q3hfowmsA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@intlify/shared" "9.9.0"
|
"@intlify/shared" "9.9.1"
|
||||||
source-map-js "^1.0.2"
|
source-map-js "^1.0.2"
|
||||||
|
|
||||||
"@intlify/message-compiler@^9.4.0":
|
"@intlify/message-compiler@^9.4.0":
|
||||||
@ -240,10 +240,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.4.0.tgz#4a78d462fc82433db900981e12eb5b1aae3d6085"
|
resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.4.0.tgz#4a78d462fc82433db900981e12eb5b1aae3d6085"
|
||||||
integrity sha512-AFqymip2kToqA0B6KZPg5jSrdcVHoli9t/VhGKE2iiMq9utFuMoGdDC/JOCIZgwxo6aXAk86QyU2XtzEoMuZ6A==
|
integrity sha512-AFqymip2kToqA0B6KZPg5jSrdcVHoli9t/VhGKE2iiMq9utFuMoGdDC/JOCIZgwxo6aXAk86QyU2XtzEoMuZ6A==
|
||||||
|
|
||||||
"@intlify/shared@9.9.0":
|
"@intlify/shared@9.9.1":
|
||||||
version "9.9.0"
|
version "9.9.1"
|
||||||
resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.9.0.tgz#56907633c0f7b2d50f53269d31e88e7b24d39187"
|
resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.9.1.tgz#b602d012b35f6c336b29a8098296dfac96a005f5"
|
||||||
integrity sha512-1ECUyAHRrzOJbOizyGufYP2yukqGrWXtkmTu4PcswVnWbkcjzk3YQGmJ0bLkM7JZ0ZYAaohLGdYvBYnTOGYJ9g==
|
integrity sha512-b3Pta1nwkz5rGq434v0psHwEwHGy1pYCttfcM22IE//K9owbpkEvFptx9VcuRAxjQdrO2If249cmDDjBu5wMDA==
|
||||||
|
|
||||||
"@intlify/unplugin-vue-i18n@^2.0.0":
|
"@intlify/unplugin-vue-i18n@^2.0.0":
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
@ -435,10 +435,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb"
|
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb"
|
||||||
integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==
|
integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==
|
||||||
|
|
||||||
"@types/node@^20.11.7":
|
"@types/node@^20.11.19":
|
||||||
version "20.11.7"
|
version "20.11.19"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.7.tgz#cb49aedd758c978c30806d0c38b520ed2a3df6e0"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.19.tgz#b466de054e9cb5b3831bee38938de64ac7f81195"
|
||||||
integrity sha512-GPmeN1C3XAyV5uybAf4cMLWT9fDWcmQhZVtMFu7OR32WjrqGG+Wnk2V1d0bmtUyE/Zy1QJ9BxyiTih9z8Oks8A==
|
integrity sha512-7xMnVEcZFu0DikYjWOlRq7NTPETrm7teqUT2WkQjrTIkEgUyyGdWsj/Zg8bEJt5TNklzbPD1X3fqfsHw3SpapQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types "~5.26.4"
|
undici-types "~5.26.4"
|
||||||
|
|
||||||
@ -552,10 +552,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
|
resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
|
||||||
integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
|
integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
|
||||||
|
|
||||||
"@vitejs/plugin-vue@^5.0.3":
|
"@vitejs/plugin-vue@^5.0.4":
|
||||||
version "5.0.3"
|
version "5.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-5.0.3.tgz#164b36653910d27c130cf6c945b4bd9bde5bcbee"
|
resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz#508d6a0f2440f86945835d903fcc0d95d1bb8a37"
|
||||||
integrity sha512-b8S5dVS40rgHdDrw+DQi/xOM9ed+kSRZzfm1T74bMmBDCd8XO87NKlFYInzCtwvtWwXZvo1QxE2OSspTATWrbA==
|
integrity sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==
|
||||||
|
|
||||||
"@volar/language-core@1.11.1", "@volar/language-core@~1.11.1":
|
"@volar/language-core@1.11.1", "@volar/language-core@~1.11.1":
|
||||||
version "1.11.1"
|
version "1.11.1"
|
||||||
@ -599,13 +599,13 @@
|
|||||||
estree-walker "^2.0.2"
|
estree-walker "^2.0.2"
|
||||||
source-map-js "^1.0.2"
|
source-map-js "^1.0.2"
|
||||||
|
|
||||||
"@vue/compiler-core@3.4.15":
|
"@vue/compiler-core@3.4.19":
|
||||||
version "3.4.15"
|
version "3.4.19"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.15.tgz#be20d1bbe19626052500b48969302cb6f396d36e"
|
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.19.tgz#3161b1ede69da00f3ce8155dfab907a3eaa0515e"
|
||||||
integrity sha512-XcJQVOaxTKCnth1vCxEChteGuwG6wqnUHxAm1DO3gCz0+uXKaJNx8/digSz4dLALCy8n2lKq24jSUs8segoqIw==
|
integrity sha512-gj81785z0JNzRcU0Mq98E56e4ltO1yf8k5PQ+tV/7YHnbZkrM0fyFyuttnN8ngJZjbpofWE/m4qjKBiLl8Ju4w==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/parser" "^7.23.6"
|
"@babel/parser" "^7.23.9"
|
||||||
"@vue/shared" "3.4.15"
|
"@vue/shared" "3.4.19"
|
||||||
entities "^4.5.0"
|
entities "^4.5.0"
|
||||||
estree-walker "^2.0.2"
|
estree-walker "^2.0.2"
|
||||||
source-map-js "^1.0.2"
|
source-map-js "^1.0.2"
|
||||||
@ -618,13 +618,13 @@
|
|||||||
"@vue/compiler-core" "3.2.47"
|
"@vue/compiler-core" "3.2.47"
|
||||||
"@vue/shared" "3.2.47"
|
"@vue/shared" "3.2.47"
|
||||||
|
|
||||||
"@vue/compiler-dom@3.4.15":
|
"@vue/compiler-dom@3.4.19":
|
||||||
version "3.4.15"
|
version "3.4.19"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.15.tgz#753f5ed55f78d33dff04701fad4d76ff0cf81ee5"
|
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.19.tgz#2457e57e978f431e3b5fd11fc50a3e92d5816f9a"
|
||||||
integrity sha512-wox0aasVV74zoXyblarOM3AZQz/Z+OunYcIHe1OsGclCHt8RsRm04DObjefaI82u6XDzv+qGWZ24tIsRAIi5MQ==
|
integrity sha512-vm6+cogWrshjqEHTzIDCp72DKtea8Ry/QVpQRYoyTIg9k7QZDX6D8+HGURjtmatfgM8xgCFtJJaOlCaRYRK3QA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/compiler-core" "3.4.15"
|
"@vue/compiler-core" "3.4.19"
|
||||||
"@vue/shared" "3.4.15"
|
"@vue/shared" "3.4.19"
|
||||||
|
|
||||||
"@vue/compiler-dom@^3.3.0":
|
"@vue/compiler-dom@^3.3.0":
|
||||||
version "3.3.2"
|
version "3.3.2"
|
||||||
@ -634,18 +634,18 @@
|
|||||||
"@vue/compiler-core" "3.3.2"
|
"@vue/compiler-core" "3.3.2"
|
||||||
"@vue/shared" "3.3.2"
|
"@vue/shared" "3.3.2"
|
||||||
|
|
||||||
"@vue/compiler-sfc@3.4.15":
|
"@vue/compiler-sfc@3.4.19":
|
||||||
version "3.4.15"
|
version "3.4.19"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.4.15.tgz#4e5811e681955fcec886cebbec483f6ae463a64b"
|
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.4.19.tgz#33b238ded6d63e51f6a7048b742626f6007df129"
|
||||||
integrity sha512-LCn5M6QpkpFsh3GQvs2mJUOAlBQcCco8D60Bcqmf3O3w5a+KWS5GvYbrrJBkgvL1BDnTp+e8q0lXCLgHhKguBA==
|
integrity sha512-LQ3U4SN0DlvV0xhr1lUsgLCYlwQfUfetyPxkKYu7dkfvx7g3ojrGAkw0AERLOKYXuAGnqFsEuytkdcComei3Yg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/parser" "^7.23.6"
|
"@babel/parser" "^7.23.9"
|
||||||
"@vue/compiler-core" "3.4.15"
|
"@vue/compiler-core" "3.4.19"
|
||||||
"@vue/compiler-dom" "3.4.15"
|
"@vue/compiler-dom" "3.4.19"
|
||||||
"@vue/compiler-ssr" "3.4.15"
|
"@vue/compiler-ssr" "3.4.19"
|
||||||
"@vue/shared" "3.4.15"
|
"@vue/shared" "3.4.19"
|
||||||
estree-walker "^2.0.2"
|
estree-walker "^2.0.2"
|
||||||
magic-string "^0.30.5"
|
magic-string "^0.30.6"
|
||||||
postcss "^8.4.33"
|
postcss "^8.4.33"
|
||||||
source-map-js "^1.0.2"
|
source-map-js "^1.0.2"
|
||||||
|
|
||||||
@ -673,13 +673,13 @@
|
|||||||
"@vue/compiler-dom" "3.2.47"
|
"@vue/compiler-dom" "3.2.47"
|
||||||
"@vue/shared" "3.2.47"
|
"@vue/shared" "3.2.47"
|
||||||
|
|
||||||
"@vue/compiler-ssr@3.4.15":
|
"@vue/compiler-ssr@3.4.19":
|
||||||
version "3.4.15"
|
version "3.4.19"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.4.15.tgz#a910a5b89ba4f0a776e40b63d69bdae2f50616cf"
|
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.4.19.tgz#1f8ee06005ebbaa354f8783fad84e9f7ea4a69c2"
|
||||||
integrity sha512-1jdeQyiGznr8gjFDadVmOJqZiLNSsMa5ZgqavkPZ8O2wjHv0tVuAEsw5hTdUoUW4232vpBbL/wJhzVW/JwY1Uw==
|
integrity sha512-P0PLKC4+u4OMJ8sinba/5Z/iDT84uMRRlrWzadgLA69opCpI1gG4N55qDSC+dedwq2fJtzmGald05LWR5TFfLw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/compiler-dom" "3.4.15"
|
"@vue/compiler-dom" "3.4.19"
|
||||||
"@vue/shared" "3.4.15"
|
"@vue/shared" "3.4.19"
|
||||||
|
|
||||||
"@vue/devtools-api@^6.5.0":
|
"@vue/devtools-api@^6.5.0":
|
||||||
version "6.5.0"
|
version "6.5.0"
|
||||||
@ -721,37 +721,37 @@
|
|||||||
estree-walker "^2.0.2"
|
estree-walker "^2.0.2"
|
||||||
magic-string "^0.25.7"
|
magic-string "^0.25.7"
|
||||||
|
|
||||||
"@vue/reactivity@3.4.15":
|
"@vue/reactivity@3.4.19":
|
||||||
version "3.4.15"
|
version "3.4.19"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.4.15.tgz#ad9d9b83f5398d2e8660ad5cfc0f171e7679a9a1"
|
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.4.19.tgz#8cf335d97d07881d8184cb23289289dc18b03f60"
|
||||||
integrity sha512-55yJh2bsff20K5O84MxSvXKPHHt17I2EomHznvFiJCAZpJTNW8IuLj1xZWMLELRhBK3kkFV/1ErZGHJfah7i7w==
|
integrity sha512-+VcwrQvLZgEclGZRHx4O2XhyEEcKaBi50WbxdVItEezUf4fqRh838Ix6amWTdX0CNb/b6t3Gkz3eOebfcSt+UA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/shared" "3.4.15"
|
"@vue/shared" "3.4.19"
|
||||||
|
|
||||||
"@vue/runtime-core@3.4.15":
|
"@vue/runtime-core@3.4.19":
|
||||||
version "3.4.15"
|
version "3.4.19"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.4.15.tgz#f81e2fd2108ea41a6d5c61c2462b11dfb754fdf0"
|
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.4.19.tgz#ef10357fdf3afdf68523b55424541000105e2aeb"
|
||||||
integrity sha512-6E3by5m6v1AkW0McCeAyhHTw+3y17YCOKG0U0HDKDscV4Hs0kgNT5G+GCHak16jKgcCDHpI9xe5NKb8sdLCLdw==
|
integrity sha512-/Z3tFwOrerJB/oyutmJGoYbuoadphDcJAd5jOuJE86THNZji9pYjZroQ2NFsZkTxOq0GJbb+s2kxTYToDiyZzw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/reactivity" "3.4.15"
|
"@vue/reactivity" "3.4.19"
|
||||||
"@vue/shared" "3.4.15"
|
"@vue/shared" "3.4.19"
|
||||||
|
|
||||||
"@vue/runtime-dom@3.4.15":
|
"@vue/runtime-dom@3.4.19":
|
||||||
version "3.4.15"
|
version "3.4.19"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.4.15.tgz#108ef86aa7334ead5d6b9c56a7d93679e1e45406"
|
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.4.19.tgz#079141e31d9f47515b9595f29843d51011f88739"
|
||||||
integrity sha512-EVW8D6vfFVq3V/yDKNPBFkZKGMFSvZrUQmx196o/v2tHKdwWdiZjYUBS+0Ez3+ohRyF8Njwy/6FH5gYJ75liUw==
|
integrity sha512-IyZzIDqfNCF0OyZOauL+F4yzjMPN2rPd8nhqPP2N1lBn3kYqJpPHHru+83Rkvo2lHz5mW+rEeIMEF9qY3PB94g==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/runtime-core" "3.4.15"
|
"@vue/runtime-core" "3.4.19"
|
||||||
"@vue/shared" "3.4.15"
|
"@vue/shared" "3.4.19"
|
||||||
csstype "^3.1.3"
|
csstype "^3.1.3"
|
||||||
|
|
||||||
"@vue/server-renderer@3.4.15":
|
"@vue/server-renderer@3.4.19":
|
||||||
version "3.4.15"
|
version "3.4.19"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.4.15.tgz#34438f998e6f6370fac78883a75efe136631957f"
|
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.4.19.tgz#e6f8ff5268d0758766ca9835375218924d5f0eb6"
|
||||||
integrity sha512-3HYzaidu9cHjrT+qGUuDhFYvF/j643bHC6uUN9BgM11DVy+pM6ATsG6uPBLnkwOgs7BpJABReLmpL3ZPAsUaqw==
|
integrity sha512-eAj2p0c429RZyyhtMRnttjcSToch+kTWxFPHlzGMkR28ZbF1PDlTcmGmlDxccBuqNd9iOQ7xPRPAGgPVj+YpQw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/compiler-ssr" "3.4.15"
|
"@vue/compiler-ssr" "3.4.19"
|
||||||
"@vue/shared" "3.4.15"
|
"@vue/shared" "3.4.19"
|
||||||
|
|
||||||
"@vue/shared@3.2.47":
|
"@vue/shared@3.2.47":
|
||||||
version "3.2.47"
|
version "3.2.47"
|
||||||
@ -763,10 +763,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.3.2.tgz#774cd9b4635ce801b70a3fc3713779a5ef5d77c3"
|
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.3.2.tgz#774cd9b4635ce801b70a3fc3713779a5ef5d77c3"
|
||||||
integrity sha512-0rFu3h8JbclbnvvKrs7Fe5FNGV9/5X2rPD7KmOzhLSUAiQH5//Hq437Gv0fR5Mev3u/nbtvmLl8XgwCU20/ZfQ==
|
integrity sha512-0rFu3h8JbclbnvvKrs7Fe5FNGV9/5X2rPD7KmOzhLSUAiQH5//Hq437Gv0fR5Mev3u/nbtvmLl8XgwCU20/ZfQ==
|
||||||
|
|
||||||
"@vue/shared@3.4.15":
|
"@vue/shared@3.4.19":
|
||||||
version "3.4.15"
|
version "3.4.19"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.15.tgz#e7d2ea050c667480cb5e1a6df2ac13bcd03a8f30"
|
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.19.tgz#28105147811bcf1e6612bf1c9ab0c6d91ada019c"
|
||||||
integrity sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g==
|
integrity sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==
|
||||||
|
|
||||||
"@vue/tsconfig@^0.5.1":
|
"@vue/tsconfig@^0.5.1":
|
||||||
version "0.5.1"
|
version "0.5.1"
|
||||||
@ -1146,17 +1146,17 @@ escodegen@^2.0.0:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
source-map "~0.6.1"
|
source-map "~0.6.1"
|
||||||
|
|
||||||
eslint-plugin-vue@^9.20.1:
|
eslint-plugin-vue@^9.21.1:
|
||||||
version "9.20.1"
|
version "9.21.1"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.20.1.tgz#7ed78846898574b2cd26939f28b0b87798a7b528"
|
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.21.1.tgz#da5629efa48527cec98278dca0daa90fada4caf7"
|
||||||
integrity sha512-GyCs8K3lkEvoyC1VV97GJhP1SvqsKCiWGHnbn0gVUYiUhaH2+nB+Dv1uekv1THFMPbBfYxukrzQdltw950k+LQ==
|
integrity sha512-XVtI7z39yOVBFJyi8Ljbn7kY9yHzznKXL02qQYn+ta63Iy4A9JFBw6o4OSB9hyD2++tVT+su9kQqetUyCCwhjw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint-community/eslint-utils" "^4.4.0"
|
"@eslint-community/eslint-utils" "^4.4.0"
|
||||||
natural-compare "^1.4.0"
|
natural-compare "^1.4.0"
|
||||||
nth-check "^2.1.1"
|
nth-check "^2.1.1"
|
||||||
postcss-selector-parser "^6.0.13"
|
postcss-selector-parser "^6.0.13"
|
||||||
semver "^7.5.4"
|
semver "^7.5.4"
|
||||||
vue-eslint-parser "^9.4.0"
|
vue-eslint-parser "^9.4.2"
|
||||||
xml-name-validator "^4.0.0"
|
xml-name-validator "^4.0.0"
|
||||||
|
|
||||||
eslint-scope@^7.1.1:
|
eslint-scope@^7.1.1:
|
||||||
@ -1834,10 +1834,10 @@ magic-string@^0.30.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@jridgewell/sourcemap-codec" "^1.4.13"
|
"@jridgewell/sourcemap-codec" "^1.4.13"
|
||||||
|
|
||||||
magic-string@^0.30.5:
|
magic-string@^0.30.6:
|
||||||
version "0.30.5"
|
version "0.30.7"
|
||||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.5.tgz#1994d980bd1c8835dc6e78db7cbd4ae4f24746f9"
|
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.7.tgz#0cecd0527d473298679da95a2d7aeb8c64048505"
|
||||||
integrity sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==
|
integrity sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@jridgewell/sourcemap-codec" "^1.4.15"
|
"@jridgewell/sourcemap-codec" "^1.4.15"
|
||||||
|
|
||||||
@ -2128,19 +2128,19 @@ postcss@^8.1.10:
|
|||||||
picocolors "^1.0.0"
|
picocolors "^1.0.0"
|
||||||
source-map-js "^1.0.2"
|
source-map-js "^1.0.2"
|
||||||
|
|
||||||
postcss@^8.4.32:
|
postcss@^8.4.33:
|
||||||
version "8.4.32"
|
version "8.4.33"
|
||||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.32.tgz#1dac6ac51ab19adb21b8b34fd2d93a86440ef6c9"
|
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.33.tgz#1378e859c9f69bf6f638b990a0212f43e2aaa742"
|
||||||
integrity sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==
|
integrity sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==
|
||||||
dependencies:
|
dependencies:
|
||||||
nanoid "^3.3.7"
|
nanoid "^3.3.7"
|
||||||
picocolors "^1.0.0"
|
picocolors "^1.0.0"
|
||||||
source-map-js "^1.0.2"
|
source-map-js "^1.0.2"
|
||||||
|
|
||||||
postcss@^8.4.33:
|
postcss@^8.4.35:
|
||||||
version "8.4.33"
|
version "8.4.35"
|
||||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.33.tgz#1378e859c9f69bf6f638b990a0212f43e2aaa742"
|
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.35.tgz#60997775689ce09011edf083a549cea44aabe2f7"
|
||||||
integrity sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==
|
integrity sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==
|
||||||
dependencies:
|
dependencies:
|
||||||
nanoid "^3.3.7"
|
nanoid "^3.3.7"
|
||||||
picocolors "^1.0.0"
|
picocolors "^1.0.0"
|
||||||
@ -2257,10 +2257,10 @@ safe-regex-test@^1.0.0:
|
|||||||
get-intrinsic "^1.1.3"
|
get-intrinsic "^1.1.3"
|
||||||
is-regex "^1.1.4"
|
is-regex "^1.1.4"
|
||||||
|
|
||||||
sass@^1.70.0:
|
sass@^1.71.0:
|
||||||
version "1.70.0"
|
version "1.71.0"
|
||||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.70.0.tgz#761197419d97b5358cb25f9dd38c176a8a270a75"
|
resolved "https://registry.yarnpkg.com/sass/-/sass-1.71.0.tgz#b3085759b9b2ab503a977aecb7e91153bf941117"
|
||||||
integrity sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==
|
integrity sha512-HKKIKf49Vkxlrav3F/w6qRuPcmImGVbIXJ2I3Kg0VMA+3Bav+8yE9G5XmP5lMj6nl4OlqbPftGAscNaNu28b8w==
|
||||||
dependencies:
|
dependencies:
|
||||||
chokidar ">=3.0.0 <4.0.0"
|
chokidar ">=3.0.0 <4.0.0"
|
||||||
immutable "^4.0.0"
|
immutable "^4.0.0"
|
||||||
@ -2450,10 +2450,10 @@ supports-preserve-symlinks-flag@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
||||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||||
|
|
||||||
terser@^5.27.0:
|
terser@^5.27.1:
|
||||||
version "5.27.0"
|
version "5.27.1"
|
||||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.27.0.tgz#70108689d9ab25fef61c4e93e808e9fd092bf20c"
|
resolved "https://registry.yarnpkg.com/terser/-/terser-5.27.1.tgz#b0092975ea1b379d166088a1a57e32f0839d84a2"
|
||||||
integrity sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==
|
integrity sha512-29wAr6UU/oQpnTw5HoadwjUZnFQXGdOfj0LjZ4sVxzqwHh/QVkvr7m8y9WoR4iN3FRitVduTc6KdjcW38Npsug==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@jridgewell/source-map" "^0.3.3"
|
"@jridgewell/source-map" "^0.3.3"
|
||||||
acorn "^8.8.2"
|
acorn "^8.8.2"
|
||||||
@ -2565,18 +2565,18 @@ vite-plugin-compression@^0.5.1:
|
|||||||
debug "^4.3.3"
|
debug "^4.3.3"
|
||||||
fs-extra "^10.0.0"
|
fs-extra "^10.0.0"
|
||||||
|
|
||||||
vite-plugin-css-injected-by-js@^3.3.1:
|
vite-plugin-css-injected-by-js@^3.4.0:
|
||||||
version "3.3.1"
|
version "3.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-3.3.1.tgz#26b41f108c5554ee728359bdec01c68c93a48547"
|
resolved "https://registry.yarnpkg.com/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-3.4.0.tgz#b09a571ab50744623736a4b056ecc85d7516311a"
|
||||||
integrity sha512-PjM/X45DR3/V1K1fTRs8HtZHEQ55kIfdrn+dzaqNBFrOYO073SeSNCxp4j7gSYhV9NffVHaEnOL4myoko0ePAg==
|
integrity sha512-wS5+UYtJXQ/vNornsqTQxOLBVO/UjXU54ZsYMeX0mj2OrbStMQ4GLgvneVDQGPwyGJcm/ntBPawc2lA7xx+Lpg==
|
||||||
|
|
||||||
vite@^5.0.12:
|
vite@^5.1.3:
|
||||||
version "5.0.12"
|
version "5.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/vite/-/vite-5.0.12.tgz#8a2ffd4da36c132aec4adafe05d7adde38333c47"
|
resolved "https://registry.yarnpkg.com/vite/-/vite-5.1.3.tgz#dd072653a80225702265550a4700561740dfde55"
|
||||||
integrity sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==
|
integrity sha512-UfmUD36DKkqhi/F75RrxvPpry+9+tTkrXfMNZD+SboZqBCMsxKtO52XeGzzuh7ioz+Eo/SYDBbdb0Z7vgcDJew==
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild "^0.19.3"
|
esbuild "^0.19.3"
|
||||||
postcss "^8.4.32"
|
postcss "^8.4.35"
|
||||||
rollup "^4.2.0"
|
rollup "^4.2.0"
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "~2.3.3"
|
fsevents "~2.3.3"
|
||||||
@ -2594,10 +2594,10 @@ vue-eslint-parser@^9.3.1:
|
|||||||
lodash "^4.17.21"
|
lodash "^4.17.21"
|
||||||
semver "^7.3.6"
|
semver "^7.3.6"
|
||||||
|
|
||||||
vue-eslint-parser@^9.4.0:
|
vue-eslint-parser@^9.4.2:
|
||||||
version "9.4.0"
|
version "9.4.2"
|
||||||
resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-9.4.0.tgz#dfd22302e2992fe45748a76553cef7afa5bdde27"
|
resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-9.4.2.tgz#02ffcce82042b082292f2d1672514615f0d95b6d"
|
||||||
integrity sha512-7KsNBb6gHFA75BtneJsoK/dbZ281whUIwFYdQxA68QrCrGMXYzUMbPDHGcOQ0OocIVKrWSKWXZ4mL7tonCXoUw==
|
integrity sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
debug "^4.3.4"
|
debug "^4.3.4"
|
||||||
eslint-scope "^7.1.1"
|
eslint-scope "^7.1.1"
|
||||||
@ -2607,13 +2607,13 @@ vue-eslint-parser@^9.4.0:
|
|||||||
lodash "^4.17.21"
|
lodash "^4.17.21"
|
||||||
semver "^7.3.6"
|
semver "^7.3.6"
|
||||||
|
|
||||||
vue-i18n@^9.9.0:
|
vue-i18n@^9.9.1:
|
||||||
version "9.9.0"
|
version "9.9.1"
|
||||||
resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-9.9.0.tgz#20d348fa7e37fc88e4c84f69781b2f1215c7769f"
|
resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-9.9.1.tgz#3c2fdf3c9db430572a1246439d541d01e2795c06"
|
||||||
integrity sha512-xQ5SxszUAqK5n84N+uUyHH/PiQl9xZ24FOxyAaNonmOQgXeN+rD9z/6DStOpOxNFQn4Cgcquot05gZc+CdOujA==
|
integrity sha512-xyQ4VspLdNSPTKBFBPWa1tvtj+9HuockZwgFeD2OhxxXuC2CWeNvV4seu2o9+vbQOyQbhAM5Ez56oxUrrnTWdw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@intlify/core-base" "9.9.0"
|
"@intlify/core-base" "9.9.1"
|
||||||
"@intlify/shared" "9.9.0"
|
"@intlify/shared" "9.9.1"
|
||||||
"@vue/devtools-api" "^6.5.0"
|
"@vue/devtools-api" "^6.5.0"
|
||||||
|
|
||||||
vue-router@^4.2.5:
|
vue-router@^4.2.5:
|
||||||
@ -2640,16 +2640,16 @@ vue-tsc@^1.8.27:
|
|||||||
"@vue/language-core" "1.8.27"
|
"@vue/language-core" "1.8.27"
|
||||||
semver "^7.5.4"
|
semver "^7.5.4"
|
||||||
|
|
||||||
vue@^3.4.15:
|
vue@^3.4.19:
|
||||||
version "3.4.15"
|
version "3.4.19"
|
||||||
resolved "https://registry.yarnpkg.com/vue/-/vue-3.4.15.tgz#91f979844ffca9239dff622ba4c79c5d5524b88c"
|
resolved "https://registry.yarnpkg.com/vue/-/vue-3.4.19.tgz#f9ae0a44db86628548736ff04152830726a97263"
|
||||||
integrity sha512-jC0GH4KkWLWJOEQjOpkqU1bQsBwf4R1rsFtw5GQJbjHVKWDzO6P0nWWBTmjp1xSemAioDFj1jdaK1qa3DnMQoQ==
|
integrity sha512-W/7Fc9KUkajFU8dBeDluM4sRGc/aa4YJnOYck8dkjgZoXtVsn3OeTGni66FV1l3+nvPA7VBFYtPioaGKUmEADw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/compiler-dom" "3.4.15"
|
"@vue/compiler-dom" "3.4.19"
|
||||||
"@vue/compiler-sfc" "3.4.15"
|
"@vue/compiler-sfc" "3.4.19"
|
||||||
"@vue/runtime-dom" "3.4.15"
|
"@vue/runtime-dom" "3.4.19"
|
||||||
"@vue/server-renderer" "3.4.15"
|
"@vue/server-renderer" "3.4.19"
|
||||||
"@vue/shared" "3.4.15"
|
"@vue/shared" "3.4.19"
|
||||||
|
|
||||||
webpack-sources@^3.2.3:
|
webpack-sources@^3.2.3:
|
||||||
version "3.2.3"
|
version "3.2.3"
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user