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
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"
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
2017-12-16 14:51:45 +00:00
enum EnergyCommands {
2019-01-05 14:39:56 +00:00
CMND_POWERCAL , CMND_VOLTAGECAL , CMND_CURRENTCAL ,
2019-09-16 15:56:16 +01:00
CMND_POWERSET , CMND_VOLTAGESET , CMND_CURRENTSET , CMND_FREQUENCYSET , CMND_MODULEADDRESS } ;
2019-08-01 14:46:12 +01:00
2019-08-11 17:12:18 +01:00
const char kEnergyCommands [ ] PROGMEM = " | " // No prefix
2019-01-05 14:39:56 +00:00
D_CMND_POWERCAL " | " D_CMND_VOLTAGECAL " | " D_CMND_CURRENTCAL " | "
2019-09-16 15:56:16 +01:00
D_CMND_POWERSET " | " D_CMND_VOLTAGESET " | " D_CMND_CURRENTSET " | " D_CMND_FREQUENCYSET " | " D_CMND_MODULEADDRESS " | "
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
2019-08-27 16:01:12 +01:00
D_CMND_ENERGYRESET " | " D_CMND_TARIFF ;
2019-08-01 14:46:12 +01:00
void ( * const EnergyCommand [ ] ) ( void ) PROGMEM = {
& CmndPowerCal , & CmndVoltageCal , & CmndCurrentCal ,
2019-09-16 15:56:16 +01:00
& CmndPowerSet , & CmndVoltageSet , & CmndCurrentSet , & CmndFrequencySet , & CmndModuleAddress ,
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
2019-08-27 16:01:12 +01:00
& CmndEnergyReset , & CmndTariff } ;
2017-12-16 14:51:45 +00:00
2019-09-15 12:10:32 +01:00
const char kEnergyPhases [ ] PROGMEM = " |%s / %s|%s / %s / %s||[%s,%s]|[%s,%s,%s] " ;
2019-08-16 13:41:02 +01:00
struct ENERGY {
2019-09-15 12:10:32 +01:00
float voltage [ 3 ] = { 0 , 0 , 0 } ; // 123.1 V
float current [ 3 ] = { 0 , 0 , 0 } ; // 123.123 A
float active_power [ 3 ] = { 0 , 0 , 0 } ; // 123.1 W
float apparent_power [ 3 ] = { NAN , NAN , NAN } ; // 123.1 VA
float reactive_power [ 3 ] = { NAN , NAN , NAN } ; // 123.1 VAr
float power_factor [ 3 ] = { NAN , NAN , NAN } ; // 0.12
float frequency [ 3 ] = { NAN , NAN , NAN } ; // 123.1 Hz
2021-02-09 15:13:40 +00:00
# if defined(SDM630_IMPORT) || defined(SDM72_IMPEXP)
2020-08-18 21:09:33 +01:00
float import_active [ 3 ] = { NAN , NAN , NAN } ; // 123.123 kWh
2021-02-09 15:13:40 +00:00
# endif // SDM630_IMPORT || SDM72_IMPEXP
2020-05-22 16:48:21 +01:00
float export_active [ 3 ] = { NAN , NAN , NAN } ; // 123.123 kWh
2019-09-15 12:10:32 +01:00
float start_energy = 0 ; // 12345.12345 kWh total previous
float daily = 0 ; // 123.123 kWh
2019-09-20 11:59:34 +01:00
float total = 0 ; // 12345.12345 kWh total energy
2019-09-15 12:10:32 +01:00
unsigned long kWhtoday_delta = 0 ; // 1212312345 Wh 10^-5 (deca micro Watt hours) - Overflows to Energy.kWhtoday (HLW and CSE only)
unsigned long kWhtoday_offset = 0 ; // 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 = 0 ; // 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
2019-09-07 15:31:39 +01:00
uint8_t fifth_second = 0 ;
2019-08-16 13:41:02 +01:00
uint8_t command_code = 0 ;
2019-09-21 16:10:52 +01:00
uint8_t data_valid [ 3 ] = { 0 , 0 , 0 } ;
2019-08-16 13:41:02 +01:00
2019-09-15 12:10:32 +01:00
uint8_t phase_count = 1 ; // Number of phases active
2020-05-23 11:15:14 +01:00
bool voltage_common = false ; // Use single voltage
bool frequency_common = false ; // Use single frequency
2020-05-10 11:59:13 +01:00
bool kWhtoday_offset_init = false ;
2019-09-15 12:10:32 +01:00
bool voltage_available = true ; // Enable if voltage is measured
bool current_available = true ; // Enable if current is measured
2019-08-16 13:41:02 +01:00
bool type_dc = false ;
bool power_on = true ;
2019-08-01 14:46:12 +01:00
# ifdef USE_ENERGY_MARGIN_DETECTION
2020-08-23 17:29:16 +01:00
uint16_t power_history [ 3 ] [ 3 ] = { { 0 } , { 0 } , { 0 } } ;
2019-08-16 13:41:02 +01:00
uint8_t power_steady_counter = 8 ; // Allow for power on stabilization
bool min_power_flag = false ;
bool max_power_flag = false ;
bool min_voltage_flag = false ;
bool max_voltage_flag = false ;
bool min_current_flag = false ;
bool max_current_flag = false ;
2017-12-16 14:51:45 +00:00
2019-08-01 14:46:12 +01:00
# ifdef USE_ENERGY_POWER_LIMIT
2019-08-16 13:41:02 +01:00
uint16_t mplh_counter = 0 ;
uint16_t mplw_counter = 0 ;
uint8_t mplr_counter = 0 ;
uint8_t max_energy_state = 0 ;
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
/********************************************************************************************/
2020-08-23 17:29:16 +01:00
char * EnergyFormatIndex ( char * result , char * input , bool json , uint32_t index , bool single = false )
{
char layout [ 16 ] ;
GetTextIndexed ( layout , sizeof ( layout ) , ( index - 1 ) + ( 3 * json ) , kEnergyPhases ) ;
switch ( index ) {
case 2 :
snprintf_P ( result , FLOATSZ * 3 , layout , input , input + FLOATSZ ) ; // Dirty
break ;
case 3 :
snprintf_P ( result , FLOATSZ * 3 , layout , input , input + FLOATSZ , input + FLOATSZ + FLOATSZ ) ; // Even dirtier
break ;
default :
snprintf_P ( result , FLOATSZ * 3 , input ) ;
}
return result ;
}
char * EnergyFormat ( char * result , char * input , bool json , bool single = false )
{
uint8_t index = ( single ) ? 1 : Energy . phase_count ; // 1,2,3
return EnergyFormatIndex ( result , input , json , index , single ) ;
}
/********************************************************************************************/
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 ;
if ( IsDst ( ) & & ( Settings . tariff [ 0 ] [ 1 ] ! = Settings . tariff [ 1 ] [ 1 ] ) ) {
dst = 1 ;
}
if ( Settings . tariff [ 0 ] [ dst ] ! = Settings . tariff [ 1 ] [ dst ] ) {
2019-11-03 12:51:22 +00:00
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 ( ) ;
2019-09-29 11:18:09 +01:00
if ( Settings . tariff [ 0 ] [ dst ] > Settings . tariff [ 1 ] [ dst ] ) {
// {"Tariff":{"Off-Peak":{"STD":"22:00","DST":"23:00"},"Standard":{"STD":"06:00","DST":"07:00"},"Weekend":"OFF"}}
return ( ( minutes > = Settings . tariff [ 0 ] [ dst ] ) | | ( minutes < Settings . tariff [ 1 ] [ dst ] ) ) ;
} else {
// {"Tariff":{"Off-Peak":{"STD":"00:29","DST":"01:29"},"Standard":{"STD":"07:29","DST":"08:29"},"Weekend":"OFF"}}
return ( ( minutes > = Settings . tariff [ 0 ] [ dst ] ) & & ( minutes < Settings . tariff [ 1 ] [ dst ] ) ) ;
}
2019-09-10 15:18:23 +01:00
} else {
return false ;
}
}
2018-11-14 13:32:09 +00:00
void EnergyUpdateToday ( void )
2018-02-04 17:09:09 +00:00
{
2019-08-16 13:41:02 +01:00
if ( Energy . kWhtoday_delta > 1000 ) {
unsigned long delta = Energy . kWhtoday_delta / 1000 ;
Energy . kWhtoday_delta - = ( delta * 1000 ) ;
Energy . kWhtoday + = delta ;
2018-04-30 11:07:48 +01:00
}
2019-08-27 16:01:12 +01:00
2019-09-06 17:02:31 +01:00
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 ;
2019-08-27 16:01:12 +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.
2019-09-22 17:30:55 +01:00
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 ) ;
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);
float export_active = 0.0 ;
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
}
2019-09-03 20:53:20 +01:00
void EnergyUpdateTotal ( float value , bool kwh )
{
2021-01-27 16:42:59 +00:00
// AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: Energy Total %4_f %sWh"), &value, (kwh) ? "k" : "");
2019-09-06 17:02:31 +01:00
2019-09-03 20:53:20 +01:00
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
}
else if ( value ! = Energy . start_energy ) {
2019-09-06 17:02:31 +01:00
Energy . kWhtoday = ( unsigned long ) ( ( value - Energy . start_energy ) * multiplier ) ;
2019-09-03 20:53:20 +01:00
}
2019-09-18 10:42:28 +01:00
2019-11-03 12:51:22 +00:00
if ( ( Energy . total < ( 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)
2019-09-18 10:42:28 +01:00
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 ( ) ;
2021-01-23 15:26:23 +00:00
// AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: Energy Total updated with hardware value"));
2019-09-18 10:42:28 +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
{
2020-10-28 18:03:39 +00: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
if ( ! Energy . kWhtoday_offset_init & & ( RtcTime . day_of_year = = Settings . energy_kWhdoy ) ) {
Energy . kWhtoday_offset = Settings . energy_kWhtoday ;
Energy . kWhtoday_offset_init = true ;
}
2017-12-16 14:51:45 +00:00
if ( LocalTime ( ) = = Midnight ( ) ) {
2019-09-06 17:02:31 +01:00
Settings . energy_kWhyesterday = RtcSettings . energy_kWhtoday ;
2019-08-27 16:01:12 +01:00
2019-09-06 17:02:31 +01:00
RtcSettings . energy_kWhtotal + = RtcSettings . energy_kWhtoday ;
Settings . energy_kWhtotal = RtcSettings . energy_kWhtotal ;
2020-10-13 23:30:01 +01:00
Energy . period - = RtcSettings . energy_kWhtoday ; // this becomes a large unsigned, effectively a negative for EnergyShow calculation
2019-08-16 13:41:02 +01:00
Energy . kWhtoday = 0 ;
2019-09-06 17:02:31 +01:00
Energy . kWhtoday_offset = 0 ;
2019-08-28 09:52:24 +01:00
RtcSettings . energy_kWhtoday = 0 ;
2019-09-06 17:02:31 +01:00
Energy . start_energy = 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
2019-08-27 16:01:12 +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)
2019-08-16 13:41:02 +01:00
if ( ( RtcTime . hour = = Settings . energy_max_energy_start ) & & ( 3 = = Energy . max_energy_state ) ) {
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
{
Settings . energy_kWhdoy = ( RtcTime . valid ) ? RtcTime . day_of_year : 0 ;
2019-08-27 16:01:12 +01:00
2019-09-06 17:02:31 +01:00
Settings . energy_kWhtoday = RtcSettings . energy_kWhtoday ;
2017-12-16 14:51:45 +00:00
Settings . energy_kWhtotal = RtcSettings . energy_kWhtotal ;
2019-08-27 16:01:12 +01:00
2019-09-10 11:31:08 +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 ) ;
}
2018-11-14 13:32:09 +00:00
void EnergyMarginCheck ( void )
2017-12-16 14:51:45 +00:00
{
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
2020-08-23 17:29:16 +01:00
int16_t power_diff [ 3 ] = { 0 } ;
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
2020-08-23 17:29:16 +01:00
if ( Settings . energy_power_delta [ phase ] ) {
power_diff [ phase ] = active_power - Energy . power_history [ phase ] [ 0 ] ;
uint16_t delta = abs ( power_diff [ phase ] ) ;
bool threshold_met = false ;
if ( delta > 0 ) {
if ( Settings . energy_power_delta [ phase ] < 101 ) { // 1..100 = Percentage
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 ;
if ( delta > Settings . energy_power_delta [ phase ] ) {
threshold_met = true ;
}
} else { // 101..32000 = Absolute
if ( delta > ( Settings . energy_power_delta [ phase ] - 100 ) ) {
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 ) {
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 * 3 ] ;
ResponseAppend_P ( PSTR ( " \" " D_CMND_POWERDELTA " \" :%s " ) , EnergyFormat ( value_chr , power_diff_chr [ 0 ] , 1 ) ) ;
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
2019-08-16 13:41:02 +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 ;
2019-08-16 13:41:02 +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
}
2019-08-16 13:41:02 +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
}
2019-08-16 13:41:02 +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
}
2019-08-16 13:41:02 +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
}
2019-08-16 13:41:02 +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
}
2019-08-16 13:41:02 +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
if ( Settings . energy_max_power_limit ) {
2019-09-15 12:10:32 +01:00
if ( Energy . active_power [ 0 ] > Settings . energy_max_power_limit ) {
2019-08-16 13:41:02 +01:00
if ( ! Energy . mplh_counter ) {
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 ) ;
2018-01-18 15:19:28 +00:00
MqttPublishPrefixTopic_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 ) {
2020-02-09 15:21:48 +00:00
Energy . mplr_counter = Settings . param [ P_MAX_POWER_RETRY ] + 1 ; // SetOption33 - Max Power Retry count
2017-12-16 14:51:45 +00:00
}
2019-08-16 13:41:02 +01:00
Energy . mplw_counter = Settings . energy_max_power_limit_window ;
2017-12-16 14:51:45 +00:00
}
}
}
2020-10-28 18:03:39 +00: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 ) ) ;
2018-01-18 15:19:28 +00:00
MqttPublishPrefixTopic_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 ) ) ;
2018-01-18 15:19:28 +00:00
MqttPublishPrefixTopic_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
if ( Settings . energy_max_energy ) {
2019-10-18 15:53:30 +01:00
uint16_t energy_daily_u = ( uint16_t ) ( Energy . daily * 1000 ) ;
2019-08-16 13:41:02 +01:00
if ( ! Energy . max_energy_state & & ( RtcTime . hour = = Settings . energy_max_energy_start ) ) {
Energy . max_energy_state = 1 ;
2019-09-04 17:06:34 +01:00
ResponseTime_P ( PSTR ( " , \" " D_JSON_ENERGYMONITOR " \" : \" %s \" } " ) , GetStateText ( 1 ) ) ;
2018-01-18 15:19:28 +00:00
MqttPublishPrefixTopic_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
}
2019-08-16 13:41:02 +01:00
else if ( ( 1 = = Energy . max_energy_state ) & & ( energy_daily_u > = Settings . energy_max_energy ) ) {
Energy . max_energy_state = 2 ;
2021-01-27 16:42:59 +00:00
ResponseTime_P ( PSTR ( " , \" " D_JSON_MAXENERGYREACHED " \" :%3_f} " ) , & Energy . daily ) ;
2018-01-18 15:19:28 +00:00
MqttPublishPrefixTopic_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
2020-10-28 16:32:07 +00:00
if ( TasmotaGlobal . global_update ) {
2020-10-28 18:03:39 +00:00
if ( TasmotaGlobal . power & & ! isnan ( TasmotaGlobal . temperature_celsius ) & & ( TasmotaGlobal . temperature_celsius > ( float ) Settings . param [ P_OVER_TEMP ] ) ) { // Device overtemp, turn off relays
2020-06-12 15:51:21 +01:00
2021-01-27 16:42:59 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " NRG: GlobTemp %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
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
2019-09-15 12:10:32 +01:00
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 ; }
2020-05-22 16:48:21 +01:00
if ( ! isnan ( Energy . export_active [ i ] ) ) { Energy . export_active [ i ] = 0 ; }
2019-09-01 16:51:25 +01:00
2019-09-21 16:10:52 +01:00
data_valid - - ;
}
2019-05-30 11:45:02 +01:00
}
}
2019-09-21 16:10:52 +01:00
if ( ! data_valid ) {
2021-01-30 00:48:47 +00:00
//Energy.start_energy = 0;
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " NRG: Energy reset by " STR ( ENERGY_WATCHDOG ) " seconds invalid data " ) ) ;
2019-09-21 16:10:52 +01:00
XnrgCall ( FUNC_ENERGY_RESET ) ;
}
# 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
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2019-10-22 17:34:41 +01:00
void EnergyCommandCalResponse ( uint32_t nvalue )
2017-12-16 14:51:45 +00:00
{
2019-10-22 17:34:41 +01:00
snprintf_P ( XdrvMailbox . command , CMDSZ , PSTR ( " %sCal " ) , XdrvMailbox . command ) ;
ResponseCmndNumber ( nvalue ) ;
2019-08-01 14:46:12 +01:00
}
void CmndEnergyReset ( void )
{
2021-01-06 12:16:55 +00:00
uint32_t values [ 2 ] = { 0 } ;
uint32_t params = ParseParameters ( 2 , values ) ;
values [ 0 ] * = 100 ;
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = 5 ) ) {
if ( params > 0 ) {
2018-01-05 11:26:19 +00:00
switch ( XdrvMailbox . index ) {
2017-12-16 14:51:45 +00:00
case 1 :
2019-09-10 11:31:08 +01:00
// Reset Energy Today
2021-01-06 12:16:55 +00:00
Energy . kWhtoday_offset = values [ 0 ] ;
2019-09-06 17:02:31 +01:00
Energy . kWhtoday = 0 ;
2019-08-16 13:41:02 +01:00
Energy . kWhtoday_delta = 0 ;
2019-10-16 10:09:48 +01:00
Energy . start_energy = 0 ;
2019-09-06 17:02:31 +01:00
Energy . period = Energy . kWhtoday_offset ;
Settings . energy_kWhtoday = Energy . kWhtoday_offset ;
RtcSettings . energy_kWhtoday = Energy . kWhtoday_offset ;
Energy . daily = ( float ) Energy . kWhtoday_offset / 100000 ;
2021-01-06 12:16:55 +00:00
if ( params > 1 ) {
Settings . energy_kWhtotal_time = values [ 1 ] ;
}
else {
if ( ! RtcSettings . energy_kWhtotal & & ! Energy . kWhtoday_offset ) {
Settings . energy_kWhtotal_time = LocalTime ( ) ;
}
2019-09-02 15:41:18 +01:00
}
2017-12-16 14:51:45 +00:00
break ;
case 2 :
2019-09-10 11:31:08 +01:00
// Reset Energy Yesterday
2021-01-06 12:16:55 +00:00
Settings . energy_kWhyesterday = values [ 0 ] ;
if ( params > 1 ) {
Settings . energy_kWhtotal_time = values [ 1 ] ;
}
2017-12-16 14:51:45 +00:00
break ;
case 3 :
2019-09-10 11:31:08 +01:00
// Reset Energy Total
2021-01-06 12:16:55 +00:00
RtcSettings . energy_kWhtotal = values [ 0 ] ;
2017-12-16 14:51:45 +00:00
Settings . energy_kWhtotal = RtcSettings . energy_kWhtotal ;
2019-10-16 10:09:48 +01:00
// Energy.total = (float)(RtcSettings.energy_kWhtotal + Energy.kWhtoday_offset + Energy.kWhtoday) / 100000;
2021-01-06 12:16:55 +00:00
if ( params > 1 ) {
Settings . energy_kWhtotal_time = values [ 1 ] ;
}
else {
Settings . energy_kWhtotal_time = ( ! Energy . kWhtoday_offset ) ? LocalTime ( ) : Midnight ( ) ;
}
2019-09-20 11:59:34 +01:00
RtcSettings . energy_usage . last_usage_kWhtotal = ( uint32_t ) ( Energy . total * 1000 ) ;
2017-12-16 14:51:45 +00:00
break ;
2019-09-20 15:05:48 +01:00
case 4 :
// Reset energy_usage.usage totals
2021-01-06 12:16:55 +00:00
RtcSettings . energy_usage . usage1_kWhtotal = values [ 0 ] ;
if ( params > 1 ) {
RtcSettings . energy_usage . usage2_kWhtotal = values [ 1 ] * 100 ;
2019-09-22 17:30:55 +01:00
}
2019-09-20 15:05:48 +01:00
Settings . energy_usage . usage1_kWhtotal = RtcSettings . energy_usage . usage1_kWhtotal ;
Settings . energy_usage . usage2_kWhtotal = RtcSettings . energy_usage . usage2_kWhtotal ;
break ;
case 5 :
// Reset energy_usage.return totals
2021-01-06 12:16:55 +00:00
RtcSettings . energy_usage . return1_kWhtotal = values [ 0 ] ;
if ( params > 1 ) {
RtcSettings . energy_usage . return2_kWhtotal = values [ 1 ] * 100 ;
2019-09-22 17:30:55 +01:00
}
2019-09-20 15:05:48 +01:00
Settings . energy_usage . return1_kWhtotal = RtcSettings . energy_usage . return1_kWhtotal ;
Settings . energy_usage . return2_kWhtotal = RtcSettings . energy_usage . return2_kWhtotal ;
break ;
}
2021-01-06 12:16:55 +00:00
}
2019-09-21 10:31:41 +01:00
}
2019-09-20 15:05:48 +01:00
2019-10-16 10:09:48 +01:00
Energy . total = ( float ) ( RtcSettings . energy_kWhtotal + Energy . kWhtoday_offset + Energy . kWhtoday ) / 100000 ;
2021-01-27 16:42:59 +00:00
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]}} " ) ,
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 ) ;
2019-08-01 14:46:12 +01:00
}
2019-08-27 16:01:12 +01: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
Settings . tariff [ tariff ] [ time_type ] = value ;
if ( value < 24 ) { // Below 24 is hours
Settings . tariff [ tariff ] [ time_type ] * = 60 ; // Multiply hours by 60 minutes
char * minute = strtok_r ( nullptr , " : " , & q ) ;
if ( minute ) {
value = strtol ( minute , nullptr , 10 ) ; // 15 or 30
if ( value > 59 ) {
value = 59 ;
}
Settings . tariff [ tariff ] [ time_type ] + = value ;
}
}
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 ) {
2019-11-03 12:51:22 +00: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 ,
2019-09-25 13:24:49 +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 ( ) ,
2019-11-03 12:51:22 +00:00
GetStateText ( Settings . flag3 . energy_weekend ) ) ; // CMND_TARIFF
2019-08-27 16:01:12 +01:00
}
2019-08-01 14:46:12 +01: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
if ( ( XdrvMailbox . payload > 999 ) & & ( XdrvMailbox . payload < 32001 ) ) {
Settings . energy_power_calibration = XdrvMailbox . payload ;
}
2019-10-22 17:34:41 +01:00
ResponseCmndNumber ( Settings . energy_power_calibration ) ;
2018-09-04 15:22:34 +01:00
}
2019-08-01 14:46:12 +01: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
if ( ( XdrvMailbox . payload > 999 ) & & ( XdrvMailbox . payload < 32001 ) ) {
Settings . energy_voltage_calibration = XdrvMailbox . payload ;
}
2019-10-22 17:34:41 +01:00
ResponseCmndNumber ( Settings . energy_voltage_calibration ) ;
2018-09-04 15:22:34 +01:00
}
2019-08-01 14:46:12 +01: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
if ( ( XdrvMailbox . payload > 999 ) & & ( XdrvMailbox . payload < 32001 ) ) {
Settings . energy_current_calibration = XdrvMailbox . payload ;
}
2019-10-22 17:34:41 +01:00
ResponseCmndNumber ( Settings . energy_current_calibration ) ;
2017-12-16 14:51:45 +00:00
}
2019-08-01 14:46:12 +01: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
2019-10-22 17:34:41 +01:00
EnergyCommandCalResponse ( Settings . energy_power_calibration ) ;
2019-01-05 14:39:56 +00:00
}
2019-08-01 14:46:12 +01: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
2019-10-22 17:34:41 +01:00
EnergyCommandCalResponse ( Settings . energy_voltage_calibration ) ;
2019-01-05 14:39:56 +00:00
}
2019-08-01 14:46:12 +01: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
2019-10-22 17:34:41 +01:00
EnergyCommandCalResponse ( Settings . energy_current_calibration ) ;
2019-01-05 14:39:56 +00:00
}
2019-08-01 14:46:12 +01: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
2019-10-22 17:34:41 +01:00
EnergyCommandCalResponse ( Settings . 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
2019-09-16 15:56:16 +01:00
void CmndModuleAddress ( void )
{
if ( ( XdrvMailbox . payload > 0 ) & & ( XdrvMailbox . payload < 4 ) & & ( 1 = = Energy . phase_count ) ) {
Energy . command_code = CMND_MODULEADDRESS ;
if ( XnrgCall ( FUNC_COMMAND ) ) { // Module address
ResponseCmndDone ( ) ;
}
}
}
2019-08-01 14:46:12 +01:00
# ifdef USE_ENERGY_MARGIN_DETECTION
void CmndPowerDelta ( void )
{
2020-08-23 17:29:16 +01:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = 3 ) ) {
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 32000 ) ) {
Settings . energy_power_delta [ XdrvMailbox . index - 1 ] = XdrvMailbox . payload ;
}
ResponseCmndIdxNumber ( Settings . energy_power_delta [ XdrvMailbox . index - 1 ] ) ;
2017-12-16 14:51:45 +00:00
}
2019-08-01 14:46:12 +01:00
}
void CmndPowerLow ( void )
{
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 3601 ) ) {
Settings . energy_min_power = XdrvMailbox . payload ;
2017-12-16 14:51:45 +00:00
}
2019-10-22 17:34:41 +01:00
ResponseCmndNumber ( Settings . energy_min_power ) ;
2019-08-01 14:46:12 +01:00
}
void CmndPowerHigh ( void )
{
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 3601 ) ) {
Settings . energy_max_power = XdrvMailbox . payload ;
2017-12-16 14:51:45 +00:00
}
2019-10-22 17:34:41 +01:00
ResponseCmndNumber ( Settings . energy_max_power ) ;
2019-08-01 14:46:12 +01:00
}
void CmndVoltageLow ( void )
{
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 501 ) ) {
Settings . energy_min_voltage = XdrvMailbox . payload ;
2017-12-16 14:51:45 +00:00
}
2019-10-22 17:34:41 +01:00
ResponseCmndNumber ( Settings . energy_min_voltage ) ;
2019-08-01 14:46:12 +01:00
}
void CmndVoltageHigh ( void )
{
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 501 ) ) {
Settings . energy_max_voltage = XdrvMailbox . payload ;
2017-12-16 14:51:45 +00:00
}
2019-10-22 17:34:41 +01:00
ResponseCmndNumber ( Settings . energy_max_voltage ) ;
2019-08-01 14:46:12 +01:00
}
void CmndCurrentLow ( void )
{
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 16001 ) ) {
Settings . energy_min_current = XdrvMailbox . payload ;
2017-12-16 14:51:45 +00:00
}
2019-10-22 17:34:41 +01:00
ResponseCmndNumber ( Settings . energy_min_current ) ;
2019-08-01 14:46:12 +01:00
}
void CmndCurrentHigh ( void )
{
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 16001 ) ) {
Settings . energy_max_current = XdrvMailbox . payload ;
2017-12-16 14:51:45 +00:00
}
2019-10-22 17:34:41 +01:00
ResponseCmndNumber ( Settings . energy_max_current ) ;
2019-08-01 14:46:12 +01:00
}
# ifdef USE_ENERGY_POWER_LIMIT
void CmndMaxPower ( void )
{
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 3601 ) ) {
Settings . energy_max_power_limit = XdrvMailbox . payload ;
2017-12-16 14:51:45 +00:00
}
2019-10-22 17:34:41 +01:00
ResponseCmndNumber ( Settings . energy_max_power_limit ) ;
2019-08-01 14:46:12 +01:00
}
2018-05-09 09:49:43 +01:00
2019-08-01 14:46:12 +01:00
void CmndMaxPowerHold ( void )
{
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 3601 ) ) {
Settings . energy_max_power_limit_hold = ( 1 = = XdrvMailbox . payload ) ? MAX_POWER_HOLD : XdrvMailbox . payload ;
}
2019-10-22 17:34:41 +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
2019-08-01 14:46:12 +01:00
void CmndMaxPowerWindow ( void )
{
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 3601 ) ) {
Settings . energy_max_power_limit_window = ( 1 = = XdrvMailbox . payload ) ? MAX_POWER_WINDOW : XdrvMailbox . payload ;
}
2019-10-22 17:34:41 +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
2019-08-01 14:46:12 +01:00
void CmndSafePower ( void )
{
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 3601 ) ) {
Settings . energy_max_power_safe_limit = XdrvMailbox . payload ;
2017-12-16 14:51:45 +00:00
}
2019-10-22 17:34:41 +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
2019-08-01 14:46:12 +01:00
void CmndSafePowerHold ( void )
{
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 3601 ) ) {
Settings . energy_max_power_safe_limit_hold = ( 1 = = XdrvMailbox . payload ) ? SAFE_POWER_HOLD : XdrvMailbox . payload ;
}
2019-10-22 17:34:41 +01:00
ResponseCmndNumber ( Settings . energy_max_power_safe_limit_hold ) ;
2017-12-16 14:51:45 +00:00
}
2019-08-01 14:46:12 +01:00
void CmndSafePowerWindow ( void )
{
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 1440 ) ) {
Settings . energy_max_power_safe_limit_window = ( 1 = = XdrvMailbox . payload ) ? SAFE_POWER_WINDOW : XdrvMailbox . payload ;
}
2019-10-22 17:34:41 +01:00
ResponseCmndNumber ( Settings . energy_max_power_safe_limit_window ) ;
2019-08-01 14:46:12 +01:00
}
void CmndMaxEnergy ( void )
{
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 3601 ) ) {
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
}
2019-10-22 17:34:41 +01:00
ResponseCmndNumber ( Settings . energy_max_energy ) ;
2019-08-01 14:46:12 +01:00
}
void CmndMaxEnergyStart ( void )
{
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 24 ) ) {
Settings . energy_max_energy_start = XdrvMailbox . payload ;
}
2019-10-22 17:34:41 +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
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 ) {
2020-05-10 11:59:13 +01:00
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 ( ) ) {
2019-09-06 17:02:31 +01:00
Energy . kWhtoday_offset = RtcSettings . energy_kWhtoday ;
2020-05-10 11:59:13 +01:00
Energy . kWhtoday_offset_init = true ;
2019-09-06 17:02:31 +01:00
}
Energy . kWhtoday = 0 ;
2019-08-16 13:41:02 +01:00
Energy . kWhtoday_delta = 0 ;
2019-09-06 17:02:31 +01:00
Energy . period = Energy . kWhtoday_offset ;
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} " ;
2020-08-18 21:09:33 +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
const char HTTP_ENERGY_SNS4 [ ] PROGMEM =
" {s} " D_IMPORT_ACTIVE " {m}%s " D_UNIT_KILOWATTHOUR " {e} " ;
2021-02-09 15:13:40 +00:00
# endif // SDM630_IMPORT || SDM72_IMPEXP
2017-12-16 14:51:45 +00:00
# endif // USE_WEBSERVER
2019-01-28 13:08:33 +00:00
void EnergyShow ( bool json )
2017-12-16 14:51:45 +00:00
{
2019-09-15 12:10:32 +01:00
for ( uint32_t i = 0 ; i < Energy . phase_count ; i + + ) {
if ( Energy . voltage_common ) {
Energy . voltage [ i ] = Energy . voltage [ 0 ] ;
}
}
float power_factor_knx = Energy . power_factor [ 0 ] ;
2018-09-28 16:02:55 +01:00
2019-09-15 12:10:32 +01:00
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 ] ;
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 + + ) {
float apparent_power = Energy . apparent_power [ i ] ;
if ( isnan ( apparent_power ) ) {
apparent_power = Energy . voltage [ i ] * Energy . current [ i ] ;
}
if ( apparent_power < Energy . active_power [ i ] ) { // Should be impossible
Energy . active_power [ i ] = apparent_power ;
}
2018-09-28 14:48:42 +01:00
2019-09-15 12:10:32 +01:00
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 ;
}
}
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 ) ) ) ) {
// 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 ( ( uint32_t ) ( apparent_power * apparent_power * 100 ) - ( uint32_t ) ( Energy . active_power [ i ] * Energy . active_power [ i ] * 100 ) ) ) / 10 ;
}
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
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 ] ) ;
}
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
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 ] ) ;
2018-09-28 14:48:42 +01:00
}
}
2019-09-15 12:10:32 +01:00
char voltage_chr [ Energy . phase_count ] [ FLOATSZ ] ;
char current_chr [ Energy . phase_count ] [ FLOATSZ ] ;
char active_power_chr [ Energy . phase_count ] [ FLOATSZ ] ;
2021-02-09 15:13:40 +00:00
# if defined(SDM630_IMPORT) || defined(SDM72_IMPEXP)
2020-08-18 21:09:33 +01:00
char import_active_chr [ Energy . phase_count ] [ FLOATSZ ] ;
2021-02-09 15:13:40 +00:00
# endif // SDM630_IMPORT || SDM72_IMPEXP
2020-05-22 16:48:21 +01:00
char export_active_chr [ Energy . phase_count ] [ FLOATSZ ] ;
2019-09-15 12:10:32 +01:00
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 ] ) ;
2021-02-09 15:13:40 +00:00
# if defined(SDM630_IMPORT) || defined(SDM72_IMPEXP)
2020-08-18 21:09:33 +01:00
dtostrfd ( Energy . import_active [ i ] , Settings . flag2 . energy_resolution , import_active_chr [ i ] ) ;
2021-02-09 15:13:40 +00:00
# endif // SDM630_IMPORT || SDM72_IMPEXP
2020-05-22 16:48:21 +01:00
dtostrfd ( Energy . export_active [ i ] , Settings . flag2 . energy_resolution , export_active_chr [ i ] ) ;
2019-09-15 12:10:32 +01:00
}
2020-05-22 16:48:21 +01:00
char energy_total_chr [ FLOATSZ ] ;
dtostrfd ( Energy . total , Settings . flag2 . energy_resolution , energy_total_chr ) ;
2019-09-01 16:51:25 +01:00
char energy_daily_chr [ FLOATSZ ] ;
2019-08-16 13:41:02 +01:00
dtostrfd ( Energy . daily , Settings . flag2 . energy_resolution , energy_daily_chr ) ;
2019-09-01 16:51:25 +01:00
char energy_yesterday_chr [ FLOATSZ ] ;
2018-09-07 17:15:47 +01:00
dtostrfd ( ( float ) Settings . energy_kWhyesterday / 100000 , Settings . flag2 . energy_resolution , energy_yesterday_chr ) ;
2019-09-22 13:00:34 +01:00
2019-09-25 13:24:49 +01:00
2020-05-22 16:48:21 +01:00
bool energy_tariff = false ;
char energy_usage_chr [ 2 ] [ FLOATSZ ] ;
char energy_return_chr [ 2 ] [ FLOATSZ ] ;
2019-09-25 13:24:49 +01:00
if ( Settings . tariff [ 0 ] [ 0 ] ! = Settings . tariff [ 1 ] [ 0 ] ) {
2020-05-22 16:48:21 +01:00
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_tariff = true ;
2019-09-15 17:05:23 +01:00
}
2017-12-16 14:51:45 +00:00
2019-09-22 13:00:34 +01:00
char value_chr [ FLOATSZ * 3 ] ; // Used by EnergyFormatIndex
2019-09-15 12:10:32 +01:00
char value2_chr [ FLOATSZ * 3 ] ;
char value3_chr [ FLOATSZ * 3 ] ;
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 ( ) ,
2020-05-22 16:48:21 +01:00
energy_total_chr ) ;
if ( energy_tariff ) {
ResponseAppend_P ( PSTR ( " , \" " D_JSON_TOTAL D_CMND_TARIFF " \" :%s " ) ,
EnergyFormatIndex ( value_chr , energy_usage_chr [ 0 ] , json , 2 ) ) ;
}
ResponseAppend_P ( PSTR ( " , \" " D_JSON_YESTERDAY " \" :%s, \" " D_JSON_TODAY " \" :%s " ) ,
2019-09-15 17:05:23 +01:00
energy_yesterday_chr ,
energy_daily_chr ) ;
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 " ) ,
EnergyFormat ( value_chr , import_active_chr [ 0 ] , json ) ) ;
if ( energy_tariff ) {
ResponseAppend_P ( PSTR ( " , \" " D_JSON_IMPORT D_CMND_TARIFF " \" :%s " ) ,
EnergyFormatIndex ( value_chr , energy_return_chr [ 0 ] , json , 2 ) ) ;
}
}
2021-02-09 15:13:40 +00:00
# endif // SDM630_IMPORT || SDM72_IMPEXP
2020-08-18 21:09:33 +01:00
2020-05-22 16:48:21 +01:00
if ( ! isnan ( Energy . export_active [ 0 ] ) ) {
2019-09-22 13:00:34 +01:00
ResponseAppend_P ( PSTR ( " , \" " D_JSON_EXPORT_ACTIVE " \" :%s " ) ,
2020-05-22 16:48:21 +01:00
EnergyFormat ( value_chr , export_active_chr [ 0 ] , json ) ) ;
if ( energy_tariff ) {
2020-05-22 17:31:14 +01:00
ResponseAppend_P ( PSTR ( " , \" " D_JSON_EXPORT D_CMND_TARIFF " \" :%s " ) ,
2020-05-22 16:48:21 +01:00
EnergyFormatIndex ( value_chr , energy_return_chr [ 0 ] , json , 2 ) ) ;
}
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 ) {
2020-10-13 23:30:01 +01:00
float energy = ( float ) ( RtcSettings . energy_kWhtoday - Energy . period ) / 100 ;
2019-09-10 11:31:08 +01:00
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 ) ;
}
2019-09-15 12:10:32 +01:00
ResponseAppend_P ( PSTR ( " , \" " D_JSON_POWERUSAGE " \" :%s " ) ,
EnergyFormat ( value_chr , active_power_chr [ 0 ] , json ) ) ;
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 " ) ,
2019-09-15 12:10:32 +01:00
EnergyFormat ( value_chr , apparent_power_chr [ 0 ] , json ) ,
EnergyFormat ( value2_chr , reactive_power_chr [ 0 ] , json ) ,
EnergyFormat ( value3_chr , power_factor_chr [ 0 ] , json ) ) ;
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 " ) ,
2020-05-23 11:15:14 +01:00
EnergyFormat ( value_chr , frequency_chr [ 0 ] , json , 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 " ) ,
EnergyFormat ( value_chr , voltage_chr [ 0 ] , json , 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 " ) ,
EnergyFormat ( value_chr , current_chr [ 0 ] , json ) ) ;
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
2020-05-22 16:48:21 +01:00
dtostrfd ( Energy . total * 1000 , 1 , energy_total_chr ) ;
DomoticzSensorPowerEnergy ( ( int ) Energy . active_power [ 0 ] , energy_total_chr ) ; // PowerUsage, EnergyToday
2019-08-27 16:01:12 +01:00
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 ] ) ;
DomoticzSensorP1SmartMeter ( energy_usage_chr [ 0 ] , energy_usage_chr [ 1 ] , energy_return_chr [ 0 ] , energy_return_chr [ 1 ] , ( int ) Energy . active_power [ 0 ] ) ;
2019-08-27 16:01:12 +01:00
2019-08-16 13:41:02 +01:00
if ( Energy . voltage_available ) {
2019-09-15 12:10:32 +01:00
DomoticzSensor ( DZ_VOLTAGE , voltage_chr [ 0 ] ) ; // Voltage
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
DomoticzSensor ( DZ_CURRENT , current_chr [ 0 ] ) ; // Current
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
}
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 ] ) ;
}
KnxSensor ( KNX_ENERGY_POWER , Energy . active_power [ 0 ] ) ;
if ( ! Energy . type_dc ) {
KnxSensor ( KNX_ENERGY_POWERFACTOR , power_factor_knx ) ;
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
KnxSensor ( KNX_ENERGY_DAILY , Energy . daily ) ;
KnxSensor ( KNX_ENERGY_TOTAL , Energy . total ) ;
KnxSensor ( KNX_ENERGY_START , Energy . start_energy ) ;
2018-05-17 05:44:46 +01:00
}
# endif // USE_KNX
2017-12-16 14:51:45 +00:00
# ifdef USE_WEBSERVER
} else {
2019-08-16 13:41:02 +01:00
if ( Energy . voltage_available ) {
2019-12-30 15:42:53 +00:00
WSContentSend_PD ( HTTP_SNS_VOLTAGE , EnergyFormat ( value_chr , voltage_chr [ 0 ] , json , Energy . voltage_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 . current_available ) {
2019-12-30 15:42:53 +00:00
WSContentSend_PD ( HTTP_SNS_CURRENT , EnergyFormat ( value_chr , current_chr [ 0 ] , json ) ) ;
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-12-30 15:42:53 +00:00
WSContentSend_PD ( HTTP_SNS_POWER , EnergyFormat ( value_chr , active_power_chr [ 0 ] , json ) ) ;
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
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 ) ) ;
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 ] ) ) {
WSContentSend_PD ( PSTR ( " {s} " D_FREQUENCY " {m}%s " D_UNIT_HERTZ " {e} " ) ,
2020-05-23 11:15:14 +01:00
EnergyFormat ( value_chr , frequency_chr [ 0 ] , json , 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
}
2018-09-28 14:48:42 +01:00
}
2020-05-22 16:48:21 +01:00
WSContentSend_PD ( HTTP_ENERGY_SNS2 , energy_daily_chr , energy_yesterday_chr , energy_total_chr ) ;
if ( ! isnan ( Energy . export_active [ 0 ] ) ) {
WSContentSend_PD ( HTTP_ENERGY_SNS3 , EnergyFormat ( value_chr , export_active_chr [ 0 ] , json ) ) ;
2019-09-10 11:31:08 +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 ] ) ) {
WSContentSend_PD ( HTTP_ENERGY_SNS4 , EnergyFormat ( value_chr , import_active_chr [ 0 ] , json ) ) ;
}
2021-02-09 15:13:40 +00:00
# endif // SDM630_IMPORT || SDM72_IMPEXP
2019-09-10 11:31:08 +01:00
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 ) {
2020-10-30 11:29:48 +00:00
TasmotaGlobal . energy_driver = ENERGY_NONE ;
2019-11-06 16:48:38 +00:00
XnrgCall ( FUNC_PRE_INIT ) ; // Find first energy driver
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