Browse Source

0.8.1030013 Merge pull request #1615 from tictrick/zero-export

0.8.1030013
pull/1616/head^2
tictrick 5 months ago
committed by GitHub
parent
commit
2c9f3a2b36
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 72
      src/app.cpp
  2. 7
      src/app.h
  3. 195
      src/config/settings.h
  4. 2
      src/defines.h
  5. 451
      src/plugins/zeroExport/powermeter.h
  6. 2051
      src/plugins/zeroExport/zeroExport.h
  7. 34
      src/publisher/pubMqtt.h
  8. 29
      src/web/RestApi.h
  9. 167
      src/web/html/setup.html
  10. 54
      src/web/lang.json

72
src/app.cpp

@ -115,6 +115,7 @@ void app::setup() {
mMqttEnabled = (mConfig->mqtt.broker[0] > 0); mMqttEnabled = (mConfig->mqtt.broker[0] > 0);
if (mMqttEnabled) { if (mMqttEnabled) {
mMqtt.setup(this, &mConfig->mqtt, mConfig->sys.deviceName, mVersion, &mSys, &mTimestamp, &mUptime); mMqtt.setup(this, &mConfig->mqtt, mConfig->sys.deviceName, mVersion, &mSys, &mTimestamp, &mUptime);
mMqtt.setConnectionCb(std::bind(&app::mqttConnectCb, this));
mMqtt.setSubscriptionCb(std::bind(&app::mqttSubRxCb, this, std::placeholders::_1)); mMqtt.setSubscriptionCb(std::bind(&app::mqttSubRxCb, this, std::placeholders::_1));
mCommunication.addAlarmListener([this](Inverter<> *iv) { mMqtt.alarmEvent(iv); }); mCommunication.addAlarmListener([this](Inverter<> *iv) { mMqtt.alarmEvent(iv); });
} }
@ -145,7 +146,7 @@ void app::setup() {
// Plugin ZeroExport // Plugin ZeroExport
#if defined(PLUGIN_ZEROEXPORT) #if defined(PLUGIN_ZEROEXPORT)
mZeroExport.setup(&mConfig->plugin.zeroExport, &mSys, mConfig, &mApi, &mMqtt); mZeroExport.setup(this, &mTimestamp, &mConfig->plugin.zeroExport, &mSys, mConfig, &mApi, &mMqtt);
#endif /*PLUGIN_ZEROEXPORT*/ #endif /*PLUGIN_ZEROEXPORT*/
// Plugin ZeroExport - Ende // Plugin ZeroExport - Ende
@ -403,10 +404,6 @@ void app::tickMinute(void) {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void app::tickMidnight(void) { void app::tickMidnight(void) {
#if defined(PLUGIN_ZEROEXPORT)
mZeroExport.tickMidnight();
#endif /*defined(PLUGIN_ZEROEXPORT)*/
uint32_t localTime = gTimezone.toLocal(mTimestamp); uint32_t localTime = gTimezone.toLocal(mTimestamp);
uint32_t nxtTrig = gTimezone.toUTC(localTime - (localTime % 86400) + 86400); // next midnight local time uint32_t nxtTrig = gTimezone.toUTC(localTime - (localTime % 86400) + 86400); // next midnight local time
onceAt(std::bind(&app::tickMidnight, this), nxtTrig, "mid2"); onceAt(std::bind(&app::tickMidnight, this), nxtTrig, "mid2");
@ -439,6 +436,10 @@ void app::tickMidnight(void) {
mMqtt.tickerMidnight(); mMqtt.tickerMidnight();
#endif #endif
} }
#if defined(PLUGIN_ZEROEXPORT)
mZeroExport.tickMidnight();
#endif /*defined(PLUGIN_ZEROEXPORT)*/
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -619,6 +620,13 @@ void app::resetSystem(void) {
mNtpReceived = false; mNtpReceived = false;
} }
//-----------------------------------------------------------------------------
void app::mqttConnectCb(void) {
#if defined(PLUGIN_ZEROEXPORT)
mZeroExport.onMqttConnect();
#endif
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void app::mqttSubRxCb(JsonObject obj) { void app::mqttSubRxCb(JsonObject obj) {
mApi.ctrlRequest(obj); mApi.ctrlRequest(obj);
@ -673,57 +681,3 @@ void app::updateLed(void) {
analogWrite(mConfig->led.led[2], led_off); analogWrite(mConfig->led.led[2], led_off);
} }
} }
//-----------------------------------------------------------------------------
// Plugin ZeroExport
#if defined(PLUGIN_ZEROEXPORT)
void app::zeroexport() {
return;
// TODO: aufr�umen
// TODO: umziehen nach loop
/*
if (!mConfig->plugin.zeroExport.enabled ||
!mSys.getInverterByPos(mConfig->plugin.zeroExport.Iv)->isProducing()) { // check if plugin is enabled && indicate to send new value
mConfig->plugin.zeroExport.lastTime = millis(); // set last timestamp
return;
}
if (millis() - mConfig->plugin.zeroExport.lastTime > mConfig->plugin.zeroExport.count_avg * 1000UL)
{
Inverter<> *iv = mSys.getInverterByPos(mConfig->plugin.zeroExport.Iv);
DynamicJsonDocument doc(512);
JsonObject object = doc.to<JsonObject>();
double nValue = round(mZeroExport.getPowertoSetnewValue());
double twoPerVal = nValue <= (iv->getMaxPower() / 100 * 2 );
if(mConfig->plugin.zeroExport.two_percent && (nValue <= twoPerVal))
nValue = twoPerVal;
if(mConfig->plugin.zeroExport.max_power <= nValue)
nValue = mConfig->plugin.zeroExport.max_power;
if(iv->actPowerLimit == nValue) {
mConfig->plugin.zeroExport.lastTime = millis(); // set last timestamp
return; // if PowerLimit same as befor, then skip
}
object["val"] = nValue;
object["id"] = mConfig->plugin.zeroExport.Iv;
object["path"] = "ctrl";
object["cmd"] = "limit_nonpersistent_absolute";
String data;
serializeJsonPretty(object, data);
DPRINTLN(DBG_INFO, data);
mApi.ctrlRequest(object);
mConfig->plugin.zeroExport.lastTime = millis(); // set last timestamp
}
*/
}
#endif
// Plugin ZeroExport - Ende

7
src/app.h

@ -370,17 +370,12 @@ class app : public IApp, public ah::Scheduler {
updateLed(); updateLed();
} }
void mqttConnectCb(void);
void mqttSubRxCb(JsonObject obj); void mqttSubRxCb(JsonObject obj);
void setupLed(); void setupLed();
void updateLed(); void updateLed();
// Plugin ZeroExport
#if defined(PLUGIN_ZEROEXPORT)
void zeroexport();
#endif
// Plugin ZeroExport - Ende
void tickReboot(void) { void tickReboot(void) {
DPRINTLN(DBG_INFO, F("Rebooting...")); DPRINTLN(DBG_INFO, F("Rebooting..."));
ah::Scheduler::resetTicker(); ah::Scheduler::resetTicker();

195
src/config/settings.h

@ -203,17 +203,17 @@ typedef struct {
// Plugin ZeroExport // Plugin ZeroExport
#if defined(PLUGIN_ZEROEXPORT) #if defined(PLUGIN_ZEROEXPORT)
#define ZEROEXPORT_DEV_POWERMETER #define ZEROEXPORT_MAX_QUEUE_ENTRIES 64
#define ZEROEXPORT_MAX_GROUPS 6 #define ZEROEXPORT_MAX_GROUPS 8
#define ZEROEXPORT_GROUP_MAX_LEN_NAME 25 #define ZEROEXPORT_GROUP_MAX_LEN_NAME 25
#define ZEROEXPORT_GROUP_MAX_LEN_PM_URL 100 #define ZEROEXPORT_GROUP_MAX_LEN_PM_URL 100
#define ZEROEXPORT_GROUP_MAX_LEN_PM_JSONPATH 100 #define ZEROEXPORT_GROUP_MAX_LEN_PM_JSONPATH 100
#define ZEROEXPORT_GROUP_MAX_LEN_PM_USER 25 #define ZEROEXPORT_GROUP_MAX_LEN_PM_USER 25
#define ZEROEXPORT_GROUP_MAX_LEN_PM_PASS 25 #define ZEROEXPORT_GROUP_MAX_LEN_PM_PASS 25
#define ZEROEXPORT_GROUP_MAX_LEN_BATT_TOPIC 100
#define ZEROEXPORT_GROUP_MAX_INVERTERS 3 #define ZEROEXPORT_GROUP_MAX_INVERTERS 3
#define ZEROEXPORT_POWERMETER_MAX_ERRORS 5 #define ZEROEXPORT_POWERMETER_MAX_ERRORS 5
#define ZEROEXPORT_DEF_INV_WAITINGTIME_MS 10000 #define ZEROEXPORT_GROUP_WR_LIMIT_MIN_DIFF 2
#define ZEROEXPORT_GROUP_WR_LIMIT_MIN_DIFF 5
#define ZEROEXPORT_POWERMETER_SHELLY #define ZEROEXPORT_POWERMETER_SHELLY
//#define ZEROEXPORT_POWERMETER_TASMOTA //#define ZEROEXPORT_POWERMETER_TASMOTA
#define ZEROEXPORT_POWERMETER_MQTT #define ZEROEXPORT_POWERMETER_MQTT
@ -221,23 +221,6 @@ typedef struct {
#define ZEROEXPORT_POWERMETER_TIBBER #define ZEROEXPORT_POWERMETER_TIBBER
#define ZEROEXPORT_POWERMETER_SHRDZM #define ZEROEXPORT_POWERMETER_SHRDZM
enum class zeroExportState : uint8_t {
INIT,
WAITREFRESH,
GETINVERTERDATA,
BATTERYPROTECTION,
GETPOWERMETER,
CONTROLLER,
PROGNOSE,
AUFTEILEN,
SETREBOOT,
SETPOWER,
SETLIMIT,
EMERGENCY,
FINISH,
ERROR
};
typedef enum { typedef enum {
None = 0, None = 0,
Shelly = 1, Shelly = 1,
@ -253,31 +236,46 @@ typedef enum {
L1 = 1, L1 = 1,
L2 = 2, L2 = 2,
L3 = 3, L3 = 3,
L1Sum = 4, } zeroExportPowermeterTarget;
L2Sum = 5,
L3Sum = 6, typedef enum {
} zeroExportInverterTarget_t; none = 0,
invUdc,
mqttU,
mqttSoC
} zeroExportBatteryCfg;
typedef enum {
doNone = 0,
doRestart,
doTurnOn,
doTurnOff,
doActivePowerContr,
} zeroExportAction_t;
typedef struct {
uint8_t group;
uint8_t inv;
uint8_t id;
} zeroExportQueue_t;
typedef struct { typedef struct {
bool enabled; bool enabled;
int8_t id; int8_t id;
int8_t target;
uint16_t powerMin; uint16_t powerMin;
uint16_t powerMax; uint16_t powerMax;
bool turnOff;
// //
int32_t power; zeroExportAction_t action;
int8_t actionTimer;
unsigned long actionTimestamp;
uint16_t power;
uint16_t MaxPower;
int32_t limit; int32_t limit;
int32_t limitNew; int32_t limitNew;
uint8_t waitAckSetLimit; uint8_t waitAck;
uint8_t waitAckSetPower;
uint8_t waitAckSetReboot;
unsigned long limitTsp;
bool state;
// //
int8_t doReboot;
int8_t doPower;
int8_t doLimit;
float dcVoltage; float dcVoltage;
} zeroExportGroupInverter_t; } zeroExportGroupInverter_t;
@ -287,57 +285,43 @@ typedef struct {
bool sleep; bool sleep;
char name[ZEROEXPORT_GROUP_MAX_LEN_NAME]; char name[ZEROEXPORT_GROUP_MAX_LEN_NAME];
// Powermeter // Powermeter
uint8_t pm_refresh;
unsigned long pm_peviousTsp;
uint8_t pm_type; uint8_t pm_type;
char pm_url[ZEROEXPORT_GROUP_MAX_LEN_PM_URL]; char pm_url[ZEROEXPORT_GROUP_MAX_LEN_PM_URL];
char pm_jsonPath[ZEROEXPORT_GROUP_MAX_LEN_PM_JSONPATH]; char pm_jsonPath[ZEROEXPORT_GROUP_MAX_LEN_PM_JSONPATH];
char pm_user[ZEROEXPORT_GROUP_MAX_LEN_PM_USER]; char pm_user[ZEROEXPORT_GROUP_MAX_LEN_PM_USER];
char pm_pass[ZEROEXPORT_GROUP_MAX_LEN_PM_PASS]; char pm_pass[ZEROEXPORT_GROUP_MAX_LEN_PM_PASS];
uint8_t pm_target;
// Inverters // Inverters
zeroExportGroupInverter_t inverters[ZEROEXPORT_GROUP_MAX_INVERTERS]; zeroExportGroupInverter_t inverters[ZEROEXPORT_GROUP_MAX_INVERTERS];
// Battery // Battery
bool battEnabled; uint8_t battCfg;
float battVoltageOn; char battTopic[ZEROEXPORT_GROUP_MAX_LEN_BATT_TOPIC];
float battVoltageOff; float battValue;
bool isChangedBattery; float battLimitOn;
float battLimitOff;
// Advanced // Advanced
int16_t setPoint; int16_t setPoint;
uint8_t refresh; bool minimum;
int32_t power; float power;
uint8_t powerTolerance; uint8_t powerTolerance;
uint16_t powerMax; uint16_t powerMax;
bool isChangedAdvanced;
// //
zeroExportState state;
// zeroExportState stateNext;
unsigned long lastRun; unsigned long lastRun;
unsigned long lastRefresh; unsigned long lastRefresh;
uint16_t wait; uint16_t wait;
int32_t pm_P;
int32_t pm_P1;
int32_t pm_P2;
int32_t pm_P3;
bool publishPower = false;
bool battSwitch; bool battSwitch;
// PID controller // PID controller
int32_t eSum; float eSum;
int32_t eSum1; float eOld;
int32_t eSum2;
int32_t eSum3;
int32_t eOld;
int32_t eOld1;
int32_t eOld2;
int32_t eOld3;
float Kp; float Kp;
float Ki; float Ki;
float Kd; float Kd;
int32_t y; float y;
int32_t y1;
int32_t y2;
int32_t y3;
} zeroExportGroup_t; } zeroExportGroup_t;
typedef struct { typedef struct {
@ -347,18 +331,6 @@ typedef struct {
bool log_over_mqtt; bool log_over_mqtt;
bool debug; bool debug;
zeroExportGroup_t groups[ZEROEXPORT_MAX_GROUPS]; zeroExportGroup_t groups[ZEROEXPORT_MAX_GROUPS];
// uint8_t query_device; // 0 - Tibber, 1 - Shelly, 2 - other (rs232?)
// char monitor_url[ZEXPORT_ADDR_LEN];
// char json_path[ZEXPORT_ADDR_LEN];
// char tibber_pw[10]; // needed for tibber QWGH-ED12
// uint8_t Iv; // saves the inverter that is used for regulation
// float power_avg;
// uint8_t count_avg;
// double total_power;
// unsigned long lastTime; // tic toc
// double max_power;
// bool two_percent; // ask if not go lower then 2%
} zeroExport_t; } zeroExport_t;
#endif #endif
@ -699,50 +671,50 @@ class settings {
mCfg.plugin.zeroExport.groups[group].sleep = false; mCfg.plugin.zeroExport.groups[group].sleep = false;
snprintf(mCfg.plugin.zeroExport.groups[group].name, ZEROEXPORT_GROUP_MAX_LEN_NAME, "%s", DEF_ZEXPORT); snprintf(mCfg.plugin.zeroExport.groups[group].name, ZEROEXPORT_GROUP_MAX_LEN_NAME, "%s", DEF_ZEXPORT);
// Powermeter // Powermeter
mCfg.plugin.zeroExport.groups[group].pm_refresh = 5;
mCfg.plugin.zeroExport.groups[group].pm_peviousTsp = 0;
mCfg.plugin.zeroExport.groups[group].pm_type = zeroExportPowermeterType_t::None; mCfg.plugin.zeroExport.groups[group].pm_type = zeroExportPowermeterType_t::None;
snprintf(mCfg.plugin.zeroExport.groups[group].pm_url, ZEROEXPORT_GROUP_MAX_LEN_PM_URL, "%s", DEF_ZEXPORT); snprintf(mCfg.plugin.zeroExport.groups[group].pm_url, ZEROEXPORT_GROUP_MAX_LEN_PM_URL, "%s", DEF_ZEXPORT);
snprintf(mCfg.plugin.zeroExport.groups[group].pm_jsonPath, ZEROEXPORT_GROUP_MAX_LEN_PM_JSONPATH, "%s", DEF_ZEXPORT); snprintf(mCfg.plugin.zeroExport.groups[group].pm_jsonPath, ZEROEXPORT_GROUP_MAX_LEN_PM_JSONPATH, "%s", DEF_ZEXPORT);
snprintf(mCfg.plugin.zeroExport.groups[group].pm_user, ZEROEXPORT_GROUP_MAX_LEN_PM_USER, "%s", DEF_ZEXPORT); snprintf(mCfg.plugin.zeroExport.groups[group].pm_user, ZEROEXPORT_GROUP_MAX_LEN_PM_USER, "%s", DEF_ZEXPORT);
snprintf(mCfg.plugin.zeroExport.groups[group].pm_pass, ZEROEXPORT_GROUP_MAX_LEN_PM_PASS, "%s", DEF_ZEXPORT); snprintf(mCfg.plugin.zeroExport.groups[group].pm_pass, ZEROEXPORT_GROUP_MAX_LEN_PM_PASS, "%s", DEF_ZEXPORT);
mCfg.plugin.zeroExport.groups[group].pm_target = zeroExportPowermeterTarget::Sum;
// Inverters // Inverters
for(uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { for(uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) {
mCfg.plugin.zeroExport.groups[group].inverters[inv].enabled = false; mCfg.plugin.zeroExport.groups[group].inverters[inv].enabled = false;
mCfg.plugin.zeroExport.groups[group].inverters[inv].id = -1; mCfg.plugin.zeroExport.groups[group].inverters[inv].id = -1;
mCfg.plugin.zeroExport.groups[group].inverters[inv].target = -1;
mCfg.plugin.zeroExport.groups[group].inverters[inv].powerMin = 10; mCfg.plugin.zeroExport.groups[group].inverters[inv].powerMin = 10;
mCfg.plugin.zeroExport.groups[group].inverters[inv].powerMax = 600; mCfg.plugin.zeroExport.groups[group].inverters[inv].powerMax = 600;
mCfg.plugin.zeroExport.groups[group].inverters[inv].turnOff = false;
// //
mCfg.plugin.zeroExport.groups[group].inverters[inv].waitAckSetLimit = false; mCfg.plugin.zeroExport.groups[group].inverters[inv].waitAck = 0;
mCfg.plugin.zeroExport.groups[group].inverters[inv].waitAckSetPower = false; mCfg.plugin.zeroExport.groups[group].inverters[inv].action = zeroExportAction_t::doNone;
mCfg.plugin.zeroExport.groups[group].inverters[inv].waitAckSetReboot = false; mCfg.plugin.zeroExport.groups[group].inverters[inv].actionTimer = 0;;
mCfg.plugin.zeroExport.groups[group].inverters[inv].doReboot = -1;
mCfg.plugin.zeroExport.groups[group].inverters[inv].doPower = -1;
mCfg.plugin.zeroExport.groups[group].inverters[inv].doLimit = -1;
mCfg.plugin.zeroExport.groups[group].inverters[inv].dcVoltage = 0; mCfg.plugin.zeroExport.groups[group].inverters[inv].dcVoltage = 0;
mCfg.plugin.zeroExport.groups[group].inverters[inv].limit = 0;
mCfg.plugin.zeroExport.groups[group].inverters[inv].limitNew = 0;
} }
// Battery // Battery
mCfg.plugin.zeroExport.groups[group].battEnabled = false; mCfg.plugin.zeroExport.groups[group].battCfg = zeroExportBatteryCfg::none;
mCfg.plugin.zeroExport.groups[group].battVoltageOn = 0; snprintf(mCfg.plugin.zeroExport.groups[group].battTopic, ZEROEXPORT_GROUP_MAX_LEN_BATT_TOPIC, "%s", DEF_ZEXPORT);
mCfg.plugin.zeroExport.groups[group].battVoltageOff = 0; mCfg.plugin.zeroExport.groups[group].battLimitOn = 0;
mCfg.plugin.zeroExport.groups[group].isChangedBattery = true; mCfg.plugin.zeroExport.groups[group].battLimitOff = 0;
// Advanced // Advanced
mCfg.plugin.zeroExport.groups[group].setPoint = 0; mCfg.plugin.zeroExport.groups[group].setPoint = 0;
mCfg.plugin.zeroExport.groups[group].refresh = 10; mCfg.plugin.zeroExport.groups[group].minimum = true;
mCfg.plugin.zeroExport.groups[group].powerTolerance = 10; mCfg.plugin.zeroExport.groups[group].powerTolerance = 10;
mCfg.plugin.zeroExport.groups[group].powerMax = 600; mCfg.plugin.zeroExport.groups[group].powerMax = 600;
mCfg.plugin.zeroExport.groups[group].Kp = -1; mCfg.plugin.zeroExport.groups[group].Kp = -1;
mCfg.plugin.zeroExport.groups[group].Ki = 0; mCfg.plugin.zeroExport.groups[group].Ki = 0;
mCfg.plugin.zeroExport.groups[group].Kd = 0; mCfg.plugin.zeroExport.groups[group].Kd = 0;
mCfg.plugin.zeroExport.groups[group].isChangedAdvanced = true;
// //
mCfg.plugin.zeroExport.groups[group].state = zeroExportState::INIT;
mCfg.plugin.zeroExport.groups[group].lastRun = 0; mCfg.plugin.zeroExport.groups[group].lastRun = 0;
mCfg.plugin.zeroExport.groups[group].lastRefresh = 0; mCfg.plugin.zeroExport.groups[group].lastRefresh = 0;
mCfg.plugin.zeroExport.groups[group].wait = 60000; mCfg.plugin.zeroExport.groups[group].wait = 60000;
mCfg.plugin.zeroExport.groups[group].pm_P = 0; // mCfg.plugin.zeroExport.groups[group].pm_P = 0;
mCfg.plugin.zeroExport.groups[group].pm_P1 = 0; // mCfg.plugin.zeroExport.groups[group].pm_P1 = 0;
mCfg.plugin.zeroExport.groups[group].pm_P2 = 0; // mCfg.plugin.zeroExport.groups[group].pm_P2 = 0;
mCfg.plugin.zeroExport.groups[group].pm_P3 = 0; // mCfg.plugin.zeroExport.groups[group].pm_P3 = 0;
mCfg.plugin.zeroExport.groups[group].battSwitch = false; mCfg.plugin.zeroExport.groups[group].battSwitch = false;
mCfg.plugin.zeroExport.groups[group].power = 0; mCfg.plugin.zeroExport.groups[group].power = 0;
@ -1036,20 +1008,20 @@ class settings {
if(set) { if(set) {
obj[F("enabled")] = mCfg.plugin.zeroExport.groups[group].inverters[inv].enabled; obj[F("enabled")] = mCfg.plugin.zeroExport.groups[group].inverters[inv].enabled;
obj[F("id")] = mCfg.plugin.zeroExport.groups[group].inverters[inv].id; obj[F("id")] = mCfg.plugin.zeroExport.groups[group].inverters[inv].id;
obj[F("target")] = mCfg.plugin.zeroExport.groups[group].inverters[inv].target;
obj[F("powerMin")] = mCfg.plugin.zeroExport.groups[group].inverters[inv].powerMin; obj[F("powerMin")] = mCfg.plugin.zeroExport.groups[group].inverters[inv].powerMin;
obj[F("powerMax")] = mCfg.plugin.zeroExport.groups[group].inverters[inv].powerMax; obj[F("powerMax")] = mCfg.plugin.zeroExport.groups[group].inverters[inv].powerMax;
obj[F("turnOff")] = mCfg.plugin.zeroExport.groups[group].inverters[inv].turnOff;
} else { } else {
if (obj.containsKey(F("enabled"))) if (obj.containsKey(F("enabled")))
getVal<bool>(obj, F("enabled"), &mCfg.plugin.zeroExport.groups[group].inverters[inv].enabled); getVal<bool>(obj, F("enabled"), &mCfg.plugin.zeroExport.groups[group].inverters[inv].enabled);
if (obj.containsKey(F("id"))) if (obj.containsKey(F("id")))
getVal<int8_t>(obj, F("id"), &mCfg.plugin.zeroExport.groups[group].inverters[inv].id); getVal<int8_t>(obj, F("id"), &mCfg.plugin.zeroExport.groups[group].inverters[inv].id);
if (obj.containsKey(F("target")))
getVal<int8_t>(obj, F("target"), &mCfg.plugin.zeroExport.groups[group].inverters[inv].target);
if (obj.containsKey(F("powerMin"))) if (obj.containsKey(F("powerMin")))
getVal<uint16_t>(obj, F("powerMin"), &mCfg.plugin.zeroExport.groups[group].inverters[inv].powerMin); getVal<uint16_t>(obj, F("powerMin"), &mCfg.plugin.zeroExport.groups[group].inverters[inv].powerMin);
if (obj.containsKey(F("powerMax"))) if (obj.containsKey(F("powerMax")))
getVal<uint16_t>(obj, F("powerMax"), &mCfg.plugin.zeroExport.groups[group].inverters[inv].powerMax); getVal<uint16_t>(obj, F("powerMax"), &mCfg.plugin.zeroExport.groups[group].inverters[inv].powerMax);
if (obj.containsKey(F("turnOff")))
getVal<bool>(obj, F("turnOff"), &mCfg.plugin.zeroExport.groups[group].inverters[inv].turnOff);
} }
} }
@ -1059,23 +1031,26 @@ class settings {
obj[F("enabled")] = mCfg.plugin.zeroExport.groups[group].enabled; obj[F("enabled")] = mCfg.plugin.zeroExport.groups[group].enabled;
obj[F("name")] = mCfg.plugin.zeroExport.groups[group].name; obj[F("name")] = mCfg.plugin.zeroExport.groups[group].name;
// Powermeter // Powermeter
obj[F("pm_refresh")] = mCfg.plugin.zeroExport.groups[group].pm_refresh;
obj[F("pm_type")] = mCfg.plugin.zeroExport.groups[group].pm_type; obj[F("pm_type")] = mCfg.plugin.zeroExport.groups[group].pm_type;
obj[F("pm_url")] = mCfg.plugin.zeroExport.groups[group].pm_url; obj[F("pm_url")] = mCfg.plugin.zeroExport.groups[group].pm_url;
obj[F("pm_jsonPath")] = mCfg.plugin.zeroExport.groups[group].pm_jsonPath; obj[F("pm_jsonPath")] = mCfg.plugin.zeroExport.groups[group].pm_jsonPath;
obj[F("pm_user")] = mCfg.plugin.zeroExport.groups[group].pm_user; obj[F("pm_user")] = mCfg.plugin.zeroExport.groups[group].pm_user;
obj[F("pm_pass")] = mCfg.plugin.zeroExport.groups[group].pm_pass; obj[F("pm_pass")] = mCfg.plugin.zeroExport.groups[group].pm_pass;
obj[F("pm_target")] = mCfg.plugin.zeroExport.groups[group].pm_target;
// Inverters // Inverters
JsonArray invArr = obj.createNestedArray(F("inverters")); JsonArray invArr = obj.createNestedArray(F("inverters"));
for(uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { for(uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) {
jsonZeroExportGroupInverter(invArr.createNestedObject(), group, inv, set); jsonZeroExportGroupInverter(invArr.createNestedObject(), group, inv, set);
} }
// Battery // Battery
obj[F("battEnabled")] = mCfg.plugin.zeroExport.groups[group].battEnabled; obj[F("battCfg")] = mCfg.plugin.zeroExport.groups[group].battCfg;
obj[F("battVoltageOn")] = mCfg.plugin.zeroExport.groups[group].battVoltageOn; obj[F("battTopic")] = mCfg.plugin.zeroExport.groups[group].battTopic;
obj[F("battVoltageOff")] = mCfg.plugin.zeroExport.groups[group].battVoltageOff; obj[F("battLimitOn")] = mCfg.plugin.zeroExport.groups[group].battLimitOn;
obj[F("battLimitOff")] = mCfg.plugin.zeroExport.groups[group].battLimitOff;
// Advanced // Advanced
obj[F("setPoint")] = mCfg.plugin.zeroExport.groups[group].setPoint; obj[F("setPoint")] = mCfg.plugin.zeroExport.groups[group].setPoint;
obj[F("refresh")] = mCfg.plugin.zeroExport.groups[group].refresh; obj[F("minimum")] = mCfg.plugin.zeroExport.groups[group].minimum;
obj[F("powerTolerance")] = mCfg.plugin.zeroExport.groups[group].powerTolerance; obj[F("powerTolerance")] = mCfg.plugin.zeroExport.groups[group].powerTolerance;
obj[F("powerMax")] = mCfg.plugin.zeroExport.groups[group].powerMax; obj[F("powerMax")] = mCfg.plugin.zeroExport.groups[group].powerMax;
obj[F("Kp")] = mCfg.plugin.zeroExport.groups[group].Kp; obj[F("Kp")] = mCfg.plugin.zeroExport.groups[group].Kp;
@ -1088,6 +1063,8 @@ class settings {
if (obj.containsKey(F("name"))) if (obj.containsKey(F("name")))
getChar(obj, F("name"), mCfg.plugin.zeroExport.groups[group].name, ZEXPORT_ADDR_LEN); getChar(obj, F("name"), mCfg.plugin.zeroExport.groups[group].name, ZEXPORT_ADDR_LEN);
// Powermeter // Powermeter
if (obj.containsKey(F("pm_refresh")))
getVal<uint8_t>(obj, F("pm_refresh"), &mCfg.plugin.zeroExport.groups[group].pm_refresh);
if (obj.containsKey(F("pm_type"))) if (obj.containsKey(F("pm_type")))
getVal<uint8_t>(obj, F("pm_type"), &mCfg.plugin.zeroExport.groups[group].pm_type); getVal<uint8_t>(obj, F("pm_type"), &mCfg.plugin.zeroExport.groups[group].pm_type);
if (obj.containsKey(F("pm_url"))) if (obj.containsKey(F("pm_url")))
@ -1098,6 +1075,8 @@ class settings {
getChar(obj, F("pm_user"), mCfg.plugin.zeroExport.groups[group].pm_user, ZEROEXPORT_GROUP_MAX_LEN_PM_USER); getChar(obj, F("pm_user"), mCfg.plugin.zeroExport.groups[group].pm_user, ZEROEXPORT_GROUP_MAX_LEN_PM_USER);
if (obj.containsKey(F("pm_pass"))) if (obj.containsKey(F("pm_pass")))
getChar(obj, F("pm_pass"), mCfg.plugin.zeroExport.groups[group].pm_pass, ZEROEXPORT_GROUP_MAX_LEN_PM_PASS); getChar(obj, F("pm_pass"), mCfg.plugin.zeroExport.groups[group].pm_pass, ZEROEXPORT_GROUP_MAX_LEN_PM_PASS);
if (obj.containsKey(F("pm_target")))
getVal<uint8_t>(obj, F("pm_target"), &mCfg.plugin.zeroExport.groups[group].pm_target);
// Inverters // Inverters
if (obj.containsKey(F("inverters"))) { if (obj.containsKey(F("inverters"))) {
for(uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { for(uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) {
@ -1105,17 +1084,19 @@ class settings {
} }
} }
// Battery // Battery
if (obj.containsKey(F("battEnabled"))) if (obj.containsKey(F("battCfg")))
getVal<bool>(obj, F("battEnabled"), &mCfg.plugin.zeroExport.groups[group].battEnabled); getVal<uint8_t>(obj, F("battCfg"), &mCfg.plugin.zeroExport.groups[group].battCfg);
if (obj.containsKey(F("battVoltageOn"))) if (obj.containsKey(F("battTopic")))
getVal<float>(obj, F("battVoltageOn"), &mCfg.plugin.zeroExport.groups[group].battVoltageOn); getChar(obj, F("battTopic"), mCfg.plugin.zeroExport.groups[group].battTopic, ZEROEXPORT_GROUP_MAX_LEN_BATT_TOPIC);
if (obj.containsKey(F("battVoltageOff"))) if (obj.containsKey(F("battLimitOn")))
getVal<float>(obj, F("battVoltageOff"), &mCfg.plugin.zeroExport.groups[group].battVoltageOff); getVal<float>(obj, F("battLimitOn"), &mCfg.plugin.zeroExport.groups[group].battLimitOn);
if (obj.containsKey(F("battLimitOff")))
getVal<float>(obj, F("battLimitOff"), &mCfg.plugin.zeroExport.groups[group].battLimitOff);
// Advanced // Advanced
if (obj.containsKey(F("setPoint"))) if (obj.containsKey(F("setPoint")))
getVal<int16_t>(obj, F("setPoint"), &mCfg.plugin.zeroExport.groups[group].setPoint); getVal<int16_t>(obj, F("setPoint"), &mCfg.plugin.zeroExport.groups[group].setPoint);
if (obj.containsKey(F("refresh"))) if (obj.containsKey(F("minimum")))
getVal<uint8_t>(obj, F("refresh"), &mCfg.plugin.zeroExport.groups[group].refresh); getVal<bool>(obj, F("minimum"), &mCfg.plugin.zeroExport.groups[group].minimum);
if (obj.containsKey(F("powerTolerance"))) if (obj.containsKey(F("powerTolerance")))
getVal<uint8_t>(obj, F("powerTolerance"), &mCfg.plugin.zeroExport.groups[group].powerTolerance); getVal<uint8_t>(obj, F("powerTolerance"), &mCfg.plugin.zeroExport.groups[group].powerTolerance);
if (obj.containsKey(F("powerMax"))) if (obj.containsKey(F("powerMax")))

2
src/defines.h

@ -13,7 +13,7 @@
//------------------------------------- //-------------------------------------
#define VERSION_MAJOR 0 #define VERSION_MAJOR 0
#define VERSION_MINOR 8 #define VERSION_MINOR 8
#define VERSION_PATCH 1030003 #define VERSION_PATCH 1030013
//------------------------------------- //-------------------------------------
typedef struct { typedef struct {
uint8_t ch; uint8_t ch;

451
src/plugins/zeroExport/powermeter.h

@ -3,17 +3,22 @@
// Creative Commons - https://creativecommons.org/licenses/by-nc-sa/4.0/deed // Creative Commons - https://creativecommons.org/licenses/by-nc-sa/4.0/deed
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#if defined(PLUGIN_ZEROEXPORT)
#ifndef __POWERMETER_H__ #ifndef __POWERMETER_H__
#define __POWERMETER_H__ #define __POWERMETER_H__
#include <AsyncJson.h> #include <AsyncJson.h>
#include <HTTPClient.h> #include <HTTPClient.h>
#include "config/settings.h" #include "config/settings.h"
#if defined(ZEROEXPORT_POWERMETER_TIBBER) #if defined(ZEROEXPORT_POWERMETER_TIBBER)
#include <base64.h> #include <base64.h>
#include <string.h> #include <string.h>
#include <list> #include <list>
#include "plugins/zeroExport/lib/sml.h" #include "plugins/zeroExport/lib/sml.h"
typedef struct { typedef struct {
@ -23,113 +28,237 @@ typedef struct {
} OBISHandler; } OBISHandler;
#endif #endif
typedef struct {
float P;
float P1;
float P2;
float P3;
} PowermeterBuffer_t;
class powermeter { class powermeter {
public: public:
powermeter() { /** powermeter
} * constructor
*/
~powermeter() { powermeter() {}
}
bool setup(zeroExport_t *cfg, JsonObject *log /*Hier muss noch geklärt werden was gebraucht wird*/) { /** ~powermeter
* destructor
*/
~powermeter() {}
/** setup
* Initialisierung
* @param *cfg
* @param *mqtt
* @param *log
* @returns void
*/
bool setup(zeroExport_t *cfg, PubMqttType *mqtt, JsonObject *log) {
mCfg = cfg; mCfg = cfg;
mMqtt = mqtt;
mLog = log; mLog = log;
return true; return true;
} }
/** loop /** loop
* abfrage der gruppen um die aktuellen Werte (Zähler) zu ermitteln. * Arbeitsschleife
* @param void
* @returns void
* @todo emergency
*/ */
void loop(unsigned long *tsp, bool *doLog) { void loop(void) {
if (*tsp - mPreviousTsp <= 1000) return; // skip when it is to fast if (millis() - mPreviousTsp <= 1000) return; // skip when it is to fast
mPreviousTsp = *tsp; mPreviousTsp = millis();
PowermeterBuffer_t power; bool result = false;
float power = 0.0;
for (u_short group = 0; group < ZEROEXPORT_MAX_GROUPS; group++) { for (u_short group = 0; group < ZEROEXPORT_MAX_GROUPS; group++) {
if ((!mCfg->groups[group].enabled) || (mCfg->groups[group].sleep)) continue;
if ((millis() - mCfg->groups[group].pm_peviousTsp) <= ((uint16_t)mCfg->groups[group].pm_refresh * 1000)) continue;
mCfg->groups[group].pm_peviousTsp = millis();
switch (mCfg->groups[group].pm_type) { switch (mCfg->groups[group].pm_type) {
#if defined(ZEROEXPORT_POWERMETER_SHELLY) #if defined(ZEROEXPORT_POWERMETER_SHELLY)
case zeroExportPowermeterType_t::Shelly: case zeroExportPowermeterType_t::Shelly:
power = getPowermeterWattsShelly(*mLog, group); result = getPowermeterWattsShelly(*mLog, group, &power);
break; break;
#endif #endif
#if defined(ZEROEXPORT_POWERMETER_TASMOTA) #if defined(ZEROEXPORT_POWERMETER_TASMOTA)
case zeroExportPowermeterType_t::Tasmota: case zeroExportPowermeterType_t::Tasmota:
power = getPowermeterWattsTasmota(*mLog, group); result = getPowermeterWattsTasmota(*mLog, group, &power);
break;
#endif
#if defined(ZEROEXPORT_POWERMETER_MQTT)
case zeroExportPowermeterType_t::Mqtt:
power = getPowermeterWattsMqtt(*mLog, group);
break; break;
#endif #endif
#if defined(ZEROEXPORT_POWERMETER_HICHI) #if defined(ZEROEXPORT_POWERMETER_HICHI)
case zeroExportPowermeterType_t::Hichi: case zeroExportPowermeterType_t::Hichi:
power = getPowermeterWattsHichi(*mLog, group); result = getPowermeterWattsHichi(*mLog, group, &power);
break; break;
#endif #endif
#if defined(ZEROEXPORT_POWERMETER_TIBBER) #if defined(ZEROEXPORT_POWERMETER_TIBBER)
case zeroExportPowermeterType_t::Tibber: case zeroExportPowermeterType_t::Tibber:
power = getPowermeterWattsTibber(*mLog, group); result = getPowermeterWattsTibber(*mLog, group, &power);
mPreviousTsp += 2000; // Zusätzliche Pause mPreviousTsp += 2000; // Zusätzliche Pause
break; break;
#endif #endif
#if defined(ZEROEXPORT_POWERMETER_SHRDZM) #if defined(ZEROEXPORT_POWERMETER_SHRDZM)
case zeroExportPowermeterType_t::Shrdzm: case zeroExportPowermeterType_t::Shrdzm:
power = getPowermeterWattsShrdzm(*mLog, group); result = getPowermeterWattsShrdzm(*mLog, group, &power);
break; break;
#endif #endif
} }
bufferWrite(power, group); if (result) {
if (mCfg->debug) *doLog = true; bufferWrite(power, group);
// MQTT - Powermeter
if (mCfg->debug) {
if (mMqtt->isConnected()) {
// P
mqttObj["Sum"] = ah::round1(power);
// mqttObj["L1"] = ah::round1(power.P1);
// mqttObj["L2"] = ah::round1(power.P2);
// mqttObj["L3"] = ah::round1(power.P3);
mMqtt->publish(String("zero/state/groups/" + String(group) + "/powermeter/P").c_str(), mqttDoc.as<std::string>().c_str(), false);
mqttDoc.clear();
// W (TODO)
}
}
}
} }
} }
/** groupGetPowermeter /** getDataAVG
* Holt die Daten vom Powermeter * Holt die Daten vom Powermeter
* @param group * @param group
* @returns true/false * @returns value
*/ */
PowermeterBuffer_t getDataAVG(uint8_t group) { float getDataAVG(uint8_t group) {
PowermeterBuffer_t avg; float avg = 0.0;
avg.P = avg.P1 = avg.P2 = avg.P2 = avg.P3 = 0;
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
avg.P += mPowermeterBuffer[group][i].P; avg += mPowermeterBuffer[group][i];
avg.P1 += mPowermeterBuffer[group][i].P1;
avg.P2 += mPowermeterBuffer[group][i].P2;
avg.P3 += mPowermeterBuffer[group][i].P3;
} }
avg.P = avg.P / 5; avg = avg / 5.0;
avg.P1 = avg.P1 / 5;
avg.P2 = avg.P2 / 5;
avg.P3 = avg.P3 / 5;
return avg; return avg;
} }
/** getDataMIN
* Holt die Daten vom Powermeter
* @param group
* @returns value
*/
float getDataMIN(uint8_t group) {
float min = 0.0;
for (int i = 0; i < 5; i++) {
if (i == 0) min = mPowermeterBuffer[group][i];
if ( min > mPowermeterBuffer[group][i]) min = mPowermeterBuffer[group][i];
}
return min;
}
/** onMqttConnect
*
*/
void onMqttConnect(void) {
#if defined(ZEROEXPORT_POWERMETER_MQTT)
for (uint8_t group = 0; group < ZEROEXPORT_MAX_GROUPS; group++) {
if (!strcmp(mCfg->groups[group].pm_jsonPath, "")) continue;
if (!mCfg->groups[group].enabled) continue;
if (mCfg->groups[group].pm_type == zeroExportPowermeterType_t::Mqtt) {
mMqtt->subscribeExtern(String(mCfg->groups[group].pm_jsonPath).c_str(), QOS_2);
}
}
#endif /*defined(ZEROEXPORT_POWERMETER_MQTT)*/
}
/** onMqttMessage
*
*/
void onMqttMessage(JsonObject obj) {
String topic = String(obj["topic"]);
#if defined(ZEROEXPORT_POWERMETER_MQTT)
for (uint8_t group = 0; group < ZEROEXPORT_MAX_GROUPS; group++) {
if (!mCfg->groups[group].enabled) continue;
if (!mCfg->groups[group].pm_type == zeroExportPowermeterType_t::Mqtt) continue;
if (!strcmp(mCfg->groups[group].pm_jsonPath, "")) continue;
if (strcmp(mCfg->groups[group].pm_jsonPath, String(topic).c_str())) continue;
float power = 0.0;
power = (uint16_t)obj["val"];
bufferWrite(power, group);
// MQTT - Powermeter
if (mCfg->debug) {
if (mMqtt->isConnected()) {
// P
mqttObj["Sum"] = ah::round1(power);
/// mqttObj["L1"] = ah::round1(power.P1);
/// mqttObj["L2"] = ah::round1(power.P2);
/// mqttObj["L3"] = ah::round1(power.P3);
mMqtt->publish(String("zero/state/groups/" + String(group) + "/powermeter/P").c_str(), mqttDoc.as<std::string>().c_str(), false);
mqttDoc.clear();
// W (TODO)
}
}
return;
}
#endif /*defined(ZEROEXPORT_POWERMETER_MQTT)*/
}
private: private:
/** mqttSubscribe
* when a MQTT Msg is needed to subscribe, then a publish is leading
* @param gr
* @param payload
* @returns void
*/
void mqttSubscribe(String gr, String payload) {
// mqttPublish(gr, payload);
mMqtt->subscribe(gr.c_str(), QOS_2);
}
/** mqttPublish
* when a MQTT Msg is needed to Publish, but not to subscribe.
* @param gr
* @param payload
* @param retain
* @returns void
*/
void mqttPublish(String gr, String payload, bool retain = false) {
mMqtt->publish(gr.c_str(), payload.c_str(), retain);
}
HTTPClient http; HTTPClient http;
zeroExport_t *mCfg; zeroExport_t *mCfg;
PubMqttType *mMqtt = nullptr;
JsonObject *mLog; JsonObject *mLog;
unsigned long mPreviousTsp = 0; unsigned long mPreviousTsp = 0;
PowermeterBuffer_t mPowermeterBuffer[ZEROEXPORT_MAX_GROUPS][5] = {0}; float mPowermeterBuffer[ZEROEXPORT_MAX_GROUPS][5] = {0};
short mPowermeterBufferPos[ZEROEXPORT_MAX_GROUPS] = {0}; short mPowermeterBufferPos[ZEROEXPORT_MAX_GROUPS] = {0};
StaticJsonDocument<512> mqttDoc; // DynamicJsonDocument mqttDoc(512);
JsonObject mqttObj = mqttDoc.to<JsonObject>();
// set HTTPClient header /** setHeader
void setHeader(HTTPClient* h) { *
*/
void setHeader(HTTPClient *h) {
h->setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); h->setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
h->setUserAgent("Ahoy-Agent"); h->setUserAgent("Ahoy-Agent");
// TODO: Ahoy-0.8.850024-zero // TODO: Ahoy-0.8.850024-zero
@ -146,10 +275,7 @@ class powermeter {
* @param group * @param group
* @returns true/false * @returns true/false
*/ */
PowermeterBuffer_t getPowermeterWattsShelly(JsonObject logObj, uint8_t group) { bool getPowermeterWattsShelly(JsonObject logObj, uint8_t group, float *power) {
PowermeterBuffer_t result;
result.P = result.P1 = result.P2 = result.P3 = 0;
logObj["mod"] = "getPowermeterWattsShelly"; logObj["mod"] = "getPowermeterWattsShelly";
setHeader(&http); setHeader(&http);
@ -165,93 +291,63 @@ class powermeter {
DeserializationError error = deserializeJson(doc, http.getString()); DeserializationError error = deserializeJson(doc, http.getString());
if (error) { if (error) {
logObj["err"] = "deserializeJson: " + String(error.c_str()); logObj["err"] = "deserializeJson: " + String(error.c_str());
return result; return false;
} else { } else {
if (doc.containsKey(F("total_power"))) { switch (mCfg->groups[group].pm_target) {
// Shelly 3EM case zeroExportPowermeterTarget::L1:
result.P = doc["total_power"]; if (doc.containsKey(F("emeters"))) {
} else if (doc.containsKey(F("em:0"))) { // Shelly 3EM
// Shelly pro 3EM *power = doc["emeters"][0]["power"];
result.P = doc["em:0"]["total_act_power"]; } else if (doc.containsKey(F("em:0"))) {
} else if (doc.containsKey(F("total_act_power"))) { // Shelly pro 3EM
// Shelly pro 3EM *power = doc["em:0"]["a_act_power"];
result.P = doc["total_act_power"]; } else if (doc.containsKey(F("a_act_power"))) {
} else { // Shelly pro 3EM
// Keine Daten *power = doc["a_act_power"];
result.P = 0; }
} break;
case zeroExportPowermeterTarget::L2:
if (doc.containsKey(F("emeters"))) { if (doc.containsKey(F("emeters"))) {
// Shelly 3EM // Shelly 3EM
result.P1 = doc["emeters"][0]["power"]; *power = doc["emeters"][1]["power"];
} else if (doc.containsKey(F("em:0"))) { } else if (doc.containsKey(F("em:0"))) {
// Shelly pro 3EM // Shelly pro 3EM
result.P1 = doc["em:0"]["a_act_power"]; *power = doc["em:0"]["b_act_power"];
} else if (doc.containsKey(F("a_act_power"))) { } else if (doc.containsKey(F("b_act_power"))) {
// Shelly pro 3EM // Shelly pro 3EM
result.P1 = doc["a_act_power"]; *power = doc["b_act_power"];
} else if (doc.containsKey(F("switch:0"))) { }
// Shelly plus1pm plus2pm break;
result.P1 = doc["switch:0"]["apower"]; case zeroExportPowermeterTarget::L3:
result.P += result.P1; if (doc.containsKey(F("emeters"))) {
} else if (doc.containsKey(F("apower"))) { // Shelly 3EM
// Shelly Alternative *power = doc["emeters"][2]["power"];
result.P1 = doc["apower"]; } else if (doc.containsKey(F("em:0"))) {
result.P += result.P1; // Shelly pro 3EM
} else { *power = doc["em:0"]["c_act_power"];
// Keine Daten } else if (doc.containsKey(F("c_act_power"))) {
result.P1 = 0; // Shelly pro 3EM
} *power = doc["c_act_power"];
}
if (doc.containsKey(F("emeters"))) { break;
// Shelly 3EM case zeroExportPowermeterTarget::Sum:
result.P2 = doc["emeters"][1]["power"]; default:
} else if (doc.containsKey(F("em:0"))) { if (doc.containsKey(F("total_power"))) {
// Shelly pro 3EM // Shelly 3EM
result.P2 = doc["em:0"]["b_act_power"]; *power = doc["total_power"];
} else if (doc.containsKey(F("b_act_power"))) { } else if (doc.containsKey(F("em:0"))) {
// Shelly pro 3EM // Shelly pro 3EM
result.P2 = doc["b_act_power"]; *power = doc["em:0"]["total_act_power"];
} else if (doc.containsKey(F("switch:1"))) { } else if (doc.containsKey(F("total_act_power"))) {
// Shelly plus1pm plus2pm // Shelly pro 3EM
result.P2 = doc["switch.1"]["apower"]; *power = doc["total_act_power"];
result.P += result.P2; }
//} else if (doc.containsKey(F("apower"))) { break;
// Shelly Alternative
// mCfg->groups[group].pmPowerL2 = doc["apower"];
// mCfg->groups[group].pmPower += mCfg->groups[group].pmPowerL2;
// ret = true;
} else {
// Keine Daten
result.P2 = 0;
}
if (doc.containsKey(F("emeters"))) {
// Shelly 3EM
result.P3 = doc["emeters"][2]["power"];
} else if (doc.containsKey(F("em:0"))) {
// Shelly pro 3EM
result.P3 = doc["em:0"]["c_act_power"];
} else if (doc.containsKey(F("c_act_power"))) {
// Shelly pro 3EM
result.P3 = doc["c_act_power"];
} else if (doc.containsKey(F("switch:2"))) {
// Shelly plus1pm plus2pm
result.P3 = doc["switch:2"]["apower"];
result.P += result.P3;
//} else if (doc.containsKey(F("apower"))) {
// Shelly Alternative
// mCfg->groups[group].pmPowerL3 = doc["apower"];
// mCfg->groups[group].pmPower += mCfg->groups[group].pmPowerL3;
// result = true;
} else {
// Keine Daten
result.P3 = 0;
} }
} }
} }
http.end(); http.end();
return result; return true;
} }
#endif #endif
@ -262,10 +358,7 @@ class powermeter {
* @param group * @param group
* @returns true/false * @returns true/false
*/ */
PowermeterBuffer_t getPowermeterWattsTasmota(JsonObject logObj, uint8_t group) { bool getPowermeterWattsTasmota(JsonObject logObj, uint8_t group, float *power) {
PowermeterBuffer_t result;
result.P = result.P1 = result.P2 = result.P3 = 0;
logObj["mod"] = "getPowermeterWattsTasmota"; logObj["mod"] = "getPowermeterWattsTasmota";
/* /*
// TODO: nicht komplett // TODO: nicht komplett
@ -326,28 +419,7 @@ class powermeter {
} }
http.end(); http.end();
*/ */
return result; return false;
}
#endif
#if defined(ZEROEXPORT_POWERMETER_MQTT)
/** getPowermeterWattsMqtt
* ...
* @param logObj
* @param group
* @returns true/false
*/
PowermeterBuffer_t getPowermeterWattsMqtt(JsonObject logObj, uint8_t group) {
PowermeterBuffer_t result;
result.P = result.P1 = result.P2 = result.P3 = 0;
logObj["mod"] = "getPowermeterWattsMqtt";
// topic for powermeter?
result.P = mCfg->groups[group].pm_P;
result.P1 = result.P2 = result.P3 = mCfg->groups[group].pm_P / 3;
return result;
} }
#endif #endif
@ -358,10 +430,7 @@ class powermeter {
* @param group * @param group
* @returns true/false * @returns true/false
*/ */
PowermeterBuffer_t getPowermeterWattsHichi(JsonObject logObj, uint8_t group) { bool getPowermeterWattsHichi(JsonObject logObj, uint8_t group, float *power) {
PowermeterBuffer_t result;
result.P = result.P1 = result.P2 = result.P3 = 0;
logObj["mod"] = "getPowermeterWattsHichi"; logObj["mod"] = "getPowermeterWattsHichi";
// Hier neuer Code - Anfang // Hier neuer Code - Anfang
@ -370,7 +439,7 @@ class powermeter {
// Hier neuer Code - Ende // Hier neuer Code - Ende
return result; return false;
} }
#endif #endif
@ -405,32 +474,27 @@ class powermeter {
07 01 00 02 08 02 ff #objName: OBIS-Kennzahl für Wirkenergie Einspeisung Tarif2 07 01 00 02 08 02 ff #objName: OBIS-Kennzahl für Wirkenergie Einspeisung Tarif2
*/ */
const std::list<OBISHandler> smlHandlerList{ const std::list<OBISHandler> smlHandlerList{
{{0x01, 0x00, 0x10, 0x07, 0x00, 0xff}, &smlOBISW, &_powerMeterTotal}, // total - OBIS-Kennzahl für momentane Gesamtwirkleistung {{0x01, 0x00, 0x10, 0x07, 0x00, 0xff}, &smlOBISW, &_powerMeterTotal}, // total - OBIS-Kennzahl für momentane Gesamtwirkleistung
{{0x01, 0x00, 0x24, 0x07, 0x00, 0xff}, &smlOBISW, &_powerMeter1Power}, // OBIS-Kennzahl für momentane Wirkleistung in Phase L1 {{0x01, 0x00, 0x24, 0x07, 0x00, 0xff}, &smlOBISW, &_powerMeter1Power}, // OBIS-Kennzahl für momentane Wirkleistung in Phase L1
{{0x01, 0x00, 0x38, 0x07, 0x00, 0xff}, &smlOBISW, &_powerMeter2Power}, // OBIS-Kennzahl für momentane Wirkleistung in Phase L2 {{0x01, 0x00, 0x38, 0x07, 0x00, 0xff}, &smlOBISW, &_powerMeter2Power}, // OBIS-Kennzahl für momentane Wirkleistung in Phase L2
{{0x01, 0x00, 0x4c, 0x07, 0x00, 0xff}, &smlOBISW, &_powerMeter3Power}, // OBIS-Kennzahl für momentane Wirkleistung in Phase L3 {{0x01, 0x00, 0x4c, 0x07, 0x00, 0xff}, &smlOBISW, &_powerMeter3Power}, // OBIS-Kennzahl für momentane Wirkleistung in Phase L3
{{0x01, 0x00, 0x01, 0x08, 0x00, 0xff}, &smlOBISWh, &_powerMeterImport}, {{0x01, 0x00, 0x01, 0x08, 0x00, 0xff}, &smlOBISWh, &_powerMeterImport},
{{0x01, 0x00, 0x02, 0x08, 0x00, 0xff}, &smlOBISWh, &_powerMeterExport}}; {{0x01, 0x00, 0x02, 0x08, 0x00, 0xff}, &smlOBISWh, &_powerMeterExport}};
PowermeterBuffer_t getPowermeterWattsTibber(JsonObject logObj, uint8_t group) { bool getPowermeterWattsTibber(JsonObject logObj, uint8_t group, float *power) {
mPreviousTsp = mPreviousTsp + 2000; // Zusätzliche Pause mPreviousTsp = mPreviousTsp + 2000; // Zusätzliche Pause
PowermeterBuffer_t result; bool result = false;
result.P = result.P1 = result.P2 = result.P3 = 0;
logObj["mod"] = "getPowermeterWattsTibber"; logObj["mod"] = "getPowermeterWattsTibber";
String auth; String auth;
if(strlen(mCfg->groups[group].pm_user) > 0 && strlen(mCfg->groups[group].pm_pass) > 0) { if (strlen(mCfg->groups[group].pm_user) > 0 && strlen(mCfg->groups[group].pm_pass) > 0) {
auth = base64::encode(String(mCfg->groups[group].pm_user) + String(":") + String(mCfg->groups[group].pm_pass)); auth = base64::encode(String(mCfg->groups[group].pm_user) + String(":") + String(mCfg->groups[group].pm_pass));
snprintf(mCfg->groups[group].pm_user, ZEROEXPORT_GROUP_MAX_LEN_PM_USER, "%s", DEF_ZEXPORT); snprintf(mCfg->groups[group].pm_user, ZEROEXPORT_GROUP_MAX_LEN_PM_USER, "%s", DEF_ZEXPORT);
snprintf(mCfg->groups[group].pm_pass, ZEROEXPORT_GROUP_MAX_LEN_PM_PASS, "%s", auth.c_str()); snprintf(mCfg->groups[group].pm_pass, ZEROEXPORT_GROUP_MAX_LEN_PM_PASS, "%s", auth.c_str());
//@TODO:mApp->saveSettings(false); //@TODO:mApp->saveSettings(false);
} } else {
else
{
auth = mCfg->groups[group].pm_pass; auth = mCfg->groups[group].pm_pass;
} }
@ -451,14 +515,8 @@ class powermeter {
switch (smlCurrentState) { switch (smlCurrentState) {
case SML_FINAL: case SML_FINAL:
result.P = _powerMeterTotal; *power = _powerMeterTotal;
result.P1 = _powerMeter1Power; result = true;
result.P2 = _powerMeter2Power;
result.P3 = _powerMeter3Power;
if (!(_powerMeter1Power && _powerMeter2Power && _powerMeter3Power)) {
result.P1 = result.P2 = result.P3 = _powerMeterTotal / 3;
}
break; break;
case SML_LISTEND: case SML_LISTEND:
// check handlers on last received list // check handlers on last received list
@ -487,19 +545,14 @@ class powermeter {
* @TODO: Username & Passwort wird mittels base64 verschlüsselt. Dies wird für die Authentizierung benötigt. Wichtig diese im WebUI unkenntlich zu machen und base64 im eeprom zu speichern, statt klartext. * @TODO: Username & Passwort wird mittels base64 verschlüsselt. Dies wird für die Authentizierung benötigt. Wichtig diese im WebUI unkenntlich zu machen und base64 im eeprom zu speichern, statt klartext.
* @TODO: Abfrage Interval einbauen. Info: Datei-Size kann auch mal 0-bytes sein? * @TODO: Abfrage Interval einbauen. Info: Datei-Size kann auch mal 0-bytes sein?
*/ */
PowermeterBuffer_t getPowermeterWattsShrdzm(JsonObject logObj, uint8_t group) { bool getPowermeterWattsShrdzm(JsonObject logObj, uint8_t group, float *power) {
PowermeterBuffer_t result;
result.P = result.P1 = result.P2 = result.P3 = 0;
logObj["mod"] = "getPowermeterWattsShrdzm"; logObj["mod"] = "getPowermeterWattsShrdzm";
setHeader(&http); setHeader(&http);
String url = String url =
String("http://") + String(mCfg->groups[group].pm_url) + String("http://") + String(mCfg->groups[group].pm_url) +
String("/") + String(mCfg->groups[group].pm_jsonPath + String("/") + String(mCfg->groups[group].pm_jsonPath + String("?user=") + String(mCfg->groups[group].pm_user) + String("&password=") + String(mCfg->groups[group].pm_pass));
String("?user=") + String(mCfg->groups[group].pm_user) +
String("&password=") + String(mCfg->groups[group].pm_pass));
http.begin(url); http.begin(url);
@ -509,25 +562,23 @@ class powermeter {
DeserializationError error = deserializeJson(doc, http.getString()); DeserializationError error = deserializeJson(doc, http.getString());
if (error) { if (error) {
logObj["err"] = "deserializeJson: " + String(error.c_str()); logObj["err"] = "deserializeJson: " + String(error.c_str());
return result; return false;
} else { } else {
if (doc.containsKey(F("16.7.0"))) { if (doc.containsKey(F("16.7.0"))) {
result.P = doc["16.7.0"]; *power = doc["16.7.0"];
} }
if (!(result.P1 && result.P2 && result.P3)) {
result.P1 = result.P2 = result.P3 = result.P / 3;
}
} }
} }
http.end(); http.end();
return result; return true;
} }
#endif #endif
void bufferWrite(PowermeterBuffer_t raw, short group) { /**
*
*/
void bufferWrite(float raw, short group) {
mPowermeterBuffer[group][mPowermeterBufferPos[group]] = raw; mPowermeterBuffer[group][mPowermeterBufferPos[group]] = raw;
mPowermeterBufferPos[group]++; mPowermeterBufferPos[group]++;
if (mPowermeterBufferPos[group] >= 5) mPowermeterBufferPos[group] = 0; if (mPowermeterBufferPos[group] >= 5) mPowermeterBufferPos[group] = 0;
@ -535,3 +586,5 @@ class powermeter {
}; };
#endif /*__POWERMETER_H__*/ #endif /*__POWERMETER_H__*/
#endif /* #if defined(PLUGIN_ZEROEXPORT) */

2051
src/plugins/zeroExport/zeroExport.h

File diff suppressed because it is too large

34
src/publisher/pubMqtt.h

@ -27,6 +27,7 @@
#include "pubMqttIvData.h" #include "pubMqttIvData.h"
typedef std::function<void(JsonObject)> subscriptionCb; typedef std::function<void(JsonObject)> subscriptionCb;
typedef std::function<void(void)> connectionCb;
typedef struct { typedef struct {
bool running; bool running;
@ -216,6 +217,16 @@ class PubMqtt {
mClient.subscribe(topic, qos); mClient.subscribe(topic, qos);
} }
void subscribeExtern(const char *subTopic, uint8_t qos = QOS_0) {
char topic[MQTT_TOPIC_LEN + 20];
snprintf(topic, (MQTT_TOPIC_LEN + 20), "%s", subTopic);
mClient.subscribe(topic, qos);
}
void setConnectionCb(connectionCb cb) {
mConnectionCb = cb;
}
void setSubscriptionCb(subscriptionCb cb) { void setSubscriptionCb(subscriptionCb cb) {
mSubscriptionCb = cb; mSubscriptionCb = cb;
} }
@ -257,15 +268,21 @@ class PubMqtt {
tickerMinute(); tickerMinute();
publish(mLwtTopic.data(), mqttStr[MQTT_STR_LWT_CONN], true, false); publish(mLwtTopic.data(), mqttStr[MQTT_STR_LWT_CONN], true, false);
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) { // for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
snprintf(mVal.data(), mVal.size(), "ctrl/limit/%d", i); // snprintf(mVal.data(), mVal.size(), "ctrl/limit/%d", i);
subscribe(mVal.data(), QOS_2); // subscribe(mVal.data(), QOS_2);
snprintf(mVal.data(), mVal.size(), "ctrl/restart/%d", i); // snprintf(mVal.data(), mVal.size(), "ctrl/restart/%d", i);
subscribe(mVal.data()); // subscribe(mVal.data());
snprintf(mVal.data(), mVal.size(), "ctrl/power/%d", i); // snprintf(mVal.data(), mVal.size(), "ctrl/power/%d", i);
subscribe(mVal.data()); // subscribe(mVal.data());
} // }
snprintf(mVal.data(), mVal.size(), "ctrl/#");
subscribe(mVal.data(), QOS_2);
subscribe(subscr[MQTT_SUBS_SET_TIME]); subscribe(subscr[MQTT_SUBS_SET_TIME]);
if(NULL == mConnectionCb)
return;
(mConnectionCb)();
} }
void onDisconnect(espMqttClientTypes::DisconnectReason reason) { void onDisconnect(espMqttClientTypes::DisconnectReason reason) {
@ -617,6 +634,7 @@ class PubMqtt {
uint32_t mRxCnt = 0, mTxCnt = 0; uint32_t mRxCnt = 0, mTxCnt = 0;
std::queue<sendListCmdIv> mSendList; std::queue<sendListCmdIv> mSendList;
std::array<bool, MAX_NUM_INVERTERS> mSendAlarm; std::array<bool, MAX_NUM_INVERTERS> mSendAlarm;
connectionCb mConnectionCb = nullptr;
subscriptionCb mSubscriptionCb = nullptr; subscriptionCb mSubscriptionCb = nullptr;
bool mLastAnyAvail = false; bool mLastAnyAvail = false;
std::array<InverterStatus, MAX_NUM_INVERTERS> mLastIvState; std::array<InverterStatus, MAX_NUM_INVERTERS> mLastIvState;

29
src/web/RestApi.h

@ -242,7 +242,7 @@ class RestApi {
if((len + index) != total) if((len + index) != total)
return; // not last frame - nothing to do return; // not last frame - nothing to do
DynamicJsonDocument json(1000); DynamicJsonDocument json(2000);
AsyncJsonResponse* response = new AsyncJsonResponse(false, 200); AsyncJsonResponse* response = new AsyncJsonResponse(false, 200);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
@ -837,11 +837,13 @@ class RestApi {
objGroup[F("enabled")] = (bool)mConfig->plugin.zeroExport.groups[group].enabled; objGroup[F("enabled")] = (bool)mConfig->plugin.zeroExport.groups[group].enabled;
objGroup[F("name")] = String(mConfig->plugin.zeroExport.groups[group].name); objGroup[F("name")] = String(mConfig->plugin.zeroExport.groups[group].name);
// Powermeter // Powermeter
objGroup[F("pm_refresh")] = (uint8_t)mConfig->plugin.zeroExport.groups[group].pm_refresh;
objGroup[F("pm_type")] = (uint8_t)mConfig->plugin.zeroExport.groups[group].pm_type; objGroup[F("pm_type")] = (uint8_t)mConfig->plugin.zeroExport.groups[group].pm_type;
objGroup[F("pm_url")] = String(mConfig->plugin.zeroExport.groups[group].pm_url); objGroup[F("pm_url")] = String(mConfig->plugin.zeroExport.groups[group].pm_url);
objGroup[F("pm_jsonPath")] = String(mConfig->plugin.zeroExport.groups[group].pm_jsonPath); objGroup[F("pm_jsonPath")] = String(mConfig->plugin.zeroExport.groups[group].pm_jsonPath);
objGroup[F("pm_user")] = String(mConfig->plugin.zeroExport.groups[group].pm_user); objGroup[F("pm_user")] = String(mConfig->plugin.zeroExport.groups[group].pm_user);
objGroup[F("pm_pass")] = String(mConfig->plugin.zeroExport.groups[group].pm_pass); objGroup[F("pm_pass")] = String(mConfig->plugin.zeroExport.groups[group].pm_pass);
objGroup[F("pm_target")] = (uint8_t)mConfig->plugin.zeroExport.groups[group].pm_target;
// Inverters // Inverters
objGroup[F("max_inverters")] = ZEROEXPORT_GROUP_MAX_INVERTERS; objGroup[F("max_inverters")] = ZEROEXPORT_GROUP_MAX_INVERTERS;
JsonArray arrInv = objGroup.createNestedArray(F("inverters")); JsonArray arrInv = objGroup.createNestedArray(F("inverters"));
@ -849,17 +851,18 @@ class RestApi {
JsonObject objGroupInv = arrInv.createNestedObject(); JsonObject objGroupInv = arrInv.createNestedObject();
objGroupInv[F("enabled")] = (bool)mConfig->plugin.zeroExport.groups[group].inverters[inv].enabled; objGroupInv[F("enabled")] = (bool)mConfig->plugin.zeroExport.groups[group].inverters[inv].enabled;
objGroupInv[F("id")] = (int8_t)mConfig->plugin.zeroExport.groups[group].inverters[inv].id; objGroupInv[F("id")] = (int8_t)mConfig->plugin.zeroExport.groups[group].inverters[inv].id;
objGroupInv[F("target")] = (int8_t)mConfig->plugin.zeroExport.groups[group].inverters[inv].target;
objGroupInv[F("powerMin")] = (uint16_t)mConfig->plugin.zeroExport.groups[group].inverters[inv].powerMin; objGroupInv[F("powerMin")] = (uint16_t)mConfig->plugin.zeroExport.groups[group].inverters[inv].powerMin;
objGroupInv[F("powerMax")] = (uint16_t)mConfig->plugin.zeroExport.groups[group].inverters[inv].powerMax; objGroupInv[F("powerMax")] = (uint16_t)mConfig->plugin.zeroExport.groups[group].inverters[inv].powerMax;
objGroupInv[F("turnOff")] = (uint16_t)mConfig->plugin.zeroExport.groups[group].inverters[inv].turnOff;
} }
// Battery // Battery
objGroup[F("battEnabled")] = (bool)mConfig->plugin.zeroExport.groups[group].battEnabled; objGroup[F("battCfg")] = (uint8_t)mConfig->plugin.zeroExport.groups[group].battCfg;
objGroup[F("battVoltageOn")] = ah::round1((float)mConfig->plugin.zeroExport.groups[group].battVoltageOn); objGroup[F("battTopic")] = String(mConfig->plugin.zeroExport.groups[group].battTopic);
objGroup[F("battVoltageOff")] = ah::round1((float)mConfig->plugin.zeroExport.groups[group].battVoltageOff); objGroup[F("battLimitOn")] = ah::round1((float)mConfig->plugin.zeroExport.groups[group].battLimitOn);
objGroup[F("battLimitOff")] = ah::round1((float)mConfig->plugin.zeroExport.groups[group].battLimitOff);
// Advanced // Advanced
objGroup[F("setPoint")] = (int16_t)mConfig->plugin.zeroExport.groups[group].setPoint; objGroup[F("setPoint")] = (int16_t)mConfig->plugin.zeroExport.groups[group].setPoint;
objGroup[F("refresh")] = (uint8_t)mConfig->plugin.zeroExport.groups[group].refresh; objGroup[F("minimum")] = (bool)mConfig->plugin.zeroExport.groups[group].minimum;
objGroup[F("power")] = (int32_t)mConfig->plugin.zeroExport.groups[group].power; objGroup[F("power")] = (int32_t)mConfig->plugin.zeroExport.groups[group].power;
objGroup[F("powerTolerance")] = (uint8_t)mConfig->plugin.zeroExport.groups[group].powerTolerance; objGroup[F("powerTolerance")] = (uint8_t)mConfig->plugin.zeroExport.groups[group].powerTolerance;
objGroup[F("powerMax")] = (uint16_t)mConfig->plugin.zeroExport.groups[group].powerMax; objGroup[F("powerMax")] = (uint16_t)mConfig->plugin.zeroExport.groups[group].powerMax;
@ -1154,33 +1157,35 @@ class RestApi {
mConfig->plugin.zeroExport.groups[group].enabled = jsonIn[F("enabled")]; mConfig->plugin.zeroExport.groups[group].enabled = jsonIn[F("enabled")];
snprintf(mConfig->plugin.zeroExport.groups[group].name, ZEROEXPORT_GROUP_MAX_LEN_NAME, "%s", jsonIn[F("name")].as<const char*>()); snprintf(mConfig->plugin.zeroExport.groups[group].name, ZEROEXPORT_GROUP_MAX_LEN_NAME, "%s", jsonIn[F("name")].as<const char*>());
// Powermeter // Powermeter
mConfig->plugin.zeroExport.groups[group].pm_refresh = jsonIn[F("pm_refresh")];
mConfig->plugin.zeroExport.groups[group].pm_type = jsonIn[F("pm_type")]; mConfig->plugin.zeroExport.groups[group].pm_type = jsonIn[F("pm_type")];
snprintf(mConfig->plugin.zeroExport.groups[group].pm_url, ZEROEXPORT_GROUP_MAX_LEN_PM_URL, "%s", jsonIn[F("pm_url")].as<const char*>()); snprintf(mConfig->plugin.zeroExport.groups[group].pm_url, ZEROEXPORT_GROUP_MAX_LEN_PM_URL, "%s", jsonIn[F("pm_url")].as<const char*>());
snprintf(mConfig->plugin.zeroExport.groups[group].pm_jsonPath, ZEROEXPORT_GROUP_MAX_LEN_PM_JSONPATH, "%s", jsonIn[F("pm_jsonPath")].as<const char*>()); snprintf(mConfig->plugin.zeroExport.groups[group].pm_jsonPath, ZEROEXPORT_GROUP_MAX_LEN_PM_JSONPATH, "%s", jsonIn[F("pm_jsonPath")].as<const char*>());
snprintf(mConfig->plugin.zeroExport.groups[group].pm_user, ZEROEXPORT_GROUP_MAX_LEN_PM_USER, "%s", jsonIn[F("pm_user")].as<const char*>()); snprintf(mConfig->plugin.zeroExport.groups[group].pm_user, ZEROEXPORT_GROUP_MAX_LEN_PM_USER, "%s", jsonIn[F("pm_user")].as<const char*>());
snprintf(mConfig->plugin.zeroExport.groups[group].pm_pass, ZEROEXPORT_GROUP_MAX_LEN_PM_PASS, "%s", jsonIn[F("pm_pass")].as<const char*>()); snprintf(mConfig->plugin.zeroExport.groups[group].pm_pass, ZEROEXPORT_GROUP_MAX_LEN_PM_PASS, "%s", jsonIn[F("pm_pass")].as<const char*>());
mConfig->plugin.zeroExport.groups[group].pm_target = jsonIn[F("pm_target")];
// Inverters // Inverters
for(uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { for(uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) {
mConfig->plugin.zeroExport.groups[group].inverters[inv].enabled = jsonIn[F("inverters")][inv][F("enabled")]; mConfig->plugin.zeroExport.groups[group].inverters[inv].enabled = jsonIn[F("inverters")][inv][F("enabled")];
mConfig->plugin.zeroExport.groups[group].inverters[inv].id = jsonIn[F("inverters")][inv][F("id")]; mConfig->plugin.zeroExport.groups[group].inverters[inv].id = jsonIn[F("inverters")][inv][F("id")];
mConfig->plugin.zeroExport.groups[group].inverters[inv].target = jsonIn[F("inverters")][inv][F("target")];
mConfig->plugin.zeroExport.groups[group].inverters[inv].powerMin = jsonIn[F("inverters")][inv][F("powerMin")]; mConfig->plugin.zeroExport.groups[group].inverters[inv].powerMin = jsonIn[F("inverters")][inv][F("powerMin")];
mConfig->plugin.zeroExport.groups[group].inverters[inv].powerMax = jsonIn[F("inverters")][inv][F("powerMax")]; mConfig->plugin.zeroExport.groups[group].inverters[inv].powerMax = jsonIn[F("inverters")][inv][F("powerMax")];
mConfig->plugin.zeroExport.groups[group].inverters[inv].turnOff = jsonIn[F("inverters")][inv][F("turnOff")];
} }
// Battery // Battery
mConfig->plugin.zeroExport.groups[group].battEnabled = jsonIn[F("battEnabled")]; mConfig->plugin.zeroExport.groups[group].battCfg = jsonIn[F("battCfg")];
mConfig->plugin.zeroExport.groups[group].battVoltageOn = jsonIn[F("battVoltageOn")]; snprintf(mConfig->plugin.zeroExport.groups[group].battTopic, ZEROEXPORT_GROUP_MAX_LEN_BATT_TOPIC, "%s", jsonIn[F("battTopic")].as<const char*>());
mConfig->plugin.zeroExport.groups[group].battVoltageOff = jsonIn[F("battVoltageOff")]; mConfig->plugin.zeroExport.groups[group].battLimitOn = jsonIn[F("battLimitOn")];
mConfig->plugin.zeroExport.groups[group].battLimitOff = jsonIn[F("battLimitOff")];
// Advanced // Advanced
mConfig->plugin.zeroExport.groups[group].setPoint = jsonIn[F("setPoint")]; mConfig->plugin.zeroExport.groups[group].setPoint = jsonIn[F("setPoint")];
mConfig->plugin.zeroExport.groups[group].refresh = jsonIn[F("refresh")]; mConfig->plugin.zeroExport.groups[group].minimum = jsonIn[F("minimum")];
mConfig->plugin.zeroExport.groups[group].powerTolerance = jsonIn[F("powerTolerance")]; mConfig->plugin.zeroExport.groups[group].powerTolerance = jsonIn[F("powerTolerance")];
mConfig->plugin.zeroExport.groups[group].powerMax = jsonIn[F("powerMax")]; mConfig->plugin.zeroExport.groups[group].powerMax = jsonIn[F("powerMax")];
mConfig->plugin.zeroExport.groups[group].Kp = jsonIn[F("Kp")]; mConfig->plugin.zeroExport.groups[group].Kp = jsonIn[F("Kp")];
mConfig->plugin.zeroExport.groups[group].Ki = jsonIn[F("Ki")]; mConfig->plugin.zeroExport.groups[group].Ki = jsonIn[F("Ki")];
mConfig->plugin.zeroExport.groups[group].Kd = jsonIn[F("Kd")]; mConfig->plugin.zeroExport.groups[group].Kd = jsonIn[F("Kd")];
// Global // Global
mConfig->plugin.zeroExport.groups[group].state = zeroExportState::INIT;
mApp->saveSettings(false); // without reboot mApp->saveSettings(false); // without reboot
} }
#endif #endif

167
src/web/html/setup.html

@ -1288,6 +1288,7 @@
} }
/*IF_PLUGIN_ZEROEXPORT*/ /*IF_PLUGIN_ZEROEXPORT*/
function apiCbBattOnOff(obj) { function apiCbBattOnOff(obj) {
// var e = document.getElementById("battSwitch"); // var e = document.getElementById("battSwitch");
// e.value = "88"; // e.value = "88";
@ -1321,9 +1322,9 @@
ml("th", {style: "width: 10%;"}, ml("input", {name: "invMax", id: "invMax", type: "hidden", value: maxInv}, null)), ml("th", {style: "width: 10%;"}, ml("input", {name: "invMax", id: "invMax", type: "hidden", value: maxInv}, null)),
ml("th", {style: "width: 10%;"}, "{#ZE_GROUP_TAB_INVERTER_ENABLED}"), ml("th", {style: "width: 10%;"}, "{#ZE_GROUP_TAB_INVERTER_ENABLED}"),
ml("th", {}, "{#ZE_GROUP_TAB_INVERTER_NAME}"), ml("th", {}, "{#ZE_GROUP_TAB_INVERTER_NAME}"),
ml("th", {}, "{#ZE_GROUP_TAB_INVERTER_SUM}"),
ml("th", {style: "width: 15%;"}, "{#ZE_GROUP_TAB_INVERTER_POWERMIN}"), ml("th", {style: "width: 15%;"}, "{#ZE_GROUP_TAB_INVERTER_POWERMIN}"),
ml("th", {style: "width: 15%;"}, "{#ZE_GROUP_TAB_INVERTER_POWERMAX}"), ml("th", {style: "width: 15%;"}, "{#ZE_GROUP_TAB_INVERTER_POWERMAX}"),
ml("th", {style: "width: 5%;"}, "{#ZE_GROUP_TAB_INVERTER_TURNOFF}"),
])); ]));
for(var inv = 0; inv < maxInv; inv++) { for(var inv = 0; inv < maxInv; inv++) {
@ -1331,7 +1332,7 @@
ml("td", {}, String(inv)), ml("td", {}, String(inv)),
ml("td", {}, ml("td", {},
ml("div", {}, [ ml("div", {}, [
ml("input", {name: "invEnabled"+inv, class: "text", id: "invEnabled"+inv, type: "checkbox"}, null) ml("input", {name: "invEnabled"+inv, class: "text", id: "invEnabled"+inv, type: "checkbox"}, null)
]), ]),
), ),
ml("td", {}, ml("td", {},
@ -1341,27 +1342,27 @@
), ),
ml("td", {}, ml("td", {},
ml("div", {}, [ ml("div", {}, [
ml("select", {name: "invTarget"+inv, class: "text", id: "invTarget"+inv}, null), ml("input", {name: "invPowerMin"+inv, class: "text", id: "invPowerMin"+inv, type: "number", min: "0", max: "65535"}, null)
]), ]),
), ),
ml("td", {}, ml("td", {},
ml("div", {}, [ ml("div", {}, [
ml("input", {name: "invPowerMin"+inv, class: "text", id: "invPowerMin"+inv, type: "number", min: "0", max: "65535"}, null) ml("input", {name: "invPowerMax"+inv, class: "text", id: "invPowerMax"+inv, type: "number", min: "0", max: "65535"}, null)
]), ]),
), ),
ml("td", {}, ml("td", {},
ml("div", {}, [ ml("div", {}, [
ml("input", {name: "invPowerMax"+inv, class: "text", id: "invPowerMax"+inv, type: "number", min: "0", max: "65535"}, null) ml("input", {name: "invTurnOff"+inv, class: "text", id: "invTurnOff"+inv, type: "checkbox"}, null)
]), ]),
), ),
])); ]));
} }
// Tab_Battery // Tab_Battery
var cb_battEnabled = ml("input", {name: "battEnabled", type: "checkbox"}, null);
cb_battEnabled.checked = (obj.battEnabled);
// Tab_Advanced // Tab_Advanced
var cb_minimum = ml("input", {name: "minimum", type: "checkbox"}, null);
cb_minimum.checked = (obj.minimum);
// Tab // Tab
var html = ml("div", {}, [ var html = ml("div", {}, [
@ -1385,7 +1386,13 @@
divRow("{#ZE_GROUP_TAB_POWERMETER_TYPE}", divRow("{#ZE_GROUP_TAB_POWERMETER_TYPE}",
ml("select", {name: "pm_type", class: "text", id: "pm_type"}, null), ml("select", {name: "pm_type", class: "text", id: "pm_type"}, null),
), ),
divRow("{#ZE_GROUP_TAB_POWERMETER_REFRESH}",
ml("input", {name: "pm_refresh", class: "text", type: "number", min: "1", max: "30", step: "1", value: obj.pm_refresh}, null),
),
// TODO: URL -> IP // TODO: URL -> IP
divRow("{#ZE_GROUP_TAB_POWERMETER_TARGET}",
ml("select", {name: "pm_target", class: "text", id: "pm_target"}, null),
),
divRow("{#ZE_GROUP_TAB_POWERMETER_IP}", [ divRow("{#ZE_GROUP_TAB_POWERMETER_IP}", [
ml("input", {name: "pm_url", class: "text", type: "text", value: obj.pm_url, maxlength: "100"}, null), ml("input", {name: "pm_url", class: "text", type: "text", value: obj.pm_url, maxlength: "100"}, null),
]), ]),
@ -1398,6 +1405,9 @@
divRow("{#ZE_GROUP_TAB_POWERMETER_PASS}", divRow("{#ZE_GROUP_TAB_POWERMETER_PASS}",
ml("input", {name: "pm_pass", class: "text", type: "password", value: "****"}, null), ml("input", {name: "pm_pass", class: "text", type: "password", value: "****"}, null),
), ),
divRow("{#ZE_GROUP_TAB_POWERMETER_TOPIC}",
ml("input", {id: 3, name: "pm_topic", class: "text", type: "text", value: ""}, null),
),
// TODO: Uebersetzen mit lang.json und auf die entsprechende Dokuseite verlinken // TODO: Uebersetzen mit lang.json und auf die entsprechende Dokuseite verlinken
divRow("Hinweis: ", divRow("Hinweis: ",
ml("a", {href: "https://docs.ahoydtu.de/de/latest/zeroExport.html"}, "Bitte beachten Sie die Ausf&uuml;llhinweise in der Dokumentation."), ml("a", {href: "https://docs.ahoydtu.de/de/latest/zeroExport.html"}, "Bitte beachten Sie die Ausf&uuml;llhinweise in der Dokumentation."),
@ -1421,9 +1431,18 @@
]), ]),
// Battery // Battery
ml("div", {id: "div{#ZE_GROUP_TAB_BATTERY}", class: "tab-content hide"}, [ ml("div", {id: "div{#ZE_GROUP_TAB_BATTERY}", class: "tab-content hide"}, [
divRow("{#ZE_GROUP_TAB_BATTERY_BATTENABLED}", cb_battEnabled), divRow("{#ZE_GROUP_TAB_BATTERY_CFG}",
divRow("{#ZE_GROUP_TAB_BATTERY_BATTVOLTAGEON}", ml("input", {name: "battVoltageOn", class: "text", type: "number", min: "0", max: "100", step: "0.1", value: obj.battVoltageOn}, null)), ml("select", {name: "battCfg", class: "text", id: "battCfg"}, null),
divRow("{#ZE_GROUP_TAB_BATTERY_BATTVOLTAGEOFF}", ml("input", {name: "battVoltageOff", class: "text", type: "number", min: "0", max: "100", step: "0.1", value: obj.battVoltageOff}, null)), ),
divRow("{#ZE_GROUP_TAB_BATTERY_TOPIC}",
ml("input", {name: "battTopic", class: "text", type: "text", value: obj.battTopic}, null),
),
divRow("{#ZE_GROUP_TAB_BATTERY_LIMITON}",
ml("input", {name: "battLimitOn", class: "text", type: "number", min: "0", max: "100", step: "0.1", value: obj.battLimitOn}, null),
),
divRow("{#ZE_GROUP_TAB_BATTERY_LIMITOFF}",
ml("input", {name: "battLimitOff", class: "text", type: "number", min: "0", max: "100", step: "0.1", value: obj.battLimitOff}, null),
),
divRow("{#ZE_GROUP_TAB_BATTERY_ONOFF}", ml("input", {name: "battSwitch", id: "battSwitch", class: "btn", type: "button", value: "{#BTN_ONOFF}", onclick: battOnOff()}, null)), divRow("{#ZE_GROUP_TAB_BATTERY_ONOFF}", ml("input", {name: "battSwitch", id: "battSwitch", class: "btn", type: "button", value: "{#BTN_ONOFF}", onclick: battOnOff()}, null)),
// TODO: Uebersetzen mit lang.json und auf die entsprechende Dokuseite verlinken // TODO: Uebersetzen mit lang.json und auf die entsprechende Dokuseite verlinken
divRow("Hinweis: ", divRow("Hinweis: ",
@ -1437,7 +1456,7 @@
// Advanced // Advanced
ml("div", {id: "div{#ZE_GROUP_TAB_ADVANCED}", class: "tab-content hide"}, [ ml("div", {id: "div{#ZE_GROUP_TAB_ADVANCED}", class: "tab-content hide"}, [
divRow("{#ZE_GROUP_TAB_ADVANCED_SETPOINT}", ml("input", {name: "setPoint", class: "text", type: "number", min: "-32768", max: "32767", step: "1", value: obj.setPoint}, null)), divRow("{#ZE_GROUP_TAB_ADVANCED_SETPOINT}", ml("input", {name: "setPoint", class: "text", type: "number", min: "-32768", max: "32767", step: "1", value: obj.setPoint}, null)),
divRow("{#ZE_GROUP_TAB_ADVANCED_REFRESH}", ml("input", {name: "refresh", class: "text", type: "number", min: "0", max: "255", value: obj.refresh}, null)), divRow("{#ZE_GROUP_TAB_ADVANCED_MINIMUM}", cb_minimum),
divRow("{#ZE_GROUP_TAB_ADVANCED_POWERTOLERANCE}", ml("input", {name: "powerTolerance", class: "text", type: "number", min: "0", max: "255", value: obj.powerTolerance}, null)), divRow("{#ZE_GROUP_TAB_ADVANCED_POWERTOLERANCE}", ml("input", {name: "powerTolerance", class: "text", type: "number", min: "0", max: "255", value: obj.powerTolerance}, null)),
divRow("{#ZE_GROUP_TAB_ADVANCED_POWERMAX}", ml("input", {name: "powerMax", class: "text", type: "number", min: "0", max: "65535", value: obj.powerMax}, null)), divRow("{#ZE_GROUP_TAB_ADVANCED_POWERMAX}", ml("input", {name: "powerMax", class: "text", type: "number", min: "0", max: "65535", value: obj.powerMax}, null)),
divRow("{#ZE_GROUP_TAB_ADVANCED_KP}", ml("input", {name: "Kp", class: "text", type: "number", min: "-1", max: "0", step: "0.001", value: obj.Kp}, null)), divRow("{#ZE_GROUP_TAB_ADVANCED_KP}", ml("input", {name: "Kp", class: "text", type: "number", min: "-1", max: "0", step: "0.001", value: obj.Kp}, null)),
@ -1462,10 +1481,11 @@
modal("{#ZE_GROUP_EDIT_MODAL}: " + obj.id, html); modal("{#ZE_GROUP_EDIT_MODAL}: " + obj.id, html);
// ser.dispatchEvent(new Event('change')); // ser.dispatchEvent(new Event('change'));
// Tab_Powermeter
// - pm_type
// Inhalt fuer pm_type aus config laden und in eine Funktion ausgliedern // Inhalt fuer pm_type aus config laden und in eine Funktion ausgliedern
var e = document.getElementById("pm_type"); var e = document.getElementById("pm_type");
selDelAllOpt(e); selDelAllOpt(e);
// TODO: uebersetzen?
e.appendChild(opt("0", "---")); e.appendChild(opt("0", "---"));
e.appendChild(opt("1", "Shelly")); e.appendChild(opt("1", "Shelly"));
//e.appendChild(opt("2", "Tasmota")); //e.appendChild(opt("2", "Tasmota"));
@ -1479,6 +1499,27 @@
} }
} }
// - pm_target
var e = document.getElementById("pm_target");
selDelAllOpt(e);
e.appendChild(opt("0", "Sum"));
e.appendChild(opt("1", "L1"));
e.appendChild(opt("2", "L2"));
e.appendChild(opt("3", "L3"));
for (var i = 0; i < e.options.length; i++) {
if (e.options[i].value == obj.pm_target) {
e.selectedIndex = i;
}
}
// add addEventListener
const selectElement = document.querySelector("#pm_type");
//selectElement.addEventListener("change", (event) => { pm_type_dropdown() });
selectElement.addEventListener("change", (event) => { pm_type_dropdown() });
// run event one time
pm_type_dropdown();
// Tab_Inverters // Tab_Inverters
// - Enabled // - Enabled
for (var inv = 0; inv < maxInv; inv++) { for (var inv = 0; inv < maxInv; inv++) {
@ -1499,25 +1540,6 @@
} }
} }
} }
// - Target
for (var inv = 0; inv < maxInv; inv++) {
var e = document.getElementById("invTarget"+inv);
selDelAllOpt(e);
// TODO: ?bersetzen?
e.appendChild(opt("-1", "---"));
e.appendChild(opt("0", "Sum"));
e.appendChild(opt("1", "L1"));
e.appendChild(opt("2", "L2"));
e.appendChild(opt("3", "L3"));
e.appendChild(opt("4", "L1 + Sum"));
e.appendChild(opt("5", "L2 + Sum"));
e.appendChild(opt("6", "L3 + Sum"));
for (var i = 0; i < e.options.length; i++) {
if (e.options[i].value == obj.inverters[inv].target) {
e.selectedIndex = i;
}
}
}
// - powerMin // - powerMin
for (var inv = 0; inv < maxInv; inv++) { for (var inv = 0; inv < maxInv; inv++) {
var e = document.getElementById("invPowerMin"+inv); var e = document.getElementById("invPowerMin"+inv);
@ -1528,6 +1550,53 @@
var e = document.getElementById("invPowerMax"+inv); var e = document.getElementById("invPowerMax"+inv);
e.value = (obj.inverters[inv].powerMax); e.value = (obj.inverters[inv].powerMax);
} }
// - TurnOff
for (var inv = 0; inv < maxInv; inv++) {
var e = document.getElementById("invTurnOff"+inv);
e.checked = (obj.inverters[inv].turnOff);
}
// Tab_Battery
// battCfg
var e = document.getElementById("battCfg");
selDelAllOpt(e);
// TODO: uebersetzen?
e.appendChild(opt("0", "---"));
e.appendChild(opt("1", "Inverter U dc"));
e.appendChild(opt("2", "MQTT U"));
e.appendChild(opt("3", "MQTT Soc"));
for (var i = 0; i < e.options.length; i++) {
if (e.options[i].value == obj.battCfg) {
e.selectedIndex = i;
}
}
function pm_type_dropdown()
{
var e = document.getElementsByName("pm_type")[0];
var value = e.options[e.selectedIndex].text;
var divsToHide = document.getElementById("divPowermeter");
// Formular for Powermeter-DropDown
// show all DIVs and remove only what is not necessary
for(var i = 0; i < divsToHide.childElementCount; i++) divsToHide.childNodes[i].style.display = '';
if(value == "---") for(var i = 1; i < divsToHide.childElementCount; i++) divsToHide.childNodes[i].style.display = 'none';
else if(value == "Shelly") {
divsToHide.childNodes[7].style.display = 'none';
}
else if(value == "Mqtt") {
divsToHide.childNodes[3].style.display = 'none';
divsToHide.childNodes[4].style.display = 'none';
divsToHide.childNodes[5].style.display = 'none';
divsToHide.childNodes[6].style.display = 'none';
}
else if(value == "Tibber") {
divsToHide.childNodes[4].style.display = 'none';
divsToHide.childNodes[7].style.display = 'none';
}
}
function save() { function save() {
var o = new Object(); var o = new Object();
@ -1537,14 +1606,17 @@
o.id = obj.id o.id = obj.id
o.enabled = document.getElementsByName("enabled")[0].checked; o.enabled = document.getElementsByName("enabled")[0].checked;
o.name = document.getElementsByName("name")[0].value; o.name = document.getElementsByName("name")[0].value;
// Powermeter // Powermeter
//o.pm_type = document.getElementsByName("pm_type")[0].selectedIndex; o.pm_refresh = document.getElementsByName("pm_refresh")[0].value;
var e = document.getElementsByName("pm_type")[0]; var e = document.getElementsByName("pm_type")[0];
o.pm_type = e.options[e.selectedIndex].value; o.pm_type = e.options[e.selectedIndex].value;
o.pm_url = document.getElementsByName("pm_url")[0].value; o.pm_url = document.getElementsByName("pm_url")[0].value;
o.pm_jsonPath = document.getElementsByName("pm_jsonPath")[0].value; o.pm_jsonPath = document.getElementsByName("pm_jsonPath")[0].value;
o.pm_user = document.getElementsByName("pm_user")[0].value; o.pm_user = document.getElementsByName("pm_user")[0].value;
o.pm_pass = document.getElementsByName("pm_pass")[0].value; o.pm_pass = document.getElementsByName("pm_pass")[0].value;
var e = document.getElementsByName("pm_target")[0];
o.pm_target = e.options[e.selectedIndex].value;
// Inverters // Inverters
o.invMax = document.getElementById("invMax").value; o.invMax = document.getElementById("invMax").value;
o.inverters = []; o.inverters = [];
@ -1553,19 +1625,20 @@
q.enabled = document.getElementById("invEnabled"+inv).checked; q.enabled = document.getElementById("invEnabled"+inv).checked;
var e = document.getElementById("invId"+inv); var e = document.getElementById("invId"+inv);
q.id = e.options[e.selectedIndex].value; q.id = e.options[e.selectedIndex].value;
var e = document.getElementById("invTarget"+inv);
q.target = e.options[e.selectedIndex].value;
q.powerMin = document.getElementById("invPowerMin"+inv).value; q.powerMin = document.getElementById("invPowerMin"+inv).value;
q.powerMax = document.getElementById("invPowerMax"+inv).value; q.powerMax = document.getElementById("invPowerMax"+inv).value;
q.turnOff = document.getElementById("invTurnOff"+inv).checked;
o.inverters.push(q); o.inverters.push(q);
} }
// Battery // Battery
o.battEnabled = document.getElementsByName("battEnabled")[0].checked; var e = document.getElementsByName("battCfg")[0];
o.battVoltageOn = document.getElementsByName("battVoltageOn")[0].value; o.battCfg = e.options[e.selectedIndex].value;
o.battVoltageOff = document.getElementsByName("battVoltageOff")[0].value; o.battTopic = document.getElementsByName("battTopic")[0].value;
o.battLimitOn = document.getElementsByName("battLimitOn")[0].value;
o.battLimitOff = document.getElementsByName("battLimitOff")[0].value;
// Advanced // Advanced
o.setPoint = document.getElementsByName("setPoint")[0].value; o.setPoint = document.getElementsByName("setPoint")[0].value;
o.refresh = document.getElementsByName("refresh")[0].value; o.minimum = document.getElementsByName("minimum")[0].checked;
o.powerTolerance = document.getElementsByName("powerTolerance")[0].value; o.powerTolerance = document.getElementsByName("powerTolerance")[0].value;
o.powerMax = document.getElementsByName("powerMax")[0].value; o.powerMax = document.getElementsByName("powerMax")[0].value;
o.Kp = document.getElementsByName("Kp")[0].value; o.Kp = document.getElementsByName("Kp")[0].value;
@ -1603,11 +1676,13 @@
o.enabled = false; o.enabled = false;
o.name = ""; o.name = "";
// Powermeter // Powermeter
o.pm_refresh = 5;
o.pm_type = 0; o.pm_type = 0;
o.pm_url = ""; o.pm_url = "";
o.pm_jsonPath = ""; o.pm_jsonPath = "";
o.pm_user = ""; o.pm_user = "";
o.pm_pass = ""; o.pm_pass = "";
o.pm_target = 0;
// Inverters // Inverters
o.invMax = obj.inverters.length; o.invMax = obj.inverters.length;
o.inverters = []; o.inverters = [];
@ -1616,19 +1691,19 @@
q.enabled = false; q.enabled = false;
var e = document.getElementById("invId"+inv); var e = document.getElementById("invId"+inv);
q.id = -1; q.id = -1;
var e = document.getElementById("invTarget"+inv);
q.target = -1;
q.powerMin = 0; q.powerMin = 0;
q.powerMax = 0; q.powerMax = 0;
q.turnOff = false;
o.inverters.push(q); o.inverters.push(q);
} }
// Battery // Battery
o.battEnabled = false; o.battCfg = 0;
o.battVoltageOn = 0; o.battTopic = "";
o.battVoltageOff = 0; o.battLimitOn = 0;
o.battLimitOff = 0;
// Advanced // Advanced
o.setPoint = 0; o.setPoint = 0;
o.refresh = 10; o.minimum = true;
o.power = 0; o.power = 0;
o.powerTolerance = 10; o.powerTolerance = 10;
o.powerMax = 600; o.powerMax = 600;
@ -1710,6 +1785,10 @@
var e = document.getElementById("ze_groups"); var e = document.getElementById("ze_groups");
e.innerHTML = ""; // remove all childs e.innerHTML = ""; // remove all childs
e.append(ml("table", {class: "table"}, ml("tbody", {}, lines))); e.append(ml("table", {class: "table"}, ml("tbody", {}, lines)));
// TODO: Hinweis Developmentversion - Entfernen wenn erledigt
e.append(
divRow("ACHTUNG:", "Dies ist eine sehr junge Developmentversion, bitte nicht unbeaufsichtig laufen lassen."),
);
// TODO: Uebersetzen mit lang.json und auf die entsprechende Dokuseite verlinken // TODO: Uebersetzen mit lang.json und auf die entsprechende Dokuseite verlinken
e.append( e.append(
divRow("Hinweis: ", divRow("Hinweis: ",

54
src/web/lang.json

@ -883,6 +883,11 @@
"en": "Powermeter", "en": "Powermeter",
"de": "Leistungsmessung" "de": "Leistungsmessung"
}, },
{
"token": "ZE_GROUP_TAB_POWERMETER_REFRESH",
"en": "Refresh (s):",
"de": "Zykluszeit (s):"
},
{ {
"token": "ZE_GROUP_TAB_POWERMETER_TYPE", "token": "ZE_GROUP_TAB_POWERMETER_TYPE",
"en": "Type:", "en": "Type:",
@ -908,6 +913,16 @@
"en": "Password:", "en": "Password:",
"de": "Passwort:" "de": "Passwort:"
}, },
{
"token": "ZE_GROUP_TAB_POWERMETER_TOPIC",
"en": "Topic:",
"de": "Topic:"
},
{
"token": "ZE_GROUP_TAB_POWERMETER_TARGET",
"en": "Target:",
"de": "Ziel:"
},
{ {
"token": "ZE_GROUP_TAB_INVERTER", "token": "ZE_GROUP_TAB_INVERTER",
"en": "Inverter", "en": "Inverter",
@ -923,11 +938,6 @@
"en": "Name", "en": "Name",
"de": "Name" "de": "Name"
}, },
{
"token": "ZE_GROUP_TAB_INVERTER_SUM",
"en": "Sum",
"de": "Gesamt"
},
{ {
"token": "ZE_GROUP_TAB_INVERTER_POWERMIN", "token": "ZE_GROUP_TAB_INVERTER_POWERMIN",
"en": "Power (Min)", "en": "Power (Min)",
@ -938,25 +948,35 @@
"en": "Power (Max)", "en": "Power (Max)",
"de": "Leistung (Max)" "de": "Leistung (Max)"
}, },
{
"token": "ZE_GROUP_TAB_INVERTER_TURNOFF",
"en": "Turn On/Off",
"de": "Schalte Ein/Aus"
},
{ {
"token": "ZE_GROUP_TAB_BATTERY", "token": "ZE_GROUP_TAB_BATTERY",
"en": "Battery", "en": "Battery",
"de": "Batterie" "de": "Batterie"
}, },
{ {
"token": "ZE_GROUP_TAB_BATTERY_BATTENABLED", "token": "ZE_GROUP_TAB_BATTERY_CFG",
"en": "Enabled:", "en": "Mode:",
"de": "Aktiviert:" "de": "Modus:"
},
{
"token": "ZE_GROUP_TAB_BATTERY_TOPIC",
"en": "Topic:",
"de": "Topic:"
}, },
{ {
"token": "ZE_GROUP_TAB_BATTERY_BATTVOLTAGEON", "token": "ZE_GROUP_TAB_BATTERY_LIMITON",
"en": "Voltage on (Volt):", "en": "Limit On:",
"de": "Spannung Ein (Volt):" "de": "Limit Ein:"
}, },
{ {
"token": "ZE_GROUP_TAB_BATTERY_BATTVOLTAGEOFF", "token": "ZE_GROUP_TAB_BATTERY_LIMITOFF",
"en": "Voltage off (Volt):", "en": "Limit Off:",
"de": "Spannung Aus (Volt):" "de": "Limit Aus:"
}, },
{ {
"token": "ZE_GROUP_TAB_BATTERY_ONOFF", "token": "ZE_GROUP_TAB_BATTERY_ONOFF",
@ -979,9 +999,9 @@
"de": "Zielpunkt (Watt):" "de": "Zielpunkt (Watt):"
}, },
{ {
"token": "ZE_GROUP_TAB_ADVANCED_REFRESH", "token": "ZE_GROUP_TAB_ADVANCED_MINIMUM",
"en": "Refresh rate (sec):", "en": "Minimum:",
"de": "Aktualisierung (sec):" "de": "Minimum (Grundlast):"
}, },
{ {
"token": "ZE_GROUP_TAB_ADVANCED_POWERTOLERANCE", "token": "ZE_GROUP_TAB_ADVANCED_POWERTOLERANCE",

Loading…
Cancel
Save