/*
 * Copyright (c) 2017 Shaun Feakes - All rights reserved
 *
 * You may use redistribute and/or modify this code under the terms of
 * the GNU General Public License version 2 as published by the 
 * Free Software Foundation. For the terms of this license, 
 * see <http://www.gnu.org/licenses/>.
 *
 * You are free to use this software under the terms of the GNU General
 * Public License, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 *  https://github.com/sfeakes/aqualinkd
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <string.h>
#include <sys/time.h>
#include <syslog.h>


#include "mongoose.h"

#include "aqualink.h"
#include "config.h"
#include "aq_programmer.h"
#include "utils.h"
#include "net_services.h"
#include "json_messages.h"
#include "domoticz.h"
#include "aq_mqtt.h"
#include "devices_jandy.h"
#include "color_lights.h"

#ifdef AQ_PDA
#include "pda.h"
#endif


//static struct aqconfig *_aqconfig_;
static struct aqualinkdata *_aqualink_data;
static char *_web_root;

static int _mqtt_exit_flag = false;
/*
static const char *s_address = "trident:1883";
static const char *s_user_name = NULL;
static const char *s_password = NULL;
static const char *s_topic = "domoticz/out";
*/
//static struct mg_mqtt_topic_expression s_topic_expr = {NULL, 0};



#ifndef MG_DISABLE_MQTT
void start_mqtt(struct mg_mgr *mgr);
static struct aqualinkdata _last_mqtt_aqualinkdata;
void mqtt_broadcast_aqualinkstate(struct mg_connection *nc);
#endif


static sig_atomic_t s_signal_received = 0;
//static const char *s_http_port = "8080";
static struct mg_serve_http_opts s_http_server_opts;

static void signal_handler(int sig_num) {
  signal(sig_num, signal_handler);  // Reinstantiate signal handler
  s_signal_received = sig_num;
}


static int is_websocket(const struct mg_connection *nc) {
  return nc->flags & MG_F_IS_WEBSOCKET && !(nc->flags & MG_F_USER_2);
}
static void set_websocket_RSraw(struct mg_connection *nc) {
  nc->flags |= MG_F_USER_2; 
}
static int is_websocket_RSraw(const struct mg_connection *nc) {
  return nc->flags & MG_F_USER_2;
}
static int is_mqtt(const struct mg_connection *nc) {
  return nc->flags & MG_F_USER_1;
}
static void set_mqtt(struct mg_connection *nc) {
  nc->flags |= MG_F_USER_1; 
}

static void ws_send(struct mg_connection *nc, char *msg)
{
  int size = strlen(msg);
  
  mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, msg, size);
  
  //logMessage (LOG_DEBUG, "WS: Sent %d characters '%s'\n",size, msg);
}

void broadcast_aqualinkstate_error(struct mg_connection *nc, char *msg) 
{
  struct mg_connection *c;
  char data[JSON_STATUS_SIZE];
  build_aqualink_error_status_JSON(data, JSON_STATUS_SIZE, msg);

  for (c = mg_next(nc->mgr, NULL); c != NULL; c = mg_next(nc->mgr, c)) {
    if (is_websocket(c))
      ws_send(c, data);
  }
  // Maybe enhacment in future to sent error messages to MQTT
}

void broadcast_aqualinkstate(struct mg_connection *nc) 
{
  static int mqtt_count=0;
  struct mg_connection *c;
  char data[JSON_STATUS_SIZE];
  build_aqualink_status_JSON(_aqualink_data, data, JSON_STATUS_SIZE);
  
#ifndef MG_DISABLE_MQTT  
  if (_mqtt_exit_flag == true) {
    mqtt_count++;
    if (mqtt_count >= 10) {
      start_mqtt(nc->mgr);
      mqtt_count = 0;
    }
  }
#endif

  for (c = mg_next(nc->mgr, NULL); c != NULL; c = mg_next(nc->mgr, c)) {
    if (is_websocket(c))
      ws_send(c, data);
#ifndef MG_DISABLE_MQTT
    else if (is_mqtt(c))
      mqtt_broadcast_aqualinkstate(c);
#endif
  }
  return;
}


void send_mqtt(struct mg_connection *nc, char *toppic, char *message)
{
  static uint16_t msg_id = 0;

  if (toppic == NULL)
    return;

  if (msg_id >= 65535){msg_id=1;}else{msg_id++;}

  //mg_mqtt_publish(nc, toppic, msg_id, MG_MQTT_QOS(0), message, strlen(message));
  mg_mqtt_publish(nc, toppic, msg_id, MG_MQTT_RETAIN | MG_MQTT_QOS(1), message, strlen(message));

  logMessage(LOG_INFO, "MQTT: Published id=%d: %s %s\n", msg_id, toppic, message);
}


void send_domoticz_mqtt_state_msg(struct mg_connection *nc, int idx, int value) 
{
  if (idx <= 0)
    return;

  char mqtt_msg[JSON_MQTT_MSG_SIZE];
  build_mqtt_status_JSON(mqtt_msg ,JSON_MQTT_MSG_SIZE, idx, value, TEMP_UNKNOWN);
  send_mqtt(nc, _aqconfig_.mqtt_dz_pub_topic, mqtt_msg);
}

void send_domoticz_mqtt_temp_msg(struct mg_connection *nc, int idx, int value) 
{
  if (idx <= 0)
    return;

  char mqtt_msg[JSON_MQTT_MSG_SIZE];
  build_mqtt_status_JSON(mqtt_msg ,JSON_MQTT_MSG_SIZE, idx, 0, (_aqualink_data->temp_units==FAHRENHEIT && _aqconfig_.convert_dz_temp)?roundf(degFtoC(value)):value);
  send_mqtt(nc, _aqconfig_.mqtt_dz_pub_topic, mqtt_msg);
}
void send_domoticz_mqtt_numeric_msg(struct mg_connection *nc, int idx, int value) 
{
  if (idx <= 0)
    return;

  char mqtt_msg[JSON_MQTT_MSG_SIZE];
  build_mqtt_status_JSON(mqtt_msg ,JSON_MQTT_MSG_SIZE, idx, 0, value);
  send_mqtt(nc, _aqconfig_.mqtt_dz_pub_topic, mqtt_msg);
}
void send_domoticz_mqtt_status_message(struct mg_connection *nc, int idx, int value, char *svalue) {
  if (idx <= 0)
    return;

  char mqtt_msg[JSON_MQTT_MSG_SIZE];
  build_mqtt_status_message_JSON(mqtt_msg, JSON_MQTT_MSG_SIZE, idx, value, svalue);

  send_mqtt(nc, _aqconfig_.mqtt_dz_pub_topic, mqtt_msg);
}

void send_mqtt_state_msg(struct mg_connection *nc, char *dev_name, aqledstate state)
{
  static char mqtt_pub_topic[250];

  sprintf(mqtt_pub_topic, "%s/%s/delay",_aqconfig_.mqtt_aq_topic, dev_name);
  send_mqtt(nc, mqtt_pub_topic, (state==FLASH?MQTT_ON:MQTT_OFF));

  sprintf(mqtt_pub_topic, "%s/%s",_aqconfig_.mqtt_aq_topic, dev_name);
  send_mqtt(nc, mqtt_pub_topic, (state==OFF?MQTT_OFF:MQTT_ON));
}

//void send_mqtt_aux_msg(struct mg_connection *nc, char *root_topic, int dev_index, char *dev_topic, int value)
void send_mqtt_aux_msg(struct mg_connection *nc, char *dev_name, char *dev_topic, int value)
{
  static char mqtt_pub_topic[250];
  static char msg[10];
  
  sprintf(msg, "%d", value);

  //sprintf(mqtt_pub_topic, "%s/%s%d%s",_aqconfig_.mqtt_aq_topic, root_topic, dev_index, dev_topic);
  sprintf(mqtt_pub_topic, "%s/%s%s",_aqconfig_.mqtt_aq_topic, dev_name, dev_topic);
  send_mqtt(nc, mqtt_pub_topic, msg);
}

void send_mqtt_heater_state_msg(struct mg_connection *nc, char *dev_name, aqledstate state)
{

  static char mqtt_pub_topic[250];

  sprintf(mqtt_pub_topic, "%s/%s",_aqconfig_.mqtt_aq_topic, dev_name);

  if (state == ENABLE) {
    send_mqtt(nc, mqtt_pub_topic, MQTT_OFF);
    sprintf(mqtt_pub_topic, "%s/%s%s",_aqconfig_.mqtt_aq_topic, dev_name, ENABELED_SUBT);
    send_mqtt(nc, mqtt_pub_topic, MQTT_ON);
  } else {
    send_mqtt(nc, mqtt_pub_topic, (state==OFF?MQTT_OFF:MQTT_ON));
    sprintf(mqtt_pub_topic, "%s/%s%s",_aqconfig_.mqtt_aq_topic, dev_name, ENABELED_SUBT);
    send_mqtt(nc, mqtt_pub_topic, (state==OFF?MQTT_OFF:MQTT_ON));
  }
}

