#include "_Plugin_Helper.h"
#if defined(USES_P020) || defined(USES_P044)

// #######################################################################################################
// #################################### Plugin 020: Ser2Net ##############################################
// #################################### Plugin 044: P1WifiGateway ########################################
// #######################################################################################################

/************
 * Changelog:
 * 2025-03-26 tonhuisman: Add optional receiving (relaying) of UDP data to Serial. Uses the same port as configured for the (default) TCP
 *                        connection. Available on ESP32 only.
 *                        Can be enabled for ESP8266 in a Custom build by adding #define P020_USE_PROTOCOL 1
 *                        Added logging for pass-through data size from TCP or UDP to Serial.
 * 2025-03-23 tonhuisman: Add 'Event data hex format' setting to convert received data as hex so it can be sent via SerialSendMix.
 *                        Data received via TCP is now sent to serial via the 'write' method instead of 'print' so any binary content is
 *                        passed through unaltered.
 *                        Add command 'serialsend_test,<content>' for filling the serial buffer with the content, and processing as if
 *                        serial data was received.
 *                        Use RX Buffer size to allocate receive buffer instead of fixed size 256 (2048 for P1 processing), also used as
 *                        limit for receiving via TCP.
 * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported yet for Ser2Net or P1 gateway)
 * 2023-08-26 tonhuisman: P044 mode: Set RX time-out default to 50 msec for better receive pace of P1 data
 * 2023-08-17 tonhuisman: P1 data: Allow some extra reading timeout between the data and the checksum, as some meters need more time to
 *                        calculate the CRC. Add CR/LF before sending P1 data.
 * 2023-08-12 tonhuisman: Strip off occasional 8th bit from received data, to avoid unexpected failures on P1 data reception
 * 2023-06-24 tonhuisman: Fix initialization with non-GPIO serial ports like CDC/HW-CDC
 *                        Add option: append the task number to the event-name (Generic and RFLink event options)
 * 2023-06-23 tonhuisman: Add option: use Serial Port name (serialxxx -> xxx= 0/1/2/0swap/i2c/sw/hwcdc/cdc) as event-name for Generic events
 * 2023-06-02 tonhuisman: Allow buffer up to 2kB. Use ESPEasySerial buffering feature
 * 2023-03-25 tonhuisman: Change serialsendmix to handle 0x00 also, by implementing parseHexTextData()
 * 2022-12-12 tonhuisman: Add character conversion for the received serial data, act on Space and/or Newline
 * 2022-10-11 tonhuisman: Add option for including the message in P1 #data event
 * 2022-10-09 tonhuisman: Check P044 migration on PLUGIN_INIT too, still needs a manual save (from UI or by save command)
 * 2022-10-08 tonhuisman: Merged code from P044 into this plugin, and use a global flag to emulate P044 with P020
 *                        When USES_P044 is enabled, also USES_P020 will be enabled!
 *                        Add Led settings, similar to P044
 * 2022-05-28 tonhuisman: Add option to generate events for all lines of a multi-line message
 * 2022-05-26 tonhuisman: Add option to allow processing without webclient connected.
 * No older changelog available.
 ***************************************************************/

# include "src/Helpers/_Plugin_Helper_serial.h"
# include "src/PluginStructs/P020_data_struct.h"
# include <ESPeasySerial.h>


# define PLUGIN_020
# define PLUGIN_ID_020         20
# define PLUGIN_NAME_020       "Communication - Serial Server"
# define PLUGIN_VALUENAME1_020 "Ser2Net"

# define PLUGIN_ID_020_044     44
# define PLUGIN_NAME_020_044   "Communication - P1 Wifi Gateway"

bool P020_Emulate_P044 = false; // Global flag
# if defined(USES_P044) && !defined(USES_P044_ORG)

// Emulate P044 using P020 with a global flag
boolean Plugin_044(uint8_t function, struct EventStruct *event, String& string) {
  P020_Emulate_P044 = true;

  boolean result = Plugin_020(function, event, string);

  P020_Emulate_P044 = false;
  return result;
}

