2017-12-16 14:51:45 +00:00
/*
2019-10-27 10:13:24 +00:00
xdrv_03_energy . ino - Energy sensor support for Tasmota
2017-12-16 14:51:45 +00:00
2021-01-01 12:44:04 +00:00
Copyright ( C ) 2021 Theo Arends
2017-12-16 14:51:45 +00:00
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# ifdef USE_ENERGY_SENSOR
/*********************************************************************************************\
2018-09-04 15:22:34 +01:00
* Energy
2017-12-16 14:51:45 +00:00
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2019-08-27 16:01:12 +01:00
# define XDRV_03 3
# define XSNS_03 3
2018-11-06 16:33:51 +00:00
2019-08-01 14:46:12 +01:00
//#define USE_ENERGY_MARGIN_DETECTION
// #define USE_ENERGY_POWER_LIMIT
2017-12-16 14:51:45 +00:00
2019-08-27 16:01:12 +01:00
# define ENERGY_NONE 0
# define ENERGY_WATCHDOG 4 // Allow up to 4 seconds before deciding no valid data present
2021-04-06 14:23:07 +01:00
# define ENERGY_MAX_PHASES 3
2019-05-14 16:46:40 +01:00
2018-11-20 14:00:24 +00:00
# include <Ticker.h>
2019-01-05 14:39:56 +00:00
# define D_CMND_POWERCAL "PowerCal"
# define D_CMND_VOLTAGECAL "VoltageCal"
# define D_CMND_CURRENTCAL "CurrentCal"
2021-03-21 16:51:57 +00:00
# define D_CMND_FREQUENCYCAL "FrequencyCal"
2019-08-27 16:01:12 +01:00
# define D_CMND_TARIFF "Tariff"
2019-09-16 15:56:16 +01:00
# define D_CMND_MODULEADDRESS "ModuleAddress"
2019-01-05 14:39:56 +00:00
2022-10-30 10:15:17 +00:00
enum EnergyCalibration {
ENERGY_POWER_CALIBRATION , ENERGY_VOLTAGE_CALIBRATION , ENERGY_CURRENT_CALIBRATION , ENERGY_FREQUENCY_CALIBRATION } ;
2017-12-16 14:51:45 +00:00
enum EnergyCommands {
2021-03-21 16:51:57 +00:00
CMND_POWERCAL , CMND_VOLTAGECAL , CMND_CURRENTCAL , CMND_FREQUENCYCAL ,
2021-04-09 08:49:33 +01:00
CMND_POWERSET , CMND_VOLTAGESET , CMND_CURRENTSET , CMND_FREQUENCYSET , CMND_MODULEADDRESS , CMND_ENERGYCONFIG } ;
2019-08-01 14:46:12 +01:00
2019-08-11 17:12:18 +01:00
const char kEnergyCommands [ ] PROGMEM = " | " // No prefix
2021-03-21 16:51:57 +00:00
D_CMND_POWERCAL " | " D_CMND_VOLTAGECAL " | " D_CMND_CURRENTCAL " | " D_CMND_FREQUENCYCAL " | "
2021-04-09 08:49:33 +01:00
D_CMND_POWERSET " | " D_CMND_VOLTAGESET " | " D_CMND_CURRENTSET " | " D_CMND_FREQUENCYSET " | " D_CMND_MODULEADDRESS " | " D_CMND_ENERGYCONFIG " | "
2019-08-01 14:46:12 +01:00
# ifdef USE_ENERGY_MARGIN_DETECTION
D_CMND_POWERDELTA " | " D_CMND_POWERLOW " | " D_CMND_POWERHIGH " | " D_CMND_VOLTAGELOW " | " D_CMND_VOLTAGEHIGH " | " D_CMND_CURRENTLOW " | " D_CMND_CURRENTHIGH " | "
# ifdef USE_ENERGY_POWER_LIMIT
D_CMND_MAXENERGY " | " D_CMND_MAXENERGYSTART " | "
2017-12-16 14:51:45 +00:00
D_CMND_MAXPOWER " | " D_CMND_MAXPOWERHOLD " | " D_CMND_MAXPOWERWINDOW " | "
2019-08-01 14:46:12 +01:00
D_CMND_SAFEPOWER " | " D_CMND_SAFEPOWERHOLD " | " D_CMND_SAFEPOWERWINDOW " | "
# endif // USE_ENERGY_POWER_LIMIT
# endif // USE_ENERGY_MARGIN_DETECTION
2022-04-16 15:07:42 +01:00
D_CMND_ENERGYTODAY " | " D_CMND_ENERGYYESTERDAY " | " D_CMND_ENERGYTOTAL " | " D_CMND_ENERGYEXPORTACTIVE " | " D_CMND_ENERGYUSAGE " | " D_CMND_ENERGYEXPORT " | " D_CMND_TARIFF ;
2019-08-01 14:46:12 +01:00
void ( * const EnergyCommand [ ] ) ( void ) PROGMEM = {
2021-03-21 16:51:57 +00:00
& CmndPowerCal , & CmndVoltageCal , & CmndCurrentCal , & CmndFrequencyCal ,
2021-04-09 08:49:33 +01:00
& CmndPowerSet , & CmndVoltageSet , & CmndCurrentSet , & CmndFrequencySet , & CmndModuleAddress , & CmndEnergyConfig ,
2019-08-01 14:46:12 +01:00
# ifdef USE_ENERGY_MARGIN_DETECTION
& CmndPowerDelta , & CmndPowerLow , & CmndPowerHigh , & CmndVoltageLow , & CmndVoltageHigh , & CmndCurrentLow , & CmndCurrentHigh ,
# ifdef USE_ENERGY_POWER_LIMIT
& CmndMaxEnergy , & CmndMaxEnergyStart ,
& CmndMaxPower , & CmndMaxPowerHold , & CmndMaxPowerWindow ,
& CmndSafePower , & CmndSafePowerHold , & CmndSafePowerWindow ,
# endif // USE_ENERGY_POWER_LIMIT
# endif // USE_ENERGY_MARGIN_DETECTION
2022-04-16 15:07:42 +01:00
& CmndEnergyToday , & CmndEnergyYesterday , & CmndEnergyTotal , & CmndEnergyExportActive , & CmndEnergyUsage , & CmndEnergyExport , & CmndTariff } ;
2017-12-16 14:51:45 +00:00
2019-08-16 13:41:02 +01:00
struct ENERGY {
2021-04-06 14:23:07 +01:00
float voltage [ ENERGY_MAX_PHASES ] ; // 123.1 V
float current [ ENERGY_MAX_PHASES ] ; // 123.123 A
float active_power [ ENERGY_MAX_PHASES ] ; // 123.1 W
float apparent_power [ ENERGY_MAX_PHASES ] ; // 123.1 VA
float reactive_power [ ENERGY_MAX_PHASES ] ; // 123.1 VAr
float power_factor [ ENERGY_MAX_PHASES ] ; // 0.12
float frequency [ ENERGY_MAX_PHASES ] ; // 123.1 Hz
float import_active [ ENERGY_MAX_PHASES ] ; // 123.123 kWh
float export_active [ ENERGY_MAX_PHASES ] ; // 123.123 kWh
2021-09-29 14:33:58 +01:00
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
2022-08-26 13:35:52 +01:00
float daily_sum_import_balanced ; // 123.123 kWh
float daily_sum_export_balanced ; // 123.123 kWh
2021-09-29 14:33:58 +01:00
2021-10-14 15:21:35 +01:00
int32_t kWhtoday_delta [ ENERGY_MAX_PHASES ] ; // 1212312345 Wh 10^-5 (deca micro Watt hours) - Overflows to Energy.kWhtoday (HLW and CSE only)
int32_t kWhtoday_offset [ ENERGY_MAX_PHASES ] ; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily
int32_t kWhtoday [ ENERGY_MAX_PHASES ] ; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily
int32_t period [ ENERGY_MAX_PHASES ] ; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily
2019-08-16 13:41:02 +01:00
2021-04-06 14:23:07 +01:00
uint8_t fifth_second ;
uint8_t command_code ;
uint8_t data_valid [ ENERGY_MAX_PHASES ] ;
2019-08-16 13:41:02 +01:00
2021-04-06 14:23:07 +01:00
uint8_t phase_count ; // Number of phases active
2021-10-29 07:51:21 +01:00
bool voltage_common ; // Use common voltage
bool frequency_common ; // Use common frequency
2021-04-09 09:04:37 +01:00
bool use_overtemp ; // Use global temperature as overtemp trigger on internal energy monitor hardware
2021-04-06 14:23:07 +01:00
bool kWhtoday_offset_init ;
2019-09-15 12:10:32 +01:00
2021-04-06 14:23:07 +01:00
bool voltage_available ; // Enable if voltage is measured
bool current_available ; // Enable if current is measured
2022-04-16 15:07:42 +01:00
bool local_energy_active_export ; // Enable if support for storing energy_active
2019-08-16 13:41:02 +01:00
2021-04-06 14:23:07 +01:00
bool type_dc ;
bool power_on ;
2019-08-01 14:46:12 +01:00
# ifdef USE_ENERGY_MARGIN_DETECTION
2021-04-06 14:23:07 +01:00
uint16_t power_history [ ENERGY_MAX_PHASES ] [ 3 ] ;
uint8_t power_steady_counter ; // Allow for power on stabilization
bool min_power_flag ;
bool max_power_flag ;
bool min_voltage_flag ;
bool max_voltage_flag ;
bool min_current_flag ;
bool max_current_flag ;
2017-12-16 14:51:45 +00:00
2019-08-01 14:46:12 +01:00
# ifdef USE_ENERGY_POWER_LIMIT
2021-04-06 14:23:07 +01:00
uint16_t mplh_counter ;
uint16_t mplw_counter ;
uint8_t mplr_counter ;
uint8_t max_energy_state ;
2019-08-01 14:46:12 +01:00
# endif // USE_ENERGY_POWER_LIMIT
# endif // USE_ENERGY_MARGIN_DETECTION
2019-08-16 13:41:02 +01:00
} Energy ;
2017-12-16 14:51:45 +00:00
Ticker ticker_energy ;
2018-02-04 17:09:09 +00:00
/********************************************************************************************/
2022-03-15 14:43:23 +00:00
char * EnergyFormat ( char * result , float * input , uint32_t resolution , uint32_t single = 0 ) ;
char * EnergyFormat ( char * result , float * input , uint32_t resolution , uint32_t single ) {
// single = 0 - Energy.phase_count - xx or [xx,xx] or [xx,xx,xx]
// single = 1 - Energy.voltage_common or Energy.frequency_common - xx
2022-03-15 16:36:25 +00:00
// single = 2 - Sum of Energy.phase_count if SO129 0 - xx or if SO129 1 - [xx,xx,xx]
2022-03-15 14:43:23 +00:00
// single = 5 - single &0x03 = 1 - xx
// single = 6 - single &0x03 = 2 - [xx,xx] - used by tarriff
// single = 7 - single &0x03 = 3 - [xx,xx,xx]
uint32_t index = ( single > 3 ) ? single & 0x03 : ( 0 = = single ) ? Energy . phase_count : 1 ; // 1,2,3
if ( single > 2 ) { single = 0 ; } // 0,1,2
2022-04-19 14:44:53 +01:00
float input_sum = 0.0f ;
2022-03-15 14:43:23 +00:00
if ( single > 1 ) {
if ( ! Settings - > flag5 . energy_phase ) { // SetOption129 - (Energy) Show phase information
for ( uint32_t i = 0 ; i < Energy . phase_count ; i + + ) {
input_sum + = input [ i ] ;
}
input = & input_sum ;
2022-03-15 16:36:25 +00:00
} else {
index = Energy . phase_count ;
2022-03-15 14:43:23 +00:00
}
}
result [ 0 ] = ' \0 ' ;
for ( uint32_t i = 0 ; i < index ; i + + ) {
ext_snprintf_P ( result , TOPSZ , PSTR ( " %s%s%*_f%s " ) , result , ( 0 = = i ) ? ( 1 = = index ) ? " " : " [ " : " , " , resolution , & input [ i ] , ( index - 1 = = i ) ? ( 1 = = index ) ? " " : " ] " : " " ) ;
2020-08-23 17:29:16 +01:00
}
return result ;
}
2022-03-15 14:43:23 +00:00
# ifdef USE_WEBSERVER
char * WebEnergyFormat ( char * result , float * input , uint32_t resolution , uint32_t single = 0 ) ;
char * WebEnergyFormat ( char * result , float * input , uint32_t resolution , uint32_t single ) {
// single = 0 - Energy.phase_count - xx / xx / xx or multi column
// single = 1 - Energy.voltage_common or Energy.frequency_common - xx or single column using colspan (if needed)
2022-03-15 16:36:25 +00:00
// single = 2 - Sum of Energy.phase_count if SO129 0 - xx or single column using colspan (if needed) or if SO129 1 - xx / xx / xx or multi column
2022-04-19 14:44:53 +01:00
float input_sum = 0.0f ;
2022-03-15 14:43:23 +00:00
if ( single > 1 ) { // Sum and/or Single column
if ( ! Settings - > flag5 . energy_phase ) { // SetOption129 - (Energy) Show phase information
for ( uint32_t i = 0 ; i < Energy . phase_count ; i + + ) {
input_sum + = input [ i ] ;
}
input = & input_sum ;
} else {
single = 0 ;
}
}
# ifdef USE_ENERGY_COLUMN_GUI
2022-10-11 10:12:54 +01:00
ext_snprintf_P ( result , GUISZ , PSTR ( " </td> " ) ) ; // Skip first column
2022-04-12 09:22:32 +01:00
if ( ( Energy . phase_count > 1 ) & & single ) { // Need to set colspan so need new columns
// </td><td colspan='3' style='text-align:right'>1.23</td><td> </td><td>
// </td><td colspan='5' style='text-align:right'>1.23</td><td> </td><td>
// </td><td colspan='7' style='text-align:right'>1.23</td><td> </td><td>
2022-10-11 10:12:54 +01:00
ext_snprintf_P ( result , GUISZ , PSTR ( " %s<td colspan='%d' style='text-align:%s'>%*_f</td><td> </td> " ) ,
2022-04-12 09:22:32 +01:00
result , ( Energy . phase_count * 2 ) - 1 , ( Settings - > flag5 . gui_table_align ) ? PSTR ( " right " ) : PSTR ( " center " ) , resolution , & input [ 0 ] ) ;
2022-03-15 14:43:23 +00:00
} else {
2022-04-12 09:22:32 +01:00
// </td><td style='text-align:right'>1.23</td><td> </td><td>
// </td><td style='text-align:right'>1.23</td><td> </td><td style='text-align:right'>1.23</td><td> </td><td>
// </td><td style='text-align:right'>1.23</td><td> </td><td style='text-align:right'>1.23</td><td> </td><td style='text-align:right'>1.23</td><td> </td><td>
// </td><td style='text-align:right'>1.23</td><td> </td><td style='text-align:right'>1.23</td><td> </td><td style='text-align:right'>1.23</td><td> </td><td style='text-align:right'>1.23</td><td> </td><td>
2022-03-15 14:43:23 +00:00
for ( uint32_t i = 0 ; i < Energy . phase_count ; i + + ) {
2022-10-11 10:12:54 +01:00
ext_snprintf_P ( result , GUISZ , PSTR ( " %s<td style='text-align:%s'>%*_f</td><td> </td> " ) ,
2022-04-12 09:22:32 +01:00
result , ( Settings - > flag5 . gui_table_align ) ? PSTR ( " right " ) : PSTR ( " left " ) , resolution , & input [ i ] ) ;
2021-09-29 14:33:58 +01:00
}
}
2022-10-11 10:12:54 +01:00
ext_snprintf_P ( result , GUISZ , PSTR ( " %s<td> " ) , result ) ;
2022-03-15 14:43:23 +00:00
# else // not USE_ENERGY_COLUMN_GUI
uint32_t index = ( single ) ? 1 : Energy . phase_count ; // 1,2,3
result [ 0 ] = ' \0 ' ;
for ( uint32_t i = 0 ; i < index ; i + + ) {
2022-10-12 10:02:54 +01:00
ext_snprintf_P ( result , GUISZ , PSTR ( " %s%s%*_f " ) , result , ( i ) ? " / " : " " , resolution , & input [ i ] ) ;
2022-03-15 14:43:23 +00:00
}
# endif // USE_ENERGY_COLUMN_GUI
return result ;
2020-08-23 17:29:16 +01:00
}
2022-03-15 14:43:23 +00:00
# endif // USE_WEBSERVER
2020-08-23 17:29:16 +01:00
/********************************************************************************************/
2019-09-10 15:18:23 +01:00
bool EnergyTariff1Active ( ) // Off-Peak hours
{
2019-09-25 13:24:49 +01:00
uint8_t dst = 0 ;
2021-06-11 17:14:12 +01:00
if ( IsDst ( ) & & ( Settings - > tariff [ 0 ] [ 1 ] ! = Settings - > tariff [ 1 ] [ 1 ] ) ) {
2019-09-25 13:24:49 +01:00
dst = 1 ;
}
2021-06-11 17:14:12 +01:00
if ( Settings - > tariff [ 0 ] [ dst ] ! = Settings - > tariff [ 1 ] [ dst ] ) {
if ( Settings - > flag3 . energy_weekend & & ( ( RtcTime . day_of_week = = 1 ) | | // CMND_TARIFF
2019-09-29 11:18:09 +01:00
( RtcTime . day_of_week = = 7 ) ) ) {
return true ;
}
2019-09-25 13:24:49 +01:00
uint32_t minutes = MinutesPastMidnight ( ) ;
2021-06-11 17:14:12 +01:00
if ( Settings - > tariff [ 0 ] [ dst ] > Settings - > tariff [ 1 ] [ dst ] ) {
2019-09-29 11:18:09 +01:00
// {"Tariff":{"Off-Peak":{"STD":"22:00","DST":"23:00"},"Standard":{"STD":"06:00","DST":"07:00"},"Weekend":"OFF"}}
2021-06-11 17:14:12 +01:00
return ( ( minutes > = Settings - > tariff [ 0 ] [ dst ] ) | | ( minutes < Settings - > tariff [ 1 ] [ dst ] ) ) ;
2019-09-29 11:18:09 +01:00
} else {
// {"Tariff":{"Off-Peak":{"STD":"00:29","DST":"01:29"},"Standard":{"STD":"07:29","DST":"08:29"},"Weekend":"OFF"}}
2021-06-11 17:14:12 +01:00
return ( ( minutes > = Settings - > tariff [ 0 ] [ dst ] ) & & ( minutes < Settings - > tariff [ 1 ] [ dst ] ) ) ;
2019-09-29 11:18:09 +01:00
}
2019-09-10 15:18:23 +01:00
} else {
return false ;
}
}
2021-09-29 14:33:58 +01:00
void EnergyUpdateToday ( void ) {
2022-04-19 14:44:53 +01:00
Energy . total_sum = 0.0f ;
Energy . yesterday_sum = 0.0f ;
Energy . daily_sum = 0.0f ;
2022-08-26 13:35:52 +01:00
int32_t delta_sum_balanced = 0 ;
2021-10-02 17:29:05 +01:00
2021-09-29 14:33:58 +01:00
for ( uint32_t i = 0 ; i < Energy . phase_count ; i + + ) {
2021-10-14 15:21:35 +01:00
if ( abs ( Energy . kWhtoday_delta [ i ] ) > 1000 ) {
int32_t delta = Energy . kWhtoday_delta [ i ] / 1000 ;
2022-08-26 13:35:52 +01:00
delta_sum_balanced + = delta ;
2021-09-29 14:33:58 +01:00
Energy . kWhtoday_delta [ i ] - = ( delta * 1000 ) ;
Energy . kWhtoday [ i ] + = delta ;
2022-04-16 15:07:42 +01:00
if ( delta < 0 ) { // Export energy
RtcSettings . energy_kWhexport_ph [ i ] + = ( delta * - 1 ) ;
}
2021-09-29 14:33:58 +01:00
}
2019-08-27 16:01:12 +01:00
2021-09-29 14:33:58 +01:00
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 ;
2022-04-20 11:04:18 +01:00
if ( Energy . local_energy_active_export ) {
Energy . export_active [ i ] = ( float ) ( RtcSettings . energy_kWhexport_ph [ i ] ) / 100000 ;
}
2021-09-29 14:33:58 +01:00
Energy . total_sum + = Energy . total [ i ] ;
Energy . yesterday_sum + = ( float ) Settings - > energy_kWhyesterday_ph [ i ] / 100000 ;
Energy . daily_sum + = Energy . daily [ i ] ;
}
2021-09-29 13:53:23 +01:00
2022-08-26 13:35:52 +01:00
if ( delta_sum_balanced > 0 ) {
2022-08-27 11:28:34 +01:00
Energy . daily_sum_import_balanced + = ( float ) delta_sum_balanced / 100000 ;
2022-08-26 13:35:52 +01:00
} else {
2022-08-27 11:28:34 +01:00
Energy . daily_sum_export_balanced + = ( float ) abs ( delta_sum_balanced ) / 100000 ;
2022-08-26 13:35:52 +01:00
}
2019-09-20 21:35:56 +01:00
if ( RtcTime . valid ) { // We calc the difference only if we have a valid RTC time.
2021-09-29 14:33:58 +01:00
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 ) ;
2019-09-20 21:35:56 +01:00
uint32_t return_diff = 0 ;
2020-05-22 16:48:21 +01:00
if ( ! isnan ( Energy . export_active [ 0 ] ) ) {
// return_diff = (uint32_t)(Energy.export_active * 100000) - RtcSettings.energy_usage.last_return_kWhtotal;
// RtcSettings.energy_usage.last_return_kWhtotal = (uint32_t)(Energy.export_active * 100000);
2022-04-19 14:44:53 +01:00
float export_active = 0.0f ;
2020-05-22 16:48:21 +01:00
for ( uint32_t i = 0 ; i < Energy . phase_count ; i + + ) {
if ( ! isnan ( Energy . export_active [ i ] ) ) {
export_active + = Energy . export_active [ i ] ;
}
}
return_diff = ( uint32_t ) ( export_active * 100000 ) - RtcSettings . energy_usage . last_return_kWhtotal ;
RtcSettings . energy_usage . last_return_kWhtotal = ( uint32_t ) ( export_active * 100000 ) ;
2019-09-20 21:35:56 +01:00
}
2019-09-20 13:37:55 +01:00
if ( EnergyTariff1Active ( ) ) { // Tarrif1 = Off-Peak
RtcSettings . energy_usage . usage1_kWhtotal + = energy_diff ;
RtcSettings . energy_usage . return1_kWhtotal + = return_diff ;
} else {
RtcSettings . energy_usage . usage2_kWhtotal + = energy_diff ;
RtcSettings . energy_usage . return2_kWhtotal + = return_diff ;
}
2019-08-27 16:01:12 +01:00
}
2018-02-04 17:09:09 +00:00
}
2021-10-02 17:29:05 +01:00
void EnergyUpdateTotal ( void ) {
// Provide total import active energy as float Energy.import_active[phase] in kWh: 98Wh = 0.098kWh
2019-09-06 17:02:31 +01:00
2021-10-02 17:29:05 +01:00
for ( uint32_t i = 0 ; i < Energy . phase_count ; i + + ) {
2021-11-30 22:05:40 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " NRG: EnergyTotal[%d] %4_f kWh " ) , i , & Energy . import_active [ i ] ) ;
2019-09-03 20:53:20 +01:00
2021-10-02 17:29:05 +01:00
if ( 0 = = Energy . start_energy [ i ] | | ( Energy . import_active [ i ] < Energy . start_energy [ i ] ) ) {
Energy . start_energy [ i ] = Energy . import_active [ i ] ; // Init after restart and handle roll-over if any
}
else if ( Energy . import_active [ i ] ! = Energy . start_energy [ i ] ) {
2021-10-14 15:21:35 +01:00
Energy . kWhtoday [ i ] = ( int32_t ) ( ( Energy . import_active [ i ] - Energy . start_energy [ i ] ) * 100000 ) ;
2021-10-02 17:29:05 +01:00
}
2019-09-18 10:42:28 +01:00
2022-04-19 14:44:53 +01:00
if ( ( Energy . total [ i ] < ( Energy . import_active [ i ] - 0.01f ) ) & & // We subtract a little offset to avoid continuous updates
2021-10-02 17:29:05 +01:00
Settings - > flag3 . hardware_energy_total ) { // SetOption72 - Enable hardware energy total counter as reference (#6561)
2021-10-14 15:21:35 +01:00
RtcSettings . energy_kWhtotal_ph [ i ] = ( int32_t ) ( ( Energy . import_active [ i ] * 100000 ) - Energy . kWhtoday_offset [ i ] - Energy . kWhtoday [ i ] ) ;
2021-10-02 17:29:05 +01:00
Settings - > energy_kWhtotal_ph [ i ] = RtcSettings . energy_kWhtotal_ph [ i ] ;
Energy . total [ i ] = ( float ) ( RtcSettings . energy_kWhtotal_ph [ i ] + Energy . kWhtoday_offset [ i ] + Energy . kWhtoday [ i ] ) / 100000 ;
Settings - > energy_kWhtotal_time = ( ! Energy . kWhtoday_offset [ i ] ) ? LocalTime ( ) : Midnight ( ) ;
// AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: Energy Total updated with hardware value"));
}
2019-09-18 10:42:28 +01:00
}
2021-10-02 17:29:05 +01:00
2019-09-03 20:53:20 +01:00
EnergyUpdateToday ( ) ;
}
2018-05-10 16:21:26 +01:00
/*********************************************************************************************/
2019-09-07 15:31:39 +01:00
void Energy200ms ( void )
2017-12-16 14:51:45 +00:00
{
2021-06-11 17:14:12 +01:00
Energy . power_on = ( TasmotaGlobal . power ! = 0 ) | Settings - > flag . no_power_on_check ; // SetOption21 - Show voltage even if powered off
2018-09-17 19:32:38 +01:00
2019-09-07 15:31:39 +01:00
Energy . fifth_second + + ;
if ( 5 = = Energy . fifth_second ) {
Energy . fifth_second = 0 ;
2017-12-16 14:51:45 +00:00
2019-04-15 17:12:42 +01:00
XnrgCall ( FUNC_ENERGY_EVERY_SECOND ) ;
2018-02-03 22:25:05 +00:00
2017-12-16 14:51:45 +00:00
if ( RtcTime . valid ) {
2020-05-10 11:59:13 +01:00
2021-06-11 17:14:12 +01:00
if ( ! Energy . kWhtoday_offset_init & & ( RtcTime . day_of_year = = Settings - > energy_kWhdoy ) ) {
2021-09-29 14:33:58 +01:00
for ( uint32_t i = 0 ; i < 3 ; i + + ) {
Energy . kWhtoday_offset [ i ] = Settings - > energy_kWhtoday_ph [ i ] ;
}
2020-05-10 11:59:13 +01:00
Energy . kWhtoday_offset_init = true ;
}
2017-12-16 14:51:45 +00:00
if ( LocalTime ( ) = = Midnight ( ) ) {
2021-09-29 14:33:58 +01:00
for ( uint32_t i = 0 ; i < 3 ; i + + ) {
Settings - > energy_kWhyesterday_ph [ i ] = RtcSettings . energy_kWhtoday_ph [ i ] ;
2019-08-27 16:01:12 +01:00
2021-09-29 14:33:58 +01:00
RtcSettings . energy_kWhtotal_ph [ i ] + = RtcSettings . energy_kWhtoday_ph [ i ] ;
Settings - > energy_kWhtotal_ph [ i ] = RtcSettings . energy_kWhtotal_ph [ i ] ;
2020-10-13 23:30:01 +01:00
2022-04-16 15:07:42 +01:00
Settings - > energy_kWhexport_ph [ i ] = RtcSettings . energy_kWhexport_ph [ i ] ;
2021-09-29 14:33:58 +01:00
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 ;
2020-10-13 23:30:01 +01:00
// Energy.kWhtoday_delta = 0; // dont zero this, we need to carry the remainder over to tomorrow
2022-08-26 13:35:52 +01:00
Energy . daily_sum_import_balanced = 0.0 ;
Energy . daily_sum_export_balanced = 0.0 ;
2021-09-29 14:33:58 +01:00
}
2018-02-06 09:06:22 +00:00
EnergyUpdateToday ( ) ;
2019-08-01 14:46:12 +01:00
# if defined(USE_ENERGY_MARGIN_DETECTION) && defined(USE_ENERGY_POWER_LIMIT)
2019-08-16 13:41:02 +01:00
Energy . max_energy_state = 3 ;
2019-08-01 14:46:12 +01:00
# endif // USE_ENERGY_POWER_LIMIT
2017-12-16 14:51:45 +00:00
}
2019-08-01 14:46:12 +01:00
# if defined(USE_ENERGY_MARGIN_DETECTION) && defined(USE_ENERGY_POWER_LIMIT)
2021-06-11 17:14:12 +01:00
if ( ( RtcTime . hour = = Settings - > energy_max_energy_start ) & & ( 3 = = Energy . max_energy_state ) ) {
2019-08-16 13:41:02 +01:00
Energy . max_energy_state = 0 ;
2017-12-16 14:51:45 +00:00
}
2019-08-01 14:46:12 +01:00
# endif // USE_ENERGY_POWER_LIMIT
2017-12-16 14:51:45 +00:00
}
}
2019-09-07 15:31:39 +01:00
XnrgCall ( FUNC_EVERY_200_MSECOND ) ;
2017-12-16 14:51:45 +00:00
}
2018-11-14 13:32:09 +00:00
void EnergySaveState ( void )
2017-12-16 14:51:45 +00:00
{
2021-06-11 17:14:12 +01:00
Settings - > energy_kWhdoy = ( RtcTime . valid ) ? RtcTime . day_of_year : 0 ;
2019-08-27 16:01:12 +01:00
2021-09-29 14:33:58 +01:00
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 ] ;
2022-04-16 15:07:42 +01:00
Settings - > energy_kWhexport_ph [ i ] = RtcSettings . energy_kWhexport_ph [ i ] ;
2021-09-29 14:33:58 +01:00
}
2019-08-27 16:01:12 +01:00
2021-06-11 17:14:12 +01:00
Settings - > energy_usage = RtcSettings . energy_usage ;
2017-12-16 14:51:45 +00:00
}
2019-08-01 14:46:12 +01:00
# ifdef USE_ENERGY_MARGIN_DETECTION
2019-01-28 13:08:33 +00:00
bool EnergyMargin ( bool type , uint16_t margin , uint16_t value , bool & flag , bool & save_flag )
2017-12-16 14:51:45 +00:00
{
2019-01-28 13:08:33 +00:00
bool change ;
2017-12-16 14:51:45 +00:00
2018-02-06 09:06:22 +00:00
if ( ! margin ) return false ;
2017-12-16 14:51:45 +00:00
change = save_flag ;
if ( type ) {
flag = ( value > margin ) ;
} else {
flag = ( value < margin ) ;
}
save_flag = flag ;
return ( change ! = save_flag ) ;
}
2022-10-31 14:02:29 +00:00
void EnergyMarginCheck ( void ) {
if ( ! Energy . phase_count | | ( TasmotaGlobal . uptime < 8 ) ) { return ; }
2019-08-16 13:41:02 +01:00
if ( Energy . power_steady_counter ) {
Energy . power_steady_counter - - ;
2017-12-16 14:51:45 +00:00
return ;
}
2020-07-20 15:26:32 +01:00
bool jsonflg = false ;
2020-07-20 15:41:05 +01:00
Response_P ( PSTR ( " { \" " D_RSLT_MARGINS " \" :{ " ) ) ;
2020-07-20 15:26:32 +01:00
2021-04-06 14:23:07 +01:00
int16_t power_diff [ ENERGY_MAX_PHASES ] = { 0 } ;
2020-08-23 17:29:16 +01:00
for ( uint32_t phase = 0 ; phase < Energy . phase_count ; phase + + ) {
uint16_t active_power = ( uint16_t ) ( Energy . active_power [ phase ] ) ;
2021-01-23 15:26:23 +00:00
// AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: APower %d, HPower0 %d, HPower1 %d, HPower2 %d"), active_power, Energy.power_history[phase][0], Energy.power_history[phase][1], Energy.power_history[phase][2]);
2020-10-28 16:32:07 +00:00
2021-06-11 17:14:12 +01:00
if ( Settings - > energy_power_delta [ phase ] ) {
2020-08-23 17:29:16 +01:00
power_diff [ phase ] = active_power - Energy . power_history [ phase ] [ 0 ] ;
uint16_t delta = abs ( power_diff [ phase ] ) ;
bool threshold_met = false ;
if ( delta > 0 ) {
2021-06-11 17:14:12 +01:00
if ( Settings - > energy_power_delta [ phase ] < 101 ) { // 1..100 = Percentage
2020-08-23 17:29:16 +01:00
uint16_t min_power = ( Energy . power_history [ phase ] [ 0 ] > active_power ) ? active_power : Energy . power_history [ phase ] [ 0 ] ;
if ( 0 = = min_power ) { min_power + + ; } // Fix divide by 0 exception (#6741)
delta = ( delta * 100 ) / min_power ;
2022-02-07 21:33:09 +00:00
if ( delta > = Settings - > energy_power_delta [ phase ] ) {
2020-08-23 17:29:16 +01:00
threshold_met = true ;
}
} else { // 101..32000 = Absolute
2022-02-07 21:33:09 +00:00
if ( delta > = ( Settings - > energy_power_delta [ phase ] - 100 ) ) {
2020-08-23 17:29:16 +01:00
threshold_met = true ;
}
2020-01-17 09:12:57 +00:00
}
}
2020-08-23 17:29:16 +01:00
if ( threshold_met ) {
Energy . power_history [ phase ] [ 1 ] = active_power ; // We only want one report so reset history
Energy . power_history [ phase ] [ 2 ] = active_power ;
jsonflg = true ;
} else {
power_diff [ phase ] = 0 ;
2019-10-25 17:16:09 +01:00
}
2018-03-15 16:33:35 +00:00
}
2020-08-23 17:29:16 +01:00
Energy . power_history [ phase ] [ 0 ] = Energy . power_history [ phase ] [ 1 ] ; // Shift in history every second allowing power changes to settle for up to three seconds
Energy . power_history [ phase ] [ 1 ] = Energy . power_history [ phase ] [ 2 ] ;
Energy . power_history [ phase ] [ 2 ] = active_power ;
}
if ( jsonflg ) {
2021-09-29 14:33:58 +01:00
float power_diff_f [ Energy . phase_count ] ;
for ( uint32_t phase = 0 ; phase < Energy . phase_count ; phase + + ) {
power_diff_f [ phase ] = power_diff [ phase ] ;
}
2022-03-15 14:43:23 +00:00
char value_chr [ TOPSZ ] ;
ResponseAppend_P ( PSTR ( " \" " D_CMND_POWERDELTA " \" :%s " ) , EnergyFormat ( value_chr , power_diff_f , 0 ) ) ;
2018-03-15 16:33:35 +00:00
}
2020-08-23 17:29:16 +01:00
uint16_t energy_power_u = ( uint16_t ) ( Energy . active_power [ 0 ] ) ;
2018-03-15 16:33:35 +00:00
2021-06-11 17:14:12 +01:00
if ( Energy . power_on & & ( Settings - > energy_min_power | | Settings - > energy_max_power | | Settings - > energy_min_voltage | | Settings - > energy_max_voltage | | Settings - > energy_min_current | | Settings - > energy_max_current ) ) {
2019-10-18 15:53:30 +01:00
uint16_t energy_voltage_u = ( uint16_t ) ( Energy . voltage [ 0 ] ) ;
uint16_t energy_current_u = ( uint16_t ) ( Energy . current [ 0 ] * 1000 ) ;
2017-12-16 14:51:45 +00:00
2019-08-08 15:24:11 +01:00
DEBUG_DRIVER_LOG ( PSTR ( " NRG: W %d, U %d, I %d " ) , energy_power_u , energy_voltage_u , energy_current_u ) ;
2017-12-16 14:51:45 +00:00
2019-10-18 15:53:30 +01:00
bool flag ;
2021-06-11 17:14:12 +01:00
if ( EnergyMargin ( false , Settings - > energy_min_power , energy_power_u , flag , Energy . min_power_flag ) ) {
2019-03-23 16:00:59 +00:00
ResponseAppend_P ( PSTR ( " %s \" " D_CMND_POWERLOW " \" : \" %s \" " ) , ( jsonflg ) ? " , " : " " , GetStateText ( flag ) ) ;
2019-01-28 13:08:33 +00:00
jsonflg = true ;
2017-12-16 14:51:45 +00:00
}
2021-06-11 17:14:12 +01:00
if ( EnergyMargin ( true , Settings - > energy_max_power , energy_power_u , flag , Energy . max_power_flag ) ) {
2019-03-23 16:00:59 +00:00
ResponseAppend_P ( PSTR ( " %s \" " D_CMND_POWERHIGH " \" : \" %s \" " ) , ( jsonflg ) ? " , " : " " , GetStateText ( flag ) ) ;
2019-01-28 13:08:33 +00:00
jsonflg = true ;
2017-12-16 14:51:45 +00:00
}
2021-06-11 17:14:12 +01:00
if ( EnergyMargin ( false , Settings - > energy_min_voltage , energy_voltage_u , flag , Energy . min_voltage_flag ) ) {
2019-03-23 16:00:59 +00:00
ResponseAppend_P ( PSTR ( " %s \" " D_CMND_VOLTAGELOW " \" : \" %s \" " ) , ( jsonflg ) ? " , " : " " , GetStateText ( flag ) ) ;
2019-01-28 13:08:33 +00:00
jsonflg = true ;
2017-12-16 14:51:45 +00:00
}
2021-06-11 17:14:12 +01:00
if ( EnergyMargin ( true , Settings - > energy_max_voltage , energy_voltage_u , flag , Energy . max_voltage_flag ) ) {
2019-03-23 16:00:59 +00:00
ResponseAppend_P ( PSTR ( " %s \" " D_CMND_VOLTAGEHIGH " \" : \" %s \" " ) , ( jsonflg ) ? " , " : " " , GetStateText ( flag ) ) ;
2019-01-28 13:08:33 +00:00
jsonflg = true ;
2017-12-16 14:51:45 +00:00
}
2021-06-11 17:14:12 +01:00
if ( EnergyMargin ( false , Settings - > energy_min_current , energy_current_u , flag , Energy . min_current_flag ) ) {
2019-10-06 10:28:57 +01:00
ResponseAppend_P ( PSTR ( " %s \" " D_CMND_CURRENTLOW " \" : \" %s \" " ) , ( jsonflg ) ? " , " : " " , GetStateText ( flag ) ) ;
2019-01-28 13:08:33 +00:00
jsonflg = true ;
2017-12-16 14:51:45 +00:00
}
2021-06-11 17:14:12 +01:00
if ( EnergyMargin ( true , Settings - > energy_max_current , energy_current_u , flag , Energy . max_current_flag ) ) {
2019-10-06 10:28:57 +01:00
ResponseAppend_P ( PSTR ( " %s \" " D_CMND_CURRENTHIGH " \" : \" %s \" " ) , ( jsonflg ) ? " , " : " " , GetStateText ( flag ) ) ;
2019-01-28 13:08:33 +00:00
jsonflg = true ;
2017-12-16 14:51:45 +00:00
}
2020-07-20 15:26:32 +01:00
}
if ( jsonflg ) {
2020-07-20 15:41:05 +01:00
ResponseJsonEndEnd ( ) ;
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( TELE , PSTR ( D_RSLT_MARGINS ) , MQTT_TELE_RETAIN ) ;
2020-07-20 15:26:32 +01:00
EnergyMqttShow ( ) ;
2017-12-16 14:51:45 +00:00
}
2019-08-01 14:46:12 +01:00
# ifdef USE_ENERGY_POWER_LIMIT
2017-12-16 14:51:45 +00:00
// Max Power
2021-06-11 17:14:12 +01:00
if ( Settings - > energy_max_power_limit ) {
if ( Energy . active_power [ 0 ] > Settings - > energy_max_power_limit ) {
2019-08-16 13:41:02 +01:00
if ( ! Energy . mplh_counter ) {
2021-06-11 17:14:12 +01:00
Energy . mplh_counter = Settings - > energy_max_power_limit_hold ;
2017-12-16 14:51:45 +00:00
} else {
2019-08-16 13:41:02 +01:00
Energy . mplh_counter - - ;
if ( ! Energy . mplh_counter ) {
2019-10-22 17:34:41 +01:00
ResponseTime_P ( PSTR ( " , \" " D_JSON_MAXPOWERREACHED " \" :%d} " ) , energy_power_u ) ;
2021-04-07 14:07:05 +01:00
MqttPublishPrefixTopicRulesProcess_P ( STAT , S_RSLT_WARNING ) ;
2017-12-16 14:51:45 +00:00
EnergyMqttShow ( ) ;
2019-09-03 20:15:36 +01:00
SetAllPower ( POWER_ALL_OFF , SRC_MAXPOWER ) ;
2019-08-16 13:41:02 +01:00
if ( ! Energy . mplr_counter ) {
2021-06-11 17:14:12 +01:00
Energy . mplr_counter = Settings - > param [ P_MAX_POWER_RETRY ] + 1 ; // SetOption33 - Max Power Retry count
2017-12-16 14:51:45 +00:00
}
2021-06-11 17:14:12 +01:00
Energy . mplw_counter = Settings - > energy_max_power_limit_window ;
2017-12-16 14:51:45 +00:00
}
}
}
2021-06-11 17:14:12 +01:00
else if ( TasmotaGlobal . power & & ( energy_power_u < = Settings - > energy_max_power_limit ) ) {
2019-08-16 13:41:02 +01:00
Energy . mplh_counter = 0 ;
Energy . mplr_counter = 0 ;
Energy . mplw_counter = 0 ;
2017-12-16 14:51:45 +00:00
}
2020-10-28 18:03:39 +00:00
if ( ! TasmotaGlobal . power ) {
2019-08-16 13:41:02 +01:00
if ( Energy . mplw_counter ) {
Energy . mplw_counter - - ;
2017-12-16 14:51:45 +00:00
} else {
2019-08-16 13:41:02 +01:00
if ( Energy . mplr_counter ) {
Energy . mplr_counter - - ;
if ( Energy . mplr_counter ) {
2019-09-04 17:06:34 +01:00
ResponseTime_P ( PSTR ( " , \" " D_JSON_POWERMONITOR " \" : \" %s \" } " ) , GetStateText ( 1 ) ) ;
2021-04-07 14:07:05 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_STAT , PSTR ( D_JSON_POWERMONITOR ) ) ;
2019-09-04 11:20:04 +01:00
RestorePower ( true , SRC_MAXPOWER ) ;
2017-12-16 14:51:45 +00:00
} else {
2019-09-04 17:06:34 +01:00
ResponseTime_P ( PSTR ( " , \" " D_JSON_MAXPOWERREACHEDRETRY " \" : \" %s \" } " ) , GetStateText ( 0 ) ) ;
2021-04-07 14:07:05 +01:00
MqttPublishPrefixTopicRulesProcess_P ( STAT , S_RSLT_WARNING ) ;
2017-12-16 14:51:45 +00:00
EnergyMqttShow ( ) ;
2020-02-09 15:21:48 +00:00
SetAllPower ( POWER_ALL_OFF , SRC_MAXPOWER ) ;
2017-12-16 14:51:45 +00:00
}
}
}
}
}
// Max Energy
2021-06-11 17:14:12 +01:00
if ( Settings - > energy_max_energy ) {
2021-09-29 14:33:58 +01:00
uint16_t energy_daily_u = ( uint16_t ) ( Energy . daily_sum * 1000 ) ;
2021-06-11 17:14:12 +01:00
if ( ! Energy . max_energy_state & & ( RtcTime . hour = = Settings - > energy_max_energy_start ) ) {
2019-08-16 13:41:02 +01:00
Energy . max_energy_state = 1 ;
2019-09-04 17:06:34 +01:00
ResponseTime_P ( PSTR ( " , \" " D_JSON_ENERGYMONITOR " \" : \" %s \" } " ) , GetStateText ( 1 ) ) ;
2021-04-07 14:07:05 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_STAT , PSTR ( D_JSON_ENERGYMONITOR ) ) ;
2019-09-04 11:20:04 +01:00
RestorePower ( true , SRC_MAXENERGY ) ;
2017-12-16 14:51:45 +00:00
}
2021-06-11 17:14:12 +01:00
else if ( ( 1 = = Energy . max_energy_state ) & & ( energy_daily_u > = Settings - > energy_max_energy ) ) {
2019-08-16 13:41:02 +01:00
Energy . max_energy_state = 2 ;
2021-09-29 14:33:58 +01:00
ResponseTime_P ( PSTR ( " , \" " D_JSON_MAXENERGYREACHED " \" :%3_f} " ) , & Energy . daily_sum ) ;
2021-04-07 14:07:05 +01:00
MqttPublishPrefixTopicRulesProcess_P ( STAT , S_RSLT_WARNING ) ;
2017-12-16 14:51:45 +00:00
EnergyMqttShow ( ) ;
2019-09-03 20:15:36 +01:00
SetAllPower ( POWER_ALL_OFF , SRC_MAXENERGY ) ;
2017-12-16 14:51:45 +00:00
}
}
2019-08-01 14:46:12 +01:00
# endif // USE_ENERGY_POWER_LIMIT
2017-12-16 14:51:45 +00:00
}
2018-11-14 13:32:09 +00:00
void EnergyMqttShow ( void )
2017-12-16 14:51:45 +00:00
{
// {"Time":"2017-12-16T11:48:55","ENERGY":{"Total":0.212,"Yesterday":0.000,"Today":0.014,"Period":2.0,"Power":22.0,"Factor":1.00,"Voltage":213.6,"Current":0.100}}
2020-10-29 12:37:09 +00:00
int tele_period_save = TasmotaGlobal . tele_period ;
TasmotaGlobal . tele_period = 2 ;
2020-10-30 11:29:48 +00:00
ResponseClear ( ) ;
2019-09-04 17:06:34 +01:00
ResponseAppendTime ( ) ;
2019-01-28 13:08:33 +00:00
EnergyShow ( true ) ;
2020-10-29 12:37:09 +00:00
TasmotaGlobal . tele_period = tele_period_save ;
Add support for Shelly 1PM Template
Add support for Shelly 1PM Template {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18} (#5716)
2019-05-13 17:26:07 +01:00
ResponseJsonEnd ( ) ;
2019-11-10 16:40:37 +00:00
MqttPublishTeleSensor ( ) ;
2017-12-16 14:51:45 +00:00
}
2019-08-01 14:46:12 +01:00
# endif // USE_ENERGY_MARGIN_DETECTION
2017-12-16 14:51:45 +00:00
2019-11-20 19:53:12 +00:00
void EnergyEverySecond ( void )
2019-05-14 16:46:40 +01:00
{
2019-09-21 16:10:52 +01:00
// Overtemp check
2021-04-09 09:04:37 +01:00
if ( Energy . use_overtemp & & TasmotaGlobal . global_update ) {
2021-06-11 17:14:12 +01:00
if ( TasmotaGlobal . power & & ! isnan ( TasmotaGlobal . temperature_celsius ) & & ( TasmotaGlobal . temperature_celsius > ( float ) Settings - > param [ P_OVER_TEMP ] ) ) { // SetOption42 Device overtemp, turn off relays
2020-06-12 15:51:21 +01:00
2021-03-06 14:04:16 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " NRG: Temperature %1_f " ) , & TasmotaGlobal . temperature_celsius ) ;
2020-06-12 15:51:21 +01:00
2019-05-14 16:46:40 +01:00
SetAllPower ( POWER_ALL_OFF , SRC_OVERTEMP ) ;
}
}
2019-09-21 16:10:52 +01:00
// Invalid data reset
2021-03-06 14:04:16 +00:00
if ( TasmotaGlobal . uptime > ENERGY_WATCHDOG ) {
2021-03-05 17:28:07 +00:00
uint32_t data_valid = Energy . phase_count ;
for ( uint32_t i = 0 ; i < Energy . phase_count ; i + + ) {
if ( Energy . data_valid [ i ] < = ENERGY_WATCHDOG ) {
Energy . data_valid [ i ] + + ;
if ( Energy . data_valid [ i ] > ENERGY_WATCHDOG ) {
// Reset energy registers
Energy . voltage [ i ] = 0 ;
Energy . current [ i ] = 0 ;
Energy . active_power [ i ] = 0 ;
if ( ! isnan ( Energy . apparent_power [ i ] ) ) { Energy . apparent_power [ i ] = 0 ; }
if ( ! isnan ( Energy . reactive_power [ i ] ) ) { Energy . reactive_power [ i ] = 0 ; }
if ( ! isnan ( Energy . frequency [ i ] ) ) { Energy . frequency [ i ] = 0 ; }
if ( ! isnan ( Energy . power_factor [ i ] ) ) { Energy . power_factor [ i ] = 0 ; }
if ( ! isnan ( Energy . export_active [ i ] ) ) { Energy . export_active [ i ] = 0 ; }
data_valid - - ;
}
2019-09-21 16:10:52 +01:00
}
2019-05-30 11:45:02 +01:00
}
2021-03-05 17:28:07 +00:00
if ( ! data_valid ) {
//Energy.start_energy = 0;
2021-03-06 14:04:16 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " NRG: Energy reset by invalid data " ) ) ;
2019-09-21 16:10:52 +01:00
2021-03-05 17:28:07 +00:00
XnrgCall ( FUNC_ENERGY_RESET ) ;
}
2019-09-21 16:10:52 +01:00
}
# ifdef USE_ENERGY_MARGIN_DETECTION
EnergyMarginCheck ( ) ;
# endif // USE_ENERGY_MARGIN_DETECTION
2019-05-14 16:46:40 +01:00
}
2017-12-16 14:51:45 +00:00
/*********************************************************************************************\
* Commands
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2021-10-02 17:29:05 +01:00
void ResponseCmndEnergyTotalYesterdayToday ( void ) {
2022-03-15 14:43:23 +00:00
char value_chr [ TOPSZ ] ; // Used by EnergyFormatIndex
char value2_chr [ TOPSZ ] ;
char value3_chr [ TOPSZ ] ;
2021-09-29 14:33:58 +01:00
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 ;
2022-04-16 15:07:42 +01:00
if ( Energy . local_energy_active_export ) {
Energy . export_active [ i ] = ( float ) ( RtcSettings . energy_kWhexport_ph [ i ] ) / 100000 ;
}
2021-09-29 14:33:58 +01:00
}
2022-04-16 15:07:42 +01:00
Response_P ( PSTR ( " { \" %s \" :{ \" " D_JSON_TOTAL " \" :%s, \" " D_JSON_YESTERDAY " \" :%s, \" " D_JSON_TODAY " \" :%s " ) ,
2021-09-29 14:33:58 +01:00
XdrvMailbox . command ,
2022-03-15 14:43:23 +00:00
EnergyFormat ( value_chr , Energy . total , Settings - > flag2 . energy_resolution ) ,
EnergyFormat ( value2_chr , energy_yesterday_ph , Settings - > flag2 . energy_resolution ) ,
EnergyFormat ( value3_chr , Energy . daily , Settings - > flag2 . energy_resolution ) ) ;
2022-04-16 15:07:42 +01:00
if ( Energy . local_energy_active_export ) {
ResponseAppend_P ( PSTR ( " , \" " D_JSON_EXPORT_ACTIVE " \" :%s " ) ,
EnergyFormat ( value_chr , Energy . export_active , Settings - > flag2 . energy_resolution ) ) ;
}
ResponseJsonEndEnd ( ) ;
2021-09-29 14:33:58 +01:00
}
void CmndEnergyTotal ( void ) {
uint32_t values [ 2 ] = { 0 } ;
uint32_t params = ParseParameters ( 2 , values ) ;
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = Energy . phase_count ) & & ( params > 0 ) ) {
uint32_t phase = XdrvMailbox . index - 1 ;
// Reset Energy Total
2021-10-02 17:29:05 +01:00
RtcSettings . energy_kWhtotal_ph [ phase ] = values [ 0 ] * 100 ;
2021-09-29 14:33:58 +01:00
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 ) ;
}
2021-10-02 17:29:05 +01:00
ResponseCmndEnergyTotalYesterdayToday ( ) ;
2021-09-29 14:33:58 +01:00
}
void CmndEnergyYesterday ( void ) {
uint32_t values [ 2 ] = { 0 } ;
uint32_t params = ParseParameters ( 2 , values ) ;
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = Energy . phase_count ) & & ( params > 0 ) ) {
uint32_t phase = XdrvMailbox . index - 1 ;
// Reset Energy Yesterday
2021-10-02 17:29:05 +01:00
Settings - > energy_kWhyesterday_ph [ phase ] = values [ 0 ] * 100 ;
2021-09-29 14:33:58 +01:00
if ( params > 1 ) {
Settings - > energy_kWhtotal_time = values [ 1 ] ;
}
}
2021-10-02 17:29:05 +01:00
ResponseCmndEnergyTotalYesterdayToday ( ) ;
2021-09-29 14:33:58 +01:00
}
2022-04-16 15:07:42 +01:00
2021-09-29 14:33:58 +01:00
void CmndEnergyToday ( void ) {
uint32_t values [ 2 ] = { 0 } ;
uint32_t params = ParseParameters ( 2 , values ) ;
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = Energy . phase_count ) & & ( params > 0 ) ) {
uint32_t phase = XdrvMailbox . index - 1 ;
// Reset Energy Today
2021-10-02 17:29:05 +01:00
Energy . kWhtoday_offset [ phase ] = values [ 0 ] * 100 ;
2021-09-29 14:33:58 +01:00
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 ] ;
}
2021-10-02 17:29:05 +01:00
else if ( ! RtcSettings . energy_kWhtotal_ph [ phase ] & & ! Energy . kWhtoday_offset [ phase ] ) {
Settings - > energy_kWhtotal_time = LocalTime ( ) ;
2021-09-29 14:33:58 +01:00
}
}
2021-10-02 17:29:05 +01:00
ResponseCmndEnergyTotalYesterdayToday ( ) ;
}
2022-04-16 15:07:42 +01:00
void CmndEnergyExportActive ( void ) {
if ( Energy . local_energy_active_export ) {
// EnergyExportActive1 24
// EnergyExportActive1 24,1650111291
uint32_t values [ 2 ] = { 0 } ;
uint32_t params = ParseParameters ( 2 , values ) ;
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = Energy . phase_count ) & & ( params > 0 ) ) {
uint32_t phase = XdrvMailbox . index - 1 ;
// Reset Energy Export Active
RtcSettings . energy_kWhexport_ph [ phase ] = values [ 0 ] * 100 ;
Settings - > energy_kWhexport_ph [ phase ] = RtcSettings . energy_kWhexport_ph [ phase ] ;
if ( params > 1 ) {
Settings - > energy_kWhtotal_time = values [ 1 ] ;
}
}
ResponseCmndEnergyTotalYesterdayToday ( ) ;
}
}
2021-10-02 17:29:05 +01:00
void ResponseCmndEnergyUsageExport ( void ) {
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_USAGE " \" :[%*_f,%*_f], \" " D_JSON_EXPORT " \" :[%*_f,%*_f]}} " ) ,
XdrvMailbox . command ,
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 CmndEnergyUsage ( void ) {
uint32_t values [ 2 ] = { 0 } ;
uint32_t params = ParseParameters ( 2 , values ) ;
if ( params > 0 ) {
// Reset energy_usage.usage totals
RtcSettings . energy_usage . usage1_kWhtotal = values [ 0 ] * 100 ;
if ( params > 1 ) {
RtcSettings . energy_usage . usage2_kWhtotal = values [ 1 ] * 100 ;
}
Settings - > energy_usage . usage1_kWhtotal = RtcSettings . energy_usage . usage1_kWhtotal ;
Settings - > energy_usage . usage2_kWhtotal = RtcSettings . energy_usage . usage2_kWhtotal ;
}
ResponseCmndEnergyUsageExport ( ) ;
}
void CmndEnergyExport ( void ) {
uint32_t values [ 2 ] = { 0 } ;
uint32_t params = ParseParameters ( 2 , values ) ;
if ( params > 0 ) {
// Reset energy_usage.return totals
RtcSettings . energy_usage . return1_kWhtotal = values [ 0 ] * 100 ;
if ( params > 1 ) {
RtcSettings . energy_usage . return2_kWhtotal = values [ 1 ] * 100 ;
}
Settings - > energy_usage . return1_kWhtotal = RtcSettings . energy_usage . return1_kWhtotal ;
Settings - > energy_usage . return2_kWhtotal = RtcSettings . energy_usage . return2_kWhtotal ;
}
ResponseCmndEnergyUsageExport ( ) ;
2021-09-29 14:33:58 +01:00
}
2021-03-21 16:51:57 +00:00
void CmndTariff ( void ) {
2019-09-25 13:24:49 +01:00
// 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
// Tariffx 1320, 1380 = minutes and also 22:00, 23:00
// Tariffx 22, 23 = hours and also 22:00, 23:00
2019-09-10 15:18:23 +01:00
// Tariff9 0/1
2019-09-10 17:04:56 +01:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = 2 ) ) {
2019-09-25 13:24:49 +01:00
uint32_t tariff = XdrvMailbox . index - 1 ;
2019-09-10 17:04:56 +01:00
uint32_t time_type = 0 ;
2019-09-25 13:24:49 +01:00
char * p ;
char * str = strtok_r ( XdrvMailbox . data , " , " , & p ) ; // 23:15, 22:30
while ( ( str ! = nullptr ) & & ( time_type < 2 ) ) {
char * q ;
uint32_t value = strtol ( str , & q , 10 ) ; // 23 or 22
2021-06-11 17:14:12 +01:00
Settings - > tariff [ tariff ] [ time_type ] = value ;
2019-09-25 13:24:49 +01:00
if ( value < 24 ) { // Below 24 is hours
2021-06-11 17:14:12 +01:00
Settings - > tariff [ tariff ] [ time_type ] * = 60 ; // Multiply hours by 60 minutes
2019-09-25 13:24:49 +01:00
char * minute = strtok_r ( nullptr , " : " , & q ) ;
if ( minute ) {
value = strtol ( minute , nullptr , 10 ) ; // 15 or 30
if ( value > 59 ) {
value = 59 ;
}
2021-06-11 17:14:12 +01:00
Settings - > tariff [ tariff ] [ time_type ] + = value ;
2019-09-25 13:24:49 +01:00
}
}
2021-06-11 17:14:12 +01:00
if ( Settings - > tariff [ tariff ] [ time_type ] > 1439 ) {
Settings - > tariff [ tariff ] [ time_type ] = 1439 ; // Max is 23:59
2019-09-10 17:04:56 +01:00
}
str = strtok_r ( nullptr , " , " , & p ) ;
2019-09-25 13:24:49 +01:00
time_type + + ;
2019-08-27 16:01:12 +01:00
}
}
2019-09-10 15:18:23 +01:00
else if ( XdrvMailbox . index = = 9 ) {
2021-06-11 17:14:12 +01:00
Settings - > flag3 . energy_weekend = XdrvMailbox . payload & 1 ; // CMND_TARIFF
2019-08-27 16:01:12 +01:00
}
2019-09-25 13:24:49 +01:00
Response_P ( PSTR ( " { \" %s \" :{ \" Off-Peak \" :{ \" STD \" : \" %s \" , \" DST \" : \" %s \" }, \" Standard \" :{ \" STD \" : \" %s \" , \" DST \" : \" %s \" }, \" Weekend \" : \" %s \" }} " ) ,
2019-09-10 15:18:23 +01:00
XdrvMailbox . command ,
2021-06-11 17:14:12 +01:00
GetMinuteTime ( Settings - > tariff [ 0 ] [ 0 ] ) . c_str ( ) , GetMinuteTime ( Settings - > tariff [ 0 ] [ 1 ] ) . c_str ( ) ,
GetMinuteTime ( Settings - > tariff [ 1 ] [ 0 ] ) . c_str ( ) , GetMinuteTime ( Settings - > tariff [ 1 ] [ 1 ] ) . c_str ( ) ,
GetStateText ( Settings - > flag3 . energy_weekend ) ) ; // CMND_TARIFF
2019-08-27 16:01:12 +01:00
}
2022-10-30 10:15:17 +00:00
uint32_t EnergyGetCalibration ( uint32_t chan , uint32_t cal_type ) {
uint32_t channel = ( ( 1 = = chan ) & & ( 2 = = Energy . phase_count ) ) ? 1 : 0 ;
if ( channel ) {
switch ( cal_type ) {
case ENERGY_POWER_CALIBRATION : return Settings - > energy_power_calibration2 ;
case ENERGY_VOLTAGE_CALIBRATION : return Settings - > energy_voltage_calibration2 ;
case ENERGY_CURRENT_CALIBRATION : return Settings - > energy_current_calibration2 ;
}
} else {
switch ( cal_type ) {
case ENERGY_POWER_CALIBRATION : return Settings - > energy_power_calibration ;
case ENERGY_VOLTAGE_CALIBRATION : return Settings - > energy_voltage_calibration ;
case ENERGY_CURRENT_CALIBRATION : return Settings - > energy_current_calibration ;
}
}
return Settings - > energy_frequency_calibration ;
}
2022-10-29 18:08:06 +01:00
void EnergyCommandCalSetResponse ( uint32_t cal_type ) {
2022-10-30 11:20:56 +00:00
if ( XdrvMailbox . payload > 99 ) {
2022-10-29 18:08:06 +01:00
uint32_t channel = ( ( 2 = = XdrvMailbox . index ) & & ( 2 = = Energy . phase_count ) ) ? 1 : 0 ;
if ( channel ) {
switch ( cal_type ) {
2022-10-30 10:15:17 +00:00
case ENERGY_POWER_CALIBRATION : Settings - > energy_power_calibration2 = XdrvMailbox . payload ; break ;
case ENERGY_VOLTAGE_CALIBRATION : Settings - > energy_voltage_calibration2 = XdrvMailbox . payload ; break ;
case ENERGY_CURRENT_CALIBRATION : Settings - > energy_current_calibration2 = XdrvMailbox . payload ; break ;
case ENERGY_FREQUENCY_CALIBRATION : Settings - > energy_frequency_calibration = XdrvMailbox . payload ; break ;
2022-10-29 18:08:06 +01:00
}
} else {
switch ( cal_type ) {
2022-10-30 10:15:17 +00:00
case ENERGY_POWER_CALIBRATION : Settings - > energy_power_calibration = XdrvMailbox . payload ; break ;
case ENERGY_VOLTAGE_CALIBRATION : Settings - > energy_voltage_calibration = XdrvMailbox . payload ; break ;
case ENERGY_CURRENT_CALIBRATION : Settings - > energy_current_calibration = XdrvMailbox . payload ; break ;
case ENERGY_FREQUENCY_CALIBRATION : Settings - > energy_frequency_calibration = XdrvMailbox . payload ; break ;
2022-10-29 18:08:06 +01:00
}
}
}
2022-10-30 10:15:17 +00:00
if ( ENERGY_FREQUENCY_CALIBRATION = = cal_type ) {
2022-10-29 18:08:06 +01:00
ResponseAppend_P ( PSTR ( " %d} " ) , Settings - > energy_frequency_calibration ) ;
} else {
if ( 2 = = Energy . phase_count ) {
2022-10-30 10:15:17 +00:00
ResponseAppend_P ( PSTR ( " [%d,%d]} " ) , EnergyGetCalibration ( 0 , cal_type ) , EnergyGetCalibration ( 1 , cal_type ) ) ;
2022-10-29 18:08:06 +01:00
} else {
2022-10-30 10:15:17 +00:00
ResponseAppend_P ( PSTR ( " %d} " ) , EnergyGetCalibration ( 0 , cal_type ) ) ;
2022-10-29 18:08:06 +01:00
}
}
}
void EnergyCommandCalResponse ( uint32_t cal_type ) {
Response_P ( PSTR ( " { \" %s \" : " ) , XdrvMailbox . command ) ;
EnergyCommandCalSetResponse ( cal_type ) ;
}
void EnergyCommandSetCalResponse ( uint32_t cal_type ) {
Response_P ( PSTR ( " { \" %sCal \" : " ) , XdrvMailbox . command ) ;
EnergyCommandCalSetResponse ( cal_type ) ;
}
2021-03-21 16:51:57 +00:00
void CmndPowerCal ( void ) {
2019-08-16 13:41:02 +01:00
Energy . command_code = CMND_POWERCAL ;
2019-08-01 14:46:12 +01:00
if ( XnrgCall ( FUNC_COMMAND ) ) { // microseconds
2022-10-30 10:15:17 +00:00
EnergyCommandCalResponse ( ENERGY_POWER_CALIBRATION ) ;
2018-09-04 15:22:34 +01:00
}
2019-08-01 14:46:12 +01:00
}
2021-03-21 16:51:57 +00:00
void CmndVoltageCal ( void ) {
2019-08-16 13:41:02 +01:00
Energy . command_code = CMND_VOLTAGECAL ;
2019-08-01 14:46:12 +01:00
if ( XnrgCall ( FUNC_COMMAND ) ) { // microseconds
2022-10-30 10:15:17 +00:00
EnergyCommandCalResponse ( ENERGY_VOLTAGE_CALIBRATION ) ;
2018-09-04 15:22:34 +01:00
}
2019-08-01 14:46:12 +01:00
}
2021-03-21 16:51:57 +00:00
void CmndCurrentCal ( void ) {
2019-08-16 13:41:02 +01:00
Energy . command_code = CMND_CURRENTCAL ;
2019-08-01 14:46:12 +01:00
if ( XnrgCall ( FUNC_COMMAND ) ) { // microseconds
2022-10-30 10:15:17 +00:00
EnergyCommandCalResponse ( ENERGY_CURRENT_CALIBRATION ) ;
2017-12-16 14:51:45 +00:00
}
2019-08-01 14:46:12 +01:00
}
2021-03-21 16:51:57 +00:00
void CmndFrequencyCal ( void ) {
Energy . command_code = CMND_FREQUENCYCAL ;
if ( XnrgCall ( FUNC_COMMAND ) ) { // microseconds
2022-10-30 10:15:17 +00:00
EnergyCommandCalResponse ( ENERGY_FREQUENCY_CALIBRATION ) ;
2021-03-21 16:51:57 +00:00
}
}
void CmndPowerSet ( void ) {
2019-08-16 13:41:02 +01:00
Energy . command_code = CMND_POWERSET ;
2019-08-01 14:46:12 +01:00
if ( XnrgCall ( FUNC_COMMAND ) ) { // Watt
2022-10-30 10:15:17 +00:00
EnergyCommandSetCalResponse ( ENERGY_POWER_CALIBRATION ) ;
2019-01-05 14:39:56 +00:00
}
2019-08-01 14:46:12 +01:00
}
2021-03-21 16:51:57 +00:00
void CmndVoltageSet ( void ) {
2019-08-16 13:41:02 +01:00
Energy . command_code = CMND_VOLTAGESET ;
2019-08-01 14:46:12 +01:00
if ( XnrgCall ( FUNC_COMMAND ) ) { // Volt
2022-10-30 10:15:17 +00:00
EnergyCommandSetCalResponse ( ENERGY_VOLTAGE_CALIBRATION ) ;
2019-01-05 14:39:56 +00:00
}
2019-08-01 14:46:12 +01:00
}
2021-03-21 16:51:57 +00:00
void CmndCurrentSet ( void ) {
2019-08-16 13:41:02 +01:00
Energy . command_code = CMND_CURRENTSET ;
2019-08-01 14:46:12 +01:00
if ( XnrgCall ( FUNC_COMMAND ) ) { // milliAmpere
2022-10-30 10:15:17 +00:00
EnergyCommandSetCalResponse ( ENERGY_CURRENT_CALIBRATION ) ;
2019-01-05 14:39:56 +00:00
}
2019-08-01 14:46:12 +01:00
}
2021-03-21 16:51:57 +00:00
void CmndFrequencySet ( void ) {
2019-08-16 13:41:02 +01:00
Energy . command_code = CMND_FREQUENCYSET ;
2019-08-01 14:46:12 +01:00
if ( XnrgCall ( FUNC_COMMAND ) ) { // Hz
2022-10-30 10:15:17 +00:00
EnergyCommandSetCalResponse ( ENERGY_FREQUENCY_CALIBRATION ) ;
2018-09-21 14:22:17 +01:00
}
2019-08-01 14:46:12 +01:00
}
2018-02-03 22:25:05 +00:00
2021-03-21 16:51:57 +00:00
void CmndModuleAddress ( void ) {
2019-09-16 15:56:16 +01:00
if ( ( XdrvMailbox . payload > 0 ) & & ( XdrvMailbox . payload < 4 ) & & ( 1 = = Energy . phase_count ) ) {
Energy . command_code = CMND_MODULEADDRESS ;
if ( XnrgCall ( FUNC_COMMAND ) ) { // Module address
ResponseCmndDone ( ) ;
}
}
}
2021-04-08 21:59:44 +01:00
void CmndEnergyConfig ( void ) {
2021-04-09 08:49:33 +01:00
Energy . command_code = CMND_ENERGYCONFIG ;
2022-03-26 15:02:50 +00:00
ResponseClear ( ) ;
2021-04-08 21:59:44 +01:00
if ( XnrgCall ( FUNC_COMMAND ) ) {
2022-03-08 17:11:52 +00:00
if ( ! ResponseLength ( ) ) {
ResponseCmndDone ( ) ;
}
2021-04-08 21:59:44 +01:00
}
}
2019-08-01 14:46:12 +01:00
# ifdef USE_ENERGY_MARGIN_DETECTION
2021-03-21 16:51:57 +00:00
void CmndPowerDelta ( void ) {
2021-04-06 14:23:07 +01:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = ENERGY_MAX_PHASES ) ) {
2020-08-23 17:29:16 +01:00
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 32000 ) ) {
2021-06-11 17:14:12 +01:00
Settings - > energy_power_delta [ XdrvMailbox . index - 1 ] = XdrvMailbox . payload ;
2020-08-23 17:29:16 +01:00
}
2021-06-11 17:14:12 +01:00
ResponseCmndIdxNumber ( Settings - > energy_power_delta [ XdrvMailbox . index - 1 ] ) ;
2017-12-16 14:51:45 +00:00
}
2019-08-01 14:46:12 +01:00
}
2021-03-21 16:51:57 +00:00
void CmndPowerLow ( void ) {
2019-08-01 14:46:12 +01:00
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 3601 ) ) {
2021-06-11 17:14:12 +01:00
Settings - > energy_min_power = XdrvMailbox . payload ;
2017-12-16 14:51:45 +00:00
}
2021-06-11 17:14:12 +01:00
ResponseCmndNumber ( Settings - > energy_min_power ) ;
2019-08-01 14:46:12 +01:00
}
2021-03-21 16:51:57 +00:00
void CmndPowerHigh ( void ) {
2019-08-01 14:46:12 +01:00
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 3601 ) ) {
2021-06-11 17:14:12 +01:00
Settings - > energy_max_power = XdrvMailbox . payload ;
2017-12-16 14:51:45 +00:00
}
2021-06-11 17:14:12 +01:00
ResponseCmndNumber ( Settings - > energy_max_power ) ;
2019-08-01 14:46:12 +01:00
}
2021-03-21 16:51:57 +00:00
void CmndVoltageLow ( void ) {
2019-08-01 14:46:12 +01:00
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 501 ) ) {
2021-06-11 17:14:12 +01:00
Settings - > energy_min_voltage = XdrvMailbox . payload ;
2017-12-16 14:51:45 +00:00
}
2021-06-11 17:14:12 +01:00
ResponseCmndNumber ( Settings - > energy_min_voltage ) ;
2019-08-01 14:46:12 +01:00
}
2021-03-21 16:51:57 +00:00
void CmndVoltageHigh ( void ) {
2019-08-01 14:46:12 +01:00
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 501 ) ) {
2021-06-11 17:14:12 +01:00
Settings - > energy_max_voltage = XdrvMailbox . payload ;
2017-12-16 14:51:45 +00:00
}
2021-06-11 17:14:12 +01:00
ResponseCmndNumber ( Settings - > energy_max_voltage ) ;
2019-08-01 14:46:12 +01:00
}
2021-03-21 16:51:57 +00:00
void CmndCurrentLow ( void ) {
2019-08-01 14:46:12 +01:00
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 16001 ) ) {
2021-06-11 17:14:12 +01:00
Settings - > energy_min_current = XdrvMailbox . payload ;
2017-12-16 14:51:45 +00:00
}
2021-06-11 17:14:12 +01:00
ResponseCmndNumber ( Settings - > energy_min_current ) ;
2019-08-01 14:46:12 +01:00
}
2021-03-21 16:51:57 +00:00
void CmndCurrentHigh ( void ) {
2019-08-01 14:46:12 +01:00
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 16001 ) ) {
2021-06-11 17:14:12 +01:00
Settings - > energy_max_current = XdrvMailbox . payload ;
2017-12-16 14:51:45 +00:00
}
2021-06-11 17:14:12 +01:00
ResponseCmndNumber ( Settings - > energy_max_current ) ;
2019-08-01 14:46:12 +01:00
}
# ifdef USE_ENERGY_POWER_LIMIT
2021-03-21 16:51:57 +00:00
void CmndMaxPower ( void ) {
2019-08-01 14:46:12 +01:00
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 3601 ) ) {
2021-06-11 17:14:12 +01:00
Settings - > energy_max_power_limit = XdrvMailbox . payload ;
2017-12-16 14:51:45 +00:00
}
2021-06-11 17:14:12 +01:00
ResponseCmndNumber ( Settings - > energy_max_power_limit ) ;
2019-08-01 14:46:12 +01:00
}
2018-05-09 09:49:43 +01:00
2021-03-21 16:51:57 +00:00
void CmndMaxPowerHold ( void ) {
2019-08-01 14:46:12 +01:00
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 3601 ) ) {
2021-06-11 17:14:12 +01:00
Settings - > energy_max_power_limit_hold = ( 1 = = XdrvMailbox . payload ) ? MAX_POWER_HOLD : XdrvMailbox . payload ;
2019-08-01 14:46:12 +01:00
}
2021-06-11 17:14:12 +01:00
ResponseCmndNumber ( Settings - > energy_max_power_limit_hold ) ;
2019-08-01 14:46:12 +01:00
}
2018-09-22 14:09:53 +01:00
2021-03-21 16:51:57 +00:00
void CmndMaxPowerWindow ( void ) {
2019-08-01 14:46:12 +01:00
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 3601 ) ) {
2021-06-11 17:14:12 +01:00
Settings - > energy_max_power_limit_window = ( 1 = = XdrvMailbox . payload ) ? MAX_POWER_WINDOW : XdrvMailbox . payload ;
2019-08-01 14:46:12 +01:00
}
2021-06-11 17:14:12 +01:00
ResponseCmndNumber ( Settings - > energy_max_power_limit_window ) ;
2019-08-01 14:46:12 +01:00
}
2018-09-22 14:09:53 +01:00
2021-03-21 16:51:57 +00:00
void CmndSafePower ( void ) {
2019-08-01 14:46:12 +01:00
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 3601 ) ) {
2021-06-11 17:14:12 +01:00
Settings - > energy_max_power_safe_limit = XdrvMailbox . payload ;
2017-12-16 14:51:45 +00:00
}
2021-06-11 17:14:12 +01:00
ResponseCmndNumber ( Settings - > energy_max_power_safe_limit ) ;
2019-08-01 14:46:12 +01:00
}
2018-05-09 09:49:43 +01:00
2021-03-21 16:51:57 +00:00
void CmndSafePowerHold ( void ) {
2019-08-01 14:46:12 +01:00
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 3601 ) ) {
2021-06-11 17:14:12 +01:00
Settings - > energy_max_power_safe_limit_hold = ( 1 = = XdrvMailbox . payload ) ? SAFE_POWER_HOLD : XdrvMailbox . payload ;
2019-08-01 14:46:12 +01:00
}
2021-06-11 17:14:12 +01:00
ResponseCmndNumber ( Settings - > energy_max_power_safe_limit_hold ) ;
2017-12-16 14:51:45 +00:00
}
2021-03-21 16:51:57 +00:00
void CmndSafePowerWindow ( void ) {
2019-08-01 14:46:12 +01:00
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 1440 ) ) {
2021-06-11 17:14:12 +01:00
Settings - > energy_max_power_safe_limit_window = ( 1 = = XdrvMailbox . payload ) ? SAFE_POWER_WINDOW : XdrvMailbox . payload ;
2019-08-01 14:46:12 +01:00
}
2021-06-11 17:14:12 +01:00
ResponseCmndNumber ( Settings - > energy_max_power_safe_limit_window ) ;
2019-08-01 14:46:12 +01:00
}
2021-03-21 16:51:57 +00:00
void CmndMaxEnergy ( void ) {
2019-08-01 14:46:12 +01:00
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 3601 ) ) {
2021-06-11 17:14:12 +01:00
Settings - > energy_max_energy = XdrvMailbox . payload ;
2019-08-16 13:41:02 +01:00
Energy . max_energy_state = 3 ;
2019-08-01 14:46:12 +01:00
}
2021-06-11 17:14:12 +01:00
ResponseCmndNumber ( Settings - > energy_max_energy ) ;
2019-08-01 14:46:12 +01:00
}
2021-03-21 16:51:57 +00:00
void CmndMaxEnergyStart ( void ) {
2019-08-01 14:46:12 +01:00
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 24 ) ) {
2021-06-11 17:14:12 +01:00
Settings - > energy_max_energy_start = XdrvMailbox . payload ;
2019-08-01 14:46:12 +01:00
}
2021-06-11 17:14:12 +01:00
ResponseCmndNumber ( Settings - > energy_max_energy_start ) ;
2019-08-01 14:46:12 +01:00
}
# endif // USE_ENERGY_POWER_LIMIT
# endif // USE_ENERGY_MARGIN_DETECTION
2021-04-06 14:23:07 +01:00
void EnergyDrvInit ( void ) {
memset ( & Energy , 0 , sizeof ( Energy ) ) ; // Reset all to 0 and false;
2022-10-26 11:45:06 +01:00
// Energy.voltage_common = false;
// Energy.frequency_common = false;
// Energy.use_overtemp = false;
2021-04-06 14:23:07 +01:00
for ( uint32_t phase = 0 ; phase < ENERGY_MAX_PHASES ; phase + + ) {
Energy . apparent_power [ phase ] = NAN ;
Energy . reactive_power [ phase ] = NAN ;
Energy . power_factor [ phase ] = NAN ;
Energy . frequency [ phase ] = NAN ;
Energy . export_active [ phase ] = NAN ;
}
Energy . phase_count = 1 ; // Number of phases active
Energy . voltage_available = true ; // Enable if voltage is measured
Energy . current_available = true ; // Enable if current is measured
Energy . power_on = true ;
TasmotaGlobal . energy_driver = ENERGY_NONE ;
2022-08-05 14:25:39 +01:00
XnrgCall ( FUNC_PRE_INIT ) ; // Find first energy driver
2022-10-06 22:27:43 +01:00
if ( TasmotaGlobal . energy_driver ) {
AddLog ( LOG_LEVEL_INFO , PSTR ( " NRG: Init driver %d " ) , TasmotaGlobal . energy_driver ) ;
}
2021-04-06 14:23:07 +01:00
}
2018-11-14 13:32:09 +00:00
void EnergySnsInit ( void )
2018-02-03 22:25:05 +00:00
{
2018-09-04 15:22:34 +01:00
XnrgCall ( FUNC_INIT ) ;
2018-02-13 13:30:30 +00:00
2020-10-30 11:29:48 +00:00
if ( TasmotaGlobal . energy_driver ) {
2022-08-05 14:25:39 +01:00
for ( uint32_t i = 0 ; i < 3 ; i + + ) {
// Energy.kWhtoday_offset[i] = 0; // Reset by EnergyDrvInit()
// 20220805 - Change from https://github.com/arendst/Tasmota/issues/16118
if ( RtcSettingsValid ( ) ) {
2021-09-29 14:33:58 +01:00
Energy . kWhtoday_offset [ i ] = RtcSettings . energy_kWhtoday_ph [ i ] ;
2022-08-05 14:25:39 +01:00
RtcSettings . energy_kWhtoday_ph [ i ] = 0 ;
Energy . kWhtoday_offset_init = true ;
2021-09-29 14:33:58 +01:00
}
2022-08-05 14:25:39 +01:00
// Energy.kWhtoday_ph[i] = 0; // Reset by EnergyDrvInit()
// Energy.kWhtoday_delta[i] = 0; // Reset by EnergyDrvInit()
2021-09-29 14:33:58 +01:00
Energy . period [ i ] = Energy . kWhtoday_offset [ i ] ;
2022-04-16 15:07:42 +01:00
if ( Energy . local_energy_active_export ) {
2022-08-05 14:25:39 +01:00
Energy . export_active [ i ] = 0 ; // Was set to NAN by EnergyDrvInit()
2022-04-16 15:07:42 +01:00
}
2021-09-29 14:33:58 +01:00
}
2018-02-04 17:09:09 +00:00
EnergyUpdateToday ( ) ;
2019-09-07 15:31:39 +01:00
ticker_energy . attach_ms ( 200 , Energy200ms ) ;
2017-12-16 14:51:45 +00:00
}
}
# ifdef USE_WEBSERVER
2019-03-19 16:31:43 +00:00
const char HTTP_ENERGY_SNS1 [ ] PROGMEM =
2018-09-28 14:48:42 +01:00
" {s} " D_POWERUSAGE_APPARENT " {m}%s " D_UNIT_VA " {e} "
" {s} " D_POWERUSAGE_REACTIVE " {m}%s " D_UNIT_VAR " {e} "
2018-09-07 17:15:47 +01:00
" {s} " D_POWER_FACTOR " {m}%s{e} " ;
Add support for Shelly 1PM Template
Add support for Shelly 1PM Template {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18} (#5716)
2019-05-13 17:26:07 +01:00
const char HTTP_ENERGY_SNS2 [ ] PROGMEM =
2019-09-01 16:51:25 +01:00
" {s} " D_ENERGY_TODAY " {m}%s " D_UNIT_KILOWATTHOUR " {e} "
2017-12-16 14:51:45 +00:00
" {s} " D_ENERGY_YESTERDAY " {m}%s " D_UNIT_KILOWATTHOUR " {e} "
" {s} " D_ENERGY_TOTAL " {m}%s " D_UNIT_KILOWATTHOUR " {e} " ; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
2019-09-10 11:31:08 +01:00
const char HTTP_ENERGY_SNS3 [ ] PROGMEM =
" {s} " D_EXPORT_ACTIVE " {m}%s " D_UNIT_KILOWATTHOUR " {e} " ;
2017-12-16 14:51:45 +00:00
# endif // USE_WEBSERVER
2021-09-29 14:33:58 +01:00
void EnergyShow ( bool json ) {
if ( Energy . voltage_common ) {
for ( uint32_t i = 0 ; i < Energy . phase_count ; i + + ) {
2019-09-15 12:10:32 +01:00
Energy . voltage [ i ] = Energy . voltage [ 0 ] ;
}
}
2021-09-29 14:33:58 +01:00
float apparent_power [ Energy . phase_count ] ;
float reactive_power [ Energy . phase_count ] ;
float power_factor [ Energy . phase_count ] ;
2019-08-16 13:41:02 +01:00
if ( ! Energy . type_dc ) {
if ( Energy . current_available & & Energy . voltage_available ) {
2019-09-15 12:10:32 +01:00
for ( uint32_t i = 0 ; i < Energy . phase_count ; i + + ) {
2021-09-29 14:33:58 +01:00
apparent_power [ i ] = Energy . apparent_power [ i ] ;
if ( isnan ( apparent_power [ i ] ) ) {
apparent_power [ i ] = Energy . voltage [ i ] * Energy . current [ i ] ;
2019-09-15 12:10:32 +01:00
}
2021-09-29 14:33:58 +01:00
if ( apparent_power [ i ] < Energy . active_power [ i ] ) { // Should be impossible
Energy . active_power [ i ] = apparent_power [ i ] ;
2019-09-15 12:10:32 +01:00
}
2018-09-28 14:48:42 +01:00
2021-09-29 14:33:58 +01:00
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 ;
2019-09-15 12:10:32 +01:00
}
}
2021-09-29 14:33:58 +01:00
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 ;
2022-04-19 14:44:53 +01:00
if ( ( Energy . current [ i ] > 0.005f ) & & ( ( difference > 15 ) | | ( difference > ( uint32_t ) ( apparent_power [ i ] * 100 / 1000 ) ) ) ) {
2019-09-15 12:10:32 +01:00
// 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%
2021-09-29 14:33:58 +01:00
//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 ] ;
2021-05-16 17:09:53 +01:00
if ( power_diff < 10737418 ) // 2^30 / 100 (RoundSqrtInt is limited to 2^30-1)
2022-04-19 14:44:53 +01:00
reactive_power [ i ] = ( float ) ( RoundSqrtInt ( ( uint32_t ) ( power_diff * 100.0f ) ) ) / 10.0f ;
2021-05-16 17:09:53 +01:00
else
2021-09-29 14:33:58 +01:00
reactive_power [ i ] = ( float ) ( SqrtInt ( ( uint32_t ) ( power_diff ) ) ) ;
2019-09-15 12:10:32 +01:00
}
Add support for Shelly 1PM Template
Add support for Shelly 1PM Template {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18} (#5716)
2019-05-13 17:26:07 +01:00
}
2018-09-28 14:48:42 +01:00
2019-09-15 12:10:32 +01:00
}
2018-09-28 14:48:42 +01:00
}
}
2022-04-19 14:44:53 +01:00
float active_power_sum = 0.0f ;
2021-09-29 14:33:58 +01:00
float energy_yesterday_ph [ Energy . phase_count ] ;
2019-09-15 12:10:32 +01:00
for ( uint32_t i = 0 ; i < Energy . phase_count ; i + + ) {
2021-09-29 14:33:58 +01:00
energy_yesterday_ph [ i ] = ( float ) Settings - > energy_kWhyesterday_ph [ i ] / 100000 ;
2021-09-29 13:53:23 +01:00
2021-09-29 14:33:58 +01:00
active_power_sum + = Energy . active_power [ i ] ;
}
2021-09-29 13:53:23 +01:00
2020-05-22 16:48:21 +01:00
bool energy_tariff = false ;
2021-09-29 14:33:58 +01:00
float energy_usage [ 2 ] ;
float energy_return [ 2 ] ;
2021-06-11 17:14:12 +01:00
if ( Settings - > tariff [ 0 ] [ 0 ] ! = Settings - > tariff [ 1 ] [ 0 ] ) {
2021-09-29 14:33:58 +01:00
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
2020-05-22 16:48:21 +01:00
energy_tariff = true ;
2019-09-15 17:05:23 +01:00
}
2017-12-16 14:51:45 +00:00
2022-10-11 10:12:54 +01:00
char value_chr [ GUISZ ] ; // Used by EnergyFormatIndex
char value2_chr [ GUISZ ] ;
char value3_chr [ GUISZ ] ;
2019-09-15 12:10:32 +01:00
2017-12-16 14:51:45 +00:00
if ( json ) {
2020-10-29 12:37:09 +00:00
bool show_energy_period = ( 0 = = TasmotaGlobal . tele_period ) ;
2019-09-10 11:31:08 +01:00
2020-05-22 16:48:21 +01:00
ResponseAppend_P ( PSTR ( " , \" " D_RSLT_ENERGY " \" :{ \" " D_JSON_TOTAL_START_TIME " \" : \" %s \" , \" " D_JSON_TOTAL " \" :%s " ) ,
2019-09-15 17:05:23 +01:00
GetDateAndTime ( DT_ENERGY ) . c_str ( ) ,
2022-03-15 14:43:23 +00:00
EnergyFormat ( value_chr , Energy . total , Settings - > flag2 . energy_resolution , 2 ) ) ;
2020-05-22 16:48:21 +01:00
if ( energy_tariff ) {
ResponseAppend_P ( PSTR ( " , \" " D_JSON_TOTAL D_CMND_TARIFF " \" :%s " ) ,
2022-03-15 14:43:23 +00:00
EnergyFormat ( value_chr , energy_usage , Settings - > flag2 . energy_resolution , 6 ) ) ;
2020-05-22 16:48:21 +01:00
}
ResponseAppend_P ( PSTR ( " , \" " D_JSON_YESTERDAY " \" :%s, \" " D_JSON_TODAY " \" :%s " ) ,
2022-03-15 14:43:23 +00:00
EnergyFormat ( value_chr , energy_yesterday_ph , Settings - > flag2 . energy_resolution , 2 ) ,
EnergyFormat ( value2_chr , Energy . daily , Settings - > flag2 . energy_resolution , 2 ) ) ;
2019-09-15 17:05:23 +01:00
2021-10-02 17:29:05 +01:00
/*
2021-02-09 15:13:40 +00:00
# if defined(SDM630_IMPORT) || defined(SDM72_IMPEXP)
2020-08-18 21:09:33 +01:00
if ( ! isnan ( Energy . import_active [ 0 ] ) ) {
ResponseAppend_P ( PSTR ( " , \" " D_JSON_IMPORT_ACTIVE " \" :%s " ) ,
2022-03-15 14:43:23 +00:00
EnergyFormat ( value_chr , Energy . import_active , Settings - > flag2 . energy_resolution ) ) ;
2020-08-18 21:09:33 +01:00
if ( energy_tariff ) {
ResponseAppend_P ( PSTR ( " , \" " D_JSON_IMPORT D_CMND_TARIFF " \" :%s " ) ,
2022-03-15 14:43:23 +00:00
EnergyFormat ( value_chr , energy_return , Settings - > flag2 . energy_resolution , 6 ) ) ;
2020-08-18 21:09:33 +01:00
}
}
2021-02-09 15:13:40 +00:00
# endif // SDM630_IMPORT || SDM72_IMPEXP
2021-10-02 17:29:05 +01:00
*/
2020-08-18 21:09:33 +01:00
2020-05-22 16:48:21 +01:00
if ( ! isnan ( Energy . export_active [ 0 ] ) ) {
2022-08-26 13:35:52 +01:00
ResponseAppend_P ( PSTR ( " , \" " D_JSON_TODAY_SUM_IMPORT " \" :%s, \" " D_JSON_TODAY_SUM_EXPORT " \" :%s, \" " D_JSON_EXPORT_ACTIVE " \" :%s " ) ,
EnergyFormat ( value_chr , & Energy . daily_sum_import_balanced , Settings - > flag2 . energy_resolution , 1 ) ,
EnergyFormat ( value2_chr , & Energy . daily_sum_export_balanced , Settings - > flag2 . energy_resolution , 1 ) ,
EnergyFormat ( value3_chr , Energy . export_active , Settings - > flag2 . energy_resolution ) ) ;
2020-05-22 16:48:21 +01:00
if ( energy_tariff ) {
2020-05-22 17:31:14 +01:00
ResponseAppend_P ( PSTR ( " , \" " D_JSON_EXPORT D_CMND_TARIFF " \" :%s " ) ,
2022-03-15 14:43:23 +00:00
EnergyFormat ( value_chr , energy_return , Settings - > flag2 . energy_resolution , 6 ) ) ;
2020-05-22 16:48:21 +01:00
}
2019-09-10 11:31:08 +01:00
}
2019-09-15 17:05:23 +01:00
2019-09-10 11:31:08 +01:00
if ( show_energy_period ) {
2021-09-29 14:33:58 +01:00
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 " ) ,
2022-03-15 14:43:23 +00:00
EnergyFormat ( value_chr , energy_period , Settings - > flag2 . wattage_resolution ) ) ;
2019-09-10 11:31:08 +01:00
}
2021-09-29 14:33:58 +01:00
2019-09-15 12:10:32 +01:00
ResponseAppend_P ( PSTR ( " , \" " D_JSON_POWERUSAGE " \" :%s " ) ,
2022-03-15 14:43:23 +00:00
EnergyFormat ( value_chr , Energy . active_power , Settings - > flag2 . wattage_resolution ) ) ;
2019-08-16 13:41:02 +01:00
if ( ! Energy . type_dc ) {
if ( Energy . current_available & & Energy . voltage_available ) {
Add support for Shelly 1PM Template
Add support for Shelly 1PM Template {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18} (#5716)
2019-05-13 17:26:07 +01:00
ResponseAppend_P ( PSTR ( " , \" " D_JSON_APPARENT_POWERUSAGE " \" :%s, \" " D_JSON_REACTIVE_POWERUSAGE " \" :%s, \" " D_JSON_POWERFACTOR " \" :%s " ) ,
2022-03-15 14:43:23 +00:00
EnergyFormat ( value_chr , apparent_power , Settings - > flag2 . wattage_resolution ) ,
EnergyFormat ( value2_chr , reactive_power , Settings - > flag2 . wattage_resolution ) ,
EnergyFormat ( value3_chr , power_factor , 2 ) ) ;
Add support for Shelly 1PM Template
Add support for Shelly 1PM Template {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18} (#5716)
2019-05-13 17:26:07 +01:00
}
2019-09-15 12:10:32 +01:00
if ( ! isnan ( Energy . frequency [ 0 ] ) ) {
ResponseAppend_P ( PSTR ( " , \" " D_JSON_FREQUENCY " \" :%s " ) ,
2022-03-15 14:43:23 +00:00
EnergyFormat ( value_chr , Energy . frequency , Settings - > flag2 . frequency_resolution , Energy . frequency_common ) ) ;
Add support for Shelly 1PM Template
Add support for Shelly 1PM Template {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18} (#5716)
2019-05-13 17:26:07 +01:00
}
}
2019-08-16 13:41:02 +01:00
if ( Energy . voltage_available ) {
2019-09-15 12:10:32 +01:00
ResponseAppend_P ( PSTR ( " , \" " D_JSON_VOLTAGE " \" :%s " ) ,
2022-03-15 14:43:23 +00:00
EnergyFormat ( value_chr , Energy . voltage , Settings - > flag2 . voltage_resolution , Energy . voltage_common ) ) ;
2018-09-28 14:48:42 +01:00
}
2019-08-16 13:41:02 +01:00
if ( Energy . current_available ) {
2019-09-15 12:10:32 +01:00
ResponseAppend_P ( PSTR ( " , \" " D_JSON_CURRENT " \" :%s " ) ,
2022-03-15 14:43:23 +00:00
EnergyFormat ( value_chr , Energy . current , Settings - > flag2 . current_resolution ) ) ;
Add support for Shelly 1PM Template
Add support for Shelly 1PM Template {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18} (#5716)
2019-05-13 17:26:07 +01:00
}
2019-09-01 16:51:25 +01:00
XnrgCall ( FUNC_JSON_APPEND ) ;
Add support for Shelly 1PM Template
Add support for Shelly 1PM Template {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18} (#5716)
2019-05-13 17:26:07 +01:00
ResponseJsonEnd ( ) ;
2018-09-28 14:48:42 +01:00
2017-12-16 14:51:45 +00:00
# ifdef USE_DOMOTICZ
if ( show_energy_period ) { // Only send if telemetry
2021-09-29 14:33:58 +01:00
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
2019-08-27 16:01:12 +01:00
2021-09-29 14:33:58 +01:00
char energy_usage_chr [ 2 ] [ FLOATSZ ] ;
char energy_return_chr [ 2 ] [ FLOATSZ ] ;
2020-05-22 16:48:21 +01:00
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 ] ) ;
2021-09-29 14:33:58 +01:00
DomoticzSensorP1SmartMeter ( energy_usage_chr [ 0 ] , energy_usage_chr [ 1 ] , energy_return_chr [ 0 ] , energy_return_chr [ 1 ] , ( int ) active_power_sum ) ;
2019-08-27 16:01:12 +01:00
2017-12-16 14:51:45 +00:00
}
# endif // USE_DOMOTICZ
2018-05-17 05:44:46 +01:00
# ifdef USE_KNX
if ( show_energy_period ) {
2019-08-16 13:41:02 +01:00
if ( Energy . voltage_available ) {
2019-09-15 12:10:32 +01:00
KnxSensor ( KNX_ENERGY_VOLTAGE , Energy . voltage [ 0 ] ) ;
Add support for Shelly 1PM Template
Add support for Shelly 1PM Template {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18} (#5716)
2019-05-13 17:26:07 +01:00
}
2019-08-16 13:41:02 +01:00
if ( Energy . current_available ) {
2019-09-15 12:10:32 +01:00
KnxSensor ( KNX_ENERGY_CURRENT , Energy . current [ 0 ] ) ;
}
2021-09-29 14:33:58 +01:00
KnxSensor ( KNX_ENERGY_POWER , active_power_sum ) ;
2019-09-15 12:10:32 +01:00
if ( ! Energy . type_dc ) {
2021-09-29 14:33:58 +01:00
KnxSensor ( KNX_ENERGY_POWERFACTOR , power_factor [ 0 ] ) ;
Add support for Shelly 1PM Template
Add support for Shelly 1PM Template {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18} (#5716)
2019-05-13 17:26:07 +01:00
}
2021-09-29 14:33:58 +01:00
KnxSensor ( KNX_ENERGY_DAILY , Energy . daily_sum ) ;
KnxSensor ( KNX_ENERGY_TOTAL , Energy . total_sum ) ;
KnxSensor ( KNX_ENERGY_YESTERDAY , Energy . yesterday_sum ) ;
2018-05-17 05:44:46 +01:00
}
# endif // USE_KNX
2017-12-16 14:51:45 +00:00
# ifdef USE_WEBSERVER
} else {
2022-03-15 14:43:23 +00:00
# ifdef USE_ENERGY_COLUMN_GUI
2022-04-12 09:22:32 +01:00
// Need a new table supporting more columns using empty columns (with in data rows) as easy column spacing
2022-04-11 17:33:57 +01:00
// {s}</th><th></th><th>Head1</th><th></th><td>{e}
// {s}</th><th></th><th>Head1</th><th></th><th>Head2</th><th></th><td>{e}
// {s}</th><th></th><th>Head1</th><th></th><th>Head2</th><th></th><th>Head3</th><th></th><td>{e}
2022-04-12 09:22:32 +01:00
// {s}</th><th></th><th>Head1</th><th></th><th>Head2</th><th></th><th>Head3</th><th></th><th>Head4</th><th></th><td>{e}
2022-04-12 09:58:02 +01:00
WSContentSend_P ( PSTR ( " </table><hr/>{t}{s}</th><th></th> " ) ) ; // First column is empty ({t} = <table style='width:100%'>, {s} = <tr><th>)
2022-03-15 14:43:23 +00:00
bool no_label = Energy . voltage_common | | ( 1 = = Energy . phase_count ) ;
for ( uint32_t i = 0 ; i < Energy . phase_count ; i + + ) {
2022-04-11 17:33:57 +01:00
WSContentSend_P ( PSTR ( " <th style='text-align:center'>%s%s<th></th> " ) , ( no_label ) ? " " : " L " , ( no_label ) ? " " : itoa ( i + 1 , value_chr , 10 ) ) ;
2022-03-15 14:43:23 +00:00
}
2022-04-11 17:33:57 +01:00
WSContentSend_P ( PSTR ( " <td>{e} " ) ) ; // Last column is units ({e} = </td></tr>)
2022-03-15 14:43:23 +00:00
# endif // USE_ENERGY_COLUMN_GUI
2019-08-16 13:41:02 +01:00
if ( Energy . voltage_available ) {
2022-03-15 14:43:23 +00:00
WSContentSend_PD ( HTTP_SNS_VOLTAGE , WebEnergyFormat ( value_chr , Energy . voltage , Settings - > flag2 . voltage_resolution , Energy . voltage_common ) ) ;
2021-09-29 14:33:58 +01:00
}
if ( ! Energy . type_dc ) {
if ( ! isnan ( Energy . frequency [ 0 ] ) ) {
WSContentSend_PD ( PSTR ( " {s} " D_FREQUENCY " {m}%s " D_UNIT_HERTZ " {e} " ) ,
2022-03-15 14:43:23 +00:00
WebEnergyFormat ( value_chr , Energy . frequency , Settings - > flag2 . frequency_resolution , Energy . frequency_common ) ) ;
2021-09-29 14:33:58 +01:00
}
Add support for Shelly 1PM Template
Add support for Shelly 1PM Template {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18} (#5716)
2019-05-13 17:26:07 +01:00
}
2019-08-16 13:41:02 +01:00
if ( Energy . current_available ) {
2022-03-15 14:43:23 +00:00
WSContentSend_PD ( HTTP_SNS_CURRENT , WebEnergyFormat ( value_chr , Energy . current , Settings - > flag2 . current_resolution ) ) ;
Add support for Shelly 1PM Template
Add support for Shelly 1PM Template {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18} (#5716)
2019-05-13 17:26:07 +01:00
}
2022-03-15 14:43:23 +00:00
WSContentSend_PD ( HTTP_SNS_POWER , WebEnergyFormat ( value_chr , Energy . active_power , Settings - > flag2 . wattage_resolution ) ) ;
2019-08-16 13:41:02 +01:00
if ( ! Energy . type_dc ) {
if ( Energy . current_available & & Energy . voltage_available ) {
2022-03-15 14:43:23 +00:00
WSContentSend_PD ( HTTP_ENERGY_SNS1 , WebEnergyFormat ( value_chr , apparent_power , Settings - > flag2 . wattage_resolution ) ,
WebEnergyFormat ( value2_chr , reactive_power , Settings - > flag2 . wattage_resolution ) ,
WebEnergyFormat ( value3_chr , power_factor , 2 ) ) ;
Add support for Shelly 1PM Template
Add support for Shelly 1PM Template {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18} (#5716)
2019-05-13 17:26:07 +01:00
}
2018-09-28 14:48:42 +01:00
}
2022-03-15 14:43:23 +00:00
WSContentSend_PD ( HTTP_ENERGY_SNS2 , WebEnergyFormat ( value_chr , Energy . daily , Settings - > flag2 . energy_resolution , 2 ) ,
WebEnergyFormat ( value2_chr , energy_yesterday_ph , Settings - > flag2 . energy_resolution , 2 ) ,
WebEnergyFormat ( value3_chr , Energy . total , Settings - > flag2 . energy_resolution , 2 ) ) ;
2020-05-22 16:48:21 +01:00
if ( ! isnan ( Energy . export_active [ 0 ] ) ) {
2022-03-15 14:43:23 +00:00
WSContentSend_PD ( HTTP_ENERGY_SNS3 , WebEnergyFormat ( value_chr , Energy . export_active , Settings - > flag2 . energy_resolution , 2 ) ) ;
2019-09-10 11:31:08 +01:00
}
2022-03-15 14:43:23 +00:00
# ifdef USE_ENERGY_COLUMN_GUI
XnrgCall ( FUNC_WEB_COL_SENSOR ) ;
2022-04-12 09:58:02 +01:00
WSContentSend_P ( PSTR ( " </table><hr/>{t} " ) ) ; // {t} = <table style='width:100%'> - Define for next FUNC_WEB_SENSOR
2022-03-15 14:43:23 +00:00
# endif // USE_ENERGY_COLUMN_GUI
2019-09-01 16:51:25 +01:00
XnrgCall ( FUNC_WEB_SENSOR ) ;
2017-12-16 14:51:45 +00:00
# endif // USE_WEBSERVER
}
}
/*********************************************************************************************\
* Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2019-01-28 13:08:33 +00:00
bool Xdrv03 ( uint8_t function )
2018-01-05 11:26:19 +00:00
{
2019-01-28 13:08:33 +00:00
bool result = false ;
2018-01-05 11:26:19 +00:00
2018-09-04 15:22:34 +01:00
if ( FUNC_PRE_INIT = = function ) {
2021-04-06 14:23:07 +01:00
EnergyDrvInit ( ) ;
2018-09-04 15:22:34 +01:00
}
2020-10-30 11:29:48 +00:00
else if ( TasmotaGlobal . energy_driver ) {
2018-01-05 11:26:19 +00:00
switch ( function ) {
2019-03-30 12:03:45 +00:00
case FUNC_LOOP :
XnrgCall ( FUNC_LOOP ) ;
break ;
2019-09-07 15:31:39 +01:00
case FUNC_EVERY_250_MSECOND :
2020-10-30 11:45:34 +00:00
if ( TasmotaGlobal . uptime > 4 ) {
XnrgCall ( FUNC_EVERY_250_MSECOND ) ;
}
2019-09-07 15:31:39 +01:00
break ;
2020-06-13 14:10:12 +01:00
case FUNC_EVERY_SECOND :
XnrgCall ( FUNC_EVERY_SECOND ) ;
break ;
2019-09-08 15:57:56 +01:00
case FUNC_SERIAL :
result = XnrgCall ( FUNC_SERIAL ) ;
break ;
2019-08-01 14:46:12 +01:00
# ifdef USE_ENERGY_MARGIN_DETECTION
2018-01-05 11:26:19 +00:00
case FUNC_SET_POWER :
2019-08-16 13:41:02 +01:00
Energy . power_steady_counter = 2 ;
2018-01-05 11:26:19 +00:00
break ;
2019-08-01 14:46:12 +01:00
# endif // USE_ENERGY_MARGIN_DETECTION
case FUNC_COMMAND :
result = DecodeCommand ( kEnergyCommands , EnergyCommand ) ;
break ;
2018-01-05 11:26:19 +00:00
}
}
return result ;
}
2019-01-28 13:08:33 +00:00
bool Xsns03 ( uint8_t function )
2017-12-16 14:51:45 +00:00
{
2019-01-28 13:08:33 +00:00
bool result = false ;
2017-12-16 14:51:45 +00:00
2020-10-30 11:29:48 +00:00
if ( TasmotaGlobal . energy_driver ) {
2017-12-16 14:51:45 +00:00
switch ( function ) {
2017-12-25 16:41:12 +00:00
case FUNC_EVERY_SECOND :
2019-09-21 16:10:52 +01:00
EnergyEverySecond ( ) ;
2017-12-16 14:51:45 +00:00
break ;
2017-12-25 16:41:12 +00:00
case FUNC_JSON_APPEND :
2019-01-28 13:08:33 +00:00
EnergyShow ( true ) ;
2017-12-16 14:51:45 +00:00
break ;
# ifdef USE_WEBSERVER
2019-03-19 16:31:43 +00:00
case FUNC_WEB_SENSOR :
2019-01-28 13:08:33 +00:00
EnergyShow ( false ) ;
2017-12-16 14:51:45 +00:00
break ;
# endif // USE_WEBSERVER
2017-12-25 16:41:12 +00:00
case FUNC_SAVE_BEFORE_RESTART :
2017-12-16 14:51:45 +00:00
EnergySaveState ( ) ;
break ;
2019-09-08 15:57:56 +01:00
case FUNC_INIT :
EnergySnsInit ( ) ;
break ;
2017-12-16 14:51:45 +00:00
}
}
return result ;
}
2018-08-27 22:35:23 +01:00
# endif // USE_ENERGY_SENSOR