pull/116/head
shaun feakes 2020-06-06 11:36:04 -05:00
parent 41ebef22e3
commit 50c48ef6b1
29 changed files with 910 additions and 202 deletions

View File

@ -12,7 +12,7 @@ LIBS := -l pthread -l m
# debug of not
#DBG = -g -O0 -fsanitize=address
DBG = -g
#DBG = -g -O0
#DBG = -D ONETOUCH
#DBG =
@ -33,13 +33,14 @@ CFLAGS = $(GCCFLAGS) $(DBG) $(LIBS) -D MG_DISABLE_MD5 -D MG_DISABLE_HTTP_DIGEST_
# Add inputs and outputs from these tool invocations to the build variables
# define the C source files
SRCS = aqualinkd.c utils.c config.c aq_serial.c init_buttons.c aq_programmer.c net_services.c json_messages.c pda.c pda_menu.c pda_aq_programmer.c aquapure.c onetouch.c onetouch_aq_programmer.c packetLogger.c pentair_messages.c mongoose.c
SRCS = aqualinkd.c utils.c config.c aq_serial.c init_buttons.c aq_programmer.c net_services.c json_messages.c pda.c pda_menu.c \
pda_aq_programmer.c devices_jandy.c onetouch.c onetouch_aq_programmer.c packetLogger.c devices_pentair.c color_lights.c mongoose.c
DBG_SRC = timespec_subtract.c
# If run with `make DEBUG=true` add debug files and pass parameter for compile
ifeq ($(DEBUG), true)
SRCS := $(SRCS) $(DBG_SRC)
CFLAGS := $(CFLAGS) -D AQ_DEBUG
CFLAGS := -g -O0 $(CFLAGS) -D AQ_DEBUG
endif
SL_SRC = serial_logger.c aq_serial.c utils.c packetLogger.c

View File