// NSF need to change this function to the _new once finished.
void send_mqtt_temp_msg(struct mg_connection *nc, char *dev_name, long value)
{
  static char mqtt_pub_topic[250];
  static char degC[10];
  sprintf(degC, "%.2f", (_aqualink_data->temp_units==FAHRENHEIT && _aqconfig_.convert_mqtt_temp)?degFtoC(value):value );
  sprintf(mqtt_pub_topic, "%s/%s", _aqconfig_.mqtt_aq_topic, dev_name);
  send_mqtt(nc, mqtt_pub_topic, degC);
}
/*
void send_mqtt_temp_msg_new(struct mg_connection *nc, char *dev_name, long value)
{
  static char mqtt_pub_topic[250];
  static char degC[5];
  // NSF remove false below once we have finished.
  sprintf(degC, "%.2f", (false && _aqualink_data->temp_units==FAHRENHEIT && _aqconfig_.convert_mqtt_temp)?degFtoC(value):value );
  //sprintf(degC, "%d", value );
  sprintf(mqtt_pub_topic, "%s/%s", _aqconfig_.mqtt_aq_topic, dev_name);
  send_mqtt(nc, mqtt_pub_topic, degC);
}
*/
void send_mqtt_setpoint_msg(struct mg_connection *nc, char *dev_name, long value)
{
  static char mqtt_pub_topic[250];
  static char degC[10];
  
  sprintf(degC, "%.2f", (_aqualink_data->temp_units==FAHRENHEIT && _aqconfig_.convert_mqtt_temp)?degFtoC(value):value );
  sprintf(mqtt_pub_topic, "%s/%s/setpoint", _aqconfig_.mqtt_aq_topic, dev_name);
  send_mqtt(nc, mqtt_pub_topic, degC);
}
void send_mqtt_numeric_msg(struct mg_connection *nc, char *dev_name, int value)
{
  static char mqtt_pub_topic[250];
  static char msg[10];
  
  sprintf(msg, "%d", value);
  sprintf(mqtt_pub_topic, "%s/%s", _aqconfig_.mqtt_aq_topic, dev_name);
  send_mqtt(nc, mqtt_pub_topic, msg);
}
void send_mqtt_float_msg(struct mg_connection *nc, char *dev_name, float value) {
  static char mqtt_pub_topic[250];
  static char msg[10];

  sprintf(msg, "%.2f", value);
  sprintf(mqtt_pub_topic, "%s/%s", _aqconfig_.mqtt_aq_topic, dev_name);
  send_mqtt(nc, mqtt_pub_topic, msg);
}

void send_mqtt_int_msg(struct mg_connection *nc, char *dev_name, int value) {
  send_mqtt_numeric_msg(nc, dev_name, value);
  /*
  static char mqtt_pub_topic[250];
  static char msg[10];

  sprintf(msg, "%d", value);
  sprintf(mqtt_pub_topic, "%s/%s", _aqconfig_.mqtt_aq_topic, dev_name);
  send_mqtt(nc, mqtt_pub_topic, msg);
  */
}

void send_mqtt_string_msg(struct mg_connection *nc, char *dev_name, char *msg) {
  static char mqtt_pub_topic[250];

  sprintf(mqtt_pub_topic, "%s/%s", _aqconfig_.mqtt_aq_topic, dev_name);
  send_mqtt(nc, mqtt_pub_topic, msg);
}


