/*
  xsns_56_rtc_chips.ino - RTC chip support for Tasmota

  SPDX-FileCopyrightText: 2022 Theo Arends

  SPDX-License-Identifier: GPL-3.0-only
*/

#ifdef USE_I2C
#ifdef USE_RTC_CHIPS
/*********************************************************************************************\
 * RTC chip support
 * 
 * #define USE_RV3028
 *   RV-3028-C7 at I2C address 0x52
 *   Used in MSB Master G1
 * #define USE_DS3231
 *   DS1307 and DS3231 at I2C address 0x68
 *   Used by Ulanzi TC001
 * #define USE_BM8563
 *   BM8563 at I2C address 0x51
 *   Used by M5Stack and IOTTIMER (v3)
 * #define USE_PCF85063
 *   PCF85063 at I2C address 0x51
 *   Used by Waveshare ESP32-S3-POE-ETH-8DI-8RO
 * #define USE_PCF85363
 *   PCF85363 at I2C address 0x51
 *   Used by Shelly 3EM
 * #define USE_RX8010
 *   RX8010 at I2C address 0x32
 *   Used by IOTTIMER (v1 and v2)
 * #define USE_RX8030
 *   RX8010 at I2C address 0x32
 *   Used by #23855 
 * #define USE_RX8025
 *   RX8025 at I2C address 0x32
 *   Used by MSB Master G2
\*********************************************************************************************/

#define XDRV_56             56

#ifdef USE_GPS                     // GPS driver has it's own NTP server
#undef RTC_NTP_SERVER              // Disable NTP server (+0k8 code)
#endif

struct {
  uint32_t (* ReadTime)(void);
  void (* SetTime)(uint32_t);
  int32_t (* MemRead)(uint8_t *, uint32_t);
  int32_t (* MemWrite)(uint8_t *, uint32_t);
  void (* ShowSensor)(bool);
  bool detected;
  int8_t mem_size = -1;
  uint8_t address;
  uint8_t bus;
  char name[10];
} RtcChip;

/*********************************************************************************************\
 * RV-3028-C7 RTC Controller
 *
 * I2C Address: 0x52
\*********************************************************************************************/
#ifdef USE_RV3028

#define XI2C_94             94      // See I2CDEVICES.md

#define RV3028_ADDR  0x52           // I2C address of RV-3028-C7

// RV-3028-C7 Register Addresses
#define RV3028_SECONDS      0x00
#define RV3028_MINUTES      0x01
#define RV3028_HOURS        0x02
#define RV3028_WEEKDAY      0x03
#define RV3028_DATE         0x04
#define RV3028_MONTH        0x05
#define RV3028_YEAR         0x06
#define RV3028_STATUS       0x0E
#define RV3028_CONTROL1     0x0F
#define RV3028_CONTROL2     0x10

// Status register bits
#define RV3028_PORF         0       // Power-on Reset flag (bit 0 in STATUS register)

/*-------------------------------------------------------------------------------------------*\
 * Init register to activate BSM from VBACKUP (Direct Switching Mode)
\*-------------------------------------------------------------------------------------------*/
void RV3028_EnableDSM(void) {
  uint8_t current_eeprom;

  I2cWrite8(RtcChip.address, 0x25, 0x37, RtcChip.bus);  // EEADDR = 0x37
  I2cWrite8(RtcChip.address, 0x27, 0x22, RtcChip.bus);  // EECMD = 0x22 (EEPROM Read)
  delay(3);  

  current_eeprom = I2cRead8(RtcChip.address, 0x26, RtcChip.bus);  // EEDATA actual data

  if (current_eeprom != 0x14) {
    I2cWrite8(RtcChip.address, 0x25, 0x37, RtcChip.bus);  // EEADDR = 0x37
    I2cWrite8(RtcChip.address, 0x26, 0x14, RtcChip.bus);  // EEDATA = 0x14 (FEDE=1, BSM=01 DSM mode)
    I2cWrite8(RtcChip.address, 0x27, 0x21, RtcChip.bus);  // EECMD = 0x21 (EEPROM Write)
    delay(25);  
    AddLog(LOG_LEVEL_INFO, PSTR("RV3028: EEPROM 0x37 updated to DSM mode."));
  } else {
    AddLog(LOG_LEVEL_DEBUG, PSTR("RV3028: EEPROM 0x37 already set to DSM mode."));
  }
}

/*-------------------------------------------------------------------------------------------*\
 * Read time from RV-3028-C7 and return the epoch time (seconds since 1-1-1970 00:00)
\*-------------------------------------------------------------------------------------------*/
uint32_t RV3028ReadTime(void) {

  uint8_t status = I2cRead8(RtcChip.address, RV3028_STATUS, RtcChip.bus);
  
  // Skontroluj PORF bit (bit 0 registra STATUS)
  if (status & _BV(RV3028_PORF)) {
    AddLog(LOG_LEVEL_DEBUG, PSTR("RV3028: PORF detected, RTC time invalid"));
    return 0;  // Invalid RTC time data
  }

  TIME_T tm;
  tm.second       = Bcd2Dec(I2cRead8(RtcChip.address, RV3028_SECONDS, RtcChip.bus) & 0x7F);
  tm.minute       = Bcd2Dec(I2cRead8(RtcChip.address, RV3028_MINUTES, RtcChip.bus) & 0x7F);
  tm.hour         = Bcd2Dec(I2cRead8(RtcChip.address, RV3028_HOURS, RtcChip.bus) & 0x3F);       // 24h mode (12_24 bit = 0)
  tm.day_of_week  = I2cRead8(RtcChip.address, RV3028_WEEKDAY, RtcChip.bus) & 0x07;              // 0..6 (3-bit weekday counter)
  tm.day_of_month = Bcd2Dec(I2cRead8(RtcChip.address, RV3028_DATE, RtcChip.bus) & 0x3F);
  tm.month        = Bcd2Dec(I2cRead8(RtcChip.address, RV3028_MONTH, RtcChip.bus) & 0x1F);
  uint8_t year    = Bcd2Dec(I2cRead8(RtcChip.address, RV3028_YEAR, RtcChip.bus));
  // RV-3028-C7 holds year 00-99 (representing 2000-2099).
  // MakeTime requires tm.year as years since 1970.
  tm.year = year + 30;   // (e.g., 23 -> 53 for year 2023)
  return MakeTime(tm);
}

