diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 8a3954b..0000000 --- a/Dockerfile +++ /dev/null @@ -1,59 +0,0 @@ -##################################### -# -# Build container -# The most basic build for aqualinkd latest version -##################################### - -FROM debian:bookworm AS aqualinkd-build - -#VOLUME ["/aqualinkd-build"] - -RUN apt-get update && \ - apt-get -y install build-essential libsystemd-dev - -# Seup working dir -RUN mkdir /home/AqualinkD -WORKDIR /home/AqualinkD - -# Get latest release -RUN curl -sL $(curl -s https://api.github.com/repos/sfeakes/AqualinkD/releases/latest | grep "tarball_url" | cut -d'"' -f4) | tar xz --strip-components=1 - -# Build aqualinkd -RUN make clean && \ - make container - -##################################### -# -# Runtime container -# -##################################### - -FROM debian:bookworm-slim AS aqualinkd - -#ARG AQUALINKD_VERSION - -RUN apt-get update && \ - apt-get install -y cron curl && \ - apt-get clean - -# Set cron to read local.d -RUN sed -i '/EXTRA_OPTS=.-l./s/^#//g' /etc/default/cron - -# Add Open Container Initiative (OCI) annotations. -# See: https://github.com/opencontainers/image-spec/blob/main/annotations.md - -LABEL org.opencontainers.image.title="AqualinkD" -LABEL org.opencontainers.image.url="https://hub.docker.com/repository/docker/sfeakes/aqualinkd/general" -LABEL org.opencontainers.image.source="https://github.com/sfeakes/AqualinkD" -LABEL org.opencontainers.image.documentation="https://github.com/sfeakes/AqualinkD" -#LABEL org.opencontainers.image.version=$AQUALINKD_VERSION - - -COPY --from=aqualinkd-build /home/AqualinkD/release/aqualinkd /usr/local/bin/aqualinkd -COPY --from=aqualinkd-build /home/AqualinkD/release/serial_logger /usr/local/bin/serial_logger -COPY --from=aqualinkd-build /home/AqualinkD/web/ /var/www/aqualinkd/ -COPY --from=aqualinkd-build /home/AqualinkD/release/aqualinkd.conf /etc/aqualinkd.conf -#COPY --from=aqualinkd-build /home/AqualinkD/docker/aqualinkd-docker.cmd /usr/local/bin/aqualinkd-docker -RUN curl -s -o /usr/local/bin/aqualinkd-docker https://raw.githubusercontent.com/sfeakes/AqualinkD/master/extras/aqualinkd-docker.cmd - -CMD ["sh", "-c", "/usr/local/bin/aqualinkd-docker"] diff --git a/Makefile b/Makefile index 520e64c..634b5d8 100755 --- a/Makefile +++ b/Makefile @@ -80,7 +80,7 @@ endif # Main source files -SRCS = aqualinkd.c utils.c config.c aq_serial.c aq_panel.c aq_programmer.c net_services.c json_messages.c rs_msg_utils.c\ +SRCS = aqualinkd.c utils.c config.c aq_serial.c aq_panel.c aq_programmer.c allbutton.c allbutton_aq_programmer.c net_services.c json_messages.c rs_msg_utils.c\ devices_jandy.c packetLogger.c devices_pentair.c color_lights.c serialadapter.c aq_timer.c aq_scheduler.c web_config.c\ serial_logger.c mongoose.c hassio.c simulator.c timespec_subtract.c @@ -140,10 +140,14 @@ SL_SRC = serial_logger.c aq_serial.c utils.c packetLogger.c rs_msg_utils.c times #MG_SRC = mongoose.c # Build durectories +SRC_DIR := ./ OBJ_DIR := ./build DBG_OBJ_DIR := $(OBJ_DIR)/debug SL_OBJ_DIR := $(OBJ_DIR)/slog +INCLUDES := -I$(SRC_DIR) + +# define path for obj files per architecture OBJ_DIR_ARMHF := $(OBJ_DIR)/armhf OBJ_DIR_ARM64 := $(OBJ_DIR)/arm64 OBJ_DIR_AMD64 := $(OBJ_DIR)/amd64 @@ -151,18 +155,37 @@ SL_OBJ_DIR_ARMHF := $(OBJ_DIR_ARMHF)/slog SL_OBJ_DIR_ARM64 := $(OBJ_DIR_ARM64)/slog SL_OBJ_DIR_AMD64 := $(OBJ_DIR_AMD64)/slog -# Object files -OBJ_FILES := $(patsubst %.c,$(OBJ_DIR)/%.o,$(SRCS)) -DBG_OBJ_FILES := $(patsubst %.c,$(DBG_OBJ_DIR)/%.o,$(DBG_SRC)) -SL_OBJ_FILES := $(patsubst %.c,$(SL_OBJ_DIR)/%.o,$(SL_SRC)) +# append path to source +SRCS := $(patsubst %.c,$(SRC_DIR)/%.c,$(SRCS)) +DBG_SRC := $(patsubst %.c,$(SRC_DIR)/%.c,$(DBG_SRC)) +SL_SRC := $(patsubst %.c,$(SRC_DIR)/%.c,$(SL_SRC)) + +# append path to obj files per architecture +OBJ_FILES := $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR)/%.o,$(SRCS)) +DBG_OBJ_FILES := $(patsubst $(SRC_DIR)/%.c,$(DBG_OBJ_DIR)/%.o,$(DBG_SRC)) +SL_OBJ_FILES := $(patsubst $(SRC_DIR)/%.c,$(SL_OBJ_DIR)/%.o,$(SL_SRC)) + +OBJ_FILES_ARMHF := $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR_ARMHF)/%.o,$(SRCS)) +OBJ_FILES_ARM64 := $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR_ARM64)/%.o,$(SRCS)) +OBJ_FILES_AMD64 := $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR_AMD64)/%.o,$(SRCS)) + +SL_OBJ_FILES_ARMHF := $(patsubst $(SRC_DIR)/%.c,$(SL_OBJ_DIR_ARMHF)/%.o,$(SL_SRC)) +SL_OBJ_FILES_ARM64 := $(patsubst $(SRC_DIR)/%.c,$(SL_OBJ_DIR_ARM64)/%.o,$(SL_SRC)) +SL_OBJ_FILES_AMD64 := $(patsubst $(SRC_DIR)/%.c,$(SL_OBJ_DIR_AMD64)/%.o,$(SL_SRC)) + +#OBJ_FILES := $(patsubst %.c,$(OBJ_DIR)/%.o,$(SRCS)) +#DBG_OBJ_FILES := $(patsubst %.c,$(DBG_OBJ_DIR)/%.o,$(DBG_SRC)) +#SL_OBJ_FILES := $(patsubst %.c,$(SL_OBJ_DIR)/%.o,$(SL_SRC)) + +#OBJ_FILES_ARMHF := $(patsubst %.c,$(OBJ_DIR_ARMHF)/%.o,$(SRCS)) +#OBJ_FILES_ARM64 := $(patsubst %.c,$(OBJ_DIR_ARM64)/%.o,$(SRCS)) +#OBJ_FILES_AMD64 := $(patsubst %.c,$(OBJ_DIR_AMD64)/%.o,$(SRCS)) + +#SL_OBJ_FILES_ARMHF := $(patsubst %.c,$(SL_OBJ_DIR_ARMHF)/%.o,$(SL_SRC)) +#SL_OBJ_FILES_ARM64 := $(patsubst %.c,$(SL_OBJ_DIR_ARM64)/%.o,$(SL_SRC)) +#SL_OBJ_FILES_AMD64 := $(patsubst %.c,$(SL_OBJ_DIR_AMD64)/%.o,$(SL_SRC)) -OBJ_FILES_ARMHF := $(patsubst %.c,$(OBJ_DIR_ARMHF)/%.o,$(SRCS)) -OBJ_FILES_ARM64 := $(patsubst %.c,$(OBJ_DIR_ARM64)/%.o,$(SRCS)) -OBJ_FILES_AMD64 := $(patsubst %.c,$(OBJ_DIR_AMD64)/%.o,$(SRCS)) -SL_OBJ_FILES_ARMHF := $(patsubst %.c,$(SL_OBJ_DIR_ARMHF)/%.o,$(SL_SRC)) -SL_OBJ_FILES_ARM64 := $(patsubst %.c,$(SL_OBJ_DIR_ARM64)/%.o,$(SL_SRC)) -SL_OBJ_FILES_AMD64 := $(patsubst %.c,$(SL_OBJ_DIR_AMD64)/%.o,$(SL_SRC)) #MG_OBJ_FILES := $(patsubst %.c,$(OBJ_DIR)/%.o,$(MG_SRC)) # define the executable file @@ -252,31 +275,31 @@ install: # Rules to compile -$(OBJ_DIR)/%.o: %.c | $(OBJ_DIR) +$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR) $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $< -$(DBG_OBJ_DIR)/%.o: %.c | $(DBG_OBJ_DIR) +$(DBG_OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(DBG_OBJ_DIR) $(CC) $(DBG_CFLAGS) $(INCLUDES) -c -o $@ $< -$(SL_OBJ_DIR)/%.o: %.c | $(SL_OBJ_DIR) +$(SL_OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(SL_OBJ_DIR) $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $< -$(OBJ_DIR_ARMHF)/%.o: %.c | $(OBJ_DIR_ARMHF) +$(OBJ_DIR_ARMHF)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR_ARMHF) $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $< -$(SL_OBJ_DIR_ARMHF)/%.o: %.c | $(SL_OBJ_DIR_ARMHF) +$(SL_OBJ_DIR_ARMHF)/%.o: $(SRC_DIR)/%.c | $(SL_OBJ_DIR_ARMHF) $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $< -$(OBJ_DIR_ARM64)/%.o: %.c | $(OBJ_DIR_ARM64) +$(OBJ_DIR_ARM64)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR_ARM64) $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $< -$(SL_OBJ_DIR_ARM64)/%.o: %.c | $(SL_OBJ_DIR_ARM64) +$(SL_OBJ_DIR_ARM64)/%.o: $(SRC_DIR)/%.c | $(SL_OBJ_DIR_ARM64) $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $< -$(OBJ_DIR_AMD64)/%.o: %.c | $(OBJ_DIR_AMD64) +$(OBJ_DIR_AMD64)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR_AMD64) $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $< -$(SL_OBJ_DIR_AMD64)/%.o: %.c | $(SL_OBJ_DIR_AMD64) +$(SL_OBJ_DIR_AMD64)/%.o: $(SRC_DIR)/%.c | $(SL_OBJ_DIR_AMD64) $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $< # Rules to link diff --git a/allbutton.c b/allbutton.c new file mode 100644 index 0000000..5154aa9 --- /dev/null +++ b/allbutton.c @@ -0,0 +1,697 @@ +#define _GNU_SOURCE 1 // for strcasestr & strptime +#include +#include +#include + +#include "aqualink.h" +#include "allbutton.h" +#include "rs_msg_utils.h" +#include "devices_jandy.h" +#include "allbutton_aq_programmer.h" + +void processLEDstate(struct aqualinkdata *aq_data) +{ + + int i = 0; + int byte; + int bit; + + for (byte = 0; byte < 5; byte++) + { + for (bit = 0; bit < 8; bit += 2) + { + if (((aq_data->raw_status[byte] >> (bit + 1)) & 1) == 1) + aq_data->aqualinkleds[i].state = FLASH; + else if (((aq_data->raw_status[byte] >> bit) & 1) == 1) + aq_data->aqualinkleds[i].state = ON; + else + aq_data->aqualinkleds[i].state = OFF; + + //LOG(ALLB_LOG,LOG_DEBUG,"Led %d state %d",i+1,aq_data->aqualinkleds[i].state); + i++; + } + } + // Reset enabled state for heaters, as they take 2 led states + if (aq_data->aqualinkleds[POOL_HTR_LED_INDEX - 1].state == OFF && aq_data->aqualinkleds[POOL_HTR_LED_INDEX].state == ON) + aq_data->aqualinkleds[POOL_HTR_LED_INDEX - 1].state = ENABLE; + + if (aq_data->aqualinkleds[SPA_HTR_LED_INDEX - 1].state == OFF && aq_data->aqualinkleds[SPA_HTR_LED_INDEX].state == ON) + aq_data->aqualinkleds[SPA_HTR_LED_INDEX - 1].state = ENABLE; + + if (aq_data->aqualinkleds[SOLAR_HTR_LED_INDEX - 1].state == OFF && aq_data->aqualinkleds[SOLAR_HTR_LED_INDEX].state == ON) + aq_data->aqualinkleds[SOLAR_HTR_LED_INDEX - 1].state = ENABLE; + /* + for (i=0; i < TOTAL_BUTTONS; i++) { + LOG(ALLB_LOG,LOG_NOTICE, "%s = %d", aq_data->aqbuttons[i].name, aq_data->aqualinkleds[i].state); + } +*/ +} + +void setUnits(char *msg, struct aqualinkdata *aq_data) +{ + char buf[AQ_MSGLEN*3]; + + rsm_strncpy(buf, (unsigned char *)msg, AQ_MSGLEN*3, AQ_MSGLONGLEN); + + //ascii(buf, msg); + LOG(ALLB_LOG,LOG_DEBUG, "Getting temp units from message '%s', looking at '%c'\n", buf, buf[strlen(buf) - 1]); + + if (msg[strlen(msg) - 1] == 'F') + aq_data->temp_units = FAHRENHEIT; + else if (msg[strlen(msg) - 1] == 'C') + aq_data->temp_units = CELSIUS; + else + aq_data->temp_units = UNKNOWN; + + LOG(ALLB_LOG,LOG_INFO, "Temp Units set to %d (F=0, C=1, Unknown=2)\n", aq_data->temp_units); +} + +// Defined as int16_t so 16 bits to mask +#define MSG_FREEZE (1 << 0) // 1 +#define MSG_SERVICE (1 << 1) // 1 +#define MSG_SWG (1 << 2) +#define MSG_BOOST (1 << 3) +#define MSG_TIMEOUT (1 << 4) +#define MSG_RS13BUTTON (1 << 5) +#define MSG_RS14BUTTON (1 << 6) +#define MSG_RS15BUTTON (1 << 7) +#define MSG_RS16BUTTON (1 << 8) +#define MSG_BATTERY_LOW (1 << 9) +#define MSG_SWG_DEVICE (1 << 10) + +#ifdef AQ_RS16 +int16_t RS16_endswithLEDstate(char *msg, struct aqualinkdata *aq_data) +{ + char *sp; + int i; + aqledstate state = LED_S_UNKNOWN; + + //if (_aqconfig_.rs_panel_size < 16) + if (PANEL_SIZE() < 16) + return false; + + sp = strrchr(msg, ' '); + + if( sp == NULL ) + return false; + + if (strncasecmp(sp, " on", 3) == 0) + state = ON; + else if (strncasecmp(sp, " off", 4) == 0) + state = OFF; + else if (strncasecmp(sp, " enabled", 8) == 0) // Total guess, need to check + state = ENABLE; + else if (strncasecmp(sp, " no idea", 8) == 0) // need to figure out these states + state = FLASH; + + if (state == LED_S_UNKNOWN) + return false; + + // Only need to start at Aux B5->B8 (12-15) + // Loop over only aqdata->aqbuttons[13] to aqdata->aqbuttons[16] + for (i = aq_data->rs16_vbutton_start; i <= aq_data->rs16_vbutton_end; i++) { + //TOTAL_BUTTONS + if ( stristr(msg, aq_data->aqbuttons[i].label) != NULL) { + aq_data->aqbuttons[i].led->state = state; + LOG(ALLB_LOG,LOG_INFO, "Set %s to %d\n", aq_data->aqbuttons[i].label, aq_data->aqbuttons[i].led->state); + // Return true should be the result, but in the if we want to continue to display message + //return true; + if (i == 13) + return MSG_RS13BUTTON; + else if (i == 14) + return MSG_RS14BUTTON; + else if (i == 15) + return MSG_RS15BUTTON; + else if (i == 16) + return MSG_RS16BUTTON; + else + { + LOG(ALLB_LOG,LOG_ERR, "RS16 Button Set error %s to %d, %d is out of scope\n", aq_data->aqbuttons[i].label, aq_data->aqbuttons[i].led->state, i); + return false; + } + + } + } + + return false; +} +#endif + +void _processMessage(char *message, struct aqualinkdata *aq_data, bool reset); + +void processMessage(char *message, struct aqualinkdata *aq_data) +{ + _processMessage(message, aq_data, false); +} +void processMessageReset(struct aqualinkdata *aq_data) +{ + _processMessage(NULL, aq_data, true); +} +void _processMessage(char *message, struct aqualinkdata *aq_data, bool reset) +{ + char *msg; + static bool _initWithRS = false; + //static bool _gotREV = false; + //static int freeze_msg_count = 0; + //static int service_msg_count = 0; + //static int swg_msg_count = 0; + //static int boost_msg_count = 0; + static int16_t msg_loop = 0; + // NSF replace message with msg +#ifdef AQ_RS16 + int16_t rs16; +#endif + + //msg = stripwhitespace(message); + //strcpy(aq_data->last_message, msg); + //LOG(ALLB_LOG,LOG_INFO, "RS Message :- '%s'\n", msg); + + + + // Check long messages in this if/elseif block first, as some messages are similar. + // ie "POOL TEMP" and "POOL TEMP IS SET TO" so want correct match first. + // + + //if (stristr(msg, "JANDY AquaLinkRS") != NULL) { + if (!reset) { + msg = stripwhitespace(message); + strcpy(aq_data->last_message, msg); + LOG(ALLB_LOG,LOG_INFO, "RS Message :- '%s'\n", msg); + // Just set this to off, it will re-set since it'll be the only message we get if on + aq_data->service_mode_state = OFF; + } else { + //aq_data->display_message = NULL; + aq_data->last_display_message[0] = ' '; + aq_data->last_display_message[1] = '\0'; + + // Anything that wasn't on during the last set of messages, turn off + if ((msg_loop & MSG_FREEZE) != MSG_FREEZE) + aq_data->frz_protect_state = OFF; + + if ((msg_loop & MSG_SERVICE) != MSG_SERVICE && + (msg_loop & MSG_TIMEOUT) != MSG_TIMEOUT ) { + aq_data->service_mode_state = OFF; // IF we get this message then Service / Timeout is off + } + + if ( ((msg_loop & MSG_SWG_DEVICE) != MSG_SWG_DEVICE) && aq_data->swg_led_state != LED_S_UNKNOWN) { + // No Additional SWG devices messages like "no flow" + if ((msg_loop & MSG_SWG) != MSG_SWG && aq_data->aqbuttons[PUMP_INDEX].led->state == OFF ) + setSWGdeviceStatus(aq_data, ALLBUTTON, SWG_STATUS_OFF); + else + setSWGdeviceStatus(aq_data, ALLBUTTON, SWG_STATUS_ON); + } + + // If no AQUAPURE message, either (no SWG, it's set 0, or it's off). + if ((msg_loop & MSG_SWG) != MSG_SWG && aq_data->swg_led_state != LED_S_UNKNOWN ) { + if (aq_data->swg_percent != 0 || aq_data->swg_led_state == ON) { + // Something is wrong here. Let's check pump, if on set SWG to 0, if off turn SWE off + if ( aq_data->aqbuttons[PUMP_INDEX].led->state == OFF) { + LOG(ALLB_LOG,LOG_INFO, "No AQUAPURE message in cycle, pump is off so setting SWG to off\n"); + setSWGoff(aq_data); + } else { + LOG(ALLB_LOG,LOG_INFO, "No AQUAPURE message in cycle, pump is on so setting SWG to 0%%\n"); + changeSWGpercent(aq_data, 0); + } + } else if (isIAQT_ENABLED == false && isONET_ENABLED == false && READ_RSDEV_SWG == false ) { + //We have no other way to read SWG %=0, so turn SWG on with pump + if ( aq_data->aqbuttons[PUMP_INDEX].led->state == ON) { + LOG(ALLB_LOG,LOG_INFO, "No AQUAPURE message in cycle, pump is off so setting SWG to off\n"); + //changeSWGpercent(aq_data, 0); + setSWGenabled(aq_data); + } + } + // NSF Need something to catch startup when SWG=0 so we set it to enabeled. + // when other ways/protocols to detect SWG=0 are turned off. + } + /* + // AQUAPURE=0 we never get that message on ALLBUTTON so don't turn off unless filter pump if off + if ((msg_loop & MSG_SWG) != MSG_SWG && aq_data->aqbuttons[PUMP_INDEX].led->state == OFF ) { + //aq_data->ar_swg_status = SWG_STATUS_OFF; + setSWGoff(aq_data); + } + */ + if ((msg_loop & MSG_BOOST) != MSG_BOOST) { + aq_data->boost = false; + aq_data->boost_msg[0] = '\0'; + aq_data->boost_duration = 0; + //if (aq_data->swg_percent >= 101) + // aq_data->swg_percent = 0; + } + + if ((msg_loop & MSG_BATTERY_LOW) != MSG_BATTERY_LOW) + aq_data->battery = OK; + +#ifdef AQ_RS16 + //if ( _aqconfig_.rs_panel_size >= 16) { + //if ( (int)PANEL_SIZE >= 16) { // NSF No idea why this fails on RS-4, but it does. Come back and find out why + if ( PANEL_SIZE() >= 16 ) { + //printf("Panel size %d What the fuck am I doing here\n",PANEL_SIZE()); + if ((msg_loop & MSG_RS13BUTTON) != MSG_RS13BUTTON) + aq_data->aqbuttons[13].led->state = OFF; + if ((msg_loop & MSG_RS14BUTTON) != MSG_RS14BUTTON) + aq_data->aqbuttons[14].led->state = OFF; + if ((msg_loop & MSG_RS15BUTTON) != MSG_RS15BUTTON) + aq_data->aqbuttons[15].led->state = OFF; + if ((msg_loop & MSG_RS16BUTTON) != MSG_RS16BUTTON) + aq_data->aqbuttons[16].led->state = OFF; + } +#endif + msg_loop = 0; + return; + } + + if (stristr(msg, LNG_MSG_BATTERY_LOW) != NULL) + { + aq_data->battery = LOW; + msg_loop |= MSG_BATTERY_LOW; + strcpy(aq_data->last_display_message, msg); // Also display the message on web UI + } + else if (stristr(msg, LNG_MSG_POOL_TEMP_SET) != NULL) + { + //LOG(ALLB_LOG,LOG_DEBUG, "**************** pool htr long message: %s", &message[20]); + aq_data->pool_htr_set_point = atoi(message + 20); + + if (aq_data->temp_units == UNKNOWN) + setUnits(msg, aq_data); + } + else if (stristr(msg, LNG_MSG_SPA_TEMP_SET) != NULL) + { + //LOG(ALLB_LOG,LOG_DEBUG, "spa htr long message: %s", &message[19]); + aq_data->spa_htr_set_point = atoi(message + 19); + + if (aq_data->temp_units == UNKNOWN) + setUnits(msg, aq_data); + } + else if (stristr(msg, LNG_MSG_FREEZE_PROTECTION_SET) != NULL) + { + //LOG(ALLB_LOG,LOG_DEBUG, "frz protect long message: %s", &message[28]); + aq_data->frz_protect_set_point = atoi(message + 28); + aq_data->frz_protect_state = ENABLE; + + if (aq_data->temp_units == UNKNOWN) + setUnits(msg, aq_data); + } + else if (strncasecmp(msg, MSG_AIR_TEMP, MSG_AIR_TEMP_LEN) == 0) + { + aq_data->air_temp = atoi(msg + MSG_AIR_TEMP_LEN); + + if (aq_data->temp_units == UNKNOWN) + setUnits(msg, aq_data); + } + else if (strncasecmp(msg, MSG_POOL_TEMP, MSG_POOL_TEMP_LEN) == 0) + { + aq_data->pool_temp = atoi(msg + MSG_POOL_TEMP_LEN); + + if (aq_data->temp_units == UNKNOWN) + setUnits(msg, aq_data); + } + else if (strncasecmp(msg, MSG_SPA_TEMP, MSG_SPA_TEMP_LEN) == 0) + { + aq_data->spa_temp = atoi(msg + MSG_SPA_TEMP_LEN); + + if (aq_data->temp_units == UNKNOWN) + setUnits(msg, aq_data); + } + // NSF If get water temp rather than pool or spa in some cases, then we are in Pool OR Spa ONLY mode + else if (strncasecmp(msg, MSG_WATER_TEMP, MSG_WATER_TEMP_LEN) == 0) + { + aq_data->pool_temp = atoi(msg + MSG_WATER_TEMP_LEN); + aq_data->spa_temp = atoi(msg + MSG_WATER_TEMP_LEN); + if (aq_data->temp_units == UNKNOWN) + setUnits(msg, aq_data); + + if (isSINGLE_DEV_PANEL != true) + { + changePanelToMode_Only(); + LOG(ALLB_LOG,LOG_ERR, "AqualinkD set to 'Combo Pool & Spa' but detected 'Only Pool OR Spa' panel, please change config\n"); + } + } + else if (stristr(msg, LNG_MSG_WATER_TEMP1_SET) != NULL) + { + aq_data->pool_htr_set_point = atoi(message + 28); + + if (aq_data->temp_units == UNKNOWN) + setUnits(msg, aq_data); + + if (isSINGLE_DEV_PANEL != true) + { + changePanelToMode_Only(); + LOG(ALLB_LOG,LOG_ERR, "AqualinkD set to 'Combo Pool & Spa' but detected 'Only Pool OR Spa' panel, please change config\n"); + } + } + else if (stristr(msg, LNG_MSG_WATER_TEMP2_SET) != NULL) + { + aq_data->spa_htr_set_point = atoi(message + 27); + + if (aq_data->temp_units == UNKNOWN) + setUnits(msg, aq_data); + + if (isSINGLE_DEV_PANEL != true) + { + changePanelToMode_Only(); + LOG(ALLB_LOG,LOG_ERR, "AqualinkD set to 'Combo Pool & Spa' but detected 'Only Pool OR Spa' panel, please change config\n"); + } + } + else if (stristr(msg, LNG_MSG_SERVICE_ACTIVE) != NULL) + { + if (aq_data->service_mode_state == OFF) + LOG(ALLB_LOG,LOG_NOTICE, "AqualinkD set to Service Mode\n"); + aq_data->service_mode_state = ON; + msg_loop |= MSG_SERVICE; + //service_msg_count = 0; + } + else if (stristr(msg, LNG_MSG_TIMEOUT_ACTIVE) != NULL) + { + if (aq_data->service_mode_state == OFF) + LOG(ALLB_LOG,LOG_NOTICE, "AqualinkD set to Timeout Mode\n"); + aq_data->service_mode_state = FLASH; + msg_loop |= MSG_TIMEOUT; + //service_msg_count = 0; + } + else if (stristr(msg, LNG_MSG_FREEZE_PROTECTION_ACTIVATED) != NULL) + { + msg_loop |= MSG_FREEZE; + aq_data->frz_protect_state = ON; + //freeze_msg_count = 0; + strcpy(aq_data->last_display_message, msg); // Also display the message on web UI + } + /* // Not sure when to do with these for the moment, so no need to compile in the test. + else if (stristr(msg, LNG_MSG_CHEM_FEED_ON) != NULL) { + } + else if (stristr(msg, LNG_MSG_CHEM_FEED_OFF) != NULL) { + } + */ + else if (msg[2] == '/' && msg[5] == '/' && msg[8] == ' ') + { // date in format '08/29/16 MON' + strcpy(aq_data->date, msg); + } + else if (stristr(msg, MSG_SWG_PCT) != NULL) + { + if (strncasecmp(msg, MSG_SWG_PCT, MSG_SWG_PCT_LEN) == 0 && strncasecmp(msg, "AQUAPURE HRS", 12) != 0) { + changeSWGpercent(aq_data, atoi(msg + MSG_SWG_PCT_LEN)); + } + else if (strncasecmp(msg, "AQUAPURE HRS", 12) != 0 && strncasecmp(msg, "SET AQUAPURE", 12) != 0) + { + if (strcasestr(msg, MSG_SWG_NO_FLOW) != NULL) + setSWGdeviceStatus(aq_data, ALLBUTTON, SWG_STATUS_NO_FLOW); + else if (strcasestr(msg, MSG_SWG_LOW_SALT) != NULL) + setSWGdeviceStatus(aq_data, ALLBUTTON, SWG_STATUS_LOW_SALT); + else if (strcasestr(msg, MSG_SWG_HIGH_SALT) != NULL) + setSWGdeviceStatus(aq_data, ALLBUTTON, SWG_STATUS_HI_SALT); + else if (strcasestr(msg, MSG_SWG_FAULT) != NULL) + setSWGdeviceStatus(aq_data, ALLBUTTON, SWG_STATUS_GENFAULT); + //setSWGdeviceStatus(aq_data, ALLBUTTON, SWG_STATUS_CHECK_PCB); + + // Any of these messages want to display. + strcpy(aq_data->last_display_message, msg); + + msg_loop |= MSG_SWG_DEVICE; + } + msg_loop |= MSG_SWG; + } + else if (strncasecmp(msg, MSG_SWG_PPM, MSG_SWG_PPM_LEN) == 0) + { + aq_data->swg_ppm = atoi(msg + MSG_SWG_PPM_LEN); + msg_loop |= MSG_SWG; + } + else if ((msg[1] == ':' || msg[2] == ':') && msg[strlen(msg) - 1] == 'M') + { // time in format '9:45 AM' + strcpy(aq_data->time, msg); + // Setting time takes a long time, so don't try until we have all other programmed data. + if (_initWithRS == true && strlen(aq_data->date) > 1 && checkAqualinkTime() != true) + { + LOG(ALLB_LOG,LOG_NOTICE, "RS time is NOT accurate '%s %s', re-setting on controller!\n", aq_data->time, aq_data->date); + aq_programmer(AQ_SET_TIME, NULL, aq_data); + } + else if (_initWithRS == false || _aqconfig_.sync_panel_time == false) + { + LOG(ALLB_LOG,LOG_DEBUG, "RS time '%s %s' not checking\n", aq_data->time, aq_data->date); + } + else if (_initWithRS == true) + { + LOG(ALLB_LOG,LOG_DEBUG, "RS time is accurate '%s %s'\n", aq_data->time, aq_data->date); + } + // If we get a time message before REV, the controller didn't see us as we started too quickly. + /* Don't need to check this anymore with the check for probe before startup. + if (_gotREV == false) + { + LOG(ALLB_LOG,LOG_NOTICE, "Getting control panel information\n", msg); + aq_programmer(AQ_GET_DIAGNOSTICS_MODEL, NULL, aq_data); + _gotREV = true; // Force it to true just incase we don't understand the model# + } + */ + } + else if (strstr(msg, " REV ") != NULL || strstr(msg, " REV. ") != NULL) + { // '8157 REV MMM' + // A master firmware revision message. + strcpy(aq_data->version, msg); + rsm_get_revision(aq_data->revision, aq_data->version, strlen(aq_data->version)); + //_gotREV = true; + LOG(ALLB_LOG,LOG_NOTICE, "Control Panel version %s\n", aq_data->version); + LOG(ALLB_LOG,LOG_NOTICE, "Control Panel revision %s\n", aq_data->revision); + if (_initWithRS == false) + { + //LOG(ALLBUTTON,LOG_NOTICE, "Standard protocol initialization complete\n"); + queueGetProgramData(ALLBUTTON, aq_data); + //queueGetExtendedProgramData(ALLBUTTON, aq_data, _aqconfig_.use_panel_aux_labels); + _initWithRS = true; + } + } + else if (stristr(msg, " TURNS ON") != NULL) + { + LOG(ALLB_LOG,LOG_NOTICE, "Program data '%s'\n", msg); + } + else if (_aqconfig_.override_freeze_protect == TRUE && strncasecmp(msg, "Press Enter* to override Freeze Protection with", 47) == 0) + { + //send_cmd(KEY_ENTER, aq_data); + //aq_programmer(AQ_SEND_CMD, (char *)KEY_ENTER, aq_data); + aq_send_allb_cmd(KEY_ENTER); + } + // Process any button states (fake LED) for RS12 and above keypads + // Text will be button label on or off ie Aux_B2 off or WaterFall off + +#ifdef AQ_RS16 + //else if ( _aqconfig_.rs_panel_size >= 16 && (rs16 = RS16_endswithLEDstate(msg)) != 0 ) + else if (PANEL_SIZE() >= 16 && (rs16 = RS16_endswithLEDstate(msg, aq_data)) != 0 ) + { + msg_loop |= rs16; + // Do nothing, just stop other else if statments executing + // make sure we also display the message. + // Note we only get ON messages here, Off messages will not be sent if something else turned it off + // use the Onetouch or iAqua equiptment page for off. + strcpy(aq_data->last_display_message, msg); + } +#endif + else if (((msg[4] == ':') || (msg[6] == ':')) && (strncasecmp(msg, "AUX", 3) == 0) ) + { // Should probable check we are in programming mode. + // 'Aux3: No Label' + // 'Aux B1: No Label' + int labelid; + int ni = 3; + if (msg[4] == 'B') { ni = 5; } + labelid = atoi(msg + ni); + if (labelid > 0 && _aqconfig_.use_panel_aux_labels == true) + { + if (ni == 5) + labelid = labelid + 8; + else + labelid = labelid + 1; + // Aux1: on panel = Button 3 in aqualinkd (button 2 in array) + if (strncasecmp(msg+ni+3, "No Label", 8) != 0) { + aq_data->aqbuttons[labelid].label = prittyString(cleanalloc(msg+ni+2)); + LOG(ALLB_LOG,LOG_NOTICE, "AUX ID %s label set to '%s'\n", aq_data->aqbuttons[labelid].name, aq_data->aqbuttons[labelid].label); + } else { + LOG(ALLB_LOG,LOG_NOTICE, "AUX ID %s has no control panel label using '%s'\n", aq_data->aqbuttons[labelid].name, aq_data->aqbuttons[labelid].label); + } + //aq_data->aqbuttons[labelid + 1].label = cleanalloc(msg + 5); + } + } + // BOOST POOL 23:59 REMAINING + else if ( (strncasecmp(msg, "BOOST POOL", 10) == 0) && (strcasestr(msg, "REMAINING") != NULL) ) { + // Ignore messages if in programming mode. We get one of these turning off for some strange reason. + if (in_programming_mode(aq_data) == false) { + snprintf(aq_data->boost_msg, 6, "%s", &msg[11]); + aq_data->boost_duration = rsm_HHMM2min(aq_data->boost_msg); + aq_data->boost = true; + msg_loop |= MSG_BOOST; + msg_loop |= MSG_SWG; + //convert_boost_to_duration(aq_data->boost_msg) + //if (aq_data->ar_swg_status != SWG_STATUS_ON) {aq_data->ar_swg_status = SWG_STATUS_ON;} + if (aq_data->swg_percent != 101) {changeSWGpercent(aq_data, 101);} + //boost_msg_count = 0; + //if (aq_data->active_thread.thread_id == 0) + strcpy(aq_data->last_display_message, msg); // Also display the message on web UI if not in programming mode + } + } + else + { + LOG(ALLB_LOG,LOG_DEBUG_SERIAL, "Ignoring '%s'\n", msg); + //aq_data->display_message = msg; + //if (in_programming_mode(aq_data) == false && aq_data->simulate_panel == false && + if (in_programming_mode(aq_data) == false && + stristr(msg, "JANDY AquaLinkRS") == NULL && + //stristr(msg, "PUMP O") == NULL &&// Catch 'PUMP ON' and 'PUMP OFF' but not 'PUMP WILL TURN ON' + strncasecmp(msg, "PUMP O", 6) != 0 &&// Catch 'PUMP ON' and 'PUMP OFF' but not 'PUMP WILL TURN ON' + stristr(msg, "MAINTAIN") == NULL && // Catch 'MAINTAIN TEMP IS OFF' + stristr(msg, "0 PSI") == NULL /* // Catch some erronious message on test harness + stristr(msg, "CLEANER O") == NULL && + stristr(msg, "SPA O") == NULL && + stristr(msg, "AUX") == NULL*/ + ) + { // Catch all AUX1 AUX5 messages + //aq_data->display_last_message = true; + strcpy(aq_data->last_display_message, msg); + //rsm_strncpy(aq_data->last_display_message, (unsigned char *)msg, AQ_MSGLONGLEN, AQ_MSGLONGLEN); + } + } + + // Send every message if we are in simulate panel mode + //if (aq_data->simulate_panel) + // strcpy(aq_data->last_display_message, msg); + //rsm_strncpy(aq_data->last_display_message, (unsigned char *)msg, AQ_MSGLONGLEN, AQ_MSGLONGLEN); + //ascii(aq_data->last_display_message, msg); + + + //LOG(ALLB_LOG,LOG_INFO, "RS Message loop :- '%d'\n", msg_loop); + + // We processed the next message, kick any threads waiting on the message. +//printf ("Message kicking\n"); + + + kick_aq_program_thread(aq_data, ALLBUTTON); +} + +bool process_allbutton_packet(unsigned char *packet, int length, struct aqualinkdata *aq_data) +{ + bool rtn = false; + //static unsigned char last_packet[AQ_MAXPKTLEN]; + static unsigned char last_checksum; + static char message[AQ_MSGLONGLEN + 1]; + static int processing_long_msg = 0; + + // Check packet against last check if different. + // Should only use the checksum, not whole packet since it's status messages. + /* + if ( packet[PKT_CMD] == CMD_STATUS && (memcmp(packet, last_packet, length) == 0)) + { + LOG(ALLB_LOG,LOG_DEBUG_SERIAL, "RS Received duplicate, ignoring.\n", length); + return rtn; + } + else + { + memcpy(last_packet, packet, length); + aq_data->last_packet_type = packet[PKT_CMD]; + rtn = true; + } + */ + + aq_data->last_packet_type = packet[PKT_CMD]; + + + if ( packet[PKT_CMD] == CMD_STATUS && packet[length-3] == last_checksum && ! in_programming_mode(aq_data) ) + { + LOG(ALLB_LOG,LOG_DEBUG_SERIAL, "RS Received duplicate, ignoring.\n", length); + return false; + } + else + { + last_checksum = packet[length-3]; + rtn = true; + } + + if (processing_long_msg > 0 && packet[PKT_CMD] != CMD_MSG_LONG) + { + processing_long_msg = 0; + //LOG(ALLB_LOG,LOG_ERR, "RS failed to receive complete long message, received '%s'\n",message); + //LOG(ALLB_LOG,LOG_DEBUG, "RS didn't finished receiving of MSG_LONG '%s'\n",message); + processMessage(message, aq_data); + } + + LOG(ALLB_LOG,LOG_DEBUG_SERIAL, "RS Received packet type 0x%02hhx length %d.\n", packet[PKT_CMD], length); + + switch (packet[PKT_CMD]) + { + case CMD_ACK: + //LOG(ALLB_LOG,LOG_DEBUG_SERIAL, "RS Received ACK length %d.\n", length); + break; + case CMD_STATUS: + //LOG(ALLB_LOG,LOG_DEBUG_SERIAL, "RS Received STATUS length %d.\n", length); + memcpy(aq_data->raw_status, packet + 4, AQ_PSTLEN); + processLEDstate(aq_data); + if (aq_data->aqbuttons[PUMP_INDEX].led->state == OFF) + { + aq_data->pool_temp = TEMP_UNKNOWN; + aq_data->spa_temp = TEMP_UNKNOWN; + //aq_data->spa_temp = _aqconfig_.report_zero_spa_temp?-18:TEMP_UNKNOWN; + } + else if (aq_data->aqbuttons[SPA_INDEX].led->state == OFF && isSINGLE_DEV_PANEL != true) + { + //aq_data->spa_temp = _aqconfig_.report_zero_spa_temp?-18:TEMP_UNKNOWN; + aq_data->spa_temp = TEMP_UNKNOWN; + } + else if (aq_data->aqbuttons[SPA_INDEX].led->state == ON && isSINGLE_DEV_PANEL != true) + { + aq_data->pool_temp = TEMP_UNKNOWN; + } + + // COLOR MODE programming relies on state changes, so let any threads know + //if (aq_data->active_thread.ptype == AQ_SET_LIGHTPROGRAM_MODE) { + if ( in_light_programming_mode(aq_data) ) { + kick_aq_program_thread(aq_data, ALLBUTTON); + } + break; + case CMD_MSG: + case CMD_MSG_LONG: + { + int index = packet[PKT_DATA]; // Will get 0x00 for complete message, 0x01 for start on long message 0x05 last of long message + //printf("RSM received message at index %d '%.*s'\n",index,AQ_MSGLEN,(char *)packet + PKT_DATA + 1); + if (index <= 1){ + memset(message, 0, AQ_MSGLONGLEN + 1); + //strncpy(message, (char *)packet + PKT_DATA + 1, AQ_MSGLEN); + rsm_strncpy(message, packet + PKT_DATA + 1, AQ_MSGLONGLEN, AQ_MSGLEN); + processing_long_msg = index; + //LOG(ALLB_LOG,LOG_ERR, "Message %s\n",message); + } else { + //strncpy(&message[(processing_long_msg * AQ_MSGLEN)], (char *)packet + PKT_DATA + 1, AQ_MSGLEN); + //rsm_strncpy(&message[(processing_long_msg * AQ_MSGLEN)], (unsigned char *)packet + PKT_DATA + 1, AQ_MSGLONGLEN, AQ_MSGLEN); + rsm_strncpy(&message[( (index-1) * AQ_MSGLEN)], (unsigned char *)packet + PKT_DATA + 1, AQ_MSGLONGLEN, AQ_MSGLEN); + //LOG(ALLB_LOG,LOG_ERR, "Long Message %s\n",message); + if (++processing_long_msg != index) { + LOG(ALLB_LOG,LOG_DEBUG, "Long message index %d doesn't match buffer %d\n",index,processing_long_msg); + //printf("RSM Long message index %d doesn't match buffer %d\n",index,processing_long_msg); + } + #ifdef PROCESS_INCOMPLETE_MESSAGES + kick_aq_program_thread(aq_data, ALLBUTTON); + #endif + } + + if (index == 0 || index == 5) { + //printf("RSM process message '%s'\n",message); + + // MOVED FROM LINE 701 see if less errors + //kick_aq_program_thread(aq_data, ALLBUTTON); + + LOG(ALLB_LOG,LOG_DEBUG, "Processing Message - '%s'\n",message); + processMessage(message, aq_data); // This will kick thread + } + + } + break; + case CMD_PROBE: + LOG(ALLB_LOG,LOG_DEBUG, "RS Received PROBE length %d.\n", length); + //LOG(ALLB_LOG,LOG_INFO, "Synch'ing with Aqualink master device...\n"); + rtn = false; + break; + case CMD_MSG_LOOP_ST: + LOG(ALLB_LOG,LOG_INFO, "RS Received message loop start\n"); + processMessageReset(aq_data); + rtn = false; + break; + default: + LOG(ALLB_LOG,LOG_INFO, "RS Received unknown packet, 0x%02hhx\n", packet[PKT_CMD]); + rtn = false; + break; + } + + return rtn; +} \ No newline at end of file diff --git a/allbutton.h b/allbutton.h new file mode 100644 index 0000000..ed01660 --- /dev/null +++ b/allbutton.h @@ -0,0 +1,8 @@ +#ifndef ALLBUTTON_H_ +#define ALLBUTTON_H_ + + + +bool process_allbutton_packet(unsigned char *packet, int length, struct aqualinkdata *aq_data); + +#endif //ALLBUTTON_H_ \ No newline at end of file diff --git a/allbutton_aq_programmer.c b/allbutton_aq_programmer.c new file mode 100644 index 0000000..8d47bdb --- /dev/null +++ b/allbutton_aq_programmer.c @@ -0,0 +1,1359 @@ +#include +#include +#include +#include +#include +#include + +#include "aqualink.h" +#include "allbutton_aq_programmer.h" +#include "utils.h" +#include "aq_programmer.h" +#include "aq_serial.h" +#include "color_lights.h" +#include "devices_jandy.h" + +bool waitForButtonState(struct aqualinkdata *aq_data, aqkey* button, aqledstate state, int numMessageReceived); +bool waitForMessage(struct aqualinkdata *aq_data, char* message, int numMessageReceived); +bool waitForEitherMessage(struct aqualinkdata *aq_data, char* message1, char* message2, int numMessageReceived); + +bool select_sub_menu_item(struct aqualinkdata *aq_data, char* item_string); +bool select_menu_item(struct aqualinkdata *aq_data, char* item_string); + +void send_cmd(unsigned char cmd); +void cancel_menu(); + +void waitfor_queue2empty(); +void longwaitfor_queue2empty(); + +int _expectNextMessage = 0; +unsigned char _allb_last_sent_command = NUL; + +#define MAX_STACK 20 +int _allb_stack_place = 0; +unsigned char _allb_commands[MAX_STACK]; +//unsigned char pgm_allb_commands[MAX_STACK]; +unsigned char _allb_pgm_command = NUL; +//unsigned char _ot_allb_pgm_command = NUL; + +bool _last_sent_was_cmd = false; + +bool push_allb_cmd(unsigned char cmd); + +// External view of adding to queue +//void aq_send_cmd(unsigned char cmd) { +void aq_send_allb_cmd(unsigned char cmd) { + push_allb_cmd(cmd); +} + +bool push_allb_cmd(unsigned char cmd) { + + //LOG(ALLB_LOG, LOG_NOTICE, "push_aq_cmd '0x%02hhx'\n", cmd); + + if (_allb_stack_place < MAX_STACK) { + _allb_commands[_allb_stack_place] = cmd; + _allb_stack_place++; + } else { + LOG(ALLB_LOG, LOG_ERR, "Command queue overflow, too many unsent commands to RS control panel\n"); + return false; + } + + return true; +} + +int get_allb_queue_length() +{ + return _allb_stack_place; +} + + + +unsigned char pop_allb_cmd(struct aqualinkdata *aq_data) +{ + unsigned char cmd = NUL; + + if ( _expectNextMessage > 0 && + (aq_data->last_packet_type == CMD_MSG || aq_data->last_packet_type == CMD_MSG_LONG || aq_data->last_packet_type == CMD_MSG_LOOP_ST)) + { + _expectNextMessage=0; + } else if (_expectNextMessage > 3) { + // NSF Should probably check this is a status command AND we are in programming mode. + LOG(ALLB_LOG, LOG_ERR, "Did not receive expected reply from last RS SEND command, resending '0x%02hhx'\n", _allb_last_sent_command); + _expectNextMessage=0; + return _allb_last_sent_command; + } else if (_expectNextMessage > 0) { + _expectNextMessage++; + } + + // Only send commands on status messages and is we are not in programming mode. + // In programming move, we don't use the queue. + // Are we in programming mode and it's not ONETOUCH programming mode + if (in_programming_mode(aq_data) && ( in_ot_programming_mode(aq_data) == false && in_iaqt_programming_mode(aq_data) == false )) { + //if (aq_data->active_thread.thread_id != 0) { + if ( _allb_pgm_command != NUL && aq_data->last_packet_type == CMD_STATUS) { + cmd = _allb_pgm_command; + _allb_pgm_command = NUL; + LOG(ALLB_LOG, LOG_DEBUG_SERIAL, "RS SEND cmd '0x%02hhx' (programming)\n", cmd); + } else if (_allb_pgm_command != NUL) { + LOG(ALLB_LOG, LOG_DEBUG_SERIAL, "RS Waiting to send cmd '0x%02hhx' (programming)\n", _allb_pgm_command); + } else { + LOG(ALLB_LOG, LOG_DEBUG_SERIAL, "RS SEND cmd '0x%02hhx' empty queue (programming)\n", cmd); + } + } else if (_allb_stack_place > 0 && aq_data->last_packet_type == CMD_STATUS ) { + cmd = _allb_commands[0]; + _allb_stack_place--; + LOG(ALLB_LOG, LOG_DEBUG_SERIAL, "RS SEND cmd '0x%02hhx'\n", cmd); + //LOG(ALLB_LOG, LOG_NOTICE, "pop_cmd '0x%02hhx'\n", cmd); + memmove(&_allb_commands[0], &_allb_commands[1], sizeof(unsigned char) * _allb_stack_place ) ; + } else { + LOG(ALLB_LOG, LOG_DEBUG_SERIAL, "RS SEND cmd '0x%02hhx'\n", cmd); + } + +//printf("RSM sending cmd '0x%02hhx' in reply to '0x%02hhx'\n",cmd,aq_data->last_packet_type); + + if (cmd == KEY_ENTER || cmd == KEY_RIGHT || cmd == KEY_LEFT || cmd == KEY_MENU ) { //KEY_CANCEL KEY_HOLD KEY_OVERRIDE + _expectNextMessage=1; + _allb_last_sent_command = cmd; + } + + return cmd; +} + + + +bool setAqualinkNumericField_new(struct aqualinkdata *aq_data, char *value_label, int value, int increment); +bool setAqualinkNumericField(struct aqualinkdata *aq_data, char *value_label, int value) +{ + return setAqualinkNumericField_new(aq_data, value_label, value, 1); +} +bool setAqualinkNumericField_new(struct aqualinkdata *aq_data, char *value_label, int value, int increment) +{ + LOG(ALLB_LOG, LOG_DEBUG,"Setting menu item '%s' to %d\n",value_label, value); + //char leading[10]; // description of the field (POOL, SPA, FRZ) + int current_val=-1; // integer value of the current set point + //char trailing[10]; // the degrees and scale + char searchBuf[20]; + + sprintf(searchBuf, "^%s", value_label); + int val_len = strlen(value_label); + int i=0; + do + { + if (waitForMessage(aq_data, searchBuf, 4) != true) { + LOG(ALLB_LOG, LOG_WARNING, "AQ_Programmer Could not set numeric input '%s', not found\n",value_label); + cancel_menu(); + return false; + } +//LOG(ALLB_LOG, LOG_DEBUG,"WAITING for kick value=%d\n",current_val); + //sscanf(aq_data->last_message, "%s %d%s", leading, ¤t_val, trailing); + //sscanf(aq_data->last_message, "%*[^0123456789]%d", ¤t_val); + sscanf(&aq_data->last_message[val_len], "%*[^0123456789]%d", ¤t_val); + LOG(ALLB_LOG, LOG_DEBUG, "%s set to %d, looking for %d\n",value_label,current_val,value); + + if(value > current_val) { + // Increment the field. + sprintf(searchBuf, "%s %d", value_label, current_val+increment); + send_cmd(KEY_RIGHT); + } + else if(value < current_val) { + // Decrement the field. + sprintf(searchBuf, "%s %d", value_label, current_val-increment); + send_cmd(KEY_LEFT); + } + else { + // Just send ENTER. We are at the right value. + sprintf(searchBuf, "%s %d", value_label, current_val); + send_cmd(KEY_ENTER); + } + + if (i++ >= 100) { + LOG(ALLB_LOG, LOG_WARNING, "AQ_Programmer Could not set numeric input '%s', to '%d'\n",value_label,value); + send_cmd(KEY_ENTER); + break; + } + } while(value != current_val); + + return true; +} + + + +bool OLD_setAqualinkNumericField_OLD(struct aqualinkdata *aq_data, char *value_label, int value) +{ // Works for everything but not SWG + LOG(ALLB_LOG, LOG_DEBUG,"Setting menu item '%s' to %d\n",value_label, value); + char leading[10]; // description of the field (POOL, SPA, FRZ) + int current_val; // integer value of the current set point + char trailing[10]; // the degrees and scale + char searchBuf[20]; + + sprintf(searchBuf, "^%s", value_label); + + do + { + if (waitForMessage(aq_data, searchBuf, 3) != true) { + LOG(ALLB_LOG, LOG_WARNING, "AQ_Programmer Could not set numeric input '%s', not found\n",value_label); + cancel_menu(); + return false; + } +//LOG(ALLB_LOG, LOG_DEBUG,"WAITING for kick value=%d\n",current_val); + sscanf(aq_data->last_message, "%s %d%s", leading, ¤t_val, trailing); + LOG(ALLB_LOG, LOG_DEBUG, "%s set to %d, looking for %d\n",value_label,current_val,value); + + if(value > current_val) { + // Increment the field. + sprintf(searchBuf, "%s %d", value_label, current_val+1); + send_cmd(KEY_RIGHT); + } + else if(value < current_val) { + // Decrement the field. + sprintf(searchBuf, "%s %d", value_label, current_val-1); + send_cmd(KEY_LEFT); + } + else { + // Just send ENTER. We are at the right value. + sprintf(searchBuf, "%s %d", value_label, current_val); + send_cmd(KEY_ENTER); + } + } while(value != current_val); + + return true; +} +/* +void *threadded_send_cmd( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_SEND_CMD); + + send_cmd( (unsigned char)*threadCtrl->thread_args, aq_data); + + cleanAndTerminateThread(threadCtrl); + + return ptr; +} +*/ + +void *set_allbutton_boost( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_BOOST); + /* + menu + +BOOST POOL + +PRESS ENTER* TO START BOOST POOL + + + + +BOOST POOL 23:59 REMAINING + + +menu + +BOOST POOL + +STOP BOOST POOL + + */ + int val = atoi((char*)threadCtrl->thread_args); + + LOG(ALLB_LOG, LOG_DEBUG, "programming BOOST to %s\n", val==true?"On":"Off"); + + if ( select_menu_item(aq_data, "BOOST POOL") != true ) { + LOG(ALLB_LOG, LOG_WARNING, "Could not select BOOST POOL menu\n"); + cancel_menu(); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + if (val==true) { + waitForMessage(threadCtrl->aq_data, "TO START BOOST POOL", 5); + send_cmd(KEY_ENTER); + longwaitfor_queue2empty(); + } else { + int wait_messages = 5; + int i=0; + while( i++ < wait_messages) + { + waitForMessage(aq_data, "STOP BOOST POOL", 1); + if (stristr(aq_data->last_message, "STOP BOOST POOL") != NULL) { + // This is a really bad hack, message sequence is out for boost for some reason, so as soon as we see stop message, force enter key. + //_allb_pgm_command = KEY_ENTER; + send_cmd(KEY_ENTER); + LOG(ALLB_LOG, LOG_DEBUG, "**** FOUND STOP BOOST POOL ****\n"); + //waitfor_queue2empty(); + break; + } else { + LOG(ALLB_LOG, LOG_DEBUG, "Find item in Menu: loop %d of %d looking for 'STOP BOOST POOL' received message '%s'\n",i,wait_messages,aq_data->last_message); + delay(200); + if (stristr(aq_data->last_message, "STOP BOOST POOL") != NULL) { + //_allb_pgm_command = KEY_ENTER; + send_cmd(KEY_ENTER); + LOG(ALLB_LOG, LOG_DEBUG, "**** FOUND STOP BOOST POOL ****\n"); + break; + } + send_cmd(KEY_RIGHT); + //printf("WAIT\n"); + longwaitfor_queue2empty(); + //printf("FINISHED WAIT\n"); + } + //waitfor_queue2empty(); + //waitForMessage(aq_data, NULL, 1); + } + if (i < wait_messages) { + // Takes ages to see bost is off from menu, to set it here. + setSWGboost(aq_data, false); + } + /* + // Extra message overcome. + send_cmd(KEY_RIGHT); + waitfor_queue2empty(); + if ( select_sub_menu_item(aq_data, "STOP BOOST POOL") != true ) { + LOG(ALLB_LOG, LOG_WARNING, "Could not select STOP BOOST POOL menu\n"); + cancel_menu(); + cleanAndTerminateThread(threadCtrl); + return ptr; + }*/ + //send_cmd(KEY_ENTER); + } + + waitForMessage(aq_data,NULL, 1); + + cleanAndTerminateThread(threadCtrl); + + // just stop compiler error, ptr is not valid as it's just been freed + return ptr; +} + + +void *set_allbutton_SWG( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_SWG_PERCENT); + + int val = atoi((char*)threadCtrl->thread_args); + val = setpoint_check(SWG_SETPOINT, val, aq_data); + + //LOG(ALLB_LOG, LOG_NOTICE, "programming SWG percent to %d\n", val); + + if ( select_menu_item(aq_data, "SET AQUAPURE") != true ) { + LOG(ALLB_LOG, LOG_WARNING, "Could not select SET AQUAPURE menu\n"); + LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); + cancel_menu(); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + // If spa is on, set SWG for spa, if not set SWG for pool + if (aq_data->aqbuttons[SPA_INDEX].led->state != OFF) { + if (select_sub_menu_item(aq_data, "SET SPA SP") != true) { + LOG(ALLB_LOG, LOG_WARNING, "Could not select SWG setpoint menu for SPA\n"); + LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); + cancel_menu(); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + setAqualinkNumericField_new(aq_data, "SPA SP", val, 5); + } else { + if (select_sub_menu_item(aq_data, "SET POOL SP") != true) { + LOG(ALLB_LOG, LOG_WARNING, "Could not select SWG setpoint menu\n"); + LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); + cancel_menu(); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + setAqualinkNumericField_new(aq_data, "POOL SP", val, 5); + } + + // Let everyone know we set SWG, if it failed we will update on next message, unless it's 0. + setSWGpercent(aq_data, val); // Don't use chageSWGpercent as we are in programming mode. + +/* + if (select_sub_menu_item(aq_data, "SET POOL SP") != true) { + LOG(ALLB_LOG, LOG_WARNING, "Could not select SWG setpoint menu\n"); + cancel_menu(aq_data); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + setAqualinkNumericField_new(aq_data, "POOL SP", val, 5); +*/ + // usually miss this message, not sure why, but wait anyway to make sure programming has ended + // NSF have see the below message RS Message :- + // 'Pool set to 20%' + // 'POOL SP IS SET TO 20%' + waitForMessage(threadCtrl->aq_data, "SET TO", 1); + //waitForMessage(threadCtrl->aq_data, "POOL SP IS SET TO", 1); + + // Since we read % directly from RS message, wait for another few messages that way + // We won't registed a SWG bounce, since we already told clients SWG was at new % before programming started + waitForMessage(threadCtrl->aq_data, NULL, 1); + + cleanAndTerminateThread(threadCtrl); + + // just stop compiler error, ptr is not valid as it's just been freed + return ptr; +} + + + +void *get_allbutton_aux_labels( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_GET_AUX_LABELS); + + if ( select_menu_item(aq_data, "REVIEW") != true ) { + LOG(ALLB_LOG, LOG_WARNING, "Could not select REVIEW menu\n"); + LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); + cancel_menu(); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + if (select_sub_menu_item(aq_data, "AUX LABELS") != true) { + LOG(ALLB_LOG, LOG_WARNING, "Could not select AUX LABELS menu\n"); + LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); + cancel_menu(); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + waitForMessage(aq_data, NULL, 5); // Receive 5 messages + + cleanAndTerminateThread(threadCtrl); + + // just stop compiler error, ptr is not valid as it's just been freed + return ptr; +} + +void *set_allbutton_light_colormode( void *ptr ) +{ + int i; + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_LIGHTCOLOR_MODE); + + char *buf = (char*)threadCtrl->thread_args; + const char *mode_name; + int val = atoi(&buf[0]); + int btn = atoi(&buf[5]); + int typ = atoi(&buf[10]); + bool use_current_mode = false; + + if (btn < 0 || btn >= aq_data->total_buttons ) { + LOG(ALLB_LOG, LOG_ERR, "Can't program light mode on button %d\n", btn); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + aqkey *button = &aq_data->aqbuttons[btn]; + unsigned char code = button->code; + + //LOG(ALLB_LOG, LOG_NOTICE, "Light Programming #: %d, on button: %s, color light type: %d\n", val, button->label, typ); + + if (val <= 0) { + use_current_mode = true; + LOG(ALLB_LOG, LOG_INFO, "Light Programming #: %d, on button: %s, color light type: %d, using current mode\n", val, button->label, typ); + } else { + mode_name = light_mode_name(typ, val-1, ALLBUTTON); + use_current_mode = false; + if (mode_name == NULL) { + LOG(ALLB_LOG, LOG_ERR, "Light Programming #: %d, on button: %s, color light type: %d, couldn't find mode name '%s'\n", val, button->label, typ, mode_name); + cleanAndTerminateThread(threadCtrl); + return ptr; + } else { + LOG(ALLB_LOG, LOG_INFO, "Light Programming #: %d, on button: %s, color light type: %d, name '%s'\n", val, button->label, typ, mode_name); + } + } +/* + // Simply turn the light off if value is 0 + if (val <= 0) { + if ( button->led->state == ON ) { + send_cmd(code); + } + cleanAndTerminateThread(threadCtrl); + return ptr; + } +*/ + // Needs to start programming sequence with light off + if ( button->led->state == ON ) { + LOG(ALLB_LOG, LOG_INFO, "Light Programming Initial state on, turning off\n"); + send_cmd(code); + waitfor_queue2empty(); + if ( !waitForMessage(threadCtrl->aq_data, "OFF", 5)) // Message like 'Aux3 Off' + LOG(ALLB_LOG, LOG_ERR, "Light Programming didn't receive OFF message\n"); + } + + // Now turn on and wait for the message "color mode name<>*" + send_cmd(code); + waitfor_queue2empty(); + i=0; + int waitCounter=12; + + do{ + LOG(ALLB_LOG, LOG_INFO,"Light program wait for message\n"); + if ( !waitForMessage(threadCtrl->aq_data, "~*", waitCounter)) + LOG(ALLB_LOG, LOG_ERR, "Light Programming didn't receive color light mode message\n"); + + // Wait for less messages after first try. We get a lot of repeat messages before the one we need. + waitCounter = 3; + + if (use_current_mode) { + LOG(ALLB_LOG, LOG_INFO, "Light Programming using color mode %s\n",aq_data->last_message); + send_cmd(KEY_ENTER); + waitfor_queue2empty(); + break; + } else if (strncasecmp(aq_data->last_message, mode_name, strlen(mode_name)) == 0) { + LOG(ALLB_LOG, LOG_INFO, "Light Programming found color mode %s\n",mode_name); + send_cmd(KEY_ENTER); + waitfor_queue2empty(); + break; + } + + send_cmd(KEY_RIGHT); + waitfor_queue2empty(); + // Just clear current message before waiting for next, since the test in the do can't distinguish + // as both messages end in "~*" + waitForMessage(threadCtrl->aq_data, NULL, 1); + + i++; + } while (i <= LIGHT_COLOR_OPTIONS); + + if (i == LIGHT_COLOR_OPTIONS) { + LOG(ALLB_LOG, LOG_ERR, "Light Programming didn't receive color light mode message for '%s'\n",use_current_mode?"light program":mode_name); + } + + cleanAndTerminateThread(threadCtrl); + + // just stop compiler error, ptr is not valid as it's just been freed + return ptr; +} + + +void *set_allbutton_light_programmode( void *ptr ) +{ + int i; + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_LIGHTPROGRAM_MODE); + + char *buf = (char*)threadCtrl->thread_args; + int val = atoi(&buf[0]); + int btn = atoi(&buf[5]); + int iOn = atoi(&buf[10]); + int iOff = atoi(&buf[15]); + float pmode = atof(&buf[20]); + + if (btn < 0 || btn >= aq_data->total_buttons ) { + LOG(ALLB_LOG, LOG_ERR, "Can't program light mode on button %d\n", btn); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + aqkey *button = &aq_data->aqbuttons[btn]; + unsigned char code = button->code; + + LOG(ALLB_LOG, LOG_INFO, "Light Programming #: %d, on button: %s, with pause mode: %f (initial on=%d, initial off=%d)\n", val, button->label, pmode, iOn, iOff); + + // Simply turn the light off if value is 0 + if (val <= 0) { + if ( button->led->state == ON ) { + send_cmd(code); + } + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + const int seconds = 1000; + // Needs to start programming sequence with light on, if off we need to turn on for 15 seconds + // before we can send the next off. + if ( button->led->state != ON ) { + LOG(ALLB_LOG, LOG_INFO, "Light Programming Initial state off, turning on for %d seconds\n",iOn); + send_cmd(code); + delay(iOn * seconds); + } + + LOG(ALLB_LOG, LOG_INFO, "Light Programming turn off for %d seconds\n",iOff); + // Now need to turn off for between 11 and 14 seconds to reset light. + send_cmd(code); + delay(iOff * seconds); + + // Now light is reset, pulse the appropiate number of times to advance program. + LOG(ALLB_LOG, LOG_INFO, "Light Programming button pulsing on/off %d times\n", val); + + // Program light in safe mode (slowley), or quick mode + if (pmode > 0) { + for (i = 1; i < (val * 2); i++) { + LOG(ALLB_LOG, LOG_INFO, "Light Programming button press number %d - %s of %d\n", i, i % 2 == 0 ? "Off" : "On", val); + send_cmd(code); + waitfor_queue2empty(); + delay(pmode * seconds); // 0.3 works, but using 0.4 to be safe + } + } else { + for (i = 1; i < val; i++) { + const int dt = 0.5; // Time to wait after receiving conformation of light on/off + LOG(ALLB_LOG, LOG_INFO, "Light Programming button press number %d - %s of %d\n", i, "ON", val); + send_cmd(code); + waitForButtonState(aq_data, button, ON, 2); + delay(dt * seconds); + LOG(ALLB_LOG, LOG_INFO, "Light Programming button press number %d - %s of %d\n", i, "OFF", val); + send_cmd(code); + waitForButtonState(aq_data, button, OFF, 2); + delay(dt * seconds); + } + LOG(ALLB_LOG, LOG_INFO, "Finished - Light Programming button press number %d - %s of %d\n", i, "ON", val); + send_cmd(code); + waitfor_queue2empty(); + } + //waitForButtonState(aq_data, &aq_data->aqbuttons[btn], ON, 2); + + cleanAndTerminateThread(threadCtrl); + + // just stop compiler error, ptr is not valid as it's just been freed + return ptr; +} +void *set_allbutton_pool_heater_temps( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + char *name; + char *menu_name; + waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_POOL_HEATER_TEMP); + + int val = atoi((char*)threadCtrl->thread_args); + /* + if (val > HEATER_MAX) { + val = HEATER_MAX; + } else if ( val < MEATER_MIN) { + val = MEATER_MIN; + } + */ + val = setpoint_check(POOL_HTR_SETOINT, val, aq_data); + + // NSF IF in TEMP1 / TEMP2 mode, we need C range of 1 to 40 is 2 to 40 for TEMP1, 1 to 39 TEMP2 + if (isSINGLE_DEV_PANEL){ + name = "TEMP1"; + menu_name = "SET TEMP1"; + } else { + name = "POOL"; + menu_name = "SET POOL TEMP"; + } + + LOG(ALLB_LOG, LOG_DEBUG, "Setting pool heater setpoint to %d\n", val); + //setAqualinkTemp(aq_data, "SET TEMP", "SET POOL TEMP", NULL, "POOL", val); + + if ( select_menu_item(aq_data, "SET TEMP") != true ) { + LOG(ALLB_LOG, LOG_WARNING, "Could not select SET TEMP menu\n"); + LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); + cancel_menu(); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + //if (select_sub_menu_item(aq_data, "SET POOL TEMP") != true) { + if (select_sub_menu_item(aq_data, menu_name) != true) { + LOG(ALLB_LOG, LOG_WARNING, "Could not select SET POOL TEMP menu\n"); + LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); + cancel_menu(); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + if (isSINGLE_DEV_PANEL){ + // Need to get pass this message 'TEMP1 MUST BE SET HIGHER THAN TEMP2' + // and get this message 'TEMP1 20�C ~*' + // Before going to numeric field. + waitForMessage(threadCtrl->aq_data, "MUST BE SET", 5); + send_cmd(KEY_LEFT); + while (stristr(aq_data->last_message, "MUST BE SET") != NULL) { + delay(500); + } + } + + //setAqualinkNumericField(aq_data, "POOL", val); + setAqualinkNumericField(aq_data, name, val); + + // usually miss this message, not sure why, but wait anyway to make sure programming has ended + waitForMessage(threadCtrl->aq_data, "POOL TEMP IS SET TO", 1); + cleanAndTerminateThread(threadCtrl); + + // just stop compiler error, ptr is not valid as it's just been freed + return ptr; +} +void *set_allbutton_spa_heater_temps( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_SPA_HEATER_TEMP); + + int val = atoi((char*)threadCtrl->thread_args); + char *name; + char *menu_name; + /* + if (val > HEATER_MAX) { + val = HEATER_MAX; + } else if ( val < MEATER_MIN) { + val = MEATER_MIN; + }*/ + val = setpoint_check(SPA_HTR_SETOINT, val, aq_data); + + // NSF IF in TEMP1 / TEMP2 mode, we need C range of 1 to 40 is 2 to 40 for TEMP1, 1 to 39 TEMP2 + + if (isSINGLE_DEV_PANEL){ + name = "TEMP2"; + menu_name = "SET TEMP2"; + } else { + name = "SPA"; + menu_name = "SET SPA TEMP"; + } + + LOG(ALLB_LOG, LOG_DEBUG, "Setting spa heater setpoint to %d\n", val); + + //setAqualinkTemp(aq_data, "SET TEMP", "SET SPA TEMP", NULL, "SPA", val); + if ( select_menu_item(aq_data, "SET TEMP") != true ) { + LOG(ALLB_LOG, LOG_WARNING, "Could not select SET TEMP menu\n"); + LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); + cancel_menu(); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + //if (select_sub_menu_item(aq_data, "SET SPA TEMP") != true) { + if (select_sub_menu_item(aq_data, menu_name) != true) { + LOG(ALLB_LOG, LOG_WARNING, "Could not select SET SPA TEMP menu\n"); + LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); + cancel_menu(); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + if (isSINGLE_DEV_PANEL){ + // Need to get pass this message 'TEMP2 MUST BE SET LOWER THAN TEMP1' + // and get this message 'TEMP2 20�C ~*' + // Before going to numeric field. + waitForMessage(threadCtrl->aq_data, "MUST BE SET", 5); + send_cmd(KEY_LEFT); + while (stristr(aq_data->last_message, "MUST BE SET") != NULL) { + delay(500); + } + } + + //setAqualinkNumericField(aq_data, "SPA", val); + setAqualinkNumericField(aq_data, name, val); + + // usually miss this message, not sure why, but wait anyway to make sure programming has ended + waitForMessage(threadCtrl->aq_data, "SPA TEMP IS SET TO", 1); + cleanAndTerminateThread(threadCtrl); + + // just stop compiler error, ptr is not valid as it's just been freed + return ptr; +} + +void *set_allbutton_freeze_heater_temps( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_FRZ_PROTECTION_TEMP); + + int val = atoi((char*)threadCtrl->thread_args); + /* + if (val > FREEZE_PT_MAX) { + val = FREEZE_PT_MAX; + } else if ( val < FREEZE_PT_MIN) { + val = FREEZE_PT_MIN; + } + */ + val = setpoint_check(FREEZE_SETPOINT, val, aq_data); + + LOG(ALLB_LOG, LOG_DEBUG, "Setting sfreeze protection to %d\n", val); + + //setAqualinkTemp(aq_data, "SYSTEM SETUP", "FRZ PROTECT", "TEMP SETTING", "FRZ", val); + if ( select_menu_item(aq_data, "SYSTEM SETUP") != true ) { + LOG(ALLB_LOG, LOG_WARNING, "Could not select SYSTEM SETUP menu\n"); + LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); + cancel_menu(); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + if (select_sub_menu_item(aq_data, "FRZ PROTECT") != true) { + LOG(ALLB_LOG, LOG_WARNING, "Could not select FRZ PROTECT menu\n"); + LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); + cancel_menu(); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + if (select_sub_menu_item(aq_data, "TEMP SETTING") != true) { + LOG(ALLB_LOG, LOG_WARNING, "Could not select TEMP SETTING menu\n"); + LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); + cancel_menu(); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + setAqualinkNumericField(aq_data, "FRZ", val); + + waitForMessage(threadCtrl->aq_data, "FREEZE PROTECTION IS SET TO", 3); + cleanAndTerminateThread(threadCtrl); + + // just stop compiler error, ptr is not valid as it's just been freed + return ptr; +} + +void *set_allbutton_time( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_TIME); + //LOG(ALLB_LOG, LOG_NOTICE, "Setting time on aqualink\n"); + + time_t now = time(0); // get time now + struct tm *result = localtime(&now); + char hour[20]; + + // Add 10 seconds to time since this can take a while to program. + // 10 to 20 seconds whould be right, but since there are no seconds we can set, add 30 seconds to get close to minute. + // Should probably set this to program the next minute then wait before hitting the final enter command. + result->tm_sec += 30; + mktime(result); + + if (result->tm_hour == 0) + sprintf(hour, "HOUR 12 AM"); + else if (result->tm_hour <= 11) + sprintf(hour, "HOUR %d AM", result->tm_hour); // Need to fix compiler warning on new GCC, but %2d or %.2d does NOT give correct string + else if (result->tm_hour == 12) + sprintf(hour, "HOUR 12 PM"); + else // Must be 13 or more + sprintf(hour, "HOUR %d PM", result->tm_hour - 12); + + + LOG(ALLB_LOG, LOG_DEBUG, "Setting time to %d/%d/%d %d:%d\n", result->tm_mon + 1, result->tm_mday, result->tm_year + 1900, result->tm_hour + 1, result->tm_min); + + if ( select_menu_item(aq_data, "SET TIME") != true ) { + LOG(ALLB_LOG, LOG_WARNING, "Could not select SET TIME menu\n"); + LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); + cancel_menu(); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + setAqualinkNumericField(aq_data, "YEAR", result->tm_year + 1900); + setAqualinkNumericField(aq_data, "MONTH", result->tm_mon + 1); + setAqualinkNumericField(aq_data, "DAY", result->tm_mday); + //setAqualinkNumericFieldExtra(aq_data, "HOUR", 11, "PM"); + select_sub_menu_item(aq_data, hour); // This will keep looping until it finds the right message + setAqualinkNumericField(aq_data, "MINUTE", result->tm_min); + + send_cmd(KEY_ENTER); + + cleanAndTerminateThread(threadCtrl); + + // just stop compiler error, ptr is not valid as it's just been freed + return ptr; +} + +void *get_allbutton_diag_model( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_GET_DIAGNOSTICS_MODEL); + + if ( select_menu_item(aq_data, "SYSTEM SETUP") != true ) { + LOG(ALLB_LOG, LOG_WARNING, "Could not select HELP menu\n"); + LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); + cancel_menu(); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + if (select_sub_menu_item(aq_data, "DIAGNOSTICS") != true) { + LOG(ALLB_LOG, LOG_WARNING, "Could not select DIAGNOSTICS menu\n"); + LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); + cancel_menu(); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + waitForMessage(aq_data, NULL, 8); // Receive 8 messages + //8157 REV MMM | BATTERY OK | Cal: -27 0 6 | CONTROL PANEL #1 | CONTROL PANEL #3 | WATER SENSOR OK | AIR SENSOR OK | SOLAR SENSOR OPENED + cleanAndTerminateThread(threadCtrl); + + // just stop compiler error, ptr is not valid as it's just been freed + return ptr; +} + +void *get_allbutton_pool_spa_heater_temps( void *ptr ) +{ +//LOG(ALLB_LOG, LOG_DEBUG, "Could not select TEMP SET menu\n"); + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_GET_POOL_SPA_HEATER_TEMPS); + //LOG(ALLB_LOG, LOG_NOTICE, "Getting pool & spa heat setpoints from aqualink\n"); + + if ( select_menu_item(aq_data, "REVIEW") != true ) { + LOG(ALLB_LOG, LOG_WARNING, "Could not select REVIEW menu\n"); + LOG(ALLB_LOG, LOG_ERR, "Can't get heater setpoints from Control Panel\n"); + cancel_menu(); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + if (select_sub_menu_item(aq_data, "TEMP SET") != true) { + LOG(ALLB_LOG, LOG_WARNING, "Could not select TEMP SET menu\n"); + LOG(ALLB_LOG, LOG_ERR, "Can't get heater setpoints from Control Panel\n"); + cancel_menu(); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + // Should receive 'POOL TEMP IS SET TO xx' then 'SPA TEMP IS SET TO xx' then 'MAINTAIN TEMP IS (OFF|ON)' + // wait for the last message + waitForMessage(threadCtrl->aq_data, "MAINTAIN TEMP IS", 5); + + //cancel_menu(threadCtrl->aq_data); + cleanAndTerminateThread(threadCtrl); + + // just stop compiler error, ptr is not valid as it's just been freed + return ptr; +} + +void *get_allbutton_freeze_protect_temp( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_GET_FREEZE_PROTECT_TEMP); + //LOG(ALLB_LOG, LOG_NOTICE, "Getting freeze protection setpoints\n"); + + if ( select_menu_item(aq_data, "REVIEW") != true ) { + LOG(ALLB_LOG, LOG_WARNING, "Could not select REVIEW menu\n"); + LOG(ALLB_LOG, LOG_ERR, "Can't get freeze setpoints from Control Panel\n"); + cancel_menu(); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + if (select_sub_menu_item(aq_data, "FRZ PROTECT") != true) { + LOG(ALLB_LOG, LOG_WARNING, "Could not select FRZ PROTECT menu\n"); + LOG(ALLB_LOG, LOG_ERR, "Can't get freeze setpoints from Control Panel\n"); + cancel_menu(); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + waitForMessage(aq_data, "FREEZE PROTECTION IS SET TO", 6); // Changed from 3 to wait for multiple items to be listed + //cancel_menu(aq_data); + cleanAndTerminateThread(threadCtrl); + + // just stop compiler error, ptr is not valid as it's just been freed + return ptr; +} + +bool get_aqualink_program_for_button(struct aqualinkdata *aq_data, unsigned char cmd) +{ + int rtnStringsWait = 7; + + if (! waitForMessage(aq_data, "SELECT DEVICE TO REVIEW or PRESS ENTER TO END", rtnStringsWait)) + return false; + + LOG(ALLB_LOG, LOG_DEBUG, "Send key '%d'\n",cmd); + send_cmd(cmd); + return waitForEitherMessage(aq_data, "NOT SET", "TURNS ON", rtnStringsWait); +} + +void *get_allbutton_programs( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + char keys[10] = {KEY_PUMP, KEY_SPA, KEY_AUX1, KEY_AUX2, KEY_AUX3, KEY_AUX4, KEY_AUX5}; // KEY_AUX6 & KEY_AUX7 kill programming mode + + waitForSingleThreadOrTerminate(threadCtrl, AQ_GET_PROGRAMS); + + if ( select_menu_item(aq_data, "REVIEW") != true ) { + //LOG(ALLB_LOG, LOG_WARNING, "Could not select REVIEW menu\n"); + cancel_menu(); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + if (select_sub_menu_item(aq_data, "PROGRAMS") != true) { + //LOG(ALLB_LOG, LOG_WARNING, "Could not select PROGRAMS menu\n"); + cancel_menu(); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + unsigned int i; + + for (i=0; i < strlen(keys); i++) { + //LOG(ALLB_LOG, LOG_DEBUG, "**** START program for key in loop %d\n",i); + if (! get_aqualink_program_for_button(aq_data, keys[i])) { + //LOG(ALLB_LOG, LOG_DEBUG, "**** Didn't find program for key in loop %d\n",i); + //cancel_menu(aq_data); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + //LOG(ALLB_LOG, LOG_DEBUG, "**** Found program for key in loop %d\n",i); + } + + + if (waitForMessage(aq_data, "SELECT DEVICE TO REVIEW or PRESS ENTER TO END", 6)) { + //LOG(ALLB_LOG, LOG_DEBUG, "Send key ENTER\n"); + send_cmd(KEY_ENTER); + } else { + //LOG(ALLB_LOG, LOG_DEBUG, "Send CANCEL\n"); + cancel_menu(); + } + + //cancel_menu(aq_data); + cleanAndTerminateThread(threadCtrl); + + // just stop compiler error, ptr is not valid as it's just been freed + return ptr; +} + +/* +void send_cmd(unsigned char cmd, struct aqualinkdata *aq_data) +{ + push_aq_cmd(cmd); + LOG(ALLB_LOG, LOG_INFO, "Queue send '0x%02hhx' to controller\n", cmd); +} +*/ + + + +void _waitfor_queue2empty(bool longwait) +{ + int i=0; + + if (_allb_pgm_command != NUL) { + LOG(ALLB_LOG, LOG_DEBUG, "Waiting for queue to empty\n"); + } else { + LOG(ALLB_LOG, LOG_DEBUG, "Queue empty!\n"); + return; + } + + while ( (_allb_pgm_command != NUL) && ( i++ < (PROGRAMMING_POLL_COUNTER*(longwait?2:1) ) ) ) { + delay(PROGRAMMING_POLL_DELAY_TIME); + } + + if (_allb_pgm_command != NUL) { + LOG(ALLB_LOG, LOG_WARNING, "Send command Queue did not empty, timeout\n"); + } else { + LOG(ALLB_LOG, LOG_DEBUG, "Queue now empty!\n"); + } + +} + +void waitfor_queue2empty() +{ + _waitfor_queue2empty(false); +} +void longwaitfor_queue2empty() +{ + _waitfor_queue2empty(true); +} + +void send_cmd(unsigned char cmd) +{ + waitfor_queue2empty(); + + _allb_pgm_command = cmd; + //delay(200); + + LOG(ALLB_LOG, LOG_INFO, "Queue send '0x%02hhx' to controller (programming)\n", _allb_pgm_command); +} +void force_queue_delete() +{ + if (_allb_pgm_command != NUL) + LOG(ALLB_LOG, LOG_INFO, "Really bad coding, don't use this in release\n"); + + _allb_pgm_command = NUL; +} + + +void cancel_menu() +{ + send_cmd(KEY_CANCEL); +} + +/* +* added functionality, if start of string is ^ use that as must start with in comparison +*/ + +bool waitForEitherMessage(struct aqualinkdata *aq_data, char* message1, char* message2, int numMessageReceived) +{ + //LOG(ALLB_LOG, LOG_DEBUG, "waitForMessage %s %d %d\n",message,numMessageReceived,cmd); + waitfor_queue2empty(); // MAke sure the last command was sent + int i=0; + pthread_mutex_lock(&aq_data->active_thread.thread_mutex); + char* msgS1 = ""; + char* msgS2 = ""; + char* ptr = NULL; + + + if (message1 != NULL) { + if (message1[0] == '^') + msgS1 = &message1[1]; + else + msgS1 = message1; + } + if (message2 != NULL) { + if (message2[0] == '^') + msgS2 = &message2[1]; + else + msgS2 = message2; + } + + while( ++i <= numMessageReceived) + { + LOG(ALLB_LOG, LOG_DEBUG, "loop %d of %d looking for '%s' OR '%s' received message1 '%s'\n",i,numMessageReceived,message1,message2,aq_data->last_message); + + if (message1 != NULL) { + ptr = stristr(aq_data->last_message, msgS1); + if (ptr != NULL) { // match + LOG(ALLB_LOG, LOG_DEBUG, "String MATCH '%s'\n", msgS1); + if (msgS1 == message1) // match & don't care if first char + break; + else if (ptr == aq_data->last_message) // match & do care if first char + break; + } + } + if (message2 != NULL) { + ptr = stristr(aq_data->last_message, msgS2); + if (ptr != NULL) { // match + LOG(ALLB_LOG, LOG_DEBUG, "String MATCH '%s'\n", msgS2); + if (msgS2 == message2) // match & don't care if first char + break; + else if (ptr == aq_data->last_message) // match & do care if first char + break; + } + } + + //LOG(ALLB_LOG, LOG_DEBUG, "looking for '%s' received message1 '%s'\n",message1,aq_data->last_message); + pthread_cond_wait(&aq_data->active_thread.thread_cond, &aq_data->active_thread.thread_mutex); + //LOG(ALLB_LOG, LOG_DEBUG, "loop %d of %d looking for '%s' received message1 '%s'\n",i,numMessageReceived,message1,aq_data->last_message); + } + + pthread_mutex_unlock(&aq_data->active_thread.thread_mutex); + + if (message1 != NULL && message2 != NULL && ptr == NULL) { + //logmessage1(LOG_ERR, "Could not select MENU of Aqualink control panel\n"); + LOG(ALLB_LOG, LOG_ERR, "Did not find '%s'\n",message1); + return false; + } + LOG(ALLB_LOG, LOG_DEBUG, "Found message1 '%s' or '%s' in '%s'\n",message1,message2,aq_data->last_message); + + return true; +} + + + +bool waitForMessage(struct aqualinkdata *aq_data, char* message, int numMessageReceived) +{ + LOG(ALLB_LOG, LOG_DEBUG, "waitForMessage %s %d\n",message,numMessageReceived); + // NSF Need to come back to this, as it stops on test enviornment but not real panel, so must be speed related. + //waitfor_queue2empty(); // MAke sure the last command was sent + + int i=0; + pthread_mutex_lock(&aq_data->active_thread.thread_mutex); + char* msgS; + char* ptr = NULL; + + if (message != NULL) { + if (message[0] == '^') + msgS = &message[1]; + else + msgS = message; + } + + while( ++i <= numMessageReceived) + { + if (message != NULL) + LOG(ALLB_LOG, LOG_DEBUG, "loop %d of %d looking for '%s', last message received '%s'\n",i,numMessageReceived,message,aq_data->last_message); + else + LOG(ALLB_LOG, LOG_DEBUG, "loop %d of %d waiting for next message, last message received '%s'\n",i,numMessageReceived,aq_data->last_message); + + if (message != NULL) { + ptr = stristr(aq_data->last_message, msgS); + if (ptr != NULL) { // match + LOG(ALLB_LOG, LOG_DEBUG, "String MATCH\n"); + if (msgS == message) // match & don't care if first char + break; + else if (ptr == aq_data->last_message) // match & do care if first char + break; + } + } + + //LOG(ALLB_LOG, LOG_DEBUG, "looking for '%s' received message '%s'\n",message,aq_data->last_message); + //LOG(ALLB_LOG, LOG_DEBUG, "*** pthread_cond_wait() sleep\n"); + pthread_cond_wait(&aq_data->active_thread.thread_cond, &aq_data->active_thread.thread_mutex); + //LOG(ALLB_LOG, LOG_DEBUG, "*** pthread_cond_wait() wake\n"); + //LOG(ALLB_LOG, LOG_DEBUG, "loop %d of %d looking for '%s' received message '%s'\n",i,numMessageReceived,message,aq_data->last_message); + } + + pthread_mutex_unlock(&aq_data->active_thread.thread_mutex); + + if (message != NULL && ptr == NULL) { + //LOG(ALLB_LOG, LOG_ERR, "Could not select MENU of Aqualink control panel\n"); + LOG(ALLB_LOG, LOG_DEBUG, "did not find '%s'\n",message); + return false; + } else if (message != NULL) + LOG(ALLB_LOG, LOG_DEBUG, "found message '%s' in '%s'\n",message,aq_data->last_message); + else + LOG(ALLB_LOG, LOG_DEBUG, "waited for %d message(s)\n",numMessageReceived); + + return true; +} + +bool select_menu_item(struct aqualinkdata *aq_data, char* item_string) +{ + char* expectedMsg = "PRESS ENTER* TO SELECT"; + //char* expectedMsg = "PROGRAM"; + int wait_messages = 5; + bool found = false; + int tries = 0; + // Select the MENU and wait to get the RS8 respond. + + while (found == false && tries <= 3) { + send_cmd(KEY_MENU); + found = waitForMessage(aq_data, expectedMsg, wait_messages); + tries++; + } + + if (found == false) + return false; + + // NSF This isn't needed and seems to cause issue on some controllers. + //send_cmd(KEY_ENTER, aq_data); + //waitForMessage(aq_data, NULL, 1); + + return select_sub_menu_item(aq_data, item_string); +} +/* +bool select_menu_item(struct aqualinkdata *aq_data, char* item_string) +{ + char* expectedMsg = "PRESS ENTER* TO SELECT"; + int wait_messages = 6; + //int i=0; + + // Select the MENU and wait to get the RS8 respond. + send_cmd(KEY_MENU, aq_data); + + if (waitForMessage(aq_data, expectedMsg, wait_messages) == false) + return false; + + send_cmd(KEY_ENTER, aq_data); + waitForMessage(aq_data, NULL, 1); + + // Blindly wait for next message + //sendCmdWaitForReturn(aq_data, KEY_ENTER); + // Can't determin the first response + //delay(500); + + return select_sub_menu_item(aq_data, item_string); +} +*/ + +//bool select_sub_menu_item(char* item_string, struct aqualinkdata *aq_data) +bool select_sub_menu_item(struct aqualinkdata *aq_data, char* item_string) +{ + int wait_messages = 28; + int i=0; + + waitfor_queue2empty(); + + while( (stristr(aq_data->last_message, item_string) == NULL) && ( i++ < wait_messages) ) + { + LOG(ALLB_LOG, LOG_DEBUG, "Find item in Menu: loop %d of %d looking for '%s' received message '%s'\n",i,wait_messages,item_string,aq_data->last_message); + send_cmd(KEY_RIGHT); + waitfor_queue2empty(); // ADDED BACK MAY 2023 setting time warked better + //waitForMessage(aq_data, NULL, 1); + waitForMessage(aq_data, item_string, 1); + } + + if (stristr(aq_data->last_message, item_string) == NULL) { + LOG(ALLB_LOG, LOG_ERR, "Could not find menu item '%s'\n",item_string); + return false; + } + + LOG(ALLB_LOG, LOG_DEBUG, "Find item in Menu: loop %d of %d FOUND menu item '%s', sending ENTER command\n",i,wait_messages, item_string); + // Enter the mode specified by the argument. + + + send_cmd(KEY_ENTER); + + + waitForMessage(aq_data, NULL, 1); + + + //sendCmdWaitForReturn(aq_data, KEY_ENTER); + + return true; + +} + +// NSF Need to test this, then use it for the color change mode. + +bool waitForButtonState(struct aqualinkdata *aq_data, aqkey* button, aqledstate state, int numMessageReceived) +{ + //LOG(ALLB_LOG, LOG_DEBUG, "waitForMessage %s %d %d\n",message,numMessageReceived,cmd); + int i=0; + pthread_mutex_lock(&aq_data->active_thread.thread_mutex); + + while( ++i <= numMessageReceived) + { + LOG(ALLB_LOG, LOG_DEBUG, "loop %d of %d looking for state change to '%d' for '%s' \n",i,numMessageReceived,button->led->state,button->name); + + if (button->led->state == state) { + LOG(ALLB_LOG, LOG_INFO, "Button State =%d for %s\n",state,button->name); + break; + } + + //LOG(ALLB_LOG, LOG_DEBUG, "looking for '%s' received message '%s'\n",message,aq_data->last_message); + pthread_cond_wait(&aq_data->active_thread.thread_cond, &aq_data->active_thread.thread_mutex); + //LOG(ALLB_LOG, LOG_DEBUG, "loop %d of %d looking for '%s' received message '%s'\n",i,numMessageReceived,message,aq_data->last_message); + } + + pthread_mutex_unlock(&aq_data->active_thread.thread_mutex); + + if (numMessageReceived >= i) { + //LOG(ALLB_LOG, LOG_ERR, "Could not select MENU of Aqualink control panel\n"); + LOG(ALLB_LOG, LOG_DEBUG, "did not find state '%d' for '%s'\n",button->led->state,button->name); + return false; + } + LOG(ALLB_LOG, LOG_DEBUG, "found state '%d' for '%s'\n",button->led->state,button->name); + + return true; +} \ No newline at end of file diff --git a/allbutton_aq_programmer.h b/allbutton_aq_programmer.h new file mode 100644 index 0000000..e41f5f2 --- /dev/null +++ b/allbutton_aq_programmer.h @@ -0,0 +1,26 @@ +#ifndef ALLBUTTON_PROGRAMMER_H_ +#define ALLBUTTON_PROGRAMMER_H_ + + +void *set_allbutton_pool_heater_temps( void *ptr ); +void *set_allbutton_spa_heater_temps( void *ptr ); +void *set_allbutton_freeze_heater_temps( void *ptr ); +void *set_allbutton_time( void *ptr ); +void *get_allbutton_pool_spa_heater_temps( void *ptr ); +void *get_allbutton_programs( void *ptr ); +void *get_allbutton_freeze_protect_temp( void *ptr ); +void *get_allbutton_diag_model( void *ptr ); +void *get_allbutton_aux_labels( void *ptr ); +//void *threadded_send_cmd( void *ptr ); +void *set_allbutton_light_programmode( void *ptr ); +void *set_allbutton_light_colormode( void *ptr ); +void *set_allbutton_SWG( void *ptr ); +void *set_allbutton_boost( void *ptr ); + +unsigned char pop_allb_cmd(struct aqualinkdata *aq_data); + + + +void aq_send_allb_cmd(unsigned char cmd); + +#endif //ALLBUTTON_PROGRAMMER_H_ \ No newline at end of file diff --git a/aq_panel.c b/aq_panel.c index 011bc9e..6a2154a 100644 --- a/aq_panel.c +++ b/aq_panel.c @@ -23,6 +23,7 @@ #include "aq_panel.h" #include "serialadapter.h" #include "aq_timer.h" +#include "allbutton_aq_programmer.h" void initPanelButtons(struct aqualinkdata *aqdata, bool rspda, int size, bool combo, bool dual); void programDeviceLightMode(struct aqualinkdata *aqdata, int value, int button); @@ -627,20 +628,22 @@ bool setDeviceState(struct aqualinkdata *aqdata, int deviceIndex, bool isON, req // since allbutton (default) is stateless, and rssaadapter is statefull, use rssaadapter for any domoricz requests set_aqualink_rssadapter_aux_state(deviceIndex, isON); } else { - aq_send_cmd(button->code); + aq_send_allb_cmd(button->code); } // Pre set device to state, next status will correct if state didn't take, but this will stop multiple ON messages setting on/off -#ifdef PRESTATE_ONOFF - if ((button->code == KEY_POOL_HTR || button->code == KEY_SPA_HTR || - button->code == KEY_SOLAR_HTR) && - isON > 0) { - button->led->state = ENABLE; // if heater and set to on, set pre-status to enable. +//#ifdef PRESTATE_ONOFF + if (_aqconfig_.device_pre_state) { + if ((button->code == KEY_POOL_HTR || button->code == KEY_SPA_HTR || + button->code == KEY_SOLAR_HTR) && + isON > 0) { + button->led->state = ENABLE; // if heater and set to on, set pre-status to enable. //_aqualink_data->updated = true; - } else if (isRSSA_ENABLED || ((button->special_mask & PROGRAM_LIGHT) != PROGRAM_LIGHT)) { - button->led->state = (isON == false ? OFF : ON); // as long as it's not programmable light , pre-set to on/off + } else if (isRSSA_ENABLED || ((button->special_mask & PROGRAM_LIGHT) != PROGRAM_LIGHT)) { + button->led->state = (isON == false ? OFF : ON); // as long as it's not programmable light , pre-set to on/off //_aqualink_data->updated = true; + } } -#endif +//#endif } } return TRUE; @@ -781,114 +784,7 @@ bool panel_device_request(struct aqualinkdata *aqdata, action_type type, int dev return TRUE; } -/* -bool create_panel_request(struct aqualinkdata *aqdata, netRequest requester, int buttonIndex, int value, bool timer) { - - // if value = 0 is OFF, - // if value = 1 is ON if (timer = false). - // if value > 0 (timer should be true, and vaue is duration). - if ((_aqualink_data->aqbuttons[buttonIndex].led->state == OFF && value == 0) || - (value > 0 && (_aqualink_data->aqbuttons[buttonIndex].led->state == ON || _aqualink_data->aqbuttons[buttonIndex].led->state == FLASH || - _aqualink_data->aqbuttons[buttonIndex].led->state == ENABLE))) { - LOG(NET_LOG, LOG_INFO, "%s: received '%s' for '%s', already '%s', Ignoring\n", actionName[requester], (value == 0 ? "OFF" : "ON"), _aqualink_data->aqbuttons[buttonIndex].name, (value == 0 ? "OFF" : "ON")); - //return false; - } else { - LOG(NET_LOG, LOG_INFO, "%s: received '%s' for '%s', turning '%s'\n", actionName[requester], (value == 0 ? "OFF" : "ON"), _aqualink_data->aqbuttons[buttonIndex].name, (value == 0 ? "OFF" : "ON")); -#ifdef AQ_PDA - if (isPDA_PANEL) { - char msg[PTHREAD_ARG]; - sprintf(msg, "%-5d%-5d", buttonIndex, (value == 0 ? OFF : ON)); - aq_programmer(AQ_PDA_DEVICE_ON_OFF, msg, _aqualink_data); - } else -#endif - { - // Check for panel programmable light. if so simple ON isn't going to work well - // Could also add "light mode" check, as this is only valid for panel configured light not aqualinkd configured light. - if ((_aqualink_data->aqbuttons[buttonIndex].special_mask & PROGRAM_LIGHT) == PROGRAM_LIGHT && _aqualink_data->aqbuttons[buttonIndex].led->state == OFF) { - // OK Programable light, and no light mode selected. Now let's work out best way to turn it on. serial_adapter protocol will to it without questions, - // all other will require programmig. - if (isRSSA_ENABLED) { - set_aqualink_rssadapter_aux_state(buttonIndex, true); - } else { - set_light_mode("0", buttonIndex); // 0 means use current light mode - } - } else { - aq_send_cmd(_aqualink_data->aqbuttons[buttonIndex].code); - } -// Pre set device to state, next status will correct if state didn't take, but this will stop multiple ON messages setting on/off -#ifdef PRESTATE_ONOFF - if ((_aqualink_data->aqbuttons[buttonIndex].code == KEY_POOL_HTR || _aqualink_data->aqbuttons[buttonIndex].code == KEY_SPA_HTR || - _aqualink_data->aqbuttons[buttonIndex].code == KEY_SOLAR_HTR) && - value > 0) { - _aqualink_data->aqbuttons[buttonIndex].led->state = ENABLE; // if heater and set to on, set pre-status to enable. - //_aqualink_data->updated = true; - } else if (isRSSA_ENABLED || ((_aqualink_data->aqbuttons[buttonIndex].special_mask & PROGRAM_LIGHT) != PROGRAM_LIGHT)) { - _aqualink_data->aqbuttons[buttonIndex].led->state = (value == 0 ? OFF : ON); // as long as it's not programmable light , pre-set to on/off - //_aqualink_data->updated = true; - } -#endif - } - } - - // If it's a timer, start the timer - if (timer) { - start_timer(_aqualink_data, &_aqualink_data->aqbuttons[buttonIndex], value); - } - - return true; -} - -void create_program_request(netRequest requester, action_type type, int value, int id) // id is only valid for PUMP RPM -{ - if (_aqualink_data->unactioned.type != NO_ACTION && type != _aqualink_data->unactioned.type) - LOG(NET_LOG,LOG_ERR, "%s: About to overwrite unactioned panel program\n",actionName[requester]); - - if (type == POOL_HTR_SETOINT || type == SPA_HTR_SETOINT || type == FREEZE_SETPOINT || type == SWG_SETPOINT ) { - _aqualink_data->unactioned.value = setpoint_check(type, value, _aqualink_data); - if (value != _aqualink_data->unactioned.value) - LOG(NET_LOG,LOG_NOTICE, "%s: requested setpoint value %d is invalid, change to %d\n",actionName[requester], value, _aqualink_data->unactioned.value); - } else if (type == PUMP_RPM) { - //_aqualink_data->unactioned.value = RPM_check(_aqualink_data->pumps[id].pumpType , value, _aqualink_data); - _aqualink_data->unactioned.value = value; - //if (value != _aqualink_data->unactioned.value) - // LOG(NET_LOG,LOG_NOTICE, "%s: requested Pump value %d is invalid, change to %d\n",actionName[requester], value, _aqualink_data->unactioned.value); - } else if (type == PUMP_VSPROGRAM) { - //_aqualink_data->unactioned.value = value; - //if (value != _aqualink_data->unactioned.value) - LOG(NET_LOG,LOG_ERR, "%s: requested Pump vsp program is not implimented yet\n",actionName[requester], value, _aqualink_data->unactioned.value); - } else { - // SWG_BOOST & PUMP_RPM - _aqualink_data->unactioned.value = value; - } - - _aqualink_data->unactioned.type = type; - _aqualink_data->unactioned.id = id; // This is only valid for pump. - - if (requester == NET_MQTT) // We can get multiple MQTT requests from some, so this will wait for last one to come in. - time(&_aqualink_data->unactioned.requested); - else - _aqualink_data->unactioned.requested = 0; - - -} - -#ifdef AQ_PDA -void create_PDA_on_off_request(aqkey *button, bool isON) -{ - int i; - char msg[PTHREAD_ARG]; - - for (i=0; i < _aqualink_data->total_buttons; i++) { - if (_aqualink_data->aqbuttons[i].code == button->code) { - sprintf(msg, "%-5d%-5d", i, (isON? ON : OFF)); - aq_programmer(AQ_PDA_DEVICE_ON_OFF, msg, _aqualink_data); - break; - } - } -} -#endif -*/ diff --git a/aq_programmer.c b/aq_programmer.c index fa01a58..b9d9f0c 100644 --- a/aq_programmer.c +++ b/aq_programmer.c @@ -26,6 +26,7 @@ #include "utils.h" #include "aq_programmer.h" #include "aq_serial.h" +#include "allbutton_aq_programmer.h" #ifdef AQ_PDA #include "pda.h" @@ -46,185 +47,72 @@ #include "timespec_subtract.h" #endif -bool select_sub_menu_item(struct aqualinkdata *aq_data, char* item_string); -bool select_menu_item(struct aqualinkdata *aq_data, char* item_string); -//void send_cmd(unsigned char cmd, struct aqualinkdata *aq_data); -void cancel_menu(); - - -void *set_aqualink_pool_heater_temps( void *ptr ); -void *set_aqualink_spa_heater_temps( void *ptr ); -void *set_aqualink_freeze_heater_temps( void *ptr ); -void *set_aqualink_time( void *ptr ); -void *get_aqualink_pool_spa_heater_temps( void *ptr ); -void *get_aqualink_programs( void *ptr ); -void *get_freeze_protect_temp( void *ptr ); -void *get_aqualink_diag_model( void *ptr ); -void *get_aqualink_aux_labels( void *ptr ); -//void *threadded_send_cmd( void *ptr ); -void *set_aqualink_light_programmode( void *ptr ); -void *set_aqualink_light_colormode( void *ptr ); -void *set_aqualink_SWG( void *ptr ); -void *set_aqualink_boost( void *ptr ); -/* -void *set_aqualink_onetouch_pump_rpm( void *ptr ); -void *set_aqualink_onetouch_macro( void *ptr ); -void *get_aqualink_onetouch_setpoints( void *ptr ); -*/ - -//void *get_aqualink_PDA_device_status( void *ptr ); -//void *set_aqualink_PDA_device_on_off( void *ptr ); - -bool waitForButtonState(struct aqualinkdata *aq_data, aqkey* button, aqledstate state, int numMessageReceived); - -//bool waitForMessage(struct aqualinkdata *aq_data, char* message, int numMessageReceived); -bool waitForEitherMessage(struct aqualinkdata *aq_data, char* message1, char* message2, int numMessageReceived); - -bool push_aq_cmd(unsigned char cmd); -//void waitfor_queue2empty(); -//void longwaitfor_queue2empty(); - void _aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_data, bool allowOveride); -#define MAX_STACK 20 -int _stack_place = 0; -unsigned char _commands[MAX_STACK]; -//unsigned char pgm_commands[MAX_STACK]; -unsigned char _pgm_command = NUL; -//unsigned char _ot_pgm_command = NUL; - -bool _last_sent_was_cmd = false; - -// External view of adding to queue -void aq_send_cmd(unsigned char cmd) { - push_aq_cmd(cmd); -} - -/* -void ot_send_cmd(unsigned char cmd) { - _ot_pgm_command = cmd; -} - -unsigned char pop_ot_cmd(unsigned char receive_type) -{ - unsigned char cmd = NUL; - - if (receive_type == CMD_STATUS) { - cmd = _ot_pgm_command; - _ot_pgm_command = NUL; - } - - return cmd; -} -*/ -bool push_aq_cmd(unsigned char cmd) { - - //LOG(PROG_LOG, LOG_NOTICE, "push_aq_cmd '0x%02hhx'\n", cmd); - - if (_stack_place < MAX_STACK) { - _commands[_stack_place] = cmd; - _stack_place++; - } else { - LOG(PROG_LOG, LOG_ERR, "Command queue overflow, too many unsent commands to RS control panel\n"); - return false; - } - - return true; -} - -int get_aq_cmd_length() -{ - return _stack_place; -} - -int _expectNextMessage = 0; -unsigned char _last_sent_command = NUL; - -unsigned char pop_aq_cmd(struct aqualinkdata *aq_data) -{ - unsigned char cmd = NUL; - - if ( _expectNextMessage > 0 && - (aq_data->last_packet_type == CMD_MSG || aq_data->last_packet_type == CMD_MSG_LONG || aq_data->last_packet_type == CMD_MSG_LOOP_ST)) - { - _expectNextMessage=0; - } else if (_expectNextMessage > 3) { - // NSF Should probably check this is a status command AND we are in programming mode. - LOG(PROG_LOG, LOG_ERR, "Did not receive expected reply from last RS SEND command, resending '0x%02hhx'\n", _last_sent_command); - _expectNextMessage=0; - return _last_sent_command; - } else if (_expectNextMessage > 0) { - _expectNextMessage++; - } - - // Only send commands on status messages - // Are we in programming mode and it's not ONETOUCH programming mode - if (in_programming_mode(aq_data) && ( in_ot_programming_mode(aq_data) == false && in_iaqt_programming_mode(aq_data) == false )) { - //if (aq_data->active_thread.thread_id != 0) { - if ( _pgm_command != NUL && aq_data->last_packet_type == CMD_STATUS) { - cmd = _pgm_command; - _pgm_command = NUL; - LOG(PROG_LOG, LOG_DEBUG_SERIAL, "RS SEND cmd '0x%02hhx' (programming)\n", cmd); - } else if (_pgm_command != NUL) { - LOG(PROG_LOG, LOG_DEBUG_SERIAL, "RS Waiting to send cmd '0x%02hhx' (programming)\n", _pgm_command); - } else { - LOG(PROG_LOG, LOG_DEBUG_SERIAL, "RS SEND cmd '0x%02hhx' empty queue (programming)\n", cmd); - } - } else if (_stack_place > 0 && aq_data->last_packet_type == CMD_STATUS ) { - cmd = _commands[0]; - _stack_place--; - LOG(PROG_LOG, LOG_DEBUG_SERIAL, "RS SEND cmd '0x%02hhx'\n", cmd); - //LOG(PROG_LOG, LOG_NOTICE, "pop_cmd '0x%02hhx'\n", cmd); - memmove(&_commands[0], &_commands[1], sizeof(unsigned char) * _stack_place ) ; - } else { - LOG(PROG_LOG, LOG_DEBUG_SERIAL, "RS SEND cmd '0x%02hhx'\n", cmd); - } - -//printf("RSM sending cmd '0x%02hhx' in reply to '0x%02hhx'\n",cmd,aq_data->last_packet_type); - - if (cmd == KEY_ENTER || cmd == KEY_RIGHT || cmd == KEY_LEFT || cmd == KEY_MENU ) { //KEY_CANCEL KEY_HOLD KEY_OVERRIDE - _expectNextMessage=1; - _last_sent_command = cmd; - } - - return cmd; -} - +// Lookup table for programming function to protocal we will use for programming panel +// Should be one function per program_type enum. /* - -unsigned char pop_aq_cmd_OLD(struct aqualinkdata *aq_data) -{ - unsigned char cmd = NUL; - // Only send commands on status messages - // Are we in programming mode - if (aq_data->active_thread.thread_id != 0) { - if ( (_pgm_command == KEY_MENU && aq_data->last_packet_type == CMD_STATUS) || - // Need to not the key_menu below - ( _pgm_command != NUL && (aq_data->last_packet_type == CMD_STATUS || aq_data->last_packet_type == CMD_MSG_LONG) )) { - cmd = _pgm_command; - _pgm_command = NUL; - LOG(PROG_LOG, LOG_DEBUG, "RS SEND cmd '0x%02hhx' (programming)\n", cmd); - } else if (_pgm_command != NUL) { - LOG(PROG_LOG, LOG_DEBUG, "RS Waiting to send cmd '0x%02hhx' (programming)\n", _pgm_command); - } else { - LOG(PROG_LOG, LOG_DEBUG, "RS SEND cmd '0x%02hhx' empty queue (programming)\n", cmd); - } - } else if (_stack_place > 0 && aq_data->last_packet_type == CMD_STATUS ) { - cmd = _commands[0]; - _stack_place--; - LOG(PROG_LOG, LOG_DEBUG, "RS SEND cmd '0x%02hhx'\n", cmd); - memmove(&_commands[0], &_commands[1], sizeof(unsigned char) * _stack_place ) ; - } else { - LOG(PROG_LOG, LOG_DEBUG, "RS SEND cmd '0x%02hhx'\n", cmd); - } - -//printf("RSM sending cmd '0x%02hhx' in reply to '0x%02hhx'\n",cmd,aq_data->last_packet_type); - - return cmd; -} + static void * (*_prog_functions[])(void *ptr) = { */ - +typedef void *(*func_ptr)(void *ptr); + +const func_ptr _prog_functions[AQP_RSSADAPTER_MAX] = { + [AQ_GET_POOL_SPA_HEATER_TEMPS] = get_allbutton_pool_spa_heater_temps, + [AQ_GET_FREEZE_PROTECT_TEMP] = get_allbutton_freeze_protect_temp, + [AQ_SET_TIME] = set_allbutton_time, + [AQ_SET_POOL_HEATER_TEMP] = set_allbutton_pool_heater_temps, + [AQ_SET_SPA_HEATER_TEMP] = set_allbutton_spa_heater_temps, + [AQ_SET_FRZ_PROTECTION_TEMP] = set_allbutton_freeze_heater_temps, + [AQ_GET_DIAGNOSTICS_MODEL] = get_allbutton_diag_model, + [AQ_GET_PROGRAMS] = get_allbutton_programs, + [AQ_SET_LIGHTPROGRAM_MODE] = set_allbutton_light_programmode, + [AQ_SET_LIGHTCOLOR_MODE] = set_allbutton_light_colormode, + [AQ_SET_SWG_PERCENT] = set_allbutton_SWG, + [AQ_GET_AUX_LABELS] = get_allbutton_aux_labels, + [AQ_SET_BOOST] = set_allbutton_boost, + [AQ_SET_ONETOUCH_PUMP_RPM] = set_aqualink_onetouch_pump_rpm, + [AQ_GET_ONETOUCH_FREEZEPROTECT] = get_aqualink_onetouch_freezeprotect, + [AQ_GET_ONETOUCH_SETPOINTS] = get_aqualink_onetouch_setpoints, + [AQ_SET_ONETOUCH_TIME] = set_aqualink_onetouch_time, + [AQ_SET_ONETOUCH_BOOST] = set_aqualink_onetouch_boost, + [AQ_SET_ONETOUCH_SWG_PERCENT] = set_aqualink_onetouch_swg_percent, + [AQ_SET_ONETOUCH_POOL_HEATER_TEMP]= set_aqualink_onetouch_pool_heater_temp, + [AQ_SET_ONETOUCH_SPA_HEATER_TEMP] = set_aqualink_onetouch_spa_heater_temp, + [AQ_SET_ONETOUCH_FREEZEPROTECT] = set_aqualink_onetouch_freezeprotect, + [AQ_SET_IAQTOUCH_PUMP_RPM] = set_aqualink_iaqtouch_pump_rpm, + [AQ_GET_IAQTOUCH_VSP_ASSIGNMENT] = set_aqualink_iaqtouch_vsp_assignments, + [AQ_GET_IAQTOUCH_SETPOINTS] = get_aqualink_iaqtouch_setpoints, + [AQ_GET_IAQTOUCH_FREEZEPROTECT] = get_aqualink_iaqtouch_freezeprotect, + [AQ_GET_IAQTOUCH_AUX_LABELS] = get_aqualink_iaqtouch_aux_labels, + [AQ_SET_IAQTOUCH_SWG_PERCENT] = set_aqualink_iaqtouch_swg_percent, + [AQ_SET_IAQTOUCH_SWG_BOOST] = set_aqualink_iaqtouch_swg_boost, + [AQ_SET_IAQTOUCH_POOL_HEATER_TEMP]= set_aqualink_iaqtouch_pool_heater_temp, + [AQ_SET_IAQTOUCH_SPA_HEATER_TEMP] = set_aqualink_iaqtouch_spa_heater_temp, + [AQ_SET_IAQTOUCH_SET_TIME] = set_aqualink_iaqtouch_time, + [AQ_SET_IAQTOUCH_PUMP_VS_PROGRAM] = set_aqualink_iaqtouch_pump_vs_program, + [AQ_SET_IAQTOUCH_LIGHTCOLOR_MODE] = set_aqualink_iaqtouch_light_colormode, + [AQ_SET_IAQTOUCH_DEVICE_ON_OFF] = set_aqualink_iaqtouch_device_on_off, + [AQ_PDA_INIT] = set_aqualink_PDA_init, + [AQ_PDA_WAKE_INIT] = set_aqualink_PDA_wakeinit, + [AQ_PDA_DEVICE_STATUS] = get_aqualink_PDA_device_status, + [AQ_PDA_DEVICE_ON_OFF] = set_aqualink_PDA_device_on_off, + [AQ_PDA_SET_BOOST] = set_PDA_aqualink_boost, + [AQ_PDA_SET_SWG_PERCENT] = set_PDA_aqualink_SWG_setpoint, + [AQ_PDA_GET_AUX_LABELS] = get_PDA_aqualink_aux_labels, + [AQ_PDA_SET_POOL_HEATER_TEMPS] = set_aqualink_PDA_pool_heater_temps, + [AQ_PDA_SET_SPA_HEATER_TEMPS] = set_aqualink_PDA_spa_heater_temps, + [AQ_PDA_SET_FREEZE_PROTECT_TEMP] = set_aqualink_PDA_freeze_protectsetpoint, + [AQ_PDA_SET_TIME] = set_PDA_aqualink_time, + //[AQ_PDA_GET_POOL_SPA_HEATER_TEMPS]= get_aqualink_PDA_pool_spa_heater_temps, + [AQ_PDA_GET_FREEZE_PROTECT_TEMP] = get_PDA_aqualink_pool_spa_heater_temps + /* + [AQ_PDA_SET_BOOST] = set_PDA_aqualink_boost + [AQ_PDA_SET_SWG_PERCENT] = set_PDA_aqualink_SWG_setpoint + [AQ_PDA_GET_AUX_LABELS] = get_PDA_aqualink_aux_labels + */ + +}; int roundTo(int num, int denominator) { @@ -416,61 +304,7 @@ void queueGetProgramData(emulation_type source_type, struct aqualinkdata *aq_dat } } -void queueGetProgramData_OLD(emulation_type source_type, struct aqualinkdata *aq_data) -{ - // Wait for onetouch if enabeled. - //if ( source_type == ALLBUTTON && ( onetouch_enabled() == false || extended_device_id_programming() == false ) ) { - if ( source_type == RSSADAPTER ) { - aq_programmer(AQ_GET_RSSADAPTER_SETPOINTS, NULL, aq_data); - } else if ( source_type == ALLBUTTON && ( isEXTP_ENABLED == false || (isONET_ENABLED == false && isIAQT_ENABLED == false)) ) { - aq_send_cmd(NUL); - if (!isRSSA_ENABLED) { - aq_programmer(AQ_GET_POOL_SPA_HEATER_TEMPS, NULL, aq_data); - } - aq_programmer(AQ_GET_FREEZE_PROTECT_TEMP, NULL, aq_data); - if (_aqconfig_.use_panel_aux_labels) { - aq_programmer(AQ_GET_AUX_LABELS, NULL, aq_data); - } -#ifdef AQ_ONETOUCH - } else if ( source_type == ONETOUCH) { - if (!isRSSA_ENABLED) { - aq_programmer(AQ_GET_ONETOUCH_FREEZEPROTECT, NULL, aq_data); - } else { - aq_programmer(AQ_GET_ONETOUCH_SETPOINTS, NULL, aq_data); // This get's freeze & heaters, we should just get freeze if isRSSA_ENABLED - } - if (_aqconfig_.use_panel_aux_labels) - aq_programmer(AQ_GET_AUX_LABELS, NULL, aq_data); -#endif -#ifdef AQ_IAQTOUCH - } else if ( source_type == IAQTOUCH) { - //if (!isRSSA_ENABLED) - aq_programmer(AQ_GET_IAQTOUCH_SETPOINTS, NULL, aq_data); // This get's freeze & heaters, we should just get freeze if isRSSA_ENABLED - if (_aqconfig_.use_panel_aux_labels) - aq_programmer(AQ_GET_AUX_LABELS, NULL, aq_data); - //aq_programmer(AQ_GET_IAQTOUCH_AUX_LABELS, NULL, aq_data); // This is not working yet -#endif -#ifdef AQ_PDA - } else if ( source_type == AQUAPDA) { - aq_programmer(AQ_PDA_INIT, NULL, aq_data); -#endif - } -} -/* -void queueGetProgramData(emulation_type source_type, struct aqualinkdata *aq_data) -{ - queueGetExtendedProgramData(source_type, aq_data, false); -} -*/ -/* -void kick_aq_program_thread(struct aqualinkdata *aq_data) -{ - if (aq_data->active_thread.thread_id != 0) { - LOG(PROG_LOG, LOG_DEBUG, "Kicking thread %d,%p message '%s'\n",aq_data->active_thread.ptype, aq_data->active_thread.thread_id,aq_data->last_message); - pthread_cond_broadcast(&aq_data->active_thread.thread_cond); - } -} -*/ bool in_light_programming_mode(struct aqualinkdata *aq_data) { if ( ( aq_data->active_thread.thread_id != 0 ) && @@ -520,34 +354,49 @@ bool in_iaqt_programming_mode(struct aqualinkdata *aq_data) return false; } -/* -Don't need this at the moment, it's kind-a inverse of above two functions - bool in_allb_programming_mode(struct aqualinkdata *aq_data) { - if ( ( aq_data->active_thread.thread_id != 0 ) && - ( aq_data->active_thread.ptype == AQ_GET_POOL_SPA_HEATER_TEMPS || - aq_data->active_thread.ptype == AQ_GET_FREEZE_PROTECT_TEMP || - aq_data->active_thread.ptype == AQ_SET_TIME || - aq_data->active_thread.ptype == AQ_SET_POOL_HEATER_TEMP || - aq_data->active_thread.ptype == AQ_SET_SPA_HEATER_TEMP || - aq_data->active_thread.ptype == AQ_SET_FRZ_PROTECTION_TEMP || - aq_data->active_thread.ptype == AQ_GET_DIAGNOSTICS_MODEL || - aq_data->active_thread.ptype == AQ_GET_PROGRAMS || - aq_data->active_thread.ptype == AQ_SET_LIGHTPROGRAM_MODE || - aq_data->active_thread.ptype == AQ_SET_LIGHTCOLOR_MODE || - aq_data->active_thread.ptype == AQ_SET_SWG_PERCENT || - aq_data->active_thread.ptype == AQ_GET_AUX_LABELS || - aq_data->active_thread.ptype == AQ_SET_BOOST || - aq_data->active_thread.ptype == AQ_SET_PUMP_RPM || - aq_data->active_thread.ptype == AQ_SET_PUMP_VS_PROGRAM) - ) { - return true; + if ( aq_data->active_thread.thread_id != 0 && + aq_data->active_thread.ptype >= AQP_ALLBUTTON_MIN && + aq_data->active_thread.ptype <= AQP_ALLBUTTONL_MAX) { + return true; } - return false; } -*/ + +// May need to re-think. if we are in PDA mode and using IAQTOUCH should we +// return PAD or IAQTOUCH +emulation_type get_programming_mode(program_type type) +{ + if (type >= AQP_ALLBUTTON_MIN && + type <= AQP_ALLBUTTONL_MAX) { + return ALLBUTTON; + } + if (type >= AQP_IAQTOUCH_MIN && + type <= AQP_IAQTOUCH_MAX) { + return IAQTOUCH; + } + if (type >= AQP_ONETOUCH_MIN && + type <= AQP_ONETOUCH_MAX) { + return ONETOUCH; + } + if (type >= AQP_PDA_MIN && + type <= AQP_PDA_MAX) { + return AQUAPDA; + } + return SIM_NONE; +} + +emulation_type get_current_programming_mode(struct aqualinkdata *aq_data) +{ + if ( !in_programming_mode(aq_data) ) { + return SIM_NONE; + } + + return get_programming_mode(aq_data->active_thread.ptype); +} + + bool in_programming_mode(struct aqualinkdata *aq_data) { if ( aq_data->active_thread.thread_id != 0 ) { @@ -590,61 +439,6 @@ void _aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_dat { struct programmingThreadCtrl *programmingthread = malloc(sizeof(struct programmingThreadCtrl)); - -/* - Seemed like a good idea to use lookuptable, but need to figure out a way to check program_type actually exists in array, - - //void* (*pf[])(void *ptr) = {get_freeze_protect_temp, get_freeze_protect_temp, get_freeze_protect_temp}; - //static const void * (*fun_lookup[])(void *ptr) = { - static void * (*function_lookup[])(void *ptr) = { - [AQ_GET_POOL_SPA_HEATER_TEMPS] = get_aqualink_pool_spa_heater_temps, - [AQ_GET_FREEZE_PROTECT_TEMP] = get_freeze_protect_temp, - [AQ_SET_TIME] = set_aqualink_time, - [AQ_SET_POOL_HEATER_TEMP] = set_aqualink_pool_heater_temps, - [AQ_SET_SPA_HEATER_TEMP] = set_aqualink_spa_heater_temps, - [AQ_SET_FRZ_PROTECTION_TEMP] = set_aqualink_freeze_heater_temps, - [AQ_GET_DIAGNOSTICS_MODEL] = get_aqualink_diag_model, - [AQ_GET_PROGRAMS] = get_aqualink_programs, - [AQ_SET_LIGHTPROGRAM_MODE] = set_aqualink_light_programmode, - [AQ_SET_LIGHTCOLOR_MODE] = set_aqualink_light_colormode, - [AQ_SET_SWG_PERCENT] = set_aqualink_SWG, - [AQ_GET_AUX_LABELS] = get_aqualink_aux_labels, - [AQ_SET_BOOST] = set_aqualink_boost, -#ifdef AQ_PDA - [AQ_PDA_INIT] = set_aqualink_PDA_init, - [AQ_PDA_DEVICE_STATUS] = get_aqualink_PDA_device_status, - [AQ_PDA_DEVICE_ON_OFF] = set_aqualink_PDA_device_on_off, - [AQ_PDA_WAKE_INIT] = set_aqualink_PDA_wakeinit, -#endif -#ifdef AQ_ONETOUCH - //[AQ_SET_PUMP_RPM] = , - //[AQ_SET_PUMP_VS_PROGRAM] = , - //[AQ_SET_ONETOUCH_MACRO] = , - [AQ_SET_ONETOUCH_PUMP_RPM] = set_aqualink_onetouch_pump_rpm, - [AQ_GET_ONETOUCH_SETPOINTS] = get_aqualink_onetouch_setpoints, - [AQ_SET_ONETOUCH_POOL_HEATER_TEMP] = set_aqualink_onetouch_pool_heater_temp, - [AQ_SET_ONETOUCH_SPA_HEATER_TEMP] = set_aqualink_onetouch_spa_heater_temp, - [AQ_SET_ONETOUCH_FREEZEPROTECT] = set_aqualink_onetouch_freezeprotect, - [AQ_SET_ONETOUCH_TIME] = set_aqualink_onetouch_time, - [AQ_SET_ONETOUCH_BOOST] = set_aqualink_onetouch_boost, - [AQ_SET_ONETOUCH_SWG_PERCENT] = set_aqualink_onetouch_swg_percent, -#endif -#ifdef AQ_IAQTOUCH - [AQ_SET_IAQTOUCH_PUMP_RPM] = set_aqualink_iaqtouch_pump_rpm, - [AQ_SET_IAQTOUCH_PUMP_VS_PROGRAM] = set_aqualink_iaqtouch_pump_vs_program, - [AQ_GET_IAQTOUCH_VSP_ASSIGNMENT] = set_aqualink_iaqtouch_vsp_assignments, - [AQ_GET_IAQTOUCH_SETPOINTS] = get_aqualink_iaqtouch_setpoints, - [AQ_GET_IAQTOUCH_AUX_LABELS] = get_aqualink_iaqtouch_aux_labels, - [AQ_SET_IAQTOUCH_SWG_PERCENT] = set_aqualink_iaqtouch_swg_percent, - [AQ_SET_IAQTOUCH_SWG_BOOST] = set_aqualink_iaqtouch_swg_boost, - [AQ_SET_IAQTOUCH_POOL_HEATER_TEMP] = set_aqualink_iaqtouch_pool_heater_temp, - [AQ_SET_IAQTOUCH_SPA_HEATER_TEMP] = set_aqualink_iaqtouch_spa_heater_temp, - [AQ_SET_IAQTOUCH_SET_TIME] = set_aqualink_iaqtouch_time, -#endif - [AQ_SET_BOOST] = set_aqualink_boost - }; -*/ - program_type type = r_type; // RS SerialAdapter is quickest for changing thermostat temps, so use that if enabeled. @@ -750,24 +544,53 @@ void _aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_dat // Check we are doing something valid request if (isPDA_PANEL && !isPDA_IAQT) { - pda_reset_sleep(); - if (type != AQ_PDA_INIT && - type != AQ_PDA_WAKE_INIT && - type != AQ_PDA_DEVICE_STATUS && - type != AQ_SET_POOL_HEATER_TEMP && - type != AQ_SET_SPA_HEATER_TEMP && - type != AQ_SET_SWG_PERCENT && - type != AQ_PDA_DEVICE_ON_OFF && + switch (r_type){ + case AQ_SET_POOL_HEATER_TEMP: + type = AQ_PDA_SET_POOL_HEATER_TEMPS; + break; + case AQ_SET_SPA_HEATER_TEMP: + type = AQ_PDA_SET_SPA_HEATER_TEMPS; + break; + case AQ_SET_SWG_PERCENT: + type = AQ_PDA_SET_SWG_PERCENT; + break; + case AQ_PDA_DEVICE_ON_OFF: + break; + case AQ_GET_POOL_SPA_HEATER_TEMPS: + type = AQ_PDA_GET_POOL_SPA_HEATER_TEMPS; + break; + case AQ_GET_FREEZE_PROTECT_TEMP: + type = AQ_PDA_GET_FREEZE_PROTECT_TEMP; + break; + case AQ_SET_FRZ_PROTECTION_TEMP: + type = AQ_PDA_SET_FREEZE_PROTECT_TEMP; + break; + case AQ_SET_BOOST: + type = AQ_PDA_SET_BOOST; + break; + case AQ_SET_TIME: + type = AQ_PDA_SET_TIME; + break; #ifdef BETA_PDA_AUTOLABEL - type != AQ_GET_AUX_LABELS && + case AQ_GET_AUX_LABELS: + type = AQ_PDA_AUX_LABELS: + break; #endif - type != AQ_GET_POOL_SPA_HEATER_TEMPS && - type != AQ_SET_FRZ_PROTECTION_TEMP && - type != AQ_SET_BOOST && - type != AQ_SET_TIME) { - LOG(PROG_LOG, LOG_ERR, "Selected Programming mode '%d' not supported with PDA mode control panel\n",type); + default: + type = r_type; + break; + } + if (get_programming_mode(type) != AQUAPDA ) { + LOG(PROG_LOG, LOG_ERR, "Selected Programming mode '%s' '%d' not supported with PDA control panel\n",ptypeName(type),type); return; - } + } + pda_reset_sleep(); + } else if (isPDA_PANEL && isPDA_IAQT) + { + if ( get_programming_mode(type) != IAQTOUCH) { + LOG(PROG_LOG, LOG_ERR, "Selected Programming mode '%s' '%d' not supported with PDA control panel in iAqualinkTouch mode\n",ptypeName(type),type); + return; + } } #endif @@ -776,6 +599,7 @@ void _aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_dat type = r_type; } + LOG(PROG_LOG, LOG_NOTICE, "Starting programming thread '%s'\n",ptypeName(type)); programmingthread->aq_data = aq_data; @@ -785,28 +609,6 @@ void _aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_dat strncpy(programmingthread->thread_args, args, sizeof(programmingthread->thread_args)-1); switch(type) { - /* - case AQ_SEND_CMD: - LOG(PROG_LOG, LOG_INFO, "Queue send '0x%02hhx' to controller\n", &args[0]); - - unsigned char cmd = (unsigned char) &args[0]; - if (cmd == NUL) { - LOG(PROG_LOG, LOG_INFO, "Queue send '0x%02hhx' to controller (NEW)\n", cmd); - push_aq_cmd( cmd ); - } else { - LOG(PROG_LOG, LOG_INFO, "Queue send '0x%02hhx' to controller (OLD)\n", cmd); - push_aq_cmd((unsigned char)*args); - }*/ - //LOG(PROG_LOG, LOG_INFO, "Queue send '0x%02hhx' to controller\n", (unsigned char)*args); - /* - if(aq_data->active_thread.thread_id == 0) { // No need to thread a plane send if no active threads - send_cmd( (unsigned char)*args, aq_data); - } else if( pthread_create( &programmingthread->thread_id , NULL , threadded_send_cmd, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - */ - //break; case AQ_GET_RSSADAPTER_SETPOINTS: get_aqualink_rssadapter_setpoints(); return; // No need to create this as thread. @@ -827,1689 +629,113 @@ void _aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_dat increase_aqualink_rssadapter_spa_setpoint(args, aq_data); return; // No need to create this as thread. break; - case AQ_GET_POOL_SPA_HEATER_TEMPS: - if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_pool_spa_heater_temps, (void*)programmingthread) < 0) { + default: + // Should check that _prog_functions[type] is valid. + if( pthread_create( &programmingthread->thread_id , NULL , _prog_functions[type], (void*)programmingthread) < 0) { LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); return; } - break; - case AQ_GET_FREEZE_PROTECT_TEMP: - if( pthread_create( &programmingthread->thread_id , NULL , get_freeze_protect_temp, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_SET_TIME: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_time, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_SET_POOL_HEATER_TEMP: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_pool_heater_temps, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_SET_SPA_HEATER_TEMP: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_spa_heater_temps, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_SET_FRZ_PROTECTION_TEMP: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_freeze_heater_temps, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_GET_DIAGNOSTICS_MODEL: - if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_diag_model, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_GET_PROGRAMS: - if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_programs, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_SET_LIGHTPROGRAM_MODE: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_light_programmode, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_SET_LIGHTCOLOR_MODE: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_light_colormode, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_SET_SWG_PERCENT: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_SWG, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_GET_AUX_LABELS: - if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_aux_labels, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_SET_BOOST: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_boost, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; -#ifdef AQ_ONETOUCH - case AQ_SET_ONETOUCH_PUMP_RPM: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_onetouch_pump_rpm, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_GET_ONETOUCH_FREEZEPROTECT: - if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_onetouch_freezeprotect, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_GET_ONETOUCH_SETPOINTS: - if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_onetouch_setpoints, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_SET_ONETOUCH_TIME: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_onetouch_time, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_SET_ONETOUCH_BOOST: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_onetouch_boost, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_SET_ONETOUCH_SWG_PERCENT: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_onetouch_swg_percent, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_SET_ONETOUCH_POOL_HEATER_TEMP: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_onetouch_pool_heater_temp, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_SET_ONETOUCH_SPA_HEATER_TEMP: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_onetouch_spa_heater_temp, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_SET_ONETOUCH_FREEZEPROTECT: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_onetouch_freezeprotect, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; -#endif -#ifdef AQ_IAQTOUCH - case AQ_SET_IAQTOUCH_PUMP_RPM: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_iaqtouch_pump_rpm, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_GET_IAQTOUCH_VSP_ASSIGNMENT: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_iaqtouch_vsp_assignments, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_GET_IAQTOUCH_SETPOINTS: - if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_iaqtouch_setpoints, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_GET_IAQTOUCH_FREEZEPROTECT: - if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_iaqtouch_freezeprotect, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_GET_IAQTOUCH_AUX_LABELS: - if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_iaqtouch_aux_labels, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_SET_IAQTOUCH_SWG_PERCENT: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_iaqtouch_swg_percent, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_SET_IAQTOUCH_SWG_BOOST: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_iaqtouch_swg_boost, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_SET_IAQTOUCH_POOL_HEATER_TEMP: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_iaqtouch_pool_heater_temp, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_SET_IAQTOUCH_SPA_HEATER_TEMP: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_iaqtouch_spa_heater_temp, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_SET_IAQTOUCH_SET_TIME: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_iaqtouch_time, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_SET_IAQTOUCH_PUMP_VS_PROGRAM: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_iaqtouch_pump_vs_program, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_SET_IAQTOUCH_LIGHTCOLOR_MODE: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_iaqtouch_light_colormode, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; -#endif -#ifdef AQ_PDA - case AQ_PDA_INIT: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_PDA_init, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_PDA_WAKE_INIT: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_PDA_wakeinit, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_PDA_DEVICE_STATUS: - if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_PDA_device_status, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_PDA_DEVICE_ON_OFF: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_PDA_device_on_off, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - case AQ_SET_IAQTOUCH_DEVICE_ON_OFF: - if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_iaqtouch_device_on_off, (void*)programmingthread) < 0) { - LOG(PROG_LOG, LOG_ERR, "could not create thread\n"); - return; - } - break; - #endif - - default: - LOG(PROG_LOG, LOG_ERR, "Didn't understand programming mode type\n"); - break; - } - - if ( programmingthread->thread_id != 0 ) { - //LOG(PROG_LOG, LOG_DEBUG, "********* DID pthread_detach %d\n",programmingthread->thread_id); - pthread_detach(programmingthread->thread_id); - } else { - //LOG(PROG_LOG, LOG_DEBUG, "********* DID NOT pthread_detach\n"); - } -} - - -void waitForSingleThreadOrTerminate(struct programmingThreadCtrl *threadCtrl, program_type type) -{ - //static int tries = 120; - int tries = 120; - static int waitTime = 1; - int i=0; - - i = 0; - while (get_aq_cmd_length() > 0 && ( i++ <= tries) ) { - LOG(PROG_LOG, LOG_DEBUG, "Thread %p (%s) sleeping, waiting command queue to empty\n", &threadCtrl->thread_id, ptypeName(type)); - sleep(waitTime); - } - if (i >= tries) { - LOG(PROG_LOG, LOG_ERR, "Thread %p (%s) timeout waiting, ending\n",&threadCtrl->thread_id,ptypeName(type)); - free(threadCtrl); - pthread_exit(0); - } - - while ( (threadCtrl->aq_data->active_thread.thread_id != 0) && ( i++ <= tries) ) { - //LOG(PROG_LOG, LOG_DEBUG, "Thread %d sleeping, waiting for thread %d to finish\n", threadCtrl->thread_id, threadCtrl->aq_data->active_thread.thread_id); - LOG(PROG_LOG, LOG_DEBUG, "Thread %p (%s) sleeping, waiting for thread %p (%s) to finish\n", - &threadCtrl->thread_id, ptypeName(type), - threadCtrl->aq_data->active_thread.thread_id, ptypeName(threadCtrl->aq_data->active_thread.ptype)); - sleep(waitTime); - } - - if (i >= tries) { - //LOG(PROG_LOG, LOG_ERR, "Thread %d timeout waiting, ending\n",threadCtrl->thread_id); - LOG(PROG_LOG, LOG_ERR, "Thread (%s) %p timeout waiting for thread (%s) %p to finish\n", - ptypeName(type), &threadCtrl->thread_id, ptypeName(threadCtrl->aq_data->active_thread.ptype), - threadCtrl->aq_data->active_thread.thread_id); - free(threadCtrl); - pthread_exit(0); - } - - // Clear out any messages to the UI. - threadCtrl->aq_data->last_display_message[0] = '\0'; - threadCtrl->aq_data->active_thread.thread_id = &threadCtrl->thread_id; - threadCtrl->aq_data->active_thread.ptype = type; - - #ifdef AQ_DEBUG - clock_gettime(CLOCK_REALTIME, &threadCtrl->aq_data->start_active_time); - #endif - - LOG(PROG_LOG, LOG_INFO, "Programming: %s, %d\n", ptypeName(threadCtrl->aq_data->active_thread.ptype), threadCtrl->aq_data->active_thread.ptype); - - LOG(PROG_LOG, LOG_DEBUG, "Thread %d,%p is active (%s)\n", - threadCtrl->aq_data->active_thread.ptype, - threadCtrl->aq_data->active_thread.thread_id, - ptypeName(threadCtrl->aq_data->active_thread.ptype)); -} - -void cleanAndTerminateThread(struct programmingThreadCtrl *threadCtrl) -{ - waitfor_queue2empty(); - #ifndef AQ_DEBUG - LOG(PROG_LOG, LOG_DEBUG, "Thread %d,%p (%s) finished\n",threadCtrl->aq_data->active_thread.ptype, threadCtrl->thread_id,ptypeName(threadCtrl->aq_data->active_thread.ptype)); - #else - struct timespec elapsed; - clock_gettime(CLOCK_REALTIME, &threadCtrl->aq_data->last_active_time); - timespec_subtract(&elapsed, &threadCtrl->aq_data->last_active_time, &threadCtrl->aq_data->start_active_time); - LOG(PROG_LOG, LOG_NOTICE, "Thread %d,%p (%s) finished in %d.%03ld sec\n", - threadCtrl->aq_data->active_thread.ptype, - threadCtrl->aq_data->active_thread.thread_id, - ptypeName(threadCtrl->aq_data->active_thread.ptype), - elapsed.tv_sec, elapsed.tv_nsec / 1000000L); - #endif - - // Quick delay to allow for last message to be sent. - delay(500); - threadCtrl->aq_data->active_thread.thread_id = 0; - threadCtrl->aq_data->active_thread.ptype = AQP_NULL; - threadCtrl->thread_id = 0; - // Force update, change display message - threadCtrl->aq_data->updated = true; - free(threadCtrl); - pthread_exit(0); -} - -bool setAqualinkNumericField_new(struct aqualinkdata *aq_data, char *value_label, int value, int increment); -bool setAqualinkNumericField(struct aqualinkdata *aq_data, char *value_label, int value) -{ - return setAqualinkNumericField_new(aq_data, value_label, value, 1); -} -bool setAqualinkNumericField_new(struct aqualinkdata *aq_data, char *value_label, int value, int increment) -{ - LOG(ALLB_LOG, LOG_DEBUG,"Setting menu item '%s' to %d\n",value_label, value); - //char leading[10]; // description of the field (POOL, SPA, FRZ) - int current_val=-1; // integer value of the current set point - //char trailing[10]; // the degrees and scale - char searchBuf[20]; - - sprintf(searchBuf, "^%s", value_label); - int val_len = strlen(value_label); - int i=0; - do - { - if (waitForMessage(aq_data, searchBuf, 4) != true) { - LOG(ALLB_LOG, LOG_WARNING, "AQ_Programmer Could not set numeric input '%s', not found\n",value_label); - cancel_menu(); - return false; - } -//LOG(ALLB_LOG, LOG_DEBUG,"WAITING for kick value=%d\n",current_val); - //sscanf(aq_data->last_message, "%s %d%s", leading, ¤t_val, trailing); - //sscanf(aq_data->last_message, "%*[^0123456789]%d", ¤t_val); - sscanf(&aq_data->last_message[val_len], "%*[^0123456789]%d", ¤t_val); - LOG(ALLB_LOG, LOG_DEBUG, "%s set to %d, looking for %d\n",value_label,current_val,value); - - if(value > current_val) { - // Increment the field. - sprintf(searchBuf, "%s %d", value_label, current_val+increment); - send_cmd(KEY_RIGHT); - } - else if(value < current_val) { - // Decrement the field. - sprintf(searchBuf, "%s %d", value_label, current_val-increment); - send_cmd(KEY_LEFT); - } - else { - // Just send ENTER. We are at the right value. - sprintf(searchBuf, "%s %d", value_label, current_val); - send_cmd(KEY_ENTER); - } - - if (i++ >= 100) { - LOG(ALLB_LOG, LOG_WARNING, "AQ_Programmer Could not set numeric input '%s', to '%d'\n",value_label,value); - send_cmd(KEY_ENTER); - break; - } - } while(value != current_val); - - return true; -} - - - -bool OLD_setAqualinkNumericField_OLD(struct aqualinkdata *aq_data, char *value_label, int value) -{ // Works for everything but not SWG - LOG(ALLB_LOG, LOG_DEBUG,"Setting menu item '%s' to %d\n",value_label, value); - char leading[10]; // description of the field (POOL, SPA, FRZ) - int current_val; // integer value of the current set point - char trailing[10]; // the degrees and scale - char searchBuf[20]; - - sprintf(searchBuf, "^%s", value_label); - - do - { - if (waitForMessage(aq_data, searchBuf, 3) != true) { - LOG(ALLB_LOG, LOG_WARNING, "AQ_Programmer Could not set numeric input '%s', not found\n",value_label); - cancel_menu(); - return false; - } -//LOG(ALLB_LOG, LOG_DEBUG,"WAITING for kick value=%d\n",current_val); - sscanf(aq_data->last_message, "%s %d%s", leading, ¤t_val, trailing); - LOG(ALLB_LOG, LOG_DEBUG, "%s set to %d, looking for %d\n",value_label,current_val,value); - - if(value > current_val) { - // Increment the field. - sprintf(searchBuf, "%s %d", value_label, current_val+1); - send_cmd(KEY_RIGHT); - } - else if(value < current_val) { - // Decrement the field. - sprintf(searchBuf, "%s %d", value_label, current_val-1); - send_cmd(KEY_LEFT); - } - else { - // Just send ENTER. We are at the right value. - sprintf(searchBuf, "%s %d", value_label, current_val); - send_cmd(KEY_ENTER); - } - } while(value != current_val); - - return true; -} -/* -void *threadded_send_cmd( void *ptr ) -{ - struct programmingThreadCtrl *threadCtrl; - threadCtrl = (struct programmingThreadCtrl *) ptr; - struct aqualinkdata *aq_data = threadCtrl->aq_data; - - waitForSingleThreadOrTerminate(threadCtrl, AQ_SEND_CMD); - - send_cmd( (unsigned char)*threadCtrl->thread_args, aq_data); - - cleanAndTerminateThread(threadCtrl); - - return ptr; -} -*/ - -void *set_aqualink_boost( void *ptr ) -{ - struct programmingThreadCtrl *threadCtrl; - threadCtrl = (struct programmingThreadCtrl *) ptr; - struct aqualinkdata *aq_data = threadCtrl->aq_data; - - waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_BOOST); - /* - menu - -BOOST POOL - -PRESS ENTER* TO START BOOST POOL - - - - -BOOST POOL 23:59 REMAINING - - -menu - -BOOST POOL - -STOP BOOST POOL - - */ - int val = atoi((char*)threadCtrl->thread_args); - -#ifdef AQ_PDA - if (isPDA_PANEL) { - set_PDA_aqualink_boost(aq_data, val); - cleanAndTerminateThread(threadCtrl); - return ptr; - } -#endif - - LOG(ALLB_LOG, LOG_DEBUG, "programming BOOST to %s\n", val==true?"On":"Off"); - - if ( select_menu_item(aq_data, "BOOST POOL") != true ) { - LOG(ALLB_LOG, LOG_WARNING, "Could not select BOOST POOL menu\n"); - cancel_menu(); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - - if (val==true) { - waitForMessage(threadCtrl->aq_data, "TO START BOOST POOL", 5); - send_cmd(KEY_ENTER); - longwaitfor_queue2empty(); - } else { - int wait_messages = 5; - int i=0; - while( i++ < wait_messages) - { - waitForMessage(aq_data, "STOP BOOST POOL", 1); - if (stristr(aq_data->last_message, "STOP BOOST POOL") != NULL) { - // This is a really bad hack, message sequence is out for boost for some reason, so as soon as we see stop message, force enter key. - //_pgm_command = KEY_ENTER; - send_cmd(KEY_ENTER); - LOG(ALLB_LOG, LOG_DEBUG, "**** FOUND STOP BOOST POOL ****\n"); - //waitfor_queue2empty(); - break; - } else { - LOG(ALLB_LOG, LOG_DEBUG, "Find item in Menu: loop %d of %d looking for 'STOP BOOST POOL' received message '%s'\n",i,wait_messages,aq_data->last_message); - delay(200); - if (stristr(aq_data->last_message, "STOP BOOST POOL") != NULL) { - //_pgm_command = KEY_ENTER; - send_cmd(KEY_ENTER); - LOG(ALLB_LOG, LOG_DEBUG, "**** FOUND STOP BOOST POOL ****\n"); - break; - } - send_cmd(KEY_RIGHT); - //printf("WAIT\n"); - longwaitfor_queue2empty(); - //printf("FINISHED WAIT\n"); - } - //waitfor_queue2empty(); - //waitForMessage(aq_data, NULL, 1); - } - if (i < wait_messages) { - // Takes ages to see bost is off from menu, to set it here. - setSWGboost(aq_data, false); - } - /* - // Extra message overcome. - send_cmd(KEY_RIGHT); - waitfor_queue2empty(); - if ( select_sub_menu_item(aq_data, "STOP BOOST POOL") != true ) { - LOG(ALLB_LOG, LOG_WARNING, "Could not select STOP BOOST POOL menu\n"); - cancel_menu(); - cleanAndTerminateThread(threadCtrl); - return ptr; - }*/ - //send_cmd(KEY_ENTER); - } - - waitForMessage(aq_data,NULL, 1); - - cleanAndTerminateThread(threadCtrl); - - // just stop compiler error, ptr is not valid as it's just been freed - return ptr; -} - - -void *set_aqualink_SWG( void *ptr ) -{ - struct programmingThreadCtrl *threadCtrl; - threadCtrl = (struct programmingThreadCtrl *) ptr; - struct aqualinkdata *aq_data = threadCtrl->aq_data; - - waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_SWG_PERCENT); - - int val = atoi((char*)threadCtrl->thread_args); - val = setpoint_check(SWG_SETPOINT, val, aq_data); - -#ifdef AQ_PDA - if (isPDA_PANEL) { - set_PDA_aqualink_SWG_setpoint(aq_data, val); - cleanAndTerminateThread(threadCtrl); - return ptr; - } -#endif - - //LOG(ALLB_LOG, LOG_NOTICE, "programming SWG percent to %d\n", val); - - if ( select_menu_item(aq_data, "SET AQUAPURE") != true ) { - LOG(ALLB_LOG, LOG_WARNING, "Could not select SET AQUAPURE menu\n"); - LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); - cancel_menu(); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - - // If spa is on, set SWG for spa, if not set SWG for pool - if (aq_data->aqbuttons[SPA_INDEX].led->state != OFF) { - if (select_sub_menu_item(aq_data, "SET SPA SP") != true) { - LOG(ALLB_LOG, LOG_WARNING, "Could not select SWG setpoint menu for SPA\n"); - LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); - cancel_menu(); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - setAqualinkNumericField_new(aq_data, "SPA SP", val, 5); - } else { - if (select_sub_menu_item(aq_data, "SET POOL SP") != true) { - LOG(ALLB_LOG, LOG_WARNING, "Could not select SWG setpoint menu\n"); - LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); - cancel_menu(); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - setAqualinkNumericField_new(aq_data, "POOL SP", val, 5); - } - - // Let everyone know we set SWG, if it failed we will update on next message, unless it's 0. - setSWGpercent(aq_data, val); // Don't use chageSWGpercent as we are in programming mode. - -/* - if (select_sub_menu_item(aq_data, "SET POOL SP") != true) { - LOG(ALLB_LOG, LOG_WARNING, "Could not select SWG setpoint menu\n"); - cancel_menu(aq_data); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - - setAqualinkNumericField_new(aq_data, "POOL SP", val, 5); -*/ - // usually miss this message, not sure why, but wait anyway to make sure programming has ended - // NSF have see the below message RS Message :- - // 'Pool set to 20%' - // 'POOL SP IS SET TO 20%' - waitForMessage(threadCtrl->aq_data, "SET TO", 1); - //waitForMessage(threadCtrl->aq_data, "POOL SP IS SET TO", 1); - - // Since we read % directly from RS message, wait for another few messages that way - // We won't registed a SWG bounce, since we already told clients SWG was at new % before programming started - waitForMessage(threadCtrl->aq_data, NULL, 1); - - cleanAndTerminateThread(threadCtrl); - - // just stop compiler error, ptr is not valid as it's just been freed - return ptr; -} - - - -void *get_aqualink_aux_labels( void *ptr ) -{ - struct programmingThreadCtrl *threadCtrl; - threadCtrl = (struct programmingThreadCtrl *) ptr; - struct aqualinkdata *aq_data = threadCtrl->aq_data; - - waitForSingleThreadOrTerminate(threadCtrl, AQ_GET_AUX_LABELS); - -#ifdef AQ_PDA - if (isPDA_PANEL) { - get_PDA_aqualink_aux_labels(aq_data); - cleanAndTerminateThread(threadCtrl); - return ptr; - } -#endif - - if ( select_menu_item(aq_data, "REVIEW") != true ) { - LOG(ALLB_LOG, LOG_WARNING, "Could not select REVIEW menu\n"); - LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); - cancel_menu(); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - - if (select_sub_menu_item(aq_data, "AUX LABELS") != true) { - LOG(ALLB_LOG, LOG_WARNING, "Could not select AUX LABELS menu\n"); - LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); - cancel_menu(); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - - waitForMessage(aq_data, NULL, 5); // Receive 5 messages - - cleanAndTerminateThread(threadCtrl); - - // just stop compiler error, ptr is not valid as it's just been freed - return ptr; -} - -void *set_aqualink_light_colormode( void *ptr ) -{ - int i; - struct programmingThreadCtrl *threadCtrl; - threadCtrl = (struct programmingThreadCtrl *) ptr; - struct aqualinkdata *aq_data = threadCtrl->aq_data; - - waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_LIGHTCOLOR_MODE); - - char *buf = (char*)threadCtrl->thread_args; - const char *mode_name; - int val = atoi(&buf[0]); - int btn = atoi(&buf[5]); - int typ = atoi(&buf[10]); - bool use_current_mode = false; - - if (btn < 0 || btn >= aq_data->total_buttons ) { - LOG(ALLB_LOG, LOG_ERR, "Can't program light mode on button %d\n", btn); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - - aqkey *button = &aq_data->aqbuttons[btn]; - unsigned char code = button->code; - - //LOG(ALLB_LOG, LOG_NOTICE, "Light Programming #: %d, on button: %s, color light type: %d\n", val, button->label, typ); - - if (val <= 0) { - use_current_mode = true; - LOG(ALLB_LOG, LOG_INFO, "Light Programming #: %d, on button: %s, color light type: %d, using current mode\n", val, button->label, typ); - } else { - mode_name = light_mode_name(typ, val-1, ALLBUTTON); - use_current_mode = false; - if (mode_name == NULL) { - LOG(ALLB_LOG, LOG_ERR, "Light Programming #: %d, on button: %s, color light type: %d, couldn't find mode name '%s'\n", val, button->label, typ, mode_name); - cleanAndTerminateThread(threadCtrl); - return ptr; - } else { - LOG(ALLB_LOG, LOG_INFO, "Light Programming #: %d, on button: %s, color light type: %d, name '%s'\n", val, button->label, typ, mode_name); - } - } -/* - // Simply turn the light off if value is 0 - if (val <= 0) { - if ( button->led->state == ON ) { - send_cmd(code); - } - cleanAndTerminateThread(threadCtrl); - return ptr; - } -*/ - // Needs to start programming sequence with light off - if ( button->led->state == ON ) { - LOG(ALLB_LOG, LOG_INFO, "Light Programming Initial state on, turning off\n"); - send_cmd(code); - waitfor_queue2empty(); - if ( !waitForMessage(threadCtrl->aq_data, "OFF", 5)) // Message like 'Aux3 Off' - LOG(ALLB_LOG, LOG_ERR, "Light Programming didn't receive OFF message\n"); - } - - // Now turn on and wait for the message "color mode name<>*" - send_cmd(code); - waitfor_queue2empty(); - i=0; - int waitCounter=12; - - do{ - LOG(ALLB_LOG, LOG_INFO,"Light program wait for message\n"); - if ( !waitForMessage(threadCtrl->aq_data, "~*", waitCounter)) - LOG(ALLB_LOG, LOG_ERR, "Light Programming didn't receive color light mode message\n"); - - // Wait for less messages after first try. We get a lot of repeat messages before the one we need. - waitCounter = 3; - - if (use_current_mode) { - LOG(ALLB_LOG, LOG_INFO, "Light Programming using color mode %s\n",aq_data->last_message); - send_cmd(KEY_ENTER); - waitfor_queue2empty(); - break; - } else if (strncasecmp(aq_data->last_message, mode_name, strlen(mode_name)) == 0) { - LOG(ALLB_LOG, LOG_INFO, "Light Programming found color mode %s\n",mode_name); - send_cmd(KEY_ENTER); - waitfor_queue2empty(); - break; - } - - send_cmd(KEY_RIGHT); - waitfor_queue2empty(); - // Just clear current message before waiting for next, since the test in the do can't distinguish - // as both messages end in "~*" - waitForMessage(threadCtrl->aq_data, NULL, 1); - - i++; - } while (i <= LIGHT_COLOR_OPTIONS); - - if (i == LIGHT_COLOR_OPTIONS) { - LOG(ALLB_LOG, LOG_ERR, "Light Programming didn't receive color light mode message for '%s'\n",use_current_mode?"light program":mode_name); - } - - cleanAndTerminateThread(threadCtrl); - - // just stop compiler error, ptr is not valid as it's just been freed - return ptr; -} - - -void *set_aqualink_light_programmode( void *ptr ) -{ - int i; - struct programmingThreadCtrl *threadCtrl; - threadCtrl = (struct programmingThreadCtrl *) ptr; - struct aqualinkdata *aq_data = threadCtrl->aq_data; - - waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_LIGHTPROGRAM_MODE); - - char *buf = (char*)threadCtrl->thread_args; - int val = atoi(&buf[0]); - int btn = atoi(&buf[5]); - int iOn = atoi(&buf[10]); - int iOff = atoi(&buf[15]); - float pmode = atof(&buf[20]); - - if (btn < 0 || btn >= aq_data->total_buttons ) { - LOG(ALLB_LOG, LOG_ERR, "Can't program light mode on button %d\n", btn); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - - aqkey *button = &aq_data->aqbuttons[btn]; - unsigned char code = button->code; - - LOG(ALLB_LOG, LOG_INFO, "Light Programming #: %d, on button: %s, with pause mode: %f (initial on=%d, initial off=%d)\n", val, button->label, pmode, iOn, iOff); - - // Simply turn the light off if value is 0 - if (val <= 0) { - if ( button->led->state == ON ) { - send_cmd(code); - } - cleanAndTerminateThread(threadCtrl); - return ptr; - } - - const int seconds = 1000; - // Needs to start programming sequence with light on, if off we need to turn on for 15 seconds - // before we can send the next off. - if ( button->led->state != ON ) { - LOG(ALLB_LOG, LOG_INFO, "Light Programming Initial state off, turning on for %d seconds\n",iOn); - send_cmd(code); - delay(iOn * seconds); - } - - LOG(ALLB_LOG, LOG_INFO, "Light Programming turn off for %d seconds\n",iOff); - // Now need to turn off for between 11 and 14 seconds to reset light. - send_cmd(code); - delay(iOff * seconds); - - // Now light is reset, pulse the appropiate number of times to advance program. - LOG(ALLB_LOG, LOG_INFO, "Light Programming button pulsing on/off %d times\n", val); - - // Program light in safe mode (slowley), or quick mode - if (pmode > 0) { - for (i = 1; i < (val * 2); i++) { - LOG(ALLB_LOG, LOG_INFO, "Light Programming button press number %d - %s of %d\n", i, i % 2 == 0 ? "Off" : "On", val); - send_cmd(code); - waitfor_queue2empty(); - delay(pmode * seconds); // 0.3 works, but using 0.4 to be safe - } - } else { - for (i = 1; i < val; i++) { - const int dt = 0.5; // Time to wait after receiving conformation of light on/off - LOG(ALLB_LOG, LOG_INFO, "Light Programming button press number %d - %s of %d\n", i, "ON", val); - send_cmd(code); - waitForButtonState(aq_data, button, ON, 2); - delay(dt * seconds); - LOG(ALLB_LOG, LOG_INFO, "Light Programming button press number %d - %s of %d\n", i, "OFF", val); - send_cmd(code); - waitForButtonState(aq_data, button, OFF, 2); - delay(dt * seconds); - } - LOG(ALLB_LOG, LOG_INFO, "Finished - Light Programming button press number %d - %s of %d\n", i, "ON", val); - send_cmd(code); - waitfor_queue2empty(); - } - //waitForButtonState(aq_data, &aq_data->aqbuttons[btn], ON, 2); - - cleanAndTerminateThread(threadCtrl); - - // just stop compiler error, ptr is not valid as it's just been freed - return ptr; -} -void *set_aqualink_pool_heater_temps( void *ptr ) -{ - struct programmingThreadCtrl *threadCtrl; - threadCtrl = (struct programmingThreadCtrl *) ptr; - struct aqualinkdata *aq_data = threadCtrl->aq_data; - char *name; - char *menu_name; - waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_POOL_HEATER_TEMP); - - int val = atoi((char*)threadCtrl->thread_args); - /* - if (val > HEATER_MAX) { - val = HEATER_MAX; - } else if ( val < MEATER_MIN) { - val = MEATER_MIN; - } - */ - val = setpoint_check(POOL_HTR_SETOINT, val, aq_data); - -#ifdef AQ_PDA - if (isPDA_PANEL) { - set_PDA_aqualink_heater_setpoint(aq_data, val, true); - cleanAndTerminateThread(threadCtrl); - return ptr; - } -#endif - - // NSF IF in TEMP1 / TEMP2 mode, we need C range of 1 to 40 is 2 to 40 for TEMP1, 1 to 39 TEMP2 - if (isSINGLE_DEV_PANEL){ - name = "TEMP1"; - menu_name = "SET TEMP1"; - } else { - name = "POOL"; - menu_name = "SET POOL TEMP"; - } - - LOG(ALLB_LOG, LOG_DEBUG, "Setting pool heater setpoint to %d\n", val); - //setAqualinkTemp(aq_data, "SET TEMP", "SET POOL TEMP", NULL, "POOL", val); - - if ( select_menu_item(aq_data, "SET TEMP") != true ) { - LOG(ALLB_LOG, LOG_WARNING, "Could not select SET TEMP menu\n"); - LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); - cancel_menu(); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - - //if (select_sub_menu_item(aq_data, "SET POOL TEMP") != true) { - if (select_sub_menu_item(aq_data, menu_name) != true) { - LOG(ALLB_LOG, LOG_WARNING, "Could not select SET POOL TEMP menu\n"); - LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); - cancel_menu(); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - - if (isSINGLE_DEV_PANEL){ - // Need to get pass this message 'TEMP1 MUST BE SET HIGHER THAN TEMP2' - // and get this message 'TEMP1 20�C ~*' - // Before going to numeric field. - waitForMessage(threadCtrl->aq_data, "MUST BE SET", 5); - send_cmd(KEY_LEFT); - while (stristr(aq_data->last_message, "MUST BE SET") != NULL) { - delay(500); - } - } - - //setAqualinkNumericField(aq_data, "POOL", val); - setAqualinkNumericField(aq_data, name, val); - - // usually miss this message, not sure why, but wait anyway to make sure programming has ended - waitForMessage(threadCtrl->aq_data, "POOL TEMP IS SET TO", 1); - cleanAndTerminateThread(threadCtrl); - - // just stop compiler error, ptr is not valid as it's just been freed - return ptr; -} -void *set_aqualink_spa_heater_temps( void *ptr ) -{ - struct programmingThreadCtrl *threadCtrl; - threadCtrl = (struct programmingThreadCtrl *) ptr; - struct aqualinkdata *aq_data = threadCtrl->aq_data; - - waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_SPA_HEATER_TEMP); - - int val = atoi((char*)threadCtrl->thread_args); - char *name; - char *menu_name; - /* - if (val > HEATER_MAX) { - val = HEATER_MAX; - } else if ( val < MEATER_MIN) { - val = MEATER_MIN; - }*/ - val = setpoint_check(SPA_HTR_SETOINT, val, aq_data); - -#ifdef AQ_PDA - if (isPDA_PANEL) { - set_PDA_aqualink_heater_setpoint(aq_data, val, false); - cleanAndTerminateThread(threadCtrl); - return ptr; - } -#endif - // NSF IF in TEMP1 / TEMP2 mode, we need C range of 1 to 40 is 2 to 40 for TEMP1, 1 to 39 TEMP2 - - if (isSINGLE_DEV_PANEL){ - name = "TEMP2"; - menu_name = "SET TEMP2"; - } else { - name = "SPA"; - menu_name = "SET SPA TEMP"; - } - - LOG(ALLB_LOG, LOG_DEBUG, "Setting spa heater setpoint to %d\n", val); - - //setAqualinkTemp(aq_data, "SET TEMP", "SET SPA TEMP", NULL, "SPA", val); - if ( select_menu_item(aq_data, "SET TEMP") != true ) { - LOG(ALLB_LOG, LOG_WARNING, "Could not select SET TEMP menu\n"); - LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); - cancel_menu(); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - - //if (select_sub_menu_item(aq_data, "SET SPA TEMP") != true) { - if (select_sub_menu_item(aq_data, menu_name) != true) { - LOG(ALLB_LOG, LOG_WARNING, "Could not select SET SPA TEMP menu\n"); - LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); - cancel_menu(); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - - if (isSINGLE_DEV_PANEL){ - // Need to get pass this message 'TEMP2 MUST BE SET LOWER THAN TEMP1' - // and get this message 'TEMP2 20�C ~*' - // Before going to numeric field. - waitForMessage(threadCtrl->aq_data, "MUST BE SET", 5); - send_cmd(KEY_LEFT); - while (stristr(aq_data->last_message, "MUST BE SET") != NULL) { - delay(500); - } - } - - //setAqualinkNumericField(aq_data, "SPA", val); - setAqualinkNumericField(aq_data, name, val); - - // usually miss this message, not sure why, but wait anyway to make sure programming has ended - waitForMessage(threadCtrl->aq_data, "SPA TEMP IS SET TO", 1); - cleanAndTerminateThread(threadCtrl); - - // just stop compiler error, ptr is not valid as it's just been freed - return ptr; -} - -void *set_aqualink_freeze_heater_temps( void *ptr ) -{ - struct programmingThreadCtrl *threadCtrl; - threadCtrl = (struct programmingThreadCtrl *) ptr; - struct aqualinkdata *aq_data = threadCtrl->aq_data; - - waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_FRZ_PROTECTION_TEMP); - - int val = atoi((char*)threadCtrl->thread_args); - /* - if (val > FREEZE_PT_MAX) { - val = FREEZE_PT_MAX; - } else if ( val < FREEZE_PT_MIN) { - val = FREEZE_PT_MIN; - } - */ - val = setpoint_check(FREEZE_SETPOINT, val, aq_data); - - LOG(ALLB_LOG, LOG_DEBUG, "Setting sfreeze protection to %d\n", val); - -#ifdef AQ_PDA - if (isPDA_PANEL) { - set_PDA_aqualink_freezeprotect_setpoint(aq_data, val); - cleanAndTerminateThread(threadCtrl); - return ptr; - } -#endif - //setAqualinkTemp(aq_data, "SYSTEM SETUP", "FRZ PROTECT", "TEMP SETTING", "FRZ", val); - if ( select_menu_item(aq_data, "SYSTEM SETUP") != true ) { - LOG(ALLB_LOG, LOG_WARNING, "Could not select SYSTEM SETUP menu\n"); - LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); - cancel_menu(); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - - if (select_sub_menu_item(aq_data, "FRZ PROTECT") != true) { - LOG(ALLB_LOG, LOG_WARNING, "Could not select FRZ PROTECT menu\n"); - LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); - cancel_menu(); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - - if (select_sub_menu_item(aq_data, "TEMP SETTING") != true) { - LOG(ALLB_LOG, LOG_WARNING, "Could not select TEMP SETTING menu\n"); - LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); - cancel_menu(); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - - setAqualinkNumericField(aq_data, "FRZ", val); - - waitForMessage(threadCtrl->aq_data, "FREEZE PROTECTION IS SET TO", 3); - cleanAndTerminateThread(threadCtrl); - - // just stop compiler error, ptr is not valid as it's just been freed - return ptr; -} - -void *set_aqualink_time( void *ptr ) -{ - struct programmingThreadCtrl *threadCtrl; - threadCtrl = (struct programmingThreadCtrl *) ptr; - struct aqualinkdata *aq_data = threadCtrl->aq_data; - - waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_TIME); - //LOG(ALLB_LOG, LOG_NOTICE, "Setting time on aqualink\n"); - -#ifdef AQ_PDA - if (isPDA_PANEL) { - set_PDA_aqualink_time(aq_data); - cleanAndTerminateThread(threadCtrl); - return ptr; - } -#endif - - time_t now = time(0); // get time now - struct tm *result = localtime(&now); - char hour[20]; - - // Add 10 seconds to time since this can take a while to program. - // 10 to 20 seconds whould be right, but since there are no seconds we can set, add 30 seconds to get close to minute. - // Should probably set this to program the next minute then wait before hitting the final enter command. - result->tm_sec += 30; - mktime(result); - - if (result->tm_hour == 0) - sprintf(hour, "HOUR 12 AM"); - else if (result->tm_hour <= 11) - sprintf(hour, "HOUR %d AM", result->tm_hour); // Need to fix compiler warning on new GCC, but %2d or %.2d does NOT give correct string - else if (result->tm_hour == 12) - sprintf(hour, "HOUR 12 PM"); - else // Must be 13 or more - sprintf(hour, "HOUR %d PM", result->tm_hour - 12); - - - LOG(ALLB_LOG, LOG_DEBUG, "Setting time to %d/%d/%d %d:%d\n", result->tm_mon + 1, result->tm_mday, result->tm_year + 1900, result->tm_hour + 1, result->tm_min); - - if ( select_menu_item(aq_data, "SET TIME") != true ) { - LOG(ALLB_LOG, LOG_WARNING, "Could not select SET TIME menu\n"); - LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); - cancel_menu(); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - - setAqualinkNumericField(aq_data, "YEAR", result->tm_year + 1900); - setAqualinkNumericField(aq_data, "MONTH", result->tm_mon + 1); - setAqualinkNumericField(aq_data, "DAY", result->tm_mday); - //setAqualinkNumericFieldExtra(aq_data, "HOUR", 11, "PM"); - select_sub_menu_item(aq_data, hour); // This will keep looping until it finds the right message - setAqualinkNumericField(aq_data, "MINUTE", result->tm_min); - - send_cmd(KEY_ENTER); - - cleanAndTerminateThread(threadCtrl); - - // just stop compiler error, ptr is not valid as it's just been freed - return ptr; -} - -void *get_aqualink_diag_model( void *ptr ) -{ - struct programmingThreadCtrl *threadCtrl; - threadCtrl = (struct programmingThreadCtrl *) ptr; - struct aqualinkdata *aq_data = threadCtrl->aq_data; - - waitForSingleThreadOrTerminate(threadCtrl, AQ_GET_DIAGNOSTICS_MODEL); - - if ( select_menu_item(aq_data, "SYSTEM SETUP") != true ) { - LOG(ALLB_LOG, LOG_WARNING, "Could not select HELP menu\n"); - LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); - cancel_menu(); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - - if (select_sub_menu_item(aq_data, "DIAGNOSTICS") != true) { - LOG(ALLB_LOG, LOG_WARNING, "Could not select DIAGNOSTICS menu\n"); - LOG(ALLB_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) ); - cancel_menu(); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - - waitForMessage(aq_data, NULL, 8); // Receive 8 messages - //8157 REV MMM | BATTERY OK | Cal: -27 0 6 | CONTROL PANEL #1 | CONTROL PANEL #3 | WATER SENSOR OK | AIR SENSOR OK | SOLAR SENSOR OPENED - cleanAndTerminateThread(threadCtrl); - - // just stop compiler error, ptr is not valid as it's just been freed - return ptr; -} - -void *get_aqualink_pool_spa_heater_temps( void *ptr ) -{ -//LOG(ALLB_LOG, LOG_DEBUG, "Could not select TEMP SET menu\n"); - struct programmingThreadCtrl *threadCtrl; - threadCtrl = (struct programmingThreadCtrl *) ptr; - struct aqualinkdata *aq_data = threadCtrl->aq_data; - - waitForSingleThreadOrTerminate(threadCtrl, AQ_GET_POOL_SPA_HEATER_TEMPS); - //LOG(ALLB_LOG, LOG_NOTICE, "Getting pool & spa heat setpoints from aqualink\n"); - -#ifdef AQ_PDA - if (isPDA_PANEL) { - if (!get_PDA_aqualink_pool_spa_heater_temps(aq_data)) { - LOG(ALLB_LOG, LOG_ERR, "Error Getting PDA pool & spa heater setpoints\n"); - } - cleanAndTerminateThread(threadCtrl); - return ptr; - } -#endif - - if ( select_menu_item(aq_data, "REVIEW") != true ) { - LOG(ALLB_LOG, LOG_WARNING, "Could not select REVIEW menu\n"); - LOG(ALLB_LOG, LOG_ERR, "Can't get heater setpoints from Control Panel\n"); - cancel_menu(); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - - if (select_sub_menu_item(aq_data, "TEMP SET") != true) { - LOG(ALLB_LOG, LOG_WARNING, "Could not select TEMP SET menu\n"); - LOG(ALLB_LOG, LOG_ERR, "Can't get heater setpoints from Control Panel\n"); - cancel_menu(); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - - // Should receive 'POOL TEMP IS SET TO xx' then 'SPA TEMP IS SET TO xx' then 'MAINTAIN TEMP IS (OFF|ON)' - // wait for the last message - waitForMessage(threadCtrl->aq_data, "MAINTAIN TEMP IS", 5); - - //cancel_menu(threadCtrl->aq_data); - cleanAndTerminateThread(threadCtrl); - - // just stop compiler error, ptr is not valid as it's just been freed - return ptr; -} - -void *get_freeze_protect_temp( void *ptr ) -{ - struct programmingThreadCtrl *threadCtrl; - threadCtrl = (struct programmingThreadCtrl *) ptr; - struct aqualinkdata *aq_data = threadCtrl->aq_data; - - waitForSingleThreadOrTerminate(threadCtrl, AQ_GET_FREEZE_PROTECT_TEMP); - //LOG(ALLB_LOG, LOG_NOTICE, "Getting freeze protection setpoints\n"); - -#ifdef AQ_PDA - if (isPDA_PANEL) { - if (! get_PDA_freeze_protect_temp(aq_data)) { - LOG(ALLB_LOG, LOG_ERR, "Error Getting PDA freeze protection setpoints\n"); - } - cleanAndTerminateThread(threadCtrl); - return ptr; - } -#endif - - if ( select_menu_item(aq_data, "REVIEW") != true ) { - LOG(ALLB_LOG, LOG_WARNING, "Could not select REVIEW menu\n"); - LOG(ALLB_LOG, LOG_ERR, "Can't get freeze setpoints from Control Panel\n"); - cancel_menu(); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - - if (select_sub_menu_item(aq_data, "FRZ PROTECT") != true) { - LOG(ALLB_LOG, LOG_WARNING, "Could not select FRZ PROTECT menu\n"); - LOG(ALLB_LOG, LOG_ERR, "Can't get freeze setpoints from Control Panel\n"); - cancel_menu(); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - - waitForMessage(aq_data, "FREEZE PROTECTION IS SET TO", 6); // Changed from 3 to wait for multiple items to be listed - //cancel_menu(aq_data); - cleanAndTerminateThread(threadCtrl); - - // just stop compiler error, ptr is not valid as it's just been freed - return ptr; -} - -bool get_aqualink_program_for_button(struct aqualinkdata *aq_data, unsigned char cmd) -{ - int rtnStringsWait = 7; - - if (! waitForMessage(aq_data, "SELECT DEVICE TO REVIEW or PRESS ENTER TO END", rtnStringsWait)) - return false; - - LOG(ALLB_LOG, LOG_DEBUG, "Send key '%d'\n",cmd); - send_cmd(cmd); - return waitForEitherMessage(aq_data, "NOT SET", "TURNS ON", rtnStringsWait); -} - -void *get_aqualink_programs( void *ptr ) -{ - struct programmingThreadCtrl *threadCtrl; - threadCtrl = (struct programmingThreadCtrl *) ptr; - struct aqualinkdata *aq_data = threadCtrl->aq_data; - char keys[10] = {KEY_PUMP, KEY_SPA, KEY_AUX1, KEY_AUX2, KEY_AUX3, KEY_AUX4, KEY_AUX5}; // KEY_AUX6 & KEY_AUX7 kill programming mode - - waitForSingleThreadOrTerminate(threadCtrl, AQ_GET_PROGRAMS); - - if ( select_menu_item(aq_data, "REVIEW") != true ) { - //LOG(ALLB_LOG, LOG_WARNING, "Could not select REVIEW menu\n"); - cancel_menu(); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - - if (select_sub_menu_item(aq_data, "PROGRAMS") != true) { - //LOG(ALLB_LOG, LOG_WARNING, "Could not select PROGRAMS menu\n"); - cancel_menu(); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - - unsigned int i; - - for (i=0; i < strlen(keys); i++) { - //LOG(ALLB_LOG, LOG_DEBUG, "**** START program for key in loop %d\n",i); - if (! get_aqualink_program_for_button(aq_data, keys[i])) { - //LOG(ALLB_LOG, LOG_DEBUG, "**** Didn't find program for key in loop %d\n",i); - //cancel_menu(aq_data); - cleanAndTerminateThread(threadCtrl); - return ptr; - } - //LOG(ALLB_LOG, LOG_DEBUG, "**** Found program for key in loop %d\n",i); - } - - - if (waitForMessage(aq_data, "SELECT DEVICE TO REVIEW or PRESS ENTER TO END", 6)) { - //LOG(ALLB_LOG, LOG_DEBUG, "Send key ENTER\n"); - send_cmd(KEY_ENTER); - } else { - //LOG(ALLB_LOG, LOG_DEBUG, "Send CANCEL\n"); - cancel_menu(); - } - - //cancel_menu(aq_data); - cleanAndTerminateThread(threadCtrl); - - // just stop compiler error, ptr is not valid as it's just been freed - return ptr; -} - -/* -void send_cmd(unsigned char cmd, struct aqualinkdata *aq_data) -{ - push_aq_cmd(cmd); - LOG(ALLB_LOG, LOG_INFO, "Queue send '0x%02hhx' to controller\n", cmd); -} -*/ - - - -void _waitfor_queue2empty(bool longwait) -{ - int i=0; - - if (_pgm_command != NUL) { - LOG(ALLB_LOG, LOG_DEBUG, "Waiting for queue to empty\n"); - } else { - LOG(ALLB_LOG, LOG_DEBUG, "Queue empty!\n"); - return; - } - - while ( (_pgm_command != NUL) && ( i++ < (PROGRAMMING_POLL_COUNTER*(longwait?2:1) ) ) ) { - delay(PROGRAMMING_POLL_DELAY_TIME); - } - -/* - //while ( (_pgm_command != NUL) && ( i++ < (30*(longwait?2:1) ) ) ) { - while ( (_pgm_command != NUL) && ( i++ < (50*(longwait?2:1) ) ) ) { - //sleep(1); // NSF Change to smaller time. - //LOG(ALLB_LOG, LOG_DEBUG, "******** QUEUE IS FULL ******** delay\n"); - delay(50); - } -*/ - if (_pgm_command != NUL) { - #ifdef AQ_PDA - if (isPDA_PANEL) { - // Wait for longer in PDA mode since it's slower. - while ( (_pgm_command != NUL) && ( i++ < (150*(longwait?2:1)) ) ) { - delay(100); - } - } - #endif - LOG(ALLB_LOG, LOG_WARNING, "Send command Queue did not empty, timeout\n"); - } else { - LOG(ALLB_LOG, LOG_DEBUG, "Queue now empty!\n"); - } - -} - -void waitfor_queue2empty() -{ - _waitfor_queue2empty(false); -} -void longwaitfor_queue2empty() -{ - _waitfor_queue2empty(true); -} - -void send_cmd(unsigned char cmd) -{ - waitfor_queue2empty(); - - _pgm_command = cmd; - //delay(200); - - LOG(ALLB_LOG, LOG_INFO, "Queue send '0x%02hhx' to controller (programming)\n", _pgm_command); -} -void force_queue_delete() -{ - if (_pgm_command != NUL) - LOG(ALLB_LOG, LOG_INFO, "Really bad coding, don't use this in release\n"); - - _pgm_command = NUL; -} - -/* -void send_cmd(unsigned char cmd, struct aqualinkdata *aq_data) -{ - int i=0; - // If there is an unsent command, wait. - while ( (aq_data->aq_command != NUL) && ( i++ < 10) ) { - //sleep(1); // NSF Change to smaller time. - delay(500); - } - - aq_data->aq_command = cmd; - //delay(200); - - LOG(ALLB_LOG, LOG_INFO, "Queue send '0x%02hhx' to controller\n", aq_data->aq_command); -} -*/ - -void cancel_menu() -{ - send_cmd(KEY_CANCEL); -} - -/* -* added functionality, if start of string is ^ use that as must start with in comparison -*/ - -bool waitForEitherMessage(struct aqualinkdata *aq_data, char* message1, char* message2, int numMessageReceived) -{ - //LOG(ALLB_LOG, LOG_DEBUG, "waitForMessage %s %d %d\n",message,numMessageReceived,cmd); - waitfor_queue2empty(); // MAke sure the last command was sent - int i=0; - pthread_mutex_lock(&aq_data->active_thread.thread_mutex); - char* msgS1 = ""; - char* msgS2 = ""; - char* ptr = NULL; - - - if (message1 != NULL) { - if (message1[0] == '^') - msgS1 = &message1[1]; - else - msgS1 = message1; - } - if (message2 != NULL) { - if (message2[0] == '^') - msgS2 = &message2[1]; - else - msgS2 = message2; - } - - while( ++i <= numMessageReceived) - { - LOG(ALLB_LOG, LOG_DEBUG, "loop %d of %d looking for '%s' OR '%s' received message1 '%s'\n",i,numMessageReceived,message1,message2,aq_data->last_message); - - if (message1 != NULL) { - ptr = stristr(aq_data->last_message, msgS1); - if (ptr != NULL) { // match - LOG(ALLB_LOG, LOG_DEBUG, "String MATCH '%s'\n", msgS1); - if (msgS1 == message1) // match & don't care if first char - break; - else if (ptr == aq_data->last_message) // match & do care if first char - break; - } - } - if (message2 != NULL) { - ptr = stristr(aq_data->last_message, msgS2); - if (ptr != NULL) { // match - LOG(ALLB_LOG, LOG_DEBUG, "String MATCH '%s'\n", msgS2); - if (msgS2 == message2) // match & don't care if first char - break; - else if (ptr == aq_data->last_message) // match & do care if first char - break; - } - } - - //LOG(ALLB_LOG, LOG_DEBUG, "looking for '%s' received message1 '%s'\n",message1,aq_data->last_message); - pthread_cond_wait(&aq_data->active_thread.thread_cond, &aq_data->active_thread.thread_mutex); - //LOG(ALLB_LOG, LOG_DEBUG, "loop %d of %d looking for '%s' received message1 '%s'\n",i,numMessageReceived,message1,aq_data->last_message); - } - - pthread_mutex_unlock(&aq_data->active_thread.thread_mutex); - - if (message1 != NULL && message2 != NULL && ptr == NULL) { - //logmessage1(LOG_ERR, "Could not select MENU of Aqualink control panel\n"); - LOG(ALLB_LOG, LOG_ERR, "Did not find '%s'\n",message1); - return false; - } - LOG(ALLB_LOG, LOG_DEBUG, "Found message1 '%s' or '%s' in '%s'\n",message1,message2,aq_data->last_message); - - return true; -} - - - -bool waitForMessage(struct aqualinkdata *aq_data, char* message, int numMessageReceived) -{ - LOG(ALLB_LOG, LOG_DEBUG, "waitForMessage %s %d\n",message,numMessageReceived); - // NSF Need to come back to this, as it stops on test enviornment but not real panel, so must be speed related. - //waitfor_queue2empty(); // MAke sure the last command was sent - - int i=0; - pthread_mutex_lock(&aq_data->active_thread.thread_mutex); - char* msgS; - char* ptr = NULL; - - if (message != NULL) { - if (message[0] == '^') - msgS = &message[1]; - else - msgS = message; - } - - while( ++i <= numMessageReceived) - { - if (message != NULL) - LOG(ALLB_LOG, LOG_DEBUG, "loop %d of %d looking for '%s', last message received '%s'\n",i,numMessageReceived,message,aq_data->last_message); - else - LOG(ALLB_LOG, LOG_DEBUG, "loop %d of %d waiting for next message, last message received '%s'\n",i,numMessageReceived,aq_data->last_message); - - if (message != NULL) { - ptr = stristr(aq_data->last_message, msgS); - if (ptr != NULL) { // match - LOG(ALLB_LOG, LOG_DEBUG, "String MATCH\n"); - if (msgS == message) // match & don't care if first char - break; - else if (ptr == aq_data->last_message) // match & do care if first char - break; - } - } - - //LOG(ALLB_LOG, LOG_DEBUG, "looking for '%s' received message '%s'\n",message,aq_data->last_message); - //LOG(ALLB_LOG, LOG_DEBUG, "*** pthread_cond_wait() sleep\n"); - pthread_cond_wait(&aq_data->active_thread.thread_cond, &aq_data->active_thread.thread_mutex); - //LOG(ALLB_LOG, LOG_DEBUG, "*** pthread_cond_wait() wake\n"); - //LOG(ALLB_LOG, LOG_DEBUG, "loop %d of %d looking for '%s' received message '%s'\n",i,numMessageReceived,message,aq_data->last_message); - } - - pthread_mutex_unlock(&aq_data->active_thread.thread_mutex); - - if (message != NULL && ptr == NULL) { - //LOG(ALLB_LOG, LOG_ERR, "Could not select MENU of Aqualink control panel\n"); - LOG(ALLB_LOG, LOG_DEBUG, "did not find '%s'\n",message); - return false; - } else if (message != NULL) - LOG(ALLB_LOG, LOG_DEBUG, "found message '%s' in '%s'\n",message,aq_data->last_message); - else - LOG(ALLB_LOG, LOG_DEBUG, "waited for %d message(s)\n",numMessageReceived); - - return true; -} - -bool select_menu_item(struct aqualinkdata *aq_data, char* item_string) -{ - char* expectedMsg = "PRESS ENTER* TO SELECT"; - //char* expectedMsg = "PROGRAM"; - int wait_messages = 5; - bool found = false; - int tries = 0; - // Select the MENU and wait to get the RS8 respond. - - while (found == false && tries <= 3) { - send_cmd(KEY_MENU); - found = waitForMessage(aq_data, expectedMsg, wait_messages); - tries++; - } - - if (found == false) - return false; - - // NSF This isn't needed and seems to cause issue on some controllers. - //send_cmd(KEY_ENTER, aq_data); - //waitForMessage(aq_data, NULL, 1); - - return select_sub_menu_item(aq_data, item_string); -} -/* -bool select_menu_item(struct aqualinkdata *aq_data, char* item_string) -{ - char* expectedMsg = "PRESS ENTER* TO SELECT"; - int wait_messages = 6; - //int i=0; - - // Select the MENU and wait to get the RS8 respond. - send_cmd(KEY_MENU, aq_data); - - if (waitForMessage(aq_data, expectedMsg, wait_messages) == false) - return false; - - send_cmd(KEY_ENTER, aq_data); - waitForMessage(aq_data, NULL, 1); - - // Blindly wait for next message - //sendCmdWaitForReturn(aq_data, KEY_ENTER); - // Can't determin the first response - //delay(500); - - return select_sub_menu_item(aq_data, item_string); -} -*/ - -//bool select_sub_menu_item(char* item_string, struct aqualinkdata *aq_data) -bool select_sub_menu_item(struct aqualinkdata *aq_data, char* item_string) -{ - int wait_messages = 28; - int i=0; - - waitfor_queue2empty(); - - while( (stristr(aq_data->last_message, item_string) == NULL) && ( i++ < wait_messages) ) - { - LOG(ALLB_LOG, LOG_DEBUG, "Find item in Menu: loop %d of %d looking for '%s' received message '%s'\n",i,wait_messages,item_string,aq_data->last_message); - send_cmd(KEY_RIGHT); - waitfor_queue2empty(); // ADDED BACK MAY 2023 setting time warked better - //waitForMessage(aq_data, NULL, 1); - waitForMessage(aq_data, item_string, 1); - } - - if (stristr(aq_data->last_message, item_string) == NULL) { - LOG(ALLB_LOG, LOG_ERR, "Could not find menu item '%s'\n",item_string); - return false; - } - - LOG(ALLB_LOG, LOG_DEBUG, "Find item in Menu: loop %d of %d FOUND menu item '%s', sending ENTER command\n",i,wait_messages, item_string); - // Enter the mode specified by the argument. - - - send_cmd(KEY_ENTER); - - - waitForMessage(aq_data, NULL, 1); - - - //sendCmdWaitForReturn(aq_data, KEY_ENTER); - - return true; - -} - -// NSF Need to test this, then use it for the color change mode. - -bool waitForButtonState(struct aqualinkdata *aq_data, aqkey* button, aqledstate state, int numMessageReceived) -{ - //LOG(ALLB_LOG, LOG_DEBUG, "waitForMessage %s %d %d\n",message,numMessageReceived,cmd); - int i=0; - pthread_mutex_lock(&aq_data->active_thread.thread_mutex); - - while( ++i <= numMessageReceived) - { - LOG(ALLB_LOG, LOG_DEBUG, "loop %d of %d looking for state change to '%d' for '%s' \n",i,numMessageReceived,button->led->state,button->name); - - if (button->led->state == state) { - LOG(ALLB_LOG, LOG_INFO, "Button State =%d for %s\n",state,button->name); - break; - } - - //LOG(ALLB_LOG, LOG_DEBUG, "looking for '%s' received message '%s'\n",message,aq_data->last_message); - pthread_cond_wait(&aq_data->active_thread.thread_cond, &aq_data->active_thread.thread_mutex); - //LOG(ALLB_LOG, LOG_DEBUG, "loop %d of %d looking for '%s' received message '%s'\n",i,numMessageReceived,message,aq_data->last_message); - } - - pthread_mutex_unlock(&aq_data->active_thread.thread_mutex); - - if (numMessageReceived >= i) { - //LOG(ALLB_LOG, LOG_ERR, "Could not select MENU of Aqualink control panel\n"); - LOG(ALLB_LOG, LOG_DEBUG, "did not find state '%d' for '%s'\n",button->led->state,button->name); - return false; - } - LOG(ALLB_LOG, LOG_DEBUG, "found state '%d' for '%s'\n",button->led->state,button->name); - - return true; -} - -const char *ptypeName(program_type type) -{ - switch (type) { - case AQ_GET_POOL_SPA_HEATER_TEMPS: - return "Get Heater setpoints"; - break; + break; + + } + + if ( programmingthread->thread_id != 0 ) { + //LOG(PROG_LOG, LOG_DEBUG, "********* DID pthread_detach %d\n",programmingthread->thread_id); + pthread_detach(programmingthread->thread_id); + } else { + //LOG(PROG_LOG, LOG_DEBUG, "********* DID NOT pthread_detach\n"); + } +} + + +void waitForSingleThreadOrTerminate(struct programmingThreadCtrl *threadCtrl, program_type type) +{ + //static int tries = 120; + int tries = 120; + static int waitTime = 1; + int i=0; +/* + i = 0; + while (get_aq_cmd_length() > 0 && ( i++ <= tries) ) { + LOG(PROG_LOG, LOG_DEBUG, "Thread %p (%s) sleeping, waiting command queue to empty\n", &threadCtrl->thread_id, ptypeName(type)); + sleep(waitTime); + } + if (i >= tries) { + LOG(PROG_LOG, LOG_ERR, "Thread %p (%s) timeout waiting, ending\n",&threadCtrl->thread_id,ptypeName(type)); + free(threadCtrl); + pthread_exit(0); + } +*/ + while ( (threadCtrl->aq_data->active_thread.thread_id != 0) && ( i++ <= tries) ) { + //LOG(PROG_LOG, LOG_DEBUG, "Thread %d sleeping, waiting for thread %d to finish\n", threadCtrl->thread_id, threadCtrl->aq_data->active_thread.thread_id); + LOG(PROG_LOG, LOG_DEBUG, "Thread %p (%s) sleeping, waiting for thread %p (%s) to finish\n", + &threadCtrl->thread_id, ptypeName(type), + threadCtrl->aq_data->active_thread.thread_id, ptypeName(threadCtrl->aq_data->active_thread.ptype)); + sleep(waitTime); + } + + if (i >= tries) { + //LOG(PROG_LOG, LOG_ERR, "Thread %d timeout waiting, ending\n",threadCtrl->thread_id); + LOG(PROG_LOG, LOG_ERR, "Thread (%s) %p timeout waiting for thread (%s) %p to finish\n", + ptypeName(type), &threadCtrl->thread_id, ptypeName(threadCtrl->aq_data->active_thread.ptype), + threadCtrl->aq_data->active_thread.thread_id); + free(threadCtrl); + pthread_exit(0); + } + + // Clear out any messages to the UI. + threadCtrl->aq_data->last_display_message[0] = '\0'; + threadCtrl->aq_data->active_thread.thread_id = &threadCtrl->thread_id; + threadCtrl->aq_data->active_thread.ptype = type; + + #ifdef AQ_DEBUG + clock_gettime(CLOCK_REALTIME, &threadCtrl->aq_data->start_active_time); + #endif + + LOG(PROG_LOG, LOG_INFO, "Programming: %s, %d\n", ptypeName(threadCtrl->aq_data->active_thread.ptype), threadCtrl->aq_data->active_thread.ptype); + + LOG(PROG_LOG, LOG_DEBUG, "Thread %d,%p is active (%s)\n", + threadCtrl->aq_data->active_thread.ptype, + threadCtrl->aq_data->active_thread.thread_id, + ptypeName(threadCtrl->aq_data->active_thread.ptype)); +} + +void cleanAndTerminateThread(struct programmingThreadCtrl *threadCtrl) +{ + //waitfor_queue2empty(); + #ifndef AQ_DEBUG + LOG(PROG_LOG, LOG_DEBUG, "Thread %d,%p (%s) finished\n",threadCtrl->aq_data->active_thread.ptype, threadCtrl->thread_id,ptypeName(threadCtrl->aq_data->active_thread.ptype)); + #else + struct timespec elapsed; + clock_gettime(CLOCK_REALTIME, &threadCtrl->aq_data->last_active_time); + timespec_subtract(&elapsed, &threadCtrl->aq_data->last_active_time, &threadCtrl->aq_data->start_active_time); + LOG(PROG_LOG, LOG_NOTICE, "Thread %d,%p (%s) finished in %d.%03ld sec\n", + threadCtrl->aq_data->active_thread.ptype, + threadCtrl->aq_data->active_thread.thread_id, + ptypeName(threadCtrl->aq_data->active_thread.ptype), + elapsed.tv_sec, elapsed.tv_nsec / 1000000L); + #endif + + // Quick delay to allow for last message to be sent. + delay(500); + threadCtrl->aq_data->active_thread.thread_id = 0; + threadCtrl->aq_data->active_thread.ptype = AQP_NULL; + threadCtrl->thread_id = 0; + // Force update, change display message + threadCtrl->aq_data->updated = true; + free(threadCtrl); + pthread_exit(0); +} + + + + +const char *ptypeName(program_type type) +{ + switch (type) { + case AQ_GET_POOL_SPA_HEATER_TEMPS: + return "Get Heater setpoints"; + break; case AQ_GET_FREEZE_PROTECT_TEMP: return "Get Freeze protect"; break; @@ -2647,6 +873,33 @@ const char *ptypeName(program_type type) case AQ_PDA_WAKE_INIT: return "PDA init after wake"; break; + case AQ_PDA_SET_BOOST: + return "Set PDA SWG Boost"; + break; + case AQ_PDA_SET_SWG_PERCENT: + return "Set PDA SWG Percent"; + break; + case AQ_PDA_GET_AUX_LABELS: + return "Get PDA Lebels"; + break; + case AQ_PDA_SET_POOL_HEATER_TEMPS: + return "Set PDA Pool Heater"; + break; + case AQ_PDA_SET_SPA_HEATER_TEMPS: + return "Set PDA Spa Heater"; + break; + case AQ_PDA_SET_FREEZE_PROTECT_TEMP: + return "Set PDA Freese protect"; + break; + case AQ_PDA_SET_TIME: + return "Set PDA time"; + break; + case AQ_PDA_GET_POOL_SPA_HEATER_TEMPS: + return "Get PDA heater setpoints"; + break; + case AQ_PDA_GET_FREEZE_PROTECT_TEMP: + return "Get PDA freeze protect"; + break; #endif case AQP_NULL: default: diff --git a/aq_programmer.h b/aq_programmer.h index de93a31..87d9d60 100644 --- a/aq_programmer.h +++ b/aq_programmer.h @@ -41,6 +41,7 @@ typedef enum emulation_type{ typedef enum { AQP_NULL = -1, + // ********* Generic Programming options, these are Allbutton by Default AQ_GET_POOL_SPA_HEATER_TEMPS, AQ_GET_FREEZE_PROTECT_TEMP, AQ_SET_TIME, @@ -48,24 +49,30 @@ typedef enum { AQ_SET_SPA_HEATER_TEMP, AQ_SET_FRZ_PROTECTION_TEMP, AQ_GET_DIAGNOSTICS_MODEL, - //AQ_SEND_CMD, AQ_GET_PROGRAMS, AQ_SET_LIGHTPROGRAM_MODE, AQ_SET_LIGHTCOLOR_MODE, AQ_SET_SWG_PERCENT, - AQ_PDA_DEVICE_STATUS, - AQ_PDA_DEVICE_ON_OFF, AQ_GET_AUX_LABELS, AQ_SET_BOOST, AQ_SET_PUMP_RPM, AQ_SET_PUMP_VS_PROGRAM, - // ******** Delimiter make sure to change MAX/MIN below - // NSF Need to add Specific ALL Button ones here - // ******** Delimiter make sure to change MAX/MIN below - // NSF Need to add ALL Specific PDA ones here + // ******** PDA Delimiter make sure to change MAX/MIN below AQ_PDA_INIT, AQ_PDA_WAKE_INIT, - // ******** Delimiter make sure to change MAX/MIN below + AQ_PDA_DEVICE_STATUS, + AQ_PDA_DEVICE_ON_OFF, + AQ_PDA_AUX_LABELS, + AQ_PDA_SET_BOOST, + AQ_PDA_SET_SWG_PERCENT, + AQ_PDA_GET_AUX_LABELS, + AQ_PDA_SET_POOL_HEATER_TEMPS, + AQ_PDA_SET_SPA_HEATER_TEMPS, + AQ_PDA_SET_FREEZE_PROTECT_TEMP, + AQ_PDA_SET_TIME, + AQ_PDA_GET_POOL_SPA_HEATER_TEMPS, + AQ_PDA_GET_FREEZE_PROTECT_TEMP, + // ******** OneTouch Delimiter make sure to change MAX/MIN below AQ_SET_ONETOUCH_PUMP_RPM, AQ_SET_ONETOUCH_MACRO, AQ_GET_ONETOUCH_SETPOINTS, @@ -76,7 +83,7 @@ typedef enum { AQ_SET_ONETOUCH_TIME, AQ_SET_ONETOUCH_BOOST, AQ_SET_ONETOUCH_SWG_PERCENT, - // ******** Delimiter make sure to change MAX/MIN below + // ******** iAqalink Touch Delimiter make sure to change MAX/MIN below AQ_SET_IAQTOUCH_PUMP_RPM, AQ_SET_IAQTOUCH_PUMP_VS_PROGRAM, AQ_GET_IAQTOUCH_VSP_ASSIGNMENT, @@ -91,7 +98,7 @@ typedef enum { AQ_SET_IAQTOUCH_SET_TIME, AQ_SET_IAQTOUCH_DEVICE_ON_OFF, AQ_SET_IAQTOUCH_LIGHTCOLOR_MODE, - // ******** Delimiter make sure to change MAX/MIN below + // ******** RS Serial Adapter Delimiter make sure to change MAX/MIN below AQ_GET_RSSADAPTER_SETPOINTS, AQ_SET_RSSADAPTER_POOL_HEATER_TEMP, AQ_SET_RSSADAPTER_SPA_HEATER_TEMP, @@ -103,11 +110,11 @@ typedef enum { #define AQP_GENERIC_MIN AQ_GET_POOL_SPA_HEATER_TEMPS #define AQP_GENERIC_MAX AQ_SET_PUMP_VS_PROGRAM -#define AQP_ALLBUTTON_MIN AQP_NULL -#define AQP_ALLBUTTONL_MAX AQP_NULL +#define AQP_ALLBUTTON_MIN AQ_GET_POOL_SPA_HEATER_TEMPS +#define AQP_ALLBUTTONL_MAX AQ_SET_BOOST #define AQP_PDA_MIN AQ_PDA_INIT -#define AQP_PDA_MAX AQ_PDA_WAKE_INIT +#define AQP_PDA_MAX AQ_SET_ONETOUCH_SWG_PERCENT #define AQP_ONETOUCH_MIN AQ_SET_ONETOUCH_PUMP_RPM #define AQP_ONETOUCH_MAX AQ_SET_ONETOUCH_SWG_PERCENT @@ -145,10 +152,10 @@ bool in_iaqt_programming_mode(struct aqualinkdata *aq_data); bool in_swg_programming_mode(struct aqualinkdata *aq_data); bool in_light_programming_mode(struct aqualinkdata *aq_data); bool in_allb_programming_mode(struct aqualinkdata *aq_data); -void aq_send_cmd(unsigned char cmd); +//void aq_send_cmd(unsigned char cmd); void queueGetProgramData(emulation_type source_type, struct aqualinkdata *aq_data); //void queueGetExtendedProgramData(emulation_type source_type, struct aqualinkdata *aq_data, bool labels); -unsigned char pop_aq_cmd(struct aqualinkdata *aq_data); +//unsigned char pop_aq_cmd(struct aqualinkdata *aq_data); void waitForSingleThreadOrTerminate(struct programmingThreadCtrl *threadCtrl, program_type type); void cleanAndTerminateThread(struct programmingThreadCtrl *threadCtrl); @@ -164,7 +171,7 @@ void cleanAndTerminateThread(struct programmingThreadCtrl *threadCtrl); //void *set_aqualink_time( void *ptr ); //void *get_aqualink_pool_spa_heater_temps( void *ptr ); -int get_aq_cmd_length(); +//int get_aq_cmd_length(); int setpoint_check(int type, int value, struct aqualinkdata *aqdata); int RPM_check(pump_type type, int value, struct aqualinkdata *aqdata); //int RPM_check(int type, int value, struct aqualinkdata *aqdata); diff --git a/aq_serial.c b/aq_serial.c index 55d5820..6801106 100644 --- a/aq_serial.c +++ b/aq_serial.c @@ -61,7 +61,68 @@ static struct timespec last_serial_read_time; void send_packet(int fd, unsigned char *packet, int length); //unsigned char getProtocolType(unsigned char* packet); +emulation_type getJandyDeviceType(unsigned char ID) { + + // Using emulation_type from aqprogrammer. At some point may merge into one + // and call device type + + if (ID >= 0x08 && ID <= 0x0B) + return ALLBUTTON; + if (ID >= 0x40 && ID <= 0x43) + return ONETOUCH; + if (ID >= 0x48 && ID <= 0x4B) + return RSSADAPTER; + if (ID >= 0x60 && ID <= 0x63) + return AQUAPDA; + if (ID >= 0x30 && ID <= 0x33) + return IAQTOUCH; +/* + if (ID >= 0x00 && ID <= 0x03) + return MASTER; + if (ID >= 0x50 && ID <= 0x53) + return SWG; + if (ID >= 0x20 && ID <= 0x23) + return SPA_R; + if (ID >= 0x38 && ID <= 0x3B) + return LX_HEATER; + if (ID >= 0x58 && ID <= 0x5B) + return PC_DOCK; + if (ID >= 0x68 && ID <= 0x6B) + return JXI_HEATER; + //if (ID >= 0x70 && ID <= 0x73) + if (ID >= 0x78 && ID <= 0x7B) + return EPUMP; + if (ID >= 0x80 && ID <= 0x83) + return CHEM; + //if (ID == 0x08) + // return KEYPAD; +*/ + return SIM_NONE; +} + +const char *getJandyDeviceName(emulation_type etype) { + switch(etype){ + case ALLBUTTON: + return "AllButton"; + break; + case ONETOUCH: + return "OneTouch"; + break; + case RSSADAPTER: + return "RS SerialAdapter"; + break; + case IAQTOUCH: + return "iAqualinkTouch"; + break; + case AQUAPDA: + return "PDA"; + break; + default: + return "Unknown"; + break; + } +} const char* get_pentair_packet_type(unsigned char* packet , int length) { diff --git a/aq_serial.h b/aq_serial.h index a2bac42..bdda06f 100644 --- a/aq_serial.h +++ b/aq_serial.h @@ -5,6 +5,10 @@ #include #include +#include "aq_programmer.h" // Need this for function getJandyDeviceType due to enum defined their. +emulation_type getJandyDeviceType(unsigned char ID); +const char *getJandyDeviceName(emulation_type etype); + #define CONNECTION_ERROR "ERROR No connection to RS control panel" #ifdef AQ_MANAGER #define CONNECTION_RUNNING_SLOG "Running serial_logger, this will take some time" diff --git a/aq_timer.c b/aq_timer.c index 9f1db6e..69161cd 100644 --- a/aq_timer.c +++ b/aq_timer.c @@ -136,9 +136,7 @@ void *timer_worker( void *ptr ) if ((tmthread->button->special_mask & PROGRAM_LIGHT) == PROGRAM_LIGHT && in_light_programming_mode(tmthread->aq_data)) { LOG(TIMR_LOG, LOG_NOTICE, "Not turning on '%s' as programmer is\n",tmthread->button->name); } else { - // crap way to do this, need to use net_service logic in teh future, but should never actually get here LOG(TIMR_LOG, LOG_NOTICE, "turning on '%s'\n",tmthread->button->name); - //aq_send_cmd(tmthread->button->code); panel_device_request(tmthread->aq_data, ON_OFF, tmthread->deviceIndex, false, NET_TIMER); } } @@ -166,15 +164,7 @@ void *timer_worker( void *ptr ) if (tmthread->button->led->state != OFF) { LOG(TIMR_LOG, LOG_INFO, "Timer waking turning '%s' off\n",tmthread->button->name); -/* -#ifdef AQ_PDA - if (isPDA_PANEL) - create_PDA_on_off_request(tmthread->button, false); - else -#endif - aq_send_cmd(tmthread->button->code); -*/ - panel_device_request(tmthread->aq_data, ON_OFF, tmthread->deviceIndex, false, NET_TIMER); + panel_device_request(tmthread->aq_data, ON_OFF, tmthread->deviceIndex, false, NET_TIMER); } else { LOG(TIMR_LOG, LOG_INFO, "Timer waking '%s' is already off\n",tmthread->button->name); } diff --git a/aqualink.h b/aqualink.h index f63e328..ce8d64a 100644 --- a/aqualink.h +++ b/aqualink.h @@ -33,7 +33,7 @@ // The below will change state of devices before that are actually set on the control panel, this helps // with duplicate messages that come in quick succession that can catch the state before it happens. -#define PRESTATE_ONOFF +//#define PRESTATE_ONOFF #define PRESTATE_SWG_SETPOINT //#define PRESTATE_HEATER_SETPOINT // This one is not implimented yet diff --git a/aqualinkd.c b/aqualinkd.c index de048e5..4ccff97 100644 --- a/aqualinkd.c +++ b/aqualinkd.c @@ -43,6 +43,8 @@ #include "pda_aq_programmer.h" #include "packetLogger.h" #include "devices_jandy.h" +#include "allbutton.h" +#include "allbutton_aq_programmer.h" #include "onetouch.h" #include "onetouch_aq_programmer.h" #include "iaqtouch.h" @@ -125,44 +127,7 @@ void intHandler(int sig_num) #endif } -void processLEDstate() -{ - - int i = 0; - int byte; - int bit; - - for (byte = 0; byte < 5; byte++) - { - for (bit = 0; bit < 8; bit += 2) - { - if (((_aqualink_data.raw_status[byte] >> (bit + 1)) & 1) == 1) - _aqualink_data.aqualinkleds[i].state = FLASH; - else if (((_aqualink_data.raw_status[byte] >> bit) & 1) == 1) - _aqualink_data.aqualinkleds[i].state = ON; - else - _aqualink_data.aqualinkleds[i].state = OFF; - - //LOG(AQUA_LOG,LOG_DEBUG,"Led %d state %d",i+1,_aqualink_data.aqualinkleds[i].state); - i++; - } - } - // Reset enabled state for heaters, as they take 2 led states - if (_aqualink_data.aqualinkleds[POOL_HTR_LED_INDEX - 1].state == OFF && _aqualink_data.aqualinkleds[POOL_HTR_LED_INDEX].state == ON) - _aqualink_data.aqualinkleds[POOL_HTR_LED_INDEX - 1].state = ENABLE; - - if (_aqualink_data.aqualinkleds[SPA_HTR_LED_INDEX - 1].state == OFF && _aqualink_data.aqualinkleds[SPA_HTR_LED_INDEX].state == ON) - _aqualink_data.aqualinkleds[SPA_HTR_LED_INDEX - 1].state = ENABLE; - - if (_aqualink_data.aqualinkleds[SOLAR_HTR_LED_INDEX - 1].state == OFF && _aqualink_data.aqualinkleds[SOLAR_HTR_LED_INDEX].state == ON) - _aqualink_data.aqualinkleds[SOLAR_HTR_LED_INDEX - 1].state = ENABLE; - /* - for (i=0; i < TOTAL_BUTTONS; i++) { - LOG(AQUA_LOG,LOG_NOTICE, "%s = %d", _aqualink_data.aqbuttons[i].name, _aqualink_data.aqualinkleds[i].state); - } -*/ -} - +// Should move to panel. bool checkAqualinkTime() { static time_t last_checked = 0; @@ -270,672 +235,6 @@ bool checkAqualinkTime() } -void setUnits(char *msg) -{ - char buf[AQ_MSGLEN*3]; - - rsm_strncpy(buf, (unsigned char *)msg, AQ_MSGLEN*3, AQ_MSGLONGLEN); - - //ascii(buf, msg); - LOG(AQUA_LOG,LOG_DEBUG, "Getting temp units from message '%s', looking at '%c'\n", buf, buf[strlen(buf) - 1]); - - if (msg[strlen(msg) - 1] == 'F') - _aqualink_data.temp_units = FAHRENHEIT; - else if (msg[strlen(msg) - 1] == 'C') - _aqualink_data.temp_units = CELSIUS; - else - _aqualink_data.temp_units = UNKNOWN; - - LOG(AQUA_LOG,LOG_INFO, "Temp Units set to %d (F=0, C=1, Unknown=2)\n", _aqualink_data.temp_units); -} - -// Defined as int16_t so 16 bits to mask -#define MSG_FREEZE (1 << 0) // 1 -#define MSG_SERVICE (1 << 1) // 1 -#define MSG_SWG (1 << 2) -#define MSG_BOOST (1 << 3) -#define MSG_TIMEOUT (1 << 4) -#define MSG_RS13BUTTON (1 << 5) -#define MSG_RS14BUTTON (1 << 6) -#define MSG_RS15BUTTON (1 << 7) -#define MSG_RS16BUTTON (1 << 8) -#define MSG_BATTERY_LOW (1 << 9) -#define MSG_SWG_DEVICE (1 << 10) -/* -#define SET_FLAG(n, f) ((n) |= (f)) -#define CHK_FLAG(n, f) ((n) & (f)) -*/ -#ifdef AQ_RS16 -int16_t RS16_endswithLEDstate(char *msg) -{ - char *sp; - int i; - aqledstate state = LED_S_UNKNOWN; - - //if (_aqconfig_.rs_panel_size < 16) - if (PANEL_SIZE() < 16) - return false; - - sp = strrchr(msg, ' '); - - if( sp == NULL ) - return false; - - if (strncasecmp(sp, " on", 3) == 0) - state = ON; - else if (strncasecmp(sp, " off", 4) == 0) - state = OFF; - else if (strncasecmp(sp, " enabled", 8) == 0) // Total guess, need to check - state = ENABLE; - else if (strncasecmp(sp, " no idea", 8) == 0) // need to figure out these states - state = FLASH; - - if (state == LED_S_UNKNOWN) - return false; - - // Only need to start at Aux B5->B8 (12-15) - // Loop over only aqdata->aqbuttons[13] to aqdata->aqbuttons[16] - for (i = _aqualink_data.rs16_vbutton_start; i <= _aqualink_data.rs16_vbutton_end; i++) { - //TOTAL_BUTTONS - if ( stristr(msg, _aqualink_data.aqbuttons[i].label) != NULL) { - _aqualink_data.aqbuttons[i].led->state = state; - LOG(AQUA_LOG,LOG_INFO, "Set %s to %d\n", _aqualink_data.aqbuttons[i].label, _aqualink_data.aqbuttons[i].led->state); - // Return true should be the result, but in the if we want to continue to display message - //return true; - if (i == 13) - return MSG_RS13BUTTON; - else if (i == 14) - return MSG_RS14BUTTON; - else if (i == 15) - return MSG_RS15BUTTON; - else if (i == 16) - return MSG_RS16BUTTON; - else - { - LOG(AQUA_LOG,LOG_ERR, "RS16 Button Set error %s to %d, %d is out of scope\n", _aqualink_data.aqbuttons[i].label, _aqualink_data.aqbuttons[i].led->state, i); - return false; - } - - } - } - - return false; -} -#endif - - - -void _processMessage(char *message, bool reset); - -void processMessage(char *message) -{ - _processMessage(message, false); -} -void processMessageReset() -{ - _processMessage(NULL, true); -} -void _processMessage(char *message, bool reset) -{ - char *msg; - static bool _initWithRS = false; - //static bool _gotREV = false; - //static int freeze_msg_count = 0; - //static int service_msg_count = 0; - //static int swg_msg_count = 0; - //static int boost_msg_count = 0; - static int16_t msg_loop = 0; - static aqledstate default_frz_protect_state = OFF; - // NSF replace message with msg -#ifdef AQ_RS16 - int16_t rs16; -#endif - - //msg = stripwhitespace(message); - //strcpy(_aqualink_data.last_message, msg); - //LOG(ALLB_LOG,LOG_INFO, "RS Message :- '%s'\n", msg); - - - - // Check long messages in this if/elseif block first, as some messages are similar. - // ie "POOL TEMP" and "POOL TEMP IS SET TO" so want correct match first. - // - - //if (stristr(msg, "JANDY AquaLinkRS") != NULL) { - if (!reset) { - msg = stripwhitespace(message); - strcpy(_aqualink_data.last_message, msg); - LOG(ALLB_LOG,LOG_INFO, "RS Message :- '%s'\n", msg); - // Just set this to off, it will re-set since it'll be the only message we get if on - _aqualink_data.service_mode_state = OFF; - } else { - //_aqualink_data.display_message = NULL; - _aqualink_data.last_display_message[0] = ' '; - _aqualink_data.last_display_message[1] = '\0'; - - // Anything that wasn't on during the last set of messages, turn off - if ((msg_loop & MSG_FREEZE) != MSG_FREEZE) - _aqualink_data.frz_protect_state = default_frz_protect_state; - - if ((msg_loop & MSG_SERVICE) != MSG_SERVICE && - (msg_loop & MSG_TIMEOUT) != MSG_TIMEOUT ) { - _aqualink_data.service_mode_state = OFF; // IF we get this message then Service / Timeout is off - } - - if ( ((msg_loop & MSG_SWG_DEVICE) != MSG_SWG_DEVICE) && _aqualink_data.swg_led_state != LED_S_UNKNOWN) { - // No Additional SWG devices messages like "no flow" - if ((msg_loop & MSG_SWG) != MSG_SWG && _aqualink_data.aqbuttons[PUMP_INDEX].led->state == OFF ) - setSWGdeviceStatus(&_aqualink_data, ALLBUTTON, SWG_STATUS_OFF); - else - setSWGdeviceStatus(&_aqualink_data, ALLBUTTON, SWG_STATUS_ON); - } - - // If no AQUAPURE message, either (no SWG, it's set 0, or it's off). - if ((msg_loop & MSG_SWG) != MSG_SWG && _aqualink_data.swg_led_state != LED_S_UNKNOWN ) { - if (_aqualink_data.swg_percent != 0 || _aqualink_data.swg_led_state == ON) { - // Something is wrong here. Let's check pump, if on set SWG to 0, if off turn SWE off - if ( _aqualink_data.aqbuttons[PUMP_INDEX].led->state == OFF) { - LOG(ALLB_LOG,LOG_INFO, "No AQUAPURE message in cycle, pump is off so setting SWG to off\n"); - setSWGoff(&_aqualink_data); - } else { - LOG(ALLB_LOG,LOG_INFO, "No AQUAPURE message in cycle, pump is on so setting SWG to 0%%\n"); - changeSWGpercent(&_aqualink_data, 0); - } - } else if (isIAQT_ENABLED == false && isONET_ENABLED == false && READ_RSDEV_SWG == false ) { - //We have no other way to read SWG %=0, so turn SWG on with pump - if ( _aqualink_data.aqbuttons[PUMP_INDEX].led->state == ON) { - LOG(ALLB_LOG,LOG_INFO, "No AQUAPURE message in cycle, pump is off so setting SWG to off\n"); - //changeSWGpercent(&_aqualink_data, 0); - setSWGenabled(&_aqualink_data); - } - } - // NSF Need something to catch startup when SWG=0 so we set it to enabeled. - // when other ways/protocols to detect SWG=0 are turned off. - } - /* - // AQUAPURE=0 we never get that message on ALLBUTTON so don't turn off unless filter pump if off - if ((msg_loop & MSG_SWG) != MSG_SWG && _aqualink_data.aqbuttons[PUMP_INDEX].led->state == OFF ) { - //_aqualink_data.ar_swg_status = SWG_STATUS_OFF; - setSWGoff(&_aqualink_data); - } - */ - if ((msg_loop & MSG_BOOST) != MSG_BOOST) { - _aqualink_data.boost = false; - _aqualink_data.boost_msg[0] = '\0'; - _aqualink_data.boost_duration = 0; - //if (_aqualink_data.swg_percent >= 101) - // _aqualink_data.swg_percent = 0; - } - - if ((msg_loop & MSG_BATTERY_LOW) != MSG_BATTERY_LOW) - _aqualink_data.battery = OK; - -#ifdef AQ_RS16 - //if ( _aqconfig_.rs_panel_size >= 16) { - //if ( (int)PANEL_SIZE >= 16) { // NSF No idea why this fails on RS-4, but it does. Come back and find out why - if ( PANEL_SIZE() >= 16 ) { - //printf("Panel size %d What the fuck am I doing here\n",PANEL_SIZE()); - if ((msg_loop & MSG_RS13BUTTON) != MSG_RS13BUTTON) - _aqualink_data.aqbuttons[13].led->state = OFF; - if ((msg_loop & MSG_RS14BUTTON) != MSG_RS14BUTTON) - _aqualink_data.aqbuttons[14].led->state = OFF; - if ((msg_loop & MSG_RS15BUTTON) != MSG_RS15BUTTON) - _aqualink_data.aqbuttons[15].led->state = OFF; - if ((msg_loop & MSG_RS16BUTTON) != MSG_RS16BUTTON) - _aqualink_data.aqbuttons[16].led->state = OFF; - } -#endif - msg_loop = 0; - return; - } - - if (stristr(msg, LNG_MSG_BATTERY_LOW) != NULL) - { - _aqualink_data.battery = LOW; - msg_loop |= MSG_BATTERY_LOW; - strcpy(_aqualink_data.last_display_message, msg); // Also display the message on web UI - } - else if (stristr(msg, LNG_MSG_POOL_TEMP_SET) != NULL) - { - //LOG(AQUA_LOG,LOG_DEBUG, "**************** pool htr long message: %s", &message[20]); - _aqualink_data.pool_htr_set_point = atoi(message + 20); - - if (_aqualink_data.temp_units == UNKNOWN) - setUnits(msg); - } - else if (stristr(msg, LNG_MSG_SPA_TEMP_SET) != NULL) - { - //LOG(AQUA_LOG,LOG_DEBUG, "spa htr long message: %s", &message[19]); - _aqualink_data.spa_htr_set_point = atoi(message + 19); - - if (_aqualink_data.temp_units == UNKNOWN) - setUnits(msg); - } - else if (stristr(msg, LNG_MSG_FREEZE_PROTECTION_SET) != NULL) - { - //LOG(AQUA_LOG,LOG_DEBUG, "frz protect long message: %s", &message[28]); - _aqualink_data.frz_protect_set_point = atoi(message + 28); - _aqualink_data.frz_protect_state = ENABLE; - default_frz_protect_state = ENABLE; - - if (_aqualink_data.temp_units == UNKNOWN) - setUnits(msg); - } - else if (strncasecmp(msg, MSG_AIR_TEMP, MSG_AIR_TEMP_LEN) == 0) - { - _aqualink_data.air_temp = atoi(msg + MSG_AIR_TEMP_LEN); - - if (_aqualink_data.temp_units == UNKNOWN) - setUnits(msg); - } - else if (strncasecmp(msg, MSG_POOL_TEMP, MSG_POOL_TEMP_LEN) == 0) - { - _aqualink_data.pool_temp = atoi(msg + MSG_POOL_TEMP_LEN); - - if (_aqualink_data.temp_units == UNKNOWN) - setUnits(msg); - } - else if (strncasecmp(msg, MSG_SPA_TEMP, MSG_SPA_TEMP_LEN) == 0) - { - _aqualink_data.spa_temp = atoi(msg + MSG_SPA_TEMP_LEN); - - if (_aqualink_data.temp_units == UNKNOWN) - setUnits(msg); - } - // NSF If get water temp rather than pool or spa in some cases, then we are in Pool OR Spa ONLY mode - else if (strncasecmp(msg, MSG_WATER_TEMP, MSG_WATER_TEMP_LEN) == 0) - { - _aqualink_data.pool_temp = atoi(msg + MSG_WATER_TEMP_LEN); - _aqualink_data.spa_temp = atoi(msg + MSG_WATER_TEMP_LEN); - if (_aqualink_data.temp_units == UNKNOWN) - setUnits(msg); - - if (isSINGLE_DEV_PANEL != true) - { - changePanelToMode_Only(); - LOG(ALLB_LOG,LOG_ERR, "AqualinkD set to 'Combo Pool & Spa' but detected 'Only Pool OR Spa' panel, please change config\n"); - } - } - else if (stristr(msg, LNG_MSG_WATER_TEMP1_SET) != NULL) - { - _aqualink_data.pool_htr_set_point = atoi(message + 28); - - if (_aqualink_data.temp_units == UNKNOWN) - setUnits(msg); - - if (isSINGLE_DEV_PANEL != true) - { - changePanelToMode_Only(); - LOG(ALLB_LOG,LOG_ERR, "AqualinkD set to 'Combo Pool & Spa' but detected 'Only Pool OR Spa' panel, please change config\n"); - } - } - else if (stristr(msg, LNG_MSG_WATER_TEMP2_SET) != NULL) - { - _aqualink_data.spa_htr_set_point = atoi(message + 27); - - if (_aqualink_data.temp_units == UNKNOWN) - setUnits(msg); - - if (isSINGLE_DEV_PANEL != true) - { - changePanelToMode_Only(); - LOG(ALLB_LOG,LOG_ERR, "AqualinkD set to 'Combo Pool & Spa' but detected 'Only Pool OR Spa' panel, please change config\n"); - } - } - else if (stristr(msg, LNG_MSG_SERVICE_ACTIVE) != NULL) - { - if (_aqualink_data.service_mode_state == OFF) - LOG(ALLB_LOG,LOG_NOTICE, "AqualinkD set to Service Mode\n"); - _aqualink_data.service_mode_state = ON; - msg_loop |= MSG_SERVICE; - //service_msg_count = 0; - } - else if (stristr(msg, LNG_MSG_TIMEOUT_ACTIVE) != NULL) - { - if (_aqualink_data.service_mode_state == OFF) - LOG(ALLB_LOG,LOG_NOTICE, "AqualinkD set to Timeout Mode\n"); - _aqualink_data.service_mode_state = FLASH; - msg_loop |= MSG_TIMEOUT; - //service_msg_count = 0; - } - else if (stristr(msg, LNG_MSG_FREEZE_PROTECTION_ACTIVATED) != NULL) - { - msg_loop |= MSG_FREEZE; - _aqualink_data.frz_protect_state = ON; - //freeze_msg_count = 0; - strcpy(_aqualink_data.last_display_message, msg); // Also display the message on web UI - } - /* // Not sure when to do with these for the moment, so no need to compile in the test. - else if (stristr(msg, LNG_MSG_CHEM_FEED_ON) != NULL) { - } - else if (stristr(msg, LNG_MSG_CHEM_FEED_OFF) != NULL) { - } - */ - else if (msg[2] == '/' && msg[5] == '/' && msg[8] == ' ') - { // date in format '08/29/16 MON' - strcpy(_aqualink_data.date, msg); - } - else if (stristr(msg, MSG_SWG_PCT) != NULL) - { - if (strncasecmp(msg, MSG_SWG_PCT, MSG_SWG_PCT_LEN) == 0 && strncasecmp(msg, "AQUAPURE HRS", 12) != 0) { - changeSWGpercent(&_aqualink_data, atoi(msg + MSG_SWG_PCT_LEN)); - } - else if (strncasecmp(msg, "AQUAPURE HRS", 12) != 0 && strncasecmp(msg, "SET AQUAPURE", 12) != 0) - { - if (strcasestr(msg, MSG_SWG_NO_FLOW) != NULL) - setSWGdeviceStatus(&_aqualink_data, ALLBUTTON, SWG_STATUS_NO_FLOW); - else if (strcasestr(msg, MSG_SWG_LOW_SALT) != NULL) - setSWGdeviceStatus(&_aqualink_data, ALLBUTTON, SWG_STATUS_LOW_SALT); - else if (strcasestr(msg, MSG_SWG_HIGH_SALT) != NULL) - setSWGdeviceStatus(&_aqualink_data, ALLBUTTON, SWG_STATUS_HI_SALT); - else if (strcasestr(msg, MSG_SWG_FAULT) != NULL) - setSWGdeviceStatus(&_aqualink_data, ALLBUTTON, SWG_STATUS_GENFAULT); - //setSWGdeviceStatus(&_aqualink_data, ALLBUTTON, SWG_STATUS_CHECK_PCB); - - // Any of these messages want to display. - strcpy(_aqualink_data.last_display_message, msg); - - msg_loop |= MSG_SWG_DEVICE; - } - msg_loop |= MSG_SWG; - } - else if (strncasecmp(msg, MSG_SWG_PPM, MSG_SWG_PPM_LEN) == 0) - { - _aqualink_data.swg_ppm = atoi(msg + MSG_SWG_PPM_LEN); - msg_loop |= MSG_SWG; - } - else if ((msg[1] == ':' || msg[2] == ':') && msg[strlen(msg) - 1] == 'M') - { // time in format '9:45 AM' - strcpy(_aqualink_data.time, msg); - // Setting time takes a long time, so don't try until we have all other programmed data. - if (_initWithRS == true && strlen(_aqualink_data.date) > 1 && checkAqualinkTime() != true) - { - LOG(ALLB_LOG,LOG_NOTICE, "RS time is NOT accurate '%s %s', re-setting on controller!\n", _aqualink_data.time, _aqualink_data.date); - aq_programmer(AQ_SET_TIME, NULL, &_aqualink_data); - } - else if (_initWithRS == false || _aqconfig_.sync_panel_time == false) - { - LOG(ALLB_LOG,LOG_DEBUG, "RS time '%s %s' not checking\n", _aqualink_data.time, _aqualink_data.date); - } - else if (_initWithRS == true) - { - LOG(ALLB_LOG,LOG_DEBUG, "RS time is accurate '%s %s'\n", _aqualink_data.time, _aqualink_data.date); - } - // If we get a time message before REV, the controller didn't see us as we started too quickly. - /* Don't need to check this anymore with the check for probe before startup. - if (_gotREV == false) - { - LOG(AQUA_LOG,LOG_NOTICE, "Getting control panel information\n", msg); - aq_programmer(AQ_GET_DIAGNOSTICS_MODEL, NULL, &_aqualink_data); - _gotREV = true; // Force it to true just incase we don't understand the model# - } - */ - } - else if (strstr(msg, " REV ") != NULL || strstr(msg, " REV. ") != NULL) - { // '8157 REV MMM' - // A master firmware revision message. - strcpy(_aqualink_data.version, msg); - rsm_get_revision(_aqualink_data.revision, _aqualink_data.version, strlen(_aqualink_data.version)); - //_gotREV = true; - LOG(ALLB_LOG,LOG_NOTICE, "Control Panel version %s\n", _aqualink_data.version); - LOG(ALLB_LOG,LOG_NOTICE, "Control Panel revision %s\n", _aqualink_data.revision); - if (_initWithRS == false) - { - //LOG(ALLBUTTON,LOG_NOTICE, "Standard protocol initialization complete\n"); - queueGetProgramData(ALLBUTTON, &_aqualink_data); - //queueGetExtendedProgramData(ALLBUTTON, &_aqualink_data, _aqconfig_.use_panel_aux_labels); - _initWithRS = true; - } - } - else if (stristr(msg, " TURNS ON") != NULL) - { - LOG(ALLB_LOG,LOG_NOTICE, "Program data '%s'\n", msg); - } - else if (_aqconfig_.override_freeze_protect == TRUE && strncasecmp(msg, "Press Enter* to override Freeze Protection with", 47) == 0) - { - //send_cmd(KEY_ENTER, aq_data); - //aq_programmer(AQ_SEND_CMD, (char *)KEY_ENTER, &_aqualink_data); - aq_send_cmd(KEY_ENTER); - } - // Process any button states (fake LED) for RS12 and above keypads - // Text will be button label on or off ie Aux_B2 off or WaterFall off - -#ifdef AQ_RS16 - //else if ( _aqconfig_.rs_panel_size >= 16 && (rs16 = RS16_endswithLEDstate(msg)) != 0 ) - else if (PANEL_SIZE() >= 16 && (rs16 = RS16_endswithLEDstate(msg)) != 0 ) - { - msg_loop |= rs16; - // Do nothing, just stop other else if statments executing - // make sure we also display the message. - // Note we only get ON messages here, Off messages will not be sent if something else turned it off - // use the Onetouch or iAqua equiptment page for off. - strcpy(_aqualink_data.last_display_message, msg); - } -#endif - else if (((msg[4] == ':') || (msg[6] == ':')) && (strncasecmp(msg, "AUX", 3) == 0) ) - { // Should probable check we are in programming mode. - // 'Aux3: No Label' - // 'Aux B1: No Label' - int labelid; - int ni = 3; - if (msg[4] == 'B') { ni = 5; } - labelid = atoi(msg + ni); - if (labelid > 0 && _aqconfig_.use_panel_aux_labels == true) - { - if (ni == 5) - labelid = labelid + 8; - else - labelid = labelid + 1; - // Aux1: on panel = Button 3 in aqualinkd (button 2 in array) - if (strncasecmp(msg+ni+3, "No Label", 8) != 0) { - _aqualink_data.aqbuttons[labelid].label = prittyString(cleanalloc(msg+ni+2)); - LOG(ALLB_LOG,LOG_NOTICE, "AUX ID %s label set to '%s'\n", _aqualink_data.aqbuttons[labelid].name, _aqualink_data.aqbuttons[labelid].label); - } else { - LOG(ALLB_LOG,LOG_NOTICE, "AUX ID %s has no control panel label using '%s'\n", _aqualink_data.aqbuttons[labelid].name, _aqualink_data.aqbuttons[labelid].label); - } - //_aqualink_data.aqbuttons[labelid + 1].label = cleanalloc(msg + 5); - } - } - // BOOST POOL 23:59 REMAINING - else if ( (strncasecmp(msg, "BOOST POOL", 10) == 0) && (strcasestr(msg, "REMAINING") != NULL) ) { - // Ignore messages if in programming mode. We get one of these turning off for some strange reason. - if (in_programming_mode(&_aqualink_data) == false) { - snprintf(_aqualink_data.boost_msg, 6, "%s", &msg[11]); - _aqualink_data.boost_duration = rsm_HHMM2min(_aqualink_data.boost_msg); - _aqualink_data.boost = true; - msg_loop |= MSG_BOOST; - msg_loop |= MSG_SWG; - //convert_boost_to_duration(_aqualink_data.boost_msg) - //if (_aqualink_data.ar_swg_status != SWG_STATUS_ON) {_aqualink_data.ar_swg_status = SWG_STATUS_ON;} - if (_aqualink_data.swg_percent != 101) {changeSWGpercent(&_aqualink_data, 101);} - //boost_msg_count = 0; - //if (_aqualink_data.active_thread.thread_id == 0) - strcpy(_aqualink_data.last_display_message, msg); // Also display the message on web UI if not in programming mode - } - } - else - { - LOG(ALLB_LOG,LOG_DEBUG_SERIAL, "Ignoring '%s'\n", msg); - //_aqualink_data.display_message = msg; - //if (in_programming_mode(&_aqualink_data) == false && _aqualink_data.simulate_panel == false && - if (in_programming_mode(&_aqualink_data) == false && - stristr(msg, "JANDY AquaLinkRS") == NULL && - //stristr(msg, "PUMP O") == NULL &&// Catch 'PUMP ON' and 'PUMP OFF' but not 'PUMP WILL TURN ON' - strncasecmp(msg, "PUMP O", 6) != 0 &&// Catch 'PUMP ON' and 'PUMP OFF' but not 'PUMP WILL TURN ON' - stristr(msg, "MAINTAIN") == NULL && // Catch 'MAINTAIN TEMP IS OFF' - stristr(msg, "0 PSI") == NULL /* // Catch some erronious message on test harness - stristr(msg, "CLEANER O") == NULL && - stristr(msg, "SPA O") == NULL && - stristr(msg, "AUX") == NULL*/ - ) - { // Catch all AUX1 AUX5 messages - //_aqualink_data.display_last_message = true; - strcpy(_aqualink_data.last_display_message, msg); - //rsm_strncpy(_aqualink_data.last_display_message, (unsigned char *)msg, AQ_MSGLONGLEN, AQ_MSGLONGLEN); - } - } - - // Send every message if we are in simulate panel mode - //if (_aqualink_data.simulate_panel) - // strcpy(_aqualink_data.last_display_message, msg); - //rsm_strncpy(_aqualink_data.last_display_message, (unsigned char *)msg, AQ_MSGLONGLEN, AQ_MSGLONGLEN); - //ascii(_aqualink_data.last_display_message, msg); - - - //LOG(ALLB_LOG,LOG_INFO, "RS Message loop :- '%d'\n", msg_loop); - - // We processed the next message, kick any threads waiting on the message. -//printf ("Message kicking\n"); - - - kick_aq_program_thread(&_aqualink_data, ALLBUTTON); -} - -bool process_packet(unsigned char *packet, int length) -{ - bool rtn = false; - //static unsigned char last_packet[AQ_MAXPKTLEN]; - static unsigned char last_checksum; - static char message[AQ_MSGLONGLEN + 1]; - static int processing_long_msg = 0; - - LOG(ALLB_LOG,LOG_DEBUG_SERIAL, "process_packer()\n", length); - - // Check packet against last check if different. - // Should only use the checksum, not whole packet since it's status messages. - /* - if ( packet[PKT_CMD] == CMD_STATUS && (memcmp(packet, last_packet, length) == 0)) - { - LOG(ALLB_LOG,LOG_DEBUG_SERIAL, "RS Received duplicate, ignoring.\n", length); - return rtn; - } - else - { - memcpy(last_packet, packet, length); - _aqualink_data.last_packet_type = packet[PKT_CMD]; - rtn = true; - } - */ - - _aqualink_data.last_packet_type = packet[PKT_CMD]; - -#ifdef AQ_PDA - if (isPDA_PANEL) - { - if (isPDA_IAQT) { - return false; - } - return process_pda_packet(packet, length); - } -#endif - - if ( packet[PKT_CMD] == CMD_STATUS && packet[length-3] == last_checksum && ! in_programming_mode(&_aqualink_data) ) - { - LOG(ALLB_LOG,LOG_DEBUG_SERIAL, "RS Received duplicate, ignoring.\n", length); - return false; - } - else - { - last_checksum = packet[length-3]; - rtn = true; - } - - if (processing_long_msg > 0 && packet[PKT_CMD] != CMD_MSG_LONG) - { - processing_long_msg = 0; - //LOG(AQUA_LOG,LOG_ERR, "RS failed to receive complete long message, received '%s'\n",message); - //LOG(AQUA_LOG,LOG_DEBUG, "RS didn't finished receiving of MSG_LONG '%s'\n",message); - processMessage(message); - } - - LOG(ALLB_LOG,LOG_DEBUG_SERIAL, "RS Received packet type 0x%02hhx length %d.\n", packet[PKT_CMD], length); - - switch (packet[PKT_CMD]) - { - case CMD_ACK: - //LOG(ALLB_LOG,LOG_DEBUG_SERIAL, "RS Received ACK length %d.\n", length); - break; - case CMD_STATUS: - //LOG(ALLB_LOG,LOG_DEBUG_SERIAL, "RS Received STATUS length %d.\n", length); - memcpy(_aqualink_data.raw_status, packet + 4, AQ_PSTLEN); - processLEDstate(); - if (_aqualink_data.aqbuttons[PUMP_INDEX].led->state == OFF) - { - _aqualink_data.pool_temp = TEMP_UNKNOWN; - _aqualink_data.spa_temp = TEMP_UNKNOWN; - //_aqualink_data.spa_temp = _aqconfig_.report_zero_spa_temp?-18:TEMP_UNKNOWN; - } - else if (_aqualink_data.aqbuttons[SPA_INDEX].led->state == OFF && isSINGLE_DEV_PANEL != true) - { - //_aqualink_data.spa_temp = _aqconfig_.report_zero_spa_temp?-18:TEMP_UNKNOWN; - _aqualink_data.spa_temp = TEMP_UNKNOWN; - } - else if (_aqualink_data.aqbuttons[SPA_INDEX].led->state == ON && isSINGLE_DEV_PANEL != true) - { - _aqualink_data.pool_temp = TEMP_UNKNOWN; - } - - // COLOR MODE programming relies on state changes, so let any threads know - //if (_aqualink_data.active_thread.ptype == AQ_SET_LIGHTPROGRAM_MODE) { - if ( in_light_programming_mode(&_aqualink_data) ) { - kick_aq_program_thread(&_aqualink_data, ALLBUTTON); - } - break; - case CMD_MSG: - case CMD_MSG_LONG: - { - int index = packet[PKT_DATA]; // Will get 0x00 for complete message, 0x01 for start on long message 0x05 last of long message - //printf("RSM received message at index %d '%.*s'\n",index,AQ_MSGLEN,(char *)packet + PKT_DATA + 1); - if (index <= 1){ - memset(message, 0, AQ_MSGLONGLEN + 1); - //strncpy(message, (char *)packet + PKT_DATA + 1, AQ_MSGLEN); - rsm_strncpy(message, packet + PKT_DATA + 1, AQ_MSGLONGLEN, AQ_MSGLEN); - processing_long_msg = index; - //LOG(ALLB_LOG,LOG_ERR, "Message %s\n",message); - } else { - //strncpy(&message[(processing_long_msg * AQ_MSGLEN)], (char *)packet + PKT_DATA + 1, AQ_MSGLEN); - //rsm_strncpy(&message[(processing_long_msg * AQ_MSGLEN)], (unsigned char *)packet + PKT_DATA + 1, AQ_MSGLONGLEN, AQ_MSGLEN); - rsm_strncpy(&message[( (index-1) * AQ_MSGLEN)], (unsigned char *)packet + PKT_DATA + 1, AQ_MSGLONGLEN, AQ_MSGLEN); - //LOG(ALLB_LOG,LOG_ERR, "Long Message %s\n",message); - if (++processing_long_msg != index) { - LOG(ALLB_LOG,LOG_DEBUG, "Long message index %d doesn't match buffer %d\n",index,processing_long_msg); - //printf("RSM Long message index %d doesn't match buffer %d\n",index,processing_long_msg); - } - #ifdef PROCESS_INCOMPLETE_MESSAGES - kick_aq_program_thread(&_aqualink_data, ALLBUTTON); - #endif - } - - if (index == 0 || index == 5) { - //printf("RSM process message '%s'\n",message); - - // MOVED FROM LINE 701 see if less errors - //kick_aq_program_thread(&_aqualink_data, ALLBUTTON); - - LOG(ALLB_LOG,LOG_DEBUG, "Processing Message - '%s'\n",message); - processMessage(message); // This will kick thread - } - - } - break; - case CMD_PROBE: - LOG(ALLB_LOG,LOG_DEBUG, "RS Received PROBE length %d.\n", length); - //LOG(AQUA_LOG,LOG_INFO, "Synch'ing with Aqualink master device...\n"); - rtn = false; - break; - case CMD_MSG_LOOP_ST: - LOG(ALLB_LOG,LOG_INFO, "RS Received message loop start\n"); - processMessageReset(); - rtn = false; - break; - default: - LOG(ALLB_LOG,LOG_INFO, "RS Received unknown packet, 0x%02hhx\n", packet[PKT_CMD]); - rtn = false; - break; - } - - return rtn; -} void action_delayed_request() { @@ -1344,6 +643,9 @@ int startup(char *self, char *cfgFile) if (_aqconfig_.frame_delay > 0) LOG(AQUA_LOG,LOG_NOTICE, "RS485 Frame delay = %dms\n", _aqconfig_.frame_delay); + if (!_aqconfig_.device_pre_state) // Default is on, so only disply if off. + LOG(AQUA_LOG,LOG_NOTICE, "Preset Device State = %s\n", bool2text(_aqconfig_.device_pre_state)); + #ifdef AQ_NO_THREAD_NETSERVICE if (_aqconfig_.thread_netservices) LOG(AQUA_LOG,LOG_NOTICE, "Thread Network Services = %s\n", bool2text(_aqconfig_.thread_netservices)); @@ -1416,7 +718,7 @@ void caculate_ack_packet(int rs_fd, unsigned char *packet_buffer, emulation_type { switch (source) { case ALLBUTTON: - send_extended_ack(rs_fd, (packet_buffer[PKT_CMD]==CMD_MSG_LONG?ACK_SCREEN_BUSY_SCROLL:ACK_NORMAL), pop_aq_cmd(&_aqualink_data)); + send_extended_ack(rs_fd, (packet_buffer[PKT_CMD]==CMD_MSG_LONG?ACK_SCREEN_BUSY_SCROLL:ACK_NORMAL), pop_allb_cmd(&_aqualink_data)); //DEBUG_TIMER_STOP(_rs_packet_timer,AQUA_LOG,"AllButton Emulation type Processed packet in"); break; case RSSADAPTER: @@ -1453,7 +755,7 @@ void caculate_ack_packet(int rs_fd, unsigned char *packet_buffer, emulation_type LOG(PDA_LOG,LOG_DEBUG, "PDA Aqualink daemon in sleep mode\n"); return; } else { - send_extended_ack(rs_fd, ACK_PDA, pop_aq_cmd(&_aqualink_data)); + send_extended_ack(rs_fd, ACK_PDA, pop_pda_cmd(&_aqualink_data)); } //DEBUG_TIMER_STOP(_rs_packet_timer,AQUA_LOG,"PDA Emulation type Processed packet in"); break; @@ -1903,56 +1205,43 @@ void main_loop() } } - - if (packet_length > 0 && packet_buffer[PKT_DEST] == _aqconfig_.device_id && getProtocolType(packet_buffer) == JANDY) + // Process and packets of devices we are acting as + if (packet_length > 0 && getProtocolType(packet_buffer) == JANDY && + (packet_buffer[PKT_DEST] == _aqconfig_.device_id || + packet_buffer[PKT_DEST] == _aqconfig_.rssa_device_id || + packet_buffer[PKT_DEST] == _aqconfig_.extended_device_id )) { - if (getLogLevel(AQUA_LOG) >= LOG_DEBUG) { - LOG(AQUA_LOG,LOG_DEBUG, "RS received packet of type %s length %d\n", get_packet_type(packet_buffer, packet_length), packet_length); - //logPacketRead(packet_buffer, packet_length); - } - _aqualink_data.updated = process_packet(packet_buffer, packet_length); - -#ifdef AQ_PDA - if (isPDA_PANEL) { - // If we are in simulator mode, the sim has already send the ack - if (isPDA_IAQT) { - //printf("****PDA IAQT Code\n"); + switch(getJandyDeviceType(packet_buffer[PKT_DEST])){ + case ALLBUTTON: + _aqualink_data.updated = process_allbutton_packet(packet_buffer, packet_length, &_aqualink_data); + caculate_ack_packet(rs_fd, packet_buffer, ALLBUTTON); + break; + case RSSADAPTER: + _aqualink_data.updated = process_rssadapter_packet(packet_buffer, packet_length, &_aqualink_data); + caculate_ack_packet(rs_fd, packet_buffer, RSSADAPTER); + break; + case IAQTOUCH: _aqualink_data.updated = process_iaqtouch_packet(packet_buffer, packet_length, &_aqualink_data); - _aqualink_data.updated = true; // FORCE UPDATE SINCE THIS IS NOT WORKING YET caculate_ack_packet(rs_fd, packet_buffer, IAQTOUCH); - if (checkAqualinkTime() == false) // Need to do this better. - {aq_programmer(AQ_SET_TIME, NULL, &_aqualink_data);} - } - else /*if (_aqualink_data.simulator_active == SIM_NONE)*/ { + break; + case ONETOUCH: + _aqualink_data.updated = process_onetouch_packet(packet_buffer, packet_length, &_aqualink_data); + caculate_ack_packet(rs_fd, packet_buffer, ONETOUCH); + break; + case AQUAPDA: + _aqualink_data.updated = process_pda_packet(packet_buffer, packet_length); caculate_ack_packet(rs_fd, packet_buffer, AQUAPDA); - } + break; + default: + break; } - else -#endif - caculate_ack_packet(rs_fd, packet_buffer, ALLBUTTON); - - DEBUG_TIMER_STOP(_rs_packet_timer,AQUA_LOG,"AllButton Emulation Processed packet in"); - } - else if (packet_length > 0 && isRSSA_ENABLED && packet_buffer[PKT_DEST] == _aqconfig_.rssa_device_id && getProtocolType(packet_buffer) == JANDY) { - _aqualink_data.updated = process_rssadapter_packet(packet_buffer, packet_length, &_aqualink_data); - caculate_ack_packet(rs_fd, packet_buffer, RSSADAPTER); - DEBUG_TIMER_STOP(_rs_packet_timer,AQUA_LOG,"SerialAdapter Emulation Processed packet in"); - } -#ifdef AQ_ONETOUCH - else if (packet_length > 0 && isONET_ENABLED && packet_buffer[PKT_DEST] == _aqconfig_.extended_device_id && getProtocolType(packet_buffer) == JANDY) { - _aqualink_data.updated = process_onetouch_packet(packet_buffer, packet_length, &_aqualink_data); - caculate_ack_packet(rs_fd, packet_buffer, ONETOUCH); - DEBUG_TIMER_STOP(_rs_packet_timer,AQUA_LOG,"OneTouch Emulation Processed packet in"); - } +#ifdef AQ_TM_DEBUG + char message[128]; + sprintf(message,"%s Emulation Processed packet in",getJandyDeviceName(getJandyDeviceType(packet_buffer[PKT_DEST]))); + DEBUG_TIMER_STOP(_rs_packet_timer,AQUA_LOG,message); #endif -#ifdef AQ_IAQTOUCH - else if (packet_length > 0 && isIAQT_ENABLED && packet_buffer[PKT_DEST] == _aqconfig_.extended_device_id && getProtocolType(packet_buffer) == JANDY) { - _aqualink_data.updated = process_iaqtouch_packet(packet_buffer, packet_length, &_aqualink_data); - caculate_ack_packet(rs_fd, packet_buffer, IAQTOUCH); - DEBUG_TIMER_STOP(_rs_packet_timer,AQUA_LOG,"AquaTouch Emulation Processed packet in"); } -#endif - //else if (packet_length > 0 && _aqconfig_.read_all_devices == true) + // Process any packets to readonly devices. else if (packet_length > 0 && _aqconfig_.read_RS485_devmask > 0) { if (getProtocolType(packet_buffer) == JANDY) diff --git a/config.c b/config.c index 9f224cf..edf6414 100644 --- a/config.c +++ b/config.c @@ -148,6 +148,7 @@ void init_parameters (struct aqconfig * parms) parms->enable_scheduler = true; parms->ftdi_low_latency = true; parms->frame_delay = 0; + parms->device_pre_state = true; generate_mqtt_id(parms->mqtt_ID, MQTT_ID_LEN); } @@ -635,7 +636,11 @@ bool setConfigValue(struct aqualinkdata *aqdata, char *param, char *value) { } else if (strncasecmp (param, "rs485_frame_delay", 17) == 0) { _aqconfig_.frame_delay = strtoul(value, NULL, 10); rtn=true; - } + } else if (strncasecmp (param, "device_pre_state", 16) == 0) { + _aqconfig_.device_pre_state = text2bool(value); + rtn=true; + } + else if (strncasecmp(param, "button_", 7) == 0) { // Check we have inichalized panel information, if not use any settings we may have if (_aqconfig_.paneltype_mask == 0) diff --git a/config.h b/config.h index 65db598..193f9bb 100644 --- a/config.h +++ b/config.h @@ -100,6 +100,7 @@ struct aqconfig bool enable_scheduler; bool ftdi_low_latency; int frame_delay; + bool device_pre_state; #ifdef AQ_NO_THREAD_NETSERVICE int rs_poll_speed; // Need to remove bool thread_netservices; // Need to remove diff --git a/devices_pentair.c b/devices_pentair.c index e3f6570..d8c391e 100644 --- a/devices_pentair.c +++ b/devices_pentair.c @@ -114,6 +114,10 @@ bool processPentairPacket(unsigned char *packet, int packet_length, struct aqual /* VSP Pump Status. + Mode 0=local control, 1=remote control + DriveState = no idea + Pressure Curve = see manual + Status = below (packet[PEN_HI_B_STATUS] * 256) + packet[PEN_LO_B_STATUS]; // Below was pulled from another project. 0 doesn;t seem to be accurate. diff --git a/docker/buildx.sh b/docker/buildx.sh index fb8755b..1d3ec0c 100755 --- a/docker/buildx.sh +++ b/docker/buildx.sh @@ -1,6 +1,9 @@ #!/bin/bash # # Script to build arm64 & amd64 containers that are published to docker.io +# +# This should never be used, unless you want to deploy AqualinkD docker containers to docer.io +# It's here incase someone taked over this repo because I'm no longer around # IMAGE=aqualinkd @@ -54,6 +57,9 @@ if echo $DOCKER_TAGS | grep -q $VERSION; then fi fi +# Login first +# cat ~sf/.docker.token | docker login --username sfeakes --password-stdin + echo "Building Docker container for $IMAGE using branch $VERSION" docker buildx build --platform=linux/amd64,linux/arm64 \ --file Dockerfile.buildx \ diff --git a/hassio.c b/hassio.c index 43553ac..44badcc 100644 --- a/hassio.c +++ b/hassio.c @@ -105,6 +105,10 @@ const char *HASSIO_SWG_DISCOVER = "{" "}"; // Use Fan for VSP +// Need to change the max / min. These do NOT lomit the slider in hassio, only the MQTT limits. +// So the 0-100% should be 600-3450 RPM and 15-130 GPM (ie 1% would = 600 & 0%=off) +// (value-600) / (3450-600) * 100 +// (value) / 100 * (3450-600) + 600 const char *HASSIO_VSP_DISCOVER = "{" "\"device\": {" HASS_DEVICE "}," "\"availability\": {" HASS_AVAILABILITY "}," @@ -119,10 +123,10 @@ const char *HASSIO_VSP_DISCOVER = "{" "\"payload_off\": \"0\"," "\"percentage_command_topic\": \"%s/%s/%s/set\"," // aqualinkd,filter_pump , RPM|GPM "\"percentage_state_topic\": \"%s/%s/%s\"," // aqualinkd,filter_pump , RPM|GPM - "\"percentage_value_template\": \"{{ ((value | float(0) / %d) * 100) | int }}\"," // 3450|130 - "\"percentage_command_template\": \"{{ ((value | float(0) / 100) * %d) | int }}\"," // 3450|130 + "\"percentage_value_template\": \"{{ (((value | float(0) - %d) / %d) * 100) | int }}\"," // 600, (3450-600) + "\"percentage_command_template\": \"{{ ((value | float(0) / 100) * %d) + %d | int }}\"," // (3450-130), 600 "\"speed_range_max\": \"100\"," - "\"speed_range_min\": \"%d\"," // 18|12 600rpm|15gpm + "\"speed_range_min\": \"0\"," // 18|12 600rpm|15gpm "\"qos\": 1," "\"retain\": false" "}"; @@ -414,17 +418,19 @@ void publish_mqtt_hassio_discover(struct aqualinkdata *aqdata, struct mg_connect // VSP Pumps for (i=0; i < aqdata->num_pumps; i++) { int maxspeed=3450; // Min is 600 - int percent_min=18; // 600 as % of max + int minspeed=600; // 600 as % of max char units[4]; sprintf(units, "RPM"); if ( aqdata->pumps[i].pumpType == VFPUMP ) { maxspeed=130; // Min is 15 - percent_min=12; // 15 as % of max + minspeed=15; // 15 as % of max sprintf(units, "GPM"); } // Create a FAN for pump against the button it' assigned to // In the future maybe change this to the pump# or change the sensors to button??? + // Need to change the max / min. These do NOT lomit the slider in hassio, only the MQTT limits. + // So the 0-100% should be 600-3450 RPM and 15-130 GPM (ie 1% would = 600 & 0%=off) sprintf(msg, HASSIO_VSP_DISCOVER, _aqconfig_.mqtt_aq_topic, aqdata->pumps[i].button->name,units, @@ -434,9 +440,8 @@ void publish_mqtt_hassio_discover(struct aqualinkdata *aqdata, struct mg_connect _aqconfig_.mqtt_aq_topic,aqdata->pumps[i].button->name, _aqconfig_.mqtt_aq_topic,aqdata->pumps[i].button->name,units, _aqconfig_.mqtt_aq_topic,aqdata->pumps[i].button->name,units, - maxspeed, - maxspeed, - percent_min); + minspeed, (maxspeed - minspeed), + (maxspeed - minspeed), minspeed); sprintf(topic, "%s/fan/aqualinkd/aqualinkd_%s_%s/config", _aqconfig_.mqtt_hass_discover_topic, aqdata->pumps[i].button->name, units); send_mqtt(nc, topic, msg); diff --git a/iaqtouch_aq_programmer.c b/iaqtouch_aq_programmer.c index bc07bfc..76a69a4 100644 --- a/iaqtouch_aq_programmer.c +++ b/iaqtouch_aq_programmer.c @@ -836,6 +836,7 @@ void *get_aqualink_iaqtouch_freezeprotect( void *ptr ) int frz = rsm_atoi(iaqtGetMessageLine(0)); if (frz >= 0) { aq_data->frz_protect_set_point = frz; + aq_data->frz_protect_state = ON; LOG(IAQT_LOG,LOG_NOTICE, "IAQ Touch Freeze Protection setpoint %d\n",frz); } diff --git a/net_services.c b/net_services.c index b096417..b962391 100644 --- a/net_services.c +++ b/net_services.c @@ -1041,10 +1041,6 @@ uriAtype action_URI(request_source from, const char *URI, int uri_length, float } else if (strncmp(ri1, "simcmd", 10) == 0 && from == NET_WS) { // Only valid from websocket. simulator_send_cmd((unsigned char)value); return uActioned; - /* - } else if (strncmp(ri1, "rawcommand", 10) == 0 && from == NET_WS) { // Only valid from websocket. - aq_send_cmd((unsigned char)value); - return uActioned;*/ #ifdef AQ_MANAGER } else if (strncmp(ri1, "aqmanager", 9) == 0 && from == NET_WS) { // Only valid from websocket. return uAQmanager; @@ -1740,18 +1736,6 @@ void action_domoticz_mqtt_message(struct mg_connection *nc, struct mg_mqtt_messa LOG(NET_LOG,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")); //create_panel_request(NET_DZMQTT, i, (nvalue == DZ_OFF?0:1), false); panel_device_request(_aqualink_data, ON_OFF, i, (nvalue == DZ_OFF?0:1), NET_DZMQTT); - /* -#ifdef AQ_PDA - if (isPDA_PANEL) { - 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. diff --git a/pda.c b/pda.c index 052706c..fa38ae6 100644 --- a/pda.c +++ b/pda.c @@ -810,6 +810,7 @@ bool process_pda_packet(unsigned char *packet, int length) static bool equiptment_update_loop = false; static bool read_equiptment_menu = false; + _aqualink_data->last_packet_type = packet[PKT_CMD]; process_pda_menu_packet(packet, length, in_programming_mode(_aqualink_data)); diff --git a/pda_aq_programmer.c b/pda_aq_programmer.c index 20c66a6..d323288 100644 --- a/pda_aq_programmer.c +++ b/pda_aq_programmer.c @@ -51,8 +51,122 @@ bool loopover_devices(struct aqualinkdata *aq_data); bool find_pda_menu_item(struct aqualinkdata *aq_data, char *menuText, int charlimit); bool select_pda_menu_item(struct aqualinkdata *aq_data, char *menuText, bool waitForNextMenu); +bool _get_PDA_aqualink_pool_spa_heater_temps(struct aqualinkdata *aq_data); +bool _get_PDA_freeze_protect_temp(struct aqualinkdata *aq_data); + static pda_type _PDA_Type; +//#define USE_ALLBUTTON_QUEUE + +#ifndef USE_ALLBUTTON_QUEUE +/* Forcing all these to allbutton for the moment */ +void waitfor_queue2empty(); +void send_cmd(unsigned char cmd); +unsigned char pop_allb_cmd(struct aqualinkdata *aq_data); +int get_allb_queue_length(); + +void waitfor_pda_queue2empty() { + waitfor_queue2empty(); +} +void send_pda_cmd(unsigned char cmd) { + send_cmd(cmd); +} +unsigned char pop_pda_cmd(struct aqualinkdata *aq_data){ + return pop_allb_cmd(aq_data); +} +int get_pda_queue_length(){ + return get_allb_queue_length(); +} + +#else + +// Use out own queue. +// This is NOT working correctly, need to come back and fix + +#define MAX_STACK 20 +int _pda_cmdstack_place = 0; +unsigned char _pda_cmd_queue[MAX_STACK]; +unsigned char _pda_command = NUL; + +bool waitfor_pda_queue2empty(); + +int get_pda_queue_length(){ + return _pda_cmdstack_place; +} + +bool push_pda_cmd(unsigned char cmd) { + _pda_command = cmd; + /* + if (_pda_cmdstack_place < MAX_STACK) { + _pda_cmd_queue[_pda_cmdstack_place] = cmd; + _pda_cmdstack_place++; + } else { + LOG(PDA_LOG, LOG_ERR, "Command queue overflow, too many unsent commands to RS control panel\n"); + return false; + } + */ + return true; +} +void send_pda_cmd(unsigned char cmd) { + if (waitfor_pda_queue2empty()) { + push_pda_cmd(cmd); + } +} + +unsigned char pop_pda_cmd(struct aqualinkdata *aq_data) +{ + unsigned char cmd = NUL; + + if (_pda_command != NUL && aq_data->last_packet_type == CMD_STATUS) { + cmd = _pda_command; + _pda_command = NUL; + } +/* + // Only send commands on status messages + if (get_pda_queue_length() > 0 && aq_data->last_packet_type == CMD_STATUS) { + cmd = _pda_cmd_queue[0]; + _pda_cmdstack_place--; + LOG(PDA_LOG, LOG_DEBUG_SERIAL, "RS SEND cmd '0x%02hhx'\n", cmd); + memmove(&_pda_cmd_queue[0], &_pda_cmd_queue[1], sizeof(unsigned char) * _pda_cmdstack_place ) ; + } +*/ + return cmd; +} + + +bool waitfor_pda_queue2empty() { + int i=0; + + if (_pda_command != NUL) { + LOG(PDA_LOG, LOG_DEBUG, "Waiting for queue to empty\n"); + } + + while ( (_pda_command != NUL) && ( i++ < PROGRAMMING_POLL_COUNTER) ) { + delay(100); + } +/* + if (get_pda_queue_length() > 0) { + LOG(PDA_LOG, LOG_DEBUG, "Waiting for queue to empty\n"); + } + + while ( (get_pda_queue_length() > 0) && ( i++ < PROGRAMMING_POLL_COUNTER) ) { + delay(100); + } +*/ + if (i >= PROGRAMMING_POLL_COUNTER) { + LOG(PDA_LOG, LOG_ERR, "Send command Queue did not empty, timeout\n"); + return false; + } + + return true; +} + +#endif + + + + + /* // Each RS message / call to this function is around 0.2 seconds apart //#define MAX_ACK_FOR_THREAD 200 // ~40 seconds (Init takes 30) @@ -141,7 +255,7 @@ bool loopover_devices(struct aqualinkdata *aq_data) { // Should look for message "ALL OFF", that's end of device list. for (i=0; i < 18 && (index = pda_find_m_index("ALL OFF")) == -1 ; i++) { - send_cmd(KEY_PDA_DOWN); + send_pda_cmd(KEY_PDA_DOWN); //waitForMessage(aq_data, NULL, 1); waitForPDAMessageTypes(aq_data,CMD_PDA_HIGHLIGHT,CMD_MSG_LONG,8); } @@ -180,7 +294,7 @@ bool find_pda_menu_item(struct aqualinkdata *aq_data, char *menuText, int charli if (strncasecmp(pda_m_line(9)," ^^ MORE", 10) == 0) { int j; for(j=0; j < 20; j++) { - send_cmd(KEY_PDA_DOWN); + send_pda_cmd(KEY_PDA_DOWN); //delay(500); //wait_for_empty_cmd_buffer(); //waitForPDAMessageType(aq_data,CMD_PDA_HIGHLIGHT,2); @@ -285,26 +399,26 @@ bool find_pda_menu_item(struct aqualinkdata *aq_data, char *menuText, int charli if ((min_index != -1) && ((index - i) > (i - min_index + max_index - index + 1))) { cnt = i - min_index + max_index - index + 1; for (i=0; i < cnt; i++) { - waitfor_queue2empty(); - send_cmd(KEY_PDA_UP); + waitfor_pda_queue2empty(); + send_pda_cmd(KEY_PDA_UP); } } else { for (i=pda_m_hlightindex(); i < index; i++) { - waitfor_queue2empty(); - send_cmd(KEY_PDA_DOWN); + waitfor_pda_queue2empty(); + send_pda_cmd(KEY_PDA_DOWN); } } } else if (i > index) { if ((min_index != -1) && ((i - index) > (index - min_index + max_index - i + 1))) { cnt = i - min_index + max_index - index + 1; for (i=0; i < cnt; i++) { - waitfor_queue2empty(); - send_cmd(KEY_PDA_UP); + waitfor_pda_queue2empty(); + send_pda_cmd(KEY_PDA_UP); } } else { for (i=pda_m_hlightindex(); i > index; i--) { - waitfor_queue2empty(); - send_cmd(KEY_PDA_UP); + waitfor_pda_queue2empty(); + send_pda_cmd(KEY_PDA_UP); } } } @@ -325,7 +439,7 @@ bool _select_pda_menu_item(struct aqualinkdata *aq_data, char *menuText, bool wa //int matchType = loose?-1:1; int matchType = loose?-1:strlen(menuText); // NSF Not way to check this. (release 2.2.0 introduced this with the line above) if ( find_pda_menu_item(aq_data, menuText, matchType) ) { - send_cmd(KEY_PDA_SELECT); + send_pda_cmd(KEY_PDA_SELECT); LOG(PDA_LOG,LOG_DEBUG, "PDA Device programmer selected menu item '%s'\n",menuText); if (waitForNextMenu) @@ -353,7 +467,7 @@ bool goto_pda_menu(struct aqualinkdata *aq_data, pda_menu_type menu) { if (pda_m_type() == PM_FW_VERSION) { LOG(PDA_LOG,LOG_DEBUG, "goto_pda_menu at FW version menu\n"); - send_cmd(KEY_PDA_BACK); + send_pda_cmd(KEY_PDA_BACK); if (! waitForPDAnextMenu(aq_data)) { LOG(PDA_LOG,LOG_ERR, "PDA Device programmer wait for next menu failed\n"); } @@ -366,14 +480,14 @@ bool goto_pda_menu(struct aqualinkdata *aq_data, pda_menu_type menu) { while (ret && (pda_m_type() != menu) && cnt <= 5) { switch (menu) { case PM_HOME: - send_cmd(KEY_PDA_BACK); + send_pda_cmd(KEY_PDA_BACK); ret = waitForPDAnextMenu(aq_data); break; case PM_EQUIPTMENT_CONTROL: if (pda_m_type() == PM_HOME) { ret = select_pda_menu_item(aq_data, "EQUIPMENT ON/OFF", true); } else { - send_cmd(KEY_PDA_BACK); + send_pda_cmd(KEY_PDA_BACK); ret = waitForPDAnextMenu(aq_data); } break; @@ -383,7 +497,7 @@ bool goto_pda_menu(struct aqualinkdata *aq_data, pda_menu_type menu) { } else if (pda_m_type() == PM_MAIN) { ret = select_pda_menu_item(aq_data, "PALM OPTIONS", true); } else { - send_cmd(KEY_PDA_BACK); + send_pda_cmd(KEY_PDA_BACK); ret = waitForPDAnextMenu(aq_data); } break; @@ -396,7 +510,7 @@ bool goto_pda_menu(struct aqualinkdata *aq_data, pda_menu_type menu) { } else if (pda_m_type() == PM_SYSTEM_SETUP) { ret = select_pda_menu_item(aq_data, "LABEL AUX", true); } else { - send_cmd(KEY_PDA_BACK); + send_pda_cmd(KEY_PDA_BACK); ret = waitForPDAnextMenu(aq_data); } } else { @@ -410,7 +524,7 @@ bool goto_pda_menu(struct aqualinkdata *aq_data, pda_menu_type menu) { } else if (pda_m_type() == PM_MAIN) { ret = select_pda_menu_item(aq_data, "SYSTEM SETUP", true); } else { - send_cmd(KEY_PDA_BACK); + send_pda_cmd(KEY_PDA_BACK); ret = waitForPDAnextMenu(aq_data); } } else { @@ -426,7 +540,7 @@ bool goto_pda_menu(struct aqualinkdata *aq_data, pda_menu_type menu) { } else if (pda_m_type() == PM_SYSTEM_SETUP) { ret = select_pda_menu_item(aq_data, "FREEZE PROTECT", true); } else { - send_cmd(KEY_PDA_BACK); + send_pda_cmd(KEY_PDA_BACK); ret = waitForPDAnextMenu(aq_data); } } else { @@ -439,7 +553,7 @@ bool goto_pda_menu(struct aqualinkdata *aq_data, pda_menu_type menu) { } else if (pda_m_type() == PM_MAIN) { ret = select_pda_menu_item(aq_data, "SET AquaPure", true); } else { - send_cmd(KEY_PDA_BACK); + send_pda_cmd(KEY_PDA_BACK); ret = waitForPDAnextMenu(aq_data); } break; @@ -450,7 +564,7 @@ bool goto_pda_menu(struct aqualinkdata *aq_data, pda_menu_type menu) { ret = select_pda_menu_item_loose(aq_data, "BOOST", true); //ret = select_pda_menu_item(aq_data, "BOOST", true); } else { - send_cmd(KEY_PDA_BACK); + send_pda_cmd(KEY_PDA_BACK); ret = waitForPDAnextMenu(aq_data); } //printf("****MENU SELECT RETURN %d*****\n",ret); @@ -471,7 +585,7 @@ bool goto_pda_menu(struct aqualinkdata *aq_data, pda_menu_type menu) { //waitForPDAMessageTypesOrMenu(aq_data,CMD_PDA_HIGHLIGHT,CMD_PDA_HIGHLIGHTCHARS,20,"press ANY key",8); } } else { - send_cmd(KEY_PDA_BACK); + send_pda_cmd(KEY_PDA_BACK); ret = waitForPDAnextMenu(aq_data); } break; @@ -481,7 +595,7 @@ bool goto_pda_menu(struct aqualinkdata *aq_data, pda_menu_type menu) { } else if (pda_m_type() == PM_MAIN) { ret = select_pda_menu_item(aq_data, "SET TIME", true); } else { - send_cmd(KEY_PDA_BACK); + send_pda_cmd(KEY_PDA_BACK); ret = waitForPDAnextMenu(aq_data); } break; @@ -541,23 +655,23 @@ void *set_aqualink_PDA_device_on_off( void *ptr ) } // NSF Added this since DEBUG hitting wrong command - //waitfor_queue2empty(); + //waitfor_pda_queue2empty(); if ( find_pda_menu_item(aq_data, device_name, 13) ) { if (aq_data->aqbuttons[device].led->state != state) { //printf("*** Select State ***\n"); LOG(PDA_LOG,LOG_INFO, "PDA Device On/Off, found device '%s', changing state\n",aq_data->aqbuttons[device].label,state); force_queue_delete(); // NSF This is a bad thing to do. Need to fix this - send_cmd(KEY_PDA_SELECT); - while (get_aq_cmd_length() > 0) { delay(500); } + send_pda_cmd(KEY_PDA_SELECT); + while (get_pda_queue_length() > 0) { delay(500); } // If you are turning on a heater there will be a sub menu to set temp if ((state == ON) && ((device == aq_data->pool_heater_index) || (device == aq_data->spa_heater_index))) { if (! waitForPDAnextMenu(aq_data)) { LOG(PDA_LOG,LOG_ERR, "PDA Device On/Off: %s on - waitForPDAnextMenu\n", aq_data->aqbuttons[device].label); } else { - send_cmd(KEY_PDA_SELECT); - while (get_aq_cmd_length() > 0) { delay(500); } + send_pda_cmd(KEY_PDA_SELECT); + while (get_pda_queue_length() > 0) { delay(500); } if (!waitForPDAMessageType(aq_data,CMD_PDA_HIGHLIGHT,20)) { LOG(PDA_LOG,LOG_ERR, "PDA Device On/Off: %s on - wait for CMD_PDA_HIGHLIGHT\n", aq_data->aqbuttons[device].label); @@ -640,7 +754,7 @@ void *set_aqualink_PDA_init( void *ptr ) //printf("****** Version '%s' ********\n",aq_data->version); LOG(PDA_LOG,LOG_DEBUG, "PDA type=%d, version=%s\n", _PDA_Type, aq_data->version); // don't wait for version menu to time out press back to get to home menu faster - //send_cmd(KEY_PDA_BACK); + //send_pda_cmd(KEY_PDA_BACK); //if (! waitForPDAnextMenu(aq_data)) { // waitForPDAnextMenu waits for highlight chars, which we don't get on normal menu if (! waitForPDAMessageType(aq_data,CMD_PDA_CLEAR,10)) { @@ -657,12 +771,12 @@ void *set_aqualink_PDA_init( void *ptr ) } */ // Get heater setpoints - if (! get_PDA_aqualink_pool_spa_heater_temps(aq_data)) { + if (! _get_PDA_aqualink_pool_spa_heater_temps(aq_data)) { LOG(PDA_LOG,LOG_ERR, "PDA Init :- Error getting heater setpoints\n"); } // Get freeze protect setpoint, AquaPalm doesn't have freeze protect in menu. - if (_PDA_Type != AQUAPALM && ! get_PDA_freeze_protect_temp(aq_data)) { + if (_PDA_Type != AQUAPALM && ! _get_PDA_freeze_protect_temp(aq_data)) { LOG(PDA_LOG,LOG_ERR, "PDA Init :- Error getting freeze setpoints\n"); } @@ -702,7 +816,7 @@ void *set_aqualink_PDA_wakeinit( void *ptr ) } -bool get_PDA_freeze_protect_temp(struct aqualinkdata *aq_data) { +bool _get_PDA_freeze_protect_temp(struct aqualinkdata *aq_data) { if ( _PDA_Type == PDA) { if (! goto_pda_menu(aq_data, PM_FREEZE_PROTECT)) { @@ -710,7 +824,7 @@ bool get_PDA_freeze_protect_temp(struct aqualinkdata *aq_data) { } /* select the freeze protect temp to see which devices are enabled by freeze protect */ - send_cmd(KEY_PDA_SELECT); + send_pda_cmd(KEY_PDA_SELECT); return waitForPDAnextMenu(aq_data); } else { LOG(PDA_LOG,LOG_INFO, "In PDA AquaPalm mode, freezepoints not supported\n"); @@ -718,7 +832,7 @@ bool get_PDA_freeze_protect_temp(struct aqualinkdata *aq_data) { } } -bool get_PDA_aqualink_pool_spa_heater_temps(struct aqualinkdata *aq_data) { +bool _get_PDA_aqualink_pool_spa_heater_temps(struct aqualinkdata *aq_data) { // Get heater setpoints if (! goto_pda_menu(aq_data, PM_SET_TEMP)) { @@ -732,6 +846,30 @@ bool get_PDA_aqualink_pool_spa_heater_temps(struct aqualinkdata *aq_data) { return true; } +void *get_PDA_aqualink_pool_spa_heater_temps( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_GET_POOL_SPA_HEATER_TEMPS); + _get_PDA_aqualink_pool_spa_heater_temps(aq_data); + cleanAndTerminateThread(threadCtrl); + return ptr; +} + +void *get_PDA_freeze_protect_temp( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_GET_FREEZE_PROTECT_TEMP); + _get_PDA_freeze_protect_temp(aq_data); + cleanAndTerminateThread(threadCtrl); + return ptr; +} + bool waitForPDAMessageHighlight(struct aqualinkdata *aq_data, int highlighIndex, int numMessageReceived) { LOG(PDA_LOG,LOG_DEBUG, "waitForPDAMessageHighlight index %d\n",highlighIndex); @@ -818,7 +956,7 @@ bool waitForPDAMessageTypesOrMenu(struct aqualinkdata *aq_data, unsigned char mt { if (gotmenu == false && line > 0 && text != NULL) { if (stristr(pda_m_line(line), text) != NULL) { - send_cmd(KEY_PDA_SELECT); + send_pda_cmd(KEY_PDA_SELECT); gotmenu = true; LOG(PDA_LOG,LOG_DEBUG, "waitForPDAMessageTypesOrMenu saw '%s' and line %d\n",text,line); } @@ -884,48 +1022,74 @@ bool set_PDA_numeric_field_value(struct aqualinkdata *aq_data, int val, int cur_ if (val < cur_val) { LOG(PDA_LOG,LOG_DEBUG, "Numeric selector %s value : lower from %d to %d\n", select_label, cur_val, val); for (i = cur_val; i > val; i=i-step) { - send_cmd(KEY_PDA_DOWN); + send_pda_cmd(KEY_PDA_DOWN); } } else if (val > cur_val) { LOG(PDA_LOG,LOG_DEBUG, "Numeric selector %s value : raise from %d to %d\n", select_label, cur_val, val); for (i = cur_val; i < val; i=i+step) { - send_cmd(KEY_PDA_UP); + send_pda_cmd(KEY_PDA_UP); } } else { LOG(PDA_LOG,LOG_DEBUG, "Numeric selector %s value : already at %d\n", select_label, val); } - send_cmd(KEY_PDA_SELECT); + send_pda_cmd(KEY_PDA_SELECT); LOG(PDA_LOG,LOG_DEBUG, "Numeric selector %s value : set to %d\n", select_label, val); return true; } -bool set_PDA_aqualink_SWG_setpoint(struct aqualinkdata *aq_data, int val) { +//bool set_PDA_aqualink_SWG_setpoint(struct aqualinkdata *aq_data, int val) { +void *set_PDA_aqualink_SWG_setpoint(void *ptr) { + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_SET_SWG_PERCENT); + + int val = atoi((char*)threadCtrl->thread_args); + val = setpoint_check(SWG_SETPOINT, val, aq_data); + if (! goto_pda_menu(aq_data, PM_AQUAPURE)) { LOG(PDA_LOG,LOG_ERR, "Error finding SWG setpoints menu\n"); - return false; + cleanAndTerminateThread(threadCtrl); + return ptr; } // wait for menu to display to capture current value with process_pda_packet_msg_long_SWG waitForPDAMessageTypes(aq_data,CMD_PDA_HIGHLIGHT,CMD_PDA_HIGHLIGHTCHARS, 10); - +/* if (pda_find_m_index("SET POOL") < 0) { // Single Setpoint Screen - return set_PDA_numeric_field_value(aq_data, val, -1, NULL, 5); - } else if (aq_data->aqbuttons[SPA_INDEX].led->state != OFF) { + set_PDA_numeric_field_value(aq_data, val, -1, NULL, 5); + } else*/ if (aq_data->aqbuttons[SPA_INDEX].led->state != OFF) { // Dual Setpoint Screen with SPA mode enabled // :TODO: aq_data should have 2 swg_precent values and GUI should be updated to // display and modify both values. - return set_PDA_numeric_field_value(aq_data, val, -1, "SET SPA", 5); + set_PDA_numeric_field_value(aq_data, val, -1, "SET SPA", 5); } else { // Dual Setpoint Screen with SPA mode disabled - return set_PDA_numeric_field_value(aq_data, val, -1, "SET POOL", 5); + set_PDA_numeric_field_value(aq_data, val, -1, "SET POOL", 5); } + + waitfor_pda_queue2empty(); + goto_pda_menu(aq_data, PM_HOME); + + cleanAndTerminateThread(threadCtrl); + return ptr; } -bool set_PDA_aqualink_boost(struct aqualinkdata *aq_data, bool val) +//bool set_PDA_aqualink_boost(struct aqualinkdata *aq_data, bool val) +void *set_PDA_aqualink_boost(void *ptr) { + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_SET_BOOST); + + int val = atoi((char*)threadCtrl->thread_args); + if (! goto_pda_menu(aq_data, PM_BOOST)) { LOG(PDA_LOG,LOG_ERR, "Error finding BOOST menu\n"); return false; @@ -948,9 +1112,14 @@ bool set_PDA_aqualink_boost(struct aqualinkdata *aq_data, bool val) select_pda_menu_item_loose(aq_data, "STOP", false); } - return true; + waitfor_pda_queue2empty(); + goto_pda_menu(aq_data, PM_HOME); + cleanAndTerminateThread(threadCtrl); + return ptr; } + + bool set_PDA_aqualink_heater_setpoint(struct aqualinkdata *aq_data, int val, bool isPool) { char label[10]; int cur_val; @@ -975,7 +1144,7 @@ bool set_PDA_aqualink_heater_setpoint(struct aqualinkdata *aq_data, int val, boo if (val == cur_val) { LOG(PDA_LOG,LOG_INFO, "PDA %s setpoint : temp already %d\n", label, val); - send_cmd(KEY_PDA_BACK); + send_pda_cmd(KEY_PDA_BACK); return true; } @@ -989,26 +1158,92 @@ bool set_PDA_aqualink_heater_setpoint(struct aqualinkdata *aq_data, int val, boo return true; } -bool set_PDA_aqualink_freezeprotect_setpoint(struct aqualinkdata *aq_data, int val) { +void *set_aqualink_PDA_pool_heater_temps( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + //char *name; + //char *menu_name; + waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_SET_POOL_HEATER_TEMPS); + + int val = atoi((char*)threadCtrl->thread_args); + val = setpoint_check(POOL_HTR_SETOINT, val, aq_data); + + set_PDA_aqualink_heater_setpoint(aq_data, val, true); + + waitfor_pda_queue2empty(); + goto_pda_menu(aq_data, PM_HOME); + + cleanAndTerminateThread(threadCtrl); + return ptr; +} +void *set_aqualink_PDA_spa_heater_temps( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + //char *name; + //char *menu_name; + waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_SET_SPA_HEATER_TEMPS); + + int val = atoi((char*)threadCtrl->thread_args); + val = setpoint_check(SPA_HTR_SETOINT, val, aq_data); + + set_PDA_aqualink_heater_setpoint(aq_data, val, false); + + waitfor_pda_queue2empty(); + goto_pda_menu(aq_data, PM_HOME); + + cleanAndTerminateThread(threadCtrl); + return ptr; +} + +//bool set_PDA_aqualink_freezeprotect_setpoint(struct aqualinkdata *aq_data, int val) { +void *set_aqualink_PDA_freeze_protectsetpoint( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_SET_FREEZE_PROTECT_TEMP); + + int val = atoi((char*)threadCtrl->thread_args); + + val = setpoint_check(FREEZE_SETPOINT, val, aq_data); if (_PDA_Type != PDA) { LOG(PDA_LOG,LOG_INFO, "In PDA AquaPalm mode, freezepoints not supported\n"); - return false; + //return false; } else if (! goto_pda_menu(aq_data, PM_FREEZE_PROTECT)) { LOG(PDA_LOG,LOG_ERR, "Error finding freeze protect setpoints menu\n"); - return false; + //return false; } else if (! set_PDA_numeric_field_value(aq_data, val, aq_data->frz_protect_set_point, NULL, 1)) { LOG(PDA_LOG,LOG_ERR, "Error failed to set freeze protect temp value\n"); - return false; + //return false; } else { - return waitForPDAnextMenu(aq_data); + waitForPDAnextMenu(aq_data); } + + waitfor_pda_queue2empty(); + goto_pda_menu(aq_data, PM_HOME); + + cleanAndTerminateThread(threadCtrl); + return ptr; } -bool set_PDA_aqualink_time(struct aqualinkdata *aq_data) { +//bool set_PDA_aqualink_time(struct aqualinkdata *aq_data) +void *set_PDA_aqualink_time( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_SET_TIME); + if (! goto_pda_menu(aq_data, PM_SET_TIME)) { LOG(PDA_LOG,LOG_ERR, "Error finding set time menu\n"); - return false; + goto f_end; } struct tm tm; struct tm panel_tm; @@ -1034,12 +1269,12 @@ Debug: PDA: PDA Menu Line 9 = to continue. if (strptime(pda_m_line(2), "%t%D %a", &panel_tm) == NULL) { LOG(PDA_LOG,LOG_ERR, "set_PDA_aqualink_time read date (%.*s) failed\n", AQ_MSGLEN, pda_m_line(2)); - return false; + goto f_end; } if (strptime(pda_m_line(3), "%t%I:%M %p", &panel_tm) == NULL) { LOG(PDA_LOG,LOG_ERR, "set_PDA_aqualink_time read time (%.*s) failed\n", AQ_MSGLEN, pda_m_line(3)); - return false; + goto f_end; } panel_tm.tm_isdst = tm.tm_isdst; panel_tm.tm_sec = 0; @@ -1065,13 +1300,26 @@ Debug: PDA: PDA Menu Line 9 = to continue. } waitForPDAnextMenu(aq_data); + waitfor_pda_queue2empty(); + goto_pda_menu(aq_data, PM_HOME); - return true; + f_end: + + cleanAndTerminateThread(threadCtrl); + return ptr; } // Test ine this. -bool get_PDA_aqualink_aux_labels(struct aqualinkdata *aq_data) { -#ifdef BETA_PDA_AUTOLABEL +//bool get_PDA_aqualink_aux_labels(struct aqualinkdata *aq_data) { +void *get_PDA_aqualink_aux_labels( void *ptr ) { + + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; +#ifdef BETA_PDA_AUTOLABEL + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_GET_AUX_LABELS); + int i=0; char label[10]; @@ -1079,19 +1327,29 @@ bool get_PDA_aqualink_aux_labels(struct aqualinkdata *aq_data) { if (! goto_pda_menu(aq_data, PM_AUX_LABEL)) { LOG(PDA_LOG,LOG_ERR, "Error finding aux label menu\n"); - return false; + goto f_end; } for (i=1;i<8;i++) { sprintf(label, "AUX%d",i); select_pda_menu_item(aq_data, label, true); - send_cmd(KEY_PDA_BACK); + send_pda_cmd(KEY_PDA_BACK); waitForPDAnextMenu(aq_data); } // Read first page of devices and make some assumptions. + + waitfor_pda_queue2empty(); + goto_pda_menu(aq_data, PM_HOME); + + f_end: + +#else + LOG(PDA_LOG,LOG_INFO, "Finding PDA labels, (NOT IMPLIMENTED)\n"); #endif - return true; + + cleanAndTerminateThread(threadCtrl); + return ptr; } /* diff --git a/pda_aq_programmer.h b/pda_aq_programmer.h index 55ea22b..9d668a0 100644 --- a/pda_aq_programmer.h +++ b/pda_aq_programmer.h @@ -7,30 +7,51 @@ typedef enum pda_type { PDA } pda_type; + + +//void waitfor_pda_queue2empty(); +//void send_pda_cmd(unsigned char cmd); +//unsigned char pop_pda_cmd(struct aqualinkdata *aq_data); + + +unsigned char pop_pda_cmd(struct aqualinkdata *aq_data); + + void *get_aqualink_PDA_device_status( void *ptr ); void *set_aqualink_PDA_device_on_off( void *ptr ); void *set_aqualink_PDA_wakeinit( void *ptr ); void *set_aqualink_PDA_init( void *ptr ); -bool set_PDA_aqualink_heater_setpoint(struct aqualinkdata *aq_data, int val, bool isPool); -bool set_PDA_aqualink_SWG_setpoint(struct aqualinkdata *aq_data, int val); -bool set_PDA_aqualink_freezeprotect_setpoint(struct aqualinkdata *aq_data, int val); +//bool set_PDA_aqualink_heater_setpoint(struct aqualinkdata *aq_data, int val, bool isPool); +//bool set_PDA_aqualink_SWG_setpoint(struct aqualinkdata *aq_data, int val); +void *set_PDA_aqualink_SWG_setpoint(void *ptr); +//bool set_PDA_aqualink_freezeprotect_setpoint(struct aqualinkdata *aq_data, int val); + -bool get_PDA_aqualink_pool_spa_heater_temps(struct aqualinkdata *aq_data); -bool get_PDA_freeze_protect_temp(struct aqualinkdata *aq_data); +void *set_aqualink_PDA_pool_heater_temps( void *ptr ); +void *set_aqualink_PDA_spa_heater_temps( void *ptr ); -bool get_PDA_aqualink_aux_labels(struct aqualinkdata *aq_data); +void *set_aqualink_PDA_freeze_protectsetpoint( void *ptr ); -bool set_PDA_aqualink_boost(struct aqualinkdata *aq_data, bool val); -bool set_PDA_aqualink_time(struct aqualinkdata *aq_data); +//bool get_PDA_aqualink_aux_labels(struct aqualinkdata *aq_data); +void *get_PDA_aqualink_aux_labels( void *ptr ); +void *get_PDA_aqualink_pool_spa_heater_temps( void *ptr ); +//bool set_PDA_aqualink_boost(struct aqualinkdata *aq_data, bool val); +void *set_PDA_aqualink_boost(void *ptr); +//bool set_PDA_aqualink_time(struct aqualinkdata *aq_data); +void *set_PDA_aqualink_time( void *ptr ); + +/* // These are from aq_programmer.c , exposed here for PDA AQ PROGRAMMER void send_cmd(unsigned char cmd); bool push_aq_cmd(unsigned char cmd); bool waitForMessage(struct aqualinkdata *aq_data, char* message, int numMessageReceived); void waitfor_queue2empty(); void longwaitfor_queue2empty(); +*/ + //void pda_programming_thread_check(struct aqualinkdata *aq_data); diff --git a/release/aqualinkd-arm64 b/release/aqualinkd-arm64 index d25f719..6abab5c 100755 Binary files a/release/aqualinkd-arm64 and b/release/aqualinkd-arm64 differ diff --git a/release/aqualinkd-armhf b/release/aqualinkd-armhf index b2c4703..155727a 100755 Binary files a/release/aqualinkd-armhf and b/release/aqualinkd-armhf differ diff --git a/release/serial_logger-arm64 b/release/serial_logger-arm64 index e1b412a..a92386d 100755 Binary files a/release/serial_logger-arm64 and b/release/serial_logger-arm64 differ diff --git a/release/serial_logger-armhf b/release/serial_logger-armhf index ddfa838..23c0159 100755 Binary files a/release/serial_logger-armhf and b/release/serial_logger-armhf differ diff --git a/serial_logger.c b/serial_logger.c index 06c61c4..c3b5fc5 100644 --- a/serial_logger.c +++ b/serial_logger.c @@ -41,7 +41,7 @@ #define SLOG_MAX 80 #define PACKET_MAX 600 -#define VERSION "serial_logger V2.2" +#define VERSION "serial_logger V2.3" /* typedef enum used { @@ -64,6 +64,7 @@ struct aqconfig _aqconfig_; char _panelType[AQ_MSGLEN]; char _panelRev[AQ_MSGLEN]; +bool _panelPDA = false; typedef struct serial_id_log { unsigned char ID; @@ -259,15 +260,17 @@ char* canUseExtended(unsigned char ID) { return " <-- can use for Aqualinkd (PDA mode only)"; } for (i = 0; i < 4; i++) { - if (ID == _goodONETID[i]) + if (ID == _goodONETID[i] && _panelPDA == false) return " <-- can use for Aqualinkd (Extended Device ID)"; } for (i = 0; i < 4; i++) { - if (ID == _goodIAQTID[i]) + if (ID == _goodIAQTID[i] && _panelPDA == false) return " <-- can use for Aqualinkd (Prefered Extended Device ID)"; + else if (ID == _goodIAQTID[i] && _panelPDA == true) + return " <-- can use for Aqualinkd (PDA mode Expermental only)"; } for (i = 0; i < 2; i++) { - if (ID == _goodRSSAID[i]) + if (ID == _goodRSSAID[i] && _panelPDA == false) return " <-- can use for Aqualinkd (RSSA ID)"; } return ""; @@ -294,6 +297,10 @@ void getPanelInfo(int rs_fd, unsigned char *packet_buffer, int packet_length) else if (msgcnt == 3) rsm_strncpy(_panelType, packet_buffer+4, AQ_MSGLEN, packet_length-5); } + + if (_panelType[1] == 'P' && _panelType[2] == 'D') { // PDA Panel + _panelPDA = true; + } } #ifdef SERIAL_LOGGER @@ -395,7 +402,8 @@ int main(int argc, char *argv[]) { _aqconfig_.log_protocol_packets = false; _aqconfig_.log_raw_bytes = false; _aqconfig_.ftdi_low_latency = true; - + _aqconfig_.frame_delay = 10; + printf("AqualinkD %s\n",VERSION); if (getuid() != 0) { @@ -742,16 +750,21 @@ int _serial_logger(int rs_fd, char *port_name, int logPackets, int logLevel, boo if (slog[i].inuse == true) continue; - if (canUseAllB(slog[i].ID) && (mainID == 0x00 || canUsePDA(mainID))) - mainID = slog[i].ID; - if (canUsePDA(slog[i].ID) && mainID == 0x00) - mainID = slog[i].ID; - else if (canUseRSSA(slog[i].ID) && rssaID == 0x00) - rssaID = slog[i].ID; - else if (canUseONET(slog[i].ID) && extID == 0x00) - extID = slog[i].ID; - else if (canUseIQAT(slog[i].ID) && (extID == 0x00 || canUseONET(extID))) - extID = slog[i].ID; + if (!_panelPDA) { + if (canUseAllB(slog[i].ID) && (mainID == 0x00 || canUsePDA(mainID))) + mainID = slog[i].ID; + if (canUsePDA(slog[i].ID) && mainID == 0x00) + mainID = slog[i].ID; + else if (canUseRSSA(slog[i].ID) && rssaID == 0x00) + rssaID = slog[i].ID; + else if (canUseONET(slog[i].ID) && extID == 0x00) + extID = slog[i].ID; + else if (canUseIQAT(slog[i].ID) && (extID == 0x00 || canUseONET(extID))) + extID = slog[i].ID; + } else { + if (canUsePDA(slog[i].ID) && mainID == 0x00) + mainID = slog[i].ID; + } } LOG(RSSD_LOG, LOG_NOTICE, "Suggested aqualinkd.conf values\n"); diff --git a/time.out b/time.out deleted file mode 100644 index 4f88267..0000000 --- a/time.out +++ /dev/null @@ -1,600 +0,0 @@ -Debug: RS Serial: Time 0.59 sec (59437865 ns) -Debug: RS Serial: Time 0.10 sec (10919058 ns) -Debug: RS Serial: Time 0.09 sec (09934119 ns) -Debug: RS Serial: Time 0.17 sec (17965098 ns) -Debug: RS Serial: Time 0.07 sec (07933240 ns) -Debug: RS Serial: Time 0.461 sec (461889391 ns) -Debug: RS Serial: Time 0.09 sec (09914193 ns) -Debug: RS Serial: Time 0.12 sec (12930179 ns) -Debug: RS Serial: Time 0.10 sec (10946132 ns) -Debug: RS Serial: Time 0.09 sec (09939767 ns) -Debug: RS Serial: Time 0.17 sec (17943857 ns) -Debug: RS Serial: Time 0.07 sec (07940721 ns) -Debug: RS Serial: Time 0.64 sec (64939477 ns) -Debug: RS Serial: Time 0.10 sec (10928577 ns) -Debug: RS Serial: Time 0.12 sec (12945622 ns) -Debug: RS Serial: Time 0.10 sec (10947262 ns) -Debug: RS Serial: Time 0.09 sec (09933341 ns) -Debug: RS Serial: Time 0.17 sec (17954134 ns) -Debug: RS Serial: Time 0.08 sec (08930290 ns) -Debug: RS Serial: Time 0.462 sec (462875126 ns) -Debug: RS Serial: Time 0.09 sec (09888860 ns) -Debug: RS Serial: Time 0.13 sec (13926859 ns) -Debug: RS Serial: Time 0.09 sec (09938878 ns) -Debug: RS Serial: Time 0.09 sec (09944415 ns) -Debug: RS Serial: Time 0.17 sec (17948098 ns) -Debug: RS Serial: Time 0.08 sec (08928735 ns) -Debug: RS Serial: Time 0.63 sec (63899667 ns) -Debug: RS Serial: Time 0.09 sec (09912563 ns) -Debug: RS Serial: Time 0.13 sec (13953320 ns) -Debug: RS Serial: Time 0.09 sec (09915989 ns) -Debug: RS Serial: Time 0.10 sec (10925226 ns) -Debug: RS Serial: Time 0.16 sec (16950732 ns) -Debug: RS Serial: Time 0.08 sec (08952531 ns) -Debug: RS Serial: Time 0.463 sec (463932706 ns) -Debug: RS Serial: Time 0.09 sec (09847991 ns) -Debug: RS Serial: Time 0.26 sec (26916220 ns) -Debug: RS Serial: Time 0.10 sec (10896133 ns) -Debug: RS Serial: Time 0.13 sec (13942969 ns) -Debug: RS Serial: Time 0.09 sec (09941396 ns) -Debug: RS Serial: Time 0.10 sec (10936465 ns) -Debug: RS Serial: Time 0.16 sec (16949028 ns) -Debug: RS Serial: Time 0.08 sec (08924216 ns) -Debug: RS Serial: Time 0.60 sec (60934255 ns) -Debug: RS Serial: Time 0.09 sec (09926545 ns) -Debug: RS Serial: Time 0.13 sec (13953173 ns) -Debug: RS Serial: Time 0.09 sec (09950711 ns) -Debug: RS Serial: Time 0.10 sec (10964539 ns) -Debug: RS Serial: Time 0.16 sec (16895881 ns) -Debug: RS Serial: Time 0.09 sec (09923582 ns) -Debug: RS Serial: Time 0.62 sec (62950690 ns) -Debug: RS Serial: Time 0.10 sec (10915410 ns) -Debug: RS Serial: Time 0.08 sec (08929772 ns) -Debug: RS Serial: Time 0.61 sec (61924307 ns) -Debug: RS Serial: Time 0.09 sec (09953915 ns) -Debug: RS Serial: Time 0.10 sec (10931521 ns) -Debug: RS Serial: Time 0.16 sec (16944009 ns) -Debug: RS Serial: Time 0.09 sec (09926138 ns) -Debug: RS Serial: Time 0.64 sec (64950808 ns) -Debug: RS Serial: Time 0.09 sec (09962155 ns) -Debug: RS Serial: Time 0.13 sec (13942839 ns) -Debug: RS Serial: Time 0.09 sec (09942452 ns) -Debug: RS Serial: Time 0.10 sec (10958058 ns) -Debug: RS Serial: Time 0.16 sec (16946065 ns) -Debug: RS Serial: Time 0.09 sec (09959840 ns) -Debug: RS Serial: Time 0.62 sec (62955707 ns) -Debug: RS Serial: Time 0.10 sec (10928058 ns) -Debug: RS Serial: Time 0.12 sec (12953307 ns) -Debug: RS Serial: Time 0.09 sec (09964488 ns) -Debug: RS Serial: Time 0.10 sec (10953502 ns) -Debug: RS Serial: Time 0.16 sec (16963398 ns) -Debug: RS Serial: Time 0.09 sec (09928804 ns) -Debug: RS Serial: Time 0.62 sec (62947318 ns) -Debug: RS Serial: Time 0.10 sec (10940836 ns) -Debug: RS Serial: Time 0.12 sec (12956233 ns) -Debug: RS Serial: Time 0.09 sec (09951229 ns) -Debug: RS Serial: Time 0.10 sec (10973298 ns) -Debug: RS Serial: Time 0.16 sec (16955713 ns) -Debug: RS Serial: Time 0.09 sec (09937285 ns) -Debug: RS Serial: Time 0.62 sec (62953874 ns) -Debug: RS Serial: Time 0.10 sec (10948465 ns) -Debug: RS Serial: Time 0.13 sec (13001954 ns) -Debug: RS Serial: Time 0.09 sec (09878953 ns) -Debug: RS Serial: Time 0.10 sec (10947780 ns) -Debug: RS Serial: Time 0.16 sec (16950287 ns) -Debug: RS Serial: Time 0.09 sec (09959118 ns) -Debug: RS Serial: Time 0.62 sec (62957096 ns) -Debug: RS Serial: Time 0.10 sec (10920910 ns) -Debug: RS Serial: Time 0.12 sec (12954474 ns) -Debug: RS Serial: Time 0.09 sec (09957785 ns) -Debug: RS Serial: Time 0.10 sec (10964909 ns) -Debug: RS Serial: Time 0.16 sec (16956398 ns) -Debug: RS Serial: Time 0.09 sec (09937174 ns) -Debug: RS Serial: Time 0.62 sec (62955911 ns) -Debug: RS Serial: Time 0.10 sec (10948725 ns) -Debug: RS Serial: Time 0.12 sec (12963122 ns) -Debug: RS Serial: Time 0.09 sec (09961488 ns) -Debug: RS Serial: Time 0.10 sec (10959539 ns) -Debug: RS Serial: Time 0.16 sec (16977398 ns) -Debug: RS Serial: Time 0.09 sec (09938285 ns) -Debug: RS Serial: Time 0.62 sec (62952077 ns) -Debug: RS Serial: Time 0.10 sec (10960335 ns) -Debug: RS Serial: Time 0.08 sec (08950568 ns) -Debug: RS Serial: Time 0.61 sec (61954767 ns) -Debug: RS Serial: Time 0.09 sec (09911304 ns) -Debug: RS Serial: Time 0.10 sec (10956409 ns) -Debug: RS Serial: Time 0.16 sec (16949916 ns) -Debug: RS Serial: Time 0.08 sec (08947049 ns) -Debug: RS Serial: Time 0.60 sec (60954568 ns) -Debug: RS Serial: Time 0.10 sec (10936910 ns) -Debug: RS Serial: Time 0.12 sec (12932900 ns) -Debug: RS Serial: Time 0.09 sec (09938508 ns) -Debug: RS Serial: Time 0.10 sec (10954113 ns) -Debug: RS Serial: Time 0.17 sec (17947356 ns) -Debug: RS Serial: Time 0.08 sec (08962345 ns) -Debug: RS Serial: Time 0.62 sec (62951132 ns) -Debug: RS Serial: Time 0.10 sec (10952335 ns) -Debug: RS Serial: Time 0.12 sec (12948751 ns) -Debug: RS Serial: Time 0.09 sec (09958507 ns) -Debug: RS Serial: Time 0.10 sec (10960836 ns) -Debug: RS Serial: Time 0.17 sec (17951505 ns) -Debug: RS Serial: Time 0.08 sec (08956660 ns) -Debug: RS Serial: Time 0.62 sec (62955818 ns) -Debug: RS Serial: Time 0.10 sec (10947558 ns) -Debug: RS Serial: Time 0.26 sec (26958849 ns) -Debug: RS Serial: Time 0.09 sec (09946081 ns) -Debug: RS Serial: Time 0.13 sec (13959357 ns) -Debug: RS Serial: Time 0.09 sec (09960452 ns) -Debug: RS Serial: Time 0.10 sec (10964298 ns) -Debug: RS Serial: Time 0.16 sec (16952806 ns) -Debug: RS Serial: Time 0.08 sec (08948679 ns) -Debug: RS Serial: Time 0.60 sec (60949513 ns) -Debug: RS Serial: Time 0.10 sec (10952150 ns) -Debug: RS Serial: Time 0.12 sec (12959880 ns) -Debug: RS Serial: Time 0.09 sec (09914638 ns) -Debug: RS Serial: Time 0.10 sec (10998871 ns) -Debug: RS Serial: Time 0.17 sec (17910320 ns) -Debug: RS Serial: Time 0.08 sec (08955735 ns) -Debug: RS Serial: Time 0.62 sec (62945910 ns) -Debug: RS Serial: Time 0.10 sec (10948447 ns) -Debug: RS Serial: Time 0.12 sec (12948047 ns) -Debug: RS Serial: Time 0.09 sec (09968544 ns) -Debug: RS Serial: Time 0.10 sec (10951947 ns) -Debug: RS Serial: Time 0.17 sec (17954745 ns) -Debug: RS Serial: Time 0.08 sec (08956530 ns) -Debug: RS Serial: Time 0.62 sec (62948540 ns) -Debug: RS Serial: Time 0.10 sec (10942132 ns) -Debug: RS Serial: Time 0.12 sec (12948047 ns) -Debug: RS Serial: Time 0.10 sec (10968613 ns) -Debug: RS Serial: Time 0.09 sec (09963081 ns) -Debug: RS Serial: Time 0.17 sec (17976170 ns) -Debug: RS Serial: Time 0.08 sec (08932920 ns) -Debug: RS Serial: Time 0.62 sec (62911503 ns) -Debug: RS Serial: Time 0.10 sec (10918299 ns) -Debug: RS Serial: Time 0.08 sec (08979567 ns) -Debug: RS Serial: Time 0.61 sec (61869176 ns) -Debug: RS Serial: Time 0.10 sec (10947576 ns) -Debug: RS Serial: Time 0.09 sec (09915619 ns) -Debug: RS Serial: Time 0.16 sec (16978526 ns) -Debug: RS Serial: Time 0.08 sec (08879903 ns) -Debug: RS Serial: Time 0.60 sec (60951160 ns) -Debug: RS Serial: Time 0.10 sec (10942909 ns) -Debug: RS Serial: Time 0.12 sec (12960380 ns) -Debug: RS Serial: Time 0.10 sec (10966909 ns) -Debug: RS Serial: Time 0.09 sec (09951470 ns) -Debug: RS Serial: Time 0.17 sec (17932227 ns) -Debug: RS Serial: Time 0.08 sec (08941919 ns) -Debug: RS Serial: Time 0.26 sec (26955311 ns) -Debug: RS Serial: Time 0.10 sec (10954798 ns) -Debug: RS Serial: Time 0.12 sec (12942789 ns) -Debug: RS Serial: Time 0.10 sec (10948835 ns) -Debug: RS Serial: Time 0.08 sec (08945197 ns) -Debug: RS Serial: Time 0.20 sec (20949360 ns) -Debug: RS Serial: Time 0.17 sec (17943226 ns) -Debug: RS Serial: Time 0.08 sec (08946067 ns) -Debug: RS Serial: Time 0.63 sec (63960775 ns) -Debug: RS Serial: Time 0.10 sec (10913318 ns) -Debug: RS Serial: Time 0.12 sec (12917493 ns) -Debug: RS Serial: Time 0.10 sec (10946299 ns) -Debug: RS Serial: Time 0.26 sec (26928274 ns) -Debug: RS Serial: Time 0.21 sec (21964687 ns) -Debug: RS Serial: Time 0.17 sec (17912819 ns) -Debug: RS Serial: Time 0.08 sec (08932956 ns) -Debug: RS Serial: Time 0.63 sec (63955423 ns) -Debug: RS Serial: Time 0.10 sec (10957150 ns) -Debug: RS Serial: Time 0.12 sec (12970251 ns) -Debug: RS Serial: Time 0.10 sec (10945946 ns) -Debug: RS Serial: Time 0.08 sec (08956326 ns) -Debug: RS Serial: Time 0.20 sec (20955138 ns) -Debug: RS Serial: Time 0.16 sec (16950249 ns) -Debug: RS Serial: Time 0.08 sec (08960919 ns) -Debug: RS Serial: Time 0.63 sec (63963645 ns) -Debug: RS Serial: Time 0.10 sec (10888226 ns) -Debug: RS Serial: Time 0.12 sec (12945381 ns) -Debug: RS Serial: Time 0.10 sec (10956668 ns) -Debug: RS Serial: Time 0.26 sec (26977737 ns) -Debug: RS Serial: Time 0.21 sec (21913652 ns) -Debug: RS Serial: Time 0.16 sec (16960749 ns) -Debug: RS Serial: Time 0.08 sec (08963160 ns) -Debug: RS Serial: Time 0.58 sec (58983336 ns) -Debug: RS Serial: Time 0.59 sec (59798410 ns) -Debug: RS Serial: Time 0.10 sec (10949113 ns) -Debug: RS Serial: Time 0.12 sec (12950807 ns) -Debug: RS Serial: Time 0.10 sec (10965502 ns) -Debug: RS Serial: Time 0.08 sec (08950308 ns) -Debug: RS Serial: Time 0.56 sec (56934902 ns) -Debug: RS Serial: Time 0.16 sec (16948379 ns) -Debug: RS Serial: Time 0.08 sec (08948920 ns) -Debug: RS Serial: Time 0.63 sec (63964015 ns) -Debug: RS Serial: Time 0.10 sec (10923613 ns) -Debug: RS Serial: Time 0.12 sec (12964380 ns) -Debug: RS Serial: Time 0.10 sec (10966834 ns) -Debug: RS Serial: Time 0.08 sec (08948160 ns) -Debug: RS Serial: Time 0.55 sec (55974795 ns) -Debug: RS Serial: Time 0.17 sec (17899339 ns) -Debug: RS Serial: Time 0.07 sec (07947795 ns) -Debug: RS Serial: Time 0.63 sec (63953088 ns) -Debug: RS Serial: Time 0.10 sec (10950705 ns) -Debug: RS Serial: Time 0.26 sec (26956774 ns) -Debug: RS Serial: Time 0.10 sec (10946520 ns) -Debug: RS Serial: Time 0.12 sec (12958547 ns) -Debug: RS Serial: Time 0.10 sec (10959909 ns) -Debug: RS Serial: Time 0.11 sec (11952904 ns) -Debug: RS Serial: Time 0.29 sec (29952536 ns) -Debug: RS Serial: Time 0.07 sec (07956998 ns) -Debug: RS Serial: Time 0.56 sec (56971586 ns) -Debug: RS Serial: Time 0.16 sec (16958749 ns) -Debug: RS Serial: Time 0.08 sec (08926976 ns) -Debug: RS Serial: Time 0.64 sec (64950139 ns) -Debug: RS Serial: Time 0.09 sec (09956932 ns) -Debug: RS Serial: Time 0.12 sec (12959806 ns) -Debug: RS Serial: Time 0.10 sec (10950780 ns) -Debug: RS Serial: Time 0.08 sec (08973660 ns) -Debug: RS Serial: Time 0.55 sec (55932351 ns) -Debug: RS Serial: Time 0.17 sec (17944337 ns) -Debug: RS Serial: Time 0.07 sec (07929203 ns) -Debug: RS Serial: Time 0.64 sec (64952825 ns) -Debug: RS Serial: Time 0.09 sec (09947155 ns) -Debug: RS Serial: Time 0.12 sec (12967861 ns) -Debug: RS Serial: Time 0.10 sec (10944669 ns) -Debug: RS Serial: Time 0.08 sec (08961622 ns) -Debug: RS Serial: Time 0.55 sec (55952758 ns) -Debug: RS Serial: Time 0.17 sec (17959504 ns) -Debug: RS Serial: Time 0.07 sec (07957979 ns) -Debug: RS Serial: Time 0.64 sec (64946380 ns) -Debug: RS Serial: Time 0.09 sec (09957081 ns) -Debug: RS Serial: Time 0.08 sec (08939826 ns) -Debug: RS Serial: Time 0.62 sec (62950982 ns) -Debug: RS Serial: Time 0.09 sec (09932896 ns) -Debug: RS Serial: Time 0.08 sec (08957881 ns) -Debug: RS Serial: Time 0.57 sec (57945210 ns) -Debug: RS Serial: Time 0.17 sec (17946670 ns) -Debug: RS Serial: Time 0.07 sec (07953146 ns) -Debug: RS Serial: Time 0.64 sec (64951064 ns) -Debug: RS Serial: Time 0.09 sec (09931507 ns) -Debug: RS Serial: Time 0.13 sec (13962690 ns) -Debug: RS Serial: Time 0.09 sec (09952655 ns) -Debug: RS Serial: Time 0.08 sec (08954586 ns) -Debug: RS Serial: Time 0.55 sec (55958628 ns) -Debug: RS Serial: Time 0.17 sec (17960688 ns) -Debug: RS Serial: Time 0.07 sec (07940239 ns) -Debug: RS Serial: Time 0.64 sec (64956972 ns) -Debug: RS Serial: Time 0.09 sec (09948414 ns) -Debug: RS Serial: Time 0.13 sec (13958449 ns) -Debug: RS Serial: Time 0.09 sec (09950989 ns) -Debug: RS Serial: Time 0.08 sec (08959456 ns) -Debug: RS Serial: Time 0.55 sec (55951739 ns) -Debug: RS Serial: Time 0.17 sec (17958781 ns) -Debug: RS Serial: Time 0.07 sec (07946813 ns) -Debug: RS Serial: Time 0.64 sec (64950843 ns) -Debug: RS Serial: Time 0.09 sec (09931544 ns) -Debug: RS Serial: Time 0.13 sec (13960153 ns) -Debug: RS Serial: Time 0.09 sec (09932026 ns) -Debug: RS Serial: Time 0.08 sec (08953790 ns) -Debug: RS Serial: Time 0.56 sec (56936087 ns) -Debug: RS Serial: Time 0.16 sec (16937657 ns) -Debug: RS Serial: Time 0.07 sec (07935943 ns) -Debug: RS Serial: Time 0.64 sec (64948231 ns) -Debug: RS Serial: Time 0.09 sec (09945636 ns) -Debug: RS Serial: Time 0.13 sec (13959468 ns) -Debug: RS Serial: Time 0.09 sec (09954081 ns) -Debug: RS Serial: Time 0.08 sec (08962900 ns) -Debug: RS Serial: Time 0.56 sec (56932216 ns) -Debug: RS Serial: Time 0.16 sec (16941009 ns) -Debug: RS Serial: Time 0.08 sec (08957512 ns) -Debug: RS Serial: Time 0.63 sec (63936255 ns) -Debug: RS Serial: Time 0.09 sec (09941100 ns) -Debug: RS Serial: Time 0.13 sec (13965709 ns) -Debug: RS Serial: Time 0.09 sec (09954877 ns) -Debug: RS Serial: Time 0.09 sec (09957118 ns) -Debug: RS Serial: Time 0.53 sec (53915639 ns) -Debug: RS Serial: Time 0.56 sec (56952901 ns) -Debug: RS Serial: Time 0.16 sec (16936731 ns) -Debug: RS Serial: Time 0.08 sec (08956567 ns) -Debug: RS Serial: Time 0.63 sec (63943291 ns) -Debug: RS Serial: Time 0.10 sec (10953464 ns) -Debug: RS Serial: Time 0.12 sec (12934992 ns) -Debug: RS Serial: Time 0.09 sec (09957988 ns) -Debug: RS Serial: Time 0.09 sec (09955784 ns) -Debug: RS Serial: Time 0.55 sec (55954812 ns) -Debug: RS Serial: Time 0.16 sec (16950694 ns) -Debug: RS Serial: Time 0.08 sec (08957622 ns) -Debug: RS Serial: Time 0.63 sec (63929495 ns) -Debug: RS Serial: Time 0.09 sec (09942340 ns) -Debug: RS Serial: Time 0.26 sec (26963661 ns) -Debug: RS Serial: Time 0.10 sec (10923465 ns) -Debug: RS Serial: Time 0.13 sec (13946375 ns) -Debug: RS Serial: Time 0.09 sec (09961062 ns) -Debug: RS Serial: Time 0.10 sec (10949909 ns) -Debug: RS Serial: Time 0.16 sec (16969656 ns) -Debug: RS Serial: Time 0.08 sec (08943326 ns) -Debug: RS Serial: Time 0.60 sec (60968084 ns) -Debug: RS Serial: Time 0.10 sec (10937539 ns) -Debug: RS Serial: Time 0.13 sec (13962727 ns) -Debug: RS Serial: Time 0.09 sec (09960117 ns) -Debug: RS Serial: Time 0.10 sec (10949094 ns) -Debug: RS Serial: Time 0.16 sec (16965619 ns) -Debug: RS Serial: Time 0.09 sec (09946006 ns) -Debug: RS Serial: Time 0.61 sec (61952171 ns) -Debug: RS Serial: Time 0.09 sec (09956173 ns) -Debug: RS Serial: Time 0.13 sec (13966301 ns) -Debug: RS Serial: Time 0.09 sec (09916786 ns) -Debug: RS Serial: Time 0.10 sec (10940465 ns) -Debug: RS Serial: Time 0.16 sec (16950268 ns) -Debug: RS Serial: Time 0.09 sec (09960228 ns) -Debug: RS Serial: Time 0.62 sec (62949871 ns) -Debug: RS Serial: Time 0.10 sec (10949465 ns) -Debug: RS Serial: Time 0.12 sec (12961750 ns) -Debug: RS Serial: Time 0.09 sec (09963228 ns) -Debug: RS Serial: Time 0.10 sec (10950687 ns) -Debug: RS Serial: Time 0.16 sec (16960082 ns) -Debug: RS Serial: Time 0.10 sec (10965057 ns) -Debug: RS Serial: Time 0.61 sec (61975671 ns) -Debug: RS Serial: Time 0.10 sec (10953205 ns) -Debug: RS Serial: Time 0.13 sec (13970652 ns) -Debug: RS Serial: Time 0.09 sec (09951747 ns) -Debug: RS Serial: Time 0.10 sec (10961612 ns) -Debug: RS Serial: Time 0.16 sec (16967730 ns) -Debug: RS Serial: Time 0.09 sec (09960154 ns) -Debug: RS Serial: Time 0.55 sec (55971960 ns) -Debug: RS Serial: Time 0.59 sec (59976903 ns) -Debug: RS Serial: Time 0.10 sec (10865374 ns) -Debug: RS Serial: Time 0.12 sec (12969361 ns) -Debug: RS Serial: Time 0.09 sec (09951432 ns) -Debug: RS Serial: Time 0.10 sec (10950965 ns) -Debug: RS Serial: Time 0.16 sec (16968248 ns) -Debug: RS Serial: Time 0.09 sec (09949525 ns) -Debug: RS Serial: Time 0.63 sec (63948142 ns) -Debug: RS Serial: Time 0.10 sec (10950279 ns) -Debug: RS Serial: Time 0.12 sec (12967139 ns) -Debug: RS Serial: Time 0.09 sec (09945340 ns) -Debug: RS Serial: Time 0.10 sec (10963446 ns) -Debug: RS Serial: Time 0.16 sec (16951823 ns) -Debug: RS Serial: Time 0.09 sec (09954821 ns) -Debug: RS Serial: Time 0.62 sec (62933925 ns) -Debug: RS Serial: Time 0.10 sec (10968260 ns) -Debug: RS Serial: Time 0.12 sec (12949417 ns) -Debug: RS Serial: Time 0.09 sec (09927155 ns) -Debug: RS Serial: Time 0.10 sec (10961927 ns) -Debug: RS Serial: Time 0.16 sec (16960156 ns) -Debug: RS Serial: Time 0.09 sec (09962507 ns) -Debug: RS Serial: Time 0.62 sec (62961332 ns) -Debug: RS Serial: Time 0.10 sec (10936427 ns) -Debug: RS Serial: Time 0.12 sec (12958121 ns) -Debug: RS Serial: Time 0.09 sec (09964284 ns) -Debug: RS Serial: Time 0.10 sec (10950539 ns) -Debug: RS Serial: Time 0.17 sec (17964632 ns) -Debug: RS Serial: Time 0.08 sec (08959067 ns) -Debug: RS Serial: Time 0.62 sec (62951721 ns) -Debug: RS Serial: Time 0.10 sec (10951575 ns) -Debug: RS Serial: Time 0.12 sec (12960435 ns) -Debug: RS Serial: Time 0.09 sec (09960376 ns) -Debug: RS Serial: Time 0.10 sec (10954335 ns) -Debug: RS Serial: Time 0.17 sec (17966503 ns) -Debug: RS Serial: Time 0.08 sec (08958474 ns) -Debug: RS Serial: Time 0.62 sec (62948628 ns) -Debug: RS Serial: Time 0.10 sec (10949557 ns) -Debug: RS Serial: Time 0.12 sec (12956973 ns) -Debug: RS Serial: Time 0.09 sec (09954210 ns) -Debug: RS Serial: Time 0.10 sec (10961260 ns) -Debug: RS Serial: Time 0.17 sec (17958355 ns) -Debug: RS Serial: Time 0.08 sec (08960067 ns) -Debug: RS Serial: Time 0.62 sec (62950962 ns) -Debug: RS Serial: Time 0.10 sec (10958428 ns) -Debug: RS Serial: Time 0.12 sec (12953121 ns) -Debug: RS Serial: Time 0.09 sec (09960839 ns) -Debug: RS Serial: Time 0.10 sec (10963112 ns) -Debug: RS Serial: Time 0.17 sec (17952577 ns) -Debug: RS Serial: Time 0.08 sec (08959826 ns) -Debug: RS Serial: Time 0.56 sec (56948529 ns) -Debug: RS Serial: Time 0.59 sec (59926588 ns) -Debug: RS Serial: Time 0.10 sec (10955390 ns) -Debug: RS Serial: Time 0.12 sec (12957565 ns) -Debug: RS Serial: Time 0.09 sec (09955562 ns) -Debug: RS Serial: Time 0.10 sec (10960908 ns) -Debug: RS Serial: Time 0.17 sec (17951744 ns) -Debug: RS Serial: Time 0.08 sec (08960141 ns) -Debug: RS Serial: Time 0.63 sec (63951734 ns) -Debug: RS Serial: Time 0.10 sec (10936946 ns) -Debug: RS Serial: Time 0.26 sec (26958068 ns) -Debug: RS Serial: Time 0.09 sec (09965431 ns) -Debug: RS Serial: Time 0.13 sec (13952486 ns) -Debug: RS Serial: Time 0.10 sec (10957872 ns) -Debug: RS Serial: Time 0.09 sec (09955636 ns) -Debug: RS Serial: Time 0.16 sec (16971174 ns) -Debug: RS Serial: Time 0.08 sec (08945715 ns) -Debug: RS Serial: Time 0.60 sec (60953805 ns) -Debug: RS Serial: Time 0.10 sec (10950538 ns) -Debug: RS Serial: Time 0.12 sec (12958824 ns) -Debug: RS Serial: Time 0.10 sec (10952298 ns) -Debug: RS Serial: Time 0.09 sec (09959821 ns) -Debug: RS Serial: Time 0.17 sec (17958651 ns) -Debug: RS Serial: Time 0.08 sec (08954381 ns) -Debug: RS Serial: Time 0.62 sec (62965257 ns) -Debug: RS Serial: Time 0.10 sec (10987685 ns) -Debug: RS Serial: Time 0.13 sec (13912913 ns) -Debug: RS Serial: Time 0.10 sec (10962889 ns) -Debug: RS Serial: Time 0.09 sec (09956784 ns) -Debug: RS Serial: Time 0.17 sec (17967743 ns) -Debug: RS Serial: Time 0.08 sec (08954418 ns) -Debug: RS Serial: Time 0.61 sec (61943300 ns) -Debug: RS Serial: Time 0.10 sec (10946076 ns) -Debug: RS Serial: Time 0.12 sec (12978861 ns) -Debug: RS Serial: Time 0.10 sec (10939983 ns) -Debug: RS Serial: Time 0.09 sec (09964173 ns) -Debug: RS Serial: Time 0.17 sec (17944596 ns) -Debug: RS Serial: Time 0.08 sec (08950974 ns) -Debug: RS Serial: Time 0.62 sec (62930203 ns) -Debug: RS Serial: Time 0.10 sec (10943113 ns) -Debug: RS Serial: Time 0.12 sec (12958861 ns) -Debug: RS Serial: Time 0.10 sec (10952464 ns) -Debug: RS Serial: Time 0.09 sec (09963228 ns) -Debug: RS Serial: Time 0.17 sec (17939133 ns) -Debug: RS Serial: Time 0.08 sec (08942252 ns) -Debug: RS Serial: Time 0.62 sec (62952035 ns) -Debug: RS Serial: Time 0.10 sec (10927391 ns) -Debug: RS Serial: Time 0.12 sec (12926880 ns) -Debug: RS Serial: Time 0.10 sec (10967834 ns) -Debug: RS Serial: Time 0.09 sec (09975746 ns) -Debug: RS Serial: Time 0.17 sec (17951632 ns) -Debug: RS Serial: Time 0.08 sec (08948752 ns) -Debug: RS Serial: Time 0.56 sec (56967676 ns) -Debug: RS Serial: Time 0.59 sec (59935680 ns) -Debug: RS Serial: Time 0.10 sec (10950779 ns) -Debug: RS Serial: Time 0.12 sec (12944862 ns) -Debug: RS Serial: Time 0.10 sec (10960853 ns) -Debug: RS Serial: Time 0.09 sec (09956192 ns) -Debug: RS Serial: Time 0.17 sec (17951707 ns) -Debug: RS Serial: Time 0.08 sec (08961789 ns) -Debug: RS Serial: Time 0.63 sec (63955585 ns) -Debug: RS Serial: Time 0.10 sec (10931872 ns) -Debug: RS Serial: Time 0.12 sec (12948917 ns) -Debug: RS Serial: Time 0.10 sec (10956612 ns) -Debug: RS Serial: Time 0.12 sec (12961009 ns) -Debug: RS Serial: Time 0.14 sec (14953610 ns) -Debug: RS Serial: Time 0.58 sec (58959462 ns) -Debug: RS Serial: Time 0.16 sec (16947230 ns) -Debug: RS Serial: Time 0.60 sec (60946193 ns) -Debug: RS Serial: Time 0.15 sec (15944661 ns) -Debug: RS Serial: Time 0.08 sec (08958196 ns) -Debug: RS Serial: Time 0.17 sec (17959984 ns) -Debug: RS Serial: Time 0.07 sec (07943683 ns) -Debug: RS Serial: Time 0.63 sec (63952381 ns) -Debug: RS Serial: Time 0.10 sec (10937317 ns) -Debug: RS Serial: Time 0.12 sec (12942417 ns) -Debug: RS Serial: Time 0.10 sec (10962556 ns) -Debug: RS Serial: Time 0.09 sec (09957154 ns) -Debug: RS Serial: Time 0.17 sec (17952521 ns) -Debug: RS Serial: Time 0.08 sec (08951067 ns) -Debug: RS Serial: Time 0.62 sec (62959071 ns) -Debug: RS Serial: Time 0.10 sec (10950094 ns) -Debug: RS Serial: Time 0.12 sec (12934899 ns) -Debug: RS Serial: Time 0.10 sec (10952667 ns) -Debug: RS Serial: Time 0.09 sec (09959858 ns) -Debug: RS Serial: Time 0.17 sec (17954077 ns) -Debug: RS Serial: Time 0.08 sec (08962159 ns) -Debug: RS Serial: Time 0.62 sec (62939721 ns) -Debug: RS Serial: Time 0.10 sec (10942816 ns) -Debug: RS Serial: Time 0.12 sec (12959899 ns) -Debug: RS Serial: Time 0.10 sec (10961631 ns) -Debug: RS Serial: Time 0.09 sec (09955654 ns) -Debug: RS Serial: Time 0.17 sec (17953836 ns) -Debug: RS Serial: Time 0.08 sec (08963103 ns) -Debug: RS Serial: Time 0.463 sec (463943040 ns) -Debug: RS Serial: Time 0.09 sec (09827139 ns) -Debug: RS Serial: Time 0.26 sec (26962290 ns) -Debug: RS Serial: Time 0.10 sec (10934576 ns) -Debug: RS Serial: Time 0.13 sec (13962782 ns) -Debug: RS Serial: Time 0.09 sec (09950506 ns) -Debug: RS Serial: Time 0.09 sec (09961524 ns) -Debug: RS Serial: Time 0.17 sec (17949096 ns) -Debug: RS Serial: Time 0.07 sec (07956053 ns) -Debug: RS Serial: Time 0.61 sec (61962779 ns) -Debug: RS Serial: Time 0.09 sec (09951617 ns) -Debug: RS Serial: Time 0.13 sec (13972985 ns) -Debug: RS Serial: Time 0.09 sec (09955191 ns) -Debug: RS Serial: Time 0.09 sec (09958969 ns) -Debug: RS Serial: Time 0.17 sec (17941484 ns) -Debug: RS Serial: Time 0.08 sec (08965603 ns) -Debug: RS Serial: Time 0.456 sec (456883334 ns) -Debug: RS Serial: Time 0.60 sec (60906415 ns) -Debug: RS Serial: Time 0.09 sec (09942321 ns) -Debug: RS Serial: Time 0.13 sec (13948523 ns) -Debug: RS Serial: Time 0.09 sec (09968969 ns) -Debug: RS Serial: Time 0.10 sec (10943297 ns) -Debug: RS Serial: Time 0.16 sec (16966099 ns) -Debug: RS Serial: Time 0.07 sec (07948850 ns) -Debug: RS Serial: Time 0.65 sec (65949703 ns) -Debug: RS Serial: Time 0.09 sec (09958173 ns) -Debug: RS Serial: Time 0.13 sec (13952671 ns) -Debug: RS Serial: Time 0.09 sec (09958247 ns) -Debug: RS Serial: Time 0.10 sec (10955316 ns) -Debug: RS Serial: Time 0.16 sec (16963174 ns) -Debug: RS Serial: Time 0.09 sec (09943673 ns) -Debug: RS Serial: Time 0.462 sec (462892078 ns) -Debug: RS Serial: Time 0.09 sec (09931192 ns) -Debug: RS Serial: Time 0.13 sec (13954004 ns) -Debug: RS Serial: Time 0.09 sec (09947118 ns) -Debug: RS Serial: Time 0.10 sec (10959760 ns) -Debug: RS Serial: Time 0.16 sec (16959600 ns) -Debug: RS Serial: Time 0.09 sec (09944598 ns) -Debug: RS Serial: Time 0.62 sec (62906089 ns) -Debug: RS Serial: Time 0.10 sec (10943853 ns) -Debug: RS Serial: Time 0.12 sec (12963416 ns) -Debug: RS Serial: Time 0.09 sec (09963190 ns) -Debug: RS Serial: Time 0.10 sec (10972389 ns) -Debug: RS Serial: Time 0.16 sec (16950100 ns) -Debug: RS Serial: Time 0.09 sec (09958599 ns) -Debug: RS Serial: Time 0.462 sec (462882870 ns) -Debug: RS Serial: Time 0.10 sec (10930908 ns) -Debug: RS Serial: Time 0.25 sec (25963554 ns) -Debug: RS Serial: Time 0.10 sec (10949278 ns) -Debug: RS Serial: Time 0.13 sec (13953541 ns) -Debug: RS Serial: Time 0.09 sec (09961061 ns) -Debug: RS Serial: Time 0.10 sec (10958760 ns) -Debug: RS Serial: Time 0.16 sec (16948063 ns) -Debug: RS Serial: Time 0.08 sec (08962863 ns) -Debug: RS Serial: Time 0.60 sec (60960246 ns) -Debug: RS Serial: Time 0.10 sec (10957204 ns) -Debug: RS Serial: Time 0.12 sec (12939269 ns) -Debug: RS Serial: Time 0.09 sec (09962654 ns) -Debug: RS Serial: Time 0.10 sec (10958890 ns) -Debug: RS Serial: Time 0.16 sec (16956989 ns) -Debug: RS Serial: Time 0.09 sec (09951117 ns) -Debug: RS Serial: Time 0.62 sec (62961347 ns) -Debug: RS Serial: Time 0.10 sec (10934427 ns) -Debug: RS Serial: Time 0.12 sec (12942750 ns) -Debug: RS Serial: Time 0.09 sec (09958136 ns) -Debug: RS Serial: Time 0.10 sec (10938797 ns) -Debug: RS Serial: Time 0.16 sec (16941693 ns) -Debug: RS Serial: Time 0.09 sec (09944488 ns) -Debug: RS Serial: Time 0.56 sec (56941841 ns) -Debug: RS Serial: Time 0.59 sec (59923845 ns) -Debug: RS Serial: Time 0.10 sec (10970241 ns) -Debug: RS Serial: Time 0.12 sec (12940046 ns) -Debug: RS Serial: Time 0.09 sec (09943876 ns) -Debug: RS Serial: Time 0.10 sec (10956186 ns) -Debug: RS Serial: Time 0.17 sec (17965576 ns) -Debug: RS Serial: Time 0.08 sec (08946196 ns) -Debug: RS Serial: Time 0.63 sec (63949564 ns) -Debug: RS Serial: Time 0.10 sec (10936205 ns) -Debug: RS Serial: Time 0.12 sec (12953786 ns) -Debug: RS Serial: Time 0.09 sec (09962654 ns) -Debug: RS Serial: Time 0.10 sec (10949778 ns) -Debug: RS Serial: Time 0.17 sec (17961446 ns) -Debug: RS Serial: Time 0.08 sec (08945474 ns) -Debug: RS Serial: Time 0.62 sec (62968049 ns) -Debug: RS Serial: Time 0.10 sec (10937816 ns) -Debug: RS Serial: Time 0.12 sec (12966361 ns) -Debug: RS Serial: Time 0.09 sec (09959969 ns) -Debug: RS Serial: Time 0.10 sec (10949667 ns) -Debug: RS Serial: Time 0.17 sec (17956002 ns) -Debug: RS Serial: Time 0.08 sec (08957418 ns) -Debug: RS Serial: Time 0.62 sec (62947735 ns) -Debug: RS Serial: Time 0.10 sec (10951334 ns) -Debug: RS Serial: Time 0.12 sec (12962417 ns) -Debug: RS Serial: Time 0.09 sec (09944617 ns) -Debug: RS Serial: Time 0.10 sec (10962556 ns) -Debug: RS Serial: Time 0.17 sec (17945354 ns) -Debug: RS Serial: Time 0.08 sec (08963603 ns) -Debug: RS Serial: Time 0.62 sec (62971198 ns) -Debug: RS Serial: Time 0.10 sec (10948297 ns) -Debug: RS Serial: Time 0.12 sec (12955416 ns) -Debug: RS Serial: Time 0.09 sec (09965950 ns) -Debug: RS Serial: Time 0.10 sec (10952464 ns) -Debug: RS Serial: Time 0.17 sec (17963298 ns) -Debug: RS Serial: Time 0.08 sec (08954437 ns) -Debug: RS Serial: Time 0.62 sec (62976476 ns) -Debug: RS Serial: Time 0.10 sec (10942890 ns) -Debug: RS Serial: Time 0.12 sec (12954083 ns) -Debug: RS Serial: Time 0.10 sec (10891113 ns) -Debug: RS Serial: Time 0.09 sec (09956728 ns) -Debug: RS Serial: Time 0.17 sec (17952372 ns) -Debug: RS Serial: Time 0.08 sec (08953196 ns) -Debug: RS Serial: Time 0.62 sec (62958087 ns) -Debug: RS Serial: Time 0.10 sec (10947223 ns) -Debug: RS Serial: Time 0.12 sec (12998156 ns) -Debug: RS Serial: Time 0.10 sec (10908947 ns) -Debug: RS Serial: Time 0.09 sec (09944839 ns) -Debug: RS Serial: Time 0.17 sec (17942521 ns) -Debug: RS Serial: Time 0.08 sec (08954029 ns) diff --git a/version.h b/version.h index fc236b3..4f13a49 100644 --- a/version.h +++ b/version.h @@ -2,4 +2,4 @@ #define AQUALINKD_NAME "Aqualink Daemon" #define AQUALINKD_SHORT_NAME "AqualinkD" -#define AQUALINKD_VERSION "2.3.7" +#define AQUALINKD_VERSION "2.3.8 (dev)"