@ -67,13 +67,12 @@ Designed to mimic AqualinkRS6 All Button keypad and (like the keypad) is used to
* http://aqualink.ip/simulator.html <- (RS8 All Button Control Panel simulator)
* http://aqualink.ip/debug.html <- (Turn on/off debug/serial debug & download logs)
#<a name="release"></a>
# Update in Release 2.0.0a
# Update in Release 2.0.0b
* Big update, lots of core changes, <b>please read wiki section https://github.com/sfeakes/AqualinkD/wiki#Version_2</b>
* Full Variable Speed Pump support.
* Please consider this ALPHA version, no need to upgrade unless you need VSP support. Use a previous version unless you want VSP.
* I've noticed there is an issues with colored lights if your light is configured as such in the control panel. For this release suggest you either :-
* A) use AqualinkD to control the colors and configure Jandy control panel as normal light
* B) Don't use AqualinkD to turn light on/off (or if you do, wait ~10 seconds)
* Full Variable Speed Pump support. (Can read,set & change RPM,GPM)
* Full support for all Colored Lights (even if Jandy Control Panel doesn't support them)
* Chemlink pH & ORP now supported. (along with posting MQTT information)
* There are some configuration changes, make sure to read wiki (link above)
# Update in Release 1.3.9a
* Improved Debugging for serial.
* Added panel Timeout mode support to UI and MQTT
@ -387,7 +386,10 @@ Other Information (Salt Water Generator)
aqualinkd/SWG/Percent ( SWG Generating %, i.e. 50)
aqualinkd/SWG/PPM ( SWG Parts Per Million i.e. 3100)
aqualinkd/SWG/Percent_f (since we use a homekit thermostat for SWG and use degC as %, we need to pass degF for US phone)
aqualinkd/CHEM/pH (pH reading if Chemlink installed)
aqualinkd/CHEM/pH_f (since we use a homekit temperature for pH and use degC as %, we need to pass degF for US phone)
aqualinkd/CHEM/ORP (ORP reading if Chemlink installed)
aqualinkd/CHEM/ORP_f (since we use a homekit temperature for pH and use degC as %, we need to pass degF for US phone)
```
To turn something on, or set information, simply add `set` to the end of the above topics, and post 1 or 0 in the message for a button, or a number for a setpoint. Topics Aqualinkd will act on.

View File

@ -23,6 +23,12 @@
#define SWG_EXTENDED_TOPIC SWG_TOPIC "/fullstatus"
#define SWG_BOOST_TOPIC SWG_TOPIC "/Boost"
#define CHEM_TOPIC "CHEM"
#define CHEM_PH_TOPIC CHEM_TOPIC "/pH"
#define CHRM_PH_F_TOPIC CHEM_TOPIC "/pH_f"
#define CHEM_ORP_TOPIC CHEM_TOPIC "/ORP"
#define CHRM_ORP_F_TOPIC CHEM_TOPIC "/ORP_f"
#define FREEZE_PROTECT "Freeze_Protect"
#define FREEZE_PROTECT_ENABELED FREEZE_PROTECT ENABELED_SUBT

View File

@ -31,6 +31,7 @@
#include "init_buttons.h"
#include "pda_aq_programmer.h"
#include "onetouch_aq_programmer.h"
#include "color_lights.h"
#ifdef AQ_DEBUG
#include <time.h>
@ -53,6 +54,7 @@ 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_programmode( void *ptr );
void *set_aqualink_light_colormode( void *ptr );
void *set_aqualink_PDA_init( void *ptr );
void *set_aqualink_SWG( void *ptr );
@ -294,8 +296,8 @@ int setpoint_check(int type, int value, struct aqualinkdata *aqdata)
if (rtn != value)
logMessage(LOG_WARNING, "Setpoint of %d for %s is outside range, using %d\n",value,type_msg,rtn);
else
logMessage(LOG_NOTICE, "Setting setpoint of %s to %d\n",type_msg,rtn);
//else
// logMessage(LOG_NOTICE, "Setting setpoint of %s to %d\n",type_msg,rtn);
return rtn;
}
@ -502,7 +504,13 @@ void aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_data
return;
}
break;
case AQ_SET_COLORMODE:
case AQ_SET_LIGHTPROGRAM_MODE:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_light_programmode, (void*)programmingthread) < 0) {
logMessage (LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_SET_LIGHTCOLOR_MODE:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_light_colormode, (void*)programmingthread) < 0) {
logMessage (LOG_ERR, "could not create thread\n");
return;
@ -599,7 +607,7 @@ void aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_data
}
break;
default:
logMessage (LOG_ERR, "Don't understand thread type\n");
logMessage (LOG_ERR, "Didn't understand programming mode type\n");
break;
}
@ -671,7 +679,7 @@ void cleanAndTerminateThread(struct programmingThreadCtrl *threadCtrl)
struct timespec elapsed;
clock_gettime(CLOCK_REALTIME, &threadCtrl->aq_data->last_active_time);
timespec_subtract(&elapsed, &threadCtrl->aq_data->last_active_time, &threadCtrl->aq_data->start_active_time);
logMessage(LOG_DEBUG, "Thread %d,%p (%s) finished in %d.%03ld sec\n",
logMessage(LOG_NOTICE, "Thread %d,%p (%s) finished in %d.%03ld sec\n",
threadCtrl->aq_data->active_thread.ptype,
threadCtrl->aq_data->active_thread.thread_id,
ptypeName(threadCtrl->aq_data->active_thread.ptype),
@ -921,7 +929,7 @@ void *set_aqualink_SWG( void *ptr )
return ptr;
}
logMessage(LOG_DEBUG, "programming SWG percent to %d\n", val);
logMessage(LOG_NOTICE, "programming SWG percent to %d\n", val);
if ( select_menu_item(aq_data, "SET AQUAPURE") != true ) {
logMessage(LOG_WARNING, "Could not select SET AQUAPURE menu\n");
@ -1021,7 +1029,97 @@ void *set_aqualink_light_colormode( void *ptr )
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_COLORMODE);
waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_LIGHTCOLOR_MODE);
char *buf = (char*)threadCtrl->thread_args;
const char *mode_name;
int val = atoi(&buf[0]);
int btn = atoi(&buf[5]);
int typ = atoi(&buf[10]);
if (btn < 0 || btn >= TOTAL_BUTTONS ) {
logMessage(LOG_ERR, "Can't program light mode on button %d\n", btn);
cleanAndTerminateThread(threadCtrl);
return ptr;
}
aqkey *button = &aq_data->aqbuttons[btn];
unsigned char code = button->code;
//logMessage(LOG_NOTICE, "Light Programming #: %d, on button: %s, color light type: %d\n", val, button->label, typ);
mode_name = light_mode_name(typ, val-1);
if (mode_name == NULL) {
logMessage(LOG_ERR, "Light Programming #: %d, on button: %s, color light type: %d, couldn't find mode name '%s'\n", val, button->label, typ, mode_name);
cleanAndTerminateThread(threadCtrl);
return ptr;
} else {
logMessage(LOG_NOTICE, "Light Programming #: %d, on button: %s, color light type: %d, name '%s'\n", val, button->label, typ, mode_name);
}
// Simply turn the light off if value is 0
if (val <= 0) {
if ( button->led->state == ON ) {
send_cmd(code);
}
cleanAndTerminateThread(threadCtrl);
return ptr;
}
// Needs to start programming sequence with light off
if ( button->led->state == ON ) {
logMessage(LOG_INFO, "Light Programming Initial state on, turning off\n");
send_cmd(code);
waitfor_queue2empty();
if ( !waitForMessage(threadCtrl->aq_data, "OFF", 5)) // Message like 'Aux3 Off'
logMessage(LOG_ERR, "Light Programming didn't receive OFF message\n");
}
// Now turn on and wait for the message "color mode name<>*"
send_cmd(code);
waitfor_queue2empty();
i=0;
do{
if ( !waitForMessage(threadCtrl->aq_data, "~*", 3))
logMessage(LOG_ERR, "Light Programming didn't receive color light mode message\n");
if (strncasecmp(aq_data->last_message, mode_name, strlen(mode_name)) == 0) {
logMessage(LOG_INFO, "Light Programming found color mode %s\n",mode_name);
send_cmd(KEY_ENTER);
waitfor_queue2empty();
break;
}
send_cmd(KEY_RIGHT);
waitfor_queue2empty();
// Just clear current message before waiting for next, since the test in the do can't distinguish
// as both messages end in "~*"
waitForMessage(threadCtrl->aq_data, NULL, 1);
i++;
} while (i <= LIGHT_COLOR_OPTIONS);
if (i == LIGHT_COLOR_OPTIONS) {
logMessage(LOG_ERR, "Light Programming didn't receive color light mode message for '%s'\n",mode_name);
}
cleanAndTerminateThread(threadCtrl);
// just stop compiler error, ptr is not valid as it's just been freed
return ptr;
}
void *set_aqualink_light_programmode( void *ptr )
{
int i;
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_LIGHTPROGRAM_MODE);
char *buf = (char*)threadCtrl->thread_args;
int val = atoi(&buf[0]);
@ -1535,7 +1633,8 @@ void _waitfor_queue2empty(bool longwait)
{
int i=0;
while ( (_pgm_command != NUL) && ( i++ < (30*(longwait?2:1) ) ) ) {
//while ( (_pgm_command != NUL) && ( i++ < (30*(longwait?2:1) ) ) ) {
while ( (_pgm_command != NUL) && ( i++ < (50*(longwait?2:1) ) ) ) {
//sleep(1); // NSF Change to smaller time.
//logMessage(LOG_DEBUG, "******** QUEUE IS FULL ******** delay\n");
delay(50);
@ -1544,7 +1643,7 @@ void _waitfor_queue2empty(bool longwait)
if (_pgm_command != NUL) {
if (pda_mode()) {
// Wait for longer in PDA mode since it's slower.
while ( (_pgm_command != NUL) && ( i++ < (130*(longwait?2:1)) ) ) {
while ( (_pgm_command != NUL) && ( i++ < (150*(longwait?2:1)) ) ) {
delay(100);
}
}
@ -1866,8 +1965,11 @@ const char *ptypeName(program_type type)
case AQ_GET_PROGRAMS:
return "Get programs";
break;
case AQ_SET_COLORMODE:
return "Set light color";
case AQ_SET_LIGHTPROGRAM_MODE:
return "Set light color (using AqualinkD)";
break;
case AQ_SET_LIGHTCOLOR_MODE:
return "Set light color (using Panel)";
break;
case AQ_PDA_INIT:
return "Init PDA";
@ -1891,7 +1993,7 @@ const char *ptypeName(program_type type)
return "SWG Boost";
break;
case AQ_SET_ONETOUCH_PUMP_RPM:
return "Set Pump RPM";
return "Set OneTouch Pump RPM";
break;
case AQ_SET_ONETOUCH_MACRO:
return "Set OneTouch Macro";
@ -1923,3 +2025,70 @@ const char *ptypeName(program_type type)
break;
}
}
// Cleaner version of above for UI display purposes.
const char *programtypeDisplayName(program_type type)
{
switch (type) {
case AQ_GET_POOL_SPA_HEATER_TEMPS:
case AQ_GET_ONETOUCH_SETPOINTS:
case AQ_GET_FREEZE_PROTECT_TEMP:
case AQ_PDA_INIT:
return "Programming: retrieving setpoints";
break;
case AQ_SET_ONETOUCH_TIME:
case AQ_SET_TIME:
return "Programming: setting time";
break;
case AQ_SET_POOL_HEATER_TEMP:
case AQ_SET_ONETOUCH_POOL_HEATER_TEMP:
case AQ_SET_SPA_HEATER_TEMP:
case AQ_SET_ONETOUCH_SPA_HEATER_TEMP:
return "Programming: setting heater";
break;
case AQ_SET_FRZ_PROTECTION_TEMP:
case AQ_SET_ONETOUCH_FREEZEPROTECT:
return "Programming: setting Freeze protect";
break;
case AQ_GET_DIAGNOSTICS_MODEL:
return "Programming: retrieving diagnostics";
break;
case AQ_GET_PROGRAMS:
return "Programming: retrieving programs";
break;
case AQ_SET_LIGHTPROGRAM_MODE:
case AQ_SET_LIGHTCOLOR_MODE:
return "Programming: setting light color";
break;
case AQ_SET_SWG_PERCENT:
case AQ_SET_ONETOUCH_SWG_PERCENT:
return "Programming: setting SWG percent";
break;
case AQ_PDA_DEVICE_STATUS:
return "Programming: retrieving PDA Device status";
break;
case AQ_PDA_DEVICE_ON_OFF:
return "Programming: setting device on/off";
break;
case AQ_GET_AUX_LABELS:
return "Programming: retrieving AUX labels";
break;
case AQ_PDA_WAKE_INIT:
return "Programming: PDA wakeup";
break;
case AQ_SET_BOOST:
case AQ_SET_ONETOUCH_BOOST:
return "Programming: setting SWG Boost";
break;
case AQ_SET_ONETOUCH_PUMP_RPM:
return "Programming: setting Pump RPM";
break;
case AQ_SET_ONETOUCH_MACRO:
return "Programming: setting OneTouch Macro";
break;
default:
return "Programming: please wait!";
break;
}
}

View File

@ -40,7 +40,8 @@ typedef enum {
AQ_GET_DIAGNOSTICS_MODEL,
//AQ_SEND_CMD,
AQ_GET_PROGRAMS,
AQ_SET_COLORMODE,
AQ_SET_LIGHTPROGRAM_MODE,
AQ_SET_LIGHTCOLOR_MODE,
AQ_PDA_INIT,
AQ_SET_SWG_PERCENT,
AQ_PDA_DEVICE_STATUS,
@ -83,6 +84,8 @@ void aq_send_cmd(unsigned char cmd);
void queueGetProgramData(emulation_type source_type, struct aqualinkdata *aq_data);
void queueGetExtendedProgramData(emulation_type source_type, struct aqualinkdata *aq_data, bool labels);
unsigned char pop_aq_cmd(struct aqualinkdata *aq_data);
//bool push_aq_cmd(unsigned char cmd);
//void send_cmd(unsigned char cmd, struct aqualinkdata *aq_data);
@ -96,6 +99,7 @@ int setpoint_check(int type, int value, struct aqualinkdata *aqdata);
int RPM_check(pump_type type, int value, struct aqualinkdata *aqdata);
//int RPM_check(int type, int value, struct aqualinkdata *aqdata);
const char *ptypeName(program_type type);
const char *programtypeDisplayName(program_type type);
// These shouldn't be here, but just for the PDA AQ PROGRAMMER
void send_cmd(unsigned char cmd);

View File

@ -84,6 +84,11 @@ bool onetouch_enabled()
{
return _onetouch_enabled;
}
bool VSP_enabled()
{
// At present this is dependant on onetouch.
return onetouch_enabled();
}
void set_extended_device_id_programming(bool mode)
{

View File

@ -25,6 +25,18 @@
#define SWG_DEV_ID 0x50
#define IAQ_DEV_ID 0x33
/* Few Device ID's in decimal for quick checking
# Pentair pump ID's
# 0x60 to 0x6F (0x60, 0x61 0x62, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F)
# Jandy pump ID's
# 0x78, 0x79, 0x7A, 0x7B
*/
#define PENTAIR_DEC_PUMP_MIN 96
#define PENTAIR_DEC_PUMP_MAX 111
#define JANDY_DEC_PUMP_MIN 120
#define JANDY_DEC_PUMP_MAX 123
// PACKET DEFINES Jandy
#define NUL 0x00
#define DLE 0x10
@ -270,6 +282,13 @@ SPILLOVER IS DISABLED WHILE SPA IS ON
#define IAQ_KEY_AUX6 0x1e
#define IAQ_KEY_AUX7 0x1f
// At the moment just used for next ack
typedef enum {
DRS_NONE,
DRS_SWG,
DRS_EPUMP
} rsDeviceType;
typedef enum {
ON,
OFF,
@ -320,6 +339,7 @@ const char* get_packet_type(unsigned char* packet , int length);
void set_onetouch_enabled(bool mode);
bool onetouch_enabled();
bool VSP_enabled();
void set_extended_device_id_programming(bool mode);
bool extended_device_id_programming();

View File

@ -23,6 +23,7 @@
#define DATE_STRING_LEN 30
#define MAX_PUMPS 4
#define MAX_LIGHTS 4
enum {
FAHRENHEIT,
@ -90,11 +91,27 @@ typedef struct pumpd
int pumpIndex;
pump_type pumpType;
//int buttonID;
protocolType ptype;
protocolType prclType;
aqkey *button;
//bool updated;
} pump_detail;
// color light modes (Aqualink program, Jandy, Jandy LED, SAm/SAL, Color Logic, Intellibrite)
typedef enum clight_type {
LC_PROGRAMABLE=0,
LC_JANDY,
LC_JANDYLED,
LC_SAL,
LC_CLOGIG,
LC_INTELLIB
} clight_type;
typedef struct clightd
{
clight_type lightType;
aqkey *button;
} clight_detail;
struct aqualinkdata
{
//char crap[AQ_MSGLEN];
@ -132,9 +149,13 @@ struct aqualinkdata
unsigned char last_packet_type;
int num_pumps;
pump_detail pumps[MAX_PUMPS];
int num_lights;
clight_detail lights[MAX_LIGHTS];
int open_websockets;
bool boost;
char boost_msg[10];
float ph;
int orp;
//bool last_msg_was_status;
//bool ar_swg_connected;
#ifdef AQ_DEBUG

View File

@ -39,10 +39,10 @@
#include "net_services.h"
#include "pda_menu.h"
#include "pda.h"
#include "pentair_messages.h"
#include "devices_pentair.h"
#include "pda_aq_programmer.h"
#include "packetLogger.h"
#include "aquapure.h"
#include "devices_jandy.h"
#include "onetouch.h"
#include "onetouch_aq_programmer.h"
#include "version.h"
@ -496,7 +496,8 @@ void processMessage(char *message)
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, "MAINTAIN") == NULL /* && // Catch 'MAINTAIN TEMP IS OFF'
stristr(msg, "MAINTAIN") == NULL && // Catch 'MAINTAIN TEMP IS OFF'
stristr(msg, "0 PSI") == NULL /* // Catch some erronious message on test harness
stristr(msg, "CLEANER O") == NULL &&
stristr(msg, "SPA O") == NULL &&
stristr(msg, "AUX") == NULL*/
@ -575,7 +576,7 @@ 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) {
if (_aqualink_data.active_thread.ptype == AQ_SET_LIGHTPROGRAM_MODE) {
//printf ("Light thread kicking\n");
kick_aq_program_thread(&_aqualink_data, ALLBUTTON);
}
@ -742,6 +743,7 @@ int main(int argc, char *argv[])
bool cmdln_debugRS485 = false;
bool cmdln_lograwRS485 = false;
_aqualink_data.num_pumps = 0;
_aqualink_data.num_lights = 0;
/*
static unsigned char msg_loop; // = '\0';
@ -898,26 +900,30 @@ int main(int argc, char *argv[])
for (i = 0; i < TOTAL_BUTONS; i++)
{
char vsp[] = "None";
int alid = 0;
//char ext[] = " VSP ID None | AL ID 0 ";
char ext[40];
ext[0] = '\0';
for (j = 0; j < _aqualink_data.num_pumps; j++) {
//if (_aqualink_data.pumps[j].buttonID == i) {
if (_aqualink_data.pumps[j].button == &_aqualink_data.aqbuttons[i]) {
sprintf(vsp,"0x%02hhx",_aqualink_data.pumps[j].pumpID);
alid = _aqualink_data.pumps[j].pumpIndex;
//printf("Pump %d %d %d\n",_aqualink_data.pumps[j].pumpID, _aqualink_data.pumps[j].buttonID, _aqualink_data.pumps[j].ptype);
sprintf(ext, "VSP ID 0x%02hhx | PMP ID %-1d |",_aqualink_data.pumps[j].pumpID, _aqualink_data.pumps[j].pumpIndex);
}
}
if (!_aqconfig_.pda_mode) {
logMessage(LOG_NOTICE, "Config BTN %-13s = label %-15s | VSP ID %-4s | AL ID %-1d | dzidx %-3d | %s\n",
_aqualink_data.aqbuttons[i].name, _aqualink_data.aqbuttons[i].label, vsp, alid, _aqualink_data.aqbuttons[i].dz_idx,
(i>0 && (i==_aqconfig_.light_programming_button_pool || i==_aqconfig_.light_programming_button_spa)?"Programable":"") );
} else {
logMessage(LOG_NOTICE, "Config BTN %-13s = label %-15s | VSP ID %-4s | AL ID %-1d | PDAlabel %-15s | dzidx %d\n",
_aqualink_data.aqbuttons[i].name, _aqualink_data.aqbuttons[i].label, vsp, alid,
_aqualink_data.aqbuttons[i].pda_label, _aqualink_data.aqbuttons[i].dz_idx );
for (j = 0; j < _aqualink_data.num_lights; j++) {
if (_aqualink_data.lights[j].button == &_aqualink_data.aqbuttons[i]) {
sprintf(ext,"Light Progm | CTYPE %-1d |",_aqualink_data.lights[j].lightType);
}
}
if (_aqualink_data.aqbuttons[i].dz_idx > 0)
sprintf(ext+strlen(ext), "dzidx %-3d", _aqualink_data.aqbuttons[i].dz_idx);
if (!_aqconfig_.pda_mode) {
logMessage(LOG_NOTICE, "Config BTN %-13s = label %-15s | %s\n",
_aqualink_data.aqbuttons[i].name, _aqualink_data.aqbuttons[i].label, ext);
} else {
logMessage(LOG_NOTICE, "Config BTN %-13s = label %-15s | PDAlabel %-15s | %s\n",
_aqualink_data.aqbuttons[i].name, _aqualink_data.aqbuttons[i].label,
_aqualink_data.aqbuttons[i].pda_label, ext);
}
//logMessage(LOG_NOTICE, "Button %d\n", i+1, _aqualink_data.aqbuttons[i].label , _aqualink_data.aqbuttons[i].dz_idx);
}
if (_aqconfig_.deamonize == true)
@ -1023,7 +1029,8 @@ void main_loop()
int rs_fd;
int packet_length;
unsigned char packet_buffer[AQ_MAXPKTLEN+1];
bool interestedInNextAck = false;
//bool interestedInNextAck = false;
rsDeviceType interestedInNextAck = DRS_NONE;
bool changed = false;
//int swg_zero_cnt = 0;
int swg_noreply_cnt = 0;
@ -1051,6 +1058,8 @@ void main_loop()
_aqualink_data.frz_protect_state = OFF;
_aqualink_data.battery = OK;
_aqualink_data.open_websockets = 0;
_aqualink_data.ph = TEMP_UNKNOWN;
_aqualink_data.orp = TEMP_UNKNOWN;
pthread_mutex_init(&_aqualink_data.active_thread.thread_mutex, NULL);
pthread_cond_init(&_aqualink_data.active_thread.thread_cond, NULL);
@ -1148,20 +1157,20 @@ void main_loop()
}
else if (packet_length > 0)
{
/* // Use this to check wait time in mg_mgr_poll(&mgr, xx); at bottom of for, and adjust as needed.
if (blank_read > 0) {
logMessage(LOG_NOTICE, "RS empry reads %d\n", blank_read);
}
*/
blank_read = 0;
changed = false;
/*
// This is handeled by aq_serial now
if (_aqconfig_.debug_RSProtocol_packets || getLogLevel() >= LOG_DEBUG_SERIAL)
logPacket(packet_buffer, packet_length);
*/
if (packet_length > 0 && packet_buffer[PKT_DEST] == _aqconfig_.device_id)
{
if (getLogLevel() >= LOG_DEBUG)
logMessage(LOG_DEBUG, "RS received packet of type %s length %d\n", get_packet_type(packet_buffer, packet_length), packet_length);
changed = process_packet(packet_buffer, packet_length);
// If we are not in PDA or Simulator mode, just sent ACK & any CMD, else caculate the ACK.
if (!_aqualink_data.simulate_panel && !_aqconfig_.pda_mode) {
//send_ack(rs_fd, pop_aq_cmd(&_aqualink_data));
@ -1179,29 +1188,39 @@ void main_loop()
else if (packet_length > 0 && _aqconfig_.read_all_devices == true)
{
//logPacket(packet_buffer, packet_length);
if (packet_buffer[PKT_DEST] == DEV_MASTER && interestedInNextAck == true)
if (packet_buffer[PKT_DEST] == DEV_MASTER && interestedInNextAck != DRS_NONE)
{
swg_noreply_cnt = 0;
changed = processPacketFromSWG(packet_buffer, packet_length, &_aqualink_data);
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 != SWG_STATUS_OFF)
{
if ( ++swg_noreply_cnt < 3 ) {
_aqualink_data.ar_swg_status = SWG_STATUS_OFF;
changed = true;
if (interestedInNextAck == DRS_SWG) {
swg_noreply_cnt = 0;
changed = processPacketFromSWG(packet_buffer, packet_length, &_aqualink_data);
} else if (interestedInNextAck == DRS_EPUMP) {
changed = processPacketFromJandyPump(packet_buffer, packet_length, &_aqualink_data);
}
interestedInNextAck = false;
interestedInNextAck = DRS_NONE;
}
else if ( packet_buffer[PKT_DEST] != DEV_MASTER && interestedInNextAck != DRS_NONE )
{ // We were expecting an ack from device as next message but didn;t get it, device must be off
if (interestedInNextAck == DRS_SWG && _aqualink_data.ar_swg_status != SWG_STATUS_OFF) {
if ( ++swg_noreply_cnt < 3 ) {
_aqualink_data.ar_swg_status = SWG_STATUS_OFF;
changed = true;
}
}
interestedInNextAck = DRS_NONE;
}
else if (packet_buffer[PKT_DEST] == SWG_DEV_ID)
{
interestedInNextAck = true;
interestedInNextAck = DRS_SWG;
changed = processPacketToSWG(packet_buffer, packet_length, &_aqualink_data, _aqconfig_.swg_zero_ignore);
}
else if (packet_buffer[PKT_DEST] >= JANDY_DEC_PUMP_MIN && packet_buffer[PKT_DEST] <= JANDY_DEC_PUMP_MAX)
{
interestedInNextAck = DRS_EPUMP;
changed = processPacketToJandyPump(packet_buffer, packet_length, &_aqualink_data);
}
else
{
interestedInNextAck = false;
interestedInNextAck = DRS_NONE;
}
if (_aqconfig_.read_pentair_packets && getProtocolType(packet_buffer) == PENTAIR) {
@ -1216,7 +1235,8 @@ void main_loop()
broadcast_aqualinkstate(mgr.active_connections);
}
mg_mgr_poll(&mgr, 10);
//mg_mgr_poll(&mgr, 10);
mg_mgr_poll(&mgr, 5);
tcdrain(rs_fd); // Make sure buffer has been sent.
// Any unactioned commands

139
color_lights.c Normal file
View File

@ -0,0 +1,139 @@
#include <stdio.h>
#include <string.h>
//#define COLOR_LIGHTS_C_
#include "color_lights.h"
/****** This list MUST be in order of clight_type enum *******/
const char *_color_light_options[LIGHT_COLOR_TYPES+1][LIGHT_COLOR_OPTIONS] =
{
// AqualnkD Colors ignored as no names in control panel.
{ "bogus" },
{ // Jandy Color
"Alpine White",
"Sky Blue",
"Cobalt Blue",
"Caribbean Blue",
"Spring Green",
"Emerald Green",
"Emerald Rose",
"Magenta",
"Violet",
"Color Splash"
},
{ // Jandy LED
"Alpine White",
"Sky Blue",
"Cobalt Blue",
"Caribbean Blue",
"Spring Green",
"Emerald Green",
"Emerald Rose",
"Magenta",
"Violet",
"Slow Splash",
"Fast Splash",
"USA",
"Fat Tuesday",
"Disco Tech"
},
{ // SAm/SAL
"White",
"Light Green",
"Green",
"Cyan",
"Blue",
"Lavender",
"Magenta"
},
{ // Color Logic
"Voodoo Lounge",
"Blue Sea",
"Royal Blue",
"Afternoon Skies",
//"Aqua Green",
"Emerald",
"Sangria",
"Cloud White",
//"Warm Red",
//"Flamingo",
//"Vivid Violet",
//"Sangria",
"Twilight",
"Tranquility",
"Gemstone",
"USA",
"Mardi Gras",
"Cool Cabaret"
},
{ // IntelliBrite
"SAm",
"Party",
"Romance",
"Caribbean",
"American",
"Cal Sunset",
"Royal",
"Blue",
"Green",
"Red",
"White",
"Magenta"
}
};
const char *light_mode_name(clight_type type, int index)
{
return _color_light_options[type][index];
}
bool isShowMode(const char *mode)
{
if (strcmp(mode, "Color Splash") == 0 ||
strcmp(mode, "Slow Splash") == 0 ||
strcmp(mode, "Fast Splash") == 0 ||
strcmp(mode, "Fat Tuesday") == 0 ||
strcmp(mode, "Disco Tech") == 0 ||
strcmp(mode, "Voodoo Lounge") == 0 ||
strcmp(mode, "Twilight") == 0 ||
strcmp(mode, "Tranquility") == 0 ||
strcmp(mode, "Gemstone") == 0 ||
strcmp(mode, "USA") == 0 ||
strcmp(mode, "Mardi Gras") == 0 ||
strcmp(mode, "Cool Cabaret") == 0 ||
strcmp(mode, "SAm") == 0 ||
strcmp(mode, "Party") == 0 ||
strcmp(mode, "Romance") == 0 ||
strcmp(mode, "Caribbean") == 0 ||
strcmp(mode, "American") == 0 ||
strcmp(mode, "Cal Sunset") == 0)
return true;
else
return false;
}
int build_color_lights_js(struct aqualinkdata *aqdata, char* buffer, int size)
{
memset(&buffer[0], 0, size);
int length = 0;
int i, j;
length += sprintf(buffer+length, "var _light_program = [];\n");
length += sprintf(buffer+length, "_light_program[0] = light_program;\n");
for (i=1; i < LIGHT_COLOR_TYPES; i++) {
length += sprintf(buffer+length, "_light_program[%d] = [", i);
for (j=0; j < LIGHT_COLOR_OPTIONS; j++) {
if (_color_light_options[i][j] != NULL)
length += sprintf(buffer+length, "\"%s%s\",", _color_light_options[i][j], (isShowMode(_color_light_options[i][j])?" - Show":"") );
}
buffer[--length] = '\0';
length += sprintf(buffer+length, "];\n");
}
return length;
}

83
color_lights.h Normal file
View File

@ -0,0 +1,83 @@
#ifndef COLOR_LIGHTS_H_
#define COLOR_LIGHTS_H_
#include "aqualink.h"
#define LIGHT_COLOR_NAME 16
#define LIGHT_COLOR_OPTIONS 17
#define LIGHT_COLOR_TYPES LC_INTELLIB
/*
// color light modes (Aqualink program, Jandy, Jandy LED, SAm/SAL, Color Logic, Intellibrite)
typedef enum clight_type {
LC_PROGRAMABLE=0,
LC_JANDY,
LC_JANDYLED,
LC_SAL,
LC_CLOGIG,
LC_INTELLIB
} clight_type;
*/
const char *light_mode_name(clight_type type, int index);
int build_color_lights_js(struct aqualinkdata *aqdata, char* buffer, int size);
//char *_color_light_options_[LIGHT_COLOR_TYPES][LIGHT_COLOR_OPTIONS][LIGHT_COLOR_NAME];
#endif //COLOR_LIGHTS_H_
/*
Color Name Jandy Colors Jandy LED SAm/SAL Color Logic IntelliBrite
----------------------------------------------------------------------------------------------
Color Splash 11 8
Alpine White 1 1
Sky Blue 2 2
Cobalt Blue 3 3
Caribbean Blu 4 4
Spring Green 5 5
Emerald Green 6 6
Emerald Rose 7 7
Magenta 8 8
Garnet Red 9 9
Violet 10 10
Slow Splash 11
Fast Splash 12
USA!!! 13
Fat Tuesday 14
Disco Tech 15
White 1
Light Green 2
Green 3
Cyan 4
Blue 5
Lavender 6
Magenta 7
Light Magenta
Voodoo Lounge 1
Deep Blue Sea 2
Afternoon Skies 3
Afternoon Sky
Emerald 4
Sangria 5
Cloud White 6
Twilight 7
Tranquility 8
Gemstone 9
USA! 10
Mardi Gras 11
Cool Cabaret 12
SAm 1
Party 2
Romance 3
Caribbean 4
American 5
Cal Sunset 6
Royal 7
Blue 8
Green 9
Red 10
White 11
Magenta 12
*/

View File

@ -77,8 +77,8 @@ void init_parameters (struct aqconfig * parms)
parms->light_programming_mode = 0;
parms->light_programming_initial_on = 15;
parms->light_programming_initial_off = 12;
parms->light_programming_button_pool = TEMP_UNKNOWN;
parms->light_programming_button_spa = TEMP_UNKNOWN;
//parms->light_programming_button_pool = TEMP_UNKNOWN;
//parms->light_programming_button_spa = TEMP_UNKNOWN;
parms->deamonize = true;
parms->log_file = '\0';
parms->pda_mode = false;
@ -383,11 +383,13 @@ bool setConfigValue(struct aqualinkdata *aqdata, char *param, char *value) {
_aqconfig_.light_programming_initial_off = strtoul(value, NULL, 10);
rtn=true;
} else if (strncasecmp(param, "light_programming_button_spa", 28) == 0) {
_aqconfig_.light_programming_button_spa = strtoul(value, NULL, 10) - 1;
logMessage(LOG_ERR, "Config error, 'light_programming_button_spa' no longer supported\n");
//_aqconfig_.light_programming_button_spa = strtoul(value, NULL, 10) - 1;
rtn=true;
} else if (strncasecmp(param, "light_programming_button", 24) == 0 ||
strncasecmp(param, "light_programming_button_pool", 29) == 0) {
_aqconfig_.light_programming_button_pool = strtoul(value, NULL, 10) - 1;
logMessage(LOG_ERR, "Config error, 'light_programming_button' & 'light_programming_button_pool' are no longer supported\n");
//_aqconfig_.light_programming_button_pool = strtoul(value, NULL, 10) - 1;
rtn=true;
} else if (strncasecmp(param, "SWG_percent_dzidx", 17) == 0) {
_aqconfig_.dzidx_swg_percent = strtoul(value, NULL, 10);
@ -479,18 +481,32 @@ bool setConfigValue(struct aqualinkdata *aqdata, char *param, char *value) {
} else if (strncasecmp(param + 9, "_PDA_label", 10) == 0) {
aqdata->aqbuttons[num].pda_label = cleanalloc(value);
rtn=true;
} else if (strncasecmp(param + 9, "_lightMode", 10) == 0) {
if (aqdata->num_lights < MAX_LIGHTS) {
int type = strtoul(value, NULL, 10);
if (type < LC_PROGRAMABLE || type > LC_INTELLIB) {
logMessage(LOG_ERR, "Config error, unknown light mode '%s'\n",type);
} else {
aqdata->lights[aqdata->num_lights].button = &aqdata->aqbuttons[num];
aqdata->lights[aqdata->num_lights].lightType = type;
aqdata->num_lights++;
}
} else {
logMessage(LOG_ERR, "Config error, (colored|programmable) Lights limited to %d, ignoring %s'\n",MAX_LIGHTS,param);
}
rtn=true;
} else if (strncasecmp(param + 9, "_pumpID", 7) == 0) {
pump_detail *pump = getpump(aqdata, num);
if (pump != NULL) {
pump->pumpID = strtoul(cleanalloc(value), NULL, 16);
if (pump->pumpID < 119) {
pump->ptype = PENTAIR;
if (pump->pumpID <= PENTAIR_DEC_PUMP_MAX) {
pump->prclType = PENTAIR;
} else {
pump->ptype = JANDY;
pump->prclType = JANDY;
//pump->pumpType = EPUMP; // For testing let the interface set this
}
} else {
logMessage(LOG_ERR, "Config error, VSP Pumps limited to %d, ignoring %s'\n",MAX_PUMPS-1,param);
logMessage(LOG_ERR, "Config error, VSP Pumps limited to %d, ignoring %s'\n",MAX_PUMPS,param);
}
rtn=true;
} else if (strncasecmp(param + 9, "_pumpIndex", 10) == 0) { //button_01_pumpIndex=1
@ -498,7 +514,7 @@ bool setConfigValue(struct aqualinkdata *aqdata, char *param, char *value) {
if (pump != NULL) {
pump->pumpIndex = strtoul(value, NULL, 10);
} else {
logMessage(LOG_ERR, "Config error, VSP Pumps limited to %d, ignoring %s'\n",MAX_PUMPS-1,param);
logMessage(LOG_ERR, "Config error, VSP Pumps limited to %d, ignoring %s'\n",MAX_PUMPS,param);
}
rtn=true;
}
@ -528,6 +544,8 @@ bool setConfigValue(struct aqualinkdata *aqdata, char *param, char *value) {
return rtn;
}
pump_detail *getpump(struct aqualinkdata *aqdata, int button)
{
//static int _pumpindex = 0;
@ -674,8 +692,11 @@ void writeIntValue (FILE *fp, char *msg, int value)
fprintf(fp, "%s = %d\n", msg, value);
}
bool writeCfg (struct aqualinkdata *aqdata)
{
{
logMessage(LOG_ERR, "writeCfg() not implimented\n");
/*
FILE *fp;
int i;
bool fs = remount_root_ro(false);
@ -747,6 +768,7 @@ bool writeCfg (struct aqualinkdata *aqdata)
}
fclose(fp);
remount_root_ro(fs);
*/
return true;
}

View File

@ -51,8 +51,8 @@ struct aqconfig
float light_programming_mode;
int light_programming_initial_on;
int light_programming_initial_off;
int light_programming_button_pool;
int light_programming_button_spa;
//int light_programming_button_pool;
//int light_programming_button_spa;
bool override_freeze_protect;
bool pda_mode;
bool onetouch_mode;

View File

@ -1,7 +1,7 @@
#include <stdio.h>
#include "aquapure.h"
#include "devices_jandy.h"
#include "aq_serial.h"
#include "aqualink.h"
#include "utils.h"
@ -176,4 +176,15 @@ void get_swg_status_mqtt(struct aqualinkdata *aqdata, char *message, int *status
*dzalert = 4;
break;
}
}
bool processPacketToJandyPump(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata)
{
logMessage(LOG_DEBUG, "Need to log ePump message here for future\n");
return false;
}
bool processPacketFromJandyPump(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata)
{
logMessage(LOG_DEBUG, "Need to log ePump message here for future\n");
return false;
}

View File

@ -7,6 +7,8 @@
bool processPacketToSWG(unsigned char *packet, int packet_length, struct aqualinkdata *aqdata, int swg_zero_ignore);
bool processPacketFromSWG(unsigned char *packet, int packet_length, struct aqualinkdata *aqdata);
bool processPacketToJandyPump(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata);
bool processPacketFromJandyPump(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata);
void get_swg_status_mqtt(struct aqualinkdata *aqdata, char *message, int *status, int *dzalert);
aqledstate get_swg_led_state(struct aqualinkdata *aqdata);

View File

@ -22,7 +22,7 @@
#include "aqualink.h"
#include "aq_serial.h"
#include "pentair_messages.h"
#include "devices_pentair.h"
#include "utils.h"
bool processPentairPacket(unsigned char *packet, int packet_length, struct aqualinkdata *aqdata)
@ -35,12 +35,12 @@ bool processPentairPacket(unsigned char *packet, int packet_length, struct aqual
//static int pumpIndex = 1;
if ( packet[PEN_PKT_CMD] == PEN_CMD_STATUS && packet[PEN_PKT_FROM] >= 96 && packet[PEN_PKT_FROM] <= 111 ){
if ( packet[PEN_PKT_CMD] == PEN_CMD_STATUS && packet[PEN_PKT_FROM] >= PENTAIR_DEC_PUMP_MIN && packet[PEN_PKT_FROM] <= PENTAIR_DEC_PUMP_MAX ){
// We have Pentair Pump packet, let's see if it's configured.
//printf("PUMP\n");
for (i = 0; i < MAX_PUMPS; i++) {
if ( aqdata->pumps[i].ptype == PENTAIR && aqdata->pumps[i].pumpID == packet[PEN_PKT_FROM] ) {
if ( aqdata->pumps[i].prclType == PENTAIR && aqdata->pumps[i].pumpID == packet[PEN_PKT_FROM] ) {
// We found the pump.
logMessage(LOG_INFO, "Pentair Pump Status message = RPM %d | WATTS %d\n",
(packet[PEN_HI_B_RPM] * 256) + packet[PEN_LO_B_RPM],

52
extras/color-lights.txt Normal file
View File

@ -0,0 +1,52 @@
Color Name Jandy Colors Jandy LED SAm/SAL Color Logic IntelliBrite
----------------------------------------------------------------------------------------------
Color Splash 11 8
Alpine White 1 1
Sky Blue 2 2
Cobalt Blue 3 3
Caribbean Blu 4 4
Spring Green 5 5
Emerald Green 6 6
Emerald Rose 7 7
Magenta 8 8
Garnet Red 9 9
Violet 10 10
Slow Splash 11
Fast Splash 12
USA!!! 13
Fat Tuesday 14
Disco Tech 15
White 1
Light Green 2
Green 3
Cyan 4
Blue 5
Lavender 6
Magenta 7
Light Magenta
Voodoo Lounge 1
Deep Blue Sea 2
Afternoon Skies 3
Afternoon Sky 4
Emerald 5
Sangria 6
Cloud White 7
Twilight 8
Tranquility 9
Gemstone 10
USA! 11
Mardi Gras 12
Cool Cabaret 13
SAm 1
Party 2
Romance 3
Caribbean 4
American 5
Cal Sunset 6
Royal 7
Blue 8
Green 9
Red 10
White 11
Magenta 12

View File

@ -28,7 +28,7 @@
#include "json_messages.h"
#include "domoticz.h"
#include "aq_mqtt.h"
#include "aquapure.h"
#include "devices_jandy.h"
#include "version.h"
@ -40,11 +40,12 @@
//{"type": "aux_labels","Pool Pump": "Pool Pump","Spa Mode": "Spa Mode","Cleaner": "Aux 1","Waterfall": "Aux 2","Spa Blower": "Aux 2","Pool Light": "Aux 4","Spa Light ": "Aux 5","Aux 6": "Aux 6","Aux 7": "Aux 7","Heater": "Heater","Heater": "Heater","Solar Heater": "Solar Heater","(null)": "(null)"}
//SPA WILL TURN OFF AFTER COOL DOWN CYCLE
#include "aq_programmer.h"
const char* getStatus(struct aqualinkdata *aqdata)
{
if (aqdata->active_thread.thread_id != 0 && !aqdata->simulate_panel) {
return JSON_PROGRAMMING;
//return JSON_PROGRAMMING;
return programtypeDisplayName(aqdata->active_thread.ptype);
}
//if (aqdata->last_message != NULL && stristr(aqdata->last_message, "SERVICE") != NULL ) {
@ -163,7 +164,7 @@ int LED2int(aqledstate state)
}
}
#define AUX_BUFFER_SIZE 100
#define AUX_BUFFER_SIZE 200
char *get_aux_information(aqkey *button, struct aqualinkdata *aqdata, char *buffer)
{
@ -172,21 +173,28 @@ char *get_aux_information(aqkey *button, struct aqualinkdata *aqdata, char *buff
buffer[0] = '\0';
for (i=0; i < aqdata->num_pumps; i++) {
if (button == aqdata->pumps[i].button) {
//if (aqdata->pumps[i].rpm != TEMP_UNKNOWN || aqdata->pumps[i].gpm != TEMP_UNKNOWN || aqdata->pumps[i].watts != TEMP_UNKNOWN) {
length += sprintf(buffer, ",\"Pump_RPM\":\"%d\",\"Pump_GPM\":\"%d\",\"Pump_Watts\":\"%d\",\"Pump_Type\":\"%s\"",
if (button == aqdata->pumps[i].button) {
length += sprintf(buffer, ",\"type_ext\":\"switch_vsp\",\"Pump_RPM\":\"%d\",\"Pump_GPM\":\"%d\",\"Pump_Watts\":\"%d\",\"Pump_Type\":\"%s\"",
aqdata->pumps[i].rpm,aqdata->pumps[i].gpm,aqdata->pumps[i].watts,
(aqdata->pumps[i].pumpType==VFPUMP?"vfPump":(aqdata->pumps[i].pumpType==VSPUMP?"vsPump":"ePump")));
break;
//}
return buffer;
}
}
for (i=0; i < aqdata->num_lights; i++) {
if (button == aqdata->lights[i].button) {
length += sprintf(buffer, ",\"type_ext\": \"switch_program\", \"Light_Type\":\"%d\"", aqdata->lights[i].lightType);
return buffer;
}
}
length += sprintf(buffer, ",\"type_ext\": \"switch\"");
return buffer;
}
int build_device_JSON(struct aqualinkdata *aqdata, int programable_switch1, int programable_switch2, char* buffer, int size, bool homekit)
//int build_device_JSON(struct aqualinkdata *aqdata, int programable_switch1, int programable_switch2, char* buffer, int size, bool homekit)
int build_device_JSON(struct aqualinkdata *aqdata, char* buffer, int size, bool homekit)
{
char aux_info[AUX_BUFFER_SIZE];
memset(&buffer[0], 0, size);
@ -232,16 +240,19 @@ int build_device_JSON(struct aqualinkdata *aqdata, int programable_switch1, int
((homekit)?2:0),
((homekit_f)?degFtoC(aqdata->spa_temp):aqdata->spa_temp),
LED2int(aqdata->aqbuttons[i].led->state));
} else if ( (programable_switch1 > 0 && programable_switch1 == i) ||
(programable_switch2 > 0 && programable_switch2 == i)) {
/*
length += sprintf(buffer+length, "{\"type\": \"switch_program\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"int_status\": \"%d\" %s},",
} else {
get_aux_information(&aqdata->aqbuttons[i], aqdata, aux_info);
length += sprintf(buffer+length, "{\"type\": \"switch\", \"type_ext\": \"switch_vsp\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"int_status\": \"%d\" %s},",
aqdata->aqbuttons[i].name,
aqdata->aqbuttons[i].label,
aqdata->aqbuttons[i].led->state==ON?JSON_ON:JSON_OFF,
LED2text(aqdata->aqbuttons[i].led->state),
LED2int(aqdata->aqbuttons[i].led->state),
get_aux_information(&aqdata->aqbuttons[i], aqdata, aux_info));*/
aux_info);
}
/*
} else if ( (programable_switch1 > 0 && programable_switch1 == i) ||
(programable_switch2 > 0 && programable_switch2 == i)) {
length += sprintf(buffer+length, "{\"type\": \"switch\", \"type_ext\": \"switch_program\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"int_status\": \"%d\"},",
aqdata->aqbuttons[i].name,
aqdata->aqbuttons[i].label,
@ -266,7 +277,7 @@ int build_device_JSON(struct aqualinkdata *aqdata, int programable_switch1, int
aux_info);
//get_aux_information(&aqdata->aqbuttons[i], aqdata, aux_info));
}
}
}*/
}
if ( aqdata->frz_protect_set_point != TEMP_UNKNOWN && aqdata->air_temp != TEMP_UNKNOWN) {
@ -333,6 +344,23 @@ int build_device_JSON(struct aqualinkdata *aqdata, int programable_switch1, int
}
if ( aqdata->ph != TEMP_UNKNOWN ) {
length += sprintf(buffer+length, "{\"type\": \"value\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"value\": \"%.*f\" },",
((homekit_f)?CHRM_PH_F_TOPIC:CHEM_PH_TOPIC),
"Water Chemistry pH",
"on",
((homekit)?2:1),
((homekit_f)?(degFtoC(aqdata->ph)):aqdata->ph));
}
if ( aqdata->orp != TEMP_UNKNOWN ) {
length += sprintf(buffer+length, "{\"type\": \"value\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"value\": \"%.*f\" },",
((homekit_f)?CHRM_ORP_F_TOPIC:CHEM_ORP_TOPIC),
"Water Chemistry ORP",
"on",
((homekit)?2:0),
((homekit_f)?(degFtoC(aqdata->orp)):aqdata->orp));
}
length += sprintf(buffer+length, "{\"type\": \"temperature\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"value\": \"%.*f\" },",
AIR_TEMP_TOPIC,
/*AIR_TEMPERATURE,*/
@ -433,6 +461,12 @@ int build_aqualink_status_JSON(struct aqualinkdata *aqdata, char* buffer, int si
if ( aqdata->swg_percent == 101 )
length += sprintf(buffer+length, ",\"swg_boost_msg\":\"%s\"",aqdata->boost_msg );
if ( aqdata->ph != TEMP_UNKNOWN )
length += sprintf(buffer+length, ",\"chem_ph\":\"%.1f\"",aqdata->ph );
if ( aqdata->orp != TEMP_UNKNOWN )
length += sprintf(buffer+length, ",\"chem_orp\":\"%d\"",aqdata->orp );
length += sprintf(buffer+length, ",\"leds\":{" );
for (i=0; i < TOTAL_BUTTONS; i++)
{
@ -676,3 +710,5 @@ bool parseJSONmqttrequest(const char *str, size_t len, int *idx, int *nvalue, ch
}
return false;
}

View File

@ -4,6 +4,7 @@
//FUNCTION PROTOTYPES
#define JSON_LABEL_SIZE 300
#define JSON_BUFFER_SIZE 4000
#define JSON_STATUS_SIZE 1024
#define JSON_MQTT_MSG_SIZE 100
@ -42,8 +43,8 @@ bool parseJSONmqttrequest(const char *str, size_t len, int *idx, int *nvalue, ch
int build_aqualink_error_status_JSON(char* buffer, int size, char *msg);
int build_mqtt_status_message_JSON(char* buffer, int size, int idx, int nvalue, char *svalue);
//int build_device_JSON(struct aqualinkdata *aqdata, int programable_switch, char* buffer, int size, bool homekit);
int build_device_JSON(struct aqualinkdata *aqdata, int programable_switch1, int programable_switch2, char* buffer, int size, bool homekit);
//int build_device_JSON(struct aqualinkdata *aqdata, int programable_switch1, int programable_switch2, char* buffer, int size, bool homekit);
int build_device_JSON(struct aqualinkdata *aqdata, char* buffer, int size, bool homekit);
#endif /* JSON_MESSAGES_H_ */

View File

@ -34,7 +34,8 @@
#include "domoticz.h"
#include "aq_mqtt.h"
#include "pda.h"
#include "aquapure.h"
#include "devices_jandy.h"
#include "color_lights.h"
//static struct aqconfig *_aqconfig_;
@ -405,6 +406,17 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
_last_mqtt_aqualinkdata.battery = _aqualink_data->battery;
send_mqtt_string_msg(nc, BATTERY_STATE, _aqualink_data->battery==OK?MQTT_ON:MQTT_OFF);
}
if (_aqualink_data->ph != TEMP_UNKNOWN && _aqualink_data->ph != _last_mqtt_aqualinkdata.ph) {
_last_mqtt_aqualinkdata.ph = _aqualink_data->ph;
send_mqtt_float_msg(nc, CHEM_PH_TOPIC, _aqualink_data->ph);
send_mqtt_float_msg(nc, CHRM_PH_F_TOPIC, roundf(degFtoC(_aqualink_data->ph)));
}
if (_aqualink_data->orp != TEMP_UNKNOWN && _aqualink_data->orp != _last_mqtt_aqualinkdata.orp) {
_last_mqtt_aqualinkdata.orp = _aqualink_data->orp;
send_mqtt_numeric_msg(nc, CHEM_ORP_TOPIC, _aqualink_data->orp);
send_mqtt_float_msg(nc, CHRM_ORP_F_TOPIC, roundf(degFtoC(_aqualink_data->orp)));
}
if (_aqualink_data->ar_swg_status != _last_mqtt_aqualinkdata.ar_swg_status ||
_aqualink_data->swg_percent != _last_mqtt_aqualinkdata.swg_percent) { // Percent can also effect status
@ -559,25 +571,41 @@ int getTempforMeteohub(char *buffer)
void set_light_mode(char *value, int button)
{
int i;
clight_detail *light = NULL;
if (_aqconfig_.pda_mode == true) {
logMessage(LOG_ERR, "Light mode control not supported in PDA mode\n");
return;
}
if (_aqconfig_.light_programming_button_pool != button &&
_aqconfig_.light_programming_button_spa != button) {
for (i=0; i < _aqualink_data->num_lights; i++) {
if (&_aqualink_data->aqbuttons[button] == _aqualink_data->lights[i].button) {
// Found the programmable light
light = &_aqualink_data->lights[i];
break;
}
}
if (light == NULL) {
logMessage(LOG_ERR, "Light mode control not configured for button %d\n",button);
return;
}
char buf[LIGHT_MODE_BUFER];
if (light->lightType == LC_PROGRAMABLE ) {
// 5 below is light index, need to look this up so it's not hard coded.
sprintf(buf, "%-5s%-5d%-5d%-5d%.2f",value,
sprintf(buf, "%-5s%-5d%-5d%-5d%.2f",value,
button,
_aqconfig_.light_programming_initial_on,
_aqconfig_.light_programming_initial_off,
_aqconfig_.light_programming_mode );
//logMessage(LOG_NOTICE, "WEB: requset light mode %s\n", buf);
aq_programmer(AQ_SET_COLORMODE, buf, _aqualink_data);
aq_programmer(AQ_SET_LIGHTPROGRAM_MODE, buf, _aqualink_data);
} else {
sprintf(buf, "%-5s%-5d%-5d",value, button, light->lightType);
aq_programmer(AQ_SET_LIGHTCOLOR_MODE, buf, _aqualink_data);
}
}
@ -600,6 +628,12 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg)
mg_get_http_var(&http_msg->query_string, "command", command, sizeof(command));
logMessage(LOG_INFO, "WEB: Message command='%s'\n", command);
if (strcmp(command, "dynamic_config") == 0) {
char data[JSON_BUFFER_SIZE];
int size = build_color_lights_js(_aqualink_data, data, JSON_BUFFER_SIZE);
mg_send_head(nc, 200, size, "Content-Type: text/javascript");
mg_send(nc, data, size);
} else
// if (strstr(http_msg->query_string.p, "command=status")) {
if (strcmp(command, "status") == 0) {
char data[JSON_STATUS_SIZE];
@ -614,19 +648,25 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg)
mg_send_head(nc, 200, size, "Content-Type: text/plain");
mg_send(nc, data, size);
} else if (strcmp(command, "poollightmode") == 0) {
logMessage(LOG_ERR, "WEB: poollightmode taken out for update (forgot to put it back)\n");
/*
char value[20];
mg_get_http_var(&http_msg->query_string, "value", value, sizeof(value));
//aq_programmer(AQ_SET_COLORMODE, value, _aqualink_data);
set_light_mode(value, _aqconfig_.light_programming_button_pool);
mg_send_head(nc, 200, strlen(GET_RTN_OK), "Content-Type: text/plain");
mg_send(nc, GET_RTN_OK, strlen(GET_RTN_OK));
*/
} else if (strcmp(command, "spalightmode") == 0) {
logMessage(LOG_ERR, "WEB: spalightmode taken out for update (forgot to put it back)\n");
/*
char value[20];
mg_get_http_var(&http_msg->query_string, "value", value, sizeof(value));
//aq_programmer(AQ_SET_COLORMODE, value, _aqualink_data);
set_light_mode(value, _aqconfig_.light_programming_button_spa);
mg_send_head(nc, 200, strlen(GET_RTN_OK), "Content-Type: text/plain");
mg_send(nc, GET_RTN_OK, strlen(GET_RTN_OK));
*/
} else if (strcmp(command, "diag") == 0) {
aq_programmer(AQ_GET_DIAGNOSTICS_MODEL, NULL, _aqualink_data);
mg_send_head(nc, 200, strlen(GET_RTN_OK), "Content-Type: text/plain");
@ -659,14 +699,25 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg)
logMessage(LOG_INFO, "Web: request to set Freeze protect to %s\n", value);
mg_send_head(nc, 200, strlen(GET_RTN_OK), "Content-Type: text/plain");
mg_send(nc, GET_RTN_OK, strlen(GET_RTN_OK));
} else if (strcmp(command, "extended_device_prg") == 0) {
char value[20];
char message[JSON_LABEL_SIZE];
mg_get_http_var(&http_msg->query_string, "value", value, sizeof(value));
bool prg = request2bool(value);
if (prg && !onetouch_enabled())
prg = false; // Ignore request if onetouch is not enabeled
set_extended_device_id_programming(prg);
sprintf(message,"{\"extended_device_prg\":\"%s\"}", bool2text(prg));
mg_send_head(nc, 200, strlen(message), "Content-Type: application/json");
mg_send(nc, message, strlen(message));
} else if (strcmp(command, "devices") == 0) {
char message[JSON_LABEL_SIZE*10];
int size = build_device_JSON(_aqualink_data, _aqconfig_.light_programming_button_pool, _aqconfig_.light_programming_button_spa, message, JSON_LABEL_SIZE*10, false);
char message[JSON_BUFFER_SIZE];
int size = build_device_JSON(_aqualink_data, message, JSON_BUFFER_SIZE, false);
mg_send_head(nc, 200, size, "Content-Type: application/json");
mg_send(nc, message, size);
} else if (strcmp(command, "homebridge") == 0) {
char message[JSON_LABEL_SIZE*10];
int size = build_device_JSON(_aqualink_data, _aqconfig_.light_programming_button_pool, _aqconfig_.light_programming_button_spa, message, JSON_LABEL_SIZE*10, true);
char message[JSON_BUFFER_SIZE];
int size = build_device_JSON(_aqualink_data, message, JSON_BUFFER_SIZE, true);
mg_send_head(nc, 200, size, "Content-Type: application/json");
mg_send(nc, message, size);
} else if (strcmp(command, "setconfigprm") == 0) {
@ -866,8 +917,8 @@ void action_websocket_request(struct mg_connection *nc, struct websocket_message
build_aux_labels_JSON(_aqualink_data, labels, JSON_LABEL_SIZE);
ws_send(nc, labels);
} else if (strcmp(request.first.value, "GET_DEVICES") == 0) {
char message[JSON_LABEL_SIZE*10];
build_device_JSON(_aqualink_data, _aqconfig_.light_programming_button_pool, _aqconfig_.light_programming_button_spa, message, JSON_LABEL_SIZE*10, false);
char message[JSON_BUFFER_SIZE];
build_device_JSON(_aqualink_data, message, JSON_BUFFER_SIZE, false);
ws_send(nc, message);
} else if ( strcmp(request.first.value, "simulator") == 0) {
_aqualink_data->simulate_panel = true;
@ -920,11 +971,13 @@ void action_websocket_request(struct mg_connection *nc, struct websocket_message
aq_programmer(AQ_SET_SWG_PERCENT, request.second.value, _aqualink_data);
_aqualink_data->swg_percent = value; // Set the value as if it's already been set, just incase it's 0 as we won't get that message, or will update next time
}
} else if (strcmp(request.first.value, "POOL_LIGHT_MODE") == 0) {
set_light_mode(request.second.value, _aqconfig_.light_programming_button_pool);
//} else if (strcmp(request.first.value, "POOL_LIGHT_MODE") == 0) {
// set_light_mode(request.second.value, _aqconfig_.light_programming_button_pool);
} else if (strcmp(request.first.value, "LIGHT_MODE") == 0) {
// second is mode & third is button_id
int i;
for (i = 0; i < TOTAL_BUTTONS; i++) {
// NSF I could pull the text here for the real light color name. 4th value in json
if (strcmp(request.third.value, _aqualink_data->aqbuttons[i].name) == 0) {
set_light_mode(request.second.value, i);
break;
@ -1119,7 +1172,7 @@ void action_domoticz_mqtt_message(struct mg_connection *nc, struct mg_mqtt_messa
logMessage(LOG_INFO, "MQTT: DZ: received '%s' for '%s', already '%s', Ignoring\n", (nvalue==DZ_OFF?"OFF":"ON"), _aqualink_data->aqbuttons[i].name, (nvalue==DZ_OFF?"OFF":"ON"));
} else {
// NSF Below if needs to check that the button pressed is actually a light. Add this later
if (_aqualink_data->active_thread.ptype == AQ_SET_COLORMODE ) {
if (_aqualink_data->active_thread.ptype == AQ_SET_LIGHTPROGRAM_MODE ) {
logMessage(LOG_NOTICE, "MQTT: DZ: received '%s' for '%s', IGNORING as we are programming light mode\n", (nvalue==DZ_OFF?"OFF":"ON"), _aqualink_data->aqbuttons[i].name);
} else {
logMessage(LOG_INFO, "MQTT: DZ: received '%s' for '%s', turning '%s'\n", (nvalue==DZ_OFF?"OFF":"ON"), _aqualink_data->aqbuttons[i].name,(nvalue==DZ_OFF?"OFF":"ON"));
@ -1229,7 +1282,7 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
int qos=0;// can't be bothered with ack, so set to 0
logMessage(LOG_INFO, "MQTT: Connection acknowledged\n");
logMessage(LOG_DEBUG, "MQTT: Connection acknowledged\n");
mqtt_msg = (struct mg_mqtt_message *)ev_data;
if (mqtt_msg->connack_ret_code != MG_EV_MQTT_CONNACK_ACCEPTED) {
logMessage(LOG_WARNING, "Got mqtt connection error: %d\n", mqtt_msg->connack_ret_code);
@ -1262,7 +1315,7 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
break;
case MG_EV_MQTT_PUBACK:
mqtt_msg = (struct mg_mqtt_message *)ev_data;
logMessage(LOG_INFO, "MQTT: Message publishing acknowledged (msg_id: %d)\n", mqtt_msg->message_id);
logMessage(LOG_DEBUG, "MQTT: Message publishing acknowledged (msg_id: %d)\n", mqtt_msg->message_id);
break;
case MG_EV_MQTT_SUBACK:
logMessage(LOG_INFO, "MQTT: Subscription(s) acknowledged\n");

View File

@ -281,18 +281,46 @@ bool log_qeuiptment_status(struct aqualinkdata *aq_data)
//printf ("Set Pump Type to %d\n",aq_data->pumps[i].pumpType);
}
}
//aqdata->pumps[pumpIndex-1].rpm = atoi((char *) &packet_buffer[13]);
} else if (ot_strcmp(_menu[2],"AQUAPURE") == 0) {
/* Info: OneTouch Menu Line 0 = Equipment Status
Info: OneTouch Menu Line 1 =
Info: OneTouch Menu Line 2 = AQUAPURE 60%
Info: OneTouch Menu Line 3 = Salt 7600 PPM */
int swgp = atoi(&_menu[2][11]);
if ( aq_data->swg_percent != swgp ) {
aq_data->swg_percent = swgp;
rtn = true;
}
logMessage(LOG_DEBUG, "OneTouch SWG = %d\n",swgp);
} else if (true == false) {
/*
Debug: OneTouch Menu Line 0 = Equipment Status
Debug: OneTouch Menu Line 1 =
Debug: OneTouch Menu Line 2 = AQUAPURE 30%
Debug: OneTouch Menu Line 3 = Salt 3800 PPM
Debug: OneTouch Menu Line 4 = Add 2 lbs Salt
*/
if (ot_strcmp(_menu[3],"Salt") == 0) {
int ppm = atoi(&_menu[3][6]);
if ( aq_data->swg_ppm != ppm ) {
aq_data->swg_ppm = ppm;
rtn = true;
}
logMessage(LOG_DEBUG, "OneTouch PPM = %d\n",ppm);
}
} else if (ot_strcmp(_menu[2],"Chemlink") == 0) {
/* Info: OneTouch Menu Line 0 = Equipment Status
Info: OneTouch Menu Line 1 =
Info: OneTouch Menu Line 2 = Chemlink 1
Info: OneTouch Menu Line 3 = ORP 750/PH 7.0 */
if (ot_strcmp(_menu[3],"ORP") == 0) {
int orp = atoi(&_menu[3][4]);
char *indx = strchr(_menu[3], '/');
float ph = atof(indx+3);
if (aq_data->ph != ph || aq_data->orp != orp) {
aq_data->ph = ph;
aq_data->orp = orp;
return true;
}
logMessage(LOG_INFO, "OneTouch Cemlink ORP = %d PH = %f\n",orp,ph);
}
}
return rtn;
}

View File

@ -129,7 +129,7 @@ bool highlight_onetouch_menu_item(struct aqualinkdata *aq_data, char *item)
// Also need page up and down " ^^ More vv"
if ( (index = onetouch_menu_find_index(item)) != -1) {
cnt = index - onetouch_menu_hlightindex();
logMessage(LOG_DEBUG, "*** OneTouch menu caculator selected=%d, wanted=%d, move=%d times ***\n",onetouch_menu_hlightindex(),index,cnt);
logMessage(LOG_DEBUG, "OneTouch menu caculator selected=%d, wanted=%d, move=%d timesn",onetouch_menu_hlightindex(),index,cnt);
for (i=0; i < cnt; i ++) {
send_ot_cmd(KEY_ONET_DOWN);
waitfor_ot_queue2empty();
@ -209,6 +209,7 @@ bool goto_onetouch_system_menu(struct aqualinkdata *aq_data)
// Get back to a known point, the system menu
while (get_onetouch_memu_type() != OTM_SYSTEM && get_onetouch_memu_type() != OTM_ONETOUCH && i < 5 ) {
send_ot_cmd(KEY_ONET_BACK);
waitfor_ot_queue2empty();
waitForNextOT_Menu(aq_data);
i++;
}
@ -244,6 +245,7 @@ bool goto_onetouch_system_menu(struct aqualinkdata *aq_data)
bool goto_onetouch_menu(struct aqualinkdata *aq_data, ot_menu_type menu)
{
bool equErr = false;
char *second_menu = false;
char *third_menu = false;
@ -292,14 +294,10 @@ bool goto_onetouch_menu(struct aqualinkdata *aq_data, ot_menu_type menu)
return true;
break;
case OTM_EQUIPTMENT_ONOFF:
//if ( select_onetouch_menu_item(aq_data, "Equipment ON/OFF") == false ) { // NSF Should use this
if ( ot_strcmp(onetouch_menu_hlight(), "Equipment ON/OFF") == 0) {
send_ot_cmd(KEY_ONET_SELECT);
waitfor_ot_queue2empty();
waitForNextOT_Menu(aq_data);
return true;
if ( select_onetouch_menu_item(aq_data, "Equipment ON/OFF") == false ) {
logMessage(LOG_ERR, "OneTouch device programmer couldn't select 'Equipment ON/OFF' menu %d\n",menu);
equErr = true;
}
return false;
break;
case OTM_MENUHELP:
case OTM_SET_TEMP:
@ -308,51 +306,31 @@ bool goto_onetouch_menu(struct aqualinkdata *aq_data, ot_menu_type menu)
case OTM_FREEZE_PROTECT:
case OTM_BOOST:
case OTM_SYSTEM_SETUP:
if ( select_onetouch_menu_item(aq_data, " Menu / Help") == false ) {
if ( select_onetouch_menu_item(aq_data, "Menu / Help") == false ) {
logMessage(LOG_ERR, "OneTouch device programmer couldn't select menu %d\n",menu);
break;
}
if (second_menu)
select_onetouch_menu_item(aq_data, second_menu);
if (third_menu)
select_onetouch_menu_item(aq_data, third_menu);
/*
if (menu == OTM_SET_HEATER) {
select_onetouch_menu_item(aq_data, "Set Temp");
} else if (menu == OTM_SET_TIME) {
select_onetouch_menu_item(aq_data, "Set Time");
} else if (menu == OTM_SET_AQUAPURE) {
select_onetouch_menu_item(aq_data, "Set AQUAPURE");
}
*/
break;
default:
logMessage(LOG_ERR, "OneTouch device programmer doesn't know how to access menu %d\n",menu);
break;
}
// We can't detect Equiptment menu yet, so use the find test above not get_onetouch_memu_type() below
if (menu == OTM_EQUIPTMENT_ONOFF ) {
return !equErr;
}
if (get_onetouch_memu_type() != menu)
return false;
return true;
}
/*
bool in_ot_programming_mode(struct aqualinkdata *aq_data)
{
//( type != AQ_SET_PUMP_RPM || type != AQ_SET_OT_MACRO )) {
if ( ( aq_data->active_thread.thread_id != 0 ) &&
( aq_data->active_thread.ptype == AQ_SET_PUMP_RPM ||
aq_data->active_thread.ptype == AQ_SET_OT_MACRO ||
aq_data->active_thread.ptype == AQ_GET_OT_POOL_SPA_HEATER_TEMPS)
) {
return true;
}
return false;
}
*/
// Return the digit at factor
// num=12 factor=10 would return 2
@ -422,7 +400,8 @@ void *set_aqualink_pump_rpm( void *ptr )
waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_ONETOUCH_PUMP_RPM);
logMessage(LOG_NOTICE, "OneTouch Set Pump %d to RPM %d, from '%s'\n",pumpIndex,pumpRPM,buf);
logMessage(LOG_NOTICE, "OneTouch Set Pump %d to RPM %d\n",pumpIndex,pumpRPM);
if (! goto_onetouch_menu(aq_data, OTM_EQUIPTMENT_ONOFF) ){
logMessage(LOG_ERR, "OneTouch device programmer didn't get Equiptment on/off menu\n");
@ -461,21 +440,26 @@ void *set_aqualink_pump_rpm( void *ptr )
//printf("FOUND MENU")
if (strstr(onetouch_menu_hlight(), "RPM") != NULL ) {
// RPM 3450 & 600 max & min
// Panel will change 2nd,3rd & 4th digits depending on previos digit
// so reget the RPM after every change.
int RPM = ot_atoi(&onetouch_menu_hlight()[7]);
intPress(digitDiff(RPM, pumpRPM, 10000));
send_ot_cmd(KEY_ONET_SELECT);
waitfor_ot_queue2empty();
RPM = ot_atoi(&onetouch_menu_hlight()[7]);
intPress(digitDiff(RPM, pumpRPM, 1000));
send_ot_cmd(KEY_ONET_SELECT);
waitfor_ot_queue2empty();
RPM = ot_atoi(&onetouch_menu_hlight()[7]);
intPress(digitDiff(RPM, pumpRPM, 100));
send_ot_cmd(KEY_ONET_SELECT);
waitfor_ot_queue2empty();
RPM = ot_atoi(&onetouch_menu_hlight()[7]);
intPress(digitDiff(RPM, pumpRPM, 10));
// Get the new RPM.
aq_data->pumps[structIndex].rpm = ot_atoi(&onetouch_menu_hlight()[7]);
send_ot_cmd(KEY_ONET_SELECT);
waitfor_ot_queue2empty();
// Reset the pump RPM
aq_data->pumps[structIndex].rpm = RPM;
waitfor_ot_queue2empty();
} else if (strstr(onetouch_menu_hlight(), "GPM") != NULL ) {
// GPM 130 max, GPM 15 min
for (i=0; i < 24 ; i++) { // Max of 23 key presses to get from max to min
@ -486,8 +470,8 @@ void *set_aqualink_pump_rpm( void *ptr )
} else if (GPM < pumpRPM) {
send_ot_cmd(KEY_ONET_UP);
} else {
aq_data->pumps[structIndex].gpm = ot_atoi(&onetouch_menu_hlight()[8]);;
send_ot_cmd(KEY_ONET_SELECT);
aq_data->pumps[structIndex].gpm = GPM;
waitfor_ot_queue2empty();
break;
}
@ -531,7 +515,7 @@ void *set_aqualink_onetouch_macro( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
//struct aqualinkdata *aq_data = threadCtrl->aq_data;
//sprintf(msg, "%-5d%-5d",index, (strcmp(value, "on") == 0)?ON:OFF);
// Use above to set
@ -783,6 +767,8 @@ void *set_aqualink_onetouch_time( void *ptr )
waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_ONETOUCH_TIME);
logMessage(LOG_ERR, "OneTouch set time not implimented\n");
logMessage(LOG_DEBUG, "OneTouch set time\n");
if ( !goto_onetouch_menu(aq_data, OTM_SET_TIME) ){

Binary file not shown.

View File

@ -99,10 +99,8 @@ read_pentair_packets = no
# 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_pool = 0
light_programming_button_spa = 0
# Lights can be programmed by control panel or AqualinkD (if controlpanel doesn;t support specific light or light mode you want)
# IF YOU WANT AQUALINKD TO PROGRAM THE LIGHT, IT MUST NOT BE CONFIGURED AS A COLOR LIGHT IN THE JANDY CONTROL PANEL.
# Light probramming mode. 0=safe mode, but slow.
# any number greater is seconds to wait between button presses.
# 0.4 seems to be the minimum. (workd for light modes below 10 presses)
@ -148,6 +146,7 @@ use_panel_aux_labels=no
#
# button_01_pumpIndex=1
# If you have assigned this pump an index number in your Aqualink control panel, (Between 1 & 4), put it here for VSP, RPM, Primp information to be captured.
#
# Labels for standard butons (shown in web UI), and domoticz idx's
button_01_label=Filter Pump

21
utils.c
View File

@ -351,10 +351,20 @@ void logMessage(int msg_level, char *format, ...)
strncpy(buffer, " ", 8);
vsprintf (&buffer[8], format, args);
va_end(args);
int i;
//test(msg_level, buffer);
//fprintf (stderr, buffer);
// Make all printable chars
for(i = 8; i < strlen(buffer); i++) {
if ( (buffer[i] < 0 || buffer[i] > 125) && buffer[1] != 10 )
buffer[i] = ' ';
}
//if ( buffer[i] != '\n' )
// buffer[i+1] = '\n';
// Logging has not been setup yet, so STD error & syslog
if (_log_level == -1) {
fprintf (stderr, buffer);
@ -372,16 +382,18 @@ void logMessage(int msg_level, char *format, ...)
//return;
}
int len;
//int len;
char *strLevel = elevel2text(msg_level);
strncpy(buffer, strLevel, strlen(strLevel));
len = strlen(buffer);
//len = strlen(buffer);
/*
if ( buffer[len-1] != '\n') {
strcat(buffer, "\n");
}
*/
// Sent the log to the UI if configured.
if (msg_level <= LOG_WARNING && _loq_display_message != NULL) {
if (msg_level <= LOG_ERR && _loq_display_message != NULL) {
snprintf(_loq_display_message, 127, buffer);
}
@ -571,7 +583,8 @@ char* stristr(const char* haystack, const char* needle) {
int ascii(char *destination, char *source) {
unsigned int i;
for(i = 0; i < strlen(source); i++) {
if ( source[i] >= 0 && source[i] < 128 )
//if ( source[i] >= 0 && source[i] < 128 )
if ( source[i] >= 0 && source[i] < 127 )
destination[i] = source[i];
else
destination[i] = ' ';

View File

@ -1,4 +1,4 @@
#define AQUALINKD_NAME "Aqualink Daemon"
#define AQUALINKD_VERSION "2.0.0a"
#define AQUALINKD_VERSION "2.0.0b"

View File

@ -26,11 +26,12 @@
"Pool_Water",
"Spa_Water",
"Freeze_Protect",
"CHEM/pH",
"CHEM/ORP"
//"Solar_Heater",
];
// Add the light program names below, the name index MUST correlate to
// the number of pulses. ie (Blue Sea is #2 or 2 pulses)
// This get's picked up by dynamic_config.js and used as mode 0
var light_program = [
"Voodoo Lounge - Show",
"Blue Sea",
@ -50,6 +51,7 @@
"Mardi Gras - Show",
"Cool Cabaret - Show"
];
/*
* BELOW IS NOT RELIVENT FOR simple.html or simple inteface
*

View File

@ -11,6 +11,7 @@
<link href='aqualinkd.png' rel='apple-touch-icon'>
<link href='aqualinkd.png' rel='icon'>
<script src='config.js'></script>
<script src='?command=dynamic_config'></script>
<style>
:root {
--fonts: 'HelveticaNeue-Light', 'Helvetica Neue Light', 'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif;
@ -478,7 +479,8 @@
//init();
function init() {
setSizeSpecifics();
populateLightProgram();
populateLightProgram(-1);
//populateLightProgram(0);
document.getElementById('thermostat_options').classList.remove("hide");
document.getElementById('swg_options').classList.remove("hide");
document.getElementById('pswitch_options').classList.remove("hide");
@ -490,7 +492,9 @@
resetBackgroundSize();
}
function populateLightProgram() {
function populateLightProgram(type=0) {
var light_program = _light_program[type];
if (_lightProgramDropdown) {
option = document.getElementById('pswitch_option_list');
try {
@ -504,20 +508,27 @@
} catch (e) {}
} else {
var tbody = document.getElementById('pswitch_table').getElementsByTagName('tbody')[0];
if (type == -1) {
// -1 is Just init, so create another row, just to delete later.
row = tbody.insertRow(2);
row.innerHTML = "<td align='center'>Solid Color</td><td align='center'>Light Show</td>";
light_program = _light_program[0]; // Use aqualinkd light modes to set size below
}
var html1 = '';
var html2 = '';
var fLen = light_program.length;
var i;
for (i = 0; i < fLen; i++) {
if (light_program[i].endsWith(" - Show"))
html2 = html2 + "<div class='option_radiocontainer'><label><span class='radio'><input type='radio' name='light_program' value='" + (i+1) + "' onchange='updatePwsitchOptions(this);'><span class='option_radio-value' aria-hidden='true'></span></span>" + light_program[i].substr(0, (light_program[i].length - 7)) + "</label></div>";
html2 = html2 + "<div class='option_radiocontainer'><label><span class='radio'><input type='radio' name='light_program' value='" + (i+1) + "' onchange='updatePwsitchOptions(this, "+type+");'><span class='option_radio-value' aria-hidden='true'></span></span>" + light_program[i].substr(0, (light_program[i].length - 7)) + "</label></div>";
else
html1 = html1 + "<div class='option_radiocontainer'><label><span class='radio'><input type='radio' name='light_program' value='" + (i+1) + "' onchange='updatePwsitchOptions(this);'><span class='option_radio-value' aria-hidden='true'></span></span>" + light_program[i] + "</label></div>";
html1 = html1 + "<div class='option_radiocontainer'><label><span class='radio'><input type='radio' name='light_program' value='" + (i+1) + "' onchange='updatePwsitchOptions(this, "+type+");'><span class='option_radio-value' aria-hidden='true'></span></span>" + light_program[i] + "</label></div>";
}
var row;
row = tbody.deleteRow(2);
row = tbody.insertRow(2);
row.innerHTML = "<td align='center'>Solid Color</td><td align='center'>Light Show</td>";
tbody.deleteRow(3);
row = tbody.insertRow(3);
row.innerHTML = "<td align='left' valign='top'>" + html1 + "</td><td align='left' valign='top'>" + html2 + "</td>";
}
@ -789,7 +800,7 @@
if (document.getElementById(id).getAttribute('status') == 'on') {
document.getElementById(id + '_status').innerHTML = text;
} else {
console.log("Tile "+id+" status is '"+document.getElementById(id).getAttribute('status')+"' not setting text to '"+text+"'");
//console.log("Tile "+id+" status is '"+document.getElementById(id).getAttribute('status')+"' not setting text to '"+text+"'");
}
} catch(exception) {}
}
@ -942,7 +953,12 @@
add_tile(object.id, object.name, object.state, 'switch', ext_type, 'hk/' + img + '-off.png', 'hk/' + img + '-on.png');
setTileOn(object.id, object.status, null);
if (typeof object.type_ext !== 'undefined' && object.type_ext == 'switch_vsp') {
if (typeof object.type_ext !== 'undefined' && object.type_ext == 'switch_program') {
if (typeof object.Light_Type !== 'undefined') {
document.getElementById(object.id).setAttribute('lighttype', object.Light_Type);
}
// Other switch_program types (other than light_type) GO HERE
} else if (typeof object.type_ext !== 'undefined' && object.type_ext == 'switch_vsp') {
if (typeof object.Pump_RPM !== 'undefined' && object.Pump_RPM) {
if (object.Pump_RPM == -2) {
setTileOnText(object.id, 'Pump Offline'); // Small txt
@ -982,7 +998,6 @@
}
function showTileOptions(show, id, contex) {
console.log("showTileOptions " + show + " "+id+" "+contex);
var active_option;
if (show == true) {
var wrapH = document.getElementById('wrapper').clientHeight + 'px';
@ -1001,6 +1016,11 @@
document.getElementById('thermostat_options').style.display = 'none';
document.getElementById('pswitch_options').style.display = 'none';
document.getElementById('vspswitch_options').style.display = 'none';
} else if (id != null && document.getElementById(id).getAttribute('type') == 'setpoint_freeze') {
active_option = document.getElementById('swg_options');
document.getElementById('thermostat_options').style.display = 'none';
document.getElementById('pswitch_options').style.display = 'none';
document.getElementById('vspswitch_options').style.display = 'none';
} else if (id != null && document.getElementById(id).getAttribute('type') == 'switch_vsp') {
active_option = document.getElementById('vspswitch_options');
document.getElementById('thermostat_options').style.display = 'none';
@ -1058,17 +1078,14 @@
close_button = document.getElementById("swg_options_close");
ext = '&deg;' + _temperature_units;
} else if (type == 'switch_program') {
//slider = document.getElementById("pswitch_option_slider_range");
//slider_output = document.getElementById("pswitch_option_slider_text_value");
populateLightProgram(tile.getAttribute('lighttype') );
title = document.getElementById("pswitch_option_title");
close_button = document.getElementById("pswitch_options_close");
//ext = '&deg;' + _temperature_units;
} else if (type == 'switch_vsp') {
slider = document.getElementById("vspoption_slider_range");
slider_output = document.getElementById("vspoption_slider_text_value");
title = document.getElementById("vspswitch_option_title");
close_button = document.getElementById("vspswitch_option_close");
//ext = '&deg;' + _temperature_units;
} else {
slider = document.getElementById("option_slider_range");
slider_output = document.getElementById("option_slider_text_value");
@ -1113,7 +1130,9 @@
ext = ' RPM';
}
}
title.innerHTML = document.getElementById(id + '_name').innerHTML;
if (type == 'switch_program') {
var pswitch = document.getElementById("pswitch_option_list");
oswitch = document.getElementById("pswitch_option_switch");
@ -1126,19 +1145,24 @@
} 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");
document.getElementById("swg_option_switch_row").style.display = "table-row";
oswitch.checked = getTileAttribute(id, "Boost")=='on'?true:false ;
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 if (type == 'setpoint_freeze') {
slider.value = sp_value;
oswitch = document.getElementById("swg_option_switch");
document.getElementById("swg_option_switch_row").style.display = "none";
slider_output.innerHTML = slider.value + ext;
slider.oninput = function() {
slider_output.innerHTML = this.value + ext;
}
} else if (type == 'switch_vsp') {
slider.value = sp_value;
@ -1190,7 +1214,7 @@
var mode=false;
if (_lightProgramDropdown) {
if (pswitch.selectedIndex > 0) {
send_light_mode(pswitch.selectedIndex, id);
send_light_mode(pswitch.selectedIndex, id, pswitch.options[pswitch.selectedIndex].text);
mode=true;
}
} else {
@ -1198,7 +1222,7 @@
var x;
for (x = 0; x < radio.length; x++) {
if (radio[x].checked == true) {
send_light_mode(radio[x].value, id);
send_light_mode(radio[x].value, id, document.getElementById('pswitch_option_switch_text_value').innerHTML);
mode=true;
break;
}
@ -1268,6 +1292,11 @@
setTileValue("Temperature/Air", data.air_temp);
setTileValue("Temperature/Pool", data.pool_temp);
setTileValue("Temperature/Spa", data.spa_temp);
if (typeof(data.chem_ph) !== 'undefined')
setTileValue("CHEM/pH", data.chem_ph);
if (typeof(data.chem_orp) !== 'undefined')
setTileValue("CHEM/ORP", data.chem_orp);
/*
setTileValue("Air", data.air_temp);
setTileValue("Pool_Water", data.pool_temp);
@ -1454,13 +1483,14 @@
socket_di.send(JSON.stringify(temperature));
}
function send_light_mode(value, id) {
console.log("Set light mode to "+value+" id="+id);
function send_light_mode(value, id, textvalue) {
console.log("Set light mode to "+value+" id="+id+" text="+textvalue);
var mode = {};
//mode.parameter = 'POOL_LIGHT_MODE';
mode.parameter = 'LIGHT_MODE';
mode.value = value;
mode.button = id;
mode.text_value = textvalue;
socket_di.send(JSON.stringify(mode));
}
@ -1487,9 +1517,12 @@
}, 5000);
}
function updatePwsitchOptions(source) {
function updatePwsitchOptions(source, light_type) {
if (source.type == 'radio') {
document.getElementById('pswitch_option_switch_text_value').innerHTML = light_program[source.value-1];
var modename = _light_program[light_type][source.value-1];
if (modename.endsWith(" - Show"))
modename = modename.substr(0, (modename.length - 7));
document.getElementById('pswitch_option_switch_text_value').innerHTML = modename;
document.getElementById('pswitch_option_switch').checked = true;
} else if (source.type == 'checkbox') {
//console.log(source);
@ -1567,7 +1600,7 @@
<th colspan='2'><span id="swg_option_title"></span>
</th>
</tr>
<tr>
<tr id="swg_option_switch_row">
<td align='right' width='50%'><span class="option_text" id='swg_option_switch_text_value'></span>
</td>
<td align='left' width='50%'>