2.6.0 (dev)

pull/428/head
sfeakes 2025-03-09 17:39:41 -05:00
parent 25bcdce8ff
commit 2e6140aa67
40 changed files with 2176 additions and 602 deletions

View File

@ -83,7 +83,7 @@ endif
# Main source files
SRCS = aqualinkd.c utils.c config.c aq_serial.c aq_panel.c aq_programmer.c allbutton.c allbutton_aq_programmer.c net_services.c json_messages.c rs_msg_utils.c\
devices_jandy.c packetLogger.c devices_pentair.c color_lights.c serialadapter.c aq_timer.c aq_scheduler.c web_config.c\
serial_logger.c mongoose.c hassio.c simulator.c sensors.c timespec_subtract.c
serial_logger.c mongoose.c hassio.c simulator.c sensors.c aq_filesystem.c timespec_subtract.c
AQ_FLAGS =

View File

@ -126,15 +126,21 @@ NEED TO FIX FOR THIS RELEASE.
* with iaqualink2 no need to poll iaqtouch devices as frequently
* update documentation on how vbutton / vpump / pump_max & min / enable_iauqalink2
* check rs serial adapter is active if light color mode 11 is used.
* Check SWG messages like "#1 TruClear", see log in this post https://github.com/sfeakes/AqualinkD/discussions/388
-->
# Updates in 2.5.1
# Updates in 2.6.0 (dev)
* Added configuration editor in aqmanager.
* Added scheduling of pump after events (Power On, Freeze Protect, Boost)
* Fixed HA bug for thermostats not converting to °C when HA is set to display °C.
* Added support for monitoring SBC system sensors, like CPU / GPU / Board (CPU temp being most useful).
* Reduced load on panel over AqualinkTouch protocol.
* Fixed higher than normal CPU load when leaving aqmanager open and sending no messages (leaving aqmanager open for over 14days).
* Reworked PDA sleep mode.
* Added preliminary Heat Pump / Chiller support (MQTT & HA support only, no Homekit or web ui yet)
# Updates in 2.5.0
* PDA panel Rev 6.0 or newer that do not have a Jandy iAqualink device attached can use the AqualinkTouch protocol rather than PDA protocol.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -652,7 +652,7 @@ void *set_allbutton_pool_heater_temps( void *ptr )
val = MEATER_MIN;
}
*/
val = setpoint_check(POOL_HTR_SETOINT, val, aq_data);
val = setpoint_check(POOL_HTR_SETPOINT, val, aq_data);
// NSF IF in TEMP1 / TEMP2 mode, we need C range of 1 to 40 is 2 to 40 for TEMP1, 1 to 39 TEMP2
if (isSINGLE_DEV_PANEL){
@ -721,7 +721,7 @@ void *set_allbutton_spa_heater_temps( void *ptr )
} else if ( val < MEATER_MIN) {
val = MEATER_MIN;
}*/
val = setpoint_check(SPA_HTR_SETOINT, val, aq_data);
val = setpoint_check(SPA_HTR_SETPOINT, val, aq_data);
// NSF IF in TEMP1 / TEMP2 mode, we need C range of 1 to 40 is 2 to 40 for TEMP1, 1 to 39 TEMP2

114
source/aq_filesystem.c Normal file
View File

@ -0,0 +1,114 @@
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mount.h>
#include <sys/statvfs.h>
#include "aqualink.h"
#include "aq_scheduler.h"
bool remount_root_ro(bool readonly)
{
#ifdef AQ_CONTAINER
// In container this is pointless
return false;
#endif
if (readonly)
{
LOG(AQUA_LOG, LOG_INFO, "reMounting root RO\n");
mount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
return true;
}
else
{
struct statvfs fsinfo;
statvfs("/", &fsinfo);
if ((fsinfo.f_flag & ST_RDONLY) == 0) // We are readwrite, ignore
return false;
LOG(AQUA_LOG, LOG_INFO, "reMounting root RW\n");
mount(NULL, "/", NULL, MS_REMOUNT, NULL);
return true;
}
}
FILE *aq_open_file(char *filename, bool *ro_root, bool *created_file)
{
FILE *fp;
*ro_root = remount_root_ro(false);
if (access(filename, F_OK) == 0)
{
*created_file = true;
}
fp = fopen(filename, "w");
if (fp == NULL)
{
remount_root_ro(*ro_root);
}
return fp;
}
bool aq_close_file(FILE *file, bool ro_root)
{
if (file != NULL)
fclose(file);
return remount_root_ro(ro_root);
}
#define BUFFER_SIZE 4096
bool copy_file(const char *source_path, const char *destination_path) {
bool ro_root = remount_root_ro(false);
FILE *source_file = fopen(source_path, "rb");
if (source_file == NULL) {
LOG(AQUA_LOG,LOG_ERR, "Error opening source file: %s\n",source_path);
remount_root_ro(ro_root);
return false;
}
FILE *destination_file = fopen(destination_path, "wb");
if (destination_file == NULL) {
LOG(AQUA_LOG,LOG_ERR, "Error opening source file: %s\n",destination_path);
fclose(source_file);
remount_root_ro(ro_root);
return false;
}
char buffer[BUFFER_SIZE];
size_t bytes_read;
while ((bytes_read = fread(buffer, 1, BUFFER_SIZE, source_file)) > 0) {
if (fwrite(buffer, 1, bytes_read, destination_file) != bytes_read) {
LOG(AQUA_LOG,LOG_ERR, "Error writing to destination file: %s\n",destination_path);
fclose(source_file);
fclose(destination_file);
remount_root_ro(ro_root);
return false;
}
}
if (ferror(source_file)) {
LOG(AQUA_LOG,LOG_ERR, "Error reading from source: %s\n",source_path);
fclose(source_file);
fclose(destination_file);
remount_root_ro(ro_root);
return false;
}
fclose(source_file);
fclose(destination_file);
remount_root_ro(ro_root);
return true;
}

12
source/aq_filesystem.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef AQ_FILESYSTEM_H_
#define AQ_FILESYSTEM_H_
FILE *aq_open_file( char *filename, bool *ro_root, bool* created_file);
bool aq_close_file(FILE *file, bool ro_root);
bool copy_file(const char *source_path, const char *destination_path);
#endif //AQ_FILESYSTEM_H_

View File

@ -41,6 +41,9 @@
#define FREEZE_PROTECT "Freeze_Protect"
#define FREEZE_PROTECT_ENABELED FREEZE_PROTECT ENABELED_SUBT
#define CHILLER "Chiller"
#define CHILLER_ENABELED CHILLER ENABELED_SUBT
#define BATTERY_STATE "Battery"
//#define POOL_THERMO_TEMP_TOPIC BTN_POOL_HTR "/Temperature"
@ -76,6 +79,7 @@
#define MQTT_FLASH "2"
#define MQTT_ON "1"
#define MQTT_OFF "0"
#define MQTT_COOL MQTT_FLASH
#define MQTT_LWM_TOPIC "Alive"

View File