void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
{
  static int cnt=0;
  //static long int lastFlashTm = 0;
  //static bool lastFlash = false;
  bool force_update = false;

  if (cnt > 300) {  // 100 = about every 2 minutes.
    force_update = true;
    cnt = 0;
  } else {
    cnt++;
    force_update = false;
  }
  int i;

//logMessage(LOG_INFO, "mqtt_broadcast_aqualinkstate: START\n");

  if (_aqualink_data->service_mode_state != _last_mqtt_aqualinkdata.service_mode_state) {
     _last_mqtt_aqualinkdata.service_mode_state = _aqualink_data->service_mode_state;
     send_mqtt_string_msg(nc, SERVICE_MODE_TOPIC, _aqualink_data->service_mode_state==OFF?MQTT_OFF:(_aqualink_data->service_mode_state==FLASH?MQTT_FLASH:MQTT_ON));
  }

  if (_aqualink_data->air_temp != TEMP_UNKNOWN && _aqualink_data->air_temp != _last_mqtt_aqualinkdata.air_temp) {
    _last_mqtt_aqualinkdata.air_temp = _aqualink_data->air_temp;
    send_mqtt_temp_msg(nc, AIR_TEMP_TOPIC, _aqualink_data->air_temp);
    //send_mqtt_temp_msg_new(nc, AIR_TEMPERATURE_TOPIC, _aqualink_data->air_temp);
    send_domoticz_mqtt_temp_msg(nc, _aqconfig_.dzidx_air_temp, _aqualink_data->air_temp);
  }
/*
  if (_aqualink_data->pool_temp != TEMP_UNKNOWN && _aqualink_data->pool_temp != _last_mqtt_aqualinkdata.pool_temp) {
    _last_mqtt_aqualinkdata.pool_temp = _aqualink_data->pool_temp;
    send_mqtt_temp_msg(nc, POOL_TEMP_TOPIC, _aqualink_data->pool_temp);
    send_domoticz_mqtt_temp_msg(nc, _aqconfig_.dzidx_pool_water_temp, _aqualink_data->pool_temp);
    // IF spa is off, report pool water temp to Domoticz.
    if (_aqualink_data->spa_temp == TEMP_UNKNOWN)
      send_domoticz_mqtt_temp_msg(nc, _aqconfig_.dzidx_spa_water_temp, _aqualink_data->pool_temp);
  }
*/

  if (_aqualink_data->pool_temp != _last_mqtt_aqualinkdata.pool_temp) {
    if (_aqualink_data->pool_temp == TEMP_UNKNOWN && _aqconfig_.report_zero_pool_temp) {
      _last_mqtt_aqualinkdata.pool_temp = TEMP_UNKNOWN;
      send_mqtt_temp_msg(nc, POOL_TEMP_TOPIC, (_aqconfig_.convert_mqtt_temp?-18:0));
    } else if (_aqualink_data->pool_temp != TEMP_UNKNOWN) {
      _last_mqtt_aqualinkdata.pool_temp = _aqualink_data->pool_temp;
      send_mqtt_temp_msg(nc, POOL_TEMP_TOPIC, _aqualink_data->pool_temp);
      send_domoticz_mqtt_temp_msg(nc, _aqconfig_.dzidx_pool_water_temp, _aqualink_data->pool_temp);
      // IF spa is off, report pool water temp to Domoticz.
      if (_aqualink_data->spa_temp == TEMP_UNKNOWN)
        send_domoticz_mqtt_temp_msg(nc, _aqconfig_.dzidx_spa_water_temp, _aqualink_data->pool_temp);
    }
  } 
  
  if (_aqualink_data->spa_temp != _last_mqtt_aqualinkdata.spa_temp) {
    if (_aqualink_data->spa_temp == TEMP_UNKNOWN && _aqconfig_.report_zero_spa_temp) {
      _last_mqtt_aqualinkdata.spa_temp = TEMP_UNKNOWN;
      send_mqtt_temp_msg(nc, SPA_TEMP_TOPIC, (_aqconfig_.convert_mqtt_temp?-18:0));
    } else if (_aqualink_data->spa_temp != TEMP_UNKNOWN) {
      _last_mqtt_aqualinkdata.spa_temp = _aqualink_data->spa_temp;
      send_mqtt_temp_msg(nc, SPA_TEMP_TOPIC, _aqualink_data->spa_temp);
      send_domoticz_mqtt_temp_msg(nc, _aqconfig_.dzidx_spa_water_temp, _aqualink_data->spa_temp);
    }
  } 

  if (_aqualink_data->pool_htr_set_point != TEMP_UNKNOWN && _aqualink_data->pool_htr_set_point != _last_mqtt_aqualinkdata.pool_htr_set_point) {
    _last_mqtt_aqualinkdata.pool_htr_set_point = _aqualink_data->pool_htr_set_point;
    send_mqtt_setpoint_msg(nc, BTN_POOL_HTR, _aqualink_data->pool_htr_set_point);
    // removed until domoticz has a better virtuel thermostat
    //send_domoticz_mqtt_temp_msg(nc, _aqconfig_.dzidx_pool_thermostat, _aqualink_data->pool_htr_set_point);
  }

  if (_aqualink_data->spa_htr_set_point != TEMP_UNKNOWN && _aqualink_data->spa_htr_set_point != _last_mqtt_aqualinkdata.spa_htr_set_point) {
    _last_mqtt_aqualinkdata.spa_htr_set_point = _aqualink_data->spa_htr_set_point;
    send_mqtt_setpoint_msg(nc, BTN_SPA_HTR, _aqualink_data->spa_htr_set_point);
  }
/*
  if (_aqualink_data->service_mode_state != _last_mqtt_aqualinkdata.service_mode_state) {
    _last_mqtt_aqualinkdata.service_mode_state = _aqualink_data->service_mode_state;
    send_mqtt_string_msg(nc, SERVICE_MODE_TOPIC, _aqualink_data->service_mode_state==ON?MQTT_ON:MQTT_OFF);
  }
*/
  if (_aqualink_data->frz_protect_set_point != TEMP_UNKNOWN && _aqualink_data->frz_protect_set_point != _last_mqtt_aqualinkdata.frz_protect_set_point) {
    _last_mqtt_aqualinkdata.frz_protect_set_point = _aqualink_data->frz_protect_set_point;
    send_mqtt_setpoint_msg(nc, FREEZE_PROTECT, _aqualink_data->frz_protect_set_point);
    send_mqtt_string_msg(nc, FREEZE_PROTECT, _aqualink_data->frz_protect_state==ON?MQTT_ON:MQTT_OFF);
    _last_mqtt_aqualinkdata.frz_protect_state = _aqualink_data->frz_protect_state;
    send_mqtt_string_msg(nc, FREEZE_PROTECT_ENABELED, MQTT_ON);
    /*
    send_mqtt_string_msg(nc, FREEZE_PROTECT_ENABELED, MQTT_ON);
    //send_domoticz_mqtt_temp_msg(nc, _aqconfig_.dzidx_rfz_protect, _aqualink_data->frz_protect_set_point);
  } else if (_aqualink_data->frz_protect_set_point != _last_mqtt_aqualinkdata.frz_protect_set_point) {
    _last_mqtt_aqualinkdata.frz_protect_set_point = _aqualink_data->frz_protect_set_point;
    send_mqtt_string_msg(nc, FREEZE_PROTECT_ENABELED, MQTT_OFF);*/
  }

  if (_aqualink_data->frz_protect_state != _last_mqtt_aqualinkdata.frz_protect_state) {
    _last_mqtt_aqualinkdata.frz_protect_state = _aqualink_data->frz_protect_state;
    send_mqtt_string_msg(nc, FREEZE_PROTECT, _aqualink_data->frz_protect_state==ON?MQTT_ON:MQTT_OFF); 
  }

  if (_aqualink_data->battery != _last_mqtt_aqualinkdata.battery) {
    _last_mqtt_aqualinkdata.battery = _aqualink_data->battery;
    send_mqtt_string_msg(nc, BATTERY_STATE, _aqualink_data->battery==OK?MQTT_ON:MQTT_OFF); 
  }

  if (_aqualink_data->ph != TEMP_UNKNOWN && _aqualink_data->ph != _last_mqtt_aqualinkdata.ph) {
    _last_mqtt_aqualinkdata.ph = _aqualink_data->ph;
    send_mqtt_float_msg(nc, CHEM_PH_TOPIC, _aqualink_data->ph);
    send_mqtt_float_msg(nc, CHRM_PH_F_TOPIC, roundf(degFtoC(_aqualink_data->ph)));
  }
  if (_aqualink_data->orp != TEMP_UNKNOWN && _aqualink_data->orp != _last_mqtt_aqualinkdata.orp) {
    _last_mqtt_aqualinkdata.orp = _aqualink_data->orp;
    send_mqtt_numeric_msg(nc, CHEM_ORP_TOPIC, _aqualink_data->orp);
    send_mqtt_float_msg(nc, CHRM_ORP_F_TOPIC, roundf(degFtoC(_aqualink_data->orp)));
  }
  
  if (_aqualink_data->ar_swg_status != _last_mqtt_aqualinkdata.ar_swg_status ||
      _aqualink_data->swg_percent != _last_mqtt_aqualinkdata.swg_percent) {  // Percent can also effect status
    char message[30];
    int status;
    int dzalert;

    get_swg_status_mqtt(_aqualink_data, message, &status, &dzalert);

    if (!_aqualink_data->simulate_panel)
      sprintf(_aqualink_data->last_display_message, message);

    send_domoticz_mqtt_status_message(nc, _aqconfig_.dzidx_swg_status, dzalert, &message[9]);
    send_mqtt_int_msg(nc, SWG_TOPIC, status);
    
    if (_aqualink_data->ar_swg_status == SWG_STATUS_OFF)
      send_mqtt_int_msg(nc, SWG_ENABELED_TOPIC, SWG_OFF);
    else
      send_mqtt_int_msg(nc, SWG_ENABELED_TOPIC, SWG_ON);

    send_mqtt_int_msg(nc, SWG_EXTENDED_TOPIC, (int)_aqualink_data->ar_swg_status);

    _last_mqtt_aqualinkdata.ar_swg_status = _aqualink_data->ar_swg_status;
  }

  if (_aqualink_data->boost != _last_mqtt_aqualinkdata.boost) {
    send_mqtt_int_msg(nc, SWG_BOOST_TOPIC, _aqualink_data->boost);
    _last_mqtt_aqualinkdata.boost = _aqualink_data->boost;
  }

    //if (_aqualink_data->ar_swg_status == SWG_STATUS_ON) { // If the SWG is actually on
  if (_aqualink_data->ar_swg_status != SWG_STATUS_OFF) { // If the SWG is actually on
    if (_aqualink_data->swg_percent != TEMP_UNKNOWN && (force_update || _aqualink_data->swg_percent != _last_mqtt_aqualinkdata.swg_percent)) {
      _last_mqtt_aqualinkdata.swg_percent = _aqualink_data->swg_percent;
      send_mqtt_numeric_msg(nc, SWG_PERCENT_TOPIC, _aqualink_data->swg_percent);
      send_mqtt_float_msg(nc, SWG_PERCENT_F_TOPIC, roundf(degFtoC(_aqualink_data->swg_percent)));
      send_mqtt_float_msg(nc, SWG_SETPOINT_TOPIC, roundf(degFtoC(_aqualink_data->swg_percent)));
      send_domoticz_mqtt_numeric_msg(nc, _aqconfig_.dzidx_swg_percent, _aqualink_data->swg_percent);
    }
    if (_aqualink_data->swg_ppm != TEMP_UNKNOWN && ( force_update || _aqualink_data->swg_ppm != _last_mqtt_aqualinkdata.swg_ppm)) {
      _last_mqtt_aqualinkdata.swg_ppm = _aqualink_data->swg_ppm;
      send_mqtt_numeric_msg(nc, SWG_PPM_TOPIC, _aqualink_data->swg_ppm);
      send_mqtt_float_msg(nc, SWG_PPM_F_TOPIC, roundf(degFtoC(_aqualink_data->swg_ppm)));
      send_domoticz_mqtt_numeric_msg(nc, _aqconfig_.dzidx_swg_ppm, _aqualink_data->swg_ppm);
    }
  }

//logMessage(LOG_INFO, "mqtt_broadcast_aqualinkstate: START LEDs\n");

//if (time(NULL) % 2) {}   <-- use to determin odd/even second in time to make state flash on enabled.

  // Loop over LED's and send any changes.
  for (i=0; i < _aqualink_data->total_buttons; i++) {
    /*
   if ( _aqualink_data->aqbuttons[i].led->state == FLASH && _aqconfig_.flash_mqtt_buttons == true ) {
      // Simply send on or off depending on if current second is odd or even.
      // will send too many off and on messages as we get hit multiple times a second, but most effecient way to handle this
      // considering flash is not very often and not for long.
      time_t     now;
      now = time(NULL);
      send_mqtt_state_msg(nc, _aqualink_data->aqbuttons[i].name, (now % 2)?OFF:ON);
      logMessage(LOG_DEBUG, "Flash button : %s %s\n",_aqualink_data->aqbuttons[i].name,(now % 2)?"off":"on");
    } else */if (_last_mqtt_aqualinkdata.aqualinkleds[i].state != _aqualink_data->aqbuttons[i].led->state) {
      _last_mqtt_aqualinkdata.aqualinkleds[i].state = _aqualink_data->aqbuttons[i].led->state;
      if (_aqualink_data->aqbuttons[i].code == KEY_POOL_HTR || _aqualink_data->aqbuttons[i].code == KEY_SPA_HTR) {
        send_mqtt_heater_state_msg(nc, _aqualink_data->aqbuttons[i].name, _aqualink_data->aqbuttons[i].led->state);
      } else {
        send_mqtt_state_msg(nc, _aqualink_data->aqbuttons[i].name, _aqualink_data->aqbuttons[i].led->state);
      }
        
      if (_aqualink_data->aqbuttons[i].dz_idx != DZ_NULL_IDX)
        send_domoticz_mqtt_state_msg(nc, _aqualink_data->aqbuttons[i].dz_idx, (_aqualink_data->aqbuttons[i].led->state==OFF?DZ_OFF:DZ_ON));
    }
  }

  // Loop over Pumps
  for (i=0; i < _aqualink_data->num_pumps; i++) {
    //_aqualink_data->pumps[i].rpm = TEMP_UNKNOWN;
    //_aqualink_data->pumps[i].gph = TEMP_UNKNOWN;
    //_aqualink_data->pumps[i].watts = TEMP_UNKNOWN;

    if (_aqualink_data->pumps[i].rpm != TEMP_UNKNOWN && _aqualink_data->pumps[i].rpm != _last_mqtt_aqualinkdata.pumps[i].rpm) {
      _last_mqtt_aqualinkdata.pumps[i].rpm = _aqualink_data->pumps[i].rpm;
      //send_mqtt_aux_msg(nc, PUMP_TOPIC, i+1, PUMP_RPM_TOPIC, _aqualink_data->pumps[i].rpm);
      send_mqtt_aux_msg(nc, _aqualink_data->pumps[i].button->name, PUMP_RPM_TOPIC, _aqualink_data->pumps[i].rpm);
    }
    if (_aqualink_data->pumps[i].gpm != TEMP_UNKNOWN && _aqualink_data->pumps[i].gpm != _last_mqtt_aqualinkdata.pumps[i].gpm) {
      _last_mqtt_aqualinkdata.pumps[i].gpm = _aqualink_data->pumps[i].gpm;
      //send_mqtt_aux_msg(nc, PUMP_TOPIC, i+1, PUMP_GPH_TOPIC, _aqualink_data->pumps[i].gph);
      send_mqtt_aux_msg(nc, _aqualink_data->pumps[i].button->name, PUMP_GPM_TOPIC, _aqualink_data->pumps[i].gpm);
    }
    if (_aqualink_data->pumps[i].watts != TEMP_UNKNOWN && _aqualink_data->pumps[i].watts != _last_mqtt_aqualinkdata.pumps[i].watts) {
      _last_mqtt_aqualinkdata.pumps[i].watts = _aqualink_data->pumps[i].watts;
      //send_mqtt_aux_msg(nc, PUMP_TOPIC, i+1, PUMP_WATTS_TOPIC, _aqualink_data->pumps[i].watts);
      send_mqtt_aux_msg(nc, _aqualink_data->pumps[i].button->name, PUMP_WATTS_TOPIC, _aqualink_data->pumps[i].watts);
    }
  }

/*
  // Loop over LED's and send any changes.
  for (i=0; i < TOTAL_BUTTONS; i++) {
    //logMessage(LOG_NOTICE, "%s = %d, last=%d", _aqualink_data->aqbuttons[i].name,  _aqualink_data->aqbuttons[i].led->state, _last_mqtt_aqualinkdata.aqualinkleds[i].state);
    //logMessage(LOG_INFO, "LED %d : new state %d | old state %d\n", i, _aqualink_data->aqbuttons[i].led->state, _last_mqtt_aqualinkdata.aqualinkleds[i].state);
    if (_last_mqtt_aqualinkdata.aqualinkleds[i].state != _aqualink_data->aqbuttons[i].led->state){
      _last_mqtt_aqualinkdata.aqualinkleds[i].state = _aqualink_data->aqbuttons[i].led->state;
      //if (_aqualink_data->aqbuttons[i].dz_idx != DZ_NULL_IDX) {
        if (_aqualink_data->aqbuttons[i].code == KEY_POOL_HTR || _aqualink_data->aqbuttons[i].code == KEY_SPA_HTR) {
          send_mqtt_heater_state_msg(nc, _aqualink_data->aqbuttons[i].name, _aqualink_data->aqbuttons[i].led->state);
        } else if (_aqualink_data->aqbuttons[i].led->state == FLASH && _aqconfig_.flash_mqtt_buttons == true) {
          // This messed up the origional LED state, which means we send the flash to WEB UI as well.
          time_t     now;
          now = time(NULL);
          if ( now != lastFlashTm ) {
            lastFlashTm = now;
            _aqualink_data->aqbuttons[i].led->state = lastFlash;
            _last_mqtt_aqualinkdata.aqualinkleds[i].state = !lastFlash;
            send_mqtt_state_msg(nc, _aqualink_data->aqbuttons[i].name, _last_mqtt_aqualinkdata.aqualinkleds[i].state);
            logMessage(LOG_DEBUG, "Flash button : %s\n",_aqualink_data->aqbuttons[i].name);
          }
        } else {
            send_mqtt_state_msg(nc, _aqualink_data->aqbuttons[i].name, _aqualink_data->aqbuttons[i].led->state);
        }
        
        if (_aqualink_data->aqbuttons[i].dz_idx != DZ_NULL_IDX)
          send_domoticz_mqtt_state_msg(nc, _aqualink_data->aqbuttons[i].dz_idx, (_aqualink_data->aqbuttons[i].led->state==OFF?DZ_OFF:DZ_ON));
      //}
      // Send mqtt
    }
  }*/
  //logMessage(LOG_INFO, "mqtt_broadcast_aqualinkstate: END\n");
}