/*-------------------------------------------------------------------------------------------*\
 * Set RV-3028-C7 time using the given epoch time (seconds since 1-1-1970 00:00)
\*-------------------------------------------------------------------------------------------*/
void RV3028SetTime(uint32_t epoch_time) {
  TIME_T tm;
  BreakTime(epoch_time, tm);
  I2cWrite8(RtcChip.address, RV3028_SECONDS, Dec2Bcd(tm.second), RtcChip.bus);
  I2cWrite8(RtcChip.address, RV3028_MINUTES, Dec2Bcd(tm.minute), RtcChip.bus);
  I2cWrite8(RtcChip.address, RV3028_HOURS,   Dec2Bcd(tm.hour),   RtcChip.bus);
  I2cWrite8(RtcChip.address, RV3028_WEEKDAY, tm.day_of_week,     RtcChip.bus);
  I2cWrite8(RtcChip.address, RV3028_DATE,    Dec2Bcd(tm.day_of_month), RtcChip.bus);
  I2cWrite8(RtcChip.address, RV3028_MONTH,   Dec2Bcd(tm.month),  RtcChip.bus);
  // Convert years since 1970 to RTC register value (00..99)
  uint8_t true_year = (tm.year < 30) ? (tm.year + 70) : (tm.year - 30);
  I2cWrite8(RtcChip.address, RV3028_YEAR, Dec2Bcd(true_year), RtcChip.bus);
  // Clear the power-on reset flag (PORF) in the status register
  uint8_t status = I2cRead8(RtcChip.address, RV3028_STATUS, RtcChip.bus);
  I2cWrite8(RtcChip.address, RV3028_STATUS, status & ~_BV(RV3028_PORF), RtcChip.bus);

  // Enable LSM mode (VBACKUP)
  RV3028_EnableDSM();
}

/*-------------------------------------------------------------------------------------------*\
 * Detection
\*-------------------------------------------------------------------------------------------*/
void RV3028Detected(void) {
  if (!RtcChip.detected && I2cEnabled(XI2C_94)) {
    RtcChip.address = RV3028_ADDR;
    for (RtcChip.bus = 0; RtcChip.bus < MAX_I2C; RtcChip.bus++) {
      if (!I2cSetDevice(RtcChip.address, RtcChip.bus)) { continue; }
      if (I2cValidRead(RtcChip.address, RV3028_STATUS, 1, RtcChip.bus)) {
        uint8_t status = I2cRead8(RtcChip.address, RV3028_STATUS, RtcChip.bus);
        if (status & _BV(RV3028_PORF)) {
          AddLog(LOG_LEVEL_DEBUG, PSTR("RV3028: PORF detected at init, RTC time invalid"));
        }
        RtcChip.detected = 1;
        strcpy_P(RtcChip.name, PSTR("RV3028"));
        RtcChip.ReadTime = &RV3028ReadTime;
        RtcChip.SetTime  = &RV3028SetTime;
        RtcChip.mem_size = 2; // RAM 2 byte
        break;
      }
    }
  }
}
#endif  // USE_RV3028

/*********************************************************************************************\
 * DS1307 and DS3231
 *
 * I2C Address: 0x68
\*********************************************************************************************/
#ifdef USE_DS3231

#define XI2C_26             26      // See I2CDEVICES.md

#define DS3231_ADDRESS      0x68    // DS3231 I2C Address

// DS3231 Register Addresses
#define DS3231_SECONDS      0x00
#define DS3231_MINUTES      0x01
#define DS3231_HOURS        0x02
#define DS3231_DAY          0x03
#define DS3231_DATE         0x04
#define DS3231_MONTH        0x05
#define DS3231_YEAR         0x06
#define DS3231_CONTROL      0x0E
#define DS3231_STATUS       0x0F
#define DS3231_TEMP_MSB     0x11
#define DS3231_TEMP_LSB     0x12

// Control register bits
#define DS3231_OSF          7
#define DS3231_EOSC         7
#define DS3231_BBSQW        6
#define DS3231_CONV         5
#define DS3231_RS2          4
#define DS3231_RS1          3
#define DS3231_INTCN        2

//Other
#define DS3231_HR1224       6       // Hours register 12 or 24 hour mode (24 hour mode==0)
#define DS3231_CENTURY      7       // Century bit in Month register
#define DS3231_DYDT         6       // Day/Date flag bit in alarm Day/Date registers

/*-------------------------------------------------------------------------------------------*\
 * Read time from DS3231 and return the epoch time (second since 1-1-1970 00:00)
\*-------------------------------------------------------------------------------------------*/
uint32_t DS3231ReadTime(void) {
  TIME_T tm;
  tm.second = Bcd2Dec(I2cRead8(RtcChip.address, DS3231_SECONDS, RtcChip.bus));
  tm.minute = Bcd2Dec(I2cRead8(RtcChip.address, DS3231_MINUTES, RtcChip.bus));
  tm.hour = Bcd2Dec(I2cRead8(RtcChip.address, DS3231_HOURS, RtcChip.bus) & ~_BV(DS3231_HR1224)); // 24h mode
  tm.day_of_week = I2cRead8(RtcChip.address, DS3231_DAY, RtcChip.bus);
  tm.day_of_month = Bcd2Dec(I2cRead8(RtcChip.address, DS3231_DATE, RtcChip.bus));
  tm.month = Bcd2Dec(I2cRead8(RtcChip.address, DS3231_MONTH, RtcChip.bus) & ~_BV(DS3231_CENTURY));  // Don't use the Century bit
  // MakeTime requires tm.year as number of years since 1970, 
  // However DS3231 is supposed to hold the true year but before this PR it was written tm.year directly
  // Assuming we read ... means ...
  //   00..21   = 1970..1990 written before PR (to support a RTC written with 1970) => don't apply correction
  //   22..51   = 2022..2051 written after PR => apply +30 years correction
  //   52..99   = 2022..2069 written before PR => don't apply correction
  uint8_t year = Bcd2Dec(I2cRead8(RtcChip.address, DS3231_YEAR, RtcChip.bus));
  tm.year = ((year <= 21) || (year >= 52)) ? year : (year + 30);
  return MakeTime(tm);
}
/*-------------------------------------------------------------------------------------------*\
 * Read temperature from DS3231 internal sensor, return as float
\*-------------------------------------------------------------------------------------------*/
#ifdef DS3231_ENABLE_TEMP
float DS3231ReadTemp(void) {
  int16_t temp_reg = I2cReadS16(RtcChip.address, DS3231_TEMP_MSB, RtcChip.bus) >> 6;
  float temp = temp_reg * 0.25;
  //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("RTC: DS3231 temp_reg=%d"), temp_reg);
  return temp;
}
#endif // #ifdef DS3231_ENABLE_TEMP

