|
|
|
@ -56,7 +56,7 @@ const char kEnergyCommands[] PROGMEM = "|" // No prefix
|
|
|
|
|
D_CMND_SAFEPOWER "|" D_CMND_SAFEPOWERHOLD "|" D_CMND_SAFEPOWERWINDOW "|"
|
|
|
|
|
#endif // USE_ENERGY_POWER_LIMIT
|
|
|
|
|
#endif // USE_ENERGY_MARGIN_DETECTION
|
|
|
|
|
D_CMND_ENERGYRESET "|" D_CMND_TARIFF;
|
|
|
|
|
D_CMND_ENERGYRESET "|" D_CMND_ENERGYTODAY "|" D_CMND_ENERGYYESTERDAY "|" D_CMND_ENERGYTOTAL "|" D_CMND_TARIFF;
|
|
|
|
|
|
|
|
|
|
void (* const EnergyCommand[])(void) PROGMEM = {
|
|
|
|
|
&CmndPowerCal, &CmndVoltageCal, &CmndCurrentCal, &CmndFrequencyCal,
|
|
|
|
@ -69,9 +69,9 @@ void (* const EnergyCommand[])(void) PROGMEM = {
|
|
|
|
|
&CmndSafePower, &CmndSafePowerHold, &CmndSafePowerWindow,
|
|
|
|
|
#endif // USE_ENERGY_POWER_LIMIT
|
|
|
|
|
#endif // USE_ENERGY_MARGIN_DETECTION
|
|
|
|
|
&CmndEnergyReset, &CmndTariff};
|
|
|
|
|
&CmndEnergyReset, &CmndEnergyToday, &CmndEnergyYesterday, &CmndEnergyTotal, &CmndTariff};
|
|
|
|
|
|
|
|
|
|
const char kEnergyPhases[] PROGMEM = "|%s / %s|%s / %s / %s||[%s,%s]|[%s,%s,%s]";
|
|
|
|
|
const char kEnergyPhases[] PROGMEM = "|%*_f / %*_f|%*_f / %*_f / %*_f||[%*_f,%*_f]|[%*_f,%*_f,%*_f]";
|
|
|
|
|
|
|
|
|
|
struct ENERGY {
|
|
|
|
|
float voltage[ENERGY_MAX_PHASES]; // 123.1 V
|
|
|
|
@ -85,15 +85,17 @@ struct ENERGY {
|
|
|
|
|
float import_active[ENERGY_MAX_PHASES]; // 123.123 kWh
|
|
|
|
|
#endif // SDM630_IMPORT || SDM72_IMPEXP
|
|
|
|
|
float export_active[ENERGY_MAX_PHASES]; // 123.123 kWh
|
|
|
|
|
float start_energy[ENERGY_MAX_PHASES]; // 12345.12345 kWh total previous
|
|
|
|
|
float daily[ENERGY_MAX_PHASES]; // 123.123 kWh
|
|
|
|
|
float total[ENERGY_MAX_PHASES]; // 12345.12345 kWh total energy
|
|
|
|
|
float daily_sum; // 123.123 kWh
|
|
|
|
|
float total_sum; // 12345.12345 kWh total energy
|
|
|
|
|
float yesterday_sum; // 123.123 kWh
|
|
|
|
|
|
|
|
|
|
float start_energy; // 12345.12345 kWh total previous
|
|
|
|
|
float daily; // 123.123 kWh
|
|
|
|
|
float total; // 12345.12345 kWh total energy
|
|
|
|
|
|
|
|
|
|
unsigned long kWhtoday_delta; // 1212312345 Wh 10^-5 (deca micro Watt hours) - Overflows to Energy.kWhtoday (HLW and CSE only)
|
|
|
|
|
unsigned long kWhtoday_offset; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily
|
|
|
|
|
unsigned long kWhtoday; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily
|
|
|
|
|
unsigned long period; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily
|
|
|
|
|
uint32_t kWhtoday_delta[ENERGY_MAX_PHASES]; // 1212312345 Wh 10^-5 (deca micro Watt hours) - Overflows to Energy.kWhtoday (HLW and CSE only)
|
|
|
|
|
uint32_t kWhtoday_offset[ENERGY_MAX_PHASES]; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily
|
|
|
|
|
uint32_t kWhtoday[ENERGY_MAX_PHASES]; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily
|
|
|
|
|
uint32_t period[ENERGY_MAX_PHASES]; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily
|
|
|
|
|
|
|
|
|
|
uint8_t fifth_second;
|
|
|
|
|
uint8_t command_code;
|
|
|
|
@ -134,27 +136,38 @@ Ticker ticker_energy;
|
|
|
|
|
|
|
|
|
|
/********************************************************************************************/
|
|
|
|
|
|
|
|
|
|
char* EnergyFormatIndex(char* result, char* input, bool json, uint32_t index, bool single = false)
|
|
|
|
|
{
|
|
|
|
|
char layout[16];
|
|
|
|
|
char* EnergyFormatIndex(char* result, float* input, uint32_t resolution, bool json, uint32_t index, bool single = false) {
|
|
|
|
|
char layout[20];
|
|
|
|
|
GetTextIndexed(layout, sizeof(layout), (index -1) + (ENERGY_MAX_PHASES * json), kEnergyPhases);
|
|
|
|
|
switch (index) {
|
|
|
|
|
case 2:
|
|
|
|
|
snprintf_P(result, FLOATSZ * ENERGY_MAX_PHASES, layout, input, input + FLOATSZ); // Dirty
|
|
|
|
|
ext_snprintf_P(result, FLOATSZ * ENERGY_MAX_PHASES, layout, resolution, &input[0], resolution, &input[1]);
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
snprintf_P(result, FLOATSZ * ENERGY_MAX_PHASES, layout, input, input + FLOATSZ, input + FLOATSZ + FLOATSZ); // Even dirtier
|
|
|
|
|
ext_snprintf_P(result, FLOATSZ * ENERGY_MAX_PHASES, layout, resolution, &input[0], resolution, &input[1], resolution, &input[2]);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
snprintf_P(result, FLOATSZ * ENERGY_MAX_PHASES, input);
|
|
|
|
|
ext_snprintf_P(result, FLOATSZ * ENERGY_MAX_PHASES, PSTR("%*_f"), resolution, &input[0]);
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char* EnergyFormat(char* result, char* input, bool json, bool single = false)
|
|
|
|
|
{
|
|
|
|
|
char* EnergyFormat(char* result, float* input, uint32_t resolution, bool json, bool single = false) {
|
|
|
|
|
uint8_t index = (single) ? 1 : Energy.phase_count; // 1,2,3
|
|
|
|
|
return EnergyFormatIndex(result, input, json, index, single);
|
|
|
|
|
return EnergyFormatIndex(result, input, resolution, json, index, single);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char* EnergyFormatSum(char* result, float* input, uint32_t resolution, bool json, bool single = false) {
|
|
|
|
|
uint8_t index = (single) ? 1 : Energy.phase_count; // 1,2,3
|
|
|
|
|
float input_sum = 0.0;
|
|
|
|
|
if (!Settings->flag5.energy_phase) {
|
|
|
|
|
for (uint32_t i = 0; i < index; i++) {
|
|
|
|
|
input_sum += input[i];
|
|
|
|
|
}
|
|
|
|
|
input = &input_sum;
|
|
|
|
|
index = 1;
|
|
|
|
|
}
|
|
|
|
|
return EnergyFormatIndex(result, input, resolution, json, index, single);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/********************************************************************************************/
|
|
|
|
@ -183,22 +196,30 @@ bool EnergyTariff1Active() // Off-Peak hours
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EnergyUpdateToday(void)
|
|
|
|
|
{
|
|
|
|
|
if (Energy.kWhtoday_delta > 1000) {
|
|
|
|
|
unsigned long delta = Energy.kWhtoday_delta / 1000;
|
|
|
|
|
Energy.kWhtoday_delta -= (delta * 1000);
|
|
|
|
|
Energy.kWhtoday += delta;
|
|
|
|
|
}
|
|
|
|
|
void EnergyUpdateToday(void) {
|
|
|
|
|
Energy.total_sum = 0.0;
|
|
|
|
|
Energy.yesterday_sum = 0.0;
|
|
|
|
|
Energy.daily_sum = 0.0;
|
|
|
|
|
for (uint32_t i = 0; i < Energy.phase_count; i++) {
|
|
|
|
|
if (Energy.kWhtoday_delta[i] > 1000) {
|
|
|
|
|
uint32_t delta = Energy.kWhtoday_delta[i] / 1000;
|
|
|
|
|
Energy.kWhtoday_delta[i] -= (delta * 1000);
|
|
|
|
|
Energy.kWhtoday[i] += delta;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RtcSettings.energy_kWhtoday = Energy.kWhtoday_offset + Energy.kWhtoday;
|
|
|
|
|
Energy.daily = (float)(RtcSettings.energy_kWhtoday) / 100000;
|
|
|
|
|
Energy.total = (float)(RtcSettings.energy_kWhtotal + RtcSettings.energy_kWhtoday) / 100000;
|
|
|
|
|
RtcSettings.energy_kWhtoday_ph[i] = Energy.kWhtoday_offset[i] + Energy.kWhtoday[i];
|
|
|
|
|
Energy.daily[i] = (float)(RtcSettings.energy_kWhtoday_ph[i]) / 100000;
|
|
|
|
|
Energy.total[i] = (float)(RtcSettings.energy_kWhtotal_ph[i] + RtcSettings.energy_kWhtoday_ph[i]) / 100000;
|
|
|
|
|
|
|
|
|
|
Energy.total_sum += Energy.total[i];
|
|
|
|
|
Energy.yesterday_sum += (float)Settings->energy_kWhyesterday_ph[i] / 100000;
|
|
|
|
|
Energy.daily_sum += Energy.daily[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (RtcTime.valid){ // We calc the difference only if we have a valid RTC time.
|
|
|
|
|
|
|
|
|
|
uint32_t energy_diff = (uint32_t)(Energy.total * 100000) - RtcSettings.energy_usage.last_usage_kWhtotal;
|
|
|
|
|
RtcSettings.energy_usage.last_usage_kWhtotal = (uint32_t)(Energy.total * 100000);
|
|
|
|
|
uint32_t energy_diff = (uint32_t)(Energy.total_sum * 100000) - RtcSettings.energy_usage.last_usage_kWhtotal;
|
|
|
|
|
RtcSettings.energy_usage.last_usage_kWhtotal = (uint32_t)(Energy.total_sum * 100000);
|
|
|
|
|
|
|
|
|
|
uint32_t return_diff = 0;
|
|
|
|
|
if (!isnan(Energy.export_active[0])) {
|
|
|
|
@ -225,25 +246,26 @@ void EnergyUpdateToday(void)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EnergyUpdateTotal(float value, bool kwh)
|
|
|
|
|
void EnergyUpdateTotal(float value, bool kwh, uint32_t phase = 0);
|
|
|
|
|
void EnergyUpdateTotal(float value, bool kwh, uint32_t phase)
|
|
|
|
|
{
|
|
|
|
|
// AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: Energy Total %4_f %sWh"), &value, (kwh) ? "k" : "");
|
|
|
|
|
|
|
|
|
|
uint32_t multiplier = (kwh) ? 100000 : 100; // kWh or Wh to deca milli Wh
|
|
|
|
|
|
|
|
|
|
if (0 == Energy.start_energy || (value < Energy.start_energy)) {
|
|
|
|
|
Energy.start_energy = value; // Init after restart and handle roll-over if any
|
|
|
|
|
if (0 == Energy.start_energy[phase] || (value < Energy.start_energy[phase])) {
|
|
|
|
|
Energy.start_energy[phase] = value; // Init after restart and handle roll-over if any
|
|
|
|
|
}
|
|
|
|
|
else if (value != Energy.start_energy) {
|
|
|
|
|
Energy.kWhtoday = (unsigned long)((value - Energy.start_energy) * multiplier);
|
|
|
|
|
else if (value != Energy.start_energy[phase]) {
|
|
|
|
|
Energy.kWhtoday[phase] = (unsigned long)((value - Energy.start_energy[phase]) * multiplier);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((Energy.total < (value - 0.01)) && // We subtract a little offset to avoid continuous updates
|
|
|
|
|
if ((Energy.total[phase] < (value - 0.01)) && // We subtract a little offset to avoid continuous updates
|
|
|
|
|
Settings->flag3.hardware_energy_total) { // SetOption72 - Enable hardware energy total counter as reference (#6561)
|
|
|
|
|
RtcSettings.energy_kWhtotal = (unsigned long)((value * multiplier) - Energy.kWhtoday_offset - Energy.kWhtoday);
|
|
|
|
|
Settings->energy_kWhtotal = RtcSettings.energy_kWhtotal;
|
|
|
|
|
Energy.total = (float)(RtcSettings.energy_kWhtotal + Energy.kWhtoday_offset + Energy.kWhtoday) / 100000;
|
|
|
|
|
Settings->energy_kWhtotal_time = (!Energy.kWhtoday_offset) ? LocalTime() : Midnight();
|
|
|
|
|
RtcSettings.energy_kWhtotal_ph[phase] = (unsigned long)((value * multiplier) - Energy.kWhtoday_offset[phase] - Energy.kWhtoday[phase]);
|
|
|
|
|
Settings->energy_kWhtotal_ph[phase] = RtcSettings.energy_kWhtotal_ph[phase];
|
|
|
|
|
Energy.total[phase] = (float)(RtcSettings.energy_kWhtotal_ph[phase] + Energy.kWhtoday_offset[phase] + Energy.kWhtoday[phase]) / 100000;
|
|
|
|
|
Settings->energy_kWhtotal_time = (!Energy.kWhtoday_offset[phase]) ? LocalTime() : Midnight();
|
|
|
|
|
// AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: Energy Total updated with hardware value"));
|
|
|
|
|
}
|
|
|
|
|
EnergyUpdateToday();
|
|
|
|
@ -264,23 +286,26 @@ void Energy200ms(void)
|
|
|
|
|
if (RtcTime.valid) {
|
|
|
|
|
|
|
|
|
|
if (!Energy.kWhtoday_offset_init && (RtcTime.day_of_year == Settings->energy_kWhdoy)) {
|
|
|
|
|
Energy.kWhtoday_offset = Settings->energy_kWhtoday;
|
|
|
|
|
for (uint32_t i = 0; i < 3; i++) {
|
|
|
|
|
Energy.kWhtoday_offset[i] = Settings->energy_kWhtoday_ph[i];
|
|
|
|
|
}
|
|
|
|
|
Energy.kWhtoday_offset_init = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (LocalTime() == Midnight()) {
|
|
|
|
|
Settings->energy_kWhyesterday = RtcSettings.energy_kWhtoday;
|
|
|
|
|
for (uint32_t i = 0; i < 3; i++) {
|
|
|
|
|
Settings->energy_kWhyesterday_ph[i] = RtcSettings.energy_kWhtoday_ph[i];
|
|
|
|
|
|
|
|
|
|
RtcSettings.energy_kWhtotal += RtcSettings.energy_kWhtoday;
|
|
|
|
|
Settings->energy_kWhtotal = RtcSettings.energy_kWhtotal;
|
|
|
|
|
RtcSettings.energy_kWhtotal_ph[i] += RtcSettings.energy_kWhtoday_ph[i];
|
|
|
|
|
Settings->energy_kWhtotal_ph[i] = RtcSettings.energy_kWhtotal_ph[i];
|
|
|
|
|
|
|
|
|
|
Energy.period -= RtcSettings.energy_kWhtoday; // this becomes a large unsigned, effectively a negative for EnergyShow calculation
|
|
|
|
|
Energy.kWhtoday = 0;
|
|
|
|
|
Energy.kWhtoday_offset = 0;
|
|
|
|
|
RtcSettings.energy_kWhtoday = 0;
|
|
|
|
|
Energy.start_energy = 0;
|
|
|
|
|
Energy.period[i] -= RtcSettings.energy_kWhtoday_ph[i]; // this becomes a large unsigned, effectively a negative for EnergyShow calculation
|
|
|
|
|
Energy.kWhtoday[i] = 0;
|
|
|
|
|
Energy.kWhtoday_offset[i] = 0;
|
|
|
|
|
RtcSettings.energy_kWhtoday_ph[i] = 0;
|
|
|
|
|
Energy.start_energy[i] = 0;
|
|
|
|
|
// Energy.kWhtoday_delta = 0; // dont zero this, we need to carry the remainder over to tomorrow
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
EnergyUpdateToday();
|
|
|
|
|
#if defined(USE_ENERGY_MARGIN_DETECTION) && defined(USE_ENERGY_POWER_LIMIT)
|
|
|
|
|
Energy.max_energy_state = 3;
|
|
|
|
@ -302,8 +327,10 @@ void EnergySaveState(void)
|
|
|
|
|
{
|
|
|
|
|
Settings->energy_kWhdoy = (RtcTime.valid) ? RtcTime.day_of_year : 0;
|
|
|
|
|
|
|
|
|
|
Settings->energy_kWhtoday = RtcSettings.energy_kWhtoday;
|
|
|
|
|
Settings->energy_kWhtotal = RtcSettings.energy_kWhtotal;
|
|
|
|
|
for (uint32_t i = 0; i < 3; i++) {
|
|
|
|
|
Settings->energy_kWhtoday_ph[i] = RtcSettings.energy_kWhtoday_ph[i];
|
|
|
|
|
Settings->energy_kWhtotal_ph[i] = RtcSettings.energy_kWhtotal_ph[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Settings->energy_usage = RtcSettings.energy_usage;
|
|
|
|
|
}
|
|
|
|
@ -371,12 +398,20 @@ void EnergyMarginCheck(void)
|
|
|
|
|
Energy.power_history[phase][2] = active_power;
|
|
|
|
|
}
|
|
|
|
|
if (jsonflg) {
|
|
|
|
|
/*
|
|
|
|
|
char power_diff_chr[Energy.phase_count][FLOATSZ];
|
|
|
|
|
for (uint32_t phase = 0; phase < Energy.phase_count; phase++) {
|
|
|
|
|
dtostrfd(power_diff[phase], 0, power_diff_chr[phase]);
|
|
|
|
|
}
|
|
|
|
|
char value_chr[FLOATSZ * ENERGY_MAX_PHASES];
|
|
|
|
|
ResponseAppend_P(PSTR("\"" D_CMND_POWERDELTA "\":%s"), EnergyFormat(value_chr, power_diff_chr[0], 1));
|
|
|
|
|
*/
|
|
|
|
|
float power_diff_f[Energy.phase_count];
|
|
|
|
|
for (uint32_t phase = 0; phase < Energy.phase_count; phase++) {
|
|
|
|
|
power_diff_f[phase] = power_diff[phase];
|
|
|
|
|
}
|
|
|
|
|
char value_chr[FLOATSZ * ENERGY_MAX_PHASES];
|
|
|
|
|
ResponseAppend_P(PSTR("\"" D_CMND_POWERDELTA "\":%s"), EnergyFormat(value_chr, power_diff_f, 0, 1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint16_t energy_power_u = (uint16_t)(Energy.active_power[0]);
|
|
|
|
@ -467,7 +502,7 @@ void EnergyMarginCheck(void)
|
|
|
|
|
|
|
|
|
|
// Max Energy
|
|
|
|
|
if (Settings->energy_max_energy) {
|
|
|
|
|
uint16_t energy_daily_u = (uint16_t)(Energy.daily * 1000);
|
|
|
|
|
uint16_t energy_daily_u = (uint16_t)(Energy.daily_sum * 1000);
|
|
|
|
|
if (!Energy.max_energy_state && (RtcTime.hour == Settings->energy_max_energy_start)) {
|
|
|
|
|
Energy.max_energy_state = 1;
|
|
|
|
|
ResponseTime_P(PSTR(",\"" D_JSON_ENERGYMONITOR "\":\"%s\"}"), GetStateText(1));
|
|
|
|
@ -476,7 +511,7 @@ void EnergyMarginCheck(void)
|
|
|
|
|
}
|
|
|
|
|
else if ((1 == Energy.max_energy_state ) && (energy_daily_u >= Settings->energy_max_energy)) {
|
|
|
|
|
Energy.max_energy_state = 2;
|
|
|
|
|
ResponseTime_P(PSTR(",\"" D_JSON_MAXENERGYREACHED "\":%3_f}"), &Energy.daily);
|
|
|
|
|
ResponseTime_P(PSTR(",\"" D_JSON_MAXENERGYREACHED "\":%3_f}"), &Energy.daily_sum);
|
|
|
|
|
MqttPublishPrefixTopicRulesProcess_P(STAT, S_RSLT_WARNING);
|
|
|
|
|
EnergyMqttShow();
|
|
|
|
|
SetAllPower(POWER_ALL_OFF, SRC_MAXENERGY);
|
|
|
|
@ -559,48 +594,9 @@ void CmndEnergyReset(void) {
|
|
|
|
|
uint32_t params = ParseParameters(2, values);
|
|
|
|
|
values[0] *= 100;
|
|
|
|
|
|
|
|
|
|
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 5)) {
|
|
|
|
|
if ((XdrvMailbox.index > 3) && (XdrvMailbox.index <= 5)) {
|
|
|
|
|
if (params > 0) {
|
|
|
|
|
switch (XdrvMailbox.index) {
|
|
|
|
|
case 1:
|
|
|
|
|
// Reset Energy Today
|
|
|
|
|
Energy.kWhtoday_offset = values[0];
|
|
|
|
|
Energy.kWhtoday = 0;
|
|
|
|
|
Energy.kWhtoday_delta = 0;
|
|
|
|
|
Energy.start_energy = 0;
|
|
|
|
|
Energy.period = Energy.kWhtoday_offset;
|
|
|
|
|
Settings->energy_kWhtoday = Energy.kWhtoday_offset;
|
|
|
|
|
RtcSettings.energy_kWhtoday = Energy.kWhtoday_offset;
|
|
|
|
|
Energy.daily = (float)Energy.kWhtoday_offset / 100000;
|
|
|
|
|
if( params > 1) {
|
|
|
|
|
Settings->energy_kWhtotal_time = values[1];
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (!RtcSettings.energy_kWhtotal && !Energy.kWhtoday_offset) {
|
|
|
|
|
Settings->energy_kWhtotal_time = LocalTime();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
// Reset Energy Yesterday
|
|
|
|
|
Settings->energy_kWhyesterday = values[0];
|
|
|
|
|
if( params > 1) {
|
|
|
|
|
Settings->energy_kWhtotal_time = values[1];
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
// Reset Energy Total
|
|
|
|
|
RtcSettings.energy_kWhtotal = values[0];
|
|
|
|
|
Settings->energy_kWhtotal = RtcSettings.energy_kWhtotal;
|
|
|
|
|
// Energy.total = (float)(RtcSettings.energy_kWhtotal + Energy.kWhtoday_offset + Energy.kWhtoday) / 100000;
|
|
|
|
|
if( params > 1) {
|
|
|
|
|
Settings->energy_kWhtotal_time = values[1];
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
Settings->energy_kWhtotal_time = (!Energy.kWhtoday_offset) ? LocalTime() : Midnight();
|
|
|
|
|
}
|
|
|
|
|
RtcSettings.energy_usage.last_usage_kWhtotal = (uint32_t)(Energy.total * 1000);
|
|
|
|
|
break;
|
|
|
|
|
case 4:
|
|
|
|
|
// Reset energy_usage.usage totals
|
|
|
|
|
RtcSettings.energy_usage.usage1_kWhtotal = values[0];
|
|
|
|
@ -623,24 +619,100 @@ void CmndEnergyReset(void) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Energy.total = (float)(RtcSettings.energy_kWhtotal + Energy.kWhtoday_offset + Energy.kWhtoday) / 100000;
|
|
|
|
|
float energy_kWhyesterday = (float)Settings->energy_kWhyesterday / 100000;
|
|
|
|
|
float usage1_kWhtotal = (float)Settings->energy_usage.usage1_kWhtotal / 100000;
|
|
|
|
|
float usage2_kWhtotal = (float)Settings->energy_usage.usage2_kWhtotal / 100000;
|
|
|
|
|
float return1_kWhtotal = (float)Settings->energy_usage.return1_kWhtotal / 100000;
|
|
|
|
|
float return2_kWhtotal = (float)Settings->energy_usage.return2_kWhtotal / 100000;
|
|
|
|
|
|
|
|
|
|
Response_P(PSTR("{\"%s\":{\"" D_JSON_TOTAL "\":%*_f,\"" D_JSON_YESTERDAY "\":%*_f,\"" D_JSON_TODAY "\":%*_f,\"" D_JSON_USAGE "\":[%*_f,%*_f],\"" D_JSON_EXPORT "\":[%*_f,%*_f]}}"),
|
|
|
|
|
Response_P(PSTR("{\"%s\":{\"" D_JSON_USAGE "\":[%*_f,%*_f],\"" D_JSON_EXPORT "\":[%*_f,%*_f]}}"),
|
|
|
|
|
XdrvMailbox.command,
|
|
|
|
|
Settings->flag2.energy_resolution, &Energy.total,
|
|
|
|
|
Settings->flag2.energy_resolution, &energy_kWhyesterday,
|
|
|
|
|
Settings->flag2.energy_resolution, &Energy.daily,
|
|
|
|
|
Settings->flag2.energy_resolution, &usage1_kWhtotal,
|
|
|
|
|
Settings->flag2.energy_resolution, &usage2_kWhtotal,
|
|
|
|
|
Settings->flag2.energy_resolution, &return1_kWhtotal,
|
|
|
|
|
Settings->flag2.energy_resolution, &return2_kWhtotal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CmndEnergyResponse(void) {
|
|
|
|
|
char value_chr[FLOATSZ * ENERGY_MAX_PHASES]; // Used by EnergyFormatIndex
|
|
|
|
|
char value2_chr[FLOATSZ * ENERGY_MAX_PHASES];
|
|
|
|
|
char value3_chr[FLOATSZ * ENERGY_MAX_PHASES];
|
|
|
|
|
|
|
|
|
|
float energy_yesterday_ph[3];
|
|
|
|
|
for (uint32_t i = 0; i < Energy.phase_count; i++) {
|
|
|
|
|
energy_yesterday_ph[i] = (float)Settings->energy_kWhyesterday_ph[i] / 100000;
|
|
|
|
|
Energy.total[i] = (float)(RtcSettings.energy_kWhtotal_ph[i] + Energy.kWhtoday_offset[i] + Energy.kWhtoday[i]) / 100000;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Response_P(PSTR("{\"%s\":{\"" D_JSON_TOTAL "\":%s,\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s}"),
|
|
|
|
|
XdrvMailbox.command,
|
|
|
|
|
EnergyFormat(value_chr, Energy.total, Settings->flag2.energy_resolution, true),
|
|
|
|
|
EnergyFormat(value2_chr, energy_yesterday_ph, Settings->flag2.energy_resolution, true),
|
|
|
|
|
EnergyFormat(value3_chr, Energy.daily, Settings->flag2.energy_resolution, true));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CmndEnergyTotal(void) {
|
|
|
|
|
uint32_t values[2] = { 0 };
|
|
|
|
|
uint32_t params = ParseParameters(2, values);
|
|
|
|
|
values[0] *= 100;
|
|
|
|
|
|
|
|
|
|
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= Energy.phase_count) && (params > 0)) {
|
|
|
|
|
uint32_t phase = XdrvMailbox.index -1;
|
|
|
|
|
// Reset Energy Total
|
|
|
|
|
RtcSettings.energy_kWhtotal_ph[phase] = values[0];
|
|
|
|
|
Settings->energy_kWhtotal_ph[phase] = RtcSettings.energy_kWhtotal_ph[phase];
|
|
|
|
|
if (params > 1) {
|
|
|
|
|
Settings->energy_kWhtotal_time = values[1];
|
|
|
|
|
} else {
|
|
|
|
|
Settings->energy_kWhtotal_time = (!Energy.kWhtoday_offset[phase]) ? LocalTime() : Midnight();
|
|
|
|
|
}
|
|
|
|
|
RtcSettings.energy_usage.last_usage_kWhtotal = (uint32_t)(Energy.total[phase] * 1000);
|
|
|
|
|
}
|
|
|
|
|
CmndEnergyResponse();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CmndEnergyYesterday(void) {
|
|
|
|
|
uint32_t values[2] = { 0 };
|
|
|
|
|
uint32_t params = ParseParameters(2, values);
|
|
|
|
|
values[0] *= 100;
|
|
|
|
|
|
|
|
|
|
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= Energy.phase_count) && (params > 0)) {
|
|
|
|
|
uint32_t phase = XdrvMailbox.index -1;
|
|
|
|
|
// Reset Energy Yesterday
|
|
|
|
|
Settings->energy_kWhyesterday_ph[phase] = values[0];
|
|
|
|
|
if (params > 1) {
|
|
|
|
|
Settings->energy_kWhtotal_time = values[1];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CmndEnergyResponse();
|
|
|
|
|
}
|
|
|
|
|
void CmndEnergyToday(void) {
|
|
|
|
|
uint32_t values[2] = { 0 };
|
|
|
|
|
uint32_t params = ParseParameters(2, values);
|
|
|
|
|
values[0] *= 100;
|
|
|
|
|
|
|
|
|
|
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= Energy.phase_count) && (params > 0)) {
|
|
|
|
|
uint32_t phase = XdrvMailbox.index -1;
|
|
|
|
|
// Reset Energy Today
|
|
|
|
|
Energy.kWhtoday_offset[phase] = values[0];
|
|
|
|
|
Energy.kWhtoday[phase] = 0;
|
|
|
|
|
Energy.kWhtoday_delta[phase] = 0;
|
|
|
|
|
Energy.start_energy[phase] = 0;
|
|
|
|
|
Energy.period[phase] = Energy.kWhtoday_offset[phase];
|
|
|
|
|
Settings->energy_kWhtoday_ph[phase] = Energy.kWhtoday_offset[phase];
|
|
|
|
|
RtcSettings.energy_kWhtoday_ph[phase] = Energy.kWhtoday_offset[phase];
|
|
|
|
|
Energy.daily[phase] = (float)Energy.kWhtoday_offset[phase] / 100000;
|
|
|
|
|
if (params > 1) {
|
|
|
|
|
Settings->energy_kWhtotal_time = values[1];
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (!RtcSettings.energy_kWhtotal_ph[phase] && !Energy.kWhtoday_offset[phase]) {
|
|
|
|
|
Settings->energy_kWhtotal_time = LocalTime();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CmndEnergyResponse();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CmndTariff(void) {
|
|
|
|
|
// Tariff1 22:00,23:00 - Tariff1 start hour for Standard Time and Daylight Savings Time
|
|
|
|
|
// Tariff2 6:00,7:00 - Tariff2 start hour for Standard Time and Daylight Savings Time
|
|
|
|
@ -910,15 +982,30 @@ void EnergySnsInit(void)
|
|
|
|
|
XnrgCall(FUNC_INIT);
|
|
|
|
|
|
|
|
|
|
if (TasmotaGlobal.energy_driver) {
|
|
|
|
|
|
|
|
|
|
// Update for split phase totals (v9.5.0.9)
|
|
|
|
|
if ((Settings->energy_kWhtoday > 0) && (0 == Settings->energy_kWhtoday_ph[0])) {
|
|
|
|
|
Settings->energy_kWhtoday_ph[0] = Settings->energy_kWhtoday;
|
|
|
|
|
Settings->energy_kWhyesterday_ph[0] = Settings->energy_kWhyesterday;
|
|
|
|
|
Settings->energy_kWhtotal_ph[0] = Settings->energy_kWhtotal;
|
|
|
|
|
RtcSettings.energy_kWhtoday_ph[0] = RtcSettings.energy_kWhtoday;
|
|
|
|
|
RtcSettings.energy_kWhtotal_ph[0] = RtcSettings.energy_kWhtotal;
|
|
|
|
|
Settings->energy_kWhtoday = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Energy.kWhtoday_offset = 0;
|
|
|
|
|
// Do not use at Power On as Rtc was invalid (but has been restored from Settings already)
|
|
|
|
|
if ((ResetReason() != REASON_DEFAULT_RST) && RtcSettingsValid()) {
|
|
|
|
|
Energy.kWhtoday_offset = RtcSettings.energy_kWhtoday;
|
|
|
|
|
for (uint32_t i = 0; i < 3; i++) {
|
|
|
|
|
Energy.kWhtoday_offset[i] = RtcSettings.energy_kWhtoday_ph[i];
|
|
|
|
|
}
|
|
|
|
|
Energy.kWhtoday_offset_init = true;
|
|
|
|
|
}
|
|
|
|
|
// Energy.kWhtoday = 0;
|
|
|
|
|
// Energy.kWhtoday_delta = 0;
|
|
|
|
|
Energy.period = Energy.kWhtoday_offset;
|
|
|
|
|
for (uint32_t i = 0; i < 3; i++) {
|
|
|
|
|
// Energy.kWhtoday_ph[i] = 0;
|
|
|
|
|
// Energy.kWhtoday_delta[i] = 0;
|
|
|
|
|
Energy.period[i] = Energy.kWhtoday_offset[i];
|
|
|
|
|
}
|
|
|
|
|
EnergyUpdateToday();
|
|
|
|
|
ticker_energy.attach_ms(200, Energy200ms);
|
|
|
|
|
}
|
|
|
|
@ -944,103 +1031,71 @@ const char HTTP_ENERGY_SNS4[] PROGMEM =
|
|
|
|
|
#endif // SDM630_IMPORT || SDM72_IMPEXP
|
|
|
|
|
#endif // USE_WEBSERVER
|
|
|
|
|
|
|
|
|
|
void EnergyShow(bool json)
|
|
|
|
|
{
|
|
|
|
|
for (uint32_t i = 0; i < Energy.phase_count; i++) {
|
|
|
|
|
if (Energy.voltage_common) {
|
|
|
|
|
void EnergyShow(bool json) {
|
|
|
|
|
if (Energy.voltage_common) {
|
|
|
|
|
for (uint32_t i = 0; i < Energy.phase_count; i++) {
|
|
|
|
|
Energy.voltage[i] = Energy.voltage[0];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float power_factor_knx = Energy.power_factor[0];
|
|
|
|
|
|
|
|
|
|
char apparent_power_chr[Energy.phase_count][FLOATSZ];
|
|
|
|
|
char reactive_power_chr[Energy.phase_count][FLOATSZ];
|
|
|
|
|
char power_factor_chr[Energy.phase_count][FLOATSZ];
|
|
|
|
|
char frequency_chr[Energy.phase_count][FLOATSZ];
|
|
|
|
|
float apparent_power[Energy.phase_count];
|
|
|
|
|
float reactive_power[Energy.phase_count];
|
|
|
|
|
float power_factor[Energy.phase_count];
|
|
|
|
|
if (!Energy.type_dc) {
|
|
|
|
|
if (Energy.current_available && Energy.voltage_available) {
|
|
|
|
|
for (uint32_t i = 0; i < Energy.phase_count; i++) {
|
|
|
|
|
float apparent_power = Energy.apparent_power[i];
|
|
|
|
|
if (isnan(apparent_power)) {
|
|
|
|
|
apparent_power = Energy.voltage[i] * Energy.current[i];
|
|
|
|
|
apparent_power[i] = Energy.apparent_power[i];
|
|
|
|
|
if (isnan(apparent_power[i])) {
|
|
|
|
|
apparent_power[i] = Energy.voltage[i] * Energy.current[i];
|
|
|
|
|
}
|
|
|
|
|
if (apparent_power < Energy.active_power[i]) { // Should be impossible
|
|
|
|
|
Energy.active_power[i] = apparent_power;
|
|
|
|
|
if (apparent_power[i] < Energy.active_power[i]) { // Should be impossible
|
|
|
|
|
Energy.active_power[i] = apparent_power[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float power_factor = Energy.power_factor[i];
|
|
|
|
|
if (isnan(power_factor)) {
|
|
|
|
|
power_factor = (Energy.active_power[i] && apparent_power) ? Energy.active_power[i] / apparent_power : 0;
|
|
|
|
|
if (power_factor > 1) {
|
|
|
|
|
power_factor = 1;
|
|
|
|
|
power_factor[i] = Energy.power_factor[i];
|
|
|
|
|
if (isnan(power_factor[i])) {
|
|
|
|
|
power_factor[i] = (Energy.active_power[i] && apparent_power[i]) ? Energy.active_power[i] / apparent_power[i] : 0;
|
|
|
|
|
if (power_factor[i] > 1) {
|
|
|
|
|
power_factor[i] = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (0 == i) { power_factor_knx = power_factor; }
|
|
|
|
|
|
|
|
|
|
float reactive_power = Energy.reactive_power[i];
|
|
|
|
|
if (isnan(reactive_power)) {
|
|
|
|
|
reactive_power = 0;
|
|
|
|
|
uint32_t difference = ((uint32_t)(apparent_power * 100) - (uint32_t)(Energy.active_power[i] * 100)) / 10;
|
|
|
|
|
if ((Energy.current[i] > 0.005) && ((difference > 15) || (difference > (uint32_t)(apparent_power * 100 / 1000)))) {
|
|
|
|
|
reactive_power[i] = Energy.reactive_power[i];
|
|
|
|
|
if (isnan(reactive_power[i])) {
|
|
|
|
|
reactive_power[i] = 0;
|
|
|
|
|
uint32_t difference = ((uint32_t)(apparent_power[i] * 100) - (uint32_t)(Energy.active_power[i] * 100)) / 10;
|
|
|
|
|
if ((Energy.current[i] > 0.005) && ((difference > 15) || (difference > (uint32_t)(apparent_power[i] * 100 / 1000)))) {
|
|
|
|
|
// calculating reactive power only if current is greater than 0.005A and
|
|
|
|
|
// difference between active and apparent power is greater than 1.5W or 1%
|
|
|
|
|
//reactive_power = (float)(RoundSqrtInt((uint64_t)(apparent_power * apparent_power * 100) - (uint64_t)(Energy.active_power[i] * Energy.active_power[i] * 100))) / 10;
|
|
|
|
|
float power_diff = apparent_power * apparent_power - Energy.active_power[i] * Energy.active_power[i];
|
|
|
|
|
//reactive_power[i] = (float)(RoundSqrtInt((uint64_t)(apparent_power[i] * apparent_power[i] * 100) - (uint64_t)(Energy.active_power[i] * Energy.active_power[i] * 100))) / 10;
|
|
|
|
|
float power_diff = apparent_power[i] * apparent_power[i] - Energy.active_power[i] * Energy.active_power[i];
|
|
|
|
|
if (power_diff < 10737418) // 2^30 / 100 (RoundSqrtInt is limited to 2^30-1)
|
|
|
|
|
reactive_power = (float)(RoundSqrtInt((uint32_t)(power_diff * 100.0))) / 10.0;
|
|
|
|
|
reactive_power[i] = (float)(RoundSqrtInt((uint32_t)(power_diff * 100.0))) / 10.0;
|
|
|
|
|
else
|
|
|
|
|
reactive_power = (float)(SqrtInt((uint32_t)(power_diff)));
|
|
|
|
|
reactive_power[i] = (float)(SqrtInt((uint32_t)(power_diff)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dtostrfd(apparent_power, Settings->flag2.wattage_resolution, apparent_power_chr[i]);
|
|
|
|
|
dtostrfd(reactive_power, Settings->flag2.wattage_resolution, reactive_power_chr[i]);
|
|
|
|
|
dtostrfd(power_factor, 2, power_factor_chr[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (uint32_t i = 0; i < Energy.phase_count; i++) {
|
|
|
|
|
float frequency = Energy.frequency[i];
|
|
|
|
|
if (isnan(Energy.frequency[i])) {
|
|
|
|
|
frequency = 0;
|
|
|
|
|
}
|
|
|
|
|
dtostrfd(frequency, Settings->flag2.frequency_resolution, frequency_chr[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char voltage_chr[Energy.phase_count][FLOATSZ];
|
|
|
|
|
char current_chr[Energy.phase_count][FLOATSZ];
|
|
|
|
|
char active_power_chr[Energy.phase_count][FLOATSZ];
|
|
|
|
|
#if defined(SDM630_IMPORT) || defined(SDM72_IMPEXP)
|
|
|
|
|
char import_active_chr[Energy.phase_count][FLOATSZ];
|
|
|
|
|
#endif // SDM630_IMPORT || SDM72_IMPEXP
|
|
|
|
|
char export_active_chr[Energy.phase_count][FLOATSZ];
|
|
|
|
|
float active_power_sum = 0.0;
|
|
|
|
|
float energy_yesterday_ph[Energy.phase_count];
|
|
|
|
|
for (uint32_t i = 0; i < Energy.phase_count; i++) {
|
|
|
|
|
dtostrfd(Energy.voltage[i], Settings->flag2.voltage_resolution, voltage_chr[i]);
|
|
|
|
|
dtostrfd(Energy.current[i], Settings->flag2.current_resolution, current_chr[i]);
|
|
|
|
|
dtostrfd(Energy.active_power[i], Settings->flag2.wattage_resolution, active_power_chr[i]);
|
|
|
|
|
#if defined(SDM630_IMPORT) || defined(SDM72_IMPEXP)
|
|
|
|
|
dtostrfd(Energy.import_active[i], Settings->flag2.energy_resolution, import_active_chr[i]);
|
|
|
|
|
#endif // SDM630_IMPORT || SDM72_IMPEXP
|
|
|
|
|
dtostrfd(Energy.export_active[i], Settings->flag2.energy_resolution, export_active_chr[i]);
|
|
|
|
|
energy_yesterday_ph[i] = (float)Settings->energy_kWhyesterday_ph[i] / 100000;
|
|
|
|
|
|
|
|
|
|
active_power_sum += Energy.active_power[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char energy_total_chr[FLOATSZ];
|
|
|
|
|
dtostrfd(Energy.total, Settings->flag2.energy_resolution, energy_total_chr);
|
|
|
|
|
char energy_daily_chr[FLOATSZ];
|
|
|
|
|
dtostrfd(Energy.daily, Settings->flag2.energy_resolution, energy_daily_chr);
|
|
|
|
|
char energy_yesterday_chr[FLOATSZ];
|
|
|
|
|
dtostrfd((float)Settings->energy_kWhyesterday / 100000, Settings->flag2.energy_resolution, energy_yesterday_chr);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool energy_tariff = false;
|
|
|
|
|
char energy_usage_chr[2][FLOATSZ];
|
|
|
|
|
char energy_return_chr[2][FLOATSZ];
|
|
|
|
|
float energy_usage[2];
|
|
|
|
|
float energy_return[2];
|
|
|
|
|
if (Settings->tariff[0][0] != Settings->tariff[1][0]) {
|
|
|
|
|
dtostrfd((float)RtcSettings.energy_usage.usage1_kWhtotal / 100000, Settings->flag2.energy_resolution, energy_usage_chr[0]); // Tariff1
|
|
|
|
|
dtostrfd((float)RtcSettings.energy_usage.usage2_kWhtotal / 100000, Settings->flag2.energy_resolution, energy_usage_chr[1]); // Tariff2
|
|
|
|
|
dtostrfd((float)RtcSettings.energy_usage.return1_kWhtotal / 100000, Settings->flag2.energy_resolution, energy_return_chr[0]); // Tariff1
|
|
|
|
|
dtostrfd((float)RtcSettings.energy_usage.return2_kWhtotal / 100000, Settings->flag2.energy_resolution, energy_return_chr[1]); // Tariff2
|
|
|
|
|
energy_usage[0] = (float)RtcSettings.energy_usage.usage1_kWhtotal / 100000; // Tariff1
|
|
|
|
|
energy_usage[1] = (float)RtcSettings.energy_usage.usage2_kWhtotal / 100000; // Tariff2
|
|
|
|
|
energy_return[0] = (float)RtcSettings.energy_usage.return1_kWhtotal / 100000; // Tariff1
|
|
|
|
|
energy_return[1] = (float)RtcSettings.energy_usage.return2_kWhtotal / 100000; // Tariff2
|
|
|
|
|
energy_tariff = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1053,86 +1108,94 @@ void EnergyShow(bool json)
|
|
|
|
|
|
|
|
|
|
ResponseAppend_P(PSTR(",\"" D_RSLT_ENERGY "\":{\"" D_JSON_TOTAL_START_TIME "\":\"%s\",\"" D_JSON_TOTAL "\":%s"),
|
|
|
|
|
GetDateAndTime(DT_ENERGY).c_str(),
|
|
|
|
|
energy_total_chr);
|
|
|
|
|
EnergyFormatSum(value_chr, Energy.total, Settings->flag2.energy_resolution, json));
|
|
|
|
|
|
|
|
|
|
if (energy_tariff) {
|
|
|
|
|
ResponseAppend_P(PSTR(",\"" D_JSON_TOTAL D_CMND_TARIFF "\":%s"),
|
|
|
|
|
EnergyFormatIndex(value_chr, energy_usage_chr[0], json, 2));
|
|
|
|
|
EnergyFormatIndex(value_chr, energy_usage, Settings->flag2.energy_resolution, json, 2));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ResponseAppend_P(PSTR(",\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s"),
|
|
|
|
|
energy_yesterday_chr,
|
|
|
|
|
energy_daily_chr);
|
|
|
|
|
EnergyFormatSum(value_chr, energy_yesterday_ph, Settings->flag2.energy_resolution, json),
|
|
|
|
|
EnergyFormatSum(value2_chr, Energy.daily, Settings->flag2.energy_resolution, json));
|
|
|
|
|
|
|
|
|
|
#if defined(SDM630_IMPORT) || defined(SDM72_IMPEXP)
|
|
|
|
|
if (!isnan(Energy.import_active[0])) {
|
|
|
|
|
ResponseAppend_P(PSTR(",\"" D_JSON_IMPORT_ACTIVE "\":%s"),
|
|
|
|
|
EnergyFormat(value_chr, import_active_chr[0], json));
|
|
|
|
|
EnergyFormat(value_chr, Energy.import_active, Settings->flag2.energy_resolution, json));
|
|
|
|
|
if (energy_tariff) {
|
|
|
|
|
ResponseAppend_P(PSTR(",\"" D_JSON_IMPORT D_CMND_TARIFF "\":%s"),
|
|
|
|
|
EnergyFormatIndex(value_chr, energy_return_chr[0], json, 2));
|
|
|
|
|
EnergyFormatIndex(value_chr, energy_return, Settings->flag2.energy_resolution, json, 2));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif // SDM630_IMPORT || SDM72_IMPEXP
|
|
|
|
|
|
|
|
|
|
if (!isnan(Energy.export_active[0])) {
|
|
|
|
|
ResponseAppend_P(PSTR(",\"" D_JSON_EXPORT_ACTIVE "\":%s"),
|
|
|
|
|
EnergyFormat(value_chr, export_active_chr[0], json));
|
|
|
|
|
EnergyFormat(value_chr, Energy.export_active, Settings->flag2.energy_resolution, json));
|
|
|
|
|
if (energy_tariff) {
|
|
|
|
|
ResponseAppend_P(PSTR(",\"" D_JSON_EXPORT D_CMND_TARIFF "\":%s"),
|
|
|
|
|
EnergyFormatIndex(value_chr, energy_return_chr[0], json, 2));
|
|
|
|
|
EnergyFormatIndex(value_chr, energy_return, Settings->flag2.energy_resolution, json, 2));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (show_energy_period) {
|
|
|
|
|
float energy = (float)(RtcSettings.energy_kWhtoday - Energy.period) / 100;
|
|
|
|
|
Energy.period = RtcSettings.energy_kWhtoday;
|
|
|
|
|
char energy_period_chr[FLOATSZ];
|
|
|
|
|
dtostrfd(energy, Settings->flag2.wattage_resolution, energy_period_chr);
|
|
|
|
|
ResponseAppend_P(PSTR(",\"" D_JSON_PERIOD "\":%s"), energy_period_chr);
|
|
|
|
|
float energy_period[Energy.phase_count];
|
|
|
|
|
for (uint32_t i = 0; i < Energy.phase_count; i++) {
|
|
|
|
|
energy_period[i] = (float)(RtcSettings.energy_kWhtoday_ph[i] - Energy.period[i]) / 100;
|
|
|
|
|
Energy.period[i] = RtcSettings.energy_kWhtoday_ph[i];
|
|
|
|
|
}
|
|
|
|
|
ResponseAppend_P(PSTR(",\"" D_JSON_PERIOD "\":%s"),
|
|
|
|
|
EnergyFormat(value_chr, energy_period, Settings->flag2.wattage_resolution, json));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ResponseAppend_P(PSTR(",\"" D_JSON_POWERUSAGE "\":%s"),
|
|
|
|
|
EnergyFormat(value_chr, active_power_chr[0], json));
|
|
|
|
|
EnergyFormat(value_chr, Energy.active_power, Settings->flag2.wattage_resolution, json));
|
|
|
|
|
if (!Energy.type_dc) {
|
|
|
|
|
if (Energy.current_available && Energy.voltage_available) {
|
|
|
|
|
ResponseAppend_P(PSTR(",\"" D_JSON_APPARENT_POWERUSAGE "\":%s,\"" D_JSON_REACTIVE_POWERUSAGE "\":%s,\"" D_JSON_POWERFACTOR "\":%s"),
|
|
|
|
|
EnergyFormat(value_chr, apparent_power_chr[0], json),
|
|
|
|
|
EnergyFormat(value2_chr, reactive_power_chr[0], json),
|
|
|
|
|
EnergyFormat(value3_chr, power_factor_chr[0], json));
|
|
|
|
|
EnergyFormat(value_chr, apparent_power, Settings->flag2.wattage_resolution, json),
|
|
|
|
|
EnergyFormat(value2_chr, reactive_power, Settings->flag2.wattage_resolution, json),
|
|
|
|
|
EnergyFormat(value3_chr, power_factor, 2, json));
|
|
|
|
|
}
|
|
|
|
|
if (!isnan(Energy.frequency[0])) {
|
|
|
|
|
ResponseAppend_P(PSTR(",\"" D_JSON_FREQUENCY "\":%s"),
|
|
|
|
|
EnergyFormat(value_chr, frequency_chr[0], json, Energy.frequency_common));
|
|
|
|
|
EnergyFormat(value_chr, Energy.frequency, Settings->flag2.frequency_resolution, json, Energy.frequency_common));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (Energy.voltage_available) {
|
|
|
|
|
ResponseAppend_P(PSTR(",\"" D_JSON_VOLTAGE "\":%s"),
|
|
|
|
|
EnergyFormat(value_chr, voltage_chr[0], json, Energy.voltage_common));
|
|
|
|
|
EnergyFormat(value_chr, Energy.voltage, Settings->flag2.voltage_resolution, json, Energy.voltage_common));
|
|
|
|
|
}
|
|
|
|
|
if (Energy.current_available) {
|
|
|
|
|
ResponseAppend_P(PSTR(",\"" D_JSON_CURRENT "\":%s"),
|
|
|
|
|
EnergyFormat(value_chr, current_chr[0], json));
|
|
|
|
|
EnergyFormat(value_chr, Energy.current, Settings->flag2.current_resolution, json));
|
|
|
|
|
}
|
|
|
|
|
XnrgCall(FUNC_JSON_APPEND);
|
|
|
|
|
ResponseJsonEnd();
|
|
|
|
|
|
|
|
|
|
#ifdef USE_DOMOTICZ
|
|
|
|
|
if (show_energy_period) { // Only send if telemetry
|
|
|
|
|
dtostrfd(Energy.total * 1000, 1, energy_total_chr);
|
|
|
|
|
DomoticzSensorPowerEnergy((int)Energy.active_power[0], energy_total_chr); // PowerUsage, EnergyToday
|
|
|
|
|
char temp_chr[FLOATSZ];
|
|
|
|
|
if (Energy.voltage_available) {
|
|
|
|
|
dtostrfd(Energy.voltage[0], Settings->flag2.voltage_resolution, temp_chr);
|
|
|
|
|
DomoticzSensor(DZ_VOLTAGE, temp_chr); // Voltage
|
|
|
|
|
}
|
|
|
|
|
if (Energy.current_available) {
|
|
|
|
|
dtostrfd(Energy.current[0], Settings->flag2.current_resolution, temp_chr);
|
|
|
|
|
DomoticzSensor(DZ_CURRENT, temp_chr); // Current
|
|
|
|
|
}
|
|
|
|
|
dtostrfd(Energy.total_sum * 1000, 1, temp_chr);
|
|
|
|
|
DomoticzSensorPowerEnergy((int)active_power_sum, temp_chr); // PowerUsage, EnergyToday
|
|
|
|
|
|
|
|
|
|
char energy_usage_chr[2][FLOATSZ];
|
|
|
|
|
char energy_return_chr[2][FLOATSZ];
|
|
|
|
|
dtostrfd((float)RtcSettings.energy_usage.usage1_kWhtotal / 100, 1, energy_usage_chr[0]); // Tariff1
|
|
|
|
|
dtostrfd((float)RtcSettings.energy_usage.usage2_kWhtotal / 100, 1, energy_usage_chr[1]); // Tariff2
|
|
|
|
|
dtostrfd((float)RtcSettings.energy_usage.return1_kWhtotal / 100, 1, energy_return_chr[0]);
|
|
|
|
|
dtostrfd((float)RtcSettings.energy_usage.return2_kWhtotal / 100, 1, energy_return_chr[1]);
|
|
|
|
|
DomoticzSensorP1SmartMeter(energy_usage_chr[0], energy_usage_chr[1], energy_return_chr[0], energy_return_chr[1], (int)Energy.active_power[0]);
|
|
|
|
|
DomoticzSensorP1SmartMeter(energy_usage_chr[0], energy_usage_chr[1], energy_return_chr[0], energy_return_chr[1], (int)active_power_sum);
|
|
|
|
|
|
|
|
|
|
if (Energy.voltage_available) {
|
|
|
|
|
DomoticzSensor(DZ_VOLTAGE, voltage_chr[0]); // Voltage
|
|
|
|
|
}
|
|
|
|
|
if (Energy.current_available) {
|
|
|
|
|
DomoticzSensor(DZ_CURRENT, current_chr[0]); // Current
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif // USE_DOMOTICZ
|
|
|
|
|
#ifdef USE_KNX
|
|
|
|
@ -1143,42 +1206,46 @@ void EnergyShow(bool json)
|
|
|
|
|
if (Energy.current_available) {
|
|
|
|
|
KnxSensor(KNX_ENERGY_CURRENT, Energy.current[0]);
|
|
|
|
|
}
|
|
|
|
|
KnxSensor(KNX_ENERGY_POWER, Energy.active_power[0]);
|
|
|
|
|
KnxSensor(KNX_ENERGY_POWER, active_power_sum);
|
|
|
|
|
if (!Energy.type_dc) {
|
|
|
|
|
KnxSensor(KNX_ENERGY_POWERFACTOR, power_factor_knx);
|
|
|
|
|
KnxSensor(KNX_ENERGY_POWERFACTOR, power_factor[0]);
|
|
|
|
|
}
|
|
|
|
|
KnxSensor(KNX_ENERGY_DAILY, Energy.daily);
|
|
|
|
|
KnxSensor(KNX_ENERGY_TOTAL, Energy.total);
|
|
|
|
|
KnxSensor(KNX_ENERGY_YESTERDAY, (float)Settings->energy_kWhyesterday / 100000);
|
|
|
|
|
KnxSensor(KNX_ENERGY_DAILY, Energy.daily_sum);
|
|
|
|
|
KnxSensor(KNX_ENERGY_TOTAL, Energy.total_sum);
|
|
|
|
|
KnxSensor(KNX_ENERGY_YESTERDAY, Energy.yesterday_sum);
|
|
|
|
|
}
|
|
|
|
|
#endif // USE_KNX
|
|
|
|
|
#ifdef USE_WEBSERVER
|
|
|
|
|
} else {
|
|
|
|
|
if (Energy.voltage_available) {
|
|
|
|
|
WSContentSend_PD(HTTP_SNS_VOLTAGE, EnergyFormat(value_chr, voltage_chr[0], json, Energy.voltage_common));
|
|
|
|
|
WSContentSend_PD(HTTP_SNS_VOLTAGE, EnergyFormat(value_chr, Energy.voltage, Settings->flag2.voltage_resolution, json, Energy.voltage_common));
|
|
|
|
|
}
|
|
|
|
|
if (Energy.current_available) {
|
|
|
|
|
WSContentSend_PD(HTTP_SNS_CURRENT, EnergyFormat(value_chr, current_chr[0], json));
|
|
|
|
|
}
|
|
|
|
|
WSContentSend_PD(HTTP_SNS_POWER, EnergyFormat(value_chr, active_power_chr[0], json));
|
|
|
|
|
if (!Energy.type_dc) {
|
|
|
|
|
if (Energy.current_available && Energy.voltage_available) {
|
|
|
|
|
WSContentSend_PD(HTTP_ENERGY_SNS1, EnergyFormat(value_chr, apparent_power_chr[0], json),
|
|
|
|
|
EnergyFormat(value2_chr, reactive_power_chr[0], json),
|
|
|
|
|
EnergyFormat(value3_chr, power_factor_chr[0], json));
|
|
|
|
|
}
|
|
|
|
|
if (!isnan(Energy.frequency[0])) {
|
|
|
|
|
WSContentSend_PD(PSTR("{s}" D_FREQUENCY "{m}%s " D_UNIT_HERTZ "{e}"),
|
|
|
|
|
EnergyFormat(value_chr, frequency_chr[0], json, Energy.frequency_common));
|
|
|
|
|
EnergyFormat(value_chr, Energy.frequency, Settings->flag2.frequency_resolution, json, Energy.frequency_common));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
WSContentSend_PD(HTTP_ENERGY_SNS2, energy_daily_chr, energy_yesterday_chr, energy_total_chr);
|
|
|
|
|
if (Energy.current_available) {
|
|
|
|
|
WSContentSend_PD(HTTP_SNS_CURRENT, EnergyFormat(value_chr, Energy.current, Settings->flag2.current_resolution, json));
|
|
|
|
|
}
|
|
|
|
|
WSContentSend_PD(HTTP_SNS_POWER, EnergyFormat(value_chr, Energy.active_power, Settings->flag2.wattage_resolution, json));
|
|
|
|
|
if (!Energy.type_dc) {
|
|
|
|
|
if (Energy.current_available && Energy.voltage_available) {
|
|
|
|
|
WSContentSend_PD(HTTP_ENERGY_SNS1, EnergyFormat(value_chr, apparent_power, Settings->flag2.wattage_resolution, json),
|
|
|
|
|
EnergyFormat(value2_chr, reactive_power, Settings->flag2.wattage_resolution, json),
|
|
|
|
|
EnergyFormat(value3_chr, power_factor, 2, json));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
WSContentSend_PD(HTTP_ENERGY_SNS2, EnergyFormatSum(value_chr, Energy.daily, Settings->flag2.energy_resolution, json),
|
|
|
|
|
EnergyFormatSum(value2_chr, energy_yesterday_ph, Settings->flag2.energy_resolution, json),
|
|
|
|
|
EnergyFormatSum(value3_chr, Energy.total, Settings->flag2.energy_resolution, json));
|
|
|
|
|
if (!isnan(Energy.export_active[0])) {
|
|
|
|
|
WSContentSend_PD(HTTP_ENERGY_SNS3, EnergyFormat(value_chr, export_active_chr[0], json));
|
|
|
|
|
WSContentSend_PD(HTTP_ENERGY_SNS3, EnergyFormat(value_chr, Energy.export_active, Settings->flag2.energy_resolution, json));
|
|
|
|
|
}
|
|
|
|
|
#if defined(SDM630_IMPORT) || defined(SDM72_IMPEXP)
|
|
|
|
|
if (!isnan(Energy.import_active[0])) {
|
|
|
|
|
WSContentSend_PD(HTTP_ENERGY_SNS4, EnergyFormat(value_chr, import_active_chr[0], json));
|
|
|
|
|
WSContentSend_PD(HTTP_ENERGY_SNS4, EnergyFormat(value_chr, Energy.import_active, Settings->flag2.energy_resolution, json));
|
|
|
|
|
}
|
|
|
|
|
#endif // SDM630_IMPORT || SDM72_IMPEXP
|
|
|
|
|
|
|
|
|
|