// 

int getTempforMeteohub(char *buffer)
{
  int length = 0;
  
  if (_aqualink_data->air_temp != TEMP_UNKNOWN)
    length += sprintf(buffer+length, "t0 %d\n",(int)degFtoC(_aqualink_data->air_temp)*10);
  else
    length += sprintf(buffer+length, "t0 \n");
    
  if (_aqualink_data->pool_temp != TEMP_UNKNOWN)
    length += sprintf(buffer+length, "t1 %d\n",(int)degFtoC(_aqualink_data->pool_temp)*10);
  else
    length += sprintf(buffer+length, "t1 \n");
    
  return strlen(buffer);
}

void set_light_mode(char *value, int button) 
{
  int i;
  clight_detail *light = NULL;
#ifdef AQ_PDA
  if (_aqconfig_.pda_mode == true) {
    logMessage(LOG_ERR, "Light mode control not supported in PDA mode\n");
    return;
  }
#endif
  for (i=0; i < _aqualink_data->num_lights; i++) {
    if (&_aqualink_data->aqbuttons[button] == _aqualink_data->lights[i].button) {
      // Found the programmable light
      light = &_aqualink_data->lights[i];
      break;
    }
  }

  if (light == NULL) {
    logMessage(LOG_ERR, "Light mode control not configured for button %d\n",button);
    return;
  }

  char buf[LIGHT_MODE_BUFER];

  if (light->lightType == LC_PROGRAMABLE ) {
  // 5 below is light index, need to look this up so it's not hard coded.
    sprintf(buf, "%-5s%-5d%-5d%-5d%.2f",value, 
                                      button, 
                                      _aqconfig_.light_programming_initial_on,
                                      _aqconfig_.light_programming_initial_off,
                                      _aqconfig_.light_programming_mode );
  //logMessage(LOG_NOTICE, "WEB: requset light mode %s\n", buf);
    aq_programmer(AQ_SET_LIGHTPROGRAM_MODE, buf, _aqualink_data);
  } else {
    sprintf(buf, "%-5s%-5d%-5d",value, button, light->lightType);
    aq_programmer(AQ_SET_LIGHTCOLOR_MODE, buf, _aqualink_data);
  }
}



