Dev update

master
sfeakes 2025-01-28 17:45:53 -06:00
parent 3b79c83d02
commit 7973213889
36 changed files with 2358 additions and 296 deletions

View File

@ -83,7 +83,7 @@ endif
# Main source files
SRCS = aqualinkd.c utils.c config.c aq_serial.c aq_panel.c aq_programmer.c allbutton.c allbutton_aq_programmer.c net_services.c json_messages.c rs_msg_utils.c\
devices_jandy.c packetLogger.c devices_pentair.c color_lights.c serialadapter.c aq_timer.c aq_scheduler.c web_config.c\
serial_logger.c mongoose.c hassio.c simulator.c timespec_subtract.c
serial_logger.c mongoose.c hassio.c simulator.c sensors.c timespec_subtract.c
AQ_FLAGS =
@ -227,6 +227,10 @@ quick:
sudo docker run -it --mount type=bind,source=./,target=/build aqualinkd-releasebin make quickbuild
$(info Binaries for release have been built)
debugbinaries:
sudo docker run -it --mount type=bind,source=./,target=/build aqualinkd-releasebin make debugbuild
$(info Binaries for release have been built)
# This is run inside container Dockerfile.releaseBinariies (aqualinkd-releasebin)
buildrelease: clean armhf arm64
$(shell cd release && ln -s ./aqualinkd-armhf ./aqualinkd && ln -s ./serial_logger-armhf ./serial_logger)
@ -235,6 +239,10 @@ buildrelease: clean armhf arm64
quickbuild: armhf arm64
$(shell cd release && [ ! -f "./aqualinkd-armhf" ] && ln -s ./aqualinkd-armhf ./aqualinkd && ln -s ./serial_logger-armhf ./serial_logger)
debugbuild: CFLAGS = $(DFLAGS)
debugbuild: armhf arm64
$(shell cd release && [ ! -f "./aqualinkd-armhf" ] && ln -s ./aqualinkd-armhf ./aqualinkd && ln -s ./serial_logger-armhf ./serial_logger)
# Rules to pass to make.
all: $(MAIN) $(SLOG)

View File

@ -132,6 +132,9 @@ NEED TO FIX FOR THIS RELEASE.
# Updates in 2.5.1
* 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).
# 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.

View File

@ -57,7 +57,7 @@ if echo $DOCKER_TAGS | grep -q $VERSION; then
fi
fi
# Login first
# Login first Run as root not with sudo on my build machine.
# cat ~/.docker.token | docker login --username sfeakes --password-stdin
echo "Building Docker container for $IMAGE using branch $VERSION"

View File

@ -4,6 +4,13 @@
PROCESSNAME=aqualinkd
MYPID=`pidof $PROCESSNAME`
if [ $? -ne 0 ]; then
MYPID=$(pidof "$PROCESSNAME-arm64")
if [ $? -ne 0 ]; then
MYPID=$(pidof "$PROCESSNAME-armhf")
fi
fi
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root"
exit 1

View File

@ -4,6 +4,13 @@
PROCESSNAME=aqualinkd
MYPID=`pidof $PROCESSNAME`
if [ $? -ne 0 ]; then
MYPID=$(pidof "$PROCESSNAME-arm64")
if [ $? -ne 0 ]; then
MYPID=$(pidof "$PROCESSNAME-armhf")
fi
fi
#if [[ $EUID -ne 0 ]]; then
# echo "This script must be run as root"
# exit 1

Binary file not shown.

Binary file not shown.

View File

@ -132,7 +132,8 @@ report_zero_spa_temp = yes
#mqtt_dz_pub_topic = domoticz/in
#mqtt_dz_sub_topic = domoticz/out
#mqtt_aq_topic = aqualinkd
#mqtt_hassio_discover_topic = homeassistant
#mqtt_ha_discover_topic = homeassistant
#mqtt_ha_discover_use_mac = no
# MQTT will only post updated information, this option AqualinkD will re-post all MQTT information every ~5 minutes.
#mqtt_timed_update = no
@ -151,6 +152,7 @@ report_zero_spa_temp = yes
# dead.
# ~40 and we will be replying too slowley, so keep below that.
# 10~20 is about what most device reply in. But 0-4 works well.
# Recomended to set to at least 4 for PDA panels.
rs485_frame_delay = 0
# Get rid of the startup warning message about no low latency. BETTER option is to buy a better adapter.
@ -322,3 +324,18 @@ button_12_label=Solar Heater
#virtual_button_03_label = OneTouch 4
#virtual_button_04_label = OneTouch 5
#virtual_button_05_label = OneTouch 6
# Sensors.
# All Raspberry Pi's (and most other SBC) report CPU temp. Most report to /sys/class/thermal/thermal_zone0/temp,
# you can monitor these and AqualinkD will post the information to MQTT.
# These will depend a lot on the board & OS you are running.
# the "factor" is the number the sensor is multiplied by to get an accurate result. example below is (millidegrees Celsius to Celsius)
#sensor_01_path = /sys/class/thermal/thermal_zone0/temp
#sensor_01_label = CPU
#sensor_01_factor = 0.001
# Boards like Radxa Zero3 have others sensors like below.
#sensor_02_path = /sys/class/thermal/thermal_zone1/temp
#sensor_02_label = GPU
#sensor_02_factor = 0.001

Binary file not shown.

Binary file not shown.

View File

