diff --git a/README.md b/README.md index 016697d..aaf76e4 100644 --- a/README.md +++ b/README.md @@ -81,8 +81,6 @@ Designed to mimic AqualinkRS devices, used to fully configure the master control * http://aqualink.ip/aquapda_sim.html <- (PDA simulator) # # ToDo (future release) -* Allow selecting of pre-defined VSP programs (Aqualink Touch & OneTouch protocols.) -* Add set time to OneTouch protocol. * Update AqualinkD Management console to manage configuration * Create iAqualink Touch Simulator * Probably decoded enough protocols for AuqlinkD to self configure. @@ -100,12 +98,6 @@ Designed to mimic AqualinkRS devices, used to fully configure the master control * Panel version * can't use iaquatouch panel / wireless -* Added iAqualinkTouch support for PDA only panels that can use that protocol. - * PDA panel needs to be Rev 6.0 or newer. - * This makes the PDA only panels quicker and less error prone. - * Introduces color light support and VSP - * Consider this PDA support Beta. - * Read PDA Wiki --> # Call for Help. @@ -115,21 +107,25 @@ Designed to mimic AqualinkRS devices, used to fully configure the master control # Updates in 2.5.0 (under development) @@ -146,6 +142,8 @@ NEED TO FIX FOR THIS RELEASE. * Allow VSP to be asigned to virtual button. * Fixed bug with timer not starting. * Increase Speed of detecting device state changes. +* Added iAqualink2 protocol support. +* Faster OneTouch device support # Updates in Release 2.4.0 * WARNING Breaking change if you use dimmer (please change button_??_lightMode from 6 to 10) diff --git a/release/aqualinkd-amd64 b/release/aqualinkd-amd64 index b69341a..fddb6d4 100755 Binary files a/release/aqualinkd-amd64 and b/release/aqualinkd-amd64 differ diff --git a/release/aqualinkd-arm64 b/release/aqualinkd-arm64 index 4658aa6..a140aa0 100755 Binary files a/release/aqualinkd-arm64 and b/release/aqualinkd-arm64 differ diff --git a/release/aqualinkd-armhf b/release/aqualinkd-armhf index 63d629c..4ce4cda 100755 Binary files a/release/aqualinkd-armhf and b/release/aqualinkd-armhf differ diff --git a/release/serial_logger-amd64 b/release/serial_logger-amd64 index b42bcf8..5ae1f43 100755 Binary files a/release/serial_logger-amd64 and b/release/serial_logger-amd64 differ diff --git a/release/serial_logger-arm64 b/release/serial_logger-arm64 index 70b76f6..d297ad9 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 48d77f6..8e4475f 100755 Binary files a/release/serial_logger-armhf and b/release/serial_logger-armhf differ diff --git a/source/aq_panel.c b/source/aq_panel.c index 5f8b1e0..46aa762 100644 --- a/source/aq_panel.c +++ b/source/aq_panel.c @@ -343,8 +343,10 @@ aqkey *addVirtualButton(struct aqualinkdata *aqdata, char *label, int vindex) { //aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX; //aqdata->aqbuttons[index].special_mask = 0; - aqled *led = malloc(sizeof(aqled)); - button->led = led; + //aqled *led = malloc(sizeof(aqled)); + //button->led = led; + + button->led = malloc(sizeof(aqled)); char *name = malloc(sizeof(char*) * 10); snprintf(name, 9, "%s%d", BTN_VAUX, vindex); @@ -362,6 +364,8 @@ aqkey *addVirtualButton(struct aqualinkdata *aqdata, char *label, int vindex) { button->rssd_code = IAQ_SPA_MODE; } else if (strncasecmp (button->label, "Clean Mode", 10) == 0) { button->rssd_code = IAQ_CLEAN_MODE; + } else if (strncasecmp (button->label, "Day Party", 9) == 0) { + button->rssd_code = IAQ_ONETOUCH_4; } else { button->rssd_code = NUL; } @@ -810,17 +814,27 @@ bool setDeviceState(struct aqualinkdata *aqdata, int deviceIndex, bool isON, req } } else if (isVBUTTON(button->special_mask)) { // Virtual buttons only supported with Aqualink Touch - LOG(PANL_LOG, LOG_NOTICE, "********** %s code=0x%02hhx iaq enabled=%s *****\n",button->name, button->rssd_code, isIAQT_ENABLED?"Yes":"No"); + LOG(PANL_LOG, LOG_INFO, "Set state for Vitrual Button %s code=0x%02hhx iAqualink2 enabled=%sn",button->name, button->rssd_code, isIAQT_ENABLED?"Yes":"No"); if (isIAQT_ENABLED) { // If it's one of the pre-defined onces & iaqualink is enabled, we can set it easile with button. - if ( isIAQL_ACTIVE && button->rssd_code != NUL) + + if ( isIAQL_ACTIVE && button->rssd_code && button->rssd_code != NUL) { + //LOG(PANL_LOG, LOG_NOTICE, "********** USE iaqualink2 ********\n"); set_iaqualink_aux_state(button, isON); } else { char msg[PTHREAD_ARG]; sprintf(msg, "%-5d%-5d", deviceIndex, (isON == false ? OFF : ON)); - aq_programmer(AQ_SET_IAQTOUCH_DEVICE_ON_OFF, msg, aqdata); - } + //if (button->rssd_code != VBUTTON_RSSD) { + //LOG(PANL_LOG, LOG_NOTICE, "********** USE AQ_SET_IAQTOUCH_DEVICE_ON_OFF ********\n"); + aq_programmer(AQ_SET_IAQTOUCH_DEVICE_ON_OFF, msg, aqdata); + //} else if (button->rssd_code != VBUTTON_ONETOUCH_RSSD) { + // LOG(PANL_LOG, LOG_NOTICE, "********** USE AQ_SET_IAQTOUCH_ONETOUCH_ON_OFF ********\n"); + // aq_programmer(AQ_SET_IAQTOUCH_ONETOUCH_ON_OFF, msg, aqdata); + //} else { + // LOG(PANL_LOG, LOG_ERR, "Configuration! do not understand code for Virtual Buttons"); + //} + } } else { LOG(PANL_LOG, LOG_ERR, "Can only use Aqualink Touch protocol for Virtual Buttons"); } @@ -965,6 +979,25 @@ void programDeviceLightMode(struct aqualinkdata *aqdata, int value, int button) _aqconfig_.light_programming_initial_off, _aqconfig_.light_programming_mode ); aq_programmer(AQ_SET_LIGHTPROGRAM_MODE, buf, aqdata); + } else if (isRSSA_ENABLED) { + // If we are using rs-serial then turn light on first. + if (light->button->led->state != ON) { + set_aqualink_rssadapter_aux_extended_state(light->button, RS_SA_ON); + } + if (light->lightType == LC_DIMMER) { + // Value 1 = 25, 2 = 50, 3 = 75, 4 = 100 (need to convert value into binary) + if (value >= 1 && value <= 4) { + unsigned char rssd_value = value * 25; + set_aqualink_rssadapter_aux_extended_state(light->button, rssd_value); + } else { + LOG(PANL_LOG,LOG_ERR, "Light mode %d is not valid for '%s'\n",value, light->button->label); + set_aqualink_rssadapter_aux_extended_state(light->button, 100); + } + } else { + // Dimmer or any color light can simply be set with value + set_aqualink_rssadapter_aux_extended_state(light->button, value); + } + /* } else if (isRSSA_ENABLED && light->lightType == LC_DIMMER2) { // Dimmer needs to be turned on before you set dimmer level if (light->button->led->state != ON) { @@ -984,6 +1017,9 @@ void programDeviceLightMode(struct aqualinkdata *aqdata, int value, int button) } else { LOG(PANL_LOG,LOG_ERR, "Light mode %d is not valid for '%s'\n",value, light->button->label); } + } else if (isRSSA_ENABLED && light->lightType != LC_PROGRAMABLE) { + // Any programmable COLOR light (that's programmed by panel) + set_aqualink_rssadapter_aux_extended_state(light->button, value);*/ } else { //sprintf(buf, "%-5s%-5d%-5d",value, button, light->lightType); sprintf(buf, "%-5d%-5d%-5d",value, button, light->lightType); diff --git a/source/aq_panel.h b/source/aq_panel.h index 09d2081..d7e9ff4 100644 --- a/source/aq_panel.h +++ b/source/aq_panel.h @@ -12,6 +12,9 @@ #define SPA_HEAT_INDEX 10 */ +//#define VBUTTON_ONETOUCH_RSSD 0xFF +//#define VBUTTON_RSSD 0xFE + // Defined as int16_t so 16 bits to mask #define RSP_4 (1 << 0) // 1 #define RSP_6 (1 << 1) // 16 diff --git a/source/aq_programmer.c b/source/aq_programmer.c index 019c534..63b67b8 100644 --- a/source/aq_programmer.c +++ b/source/aq_programmer.c @@ -94,6 +94,7 @@ const func_ptr _prog_functions[AQP_RSSADAPTER_MAX] = { [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_SET_IAQTOUCH_ONETOUCH_ON_OFF] = set_aqualink_iaqtouch_onetouch_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, @@ -867,6 +868,9 @@ const char *ptypeName(program_type type) case AQ_SET_IAQTOUCH_DEVICE_ON_OFF: return "Set AqualinkTouch Device On/Off"; break; + case AQ_SET_IAQTOUCH_ONETOUCH_ON_OFF: + return "Set AqualinkTouch OneTouch On/Off"; + break; case AQ_SET_IAQTOUCH_LIGHTCOLOR_MODE: return "Set AqualinkTouch Light Color (using panel)"; break; @@ -1006,6 +1010,7 @@ const char *programtypeDisplayName(program_type type) return "Get Pump Assignment"; break; case AQ_SET_IAQTOUCH_DEVICE_ON_OFF: + case AQ_SET_IAQTOUCH_ONETOUCH_ON_OFF: return "Programming: setting device on/off"; break; #ifdef AQ_PDA diff --git a/source/aq_programmer.h b/source/aq_programmer.h index 4e1d216..d718148 100644 --- a/source/aq_programmer.h +++ b/source/aq_programmer.h @@ -96,6 +96,7 @@ typedef enum { AQ_SET_IAQTOUCH_SWG_BOOST, AQ_SET_IAQTOUCH_SET_TIME, AQ_SET_IAQTOUCH_DEVICE_ON_OFF, + AQ_SET_IAQTOUCH_ONETOUCH_ON_OFF, AQ_SET_IAQTOUCH_POOL_HEATER_TEMP, AQ_SET_IAQTOUCH_SPA_HEATER_TEMP, AQ_SET_IAQLINK_POOL_HEATER_TEMP, // Same as above but using iAqualink not AqualinkTouch diff --git a/source/aqualinkd.c b/source/aqualinkd.c index 083473a..7d9f1fc 100644 --- a/source/aqualinkd.c +++ b/source/aqualinkd.c @@ -579,7 +579,7 @@ int startup(char *self, char *cfgFile) LOG(AQUA_LOG,LOG_NOTICE, "Config rssa_device_id = 0x%02hhx\n", _aqconfig_.rssa_device_id); #if defined AQ_ONETOUCH || defined AQ_IAQTOUCH LOG(AQUA_LOG,LOG_NOTICE, "Config extra_device_id = 0x%02hhx\n", _aqconfig_.extended_device_id); - if (_aqconfig_.enable_iaqualink) { + if (_aqconfig_.extended_device_id >= JANDY_DEV_AQLNK_MIN && _aqconfig_.extended_device_id <= JANDY_DEV_AQLNK_MAX) { LOG(AQUA_LOG,LOG_NOTICE, "Config enable_iaqualink = %s\n", bool2text(_aqconfig_.enable_iaqualink)); } LOG(AQUA_LOG,LOG_NOTICE, "Config extra_device_prog = %s\n", bool2text(_aqconfig_.extended_device_id_programming)); @@ -697,21 +697,18 @@ int startup(char *self, char *cfgFile) sprintf(ext,"Light Progm | CTYPE %-1d |",_aqualink_data.lights[j].lightType); } } - if (_aqualink_data.aqbuttons[i].dz_idx > 0) + if (isVBUTTON(_aqualink_data.aqbuttons[i].special_mask)) { + if (_aqualink_data.aqbuttons[i].rssd_code != NUL) { + sprintf(ext,"OneTouch %d |",_aqualink_data.aqbuttons[i].rssd_code - 15); + } + } + if (_aqualink_data.aqbuttons[i].dz_idx > 0) { sprintf(ext+strlen(ext), "dzidx %-3d", _aqualink_data.aqbuttons[i].dz_idx); -/* -#ifdef AQ_PDA - if (isPDA_PANEL) { - LOG(AQUA_LOG,LOG_NOTICE, "Config BTN %-13s = label %-15s | PDAlabel %-15s | %s\n", - _aqualink_data.aqbuttons[i].name, _aqualink_data.aqbuttons[i].label, - _aqualink_data.aqbuttons[i].pda_label, ext); - } else -#endif -*/ - { - LOG(AQUA_LOG,LOG_NOTICE, "Config BTN %-13s = label %-15s | %s\n", - _aqualink_data.aqbuttons[i].name, _aqualink_data.aqbuttons[i].label, ext); } + + LOG(AQUA_LOG,LOG_NOTICE, "Config BTN %-13s = label %-15s | %s\n", + _aqualink_data.aqbuttons[i].name, _aqualink_data.aqbuttons[i].label, ext); + if ( ((_aqualink_data.aqbuttons[i].special_mask & VIRTUAL_BUTTON) == VIRTUAL_BUTTON) && ((_aqualink_data.aqbuttons[i].special_mask & VS_PUMP ) != VS_PUMP) && @@ -1177,7 +1174,7 @@ void main_loop() //printf("rs_fd =% d\n",rs_fd); if (rs_fd < 0) { - // sleep(1); + sleep(1); sprintf(_aqualink_data.last_display_message, CONNECTION_ERROR); LOG(AQUA_LOG,LOG_ERR, "Aqualink daemon waiting to connect to master device...\n"); _aqualink_data.updated = true; diff --git a/source/config.c b/source/config.c index 028b04e..4c8afe0 100644 --- a/source/config.c +++ b/source/config.c @@ -40,6 +40,7 @@ #include "aq_serial.h" #include "aq_panel.h" #include "aqualink.h" +#include "iaqualink.h" #define MAXCFGLINE 256 @@ -49,6 +50,7 @@ char *generate_mqtt_id(char *buf, int len); pump_detail *getpump(struct aqualinkdata *aqdata, int button); bool populatePumpData(struct aqualinkdata *aqdata, char *pumpcfg ,aqkey *button, char *value); pump_detail *getPumpFromButtonID(struct aqualinkdata *aqdata, aqkey *button); +aqkey *getVirtualButton(struct aqualinkdata *aqdata, int num); struct tmpPanelInfo { int size; @@ -415,6 +417,10 @@ bool setConfigValue(struct aqualinkdata *aqdata, char *param, char *value) { rtn=true; } else if (strncasecmp(param, "extended_device_id", 18) == 0) { _aqconfig_.extended_device_id = strtoul(cleanalloc(value), NULL, 16); + // Enable enable_iaqualink by default and let people turn it off + //if (_aqconfig_.extended_device_id >= JANDY_DEV_AQLNK_MIN && _aqconfig_.extended_device_id <= JANDY_DEV_AQLNK_MAX) { + // _aqconfig_.enable_iaqualink = true; + //} rtn=true; } else if (strncasecmp(param, "enable_iaqualink", 16) == 0) { _aqconfig_.enable_iaqualink = text2bool(value); @@ -701,7 +707,11 @@ bool setConfigValue(struct aqualinkdata *aqdata, char *param, char *value) { aqdata->lights[aqdata->num_lights].lightType = type; aqdata->aqbuttons[num].special_mask |= PROGRAM_LIGHT; aqdata->aqbuttons[num].special_mask_ptr = &aqdata->lights[aqdata->num_lights]; - + if ( aqdata->lights[aqdata->num_lights].lightType == LC_DIMMER2 && _aqconfig_.rssa_device_id != 0x48 ) { + LOG(AQUA_LOG,LOG_ERR, "Config error, button '%s' has light mode '%d' set. This only supported when 'rssa_device_id' is enabled, changing to light mode '%d'\n", + aqdata->aqbuttons[num].label, LC_DIMMER2,LC_DIMMER); + aqdata->lights[aqdata->num_lights].lightType = LC_DIMMER; + } aqdata->num_lights++; } } else { @@ -731,26 +741,56 @@ bool setConfigValue(struct aqualinkdata *aqdata, char *param, char *value) { aqkey *button = addVirtualButton(aqdata, label, num); if (button != NULL) { button->special_mask |= VIRTUAL_BUTTON; + button->led->state = OFF; } else { LOG(AQUA_LOG,LOG_WARNING, "Error with '%s', total buttons=%d, config has %d already, ignoring!\n",param, TOTAL_BUTTONS, aqdata->total_buttons+1); } } else if (strncasecmp(param + 17, "_pump", 5) == 0) { - char *vbname = malloc(sizeof(char*) * 10); - snprintf(vbname, 9, "%s%d", BTN_VAUX, num); - aqkey *vbutton = NULL; - for (int i = aqdata->virtual_button_start; i < aqdata->total_buttons; i++) { - //printf("Checking %s agasinsdt %s\n",aqdata->aqbuttons[i].name, vbname); - if ( strcmp( aqdata->aqbuttons[i].name, vbname) == 0 ) { - vbutton = &aqdata->aqbuttons[i]; - vbutton->led->state = ON; //Virtual pump is always on - if ( ! populatePumpData(aqdata, param + 18, vbutton, value) ) - { - LOG(AQUA_LOG,LOG_ERR, "Config error, VSP Pumps limited to %d, ignoring : %s",MAX_PUMPS,param); - } - break; + aqkey *vbutton = getVirtualButton(aqdata, num); + if (vbutton != NULL) { + vbutton->led->state = ON; //Virtual pump default to on + if ( ! populatePumpData(aqdata, param + 18, vbutton, value) ) + { + LOG(AQUA_LOG,LOG_ERR, "Config error, VSP Pumps limited to %d, ignoring : %s",MAX_PUMPS,param); } + } else { + LOG(AQUA_LOG,LOG_ERR, "Config error, could not find vitrual button for `%s`",param); } - if (vbutton == NULL) { + } else if (strncasecmp(param + 17, "_onetouchID", 11) == 0) { + aqkey *vbutton = getVirtualButton(aqdata, num); + if (vbutton != NULL) { + switch (strtoul(value, NULL, 10)) { + case 1: + vbutton->code = IAQ_ONETOUCH_1; + vbutton->rssd_code = IAQ_ONETOUCH_1; + break; + case 2: + vbutton->code = IAQ_ONETOUCH_2; + vbutton->rssd_code = IAQ_ONETOUCH_2; + break; + case 3: + vbutton->code = IAQ_ONETOUCH_3; + vbutton->rssd_code = IAQ_ONETOUCH_3; + break; + case 4: + vbutton->code = IAQ_ONETOUCH_4; + vbutton->rssd_code = IAQ_ONETOUCH_4; + break; + case 5: + vbutton->code = IAQ_ONETOUCH_5; + vbutton->rssd_code = IAQ_ONETOUCH_5; + break; + case 6: + vbutton->code = IAQ_ONETOUCH_5; + vbutton->rssd_code = IAQ_ONETOUCH_5; + break; + default: + vbutton->code = NUL; + vbutton->rssd_code = NUL; + break; + } + + } else { LOG(AQUA_LOG,LOG_ERR, "Config error, could not find vitrual button for `%s`",param); } } @@ -760,6 +800,24 @@ bool setConfigValue(struct aqualinkdata *aqdata, char *param, char *value) { return rtn; } +aqkey *getVirtualButton(struct aqualinkdata *aqdata, int num) +{ + aqkey *vbutton = NULL; + char vbname[10]; + snprintf(vbname, 9, "%s%d", BTN_VAUX, num); + + for (int i = aqdata->virtual_button_start; i < aqdata->total_buttons; i++) + { + // printf("Checking %s agasinsdt %s\n",aqdata->aqbuttons[i].name, vbname); + if (strcmp(aqdata->aqbuttons[i].name, vbname) == 0) + { + vbutton = &aqdata->aqbuttons[i]; + break; + } + } + return vbutton; +} + // pumpcfg is pointer to pumpIndex, pumpName, pumpType pumpID, (ie pull off button_??_ or vurtual_button_??_) bool populatePumpData(struct aqualinkdata *aqdata, char *pumpcfg ,aqkey *button, char *value) { diff --git a/source/iaqtouch.c b/source/iaqtouch.c index fc814bd..eba37ab 100644 --- a/source/iaqtouch.c +++ b/source/iaqtouch.c @@ -477,23 +477,32 @@ void processPageButton(unsigned char *message, int length, struct aqualinkdata * void iaqt_pump_update(struct aqualinkdata *aq_data, int updated) { const int bitmask[MAX_PUMPS] = {1,2,4,8}; static unsigned char updates = '\0'; - int i; + //int i; if (updated == -1) { - for(i=0; i < MAX_PUMPS; i++) { + for(int i=0; i < MAX_PUMPS; i++) { if ((updates & bitmask[i]) != bitmask[i]) { aq_data->pumps[i].rpm = PUMP_OFF_RPM; aq_data->pumps[i].gpm = PUMP_OFF_GPM; aq_data->pumps[i].watts = PUMP_OFF_WAT; aq_data->pumps[i].pStatus = PS_OFF; + // If Virtual pump, turn it off as well. + if (i < aq_data->num_pumps && isVBUTTON(aq_data->pumps[i].button->special_mask) && aq_data->pumps[i].button->led->state == ON ) { + LOG(IAQT_LOG,LOG_DEBUG, "Turning off virtual button pump %d - \n",aq_data->pumps[i].pumpIndex,aq_data->pumps[i].button->label); + aq_data->pumps[i].button->led->state = OFF; + } LOG(IAQT_LOG,LOG_DEBUG, "Clearing pump %d\n",i); aq_data->updated =true; } } updates = '\0'; } else if (updated >=0 && updated < MAX_PUMPS) { - updates |= bitmask[updated]; - LOG(IAQT_LOG,LOG_DEBUG, "Got pump update message for pump %d\n",updated); + updates |= bitmask[updated]; + LOG(IAQT_LOG,LOG_DEBUG, "Got pump update message for pump %d\n",updated); + if (updated < aq_data->num_pumps && isVBUTTON(aq_data->pumps[updated].button->special_mask) && aq_data->pumps[updated].button->led->state == OFF) { + LOG(IAQT_LOG,LOG_DEBUG, "Turning on virtual button pump %d - %s\n",aq_data->pumps[updated].pumpIndex,aq_data->pumps[updated].button->label); + aq_data->pumps[updated].button->led->state = ON; + } } } @@ -874,13 +883,14 @@ void processPage(struct aqualinkdata *aq_data) } } -#define REQUEST_STATUS_POLL_COUNT 10 +#define REQUEST_STATUS_POLL_COUNT 10 +#define REQUEST_DEVICES_POLL_COUNT 30 // if _aqconfig_.enable_iaqualink=true then REQUEST_STATUS_POLL_COUNT will be used. bool process_iaqtouch_packet(unsigned char *packet, int length, struct aqualinkdata *aq_data) { static bool gotInit = false; static int cnt = 0; - static int probesSinceLastPageCMD=0; + //static int probesSinceLastPageCMD=0; static bool gotStatus = true; static char message[AQ_MSGLONGLEN + 1]; bool fake_pageend = false; @@ -1065,7 +1075,7 @@ bool process_iaqtouch_packet(unsigned char *packet, int length, struct aqualinkd // Standard ack/poll if (packet[3] == CMD_IAQ_POLL) { - probesSinceLastPageCMD++; + //probesSinceLastPageCMD++; #ifdef NEW_POLL_CYCLE /* @@ -1121,6 +1131,8 @@ if not programming && poll packet { break; } + iaqt_queue_cmd(nextPageRequestKey); + /* if (probesSinceLastPageCMD > 3) { // Seems to be a bug with wifi device ghosting command on/off, kind-a looks like our page commands don;t take sometimes so wait. // This didn;t fix issue, but see @@ -1129,6 +1141,7 @@ if not programming && poll packet { } else { LOG(IAQT_LOG, LOG_INFO, "Waiting to send next page cnt %d\n",probesSinceLastPageCMD); } + */ } } else if (in_programming_mode(aq_data) == true) { // Set count to something close to max, so we will pull latest info once programming has finished. diff --git a/source/iaqtouch_aq_programmer.c b/source/iaqtouch_aq_programmer.c index 0537c8e..ce27d6d 100644 --- a/source/iaqtouch_aq_programmer.c +++ b/source/iaqtouch_aq_programmer.c @@ -380,6 +380,17 @@ bool goto_iaqt_page(const unsigned char pageID, struct aqualinkdata *aq_data) { } LOG(IAQT_LOG, LOG_DEBUG, "IAQ Touch got to Device page\n"); return true; + } else if (pageID == IAQ_PAGE_ONETOUCH) { + send_aqt_cmd(KEY_IAQTCH_ONETOUCH); + if (iaqtCurrentPage() != IAQ_PAGE_ONETOUCH) { + unsigned char page = waitfor_iaqt_nextPage(aq_data); + if (page != IAQ_PAGE_ONETOUCH ) { + LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did not find OneTouch page\n"); + return false; + } + } + LOG(IAQT_LOG, LOG_DEBUG, "IAQ Touch got to OneTouch page\n"); + return true; } else if (pageID == IAQ_PAGE_MENU || pageID == IAQ_PAGE_SET_TEMP || pageID == IAQ_PAGE_SET_TIME || pageID == IAQ_PAGE_SET_SWG || pageID == IAQ_PAGE_SYSTEM_SETUP || pageID == IAQ_PAGE_FREEZE_PROTECT || pageID == IAQ_PAGE_LABEL_AUX || pageID == IAQ_PAGE_VSP_SETUP) { @@ -549,6 +560,39 @@ void *set_aqualink_iaqtouch_device_on_off( void *ptr ) return ptr; } + +void *set_aqualink_iaqtouch_onetouch_on_off( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + char *buf = (char*)threadCtrl->thread_args; + //char device_name[15]; + struct iaqt_page_button *button; + + unsigned int device = atoi(&buf[0]); + unsigned int state = atoi(&buf[5]); + + waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_IAQTOUCH_ONETOUCH_ON_OFF); + + LOG(IAQT_LOG,LOG_INFO, "OneTouch Device On/Off, device '%s', state %d\n",aq_data->aqbuttons[device].label,state); + + if ( goto_iaqt_page(IAQ_PAGE_ONETOUCH, aq_data) == false ) + goto f_end; + + // See if it's on the current page + button = iaqtFindButtonByLabel(aq_data->aqbuttons[device].label); + + + f_end: + goto_iaqt_page(IAQ_PAGE_HOME, aq_data); + cleanAndTerminateThread(threadCtrl); + + return ptr; +} + + + void *set_aqualink_iaqtouch_light_colormode( void *ptr ) { struct programmingThreadCtrl *threadCtrl; diff --git a/source/iaqtouch_aq_programmer.h b/source/iaqtouch_aq_programmer.h index 94d943f..a7dfe01 100644 --- a/source/iaqtouch_aq_programmer.h +++ b/source/iaqtouch_aq_programmer.h @@ -21,6 +21,7 @@ void *set_aqualink_iaqtouch_time( void *ptr ); void *set_aqualink_iaqtouch_pump_vs_program( void *ptr ); void *set_aqualink_iaqtouch_light_colormode( void *ptr ); void *set_aqualink_iaqtouch_device_on_off( void *ptr ); // For PDA only +void *set_aqualink_iaqtouch_onetouch_on_off( void *ptr ); int ref_iaqt_control_cmd(unsigned char **cmd); void rem_iaqt_control_cmd(unsigned char *cmd); diff --git a/source/iaqualink.c b/source/iaqualink.c index 5146f84..bcaf71a 100644 --- a/source/iaqualink.c +++ b/source/iaqualink.c @@ -180,42 +180,47 @@ unsigned char iAqalnkDevID(aqkey *button) { return 0xFF; } +void lastchecksum(unsigned char *packet, int length) +{ + if (getLogLevel(IAQL_LOG) >= LOG_DEBUG) + { + static unsigned char last70checksum = 0x00; + static unsigned char last71checksum = 0x00; + static unsigned char last72checksum = 0x00; -void lastchecksum(unsigned char *packet, int length){ - static unsigned char last70checksum = 0x00; - static unsigned char last71checksum = 0x00; - static unsigned char last72checksum = 0x00; - - switch (packet[PKT_CMD]){ + switch (packet[PKT_CMD]) + { case 0x70: - if (last70checksum != packet[length-3] && last70checksum != 0x00) { - LOG(IAQL_LOG, LOG_INFO,"*****************************************\n"); - LOG(IAQL_LOG, LOG_INFO,"******* CHECKSUM CHANGED for 0x70 *******\n"); - LOG(IAQL_LOG, LOG_INFO,"*****************************************\n"); + if (last70checksum != packet[length - 3] && last70checksum != 0x00) + { + LOG(IAQL_LOG, LOG_INFO, "*****************************************\n"); + LOG(IAQL_LOG, LOG_INFO, "******* CHECKSUM CHANGED for 0x70 *******\n"); + LOG(IAQL_LOG, LOG_INFO, "*****************************************\n"); } - last70checksum = packet[length-3]; - break; + last70checksum = packet[length - 3]; + break; case 0x71: - if (last71checksum != packet[length-3] && last71checksum != 0x00) { - LOG(IAQL_LOG, LOG_INFO,"*****************************************\n"); - LOG(IAQL_LOG, LOG_INFO,"******* CHECKSUM CHANGED for 0x71 *******\n"); - LOG(IAQL_LOG, LOG_INFO,"*****************************************\n"); + if (last71checksum != packet[length - 3] && last71checksum != 0x00) + { + LOG(IAQL_LOG, LOG_INFO, "*****************************************\n"); + LOG(IAQL_LOG, LOG_INFO, "******* CHECKSUM CHANGED for 0x71 *******\n"); + LOG(IAQL_LOG, LOG_INFO, "*****************************************\n"); } - last71checksum = packet[length-3]; - break; + last71checksum = packet[length - 3]; + break; case 0x72: - if (last72checksum != packet[length-3] && last72checksum != 0x00) { - LOG(IAQL_LOG, LOG_INFO,"*****************************************\n"); - LOG(IAQL_LOG, LOG_INFO,"******* CHECKSUM CHANGED for 0x72 *******\n"); - LOG(IAQL_LOG, LOG_INFO,"*****************************************\n"); + if (last72checksum != packet[length - 3] && last72checksum != 0x00) + { + LOG(IAQL_LOG, LOG_INFO, "*****************************************\n"); + LOG(IAQL_LOG, LOG_INFO, "******* CHECKSUM CHANGED for 0x72 *******\n"); + LOG(IAQL_LOG, LOG_INFO, "*****************************************\n"); } - last72checksum = packet[length-3]; - break; - } + last72checksum = packet[length - 3]; + break; + } + } } - - /* All taken from panel Yg, but only heater setpoints seem to work. @@ -471,7 +476,7 @@ bool process_iaqualink_packet(unsigned char *packet, int length, struct aqualink static int cnt = 0; //static unsigned char ID = 0; //static cur_swg = 0; - static unsigned char sendid = 0x00; + //static unsigned char sendid = 0x00; if (packet[PKT_CMD] == 0x53) { diff --git a/source/iaqualink.h b/source/iaqualink.h index 4cda702..7b702d5 100644 --- a/source/iaqualink.h +++ b/source/iaqualink.h @@ -22,9 +22,19 @@ void set_iaqualink_heater_setpoint(int value, bool isPool); #define IAQ_SPA 0x02 #define IAQ_SPA_HTR 0x03 //....... some missing .... -#define IAQ_ALL_OFF 0x10 // Not sure on this -#define IAQ_SPA_MODE 0x11 -#define IAQ_CLEAN_MODE 0x12 +//#define IAQ_ALL_OFF 0x10 +//#define IAQ_SPA_MODE 0x11 +//#define IAQ_CLEAN_MODE 0x12 +#define IAQ_ONETOUCH_1 0x10 +#define IAQ_ONETOUCH_2 0x11 +#define IAQ_ONETOUCH_3 0x12 +#define IAQ_ONETOUCH_4 0x13 // Im gessing +#define IAQ_ONETOUCH_5 0x14 // Im gessing +#define IAQ_ONETOUCH_6 0x15 // Im gessing + +#define IAQ_ALL_OFF IAQ_ONETOUCH_1 // Using default name +#define IAQ_SPA_MODE IAQ_ONETOUCH_2 // Using default name +#define IAQ_CLEAN_MODE IAQ_ONETOUCH_3 // Using default name #define IAD_SWG 0x19 //....... some missing .... diff --git a/source/json_messages.c b/source/json_messages.c index a334c31..3b59b76 100644 --- a/source/json_messages.c +++ b/source/json_messages.c @@ -727,7 +727,7 @@ printf("Pump Type %d\n",aqdata->pumps[i].pumpType); length--; length += sprintf(buffer+length, "}"); - length += sprintf(buffer+length, ",\"light_program\":{" ); + length += sprintf(buffer+length, ",\"light_program_names\":{" ); for (i=0; i < aqdata->num_lights; i++) { if (aqdata->lights[i].lightType == LC_DIMMER2) { diff --git a/source/serial_logger.c b/source/serial_logger.c index e306b89..bb63968 100644 --- a/source/serial_logger.c +++ b/source/serial_logger.c @@ -41,7 +41,7 @@ #define SLOG_MAX 80 #define PACKET_MAX 800 -#define VERSION "serial_logger V2.6" +#define VERSION "serial_logger V2.7" /* typedef enum used { @@ -668,6 +668,7 @@ int _serial_logger(int rs_fd, char *port_name, int logPackets, int logLevel, boo bool found_lx =false; bool found_chem =false; bool found_pent_vsp =false; + bool found_iAqualnk =false; clock_gettime(CLOCK_REALTIME, &start_time); if (timePackets) { @@ -859,6 +860,8 @@ int _serial_logger(int rs_fd, char *port_name, int logPackets, int logLevel, boo found_lx =true; } else if (slog[i].ID >= JANDY_DEC_CHEM_MIN && slog[i].ID <= JANDY_DEC_CHEM_MAX) { found_chem =true; + } else if (slog[i].ID >= JANDY_DEV_AQLNK_MIN && slog[i].ID <= JANDY_DEV_AQLNK_MAX) { + found_iAqualnk =true; } } } @@ -936,6 +939,8 @@ int _serial_logger(int rs_fd, char *port_name, int logPackets, int logLevel, boo LOG(SLOG_LOG, LOG_NOTICE, "read_RS485_LX = yes\n"); if (found_chem) LOG(SLOG_LOG, LOG_NOTICE, "read_RS485_Chem = yes\n"); + if (found_iAqualnk && _panelPDA) + LOG(SLOG_LOG, LOG_NOTICE, "read_RS485_iAqualink = yes\n"); LOG(SLOG_LOG, LOG_NOTICE, "-------------------------\n"); diff --git a/source/serialadapter.c b/source/serialadapter.c index bee8f0b..4650284 100644 --- a/source/serialadapter.c +++ b/source/serialadapter.c @@ -139,7 +139,7 @@ void set_aqualink_rssadapter_aux_extended_state(const aqkey *button, const unsig LOG(RSSA_LOG,LOG_DEBUG, "Sending 0x%02hhx to %s\n",state, button->label); rssadapter_device_state(button->rssd_code, state); // Set state - rssadapter_device_state(button->rssd_code, 0x00); // 0x00 meand Get curent state (for color lights the return state is 0) + rssadapter_device_state(button->rssd_code, 0x00); // 0x00 means Get curent state (for color lights the return state is 0) } void set_aqualink_rssadapter_aux_state(const aqkey *button, bool turnOn) diff --git a/source/utils.c b/source/utils.c index 522465c..0d79127 100644 --- a/source/utils.c +++ b/source/utils.c @@ -674,7 +674,7 @@ void _LOG(logmask_t from, int msg_level, char *message, int message_buffer_size) //snprintf(_loq_display_message, AQ_MSGLONGLEN-2, "%s\n",message); int len = rsm_strncpy(_loq_display_message, (unsigned char*)message, AQ_MSGLONGLEN-1, message_buffer_size); _loq_display_message[len] = '\0'; - printf ("*** Adding ERROR to buffer '%s' **** \n",_loq_display_message); + //printf ("*** Adding ERROR to buffer '%s' **** \n",_loq_display_message); } #ifndef AQ_MANAGER diff --git a/source/version.h b/source/version.h index 8a7da6c..9a2b6d2 100644 --- a/source/version.h +++ b/source/version.h @@ -4,4 +4,4 @@ #define AQUALINKD_SHORT_NAME "AqualinkD" // Use Magor . Minor . Patch -#define AQUALINKD_VERSION "2.5.0 (Dev 0.1)" +#define AQUALINKD_VERSION "2.5.0 (Dev 0.2)" diff --git a/web/controller.html b/web/controller.html index 6532a4c..740e4b5 100644 --- a/web/controller.html +++ b/web/controller.html @@ -649,7 +649,7 @@ resetBackgroundSize(); } - function populateLightProgram(type=0) { + function populateLightProgram(type=0, current_mode="") { var light_program = _light_program[type]; if (_lightProgramDropdown) { @@ -676,10 +676,14 @@ var fLen = light_program.length; var i; for (i = 0; i < fLen; i++) { + selected = ""; + if (current_mode != "" && light_program[i].startsWith(current_mode)) { + selected = "checked"; + } if (light_program[i].endsWith(" - Show")) - html2 = html2 + "
"; + html2 = html2 + ""; else - html1 = html1 + ""; + html1 = html1 + ""; } var row; row = tbody.deleteRow(2); @@ -1047,9 +1051,27 @@ } else { //console.log("Tile "+id+" status is '"+document.getElementById(id).getAttribute('status')+"' not setting text to '"+text+"'"); } - } catch(exception) {} + } catch(exception) { console.log(exception); } return false; } + function getTileOnText(id) { + try { + var tile = document.getElementById(id); + if (tile.getAttribute('status') == 'on' || tile.getAttribute('status') == 'enabled') { + return document.getElementById(id + '_status').innerHTML; + } else { + //console.log("Tile "+id+" status is '"+document.getElementById(id).getAttribute('status')+"' not setting text to '"+text+"'"); + } + } catch(exception) {} + + return ""; + } + function getTileSPvalue(id) { + try { + return parseInt(document.getElementById(id).getAttribute('spvalue')); + } catch(exception) {} + return -1; + } function setThermostatTile(id, value, sp_value) { setTileValue(id, value); @@ -1234,6 +1256,14 @@ if (typeof object.type_ext !== 'undefined' && (object.type_ext == 'switch_program' || object.type_ext == 'light_dimmer')) { if (typeof object.Light_Type !== 'undefined') { document.getElementById(object.id).setAttribute('lighttype', object.Light_Type); + + if (typeof object.Light_Program !== 'undefined') { + light_mode = parseInt(object.Light_Program); + if ( object.Light_Type != "10" && object.type_ext != 'light_dimmer' ) { + document.getElementById(object.id).setAttribute('spvalue', light_mode-1); + //console.log("Light_Program spvalue = "+document.getElementById(object.id).getAttribute('spvalue')); + } + } } // Other switch_program types (other than light_type) GO HERE } else if (typeof object.type_ext !== 'undefined' && object.type_ext == 'switch_timer') { @@ -1425,7 +1455,9 @@ close_button = document.getElementById("swg_options_close"); ext = '°' + _temperature_units; } else if (type == 'switch_program') { - populateLightProgram(tile.getAttribute('lighttype') ); + //var current_mode = ""; + //try { current_mode = document.getElementById(id + '_status').innerHTML } catch(exception) {} + populateLightProgram(tile.getAttribute('lighttype'),getTileOnText(id)); title = document.getElementById("pswitch_option_title"); close_button = document.getElementById("pswitch_options_close"); tm_slider = document.getElementById("ps_timer_slider_range"); @@ -1676,7 +1708,10 @@ var x; for (x = 0; x < radio.length; x++) { if (radio[x].checked == true) { - send_light_mode(radio[x].value, id, document.getElementById('pswitch_option_switch_text_value').innerHTML); + console.log("Light Current= "+(getTileSPvalue(id)+1)+" - "+getTileOnText(id)+" Selected="+radio[x].value+" - "+document.getElementById('pswitch_option_switch_text_value').innerHTML); + if (getTileSPvalue(id)+1 != radio[x].value) { + send_light_mode(radio[x].value, id, document.getElementById('pswitch_option_switch_text_value').innerHTML); + } mode=true; break; } @@ -1688,10 +1723,11 @@ } } else if (type == 'light_dimmer') { var value = slider.value; - if (state == (tile.getAttribute('status') == 'off')) + if (state == (tile.getAttribute('status') == 'off')) { setTileState(id, state); - if (sp_value != slider.value && dimmer_slider_changed == true) - setThermostatSetpoint(id, slider.value) + } else if (sp_value != slider.value && dimmer_slider_changed == true) { + setThermostatSetpoint(id, slider.value); + } } else if (type == 'setpoint_swg') { //console.log ("Boost attribute = "+tile.getAttribute('boost')); //console.log ("state = "+state); @@ -2224,32 +2260,44 @@ } for (var obj in data.timers) { setTileOnText(obj.toString(),"On (timer)"); + //setTileOnTextLine2(obj.toString(),"On (timer)"); //console.log("TIMER "+obj.toString()); } for (var obj in data.timer_durations) { setTileOnText(obj.toString(),"Timer "+toHoursAndMinutes(data.timer_durations[obj])); + //setTileOnTextLine2(obj.toString(),"Timer "+toHoursAndMinutes(data.timer_durations[obj])); //console.log("TIMER "+obj.toString()+" duration "+data.timer_durations[obj]); } - for (var obj in data.light_program) { - if (data.light_program[obj] != "") { - var light_mode = data.light_program[obj]; + for (var obj in data.light_program_names) { + if (data.light_program_names[obj] != "") { + var light_mode_name = data.light_program_names[obj]; + var light_mode = -1; // If aqualinkd programmed light, need to convert int to text index, if not value is text try { var light_type = document.getElementById(obj.toString()).getAttribute('lighttype'); - if (light_type == 0) { - var light_mode = _light_program[light_type][parseInt(data.light_program[obj])-1]; - if (light_mode.endsWith(" - Show")) - light_mode = light_mode.slice(0, -7); - //console.log("light program -- "+light_mode); - } else if (light_type == 10 || light_type == 11) { + if (light_type == "0") { + light_mode = parseInt(data.light_program_names[obj])-1; + var light_mode_name = _light_program[light_type][light_mode]; + if (light_mode_name.endsWith(" - Show")) { + light_mode_name = light_mode_name.slice(0, -7); + } + } else if (/*light_type == 10 ||*/ light_type == 11) { // Fo Dimmer Lights. - document.getElementById(obj.toString()).setAttribute('setpoint', parseInt(light_mode)); + light_mode = parseInt(light_mode_name); + document.getElementById(obj.toString()).setAttribute('setpoint', light_mode); + } else { + light_mode = _light_program[light_type].findIndex(element => element.startsWith(light_mode_name)) } - } catch (e) {} + } catch (e) { /*console.log(e);*/ } - setTileOnText(obj.toString(),light_mode); - // Below is only needed for full dimmer + setTileOnText(obj.toString(),light_mode_name); + + try { + document.getElementById(obj.toString()).setAttribute('spvalue', light_mode); + //console.log("Light mode index "+light_mode+" - "+light_mode_name+" ----- "+_light_program[light_type][light_mode]); + } catch (e) { /*console.log(e);*/ } + } } @@ -2507,7 +2555,7 @@ } function send_light_mode(value, id, textvalue) { - console.log("Set light mode to "+value+" id="+id+" text="+textvalue); + //console.log("Set light mode to "+value+" id="+id+" text="+textvalue); var mode = {}; //mode.parameter = 'POOL_LIGHT_MODE'; //mode.parameter = 'LIGHT_MODE';