void action_web_request(struct mg_connection *nc, struct http_message *http_msg) {
  // struct http_message *http_msg = (struct http_message *)ev_data;
  if (getLogLevel() >= LOG_INFO) { // Simply for log message, check we are at
                                   // this log level before running all this
                                   // junk
    char *uri = (char *)malloc(http_msg->uri.len + http_msg->query_string.len + 2);
    strncpy(uri, http_msg->uri.p, http_msg->uri.len + http_msg->query_string.len + 1);
    uri[http_msg->uri.len + http_msg->query_string.len + 1] = '\0';
    logMessage(LOG_INFO, "URI request: '%s'\n", uri);
    free(uri);
  }
  // If we have a get request, pass it
  if (strstr(http_msg->method.p, "GET") && http_msg->query_string.len > 0) {
    char command[20];

    mg_get_http_var(&http_msg->query_string, "command", command, sizeof(command));
    logMessage(LOG_INFO, "WEB: Message command='%s'\n", command);

    if (strcmp(command, "dynamic_config") == 0) {
      char data[JSON_BUFFER_SIZE];
      int size = build_color_lights_js(_aqualink_data, data, JSON_BUFFER_SIZE);
      mg_send_head(nc, 200, size, "Content-Type: text/javascript");
      mg_send(nc, data, size);
    } else
    // if (strstr(http_msg->query_string.p, "command=status")) {
    if (strcmp(command, "status") == 0) {
      char data[JSON_STATUS_SIZE];
      int size = build_aqualink_status_JSON(_aqualink_data, data, JSON_STATUS_SIZE);
      mg_send_head(nc, 200, size, "Content-Type: application/json");
      mg_send(nc, data, size);

      //} else if (strstr(http_msg->query_string.p, "command=mhstatus")) {
    } else if (strcmp(command, "mhstatus") == 0) {
      char data[20];
      int size = getTempforMeteohub(data);
      mg_send_head(nc, 200, size, "Content-Type: text/plain");
      mg_send(nc, data, size);
    } else if (strcmp(command, "poollightmode") == 0) {
logMessage(LOG_ERR, "WEB: poollightmode taken out for update (forgot to put it back)\n");
/*
      char value[20];
      mg_get_http_var(&http_msg->query_string, "value", value, sizeof(value));
      //aq_programmer(AQ_SET_COLORMODE, value, _aqualink_data);
      set_light_mode(value, _aqconfig_.light_programming_button_pool);
      mg_send_head(nc, 200, strlen(GET_RTN_OK), "Content-Type: text/plain");
      mg_send(nc, GET_RTN_OK, strlen(GET_RTN_OK));
*/
    } else if (strcmp(command, "spalightmode") == 0) {
logMessage(LOG_ERR, "WEB: spalightmode taken out for update (forgot to put it back)\n");
/*
      char value[20];
      mg_get_http_var(&http_msg->query_string, "value", value, sizeof(value));
      //aq_programmer(AQ_SET_COLORMODE, value, _aqualink_data);
      set_light_mode(value, _aqconfig_.light_programming_button_spa);
      mg_send_head(nc, 200, strlen(GET_RTN_OK), "Content-Type: text/plain");
      mg_send(nc, GET_RTN_OK, strlen(GET_RTN_OK));
*/
    } else if (strcmp(command, "diag") == 0) {
      aq_programmer(AQ_GET_DIAGNOSTICS_MODEL, NULL, _aqualink_data);
      mg_send_head(nc, 200, strlen(GET_RTN_OK), "Content-Type: text/plain");
      mg_send(nc, GET_RTN_OK, strlen(GET_RTN_OK));
    } else if (strcmp(command, "swg_percent") == 0) {
      char value[20];
      mg_get_http_var(&http_msg->query_string, "value", value, sizeof(value));
      aq_programmer(AQ_SET_SWG_PERCENT, value, _aqualink_data);
      logMessage(LOG_INFO, "Web: request to set SWG to %s\n", value);
      mg_send_head(nc, 200, strlen(GET_RTN_OK), "Content-Type: text/plain");
      mg_send(nc, GET_RTN_OK, strlen(GET_RTN_OK));
    } else if (strcmp(command, "pool_htr_set_pnt") == 0) {
      char value[20];
      mg_get_http_var(&http_msg->query_string, "value", value, sizeof(value));
      aq_programmer(AQ_SET_POOL_HEATER_TEMP, value, _aqualink_data);
      logMessage(LOG_INFO, "Web: request to set Pool heater to %s\n", value);
      mg_send_head(nc, 200, strlen(GET_RTN_OK), "Content-Type: text/plain");
      mg_send(nc, GET_RTN_OK, strlen(GET_RTN_OK));
    } else if (strcmp(command, "spa_htr_set_pnt") == 0) {
      char value[20];
      mg_get_http_var(&http_msg->query_string, "value", value, sizeof(value));
      aq_programmer(AQ_SET_SPA_HEATER_TEMP, value, _aqualink_data);
      logMessage(LOG_INFO, "Web: request to set Spa heater to %s\n", value);
      mg_send_head(nc, 200, strlen(GET_RTN_OK), "Content-Type: text/plain");
      mg_send(nc, GET_RTN_OK, strlen(GET_RTN_OK));
    } else if (strcmp(command, "frz_protect_set_pnt") == 0) {
      char value[20];
      mg_get_http_var(&http_msg->query_string, "value", value, sizeof(value));
      aq_programmer(AQ_SET_FRZ_PROTECTION_TEMP, value, _aqualink_data);
      logMessage(LOG_INFO, "Web: request to set Freeze protect to %s\n", value);
      mg_send_head(nc, 200, strlen(GET_RTN_OK), "Content-Type: text/plain");
      mg_send(nc, GET_RTN_OK, strlen(GET_RTN_OK));
    } else if (strcmp(command, "extended_device_prg") == 0) {
      char value[20];
      char message[JSON_LABEL_SIZE];
      mg_get_http_var(&http_msg->query_string, "value", value, sizeof(value));
      bool prg = request2bool(value);
      if (prg && !onetouch_enabled())
        prg = false; // Ignore request if onetouch is not enabeled
      set_extended_device_id_programming(prg);
      sprintf(message,"{\"extended_device_prg\":\"%s\"}", bool2text(prg));
      mg_send_head(nc, 200, strlen(message), "Content-Type: application/json");
      mg_send(nc, message, strlen(message));
    } else if (strcmp(command, "devices") == 0) {
      char message[JSON_BUFFER_SIZE];
      int size = build_device_JSON(_aqualink_data, message, JSON_BUFFER_SIZE, false);
      mg_send_head(nc, 200, size, "Content-Type: application/json");
      mg_send(nc, message, size);
    } else if (strcmp(command, "homebridge") == 0) {
      char message[JSON_BUFFER_SIZE];
      int size = build_device_JSON(_aqualink_data, message, JSON_BUFFER_SIZE, true);
      mg_send_head(nc, 200, size, "Content-Type: application/json");
      mg_send(nc, message, size);
    } else if (strcmp(command, "setconfigprm") == 0) {
      char value[20];
      char param[30];
      char *webrtn;
      mg_get_http_var(&http_msg->query_string, "param", param, sizeof(param));
      mg_get_http_var(&http_msg->query_string, "value", value, sizeof(value));
      
      if (setConfigValue(_aqualink_data, param, value)) {
        webrtn = GET_RTN_OK;
        logMessage(LOG_INFO, "Web: request to config %s to %s\n", param, value);
      } else {
        webrtn = GET_RTN_ERROR;
        logMessage(LOG_ERR, "Web: request to config %s to %s failed\n", param, value);
      }
      mg_send_head(nc, 200, strlen(webrtn), "Content-Type: text/plain");
      mg_send(nc, webrtn, strlen(webrtn));
    } else if (strcmp(command, "writeconfig") == 0) {
      if (writeCfg (_aqualink_data)) {
        mg_send_head(nc, 200, strlen(GET_RTN_OK), "Content-Type: text/plain");
        mg_send(nc, GET_RTN_OK, strlen(GET_RTN_OK));
      } else {
        mg_send_head(nc, 200, strlen(GET_RTN_ERROR), "Content-Type: text/plain");
        mg_send(nc, GET_RTN_ERROR, strlen(GET_RTN_ERROR));
      }
    } else if (strcmp(command, "debug") == 0) {
      char value[80];
      char *rtn;
      mg_get_http_var(&http_msg->query_string, "value", value, sizeof(value));
      if (strcmp(value, "start") == 0) {
        startInlineDebug();
        rtn = GET_RTN_OK;
        logMessage(LOG_DEBUG, "WEB: Started inline debug mode\n");
      } else if (strcmp(value, "stop") == 0) {
        logMessage(LOG_DEBUG, "WEB: Stoped inline debug mode\n");
        stopInlineDebug();
        rtn = GET_RTN_OK;
      } else if (strcmp(value, "serialstart") == 0) {
        startInlineSerialDebug();
        logMessage(LOG_DEBUG, "WEB: Started inline debug mode\n");
        rtn = GET_RTN_OK;
      } else if (strcmp(value, "serialstop") == 0) {
        logMessage(LOG_DEBUG, "WEB: Stoped inline debug mode\n");
        stopInlineDebug();
        rtn = GET_RTN_OK;
      } else if (strcmp(value, "status") == 0) {
        snprintf(value,80,"{\"sLevel\":\"%s\", \"iLevel\":%d, \"logReady\":\"%s\"}\n",elevel2text(getLogLevel()),getLogLevel(),islogFileReady()?"true":"false" );
        mg_send_head(nc, 200, strlen(value), "Content-Type: text/json");
        mg_send(nc, value, strlen(value));
        return;
        //rtn = value;
      } else if (strcmp(value, "clean") == 0) {
        cleanInlineDebug();
        rtn = GET_RTN_OK;
      } else if (strcmp(value, "download") == 0) {
        mg_http_serve_file(nc, http_msg, getInlineLogFName(), mg_mk_str("text/plain"), mg_mk_str(""));
        return;
      } else {
        rtn = GET_RTN_UNKNOWN;
      }
      mg_send_head(nc, 200, strlen(rtn), "Content-Type: text/plain");
      mg_send(nc, rtn, strlen(rtn));
    } else if (strncmp(command, "Pump_", 5) == 0) {
    // Set Pump RPM
      bool found = false;
      int pumpIndex = atoi(command+5); // Check for 0
      char value[10];
      mg_get_http_var(&http_msg->query_string, "value", value, sizeof(value));
      int rpm = atoi(value);
    // Check for pumpIndex = 0 (BAD) and check RPM Value
    //printf("******** ADD CHECK FOR PUMP & RPM HERE ********\n");
      int pi;
      for (pi=0; pi < _aqualink_data->num_pumps; pi++) {
        if (_aqualink_data->pumps[pi].pumpIndex == pumpIndex) {
          logMessage(LOG_NOTICE, "WEB: request to change pump %d to %d\n",pumpIndex, round(rpm));
          _aqualink_data->unactioned.type = PUMP_RPM;
          _aqualink_data->unactioned.value = round(rpm);
          _aqualink_data->unactioned.id = pumpIndex;
          found=true;
          break;
        }
      }
      if(!found)
        logMessage(LOG_ERR, "WEB: Didn't find pump %d from command %s\n",pumpIndex,command);
    } else {
      int i;
      for (i = 0; i < _aqualink_data->total_buttons; i++) {
        if (strcmp(command, _aqualink_data->aqbuttons[i].name) == 0) {
          char value[20];
          char *rtn;
          mg_get_http_var(&http_msg->query_string, "value", value, sizeof(value));
          // logMessage (LOG_INFO, "Web Message command='%s'\n",command);
          // aq_programmer(AQ_SEND_CMD, (char
          // *)&_aqualink_data->aqbuttons[i].code, _aqualink_data);
          logMessage(LOG_DEBUG, "WEB: Message request '%s' change state to '%s'\n", command, value);
#ifdef AQ_PDA          
          if (_aqconfig_.pda_mode == true) {
            char msg[PTHREAD_ARG];
            sprintf(msg, "%-5d%-5d",i, (strcmp(value, "on") == 0)?ON:OFF);
            //printf("******* '%s' ********\n",msg);
            aq_programmer(AQ_PDA_DEVICE_ON_OFF, msg, _aqualink_data);
            rtn = GET_RTN_OK;
          }
          else
#endif 
          if (strcmp(value, "on") == 0) {
            if (_aqualink_data->aqbuttons[i].led->state == OFF || _aqualink_data->aqbuttons[i].led->state == FLASH) {
              //aq_programmer(AQ_SEND_CMD, (char *)&_aqualink_data->aqbuttons[i].code, _aqualink_data);
              aq_send_cmd(_aqualink_data->aqbuttons[i].code);
              rtn = GET_RTN_OK;
              logMessage(LOG_INFO, "WEB: turn ON '%s' changed state to '%s'\n", command, value);
            } else {
              rtn = GET_RTN_NOT_CHANGED;
              logMessage(LOG_INFO, "WEB: '%s' is already on '%s', current state %d\n", command, value, _aqualink_data->aqbuttons[i].led->state);
            }
          } else if (strcmp(value, "off") == 0) {
            if (_aqualink_data->aqbuttons[i].led->state == ON || 
                _aqualink_data->aqbuttons[i].led->state == ENABLE ||
                _aqualink_data->aqbuttons[i].led->state == FLASH) {
              //aq_programmer(AQ_SEND_CMD, (char *)&_aqualink_data->aqbuttons[i].code, _aqualink_data);
              aq_send_cmd(_aqualink_data->aqbuttons[i].code);
              rtn = GET_RTN_OK;
              logMessage(LOG_INFO, "WEB: turn Off '%s' changed state to '%s'\n", command, value);
            } else {
              rtn = GET_RTN_NOT_CHANGED;
              logMessage(LOG_INFO, "WEB: '%s' is already off '%s', current state %d\n", command, value, _aqualink_data->aqbuttons[i].led->state);
            }
          } else { // Blind switch
            //aq_programmer(AQ_SEND_CMD, (char *)&_aqualink_data->aqbuttons[i].code, _aqualink_data);
            aq_send_cmd(_aqualink_data->aqbuttons[i].code);
            rtn = GET_RTN_OK;
            logMessage(LOG_INFO, "WEB: '%s' blindly changed state\n", command, value);
          }

          logMessage(LOG_DEBUG, "WEB: On=%d, Off=%d, Enable=%d, Flash=%d\n", ON, OFF, ENABLE, FLASH);
          // NSF change OK and 2 below to a constant
          mg_send_head(nc, 200, strlen(rtn), "Content-Type: text/plain");
          mg_send(nc, rtn, strlen(rtn));

          // NSF place check we found command here
        }
      }
    }
    // If we get here, got a bad query
    mg_send_head(nc, 200, strlen(GET_RTN_UNKNOWN), "Content-Type: text/plain");
    mg_send(nc, GET_RTN_UNKNOWN, strlen(GET_RTN_UNKNOWN));

  } else {
    struct mg_serve_http_opts opts;
    memset(&opts, 0, sizeof(opts)); // Reset all options to defaults
    opts.document_root = _web_root; // Serve files from the current directory
    // logMessage (LOG_DEBUG, "Doc root=%s\n",opts.document_root);
    mg_serve_http(nc, http_msg, s_http_server_opts);
  }

}

