Release 2.6.0

master v2.6.0
sfeakes 2025-03-22 16:14:14 -05:00
parent 53c88deda2
commit b0ead8dfe9
35 changed files with 1037 additions and 341 deletions

View File

@ -139,13 +139,15 @@ DBG_CFLAGS = $(DBGFLAGS) $(AQ_FLAGS) $(MGFLAGS)
# Other sources.
DBG_SRC = $(SRCS) debug_timer.c
SL_SRC = serial_logger.c aq_serial.c utils.c packetLogger.c rs_msg_utils.c timespec_subtract.c
#MG_SRC = mongoose.c
DD_SRC = dummy_device.c aq_serial.c utils.c packetLogger.c rs_msg_utils.c timespec_subtract.c
# Build durectories
SRC_DIR := ./source
OBJ_DIR := ./build
DBG_OBJ_DIR := $(OBJ_DIR)/debug
SL_OBJ_DIR := $(OBJ_DIR)/slog
DD_OBJ_DIR := $(OBJ_DIR)/dummydevice
INCLUDES := -I$(SRC_DIR)
@ -161,11 +163,13 @@ SL_OBJ_DIR_AMD64 := $(OBJ_DIR_AMD64)/slog
SRCS := $(patsubst %.c,$(SRC_DIR)/%.c,$(SRCS))
DBG_SRC := $(patsubst %.c,$(SRC_DIR)/%.c,$(DBG_SRC))
SL_SRC := $(patsubst %.c,$(SRC_DIR)/%.c,$(SL_SRC))
DD_SRC := $(patsubst %.c,$(SRC_DIR)/%.c,$(DD_SRC))
# append path to obj files per architecture
OBJ_FILES := $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR)/%.o,$(SRCS))
DBG_OBJ_FILES := $(patsubst $(SRC_DIR)/%.c,$(DBG_OBJ_DIR)/%.o,$(DBG_SRC))
SL_OBJ_FILES := $(patsubst $(SRC_DIR)/%.c,$(SL_OBJ_DIR)/%.o,$(SL_SRC))
DD_OBJ_FILES := $(patsubst $(SRC_DIR)/%.c,$(DD_OBJ_DIR)/%.o,$(DD_SRC))
OBJ_FILES_ARMHF := $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR_ARMHF)/%.o,$(SRCS))
OBJ_FILES_ARM64 := $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR_ARM64)/%.o,$(SRCS))
@ -194,6 +198,7 @@ SL_OBJ_FILES_AMD64 := $(patsubst $(SRC_DIR)/%.c,$(SL_OBJ_DIR_AMD64)/%.o,$(SL_SRC
MAIN = ./release/aqualinkd
SLOG = ./release/serial_logger
DEBG = ./release/aqualinkd-debug
DDEVICE = ./release/dummydevice
MAIN_ARM64 = ./release/aqualinkd-arm64
MAIN_ARMHF = ./release/aqualinkd-armhf
@ -256,6 +261,9 @@ slog: $(SLOG)
aqdebug: $(DEBG)
$(info $(DEBG) has been compiled)
dummydevice: $(DDEVICE)
$(info $(DDEVICE) has been compiled)
# Container, add container flag and compile
container: CFLAGS := $(CFLAGS) -D AQ_CONTAINER
container: $(MAIN) $(SLOG)
@ -307,6 +315,9 @@ $(DBG_OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(DBG_OBJ_DIR)
$(SL_OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(SL_OBJ_DIR)
$(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
$(DD_OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(DD_OBJ_DIR)
$(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
$(OBJ_DIR_ARMHF)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR_ARMHF)
$(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
@ -357,6 +368,10 @@ $(SLOG_AMD64): CFLAGS := $(CFLAGS) -D SERIAL_LOGGER
$(SLOG_AMD64): $(SL_OBJ_FILES_AMD64)
$(CC) $(CFLAGS) $(INCLUDES) -o $@ $^ $(LIBS)
$(DDEVICE): CFLAGS := $(CFLAGS) -D SERIAL_LOGGER -D DUMMY_DEVICE
$(DDEVICE): $(DD_OBJ_FILES)
$(CC) $(CFLAGS) $(INCLUDES) -o $@ $^ $(LIBS)
# Rules to make object directories.
$(OBJ_DIR):
$(MKDIR) $(call FixPath,$@)
@ -364,6 +379,9 @@ $(OBJ_DIR):
$(SL_OBJ_DIR):
$(MKDIR) $(call FixPath,$@)
$(DD_OBJ_DIR):
$(MKDIR) $(call FixPath,$@)
$(DBG_OBJ_DIR):
$(MKDIR) $(call FixPath,$@)
@ -388,10 +406,10 @@ $(SL_OBJ_DIR_AMD64):
# Clean rules
clean: clean-buildfiles
$(RM) *.o *~ $(MAIN) $(MAIN_U) $(PLAY) $(PL_EXOBJ) $(DEBG)
$(RM) $(wildcard *.o) $(wildcard *~) $(MAIN) $(MAIN_ARM64) $(MAIN_ARMHF) $(MAIN_AMD64) $(SLOG) $(SLOG_ARM64) $(SLOG_ARMHF) $(SLOG_AMD64) $(MAIN_U) $(PLAY) $(PL_EXOBJ) $(LOGR) $(PLAY) $(DEBG)
$(RM) *.o *~ $(MAIN) $(MAIN_U) $(PLAY) $(PL_EXOBJ) $(DEBG) $(DDEVICE)
$(RM) $(wildcard *.o) $(wildcard *~) $(MAIN) $(MAIN_ARM64) $(MAIN_ARMHF) $(MAIN_AMD64) $(SLOG) $(DDEVICE) $(SLOG_ARM64) $(SLOG_ARMHF) $(SLOG_AMD64) $(MAIN_U) $(PLAY) $(PL_EXOBJ) $(LOGR) $(PLAY) $(DEBG)
clean-buildfiles:
$(RM) $(wildcard *.o) $(wildcard *~) $(OBJ_FILES) $(DBG_OBJ_FILES) $(SL_OBJ_FILES) $(OBJ_FILES_ARMHF) $(OBJ_FILES_ARM64) $(OBJ_FILES_AMD64) $(SL_OBJ_FILES_ARMHF) $(SL_OBJ_FILES_ARM64) $(SL_OBJ_FILES_AMD64)
$(RM) $(wildcard *.o) $(wildcard *~) $(OBJ_FILES) $(DBG_OBJ_FILES) $(SL_OBJ_FILES) $(DD_OBJ_FILES) $(OBJ_FILES_ARMHF) $(OBJ_FILES_ARM64) $(OBJ_FILES_AMD64) $(SL_OBJ_FILES_ARMHF) $(SL_OBJ_FILES_ARM64) $(SL_OBJ_FILES_AMD64)

View File

@ -131,6 +131,10 @@ NEED TO FIX FOR THIS RELEASE.
* Check SWG messages like "#1 TruClear", see log in this post https://github.com/sfeakes/AqualinkD/discussions/388
* Finish off heat pump / chiller. Probably use a thermostat for both with heat going to heater SP can cool to chiller SP
* Hide passwords (bitmask)
* Startup config validity checks
* remove "extended_programming", or hide it.
-->
@ -143,7 +147,7 @@ NEED TO FIX FOR THIS RELEASE.
* 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)
* Added support for Heat Pump / Chiller support.
# Updates in 2.5.0

Binary file not shown.

Binary file not shown.

View File

@ -1,12 +1,13 @@
# aqualinkd.conf
#
# The directory where the web files are stored
web_directory=/var/www/aqualinkd/
# The socket port that the daemon listens to
# If you change this from 80, remember to update aqualink.service.avahi
socket_port=80
# Log to file, comment out if you do not want to log to file
#log_file=/var/log/aqualinkd.log
# The serial port the daemon access to read the Aqualink RS8
serial_port=/dev/ttyUSB0
# The log level. [DEBUG_DERIAL, DEBUG, INFO, NOTICE, WARNING, ERROR]
# Pick the highest level, and all levels below will be sent to syslog.
@ -21,19 +22,9 @@ web_directory=/var/www/aqualinkd/
log_level=NOTICE
#log_level=WARNING
# Display any ERROR & Warning messages in web interface.
display_warnings_in_web=true
# The directory where the web files are stored
web_directory=/var/www/aqualinkd/
# The socket port that the daemon listens to
# If you change this from 80, remember to update aqualink.service.avahi
socket_port=80
# Note on serial port below. If you want aqualinkd to start and run without connecting to a panel or port
# use 0x00 for device_id and a dummy serial_port like /dev/tty0.
# The serial port the daemon access to read the Aqualink RS8
serial_port=/dev/ttyUSB0
# Your RS panel size. ie 4, 6, 8, 12 or 16 relates to RS4, RS6, RS8, RS12 or RS16.
# VERY important that you select 12 or 16, if you have either of those size panels.
@ -63,6 +54,7 @@ panel_type = RS-8 Combo
# Working RS ID's are 0x0a 0x0b 0x09 0x08 <- 0x08 is usually taken
# If your panel is a PDA only model, then PDA device ID's are 0x60, 0x61, 0x62, 0x63.
# (These are NOT recomended to use unless you absolutly have no other option)
# Use 0xFF to let Aqualink auto configure all the ID's device_id, rssa_device_id, extended_device_id
device_id=0xFF
@ -80,21 +72,16 @@ device_id=0xFF
# If using 0x30 to 0x33 for extended_device_id, then enable below if you want to use virtual buttons
#enable_iaqualink=yes
# If you have extended_device_id set, then you can also use that ID for programming some features.
# This means that you can turn things on/off while AqualinkD is programming certian features.
# If you are using Aqualink Touch protocol for extended_device_id then this is highly recomended
# as it will speed up programming substantially. if One Touch it's 50/50.
#extended_device_id_programming = yes
# Read information from these devices directly from the RS485 bus as well as control panel.
# Read information from these devices directly from the RS485 bus as well as control panel. This will
# give you quicker updates and more information.
# swg = Salt Water Generator
# ePump = Jandy ePump or ePump AC
# vsfPump = Pentair VS,VF,VSF pump
# JXi = Jandy JXi heater (might also be LXi heaters)
# LX = Jandy LX & LT heaters
# Chem = Jandy Chemical Feeder
# iAqualink = Read iAqualink2 (wifi device). Only relivent in PDA mode IF you have iAqualink2/3 device
# iAqualink = Read iAqualink2 (wifi device). Only relevant in PDA mode IF you have iAqualink2/3 device
# HeatPump = Heatpumps.
#read_RS485_swg = yes
#read_RS485_ePump = yes
#read_RS485_vsfPump = yes
@ -102,61 +89,18 @@ device_id=0xFF
#read_RS485_LX = yes
#read_RS485_Chem = yes
#read_RS485_iAqualink = yes
#read_RS485_HeatPump = yes
# Keep the panel time synced with systemtime. Make sure to set systemtime / NTP correctly.
keep_paneltime_synced = yes
# If equiptment is in freeze protect mode some commands like pump_off / spa_on are
# ignored. You can force these to work by setting the below.
override_freeze_protect = no
# AqualinkD will start with no extra devices by default, and once it notices the device it will add it.
# This is not so good for automation hubs (Homekit / HomeAssistant etc), these options will force AqualinkD
# to start with these devides.
#force_swg = yes
#force_ps_setpoints = yes
#force_frzprotect_setpoints = yes
#force_chem_feeder = yes
#force_chiller = yes
# Convert Deg F to Deg C when posting to Domoticz or MQTT.
# If using homebridge-aqualinkd convert_mqtt_temp_to_c must be set to yes.
convert_mqtt_temp_to_c = yes
convert_dz_temp_to_c = yes
# default is to use pool water temp as spa water temp when spa is off (and there for not able to report water temp)
# enable below to report 0 as the spa temp when spa is off.
# This is for MQTT cnnections only, WEB socket and WEB API always report TEMP_UNKNOWN (-999) allowing the consumer to
# decide how to report.
report_zero_spa_temp = yes
# When pool or spa is off, report 0deg for water temp. If set to no, last known value will be used.
report_zero_pool_temp = yes
report_zero_spa_temp = yes
# mqtt stuff
#mqtt_address = localhost:1883
#mqtt_user = someusername
#mqtt_passwd = somepassword
#mqtt_dz_pub_topic = domoticz/in
#mqtt_dz_sub_topic = domoticz/out
#mqtt_aq_topic = aqualinkd
#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
# Please see forum for this, only set to yes when logging information to support new devices. (or debugging protocol)
# Information will be written to /tmp/RS485.log & /tmp/RS485_raw.log respectively
#debug_RSProtocol_packets = no
#debug_RSProtocol_bytes = no
# Log any packets from this device.
#serial_debug_filter = 0x00
# Will change how RS485 / Serial works, Only use if asked to for problem solving purposes.
# Delay between RS485 frame (set or packets that make up a command), reply too quickly can
# cause slow panels (like PDA only) issues, reply too slowly and the control panel will think we are
# 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.
#ftdi_low_latency = no
# Enable AqualinkD scheduler.
# A version of cron that supports cron.d must be installed for the scheduler to work.
@ -168,11 +112,42 @@ enable_scheduler = yes
# Example below is if pump is off due to power reset, freezeprotect or swg boots is turned off between 6am and 11pm then turn the pump on.
# You can leave scheduler_check_pumpon_hour & scheduler_check_pumpoff_hour commented out and AqualinkD will try to find the hours from the actual schedule
# that's been set in scheduler. This only works if you have the same schedule for every day of the week.
#scheduler_check_pumpon_hour = 6
#scheduler_check_pumpoff_hour = 23
#scheduler_check_poweron = yes
#scheduler_check_freezeprotectoff = yes
#scheduler_check_boostoff = yes
#event_check_use_scheduler_times = NO
#event_poweron_check_pump = YES
#event_freezeprotectoff_check_pump = YES
#event_boostoff_check_pump = YES
#event_check_pumpon_hour = 6
#event_check_pumpoff_hour = 24
# Set the RS485 adapter into low latency mode (of supported)
ftdi_low_latency=YES
# Will change how RS485 / Serial works, Only use if asked to for problem solving purposes.
# Delay between RS485 frame (set or packets that make up a command), reply too quickly can
# cause slow panels (like PDA only) issues, reply too slowly and the control panel will think we are
# 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=10
# Keep the panel time synced with systemtime. Make sure to set systemtime / NTP correctly.
sync_panel_time = yes
# Display any warnings in web UI
display_warnings_in_web = yes
# If equiptment is in freeze protect mode some commands like pump_off / spa_on are
# ignored. You can force these to work by setting the below.
#override_freeze_protect = yes
# default is to use pool water temp as spa water temp when spa is off (and there for not able to report water temp)
# enable below to report 0 as the spa temp when spa is off.
# This is for MQTT cnnections only, WEB socket and WEB API always report TEMP_UNKNOWN (-999) allowing the consumer to
# decide how to report.
report_zero_spa_temp = yes
# When pool or spa is off, report 0deg for water temp. If set to no, last known value will be used.
report_zero_pool_temp = yes
# Put AqualinkD to sleep when in PDA mode after inactivity.
# Ignore if you are not using PDA mode.
@ -180,54 +155,44 @@ enable_scheduler = yes
# If you don't have a Jandy PDA leave this at no as AqualinkD will be a lot quicker.
#pda_sleep_mode = yes
# If you have a SWG connected to the control panel, set this to yes.
# AqualinkD can only detect a SWG if it's on, so after a restart you will not see/access a SWG until the the next time the pump is on.
force_SWG = no
# AqualinkD can take sime time to find heater setpoints (if they exist), This will force the pool & spa
# heaters to be listed as thermostats vs switches on startup, helps with homekit.
force_PS_setpoints = no
# AqualinkD can take sime time to find freeze protect (if panel supports it), This will force the freeze protect
# to be listed as thermostat on startup.
force_Frzprotect_setpoints = no
# AqualinkD can take sime time to find chemical feeder (if panel supports it), This will force the chemical feeder
# to be listed on startup.
force_chem_feeder = no
# Lights can be programmed by control panel or AqualinkD (if controlpanel doesn;t support specific light or light mode you want)
# Lights can be programmed by control panel or AqualinkD (if controlpanel doesn't support specific light or light mode you want)
# IF YOU WANT AQUALINKD TO PROGRAM THE LIGHT, IT MUST NOT BE CONFIGURED AS A COLOR LIGHT IN THE JANDY CONTROL PANEL.
# Light probramming mode. 0=safe mode, but slow.
# any number greater is seconds to wait between button presses.
# 0.4 seems to be the minimum. (workd for light modes below 10 presses)
# 0.6 seems to work about 95% of the time, but above 20 presses can be hit or miss.
# 0 will simply wait for the controler to send the response back before sending the next, so is equivelent to about 1.2
light_programming_mode=0
#light_programming_mode=0
# Light programming assumes light needs to be on before sending pulse (above setting)
# If the light is off when request is made to change "light show", then the below value are used
light_programming_initial_on=15
#light_programming_initial_on=15
# Turn the light off for below time before start programmig puleses.
light_programming_initial_off=12
#light_programming_initial_off=12
# If AqualinkD is programming the lights (and not control panel), set the light names / modes below/.
#light_program_01=Voodoo Lounge - show
#light_program_02=Blue Sea
#light_program_03=Royal Blue
#light_program_04=Afternoon Skies
#light_program_05=Aqua Green
#light_program_06=Emerald
#light_program_07=Cloud White
#light_program_08=Warm Red
#light_program_09=Flamingo
#light_program_10=Vivid Violet
#light_program_11=Sangria
#light_program_12=Twilight - show
#light_program_13=Tranquility - show
#light_program_14=Gemstone - show
#light_program_15=USA - show
#light_program_16=Mardi Gras - show
#light_program_17=Cool Cabaret - show
# Everything below here, if it ends with dzidx, then that's the ID for domoticz,
# so not needed if you are not suing dooticz.
# Domoticz ID's for temps.
# All below are Virtual Sensors
#air_temp_dzidx=0
#pool_water_temp_dzidx=0
#spa_water_temp_dzidx=0
#SWG_percent_dzidx=0
#SWG_PPM_dzidx=0
# Must be Virtual Alert Sensor
#SWG_Status_dzidx=0
# Use/find labels from Control Panel, these will overwrite the button_xx_label below,
# it noes NOT work in PDA mode, and it also considerable slows down AqualinkD startup process.
use_panel_aux_labels=no
# These are all the button labels / options / pump and light configurations you want to use.
# Simply change these to your setup, valid options for wach button are :-
@ -285,31 +250,29 @@ use_panel_aux_labels=no
# button_xx_lightMode = (0=Aqualink program, 1=Jandy, 2=Jandy LED, 3=SAm/SAL, 4=Color Logic, 5=Intellibrite, 6=Hayw Univ Color, 7,8,9(future), 10=Dimmer, 11=Full Range Dimmer)
#
# Below are settings for standard buttons on RS-8 Combo panel used as example.
button_01_label=Filter Pump
#
#button_01_label=Filter Pump
#button_01_pumpIndex=1
#button_01_pumpID=0x78
#button_01_pumpName=Intelliflo VS 1
#button_01_pumpType=Pentair VS
button_02_label=Spa
#button_02_label=Spa
#button_03_label=Cleaner
#button_04_label=Waterfall
#button_05_label=Spa Blower
button_03_label=Cleaner
#button_06_label=Pool Light
#button_06_lightMode=2
button_04_label=Waterfall
#button_07_label=Spa Light
#button_07_lightMode=2
button_05_label=Spa Blower
button_06_label=Pool Light
#button_05_lightMode=0
button_07_label=Spa Light
#button_05_lightMode=0
button_08_label=NONE
button_09_label=NONE
button_10_label=Pool Heater
button_11_label=Spa Heater
button_12_label=Solar Heater
#button_08_label=NONE\
#button_09_label=NONE
#button_10_label=Pool Heater
#button_11_label=Spa Heater
#button_12_label=Solar Heater
# Virtual buttons.
# To use these you must have extended_device_id set to AqualnkTouch protocol, ie 0x31, 0x31, 0x32, 0x33
@ -339,3 +302,5 @@ button_12_label=Solar Heater
#sensor_02_path = /sys/class/thermal/thermal_zone1/temp
#sensor_02_label = GPU
#sensor_02_factor = 0.001

View File

@ -129,11 +129,12 @@ else
fi
fi
# V2.3.9 has kind-a breaking change for config.js, so check existing and rename if needed
# V2.3.9 & V2.6.0 has kind-a breaking change for config.js, so check existing and rename if needed
# we added Aux_V? to the button list
if [ -f "$WEBLocation/config.js" ]; then
# Test is if has AUX_V1 in file AND "Spa" is in file (Spa_mode changed to Spa)
if ! grep -q 'Aux_V1' $WEBLocation/$file || ! grep -q '"Spa"' $WEBLocation/$file; then
# Version 2.6.0 added Chiller as well
if ! grep -q 'Aux_V1' $WEBLocation/$file || ! grep -q '"Spa"' $WEBLocation/$file || ! grep -q '"Chiller"' $WEBLocation/$file; then
dateext=`date +%Y%m%d_%H_%M_%S`
echo "AqualinkD web config is old, making copy to $WEBLocation/config.js.$dateext"
echo "Please make changes to new version $WEBLocation/config.js"

Binary file not shown.

Binary file not shown.

View File

@ -349,11 +349,20 @@ aqkey *addVirtualButton(struct aqualinkdata *aqdata, char *label, int vindex) {
int index = vindex;
// vindex 0 means find first index.
if (vindex == 0) {
printf(" TOTAL=%d VSTART=%d\n",aqdata->total_buttons,aqdata->virtual_button_start);
index = aqdata->total_buttons - aqdata->virtual_button_start + 1;
//printf(" TOTAL=%d VSTART=%d\n",aqdata->total_buttons,aqdata->virtual_button_start);
//index = aqdata->total_buttons - aqdata->virtual_button_start + 1;
if (aqdata->virtual_button_start <= 0) {
// Their are no vbuttons, so start at 1.
index = 1;
} else {
index = aqdata->total_buttons - aqdata->virtual_button_start + 1;
}
printf("TOTAL=%d VSTART=%d INDEXNAME=%d\n",aqdata->total_buttons,aqdata->virtual_button_start,index);
}
if (aqdata->virtual_button_start <= 0) {
aqdata->virtual_button_start = aqdata->total_buttons;
@ -452,7 +461,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo
int index = 0;
aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[7-1];
aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aqdata->aqbuttons[index].label = rs?name2label(BTN_PUMP):BTN_PDA_PUMP;
aqdata->aqbuttons[index].label = rs?name2label(BTN_PUMP):cleanalloc(BTN_PDA_PUMP);
aqdata->aqbuttons[index].name = BTN_PUMP;
aqdata->aqbuttons[index].code = KEY_PUMP;
aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
@ -463,7 +472,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo
if (combo) {
aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[6-1];
aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aqdata->aqbuttons[index].label = rs?name2label(BTN_SPA):BTN_PDA_SPA;
aqdata->aqbuttons[index].label = rs?name2label(BTN_SPA):cleanalloc(BTN_PDA_SPA);
aqdata->aqbuttons[index].name = BTN_SPA;
aqdata->aqbuttons[index].code = KEY_SPA;
aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
@ -474,7 +483,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo
aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[5-1];
aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aqdata->aqbuttons[index].label = rs?name2label(BTN_AUX1):BTN_PDA_AUX1;
aqdata->aqbuttons[index].label = rs?name2label(BTN_AUX1):cleanalloc(BTN_PDA_AUX1);
aqdata->aqbuttons[index].name = BTN_AUX1;
aqdata->aqbuttons[index].code = KEY_AUX1;
aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
@ -484,7 +493,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo
aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[4-1];
aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aqdata->aqbuttons[index].label = rs?name2label(BTN_AUX2):BTN_PDA_AUX2;
aqdata->aqbuttons[index].label = rs?name2label(BTN_AUX2):cleanalloc(BTN_PDA_AUX2);
aqdata->aqbuttons[index].name = BTN_AUX2;
aqdata->aqbuttons[index].code = KEY_AUX2;
aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
@ -494,7 +503,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo
aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[3-1];
aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aqdata->aqbuttons[index].label = rs?name2label(BTN_AUX3):BTN_PDA_AUX3;
aqdata->aqbuttons[index].label = rs?name2label(BTN_AUX3):cleanalloc(BTN_PDA_AUX3);
aqdata->aqbuttons[index].name = BTN_AUX3;
aqdata->aqbuttons[index].code = KEY_AUX3;
aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
@ -506,7 +515,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo
if (size >= 6) {
aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[9-1];
aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aqdata->aqbuttons[index].label = rs?name2label(BTN_AUX4):BTN_PDA_AUX4;
aqdata->aqbuttons[index].label = rs?name2label(BTN_AUX4):cleanalloc(BTN_PDA_AUX4);
aqdata->aqbuttons[index].name = BTN_AUX4;
aqdata->aqbuttons[index].code = KEY_AUX4;
aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
@ -516,7 +525,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo
aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[8-1];
aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aqdata->aqbuttons[index].label = rs?name2label(BTN_AUX5):BTN_PDA_AUX5;
aqdata->aqbuttons[index].label = rs?name2label(BTN_AUX5):cleanalloc(BTN_PDA_AUX5);
aqdata->aqbuttons[index].name = BTN_AUX5;
aqdata->aqbuttons[index].code = KEY_AUX5;
aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
@ -528,7 +537,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo
if (size >= 8) {
aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[12-1];
aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aqdata->aqbuttons[index].label = rs?name2label(BTN_AUX6):BTN_PDA_AUX6;
aqdata->aqbuttons[index].label = rs?name2label(BTN_AUX6):cleanalloc(BTN_PDA_AUX6);
aqdata->aqbuttons[index].name = BTN_AUX6;
aqdata->aqbuttons[index].code = KEY_AUX6;
aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
@ -538,7 +547,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo
aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[1-1];
aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aqdata->aqbuttons[index].label = rs?name2label(BTN_AUX7):BTN_PDA_AUX7;
aqdata->aqbuttons[index].label = rs?name2label(BTN_AUX7):cleanalloc(BTN_PDA_AUX7);
aqdata->aqbuttons[index].name = BTN_AUX7;
aqdata->aqbuttons[index].code = KEY_AUX7;
aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
@ -668,7 +677,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo
aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[15-1];
aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aqdata->aqbuttons[index].label = rs?name2label(combo?BTN_POOL_HTR:BTN_TEMP1_HTR):BTN_PDA_POOL_HTR;
aqdata->aqbuttons[index].label = rs?name2label(combo?BTN_POOL_HTR:BTN_TEMP1_HTR):cleanalloc(BTN_PDA_POOL_HTR);
aqdata->aqbuttons[index].name = BTN_POOL_HTR;
aqdata->aqbuttons[index].code = KEY_POOL_HTR;
aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
@ -678,7 +687,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo
aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[17-1];
aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aqdata->aqbuttons[index].label = rs?name2label(combo?BTN_SPA_HTR:BTN_TEMP2_HTR):BTN_PDA_SPA_HTR;
aqdata->aqbuttons[index].label = rs?name2label(combo?BTN_SPA_HTR:BTN_TEMP2_HTR):cleanalloc(BTN_PDA_SPA_HTR);
aqdata->aqbuttons[index].name = BTN_SPA_HTR;
aqdata->aqbuttons[index].code = KEY_SPA_HTR;
aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
@ -688,7 +697,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo
aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[19-1];
aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aqdata->aqbuttons[index].label = rs?name2label(BTN_EXT_AUX):BTN_PDA_EXT_AUX;
aqdata->aqbuttons[index].label = rs?name2label(BTN_EXT_AUX):cleanalloc(BTN_PDA_EXT_AUX);
aqdata->aqbuttons[index].name = BTN_EXT_AUX;
aqdata->aqbuttons[index].code = KEY_EXT_AUX;
aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;

View File

@ -517,6 +517,12 @@ void _aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_dat
else if ((isIAQT_ENABLED && isEXTP_ENABLED) || isPDA_IAQT) {
// IAQ Touch programming modes that should overite standard ones.
switch (r_type){
case AQ_SET_CHILLER_TEMP:
//if (isIAQL_ACTIVE)
// type = AQ_SET_IAQLINK_CHILLER_TEMP; // This might work on rev Yg, but not on T2
//else
type = AQ_SET_IAQTOUCH_CHILLER_TEMP;
break;
case AQ_GET_POOL_SPA_HEATER_TEMPS:
//case AQ_GET_FREEZE_PROTECT_TEMP:
type = AQ_GET_IAQTOUCH_SETPOINTS;
@ -528,10 +534,16 @@ void _aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_dat
type = AQ_SET_IAQTOUCH_SWG_BOOST;
break;
case AQ_SET_POOL_HEATER_TEMP:
type = AQ_SET_IAQTOUCH_POOL_HEATER_TEMP;
if (isIAQL_ACTIVE)
type = AQ_SET_IAQLINK_POOL_HEATER_TEMP;
else
type = AQ_SET_IAQTOUCH_POOL_HEATER_TEMP;
break;
case AQ_SET_SPA_HEATER_TEMP:
type = AQ_SET_IAQTOUCH_SPA_HEATER_TEMP;
if (isIAQL_ACTIVE)
type = AQ_SET_IAQLINK_SPA_HEATER_TEMP;
else
type = AQ_SET_IAQTOUCH_SPA_HEATER_TEMP;
break;
case AQ_SET_TIME:
type = AQ_SET_IAQTOUCH_SET_TIME;
@ -654,11 +666,15 @@ void _aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_dat
return; // No need to create this as thread.
break;
case AQ_SET_IAQLINK_POOL_HEATER_TEMP:
set_iaqualink_heater_setpoint(atoi(args), true);
set_iaqualink_heater_setpoint(atoi(args), SP_POOL);
return; // No need to create this as thread.
break;
case AQ_SET_IAQLINK_SPA_HEATER_TEMP:
set_iaqualink_heater_setpoint(atoi(args), false);
set_iaqualink_heater_setpoint(atoi(args), SP_SPA);
return; // No need to create this as thread.
break;
case AQ_SET_IAQLINK_CHILLER_TEMP:
set_iaqualink_heater_setpoint(atoi(args), SP_CHILLER);
return; // No need to create this as thread.
break;
default:
@ -903,6 +919,9 @@ const char *ptypeName(program_type type)
case AQ_SET_IAQLINK_SPA_HEATER_TEMP:
return "Set iAqualink Pool Heater";
break;
case AQ_SET_IAQLINK_CHILLER_TEMP:
return "Set iAqualink Chiller Heater";
break;
#ifdef AQ_PDA
case AQ_PDA_INIT:

View File

@ -62,6 +62,7 @@ typedef enum {
AQ_SET_BOOST,
AQ_SET_PUMP_RPM,
AQ_SET_PUMP_VS_PROGRAM,
AQ_SET_CHILLER_TEMP,
// ******** PDA Delimiter make sure to change MAX/MIN below
AQ_PDA_INIT,
AQ_PDA_WAKE_INIT,
@ -106,6 +107,7 @@ typedef enum {
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_IAQLINK_CHILLER_TEMP,
AQ_SET_IAQTOUCH_LIGHTCOLOR_MODE,
// ******** RS Serial Adapter Delimiter make sure to change MAX/MIN below
AQ_GET_RSSADAPTER_SETPOINTS,
@ -118,7 +120,7 @@ typedef enum {
#define AQ_SET_CHILLER_TEMP AQ_SET_IAQTOUCH_CHILLER_TEMP
//#define AQ_SET_CHILLER_TEMP AQ_SET_IAQTOUCH_CHILLER_TEMP

View File

@ -31,6 +31,7 @@
#include "config.h"
#include "packetLogger.h"
#include "timespec_subtract.h"
#include "aqualink.h"
/*
Notes for serial usb speed
@ -304,6 +305,13 @@ const char* get_jandy_packet_type(unsigned char* packet , int length)
return "iAqalnk Poll";
break;
case CMD_IAQ_CMD_READY:
return "iAqalnk rec ready";
break;
case CMD_IAQ_CTRL_READY:
return "iAq receive ready";
break;
default:
sprintf(buf, "Unknown '0x%02hhx'", packet[PKT_CMD]);
return buf;
@ -544,13 +552,23 @@ int lock_port(int fd, const char* tty)
int unlock_port(int fd)
{
if (ioctl(fd, TIOCNXCL) < 0) {
LOG(RSSD_LOG,LOG_ERR, "Failed to remove into exclusive mode (%d): %s\n", errno, strerror( errno ));
}
if (flock(fd, LOCK_UN) < 0) {
LOG(RSSD_LOG,LOG_ERR, "Can't unlock serial port (%d): %s\n",errno, strerror( errno ));
//if (!isAqualinkDStopping()) {
LOG(RSSD_LOG,LOG_ERR, "Can't unlock serial port (%d): %s\n",errno, strerror( errno ));
//}
return -1;
}
return 0;
}
int is_valid_port(int fd) {
return fcntl(fd, F_GETFD) != -1 || errno != EBADF;
}
// https://www.cmrr.umn.edu/~strupp/serial.html#2_5_2
// http://unixwiz.net/techtips/termios-vmin-vtime.html
@ -565,7 +583,9 @@ int _init_serial_port(const char* tty, bool blocking, bool readahead)
_blocking_mode = blocking;
int fd = open(tty, O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY);
//int fd = open(tty, O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY);
int fd = open(tty, O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY | O_CLOEXEC);
//int fd = open(tty, O_RDWR | O_NOCTTY | O_SYNC); // This is way to slow at reading
if (fd < 0) {
LOG(RSSD_LOG,LOG_ERR, "Unable to open port: %s, error %d\n", tty, errno);
@ -655,6 +675,11 @@ void close_blocking_serial_port()
/* close tty port */
void close_serial_port(int fd)
{
if ( fcntl(fd, F_GETFD, 0) == -1 || errno == EBADF ) {
// Looks like bad fd or already closed. return with no error since we can get called twice
return;
}
unlock_port(fd);
tcsetattr(fd, TCSANOW, &_oldtio);
close(fd);
@ -1137,13 +1162,17 @@ int get_packet(int fd, unsigned char* packet)
} else if(bytesRead < 0) {
// Got a read error. Wait one millisecond for the next byte to
// arrive.
LOG(RSSD_LOG,LOG_WARNING, "Read error: %d - %s\n", errno, strerror(errno));
if(errno == 9) {
if (! isAqualinkDStopping() ) {
LOG(RSSD_LOG,LOG_WARNING, "Read error: %d - %s\n", errno, strerror(errno));
if(errno == 9) {
// Bad file descriptor. Port has been disconnected for some reason.
// Return a -1.
return AQSERR_READ;
return AQSERR_READ;
}
delay(100);
} else {
return 0;
}
delay(100);
}
// Break out of the loop if we exceed maximum packet

View File

@ -422,6 +422,8 @@ SPILLOVER IS DISABLED WHILE SPA IS ON
#define CMD_IAQ_MAIN_STATUS 0x70
#define CMD_IAQ_1TOUCH_STATUS 0x71
#define CMD_IAQ_AUX_STATUS 0x72 // Get this on AqualinkTouch protocol when iAqualink protocol sends 0x18 (get aux status I assume)
#define CMD_IAQ_CMD_READY 0x73 // iAqualink ready to receive command
/*
#define CMD_IAQ_MSG_3 0x2d // Equiptment status message??
#define CMD_IAQ_0x31 0x31 // Some pump speed info
@ -575,6 +577,7 @@ void send_extended_ack(int fd, unsigned char ack_type, unsigned char command);
//void send_cmd(int file_descriptor, unsigned char cmd, unsigned char args);
int get_packet(int file_descriptor, unsigned char* packet);
//int get_packet_lograw(int fd, unsigned char* packet);
int is_valid_port(int fd);
//int get_packet_new(int fd, unsigned char* packet);
//int get_packet_new_lograw(int fd, unsigned char* packet);

View File

@ -45,6 +45,8 @@
void intHandler(int dummy);
bool isAqualinkDStopping();
#ifdef AQ_PDA
bool checkAqualinkTime(); // Only need to externalise this for PDA
#endif
@ -182,6 +184,13 @@ typedef enum simulator_type {
} simulator_type;
*/
// Set Point types
typedef enum SP_TYPE{
SP_POOL,
SP_SPA,
SP_CHILLER
} SP_TYPE;
//#define PUMP_PRIMING -1
//#define PUMP_OFFLINE -2
//#define PUMP_ERROR -3

View File

@ -56,6 +56,7 @@
#include "simulator.h"
#include "debug_timer.h"
#include "aq_scheduler.h"
#include "json_messages.h"
#ifdef AQ_MANAGER
#include "serial_logger.h"
@ -94,6 +95,11 @@ bool _cmdln_nostartupcheck = false;
void main_loop();
int startup(char *self, char *cfgFile);
bool isAqualinkDStopping() {
return !_keepRunning;
}
void intHandler(int sig_num)
{
LOG(AQUA_LOG,LOG_WARNING, "Stopping!\n");
@ -483,11 +489,7 @@ int main(int argc, char *argv[])
else if (strcmp(argv[i], "-rsrd") == 0)
{
_cmdln_lograwRS485 = true;
}
else if (strcmp(argv[i], "-nc") == 0)
{
_cmdln_nostartupcheck = true;
}
}
}
// Set this here, so it doesn;t get reset if the manager restarts the AqualinkD process.
@ -505,6 +507,8 @@ int startup(char *self, char *cfgFile)
AddAQDstatusMask(CHECKING_CONFIG);
AddAQDstatusMask(NOT_CONNECTED);
_aqualink_data.updated = true;
//_aqualink_data.chiller_button == NULL; // HATE having this here, but needs to be null before config.
sprintf(_aqualink_data.self, basename(self));
clearDebugLogMask();
@ -693,6 +697,8 @@ void caculate_ack_packet(int rs_fd, unsigned char *packet_buffer, emulation_type
}
#define MAX_AUTO_PACKETS 1200
bool auto_configure(unsigned char* packet) {
// Loop over PROBE packets and store any we can use,
// once we see the 2nd probe of any ID we fave stored, then the loop is complete,
@ -709,6 +715,12 @@ bool auto_configure(unsigned char* packet) {
static unsigned char lastID = 0x00;
static bool seen_iAqualink2 = false;
static int foundIDs = 0;
static int packetsReceived=0;
if (++packetsReceived >= MAX_AUTO_PACKETS ) {
LOG(AQUA_LOG,LOG_ERR, "Received %d packets, and didn't get a full probe cycle, stoping Auto Configure!\n",packetsReceived);
return true;
}
if ( packet[PKT_CMD] == CMD_PROBE ) {
LOG(AQUA_LOG,LOG_INFO, "Got Probe on ID 0x%02hhx\n",packet[PKT_DEST]);
@ -727,7 +739,9 @@ bool auto_configure(unsigned char* packet) {
_aqconfig_.device_id = 0x00;
_aqconfig_.rssa_device_id = 0x00;
_aqconfig_.extended_device_id = 0x00;
_aqconfig_.extended_device_id_programming = false;
AddAQDstatusMask(AUTOCONFIGURE_ID);
_aqualink_data.updated = true;
//AddAQDstatusMask(AUTOCONFIGURE_PANEL); // Not implimented yet.
}
@ -768,6 +782,7 @@ bool auto_configure(unsigned char* packet) {
LOG(AQUA_LOG,LOG_NOTICE, "Finished Autoconfigure using device_id=0x%02hhx rssa_device_id=0x%02hhx extended_device_id=0x%02hhx (%s iAqualink2/3)\n",
_aqconfig_.device_id,_aqconfig_.rssa_device_id,_aqconfig_.extended_device_id, _aqconfig_.enable_iaqualink?"Enable":"Disable");
RemoveAQDstatusMask(AUTOCONFIGURE_ID);
_aqualink_data.updated = true;
return true; // we can exit finally.
}
@ -833,6 +848,7 @@ void main_loop()
//_aqualink_data.panelstatus = STARTING;
AddAQDstatusMask(CHECKING_CONFIG);
_aqualink_data.updated = true;
sprintf(_aqualink_data.last_display_message, "%s", "Connecting to Control Panel");
_aqualink_data.is_display_message_programming = false;
//_aqualink_data.simulate_panel = false;
@ -863,6 +879,7 @@ void main_loop()
_aqualink_data.simulator_active = SIM_NONE;
_aqualink_data.boost_duration = 0;
_aqualink_data.boost = false;
pthread_mutex_init(&_aqualink_data.active_thread.thread_mutex, NULL);
pthread_cond_init(&_aqualink_data.active_thread.thread_cond, NULL);
@ -927,17 +944,25 @@ void main_loop()
rs_fd = init_serial_port(_aqconfig_.serial_port);
/*
if (rs_fd == -1) {
LOG(AQUA_LOG,LOG_ERR, "Error Aqualink setting serial port: %s\n", _aqconfig_.serial_port);
//_aqualink_data.panelstatus = SERIAL_ERROR;
AddAQDstatusMask(ERROR_SERIAL);
_aqualink_data.updated = true;
#ifndef AQ_CONTAINER
exit(EXIT_FAILURE);
#endif
} else {
//AddAQDstatusMask(CHECKING_CONFIG);
}
LOG(AQUA_LOG,LOG_NOTICE, "Listening to Aqualink RS8 on serial port: %s\n", _aqconfig_.serial_port);
*/
if (is_valid_port(rs_fd)) {
LOG(AQUA_LOG,LOG_NOTICE, "Listening to Aqualink RS8 on serial port: %s\n", _aqconfig_.serial_port);
} else {
LOG(AQUA_LOG,LOG_ERR, "Error Aqualink bad serial port: %s\n", _aqconfig_.serial_port);
AddAQDstatusMask(ERROR_SERIAL);
}
if (!serial_blockingmode())
blank_read_reconnect = MAX_ZERO_READ_BEFORE_RECONNECT_NONBLOCKING;
@ -957,6 +982,7 @@ void main_loop()
// Set probes to true for any device we are not searching for.
RemoveAQDstatusMask(CHECKING_CONFIG);
_aqualink_data.updated = true;
if (_aqconfig_.rssa_device_id == 0x00)
got_probe_rssa = true;
@ -969,27 +995,29 @@ void main_loop()
}
if (_aqconfig_.device_id == 0xFF) {
LOG(AQUA_LOG,LOG_NOTICE, "Waiting for Control Panel information\n\n");
LOG(AQUA_LOG,LOG_NOTICE, "Waiting for Control Panel information");
LOG(AQUA_LOG,LOG_WARNING, "Unsing Auto configure, this will take some time, (make sure to undate aqualinkd configuration to speed up startup!)\n");
auto_config_complete = false;
//_aqualink_data.panelstatus = LOOKING_IDS;
AddAQDstatusMask(AUTOCONFIGURE_ID);
_aqualink_data.updated = true;
} else {
LOG(AQUA_LOG,LOG_NOTICE, "Waiting for Control Panel probe\n");
//_aqualink_data.panelstatus = CONECTING;
AddAQDstatusMask(CONNECTING);
_aqualink_data.updated = true;
}
i=0;
// Loop until we get the probe messages, that means we didn;t start too soon after last shutdown.
while ( (got_probe == false || got_probe_rssa == false || got_probe_extended == false || auto_config_complete == false) && _keepRunning == true && _cmdln_nostartupcheck == false)
while ( is_valid_port(rs_fd) && (got_probe == false || got_probe_rssa == false || got_probe_extended == false || auto_config_complete == false) && _keepRunning == true && _cmdln_nostartupcheck == false)
{
if (blank_read == blank_read_reconnect / 2) {
LOG(AQUA_LOG,LOG_ERR, "Nothing read on '%s', are you sure that's right?\n",_aqconfig_.serial_port);
#ifdef AQ_CONTAINER
//#ifdef AQ_CONTAINER
// Reset blank reads here, we want to ignore TTY errors in container to keep it running
blank_read = 1;
#endif
//#endif
if (_aqconfig_.device_id == 0x00) {
blank_read = 1; // if device id=0x00 it's code for don't exit
}
@ -1013,6 +1041,7 @@ void main_loop()
blank_read = 0;
auto_config_complete = auto_configure(packet_buffer);
AddAQDstatusMask(AUTOCONFIGURE_ID);
_aqualink_data.updated = true;
if (auto_config_complete) {
//if (_aqconfig_.device_id != 0x00)
got_probe = true;
@ -1026,6 +1055,7 @@ void main_loop()
if (packet_length > 0 && _aqconfig_.device_id == 0x00) {
blank_read = 0;
AddAQDstatusMask(AUTOCONFIGURE_ID);
_aqualink_data.updated = true;
_aqconfig_.device_id = find_unused_address(packet_buffer);
continue;
}
@ -1104,15 +1134,16 @@ void main_loop()
RemoveAQDstatusMask(AUTOCONFIGURE_ID);
RemoveAQDstatusMask(NOT_CONNECTED);
AddAQDstatusMask(CONNECTING);
_aqualink_data.updated = true;
//At this point we should have correct ID and seen probes on those ID's.
// Setup the panel
if (_aqconfig_.device_id == 0x00) {
if (_aqconfig_.device_id <= 0x08 && _aqconfig_.device_id >= 0x0B && _aqconfig_.device_id != 0x60 && _aqconfig_.device_id != 0x33) {
LOG(AQUA_LOG,LOG_ERR, "Aqualink daemon has no valid device_id, can't connect to control panel");
//_aqualink_data.panelstatus = NO_IDS_ERROR;
RemoveAQDstatusMask(CONNECTING); // Not sure if we should remove this
AddAQDstatusMask(ERROR_NO_DEVICE_ID);
_aqualink_data.updated = true;
}
if (_aqconfig_.rssa_device_id >= 0x48 && _aqconfig_.rssa_device_id <= 0x49) {
@ -1128,6 +1159,7 @@ void main_loop()
// We can only get panel size info from extended ID
if (_aqconfig_.extended_device_id != 0x00) {
RemoveAQDstatusMask(AUTOCONFIGURE_PANEL);
_aqualink_data.updated = true;
}
if (_aqconfig_.extended_device_id_programming == true && (isONET_ENABLED || isIAQT_ENABLED) )
@ -1164,14 +1196,17 @@ void main_loop()
while ((rs_fd < 0 || blank_read >= blank_read_reconnect) && _keepRunning == true)
{
//printf("rs_fd =% d\n",rs_fd);
if (rs_fd < 0)
if (!is_valid_port(rs_fd))
{
sleep(1);
LOG(AQUA_LOG,LOG_ERR, "Bad serial port '%s', are you sure that's right?\n",_aqconfig_.serial_port);
sprintf(_aqualink_data.last_display_message, CONNECTION_ERROR);
LOG(AQUA_LOG,LOG_ERR, "Aqualink daemon waiting to connect to master device...\n");
//LOG(AQUA_LOG,LOG_ERR, "Serial port error, Aqualink daemon waiting to connect to master device...\n");
_aqualink_data.updated = true;
AddAQDstatusMask(ERROR_SERIAL);
broadcast_aqualinkstate_error(CONNECTION_ERROR);
//broadcast_aqualinkstate_error(CONNECTION_ERROR);
broadcast_aqualinkstate_error(getAqualinkDStatusMessage(&_aqualink_data));
sleep(10);
#ifdef AQ_NO_THREAD_NETSERVICE
poll_net_services(1000);
poll_net_services(3000);
@ -1184,11 +1219,12 @@ void main_loop()
LOG(AQUA_LOG,LOG_ERR, "Aqualink daemon looks like serial error, resetting.\n");
_aqualink_data.updated = true;
AddAQDstatusMask(ERROR_SERIAL);
broadcast_aqualinkstate_error(CONNECTION_ERROR);
//broadcast_aqualinkstate_error(CONNECTION_ERROR);
broadcast_aqualinkstate_error(getAqualinkDStatusMessage(&_aqualink_data));
close_serial_port(rs_fd);
rs_fd = init_serial_port(_aqconfig_.serial_port);
//rs_fd = init_serial_port(_aqconfig_.serial_port);
}
rs_fd = init_serial_port(_aqconfig_.serial_port);
blank_read = 0;
}
@ -1243,6 +1279,7 @@ void main_loop()
RemoveAQDstatusMask(ERROR_SERIAL);
RemoveAQDstatusMask(CONNECTING);
AddAQDstatusMask(CONNECTED);
_aqualink_data.updated = true;
DEBUG_TIMER_START(&_rs_packet_timer);
blank_read = 0;

View File

@ -175,6 +175,8 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].name = CFG_N_socket_port;
//_cfgParams[_numCfgParams].advanced = true;
_cfgParams[_numCfgParams].config_mask |= CFG_GRP_ADVANCED;
//_cfgParams[_numCfgParams].config_mask |= CFG_READONLY; // Take out once below is working
_cfgParams[_numCfgParams].config_mask |= CFG_FORCE_RESTART;
_cfgParams[_numCfgParams].default_value = (void *)_dcfg_web_port;
_numCfgParams++;
@ -183,6 +185,8 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].name = CFG_N_serial_port;
//_cfgParams[_numCfgParams].advanced = true;
_cfgParams[_numCfgParams].config_mask |= CFG_GRP_ADVANCED;
//_cfgParams[_numCfgParams].config_mask |= CFG_READONLY; // Take out once below is working
_cfgParams[_numCfgParams].config_mask |= CFG_FORCE_RESTART;
_cfgParams[_numCfgParams].default_value = (void *)_dcfg_serial_port;
_numCfgParams++;
@ -192,6 +196,15 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].valid_values = CFG_V_log_level;
_cfgParams[_numCfgParams].default_value = (void *) &_dcfg_loglevel;
_numCfgParams++;
_cfgParams[_numCfgParams].value_ptr = &_aqconfig_.web_directory;
_cfgParams[_numCfgParams].value_type = CFG_STRING;
_cfgParams[_numCfgParams].name = CFG_N_web_directory;
//_cfgParams[_numCfgParams].advanced = true;
_cfgParams[_numCfgParams].config_mask |= CFG_GRP_ADVANCED;
_cfgParams[_numCfgParams].config_mask |= CFG_READONLY;
_cfgParams[_numCfgParams].default_value = NULL;
_numCfgParams++;
_cfgParams[_numCfgParams].value_ptr = &_aqconfig_.paneltype_mask;
_cfgParams[_numCfgParams].value_type = CFG_SPECIAL;
@ -217,6 +230,7 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].value_type = CFG_BOOL;
_cfgParams[_numCfgParams].name = CFG_N_extended_device_id_programming;
_cfgParams[_numCfgParams].valid_values = CFG_V_BOOL;
_cfgParams[_numCfgParams].config_mask |= CFG_HIDE;
_cfgParams[_numCfgParams].default_value = (void *)&_dcfg_true;
_numCfgParams++;
@ -232,14 +246,6 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].name = CFG_N_enable_iaqualink;
_cfgParams[_numCfgParams].valid_values = CFG_V_BOOL;
_cfgParams[_numCfgParams].default_value = (void *)&_dcfg_false;
_numCfgParams++;
_cfgParams[_numCfgParams].value_ptr = &_aqconfig_.web_directory;
_cfgParams[_numCfgParams].value_type = CFG_STRING;
_cfgParams[_numCfgParams].name = CFG_N_web_directory;
//_cfgParams[_numCfgParams].advanced = true;
_cfgParams[_numCfgParams].config_mask |= CFG_GRP_ADVANCED;
_cfgParams[_numCfgParams].default_value = NULL;
#ifndef AQ_MANAGER
_numCfgParams++;
@ -267,6 +273,7 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].value_ptr = &_aqconfig_.mqtt_passwd;
_cfgParams[_numCfgParams].value_type = CFG_STRING;
_cfgParams[_numCfgParams].name = CFG_N_mqtt_passwd;
_cfgParams[_numCfgParams].config_mask |= CFG_PASSWD_MASK;
_cfgParams[_numCfgParams].default_value = (void *)NULL;
_numCfgParams++;
@ -466,7 +473,7 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].value_type = CFG_BITMASK;
_cfgParams[_numCfgParams].name = CFG_N_force_swg;
_cfgParams[_numCfgParams].mask = FORCE_SWG_SP;
_cfgParams[_numCfgParams].default_value = (void *)&_dcfg_true;
_cfgParams[_numCfgParams].default_value = (void *)&_dcfg_false;
_numCfgParams++;
_cfgParams[_numCfgParams].value_ptr = &_aqconfig_.force_device_devmask;
_cfgParams[_numCfgParams].value_type = CFG_BITMASK;
@ -940,14 +947,21 @@ bool setConfigValue(struct aqualinkdata *aqdata, char *param, char *value) {
return true;
}
if (isMASK_SET(_cfgParams[i].config_mask, CFG_PASSWD_MASK)) {
if (strncmp(value, PASSWD_MASK_TEXT, strlen(PASSWD_MASK_TEXT)) == 0) {
// Don't set password when it's the mask text
return false;
}
}
switch (_cfgParams[i].value_type) {
case CFG_STRING:
if (_cfgParams[i].value_ptr != NULL && *(char **)_cfgParams[i].value_ptr != _cfgParams[i].default_value) {
LOG(AQUA_LOG,LOG_DEBUG,"FREE Memory for config %s %s\n",_cfgParams[i].name, *(char **)_cfgParams[i].value_ptr);
free(*(char **)_cfgParams[i].value_ptr);
*(char **)_cfgParams[i].value_ptr = NULL;
}
*(char **)_cfgParams[i].value_ptr = cleanalloc(value);
if (_cfgParams[i].value_ptr != NULL && *(char **)_cfgParams[i].value_ptr != _cfgParams[i].default_value) {
LOG(AQUA_LOG,LOG_DEBUG,"FREE Memory for config %s %s\n",_cfgParams[i].name, *(char **)_cfgParams[i].value_ptr);
free(*(char **)_cfgParams[i].value_ptr);
*(char **)_cfgParams[i].value_ptr = NULL;
}
*(char **)_cfgParams[i].value_ptr = cleanalloc(value);
break;
case CFG_INT:
*(int *)_cfgParams[i].value_ptr = strtoul(cleanwhitespace(value), NULL, 10);
@ -1546,40 +1560,62 @@ void check_print_config (struct aqualinkdata *aqdata)
// Check chiller
if (ENABLE_CHILLER) {
for (i = 0; i < aqdata->total_buttons; i++)
{
if (isVBUTTON_ALTLABEL(aqdata->aqbuttons[i].special_mask) && (rsm_strmatch(((vbutton_detail *)aqdata->aqbuttons[i].special_mask_ptr)->altlabel, "Chiller") == 0) ){
aqdata->chiller_button = &aqdata->aqbuttons[i];
} else if (isVBUTTON(aqdata->aqbuttons[i].special_mask) && rsm_strmatch(aqdata->aqbuttons[i].label, "Heat Pump") == 0 ) {
LOG(AQUA_LOG,LOG_ERR, "Config error, `%s` is enabled, but Virtual Button Heat Pump does not have alt_name Chiller! Creating.",CFG_N_force_chiller);
setVirtualButtonAltLabel(&aqdata->aqbuttons[i], "Chiller");
aqdata->chiller_button = &aqdata->aqbuttons[i];
aqdata->chiller_button->special_mask |= VIRTUAL_BUTTON_CHILLER;
if (_aqconfig_.extended_device_id >= 0x30 && _aqconfig_.extended_device_id <= 0x33) {
for (i = 0; i < aqdata->total_buttons; i++)
{
if (isVBUTTON_ALTLABEL(aqdata->aqbuttons[i].special_mask) && (rsm_strmatch(((vbutton_detail *)aqdata->aqbuttons[i].special_mask_ptr)->altlabel, "Chiller") == 0) ){
aqdata->chiller_button = &aqdata->aqbuttons[i];
//aqdata->chiller_button->special_mask |= VIRTUAL_BUTTON_CHILLER;
setMASK(aqdata->chiller_button->special_mask, VIRTUAL_BUTTON_CHILLER);
} else if (isVBUTTON(aqdata->aqbuttons[i].special_mask) && rsm_strmatch(aqdata->aqbuttons[i].label, "Heat Pump") == 0 ) {
LOG(AQUA_LOG,LOG_ERR, "Config error, `%s` is enabled, but Virtual Button Heat Pump does not have alt_name Chiller! Creating.",CFG_N_force_chiller);
setVirtualButtonAltLabel(&aqdata->aqbuttons[i], cleanalloc("Chiller")); // Need to malloc this so it can be freed
aqdata->chiller_button = &aqdata->aqbuttons[i];
//aqdata->chiller_button->special_mask |= VIRTUAL_BUTTON_CHILLER;
setMASK(aqdata->chiller_button->special_mask, VIRTUAL_BUTTON_CHILLER);
}
}
if (aqdata->chiller_button == NULL) {
LOG(AQUA_LOG,LOG_ERR, "Config error, `%s` is enabled, but no Virtual Button set for Heat Pump / Chiller! Creating vbutton.",CFG_N_force_chiller);
aqkey *button = getVirtualButton(aqdata, 0);
setVirtualButtonLabel(button, cleanalloc("Heat Pump"));// Need to malloc this so it can be freed
setVirtualButtonAltLabel(button, cleanalloc("Chiller"));// Need to malloc this so it can be freed
aqdata->chiller_button = button;
//aqdata->chiller_button->special_mask |= VIRTUAL_BUTTON_CHILLER;
setMASK(aqdata->chiller_button->special_mask, VIRTUAL_BUTTON_CHILLER);
}
} else {
LOG(AQUA_LOG,LOG_ERR, "Config error, `%s` can only be enabled, if using an iAqualink Touch ID for `%s`, Turning off\n",CFG_N_force_chiller, CFG_N_extended_device_id );
removeMASK(_aqconfig_.force_device_devmask,FORCE_CHILLER);
aqdata->chiller_button = NULL;
}
if (aqdata->chiller_button == NULL) {
LOG(AQUA_LOG,LOG_ERR, "Config error, `%s` is enabled, but no Virtual Button set for Heat Pump / Chiller! Creating vbutton.",CFG_N_force_chiller);
aqkey *button = getVirtualButton(aqdata, 0);
setVirtualButtonLabel(button, "Heat Pump");
setVirtualButtonAltLabel(button, "Chiller");
aqdata->chiller_button = button;
aqdata->chiller_button->special_mask |= VIRTUAL_BUTTON_CHILLER;
}
} else {
aqdata->chiller_button = NULL;
}
// Turn off extended programming if we don't have device
if ( _aqconfig_.extended_device_id == 0x00 )
{
_aqconfig_.extended_device_id_programming = false;
}
/*
_cfgParams[_numCfgParams].mask = READ_RS485_IAQUALNK;
if ( bitmask READ_RS485_IAQUALNK && _aqconfig_.enable_iaqualink ) error and use (_aqconfig_.enable_iaqualink, disable bitmask
if (_aqconfig_.enable_iaqualink)
LOG(AQUA_LOG,LOG_WARNING, "Config error, 'read_RS485_iAqualink' is not valid when 'enable_iaqualink=yes', ignoring read_RS485_iAqualink!\n");
else
_aqconfig_.read_RS485_devmask |= READ_RS485_IAQUALNK;
} else {
_aqconfig_.read_RS485_devmask &= ~READ_RS485_IAQUALNK;
}
if ( bitmask READ_RS485_IAQUALNK && _aqconfig_.enable_iaqualink ) error and use (_aqconfig_.enable_iaqualink, disable bitmask
*/
if (_aqconfig_.enable_iaqualink==true && (_aqconfig_.extended_device_id < 0x30 || _aqconfig_.extended_device_id > 0x33) )
{
LOG(AQUA_LOG,LOG_WARNING, "Config error, 'enable_iaqualink', is only valed with AqualinkTouch ID's, ignoring!\n");
_aqconfig_.enable_iaqualink = true;
}
// Can't read iaqualink if we are also using iaqualink protocol.
if (isMASK_SET(_aqconfig_.enable_iaqualink, READ_RS485_IAQUALNK) && _aqconfig_.enable_iaqualink == true )
{
LOG(AQUA_LOG,LOG_WARNING, "Config error, 'read_RS485_iAqualink' is not valid when 'enable_iaqualink=yes', ignoring read_RS485_iAqualink!\n");
_aqconfig_.read_RS485_devmask &= ~READ_RS485_IAQUALNK;
}
/*
PDA sleep and PDA ID.
*/
@ -1610,8 +1646,12 @@ void check_print_config (struct aqualinkdata *aqdata)
case CFG_STRING:
if (*(char **)_cfgParams[i].value_ptr == NULL)
LOG(AQUA_LOG,LOG_NOTICE, "%-35s =\n", name);
else
LOG(AQUA_LOG,LOG_NOTICE, "%-35s = %s\n",name, *(char **)_cfgParams[i].value_ptr);
else {
if (isMASK_SET(_cfgParams[i].config_mask ,CFG_PASSWD_MASK) )
LOG(AQUA_LOG,LOG_NOTICE, "%-35s = %s\n",name, PASSWD_MASK_TEXT);
else
LOG(AQUA_LOG,LOG_NOTICE, "%-35s = %s\n",name, *(char **)_cfgParams[i].value_ptr);
}
break;
case CFG_INT:
if (*(int *)_cfgParams[i].value_ptr == TEMP_UNKNOWN)
@ -1662,7 +1702,7 @@ void check_print_config (struct aqualinkdata *aqdata)
for (i = 0; i < aqdata->total_buttons; i++)
{
//char ext[] = " VSP ID None | AL ID 0 ";
char ext[40];
char ext[60];
ext[0] = '\0';
for (j = 0; j < aqdata->num_pumps; j++) {
if (aqdata->pumps[j].button == &aqdata->aqbuttons[i]) {
@ -1782,6 +1822,7 @@ int save_config_js(const char* inBuf, int inSize, char* outBuf, int outSize, str
} else if (isPLIGHT(aqdata->aqbuttons[i].special_mask)) {
((clight_detail *)aqdata->aqbuttons[i].special_mask_ptr)->button = NULL;
}
//printf("Freeing %d of %d - %s\n",i,aqdata->total_buttons,aqdata->aqbuttons[i].label);
free ( aqdata->aqbuttons[i].label);
aqdata->aqbuttons[i].special_mask = 0;
aqdata->aqbuttons[i].special_mask_ptr = NULL;
@ -1827,6 +1868,8 @@ int save_config_js(const char* inBuf, int inSize, char* outBuf, int outSize, str
//cursor = inBuf+start+1;
for (m = 0; m < maxMatches; m ++)
{
ignorePair = false;
if (0 != (rc = regexec(&regexCompiled, cursor, maxGroups, groupArray, 0))) {
//printf("Failed to match '%s' with '%s',returning %d.\n", cursor, pattern, rc);
break;
@ -1846,6 +1889,7 @@ int save_config_js(const char* inBuf, int inSize, char* outBuf, int outSize, str
if (!ignorePair) {
setConfigValue(_aqdata ,key,value);
//printf("Config pair %s %s\n",key,value);
}
// Check if panel size has changed
@ -1922,6 +1966,9 @@ bool writeCfg (struct aqualinkdata *aqdata)
//char fp[100];
for ( i=0; i <= _numCfgParams; i++) {
if (isMASK_SET(_cfgParams[i].config_mask, CFG_HIDE) ) {
continue;
}
//printf("Writing %s\n",_cfgParams[i].name);
// Group values by fist letter, if the same group together.
if (lastName != NULL && lastName[0] != _cfgParams[i].name[0]) {

View File

@ -185,11 +185,17 @@ typedef enum cfg_value_type{
#define CFG_PERSISTANT (1 << 0) // Don't free memory, things referance the pointer
#define CFG_NO_EDIT (1 << 1) // Don't allow editing
#define CFG_GRP_ADVANCED (1 << 2) // Show in group advanced
#define CFG_HIDE (1 << 3) // Like passwords.
#define CFG_GRP_ADVANCED (1 << 1) // Show in group advanced
#define CFG_READONLY (1 << 2) // Don't show in UI, but do write to CFG file. (Maybe display in UI but no edit)
#define CFG_HIDE (1 << 3) // Don't show in any UI listing, don't write to CFG file.
//#define CFG_READONLY (1 << 4) // Don't show in UI, but do write to CFG file.
#define CFG_PASSWD_MASK (1 << 4) // Mask password with *****
#define CFG_FORCE_RESTART (1 << 5) // Force aqualinkd to restart
//#define CFG_ (1 << 3)
// Text to show when CFG_PASSWD_MASK is set
#define PASSWD_MASK_TEXT "********"
#define isMASKSET(mask, bit) ((mask & bit) == bit)
typedef struct cfgParam {
@ -230,7 +236,7 @@ int _numCfgParams;
#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_V_rssa_device_id "[\"0x00\", \"0x48\"]"
#define CFG_C_rssa_device_id 14
#define CFG_N_RSSD_LOG_filter "RSSD_LOG_filter"
@ -239,7 +245,7 @@ int _numCfgParams;
#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_V_extended_device_id "[\"0x00\", \"0x30\", \"0x31\", \"0x32\", \"0x33\", \"0x40\", \"0x41\", \"0x42\", \"0x43\"]"
#define CFG_C_extended_device_id 18
#define CFG_N_sync_panel_time "sync_panel_time"

View File

@ -25,6 +25,7 @@
#include "packetLogger.h"
#include "iaqualink.h"
#include "json_messages.h"
/*
All button errors
'Check AQUAPURE No Flow'
@ -35,7 +36,13 @@
static int _swg_noreply_cnt = 0;
void updateHeatPumpLed(aqledstate state, struct aqualinkdata *aqdata);
typedef enum heatpumpstate{
HP_HEAT,
HP_COOL,
HP_UNKNOWN
} heatpumpstate;
void updateHeatPumpLed(heatpumpstate state, aqledstate ledstate, struct aqualinkdata *aqdata, bool fromMessage);
bool processJandyPacket(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata)
{
@ -585,7 +592,7 @@ bool processPacketToJandyPump(unsigned char *packet_buffer, int packet_length, s
if (getLogLevel(DJAN_LOG) == LOG_DEBUG && getLogLevel(RSSD_LOG) < LOG_DEBUG ) {
char msg[1024];
beautifyPacket(msg, 1024, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_DEBUG, "To ePump: %s\n", msg);
LOG(DJAN_LOG, LOG_DEBUG, "To ePump: %s", msg);
}
// If type 0x45 and 0x44 set to interested in next command.
@ -618,7 +625,7 @@ bool processPacketFromJandyPump(unsigned char *packet_buffer, int packet_length,
char msg[1024];
//logMessage(LOG_DEBUG, "Need to log ePump message here for future\n");
beautifyPacket(msg, 1024, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_DEBUG, "From ePump: %s\n", msg);
LOG(DJAN_LOG, LOG_DEBUG, "From ePump: %s", msg);
}
if (packet_buffer[3] == CMD_EPUMP_STATUS && packet_buffer[4] == CMD_EPUMP_RPM) {
@ -683,7 +690,7 @@ bool processPacketToJandyJXiHeater(unsigned char *packet_buffer, int packet_leng
char msg[1024];
//logMessage(LOG_DEBUG, "Need to log ePump message here for future\n");
beautifyPacket(msg, 1024, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_DEBUG, "To JXi: %s\n", msg);
LOG(DJAN_LOG, LOG_DEBUG, "To JXi: %s", msg);
}
if (packet_buffer[3] != CMD_JXI_PING) {
@ -816,7 +823,7 @@ bool processPacketFromJandyJXiHeater(unsigned char *packet_buffer, int packet_le
char msg[1024];
//logMessage(LOG_DEBUG, "Need to log ePump message here for future\n");
beautifyPacket(msg, 1024, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_DEBUG, "From JXi: %s\n", msg);
LOG(DJAN_LOG, LOG_DEBUG, "From JXi: %s", msg);
}
if (packet_buffer[3] != CMD_JXI_STATUS) {
@ -873,7 +880,7 @@ bool processPacketToJandyLXHeater(unsigned char *packet_buffer, int packet_lengt
int length = 0;
beautifyPacket(msg, 1024, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_INFO, "To LX: %s\n", msg);
LOG(DJAN_LOG, LOG_INFO, "To LX: %s", msg);
length += sprintf(msg+length, "Last panel info ");
@ -902,7 +909,7 @@ bool processPacketFromJandyLXHeater(unsigned char *packet_buffer, int packet_len
int length = 0;
beautifyPacket(msg, 1024, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_INFO, "From LX: %s\n", msg);
LOG(DJAN_LOG, LOG_INFO, "From LX: %s", msg);
length += sprintf(msg+length, "Last panel info ");
@ -931,7 +938,7 @@ bool processPacketToJandyChemFeeder(unsigned char *packet_buffer, int packet_len
int length = 0;
beautifyPacket(msg, 1024, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_INFO, "To Chem: %s\n", msg);
LOG(DJAN_LOG, LOG_INFO, "To Chem: %s", msg);
length += sprintf(msg+length, "Last panel info ");
@ -947,7 +954,7 @@ bool processPacketFromJandyChemFeeder(unsigned char *packet_buffer, int packet_l
int length = 0;
beautifyPacket(msg, 1024, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_INFO, "From Chem: %s\n", msg);
LOG(DJAN_LOG, LOG_INFO, "From Chem: %s", msg);
length += sprintf(msg+length, "Last panel info ");
@ -970,36 +977,40 @@ bool processPacketToHeatPump(unsigned char *packet_buffer, int packet_length, st
char msg[1024];
beautifyPacket(msg, 1024, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_INFO, "To HPump: %s\n", msg);
LOG(DJAN_LOG, LOG_INFO, "To HPump: %s", msg);
/* Byted 3 and 4
0x0c|0x01 = Heat Pump Enabled
0x0c|0x29 = Chiller on
0x0c|0x00 = Off
0x0c|0x09 = inknown at present
0x0c|0x0a = unknown at present
*/
/*
0x0c|0x00 = Request off
0x0c|0x09 = Request Heat
0x0c|0x29 = Request Cool
*/
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 - status 0x%02hhx\n",packet_buffer[2],packet_buffer[4] );
updateHeatPumpLed(OFF, aqdata);
} else if (packet_buffer[4] == 0x01) {
LOG(DJAN_LOG, LOG_DEBUG, "Heat Pump 0x%02hhx is Enabled - status 0x%02hhx\n",packet_buffer[2],packet_buffer[4] );
// Think this is Heat Pump Only, Not Chiller. Not sure.
// Looks like heat pump name simply changes to chiller depending on in the water temp is above or below the chiller set point
// So I think "Enabled" is the same for both.
// So going to set it to enabled.
updateHeatPumpLed(ENABLE, aqdata);
LOG(DJAN_LOG, LOG_DEBUG, "Heat Pump 0x%02hhx request to Off - status 0x%02hhx\n",packet_buffer[PKT_DEST],packet_buffer[4] );
updateHeatPumpLed(false, OFF, aqdata, false);
} else if (packet_buffer[4] == 0x09) {
LOG(DJAN_LOG, LOG_DEBUG, "Heat Pump 0x%02hhx request to Heat - status 0x%02hhx\n",packet_buffer[PKT_DEST],packet_buffer[4] );
// Heat
updateHeatPumpLed(false, ENABLE, aqdata, false);
} else if (packet_buffer[4] == 0x29) {
// not sure if this is also Heat on.
LOG(DJAN_LOG, LOG_DEBUG, "Heat Pump 0x%02hhx Chiller is On - status 0x%02hhx\n",packet_buffer[2],packet_buffer[4] );
updateHeatPumpLed(ON, aqdata);
// Cool
LOG(DJAN_LOG, LOG_DEBUG, "Heat Pump 0x%02hhx request to Cool - status 0x%02hhx\n",packet_buffer[PKT_DEST],packet_buffer[4] );
updateHeatPumpLed(true, ENABLE, aqdata, false);
} else {
// Heat Pump is on or enabled (not sure what state), but set to something other than off
LOG(DJAN_LOG, LOG_DEBUG, "Heat Pump 0x%02hhx is (unknown status) 0x%02hhx\n",packet_buffer[2], packet_buffer[4]);
LOG(DJAN_LOG, LOG_INFO, "Heat Pump 0x%02hhx request to (unknown status) 0x%02hhx\n",packet_buffer[PKT_DEST], packet_buffer[4]);
if (aqdata->chiller_button != NULL && aqdata->chiller_button->led->state == OFF)
updateHeatPumpLed(ENABLE, aqdata); // Guess at enabled. ()
updateHeatPumpLed(false, ENABLE, aqdata, false); // Guess at enabled. ()
}
} else {
LOG(DJAN_LOG, LOG_INFO, "Heat Pump 0x%02hhx request unknown 0x%02hhx 0x%02hhx\n",packet_buffer[PKT_DEST], packet_buffer[3] , packet_buffer[4]);
}
@ -1015,8 +1026,27 @@ bool processPacketFromHeatPump(unsigned char *packet_buffer, int packet_length,
// Reply is some status 0x40,0x48,0x68
*/
// 0x40 = OFF
// 0x48 = HEATING.
// 0x68 = COOL.
if (packet_buffer[3] == 0x0d ) {
if (packet_buffer[4] == 0x40) {
updateHeatPumpLed(HP_HEAT, OFF, aqdata, false);
} else if (packet_buffer[4] == 0x48) {
updateHeatPumpLed(HP_HEAT, ON, aqdata, false);
} else if (packet_buffer[4] == 0x68) {
updateHeatPumpLed(HP_COOL, ON, aqdata, false);
} else {
//LOG(DJAN_LOG, LOG_INFO, "Heat Pump 0x%02hhx ");
LOG(DJAN_LOG, LOG_INFO, "Heat Pump 0x%02hhx returned unknown state 0x%02hhx\n",packet_buffer[PKT_DEST], packet_buffer[4]);
}
} else {
LOG(DJAN_LOG, LOG_INFO, "Heat Pump 0x%02hhx returned unknown information 0x%02hhx 0x%02hhx\n",packet_buffer[PKT_DEST], packet_buffer[3], packet_buffer[4]);
}
beautifyPacket(msg, 1024, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_INFO, "From HPump: %s\n", msg);
LOG(DJAN_LOG, LOG_INFO, "From HPump: %s", msg);
return false;
}
@ -1028,34 +1058,51 @@ void processHeatPumpDisplayMessage(char *msg, struct aqualinkdata *aqdata) {
// ' Heat Pump ENA '
// 'Heat Pump Enabled'
// Or chiller.
heatpumpstate hpstate = HP_HEAT;
// are we heat pump or chiller
if (stristr(msg,"Chiller") != NULL) {
// NSF Should check alt_mode is Chiller and not Heat Pump
((vbutton_detail *)aqdata->chiller_button->special_mask_ptr)->in_alt_mode = true;
hpstate = HP_COOL;
}
if (stristr(msg," ENA") != NULL) {
updateHeatPumpLed(ENABLE, aqdata);
updateHeatPumpLed(hpstate, ENABLE, aqdata, true);
} else if (stristr(msg," OFF") != NULL) {
updateHeatPumpLed(OFF, aqdata);
updateHeatPumpLed(hpstate, OFF, aqdata, true);
} else if (stristr(msg," ON") != NULL) {
updateHeatPumpLed(ON, aqdata);
updateHeatPumpLed(hpstate, ON, aqdata, true);
}
LOG(AQUA_LOG,LOG_DEBUG, "Set %s to %s from message '%s'",
((vbutton_detail *)aqdata->chiller_button->special_mask_ptr)->in_alt_mode?((vbutton_detail *)aqdata->chiller_button->special_mask_ptr)->altlabel:aqdata->chiller_button->label,
aqdata->chiller_button->led->state==ENABLE?"Enabled":(aqdata->chiller_button->led->state==ON?"On":"Off"),
msg);
LED2text(aqdata->chiller_button->led->state), msg);
}
void updateHeatPumpLed(aqledstate state, struct aqualinkdata *aqdata) {
//void updateHeatPumpLed(bool chiller, aqledstate state, struct aqualinkdata *aqdata) {
void updateHeatPumpLed(heatpumpstate state, aqledstate ledstate, struct aqualinkdata *aqdata, bool fromMessage)
{
if (aqdata->chiller_button == NULL)
return;
if (aqdata->chiller_button->led->state != state) {
aqdata->chiller_button->led->state = ON;
// If LED state is enable (that's a reqest), so only change if off.
// if froma displayed message, that's from ON to ENA, so set that one.
if ( !fromMessage && ledstate == ENABLE && aqdata->chiller_button->led->state == ON) {
//printf("**** Request from %s Heat Pump %s, currently %s, ignore!\n",fromMessage?"Display":"RS485",LED2text(ledstate),LED2text(aqdata->chiller_button->led->state) );
return;
}
if (aqdata->chiller_button->led->state != ledstate) {
//printf("**** Heat Pump Setting to %s, from %s!\n",LED2text(ledstate),LED2text(aqdata->chiller_button->led->state));
aqdata->chiller_button->led->state = ledstate;
aqdata->updated = true;
if (state == HP_COOL) {
((vbutton_detail *)aqdata->chiller_button->special_mask_ptr)->in_alt_mode = true;
} else if (state == HP_HEAT) {
((vbutton_detail *)aqdata->chiller_button->special_mask_ptr)->in_alt_mode = false;
}
} else {
//printf("**** Heat Pump %s, already %s, ignore!\n",LED2text(ledstate),LED2text(aqdata->chiller_button->led->state));
}
}
/*

208
source/dummy_device.c Normal file
View File

@ -0,0 +1,208 @@
/*
*
* Program to simulate devices to help debug messages.
* Not in release code / binary for AqualinkD
*
*/
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libgen.h>
#include <fcntl.h>
#include <time.h>
// #include "serial_logger.h"
#include "aq_serial.h"
#include "utils.h"
#include "packetLogger.h"
#include "rs_msg_utils.h"
#define CONFIG_C // Make us look like config.c when we load config.h so we get globals.
#include "config.h"
unsigned char DEVICE_ID = 0x70;
bool _keepRunning = true;
int _rs_fd;
void intHandler(int dummy)
{
_keepRunning = false;
LOG(SLOG_LOG, LOG_NOTICE, "Stopping!\n");
}
bool isAqualinkDStopping() {
return !_keepRunning;
}
void process_heatpump_packet(unsigned char *packet_buffer, const int packet_length);
int main(int argc, char *argv[])
{
int logLevel = LOG_INFO;
int packet_length;
unsigned char packet_buffer[AQ_MAXPKTLEN];
int blankReads = 0;
bool returnError = false;
if (argc < 2 || access(argv[1], F_OK) == -1)
{
fprintf(stderr, "ERROR, first param must be valid serial port, ie:-\n\t%s /dev/ttyUSB0\n\n", argv[0]);
return 1;
}
setLoggingPrms(logLevel, false, NULL);
LOG(SLOG_LOG, LOG_NOTICE, "Starting %s\n", basename(argv[0]));
// _rs_fd = init_serial_port(argv[1]);
_rs_fd = init_blocking_serial_port(argv[1]);
if (_rs_fd < 0)
{
LOG(SLOG_LOG, LOG_ERR, "Unable to open port: %s\n", argv[1]);
displayLastSystemError(argv[1]);
return -1;
}
signal(SIGINT, intHandler);
signal(SIGTERM, intHandler);
// Force all packets to be printed.
addDebugLogMask(RSSD_LOG);
_aqconfig_.RSSD_LOG_filter[0] = DEVICE_ID;
while (_keepRunning)
{
if (_rs_fd < 0)
{
LOG(SLOG_LOG, LOG_ERR, "ERROR, serial port disconnect\n");
_keepRunning = false;
}
packet_length = get_packet(_rs_fd, packet_buffer);
if (packet_length == AQSERR_READ)
{
// Unrecoverable read error. Force an attempt to reconnect.
LOG(SLOG_LOG, LOG_ERR, "ERROR, on serial port! Please check %s\n", argv[1]);
_keepRunning = false;
returnError = true;
}
else if (packet_length == AQSERR_TIMEOUT)
{
// Unrecoverable read error. Force an attempt to reconnect.
LOG(SLOG_LOG, LOG_ERR, "ERROR, Timeout on serial port, nothing read! Please check %s\n", argv[1]);
_keepRunning = false;
returnError = true;
}
else if (packet_length < 0)
{
// Error condition
if (packet_length == AQSERR_CHKSUM)
{
LOG(SLOG_LOG, LOG_WARNING, "Checksum error\n");
}
else if (packet_length == AQSERR_2LARGE)
{
LOG(SLOG_LOG, LOG_WARNING, "Packet too large error\n");
}
else if (packet_length == AQSERR_2SMALL)
{
LOG(SLOG_LOG, LOG_WARNING, "Packet too small error\n");
}
else
{
LOG(SLOG_LOG, LOG_WARNING, "Unknown error reading packet\n");
}
}
else if (packet_length == 0)
{
if (++blankReads > 10)
{
LOG(SLOG_LOG, LOG_ERR, "ERROR, too many blank reads! Please check %s\n", argv[1]);
_keepRunning = false;
returnError = true;
}
delay(1);
}
else if (packet_length > 0)
{
blankReads = 0;
//debuglogPacket(SLOG_LOG, packet_buffer, packet_length, true, true);
if (packet_buffer[PKT_DEST] == DEVICE_ID)
{
process_heatpump_packet(packet_buffer, packet_length);
}
}
}
LOG(SLOG_LOG, LOG_NOTICE, "Stopping!\n");
close_serial_port(_rs_fd);
if (returnError)
return 1;
return 0;
}
/*
* Reply to message
*/
void process_heatpump_packet(unsigned char *packet_buffer, const int packet_length)
{
//LOG(SLOG_LOG, LOG_NOTICE, "Replying to packet 0x%02hhx!\n",packet_buffer[PKT_DEST]);
// reply to off 0x10|0x02|0x00|0x0d|0x40|0x00|0x00|0x5f|0x10|0x03|
static unsigned char hp_off[] = {0x00, 0x0d, 0x40, 0x00, 0x00};
static unsigned char hp_heat[] = {0x00, 0x0d, 0x48, 0x00, 0x00};
static unsigned char hp_cool[] = {0x00, 0x0d, 0x68, 0x00, 0x00};
if (packet_buffer[PKT_CMD] == CMD_PROBE)
{
send_ack(_rs_fd, 0x00);
LOG(SLOG_LOG, LOG_NOTICE, "Replied to Probe packet to 0x%02hhx with ACK\n",packet_buffer[PKT_DEST]);
}
else if (packet_buffer[3] == 0x0c)
{
if (packet_buffer[4] == 0x00)
{ // Off
send_jandy_command(_rs_fd, hp_off, 5);
LOG(SLOG_LOG, LOG_NOTICE, "Replied to OFF 0x%02hhx packet to 0x%02hhx with ACK\n",packet_buffer[4],packet_buffer[PKT_DEST]);
}
else if (packet_buffer[4] == 0x09) // Heat
{ // Enable
send_jandy_command(_rs_fd, hp_heat, 5);
LOG(SLOG_LOG, LOG_NOTICE, "Replied to HEAT 0x%02hhx packet to 0x%02hhx with ACK\n",packet_buffer[4],packet_buffer[PKT_DEST]);
}
else if (packet_buffer[4] == 0x29) // Cool
{ // Enable
send_jandy_command(_rs_fd, hp_cool, 5);
LOG(SLOG_LOG, LOG_NOTICE, "Replied to COOL 0x%02hhx packet to 0x%02hhx with ACK\n",packet_buffer[4],packet_buffer[PKT_DEST]);
}
else
{ // Enable
LOG(SLOG_LOG, LOG_ERR, "************* Unknown State Request 0x%02hhx *************",packet_buffer[4]);
LOG(SLOG_LOG, LOG_NOTICE, "NOT Replying to UNKNOWN 0x%02hhx packet to 0x%02hhx with ACK\n",packet_buffer[4],packet_buffer[PKT_DEST]);
//send_jandy_command(_rs_fd, hp_unknown, 5);
}
}
}

View File

@ -595,7 +595,7 @@ void iaqt_pump_update(struct aqualinkdata *aq_data, int updated) {
/* Some newer revisions support pump by name and not number.
Seen this is rev Yg
Seen this is rev Yg Looks like this ID came in T.2g (not T.2 though)
Info: iAQ Touch: Page: Status (diff ID) | 0x2a\
Info: iAQ Touch: Status page 00| Filter Pump\

View File

@ -46,13 +46,6 @@
#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;
unsigned char _iaqt_pgm_command = NUL;

View File

@ -278,17 +278,33 @@ void set_iaqualink_aux_state(aqkey *button, bool isON) {
_fullcmd[4] = 0x00;
}
void set_iaqualink_heater_setpoint(int value, bool isPool) {
if (isPool) {
// AQ_SET_IAQTOUCH_CHILLER_TEMP
// AQ_SET_IAQLINK_CHILLER_TEMP //
//void set_iaqualink_heater_setpoint(int value, bool isPool) {
void set_iaqualink_heater_setpoint(int value, SP_TYPE type) {
if (type == SP_POOL) {
_fullcmd[4] = 0x05;
} else {
_fullcmd[6] = value;
} else if (type == SP_SPA) {
_fullcmd[4] = 0x06;
_fullcmd[6] = value;
} else if (type == SP_CHILLER) {
// Note This doesn;t work on T2, but does on Yg. Turned off in aq_programmer
_fullcmd[4] = 0x1f;
_fullcmd[6] = 0x4b;
_fullcmd[8] = 0x63;
_fullcmd[10] = value;
} else {
LOG(IAQL_LOG, LOG_ERR, "Didn't understand setpoint type %d\n",type);
return;
}
// byte[4] = 0x1f, byte[6]=0x4b, byte[8]=0x53, byte[10]=value // for Chiller
// Should check value is valid here.
_fullcmd[6] = value;
//_fullcmd[6] = value;
push_iaqualink_cmd(_cmd_readyCommand, 2);
push_iaqualink_cmd(_fullcmd, 19);
@ -296,6 +312,8 @@ void set_iaqualink_heater_setpoint(int value, bool isPool) {
// reset
_fullcmd[4] = 0x00;
_fullcmd[6] = 0x00;
_fullcmd[8] = 0x00;
_fullcmd[10] = 0x00;
}
void iAqSetButtonState(struct aqualinkdata *aq_data, int index, const unsigned char byte)
@ -326,9 +344,9 @@ bool process_iAqualinkStatusPacket(unsigned char *packet, int length, struct aqu
int startIndex = 4 + 1;
int numberBytes = packet[4];
int offsetIndex = startIndex + numberBytes;
bool foundSpaSP = false;
bool foundWaterTemp = false;
bool foundAirTemp = false;
//bool foundSpaSP = false;
//bool foundWaterTemp = false;
//bool foundAirTemp = false;
for (int i = 0; i <= numberBytes; i++)
{
@ -338,6 +356,7 @@ bool process_iAqualinkStatusPacket(unsigned char *packet, int length, struct aqu
// Some panels have blanks for the last 3 buys, the first of which is "water temp" (not sure on others 0x20, 0x21)
// So if we saw 0x1d break loop if not force next as water temp.
/*
if (foundWaterTemp && i == numberBytes)
{
break;
@ -345,8 +364,70 @@ bool process_iAqualinkStatusPacket(unsigned char *packet, int length, struct aqu
else if (i == numberBytes)
{
byteType = 0x1d;
}
}*/
// Seems to be in order rather than type
label = " ";
if (byte != 0xff)
{
switch (i)
{
case 0:
// 0x00
label = "Filter Pump ";
if (byteType != 0x00)
label = "Filter Pump *";
break;
case 1:
// 0x01
label = "Pool Heater ";
if (byteType != 0x01)
label = "Pool Heater *";
break;
case 2:
// 0x02
label = "Spa ";
if (byteType != 0x02)
label = "Spa *";
break;
case 3:
// 0x03
label = "Spa Heater ";
if (byteType != 0x03)
label = "Spa Heater *";
break;
case 5:
// 0x06 good
label = "Pool Heater SP ";
if (byteType != 0x06)
label = "Pool Heater SP *";
break;
case 7:
// 0x08 good
// 0x09 (when spa is on???)
// 0x0e (good not sure)
label = "Spa Heater SP ";
if (byteType != 0x08)
label = "Spa Heater SP *";
break;
case 9:
// 0x0f good
label = "Air Temp ";
if (byteType != 0x0f)
label = "Air Temp *";
break;
case 11:
// 0x1d good
// 0x1c (good) ?? maybe spa
// 0x1f bad
label = "Water Temp ";
if (byteType != 0x1d)
label = "Water Temp *";
break;
}
}
/*
if (byteType == 0) {
label = "Filter Pump ";
if (isPDA_PANEL) { iAqSetButtonState(aq_data, 0, byte); }
@ -363,7 +444,7 @@ bool process_iAqualinkStatusPacket(unsigned char *packet, int length, struct aqu
} else if (byteType == 8 || byteType == 9) {// 8 usually, also get 9 & 14 (different spa/heater modes not sorted out yet. 14 sometimes blank as well)
label = "Spa Htr setpoint ";
foundSpaSP=true;
} else if ( (/*byteType == 14 ||*/ byteType == 12) && foundSpaSP==false && byte != 0) {
} else if ( (byteType == 12) && foundSpaSP==false && byte != 0) { // Sometimes 14
label = "Spa Htr setpoint ";
} else if ( (byteType == 14 || byteType == 15 || byteType == 26) && byte != 0 && byte != 255 && foundAirTemp == false ) {// 0x0f
label = "Air Temp "; // we also see this as 14 (RS16) ONLY
@ -375,7 +456,7 @@ bool process_iAqualinkStatusPacket(unsigned char *packet, int length, struct aqu
}
else
label = " ";
*/
LOG(IAQL_LOG, LOG_INFO, "%-17s = %3d | index=%d type=(%0.2d 0x%02hhx) value=0x%02hhx offset=%d\n", label, byte, i, byteType, byteType, byte, (offsetIndex + i));
}
LOG(IAQL_LOG, LOG_INFO, "Status from other protocols Pump %s, Spa %s, SWG %d, PumpRPM %d, PoolSP=%d, SpaSP=%d, WaterTemp=%d, AirTemp=%d\n",

View File

@ -11,7 +11,7 @@ bool process_iaqualink_packet(unsigned char *packet, int length, struct aqualink
bool process_iAqualinkStatusPacket(unsigned char *packet, int length, struct aqualinkdata *aq_data);
void set_iaqualink_aux_state(aqkey *button, bool isON);
void set_iaqualink_heater_setpoint(int value, bool isPool);
void set_iaqualink_heater_setpoint(int value, SP_TYPE type);
// Send the below commands to turn on/off (toggle)
// This is the button in pButton. (byte 6 in below)
@ -385,4 +385,35 @@ Bubblers = Off | index 3 type=(04 0x04) status=0x00 start=43 length=8
Waterfall1 = Off | index 4 type=(05 0x05) status=0x00 start=53 length=10
*** SWG is in the next bytes from iAq 1Tch status. But can't tell how to pull them since RPM is also here as well. ****
*/
/*
This works on rev Yg, but doesn't seem to T.2
Set Heat Pump Chiller Setpoint.
Set to 94 (0x5e in type iAqualnk sendCmd)
Byte 12 in iAqualink sendCmd (sets set point)
Byte 6 from end iAq Main status (looks like return)
Set to 94 (0x5e in type iAqualink sentCmd)
packet To 0x33 of type iAq Poll | HEX: 0x10|0x02|0x33|0x30|0x75|0x10|0x03|
packet To 0x00 of type Ack | HEX: 0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
packet To 0xa3 of type Unknown '0x73' | HEX: 0x10|0x02|0xa3|0x73|0x28|0x10|0x03|
packet To 0x00 of type iAqualnk sendCmd | HEX: 0x10|0x02|0x00|0x24|0x73|0x01|0x1f|0x00|0x4b|0x00|0x63|0x00|0x5e|0x00|0x00|0x00|0x00|0x00|0x00|0x00|0x00|0xd5|0x10|0x03|
packet To 0x33 of type iAq Main status | HEX: 0x10|0x02|0x33|0x70|0x11|0x00|0x01|0x02|0x03|0x05|0x06|0x07|0x08|0x0e|0x0f|0x1a|0x1d|0x1f|0x20|0x21|0x24|0x25|0x01|0x00|0x00|0x00|0x00|0x4b|0x00|0x63|0x00|0x59|0x00|0x4f|0x00|0x00|0x00|0x37|0x18|0x42|0x30|0x33|0x31|0x36|0x38|0x32|0x33|0x20|0x52|0x53|0x2d|0x34|0x20|0x43|0x6f|0x6d|0x62|0x6f|0x00|0x00|0x00|0x5e|0x00|0x37|0xfd|0x10|0x03|
Set to 92 (0x5c in type iAqualink sentCmd)
packet To 0x33 of type iAq Poll | HEX: 0x10|0x02|0x33|0x30|0x75|0x10|0x03|
packet To 0x00 of type Ack | HEX: 0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
packet To 0xa3 of type Unknown '0x73' | HEX: 0x10|0x02|0xa3|0x73|0x28|0x10|0x03|
packet To 0x00 of type iAqualnk sendCmd | HEX: 0x10|0x02|0x00|0x24|0x73|0x01|0x1f|0x00|0x4b|0x00|0x63|0x00|0x5c|0x00|0x00|0x00|0x00|0x00|0x00|0x00|0x00|0xd3|0x10|0x03|
packet To 0x33 of type iAq Main status | HEX: 0x10|0x02|0x33|0x70|0x11|0x00|0x01|0x02|0x03|0x05|0x06|0x07|0x08|0x0e|0x0f|0x1a|0x1d|0x1f|0x20|0x21|0x24|0x25|0x01|0x00|0x00|0x00|0x00|0x4b|0x00|0x63|0x00|0x59|0x00|0x4f|0x00|0x00|0x00|0x37|0x18|0x42|0x30|0x33|0x31|0x36|0x38|0x32|0x33|0x20|0x52|0x53|0x2d|0x34|0x20|0x43|0x6f|0x6d|0x62|0x6f|0x00|0x00|0x00|0x5c|0x00|0x37|0xfb|0x10|0x03|
*/

View File

@ -126,9 +126,9 @@ const char* _getStatus(struct aqualinkdata *aqdata, const char *blankmsg)
// If only one bit set (conected) then ignore all these if's
if (aqdata->status_mask != CONNECTED) {
if ((aqdata->status_mask & ERROR_SERIAL) == ERROR_SERIAL)
return "ERROR SERIAL CONNECTION";
return "ERROR No Serial connection";
else if ((aqdata->status_mask & ERROR_NO_DEVICE_ID) == ERROR_NO_DEVICE_ID)
return "ERROR NO DEVICE ID";
return "ERROR No device ID";
else if ((aqdata->status_mask & CHECKING_CONFIG) == CHECKING_CONFIG)
return "Checking Config";
else if ((aqdata->status_mask & AUTOCONFIGURE_ID) == AUTOCONFIGURE_ID)
@ -138,7 +138,7 @@ const char* _getStatus(struct aqualinkdata *aqdata, const char *blankmsg)
else if ((aqdata->status_mask & CONNECTING) == CONNECTING)
return "Connecting (waiting for control panel)";
else if ((aqdata->status_mask & NOT_CONNECTED) == NOT_CONNECTED)
return "NOT CONNECTED";
return "Not Connected to panel";
}
if (aqdata->active_thread.thread_id != 0) {
@ -209,7 +209,7 @@ int build_mqtt_status_message_JSON(char* buffer, int size, int idx, int nvalue,
return strlen(buffer);
}
int build_aqualink_error_status_JSON(char* buffer, int size, char *msg)
int build_aqualink_error_status_JSON(char* buffer, int size, const char *msg)
{
//return snprintf(buffer, size, "{\"type\": \"error\",\"status\":\"%s\"}", msg);
return snprintf(buffer, size, "{\"type\": \"status\",\"status\":\"%s\",\"version\":\"xx\",\"time\":\"xx\",\"air_temp\":\"0\",\"pool_temp\":\"0\",\"spa_temp\":\"0\",\"pool_htr_set_pnt\":\"0\",\"spa_htr_set_pnt\":\"0\",\"frz_protect_set_pnt\":\"0\",\"temp_units\":\"f\",\"battery\":\"ok\",\"leds\":{\"Filter_Pump\": \"off\",\"Spa_Mode\": \"off\",\"Aux_1\": \"off\",\"Aux_2\": \"off\",\"Aux_3\": \"off\",\"Aux_4\": \"off\",\"Aux_5\": \"off\",\"Aux_6\": \"off\",\"Aux_7\": \"off\",\"Pool_Heater\": \"off\",\"Spa_Heater\": \"off\",\"Solar_Heater\": \"off\"}}", msg);
@ -375,6 +375,10 @@ int build_device_JSON(struct aqualinkdata *aqdata, char* buffer, int size, bool
((aqdata->aqbuttons[i].special_mask & TIMER_ACTIVE) == TIMER_ACTIVE?JSON_ON:JSON_OFF));
} else {
if (!homekit && ENABLE_CHILLER && isVBUTTON_CHILLER(aqdata->aqbuttons[i].special_mask) ) {
// We will add this VButton as a thermostat
continue;
}
get_aux_information(&aqdata->aqbuttons[i], aqdata, aux_info);
//length += sprintf(buffer+length, "{\"type\": \"switch\", \"type_ext\": \"switch_vsp\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"int_status\": \"%d\" %s},",
length += sprintf(buffer+length, "{\"type\": \"switch\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"int_status\": \"%d\" %s},",
@ -431,11 +435,11 @@ int build_device_JSON(struct aqualinkdata *aqdata, char* buffer, int size, bool
if ( (ENABLE_CHILLER || (aqdata->chiller_set_point != TEMP_UNKNOWN && getWaterTemp(aqdata) != TEMP_UNKNOWN)) && (aqdata->chiller_button != NULL) ) {
length += sprintf(buffer+length, "{\"type\": \"setpoint_chiller\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"spvalue\": \"%.*f\", \"value\": \"%.*f\", \"int_status\": \"%d\" },",
CHILLER,
"Chiller",
//aqdata->chiller_state==ON?JSON_ON:JSON_OFF,
((vbutton_detail *)aqdata->chiller_button->special_mask_ptr)->in_alt_mode?JSON_ON:JSON_OFF,
//aqdata->chiller_state==ON?LED2text(ON):LED2text(ENABLE),
((vbutton_detail *)aqdata->chiller_button->special_mask_ptr)->in_alt_mode?(aqdata->chiller_button->led->state==ON?LED2text(ON):LED2text(ENABLE)):JSON_OFF,
"Heat Pump Chiller",
aqdata->chiller_button->led->state==ON?JSON_ON:JSON_OFF,
//((vbutton_detail *)aqdata->chiller_button->special_mask_ptr)->in_alt_mode?JSON_ON:JSON_OFF,
aqdata->chiller_button->led->state==ON?LED2text(ON):LED2text(ENABLE),
//((vbutton_detail *)aqdata->chiller_button->special_mask_ptr)->in_alt_mode?(aqdata->chiller_button->led->state==ON?LED2text(ON):LED2text(ENABLE)):JSON_OFF,
((homekit)?2:0),
((homekit_f)?degFtoC(aqdata->chiller_set_point):aqdata->chiller_set_point),
((homekit)?2:0),
@ -664,8 +668,10 @@ 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) {
if ( (ENABLE_CHILLER || aqdata->chiller_set_point != TEMP_UNKNOWN) && aqdata->chiller_button != NULL) {
length += sprintf(buffer+length, ",\"chiller_set_pnt\":\"%d\"",aqdata->chiller_set_point );//"0",
if (isVBUTTON_CHILLER(aqdata->chiller_button->special_mask))
length += sprintf(buffer+length, ",\"chiller_mode\":\"%s\"",((vbutton_detail *)aqdata->chiller_button->special_mask_ptr)->in_alt_mode?"cool":"heat");
}
if ( aqdata->air_temp == TEMP_UNKNOWN )
@ -736,7 +742,10 @@ int build_aqualink_status_JSON(struct aqualinkdata *aqdata, char* buffer, int si
//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) );
}
// Add Chiller if exists
if (aqdata->chiller_button != NULL) {
length += sprintf(buffer+length, ", \"%s\": \"%s\"", CHILLER, LED2text(aqdata->chiller_button->led->state) );
}
//length += sprintf(buffer+length, "}, \"extra\":{" );
length += sprintf(buffer+length, "},");
@ -800,6 +809,18 @@ printf("Pump Type %d\n",aqdata->pumps[i].pumpType);
length += sprintf(buffer+length, "}");
length += sprintf(buffer+length, ",\"alternate_modes\":{" );
for (i=aqdata->virtual_button_start; i < aqdata->total_buttons; i++)
{
if (isVBUTTON_ALTLABEL(aqdata->aqbuttons[i].special_mask)) {
length += sprintf(buffer+length, "\"%s\": \"%s\",",aqdata->aqbuttons[i].name, ((vbutton_detail *)aqdata->aqbuttons[i].special_mask_ptr)->in_alt_mode?JSON_ON:JSON_OFF );
}
}
if (buffer[length-1] == ',')
length--;
length += sprintf(buffer+length, "}");
length += sprintf(buffer+length, ",\"sensors\":{" );
for (i=0; i < aqdata->num_sensors; i++)
{
@ -1143,13 +1164,25 @@ int json_cfg_element(char* buffer, int size, const char *name, const void *value
int result = 0;
char valid_values[256];
char adv[20];
char adv[128];
int adv_size=0;
// We shouldn't get CFG_HIDE here. Since we can't exit with 0, simply add a space
if (isMASKSET(config_mask, CFG_HIDE)) {
return snprintf(buffer, size, " ");
}
if (valid_val != NULL) {
sprintf(valid_values,",\"valid values\":%s",valid_val);
}
sprintf(adv,",\"advanced\": \"%s\"", isMASKSET(config_mask, CFG_GRP_ADVANCED)?"yes":"no");
adv_size = sprintf(adv,",\"advanced\": \"%s\"", isMASKSET(config_mask, CFG_GRP_ADVANCED)?"yes":"no");
if (isMASKSET(config_mask, CFG_READONLY))
adv_size += sprintf(adv+adv_size,",\"readonly\": \"yes\"");
if (isMASKSET(config_mask, CFG_FORCE_RESTART))
adv_size += sprintf(adv+adv_size,",\"force_restart\": \"yes\"");
switch(type){
@ -1164,7 +1197,11 @@ int json_cfg_element(char* buffer, int size, const char *name, const void *value
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 );
if (isMASK_SET(config_mask, CFG_PASSWD_MASK)) {
result = snprintf(buffer, size, ",\"%s\" : {\"value\":\"%s\", \"type\":\"string\", \"passwd_mask\":\"yes\" %s}", name, PASSWD_MASK_TEXT,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:
@ -1260,13 +1297,23 @@ int build_aqualink_config_JSON(char* buffer, int size, struct aqualinkdata *aq_d
//#ifdef CONFIG_DEV_TEST
for (int i=0; i <= _numCfgParams; i++) {
if (isMASK_SET(_cfgParams[i].config_mask, CFG_HIDE) ) {
continue;
}
// 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 (isMASK_SET(_cfgParams[i].config_mask, CFG_READONLY) ) {
// NSF in the future we should allow these to pass, but set the UI as readonly.
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].config_mask)) <= 0) {
LOG(NET_LOG,LOG_ERR, "Config json buffer full in, result truncated! size=%d curently used=%d\n",size,length);
return length;
@ -1385,7 +1432,7 @@ int build_aqualink_config_JSON(char* buffer, int size, struct aqualinkdata *aq_d
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) ) <= 0) {
if ((result = json_cfg_element(buffer+length, size-length, buf, &stringptr, CFG_STRING, 0, "[\"\", \"JANDY ePUMP\",\"Pentair VS\",\"Pentair VF\"]", 0) ) <= 0) {
LOG(NET_LOG,LOG_ERR, "Config json buffer full in, result truncated! size=%d curently used=%d\n",size,length);
return length;
} else
@ -1403,7 +1450,7 @@ int build_aqualink_config_JSON(char* buffer, int size, struct aqualinkdata *aq_d
} 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)) <= 0) {
if ((result = json_cfg_element(buffer+length, size-length, buf, &oID, CFG_INT, 0, "[\"\", \"1\",\"2\",\"3\",\"4\",\"5\",\"6\"]", 0)) <= 0) {
LOG(NET_LOG,LOG_ERR, "Config json buffer full in, result truncated! size=%d curently used=%d\n",size,length);
return length;
} else

View File

@ -54,7 +54,7 @@ bool parseJSONrequest(char *buffer, struct JSONkvptr *request);
int build_logmsg_JSON(char *dest, int loglevel, const char *src, int dest_len, int src_len);
int build_mqtt_status_JSON(char* buffer, int size, int idx, int nvalue, float setpoint/*char *svalue*/);
bool parseJSONmqttrequest(const char *str, size_t len, int *idx, int *nvalue, char *svalue);
int build_aqualink_error_status_JSON(char* buffer, int size, char *msg);
int build_aqualink_error_status_JSON(char* buffer, int size, const char *msg);
int build_mqtt_status_message_JSON(char* buffer, int size, int idx, int nvalue, char *svalue);
int build_aqualink_aqmanager_JSON(struct aqualinkdata *aqdata, char* buffer, int size);
//int build_device_JSON(struct aqualinkdata *aqdata, int programable_switch, char* buffer, int size, bool homekit);
@ -63,6 +63,8 @@ int build_device_JSON(struct aqualinkdata *aqdata, char* buffer, int size, bool
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);
char *LED2text(aqledstate state);
#endif /* JSON_MESSAGES_H_ */
/*

View File

@ -133,10 +133,11 @@ static void ws_send(struct mg_connection *nc, char *msg)
//LOG(NET_LOG,LOG_DEBUG, "WS: Sent %d characters '%s'\n",size, msg);
}
void _broadcast_aqualinkstate_error(struct mg_connection *nc, char *msg)
void _broadcast_aqualinkstate_error(struct mg_connection *nc, const char *msg)
{
struct mg_connection *c;
char data[JSON_STATUS_SIZE];
build_aqualink_error_status_JSON(data, JSON_STATUS_SIZE, msg);
for (c = mg_next(nc->mgr, NULL); c != NULL; c = mg_next(nc->mgr, c)) {
@ -2288,7 +2289,7 @@ f_end:
void broadcast_aqualinkstate() {
_aqualink_data->updated = true;
}
void broadcast_aqualinkstate_error(char *msg) {
void broadcast_aqualinkstate_error(const char *msg) {
_broadcast_aqualinkstate_error(_mgr.active_connections, msg);
}
void broadcast_simulator_message() {

View File

@ -28,7 +28,7 @@ bool start_net_services(struct aqualinkdata *aqdata);
void stop_net_services();
time_t poll_net_services(int timeout_ms);
void broadcast_aqualinkstate();
void broadcast_aqualinkstate_error(char *msg);
void broadcast_aqualinkstate_error(const char *msg);
void broadcast_simulator_message();

View File

@ -135,11 +135,12 @@ void _logPacket(logmask_t from, unsigned char *packet_buffer, int packet_length,
lastPacketTo = packet_buffer[PKT_DEST];
}
#ifndef DUMMY_DEVICE // Not interested if in dummy_device
if (is_read)
LOG(from,LOG_DEBUG_SERIAL, "Serial read %d bytes\n",packet_length);
else
LOG(from,LOG_DEBUG_SERIAL, "Serial write %d bytes\n",packet_length);
#endif
//char buff[1000];
char buff[LARGELOGBUFFER];

View File

@ -33,15 +33,15 @@
#include "rs_msg_utils.h"
#ifdef SERIAL_LOGGER
// Make us look lie config.c when we load config.h
// Make us look like config.c when we load config.h so we get globals.
#define CONFIG_C
#endif
#include "config.h"
#define SLOG_MAX 80
#define PACKET_MAX 800
#define PACKET_MAX 1200
#define VERSION "serial_logger V2.8"
#define VERSION "serial_logger V2.9"
/*
typedef enum used {
@ -103,6 +103,9 @@ void intHandler(int dummy) {
if (_playback_file) // If we are reading file, loop is irevelent
exit(0);
}
bool isAqualinkDStopping() {
return !_keepRunning;
}
#else
int serial_logger (int rs_fd, char *port_name, int logLevel, int slogger_packets, char *slogger_ids)
{
@ -531,7 +534,7 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "Optional parameters are :-\n");
fprintf(stderr, "\t-n (Do not probe panel for type/rev info)\n");
fprintf(stderr, "\t-d (debug / print messages)\n");
fprintf(stderr, "\t-p <number> (# packets to log, default=%d)\n",PACKET_MAX);
fprintf(stderr, "\t-p <number> (# packets to log, default=%d, use -1 for probe cycle <quickest> )\n",PACKET_MAX);
fprintf(stderr, "\t-i <ID> (just log specific ID, can use multiple -i. will also force -d switc)\n");
fprintf(stderr, "\t-pi <ID> (just log specific Pantair ID, can use multiple -pi. will also force -d switch)\n");
fprintf(stderr, "\t-r (raw)\n");
@ -550,7 +553,9 @@ int main(int argc, char *argv[]) {
if (strcmp(argv[i], "-d") == 0) {
logLevel = LOG_DEBUG;
} else if (strcmp(argv[i], "-p") == 0 && i+1 < argc) {
logPackets = atoi(argv[i+1]);
char *endptr;
//logPackets = atoi(argv[i+1]);
logPackets = strtol(argv[i+1], &endptr, 10);
} else if (strcmp(argv[i], "-i") == 0 && i+1 < argc) {
unsigned int n;
sscanf(argv[i+1], "0x%2x", &n);
@ -651,6 +656,7 @@ int _serial_logger(int rs_fd, char *port_name, int logPackets, int logLevel, boo
unsigned char packet_buffer[AQ_MAXPKTLEN];
unsigned char last_packet_buffer[AQ_MAXPKTLEN];
unsigned char lastID = 0x00;
unsigned char firstProbe = 0x00;
int i = 0;
bool found;
serial_id_log slog[SLOG_MAX];
@ -676,6 +682,14 @@ int _serial_logger(int rs_fd, char *port_name, int logPackets, int logLevel, boo
bool found_pent_vsp =false;
bool found_iAqualnk =false;
bool probeCycle = false;
int probeCycleCnt = 0;
if (logPackets <= 0) {
logPackets = PACKET_MAX;
probeCycle = true;
}
clock_gettime(CLOCK_REALTIME, &start_time);
if (timePackets) {
clock_gettime(CLOCK_REALTIME, &packet_start_time);
@ -725,6 +739,25 @@ int _serial_logger(int rs_fd, char *port_name, int logPackets, int logLevel, boo
delay(1);
} else if (packet_length > 0) {
blankReads = 0;
if (probeCycle) {
if (packet_buffer[PKT_CMD] == CMD_PROBE &&
packet_buffer[PKT_DEST] != 0x60 &&
packet_buffer[PKT_DEST] != DEV_MASTER &&
firstProbe == 0x00)
{
firstProbe = packet_buffer[PKT_DEST];
printf("\nFirst Probe = 0x%02hhx\n",firstProbe);
} else if ( firstProbe != 0x00 && packet_buffer[PKT_DEST] == firstProbe && packet_buffer[PKT_CMD] == CMD_PROBE ) {
printf("\nGot probe again after %d packets\n",received_packets);
if (++probeCycleCnt > 2) {
_keepRunning = false;
}
} else if ( firstProbe != 0x00 && packet_buffer[PKT_DEST] == firstProbe && packet_buffer[PKT_CMD] != CMD_PROBE ) {
// Something connected to the first probe we saw, can't exit quickley
printf("\n Someone connected to probe 0x%02hhx\n",packet_buffer[PKT_DEST]);
}
}
//LOG(SLOG_LOG, LOG_DEBUG_SERIAL, "Received Packet for ID 0x%02hhx of type %s\n", packet_buffer[PKT_DEST], get_packet_type(packet_buffer, packet_length));
#ifdef SERIAL_LOGGER
if (logLevel > LOG_NOTICE)
@ -945,8 +978,10 @@ int _serial_logger(int rs_fd, char *port_name, int logPackets, int logLevel, boo
LOG(SLOG_LOG, LOG_NOTICE, "read_RS485_LX = yes\n");
if (found_chem)
LOG(SLOG_LOG, LOG_NOTICE, "read_RS485_Chem = yes\n");
if (found_iAqualnk && _panelPDA)
if (found_iAqualnk)
LOG(SLOG_LOG, LOG_NOTICE, "read_RS485_iAqualink = yes\n");
else if (!found_iAqualnk && (extID >= JANDY_DEV_AQLNK_MIN && extID <= JANDY_DEV_AQLNK_MAX))
LOG(SLOG_LOG, LOG_NOTICE, "enable_iaqualink = yes\n");
LOG(SLOG_LOG, LOG_NOTICE, "-------------------------\n");

View File

@ -398,7 +398,8 @@ char *cleanwhitespace(char *str)
while(end > str && isspace(*end)) end--;
// Write new null terminator
*(end+1) = 0;
if (end != (str + strlen(str) - 1) )
*(end+1) = 0;
return str;
}

View File

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

View File

@ -399,10 +399,10 @@
if (log_element_count >= LOGS2DISPLAY) {
try {
console.log("Removing " + logcontainer.lastElementChild.innerHTML);
//console.log("Removing " + logcontainer.lastElementChild.innerHTML);
logcontainer.lastElementChild.remove();
} catch (e) {
console.log("ERROR Removing log '" + logcontainer.lastElementChild.innerHTML + "'");
//console.log("ERROR Removing log '" + logcontainer.lastElementChild.innerHTML + "'");
log_element_count++;
}
} else {
@ -546,15 +546,15 @@
for (var obj in _config) {
if (_config[obj].value !== undefined) {
//if (_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) {
if (_config[obj][obj1].value) {
json_ordered.values[obj1] = _config[obj][obj1].value;
//}
}
}
}
}
@ -635,6 +635,10 @@
input.addEventListener('change', cfgValueChanged);
input.appendChild(option);
}
input.addEventListener('change', cfgValueChanged);
if (data[obj].force_restart == "yes") {
input.addEventListener('click', cfgAlertForceRestart);
}
cell2.appendChild(input);
} else if (data[obj].type == "string") {
const input = document.createElement("input");
@ -644,6 +648,9 @@
}
input.setAttribute('key', obj.toString());
input.addEventListener('change', cfgValueChanged);
if (data[obj].force_restart == "yes") {
input.addEventListener('click', cfgAlertForceRestart);
}
cell2.appendChild(input);
} else if (data[obj].type == "int") {
const input = document.createElement("input");
@ -655,6 +662,9 @@
input.step = 1;
input.setAttribute('key', obj.toString());
input.addEventListener('change', cfgValueChanged);
if (data[obj].force_restart == "yes") {
input.addEventListener('click', cfgAlertForceRestart);
}
cell2.appendChild(input);
} else if (data[obj].type == "float") {
const input = document.createElement("input");
@ -664,6 +674,9 @@
input.step = 0.01;
input.setAttribute('key', obj.toString());
input.addEventListener('change', cfgValueChanged);
if (data[obj].force_restart == "yes") {
input.addEventListener('click', cfgAlertForceRestart);
}
cell2.appendChild(input);
} else if (data[obj].type == "hex") {
const input = document.createElement("input");
@ -672,6 +685,9 @@
input.size = 4;
input.setAttribute('key', obj.toString());
input.addEventListener('change', cfgValueChanged);
if (data[obj].force_restart == "yes") {
input.addEventListener('click', cfgAlertForceRestart);
}
cell2.appendChild(input);
} else {
cell2.textContent = data[obj].value;
@ -989,8 +1005,8 @@
break;
case "pumpType":
js.type = "string";
let validvalues = ["", "JANDY ePUMP", "Pentair VS", "Pentair VF"];
js['valid values'] = validvalues;
//let validvalues = ["BLANK", "JANDY ePUMP", "Pentair VS", "Pentair VF"];
//js['valid values'] = validvalues;
break;
case "pumpName":
case "altLabel":
@ -1208,6 +1224,14 @@
}
function cfgAlertForceRestart(event) {
console.log("Caught event");
var key = event.srcElement.getAttribute('key');
var value = event.srcElement.value;
alert("If you change "+key+" You will need to restart AqualinkD after saving config!");
}
function cfgValueChanged(event) {
var key = event.srcElement.getAttribute('key');
var value = event.srcElement.value;
@ -1217,14 +1241,15 @@
try {
_config[key].value = value;
} catch (exception) {
console.log("Error setting cfg");
//console.error(exception);
//console.log("Error setting cfg '"+key+"' to '"+value+"'");
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
} else {
console.log("Error setting cfg '"+key+"' to '"+value+"'");
}
}
console.log(_config);
@ -1548,17 +1573,22 @@
send_command(cmd);
}
function getLatestVersion() {
function getLatestVersion(url="https://api.github.com/repos/AqualinkD/AqualinkD/releases/latest", tryagain=true) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
var JS = JSON.parse(this.responseText);
_latestVersionAvailable = JS.tag_name.replace(/[^\d.-]/g, '');
document.getElementById("latestaqualinkdversion").innerHTML = _latestVersionAvailable;
document.getElementById("latestaqualinkdversion").innerHTML = _latestVersionAvailable;
} else if (this.readyState == 4 && this.status == 404) {
//console.log("URL ERROR "+this.readyState+" "+this.status);
if (tryagain) {
// Try sfeakes repo
getLatestVersion("https://api.github.com/repos/sfeakes/AqualinkD/releases/latest", false);
}
}
};
xmlhttp.open("GET", "https://api.github.com/repos/sfeakes/AqualinkD/releases/latest", true);
xmlhttp.open("GET", url);
xmlhttp.send();
}
</script>

View File

@ -53,6 +53,7 @@
"Aux_V13",
"Aux_V14",
"Aux_V15",
"Chiller",
];
// This get's picked up by dynamic_config.js and used as mode 0

View File

@ -1,3 +1,8 @@
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";
_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 ZZZZ (XX=RS or PD, N=Circuits, ZZZ=Combo or Only or Dual)";
_confighelp["device_id"]="The id of the AqualinkD to use, use serial_logger to find ID's. If your panel is a PDA only model then PDA device ID is 0x60. set to device_id to 0xFF for to autoconfigure all this section";
_confighelp["mqtt_address"]="MQTT address has to be set to ip:port enable MQTT"
_confighelp["read_RS485_swg"]="Read device information directly from RS485 bus"
_confighelp["force_swg"]="Force any devices to be active at startup. Must set these for Home Assistant integration"
_confighelp["enable_scheduler"]="AqualinkD's internal scheduler"
_confighelp["event_check_use_scheduler_times"]="Turn on filter pump from events that can cause it to turn off"

View File

@ -1005,7 +1005,7 @@
value = '--';
} else {
if ((type = document.getElementById(id).getAttribute('type')) != null) {
if (type == 'temperature' || type == 'setpoint_thermo' || type == 'setpoint_freeze')
if (type == 'temperature' || type == 'setpoint_thermo' || type == 'setpoint_freeze' || type == 'setpoint_chiller')
ext = '&deg;';
else if (type == 'setpoint_swg')
ext = '%';
@ -1098,6 +1098,10 @@
return null;
}
function capitalizeFirstLetter(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
function formatSatus(status) {
var index;
var aux;
@ -1171,15 +1175,23 @@
else
text = 'Generating';
}
else if (type == 'setpoint_thermo')
else if (type == 'setpoint_thermo') {
//if (status == 'enabled')
text = 'Heat to ' + tile.getAttribute('setpoint');
//else
// text = 'Heating to ' + tile.getAttribute('setpoint'); // too large for phone
else if (type == 'setpoint_freeze')
}
else if (type == 'setpoint_chiller') {
try{
text = capitalizeFirstLetter(tile.getAttribute("mode"))+' to ' + tile.getAttribute('setpoint');
} catch (except){}
//text = 'Cool to ' + tile.getAttribute('setpoint');
}
else if (type == 'setpoint_freeze') {
text = 'Turn on ' + tile.getAttribute('setpoint') + "&deg;";
//else
// text = "On";
}
}
//document.getElementById(id + '_status').innerHTML = "On";
} else {
@ -1217,6 +1229,17 @@
tile_icon.classList.remove("disabled");
tile_icon.classList.remove("cool");
tile_icon.classList.add("heat");
} else if (status == 'on' && type == 'setpoint_chiller') {
tile_icon.classList.remove("enabled");
tile_icon.classList.remove("disabled");
console.log("mode="+tile_icon.parentElement.parentElement.getAttribute("mode"));
if ( tile_icon.parentElement.parentElement.getAttribute("mode") == "heat" ) {
tile_icon.classList.remove("cool");
tile_icon.classList.add("heat");
} else {
tile_icon.classList.remove("heat");
tile_icon.classList.add("cool");
}
} else if (status == 'on') {
tile_icon.classList.remove("enabled");
tile_icon.classList.remove("disabled");
@ -1289,7 +1312,7 @@
} else if (object.type == 'value' || object.type == 'temperature') {
add_tile(object.id, object.name, object.state, 'value', object.type);
setTileValue(object.id, object.value);
} else if (object.type == 'setpoint_thermo' || object.type == 'setpoint_swg' || object.type == 'setpoint_freeze') {
} else if (object.type == 'setpoint_thermo' || object.type == 'setpoint_swg' || object.type == 'setpoint_freeze' || object.type == 'setpoint_chiller') {
add_tile(object.id, object.name, object.state, 'thermostat', object.type);
document.getElementById(object.id).setAttribute('setpoint', object.spvalue);
setTileValue(object.id, object.value);
@ -1297,13 +1320,24 @@
} else {
//console.log("Unknown Device");
}
// Set Alternate name
if (object.alt_label !== undefined) {
//console.log("Set "+object.id+" label "+object.name+" alt_label "+object.alt_label);
document.getElementById(object.id).setAttribute('alt_label', object.alt_label);
document.getElementById(object.id).setAttribute('label', object.name);
//document.getElementById(object.id).setAttribute('id', object.id + '_status_line2');
}
}
function showTileOptions(show, id, contex) {
var active_option;
if (show == true) {
var wrapH = document.getElementById('wrapper').clientHeight + 'px';
if (id != null && document.getElementById(id).getAttribute('type') == 'setpoint_thermo') {
if (id != null && (document.getElementById(id).getAttribute('type') == 'setpoint_thermo' ||
document.getElementById(id).getAttribute('type') == 'setpoint_chiller')) {
active_option = document.getElementById('thermostat_options');
document.getElementById('swg_options').style.display = 'none';
document.getElementById('pswitch_options').style.display = 'none';
@ -1512,7 +1546,7 @@
slider.max = 5;
slider.step = 1;
}
} else if (type == 'setpoint_thermo') {
} else if (type == 'setpoint_thermo' || type == 'setpoint_chiller') {
if (_temperature_units != 'c') { // Change to DegF
//slider.min = 36;
//slider.max = 104;
@ -1874,6 +1908,7 @@
switch (obj.type) {
case "setpoint_thermo":
case "setpoint_freeze":
case "setpoint_chiller":
return "temperature";
break;
case "setpoint_swg":
@ -2056,6 +2091,7 @@
}
break
case "setpoint_thermo":
case "setpoint_chiller":
html = html + '<option value="' + V_SETPOINT + '" ' + cs_isSelected("setpoint", val1, val2) + '>Temp</option>'
break
case "setpoint_swg":
@ -2127,6 +2163,7 @@
if (device == obj.id) {
switch (obj.type) {
case "setpoint_thermo":
case "setpoint_chiller":
case "setpoint_freeze":
return "/setpoint";
break;
@ -2227,6 +2264,20 @@
setThermostatTile("Pool_Heater", data.pool_temp, data.pool_htr_set_pnt);
setThermostatTile("Spa_Heater", data.spa_temp, data.spa_htr_set_pnt);
setThermostatTile("Freeze_Protect", data.air_temp, data.frz_protect_set_pnt);
if ( data.chiller_set_pnt !== undefined ) {
// NSF Should use spa temp if in spa mode
if (data.leds.Spa !== undefined && data.leds.Spa == "on")
setThermostatTile("Chiller", data.spa_temp, data.chiller_set_pnt);
else
setThermostatTile("Chiller", data.pool_temp, data.chiller_set_pnt);
}
if ( data.chiller_mode !== undefined ) {
try{
let elmt = document.getElementById("Chiller");
elmt.setAttribute("mode", data.chiller_mode);
} catch (exception){}
}
setTileValue("Temperature/Air", data.air_temp);
setTileValue("Temperature/Pool", data.pool_temp);
@ -2269,6 +2320,19 @@
//console.log("TIMER "+obj.toString()+" duration "+data.timer_durations[obj]);
}
for (var obj in data.alternate_modes) {
//console.log(obj.toString() +" "+ data.alternate_modes[obj.toString()] );
try {
let element = document.getElementById(obj.toString());
let label = document.getElementById(obj.toString()+'_name');
if ( data.alternate_modes[obj.toString()] == "on") {
label.textContent = element.getAttribute('label')+" "+element.getAttribute('alt_label');
} else {
label.textContent = element.getAttribute('label');
}
} catch (exception){}
}
for (var obj in data.light_program_names) {
if (data.light_program_names[obj] != "") {
var light_mode_name = data.light_program_names[obj];
@ -2295,7 +2359,7 @@
}
} catch (e) { /*console.log(e);*/ }
if (light_mode_name != "-999%" && light_mode_name != "Off")
if (light_mode_name != "-999%" && light_mode_name != "Off" && light_mode_name != "off")
setTileOnText(obj.toString(),light_mode_name);
try {