bool P020_ConvertP044Settings(struct EventStruct *event) {
  if (P020_Emulate_P044 && !P020_GET_P044_MODE_SAVED) {
    // Convert existing P044 settings to P020 settings
    P020_RX_WAIT = PCONFIG(0); // No conflict
    // P020_SERIAL_CONFIG    = PCONFIG(1); // No need to convert
    P020_RESET_TARGET_PIN = CONFIG_PIN1;

    // 'Conflicting' stuff, set defaults to: Serial0, RX=gpio-3 and TX=gpio-1
    CONFIG_PORT = static_cast<int>(ESPEasySerialPort::serial0);            // P044 Serial port
    CONFIG_PIN1 = 3;                                                       // P044 RX pin
    CONFIG_PIN2 = 1;                                                       // P044 TX pin

    // Former P044 defaults
    P020_FLAGS = 0u;                                                       // Reset
    bitSet(P020_FLAGS, P020_FLAG_LED_ENABLED);                             // Led enabled...
    P020_LED_PIN           = P020_STATUS_LED;                              // ...and connected to GPIO-12
    P020_SERIAL_PROCESSING = static_cast<int>(P020_Events::P1WiFiGateway); // Enable P1 WiFi Gateway processing
    return true;
  }
  return false;
}

# endif // if defined(USES_P044) && !defined(USES_P044_ORG)