void action_websocket_request(struct mg_connection *nc, struct websocket_message *wm) {
  char buffer[50];
  struct JSONwebrequest request;
  
#ifdef AQ_PDA
  // Any websocket request means UI is active, so don't let AqualinkD go to sleep if in PDA mode
  if (pda_mode())
    pda_reset_sleep();
#endif

  strncpy(buffer, (char *)wm->data, wm->size);
  buffer[wm->size] = '\0';
  // logMessage (LOG_DEBUG, "buffer '%s'\n", buffer);

  parseJSONwebrequest(buffer, &request);
  logMessage(LOG_INFO, "WS: Message - Key '%s' Value '%s' | Key2 '%s' Value2 '%s'\n", request.first.key, request.first.value, request.second.key,
             request.second.value);
/*
  if (strcmp(request.first.key, "raw") == 0 || strcmp(request.first.key, "simulator") == 0 )
    _aqualink_data->simulate_panel = true;
  else
    _aqualink_data->simulate_panel = false;
*/

  if (strcmp(request.first.key, "raw") == 0) {
    _aqualink_data->simulate_panel = true;
    logMessage (LOG_NOTICE, "WS: Send raw command to controller %s\n",request.first.value);
    unsigned int n;
    sscanf(request.first.value, "0x%2x", &n);
    //aq_programmer(AQ_SEND_CMD, (char *)&n, NULL);
    aq_send_cmd((unsigned char)n);
    //char message[JSON_LABEL_SIZE*10];
    //build_device_JSON(_aqualink_data, _aqconfig_.light_programming_button, message, JSON_LABEL_SIZE*10);
    //ws_send(nc, message);
  } else if (strcmp(request.first.key, "mode") == 0) {
    if (strcmp(request.first.value, "onetouchraw") == 0) {
      set_websocket_RSraw(nc);
    }
  } else if (strcmp(request.first.key, "command") == 0) {
    _aqualink_data->simulate_panel = false;
    if (strcmp(request.first.value, "GET_AUX_LABELS") == 0) {
      char labels[JSON_LABEL_SIZE];
      build_aux_labels_JSON(_aqualink_data, labels, JSON_LABEL_SIZE);
      ws_send(nc, labels);
    } else if (strcmp(request.first.value, "GET_DEVICES") == 0) {
      char message[JSON_BUFFER_SIZE];
      build_device_JSON(_aqualink_data, message, JSON_BUFFER_SIZE, false);
      ws_send(nc, message);
    } else if ( strcmp(request.first.value, "simulator") == 0) {
      _aqualink_data->simulate_panel = true;
      logMessage(LOG_INFO, "WS: Set Simulator mode");
      char labels[JSON_LABEL_SIZE];
      build_aux_labels_JSON(_aqualink_data, labels, JSON_LABEL_SIZE);
      ws_send(nc, labels);
    } else if ( strcmp(request.first.value, SWG_BOOST_TOPIC) == 0) {
      //logMessage(LOG_INFO, "Boost ");
      if (request.second.value != NULL)
        _aqualink_data->unactioned.value = request2bool(request.second.value);
      else
        _aqualink_data->unactioned.value = !_aqualink_data->boost;
        
      _aqualink_data->unactioned.type = SWG_BOOST;
    } else { // Search for value in command list
      int i;
      for (i = 0; i < _aqualink_data->total_buttons; i++) {
        if (strcmp(request.first.value, _aqualink_data->aqbuttons[i].name) == 0) {
          logMessage (LOG_INFO, "WS: button '%s' pressed\n",_aqualink_data->aqbuttons[i].name);
#ifdef AQ_PDA
          if (_aqconfig_.pda_mode) {
            char msg[PTHREAD_ARG];
            sprintf(msg, "%-5d%-5d",i, (_aqualink_data->aqbuttons[i].led->state!=OFF)?OFF:ON);
            aq_programmer(AQ_PDA_DEVICE_ON_OFF, msg, _aqualink_data);
          } else
#endif
          {
            logMessage (LOG_DEBUG, "WS: request 0x%02hhx to be sent to controller\n",_aqualink_data->aqbuttons[i].code);
            aq_send_cmd(_aqualink_data->aqbuttons[i].code);
          }
          break;
          // NSF place check we found command here
        }
      }
    }
  } else if (strcmp(request.first.key, "parameter") == 0) {
    _aqualink_data->simulate_panel = false;
    if (strcmp(request.first.value, "FRZ_PROTECT") == 0 || strcmp(request.first.value, FREEZE_PROTECT) == 0) {
      aq_programmer(AQ_SET_FRZ_PROTECTION_TEMP, request.second.value, _aqualink_data);
    } else if (strcmp(request.first.value, "POOL_HTR") == 0 || strcmp(request.first.value, BTN_POOL_HTR) == 0 ) {
      aq_programmer(AQ_SET_POOL_HEATER_TEMP, request.second.value, _aqualink_data);
    } else if (strcmp(request.first.value, "SPA_HTR") == 0 || strcmp(request.first.value, BTN_SPA_HTR) == 0) {
      aq_programmer(AQ_SET_SPA_HEATER_TEMP, request.second.value, _aqualink_data);
    } else if (strcmp(request.first.value, SWG_TOPIC) == 0) {
      //aq_programmer(AQ_SET_SWG_PERCENT, request.second.value, _aqualink_data);
      int value = setpoint_check(SWG_SETPOINT, atoi(request.second.value), _aqualink_data);
      
      if (_aqualink_data->ar_swg_status == SWG_STATUS_OFF ) {
         // SWG is off, can't set %, so delay the set until it's on.
        _aqualink_data->swg_delayed_percent = value;
      } else {
        aq_programmer(AQ_SET_SWG_PERCENT, request.second.value, _aqualink_data);
        _aqualink_data->swg_percent = value; // Set the value as if it's already been set, just incase it's 0 as we won't get that message, or will update next time
      }
      
    //} else if (strcmp(request.first.value, "POOL_LIGHT_MODE") == 0) { 
    //  set_light_mode(request.second.value, _aqconfig_.light_programming_button_pool);
    } else if (strcmp(request.first.value, "LIGHT_MODE") == 0) {
      // second is mode & third is button_id
      int i;
      for (i = 0; i < _aqualink_data->total_buttons; i++) {
        // NSF I could pull the text here for the real light color name. 4th value in json
        if (strcmp(request.third.value, _aqualink_data->aqbuttons[i].name) == 0) {
          set_light_mode(request.second.value, i);
          break;
        }
      }
      //set_light_mode(request.second.value, 0);
    } else {
      int i;
      bool found = false;
      for (i = 0; i < _aqualink_data->total_buttons; i++) {
        if (strcmp(request.first.value, _aqualink_data->aqbuttons[i].name) == 0) {
          int pi;
          logMessage (LOG_INFO, "WS: button parameter request '%s' '%s'\n",_aqualink_data->aqbuttons[i].name, request.second.value);
          for (pi=0; pi < _aqualink_data->num_pumps; pi++) {
            if (_aqualink_data->pumps[pi].button == &_aqualink_data->aqbuttons[i]) {
              logMessage(LOG_NOTICE, "WS: request to change pump %d %s to %s\n",pi, _aqualink_data->aqbuttons[i].name, request.second.value);
              _aqualink_data->unactioned.type = PUMP_RPM;
              _aqualink_data->unactioned.value = atoi(request.second.value);
              _aqualink_data->unactioned.id = _aqualink_data->pumps[pi].pumpIndex;
              found=true;
              break;
            }
          }
        }
      }
      if (!found)
        logMessage(LOG_DEBUG, "WS: Unknown parameter %s\n", request.first.value);
    }
  } 
}