/*-------------------------------------------------------------------------------------------*\
 * Show temperature from DS3231 internal sensor, Web or SENSOR
\*-------------------------------------------------------------------------------------------*/
#ifdef DS3231_ENABLE_TEMP
void D3231ShowSensor(bool json) {
    float f_temperature = ConvertTemp(DS3231ReadTemp());

    if (json) {
        ResponseAppend_P(PSTR(",\"DS3231\":{\"" D_JSON_TEMPERATURE "\":%*_f}"), Settings->flag2.temperature_resolution, &f_temperature);
#ifdef USE_DOMOTICZ
        if (0 == TasmotaGlobal.tele_period) {
          DomoticzFloatSensor(DZ_TEMP, f_temperature);
        }
#endif  // USE_DOMOTICZ
#ifdef USE_KNX
        if (0 == TasmotaGlobal.tele_period) {
          KnxSensor(KNX_TEMPERATURE, f_temperature);
        }
#endif  // USE_KNX
    } 
#ifdef USE_WEBSERVER
    else {
        WSContentSend_Temp("DS3231", f_temperature);
    }
#endif // #ifdef USE_WEBSERVER
}
#endif // #ifdef DS3231_ENABLE_TEMP

/*-------------------------------------------------------------------------------------------*\
 * Get time as TIME_T and set the DS3231 time to this value
\*-------------------------------------------------------------------------------------------*/
void DS3231SetTime(uint32_t epoch_time) {
  TIME_T tm;
  BreakTime(epoch_time, tm);
  I2cWrite8(RtcChip.address, DS3231_SECONDS, Dec2Bcd(tm.second), RtcChip.bus);
  I2cWrite8(RtcChip.address, DS3231_MINUTES, Dec2Bcd(tm.minute), RtcChip.bus);
  I2cWrite8(RtcChip.address, DS3231_HOURS, Dec2Bcd(tm.hour), RtcChip.bus);
  I2cWrite8(RtcChip.address, DS3231_DAY, tm.day_of_week, RtcChip.bus);
  I2cWrite8(RtcChip.address, DS3231_DATE, Dec2Bcd(tm.day_of_month), RtcChip.bus);
  I2cWrite8(RtcChip.address, DS3231_MONTH, Dec2Bcd(tm.month), RtcChip.bus);
  uint8_t true_year = (tm.year < 30) ? (tm.year + 70) : (tm.year - 30);
  I2cWrite8(RtcChip.address, DS3231_YEAR, Dec2Bcd(true_year), RtcChip.bus);
  I2cWrite8(RtcChip.address, DS3231_STATUS, I2cRead8(RtcChip.address, DS3231_STATUS, RtcChip.bus) & ~_BV(DS3231_OSF), RtcChip.bus);
}

/*-------------------------------------------------------------------------------------------*\
 * Detection
\*-------------------------------------------------------------------------------------------*/
void DS3231Detected(void) {
  if (!RtcChip.detected && I2cEnabled(XI2C_26)) {
    RtcChip.address = DS3231_ADDRESS;
    for (RtcChip.bus = 0; RtcChip.bus < MAX_I2C; RtcChip.bus++) {
      if (!I2cSetDevice(RtcChip.address, RtcChip.bus)) { continue; }
      if (I2cValidRead(RtcChip.address, DS3231_STATUS, 1, RtcChip.bus)) {
        RtcChip.detected = 1;
        strcpy_P(RtcChip.name, PSTR("DS3231"));
        RtcChip.ReadTime = &DS3231ReadTime;
        RtcChip.SetTime = &DS3231SetTime;
#ifdef DS3231_ENABLE_TEMP
        RtcChip.ShowSensor = &D3231ShowSensor;
#endif
        RtcChip.mem_size = -1;
        break; 
      }
    }
  }
}
#endif  // USE_DS3231

/*********************************************************************************************\
 * PCF85063 support
 *
 * I2C Address: 0x51
\*********************************************************************************************/
#ifdef USE_PCF85063

#define XI2C_92             92       // Unique ID for I2C device search

#define PCF85063_ADDRESS    0x51     // PCF85063 I2C Address

#define PCF85063_REG_CTRL1      0x00
#define PCF85063_REG_CTRL2      0x01
#define PCF85063_REG_OFFSET     0x02
#define PCF85063_REG_SECONDS    0x04
#define PCF85063_REG_MINUTES    0x05
#define PCF85063_REG_HOURS      0x06
#define PCF85063_REG_DAYS       0x07
#define PCF85063_REG_WEEKDAYS   0x08
#define PCF85063_REG_MONTHS     0x09
#define PCF85063_REG_YEARS      0x0A

/*-------------------------------------------------------------------------------------------*\
 * Read time and return the epoch time (second since 1-1-1970 00:00)
\*-------------------------------------------------------------------------------------------*/
uint32_t Pcf85063ReadTime(void) {
  TwoWire& myWire = I2cGetWire(RtcChip.bus);
  myWire.beginTransmission(RtcChip.address);
  myWire.write(PCF85063_REG_SECONDS);
  myWire.endTransmission(false);   // false -> repeated start
  myWire.requestFrom((uint8_t)RtcChip.address, (uint8_t)7);

  uint8_t sec   = myWire.read(); // 0x04
  uint8_t min   = myWire.read(); // 0x05
  uint8_t hour  = myWire.read(); // 0x06
  uint8_t day   = myWire.read(); // 0x07
  uint8_t wday  = myWire.read(); // 0x08
  uint8_t month = myWire.read(); // 0x09
  uint8_t year  = myWire.read(); // 0x0A

  TIME_T tm;
  tm.second       = Bcd2Dec(sec  & 0x7F); 
  tm.minute       = Bcd2Dec(min  & 0x7F);
  tm.hour         = Bcd2Dec(hour & 0x3F);
  tm.day_of_month = Bcd2Dec(day  & 0x3F);
  tm.day_of_week  = wday & 0x07; 
  tm.month        = Bcd2Dec(month & 0x1F) -1; 
  uint8_t y = Bcd2Dec(year);
  tm.year = (y + 30);
  return MakeTime(tm);
}

/*-------------------------------------------------------------------------------------------*\
 * Get time as TIME_T and set time to this value
\*-------------------------------------------------------------------------------------------*/
void Pcf85063SetTime(uint32_t epoch_time) {
  TIME_T tm;
  BreakTime(epoch_time, tm);

  uint8_t year = (tm.year -30); 
  if (year > 99) { year = 99; } 

  uint8_t bcd_sec   = Dec2Bcd(tm.second);
  uint8_t bcd_min   = Dec2Bcd(tm.minute);
  uint8_t bcd_hour  = Dec2Bcd(tm.hour);
  uint8_t bcd_day   = Dec2Bcd(tm.day_of_month);
  uint8_t bcd_wday  = tm.day_of_week & 0x07;
  uint8_t bcd_month = Dec2Bcd(tm.month +1);
  uint8_t bcd_year  = Dec2Bcd(year);

  TwoWire& myWire = I2cGetWire(RtcChip.bus);
  myWire.beginTransmission(RtcChip.address);
  myWire.write(PCF85063_REG_SECONDS);
  myWire.write(bcd_sec);
  myWire.write(bcd_min);
  myWire.write(bcd_hour);
  myWire.write(bcd_day);
  myWire.write(bcd_wday);
  myWire.write(bcd_month);
  myWire.write(bcd_year);
  myWire.endTransmission();
}