@ -183,6 +183,7 @@ setPanel("RS-8 Combo");
*/
char _panelString[60];
char _panelStringShort[60];
void setPanelString()
{
snprintf(_panelString, sizeof(_panelString), "%s%s-%s%d %s",
@ -191,21 +192,28 @@ void setPanelString()
isDUAL_EQPT_PANEL?"2/":"",
PANEL_SIZE(),
isDUAL_EQPT_PANEL?"Dual Equipment":(
isCOMBO_PANEL?"Combo Pool/Spa":(isSINGLE_DEV_PANEL?"Only Pool/Spa":"")
)
);
/*
isCOMBO_PANEL?"Combo Pool/Spa":"",
isSINGLE_DEV_PANEL?"Pool/Spa Only":"",
isDUAL_EQPT_PANEL?" Dual Equipment":"");
*/
isCOMBO_PANEL?"Combo (Pool & Spa)":(isSINGLE_DEV_PANEL?"Only (Pool or Spa)":"")
));
snprintf(_panelStringShort, sizeof(_panelString), "%s%s-%s%d %s",
isRS_PANEL?"RS":"",
isPDA_PANEL?"PDA":"", // No need for both of these, but for error validation leave it in.
isDUAL_EQPT_PANEL?"2/":"",
PANEL_SIZE(),
isDUAL_EQPT_PANEL?"Dual":(
isCOMBO_PANEL?"Combo":(isSINGLE_DEV_PANEL?"Only":"")
));
}
const char* getPanelString()
{
return _panelString;
}
const char* getShortPanelString()
{
return _panelStringShort;
}
//bool setPanelByName(const char *str) {
@ -360,6 +368,13 @@ aqkey *addVirtualButton(struct aqualinkdata *aqdata, char *label, int vindex) {
snprintf(name, 9, "%s%d", BTN_VAUX, vindex);
button->name = name;
if (label == NULL || strlen(label) <= 0) {
//button->label = name;
setVirtualButtonLabel(button, name);
} else {
setVirtualButtonLabel(button, label);
}
/*
if (strlen(label) <= 0) {
button->label = name;
} else {
@ -376,7 +391,7 @@ aqkey *addVirtualButton(struct aqualinkdata *aqdata, char *label, int vindex) {
button->rssd_code = IAQ_ONETOUCH_4;
} else {
button->rssd_code = NUL;
}
}*/
button->code = NUL;
button->dz_idx = DZ_NULL_IDX;
@ -385,6 +400,26 @@ aqkey *addVirtualButton(struct aqualinkdata *aqdata, char *label, int vindex) {
return button;
}
bool setVirtualButtonLabel(aqkey *button, const char *label) {
button->label = (char *)label;
// These 3 vbuttons have a button code on iaqualink protocol, so use that for rssd_code.
if (strncasecmp (button->label, "ALL OFF", 7) == 0) {
button->rssd_code = IAQ_ALL_OFF;
} else if (strncasecmp (button->label, "Spa Mode", 8) == 0) {
button->rssd_code = IAQ_SPA_MODE;
} else if (strncasecmp (button->label, "Clean Mode", 10) == 0) {
button->rssd_code = IAQ_CLEAN_MODE;
} else if (strncasecmp (button->label, "Day Party", 9) == 0) {
button->rssd_code = IAQ_ONETOUCH_4;
} else {
button->rssd_code = NUL;
}
return true;
}
// So the 0-100% should be 600-3450 RPM and 15-130 GPM (ie 1% would = 600 & 0%=off)
// (value-600) / (3450-600) * 100
// (value) / 100 * (3450-600) + 600
@ -724,12 +759,15 @@ const char* getActionName(action_type type)
case NO_ACTION:
return "No Action";
break;
case POOL_HTR_SETOINT:
case POOL_HTR_SETPOINT:
return "Pool Heater Setpoint";
break;
case SPA_HTR_SETOINT:
case SPA_HTR_SETPOINT:
return "Spa Heater Setpoint";
break;
case CHILLER_SETPOINT:
return "Chiller Setpoint";
break;
case FREEZE_SETPOINT:
return "Freeze Protect Setpoint";
break;
@ -774,6 +812,20 @@ const char* getActionName(action_type type)
//bool create_panel_request(struct aqualinkdata *aqdata, netRequest requester, int buttonIndex, int value, bool timer);
//void create_program_request(struct aqualinkdata *aqdata, netRequest requester, action_type type, int value, int id); // id is only valid for PUMP RPM
// Get Pool or Spa temp depending on what's on
int getWaterTemp(struct aqualinkdata *aqdata)
{
if (isSINGLE_DEV_PANEL)
return aqdata->pool_temp;
// NSF Need to check if spa is on.
if (aqdata->aqbuttons[1].led->state == OFF)
return aqdata->pool_temp;
else
return aqdata->spa_temp;
}
//bool setDeviceState(aqkey *button, bool isON)
bool setDeviceState(struct aqualinkdata *aqdata, int deviceIndex, bool isON, request_source source)
@ -907,10 +959,19 @@ bool programDeviceValue(struct aqualinkdata *aqdata, action_type type, int value
if (aqdata->unactioned.type != NO_ACTION && type != aqdata->unactioned.type)
LOG(PANL_LOG,LOG_ERR, "about to overwrite unactioned panel program\n");
if (type == POOL_HTR_SETOINT || type == SPA_HTR_SETOINT || type == FREEZE_SETPOINT || type == SWG_SETPOINT ) {
if (type == POOL_HTR_SETPOINT || type == SPA_HTR_SETPOINT || type == FREEZE_SETPOINT || type == SWG_SETPOINT ) {
aqdata->unactioned.value = setpoint_check(type, value, aqdata);
if (value != aqdata->unactioned.value)
LOG(PANL_LOG,LOG_NOTICE, "requested setpoint value %d is invalid, change to %d\n", value, aqdata->unactioned.value);
} else if (type == CHILLER_SETPOINT) {
if (isIAQT_ENABLED) {
aqdata->unactioned.value = setpoint_check(type, value, aqdata);
if (value != aqdata->unactioned.value)
LOG(PANL_LOG,LOG_NOTICE, "requested setpoint value %d is invalid, change to %d\n", value, aqdata->unactioned.value);
} else {
LOG(PANL_LOG,LOG_ERR, "Chiller setpoint can only be set when `%s` is set to iAqualinkTouch procotol\n", CFG_N_extended_device_id);
return false;
}
} else if (type == PUMP_RPM) {
aqdata->unactioned.value = value;
} else if (type == PUMP_VSPROGRAM) {
@ -1105,8 +1166,9 @@ bool panel_device_request(struct aqualinkdata *aqdata, action_type type, int dev
programDeviceLightMode(aqdata, value, deviceIndex);
}
break;
case POOL_HTR_SETOINT:
case SPA_HTR_SETOINT:
case POOL_HTR_SETPOINT:
case SPA_HTR_SETPOINT:
case CHILLER_SETPOINT:
case FREEZE_SETPOINT:
case SWG_SETPOINT:
case SWG_BOOST:

View File

@ -54,11 +54,14 @@
void setPanelByName(struct aqualinkdata *aqdata, const char *str);
void setPanel(struct aqualinkdata *aqdata, bool rs, int size, bool combo, bool dual);
const char* getPanelString();
const char* getShortPanelString();
bool panel_device_request(struct aqualinkdata *aqdata, action_type type, int deviceIndex, int value, request_source source);
void updateButtonLightProgram(struct aqualinkdata *aqdata, int value, int button);
int getWaterTemp(struct aqualinkdata *aqdata);
void changePanelToMode_Only();
void addPanelOneTouchInterface();
void addPanelIAQTouchInterface();
@ -71,6 +74,7 @@ int convertPumpPercentToSpeed(pump_detail *pump, int value); // This is probable
uint16_t getPanelSupport( char *rev_string, int rev_len);
aqkey *addVirtualButton(struct aqualinkdata *aqdata, char *label, int vindex);
bool setVirtualButtonLabel(aqkey *button, const char *label);
clight_detail *getProgramableLight(struct aqualinkdata *aqdata, int button);
pump_detail *getPumpDetail(struct aqualinkdata *aqdata, int button);

View File

@ -90,6 +90,7 @@ const func_ptr _prog_functions[AQP_RSSADAPTER_MAX] = {
[AQ_SET_IAQTOUCH_SWG_BOOST] = set_aqualink_iaqtouch_swg_boost,
[AQ_SET_IAQTOUCH_POOL_HEATER_TEMP]= set_aqualink_iaqtouch_pool_heater_temp,
[AQ_SET_IAQTOUCH_SPA_HEATER_TEMP] = set_aqualink_iaqtouch_spa_heater_temp,
[AQ_SET_IAQTOUCH_CHILLER_TEMP] = set_aqualink_iaqtouch_chiller_temp,
[AQ_SET_IAQTOUCH_SET_TIME] = set_aqualink_iaqtouch_time,
[AQ_SET_IAQTOUCH_PUMP_VS_PROGRAM] = set_aqualink_iaqtouch_pump_vs_program,
[AQ_SET_IAQTOUCH_LIGHTCOLOR_MODE] = set_aqualink_iaqtouch_light_colormode,
@ -156,7 +157,7 @@ int setpoint_check(int type, int value, struct aqualinkdata *aqdata)
char *type_msg;
switch(type) {
case POOL_HTR_SETOINT:
case POOL_HTR_SETPOINT:
type_msg = (isSINGLE_DEV_PANEL?"Temp1":"Pool");
if ( aqdata->temp_units == CELSIUS ) {
max = HEATER_MAX_C;
@ -173,7 +174,7 @@ int setpoint_check(int type, int value, struct aqualinkdata *aqdata)
min = aqdata->spa_htr_set_point + 1;
}
break;
case SPA_HTR_SETOINT:
case SPA_HTR_SETPOINT:
type_msg = (isSINGLE_DEV_PANEL?"Temp2":"Spa");
if ( aqdata->temp_units == CELSIUS ) {
max = (isSINGLE_DEV_PANEL?HEATER_MAX_C-1:HEATER_MAX_C);
@ -200,6 +201,16 @@ int setpoint_check(int type, int value, struct aqualinkdata *aqdata)
min = FREEZE_PT_MIN_F;
}
break;
case CHILLER_SETPOINT:
type_msg = "Freeze protect";
if ( aqdata->temp_units == CELSIUS ) {
max = CHILLER_MAX_C;
min = CHILLER_MIN_C;
} else {
max = CHILLER_MAX_F;
min = CHILLER_MIN_F;
}
break;
case SWG_SETPOINT:
type_msg = "Salt Water Generator";
max = SWG_PERCENT_MAX;
@ -880,6 +891,9 @@ const char *ptypeName(program_type type)
case AQ_SET_IAQTOUCH_SPA_HEATER_TEMP:
return "Set AqualinkTouch Spa Heater";
break;
case AQ_SET_IAQTOUCH_CHILLER_TEMP:
return "Set AqualinkTouch Chiller Temp";
break;
// These to same as above, but on the iAqualink protocol, not AqualinkTouch protocol
case AQ_SET_IAQLINK_POOL_HEATER_TEMP:
return "Set iAqualink Pool Heater";
@ -965,6 +979,7 @@ const char *programtypeDisplayName(program_type type)
case AQ_SET_IAQTOUCH_POOL_HEATER_TEMP:
case AQ_SET_RSSADAPTER_POOL_HEATER_TEMP:
case AQ_SET_RSSADAPTER_SPA_HEATER_TEMP:
case AQ_SET_IAQTOUCH_CHILLER_TEMP:
return "Programming: setting heater";
break;
case AQ_SET_FRZ_PROTECTION_TEMP:

View File

@ -16,11 +16,15 @@
#define HEATER_MIN_F 36
#define FREEZE_PT_MAX_F 42
#define FREEZE_PT_MIN_F 34
#define CHILLER_MAX_F 104
#define CHILLER_MIN_F 34
#define HEATER_MAX_C 40
#define HEATER_MIN_C 0
#define FREEZE_PT_MAX_C 5
#define FREEZE_PT_MIN_C 1
#define CHILLER_MAX_C 40
#define CHILLER_MIN_C 0
#define SWG_PERCENT_MAX 101
#define SWG_PERCENT_MIN 0
@ -99,6 +103,7 @@ typedef enum {
AQ_SET_IAQTOUCH_ONETOUCH_ON_OFF,
AQ_SET_IAQTOUCH_POOL_HEATER_TEMP,
AQ_SET_IAQTOUCH_SPA_HEATER_TEMP,
AQ_SET_IAQTOUCH_CHILLER_TEMP,
AQ_SET_IAQLINK_POOL_HEATER_TEMP, // Same as above but using iAqualink not AqualinkTouch
AQ_SET_IAQLINK_SPA_HEATER_TEMP, // Same as above but using iAqualink not AqualinkTouch
AQ_SET_IAQTOUCH_LIGHTCOLOR_MODE,
@ -111,6 +116,12 @@ typedef enum {
// ******** Delimiter make sure to change MAX/MIN below
} program_type;
#define AQ_SET_CHILLER_TEMP AQ_SET_IAQTOUCH_CHILLER_TEMP
#define AQP_GENERIC_MIN AQ_GET_POOL_SPA_HEATER_TEMPS
#define AQP_GENERIC_MAX AQ_SET_PUMP_VS_PROGRAM

View File

@ -29,6 +29,7 @@
#include "config.h"
#include "aq_panel.h"
//#include "utils.h"
#include "aq_filesystem.h"
@ -38,28 +39,7 @@ Example /etc/cron.d/aqualinkd
01 10 1 * * curl localhost:80/api/Filter_Pump/set -d value=2 -X PUT
*/
bool remount_root_ro(bool readonly) {
#ifdef AQ_CONTAINER
// In container this is pointless
return false;
#endif
if (readonly) {
LOG(SCHD_LOG,LOG_INFO, "reMounting root RO\n");
mount (NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
return true;
} else {
struct statvfs fsinfo;
statvfs("/", &fsinfo);
if ((fsinfo.f_flag & ST_RDONLY) == 0) // We are readwrite, ignore
return false;
LOG(SCHD_LOG,LOG_INFO, "reMounting root RW\n");
mount (NULL, "/", NULL, MS_REMOUNT, NULL);
return true;
}
}
bool passJson_scObj(const char* line, int length, aqs_cron *values)
{
@ -131,6 +111,9 @@ bool passJson_scObj(const char* line, int length, aqs_cron *values)
return (captured >= 7)?true:false;
}
int save_schedules_js(const char* inBuf, int inSize, char* outBuf, int outSize)
{
FILE *fp;
@ -138,6 +121,7 @@ int save_schedules_js(const char* inBuf, int inSize, char* outBuf, int outSize)
bool inarray = false;
aqs_cron cline;
bool fileexists = false;
bool fs = false;
if ( !_aqconfig_.enable_scheduler) {
LOG(SCHD_LOG,LOG_WARNING, "Schedules are disabled\n");
@ -146,13 +130,19 @@ int save_schedules_js(const char* inBuf, int inSize, char* outBuf, int outSize)
LOG(SCHD_LOG,LOG_NOTICE, "Saving Schedule:\n");
/*
bool fs = remount_root_ro(false);
if (access(CRON_FILE, F_OK) == 0)
fileexists = true;
fp = fopen(CRON_FILE, "w");
*/
fp = aq_open_file( CRON_FILE, &fs, &fileexists);
if (fp == NULL) {
LOG(SCHD_LOG,LOG_ERR, "Open file failed '%s'\n", CRON_FILE);
remount_root_ro(true);
//remount_root_ro(true);
aq_close_file(fp, fs);
return sprintf(outBuf, "{\"message\":\"Error Saving Schedules\"}");
}
@ -175,14 +165,16 @@ int save_schedules_js(const char* inBuf, int inSize, char* outBuf, int outSize)
}
fprintf(fp, "#***** AUTO GENERATED DO NOT EDIT *****\n");
fclose(fp);
//fclose(fp);
// if we created file, change the permissions
if (!fileexists)
if ( chmod(CRON_FILE, S_IRUSR | S_IWUSR ) < 0 )
LOG(SCHD_LOG,LOG_ERR, "Could not change permissions on cron file %s, scheduling may not work\n",CRON_FILE);
remount_root_ro(fs);
//remount_root_ro(fs);
aq_close_file(fp, fs);
return sprintf(outBuf, "{\"message\":\"Saved Schedules\"}");
}
@ -285,7 +277,7 @@ int build_schedules_js(char* buffer, int size)
//LOG(SCHD_LOG,LOG_DEBUG, "Read from cron Day %d | Time %d:%d | Zone %d | Runtime %d\n",day,hour,minute,zone,runtime);
// Test / get for pump start and end time
if (isAQS_USE_PUMP_TIME_FROM_CRON_ENABLED) {
if (isAQS_USE_CRON_PUMP_TIME_ENABLED) {
// Could also check that dayw is *
if ( cline.enabled && strstr(cline.url, AQS_PUMP_URL ))
{
@ -335,11 +327,21 @@ bool event_happened_set_device_state(reset_event_type type, struct aqualinkdata
// Check time is between hours.
bool scheduledOn = false;
if (isAQS_USE_PUMP_TIME_FROM_CRON_ENABLED) {
if (isAQS_USE_CRON_PUMP_TIME_ENABLED) {
get_cron_pump_times();
LOG(SCHD_LOG,LOG_DEBUG, "Pump on times from scheduler are between hours %.2d & %.2d\n",_aqconfig_.sched_chk_pumpon_hour, _aqconfig_.sched_chk_pumpoff_hour);
}
/*
if (_aqconfig_.sched_chk_pumpon_hour == AQ_UNKNOWN || _aqconfig_.sched_chk_pumpoff_hour == AQ_UNKNOWN ) {
if ( CRON TURNED OFF ) {
LOG(SCHD_LOG,LOG_ERR, "No pump on / off times configures and cron scheduler not enabled, can't action event!");
return false;
}
get_cron_pump_times();
LOG(SCHD_LOG,LOG_DEBUG, "Pump on times from scheduler are between hours %.2d & %.2d\n",_aqconfig_.sched_chk_pumpon_hour, _aqconfig_.sched_chk_pumpoff_hour);
}
*/
time_t now = time(NULL);
struct tm *tm_struct = localtime(&now);

View File

@ -28,20 +28,25 @@ int build_schedules_js(char* buffer, int size);
int save_schedules_js(const char* inBuf, int inSize, char* outBuf, int outSize);
void get_cron_pump_times();
#define AQS_PUMP_URL BTN_PUMP "/set"
#define AQS_DONT_USE_CRON_PUMP_TIME (1 << 0)
// All below AQS_ are the same mask, but don;t want CRON in the emum
#define AQS_USE_CRON_PUMP_TIME (1 << 0)
typedef enum reset_event_type{
AQS_POWER_ON = (1 << 1),
AQS_FRZ_PROTECT_OFF = (1 << 2),
AQS_BOOST_OFF = (1 << 3)
AQS_POWER_ON = (1 << 1),
AQS_FRZ_PROTECT_OFF = (1 << 2),
AQS_BOOST_OFF = (1 << 3)
} reset_event_type;
#define isAQS_START_PUMP_EVENT_ENABLED ( ((_aqconfig_.schedule_event_mask & AQS_POWER_ON) == AQS_POWER_ON) || \
((_aqconfig_.schedule_event_mask & AQS_FRZ_PROTECT_OFF) == AQS_FRZ_PROTECT_OFF) || \
((_aqconfig_.schedule_event_mask & AQS_BOOST_OFF) == AQS_BOOST_OFF) )
#define isAQS_USE_PUMP_TIME_FROM_CRON_ENABLED !((_aqconfig_.schedule_event_mask & AQS_DONT_USE_CRON_PUMP_TIME) == AQS_DONT_USE_CRON_PUMP_TIME)
//#define isAQS_USE_PUMP_TIME_FROM_CRON_ENABLED !((_aqconfig_.schedule_event_mask & AQS_DONT_USE_CRON_PUMP_TIME) == AQS_DONT_USE_CRON_PUMP_TIME)
#define isAQS_USE_CRON_PUMP_TIME_ENABLED ((_aqconfig_.schedule_event_mask & AQS_USE_CRON_PUMP_TIME) == AQS_USE_CRON_PUMP_TIME)
#define isAQS_POWER_ON_ENABED ((_aqconfig_.schedule_event_mask & AQS_POWER_ON) == AQS_POWER_ON)
#define isAQS_FRZ_PROTECT_OFF_ENABED ((_aqconfig_.schedule_event_mask & AQS_FRZ_PROTECT_OFF) == AQS_FRZ_PROTECT_OFF)
#define isAQS_BOOST_OFF_ENABED ((_aqconfig_.schedule_event_mask & AQS_BOOST_OFF) == AQS_BOOST_OFF)

View File

@ -63,6 +63,8 @@ const char *getJandyDeviceName(emulation_type etype);
#define JANDY_DEV_AQLNK_MIN 0x30 //
#define JANDY_DEV_AQLNK_MAX 0x33 // 0
#define JANDY_DEV_HPUMP_MIN 0x70
#define JANDY_DEV_HPUMP_MAX 0x73
/*
//===== Device ID's =====//
//=========================================================================//
@ -522,7 +524,8 @@ typedef enum {
DRS_EPUMP,
DRS_JXI,
DRS_LX,
DRS_CHEM
DRS_CHEM,
DRS_HEATPUMP
} rsDeviceType;
typedef enum {

View File

@ -61,6 +61,8 @@ bool checkAqualinkTime(); // Only need to externalise this for PDA
*/
#define TEMP_UNKNOWN -999
#define TEMP_REFRESH -998
#define AQ_UNKNOWN TEMP_UNKNOWN
//#define UNKNOWN TEMP_UNKNOWN
#define DATE_STRING_LEN 30
@ -116,12 +118,21 @@ struct programmingthread {
};
typedef enum panel_status {
CONNECTED,
CONECTING,
LOOKING_IDS,
STARTING,
SERIAL_ERROR, // Errors that stop reading serial port should be below this line
NO_IDS_ERROR,
} panel_status;
typedef enum action_type {
NO_ACTION = -1,
POOL_HTR_SETOINT,
SPA_HTR_SETOINT,
POOL_HTR_SETPOINT,
SPA_HTR_SETPOINT,
FREEZE_SETPOINT,
CHILLER_SETPOINT,
SWG_SETPOINT,
SWG_BOOST,
PUMP_RPM,
@ -249,6 +260,7 @@ typedef struct clightd
struct aqualinkdata
{
panel_status panelstatus;
char version[AQ_MSGLEN*2];
char revision[AQ_MSGLEN];
char date[AQ_MSGLEN];
@ -271,11 +283,13 @@ struct aqualinkdata
int spa_htr_set_point;
int swg_percent;
int swg_ppm;
int chiller_set_point;
unsigned char ar_swg_device_status; // Actual state
unsigned char heater_err_status;
aqledstate swg_led_state; // Display state for UI's
aqledstate service_mode_state;
aqledstate frz_protect_state;
aqledstate chiller_state;
int num_pumps;
pump_detail pumps[MAX_PUMPS];
int num_lights;

View File

@ -134,6 +134,7 @@ bool isVirtualButtonEnabled() {
return _aqualink_data.virtual_button_start>0?true:false;
}
// Should move to panel.
bool checkAqualinkTime()
{
@ -250,12 +251,12 @@ void action_delayed_request()
// If we don't know the units yet, we can't action setpoint, so wait until we do.
if (_aqualink_data.temp_units == UNKNOWN &&
(_aqualink_data.unactioned.type == POOL_HTR_SETOINT || _aqualink_data.unactioned.type == SPA_HTR_SETOINT || _aqualink_data.unactioned.type == FREEZE_SETPOINT))
(_aqualink_data.unactioned.type == POOL_HTR_SETPOINT || _aqualink_data.unactioned.type == SPA_HTR_SETPOINT || _aqualink_data.unactioned.type == FREEZE_SETPOINT))
return;
if (_aqualink_data.unactioned.type == POOL_HTR_SETOINT)
if (_aqualink_data.unactioned.type == POOL_HTR_SETPOINT)
{
_aqualink_data.unactioned.value = setpoint_check(POOL_HTR_SETOINT, _aqualink_data.unactioned.value, &_aqualink_data);
_aqualink_data.unactioned.value = setpoint_check(POOL_HTR_SETPOINT, _aqualink_data.unactioned.value, &_aqualink_data);
if (_aqualink_data.pool_htr_set_point != _aqualink_data.unactioned.value)
{
aq_programmer(AQ_SET_POOL_HEATER_TEMP, sval, &_aqualink_data);
@ -266,9 +267,9 @@ void action_delayed_request()
LOG(AQUA_LOG,LOG_NOTICE, "Pool heater setpoint is already %d, not changing\n", _aqualink_data.unactioned.value);
}
}
else if (_aqualink_data.unactioned.type == SPA_HTR_SETOINT)
else if (_aqualink_data.unactioned.type == SPA_HTR_SETPOINT)
{
_aqualink_data.unactioned.value = setpoint_check(SPA_HTR_SETOINT, _aqualink_data.unactioned.value, &_aqualink_data);
_aqualink_data.unactioned.value = setpoint_check(SPA_HTR_SETPOINT, _aqualink_data.unactioned.value, &_aqualink_data);
if (_aqualink_data.spa_htr_set_point != _aqualink_data.unactioned.value)
{
aq_programmer(AQ_SET_SPA_HEATER_TEMP, sval, &_aqualink_data);
@ -359,6 +360,19 @@ void action_delayed_request()
else if (_aqualink_data.unactioned.type == LIGHT_MODE) {
panel_device_request(&_aqualink_data, LIGHT_MODE, _aqualink_data.unactioned.id, _aqualink_data.unactioned.value, UNACTION_TIMER);
}
else if (_aqualink_data.unactioned.type == CHILLER_SETPOINT)
{
_aqualink_data.unactioned.value = setpoint_check(CHILLER_SETPOINT, _aqualink_data.unactioned.value, &_aqualink_data);
if (_aqualink_data.chiller_set_point != _aqualink_data.unactioned.value)
{
aq_programmer(AQ_SET_CHILLER_TEMP, sval, &_aqualink_data);
LOG(AQUA_LOG,LOG_NOTICE, "Setting Chiller setpoint to %d\n", _aqualink_data.unactioned.value);
}
else
{
LOG(AQUA_LOG,LOG_NOTICE, "Chiller setpoint is already %d, not changing\n", _aqualink_data.unactioned.value);
}
}
else
{
LOG(AQUA_LOG,LOG_ERR, "Unknown request of type %d\n", _aqualink_data.unactioned.type);
@ -479,7 +493,7 @@ int main(int argc, char *argv[])
return startup(argv[0], cfgFile);
}
int startup(char *self, char *cfgFile)
int OLD_startup_OLD(char *self, char *cfgFile)
{
int i, j;
@ -575,6 +589,7 @@ int startup(char *self, char *cfgFile)
isSINGLE_DEV_PANEL?"Pool/Spa Only":"",
isDUAL_EQPT_PANEL?"Dual Equipment":"");
*/
LOG(AQUA_LOG,LOG_NOTICE, "Panel set to %s\n", getPanelString());
LOG(AQUA_LOG,LOG_NOTICE, "Config log_level = %d\n", _aqconfig_.log_level);
LOG(AQUA_LOG,LOG_NOTICE, "Config device_id = 0x%02hhx\n", _aqconfig_.device_id);
@ -591,7 +606,7 @@ int startup(char *self, char *cfgFile)
LOG(AQUA_LOG,LOG_NOTICE, "Config socket_port = %s\n", _aqconfig_.socket_port);
LOG(AQUA_LOG,LOG_NOTICE, "Config web_directory = %s\n", _aqconfig_.web_directory);
//LOG(AQUA_LOG,LOG_NOTICE, "Config read_all_devices = %s\n", bool2text(_aqconfig_.read_all_devices));
LOG(AQUA_LOG,LOG_NOTICE, "Config use_aux_labels = %s\n", bool2text(_aqconfig_.use_panel_aux_labels));
//LOG(AQUA_LOG,LOG_NOTICE, "Config use_aux_labels = %s\n", bool2text(_aqconfig_.use_panel_aux_labels));
LOG(AQUA_LOG,LOG_NOTICE, "Config override frz prot = %s\n", bool2text(_aqconfig_.override_freeze_protect));
#ifndef MG_DISABLE_MQTT
LOG(AQUA_LOG,LOG_NOTICE, "Config mqtt server = %s\n", _aqconfig_.mqtt_server);
@ -626,9 +641,9 @@ int startup(char *self, char *cfgFile)
LOG(AQUA_LOG,LOG_NOTICE, "Config PDA Sleep Mode = %s\n", bool2text(_aqconfig_.pda_sleep_mode));
}
#endif
LOG(AQUA_LOG,LOG_NOTICE, "Config force SWG = %s\n", bool2text(_aqconfig_.force_swg));
LOG(AQUA_LOG,LOG_NOTICE, "Config force PS setpoint = %s\n", bool2text(_aqconfig_.force_ps_setpoints));
LOG(AQUA_LOG,LOG_NOTICE, "Config force Freeze Prot = %s\n", bool2text(_aqconfig_.force_frzprotect_setpoints));
LOG(AQUA_LOG,LOG_NOTICE, "Config force SWG = %s\n", bool2text(ENABLE_SWG));
LOG(AQUA_LOG,LOG_NOTICE, "Config force PS setpoint = %s\n", bool2text(ENABLE_HEATERS));
LOG(AQUA_LOG,LOG_NOTICE, "Config force Freeze Prot = %s\n", bool2text(ENABLE_FREEZEPROTECT));
/* removed until domoticz has a better virtual thermostat
LOG(AQUA_LOG,LOG_NOTICE, "Config idx pool thermostat = %d\n", _aqconfig_.dzidx_pool_thermostat);
LOG(AQUA_LOG,LOG_NOTICE, "Config idx spa thermostat = %d\n", _aqconfig_.dzidx_spa_thermostat);
@ -655,7 +670,7 @@ int startup(char *self, char *cfgFile)
LOG(AQUA_LOG,LOG_NOTICE, "Read JXi heater direct = %s\n", bool2text(READ_RSDEV_JXI));
LOG(AQUA_LOG,LOG_NOTICE, "Read LX heater direct = %s\n", bool2text(READ_RSDEV_LX));
LOG(AQUA_LOG,LOG_NOTICE, "Read Chem Feeder direct = %s\n", bool2text(READ_RSDEV_CHEM));
/*
if (isAQS_START_PUMP_EVENT_ENABLED) {
if (isAQS_USE_PUMP_TIME_FROM_CRON_ENABLED) {
get_cron_pump_times();
@ -664,7 +679,7 @@ int startup(char *self, char *cfgFile)
LOG(AQUA_LOG,LOG_NOTICE, "Start Pump between times = %d:00 and %d:00\n",_aqconfig_.sched_chk_pumpon_hour,_aqconfig_.sched_chk_pumpoff_hour);
} else {
LOG(AQUA_LOG,LOG_NOTICE, "Start Pump on events = %s\n", bool2text(false));
}
}*/
/*
if (READ_RSDEV_SWG && _aqconfig_.swg_zero_ignore != DEFAULT_SWG_ZERO_IGNORE_COUNT)
LOG(AQUA_LOG,LOG_NOTICE, "Ignore SWG 0 msg count = %d\n", _aqconfig_.swg_zero_ignore);
@ -743,11 +758,105 @@ int startup(char *self, char *cfgFile)
_aqualink_data.aqbuttons[i].rssd_code);
}
*/
#ifdef CONFIG_EDITOR
check_print_config(&_aqualink_data);
writeCfg(&_aqualink_data);
if (_aqconfig_.deamonize == true)
{
char pidfile[256];
// sprintf(pidfile, "%s/%s.pid",PIDLOCATION, basename(argv[0]));
//sprintf(pidfile, "%s/%s.pid", "/run", basename(argv[0]));
//sprintf(pidfile, "%s/%s.pid", "/run", basename(self));
sprintf(pidfile, "%s/%s.pid", "/run", _aqualink_data.self);
daemonise(pidfile, main_loop);
}
else
{
main_loop();
}
exit(EXIT_SUCCESS);
}
int startup(char *self, char *cfgFile)
{
_self = self;
_cfgFile = cfgFile;
sprintf(_aqualink_data.self, basename(self));
clearDebugLogMask();
read_config(&_aqualink_data, cfgFile);
if (_cmdln_loglevel != -1)
_aqconfig_.log_level = _cmdln_loglevel;
if (_cmdln_debugRS485)
_aqconfig_.log_protocol_packets = true;
if (_cmdln_lograwRS485)
_aqconfig_.log_raw_bytes = true;
#ifdef AQ_MANAGER
setLoggingPrms(_aqconfig_.log_level, _aqconfig_.deamonize, (_aqconfig_.display_warnings_web?_aqualink_data.last_display_message:NULL));
#else
if (_aqconfig_.display_warnings_web == true)
setLoggingPrms(_aqconfig_.log_level, _aqconfig_.deamonize, _aqconfig_.log_file, _aqualink_data.last_display_message);
else
setLoggingPrms(_aqconfig_.log_level, _aqconfig_.deamonize, _aqconfig_.log_file, NULL);
#endif
LOG(AQUA_LOG,LOG_NOTICE, "%s v%s\n", AQUALINKD_NAME, AQUALINKD_VERSION);
check_print_config(&_aqualink_data);
// Sanity check on Device ID's against panel type
if (isRS_PANEL) {
if ( (_aqconfig_.device_id >= 0x08 && _aqconfig_.device_id <= 0x0B) || _aqconfig_.device_id == 0x00 /*|| _aqconfig_.device_id == 0xFF*/) {
// We are good
} else {
LOG(AQUA_LOG,LOG_ERR, "Device ID 0x%02hhx does not match RS panel, Going to search for ID!\n", _aqconfig_.device_id);
_aqconfig_.device_id = 0x00;
//return EXIT_FAILURE;
}
} else if (isPDA_PANEL) {
if ( (_aqconfig_.device_id >= 0x60 && _aqconfig_.device_id <= 0x63) || _aqconfig_.device_id == 0x33 ) {
if ( _aqconfig_.device_id == 0x33 ) {
LOG(AQUA_LOG,LOG_NOTICE, "Enabeling iAqualink protocol.\n");
_aqconfig_.enable_iaqualink = true;
}
// We are good
} else {
LOG(AQUA_LOG,LOG_ERR, "Device ID 0x%02hhx does not match PDA panel, please check config!\n", _aqconfig_.device_id);
return EXIT_FAILURE;
}
} else {
LOG(AQUA_LOG,LOG_ERR, "Error unknown panel type, please check config!\n");
return EXIT_FAILURE;
}
if (_aqconfig_.rssa_device_id != 0x00) {
if (_aqconfig_.rssa_device_id >= 0x48 && _aqconfig_.rssa_device_id <= 0x4B /*&& _aqconfig_.rssa_device_id != 0xFF*/) {
// We are good
} else {
LOG(AQUA_LOG,LOG_ERR, "RSSA Device ID 0x%02hhx does not match RS panel, please check config!\n", _aqconfig_.rssa_device_id);
return EXIT_FAILURE;
}
}
#if defined AQ_ONETOUCH || defined AQ_IAQTOUCH
if (_aqconfig_.extended_device_id != 0x00) {
if ( (_aqconfig_.extended_device_id >= 0x30 && _aqconfig_.extended_device_id <= 0x33) ||
(_aqconfig_.extended_device_id >= 0x40 && _aqconfig_.extended_device_id <= 0x43) /*||
_aqconfig_.extended_device_id != 0xFF*/ ) {
// We are good
} else {
LOG(AQUA_LOG,LOG_ERR, "Extended Device ID 0x%02hhx does not match OneTouch or AqualinkTouch ID, please check config!\n", _aqconfig_.extended_device_id);
return EXIT_FAILURE;
}
}
#endif
if (_aqconfig_.deamonize == true)
{
char pidfile[256];
@ -898,6 +1007,7 @@ void main_loop()
bool print_once = false;
int blank_read_reconnect = MAX_ZERO_READ_BEFORE_RECONNECT_BLOCKING; // Will get reset if non blocking
_aqualink_data.panelstatus = STARTING;
sprintf(_aqualink_data.last_display_message, "%s", "Connecting to Control Panel");
_aqualink_data.is_display_message_programming = false;
//_aqualink_data.simulate_panel = false;
@ -908,6 +1018,8 @@ void main_loop()
_aqualink_data.frz_protect_set_point = TEMP_UNKNOWN;
_aqualink_data.pool_htr_set_point = TEMP_UNKNOWN;
_aqualink_data.spa_htr_set_point = TEMP_UNKNOWN;
_aqualink_data.chiller_set_point = TEMP_UNKNOWN;
_aqualink_data.chiller_state = LED_S_UNKNOWN;
_aqualink_data.unactioned.type = NO_ACTION;
_aqualink_data.swg_percent = TEMP_UNKNOWN;
_aqualink_data.swg_ppm = TEMP_UNKNOWN;
@ -961,14 +1073,14 @@ void main_loop()
_aqualink_data.sensors[i].value = TEMP_UNKNOWN;
}
if (_aqconfig_.force_swg == true) {
if (ENABLE_SWG) {
//_aqualink_data.ar_swg_device_status = SWG_STATUS_OFF;
_aqualink_data.swg_led_state = OFF;
_aqualink_data.swg_percent = 0;
_aqualink_data.swg_ppm = 0;
}
if (_aqconfig_.force_chem_feeder == true) {
if (ENABLE_CHEM_FEEDER) {
_aqualink_data.ph = 0;
_aqualink_data.orp = 0;
}
@ -992,9 +1104,12 @@ void main_loop()
if (rs_fd == -1) {
LOG(AQUA_LOG,LOG_ERR, "Error Aqualink setting serial port: %s\n", _aqconfig_.serial_port);
_aqualink_data.panelstatus = SERIAL_ERROR;
#ifndef AQ_CONTAINER
exit(EXIT_FAILURE);
#endif
} else {
_aqualink_data.panelstatus = LOOKING_IDS;
}
LOG(AQUA_LOG,LOG_NOTICE, "Listening to Aqualink RS8 on serial port: %s\n", _aqconfig_.serial_port);
@ -1052,7 +1167,7 @@ void main_loop()
#endif
// Enable or disable rs serial adapter interface.
if (_aqconfig_.rssa_device_id != 0x00)
if (_aqconfig_.rssa_device_id != 0x00 /*&& _aqconfig_.rssa_device_id != 0xFF*/)
addPanelRSserialAdapterInterface();
else
got_probe_rssa = true;

View File

@ -18,6 +18,7 @@ Haywood Universal Color
*/
bool isShowMode(const char *mode);
/****** This list MUST be in order of clight_type enum *******/
char *_color_light_options[NUMBER_LIGHT_COLOR_TYPES][LIGHT_COLOR_OPTIONS] =
//char *_color_light_options[NUMBER_LIGHT_COLOR_TYPES][LIGHT_COLOR_OPTIONS] =
@ -158,6 +159,16 @@ void setColorLightsPanelVersion(uint8_t supported)
set = true;
}
*/
void clear_aqualinkd_light_modes()
{
//_color_light_options[0] = _aqualinkd_custom_colors;
for (int i=0; i < LIGHT_COLOR_OPTIONS; i++) {
_color_light_options[0][i] = NULL;
//_color_light_options[0][i] = i;
//_color_light_options[0][i] = _aqualinkd_custom_colors[i];
}
}
bool set_aqualinkd_light_mode_name(char *name, int index, bool isShow)
{
@ -180,7 +191,7 @@ bool set_aqualinkd_light_mode_name(char *name, int index, bool isShow)
const char *get_aqualinkd_light_mode_name(int index, bool *isShow)
{
// if index 1 is "1" then none are set.
if ( strcmp(_color_light_options[0][1], "1") == 0) {
if ( _color_light_options[0][1] == NULL || strcmp(_color_light_options[0][1], "1") == 0) {
return NULL;
}
@ -205,6 +216,9 @@ const char *get_currentlight_mode_name(clight_detail light, emulation_type proto
return "";
}
if (_color_light_options[light.lightType][light.currentValue] == NULL) {
return "";
}
// Rename any modes depending on emulation type
if (protocol == ALLBUTTON) {
if (strcmp(_color_light_options[light.lightType][light.currentValue],"Afternoon Skies") == 0) {
@ -223,6 +237,10 @@ const char *light_mode_name(clight_type type, int index, emulation_type protocol
return "";
}
if (_color_light_options[type][index] == NULL) {
return "";
}
// Rename any modes depending on emulation type
if (protocol == ALLBUTTON) {
if (strcmp(_color_light_options[type][index],"Afternoon Skies") == 0) {
@ -290,7 +308,7 @@ int build_color_lights_js(struct aqualinkdata *aqdata, char* buffer, int size)
length += sprintf(buffer+length, "var _light_program = [];\n");
if ( strcmp(_color_light_options[0][1], "1") == 0) {
if ( _color_light_options[0][1] == NULL || strcmp(_color_light_options[0][1], "1") == 0) {
length += sprintf(buffer+length, "_light_program[0] = light_program;\n");
i=1;
} else {

View File

@ -32,6 +32,7 @@ const char *light_mode_name(clight_type type, int index, emulation_type protocol
int build_color_lights_js(struct aqualinkdata *aqdata, char* buffer, int size);
int build_color_light_jsonarray(int index, char* buffer, int size);
void clear_aqualinkd_light_modes();
void set_currentlight_value(clight_detail *light, int index);
bool set_aqualinkd_light_mode_name(char *name, int index, bool isShow);

File diff suppressed because it is too large Load Diff

View File

@ -6,22 +6,25 @@
#include "aq_serial.h"
#include "aqualink.h"
//#define CONFIG_DEV_TEST
//#define CONFIG_EDITOR
#define CONFIG_DEV_TEST
#define CONFIG_EDITOR
//#define DEFAULT_LOG_LEVEL 10
#define DEFAULT_LOG_LEVEL LOG_NOTICE
#define DEFAULT_WEBPORT "6580"
#define DEFAULT_WEBROOT "./"
//#define DEFAULT_WEBPORT "6580"
//#define DEFAULT_WEBROOT "./"
#define DEFAULT_WEBPORT "80"
#define DEFAULT_WEBROOT "/var/www/aqualinkd/"
#define DEFAULT_SERIALPORT "/dev/ttyUSB0"
#define DEFAULT_DEVICE_ID "0x0a"
#define DEFAULT_MQTT_DZ_IN NULL
#define DEFAULT_MQTT_DZ_OUT NULL
#define DEFAULT_HASS_DISCOVER NULL
#define DEFAULT_MQTT_AQ_TP NULL
#define DEFAULT_MQTT_DZ_IN NULL // "domoticz/in"
#define DEFAULT_MQTT_DZ_OUT NULL // "domoticz/out"
#define DEFAULT_HASS_DISCOVER "homeassistant"
#define DEFAULT_MQTT_AQ_TP "aqualinkd"
#define DEFAULT_MQTT_SERVER NULL
#define DEFAULT_MQTT_USER NULL
#define DEFAULT_MQTT_PASSWD NULL
//#define DEFAULT_SWG_ZERO_IGNORE_COUNT 0
#define MQTT_ID_LEN 18 // 20 seems to kill mosquitto 1.6
@ -34,6 +37,7 @@
#define READ_RS485_JAN_LX (1 << 4) // Jandy LX heater
#define READ_RS485_JAN_CHEM (1 << 5) // Jandy Chemical Feeder
#define READ_RS485_IAQUALNK (1 << 6) // Read iAqualink messages
#define READ_RS485_HEATPUMP (1 << 7) // Read HeatPump messages
#define MAX_RSSD_LOG_FILTERS 4
@ -87,15 +91,9 @@ struct aqconfig
//bool read_all_devices;
//bool read_pentair_packets;
uint8_t read_RS485_devmask;
bool use_panel_aux_labels;
bool use_panel_aux_labels; // Took this option out of config
//uint8_t force_device_devmask; // should change the below to devmask
bool force_swg;
bool force_ps_setpoints;
bool force_frzprotect_setpoints;
bool force_chem_feeder;
uint8_t force_device_devmask;
//int swg_zero_ignore; // This can be removed since this was due to VSP that's been fixed.
bool display_warnings_web;
@ -103,20 +101,11 @@ struct aqconfig
bool log_raw_bytes; // Read as bytes
unsigned char RSSD_LOG_filter[MAX_RSSD_LOG_FILTERS];
//bool log_raw_RS_bytes;
/*
#ifdef AQ_RS_EXTRA_OPTS
bool readahead_b4_write;
bool prioritize_ack;
#endif
*/
bool mqtt_timed_update;
bool sync_panel_time;
bool enable_scheduler;
int8_t schedule_event_mask; // Was int16_t, but no need
//int16_t schedule_event_mask;
//bool sched_chk_poweron;
//bool sched_chk_freezeprotectoff;
//bool sched_chk_boostoff;
int sched_chk_pumpon_hour;
int sched_chk_pumpoff_hour;
bool ftdi_low_latency;
@ -142,21 +131,23 @@ struct aqconfig _aqconfig_;
#define READ_RSDEV_LX ((_aqconfig_.read_RS485_devmask & READ_RS485_JAN_LX) == READ_RS485_JAN_LX)
#define READ_RSDEV_CHEM ((_aqconfig_.read_RS485_devmask & READ_RS485_JAN_CHEM) == READ_RS485_JAN_CHEM)
#define READ_RSDEV_iAQLNK ((_aqconfig_.read_RS485_devmask & READ_RS485_IAQUALNK) == READ_RS485_IAQUALNK)
#define READ_RSDEV_HPUMP ((_aqconfig_.read_RS485_devmask & READ_RS485_HEATPUMP) == READ_RS485_HEATPUMP)
#define isPDA_IAQT (_aqconfig_.device_id == 0x33)
//#define isPDA ((_aqconfig_.paneltype_mask & RSP_PDA) == RSP_PDA)
/*
#define FORCE_SWG_SP (1 << 0)
#define FORCE_POOLSPA_SP (1 << 1)
#define FORCE_FREEZEPROTECT_SP (1 << 2)
#define FORCE_CHEM_FEEDER (1 << 3)
#define FORCE_CHILLER (1 << 4)
#define ENABLE_SWG ((_aqconfig_.force_device_devmask & FORCE_SWG_SP) == FORCE_SWG_SP)
#define ENABLE_HEATERs ((_aqconfig_.force_device_devmask & FORCE_POOLSPA_SP) == FORCE_POOLSPA_SP)
#define ENABLE_HEATERS ((_aqconfig_.force_device_devmask & FORCE_POOLSPA_SP) == FORCE_POOLSPA_SP)
#define ENABLE_FREEZEPROTECT ((_aqconfig_.force_device_devmask & FORCE_FREEZEPROTECT_SP) == FORCE_FREEZEPROTECT_SP)
#define ENABLE_CHEM_FEEDER ((_aqconfig_.force_device_devmask & FORCE_CHEM_FEEDER) == FORCE_CHEM_FEEDER)
*/
#define ENABLE_CHILLER ((_aqconfig_.force_device_devmask & FORCE_CHILLER) == FORCE_CHILLER)
/*
#ifndef CONFIG_C
@ -184,7 +175,7 @@ char *ncleanalloc(char *str, int length);
const char *pumpType2String(pump_type ptype);
#ifdef CONFIG_EDITOR
int save_config_js(const char* inBuf, int inSize, char* outBuf, int outSize);
int save_config_js(const char* inBuf, int inSize, char* outBuf, int outSize, struct aqualinkdata *aqdata);
void check_print_config (struct aqualinkdata *aqdata);
#endif
@ -203,11 +194,13 @@ typedef enum cfg_value_type{
#ifdef CONFIG_DEV_TEST
typedef struct cfgParam {
void *value_ptr;
void *default_value;
//int max_value; // Max length of string (maybe mad int as well)
cfg_value_type value_type;
char *name;
char *valid_values;
uint8_t mask;
bool advanced;
} cfgParam;
#ifndef CONFIG_C
@ -229,7 +222,7 @@ int _numCfgParams;
#define CFG_N_log_level "log_level"
#define CFG_V_log_level "[\"DEBUG\", \"INFO\", \"NOTICE\", \"WARNING\", \"ERROR\"]"
#define CFG_C_log_level 9
#define CFG_N_socket_port "socket_port"
#define CFG_N_socket_port "socket_port" // Change to Web_socket
#define CFG_C_socket_port 11
#define CFG_N_web_directory "web_directory"
#define CFG_C_web_directory 13
@ -322,6 +315,7 @@ int _numCfgParams;
#define CFG_C_force_frzprotect_setpoints 26
#define CFG_N_force_chem_feeder "force_chem_feeder"
#define CFG_C_force_chem_feeder 17
#define CFG_N_force_chiller "force_chiller"
#define CFG_N_display_warnings_web "display_warnings_web"
#define CFG_C_display_warnings_web 20
#define CFG_N_log_protocol_packets "log_protocol_packets"
@ -343,20 +337,24 @@ int _numCfgParams;
#define CFG_C_read_RS485_Chem 15
#define CFG_N_read_RS485_iAqualink "read_RS485_iAqualink"
#define CFG_C_read_RS485_iAqualink 20
#define CFG_N_read_RS485_HeatPump "read_RS485_HeatPump"
#define CFG_N_enable_scheduler "enable_scheduler"
#define CFG_C_enable_scheduler 16
#define CFG_N_scheduler_check_poweron "scheduler_check_poweron"
#define CFG_C_scheduler_check_poweron 23
#define CFG_N_scheduler_check_freezeprotectoff "scheduler_check_freezeprotectoff"
#define CFG_C_scheduler_check_freezeprotectoff 32
#define CFG_N_scheduler_check_boostoff "scheduler_check_boostoff"
#define CFG_C_scheduler_check_boostoff 24
#define CFG_N_scheduler_check_pumpon_hour "scheduler_check_pumpon_hour"
#define CFG_C_scheduler_check_pumpon_hour 27
#define CFG_N_scheduler_check_pumpoff_hour "scheduler_check_pumpoff_hour"
#define CFG_C_scheduler_check_pumpoff_hour 28
#define CFG_N_event_check_poweron "event_poweron_check_pump"
#define CFG_C_event_check_poweron 24
#define CFG_N_event_check_freezeprotectoff "event_freezeprotectoff_check_pump"
#define CFG_C_event_check_freezeprotectoff 33
#define CFG_N_event_check_boostoff "event_boostoff_check_pump"
#define CFG_C_event_check_boostoff 25
#define CFG_N_event_check_pumpon_hour "event_check_pumpon_hour"
#define CFG_C_event_check_pumpon_hour 23
#define CFG_N_event_check_pumpoff_hour "event_check_pumpoff_hour"
#define CFG_C_event_check_pumpoff_hour 24
#define CFG_N_event_check_usecron "event_check_use_scheduler_times"
#define CFG_C_event_check_usecron 32
#define CFG_N_ftdi_low_latency "ftdi_low_latency"
#define CFG_C_ftdi_low_latency 16

View File

@ -64,6 +64,10 @@ bool processJandyPacket(unsigned char *packet_buffer, int packet_length, struct
{
rtn = processPacketFromJandyChemFeeder(packet_buffer, packet_length, aqdata, previous_packet_to);
}
else if (interestedInNextAck == DRS_HEATPUMP)
{
rtn = processPacketFromHeatPump(packet_buffer, packet_length, aqdata, previous_packet_to);
}
interestedInNextAck = DRS_NONE;
previous_packet_to = NUL;
}
@ -117,6 +121,12 @@ bool processJandyPacket(unsigned char *packet_buffer, int packet_length, struct
{
process_iAqualinkStatusPacket(packet_buffer, packet_length, aqdata);
}
else if (READ_RSDEV_HPUMP && packet_buffer[PKT_DEST] >= JANDY_DEV_HPUMP_MIN && packet_buffer[PKT_DEST] <= JANDY_DEV_HPUMP_MAX)
{
interestedInNextAck = DRS_HEATPUMP;
rtn = processPacketToHeatPump(packet_buffer, packet_length, aqdata);
previous_packet_to = packet_buffer[PKT_DEST];
}
else
{
interestedInNextAck = DRS_NONE;
@ -953,8 +963,35 @@ bool processPacketFromJandyChemFeeder(unsigned char *packet_buffer, int packet_l
return false;
}
bool processPacketToHeatPump(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata)
{
char msg[1024];
beautifyPacket(msg, 1024, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_INFO, "To HPump: %s\n", msg);
if (packet_buffer[3] == 0x0c ) {
if (packet_buffer[4] == 0x00) {
// Heat Pump is off
LOG(DJAN_LOG, LOG_DEBUG, "Heat Pump 0x%02hhx is off\n",packet_buffer[2] );
} else {
// Heat Pump is on or enabled
LOG(DJAN_LOG, LOG_DEBUG, "Heat Pump 0x%02hhx is on or enabled\n",packet_buffer[2]);
}
}
return false;
}
bool processPacketFromHeatPump(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata, const unsigned char previous_packet_to)
{
char msg[1024];
beautifyPacket(msg, 1024, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_INFO, "From HPump: %s\n", msg);
return false;
}
/*
// JXi Heater
@ -1006,4 +1043,23 @@ Fault Check Igntion Control
Fault Short H20 sensor (or Fault open water sensor) -> 0x02
Pump fault
AUX Monitor -> 0x08
*/
/*
Heat Pump Chiller. Messages are in this thread.
https://github.com/sfeakes/AqualinkD/discussions/391#discussioncomment-12431509
LXi heater ping | HEX: 0x10|0x02|0x70|0x0c|0x09|0x00|0x00|0x00|0x97|0x10|0x03|
LXi status | HEX: 0x10|0x02|0x00|0x0d|0x48|0x00|0x00|0x67|0x10|0x03|
LXi heater ping | HEX: 0x10|0x02|0x70|0x0c|0x00|0x00|0x00|0x00|0x8e|0x10|0x03| byte 4 0x00 is OFF.
LXi status | HEX: 0x10|0x02|0x00|0x0d|0x40|0x00|0x00|0x5f|0x10|0x03| 0x40 (maybe off, but odd message for off)
LXi heater ping | HEX: 0x10|0x02|0x70|0x0c|0x29|0x00|0x00|0x00|0xb7|0x10|0x03|. byte 4 0x29 This is some on / enable
LXi status | HEX: 0x10|0x02|0x00|0x0d|0x68|0x00|0x00|0x87|0x10|0x03| 0x68 probably is chiller ON
*/

View File

@ -22,6 +22,9 @@ bool processPacketToJandyLXHeater(unsigned char *packet_buffer, int packet_lengt
bool processPacketFromJandyChemFeeder(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata, const unsigned char previous_packet_to );
bool processPacketToJandyChemFeeder(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata);
bool processPacketToHeatPump(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata);
bool processPacketFromHeatPump(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata, const unsigned char previous_packet_to);
void get_swg_status_mqtt(struct aqualinkdata *aqdata, char *message, int *status, int *dzalert);
aqledstate get_swg_led_state(struct aqualinkdata *aqdata);

View File

@ -111,6 +111,33 @@ const char *HASSIO_SWG_DISCOVER = "{"
"\"optimistic\": false"
"}";
const char *HASSIO_CHILLER_DISCOVER = "{"
"\"device\": {" HASS_DEVICE "},"
"\"availability\": {" HASS_AVAILABILITY "},"
"\"type\": \"climate\","
"\"unique_id\": \"aqualinkd_%s\","
"\"name\": \"Chiller\","
"\"modes\": [\"off\", \"cool\"],"
"\"send_if_off\": true,"
"\"initial\": 34,"
"\"power_command_topic\": \"%s/%s/set\"," // add
"\"payload_on\": \"2\","
"\"payload_off\": \"0\","
"\"current_temperature_topic\": \"%s/%s\","
"\"mode_command_topic\": \"%s/%s/set\"," // add
"\"mode_state_topic\": \"%s/%s\","
"\"mode_state_template\": \"{%% set values = { '0':'off', '2':'cool'} %%}{{ values[value] if value in values.keys() else 'off' }}\","
"\"temperature_command_topic\": \"%s/%s/setpoint/set\","
"\"temperature_state_topic\": \"%s/%s/setpoint\","
"\"action_template\": \"{%% set values = { '0':'off', '2':'cooling'} %%}{{ values[value] if value in values.keys() else 'off' }}\","
"\"action_topic\": \"%s/%s\","
/*"\"temperature_state_template\": \"{{ value_json }}\""*/
"\"min_temp\": %0.2f,"
"\"max_temp\": %0.2f,"
"\"temperature_unit\": \"%s\""
//"%s"
"}";
// Use Fan for VSP
// Need to change the max / min. These do NOT lomit the slider in hassio, only the MQTT limits.
// So the 0-100% should be 600-3450 RPM and 15-130 GPM (ie 1% would = 600 & 0%=off)
@ -375,8 +402,8 @@ void publish_mqtt_hassio_discover(struct aqualinkdata *aqdata, struct mg_connect
{
if (strcmp("NONE",aqdata->aqbuttons[i].label) != 0 ) {
// Heaters
if ( (strcmp(BTN_POOL_HTR,aqdata->aqbuttons[i].name) == 0 && (_aqconfig_.force_ps_setpoints || aqdata->pool_htr_set_point != TEMP_UNKNOWN)) ||
(strcmp(BTN_SPA_HTR,aqdata->aqbuttons[i].name)==0 && (_aqconfig_.force_ps_setpoints || aqdata->spa_htr_set_point != TEMP_UNKNOWN)) ) {
if ( (strcmp(BTN_POOL_HTR,aqdata->aqbuttons[i].name) == 0 && (ENABLE_HEATERS || aqdata->pool_htr_set_point != TEMP_UNKNOWN)) ||
(strcmp(BTN_SPA_HTR,aqdata->aqbuttons[i].name)==0 && (ENABLE_HEATERS || aqdata->spa_htr_set_point != TEMP_UNKNOWN)) ) {
sprintf(msg,HASSIO_CLIMATE_DISCOVER,
connections,
_aqconfig_.mqtt_aq_topic,
@ -390,8 +417,10 @@ void publish_mqtt_hassio_discover(struct aqualinkdata *aqdata, struct mg_connect
_aqconfig_.mqtt_aq_topic,aqdata->aqbuttons[i].name,
_aqconfig_.mqtt_aq_topic,aqdata->aqbuttons[i].name,
//(_aqconfig_.convert_mqtt_temp?HASSIO_CONVERT_CLIMATE_TOF:HASSIO_NO_CONVERT_CLIMATE));
(_aqconfig_.convert_mqtt_temp?degFtoC(36):36.00),
(_aqconfig_.convert_mqtt_temp?degFtoC(104):104.00),
//(_aqconfig_.convert_mqtt_temp?degFtoC(36):36.00),
//(_aqconfig_.convert_mqtt_temp?degFtoC(104):104.00),
(_aqconfig_.convert_mqtt_temp?(float)HEATER_MIN_C:(float)HEATER_MIN_F),
(_aqconfig_.convert_mqtt_temp?(float)HEATER_MAX_C:(float)HEATER_MAX_F),
(_aqconfig_.convert_mqtt_temp?"C":"F"));
sprintf(topic, "%s/climate/aqualinkd/aqualinkd_%s/config", _aqconfig_.mqtt_hass_discover_topic, aqdata->aqbuttons[i].name);
send_mqtt(nc, topic, msg);
@ -461,7 +490,7 @@ void publish_mqtt_hassio_discover(struct aqualinkdata *aqdata, struct mg_connect
}
// Freezeprotect
if ( _aqconfig_.force_frzprotect_setpoints || (aqdata->frz_protect_set_point != TEMP_UNKNOWN && aqdata->air_temp != TEMP_UNKNOWN) ) {
if ( ENABLE_FREEZEPROTECT || (aqdata->frz_protect_set_point != TEMP_UNKNOWN && aqdata->air_temp != TEMP_UNKNOWN) ) {
sprintf(msg, HASSIO_FREEZE_PROTECT_DISCOVER,
connections,
_aqconfig_.mqtt_aq_topic,
@ -472,15 +501,40 @@ void publish_mqtt_hassio_discover(struct aqualinkdata *aqdata, struct mg_connect
_aqconfig_.mqtt_aq_topic,FREEZE_PROTECT,
_aqconfig_.mqtt_aq_topic,FREEZE_PROTECT,
//(_aqconfig_.convert_mqtt_temp?HASSIO_CONVERT_CLIMATE_TOF:HASSIO_NO_CONVERT_CLIMATE));
(_aqconfig_.convert_mqtt_temp?degFtoC(34):34.00),
(_aqconfig_.convert_mqtt_temp?degFtoC(42):42.00),
//(_aqconfig_.convert_mqtt_temp?degFtoC(34):34.00),
//(_aqconfig_.convert_mqtt_temp?degFtoC(42):42.00),
(_aqconfig_.convert_mqtt_temp?(float)FREEZE_PT_MIN_C:(float)FREEZE_PT_MIN_F),
(_aqconfig_.convert_mqtt_temp?(float)FREEZE_PT_MAX_C:(float)FREEZE_PT_MAX_F),
(_aqconfig_.convert_mqtt_temp?"C":"F"));
sprintf(topic, "%s/climate/aqualinkd/aqualinkd_%s/config", _aqconfig_.mqtt_hass_discover_topic, FREEZE_PROTECT);
send_mqtt(nc, topic, msg);
}
if (ENABLE_CHILLER || (aqdata->chiller_set_point != TEMP_UNKNOWN && aqdata->chiller_state != LED_S_UNKNOWN) ) {
// USe freeze protect for the moment.
sprintf(msg, HASSIO_CHILLER_DISCOVER,
connections,
_aqconfig_.mqtt_aq_topic,
CHILLER,
_aqconfig_.mqtt_aq_topic,CHILLER,
_aqconfig_.mqtt_aq_topic,POOL_TEMP_TOPIC,
_aqconfig_.mqtt_aq_topic,CHILLER,
_aqconfig_.mqtt_aq_topic,CHILLER_ENABELED,
_aqconfig_.mqtt_aq_topic,CHILLER,
_aqconfig_.mqtt_aq_topic,CHILLER,
_aqconfig_.mqtt_aq_topic,CHILLER,
//(_aqconfig_.convert_mqtt_temp?HASSIO_CONVERT_CLIMATE_TOF:HASSIO_NO_CONVERT_CLIMATE));
//(_aqconfig_.convert_mqtt_temp?degFtoC(34):34.00),
//(_aqconfig_.convert_mqtt_temp?degFtoC(104):104.00),
(_aqconfig_.convert_mqtt_temp?(float)CHILLER_MIN_C:(float)CHILLER_MIN_F),
(_aqconfig_.convert_mqtt_temp?(float)CHILLER_MAX_C:(float)CHILLER_MAX_F),
(_aqconfig_.convert_mqtt_temp?"C":"F"));
sprintf(topic, "%s/climate/aqualinkd/aqualinkd_%s/config", _aqconfig_.mqtt_hass_discover_topic, CHILLER);
send_mqtt(nc, topic, msg);
}
// SWG
if ( aqdata->swg_percent != TEMP_UNKNOWN ) {
if ( ENABLE_SWG || aqdata->swg_percent != TEMP_UNKNOWN ) {
sprintf(msg, HASSIO_SWG_DISCOVER,
connections,
@ -636,14 +690,14 @@ void publish_mqtt_hassio_discover(struct aqualinkdata *aqdata, struct mg_connect
}
// Chem feeder (ph/orp)
if (_aqconfig_.force_chem_feeder || aqdata->ph != TEMP_UNKNOWN) {
if (ENABLE_CHEM_FEEDER || aqdata->ph != TEMP_UNKNOWN) {
rsm_char_replace(idbuf, CHEM_PH_TOPIC, "/", "_");
sprintf(msg, HASSIO_SENSOR_DISCOVER,connections,_aqconfig_.mqtt_aq_topic,idbuf,"Water Chemistry pH",_aqconfig_.mqtt_aq_topic,CHEM_PH_TOPIC, "pH", "mdi:water-outline");
sprintf(topic, "%s/sensor/aqualinkd/aqualinkd_%s/config", _aqconfig_.mqtt_hass_discover_topic, idbuf);
send_mqtt(nc, topic, msg);
}
if (_aqconfig_.force_chem_feeder || aqdata->orp != TEMP_UNKNOWN) {
if (ENABLE_CHEM_FEEDER || aqdata->orp != TEMP_UNKNOWN) {
rsm_char_replace(idbuf, CHEM_ORP_TOPIC, "/", "_");
sprintf(msg, HASSIO_SENSOR_DISCOVER,connections,_aqconfig_.mqtt_aq_topic,idbuf,"Water Chemistry ORP",_aqconfig_.mqtt_aq_topic,CHEM_ORP_TOPIC, "orp", "mdi:water-outline");
sprintf(topic, "%s/sensor/aqualinkd/aqualinkd_%s/config", _aqconfig_.mqtt_hass_discover_topic, idbuf);

View File

@ -348,6 +348,39 @@ void updateAQButtonFromPageButton(struct aqualinkdata *aq_data, struct iaqt_page
}
}
}
// Quick and dirty check for heat pump/chiller
// if we see Heat Pump then chiller is not enabled.
// if we see chiller then it is enabled.
// Not sure why button name changes from "Heat Pump" to "Chiller", need to figure this out. (might be pump)
// THIS NEEDS TO BE DELETED AND MAKE A HEAT_PUMP / CHILLER BUTTON
int ignore = 5;
if (_currentPageLoading == IAQ_PAGE_HOME)
ignore = 0;
if ( rsm_strmatch_ignore((const char *)pageButton->name, "Heat Pump", ignore) == 0 ) {
aq_data->chiller_state = OFF;
aq_data->updated = true;
//printf("********* Disable Chiller \n");
} else if (rsm_strmatch_ignore((const char *)pageButton->name, "Chiller", ignore) == 0) {
switch(pageButton->state) {
case 0x00:
aq_data->chiller_state = OFF;
break;
case 0x01:
aq_data->chiller_state = ON;
break;
case 0x02:
aq_data->chiller_state = FLASH;
break;
case 0x03:
aq_data->chiller_state = ENABLE;
break;
}
aq_data->updated = true;
LOG(IAQT_LOG,LOG_DEBUG, "*** Found Status for %s state 0x%02hhx\n", "Chiller", pageButton->state);
}
}
void processPageButton(unsigned char *message, int length, struct aqualinkdata *aq_data)
@ -471,7 +504,31 @@ void processPageButton(unsigned char *message, int length, struct aqualinkdata *
} */
}
/* No need as we can see this in buttons
typedef enum iaqt_devices{
DEVICE_RESET = (1 << 0),
DEVICE_CHILLER = (1 << 1)
}iaqt_devices;
void iaqt_device_update(struct aqualinkdata *aq_data, iaqt_devices mask) {
static uint8_t device_bitmask;
if (mask == DEVICE_RESET) {
if ( (device_bitmask & DEVICE_CHILLER) == DEVICE_CHILLER ) {
aq_data->chiller_state = ON;
LOG(IAQT_LOG,LOG_INFO, "Saw Chiller in device status\n");
} else {
aq_data->chiller_state = OFF;
}
device_bitmask = 0;
return;
}
device_bitmask |= mask;
}
*/
// Log if we saw a pump in a device page cycle.
void iaqt_pump_update(struct aqualinkdata *aq_data, int updated) {
@ -699,6 +756,12 @@ void passDeviceStatusPage(struct aqualinkdata *aq_data)
LOG(IAQT_LOG,LOG_INFO, "Set Cemlink ORP = %d PH = %f from message '%s'\n",orp,ph,_deviceStatus[i]);
}
}
/*
if (rsm_strcmp(_deviceStatus[i],"Chiller") == 0) {
iaqt_device_update(aq_data, DEVICE_CHILLER);
aq_data->chiller_state = ON;
}
*/
//#ifdef READ_SWG_FROM_EXTENDED_ID
else if (isPDA_PANEL) {
if (rsm_strcmp(_deviceStatus[i],"AQUAPURE") == 0) {
@ -754,6 +817,7 @@ void processPage(struct aqualinkdata *aq_data)
iaqt_queue_cmd(KEY_IAQTCH_KEY02);
} else {
iaqt_pump_update(aq_data, -1); // Reset pumps.
//iaqt_device_update(aq_data,DEVICE_RESET); // Reset any devices we monitor.
if ( (isPDA_PANEL || PANEL_SIZE() >= 16) && !in_iaqt_programming_mode(aq_data) ) {
iaqt_queue_cmd(KEY_IAQTCH_HOME);
}
@ -1160,7 +1224,7 @@ if not programming && poll packet {
iaqt_queue_cmd(nextPageRequestKey);
} else if ( (_pollCnt % DEVICE_STATUS_POLL_COUNT == 0) &&
_currentPage == IAQ_PAGE_DEVICES || _currentPage == IAQ_PAGE_DEVICES_REV_Yg) {
(_currentPage == IAQ_PAGE_DEVICES || _currentPage == IAQ_PAGE_DEVICES_REV_Yg) ) {
iaqt_queue_cmd(KEY_IAQTCH_STATUS); // This will force us to go to status, then it'll jump back to devices, then force status again
}
} else if (in_programming_mode(aq_data) == true) {

View File

@ -46,7 +46,12 @@
#define KEY_IAQTCH_LOCKOUT_PASSWD KEY_IAQTCH_KEY08
#define KEY_IAQTCH_SET_ACQUAPURE KEY_IAQTCH_KEY09
// Set Point types
typedef enum SP_TYPE{
SP_POOL,
SP_SPA,
SP_CHILLER
} SP_TYPE;
bool _cansend = false;
@ -960,6 +965,12 @@ void *get_aqualink_iaqtouch_setpoints( void *ptr )
}
}
button = iaqtFindButtonByLabel("Chiller");
if (button != NULL) {
aq_data->chiller_set_point = rsm_atoi((char *)&button->name + 8);
LOG(IAQT_LOG,LOG_DEBUG, "IAQ Touch got to Chiller setpoint %d\n",aq_data->chiller_set_point);
}
if ( goto_iaqt_page(IAQ_PAGE_FREEZE_PROTECT, aq_data) == false )
goto f_end;
@ -1191,8 +1202,7 @@ void *set_aqualink_iaqtouch_swg_boost( void *ptr )
}
bool set_aqualink_iaqtouch_heater_setpoint( struct aqualinkdata *aq_data, bool pool, int val)
bool set_aqualink_iaqtouch_heater_setpoint( struct aqualinkdata *aq_data, SP_TYPE type, int val)
{
struct iaqt_page_button *button;
char *name;
@ -1201,17 +1211,21 @@ bool set_aqualink_iaqtouch_heater_setpoint( struct aqualinkdata *aq_data, bool p
return false;
if (isCOMBO_PANEL) {
if (pool)
if (type == SP_POOL)
name = "Pool Heat";
else
name = "Spa Heat";
} else {
if (pool)
if (type == SP_POOL)
name = "Temp1";
else
name = "Temp2";
}
if (type == SP_CHILLER) {
name = "Chiller";
}
button = iaqtFindButtonByLabel(name);
if (button == NULL) {
@ -1231,12 +1245,18 @@ bool set_aqualink_iaqtouch_heater_setpoint( struct aqualinkdata *aq_data, bool p
button = iaqtFindButtonByLabel(name);
if (button != NULL) {
if (pool)
int value = 0;
if (type == SP_POOL) {
aq_data->pool_htr_set_point = rsm_atoi((char *)&button->name + strlen(name));
else
value = aq_data->pool_htr_set_point;
} else if (type == SP_SPA) {
aq_data->spa_htr_set_point = rsm_atoi((char *)&button->name + strlen(name));
LOG(IAQT_LOG,LOG_DEBUG, "IAQ Touch set %s heater setpoint to %d\n",name,pool?aq_data->pool_htr_set_point:aq_data->spa_htr_set_point);
value = aq_data->spa_htr_set_point;
} else if (type == SP_CHILLER) {
aq_data->chiller_set_point = rsm_atoi((char *)&button->name + strlen(name));
value = aq_data->chiller_set_point;
}
LOG(IAQT_LOG,LOG_DEBUG, "IAQ Touch set %s heater setpoint to %d\n",name,value);
}
return true;
@ -1251,9 +1271,9 @@ void *set_aqualink_iaqtouch_spa_heater_temp( void *ptr )
waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_IAQTOUCH_SPA_HEATER_TEMP);
int val = atoi((char*)threadCtrl->thread_args);
val = setpoint_check(SPA_HTR_SETOINT, val, aq_data);
val = setpoint_check(SPA_HTR_SETPOINT, val, aq_data);
set_aqualink_iaqtouch_heater_setpoint(aq_data, false, val);
set_aqualink_iaqtouch_heater_setpoint(aq_data, SP_SPA, val);
goto_iaqt_page(IAQ_PAGE_HOME, aq_data);
cleanAndTerminateThread(threadCtrl);
@ -1270,9 +1290,28 @@ void *set_aqualink_iaqtouch_pool_heater_temp( void *ptr )
waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_IAQTOUCH_POOL_HEATER_TEMP);
int val = atoi((char*)threadCtrl->thread_args);
val = setpoint_check(POOL_HTR_SETOINT, val, aq_data);
val = setpoint_check(POOL_HTR_SETPOINT, val, aq_data);
set_aqualink_iaqtouch_heater_setpoint(aq_data, true, val);
set_aqualink_iaqtouch_heater_setpoint(aq_data, SP_POOL, val);
goto_iaqt_page(IAQ_PAGE_HOME, aq_data);
cleanAndTerminateThread(threadCtrl);
return ptr;
}
void *set_aqualink_iaqtouch_chiller_temp( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_IAQTOUCH_POOL_HEATER_TEMP);
int val = atoi((char*)threadCtrl->thread_args);
//val = setpoint_check(POOL_HTR_SETPOINT, val, aq_data);
set_aqualink_iaqtouch_heater_setpoint(aq_data, SP_CHILLER, val);
goto_iaqt_page(IAQ_PAGE_HOME, aq_data);
cleanAndTerminateThread(threadCtrl);

View File

@ -3,6 +3,8 @@
#ifndef IAQ_TOUCH_PROGRAMMER_H_
#define IAQ_TOUCH_PROGRAMMER_H_
unsigned char pop_iaqt_cmd(unsigned char receive_type);
void set_iaq_cansend(bool cansend);
@ -17,6 +19,7 @@ void *set_aqualink_iaqtouch_swg_percent( void *ptr );
void *set_aqualink_iaqtouch_swg_boost( void *ptr );
void *set_aqualink_iaqtouch_spa_heater_temp( void *ptr );
void *set_aqualink_iaqtouch_pool_heater_temp( void *ptr );
void *set_aqualink_iaqtouch_chiller_temp( void *ptr );
void *set_aqualink_iaqtouch_time( void *ptr );
void *set_aqualink_iaqtouch_pump_vs_program( void *ptr );
void *set_aqualink_iaqtouch_light_colormode( void *ptr );

View File

@ -321,7 +321,7 @@ int build_device_JSON(struct aqualinkdata *aqdata, char* buffer, int size, bool
for (i=0; i < aqdata->total_buttons; i++)
{
if ( strcmp(BTN_POOL_HTR,aqdata->aqbuttons[i].name) == 0 && (_aqconfig_.force_ps_setpoints || aqdata->pool_htr_set_point != TEMP_UNKNOWN)) {
if ( strcmp(BTN_POOL_HTR,aqdata->aqbuttons[i].name) == 0 && (ENABLE_HEATERS || aqdata->pool_htr_set_point != TEMP_UNKNOWN)) {
length += sprintf(buffer+length, "{\"type\": \"setpoint_thermo\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"spvalue\": \"%.*f\", \"value\": \"%.*f\", \"int_status\": \"%d\", \"timer_active\":\"%s\" },",
aqdata->aqbuttons[i].name,
aqdata->aqbuttons[i].label,
@ -334,7 +334,7 @@ int build_device_JSON(struct aqualinkdata *aqdata, char* buffer, int size, bool
LED2int(aqdata->aqbuttons[i].led->state),
((aqdata->aqbuttons[i].special_mask & TIMER_ACTIVE) == TIMER_ACTIVE?JSON_ON:JSON_OFF) );
} else if ( strcmp(BTN_SPA_HTR,aqdata->aqbuttons[i].name)==0 && (_aqconfig_.force_ps_setpoints || aqdata->spa_htr_set_point != TEMP_UNKNOWN)) {
} else if ( strcmp(BTN_SPA_HTR,aqdata->aqbuttons[i].name)==0 && (ENABLE_HEATERS || aqdata->spa_htr_set_point != TEMP_UNKNOWN)) {
length += sprintf(buffer+length, "{\"type\": \"setpoint_thermo\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"spvalue\": \"%.*f\", \"value\": \"%.*f\", \"int_status\": \"%d\", \"timer_active\":\"%s\" },",
aqdata->aqbuttons[i].name,
aqdata->aqbuttons[i].label,
@ -388,7 +388,7 @@ int build_device_JSON(struct aqualinkdata *aqdata, char* buffer, int size, bool
}*/
}
if ( _aqconfig_.force_frzprotect_setpoints || (aqdata->frz_protect_set_point != TEMP_UNKNOWN && aqdata->air_temp != TEMP_UNKNOWN) ) {
if ( ENABLE_FREEZEPROTECT || (aqdata->frz_protect_set_point != TEMP_UNKNOWN && aqdata->air_temp != TEMP_UNKNOWN) ) {
length += sprintf(buffer+length, "{\"type\": \"setpoint_freeze\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"spvalue\": \"%.*f\", \"value\": \"%.*f\", \"int_status\": \"%d\" },",
FREEZE_PROTECT,
"Freeze Protection",
@ -403,6 +403,21 @@ int build_device_JSON(struct aqualinkdata *aqdata, char* buffer, int size, bool
aqdata->frz_protect_state==ON?1:0);
}
if ( ENABLE_CHILLER || (aqdata->chiller_set_point != TEMP_UNKNOWN && getWaterTemp(aqdata) != TEMP_UNKNOWN) ) {
length += sprintf(buffer+length, "{\"type\": \"setpoint_chiller\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"spvalue\": \"%.*f\", \"value\": \"%.*f\", \"int_status\": \"%d\" },",
CHILLER,
"Chiller",
//JSON_OFF,
aqdata->chiller_state==ON?JSON_ON:JSON_OFF,
//JSON_ENABLED,
aqdata->chiller_state==ON?LED2text(ON):LED2text(ENABLE),
((homekit)?2:0),
((homekit_f)?degFtoC(aqdata->chiller_set_point):aqdata->chiller_set_point),
((homekit)?2:0),
((homekit_f)?degFtoC(getWaterTemp(aqdata)):getWaterTemp(aqdata)),
aqdata->chiller_state==ON?1:0);
}
if (aqdata->swg_led_state != LED_S_UNKNOWN) {
if ( aqdata->swg_percent != TEMP_UNKNOWN ) {
length += sprintf(buffer+length, "{\"type\": \"setpoint_swg\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"spvalue\": \"%.*f\", \"value\": \"%.*f\", \"int_status\": \"%d\" },",
@ -603,7 +618,8 @@ int build_aqualink_status_JSON(struct aqualinkdata *aqdata, char* buffer, int si
length += sprintf(buffer+length, "{\"type\": \"status\"");
length += sprintf(buffer+length, ",\"status\":\"%s\"",getStatus(aqdata) );
length += sprintf(buffer+length, ",\"panel_message\":\"%s\"",aqdata->last_message );
length += sprintf(buffer+length, ",\"panel_type\":\"%s\"",getPanelString());
length += sprintf(buffer+length, ",\"panel_type_full\":\"%s\"",getPanelString());
length += sprintf(buffer+length, ",\"panel_type\":\"%s\"",getShortPanelString());
//length += sprintf(buffer+length, ",\"message\":\"%s\"",aqdata->message );
length += sprintf(buffer+length, ",\"version\":\"%s\"",aqdata->version );//8157 REV MMM",
length += sprintf(buffer+length, ",\"aqualinkd_version\":\"%s\"", AQUALINKD_VERSION ); //1.0b,
@ -617,6 +633,9 @@ int build_aqualink_status_JSON(struct aqualinkdata *aqdata, char* buffer, int si
length += sprintf(buffer+length, ",\"spa_htr_set_pnt\":\"%d\"",aqdata->spa_htr_set_point );//"99",
//length += sprintf(buffer+length, ",\"freeze_protection":\"%s\"",aqdata->frz_protect_set_point );//"off",
length += sprintf(buffer+length, ",\"frz_protect_set_pnt\":\"%d\"",aqdata->frz_protect_set_point );//"0",
if (ENABLE_CHILLER || aqdata->chiller_set_point != TEMP_UNKNOWN) {
length += sprintf(buffer+length, ",\"chiller_set_pnt\":\"%d\"",aqdata->chiller_set_point );//"0",
}
if ( aqdata->air_temp == TEMP_UNKNOWN )
length += sprintf(buffer+length, ",\"air_temp\":\" \"");
@ -682,7 +701,7 @@ int build_aqualink_status_JSON(struct aqualinkdata *aqdata, char* buffer, int si
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 || _aqconfig_.force_frzprotect_setpoints ) {
if ( aqdata->frz_protect_set_point != TEMP_UNKNOWN || ENABLE_FREEZEPROTECT ) {
//length += sprintf(buffer+length, ", \"%s\": \"%s\"", FREEZE_PROTECT, aqdata->frz_protect_state==ON?JSON_ON:JSON_ENABLED);
length += sprintf(buffer+length, ", \"%s\": \"%s\"", FREEZE_PROTECT, LED2text(aqdata->frz_protect_state) );
}
@ -1089,40 +1108,55 @@ int json_cfg_element_OLD(char* buffer, int size, const char *name, const void *v
#ifdef CONFIG_EDITOR
int json_cfg_element(char* buffer, int size, const char *name, const void *value, cfg_value_type type, uint8_t mask, char *valid_val) {
int json_cfg_element(char* buffer, int size, const char *name, const void *value, cfg_value_type type, uint8_t mask, char *valid_val, bool advanced) {
int result = 0;
char valid_values[256];
char adv[20];
if (valid_val != NULL) {
sprintf(valid_values,",\"valid values\":%s",valid_val);
}
sprintf(adv,",\"advanced\": \"%s\"", advanced?"yes":"no");
switch(type){
case CFG_INT:
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"%d\", \"type\":\"int\" %s}", name, *(int *)value, (valid_val==NULL?"":valid_values) );
if (*(int *)value == AQ_UNKNOWN) {
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"\", \"type\":\"int\" %s %s}", name, (valid_val==NULL?"":valid_values),adv );
} else {
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"%d\", \"type\":\"int\" %s %s}", name, *(int *)value, (valid_val==NULL?"":valid_values),adv );
}
break;
case CFG_STRING:
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"%s\", \"type\":\"string\" %s}", name, *(char **)value, (valid_val==NULL?"":valid_values) );
if (*(char **)value == NULL) {
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"\", \"type\":\"string\" %s %s}", name, (valid_val==NULL?"":valid_values),adv );
} else {
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"%s\", \"type\":\"string\" %s %s}", name, *(char **)value, (valid_val==NULL?"":valid_values),adv );
}
break;
case CFG_BOOL:
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"%s\", \"type\":\"bool\" %s}", name, bool2text(*(bool *)value), (valid_val==NULL?"":valid_values));
//result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"%s\", \"type\":\"bool\" %s}", name, bool2text(*(bool *)value), (valid_val==NULL?"":valid_values));
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"%s\", \"type\":\"bool\", \"valid values\": %s %s}", name, bool2text(*(bool *)value), CFG_V_BOOL, adv);
break;
case CFG_HEX:
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"0x%02hhx\", \"type\":\"hex\" %s}", name, *(unsigned char *)value, (valid_val==NULL?"":valid_values) );
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"0x%02hhx\", \"type\":\"hex\" %s %s}", name, *(unsigned char *)value, (valid_val==NULL?"":valid_values), adv );
break;
case CFG_FLOAT:
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"%f\", \"type\":\"float\" %s}", name, *(float *)value, (valid_val==NULL?"":valid_values) );
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"%f\", \"type\":\"float\" %s %s}", name, *(float *)value, (valid_val==NULL?"":valid_values), adv );
break;
case CFG_BITMASK:
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"%s\", \"type\":\"bool\" %s}", name, (*(uint8_t *)value & mask) == mask? bool2text(true):bool2text(false) ,CFG_V_BOOL );
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"%s\", \"type\":\"bool\", \"valid values\": %s %s}", name, (*(uint8_t *)value & mask) == mask? bool2text(true):bool2text(false) ,CFG_V_BOOL, adv );
break;
case CFG_SPECIAL:
if (strncasecmp(name, CFG_N_log_level, strlen(CFG_N_log_level)) == 0) {
//fprintf(fp, "%s=%s\n", _cfgParams[i].name, loglevel2cgn_name(_aqconfig_.log_level));
//result = json_cfg_element(buffer+length, size-length, CFG_N_log_level, &stringptr, CFG_STRING, "[\"DEBUG\", \"INFO\", \"NOTICE\", \"WARNING\", \"ERROR\"]"));
//stringptr = loglevel2cgn_name(_aqconfig_.log_level);
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"%s\", \"type\":\"string\" %s}",name,loglevel2cgn_name(*(int *)value), "[\"DEBUG\", \"INFO\", \"NOTICE\", \"WARNING\", \"ERROR\"]");
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"%s\", \"type\":\"string\" %s %s}",name,loglevel2cgn_name(*(int *)value), ",\"valid values\":[\"DEBUG\", \"INFO\", \"NOTICE\", \"WARNING\", \"ERROR\"]", adv);
} else if (strncasecmp(name, CFG_N_panel_type, strlen(CFG_N_panel_type)) == 0) {
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"%s\", \"type\":\"string\" %s}",name,getShortPanelString(), adv);
} else {
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"Something went wrong\", \"type\":\"string\"}",name);
}
@ -1130,7 +1164,7 @@ int json_cfg_element(char* buffer, int size, const char *name, const void *value
}
if (result <= 0 || result >= size) {
LOG(NET_LOG,LOG_ERR, "Buffer full in build_aqualink_config_JSON(), result truncated!");
LOG(NET_LOG,LOG_ERR, "Buffer full, result truncated! size left=%d needed=%d @ element %s\n",size,result,name);
return 0;
}
@ -1166,7 +1200,9 @@ int build_aqualink_config_JSON(char* buffer, int size, struct aqualinkdata *aq_d
int result;
int i;
char buf[256];
char buf1[256];
const char *stringptr;
int delectCharAt = 0;
if ((result = snprintf(buffer+length, size-length, "{\"type\": \"config\"")) < 0 || result >= size-length) {
length += snprintf(buffer+length, size-length, "}");
@ -1174,29 +1210,103 @@ int build_aqualink_config_JSON(char* buffer, int size, struct aqualinkdata *aq_d
} else {
length += result;
}
/*
if ((result = snprintf(buffer+length, size-length, ",\"status\": \"!!!! NOT FULLY IMPLIMENTED YET !!!!\"")) < 0 || result >= size-length) {
length += snprintf(buffer+length, size-length, "}");
return length;
} else {
length += result;
}
*/
if ((result = snprintf(buffer+length, size-length, ",\"max_pumps\": \"%d\",\"max_lights\": \"%d\",\"max_sensors\": \"%d\",\"max_light_programs\": \"%d\",\"max_vbuttons\": \"%d\"",
MAX_PUMPS,MAX_LIGHTS,MAX_SENSORS,LIGHT_COLOR_OPTIONS-1, (TOTAL_BUTTONS - aq_data->virtual_button_start) )) < 0 || result >= size-length) {
length += snprintf(buffer+length, size-length, "}");
return length;
} else {
length += result;
}
#ifdef CONFIG_DEV_TEST
for (int i=0; i <= _numCfgParams; i++) {
if ((result = json_cfg_element(buffer+length, size-length, _cfgParams[i].name, _cfgParams[i].value_ptr, _cfgParams[i].value_type, _cfgParams[i].mask, _cfgParams[i].valid_values)) <= 0)
// We can't change web_directory or port while running, so don;t even chow those options.
// mongoose holds a pointer to the string web_directoy, so can;t change that easily while running
// web port = well derr we are using that currently
if ( strncasecmp(_cfgParams[i].name, CFG_N_socket_port, strlen(CFG_N_socket_port)) == 0 ||
strncasecmp(_cfgParams[i].name, CFG_N_web_directory, strlen(CFG_N_web_directory)) == 0 ) {
continue;
}
if ((result = json_cfg_element(buffer+length, size-length, _cfgParams[i].name, _cfgParams[i].value_ptr, _cfgParams[i].value_type, _cfgParams[i].mask, _cfgParams[i].valid_values, _cfgParams[i].advanced)) <= 0) {
LOG(NET_LOG,LOG_ERR, "Config json buffer full in, result truncated! size=%d curently used=%d\n",size,length);
return length;
} else {
length += result;
}
}
// TODO add custom light modes/colors
// TODO add RSSD LOG FILTERS
// Add custom censors
for (i = 1; i <= aq_data->num_sensors; i++)
{
length += sprintf(buffer+length, ",\"sensor_%.2d\":{ \"advanced\":\"yes\",",i );
// The next json_cfg_element() call will add a , at the beginning, so save the next char index so we can delete it later.
delectCharAt = length;
//fprintf(fp,"\nsensor_%.2d_path=%s\n",i+1,aqdata->sensors->path);
//fprintf(fp,"sensor_%.2d_label=%s\n",i+1,aqdata->sensors->label);
//fprintf(fp,"sensor_%.2d_factor=%f\n",i+1,aqdata->sensors->factor);
sprintf(buf,"sensor_%.2d_path", i);
if ((result = json_cfg_element(buffer+length, size-length, buf, &aq_data->sensors[i-1].path, CFG_STRING, 0, NULL, true)) <= 0)
return length;
else
length += result;
sprintf(buf,"sensor_%.2d_label", i);
if ((result = json_cfg_element(buffer+length, size-length, buf, &aq_data->sensors[i-1].label, CFG_STRING, 0, NULL, true)) <= 0)
return length;
else
length += result;
sprintf(buf,"sensor_%.2d_factor", i);
if ((result = json_cfg_element(buffer+length, size-length, buf, &aq_data->sensors[i-1].factor, CFG_FLOAT, 0, NULL, true)) <= 0)
return length;
else
length += result;
length += sprintf(buffer+length, "}" );
if (delectCharAt != 0) {
buffer[delectCharAt] = ' ';
delectCharAt = 0;
}
}
#endif
// add custom light modes/colors
bool isShow;
const char *lname;
const char *bufptr = buf1;
for (i=1; i < LIGHT_COLOR_OPTIONS; i++) {
if ((lname = get_aqualinkd_light_mode_name(i, &isShow)) != NULL) {
//fprintf(fp,"light_program_%.2d=%s%s\n",i,lname,isShow?" - show":"");
sprintf(buf,"light_program_%.2d", i);
sprintf(buf1,"%s%s",lname,isShow?" - show":"");
//printf("%s %s\n",buf,buf1);
if ((result = json_cfg_element(buffer+length, size-length, buf, &bufptr, CFG_STRING, 0, NULL, true)) <= 0)
return length;
else
length += result;
} else {
break;
}
}
// TODO add custom light modes/colors
// TODO add RSSD LOG FILTERS
// TODO add devmask for reading RS485 external
#else
@ -1206,6 +1316,7 @@ int build_aqualink_config_JSON(char* buffer, int size, struct aqualinkdata *aq_d
else
length += result;
*/
/*
stringptr = getPanelString();
if ((result = json_cfg_element(buffer+length, size-length, CFG_N_panel_type, &stringptr, CFG_STRING, 0, NULL)) <= 0)
return length;
@ -1289,9 +1400,11 @@ int build_aqualink_config_JSON(char* buffer, int size, struct aqualinkdata *aq_d
else
length += result;
*/
#endif // CONGIG_DEV_TEST
// All buttons
for (i = 0; i < aq_data->total_buttons; i++)
{
char prefix[30];
@ -1300,64 +1413,91 @@ int build_aqualink_config_JSON(char* buffer, int size, struct aqualinkdata *aq_d
} else {
sprintf(prefix,"button_%.2d",i+1);
}
//length += sprintf(buffer+length, ",\"%s\":{",prefix );
length += sprintf(buffer+length, ",\"%s\":{ \"default\":\"%s\", ",prefix, aq_data->aqbuttons[i].name );
// The next json_cfg_element() call will add a , at the beginning, so save the next char index so we can delete it later.
delectCharAt = length;
sprintf(buf,"%s_label", prefix);
if ((result = json_cfg_element(buffer+length, size-length, buf, &aq_data->aqbuttons[i].label, CFG_STRING, 0, NULL)) <= 0)
if ((result = json_cfg_element(buffer+length, size-length, buf, &aq_data->aqbuttons[i].label, CFG_STRING, 0, NULL, false)) <= 0) {
LOG(NET_LOG,LOG_ERR, "Config json buffer full in, result truncated! size=%d curently used=%d\n",size,length);
return length;
else
} else
length += result;
if (isVS_PUMP(aq_data->aqbuttons[i].special_mask))
{
if (((pump_detail *)aq_data->aqbuttons[i].special_mask_ptr)->pumpIndex > 0) {
sprintf(buf,"%s_pumpIndex", prefix);
if ((result = json_cfg_element(buffer+length, size-length, buf, &((pump_detail *)aq_data->aqbuttons[i].special_mask_ptr)->pumpIndex, CFG_INT, 0, NULL)) <= 0)
if ((result = json_cfg_element(buffer+length, size-length, buf, &((pump_detail *)aq_data->aqbuttons[i].special_mask_ptr)->pumpIndex, CFG_INT, 0, NULL, false)) <= 0) {
LOG(NET_LOG,LOG_ERR, "Config json buffer full in, result truncated! size=%d curently used=%d\n",size,length);
return length;
else
} else
length += result;
}
if (((pump_detail *)aq_data->aqbuttons[i].special_mask_ptr)->pumpID != NUL) {
sprintf(buf,"%s_pumpID", prefix);
if ((result = json_cfg_element(buffer+length, size-length, buf, &((pump_detail *)aq_data->aqbuttons[i].special_mask_ptr)->pumpID, CFG_HEX, 0, NULL)) <= 0)
if ((result = json_cfg_element(buffer+length, size-length, buf, &((pump_detail *)aq_data->aqbuttons[i].special_mask_ptr)->pumpID, CFG_HEX, 0, NULL, false)) <= 0) {
LOG(NET_LOG,LOG_ERR, "Config json buffer full in, result truncated! size=%d curently used=%d\n",size,length);
return length;
else
} else
length += result;
}
if (((pump_detail *)aq_data->aqbuttons[i].special_mask_ptr)->pumpName[0] != '\0') {
sprintf(buf,"%s_pumpName", prefix);
stringptr = ((pump_detail *)aq_data->aqbuttons[i].special_mask_ptr)->pumpName;
if ((result = json_cfg_element(buffer+length, size-length, buf, &stringptr, CFG_STRING, 0, NULL)) <= 0)
if ((result = json_cfg_element(buffer+length, size-length, buf, &stringptr, CFG_STRING, 0, NULL, false)) <= 0) {
LOG(NET_LOG,LOG_ERR, "Config json buffer full in, result truncated! size=%d curently used=%d\n",size,length);
return length;
else
} else
length += result;
}
if (((pump_detail *)aq_data->aqbuttons[i].special_mask_ptr)->pumpType != PT_UNKNOWN) {
sprintf(buf,"%s_pumpType", prefix);
stringptr = pumpType2String(((pump_detail *)aq_data->aqbuttons[i].special_mask_ptr)->pumpType);
if ((result = json_cfg_element(buffer+length, size-length, buf, &stringptr, CFG_STRING, 0, "[\"JANDY ePUMP\",\"Pentair VS\",\"Pentair VF\"]")) <= 0)
if ((result = json_cfg_element(buffer+length, size-length, buf, &stringptr, CFG_STRING, 0, "[\"JANDY ePUMP\",\"Pentair VS\",\"Pentair VF\"]", false) ) <= 0) {
LOG(NET_LOG,LOG_ERR, "Config json buffer full in, result truncated! size=%d curently used=%d\n",size,length);
return length;
else
} else
length += result;
}
} else if (isPLIGHT(aq_data->aqbuttons[i].special_mask)) {
if (((clight_detail *)aq_data->aqbuttons[i].special_mask_ptr)->lightType > 0) {
sprintf(buf,"%s_lightMode", prefix);
if ((result = json_cfg_element(buffer+length, size-length, buf, &((clight_detail *)aq_data->aqbuttons[i].special_mask_ptr)->lightType, CFG_INT, 0, NULL)) <= 0)
if ((result = json_cfg_element(buffer+length, size-length, buf, &((clight_detail *)aq_data->aqbuttons[i].special_mask_ptr)->lightType, CFG_INT, 0, NULL, false)) <= 0) {
LOG(NET_LOG,LOG_ERR, "Config json buffer full in, result truncated! size=%d curently used=%d\n",size,length);
return length;
else
} else
length += result;
}
} else if ( (isVBUTTON(aq_data->aqbuttons[i].special_mask) && aq_data->aqbuttons[i].rssd_code >= IAQ_ONETOUCH_1 && aq_data->aqbuttons[i].rssd_code <= IAQ_ONETOUCH_6 ) ) {
sprintf(buf,"%s_onetouchID", prefix);
int oID = (aq_data->aqbuttons[i].rssd_code - 15);
if ((result = json_cfg_element(buffer+length, size-length, buf, &oID, CFG_INT, 0, "[\"1\",\"2\",\"3\",\"4\",\"5\",\"6\"]")) <= 0)
if ((result = json_cfg_element(buffer+length, size-length, buf, &oID, CFG_INT, 0, "[\"1\",\"2\",\"3\",\"4\",\"5\",\"6\"]", false)) <= 0) {
LOG(NET_LOG,LOG_ERR, "Config json buffer full in, result truncated! size=%d curently used=%d\n",size,length);
return length;
else
} else
length += result;
}
}
length += sprintf(buffer+length, "}" );
if (delectCharAt != 0) {
buffer[delectCharAt] = ' ';
delectCharAt = 0;
}
}
// Need to add one last element, can be crap. Makes the HTML/JS easier in the loop
if ((result = snprintf(buffer+length, size-length, ",\"version\": \"1.0\"")) < 0 || result >= size-length) {
length += snprintf(buffer+length, size-length, "}");
return length;
} else {
length += result;
}

View File

@ -7,7 +7,9 @@
//#define JSON_BUFFER_SIZE 4000
//#define JSON_STATUS_SIZE 1024
#define JSON_LABEL_SIZE 600
#define JSON_BUFFER_SIZE 5120
//#define JSON_BUFFER_SIZE 8192
//#define JSON_BUFFER_SIZE 10240
#define JSON_BUFFER_SIZE 12288
#define JSON_STATUS_SIZE 2048
#define JSON_SIMULATOR_SIZE 2048

View File

@ -588,7 +588,7 @@ void send_mqtt_led_state_msg(struct mg_connection *nc, char *dev_name, aqledstat
void send_mqtt_swg_state_msg(struct mg_connection *nc, char *dev_name, aqledstate state)
{
//send_mqtt_led_state_msg(nc, dev_name, state, SWG_ON, SWG_OFF);
send_mqtt_led_state_msg(nc, dev_name, state, "2", "0");
send_mqtt_led_state_msg(nc, dev_name, state, MQTT_COOL, MQTT_OFF);
}
void send_mqtt_heater_state_msg(struct mg_connection *nc, char *dev_name, aqledstate state)
@ -787,6 +787,17 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
//send_mqtt_string_msg(nc, FREEZE_PROTECT_ENABELED, MQTT_ON);
}
if (_aqualink_data->chiller_set_point != TEMP_UNKNOWN && _aqualink_data->chiller_set_point != _last_mqtt_aqualinkdata.chiller_set_point) {
_last_mqtt_aqualinkdata.chiller_set_point = _aqualink_data->chiller_set_point;
send_mqtt_setpoint_msg(nc, CHILLER, _aqualink_data->chiller_set_point);
//send_mqtt_string_msg(nc, CHILLER_ENABELED, MQTT_ON);
}
if (_aqualink_data->chiller_state != _last_mqtt_aqualinkdata.chiller_state) {
_last_mqtt_aqualinkdata.chiller_state = _aqualink_data->chiller_state;
//send_mqtt_string_msg(nc, CHILLER, _aqualink_data->chiller_state==ON?MQTT_ON:MQTT_OFF);
send_mqtt_led_state_msg(nc, CHILLER, _aqualink_data->chiller_state, MQTT_COOL, MQTT_OFF);
}
if (_aqualink_data->battery != _last_mqtt_aqualinkdata.battery) {
_last_mqtt_aqualinkdata.battery = _aqualink_data->battery;
send_mqtt_string_msg(nc, BATTERY_STATE, _aqualink_data->battery==OK?MQTT_ON:MQTT_OFF);
@ -1239,14 +1250,17 @@ uriAtype action_URI(request_source from, const char *URI, int uri_length, float
} else if (ri3 != NULL && (strncasecmp(ri2, "setpoint", 8) == 0) && (strncasecmp(ri3, "set", 3) == 0)) {
int val = convertTemp? round(degCtoF(value)) : round(value);
if (strncmp(ri1, BTN_POOL_HTR, strlen(BTN_POOL_HTR)) == 0) {
//create_program_request(from, POOL_HTR_SETOINT, val, 0);
panel_device_request(_aqualink_data, POOL_HTR_SETOINT, 0, val, from);
//create_program_request(from, POOL_HTR_SETPOINT, val, 0);
panel_device_request(_aqualink_data, POOL_HTR_SETPOINT, 0, val, from);
} else if (strncmp(ri1, BTN_SPA_HTR, strlen(BTN_SPA_HTR)) == 0) {
//create_program_request(from, SPA_HTR_SETOINT, val, 0);
panel_device_request(_aqualink_data, SPA_HTR_SETOINT, 0, val, from);
//create_program_request(from, SPA_HTR_SETPOINT, val, 0);
panel_device_request(_aqualink_data, SPA_HTR_SETPOINT, 0, val, from);
} else if (strncmp(ri1, FREEZE_PROTECT, strlen(FREEZE_PROTECT)) == 0) {
//create_program_request(from, FREEZE_SETPOINT, val, 0);
panel_device_request(_aqualink_data, FREEZE_SETPOINT, 0, val, from);
} else if (strncmp(ri1, CHILLER, strlen(CHILLER)) == 0) {
//create_program_request(from, FREEZE_SETPOINT, val, 0);
panel_device_request(_aqualink_data, CHILLER_SETPOINT, 0, val, from);
} else if (strncmp(ri1, "SWG", 3) == 0) { // If we get SWG percent as setpoint message it's from homebridge so use the convert
//int val = round(degCtoF(value));
//int val = convertTemp? round(degCtoF(value)) : round(value);
@ -1892,7 +1906,7 @@ void action_websocket_request(struct mg_connection *nc, struct websocket_message
{
DEBUG_TIMER_START(&tid);
char message[JSON_BUFFER_SIZE];
save_config_js((char *)wm->data, wm->size, message, JSON_BUFFER_SIZE);
save_config_js((char *)wm->data, wm->size, message, JSON_BUFFER_SIZE, _aqualink_data);
DEBUG_TIMER_STOP(tid, NET_LOG, "action_websocket_request() save_config_js took");
ws_send(nc, message);
}
@ -2108,6 +2122,8 @@ void reset_last_mqtt_status()
_last_mqtt_aqualinkdata.service_mode_state = -1;
_last_mqtt_aqualinkdata.pool_htr_set_point = TEMP_REFRESH;
_last_mqtt_aqualinkdata.spa_htr_set_point = TEMP_REFRESH;
_last_mqtt_aqualinkdata.chiller_set_point = TEMP_REFRESH;
_last_mqtt_aqualinkdata.chiller_state = LED_S_UNKNOWN;
_last_mqtt_aqualinkdata.ph = -1;
_last_mqtt_aqualinkdata.orp = -1;
_last_mqtt_aqualinkdata.boost = -1;
@ -2136,11 +2152,13 @@ void reset_last_mqtt_status()
}
void start_mqtt(struct mg_mgr *mgr) {
LOG(NET_LOG,LOG_NOTICE, "Starting MQTT client to %s\n", _aqconfig_.mqtt_server);
if ( _aqconfig_.mqtt_server == NULL ||
( _aqconfig_.mqtt_aq_topic == NULL && _aqconfig_.mqtt_dz_pub_topic == NULL && _aqconfig_.mqtt_dz_sub_topic == NULL) )
return;
LOG(NET_LOG,LOG_NOTICE, "Starting MQTT client to %s\n", _aqconfig_.mqtt_server);
if (mg_connect(mgr, _aqconfig_.mqtt_server, ev_handler) == NULL) {
LOG(NET_LOG,LOG_ERR, "Failed to create MQTT listener to %s\n", _aqconfig_.mqtt_server);
} else {

View File

@ -726,7 +726,7 @@ void *set_aqualink_onetouch_pool_heater_temp( void *ptr )
waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_ONETOUCH_POOL_HEATER_TEMP);
int val = atoi((char*)threadCtrl->thread_args);
val = setpoint_check(POOL_HTR_SETOINT, val, aq_data);
val = setpoint_check(POOL_HTR_SETPOINT, val, aq_data);
LOG(ONET_LOG,LOG_DEBUG, "OneTouch set pool heater temp to %d\n", val);
set_aqualink_onetouch_heater_setpoint(aq_data, true, val);
@ -746,7 +746,7 @@ void *set_aqualink_onetouch_spa_heater_temp( void *ptr )
waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_ONETOUCH_SPA_HEATER_TEMP);
int val = atoi((char*)threadCtrl->thread_args);
val = setpoint_check(SPA_HTR_SETOINT, val, aq_data);
val = setpoint_check(SPA_HTR_SETPOINT, val, aq_data);
LOG(ONET_LOG,LOG_DEBUG, "OneTouch set spa heater temp to %d\n", val);
set_aqualink_onetouch_heater_setpoint(aq_data, false, val);

View File

@ -41,13 +41,14 @@
// static struct aqualinkdata _aqualink_data;
static struct aqualinkdata *_aqualink_data;
static unsigned char _last_packet_type;
static unsigned long _pda_loop_cnt = 0;
static unsigned long _pda_loop_cnt = -0;
static bool _initWithRS = false;
// Each RS message is around 0.25 seconds apart
#define PDA_SLEEP_FOR 120 // 30 seconds
#define PDA_WAKE_FOR 6 // ~1 seconds
//#define PDA_SLEEP_FOR 120 //
#define PDA_SLEEP_FOR 40 //
#define PDA_WAKE_FOR 10 //
#define PDA_IGNORE_NO_STATUS_PAGE 40 // 36 seems to be the status loop
@ -59,14 +60,16 @@ void init_pda(struct aqualinkdata *aqdata)
bool pda_shouldSleep() {
//LOG(PDA_LOG,LOG_DEBUG, "PDA loop count %d, will sleep at %d\n",_pda_loop_cnt,PDA_LOOP_COUNT);
if (_pda_loop_cnt++ < PDA_WAKE_FOR) {
return false;
} else if (_pda_loop_cnt > PDA_WAKE_FOR + PDA_SLEEP_FOR) {
_pda_loop_cnt = 0;
return false;
}
static bool sawHome = false;
static bool sawStatus = false;
static bool inSleep = false;
if (pda_m_type() == PM_HOME)
sawHome = true;
if (pda_m_type() == PM_EQUIPTMENT_STATUS)
sawStatus = true;
// NSF NEED TO CHECK ACTIVE THREADS.
if (_aqualink_data->active_thread.thread_id != 0) {
LOG(PDA_LOG,LOG_DEBUG, "PDA can't sleep as thread %d,%p is active\n",
@ -79,10 +82,47 @@ bool pda_shouldSleep() {
// Last see if there are any open websockets. (don't sleep if the web UI is open)
if ( _aqualink_data->open_websockets > 0 ) {
_pda_loop_cnt = 0;
LOG(PDA_LOG,LOG_DEBUG, "PDA can't sleep as websocket is active\n");
return false;
}
// Only sleep if we are on home page
if (pda_m_type() != PM_HOME /*&& pda_m_type() != PM_BUILDING_HOME*/) {
_pda_loop_cnt = 0;
LOG(PDA_LOG,LOG_DEBUG, "PDA can't sleep not on home page\n");
return false;
}
_pda_loop_cnt++;
if (inSleep == false &&
(sawHome == false || ( sawStatus == false && _pda_loop_cnt <= PDA_IGNORE_NO_STATUS_PAGE))){
LOG(PDA_LOG,LOG_DEBUG, "PDA can't sleep haven't seen both Home and Status pages\n");
return false;
} else if (inSleep == false &&
(sawHome == false || ( sawStatus == false && _pda_loop_cnt > PDA_IGNORE_NO_STATUS_PAGE))){
// We hit the timeout waiting for status page (ie no devices are on), so reset counter
LOG(PDA_LOG,LOG_DEBUG, "PDA Allowing sleep - timeout waiting for Status page (all devices must be off)\n");
_pda_loop_cnt = PDA_WAKE_FOR;
}
//_pda_loop_cnt++;
LOG(PDA_LOG,LOG_DEBUG, "PDA loop count %d, wake between 0->%d, sleep %d->%d\n",_pda_loop_cnt, PDA_WAKE_FOR, PDA_WAKE_FOR, (PDA_WAKE_FOR + PDA_SLEEP_FOR));
if (_pda_loop_cnt < PDA_WAKE_FOR) {
//inSleep == false;
return false;
} else if (_pda_loop_cnt > PDA_WAKE_FOR + PDA_SLEEP_FOR) {
_pda_loop_cnt = 0;
inSleep = false;
return false;
}
sawHome = false;
sawStatus = false;
inSleep = true;
return true;
}

View File

@ -1168,7 +1168,7 @@ void *set_aqualink_PDA_pool_heater_temps( void *ptr )
waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_SET_POOL_HEATER_TEMPS);
int val = atoi((char*)threadCtrl->thread_args);
val = setpoint_check(POOL_HTR_SETOINT, val, aq_data);
val = setpoint_check(POOL_HTR_SETPOINT, val, aq_data);
set_PDA_aqualink_heater_setpoint(aq_data, val, true);
@ -1188,7 +1188,7 @@ void *set_aqualink_PDA_spa_heater_temps( void *ptr )
waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_SET_SPA_HEATER_TEMPS);
int val = atoi((char*)threadCtrl->thread_args);
val = setpoint_check(SPA_HTR_SETOINT, val, aq_data);
val = setpoint_check(SPA_HTR_SETPOINT, val, aq_data);
set_PDA_aqualink_heater_setpoint(aq_data, val, false);

View File

@ -41,7 +41,7 @@
#define SLOG_MAX 80
#define PACKET_MAX 800
#define VERSION "serial_logger V2.7"
#define VERSION "serial_logger V2.8"
/*
typedef enum used {
@ -152,6 +152,7 @@ int serial_logger (int rs_fd, char *port_name, int logLevel, int slogger_packets
#define PC_DOCK " <-- PC Interface (RS485 to RS232)"
#define PDA " <-- PDA Remote"
#define EPUMP " <-- Jandy VSP ePump"
#define EPUMP2 " <-- Jandy VSP (rev W or newer)"
#define CHEM " <-- Chemlink"
#define JXI_HEATER " <-- LXi / LRZ Heater"
@ -169,6 +170,8 @@ int serial_logger (int rs_fd, char *port_name, int logLevel, int slogger_packets
#define P_RWCTL " <-- Remote wireless controller (Screen Logic)"
#define P_CTL " <-- Pool controller (EasyTouch)"
// 0xE0 -> this is pump ID on rev W panel (16 pumps available total) - so maybe last is 0xF0 or 0xEF
const char *getDevice(unsigned char ID) {
if (ID >= 0x00 && ID <= 0x03)
return MASTER;
@ -219,6 +222,9 @@ const char *getDevice(unsigned char ID) {
if (ID >= 0x28 && ID <= 0x2B)
return REM_PWR_CENT;
if (ID >= 0xE0 && ID <= 0xEF) // this could end at 0xF0
return EPUMP2;
//if (ID == 0x08)
// return KEYPAD;

View File

@ -162,7 +162,7 @@ void set_aqualink_rssadapter_aux_state(int buttonIndex, bool turnOn)
*/
void increase_aqualink_rssadapter_pool_setpoint(char *args, struct aqualinkdata *aqdata) {
int val = atoi(args);
val = setpoint_check(POOL_HTR_SETOINT, aqdata->pool_htr_set_point + val, aqdata);
val = setpoint_check(POOL_HTR_SETPOINT, aqdata->pool_htr_set_point + val, aqdata);
LOG(RSSA_LOG,LOG_DEBUG, "Increasing pool heater from %d to %d\n",aqdata->pool_htr_set_point,val);
@ -172,7 +172,7 @@ void increase_aqualink_rssadapter_pool_setpoint(char *args, struct aqualinkdata
void increase_aqualink_rssadapter_spa_setpoint(char *args, struct aqualinkdata *aqdata) {
int val = atoi(args);
val = setpoint_check(SPA_HTR_SETOINT, aqdata->spa_htr_set_point + val, aqdata);
val = setpoint_check(SPA_HTR_SETPOINT, aqdata->spa_htr_set_point + val, aqdata);
LOG(RSSA_LOG,LOG_DEBUG, "Increasing spa heater from %d to %d\n",aqdata->spa_htr_set_point,val);
@ -181,7 +181,7 @@ void increase_aqualink_rssadapter_spa_setpoint(char *args, struct aqualinkdata *
void set_aqualink_rssadapter_pool_setpoint(char *args, struct aqualinkdata *aqdata) {
int val = atoi(args);
val = setpoint_check(POOL_HTR_SETOINT, val, aqdata);
val = setpoint_check(POOL_HTR_SETPOINT, val, aqdata);
LOG(RSSA_LOG,LOG_DEBUG, "Setting pool heater to %d\n",val);
@ -198,7 +198,7 @@ void set_aqualink_rssadapter_pool_setpoint(char *args, struct aqualinkdata *aqda
void set_aqualink_rssadapter_spa_setpoint(char *args, struct aqualinkdata *aqdata) {
int val = atoi(args);
val = setpoint_check(SPA_HTR_SETOINT, val, aqdata);
val = setpoint_check(SPA_HTR_SETPOINT, val, aqdata);
queue_aqualink_rssadapter_setpoint( (isSINGLE_DEV_PANEL?RS_SA_POOLSP2:RS_SA_SPASP), val);
/*

View File

@ -4,4 +4,4 @@
#define AQUALINKD_SHORT_NAME "AqualinkD"
// Use Magor . Minor . Patch
#define AQUALINKD_VERSION "2.5.2 (dev)"
#define AQUALINKD_VERSION "2.6.0 (dev)"

View File

@ -302,13 +302,23 @@
</style>
<script type='text/javascript'>
var _panel_size = 6;
var _panel_set = 0;
var _latestVersionAvailable = 0;
var _rssd_logmask = 0;
const RSSD_MASK_ID = 512; // Must match RSSD_LOG in utils.c
_addedBlankLightProgram = false;
_addedBlankSensor = false;
_addedBlankVirtualButton = false;
var _config = {};
// if loading confighelp.js failes, create a blank variable that it creates.
if (typeof _confighelp === 'undefined') {
var _confighelp = {};
}
function init_collapsible() {
var coll = document.getElementsByClassName("collapsible");
var i;
@ -520,8 +530,6 @@
} catch (Error) { }
}
var _config = {};
function editconfig(caller) {
var msg = {
uri: "config"
@ -530,37 +538,51 @@
}
function saveconfig(caller) {
var json = {
uri: "config/set"
var json_ordered = {
uri: "config/set",
values: {}
};
var values = {}
for (var obj in _config) {
if (_config[obj].value !== undefined) {
//json.values.push('"'+obj+'" : "'+_config[obj].value+'"' );
//json.values.push(obj : _config[obj].value );
values[obj] = _config[obj].value;
//if (_config[obj].value) {
json_ordered.values[obj] = _config[obj].value;
//}
} else if (obj.toString().startsWith("button_") || obj.toString().startsWith("virtual_button_") || obj.toString().startsWith("sensor_") ) {
for (var obj1 in _config[obj]) {
if (obj1.toString() != "advanced") {
//if (_config[obj][obj1].value) {
json_ordered.values[obj1] = _config[obj][obj1].value;
//}
}
}
}
}
json["values"] = values;
//console.log (JSON.stringify(json));
send_command(json);
//console.log(json_ordered);
send_command(json_ordered);
closeconfig();
}
function closeconfig() {
function clearconfigtable() {
const configtable = document.getElementById("config_table");
while (configtable.rows.length > 1) {
configtable.deleteRow(1); // Delete the first data row (index 1)
while (configtable.rows.length > 2) {
configtable.deleteRow(2); // Delete the first data row (index 1)
}
}
function closeconfig() {
clearconfigtable();
document.getElementById("config_options").classList.add("hide");
document.getElementById("config_options_pane").classList.add("hide");
}
function resetConfig(caller) {
clearconfigtable();
populateconfigtable();
}
function showConfig(data) {
var h = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
//console.log("Height="+h);
@ -568,14 +590,557 @@
document.getElementById("config_options").classList.remove("hide");
document.getElementById("config_options_pane").classList.remove("hide");
populateconfigtable(data);
}
function addConfigRow(hideLineSeperator, data, obj, row=-1) {
const configtable = document.getElementById("config_table");
const newRow = configtable.insertRow(row);
/*
if (row==null) {
const newRow = configtable.insertRow();
} else {
newRow = row;
}*/
if (!hideLineSeperator) {
//newRow.style.borderBottom = "1px solid black";
newRow.style.borderTop = "1px solid black";
}
//console.log(data[obj]["valid values"]+" == "+ obj["valid values"]);
//console.log(data[obj].value+" == "+ obj.value);
const cell1 = newRow.insertCell();
const cell2 = newRow.insertCell();
const cell3 = newRow.insertCell();
cell1.textContent = obj.toString();
cell1.style.paddingLeft = "20px";
cell3.textContent = " ";
if (data[obj]["valid values"] !== undefined) {
//console.log(data[obj]["valid values"]);
const input = document.createElement("select");
array = data[obj]["valid values"];
for (var i = 0; i < array.length; i++) {
var option = document.createElement("option");
option.value = array[i];
option.text = array[i];
if (array[i].toUpperCase() == data[obj].value.toUpperCase()) {
option.selected = true;
}
input.setAttribute('key', obj.toString());
input.addEventListener('change', cfgValueChanged);
input.appendChild(option);
}
cell2.appendChild(input);
} else if (data[obj].type == "string") {
const input = document.createElement("input");
input.type = "text";
if (data[obj].value != "(null)") {
input.value = data[obj].value;
}
input.setAttribute('key', obj.toString());
input.addEventListener('change', cfgValueChanged);
cell2.appendChild(input);
} else if (data[obj].type == "int") {
const input = document.createElement("input");
input.type = "number";
if (data[obj].value != -999) {
input.value = data[obj].value;
}
input.size = 4;
input.step = 1;
input.setAttribute('key', obj.toString());
input.addEventListener('change', cfgValueChanged);
cell2.appendChild(input);
} else if (data[obj].type == "float") {
const input = document.createElement("input");
input.type = "number";
input.value = data[obj].value;
input.size = 4;
input.step = 0.01;
input.setAttribute('key', obj.toString());
input.addEventListener('change', cfgValueChanged);
cell2.appendChild(input);
} else if (data[obj].type == "hex") {
const input = document.createElement("input");
input.type = "text";
input.value = data[obj].value;
input.size = 4;
input.setAttribute('key', obj.toString());
input.addEventListener('change', cfgValueChanged);
cell2.appendChild(input);
} else {
cell2.textContent = data[obj].value;
}
}
function addButtonOptionRow(key=0, isVirtual) {
//if ( isVirtual == true && key == 0 ) {
// if (_addedBlankVirtualButton == true) {
// return;
// }
// key = 1
//}
const buttonTypelist = ["pumpIndex", "pumpID", "pumpType", "pumpName", "lightMode"];
const virtButtonTypelist = ["pumpIndex", "pumpID", "pumpType", "pumpName", "onetouchID"];
const configtable = document.getElementById("config_table");
const newRow = configtable.insertRow();
var cell1 = newRow.insertCell();
var cell2 = newRow.insertCell();
var cell3 = newRow.insertCell();
cell1.style.paddingLeft = "20px";
cell1.style.fontSize = "small";
cell2.style.fontSize = "small";
cell3.style.fontSize = "small";
var blist = isVirtual?virtButtonTypelist:buttonTypelist;
var options = "";
for (const index in blist ) {
if (!_config[key][key+"_"+blist[index]]) {
options=options+'<option value="'+blist[index]+'">'+blist[index]+'</option>';
}
}
cell1.innerHTML = 'Add <select id="add_button_type"><option value="" selected="true"></option>'+options+'</select>';
cell1.setAttribute('button_key', key);
cell1.addEventListener('change', cfgAddButtonOption);
//if ( isVirtual == true ){
// _addedBlankVirtualButton = true;
//}
}
function addLightProgramRow(num=0, row=-1){
//console.log("addLightProgramRow( "+num+" )");
if ( num == 0 ) {
if (_addedBlankLightProgram == true) {
return;
}
//num = 1
}
if (num >= _config.max_light_programs) {
console.log("MAX LIGHTS REACHED");
_addedBlankLightProgram = true;
return;
}
const configtable = document.getElementById("config_table");
if (num == 0) {
var newRow = configtable.insertRow();
var cell1 = newRow.insertCell();
cell1.colSpan = 3;
cell1.style.fontSize = "small";
cell1.style.paddingLeft = "5px";
cell1.innerHTML = "Custom Light Programs";
cell1.style.borderTop = "1px solid black";
}
var newRow = configtable.insertRow(row);
var cell1 = newRow.insertCell();
var cell2 = newRow.insertCell();
var cell3 = newRow.insertCell();
cell1.style.paddingLeft = "20px";
key = 'light_program_'+String(num+1).padStart(2, '0')
cell1.innerHTML = 'Add '+key;
cell1.setAttribute('label', key);
const input = document.createElement("input");
input.type = "text";
//input.setAttribute('light_program_key', key);
input.setAttribute('key', key);
input.addEventListener('change', cfgAddLightProgram);
//input.addEventListener('change', cfgValueUnhide);
cell2.appendChild(input);
//cell1.setAttribute('button_key', key);
//cell1.addEventListener('change', cfgAddButtonOption);
_addedBlankLightProgram = true;
}
function addVirtualButtonRow(num=0, row=-1){
if ( num == 0 ) {
if (_addedBlankVirtualButton == true) {
return;
}
}
num++;
if (num >= _config.max_vbuttons) {
console.log("MAX VIRTUAL BUTTONS REACHED");
_addedBlankVirtualButton = true;
return;
}
const virtButtonTypelist = ["pumpIndex", "pumpID", "pumpType", "pumpName", "onetouchID"];
const configtable = document.getElementById("config_table");
//console.log (data);
_config = data;
var lastBtnNum = 1; // Use 1 so only hits if on button_02
var vBtnCnt = 0;
vbname="virtual_button_"+String(num).padStart(2, '0');
vblabel=vbname+"_label";
newRow = configtable.insertRow();
var cell1 = newRow.insertCell();
cell1.colSpan = 3;
cell1.style.fontSize = "small";
cell1.style.paddingLeft = "5px";
cell1.innerHTML = vbname + " ( <b>Aux_V"+num+"</b> )";
cell1.style.borderTop = "1px solid black";
newRow = configtable.insertRow();
//newRow.style.borderTop = "1px solid black";
var cell1 = newRow.insertCell();
var cell2 = newRow.insertCell();
var cell3 = newRow.insertCell();
cell1.style.paddingLeft = "20px";
cell1.textContent = vblabel;
const input = document.createElement("input");
input.type = "text";
input.setAttribute('key', vblabel);
//input.addEventListener('change', cfgValueChanged);
input.addEventListener('change', cfgValueUnhide);
cell2.appendChild(input);
///////////
newRow = configtable.insertRow();
//newRow.style.hide = true;
newRow.style.display = 'none';
cell1 = newRow.insertCell();
cell2 = newRow.insertCell();
cell3 = newRow.insertCell();
cell1.style.paddingLeft = "20px";
cell1.style.fontSize = "small";
cell2.style.fontSize = "small";
cell3.style.fontSize = "small";
var options = "";
for (const index in virtButtonTypelist ) {
options=options+'<option value="'+virtButtonTypelist[index]+'">'+virtButtonTypelist[index]+'</option>';
}
cell1.innerHTML = 'Add <select id="add_button_type"><option value="" selected="true"></option>'+options+'</select>';
cell1.setAttribute('button_key', vbname);
cell1.addEventListener('change', cfgAddButtonOption);
_addedBlankVirtualButton = true;
}
function cfgValueUnhide(event) {
var key = event.srcElement.getAttribute('key');
var value = event.srcElement.value;
if (key.startsWith("virtual_button_") ) {
var js = {};
var jso = {};
js.value=value;
js.type="string";
js.advanced="no";
jso[key] = js;
_config[key.slice(0, 17)] = jso;
console.log(_config);
}
console.log("cfgValueUnhide key="+key+" value="+value);
event.target.removeEventListener('change', cfgValueUnhide);
event.target.addEventListener('change', cfgValueChanged);
// Show the next table row.
event.target.parentNode.parentNode.nextSibling.style.display = '';
if (key.startsWith("sensor_") ) {
event.srcElement.parentNode.parentNode.childNodes[0].innerHTML = key;
event.target.parentNode.parentNode.nextSibling.nextSibling.style.display = '';
}
for (var obj in data) {
var isBtn = false;
console.log(event);
}
function addSensorRow(num=0, row=-1){
if ( num == 0 ) {
if (_addedBlankSensor == true) {
return;
}
//num = 1
}
if (num >= _config.max_sensors) {
console.log("MAX SENSORS REACHED");
_addedBlankSensor = true;
return;
}
const configtable = document.getElementById("config_table");
if (num == 0) {
var newRow = configtable.insertRow();
var cell1 = newRow.insertCell();
cell1.colSpan = 3;
cell1.style.fontSize = "small";
cell1.style.paddingLeft = "5px";
cell1.innerHTML = "Sensors";
cell1.style.borderTop = "1px solid black";
}
var newRow = configtable.insertRow(row);
if (num != 0) {
newRow.style.borderTop = "1px solid black";
}
key = 'sensor_'+String(num+1).padStart(2, '0');
// Add sensor_xx_path row
var cell1 = newRow.insertCell();
var cell2 = newRow.insertCell();
//var cell3 = newRow.insertCell();
cell1.style.paddingLeft = "20px";
cell1.innerHTML = "<small>Add</small> "+key+"_path";
var input = document.createElement("input");
input.type = "text";
input.setAttribute('key', key+"_path");
input.setAttribute('root_key', key);
input.id = key+"_path";
input.addEventListener('change', cfgAddSensor);
input.addEventListener('change', cfgValueUnhide);
cell2.appendChild(input);
// Add sensor_xx_label row
if (row != -1){row++};
newRow = configtable.insertRow(row);
newRow.style.display = 'none';
cell1 = newRow.insertCell();
cell2 = newRow.insertCell();
//cell3 = newRow.insertCell();
cell1.style.paddingLeft = "20px";
cell1.innerHTML = key+"_label";
input = document.createElement("input");
input.type = "text";
input.setAttribute('key', key+"_label");
input.setAttribute('root_key', key);
input.id = key+"_label";
input.addEventListener('change', cfgAddSensor);
cell2.appendChild(input);
// Add sensor_xx_factor row
if (row != -1){row++};
newRow = configtable.insertRow(row);
newRow.style.display = 'none';
cell1 = newRow.insertCell();
cell2 = newRow.insertCell();
//cell3 = newRow.insertCell();
cell1.style.paddingLeft = "20px";
cell1.innerHTML = key+"_factor";
input = document.createElement("input");
input.type = "number";
input.size = 4;
input.step = 0.01;
input.setAttribute('key', key+"_factor");
input.setAttribute('root_key', key);
input.id = key+"_factor";
input.addEventListener('change', cfgAddSensor);
cell2.appendChild(input);
_addedBlankSensor = true;
}
function cfgAddButtonOption(event) {
var type = event.srcElement.value;
var key = event.srcElement.parentNode.getAttribute('button_key');
for (var obj in _config) {
if (obj.toString() == key) {
var js = {};
js.advanced = "no";
switch(type) {
case "pumpIndex":
case "lightMode":
case "onetouchID":
js.type = "int";
break;
case "pumpID":
js.type = "hex";
break;
case "pumpType":
js.type = "string";
let validvalues = ["", "JANDY ePUMP", "Pentair VS", "Pentair VF"];
js['valid values'] = validvalues;
break;
case "pumpName":
js.type = "string";
break;
}
js.value = "";
_config[obj][key+"_"+type] = js;
break;
}
}
const configtable = document.getElementById("config_table");
for (let i = 0; i < configtable.rows.length; i++) {
if(configtable.rows[i].cells[0].getAttribute('button_key') !== null) {
if(configtable.rows[i].cells[0].getAttribute('button_key') == key) {
addConfigRow(true, _config[obj], key+"_"+type, i);
break;
}
}
}
}
function cfgAddLightProgram(event){
var value = event.srcElement.value;
var key = event.srcElement.getAttribute('key');
//console.log(key + " = "+ value);
event.srcElement.parentNode.parentNode.childNodes[0].innerHTML = key;
//event.target.innerHTML = key;
//event.srcElement.parentNode.parentNode.childNodes[1].childNodes[0].removeEventListener('change', cfgAddLightProgram);
//event.srcElement.parentNode.parentNode.childNodes[1].childNodes[0].addEventListener('change', cfgValueChanged);
event.target.removeEventListener('change', cfgAddLightProgram);
event.target.addEventListener('change', cfgValueChanged);
var js = {};
js.advanced = "yes";
js.type = "string";
js.value = value;
_config[key] = js;
//console.log(_config);
const configtable = document.getElementById("config_table");
for (let i = 0; i < configtable.rows.length; i++) {
if(configtable.rows[i].cells[0].getAttribute('label') !== null) {
//console.log(configtable.rows[i].cells[0].innerHTML+" test= "+key);
if(configtable.rows[i].cells[0].getAttribute('label') == key) {
addLightProgramRow(parseInt(key.match(/[0-9]+/)), i+1);
break;
}
}
}
}
function cfgAddSensor(event){
var value = event.srcElement.value;
var key = event.srcElement.getAttribute('key');
var root_key = event.srcElement.getAttribute('root_key');
console.log(key + " = "+ value);
//event.srcElement.parentNode.parentNode.childNodes[1].childNodes[0].removeEventListener('change', cfgAddLightProgram);
//event.srcElement.parentNode.parentNode.childNodes[1].childNodes[0].addEventListener('change', cfgValueChanged);
event.target.removeEventListener('change', cfgAddSensor);
event.target.addEventListener('change', cfgValueChanged);
var js = {};
js.advanced = "yes";
if (key.endsWith("_factor")) {
js.type = "float";
} else {
js.type = "string";
}
js.value = value;
//_config[key] = js;
if (_config[root_key]) {
console.log("Json exists");
_config[root_key][key] = js;
} else {
console.log("Json not exists");
jsobj={};
jsobj.advanced="yes";
jsobj[key] = js;
_config[root_key] = jsobj
}
console.log(_config);
// If all 3 have an entry, add another sensor
if ( (document.getElementById(root_key+"_path").value) &&
(document.getElementById(root_key+"_label").value) &&
(document.getElementById(root_key+"_factor").value)) {
const configtable = document.getElementById("config_table");
for (let i = 0; i < configtable.rows.length; i++) {
try{
console.log(configtable.rows[i].cells[0].innerHTML);
if(configtable.rows[i].cells[0].innerHTML == key) {
addSensorRow( parseInt(key.match(/[0-9]+/)), i+1 )
break;
}
} catch (e){}
}
}
}
function populateconfigtable(data=null) {
_addedBlankLightProgram = false;
_addedBlankSensor = false;
_addedBlankVirtualButton = false;
const configtable = document.getElementById("config_table");
showAdvanced = document.getElementById("show_advanced").checked;
//console.log (data);
if (data != null)
_config = data;
var lastKey = "";
//var lastBtnNum = 0;
//var lastSenNum = 0;
//var vBtnCnt = 0;
var maxSensor = 0;
var maxLightProgramName = 0;
for (var obj in _config) {
var hideLineSeperator = false;
if (!showAdvanced && _config[obj].advanced == "yes") {
continue;
}
// Basic groupings by first letter change
if ( lastKey.charAt(0) == obj.toString().charAt(0) ) {
hideLineSeperator = true;
}
// More advanced groupings
if (obj.toString().includes("device_id") && lastKey.includes("device_id") ) {
hideLineSeperator = true;
} else if (obj.toString().includes("light_program_") ) {
hideLineSeperator = true;
if (obj.toString().includes("light_program_01") ) {
hideLineSeperator = false;
}
const num = parseInt(obj.toString().match(/[0-9]+/));
if (num > maxLightProgramName)
maxLightProgramName = num;
}
if (_confighelp[obj.toString()] !== undefined) {
//console.log("FOUND HELP");
//console.log(_confighelp[obj.toString()]);
@ -584,218 +1149,84 @@
cell1.colSpan = 3;
cell1.style.fontSize = "small";
cell1.textContent=_confighelp[obj.toString()];
}
if (obj.toString().includes("button")) {
isBtn = true;
const num = parseInt(obj.toString().match(/[0-9]+/));
//console.log("lastnum="+lastBtnNum+" num="+num);
if (num && num != lastBtnNum) {
const newRow = configtable.insertRow();
newRow.style.borderBottom = "1px solid black";
/*
const cell1 = newRow.insertCell();
const cell2 = newRow.insertCell();
const cell3 = newRow.insertCell();
//cell1.colSpan = 3;
cell1.style.fontSize = "small";
if (obj.toString().includes("virtual")) {
vBtnCnt++;
}
if (vBtnCnt > 1) {
cell1.textContent = "Add Virtual Button";
} else {
cell1.style.paddingLeft = "20px";
cell1.textContent = "Add Button_"+lastBtnNum+"_??????";
cell2.innerHTML = '<select key="button_'+lastBtnNum+'_ADD"><option value="" selected="true"></option><option value="pumpIndex">pumpIndex</option><option value="pumpID">pumpID</option><option value="pumpType">pumpType</option><option value="pumpName">pumpName</option><option value="lightMode">lightMode</option></select>';
cell2.addEventListener('change', cfgButtonAdd);
}*/
lastBtnNum = num;
if (! hideLineSeperator) {
//newRow.style.borderBottom = "1px solid black";
newRow.style.borderTop = "1px solid black";
hideLineSeperator = true;
}
}
if (data[obj].value !== undefined) {
//console.log(obj.toString() + " = " + data[obj].value);
const newRow = configtable.insertRow();
if (! isBtn) {
newRow.style.borderBottom = "1px solid black";
}
const cell1 = newRow.insertCell();
const cell2 = newRow.insertCell();
const cell3 = newRow.insertCell();
cell1.textContent = obj.toString();
cell1.style.paddingLeft = "20px";
cell3.textContent = " ";
if (data[obj]["valid values"] !== undefined) {
//console.log(data[obj]["valid values"]);
const input = document.createElement("select");
array = data[obj]["valid values"];
for (var i = 0; i < array.length; i++) {
var option = document.createElement("option");
option.value = array[i];
option.text = array[i];
if (array[i] == data[obj].value) {
option.selected = true;
if (lastKey.startsWith("light_program_") && ! obj.toString().startsWith("light_program_") ) {
addLightProgramRow(parseInt(lastKey.match(/[0-9]+/)));
} else if (lastKey.startsWith("sensor_") && ! obj.toString().startsWith("sensor_") ) {
addSensorRow(parseInt(lastKey.match(/[0-9]+/)));
} else if (lastKey.startsWith("virtual_button_") && ! obj.toString().startsWith("virtual_button_") ) {
addVirtualButtonRow(parseInt(lastKey.match(/[0-9]+/)));
}
if (_config[obj].value !== undefined) {
addConfigRow(hideLineSeperator, _config, obj);
} else if (obj.toString().startsWith("button_") || obj.toString().startsWith("virtual_button_") ) {
var hide=false;
for (var obj1 in _config[obj]) {
if (obj1.toString() == "default") {
const newRow = configtable.insertRow();
const cell1 = newRow.insertCell();
cell1.colSpan = 3;
cell1.style.fontSize = "small";
cell1.style.paddingLeft = "5px";
cell1.innerHTML = obj.toString() + " ( <b>" +_config[obj]["default"] + "</b> )";
cell1.style.borderTop = "1px solid black";
hide=true;
} else if (obj1.toString() != "advanced") {
addConfigRow(hide, _config[obj], obj1);
hide=true;
}
input.setAttribute('key', obj.toString());
input.addEventListener('change', cfgValueChanged);
input.appendChild(option);
}
cell2.appendChild(input);
} else if (data[obj].type == "string") {
const input = document.createElement("input");
input.type = "text";
input.value = data[obj].value;
input.setAttribute('key', obj.toString());
input.addEventListener('change', cfgValueChanged);
cell2.appendChild(input);
} else if (data[obj].type == "int") {
const input = document.createElement("input");
input.type = "number";
input.value = data[obj].value;
input.size = 4;
input.setAttribute('key', obj.toString());
input.addEventListener('change', cfgValueChanged);
cell2.appendChild(input);
} else if (data[obj].type == "hex") {
const input = document.createElement("input");
input.type = "text";
input.value = data[obj].value;
input.size = 4;
input.setAttribute('key', obj.toString());
input.addEventListener('change', cfgValueChanged);
cell2.appendChild(input);
} else if (data[obj].type == "float") {
console.log("NEED TO IMPLIMENT float about line 669");
cell2.textContent = data[obj].value;
} else {
cell2.textContent = data[obj].value;
}
}
}
//var jsonString= JSON.stringify(_config);
//console.log (jsonString);
var newRow = configtable.insertRow();
newRow.style.borderTop = "1px solid black";
var cell1 = newRow.insertCell();
cell1.colSpan = 3;
cell1.textContent = "Add Button or Virtual Button";
newRow = configtable.insertRow();
newRow.style.borderBottom = "1px solid black";
cell1 = newRow.insertCell();
var cell2 = newRow.insertCell();
var cell3 = newRow.insertCell();
cell1.innerHTML = '<select id="button_type_ADD"><option value="" selected="true"></option><option value="button">button</option><option value="virtual_button">virtual button</option></select> \
<input disabled id="button_number_ADD" type="number" min="1" max="30" size="2"></input> \
<select disabled id="button_option_ADD"><option value="" selected="true">';
cell1.addEventListener('change', cfgEnbleButtonOptions);
//cell2.innerHTML = '<input disabled id="button_value_ADD">';
cell2.innerHTML = '<div id="button_valuetype_ADD"></div>';
cell2.addEventListener('change', cfgEnbleButtonOptions);
cell3.innerHTML = '<input id="addButton" type="submit" value="Add" onClick="cfgButtonAdd()"></input>';
//cell3.addEventListener('change', cfgEnbleButtonOptions);
disablebutton("addButton");
}
function cfgEnbleButtonOptions(event) {
btnType = document.getElementById("button_type_ADD");
btnNum = document.getElementById("button_number_ADD");
btnOption = document.getElementById("button_option_ADD");
btnValueType = document.getElementById("button_valuetype_ADD");
if (event.srcElement.id == "button_type_ADD"){
btnNum.disabled = false;
if (event.srcElement.value == "button") {
btnNum.max=20;
btnOption.innerHTML = '<option value="" selected="true"></option><option value="pumpIndex">pumpIndex</option><option value="pumpID">pumpID</option><option value="pumpType">pumpType</option><option value="pumpName">pumpName</option><option value="lightMode">lightMode</option>';
} else if (event.srcElement.value == "virtual_button") {
btnNum.max=10;
btnOption.innerHTML = '<option value="" selected="true"></option><option value="pumpIndex">pumpIndex</option><option value="pumpID">pumpID</option><option value="pumpType">pumpType</option><option value="pumpName">pumpName</option><option value="onetouchID">onetouchID</option>';
}
} else if (event.srcElement.id == "button_number_ADD"){
btnOption.disabled = false;
} else if (event.srcElement.id == "button_option_ADD"){
//btnValue.disabled = false;
//console.log(event.srcElement.value);
switch(event.srcElement.value) {
case "pumpIndex":
//btnValueType.innerHTML = '<select id="button_value_ADD"><option value="1">1</option><option value="2">2</option><option value="3">3</option><option value="4">4</option></select>';
btnValueType.innerHTML = '<input id="button_value_ADD" type="number" min="1" max="4" size="1"></input>';
break;
case "pumpID":
btnValueType.innerHTML = '<input id="button_value_ADD" type="text" size="4" value="0x60"></imput>';
break;
case "pumpType":
btnValueType.innerHTML = '<select id="button_value_ADD"><option value="JANDY ePUMP">JANDY ePUMP</option><option value="Pentair VS">Pentair VS</option><option value="Pentair VF">Pentair VF</option></select>';
break;
case "pumpName":
btnValueType.innerHTML = '<input id="button_value_ADD" type="text"></imput>';
break;
case "lightMode":
btnValueType.innerHTML = '<input id="button_value_ADD" type="number" min="1" max="11" size="2"></input>';
break;
case "onetouchID":
btnValueType.innerHTML = '<input id="button_value_ADD" type="number" min="1" max="6" size="2"></input>';
break;
addButtonOptionRow(obj.toString(), !obj.toString().startsWith("button_"));
} else if (obj.toString().startsWith("sensor_")) {
var hide=false;
for (var obj1 in _config[obj]) {
if (obj1.toString() != "advanced") {
addConfigRow(hide, _config[obj], obj1);
hide=true;
}
}
} else {
//console.log("UNDIFINED " + obj.toString() + " = " + _config[obj].value);
}
lastKey = obj.toString();
}
// Get this here since the previous statment can change it.
btnValue = document.getElementById("button_value_ADD");
if (btnType.value != "" && btnNum.value != "" && btnOption.value != "" && btnValue && btnValue.value != "") {
enablebutton("addButton");
} else {
disablebutton("addButton");
if (showAdvanced) {
addLightProgramRow();
addSensorRow();
}
addVirtualButtonRow();
}
function cfgValueChanged(event) {
//console.log("Config set "+event.srcElement.getAttribute('key')+" to "+event.srcElement.value)
_config[event.srcElement.getAttribute('key')].value = event.srcElement.value;
}
var key = event.srcElement.getAttribute('key');
var value = event.srcElement.value;
function cfgButtonAdd(){
//console.log(document.getElementById("button_type_ADD").value);
//console.log(document.getElementById("button_number_ADD").value);
//console.log(document.getElementById("button_option_ADD").value);
//console.log(document.getElementById("button_value_ADD").value);
console.log("Config set "+key+" to "+value)
var button = document.getElementById("button_type_ADD").value + "_" + ("0" + document.getElementById("button_number_ADD").value).slice (-2);
var buttonName = button+"_"+document.getElementById("button_option_ADD").value;
var buttonValue = document.getElementById("button_value_ADD").value;
//console.log("Looking for "+button);
const configtable = document.getElementById("config_table");
var found=false;
for (let i = 0; i < configtable.rows.length; i++) {
try{
if ( configtable.rows[i].cells[0].childNodes[0].nodeValue.includes(button) ) {
//console.log("**** FOUND *****");
found=true;
} else if (found == true) {
//console.log("** ADD HERE **");
const newRow = configtable.insertRow(i);
newRow.classList.add("config_options_added");
var cell1 = newRow.insertCell();
var cell2 = newRow.insertCell();
var cell3 = newRow.insertCell();
newRow.style.borderBottom = "1px solid black";
cell1.style.paddingLeft = "20px";
cell1.innerHTML = buttonName;
cell2.innerHTML = buttonValue;
//cell3.innerHTML = '<input id="addButton" type="submit" value="Delete" onClick="cfgButtonDelete()"></input>';
var js = {};
js.value = buttonValue
_config[buttonName] = js;
//console.log(_config);
break;
}
//console.log(configtable.rows[i].cells[0].childNodes[0].nodeValue);
} catch (e){}
try {
_config[key].value = value;
} catch (exception) {
console.log("Error setting cfg");
//console.error(exception);
if (key.startsWith("button_") ) {
_config[key.slice(0, 9)][key].value = value
} else if (key.startsWith("virtual_button_") ) {
_config[key.slice(0, 17)][key].value = value
} else if (key.startsWith("sensor_") ) {
_config[key.slice(0, 9)][key].value = value
}
}
console.log(_config);
}
function update_status(data) {
@ -1259,12 +1690,21 @@
</th>
</tr>
<tr>
<td align="center" colspan="3">
<label>
<input class="toggle-checkbox" type="checkbox" id="show_advanced" onclick="resetConfig(this);">
<div class="toggle-switch"></div>
<span class="toggle-label">Show&nbsp;Advanced&nbsp;Options</span>
</label>
</td>
<!--
<td align="right" width="50%">
Key
</td>
<td align="left" width="50%">
Value
</td>
-->
</tr>
</tbody>
</table>

View File

@ -1,3 +1,3 @@
let _confighelp = {};
var _confighelp = {};
_confighelp["panel_type"]="Your RS panel type & size. ie 4, 6, 8, 12 or 16 relates to RS4, RS6, RS8, RS12 or RS16. Must be in format XX-N ???? (XX=RS or PD, N=Circuits, ????=Combo or Only or Dual)";
_confighelp["device_id"]="The id of the AqualinkD to use. Valid RS ID's are 0x0a 0x0b 0x09 0x08. If your panel is a PDA only model then PDA device ID is 0x60. This HAS to be an ID that's not currently used by another device (like keypad), run serial_logger if unsure";