void action_mqtt_message(struct mg_connection *nc, struct mg_mqtt_message *msg) {
  unsigned int i;
  //logMessage(LOG_DEBUG, "MQTT: topic %.*s %.2f\n",msg->topic.len, msg->topic.p, atof(msg->payload.p));
  // If message doesn't end in set we don't care about it.
  if (strncmp(&msg->topic.p[msg->topic.len -4], "/set", 4) != 0) {
    logMessage(LOG_DEBUG, "MQTT: Ignore %.*s %.*s\n",msg->topic.len, msg->topic.p, msg->payload.len, msg->payload.p);
    return;
  }
  logMessage(LOG_DEBUG, "MQTT: topic %.*s %.*s\n",msg->topic.len, msg->topic.p, msg->payload.len, msg->payload.p);

  //Need to do this in a better manor, but for present it's ok.
  static char tmp[20];
  strncpy(tmp, msg->payload.p, msg->payload.len);
  tmp[msg->payload.len] = '\0';

  float value = atof(tmp);
  //logMessage(LOG_DEBUG, "MQTT: topic converted %.*s %s %.2f\n",msg->topic.len, msg->topic.p, tmp, value);

//printf("Topic %.*s\n",msg->topic.len, msg->topic.p);
  // get the parts from the topic
  char *pt1 = (char *)&msg->topic.p[strlen(_aqconfig_.mqtt_aq_topic)+1];
  char *pt2 = NULL;
  char *pt3 = NULL;

  //for (i=10; i < msg->topic.len; i++) {
  for (i=strlen(_aqconfig_.mqtt_aq_topic)+1; i < msg->topic.len; i++) {
    if ( msg->topic.p[i] == '/' ) {
      if (pt2 == NULL) {
        pt2 = (char *)&msg->topic.p[++i];
      } else if (pt3 == NULL) {
        pt3 = (char *)&msg->topic.p[++i];
        break;
      }
    }
  }

  //logMessage(LOG_DEBUG, "MQTT: topic %.*s %.2f\n",msg->topic.len, msg->topic.p, atof(msg->payload.p));
  //only care about topics with set at the end.
  //aqualinkd/Freeze/setpoint/set
  //aqualinkd/Filter_Pump/set
  //aqualinkd/Pool_Heater/setpoint/set
  //aqualinkd/Pool_Heater/set
  //aqualinkd/SWG/Percent_f/set

  //aqualinkd/Filter_Pump/RPM/set // Should add this at some point
  //aqualinkd/Pump_1/RPM/set

  if (pt3 != NULL && (strncmp(pt2, "setpoint", 8) == 0) && (strncmp(pt3, "set", 3) == 0)) {
    int val = _aqualink_data->unactioned.value = (_aqualink_data->temp_units != CELSIUS && _aqconfig_.convert_mqtt_temp) ? round(degCtoF(value)) : round(value);
    if (strncmp(pt1, BTN_POOL_HTR, strlen(BTN_POOL_HTR)) == 0) {
      _aqualink_data->unactioned.value = setpoint_check(POOL_HTR_SETOINT, val, _aqualink_data);
      _aqualink_data->unactioned.type = POOL_HTR_SETOINT;
    } else if (strncmp(pt1, BTN_SPA_HTR, strlen(BTN_SPA_HTR)) == 0) {
      _aqualink_data->unactioned.value = setpoint_check(SPA_HTR_SETOINT, val, _aqualink_data);
      _aqualink_data->unactioned.type = SPA_HTR_SETOINT;
    } else if (strncmp(pt1, FREEZE_PROTECT, strlen(FREEZE_PROTECT)) == 0) {
      _aqualink_data->unactioned.value = setpoint_check(FREEZE_SETPOINT, val, _aqualink_data);
      _aqualink_data->unactioned.type = FREEZE_SETPOINT;
    } else if (strncmp(pt1, "SWG", 3) == 0) {  // If we get SWG percent as setpoint message.
      int val = round(degCtoF(value));
      _aqualink_data->unactioned.value = setpoint_check(SWG_SETPOINT, val, _aqualink_data);
      _aqualink_data->unactioned.type = SWG_SETPOINT;
    } else {
      // Not sure what the setpoint is, ignore.
      logMessage(LOG_DEBUG, "MQTT: ignoring %.*s don't recognise button setpoint\n", msg->topic.len, msg->topic.p);
      return;
    }
    // logMessage(LOG_INFO, "MQTT: topic %.*s %.2f, setting %s\n",msg->topic.len, msg->topic.p, value);
    time(&_aqualink_data->unactioned.requested);
  //} else if ((pt3 != NULL && (strncmp(pt1, "SWG", 3) == 0) && (strncmp(pt2, "Percent_f", 8) == 0) && (strncmp(pt3, "set", 3) == 0))) {
  } else if ((pt3 != NULL && (strncmp(pt1, "SWG", 3) == 0) && (strncmp(pt2, "Percent", 7) == 0) && (strncmp(pt3, "set", 3) == 0))) {
      int val;
      if ( (strncmp(pt2, "Percent_f", 9) == 0)  ) {
        val = _aqualink_data->unactioned.value = round(degCtoF(value));
      } else {
        val = _aqualink_data->unactioned.value = round(value);
      }
      _aqualink_data->unactioned.value = setpoint_check(SWG_SETPOINT, val, _aqualink_data);
      _aqualink_data->unactioned.type = SWG_SETPOINT;
  } else if ((pt3 != NULL && (strncmp(pt1, "SWG", 3) == 0) && (strncmp(pt2, "Boost", 5) == 0) && (strncmp(pt3, "set", 3) == 0))) {
    _aqualink_data->unactioned.value = round(value);
    _aqualink_data->unactioned.type = SWG_BOOST;
  //} else if ((pt3 != NULL && (strncmp(pt1, "Pump_", 5) == 0) && ((strncmp(pt2, "RPM", 3) == 0) || (strncmp(pt2, "GPM", 3) == 0)) && (strncmp(pt3, "set", 3) == 0))) {
  } else if ((pt3 != NULL && ((strncmp(pt2, "RPM", 3) == 0) || (strncmp(pt2, "GPM", 3) == 0)) && (strncmp(pt3, "set", 3) == 0))) {
    bool found = false;
    //int pi;
    if (strncmp(pt1, "Pump_", 5) == 0) { // Pump by number
      int pumpIndex = atoi(pt1+5); // Check for 0
      
      for (i=0; i < _aqualink_data->num_pumps; i++) {
        if (_aqualink_data->pumps[i].pumpIndex == pumpIndex) {
          logMessage(LOG_NOTICE, "MQTT: request to change pump %d %s to %d\n",pumpIndex, (strncmp(pt2, "RPM", 3) == 0)?"RPM":"GPM", round(value));
          _aqualink_data->unactioned.type = PUMP_RPM;
          _aqualink_data->unactioned.value = round(value);
          _aqualink_data->unactioned.id = pumpIndex;
          found=true;
          break;
        }
      }
    } else { // Pump by button name
      for (i=0; i < _aqualink_data->total_buttons ; i++) {
        if (strncmp(pt1, _aqualink_data->aqbuttons[i].name, strlen(_aqualink_data->aqbuttons[i].name)) == 0 ){
          int pi;
          for (pi=0; pi < _aqualink_data->num_pumps; pi++) {
            if (_aqualink_data->pumps[pi].button == &_aqualink_data->aqbuttons[i]) {
              logMessage(LOG_NOTICE, "MQTT: request to change pump %d %s to %d\n",pi, (strncmp(pt2, "RPM", 3) == 0)?"RPM":"GPM", round(value));
              _aqualink_data->unactioned.type = PUMP_RPM;
              _aqualink_data->unactioned.value = round(value);
              _aqualink_data->unactioned.id = _aqualink_data->pumps[pi].pumpIndex;
              found=true;
              break;
            }
          }
        }
      }
    }
    if(!found)
      logMessage(LOG_ERR, "MQTT: Didn't find pump from message %.*s\n",msg->topic.len, msg->topic.p);

  } else if (pt2 != NULL && (strncmp(pt2, "set", 3) == 0) && (strncmp(pt2, "setpoint", 8) != 0)) {
    // Must be a switch on / off
    for (i=0; i < _aqualink_data->total_buttons; i++) {
      if (strncmp(pt1, _aqualink_data->aqbuttons[i].name, strlen(_aqualink_data->aqbuttons[i].name)) == 0 ){
        logMessage(LOG_INFO, "MQTT: MATCH %s to topic %.*s\n",_aqualink_data->aqbuttons[i].name,msg->topic.len, msg->topic.p);
        // Message is either a 1 or 0 for on or off
        //int status = atoi(msg->payload.p);
        if ( value > 1 || value < 0) {
          logMessage(LOG_ERR, "MQTT: topic %.*s %.2f\n",msg->topic.len, msg->topic.p, value);
          logMessage(LOG_ERR, "MQTT: received unknown status of '%.*s' for '%s', Ignoring!\n", msg->payload.len, msg->payload.p, _aqualink_data->aqbuttons[i].name);
        }
        else if ( (_aqualink_data->aqbuttons[i].led->state == OFF && value==0) ||
           (value == 1 && (_aqualink_data->aqbuttons[i].led->state == ON || 
                                _aqualink_data->aqbuttons[i].led->state == FLASH || 
                                _aqualink_data->aqbuttons[i].led->state == ENABLE))) {
          logMessage(LOG_INFO, "MQTT: received '%s' for '%s', already '%s', Ignoring\n", (value==0?"OFF":"ON"), _aqualink_data->aqbuttons[i].name, (value==0?"OFF":"ON"));
        } else {
          logMessage(LOG_INFO, "MQTT: received '%s' for '%s', turning '%s'\n", (value==0?"OFF":"ON"), _aqualink_data->aqbuttons[i].name,(value==0?"OFF":"ON"));
          //aq_programmer(AQ_SEND_CMD, (char *)&_aqualink_data->aqbuttons[i].code, _aqualink_data);
#ifdef AQ_PDA
          if (_aqconfig_.pda_mode) {
            char msg[PTHREAD_ARG];
            sprintf(msg, "%-5d%-5d",i, (value==0?OFF:ON) );
            aq_programmer(AQ_PDA_DEVICE_ON_OFF, msg, _aqualink_data);
          } else
#endif
          {
            aq_send_cmd(_aqualink_data->aqbuttons[i].code);
          }
          // Some circumstances these are ignored by control panel, so furce the MQTT state to be sent again on next poll just ncase it doesn't change
          // This causes a good MQTT button to flash on/off once before getting the latest update.  Need to find a better way
          //_last_mqtt_aqualinkdata.aqualinkleds[i].state = LED_S_UNKNOWN;
          // NSF   Update above to cater for ignored buttons.
        }
        break;
      }
    }
  } else {
    // We don's care
    logMessage(LOG_DEBUG, "MQTT: ignoring topic %.*s\n",msg->topic.len, msg->topic.p);
  }
}

void action_domoticz_mqtt_message(struct mg_connection *nc, struct mg_mqtt_message *msg) {
  int idx = -1;
  int nvalue = -1;
  int i;
  char svalue[DZ_SVALUE_LEN+1];

  if (parseJSONmqttrequest(msg->payload.p, msg->payload.len, &idx, &nvalue, svalue)) {
    logMessage(LOG_DEBUG, "MQTT: DZ: Received message IDX=%d nValue=%d sValue=%s\n", idx, nvalue, svalue);
    for (i=0; i < _aqualink_data->total_buttons; i++) {
      if (_aqualink_data->aqbuttons[i].dz_idx == idx){
        //NSF, should try to simplify this if statment, but not easy since AQ ON and DZ ON are different, and AQ has other states.
        if ( (_aqualink_data->aqbuttons[i].led->state == OFF && nvalue==DZ_OFF) ||
           (nvalue == DZ_ON && (_aqualink_data->aqbuttons[i].led->state == ON || 
                                _aqualink_data->aqbuttons[i].led->state == FLASH || 
                                _aqualink_data->aqbuttons[i].led->state == ENABLE))) {
          logMessage(LOG_INFO, "MQTT: DZ: received '%s' for '%s', already '%s', Ignoring\n", (nvalue==DZ_OFF?"OFF":"ON"), _aqualink_data->aqbuttons[i].name, (nvalue==DZ_OFF?"OFF":"ON"));
        } else {
          // NSF Below if needs to check that the button pressed is actually a light. Add this later
          if (_aqualink_data->active_thread.ptype == AQ_SET_LIGHTPROGRAM_MODE ) {
            logMessage(LOG_NOTICE, "MQTT: DZ: received '%s' for '%s', IGNORING as we are programming light mode\n", (nvalue==DZ_OFF?"OFF":"ON"), _aqualink_data->aqbuttons[i].name);
          } else {
            logMessage(LOG_INFO, "MQTT: DZ: received '%s' for '%s', turning '%s'\n", (nvalue==DZ_OFF?"OFF":"ON"), _aqualink_data->aqbuttons[i].name,(nvalue==DZ_OFF?"OFF":"ON"));
#ifdef AQ_PDA
            if (_aqconfig_.pda_mode) {
              char msg[PTHREAD_ARG];
              sprintf(msg, "%-5d%-5d",i, (nvalue == DZ_OFF?OFF:ON) );
              aq_programmer(AQ_PDA_DEVICE_ON_OFF, msg, _aqualink_data);
            } else
#endif
            {
              aq_send_cmd(_aqualink_data->aqbuttons[i].code);
            }
          }
        }
        break; // no need to continue in for loop, we found button.
      }
    }
    /* removed until domoticz has a better virtual thermostat
    if (idx == _aqconfig_.dzidx_pool_thermostat) {
      float degC = atof(svalue);
      int degF = (int)degCtoF(degC);
      if (degC > 0.0 && 1 < (degF - _aqualink_data->pool_htr_set_point)) {
        logMessage(LOG_INFO, "MQTT: DZ: received temp setting '%s' for 'pool heater setpoint' old value %d(f) setting to %f(c) %d(f)\n",svalue, _aqualink_data->pool_htr_set_point, degC, degF);
        //aq_programmer(AQ_SET_POOL_HEATER_TEMP, (int)degCtoF(degl), _aqualink_data);
      } else {
        logMessage(LOG_INFO, "MQTT: DZ: received temp setting '%s' for 'pool heater setpoint' matched current setting (or was zero), ignoring!\n", svalue);
      }
    } else if (idx == _aqconfig_.dzidx_spa_thermostat) {
      float degC = atof(svalue);
      int degF = (int)degCtoF(degC);
      if (degC > 0.0 && 1 < (degF - _aqualink_data->spa_htr_set_point)) {
        logMessage(LOG_INFO, "MQTT: DZ: received temp setting '%s' for 'spa heater setpoint' old value %d(f) setting to %f(c) %d(f)\n",svalue, _aqualink_data->spa_htr_set_point, degC, degF);
        //aq_programmer(AQ_SET_POOL_HEATER_TEMP, (int)degCtoF(degl), _aqualink_data);
      } else {
        logMessage(LOG_INFO, "MQTT: DZ: received temp setting '%s' for 'spa heater setpoint' matched current setting (or was zero), ignoring!\n", svalue);
      }
    }*/
//printf("Finished checking IDX for setpoint\n"); 
  }

  // NSF Need to check idx against ours and decide if we are interested in it.
}

