/*
;    Project:       ESP GLCD SOLAR MONITOR
;    Date:          3rd Sep 2022
;
;    (C) 2022       Carsten Schmiemann
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in
; all copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
; THE SOFTWARE.
*/
//#include <Arduino.h>
#include <logger.h>
#ifdef ESP32
  #include <SPIFFS.h>
  #define Flash SPIFFS
  #include <WiFi.h>
#endif
#ifdef ESP8266
  #include <LittleFS.h>
  #define Flash LittleFS
  #include <ESP8266WiFi.h>
#endif

#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <Wire.h>
#include <U8g2lib.h>
#include "time.h"

//Program parts
#include <pins.h>
#include <lang.h>
#include <display.h>

char VERSION[6] = "v1.0";

//Defaults
char mqtt_server[15] = "";
char gx_vrm_id[20] = "";
char disp_refresh_interval[6] = "500";
char disp_screen_interval[6] = "10000";
char address_pv_charger[6] = "";
char address_grid_meter[6] = "";
char address_inverter[6] = "";
char address_battery[6] = "";
char address_outside_temperature[6] = "";
char display_lang[6] = "";
char time_zone[40] = "CET-1CEST,M3.5.0,M10.5.0/3"; // Europe/Berlin
char time_server[20] = "";
char show_graph[4] = "";
char show_statistics[4] = "";
long unsigned int DISPLAY_REFRESH_INTERVAL; 
long unsigned int DISPLAY_SCREEN_INTERVAL;

//MQTT connect strings
char mqtt_pv_voltage[80];
char mqtt_pv_power[80];
char mqtt_pv_yield[80];
char mqtt_pv_bat_current[80];
char mqtt_grid_power[80];
char mqtt_inv_power[80];
char mqtt_inv_current[80];
char mqtt_bat_soc[80];
char mqtt_bat_voltage[80];
char mqtt_bat_current[80];
char mqtt_bat_power[80];
char mqtt_bat_max_cell_volt[80];
char mqtt_bat_min_cell_volt[80];
char mqtt_temp_outside[80];
char mqtt_grid_power_l1[80];
char mqtt_grid_power_l2[80];
char mqtt_grid_power_l3[80];

//Display config
bool display_conf_temp_oa_present = false;
bool display_conf_time_present = false;
bool display_conf_dc_pvcharger_present = false;
bool display_conf_graph_present = false;
bool display_conf_statistics_present = false;
const int display_graph_buffersize = 109;
float display_graph_ringbuffer_load_1h[display_graph_buffersize] = { 0 };
float display_graph_ringbuffer_load_24h[display_graph_buffersize] = { 0 };
float display_graph_ringbuffer_pv_1h[display_graph_buffersize] = { 0 };
float display_graph_ringbuffer_pv_24h[display_graph_buffersize] = { 0 };
int display_refresh_counter = 0;
int display_refresh_time;

//WiFiManager flag for saving data
bool shouldSaveConfig = false;

//WiFiManager callback notifying us of the need to save config
void saveConfigCallback () {
  logPrintlnI("Should save config");
  shouldSaveConfig = true;
}

//Library setup
WiFiClient espClient;
PubSubClient client(espClient);

//Time library setup
time_t now;
tm tm;


//Globals
long lastMsg = 0;
char msg[50];
float pv_v = 0, pv_w = 0, pv_i = 0, pv_kwh = 0, inv_p = 0, inv_i = 0, batt_v = 0, batt_i = 0, batt_p = 0, batt_c_min = 0, batt_c_max = 0, grid_p = 0, consum_p = 0, batt_soc = 0, load_ph1 = 0, load_ph2 = 0, load_ph3 = 0;
bool lastButtonRotationState = HIGH, lastButtonSetupState = HIGH;
bool currentButtonRotationState, currentButtonSetupState;
static unsigned long lastDispRefreshTime = 0;
static unsigned long lastScreenChangeTime = 0;
static unsigned long lastGraphRecord_1h = 0;
static unsigned long lastGraphRecord_24h = 0;
int display_screen = 0;