@ -58,6 +58,8 @@
#define LIGHT_PROGRAM_TOPIC "/program"
#define LIGHT_DIMMER_VALUE_TOPIC "/brightness"
#define SENSOR_TOPIC "Sensor"
/*
#define AIR_TEMPERATURE "Air"
#define POOL_TEMPERATURE "Pool_Water"

View File

@ -185,13 +185,21 @@ setPanel("RS-8 Combo");
char _panelString[60];
void setPanelString()
{
snprintf(_panelString, sizeof(_panelString), "%s%s-%d %s%s%s",
snprintf(_panelString, 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 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":"");
*/
}
const char* getPanelString()
{
@ -407,6 +415,10 @@ int convertPumpPercentToSpeed(pump_detail *pump, int pValue) {
// 4,6,8,10,12,14
void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo, bool dual) {
// Since we are resetting all special buttons here (.special_mask), we need to clean out the lights and pumps.
aqdata->num_lights = 0;
aqdata->num_pumps = 0;
int index = 0;
aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[7-1];
aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;

View File

@ -107,10 +107,16 @@ int PANEL_SIZE();
*/
// If we need to increase virtual buttons, then increase below.
// NEED TO FIX, IF WE CHANGE TO ANOTHING OTHER THAN 0 CORE DUMP (we also had "panel_type = RS-16 Combo" in config)
#define VIRTUAL_BUTTONS 0
#ifndef AQ_RS16
#define TOTAL_BUTTONS 12
#define TOTAL_BUTTONS 12+VIRTUAL_BUTTONS
#else
#define TOTAL_BUTTONS 20
#define TOTAL_BUTTONS 20+VIRTUAL_BUTTONS // Biggest jandy panel
// This needs to be called AFTER and as well as initButtons
void initButtons_RS16(struct aqualinkdata *aqdata);
#endif

View File

@ -212,7 +212,7 @@ int build_schedules_js(char* buffer, int size)
//char *regexString="([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s.*(\\/api\\/.*\\/set).* value=([0-9]+).*";
//char *regexString="([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s.*(\\/api\\/.*\\/set).* value=([0-9]+).*";
char *regexString="(#{0,1})([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s.*(\\/api\\/.*\\/set).* value=([0-9]+).*";
const char *regexString="(#{0,1})([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s.*(\\/api\\/.*\\/set).* value=([0-9]+).*";
//char *regexString="([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s.*(/api/.*/set).*value=([0-9]+).*";

View File

@ -7,6 +7,7 @@
#include <stdint.h>
#include "aq_serial.h"
#include "aq_programmer.h"
#include "sensors.h"
//#include "aq_panel.h" // Moved to later in file to overcome circular dependancy. (crappy I know)
@ -65,6 +66,7 @@ bool checkAqualinkTime(); // Only need to externalise this for PDA
#define MAX_PUMPS 4
#define MAX_LIGHTS 4
#define MAX_SENSORS 4
bool isVirtualButtonEnabled();
@ -308,6 +310,9 @@ struct aqualinkdata
volatile bool updated;
char self[AQ_MSGLEN*2];
int num_sensors;
external_sensor sensors[MAX_SENSORS];
#ifdef AQ_MANAGER
volatile bool run_slogger;
int slogger_packets;

View File

@ -396,6 +396,7 @@ int main(int argc, char *argv[])
_aqualink_data.num_pumps = 0;
_aqualink_data.num_lights = 0;
_aqualink_data.num_sensors = 0;
#ifdef AQ_TM_DEBUG
addDebugLogMask(DBGT_LOG);
@ -656,7 +657,9 @@ int startup(char *self, char *cfgFile)
LOG(AQUA_LOG,LOG_NOTICE, "Read Chem Feeder direct = %s\n", bool2text(READ_RSDEV_CHEM));
if (isAQS_START_PUMP_EVENT_ENABLED) {
get_cron_pump_times();
if (isAQS_USE_PUMP_TIME_FROM_CRON_ENABLED) {
get_cron_pump_times();
}
LOG(AQUA_LOG,LOG_NOTICE, "Start Pump on events = %s %s %s\n",isAQS_POWER_ON_ENABED?"PowerON":"",AQS_FRZ_PROTECT_OFF?"FreezeProtect":"",AQS_BOOST_OFF?"Boost":"");
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 {
@ -725,6 +728,11 @@ int startup(char *self, char *cfgFile)
LOG(AQUA_LOG,LOG_WARNING, "Config error, extended_device_id must be on of the folowing (0x30,0x31,0x32,0x33) to use virtual button : '%s'",_aqualink_data.aqbuttons[i].label);
}
}
for (i = 0; i < _aqualink_data.num_sensors; i++)
{
LOG(AQUA_LOG,LOG_NOTICE, "Config Sensor %02d = label %-15s | %s\n", i+1, _aqualink_data.sensors[i].label, _aqualink_data.sensors[i].path);
}
/*
for (i=0; i < _aqualink_data.total_buttons; i++)
{
@ -735,6 +743,10 @@ int startup(char *self, char *cfgFile)
_aqualink_data.aqbuttons[i].rssd_code);
}
*/
#ifdef CONFIG_EDITOR
check_print_config(&_aqualink_data);
writeCfg(&_aqualink_data);
#endif
if (_aqconfig_.deamonize == true)
{
@ -945,6 +957,10 @@ void main_loop()
_aqualink_data.lights[i].RSSDstate = OFF;
}
for (i=0; i < _aqualink_data.num_sensors; i++) {
_aqualink_data.sensors[i].value = TEMP_UNKNOWN;
}
if (_aqconfig_.force_swg == true) {
//_aqualink_data.ar_swg_device_status = SWG_STATUS_OFF;
_aqualink_data.swg_led_state = OFF;
@ -1174,6 +1190,7 @@ void main_loop()
blank_read_reconnect = blank_read_reconnect * 50;
#endif
int loopnum=0;
blank_read = 0;
// OK, Now go into infinate loop
while (_keepRunning == true)
@ -1375,6 +1392,15 @@ void main_loop()
}
}
if ( _aqualink_data.num_sensors > 0 && ++loopnum >= 200 ) {
loopnum=0;
for (int i=0; i < _aqualink_data.num_sensors; i++) {
if (read_sensor(&_aqualink_data.sensors[i]) ) {
_aqualink_data.updated = true;
}
}
}
//tcdrain(rs_fd); // Make sure buffer has been sent.
//delay(10);
}

View File

