diff --git a/Makefile b/Makefile index 55fe38e..c56513a 100755 --- a/Makefile +++ b/Makefile @@ -11,9 +11,10 @@ LIBS := -l pthread -l m #LIBS := -lpthread -lwebsockets # debug of not -#DBG = -g -O0 -fsanitize=address -static-libasan -#DBG = -g -DBG = +#DBG = -g -O0 -fsanitize=address +DBG = -g +#DBG = -D ONETOUCH +#DBG = # USe below to remove unused functions and global variables. #LFLAGS = -Wl,--gc-sections,--print-gc-sections @@ -32,7 +33,7 @@ CFLAGS = $(GCCFLAGS) $(DBG) $(LIBS) -D MG_DISABLE_MD5 -D MG_DISABLE_HTTP_DIGEST_ # Add inputs and outputs from these tool invocations to the build variables # define the C source files -SRCS = aqualinkd.c utils.c config.c aq_serial.c init_buttons.c aq_programmer.c net_services.c json_messages.c pda.c pda_menu.c pda_aq_programmer.c aquapure.c packetLogger.c pentair_messages.c mongoose.c +SRCS = aqualinkd.c utils.c config.c aq_serial.c init_buttons.c aq_programmer.c net_services.c json_messages.c pda.c pda_menu.c pda_aq_programmer.c aquapure.c onetouch.c onetouch_aq_programmer.c packetLogger.c pentair_messages.c mongoose.c DBG_SRC = timespec_subtract.c # If run with `make DEBUG=true` add debug files and pass parameter for compile diff --git a/README.md b/README.md index f7608c5..210f130 100644 --- a/README.md +++ b/README.md @@ -65,11 +65,17 @@ Designed to mimic AqualinkRS6 All Button keypad and (like the keypad) is used to * http://aqualink.ip/ <- (Standard WEB UI * http://aqualink.ip/simple.html <- (Simple opion if you don't like the above) * http://aqualink.ip/simulator.html <- (RS8 All Button Control Panel simulator) +* http://aqualink.ip/debug.html <- (Turn on/off debug/serial debug & download logs) # -# Update in Release 1.3.9b -* Small fixes for Jandy protocol. -* Fixed some debug messages. -# Update in Release 1.3.9 +# Update in Release 2.0.0a +* Big update, lots of core changes, please read wiki section https://github.com/sfeakes/AqualinkD/wiki/Version_2 +* Full Variable Speed Pump support. +* Please consider this ALPHA version, no need to upgrade unless you need VSP support. Use a previous version unless you want VSP. +* I've noticed there is an issues with colored lights if your light is configured as such in the control panel. For this release suggest you either :- + * A) use AqualinkD to control the colors and configure Jandy control panel as normal light + * B) Don't use AqualinkD to turn light on/off (or if you do, wait ~10 seconds) +# Update in Release 1.3.9a +* Improved Debugging for serial. * Added panel Timeout mode support to UI and MQTT * Fixed SWG bug while in Service & Timeout modes * Cleanded up SWG code and MQTT status messages for SWG and SWG/enabled diff --git a/a.out b/a.out new file mode 100755 index 0000000..892e3fe Binary files /dev/null and b/a.out differ diff --git a/aq_mqtt.h b/aq_mqtt.h index b91f4b9..78d1df4 100644 --- a/aq_mqtt.h +++ b/aq_mqtt.h @@ -33,7 +33,7 @@ //#define PUMP_TOPIC "Pump_" #define PUMP_RPM_TOPIC "/RPM" -#define PUMP_GPH_TOPIC "/GPH" +#define PUMP_GPM_TOPIC "/GPM" #define PUMP_WATTS_TOPIC "/Watts" /* #define AIR_TEMPERATURE "Air" diff --git a/aq_programmer.c b/aq_programmer.c index b75f21f..e848257 100644 --- a/aq_programmer.c +++ b/aq_programmer.c @@ -30,6 +30,7 @@ #include "pda_menu.h" #include "init_buttons.h" #include "pda_aq_programmer.h" +#include "onetouch_aq_programmer.h" #ifdef AQ_DEBUG #include @@ -56,6 +57,9 @@ void *set_aqualink_light_colormode( void *ptr ); void *set_aqualink_PDA_init( void *ptr ); void *set_aqualink_SWG( void *ptr ); void *set_aqualink_boost( void *ptr ); +void *set_aqualink_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 ); @@ -67,12 +71,14 @@ bool waitForEitherMessage(struct aqualinkdata *aq_data, char* message1, char* me bool push_aq_cmd(unsigned char cmd); void waitfor_queue2empty(); +void longwaitfor_queue2empty(); #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; @@ -81,6 +87,23 @@ 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) { //logMessage(LOG_DEBUG, "push_aq_cmd '0x%02hhx'\n", cmd); @@ -102,17 +125,47 @@ int get_aq_cmd_length() } unsigned char pop_aq_cmd(struct aqualinkdata *aq_data) +{ + unsigned char cmd = NUL; + // Only send commands on status messages + // Are we in programming mode and it's not ONETOUCH programming mode + if (aq_data->active_thread.thread_id != 0 && in_ot_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; + logMessage(LOG_DEBUG_SERIAL, "RS SEND cmd '0x%02hhx' (programming)\n", cmd); + } else if (_pgm_command != NUL) { + logMessage(LOG_DEBUG_SERIAL, "RS Waiting to send cmd '0x%02hhx' (programming)\n", _pgm_command); + } else { + logMessage(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--; + logMessage(LOG_DEBUG_SERIAL, "RS SEND cmd '0x%02hhx'\n", cmd); + memmove(&_commands[0], &_commands[1], sizeof(unsigned char) * _stack_place ) ; + } else { + logMessage(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); + + return cmd; +} + + +/* + +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) || + 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) )) {*/ - if ( (_pgm_command != NUL && (aq_data->last_packet_type == CMD_STATUS)) || - // Boost pool has to send commands to msg long - (aq_data->active_thread.ptype == AQ_SET_BOOST && (aq_data->last_packet_type == CMD_STATUS || aq_data->last_packet_type == CMD_MSG_LONG)) ) { + ( _pgm_command != NUL && (aq_data->last_packet_type == CMD_STATUS || aq_data->last_packet_type == CMD_MSG_LONG) )) { cmd = _pgm_command; _pgm_command = NUL; logMessage(LOG_DEBUG, "RS SEND cmd '0x%02hhx' (programming)\n", cmd); @@ -134,95 +187,39 @@ unsigned char pop_aq_cmd(struct aqualinkdata *aq_data) return cmd; } - - -//unsigned char pop_aq_cmd_old(struct aqualinkdata *aq_data); - -unsigned char pop_aq_cmd_XXXXXX(struct aqualinkdata *aq_data) -{ - unsigned char cmd = NUL; - static bool last_sent_was_cmd = false; - - // USE BELOW IF PDA HAS ISSUES WITH NEW COMMAND LOGIC - - //if ( pda_mode() == true ) { - // return pop_aq_cmd_old(aq_data); - //} - - // Only press menu to a status command - // Only send commands on status messages when programming date - // Otherwise send every other command. - - // Are we in programming mode - if (aq_data->active_thread.thread_id != 0) { - if ( ((_pgm_command == KEY_MENU || aq_data->active_thread.ptype == AQ_SET_TIME) && aq_data->last_packet_type == CMD_STATUS) || - (pda_mode() == false && aq_data->active_thread.ptype != AQ_SET_TIME && last_sent_was_cmd == false) || - (pda_mode() == true && aq_data->last_packet_type == CMD_STATUS) - //(pda_mode() == true && last_sent_was_cmd == false) - ) { - cmd = _pgm_command; - _pgm_command = NUL; - logMessage(LOG_DEBUG, "RS SEND cmd '0x%02hhx' (programming)\n", cmd); - /*} else if (aq_data->active_thread.ptype != AQ_SET_TIME && last_sent_was_cmd == false) { - cmd = _pgm_command; - _pgm_command = NUL; - logMessage(LOG_DEBUG, "RS SEND cmd '0x%02hhx' (programming)\n", cmd);*/ - } else if (_pgm_command != NUL) { - logMessage(LOG_DEBUG, "RS Waiting to send cmd '0x%02hhx' (programming)\n", _pgm_command); - } else { - logMessage(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--; - logMessage(LOG_DEBUG, "RS SEND cmd '0x%02hhx'\n", cmd); - memmove(&_commands[0], &_commands[1], sizeof(unsigned char) * _stack_place ) ; - } else { - logMessage(LOG_DEBUG, "RS SEND cmd '0x%02hhx'\n", cmd); - } - - if (cmd == NUL) - last_sent_was_cmd= false; - else - last_sent_was_cmd= true; - - return cmd; -} - -unsigned char pop_aq_cmd_old(struct aqualinkdata *aq_data) -{ - unsigned char cmd = NUL; - //logMessage(LOG_DEBUG, "pop_aq_cmd\n"); - // can only send a command every other ack. - - if (_last_sent_was_cmd == true) { - _last_sent_was_cmd= false; - } - else if (aq_data->active_thread.thread_id != 0) { - cmd = _pgm_command; - _pgm_command = NUL; - //logMessage(LOG_DEBUG, "pop_aq_cmd '0x%02hhx' (programming)\n", cmd); - } - else if (_stack_place > 0) { - cmd = _commands[0]; - _stack_place--; - //logMessage(LOG_DEBUG, "pop_aq_cmd '0x%02hhx'\n", cmd); - //memcpy(&_commands[0], &_commands[1], (sizeof(unsigned char) * MAX_STACK) - 1); - memmove(&_commands[0], &_commands[1], sizeof(unsigned char) * _stack_place ) ; - } - - if (cmd == NUL) - _last_sent_was_cmd= false; - else - _last_sent_was_cmd= true; - - return cmd; -} +*/ int roundTo(int num, int denominator) { return ((num + (denominator/2) ) / denominator )* denominator; } +//(Intelliflo VF you set GPM, not RPM) +int RPM_check(pump_type type, int value, struct aqualinkdata *aqdata) +{ + int rtn = value; + // RPM 3450 seems to be max + // RPM 600 min + // GPM 130 max + // GPM 15 min + if (type == VFPUMP) { + if (rtn > 130) + rtn = 130; + else if (rtn < 15) + rtn = 15; + else + rtn = roundTo(rtn, 5); + } else { + if (rtn > 3450) + rtn = 3450; + else if (rtn < 600) + rtn = 600; + else + rtn = roundTo(rtn, 5); + } + + return rtn; +} + int setpoint_check(int type, int value, struct aqualinkdata *aqdata) { int rtn = value; @@ -303,6 +300,28 @@ int setpoint_check(int type, int value, struct aqualinkdata *aqdata) return rtn; } +void queueGetExtendedProgramData(emulation_type source_type, struct aqualinkdata *aq_data, bool labels) +{ + // Wait for onetouch if enabeled. + if ( source_type == ALLBUTTON && ( onetouch_enabled() == false || extended_device_id_programming() == false ) ) { + aq_send_cmd(NUL); + aq_programmer(AQ_GET_POOL_SPA_HEATER_TEMPS, NULL, aq_data); + aq_programmer(AQ_GET_FREEZE_PROTECT_TEMP, NULL, aq_data); + if (labels) + aq_programmer(AQ_GET_AUX_LABELS, NULL, aq_data); + } else if ( source_type == ONETOUCH) { + aq_programmer(AQ_GET_ONETOUCH_SETPOINTS, NULL, aq_data); + } else if ( source_type == AQUAPDA) { + aq_programmer(AQ_PDA_INIT, NULL, aq_data); + } +} + +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) { @@ -310,11 +329,78 @@ void kick_aq_program_thread(struct aqualinkdata *aq_data) pthread_cond_broadcast(&aq_data->active_thread.thread_cond); } } +*/ +bool in_ot_programming_mode(struct aqualinkdata *aq_data) +{ + //( type != AQ_SET_PUMP_RPM || type != AQ_SET_OT_MACRO )) { -void aq_programmer(program_type type, char *args, struct aqualinkdata *aq_data) + if ( ( aq_data->active_thread.thread_id != 0 ) && + ( aq_data->active_thread.ptype == AQ_SET_ONETOUCH_PUMP_RPM || + aq_data->active_thread.ptype == AQ_SET_ONETOUCH_MACRO || + aq_data->active_thread.ptype == AQ_GET_ONETOUCH_SETPOINTS || + aq_data->active_thread.ptype == AQ_SET_ONETOUCH_TIME || + aq_data->active_thread.ptype == AQ_SET_ONETOUCH_SWG_PERCENT || + aq_data->active_thread.ptype == AQ_SET_ONETOUCH_BOOST || + aq_data->active_thread.ptype == AQ_SET_ONETOUCH_POOL_HEATER_TEMP || + aq_data->active_thread.ptype == AQ_SET_ONETOUCH_SPA_HEATER_TEMP || + aq_data->active_thread.ptype == AQ_SET_ONETOUCH_FREEZEPROTECT) + ) { + return true; + } + + return false; +} + +void kick_aq_program_thread(struct aqualinkdata *aq_data, emulation_type source_type) +{ + if ( aq_data->active_thread.thread_id != 0 ) { + if ( (source_type == ONETOUCH) && in_ot_programming_mode(aq_data)) + { + logMessage(LOG_DEBUG, "Kicking OneTouch thread %d,%p\n",aq_data->active_thread.ptype, aq_data->active_thread.thread_id); + pthread_cond_broadcast(&aq_data->active_thread.thread_cond); + } + else if (source_type == AQUAPDA && !in_ot_programming_mode(aq_data)) { + logMessage(LOG_DEBUG, "Kicking PDA thread %d,%p\n",aq_data->active_thread.ptype, aq_data->active_thread.thread_id); + pthread_cond_broadcast(&aq_data->active_thread.thread_cond); + } + else if (source_type == ALLBUTTON && !in_ot_programming_mode(aq_data)) { + logMessage(LOG_DEBUG, "Kicking RS 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); + } + + } +} + +void aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_data) { struct programmingThreadCtrl *programmingthread = malloc(sizeof(struct programmingThreadCtrl)); - + + program_type type = r_type; + + // reset any types if to onetouch if available and if one touch is quicker + // At moment. onetouch is quicker for boost, and slower for heaters + if (onetouch_enabled() && extended_device_id_programming()) { + switch (r_type){ + case AQ_GET_POOL_SPA_HEATER_TEMPS: + case AQ_GET_FREEZE_PROTECT_TEMP: + type = AQ_GET_ONETOUCH_SETPOINTS; + break; + case AQ_SET_POOL_HEATER_TEMP: + type = AQ_SET_ONETOUCH_POOL_HEATER_TEMP; + break; + case AQ_SET_SPA_HEATER_TEMP: + type = AQ_SET_ONETOUCH_SPA_HEATER_TEMP; + break; + case AQ_SET_BOOST: + type = AQ_SET_ONETOUCH_BOOST; + break; + default: + type = r_type; + break; + } + } + + // Check we are doing something valid request if (pda_mode() == true) { pda_reset_sleep(); if (type != AQ_PDA_INIT && @@ -332,7 +418,11 @@ void aq_programmer(program_type type, char *args, struct aqualinkdata *aq_data) type != AQ_SET_BOOST) { logMessage(LOG_ERR, "Selected Programming mode '%d' not supported with PDA mode control panel\n",type); return; - } + } /*else if (onetouch_enabled() == false && + ( type != AQ_SET_ONETOUCH_PUMP_RPM || type != AQ_SET_ONETOUCH_MACRO || type != AQ_GET_ONETOUCH_SETPOINTS)) { + logMessage(LOG_ERR, "Selected Programming mode '%d' not supported without OneTouch mode (extra_device_id) enabled\n",type); + return; + }*/ } programmingthread->aq_data = aq_data; @@ -460,6 +550,54 @@ void aq_programmer(program_type type, char *args, struct aqualinkdata *aq_data) return; } break; + case AQ_SET_ONETOUCH_PUMP_RPM: + if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_pump_rpm, (void*)programmingthread) < 0) { + logMessage (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) { + logMessage (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) { + logMessage (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) { + logMessage (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) { + logMessage (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) { + logMessage (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) { + logMessage (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) { + logMessage (LOG_ERR, "could not create thread\n"); + return; + } + break; default: logMessage (LOG_ERR, "Don't understand thread type\n"); break; @@ -707,19 +845,17 @@ STOP BOOST POOL if (val==true) { waitForMessage(threadCtrl->aq_data, "TO START BOOST POOL", 5); send_cmd(KEY_ENTER); - waitfor_queue2empty(); + longwaitfor_queue2empty(); } else { int wait_messages = 5; int i=0; - //waitfor_queue2empty(); - //waitForMessage(aq_data, NULL, 1); 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); + //_pgm_command = KEY_ENTER; + send_cmd(KEY_ENTER); logMessage(LOG_DEBUG, "**** FOUND STOP BOOST POOL ****\n"); //waitfor_queue2empty(); break; @@ -727,38 +863,29 @@ STOP BOOST POOL logMessage(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; + //_pgm_command = KEY_ENTER; + send_cmd(KEY_ENTER); logMessage(LOG_DEBUG, "**** FOUND STOP BOOST POOL ****\n"); break; } send_cmd(KEY_RIGHT); //printf("WAIT\n"); - waitfor_queue2empty(); + longwaitfor_queue2empty(); //printf("FINISHED WAIT\n"); } //waitfor_queue2empty(); //waitForMessage(aq_data, NULL, 1); } - - waitForMessage(aq_data, "STOP BOOST POOL", 1); - if (stristr(aq_data->last_message, "STOP BOOST POOL") != NULL) { - //logMessage(LOG_DEBUG, "**** FOUND STOP BOOST POOL ****\n"); - send_cmd(KEY_ENTER); - } else { - logMessage(LOG_DEBUG, "**** GIVING UP ****\n"); + if (i < wait_messages) { + // Takes ages to see bost is off from menu, to set it here. + aq_data->boost = false; + aq_data->boost_msg[0] = '\0'; + aq_data->swg_percent = 0; } -/* - if (stristr(aq_data->last_message, "STOP BOOST POOL") == NULL) { - send_cmd(KEY_RIGHT); - logMessage(LOG_DEBUG, "**** FOUND STOP BOOST POOL ****\n"); - } else { - logMessage(LOG_ERR, "**** NOT FOUND STOP BOOST POOL ****\n"); - } -*/ - // Extra message overcome. - //send_cmd(KEY_RIGHT); - //waitfor_queue2empty(); /* + // Extra message overcome. + send_cmd(KEY_RIGHT); + waitfor_queue2empty(); if ( select_sub_menu_item(aq_data, "STOP BOOST POOL") != true ) { logMessage(LOG_WARNING, "Could not select STOP BOOST POOL menu\n"); cancel_menu(); @@ -767,6 +894,7 @@ STOP BOOST POOL }*/ //send_cmd(KEY_ENTER); } + waitForMessage(aq_data,NULL, 1); cleanAndTerminateThread(threadCtrl); @@ -1403,11 +1531,11 @@ void send_cmd(unsigned char cmd, struct aqualinkdata *aq_data) } */ -void waitfor_queue2empty() +void _waitfor_queue2empty(bool longwait) { int i=0; - while ( (_pgm_command != NUL) && ( i++ < 20) ) { + while ( (_pgm_command != NUL) && ( i++ < (30*(longwait?2:1) ) ) ) { //sleep(1); // NSF Change to smaller time. //logMessage(LOG_DEBUG, "******** QUEUE IS FULL ******** delay\n"); delay(50); @@ -1416,7 +1544,7 @@ void waitfor_queue2empty() if (_pgm_command != NUL) { if (pda_mode()) { // Wait for longer in PDA mode since it's slower. - while ( (_pgm_command != NUL) && ( i++ < 100) ) { + while ( (_pgm_command != NUL) && ( i++ < (130*(longwait?2:1)) ) ) { delay(100); } } @@ -1425,6 +1553,15 @@ void waitfor_queue2empty() } +void waitfor_queue2empty() +{ + _waitfor_queue2empty(false); +} +void longwaitfor_queue2empty() +{ + _waitfor_queue2empty(true); +} + void send_cmd(unsigned char cmd) { waitfor_queue2empty(); @@ -1753,6 +1890,33 @@ const char *ptypeName(program_type type) case AQ_SET_BOOST: return "SWG Boost"; break; + case AQ_SET_ONETOUCH_PUMP_RPM: + return "Set Pump RPM"; + break; + case AQ_SET_ONETOUCH_MACRO: + return "Set OneTouch Macro"; + break; + case AQ_GET_ONETOUCH_SETPOINTS: + return "Get OneTouch setpoints"; + break; + case AQ_SET_ONETOUCH_TIME: + return "Set OneTouch time"; + break; + case AQ_SET_ONETOUCH_BOOST: + return "Set OneTouch Boost"; + break; + case AQ_SET_ONETOUCH_SWG_PERCENT: + return "Set OneTouch SWG Percent"; + break; + case AQ_SET_ONETOUCH_FREEZEPROTECT: + return "Set OneTouch Freezeprotect"; + break; + case AQ_SET_ONETOUCH_POOL_HEATER_TEMP: + return "Set OneTouch Pool Heater Temp"; + break; + case AQ_SET_ONETOUCH_SPA_HEATER_TEMP: + return "Set OneTouch Spa Heater Temp"; + break; case AQP_NULL: default: return "Unknown"; diff --git a/aq_programmer.h b/aq_programmer.h index dfe8ed2..7b02f5c 100644 --- a/aq_programmer.h +++ b/aq_programmer.h @@ -2,6 +2,8 @@ #ifndef AQ_PROGRAMMER_H_ #define AQ_PROGRAMMER_H_ +#include +//#include "aqualink.h" // need to get the C values from aqualink manual and add those just incase // someone has the controller set to C. @@ -21,6 +23,12 @@ #define PTHREAD_ARG 25 #define LIGHT_MODE_BUFER PTHREAD_ARG +typedef enum emulation_type{ + ALLBUTTON, + ONETOUCH, + AQUAPDA // AQUAPALM and PDA are taken as specific type. +} emulation_type; + typedef enum { AQP_NULL = -1, AQ_GET_POOL_SPA_HEATER_TEMPS, @@ -39,7 +47,16 @@ typedef enum { AQ_PDA_DEVICE_ON_OFF, AQ_GET_AUX_LABELS, AQ_PDA_WAKE_INIT, - AQ_SET_BOOST + AQ_SET_BOOST, + AQ_SET_ONETOUCH_PUMP_RPM, + AQ_SET_ONETOUCH_MACRO, + AQ_GET_ONETOUCH_SETPOINTS, + AQ_SET_ONETOUCH_POOL_HEATER_TEMP, + AQ_SET_ONETOUCH_SPA_HEATER_TEMP, + AQ_SET_ONETOUCH_FREEZEPROTECT, + AQ_SET_ONETOUCH_TIME, + AQ_SET_ONETOUCH_BOOST, + AQ_SET_ONETOUCH_SWG_PERCENT } program_type; struct programmingThreadCtrl { @@ -49,14 +66,22 @@ struct programmingThreadCtrl { struct aqualinkdata *aq_data; }; +typedef enum pump_type { + PT_UNKNOWN = -1, + EPUMP, + VSPUMP, + VFPUMP +} pump_type; //void aq_programmer(program_type type, void *args, struct aqualinkdata *aq_data); void aq_programmer(program_type type, char *args, struct aqualinkdata *aq_data); -void kick_aq_program_thread(struct aqualinkdata *aq_data); - +//void kick_aq_program_thread(struct aqualinkdata *aq_data); +void kick_aq_program_thread(struct aqualinkdata *aq_data, emulation_type source_type); +bool in_ot_programming_mode(struct aqualinkdata *aq_data); 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); //bool push_aq_cmd(unsigned char cmd); @@ -68,6 +93,8 @@ unsigned char pop_aq_cmd(struct aqualinkdata *aq_data); 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); const char *ptypeName(program_type type); // These shouldn't be here, but just for the PDA AQ PROGRAMMER diff --git a/aq_serial.c b/aq_serial.c index d9cf9ff..6c0be5d 100644 --- a/aq_serial.c +++ b/aq_serial.c @@ -40,7 +40,27 @@ static struct termios _oldtio; void send_packet(int fd, unsigned char *packet, int length); //unsigned char getProtocolType(unsigned char* packet); +/* +#ifdef ONETOUCH +bool _onetouch_mode = false; +void set_onetouch_mode(bool mode) +{ + if (mode) + logMessage(LOG_NOTICE, "AqualinkD is using Onetouch mode\n"); + + _onetouch_mode = mode; +} +bool onetouch_mode() +{ + return _onetouch_mode; +} +#endif +*/ + bool _pda_mode = false; +bool _onetouch_enabled = false; +bool _extended_device_id_programming = false; + void set_pda_mode(bool mode) { if (mode) @@ -53,6 +73,30 @@ bool pda_mode() return _pda_mode; } +void set_onetouch_enabled(bool mode) +{ + if (mode) + logMessage(LOG_NOTICE, "AqualinkD is using use ONETOUCH mode for VSP programming\n"); + _onetouch_enabled = mode; +} + +bool onetouch_enabled() +{ + return _onetouch_enabled; +} + +void set_extended_device_id_programming(bool mode) +{ + if (mode) + logMessage(LOG_NOTICE, "AqualinkD is using use ONETOUCH mode for programming (where supported)\n"); + _extended_device_id_programming = mode; +} + +bool extended_device_id_programming() +{ + return _extended_device_id_programming; +} + const char* get_packet_type(unsigned char* packet , int length) { static char buf[15]; @@ -101,7 +145,7 @@ const char* get_packet_type(unsigned char* packet , int length) return "PDA Shiftlines"; break; case CMD_PDA_HIGHLIGHTCHARS: - return "PDA C_HlightChar"; + return "PDA HlightChars"; break; case CMD_IAQ_MSG: return "iAq Message"; @@ -438,10 +482,8 @@ void send_packet(int fd, unsigned char *packet, int length) } if ( getLogLevel() >= LOG_DEBUG_SERIAL) { - //char buf[30]; - //sprintf(buf, "Sent %8.8s ", get_packet_type(packet+1, length)); - //log_packet(buf, packet, length); - logMessage(LOG_DEBUG_SERIAL, "Serial send %d bytes\n",length-2); + // Packet is padded with 0x00, so discard for logging + logMessage(LOG_DEBUG_SERIAL, "Serial write %d bytes\n",length-2); logPacket(&packet[1], length-2); } } @@ -460,6 +502,8 @@ void _send_ack(int fd, unsigned char ack_type, unsigned char command) ackPacket[7] = generate_checksum(ackPacket, length-1); } + //printf("***Send ACK (%s) ***\n",(ack_type==ACK_NORMAL?"Normal":(ack_type==ACK_SCREEN_BUSY?"ScreenBusy":"ScreenBusyDisplay")) ); + send_packet(fd, ackPacket, length); } @@ -660,10 +704,8 @@ int _get_packet(int fd, unsigned char* packet, bool rawlog) return 0; } - if ( getLogLevel() >= LOG_DEBUG_SERIAL) { - logMessage(LOG_DEBUG_SERIAL, "Serial read %d bytes\n",index); - logPacket(packet, index); - } + logMessage(LOG_DEBUG_SERIAL, "Serial read %d bytes\n",index); + logPacket(packet, index); // Return the packet length. return index; } @@ -673,6 +715,63 @@ int _get_packet(int fd, unsigned char* packet, bool rawlog) #endif + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #else //USE_AQ_SERIAL_OLD diff --git a/aq_serial.h b/aq_serial.h index 71ce0c2..15b8b95 100644 --- a/aq_serial.h +++ b/aq_serial.h @@ -65,9 +65,27 @@ /* ACK RETURN COMMANDS */ #define ACK_NORMAL 0x00 -#define ACK_SCREEN_BUSY 0x01 // Seems to be busy but can cache a message, +#define ACK_SCREEN_BUSY_SCROLL 0x01 // Seems to be busy but can cache a message, #define ACK_SCREEN_BUSY_BLOCK 0x03 // Seems to be don't send me shit. + +// Remove this and fix all compile errors when get time. +#define ACK_SCREEN_BUSY ACK_SCREEN_BUSY_SCROLL + #define ACK_PDA 0x40 +#define ACK_ONETOUCH 0x80 +#define ACK_ALLB_SIM 0x80 // Jandy's Allbutton simulator uses this and not ACK_NORMAL +#define ACK_ALLB_SIM_BUSY 0x81 // Jandy's Allbutton simulator uses this and not ACK_SCREEN_BUSY_SCROLL + +/* ONE TOUCH KEYCODES */ +#define KEY_ONET_UP 0x06 +#define KEY_ONET_DOWN 0x05 +#define KEY_ONET_SELECT 0x04 +#define KEY_ONET_PAGE_UP 0x03 // Top +#define KEY_ONET_BACK 0x02 // Middle +#define KEY_ONET_PAGE_DN 0x01 // Bottom +#define KEY_ONET_SELECT_1 KEY_ONET_PAGE_UP +#define KEY_ONET_SELECT_2 KEY_ONET_BACK +#define KEY_ONET_SELECT_3 KEY_ONET_PAGE_DN /* AquaRite commands */ #define CMD_GETID 0x14 // May be remote control control @@ -187,7 +205,7 @@ SPILLOVER IS DISABLED WHILE SPA IS ON #define MSG_PMP_RPM "RPM:" #define MSG_PMP_WAT "Watts:" -#define MSG_PMP_GPH "GPH:" +#define MSG_PMP_GPM "GPM:" /* AQUAPURE SWG */ @@ -221,11 +239,36 @@ SPILLOVER IS DISABLED WHILE SPA IS ON #define CMD_PDA_SHIFTLINES 0x0F #define CMD_PDA_HIGHLIGHTCHARS 0x10 +// One Touch commands +//#define CMD_PDA_0x04 0x04 // No idea, might be building menu + /* iAqualink */ -#define CMD_IAQ_MSG 0x25 +/* None of these are used, just here to gather data for the moment */ +#define CMD_IAQ_MSG 0x25 // Equiptment status message?? #define CMD_IAQ_MENU_MSG 0x24 +#define CMD_IAQ_MSG_LONG 0x2c // Long status message?? +#define CMD_IAQ_MSG_3 0x2d // Equiptment status message?? +#define CMD_IAQ_0x30 0x30 +#define CMD_IAQ_0x23 0x23 +#define CMD_IAQ_0x24 0x24 // Text for labels or maybe buttons (looks like next BIT is placment) +#define CMD_IAQ_0x25 0x25 // Status for labels +#define CMD_IAQ_0x31 0x31 // Some pump speed info +#define IAQ_KEY_PUMP 0x11 +#define IAQ_KEY_SPA 0x12 +#define IAQ_KEY_POOL_HEAT 0x13 +#define IAQ_KEY_SPA_HEAT 0x14 +#define IAQ_KEY_CUST_1 0x15 +#define IAQ_KEY_CUST_2 0x16 +#define IAQ_KEY_CUST_3 0x17 +#define IAQ_KEY_AUX1 0x19 // Depending on page this is 0x15 +#define IAQ_KEY_AUX2 0x1a +#define IAQ_KEY_AUX3 0x1b +#define IAQ_KEY_AUX4 0x1c +#define IAQ_KEY_AUX5 0x1d +#define IAQ_KEY_AUX6 0x1e +#define IAQ_KEY_AUX7 0x1f typedef enum { ON, @@ -275,11 +318,22 @@ int get_packet_lograw(int fd, unsigned char* packet); void process_status(unsigned char* ptr); const char* get_packet_type(unsigned char* packet , int length); +void set_onetouch_enabled(bool mode); +bool onetouch_enabled(); + +void set_extended_device_id_programming(bool mode); +bool extended_device_id_programming(); void send_jandy_command(int fd, unsigned char *packet_buffer, int size); void send_pentair_command(int fd, unsigned char *packet_buffer, int size); void send_command(int fd, unsigned char *packet_buffer, int size); +/* +#ifdef ONETOUCH +void set_onetouch_mode(bool mode); +bool onetouch_mode(); +#endif +*/ //void send_test_cmd(int fd, unsigned char destination, unsigned char b1, unsigned char b2, unsigned char b3); //void send_command(int fd, unsigned char destination, unsigned char b1, unsigned char b2, unsigned char b3); //void send_messaged(int fd, unsigned char destination, char *message); diff --git a/aqualink.h b/aqualink.h index 8055522..1fc96e7 100644 --- a/aqualink.h +++ b/aqualink.h @@ -59,25 +59,40 @@ typedef enum action_type { SPA_HTR_SETOINT, FREEZE_SETPOINT, SWG_SETPOINT, - SWG_BOOST + SWG_BOOST, + PUMP_RPM } action_type; struct action { action_type type; time_t requested; int value; + int id; // Only used for Pumps at the moment. //char value[10]; }; +// Moved to aq_programmer to stop circular dependancy +/* +typedef enum pump_type { + PT_UNKNOWN = -1, + EPUMP, + VSPUMP, + VFPUMP +} pump_type; +*/ + typedef struct pumpd { int rpm; - int gph; + int gpm; int watts; unsigned char pumpID; + int pumpIndex; + pump_type pumpType; //int buttonID; protocolType ptype; aqkey *button; + //bool updated; } pump_detail; struct aqualinkdata @@ -115,6 +130,7 @@ struct aqualinkdata aqledstate service_mode_state; aqledstate frz_protect_state; unsigned char last_packet_type; + int num_pumps; pump_detail pumps[MAX_PUMPS]; int open_websockets; bool boost; diff --git a/aqualinkd.c b/aqualinkd.c index 9c6fedb..141e867 100644 --- a/aqualinkd.c +++ b/aqualinkd.c @@ -28,6 +28,7 @@ #include // Need GNU_SOURCE & XOPEN defined for strptime +#define AQUALINKD_C #include "mongoose.h" #include "aqualink.h" #include "utils.h" @@ -42,14 +43,15 @@ #include "pda_aq_programmer.h" #include "packetLogger.h" #include "aquapure.h" +#include "onetouch.h" +#include "onetouch_aq_programmer.h" #include "version.h" -//#define PROCESS_INCOMPLETE_MESSAGES //#define DEFAULT_CONFIG_FILE "./aqualinkd.conf" static volatile bool _keepRunning = true; -static struct aqconfig _config_parameters; +//static struct aqconfig _aqconfig_; static struct aqualinkdata _aqualink_data; void main_loop(); @@ -182,26 +184,34 @@ void aqualink_strcpy(char *dest, char *src) //dest[10] = '\0'; } */ +/* void queueGetProgramData() { //aq_programmer(AQ_GET_DIAGNOSTICS_MODEL, NULL, &_aqualink_data); // Init string good time to get setpoints //aq_programmer(AQ_SEND_CMD, (char *)KEY_ENTER, &_aqualink_data); //aq_programmer(AQ_SEND_CMD, (char *)*NUL, &_aqualink_data); + +#ifndef DEBUG_NO_INIT_TEST_REMOVE aq_send_cmd(NUL); aq_programmer(AQ_GET_POOL_SPA_HEATER_TEMPS, NULL, &_aqualink_data); aq_programmer(AQ_GET_FREEZE_PROTECT_TEMP, NULL, &_aqualink_data); +#endif + //aq_programmer(AQ_GET_POOL_SPA_HEATER_TEMPS, NULL, &_aqualink_data); - if (_config_parameters.use_panel_aux_labels == true) + if (_aqconfig_.use_panel_aux_labels == true) { aq_programmer(AQ_GET_AUX_LABELS, NULL, &_aqualink_data); } //aq_programmer(AQ_GET_PROGRAMS, NULL, &_aqualink_data); // only displays to log at present, also seems to confuse getting set_points } - +*/ void setUnits(char *msg) { - logMessage(LOG_DEBUG, "Getting temp units from message %s, looking at %c", msg, msg[strlen(msg) - 1]); + char buf[AQ_MSGLEN*3]; + + ascii(buf, msg); + logMessage(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; @@ -210,20 +220,29 @@ void setUnits(char *msg) else _aqualink_data.temp_units = UNKNOWN; - logMessage(LOG_INFO, "Temp Units set to %d (F=0, C=1, Unknown=3)", _aqualink_data.temp_units); + logMessage(LOG_INFO, "Temp Units set to %d (F=0, C=1, Unknown=2)\n", _aqualink_data.temp_units); } + + +#define MSG_FREEZE 1 // 2^0, bit 0 +#define MSG_SERVICE 2 // 2^1, bit 1 +#define MSG_SWG 4 // 2^2, bit 2 +#define MSG_BOOST 8 // 2^3, bit 3 + void processMessage(char *message) { 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 int freeze_msg_count = 0; + //static int service_msg_count = 0; + //static int swg_msg_count = 0; + //static int boost_msg_count = 0; + static unsigned char msg_loop = '\0'; // NSF replace message with msg msg = stripwhitespace(message); + strcpy(_aqualink_data.last_message, msg); //_aqualink_data.last_message = _aqualink_data.message; //_aqualink_data.display_message = NULL; @@ -233,6 +252,9 @@ void processMessage(char *message) logMessage(LOG_INFO, "RS Message :- '%s'\n", msg); //logMessage(LOG_NOTICE, "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; + // 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. // @@ -240,40 +262,26 @@ void processMessage(char *message) if (stristr(msg, "JANDY AquaLinkRS") != NULL) { //_aqualink_data.display_message = NULL; _aqualink_data.last_display_message[0] = '\0'; - } - // Don't do any message counts if we are programming - if (_aqualink_data.active_thread.thread_id == 0) { - // If we have more than 10 messages without "Service Mode is active" assume it's off. - if (_aqualink_data.service_mode_state != OFF && service_msg_count++ > 10) { - _aqualink_data.service_mode_state = OFF; - service_msg_count = 0; - } - - // If we have more than 40 messages without "SALT or AQUAPURE" assume SWG is off. - if ( _config_parameters.read_all_devices == false ) { - if (_aqualink_data.ar_swg_status == SWG_STATUS_ON && swg_msg_count++ > 40) { - //printf("***************** Manually turned SWG off ************************\n"); - _aqualink_data.ar_swg_status = SWG_STATUS_OFF; - swg_msg_count = 0; - } - } - - // If we have more than 10 messages without "FREE PROTECT ACTIVATED" assume it's off. - if (_aqualink_data.frz_protect_state == ON && freeze_msg_count++ > 10) { - _aqualink_data.frz_protect_state = ENABLE; - freeze_msg_count = 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 = OFF; - if (_aqualink_data.boost == true && boost_msg_count++ > 10) { + //if ((msg_loop & MSG_SERVICE) != MSG_SERVICE) + // _aqualink_data.service_mode_state = OFF; // IF we get this message then Service / Timeout is off + + if ((msg_loop & MSG_SWG) != MSG_SWG) + _aqualink_data.ar_swg_status = SWG_STATUS_OFF; + + if ((msg_loop & MSG_BOOST) != MSG_BOOST) { _aqualink_data.boost = false; _aqualink_data.boost_msg[0] = '\0'; - boost_msg_count = 0; + if (_aqualink_data.swg_percent >= 101) + _aqualink_data.swg_percent = 0; } - } - //else - //_aqualink_data.display_last_message = false; + msg_loop = '\0'; + } if (stristr(msg, LNG_MSG_BATTERY_LOW) != NULL) { @@ -371,30 +379,35 @@ void processMessage(char *message) if (_aqualink_data.service_mode_state == OFF) logMessage(LOG_NOTICE, "AqualinkD set to Service Mode\n"); _aqualink_data.service_mode_state = ON; - service_msg_count = 0; + msg_loop |= MSG_SERVICE; + //service_msg_count = 0; } else if (stristr(msg, LNG_MSG_TIMEOUT_ACTIVE) != NULL) { if (_aqualink_data.service_mode_state == OFF) logMessage(LOG_NOTICE, "AqualinkD set to Timeout Mode\n"); _aqualink_data.service_mode_state = FLASH; - service_msg_count = 0; + msg_loop |= MSG_SERVICE; + //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; + //freeze_msg_count = 0; strcpy(_aqualink_data.last_display_message, msg); // Also display the message on web UI } else if (msg[2] == '/' && msg[5] == '/' && msg[8] == ' ') { // date in format '08/29/16 MON' strcpy(_aqualink_data.date, msg); } - else if (strncasecmp(msg, MSG_SWG_PCT, MSG_SWG_PCT_LEN) == 0) + //else if (strncasecmp(msg, MSG_SWG_PCT, MSG_SWG_PCT_LEN) == 0) + else if (strncasecmp(msg, MSG_SWG_PCT, MSG_SWG_PCT_LEN) == 0 && strncasecmp(msg, "AQUAPURE HRS", 12) != 0) { _aqualink_data.swg_percent = atoi(msg + MSG_SWG_PCT_LEN); if (_aqualink_data.ar_swg_status == SWG_STATUS_OFF) {_aqualink_data.ar_swg_status = SWG_STATUS_ON;} - swg_msg_count = 0; + //swg_msg_count = 0; + msg_loop |= MSG_SWG; //logMessage(LOG_DEBUG, "*** '%s' ***\n", msg); //logMessage(LOG_DEBUG, "SWG set to %d due to message from control panel\n", _aqualink_data.swg_percent); } @@ -402,7 +415,8 @@ void processMessage(char *message) { _aqualink_data.swg_ppm = atoi(msg + MSG_SWG_PPM_LEN); if (_aqualink_data.ar_swg_status == SWG_STATUS_OFF) {_aqualink_data.ar_swg_status = SWG_STATUS_ON;} - swg_msg_count = 0; + msg_loop |= MSG_SWG; + //swg_msg_count = 0; //logMessage(LOG_DEBUG, "Stored SWG PPM as %d\n", _aqualink_data.swg_ppm); } else if ((msg[1] == ':' || msg[2] == ':') && msg[strlen(msg) - 1] == 'M') @@ -434,7 +448,8 @@ void processMessage(char *message) logMessage(LOG_NOTICE, "Control Panel %s\n", msg); if (_initWithRS == false) { - queueGetProgramData(); + //queueGetProgramData(ALLBUTTON, &_aqualink_data); + queueGetExtendedProgramData(ALLBUTTON, &_aqualink_data, _aqconfig_.use_panel_aux_labels); _initWithRS = true; } } @@ -442,7 +457,7 @@ void processMessage(char *message) { logMessage(LOG_NOTICE, "Program data '%s'\n", msg); } - else if (_config_parameters.override_freeze_protect == TRUE && strncasecmp(msg, "Press Enter* to override Freeze Protection with", 47) == 0) + 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); @@ -451,7 +466,7 @@ void processMessage(char *message) else if ((msg[4] == ':') && (strncasecmp(msg, "AUX", 3) == 0)) { // AUX label "AUX1:" int labelid = atoi(msg + 3); - if (labelid > 0 && _config_parameters.use_panel_aux_labels == true) + if (labelid > 0 && _aqconfig_.use_panel_aux_labels == true) { // Aux1: on panel = Button 3 in aqualinkd (button 2 in array) logMessage(LOG_NOTICE, "AUX LABEL %d '%s'\n", labelid + 1, msg); @@ -460,12 +475,16 @@ void processMessage(char *message) } } // BOOST POOL 23:59 REMAINING - else if ( (strncasecmp(msg, "BOOST POOL", 10) == 0) && (strstr(msg, "REMAINING") != NULL) ) { + 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 (_aqualink_data.active_thread.thread_id == 0) { snprintf(_aqualink_data.boost_msg, 6, &msg[11]); _aqualink_data.boost = true; - boost_msg_count = 0; + msg_loop |= MSG_BOOST; + msg_loop |= MSG_SWG; + if (_aqualink_data.ar_swg_status != SWG_STATUS_ON) {_aqualink_data.ar_swg_status = SWG_STATUS_ON;} + if (_aqualink_data.swg_percent != 101) {_aqualink_data.swg_percent = 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 } @@ -494,59 +513,9 @@ void processMessage(char *message) // We processed the next message, kick any threads waiting on the message. //printf ("Message kicking\n"); - kick_aq_program_thread(&_aqualink_data); + kick_aq_program_thread(&_aqualink_data, ALLBUTTON); } -/* -bool process_pda_monitor_packet(unsigned char *packet, int length) -{ - bool rtn = false; - static bool checkedMenu = false; - process_pda_menu_packet(packet, length); - - //printf("*** PDA Received 0x%02hhx %s ***\n", packet[PKT_CMD], get_packet_type(packet, length)); - - if (packet[PKT_CMD] == CMD_STATUS && pda_m_type() == PM_EQUIPTMENT_STATUS && checkedMenu == false) { - // Can't use any of the build in pda_menu find functions. - //printf("*** PDA Checking equiptment status ***\n"); - logMessage(LOG_INFO, "Checking PDA equiptment status\n"); - int i; - char *index; - int pi = 0; - for (i = 0; i < PDA_LINES; i++) { - if (strcasestr(pda_m_line(i), "epump") != NULL || strcasestr(pda_m_line(i), "Intelliflo") != NULL ) - { // Pump labels arew ePUMP & Intelliflo with number at end "Jandy ePUMP 1" "Intelliflo VS 1" - pi = atoi(pda_m_line(i)+14)-1; - if (pi < 0 || pi >= MAX_PUMPS-1) - pi = 0; - } - else if ((index = strcasestr(pda_m_line(i), MSG_PMP_RPM)) != NULL) - { - _aqualink_data.pumps[0].rpm = atoi(index + strlen(MSG_PMP_RPM)); - rtn = true; - logMessage(LOG_DEBUG, "Pump %d RPM = %d\n", pi+1, _aqualink_data.pumps[0].rpm); - } - else if ((index = strcasestr(pda_m_line(i), MSG_PMP_WAT)) != NULL) - { // Default to pump 0, should check for correct pump - _aqualink_data.pumps[pi].watts = atoi(index + strlen(MSG_PMP_WAT)); - rtn = true; - logMessage(LOG_DEBUG, "Pump %d Watts = %d\n", pi+1, _aqualink_data.pumps[pi].watts); - } - else if ((index = strcasestr(pda_m_line(i), MSG_PMP_GPH)) != NULL) - { // Default to pump 0, should check for correct pump - _aqualink_data.pumps[pi].gph = atoi(index + strlen(MSG_PMP_GPH)); - rtn = true; - logMessage(LOG_DEBUG, "Pump %d GPH = %d\n", pi+1, _aqualink_data.pumps[pi].watts); - } - } - checkedMenu = true; - } else if (packet[PKT_CMD] != CMD_STATUS) { // a lot of STATUS messages after menu has been created, so ignore them reset on different message - checkedMenu = false; - } - - return rtn; -} -*/ bool process_packet(unsigned char *packet, int length) { bool rtn = false; @@ -567,12 +536,11 @@ bool process_packet(unsigned char *packet, int length) rtn = true; } - if (_config_parameters.pda_mode == true) + if (_aqconfig_.pda_mode == true) { return process_pda_packet(packet, length); } - // If we are in the middle of processing a message, and get another, we end the message if (processing_long_msg > 0 && packet[PKT_CMD] != CMD_MSG_LONG) { processing_long_msg = 0; @@ -594,11 +562,11 @@ bool process_packet(unsigned char *packet, int length) { _aqualink_data.pool_temp = TEMP_UNKNOWN; _aqualink_data.spa_temp = TEMP_UNKNOWN; - //_aqualink_data.spa_temp = _config_parameters.report_zero_spa_temp?-18:TEMP_UNKNOWN; + //_aqualink_data.spa_temp = _aqconfig_.report_zero_spa_temp?-18:TEMP_UNKNOWN; } else if (_aqualink_data.aqbuttons[SPA_INDEX].led->state == OFF && _aqualink_data.single_device != true) { - //_aqualink_data.spa_temp = _config_parameters.report_zero_spa_temp?-18:TEMP_UNKNOWN; + //_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 && _aqualink_data.single_device != true) @@ -609,11 +577,11 @@ bool process_packet(unsigned char *packet, int length) // COLOR MODE programming relies on state changes, so let any threads know if (_aqualink_data.active_thread.ptype == AQ_SET_COLORMODE) { //printf ("Light thread kicking\n"); - kick_aq_program_thread(&_aqualink_data); + kick_aq_program_thread(&_aqualink_data, ALLBUTTON); } break; - case CMD_MSG: - case CMD_MSG_LONG: + 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); @@ -628,7 +596,7 @@ bool process_packet(unsigned char *packet, int length) //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); + kick_aq_program_thread(&_aqualink_data, ALLBUTTON); #endif } @@ -639,37 +607,6 @@ bool process_packet(unsigned char *packet, int length) } break; -/* - case CMD_MSG: - memset(message, 0, AQ_MSGLONGLEN + 1); - strncpy(message, (char *)packet + PKT_DATA + 1, AQ_MSGLEN); - - logMessage(LOG_DEBUG_SERIAL, "RS Received message '%s'\n",message); - //kick_aq_program_thread(&_aqualink_data); - - if (packet[PKT_DATA] == 1) // Start of long message, get them all before processing - { -//printf ("Start long message thread kicking\n"); -// kick_aq_program_thread(&_aqualink_data); - break; - } - processMessage(message); // This will kick thread - //processMessage(message); - break; - case CMD_MSG_LONG: - // First in sequence is normal message. - processing_long_msg++; - strncpy(&message[processing_long_msg * AQ_MSGLEN], (char *)packet + PKT_DATA + 1, AQ_MSGLEN); - logMessage(LOG_DEBUG_SERIAL, "RS Received long message '%s'\n",message); - if (processing_long_msg == 3) - { - //logMessage(LOG_DEBUG, "RS Finished receiving of MSG_LONG '%s'\n",message); - processMessage(message); // This will kick thread - processing_long_msg = 0; - } else { - //kick_aq_program_thread(&_aqualink_data); - } - break;*/ case CMD_PROBE: logMessage(LOG_DEBUG, "RS Received PROBE length %d.\n", length); //logMessage(LOG_INFO, "Synch'ing with Aqualink master device...\n"); @@ -689,8 +626,9 @@ void action_delayed_request() char sval[10]; snprintf(sval, 9, "%d", _aqualink_data.unactioned.value); - // If we don't know the units yet, we can't action, so wait until we do. - if (_aqualink_data.temp_units == UNKNOWN && _aqualink_data.unactioned.type != SWG_SETPOINT) + // If we don't know the units yet, we can't action setpoint, so wait until we do. + if (_aqualink_data.temp_units == UNKNOWN && + (_aqualink_data.unactioned.type == POOL_HTR_SETOINT || _aqualink_data.unactioned.type == SPA_HTR_SETOINT || _aqualink_data.unactioned.type == FREEZE_SETPOINT)) return; if (_aqualink_data.unactioned.type == POOL_HTR_SETOINT) @@ -768,9 +706,16 @@ void action_delayed_request() // Let's just tell everyone we set it, before we actually did. Makes homekit happy, and it will re-correct on error. _aqualink_data.boost = _aqualink_data.unactioned.value; } + else if (_aqualink_data.unactioned.type == PUMP_RPM) + { + snprintf(sval, 9, "%1d|%d", _aqualink_data.unactioned.id, _aqualink_data.unactioned.value); + //printf("**** program string '%s'\n",sval); + aq_programmer(AQ_SET_ONETOUCH_PUMP_RPM, sval, &_aqualink_data); + } _aqualink_data.unactioned.type = NO_ACTION; _aqualink_data.unactioned.value = -1; + _aqualink_data.unactioned.id = -1; _aqualink_data.unactioned.requested = 0; } @@ -785,10 +730,10 @@ void printHelp() printf("\t-rsd (RS485 debug)\n"); printf("\t-rsrd (RS485 raw debug)\n"); } + + int main(int argc, char *argv[]) { - // main_loop (); - int i, j; //char *cfgFile = DEFAULT_CONFIG_FILE; char defaultCfg[] = "./aqualinkd.conf"; @@ -796,12 +741,44 @@ int main(int argc, char *argv[]) int cmdln_loglevel = -1; bool cmdln_debugRS485 = false; bool cmdln_lograwRS485 = false; + _aqualink_data.num_pumps = 0; + +/* + static unsigned char msg_loop; // = '\0'; + //msg_loop &= ~MSG_SERVICE; + if ((msg_loop & MSG_SERVICE) != MSG_SERVICE) + printf("Off\n"); + else + printf("On\n"); + + //msg_loop &= ~MSG_SERVICE; + msg_loop |= MSG_SERVICE; + if ((msg_loop & MSG_SERVICE) != MSG_SERVICE) + printf("Off\n"); + else + printf("On\n"); + + msg_loop |= MSG_SERVICE; + if ((msg_loop & MSG_SERVICE) != MSG_SERVICE) + printf("Off\n"); + else + printf("On\n"); + + msg_loop |= MSG_SERVICE; + if ((msg_loop & MSG_SERVICE) != MSG_SERVICE) + printf("Off\n"); + else + printf("On\n"); + + + return 0; +*/ if (argc > 1 && strcmp(argv[1], "-h") == 0) - { - printHelp(); - return 0; - } + { + printHelp(); + return 0; + } // struct lws_context_creation_info info; // Log only NOTICE messages and above. Debug and info messages @@ -816,7 +793,8 @@ int main(int argc, char *argv[]) } // Initialize the daemon's parameters. - init_parameters(&_config_parameters); + //init_parameters(&_aqconfig_); + init_config(); cfgFile = defaultCfg; //sprintf(cfgFile, "%s", DEFAULT_CONFIG_FILE); @@ -829,7 +807,7 @@ int main(int argc, char *argv[]) } if (strcmp(argv[i], "-d") == 0) { - _config_parameters.deamonize = false; + _aqconfig_.deamonize = false; } else if (strcmp(argv[i], "-c") == 0) { @@ -854,91 +832,95 @@ int main(int argc, char *argv[]) } initButtons(&_aqualink_data); - - readCfg(&_config_parameters, &_aqualink_data, cfgFile); + + read_config(&_aqualink_data, cfgFile); if (cmdln_loglevel != -1) - _config_parameters.log_level = cmdln_loglevel; + _aqconfig_.log_level = cmdln_loglevel; if (cmdln_debugRS485) - _config_parameters.debug_RSProtocol_packets = true; + _aqconfig_.debug_RSProtocol_packets = true; if (cmdln_lograwRS485) - _config_parameters.log_raw_RS_bytes = true; + _aqconfig_.log_raw_RS_bytes = true; - if (_config_parameters.display_warnings_web == true) - setLoggingPrms(_config_parameters.log_level, _config_parameters.deamonize, _config_parameters.log_file, _aqualink_data.last_display_message); + if (_aqconfig_.display_warnings_web == true) + setLoggingPrms(_aqconfig_.log_level, _aqconfig_.deamonize, _aqconfig_.log_file, _aqualink_data.last_display_message); else - setLoggingPrms(_config_parameters.log_level, _config_parameters.deamonize, _config_parameters.log_file, NULL); + setLoggingPrms(_aqconfig_.log_level, _aqconfig_.deamonize, _aqconfig_.log_file, NULL); logMessage(LOG_NOTICE, "%s v%s\n", AQUALINKD_NAME, AQUALINKD_VERSION); - logMessage(LOG_NOTICE, "Config log_level = %d\n", _config_parameters.log_level); - logMessage(LOG_NOTICE, "Config socket_port = %s\n", _config_parameters.socket_port); - logMessage(LOG_NOTICE, "Config serial_port = %s\n", _config_parameters.serial_port); - logMessage(LOG_NOTICE, "Config web_directory = %s\n", _config_parameters.web_directory); - logMessage(LOG_NOTICE, "Config device_id = 0x%02hhx\n", _config_parameters.device_id); - logMessage(LOG_NOTICE, "Config read_all_devices = %s\n", bool2text(_config_parameters.read_all_devices)); - logMessage(LOG_NOTICE, "Config use_aux_labels = %s\n", bool2text(_config_parameters.use_panel_aux_labels)); - logMessage(LOG_NOTICE, "Config override frz prot = %s\n", bool2text(_config_parameters.override_freeze_protect)); + logMessage(LOG_NOTICE, "Config log_level = %d\n", _aqconfig_.log_level); + logMessage(LOG_NOTICE, "Config socket_port = %s\n", _aqconfig_.socket_port); + logMessage(LOG_NOTICE, "Config serial_port = %s\n", _aqconfig_.serial_port); + logMessage(LOG_NOTICE, "Config web_directory = %s\n", _aqconfig_.web_directory); + logMessage(LOG_NOTICE, "Config device_id = 0x%02hhx\n", _aqconfig_.device_id); + logMessage(LOG_NOTICE, "Config extra_device_id = 0x%02hhx\n", _aqconfig_.onetouch_device_id); + logMessage(LOG_NOTICE, "Config extra_device_prog = %s\n", bool2text(_aqconfig_.extended_device_id_programming)); + logMessage(LOG_NOTICE, "Config read_all_devices = %s\n", bool2text(_aqconfig_.read_all_devices)); + logMessage(LOG_NOTICE, "Config use_aux_labels = %s\n", bool2text(_aqconfig_.use_panel_aux_labels)); + logMessage(LOG_NOTICE, "Config override frz prot = %s\n", bool2text(_aqconfig_.override_freeze_protect)); #ifndef MG_DISABLE_MQTT - logMessage(LOG_NOTICE, "Config mqtt_server = %s\n", _config_parameters.mqtt_server); - logMessage(LOG_NOTICE, "Config mqtt_dz_sub_topic = %s\n", _config_parameters.mqtt_dz_sub_topic); - logMessage(LOG_NOTICE, "Config mqtt_dz_pub_topic = %s\n", _config_parameters.mqtt_dz_pub_topic); - logMessage(LOG_NOTICE, "Config mqtt_aq_topic = %s\n", _config_parameters.mqtt_aq_topic); - logMessage(LOG_NOTICE, "Config mqtt_user = %s\n", _config_parameters.mqtt_user); - logMessage(LOG_NOTICE, "Config mqtt_passwd = %s\n", _config_parameters.mqtt_passwd); - logMessage(LOG_NOTICE, "Config mqtt_ID = %s\n", _config_parameters.mqtt_ID); - logMessage(LOG_NOTICE, "Config idx water temp = %d\n", _config_parameters.dzidx_air_temp); - logMessage(LOG_NOTICE, "Config idx pool temp = %d\n", _config_parameters.dzidx_pool_water_temp); - logMessage(LOG_NOTICE, "Config idx spa temp = %d\n", _config_parameters.dzidx_spa_water_temp); - logMessage(LOG_NOTICE, "Config idx SWG Percent = %d\n", _config_parameters.dzidx_swg_percent); - logMessage(LOG_NOTICE, "Config idx SWG PPM = %d\n", _config_parameters.dzidx_swg_ppm); - logMessage(LOG_NOTICE, "Config PDA Mode = %s\n", bool2text(_config_parameters.pda_mode)); - logMessage(LOG_NOTICE, "Config PDA Sleep Mode = %s\n", bool2text(_config_parameters.pda_sleep_mode)); - logMessage(LOG_NOTICE, "Config force SWG = %s\n", bool2text(_config_parameters.force_swg)); + logMessage(LOG_NOTICE, "Config mqtt_server = %s\n", _aqconfig_.mqtt_server); + logMessage(LOG_NOTICE, "Config mqtt_dz_sub_topic = %s\n", _aqconfig_.mqtt_dz_sub_topic); + logMessage(LOG_NOTICE, "Config mqtt_dz_pub_topic = %s\n", _aqconfig_.mqtt_dz_pub_topic); + logMessage(LOG_NOTICE, "Config mqtt_aq_topic = %s\n", _aqconfig_.mqtt_aq_topic); + logMessage(LOG_NOTICE, "Config mqtt_user = %s\n", _aqconfig_.mqtt_user); + logMessage(LOG_NOTICE, "Config mqtt_passwd = %s\n", _aqconfig_.mqtt_passwd); + logMessage(LOG_NOTICE, "Config mqtt_ID = %s\n", _aqconfig_.mqtt_ID); + logMessage(LOG_NOTICE, "Config idx water temp = %d\n", _aqconfig_.dzidx_air_temp); + logMessage(LOG_NOTICE, "Config idx pool temp = %d\n", _aqconfig_.dzidx_pool_water_temp); + logMessage(LOG_NOTICE, "Config idx spa temp = %d\n", _aqconfig_.dzidx_spa_water_temp); + logMessage(LOG_NOTICE, "Config idx SWG Percent = %d\n", _aqconfig_.dzidx_swg_percent); + logMessage(LOG_NOTICE, "Config idx SWG PPM = %d\n", _aqconfig_.dzidx_swg_ppm); + logMessage(LOG_NOTICE, "Config PDA Mode = %s\n", bool2text(_aqconfig_.pda_mode)); + logMessage(LOG_NOTICE, "Config PDA Sleep Mode = %s\n", bool2text(_aqconfig_.pda_sleep_mode)); + logMessage(LOG_NOTICE, "Config force SWG = %s\n", bool2text(_aqconfig_.force_swg)); /* removed until domoticz has a better virtual thermostat - logMessage(LOG_NOTICE, "Config idx pool thermostat = %d\n", _config_parameters.dzidx_pool_thermostat); - logMessage(LOG_NOTICE, "Config idx spa thermostat = %d\n", _config_parameters.dzidx_spa_thermostat); + logMessage(LOG_NOTICE, "Config idx pool thermostat = %d\n", _aqconfig_.dzidx_pool_thermostat); + logMessage(LOG_NOTICE, "Config idx spa thermostat = %d\n", _aqconfig_.dzidx_spa_thermostat); */ #endif // MG_DISABLE_MQTT - logMessage(LOG_NOTICE, "Config deamonize = %s\n", bool2text(_config_parameters.deamonize)); - logMessage(LOG_NOTICE, "Config log_file = %s\n", _config_parameters.log_file); - logMessage(LOG_NOTICE, "Config light_pgm_mode = %.2f\n", _config_parameters.light_programming_mode); - logMessage(LOG_NOTICE, "Debug RS485 protocol = %s\n", bool2text(_config_parameters.debug_RSProtocol_packets)); - //logMessage(LOG_NOTICE, "Use PDA 4 auxiliary info = %s\n", bool2text(_config_parameters.use_PDA_auxiliary)); - logMessage(LOG_NOTICE, "Read Pentair Packets = %s\n", bool2text(_config_parameters.read_pentair_packets)); + logMessage(LOG_NOTICE, "Config deamonize = %s\n", bool2text(_aqconfig_.deamonize)); + logMessage(LOG_NOTICE, "Config log_file = %s\n", _aqconfig_.log_file); + logMessage(LOG_NOTICE, "Config light_pgm_mode = %.2f\n", _aqconfig_.light_programming_mode); + logMessage(LOG_NOTICE, "Debug RS485 protocol = %s\n", bool2text(_aqconfig_.debug_RSProtocol_packets)); + //logMessage(LOG_NOTICE, "Use PDA 4 auxiliary info = %s\n", bool2text(_aqconfig_.use_PDA_auxiliary)); + logMessage(LOG_NOTICE, "Read Pentair Packets = %s\n", bool2text(_aqconfig_.read_pentair_packets)); // logMessage (LOG_NOTICE, "Config serial_port = %s\n", config_parameters->serial_port); - logMessage(LOG_NOTICE, "Display warnings in web = %s\n", bool2text(_config_parameters.display_warnings_web)); + logMessage(LOG_NOTICE, "Display warnings in web = %s\n", bool2text(_aqconfig_.display_warnings_web)); - if (_config_parameters.swg_zero_ignore > 0) - logMessage(LOG_NOTICE, "Ignore SWG 0 msg count = %d\n", _config_parameters.swg_zero_ignore); + if (_aqconfig_.swg_zero_ignore > 0) + logMessage(LOG_NOTICE, "Ignore SWG 0 msg count = %d\n", _aqconfig_.swg_zero_ignore); for (i = 0; i < TOTAL_BUTONS; i++) { char vsp[] = "None"; - for (j = 0; j < MAX_PUMPS; j++) { + int alid = 0; + for (j = 0; j < _aqualink_data.num_pumps; j++) { //if (_aqualink_data.pumps[j].buttonID == i) { if (_aqualink_data.pumps[j].button == &_aqualink_data.aqbuttons[i]) { sprintf(vsp,"0x%02hhx",_aqualink_data.pumps[j].pumpID); + alid = _aqualink_data.pumps[j].pumpIndex; //printf("Pump %d %d %d\n",_aqualink_data.pumps[j].pumpID, _aqualink_data.pumps[j].buttonID, _aqualink_data.pumps[j].ptype); } } - if (!_config_parameters.pda_mode) { - logMessage(LOG_NOTICE, "Config BTN %-13s = label %-15s | VSP ID %-4s | dzidx %d | %s\n", - _aqualink_data.aqbuttons[i].name, _aqualink_data.aqbuttons[i].label, vsp, _aqualink_data.aqbuttons[i].dz_idx, - (i>0 && (i==_config_parameters.light_programming_button_pool || i==_config_parameters.light_programming_button_spa)?"Programable":"") ); + if (!_aqconfig_.pda_mode) { + logMessage(LOG_NOTICE, "Config BTN %-13s = label %-15s | VSP ID %-4s | AL ID %-1d | dzidx %-3d | %s\n", + _aqualink_data.aqbuttons[i].name, _aqualink_data.aqbuttons[i].label, vsp, alid, _aqualink_data.aqbuttons[i].dz_idx, + (i>0 && (i==_aqconfig_.light_programming_button_pool || i==_aqconfig_.light_programming_button_spa)?"Programable":"") ); } else { - logMessage(LOG_NOTICE, "Config BTN %-13s = label %-15s | VSP ID %-4s | PDAlabel %-15s | dzidx %d\n", - _aqualink_data.aqbuttons[i].name, _aqualink_data.aqbuttons[i].label, vsp, + logMessage(LOG_NOTICE, "Config BTN %-13s = label %-15s | VSP ID %-4s | AL ID %-1d | PDAlabel %-15s | dzidx %d\n", + _aqualink_data.aqbuttons[i].name, _aqualink_data.aqbuttons[i].label, vsp, alid, _aqualink_data.aqbuttons[i].pda_label, _aqualink_data.aqbuttons[i].dz_idx ); } //logMessage(LOG_NOTICE, "Button %d\n", i+1, _aqualink_data.aqbuttons[i].label , _aqualink_data.aqbuttons[i].dz_idx); } - if (_config_parameters.deamonize == true) + if (_aqconfig_.deamonize == true) { char pidfile[256]; // sprintf(pidfile, "%s/%s.pid",PIDLOCATION, basename(argv[0])); @@ -953,116 +935,6 @@ int main(int argc, char *argv[]) exit(EXIT_SUCCESS); } -/* -void debugPacketPrint(unsigned char ID, unsigned char *packet_buffer, int packet_length) -{ - char buff[1000]; - int i = 0; - int cnt = 0; - - cnt = sprintf(buff, "%4.4s 0x%02hhx of type %8.8s", (packet_buffer[PKT_DEST] == 0x00 ? "From" : "To"), ID, get_packet_type(packet_buffer, packet_length)); - cnt += sprintf(buff + cnt, " | HEX: "); - //printHex(packet_buffer, packet_length); - for (i = 0; i < packet_length; i++) - cnt += sprintf(buff + cnt, "0x%02hhx|", packet_buffer[i]); - - if (packet_buffer[PKT_CMD] == CMD_MSG) - { - cnt += sprintf(buff + cnt, " Message : "); - //fwrite(packet_buffer + 4, 1, packet_length - 4, stdout); - strncpy(buff + cnt, (char *)packet_buffer + PKT_DATA + 1, AQ_MSGLEN); - cnt += AQ_MSGLEN; - } - - if (packet_buffer[PKT_DEST] == 0x00) - cnt += sprintf(buff + cnt, "\n\n"); - else - cnt += sprintf(buff + cnt, "\n"); - - //logMessage(LOG_NOTICE, "- AQUA SWG - \n%s", buff); - if (_config_parameters.debug_RSProtocol_packets) - writePacketLog(buff); - else - logMessage(LOG_NOTICE, "%s", buff); -} - -void debugPacket(unsigned char *packet_buffer, int packet_length) -{ - static unsigned char lastID; - - if (packet_buffer[PKT_DEST] == DEV_MASTER && (lastID == 0x50 || lastID == 0x58)) - { - debugPacketPrint(lastID, packet_buffer, packet_length); - } - else if (packet_buffer[PKT_DEST] == 0x50 || packet_buffer[PKT_DEST] == 0x58) - { - debugPacketPrint(packet_buffer[PKT_DEST], packet_buffer, packet_length); - } - - lastID = packet_buffer[PKT_DEST]; -} - -void logPacket(unsigned char *packet_buffer, int packet_length) -{ - static unsigned char last_packet_buffer[AQ_MAXPKTLEN]; - static int last_packet_length; - - if (packet_buffer[PKT_DEST] != DEV_MASTER) - { - memcpy(last_packet_buffer, packet_buffer, packet_length); - last_packet_length = packet_length; - } - else - { - debugPacketPrint(last_packet_buffer[PKT_DEST], last_packet_buffer, last_packet_length); - debugPacketPrint(last_packet_buffer[PKT_DEST], packet_buffer, packet_length); - } -} -*/ -/* -void logPacket_new(unsigned char* packet_buffer, int packet_length) -{ - char buff[1000]; - int i = 0; - int cnt = 0; - - cnt = sprintf(buff, "%8.8s Packet | HEX: ",getProtocolType(packet_buffer)==JANDY?"Jandy":"Pentair"); - - for (i=0;i 0) { @@ -1178,40 +1057,48 @@ void main_loop() for (i=0; i < MAX_PUMPS; i++) { _aqualink_data.pumps[i].rpm = TEMP_UNKNOWN; - _aqualink_data.pumps[i].gph = TEMP_UNKNOWN; + _aqualink_data.pumps[i].gpm = TEMP_UNKNOWN; _aqualink_data.pumps[i].watts = TEMP_UNKNOWN; } - if (_config_parameters.force_swg == true) { + if (_aqconfig_.force_swg == true) { _aqualink_data.swg_percent = 0; _aqualink_data.swg_ppm = 0; } - if (!start_net_services(&mgr, &_aqualink_data, &_config_parameters)) + if (!start_net_services(&mgr, &_aqualink_data)) { - logMessage(LOG_ERR, "Can not start webserver on port %s.\n", _config_parameters.socket_port); + logMessage(LOG_ERR, "Can not start webserver on port %s.\n", _aqconfig_.socket_port); exit(EXIT_FAILURE); } - startPacketLogger(_config_parameters.debug_RSProtocol_packets, _config_parameters.read_pentair_packets); + startPacketLogger(_aqconfig_.debug_RSProtocol_packets, _aqconfig_.read_pentair_packets); signal(SIGINT, intHandler); signal(SIGTERM, intHandler); int blank_read = 0; - rs_fd = init_serial_port(_config_parameters.serial_port); - logMessage(LOG_NOTICE, "Listening to Aqualink RS8 on serial port: %s\n", _config_parameters.serial_port); + rs_fd = init_serial_port(_aqconfig_.serial_port); + logMessage(LOG_NOTICE, "Listening to Aqualink RS8 on serial port: %s\n", _aqconfig_.serial_port); - if (_config_parameters.pda_mode == true) + if (_aqconfig_.pda_mode == true) { #ifdef BETA_PDA_AUTOLABEL - init_pda(&_aqualink_data, &_config_parameters); + init_pda(&_aqualink_data, &_aqconfig_); #else init_pda(&_aqualink_data); #endif } + if (_aqconfig_.onetouch_device_id != 0x00) + { + set_onetouch_enabled(true); + } + if (_aqconfig_.extended_device_id_programming == true) + { + set_extended_device_id_programming(true); + } - if (_config_parameters.device_id == 0x00) { + if (_aqconfig_.device_id == 0x00) { logMessage(LOG_NOTICE, "Searching for valid ID, please configure one for faster startup\n"); } @@ -1234,11 +1121,11 @@ void main_loop() logMessage(LOG_ERR, "Aqualink daemon looks like serial error, resetting.\n"); close_serial_port(rs_fd); } - rs_fd = init_serial_port(_config_parameters.serial_port); + rs_fd = init_serial_port(_aqconfig_.serial_port); blank_read = 0; } - if (_config_parameters.log_raw_RS_bytes) + if (_aqconfig_.log_raw_RS_bytes) packet_length = get_packet_lograw(rs_fd, packet_buffer); else packet_length = get_packet(rs_fd, packet_buffer); @@ -1254,70 +1141,44 @@ void main_loop() //logMessage(LOG_DEBUG_SERIAL, "Nothing read on serial\n"); blank_read++; } - else if (_config_parameters.device_id == 0x00) { + else if (_aqconfig_.device_id == 0x00) { blank_read = 0; - _config_parameters.device_id = find_unused_address(packet_buffer); + _aqconfig_.device_id = find_unused_address(packet_buffer); continue; } else if (packet_length > 0) { blank_read = 0; changed = false; - - //if (_config_parameters.debug_RSProtocol_packets || getLogLevel() >= LOG_DEBUG_SERIAL) - // logPacket(packet_buffer, packet_length); - - if (packet_length > 0 && packet_buffer[PKT_DEST] == _config_parameters.device_id) +/* + // This is handeled by aq_serial now + if (_aqconfig_.debug_RSProtocol_packets || getLogLevel() >= LOG_DEBUG_SERIAL) + logPacket(packet_buffer, packet_length); +*/ + if (packet_length > 0 && packet_buffer[PKT_DEST] == _aqconfig_.device_id) { - if (getLogLevel() >= LOG_DEBUG) logMessage(LOG_DEBUG, "RS received packet of type %s length %d\n", get_packet_type(packet_buffer, packet_length), packet_length); - - //logMessage(LOG_DEBUG, "RS received packet of type %s length %d\n", get_packet_type(packet_buffer, packet_length), packet_length); - //debugPacketPrint(0x00, packet_buffer, packet_length); - //unsigned char ID, unsigned char *packet_buffer, int packet_length) - - // Process the packet. This includes deriving general status, and identifying - // warnings and errors. If something changed, notify any listeners - if (process_packet(packet_buffer, packet_length) != false) - { - //broadcast_aqualinkstate(mgr.active_connections); - changed = true; - } - - //_aqualink_data.last_packet_type = packet_buffer[PKT_CMD]; + changed = process_packet(packet_buffer, packet_length); // If we are not in PDA or Simulator mode, just sent ACK & any CMD, else caculate the ACK. - if (!_aqualink_data.simulate_panel && !_config_parameters.pda_mode) { - send_ack(rs_fd, pop_aq_cmd(&_aqualink_data)); + if (!_aqualink_data.simulate_panel && !_aqconfig_.pda_mode) { + //send_ack(rs_fd, pop_aq_cmd(&_aqualink_data)); + send_extended_ack(rs_fd, (_aqualink_data.last_packet_type==CMD_MSG_LONG?ACK_SCREEN_BUSY_SCROLL:ACK_NORMAL), pop_aq_cmd(&_aqualink_data)); } else caculate_ack_packet(rs_fd, packet_buffer); -/* MOVE PROCESSING TO AFTER ACK, long programming will fail otherwise (like set time) */ - // Process the packet. This includes deriving general status, and identifying - // warnings and errors. If something changed, notify any listeners - /* - if (process_packet(packet_buffer, packet_length) != false) - { - //broadcast_aqualinkstate(mgr.active_connections); - changed = true; - } - */ - }/* - else if (_config_parameters.use_PDA_auxiliary && packet_length > 0 && packet_buffer[PKT_DEST] == 0x60 && _aqualink_data.aqbuttons[PUMP_INDEX].led->state != OFF) + } + else if (packet_length > 0 && onetouch_enabled() && packet_buffer[PKT_DEST] == _aqconfig_.onetouch_device_id) { + //if (getLogLevel() >= LOG_DEBUG) + // logMessage(LOG_DEBUG, "RS received ONETOUCH packet of type %s length %d\n", get_packet_type(packet_buffer, packet_length), packet_length); + changed = process_onetouch_packet(packet_buffer, packet_length, &_aqualink_data); + caculate_ack_packet(rs_fd, packet_buffer); + } + else if (packet_length > 0 && _aqconfig_.read_all_devices == true) { - if (process_pda_monitor_packet(packet_buffer, packet_length)) - broadcast_aqualinkstate(mgr.active_connections); - - //send_ack(rs_fd, NUL); - send_extended_ack(rs_fd, ACK_PDA, NUL); - }*/ - else if (packet_length > 0 && _config_parameters.read_all_devices == true) - { - //logPacket(packet_buffer, packet_length); - if (packet_buffer[PKT_DEST] == DEV_MASTER && interestedInNextAck == true) { swg_noreply_cnt = 0; @@ -1336,35 +1197,27 @@ void main_loop() else if (packet_buffer[PKT_DEST] == SWG_DEV_ID) { interestedInNextAck = true; - changed = processPacketToSWG(packet_buffer, packet_length, &_aqualink_data, _config_parameters.swg_zero_ignore); + changed = processPacketToSWG(packet_buffer, packet_length, &_aqualink_data, _aqconfig_.swg_zero_ignore); } else { interestedInNextAck = false; } - if (_config_parameters.read_pentair_packets && getProtocolType(packet_buffer) == PENTAIR) { + if (_aqconfig_.read_pentair_packets && getProtocolType(packet_buffer) == PENTAIR) { if (processPentairPacket(packet_buffer, packet_length, &_aqualink_data)) { //broadcast_aqualinkstate(mgr.active_connections); changed = true; } - } - /* Removed, iAqualink has sleep mode, so no use - if (packet_buffer[PKT_DEST] == IAQ_DEV_ID && packet_buffer[PKT_CMD] == CMD_IAQ_MSG) { - if (processiAqualinkMsg(packet_buffer, packet_length, &_aqualink_data) != false) - broadcast_aqualinkstate(mgr.active_connections); - } - */ - //} - - // logMessage(LOG_DEBUG_SERIAL, "Received Packet for ID 0x%02hhx of type %s %s\n",packet_buffer[PKT_DEST], get_packet_type(packet_buffer, packet_length), - // (packet_buffer[PKT_DEST] == _config_parameters.device_id)?" <-- Aqualinkd ID":""); + } } if (changed) broadcast_aqualinkstate(mgr.active_connections); } - mg_mgr_poll(&mgr, 0); + + mg_mgr_poll(&mgr, 10); + tcdrain(rs_fd); // Make sure buffer has been sent. // Any unactioned commands if (_aqualink_data.unactioned.type != NO_ACTION) @@ -1378,15 +1231,12 @@ void main_loop() } } -#ifdef BLOCKING_MODE -#else - tcdrain(rs_fd); // Make sure buffer has been sent. - delay(10); -#endif - //} + + //tcdrain(rs_fd); // Make sure buffer has been sent. + //delay(10); } - //if (_config_parameters.debug_RSProtocol_packets) stopPacketLogger(); + //if (_aqconfig_.debug_RSProtocol_packets) stopPacketLogger(); stopPacketLogger(); // Reset and close the port. close_serial_port(rs_fd); diff --git a/aquapure.c b/aquapure.c index 56fbf7d..0a33619 100644 --- a/aquapure.c +++ b/aquapure.c @@ -17,19 +17,23 @@ bool processPacketToSWG(unsigned char *packet, int packet_length, struct aqualin // Not really sure what to do with this, just ignore 0xff / 255 for the moment. (if statment above) // SWG can get ~10 messages to set to 0 then go back again for some reason, so don't go to 0 until 10 messages are received - if (swg_zero_cnt <= swg_zero_ignore && packet[4] == 0x00 && packet[5] == 0x73) { + if (swg_zero_cnt <= swg_zero_ignore && packet[4] == 0x00) { logMessage(LOG_DEBUG, "Ignoring SWG set to %d due to packet packet count %d <= %d from control panel to SWG 0x%02hhx 0x%02hhx\n", (int)packet[4], swg_zero_cnt, swg_zero_ignore, packet[4], packet[5]); swg_zero_cnt++; - } else if (swg_zero_cnt > swg_zero_ignore && packet[4] == 0x00 && packet[5] == 0x73) { - aqdata->swg_percent = (int)packet[4]; - changedAnything = true; + } else if (swg_zero_cnt > swg_zero_ignore && packet[4] == 0x00) { + if (aqdata->swg_percent != (int)packet[4]) { + aqdata->swg_percent = (int)packet[4]; + changedAnything = true; + } // logMessage(LOG_DEBUG, "SWG set to %d due to packet packet count %d <= %d from control panel to SWG 0x%02hhx 0x%02hhx\n", // (int)packet[4],swg_zero_cnt,SWG_ZERO_IGNORE_COUNT,packet[4],packet[5]); swg_zero_cnt++; } else { swg_zero_cnt = 0; - aqdata->swg_percent = (int)packet[4]; - changedAnything = true; + if (aqdata->swg_percent != (int)packet[4]) { + aqdata->swg_percent = (int)packet[4]; + changedAnything = true; + } // logMessage(LOG_DEBUG, "SWG set to %d due to packet from control panel to SWG 0x%02hhx 0x%02hhx\n", // aqdata.swg_percent,packet[4],packet[5]); } @@ -66,7 +70,7 @@ bool processPacketFromSWG(unsigned char *packet, int packet_length, struct aqual aqledstate get_swg_led_state(struct aqualinkdata *aqdata) { switch (aqdata->ar_swg_status) { - // Level = (0=gray, 1=green, 2=yellow, 3=orange, 4=red) + case SWG_STATUS_ON: return (aqdata->swg_percent > 0?ON:ENABLE); break; diff --git a/config.c b/config.c index 336040b..f6fb53b 100644 --- a/config.c +++ b/config.c @@ -33,19 +33,24 @@ #include #include +#define CONFIG_C #include "config.h" #include "utils.h" #include "aq_serial.h" #define MAXCFGLINE 256 + + char *generate_mqtt_id(char *buf, int len); +pump_detail *getpump(struct aqualinkdata *aqdata, int button); /* * initialize data to default values */ void init_parameters (struct aqconfig * parms) { + //int i; //char *p; parms->serial_port = DEFAULT_SERIALPORT; parms->log_level = DEFAULT_LOG_LEVEL; @@ -53,6 +58,7 @@ void init_parameters (struct aqconfig * parms) parms->web_directory = DEFAULT_WEBROOT; //parms->device_id = strtoul(DEFAULT_DEVICE_ID, &p, 16); parms->device_id = strtoul(DEFAULT_DEVICE_ID, NULL, 16); + parms->onetouch_device_id = 0x00; //sscanf(DEFAULT_DEVICE_ID, "0x%x", &parms->device_id); parms->override_freeze_protect = FALSE; @@ -76,6 +82,7 @@ void init_parameters (struct aqconfig * parms) parms->deamonize = true; parms->log_file = '\0'; parms->pda_mode = false; + parms->onetouch_mode = false; parms->pda_sleep_mode = false; parms->convert_mqtt_temp = true; parms->convert_dz_temp = true; @@ -90,6 +97,7 @@ void init_parameters (struct aqconfig * parms) parms->swg_zero_ignore = DEFAILT_SWG_ZERO_IGNORE_COUNT; parms->display_warnings_web = false; parms->log_raw_RS_bytes = false; + parms->extended_device_id_programming = false; generate_mqtt_id(parms->mqtt_ID, MQTT_ID_LEN); } @@ -219,69 +227,69 @@ void readCfg_OLD (struct aqconfig *config_parameters, struct aqualinkdata *aqdat if ( indx != NULL) { if (strncasecmp (b_ptr, "socket_port", 11) == 0) { - //config_parameters->socket_port = cleanint(indx+1); - config_parameters->socket_port = cleanalloc(indx+1); + //_aqconfig_.socket_port = cleanint(indx+1); + _aqconfig_.socket_port = cleanalloc(indx+1); } else if (strncasecmp (b_ptr, "serial_port", 11) == 0) { - config_parameters->serial_port = cleanalloc(indx+1); + _aqconfig_.serial_port = cleanalloc(indx+1); } else if (strncasecmp (b_ptr, "log_level", 9) == 0) { - config_parameters->log_level = text2elevel(cleanalloc(indx+1)); + _aqconfig_.log_level = text2elevel(cleanalloc(indx+1)); // should fee mem here } else if (strncasecmp (b_ptr, "device_id", 9) == 0) { - config_parameters->device_id = strtoul(cleanalloc(indx+1), NULL, 16); + _aqconfig_.device_id = strtoul(cleanalloc(indx+1), NULL, 16); // should fee mem here } else if (strncasecmp (b_ptr, "web_directory", 13) == 0) { - config_parameters->web_directory = cleanalloc(indx+1); + _aqconfig_.web_directory = cleanalloc(indx+1); } else if (strncasecmp (b_ptr, "log_file", 8) == 0) { - config_parameters->log_file = cleanalloc(indx+1); + _aqconfig_.log_file = cleanalloc(indx+1); } else if (strncasecmp (b_ptr, "mqtt_address", 12) == 0) { - config_parameters->mqtt_server = cleanalloc(indx+1); + _aqconfig_.mqtt_server = cleanalloc(indx+1); } else if (strncasecmp (b_ptr, "mqtt_dz_sub_topic", 17) == 0) { - config_parameters->mqtt_dz_sub_topic = cleanalloc(indx+1); + _aqconfig_.mqtt_dz_sub_topic = cleanalloc(indx+1); } else if (strncasecmp (b_ptr, "mqtt_dz_pub_topic", 17) == 0) { - config_parameters->mqtt_dz_pub_topic = cleanalloc(indx+1); + _aqconfig_.mqtt_dz_pub_topic = cleanalloc(indx+1); } else if (strncasecmp (b_ptr, "mqtt_aq_topic", 13) == 0) { - config_parameters->mqtt_aq_topic = cleanalloc(indx+1); + _aqconfig_.mqtt_aq_topic = cleanalloc(indx+1); } else if (strncasecmp (b_ptr, "mqtt_user", 9) == 0) { - config_parameters->mqtt_user = cleanalloc(indx+1); + _aqconfig_.mqtt_user = cleanalloc(indx+1); } else if (strncasecmp (b_ptr, "mqtt_passwd", 11) == 0) { - config_parameters->mqtt_passwd = cleanalloc(indx+1); + _aqconfig_.mqtt_passwd = cleanalloc(indx+1); } else if (strncasecmp (b_ptr, "air_temp_dzidx", 14) == 0) { - config_parameters->dzidx_air_temp = strtoul(indx+1, NULL, 10); + _aqconfig_.dzidx_air_temp = strtoul(indx+1, NULL, 10); } else if (strncasecmp (b_ptr, "pool_water_temp_dzidx", 21) == 0) { - config_parameters->dzidx_pool_water_temp = strtoul(indx+1, NULL, 10); + _aqconfig_.dzidx_pool_water_temp = strtoul(indx+1, NULL, 10); } else if (strncasecmp (b_ptr, "spa_water_temp_dzidx", 20) == 0) { - config_parameters->dzidx_spa_water_temp = strtoul(indx+1, NULL, 10); + _aqconfig_.dzidx_spa_water_temp = strtoul(indx+1, NULL, 10); } else if (strncasecmp (b_ptr, "light_programming_mode", 21) == 0) { - config_parameters->light_programming_mode = atof(cleanalloc(indx+1)); // should free this + _aqconfig_.light_programming_mode = atof(cleanalloc(indx+1)); // should free this } else if (strncasecmp (b_ptr, "light_programming_initial_on", 28) == 0) { - config_parameters->light_programming_initial_on = strtoul(indx+1, NULL, 10); + _aqconfig_.light_programming_initial_on = strtoul(indx+1, NULL, 10); } else if (strncasecmp (b_ptr, "light_programming_initial_off", 29) == 0) { - config_parameters->light_programming_initial_off = strtoul(indx+1, NULL, 10); + _aqconfig_.light_programming_initial_off = strtoul(indx+1, NULL, 10); } else if (strncasecmp (b_ptr, "light_programming_button", 21) == 0) { - config_parameters->light_programming_button = strtoul(indx+1, NULL, 10) - 1; + _aqconfig_.light_programming_button = strtoul(indx+1, NULL, 10) - 1; } else if (strncasecmp (b_ptr, "SWG_percent_dzidx", 17) == 0) { - config_parameters->dzidx_swg_percent = strtoul(indx+1, NULL, 10); + _aqconfig_.dzidx_swg_percent = strtoul(indx+1, NULL, 10); } else if (strncasecmp (b_ptr, "SWG_PPM_dzidx", 13) == 0) { - config_parameters->dzidx_swg_ppm = strtoul(indx+1, NULL, 10); + _aqconfig_.dzidx_swg_ppm = strtoul(indx+1, NULL, 10); } else if (strncasecmp (b_ptr, "SWG_Status_dzidx", 14) == 0) { - config_parameters->dzidx_swg_status = strtoul(indx+1, NULL, 10); + _aqconfig_.dzidx_swg_status = strtoul(indx+1, NULL, 10); } else if (strncasecmp (b_ptr, "override_freeze_protect", 23) == 0) { - config_parameters->override_freeze_protect = text2bool(indx+1); + _aqconfig_.override_freeze_protect = text2bool(indx+1); } else if (strncasecmp (b_ptr, "pda_mode", 8) == 0) { - config_parameters->pda_mode = text2bool(indx+1); - set_pda_mode(config_parameters->pda_mode); + _aqconfig_.pda_mode = text2bool(indx+1); + set_pda_mode(_aqconfig_.pda_mode); } else if (strncasecmp (b_ptr, "convert_mqtt_temp_to_c", 22) == 0) { - config_parameters->convert_mqtt_temp = text2bool(indx+1); + _aqconfig_.convert_mqtt_temp = text2bool(indx+1); } else if (strncasecmp (b_ptr, "convert_dz_temp_to_c", 21) == 0) { - config_parameters->convert_dz_temp = text2bool(indx+1); + _aqconfig_.convert_dz_temp = text2bool(indx+1); } else if (strncasecmp (b_ptr, "flash_mqtt_buttons", 18) == 0) { - config_parameters->flash_mqtt_buttons = text2bool(indx+1); + _aqconfig_.flash_mqtt_buttons = text2bool(indx+1); } else if (strncasecmp (b_ptr, "report_zero_pool_temp", 21) == 0) { - config_parameters->report_zero_pool_temp = text2bool(indx+1); + _aqconfig_.report_zero_pool_temp = text2bool(indx+1); } else if (strncasecmp (b_ptr, "report_zero_spa_temp", 20) == 0) { - config_parameters->report_zero_spa_temp = text2bool(indx+1); + _aqconfig_.report_zero_spa_temp = text2bool(indx+1); } else if (strncasecmp (b_ptr, "report_zero_pool_temp", 21) == 0) { - config_parameters->report_zero_pool_temp = text2bool(indx+1); + _aqconfig_.report_zero_pool_temp = text2bool(indx+1); } else if (strncasecmp (b_ptr, "button_", 7) == 0) { int num = strtoul(b_ptr+7, NULL, 10) - 1; //logMessage (LOG_DEBUG, "Button %d\n", strtoul(b_ptr+7, NULL, 10)); @@ -310,146 +318,154 @@ void readCfg_OLD (struct aqconfig *config_parameters, struct aqualinkdata *aqdat } */ -bool setConfigValue(struct aqconfig *config_parameters, struct aqualinkdata *aqdata, char *param, char *value) { +bool setConfigValue(struct aqualinkdata *aqdata, char *param, char *value) { bool rtn = false; - static int pi=0; if (strncasecmp(param, "socket_port", 11) == 0) { - config_parameters->socket_port = cleanalloc(value); + _aqconfig_.socket_port = cleanalloc(value); rtn=true; } else if (strncasecmp(param, "serial_port", 11) == 0) { - config_parameters->serial_port = cleanalloc(value); + _aqconfig_.serial_port = cleanalloc(value); rtn=true; } else if (strncasecmp(param, "log_level", 9) == 0) { - config_parameters->log_level = text2elevel(cleanalloc(value)); + _aqconfig_.log_level = text2elevel(cleanalloc(value)); rtn=true; } else if (strncasecmp(param, "device_id", 9) == 0) { - config_parameters->device_id = strtoul(cleanalloc(value), NULL, 16); + _aqconfig_.device_id = strtoul(cleanalloc(value), NULL, 16); + } else if (strncasecmp (param, "extended_device_id_programming", 30) == 0) { + // Has to be before the below. + _aqconfig_.extended_device_id_programming = text2bool(value); + rtn=true; + } else if (strncasecmp(param, "extended_device_id", 9) == 0) { + _aqconfig_.onetouch_device_id = strtoul(cleanalloc(value), NULL, 16); + //_config_parameters.onetouch_device_id != 0x00 rtn=true; } else if (strncasecmp(param, "web_directory", 13) == 0) { - config_parameters->web_directory = cleanalloc(value); + _aqconfig_.web_directory = cleanalloc(value); rtn=true; } else if (strncasecmp(param, "log_file", 8) == 0) { - config_parameters->log_file = cleanalloc(value); + _aqconfig_.log_file = cleanalloc(value); rtn=true; } else if (strncasecmp(param, "mqtt_address", 12) == 0) { - config_parameters->mqtt_server = cleanalloc(value); + _aqconfig_.mqtt_server = cleanalloc(value); rtn=true; } else if (strncasecmp(param, "mqtt_dz_sub_topic", 17) == 0) { - config_parameters->mqtt_dz_sub_topic = cleanalloc(value); + _aqconfig_.mqtt_dz_sub_topic = cleanalloc(value); rtn=true; } else if (strncasecmp(param, "mqtt_dz_pub_topic", 17) == 0) { - config_parameters->mqtt_dz_pub_topic = cleanalloc(value); + _aqconfig_.mqtt_dz_pub_topic = cleanalloc(value); rtn=true; } else if (strncasecmp(param, "mqtt_aq_topic", 13) == 0) { - config_parameters->mqtt_aq_topic = cleanalloc(value); + _aqconfig_.mqtt_aq_topic = cleanalloc(value); rtn=true; } else if (strncasecmp(param, "mqtt_user", 9) == 0) { - config_parameters->mqtt_user = cleanalloc(value); + _aqconfig_.mqtt_user = cleanalloc(value); rtn=true; } else if (strncasecmp(param, "mqtt_passwd", 11) == 0) { - config_parameters->mqtt_passwd = cleanalloc(value); + _aqconfig_.mqtt_passwd = cleanalloc(value); rtn=true; } else if (strncasecmp(param, "air_temp_dzidx", 14) == 0) { - config_parameters->dzidx_air_temp = strtoul(value, NULL, 10); + _aqconfig_.dzidx_air_temp = strtoul(value, NULL, 10); rtn=true; } else if (strncasecmp(param, "pool_water_temp_dzidx", 21) == 0) { - config_parameters->dzidx_pool_water_temp = strtoul(value, NULL, 10); + _aqconfig_.dzidx_pool_water_temp = strtoul(value, NULL, 10); rtn=true; } else if (strncasecmp(param, "spa_water_temp_dzidx", 20) == 0) { - config_parameters->dzidx_spa_water_temp = strtoul(value, NULL, 10); + _aqconfig_.dzidx_spa_water_temp = strtoul(value, NULL, 10); rtn=true; } else if (strncasecmp(param, "light_programming_mode", 21) == 0) { - config_parameters->light_programming_mode = atof(cleanalloc(value)); // should free this + _aqconfig_.light_programming_mode = atof(cleanalloc(value)); // should free this rtn=true; } else if (strncasecmp(param, "light_programming_initial_on", 28) == 0) { - config_parameters->light_programming_initial_on = strtoul(value, NULL, 10); + _aqconfig_.light_programming_initial_on = strtoul(value, NULL, 10); rtn=true; } else if (strncasecmp(param, "light_programming_initial_off", 29) == 0) { - config_parameters->light_programming_initial_off = strtoul(value, NULL, 10); + _aqconfig_.light_programming_initial_off = strtoul(value, NULL, 10); rtn=true; } else if (strncasecmp(param, "light_programming_button_spa", 28) == 0) { - config_parameters->light_programming_button_spa = strtoul(value, NULL, 10) - 1; + _aqconfig_.light_programming_button_spa = strtoul(value, NULL, 10) - 1; rtn=true; } else if (strncasecmp(param, "light_programming_button", 24) == 0 || strncasecmp(param, "light_programming_button_pool", 29) == 0) { - config_parameters->light_programming_button_pool = strtoul(value, NULL, 10) - 1; + _aqconfig_.light_programming_button_pool = strtoul(value, NULL, 10) - 1; rtn=true; } else if (strncasecmp(param, "SWG_percent_dzidx", 17) == 0) { - config_parameters->dzidx_swg_percent = strtoul(value, NULL, 10); + _aqconfig_.dzidx_swg_percent = strtoul(value, NULL, 10); rtn=true; } else if (strncasecmp(param, "SWG_PPM_dzidx", 13) == 0) { - config_parameters->dzidx_swg_ppm = strtoul(value, NULL, 10); + _aqconfig_.dzidx_swg_ppm = strtoul(value, NULL, 10); rtn=true; } else if (strncasecmp(param, "SWG_Status_dzidx", 14) == 0) { - config_parameters->dzidx_swg_status = strtoul(value, NULL, 10); + _aqconfig_.dzidx_swg_status = strtoul(value, NULL, 10); rtn=true; } else if (strncasecmp(param, "override_freeze_protect", 23) == 0) { - config_parameters->override_freeze_protect = text2bool(value); + _aqconfig_.override_freeze_protect = text2bool(value); rtn=true; } else if (strncasecmp(param, "pda_mode", 8) == 0) { - config_parameters->pda_mode = text2bool(value); - set_pda_mode(config_parameters->pda_mode); - //config_parameters->use_PDA_auxiliary = false; + _aqconfig_.pda_mode = text2bool(value); + set_pda_mode(_aqconfig_.pda_mode); + //_aqconfig_.use_PDA_auxiliary = false; rtn=true; } else if (strncasecmp(param, "pda_sleep_mode", 8) == 0) { - config_parameters->pda_sleep_mode = text2bool(value); - //set_pda_mode(config_parameters->pda_mode); + _aqconfig_.pda_sleep_mode = text2bool(value); + //set_pda_mode(_aqconfig_.pda_mode); rtn=true; } else if (strncasecmp(param, "convert_mqtt_temp_to_c", 22) == 0) { - config_parameters->convert_mqtt_temp = text2bool(value); + _aqconfig_.convert_mqtt_temp = text2bool(value); rtn=true; } else if (strncasecmp(param, "convert_dz_temp_to_c", 20) == 0) { - config_parameters->convert_dz_temp = text2bool(value); + _aqconfig_.convert_dz_temp = text2bool(value); rtn=true; /* } else if (strncasecmp(param, "flash_mqtt_buttons", 18) == 0) { - config_parameters->flash_mqtt_buttons = text2bool(value); + _aqconfig_.flash_mqtt_buttons = text2bool(value); rtn=true;*/ } else if (strncasecmp(param, "report_zero_spa_temp", 20) == 0) { - config_parameters->report_zero_spa_temp = text2bool(value); + _aqconfig_.report_zero_spa_temp = text2bool(value); rtn=true; } else if (strncasecmp (param, "report_zero_pool_temp", 21) == 0) { - config_parameters->report_zero_pool_temp = text2bool(value); + _aqconfig_.report_zero_pool_temp = text2bool(value); rtn=true; } else if (strncasecmp (param, "read_all_devices", 16) == 0) { - config_parameters->read_all_devices = text2bool(value); + _aqconfig_.read_all_devices = text2bool(value); rtn=true; } else if (strncasecmp (param, "use_panel_aux_labels", 20) == 0) { - config_parameters->use_panel_aux_labels = text2bool(value); + _aqconfig_.use_panel_aux_labels = text2bool(value); rtn=true; } else if (strncasecmp (param, "force_SWG", 9) == 0) { - config_parameters->force_swg = text2bool(value); + _aqconfig_.force_swg = text2bool(value); rtn=true; } else if (strncasecmp (param, "debug_RSProtocol_packets", 24) == 0) { - config_parameters->debug_RSProtocol_packets = text2bool(value); + _aqconfig_.debug_RSProtocol_packets = text2bool(value); rtn=true; } else if (strncasecmp (param, "read_pentair_packets", 17) == 0) { - config_parameters->read_pentair_packets = text2bool(value); - config_parameters->read_all_devices = true; + _aqconfig_.read_pentair_packets = text2bool(value); + if (_aqconfig_.read_pentair_packets) + _aqconfig_.read_all_devices = true; rtn=true; } else if (strncasecmp (param, "swg_zero_ignore_count", 21) == 0) { - config_parameters->swg_zero_ignore = strtoul(value, NULL, 10); + _aqconfig_.swg_zero_ignore = strtoul(value, NULL, 10); rtn=true; } else if (strncasecmp (param, "display_warnings_in_web", 23) == 0) { - config_parameters->display_warnings_web = text2bool(value); + _aqconfig_.display_warnings_web = text2bool(value); rtn=true; } + /* else if (strncasecmp (param, "use_PDA_auxiliary", 17) == 0) { - config_parameters->use_PDA_auxiliary = text2bool(value); + _aqconfig_.use_PDA_auxiliary = text2bool(value); if ( pda_mode() ) { logMessage(LOG_ERR, "ERROR Can't use `use_PDA_auxiliary` in PDA mode, ignoring'\n"); - config_parameters->use_PDA_auxiliary = false; + _aqconfig_.use_PDA_auxiliary = false; } rtn=true; } */ // removed until domoticz has a better virtual thermostat /*else if (strncasecmp (param, "pool_thermostat_dzidx", 21) == 0) { - config_parameters->dzidx_pool_thermostat = strtoul(value, NULL, 10); + _aqconfig_.dzidx_pool_thermostat = strtoul(value, NULL, 10); rtn=true; } else if (strncasecmp (param, "spa_thermostat_dzidx", 20) == 0) { - config_parameters->dzidx_spa_thermostat = strtoul(value, NULL, 10); + _aqconfig_.dzidx_spa_thermostat = strtoul(value, NULL, 10); rtn=true; } */ else if (strncasecmp(param, "button_", 7) == 0) { @@ -463,30 +479,92 @@ bool setConfigValue(struct aqconfig *config_parameters, struct aqualinkdata *aqd } else if (strncasecmp(param + 9, "_PDA_label", 10) == 0) { aqdata->aqbuttons[num].pda_label = cleanalloc(value); rtn=true; + } else if (strncasecmp(param + 9, "_pumpID", 7) == 0) { + pump_detail *pump = getpump(aqdata, num); + if (pump != NULL) { + pump->pumpID = strtoul(cleanalloc(value), NULL, 16); + if (pump->pumpID < 119) { + pump->ptype = PENTAIR; + } else { + pump->ptype = JANDY; + //pump->pumpType = EPUMP; // For testing let the interface set this + } + } else { + logMessage(LOG_ERR, "Config error, VSP Pumps limited to %d, ignoring %s'\n",MAX_PUMPS-1,param); + } + rtn=true; + } else if (strncasecmp(param + 9, "_pumpIndex", 10) == 0) { //button_01_pumpIndex=1 + pump_detail *pump = getpump(aqdata, num); + if (pump != NULL) { + pump->pumpIndex = strtoul(value, NULL, 10); + } else { + logMessage(LOG_ERR, "Config error, VSP Pumps limited to %d, ignoring %s'\n",MAX_PUMPS-1,param); + } + rtn=true; + } + /* } else if (strncasecmp(param + 9, "_pumpID", 7) == 0) { //aqdata->aqbuttons[num].pda_label = cleanalloc(value); //96 to 111 = Pentair, 120 to 123 = Jandy if (pi < MAX_PUMPS) { aqdata->pumps[pi].button = &aqdata->aqbuttons[num]; aqdata->pumps[pi].pumpID = strtoul(cleanalloc(value), NULL, 16); + aqdata->pumps[pi].pumpIndex = pi+1; //aqdata->pumps[pi].buttonID = num; if (aqdata->pumps[pi].pumpID < 119) aqdata->pumps[pi].ptype = PENTAIR; else aqdata->pumps[pi].ptype = JANDY; pi++; + } else { logMessage(LOG_ERR, "Config error, VSP Pumps limited to %d, ignoring %s'\n",MAX_PUMPS,param); } rtn=true; - } + } else if (strncasecmp(param + 9, "_pumpIndex", 10) == 0) { //button_01_pumpIndex=1 + }*/ } return rtn; } +pump_detail *getpump(struct aqualinkdata *aqdata, int button) +{ + //static int _pumpindex = 0; + //aqdata->num_pumps + int pi; -void readCfg (struct aqconfig *config_parameters, struct aqualinkdata *aqdata, char *cfgFile) + // Does it exist + for (pi=0; pi < aqdata->num_pumps; pi++) { + if (aqdata->pumps[pi].button == &aqdata->aqbuttons[button]) { + //printf ("Found pump %d\n",button); + return &aqdata->pumps[pi]; + } + } + + // Create new entry + if (aqdata->num_pumps < MAX_PUMPS) { + //printf ("Creating pump %d\n",button); + aqdata->pumps[aqdata->num_pumps].button = &aqdata->aqbuttons[button]; + aqdata->pumps[aqdata->num_pumps].pumpType = PT_UNKNOWN; + aqdata->pumps[aqdata->num_pumps].rpm = TEMP_UNKNOWN; + aqdata->pumps[aqdata->num_pumps].watts = TEMP_UNKNOWN; + aqdata->pumps[aqdata->num_pumps].gpm = TEMP_UNKNOWN; + aqdata->num_pumps++; + return &aqdata->pumps[aqdata->num_pumps-1]; + } + + return NULL; +} + + +void init_config() +{ + init_parameters(&_aqconfig_); +} + +//void readCfg (struct aqconfig *config_parameters, struct aqualinkdata *aqdata, char *cfgFile) +void read_config (struct aqualinkdata *aqdata, char *cfgFile) { FILE * fp ; char bufr[MAXCFGLINE]; @@ -496,7 +574,7 @@ void readCfg (struct aqconfig *config_parameters, struct aqualinkdata *aqdata, c //int tokenindex = 0; char *b_ptr; - config_parameters->config_file = cleanalloc(cfgFile); + _aqconfig_.config_file = cleanalloc(cfgFile); if( (fp = fopen(cfgFile, "r")) != NULL){ while(! feof(fp)){ @@ -511,7 +589,7 @@ void readCfg (struct aqconfig *config_parameters, struct aqualinkdata *aqdata, c indx = strchr(b_ptr, '='); if ( indx != NULL) { - if ( ! setConfigValue(config_parameters, aqdata, b_ptr, indx+1)) + if ( ! setConfigValue(aqdata, b_ptr, indx+1)) logMessage(LOG_ERR, "Unknown config parameter '%.*s'\n",strlen(b_ptr)-1, b_ptr); } } @@ -596,67 +674,67 @@ void writeIntValue (FILE *fp, char *msg, int value) fprintf(fp, "%s = %d\n", msg, value); } -bool writeCfg (struct aqconfig *config_parameters, struct aqualinkdata *aqdata) +bool writeCfg (struct aqualinkdata *aqdata) { FILE *fp; int i; bool fs = remount_root_ro(false); - fp = fopen(config_parameters->config_file, "w"); + fp = fopen(_aqconfig_.config_file, "w"); if (fp == NULL) { - logMessage(LOG_ERR, "Open config file failed '%s'\n", config_parameters->config_file); + logMessage(LOG_ERR, "Open config file failed '%s'\n", _aqconfig_.config_file); remount_root_ro(true); //fprintf(stdout, "Open file failed 'sprinkler.cron'\n"); return false; } fprintf(fp, "#***** AqualinkD configuration *****\n"); - fprintf(fp, "socket_port = %s\n", config_parameters->socket_port); - fprintf(fp, "serial_port = %s\n", config_parameters->serial_port); - fprintf(fp, "device_id = 0x%02hhx\n", config_parameters->device_id); - fprintf(fp, "read_all_devices = %s", bool2text(config_parameters->read_all_devices)); - writeCharValue(fp, "log_level", errorlevel2text(config_parameters->log_level)); - writeCharValue(fp, "web_directory", config_parameters->web_directory); - writeCharValue(fp, "log_file", config_parameters->log_file); - fprintf(fp, "pda_mode = %s\n", bool2text(config_parameters->pda_mode)); + fprintf(fp, "socket_port = %s\n", _aqconfig_.socket_port); + fprintf(fp, "serial_port = %s\n", _aqconfig_.serial_port); + fprintf(fp, "device_id = 0x%02hhx\n", _aqconfig_.device_id); + fprintf(fp, "read_all_devices = %s", bool2text(_aqconfig_.read_all_devices)); + writeCharValue(fp, "log_level", errorlevel2text(_aqconfig_.log_level)); + writeCharValue(fp, "web_directory", _aqconfig_.web_directory); + writeCharValue(fp, "log_file", _aqconfig_.log_file); + fprintf(fp, "pda_mode = %s\n", bool2text(_aqconfig_.pda_mode)); fprintf(fp, "\n#** MQTT Configuration **\n"); - writeCharValue(fp, "mqtt_address", config_parameters->mqtt_server); - writeCharValue(fp, "mqtt_dz_sub_topic", config_parameters->mqtt_dz_sub_topic); - writeCharValue(fp, "mqtt_dz_pub_topic", config_parameters->mqtt_dz_pub_topic); - writeCharValue(fp, "mqtt_aq_topic", config_parameters->mqtt_aq_topic); - writeCharValue(fp, "mqtt_user", config_parameters->mqtt_user); - writeCharValue(fp, "mqtt_passwd", config_parameters->mqtt_passwd); + writeCharValue(fp, "mqtt_address", _aqconfig_.mqtt_server); + writeCharValue(fp, "mqtt_dz_sub_topic", _aqconfig_.mqtt_dz_sub_topic); + writeCharValue(fp, "mqtt_dz_pub_topic", _aqconfig_.mqtt_dz_pub_topic); + writeCharValue(fp, "mqtt_aq_topic", _aqconfig_.mqtt_aq_topic); + writeCharValue(fp, "mqtt_user", _aqconfig_.mqtt_user); + writeCharValue(fp, "mqtt_passwd", _aqconfig_.mqtt_passwd); fprintf(fp, "\n#** General **\n"); - fprintf(fp, "convert_mqtt_temp_to_c = %s\n", bool2text(config_parameters->convert_mqtt_temp)); - fprintf(fp, "override_freeze_protect = %s\n", bool2text(config_parameters->override_freeze_protect)); - //fprintf(fp, "flash_mqtt_buttons = %s\n", bool2text(config_parameters->flash_mqtt_buttons)); - fprintf(fp, "report_zero_spa_temp = %s\n", bool2text(config_parameters->report_zero_spa_temp)); - fprintf(fp, "report_zero_pool_temp = %s\n", bool2text(config_parameters->report_zero_pool_temp)); + fprintf(fp, "convert_mqtt_temp_to_c = %s\n", bool2text(_aqconfig_.convert_mqtt_temp)); + fprintf(fp, "override_freeze_protect = %s\n", bool2text(_aqconfig_.override_freeze_protect)); + //fprintf(fp, "flash_mqtt_buttons = %s\n", bool2text(_aqconfig_.flash_mqtt_buttons)); + fprintf(fp, "report_zero_spa_temp = %s\n", bool2text(_aqconfig_.report_zero_spa_temp)); + fprintf(fp, "report_zero_pool_temp = %s\n", bool2text(_aqconfig_.report_zero_pool_temp)); fprintf(fp, "\n#** Programmable light **\n"); - //if (config_parameters->light_programming_button_pool <= 0) { - // fprintf(fp, "#light_programming_button_pool = %d\n", config_parameters->light_programming_button_pool); - // fprintf(fp, "#light_programming_mode = %f\n", config_parameters->light_programming_mode); - // fprintf(fp, "#light_programming_initial_on = %d\n", config_parameters->light_programming_initial_on); - // fprintf(fp, "#light_programming_initial_off = %d\n", config_parameters->light_programming_initial_off); + //if (_aqconfig_.light_programming_button_pool <= 0) { + // fprintf(fp, "#light_programming_button_pool = %d\n", _aqconfig_.light_programming_button_pool); + // fprintf(fp, "#light_programming_mode = %f\n", _aqconfig_.light_programming_mode); + // fprintf(fp, "#light_programming_initial_on = %d\n", _aqconfig_.light_programming_initial_on); + // fprintf(fp, "#light_programming_initial_off = %d\n", _aqconfig_.light_programming_initial_off); //} else { - fprintf(fp, "light_programming_button_pool = %d\n", config_parameters->light_programming_button_pool); - fprintf(fp, "light_programming_button_spa = %d\n", config_parameters->light_programming_button_spa); - fprintf(fp, "light_programming_mode = %f\n", config_parameters->light_programming_mode); - fprintf(fp, "light_programming_initial_on = %d\n", config_parameters->light_programming_initial_on); - fprintf(fp, "light_programming_initial_off = %d\n", config_parameters->light_programming_initial_off); + fprintf(fp, "light_programming_button_pool = %d\n", _aqconfig_.light_programming_button_pool); + fprintf(fp, "light_programming_button_spa = %d\n", _aqconfig_.light_programming_button_spa); + fprintf(fp, "light_programming_mode = %f\n", _aqconfig_.light_programming_mode); + fprintf(fp, "light_programming_initial_on = %d\n", _aqconfig_.light_programming_initial_on); + fprintf(fp, "light_programming_initial_off = %d\n", _aqconfig_.light_programming_initial_off); //} fprintf(fp, "\n#** Domoticz **\n"); - fprintf(fp, "convert_dz_temp_to_c = %s\n", bool2text(config_parameters->convert_dz_temp)); - writeIntValue(fp, "air_temp_dzidx", config_parameters->dzidx_air_temp); - writeIntValue(fp, "pool_water_temp_dzidx", config_parameters->dzidx_pool_water_temp); - writeIntValue(fp, "spa_water_temp_dzidx", config_parameters->dzidx_spa_water_temp); - writeIntValue(fp, "SWG_percent_dzidx", config_parameters->dzidx_swg_percent); - writeIntValue(fp, "SWG_PPM_dzidx", config_parameters->dzidx_swg_ppm); - writeIntValue(fp, "SWG_Status_dzidx", config_parameters->dzidx_swg_status); + fprintf(fp, "convert_dz_temp_to_c = %s\n", bool2text(_aqconfig_.convert_dz_temp)); + writeIntValue(fp, "air_temp_dzidx", _aqconfig_.dzidx_air_temp); + writeIntValue(fp, "pool_water_temp_dzidx", _aqconfig_.dzidx_pool_water_temp); + writeIntValue(fp, "spa_water_temp_dzidx", _aqconfig_.dzidx_spa_water_temp); + writeIntValue(fp, "SWG_percent_dzidx", _aqconfig_.dzidx_swg_percent); + writeIntValue(fp, "SWG_PPM_dzidx", _aqconfig_.dzidx_swg_ppm); + writeIntValue(fp, "SWG_Status_dzidx", _aqconfig_.dzidx_swg_status); fprintf(fp, "\n#** Buttons **\n"); for (i=0; i < TOTAL_BUTTONS; i++) diff --git a/config.h b/config.h index 80b2896..23116cf 100644 --- a/config.h +++ b/config.h @@ -7,7 +7,8 @@ #include "aqualink.h" -#define DEFAULT_LOG_LEVEL 5 +//#define DEFAULT_LOG_LEVEL 10 +#define DEFAULT_LOG_LEVEL LOG_NOTICE #define DEFAULT_WEBPORT "6580" #define DEFAULT_WEBROOT "./" #define DEFAULT_SERIALPORT "/dev/ttyUSB0" @@ -18,7 +19,6 @@ #define DEFAULT_MQTT_SERVER NULL #define DEFAULT_MQTT_USER NULL #define DEFAULT_MQTT_PASSWD NULL -// Set this high, as people are confused about SWG bouncing to zero on some panels, just stop the questions #define DEFAILT_SWG_ZERO_IGNORE_COUNT 20 #define MQTT_ID_LEN 18 // 20 seems to kill mosquitto 1.6 @@ -31,6 +31,8 @@ struct aqconfig char *socket_port; char *web_directory; unsigned char device_id; + unsigned char onetouch_device_id; + bool extended_device_id_programming; bool deamonize; char *log_file; char *mqtt_dz_sub_topic; @@ -53,6 +55,7 @@ struct aqconfig int light_programming_button_spa; bool override_freeze_protect; bool pda_mode; + bool onetouch_mode; bool pda_sleep_mode; bool convert_mqtt_temp; bool convert_dz_temp; @@ -75,13 +78,31 @@ struct aqconfig //char *mqtt_pub_tp_ptr = mqtt_pub_topic[]; }; +#ifndef CONFIG_C +extern struct aqconfig _aqconfig_; +#else +struct aqconfig _aqconfig_; +#endif +/* +#ifndef CONFIG_C +#ifdef AQUALINKD_C +extern struct aqconfig _aqconfig_; +#else +extern const struct aqconfig _aqconfig_; +#endif +#endif +*/ void init_parameters (struct aqconfig * parms); //bool parse_config (struct aqconfig * parms, char *cfgfile); //void readCfg (struct aqconfig *config_parameters, char *cfgFile); -void readCfg (struct aqconfig *config_parameters, struct aqualinkdata *aqualink_data, char *cfgFile); -bool writeCfg (struct aqconfig *config_parameters, struct aqualinkdata *aqdata); -bool setConfigValue(struct aqconfig *config_parameters, struct aqualinkdata *aqdata, char *param, char *value); +//void readCfg (struct aqconfig *config_parameters, struct aqualinkdata *aqualink_data, char *cfgFile); +void read_config(struct aqualinkdata *aqdata, char *cfgFile); +void init_config(); + +bool writeCfg (struct aqualinkdata *aqdata); +bool setConfigValue(struct aqualinkdata *aqdata, char *param, char *value); + char *cleanalloc(char*str); #endif diff --git a/epump.h b/epump.h new file mode 100644 index 0000000..2d2ab2e --- /dev/null +++ b/epump.h @@ -0,0 +1,16 @@ + +/* +Nothing seems to change these in simulator, need real pump to test +0x10|0x02|0x7a|0x44|0x00|0x58|0x1b|0x43|0x10|0x03| + +0x10|0x02|0x7a|0x44|0x00|0x58|0x1b|0x43|0x10|0x03| + +0x10|0x02|0x7a|0x44|0x00|0x58|0x1b|0x43|0x10|0x03| +0x10|0x02|0x7a|0x41|0xcd|0x10|0x03| + +0x10|0x02|0x7a|0x44|0x00|0x58|0x1b|0x43|0x10|0x03| +0x10|0x02|0x7a|0x41|0xcd|0x10|0x03| + +0x10|0x02|0x7a|0x44|0x00|0x58|0x1b|0x43|0x10|0x03| +0x10|0x02|0x7a|0x41|0xcd|0x10|0x03| +*/ \ No newline at end of file diff --git a/json_messages.c b/json_messages.c index 3ebe225..3b96c86 100644 --- a/json_messages.c +++ b/json_messages.c @@ -171,12 +171,15 @@ char *get_aux_information(aqkey *button, struct aqualinkdata *aqdata, char *buff int length = 0; buffer[0] = '\0'; - for (i=0; i < MAX_PUMPS; i++) { + for (i=0; i < aqdata->num_pumps; i++) { if (button == aqdata->pumps[i].button) { - if (aqdata->pumps[i].rpm != TEMP_UNKNOWN || aqdata->pumps[i].gph != TEMP_UNKNOWN || aqdata->pumps[i].watts != TEMP_UNKNOWN) { - length += sprintf(buffer, ",\"Pump_RPM\":\"%d\",\"Pump_GPH\":\"%d\",\"Pump_Watts\":\"%d\"", aqdata->pumps[i].rpm,aqdata->pumps[i].gph,aqdata->pumps[i].watts); + + //if (aqdata->pumps[i].rpm != TEMP_UNKNOWN || aqdata->pumps[i].gpm != TEMP_UNKNOWN || aqdata->pumps[i].watts != TEMP_UNKNOWN) { + length += sprintf(buffer, ",\"Pump_RPM\":\"%d\",\"Pump_GPM\":\"%d\",\"Pump_Watts\":\"%d\",\"Pump_Type\":\"%s\"", + aqdata->pumps[i].rpm,aqdata->pumps[i].gpm,aqdata->pumps[i].watts, + (aqdata->pumps[i].pumpType==VFPUMP?"vfPump":(aqdata->pumps[i].pumpType==VSPUMP?"vsPump":"ePump"))); break; - } + //} } } @@ -231,21 +234,38 @@ int build_device_JSON(struct aqualinkdata *aqdata, int programable_switch1, int LED2int(aqdata->aqbuttons[i].led->state)); } else if ( (programable_switch1 > 0 && programable_switch1 == i) || (programable_switch2 > 0 && programable_switch2 == i)) { + /* length += sprintf(buffer+length, "{\"type\": \"switch_program\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"int_status\": \"%d\" %s},", aqdata->aqbuttons[i].name, aqdata->aqbuttons[i].label, aqdata->aqbuttons[i].led->state==ON?JSON_ON:JSON_OFF, LED2text(aqdata->aqbuttons[i].led->state), LED2int(aqdata->aqbuttons[i].led->state), - get_aux_information(&aqdata->aqbuttons[i], aqdata, aux_info)); + get_aux_information(&aqdata->aqbuttons[i], aqdata, aux_info));*/ + length += sprintf(buffer+length, "{\"type\": \"switch\", \"type_ext\": \"switch_program\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"int_status\": \"%d\"},", + aqdata->aqbuttons[i].name, + aqdata->aqbuttons[i].label, + aqdata->aqbuttons[i].led->state==ON?JSON_ON:JSON_OFF, + LED2text(aqdata->aqbuttons[i].led->state), + LED2int(aqdata->aqbuttons[i].led->state)); } else { - length += sprintf(buffer+length, "{\"type\": \"switch\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"int_status\": \"%d\" %s},", + if ( get_aux_information(&aqdata->aqbuttons[i], aqdata, aux_info)[0] == '\0' ) { + length += sprintf(buffer+length, "{\"type\": \"switch\", \"type_ext\": \"switch\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"int_status\": \"%d\"},", + aqdata->aqbuttons[i].name, + aqdata->aqbuttons[i].label, + aqdata->aqbuttons[i].led->state==ON?JSON_ON:JSON_OFF, + LED2text(aqdata->aqbuttons[i].led->state), + LED2int(aqdata->aqbuttons[i].led->state)); + } else { + length += sprintf(buffer+length, "{\"type\": \"switch\", \"type_ext\": \"switch_vsp\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"int_status\": \"%d\" %s},", aqdata->aqbuttons[i].name, aqdata->aqbuttons[i].label, aqdata->aqbuttons[i].led->state==ON?JSON_ON:JSON_OFF, LED2text(aqdata->aqbuttons[i].led->state), LED2int(aqdata->aqbuttons[i].led->state), - get_aux_information(&aqdata->aqbuttons[i], aqdata, aux_info)); + aux_info); + //get_aux_information(&aqdata->aqbuttons[i], aqdata, aux_info)); + } } } @@ -455,10 +475,12 @@ int build_aqualink_status_JSON(struct aqualinkdata *aqdata, char* buffer, int si //length += sprintf(buffer+length, "}, \"extra\":{" ); length += sprintf(buffer+length, "},"); - for (i=0; i < MAX_PUMPS; i++) { - if (aqdata->pumps[i].rpm != TEMP_UNKNOWN || aqdata->pumps[i].gph != TEMP_UNKNOWN || aqdata->pumps[i].watts != TEMP_UNKNOWN) { - length += sprintf(buffer+length, "\"Pump_%d\":{\"name\":\"%s\",\"id\":\"%s\",\"RPM\":\"%d\",\"GPH\":\"%d\",\"Watts\":\"%d\"},", - i+1,aqdata->pumps[i].button->label,aqdata->pumps[i].button->name,aqdata->pumps[i].rpm,aqdata->pumps[i].gph,aqdata->pumps[i].watts); + // NSF Check below needs to be for VSP Pump (any state), not just known state + for (i=0; i < aqdata->num_pumps; i++) { + if (aqdata->pumps[i].pumpType != PT_UNKNOWN && (aqdata->pumps[i].rpm != TEMP_UNKNOWN || aqdata->pumps[i].gpm != TEMP_UNKNOWN || aqdata->pumps[i].watts != TEMP_UNKNOWN)) { + length += sprintf(buffer+length, "\"Pump_%d\":{\"name\":\"%s\",\"id\":\"%s\",\"RPM\":\"%d\",\"GPM\":\"%d\",\"Watts\":\"%d\",\"Pump_Type\":\"%s\"},", + i+1,aqdata->pumps[i].button->label,aqdata->pumps[i].button->name,aqdata->pumps[i].rpm,aqdata->pumps[i].gpm,aqdata->pumps[i].watts, + (aqdata->pumps[i].pumpType==VFPUMP?"vfPump":(aqdata->pumps[i].pumpType==VSPUMP?"vsPump":"ePump"))); } } diff --git a/json_messages.h b/json_messages.h index 256f5de..e442191 100644 --- a/json_messages.h +++ b/json_messages.h @@ -4,7 +4,7 @@ //FUNCTION PROTOTYPES #define JSON_LABEL_SIZE 300 -#define JSON_STATUS_SIZE 800 +#define JSON_STATUS_SIZE 1024 #define JSON_MQTT_MSG_SIZE 100 #define JSON_ON "on" diff --git a/net_services.c b/net_services.c index 1e6cd09..d2c6d54 100644 --- a/net_services.c +++ b/net_services.c @@ -37,7 +37,7 @@ #include "aquapure.h" -static struct aqconfig *_aqualink_config; +//static struct aqconfig *_aqconfig_; static struct aqualinkdata *_aqualink_data; static char *_web_root; @@ -70,7 +70,13 @@ static void signal_handler(int sig_num) { static int is_websocket(const struct mg_connection *nc) { - return nc->flags & MG_F_IS_WEBSOCKET; + return nc->flags & MG_F_IS_WEBSOCKET && !(nc->flags & MG_F_USER_2); +} +static void set_websocket_RSraw(struct mg_connection *nc) { + nc->flags |= MG_F_USER_2; +} +static int is_websocket_RSraw(const struct mg_connection *nc) { + return nc->flags & MG_F_USER_2; } static int is_mqtt(const struct mg_connection *nc) { return nc->flags & MG_F_USER_1; @@ -153,7 +159,7 @@ void send_domoticz_mqtt_state_msg(struct mg_connection *nc, int idx, int value) char mqtt_msg[JSON_MQTT_MSG_SIZE]; build_mqtt_status_JSON(mqtt_msg ,JSON_MQTT_MSG_SIZE, idx, value, TEMP_UNKNOWN); - send_mqtt(nc, _aqualink_config->mqtt_dz_pub_topic, mqtt_msg); + send_mqtt(nc, _aqconfig_.mqtt_dz_pub_topic, mqtt_msg); } void send_domoticz_mqtt_temp_msg(struct mg_connection *nc, int idx, int value) @@ -162,8 +168,8 @@ void send_domoticz_mqtt_temp_msg(struct mg_connection *nc, int idx, int value) return; char mqtt_msg[JSON_MQTT_MSG_SIZE]; - build_mqtt_status_JSON(mqtt_msg ,JSON_MQTT_MSG_SIZE, idx, 0, (_aqualink_data->temp_units==FAHRENHEIT && _aqualink_config->convert_dz_temp)?roundf(degFtoC(value)):value); - send_mqtt(nc, _aqualink_config->mqtt_dz_pub_topic, mqtt_msg); + build_mqtt_status_JSON(mqtt_msg ,JSON_MQTT_MSG_SIZE, idx, 0, (_aqualink_data->temp_units==FAHRENHEIT && _aqconfig_.convert_dz_temp)?roundf(degFtoC(value)):value); + send_mqtt(nc, _aqconfig_.mqtt_dz_pub_topic, mqtt_msg); } void send_domoticz_mqtt_numeric_msg(struct mg_connection *nc, int idx, int value) { @@ -172,7 +178,7 @@ void send_domoticz_mqtt_numeric_msg(struct mg_connection *nc, int idx, int value char mqtt_msg[JSON_MQTT_MSG_SIZE]; build_mqtt_status_JSON(mqtt_msg ,JSON_MQTT_MSG_SIZE, idx, 0, value); - send_mqtt(nc, _aqualink_config->mqtt_dz_pub_topic, mqtt_msg); + send_mqtt(nc, _aqconfig_.mqtt_dz_pub_topic, mqtt_msg); } void send_domoticz_mqtt_status_message(struct mg_connection *nc, int idx, int value, char *svalue) { if (idx <= 0) @@ -181,17 +187,17 @@ void send_domoticz_mqtt_status_message(struct mg_connection *nc, int idx, int va char mqtt_msg[JSON_MQTT_MSG_SIZE]; build_mqtt_status_message_JSON(mqtt_msg, JSON_MQTT_MSG_SIZE, idx, value, svalue); - send_mqtt(nc, _aqualink_config->mqtt_dz_pub_topic, mqtt_msg); + send_mqtt(nc, _aqconfig_.mqtt_dz_pub_topic, mqtt_msg); } void send_mqtt_state_msg(struct mg_connection *nc, char *dev_name, aqledstate state) { static char mqtt_pub_topic[250]; - sprintf(mqtt_pub_topic, "%s/%s/delay",_aqualink_config->mqtt_aq_topic, dev_name); + sprintf(mqtt_pub_topic, "%s/%s/delay",_aqconfig_.mqtt_aq_topic, dev_name); send_mqtt(nc, mqtt_pub_topic, (state==FLASH?MQTT_ON:MQTT_OFF)); - sprintf(mqtt_pub_topic, "%s/%s",_aqualink_config->mqtt_aq_topic, dev_name); + sprintf(mqtt_pub_topic, "%s/%s",_aqconfig_.mqtt_aq_topic, dev_name); send_mqtt(nc, mqtt_pub_topic, (state==OFF?MQTT_OFF:MQTT_ON)); } @@ -203,8 +209,8 @@ void send_mqtt_aux_msg(struct mg_connection *nc, char *dev_name, char *dev_topic sprintf(msg, "%d", value); - //sprintf(mqtt_pub_topic, "%s/%s%d%s",_aqualink_config->mqtt_aq_topic, root_topic, dev_index, dev_topic); - sprintf(mqtt_pub_topic, "%s/%s%s",_aqualink_config->mqtt_aq_topic, dev_name, dev_topic); + //sprintf(mqtt_pub_topic, "%s/%s%d%s",_aqconfig_.mqtt_aq_topic, root_topic, dev_index, dev_topic); + sprintf(mqtt_pub_topic, "%s/%s%s",_aqconfig_.mqtt_aq_topic, dev_name, dev_topic); send_mqtt(nc, mqtt_pub_topic, msg); } @@ -213,15 +219,15 @@ void send_mqtt_heater_state_msg(struct mg_connection *nc, char *dev_name, aqleds static char mqtt_pub_topic[250]; - sprintf(mqtt_pub_topic, "%s/%s",_aqualink_config->mqtt_aq_topic, dev_name); + sprintf(mqtt_pub_topic, "%s/%s",_aqconfig_.mqtt_aq_topic, dev_name); if (state == ENABLE) { send_mqtt(nc, mqtt_pub_topic, MQTT_OFF); - sprintf(mqtt_pub_topic, "%s/%s%s",_aqualink_config->mqtt_aq_topic, dev_name, ENABELED_SUBT); + sprintf(mqtt_pub_topic, "%s/%s%s",_aqconfig_.mqtt_aq_topic, dev_name, ENABELED_SUBT); send_mqtt(nc, mqtt_pub_topic, MQTT_ON); } else { send_mqtt(nc, mqtt_pub_topic, (state==OFF?MQTT_OFF:MQTT_ON)); - sprintf(mqtt_pub_topic, "%s/%s%s",_aqualink_config->mqtt_aq_topic, dev_name, ENABELED_SUBT); + sprintf(mqtt_pub_topic, "%s/%s%s",_aqconfig_.mqtt_aq_topic, dev_name, ENABELED_SUBT); send_mqtt(nc, mqtt_pub_topic, (state==OFF?MQTT_OFF:MQTT_ON)); } } @@ -231,8 +237,8 @@ void send_mqtt_temp_msg(struct mg_connection *nc, char *dev_name, long value) { static char mqtt_pub_topic[250]; static char degC[10]; - sprintf(degC, "%.2f", (_aqualink_data->temp_units==FAHRENHEIT && _aqualink_config->convert_mqtt_temp)?degFtoC(value):value ); - sprintf(mqtt_pub_topic, "%s/%s", _aqualink_config->mqtt_aq_topic, dev_name); + sprintf(degC, "%.2f", (_aqualink_data->temp_units==FAHRENHEIT && _aqconfig_.convert_mqtt_temp)?degFtoC(value):value ); + sprintf(mqtt_pub_topic, "%s/%s", _aqconfig_.mqtt_aq_topic, dev_name); send_mqtt(nc, mqtt_pub_topic, degC); } /* @@ -241,9 +247,9 @@ void send_mqtt_temp_msg_new(struct mg_connection *nc, char *dev_name, long value static char mqtt_pub_topic[250]; static char degC[5]; // NSF remove false below once we have finished. - sprintf(degC, "%.2f", (false && _aqualink_data->temp_units==FAHRENHEIT && _aqualink_config->convert_mqtt_temp)?degFtoC(value):value ); + sprintf(degC, "%.2f", (false && _aqualink_data->temp_units==FAHRENHEIT && _aqconfig_.convert_mqtt_temp)?degFtoC(value):value ); //sprintf(degC, "%d", value ); - sprintf(mqtt_pub_topic, "%s/%s", _aqualink_config->mqtt_aq_topic, dev_name); + sprintf(mqtt_pub_topic, "%s/%s", _aqconfig_.mqtt_aq_topic, dev_name); send_mqtt(nc, mqtt_pub_topic, degC); } */ @@ -252,8 +258,8 @@ void send_mqtt_setpoint_msg(struct mg_connection *nc, char *dev_name, long value static char mqtt_pub_topic[250]; static char degC[10]; - sprintf(degC, "%.2f", (_aqualink_data->temp_units==FAHRENHEIT && _aqualink_config->convert_mqtt_temp)?degFtoC(value):value ); - sprintf(mqtt_pub_topic, "%s/%s/setpoint", _aqualink_config->mqtt_aq_topic, dev_name); + sprintf(degC, "%.2f", (_aqualink_data->temp_units==FAHRENHEIT && _aqconfig_.convert_mqtt_temp)?degFtoC(value):value ); + sprintf(mqtt_pub_topic, "%s/%s/setpoint", _aqconfig_.mqtt_aq_topic, dev_name); send_mqtt(nc, mqtt_pub_topic, degC); } void send_mqtt_numeric_msg(struct mg_connection *nc, char *dev_name, int value) @@ -262,7 +268,7 @@ void send_mqtt_numeric_msg(struct mg_connection *nc, char *dev_name, int value) static char msg[10]; sprintf(msg, "%d", value); - sprintf(mqtt_pub_topic, "%s/%s", _aqualink_config->mqtt_aq_topic, dev_name); + sprintf(mqtt_pub_topic, "%s/%s", _aqconfig_.mqtt_aq_topic, dev_name); send_mqtt(nc, mqtt_pub_topic, msg); } void send_mqtt_float_msg(struct mg_connection *nc, char *dev_name, float value) { @@ -270,7 +276,7 @@ void send_mqtt_float_msg(struct mg_connection *nc, char *dev_name, float value) static char msg[10]; sprintf(msg, "%.2f", value); - sprintf(mqtt_pub_topic, "%s/%s", _aqualink_config->mqtt_aq_topic, dev_name); + sprintf(mqtt_pub_topic, "%s/%s", _aqconfig_.mqtt_aq_topic, dev_name); send_mqtt(nc, mqtt_pub_topic, msg); } @@ -281,7 +287,7 @@ void send_mqtt_int_msg(struct mg_connection *nc, char *dev_name, int value) { static char msg[10]; sprintf(msg, "%d", value); - sprintf(mqtt_pub_topic, "%s/%s", _aqualink_config->mqtt_aq_topic, dev_name); + sprintf(mqtt_pub_topic, "%s/%s", _aqconfig_.mqtt_aq_topic, dev_name); send_mqtt(nc, mqtt_pub_topic, msg); */ } @@ -289,7 +295,7 @@ void send_mqtt_int_msg(struct mg_connection *nc, char *dev_name, int value) { void send_mqtt_string_msg(struct mg_connection *nc, char *dev_name, char *msg) { static char mqtt_pub_topic[250]; - sprintf(mqtt_pub_topic, "%s/%s", _aqualink_config->mqtt_aq_topic, dev_name); + sprintf(mqtt_pub_topic, "%s/%s", _aqconfig_.mqtt_aq_topic, dev_name); send_mqtt(nc, mqtt_pub_topic, msg); } @@ -321,41 +327,41 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc) _last_mqtt_aqualinkdata.air_temp = _aqualink_data->air_temp; send_mqtt_temp_msg(nc, AIR_TEMP_TOPIC, _aqualink_data->air_temp); //send_mqtt_temp_msg_new(nc, AIR_TEMPERATURE_TOPIC, _aqualink_data->air_temp); - send_domoticz_mqtt_temp_msg(nc, _aqualink_config->dzidx_air_temp, _aqualink_data->air_temp); + send_domoticz_mqtt_temp_msg(nc, _aqconfig_.dzidx_air_temp, _aqualink_data->air_temp); } /* if (_aqualink_data->pool_temp != TEMP_UNKNOWN && _aqualink_data->pool_temp != _last_mqtt_aqualinkdata.pool_temp) { _last_mqtt_aqualinkdata.pool_temp = _aqualink_data->pool_temp; send_mqtt_temp_msg(nc, POOL_TEMP_TOPIC, _aqualink_data->pool_temp); - send_domoticz_mqtt_temp_msg(nc, _aqualink_config->dzidx_pool_water_temp, _aqualink_data->pool_temp); + send_domoticz_mqtt_temp_msg(nc, _aqconfig_.dzidx_pool_water_temp, _aqualink_data->pool_temp); // IF spa is off, report pool water temp to Domoticz. if (_aqualink_data->spa_temp == TEMP_UNKNOWN) - send_domoticz_mqtt_temp_msg(nc, _aqualink_config->dzidx_spa_water_temp, _aqualink_data->pool_temp); + send_domoticz_mqtt_temp_msg(nc, _aqconfig_.dzidx_spa_water_temp, _aqualink_data->pool_temp); } */ if (_aqualink_data->pool_temp != _last_mqtt_aqualinkdata.pool_temp) { - if (_aqualink_data->pool_temp == TEMP_UNKNOWN && _aqualink_config->report_zero_pool_temp) { + if (_aqualink_data->pool_temp == TEMP_UNKNOWN && _aqconfig_.report_zero_pool_temp) { _last_mqtt_aqualinkdata.pool_temp = TEMP_UNKNOWN; - send_mqtt_temp_msg(nc, POOL_TEMP_TOPIC, (_aqualink_config->convert_mqtt_temp?-18:0)); + send_mqtt_temp_msg(nc, POOL_TEMP_TOPIC, (_aqconfig_.convert_mqtt_temp?-18:0)); } else if (_aqualink_data->pool_temp != TEMP_UNKNOWN) { _last_mqtt_aqualinkdata.pool_temp = _aqualink_data->pool_temp; send_mqtt_temp_msg(nc, POOL_TEMP_TOPIC, _aqualink_data->pool_temp); - send_domoticz_mqtt_temp_msg(nc, _aqualink_config->dzidx_pool_water_temp, _aqualink_data->pool_temp); + send_domoticz_mqtt_temp_msg(nc, _aqconfig_.dzidx_pool_water_temp, _aqualink_data->pool_temp); // IF spa is off, report pool water temp to Domoticz. if (_aqualink_data->spa_temp == TEMP_UNKNOWN) - send_domoticz_mqtt_temp_msg(nc, _aqualink_config->dzidx_spa_water_temp, _aqualink_data->pool_temp); + send_domoticz_mqtt_temp_msg(nc, _aqconfig_.dzidx_spa_water_temp, _aqualink_data->pool_temp); } } if (_aqualink_data->spa_temp != _last_mqtt_aqualinkdata.spa_temp) { - if (_aqualink_data->spa_temp == TEMP_UNKNOWN && _aqualink_config->report_zero_spa_temp) { + if (_aqualink_data->spa_temp == TEMP_UNKNOWN && _aqconfig_.report_zero_spa_temp) { _last_mqtt_aqualinkdata.spa_temp = TEMP_UNKNOWN; - send_mqtt_temp_msg(nc, SPA_TEMP_TOPIC, (_aqualink_config->convert_mqtt_temp?-18:0)); + send_mqtt_temp_msg(nc, SPA_TEMP_TOPIC, (_aqconfig_.convert_mqtt_temp?-18:0)); } else if (_aqualink_data->spa_temp != TEMP_UNKNOWN) { _last_mqtt_aqualinkdata.spa_temp = _aqualink_data->spa_temp; send_mqtt_temp_msg(nc, SPA_TEMP_TOPIC, _aqualink_data->spa_temp); - send_domoticz_mqtt_temp_msg(nc, _aqualink_config->dzidx_spa_water_temp, _aqualink_data->spa_temp); + send_domoticz_mqtt_temp_msg(nc, _aqconfig_.dzidx_spa_water_temp, _aqualink_data->spa_temp); } } @@ -363,7 +369,7 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc) _last_mqtt_aqualinkdata.pool_htr_set_point = _aqualink_data->pool_htr_set_point; send_mqtt_setpoint_msg(nc, BTN_POOL_HTR, _aqualink_data->pool_htr_set_point); // removed until domoticz has a better virtuel thermostat - //send_domoticz_mqtt_temp_msg(nc, _aqualink_config->dzidx_pool_thermostat, _aqualink_data->pool_htr_set_point); + //send_domoticz_mqtt_temp_msg(nc, _aqconfig_.dzidx_pool_thermostat, _aqualink_data->pool_htr_set_point); } if (_aqualink_data->spa_htr_set_point != TEMP_UNKNOWN && _aqualink_data->spa_htr_set_point != _last_mqtt_aqualinkdata.spa_htr_set_point) { @@ -384,7 +390,7 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc) send_mqtt_string_msg(nc, FREEZE_PROTECT_ENABELED, MQTT_ON); /* send_mqtt_string_msg(nc, FREEZE_PROTECT_ENABELED, MQTT_ON); - //send_domoticz_mqtt_temp_msg(nc, _aqualink_config->dzidx_rfz_protect, _aqualink_data->frz_protect_set_point); + //send_domoticz_mqtt_temp_msg(nc, _aqconfig_.dzidx_rfz_protect, _aqualink_data->frz_protect_set_point); } else if (_aqualink_data->frz_protect_set_point != _last_mqtt_aqualinkdata.frz_protect_set_point) { _last_mqtt_aqualinkdata.frz_protect_set_point = _aqualink_data->frz_protect_set_point; send_mqtt_string_msg(nc, FREEZE_PROTECT_ENABELED, MQTT_OFF);*/ @@ -411,7 +417,7 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc) if (!_aqualink_data->simulate_panel) sprintf(_aqualink_data->last_display_message, message); - send_domoticz_mqtt_status_message(nc, _aqualink_config->dzidx_swg_status, dzalert, &message[9]); + send_domoticz_mqtt_status_message(nc, _aqconfig_.dzidx_swg_status, dzalert, &message[9]); send_mqtt_int_msg(nc, SWG_TOPIC, status); if (_aqualink_data->ar_swg_status == SWG_STATUS_OFF) @@ -436,13 +442,13 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc) send_mqtt_numeric_msg(nc, SWG_PERCENT_TOPIC, _aqualink_data->swg_percent); send_mqtt_float_msg(nc, SWG_PERCENT_F_TOPIC, roundf(degFtoC(_aqualink_data->swg_percent))); send_mqtt_float_msg(nc, SWG_SETPOINT_TOPIC, roundf(degFtoC(_aqualink_data->swg_percent))); - send_domoticz_mqtt_numeric_msg(nc, _aqualink_config->dzidx_swg_percent, _aqualink_data->swg_percent); + send_domoticz_mqtt_numeric_msg(nc, _aqconfig_.dzidx_swg_percent, _aqualink_data->swg_percent); } if (_aqualink_data->swg_ppm != TEMP_UNKNOWN && ( force_update || _aqualink_data->swg_ppm != _last_mqtt_aqualinkdata.swg_ppm)) { _last_mqtt_aqualinkdata.swg_ppm = _aqualink_data->swg_ppm; send_mqtt_numeric_msg(nc, SWG_PPM_TOPIC, _aqualink_data->swg_ppm); send_mqtt_float_msg(nc, SWG_PPM_F_TOPIC, roundf(degFtoC(_aqualink_data->swg_ppm))); - send_domoticz_mqtt_numeric_msg(nc, _aqualink_config->dzidx_swg_ppm, _aqualink_data->swg_ppm); + send_domoticz_mqtt_numeric_msg(nc, _aqconfig_.dzidx_swg_ppm, _aqualink_data->swg_ppm); } } @@ -453,7 +459,7 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc) // Loop over LED's and send any changes. for (i=0; i < TOTAL_BUTTONS; i++) { /* - if ( _aqualink_data->aqbuttons[i].led->state == FLASH && _aqualink_config->flash_mqtt_buttons == true ) { + if ( _aqualink_data->aqbuttons[i].led->state == FLASH && _aqconfig_.flash_mqtt_buttons == true ) { // Simply send on or off depending on if current second is odd or even. // will send too many off and on messages as we get hit multiple times a second, but most effecient way to handle this // considering flash is not very often and not for long. @@ -475,7 +481,7 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc) } // Loop over Pumps - for (i=0; i < MAX_PUMPS; i++) { + for (i=0; i < _aqualink_data->num_pumps; i++) { //_aqualink_data->pumps[i].rpm = TEMP_UNKNOWN; //_aqualink_data->pumps[i].gph = TEMP_UNKNOWN; //_aqualink_data->pumps[i].watts = TEMP_UNKNOWN; @@ -485,10 +491,10 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc) //send_mqtt_aux_msg(nc, PUMP_TOPIC, i+1, PUMP_RPM_TOPIC, _aqualink_data->pumps[i].rpm); send_mqtt_aux_msg(nc, _aqualink_data->pumps[i].button->name, PUMP_RPM_TOPIC, _aqualink_data->pumps[i].rpm); } - if (_aqualink_data->pumps[i].gph != TEMP_UNKNOWN && _aqualink_data->pumps[i].gph != _last_mqtt_aqualinkdata.pumps[i].gph) { - _last_mqtt_aqualinkdata.pumps[i].gph = _aqualink_data->pumps[i].gph; + if (_aqualink_data->pumps[i].gpm != TEMP_UNKNOWN && _aqualink_data->pumps[i].gpm != _last_mqtt_aqualinkdata.pumps[i].gpm) { + _last_mqtt_aqualinkdata.pumps[i].gpm = _aqualink_data->pumps[i].gpm; //send_mqtt_aux_msg(nc, PUMP_TOPIC, i+1, PUMP_GPH_TOPIC, _aqualink_data->pumps[i].gph); - send_mqtt_aux_msg(nc, _aqualink_data->pumps[i].button->name, PUMP_GPH_TOPIC, _aqualink_data->pumps[i].gph); + send_mqtt_aux_msg(nc, _aqualink_data->pumps[i].button->name, PUMP_GPM_TOPIC, _aqualink_data->pumps[i].gpm); } if (_aqualink_data->pumps[i].watts != TEMP_UNKNOWN && _aqualink_data->pumps[i].watts != _last_mqtt_aqualinkdata.pumps[i].watts) { _last_mqtt_aqualinkdata.pumps[i].watts = _aqualink_data->pumps[i].watts; @@ -507,7 +513,7 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc) //if (_aqualink_data->aqbuttons[i].dz_idx != DZ_NULL_IDX) { if (_aqualink_data->aqbuttons[i].code == KEY_POOL_HTR || _aqualink_data->aqbuttons[i].code == KEY_SPA_HTR) { send_mqtt_heater_state_msg(nc, _aqualink_data->aqbuttons[i].name, _aqualink_data->aqbuttons[i].led->state); - } else if (_aqualink_data->aqbuttons[i].led->state == FLASH && _aqualink_config->flash_mqtt_buttons == true) { + } else if (_aqualink_data->aqbuttons[i].led->state == FLASH && _aqconfig_.flash_mqtt_buttons == true) { // This messed up the origional LED state, which means we send the flash to WEB UI as well. time_t now; now = time(NULL); @@ -553,12 +559,12 @@ int getTempforMeteohub(char *buffer) void set_light_mode(char *value, int button) { - if (_aqualink_config->pda_mode == true) { + if (_aqconfig_.pda_mode == true) { logMessage(LOG_ERR, "Light mode control not supported in PDA mode\n"); return; } - if (_aqualink_config->light_programming_button_pool != button && - _aqualink_config->light_programming_button_spa != button) { + if (_aqconfig_.light_programming_button_pool != button && + _aqconfig_.light_programming_button_spa != button) { logMessage(LOG_ERR, "Light mode control not configured for button %d\n",button); return; } @@ -567,9 +573,9 @@ void set_light_mode(char *value, int button) // 5 below is light index, need to look this up so it's not hard coded. sprintf(buf, "%-5s%-5d%-5d%-5d%.2f",value, button, - _aqualink_config->light_programming_initial_on, - _aqualink_config->light_programming_initial_off, - _aqualink_config->light_programming_mode ); + _aqconfig_.light_programming_initial_on, + _aqconfig_.light_programming_initial_off, + _aqconfig_.light_programming_mode ); //logMessage(LOG_NOTICE, "WEB: requset light mode %s\n", buf); aq_programmer(AQ_SET_COLORMODE, buf, _aqualink_data); } @@ -611,24 +617,20 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg) char value[20]; mg_get_http_var(&http_msg->query_string, "value", value, sizeof(value)); //aq_programmer(AQ_SET_COLORMODE, value, _aqualink_data); - set_light_mode(value, _aqualink_config->light_programming_button_pool); + set_light_mode(value, _aqconfig_.light_programming_button_pool); mg_send_head(nc, 200, strlen(GET_RTN_OK), "Content-Type: text/plain"); mg_send(nc, GET_RTN_OK, strlen(GET_RTN_OK)); } else if (strcmp(command, "spalightmode") == 0) { char value[20]; mg_get_http_var(&http_msg->query_string, "value", value, sizeof(value)); //aq_programmer(AQ_SET_COLORMODE, value, _aqualink_data); - set_light_mode(value, _aqualink_config->light_programming_button_spa); + set_light_mode(value, _aqconfig_.light_programming_button_spa); mg_send_head(nc, 200, strlen(GET_RTN_OK), "Content-Type: text/plain"); mg_send(nc, GET_RTN_OK, strlen(GET_RTN_OK)); } else if (strcmp(command, "diag") == 0) { aq_programmer(AQ_GET_DIAGNOSTICS_MODEL, NULL, _aqualink_data); mg_send_head(nc, 200, strlen(GET_RTN_OK), "Content-Type: text/plain"); mg_send(nc, GET_RTN_OK, strlen(GET_RTN_OK)); - } else if (strcmp(command, "settime") == 0) { - aq_programmer(AQ_SET_TIME, NULL, _aqualink_data); - mg_send_head(nc, 200, strlen(GET_RTN_OK), "Content-Type: text/plain"); - mg_send(nc, GET_RTN_OK, strlen(GET_RTN_OK)); } else if (strcmp(command, "swg_percent") == 0) { char value[20]; mg_get_http_var(&http_msg->query_string, "value", value, sizeof(value)); @@ -659,12 +661,12 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg) mg_send(nc, GET_RTN_OK, strlen(GET_RTN_OK)); } else if (strcmp(command, "devices") == 0) { char message[JSON_LABEL_SIZE*10]; - int size = build_device_JSON(_aqualink_data, _aqualink_config->light_programming_button_pool, _aqualink_config->light_programming_button_spa, message, JSON_LABEL_SIZE*10, false); + int size = build_device_JSON(_aqualink_data, _aqconfig_.light_programming_button_pool, _aqconfig_.light_programming_button_spa, message, JSON_LABEL_SIZE*10, false); mg_send_head(nc, 200, size, "Content-Type: application/json"); mg_send(nc, message, size); } else if (strcmp(command, "homebridge") == 0) { char message[JSON_LABEL_SIZE*10]; - int size = build_device_JSON(_aqualink_data, _aqualink_config->light_programming_button_pool, _aqualink_config->light_programming_button_spa, message, JSON_LABEL_SIZE*10, true); + int size = build_device_JSON(_aqualink_data, _aqconfig_.light_programming_button_pool, _aqconfig_.light_programming_button_spa, message, JSON_LABEL_SIZE*10, true); mg_send_head(nc, 200, size, "Content-Type: application/json"); mg_send(nc, message, size); } else if (strcmp(command, "setconfigprm") == 0) { @@ -674,7 +676,7 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg) mg_get_http_var(&http_msg->query_string, "param", param, sizeof(param)); mg_get_http_var(&http_msg->query_string, "value", value, sizeof(value)); - if (setConfigValue(_aqualink_config, _aqualink_data, param, value)) { + if (setConfigValue(_aqualink_data, param, value)) { webrtn = GET_RTN_OK; logMessage(LOG_INFO, "Web: request to config %s to %s\n", param, value); } else { @@ -684,7 +686,7 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg) mg_send_head(nc, 200, strlen(webrtn), "Content-Type: text/plain"); mg_send(nc, webrtn, strlen(webrtn)); } else if (strcmp(command, "writeconfig") == 0) { - if (writeCfg (_aqualink_config, _aqualink_data)) { + if (writeCfg (_aqualink_data)) { mg_send_head(nc, 200, strlen(GET_RTN_OK), "Content-Type: text/plain"); mg_send(nc, GET_RTN_OK, strlen(GET_RTN_OK)); } else { @@ -704,8 +706,12 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg) stopInlineDebug(); rtn = GET_RTN_OK; } else if (strcmp(value, "serialstart") == 0) { + startInlineSerialDebug(); + logMessage(LOG_DEBUG, "WEB: Started inline debug mode\n"); rtn = GET_RTN_OK; } else if (strcmp(value, "serialstop") == 0) { + logMessage(LOG_DEBUG, "WEB: Stoped inline debug mode\n"); + stopInlineDebug(); rtn = GET_RTN_OK; } else if (strcmp(value, "status") == 0) { snprintf(value,80,"{\"sLevel\":\"%s\", \"iLevel\":%d, \"logReady\":\"%s\"}\n",elevel2text(getLogLevel()),getLogLevel(),islogFileReady()?"true":"false" ); @@ -724,6 +730,28 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg) } mg_send_head(nc, 200, strlen(rtn), "Content-Type: text/plain"); mg_send(nc, rtn, strlen(rtn)); + } else if (strncmp(command, "Pump_", 5) == 0) { + // Set Pump RPM + bool found = false; + int pumpIndex = atoi(command+5); // Check for 0 + char value[10]; + mg_get_http_var(&http_msg->query_string, "value", value, sizeof(value)); + int rpm = atoi(value); + // Check for pumpIndex = 0 (BAD) and check RPM Value + //printf("******** ADD CHECK FOR PUMP & RPM HERE ********\n"); + int pi; + for (pi=0; pi < _aqualink_data->num_pumps; pi++) { + if (_aqualink_data->pumps[pi].pumpIndex == pumpIndex) { + logMessage(LOG_NOTICE, "WEB: request to change pump %d to %d\n",pumpIndex, round(rpm)); + _aqualink_data->unactioned.type = PUMP_RPM; + _aqualink_data->unactioned.value = round(rpm); + _aqualink_data->unactioned.id = pumpIndex; + found=true; + break; + } + } + if(!found) + logMessage(LOG_ERR, "WEB: Didn't find pump %d from command %s\n",pumpIndex,command); } else { int i; for (i = 0; i < TOTAL_BUTTONS; i++) { @@ -736,7 +764,7 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg) // *)&_aqualink_data->aqbuttons[i].code, _aqualink_data); logMessage(LOG_DEBUG, "WEB: Message request '%s' change state to '%s'\n", command, value); - if (_aqualink_config->pda_mode == true) { + if (_aqconfig_.pda_mode == true) { char msg[PTHREAD_ARG]; sprintf(msg, "%-5d%-5d",i, (strcmp(value, "on") == 0)?ON:OFF); //printf("******* '%s' ********\n",msg); @@ -825,8 +853,12 @@ void action_websocket_request(struct mg_connection *nc, struct websocket_message //aq_programmer(AQ_SEND_CMD, (char *)&n, NULL); aq_send_cmd((unsigned char)n); //char message[JSON_LABEL_SIZE*10]; - //build_device_JSON(_aqualink_data, _aqualink_config->light_programming_button, message, JSON_LABEL_SIZE*10); + //build_device_JSON(_aqualink_data, _aqconfig_.light_programming_button, message, JSON_LABEL_SIZE*10); //ws_send(nc, message); + } else if (strcmp(request.first.key, "mode") == 0) { + if (strcmp(request.first.value, "onetouchraw") == 0) { + set_websocket_RSraw(nc); + } } else if (strcmp(request.first.key, "command") == 0) { _aqualink_data->simulate_panel = false; if (strcmp(request.first.value, "GET_AUX_LABELS") == 0) { @@ -835,7 +867,7 @@ void action_websocket_request(struct mg_connection *nc, struct websocket_message ws_send(nc, labels); } else if (strcmp(request.first.value, "GET_DEVICES") == 0) { char message[JSON_LABEL_SIZE*10]; - build_device_JSON(_aqualink_data, _aqualink_config->light_programming_button_pool, _aqualink_config->light_programming_button_spa, message, JSON_LABEL_SIZE*10, false); + build_device_JSON(_aqualink_data, _aqconfig_.light_programming_button_pool, _aqconfig_.light_programming_button_spa, message, JSON_LABEL_SIZE*10, false); ws_send(nc, message); } else if ( strcmp(request.first.value, "simulator") == 0) { _aqualink_data->simulate_panel = true; @@ -857,7 +889,7 @@ void action_websocket_request(struct mg_connection *nc, struct websocket_message if (strcmp(request.first.value, _aqualink_data->aqbuttons[i].name) == 0) { logMessage (LOG_INFO, "WS: button '%s' pressed\n",_aqualink_data->aqbuttons[i].name); // send_command( (unsigned char)_aqualink_data->aqbuttons[i].code); - if (_aqualink_config->pda_mode == false) { + if (_aqconfig_.pda_mode == false) { //aq_programmer(AQ_SEND_CMD, (char *)&_aqualink_data->aqbuttons[i].code, _aqualink_data); aq_send_cmd(_aqualink_data->aqbuttons[i].code); } else { @@ -889,7 +921,7 @@ void action_websocket_request(struct mg_connection *nc, struct websocket_message _aqualink_data->swg_percent = value; // Set the value as if it's already been set, just incase it's 0 as we won't get that message, or will update next time } } else if (strcmp(request.first.value, "POOL_LIGHT_MODE") == 0) { - set_light_mode(request.second.value, _aqualink_config->light_programming_button_pool); + set_light_mode(request.second.value, _aqconfig_.light_programming_button_pool); } else if (strcmp(request.first.value, "LIGHT_MODE") == 0) { int i; for (i = 0; i < TOTAL_BUTTONS; i++) { @@ -900,7 +932,26 @@ void action_websocket_request(struct mg_connection *nc, struct websocket_message } //set_light_mode(request.second.value, 0); } else { - logMessage(LOG_DEBUG, "WS: Unknown parameter %s\n", request.first.value); + int i; + bool found = false; + for (i = 0; i < TOTAL_BUTTONS; i++) { + if (strcmp(request.first.value, _aqualink_data->aqbuttons[i].name) == 0) { + int pi; + logMessage (LOG_INFO, "WS: button parameter request '%s' '%s'\n",_aqualink_data->aqbuttons[i].name, request.second.value); + for (pi=0; pi < _aqualink_data->num_pumps; pi++) { + if (_aqualink_data->pumps[pi].button == &_aqualink_data->aqbuttons[i]) { + logMessage(LOG_NOTICE, "WS: request to change pump %d %s to %s\n",pi, _aqualink_data->aqbuttons[i].name, request.second.value); + _aqualink_data->unactioned.type = PUMP_RPM; + _aqualink_data->unactioned.value = atoi(request.second.value); + _aqualink_data->unactioned.id = _aqualink_data->pumps[pi].pumpIndex; + found=true; + break; + } + } + } + } + if (!found) + logMessage(LOG_DEBUG, "WS: Unknown parameter %s\n", request.first.value); } } } @@ -925,12 +976,12 @@ void action_mqtt_message(struct mg_connection *nc, struct mg_mqtt_message *msg) //printf("Topic %.*s\n",msg->topic.len, msg->topic.p); // get the parts from the topic - char *pt1 = (char *)&msg->topic.p[strlen(_aqualink_config->mqtt_aq_topic)+1]; + char *pt1 = (char *)&msg->topic.p[strlen(_aqconfig_.mqtt_aq_topic)+1]; char *pt2 = NULL; char *pt3 = NULL; //for (i=10; i < msg->topic.len; i++) { - for (i=strlen(_aqualink_config->mqtt_aq_topic)+1; i < msg->topic.len; i++) { + for (i=strlen(_aqconfig_.mqtt_aq_topic)+1; i < msg->topic.len; i++) { if ( msg->topic.p[i] == '/' ) { if (pt2 == NULL) { pt2 = (char *)&msg->topic.p[++i]; @@ -949,8 +1000,11 @@ void action_mqtt_message(struct mg_connection *nc, struct mg_mqtt_message *msg) //aqualinkd/Pool_Heater/set //aqualinkd/SWG/Percent_f/set + //aqualinkd/Filter_Pump/RPM/set // Should add this at some point + //aqualinkd/Pump_1/RPM/set + if (pt3 != NULL && (strncmp(pt2, "setpoint", 8) == 0) && (strncmp(pt3, "set", 3) == 0)) { - int val = _aqualink_data->unactioned.value = (_aqualink_data->temp_units != CELSIUS && _aqualink_config->convert_mqtt_temp) ? round(degCtoF(value)) : round(value); + int val = _aqualink_data->unactioned.value = (_aqualink_data->temp_units != CELSIUS && _aqconfig_.convert_mqtt_temp) ? round(degCtoF(value)) : round(value); if (strncmp(pt1, BTN_POOL_HTR, strlen(BTN_POOL_HTR)) == 0) { _aqualink_data->unactioned.value = setpoint_check(POOL_HTR_SETOINT, val, _aqualink_data); _aqualink_data->unactioned.type = POOL_HTR_SETOINT; @@ -984,6 +1038,28 @@ void action_mqtt_message(struct mg_connection *nc, struct mg_mqtt_message *msg) } else if ((pt3 != NULL && (strncmp(pt1, "SWG", 3) == 0) && (strncmp(pt2, "Boost", 5) == 0) && (strncmp(pt3, "set", 3) == 0))) { _aqualink_data->unactioned.value = round(value); _aqualink_data->unactioned.type = SWG_BOOST; + } else if ((pt3 != NULL && (strncmp(pt1, "Pump_", 5) == 0) && ((strncmp(pt2, "RPM", 3) == 0) || (strncmp(pt2, "GPM", 3) == 0)) && (strncmp(pt3, "set", 3) == 0))) { + // Set Pump RPM + bool found = false; + int pumpIndex = atoi(pt1+5); // Check for 0 + // Check for pumpIndex = 0 (BAD) and check RPM Value + //printf("******** ADD CHECK FOR PUMP & RPM HERE ********\n"); + int pi; + for (pi=0; pi < _aqualink_data->num_pumps; pi++) { + if (_aqualink_data->pumps[pi].pumpIndex == pumpIndex) { + //printf("******** Set pump %d RPM to %d ******\n",pumpIndex, round(value)); + //int val = RPM_check(_aqualink_data->pumps[pi].pumpType, val, _aqualink_data); + //RPM_check( val) + logMessage(LOG_NOTICE, "MQTT: request to change pump %d %s to %d\n",pumpIndex, (strncmp(pt2, "RPM", 3) == 0)?"RPM":"GPM", round(value)); + _aqualink_data->unactioned.type = PUMP_RPM; + _aqualink_data->unactioned.value = round(value); + _aqualink_data->unactioned.id = pumpIndex; + found=true; + break; + } + } + if(!found) + logMessage(LOG_ERR, "MQTT: Didn't find pump %d from message %.*s\n",pumpIndex,msg->topic.len, msg->topic.p); } else if (pt2 != NULL && (strncmp(pt2, "set", 3) == 0) && (strncmp(pt2, "setpoint", 8) != 0)) { // Must be a switch on / off for (i=0; i < TOTAL_BUTTONS; i++) { @@ -1003,7 +1079,7 @@ void action_mqtt_message(struct mg_connection *nc, struct mg_mqtt_message *msg) } else { logMessage(LOG_INFO, "MQTT: received '%s' for '%s', turning '%s'\n", (value==0?"OFF":"ON"), _aqualink_data->aqbuttons[i].name,(value==0?"OFF":"ON")); //aq_programmer(AQ_SEND_CMD, (char *)&_aqualink_data->aqbuttons[i].code, _aqualink_data); - if (_aqualink_config->pda_mode == false) { + if (_aqconfig_.pda_mode == false) { //aq_programmer(AQ_SEND_CMD, (char *)&_aqualink_data->aqbuttons[i].code, _aqualink_data); aq_send_cmd(_aqualink_data->aqbuttons[i].code); } else { @@ -1048,7 +1124,7 @@ void action_domoticz_mqtt_message(struct mg_connection *nc, struct mg_mqtt_messa } else { logMessage(LOG_INFO, "MQTT: DZ: received '%s' for '%s', turning '%s'\n", (nvalue==DZ_OFF?"OFF":"ON"), _aqualink_data->aqbuttons[i].name,(nvalue==DZ_OFF?"OFF":"ON")); //aq_programmer(AQ_SEND_CMD, (char *)&_aqualink_data->aqbuttons[i].code, _aqualink_data); - if (_aqualink_config->pda_mode == false) { + if (_aqconfig_.pda_mode == false) { //aq_programmer(AQ_SEND_CMD, (char *)&_aqualink_data->aqbuttons[i].code, _aqualink_data); aq_send_cmd(_aqualink_data->aqbuttons[i].code); } else { @@ -1062,7 +1138,7 @@ void action_domoticz_mqtt_message(struct mg_connection *nc, struct mg_mqtt_messa } } /* removed until domoticz has a better virtual thermostat - if (idx == _aqualink_config->dzidx_pool_thermostat) { + if (idx == _aqconfig_.dzidx_pool_thermostat) { float degC = atof(svalue); int degF = (int)degCtoF(degC); if (degC > 0.0 && 1 < (degF - _aqualink_data->pool_htr_set_point)) { @@ -1071,7 +1147,7 @@ void action_domoticz_mqtt_message(struct mg_connection *nc, struct mg_mqtt_messa } else { logMessage(LOG_INFO, "MQTT: DZ: received temp setting '%s' for 'pool heater setpoint' matched current setting (or was zero), ignoring!\n", svalue); } - } else if (idx == _aqualink_config->dzidx_spa_thermostat) { + } else if (idx == _aqconfig_.dzidx_spa_thermostat) { float degC = atof(svalue); int degF = (int)degCtoF(degC); if (degC > 0.0 && 1 < (degF - _aqualink_data->spa_htr_set_point)) { @@ -1132,18 +1208,18 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { //char *MQTT_id = "AQUALINK_MQTT_TEST_ID"; struct mg_send_mqtt_handshake_opts opts; memset(&opts, 0, sizeof(opts)); - opts.user_name = _aqualink_config->mqtt_user; - opts.password = _aqualink_config->mqtt_passwd; + opts.user_name = _aqconfig_.mqtt_user; + opts.password = _aqconfig_.mqtt_passwd; opts.keep_alive = 5; opts.flags |= MG_MQTT_CLEAN_SESSION; // NFS Need to readup on this - snprintf(aq_topic, 24, "%s/%s", _aqualink_config->mqtt_aq_topic,MQTT_LWM_TOPIC); + snprintf(aq_topic, 24, "%s/%s", _aqconfig_.mqtt_aq_topic,MQTT_LWM_TOPIC); opts.will_topic = aq_topic; opts.will_message = MQTT_OFF; mg_set_protocol_mqtt(nc); - mg_send_mqtt_handshake_opt(nc, _aqualink_config->mqtt_ID, opts); - logMessage(LOG_INFO, "MQTT: Subscribing mqtt with id of: %s\n", _aqualink_config->mqtt_ID); + mg_send_mqtt_handshake_opt(nc, _aqconfig_.mqtt_ID, opts); + logMessage(LOG_INFO, "MQTT: Subscribing mqtt with id of: %s\n", _aqconfig_.mqtt_ID); //last_control_time = mg_time(); } break; @@ -1160,27 +1236,27 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { _mqtt_exit_flag = true; } - snprintf(aq_topic, 29, "%s/#", _aqualink_config->mqtt_aq_topic); - if (_aqualink_config->mqtt_aq_topic != NULL && _aqualink_config->mqtt_dz_sub_topic != NULL) { + snprintf(aq_topic, 29, "%s/#", _aqconfig_.mqtt_aq_topic); + if (_aqconfig_.mqtt_aq_topic != NULL && _aqconfig_.mqtt_dz_sub_topic != NULL) { topics[0].topic = aq_topic; topics[0].qos = qos; - topics[1].topic = _aqualink_config->mqtt_dz_sub_topic; + topics[1].topic = _aqconfig_.mqtt_dz_sub_topic; topics[1].qos = qos; mg_mqtt_subscribe(nc, topics, 2, 42); logMessage(LOG_INFO, "MQTT: Subscribing to '%s'\n", aq_topic); - logMessage(LOG_INFO, "MQTT: Subscribing to '%s'\n", _aqualink_config->mqtt_dz_sub_topic); + logMessage(LOG_INFO, "MQTT: Subscribing to '%s'\n", _aqconfig_.mqtt_dz_sub_topic); } - else if (_aqualink_config->mqtt_aq_topic != NULL) { + else if (_aqconfig_.mqtt_aq_topic != NULL) { topics[0].topic = aq_topic; topics[0].qos = qos; mg_mqtt_subscribe(nc, topics, 1, 42); logMessage(LOG_INFO, "MQTT: Subscribing to '%s'\n", aq_topic); } - else if (_aqualink_config->mqtt_dz_sub_topic != NULL) { - topics[0].topic = _aqualink_config->mqtt_dz_sub_topic;; + else if (_aqconfig_.mqtt_dz_sub_topic != NULL) { + topics[0].topic = _aqconfig_.mqtt_dz_sub_topic;; topics[0].qos = qos; mg_mqtt_subscribe(nc, topics, 1, 42); - logMessage(LOG_INFO, "MQTT: Subscribing to '%s'\n", _aqualink_config->mqtt_dz_sub_topic); + logMessage(LOG_INFO, "MQTT: Subscribing to '%s'\n", _aqconfig_.mqtt_dz_sub_topic); } } break; @@ -1190,20 +1266,21 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { break; case MG_EV_MQTT_SUBACK: logMessage(LOG_INFO, "MQTT: Subscription(s) acknowledged\n"); - snprintf(aq_topic, 24, "%s/%s", _aqualink_config->mqtt_aq_topic,MQTT_LWM_TOPIC); + snprintf(aq_topic, 24, "%s/%s", _aqconfig_.mqtt_aq_topic,MQTT_LWM_TOPIC); send_mqtt(nc, aq_topic ,MQTT_ON); break; case MG_EV_MQTT_PUBLISH: mqtt_msg = (struct mg_mqtt_message *)ev_data; if (mqtt_msg->message_id != 0) { - logMessage(LOG_INFO, "MQTT: received (msg_id: %d), looks like my own message, ignoring\n", mqtt_msg->message_id); + logMessage(LOG_DEBUG, "MQTT: received (msg_id: %d), looks like my own message, ignoring\n", mqtt_msg->message_id); } // NSF Need to change strlen to a global so it's not executed every time we check a topic - if (_aqualink_config->mqtt_aq_topic != NULL && strncmp(mqtt_msg->topic.p, _aqualink_config->mqtt_aq_topic, strlen(_aqualink_config->mqtt_aq_topic)) == 0) { + if (_aqconfig_.mqtt_aq_topic != NULL && strncmp(mqtt_msg->topic.p, _aqconfig_.mqtt_aq_topic, strlen(_aqconfig_.mqtt_aq_topic)) == 0) + { action_mqtt_message(nc, mqtt_msg); } - if (_aqualink_config->mqtt_dz_sub_topic != NULL && strncmp(mqtt_msg->topic.p, _aqualink_config->mqtt_dz_sub_topic, strlen(_aqualink_config->mqtt_dz_sub_topic)) == 0) { + if (_aqconfig_.mqtt_dz_sub_topic != NULL && strncmp(mqtt_msg->topic.p, _aqconfig_.mqtt_dz_sub_topic, strlen(_aqconfig_.mqtt_dz_sub_topic)) == 0) { action_domoticz_mqtt_message(nc, mqtt_msg); } break; @@ -1224,13 +1301,13 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { } void start_mqtt(struct mg_mgr *mgr) { - logMessage (LOG_NOTICE, "Starting MQTT client to %s\n", _aqualink_config->mqtt_server); - if ( _aqualink_config->mqtt_server == NULL || - ( _aqualink_config->mqtt_aq_topic == NULL && _aqualink_config->mqtt_dz_pub_topic == NULL && _aqualink_config->mqtt_dz_sub_topic == NULL) ) + logMessage (LOG_NOTICE, "Starting MQTT client to %s\n", _aqconfig_.mqtt_server); + if ( _aqconfig_.mqtt_server == NULL || + ( _aqconfig_.mqtt_aq_topic == NULL && _aqconfig_.mqtt_dz_pub_topic == NULL && _aqconfig_.mqtt_dz_sub_topic == NULL) ) return; - if (mg_connect(mgr, _aqualink_config->mqtt_server, ev_handler) == NULL) { - logMessage (LOG_ERR, "Failed to create MQTT listener to %s\n", _aqualink_config->mqtt_server); + if (mg_connect(mgr, _aqconfig_.mqtt_server, ev_handler) == NULL) { + logMessage (LOG_ERR, "Failed to create MQTT listener to %s\n", _aqconfig_.mqtt_server); } else { int i; for (i=0; i < TOTAL_BUTTONS; i++) { @@ -1246,10 +1323,11 @@ void start_mqtt(struct mg_mgr *mgr) { } //bool start_web_server(struct mg_mgr *mgr, struct aqualinkdata *aqdata, char *port, char* web_root) { -bool start_net_services(struct mg_mgr *mgr, struct aqualinkdata *aqdata, struct aqconfig *aqconfig) { +//bool start_net_services(struct mg_mgr *mgr, struct aqualinkdata *aqdata, struct aqconfig *aqconfig) { +bool start_net_services(struct mg_mgr *mgr, struct aqualinkdata *aqdata) { struct mg_connection *nc; _aqualink_data = aqdata; - _aqualink_config = aqconfig; + //_aqconfig_ = aqconfig; signal(SIGTERM, signal_handler); signal(SIGINT, signal_handler); @@ -1257,8 +1335,8 @@ bool start_net_services(struct mg_mgr *mgr, struct aqualinkdata *aqdata, struct setvbuf(stderr, NULL, _IOLBF, 0); mg_mgr_init(mgr, NULL); - logMessage (LOG_NOTICE, "Starting web server on port %s\n", _aqualink_config->socket_port); - nc = mg_bind(mgr, _aqualink_config->socket_port, ev_handler); + logMessage (LOG_NOTICE, "Starting web server on port %s\n", _aqconfig_.socket_port); + nc = mg_bind(mgr, _aqconfig_.socket_port, ev_handler); if (nc == NULL) { logMessage (LOG_ERR, "Failed to create listener\n"); return false; @@ -1266,7 +1344,7 @@ bool start_net_services(struct mg_mgr *mgr, struct aqualinkdata *aqdata, struct // Set up HTTP server parameters mg_set_protocol_http_websocket(nc); - s_http_server_opts.document_root = _aqualink_config->web_directory; // Serve current directory + s_http_server_opts.document_root = _aqconfig_.web_directory; // Serve current directory s_http_server_opts.enable_directory_listing = "yes"; #ifndef MG_DISABLE_MQTT diff --git a/net_services.h b/net_services.h index ea809df..33e84be 100644 --- a/net_services.h +++ b/net_services.h @@ -9,7 +9,8 @@ //void main_server(); //void main_server_TEST(struct aqualinkdata *aqdata, char *s_http_port); //bool start_web_server(struct mg_mgr *mgr, struct aqualinkdata *aqdata, char *port, char* web_root); -bool start_net_services(struct mg_mgr *mgr, struct aqualinkdata *aqdata, struct aqconfig *aqconfig); +//bool start_net_services(struct mg_mgr *mgr, struct aqualinkdata *aqdata, struct aqconfig *aqconfig); +bool start_net_services(struct mg_mgr *mgr, struct aqualinkdata *aqdata); void broadcast_aqualinkstate(struct mg_connection *nc); void broadcast_aqualinkstate_error(struct mg_connection *nc, char *msg); diff --git a/onetouch.c b/onetouch.c new file mode 100644 index 0000000..2bde185 --- /dev/null +++ b/onetouch.c @@ -0,0 +1,689 @@ +#include +#include +#include +#include +#include + +#include "onetouch.h" +#include "onetouch_aq_programmer.h" +#include "aq_serial.h" +#include "utils.h" +#include "aq_serial.h" +#include "packetLogger.h" +#include "aq_programmer.h" +#include "aqualink.h" +//#include "pda_menu.h" + + +int _ot_hlightindex = -1; +int _ot_hlightcharindexstart = -1; +int _ot_hlightcharindexstop = -1; +char _menu[ONETOUCH_LINES][AQ_MSGLEN+1]; +struct ot_macro _macros[3]; + +void set_macro_status(); +void pump_update(struct aqualinkdata *aq_data, int updated); + +void print_onetouch_menu() +{ + int i; + for (i=0; i < ONETOUCH_LINES; i++) { + //printf("PDA Line %d = %s\n",i,_menu[i]); + logMessage(LOG_INFO, "OneTouch Menu Line %d = %s\n",i,_menu[i]); + } + + if (_ot_hlightcharindexstart > -1) { + logMessage(LOG_INFO, "OneTouch Menu highlighted line = %d, '%s' hligh-char(s) '%.*s'\n", + _ot_hlightindex,_menu[_ot_hlightindex], + (_ot_hlightcharindexstart - _ot_hlightcharindexstop + 1), &_menu[_ot_hlightindex][_ot_hlightcharindexstart]); + } else if (_ot_hlightindex > -1) { + logMessage(LOG_INFO, "OneTouch Menu highlighted line = %d = %s\n",_ot_hlightindex,_menu[_ot_hlightindex]); + } +} + +int onetouch_menu_hlightindex() +{ + return _ot_hlightindex; +} + +char *onetouch_menu_hlight() +{ + return onetouch_menu_line(_ot_hlightindex); +} + +char *onetouch_menu_hlightchars(int *len) +{ + *len = _ot_hlightcharindexstart - _ot_hlightcharindexstop + 1; + return &_menu[_ot_hlightindex][_ot_hlightcharindexstart]; +} + +char *onetouch_menu_line(int index) +{ + if (index >= 0 && index < ONETOUCH_LINES) + return _menu[index]; + else + return "-"; // Just return something bad so I can use string comparison with no null check + // return NULL; +} + +// Find exact menu item. +int onetouch_menu_find_index(char *text) +{ + int i; + + for (i = 0; i < ONETOUCH_LINES; i++) { + if (ot_strcmp(onetouch_menu_line(i), text) == 0) + //if (ot_strcmp(onetouch_menu_line(i), text, limit) == 0) + return i; + } + + return -1; +} + + +bool process_onetouch_menu_packet(unsigned char* packet, int length) +{ + bool rtn = true; + signed char first_line; + signed char last_line; + signed char line_shift; + signed char i; + + switch (packet[PKT_CMD]) { + case CMD_PDA_CLEAR: + _ot_hlightindex = -1; + _ot_hlightcharindexstart = -1; + _ot_hlightcharindexstart = -1; + memset(_menu, 0, ONETOUCH_LINES * (AQ_MSGLEN+1)); + break; + case CMD_MSG_LONG: + if (packet[PKT_DATA] < ONETOUCH_LINES) { + memset(_menu[(int)packet[PKT_DATA]], 0, AQ_MSGLEN); + strncpy(_menu[(int)packet[PKT_DATA]], (char*)packet+PKT_DATA+1, AQ_MSGLEN); + _menu[packet[PKT_DATA]][AQ_MSGLEN] = '\0'; + } + //if (getLogLevel() >= LOG_DEBUG){print_onetouch_menu();} + break; + case CMD_PDA_HIGHLIGHT: + // when switching from hlight to hlightchars index 255 is sent to turn off hlight + if (packet[4] <= ONETOUCH_LINES) { + _ot_hlightindex = packet[4]; + _ot_hlightcharindexstart = -1; + _ot_hlightcharindexstart = -1; + } else { + _ot_hlightindex = -1; + _ot_hlightcharindexstart = -1; + _ot_hlightcharindexstart = -1; + } + logMessage(LOG_DEBUG, "OneTouch Menu highlighted line = %d = %s\n",_ot_hlightindex,_menu[_ot_hlightindex]); + //if (getLogLevel() >= LOG_DEBUG){print_onetouch_menu();} + break; + case CMD_PDA_HIGHLIGHTCHARS: + if (packet[4] <= ONETOUCH_LINES) { + _ot_hlightindex = packet[4]; + _ot_hlightcharindexstart = packet[5]; + _ot_hlightcharindexstop = packet[6]; + } else { + _ot_hlightindex = -1; + _ot_hlightcharindexstart = -1; + _ot_hlightcharindexstart = -1; + } + logMessage(LOG_DEBUG, "OneTouch Menu highlighted line = %d, '%s' chars '%.*s'\n", + _ot_hlightindex,_menu[_ot_hlightindex], + (_ot_hlightcharindexstart - _ot_hlightcharindexstop + 1), &_menu[_ot_hlightindex][_ot_hlightcharindexstart]); + //if (getLogLevel() >= LOG_DEBUG){print_onetouch_menu();} + break; + case CMD_PDA_SHIFTLINES: + /// press up from top - shift menu down by 1 + // PDA Shif | HEX: 0x10|0x02|0x62|0x0f|0x01|0x08|0x01|0x8d|0x10|0x03| + // press down from bottom - shift menu up by 1 + // PDA Shif | HEX: 0x10|0x02|0x62|0x0f|0x01|0x08|0xff|0x8b|0x10|0x03| + first_line = (signed char)(packet[4]); + last_line = (signed char)(packet[5]); + line_shift = (signed char)(packet[6]); + logMessage(LOG_DEBUG, "\n"); + if (line_shift < 0) { + for (i = first_line-line_shift; i <= last_line; i++) { + memcpy(_menu[i+line_shift], _menu[i], AQ_MSGLEN+1); + } + } else { + for (i = last_line; i >= first_line+line_shift; i--) { + memcpy(_menu[i], _menu[i-line_shift], AQ_MSGLEN+1); + } + } + //if (getLogLevel() >= LOG_DEBUG){print_onetouch_menu();} + break; + } + + return rtn; +} + +void setUnits_ot(char *str, struct aqualinkdata *aq_data) +{ + // NSF This needs to use setUnits from aqualinkd.c + if (aq_data->temp_units == UNKNOWN) { + if (str[15] == 'F') + aq_data->temp_units = FAHRENHEIT; + else if (str[15] == 'C') + aq_data->temp_units = CELSIUS; + else + aq_data->temp_units = UNKNOWN; + + logMessage(LOG_INFO, "Temp Units set to %d (F=0, C=1, Unknown=2)\n", aq_data->temp_units); + } +} + +bool log_heater_setpoints(struct aqualinkdata *aq_data) +{ + bool rtn = false; + + if (ot_strcmp(_menu[2], "Pool Heat") == 0) + aq_data->pool_htr_set_point = ot_atoi(&_menu[2][10]); + if (ot_strcmp(_menu[3], "Spa Heat") == 0 ) + aq_data->spa_htr_set_point = ot_atoi(&_menu[3][9]); + + setUnits_ot(_menu[2], aq_data); + + logMessage(LOG_DEBUG, "POOL HEATER SETPOINT %d\n",aq_data->pool_htr_set_point); + logMessage(LOG_DEBUG, "SPA HEATER SETPOINT %d\n",aq_data->spa_htr_set_point); + + return rtn; +} + +bool log_panelversion(struct aqualinkdata *aq_data) +{ + char *end; + + // It's already been set + if (strlen(aq_data->version) > 0) { + return false; + } + + strcpy(aq_data->version, trimwhitespace(_menu[4])); + // Trim trailing space + end = aq_data->version + strlen(aq_data->version) - 1; + while(end > aq_data->version && isspace(*end)) end--; + + strcpy(end+2, trimwhitespace(_menu[7])); + // Trim trailing space + end = aq_data->version + strlen(aq_data->version) - 1; + while(end > aq_data->version && isspace(*end)) end--; + + // Write new null terminator + *(end+1) = 0; + + logMessage(LOG_DEBUG, "**** '%s' ****\n",aq_data->version); + + return true; +} + +//Info: OneTouch Menu Line 3 = Temp 38`F +bool log_freeze_setpoints(struct aqualinkdata *aq_data) +{ + bool rtn = false; + + if (ot_strcmp(_menu[3], "Temp") == 0) + aq_data->frz_protect_set_point = ot_atoi(&_menu[3][11]); + + setUnits_ot(_menu[3], aq_data); + + logMessage(LOG_DEBUG, "FREEZE PROTECT SETPOINT %d\n",aq_data->frz_protect_set_point); + + return rtn; +} + +bool log_qeuiptment_status(struct aqualinkdata *aq_data) +{ + bool rtn = false; + + if (ot_strcmp(_menu[2],"Intelliflo VS") == 0 || + ot_strcmp(_menu[2],"Intelliflo VF") == 0 || + ot_strcmp(_menu[2],"Jandy ePUMP") == 0) { + rtn = true; + int rpm = 0; + int watts = 0; + int gpm = 0; + int pump_index = ot_atoi(&_menu[2][14]); + // RPM displays differently depending on 3 or 4 digit rpm. + if (ot_strcmp(_menu[3],"RPM:") == 0){ + rpm = ot_atoi(&_menu[3][10]); + if (ot_strcmp(_menu[4],"Watts:") == 0) { + watts = ot_atoi(&_menu[4][11]); + } + if (ot_strcmp(_menu[5],"GPM:") == 0) { + gpm = ot_atoi(&_menu[5][11]); + } + } else if (ot_strcmp(_menu[3],"*** Priming ***") == 0){ + rpm = PUMP_PRIMING; + } else if (ot_strcmp(_menu[3],"(Offline)") == 0){ + rpm = PUMP_OFFLINE; + } + + logMessage(LOG_DEBUG, "OneTouch Pump %s, Index %d, RPM %d, Watts %d, GPM %d\n",_menu[2],pump_index,rpm,watts,gpm); + + int i; + for (i=0; i < aq_data->num_pumps; i++) { + if (aq_data->pumps[i].pumpIndex == pump_index) { + //printf("**** FOUND PUMP %d at index %d *****\n",pump_index,i); + //aq_data->pumps[i].updated = true; + pump_update(aq_data, i); + aq_data->pumps[i].rpm = rpm; + aq_data->pumps[i].watts = watts; + aq_data->pumps[i].gpm = gpm; + if (aq_data->pumps[i].pumpType == PT_UNKNOWN){ + if (ot_strcmp(_menu[2],"Intelliflo VS") == 0) + aq_data->pumps[i].pumpType = VSPUMP; + else if (ot_strcmp(_menu[2],"Intelliflo VF") == 0) + aq_data->pumps[i].pumpType = VFPUMP; + else if (ot_strcmp(_menu[2],"Jandy ePUMP") == 0) + aq_data->pumps[i].pumpType = EPUMP; + } + //printf ("Set Pump Type to %d\n",aq_data->pumps[i].pumpType); + } + } + + //aqdata->pumps[pumpIndex-1].rpm = atoi((char *) &packet_buffer[13]); + + } else if (true == false) { + /* + Debug: OneTouch Menu Line 0 = Equipment Status + Debug: OneTouch Menu Line 1 = + Debug: OneTouch Menu Line 2 = AQUAPURE 30% + Debug: OneTouch Menu Line 3 = Salt 3800 PPM + Debug: OneTouch Menu Line 4 = Add 2 lbs Salt + */ + } + return rtn; +} + +ot_menu_type get_onetouch_memu_type() +{ + if (ot_strcmp(_menu[11],"SYSTEM") == 0) + return OTM_ONETOUCH; + else if (ot_strcmp(_menu[0],"Jandy AquaLinkRS") == 0) + return OTM_SYSTEM; + else if (ot_strcmp(_menu[0],"EQUIPMENT STATUS") == 0) + return OTM_EQUIPTMENT_STATUS; + else if (ot_strcmp(_menu[0],"Select Speed") == 0) + return OTM_SELECT_SPEED; + else if (ot_strcmp(_menu[0],"Menu") == 0) + return OTM_MENUHELP; + else if (ot_strcmp(_menu[0],"Set Temp") == 0) + return OTM_SET_TEMP; + else if (ot_strcmp(_menu[0],"Set Time") == 0) + return OTM_SET_TIME; + else if (ot_strcmp(_menu[0],"System Setup") == 0) + return OTM_SYSTEM_SETUP; + else if (ot_strcmp(_menu[0],"Freeze Protect") == 0) + return OTM_FREEZE_PROTECT; + else if (ot_strcmp(_menu[0],"Boost Pool") == 0) + return OTM_BOOST; + else if (ot_strcmp(_menu[7],"REV ") == 0) // NSF Need a better check. + return OTM_VERSION; + + return OTM_UNKNOWN; +} + +void pump_update(struct aqualinkdata *aq_data, int updated) { + const int bitmask[MAX_PUMPS] = {1,2,4,8}; + static unsigned char updates = '\0'; + int i; + + if (updated == -1) { + for(i=0; i < MAX_PUMPS; i++) { + if ((updates & bitmask[i]) != bitmask[i]) { + aq_data->pumps[i].rpm = TEMP_UNKNOWN; + aq_data->pumps[i].gpm = TEMP_UNKNOWN; + aq_data->pumps[i].watts = TEMP_UNKNOWN; + } + } + updates = '\0'; + } else if (updated >=0 && updated < MAX_PUMPS) { + updates |= bitmask[updated]; + } +} + +bool new_menu(struct aqualinkdata *aq_data) +{ + static bool initRS = false; + bool rtn = false; + static ot_menu_type last_menu_type = OTM_UNKNOWN; + ot_menu_type menu_type = get_onetouch_memu_type(); + + print_onetouch_menu(); + + switch (menu_type) { + case OTM_ONETOUCH: + set_macro_status(); + break; + case OTM_EQUIPTMENT_STATUS: + if (initRS == false) { + queueGetProgramData(ONETOUCH, aq_data); + initRS = true; + } + rtn = log_qeuiptment_status(aq_data); + // Hit select to get to next menu ASAP. + if ( in_ot_programming_mode(aq_data) == false ) + ot_queue_cmd(KEY_ONET_SELECT); + break; + case OTM_SET_TEMP: + rtn = log_heater_setpoints(aq_data); + break; + case OTM_FREEZE_PROTECT: + rtn = log_freeze_setpoints(aq_data); + break; + case OTM_VERSION: + rtn = log_panelversion(aq_data); + logMessage(LOG_DEBUG, "**** ONETOUCH INIT ****"); + queueGetProgramData(ONETOUCH, aq_data); + //set_aqualink_onetouch_pool_heater_temp() + //aq_programmer(AQ_SET_ONETOUCH_POOL_HEATER_TEMP, "95", aq_data); + //aq_programmer(AQ_SET_ONETOUCH_SPA_HEATER_TEMP, "94", aq_data); + initRS = true; + break; + default: + break; + } + + if (last_menu_type == OTM_EQUIPTMENT_STATUS && menu_type != OTM_EQUIPTMENT_STATUS ) { + // End of equiptment status chain of menus, reset any pump that wasn't listed in menus + pump_update(aq_data, -1); + } + + last_menu_type = menu_type; + + return rtn; +} + +void set_macro_status() +{ + // OneTouch Menu Line 2 = SPA MODE OFF + // OneTouch Menu Line 5 = CLEAN MODE ON + // OneTouch Menu Line 8 = ONETOUCH 3 OFF + if (get_onetouch_memu_type() == OTM_ONETOUCH) { + strncpy(_macros[0].name, _menu[2], 13); + chopwhitespace(_macros[0].name); + _macros[0].ison = (_menu[2][15] == 'N'?true:false); + + strncpy(_macros[1].name, _menu[5], 13); + chopwhitespace(_macros[1].name); + _macros[1].ison = (_menu[5][15] == 'N'?true:false); + + strncpy(_macros[2].name, _menu[8], 13); + chopwhitespace(_macros[2].name); + _macros[2].ison = (_menu[8][15] == 'N'?true:false); + + logMessage(LOG_DEBUG, "Macro #1 '%s' is %s\n",_macros[0].name,_macros[0].ison?"On":"Off"); + logMessage(LOG_DEBUG, "Macro #2 '%s' is %s\n",_macros[1].name,_macros[1].ison?"On":"Off"); + logMessage(LOG_DEBUG, "Macro #3 '%s' is %s\n",_macros[2].name,_macros[2].ison?"On":"Off"); + + } +} + +unsigned char _last_msg_type = 0x00; +unsigned char _last_kick_type = -1; + +int thread_kick_type() +{ + return _last_kick_type; +} + +unsigned char *last_onetouch_packet() +{ + return &_last_msg_type; +} + +bool process_onetouch_packet(unsigned char *packet, int length, struct aqualinkdata *aq_data) +{ + static bool filling_menu = false; + bool rtn = false; + //int i; + //char *msg; + //static unsigned char last_msg_type = 0x00; + //static bool init = false; + + //process_pda_packet(packet, length); + + //logMessage(LOG_DEBUG, "RS Received ONETOUCH 0x%02hhx\n", packet[PKT_CMD]); + //debuglogPacket(packet, length); + + process_onetouch_menu_packet(packet, length); + + // Check for new menu. + // Usually PDA_CLEAR bunch of CMD_MSG_LONG then a CMD_PDA_HIGHLIGHT or CMD_PDA_HIGHLIGHTCHARS + // When we hit page down, just CMD_MSG_LONG then a CMD_PDA_HIGHLIGHT. (not seen CMD_PDA_HIGHLIGHTCHARS yet) + if ( (filling_menu == true && + (//packet[PKT_CMD] == CMD_PDA_HIGHLIGHTCHARS || + packet[PKT_CMD] == CMD_PDA_HIGHLIGHT || + packet[PKT_CMD] == CMD_STATUS) ) + || + ( _last_msg_type == CMD_MSG_LONG && packet[PKT_CMD] == CMD_PDA_HIGHLIGHT ) + ) + { + filling_menu = false; + rtn = new_menu(aq_data); + _last_kick_type = KICKT_MENU; + } else { + _last_kick_type = KICKT_CMD; + } + + if (packet[PKT_CMD] == CMD_PDA_CLEAR) + filling_menu = true; + +/* + //if (_last_msg_type == CMD_MSG_LONG && packet[PKT_CMD] != CMD_MSG_LONG) { + if (_last_msg_type == CMD_MSG_LONG && ( packet[PKT_CMD] != CMD_MSG_LONG && packet[PKT_CMD] != CMD_PDA_HIGHLIGHTCHARS) ) { // CMD_PDA_SHIFTLINES + rtn = new_menu(aq_data); + _last_kick_type = KICKT_MENU; + } else { + _last_kick_type = KICKT_CMD; + } +*/ + _last_msg_type = packet[PKT_CMD]; + + // Receive 0x04 for System menu (before 0x02) + // Receive 0x04 for startup menu (before 0x02) + // Receive 0x08 for Equiptment menu (before 0x02) + + // Receive 0x04 while building menu + + if ( packet[PKT_CMD] == CMD_MSG_LONG) + logMessage(LOG_DEBUG, "RS received ONETOUCH packet of type %s length %d '%.*s'\n", get_packet_type(packet, length), length, AQ_MSGLEN, (char*)packet+PKT_DATA+1); + else + logMessage(LOG_DEBUG, "RS received ONETOUCH packet of type %s length %d\n", get_packet_type(packet, length), length); + + + //debuglogPacket(packet, length); + + //if ( in_ot_programming_mode(aq_data) == true ) + kick_aq_program_thread(aq_data, ONETOUCH); +/* + switch (packet[PKT_CMD]) + { + case CMD_ACK: + //logMessage(LOG_DEBUG, "RS Received ACK length %d.\n", length); + break; + + //case CMD_PDA_HIGHLIGHT: // This doesn't work for end of menu, if menu is complete then get a change line, highlight isn't sent. + //set_macro_status(); + // break; + + case 0x04: + case 0x08: + logMessage(LOG_DEBUG, "RS Received MENU complete\n"); + set_macro_status(); + break; + + + + case CMD_MSG_LONG: + msg = (char *)packet + PKT_DATA + 1; + logMessage(LOG_DEBUG, "RS Received message data 0x%02hhx string '%s'\n",packet[PKT_DATA],msg); + break; + + default: + //logMessage(LOG_DEBUG, "RS Received 0x%02hhx\n", packet[PKT_CMD]); + break; + } +*/ + return rtn; +} + + +// Check s2 exists in s1 +int ot_strcmp(const char *s1, const char *s2) +{ + char *sp1 = (char *)s1; + char *sp2 = (char *)s2; + //int i=0; + // Get rid of all padding + while(isspace(*sp1)) sp1++; + while(isspace(*sp2)) sp2++; + + // Need to write this myself for speed + //logMessage(LOG_DEBUG, "OneTouch compare (reset)%d chars of '%s' to '%s'\n",strlen(sp2),sp1,sp2); + return strncasecmp(sp1, sp2, strlen(sp2)); +} + + + +#define INT_MAX +2147483647 +#define INT_MIN -2147483647 + +// atoi that can have blank start +int ot_atoi(const char* str) +{ + int sign = 1, base = 0, i = 0; + // if whitespaces then ignore. + while (str[i] == ' ') { + i++; + } + + // checking for valid input + while (str[i] >= '0' && str[i] <= '9') { + // handling overflow test case + if (base > INT_MAX / 10 || (base == INT_MAX / 10 && str[i] - '0' > 7)) { + if (sign == 1) + return INT_MAX; + else + return INT_MIN; + } + base = 10 * base + (str[i++] - '0'); + } + return base * sign; +} + +/* +Version something like +Info: OneTouch Menu Line 0 = +Info: OneTouch Menu Line 1 = +Info: OneTouch Menu Line 2 = +Info: OneTouch Menu Line 3 = +Info: OneTouch Menu Line 4 = B0029221 +Info: OneTouch Menu Line 5 = RS-8 Combo +Info: OneTouch Menu Line 6 = +Info: OneTouch Menu Line 7 = REV T.0.1 +Info: OneTouch Menu Line 8 = +Info: OneTouch Menu Line 9 = +Info: OneTouch Menu Line 10 = +Info: OneTouch Menu Line 11 = +*/ +/* +Info: OneTouch Menu Line 0 = Set Temp +Info: OneTouch Menu Line 1 = +Info: OneTouch Menu Line 2 = Pool Heat 90`F +Info: OneTouch Menu Line 3 = Spa Heat 102`F +Info: OneTouch Menu Line 4 = +Info: OneTouch Menu Line 5 = Maintain OFF +Info: OneTouch Menu Line 6 = Hours 12AM-12AM +Info: OneTouch Menu Line 7 = +Info: OneTouch Menu Line 8 = Highlight an +Info: OneTouch Menu Line 9 = item and press +Info: OneTouch Menu Line 10 = Select +Info: OneTouch Menu Line 11 = +*/ +/* +nfo: OneTouch Menu Line 0 = Freeze Protect +Info: OneTouch Menu Line 1 = +Info: OneTouch Menu Line 2 = +Info: OneTouch Menu Line 3 = Temp 38`F +Info: OneTouch Menu Line 4 = +Info: OneTouch Menu Line 5 = +Info: OneTouch Menu Line 6 = Use Arrow Keys +Info: OneTouch Menu Line 7 = to set value. +Info: OneTouch Menu Line 8 = Press SELECT +Info: OneTouch Menu Line 9 = to continue. +Info: OneTouch Menu Line 10 = +Info: OneTouch Menu Line 11 = +*/ + +/* +Pump Stuff Use Intelliflo|Jandy & last number of line. +(Intelliflo VF you set GPM, not RPM) + +Debug: OneTouch Menu Line 0 = Equipment Status +Debug: OneTouch Menu Line 1 = +Debug: OneTouch Menu Line 2 = Intelliflo VS 3 +Debug: OneTouch Menu Line 3 = *** Priming *** +Debug: OneTouch Menu Line 4 = Watts: 100 +Debug: OneTouch Menu Line 5 = +Debug: OneTouch Menu Line 6 = +Debug: OneTouch Menu Line 7 = +Debug: OneTouch Menu Line 8 = +Debug: OneTouch Menu Line 9 = +Debug: OneTouch Menu Line 10 = +Debug: OneTouch Menu Line 11 = + +Debug: OneTouch Menu Line 0 = Equipment Status +Debug: OneTouch Menu Line 1 = +Debug: OneTouch Menu Line 2 = Intelliflo VS 3 +Debug: OneTouch Menu Line 3 = RPM: 2750 +Debug: OneTouch Menu Line 3 = RPM: 600 // Option for 3 digit RPM +Debug: OneTouch Menu Line 4 = Watts: 55 +Debug: OneTouch Menu Line 5 = +Debug: OneTouch Menu Line 6 = +Debug: OneTouch Menu Line 7 = +Debug: OneTouch Menu Line 8 = +Debug: OneTouch Menu Line 9 = +Debug: OneTouch Menu Line 10 = +Debug: OneTouch Menu Line 11 = + +Debug: OneTouch Menu Line 0 = Equipment Status +Debug: OneTouch Menu Line 1 = +Debug: OneTouch Menu Line 2 = Intelliflo VF 2 +Debug: OneTouch Menu Line 3 = (Offline) +Debug: OneTouch Menu Line 4 = +Debug: OneTouch Menu Line 5 = +Debug: OneTouch Menu Line 6 = +Debug: OneTouch Menu Line 7 = +Debug: OneTouch Menu Line 8 = +Debug: OneTouch Menu Line 9 = +Debug: OneTouch Menu Line 10 = +Debug: OneTouch Menu Line 11 = + +Debug: OneTouch Menu Line 0 = Equipment Status +Debug: OneTouch Menu Line 1 = +Debug: OneTouch Menu Line 2 = Intelliflo VF 2 +Debug: OneTouch Menu Line 3 = RPM: 2250 +Debug: OneTouch Menu Line 4 = Watts: 55 +Debug: OneTouch Menu Line 5 = GPM: 80 +Debug: OneTouch Menu Line 6 = +Debug: OneTouch Menu Line 7 = +Debug: OneTouch Menu Line 8 = +Debug: OneTouch Menu Line 9 = +Debug: OneTouch Menu Line 10 = +Debug: OneTouch Menu Line 11 = + +Debug: OneTouch Menu Line 0 = Equipment Status +Debug: OneTouch Menu Line 1 = +Debug: OneTouch Menu Line 2 = Jandy ePUMP 1 +Debug: OneTouch Menu Line 3 = RPM: 1750 +Debug: OneTouch Menu Line 4 = Watts: 43 +Debug: OneTouch Menu Line 5 = +Debug: OneTouch Menu Line 6 = +Debug: OneTouch Menu Line 7 = +Debug: OneTouch Menu Line 8 = +Debug: OneTouch Menu Line 9 = +Debug: OneTouch Menu Line 10 = +Debug: OneTouch Menu Line 11 = +*/ diff --git a/onetouch.h b/onetouch.h new file mode 100644 index 0000000..97d0fa8 --- /dev/null +++ b/onetouch.h @@ -0,0 +1,80 @@ + +#ifndef ONETOUCH_H_ +#define ONETOUCH_H_ + +#include "aq_serial.h" +#include "aqualink.h" + +#define ONETOUCH_LINES 12 + +#define PUMP_PRIMING -1 +#define PUMP_OFFLINE -2 + +#define KICKT_CMD 0 +#define KICKT_MENU 1 + +typedef enum ot_menu_type { + OTM_ONETOUCH, + OTM_EQUIPTMENT_STATUS, + OTM_SYSTEM, // Default screen with date,time & temperature + OTM_MAIN, + OTM_EQUIPTMENT, + OTM_EQUIPTMENT_ONOFF, + OTM_SELECT_SPEED, + OTM_MENUHELP, + OTM_VERSION, + OTM_SET_TEMP, + OTM_SET_TIME, + OTM_SYSTEM_SETUP, + OTM_FREEZE_PROTECT, + OTM_SET_AQUAPURE, + OTM_BOOST, + OTM_UNKNOWN +} ot_menu_type; + +struct ot_macro { + char name[AQ_MSGLEN]; + bool ison; +}; + +/* + +Left Top Button +Ack | HEX: 0x10|0x02|0x00|0x01|0x80|0x03|0x96|0x10|0x03| + +Left Middle Button (Back) +Ack | HEX: 0x10|0x02|0x00|0x01|0x80|0x02|0x95|0x10|0x03| + +Left Botom Button +Ack | HEX: 0x10|0x02|0x00|0x01|0x80|0x01|0x94|0x10|0x03| + +Select Button +Ack | HEX: 0x10|0x02|0x00|0x01|0x80|0x04|0x97|0x10|0x03| + +Up Button +Ack | HEX: 0x10|0x02|0x00|0x01|0x80|0x06|0x99|0x10|0x03| + +Down Button +Ack | HEX: 0x10|0x02|0x00|0x01|0x80|0x05|0x98|0x10|0x03| + + +*/ + +bool process_onetouch_packet(unsigned char *packet, int length,struct aqualinkdata *aq_data); +ot_menu_type get_onetouch_memu_type(); +unsigned char *last_onetouch_packet(); +int thread_kick_type(); + +int onetouch_menu_hlightindex(); +char *onetouch_menu_hlight(); +char *onetouch_menu_line(int index); +char *onetouch_menu_hlightchars(int *len); +int onetouch_menu_find_index(char *text); +int ot_atoi(const char* str); +int ot_strcmp(const char *s1, const char *s2); + + + + + +#endif // ONETOUCH_H_ \ No newline at end of file diff --git a/onetouch_aq_programmer.c b/onetouch_aq_programmer.c new file mode 100644 index 0000000..36f7cae --- /dev/null +++ b/onetouch_aq_programmer.c @@ -0,0 +1,803 @@ + +#include +#include +#include + +#include "utils.h" +#include "aq_programmer.h" +#include "onetouch.h" +#include "aqualink.h" + +unsigned char _ot_pgm_command = NUL; + +// External command +bool ot_queue_cmd(unsigned char cmd) { + + if (_ot_pgm_command == NUL) { + _ot_pgm_command = cmd; + return true; + } + + return false; +} + +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; + } + + logMessage(LOG_DEBUG, "OneTouch Sending '0x%02hhx' to controller\n", cmd); + return cmd; +} + +void waitfor_ot_queue2empty() +{ + int i=0; + + while ( (_ot_pgm_command != NUL) && ( i++ < 20) ) { + delay(50); + } + + if (_ot_pgm_command != NUL) { + // Wait for longer interval + while ( (_ot_pgm_command != NUL) && ( i++ < 100) ) { + delay(100); + } + } + + if (_ot_pgm_command != NUL) { + logMessage(LOG_WARNING, "OneTouch Send command Queue did not empty, timeout\n"); + } +} + +// This is for internal use only. +// Will only work in programming mode +void send_ot_cmd(unsigned char cmd) +{ + waitfor_ot_queue2empty(); + + ot_queue_cmd(cmd); + + logMessage(LOG_INFO, "OneTouch Queue send '0x%02hhx' to controller (programming)\n", _ot_pgm_command); +} + +bool waitForOT_MessageTypes(struct aqualinkdata *aq_data, unsigned char mtype1, unsigned char mtype2, int numMessageReceived) +{ + //logMessage(LOG_DEBUG, "waitForOT_MessageType 0x%02hhx || 0x%02hhx\n",mtype1,mtype2); + + int i=0; + pthread_mutex_lock(&aq_data->active_thread.thread_mutex); + + while( ++i <= numMessageReceived) + { + logMessage(LOG_DEBUG, "waitForOT_MessageType 0x%02hhx||0x%02hhx, last message type was 0x%02hhx (%d of %d)\n",mtype1,mtype2,*last_onetouch_packet(),i,numMessageReceived); + + if (*last_onetouch_packet() == mtype1 || *last_onetouch_packet() == mtype2) break; + + pthread_cond_wait(&aq_data->active_thread.thread_cond, &aq_data->active_thread.thread_mutex); + } + + pthread_mutex_unlock(&aq_data->active_thread.thread_mutex); + + if (*last_onetouch_packet() != mtype1 && *last_onetouch_packet() != mtype2) { + //logMessage(LOG_ERR, "Could not select MENU of Aqualink control panel\n"); + logMessage(LOG_DEBUG, "waitForOT_MessageType: did not receive 0x%02hhx||0x%02hhx\n",mtype1,mtype2); + return false; + } else + logMessage(LOG_DEBUG, "waitForOT_MessageType: received 0x%02hhx\n",*last_onetouch_packet()); + + return true; +} + +bool waitForNextOT_Menu(struct aqualinkdata *aq_data) { + //waitForOT_MessageTypes(aq_data,CMD_PDA_CLEAR,CMD_PDA_0x04,10); + //return waitForOT_MessageTypes(aq_data,CMD_PDA_HIGHLIGHT,CMD_PDA_HIGHLIGHTCHARS,15); + + int i=0; + const int numMessageReceived = 20; + + pthread_mutex_lock(&aq_data->active_thread.thread_mutex); + + while( ++i <= 20) + { + logMessage(LOG_DEBUG, "waitForNextOT_Menu (%d of %d)\n",i,numMessageReceived); + + //if(thread_kick_type() == KICKT_MENU) break; + + pthread_cond_wait(&aq_data->active_thread.thread_cond, &aq_data->active_thread.thread_mutex); + if(thread_kick_type() == KICKT_MENU) break; + } + + pthread_mutex_unlock(&aq_data->active_thread.thread_mutex); + + if(thread_kick_type() == KICKT_MENU) + return true; + else + return false; +} + +bool highlight_onetouch_menu_item(struct aqualinkdata *aq_data, char *item) +{ + int i; + int index; + int cnt; + // Should probably to an UP as well as DOWN here. + // Also need page up and down " ^^ More vv" + if ( (index = onetouch_menu_find_index(item)) != -1) { + cnt = index - onetouch_menu_hlightindex(); + logMessage(LOG_DEBUG, "*** OneTouch menu caculator selected=%d, wanted=%d, move=%d times ***\n",onetouch_menu_hlightindex(),index,cnt); + for (i=0; i < cnt; i ++) { + send_ot_cmd(KEY_ONET_DOWN); + waitfor_ot_queue2empty(); + waitForOT_MessageTypes(aq_data,CMD_PDA_HIGHLIGHT,CMD_PDA_HIGHLIGHTCHARS,3); + if (ot_strcmp(onetouch_menu_hlight(), item) == 0) { + // We got here early, probably because blank menu item + break; + } + } + // check if it's quicker to go up rather than down, if second page can't go up. + // This doesn;t work yet, not all versions of control panels have the same amount of items. + /* + if (cnt <= 5 || ot_strcmp(onetouch_menu_line(10), " ^^ More", 10) == 0 ) { + logMessage(LOG_DEBUG, "OneTouch device programmer pressing down %d times\n",cnt); + for (i=0; i < cnt; i ++) { + send_ot_cmd(KEY_ONET_DOWN); + //waitfor_ot_queue2empty(); + //waitForOT_MessageTypes(aq_data,CMD_PDA_HIGHLIGHT,CMD_PDA_HIGHLIGHTCHARS,15); + } + } else { + cnt = 11 - cnt; + logMessage(LOG_DEBUG, "OneTouch device programmer pressing up %d times\n",cnt); + for (i=0; i < cnt; i ++) { + send_ot_cmd(KEY_ONET_UP); + //waitfor_ot_queue2empty(); + //waitForOT_MessageTypes(aq_data,CMD_PDA_HIGHLIGHT,CMD_PDA_HIGHLIGHTCHARS,15); + } + }*/ + // Not much quicker doing it this way that in the for loops above, may have to change back. + //waitfor_ot_queue2empty(); + //waitForOT_MessageTypes(aq_data,CMD_PDA_HIGHLIGHT,CMD_PDA_HIGHLIGHTCHARS,15); + } else { + // Is their another page to search + if (ot_strcmp(onetouch_menu_line(10), "^^ More") == 0) { + char first_item[AQ_MSGLEN+1]; + do { + send_ot_cmd(KEY_ONET_PAGE_DN); + waitForNextOT_Menu(aq_data); + if (onetouch_menu_find_index(item) != -1) { + return highlight_onetouch_menu_item(aq_data, item); + } + } while (strncpy(first_item, onetouch_menu_line(1), AQ_MSGLEN)); + // Need to get menu item 1. + // Hit page down until menu item matches 1. + } else + logMessage(LOG_ERR, "OneTouch device programmer menu item '%s' does not exist\n",item); + //print_onetouch_menu(); + } + + if ( ot_strcmp(onetouch_menu_hlight(), item) != 0) { + logMessage(LOG_ERR, "OneTouch device programmer menu item '%s' not selected\n",item); + //print_onetouch_menu(); + return false; + } else { + return true; + } +} + +bool select_onetouch_menu_item(struct aqualinkdata *aq_data, char *item) +{ + if (highlight_onetouch_menu_item(aq_data, item)) { + send_ot_cmd(KEY_ONET_SELECT); + waitForNextOT_Menu(aq_data); + return true; + } + return false; +} + + +bool goto_onetouch_system_menu(struct aqualinkdata *aq_data) +{ + int i=0; + + if (get_onetouch_memu_type() == OTM_SYSTEM) + return true; + + // Get back to a known point, the system menu + while (get_onetouch_memu_type() != OTM_SYSTEM && get_onetouch_memu_type() != OTM_ONETOUCH && i < 5 ) { + send_ot_cmd(KEY_ONET_BACK); + waitForNextOT_Menu(aq_data); + i++; + } + + if (get_onetouch_memu_type() == OTM_SYSTEM) { + //printf("*** SYSTEM MENU ***\n"); + //return false; + } else if (get_onetouch_memu_type() == OTM_ONETOUCH) { + //printf("*** ONE TOUCH MENU ***\n"); + // Can only be one of 2 options in this menu, so if it's not the one we want, hit down first + if ( ot_strcmp(onetouch_menu_hlight(), "System") != 0) { + send_ot_cmd(KEY_ONET_DOWN); + waitForOT_MessageTypes(aq_data,CMD_PDA_HIGHLIGHT,CMD_PDA_HIGHLIGHTCHARS,15); + } + send_ot_cmd(KEY_ONET_SELECT); + waitfor_ot_queue2empty(); + waitForNextOT_Menu(aq_data); + //return false; + } else { + logMessage(LOG_ERR, "OneTouch device programmer couldn't get to System menu\n"); + return false; + } + + if (get_onetouch_memu_type() != OTM_SYSTEM) { + logMessage(LOG_ERR, "OneTouch device programmer couldn't get to System menu\n"); + return false; + } + + + + return true; +} + +bool goto_onetouch_menu(struct aqualinkdata *aq_data, ot_menu_type menu) +{ + char *second_menu = false; + char *third_menu = false; + + logMessage(LOG_DEBUG, "OneTouch device programmer request for menu %d\n",menu); + + if ( ! goto_onetouch_system_menu(aq_data) ) { + logMessage(LOG_ERR, "OneTouch device programmer failed to get system menu\n"); + return false; + } + + // Now we are at main menu, we should have 3 options + /* Debug: OneTouch Menu Line 9 = Equipment ON/OFF + Debug: OneTouch Menu Line 10 = OneTouch ON/OFF + Debug: OneTouch Menu Line 11 = Menu / Help + */ + + // First setup any secondary menu's to hit. + switch(menu){ + case OTM_SET_TEMP: + second_menu = "Set Temp"; + break; + case OTM_SET_TIME: + second_menu = "Set Time"; + break; + case OTM_SET_AQUAPURE: + second_menu = "Set AQUAPURE"; + break; + case OTM_FREEZE_PROTECT: + second_menu = "System Setup"; + third_menu = "Freeze Protect"; + break; + case OTM_SYSTEM_SETUP: + second_menu = "System Setup"; + break; + case OTM_BOOST: + second_menu = "Boost"; + break; + default: + break; + } + + // Now actually get the menu + switch(menu){ + case OTM_SYSTEM: + // We are already here + return true; + break; + case OTM_EQUIPTMENT_ONOFF: + //if ( select_onetouch_menu_item(aq_data, "Equipment ON/OFF") == false ) { // NSF Should use this + if ( ot_strcmp(onetouch_menu_hlight(), "Equipment ON/OFF") == 0) { + send_ot_cmd(KEY_ONET_SELECT); + waitfor_ot_queue2empty(); + waitForNextOT_Menu(aq_data); + return true; + } + return false; + break; + case OTM_MENUHELP: + case OTM_SET_TEMP: + case OTM_SET_TIME: + case OTM_SET_AQUAPURE: + case OTM_FREEZE_PROTECT: + case OTM_BOOST: + case OTM_SYSTEM_SETUP: + if ( select_onetouch_menu_item(aq_data, " Menu / Help") == false ) { + logMessage(LOG_ERR, "OneTouch device programmer couldn't select menu %d\n",menu); + } + if (second_menu) + select_onetouch_menu_item(aq_data, second_menu); + if (third_menu) + select_onetouch_menu_item(aq_data, third_menu); + /* + if (menu == OTM_SET_HEATER) { + select_onetouch_menu_item(aq_data, "Set Temp"); + } else if (menu == OTM_SET_TIME) { + select_onetouch_menu_item(aq_data, "Set Time"); + } else if (menu == OTM_SET_AQUAPURE) { + select_onetouch_menu_item(aq_data, "Set AQUAPURE"); + } + */ + break; + default: + logMessage(LOG_ERR, "OneTouch device programmer doesn't know how to access menu %d\n",menu); + break; + } + + if (get_onetouch_memu_type() != menu) + return false; + + return true; +} + +/* +bool in_ot_programming_mode(struct aqualinkdata *aq_data) +{ + //( type != AQ_SET_PUMP_RPM || type != AQ_SET_OT_MACRO )) { + + if ( ( aq_data->active_thread.thread_id != 0 ) && + ( aq_data->active_thread.ptype == AQ_SET_PUMP_RPM || + aq_data->active_thread.ptype == AQ_SET_OT_MACRO || + aq_data->active_thread.ptype == AQ_GET_OT_POOL_SPA_HEATER_TEMPS) + ) { + return true; + } + + return false; +} +*/ + + +// Return the digit at factor +// num=12 factor=10 would return 2 +// num=12 factor=100 would return 1 +int digit(int num, int factor) +{ + return ( (num % factor) - (num % (factor/10)) ) / (factor/10) ; +} + +// REturn the differance at a particular digit. +// new=120, old=130, factor=100 return 2-3 = -1 +int digitDiff(int new, int old, int factor) +{ + return ( digit(old, factor) - digit(new, factor) ); +} + +bool intPress(int diff) { + int i; + unsigned char key = KEY_ONET_UP; + + if (diff < 0) { + diff = 0 - diff; + key = KEY_ONET_DOWN; + //printf ("**** Pressing down %d times\n",diff); + } else { + //printf ("**** Pressing UP %d times\n",diff); + } + + for (i=0; i < diff; i++) { + send_ot_cmd(key); + waitfor_ot_queue2empty(); + } + + return true; +} + +/* + PROGRAMMING FUNCTIONS +*/ + +void *set_aqualink_pump_rpm( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + char *buf = (char*)threadCtrl->thread_args; + char VSPstr[20]; + int i, structIndex; + + //printf("**** program string '%s'\n",buf); + + int pumpIndex = atoi(&buf[0]); + int pumpRPM = -1; + //int pumpRPM = atoi(&buf[2]); + for (structIndex=0; structIndex < aq_data->num_pumps; structIndex++) { + if (aq_data->pumps[structIndex].pumpIndex == pumpIndex) { + if (aq_data->pumps[structIndex].pumpType == PT_UNKNOWN) { + logMessage(LOG_ERR, "Can't set Pump RPM/GPM until type is known\n"); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + pumpRPM = RPM_check(aq_data->pumps[structIndex].pumpType, atoi(&buf[2]), aq_data); + break; + } + } + // NSF Should probably check pumpRPM is not -1 here + + waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_ONETOUCH_PUMP_RPM); + + logMessage(LOG_NOTICE, "OneTouch Set Pump %d to RPM %d, from '%s'\n",pumpIndex,pumpRPM,buf); + + if (! goto_onetouch_menu(aq_data, OTM_EQUIPTMENT_ONOFF) ){ + logMessage(LOG_ERR, "OneTouch device programmer didn't get Equiptment on/off menu\n"); + } + + sprintf(VSPstr, "VSP%1d Spd Adj",pumpIndex); +/* + if ( (index = onetouch_menu_find_index(VSPstr)) != -1) { + int cnt = index - onetouch_menu_hlightindex(); + for (i=0; i < cnt; i ++) { + send_ot_cmd(KEY_ONET_DOWN); + waitfor_ot_queue2empty(); + } + send_ot_cmd(KEY_ONET_SELECT); + waitForNextOT_Menu(aq_data); +*/ + if ( select_onetouch_menu_item(aq_data, VSPstr) ) { + if ( get_onetouch_memu_type() == OTM_SELECT_SPEED) { + // Now fine menu item with X as last digit, and select that menu. + //Pool X + for (i=0; i < 12; i++) { + if ( onetouch_menu_hlight()[15] != 'X' ) { + send_ot_cmd(KEY_ONET_DOWN); + waitfor_ot_queue2empty(); + waitForOT_MessageTypes(aq_data,CMD_PDA_HIGHLIGHT,CMD_PDA_HIGHLIGHTCHARS,15); + } else { + send_ot_cmd(KEY_ONET_SELECT); + waitfor_ot_queue2empty(); + waitForNextOT_Menu(aq_data); + break; + } + } + //OneTouch Menu Line 3 = set to 50 GPM + //OneTouch Menu Line 3 = set to 1750 RPM + if ( strstr(onetouch_menu_hlight(), "set to") != NULL ) { + //printf("FOUND MENU") + if (strstr(onetouch_menu_hlight(), "RPM") != NULL ) { + // RPM 3450 & 600 max & min + int RPM = ot_atoi(&onetouch_menu_hlight()[7]); + intPress(digitDiff(RPM, pumpRPM, 10000)); + send_ot_cmd(KEY_ONET_SELECT); + waitfor_ot_queue2empty(); + intPress(digitDiff(RPM, pumpRPM, 1000)); + send_ot_cmd(KEY_ONET_SELECT); + waitfor_ot_queue2empty(); + intPress(digitDiff(RPM, pumpRPM, 100)); + send_ot_cmd(KEY_ONET_SELECT); + waitfor_ot_queue2empty(); + intPress(digitDiff(RPM, pumpRPM, 10)); + send_ot_cmd(KEY_ONET_SELECT); + waitfor_ot_queue2empty(); + // Reset the pump RPM + aq_data->pumps[structIndex].rpm = RPM; + } else if (strstr(onetouch_menu_hlight(), "GPM") != NULL ) { + // GPM 130 max, GPM 15 min + for (i=0; i < 24 ; i++) { // Max of 23 key presses to get from max to min + int GPM = ot_atoi(&onetouch_menu_hlight()[8]); + //printf ("*** GPM = %d | Setting to %d\n",GPM,pumpRPM); + if ( GPM > pumpRPM ) { + send_ot_cmd(KEY_ONET_DOWN); + } else if (GPM < pumpRPM) { + send_ot_cmd(KEY_ONET_UP); + } else { + send_ot_cmd(KEY_ONET_SELECT); + aq_data->pumps[structIndex].gpm = GPM; + waitfor_ot_queue2empty(); + break; + } + waitfor_ot_queue2empty(); + // This really does slow it down, but we hit up.down once too ofter without it, need to fix. + waitForOT_MessageTypes(aq_data,CMD_PDA_HIGHLIGHT,CMD_PDA_HIGHLIGHTCHARS,5); + // Reset the pump GPM + aq_data->pumps[structIndex].rpm = GPM; + //waitForOT_MessageTypes(aq_data,CMD_PDA_HIGHLIGHT,CMD_MSG_LONG,5); + //waitForNextOT_Menu(aq_data); + } + } else { + logMessage(LOG_ERR, "OneTouch device programmer Not sure how to set '%s'\n",onetouch_menu_hlight()); + } + } else { + logMessage(LOG_ERR, "OneTouch device programmer didn't select VSP\n"); + } + } else { + logMessage(LOG_ERR, "OneTouch device programmer Couldn't find Select Speed menu\n"); + } + } else { + logMessage(LOG_ERR, "OneTouch device programmer Couldn't find VSP in Equiptment on/off menu\n"); + } + //printf( "Menu Index %d\n", onetouch_menu_find_index(VSPstr)); + + //printf("**** GOT THIS FAR, NOW LET'S GO BACK ****\n"); + + if (! goto_onetouch_menu(aq_data, OTM_SYSTEM) ){ + logMessage(LOG_ERR, "OneTouch device programmer didn't get back to System menu\n"); + } + + //printf("**** CLEAN EXIT ****\n"); + + cleanAndTerminateThread(threadCtrl); + + // just stop compiler error, ptr is not valid as it's just been freed + return ptr; +} + +void *set_aqualink_onetouch_macro( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + //sprintf(msg, "%-5d%-5d",index, (strcmp(value, "on") == 0)?ON:OFF); + // Use above to set + char *buf = (char*)threadCtrl->thread_args; + unsigned int device = atoi(&buf[0]); + unsigned int state = atoi(&buf[5]); + + waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_ONETOUCH_MACRO); + + logMessage(LOG_DEBUG, "OneTouch Marco\n"); + + logMessage(LOG_ERR, "OneTouch Macro not implimented (device=%d|state=%d)\n",device,state); + + cleanAndTerminateThread(threadCtrl); + + // just stop compiler error, ptr is not valid as it's just been freed + return ptr; +} + +void *get_aqualink_onetouch_setpoints( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_GET_ONETOUCH_SETPOINTS); + + logMessage(LOG_DEBUG, "OneTouch get heater temps\n"); + + if ( !goto_onetouch_menu(aq_data, OTM_SET_TEMP) ){ + logMessage(LOG_ERR, "OneTouch device programmer failed to get heater temp menu\n"); + } + + if ( !goto_onetouch_menu(aq_data, OTM_FREEZE_PROTECT) ){ + logMessage(LOG_ERR, "OneTouch device programmer failed to get freeze protect menu\n"); + } + + if (! goto_onetouch_menu(aq_data, OTM_SYSTEM) ){ + logMessage(LOG_ERR, "OneTouch device programmer didn't get back to System menu\n"); + } +/* + logMessage(LOG_DEBUG, "*** OneTouch device programmer TEST page down ***\n"); + goto_onetouch_menu(aq_data, OTM_SYSTEM_SETUP); + highlight_onetouch_menu_item(aq_data, "About"); + send_ot_cmd(KEY_ONET_SELECT); + waitfor_ot_queue2empty(); + waitForNextOT_Menu(aq_data); + logMessage(LOG_DEBUG, "*** OneTouch device programmer END TEST page down ***\n"); +*/ + cleanAndTerminateThread(threadCtrl); + + // just stop compiler error, ptr is not valid as it's just been freed + return ptr; +} + +void set_aqualink_onetouch_heater_setpoint( struct aqualinkdata *aq_data, bool ispool, int val ) +{ + int cval; + int diff; + int i; + int len; + //char *st; + unsigned char direction; + + if ( !goto_onetouch_menu(aq_data, OTM_SET_TEMP) ){ + logMessage(LOG_ERR, "OneTouch device programmer failed to get heater temp menu\n"); + } + + if(ispool){ + if (!highlight_onetouch_menu_item(aq_data, "Pool Heat")) { + logMessage(LOG_ERR, "OneTouch device programmer failed to get pool heater temp menu\n"); + return; + } + } else { + if (!highlight_onetouch_menu_item(aq_data, "Spa Heat")) { + logMessage(LOG_ERR, "OneTouch device programmer failed to get spa heater temp menu\n"); + return; + } + } + + send_ot_cmd(KEY_ONET_SELECT); + waitfor_ot_queue2empty(); + waitForOT_MessageTypes(aq_data,CMD_PDA_HIGHLIGHTCHARS,0x00,15); // CMD_PDA_0x04 is just a packer. + + { + char *st = onetouch_menu_hlightchars(&len); + logMessage(LOG_DEBUG, "** OneTouch set heater temp highlighted='%.*s'\n", len, st); + } + + cval = atoi(onetouch_menu_hlightchars(&len)); + diff = val - cval; + if (diff > 0) { + direction = KEY_ONET_UP; + } else if (diff < 0) { + direction = KEY_ONET_DOWN; + diff=-diff; + } + + logMessage(LOG_DEBUG, "** OneTouch set heater temp diff='%d'\n", diff); + + for (i=0; i < diff; i++) { + send_ot_cmd(direction); + waitfor_ot_queue2empty(); + } + + send_ot_cmd(KEY_ONET_SELECT); + waitfor_ot_queue2empty(); + send_ot_cmd(KEY_ONET_BACK); + waitfor_ot_queue2empty(); + +/* + logMessage(LOG_DEBUG, "** OneTouch set heater temp line='%s'\n", onetouch_menu_line(line)); + logMessage(LOG_DEBUG, "** OneTouch set heater temp highlight='%d'\n", onetouch_menu_hlightindex()); + logMessage(LOG_DEBUG, "** OneTouch set heater temp highlightline='%s'\n", onetouch_menu_line(onetouch_menu_hlightindex())); + logMessage(LOG_DEBUG, "** OneTouch set heater temp highlightchars='%s'\n", onetouch_menu_hlight()); +*/ + //onetouch_menu_line(line) + //onetouch_menu_hlightindex + //onetouch_menu_hlight +} + +void *set_aqualink_onetouch_pool_heater_temp( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_ONETOUCH_POOL_HEATER_TEMP); + + int val = atoi((char*)threadCtrl->thread_args); + val = setpoint_check(POOL_HTR_SETOINT, val, aq_data); + + logMessage(LOG_DEBUG, "OneTouch set pool heater temp to %d\n", val); + set_aqualink_onetouch_heater_setpoint(aq_data, true, val); + + cleanAndTerminateThread(threadCtrl); + + // just stop compiler error, ptr is not valid as it's just been freed + return ptr; +} + +void *set_aqualink_onetouch_spa_heater_temp( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_ONETOUCH_SPA_HEATER_TEMP); + + int val = atoi((char*)threadCtrl->thread_args); + val = setpoint_check(SPA_HTR_SETOINT, val, aq_data); + + logMessage(LOG_DEBUG, "OneTouch set spa heater temp to %d\n", val); + set_aqualink_onetouch_heater_setpoint(aq_data, false, val); + + cleanAndTerminateThread(threadCtrl); + + // just stop compiler error, ptr is not valid as it's just been freed + return ptr; +} + +void *set_aqualink_onetouch_boost( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_ONETOUCH_BOOST); + + int val = atoi((char*)threadCtrl->thread_args); + + logMessage(LOG_DEBUG, "OneTouch request set Boost to '%d'\n",val==true?"On":"Off"); + + if ( !goto_onetouch_menu(aq_data, OTM_BOOST) ){ + logMessage(LOG_ERR, "OneTouch device programmer failed to get BOOST menu\n"); + } else { + if ( ot_strcmp(onetouch_menu_hlight(), "Start") == 0 ) { + if (val) { + logMessage(LOG_DEBUG, "OneTouch Boost is Off, turning On\n"); + send_ot_cmd(KEY_ONET_SELECT); + waitfor_ot_queue2empty(); + } else { + logMessage(LOG_INFO, "OneTouch Boost is Off, ignore request\n"); + } + } else if ( ot_strcmp(onetouch_menu_hlight(), "Pause") == 0 ) { + if (! val) { + logMessage(LOG_DEBUG, "OneTouch set Boost is ON, turning Off\n"); + highlight_onetouch_menu_item(aq_data, "Stop"); + send_ot_cmd(KEY_ONET_SELECT); + waitfor_ot_queue2empty(); + // Takes ages to see bost is off from menu, to set it here. + aq_data->boost = false; + aq_data->boost_msg[0] = '\0'; + aq_data->swg_percent = 0; + } else { + logMessage(LOG_INFO, "OneTouch Boost is On, ignore request\n"); + } + } else { + logMessage(LOG_ERR, "OneTouch Boost unknown menu\n"); + } + } + + if (! goto_onetouch_menu(aq_data, OTM_SYSTEM) ){ + logMessage(LOG_ERR, "OneTouch device programmer didn't get back to System menu\n"); + } + + cleanAndTerminateThread(threadCtrl); + + // just stop compiler error, ptr is not valid as it's just been freed + return ptr; +} + +void *set_aqualink_onetouch_swg_percent( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_ONETOUCH_SWG_PERCENT); + + logMessage(LOG_DEBUG, "OneTouch set SWG Percent\n"); + + if ( !goto_onetouch_menu(aq_data, OTM_SET_AQUAPURE) ){ + logMessage(LOG_ERR, "OneTouch device programmer failed to get Aquapure menu\n"); + } else { + + } + + if (! goto_onetouch_menu(aq_data, OTM_SYSTEM) ){ + logMessage(LOG_ERR, "OneTouch device programmer didn't get back to System menu\n"); + } + + cleanAndTerminateThread(threadCtrl); + + // just stop compiler error, ptr is not valid as it's just been freed + return ptr; +} + +void *set_aqualink_onetouch_freezeprotect( void *ptr ) +{ + return ptr; +} + +void *set_aqualink_onetouch_time( void *ptr ) +{ + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_ONETOUCH_TIME); + + logMessage(LOG_DEBUG, "OneTouch set time\n"); + + if ( !goto_onetouch_menu(aq_data, OTM_SET_TIME) ){ + logMessage(LOG_ERR, "OneTouch device programmer failed to get time menu\n"); + } else { + + } + + if (! goto_onetouch_menu(aq_data, OTM_SYSTEM) ){ + logMessage(LOG_ERR, "OneTouch device programmer didn't get back to System menu\n"); + } + + cleanAndTerminateThread(threadCtrl); + + // just stop compiler error, ptr is not valid as it's just been freed + return ptr; +} + diff --git a/onetouch_aq_programmer.h b/onetouch_aq_programmer.h new file mode 100644 index 0000000..23dfd09 --- /dev/null +++ b/onetouch_aq_programmer.h @@ -0,0 +1,20 @@ + +#ifndef ONETOUCH_AQ_PROGRAMMER_H_ +#define ONETOUCH_AQ_PROGRAMMER_H_ + +unsigned char pop_ot_cmd(unsigned char receive_type); +bool ot_queue_cmd(unsigned char cmd); + +//bool in_ot_programming_mode(struct aqualinkdata *aq_data); + +void *set_aqualink_pump_rpm( void *ptr ); +void *set_aqualink_onetouch_macro( void *ptr ); +void *get_aqualink_onetouch_setpoints( void *ptr ); +void *set_aqualink_onetouch_spa_heater_temp( void *ptr ); +void *set_aqualink_onetouch_pool_heater_temp( void *ptr ); +void *set_aqualink_onetouch_swg_percent( void *ptr ); +void *set_aqualink_onetouch_boost( void *ptr ); +void *set_aqualink_onetouch_time( void *ptr ); +void *set_aqualink_onetouch_freezeprotect( void *ptr ); + +#endif // ONETOUCH_AQ_PROGRAMMER_H_ \ No newline at end of file diff --git a/packetLogger.c b/packetLogger.c index bb31a7f..9d332f6 100644 --- a/packetLogger.c +++ b/packetLogger.c @@ -11,7 +11,7 @@ static FILE *_byteLogFile = NULL; static bool _log2file = false; static bool _includePentair = false; -void _logPacket(unsigned char *packet_buffer, int packet_length, bool error); +void _logPacket(unsigned char *packet_buffer, int packet_length, bool error, bool force); void startPacketLogger(bool debug_RSProtocol_packets, bool read_pentair_packets) { _log2file = debug_RSProtocol_packets; @@ -37,17 +37,21 @@ void writePacketLog(char *buffer) { } void logPacket(unsigned char *packet_buffer, int packet_length) { - _logPacket(packet_buffer, packet_length, false); + _logPacket(packet_buffer, packet_length, false, false); } void logPacketError(unsigned char *packet_buffer, int packet_length) { - _logPacket(packet_buffer, packet_length, true); + _logPacket(packet_buffer, packet_length, true, false); } -void _logPacket(unsigned char *packet_buffer, int packet_length, bool error) +void debuglogPacket(unsigned char *packet_buffer, int packet_length) { + _logPacket(packet_buffer, packet_length, false, true); +} + +void _logPacket(unsigned char *packet_buffer, int packet_length, bool error, bool force) { // No point in continuing if loglevel is < debug_serial and not writing to file - if ( error == false && getLogLevel() < LOG_DEBUG_SERIAL && _log2file == false) + if ( force == false && error == false && getLogLevel() < LOG_DEBUG_SERIAL && _log2file == false) return; char buff[1000]; @@ -70,8 +74,12 @@ void _logPacket(unsigned char *packet_buffer, int packet_length, bool error) if (error == true) logMessage(LOG_WARNING, "%s", buff); - else - logMessage(LOG_DEBUG_SERIAL, "%s", buff); + else { + if (force) + logMessage(LOG_DEBUG, "%s", buff); + else + logMessage(LOG_DEBUG_SERIAL, "%s", buff); + } } //#define RAW_BUFFER_SIZE 100 diff --git a/packetLogger.h b/packetLogger.h index 76751b1..a074ecd 100644 --- a/packetLogger.h +++ b/packetLogger.h @@ -13,5 +13,8 @@ void logPacket(unsigned char *packet_buffer, int packet_length); void logPacketError(unsigned char *packet_buffer, int packet_length); void logPacketByte(unsigned char *byte); +// Only use for manual debugging +void debuglogPacket(unsigned char *packet_buffer, int packet_length); + #endif //PACKETLOGGER_H_ \ No newline at end of file diff --git a/pda.c b/pda.c index 99e2bf8..9fb3fd9 100644 --- a/pda.c +++ b/pda.c @@ -43,11 +43,11 @@ static bool _initWithRS = false; #ifdef BETA_PDA_AUTOLABEL -static struct aqconfig *_aqualink_config; -void init_pda(struct aqualinkdata *aqdata, struct aqconfig *aqconfig) +//static struct aqconfig *_aqconfig_; +void init_pda(struct aqualinkdata *aqdata) { _aqualink_data = aqdata; - _aqualink_config = aqconfig; + //_aqconfig_ = aqconfig; set_pda_mode(true); } #else @@ -491,7 +491,7 @@ void process_pda_packet_msg_long_level_aux_device(const char *msg) int li=-1; char *str, *label; - if (! _aqualink_config->use_panel_aux_labels) + if (! _aqconfig_->use_panel_aux_labels) return; // NSF Need to check config for use_panel_aux_labels value and ignore if not set @@ -686,10 +686,11 @@ bool process_pda_packet(unsigned char *packet, int length) { _initWithRS = true; logMessage(LOG_DEBUG, "**** PDA INIT ****"); - aq_programmer(AQ_PDA_INIT, NULL, _aqualink_data); + //aq_programmer(AQ_PDA_INIT, NULL, _aqualink_data); + queueGetProgramData(AQUAPDA, _aqualink_data); delay(50); // Make sure this one runs first. #ifdef BETA_PDA_AUTOLABEL - if (_aqualink_config->use_panel_aux_labels) + if (_aqconfig_->use_panel_aux_labels) aq_programmer(AQ_GET_AUX_LABELS, NULL, _aqualink_data); #endif aq_programmer(AQ_PDA_WAKE_INIT, NULL, _aqualink_data); @@ -707,7 +708,7 @@ bool process_pda_packet(unsigned char *packet, int length) packet[PKT_CMD] == CMD_PDA_HIGHLIGHTCHARS) { // We processed the next message, kick any threads waiting on the message. - kick_aq_program_thread(_aqualink_data); + kick_aq_program_thread(_aqualink_data, AQUAPDA); } return rtn; } diff --git a/pentair_messages.c b/pentair_messages.c index a1ec155..8b20811 100644 --- a/pentair_messages.c +++ b/pentair_messages.c @@ -105,7 +105,7 @@ bool processiAqualinkMsg(unsigned char *packet_buffer, int packet_length, struct pumpIndex = atoi((char *) &lastmessage[14]); - if ( pumpIndex < MAX_PUMPS && pumpIndex < 0) { + if ( pumpIndex < aqdata->num_pumps && pumpIndex < 0) { pumpIndex = 1; logMessage(LOG_ERR, "Can't find pump index for messsage '%.*s' in string '%.*s' using %d\n",AQ_MSGLEN, packet_buffer+4, AQ_MSGLEN, lastmessage, pumpIndex); } diff --git a/release/aqualinkd b/release/aqualinkd index a85b4e4..c12bdec 100755 Binary files a/release/aqualinkd and b/release/aqualinkd differ diff --git a/release/aqualinkd.conf b/release/aqualinkd.conf index 82af0be..864ca05 100755 --- a/release/aqualinkd.conf +++ b/release/aqualinkd.conf @@ -64,6 +64,17 @@ report_zero_pool_temp = no # You can use 0x00 and AqualinkD will find an ID for you, but this makes a slow startup device_id=0x0a +# The ID for extended settings, These are ONE TOUCH MACROS & VARIABLE SPEED PUMP RPM +# Do not enable this if you don't use either, you'll just waste memory and cpu cycles +# Valid ID's are 0x40, 0x41, 0x42 & 0x43. +# If you have a one touch remote do not use Ox40 +#extended_device_id=0x41 + +# If you have extended_device_id set, then you can also use that ID for programming some features. +# This means that you can turn things on/off while AqualinkD is programming certian features. +# At the moment only heater setpoints & swg boost is on the extended device programming +#extended_device_id_programming = yes + # Please see forum for this, only set to yes when logging information to support # new devices. Inflrmation will be written to /tmp/RS485.log #debug_RSProtocol_packets = no @@ -127,18 +138,23 @@ use_panel_aux_labels=no # Simply change these to your setup, comment out ones that ent in _dzidx if you don't use Domoticz. # If using PDA mode, PDA Labels below are of the utmost importance, the PDA labels MUST match the labels in the "EQUIPTMENT ON/OFF" menu of the PDA device. # -# Optional, If you have a Variable Speed Pump, then assign the RS485 ID to the button below so RPM/GPH/WATTS are displayed +# Optional, ( button_01_pumpID & button_01_pumpIndex ) +# If you have a Variable Speed Pump, then assign the RS485 ID to the button below so RPM/GPH/WATTS are displayed # Format is button_01_pumpID=0x60. Leave blank if you don't have a VSP. # Pentair pump ID's # 0x60 to 0x6F (0x60, 0x61 0x62, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F) # Jandy pump ID's # 0x78, 0x79, 0x7A, 0x7B +# +# button_01_pumpIndex=1 +# If you have assigned this pump an index number in your Aqualink control panel, (Between 1 & 4), put it here for VSP, RPM, Primp information to be captured. # Labels for standard butons (shown in web UI), and domoticz idx's button_01_label=Filter Pump #button_01_dzidx=37 #button_01_PDA_label=FILTER PUMP #button_01_pumpID=0x60 +#button_01_pumpIndex=1 button_02_label=Spa Mode #button_02_dzidx=38 @@ -151,7 +167,8 @@ button_03_label=Cleaner button_04_label=Waterfall #button_04_dzidx=40 #button_04_PDA_label=AUX2 -#button_01_pumpID=0x61 +#button_04_pumpID=0x61 +#button_04_pumpIndex=2 button_05_label=Spa Blower #button_05_dzidx=41 diff --git a/release/install.sh b/release/install.sh index d020076..c69c821 100755 --- a/release/install.sh +++ b/release/install.sh @@ -38,42 +38,19 @@ command -v systemctl >/dev/null 2>&1 || { echo "This script needs systemd's syst systemctl stop $SERVICE > /dev/null 2>&1 SERVICE_EXISTS=$(echo $?) -# Clean everything if requested. -if [ "$1" == "clean" ]; then - echo "Deleting install" - systemctl disable $SERVICE > /dev/null 2>&1 - if [ -f $BINLocation/$BIN ]; then - rm -f $BINLocation/$BIN - fi - if [ -f $SRVLocation/$SRV ]; then - rm -f $SRVLocation/$SRV - fi - if [ -f $CFGLocation/$CFG ]; then - rm -f $CFGLocation/$CFG - fi - if [ -f $DEFLocation/$DEF ]; then - rm -f $DEFLocation/$DEF - fi - if [ -d $WEBLocation ]; then - rm -rf $WEBLocation - fi - systemctl daemon-reload - exit -fi - # copy files to locations, but only copy cfg if it doesn;t already exist cp $BUILD/$BIN $BINLocation/$BIN cp $BUILD/$SRV $SRVLocation/$SRV if [ -f $CFGLocation/$CFG ]; then - echo "AqualinkD config exists, did not copy new config, you may need to edit existing! $CFGLocation/$CFG" + echo "Config exists, did not copy new config, you may need to edit existing! $CFGLocation/$CFG" else cp $BUILD/$CFG $CFGLocation/$CFG fi if [ -f $DEFLocation/$DEF ]; then - echo "AqualinkD defaults exists, did not copy new defaults to $DEFLocation/$DEF" + echo "Defaults exists, did not copy new defaults to $DEFLocation/$DEF" else cp $BUILD/$DEF.defaults $DEFLocation/$DEF fi @@ -93,7 +70,7 @@ if [ ! -d "$WEBLocation" ]; then fi if [ -f "$WEBLocation/config.js" ]; then - echo "AqualinkD web config exists, did not copy new config, you may need to edit existing $WEBLocation/config.js " + echo "$WEBLocation/config.js exists, did not copy overight, please make sure to update manually" rsync -avq --exclude='config.js' $BUILD/../web/* $WEBLocation else cp -r $BUILD/../web/* $WEBLocation @@ -106,7 +83,5 @@ systemctl daemon-reload if [ $SERVICE_EXISTS -eq 0 ]; then echo "Starting daemon $SERVICE" systemctl start $SERVICE -else - echo "Please edit $CFGLocation/$CFG, then start AqualinkD service" fi diff --git a/release/serial_logger b/release/serial_logger index 3d148ae..c2f1a5f 100755 Binary files a/release/serial_logger and b/release/serial_logger differ diff --git a/serial_logger.c b/serial_logger.c index 8f92df9..b1af85c 100644 --- a/serial_logger.c +++ b/serial_logger.c @@ -28,11 +28,12 @@ #include "aq_serial.h" #include "utils.h" +#include "packetLogger.h" #define SLOG_MAX 80 #define PACKET_MAX 600 -#define VERSION "serial_logger V1.1" +#define VERSION "serial_logger V1.2" /* typedef enum used { @@ -51,6 +52,7 @@ bool _keepRunning = true; unsigned char _goodID[] = {0x0a, 0x0b, 0x08, 0x09}; unsigned char _goodPDAID[] = {0x60, 0x61, 0x62, 0x63}; +unsigned char _goodONETID[] = {0x40, 0x41, 0x42, 0x43}; unsigned char _filter[10]; int _filters=0; bool _rawlog=false; @@ -67,7 +69,7 @@ void intHandler(int dummy) { #define SWG " <-- Salt Water Generator (Aquarite mode)" #define KEYPAD " <-- RS Keypad" #define SPA_R " <-- Spa remote" -#define AQUA " <-- Aqualink (iAqualink?)" +#define AQUA " <-- Aqualink (iAqualink / Touch)" #define HEATER " <-- LX Heater" #define ONE_T " <-- Onetouch device" #define PC_DOCK " <-- PC Interface (RS485 to RS232)" @@ -152,6 +154,10 @@ bool canUse(unsigned char ID) { if (ID == _goodPDAID[i]) return true; } + for (i = 0; i < 4; i++) { + if (ID == _goodONETID[i]) + return true; + } return false; } char* canUseExtended(unsigned char ID) { @@ -164,6 +170,10 @@ char* canUseExtended(unsigned char ID) { if (ID == _goodPDAID[i]) return " <-- can use for Aqualinkd (PDA mode only)"; } + for (i = 0; i < 4; i++) { + if (ID == _goodONETID[i]) + return " <-- can use for Aqualinkd (Extended Device ID)"; + } return ""; } @@ -249,6 +259,7 @@ int main(int argc, char *argv[]) { int received_packets = 0; int logPackets = PACKET_MAX; int logLevel = LOG_NOTICE; + bool rsRawDebug = false; //bool playback_file = false; //int logLevel; @@ -264,7 +275,14 @@ int main(int argc, char *argv[]) { if (argc < 2 || access( argv[1], F_OK ) == -1 ) { fprintf(stderr, "ERROR, first param must be valid serial port, ie:-\n\t%s /dev/ttyUSB0\n\n", argv[0]); - fprintf(stderr, "Optional parameters are -d (debug) & -p (log # packets) & -i & -r (raw) ie:=\n\t%s /dev/ttyUSB0 -d -p 1000 -i 0x08\n\n", argv[0]); + //fprintf(stderr, "Optional parameters are -d (debug) & -p (log # packets) & -i & -r (raw) ie:=\n\t%s /dev/ttyUSB0 -d -p 1000 -i 0x08\n\n", argv[0]); + fprintf(stderr, "Optional parameters are :-\n"); + fprintf(stderr, "\t-d (debug)\n"); + fprintf(stderr, "\t-p (log # packets)\n"); + fprintf(stderr, "\t-i (just log these ID's, can use multiple -i)\n"); + fprintf(stderr, "\t-r (raw)\n"); + fprintf(stderr, "\t-rsrd (log raw RS bytes to %s)\n",RS485BYTELOGFILE); + fprintf(stderr, "\nie:\t%s /dev/ttyUSB0 -d -p 1000 -i 0x08 -i 0x0a\n\n", argv[0]); return 1; } @@ -286,6 +304,8 @@ int main(int argc, char *argv[]) { logLevel = LOG_DEBUG; } else if (strcmp(argv[i], "-f") == 0) { _playback_file = true; + } else if (strcmp(argv[i], "-rsrd") == 0) { + rsRawDebug = true; } } @@ -315,7 +335,10 @@ int main(int argc, char *argv[]) { } //packet_length = get_packet(rs_fd, packet_buffer); - packet_length = get_packet(rs_fd, packet_buffer); + if (rsRawDebug) + packet_length = get_packet_lograw(rs_fd, packet_buffer); + else + packet_length = get_packet(rs_fd, packet_buffer); if (packet_length == -1) { // Unrecoverable read error. Force an attempt to reconnect. @@ -401,6 +424,7 @@ int main(int argc, char *argv[]) { if (sindex >= SLOG_MAX) logMessage(LOG_ERR, "Ran out of storage, some ID's were not captured, please increase SLOG_MAX and recompile\n"); + logMessage(LOG_NOTICE, "Jandy ID's found\n"); for (i = 0; i < sindex; i++) { //logMessage(LOG_NOTICE, "ID 0x%02hhx is %s %s\n", slog[i].ID, (slog[i].inuse == true) ? "in use" : "not used", diff --git a/utils.c b/utils.c index f9ac7e5..5e526fb 100644 --- a/utils.c +++ b/utils.c @@ -87,6 +87,14 @@ void startInlineDebug() _log_filename = DEFAULT_LOG_FILE; } +void startInlineSerialDebug() +{ + _log_level = LOG_DEBUG_SERIAL; + _log2file = true; + if (_log_filename == NULL) + _log_filename = DEFAULT_LOG_FILE; +} + void stopInlineDebug() { _log_level = _cfg_log_level; @@ -343,7 +351,7 @@ void logMessage(int msg_level, char *format, ...) strncpy(buffer, " ", 8); vsprintf (&buffer[8], format, args); va_end(args); - + //test(msg_level, buffer); //fprintf (stderr, buffer); diff --git a/utils.h b/utils.h index 2534e8f..d67df0a 100644 --- a/utils.h +++ b/utils.h @@ -56,6 +56,7 @@ char *prittyString(char *str); //void writePacketLog(char *buff); //void closePacketLog(); void startInlineDebug(); +void startInlineSerialDebug(); void stopInlineDebug(); void cleanInlineDebug(); char *getInlineLogFName(); diff --git a/version.h b/version.h index b46a25f..ecdb6f6 100644 --- a/version.h +++ b/version.h @@ -1,4 +1,4 @@ #define AQUALINKD_NAME "Aqualink Daemon" -#define AQUALINKD_VERSION "1.3.9c" +#define AQUALINKD_VERSION "2.0.0a" diff --git a/web/config.js b/web/config.js index 8ae7986..d3f38ab 100644 --- a/web/config.js +++ b/web/config.js @@ -62,6 +62,10 @@ // 0 means only load once when page loads. //var background_reload = 10; + // By default all Variable Speed Pumps will show RPM. + // this will show GPM on VSP's that you can only set GPM (ie Jandy VF pumps) + //var show_vsp_gpm=false; + var body_background = "#EBEBEA"; var body_text = "#000000"; diff --git a/web/controller.html b/web/controller.html index 8f8fba5..2ec6ac9 100644 --- a/web/controller.html +++ b/web/controller.html @@ -469,6 +469,12 @@ var _aqualink_data; var _landscape = false; var _displayNames = []; + + if (typeof show_vsp_gpm !== 'undefined' && show_vsp_gpm == false) + var _show_vsp_gpm=false; + else + var _show_vsp_gpm=true; + //init(); function init() { setSizeSpecifics(); @@ -476,6 +482,7 @@ document.getElementById('thermostat_options').classList.remove("hide"); document.getElementById('swg_options').classList.remove("hide"); document.getElementById('pswitch_options').classList.remove("hide"); + document.getElementById('vspswitch_options').classList.remove("hide"); setColors(); load_background(); showTileOptions(false); @@ -714,10 +721,13 @@ subdiv.setAttribute('id', id + '_status'); subdiv.textContent = formatSatus(status); div.appendChild(subdiv); - if (type == "switch" && subtype != "switch_program") { + if (type == "switch" && (subtype != "switch_program" && subtype != "switch_vsp") ) { + //if (type == "switch" && subtype != "switch_program" ) { div.setAttribute('onclick', "switchTileState('" + id + "')"); + //console.log("add onclick switchtilestate to "+id); } else /*if (id != "SWG/Percent")*/ { add_clickEvent(div, id); + //console.log("add click Event to "+id); } } document.getElementById('wrapper').appendChild(div); @@ -735,9 +745,9 @@ try { if (state == (document.getElementById(id).getAttribute('status') == 'off')) { send_command(id); - console.log("Switch state "+id+" to "+(state)?"on":"off"); + //console.log("Switch state "+id+" to "+(state)?"on":"off"); } else { - console.log("state "+id+" to "+(state)?"on":"off") + //console.log("state "+id+" to "+(state)?"on":"off") } setTileOn(id, ((state) ? "on" : "off")); } catch(exception) {} @@ -866,10 +876,10 @@ text = 'Generating'; } else if (type == 'setpoint_thermo') - if (status == 'enabled') + //if (status == 'enabled') text = 'Heat to ' + tile.getAttribute('setpoint'); - else - text = 'Heating to ' + tile.getAttribute('setpoint'); + //else + // text = 'Heating to ' + tile.getAttribute('setpoint'); // too large for phone else if (type == 'setpoint_freeze') text = 'Turn on ' + tile.getAttribute('setpoint') + "°"; //else @@ -921,12 +931,42 @@ if (typeof devices !== 'undefined' && devices.indexOf(object.id) < 0) { return; } - if (object.type == 'switch' || object.type == 'switch_program') { + //if (object.type == 'switch' || object.type == 'switch_program') { + if (object.type == 'switch') { var img = object.id.replace('/', '_'); - add_tile(object.id, object.name, object.state, 'switch', object.type, 'hk/' + img + '-off.png', 'hk/' + img + '-on.png'); + var ext_type; + if (typeof object.type_ext !== 'undefined') + ext_type = object.type_ext; + else + ext_type = object.type; + + add_tile(object.id, object.name, object.state, 'switch', ext_type, 'hk/' + img + '-off.png', 'hk/' + img + '-on.png'); setTileOn(object.id, object.status, null); - if (typeof object.Pump_RPM !== 'undefined' && object.Pump_RPM) { - setTileOnText(object.id, 'RPM:'+object.Pump_RPM); + if (typeof object.type_ext !== 'undefined' && object.type_ext == 'switch_vsp') { + if (typeof object.Pump_RPM !== 'undefined' && object.Pump_RPM) { + if (object.Pump_RPM == -2) { + setTileOnText(object.id, 'Pump Offline'); // Small txt + document.getElementById(object.id).setAttribute('setpoint', 0); + } else if (object.Pump_RPM == -1) { + setTileOnText(object.id, 'Pump Priming'); // Small text + document.getElementById(object.id).setAttribute('setpoint', 0); + } else { + if (object.Pump_Type == "vfPump") { + if (_show_vsp_gpm == true) + setTileOnText(object.id, 'GPM:'+object.Pump_GPM); + else + setTileOnText(object.id, 'RPM:'+object.Pump_RPM); + + document.getElementById(object.id).setAttribute('setpoint', object.Pump_GPM); + } else { + setTileOnText(object.id, 'RPM:'+object.Pump_RPM); + document.getElementById(object.id).setAttribute('setpoint', object.Pump_RPM); + } + } + } + if (typeof object.Pump_Type !== 'undefined') { + document.getElementById(object.id).setAttribute('pumptype', object.Pump_Type); + } } } else if (object.type == 'value' || object.type == 'temperature') { add_tile(object.id, object.name, object.state, 'value', object.type); @@ -942,6 +982,7 @@ } function showTileOptions(show, id, contex) { + console.log("showTileOptions " + show + " "+id+" "+contex); var active_option; if (show == true) { var wrapH = document.getElementById('wrapper').clientHeight + 'px'; @@ -949,14 +990,22 @@ active_option = document.getElementById('thermostat_options'); document.getElementById('swg_options').style.display = 'none'; document.getElementById('pswitch_options').style.display = 'none'; + document.getElementById('vspswitch_options').style.display = 'none'; } else if (id != null && document.getElementById(id).getAttribute('type') == 'switch_program') { active_option = document.getElementById('pswitch_options'); document.getElementById('thermostat_options').style.display = 'none'; document.getElementById('swg_options').style.display = 'none'; - } else /*if (id != null && document.getElementById(id).getAttribute('type') == 'setpoint_swg')*/ { + document.getElementById('vspswitch_options').style.display = 'none'; + } else if (id != null && document.getElementById(id).getAttribute('type') == 'setpoint_swg') { active_option = document.getElementById('swg_options'); document.getElementById('thermostat_options').style.display = 'none'; document.getElementById('pswitch_options').style.display = 'none'; + document.getElementById('vspswitch_options').style.display = 'none'; + } else if (id != null && document.getElementById(id).getAttribute('type') == 'switch_vsp') { + active_option = document.getElementById('vspswitch_options'); + document.getElementById('thermostat_options').style.display = 'none'; + document.getElementById('pswitch_options').style.display = 'none'; + document.getElementById('swg_options').style.display = 'none'; } active_option.style.display = 'flex'; var optionH = window.getComputedStyle(active_option, null).getPropertyValue("height"); @@ -974,10 +1023,13 @@ document.getElementById("swg_options_close").click(); else if (document.getElementById('pswitch_options').style.display == 'flex') document.getElementById("pswitch_options_close").click(); + else if (document.getElementById('vspswitch_options').style.display == 'flex') + document.getElementById("vspswitch_options").click(); } document.getElementById('thermostat_options').style.display = 'none'; document.getElementById('swg_options').style.display = 'none'; document.getElementById('pswitch_options').style.display = 'none'; + document.getElementById('vspswitch_options').style.display = 'none'; document.getElementById('wrapper').classList.remove("opaque"); return; } @@ -1011,6 +1063,12 @@ title = document.getElementById("pswitch_option_title"); close_button = document.getElementById("pswitch_options_close"); //ext = '°' + _temperature_units; + } else if (type == 'switch_vsp') { + slider = document.getElementById("vspoption_slider_range"); + slider_output = document.getElementById("vspoption_slider_text_value"); + title = document.getElementById("vspswitch_option_title"); + close_button = document.getElementById("vspswitch_option_close"); + //ext = '°' + _temperature_units; } else { slider = document.getElementById("option_slider_range"); slider_output = document.getElementById("option_slider_text_value"); @@ -1042,6 +1100,18 @@ slider.max = 40; slider.step = 1; } + } else if (type == 'switch_vsp') { + if ( tile.getAttribute('pumptype') == "vfPump" ) { // RPM vs GPM + slider.min = 15; + slider.max = 130; + slider.step = 5; + ext = ' GPM'; + } else { + slider.min = 600; + slider.max = 3450; + slider.step = 5; + ext = ' RPM'; + } } title.innerHTML = document.getElementById(id + '_name').innerHTML; if (type == 'switch_program') { @@ -1070,6 +1140,21 @@ oswitch_output.innerHTML = ((oswitch.checked) ? "Boost On" : "Boost Off"); //setTileOn(id, ((oswitch.checked)?"on":"off"), null); } + } else if (type == 'switch_vsp') { + slider.value = sp_value; + oswitch = document.getElementById("vspoption_switch"); + oswitch.checked = tile_state; + var oswitch_output = document.getElementById("vspoption_switch_text_value"); + slider_output.innerHTML = slider.value + ext; + oswitch_output.innerHTML = ((oswitch.checked) ? "On" : "Off"); + slider.oninput = function() { + slider_output.innerHTML = this.value + ext; + //sp_value = this.value + } + oswitch.onclick = function() { + oswitch_output.innerHTML = ((oswitch.checked) ? "On" : "Off"); + //setTileOn(id, ((oswitch.checked)?"on":"off"), null); + } } else { slider.value = sp_value; oswitch = document.getElementById("option_switch"); @@ -1133,6 +1218,12 @@ if (sp_value != slider.value && slider.value != 101) // Don't change setpoint if slider is on boost setThermostatSetpoint(id, slider.value); } + } else if (type == 'switch_program') { + var value = slider.value; + if (state == (tile.getAttribute('status') == 'off')) + setTileState(id, state); + if (sp_value != slider.value) + setThermostatSetpoint(id, slider.value) } else { var value = slider.value; if (state == (tile.getAttribute('status') == 'off')) @@ -1215,7 +1306,24 @@ while (i < 5) { //console.log(data["Pump_"+i].RPM); if ((typeof data["Pump_"+i] !== 'undefined') && (typeof data["Pump_"+i].RPM !== 'undefined')) { - setTileOnText(data["Pump_"+i].id, 'RPM:'+data["Pump_"+i].RPM); + if (data["Pump_"+i].RPM == -2) { + setTileOnText(data["Pump_"+i].id, 'Pump Offline'); + } else if (data["Pump_"+i].RPM == -1) { + setTileOnText(data["Pump_"+i].id, 'Pump Priming'); + } else { + //setTileOnText(object.id, 'RPM:'+object.Pump_RPM); + if (data["Pump_"+i].Pump_Type == "vfPump") { + if (_show_vsp_gpm == true) + setTileOnText(data["Pump_"+i].id, 'GPM:'+data["Pump_"+i].GPM); + else + setTileOnText(data["Pump_"+i].id, 'RPM:'+data["Pump_"+i].RPM); + + document.getElementById(data["Pump_"+i].id).setAttribute('setpoint', data["Pump_"+i].GPM); + } else { + document.getElementById(data["Pump_"+i].id).setAttribute('setpoint', data["Pump_"+i].RPM); + setTileOnText(data["Pump_"+i].id, 'RPM:'+data["Pump_"+i].RPM); + } + } /* if (document.getElementById(data["Pump_"+i].id).getAttribute('status') == 'on') document.getElementById(data["Pump_"+i].id + '_status').innerHTML = 'RPM:'+data["Pump_"+i].RPM; @@ -1247,8 +1355,12 @@ createTile(data['devices'][obj]); } else { //console.log("old type "+document.getElementById(data['devices'][obj].id).getAttribute('type')+" | new "+data['devices'][obj].type); - if (document.getElementById(data['devices'][obj].id).getAttribute('type') != data['devices'][obj].type) { - //console.log("Remove and create "+data['devices'][obj].id); + var element_type = document.getElementById(data['devices'][obj].id).getAttribute('type'); + + if ( (element_type != data['devices'][obj].type) && + (typeof data['devices'][obj].type_ext !== 'undefined' && element_type != data['devices'][obj].type_ext ) ) { + console.log("Remove and create "+data['devices'][obj].id); + console.log("Old type "+element_type+" | new type "+data['devices'][obj].type+" sub "+data['devices'][obj].type_ext); var element = document.getElementById(data['devices'][obj].id); element.parentNode.removeChild(element); createTile(data['devices'][obj]); @@ -1335,8 +1447,10 @@ if ((tile = document.getElementById(id)) == null) { return; } + if (tile.getAttribute('setpoint')) temperature.parameter = tile.getAttribute('id'); temperature.value = tile.getAttribute('setpoint'); +//console.log("Send value back "+temperature.parameter+" "+temperature.value); socket_di.send(JSON.stringify(temperature)); } @@ -1516,6 +1630,42 @@ +
+
+ + + + + + + + + + + + + + + + + +
+
+ + +
+
+
+ +
+
+ +
+
+
diff --git a/web/debug.html b/web/debug.html index 9a2b0d2..e0af0e7 100644 --- a/web/debug.html +++ b/web/debug.html @@ -56,6 +56,8 @@ Button style taken from https://github.com/ubuwaits/css3-buttons */ if (element.id == "start") { document.getElementById('messages').innerHTML = "Debug Starting!" + } else if (element.id == "serialstart") { + document.getElementById('messages').innerHTML = "Serial Debug Starting!" } else if (element.id == "stop") { document.getElementById('messages').innerHTML = "Debug Stopping!" } else if (element.id == "clean") { @@ -90,10 +92,15 @@ Button style taken from https://github.com/ubuwaits/css3-buttons */ //console.log(data.iLevel + " : " + data.sLevel); if ( data.iLevel >= 7 ) { document.getElementById('start').disabled = true; - document.getElementById('stop').disabled = false; - document.getElementById('messages').innerHTML = "Debug Running!" + document.getElementById('serialstart').disabled = true; + document.getElementById('stop').disabled = false; + if ( data.iLevel >= 8 ) + document.getElementById('messages').innerHTML = "Serial debug Running!" + else + document.getElementById('messages').innerHTML = "Debug Running!" } else if ( data.iLevel < 7 ) { document.getElementById('start').disabled = false; + document.getElementById('serialstart').disabled = false; document.getElementById('stop').disabled = true; document.getElementById('messages').innerHTML = "Not debugging, Log level = "+data.sLevel; } @@ -125,20 +132,25 @@ Button style taken from https://github.com/ubuwaits/css3-buttons */ - - -
+
 
+ +
- +
+ + +
+ + + +
- -