Version 1.3.7

pull/101/merge
sfeakes 2019-10-13 10:07:14 -05:00
parent 96c81fb8be
commit 51a8b686a5
24 changed files with 481 additions and 77 deletions

View File

@ -66,6 +66,18 @@ Designed to mimic AqualinkRS6 All Button keypad and (like the keypad) is used to
* 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.7
* PDA SUPPORT IS BROKEN IN 1.3.7 DON'T UPGRADE IF YOU'RE USING PDA Mode
* PDA Note:- Due to changes to speed up programming the control panel, PDA mode does not function correctly, I will come back and fix this, but I don't have the time for this release.
* SWG updates
* Simulator update
* Added boost functionality for SWG. (Web UI & MQTT only, not Apple homekit yet)
* MQTT boost status is aqualinkd/SWG/Boost
* MQTT boost on/off is aqualinkd/SWG/Boost/set
* Web UI, long press on SWG icon for boost & percent options
* Simple Web Ui, extra button called Boost
* Changed how programming works. (Please test fully things like, changing heater setpoints, SWG percent etc, be prepared to role back)
* Added raw RS485 logging
# Update in Release 1.3.6
* Can now debug inline from a web ui. (http://aqualinkd.ip.address/debug.html)
* Fix SWG in homekit sometimes displaying wrong value. Note to Homekit users, Upgrading to 1.3.5c (and above) will add an aditional SWG PPM tile, (look in default room). You'll need to update homebridge-aqualinkd to 0.0.8 (or later) to remove the old PPM tile (or delete you homebridge cache). This is due to a bug in homebridge-aqualinkd < 0.0.7 that didn't delete unused tiles.

View File

@ -21,6 +21,7 @@
#define SWG_PERCENT_F_TOPIC SWG_TOPIC "/Percent_f"
#define SWG_SETPOINT_TOPIC SWG_TOPIC "/setpoint"
#define SWG_EXTENDED_TOPIC SWG_TOPIC "/fullstatus"
#define SWG_BOOST_TOPIC SWG_TOPIC "/Boost"
#define FREEZE_PROTECT "Freeze_Protect"
#define FREEZE_PROTECT_ENABELED FREEZE_PROTECT ENABELED_SUBT

View File

@ -55,6 +55,7 @@ void *get_aqualink_aux_labels( void *ptr );
void *set_aqualink_light_colormode( void *ptr );
void *set_aqualink_PDA_init( void *ptr );
void *set_aqualink_SWG( void *ptr );
void *set_aqualink_boost( void *ptr );
//void *get_aqualink_PDA_device_status( void *ptr );
//void *set_aqualink_PDA_device_on_off( void *ptr );
@ -65,6 +66,7 @@ bool waitForButtonState(struct aqualinkdata *aq_data, aqkey* button, aqledstate
bool waitForEitherMessage(struct aqualinkdata *aq_data, char* message1, char* message2, int numMessageReceived);
bool push_aq_cmd(unsigned char cmd);
void waitfor_queue2empty();
#define MAX_STACK 20
int _stack_place = 0;
@ -99,7 +101,56 @@ int get_aq_cmd_length()
return _stack_place;
}
unsigned char pop_aq_cmd_old(struct aqualinkdata *aq_data);
unsigned char pop_aq_cmd(struct aqualinkdata *aq_data)
{
unsigned char cmd = NUL;
static bool last_sent_was_cmd = false;
// USE BELOW IF PDA HAS ISSUES WITH NEW COMMAND LOGIC
//if ( pda_mode() == true ) {
// return pop_aq_cmd_old(aq_data);
//}
// Only press menu to a status command
// Only send commands on status messages when programming date
// Otherwise send every other command.
// Are we in programming mode
if (aq_data->active_thread.thread_id != 0) {
if ( ((_pgm_command == KEY_MENU || aq_data->active_thread.ptype == AQ_SET_TIME) && aq_data->last_packet_type == CMD_STATUS) ||
(aq_data->active_thread.ptype != AQ_SET_TIME && last_sent_was_cmd == false) ||
(pda_mode() == true && aq_data->last_packet_type == CMD_STATUS)
//(pda_mode() == true && last_sent_was_cmd == false)
) {
cmd = _pgm_command;
_pgm_command = NUL;
logMessage(LOG_DEBUG, "RS SEND cmd '0x%02hhx' (programming)\n", cmd);
/*} else if (aq_data->active_thread.ptype != AQ_SET_TIME && last_sent_was_cmd == false) {
cmd = _pgm_command;
_pgm_command = NUL;
logMessage(LOG_DEBUG, "RS SEND cmd '0x%02hhx' (programming)\n", cmd);*/
} else {
logMessage(LOG_DEBUG, "RS Waiting to send cmd '0x%02hhx'\n", _pgm_command);
}
} else if (_stack_place > 0 && aq_data->last_packet_type == CMD_STATUS ) {
cmd = _commands[0];
_stack_place--;
logMessage(LOG_DEBUG, "RS SEND cmd '0x%02hhx'\n", cmd);
memmove(&_commands[0], &_commands[1], sizeof(unsigned char) * _stack_place ) ;
}
if (cmd == NUL)
last_sent_was_cmd= false;
else
last_sent_was_cmd= true;
return cmd;
}
unsigned char pop_aq_cmd_old(struct aqualinkdata *aq_data)
{
unsigned char cmd = NUL;
//logMessage(LOG_DEBUG, "pop_aq_cmd\n");
@ -129,8 +180,6 @@ unsigned char pop_aq_cmd(struct aqualinkdata *aq_data)
return cmd;
}
int setpoint_check(int type, int value, struct aqualinkdata *aqdata)
{
int rtn = value;
@ -215,7 +264,7 @@ int setpoint_check(int type, int value, struct aqualinkdata *aqdata)
void kick_aq_program_thread(struct aqualinkdata *aq_data)
{
if (aq_data->active_thread.thread_id != 0) {
logMessage(LOG_DEBUG, "Kicking thread %d,%p\n",aq_data->active_thread.ptype, aq_data->active_thread.thread_id);
logMessage(LOG_DEBUG, "Kicking thread %d,%p message '%s'\n",aq_data->active_thread.ptype, aq_data->active_thread.thread_id,aq_data->last_message);
pthread_cond_broadcast(&aq_data->active_thread.thread_cond);
}
}
@ -362,6 +411,12 @@ void aq_programmer(program_type type, char *args, struct aqualinkdata *aq_data)
return;
}
break;
case AQ_SET_BOOST:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_boost, (void*)programmingthread) < 0) {
logMessage (LOG_ERR, "could not create thread\n");
return;
}
break;
default:
logMessage (LOG_ERR, "Don't understand thread type\n");
break;
@ -385,20 +440,20 @@ void waitForSingleThreadOrTerminate(struct programmingThreadCtrl *threadCtrl, pr
i = 0;
while (get_aq_cmd_length() > 0 && ( i++ <= tries) ) {
logMessage (LOG_DEBUG, "Thread %d sleeping, waiting for queue to empty\n", threadCtrl->thread_id, threadCtrl->aq_data->active_thread.thread_id);
logMessage (LOG_DEBUG, "Thread %p (%s) sleeping, waiting command queue to empty\n", &threadCtrl->thread_id, ptypeName(type));
sleep(waitTime);
}
if (i >= tries) {
logMessage (LOG_ERR, "Thread %d timeout waiting, ending\n",threadCtrl->thread_id);
logMessage (LOG_ERR, "Thread %p (%s) timeout waiting, ending\n",&threadCtrl->thread_id,ptypeName(type));
free(threadCtrl);
pthread_exit(0);
}
while ( (threadCtrl->aq_data->active_thread.thread_id != 0) && ( i++ <= tries) ) {
//logMessage (LOG_DEBUG, "Thread %d sleeping, waiting for thread %d to finish\n", threadCtrl->thread_id, threadCtrl->aq_data->active_thread.thread_id);
logMessage (LOG_DEBUG, "Thread %d,%p (%s) sleeping, waiting for thread %d,%p (%s) to finish\n",
type, &threadCtrl->thread_id, ptypeName(type),
threadCtrl->aq_data->active_thread.ptype, threadCtrl->aq_data->active_thread.thread_id, ptypeName(threadCtrl->aq_data->active_thread.ptype));
logMessage (LOG_DEBUG, "Thread %p (%s) sleeping, waiting for thread %p (%s) to finish\n",
&threadCtrl->thread_id, ptypeName(type),
threadCtrl->aq_data->active_thread.thread_id, ptypeName(threadCtrl->aq_data->active_thread.ptype));
sleep(waitTime);
}
@ -411,6 +466,8 @@ void waitForSingleThreadOrTerminate(struct programmingThreadCtrl *threadCtrl, pr
pthread_exit(0);
}
// Clear out any messages to the UI.
threadCtrl->aq_data->last_display_message[0] = '\0';
threadCtrl->aq_data->active_thread.thread_id = &threadCtrl->thread_id;
threadCtrl->aq_data->active_thread.ptype = type;
@ -467,7 +524,7 @@ bool setAqualinkNumericField_new(struct aqualinkdata *aq_data, char *value_label
int i=0;
do
{
if (waitForMessage(aq_data, searchBuf, 3) != true) {
if (waitForMessage(aq_data, searchBuf, 4) != true) {
logMessage(LOG_WARNING, "AQ_Programmer Could not set numeric input '%s', not found\n",value_label);
cancel_menu();
return false;
@ -560,6 +617,67 @@ void *threadded_send_cmd( void *ptr )
}
*/
void *set_aqualink_boost( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_BOOST);
/*
menu
<find menu>
BOOST POOL
<wait 2 messages>
PRESS ENTER* TO START BOOST POOL
<press enter>
<Menu when in boost>
BOOST POOL 23:59 REMAINING
menu
<find menu>
BOOST POOL
<find menu>
STOP BOOST POOL
<press enter>
*/
int val = atoi((char*)threadCtrl->thread_args);
logMessage(LOG_DEBUG, "programming BOOST to %s\n", val==true?"On":"Off");
if ( select_menu_item(aq_data, "BOOST POOL") != true ) {
logMessage(LOG_WARNING, "Could not select BOOST POOL menu\n");
cancel_menu();
cleanAndTerminateThread(threadCtrl);
return ptr;
}
if (val==true) {
waitForMessage(threadCtrl->aq_data, "TO START BOOST POOL", 5);
send_cmd(KEY_ENTER);
waitfor_queue2empty();
} else {
// Extra message overcome.
send_cmd(KEY_RIGHT);
waitfor_queue2empty();
if ( select_sub_menu_item(aq_data, "STOP BOOST POOL") != true ) {
logMessage(LOG_WARNING, "Could not select STOP BOOST POOL menu\n");
cancel_menu();
cleanAndTerminateThread(threadCtrl);
return ptr;
}
//send_cmd(KEY_ENTER);
}
cleanAndTerminateThread(threadCtrl);
// just stop compiler error, ptr is not valid as it's just been freed
return ptr;
}
void *set_aqualink_SWG( void *ptr )
{
@ -1185,15 +1303,24 @@ void send_cmd(unsigned char cmd, struct aqualinkdata *aq_data)
}
*/
void send_cmd(unsigned char cmd)
void waitfor_queue2empty()
{
int i=0;
// If there is an unsent command, wait.
while ( (_pgm_command != NUL) && ( i++ < 10) ) {
while ( (_pgm_command != NUL) && ( i++ < 20) ) {
//sleep(1); // NSF Change to smaller time.
//logMessage(LOG_ERR, "******** QUEUE IS FULL ******** delay\n", pgm_command);
delay(500);
//logMessage(LOG_DEBUG, "******** QUEUE IS FULL ******** delay\n");
delay(50);
}
if (_pgm_command != NUL)
logMessage(LOG_WARNING, "Send command Queue did not empty, timeout\n");
}
void send_cmd(unsigned char cmd)
{
waitfor_queue2empty();
_pgm_command = cmd;
//delay(200);
@ -1230,6 +1357,7 @@ void cancel_menu()
bool waitForEitherMessage(struct aqualinkdata *aq_data, char* message1, char* message2, int numMessageReceived)
{
//logMessage(LOG_DEBUG, "waitForMessage %s %d %d\n",message,numMessageReceived,cmd);
waitfor_queue2empty(); // MAke sure the last command was sent
int i=0;
pthread_mutex_lock(&aq_data->active_thread.thread_mutex);
char* msgS1 = "";
@ -1297,6 +1425,8 @@ bool waitForEitherMessage(struct aqualinkdata *aq_data, char* message1, char* me
bool waitForMessage(struct aqualinkdata *aq_data, char* message, int numMessageReceived)
{
logMessage(LOG_DEBUG, "waitForMessage %s %d\n",message,numMessageReceived);
waitfor_queue2empty(); // MAke sure the last command was sent
int i=0;
pthread_mutex_lock(&aq_data->active_thread.thread_mutex);
char* msgS;
@ -1312,9 +1442,9 @@ bool waitForMessage(struct aqualinkdata *aq_data, char* message, int numMessageR
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);
logMessage(LOG_DEBUG, "Programming mode: loop %d of %d looking for '%s', last message received '%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);
logMessage(LOG_DEBUG, "Programming mode: loop %d of %d waiting for next message, last message received '%s'\n",i,numMessageReceived,aq_data->last_message);
if (message != NULL) {
ptr = stristr(aq_data->last_message, msgS);
@ -1328,7 +1458,9 @@ bool waitForMessage(struct aqualinkdata *aq_data, char* message, int numMessageR
}
//logMessage(LOG_DEBUG, "Programming mode: looking for '%s' received message '%s'\n",message,aq_data->last_message);
//logMessage(LOG_DEBUG, "*** pthread_cond_wait() sleep\n");
pthread_cond_wait(&aq_data->active_thread.thread_cond, &aq_data->active_thread.thread_mutex);
//logMessage(LOG_DEBUG, "*** pthread_cond_wait() wake\n");
//logMessage(LOG_DEBUG, "Programming mode: loop %d of %d looking for '%s' received message '%s'\n",i,numMessageReceived,message,aq_data->last_message);
}
@ -1340,6 +1472,8 @@ bool waitForMessage(struct aqualinkdata *aq_data, char* message, int numMessageR
return false;
} else if (message != NULL)
logMessage(LOG_DEBUG, "Programming mode: found message '%s' in '%s'\n",message,aq_data->last_message);
else
logMessage(LOG_DEBUG, "Programming mode: waited for %d message(s)\n",numMessageReceived);
return true;
}
@ -1347,6 +1481,7 @@ bool waitForMessage(struct aqualinkdata *aq_data, char* message, int numMessageR
bool select_menu_item(struct aqualinkdata *aq_data, char* item_string)
{
char* expectedMsg = "PRESS ENTER* TO SELECT";
//char* expectedMsg = "PROGRAM";
int wait_messages = 5;
bool found = false;
int tries = 0;
@ -1400,9 +1535,9 @@ bool select_sub_menu_item(struct aqualinkdata *aq_data, char* item_string)
while( (stristr(aq_data->last_message, item_string) == NULL) && ( i++ < wait_messages) )
{
logMessage(LOG_DEBUG, "Find item in Menu: loop %d of %d looking for '%s' received message '%s'\n",i,wait_messages,item_string,aq_data->last_message);
send_cmd(KEY_RIGHT);
waitForMessage(aq_data, NULL, 1);
logMessage(LOG_DEBUG, "Find item in Menu: loop %d of %d looking for '%s' received message '%s'\n",i,wait_messages,item_string,aq_data->last_message);
}
if (stristr(aq_data->last_message, item_string) == NULL) {
@ -1410,12 +1545,16 @@ bool select_sub_menu_item(struct aqualinkdata *aq_data, char* item_string)
return false;
}
logMessage(LOG_DEBUG, "Programming mode: found menu item '%s'\n", item_string);
logMessage(LOG_DEBUG, "Find item in Menu: loop %d of %d FOUND menu item '%s', sending ENTER command\n",i,wait_messages, item_string);
// Enter the mode specified by the argument.
send_cmd(KEY_ENTER);
waitForMessage(aq_data, NULL, 1);
//sendCmdWaitForReturn(aq_data, KEY_ENTER);
return true;
@ -1503,6 +1642,9 @@ const char *ptypeName(program_type type)
break;
case AQ_PDA_WAKE_INIT:
return "PDA init after wake";
break;
case AQ_SET_BOOST:
return "SWG Boost";
break;
case AQP_NULL:
default:

View File

@ -38,7 +38,8 @@ typedef enum {
AQ_PDA_DEVICE_STATUS,
AQ_PDA_DEVICE_ON_OFF,
AQ_GET_AUX_LABELS,
AQ_PDA_WAKE_INIT
AQ_PDA_WAKE_INIT,
AQ_SET_BOOST
} program_type;
struct programmingThreadCtrl {

View File

@ -605,7 +605,17 @@ bool check_pentair_checksum(unsigned char* packet, int length)
return false;
}
int _get_packet(int fd, unsigned char* packet, bool rawlog);
int get_packet_new_lograw(int fd, unsigned char* packet)
{
return _get_packet(fd, packet, true);
}
int get_packet_new(int fd, unsigned char* packet)
{
return _get_packet(fd, packet, false);
}
int _get_packet(int fd, unsigned char* packet, bool rawlog)
{
unsigned char byte;
int bytesRead;
@ -644,6 +654,9 @@ int get_packet_new(int fd, unsigned char* packet)
delay(10);
} else if (bytesRead == 1) {
if (rawlog)
logPacketByte(&byte);
if (lastByteDLE == true && byte == NUL)
{
// Check for DLE | NULL (that's escape DLE so delete the NULL)

View File

@ -185,6 +185,9 @@ SPILLOVER IS DISABLED WHILE SPA IS ON
/* AQUAPURE SWG */
// These are madeup.
//#define SWG_STATUS_OFF 0xFF
//#define SWG_STATUS_OFFLINE 0xFE
#define SWG_STATUS_OFF 0xFF
#define SWG_STATUS_UNKNOWN -128
// These are actual from RS485
@ -255,11 +258,12 @@ void send_extended_ack(int fd, unsigned char ack_type, unsigned char command);
//void send_cmd(int file_descriptor, unsigned char cmd, unsigned char args);
int get_packet(int file_descriptor, unsigned char* packet);
int get_packet_new(int fd, unsigned char* packet);
int get_packet_new_lograw(int fd, unsigned char* packet);
//void close_serial_port(int file_descriptor, struct termios* oldtio);
//void process_status(void const * const ptr);
void process_status(unsigned char* ptr);
const char* get_packet_type(unsigned char* packet , int length);
void send_test_cmd(int fd, unsigned char destination, unsigned char b1, unsigned char b2, unsigned char b3);
void send_command(int fd, unsigned char destination, unsigned char b1, unsigned char b2, unsigned char b3);
void send_messaged(int fd, unsigned char destination, char *message);
//void send_test_cmd(int fd, unsigned char destination, unsigned char b1, unsigned char b2, unsigned char b3);
//void send_command(int fd, unsigned char destination, unsigned char b1, unsigned char b2, unsigned char b3);
//void send_messaged(int fd, unsigned char destination, char *message);
#endif // AQ_SERIAL_H_

View File

@ -8,9 +8,12 @@
#include "aq_programmer.h"
#define TIME_CHECK_INTERVAL 3600
//#define TIME_CHECK_INTERVAL 600
#define ACCEPTABLE_TIME_DIFF 120
// Use these settings to test time
//#define TIME_CHECK_INTERVAL 100
//#define ACCEPTABLE_TIME_DIFF 10
#define MAX_ZERO_READ_BEFORE_RECONNECT 500
#define TOTAL_BUTTONS 12
@ -55,7 +58,8 @@ typedef enum action_type {
POOL_HTR_SETOINT,
SPA_HTR_SETOINT,
FREEZE_SETPOINT,
SWG_SETPOINT
SWG_SETPOINT,
SWG_BOOST
} action_type;
struct action {
@ -113,6 +117,8 @@ struct aqualinkdata
unsigned char last_packet_type;
pump_detail pumps[MAX_PUMPS];
int open_websockets;
bool boost;
char boost_msg[10];
//bool last_msg_was_status;
//bool ar_swg_connected;
#ifdef AQ_DEBUG

View File

@ -141,8 +141,17 @@ bool checkAqualinkTime()
// Set the return value to true.
return true;
}
/*
char buff[30];
struct tm * timeinfo;
timeinfo = localtime (&now);
strftime(buff, sizeof(buff), "%y %b %d %H:%M:%S", timeinfo);
logMessage(LOG_DEBUG, "System time %s\n", buff);
timeinfo = localtime (&aqualink_time);
strftime(buff, sizeof(buff), "%y %b %d %H:%M:%s", timeinfo);
logMessage(LOG_DEBUG, "Aqualink time %s\n", buff);
/*
char datestring[256];
//time_t t = time(0); // get time now
struct tm * tm = localtime( & now );
@ -210,6 +219,7 @@ void processMessage(char *message)
static int freeze_msg_count = 0;
static int service_msg_count = 0;
static int swg_msg_count = 0;
static int boost_msg_count = 0;
// NSF replace message with msg
msg = stripwhitespace(message);
strcpy(_aqualink_data.last_message, msg);
@ -252,6 +262,12 @@ void processMessage(char *message)
_aqualink_data.frz_protect_state = ENABLE;
freeze_msg_count = 0;
}
if (_aqualink_data.boost == true && boost_msg_count++ > 10) {
_aqualink_data.boost = false;
_aqualink_data.boost_msg[0] = '\0';
boost_msg_count = 0;
}
}
//else
@ -264,7 +280,7 @@ void processMessage(char *message)
}
else if (stristr(msg, LNG_MSG_POOL_TEMP_SET) != NULL)
{
//logMessage(LOG_DEBUG, "pool htr long message: %s", &message[20]);
//logMessage(LOG_DEBUG, "**************** pool htr long message: %s", &message[20]);
_aqualink_data.pool_htr_set_point = atoi(message + 20);
if (_aqualink_data.temp_units == UNKNOWN)
@ -434,13 +450,25 @@ void processMessage(char *message)
//_aqualink_data.aqbuttons[labelid + 1].label = cleanalloc(msg + 5);
}
}
// BOOST POOL 23:59 REMAINING
else if ( (strncasecmp(msg, "BOOST POOL", 10) == 0) && (strstr(msg, "REMAINING") != NULL) ) {
// Ignore messages if in programming mode. We get one of these turning off for some strange reason.
if (_aqualink_data.active_thread.thread_id == 0) {
snprintf(_aqualink_data.boost_msg, 6, &msg[11]);
_aqualink_data.boost = true;
boost_msg_count = 0;
//if (_aqualink_data.active_thread.thread_id == 0)
strcpy(_aqualink_data.last_display_message, msg); // Also display the message on web UI if not in programming mode
}
}
else
{
logMessage(LOG_DEBUG_SERIAL, "Ignoring '%s'\n", msg);
//_aqualink_data.display_message = msg;
if (_aqualink_data.active_thread.thread_id == 0 &&
stristr(msg, "JANDY AquaLinkRS") == NULL &&
stristr(msg, "PUMP O") == NULL /*&& // Catch 'PUMP ON' and 'PUMP OFF' but not 'PUMP WILL TURN ON'
stristr(msg, "PUMP O") == NULL &&// Catch 'PUMP ON' and 'PUMP OFF' but not 'PUMP WILL TURN ON'
stristr(msg, "MAINTAIN") == NULL /* && // Catch 'MAINTAIN TEMP IS OFF'
stristr(msg, "CLEANER O") == NULL &&
stristr(msg, "SPA O") == NULL &&
stristr(msg, "AUX") == NULL*/
@ -456,6 +484,7 @@ void processMessage(char *message)
ascii(_aqualink_data.last_display_message, msg);
// We processed the next message, kick any threads waiting on the message.
//printf ("Message kicking\n");
kick_aq_program_thread(&_aqualink_data);
}
/*
@ -545,10 +574,10 @@ bool process_packet(unsigned char *packet, int length)
switch (packet[PKT_CMD])
{
case CMD_ACK:
logMessage(LOG_DEBUG, "RS Received ACK length %d.\n", length);
//logMessage(LOG_DEBUG, "RS Received ACK length %d.\n", length);
break;
case CMD_STATUS:
logMessage(LOG_DEBUG, "RS Received STATUS length %d.\n", length);
//logMessage(LOG_DEBUG, "RS Received STATUS length %d.\n", length);
memcpy(_aqualink_data.raw_status, packet + 4, AQ_PSTLEN);
processLEDstate();
if (_aqualink_data.aqbuttons[PUMP_INDEX].led->state == OFF)
@ -569,21 +598,23 @@ bool process_packet(unsigned char *packet, int length)
// COLOR MODE programming relies on state changes, so let any threads know
if (_aqualink_data.active_thread.ptype == AQ_SET_COLORMODE) {
//printf ("Light thread kicking\n");
//printf ("Light thread kicking\n");
kick_aq_program_thread(&_aqualink_data);
}
break;
case CMD_MSG:
memset(message, 0, AQ_MSGLONGLEN + 1);
strncpy(message, (char *)packet + PKT_DATA + 1, AQ_MSGLEN);
//logMessage(LOG_DEBUG_SERIAL, "RS Received message '%s'\n",message);
if (packet[PKT_DATA] == 1) // Start of long message, get them all before processing
{
kick_aq_program_thread(&_aqualink_data);
//printf ("Start long message thread kicking\n");
// kick_aq_program_thread(&_aqualink_data);
break;
}
}
processMessage(message);
//processMessage(message);
break;
case CMD_MSG_LONG:
// First in sequence is normal message.
@ -682,6 +713,19 @@ void action_delayed_request()
// Let's just tell everyone we set it, before we actually did. Makes homekit happy, and it will re-correct on error.
_aqualink_data.swg_percent = _aqualink_data.unactioned.value;
}
else if (_aqualink_data.unactioned.type == SWG_BOOST)
{
//logMessage(LOG_NOTICE, "SWG BOST to %d\n", _aqualink_data.unactioned.value);
if (_aqualink_data.ar_swg_status == SWG_STATUS_OFF) {
logMessage(LOG_ERR, "SWG is off, can't Boost pool\n");
} else if (_aqualink_data.unactioned.value == _aqualink_data.boost ) {
logMessage(LOG_ERR, "Request to turn Boost %s ignored, Boost is already %s\n",_aqualink_data.unactioned.value?"On":"Off", _aqualink_data.boost?"On":"Off");
} else {
aq_programmer(AQ_SET_BOOST, sval, &_aqualink_data);
}
// Let's just tell everyone we set it, before we actually did. Makes homekit happy, and it will re-correct on error.
_aqualink_data.boost = _aqualink_data.unactioned.value;
}
_aqualink_data.unactioned.type = NO_ACTION;
_aqualink_data.unactioned.value = -1;
@ -698,6 +742,7 @@ int main(int argc, char *argv[])
char *cfgFile;
int cmdln_loglevel = -1;
bool cmdln_debugRS485 = false;
bool cmdln_lograwRS485 = false;
// struct lws_context_creation_info info;
// Log only NOTICE messages and above. Debug and info messages
@ -734,6 +779,10 @@ int main(int argc, char *argv[])
{
cmdln_debugRS485 = true;
}
else if (strcmp(argv[i], "-rsrd") == 0)
{
cmdln_lograwRS485 = true;
}
}
initButtons(&_aqualink_data);
@ -746,6 +795,10 @@ int main(int argc, char *argv[])
if (cmdln_debugRS485)
_config_parameters.debug_RSProtocol_packets = true;
if (cmdln_lograwRS485)
_config_parameters.log_raw_RS_bytes = true;
if (_config_parameters.display_warnings_web == true)
setLoggingPrms(_config_parameters.log_level, _config_parameters.deamonize, _config_parameters.log_file, _aqualink_data.last_display_message);
else
@ -949,7 +1002,7 @@ void logPacket(unsigned char *packet_buffer, int packet_length)
void caculate_ack_packet(int rs_fd, unsigned char *packet_buffer) {
static int delayAckCnt = 0;
//if (!_aqualink_data.simulate_panel || _aqualink_data.active_thread.thread_id != 0) {
// if PDA mode, should we sleep? if not Can only send command to status message on PDA.
if (_config_parameters.pda_mode == true) {
@ -1025,6 +1078,7 @@ void main_loop()
bool interestedInNextAck = false;
bool changed = false;
int swg_zero_cnt = 0;
int swg_noreply_cnt = 0;
int i;
//int delayAckCnt = 0;
@ -1060,8 +1114,10 @@ void main_loop()
_aqualink_data.pumps[i].watts = TEMP_UNKNOWN;
}
if (_config_parameters.force_swg == true)
if (_config_parameters.force_swg == true) {
_aqualink_data.swg_percent = 0;
_aqualink_data.swg_ppm = 0;
}
if (!start_net_services(&mgr, &_aqualink_data, &_config_parameters))
{
@ -1108,12 +1164,15 @@ void main_loop()
else
{
logMessage(LOG_ERR, "Aqualink daemon looks like serial error, resetting.\n");
close_serial_port(rs_fd);
}
rs_fd = init_serial_port(_config_parameters.serial_port);
blank_read = 0;
}
if (_config_parameters.read_pentair_packets)
if (_config_parameters.log_raw_RS_bytes)
packet_length = get_packet_new_lograw(rs_fd, packet_buffer);
else if (_config_parameters.read_pentair_packets)
packet_length = get_packet_new(rs_fd, packet_buffer);
else
packet_length = get_packet(rs_fd, packet_buffer);
@ -1138,10 +1197,10 @@ void main_loop()
{
blank_read = 0;
changed = false;
/*
if (_config_parameters.debug_RSProtocol_packets || getLogLevel() >= LOG_DEBUG_SERIAL)
logPacket(packet_buffer, packet_length);
*/
if (packet_length > 0 && packet_buffer[PKT_DEST] == _config_parameters.device_id)
{
@ -1151,29 +1210,34 @@ void main_loop()
//logMessage(LOG_DEBUG, "RS received packet of type %s length %d\n", get_packet_type(packet_buffer, packet_length), packet_length);
//debugPacketPrint(0x00, packet_buffer, packet_length);
//unsigned char ID, unsigned char *packet_buffer, int packet_length)
/*
// Process the packet. This includes deriving general status, and identifying
// warnings and errors. If something changed, notify any listeners
if (process_packet(packet_buffer, packet_length) != false)
{
broadcast_aqualinkstate(mgr.active_connections);
}
*/
// If we are not in PDA or Simulator mode, just sent ACK & any CMD, else caculate the ACK.
if (!_aqualink_data.simulate_panel && !_config_parameters.pda_mode)
send_ack(rs_fd, pop_aq_cmd(&_aqualink_data));
else
caculate_ack_packet(rs_fd, packet_buffer);
/* MOVE PROCESSING TO AFTER ACK, long programming will fail otherwise (like set time) */
// Process the packet. This includes deriving general status, and identifying
// warnings and errors. If something changed, notify any listeners
if (process_packet(packet_buffer, packet_length) != false)
{
//broadcast_aqualinkstate(mgr.active_connections);
changed = true;
}
//_aqualink_data.last_packet_type = packet_buffer[PKT_CMD];
// If we are not in PDA or Simulator mode, just sent ACK & any CMD, else caculate the ACK.
if (!_aqualink_data.simulate_panel && !_config_parameters.pda_mode) {
send_ack(rs_fd, pop_aq_cmd(&_aqualink_data));
} else
caculate_ack_packet(rs_fd, packet_buffer);
/* MOVE PROCESSING TO AFTER ACK, long programming will fail otherwise (like set time) */
// Process the packet. This includes deriving general status, and identifying
// warnings and errors. If something changed, notify any listeners
/*
if (process_packet(packet_buffer, packet_length) != false)
{
//broadcast_aqualinkstate(mgr.active_connections);
changed = true;
}
*/
}/*
else if (_config_parameters.use_PDA_auxiliary && packet_length > 0 && packet_buffer[PKT_DEST] == 0x60 && _aqualink_data.aqbuttons[PUMP_INDEX].led->state != OFF)
{
@ -1190,6 +1254,7 @@ void main_loop()
if (packet_buffer[PKT_DEST] == DEV_MASTER && interestedInNextAck == true)
{
swg_noreply_cnt = 0;
if (packet_buffer[PKT_CMD] == CMD_PPM)
{
_aqualink_data.ar_swg_status = packet_buffer[5];
@ -1207,10 +1272,13 @@ void main_loop()
}
interestedInNextAck = false;
}
else if (interestedInNextAck == true && packet_buffer[PKT_DEST] != DEV_MASTER && _aqualink_data.ar_swg_status != 0x00)
//else if (interestedInNextAck == true && packet_buffer[PKT_DEST] != DEV_MASTER && _aqualink_data.ar_swg_status != 0x00)
else if (interestedInNextAck == true && packet_buffer[PKT_DEST] != DEV_MASTER && _aqualink_data.ar_swg_status != SWG_STATUS_OFF)
{
_aqualink_data.ar_swg_status = SWG_STATUS_OFF;
changed = true;
if ( ++swg_noreply_cnt < 3 ) {
_aqualink_data.ar_swg_status = SWG_STATUS_OFF;
changed = true;
}
interestedInNextAck = false;
}
else if (packet_buffer[PKT_DEST] == SWG_DEV_ID)
@ -1221,7 +1289,7 @@ void main_loop()
if (packet_buffer[3] == CMD_PERCENT && _aqualink_data.active_thread.thread_id == 0)
{
// SWG can get ~10 messages to set to 0 then go back again for some reason, so don't go to 0 until 10 messages are received
if (swg_zero_cnt < _config_parameters.swg_zero_ignore && packet_buffer[4] == 0x00 && packet_buffer[5] == 0x73) {
if (swg_zero_cnt <= _config_parameters.swg_zero_ignore && packet_buffer[4] == 0x00 && packet_buffer[5] == 0x73) {
logMessage(LOG_DEBUG, "Ignoring SWG set to %d due to packet packet count %d <= %d from control panel to SWG 0x%02hhx 0x%02hhx\n", (int)packet_buffer[4],swg_zero_cnt,_config_parameters.swg_zero_ignore,packet_buffer[4],packet_buffer[5]);
swg_zero_cnt++;
} else if (swg_zero_cnt > _config_parameters.swg_zero_ignore && packet_buffer[4] == 0x00 && packet_buffer[5] == 0x73) {
@ -1235,6 +1303,10 @@ void main_loop()
changed = true;
//logMessage(LOG_DEBUG, "SWG set to %d due to packet from control panel to SWG 0x%02hhx 0x%02hhx\n", _aqualink_data.swg_percent,packet_buffer[4],packet_buffer[5]);
}
if (_aqualink_data.swg_percent > 100)
_aqualink_data.boost = true;
else
_aqualink_data.boost = false;
}
}
else

View File

@ -89,6 +89,7 @@ void init_parameters (struct aqconfig * parms)
parms->read_pentair_packets = false;
parms->swg_zero_ignore = 0;
parms->display_warnings_web = false;
parms->log_raw_RS_bytes = false;
generate_mqtt_id(parms->mqtt_ID, MQTT_ID_LEN);
}

View File

@ -66,6 +66,7 @@ struct aqconfig
//bool use_PDA_auxiliary;
bool read_pentair_packets;
bool debug_RSProtocol_packets;
bool log_raw_RS_bytes;
//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];

View File

@ -264,13 +264,13 @@ int build_device_JSON(struct aqualinkdata *aqdata, int programable_switch1, int
length += sprintf(buffer+length, "{\"type\": \"setpoint_swg\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"spvalue\": \"%.*f\", \"value\": \"%.*f\", \"int_status\": \"%d\" },",
SWG_TOPIC,
"Salt Water Generator",
aqdata->ar_swg_status == 0x00?JSON_ON:JSON_OFF,
aqdata->ar_swg_status == 0x00?JSON_ON:JSON_OFF,
aqdata->ar_swg_status == SWG_STATUS_OFF?JSON_OFF:JSON_ON,
aqdata->ar_swg_status == SWG_STATUS_OFF?JSON_OFF:JSON_ON,
((homekit)?2:0),
((homekit_f)?degFtoC(aqdata->swg_percent):aqdata->swg_percent),
((homekit)?2:0),
((homekit_f)?degFtoC(aqdata->swg_percent):aqdata->swg_percent),
aqdata->ar_swg_status == 0x00?1:0);
aqdata->ar_swg_status == SWG_STATUS_OFF?LED2int(OFF):LED2int(ON));
//length += sprintf(buffer+length, "{\"type\": \"value\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"value\": \"%d\" },",
length += sprintf(buffer+length, "{\"type\": \"value\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"value\": \"%.*f\" },",
@ -279,6 +279,14 @@ int build_device_JSON(struct aqualinkdata *aqdata, int programable_switch1, int
"on",
((homekit_f)?2:0),
((homekit_f)?degFtoC(aqdata->swg_percent):aqdata->swg_percent));
if (!homekit) { // For the moment keep boost off homekit
length += sprintf(buffer+length, "{\"type\": \"switch\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"int_status\": \"%d\"},",
SWG_BOOST_TOPIC,
"SWG Boost",
aqdata->boost?JSON_ON:JSON_OFF,
aqdata->boost?JSON_ON:JSON_OFF,
aqdata->boost?LED2int(ON):LED2int(OFF));
}
}
if ( aqdata->swg_ppm != TEMP_UNKNOWN ) {
@ -290,13 +298,14 @@ int build_device_JSON(struct aqualinkdata *aqdata, int programable_switch1, int
((homekit)?2:0),
((homekit_f)?roundf(degFtoC(aqdata->swg_ppm)):aqdata->swg_ppm));
/*
/*
length += sprintf(buffer+length, "{\"type\": \"value\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"value\": \"%d\" },",
SWG_PPM_TOPIC,
"Salt Level PPM",
"on",
aqdata->swg_ppm);
*/
*/
}
length += sprintf(buffer+length, "{\"type\": \"temperature\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"value\": \"%.*f\" },",
@ -395,6 +404,9 @@ int build_aqualink_status_JSON(struct aqualinkdata *aqdata, char* buffer, int si
length += sprintf(buffer+length, ",\"battery\":\"%s\"",JSON_OK );//"ok",
else
length += sprintf(buffer+length, ",\"battery\":\"%s\"",JSON_LOW );//"ok",
if ( aqdata->swg_percent == 101 )
length += sprintf(buffer+length, ",\"swg_boost_msg\":\"%s\"",aqdata->boost_msg );
length += sprintf(buffer+length, ",\"leds\":{" );
for (i=0; i < TOTAL_BUTTONS; i++)
@ -423,7 +435,8 @@ int build_aqualink_status_JSON(struct aqualinkdata *aqdata, char* buffer, int si
}
if ( aqdata->swg_percent != TEMP_UNKNOWN ) {
length += sprintf(buffer+length, ", \"%s\": \"%s\"", SWG_TOPIC, aqdata->ar_swg_status == 0x00?JSON_ON:JSON_OFF);
length += sprintf(buffer+length, ", \"%s\": \"%s\"", SWG_TOPIC, aqdata->ar_swg_status == SWG_STATUS_OFF?JSON_OFF:JSON_ON);
length += sprintf(buffer+length, ", \"%s\": \"%s\"", SWG_BOOST_TOPIC, aqdata->boost?JSON_ON:JSON_OFF);
}
//NSF Need to come back and read what the display states when Freeze protection is on
if ( aqdata->frz_protect_set_point != TEMP_UNKNOWN ) {

View File

@ -399,7 +399,8 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
send_mqtt_string_msg(nc, BATTERY_STATE, _aqualink_data->battery==OK?MQTT_ON:MQTT_OFF);
}
if (_aqualink_data->ar_swg_status == SWG_STATUS_ON) { // If the SWG is actually on
//if (_aqualink_data->ar_swg_status == SWG_STATUS_ON) { // If the SWG is actually on
if (_aqualink_data->ar_swg_status != SWG_STATUS_OFF) { // If the SWG is actually on
if (_aqualink_data->swg_percent != TEMP_UNKNOWN && (force_update || _aqualink_data->swg_percent != _last_mqtt_aqualinkdata.swg_percent)) {
_last_mqtt_aqualinkdata.swg_percent = _aqualink_data->swg_percent;
send_mqtt_numeric_msg(nc, SWG_PERCENT_TOPIC, _aqualink_data->swg_percent);
@ -448,7 +449,7 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
if (!_aqualink_data->simulate_panel)
sprintf(_aqualink_data->last_display_message, "AquaPure High Salt");
send_domoticz_mqtt_status_message(nc, _aqualink_config->dzidx_swg_status, 3, "HIGH SALT");
send_mqtt_int_msg(nc, SWG_TOPIC, SWG_OFF);
send_mqtt_int_msg(nc, SWG_TOPIC, SWG_ON);
break;
case SWG_STATUS_HIGH_CURRENT:
if (!_aqualink_data->simulate_panel)
@ -494,11 +495,17 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
break;
default:
send_domoticz_mqtt_status_message(nc, _aqualink_config->dzidx_swg_status, 4, "Unknown");
send_mqtt_int_msg(nc, SWG_TOPIC, SWG_ON);
break;
}
_last_mqtt_aqualinkdata.ar_swg_status = _aqualink_data->ar_swg_status;
}
if (_aqualink_data->boost != _last_mqtt_aqualinkdata.boost) {
send_mqtt_int_msg(nc, SWG_BOOST_TOPIC, _aqualink_data->boost);
_last_mqtt_aqualinkdata.boost = _aqualink_data->boost;
}
//logMessage(LOG_INFO, "mqtt_broadcast_aqualinkstate: START LEDs\n");
//if (time(NULL) % 2) {} <-- use to determin odd/even second in time to make state flash on enabled.
@ -868,7 +875,7 @@ void action_websocket_request(struct mg_connection *nc, struct websocket_message
if (strcmp(request.first.key, "raw") == 0) {
_aqualink_data->simulate_panel = true;
logMessage (LOG_WARNING, "WS: Send raw command to controller %s\n",request.first.value);
logMessage (LOG_NOTICE, "WS: Send raw command to controller %s\n",request.first.value);
unsigned int n;
sscanf(request.first.value, "0x%2x", &n);
//aq_programmer(AQ_SEND_CMD, (char *)&n, NULL);
@ -892,6 +899,14 @@ void action_websocket_request(struct mg_connection *nc, struct websocket_message
char labels[JSON_LABEL_SIZE];
build_aux_labels_JSON(_aqualink_data, labels, JSON_LABEL_SIZE);
ws_send(nc, labels);
} else if ( strcmp(request.first.value, SWG_BOOST_TOPIC) == 0) {
//logMessage(LOG_INFO, "Boost ");
if (request.second.value != NULL)
_aqualink_data->unactioned.value = request2bool(request.second.value);
else
_aqualink_data->unactioned.value = !_aqualink_data->boost;
_aqualink_data->unactioned.type = SWG_BOOST;
} else { // Search for value in command list
int i;
for (i = 0; i < TOTAL_BUTTONS; i++) {
@ -1022,6 +1037,9 @@ void action_mqtt_message(struct mg_connection *nc, struct mg_mqtt_message *msg)
}
_aqualink_data->unactioned.value = setpoint_check(SWG_SETPOINT, val, _aqualink_data);
_aqualink_data->unactioned.type = SWG_SETPOINT;
} else if ((pt3 != NULL && (strncmp(pt1, "SWG", 3) == 0) && (strncmp(pt2, "Boost", 5) == 0) && (strncmp(pt3, "set", 3) == 0))) {
_aqualink_data->unactioned.value = round(value);
_aqualink_data->unactioned.type = SWG_BOOST;
} else if (pt2 != NULL && (strncmp(pt2, "set", 3) == 0) && (strncmp(pt2, "setpoint", 8) != 0)) {
// Must be a switch on / off
for (i=0; i < TOTAL_BUTTONS; i++) {
@ -1277,6 +1295,7 @@ void start_mqtt(struct mg_mgr *mgr) {
_last_mqtt_aqualinkdata.ar_swg_status = SWG_STATUS_UNKNOWN;
_last_mqtt_aqualinkdata.battery = -1;
_last_mqtt_aqualinkdata.frz_protect_state = -1;
_last_mqtt_aqualinkdata.boost = -1;
_mqtt_exit_flag = false; // set here to stop multiple connects, if it fails truley fails it will get set to false.
}
}

View File

@ -7,6 +7,7 @@
#include "utils.h"
static FILE *_packetLogFile = NULL;
static FILE *_byteLogFile = NULL;
static bool _log2file = false;
static bool _includePentair = false;
@ -20,12 +21,15 @@ void startPacketLogger(bool debug_RSProtocol_packets, bool read_pentair_packets)
void stopPacketLogger() {
if (_packetLogFile != NULL)
fclose(_packetLogFile);
if (_byteLogFile != NULL)
fclose(_byteLogFile);
}
void writePacketLog(char *buffer) {
if (_packetLogFile == NULL)
_packetLogFile = fopen(RS485LOGFILE, "a");
_packetLogFile = fopen(RS485LOGFILE, "w");
if (_packetLogFile != NULL) {
fputs(buffer, _packetLogFile);
@ -68,4 +72,25 @@ void _logPacket(unsigned char *packet_buffer, int packet_length, bool error)
logMessage(LOG_WARNING, "%s", buff);
else
logMessage(LOG_DEBUG_SERIAL, "%s", buff);
}
//#define RAW_BUFFER_SIZE 100
// Log Raw Bytes
void logPacketByte(unsigned char *byte)
{
char buff[10];
//static int _length = 0;
//static unsigned char _bytes[RAW_BUFFER_SIZE];
//_bytes[_length++] = byte;
//if (_length >= RAW_BUFFER_SIZE) {
if (_byteLogFile == NULL)
_byteLogFile = fopen(RS485BYTELOGFILE, "w");
if (_byteLogFile != NULL) {
sprintf(buff, "0x%02hhx|",*byte);
fputs( buff, _byteLogFile);
}
//}
}

View File

@ -4,12 +4,14 @@
#include <stdbool.h>
#define RS485LOGFILE "/tmp/RS485.log"
#define RS485BYTELOGFILE "/tmp/RS485raw.log"
void startPacketLogger(bool debug_RSProtocol_packets, bool read_pentair_packets);
void stopPacketLogger();
//void logPacket(unsigned char *packet_buffer, int packet_length, bool checksumerror);
void logPacket(unsigned char *packet_buffer, int packet_length);
void logPacketError(unsigned char *packet_buffer, int packet_length);
void logPacketByte(unsigned char *byte);
#endif //PACKETLOGGER_H_

Binary file not shown.

View File

@ -20,8 +20,8 @@ log_level=DEBUG
#log_level=INFO
#log_level=NOTICE
# Display any ERROR or Warning messages in web interface.
display_warnings_in_web=false
# Display any ERROR & Warning messages in web interface.
display_warnings_in_web=true
# The socket port that the daemon listens to
# If you change this from 80, remember to update aqualink.service.avahi

Binary file not shown.

11
utils.c
View File

@ -505,6 +505,15 @@ bool text2bool(char *str)
return FALSE;
}
bool request2bool(char *str)
{
str = cleanwhitespace(str);
if (strcasecmp (str, "YES") == 0 || strcasecmp (str, "ON") == 0 || atoi(str) == 1)
return TRUE;
else
return FALSE;
}
char *bool2text(bool val)
{
if(val == TRUE)
@ -616,3 +625,5 @@ void closePacketLog() {
fclose(_packetLogFile);
}
*/

View File

@ -45,6 +45,7 @@ char *trimwhitespace(char *str);
char *stripwhitespace(char *str);
int cleanint(char*str);
bool text2bool(char *str);
bool request2bool(char *str);
char *bool2text(bool val);
void delay (unsigned int howLong);
float degFtoC(float degF);

View File

@ -1,4 +1,4 @@
#define AQUALINKD_NAME "Aqualink Daemon"
#define AQUALINKD_VERSION "1.3.6"
#define AQUALINKD_VERSION "1.3.7"

View File

@ -19,6 +19,7 @@
"SWG",
//"SWG/Percent",
"SWG/PPM",
"SWG/Boost",
"Temperature/Air",
"Temperature/Pool",
"Temperature/Spa",

View File

@ -792,6 +792,23 @@
}
}
function setTileAttribute(id, attribute, value) {
var tile;
if ((tile = document.getElementById(id)) != null) {
//console.log("Set Attribute "+attribute+ " to "+value+" for "+id);
tile.setAttribute(attribute, value);
}
}
function getTileAttribute(id, attribute) {
var tile;
if ((tile = document.getElementById(id)) != null) {
//console.log("get Attribute "+attribute+ " to "+tile.getAttribute(attribute)+" for "+id);
return tile.getAttribute(attribute);
}
return null;
}
function formatSatus(status) {
var index;
if ((index = status.indexOf("AUX")) >= 0) {
@ -840,8 +857,12 @@
}
var type;
if ((type = tile.getAttribute('type')) != null) {
if (type == 'setpoint_swg')
text = 'Generating';
if (type == 'setpoint_swg') {
if (tile.getAttribute('Boost') == 'on')
text = "Boost";
else
text = 'Generating';
}
else if (type == 'setpoint_thermo')
text = 'Heating to ' + tile.getAttribute('setpoint');
else if (type == 'setpoint_freeze')
@ -896,7 +917,8 @@
return;
}
if (object.type == 'switch' || object.type == 'switch_program') {
add_tile(object.id, object.name, object.state, 'switch', object.type, 'hk/' + object.id + '-off.png', 'hk/' + object.id + '-on.png');
var img = object.id.replace('/', '_');
add_tile(object.id, object.name, object.state, 'switch', object.type, 'hk/' + img + '-off.png', 'hk/' + img + '-on.png');
setTileOn(object.id, object.status, null);
if (typeof object.Pump_RPM !== 'undefined' && object.Pump_RPM) {
setTileOnText(object.id, 'RPM:'+object.Pump_RPM);
@ -936,7 +958,7 @@
if (optionH <= wrapH)
active_option.style.height = wrapH;
console.log("Option:"+optionH+" wrapH:"+wrapH);
//console.log("Option:"+optionH+" wrapH:"+wrapH);
document.getElementById('wrapper').classList.add("opaque");
} else {
// Fake onclick to close and save any values if open
@ -993,7 +1015,7 @@
}
if (type == 'setpoint_swg') {
slider.min = 0;
slider.max = 100;
slider.max = 101;
slider.step = 5;
} else if (type == 'setpoint_freeze') {
if (_temperature_units != 'c') { // Change to DegF
@ -1026,6 +1048,23 @@
oswitch.onclick = function() {
oswitch_output.innerHTML = ((oswitch.checked) ? "On" : "Off");
}
} else if (type == 'setpoint_swg') {
slider.value = sp_value;
oswitch = document.getElementById("swg_option_switch");
//oswitch.checked = tile_state;
//console.log("Boost attribute " + getTileAttribute(id, "boost") + " for "+id);
oswitch.checked = getTileAttribute(id, "Boost")=='on'?true:false ;
var oswitch_output = document.getElementById("swg_option_switch_text_value");
slider_output.innerHTML = slider.value + ext;
oswitch_output.innerHTML = ((oswitch.checked) ? "Boost On" : "Boost Off");
slider.oninput = function() {
slider_output.innerHTML = this.value + ext;
//sp_value = this.value
}
oswitch.onclick = function() {
oswitch_output.innerHTML = ((oswitch.checked) ? "Boost On" : "Boost Off");
//setTileOn(id, ((oswitch.checked)?"on":"off"), null);
}
} else {
slider.value = sp_value;
oswitch = document.getElementById("option_switch");
@ -1079,6 +1118,16 @@
// if (state == (tile.getAttribute('status') == 'off')) // Only bother with this if we didn;t set the light mode.
setTileState(id, state);
}
} else if (type == 'setpoint_swg') {
//console.log ("Boost attribute = "+tile.getAttribute('boost'));
//console.log ("state = "+state);
if (state == (tile.getAttribute('Boost') == 'off')) {
//console.log ("change boost");
send_command( 'SWG/Boost', (state==true?"on":"off") );
} else {
if (sp_value != slider.value && slider.value != 101) // Don't change setpoint if slider is on boost
setThermostatSetpoint(id, slider.value);
}
} else {
var value = slider.value;
if (state == (tile.getAttribute('status') == 'off'))
@ -1128,9 +1177,25 @@
setTileValue("Pool_Water", data.pool_temp);
setTileValue("Spa_Water", data.spa_temp);*/
for (var obj in data.leds) {
setTileOn(obj.toString(), data.leds[obj])
setTileOn(obj.toString(), data.leds[obj]);
if (obj.toString() == 'SWG/Boost') {
//console.log("BOOST IS "+data.leds[obj]);
setTileAttribute('SWG', "Boost", data.leds[obj]);
}
}
if (data.swg_boost_msg != null ) {
var tile = document.getElementById('SWG');
if (tile != null) {
var sp_value = tile.getAttribute('setpoint');
if (sp_value == 101)
document.getElementById('SWG' + '_status').innerHTML = 'Boost '+data.swg_boost_msg;
}
}
//obj.leds.'SWG/Boost'
// NSF Really quick hack to show RPM on filter pump. Need to come back and do this correctly.
/*
if ((typeof data.extra["Pump_1"] !== 'undefined') && (typeof data.extra["Pump_1"].RPM !== 'undefined')) {
@ -1251,9 +1316,11 @@
socket_di.send(JSON.stringify(msg));
}
function send_command(cmd) {
function send_command(cmd, value=null) {
var _cmd = {};
_cmd.command = cmd;
if (value != null)
_cmd.value = value;
socket_di.send(JSON.stringify(_cmd));
}
@ -1381,6 +1448,16 @@
<th colspan='2'><span id="swg_option_title"></span>
</th>
</tr>
<tr>
<td align='right' width='50%'><span class="option_text" id='swg_option_switch_text_value'></span>
</td>
<td align='left' width='50%'>
<label class="option_switch">
<input type="checkbox" id='swg_option_switch'>
<span class="option_switch_slide"></span>
</label>
</td>
</tr>
<tr>
<td colspan='2' align='center'><span class="option_text" id="swg_option_slider_text_value"></span>
</td>

1
web/hk/SWG_Boost-off.png Symbolic link
View File

@ -0,0 +1 @@
switch-off.png

1
web/hk/SWG_Boost-on.png Symbolic link
View File

@ -0,0 +1 @@
switch-on.png