/*-------------------------------------------------------------------------------------------*\
 * Detection
\*-------------------------------------------------------------------------------------------*/
void Pcf85063Detected(void) {
  if (!RtcChip.detected && I2cEnabled(XI2C_92)) {
    RtcChip.address = PCF85063_ADDRESS;
    for (RtcChip.bus = 0; RtcChip.bus < MAX_I2C; RtcChip.bus++) {
      // Vyskúšame, či vieme prečítať nejaký register
      if (!I2cSetDevice(RtcChip.address, RtcChip.bus)) { continue; }
      // Skúsime napr. prečítať PCF85063_REG_CTRL1
      if (I2cValidRead(RtcChip.address, PCF85063_REG_CTRL1, 1, RtcChip.bus)) { 
        RtcChip.detected = 1;
        strcpy_P(RtcChip.name, PSTR("PCF85063"));
        RtcChip.ReadTime = &Pcf85063ReadTime;
        RtcChip.SetTime  = &Pcf85063SetTime;
        RtcChip.mem_size = -1;    // Nemá extra user RAM, ak by si nepotreboval
        // Ak by si chcel implementovať MemRead/MemWrite, doplň RtcChip.MemRead a RtcChip.MemWrite
        break;
      }
    }
  }
}
#endif // USE_PCF85063

/*********************************************************************************************\
 * BM8563 - Real Time Clock
 *
 * I2C Address: 0x51 (Fixed in library as BM8563_ADRESS)
\*********************************************************************************************/
#ifdef USE_BM8563

#define XI2C_59             59       // See I2CDEVICES.md

#include "BM8563.h"
BM8563 Bm8563Rtc;

uint32_t BM8563GetUtc(void) {
  RTC_TimeTypeDef RTCtime;
  // 1. read has errors ???
  Bm8563Rtc.GetTime(&RTCtime);
//   core2_globs.Rtc.GetTime(&RTCtime);
  RTC_DateTypeDef RTCdate;
  Bm8563Rtc.GetDate(&RTCdate);
  TIME_T tm;
  tm.second =  RTCtime.Seconds;
  tm.minute = RTCtime.Minutes;
  tm.hour = RTCtime.Hours;
  tm.day_of_week = RTCdate.WeekDay;
  tm.day_of_month = RTCdate.Date;
  tm.month = RTCdate.Month;
  tm.year = RTCdate.Year - 1970;
  return MakeTime(tm);
}

void BM8563SetUtc(uint32_t epoch_time) {
  TIME_T tm;
  BreakTime(epoch_time, tm);
  RTC_TimeTypeDef RTCtime;
  RTCtime.Hours = tm.hour;
  RTCtime.Minutes = tm.minute;
  RTCtime.Seconds = tm.second;
  Bm8563Rtc.SetTime(&RTCtime);
  RTC_DateTypeDef RTCdate;
  RTCdate.WeekDay = tm.day_of_week;
  RTCdate.Month = tm.month;
  RTCdate.Date = tm.day_of_month;
  RTCdate.Year = tm.year + 1970;
  Bm8563Rtc.SetDate(&RTCdate);
}

/*-------------------------------------------------------------------------------------------*\
 * Detection
\*-------------------------------------------------------------------------------------------*/
void BM8563Detected(void) {
  if (!RtcChip.detected && I2cEnabled(XI2C_59)) {
    RtcChip.address = BM8563_ADRESS;
    for (RtcChip.bus = 0; RtcChip.bus < MAX_I2C; RtcChip.bus++) {
      if (!I2cSetDevice(RtcChip.address, RtcChip.bus)) { continue; }
      Bm8563Rtc.begin(&I2cGetWire(RtcChip.bus));
      RtcChip.detected = 1;
      strcpy_P(RtcChip.name, PSTR("BM8563"));
      RtcChip.ReadTime = &BM8563GetUtc;
      RtcChip.SetTime = &BM8563SetUtc;
      RtcChip.mem_size = -1;
      break;
    }
  }
}
#endif  // USE_BM8563

/*********************************************************************************************\
 * PCF85363 support
 *
 * I2C Address: 0x51
\*********************************************************************************************/
#ifdef USE_PCF85363

#define XI2C_66             66      // See I2CDEVICES.md

#define PCF85363_ADDRESS    0x51    // PCF85363 I2C Address

/*-------------------------------------------------------------------------------------------*\
 * Read time and return the epoch time (second since 1-1-1970 00:00)
\*-------------------------------------------------------------------------------------------*/
uint32_t Pcf85363ReadTime(void) {
  TwoWire& myWire = I2cGetWire(RtcChip.bus);
  myWire.beginTransmission(RtcChip.address);
  myWire.write(0x00);
  myWire.endTransmission();

  uint8_t buffer[8];
  myWire.requestFrom(RtcChip.address, (uint8_t)8);
  for (uint32_t i = 0; i < 8; i++) { buffer[i] = myWire.read(); }
  myWire.endTransmission();

  TIME_T tm;
  tm.second = Bcd2Dec(buffer[1] & 0x7F);
  tm.minute = Bcd2Dec(buffer[2] & 0x7F);
  tm.hour = Bcd2Dec(buffer[3]);
  tm.day_of_month = Bcd2Dec(buffer[4]);
  tm.day_of_week = buffer[5];
  tm.month = Bcd2Dec(buffer[6]);
  tm.year = 30 + Bcd2Dec(buffer[7]);  // Offset from 1970. So 2022 - 1970 = 52
  return MakeTime(tm);
}