static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
  struct mg_mqtt_message *mqtt_msg;
  struct http_message *http_msg;
  struct websocket_message *ws_msg;
  char aq_topic[30];
  //static double last_control_time;

  // logMessage (LOG_DEBUG, "Event\n");
  switch (ev) {
  case MG_EV_HTTP_REQUEST:
    //nc->user_data = WEB;
    http_msg = (struct http_message *)ev_data;
    action_web_request(nc, http_msg);
    logMessage(LOG_DEBUG, "Served WEB request\n");
    break;
  
  case MG_EV_WEBSOCKET_HANDSHAKE_DONE:
    //nc->user_data = WS;
    _aqualink_data->open_websockets++;
    logMessage(LOG_DEBUG, "++ Websocket joined\n");
    break;
  
  case MG_EV_WEBSOCKET_FRAME: 
    ws_msg = (struct websocket_message *)ev_data;
    action_websocket_request(nc, ws_msg);
    break;
  
  case MG_EV_CLOSE: 
    if (is_websocket(nc)) {
      _aqualink_data->open_websockets--;
      logMessage(LOG_DEBUG, "-- Websocket left\n");
    } else if (is_mqtt(nc)) {
      logMessage(LOG_WARNING, "MQTT Connection closed\n");
      _mqtt_exit_flag = true;
    }
    break;
  
  case MG_EV_CONNECT: {
    //nc->user_data = MQTT;
    //nc->flags |= MG_F_USER_1; // NFS Need to readup on this
    set_mqtt(nc);
    _mqtt_exit_flag = false;
    //char *MQTT_id = "AQUALINK_MQTT_TEST_ID";
    struct mg_send_mqtt_handshake_opts opts;
    memset(&opts, 0, sizeof(opts));
    opts.user_name = _aqconfig_.mqtt_user;
    opts.password = _aqconfig_.mqtt_passwd;
    opts.keep_alive = 5;
    opts.flags |= MG_MQTT_CLEAN_SESSION; // NFS Need to readup on this

    snprintf(aq_topic, 24, "%s/%s", _aqconfig_.mqtt_aq_topic,MQTT_LWM_TOPIC);
    opts.will_topic = aq_topic;
    opts.will_message = MQTT_OFF;

    mg_set_protocol_mqtt(nc);
    mg_send_mqtt_handshake_opt(nc, _aqconfig_.mqtt_ID, opts);
    logMessage(LOG_INFO, "MQTT: Subscribing mqtt with id of: %s\n", _aqconfig_.mqtt_ID);
    //last_control_time = mg_time();
  } break;

  case MG_EV_MQTT_CONNACK:
    {
      struct mg_mqtt_topic_expression topics[2];
      
      int qos=0;// can't be bothered with ack, so set to 0

      logMessage(LOG_DEBUG, "MQTT: Connection acknowledged\n");
      mqtt_msg = (struct mg_mqtt_message *)ev_data;
      if (mqtt_msg->connack_ret_code != MG_EV_MQTT_CONNACK_ACCEPTED) {
        logMessage(LOG_WARNING, "Got mqtt connection error: %d\n", mqtt_msg->connack_ret_code);
        _mqtt_exit_flag = true;
      }
      
      snprintf(aq_topic, 29, "%s/#", _aqconfig_.mqtt_aq_topic);
      if (_aqconfig_.mqtt_aq_topic != NULL && _aqconfig_.mqtt_dz_sub_topic != NULL) {
        topics[0].topic = aq_topic;
        topics[0].qos = qos; 
        topics[1].topic = _aqconfig_.mqtt_dz_sub_topic;
        topics[1].qos = qos;
        mg_mqtt_subscribe(nc, topics, 2, 42);
        logMessage(LOG_INFO, "MQTT: Subscribing to '%s'\n", aq_topic);
        logMessage(LOG_INFO, "MQTT: Subscribing to '%s'\n", _aqconfig_.mqtt_dz_sub_topic);
      } 
      else if (_aqconfig_.mqtt_aq_topic != NULL) {
        topics[0].topic = aq_topic;
        topics[0].qos = qos;
        mg_mqtt_subscribe(nc, topics, 1, 42);
        logMessage(LOG_INFO, "MQTT: Subscribing to '%s'\n", aq_topic);
      } 
      else if (_aqconfig_.mqtt_dz_sub_topic != NULL) {
        topics[0].topic = _aqconfig_.mqtt_dz_sub_topic;;
        topics[0].qos = qos;
        mg_mqtt_subscribe(nc, topics, 1, 42);
        logMessage(LOG_INFO, "MQTT: Subscribing to '%s'\n", _aqconfig_.mqtt_dz_sub_topic);
      }
    }
    break;
  case MG_EV_MQTT_PUBACK:
    mqtt_msg = (struct mg_mqtt_message *)ev_data;
    logMessage(LOG_DEBUG, "MQTT: Message publishing acknowledged (msg_id: %d)\n", mqtt_msg->message_id);
    break;
  case MG_EV_MQTT_SUBACK:
    logMessage(LOG_INFO, "MQTT: Subscription(s) acknowledged\n");
    snprintf(aq_topic, 24, "%s/%s", _aqconfig_.mqtt_aq_topic,MQTT_LWM_TOPIC);
    send_mqtt(nc, aq_topic ,MQTT_ON);
    break;
  case MG_EV_MQTT_PUBLISH: 
    mqtt_msg = (struct mg_mqtt_message *)ev_data;
    
    if (mqtt_msg->message_id != 0) {
      logMessage(LOG_DEBUG, "MQTT: received (msg_id: %d), looks like my own message, ignoring\n", mqtt_msg->message_id);
    }
// NSF Need to change strlen to a global so it's not executed every time we check a topic
    if (_aqconfig_.mqtt_aq_topic != NULL && strncmp(mqtt_msg->topic.p, _aqconfig_.mqtt_aq_topic, strlen(_aqconfig_.mqtt_aq_topic)) == 0) 
    {
      action_mqtt_message(nc, mqtt_msg);
    }
    if (_aqconfig_.mqtt_dz_sub_topic != NULL && strncmp(mqtt_msg->topic.p, _aqconfig_.mqtt_dz_sub_topic, strlen(_aqconfig_.mqtt_dz_sub_topic)) == 0) {
      action_domoticz_mqtt_message(nc, mqtt_msg);
    }
    break;
    /*
    // MQTT ping wasn't working in previous versions.
  case MG_EV_POLL: {
    struct mg_mqtt_proto_data *pd = (struct mg_mqtt_proto_data *)nc->proto_data;
    double now = mg_time();

    if (pd->keep_alive > 0 && last_control_time > 0 && (now - last_control_time) > pd->keep_alive) {
      logMessage(LOG_INFO, "MQTT: Sending MQTT ping\n");
      mg_mqtt_ping(nc);
      last_control_time = now;
    }
  }
    break;*/
  }
}

void start_mqtt(struct mg_mgr *mgr) {
  logMessage (LOG_NOTICE, "Starting MQTT client to %s\n", _aqconfig_.mqtt_server);
  if ( _aqconfig_.mqtt_server == NULL || 
      ( _aqconfig_.mqtt_aq_topic == NULL && _aqconfig_.mqtt_dz_pub_topic == NULL && _aqconfig_.mqtt_dz_sub_topic == NULL) )
    return;

  if (mg_connect(mgr, _aqconfig_.mqtt_server, ev_handler) == NULL) {
      logMessage (LOG_ERR, "Failed to create MQTT listener to %s\n", _aqconfig_.mqtt_server);
  } else {
    int i;
    for (i=0; i < _aqualink_data->total_buttons; i++) {
      _last_mqtt_aqualinkdata.aqualinkleds[i].state = LED_S_UNKNOWN;
    }
    _last_mqtt_aqualinkdata.ar_swg_status = SWG_STATUS_UNKNOWN;
    _last_mqtt_aqualinkdata.battery = -1;
    _last_mqtt_aqualinkdata.frz_protect_state = -1;
    _last_mqtt_aqualinkdata.boost = -1;
    _last_mqtt_aqualinkdata.service_mode_state = -1;
    _mqtt_exit_flag = false; // set here to stop multiple connects, if it fails truley fails it will get set to false.
  }
}

//bool start_web_server(struct mg_mgr *mgr, struct aqualinkdata *aqdata, char *port, char* web_root) {
//bool start_net_services(struct mg_mgr *mgr, struct aqualinkdata *aqdata, struct aqconfig *aqconfig) {
bool start_net_services(struct mg_mgr *mgr, struct aqualinkdata *aqdata) {
  struct mg_connection *nc;
  _aqualink_data = aqdata;
  //_aqconfig_ = aqconfig;
 
  signal(SIGTERM, signal_handler);
  signal(SIGINT, signal_handler);
  setvbuf(stdout, NULL, _IOLBF, 0);
  setvbuf(stderr, NULL, _IOLBF, 0);
  
  mg_mgr_init(mgr, NULL);
  logMessage (LOG_NOTICE, "Starting web server on port %s\n", _aqconfig_.socket_port);
  nc = mg_bind(mgr, _aqconfig_.socket_port, ev_handler);
  if (nc == NULL) {
    logMessage (LOG_ERR, "Failed to create listener\n");
    return false;
  }

  // Set up HTTP server parameters
  mg_set_protocol_http_websocket(nc);
  s_http_server_opts.document_root = _aqconfig_.web_directory;  // Serve current directory
  s_http_server_opts.enable_directory_listing = "yes";
  
#ifndef MG_DISABLE_MQTT
  // Start MQTT
  start_mqtt(mgr);
#endif


  return true;
}
