AqualinkD/iaqtouch_aq_programmer.c

1079 lines
35 KiB
C

/*
* Copyright (c) 2017 Shaun Feakes - All rights reserved
*
* You may use redistribute and/or modify this code under the terms of
* the GNU General Public License version 2 as published by the
* Free Software Foundation. For the terms of this license,
* see <http://www.gnu.org/licenses/>.
*
* You are free to use this software under the terms of the GNU General
* Public License, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* https://github.com/sfeakes/aqualinkd
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "iaqtouch_aq_programmer.h"
#include "aq_serial.h"
#include "utils.h"
#include "aq_programmer.h"
#include "aqualink.h"
//#include "packetLogger.h"
#include "iaqtouch.h"
#include "rs_msg_utils.h"
#include "aq_panel.h"
#include "config.h"
#include "devices_jandy.h"
#include "packetLogger.h"
// System Page is obfiously fixed and not dynamic loaded, so set buttons to stop confustion.
#define KEY_IAQTCH_SCHEDULE KEY_IAQTCH_KEY01
#define KEY_IAQTCH_CUSTOMIZE_HOME KEY_IAQTCH_KEY02
#define KEY_IAQTCH_PROGRAM_GROUP KEY_IAQTCH_KEY03
#define KEY_IAQTCH_SET_TEMP KEY_IAQTCH_KEY04
#define KEY_IAQTCH_SYSTEM_SETUP KEY_IAQTCH_KEY05
#define KEY_IAQTCH_TOUCH_SETUP KEY_IAQTCH_KEY06
#define KEY_IAQTCH_SET_DATETIME KEY_IAQTCH_KEY07
#define KEY_IAQTCH_LOCKOUT_PASSWD KEY_IAQTCH_KEY08
#define KEY_IAQTCH_SET_ACQUAPURE KEY_IAQTCH_KEY09
bool _cansend = false;
unsigned char _iaqt_pgm_command = NUL;
/**************
*
* Command queue stuff
*
*/
// External command
bool iaqt_queue_cmd(unsigned char cmd) {
if (_iaqt_pgm_command == NUL) {
_iaqt_pgm_command = cmd;
return true;
}
return false;
}
void set_iaq_cansend(bool cansend){
_cansend = cansend;
}
unsigned char pop_iaqt_cmd(unsigned char receive_type)
{
unsigned char cmd = NUL;
if (!_cansend)
return cmd;
if (receive_type == CMD_IAQ_POLL) {
cmd = _iaqt_pgm_command;
_iaqt_pgm_command = NUL;
}
if (cmd != NUL)
LOG(IAQT_LOG,LOG_DEBUG, "Sending '0x%02hhx' to controller\n", cmd);
return cmd;
}
void waitfor_iaqt_queue2empty()
{
int i=0;
while ( (_iaqt_pgm_command != NUL) && ( i++ < PROGRAMMING_POLL_COUNTER) ) {
delay(PROGRAMMING_POLL_DELAY_TIME);
}
if (_iaqt_pgm_command != NUL) {
// Wait for longer interval
while ( (_iaqt_pgm_command != NUL) && ( i++ < PROGRAMMING_POLL_COUNTER * 2 ) ) {
delay(PROGRAMMING_POLL_DELAY_TIME * 2);
}
}
if (_iaqt_pgm_command != NUL) {
LOG(IAQT_LOG,LOG_WARNING, "Send command Queue did not empty, timeout\n");
}
}
void send_aqt_cmd(unsigned char cmd)
{
waitfor_iaqt_queue2empty();
iaqt_queue_cmd(cmd);
LOG(IAQT_LOG,LOG_DEBUG, "Queue send '0x%02hhx' to controller (programming)\n", _iaqt_pgm_command);
}
/**************
*
* Control Command queue stuff
*
*/
unsigned char _iaqt_control_cmd[AQ_MAXPKTLEN];
int _iaqt_control_cmd_len;
int ref_iaqt_control_cmd(unsigned char **cmd)
{
//printf("*********** GET READY SENDING CONTROL ****************\n");
*cmd = _iaqt_control_cmd;
if ( getLogLevel(IAQT_LOG) >= LOG_DEBUG ) {
char buff[1000];
//sprintf("Sending control command:")
beautifyPacket(buff, _iaqt_control_cmd, _iaqt_control_cmd_len);
LOG(IAQT_LOG,LOG_DEBUG, "Sending commandsed : %s\n", buff);
}
return _iaqt_control_cmd_len;
}
void rem_iaqt_control_cmd(unsigned char *cmd)
{
memset(_iaqt_control_cmd, 0, AQ_MAXPKTLEN * sizeof(unsigned char));
_iaqt_control_cmd_len = 0;
}
bool waitfor_iaqt_ctrl_queue2empty()
{
int i=0;
while ( (_iaqt_control_cmd_len >0 ) && ( i++ < 100) ) {
LOG(IAQT_LOG,LOG_DEBUG, "Waiting for commandset to send\n");
delay(50);
}
LOG(IAQT_LOG,LOG_DEBUG, "Wait for commandset over!\n");
if (_iaqt_control_cmd_len > 0 ) {
LOG(IAQT_LOG,LOG_WARNING, "Send control command Queue did not empty, timeout\n");
return false;
}
return true;
}
unsigned const char waitfor_iaqt_nextPage(struct aqualinkdata *aq_data) {
waitfor_iaqt_queue2empty();
int i=0;
const int numMessageReceived = 30;
pthread_mutex_lock(&aq_data->active_thread.thread_mutex);
while( ++i <= numMessageReceived)
{
//LOG(IAQT_LOG,LOG_DEBUG, "waitfor_iaqt_nextPage (%d of %d)\n",i,numMessageReceived);
pthread_cond_wait(&aq_data->active_thread.thread_cond, &aq_data->active_thread.thread_mutex);
if(wasiaqtThreadKickTypePage()) break;
}
LOG(IAQT_LOG,LOG_DEBUG, "waitfor_iaqt_nextPage finished in (%d of %d)\n",i,numMessageReceived);
pthread_mutex_unlock(&aq_data->active_thread.thread_mutex);
if(wasiaqtThreadKickTypePage())
return iaqtCurrentPage();
else
return NUL;
}
unsigned const char waitfor_iaqt_nextMessage(struct aqualinkdata *aq_data, const unsigned char msg_type) {
waitfor_iaqt_queue2empty();
int i=0;
const int numMessageReceived = 30;
pthread_mutex_lock(&aq_data->active_thread.thread_mutex);
while( ++i <= numMessageReceived)
{
LOG(IAQT_LOG,LOG_DEBUG, "waitfor_iaqt_nextMessage 0x%02hhx (%d of %d)\n",msg_type,i,numMessageReceived);
pthread_cond_wait(&aq_data->active_thread.thread_cond, &aq_data->active_thread.thread_mutex);
if( msg_type == NUL || iaqtLastMsg() == msg_type) break;
}
pthread_mutex_unlock(&aq_data->active_thread.thread_mutex);
return iaqtLastMsg();
}
//typedef enum iaqt_control_command_type()
typedef enum {icct_setrpm, icct_settime, icct_setdate} iaqtControlCmdYype;
// Type is always 0 at the moment, haven't found any
void queue_iaqt_control_command(iaqtControlCmdYype type, int num) {
//unsigned char packets[AQ_MAXPKTLEN];
//int cnt;
if (waitfor_iaqt_ctrl_queue2empty() == false)
return;
_iaqt_control_cmd[0] = DEV_MASTER;
_iaqt_control_cmd[1] = 0x24;
_iaqt_control_cmd[2] = 0x31;
_iaqt_control_cmd_len = num2iaqtRSset(&_iaqt_control_cmd[3], num, true);
// Pad with 0xcd for some reason.
for(_iaqt_control_cmd_len = _iaqt_control_cmd_len+3; _iaqt_control_cmd_len <= 18; _iaqt_control_cmd_len++)
_iaqt_control_cmd[_iaqt_control_cmd_len] = 0xcd;
// Tell the control panel we are ready to send this shit.
send_aqt_cmd(ACK_CMD_READY_CTRL);
LOG(IAQT_LOG,LOG_DEBUG, "Queued extended commandsed of length %d\n",_iaqt_control_cmd_len);
//printHex(packets, 19);
//printf("\n");
//send_jandy_command(NULL, packets, cnt);
}
bool queue_iaqt_control_command_str(iaqtControlCmdYype type, char *str) {
if (waitfor_iaqt_ctrl_queue2empty() == false)
return false;
_iaqt_control_cmd[0] = DEV_MASTER;
_iaqt_control_cmd[1] = 0x24;
_iaqt_control_cmd[2] = 0x31;
_iaqt_control_cmd_len = char2iaqtRSset(&_iaqt_control_cmd[3], str, strlen(str));
// Need to bad time for some reason not yet known
if (type == icct_settime) {
//Debug: RS Serial: To 0x00 of type iAq pBut | HEX: 0x10|0x02|0x00|0x24|0x31|0x30|0x31|0x3a|0x30|0x31|0x00|0x30|0x32|0x00|0xcd|0xcd|0xcd|0xcd|0xcd|0xcd|0xcd|0x60|0x10|0x03|
// From position 11 (8 without pre) add 0x30|0x32|0x00
_iaqt_control_cmd_len += 3;
_iaqt_control_cmd[9] = 0x30;
_iaqt_control_cmd[10] = 0x32;
_iaqt_control_cmd[11] = 0x00;
}
// Pad with 0xcd for some reason.
for(_iaqt_control_cmd_len = _iaqt_control_cmd_len+3; _iaqt_control_cmd_len <= 18; _iaqt_control_cmd_len++)
_iaqt_control_cmd[_iaqt_control_cmd_len] = 0xcd;
// Tell the control panel we are ready to send this shit.
send_aqt_cmd(ACK_CMD_READY_CTRL);
return true;
//debuglogPacket()
}
bool goto_iaqt_page(const unsigned char pageID, struct aqualinkdata *aq_data) {
if (iaqtCurrentPage() == pageID)
return true;
// If we go to Other Status Page, do that so we can quit
if (pageID == IAQ_PAGE_STATUS) {
send_aqt_cmd(KEY_IAQTCH_STATUS);
unsigned char rtn = waitfor_iaqt_nextPage(aq_data);
if (rtn != IAQ_PAGE_STATUS && rtn != IAQ_PAGE_STATUS2) {
LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did not find Status page\n");
return false;
}
LOG(IAQT_LOG, LOG_DEBUG, "IAQ Touch got to Status page\n");
return true;
}
if (pageID == IAQ_PAGE_HOME || pageID == IAQ_PAGE_DEVICES) {
if (iaqtCurrentPage() != IAQ_PAGE_HOME) {
send_aqt_cmd(KEY_IAQTCH_HOME);
if (waitfor_iaqt_nextPage(aq_data) != IAQ_PAGE_HOME) {
//LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did not find Home page\n");
// We reset to home page after every programming cycle and error really don't care, so don't throw the error.
return false;
}
LOG(IAQT_LOG, LOG_DEBUG, "IAQ Touch got to Home page\n");
}
if (pageID == IAQ_PAGE_DEVICES) {
send_aqt_cmd(KEY_IAQTCH_HOMEP_KEY08);
if (waitfor_iaqt_nextPage(aq_data) != IAQ_PAGE_DEVICES) {
LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did not find Device page\n");
return false;
}
}
LOG(IAQT_LOG, LOG_DEBUG, "IAQ Touch got to Device page\n");
return true;
} else if (pageID == IAQ_PAGE_MENU || pageID == IAQ_PAGE_SET_TEMP || pageID == IAQ_PAGE_SET_TIME || pageID == IAQ_PAGE_SET_SWG ||
pageID == IAQ_PAGE_SYSTEM_SETUP || pageID == IAQ_PAGE_FREEZE_PROTECT || pageID == IAQ_PAGE_LABEL_AUX || pageID == IAQ_PAGE_VSP_SETUP) {
// All other pages require us to go to Menu page
send_aqt_cmd(KEY_IAQTCH_MENU);
if (waitfor_iaqt_nextPage(aq_data) != IAQ_PAGE_MENU) {
LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did not find Menu page\n");
return false;
} else
LOG(IAQT_LOG, LOG_INFO, "IAQ Touch got to Menu page\n");
// There are several pages from the MENU page that have no other pages,
// So hit those first
if (pageID == IAQ_PAGE_SET_TEMP) {
send_aqt_cmd(KEY_IAQTCH_SET_TEMP);
if (waitfor_iaqt_nextPage(aq_data) != IAQ_PAGE_SET_TEMP) {
LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did not find Set Temp page\n");
return false;
}
LOG(IAQT_LOG, LOG_INFO, "IAQ Touch got to Set Temp page\n");
return true;
}
if (pageID == IAQ_PAGE_SET_TIME) {
send_aqt_cmd(KEY_IAQTCH_SET_DATETIME);
if (waitfor_iaqt_nextPage(aq_data) != IAQ_PAGE_SET_TIME) {
LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did not find Set Time page\n");
return false;
}
LOG(IAQT_LOG, LOG_INFO, "IAQ Touch got to Set Time page\n");
return true;
}
if (pageID == IAQ_PAGE_SET_SWG) {
send_aqt_cmd(KEY_IAQTCH_SET_ACQUAPURE);
if (waitfor_iaqt_nextPage(aq_data) != IAQ_PAGE_SET_SWG) {
LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did not find Aquapure page\n");
return false;
}
LOG(IAQT_LOG, LOG_INFO, "IAQ Touch got to Aquapure page\n");
return true;
}
// All pages now require us to goto System Setup
send_aqt_cmd(KEY_IAQTCH_SYSTEM_SETUP);
if (waitfor_iaqt_nextPage(aq_data) != IAQ_PAGE_SYSTEM_SETUP) {
LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did not find System Setup page\n");
return false;
}
LOG(IAQT_LOG, LOG_INFO, "IAQ Touch got to System Setup page\n");
// Look for menu items for the next pages as they can change
char *menuText;
struct iaqt_page_button *button;
switch (pageID) {
case IAQ_PAGE_FREEZE_PROTECT:
menuText = "Freeze Protection";
break;
case IAQ_PAGE_LABEL_AUX:
menuText = "Label Aux";
break;
case IAQ_PAGE_VSP_SETUP:
menuText = "VSP Setup";
break;
default:
LOG(IAQT_LOG, LOG_ERR, "IAQ Touch unknown menu '0x%02hhx'\n", pageID);
return false;
break;
}
button = iaqtFindButtonByLabel(menuText);
if (button == NULL) {
LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did not find '%s' button on page setup\n", menuText);
return false;
}
// send_aqt_cmd(KEY_IAQTCH_KEY01);
send_aqt_cmd(button->keycode);
if (waitfor_iaqt_nextPage(aq_data) != pageID) {
LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did not find %s page\n", menuText);
return false;
} else
LOG(IAQT_LOG, LOG_INFO, "IAQ Touch got to %s page\n", menuText);
return true;
}
LOG(IAQT_LOG, LOG_ERR, "IAQ Touch unknown menu '0x%02hhx'\n", pageID);
return false;
}
void *set_aqualink_iaqtouch_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 structIndex;
struct iaqt_page_button *button;
//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) {
LOG(IAQT_LOG,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;
}
}
//int pumpRPM = atoi(&buf[2]);
//int pumpIndex = 1;
// Just force to pump 1 for testing
sprintf(VSPstr, "VSP%1d Spd ADJ",pumpIndex);
// NSF Should probably check pumpRPM is not -1 here
waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_IAQTOUCH_PUMP_RPM);
LOG(IAQT_LOG,LOG_NOTICE, "IAQ Touch Set Pump %d to RPM %d\n",pumpIndex,pumpRPM);
if ( goto_iaqt_page(IAQ_PAGE_DEVICES, aq_data) == false )
goto f_end;
button = iaqtFindButtonByLabel(VSPstr);
if (button == NULL) {
LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did not find '%s' button on page setup\n", VSPstr);
goto f_end;
}
send_aqt_cmd(button->keycode);
if (waitfor_iaqt_nextPage(aq_data) != IAQ_PAGE_SET_VSP) {
LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did not find %s page\n", VSPstr);
goto f_end;
}
LOG(IAQT_LOG, LOG_INFO, "IAQ Touch got to %s page\n", VSPstr);
//send_aqt_cmd(ACK_CMD_READY_CTRL);
queue_iaqt_control_command(0, pumpRPM);
waitfor_iaqt_ctrl_queue2empty();
LOG(IAQT_LOG, LOG_INFO, "IAQ Touch got to %s page\n", VSPstr);
// We should get the device page back.
waitfor_iaqt_nextPage(aq_data);
//goto_iaqt_page(IAQ_PAGE_STATUS, aq_data);
/*
// Send Devices button.
send_aqt_cmd(KEY_IAQTCH_HOME);
waitfor_iaqt_queue2empty();
sleep(1);
send_aqt_cmd(KEY_IAQTCH_HOMEP_KEY08);
waitfor_iaqt_queue2empty();
sleep(1);
send_aqt_cmd(KEY_IAQTCH_KEY03);
*/
//send_aqt_cmd(0x80);
// Go to status page on startup to read devices
goto_iaqt_page(IAQ_PAGE_STATUS, aq_data);
f_end:
goto_iaqt_page(IAQ_PAGE_HOME, aq_data);
cleanAndTerminateThread(threadCtrl);
// just stop compiler error, ptr is not valid as it's just been freed
return ptr;
}
void *set_aqualink_iaqtouch_vsp_assignments( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
waitForSingleThreadOrTerminate(threadCtrl, AQ_GET_IAQTOUCH_VSP_ASSIGNMENT);
if ( goto_iaqt_page(IAQ_PAGE_VSP_SETUP, aq_data) == false )
goto f_end;
/* Info: Button 00| ePump | type=0xff | state=0x00 | unknown=0xff
* Info: Button 01| Intelliflo VF | type=0xff | state=0x00 | unknown=0xff
* Info: Button 02| Intelliflo VS | type=0xff | state=0x00 | unknown=0xff
* Info: Button 03| ePump | type=0xff | state=0x00 | unknown=0xff
* ---- This is next column
* Info: Button 04| Filtration | type=0xff | state=0x00 | unknown=0xff
* Info: Button 05| Filtration | type=0xff | state=0x00 | unknown=0xff
* Info: Button 06| Filtration | type=0xff | state=0x00 | unknown=0xff
* Info: Button 07| Not Installed | type=0xff | state=0x00 | unknown=0xff
* ----- Next column
* Info: Button 08| 600 RPM | type=0xff | state=0x00 | unknown=0xff
* Info: Button 09| 15 GPM | type=0xff | state=0x00 | unknown=0xff
* Info: Button 10| 600 RPM | type=0xff | state=0x00 | unknown=0xff
* Info: Button 11| 600 RPM | type=0xff | state=0x00 | unknown=0xff
* ----- Next colums
* Info: Button 12| 3450 RPM | type=0xff | state=0x00 | unknown=0xff
* Info: Button 13| 130 GPM | type=0xff | state=0x00 | unknown=0xff
* Info: Button 14| 3450 RPM | type=0xff | state=0x00 | unknown=0xff
* Info: Button 15| 3450 RPM | type=0xff | state=0x00 | unknown=0xff
* ----- Next column
* Info: Button 16| 2500 | type=0xff | state=0x00 | unknown=0xff
* Info: Button 17| N/A | type=0xff | state=0x00 | unknown=0xff
* Info: Button 18| 2500 | type=0xff | state=0x00 | unknown=0xff
* Info: Button 19| 2500 | type=0xff | state=0x00 | unknown=0xff
* ----- Next column
* Info: Button 20| :03 | type=0xff | state=0x00 | unknown=0xff
* Info: Button 21| N/A | type=0xff | state=0x00 | unknown=0xff
* Info: Button 22| :03 | type=0xff | state=0x00 | unknown=0xff
* Info: Button 23| :03 | type=0xff | state=0x00 | unknown=0xff
*
*/
f_end:
goto_iaqt_page(IAQ_PAGE_HOME, aq_data);
cleanAndTerminateThread(threadCtrl);
// just stop compiler error, ptr is not valid as it's just been freed
return ptr;
}
void *get_aqualink_iaqtouch_setpoints( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
struct iaqt_page_button *button;
waitForSingleThreadOrTerminate(threadCtrl, AQ_GET_IAQTOUCH_SETPOINTS);
if ( goto_iaqt_page(IAQ_PAGE_SET_TEMP, aq_data) == false )
goto f_end;
// Button 0 is "Pool Heat 50"
// Button 2 is "Spa Heat 100"
button = iaqtFindButtonByLabel("Pool Heat");
if (button != NULL) {
aq_data->pool_htr_set_point = rsm_atoi((char *)&button->name + 9);
LOG(IAQT_LOG,LOG_DEBUG, "IAQ Touch got to Pool heater setpoint %d\n",aq_data->pool_htr_set_point);
} else {
button = iaqtFindButtonByLabel("Temp1");
if (button != NULL) {
aq_data->pool_htr_set_point = rsm_atoi((char *)&button->name + 5);
LOG(IAQT_LOG,LOG_DEBUG, "IAQ Touch got to Temp1 setpoint %d\n",aq_data->pool_htr_set_point);
if (isSINGLE_DEV_PANEL != true)
{
changePanelToMode_Only();
LOG(AQRS_LOG,LOG_ERR, "AqualinkD set to 'Combo Pool & Spa' but detected 'Only Pool OR Spa' panel, please change config\n");
}
}
}
button = iaqtFindButtonByLabel("Spa Heat");
if (button != NULL) {
aq_data->spa_htr_set_point = rsm_atoi((char *)&button->name + 8);
LOG(IAQT_LOG,LOG_DEBUG, "IAQ Touch got to Spa heater setpoint %d\n",aq_data->spa_htr_set_point);
} else {
button = iaqtFindButtonByLabel("Temp2");
if (button != NULL) {
aq_data->spa_htr_set_point = rsm_atoi((char *)&button->name + 5);
LOG(IAQT_LOG,LOG_DEBUG, "IAQ Touch got to Temp2 setpoint %d\n",aq_data->spa_htr_set_point);
if (isSINGLE_DEV_PANEL != true)
{
changePanelToMode_Only();
LOG(AQRS_LOG,LOG_ERR, "AqualinkD set to 'Combo Pool & Spa' but detected 'Only Pool OR Spa' panel, please change config\n");
}
}
}
if ( goto_iaqt_page(IAQ_PAGE_FREEZE_PROTECT, aq_data) == false )
goto f_end;
// The Message at index 0 is the deg that freeze protect is set to.
int frz = rsm_atoi(iaqtGetMessageLine(0));
if (frz >= 0) {
aq_data->frz_protect_set_point = frz;
LOG(IAQT_LOG,LOG_NOTICE, "IAQ Touch Freeze Protection setpoint %d\n",frz);
}
// Need to run over table messages and check ens with X for on off.
// Go to status page on startup to read devices
goto_iaqt_page(IAQ_PAGE_STATUS, aq_data);
f_end:
goto_iaqt_page(IAQ_PAGE_HOME, aq_data);
cleanAndTerminateThread(threadCtrl);
// just stop compiler error, ptr is not valid as it's just been freed
return ptr;
}
// THIS IS NOT FINISHED.
void *get_aqualink_iaqtouch_aux_labels( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
int i;
waitForSingleThreadOrTerminate(threadCtrl, AQ_GET_IAQTOUCH_AUX_LABELS);
if ( goto_iaqt_page(IAQ_PAGE_LABEL_AUX, aq_data) == false )
goto f_end;
// Need to loop over messages. Tab 0x09 is next in each message
/*
* Info: Table Messages 00| Circuit Circuit Name Freeze
* Info: Table Messages 01| Aux1 Aux1 No
* Info: Table Messages 02| Aux2 Aux2 No
* Info: Table Messages 03| Aux3 Pool Light No
* Info: Table Messages 04| Aux4 Aux4 No
* Info: Table Messages 05| Aux5 Aux5 No
* Info: Table Messages 06| Aux6 Aux6 No
* Info: Table Messages 07| Aux7 Aux7 No
*
* Info: Table Messages ??| Aux B7 Aux B7 No
*/
const char *buf;
int aux;
// Loop over panel buttons or lines which ever is lowest.
//for(i=1; i < 18; i++) // NSF Need to take out hard code of 18
for(i=1; i < PANEL_SIZE(); i++)
{
buf = iaqtGetTableInfoLine(i);
LOG(IAQT_LOG,LOG_INFO, "From Messages %.2d| %s\n",i,buf);
//printf("**** BUF '%s'\n",aux,buf);
aux = rsm_atoi(buf + 3);
LOG(IAQT_LOG,LOG_INFO, "**** AUX %d = '%s'\n",aux,buf + 5);
}
f_end:
goto_iaqt_page(IAQ_PAGE_HOME, aq_data);
cleanAndTerminateThread(threadCtrl);
return ptr;
}
bool set_aqualink_iaqtouch_aquapure( struct aqualinkdata *aq_data, bool boost, int val )
{
struct iaqt_page_button *button;
static unsigned char b_pool = NUL;
static unsigned char b_spa = NUL;
static unsigned char b_boost = NUL;
if ( goto_iaqt_page(IAQ_PAGE_SET_SWG, aq_data) == false )
return false;
// IF pool button not set, first time here, need to cache the buttons
// as we only get them once.
if (b_pool == NUL) {
button = iaqtFindButtonByLabel("Pool");
if (button != NULL)
b_pool = button->keycode;
button = iaqtFindButtonByLabel("Spa");
if (button != NULL)
b_spa = button->keycode;
button = iaqtFindButtonByLabel("Quick Boost");
if (button != NULL)
b_boost = button->keycode;
}
// If spa is on, set SWG for spa, if not set SWG for pool
if (boost) {
if (b_boost != NUL)
send_aqt_cmd(b_boost);
else {
LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did not find Boost button on SWG page\n");
return false;
}
waitfor_iaqt_queue2empty();
if (waitfor_iaqt_nextPage(aq_data) != IAQ_PAGE_SET_QBOOST) {
LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did not find Boost Start button on SWG page\n");
}
if(val==true)
button = iaqtFindButtonByLabel("Start");
else
button = iaqtFindButtonByLabel("Stop");
send_aqt_cmd(button->keycode);
waitfor_iaqt_queue2empty();
waitfor_iaqt_nextPage(aq_data);
aq_data->boost = val;
} else {
if (aq_data->aqbuttons[SPA_INDEX].led->state != OFF) {
if (b_spa != NUL)
send_aqt_cmd(b_spa);
else {
LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did not find Spa button on SWG page\n");
return false;
}
} else {
if (b_pool != NUL)
send_aqt_cmd(b_pool);
else {
LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did not find Pool button on SWG page\n");
return false;;
}
}
waitfor_iaqt_queue2empty();
queue_iaqt_control_command(0, val);
waitfor_iaqt_ctrl_queue2empty();
waitfor_iaqt_nextMessage(aq_data, CMD_IAQ_PAGE_BUTTON);
}
//LOG(IAQT_LOG, LOG_NOTICE, "IAQ Touch got to %s page\n", VSPstr);
// We should get the device page back.
//waitfor_iaqt_nextPage(aq_data);
goto_iaqt_page(IAQ_PAGE_STATUS, aq_data);
return true;
}
void *set_aqualink_iaqtouch_swg_percent( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_IAQTOUCH_SWG_PERCENT);
int val = atoi((char*)threadCtrl->thread_args);
val = setpoint_check(SWG_SETPOINT, val, aq_data);
if (set_aqualink_iaqtouch_aquapure(aq_data, false, val))
setSWGpercent(aq_data, val);
goto_iaqt_page(IAQ_PAGE_HOME, aq_data);
cleanAndTerminateThread(threadCtrl);
return ptr;
}
void *set_aqualink_iaqtouch_swg_boost( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_IAQTOUCH_SWG_BOOST);
int val = atoi((char*)threadCtrl->thread_args);
//logMessage(LOG_DEBUG, "programming BOOST to %s\n", val==true?"On":"Off");
set_aqualink_iaqtouch_aquapure(aq_data, true, val);
goto_iaqt_page(IAQ_PAGE_HOME, aq_data);
cleanAndTerminateThread(threadCtrl);
return ptr;
}
bool set_aqualink_iaqtouch_heater_setpoint( struct aqualinkdata *aq_data, bool pool, int val)
{
struct iaqt_page_button *button;
char *name;
if ( goto_iaqt_page(IAQ_PAGE_SET_TEMP, aq_data) == false )
return false;
if (isCOMBO_PANEL) {
if (pool)
name = "Pool Heat";
else
name = "Spa Heat";
} else {
if (pool)
name = "Temp1";
else
name = "Temp2";
}
button = iaqtFindButtonByLabel(name);
if (button == NULL) {
//aq_data->pool_htr_set_point = rsm_atoi((char *)&button->name + 9);
LOG(IAQT_LOG,LOG_ERR, "IAQ Touch did not find heater setpoint '%s' on page\n",name);
return false;
}
send_aqt_cmd(button->keycode);
waitfor_iaqt_queue2empty();
queue_iaqt_control_command(0, val);
waitfor_iaqt_ctrl_queue2empty();
waitfor_iaqt_nextMessage(aq_data, CMD_IAQ_PAGE_BUTTON);
button = iaqtFindButtonByLabel(name);
if (button != NULL) {
if (pool)
aq_data->pool_htr_set_point = rsm_atoi((char *)&button->name + strlen(name));
else
aq_data->spa_htr_set_point = rsm_atoi((char *)&button->name + strlen(name));
LOG(IAQT_LOG,LOG_DEBUG, "IAQ Touch set %s heater setpoint to %d\n",name,pool?aq_data->pool_htr_set_point:aq_data->spa_htr_set_point);
}
return true;
}
void *set_aqualink_iaqtouch_spa_heater_temp( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_IAQTOUCH_SPA_HEATER_TEMP);
int val = atoi((char*)threadCtrl->thread_args);
val = setpoint_check(SPA_HTR_SETOINT, val, aq_data);
set_aqualink_iaqtouch_heater_setpoint(aq_data, false, val);
goto_iaqt_page(IAQ_PAGE_HOME, aq_data);
cleanAndTerminateThread(threadCtrl);
return ptr;
}
void *set_aqualink_iaqtouch_pool_heater_temp( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_IAQTOUCH_POOL_HEATER_TEMP);
int val = atoi((char*)threadCtrl->thread_args);
val = setpoint_check(POOL_HTR_SETOINT, val, aq_data);
set_aqualink_iaqtouch_heater_setpoint(aq_data, true, val);
goto_iaqt_page(IAQ_PAGE_HOME, aq_data);
cleanAndTerminateThread(threadCtrl);
return ptr;
}
void *set_aqualink_iaqtouch_pump_vs_program( 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 structIndex;
struct iaqt_page_button *button;
waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_IAQTOUCH_PUMP_VS_PROGRAM);
int pumpIndex = atoi(&buf[0]);
//int pumpRPM = -1;
int vspindex = 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) {
LOG(IAQT_LOG,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;
}
}
//int pumpRPM = atoi(&buf[2]);
//int pumpIndex = 1;
sprintf(VSPstr, "VSP%1d Spd ADJ",pumpIndex);
LOG(IAQT_LOG,LOG_NOTICE, "Set Pump %d to VSP Index %d\n",pumpIndex,vspindex);
if ( goto_iaqt_page(IAQ_PAGE_DEVICES, aq_data) == false )
goto f_end;
button = iaqtFindButtonByLabel(VSPstr);
if (button == NULL) {
LOG(IAQT_LOG, LOG_ERR, "Did not find '%s' button on page setup\n", VSPstr);
goto f_end;
}
send_aqt_cmd(button->keycode);
if (waitfor_iaqt_nextPage(aq_data) != IAQ_PAGE_SET_VSP) {
LOG(IAQT_LOG, LOG_ERR, "Did not find %s page\n", VSPstr);
goto f_end;
}
LOG(IAQT_LOG, LOG_INFO, "Got to %s page\n", VSPstr);
// Select the button index.
button = iaqtFindButtonByIndex(vspindex);
if (button == NULL) {
LOG(IAQT_LOG, LOG_ERR, "Did not find '%d' button on page\n", vspindex);
goto f_end;
}
send_aqt_cmd(button->keycode);
waitfor_iaqt_queue2empty();
// Probably wait.
f_end:
goto_iaqt_page(IAQ_PAGE_HOME, aq_data);
cleanAndTerminateThread(threadCtrl);
return ptr;
}
void *set_aqualink_iaqtouch_time( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
struct iaqt_page_button *button;
char buf[20];
//int len;
int i;
//bool AM;
waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_IAQTOUCH_SET_TIME);
time_t now = time(0); // get time now
struct tm *result = localtime(&now);
if ( goto_iaqt_page(IAQ_PAGE_SET_TIME, aq_data) == false ) {
LOG(IAQT_LOG,LOG_ERR, "IAQ Touch didn't find set time page\n");
goto f_end;
}
// Button 0 is date.
button = iaqtFindButtonByIndex(0);
if (button == NULL) {
LOG(IAQT_LOG,LOG_ERR, "IAQ Touch date button on set time page\n");
goto f_end;
}
// Print DD/MM/YY into string
strftime(buf, 20, "%D", result);
// Do we need to set the date
if (rsm_strcmp(buf, (char *)button->name) != 0) {
// Press date button.
send_aqt_cmd(button->keycode);
waitfor_iaqt_queue2empty();
// Queue the date string
if ( queue_iaqt_control_command_str(icct_setdate, buf)) {
LOG(IAQT_LOG,LOG_NOTICE, "Set date to %s\n",buf);
waitfor_iaqt_ctrl_queue2empty();
} else {
LOG(IAQT_LOG,LOG_ERR, "Failed to queue commandset for setting date\n");
}
} else {
LOG(IAQT_LOG,LOG_DEBUG, "Date %s is accurate, not changing\n",button->name);
}
// Always assume time is incorrect if wer have been called.
// Button 1 is time.
button = iaqtFindButtonByIndex(1);
if (button == NULL) {
LOG(IAQT_LOG,LOG_ERR, "IAQ Touch time button on set time page\n");
goto f_end;
}
// Press time button.
send_aqt_cmd(button->keycode);
waitfor_iaqt_queue2empty();
// Set AM/PM button, not sure how to get default state since it's blank to start, so keep pressing till we get AM or PM we want
i = 0;
strftime(buf, 20, "%p", result);
LOG(IAQT_LOG,LOG_DEBUG, "IAQ Touch AM/PM check '%s' to '%s'\n",buf,iaqtGetMessageLine(2));
while ( rsm_strcmp(buf, iaqtGetMessageLine(2)) != 0 && i < 3) {
send_aqt_cmd(0x13);
waitfor_iaqt_queue2empty();
waitfor_iaqt_nextMessage(aq_data, CMD_IAQ_PAGE_MSG);
i++;
}
LOG(IAQT_LOG,LOG_DEBUG, "IAQ Touch AM/PM is now '%s'\n",iaqtGetMessageLine(2));
// Print HH:MM into string
strftime(buf, 20, "%I:%M", result);
if (queue_iaqt_control_command_str(icct_settime, buf)) {
LOG(IAQT_LOG,LOG_NOTICE, "Set time to %s\n",buf);
waitfor_iaqt_ctrl_queue2empty();
} else {
LOG(IAQT_LOG,LOG_ERR, "Failed to queue commandset for setting time\n");
}
f_end:
goto_iaqt_page(IAQ_PAGE_HOME, aq_data);
cleanAndTerminateThread(threadCtrl);
return ptr;
}
/* Infor on setting time
//Info: iAQ Touch: Button 00| 07/05/20 | type=0x01 | state=0x00 | unknown=0x00 | keycode=0x11
//Info: iAQ Touch: Button 01| 1:02 PM | type=0x01 | state=0x00 | unknown=0x00 | keycode=0x12
// Send button to select time / date.
// Date
//Debug: RS Serial: To 0x00 of type Ack | HEX: 0x10|0x02|0x00|0x01|0x00|0x11|0x24|0x10|0x03|
// Set Time
//Debug: RS Serial: To 0x00 of type Ack | HEX: 0x10|0x02|0x00|0x01|0x00|0x12|0x25|0x10|0x03|
// Wait for Time page comes back
//Info: iAQ Touch: Page: Set Time | 0x4b
// Set time to 01/01/01
//Debug: RS Serial: To 0x00 of type Ack | HEX: 0x10|0x02|0x00|0x01|0x00|0x80|0x93|0x10|0x03|
//Debug: iAQ Touch: To 0x33 of type Unknown | HEX: 0x10|0x02|0x33|0x31|0x01|0x77|0x10|0x03|
//Debug: RS Serial: To 0x00 of type iAq pBut | HEX: 0x10|0x02|0x00|0x24|0x31|0x30|0x31|0x2f|0x30|0x31|0x2f|0x30|0x31|0x00|0xcd|0xcd|0xcd|0xcd|0xcd|0xcd|0xcd|0x83|0x10|0x03|
//0x30|0x31|0x2f|0x30|0x31|0x2f|0x30|0x31|
// Set Time to 02/02/02
//Debug: RS Serial: To 0x00 of type Ack | HEX: 0x10|0x02|0x00|0x01|0x00|0x80|0x93|0x10|0x03|
//Debug: iAQ Touch: To 0x33 of type Unknown | HEX: 0x10|0x02|0x33|0x31|0x01|0x77|0x10|0x03|
//Debug: RS Serial: To 0x00 of type iAq pBut | HEX: 0x10|0x02|0x00|0x24|0x31|0x30|0x32|0x2f|0x30|0x32|0x2f|0x30|0x32|0x00|0xcd|0xcd|0xcd|0xcd|0xcd|0xcd|0xcd|0x86|0x10|0x03|
// So 0x30 is base numbers again (ie 0)
//0x24|0x31 Set time & start?
//0x2f is seperator or /
//0x00 is end
//0xcd padding
// Set time 01:01 AM
//Debug: RS Serial: To 0x00 of type Ack | HEX: 0x10|0x02|0x00|0x01|0x00|0x80|0x93|0x10|0x03|
//Debug: iAQ Touch: To 0x33 of type Unknown | HEX: 0x10|0x02|0x33|0x31|0x01|0x77|0x10|0x03|
//Debug: RS Serial: To 0x00 of type iAq pBut | HEX: 0x10|0x02|0x00|0x24|0x31|0x30|0x31|0x3a|0x30|0x31|0x00|0x30|0x32|0x00|0xcd|0xcd|0xcd|0xcd|0xcd|0xcd|0xcd|0x60|0x10|0x03|
// Set time 01:01 PM
//Debug: RS Serial: To 0x00 of type Ack | HEX: 0x10|0x02|0x00|0x01|0x00|0x80|0x93|0x10|0x03|
//Debug: iAQ Touch: To 0x33 of type Unknown | HEX: 0x10|0x02|0x33|0x31|0x01|0x77|0x10|0x03|
//Debug: RS Serial: To 0x00 of type iAq pBut | HEX: 0x10|0x02|0x00|0x24|0x31|0x30|0x31|0x3a|0x30|0x31|0x00|0x30|0x32|0x00|0xcd|0xcd|0xcd|0xcd|0xcd|0xcd|0xcd|0x60|0x10|0x03|
// Set time 09:55
//Debug: RS Serial: To 0x00 of type Ack | HEX: 0x10|0x02|0x00|0x01|0x00|0x80|0x93|0x10|0x03|
//Debug: iAQ Touch: To 0x33 of type Unknown | HEX: 0x10|0x02|0x33|0x31|0x01|0x77|0x10|0x03|
//Debug: RS Serial: To 0x00 of type iAq pBut | HEX: 0x10|0x02|0x00|0x24|0x31|0x30|0x39|0x3a|0x35|0x35|0x00|0x30|0x32|0x00|0xcd|0xcd|0xcd|0xcd|0xcd|0xcd|0xcd|0x71|0x10|0x03|
// 0x3a seperator :
// Select PM
// Debug: RS Serial: To 0x00 of type Ack | HEX: 0x10|0x02|0x00|0x01|0x00|0x13|0x26|0x10|0x03|
// Debug: iAQ Touch: To 0x33 of type iAq pMes | HEX: 0x10|0x02|0x33|0x25|0x02|0x50|0x4d|0x00|0x09|0x10|0x03|
// Select AM
// Debug: RS Serial: To 0x00 of type Ack | HEX: 0x10|0x02|0x00|0x01|0x00|0x13|0x26|0x10|0x03|
// Debug: iAQ Touch: To 0x33 of type iAq pMes | HEX: 0x10|0x02|0x33|0x25|0x02|0x41|0x4d|0x00|0xfa|0x10|0x03|
//LOG(IAQT_LOG,LOG_DEBUG "Setting time to %d/%d/%d %d:%d\n", result->tm_mon + 1, result->tm_mday, result->tm_year + 1900, result->tm_hour + 1, result->tm_min);
*/