/*-------------------------------------------------------------------------------------------*\
 * Get time as TIME_T and set time to this value
\*-------------------------------------------------------------------------------------------*/
void Pcf85363SetTime(uint32_t epoch_time) {
  TIME_T tm;
  BreakTime(epoch_time, tm);

  uint8_t buffer[8];
  buffer[0] = 0x00;                  // 100th_seconds (not used)
  buffer[1] = Dec2Bcd(tm.second);
  buffer[2] = Dec2Bcd(tm.minute);
  buffer[3] = Dec2Bcd(tm.hour);
  buffer[4] = Dec2Bcd(tm.day_of_month);
  buffer[5] = tm.day_of_week;
  buffer[6] = Dec2Bcd(tm.month);
  buffer[7] = Dec2Bcd(tm.year -30);  // Offset from 1970

  TwoWire& myWire = I2cGetWire(RtcChip.bus);
/*
  // Handbook page 13
  myWire.beginTransmission(RtcChip.address);
  myWire.write(0x2E);
  myWire.write(0x01);                  // Set stop
  myWire.write(0xA4);                  // Clear prescaler
  for (uint32_t i = 0; i < 8; i++) { myWire.write(buffer[i]); }
  myWire.endTransmission();
  myWire.beginTransmission(RtcChip.address);
  myWire.write(0x2E);
  myWire.write(0x00);                  // Set start
  myWire.endTransmission();
*/
  myWire.beginTransmission(RtcChip.address);
  myWire.write(0x00);
  for (uint32_t i = 0; i < 8; i++) { myWire.write(buffer[i]); }
  myWire.endTransmission();
}

/*-------------------------------------------------------------------------------------------*\
 * Dump all registers
\*-------------------------------------------------------------------------------------------*/
/*
void Pcf85363Dump(void) {
  uint8_t buffer[64];

  TwoWire& myWire = I2cGetWire(RtcChip.bus);
  // 0x00 to 0x2F
  myWire.beginTransmission(RtcChip.address);
  myWire.write(0x00);
  myWire.endTransmission();
  myWire.requestFrom(RtcChip.address, (uint8_t)48);
  for (uint32_t i = 0; i < 48; i++) {
    buffer[i] = myWire.read();
  }
  myWire.endTransmission();
  AddLog(LOG_LEVEL_DEBUG, PSTR("P85: Read 0x00: %48_H"), buffer);

  // 0x40 to 0x7F
  myWire.beginTransmission(RtcChip.address);
  myWire.write(0x40);
  myWire.endTransmission();
  myWire.requestFrom(RtcChip.address, (uint8_t)64);
  for (uint32_t i = 0; i < 64; i++) {
    buffer[i] = myWire.read();
  }
  myWire.endTransmission();
  AddLog(LOG_LEVEL_DEBUG, PSTR("P85: Read 0x40: %64_H"), buffer);
}
*/

/*-------------------------------------------------------------------------------------------*\
 * Memory block functions
\*-------------------------------------------------------------------------------------------*/
int32_t Pcf8563MemRead(uint8_t *buffer, uint32_t size) {
  return I2cReadBuffer(RtcChip.address, 0x40, buffer, size, RtcChip.bus);
}

int32_t Pcf8563MemWrite(uint8_t *buffer, uint32_t size) {
  return I2cWriteBuffer(RtcChip.address, 0x40, (uint8_t *)buffer, size, RtcChip.bus);
}

/*-------------------------------------------------------------------------------------------*\
 * Detection
\*-------------------------------------------------------------------------------------------*/
void Pcf85363Detected(void) {
  if (!RtcChip.detected && I2cEnabled(XI2C_66)) {
    RtcChip.address = PCF85363_ADDRESS;
    for (RtcChip.bus = 0; RtcChip.bus < MAX_I2C; RtcChip.bus++) {
      if (!I2cSetDevice(RtcChip.address, RtcChip.bus)) { continue; }
      RtcChip.detected = 1;
      strcpy_P(RtcChip.name, PSTR("PCF85363"));
      RtcChip.ReadTime = &Pcf85363ReadTime;
      RtcChip.SetTime = &Pcf85363SetTime;
      RtcChip.mem_size = 64;
      RtcChip.MemRead = &Pcf8563MemRead;
      RtcChip.MemWrite = &Pcf8563MemWrite;
      break;
    }
  }
}
#endif // USE_PCF85363

/*********************************************************************************************\
 * RX8010 and RX8030 - Real Time Clock
 * based on linux/rtc-rx8010.c
 *
 * I2C Address: 0x32
\*********************************************************************************************/
#if defined(USE_RX8010) || defined(USE_RX8030)

#define XI2C_90             90       // See I2CDEVICES.md

#define RX8010_ADDRESS      0x32     // RX8010 I2C Address

// RX8010 Register Addresses
#define RX8010_REG_SEC		  0x10
#define RX8010_REG_MIN		  0x11
#define RX8010_REG_HOUR		  0x12
#define RX8010_REG_WDAY		  0x13
#define RX8010_REG_MDAY		  0x14
#define RX8010_REG_MONTH	  0x15
#define RX8010_REG_YEAR		  0x16
#ifdef USE_RX8030
#define RX80x0_REG_CTRL     0x1E
#endif
#ifdef USE_RX8010
#undef RX80x0_REG_CTRL
#define RX80x0_REG_CTRL		  0x1F
#endif

// Control Register (1Fh) bit positions
#define RX8010_BIT_CTRL_STOP	6

/*-------------------------------------------------------------------------------------------*\
 * Read time from RX8010 and return the epoch time (second since 1-1-1970 00:00)
\*-------------------------------------------------------------------------------------------*/
uint32_t Rx8010ReadTime(void) {
  TIME_T tm;

  uint8_t data[7];
  I2cReadBuffer(RtcChip.address, RX8010_REG_SEC, data, 7, RtcChip.bus);
  tm.second = Bcd2Dec(data[0] & 0x7F);
  tm.minute = Bcd2Dec(data[1] & 0x7F);
  tm.hour = Bcd2Dec(data[2] & 0x3F);    // Assumes 24hr clock
  tm.day_of_month = Bcd2Dec(data[3] & 0x3F);
  tm.month = Bcd2Dec(data[4] & 0x3F) -1;
  tm.year = Bcd2Dec(data[5]);
	if (tm.year < 70) { tm.year += 100; }
  tm.day_of_week = Bcd2Dec(data[6] & 0x7F);
  return MakeTime(tm);
}

/*-------------------------------------------------------------------------------------------*\
 * Get time as TIME_T and set the RX8010 time to this value
\*-------------------------------------------------------------------------------------------*/
void Rx8010SetTime(uint32_t epoch_time) {
  TIME_T tm;
  BreakTime(epoch_time, tm);
	// Set STOP bit before changing clock/calendar
  I2cWrite8(RtcChip.address, RX80x0_REG_CTRL, I2cRead8(RtcChip.address, RX80x0_REG_CTRL, RtcChip.bus) | _BV(RX8010_BIT_CTRL_STOP), RtcChip.bus);
  uint8_t data[7];
  data[0] = Dec2Bcd(tm.second);
  data[1] = Dec2Bcd(tm.minute);
  data[2] = Dec2Bcd(tm.hour);
  data[3] = Dec2Bcd(tm.day_of_month);
  data[4] = Dec2Bcd(tm.month +1);
  data[5] = Dec2Bcd(tm.year % 100);
  data[6] = Dec2Bcd(tm.day_of_week);
  I2cWriteBuffer(RtcChip.address, RX8010_REG_SEC, data, 7, RtcChip.bus);
	// Clear STOP bit after changing clock/calendar
  I2cWrite8(RtcChip.address, RX80x0_REG_CTRL, I2cRead8(RtcChip.address, RX80x0_REG_CTRL, RtcChip.bus) & ~_BV(RX8010_BIT_CTRL_STOP), RtcChip.bus);
}

