mirror of https://github.com/sfeakes/AqualinkD.git
2.6.0 (dev)
parent
25bcdce8ff
commit
2e6140aa67
2
Makefile
2
Makefile
|
@ -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 =
|
||||
|
|
|
@ -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.
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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_
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
669
source/config.c
669
source/config.c
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
*/
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
64
source/pda.c
64
source/pda.c
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
/*
|
||||
|
|
|
@ -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)"
|
||||
|
|
|
@ -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 Advanced Options</span>
|
||||
</label>
|
||||
</td>
|
||||
<!--
|
||||
<td align="right" width="50%">
|
||||
Key
|
||||
</td>
|
||||
<td align="left" width="50%">
|
||||
Value
|
||||
</td>
|
||||
-->
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -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";
|
||||
|
|
Loading…
Reference in New Issue