@ -16,6 +16,7 @@ Intelibright
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] =
@ -170,11 +171,23 @@ bool set_aqualinkd_light_mode_name(char *name, int index, bool isShow)
}
}
// TODO NSF check isShow and add a custom one if needed
_color_light_options[0][index] = name;
return true;
}
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) {
return NULL;
}
*isShow = isShowMode(_color_light_options[0][index]);
return _color_light_options[0][index];
}
const char *get_currentlight_mode_name(clight_detail light, emulation_type protocol)
{
/*
@ -223,6 +236,9 @@ const char *light_mode_name(clight_type type, int index, emulation_type protocol
bool isShowMode(const char *mode)
{
if (mode == NULL)
return false;
if (strcmp(mode, "Color Splash") == 0 ||
strcmp(mode, "Slow Splash") == 0 ||
strcmp(mode, "Fast Splash") == 0 ||

View File

@ -35,6 +35,7 @@ int build_color_light_jsonarray(int index, char* buffer, int size);
void set_currentlight_value(clight_detail *light, int index);
bool set_aqualinkd_light_mode_name(char *name, int index, bool isShow);
const char *get_aqualinkd_light_mode_name(int index, bool *isShow);
//char *_color_light_options_[LIGHT_COLOR_TYPES][LIGHT_COLOR_OPTIONS][LIGHT_COLOR_NAME];

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,8 @@
#include "aq_serial.h"
#include "aqualink.h"
//#define CONFIG_DEV_TEST
//#define CONFIG_EDITOR
//#define DEFAULT_LOG_LEVEL 10
#define DEFAULT_LOG_LEVEL LOG_NOTICE
@ -86,10 +88,15 @@ struct aqconfig
//bool read_pentair_packets;
uint8_t read_RS485_devmask;
bool use_panel_aux_labels;
//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;
//int swg_zero_ignore; // This can be removed since this was due to VSP that's been fixed.
bool display_warnings_web;
bool log_protocol_packets; // Read & Write as packets
@ -105,7 +112,8 @@ struct aqconfig
bool mqtt_timed_update;
bool sync_panel_time;
bool enable_scheduler;
int16_t schedule_event_mask;
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;
@ -138,6 +146,17 @@ struct aqconfig _aqconfig_;
#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 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_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)
*/
/*
#ifndef CONFIG_C
@ -159,6 +178,189 @@ void init_config();
bool writeCfg (struct aqualinkdata *aqdata);
bool setConfigValue(struct aqualinkdata *aqdata, char *param, char *value);
bool mac(char *buf, int len, bool useDelimiter);
char *cleanalloc(char*str);
char *cleanalloc(char *str);
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);
void check_print_config (struct aqualinkdata *aqdata);
#endif
#if defined(CONFIG_DEV_TEST) || defined(CONFIG_EDITOR)
typedef enum cfg_value_type{
CFG_STRING,
CFG_INT,
CFG_FLOAT,
CFG_HEX,
CFG_BOOL,
CFG_BITMASK,
CFG_SPECIAL
} cfg_value_type;
#endif
#ifdef CONFIG_DEV_TEST
typedef struct cfgParam {
void *value_ptr;
//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;
} cfgParam;
#ifndef CONFIG_C
extern cfgParam _cfgParams[];
extern int _numCfgParams;
#else
cfgParam _cfgParams[100];
int _numCfgParams;
#endif // CONFIG_C
#endif // CONFIG_DEV_TEST
// Below are missed
//RSSD_LOG_filter
//debug_log_mask
#define CFG_V_BOOL "[\"Yes\", \"No\"]"
#define CFG_N_serial_port "serial_port"
#define CFG_C_serial_port 11
#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_C_socket_port 11
#define CFG_N_web_directory "web_directory"
#define CFG_C_web_directory 13
#define CFG_N_device_id "device_id"
#define CFG_V_device_id "[\"0x0a\", \"0x0b\", \"0x09\", \"0x08\", \"0x60\", \"0xFF\"]"
#define CFG_C_device_id 9
#define CFG_N_rssa_device_id "rssa_device_id"
#define CFG_V_rssa_device_id "[\"0x00\", \"0x48\", \"0xFF\"]"
#define CFG_C_rssa_device_id 14
#define CFG_N_RSSD_LOG_filter "RSSD_LOG_filter"
#define CFG_C_RSSD_LOG_filter 15
#define CFG_N_panel_type "panel_type"
#define CFG_C_panel_type 10
#define CFG_N_extended_device_id "extended_device_id"
#define CFG_V_extended_device_id "[\"0x00\", \"0x30\", \"0x31\", \"0x32\", \"0x33\", \"0x40\", \"0x41\", \"0x42\", \"0x43\", \"0xFF\"]"
#define CFG_C_extended_device_id 18
#define CFG_N_sync_panel_time "sync_panel_time"
#define CFG_C_sync_panel_time 15
//#define CFG_N_extended_device_id2 "extended_device_id2"
//#define CFG_C_extended_device_id2 20
#define CFG_N_extended_device_id_programming "extended_device_id_programming"
#define CFG_C_extended_device_id_programming 30
#define CFG_N_enable_iaqualink "enable_iaqualink"
#define CFG_C_enable_iaqualink 16
#define CFG_N_log_file "log_file"
#define CFG_C_log_file 8
#define CFG_N_mqtt_aq_topic "mqtt_aq_topic"
#define CFG_C_mqtt_aq_topic 13
#define CFG_N_mqtt_server "mqtt_address"
#define CFG_C_mqtt_server 12
#define CFG_N_mqtt_user "mqtt_user"
#define CFG_C_mqtt_user 9
#define CFG_N_mqtt_passwd "mqtt_passwd"
#define CFG_C_mqtt_passwd 11
#define CFG_N_mqtt_hass_discover_topic "mqtt_ha_discover_topic"
#define CFG_C_mqtt_hass_discover_topic 24
#define CFG_N_mqtt_hass_discover_use_mac "mqtt_ha_discover_use_mac"
#define CFG_C_mqtt_hass_discover_use_mac 27
#define CFG_N_mqtt_timed_update "mqtt_timed_update"
#define CFG_C_mqtt_timed_update 17
//#define CFG_N_mqtt_ID "mqtt_ID"
//#define CFG_C_mqtt_ID 7
#define CFG_N_mqtt_dz_sub_topic "mqtt_dz_sub_topic"
#define CFG_C_mqtt_dz_sub_topic 17
#define CFG_N_mqtt_dz_pub_topic "mqtt_dz_pub_topic"
#define CFG_C_mqtt_dz_pub_topic 17
#define CFG_N_dzidx_air_temp "dzidx_air_temp"
#define CFG_C_dzidx_air_temp 14
#define CFG_N_dzidx_pool_water_temp "dzidx_pool_water_temp"
#define CFG_C_dzidx_pool_water_temp 21
#define CFG_N_dzidx_spa_water_temp "dzidx_spa_water_temp"
#define CFG_C_dzidx_spa_water_temp 20
#define CFG_N_dzidx_swg_percent "dzidx_SWG_percent"
#define CFG_C_dzidx_swg_percent 17
#define CFG_N_dzidx_swg_ppm "dzidx_SWG_PPM"
#define CFG_C_dzidx_swg_ppm 13
#define CFG_N_dzidx_swg_status "dzidx_SWG_Status"
#define CFG_C_dzidx_swg_status 16
#define CFG_N_light_programming_mode "light_programming_mode"
#define CFG_C_light_programming_mode 22
#define CFG_N_light_programming_initial_on "light_programming_initial_on"
#define CFG_C_light_programming_initial_on 28
#define CFG_N_light_programming_initial_off "light_programming_initial_off"
#define CFG_C_light_programming_initial_off 29
#define CFG_N_override_freeze_protect "override_freeze_protect"
#define CFG_C_override_freeze_protect 23
#define CFG_N_pda_sleep_mode "pda_sleep_mode"
#define CFG_C_pda_sleep_mode 14
#define CFG_N_convert_mqtt_temp "mqtt_convert_temp_to_c"
#define CFG_C_convert_mqtt_temp 22
#define CFG_N_convert_dz_temp "dz_convert_temp_to_c"
#define CFG_C_convert_dz_temp 20
#define CFG_N_report_zero_spa_temp "report_zero_spa_temp"
#define CFG_C_report_zero_spa_temp 20
#define CFG_N_report_zero_pool_temp "report_zero_pool_temp"
#define CFG_C_report_zero_pool_temp 21
#define CFG_N_read_RS485_devmask "read_RS485_devmask"
#define CFG_C_read_RS485_devmask 18
#define CFG_N_use_panel_aux_labels "use_panel_aux_labels"
#define CFG_C_use_panel_aux_labels 20
#define CFG_N_force_swg "force_swg"
#define CFG_C_force_swg 9
#define CFG_N_force_ps_setpoints "force_ps_setpoints"
#define CFG_C_force_ps_setpoints 18
#define CFG_N_force_frzprotect_setpoints "force_frzprotect_setpoints"
#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_display_warnings_web "display_warnings_web"
#define CFG_C_display_warnings_web 20
#define CFG_N_log_protocol_packets "log_protocol_packets"
#define CFG_C_log_protocol_packets 20
#define CFG_N_device_pre_state "device_pre_state"
#define CFG_C_device_pre_state 16
#define CFG_N_read_RS485_swg "read_RS485_swg"
#define CFG_C_read_RS485_swg 14
#define CFG_N_read_RS485_ePump "read_RS485_ePump"
#define CFG_C_read_RS485_ePump 16
#define CFG_N_read_RS485_vsfPump "read_RS485_vsfPump"
#define CFG_C_read_RS485_vsfPump 18
#define CFG_N_read_RS485_JXi "read_RS485_JXi"
#define CFG_C_read_RS485_JXi 14
#define CFG_N_read_RS485_LX "read_RS485_LX"
#define CFG_C_read_RS485_LX 13
#define CFG_N_read_RS485_Chem "read_RS485_Chem"
#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_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_ftdi_low_latency "ftdi_low_latency"
#define CFG_C_ftdi_low_latency 16
#define CFG_N_rs485_frame_delay "rs485_frame_delay"
#define CFG_C_rs485_frame_delay 17
#endif

View File

@ -667,5 +667,17 @@ void publish_mqtt_hassio_discover(struct aqualinkdata *aqdata, struct mg_connect
sprintf(msg, HASSIO_BATTERY_SENSOR_DISCOVER,connections,_aqconfig_.mqtt_aq_topic,BATTERY_STATE,BATTERY_STATE,_aqconfig_.mqtt_aq_topic,BATTERY_STATE);
sprintf(topic, "%s/binary_sensor/aqualinkd/aqualinkd_%s/config", _aqconfig_.mqtt_hass_discover_topic,BATTERY_STATE);
send_mqtt(nc, topic, msg);
for (i=0; i < aqdata->num_sensors; i++) {
//sprintf(idbuf, "%s_%s","sensor",aqdata->sensors[i].label);
sprintf(topic, "%s/%s",SENSOR_TOPIC,aqdata->sensors[i].label);
rsm_char_replace(idbuf, topic, "/", "_");
//sprintf(msg, HASSIO_SENSOR_DISCOVER,connections,_aqconfig_.mqtt_aq_topic,idbuf,aqdata->sensors[i].label,_aqconfig_.mqtt_aq_topic,topic, "°C", "mdi:thermometer");
// Use HASSIO_TEMP_SENSOR_DISCOVER over HASSIO_SENSOR_DISCOVER since it has device class temperature and HA will convert automatically.
sprintf(msg, HASSIO_TEMP_SENSOR_DISCOVER,connections,_aqconfig_.mqtt_aq_topic,idbuf,aqdata->sensors[i].label,_aqconfig_.mqtt_aq_topic,topic, "°C", "mdi:thermometer");
sprintf(topic, "%s/sensor/aqualinkd/aqualinkd_%s/config", _aqconfig_.mqtt_hass_discover_topic, idbuf);
send_mqtt(nc, topic, msg);
}
}

View File

@ -883,13 +883,26 @@ void processPage(struct aqualinkdata *aq_data)
}
}
#define REQUEST_STATUS_POLL_COUNT 10
#define REQUEST_DEVICES_POLL_COUNT 30 // if _aqconfig_.enable_iaqualink=true then REQUEST_STATUS_POLL_COUNT will be used.
// if enable_iaqualink this poll count can be increased if we sit on the device status page
// all device status are quicker to update in enable_iaqualink, so leaves just pump/swg info to get.
#define FULL_STATUS_POLL_COUNT 200 // We did have this at 20, but put too much load on panel, (couldn't program light)
#define DEVICE_STATUS_POLL_COUNT 20 // This must be less than FULL_STATUS_POLL_COUNT
//#define REQUEST_DEVICES_POLL_COUNT 30 // if _aqconfig_.enable_iaqualink=true then REQUEST_STATUS_POLL_COUNT will be used.
static int _pollCnt;
// running through status while programming a lighgt seems to confuse the panel, so let
// other people reset our poll count.
void reset_iaqTouchPollCounter()
{
_pollCnt = 0;
}
bool process_iaqtouch_packet(unsigned char *packet, int length, struct aqualinkdata *aq_data)
{
static bool gotInit = false;
static int cnt = 0;
//static int _pollCnt = 0;
//static int probesSinceLastPageCMD=0;
static bool gotStatus = true;
static char message[AQ_MSGLONGLEN + 1];
@ -1095,20 +1108,33 @@ if not programming && poll packet {
    else if status goto Home
  }
  increase counter
The above sits on device page.
Should we sit on status page to get better updates of VSP/SWG? would make sence if iaqualink is enabeled
since that gets all device updates quicker and cleaner.
if we a PDA only, then sit on devices page.
if iaqualink is not enabeled, still sit on status page.
So why did I update this to sit on devices page???????????
RS16 should also probably sit on devices page.
}*/
if (in_programming_mode(aq_data) == false) {
//LOG(IAQT_LOG,LOG_DEBUG, "Poll counter = %d\n",_pollCnt);
if (_currentPage == IAQ_PAGE_HOME) {
iaqt_queue_cmd(KEY_IAQTCH_HOMEP_KEY08); // This is "other devices on/off" page
cnt = 0;
_pollCnt = 0;
}
//if ( (isPDA_PANEL || isVirtualButtonEnabled() || PANEL_SIZE() >= 16) && !in_iaqt_programming_mode(aq_data) ) {
// Just sent Status Page request if none of the above are active
//}
// After we send devices page in above if statment, kick us through a loop of
// devices devices1 devices2 devices2 status.
// We probably only need to go over this loop if iaqualink2 is NOT enabled.
// But may be better to simply increase FULL_STATUS_POLL_COUNT when it's not enabled.
uint8_t nextPageRequestKey = KEY_IAQTCH_HOME;
if (cnt++ > REQUEST_STATUS_POLL_COUNT) {
if (_pollCnt++ > FULL_STATUS_POLL_COUNT) {
switch(_currentPage) {
case IAQ_PAGE_DEVICES:
case IAQ_PAGE_DEVICES_REV_Yg:
@ -1132,27 +1158,22 @@ if not programming && poll packet {
}
iaqt_queue_cmd(nextPageRequestKey);
/*
if (probesSinceLastPageCMD > 3) {
// Seems to be a bug with wifi device ghosting command on/off, kind-a looks like our page commands don;t take sometimes so wait.
// This didn;t fix issue, but see
iaqt_queue_cmd(nextPageRequestKey);
probesSinceLastPageCMD=0;
} else {
LOG(IAQT_LOG, LOG_INFO, "Waiting to send next page cnt %d\n",probesSinceLastPageCMD);
}
*/
} else if ( (_pollCnt % DEVICE_STATUS_POLL_COUNT == 0) &&
_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) {
// Set count to something close to max, so we will pull latest info once programming has finished.
// This is good for VSP GPM programming as it takes number of seconds to register once finished programming.
// -5 seems to be too quick for VSP/GPM so using 10
cnt = REQUEST_STATUS_POLL_COUNT - 10;
// This is probably not needed any more, since we grab status quite often now.
_pollCnt = FULL_STATUS_POLL_COUNT - 10;
}
#else
//LOG(IAQT_LOG,LOG_DEBUG, "poll count %d\n",cnt);
//LOG(IAQT_LOG,LOG_DEBUG, "poll count %d\n",_pollCnt);
// Load status page every 50 messages
if (cnt++ > REQUEST_STATUS_POLL_COUNT && in_programming_mode(aq_data) == false ) {
if (_pollCnt++ > FULL_STATUS_POLL_COUNT && in_programming_mode(aq_data) == false ) {
if (isPDA_PANEL || PANEL_SIZE() >= 16) {
iaqt_queue_cmd(KEY_IAQTCH_HOMEP_KEY08);
} else {
@ -1160,12 +1181,12 @@ if not programming && poll packet {
}
gotStatus = false; // Reset if we got status page, for fix panel bug.
//aq_programmer(AQ_GET_IAQTOUCH_VSP_ASSIGNMENT, NULL, aq_data);
cnt = 0;
} else if (gotStatus == false && cnt > 3) {
_pollCnt = 0;
} else if (gotStatus == false && _pollCnt > 3) {
// Fix bug with control panel where after a few hours status page disapears and you need to hit menu.
LOG(IAQT_LOG,LOG_INFO, "Overcomming Jandy control panel bug, (missing status, goto menu)\n",cnt);
LOG(IAQT_LOG,LOG_INFO, "Overcomming Jandy control panel bug, (missing status, goto menu)\n",_pollCnt);
iaqt_queue_cmd(KEY_IAQTCH_HOME);
cnt = REQUEST_STATUS_POLL_COUNT - 5;
_pollCnt = FULL_STATUS_POLL_COUNT - 5;
/*
if (isPDA_PANEL) {
iaqt_queue_cmd(KEY_IAQTCH_HOMEP_KEY08);
@ -1179,7 +1200,7 @@ if not programming && poll packet {
// Set count to something close to max, so we will pull latest info once programming has finished.
// This is good for VSP GPM programming as it takes number of seconds to register once finished programming.
// -5 seems to be too quick for VSP/GPM so using 10
cnt = REQUEST_STATUS_POLL_COUNT - 10;
_pollCnt = FULL_STATUS_POLL_COUNT - 10;
}
// On poll no need to kick programming threads

View File

@ -481,7 +481,7 @@ bool process_iaqualink_packet(unsigned char *packet, int length, struct aqualink
if (packet[PKT_CMD] == 0x53)
{
cnt++;
if (cnt == 20) {
if (cnt == 20) { // 20 is probably too low, should increase. (only RS16 and below)
cnt=0;
/*
sendid=sendid==0x18?0x60:0x18;

View File

@ -34,6 +34,7 @@
#include "aq_programmer.h"
#include "rs_msg_utils.h"
#include "color_lights.h"
#include "iaqualink.h"
//#define test_message "{\"type\": \"status\",\"version\": \"8157 REV MMM\",\"date\": \"09/01/16 THU\",\"time\": \"1:16 PM\",\"temp_units\": \"F\",\"air_temp\": \"96\",\"pool_temp\": \"86\",\"spa_temp\": \" \",\"battery\": \"ok\",\"pool_htr_set_pnt\": \"85\",\"spa_htr_set_pnt\": \"99\",\"freeze_protection\": \"off\",\"frz_protect_set_pnt\": \"0\",\"leds\": {\"pump\": \"on\",\"spa\": \"off\",\"aux1\": \"off\",\"aux2\": \"off\",\"aux3\": \"off\",\"aux4\": \"off\",\"aux5\": \"off\",\"aux6\": \"off\",\"aux7\": \"off\",\"pool_heater\": \"off\",\"spa_heater\": \"off\",\"solar_heater\": \"off\"}}"
//#define test_labels "{\"type\": \"aux_labels\",\"aux1_label\": \"Cleaner\",\"aux2_label\": \"Waterfall\",\"aux3_label\": \"Spa Blower\",\"aux4_label\": \"Pool Light\",\"aux5_label\": \"Spa Light\",\"aux6_label\": \"Unassigned\",\"aux7_label\": \"Unassigned\"}"
@ -539,6 +540,12 @@ int build_aqualink_aqmanager_JSON(struct aqualinkdata *aqdata, char* buffer, int
length += sprintf(buffer+length, "{\"type\": \"aqmanager\"");
length += sprintf(buffer+length, ",\"deamonized\": \"%s\"", (_aqconfig_.deamonize?JSON_ON:JSON_OFF) );
#ifdef CONFIG_EDITOR
length += sprintf(buffer+length, ",\"config_editor\": \"yes\"");
#else
length += sprintf(buffer+length, ",\"config_editor\": \"no\"");
#endif
/*
length += sprintf(buffer+length, ",\"panel_type\":\"%s\"",getPanelString());
length += sprintf(buffer+length, ",\"version\":\"%s\"",aqdata->version );//8157 REV MMM",
@ -742,22 +749,18 @@ printf("Pump Type %d\n",aqdata->pumps[i].pumpType);
length--;
length += sprintf(buffer+length, "}");
/*
for (i=0; i < aqdata->num_lights; i++)
length += sprintf(buffer+length, ",\"sensors\":{" );
for (i=0; i < aqdata->num_sensors; i++)
{
length += sprintf(buffer+length, ",\"Plight_%d\":{\"name\":\"%s\",\"id\":\"%s\", \"type\":\"%d\", \"value\":\"%d\", \"state\":\"%s\"}",
i+1,
aqdata->lights[i].button->label,
aqdata->lights[i].button->name,
aqdata->lights[i].lightType,
aqdata->lights[i].currentValue,
LED2text(aqdata->lights[i].RSSDstate)
);
//printf("Sensor value %f %.2f\n",aqdata->sensors[i].value,aqdata->sensors[i].value);
if (aqdata->sensors[i].value != TEMP_UNKNOWN) {
length += sprintf(buffer+length, "\"%s\": \"%.2f\",", aqdata->sensors[i].label, aqdata->sensors[i].value );
}
}
*/
//if (buffer[length-1] == ',')
// length--;
//length += sprintf(buffer+length, "}");
if (buffer[length-1] == ',')
length--;
length += sprintf(buffer+length, "}");
length += sprintf(buffer+length, "}" );
@ -1050,4 +1053,323 @@ bool parseJSONmqttrequest(const char *str, size_t len, int *idx, int *nvalue, ch
return false;
}
/*
int json_cfg_element_OLD(char* buffer, int size, const char *name, const void *value, cfg_value_type type, char *valid_val) {
int result = 0;
char valid_values[256];
if (valid_val != NULL) {
sprintf(valid_values,",\"valid values\":%s",valid_val);
}
switch(type){
case CFG_INT:
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"%d\", \"type\":\"int\" %s}", name, *(int *)value, (valid_val==NULL?"":valid_values) );
break;
case CFG_STRING:
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"%s\", \"type\":\"string\" %s}", name, (char *)value, (valid_val==NULL?"":valid_values) );
break;
case CFG_BOOL:
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"%s\", \"type\":\"bool\" %s}", name, bool2text(*(bool *)value), (valid_val==NULL?"":valid_values));
break;
case CFG_HEX:
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"0x%02hhx\", \"type\":\"hex\" %s}", name, *(unsigned char *)value, (valid_val==NULL?"":valid_values) );
break;
}
if (result <= 0 || result >= size) {
LOG(NET_LOG,LOG_ERR, "Buffer full in build_aqualink_config_JSON(), result truncated!");
return 0;
}
return result;
}
*/
#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 result = 0;
char valid_values[256];
if (valid_val != NULL) {
sprintf(valid_values,",\"valid values\":%s",valid_val);
}
switch(type){
case CFG_INT:
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"%d\", \"type\":\"int\" %s}", name, *(int *)value, (valid_val==NULL?"":valid_values) );
break;
case CFG_STRING:
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"%s\", \"type\":\"string\" %s}", name, *(char **)value, (valid_val==NULL?"":valid_values) );
break;
case CFG_BOOL:
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"%s\", \"type\":\"bool\" %s}", name, bool2text(*(bool *)value), (valid_val==NULL?"":valid_values));
break;
case CFG_HEX:
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"0x%02hhx\", \"type\":\"hex\" %s}", name, *(unsigned char *)value, (valid_val==NULL?"":valid_values) );
break;
case CFG_FLOAT:
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"%f\", \"type\":\"float\" %s}", name, *(float *)value, (valid_val==NULL?"":valid_values) );
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 );
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\"]");
} else {
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"Something went wrong\", \"type\":\"string\"}",name);
}
break;
}
if (result <= 0 || result >= size) {
LOG(NET_LOG,LOG_ERR, "Buffer full in build_aqualink_config_JSON(), result truncated!");
return 0;
}
return result;
}
/*
const char *pumpType2String(pump_type ptype) {
switch (ptype) {
case EPUMP:
return "JANDY ePUMP";
break;
case VSPUMP:
return "Pentair VF";
break;
case VFPUMP:
return "Pentair VS";
break;
case PT_UNKNOWN:
default:
return "unknown";
break;
}
}
*/
int build_aqualink_config_JSON(char* buffer, int size, struct aqualinkdata *aq_data)
{
memset(&buffer[0], 0, size);
int length = 0;
int result;
int i;
char buf[256];
const char *stringptr;
if ((result = snprintf(buffer+length, size-length, "{\"type\": \"config\"")) < 0 || result >= size-length) {
length += snprintf(buffer+length, size-length, "}");
return length;
} 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;
}
#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)
return length;
else
length += result;
}
#endif
// TODO add custom light modes/colors
// TODO add RSSD LOG FILTERS
// TODO add devmask for reading RS485 external
/*
if ((result = json_cfg_element(buffer+length, size-length, "config_file", _aqconfig_.config_file, CFG_STRING, NULL)) <= 0)
return length;
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;
else
length += result;
if ((result = json_cfg_element(buffer+length, size-length, CFG_N_device_id, &_aqconfig_.device_id, CFG_HEX, 0, "[\"0x0a\", \"0x0b\", \"0x09\", \"0x08\", \"0x60\", \"0xFF\"]")) <= 0)
return length;
else
length += result;
if ((result = json_cfg_element(buffer+length, size-length, CFG_N_rssa_device_id, &_aqconfig_.rssa_device_id, CFG_HEX, 0, "[\"0x00\", \"0x48\", \"0xFF\"]")) <= 0)
return length;
else
length += result;
#if defined AQ_ONETOUCH || defined AQ_IAQTOUCH
if ((result = json_cfg_element(buffer+length, size-length, CFG_N_extended_device_id, &_aqconfig_.extended_device_id, CFG_HEX, 0, "[\"0x00\", \"0x30\", \"0x31\", \"0x32\", \"0x33\", \"0x40\", \"0x41\", \"0x42\", \"0x43\", \"0xFF\"]")) <= 0)
return length;
else
length += result;
if ((result = json_cfg_element(buffer+length, size-length, CFG_N_extended_device_id, &_aqconfig_.extended_device_id_programming, CFG_BOOL, 0, "[\"YES\", \"NO\"]")) <= 0)
return length;
else
length += result;
if ((result = json_cfg_element(buffer+length, size-length, CFG_N_enable_iaqualink, &_aqconfig_.enable_iaqualink, CFG_BOOL, 0, "[\"YES\", \"NO\"]")) <= 0)
return length;
else
length += result;
#endif
if ((result = json_cfg_element(buffer+length, size-length, CFG_N_serial_port, &_aqconfig_.serial_port, CFG_STRING, 0, NULL)) <= 0)
return length;
else
length += result;
if ((result = json_cfg_element(buffer+length, size-length, CFG_N_socket_port, &_aqconfig_.socket_port, CFG_STRING, 0, NULL )) <= 0)
return length;
else
length += result;
stringptr = loglevel2cgn_name(_aqconfig_.log_level);
if ((result = json_cfg_element(buffer+length, size-length, CFG_N_log_level, &stringptr, CFG_STRING, 0, "[\"DEBUG\", \"INFO\", \"NOTICE\", \"WARNING\", \"ERROR\"]")) <= 0)
return length;
else
length += result;
if ((result = json_cfg_element(buffer+length, size-length, CFG_N_web_directory, &_aqconfig_.web_directory, CFG_STRING, 0, NULL)) <= 0)
return length;
else
length += result;
if ((result = json_cfg_element(buffer+length, size-length, "sched_chk_pumpon_hour", &_aqconfig_.sched_chk_pumpon_hour, CFG_INT, 0, NULL )) <= 0)
return length;
else
length += result;
// OTHERS
// OTHERS
if ((result = json_cfg_element(buffer+length, size-length, "force_swg", &_aqconfig_.force_swg, CFG_BOOL, 0, "[\"YES\", \"NO\"]")) <= 0)
return length;
else
length += result;
if ((result = json_cfg_element(buffer+length, size-length, "use_panel_aux_labels", &_aqconfig_.use_panel_aux_labels, CFG_BOOL, 0, NULL)) <= 0)
return length;
else
length += result;
// All buttons
for (i = 0; i < aq_data->total_buttons; i++)
{
char prefix[30];
if (isVBUTTON(aq_data->aqbuttons[i].special_mask)) {
sprintf(prefix,"virtual_button_%.2d",(i+1)-aq_data->virtual_button_start);
} else {
sprintf(prefix,"button_%.2d",i+1);
}
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)
return length;
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)
return length;
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)
return length;
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)
return length;
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)
return length;
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)
return length;
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)
return length;
else
length += result;
}
}
if ((result = snprintf(buffer+length, size-length, "}")) < 0) {
return 0;
} else {
length += result;
}
return length;
}
#endif

View File

@ -59,6 +59,7 @@ int build_aqualink_aqmanager_JSON(struct aqualinkdata *aqdata, char* buffer, int
//int build_device_JSON(struct aqualinkdata *aqdata, int programable_switch1, int programable_switch2, char* buffer, int size, bool homekit);
int build_device_JSON(struct aqualinkdata *aqdata, char* buffer, int size, bool homekit);
int build_aqualink_simulator_packet_JSON(struct aqualinkdata *aqdata, char* buffer, int size);
int build_aqualink_config_JSON(char* buffer, int size, struct aqualinkdata *aq_data);
#endif /* JSON_MESSAGES_H_ */