/*-------------------------------------------------------------------------------------------*\
 * Detection
\*-------------------------------------------------------------------------------------------*/
void Rx8010Detected(void) {
  if (!RtcChip.detected && I2cEnabled(XI2C_90)) {
    RtcChip.address = RX8010_ADDRESS;
    for (RtcChip.bus = 0; RtcChip.bus < MAX_I2C; RtcChip.bus++) {
      if (!I2cSetDevice(RtcChip.address, RtcChip.bus)) { continue; }
      if (I2cValidRead(RtcChip.address, RX80x0_REG_CTRL, 1, RtcChip.bus)) {
        RtcChip.detected = 1;
#ifdef USE_RX8030
        strcpy_P(RtcChip.name, PSTR("RX8030"));
#else
        strcpy_P(RtcChip.name, PSTR("RX8010"));
#endif
        RtcChip.ReadTime = &Rx8010ReadTime;
        RtcChip.SetTime = &Rx8010SetTime;
        RtcChip.mem_size = -1;
        break;
      }
    }
  }
}
#endif  // USE_RX8010
/*********************************************************************************************\
 * RX8025T - Real Time Clock
 *
 * I2C Address: 0x32
\*********************************************************************************************/
#if defined(USE_RX8025)

#define XI2C_96             96       // See I2CDEVICES.md
#define RX8025_ADDRESS      0x32

// RX8025T Register Addresses (per datasheet table 0..F)
#define RX8025_REG_SEC      0x00
#define RX8025_REG_MIN      0x01
#define RX8025_REG_HOUR     0x02
#define RX8025_REG_WEEK     0x03     // bitfield 6..0 (Sun..Sat)
#define RX8025_REG_MDAY     0x04
#define RX8025_REG_MONTH    0x05     // 01..12
#define RX8025_REG_YEAR     0x06     // 00..99 (2000..2099)

#define RX8025_REG_EXT      0x0D
#define RX8025_REG_FLAG     0x0E
#define RX8025_REG_CTRL     0x0F

// FLAG bits (per table: ... VLF VDET)
#define RX8025_FLAG_VLF     1
#define RX8025_FLAG_VDET    0

// CTRL RESET bit0 = stop status (datasheet)
#define RX8025_BIT_CTRL_RESET  0

// WEEK bitfield <-> Tasmota day_of_week (1..7, Sunday=1)
static uint8_t Rx8025WeekToWday(uint8_t week) {
  week &= 0x7F;                       // ignore bit7
  for (uint8_t i = 0; i < 7; i++) {
    if (week & (1U << i)) { return (uint8_t)(i + 1); }  // Sun=1
  }
  return 1;
}

static uint8_t Rx8025WdayToWeek(uint8_t wday) {
  if (wday < 1 || wday > 7) { wday = 1; }
  return (uint8_t)(1U << (wday - 1)); // one-hot bits 0..6
}

static void Rx8025LogRaw(const char *tag, uint8_t bus, uint8_t addr,
                         const uint8_t data[7], uint8_t ctrl, uint8_t flag, uint8_t ext) {
  AddLog(LOG_LEVEL_DEBUG,
         PSTR("RTC: RX8025T %s bus=%d addr=0x%02X RAW[00..06]=%02X %02X %02X %02X %02X %02X %02X  CTRL=%02X FLAG=%02X EXT=%02X"),
         tag, bus, addr,
         data[0], data[1], data[2], data[3], data[4], data[5], data[6],
         ctrl, flag, ext);
}

static void Rx8025PreInitIfNeeded(void) {
  uint8_t ctrl = I2cRead8(RtcChip.address, RX8025_REG_CTRL, RtcChip.bus);
  uint8_t flag = I2cRead8(RtcChip.address, RX8025_REG_FLAG, RtcChip.bus);
  uint8_t ext  = I2cRead8(RtcChip.address, RX8025_REG_EXT,  RtcChip.bus);

  AddLog(LOG_LEVEL_DEBUG, PSTR("RTC: RX8025T PRE-INIT CTRL=%02X FLAG=%02X EXT=%02X"), ctrl, flag, ext);

  // If VLF or VDET is set, datasheet says initialize registers before use.
  if (flag & (_BV(RX8025_FLAG_VLF) | _BV(RX8025_FLAG_VDET))) {
    AddLog(LOG_LEVEL_DEBUG, PSTR("RTC: RX8025T VLF/VDET set -> clearing EXT/FLAG (arduino-style)"));

    // Keep CTRL upper bits, force reserved bits 2..1 = 0
    uint8_t ctrl_base = ctrl & 0xF8;

    // Enter stop status (RESET=1)
    I2cWrite8(RtcChip.address, RX8025_REG_CTRL, ctrl_base | _BV(RX8025_BIT_CTRL_RESET), RtcChip.bus);

    // Clear EXT and FLAG (common minimal init used by reference libs)
    I2cWrite8(RtcChip.address, RX8025_REG_EXT,  0x00, RtcChip.bus);
    I2cWrite8(RtcChip.address, RX8025_REG_FLAG, 0x00, RtcChip.bus);

    // Exit stop status (RESET=0)
    I2cWrite8(RtcChip.address, RX8025_REG_CTRL, ctrl_base, RtcChip.bus);

    ctrl = I2cRead8(RtcChip.address, RX8025_REG_CTRL, RtcChip.bus);
    flag = I2cRead8(RtcChip.address, RX8025_REG_FLAG, RtcChip.bus);
    ext  = I2cRead8(RtcChip.address, RX8025_REG_EXT,  RtcChip.bus);
    AddLog(LOG_LEVEL_DEBUG, PSTR("RTC: RX8025T POST-INIT CTRL=%02X FLAG=%02X EXT=%02X"), ctrl, flag, ext);
  }
}