//Display graph ringbuffer
void display_graph_ringbuffer_add(float ringbuffer [display_graph_buffersize], float value) {
  ringbuffer[0] = value;
  memmove(ringbuffer, ringbuffer + 1, sizeof(int)*(display_graph_buffersize - 1));
  ringbuffer[display_graph_buffersize - 1] = value;
}

//MQTT topics
void callback(char* topic, byte* message, unsigned int length) {
  logPrintlnD("Message arrived on topic: ");
  logPrintlnD(topic);
  logPrintlnD("Message: ");
  String messageTemp;
  
  //Status LED flash on receive
  digitalWrite(STATUS_LED, !digitalRead(STATUS_LED));

  for (byte i = 0; i < length; i++) {
        char tmp = char(message[i]);
        messageTemp += tmp;
  }
  logPrintlnD(messageTemp);

  //PV Charger - solar voltage
  if (String(topic) == mqtt_pv_voltage) {
    DynamicJsonDocument json(512);
    deserializeJson(json, messageTemp);
    pv_v = json["value"];
  }
  //PV Charger - solar power
  if (String(topic) == mqtt_pv_power) {
    DynamicJsonDocument json(512);
    deserializeJson(json, messageTemp);
    pv_w = json["value"];
  }
  //PV Charger - solar yield
  if (String(topic) == mqtt_pv_yield) {
    DynamicJsonDocument json(512);
    deserializeJson(json, messageTemp);
    pv_kwh = json["value"];
  }
  //PV Charger - bat charge current
  if (String(topic) == mqtt_pv_bat_current) {
    DynamicJsonDocument json(512);
    deserializeJson(json, messageTemp);
    pv_i = json["value"];
  }

  //Grid meter - power
  if (String(topic) == mqtt_grid_power) {
    DynamicJsonDocument json(512);
    deserializeJson(json, messageTemp);
    grid_p = json["value"];
  }

  //Inverter - power
  if (String(topic) == mqtt_inv_power) {
    DynamicJsonDocument json(512);
    deserializeJson(json, messageTemp);
    inv_p = json["value"];
  }
  //Inverter - current
  if (String(topic) == mqtt_inv_current) {
    DynamicJsonDocument json(512);
    deserializeJson(json, messageTemp);
    inv_i = json["value"];
  }

  //Battery - SOC
  if (String(topic) == mqtt_bat_soc) {
    DynamicJsonDocument json(512);
    deserializeJson(json, messageTemp);
    batt_soc = json["value"];
  }
  //Battery - voltage
  if (String(topic) == mqtt_bat_voltage) {
    DynamicJsonDocument json(512);
    deserializeJson(json, messageTemp);
    batt_v = json["value"];
  }
  //Battery - current
  if (String(topic) == mqtt_bat_current) {
    DynamicJsonDocument json(512);
    deserializeJson(json, messageTemp);
    batt_i = json["value"];
  }
  //Battery - power
  if (String(topic) == mqtt_bat_power) {
    DynamicJsonDocument json(512);
    deserializeJson(json, messageTemp);
    batt_p = json["value"];
  }
  //Battery - max cell volts
  if (String(topic) == mqtt_bat_max_cell_volt) {
    DynamicJsonDocument json(512);
    deserializeJson(json, messageTemp);
    batt_c_max = json["value"];
  }
  //Battery - min cell volts
  if (String(topic) == mqtt_bat_min_cell_volt) {
    DynamicJsonDocument json(512);
    deserializeJson(json, messageTemp);
    batt_c_min = json["value"];
  }

  //Temperature - outside
  if (String(topic) == mqtt_temp_outside) {
    DynamicJsonDocument json(512);
    deserializeJson(json, messageTemp);
    display_temp_outside(json["value"]);
  }

  //System - AC Load Phase 1
  if (String(topic) == mqtt_grid_power_l1) {
    DynamicJsonDocument json(512);
    deserializeJson(json, messageTemp);
    load_ph1 = json["value"];
    consum_p = load_ph1 + load_ph2 + load_ph3; //calc consumption on each msg for precision
  }
  //System - AC Load Phase 2
  if (String(topic) == mqtt_grid_power_l2) {
    DynamicJsonDocument json(512);
    deserializeJson(json, messageTemp);
    load_ph2 = json["value"];
    consum_p = load_ph1 + load_ph2 + load_ph3; //calc consumption on each msg for precision
  }
  //System - AC Load Phase 3
  if (String(topic) == mqtt_grid_power_l3) {
    DynamicJsonDocument json(512);
    deserializeJson(json, messageTemp);
    load_ph3 = json["value"];
    consum_p = load_ph1 + load_ph2 + load_ph3; //calc consumption on each msg for precision
  }
}

