Version 1.3.0

pull/66/head
shaun feakes 2019-05-25 11:52:36 -05:00
parent d0289b79c3
commit d884f2e37c
26 changed files with 2206 additions and 793 deletions

View File

@ -29,7 +29,7 @@ INCLUDES = -I/nas/data/Development/Raspberry/aqualink/aqualinkd
# 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_menu.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 mongoose.c
SL_SRC = serial_logger.c aq_serial.c utils.c
PDA_SRC = pda_test.c pda_menu.c aq_serial.c utils.c

View File

@ -59,15 +59,26 @@ Designed to mimic AqualinkRS6 All Button keypad, and just like the keypad you ca
<img src="extras/HomeAssistant.png?raw=true" width="600"></img>
## All Web interfaces.
* http://aqualink.ip/ <- (New UI)
* http://aqualink.ip/old <- (If you prefer the old UI, this is not maintained)
* http://aqualink.ip/simple.html <- (Anothr opion if you don't like the above)
* 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)
#<a name="release"></a>
## Update in Release 1.3.0
* Large update for PDA only control panels (Majority of this is ballle98 work)
* Can distinguish between AquaPalm and PDA supported control panels.
* PDA Freeze & Heater setpoints now supported.
* Added PDA Sleep mode so AqualinkD can work inconjunction with a real Jandy PDA.
* Speeded up many PDA functions.
* Fixed many PDA bugs.
* Non PDA specific updates :-
* Can get button labels from control panel (not in PDA mode)
* RS485 Logging so users can submit information on Variable Speed Pumps & other devices for future support.
* Force SWG status on startup, rather than wait for pump to turn on.
* General bug fixes and improved code in many areas.
## Update in Release 1.2.6f
* Solution to overcome bug in Mosquitto 1.6.
* Fixed Salt Water Generator when % was set to 0.
* Added support for different SWG % for pool & spa.
* Added support for different SWG % for pool & spa. (SWG reports and sets the mode that's currently active)
* Increased speed of SWG messages.
* Few other bug fixes (Thanks to ballle98)
## Update in Release 1.2.6e (This is a quick update, please only use if you need one of the items below.)

View File

@ -26,12 +26,14 @@
#include "utils.h"
#include "aq_programmer.h"
#include "aq_serial.h"
#include "pda.h"
#include "pda_menu.h"
#include "init_buttons.h"
#include "pda_aq_programmer.h"
bool select_sub_menu_item(struct aqualinkdata *aq_data, char* item_string);
bool select_menu_item(struct aqualinkdata *aq_data, char* item_string);
void send_cmd(unsigned char cmd, struct aqualinkdata *aq_data);
//void send_cmd(unsigned char cmd, struct aqualinkdata *aq_data);
void cancel_menu(struct aqualinkdata *aq_data);
@ -43,17 +45,18 @@ void *get_aqualink_pool_spa_heater_temps( void *ptr );
void *get_aqualink_programs( void *ptr );
void *get_freeze_protect_temp( void *ptr );
void *get_aqualink_diag_model( void *ptr );
void *get_aqualink_aux_labels( void *ptr );
void *threadded_send_cmd( void *ptr );
void *set_aqualink_light_colormode( void *ptr );
void *set_aqualink_PDA_init( void *ptr );
void *set_aqualink_SWG( void *ptr );
void *get_aqualink_PDA_device_status( void *ptr );
void *set_aqualink_PDA_device_on_off( void *ptr );
//void *get_aqualink_PDA_device_status( void *ptr );
//void *set_aqualink_PDA_device_on_off( void *ptr );
bool waitForButtonState(struct aqualinkdata *aq_data, aqkey* button, aqledstate state, int numMessageReceived);
bool waitForMessage(struct aqualinkdata *aq_data, char* message, int numMessageReceived);
//bool waitForMessage(struct aqualinkdata *aq_data, char* message, int numMessageReceived);
bool waitForEitherMessage(struct aqualinkdata *aq_data, char* message1, char* message2, int numMessageReceived);
bool push_aq_cmd(unsigned char cmd);
@ -204,9 +207,15 @@ void aq_programmer(program_type type, char *args, struct aqualinkdata *aq_data)
struct programmingThreadCtrl *programmingthread = malloc(sizeof(struct programmingThreadCtrl));
if (pda_mode() == true) {
pda_reset_sleep();
if (type != AQ_PDA_INIT &&
type != AQ_PDA_WAKE_INIT &&
type != AQ_PDA_DEVICE_STATUS &&
type != AQ_PDA_DEVICE_ON_OFF) {
type != AQ_SET_POOL_HEATER_TEMP &&
type != AQ_SET_SPA_HEATER_TEMP &&
type != AQ_SET_SWG_PERCENT &&
type != AQ_PDA_DEVICE_ON_OFF &&
type != AQ_GET_POOL_SPA_HEATER_TEMPS ) {
logMessage(LOG_ERR, "Selected Programming mode '%d' not supported with PDA mode control panel\n",type);
return;
}
@ -289,6 +298,12 @@ void aq_programmer(program_type type, char *args, struct aqualinkdata *aq_data)
return;
}
break;
case AQ_PDA_WAKE_INIT:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_PDA_wakeinit, (void*)programmingthread) < 0) {
logMessage (LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_SET_SWG_PERCENT:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_SWG, (void*)programmingthread) < 0) {
logMessage (LOG_ERR, "could not create thread\n");
@ -307,6 +322,12 @@ void aq_programmer(program_type type, char *args, struct aqualinkdata *aq_data)
return;
}
break;
case AQ_GET_AUX_LABELS:
if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_aux_labels, (void*)programmingthread) < 0) {
logMessage (LOG_ERR, "could not create thread\n");
return;
}
break;
default:
logMessage (LOG_ERR, "Don't understand thread type\n");
break;
@ -478,17 +499,13 @@ void *set_aqualink_SWG( void *ptr )
int val = atoi((char*)threadCtrl->thread_args);
val = setpoint_check(SWG_SETPOINT, val, aq_data);
// Just recheck it's in multiple of 5.
/*
if (0 != (val % 5) )
val = ((val + 5) / 10) * 10;
if (val > SWG_PERCENT_MAX) {
val = SWG_PERCENT_MAX;
} else if ( val < SWG_PERCENT_MIN) {
val = SWG_PERCENT_MIN;
if (pda_mode() == true) {
set_PDA_aqualink_SWG_setpoint(aq_data, val);
cleanAndTerminateThread(threadCtrl);
return ptr;
}
*/
logMessage(LOG_DEBUG, "programming SWG percent to %d\n", val);
if ( select_menu_item(aq_data, "SET AQUAPURE") != true ) {
@ -541,258 +558,37 @@ void *set_aqualink_SWG( void *ptr )
return ptr;
}
bool select_pda_main_menu(struct aqualinkdata *aq_data)
{
int i=0;
// Check to see if we are at the main menu
if (pda_m_type() == PM_MAIN) {
return true;
}
// First send back
send_cmd(KEY_PDA_BACK, aq_data);
while (_pgm_command != NUL) {
delay(500);
if (i++ > 6) return false;
}
//delay(1000);
i=0;
while (pda_m_type() != PM_MAIN) {
delay(500);
if (i++ > 6) return false;
}
return true;
}
bool wait_pda_selected_item()
{
int i=0;
i=0;
while (pda_m_hlightindex() == -1){
if (i++ > 10)
break;
delay(100);
}
if (pda_m_hlightindex() == -1)
return false;
else
return true;
}
bool select_pda_main_menu_item(struct aqualinkdata *aq_data, pda_menu_type menu_item)
{
int i=0;
char *menu;
if (! select_pda_main_menu(aq_data))
return false;
logMessage(LOG_DEBUG, "PDA Device programmer at main menu\n");
if (menu_item == PM_MAIN)
return true;
else if (menu_item == PM_SETTINGS)
menu = "MENU";
else if (menu_item == PM_EQUIPTMENT_CONTROL)
menu = "EQUIPMENT ON/OFF";
else
return false;
if (!wait_pda_selected_item()){
logMessage(LOG_ERR, "PDA Device programmer didn't find a selected item\n");
return false;
}
while ( strncmp(pda_m_hlight(), menu, strlen(menu)) != 0 ) {
if (_pgm_command == NUL) {
send_cmd(KEY_PDA_DOWN, aq_data);
logMessage(LOG_DEBUG, "PDA Device programmer selected sub menu\n");
waitForMessage(aq_data, NULL, 1);
}
if (i++ > (PDA_LINES * 2))
return false;
delay(500);
}
send_cmd(KEY_PDA_SELECT, aq_data);
while (_pgm_command != NUL) { delay(500); }
return true;
/*
send_cmd(KEY_PDA_DOWN, aq_data);
while (_pgm_command != NUL) { delay(500); }
*/
}
void *set_aqualink_PDA_device_on_off( void *ptr )
void *get_aqualink_aux_labels( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
int i=0;
int found;
waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_DEVICE_STATUS);
waitForSingleThreadOrTerminate(threadCtrl, AQ_GET_AUX_LABELS);
char *buf = (char*)threadCtrl->thread_args;
int device = atoi(&buf[0]);
int state = atoi(&buf[5]);
if (device < 0 || device > TOTAL_BUTTONS) {
logMessage(LOG_ERR, "PDA Device On/Off :- bad device number '%d'\n",device);
if ( select_menu_item(aq_data, "REVIEW") != true ) {
logMessage(LOG_WARNING, "Could not select REVIEW menu\n");
cancel_menu(aq_data);
cleanAndTerminateThread(threadCtrl);
return ptr;
}
logMessage(LOG_INFO, "PDA Device On/Off, device '%s', state %d\n",aq_data->aqbuttons[device].pda_label,state);
//printf("DEVICE LABEL = %s\n",aq_data->aqbuttons[device].pda_label);
if (! select_pda_main_menu_item(aq_data, PM_EQUIPTMENT_CONTROL)) {
logMessage(LOG_ERR, "PDA Device On/Off :- can't find main menu\n");
if (select_sub_menu_item(aq_data, "AUX LABELS") != true) {
logMessage(LOG_WARNING, "Could not select AUX LABELS menu\n");
cancel_menu(aq_data);
cleanAndTerminateThread(threadCtrl);
return ptr;
}
/*
i=0;
while (pda_m_hlightindex() == -1){
if (i++ > 10)
break;
delay(100);
}
*/
delay(500);
printf("Wait for select\n");
if (!wait_pda_selected_item()){
logMessage(LOG_ERR, "PDA Device programmer didn't find a selected item\n");
return false;
}
printf("End wait select\n");
i=0;
char labelBuff[AQ_MSGLEN];
strncpy(labelBuff, pda_m_hlight(), AQ_MSGLEN-4);
labelBuff[AQ_MSGLEN-4] = 0;
while ( (found = strcasecmp(stripwhitespace(labelBuff), aq_data->aqbuttons[device].pda_label)) != 0 ) {
if (_pgm_command == NUL) {
send_cmd(KEY_PDA_DOWN, aq_data);
//printf("*** Send Down for %s ***\n",pda_m_hlight());
waitForMessage(aq_data, NULL, 1);
}
if (i++ > (PDA_LINES * 2)) {
break;
}
delay(500);
strncpy(labelBuff, pda_m_hlight(), AQ_MSGLEN-4);
labelBuff[AQ_MSGLEN-4] = 0;
}
if (found == 0) {
//printf("*** FOUND ITEM %s ***\n",pda_m_hlight());
if (aq_data->aqbuttons[device].led->state != state) {
//printf("*** Select State ***\n");
logMessage(LOG_INFO, "PDA Device On/Off, found device '%s', changing state\n",aq_data->aqbuttons[device].pda_label,state);
send_cmd(KEY_PDA_SELECT, aq_data);
while (_pgm_command != NUL) { delay(500); }
} else {
logMessage(LOG_INFO, "PDA Device On/Off, found device '%s', not changing state, is same\n",aq_data->aqbuttons[device].pda_label,state);
}
} else {
//printf("*** NOT FOUND ITEM ***\n");
logMessage(LOG_ERR, "PDA Device On/Off, device '%s' not found\n",aq_data->aqbuttons[device].pda_label);
}
select_pda_main_menu_item(aq_data, PM_MAIN);
//while (_pgm_command != NUL) { delay(500); }
waitForMessage(aq_data, NULL, 5); // Receive 5 messages
cleanAndTerminateThread(threadCtrl);
// just stop compiler error, ptr is not valid as it's just been freed
return ptr;
}
void *get_aqualink_PDA_device_status( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
int i;
waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_DEVICE_STATUS);
//int val = atoi((char*)threadCtrl->thread_args);
logMessage(LOG_DEBUG, "PDA Device Status\n");
if (! select_pda_main_menu_item(aq_data, PM_EQUIPTMENT_CONTROL)) {
logMessage(LOG_ERR, "PDA Device Status :- can't find main menu\n");
cleanAndTerminateThread(threadCtrl);
return ptr;
}
//select_pda_main_menu_item(aq_data, "EQUIPMENT ON/OFF");
// Just loop over all the dvices 18 times should do it.
for (i=0; i < 18; i++) {
send_cmd(KEY_PDA_DOWN, aq_data);
while (_pgm_command != NUL) { delay(100); }
}
//printf("*** GET MAIN MENU ***\n");
select_pda_main_menu_item(aq_data, PM_MAIN);
//printf("*** FINISHED ***\n");
/*
send_cmd(KEY_PDA_BACK, aq_data);
while (_pgm_command != NUL) { delay(500); }
*/
cleanAndTerminateThread(threadCtrl);
// just stop compiler error, ptr is not valid as it's just been freed
return ptr;
}
void *set_aqualink_PDA_init( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
int i=0;
waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_INIT);
//int val = atoi((char*)threadCtrl->thread_args);
//logMessage(LOG_DEBUG, "PDA Init\n", val);
logMessage(LOG_DEBUG, "PDA Init\n");
if (! select_pda_main_menu_item(aq_data, PM_EQUIPTMENT_CONTROL)) {
logMessage(LOG_ERR, "PDA Init :- can't find main menu\n");
cleanAndTerminateThread(threadCtrl);
return ptr;
}
//select_pda_main_menu_item(aq_data, "EQUIPMENT ON/OFF");
// Just loop over all the dvices 20 times should do it.
for (i=0; i < 18; i++) {
send_cmd(KEY_PDA_DOWN, aq_data);
while (_pgm_command != NUL) { delay(500); }
}
select_pda_main_menu_item(aq_data, PM_MAIN);
printf("*** PDA Init :- add code to find setpoints ***\n");
// Run through menu and find freeze setpoints / heater setpoints etc.
cleanAndTerminateThread(threadCtrl);
// just stop compiler error, ptr is not valid as it's just been freed
return ptr;
}
void *set_aqualink_light_colormode( void *ptr )
{
@ -894,6 +690,13 @@ void *set_aqualink_pool_heater_temps( void *ptr )
}
*/
val = setpoint_check(POOL_HTR_SETOINT, val, aq_data);
if (pda_mode() == true) {
set_PDA_aqualink_heater_setpoint(aq_data, val, true);
cleanAndTerminateThread(threadCtrl);
return ptr;
}
// NSF IF in TEMP1 / TEMP2 mode, we need C range of 1 to 40 is 2 to 40 for TEMP1, 1 to 39 TEMP2
if (aq_data->single_device == true ){
name = "TEMP1";
@ -960,6 +763,13 @@ void *set_aqualink_spa_heater_temps( void *ptr )
val = MEATER_MIN;
}*/
val = setpoint_check(SPA_HTR_SETOINT, val, aq_data);
if (pda_mode() == true) {
set_PDA_aqualink_heater_setpoint(aq_data, val, true);
cleanAndTerminateThread(threadCtrl);
return ptr;
}
// NSF IF in TEMP1 / TEMP2 mode, we need C range of 1 to 40 is 2 to 40 for TEMP1, 1 to 39 TEMP2
if (aq_data->single_device == true ){
@ -1148,6 +958,14 @@ void *get_aqualink_pool_spa_heater_temps( void *ptr )
waitForSingleThreadOrTerminate(threadCtrl, AQ_GET_POOL_SPA_HEATER_TEMPS);
logMessage(LOG_NOTICE, "Getting pool & spa heat setpoints from aqualink\n");
if (pda_mode() == true) {
if (!get_PDA_aqualink_pool_spa_heater_temps(aq_data)) {
logMessage(LOG_ERR, "Error Getting PDA pool & spa heat protection setpoints\n");
}
cleanAndTerminateThread(threadCtrl);
return ptr;
}
if ( select_menu_item(aq_data, "REVIEW") != true ) {
logMessage(LOG_WARNING, "Could not select REVIEW menu\n");
cancel_menu(aq_data);
@ -1182,6 +1000,15 @@ void *get_freeze_protect_temp( void *ptr )
waitForSingleThreadOrTerminate(threadCtrl, AQ_GET_FREEZE_PROTECT_TEMP);
logMessage(LOG_NOTICE, "Getting freeze protection setpoints\n");
if (pda_mode() == true) {
if (! get_PDA_freeze_protect_temp(aq_data)) {
logMessage(LOG_ERR, "Error Getting PDA freeze protection setpoints\n");
}
cleanAndTerminateThread(threadCtrl);
return ptr;
}
if ( select_menu_item(aq_data, "REVIEW") != true ) {
logMessage(LOG_WARNING, "Could not select REVIEW menu\n");
cancel_menu(aq_data);

View File

@ -8,7 +8,7 @@
#define HEATER_MAX_F 104
#define HEATER_MIN_F 36
#define FREEZE_PT_MAX_F 42
#define FREEZE_PT_MIN_F 36
#define FREEZE_PT_MIN_F 34
#define HEATER_MAX_C 40
#define HEATER_MIN_C 0
@ -36,7 +36,9 @@ typedef enum {
AQ_PDA_INIT,
AQ_SET_SWG_PERCENT,
AQ_PDA_DEVICE_STATUS,
AQ_PDA_DEVICE_ON_OFF
AQ_PDA_DEVICE_ON_OFF,
AQ_GET_AUX_LABELS,
AQ_PDA_WAKE_INIT
} program_type;
struct programmingThreadCtrl {
@ -64,4 +66,12 @@ unsigned char pop_aq_cmd(struct aqualinkdata *aq_data);
int get_aq_cmd_length();
int setpoint_check(int type, int value, struct aqualinkdata *aqdata);
// These shouldn't be here, but just for the PDA AQ PROGRAMMER
void send_cmd(unsigned char cmd, struct aqualinkdata *aq_data);
bool push_aq_cmd(unsigned char cmd);
void waitForSingleThreadOrTerminate(struct programmingThreadCtrl *threadCtrl, program_type type);
void cleanAndTerminateThread(struct programmingThreadCtrl *threadCtrl);
bool waitForMessage(struct aqualinkdata *aq_data, char* message, int numMessageReceived);
#endif

View File

@ -125,6 +125,9 @@ const char* get_packet_type(unsigned char* packet , int length)
case CMD_PDA_0x05:
return "PDA Unknown";
break;
case CMD_PDA_0x1B:
return "PDA Init (*guess*)";
break;
case CMD_PDA_HIGHLIGHT:
return "PDA Hlight";
break;

View File

@ -53,8 +53,8 @@
#define KEY_PDA_DOWN 0x05
#define KEY_PDA_BACK 0x02
#define KEY_PDA_SELECT 0x04
#define KEY_PDA_PGUP 0x01
#define KEY_PDA_PGDN 0x03
//#define KEY_PDA_PGUP 0x01 // Think these are hot key #1
//#define KEY_PDA_PGDN 0x03 // Think these are hot key #2
/* KEY/BUTTON CODES */
#define KEY_PUMP 0x02
@ -178,6 +178,7 @@ SPILLOVER IS DISABLED WHILE SPA IS ON
#define SWG_STATUS_CHECK_PCB 0x80 // check PCB 0x80
#define CMD_PDA_0x05 0x05
#define CMD_PDA_0x1B 0x1b
#define CMD_PDA_HIGHLIGHT 0x08
#define CMD_PDA_CLEAR 0x09
#define CMD_PDA_SHIFTLINES 0x0F

View File

@ -97,6 +97,7 @@ struct aqualinkdata
bool simulate_panel;
aqledstate service_mode_state;
aqledstate frz_protect_state;
unsigned char last_packet_type;
//bool last_msg_was_status;
//bool ar_swg_connected;
};

File diff suppressed because it is too large Load Diff

View File

@ -75,11 +75,15 @@ void init_parameters (struct aqconfig * parms)
parms->deamonize = true;
parms->log_file = '\0';
parms->pda_mode = false;
parms->pda_sleep_mode = false;
parms->convert_mqtt_temp = true;
parms->convert_dz_temp = true;
parms->report_zero_pool_temp = false;
parms->report_zero_spa_temp = false;
parms->read_all_devices = true;
parms->use_panel_aux_labels = false;
parms->debug_RSProtocol_packets = false;
parms->force_swg = false;
generate_mqtt_id(parms->mqtt_ID, MQTT_ID_LEN);
}
@ -377,6 +381,10 @@ bool setConfigValue(struct aqconfig *config_parameters, struct aqualinkdata *aqd
config_parameters->pda_mode = text2bool(value);
set_pda_mode(config_parameters->pda_mode);
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);
rtn=true;
} else if (strncasecmp(param, "convert_mqtt_temp_to_c", 22) == 0) {
config_parameters->convert_mqtt_temp = text2bool(value);
rtn=true;
@ -396,7 +404,17 @@ bool setConfigValue(struct aqconfig *config_parameters, struct aqualinkdata *aqd
} else if (strncasecmp (param, "read_all_devices", 16) == 0) {
config_parameters->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);
rtn=true;
} else if (strncasecmp (param, "force_SWG", 9) == 0) {
config_parameters->force_swg = text2bool(value);
rtn=true;
} else if (strncasecmp (param, "debug_RSProtocol_packets", 24) == 0) {
config_parameters->debug_RSProtocol_packets = text2bool(value);
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);

View File

@ -50,12 +50,16 @@ struct aqconfig
int light_programming_button;
bool override_freeze_protect;
bool pda_mode;
bool pda_sleep_mode;
bool convert_mqtt_temp;
bool convert_dz_temp;
//bool flash_mqtt_buttons;
bool report_zero_spa_temp;
bool report_zero_pool_temp;
bool read_all_devices;
bool use_panel_aux_labels;
bool force_swg;
bool debug_RSProtocol_packets;
//int dzidx_pool_thermostat; // Domoticz virtual thermostats are crap removed until better
//int dzidx_spa_thermostat; // Domoticz virtual thermostats are crap removed until better
//char mqtt_pub_topic[250];
@ -69,5 +73,6 @@ void init_parameters (struct aqconfig * parms);
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);
char *cleanalloc(char*str);
#endif