/*-------------------------------------------------------------------------------------------*\
 * Read time from RX8025T and return epoch time
\*-------------------------------------------------------------------------------------------*/
uint32_t Rx8025ReadTime(void) {
  TIME_T tm;

  uint8_t data[7];
  I2cReadBuffer(RtcChip.address, RX8025_REG_SEC, data, 7, RtcChip.bus);

  uint8_t ctrl = I2cRead8(RtcChip.address, RX8025_REG_CTRL, RtcChip.bus);
  uint8_t flag = I2cRead8(RtcChip.address, RX8025_REG_FLAG, RtcChip.bus);
  uint8_t ext  = I2cRead8(RtcChip.address, RX8025_REG_EXT,  RtcChip.bus);

  Rx8025LogRaw("READ", RtcChip.bus, RtcChip.address, data, ctrl, flag, ext);

  tm.second = Bcd2Dec(data[0] & 0x7F);
  tm.minute = Bcd2Dec(data[1] & 0x7F);
  tm.hour   = Bcd2Dec(data[2] & 0x3F);

  tm.day_of_week  = Rx8025WeekToWday(data[3]);
  tm.day_of_month = Bcd2Dec(data[4] & 0x3F);

  // RX8025: MONTH is 01..12 (no -1)
  tm.month = Bcd2Dec(data[5] & 0x1F);

  // RX8025: YEAR is 00..99 for 2000..2099  => yOff = (2000-1970)+y2k = 30+y2k
  uint8_t y2k = Bcd2Dec(data[6]);
  tm.year = (uint8_t)(30 + y2k);

  uint32_t epoch = MakeTime(tm);

  AddLog(LOG_LEVEL_DEBUG,
         PSTR("RTC: RX8025T DECODE y2k=%u -> yOff=%u (abs=%u) m=%u d=%u w=%u %02u:%02u:%02u -> epoch=%u"),
         y2k, tm.year, (uint16_t)(1970 + tm.year),
         tm.month, tm.day_of_month, tm.day_of_week,
         tm.hour, tm.minute, tm.second,
         epoch);

  return epoch;
}

/*-------------------------------------------------------------------------------------------*\
 * Set RX8025T time from epoch
\*-------------------------------------------------------------------------------------------*/
void Rx8025SetTime(uint32_t epoch_time) {
  TIME_T tm;
  BreakTime(epoch_time, tm);

  uint16_t abs_year = (uint16_t)(1970 + tm.year);
  uint8_t y2k = 0;
  if (abs_year < 2000) { y2k = 0; }
  else if (abs_year > 2099) { y2k = 99; }
  else { y2k = (uint8_t)(abs_year - 2000); }

  AddLog(LOG_LEVEL_DEBUG,
         PSTR("RTC: RX8025T SET epoch=%u -> yOff=%u (abs=%u) m=%u d=%u w=%u %02u:%02u:%02u"),
         epoch_time, tm.year, abs_year,
         tm.month, tm.day_of_month, tm.day_of_week,
         tm.hour, tm.minute, tm.second);

  // CTRL: clear reserved bits 2..1, preserve others
  uint8_t ctrl0 = I2cRead8(RtcChip.address, RX8025_REG_CTRL, RtcChip.bus);
  uint8_t ctrl_base = ctrl0 & 0xF8;

  AddLog(LOG_LEVEL_DEBUG, PSTR("RTC: RX8025T CTRL before=%02X masked=%02X (set RESET bit0=stop)"), ctrl0, ctrl_base);

  // Enter stop status (RESET=1)
  I2cWrite8(RtcChip.address, RX8025_REG_CTRL, ctrl_base | _BV(RX8025_BIT_CTRL_RESET), RtcChip.bus);

  uint8_t ctrl1 = I2cRead8(RtcChip.address, RX8025_REG_CTRL, RtcChip.bus);
  AddLog(LOG_LEVEL_DEBUG, PSTR("RTC: RX8025T CTRL after STOP(read)=%02X"), ctrl1);

  uint8_t data[7];
  data[0] = Dec2Bcd(tm.second);
  data[1] = Dec2Bcd(tm.minute);
  data[2] = Dec2Bcd(tm.hour);
  data[3] = Rx8025WdayToWeek(tm.day_of_week);
  data[4] = Dec2Bcd(tm.day_of_month);

  // RX8025: MONTH is 01..12 (no +1)
  data[5] = Dec2Bcd(tm.month);

  data[6] = Dec2Bcd(y2k);

  AddLog(LOG_LEVEL_DEBUG,
         PSTR("RTC: RX8025T WRITE y2k=%u RAW=%02X %02X %02X %02X %02X %02X %02X"),
         y2k, data[0], data[1], data[2], data[3], data[4], data[5], data[6]);

  I2cWriteBuffer(RtcChip.address, RX8025_REG_SEC, data, 7, RtcChip.bus);

  // Optional readback for verification
  uint8_t rb[7];
  I2cReadBuffer(RtcChip.address, RX8025_REG_SEC, rb, 7, RtcChip.bus);
  uint8_t ctrl_rb = I2cRead8(RtcChip.address, RX8025_REG_CTRL, RtcChip.bus);
  uint8_t flag_rb = I2cRead8(RtcChip.address, RX8025_REG_FLAG, RtcChip.bus);
  uint8_t ext_rb  = I2cRead8(RtcChip.address, RX8025_REG_EXT,  RtcChip.bus);
  Rx8025LogRaw("READBACK", RtcChip.bus, RtcChip.address, rb, ctrl_rb, flag_rb, ext_rb);

  // Exit stop status (RESET=0)
  I2cWrite8(RtcChip.address, RX8025_REG_CTRL, ctrl_base, RtcChip.bus);
  uint8_t ctrl2 = I2cRead8(RtcChip.address, RX8025_REG_CTRL, RtcChip.bus);
  AddLog(LOG_LEVEL_DEBUG, PSTR("RTC: RX8025T CTRL after CLEAR(read)=%02X"), ctrl2);

  AddLog(LOG_LEVEL_DEBUG, PSTR("RTC: %s re-synced (" D_UTC_TIME ") %s"), RtcChip.name, GetDateAndTime(DT_UTC).c_str());
}