//Wifi re-/connect
void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    logPrintlnI("Attempting MQTT connection...");
    display_text((char*)"Connect to GX MQTT...", mqtt_server);
    delay(2000);
    // Attempt to connect
    if (client.connect("PV_solar_monitor")) {
      logPrintlnI("connected");
      // Subscribe
      if (display_conf_dc_pvcharger_present)
      {
        client.subscribe(mqtt_pv_voltage);
        client.subscribe(mqtt_pv_power);
        client.subscribe(mqtt_pv_yield);
        client.subscribe(mqtt_pv_bat_current);
      }
      client.subscribe(mqtt_grid_power);
      client.subscribe(mqtt_inv_power);
      client.subscribe(mqtt_inv_current);
      client.subscribe(mqtt_bat_soc);
      client.subscribe(mqtt_bat_voltage);
      client.subscribe(mqtt_bat_current);
      client.subscribe(mqtt_bat_power);
      client.subscribe(mqtt_bat_max_cell_volt);
      client.subscribe(mqtt_bat_min_cell_volt);
      if (display_conf_temp_oa_present)
      {
        client.subscribe(mqtt_temp_outside);
      }
      client.subscribe(mqtt_grid_power_l1);
      client.subscribe(mqtt_grid_power_l2);
      client.subscribe(mqtt_grid_power_l3);
      logPrintlnI("subscribed to topics");
    } else {
      logPrintlnE("failed, rc=");
      logPrintlnE((char*)client.state());
      logPrintlnE(" try again in 10 seconds");
      String msg = "  MQTT Error rc=" + (String)client.state();
      display_text_fullscreen((char*)"MQTT Connection Error", (char*)"Did you enable MQTT", (char*)"on your GX device?", (char*)msg.c_str(), (char*)"", (char*)"Retry in 10 seconds...");
      // Wait 10 seconds before retrying
      delay(5000);
    }
  }
}