boolean Plugin_020(uint8_t function, struct EventStruct *event, String& string)
{
  boolean success = false;

  switch (function)
  {
    case PLUGIN_DEVICE_ADD:
    {
      auto& dev = Device[++deviceCount];

      if (P020_Emulate_P044) {
        dev.Number         = PLUGIN_ID_020_044;
        dev.SendDataOption = false;
      } else {
        dev.Number         = PLUGIN_ID_020;
        dev.SendDataOption = true;
      }
      dev.Type  = DEVICE_TYPE_SERIAL;
      dev.VType = Sensor_VType::SENSOR_TYPE_STRING;
      break;
    }

    case PLUGIN_GET_DEVICENAME:
    {
      string = P020_Emulate_P044 ? F(PLUGIN_NAME_020_044) : F(PLUGIN_NAME_020);
      break;
    }

    # if FEATURE_MQTT_DISCOVER
    case PLUGIN_GET_DISCOVERY_VTYPES:
    {
      event->Par1 = static_cast<int>(Sensor_VType::SENSOR_TYPE_NONE); // Not yet supported
      success     = true;
      break;
    }
    # endif // if FEATURE_MQTT_DISCOVER

    case PLUGIN_SET_DEFAULTS:
    {
      if (P020_Emulate_P044) {
        CONFIG_PORT            = static_cast<int>(ESPEasySerialPort::serial0); // P044 Serial port
        CONFIG_PIN1            = 3;                                            // P044 RX pin
        CONFIG_PIN2            = 1;                                            // P044 TX pin
        P020_SET_BAUDRATE      = P020_DEFAULT_P044_BAUDRATE;
        P020_SET_SERVER_PORT   = P020_DEFAULT_P044_SERVER_PORT;
        P020_RESET_TARGET_PIN  = P020_DEFAULT_RESET_TARGET_PIN;
        P020_SERIAL_PROCESSING = static_cast<int>(P020_Events::P1WiFiGateway); // Enable P1 WiFi Gateway processing (only)
        P020_LED_PIN           = P020_STATUS_LED;
        P020_RX_WAIT           = 50;                                           // 50 msec for proper P1 packet receive mode
        P020_REPLACE_SPACE     = 0;                                            // Force empty
        P020_REPLACE_NEWLINE   = 0;
        P020_FLAGS             = 0u;                                           // Reset
        bitSet(P020_FLAGS, P020_FLAG_LED_ENABLED);
        bitSet(P020_FLAGS, P020_FLAG_P044_MODE_SAVED);                         // Inital config, no conversion needed
      } else {
        P020_SET_BAUDRATE     = P020_DEFAULT_BAUDRATE;
        P020_SET_SERVER_PORT  = P020_DEFAULT_SERVER_PORT;
        P020_RESET_TARGET_PIN = P020_DEFAULT_RESET_TARGET_PIN;
        P020_RX_BUFFER        = P020_DEFAULT_RX_BUFFER;
        P020_LED_PIN          = -1;
      }
      success = true;
      break;
    }


    case PLUGIN_WEBFORM_SHOW_CONFIG:
    {
      string += serialHelper_getSerialTypeLabel(event);
      success = true;
      break;
    }

    case PLUGIN_GET_DEVICEGPIONAMES:
    {
      serialHelper_getGpioNames(event);
      break;
    }

    case PLUGIN_WEBFORM_SHOW_GPIO_DESCR:
    {
      string  = F("RST: ");
      string += formatGpioLabel(P020_RESET_TARGET_PIN, false);
      string += event->String1;
      string += F("LED: ");
      string += formatGpioLabel(P020_GET_LED_ENABLED ? P020_LED_PIN : -1, false);

      if ((P020_GET_LED_INVERTED == 1) && (P020_GET_LED_ENABLED)) {
        string += F(" (inv)");
      }
      success = true;
      break;
    }

    # ifdef USES_P044
    case PLUGIN_WEBFORM_PRE_SERIAL_PARAMS:
    {
      // P044 Settings to convert?
      if (P020_Emulate_P044 && P020_ConvertP044Settings(event)) {
        addFormNote(F("Settings migrated from previous plugin version."));
      }
      break;
    }
    # endif // ifdef USES_P044

    case PLUGIN_WEBFORM_LOAD:
    {
      addFormNumericBox(F("TCP Port"), F("pport"), P020_GET_SERVER_PORT, 0);
      # ifndef LIMIT_BUILD_SIZE
      addUnit(F("0..65535"));
      # endif // ifndef LIMIT_BUILD_SIZE

      # if P020_USE_PROTOCOL
      const __FlashStringHelper*tcpudpOptions[] = {
        F("TCP"),
        F("UDP"),
        F("TCP & UDP"),
      };
      constexpr int tcpudpCount = NR_ELEMENTS(tcpudpOptions);
      FormSelectorOptions tcpudpSelector(tcpudpCount, tcpudpOptions);
      tcpudpSelector.default_index = 0;
      tcpudpSelector.addFormSelector(
        F("Protocol"),
        F("pproto"),
        P020_GET_PROTOCOL);
      # endif // if P020_USE_PROTOCOL

      addFormNumericBox(F("Baud Rate"), F("pbaud"), P020_GET_BAUDRATE, 0);
      uint8_t serialConfChoice = serialHelper_convertOldSerialConfig(P020_SERIAL_CONFIG);
      serialHelper_serialconfig_webformLoad(event, serialConfChoice);
      {
        if (!P020_Emulate_P044) {
          const __FlashStringHelper *options[] = {
            F("None"),
            F("Generic"),
            F("RFLink"),
            F("P1 WiFi Gateway")
          };

          constexpr int optionCount = NR_ELEMENTS(options);
          const FormSelectorOptions selector(optionCount, options /*, optionValues*/);
          selector.addFormSelector(
            F("Event processing"),
            F("pevents"),
            P020_SERIAL_PROCESSING);
        }
        addFormCheckBox(F("P1 #data event with message"), F("pp1event"), P020_GET_P1_EVENT_DATA);
        # ifndef LIMIT_BUILD_SIZE
        addFormNote(F("When enabled, passes the entire message in the event. <B>Warning:</B> can cause memory overflow issues!"));
        # endif // ifndef LIMIT_BUILD_SIZE
        addFormCheckBox(F("Event data hex format"), F("phexdata"), P020_GET_EVENT_AS_HEX);

        if (P020_Events::Generic == static_cast<P020_Events>(P020_SERIAL_PROCESSING)) {
          addFormCheckBox(F("Use Serial Port as eventname"), F("pevtname"), P020_GET_EVENT_SERIAL_ID);
          # ifndef LIMIT_BUILD_SIZE
          addFormNote(F("(Event processing: Generic only!)"));
          # endif // ifndef LIMIT_BUILD_SIZE
        }

        if (P020_Events::P1WiFiGateway != static_cast<P020_Events>(P020_SERIAL_PROCESSING)) {
          addFormCheckBox(F("Append Task Number to eventname"), F("papptask"), P020_GET_APPEND_TASK_ID);
          # ifndef LIMIT_BUILD_SIZE
          addFormNote(F("(Event processing: Generic and RFLink only!)"));
          # endif // ifndef LIMIT_BUILD_SIZE
        }

        if (!P020_Emulate_P044) { // Not appropriate for P1 WiFi Gateway
          addFormSeparatorCharInput(F("Replace spaces in event by"),   F("replspace"),
                                    P020_REPLACE_SPACE, F(P020_REPLACE_CHAR_SET), F(""));

          addFormSeparatorCharInput(F("Replace newlines in event by"), F("replcrlf"),
                                    P020_REPLACE_NEWLINE, F(P020_REPLACE_CHAR_SET), F(""));
        }

        addFormCheckBox(F("Process events without client"), F("pignoreclient"), P020_IGNORE_CLIENT_CONNECTED);
        # ifndef LIMIT_BUILD_SIZE
        addFormNote(F("When enabled, will process serial data without a network client connected."));
        # endif // ifndef LIMIT_BUILD_SIZE

        if (!P020_Emulate_P044) { // Not appropriate for P1 WiFi Gateway
          addFormCheckBox(F("Multiple lines processing"), F("pmultiline"), P020_HANDLE_MULTI_LINE);
        }
      }
      {
        addFormNumericBox(F("RX Receive Timeout (mSec)"), F("prxwait"), P020_RX_WAIT, 0, 200);
        addFormPinSelect(PinSelectPurpose::Generic_output, F("Reset target after init"), F("presetpin"), P020_RESET_TARGET_PIN);

        if (!P020_Emulate_P044) {
          addFormNumericBox(F("RX buffer size (bytes)"), F("prx_buffer"), P020_RX_BUFFER, 256, 2048);
          # ifndef LIMIT_BUILD_SIZE
          addFormNote(F("Standard RX buffer 256B; higher values could be unstable; energy meters could require 1024B"));
          # endif // ifndef LIMIT_BUILD_SIZE
        }
      }
      { // Led settings
        addFormSubHeader(F("Led"));

        addFormCheckBox(F("Led enabled"), F("pled"), P020_GET_LED_ENABLED);
        addFormPinSelect(PinSelectPurpose::Generic_output, F("Led pin"), F("pledpin"), P020_LED_PIN);
        addFormCheckBox(F("Led inverted"), F("pledinv"), P020_GET_LED_INVERTED == 1);
      }

      success = true;
      break;
    }

    case PLUGIN_WEBFORM_SAVE:
    {
      P020_SET_SERVER_PORT  = getFormItemInt(F("pport"));
      P020_SET_BAUDRATE     = getFormItemInt(F("pbaud"));
      P020_SERIAL_CONFIG    = serialHelper_serialconfig_webformSave();
      P020_RX_WAIT          = getFormItemInt(F("prxwait"));
      P020_RESET_TARGET_PIN = getFormItemInt(F("presetpin"));

      if (P020_Emulate_P044) {
        P020_SERIAL_PROCESSING = static_cast<int>(P020_Events::P1WiFiGateway); // Force P1 WiFi Gateway processing
      } else {
        P020_SERIAL_PROCESSING = getFormItemInt(F("pevents"));
        P020_RX_BUFFER         = getFormItemInt(F("prx_buffer"));
        P020_REPLACE_SPACE     = getFormItemInt(F("replspace"));
        P020_REPLACE_NEWLINE   = getFormItemInt(F("replcrlf"));
      }
      P020_LED_PIN = getFormItemInt(F("pledpin"));

      uint32_t lSettings = 0u;
      bitWrite(lSettings, P020_FLAG_IGNORE_CLIENT, isFormItemChecked(F("pignoreclient")));
      bitWrite(lSettings, P020_FLAG_LED_ENABLED,   isFormItemChecked(F("pled")));
      bitWrite(lSettings, P020_FLAG_LED_INVERTED,  isFormItemChecked(F("pledinv")));
      bitWrite(lSettings, P020_FLAG_P1_EVENT_DATA, isFormItemChecked(F("pp1event")));
      bitWrite(lSettings, P020_FLAG_EVENT_AS_HEX,  isFormItemChecked(F("phexdata")));

      if (P020_Events::Generic == static_cast<P020_Events>(P020_SERIAL_PROCESSING)) {
        bitWrite(lSettings, P020_FLAG_EVENT_SERIAL_ID, isFormItemChecked(F("pevtname")));
      }

      if (P020_Events::P1WiFiGateway != static_cast<P020_Events>(P020_SERIAL_PROCESSING)) {
        bitWrite(lSettings, P020_FLAG_APPEND_TASK_ID, isFormItemChecked(F("papptask")));
      }

      if (P020_Emulate_P044) {
        bitSet(lSettings, P020_FLAG_P044_MODE_SAVED); // Set to P044 configuration done on every save
      } else {
        bitWrite(lSettings, P020_FLAG_MULTI_LINE, isFormItemChecked(F("pmultiline")));
      }
      # if P020_USE_PROTOCOL
      set2BitToUL(lSettings, P020_FLAG_PROTOCOL, getFormItemInt(F("pproto")));
      # endif // if P020_USE_PROTOCOL

      P020_FLAGS = lSettings;

      success = true;
      break;
    }

    case PLUGIN_INIT:
    {
      # ifdef USES_P044

      // P044 Settings to convert?
      if (P020_Emulate_P044 && P020_ConvertP044Settings(event)) {
        addLog(LOG_LEVEL_INFO, F("P1   : Automatic settings conversion, please save settings manually."));
        bitSet(P020_FLAGS, P020_FLAG_P044_MODE_SAVED); // Set to P044 configuration done on next save
      }
      # endif // ifdef USES_P044

      if (P020_GET_LED_ENABLED && validGpio(P020_LED_PIN)) {
        pinMode(P020_LED_PIN, OUTPUT);
        digitalWrite(P020_LED_PIN, P020_GET_LED_INVERTED ? 1 : 0);
      }

      if ((P020_GET_SERVER_PORT == 0) || (P020_GET_BAUDRATE == 0)) {
        clearPluginTaskData(event->TaskIndex);
        break;
      }

      // try to reuse to keep webserver running
      P020_Task *task = static_cast<P020_Task *>(getPluginTaskData(event->TaskIndex));

      if ((nullptr != task) && task->isInit()) {
        // It was already created and initialzed
        // So don't recreate to keep the webserver running.
      } else {
        initPluginTaskData(event->TaskIndex, new (std::nothrow) P020_Task(event));
        task = static_cast<P020_Task *>(getPluginTaskData(event->TaskIndex));
      }

      if (nullptr == task) {
        break;
      }
      task->handleMultiLine = P020_HANDLE_MULTI_LINE && static_cast<P020_Events>(P020_SERIAL_PROCESSING) != P020_Events::P1WiFiGateway;

      int rxPin                    = CONFIG_PIN1;
      int txPin                    = CONFIG_PIN2;
      const ESPEasySerialPort port = static_cast<ESPEasySerialPort>(CONFIG_PORT);

      if ((rxPin < 0) && (txPin < 0)) {
        ESPeasySerialType::getSerialTypePins(port, rxPin, txPin);
        CONFIG_PIN1 = rxPin;
        CONFIG_PIN2 = txPin;
      }

      # ifndef LIMIT_BUILD_SIZE

      if (loglevelActiveFor(LOG_LEVEL_INFO)) {
        addLog(LOG_LEVEL_INFO, strformat(F("Ser2Net: TaskIndex=%d port=%d rxPin=%d txPin=%d BAUDRATE=%d SERVER_PORT=%d SERIAL_PROCESSING=%d"
                                           #  if P020_USE_PROTOCOL
                                           " Protocol=%d"
                                           #  endif // if P020_USE_PROTOCOL
                                           ),
                                         event->TaskIndex + 1,
                                         CONFIG_PORT,
                                         rxPin,
                                         txPin,
                                         P020_GET_BAUDRATE,
                                         P020_GET_SERVER_PORT,
                                         P020_SERIAL_PROCESSING
                                         #  if P020_USE_PROTOCOL
                                         , P020_GET_PROTOCOL
                                         #  endif // if P020_USE_PROTOCOL
                                         ));
      }
      # endif // ifndef LIMIT_BUILD_SIZE

      // serial0 on esp32 is Ser2net: port=2 rxPin=3 txPin=1; serial1 on esp32 is Ser2net: port=4 rxPin=13 txPin=15; Serial2 on esp32 is
      // Ser2net: port=4 rxPin=16 txPin=17
      uint8_t serialconfig = serialHelper_convertOldSerialConfig(P020_SERIAL_CONFIG);
      task->serialBegin(port, rxPin, txPin, P020_GET_BAUDRATE, serialconfig);
      task->startServer(P020_GET_SERVER_PORT);

      if (!task->isInit()) {
        clearPluginTaskData(event->TaskIndex);
        break;
      }

      if (validGpio(P020_RESET_TARGET_PIN)) {
        # ifndef BUILD_NO_DEBUG

        if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
          addLogMove(LOG_LEVEL_DEBUG, strformat(
                       F("Ser2net  : P020_RESET_TARGET_PIN : %d"),
                       P020_RESET_TARGET_PIN));
        }
        # endif // ifndef BUILD_NO_DEBUG
        pinMode(P020_RESET_TARGET_PIN, OUTPUT);
        digitalWrite(P020_RESET_TARGET_PIN, LOW);
        delay(500);
        digitalWrite(P020_RESET_TARGET_PIN, HIGH);
        pinMode(P020_RESET_TARGET_PIN, INPUT_PULLUP);
      }

      task->serial_processing = static_cast<P020_Events>(P020_SERIAL_PROCESSING);
      task->_P1EventData      = P020_GET_P1_EVENT_DATA;

      task->blinkLED();

      if (task->serial_processing == P020_Events::P1WiFiGateway) {
        task->_CRCcheck = P020_GET_BAUDRATE == 115200;
        # ifndef BUILD_NO_DEBUG

        if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
          addLog(LOG_LEVEL_DEBUG, strformat(F("P1   : DSMR version %d meter, CRC %s"),
                                            task->_CRCcheck ? 5 : 4,
                                            FsP(task->_CRCcheck ? F("on") : F("off"))));
        }
        # endif // ifndef BUILD_NO_DEBUG
      }

      success = true;
      break;
    }

    case PLUGIN_EXIT:
    {
      P020_Task *task = static_cast<P020_Task *>(getPluginTaskData(event->TaskIndex));

      if (nullptr != task) {
        task->stopServer();
        task->serialEnd();
      }
      success = true;
      break;
    }

    case PLUGIN_ONCE_A_SECOND:
    {
      P020_Task *task = static_cast<P020_Task *>(getPluginTaskData(event->TaskIndex));

      if (nullptr != task) {
        task->checkServer();
        success = true;
      }
      break;
    }

    case PLUGIN_FIFTY_PER_SECOND:
    {
      P020_Task *task = static_cast<P020_Task *>(getPluginTaskData(event->TaskIndex));

      if (nullptr != task) {
        const bool hasClient = task->hasClientConnected();
        # if P020_USE_PROTOCOL
        const P020_Protocol_e prot = static_cast<P020_Protocol_e>(P020_GET_PROTOCOL);
        const bool useUdp          = P020_Protocol_e::UDP == prot || P020_Protocol_e::TCP_UDP == prot;
        # endif // if P020_USE_PROTOCOL

        if (P020_IGNORE_CLIENT_CONNECTED || hasClient
            # if P020_USE_PROTOCOL
            || useUdp
            # endif // if P020_USE_PROTOCOL
            ) {
          if (hasClient
              # if P020_USE_PROTOCOL
              || useUdp
              # endif // if P020_USE_PROTOCOL
              ) {
            task->handleClientIn(event);
          }
          task->handleSerialIn(event); // in case of second serial connected, PLUGIN_SERIAL_IN is not called anymore
        }
        task->checkBlinkLED();
        success = true;
      }
      break;
    }

    case PLUGIN_SERIAL_IN:
    {
      P020_Task *task = static_cast<P020_Task *>(getPluginTaskData(event->TaskIndex));

      if (nullptr != task) {
        if (P020_IGNORE_CLIENT_CONNECTED || task->hasClientConnected()) {
          task->handleSerialIn(event);
        } else {
          task->discardSerialIn();
        }
        success = true;
      }
      break;
    }

    case PLUGIN_WRITE:
    {
      P020_Task *task = static_cast<P020_Task *>(getPluginTaskData(event->TaskIndex));

      if (nullptr != task) {
        String command = parseString(string, 1);

        if (equals(command, F("serialsend"))) {
          task->ser2netSerial->write(string.substring(11).c_str());
          task->ser2netSerial->flush();
          success = true;
        } else if (equals(command, F("serialsendmix"))) {
          std::vector<uint8_t> argument = parseHexTextData(string);
          task->ser2netSerial->write(&argument[0], argument.size());
          task->ser2netSerial->flush();
          success = true;
        } else if ((equals(command, F("ser2netclientsend"))) && (task->hasClientConnected())) {
          task->ser2netClient.print(string.substring(18));
          task->ser2netClient.PR_9453_FLUSH_TO_CLEAR();
          success = true;
        } else if (equals(command, F("serialsend_test"))) {
          task->serial_buffer = parseStringToEndKeepCaseNoTrim(string, 2);
          task->handleSerialIn(event);
          success = true;
        }
        break;
      }
    }
  }
  return success;
}

#endif // if defined(USES_P020) || defined(USES_P044)