/*-------------------------------------------------------------------------------------------*\
 * Detection
\*-------------------------------------------------------------------------------------------*/
void Rx8025Detected(void) {
  if (!RtcChip.detected && I2cEnabled(XI2C_96)) {
    RtcChip.address = RX8025_ADDRESS;

    for (RtcChip.bus = 0; RtcChip.bus < MAX_I2C; RtcChip.bus++) {

      AddLog(LOG_LEVEL_DEBUG, PSTR("RTC: RX8025T DETECT try bus=%d addr=0x%02X"), RtcChip.bus, RtcChip.address);

      if (!I2cSetDevice(RtcChip.address, RtcChip.bus)) {
        AddLog(LOG_LEVEL_DEBUG, PSTR("RTC: RX8025T DETECT bus=%d -> I2cSetDevice FAIL"), RtcChip.bus);
        continue;
      }

      // Basic presence check: CTRL must be readable
      if (!I2cValidRead(RtcChip.address, RX8025_REG_CTRL, 1, RtcChip.bus)) {
        AddLog(LOG_LEVEL_DEBUG, PSTR("RTC: RX8025T DETECT bus=%d -> I2cValidRead(CTRL) FAIL"), RtcChip.bus);
        continue;
      }

      // If VLF/VDET set, clear them before using registers (datasheet requirement)
      Rx8025PreInitIfNeeded();

      // Debug snapshot of time regs at detect
      uint8_t data[7];
      I2cReadBuffer(RtcChip.address, RX8025_REG_SEC, data, 7, RtcChip.bus);
      uint8_t ctrl = I2cRead8(RtcChip.address, RX8025_REG_CTRL, RtcChip.bus);
      uint8_t flag = I2cRead8(RtcChip.address, RX8025_REG_FLAG, RtcChip.bus);
      uint8_t ext  = I2cRead8(RtcChip.address, RX8025_REG_EXT,  RtcChip.bus);
      Rx8025LogRaw("DETECTED", RtcChip.bus, RtcChip.address, data, ctrl, flag, ext);

      RtcChip.detected = 1;
      strcpy_P(RtcChip.name, PSTR("RX8025T"));
      RtcChip.ReadTime = &Rx8025ReadTime;
      RtcChip.SetTime  = &Rx8025SetTime;
      RtcChip.mem_size = -1;
      break;
    }
  }
}
#endif  // USE_RX8025

/*********************************************************************************************\
 * RTC Detect and time set
\*********************************************************************************************/

void RtcChipDetect(void) {
  RtcChip.detected = 0;
  RtcChip.bus = 0;

#ifdef USE_RV3028
  RV3028Detected();
#endif  // USE_RV3028
#ifdef USE_DS3231
  DS3231Detected();
#endif  // USE_DS3231
#ifdef USE_BM8563
  BM8563Detected();
#endif  // USE_BM8563
#ifdef USE_PCF85363
  Pcf85363Detected();
#endif // USE_PCF85363
#if defined(USE_RX8010) || defined(USE_RX8030)
  Rx8010Detected();
#endif  // USE_RX8010
#ifdef USE_PCF85063
  Pcf85063Detected();
#endif  // USE_PCF85063
#ifdef USE_RX8025
  Rx8025Detected();
#endif  // USE_RX8025

  if (!RtcChip.detected) { return; }

  I2cSetActiveFound(RtcChip.address, RtcChip.name, RtcChip.bus);

  if (Rtc.utc_time < START_VALID_TIME) {                          // Not sync with NTP/GPS (time not valid), so read time
    uint32_t time = RtcChip.ReadTime();                           // Read UTC TIME
    if (time > START_VALID_TIME) {
      Rtc.utc_time = time;
      RtcSync(RtcChip.name);
    }
  }
}

void RtcChipTimeSynced(void) {
  if ((Rtc.utc_time > START_VALID_TIME) &&                        // Valid UTC time
      (abs((int32_t)(Rtc.utc_time - RtcChip.ReadTime())) > 2)) {  // Time has drifted from RTC more than 2 seconds
    RtcChip.SetTime(Rtc.utc_time);                                // Update time
    AddLog(LOG_LEVEL_DEBUG, PSTR("RTC: %s re-synced (" D_UTC_TIME ") %s"), RtcChip.name, GetDateAndTime(DT_UTC).c_str());
  }
}

int32_t RtcChipMemSize(void) {
  return RtcChip.mem_size;                                        // Not supported or max size
}

int32_t RtcChipMemRead(uint8_t *buffer, uint32_t size) {
  if (size <= RtcChip.mem_size) {
    return RtcChip.MemRead(buffer, size);
  }
  return -1;                                                      // Not supported or too large
}

int32_t RtcChipMemWrite(uint8_t *buffer, uint32_t size) {
  if (size <= RtcChip.mem_size) {
    return RtcChip.MemWrite(buffer, size);
  }
  return -1;                                                      // Not supported or too large
}

/*********************************************************************************************\
 * NTP server functions
\*********************************************************************************************/
#ifdef RTC_NTP_SERVER

#include "NTPServer.h"
#include "NTPPacket.h"

#define NTP_MILLIS_OFFSET   50

const char kRtcChipCommands[] PROGMEM = "Rtc|"  // Prefix
  D_CMND_NTPSERVER;

void (* const RtcChipCommand[])(void) PROGMEM = {
  &CmndRtcNtpServer };

NtpServer RtcChipTimeServer(PortUdp);

void RtcChipEverySecond(void) {
  static bool ntp_server_started = false;

  if (TasmotaGlobal.global_state.network_down) { return; }  // Exception on ESP32 if network is down (#17338)

  if (Settings->sbflag1.local_ntp_server && (Rtc.utc_time > START_VALID_TIME)) {
    if (!ntp_server_started) {
      if (RtcChipTimeServer.beginListening()) {
        ntp_server_started = true;
        AddLog(LOG_LEVEL_DEBUG, PSTR("RTC: NTP server started"));
      }
    } else {
      RtcChipTimeServer.processOneRequest(Rtc.utc_time, NTP_MILLIS_OFFSET);
    }
  }
}

void CmndRtcNtpServer(void) {
  // RtcChipNtpServer 0 or 1
  if ((XdrvMailbox.payload >= 0) && !TasmotaGlobal.global_state.network_down) {
    Settings->sbflag1.local_ntp_server = 0;
    if ((XdrvMailbox.payload &1) && RtcChipTimeServer.beginListening()) {
      Settings->sbflag1.local_ntp_server = 1;
    }
  }
  ResponseCmndStateText(Settings->sbflag1.local_ntp_server);
}
#endif  // RTC_NTP_SERVER

/*********************************************************************************************\
 * Interface
\*********************************************************************************************/

bool Xdrv56(uint32_t function) {
  bool result = false;

#ifdef RTC_NTP_SERVER
  switch (function) {
    case FUNC_EVERY_SECOND:
      RtcChipEverySecond();
      break;
    case FUNC_COMMAND:
      result = DecodeCommand(kRtcChipCommands, RtcChipCommand);
      break;
  }
#endif  // RTC_NTP_SERVER

  if (FUNC_SETUP_RING1 == function) {
    RtcChipDetect();
  }
  else if (RtcChip.detected) {
    switch (function) {
      case FUNC_TIME_SYNCED:
        RtcChipTimeSynced();
        break;
      case FUNC_WEB_SENSOR:
        if (RtcChip.ShowSensor) RtcChip.ShowSensor(0);
        break;
      case FUNC_JSON_APPEND:
        if (RtcChip.ShowSensor) RtcChip.ShowSensor(1);
        break;
      case FUNC_ACTIVE:
        result = true;
        break;
    }
  }

  return result;
}

#endif  // USE_RTC_CHIPS
#endif  // USE_I2C