void setup() {
  Serial.begin(115200);
  logPrintlnI("Booting...");
  pinMode(BUTTON_ROTATION, INPUT_PULLUP);
  pinMode(BUTTON_SETUP, INPUT_PULLUP);
  pinMode(STATUS_LED, OUTPUT);
  display_begin();
  logPrintlnI("Display init...");
  display_init(VERSION);
  delay(2000);

  //read configuration from Flash json
  logPrintlnI("mounting Flash...");

  if (Flash.begin()) {
    logPrintlnI("mounted file system");
    if (Flash.exists("/config.json")) {
      //file exists, reading and loading
      logPrintlnI("reading config file");
      File configFile = Flash.open("/config.json", "r");
      if (configFile) {
        logPrintlnI("opened config file");
        size_t size = configFile.size();
        // Allocate a buffer to store contents of the file.
        std::unique_ptr<char[]> buf(new char[size]);

        configFile.readBytes(buf.get(), size);

        #if defined(ARDUINOJSON_VERSION_MAJOR) && ARDUINOJSON_VERSION_MAJOR >= 6
        DynamicJsonDocument json(1024);
        auto deserializeError = deserializeJson(json, buf.get());
        serializeJson(json, Serial);
        if ( ! deserializeError ) {
        #else
        DynamicJsonBuffer jsonBuffer;
        JsonObject& json = jsonBuffer.parseObject(buf.get());
        json.printTo(Serial);
        if (json.success()) {
        #endif
          logPrintlnI("\nparsed json");
          strcpy(mqtt_server, json["mqtt_server"]);
          strcpy(gx_vrm_id, json["gx_vrm_id"]);
          strcpy(disp_refresh_interval, json["disp_refresh_interval"]);
          strcpy(disp_screen_interval, json["disp_screen_interval"]);
          strcpy(address_pv_charger, json["address_pv_charger"]);
          strcpy(address_grid_meter, json["address_grid_meter"]);
          strcpy(address_inverter, json["address_inverter"]);
          strcpy(address_battery, json["address_battery"]);
          strcpy(address_outside_temperature, json["address_outside_temperature"]);
          strcpy(display_lang, json["display_language"]);
          strcpy(time_zone, json["time_zone"]);
          strcpy(time_server, json["time_server"]);
          strcpy(show_graph, json["show_graph"]);
          strcpy(show_statistics, json["show_statistics"]);
        } else {
          logPrintlnI("failed to load json config");
        }
        configFile.close();
      }
    }
  } else {
    logPrintlnI("failed to mount Flash");
  }

  delay(2000);
  String bootmsg = "Booting solar monitor app..." + (String)VERSION;
  logPrintlnI(bootmsg.c_str());
  display_text((char*)"Connect to AP:PV_Mon", (char*)"  for configuration");
  
  #ifdef ESP8266
      WiFi.hostname("ESP_Solar_Monitor");
  #endif
  #ifdef ESP32
      WiFi.setHostname("ESP_Solar_Monitor");
  #endif
  
  //Wifi Manager parameters
  WiFiManagerParameter custom_mqtt_server("mqtt_server", "IP adress of GX Device (MQTT No SSL)", mqtt_server, 15);
  WiFiManagerParameter custom_gx_vrm_id("gx_vrm_id", "VRM ID of GX device", gx_vrm_id, 20);
  WiFiManagerParameter custom_disp_refresh_interval("disp_refresh_interval", "Refresh cycle of display in ms", disp_refresh_interval, 6);
  WiFiManagerParameter custom_disp_screen_interval("disp_screen_interval", "Rotation time of screens in ms", disp_screen_interval, 6);
  WiFiManagerParameter custom_address_pv_charger("address_pv_charger", "Address of pv charger", address_pv_charger, 6);
  WiFiManagerParameter custom_address_grid_meter("address_grid_meter", "Address of grid meter", address_grid_meter, 6);
  WiFiManagerParameter custom_address_inverter("address_inverter", "Address of vebus inverter", address_inverter, 6);
  WiFiManagerParameter custom_address_battery("address_battery", "Address of can battery", address_battery, 6);
  WiFiManagerParameter custom_address_outside_temperature("address_outside_temperature", "Address of outside temperature", address_outside_temperature, 6);
  WiFiManagerParameter custom_display_lang("display_lang", "Display language en, de", display_lang, 6);
  WiFiManagerParameter custom_time_zone("time_zone", "Set timezone string", time_zone, 40);
  WiFiManagerParameter custom_time_server("time_server", "Set time server", time_server, 20);
  WiFiManagerParameter custom_show_graph("show_graph", "Show PV graph yes=enable", show_graph, 4);
  WiFiManagerParameter custom_show_statistics("show_statistics", "Show statistics screen yes=enable", show_statistics, 4);
  WiFiManager wm;

  wm.setSaveConfigCallback(saveConfigCallback);
  wm.addParameter(&custom_mqtt_server);
  wm.addParameter(&custom_gx_vrm_id);
  wm.addParameter(&custom_disp_refresh_interval);
  wm.addParameter(&custom_disp_screen_interval);
  wm.addParameter(&custom_address_pv_charger);
  wm.addParameter(&custom_address_grid_meter);
  wm.addParameter(&custom_address_inverter);
  wm.addParameter(&custom_address_battery);
  wm.addParameter(&custom_address_outside_temperature);
  wm.addParameter(&custom_display_lang);
  wm.addParameter(&custom_time_zone);
  wm.addParameter(&custom_time_server);
  wm.addParameter(&custom_show_graph);
  wm.addParameter(&custom_show_statistics);

  //Start wifi manager configuration if BUTTON_SETUP is pressed on start-up
  currentButtonSetupState = digitalRead(BUTTON_SETUP);
  if(lastButtonSetupState == HIGH && currentButtonSetupState == LOW) 
  {
    display_text((char*)"Connect to AP:PV_Mon", (char*)"for re-configuration");
    wm.startConfigPortal("PV_Mon");
  } else 
  {
    wm.autoConnect("PV_Mon");
  }

  display_text((char*)"  Connecting wifi...", (char*)"");
  delay(500);
  logPrintlnI("Connecting wifi...");
  IPAddress ip = WiFi.localIP();
  strcpy(mqtt_server, custom_mqtt_server.getValue());
  strcpy(gx_vrm_id, custom_gx_vrm_id.getValue());
  strcpy(disp_refresh_interval, custom_disp_refresh_interval.getValue());
  strcpy(disp_screen_interval, custom_disp_screen_interval.getValue());
  strcpy(address_pv_charger, custom_address_pv_charger.getValue());
  strcpy(address_grid_meter, custom_address_grid_meter.getValue());
  strcpy(address_inverter, custom_address_inverter.getValue());
  strcpy(address_battery, custom_address_battery.getValue());
  strcpy(address_outside_temperature, custom_address_outside_temperature.getValue());
  strcpy(display_lang, custom_display_lang.getValue());
  strcpy(time_zone, custom_time_zone.getValue());
  strcpy(time_server, custom_time_server.getValue());
  strcpy(show_graph, custom_show_graph.getValue());
  strcpy(show_statistics, custom_show_statistics.getValue());

  DISPLAY_REFRESH_INTERVAL = strtol(disp_refresh_interval, NULL, 10);
  DISPLAY_SCREEN_INTERVAL = strtol(disp_screen_interval, NULL, 10);
  display_refresh_time = DISPLAY_SCREEN_INTERVAL / DISPLAY_REFRESH_INTERVAL;

  //parse config and set display parameter
  if (strtol(address_outside_temperature, NULL, 10) != 0)
  {
    display_conf_temp_oa_present = true;
  }
  else
  {
    display_conf_temp_oa_present = false;
  }
  if (strcmp(time_server,"") != 0)
  {
    display_conf_time_present = true;
  }
  else
  {
    display_conf_time_present = false;
  }
  if (strtol(address_pv_charger, NULL, 10) != 0)
  {
    display_conf_dc_pvcharger_present = true;
  }
  else
  {
    display_conf_dc_pvcharger_present = false;
  }
  if (strcmp(show_graph,"yes") == 0)
  {
    display_conf_graph_present = true;
  }
  else
  {
    display_conf_graph_present = false;
  }
  if (strcmp(show_statistics,"yes") == 0)
  {
    display_conf_statistics_present = true;
  }
  else
  {
    display_conf_statistics_present = false;
  }

  display_setup(display_lang, display_conf_temp_oa_present, display_conf_time_present, display_conf_dc_pvcharger_present, display_conf_graph_present, display_conf_statistics_present);

  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
  char* ip_address = new char[40]();
  sprintf(ip_address, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
  display_text((char*)"   Connected. IP:", ip_address);
  //save the custom parameters to Flash
  if (shouldSaveConfig) {
    logPrintlnI("saving config");
  #if defined(ARDUINOJSON_VERSION_MAJOR) && ARDUINOJSON_VERSION_MAJOR >= 6
    DynamicJsonDocument json(1024);
  #else
    DynamicJsonBuffer jsonBuffer;
    JsonObject& json = jsonBuffer.createObject();
  #endif
    json["mqtt_server"] = mqtt_server;
    json["gx_vrm_id"] = gx_vrm_id;
    json["disp_refresh_interval"] = disp_refresh_interval;
    json["disp_screen_interval"] = disp_screen_interval;
    json["address_pv_charger"] = address_pv_charger;
    json["address_grid_meter"] = address_grid_meter;
    json["address_inverter"] = address_inverter;
    json["address_battery"] = address_battery;
    json["address_outside_temperature"] = address_outside_temperature;
    json["display_language"] = display_lang;
    json["time_zone"] = time_zone;
    json["time_server"] = time_server;
    json["show_graph"] = show_graph;
    json["show_statistics"] = show_statistics;

    File configFile = Flash.open("/config.json", "w");
    if (!configFile) {
      logPrintlnI("failed to open config file for writing");
    }

  #if defined(ARDUINOJSON_VERSION_MAJOR) && ARDUINOJSON_VERSION_MAJOR >= 6
    serializeJson(json, Serial);
    serializeJson(json, configFile);
  #else
    json.printTo(Serial);
    json.printTo(configFile);
  #endif
    configFile.close();
    //end save
  }

  if (display_conf_time_present)
  {
    //NTP setup
    #ifdef ESP32
      configTime(0, 0, time_server);
      setenv("TZ", time_zone, 1);
      tzset();
    #endif
    #ifdef ESP8266
      configTime(time_zone, time_server);
    #endif
  }


  delay(1000); //for screen display

  //Generate mqtt topics from id and address
  logPrintlnI("-------------- MQTT topic list --------------");
  sprintf(mqtt_pv_voltage, "N/%s/solarcharger/%s/Pv/V", gx_vrm_id, address_pv_charger);
  logPrintlnI(mqtt_pv_voltage);
  sprintf(mqtt_pv_power, "N/%s/solarcharger/%s/Yield/Power", gx_vrm_id, address_pv_charger);
  logPrintlnI(mqtt_pv_power);
  sprintf(mqtt_pv_yield, "N/%s/solarcharger/%s/History/Daily/0/Yield", gx_vrm_id, address_pv_charger);
  logPrintlnI(mqtt_pv_yield);
  sprintf(mqtt_pv_bat_current, "N/%s/solarcharger/%s/Dc/0/Current", gx_vrm_id, address_pv_charger);
  logPrintlnI(mqtt_pv_bat_current);
  sprintf(mqtt_grid_power, "N/%s/grid/%s/Ac/Power", gx_vrm_id, address_grid_meter);
  logPrintlnI(mqtt_grid_power);
  sprintf(mqtt_inv_power, "N/%s/vebus/%s/Ac/ActiveIn/L1/P", gx_vrm_id, address_inverter);
  logPrintlnI(mqtt_inv_power);
  sprintf(mqtt_inv_current, "N/%s/vebus/%s/Dc/0/Current", gx_vrm_id, address_inverter);
  logPrintlnI(mqtt_inv_current);
  sprintf(mqtt_bat_soc, "N/%s/battery/%s/Soc", gx_vrm_id, address_battery);
  logPrintlnI(mqtt_bat_soc);
  sprintf(mqtt_bat_voltage, "N/%s/battery/%s/Dc/0/Voltage", gx_vrm_id, address_battery);
  logPrintlnI(mqtt_bat_voltage);
  sprintf(mqtt_bat_current, "N/%s/battery/%s/Dc/0/Current", gx_vrm_id, address_battery);
  logPrintlnI(mqtt_bat_current);
  sprintf(mqtt_bat_power, "N/%s/battery/%s/Dc/0/Power", gx_vrm_id, address_battery);
  logPrintlnI(mqtt_bat_power);
  sprintf(mqtt_bat_max_cell_volt, "N/%s/battery/%s/System/MaxCellVoltage", gx_vrm_id, address_battery);
  logPrintlnI(mqtt_bat_max_cell_volt);
  sprintf(mqtt_bat_min_cell_volt, "N/%s/battery/%s/System/MinCellVoltage", gx_vrm_id, address_battery);
  logPrintlnI(mqtt_bat_min_cell_volt);
  sprintf(mqtt_temp_outside, "N/%s/temperature/%s/Temperature", gx_vrm_id, address_outside_temperature);
  logPrintlnI(mqtt_temp_outside);
  sprintf(mqtt_grid_power_l1, "N/%s/system/0/Ac/ConsumptionOnInput/L1/Power", gx_vrm_id);
  logPrintlnI(mqtt_grid_power_l1);
  sprintf(mqtt_grid_power_l2, "N/%s/system/0/Ac/ConsumptionOnInput/L2/Power", gx_vrm_id);
  logPrintlnI(mqtt_grid_power_l2);
  sprintf(mqtt_grid_power_l3, "N/%s/system/0/Ac/ConsumptionOnInput/L3/Power", gx_vrm_id);
  logPrintlnI(mqtt_grid_power_l3);

  Serial.println();
  Serial.println("-------------- Settings --------------");
  Serial.print("mqtt server: "); Serial.println(mqtt_server);
  Serial.print("gx_vrm_id: "); Serial.println(gx_vrm_id);
  Serial.print("disp_refresh_interval: "); Serial.println(DISPLAY_REFRESH_INTERVAL);
  Serial.print("disp_screen_interval: "); Serial.println(DISPLAY_SCREEN_INTERVAL);
  Serial.print("display language: "); Serial.println(display_lang);
  Serial.print("display temp present: "); Serial.println(display_conf_temp_oa_present);
  Serial.print("display time present: "); Serial.println(display_conf_time_present);
  Serial.print("display dc pc chg present: "); Serial.println(display_conf_dc_pvcharger_present);
  Serial.print("display graph present: "); Serial.println(display_conf_graph_present);
  Serial.print("display stats present: "); Serial.println(display_conf_statistics_present);
  display_text_fullscreen((char*)"Configuration summary", (char*)"GX MQTT Server IP:", mqtt_server, (char*)"GX VRM ID:", gx_vrm_id, (char*)"                 1/2");
  delay(3000);
  display_text_fullscreen((char*)"Configuration summary", (char*)"Disp. Refresh:", disp_refresh_interval, (char*)"Disp. rotation:", disp_screen_interval, (char*)"                 2/2");
  delay(3000);
  lastDispRefreshTime = millis();
  lastScreenChangeTime = millis();
  lastGraphRecord_1h = millis();
  lastGraphRecord_24h = millis();
}

void loop() {	
  //Refresh display values
	if(millis() - lastDispRefreshTime >= DISPLAY_REFRESH_INTERVAL)
	{
		digitalWrite(STATUS_LED, LOW); //Turn Status LED ON again
    lastDispRefreshTime += DISPLAY_REFRESH_INTERVAL;

    if (display_refresh_counter <= display_refresh_time)
    {
      display_screen_grid(grid_p, consum_p, inv_p, inv_i, pv_w);
    }
    if (display_refresh_counter >= display_refresh_time && display_refresh_counter <= (display_refresh_time*2))
    {
      display_screen_battery(batt_v, batt_i, batt_p, batt_soc, batt_c_min, batt_c_max);
    }
    if (display_refresh_counter >= (display_refresh_time*2) && display_refresh_counter <= (display_refresh_time*3))
    {
      if (display_conf_dc_pvcharger_present)
      {
        display_screen_pv(pv_v, pv_w, batt_v, pv_i, pv_kwh);
      }
      else
      {
        display_refresh_counter = (display_refresh_time*3) + 1;
      }
    }
    if (display_refresh_counter >= (display_refresh_time*3) && display_refresh_counter <= (display_refresh_time*4))
    {
      if (display_conf_graph_present)
      {
        display_screen_graph(display_graph_ringbuffer_load_1h, (char*)"1h", 0, display_graph_buffersize);
      }
      else
      {
        display_refresh_counter = (display_refresh_time*4) + 1;
      }
    }
        if (display_refresh_counter >= (display_refresh_time*4) && display_refresh_counter <= (display_refresh_time*5))
    {
      if (display_conf_graph_present)
      {
        display_screen_graph(display_graph_ringbuffer_load_24h, (char*)"24h", 0, display_graph_buffersize);
      }
      else
      {
        display_refresh_counter = (display_refresh_time*5) + 1;
      }
    }
        if (display_refresh_counter >= (display_refresh_time*5) && display_refresh_counter <= (display_refresh_time*6))
    {
      if (display_conf_graph_present && display_conf_dc_pvcharger_present)
      {
        display_screen_graph(display_graph_ringbuffer_pv_1h, (char*)"1h", 1, display_graph_buffersize);
      }
      else
      {
        display_refresh_counter = (display_refresh_time*6) + 1;
      }
    }
        if (display_refresh_counter >= (display_refresh_time*6) && display_refresh_counter <= (display_refresh_time*7))
    {
      if (display_conf_graph_present && display_conf_dc_pvcharger_present)
      {
        display_screen_graph(display_graph_ringbuffer_pv_24h, (char*)"24h", 1, display_graph_buffersize);
      }
      else
      {
        display_refresh_counter = (display_refresh_time*7) + 1;
      }
    }
    if (display_refresh_counter >= (display_refresh_time*7) && display_refresh_counter <= (display_refresh_time*8))
    {
      if (display_conf_statistics_present)
      {
        //To Do: display_screen_statistics(mqtt_grid_power_l1, mqtt_grid_power_l2, mqtt_grid_power_l3);
      }
      else
      {
        display_refresh_counter = (display_refresh_time*8) + 1;
      }
    }

    display_refresh_counter++;
    if (display_refresh_counter > (display_refresh_time*8))
    {
      display_refresh_counter = 0;
    }
	}

  //Change screen after time if intervall setting not zero
  if (DISPLAY_SCREEN_INTERVAL != 0)
  {
    if(millis() - lastScreenChangeTime >= DISPLAY_SCREEN_INTERVAL)
	  {
      time(&now); localtime_r(&now, &tm); //Get local time

      display_time(tm.tm_hour, tm.tm_min);
		  lastScreenChangeTime += DISPLAY_SCREEN_INTERVAL;
    }
	}

  //Graph recorder - 1h
  if (display_conf_graph_present)
  {
    if(millis() - lastGraphRecord_1h >= ((1 *60*60*1000 ) / display_graph_buffersize)) //temporarily set to half minute
    {
      display_graph_ringbuffer_add(display_graph_ringbuffer_load_1h, consum_p);
      if (display_conf_dc_pvcharger_present)
      {
        display_graph_ringbuffer_add(display_graph_ringbuffer_pv_1h, pv_w);
      }
      lastGraphRecord_1h += (1 *60*60*1000 ) / display_graph_buffersize;
    }
    if(millis() - lastGraphRecord_24h >= ((24 *60*60*1000 ) / display_graph_buffersize))
    {
      display_graph_ringbuffer_add(display_graph_ringbuffer_load_24h, consum_p);
      if (display_conf_dc_pvcharger_present)
      {
        display_graph_ringbuffer_add(display_graph_ringbuffer_pv_24h, pv_w);
      }
      lastGraphRecord_24h += (24 *60*60*1000 ) / display_graph_buffersize;
    }
  }

  //Rotate screen if BUTTON_ROTATION is pressed
  currentButtonRotationState = digitalRead(BUTTON_ROTATION);
  if(lastButtonRotationState == HIGH && currentButtonRotationState == LOW) 
  {
    display_screen++;
    lastButtonRotationState = currentButtonRotationState;
  }

  if (!client.connected()) {
    reconnect();
  }
  client.loop();
}