View File

@ -307,7 +307,7 @@ bool _broadcast_systemd_logmessages(bool aqMgrActive, bool reOpenStaleConnection
size_t plen;
int rtn;
while ( (rtn = sd_journal_next(journal)) > 0) // need to capture return of this
while ( (rtn = sd_journal_next(journal)) > 0)
{
if (sd_journal_get_data(journal, "MESSAGE", &log, &len) < 0) {
build_logmsg_JSON(msg, LOG_ERR, "Failed to get journal message", WS_LOG_LENGTH,29);
@ -323,7 +323,7 @@ bool _broadcast_systemd_logmessages(bool aqMgrActive, bool reOpenStaleConnection
}
}
if (rtn < 0) {
build_logmsg_JSON(msg, LOG_ERR, "Failed to get seen to next journal message", WS_LOG_LENGTH,42);
build_logmsg_JSON(msg, LOG_ERR, "Failed to seek to next journal message", WS_LOG_LENGTH,42);
ws_send_logmsg(_mgr.active_connections, msg);
sd_journal_close(journal);
active = false;
@ -339,6 +339,8 @@ bool _broadcast_systemd_logmessages(bool aqMgrActive, bool reOpenStaleConnection
//LOG(NET_LOG, LOG_WARNING, "**** %d Too many blank reads, resetting!! ****\n",cnt);
return _broadcast_systemd_logmessages(aqMgrActive, true);
}
cnt = 0; // Reset this so we don't keep hitting this when we don't print the message above.
//LOG(NET_LOG, LOG_WARNING, "**** Reset didn't work ****\n",cnt);
//return false;
}
@ -975,10 +977,20 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
}
}
}
// Loop over sensors
for (i=0; i < _aqualink_data->num_sensors; i++) {
if ( _aqualink_data->sensors[i].value != TEMP_UNKNOWN && _last_mqtt_aqualinkdata.sensors[i].value != _aqualink_data->sensors[i].value) {
char topic[50];
sprintf(topic, "%s/%s", SENSOR_TOPIC, _aqualink_data->sensors[i].label);
send_mqtt_float_msg(nc, topic, _aqualink_data->sensors[i].value);
_last_mqtt_aqualinkdata.sensors[i].value = _aqualink_data->sensors[i].value;
}
}
}
typedef enum {uActioned, uBad, uDevices, uStatus, uHomebridge, uDynamicconf, uDebugStatus, uDebugDownload, uSimulator, uSchedules, uSetSchedules, uAQmanager, uLogDownload, uNotAvailable} uriAtype;
typedef enum {uActioned, uBad, uDevices, uStatus, uHomebridge, uDynamicconf, uDebugStatus, uDebugDownload, uSimulator, uSchedules, uSetSchedules, uAQmanager, uLogDownload, uNotAvailable, uConfig, uSaveConfig} uriAtype;
//typedef enum {NET_MQTT=0, NET_API, NET_WS, DZ_MQTT} netRequest;
const char actionName[][5] = {"MQTT", "API", "WS", "DZ"};
@ -1067,6 +1079,10 @@ uriAtype action_URI(request_source from, const char *URI, int uri_length, float
return uSetSchedules;
} else if (strncmp(ri1, "schedules", 9) == 0) {
return uSchedules;
} else if (strncmp(ri1, "config/set", 10) == 0) {
return uSaveConfig;
} else if (strncmp(ri1, "config", 6) == 0) {
return uConfig;
} else if (strncmp(ri1, "simulator", 9) == 0 && from == NET_WS) { // Only valid from websocket.
if (ri2 != NULL && strncmp(ri2, "onetouch", 8) == 0) {
start_simulator(_aqualink_data, ONETOUCH);
@ -1678,7 +1694,7 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg)
DEBUG_TIMER_START(&tid2);
int size = build_schedules_js(message, JSON_BUFFER_SIZE);
DEBUG_TIMER_STOP(tid2, NET_LOG, "action_web_request() build_schedules_js took");
mg_send_head(nc, 200, size, CONTENT_JS);
mg_send_head(nc, 200, size, CONTENT_JSON);
mg_send(nc, message, size);
}
break;
@ -1688,10 +1704,22 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg)
DEBUG_TIMER_START(&tid2);
int size = save_schedules_js(http_msg->body.p, http_msg->body.len, message, JSON_BUFFER_SIZE);
DEBUG_TIMER_STOP(tid2, NET_LOG, "action_web_request() save_schedules_js took");
mg_send_head(nc, 200, size, CONTENT_JS);
mg_send_head(nc, 200, size, CONTENT_JSON);
mg_send(nc, message, size);
}
break;
#ifdef CONFIG_EDITOR
case uConfig:
{
char message[JSON_BUFFER_SIZE];
DEBUG_TIMER_START(&tid2);
int size = build_aqualink_config_JSON(message, JSON_BUFFER_SIZE, _aqualink_data);
DEBUG_TIMER_STOP(tid2, NET_LOG, "action_web_request() build_aqualink_config_JSON took");
mg_send_head(nc, 200, size, CONTENT_JSON);
mg_send(nc, message, size);
}
break;
#endif
#ifndef AQ_MANAGER
case uDebugStatus:
{
@ -1757,7 +1785,7 @@ void action_websocket_request(struct mg_connection *nc, struct websocket_message
pda_reset_sleep();
#endif
strncpy(buffer, (char *)wm->data, wm->size);
strncpy(buffer, (char *)wm->data, MIN(wm->size, 99));
buffer[wm->size] = '\0';
parseJSONrequest(buffer, &jsonkv);
@ -1849,6 +1877,27 @@ void action_websocket_request(struct mg_connection *nc, struct websocket_message
DEBUG_TIMER_STOP(tid, NET_LOG, "action_websocket_request() save_schedules_js took");
ws_send(nc, message);
}
break;
#ifdef CONFIG_EDITOR
case uConfig:
{
DEBUG_TIMER_START(&tid);
char message[JSON_BUFFER_SIZE];
build_aqualink_config_JSON(message, JSON_BUFFER_SIZE, _aqualink_data);
DEBUG_TIMER_STOP(tid, NET_LOG, "action_websocket_request() build_aqualink_config_JSON took");
ws_send(nc, message);
}
break;
case uSaveConfig:
{
DEBUG_TIMER_START(&tid);
char message[JSON_BUFFER_SIZE];
save_config_js((char *)wm->data, wm->size, message, JSON_BUFFER_SIZE);
DEBUG_TIMER_STOP(tid, NET_LOG, "action_websocket_request() save_config_js took");
ws_send(nc, message);
}
break;
#endif
case uBad:
default:
if (msg == NULL)
@ -2080,6 +2129,10 @@ void reset_last_mqtt_status()
_last_mqtt_aqualinkdata.lights[i].currentValue = TEMP_UNKNOWN;
}
for (i=0; i < _aqualink_data->num_sensors; i++) {
_last_mqtt_aqualinkdata.sensors[i].value = TEMP_UNKNOWN;
}
}
void start_mqtt(struct mg_mgr *mgr) {

View File

@ -372,6 +372,8 @@ int rsm_strncmp(const char *haystack, const char *needle, int length)
char *rsm_char_replace(char *replaced , char *search, char *find, char *replace)
{
return rsm_nchar_replace(replaced, 0, search, find, replace);
/*
int len;
int i;
char *fp = find;
@ -386,6 +388,30 @@ char *rsm_char_replace(char *replaced , char *search, char *find, char *replac
}
replaced[i] = '\0';
return replaced;
*/
}
char *rsm_nchar_replace(char *replaced, int replaced_len, char *search, char *find, char *replace)
{
int len;
int i;
char *fp = find;
char *rp = replace;
len = strlen(search);
if (replaced_len > 0)
len = AQ_MIN(len, replaced_len);
for(i = 0; i < len; i++){
if (search[i] == *fp)
replaced[i] = *rp;
else
replaced[i] = search[i];
}
replaced[i] = '\0';
return replaced;
}

View File

@ -27,6 +27,7 @@ float rsm_atof(const char* str);
char *rsm_strncpycut(char *dest, const char *src, int dest_len, int src_len);
//char *rsm_char_replace(char *replaced , char *search, const char find, const char replace);
char *rsm_char_replace(char *replaced , char *search, char *find, char *replace);
char *rsm_nchar_replace(char *replaced, int replaced_len, char *search, char *find, char *replace);
int rsm_HHMM2min(char *message);
#endif //RS_MSG_UTILS_H_

44
source/sensors.c Normal file
View File

@ -0,0 +1,44 @@
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include "sensors.h"
#include "aqualink.h"
/*
read sensor value from ie /sys/class/thermal/thermal_zone0/temp
return true if current reading is different to last value stored
*/
bool read_sensor(external_sensor *sensor) {
FILE *fp;
float value;
fp = fopen(sensor->path, "r");
if (fp == NULL) {
LOGSystemError(errno, AQUA_LOG, sensor->path);
LOG(AQUA_LOG,LOG_ERR, "Reading sensor %s %s\n",sensor->label, sensor->path);
return FALSE;
}
fscanf(fp, "%f", &value);
fclose(fp);
// Convert usually from millidegrees Celsius to degrees Celsius
//printf("Factor = %f - value %f\n",sensor->factor, value);
//printf("Read Sensor value %f %.2f\n",value,value * sensor->factor);
value = value * sensor->factor;
//printf("Converted value %f\n",value);
LOG(AQUA_LOG,LOG_DEBUG, "Read sensor %s value=%.2f\n",sensor->label, value);
if (sensor->value != value) {
sensor->value = value;
return TRUE;
}
return FALSE;
}

15
source/sensors.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef SENSORS_H_
#define SENSORS_H_
#include <stdbool.h>
typedef struct external_sensor{
char *path;
float factor;
char *label;
float value;
} external_sensor;
bool read_sensor(external_sensor *sensor);
#endif

View File

@ -286,6 +286,31 @@ const char* loglevel2name(int level)
return elevel2text(level);
}
const char* loglevel2cgn_name(int level)
{
switch(level) {
case LOG_ERR:
return "ERROR";
break;
case LOG_WARNING:
return "WARNING";
break;
case LOG_NOTICE:
return "NOTICE";
break;
case LOG_INFO:
return "INFO";
break;
case LOG_DEBUG_SERIAL:
return "DEBUG_SERIAL";
break;
case LOG_DEBUG:
default:
return "DEBUG";
break;
}
}
const char* logmask2name(logmask_t from)
{
switch (from) {

View File

@ -92,6 +92,7 @@ void clearDebugLogMask();
bool isDebugLogMaskSet(logmask_t flag);
const char* logmask2name(logmask_t mask);
const char* loglevel2name(int level);
const char* loglevel2cgn_name(int level);
//#define logMessage(msg_level, format, ...) LOG (1, msg_level, format, ##__VA_ARGS__)

View File

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

View File

@ -10,7 +10,11 @@
<meta name='apple-mobile-web-app-status-bar-style' content='black'>
<link href="aqualinkd.png" rel="apple-touch-icon">
<link href="aqualinkd.png" rel="icon">
<script type="text/javascript" src="confighelp.js"></script>
<style>
:root {
--max-config-height: 870px;
}
html {}
body {
@ -53,6 +57,19 @@
/*overflow-y: scroll; this only works if we know the height, we can caculate but not important at moment */
}
.configpopup {
top: 10px;
/*left: 10px;*/
position: fixed;
display: flex;
justify-content: center;
align-items: center;
width: 100vw;
/*
height: 100%;
*/
}
/*
.loglevelstitle {
background-color: rgb(221, 221, 221);
@ -239,6 +256,49 @@
background-color: #f1f1f1;
font-size: 14px;
}
/* FOR CONFIG PUPUP */
.config_options {
top: 10px;
position: fixed;
display: flex;
justify-content: center;
align-items: center;
width: 100vw;
/*height: 100vw;*/
}
.config_options_pane {
background-color: var(--options_pane_background);
border: 2px solid var(--options_pane_bordercolor);
border-radius: 20px;
justify-content: center;
align-items: center;
width: 700px;
/*max-height: 870px;*/
max-height: var(--max-config-height);
overflow-x: hidden;
overflow-y: auto;
}
.config_options_added {
background-color: white;
}
.hide {
display: none;
filter: alpha(opacity=0);
opacity: 0.0;
}
/*
.helptxt {
font-size: x-small;
}
*/
/*
tr{
border-bottom: 1px solid black;
}
*/
</style>
<script type='text/javascript'>
@ -413,7 +473,7 @@
}
function downloadFile(filePath, lines) {
console.log("Lines=" + lines);
//console.log("Lines=" + lines);
var link = document.createElement('a');
link.href = filePath + "/" + lines;
@ -460,7 +520,283 @@
} catch (Error) { }
}
var _config = {};
function editconfig(caller) {
var msg = {
uri: "config"
};
send_command(msg);
}
function saveconfig(caller) {
var json = {
uri: "config/set"
};
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;
}
}
json["values"] = values;
//console.log (JSON.stringify(json));
send_command(json);
closeconfig();
}
function closeconfig() {
const configtable = document.getElementById("config_table");
while (configtable.rows.length > 1) {
configtable.deleteRow(1); // Delete the first data row (index 1)
}
document.getElementById("config_options").classList.add("hide");
document.getElementById("config_options_pane").classList.add("hide");
}
function showConfig(data) {
var h = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
//console.log("Height="+h);
document.documentElement.style.setProperty('--max-config-height', (h-30)+'px');
document.getElementById("config_options").classList.remove("hide");
document.getElementById("config_options_pane").classList.remove("hide");
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;
for (var obj in data) {
var isBtn = false;
if (_confighelp[obj.toString()] !== undefined) {
//console.log("FOUND HELP");
//console.log(_confighelp[obj.toString()]);
const newRow = configtable.insertRow();
const cell1 = newRow.insertCell();
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 (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;
}
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;
}
}
// 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");
}
}
function cfgValueChanged(event) {
//console.log("Config set "+event.srcElement.getAttribute('key')+" to "+event.srcElement.value)
_config[event.srcElement.getAttribute('key')].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);
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){}
}
}
function update_status(data) {
/*
@ -519,6 +855,11 @@
//console.log("deamonized=" + data['deamonized'] + " Need to rename Restart to Reload");
disablebutton("restart");
}
if (data['config_editor'] == 'yes') {
enablebutton("editconfig");
} else {
disablebutton("editconfig");
}
/*
if (data['logfilename'] == "(null)")
@ -577,7 +918,7 @@
var filterentry = document.getElementById("rssd_filter");
filterentry.value = "";
for (var id in data['debugmasks'][obj].filters) {
console.log("Filter "+data['debugmasks'][obj].filters[id]);
//console.log("Filter "+data['debugmasks'][obj].filters[id]);
if (data['debugmasks'][obj].filters[id] != "0x00") {
if (filterentry.value.length == 0) {
filterentry.value = data['debugmasks'][obj].filters[id];
@ -651,6 +992,8 @@
update_status(data);
} else if (data.type == 'aux_labels') {
//set_labels(data);
} else if (data.type == 'config') {
showConfig(data);
}
}
socket_di.onclose = function () {
@ -669,7 +1012,7 @@
}
function send_command(cmd) {
console.log("Send " + JSON.stringify(cmd));
//console.log("Send " + JSON.stringify(cmd));
socket_di.send(JSON.stringify(cmd));
}
@ -786,6 +1129,7 @@
<body onload="init();init_collapsible();">
<div class="wrapper">
<div class="aqualinkd">
<iframe id="aqd" src="/" width="100%" height="350px"></iframe>
</div>
@ -889,8 +1233,14 @@
</div>
<button class="collapsible">Config</button>
<div class="content" id="config">
<!-- Will be populated on load -->
<label>Not Implimented</label>
<table border='0'>
<tr>
<td align="center">
<input id="editconfig" type="button" onclick="editconfig(this);"
value="edit config"></input>
</td>
</tr>
</table>
</div>
</div>
</div>
@ -900,6 +1250,34 @@
<!--<div class="inner">END</div>-->
</div>
</div>
<!-- <div id="config_options" class="config_options hide" style="display: flex;" onclick="closeconfig();"> -->
<div id="config_options" class="config_options hide" style="display: flex;">
<div id="config_options_pane" class="config_options_pane hide">
<table id="config_table" border="0" cellpadding="1px" width="100%" style="border-collapse: collapse;">
<tbody><tr class="options_title">
<th colspan="3"><span>AqualinkD Configuration</span>
</th>
</tr>
<tr>
<td align="right" width="50%">
Key
</td>
<td align="left" width="50%">
Value
</td>
</tr>
</tbody>
</table>
<table border="0" cellpadding="0px" width="100%"><tr>
<td align="right" style="width:50%;padding-right: 10px;">
<input id="saveconfig" type="button" onclick="saveconfig(this);" value="Save Config"></input>
</td>
<td align="left" style="width:50%;padding-left: 10px;">
<input id="saveconfig" type="button" onclick="closeconfig(this);" value="Close (without saving)"></input>
</td>
</tr></table>
</div>
</div>
<!--<iframe src='about:blank' id="logdownload"></iframe>-->
</div>
</body>

3
web/confighelp.js Normal file
View File

@ -0,0 +1,3 @@
let _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";

View File

@ -2513,6 +2513,7 @@
}
function set_schedules(msg) {
msg.uri = "schedules/set";
//console.log(JSON.stringify(msg));
socket_di.send(JSON.stringify(msg));
//socket_di.send(msg);
}