View File

@ -33,6 +33,7 @@
#include "json_messages.h"
#include "domoticz.h"
#include "aq_mqtt.h"
#include "pda.h"
static struct aqconfig *_aqualink_config;
@ -762,6 +763,10 @@ void action_websocket_request(struct mg_connection *nc, struct websocket_message
char buffer[50];
struct JSONwebrequest request;
// Any websocket request means UI is active, so don't let AqualinkD go to sleep if in PDA mode
if (pda_mode())
pda_reset_sleep();
strncpy(buffer, (char *)wm->data, wm->size);
buffer[wm->size] = '\0';
// logMessage (LOG_DEBUG, "buffer '%s'\n", buffer);

557
pda.c Normal file
View File

@ -0,0 +1,557 @@
#define _GNU_SOURCE 1 // for strcasestr & strptime
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "aqualink.h"
#include "init_buttons.h"
#include "pda_menu.h"
#include "utils.h"
// static struct aqualinkdata _aqualink_data;
static struct aqualinkdata *_aqualink_data;
static unsigned char _last_packet_type;
static unsigned long _pda_loop_cnt = 0;
static bool _initWithRS = false;
// Each RS message is around 0.5 seconds apart, so 2 mins = 120 seconds = 240 polls
#define PDA_LOOP_COUNT 240 // 2 mins in poll (sleep timer)
void init_pda(struct aqualinkdata *aqdata)
{
_aqualink_data = aqdata;
set_pda_mode(true);
//clock_gettime(CLOCK_REALTIME, &_aqualink_data->last_active_time);
//aq_programmer(AQ_PDA_INIT, NULL, _aqualink_data); // NEED TO MOVE THIS. Can't run this here incase serial connection fails / needs to be cleaned up.
}
bool pda_shouldSleep() {
//logMessage(LOG_DEBUG, "PDA loop count %d, will sleep at %d\n",_pda_loop_cnt,PDA_LOOP_COUNT);
if (_pda_loop_cnt++ < PDA_LOOP_COUNT) {
return false;
} else if (_pda_loop_cnt > PDA_LOOP_COUNT*2) {
_pda_loop_cnt = 0;
return false;
}
return true;
}
void pda_wake() {
}
void pda_reset_sleep() {
_pda_loop_cnt = 0;
}
unsigned char get_last_pda_packet_type()
{
return _last_packet_type;
}
void set_pda_led(struct aqualinkled *led, char state)
{
aqledstate old_state = led->state;
if (state == 'N')
{
led->state = ON;
}
else if (state == 'A')
{
led->state = ENABLE;
}
else if (state == '*')
{
led->state = FLASH;
}
else
{
led->state = OFF;
}
if (old_state != led->state)
{
logMessage(LOG_DEBUG, "set_pda_led from %d to %d\n", old_state, led->state);
}
}
void pass_pda_equiptment_status_item(char *msg)
{
static char *index;
int i;
// EQUIPMENT STATUS
//
// AquaPure 100%
// SALT 25500 PPM
// FILTER PUMP
// POOL HEAT
// SPA HEAT ENA
// EQUIPMENT STATUS
//
// FREEZE PROTECT
// AquaPure 100%
// SALT 25500 PPM
// CHECK AquaPure
// GENERAL FAULT
// FILTER PUMP
// CLEANER
//
// Check message for status of device
// Loop through all buttons and match the PDA text.
if ((index = strcasestr(msg, "CHECK AquaPure")) != NULL)
{
logMessage(LOG_DEBUG, "CHECK AquaPure\n");
}
else if ((index = strcasestr(msg, "FREEZE PROTECT")) != NULL)
{
_aqualink_data->frz_protect_state = ON;
}
else if ((index = strcasestr(msg, MSG_SWG_PCT)) != NULL)
{
_aqualink_data->swg_percent = atoi(index + strlen(MSG_SWG_PCT));
logMessage(LOG_DEBUG, "AquaPure = %d\n", _aqualink_data->swg_percent);
}
else if ((index = strcasestr(msg, MSG_SWG_PPM)) != NULL)
{
_aqualink_data->swg_ppm = atoi(index + strlen(MSG_SWG_PPM));
logMessage(LOG_DEBUG, "SALT = %d\n", _aqualink_data->swg_ppm);
}
else
{
char labelBuff[AQ_MSGLEN + 1];
strncpy(labelBuff, msg, AQ_MSGLEN + 1);
msg = stripwhitespace(labelBuff);
if (strcasecmp(msg, "POOL HEAT ENA") == 0)
{
_aqualink_data->aqbuttons[POOL_HEAT_INDEX].led->state = ENABLE;
}
else if (strcasecmp(msg, "SPA HEAT ENA") == 0)
{
_aqualink_data->aqbuttons[SPA_HEAT_INDEX].led->state = ENABLE;
}
else
{
for (i = 0; i < TOTAL_BUTTONS; i++)
{
if (strcasecmp(msg, _aqualink_data->aqbuttons[i].pda_label) == 0)
{
logMessage(LOG_DEBUG, "*** Found Status for %s = '%.*s'\n", _aqualink_data->aqbuttons[i].pda_label, AQ_MSGLEN, msg);
// It's on (or delayed) if it's listed here.
if (_aqualink_data->aqbuttons[i].led->state != FLASH)
{
_aqualink_data->aqbuttons[i].led->state = ON;
}
break;
}
}
}
}
}
void process_pda_packet_msg_long_temp(const char *msg)
{
// 'AIR POOL'
// ' 86` 86` '
// 'AIR SPA '
// ' 86` 86` '
// 'AIR '
// ' 86` '
_aqualink_data->temp_units = FAHRENHEIT; // Force FAHRENHEIT
if (stristr(pda_m_line(1), "AIR") != NULL)
_aqualink_data->air_temp = atoi(msg);
if (stristr(pda_m_line(1), "SPA") != NULL)
{
_aqualink_data->spa_temp = atoi(msg + 4);
_aqualink_data->pool_temp = TEMP_UNKNOWN;
}
else if (stristr(pda_m_line(1), "POOL") != NULL)
{
_aqualink_data->pool_temp = atoi(msg + 7);
_aqualink_data->spa_temp = TEMP_UNKNOWN;
}
else
{
_aqualink_data->pool_temp = TEMP_UNKNOWN;
_aqualink_data->spa_temp = TEMP_UNKNOWN;
}
// printf("Air Temp = %d | Water Temp = %d\n",atoi(msg),atoi(msg+7));
}
void process_pda_packet_msg_long_time(const char *msg)
{
// message " SAT 8:46AM "
// " SAT 10:29AM"
// " SAT 4:23PM "
// printf("TIME = '%.*s'\n",AQ_MSGLEN,msg );
// printf("TIME = '%c'\n",msg[AQ_MSGLEN-1] );
if (msg[AQ_MSGLEN - 1] == ' ')
{
strncpy(_aqualink_data->time, msg + 9, 6);
}
else
{
strncpy(_aqualink_data->time, msg + 9, 7);
}
strncpy(_aqualink_data->date, msg + 5, 3);
// :TODO: NSF Come back and change the above to correctly check date and time in future
}
void process_pda_packet_msg_long_equipment_control(const char *msg)
{
// These are listed as "FILTER PUMP OFF"
int i;
char labelBuff[AQ_MSGLEN + 1];
strncpy(labelBuff, msg, AQ_MSGLEN - 4);
labelBuff[AQ_MSGLEN - 4] = 0;
logMessage(LOG_DEBUG, "*** Checking Equiptment '%s'\n", labelBuff);
for (i = 0; i < TOTAL_BUTTONS; i++)
{
if (strcasecmp(stripwhitespace(labelBuff), _aqualink_data->aqbuttons[i].pda_label) == 0)
{
logMessage(LOG_DEBUG, "*** Found EQ CTL Status for %s = '%.*s'\n", _aqualink_data->aqbuttons[i].pda_label, AQ_MSGLEN, msg);
set_pda_led(_aqualink_data->aqbuttons[i].led, msg[AQ_MSGLEN - 1]);
}
}
}
void process_pda_packet_msg_long_home(const char *msg)
{
if (stristr(msg, "POOL MODE") != NULL)
{
// If pool mode is on the filter pump is on but if it is off the filter pump might be on if spa mode is on.
if (msg[AQ_MSGLEN - 1] == 'N')
{
_aqualink_data->aqbuttons[PUMP_INDEX].led->state = ON;
}
else if (msg[AQ_MSGLEN - 1] == '*')
{
_aqualink_data->aqbuttons[PUMP_INDEX].led->state = FLASH;
}
}
else if (stristr(msg, "POOL HEATER") != NULL)
{
set_pda_led(_aqualink_data->aqbuttons[POOL_HEAT_INDEX].led, msg[AQ_MSGLEN - 1]);
}
else if (stristr(msg, "SPA MODE") != NULL)
{
// when SPA mode is on the filter may be on or pending
if (msg[AQ_MSGLEN - 1] == 'N')
{
_aqualink_data->aqbuttons[PUMP_INDEX].led->state = ON;
_aqualink_data->aqbuttons[SPA_INDEX].led->state = ON;
}
else if (msg[AQ_MSGLEN - 1] == '*')
{
_aqualink_data->aqbuttons[PUMP_INDEX].led->state = FLASH;
_aqualink_data->aqbuttons[SPA_INDEX].led->state = ON;
}
else
{
_aqualink_data->aqbuttons[SPA_INDEX].led->state = OFF;
}
}
else if (stristr(msg, "SPA HEATER") != NULL)
{
set_pda_led(_aqualink_data->aqbuttons[SPA_HEAT_INDEX].led, msg[AQ_MSGLEN - 1]);
}
}
void process_pda_packet_msg_long_set_temp(const char *msg)
{
logMessage(LOG_DEBUG, "process_pda_packet_msg_long_set_temp\n");
if (stristr(msg, "POOL HEAT") != NULL)
{
_aqualink_data->pool_htr_set_point = atoi(msg + 10);
logMessage(LOG_DEBUG, "pool_htr_set_point = %d\n", _aqualink_data->pool_htr_set_point);
}
else if (stristr(msg, "SPA HEAT") != NULL)
{
_aqualink_data->spa_htr_set_point = atoi(msg + 10);
logMessage(LOG_DEBUG, "spa_htr_set_point = %d\n", _aqualink_data->spa_htr_set_point);
}
}
void process_pda_packet_msg_long_spa_heat(const char *msg)
{
if (strncmp(msg, " ENABLED ", 16) == 0)
{
_aqualink_data->aqbuttons[SPA_HEAT_INDEX].led->state = ENABLE;
}
else if (strncmp(msg, " SET TO", 8) == 0)
{
_aqualink_data->spa_htr_set_point = atoi(msg + 8);
logMessage(LOG_DEBUG, "spa_htr_set_point = %d\n", _aqualink_data->spa_htr_set_point);
}
}
void process_pda_packet_msg_long_pool_heat(const char *msg)
{
if (strncmp(msg, " ENABLED ", 16) == 0)
{
_aqualink_data->aqbuttons[POOL_HEAT_INDEX].led->state = ENABLE;
}
else if (strncmp(msg, " SET TO", 8) == 0)
{
_aqualink_data->pool_htr_set_point = atoi(msg + 8);
logMessage(LOG_DEBUG, "pool_htr_set_point = %d\n", _aqualink_data->pool_htr_set_point);
}
}
void process_pda_packet_msg_long_freeze_protect(const char *msg)
{
if (strncmp(msg, "TEMP ", 10) == 0)
{
_aqualink_data->frz_protect_set_point = atoi(msg + 10);
logMessage(LOG_DEBUG, "frz_protect_set_point = %d\n", _aqualink_data->frz_protect_set_point);
}
}
void process_pda_packet_msg_long_SWG(const char *msg)
{
//PDA Line 0 = SET AquaPure
//PDA Line 1 =
//PDA Line 2 =
//PDA Line 3 = SET POOL TO: 45%
//PDA Line 4 = SET SPA TO: 0%
// If spa is on, read SWG for spa, if not set SWG for pool
if (_aqualink_data->aqbuttons[SPA_INDEX].led->state != OFF) {
if (strncmp(msg, "SET SPA TO:", 11) == 0)
{
_aqualink_data->swg_percent = atoi(msg + 13);
logMessage(LOG_DEBUG, "SPA swg_percent = %d\n", _aqualink_data->swg_percent);
}
} else {
if (strncmp(msg, "SET POOL TO:", 12) == 0)
{
_aqualink_data->swg_percent = atoi(msg + 13);
logMessage(LOG_DEBUG, "POOL swg_percent = %d\n", _aqualink_data->swg_percent);
}
}
}
void process_pda_packet_msg_long_unknown(const char *msg)
{
int i;
// Lets make a guess here and just see if there is an ON/OFF/ENA/*** at the end of the line
// When you turn on/off a piece of equiptment, a clear screen followed by single message is sent.
// So we are not in any PDA menu, try to catch that message here so we catch new device state ASAP.
if (msg[AQ_MSGLEN - 1] == 'N' || msg[AQ_MSGLEN - 1] == 'F' || msg[AQ_MSGLEN - 1] == 'A' || msg[AQ_MSGLEN - 1] == '*')
{
for (i = 0; i < TOTAL_BUTTONS; i++)
{
if (stristr(msg, _aqualink_data->aqbuttons[i].pda_label) != NULL)
{
printf("*** UNKNOWN Found Status for %s = '%.*s'\n", _aqualink_data->aqbuttons[i].pda_label, AQ_MSGLEN, msg);
// set_pda_led(_aqualink_data->aqbuttons[i].led, msg[AQ_MSGLEN-1]);
}
}
}
}
void process_pda_freeze_protect_devices()
{
// PDA Line 0 = FREEZE PROTECT
// PDA Line 1 = DEVICES
// PDA Line 2 =
// PDA Line 3 = FILTER PUMP X
// PDA Line 4 = SPA
// PDA Line 5 = CLEANER X
// PDA Line 6 = POOL LIGHT
// PDA Line 7 = SPA LIGHT
// PDA Line 8 = EXTRA AUX
// PDA Line 9 =
int i;
logMessage(LOG_DEBUG, "process_pda_freeze_protect_devices\n");
for (i = 1; i < PDA_LINES; i++)
{
if (pda_m_line(i)[AQ_MSGLEN - 1] == 'X')
{
logMessage(LOG_DEBUG, "PDA freeze protect enabled by %s\n", pda_m_line(i));
if (_aqualink_data->frz_protect_state == OFF)
{
_aqualink_data->frz_protect_state = ENABLE;
break;
}
}
}
}
bool process_pda_packet(unsigned char *packet, int length)
{
bool rtn = true;
int i;
char *msg;
static bool init = false;
static time_t _lastStatus = 0;
if (_lastStatus == 0)
{
time(&_lastStatus);
}
process_pda_menu_packet(packet, length);
// NSF.
//_aqualink_data->last_msg_was_status = false;
// debugPacketPrint(0x00, packet, length);
switch (packet[PKT_CMD])
{
case CMD_ACK:
logMessage(LOG_DEBUG, "RS Received ACK length %d.\n", length);
if (init == false)
{
aq_programmer(AQ_PDA_INIT, NULL, _aqualink_data);
init = true;
}
break;
case CMD_STATUS:
_aqualink_data->last_display_message[0] = '\0';
// If we get a status packet, and we are on the status menu, this is a list of what's on
// or pending so unless flash turn everything off, and just turn on items that are listed.
// This is the only way to update a device that's been turned off by a real PDA / keypad.
// Note: if the last line of the status menu is present it may be cut off
if (pda_m_type() == PM_EQUIPTMENT_STATUS)
{
if (_aqualink_data->frz_protect_state == ON)
{
_aqualink_data->frz_protect_state = ENABLE;
}
if (pda_m_line(PDA_LINES - 1)[0] == '\0')
{
for (i = 0; i < TOTAL_BUTTONS; i++)
{
if (_aqualink_data->aqbuttons[i].led->state != FLASH)
{
_aqualink_data->aqbuttons[i].led->state = OFF;
}
}
}
else
{
logMessage(LOG_DEBUG, "PDA Equipment status may be truncated.\n");
}
for (i = 1; i < PDA_LINES; i++)
{
pass_pda_equiptment_status_item(pda_m_line(i));
}
time(&_lastStatus);
}
else
{
time_t now;
time(&now);
if (init && difftime(now, _lastStatus) > 60)
{
logMessage(LOG_DEBUG, "OVER 60 SECONDS SINCE LAST STATUS UPDATE, forcing refresh\n");
// Reset aquapure to nothing since it must be off at this point
_aqualink_data->pool_temp = TEMP_UNKNOWN;
_aqualink_data->spa_temp = TEMP_UNKNOWN;
time(&_lastStatus);
aq_programmer(AQ_PDA_DEVICE_STATUS, NULL, _aqualink_data);
}
}
if (pda_m_type() == PM_FREEZE_PROTECT_DEVICES)
{
process_pda_freeze_protect_devices();
}
break;
case CMD_MSG_LONG:
{
//printf ("*******************************************************************************************\n");
//printf ("menu type %d\n",pda_m_type());
msg = (char *)packet + PKT_DATA + 1;
//strcpy(_aqualink_data->last_message, msg);
if (packet[PKT_DATA] == 0x82)
{ // Air & Water temp is always this ID
process_pda_packet_msg_long_temp(msg);
}
else if (packet[PKT_DATA] == 0x40)
{ // Time is always on this ID
process_pda_packet_msg_long_time(msg);
// If it wasn't a specific msg, (above) then run through and see what kind
// of message it is depending on the PDA menu. Note don't process EQUIPTMENT
// STATUS menu here, wait until a CMD_STATUS is received.
}
else {
switch (pda_m_type()) {
case PM_EQUIPTMENT_CONTROL:
process_pda_packet_msg_long_equipment_control(msg);
break;
case PM_HOME:
case PM_BUILDING_HOME:
process_pda_packet_msg_long_home(msg);
break;
case PM_SET_TEMP:
process_pda_packet_msg_long_set_temp(msg);
break;
case PM_SPA_HEAT:
process_pda_packet_msg_long_spa_heat(msg);
break;
case PM_POOL_HEAT:
process_pda_packet_msg_long_pool_heat(msg);
break;
case PM_FREEZE_PROTECT:
process_pda_packet_msg_long_freeze_protect(msg);
break;
case PM_AQUAPURE:
process_pda_packet_msg_long_SWG(msg);
break;
case PM_UNKNOWN:
default:
process_pda_packet_msg_long_unknown(msg);
break;
}
}
// printf("** Line index='%d' Highligh='%s' Message='%.*s'\n",pda_m_hlightindex(), pda_m_hlight(), AQ_MSGLEN, msg);
logMessage(LOG_INFO, "PDA Menu '%d' Selectedline '%s', Last line received '%.*s'\n", pda_m_type(), pda_m_hlight(), AQ_MSGLEN, msg);
break;
}
case CMD_PDA_0x1B:
{
// We get two of these on startup, one with 0x00 another with 0x01 at index 4. Just act on one.
// Think this is PDA finishd showing startup screen
if (packet[4] == 0x00) {
if (_initWithRS == false)
{
_initWithRS = true;
logMessage(LOG_DEBUG, "**** PDA INIT ****");
aq_programmer(AQ_PDA_INIT, NULL, _aqualink_data);
} else {
logMessage(LOG_DEBUG, "**** PDA WAKE INIT ****");
aq_programmer(AQ_PDA_WAKE_INIT, NULL, _aqualink_data);
}
}
}
break;
}
if (packet[PKT_CMD] == CMD_MSG_LONG || packet[PKT_CMD] == CMD_PDA_HIGHLIGHT ||
packet[PKT_CMD] == CMD_PDA_SHIFTLINES || packet[PKT_CMD] == CMD_PDA_CLEAR)
{
// We processed the next message, kick any threads waiting on the message.
kick_aq_program_thread(_aqualink_data);
}
return rtn;
}

12
pda.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef PDA_H_
#define PDA_H_
void init_pda(struct aqualinkdata *aqdata);
bool process_pda_packet(unsigned char* packet, int length);
bool pda_shouldSleep();
void pda_wake();
void pda_reset_sleep();
#endif // PDA_MESSAGES_H_

770
pda_aq_programmer.c Normal file
View File

@ -0,0 +1,770 @@
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include "aqualink.h"
#include "utils.h"
#include "aq_programmer.h"
#include "aq_serial.h"
#include "pda.h"
#include "pda_menu.h"
#include "pda_aq_programmer.h"
#include "init_buttons.h"
bool waitForPDAMessageHighlight(struct aqualinkdata *aq_data, int highlighIndex, int numMessageReceived);
bool waitForPDAMessageType(struct aqualinkdata *aq_data, unsigned char mtype, int numMessageReceived);
bool goto_pda_menu(struct aqualinkdata *aq_data, pda_menu_type menu);
bool wait_pda_selected_item(struct aqualinkdata *aq_data);
bool waitForPDAnextMenu(struct aqualinkdata *aq_data);
bool loopover_devices(struct aqualinkdata *aq_data);
bool find_pda_menu_item(struct aqualinkdata *aq_data, char *menuText, int charlimit);
bool select_pda_menu_item(struct aqualinkdata *aq_data, char *menuText, bool waitForNextMenu);
static pda_type _PDA_Type;
bool wait_pda_selected_item(struct aqualinkdata *aq_data)
{
while (pda_m_hlightindex() == -1){
waitForPDAMessageType(aq_data,CMD_PDA_HIGHLIGHT,10);
}
if (pda_m_hlightindex() == -1)
return false;
else
return true;
}
bool waitForPDAnextMenu(struct aqualinkdata *aq_data) {
waitForPDAMessageType(aq_data,CMD_PDA_CLEAR,10);
return waitForPDAMessageType(aq_data,CMD_PDA_HIGHLIGHT,15);
}
bool loopover_devices(struct aqualinkdata *aq_data) {
int i;
if (! goto_pda_menu(aq_data, PM_EQUIPTMENT_CONTROL)) {
//logMessage(LOG_ERR, "PDA :- can't find main menu\n");
//cleanAndTerminateThread(threadCtrl);
return false;
}
// Should look for message "ALL OFF", that's end of device list.
for (i=0; i < 18 && pda_find_m_index("ALL OFF") == -1 ; i++) {
send_cmd(KEY_PDA_DOWN, aq_data);
//while (get_aq_cmd_length() > 0) { delay(200); }
//waitForPDAMessageType(aq_data,CMD_PDA_HIGHLIGHT,3);
waitForMessage(aq_data, NULL, 1);
}
return true;
}
/*
if charlimit is set, use case insensitive match and limit chars.
*/
bool find_pda_menu_item(struct aqualinkdata *aq_data, char *menuText, int charlimit) {
int i=pda_m_hlightindex();
int index = (charlimit == 0)?pda_find_m_index(menuText):pda_find_m_index_case(menuText, charlimit);
logMessage(LOG_DEBUG, "PDA Device programmer menu text '%s'\n",menuText);
if (index < 0) { // No menu, is there a page down. "PDA Line 9 = ^^ MORE __"
if (strncmp(pda_m_line(9)," ^^ MORE", 10) == 0) {
int j;
for(j=0; j < 20; j++) {
send_cmd(KEY_PDA_DOWN, aq_data);
//delay(500);
//wait_for_empty_cmd_buffer();
waitForPDAMessageType(aq_data,CMD_PDA_HIGHLIGHT,2);
//waitForMessage(aq_data, NULL, 1);
index = (charlimit == 0)?pda_find_m_index(menuText):pda_find_m_index_case(menuText, charlimit);
if (index >= 0) {
i=pda_m_hlightindex();
break;
}
}
if (index < 0) {
logMessage(LOG_ERR, "PDA Device programmer couldn't find menu item on any page '%s'\n",menuText);
return false;
}
} else {
logMessage(LOG_ERR, "PDA Device programmer couldn't find menu item '%s'\n",menuText);
return false;
}
}
// Found the text we want in the menu, now move to that position and select it.
//logMessage(LOG_DEBUG, "******************PDA Device programmer menu text '%s' is at index %d\n",menuText, index);
if (i < index) {
for (i=pda_m_hlightindex(); i < index; i++) {
//logMessage(LOG_DEBUG, "******************PDA queue down index %d\n",i);
send_cmd(KEY_PDA_DOWN, aq_data);
}
} else if (i > index) {
for (i=pda_m_hlightindex(); i > index; i--) {
//logMessage(LOG_DEBUG, "******************PDA queue down index %d\n",i);
send_cmd(KEY_PDA_UP, aq_data);
}
}
return waitForPDAMessageHighlight(aq_data, index, 10);
}
bool select_pda_menu_item(struct aqualinkdata *aq_data, char *menuText, bool waitForNextMenu) {
if ( find_pda_menu_item(aq_data, menuText, 0) ) {
send_cmd(KEY_PDA_SELECT, aq_data);
logMessage(LOG_DEBUG, "PDA Device programmer selected menu item '%s'\n",menuText);
if (waitForNextMenu)
waitForPDAnextMenu(aq_data);
return true;
}
logMessage(LOG_ERR, "PDA Device programmer couldn't selected menu item '%s' at index %d\n",menuText, index);
return false;
}
bool goto_pda_menu(struct aqualinkdata *aq_data, pda_menu_type menu) {
//int i = 0;
//char *menuText;
logMessage(LOG_DEBUG, "PDA Device programmer request for menu %d\n",menu);
// Keep going back, checking each time to get to home.
while (pda_m_type() == PM_FW_VERSION || pda_m_type() == PM_BUILDING_HOME) {
//logMessage(LOG_DEBUG, "******************PDA Device programmer delay on firmware or building home menu\n");
delay(500);
}
while ( pda_m_type() != menu && pda_m_type() != PM_HOME ) {
if (pda_m_type() != PM_BUILDING_HOME) {
send_cmd(KEY_PDA_BACK, aq_data);
//logMessage(LOG_DEBUG, "******************PDA Device programmer selected back button\n",menu);
waitForPDAnextMenu(aq_data);
} else {
waitForPDAMessageType(aq_data,CMD_PDA_HIGHLIGHT,15);
}
//logMessage(LOG_DEBUG, "******************PDA Device programmer menu type %d\n",pda_m_type());
//if (!wait_for_empty_cmd_buffer() || i++ > 6)
// return false;
}
if (pda_m_type() == menu)
return true;
switch (menu) {
//case PM_SYSTEM_SETUP:
// select_pda_menu_item(aq_data, "MENU");
//break;
case PM_EQUIPTMENT_CONTROL:
select_pda_menu_item(aq_data, "EQUIPMENT ON/OFF", true);
break;
case PM_PALM_OPTIONS:
select_pda_menu_item(aq_data, "MENU", true);
select_pda_menu_item(aq_data, "PALM OPTIONS", true);
case PM_AUX_LABEL:
if ( _PDA_Type == PDA) {
select_pda_menu_item(aq_data, "MENU", true);
select_pda_menu_item(aq_data, "SYSTEM SETUP", true); // This is a guess, (I have rev#)
select_pda_menu_item(aq_data, "LABEL AUX", true);
} else {
logMessage(LOG_ERR, "PDA in AquaPlalm mode, there is no SYSTEM SETUP / LABEL AUX menu\n");
}
break;
case PM_SYSTEM_SETUP:
if ( _PDA_Type == PDA) {
select_pda_menu_item(aq_data, "MENU", true);
select_pda_menu_item(aq_data, "SYSTEM SETUP", true); // This is a guess, (I have rev#)
} else {
logMessage(LOG_ERR, "PDA in AquaPlalm mode, there is no SYSTEM SETUP menu\n");
}
break;
case PM_FREEZE_PROTECT:
if ( _PDA_Type == PDA) {
select_pda_menu_item(aq_data, "MENU", true);
select_pda_menu_item(aq_data, "SYSTEM SETUP", true); // This is a guess, (I have rev#)
select_pda_menu_item(aq_data, "FREEZE PROTECT", true); // This is a guess, (I have rev#)
} else {
logMessage(LOG_ERR, "PDA in AquaPlalm mode, there is no SYSTEM SETUP / FREEZE PROTECT menu\n");
}
break;
case PM_AQUAPURE:
select_pda_menu_item(aq_data, "MENU", true);
select_pda_menu_item(aq_data, "SET AquaPure", true);
//select_pda_menu_item(aq_data, "LABEL AUX");
break;
case PM_SET_TEMP:
select_pda_menu_item(aq_data, "MENU", true);
select_pda_menu_item(aq_data, "SET TEMP", true);
//select_pda_menu_item(aq_data, "LABEL AUX");
break;
case PM_SET_TIME:
select_pda_menu_item(aq_data, "MENU", true);
select_pda_menu_item(aq_data, "SET TIME", true);
//select_pda_menu_item(aq_data, "LABEL AUX");
break;
default:
logMessage(LOG_ERR, "PDA Device programmer didn't understand requested menu\n");
return false;
break;
}
if (pda_m_type() != menu) {
logMessage(LOG_ERR, "PDA Device programmer didn't find a requested menu\n");
return true;
}
//logMessage(LOG_DEBUG, "******************PDA Device programmer request for menu %d found\n",menu);
return true;
}
void *set_aqualink_PDA_device_on_off( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
//int i=0;
//int found;
char device_name[15];
waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_DEVICE_STATUS);
char *buf = (char*)threadCtrl->thread_args;
int device = atoi(&buf[0]);
int state = atoi(&buf[5]);
if (device < 0 || device > TOTAL_BUTTONS) {
logMessage(LOG_ERR, "PDA Device On/Off :- bad device number '%d'\n",device);
cleanAndTerminateThread(threadCtrl);
return ptr;
}
logMessage(LOG_INFO, "PDA Device On/Off, device '%s', state %d\n",aq_data->aqbuttons[device].pda_label,state);
if (! goto_pda_menu(aq_data, PM_EQUIPTMENT_CONTROL)) {
logMessage(LOG_ERR, "PDA Device On/Off :- can't find main menu\n");
cleanAndTerminateThread(threadCtrl);
return ptr;
}
//Pad name with spaces so something like "SPA" doesn't match "SPA BLOWER"
sprintf(device_name,"%-14s\n",aq_data->aqbuttons[device].pda_label);
if ( find_pda_menu_item(aq_data, device_name, 13) ) {
if (aq_data->aqbuttons[device].led->state != state) {
//printf("*** Select State ***\n");
logMessage(LOG_INFO, "PDA Device On/Off, found device '%s', changing state\n",aq_data->aqbuttons[device].pda_label,state);
send_cmd(KEY_PDA_SELECT, aq_data);
while (get_aq_cmd_length() > 0) { delay(500); }
} else {
logMessage(LOG_INFO, "PDA Device On/Off, found device '%s', not changing state, is same\n",aq_data->aqbuttons[device].pda_label,state);
}
} else {
logMessage(LOG_ERR, "PDA Device On/Off, device '%s' not found\n",aq_data->aqbuttons[device].pda_label);
}
goto_pda_menu(aq_data, PM_HOME);
//while (_pgm_command != NUL) { delay(500); }
cleanAndTerminateThread(threadCtrl);
// just stop compiler error, ptr is not valid as it's just been freed
return ptr;
}
void *get_aqualink_PDA_device_status( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
//int i;
waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_DEVICE_STATUS);
if (! loopover_devices(aq_data)) {
logMessage(LOG_ERR, "PDA Device Status :- failed\n");
}
goto_pda_menu(aq_data, PM_HOME);
cleanAndTerminateThread(threadCtrl);
// just stop compiler error, ptr is not valid as it's just been freed
return ptr;
}
void *set_aqualink_PDA_init( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
//int i=0;
waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_INIT);
//int val = atoi((char*)threadCtrl->thread_args);
//logMessage(LOG_DEBUG, "PDA Init\n", val);
logMessage(LOG_DEBUG, "PDA Init\n");
if (pda_m_type() == PM_FW_VERSION) {
// check pda_m_line(1) to "AquaPalm"
if (strstr(pda_m_line(1), "AquaPalm") != NULL) {
_PDA_Type = AQUAPALM;
} else {
_PDA_Type = PDA;
}
char *ptr = pda_m_line(5);
ptr[AQ_MSGLEN+1] = '\0';
strcpy(aq_data->version, stripwhitespace(ptr));
}
// Get status of all devices
if (! loopover_devices(aq_data)) {
logMessage(LOG_ERR, "PDA Init :- can't find menu\n");
}
// Get heater setpoints
if (! get_PDA_aqualink_pool_spa_heater_temps(aq_data)) {
logMessage(LOG_ERR, "PDA Init :- Error getting heater setpoints\n");
}
// Get freeze protect setpoint, AquaPalm doesn't have freeze protect in menu.
if (_PDA_Type != AQUAPALM && ! get_PDA_freeze_protect_temp(aq_data)) {
logMessage(LOG_ERR, "PDA Init :- Error getting freeze setpoints\n");
}
goto_pda_menu(aq_data, PM_HOME);
pda_reset_sleep();
cleanAndTerminateThread(threadCtrl);
// just stop compiler error, ptr is not valid as it's just been freed
return ptr;
}
void *set_aqualink_PDA_wakeinit( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
//int i=0;
// At this point, we should probably just exit if there is a thread already going as
// it means the wake was called due to changing a device.
waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_INIT);
logMessage(LOG_DEBUG, "PDA Wake Init\n");
// Get status of all devices
if (! loopover_devices(aq_data)) {
logMessage(LOG_ERR, "PDA Init :- can't find menu\n");
}
cleanAndTerminateThread(threadCtrl);
// just stop compiler error, ptr is not valid as it's just been freed
return ptr;
}
bool get_PDA_freeze_protect_temp(struct aqualinkdata *aq_data) {
if ( _PDA_Type == PDA) {
if (! goto_pda_menu(aq_data, PM_FREEZE_PROTECT)) {
return false;
}
} else {
logMessage(LOG_INFO, "In PDA AquaPalm mode, freezepoints not supported\n");
return false;
}
return true;
}
bool get_PDA_aqualink_pool_spa_heater_temps(struct aqualinkdata *aq_data) {
// Get heater setpoints
if (! goto_pda_menu(aq_data, PM_SET_TEMP)) {
return false;
}
return true;
}
bool waitForPDAMessageHighlight(struct aqualinkdata *aq_data, int highlighIndex, int numMessageReceived)
{
logMessage(LOG_DEBUG, "waitForPDAMessageHighlight index %d\n",highlighIndex);
if(pda_m_hlightindex() == highlighIndex) return true;
int i=0;
pthread_mutex_init(&aq_data->active_thread.thread_mutex, NULL);
pthread_mutex_lock(&aq_data->active_thread.thread_mutex);
while( ++i <= numMessageReceived)
{
logMessage(LOG_DEBUG, "waitForPDAMessageHighlight last = 0x%02hhx : index %d : (%d of %d)\n",aq_data->last_packet_type,pda_m_hlightindex(),i,numMessageReceived);
if (aq_data->last_packet_type == CMD_PDA_HIGHLIGHT && pda_m_hlightindex() == highlighIndex) break;
pthread_cond_init(&aq_data->active_thread.thread_cond, NULL);
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 (pda_m_hlightindex() != highlighIndex) {
//logMessage(LOG_ERR, "Could not select MENU of Aqualink control panel\n");
logMessage(LOG_DEBUG, "waitForPDAMessageHighlight: did not receive index '%d'\n",highlighIndex);
return false;
} else
logMessage(LOG_DEBUG, "waitForPDAMessageHighlight: received index '%d'\n",highlighIndex);
return true;
}
bool waitForPDAMessageType(struct aqualinkdata *aq_data, unsigned char mtype, int numMessageReceived)
{
logMessage(LOG_DEBUG, "waitForPDAMessageType 0x%02hhx\n",mtype);
int i=0;
pthread_mutex_init(&aq_data->active_thread.thread_mutex, NULL);
pthread_mutex_lock(&aq_data->active_thread.thread_mutex);
while( ++i <= numMessageReceived)
{
logMessage(LOG_DEBUG, "waitForPDAMessageType 0x%02hhx, last message type was 0x%02hhx (%d of %d)\n",mtype,aq_data->last_packet_type,i,numMessageReceived);
if (aq_data->last_packet_type == mtype) break;
pthread_cond_init(&aq_data->active_thread.thread_cond, NULL);
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 (aq_data->last_packet_type != mtype) {
//logMessage(LOG_ERR, "Could not select MENU of Aqualink control panel\n");
logMessage(LOG_DEBUG, "waitForPDAMessageType: did not receive 0x%02hhx\n",mtype);
return false;
} else
logMessage(LOG_DEBUG, "waitForPDAMessageType: received 0x%02hhx\n",mtype);
return true;
}
bool set_PDA_numeric_field_value(struct aqualinkdata *aq_data, int val, int *cur_val, char *select_label, int step) {
int i;
// Should probably change below to call find_pda_menu_item(), rather than doing it here
// If we lease this, need to limit on the number of loops
while ( strncmp(pda_m_hlight(), select_label, 8) != 0 ) {
send_cmd(KEY_PDA_DOWN, aq_data);
delay(500); // Last message probably was CMD_PDA_HIGHLIGHT, so wait before checking.
waitForPDAMessageType(aq_data,CMD_PDA_HIGHLIGHT,2);
}
send_cmd(KEY_PDA_SELECT, aq_data);
if (val < *cur_val) {
logMessage(LOG_DEBUG, "PDA %s value : lower from %d to %d\n", select_label, *cur_val, val);
for (i = *cur_val; i > val; i=i-step) {
send_cmd(KEY_PDA_DOWN, aq_data);
}
} else if (val > *cur_val) {
logMessage(LOG_DEBUG, "PDA %s value : raise from %d to %d\n", select_label, *cur_val, val);
for (i = *cur_val; i < val; i=i+step) {
send_cmd(KEY_PDA_UP, aq_data);
}
} else {
logMessage(LOG_INFO, "PDA %s value : already at %d\n", select_label, val);
send_cmd(KEY_PDA_BACK, aq_data);
return true;
}
send_cmd(KEY_PDA_SELECT, aq_data);
logMessage(LOG_DEBUG, "PDA %s value : set to %d\n", select_label, *cur_val);
return true;
}
bool set_PDA_aqualink_SWG_setpoint(struct aqualinkdata *aq_data, int val) {
if (! goto_pda_menu(aq_data, PM_AQUAPURE)) {
logMessage(LOG_ERR, "Error getting setpoints menu\n");
}
if (aq_data->aqbuttons[SPA_INDEX].led->state != OFF)
set_PDA_numeric_field_value(aq_data, val, &aq_data->swg_percent, "SET SPA", 5);
else
set_PDA_numeric_field_value(aq_data, val, &aq_data->swg_percent, "SET POOL", 5);
return true;
}
bool set_PDA_aqualink_heater_setpoint(struct aqualinkdata *aq_data, int val, bool isPool) {
char label[10];
int *cur_val;
if (isPool) {
sprintf(label, "POOL HEAT");
cur_val = &aq_data->pool_htr_set_point;
} else {
sprintf(label, "SPA HEAT");
cur_val = &aq_data->spa_htr_set_point;
}
if (val == *cur_val) {
logMessage(LOG_INFO, "PDA %s setpoint : temp already %d\n", label, val);
send_cmd(KEY_PDA_BACK, aq_data);
return true;
}
if (! goto_pda_menu(aq_data, PM_SET_TEMP)) {
logMessage(LOG_ERR, "Error getting setpoints menu\n");
}
set_PDA_numeric_field_value(aq_data, val, cur_val, label, 1);
return true;
}
bool set_PDA_aqualink_freezeprotect_setpoint(struct aqualinkdata *aq_data, int val) {
if (! goto_pda_menu(aq_data, PM_FREEZE_PROTECT)) {
logMessage(LOG_ERR, "Error getting setpoints menu\n");
}
set_PDA_numeric_field_value(aq_data, val, &aq_data->frz_protect_set_point, "TEMP", 1);
return true;
}
/*
bool waitForPDAMessage(struct aqualinkdata *aq_data, int numMessageReceived, unsigned char packettype)
{
logMessage(LOG_DEBUG, "waitForPDAMessage %s %d\n",message,numMessageReceived);
int i=0;
pthread_mutex_init(&aq_data->active_thread.thread_mutex, NULL);
pthread_mutex_lock(&aq_data->active_thread.thread_mutex);
char* msgS;
char* ptr;
if (message != NULL) {
if (message[0] == '^')
msgS = &message[1];
else
msgS = message;
}
while( ++i <= numMessageReceived)
{
if (message != NULL)
logMessage(LOG_DEBUG, "Programming mode: loop %d of %d looking for '%s' received message '%s'\n",i,numMessageReceived,message,aq_data->last_message);
else
logMessage(LOG_DEBUG, "Programming mode: loop %d of %d waiting for next message, received '%s'\n",i,numMessageReceived,aq_data->last_message);
if (message != NULL) {
ptr = stristr(aq_data->last_message, msgS);
if (ptr != NULL) { // match
logMessage(LOG_DEBUG, "Programming mode: String MATCH\n");
if (msgS == message) // match & don't care if first char
break;
else if (ptr == aq_data->last_message) // match & do care if first char
break;
}
}
//logMessage(LOG_DEBUG, "Programming mode: looking for '%s' received message '%s'\n",message,aq_data->last_message);
pthread_cond_init(&aq_data->active_thread.thread_cond, NULL);
pthread_cond_wait(&aq_data->active_thread.thread_cond, &aq_data->active_thread.thread_mutex);
//logMessage(LOG_DEBUG, "Programming mode: loop %d of %d looking for '%s' received message '%s'\n",i,numMessageReceived,message,aq_data->last_message);
}
pthread_mutex_unlock(&aq_data->active_thread.thread_mutex);
if (message != NULL && ptr == NULL) {
//logMessage(LOG_ERR, "Could not select MENU of Aqualink control panel\n");
logMessage(LOG_DEBUG, "Programming mode: did not find '%s'\n",message);
return false;
} else if (message != NULL)
logMessage(LOG_DEBUG, "Programming mode: found message '%s' in '%s'\n",message,aq_data->last_message);
return true;
}
*/
/*
Link to two different menu's used in PDA
http://www.poolequipmentpriceslashers.com.au/wp-content/uploads/2012/11/Jandy-Aqualink-RS-PDA-Wireless-Pool-Controller_manual.pdf
https://www.jandy.com/-/media/zodiac/global/downloads/h/h0574200.pdf
*/
/*
List of how menu's display
PDA Line 0 =
PDA Line 1 = AquaPalm
PDA Line 2 =
PDA Line 3 = Firmware Version
PDA Line 4 =
PDA Line 5 = REV MMM
PDA Line 6 =
PDA Line 7 =
PDA Line 8 =
PDA Line 9 =
***************** Think this is startup different rev *************
Line 0 =
Line 1 = PDA-PS4 Combo
Line 2 =
Line 3 = Firmware Version
Line 4 =
Line 5 = PPD: PDA 1.2
PDA Line 0 =
PDA Line 1 = AIR POOL
PDA Line 2 =
PDA Line 3 =
PDA Line 4 = POOL MODE ON
PDA Line 5 = POOL HEATER OFF
PDA Line 6 = SPA MODE OFF
PDA Line 7 = SPA HEATER OFF
PDA Line 8 = MENU
PDA Line 9 = EQUIPMENT ON/OFF
PDA Line 0 = MAIN MENU
PDA Line 1 =
PDA Line 2 = SET TEMP >
PDA Line 3 = SET TIME >
PDA Line 4 = SET AquaPure >
PDA Line 5 = PALM OPTIONS >
PDA Line 6 =
PDA Line 7 = BOOST POOL
PDA Line 8 =
PDA Line 9 =
**************** OPTION 2 FOR THIS MENU ********************
PDA Line 0 = MAIN MENU
PDA Line 1 =
PDA Line 2 = HELP >
PDA Line 3 = PROGRAM >
PDA Line 4 = SET TEMP >
PDA Line 5 = SET TIME >
PDA Line 6 = PDA OPTIONS >
PDA Line 7 = SYSTEM SETUP >
PDA Line 8 =
PDA Line 9 = BOOST
********** Guess at SYSTEM SETUP Menu (not on Rev MMM or before)************
// PDA Line 0 = SYSTEM SETUP
// PDA Line 1 = LABEL AUX >
// PDA Line 2 = FREEZE PROTECT >
// PDA Line 3 = AIR TEMP >
// PDA Line 4 = DEGREES C/F >
// PDA Line 5 = TEMP CALIBRATE >
// PDA Line 6 = SOLAR PRIORITY >
// PDA Line 7 = PUMP LOCKOUT >
// PDA Line 8 = ASSIGN JVAs >
// PDA Line 9 = ^^ MORE __
// PDA Line 5 = COLOR LIGHTS >
// PDA Line 6 = SPA SWITCH >
// PDA Line 7 = SERVICE INFO >
// PDA Line 8 = CLEAR MEMORY >
PDA Line 0 = PALM OPTIONS
PDA Line 1 =
PDA Line 2 =
PDA Line 3 = SET AUTO-OFF >
PDA Line 4 = BACKLIGHT >
PDA Line 5 = ASSIGN HOTKEYS >
PDA Line 6 =
PDA Line 7 = Choose setting
PDA Line 8 = and press SELECT
PDA Line 9 =
PDA Line 0 = SET AquaPure
PDA Line 1 =
PDA Line 2 =
PDA Line 3 = SET POOL TO: 45%
PDA Line 4 = SET SPA TO: 0%
PDA Line 5 =
PDA Line 6 =
PDA Line 7 = Highlight an
PDA Line 8 = item and press
PDA Line 9 = SELECT
PDA Line 0 = SET TIME
PDA Line 1 =
PDA Line 2 = 05/22/19 WED
PDA Line 3 = 10:53 AM
PDA Line 4 =
PDA Line 5 =
PDA Line 6 = Use ARROW KEYS
PDA Line 7 = to set value.
PDA Line 8 = Press SELECT
PDA Line 9 = to continue.
PDA Line 0 = SET TEMP
PDA Line 1 =
PDA Line 2 = POOL HEAT 70`F
PDA Line 3 = SPA HEAT 98`F
PDA Line 4 =
PDA Line 5 =
PDA Line 6 =
PDA Line 7 = Highlight an
PDA Line 8 = item and press
PDA Line 9 = SELECT
PDA Line 0 = EQUIPMENT
PDA Line 1 = FILTER PUMP ON
PDA Line 2 = SPA OFF
PDA Line 3 = POOL HEAT OFF
PDA Line 4 = SPA HEAT OFF
PDA Line 5 = CLEANER ON
PDA Line 6 = WATERFALL OFF
PDA Line 7 = AIR BLOWER OFF
PDA Line 8 = LIGHT OFF
PDA Line 9 = ^^ MORE __
PDA Line 0 = EQUIPMENT
PDA Line 1 = WATERFALL OFF
PDA Line 2 = AIR BLOWER OFF
PDA Line 3 = LIGHT OFF
PDA Line 4 = AUX5 OFF
PDA Line 5 = EXTRA AUX OFF
PDA Line 6 = SPA MODE OFF
PDA Line 7 = CLEAN MODE OFF
PDA Line 8 = ALL OFF
PDA Line 9 =
*/

21
pda_aq_programmer.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef PDA_AQ_PROGRAMMER_H_
#define PDA_AQ_PROGRAMMER_H_
typedef enum pda_type {
AQUAPALM,
PDA
} pda_type;
void *get_aqualink_PDA_device_status( void *ptr );
void *set_aqualink_PDA_device_on_off( void *ptr );
void *set_aqualink_PDA_wakeinit( void *ptr );
bool set_PDA_aqualink_heater_setpoint(struct aqualinkdata *aq_data, int val, bool isPool);
bool set_PDA_aqualink_SWG_setpoint(struct aqualinkdata *aq_data, int val);
bool set_PDA_aqualink_freezeprotect_setpoint(struct aqualinkdata *aq_data, int val);
bool get_PDA_aqualink_pool_spa_heater_temps(struct aqualinkdata *aq_data);
bool get_PDA_freeze_protect_temp(struct aqualinkdata *aq_data);
#endif // AQ_PDA_PROGRAMMER_H_

View File

@ -39,23 +39,81 @@ char *pda_m_line(int index)
// return NULL;
}
int pda_find_m_index(char *text)
{
int i;
for (i = 0; i < PDA_LINES; i++) {
if (strncmp(pda_m_line(i), text, strlen(text)) == 0)
return i;
}
return -1;
}
int pda_find_m_index_case(char *text, int limit)
{
int i;
for (i = 0; i < PDA_LINES; i++) {
//printf ("+++ Compare '%s' to '%s' index %d\n",text,pda_m_line(i),i);
if (strncasecmp(pda_m_line(i), text, limit) == 0)
return i;
}
return -1;
}
pda_menu_type pda_m_type()
{
if (strncmp(_menu[1],"AIR ", 5) == 0)
return PM_MAIN;
return PM_HOME;
else if (strncmp(_menu[0],"EQUIPMENT STATUS", 16) == 0)
return PM_EQUIPTMENT_STATUS;
else if (strncmp(_menu[0]," EQUIPMENT ", 16) == 0)
return PM_EQUIPTMENT_CONTROL;
else if (strncmp(_menu[0]," MAIN MENU ", 16) == 0)
return PM_SETTINGS;
return PM_MAIN;
//else if ((_menu[0] == '\0' && _hlightindex == -1) || strncmp(_menu[4], "POOL MODE", 9) == 0 )// IF we are building the main menu this may be valid
else if (strncmp(_menu[4], "POOL MODE", 9) == 0 )
return PM_BUILDING_MAIN;
else if (strncmp(_menu[4], "POOL MODE", 9) == 0 ) {
if (pda_m_hlightindex() == -1)
return PM_BUILDING_HOME;
else
return PM_HOME;
}
else if (strncmp(_menu[0]," SET TEMP ", 16) == 0)
return PM_SET_TEMP;
else if (strncmp(_menu[0]," SET TIME ", 16) == 0)
return PM_SET_TIME;
else if (strncmp(_menu[0]," SET AquaPure ", 16) == 0)
return PM_AQUAPURE;
else if (strncmp(_menu[0]," SPA HEAT ", 16) == 0)
return PM_SPA_HEAT;
else if (strncmp(_menu[0]," POOL HEAT ", 16) == 0)
return PM_POOL_HEAT;
else if (strncmp(_menu[6],"Use ARROW KEYS ", 16) == 0 &&
strncmp(_menu[0]," FREEZE PROTECT ", 16) == 0)
return PM_FREEZE_PROTECT;
else if (strncmp(_menu[1]," DEVICES ", 16) == 0 &&
strncmp(_menu[0]," FREEZE PROTECT ", 16) == 0)
return PM_FREEZE_PROTECT_DEVICES;
else if (strncmp(_menu[3],"Firmware Version", 16) == 0 ||
strncmp(_menu[1]," AquaPalm", 12) == 0 ||
strncmp(_menu[1]," PDA-PS4 Combo", 14) == 0)
return PM_FW_VERSION;
return PM_UNKNOWN;
}
/*
--- Main Menu ---
Line 0 =
@ -86,15 +144,105 @@ bool process_pda_menu_packet(unsigned char* packet, int length)
if (getLogLevel() >= LOG_DEBUG){print_menu();}
break;
case CMD_PDA_HIGHLIGHT:
_hlightindex = packet[4],_menu[packet[4]];
// when switching from hlight to hlightchars index 255 is sent to turn off hlight
if (packet[4] <= PDA_LINES) {
_hlightindex = packet[4];
} else {
_hlightindex = -1;
}
if (getLogLevel() >= LOG_DEBUG){print_menu();}
break;
case CMD_PDA_HIGHLIGHTCHARS:
if (packet[4] <= PDA_LINES) {
_hlightindex = packet[4];
} else {
_hlightindex = -1;
}
if (getLogLevel() >= LOG_DEBUG){print_menu();}
break;
case CMD_PDA_SHIFTLINES:
memcpy(_menu[1], _menu[2], (PDA_LINES-1) * (AQ_MSGLEN+1) );
if (getLogLevel() >= LOG_DEBUG){print_menu();}
break;
}
return rtn;
}
#ifdef SOME_CRAP
bool NEW_process_pda_menu_packet_NEW(unsigned char* packet, int length)
{
bool rtn = true;
signed char first_line;
signed char last_line;
signed char line_shift;
signed char i;
pthread_mutex_lock(&_pda_menu_mutex);
switch (packet[PKT_CMD]) {
case CMD_STATUS:
pthread_cond_signal(&_pda_menu_update_complete_cond);
break;
case CMD_PDA_CLEAR:
rtn = pda_m_clear();
break;
case CMD_MSG_LONG:
if (packet[PKT_DATA] < 10) {
memset(_menu[packet[PKT_DATA]], 0, AQ_MSGLEN);
strncpy(_menu[packet[PKT_DATA]], (char*)packet+PKT_DATA+1, AQ_MSGLEN);
_menu[packet[PKT_DATA]][AQ_MSGLEN] = '\0';
}
if (packet[PKT_DATA] == _hlightindex) {
logMessage(LOG_DEBUG, "process_pda_menu_packet: hlight changed from shift or up/down value\n");
pthread_cond_signal(&_pda_menu_hlight_change_cond);
}
if (getLogLevel() >= LOG_DEBUG){print_menu();}
update_pda_menu_type();
break;
case CMD_PDA_HIGHLIGHT:
// when switching from hlight to hlightchars index 255 is sent to turn off hlight
if (packet[4] <= PDA_LINES) {
_hlightindex = packet[4];
} else {
_hlightindex = -1;
}
pthread_cond_signal(&_pda_menu_hlight_change_cond);
if (getLogLevel() >= LOG_DEBUG){print_menu();}
break;
case CMD_PDA_HIGHLIGHTCHARS:
if (packet[4] <= PDA_LINES) {
_hlightindex = packet[4];
} else {
_hlightindex = -1;
}
pthread_cond_signal(&_pda_menu_hlight_change_cond);
if (getLogLevel() >= LOG_DEBUG){print_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_menu();}
break;
}
pthread_mutex_unlock(&_pda_menu_mutex);
return rtn;
}
#endif

View File

@ -5,6 +5,38 @@
#define PDA_LINES 10 // There is only 9 lines, but add buffer to make shifting easier
typedef enum pda_menu_type {
PM_UNKNOWN,
PM_FW_VERSION,
PM_HOME,
PM_BUILDING_HOME,
PM_MAIN,
PM_DIAGNOSTICS,
PM_PROGRAM,
PM_SET_TEMP,
PM_SET_TIME,
PM_POOL_HEAT,
PM_SPA_HEAT,
PM_AQUAPURE,
PM_SYSTEM_SETUP,
PM_AUX_LABEL,
PM_FREEZE_PROTECT,
PM_FREEZE_PROTECT_DEVICES,
PM_VSP,
PM_SETTINGS,
PM_EQUIPTMENT_CONTROL,
PM_EQUIPTMENT_STATUS,
PM_PALM_OPTIONS // This seems to be only older revisions
} pda_menu_type;
/*
typedef enum pda_home_menu_item {
PMI_MAIN,
PMI_EQUIPTMENT_CONTROL
} pda_home_menu_item;
*/
/*
typedef enum pda_menu_type {
PM_UNKNOWN,
PM_MAIN,
@ -13,7 +45,25 @@ typedef enum pda_menu_type {
PM_EQUIPTMENT_STATUS,
PM_BUILDING_MAIN
} pda_menu_type;
*/
/*
typedef enum pda_menu_type {
PM_UNKNOWN,
PM_FW_VERSION,
PM_HOME,
PM_MAIN_MENU,
PM_EQUIPTMENT_CONTROL,
PM_EQUIPTMENT_STATUS,
PM_BUILDING_HOME,
PM_SYSTEM_SETUP,
PM_SET_TEMP,
PM_POOL_HEAT,
PM_SPA_HEAT,
PM_FREEZE_PROTECT,
PM_FREEZE_PROTECT_DEVICES,
PM_SET_AQUAPURE
} pda_menu_type;
*/
bool pda_mode();
void set_pda_mode(bool val);
bool process_pda_menu_packet(unsigned char* packet, int length);
@ -21,5 +71,7 @@ int pda_m_hlightindex();
char *pda_m_hlight();
char *pda_m_line(int index);
pda_menu_type pda_m_type();
int pda_find_m_index(char *text);
int pda_find_m_index_case(char *text, int limit);
#endif

Binary file not shown.

View File

@ -60,10 +60,27 @@ report_zero_pool_temp = no
# Working RS ID's are 0x0a 0x0b 0x09 0x08 <- 0x08 is usually taken
device_id=0x0a
# 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
#Only for PDA mode
# set PDA mode
#pda_mode = yes
#
# Put AqualinkD to sleep when in PDA mode after inactivity.
# If you have Jandy PDA then this MUST be set to yes as the controller can only support one PDA.
# If you don't have a Jandy PDA leave this at no as AqualinkD will be a lot quicker.
# Sleep timer is around 2 mins of inactivity, then wake after 2 mins of sleep.
#pda_sleep_mode = yes
# Read status information from other devices on the RS485 bus.
# At the moment just Salt Water Generators are supported.
read_all_devices = yes
# If you have a SWG connected to the control panel, set this to yes.
# AqualinkD can only detect a SWG if it's on, so after a restart you will not see/access a SWG until the the next time the pump is on.
force_SWG = no
# Button inxed light probramming button is assigned to. (look at your button labels below)
light_programming_button = 0
@ -91,6 +108,10 @@ spa_water_temp_dzidx=0
SWG_percent_dzidx=0
SWG_PPM_dzidx=0
# Try to use labels from Control Panel.
use_panel_aux_labels=yes
# Labels for standard butons (shown in web UI), and domoticz idx's
button_01_label=Filter Pump
#button_01_dzidx=37

View File

@ -19,8 +19,8 @@ web_directory=/nas/data/Development/Raspberry/AqualinkD/web
# DEBUG would print everything possible
#log_level=DEBUG_SERIAL
log_level=DEBUG
#log_level=INFO
#log_level=DEBUG
log_level=INFO
#log_level=NOTICE
# The socket port that the daemon listens to
@ -33,13 +33,13 @@ serial_port=/dev/ttyUSB0
override_freeze_protect = no
# mqtt stuff
mqtt_address = trident:1883
#mqtt_address = trident:1883
#mqtt_user = someusername
#mqtt_passwd = somepassword
#mqtt_dz_pub_topic = domoticz/in
#mqtt_dz_sub_topic = domoticz/out
mqtt_aq_topic = aqualinkd_test
#mqtt_aq_topic = aqualinkd
# The id of the Aqualink terminal device. Devices probed by RS8 master are:
# 08-0b, 10-13, 18-1b, 20-23, 28-2b, 30-33, 38-3b, 40-43
@ -49,6 +49,7 @@ mqtt_aq_topic = aqualinkd_test
#device_id=0x09
device_id=0x60
pda_mode = yes
pda_sleep_mode = yes
convert_mqtt_temp_to_c = yes
convert_dz_temp_to_c = yes
@ -101,15 +102,15 @@ button_03_PDA_label=CLEANER
button_04_label=Waterfall
#button_04_dzidx=40
button_04_PDA_label=AUX2
button_04_PDA_label=WATERFALL
button_05_label=Spa Blower
#button_05_dzidx=41
button_05_PDA_label=AUX3
button_05_PDA_label=AIR BLOWER
button_06_label=Pool Light
#button_06_dzidx=42
button_06_PDA_label=AUX4
button_06_PDA_label=LIGHT
button_07_label=NONE
#button_07_dzidx=43

Binary file not shown.

View File

@ -217,7 +217,7 @@ int main(int argc, char *argv[]) {
}
if (packet_buffer[PKT_DEST] == DEV_MASTER /*&& packet_buffer[PKT_CMD] == CMD_ACK*/) {
//logMessage(LOG_DEBUG_SERIAL, "ID is in use 0x%02hhx %x\n", lastID, lastID);
//logMessage(LOG_NOTICE, "ID is in use 0x%02hhx %x\n", lastID, lastID);
for (i = 0; i <= sindex; i++) {
if (slog[i].ID == lastID) {
slog[i].inuse = true;
@ -257,7 +257,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, "ID's found\n");
for (i = 0; i <= sindex; i++) {
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",
// (slog[i].inuse == false && canUse(slog[i].ID) == true)? " <-- can use for Aqualinkd" : "");

46
utils.c
View File

@ -483,3 +483,49 @@ int ascii(char *destination, char *source) {
destination[i] = '\0';
return i;
}
char *prittyString(char *str)
{
char *ptr = str;
char *end;
bool lastspace=true;
end = str + strlen(str) - 1;
while(end >= ptr){
//printf("%d %s ", *ptr, ptr);
if (lastspace && *ptr > 96 && *ptr < 123) {
*ptr = *ptr - 32;
lastspace=false;
//printf("to upper\n");
} else if (lastspace == false && *ptr > 54 && *ptr < 91) {
*ptr = *ptr + 32;
lastspace=false;
//printf("to lower\n");
} else if (*ptr == 32) {
lastspace=true;
//printf("space\n");
} else {
lastspace=false;
//printf("leave\n");
}
ptr++;
}
//printf("-- %s --\n", str);
return str;
}
static FILE *_packetLogFile = NULL;
void writePacketLog(char *buffer) {
if (_packetLogFile == NULL)
_packetLogFile = fopen("/tmp/RS485.log", "a");
if (_packetLogFile != NULL) {
fputs(buffer, _packetLogFile);
}
}
void closePacketLog() {
fclose(_packetLogFile);
}

View File

@ -49,7 +49,9 @@ float degFtoC(float degF);
float degCtoF(float degC);
char* stristr(const char* haystack, const char* needle);
int ascii(char *destination, char *source);
char *prittyString(char *str);
void writePacketLog(char *buff);
void closePacketLog();
//#ifndef _UTILS_C_
extern bool _daemon_;

View File

@ -1,4 +1,4 @@
#define AQUALINKD_NAME "Aqualink Daemon"
#define AQUALINKD_VERSION "1.2.6f"
#define AQUALINKD_VERSION "1.3.0"

View File

@ -1178,7 +1178,8 @@
} else if (data.type == 'devices') {
check_devices(data);
resetBackgroundSize();
window.setTimeout(get_devices, (300 * 1000)); // Check for new dvices ever 5 mins.
//window.setTimeout(get_devices, (300 * 1000)); // Check for new dvices ever 5 mins.
window.setTimeout(get_devices, (60 * 1000)); // Check for new dvices ever 1 mins.
}
}
socket_di.onclose = function() {