Compare commits

...

17 Commits

Author SHA1 Message Date
sfeakes be9a518ffa Version 2.6.9 2025-07-26 20:41:14 -05:00
sfeakes 71bc573f61 Release 2.6.8 2025-06-29 18:28:52 -05:00
sfeakes f95310d170 Update for 2.6.8 (Dev) 2025-06-14 15:56:27 -05:00
sfeakes 0518337472
Merge pull request #433 from locnho/pda-ps6-swg-prog-fix
Fix failure setting SWG percent
2025-06-14 15:49:40 -05:00
sfeakes de5697bb25
Merge pull request #430 from locnho/psa-ps6-freeze-prot-fix
Fix #79: Freeze protect falsely reported as off from ballle98
2025-06-14 15:34:53 -05:00
sfeakes 9d5d90937b 2.6.8 (Dev) 2025-06-14 15:32:35 -05:00
sfeakes e513f14dd6
Merge pull request #428 from locnho/pda-boost-pool-menu-fix
Fix PDA menu with 'BOOST POOL' instead 'BOOST
2025-06-14 15:30:05 -05:00
sfeakes 62943ab133 2.6.8 (Dev) 2025-06-14 13:14:11 -05:00
locnho c4cc4504d9 Fix failure setting SWG percent
At time, setting the SWG percent doesn't work. Instead, it turns off the
filter pump. Require to bring back to the HOME menu with the
first item highlighted.
Add an option to got back to PDM Home menu with goto_pda_menu.

Signed-off-by: locnho <locnhinho@gmail.com>
2025-06-08 16:18:32 -07:00
Loc Ho 9a357841aa Fix #79: Freeze protect falsely reported as off from ballle98
Signed-off-by: Loc Ho <locnhinho@gmail.com>
2025-06-08 15:48:46 -07:00
sfeakes 5b704cf3fb Version 2.6.7 2025-05-31 14:44:10 -05:00
Loc Ho f7846a8f91 Fix PDA menu with 'BOOST POOL' instead 'BOOST
Signed-off-by: Loc Ho <locnhinho@gmail.com>
2025-05-31 09:45:04 -07:00
sfeakes d7e45594b3 Version 2.6.6 2025-05-23 18:11:24 -05:00
sfeakes 94fc20191b Update install.sh 2025-05-17 16:37:53 -05:00
sfeakes c06a0037b6 Update install.sh 2025-05-17 14:55:15 -05:00
sfeakes dd7161ef08 Update install.sh 2025-05-17 14:48:10 -05:00
sfeakes d182784ae2 Update 2025-05-17 14:15:16 -05:00
46 changed files with 2128 additions and 781 deletions

View File

@ -1,293 +0,0 @@
#####################################
#
# Build container to compile AqualnkD Release binaries (armhf and arm64)
#
# armhf is 32 bit armv6l (armhf) stretch and newer - work on all Pi's running 32bit (Pi1 to Pi4)
# arm64 is 64 bit aarch64 buster and newer - work on Pi3/Pi4/2w running 64bit os
#
# docker build -f Dockerfile.releaseBinaries2 -t aqualinkd-releasebin2 .
# For better debug logs use --progress=plain
# docker build --progress=plain -f Dockerfile.releaseBinaries2 -t aqualinkd-releasebin2 .
#
# docker run -it --mount type=bind,source=./build,target=/build aqualinkd-releasebin2 bash
#
# clean method
# docker system prune
# docker buildx prune <- just build env
#
# armhf =
# COLLECT_GCC=arm-linux-gnueabihf-gcc
# COLLECT_LTO_WRAPPER=/opt/cross-pi-gcc/libexec/gcc/arm-linux-gnueabihf/6.3.0/lto-wrapper
# Target: arm-linux-gnueabihf
# Configured with: ../gcc-6.3.0/configure --prefix=/opt/cross-pi-gcc --target=arm-linux-gnueabihf --enable-languages=c,c++,fortran --with-arch=armv6 --with-fpu=vfp --with-float=hard --disable-multilib --enable-linker-build-id
# Thread model: posix
# gcc version 6.3.0 (GCC)
# GLIBC version 2.24
#
# arm64 =
# COLLECT_GCC=aarch64-linux-gnu-gcc
# COLLECT_LTO_WRAPPER=/usr/lib/gcc-cross/aarch64-linux-gnu/8/lto-wrapper
# Target: aarch64-linux-gnu
# Configured with: ../src/configure -v --with-pkgversion='Debian 8.3.0-2' --with-bugurl=file:///usr/share/doc/gcc-8/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++ --prefix=/usr --with-gcc-major-version-only --program-suffix=-8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-libquadmath --disable-libquadmath-support --enable-plugin --enable-default-pie --with-system-zlib --disable-libphobos --enable-multiarch --enable-fix-cortex-a53-843419 --disable-werror --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=aarch64-linux-gnu --program-prefix=aarch64-linux-gnu- --includedir=/usr/aarch64-linux-gnu/include
# Thread model: posix
# gcc version 8.3.0 (Debian 8.3.0-2)
# GLIBC 2.28-10+deb10u3
#####################################
FROM debian:buster
# ############
# Get arm64 build environment.
#
RUN apt-get update && \
apt-get install -y \
build-essential \
libsystemd-dev \
gcc-aarch64-linux-gnu \
binutils-arm-linux-gnueabi \
file
RUN dpkg --add-architecture arm64
RUN apt-get update && \
apt-get install -y libsystemd-dev:arm64
# ############
# Get armhf build environment
# prebuilt armhf doesn't support hard float, (or something that causes it to fail on armhf machines)
#RUN apt-get install -y \
# gcc-arm-linux-gnueabihf \
# binutils-arm-linux-gnueabihf
#RUN dpkg --add-architecture armhf
#RUN apt-get update && \
# apt-get install -y libsystemd-dev:armhf
# So we need to build arnhf our selves. Since we are doing that, using debian/rasbian stretch versions of
# everthing for best compatibality
ENV GCC_VERSION gcc-6.3.0
ENV GLIBC_VERSION glibc-2.24
ENV BINUTILS_VERSION binutils-2.28
ARG DEBIAN_FRONTEND=noninteractive
# Install some tools and compilers + clean up
RUN apt-get update && \
#apt-get install -y rsync git wget gcc-6 g++-6 cmake gdb gdbserver bzip2 && \
apt-get install -y rsync git wget cmake gdb gdbserver bzip2 && \
apt-get clean autoclean && \
apt-get autoremove -y && \
rm -rf /var/lib/apt/lists/*
# Use GCC 6 as the default
#RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-6 999 \
# && update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-6 999 \
# && update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-6 999 \
# && update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++-6 999
# Add a user called `develop`
RUN useradd -ms /bin/bash develop
RUN echo "develop ALL=(ALL:ALL) ALL" >> /etc/sudoers
WORKDIR /home/develop
# Download and extract GCC
RUN wget https://ftp.gnu.org/gnu/gcc/${GCC_VERSION}/${GCC_VERSION}.tar.gz && \
tar xf ${GCC_VERSION}.tar.gz && \
rm ${GCC_VERSION}.tar.gz
# Download and extract LibC
RUN wget https://ftp.gnu.org/gnu/libc/${GLIBC_VERSION}.tar.bz2 && \
tar xjf ${GLIBC_VERSION}.tar.bz2 && \
rm ${GLIBC_VERSION}.tar.bz2
# Download and extract BinUtils
RUN wget https://ftp.gnu.org/gnu/binutils/${BINUTILS_VERSION}.tar.bz2 && \
tar xjf ${BINUTILS_VERSION}.tar.bz2 && \
rm ${BINUTILS_VERSION}.tar.bz2
# Download the GCC prerequisites
RUN cd ${GCC_VERSION} && contrib/download_prerequisites && rm *.tar.*
#RUN cd gcc-9.2.0 && contrib/download_prerequisites && rm *.tar.*
# Build BinUtils
RUN mkdir -p /opt/cross-pi-gcc
WORKDIR /home/develop/build-binutils
RUN ../${BINUTILS_VERSION}/configure \
--prefix=/opt/cross-pi-gcc --target=arm-linux-gnueabihf \
--with-arch=armv6 --with-fpu=vfp --with-float=hard \
--disable-multilib
RUN make -j$(nproc)
RUN make install
# Apply batch to GCC
# https://github.com/qca/open-ath9k-htc-firmware/issues/135
WORKDIR /home/develop
RUN sed -i '1474s/file ==/file[0] ==/' gcc-6.3.0/gcc/ubsan.c
# Build the first part of GCC
WORKDIR /home/develop/build-gcc
RUN ../${GCC_VERSION}/configure \
--prefix=/opt/cross-pi-gcc \
--target=arm-linux-gnueabihf \
--enable-languages=c,c++,fortran \
--with-arch=armv6 --with-fpu=vfp --with-float=hard \
--disable-multilib \
--enable-linker-build-id
RUN make -j$(nproc) 'LIMITS_H_TEST=true' all-gcc
RUN make install-gcc
ENV PATH=/opt/cross-pi-gcc/bin:${PATH}
# Install dependencies
RUN apt-get update && \
apt-get install -y gawk bison python3 && \
apt-get clean autoclean && \
apt-get autoremove -y && \
rm -rf /var/lib/apt/lists/*
# Download and install the Linux headers
WORKDIR /home/develop
# Should probably use below and change branch. Known to build with rpi-6.1.y or rpi-6.9.y rpi-6.12.y
#RUN git clone -b <branch> --depth=1 https://github.com/raspberrypi/linux
RUN git clone --depth=1 https://github.com/raspberrypi/linux
WORKDIR /home/develop/linux
ENV KERNEL=kernel7
RUN make ARCH=arm INSTALL_HDR_PATH=/opt/cross-pi-gcc/arm-linux-gnueabihf headers_install
# Build GLIBC
WORKDIR /home/develop/build-glibc
RUN ../${GLIBC_VERSION}/configure \
--prefix=/opt/cross-pi-gcc/arm-linux-gnueabihf \
--build=$MACHTYPE --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf \
--with-arch=armv6 --with-fpu=vfp --with-float=hard \
--with-headers=/opt/cross-pi-gcc/arm-linux-gnueabihf/include \
--disable-multilib libc_cv_forced_unwind=yes
RUN make install-bootstrap-headers=yes install-headers
RUN make -j8 csu/subdir_lib
RUN install csu/crt1.o csu/crti.o csu/crtn.o /opt/cross-pi-gcc/arm-linux-gnueabihf/lib
RUN arm-linux-gnueabihf-gcc -nostdlib -nostartfiles -shared -x c /dev/null \
-o /opt/cross-pi-gcc/arm-linux-gnueabihf/lib/libc.so
RUN touch /opt/cross-pi-gcc/arm-linux-gnueabihf/include/gnu/stubs.h
# Continue building GCC
WORKDIR /home/develop/build-gcc
RUN make -j$(nproc) all-target-libgcc
RUN make install-target-libgcc
# Finish building GLIBC
WORKDIR /home/develop/build-glibc
RUN make -j$(nproc)
RUN make install
# Finish building GCC
WORKDIR /home/develop/build-gcc
RUN make -j$(nproc)
RUN make install
# Download systemd and it's dependancys.
RUN mkdir -p /home/develop/packages
WORKDIR /home/develop/packages
####################
# Manually libsystemd-dev and all it's depandancys
# Commented out ones are what I really want, but couldn;t find.
#####RUN wget https://archive.debian.org/debian/pool/main/s/systemd/libsystemd-dev_232-25+deb9u14_armhf.deb
#RUN wget https://archive.debian.org/debian/pool/main/s/systemd/libsystemd-dev_232-25+deb9u12_armhf.deb
#####RUN wget https://archive.debian.org/debian/pool/main/s/systemd/libsystemd0_232-25+deb9u14_armhf.deb
#RUN wget https://archive.debian.org/debian/pool/main/s/systemd/libsystemd0_232-25+deb9u12_armhf.deb
#RUN wget https://archive.debian.org/debian/pool/main/g/glibc/libc6_2.24-11+deb9u4_armhf.deb
#RUN wget https://archive.debian.org/debian/pool/main/libg/libgpg-error/libgpg-error0_1.26-2_armhf.deb
#####RUN wget https://archive.debian.org/debian/pool/main/x/xz-utils/liblzma5_5.2.2-1.2+deb9u1_armhf.deb
#RUN wget https://archive.debian.org/debian/pool/main/x/xz-utils/liblzma5_5.2.2-1.2+b1_armhf.deb
#RUN wget https://archive.debian.org/debian/pool/main/libs/libselinux/libselinux1_2.6-3+b3_armhf.deb
#####RUN wget https://archive.debian.org/debian/pool/main/libg/libgcrypt20//libgcrypt20_1.7.6-2+deb9u4_armhf.deb
#RUN wget https://archive.debian.org/debian/pool/main/libg/libgcrypt20//libgcrypt20_1.7.6-2+deb9u3_armhf.deb
#####RUN wget https://archive.debian.org/debian/pool/main/l/lz4/liblz4-1_0.0~r131-2+deb9u1_armhf.deb
#RUN wget https://archive.debian.org/debian/pool/main/l/lz4/liblz4-1_0.0~r131-2+b1_armhf.deb
#####RUN wget https://archive.debian.org/debian/pool/main/p/pcre3/libpcre3_2%3a8.39-3_armhf.deb
#RUN wget https://archive.debian.org/debian/pool/main/p/pcre3/libpcre3_8.39-3_armhf.deb
#
# Now we have all packaged, let's unpack them.
#
# Install all packages into /opt/cross-pi-gcc/arm-linux-gnueabihf
#RUN for file in *; do dpkg-deb -x $file /opt/cross-pi-gcc/arm-linux-gnueabihf; done
#
####################
# Rather than manually, Let's do some modifications to apt and get that working (kinda)
# Get just enough for apt-get and dpk to run. apt-get doesn't actually work, just enough to download
ENV APT_ROOT=/opt/cross-pi-gcc/apt-armhf
RUN mkdir -p $APT_ROOT
#RUN APT_ROOT=/opt/cross-pi-gcc/apt-armhf; export APT_ROOT
RUN mkdir -p $APT_ROOT/etc/apt/sources.list.d/
RUN mkdir -p $APT_ROOT/var/lib/dpkg/updates/
RUN mkdir -p $APT_ROOT/var/lib/dpkg/info
RUN mkdir -p $APT_ROOT/var/cache/apt/archives/partial
RUN mkdir -p $APT_ROOT/var/log/apt/
#mkdir -p $APT_ROOT/usr/share/
RUN echo "deb http://archive.debian.org/debian/ stretch main contrib non-free" > $APT_ROOT/etc/apt/sources.list
RUN echo "deb http://archive.debian.org/debian/ stretch-proposed-updates main contrib non-free" >> $APT_ROOT/etc/apt/sources.list
RUN echo "deb http://archive.debian.org/debian-security stretch/updates main contrib non-free" >> $APT_ROOT/etc/apt/sources.list
RUN touch $APT_ROOT/var/lib/dpkg/status
RUN ln -s /etc/apt/trusted.gpg.d $APT_ROOT/etc/apt/
RUN ln -s /etc/apt/preferences.d $APT_ROOT/etc/apt/
RUN ln -s /etc/apt/auth.conf.d $APT_ROOT/etc/apt/
# needed for download
RUN dpkg --add-architecture armhf
# needed for install
RUN dpkg --root=$APT_ROOT --add-architecture armhf
RUN apt -o Dir=$APT_ROOT update
RUN apt -o Dir=$APT_ROOT download libsystemd-dev:armhf \
libsystemd0:armhf \
libc6:armhf \
libgcrypt20:armhf \
liblz4-1:armhf \
liblzma5:armhf \
libselinux1:armhf \
libpcre3:armhf \
libgpg-error0:armhf
############
# Now we have all packaged, let's unpack them.
# Install all packages into /opt/cross-pi-gcc/arm-linux-gnueabihf
# Could use `dpkg --root=$APT_ROOT --force-all -i` in below, but extract works without any warnings.
RUN for file in *; do dpkg -x $file /opt/cross-pi-gcc/arm-linux-gnueabihf; done
# the above will ge installed in /opt/cross-pi-gcc/arm-linux-gnueabihf/lib/arm-linux-gnueabihf,
# and we need them in /opt/cross-pi-gcc/arm-linux-gnueabihf/lib/, so make come links.
WORKDIR /opt/cross-pi-gcc/arm-linux-gnueabihf/lib
RUN for file in ./arm-linux-gnueabihf/*; do ln -s $file ./`basename $file` 2> /dev/null; done; exit 0
# liblz4.so.1 is installed in a different directory, so link that as well.
RUN ln -s /opt/cross-pi-gcc/arm-linux-gnueabihf/usr/lib/arm-linux-gnueabihf/liblz4.so.1 /opt/cross-pi-gcc/arm-linux-gnueabihf/lib/liblz4.so.1
ENV C_INCLUDE_PATH=/opt/cross-pi-gcc/arm-linux-gnueabihf/usr/include
ENV PATH=$PATH:/opt/cross-pi-gcc/bin:/opt/cross-pi-gcc/libexec/gcc/arm-linux-gnueabihf/6.3.0/
RUN mkdir /build
WORKDIR /build
# Add a user called `build` uid 1001 & gid 10000
# You chould change RB_UID & RB_GID to what works on your build setup
ENV RB_USER=build
ENV RB_UID=1001
ENV RB_GID=1000
RUN groupadd -g $RB_GID $RB_USER 2> /dev/null; exit 0
RUN useradd $RB_USER -u $RB_UID -g $RB_GID -m -s /bin/bash
RUN echo "$RB_USER ALL=(ALL:ALL) ALL" >> /etc/sudoers
USER $RB_USER

View File

@ -141,6 +141,7 @@ 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
DD_SRC = dummy_device.c aq_serial.c utils.c packetLogger.c rs_msg_utils.c timespec_subtract.c
DR_SRC = dummy_reader.c aq_serial.c utils.c packetLogger.c rs_msg_utils.c timespec_subtract.c
# Build durectories
SRC_DIR := ./source
@ -148,6 +149,7 @@ OBJ_DIR := ./build
DBG_OBJ_DIR := $(OBJ_DIR)/debug
SL_OBJ_DIR := $(OBJ_DIR)/slog
DD_OBJ_DIR := $(OBJ_DIR)/dummydevice
DR_OBJ_DIR := $(OBJ_DIR)/dummyreader
INCLUDES := -I$(SRC_DIR)
@ -164,12 +166,14 @@ 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))
DR_SRC := $(patsubst %.c,$(SRC_DIR)/%.c,$(DR_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))
DR_OBJ_FILES := $(patsubst $(SRC_DIR)/%.c,$(DR_OBJ_DIR)/%.o,$(DR_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))
@ -199,6 +203,7 @@ MAIN = ./release/aqualinkd
SLOG = ./release/serial_logger
DEBG = ./release/aqualinkd-debug
DDEVICE = ./release/dummydevice
DREADER = ./release/dummyreader
MAIN_ARM64 = ./release/aqualinkd-arm64
MAIN_ARMHF = ./release/aqualinkd-armhf
@ -273,6 +278,9 @@ aqdebug: $(DEBG)
dummydevice: $(DDEVICE)
$(info $(DDEVICE) has been compiled)
dummyreader: $(DREADER)
$(info $(DREADER) has been compiled)
# Container, add container flag and compile
container: CFLAGS := $(CFLAGS) -D AQ_CONTAINER
container: $(MAIN) $(SLOG)
@ -327,6 +335,9 @@ $(SL_OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(SL_OBJ_DIR)
$(DD_OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(DD_OBJ_DIR)
$(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
$(DR_OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(DR_OBJ_DIR)
$(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
$(OBJ_DIR_ARMHF)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR_ARMHF)
$(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
@ -381,6 +392,10 @@ $(DDEVICE): CFLAGS := $(CFLAGS) -D SERIAL_LOGGER -D DUMMY_DEVICE
$(DDEVICE): $(DD_OBJ_FILES)
$(CC) $(CFLAGS) $(INCLUDES) -o $@ $^ $(LIBS)
$(DREADER): CFLAGS := $(CFLAGS) -D SERIAL_LOGGER -D DUMMY_DEVICE -D DUMMY_READER
$(DREADER): $(DR_OBJ_FILES)
$(CC) $(CFLAGS) $(INCLUDES) -o $@ $^ $(LIBS)
# Rules to make object directories.
$(OBJ_DIR):
$(MKDIR) $(call FixPath,$@)
@ -391,6 +406,9 @@ $(SL_OBJ_DIR):
$(DD_OBJ_DIR):
$(MKDIR) $(call FixPath,$@)
$(DR_OBJ_DIR):
$(MKDIR) $(call FixPath,$@)
$(DBG_OBJ_DIR):
$(MKDIR) $(call FixPath,$@)
@ -415,10 +433,10 @@ $(SL_OBJ_DIR_AMD64):
# Clean rules
clean: clean-buildfiles
$(RM) *.o *~ $(MAIN) $(MAIN_U) $(PLAY) $(PL_EXOBJ) $(DEBG) $(DDEVICE)
$(RM) *.o *~ $(MAIN) $(MAIN_U) $(PLAY) $(PL_EXOBJ) $(DEBG) $(DDEVICE) $(DREADER)
$(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) $(DD_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) $(DR_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

@ -91,6 +91,7 @@ Designed to mimic AqualinkRS devices, used to fully configure the master control
# ToDo (future release)
* Create iAqualink Touch Simulator
* AqualinkD to self configure. (Done for ID's, need to do for Panel type/size)
* Support for (non Jandy) external ORP and Ph sensors
<!--
@ -125,17 +126,52 @@ NEED TO FIX FOR THIS RELEASE.
* Try an auto-update
* Update Mongoose
* PDA Crap.
* pda_aq_programmer line 702/703 color light.
* pda_aq_programmer line 782 back menu testing in pda_init
* FIX Panel name in config to accept output from panel actual name
-->
# Updates in 2.6.5
# Updates in 2.6.9 (July 26 2025)
* Config fixes for 0x33 ID / PDA
* Changes to virtual buttons and light modes
* Updates to aqmanager & config editor
* Updates to jandy device logging & heatpump performance.
* Increased performance of external sensors
* Added unit of measure for External sensors
* External sensors now have rexexp support (good for onewire devices)
# Updates in 2.6.8 (June 29 2025)
* Fixed some UI bugs, added download config option
* Changes to config options & config editor
* Heatpump / chiller updates
* PDA updates (detect temperature units, other small changes)
# Updates in 2.6.7 (May 23 2025)
* Fixed bug with iaqualink protocol when no virtual buttons configured.
* Updated RS timing debug messages.
# Updates in 2.6.6 (May 23 2025)
* Fixed some HTTP response codes.
* Added checks for protocols vs panel revision.
* Fixed auto_configure for panel REV I & K.
* Updates to install scripts.
* Update to WebUI dimmers.
# Updates in 2.6.5 (May 5 2025)
* Changes to virtual buttons.
# Updates in 2.6.4
# Updates in 2.6.4 (Apr 28 2025)
* Fix docker crash where journal not configured correctly.
* Updates to config editor.
* Increased number of virtual buttons. (previous limitation only effected RS 16 panels).
* Changed to dimmable lights.
# Updates in 2.6.3
# Updates in 2.6.3 (April 13 2025)
* AqualinkD can how self-update directly from github. use `Upgrade AqualinkD` button in Aqmanager
* New install and remote_install scripts.
* Changed MQTT posting frequency when Timers are enabled for better
@ -145,13 +181,13 @@ NEED TO FIX FOR THIS RELEASE.
* -or-
* `curl -fsSL https://raw.githubusercontent.com/aqualinkd/AqualinkD/master/release/remote_install.sh | sudo bash -s -- latest`
# Updates in 2.6.1
# Updates in 2.6.1 (Mar 26 2025)
* Added External Sensors to Web UI & HomeKit.
* Added Heat Pump / Chiller Thermostat to Web UI & HomeKit.
* Fixed some bugs in Configuration Editor.
* Link device/virtual/onetouch button with SWG BOOST. (Allows you to set VSP RPM when in Boost mode)
# Updates in 2.6.0
# Updates in 2.6.0 (Mar 22 2025)
* Added configuration editor in aqmanager. [Wiki - AQManager](https://github.com/aqualinkd/AqualinkD/wiki#AQManager)
* Can now self-configure on startup. set `device_id=0xFF`
* Added scheduling of pump after events (Power On, Freeze Protect, Boost)
@ -162,8 +198,10 @@ NEED TO FIX FOR THIS RELEASE.
* Reworked PDA sleep mode.
* Added support for Heat Pump / Chiller support.
# Updates in 2.5.1 (Dec 7 2024)
* Patch
# Updates in 2.5.0
# Updates in 2.5.0 (Nov 16 2024)
* PDA panel Rev 6.0 or newer that do not have a Jandy iAqualink device attached can use the AqualinkTouch protocol rather than PDA protocol.
* This is faster, more reliable and does not intefear with the physical PDA device (like existing implimentation)
* Please consider this very much BETA at the moment.
@ -183,7 +221,7 @@ NEED TO FIX FOR THIS RELEASE.
* Faster OneTouch device support.
# Updates in Release 2.4.0
# Updates in Release 2.4.0 (Sept 7 2024)
* <b>WARNING</b> Breaking change if you use dimmer (please change button_??_lightMode from 6 to 10)
* Fixed bugs with particular Jandy panel versions and color lights.
* Added support for more color lights, and sped up programming
@ -200,10 +238,10 @@ NEED TO FIX FOR THIS RELEASE.
* look at `virtual_button??_label` in aqualinkd.conf
* have to use AqualinkTouch protocol for `extended_device_id` 0x31->0x33 in aqualinkd.conf
# Updates in Release 2.3.8
# Updates in Release 2.3.8 (Sept 3 2024)
* Release removed. Bug with light program 0.
# Updates in Release 2.3.7
# Updates in Release 2.3.7 (Jun 11 2024)
* Fix for Pentair VSP losing connection & bouncing SWG to 0 and back.
* Added more VSP data (Mode, Status, Pressure Curve, both RPM & GPM) for all Pentair Pumps (VS/VF/VSF).
* Few updates to HomeAssistant integration.
@ -215,14 +253,14 @@ NEED TO FIX FOR THIS RELEASE.
* Fix freeze protect button in UI not showing enabled.
* Few updates to AQmanager UI.
# Update in Release 2.3.6
# Update in Release 2.3.6 (May 31 2024)
* No functionality changes
* Build & Docker changes
* Going forward AqualinkD will release binaries for both armhf & arm64
* armhf = any Pi (or equiv board) running 32 bit Debain based OS, stretch or newer
* arm64 = Pi3/4/2w running 64 bit Debain based OS, buster or newer
# Update in Release 2.3.5
# Update in Release 2.3.5 (May 23 2024)
* Added Home Assistant integration through MQTT discover
* Please read the Home Assistant section of the [Wiki - HASSIO](https://github.com/aqualinkd/AqualinkD/wiki#HASSIO)
* There are still some enhacments to come on this.
@ -232,7 +270,7 @@ NEED TO FIX FOR THIS RELEASE.
* Added Color Light to iAqualinkTouch protocol.
* Fixed issue mqtt_timed_update (1~2 min rather than between 2 & 20 min)
# Update in Release 2.3.4
# Update in Release 2.3.4 (May 13 2024)
* Changes for Docker
* Updated simulator code base and added new simulators for AllButton, OneTouch & PDA.
* <aqualinkd.ip>/allbutton_sim.html
@ -241,7 +279,7 @@ NEED TO FIX FOR THIS RELEASE.
* On PDA only panel AqualinkD has to share the same ID with the PDA simulator. Therefore for AqualinkD will not respond to commands while simulator is active.
* Now you can completely program the control panel with the simulators removing the need to have Jandy device.
# Update in Release 2.3.3
# Update in Release 2.3.3 (May 30 2024)
* Introduced Aqualink Manager UI http://aqualink.ip/aqmanager.html
* [AqualinkD Manager](https://github.com/aqualinkd/AqualinkD/wiki#AQManager)
* Moved logging into systemd/journal (journalctl) from syslog
@ -254,12 +292,12 @@ NEED TO FIX FOR THIS RELEASE.
* Add ```rs485_frame_delay = 4``` to /etc/aqualinkd.conf, 4 is number of milliseconds between frames, 0 will turn off ie no pause.
* PDA Changes to support SWG and Boot.
# Update in Release 2.3.2
# Update in Release 2.3.2 (Jul 8 2023)
* Added support for VSP on panel versions REV 0.1 & 0.2
* Can change heater sliver min/max values in web UI. `./web/config.js`
* Added reading ePump RPM/Watts directly from RS485 bus.
# Update in Release 2.3.1
# Update in Release 2.3.1 (Jun 23 2023)
* Changed a lot of logic around different protocols.
* Added low latency support for FTDI usb driver.
* AqualinkD will find out the fastest way to change something depending on the protocols available.

Binary file not shown.

Binary file not shown.

View File

@ -1,32 +0,0 @@
# aqualinkd - aqualinkd job file
#
# check with `initctl check-config aqualinkd`
#
description "aqualink RS daemon service"
author "Me <myself@i.com>"
# Stanzas
#
# Stanzas control when and how a process is started and stopped
# See a list of stanzas here: http://upstart.ubuntu.com/wiki/Stanzas#respawn
# When to start the service
start on runlevel [2345]
# When to stop the service
stop on runlevel [016]
# Automatically restart process if crashed
respawn
# Essentially lets upstart know the process will detach itself to the background
expect fork
# Run before process
#pre-start script
# [ -d /var/run/myservice ] || mkdir -p /var/run/myservice
# echo "Put bash code here"
#end script
# Start the process
exec /usr/local/bin/aqualinkd -c /etc/aqualinkd/aqualinkd.conf

View File

@ -5,7 +5,7 @@
BUILD="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PARENT_COMMAND=$(ps -o comm= $PPID)
PARENT_COMMAND=$(ps -o comm= $PPID 2>/dev/null)
SERVICE="aqualinkd"
@ -30,9 +30,10 @@ REMOUNT_RO=1
TRUE=0
FALSE=1
_logfile="";
_frommake=$FALSE;
_ignorearch=$FALSE;
_logfile=""
_frommake=$FALSE
_ignorearch=$FALSE
_nosystemd=$FALSE
log()
{
@ -59,7 +60,7 @@ printHelp()
}
log "Called $0 with $*"
#log "Called $0 with $*"
while [[ $# -gt 0 ]]; do
case "$1" in
@ -83,6 +84,9 @@ while [[ $# -gt 0 ]]; do
ignorearch)
_ignorearch=$TRUE
;;
nosystemd)
_nosystemd=$TRUE
;;
help | -help | --help | -h)
printHelp
exit $TRUE;
@ -106,15 +110,17 @@ if [[ $EUID -ne 0 ]]; then
exit 1
fi
if [[ $(mount | grep " / " | grep "(ro,") ]]; then
if mount / -o remount,rw &>/dev/null; then
if [ "$_nosystemd" -eq $FALSE ]; then
if [[ $(mount | grep " / " | grep "(ro,") ]]; then
if mount / -o remount,rw &>/dev/null; then
# can mount RW.
#mount / -o remount,rw &>/dev/null
log "Root filesystem is readonly, remounted RW"
REMOUNT_RO=0;
else
log "Root filesystem is readonly, can't install"
exit 1
log "Root filesystem is readonly, remounted RW"
REMOUNT_RO=0;
else
log "Root filesystem is readonly, can't install"
exit 1
fi
fi
fi
@ -170,9 +176,11 @@ fi
# Exit if we can't find systemctl
command -v systemctl >/dev/null 2>&1 || { log "This script needs systemd's systemctl manager, Please check path or install manually" >&2; exit 1; }
# stop service, hide any error, as the service may not be installed yet
systemctl stop $SERVICE > /dev/null 2>&1
SERVICE_EXISTS=$(echo $?)
if [ "$_nosystemd" -eq $FALSE ]; then
# stop service, hide any error, as the service may not be installed yet
systemctl stop $SERVICE > /dev/null 2>&1
SERVICE_EXISTS=$(echo $?)
fi
# Clean everything if requested.
if [ "$1" == "clean" ]; then
@ -202,19 +210,12 @@ fi
# Check cron.d options
if [ ! -d "/etc/cron.d" ]; then
log "The version of Cron may not support chron.d, if so AqualinkD Scheduler will not work"
log "Please check before starting"
else
if [ -f "/etc/default/cron" ]; then
CD=$(cat /etc/default/cron | grep -v ^# | grep "\-l")
if [ -z "$CD" ]; then
log "Please enabled cron.d support, if not AqualinkD Scheduler will not work"
log "Edit /etc/default/cron and look for the -l option, usually in EXTRA_OPTS"
fi
else
log "Please make sure the version if Cron supports chron.d, if not the AqualinkD Scheduler will not work"
if systemctl is-active --quiet cron.service; then
if [ ! -d "/etc/cron.d" ]; then
log "The version of cron installed may not support chron.d, if so AqualinkD Scheduler will not work"
fi
else
log "Please install cron, if not the AqualinkD Scheduler will not work"
fi
# V2.3.9 & V2.6.0 has kind-a breaking change for config.js, so check existing and rename if needed
@ -224,7 +225,8 @@ if [ -f "$WEBLocation/config.js" ]; then
# Version 2.6.0 added Chiller as well
if ! grep -q '"Aux_V1"' "$WEBLocation/config.js" ||
! grep -q '"Spa"' "$WEBLocation/config.js" ||
! grep -q '"Chiller"' "$WEBLocation/config.js"; then
! grep -q '"Chiller"' "$WEBLocation/config.js" ||
! grep -q '"Aux_S1"' "$WEBLocation/config.js"; then
dateext=`date +%Y%m%d_%H_%M_%S`
log "AqualinkD web config is old, making copy to $WEBLocation/config.js.$dateext"
log "Please make changes to new version $WEBLocation/config.js"
@ -287,6 +289,11 @@ fi
# remount root ro
if [[ $REMOUNT_RO -eq 0 ]]; then
mount / -o remount,ro &>/dev/null
log "Root filesystem remounted RO"
fi
if [ "$_nosystemd" -eq $TRUE ]; then
exit 0
fi
systemctl enable $SERVICE

View File

@ -45,6 +45,7 @@ log()
echo "$*"
if [ "$SYSTEMD_LOG" -eq $TRUE ]; then
# For some unknown reason, only way below works from aqualinkd process is adding "&>> "$OUTPUT""
echo "Upgrade: $*" | systemd-cat -t aqualinkd -p info &>> "$OUTPUT"
else
logger -p local0.notice -t aqualinkd "Upgrade: $*"
@ -258,7 +259,7 @@ function cleanup {
#
# See if we are called from curl ot local dir.
# See if we are called from curl or local dir.
# with curl no tty input and script name wil be blank.
if ! tty > /dev/null 2>&1; then
script=$(basename "$0")

Binary file not shown.

Binary file not shown.

View File

@ -612,10 +612,12 @@ void *set_allbutton_light_programmode( void *ptr )
} else {
for (i = 1; i < val; i++) {
const int dt = 0.5; // Time to wait after receiving conformation of light on/off
waitfor_queue2empty();
LOG(ALLB_LOG, LOG_INFO, "Light Programming button press number %d - %s of %d\n", i, "ON", val);
send_cmd(code);
waitForButtonState(aq_data, button, ON, 2);
delay(dt * seconds);
waitfor_queue2empty();
LOG(ALLB_LOG, LOG_INFO, "Light Programming button press number %d - %s of %d\n", i, "OFF", val);
send_cmd(code);
waitForButtonState(aq_data, button, OFF, 2);
@ -623,7 +625,8 @@ void *set_allbutton_light_programmode( void *ptr )
}
LOG(ALLB_LOG, LOG_INFO, "Finished - Light Programming button press number %d - %s of %d\n", i, "ON", val);
send_cmd(code);
waitfor_queue2empty();
//waitfor_queue2empty();
longwaitfor_queue2empty();
}
//waitForButtonState(aq_data, &aq_data->aqbuttons[btn], ON, 2);

View File

@ -33,6 +33,11 @@ void programDeviceLightMode(struct aqualinkdata *aqdata, int value, int button);
void printPanelSupport(struct aqualinkdata *aqdata);
uint16_t setPanelSupport(struct aqualinkdata *aqdata);
void removePanelRSserialAdapterInterface();
void removePanelOneTouchInterface();
void removePanelIAQTouchInterface();
char *name2label(char *str)
{
int len = strlen(str);
@ -117,6 +122,40 @@ const char* find_rev_chars(const char *str, int length, int *out_len) {
return NULL; // Pattern not found
}
void checkPanelConfig(struct aqualinkdata *aqdata) {
// Check panel rev for common errors.
// Aqualink Touch.
if ( _aqconfig_.extended_device_id >= 0x30 && _aqconfig_.extended_device_id <= 0x33) {
if ( !isMASKSET(aqdata->panel_support_options, RSP_SUP_AQLT)) {
LOG(PANL_LOG, LOG_ERR, "Panel REV %s does not support AqualinkTouch protocol, please change configuration option '%s'\n",aqdata->panel_rev, CFG_N_extended_device_id);
LOG(PANL_LOG, LOG_WARNING, "Removing option '%s', please correct configuration\n",CFG_N_extended_device_id);
_aqconfig_.extended_device_id = 0x00;
removePanelIAQTouchInterface();
}
}
// One Touch
if ( _aqconfig_.extended_device_id >= 0x40 && _aqconfig_.extended_device_id <= 0x43) {
if ( !isMASKSET(aqdata->panel_support_options, RSP_SUP_ONET)) {
LOG(PANL_LOG, LOG_ERR, "Panel REV %s does not support OneTouch protocol, please change configuration option '%s'\n",aqdata->panel_rev, CFG_N_extended_device_id);
LOG(PANL_LOG, LOG_WARNING, "Removing option '%s', please correct configuration\n",CFG_N_extended_device_id);
_aqconfig_.extended_device_id = 0x00;
removePanelOneTouchInterface();
}
}
// Serial Adapter
if ( _aqconfig_.rssa_device_id >= 0x48 && _aqconfig_.rssa_device_id <= 0x49) {
if ( !isMASKSET(aqdata->panel_support_options, RSP_SUP_RSSA)) {
LOG(PANL_LOG, LOG_ERR, "Panel REV %s does not support RS SerialAdapter protocol, please change configuration option '%s'\n",aqdata->panel_rev, CFG_N_rssa_device_id);
LOG(PANL_LOG, LOG_WARNING, "Removing option '%s', please correct configuration\n",CFG_N_rssa_device_id);
_aqconfig_.rssa_device_id = 0x00;
removePanelRSserialAdapterInterface();
}
}
}
/*
pull board CPU, revision & panel string from strings like
@ -146,7 +185,7 @@ pull board CPU, revision & panel string from strings like
uint8_t setPanelInformationFromPanelMsg(struct aqualinkdata *aqdata, const char *input, uint8_t type, emulation_type source) {
const char *rev_pos = NULL;
uint8_t rtn = 0;
printf("Calculate panel from %s\n",input);
//const char *rev_pos = strstr(input, "REV"); // Find the position of "REV"
const char *sp;
int length = 0;
@ -164,6 +203,12 @@ uint8_t setPanelInformationFromPanelMsg(struct aqualinkdata *aqdata, const char
LOG(PANL_LOG, LOG_NOTICE, "Panel REV %s from %s\n",aqdata->panel_rev,getJandyDeviceName(source));
setPanelSupport(aqdata);
//printPanelSupport(aqdata);
if (source == SIM_NONE) {
// We pass SIM_NONE when we are in auto_config mode, so reset the panel name so we get it again when we fully start
aqdata->panel_rev[0] = '\0';
} else {
checkPanelConfig(aqdata);
}
} else {
//printf("Failed to find REV, length\n");
}
@ -397,6 +442,16 @@ void addPanelIAQTouchInterface() {
_aqconfig_.paneltype_mask &= ~RSP_ONET;
}
void removePanelRSserialAdapterInterface() {
_aqconfig_.paneltype_mask &= ~RSP_RSSA;
}
void removePanelOneTouchInterface() {
_aqconfig_.paneltype_mask &= ~RSP_ONET;
}
void removePanelIAQTouchInterface() {
_aqconfig_.paneltype_mask &= ~RSP_IAQT;
}
int PANEL_SIZE() {
if ((_aqconfig_.paneltype_mask & RSP_4) == RSP_4)
return 4;
@ -570,6 +625,8 @@ void setPanelByName(struct aqualinkdata *aqdata, const char *str)
rs = false;
if (str[2] == '-' || str[2] == ' ') // Account for PD-8
size = atoi(&str[3]);
if (str[3] == '-' && str[4] == 'P') // PDA-PS6 Combo
size = atoi(&str[6]);
else // Account for PDA-8
size = atoi(&str[4]);
} else {
@ -1308,12 +1365,15 @@ void programDeviceLightBrightness(struct aqualinkdata *aqdata, int value, int de
//void programDeviceLightMode(struct aqualinkdata *aqdata, int value, int button)
void programDeviceLightMode(struct aqualinkdata *aqdata, int value, int deviceIndex)
{
#ifdef AQ_PDA
if (isPDA_PANEL && !isPDA_IAQT) {
LOG(PANL_LOG,LOG_ERR, "Light mode control not supported in PDA mode\n");
return;
}
#endif
/*
int i;
clight_detail *light = NULL;
@ -1345,7 +1405,12 @@ void programDeviceLightMode(struct aqualinkdata *aqdata, int value, int deviceIn
} else if (isRSSA_ENABLED) {
// If we are using rs-serial then turn light on first.
if (light->button->led->state != ON) {
set_aqualink_rssadapter_aux_extended_state(light->button, RS_SA_ON);
set_aqualink_rssadapter_aux_state(light->button, TRUE);
//set_aqualink_rssadapter_aux_extended_state(light->button, RS_SA_ON);
//set_aqualink_rssadapter_aux_extended_state(light->button, 100);
// Add a few delays to slow it down. 0 is get status
//set_aqualink_rssadapter_aux_extended_state(light->button, 0);
//set_aqualink_rssadapter_aux_extended_state(light->button, 0);
}
if (light->lightType == LC_DIMMER) {
// Value 1 = 25, 2 = 50, 3 = 75, 4 = 100 (need to convert value into binary)
@ -1490,8 +1555,13 @@ void updateButtonLightProgram(struct aqualinkdata *aqdata, int value, int button
}
light->currentValue = value;
if (value > 0)
if (value > 0 && light->lastValue != value) {
light->lastValue = value;
if (_aqconfig_.save_light_programming_value && light->lightType == LC_PROGRAMABLE ) {
LOG(PANL_LOG,LOG_NOTICE, "Writing light programming value to config\n",button);
writeCfg(aqdata);
}
}
}
clight_detail *getProgramableLight(struct aqualinkdata *aqdata, int button)

View File

@ -107,6 +107,7 @@ const func_ptr _prog_functions[AQP_RSSADAPTER_MAX] = {
[AQ_PDA_SET_SPA_HEATER_TEMPS] = set_aqualink_PDA_spa_heater_temps,
[AQ_PDA_SET_FREEZE_PROTECT_TEMP] = set_aqualink_PDA_freeze_protectsetpoint,
[AQ_PDA_SET_TIME] = set_PDA_aqualink_time,
[AQ_PDA_SET_LIGHT_MODE] = set_aqualink_PDA_light_mode,
//[AQ_PDA_GET_POOL_SPA_HEATER_TEMPS]= get_aqualink_PDA_pool_spa_heater_temps,
[AQ_PDA_GET_FREEZE_PROTECT_TEMP] = get_PDA_aqualink_pool_spa_heater_temps
/*
@ -599,6 +600,9 @@ void _aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_dat
case AQ_SET_TIME:
type = AQ_PDA_SET_TIME;
break;
case AQ_SET_LIGHTCOLOR_MODE:
type = AQ_PDA_SET_LIGHT_MODE;
break;
#ifdef BETA_PDA_AUTOLABEL
case AQ_GET_AUX_LABELS:
type = AQ_PDA_AUX_LABELS:
@ -963,6 +967,9 @@ const char *ptypeName(program_type type)
case AQ_PDA_GET_FREEZE_PROTECT_TEMP:
return "Get PDA freeze protect";
break;
case AQ_PDA_SET_LIGHT_MODE:
return "Set PDA light mode";
break;
#endif
case AQP_NULL:
default:
@ -1059,6 +1066,9 @@ const char *programtypeDisplayName(program_type type)
case AQ_PDA_WAKE_INIT:
return "Programming: PDA wakeup";
break;
case AQ_PDA_SET_LIGHT_MODE:
return "Programming: setting light color";
break;
#endif
default:
return "Programming: please wait!";

View File

@ -78,6 +78,7 @@ typedef enum {
AQ_PDA_SET_TIME,
AQ_PDA_GET_POOL_SPA_HEATER_TEMPS,
AQ_PDA_GET_FREEZE_PROTECT_TEMP,
AQ_PDA_SET_LIGHT_MODE,
// ******** OneTouch Delimiter make sure to change MAX/MIN below
AQ_SET_ONETOUCH_PUMP_RPM,
AQ_SET_ONETOUCH_MACRO,

View File

@ -121,13 +121,16 @@ const char *getJandyDeviceName(emulation_type etype) {
case AQUAPDA:
return "PDA";
break;
case SIM_NONE:
return "AutoConfig";
break;
default:
return "Unknown";
break;
}
}
const char* get_pentair_packet_type(unsigned char* packet , int length)
const char* get_pentair_packet_type(const unsigned char* packet , int length)
{
static char buf[15];
@ -165,7 +168,7 @@ const char* get_pentair_packet_type(unsigned char* packet , int length)
break;
}
}
const char* get_jandy_packet_type(unsigned char* packet , int length)
const char* get_jandy_packet_type(const unsigned char* packet , int length)
{
static char buf[15];
@ -319,7 +322,7 @@ const char* get_jandy_packet_type(unsigned char* packet , int length)
}
}
const char* get_packet_type(unsigned char* packet , int length)
const char* get_packet_type(const unsigned char* packet , int length)
{
if (getProtocolType(packet)==PENTAIR) {
return get_pentair_packet_type(packet, length);
@ -409,7 +412,7 @@ void generate_pentair_checksum(unsigned char* packet, int length)
}
protocolType getProtocolType(unsigned char* packet) {
protocolType getProtocolType(const unsigned char* packet) {
if (packet[0] == DLE)
return JANDY;
else if (packet[0] == PP1)
@ -1030,6 +1033,114 @@ int get_packet_lograw(int fd, unsigned char* packet)
int _get_packet(int fd, unsigned char* packet, bool rawlog)
*/
#ifdef DUMMY_READER
int fix_packet(unsigned char *packet_buffer, int packet_length, bool getCached) {
static unsigned char saved_buffer[AQ_MAXPKTLEN+1];
static int saved_buffer_length = 0;
if (getCached) {
if (saved_buffer_length > 0) {
memcpy(packet_buffer, saved_buffer, saved_buffer_length);
LOG(RSSD_LOG,LOG_DEBUG, "2nd part of fixed frame\n");
logPacket(RSSD_LOG, LOG_DEBUG, packet_buffer, saved_buffer_length, true);
debuglogPacket(RSSD_LOG, packet_buffer, saved_buffer_length, true, true);
} else {
printf("NO 2nd PACKET %d\n",saved_buffer_length );
}
return saved_buffer_length;
}
char pbuf[256];
int rtn = 0;
memset(saved_buffer, 0, sizeof(saved_buffer));
saved_buffer_length = 0;
//LOG(RSSD_LOG,LOG_DEBUG, "Trying to fix bad packet\n");
LOG(RSSD_LOG,LOG_WARNING, "Serial read bad Jandy checksum, Trying to fix bad packet\n");
// Check end is valid
if ( packet_buffer[packet_length-2] == DLE && packet_buffer[packet_length-1] == ETX ) {
//LOG(RSSD_LOG,LOG_DEBUG, "Packet Fix: good end\n");
} else {
LOG(RSSD_LOG,LOG_DEBUG, "Packet Fix: bad end\n");
return 0;
}
// Specific fix for 2 frames where the first didn't end (but still holds the checksum)
// HEX: 0x10|0x02|0x00|0x0d|0x40|0x00|0x00|0x5f|0x10|0x02|0x84|0x00|0x96|0x10|0x03| <- no end 0x10|0x03, start 0x10|0x02 in middle
// HEX: 0x10|0x02|0x00|0x0d|0x40|0x00|0x00|0x5f|0x10|0x10|0x02|0x33|0x30|0x75|0x10|0x03| <- no 0x03 for end, start 0x10|0x02 in middle
// Run over the packet, see if their is a start in the middle.
// Ignore the first two bytes, assuming they are 0x10, 0x02
for (int i=2; i <= packet_length; i++ ) {
if (packet_buffer[i] == DLE && packet_buffer[i+1] == STX) {
LOG(RSSD_LOG,LOG_DEBUG, "Packet Fix: found start in middle of frame\n");
int p1_length = i;
int p2_length = packet_length-i;
unsigned char *p2_start = &packet_buffer[i];
bool validstart=FALSE;
bool validend=FALSE;
// Something we catch the end DLE as well as start DLE
while (packet_buffer[p1_length] == DLE) {
p1_length--;
}
beautifyPacket(pbuf, 256, packet_buffer, packet_length, TRUE);
LOG(RSSD_LOG,LOG_DEBUG, "Packet Fix: SPLITTING start to: %s\n",pbuf);
LOG(RSSD_LOG,LOG_DEBUG, "Packet Fix: Check 0x%02hhx (%d)\n",packet_buffer[p1_length], p1_length);
// see if we have a valid end
if (check_jandy_checksum(p2_start, p2_length) == true){
//LOG(RSSD_LOG,LOG_DEBUG, "Valid packet found at end\n");
beautifyPacket(pbuf, 256, p2_start, p2_length, TRUE);
LOG(RSSD_LOG,LOG_DEBUG, "Packet Fix: FIXED end to: %s\n",pbuf);
validend=TRUE;
}
if (check_jandy_checksum(packet_buffer, p1_length+3) == true){
//LOG(RSSD_LOG,LOG_DEBUG, "Valid packet found at start\n");
beautifyPacket(pbuf, 256, packet_buffer, p1_length, TRUE);
LOG(RSSD_LOG,LOG_DEBUG, "Packet Fix: FIXED start to: %s\n",pbuf);
validstart=TRUE;
}
if (validstart && validend) {
// Save the 2nd frame
memcpy(saved_buffer, p2_start, p2_length);
saved_buffer_length = p2_length;
// correct the 1st frame
packet_buffer[p1_length+1] = DLE;
packet_buffer[p1_length+2] = ETX;
rtn = p1_length+3;
} else if (validend) {
// Return the valid part of the packet.
memcpy(packet_buffer, p2_start, p2_length);
rtn = p2_length;
} else if (validstart) {
// correct the 1st frame
packet_buffer[p1_length+1] = DLE;
packet_buffer[p1_length+2] = ETX;
rtn = p1_length+3;
}
// Try the start.
//memcpy(new_buffer, packet_buffer, i);
break;
}
}
beautifyPacket(pbuf, 256, packet_buffer, rtn, TRUE);
LOG(RSSD_LOG,LOG_DEBUG, "Packet Fix: RETURNING: %s\n",pbuf);
return rtn;
}
#endif
int get_packet(int fd, unsigned char* packet)
{
@ -1050,6 +1161,17 @@ int get_packet(int fd, unsigned char* packet)
memset(packet, 0, AQ_MAXPKTLEN);
#ifdef DUMMY_READER
static bool haveFixedPacket = false;
if (haveFixedPacket) {
haveFixedPacket = false;
int rtn = fix_packet(packet, AQ_MAXPKTLEN, true);
if (rtn > 0) {
LOG(RSSD_LOG,LOG_DEBUG, "RETURNING PART 2 OF FIXED PACKET:\n");
return rtn;
}
}
#endif
// Read packet in byte order below
// DLE STX ........ ETX DLE
// sometimes we get ETX DLE and no start, so for now just ignoring that. Seem to be more applicable when busy RS485 traffic
@ -1198,11 +1320,19 @@ int get_packet(int fd, unsigned char* packet)
//LOG(RSSD_LOG,LOG_DEBUG, "Serial checksum, length %d got 0x%02hhx expected 0x%02hhx\n", index, packet[index-3], generate_checksum(packet, index));
if (jandyPacketStarted) {
if (check_jandy_checksum(packet, index) != true){
LOG(RSSD_LOG,LOG_WARNING, "Serial read bad Jandy checksum, ignoring\n");
logPacketError(packet, index);
//log_packet(LOG_WARNING, "Bad receive packet ", packet, index);
return AQSERR_CHKSUM;
if (check_jandy_checksum(packet, index) != true) {
#ifdef DUMMY_READER
int size = fix_packet(packet, index, false);
if (size > 0) {
haveFixedPacket = true;
index = size;
} else
#endif
{
LOG(RSSD_LOG,LOG_WARNING, "Serial read bad Jandy checksum, ignoring\n");
logPacketError(packet, index);
return AQSERR_CHKSUM;
}
}
} else if (pentairPacketStarted) {
if (check_pentair_checksum(packet, index) != true){

View File

@ -48,7 +48,7 @@ const char *getJandyDeviceName(emulation_type etype);
#define JANDY_DEC_PUMP_MAX 123 // 0x7b
// Have also seen epump at 0xe0 with panel rev W that supports more pumps
#define JANDY_DEC_PUMP2_MIN 224 // 0xe0
#define JANDY_DEC_PUMP2_MAX 228 // 0xe3 // Their are probably more, but this is a guess
#define JANDY_DEC_PUMP2_MAX 228 // 0xe3 // (should be 0xEF?????) Their are probably more, but this is a guess
#define JANDY_DEC_JXI_MIN 104 // 0x68
#define JANDY_DEC_JXI_MAX 107 // 0x6B
@ -65,6 +65,9 @@ const char *getJandyDeviceName(emulation_type etype);
#define JANDY_DEV_HPUMP_MIN 0x70
#define JANDY_DEV_HPUMP_MAX 0x73
#define JANDY_DEV_JLIGHT_MIN 0xF0
#define JANDY_DEV_JLIGHT_MAX 0xF4 // 0xF4 is total guess.
/*
//===== Device ID's =====//
//=========================================================================//
@ -524,7 +527,8 @@ typedef enum {
DRS_JXI,
DRS_LX,
DRS_CHEM,
DRS_HEATPUMP
DRS_HEATPUMP,
DRS_JLIGHT
} rsDeviceType;
typedef enum {
@ -567,7 +571,7 @@ bool serial_blockingmode();
//bool pda_mode();
//#endif
int generate_checksum(unsigned char* packet, int length);
protocolType getProtocolType(unsigned char* packet);
protocolType getProtocolType(const unsigned char* packet);
bool check_jandy_checksum(unsigned char* packet, int length);
bool check_pentair_checksum(unsigned char* packet, int length);
void send_ack(int file_descriptor, unsigned char command);
@ -582,7 +586,7 @@ int is_valid_port(int fd);
//void close_serial_port(int file_descriptor, struct termios* oldtio);
//void process_status(void const * const ptr);
void process_status(unsigned char* ptr);
const char* get_packet_type(unsigned char* packet , int length);
const char* get_packet_type(const unsigned char* packet , int length);
/*
void set_onetouch_enabled(bool mode);
bool onetouch_enabled();

View File

@ -83,12 +83,13 @@ bool isVirtualButtonEnabled();
#define PUMP_GPM_MAX 130
#define PUMP_GPM_MIN 15
enum {
/*
typedef enum temperatureUOM {
FAHRENHEIT,
CELSIUS,
UNKNOWN
};
} temperatureUOM;
*/
typedef struct aqualinkkey
{
@ -258,7 +259,7 @@ typedef enum clight_type {
LC_CLOGIG,
LC_INTELLIB,
LC_HAYWCL,
LC_SPARE_1,
LC_JANDYINFINATE, // was SPARE_1 (Infinate watercolors LED)
LC_SPARE_2,
LC_SPARE_3,
LC_DIMMER, // use 0, 25, 50, 100
@ -295,6 +296,7 @@ typedef struct clightd
{
clight_type lightType;
aqkey *button;
unsigned char lightID; // RS485 ID (only Jandy infinate watercolor)
int currentValue;
int lastValue; // Used for AqualinkD self programming
aqledstate RSSDstate; // state from rs serial adapter

View File

@ -740,7 +740,7 @@ void caculate_ack_packet(int rs_fd, unsigned char *packet_buffer, emulation_type
#define MAX_AUTO_PACKETS 1200
bool auto_configure(unsigned char* packet) {
bool auto_configure(unsigned char* packet, int rs_fd) {
// 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,
// set ID's and exit true, exit falce to get called again.
@ -755,9 +755,16 @@ bool auto_configure(unsigned char* packet) {
static unsigned char firstprobe = 0x00;
static unsigned char lastID = 0x00;
static bool seen_iAqualink2 = false;
static bool ignore_AqualinkTouch = false;
static int foundIDs = 0;
static int packetsReceived=0;
static bool gotRev = false;
static unsigned char gettingRevID = 0xFF;
static int loopsCompleted=0;
//static char message[AQ_MSGLONGLEN + 1];
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;
@ -766,6 +773,35 @@ bool auto_configure(unsigned char* packet) {
if ( packet[PKT_CMD] == CMD_PROBE ) {
LOG(AQUA_LOG,LOG_INFO, "Got Probe on ID 0x%02hhx\n",packet[PKT_DEST]);
//printf(" *** Got Probe on ID 0x%02hhx\n",packet[PKT_DEST]);
if ( packet[PKT_DEST] >= 0x08 && packet[PKT_DEST] <= 0x0B && gotRev == false && gettingRevID == 0xFF) {
// Try replying to get panel rev
gettingRevID = packet[PKT_DEST];
caculate_ack_packet(rs_fd, packet, ALLBUTTON);
return false; // We don't want to store this ID since we use it for getting REV and is won't go back to probe when AqualinkD starts
}
} else if (packet[PKT_DEST] == gettingRevID) {
if ( packet[PKT_CMD] == CMD_MSG ) {
if ( rsm_strnstr((char *)&packet[5], " REV", AQ_MSGLEN) != NULL ) {
// We need to get the rev to cater for panel rev I & k (maybe others) that send AqualinkTouch probe messages
// then they don't support that protocol.
LOG(AQUA_LOG,LOG_DEBUG, "Got %15s from ID 0x%02hhx\n",(char *)&packet[5],packet[PKT_DEST]);
gotRev = true;
gettingRevID = 0xFF;
setPanelInformationFromPanelMsg(&_aqualink_data, (char *)&packet[5], PANEL_CPU | PANEL_REV, SIM_NONE);
if ( !isMASKSET(_aqualink_data.panel_support_options, RSP_SUP_AQLT)) {
LOG(AQUA_LOG,LOG_NOTICE, "Ignoring AqualinkTouch probes due to panel rev\n");
ignore_AqualinkTouch = true;
if ( _aqconfig_.extended_device_id >= 0x30 && _aqconfig_.extended_device_id <= 0x33 ) {
_aqconfig_.extended_device_id = 0x00;
_aqconfig_.enable_iaqualink = false;
_aqconfig_.read_RS485_devmask &= ~ READ_RS485_IAQUALNK;
//firstprobe = 0x00;
foundIDs--;
}
}
}
}
caculate_ack_packet(rs_fd, packet, ALLBUTTON);
}
if (lastID != 0x00 && packet[PKT_DEST] == DEV_MASTER ) { // Can't use got a reply to the late probe.
@ -803,7 +839,7 @@ bool auto_configure(unsigned char* packet) {
_aqconfig_.extended_device_id_programming = true;
// Don't increase foundIDs as we prefer not to use this one.
LOG(AQUA_LOG,LOG_NOTICE, "Found valid unused extended ID 0x%02hhx\n",lastID);
} else if ( (lastID >= 0x30 && lastID <= 0x33) &&
} else if ( (lastID >= 0x30 && lastID <= 0x33) && ignore_AqualinkTouch == false &&
(_aqconfig_.extended_device_id < 0x30 || _aqconfig_.extended_device_id > 0x33)) { //Overide is it's been set to Touch or not set.
_aqconfig_.extended_device_id = lastID;
_aqconfig_.extended_device_id_programming = true;
@ -816,9 +852,17 @@ bool auto_configure(unsigned char* packet) {
}
// Now reset ID
lastID = 0x00;
return false;
}
if ( foundIDs >= 3 || (packet[PKT_DEST] == firstprobe && packet[PKT_CMD] == CMD_PROBE) ) {
if (packet[PKT_DEST] == firstprobe && packet[PKT_CMD] == CMD_PROBE) {
loopsCompleted++;
//LOG(AQUA_LOG,LOG_DEBUG, "***** Loop %d *****\n",loopsCompleted);
}
//if ( foundIDs >= 3 || (packet[PKT_DEST] == firstprobe && packet[PKT_CMD] == CMD_PROBE) ) {
if ( (foundIDs >= 3 && gotRev) || loopsCompleted == 2 ) {
// We should have seen one complete probe cycle my now.
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");
@ -1003,7 +1047,7 @@ void main_loop()
}
*/
if (is_valid_port(rs_fd)) {
LOG(AQUA_LOG,LOG_NOTICE, "Listening to Aqualink RS8 on serial port: %s\n", _aqconfig_.serial_port);
LOG(AQUA_LOG,LOG_NOTICE, "Listening to Aqualink %s on serial port: %s\n", getPanelString(), _aqconfig_.serial_port);
} else {
LOG(AQUA_LOG,LOG_ERR, "Error Aqualink bad serial port: %s\n", _aqconfig_.serial_port);
AddAQDstatusMask(ERROR_SERIAL);
@ -1072,6 +1116,7 @@ void main_loop()
stopPacketLogger();
close_serial_port(rs_fd);
stop_net_services();
stop_sensors_thread();
return;
}
/*
@ -1084,7 +1129,7 @@ void main_loop()
if (packet_length > 0 && auto_config_complete == false) {
blank_read = 0;
auto_config_complete = auto_configure(packet_buffer);
auto_config_complete = auto_configure(packet_buffer, rs_fd);
AddAQDstatusMask(AUTOCONFIGURE_ID);
_aqualink_data.updated = true;
if (auto_config_complete) {
@ -1171,6 +1216,7 @@ void main_loop()
stopPacketLogger();
close_serial_port(rs_fd);
stop_net_services();
stop_sensors_thread();
return;
}
}
@ -1216,6 +1262,12 @@ void main_loop()
_aqconfig_.extended_device_id_programming = false;
}
if ( _aqualink_data.num_sensors > 0){
start_sensors_thread(&_aqualink_data);
}
/*
*
* This is the main loop
@ -1232,7 +1284,7 @@ void main_loop()
blank_read_reconnect = blank_read_reconnect * 50;
#endif
int loopnum=0;
//int loopnum=0;
blank_read = 0;
// OK, Now go into infinate loop
while (_keepRunning == true)
@ -1441,6 +1493,7 @@ void main_loop()
}
}
/*
if ( _aqualink_data.num_sensors > 0 && ++loopnum >= 200 ) {
loopnum=0;
for (int i=0; i < _aqualink_data.num_sensors; i++) {
@ -1448,7 +1501,8 @@ void main_loop()
_aqualink_data.updated = true;
}
}
}
}
*/
//tcdrain(rs_fd); // Make sure buffer has been sent.
//delay(10);
@ -1459,6 +1513,7 @@ void main_loop()
if (! _restart) { // Stop network if we are not restarting
stop_net_services();
stop_sensors_thread();
}
// Reset and close the port.

View File

@ -52,7 +52,7 @@ char *_color_light_options[NUMBER_LIGHT_COLOR_TYPES][LIGHT_COLOR_OPTIONS] =
"Violet",
"Slow Splash",
"Fast Splash",
"USA",
"USA", // America the Beautiful <- Think this is Infinite Water Colors. Need to check what version that changed.
"Fat Tuesday",
"Disco Tech"
},
@ -116,7 +116,24 @@ char *_color_light_options[NUMBER_LIGHT_COLOR_TYPES][LIGHT_COLOR_OPTIONS] =
"Mardi Gras", // 0x50 (home panel) // 0x4b (simulator)
"Cool Cabaret" // 0x51 (home panel) // 0x4c
},
{/*Spare 1*/},
{// Jandy Infinate Water Colors (RS485)
"Off",
"Alpine White",
"Sky Blue",
"Cobalt Blue",
"Caribbean Blue",
"Spring Green",
"Emerald Green",
"Emerald Rose",
"Ruby Red", // Added over Jandy LED
"Magenta",
"Violet",
"Slow Splash",
"Fast Splash",
"America The Beautiful", // America the Beautiful <- Think this is Infinite Water Colors. Need to check what version that changed.
"Fat Tuesday",
"Disco Tech"
},
{/*Spare 2*/},
{/*Spare 3*/},
{ // Dimmer // From manual this is 0 for off, 128+<value%> so 153 = 25% = 0x99
@ -309,7 +326,7 @@ int build_color_lights_js(struct aqualinkdata *aqdata, char* buffer, int size)
length += sprintf(buffer+length, "var _light_program = [];\n");
if ( _color_light_options[0][1] == NULL || strcmp(_color_light_options[0][1], "1") == 0) {
length += sprintf(buffer+length, "_light_program[0] = light_program;\n");
length += sprintf(buffer+length, "_light_program[0] = [];\n");
i=1;
} else {
i=0;

View File

@ -23,6 +23,8 @@
#include <libgen.h>
//#include <locale.h>
#include <sys/ioctl.h>
//#include <sys/socket.h>
//#include <sys/time.h>
@ -34,6 +36,8 @@
#include <unistd.h>
#include <net/if.h>
#include <regex.h>
#include <limits.h>
#define CONFIG_C
#include "config.h"
@ -55,7 +59,9 @@
char *generate_mqtt_id(char *buf, int len);
pump_detail *getpump(struct aqualinkdata *aqdata, int button);
bool populatePumpData(struct aqualinkdata *aqdata, char *pumpcfg ,aqkey *button, char *value);
bool populateLightData(struct aqualinkdata *aqdata, char *lightcfg ,aqkey *button, char *value);
pump_detail *getPumpFromButtonID(struct aqualinkdata *aqdata, aqkey *button);
clight_detail *getLightFromButtonID(struct aqualinkdata *aqdata, aqkey *button);
aqkey *getVirtualButton(struct aqualinkdata *aqdata, int num);
struct aqualinkdata *_aqdata = NULL;
@ -162,6 +168,7 @@ const int _dcfg_light_programming_mode = 0;
const int _dcfg_light_programming_initial_on = 15;
const int _dcfg_light_programming_initial_off = 12;
const int _dcfg_sensor_poll_time = 300;
void init_parameters (struct aqconfig * parms)
{
@ -203,6 +210,7 @@ void init_parameters (struct aqconfig * parms)
//_cfgParams[_numCfgParams].advanced = true;
_cfgParams[_numCfgParams].config_mask |= CFG_GRP_ADVANCED;
_cfgParams[_numCfgParams].config_mask |= CFG_READONLY;
_cfgParams[_numCfgParams].config_mask |= CFG_FORCE_RESTART;
_cfgParams[_numCfgParams].default_value = NULL;
_numCfgParams++;
@ -210,6 +218,7 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].value_type = CFG_SPECIAL;
_cfgParams[_numCfgParams].name = CFG_N_panel_type;
_cfgParams[_numCfgParams].default_value = NULL;
_cfgParams[_numCfgParams].config_mask |= CFG_FORCE_RESTART;
_numCfgParams++;
_cfgParams[_numCfgParams].value_ptr = &_aqconfig_.device_id;
@ -217,6 +226,7 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].name = CFG_N_device_id;
_cfgParams[_numCfgParams].valid_values = CFG_V_device_id;
_cfgParams[_numCfgParams].default_value = (void *)&_dcfg_findIDHex;
_cfgParams[_numCfgParams].config_mask |= CFG_FORCE_RESTART;
_numCfgParams++;
_cfgParams[_numCfgParams].value_ptr = &_aqconfig_.rssa_device_id;
@ -262,12 +272,15 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].value_type = CFG_STRING;
_cfgParams[_numCfgParams].name = CFG_N_mqtt_server;
_cfgParams[_numCfgParams].default_value = (void *)_dcfg_null;
_cfgParams[_numCfgParams].config_mask |= CFG_ALLOW_BLANK;
_cfgParams[_numCfgParams].config_mask |= CFG_FORCE_RESTART;
_numCfgParams++;
_cfgParams[_numCfgParams].value_ptr = &_aqconfig_.mqtt_user;
_cfgParams[_numCfgParams].value_type = CFG_STRING;
_cfgParams[_numCfgParams].name = CFG_N_mqtt_user;
_cfgParams[_numCfgParams].default_value = (void *)NULL;
_cfgParams[_numCfgParams].config_mask |= CFG_ALLOW_BLANK;
_numCfgParams++;
_cfgParams[_numCfgParams].value_ptr = &_aqconfig_.mqtt_passwd;
@ -275,18 +288,21 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].name = CFG_N_mqtt_passwd;
_cfgParams[_numCfgParams].config_mask |= CFG_PASSWD_MASK;
_cfgParams[_numCfgParams].default_value = (void *)NULL;
_cfgParams[_numCfgParams].config_mask |= CFG_ALLOW_BLANK;
_numCfgParams++;
_cfgParams[_numCfgParams].value_ptr = &_aqconfig_.mqtt_aq_topic;
_cfgParams[_numCfgParams].value_type = CFG_STRING;
_cfgParams[_numCfgParams].name = CFG_N_mqtt_aq_topic;
_cfgParams[_numCfgParams].default_value = (void *)_dcfg_mqtt_aq_tp;
_cfgParams[_numCfgParams].config_mask |= CFG_ALLOW_BLANK;
_numCfgParams++;
_cfgParams[_numCfgParams].value_ptr = &_aqconfig_.mqtt_hass_discover_topic;
_cfgParams[_numCfgParams].value_type = CFG_STRING;
_cfgParams[_numCfgParams].name = CFG_N_mqtt_hass_discover_topic;
_cfgParams[_numCfgParams].default_value = (void *)_dcfg_mqtt_ha_discover;
_cfgParams[_numCfgParams].config_mask |= CFG_ALLOW_BLANK;
_numCfgParams++;
@ -313,6 +329,7 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].name = CFG_N_mqtt_dz_sub_topic;
//_cfgParams[_numCfgParams].advanced = true;
_cfgParams[_numCfgParams].config_mask |= CFG_GRP_ADVANCED;
_cfgParams[_numCfgParams].config_mask |= CFG_ALLOW_BLANK;
_cfgParams[_numCfgParams].default_value = NULL;
_numCfgParams++;
@ -321,6 +338,7 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].name = CFG_N_mqtt_dz_pub_topic;
//_cfgParams[_numCfgParams].advanced = true;
_cfgParams[_numCfgParams].config_mask |= CFG_GRP_ADVANCED;
_cfgParams[_numCfgParams].config_mask |= CFG_ALLOW_BLANK;
_cfgParams[_numCfgParams].default_value = NULL;
/*
_numCfgParams++;
@ -336,6 +354,7 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].name = CFG_N_dzidx_air_temp;
//_cfgParams[_numCfgParams].advanced = true;
_cfgParams[_numCfgParams].config_mask |= CFG_GRP_ADVANCED;
_cfgParams[_numCfgParams].config_mask |= CFG_ALLOW_BLANK;
_cfgParams[_numCfgParams].default_value = (void *)&unknownInt;
_numCfgParams++;
@ -344,6 +363,7 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].name = CFG_N_dzidx_pool_water_temp;
//_cfgParams[_numCfgParams].advanced = true;
_cfgParams[_numCfgParams].config_mask |= CFG_GRP_ADVANCED;
_cfgParams[_numCfgParams].config_mask |= CFG_ALLOW_BLANK;
_cfgParams[_numCfgParams].default_value = (void *)&unknownInt;
_numCfgParams++;
@ -352,6 +372,7 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].name = CFG_N_dzidx_spa_water_temp;
//_cfgParams[_numCfgParams].advanced = true;
_cfgParams[_numCfgParams].config_mask |= CFG_GRP_ADVANCED;
_cfgParams[_numCfgParams].config_mask |= CFG_ALLOW_BLANK;
_cfgParams[_numCfgParams].default_value = (void *)&unknownInt;
_numCfgParams++;
@ -360,6 +381,7 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].name = CFG_N_dzidx_swg_percent;
//_cfgParams[_numCfgParams].advanced = true;
_cfgParams[_numCfgParams].config_mask |= CFG_GRP_ADVANCED;
_cfgParams[_numCfgParams].config_mask |= CFG_ALLOW_BLANK;
_cfgParams[_numCfgParams].default_value = (void *)&unknownInt;
_numCfgParams++;
@ -368,6 +390,7 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].name = CFG_N_dzidx_swg_ppm;
//_cfgParams[_numCfgParams].advanced = true;
_cfgParams[_numCfgParams].config_mask |= CFG_GRP_ADVANCED;
_cfgParams[_numCfgParams].config_mask |= CFG_ALLOW_BLANK;
_cfgParams[_numCfgParams].default_value = (void *)&unknownInt;
_numCfgParams++;
@ -376,6 +399,7 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].name = CFG_N_dzidx_swg_status;
//_cfgParams[_numCfgParams].advanced = true;
_cfgParams[_numCfgParams].config_mask |= CFG_GRP_ADVANCED;
_cfgParams[_numCfgParams].config_mask |= CFG_ALLOW_BLANK;
_cfgParams[_numCfgParams].default_value = (void *)&unknownInt;
_numCfgParams++;
@ -384,6 +408,7 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].name = CFG_N_convert_dz_temp;
//_cfgParams[_numCfgParams].advanced = true;
_cfgParams[_numCfgParams].config_mask |= CFG_GRP_ADVANCED;
_cfgParams[_numCfgParams].config_mask |= CFG_ALLOW_BLANK;
_cfgParams[_numCfgParams].default_value = (void *)&unknownInt;
_numCfgParams++;
@ -513,6 +538,7 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].name = CFG_N_event_check_usecron;
_cfgParams[_numCfgParams].mask = AQS_USE_CRON_PUMP_TIME;
_cfgParams[_numCfgParams].default_value = (void *)&_dcfg_false;
_cfgParams[_numCfgParams].config_mask |= CFG_GRP_ADVANCED;
_numCfgParams++;
_cfgParams[_numCfgParams].value_ptr = &_aqconfig_.schedule_event_mask;
@ -520,6 +546,7 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].name = CFG_N_event_check_poweron;
_cfgParams[_numCfgParams].mask = AQS_POWER_ON;
_cfgParams[_numCfgParams].default_value = (void *)&_dcfg_false;
_cfgParams[_numCfgParams].config_mask |= CFG_GRP_ADVANCED;
_numCfgParams++;
_cfgParams[_numCfgParams].value_ptr = &_aqconfig_.schedule_event_mask;
@ -527,6 +554,7 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].name = CFG_N_event_check_freezeprotectoff;
_cfgParams[_numCfgParams].mask = AQS_FRZ_PROTECT_OFF;
_cfgParams[_numCfgParams].default_value = (void *)&_dcfg_false;
_cfgParams[_numCfgParams].config_mask |= CFG_GRP_ADVANCED;
_numCfgParams++;
_cfgParams[_numCfgParams].value_ptr = &_aqconfig_.schedule_event_mask;
@ -534,24 +562,28 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].name = CFG_N_event_check_boostoff;
_cfgParams[_numCfgParams].mask = AQS_BOOST_OFF;
_cfgParams[_numCfgParams].default_value = (void *)&_dcfg_false;
_cfgParams[_numCfgParams].config_mask |= CFG_GRP_ADVANCED;
_numCfgParams++;
_cfgParams[_numCfgParams].value_ptr = &_aqconfig_.sched_chk_pumpon_hour;
_cfgParams[_numCfgParams].value_type = CFG_INT;
_cfgParams[_numCfgParams].name = CFG_N_event_check_pumpon_hour;
_cfgParams[_numCfgParams].default_value = (void *)&_dcfg_zero;
_cfgParams[_numCfgParams].config_mask |= CFG_GRP_ADVANCED;
_numCfgParams++;
_cfgParams[_numCfgParams].value_ptr = &_aqconfig_.sched_chk_pumpoff_hour;
_cfgParams[_numCfgParams].value_type = CFG_INT;
_cfgParams[_numCfgParams].name = CFG_N_event_check_pumpoff_hour;
_cfgParams[_numCfgParams].default_value = (void *)&_dcfg_zero;
_cfgParams[_numCfgParams].config_mask |= CFG_GRP_ADVANCED;
_numCfgParams++;
_cfgParams[_numCfgParams].value_ptr = &_aqconfig_.sched_chk_booston_device;
_cfgParams[_numCfgParams].value_type = CFG_STRING;
_cfgParams[_numCfgParams].name = CFG_N_event_check_booston_device;
_cfgParams[_numCfgParams].default_value = NULL;
_cfgParams[_numCfgParams].config_mask |= CFG_GRP_ADVANCED;
_numCfgParams++;
_cfgParams[_numCfgParams].value_ptr = &_aqconfig_.ftdi_low_latency;
@ -610,6 +642,30 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].default_value = (void *)&_dcfg_true;
#endif
// Sensor poll time
_numCfgParams++;
_cfgParams[_numCfgParams].value_ptr = &_aqconfig_.sensor_poll_time;
_cfgParams[_numCfgParams].value_type = CFG_INT;
_cfgParams[_numCfgParams].name = "sensor_poll_time";
_cfgParams[_numCfgParams].default_value = (void *)&_dcfg_sensor_poll_time;
_cfgParams[_numCfgParams].config_mask |= CFG_GRP_ADVANCED;
// Optional values to store in config
_numCfgParams++;
_cfgParams[_numCfgParams].value_ptr = &_aqconfig_.save_debug_log_masks;
_cfgParams[_numCfgParams].value_type = CFG_BOOL;
_cfgParams[_numCfgParams].name = CFG_N_save_debug_log_masks;
_cfgParams[_numCfgParams].config_mask |= CFG_READONLY;
_cfgParams[_numCfgParams].default_value = (void *)&_dcfg_false;
_numCfgParams++;
_cfgParams[_numCfgParams].value_ptr = &_aqconfig_.save_light_programming_value;
_cfgParams[_numCfgParams].value_type = CFG_BOOL;
_cfgParams[_numCfgParams].name = CFG_N_save_light_programming_value;
_cfgParams[_numCfgParams].config_mask |= CFG_READONLY;
_cfgParams[_numCfgParams].default_value = (void *)&_dcfg_false;
//#endif
// Default to daemonize
@ -947,7 +1003,7 @@ bool setConfigValue(struct aqualinkdata *aqdata, char *param, char *value) {
}
if (strlen(cleanwhitespace(value)) <= 0) {
LOG(AQUA_LOG,LOG_NOTICE,"Set configuration option `%s` to default since value is blank\n",_cfgParams[i].name );
LOG(AQUA_LOG,LOG_INFO,"Set configuration option `%s` to default since value is blank\n",_cfgParams[i].name );
set_cfg_parm_to_default(&_cfgParams[i]);
return true;
}
@ -1125,9 +1181,8 @@ if (strlen(cleanwhitespace(value)) <= 0) {
char *name = cleanalloc(value);
int len = strlen(name);
if (len > 0) {
if ( strncmp(name+len-7, " - Show", 7) == 0 ) {
if ( strncasecmp(name+len-7, " - show", 7) == 0 ) {
name[len-7] = '\0';
//printf("Value '%s' index %d is show\n",name,num);
set_aqualinkd_light_mode_name(name,num,true);
} else {
set_aqualinkd_light_mode_name(name,num,false);
@ -1159,6 +1214,20 @@ if (strlen(cleanwhitespace(value)) <= 0) {
aqdata->aqbuttons[num].label = cleanalloc(value);
rtn=true;
#endif
/*
} else if (strncasecmp(param + 9, "_lightModeCacheValue", 20) == 0) {
if (isPLIGHT(aqdata->aqbuttons[num].special_mask)) {
if ( ((clight_detail *)aqdata->aqbuttons[num].special_mask_ptr)->lightType == LC_PROGRAMABLE ) {
int val = strtoul(value, NULL, 10);
((clight_detail *)aqdata->aqbuttons[num].special_mask_ptr)->lastValue = val;
printf("**** Set lastValue=%d for %s\n",val,aqdata->aqbuttons[num].label);
} else {
LOG(AQUA_LOG,LOG_ERR, "Config error, '%s' is invalied for light type '%s'\n",value,((clight_detail *)aqdata->aqbuttons[num].special_mask_ptr)->lightType);
}
} else {
LOG(AQUA_LOG,LOG_ERR, "Config error, Couldn't find light for '%s'\n",value);
}
rtn=true;
} else if (strncasecmp(param + 9, "_lightMode", 10) == 0) {
int type = strtoul(value, NULL, 10);
@ -1186,6 +1255,28 @@ if (strlen(cleanwhitespace(value)) <= 0) {
LOG(AQUA_LOG,LOG_ERR, "Config error, (colored|programmable) Lights limited to %d, ignoring %s'\n",MAX_LIGHTS,param);
}
rtn=true;
*/
} else if (strncasecmp(param + 9, "_light", 6) == 0) {
if ( ! populateLightData(aqdata, param + 10, &aqdata->aqbuttons[num], value) )
{
LOG(AQUA_LOG,LOG_ERR, "Config error, %s=%s Ignored!",param,value);
}
rtn=true;
/*
} else if (strncasecmp(param + 9, "_lightModeCacheValue", 20) == 0) {
int val = strtoul(value, NULL, 20);
if (isPLIGHT(aqdata->aqbuttons[num].special_mask)) {
if ( ((clight_detail *)aqdata->aqbuttons[num].special_mask_ptr)->lightType == LC_PROGRAMABLE ) {
((clight_detail *)aqdata->aqbuttons[num].special_mask_ptr)->lastValue = val;
} else {
LOG(AQUA_LOG,LOG_ERR, "Config error, '%s' is invalied for light type '%s'\n",value,((clight_detail *)aqdata->aqbuttons[num].special_mask_ptr)->lightType);
}
} else {
LOG(AQUA_LOG,LOG_ERR, "Config error, Couldn't find light for '%s'\n",value);
}*/
} else if (strncasecmp(param + 9, "_pump", 5) == 0) {
if ( ! populatePumpData(aqdata, param + 10, &aqdata->aqbuttons[num], value) )
@ -1197,7 +1288,7 @@ if (strlen(cleanwhitespace(value)) <= 0) {
}
//#if defined AQ_IAQTOUCH
} else if (strncasecmp(param, "virtual_button_", 15) == 0) {
rtn=true;
int num = strtoul(param + 15, NULL, 10);
if (_aqconfig_.paneltype_mask == 0) {
// ERROR the vbutton will be irnored.
@ -1212,6 +1303,7 @@ if (strlen(cleanwhitespace(value)) <= 0) {
} else {
LOG(AQUA_LOG,LOG_WARNING, "Error with '%s', total buttons=%d, config has %d already, ignoring!\n",param, TOTAL_BUTTONS, aqdata->total_buttons);
}
rtn=true;
} else if (strncasecmp(param + 17, "_altLabel", 9) == 0) {
char *label = cleanalloc(value);
aqkey *button = getVirtualButton(aqdata, num);
@ -1220,6 +1312,7 @@ if (strlen(cleanwhitespace(value)) <= 0) {
} else {
LOG(AQUA_LOG,LOG_WARNING, "Error with '%s', total buttons=%d, config has %d already, ignoring!\n",param, TOTAL_BUTTONS, aqdata->total_buttons);
}
rtn=true;
} else if (strncasecmp(param + 17, "_pump", 5) == 0) {
aqkey *vbutton = getVirtualButton(aqdata, num);
if (vbutton != NULL) {
@ -1231,6 +1324,18 @@ if (strlen(cleanwhitespace(value)) <= 0) {
} else {
LOG(AQUA_LOG,LOG_ERR, "Config error, could not find vitrual button for `%s`",param);
}
rtn=true;
} else if (strncasecmp(param + 17, "_light", 6) == 0) {
aqkey *vbutton = getVirtualButton(aqdata, num);
if (vbutton != NULL) {
if ( ! populateLightData(aqdata, param + 18, vbutton, value) )
{
LOG(AQUA_LOG,LOG_ERR, "Config error, %s=%s Ignored!",param,value);
}
} else {
LOG(AQUA_LOG,LOG_ERR, "Config error, could not find vitrual button for `%s`",param);
}
rtn=true;
} else if (strncasecmp(param + 17, "_onetouchID", 11) == 0) {
aqkey *vbutton = getVirtualButton(aqdata, num);
if (vbutton != NULL) {
@ -1268,6 +1373,7 @@ if (strlen(cleanwhitespace(value)) <= 0) {
} else {
LOG(AQUA_LOG,LOG_ERR, "Config error, could not find vitrual button for `%s`",param);
}
rtn=true;
}
} else if (strncasecmp(param, "sensor_", 7) == 0) {
int num = strtoul(param + 7, NULL, 10) - 1;
@ -1277,6 +1383,7 @@ if (strlen(cleanwhitespace(value)) <= 0) {
if ( num + 1 > aqdata->num_sensors ) {
aqdata->num_sensors = num + 1;
}
sprintf(aqdata->sensors[num].ID, "Aux_S%d", num+1);
if (strncasecmp(param + 9, "_label", 6) == 0) {
aqdata->sensors[num].label = ncleanalloc(value, AQ_MSGLEN);
rtn=true;
@ -1290,8 +1397,14 @@ if (strlen(cleanwhitespace(value)) <= 0) {
LOG(AQUA_LOG,LOG_ERR, "Config error, couldn't understand `%s` from `%s`, using `1.0`!",value,param);
aqdata->sensors[num].factor = 1;
}
rtn=true;
} else if (strncasecmp(param + 9, "_regex", 5) == 0) {
aqdata->sensors[num].regex = cleanalloc(value);
rtn=true;
} else if (strncasecmp(param + 9, "_uom", 3) == 0) {
aqdata->sensors[num].uom = cleanalloc(value);
rtn=true;
}
rtn=true;
} else {
LOG(AQUA_LOG,LOG_ERR, "Config error, blank value for `%s`\n",param);
rtn = false;
@ -1367,11 +1480,46 @@ bool populatePumpData(struct aqualinkdata *aqdata, char *pumpcfg ,aqkey *button,
pump->maxSpeed = strtoul(value, NULL, 10);
} else if (strncasecmp(pumpcfg, "pumpMinSpeed", 12) == 0) {
pump->minSpeed = strtoul(value, NULL, 10);
} else {
return false;
}
return true;
}
// lightcfg is pointer to lightMode, lightID, lightModeCacheValue (ie pull off button_??_ or vurtual_button_??_)
bool populateLightData(struct aqualinkdata *aqdata, char *lightcfg ,aqkey *button, char *value)
{
clight_detail *light = getLightFromButtonID(aqdata, button);
if (light == NULL) {
return false;
}
if (strncasecmp(lightcfg, "lightModeCacheValue", 19) == 0) {
light->lastValue = strtoul(value, NULL, 10);
return true;
} else if (strncasecmp(lightcfg, "lightMode", 9) == 0) {
light->lightType = strtoul(value, NULL, 10);
if (light->lightType < LC_PROGRAMABLE || light->lightType >= NUMBER_LIGHT_COLOR_TYPES) {
LOG(AQUA_LOG,LOG_ERR, "Config error, unknown light mode '%d'\n",light->lightType);
}
if ( light->lightType == LC_DIMMER2 && _aqconfig_.rssa_device_id != 0x48 ) {
LOG(AQUA_LOG,LOG_ERR, "Config error, button '%s' has light mode '%d' set. This only supported when 'rssa_device_id' is enabled, changing to light mode '%d'\n",
button->label, LC_DIMMER2,LC_DIMMER);
light->lightType = LC_DIMMER;
}
return true;
} else if (strncasecmp(lightcfg, "lightID", 7) == 0) {
light->lightID = strtoul(cleanwhitespace(value), NULL, 16);
return true;
} else {
return false;
}
return false;
}
pump_detail *getPumpFromButtonID(struct aqualinkdata *aqdata, aqkey *button)
{
int pi;
@ -1406,6 +1554,29 @@ pump_detail *getPumpFromButtonID(struct aqualinkdata *aqdata, aqkey *button)
return NULL;
}
clight_detail *getLightFromButtonID(struct aqualinkdata *aqdata, aqkey *button)
{
int li;
// Does it exist
for (li=0; li < aqdata->num_lights; li++) {
if (aqdata->lights[li].button == button) {
return &aqdata->lights[li];
}
}
// Create new entry
if (aqdata->num_lights < MAX_LIGHTS) {
button->special_mask |= PROGRAM_LIGHT;
button->special_mask_ptr = (void*)&aqdata->lights[aqdata->num_lights];
aqdata->lights[aqdata->num_lights].button = button;
aqdata->num_lights++;
return &aqdata->lights[aqdata->num_lights-1];
}
return NULL;
}
/*
pump_detail *getpump(struct aqualinkdata *aqdata, int button)
{
@ -1447,6 +1618,8 @@ void init_config()
init_parameters(&_aqconfig_);
}
//void readCfg (struct aqconfig *config_parameters, struct aqualinkdata *aqdata, char *cfgFile)
void read_config (struct aqualinkdata *aqdata, char *cfgFile)
{
@ -1459,6 +1632,9 @@ void read_config (struct aqualinkdata *aqdata, char *cfgFile)
//int tokenindex = 0;
char *b_ptr;
//setlocale(LC_ALL, "en_US.UTF-8");
_aqconfig_.config_file = cleanalloc(cfgFile);
if( (fp = fopen(cfgFile, "r")) != NULL){
@ -1539,7 +1715,7 @@ void check_print_config (struct aqualinkdata *aqdata)
// Sanity checks
// If no panel has been set, use default one
if (_aqconfig_.paneltype_mask == 0) {
//printf("Set temp panel info Size=%d, RS=%d, combo=%d, dual=%d\n",_defaultPanel.size,_defaultPanel.rs,_defaultPanel.combo, _defaultPanel.dual);
@ -1549,6 +1725,9 @@ void check_print_config (struct aqualinkdata *aqdata)
// Make sure all sensors are fully populated
for (i=0; i < aqdata->num_sensors; i++ ) {
//if (aqdata->sensors[i].uom == NULL) {
// aqdata->sensors[i].uom = cleanalloc("°C");
//}
if ( aqdata->sensors[i].label == NULL || aqdata->sensors[i].path == NULL ) {
LOG(AQUA_LOG,LOG_ERR, "Invalid sensor %d, removing!\n",i+1);
if (i == (aqdata->num_sensors-1) ) { // last sensor
@ -1558,6 +1737,8 @@ void check_print_config (struct aqualinkdata *aqdata)
aqdata->sensors[j].label = aqdata->sensors[j+1].label;
aqdata->sensors[j].path = aqdata->sensors[j+1].path;
aqdata->sensors[j].factor = aqdata->sensors[j+1].factor;
aqdata->sensors[j].regex = aqdata->sensors[j+1].regex;
aqdata->sensors[j].uom = aqdata->sensors[j+1].uom;
}
}
aqdata->num_sensors --;
@ -1655,6 +1836,26 @@ void check_print_config (struct aqualinkdata *aqdata)
}
/*
PDA Mode
No light mode 11
(chiller????? )
<probably ton of other stuff>
*/
if (_aqconfig_.device_id == 0x60) {
// Check lights.
for (i = 0; i < aqdata->total_buttons; i++)
{
if (isPLIGHT(aqdata->aqbuttons[i].special_mask)) {
if ( ((clight_detail *)aqdata->aqbuttons[i].special_mask_ptr)->lightType == LC_DIMMER2 ) {
LOG(AQUA_LOG,LOG_WARNING, "Config error, PDA does not support lightmode %d setting to %d\n",LC_DIMMER2,LC_DIMMER);
((clight_detail *)aqdata->aqbuttons[i].special_mask_ptr)->lightType = LC_DIMMER;
}
}
}
}
/*
PDA sleep and PDA ID.
*/
@ -1680,6 +1881,28 @@ void check_print_config (struct aqualinkdata *aqdata)
for ( i=0; i <= _numCfgParams; i++) {
// Reconfigure some CFG values depending on PDA
if ( strcmp(_cfgParams[i].name, CFG_N_device_id) == 0 ) {
if (isPDA_PANEL) {
_cfgParams[i].valid_values = CFG_V_device_id_PDA;
} else {
_cfgParams[i].valid_values = CFG_V_device_id_RS;
}
}
if ( strcmp(_cfgParams[i].name, CFG_N_rssa_device_id) == 0 && isPDA_PANEL) {
_cfgParams[i].config_mask |= CFG_GREYED_OUT;
}
if ( strcmp(_cfgParams[i].name, CFG_N_extended_device_id) == 0 && isPDA_PANEL) {
_cfgParams[i].config_mask |= CFG_GREYED_OUT;
}
// Don't show PDA stuff on RS panel
if ( strcmp(_cfgParams[i].name, CFG_N_pda_sleep_mode) == 0 && !isPDA_PANEL) {
_cfgParams[i].config_mask |= CFG_GREYED_OUT;
_cfgParams[i].config_mask |= CFG_READONLY;
}
rsm_nchar_replace(name, MAX_PRINTLEN, _cfgParams[i].name, "_", " ");
switch (_cfgParams[i].value_type) {
case CFG_STRING:
@ -1775,14 +1998,21 @@ void check_print_config (struct aqualinkdata *aqdata)
if ( ((aqdata->aqbuttons[i].special_mask & VIRTUAL_BUTTON) == VIRTUAL_BUTTON) &&
((aqdata->aqbuttons[i].special_mask & VS_PUMP ) != VS_PUMP) &&
(_aqconfig_.extended_device_id < 0x30 || _aqconfig_.extended_device_id > 0x33 ) ){
LOG(AQUA_LOG,LOG_WARNING, "Config error, extended_device_id must be on of the folowing (0x30,0x31,0x32,0x33) to use virtual button : '%s'",aqdata->aqbuttons[i].label);
LOG(AQUA_LOG,LOG_WARNING, "Config error, extended_device_id must be one of the folowing (0x30,0x31,0x32,0x33) to use virtual button : '%s'",aqdata->aqbuttons[i].label);
}
}
for (i = 0; i < aqdata->num_sensors; i++)
{
LOG(AQUA_LOG,LOG_NOTICE, "Config Sensor %02d = label %-15s | %s\n", i+1, aqdata->sensors[i].label,aqdata->sensors[i].path);
//LOG(AQUA_LOG,LOG_NOTICE, "Sensor %-13s = label %-15s | %s\n", aqdata->sensors[i].ID, aqdata->sensors[i].label,aqdata->sensors[i].path);
LOG(AQUA_LOG,LOG_NOTICE, "Sensor %-13s = label %-15s | %s | %s%s %s\n",
aqdata->sensors[i].ID,
aqdata->sensors[i].label,
aqdata->sensors[i].path,
(aqdata->sensors[i].uom==NULL?"":aqdata->sensors[i].uom),
(aqdata->sensors[i].uom==NULL?"":" |"),
(aqdata->sensors[i].regex==NULL?"":aqdata->sensors[i].regex));
}
@ -1842,7 +2072,7 @@ int save_config_js(const char* inBuf, int inSize, char* outBuf, int outSize, str
regmatch_t groupArray[maxGroups];
regex_t regexCompiled;
int rc;
char * cursor = (char *)inBuf;;
char * cursor = (char *)inBuf;
unsigned int m;
char key[64];
char value[64];
@ -1878,6 +2108,11 @@ int save_config_js(const char* inBuf, int inSize, char* outBuf, int outSize, str
free(aqdata->sensors[i].path);
aqdata->sensors[i].label = NULL;
aqdata->sensors[i].path = NULL;
// NSF When fixed the JSON & config editor, put these lines back.
//free(aqdata->sensors[i].regex);
//aqdata->sensors[i].regex = NULL;
free(aqdata->sensors[i].uom);
aqdata->sensors[i].uom = NULL;
}
aqdata->num_sensors=0;
@ -1918,6 +2153,8 @@ int save_config_js(const char* inBuf, int inSize, char* outBuf, int outSize, str
snprintf(value, 64, "%.*s", (groupArray[2].rm_eo - groupArray[2].rm_so), (cursor + groupArray[2].rm_so));
//printf("**** Pair = %s : %s \n",key,value);
LOG(AQUA_LOG,LOG_DEBUG, "Read json cfg Pair = %s : %s \n",key,value);
// If panel size changed, see if we should ignore the label
if (strncasecmp(key, "button_", 7 ) == 0) {
if ( strtoul(key + 7, NULL, 10) >= ignodeBtnLabelsGrater) {
@ -2015,6 +2252,18 @@ bool writeCfg (struct aqualinkdata *aqdata)
//char fp[100];
// Testing shit
if (_aqconfig_.save_debug_log_masks) {
for (int i = 0; i < (sizeof(logmask_t) * CHAR_BIT); i++) {
if(isDebugLogMaskSet((1 << i))) {
fprintf(fp, "debug_log_mask=%d\n", (1 << i));
}
}
fprintf(fp,"\n");
}
// Loop over config parameters.
for ( i=0; i <= _numCfgParams; i++) {
if (isMASK_SET(_cfgParams[i].config_mask, CFG_HIDE) ) {
continue;
@ -2075,6 +2324,17 @@ bool writeCfg (struct aqualinkdata *aqdata)
fprintf(fp,"\nsensor_%.2d_path=%s\n",i,aqdata->sensors[i-1].path);
fprintf(fp,"sensor_%.2d_label=%s\n",i,aqdata->sensors[i-1].label);
fprintf(fp,"sensor_%.2d_factor=%f\n",i,aqdata->sensors[i-1].factor);
if (aqdata->sensors[i-1].regex != NULL) {
fprintf(fp,"sensor_%.2d_regex=%s\n",i,aqdata->sensors[i-1].regex);
}
if (aqdata->sensors[i-1].uom != NULL) {
fprintf(fp,"sensor_%.2d_uom=%s\n",i,aqdata->sensors[i-1].uom);
}
/*
if (aqdata->sensors[i-1].regex != NULL) {
fprintf(fp,"sensor_%.2d_regex=%f\n",i,aqdata->sensors[i-1].regex);
}
*/
}
fprintf(fp,"\n");
@ -2137,6 +2397,9 @@ bool writeCfg (struct aqualinkdata *aqdata)
} else if (isPLIGHT(aqdata->aqbuttons[i].special_mask)) {
//if (((clight_detail *)aqdata->aqbuttons[i].special_mask_ptr)->lightType > 0) {
fprintf(fp,"%s_lightMode=%d\n", prefix, ((clight_detail *)aqdata->aqbuttons[i].special_mask_ptr)->lightType);
if (_aqconfig_.save_light_programming_value && ((clight_detail *)aqdata->aqbuttons[i].special_mask_ptr)->lightType == LC_PROGRAMABLE ) {
fprintf(fp,"%s_lightModeCacheValue=%d\n", prefix, ((clight_detail *)aqdata->aqbuttons[i].special_mask_ptr)->lastValue);
}
//}
} else if ( (isVBUTTON(aqdata->aqbuttons[i].special_mask) && aqdata->aqbuttons[i].rssd_code >= IAQ_ONETOUCH_1 && aqdata->aqbuttons[i].rssd_code <= IAQ_ONETOUCH_6 ) ) {
fprintf(fp,"%s_onetouchID=%d\n", prefix, (aqdata->aqbuttons[i].rssd_code - 15));

View File

@ -35,6 +35,7 @@
#define READ_RS485_JAN_CHEM (1 << 5) // Jandy Chemical Feeder
#define READ_RS485_IAQUALNK (1 << 6) // Read iAqualink messages
#define READ_RS485_HEATPUMP (1 << 7) // Read HeatPump messages
#define READ_RS485_JLIGHT (1 << 8) // Read Jandy infinite watercolor light
#define MAX_RSSD_LOG_FILTERS 4
@ -107,6 +108,9 @@ struct aqconfig
bool ftdi_low_latency;
int frame_delay;
bool device_pre_state;
bool save_debug_log_masks;
bool save_light_programming_value;
int sensor_poll_time;
#ifdef AQ_NO_THREAD_NETSERVICE
int rs_poll_speed; // Need to remove
bool thread_netservices; // Need to remove
@ -128,6 +132,7 @@ struct aqconfig _aqconfig_;
#define READ_RSDEV_CHEM ((_aqconfig_.read_RS485_devmask & READ_RS485_JAN_CHEM) == READ_RS485_JAN_CHEM)
#define READ_RSDEV_iAQLNK ((_aqconfig_.read_RS485_devmask & READ_RS485_IAQUALNK) == READ_RS485_IAQUALNK)
#define READ_RSDEV_HPUMP ((_aqconfig_.read_RS485_devmask & READ_RS485_HEATPUMP) == READ_RS485_HEATPUMP)
#define READ_RSDEV_JLIGHT ((_aqconfig_.read_RS485_devmask & READ_RS485_JLIGHT) == READ_RS485_JLIGHT)
#define isPDA_IAQT (_aqconfig_.device_id == 0x33)
//#define isPDA ((_aqconfig_.paneltype_mask & RSP_PDA) == RSP_PDA)
@ -192,6 +197,8 @@ typedef enum cfg_value_type{
//#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_ALLOW_BLANK (1 << 6) // Allow blank entry
#define CFG_GREYED_OUT (1 << 7) // Greyout in UI, show but not editable
//#define CFG_ (1 << 3)
// Text to show when CFG_PASSWD_MASK is set
@ -222,152 +229,98 @@ int _numCfgParams;
// Below are missed
//RSSD_LOG_filter
//debug_log_mask
#define CFG_V_BOOL "[\"Yes\", \"No\"]"
#define CFG_N_serial_port "serial_port"
#define CFG_C_serial_port 11
#define CFG_N_log_level "log_level"
#define CFG_V_log_level "[\"DEBUG\", \"INFO\", \"NOTICE\", \"WARNING\", \"ERROR\"]"
#define CFG_C_log_level 9
#define CFG_N_socket_port "socket_port" // Change to Web_socket
#define CFG_C_socket_port 11
#define CFG_V_log_level "[\"DEBUG_SERIAL\", \"DEBUG\", \"INFO\", \"NOTICE\", \"WARNING\", \"ERROR\"]"
#define CFG_N_socket_port "socket_port"
#define CFG_N_web_directory "web_directory"
#define CFG_C_web_directory 13
#define CFG_N_device_id "device_id"
#define CFG_V_device_id "[\"0x0a\", \"0x0b\", \"0x09\", \"0x08\", \"0x60\", \"0xFF\"]"
#define CFG_C_device_id 9
#define CFG_V_device_id "[\"0x0a\", \"0x0b\", \"0x09\", \"0x08\", \"0x60\", \"0x33\", \"0xFF\"]"
#define CFG_V_device_id_RS "[\"0x0a\", \"0x0b\", \"0x09\", \"0x08\", \"0xFF\"]"
#define CFG_V_device_id_PDA "[\"0x60\", \"0x33\", \"0xFF\"]"
#define CFG_N_rssa_device_id "rssa_device_id"
#define CFG_V_rssa_device_id "[\"0x00\", \"0x48\"]"
#define CFG_C_rssa_device_id 14
#define CFG_N_RSSD_LOG_filter "RSSD_LOG_filter"
#define CFG_C_RSSD_LOG_filter 15
#define CFG_N_panel_type "panel_type"
#define CFG_C_panel_type 10
#define CFG_N_extended_device_id "extended_device_id"
#define CFG_V_extended_device_id "[\"0x00\", \"0x30\", \"0x31\", \"0x32\", \"0x33\", \"0x40\", \"0x41\", \"0x42\", \"0x43\"]"
#define CFG_C_extended_device_id 18
#define CFG_N_sync_panel_time "sync_panel_time"
#define CFG_C_sync_panel_time 15
//#define CFG_N_extended_device_id2 "extended_device_id2"
//#define CFG_C_extended_device_id2 20
#define CFG_N_extended_device_id_programming "extended_device_id_programming"
#define CFG_C_extended_device_id_programming 30
#define CFG_N_enable_iaqualink "enable_iaqualink"
#define CFG_C_enable_iaqualink 16
#define CFG_N_log_file "log_file"
#define CFG_C_log_file 8
#define CFG_N_mqtt_aq_topic "mqtt_aq_topic"
#define CFG_C_mqtt_aq_topic 13
#define CFG_N_mqtt_server "mqtt_address"
#define CFG_C_mqtt_server 12
#define CFG_N_mqtt_user "mqtt_user"
#define CFG_C_mqtt_user 9
#define CFG_N_mqtt_passwd "mqtt_passwd"
#define CFG_C_mqtt_passwd 11
#define CFG_N_mqtt_hass_discover_topic "mqtt_ha_discover_topic"
#define CFG_C_mqtt_hass_discover_topic 24
#define CFG_N_mqtt_hass_discover_use_mac "mqtt_ha_discover_use_mac"
#define CFG_C_mqtt_hass_discover_use_mac 27
#define CFG_N_mqtt_timed_update "mqtt_timed_update"
#define CFG_C_mqtt_timed_update 17
//#define CFG_N_mqtt_ID "mqtt_ID"
//#define CFG_C_mqtt_ID 7
#define CFG_N_mqtt_dz_sub_topic "mqtt_dz_sub_topic"
#define CFG_C_mqtt_dz_sub_topic 17
#define CFG_N_mqtt_dz_pub_topic "mqtt_dz_pub_topic"
#define CFG_C_mqtt_dz_pub_topic 17
#define CFG_N_dzidx_air_temp "dzidx_air_temp"
#define CFG_C_dzidx_air_temp 14
#define CFG_N_dzidx_pool_water_temp "dzidx_pool_water_temp"
#define CFG_C_dzidx_pool_water_temp 21
#define CFG_N_dzidx_spa_water_temp "dzidx_spa_water_temp"
#define CFG_C_dzidx_spa_water_temp 20
#define CFG_N_dzidx_swg_percent "dzidx_SWG_percent"
#define CFG_C_dzidx_swg_percent 17
#define CFG_N_dzidx_swg_ppm "dzidx_SWG_PPM"
#define CFG_C_dzidx_swg_ppm 13
#define CFG_N_dzidx_swg_status "dzidx_SWG_Status"
#define CFG_C_dzidx_swg_status 16
#define CFG_N_light_programming_mode "light_programming_mode"
#define CFG_C_light_programming_mode 22
#define CFG_N_light_programming_initial_on "light_programming_initial_on"
#define CFG_C_light_programming_initial_on 28
#define CFG_N_light_programming_initial_off "light_programming_initial_off"
#define CFG_C_light_programming_initial_off 29
#define CFG_N_override_freeze_protect "override_freeze_protect"
#define CFG_C_override_freeze_protect 23
#define CFG_N_pda_sleep_mode "pda_sleep_mode"
#define CFG_C_pda_sleep_mode 14
#define CFG_N_convert_mqtt_temp "mqtt_convert_temp_to_c"
#define CFG_C_convert_mqtt_temp 22
#define CFG_N_convert_dz_temp "dz_convert_temp_to_c"
#define CFG_C_convert_dz_temp 20
#define CFG_N_report_zero_spa_temp "report_zero_spa_temp"
#define CFG_C_report_zero_spa_temp 20
#define CFG_N_report_zero_pool_temp "report_zero_pool_temp"
#define CFG_C_report_zero_pool_temp 21
#define CFG_N_read_RS485_devmask "read_RS485_devmask"
#define CFG_C_read_RS485_devmask 18
#define CFG_N_use_panel_aux_labels "use_panel_aux_labels"
#define CFG_C_use_panel_aux_labels 20
#define CFG_N_force_swg "force_swg"
#define CFG_C_force_swg 9
#define CFG_N_force_ps_setpoints "force_ps_setpoints"
#define CFG_C_force_ps_setpoints 18
#define CFG_N_force_frzprotect_setpoints "force_frzprotect_setpoints"
#define CFG_C_force_frzprotect_setpoints 26
#define CFG_N_force_chem_feeder "force_chem_feeder"
#define CFG_C_force_chem_feeder 17
#define CFG_N_force_chiller "force_chiller"
#define CFG_N_display_warnings_web "display_warnings_web"
#define CFG_C_display_warnings_web 20
#define CFG_N_log_protocol_packets "log_protocol_packets"
#define CFG_C_log_protocol_packets 20
#define CFG_N_device_pre_state "device_pre_state"
#define CFG_C_device_pre_state 16
#define CFG_N_read_RS485_swg "read_RS485_swg"
#define CFG_C_read_RS485_swg 14
#define CFG_N_read_RS485_ePump "read_RS485_ePump"
#define CFG_C_read_RS485_ePump 16
#define CFG_N_read_RS485_vsfPump "read_RS485_vsfPump"
#define CFG_C_read_RS485_vsfPump 18
#define CFG_N_read_RS485_JXi "read_RS485_JXi"
#define CFG_C_read_RS485_JXi 14
#define CFG_N_read_RS485_LX "read_RS485_LX"
#define CFG_C_read_RS485_LX 13
#define CFG_N_read_RS485_Chem "read_RS485_Chem"
#define CFG_C_read_RS485_Chem 15
#define CFG_N_read_RS485_iAqualink "read_RS485_iAqualink"
#define CFG_C_read_RS485_iAqualink 20
#define CFG_N_read_RS485_HeatPump "read_RS485_HeatPump"
#define CFG_N_enable_scheduler "enable_scheduler"
#define CFG_C_enable_scheduler 16
#define CFG_N_event_check_poweron "event_poweron_check_pump"
#define CFG_C_event_check_poweron 24
#define CFG_N_event_check_freezeprotectoff "event_freezeprotectoff_check_pump"
#define CFG_C_event_check_freezeprotectoff 33
#define CFG_N_event_check_boostoff "event_boostoff_check_pump"
#define CFG_C_event_check_boostoff 25
#define CFG_N_event_check_pumpon_hour "event_check_pumpon_hour"
#define CFG_C_event_check_pumpon_hour 23
#define CFG_N_event_check_pumpoff_hour "event_check_pumpoff_hour"
#define CFG_C_event_check_pumpoff_hour 24
#define CFG_N_event_check_usecron "event_check_use_scheduler_times"
#define CFG_C_event_check_usecron 32
#define CFG_N_event_check_booston_device "event_booston_check_device"
#define CFG_N_ftdi_low_latency "ftdi_low_latency"
#define CFG_C_ftdi_low_latency 16
#define CFG_N_rs485_frame_delay "rs485_frame_delay"
#define CFG_C_rs485_frame_delay 17
#endif
#define CFG_N_save_debug_log_masks "save_debug_log_masks"
#define CFG_N_save_light_programming_value "save_light_programming_value"
//#define CFG_V_UOM "[\"°C\", \"°F\", \"K\", \"Hz\", \"GHz\", \"Pa\", \"0x41\", \"hPa\", \"bar\", \"mbar\", \"inHg\", \"psi\", \"L\", \"mL\", \"m³\", \"ft³\", \"fl. oz.\", \"m³/h\", \"ft³/m\"]"
#endif

View File

@ -42,7 +42,31 @@ typedef enum heatpumpstate{
HP_UNKNOWN
} heatpumpstate;
void updateHeatPumpLed(heatpumpstate state, aqledstate ledstate, struct aqualinkdata *aqdata, bool fromMessage);
typedef enum heatpumpmsgfrom{
HP_TO_PANEL,
HP_FROM_PANEL,
HP_DISPLAY
} heatpumpmsgfrom;
void updateHeatPumpLed(heatpumpstate state, aqledstate ledstate, struct aqualinkdata *aqdata, heatpumpmsgfrom from);
void printJandyDebugPacket (const char *msg, const unsigned char *packet, int packet_length)
{
// Only log if we are jandy debug mode and not serial debug (otherwise it'll print twice)
if (getLogLevel(DJAN_LOG) == LOG_DEBUG && getLogLevel(RSSD_LOG) < LOG_DEBUG ) {
char frame[1024];
//beautifyPacket(frame, 1024, packet, packet_length, true);
sprintFrame(frame, 1024, packet, packet_length);
LOG(DJAN_LOG, LOG_DEBUG, "%-4s %-6s: 0x%02hhx of type %16.16s | HEX: %s",
(packet[PKT_DEST]==0x00?"From":"To"),
msg,
packet[PKT_DEST],
get_packet_type(packet, packet_length),
frame);
}
}
bool processJandyPacket(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata)
{
@ -55,28 +79,39 @@ bool processJandyPacket(unsigned char *packet_buffer, int packet_length, struct
{
if (interestedInNextAck == DRS_SWG)
{
printJandyDebugPacket("SWG", packet_buffer, packet_length);
rtn = processPacketFromSWG(packet_buffer, packet_length, aqdata, previous_packet_to);
}
else if (interestedInNextAck == DRS_EPUMP)
{
printJandyDebugPacket("EPump", packet_buffer, packet_length);
rtn = processPacketFromJandyPump(packet_buffer, packet_length, aqdata, previous_packet_to);
}
else if (interestedInNextAck == DRS_JXI)
{
printJandyDebugPacket("JXi", packet_buffer, packet_length);
rtn = processPacketFromJandyJXiHeater(packet_buffer, packet_length, aqdata, previous_packet_to);
}
else if (interestedInNextAck == DRS_LX)
{
printJandyDebugPacket("LX", packet_buffer, packet_length);
rtn = processPacketFromJandyLXHeater(packet_buffer, packet_length, aqdata, previous_packet_to);
}
else if (interestedInNextAck == DRS_CHEM)
{
printJandyDebugPacket("CHEM", packet_buffer, packet_length);
rtn = processPacketFromJandyChemFeeder(packet_buffer, packet_length, aqdata, previous_packet_to);
}
else if (interestedInNextAck == DRS_HEATPUMP)
{
printJandyDebugPacket("HPump", packet_buffer, packet_length);
rtn = processPacketFromHeatPump(packet_buffer, packet_length, aqdata, previous_packet_to);
}
else if (interestedInNextAck == DRS_JLIGHT)
{
printJandyDebugPacket("JLight", packet_buffer, packet_length);
rtn = processPacketFromJandyLight(packet_buffer, packet_length, aqdata, previous_packet_to);
}
interestedInNextAck = DRS_NONE;
previous_packet_to = NUL;
}
@ -97,6 +132,7 @@ bool processJandyPacket(unsigned char *packet_buffer, int packet_length, struct
else if (READ_RSDEV_SWG && packet_buffer[PKT_DEST] >= JANDY_DEC_SWG_MIN && packet_buffer[PKT_DEST] <= JANDY_DEC_SWG_MAX)
{
interestedInNextAck = DRS_SWG;
printJandyDebugPacket("SWG", packet_buffer, packet_length);
rtn = processPacketToSWG(packet_buffer, packet_length, aqdata/*, _aqconfig_.swg_zero_ignore*/);
previous_packet_to = packet_buffer[PKT_DEST];
}
@ -104,24 +140,28 @@ bool processJandyPacket(unsigned char *packet_buffer, int packet_length, struct
|| (packet_buffer[PKT_DEST] >= JANDY_DEC_PUMP2_MIN && packet_buffer[PKT_DEST] <= JANDY_DEC_PUMP2_MAX) ) )
{
interestedInNextAck = DRS_EPUMP;
printJandyDebugPacket("EPump", packet_buffer, packet_length);
rtn = processPacketToJandyPump(packet_buffer, packet_length, aqdata);
previous_packet_to = packet_buffer[PKT_DEST];
}
else if (READ_RSDEV_JXI && packet_buffer[PKT_DEST] >= JANDY_DEC_JXI_MIN && packet_buffer[PKT_DEST] <= JANDY_DEC_JXI_MAX)
{
interestedInNextAck = DRS_JXI;
printJandyDebugPacket("JXi", packet_buffer, packet_length);
rtn = processPacketToJandyJXiHeater(packet_buffer, packet_length, aqdata);
previous_packet_to = packet_buffer[PKT_DEST];
}
else if (READ_RSDEV_LX && packet_buffer[PKT_DEST] >= JANDY_DEC_LX_MIN && packet_buffer[PKT_DEST] <= JANDY_DEC_LX_MAX)
{
interestedInNextAck = DRS_LX;
printJandyDebugPacket("LX", packet_buffer, packet_length);
rtn = processPacketToJandyLXHeater(packet_buffer, packet_length, aqdata);
previous_packet_to = packet_buffer[PKT_DEST];
}
else if (READ_RSDEV_CHEM && packet_buffer[PKT_DEST] >= JANDY_DEC_CHEM_MIN && packet_buffer[PKT_DEST] <= JANDY_DEC_CHEM_MAX)
{
interestedInNextAck = DRS_CHEM;
printJandyDebugPacket("CHEM", packet_buffer, packet_length);
rtn = processPacketToJandyChemFeeder(packet_buffer, packet_length, aqdata);
previous_packet_to = packet_buffer[PKT_DEST];
}
@ -133,9 +173,17 @@ bool processJandyPacket(unsigned char *packet_buffer, int packet_length, struct
else if (READ_RSDEV_HPUMP && packet_buffer[PKT_DEST] >= JANDY_DEV_HPUMP_MIN && packet_buffer[PKT_DEST] <= JANDY_DEV_HPUMP_MAX)
{
interestedInNextAck = DRS_HEATPUMP;
printJandyDebugPacket("HPump", packet_buffer, packet_length);
rtn = processPacketToHeatPump(packet_buffer, packet_length, aqdata);
previous_packet_to = packet_buffer[PKT_DEST];
}
else if (READ_RSDEV_JLIGHT && packet_buffer[PKT_DEST] >= JANDY_DEV_JLIGHT_MIN && packet_buffer[PKT_DEST] <= JANDY_DEV_JLIGHT_MAX)
{
interestedInNextAck = DRS_JLIGHT;
printJandyDebugPacket("JLight", packet_buffer, packet_length);
rtn = processPacketToJandyLight(packet_buffer, packet_length, aqdata);
previous_packet_to = packet_buffer[PKT_DEST];
}
else
{
interestedInNextAck = DRS_NONE;
@ -151,16 +199,16 @@ bool processJandyPacket(unsigned char *packet_buffer, int packet_length, struct
return rtn;
}
bool processPacketToSWG(unsigned char *packet, int packet_length, struct aqualinkdata *aqdata /*, int swg_zero_ignore*/) {
//static int swg_zero_cnt = 0;
bool changedAnything = false;
// Only log if we are jandy debug move and not serial (otherwise it'll print twice)
if (getLogLevel(DJAN_LOG) == LOG_DEBUG && getLogLevel(RSSD_LOG) < LOG_DEBUG ) {
char buff[1024];
beautifyPacket(buff, 1024, packet, packet_length, true);
LOG(DJAN_LOG,LOG_DEBUG, "To SWG: %s", buff);
}
// Only read message from controller to SWG to set SWG Percent if we are not programming, as we might be changing this
if (packet[3] == CMD_PERCENT && aqdata->active_thread.thread_id == 0 && packet[4] != 0xFF) {
@ -219,13 +267,6 @@ bool processPacketFromSWG(unsigned char *packet, int packet_length, struct aqual
return changedAnything;
}
// Only log if we are jandy debug move and not serial (otherwise it'll print twice)
if (getLogLevel(DJAN_LOG) == LOG_DEBUG && getLogLevel(RSSD_LOG) < LOG_DEBUG ) {
char buff[1024];
beautifyPacket(buff, 1024, packet, packet_length, true);
LOG(DJAN_LOG,LOG_DEBUG, "From SWG: %s", buff);
}
if (packet[PKT_CMD] == CMD_PPM) {
//aqdata->ar_swg_device_status = packet[5];
setSWGdeviceStatus(aqdata, JANDY_DEVICE, packet[5]);
@ -588,12 +629,6 @@ bool processPacketToJandyPump(unsigned char *packet_buffer, int packet_length, s
Type 0x1F and cmd 0x45 is RPM = 39 * (256) + 96 / 4 = 2520 or Byte 8 * 265 + Byte 7 / 4
*/
// Only log if we are jandy debug move and not serial (otherwise it'll print twice)
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", msg);
}
// If type 0x45 and 0x44 set to interested in next command.
if (packet_buffer[3] == CMD_EPUMP_RPM) {
@ -620,14 +655,6 @@ bool processPacketFromJandyPump(unsigned char *packet_buffer, int packet_length,
{
bool found=false;
// Only log if we are jandy debug move and not serial (otherwise it'll print twice)
if (getLogLevel(DJAN_LOG) == LOG_DEBUG && getLogLevel(RSSD_LOG) < LOG_DEBUG ) {
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", msg);
}
if (packet_buffer[3] == CMD_EPUMP_STATUS && packet_buffer[4] == CMD_EPUMP_RPM) {
for (int i = 0; i < MAX_PUMPS; i++) {
if ( aqdata->pumps[i].prclType == JANDY && aqdata->pumps[i].pumpID == previous_packet_to ) {
@ -686,13 +713,6 @@ int getPumpStatus(int pumpIndex, struct aqualinkdata *aqdata)
bool processPacketToJandyJXiHeater(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata)
{
if (getLogLevel(DJAN_LOG) == LOG_DEBUG && getLogLevel(RSSD_LOG) < LOG_DEBUG ) {
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", msg);
}
if (packet_buffer[3] != CMD_JXI_PING) {
// Not sure what this message is, so ignore
// Maybe print a messsage.
@ -819,12 +839,6 @@ void getJandyHeaterErrorMQTT(struct aqualinkdata *aqdata, char *message)
bool processPacketFromJandyJXiHeater(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata, const unsigned char previous_packet_to)
{
if (getLogLevel(DJAN_LOG) == LOG_DEBUG && getLogLevel(RSSD_LOG) < LOG_DEBUG ) {
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", msg);
}
if (packet_buffer[3] != CMD_JXI_STATUS) {
// Not sure what this message is, so ignore
@ -879,9 +893,6 @@ bool processPacketToJandyLXHeater(unsigned char *packet_buffer, int packet_lengt
char msg[1024];
int length = 0;
beautifyPacket(msg, 1024, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_INFO, "To LX: %s", msg);
length += sprintf(msg+length, "Last panel info ");
for (int i=0; i < aqdata->total_buttons; i++)
@ -908,9 +919,6 @@ bool processPacketFromJandyLXHeater(unsigned char *packet_buffer, int packet_len
char msg[1024];
int length = 0;
beautifyPacket(msg, 1024, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_INFO, "From LX: %s", msg);
length += sprintf(msg+length, "Last panel info ");
for (int i=0; i < aqdata->total_buttons; i++)
@ -937,9 +945,6 @@ bool processPacketToJandyChemFeeder(unsigned char *packet_buffer, int packet_len
char msg[1024];
int length = 0;
beautifyPacket(msg, 1024, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_INFO, "To Chem: %s", msg);
length += sprintf(msg+length, "Last panel info ");
length += sprintf(msg+length, ", pH=%f, ORP=%d",aqdata->ph, aqdata->orp);
@ -953,9 +958,6 @@ bool processPacketFromJandyChemFeeder(unsigned char *packet_buffer, int packet_l
char msg[1024];
int length = 0;
beautifyPacket(msg, 1024, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_INFO, "From Chem: %s", msg);
length += sprintf(msg+length, "Last panel info ");
length += sprintf(msg+length, ", pH=%f, ORP=%d",aqdata->ph, aqdata->orp);
@ -974,10 +976,6 @@ bool processPacketFromJandyChemFeeder(unsigned char *packet_buffer, int packet_l
bool processPacketToHeatPump(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata)
{
char msg[1024];
beautifyPacket(msg, 1024, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_INFO, "To HPump: %s", msg);
/* Byted 3 and 4
0x0c|0x01 = Heat Pump Enabled
0x0c|0x29 = Chiller on
@ -989,25 +987,39 @@ bool processPacketToHeatPump(unsigned char *packet_buffer, int packet_length, st
0x0c|0x00 = Request off
0x0c|0x09 = Request Heat
0x0c|0x29 = Request Cool
*/
/*
0x0c|0x00|0x00|0x00|0x00| CMD HP Disabled
0x0c|0x0a|0x00|0x00|0x00| CMD HP Heat SPA
0x0c|0x01|0x00|0x00|0x00| CMD HP Enabled Pool
0x0c|0x02|0x00|0x00|0x00| CMD HP Enabled SPA
0x0c|0x09|0x00|0x00|0x00| CMD HP Heat Pool
*/
if (packet_buffer[3] == 0x0c ) {
if (packet_buffer[4] == 0x00) {
// Heat Pump is off
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) {
// 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_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(false, ENABLE, aqdata, false); // Guess at enabled. ()
switch(packet_buffer[4]) {
case 0x00: // Heat Pump is off
LOG(DJAN_LOG, LOG_DEBUG, "Heat Pump 0x%02hhx request to Off - status 0x%02hhx\n",packet_buffer[PKT_DEST],packet_buffer[4] );
updateHeatPumpLed(HP_UNKNOWN, OFF, aqdata, HP_FROM_PANEL);
break;
case 0x09: // Heat Pool
case 0x0a: // Heat Spa
LOG(DJAN_LOG, LOG_DEBUG, "Heat Pump 0x%02hhx request to Heat - status 0x%02hhx\n",packet_buffer[PKT_DEST],packet_buffer[4] );
updateHeatPumpLed(HP_HEAT, ON, aqdata, HP_FROM_PANEL);
break;
case 0x01: // Enabled Pool
case 0x02: // Enabled SPA
LOG(DJAN_LOG, LOG_DEBUG, "Heat Pump 0x%02hhx Enabled - status 0x%02hhx\n",packet_buffer[PKT_DEST],packet_buffer[4] );
updateHeatPumpLed(HP_UNKNOWN, ENABLE, aqdata, HP_FROM_PANEL);
break;
case 0x29: // 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(HP_COOL, ENABLE, aqdata, HP_FROM_PANEL);
break;
default:
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(HP_UNKNOWN, ENABLE, aqdata, HP_FROM_PANEL); // Guess at enabled. ()
break;
}
} 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]);
@ -1018,7 +1030,6 @@ bool processPacketToHeatPump(unsigned char *packet_buffer, int packet_length, st
}
bool processPacketFromHeatPump(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata, const unsigned char previous_packet_to)
{
char msg[1024];
/*
HEX: 0x10|0x02|0x00|0x0d|0x40|0x00|0x00|0x5f|0x10|0x03|
HEX: 0x10|0x02|0x00|0x0d|0x48|0x00|0x00|0x67|0x10|0x03|
@ -1032,11 +1043,12 @@ bool processPacketFromHeatPump(unsigned char *packet_buffer, int packet_length,
if (packet_buffer[3] == 0x0d ) {
if (packet_buffer[4] == 0x40) {
updateHeatPumpLed(HP_HEAT, OFF, aqdata, false);
//updateHeatPumpLed(HP_HEAT, OFF, aqdata, HP_TO_PANEL);
updateHeatPumpLed(HP_HEAT, ENABLE, aqdata, HP_TO_PANEL);
} else if (packet_buffer[4] == 0x48) {
updateHeatPumpLed(HP_HEAT, ON, aqdata, false);
updateHeatPumpLed(HP_HEAT, ON, aqdata, HP_TO_PANEL);
} else if (packet_buffer[4] == 0x68) {
updateHeatPumpLed(HP_COOL, ON, aqdata, false);
updateHeatPumpLed(HP_COOL, ON, aqdata, HP_TO_PANEL);
} 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]);
@ -1045,9 +1057,6 @@ bool processPacketFromHeatPump(unsigned char *packet_buffer, int packet_length,
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", msg);
return false;
}
@ -1067,11 +1076,11 @@ void processHeatPumpDisplayMessage(char *msg, struct aqualinkdata *aqdata) {
hpstate = HP_COOL;
}
if (stristr(msg," ENA") != NULL) {
updateHeatPumpLed(hpstate, ENABLE, aqdata, true);
updateHeatPumpLed(hpstate, ENABLE, aqdata, HP_DISPLAY);
} else if (stristr(msg," OFF") != NULL) {
updateHeatPumpLed(hpstate, OFF, aqdata, true);
updateHeatPumpLed(hpstate, OFF, aqdata, HP_DISPLAY);
} else if (stristr(msg," ON") != NULL) {
updateHeatPumpLed(hpstate, ON, aqdata, true);
updateHeatPumpLed(hpstate, ON, aqdata, HP_DISPLAY);
}
LOG(AQUA_LOG,LOG_DEBUG, "Set %s to %s from message '%s'",
@ -1080,11 +1089,28 @@ void processHeatPumpDisplayMessage(char *msg, struct aqualinkdata *aqdata) {
}
//void updateHeatPumpLed(bool chiller, aqledstate state, struct aqualinkdata *aqdata) {
void updateHeatPumpLed(heatpumpstate state, aqledstate ledstate, struct aqualinkdata *aqdata, bool fromMessage)
void updateHeatPumpLed(heatpumpstate state, aqledstate ledstate, struct aqualinkdata *aqdata, heatpumpmsgfrom from)
{
if (aqdata->chiller_button == NULL)
return;
// ledstate Enabled is valied from Display and FromPanel HP_FROM_PANEL, HP_DISPLAY (NOT valid from HP ie HP_TO_PANEL)
// ledstate on off is only valid from HP or DISPLAY HP_TO_PANEL or HP_TO_PANEL (NOT FROM HP_FROM_PANEL)
if ( (ledstate == ENABLE && (from == HP_DISPLAY || from == HP_FROM_PANEL)) ||
( (ledstate == ON || ledstate == OFF) && (from == HP_DISPLAY || from == HP_TO_PANEL) )) {
if ( ledstate != 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;
}
/*
// 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) {
@ -1104,7 +1130,36 @@ void updateHeatPumpLed(heatpumpstate state, aqledstate ledstate, struct aqualink
} else {
//printf("**** Heat Pump %s, already %s, ignore!\n",LED2text(ledstate),LED2text(aqdata->chiller_button->led->state));
}
*/
}
bool processPacketToJandyLight(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata)
{
if (packet_buffer[3] == 0x4b) {
LOG(DJAN_LOG, LOG_INFO, "Request to set Jandy Light brightness to %d\n", packet_buffer[6]);
} else if (packet_buffer[3] == 0x3a) {
LOG(DJAN_LOG, LOG_INFO, "Request to set Jandy Light RGB to %d:%d:%d\n", packet_buffer[6],packet_buffer[7],packet_buffer[8]);
}
logPacket(DJAN_LOG, LOG_INFO, packet_buffer, packet_length, true);
return true;
}
bool processPacketFromJandyLight(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata, const unsigned char previous_packet_to)
{
if (packet_buffer[3] == 0x31 ) {
// This has most of the info.
LOG(DJAN_LOG, LOG_INFO, "Light brightness=%d\n", packet_buffer[38]);
}
logPacket(DJAN_LOG, LOG_INFO, packet_buffer, packet_length, true);
return true;
}
/*
// JXi Heater
@ -1173,6 +1228,9 @@ LXi status | HEX: 0x10|0x02|0x00|0x0d|0x40|0x00|0x00|0x5f|0x10|0x03|
LXi heater ping | HEX: 0x10|0x02|0x70|0x0c|0x29|0x00|0x00|0x00|0xb7|0x10|0x03|. byte 4 0x29 This is some on / enable
LXi status | HEX: 0x10|0x02|0x00|0x0d|0x68|0x00|0x00|0x87|0x10|0x03| 0x68 probably is chiller ON
Below is when heatpump chiller is enabled but NOT on (heat or cool)
JandyDvce: To HPump: Read Jandy packet To 0x70 of type LXi heater ping | HEX: 0x10|0x02|0x70|0x0c|0x01|0x00|0x00|0x00|0x8f|0x10|0x03|
JandyDvce: From HPump: Read Jandy packet To 0x00 of type LXi status | HEX: 0x10|0x02|0x00|0x0d|0x40|0x00|0x00|0x5f|0x10|0x03|
0x0c|0x01 = Enabled
0x0c|0x29 = Chiller on
@ -1195,4 +1253,98 @@ Heat Pump Off
JandyDvce: To HPump: Read Jandy packet To 0x70 of type LXi heater ping | HEX: 0x10|0x02|0x70|0x0c|0x00|0x00|0x00|0x00|0x8e|0x10|0x03|
JandyDvce: From HPump: Read Jandy packet To 0x00 of type LXi status | HEX: 0x10|0x02|0x00|0x0d|0x40|0x00|0x00|0x5f|0x10|0x03|
*/
/*
RS485 Color Lights.
Turned lights on (was already set to "America the Beautiful") <- USA?? index 12
Switch color to "Alpine White". <- index 1
Turned brightness down to 50%
Turned brightness down to 25%
Turned off
packet To 0xf0 of type Unknown '0x32' | HEX: 0x10|0x02|0xf0|0x32|0x00|0x34|0x10|0x03|
packet To 0x00 of type Unknown '0x33' | HEX: 0x10|0x02|0x00|0x33|0x2d|0x00|0x72|0x10|0x03|
--
packet To 0xf0 of type Unknown '0x32' | HEX: 0x10|0x02|0xf0|0x32|0x00|0x34|0x10|0x03|
packet To 0x00 of type Unknown '0x33' | HEX: 0x10|0x02|0x00|0x33|0x2d|0x10|0x82|0x10|0x03|
--
******. Below reply byte 11 = 0x23 (color mode)????
packet To 0xf0 of type iAq Poll | HEX: 0x10|0x02|0xf0|0x30|0x00|0x32|0x10|0x03|
packet To 0x00 of type iAq receive read | HEX: 0x10|0x02|0x00|0x31|0x2d|0x06|0x02|0x22|0x02|0x21|0x01|0x23|0x02|0x21|0x01|0x23|0x01|0x24|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0x64|0x64|0x64|0x64|0x00|0x00|0x00|0x81|0x0c|0x0f|0x03|0x72|0x10|0x03|
--
packet To 0xf0 of type Unknown '0x32' | HEX: 0x10|0x02|0xf0|0x32|0x00|0x34|0x10|0x03|
packet To 0x00 of type Unknown '0x33' | HEX: 0x10|0x02|0x00|0x33|0x2d|0x00|0x72|0x10|0x03|
--
packet To 0xf0 of type Unknown '0x32' | HEX: 0x10|0x02|0xf0|0x32|0x00|0x34|0x10|0x03|
packet To 0x00 of type Unknown '0x33' | HEX: 0x10|0x02|0x00|0x33|0x2d|0x10|0x82|0x10|0x03|
--
***** Below reply packed 38 is 0x64 (brightness 100%)
***** Below reply packet 11 = 0x24 (color mode)?????
packet To 0xf0 of type iAq Poll | HEX: 0x10|0x02|0xf0|0x30|0x00|0x32|0x10|0x03|
packet To 0x00 of type iAq receive read | HEX: 0x10|0x02|0x00|0x31|0x2d|0x06|0x02|0x22|0x02|0x21|0x01|0x24|0x02|0x21|0x01|0x24|0x01|0x24|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0x64|0x64|0x64|0x64|0x00|0x00|0x00|0x81|0x0c|0x0f|0x03|0x74|0x10|0x03|
--
packet To 0xf0 of type Unknown '0x32' | HEX: 0x10|0x02|0xf0|0x32|0x00|0x34|0x10|0x03|
packet To 0x00 of type Unknown '0x33' | HEX: 0x10|0x02|0x00|0x33|0x2d|0x00|0x72|0x10|0x03|
--
packet To 0xf0 of type Unknown '0x32' | HEX: 0x10|0x02|0xf0|0x32|0x00|0x34|0x10|0x03|
packet To 0x00 of type Unknown '0x33' | HEX: 0x10|0x02|0x00|0x33|0x2d|0x00|0x72|0x10|0x03|
--
packet To 0xf0 of type Unknown '0x32' | HEX: 0x10|0x02|0xf0|0x32|0x00|0x34|0x10|0x03|
packet To 0x00 of type Unknown '0x33' | HEX: 0x10|0x02|0x00|0x33|0x2d|0x00|0x72|0x10|0x03|
--
********** Below Change to 50% ***********
packet To 0xf0 of type Unknown '0x4b' | HEX: 0x10|0x02|0xf0|0x4b|0x00|0x01|0x32|0x00|0x00|0x00|0x00|0x80|0x10|0x03|
packet To 0x00 of type Ack | HEX: 0x10|0x02|0x00|0x01|0x4b|0x00|0x5e|0x10|0x03|
--
packet To 0xf0 of type Unknown '0x32' | HEX: 0x10|0x02|0xf0|0x32|0x00|0x34|0x10|0x03|
packet To 0x00 of type Unknown '0x33' | HEX: 0x10|0x02|0x00|0x33|0x2d|0x50|0xc2|0x10|0x03|
--
***** Below reply packed 38 is 0x32 (brightness 50%)
packet To 0xf0 of type iAq Poll | HEX: 0x10|0x02|0xf0|0x30|0x00|0x32|0x10|0x03|
packet To 0x00 of type iAq receive read | HEX: 0x10|0x02|0x00|0x31|0x2d|0x06|0x02|0x22|0x02|0x21|0x01|0x23|0x02|0x21|0x01|0x23|0x01|0x24|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0x32|0x64|0x64|0x64|0x00|0x00|0x00|0x81|0x0c|0x0f|0x03|0x40|0x10|0x03|
--
packet To 0xf0 of type Unknown '0x32' | HEX: 0x10|0x02|0xf0|0x32|0x00|0x34|0x10|0x03|
packet To 0x00 of type Unknown '0x33' | HEX: 0x10|0x02|0x00|0x33|0x2d|0x02|0x74|0x10|0x03|
--
packet To 0xf0 of type Unknown '0x32' | HEX: 0x10|0x02|0xf0|0x32|0x00|0x34|0x10|0x03|
packet To 0x00 of type Unknown '0x33' | HEX: 0x10|0x02|0x00|0x33|0x2d|0x00|0x72|0x10|0x03|
--
***** Below reply packed 38 is 0x19 (brightness 25%)
*********** Below Change to 25% ************
packet To 0xf0 of type Unknown '0x4b' | HEX: 0x10|0x02|0xf0|0x4b|0x00|0x01|0x19|0x00|0x00|0x00|0x00|0x67|0x10|0x03|
packet To 0x00 of type Ack | HEX: 0x10|0x02|0x00|0x01|0x4b|0x00|0x5e|0x10|0x03|
--
packet To 0xf0 of type Unknown '0x32' | HEX: 0x10|0x02|0xf0|0x32|0x00|0x34|0x10|0x03|
packet To 0x00 of type Unknown '0x33' | HEX: 0x10|0x02|0x00|0x33|0x2d|0x50|0xc2|0x10|0x03|
--
packet To 0xf0 of type iAq Poll | HEX: 0x10|0x02|0xf0|0x30|0x00|0x32|0x10|0x03|
packet To 0x00 of type iAq receive read | HEX: 0x10|0x02|0x00|0x31|0x2d|0x06|0x02|0x22|0x02|0x21|0x01|0x23|0x02|0x21|0x01|0x23|0x01|0x23|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0x19|0x64|0x64|0x64|0x00|0x00|0x00|0x81|0x0c|0x0f|0x03|0x26|0x10|0x03|
--
packet To 0xf0 of type Unknown '0x32' | HEX: 0x10|0x02|0xf0|0x32|0x00|0x34|0x10|0x03|
packet To 0x00 of type Unknown '0x33' | HEX: 0x10|0x02|0x00|0x33|0x2d|0x00|0x72|0x10|0x03|
--
packet To 0xf0 of type Unknown '0x32' | HEX: 0x10|0x02|0xf0|0x32|0x00|0x34|0x10|0x03|
packet To 0x00 of type Unknown '0x33' | HEX: 0x10|0x02|0x00|0x33|0x2d|0x00|0x72|0x10|0x03|
--
packet To 0xf0 of type Unknown '0x32' | HEX: 0x10|0x02|0xf0|0x32|0x00|0x34|0x10|0x03|
packet To 0x00 of type Unknown '0x33' | HEX: 0x10|0x02|0x00|0x33|0x2d|0x40|0xb2|0x10|0x03|
--
packet To 0xf0 of type iAq Poll | HEX: 0x10|0x02|0xf0|0x30|0x00|0x32|0x10|0x03|
packet To 0x00 of type iAq receive read | HEX: 0x10|0x02|0x00|0x31|0x2d|0x06|0x02|0x22|0x02|0x21|0x01|0x23|0x02|0x21|0x01|0x23|0x01|0x23|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0x19|0x64|0x64|0x64|0x00|0x00|0x00|0x01|0x0c|0x0f|0x03|0xa6|0x10|0x03|
--
packet To 0xf0 of type Unknown '0x32' | HEX: 0x10|0x02|0xf0|0x32|0x00|0x34|0x10|0x03|
packet To 0x00 of type Unknown '0x33' | HEX: 0x10|0x02|0x00|0x33|0x2d|0x10|0x82|0x10|0x03|
--
packet To 0xf0 of type iAq Poll | HEX: 0x10|0x02|0xf0|0x30|0x00|0x32|0x10|0x03|
packet To 0x00 of type iAq receive read | HEX: 0x10|0x02|0x00|0x31|0x2d|0x06|0x02|0x22|0x02|0x21|0x01|0x22|0x02|0x21|0x01|0x23|0x01|0x23|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0xff|0x00|0x19|0x64|0x64|0x64|0x00|0x00|0x00|0x01|0x0c|0x0f|0x03|0xa5|0x10|0x03|
*/

View File

@ -25,6 +25,9 @@ bool processPacketToJandyChemFeeder(unsigned char *packet_buffer, int packet_len
bool processPacketToHeatPump(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata);
bool processPacketFromHeatPump(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata, const unsigned char previous_packet_to);
bool processPacketToJandyLight(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata);
bool processPacketFromJandyLight(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata, const unsigned char previous_packet_to);
void get_swg_status_mqtt(struct aqualinkdata *aqdata, char *message, int *status, int *dzalert);
aqledstate get_swg_led_state(struct aqualinkdata *aqdata);

View File

@ -28,7 +28,14 @@
#include "config.h"
#define SWG
//#define HEATPUMP
#ifdef SWG
unsigned char DEVICE_ID = 0x50;
#else
unsigned char DEVICE_ID = 0x70;
#endif
bool _keepRunning = true;
int _rs_fd;
@ -43,6 +50,7 @@ bool isAqualinkDStopping() {
return !_keepRunning;
}
void process_swg_packet(unsigned char *packet_buffer, const int packet_length);
void process_heatpump_packet(unsigned char *packet_buffer, const int packet_length);
int main(int argc, char *argv[])
@ -143,7 +151,11 @@ int main(int argc, char *argv[])
if (packet_buffer[PKT_DEST] == DEVICE_ID)
{
#ifdef SWG
process_swg_packet(packet_buffer, packet_length);
#else
process_heatpump_packet(packet_buffer, packet_length);
#endif
}
}
}
@ -206,3 +218,36 @@ void process_heatpump_packet(unsigned char *packet_buffer, const int packet_leng
}
void process_swg_packet(unsigned char *packet_buffer, const int packet_length)
{
//(packet[3] = 0x16
//(packet[4] * 100) = PPM
static unsigned char swg_ack[] = {0x00,0x01,0x00,0x00};
static unsigned char swg_ppm[] = {0x00, 0x16, 0x1f, 0x00, 0x00, 0x00};
//static unsigned char swg_id[] = {0x00,0x03,0x00,0x49,0x6e,0x74,0x65,0x6c,0x6c,0x69,0x63,0x68,0x6c,0x6f,0x72,0x2d,0x2d,0x34,0x30};
//static unsigned char swg_id[] = {0x00,0x03,0x00,0x41,0x71,0x75,0x61,0x50,0x75,0x72,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
static unsigned char swg_id[] = {0x00,0x03,0x01,0x41,0x71,0x75,0x61,0x50,0x75,0x72,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
// 0x11 set SWG %
if (packet_buffer[PKT_CMD] == CMD_PROBE)
{
//send_ack(_rs_fd, 0x00);
send_jandy_command(_rs_fd, swg_ack, 4);
LOG(SLOG_LOG, LOG_NOTICE, "Replied to Probe packet to 0x%02hhx with ACK\n",packet_buffer[PKT_DEST]);
}
else if (packet_buffer[3] == 0x11)
{
//int percent = (int)packet_buffer[4];
send_jandy_command(_rs_fd, swg_ppm, 6);
LOG(SLOG_LOG, LOG_NOTICE, "Received %%=%d, Replied PPM=%d\n",(int)packet_buffer[4],swg_ppm[2] * 100);
}
else if (packet_buffer[3] == 0x14)
{
send_jandy_command(_rs_fd, swg_id, 19);
LOG(SLOG_LOG, LOG_NOTICE, "Receive ID request, replied\n");
}
else
{
LOG(SLOG_LOG, LOG_ERR, "************* Unknown Request 0x%02hhx *************",packet_buffer[3]);
}
}

188
source/dummy_reader.c Normal file
View File

@ -0,0 +1,188 @@
#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"
bool _keepRunning = true;
int _rs_fd;
void intHandler(int dummy)
{
_keepRunning = false;
LOG(SLOG_LOG, LOG_NOTICE, "Stopping!\n");
}
bool isAqualinkDStopping() {
return !_keepRunning;
}
int get_bytes(FILE *fd, unsigned char* buffer)
{
int packet_length = 0;
char line[4000];
char hex[6];
int i;
bool foundHex=false;
/*
const char *hex_string = "0xFF";
unsigned char value;
// Use strtoul to convert the hex string to an unsigned long
// The third argument, 16, specifies the base (hexadecimal)
unsigned long temp_value = strtoul(hex_string, NULL, 16);
// Cast the unsigned long to unsigned char
value = (unsigned char)temp_value;
*/
if ( fgets ( line, sizeof line, fd ) != NULL ) /* read a line */
{
packet_length=0;
for (i=0; i < strlen(line); i++)
{
if (line[i] == 'H' && line[i+1] == 'E' && line[i+2] == 'X' && line[i+3] == ':') {
foundHex=true;
i=i+4;
}
if (line[i] == '0' && line[i+1] == 'x' && foundHex) {
break;
}
if (i<=1 && line[i] == '0' && line[i+1] == 'x') {
//printf(stdout,"Line starting with hex\n");
break;
}
}
if (i == strlen(line)) {
//printf( "PLAYBACK No binary\n");
return 0;
} else {
//printf(" Transposed from index %d=",i);
//printf( "%s",line);
LOG(SLOG_LOG, LOG_DEBUG, "Read bytes %s", line);
}
for (i=i; i < strlen(line); i=i+5)
{
strncpy(hex, &line[i], 4);
hex[5] = '\0';
buffer[packet_length] = (int)strtol(hex, NULL, 16);
packet_length++;
}
packet_length--;
//printf("End Char = 0x%02hhx\n",buffer[packet_length-1]);
buffer[packet_length] = '\0';
return packet_length;
}
return -1;
}
bool createBinaryFile(char *dest, char *source) {
int size = 0;
unsigned char buffer[4000];
FILE *sfp = fopen ( source, "r" );
if ( sfp == NULL )
{
perror ( source ); /* why didn't the file open? */
return FALSE;
}
FILE *dfp = fopen ( dest, "wb" );
if ( dfp == NULL )
{
perror ( dest ); /* why didn't the file open? */
fclose(sfp);
return FALSE;
}
while (size != -1) {
size = get_bytes(sfp, buffer);
if (size > 0) {
//printf("GOT %d bytes\n",size);
//fputs(buffer, dfp);
fwrite(&buffer, sizeof(unsigned char), size, dfp);
//char pbuf[256];
//beautifyPacket(pbuf, 256, buffer, size, TRUE);
//printf("%s\n",pbuf);
}
//printf("-----------------\n");
//if (buffer[3] == 0x72) {
// print72pck(buffer, size);
//process_iAqualinkStatusPacket(buffer, size);
//}
}
fclose(sfp);
fclose(dfp);
return TRUE;
}
int main(int argc, char *argv[])
{
int logLevel = LOG_DEBUG;
int fp;
int packet_length;
unsigned char packet_buffer[AQ_MAXPKTLEN+1];
if (argc < 2 || access(argv[1], F_OK) == -1)
{
fprintf(stderr, "ERROR, first param must be valid filename\n");
return 1;
}
setLoggingPrms(logLevel, false, NULL);
LOG(SLOG_LOG, LOG_INFO, "Start reading %s\n", basename(argv[1]));
createBinaryFile("/tmp/tmp.tmp", argv[1]);
if ((fp = open("/tmp/tmp.tmp", O_RDONLY)) == -1)
{
fprintf(stderr, "Cannot open %s\n",argv[1]);
return 1;
}
//packet_length = get_packet(fp, packet_buffer);
while ((packet_length = get_packet(fp, packet_buffer)) != 0) {
LOG(SLOG_LOG, LOG_INFO, "Read %d bytes\n", packet_length);
}
/*
if (packet_length == 0)
{
LOG(SLOG_LOG, LOG_ERR, "Error Read, %d\n", packet_length);
close(fp);
return 1;
} else {
LOG(SLOG_LOG, LOG_INFO, "Read %d bytes\n", packet_length);
}
*/
LOG(SLOG_LOG, LOG_INFO, "Stopping!\n");
close(fp);
return 0;
}

View File

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

View File

@ -489,7 +489,11 @@ bool process_iAqualinkStatusPacket(unsigned char *packet, int length, struct aqu
int byteType = packet[5 + i];
LOG(IAQL_LOG, LOG_INFO, "%-15.*s = %s | index %d type=(%0.2d 0x%02hhx) status=0x%02hhx start=%d length=%d\n", length, &packet[start + 2], (status == 0x00 ? "Off" : "On "), i, byteType, byteType, status, start, length);
// Check against virtual onetouch buttons.
// NSF This needs to check strlingth, ie "Spa Mode" vs "Spa"
if ( aq_data->virtual_button_start > 0 ) {
for (int bi=aq_data->virtual_button_start ; bi < aq_data->total_buttons ; bi++) {
//LOG(IAQL_LOG, LOG_INFO, "Check %s against %s\n",(char *)&packet[start + 2], aq_data->aqbuttons[bi].label);
if (rsm_strcmp((char *)&packet[start + 2], aq_data->aqbuttons[bi].label) == 0) {
//LOG(IAQL_LOG, LOG_INFO, "Status for %s is %s\n",aq_data->aqbuttons[bi].label,(status == 0x00 ? "Off" : "On "));
// == means doesn;t match, RS 1=on 0=off / LED enum 1=off 0=on
@ -501,6 +505,7 @@ bool process_iAqualinkStatusPacket(unsigned char *packet, int length, struct aqu
}
}
start = start + packet[start + 1] + 2;
}
}
}
else if (packet[PKT_CMD] == CMD_IAQ_AUX_STATUS)

View File

@ -545,12 +545,36 @@ int build_device_JSON(struct aqualinkdata *aqdata, char* buffer, int size, bool
{
if (aqdata->sensors[i].value != TEMP_UNKNOWN) {
//length += sprintf(buffer+length, "\"%s\": \"%.2f\",", aqdata->sensors[i].label, aqdata->sensors[i].value );
/*
length += sprintf(buffer+length, "{\"type\": \"temperature\", \"id\": \"%s/%s\", \"name\": \"%s\", \"state\": \"%s\", \"value\": \"%.*f\" },",
SENSOR_TOPIC,aqdata->sensors[i].label,
aqdata->sensors[i].label,
"on",
((homekit)?2:0),
((homekit_f)?aqdata->sensors[i].value:aqdata->sensors[i].value));*/
temperatureUOM uom = getTemperatureUOM(aqdata->sensors[i].uom);
if (uom == UNKNOWN) {
length += sprintf(buffer+length, "{\"type\": \"value\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"on\", \"value\": \"%.*f\" },",
aqdata->sensors[i].ID,
aqdata->sensors[i].label,
2,
aqdata->sensors[i].value);
} else if ( !homekit && (aqdata->temp_units == FAHRENHEIT && uom == CELSIUS) ) {
length += sprintf(buffer+length, "{\"type\": \"temperature\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"on\", \"value\": \"%.*f\" },",
aqdata->sensors[i].ID,
aqdata->sensors[i].label,
2,
degCtoF(aqdata->sensors[i].value));
} else {
length += sprintf(buffer+length, "{\"type\": \"temperature\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"value\": \"%.*f\" },",
aqdata->sensors[i].ID,
aqdata->sensors[i].label,
"on",
((homekit)?2:0),
((homekit_f)?aqdata->sensors[i].value:aqdata->sensors[i].value));
}
}
}
/*
@ -567,6 +591,8 @@ int build_device_JSON(struct aqualinkdata *aqdata, char* buffer, int size, bool
buffer[length] = '\0';
//printf("%s\n",buffer);
return strlen(buffer);
//return length;
@ -829,14 +855,16 @@ printf("Pump Type %d\n",aqdata->pumps[i].pumpType);
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 (aqdata->virtual_button_start > 0) {
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--;
}
if (buffer[length-1] == ',')
length--;
length += sprintf(buffer+length, "}");
@ -844,8 +872,14 @@ printf("Pump Type %d\n",aqdata->pumps[i].pumpType);
for (i=0; i < aqdata->num_sensors; i++)
{
//printf("Sensor value %f %.2f\n",aqdata->sensors[i].value,aqdata->sensors[i].value);
if (aqdata->sensors[i].value != TEMP_UNKNOWN) {
length += sprintf(buffer+length, "\"%s\": \"%.2f\",", aqdata->sensors[i].label, aqdata->sensors[i].value );
//length += sprintf(buffer+length, "\"%s\": \"%.2f\",", aqdata->sensors[i].label, aqdata->sensors[i].value );
if ( aqdata->temp_units == FAHRENHEIT && getTemperatureUOM(aqdata->sensors[i].uom) == CELSIUS ) {
length += sprintf(buffer+length, "\"%s\": \"%.1f\",", aqdata->sensors[i].ID, degCtoF(aqdata->sensors[i].value) );
} else {
length += sprintf(buffer+length, "\"%s\": \"%.1f\",", aqdata->sensors[i].ID, aqdata->sensors[i].value );
}
}
}
if (buffer[length-1] == ',')
@ -1183,7 +1217,7 @@ int json_cfg_element(char* buffer, int size, const char *name, const void *value
int result = 0;
char valid_values[256];
char adv[128];
char adv[256];
int adv_size=0;
// We shouldn't get CFG_HIDE here. Since we can't exit with 0, simply add a space
@ -1200,8 +1234,19 @@ int json_cfg_element(char* buffer, int size, const char *name, const void *value
if (isMASKSET(config_mask, CFG_READONLY))
adv_size += sprintf(adv+adv_size,",\"readonly\": \"yes\"");
if (isMASKSET(config_mask, CFG_FORCE_RESTART))
if (isMASKSET(config_mask, CFG_FORCE_RESTART)) {
adv_size += sprintf(adv+adv_size,",\"force_restart\": \"yes\"");
if ( strcmp(name, CFG_N_panel_type) == 0 ) {
adv_size += sprintf(adv+adv_size,",\"force_restart_msg\": \"If you panel_type, you must save and reload config for correct config options to show, and must also restart AqualinkD once finished!\"");
}
}
if (isMASKSET(config_mask, CFG_ALLOW_BLANK))
adv_size += sprintf(adv+adv_size,",\"allow_blank\": \"yes\"");
if (isMASKSET(config_mask, CFG_GREYED_OUT))
adv_size += sprintf(adv+adv_size,",\"greyed_out\": \"yes\"");
switch(type){
@ -1316,18 +1361,11 @@ 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;
@ -1368,6 +1406,25 @@ int build_aqualink_config_JSON(char* buffer, int size, struct aqualinkdata *aq_d
else
length += result;
sprintf(buf,"sensor_%.2d_uom", i);
if ((result = json_cfg_element(buffer+length, size-length, buf, &aq_data->sensors[i-1].uom, CFG_STRING, 0, NULL, CFG_GRP_ADVANCED)) <= 0)
return length;
else
length += result;
//(&aq_data->sensors[i-1].uom==NULL ? "" : &aq_data->sensors[i-1].uom)
/*
// Need to escape / with /// for this to work, and fix the disply that will show // for ////
// Don;t forget config.c, Line 2096, search comment // NSF When fixed the JSON & config editor, put these lines back.
if (&aq_data->sensors[i-1].regex != NULL) {
sprintf(buf,"sensor_%.2d_regex", i);
if ((result = json_cfg_element(buffer+length, size-length, buf, &aq_data->sensors[i-1].regex, CFG_STRING, 0, NULL, CFG_GRP_ADVANCED)) <= 0)
return length;
else
length += result;
}
*/
length += sprintf(buffer+length, "}" );
if (delectCharAt != 0) {
buffer[delectCharAt] = ' ';
@ -1485,7 +1542,7 @@ int build_aqualink_config_JSON(char* buffer, int size, struct aqualinkdata *aq_d
} else if (isPLIGHT(aq_data->aqbuttons[i].special_mask)) {
if (((clight_detail *)aq_data->aqbuttons[i].special_mask_ptr)->lightType > 0) {
if (((clight_detail *)aq_data->aqbuttons[i].special_mask_ptr)->lightType >= 0) {
sprintf(buf,"%s_lightMode", prefix);
if ((result = json_cfg_element(buffer+length, size-length, buf, &((clight_detail *)aq_data->aqbuttons[i].special_mask_ptr)->lightType, CFG_INT, 0, NULL, 0)) <= 0) {
LOG(NET_LOG,LOG_ERR, "Config json buffer full in, result truncated! size=%d curently used=%d\n",size,length);

View File

@ -221,14 +221,20 @@ sd_journal *open_journal() {
return journal;
}
void find_aqualinkd_startupmsg(sd_journal *journal)
void find_aqualinkd_startupmsg(sd_journal *journal, int fallbacklines)
{
static bool once=false;
const void *log;
size_t len;
if (fallbacklines == 0) {
return;
}
// Only going to do this one time, incase re reset while reading.
if (once) {
// Simply go back number of lines since we have already gone back to startup message
sd_journal_previous_skip(journal, fallbacklines);
return;
}
once=true;
@ -276,8 +282,8 @@ bool _broadcast_systemd_logmessages(bool aqMgrActive, bool reOpenStaleConnection
} else {
sd_journal_close(journal);
active = false;
return true;
cursor = NULL;
return true;
}
}
// aqManager is active
@ -299,8 +305,9 @@ bool _broadcast_systemd_logmessages(bool aqMgrActive, bool reOpenStaleConnection
if (cursor != NULL) {
sd_journal_seek_cursor(journal, cursor);
sd_journal_next(journal);
} else
find_aqualinkd_startupmsg(journal);
} else {
find_aqualinkd_startupmsg(journal, (reOpenStaleConnection?0:10) );
}
active = true;
}
@ -1020,7 +1027,7 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
for (i=0; i < _aqualink_data->num_sensors; i++) {
if ( _aqualink_data->sensors[i].value != TEMP_UNKNOWN && _last_mqtt_aqualinkdata.sensors[i].value != _aqualink_data->sensors[i].value) {
char topic[50];
sprintf(topic, "%s/%s", SENSOR_TOPIC, _aqualink_data->sensors[i].label);
sprintf(topic, "%s/%s", SENSOR_TOPIC, _aqualink_data->sensors[i].ID);
send_mqtt_float_msg(nc, topic, _aqualink_data->sensors[i].value);
_last_mqtt_aqualinkdata.sensors[i].value = _aqualink_data->sensors[i].value;
}
@ -1028,7 +1035,7 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
}
typedef enum {uActioned, uBad, uDevices, uStatus, uHomebridge, uDynamicconf, uDebugStatus, uDebugDownload, uSimulator, uSchedules, uSetSchedules, uAQmanager, uLogDownload, uNotAvailable, uConfig, uSaveConfig} uriAtype;
typedef enum {uActioned, uBad, uDevices, uStatus, uHomebridge, uDynamicconf, uDebugStatus, uDebugDownload, uSimulator, uSchedules, uSetSchedules, uAQmanager, uLogDownload, uNotAvailable, uConfig, uSaveConfig, uConfigDownload} uriAtype;
//typedef enum {NET_MQTT=0, NET_API, NET_WS, DZ_MQTT} netRequest;
const char actionName[][5] = {"MQTT", "API", "WS", "DZ"};
@ -1117,6 +1124,8 @@ uriAtype action_URI(request_source from, const char *URI, int uri_length, float
return uSetSchedules;
} else if (strncmp(ri1, "schedules", 9) == 0) {
return uSchedules;
} else if (strncmp(ri1, "config/download", 10) == 0) {
return uConfigDownload;
} else if (strncmp(ri1, "config/set", 10) == 0) {
return uSaveConfig;
} else if (strncmp(ri1, "config", 6) == 0) {
@ -1173,14 +1182,8 @@ uriAtype action_URI(request_source from, const char *URI, int uri_length, float
}
return uAQmanager; // Want to resent updated status
} else if (strncmp(ri1, "logfile", 7) == 0) {
/*
if (ri2 != NULL && strncmp(ri2, "start", 5) == 0) {
startInlineLog2File();
} else if (ri2 != NULL && strncmp(ri2, "stop", 4) == 0) {
stopInlineLog2File();
} else if (ri2 != NULL && strncmp(ri2, "clean", 5) == 0) {
cleanInlineLog2File();
} else*/ if (ri2 != NULL && strncmp(ri2, "download", 8) == 0) {
if (ri2 != NULL && strncmp(ri2, "download", 8) == 0) {
LOG(NET_LOG,LOG_INFO, "Received download log request!\n");
return uLogDownload;
}
return uAQmanager; // Want to resent updated status
@ -1344,6 +1347,8 @@ uriAtype action_URI(request_source from, const char *URI, int uri_length, float
*rtnmsg = NO_PLIGHT_DEVICE;
LOG(NET_LOG,LOG_WARNING, "%s: Didn't find device that matched URI '%.*s'\n",actionName[from], uri_length, URI);
rtn = uBad;
} else {
rtn = uActioned;
}
} else if ((ri3 != NULL && (strncasecmp(ri2, "brightness", 10) == 0) && (strncasecmp(ri3, "set", 3) == 0))) {
found = false;
@ -1360,6 +1365,8 @@ uriAtype action_URI(request_source from, const char *URI, int uri_length, float
*rtnmsg = NO_PLIGHT_DEVICE;
LOG(NET_LOG,LOG_WARNING, "%s: Didn't find device that matched URI '%.*s'\n",actionName[from], uri_length, URI);
rtn = uBad;
} else {
rtn = uActioned;
}
// Action a pump RPM/GPM message
} else if ((ri3 != NULL && ((strncasecmp(ri2, "RPM", 3) == 0) || (strncasecmp(ri2, "GPM", 3) == 0) || (strncasecmp(ri2, "Speed", 5) == 0) || (strncasecmp(ri2, "VSP", 3) == 0)) && (strncasecmp(ri3, "set", 3) == 0))) {
@ -1787,6 +1794,11 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg)
remove("/dev/shm/aqualinkd.log");
}
break;
case uConfigDownload:
LOG(NET_LOG, LOG_DEBUG, "Downloading config\n");
mg_http_serve_file(nc, http_msg, _aqconfig_.config_file, mg_mk_str("text/plain"), mg_mk_str("Content-Disposition: attachment; filename=\"aqualinkd.conf\""));
break;
#endif
case uBad:
default:

View File

@ -300,13 +300,13 @@ bool log_panelversion(struct aqualinkdata *aq_data)
if (strlen(aq_data->version) > 0) {
// If another protocol set the version, we need to check the rev.
if (!revTest){
LOG(ONET_LOG,LOG_NOTICE, "Control Panel revision %s\n", aq_data->revision);
//LOG(ONET_LOG,LOG_NOTICE, "Control Panel revision %s\n", aq_data->revision);
if ( strcmp(aq_data->revision, "O.1") == 0 || strcmp(aq_data->revision, "O.2") == 0 ) {
LOG(ONET_LOG,LOG_NOTICE, "Setting early version for OneTouch\n");
_panel_version_P2 = true;
revTest = true;
}
LOG(ONET_LOG,LOG_NOTICE, "Control Panel revision %s\n", aq_data->revision);
//LOG(ONET_LOG,LOG_NOTICE, "Control Panel revision %s\n", aq_data->revision);
}
return false;
}

View File

@ -15,8 +15,8 @@ static bool _logfile_packets = false;
//static bool _includePentair = false;
//static unsigned char _lastReadFrom = NUL;
void _logPacket(logmask_t from, unsigned char *packet_buffer, int packet_length, bool error, bool force, bool is_read);
int _beautifyPacket(char *buff, int buff_size, unsigned char *packet_buffer, int packet_length, bool error, bool is_read);
void _logPacket(logmask_t from, const unsigned char *packet_buffer, int packet_length, bool error, bool force, bool is_read);
int _beautifyPacket(char *buff, int buff_size, const unsigned char *packet_buffer, int packet_length, bool error, bool is_read);
//void startPacketLogger(bool debug_RSProtocol_packets) {
void startPacketLogger() {
@ -56,7 +56,7 @@ void writePacketLog(char *buffer) {
}
// Log Raw Bytes
void logPacketByte(unsigned char *byte)
void logPacketByte(const unsigned char *byte)
{
if (!_logfile_raw)
return;
@ -77,24 +77,24 @@ void logPacket(unsigned char *packet_buffer, int packet_length) {
_logPacket(RSSD_LOG, packet_buffer, packet_length, false, false);
}
*/
void logPacketRead(unsigned char *packet_buffer, int packet_length) {
void logPacketRead(const unsigned char *packet_buffer, int packet_length) {
_logPacket(RSSD_LOG, packet_buffer, packet_length, false, false, true);
}
void logPacketWrite(unsigned char *packet_buffer, int packet_length) {
void logPacketWrite(const unsigned char *packet_buffer, int packet_length) {
_logPacket(RSSD_LOG, packet_buffer, packet_length, false, false, false);
}
void logPacketError(unsigned char *packet_buffer, int packet_length) {
void logPacketError(const unsigned char *packet_buffer, int packet_length) {
_logPacket(RSSD_LOG, packet_buffer, packet_length, true, false, true);
}
/* This should never be used in production */
void debuglogPacket(logmask_t from, unsigned char *packet_buffer, int packet_length, bool is_read, bool forcelog) {
void debuglogPacket(logmask_t from, const unsigned char *packet_buffer, int packet_length, bool is_read, bool forcelog) {
if ( forcelog == true || getLogLevel(from) >= LOG_DEBUG )
_logPacket(from, packet_buffer, packet_length, false, forcelog, is_read);
}
void logPacket(logmask_t from, int level, unsigned char *packet_buffer, int packet_length, bool is_read) {
void logPacket(logmask_t from, int level, const unsigned char *packet_buffer, int packet_length, bool is_read) {
if ( getLogLevel(from) >= level )
_logPacket(from, packet_buffer, packet_length, false, false, is_read);
}
@ -108,7 +108,7 @@ bool RSSD_LOG_filter_match(unsigned char ID) {
return false;
}
void _logPacket(logmask_t from, unsigned char *packet_buffer, int packet_length, bool error, bool force, bool is_read)
void _logPacket(logmask_t from, const unsigned char *packet_buffer, int packet_length, bool error, bool force, bool is_read)
{
static unsigned char lastPacketTo = NUL;
@ -164,13 +164,13 @@ void _logPacket(logmask_t from, unsigned char *packet_buffer, int packet_length,
}
}
int beautifyPacket(char *buff, int buff_size, unsigned char *packet_buffer, int packet_length, bool is_read)
int beautifyPacket(char *buff, int buff_size, const unsigned char *packet_buffer, int packet_length, bool is_read)
{
return _beautifyPacket(buff, buff_size, packet_buffer, packet_length, false, is_read);
}
int _beautifyPacket(char *buff, int buff_size, unsigned char *packet_buffer, int packet_length, bool error, bool is_read)
int _beautifyPacket(char *buff, int buff_size, const unsigned char *packet_buffer, int packet_length, bool error, bool is_read)
{
int i = 0;
//int i = 0;
int cnt = 0;
protocolType ptype = getProtocolType(packet_buffer);
@ -188,6 +188,27 @@ int _beautifyPacket(char *buff, int buff_size, unsigned char *packet_buffer, int
cnt = sprintf(buff, "%5.5s %sTo 0x%02hhx of type %16.16s | HEX: ",(is_read?"Read":"Write"),(error?"BAD PACKET ":""), packet_buffer[PKT_DEST], get_packet_type(packet_buffer, packet_length));
}
*/
return cnt + ( sprintFrame( (buff + cnt), (buff_size - cnt), packet_buffer, packet_length));
/*
for (i = 0; i < packet_length; i++) {
// Check we have enough space for next set of chars
if ( (cnt + 6) > buff_size)
break;
cnt += sprintf(buff + cnt, "0x%02hhx|", packet_buffer[i]);
}
cnt += sprintf(buff + cnt, "\n");
return cnt;*/
}
int sprintFrame(char *buff, int buff_size, const unsigned char *packet_buffer, int packet_length)
{
int i = 0;
int cnt = 0;
for (i = 0; i < packet_length; i++) {
// Check we have enough space for next set of chars
if ( (cnt + 6) > buff_size)

View File

@ -16,15 +16,17 @@ void startPacketLogging(bool debug_protocol_packets, bool debug_raw_bytes); // S
void stopPacketLogger();
//void logPacket(unsigned char *packet_buffer, int packet_length, bool checksumerror);
//void logPacket(unsigned char *packet_buffer, int packet_length);
void logPacketRead(unsigned char *packet_buffer, int packet_length);
void logPacketWrite(unsigned char *packet_buffer, int packet_length);
void logPacketError(unsigned char *packet_buffer, int packet_length);
void logPacketByte(unsigned char *byte);
void logPacket(logmask_t from, int level, unsigned char *packet_buffer, int packet_length, bool is_read) ;
int beautifyPacket(char *buff, int buff_size, unsigned char *packet_buffer, int packet_length, bool is_read);
void logPacketRead(const unsigned char *packet_buffer, int packet_length);
void logPacketWrite(const unsigned char *packet_buffer, int packet_length);
void logPacketError(const unsigned char *packet_buffer, int packet_length);
void logPacketByte(const unsigned char *byte);
void logPacket(logmask_t from, int level, const unsigned char *packet_buffer, int packet_length, bool is_read) ;
int beautifyPacket(char *buff, int buff_size, const unsigned char *packet_buffer, int packet_length, bool is_read);
int sprintFrame(char *buff, int buff_size, const unsigned char *packet_buffer, int packet_length);
// Only use for manual debugging
void debuglogPacket(logmask_t from, unsigned char *packet_buffer, int packet_length, bool is_read, bool forcelog);
void debuglogPacket(logmask_t from, const unsigned char *packet_buffer, int packet_length, bool is_read, bool forcelog);
#endif //PACKETLOGGER_H_

View File

@ -43,6 +43,7 @@ static struct aqualinkdata *_aqualink_data;
static unsigned char _last_packet_type;
static unsigned long _pda_loop_cnt = -0;
static bool _initWithRS = false;
static bool _first_status_after_clear = false;
// Each RS message is around 0.25 seconds apart
//#define PDA_SLEEP_FOR 120 //
@ -169,6 +170,10 @@ void set_pda_led(struct aqualinkled *led, char state)
{
led->state = FLASH;
}
else if (state == '%')
{
led->state = ON;
}
else
{
led->state = OFF;
@ -252,7 +257,10 @@ void process_pda_packet_msg_long_temp(const char *msg)
// 'AIR '
// ' 86` '
// 'AIR WATER' // In case of single device.
_aqualink_data->temp_units = FAHRENHEIT; // Force FAHRENHEIT
if (_aqualink_data->temp_units == UNKNOWN && !in_programming_mode(_aqualink_data)) {
LOG(PDA_LOG,LOG_NOTICE, "Forcing temperature units to FAHRENHEIT\n");
_aqualink_data->temp_units = FAHRENHEIT; // Force FAHRENHEIT
}
if (stristr(pda_m_line(1), "AIR") != NULL)
_aqualink_data->air_temp = atoi(msg);
@ -407,6 +415,27 @@ void process_pda_packet_msg_long_set_time(const char *msg)
*/
}
//
//PDA Line 2 = POOL HEAT 70`F
//PDA Line 3 = SPA HEAT 98`F
// temp units are last char as above.
void get_pda_temp_units(const char *msg)
{
if (_aqualink_data->temp_units != UNKNOWN) {
return;
}
if (msg[15] == 'F') {
_aqualink_data->temp_units = FAHRENHEIT;
LOG(PDA_LOG,LOG_DEBUG, "Set temperature units to FAHRENHEIT\n");
} else if (msg[15] == 'C') {
_aqualink_data->temp_units = CELSIUS;
LOG(PDA_LOG,LOG_DEBUG, "Set temperature units to CELSIUS\n");
} else {
LOG(PDA_LOG,LOG_DEBUG, "Unknown temperature units '%c'\n",msg[15]);
}
}
void process_pda_packet_msg_long_set_temp(const char *msg)
{
LOG(PDA_LOG,LOG_DEBUG, "process_pda_packet_msg_long_set_temp\n");
@ -415,23 +444,27 @@ void process_pda_packet_msg_long_set_temp(const char *msg)
{
_aqualink_data->pool_htr_set_point = atoi(msg + 10);
LOG(PDA_LOG,LOG_DEBUG, "pool_htr_set_point = %d\n", _aqualink_data->pool_htr_set_point);
get_pda_temp_units(msg);
}
else if (stristr(msg, "SPA HEAT") != NULL)
{
_aqualink_data->spa_htr_set_point = atoi(msg + 10);
LOG(PDA_LOG,LOG_DEBUG, "spa_htr_set_point = %d\n", _aqualink_data->spa_htr_set_point);
get_pda_temp_units(msg);
}
else if (stristr(msg, "TEMP1") != NULL)
{
setSingleDeviceMode();
_aqualink_data->pool_htr_set_point = atoi(msg + 10);
LOG(PDA_LOG,LOG_DEBUG, "pool_htr_set_point = %d\n", _aqualink_data->pool_htr_set_point);
get_pda_temp_units(msg);
}
else if (stristr(msg, "TEMP2") != NULL)
{
setSingleDeviceMode();
_aqualink_data->spa_htr_set_point = atoi(msg + 10);
LOG(PDA_LOG,LOG_DEBUG, "spa_htr_set_point = %d\n", _aqualink_data->spa_htr_set_point);
get_pda_temp_units(msg);
}
@ -518,7 +551,7 @@ void process_pda_packet_msg_long_unknown(const char *msg)
// Lets make a guess here and just see if there is an ON/OFF/ENA/*** at the end of the line
// When you turn on/off a piece of equiptment, a clear screen followed by single message is sent.
// So we are not in any PDA menu, try to catch that message here so we catch new device state ASAP.
if (msg[AQ_MSGLEN - 1] == 'N' || msg[AQ_MSGLEN - 1] == 'F' || msg[AQ_MSGLEN - 1] == 'A' || msg[AQ_MSGLEN - 1] == '*')
if (msg[AQ_MSGLEN - 1] == 'N' || msg[AQ_MSGLEN - 1] == 'F' || msg[AQ_MSGLEN - 1] == 'A' || msg[AQ_MSGLEN - 1] == '*' || msg[AQ_MSGLEN - 1] == '%')
{
for (i = 0; i < _aqualink_data->total_buttons; i++)
{
@ -875,6 +908,7 @@ bool process_pda_packet(unsigned char *packet, int length)
case CMD_PDA_CLEAR:
read_equiptment_menu = false; // Reset the have read menu flag, since this is new menu.
_first_status_after_clear = true;
break;
case CMD_STATUS:
@ -911,6 +945,11 @@ bool process_pda_packet(unsigned char *packet, int length)
//printf("**** PDA INIT PUT BACK IN ****\n");
queueGetProgramData(AQUAPDA, _aqualink_data);
}
else if (_first_status_after_clear && (pda_m_type() == PM_FREEZE_PROTECT_DEVICES))
{
process_pda_freeze_protect_devices();
}
_first_status_after_clear = false;
break;
case CMD_MSG_LONG:

View File

@ -15,6 +15,7 @@
* https://github.com/sfeakes/aqualinkd
*/
#define _GNU_SOURCE 1 // for strcasestr & strptime
#include <stdio.h>
#include <stdarg.h>
@ -34,7 +35,7 @@
#include "config.h"
#include "aq_panel.h"
#include "rs_msg_utils.h"
#include "color_lights.h"
#ifdef AQ_DEBUG
#include "timespec_subtract.h"
@ -347,7 +348,8 @@ bool find_pda_menu_item(struct aqualinkdata *aq_data, char *menuText, int charli
// Line 9 = BOOST
// "SET AquaPure" and "BOOST" are only present when filter pump is running
if (strncasecmp(pda_m_line(9)," BOOST ", 16) == 0) {
if ((strncasecmp(pda_m_line(9)," BOOST ", 16) == 0) ||
(strncasecmp(pda_m_line(9)," BOOST POOL ", 16) == 0)) {
min_index = 1;
max_index = 8; // to account for 8 missing
if (index == 9) { // looking for boost
@ -657,26 +659,37 @@ void *set_aqualink_PDA_device_on_off( void *ptr )
// NSF Added this since DEBUG hitting wrong command
//waitfor_pda_queue2empty();
if ( find_pda_menu_item(aq_data, device_name, 13) ) {
if ( find_pda_menu_item(aq_data, device_name, 12) ) { // Remove 1 char to account for '100%' (4 chars not the usual 3)
if (aq_data->aqbuttons[device].led->state != state) {
//printf("*** Select State ***\n");
LOG(PDA_LOG,LOG_INFO, "PDA Device On/Off, found device '%s', changing state\n",aq_data->aqbuttons[device].label,state);
LOG(PDA_LOG,LOG_INFO, "PDA Device On/Off, found device '%s', changing state\n",aq_data->aqbuttons[device].label);
force_queue_delete(); // NSF This is a bad thing to do. Need to fix this
send_pda_cmd(KEY_PDA_SELECT);
while (get_pda_queue_length() > 0) { delay(500); }
// If you are turning on a heater there will be a sub menu to set temp
if ((state == ON) && ((device == aq_data->pool_heater_index) || (device == aq_data->spa_heater_index))) {
if (! waitForPDAnextMenu(aq_data)) {
LOG(PDA_LOG,LOG_ERR, "PDA Device On/Off: %s on - waitForPDAnextMenu\n",
aq_data->aqbuttons[device].label);
} else {
send_pda_cmd(KEY_PDA_SELECT);
while (get_pda_queue_length() > 0) { delay(500); }
if (!waitForPDAMessageType(aq_data,CMD_PDA_HIGHLIGHT,20)) {
LOG(PDA_LOG,LOG_ERR, "PDA Device On/Off: %s on - wait for CMD_PDA_HIGHLIGHT\n",
aq_data->aqbuttons[device].label);
}
if (! waitForPDAnextMenu(aq_data)) {
LOG(PDA_LOG,LOG_ERR, "PDA Device On/Off: %s on - waitForPDAnextMenu\n", aq_data->aqbuttons[device].label);
} else {
send_pda_cmd(KEY_PDA_SELECT);
while (get_pda_queue_length() > 0) { delay(500); }
if (!waitForPDAMessageType(aq_data,CMD_PDA_HIGHLIGHT,20)) {
LOG(PDA_LOG,LOG_ERR, "PDA Device On/Off: %s on - wait for CMD_PDA_HIGHLIGHT\n",aq_data->aqbuttons[device].label);
}
}
} else if ( isPLIGHT(aq_data->aqbuttons[device].special_mask) ) {
// THIS EXTRA ENTER IS ONLY FOR ON, NOT OFF
// PDA Menu Line 0 = Set Color // for color light
// PDA Menu Line 0 = Set // for dimmer light
if ( state == ON ) {
waitForPDAMessageTypes(aq_data,CMD_PDA_HIGHLIGHT,CMD_PDA_HIGHLIGHTCHARS,5);
if (strncasecmp(pda_m_line(0),"Set", 3) == 0) {
LOG(PDA_LOG,LOG_DEBUG, "PDA Device On/Off, '%s' is programmable light, but no mode using default\n",aq_data->aqbuttons[device].label);
send_pda_cmd(KEY_PDA_SELECT);
} else {
LOG(PDA_LOG,LOG_ERR, "PDA Device On/Off: expected Set menu for programmable light '%s', not found\n",aq_data->aqbuttons[device].label);
}
}
} else { // not turning on heater wait for line update
// worst case spa when pool is running
if (!waitForPDAMessageType(aq_data,CMD_MSG_LONG,2)) {
@ -700,6 +713,100 @@ void *set_aqualink_PDA_device_on_off( void *ptr )
}
void *set_aqualink_PDA_light_mode( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
//int i=0;
//int found;
//char device_name[15];
waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_SET_LIGHT_MODE);
char *buf = (char*)threadCtrl->thread_args;
const char *mode_name = NULL;
int val = atoi(&buf[0]);
int btn = atoi(&buf[5]);
int typ = atoi(&buf[10]);
bool use_current_mode = false;
if (btn < 0 || btn >= aq_data->total_buttons ) {
LOG(PDA_LOG, LOG_ERR, "Can't program light mode on button %d\n", btn);
cleanAndTerminateThread(threadCtrl);
return ptr;
}
aqkey *button = &aq_data->aqbuttons[btn];
if ( ! isPLIGHT(button->special_mask) ) {
LOG(PDA_LOG, LOG_ERR, "Can't program light mode on button %d, it's not a programmable light\n", btn);
cleanAndTerminateThread(threadCtrl);
return ptr;
}
clight_type lighttype = ((clight_detail *)button->special_mask_ptr)->lightType;
if (val <= 0) {
use_current_mode = true;
LOG(PDA_LOG, LOG_INFO, "PDA Light Programming #: %d, on button: %s, color light type: %d, using current mode\n", val, button->label, typ);
} else {
if (lighttype == LC_DIMMER2 || LC_DIMMER2 == LC_DIMMER) {
mode_name = light_mode_name(typ, val-1, AQUAPDA);
} else {
mode_name = light_mode_name(typ, val, AQUAPDA);
}
use_current_mode = false;
if (mode_name == NULL) {
LOG(PDA_LOG, LOG_ERR, "PDA Light Programming #: %d, on button: %s, color light type: %d, couldn't find mode name '%s'\n", val, button->label, typ, mode_name);
cleanAndTerminateThread(threadCtrl);
return ptr;
} else {
LOG(PDA_LOG, LOG_INFO, "PDA Light Programming #: %d, on button: %s, color light type: %d, name '%s'\n", val, button->label, typ, mode_name);
}
}
if (! goto_pda_menu(aq_data, PM_EQUIPTMENT_CONTROL)) {
LOG(PDA_LOG,LOG_ERR, "PDA light Programming :- can't find EQUIPTMENT CONTROL menu\n");
cleanAndTerminateThread(threadCtrl);
return ptr;
}
if ( find_pda_menu_item(aq_data, button->label, 0) ) { // Remove 1 char to account for '100%' (4 chars not the usual 3)
LOG(PDA_LOG,LOG_INFO, "PDA Light Programming, found device '%s', changing to '%s'\n",button->label,mode_name);
force_queue_delete(); // NSF This is a bad thing to do. Need to fix this
send_pda_cmd(KEY_PDA_SELECT);
waitForPDAMessageTypes(aq_data,CMD_PDA_HIGHLIGHT,CMD_PDA_HIGHLIGHTCHARS,15);
// if we get `PDA Menu Line 3 = Light will turn ` light is on and we need to press enter again.
// if we get `PDA Menu Line 2 = Dimmer Power ` we need to cycle over 25%/50%/75%/100%
// if we get `Menu Line 0 = Set Color` we can set color.
if (lighttype == LC_DIMMER2 || LC_DIMMER2 == LC_DIMMER) {
} else {
if (strncasecmp(pda_m_line(3),"Light will turn", 15) == 0) {
send_pda_cmd(KEY_PDA_SELECT);
waitForPDAMessageTypes(aq_data,CMD_PDA_HIGHLIGHT,CMD_PDA_HIGHLIGHTCHARS,5);
}
if (use_current_mode && mode_name != NULL) {
if (find_pda_menu_item(aq_data,(char *)mode_name,strlen(mode_name))) {
send_pda_cmd(KEY_PDA_SELECT);
} else {
LOG(PDA_LOG,LOG_ERR, "PDA Light Programming, could find mode '%s' for device '%s'\n",mode_name,button->label);
}
}
}
} else {
LOG(PDA_LOG,LOG_ERR, "PDA Light Programming, device '%s' not found\n",button->label);
}
cleanAndTerminateThread(threadCtrl);
// just stop compiler error, ptr is not valid as it's just been freed
return ptr;
}
void *get_aqualink_PDA_device_status( void *ptr )
{
@ -753,8 +860,11 @@ void *set_aqualink_PDA_init( void *ptr )
//printf("****** Version '%s' ********\n",aq_data->version);
LOG(PDA_LOG,LOG_DEBUG, "PDA type=%d, version=%s\n", _PDA_Type, aq_data->version);
// don't wait for version menu to time out press back to get to home menu faster
//send_pda_cmd(KEY_PDA_BACK);
send_pda_cmd(KEY_PDA_BACK);
//if (! waitForPDAnextMenu(aq_data)) { // waitForPDAnextMenu waits for highlight chars, which we don't get on normal menu
if (! waitForPDAMessageType(aq_data,CMD_PDA_CLEAR,10)) {

View File

@ -43,6 +43,8 @@ void *set_PDA_aqualink_boost(void *ptr);
//bool set_PDA_aqualink_time(struct aqualinkdata *aq_data);
void *set_PDA_aqualink_time( void *ptr );
void *set_aqualink_PDA_light_mode( void *ptr );
/*
// These are from aq_programmer.c , exposed here for PDA AQ PROGRAMMER
void send_cmd(unsigned char cmd);

View File

@ -2,9 +2,31 @@
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>
//#include <errno.h>
#include <string.h>
#include <regex.h>
#include <fcntl.h>
#include <unistd.h>
#include "sensors.h"
#include "aqualink.h"
//#include "utils.h"
#include "sensors.h"
/*
----------
GPIO and thermal sensors are simply read value in file
----------
1Wire is similar to
/sys/bus/w1/devices/28-011564e2feff/w1_slave
90 01 4b 46 7f ff 0c 10 33 : crc=33 YES
90 01 4b 46 7f ff 0c 10 33 t=25000
read the t= value.
regex would be something like .*t=([0-9|\.]*)
-----------
*/
/*
@ -13,7 +35,152 @@
return true if current reading is different to last value stored
*/
struct sensorthread {
pthread_t thread_id;
pthread_mutex_t thread_mutex;
pthread_cond_t thread_cond;
struct aqualinkdata *aq_data;
struct timespec timeout;
};
struct sensorthread *_sthread = NULL;
void *sensors_worker( void *ptr );
void stop_sensors_thread() {
LOG(AQUA_LOG, LOG_INFO, "Stopping sensor thread\n");
pthread_cond_broadcast(&_sthread->thread_cond);
}
void start_sensors_thread(struct aqualinkdata *aq_data) {
_sthread = calloc(1, sizeof(struct sensorthread));
_sthread->aq_data = aq_data;
_sthread->thread_id = 0;
if( pthread_create( &_sthread->thread_id , NULL , sensors_worker, (void*)_sthread) < 0) {
LOG(AQUA_LOG, LOG_ERR, "could not create sensors thread\n");
free(_sthread);
return;
}
if ( _sthread->thread_id != 0 ) {
pthread_detach(_sthread->thread_id);
}
}
void *sensors_worker( void *ptr )
{
struct sensorthread *sthread;
sthread = (struct sensorthread *) ptr;
int retval = 0;
LOG(AQUA_LOG, LOG_NOTICE, "Started sensor thread\n");
pthread_mutex_lock(&sthread->thread_mutex);
do {
if (retval != 0 && retval != ETIMEDOUT) {
LOG(AQUA_LOG, LOG_ERR, "Sensor thread pthread_cond_timedwait failed for error %d %s\n",retval,strerror(retval));
break;
}
for (int i=0; i < sthread->aq_data->num_sensors; i++) {
//LOG(AQUA_LOG, LOG_DEBUG, "Sensor thread reading %s\n",sthread->aq_data->sensors[i].label);
if (read_sensor(&sthread->aq_data->sensors[i]) ) {
sthread->aq_data->updated = true;
}
}
clock_gettime(CLOCK_REALTIME, &sthread->timeout);
sthread->timeout.tv_sec += _aqconfig_.sensor_poll_time;
//sthread->started_at = time(0);
//LOG(AQUA_LOG, LOG_DEBUG, "Sensor thread will sleep for %d seconds\n",_aqconfig_.sensor_poll_time);
} while ((retval = pthread_cond_timedwait(&sthread->thread_cond, &sthread->thread_mutex, &sthread->timeout)) == ETIMEDOUT);
pthread_mutex_unlock(&sthread->thread_mutex);
LOG(AQUA_LOG, LOG_DEBUG, "End sensor thread\n");
free(sthread);
pthread_exit(0);
}
#define READ_BUFFER_SIZE 256
bool read_sensor(external_sensor *sensor) {
int fp;
float value = 0.0;
char buffer[READ_BUFFER_SIZE];
char *startptr = &buffer[0];
char *endptr;
fp = open(sensor->path, O_RDONLY, 0);
if (fp < 0) {
LOGSystemError(errno, AQUA_LOG, sensor->path);
LOG(AQUA_LOG,LOG_ERR, "Reading sensor %s %s\n",sensor->label, sensor->path);
return FALSE;
}
// Read the sensor
if ( read(fp, buffer, READ_BUFFER_SIZE) <= 0 ) {
LOG(AQUA_LOG,LOG_ERR, "Reading value from sensor %s %s\n",sensor->label, sensor->path);
close(fp);
return FALSE;
}
close(fp);
// If regex pass that
if (sensor->regex != NULL) {
regex_t preg;
regmatch_t pmatch[2];
//const char *pattern = ".*t=([0-9|\\.]*)";
//const char *pattern = ".*([0-9]+).*";
int status;
// Compile the regular expression
status = regcomp(&preg, sensor->regex, REG_EXTENDED);
if (status != 0) {
LOG(AQUA_LOG,LOG_ERR, "Compiling sensor regex %s\n",sensor->regex);
return 1;
}
// Run regex
if ( (status = regexec(&preg, buffer, 2, pmatch, 0)) == 0) {
startptr = buffer + pmatch[1].rm_so;
} else if (status == REG_NOMATCH) {
//LOG(AQUA_LOG,LOG_DEBUG, "No sensor regex match '%s' on line '%s'\n",sensor->regex,line_buffer);
} else {
LOG(AQUA_LOG,LOG_ERR, "regex match error %d using '%s' on line '%s'\n",status,sensor->regex,buffer);
}
// Free the compiled regular expression
regfree(&preg);
}
// Convert value to float
value = strtof(startptr, &endptr);
if (endptr == buffer) {
LOG(AQUA_LOG,LOG_ERR, "Reading sensor value from %s\n", sensor->path);
}
value = value * sensor->factor;
LOG(AQUA_LOG,LOG_DEBUG, "Read sensor %s value=%.2f\n",sensor->label, value);
if (sensor->value != value) {
sensor->value = value;
return TRUE;
}
return FALSE;
}
#ifdef DO_NOT_COMPILE
/* TRY not to use this unless have too, unbuffered is far quicker for sysfs */
bool read_sensor_buffered(external_sensor *sensor) {
FILE *fp;
float value;
@ -25,12 +192,37 @@ bool read_sensor(external_sensor *sensor) {
return FALSE;
}
fscanf(fp, "%f", &value);
if (sensor->regex != NULL) {
char line_buffer[256];
regex_t preg;
regmatch_t pmatch[2];
int status;
// Compile the regular expression
status = regcomp(&preg, sensor->regex, REG_EXTENDED);
if (status != 0) {
LOG(AQUA_LOG,LOG_ERR, "Compiling sensor regex %s\n",sensor->regex);
return 1;
}
while (fgets(line_buffer, sizeof(line_buffer), fp) != NULL) {
if ( (status = regexec(&preg, line_buffer, 2, pmatch, 0)) == 0) {
value = atof(line_buffer + pmatch[1].rm_so);
break;
} else if (status == REG_NOMATCH) {
//LOG(AQUA_LOG,LOG_DEBUG, "No sensor regex match '%s' on line '%s'\n",sensor->regex,line_buffer);
} else {
LOG(AQUA_LOG,LOG_ERR, "regex match error %d using '%s' on line '%s'\n",status,sensor->regex,line_buffer);
}
}
// Free the compiled regular expression
regfree(&preg);
} else {
fscanf(fp, "%f", &value);
}
fclose(fp);
// Convert usually from millidegrees Celsius to degrees Celsius
//printf("Factor = %f - value %f\n",sensor->factor, value);
//printf("Read Sensor value %f %.2f\n",value,value * sensor->factor);
value = value * sensor->factor;
//printf("Converted value %f\n",value);
LOG(AQUA_LOG,LOG_DEBUG, "Read sensor %s value=%.2f\n",sensor->label, value);
@ -41,4 +233,6 @@ bool read_sensor(external_sensor *sensor) {
}
return FALSE;
}
}
#endif

View File

@ -3,13 +3,20 @@
#include <stdbool.h>
#include "aqualink.h"
typedef struct external_sensor{
char *path;
float factor;
char *label;
float value;
char ID[7];
char *regex;
char *uom;
} external_sensor;
bool read_sensor(external_sensor *sensor);
void stop_sensors_thread();
void start_sensors_thread(struct aqualinkdata *aq_data);
#endif

View File

@ -163,6 +163,8 @@ int serial_logger (int rs_fd, char *port_name, int logLevel, int slogger_packets
#define HEAT_PUMP " <-- Heat Pump"
#define REM_PWR_CENT " <-- Remote Power Center"
#define JWC_LIGHTS " <-- Jandy WaterColor Lights"
#define UNKNOWN " <-- Unknown Device"
#define P_VSP " <-- Pentair VSP"
@ -225,9 +227,11 @@ const char *getDevice(unsigned char ID) {
if (ID >= 0x28 && ID <= 0x2B)
return REM_PWR_CENT;
if (ID >= 0xE0 && ID <= 0xEF) // this could end at 0xF0
if (ID >= 0xE0 && ID <= 0xEF)
return EPUMP2;
if (ID >= 0xF0 && ID <= 0xF4) // 0xF4 is total guess.
return JWC_LIGHTS;
//if (ID == 0x08)
// return KEYPAD;

View File

@ -363,7 +363,7 @@ bool process_rssadapter_packet(unsigned char *packet, int length, struct aqualin
// CHANGE TO DEBUG BEFORE RELEASE
if (aq_data->lights[i].lightType == LC_DIMMER || aq_data->lights[i].lightType == LC_DIMMER2) {
LOG(RSSA_LOG,LOG_DEBUG,"DimmerLight '%s' is %s 0x%02hhx value '%d'%%\n",
LOG(RSSA_LOG,LOG_DEBUG,"DimmerLight '%s' is %s, rawvalue 0x%02hhx value '%d'%%\n",
aq_data->lights[i].button->label,
packet[6]==0x00?"OFF":"ON",
packet[6],

View File

@ -45,6 +45,7 @@
#include "utils.h"
#include "rs_msg_utils.h"
#include "aq_serial.h"
#include "aqualink.h"
#define DEFAULT_LOG_FILE "/tmp/aqualinkd-inline.log"
//#define MAXCFGLINE 265
@ -109,15 +110,15 @@ int getSystemLogLevel()
int getLogLevel(logmask_t from)
{
// RSSD_LOG should default to INFO unless the mask is explicitly set.
// IE Even if DEBUG is set, (Note ignored for the moment)
//if ( from == RSSD_LOG && ((_logforcemask & from) == from ) && _log_level < LOG_DEBUG_SERIAL)
if ( (from == RSSD_LOG || from == SLOG_LOG) && ((_logforcemask & from) == from ) && _log_level < LOG_DEBUG_SERIAL)
if ( (from == RSSD_LOG || from == SLOG_LOG) && isMASK_SET(_logforcemask, from) && _log_level < LOG_DEBUG_SERIAL) {
return LOG_DEBUG_SERIAL;
else if ( ((_logforcemask & from) == from ) && _log_level < LOG_DEBUG_SERIAL)
} else if ( isMASK_SET(_logforcemask, from) && _log_level < LOG_DEBUG_SERIAL) {
return LOG_DEBUG;
} else if (from == RSTM_LOG && _log_level < LOG_DEBUG_SERIAL && !isMASK_SET(_logforcemask, from)) {
// RSTM_LOG (RS serial timings) should only print if debug_serial or explicitly set in the forcelogmask.
return LOG_NOTICE;
}
return _log_level;
}
@ -593,8 +594,9 @@ void LOG(const logmask_t from, const int msg_level, const char * format, ...)
return;
}
*/
if ( msg_level > getLogLevel(from))
if ( msg_level > getLogLevel(from)) {
return;
}
char buffer[LOGBUFFER];
va_list args;
@ -963,3 +965,61 @@ char *prittyString(char *str)
return str;
}
temperatureUOM getTemperatureUOM(const char *uom) {
if (uom == NULL)
return UNKNOWN;
if (uom[0] == 194 && uom[1] == 176) { // Deg symbol is 2 chars '°'
switch (uom[2]) {
case 'F':
case 'f':
//printf("UOM %s is FAHRENHEIT\n", uom);
return FAHRENHEIT;
break;
case 'C':
case 'c':
//printf("UOM %s is CELSIUS\n", uom);
return CELSIUS;
break;
}
}
//printf("UOM %s is UNKNOWN\n", uom);
return UNKNOWN;
}
bool isUomTemperature( const char *uom) {
/*
for (int i=0; i<strlen(uom); i++) {
printf(" %d |",uom[i]);
}
printf("\n");
*/
if (uom == NULL)
return false;
if (uom[0] == 194 && uom[1] == 176) { // Deg symbol is 2 chars '°'
//printf("UOM %s is temp\n", uom);
return true;
}
if (strlen(uom) == 1) {
switch (uom[0]) {
case 'F':
case 'f':
case 'C':
case 'c':
//printf("UOM %s is temp\n", uom);
return true;
break;
}
}
//printf("UOM %s is NOT temp %d %c\n", uom, uom[0], uom[0]);
return false;
}

View File

@ -3,6 +3,9 @@
#include <stdint.h>
#include <time.h>
//#include "aqualink.h"
#ifndef UTILS_H_
#define UTILS_H_
@ -18,6 +21,12 @@
#define FALSE 0
#endif
typedef enum tempUOM {
FAHRENHEIT,
CELSIUS,
UNKNOWN
} temperatureUOM;
#define LOGBUFFER 256
#define LARGELOGBUFFER 1400 // / Must be at least AQ_MAXPKTLEN * 5 + 100
@ -129,6 +138,10 @@ char *prittyString(char *str);
//void writePacketLog(char *buff);
//void closePacketLog();
float timespec2float(const struct timespec *elapsed);
bool isUomTemperature( const char *uom);
temperatureUOM getTemperatureUOM(const char *uom);
#ifdef AQ_MANAGER
//void startInlineLog2File();

View File

@ -4,4 +4,5 @@
#define AQUALINKD_SHORT_NAME "AqualinkD"
// Use Magor . Minor . Patch
#define AQUALINKD_VERSION "2.6.5"
#define AQUALINKD_VERSION "2.6.9"

View File

@ -18,7 +18,9 @@
--aqualinkd-scale: 0.75;
--aqualinkd-container: 265px;
--aqualinkd-iframe: 350px; /* Seems backwards, but the way the iframe scales the iframe need to be the same amount bigger 350*0.75*/
--grid-width: 300px; /* Width of left column */
--logcontainer-height: 510px; /* This will get changed on page load */
--logcontainer-width: 1040px; /* This will get changed on page load */
}
html {}
@ -33,8 +35,10 @@
.wrapper {
display: grid;
grid-template-columns: 300px 1fr;
grid-gap: 1rem;
/*grid-template-columns: 300px 1fr;*/
grid-template-columns: var(--grid-width) 1fr;
/*grid-gap: 1rem;*/
grid-gap: 0px;
}
.inner {
@ -44,12 +48,16 @@
justify-content: center;
/*border: 1px solid red;*/
align-items: flex-start;
margin-top: 10px;
margin-left: 5px;
margin-right: 0px;
margin-bottom: 0px;
}
.aqualinkd {
grid-column: 1 / -1;
width: 133%;
/*width: 100%;*/
height: var(--aqualinkd-container); /* Iframe is set to 350px in the HTML so scale it back here */
/*transform: scale(0.75);*/
transform: scale(var(--aqualinkd-scale));
@ -73,7 +81,7 @@
align-items: center;
justify-content: center;
/*border: 1px solid red;*/
row-gap: 20px;
/*row-gap: 10px;*/
/*overflow-y: scroll; this only works if we know the height, we can caculate but not important at moment */
}
@ -170,15 +178,25 @@
/*height: 510px;*//* This was size before scaling aqualinkd iframe*/
/*height: 650px;*/
height: var(--logcontainer-height);
width: 100%;
/*width: 100%;*/
/*width: 500px;*/
width: var(--logcontainer-width);
/*width: calc(100% - 300px);*/
overflow: auto;
overflow-x: auto;
display: flex;
flex-direction: column-reverse;
/*flex-direction: column-reverse;*/
flex-direction: column;
background-color: #2b2b2b;
color: white;
white-space: pre;
}
.logline {
margin-left: 2px;
font-size: small;
}
.toggle {
cursor: pointer;
display: inline-block;
@ -351,6 +369,9 @@
_addedBlankLightProgram = false;
_addedBlankSensor = false;
_addedBlankVirtualButton = false;
//var _cfgRestartAlertShown = false;
let _AlertsShown = {};
var _config = {};
@ -428,36 +449,69 @@
return number;
}
var last_message_element = null;
var log_element_count = 0;
var _last_message_element = null;
var _log_element_count = 0;
const LOGS2DISPLAY = 500;
var _log_div_pressed = -1;
function update_log_message(message) {
if (_log_div_pressed == -1) {
document.getElementById("logs").addEventListener("mousedown", function (e) {_log_div_pressed = true;})
document.getElementById("logs").addEventListener("mouseup", function (e) {_log_div_pressed = false;})
_log_div_pressed = false;
}
//console.log(message);
var element = document.createElement("div");
element.classList.add("logline");
if (message.startsWith("Error:")) {
element.classList.add("logmsgerror");
}
var logcontainer = document.getElementById("logs");
if (log_element_count >= LOGS2DISPLAY) {
if (_log_element_count >= LOGS2DISPLAY) {
try {
//console.log("Removing " + logcontainer.lastElementChild.innerHTML);
logcontainer.lastElementChild.remove();
logcontainer.firstElementChild.remove();
} catch (e) {
//console.log("ERROR Removing log '" + logcontainer.lastElementChild.innerHTML + "'");
log_element_count++;
_log_element_count++;
}
} else {
log_element_count++;
_log_element_count++;
}
element.appendChild(document.createTextNode(message));
logcontainer.insertBefore(element, last_message_element);
last_message_element = element;
logcontainer.appendChild(element);
_last_message_element = element;
if (_log_div_pressed == false) {
logcontainer.scrollTop = logcontainer.scrollHeight - logcontainer.clientHeight;
}
}
/* Below needs flex-direction: column-reverse; set in CSS .logcontainer */
/*
function OLD_update_log_message_OLD(message) {
var element = document.createElement("div");
if (message.startsWith("Error:")) {
element.classList.add("logmsgerror");
}
var logcontainer = document.getElementById("logs");
if (_log_element_count >= LOGS2DISPLAY) {
try {
logcontainer.lastElementChild.remove();
} catch (e) {
_log_element_count++;
}
} else {
_log_element_count++;
}
element.appendChild(document.createTextNode(message));
logcontainer.insertBefore(element, _last_message_element);
_last_message_element = element;
}
*/
function update_status(data) {
}
@ -496,43 +550,28 @@
}
send_command(msg);
}
function setlogfile(caller) {
function requestFileDownload(caller) {
var msg = {};
/*
if (caller.id == "logfile") {
var startstop = "";
if (caller.checked) {
startstop = "start";
} else {
startstop = "stop";
}
msg = {
uri: "logfile/" + startstop,
value: 0
};
} else if (caller.id == "cleanlog") {
msg = {
uri: "logfile/clean",
value: 0
};
} else*/
if (caller.id == "downloadlog") {
if (caller.id == "downloadlog") {
//window.location = '/api/debug/download';
downloadFile('/api/logfile/download', document.getElementById("logsize").value);
return;
downloadFile('/api/logfile/download', "aqualinkd.log", document.getElementById("logsize").value);
return;
} else if ( caller.id == "downloadconfig") {
downloadFile('/api/config/download', "aqualinkd.conf");
return;
}
send_command(msg);
}
function downloadFile(filePath, lines) {
function downloadFile(filePath, fname, lines=0) {
//console.log("Lines=" + lines);
var link = document.createElement('a');
link.href = filePath + "/" + lines;
//link.download = filePath.substr(filePath.lastIndexOf('/') + 1);
link.download = "aqualinkd.log"
link.download = fname
link.click();
// Can't get a form post to cleanly download, and "a download" above can only do get. So add value
/*
@ -587,11 +626,15 @@
values: {}
};
for (var obj in _config) {
//console.log(_config);
for (var obj in _config) {
if (_config[obj].value !== undefined) {
if (_config[obj].value) {
json_ordered.values[obj] = _config[obj].value;
} else if (_config[obj].allow_blank !== undefined && _config[obj].allow_blank == "yes") {
//console.log("----- Add Blank value for "+ obj+"\n");
json_ordered.values[obj] = "";
}
} else if (obj.toString().startsWith("button_") || obj.toString().startsWith("virtual_button_") || obj.toString().startsWith("sensor_") ) {
for (var obj1 in _config[obj]) {
@ -670,11 +713,13 @@
if (deletable) {
cell3.innerHTML = '<input type="button" class="config_option_deletebutton" id="delete" key="'+obj.toString()+'" value="X" onclick="deleteCfgOption(this);">';
}
var input;
if (data[obj]["valid values"] !== undefined) {
//console.log(data[obj]["valid values"]);
const input = document.createElement("select");
//const
input = document.createElement("select");
array = data[obj]["valid values"];
for (var i = 0; i < array.length; i++) {
var option = document.createElement("option");
@ -688,24 +733,26 @@
input.appendChild(option);
}
input.addEventListener('change', cfgValueChanged);
if (data[obj].force_restart == "yes") {
input.addEventListener('click', cfgAlertForceRestart);
}
//if (data[obj].force_restart == "yes") {
// input.addEventListener('click', cfgAlertForceRestart);
// }
cell2.appendChild(input);
} else if (data[obj].type == "string") {
const input = document.createElement("input");
//const
input = document.createElement("input");
input.type = "text";
if (data[obj].value != "(null)") {
input.value = data[obj].value;
}
input.setAttribute('key', obj.toString());
input.addEventListener('change', cfgValueChanged);
if (data[obj].force_restart == "yes") {
input.addEventListener('click', cfgAlertForceRestart);
}
//if (data[obj].force_restart == "yes") {
// input.addEventListener('click', cfgAlertForceRestart);
//}
cell2.appendChild(input);
} else if (data[obj].type == "int") {
const input = document.createElement("input");
//const
input = document.createElement("input");
input.type = "number";
if (data[obj].value != -999) {
input.value = data[obj].value;
@ -714,36 +761,52 @@
input.step = 1;
input.setAttribute('key', obj.toString());
input.addEventListener('change', cfgValueChanged);
if (data[obj].force_restart == "yes") {
input.addEventListener('click', cfgAlertForceRestart);
}
//if (data[obj].force_restart == "yes") {
// input.addEventListener('click', cfgAlertForceRestart);
//}
cell2.appendChild(input);
} else if (data[obj].type == "float") {
const input = document.createElement("input");
//const
input = document.createElement("input");
input.type = "number";
input.value = data[obj].value;
input.size = 4;
input.step = 0.01;
input.setAttribute('key', obj.toString());
input.addEventListener('change', cfgValueChanged);
if (data[obj].force_restart == "yes") {
input.addEventListener('click', cfgAlertForceRestart);
}
//if (data[obj].force_restart == "yes") {
// input.addEventListener('click', cfgAlertForceRestart);
//}
cell2.appendChild(input);
} else if (data[obj].type == "hex") {
const input = document.createElement("input");
//const
input = document.createElement("input");
input.type = "text";
input.value = data[obj].value;
input.size = 4;
input.setAttribute('key', obj.toString());
input.addEventListener('change', cfgValueChanged);
if (data[obj].force_restart == "yes") {
input.addEventListener('click', cfgAlertForceRestart);
}
//if (data[obj].force_restart == "yes") {
// input.addEventListener('click', cfgAlertForceRestart);
//}
cell2.appendChild(input);
} else {
cell2.textContent = data[obj].value;
}
// Set any generic stuff.
if (typeof input !== "undefined") {
if (data[obj].force_restart == "yes") {
input.addEventListener('click', cfgAlertForceRestart);
}
if (data[obj].greyed_out == "yes") {
console.log("GREYED OUT");
console.log(newRow);
newRow.style.opacity = '0.4'; // Sets 40% opacity
input.enabled = false;
input.disabled = true;
}
}
}
@ -929,19 +992,22 @@
console.log(_config);
}
console.log("cfgValueUnhide key="+key+" value="+value);
//console.log("cfgValueUnhide key="+key+" value="+value);
event.target.removeEventListener('change', cfgValueUnhide);
event.target.addEventListener('change', cfgValueChanged);
// Show the next table row.
event.target.parentNode.parentNode.nextSibling.style.display = '';
// If sensor show next 2 rows.
if (key.startsWith("sensor_") ) {
event.srcElement.parentNode.parentNode.childNodes[0].innerHTML = key;
event.target.parentNode.parentNode.nextSibling.nextSibling.style.display = '';
event.target.parentNode.parentNode.nextSibling.nextSibling.nextSibling.style.display = '';
}
console.log(event);
//console.log(event);
}
function deleteCfgOption(event) {
@ -958,7 +1024,7 @@
//console.log(event.parentNode.parentNode);
//console.log("deleteCfgOption - "+key);
//console.log(_config[key]);
console.log(_config);
//console.log(_config);
if (!rebuild) {
const configtable = document.getElementById("config_table");
@ -1039,7 +1105,7 @@
var cell1 = newRow.insertCell();
var cell2 = newRow.insertCell();
//var cell3 = newRow.insertCell();
//console.log("num="+num+" String="+String(num+1)+" Key="+key);
cell1.style.paddingLeft = "20px";
cell1.innerHTML = "<small>Add</small> "+key+"_path";
@ -1090,6 +1156,23 @@
input.addEventListener('change', cfgAddSensor);
cell2.appendChild(input);
// Add sensor_xx_uom row
if (row != -1){row++};
newRow = configtable.insertRow(row);
newRow.style.display = 'none';
cell1 = newRow.insertCell();
cell2 = newRow.insertCell();
cell1.style.paddingLeft = "20px";
cell1.innerHTML = key+"_uom";
input = document.createElement("input");
input.type = "text";
input.setAttribute('key', key+"_uom");
input.setAttribute('root_key', key);
input.id = key+"_uom";
input.addEventListener('change', cfgAddSensor);
cell2.appendChild(input);
_addedBlankSensor = true;
}
@ -1175,7 +1258,7 @@
var value = event.srcElement.value;
var key = event.srcElement.getAttribute('key');
var root_key = event.srcElement.getAttribute('root_key');
console.log(key + " = "+ value);
//console.log(key + " = "+ value);
//event.srcElement.parentNode.parentNode.childNodes[1].childNodes[0].removeEventListener('change', cfgAddLightProgram);
//event.srcElement.parentNode.parentNode.childNodes[1].childNodes[0].addEventListener('change', cfgValueChanged);
@ -1194,28 +1277,34 @@
//_config[key] = js;
if (_config[root_key]) {
console.log("Json exists");
//console.log("Json exists");
_config[root_key][key] = js;
} else {
console.log("Json not exists");
//console.log("Json not exists");
jsobj={};
jsobj.advanced="yes";
jsobj[key] = js;
_config[root_key] = jsobj
}
console.log(_config);
//console.log(_config);
// If all 3 have an entry, add another sensor
if ( (document.getElementById(root_key+"_path").value) &&
(document.getElementById(root_key+"_label").value) &&
(document.getElementById(root_key+"_factor").value)) {
(document.getElementById(root_key+"_factor").value) &&
(document.getElementById(root_key+"_uom").value)) {
const configtable = document.getElementById("config_table");
//console.log("Searching for key "+key+" | root key "+root_key);
var found=false;
for (let i = 0; i < configtable.rows.length; i++) {
try{
console.log(configtable.rows[i].cells[0].innerHTML);
if(configtable.rows[i].cells[0].innerHTML == key) {
addSensorRow( parseInt(key.match(/[0-9]+/)), i+1 )
if ( root_key === configtable.rows[i].cells[0].innerHTML.substring(0, root_key.length ) ) {
//console.log("Found "+configtable.rows[i].cells[0].innerHTML);
found = true;
} else if (found == true) {
//console.log("Should Add add element here index="+i+" name="+configtable.rows[i].cells[0].innerHTML);
addSensorRow( parseInt(key.match(/[0-9]+/)), i);
break;
}
} catch (e){}
@ -1224,7 +1313,8 @@
}
function isNumber(n) { return !isNaN(parseFloat(n)) && !isNaN(n - 0) }
function populateconfigtable(data=null) {
_addedBlankLightProgram = false;
_addedBlankSensor = false;
@ -1286,17 +1376,19 @@
if (lastKey.startsWith("light_program_") && ! obj.toString().startsWith("light_program_") ) {
addLightProgramRow(parseInt(lastKey.match(/[0-9]+/)));
} else if (lastKey.startsWith("sensor_") && ! obj.toString().startsWith("sensor_") ) {
addSensorRow(parseInt(lastKey.match(/[0-9]+/)));
if (isNumber(lastKey.match(/[0-9]+/))) {
addSensorRow(parseInt(lastKey.match(/[0-9]+/)));
}
} else if (lastKey.startsWith("virtual_button_") && ! obj.toString().startsWith("virtual_button_") ) {
addVirtualButtonRow(parseInt(lastKey.match(/[0-9]+/)));
}
if (_config[obj].value !== undefined) {
if (obj.toString().startsWith("light_program_") ) {
console.log(obj.toString());
//console.log(obj.toString());
num = parseInt(lastKey.match(/[0-9]+/));
num++;
console.log("check for light_program_"+String(num+1).padStart(2, '0'));
//console.log("check for light_program_"+String(num+1).padStart(2, '0'));
//console.log("check for light_program_"+num);
if ( !('light_program_'+String(num+1).padStart(2, '0') in _config)) {
addConfigRow(hideLineSeperator, _config, obj, -1, true);
@ -1351,19 +1443,27 @@
}
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!");
//console.log( _AlertsShown[key]);
if ( _AlertsShown[key] == undefined || _AlertsShown[key] == false) {
_AlertsShown[key] = true;
if ("force_restart_msg" in _config[key]) {
alert(_config[key].force_restart_msg);
} else {
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;
console.log("Config set "+key+" to "+value)
//console.log("Config set '"+key+"'' to '"+value+"'");
try {
_config[key].value = value;
@ -1379,7 +1479,7 @@
console.log("Error setting cfg '"+key+"' to '"+value+"'");
}
}
console.log(_config);
//console.log(_config);
}
function update_status(data) {
@ -1438,6 +1538,26 @@
} catch (Error) { }
}
function delayedVersionCheck(currentVersion, call=0) {
//console.log("DELAY VERSION call="+call+" - "+currentVersion+". latest "+_latestVersionAvailable);
if (_latestVersionAvailable == 0) {
if (call > 5) {
console.log("ERROR getting latest Aqualinkd version information");
return;
}
setTimeout(function() {
delayedVersionCheck(currentVersion, ++call);
}, 100);
} else {
//console.log("VERSION call="+call+" - "+currentVersion+". latest "+_latestVersionAvailable);
if ( isNewerVersion(_latestVersionAvailable, currentVersion ) || _urlParams.get('upgrade') != null) {
enablebutton("upgrade");
} else {
disablebutton("upgrade");
}
}
}
function setAqManagerOptions(data) {
/*
read deamonized logging2file logfilename debugmasks[] loglevels[]
@ -1462,10 +1582,11 @@
} else {
document.getElementById("latesversionavailable").classList.add("newversion");
}
if ( isNewerVersion(_latestVersionAvailable, data['aqualinkd_version'] ) || _urlParams.get('upgrade') != null) {
enablebutton("upgrade");
} else {
disablebutton("upgrade");
delayedVersionCheck(data['aqualinkd_version']);
if (_urlParams.get('upgrade') != null) {
enablebutton("upgrade");
}
}
}
@ -1629,7 +1750,7 @@
}, 5000);
}
} catch (exception) {
alert('<p>Error' + exception);
//alert('<p>Error' + exception);
}
}
@ -1681,8 +1802,34 @@
function init() {
// Resize log container
var h = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
//console.log("Height="+h+" Setting to "+(h-290) );
document.documentElement.style.setProperty('--logcontainer-height', (h-290)+'px');
var w = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
if (h > 0) {
try {
document.documentElement.style.setProperty('--logcontainer-height', (h-290)+'px');
} catch (e) {}
} else {
console.log("ERROR can't get browser width");
}
if (w > 0) {
try {
var computedStyles = getComputedStyle(document.documentElement);
var gw = parseInt(computedStyles.getPropertyValue('--grid-width'));
document.documentElement.style.setProperty('--logcontainer-width', (w - gw - 20)+'px');
} catch (e) {}
//console.log("grid width = " + gw );
//console.log("Setting logcontainer width to "+(w-gw) );
} else {
console.log("ERROR can't get browser height");
}
//computedStyles.getPropertyValue
//var element = document.documentElement; // For a variable defined on :root
//var computedStyles = getComputedStyle(document.documentElement);
//console.log("grid width = " + computedStyles.getPropertyValue('--grid-width') );
//console.log("grid width complete= " + computedStyles.getPropertyValue('--grid-width-complete') );
disablebutton("restart");
disablebutton("upgrade");
@ -1852,14 +1999,14 @@
<div class="content" id="logfile">
<!--
<label class="toggle logtoggle"><input class="toggle-checkbox" type="checkbox" id="logfile"
onclick="setlogfile(this);">
onclick="requestFileDownload(this);">
<div class="toggle-switch"></div><span class="toggle-label">Log to file</span>
</label>
-->
<table border='0'>
<tr>
<td>
<input id="downloadlog" type="button" onclick="setlogfile(this);"
<input id="downloadlog" type="button" onclick="requestFileDownload(this);"
value="Download logfile"></input>
</td>
<td>
@ -1904,6 +2051,10 @@
<input id="editconfig" type="button" onclick="editconfig(this);"
value="edit config"></input>
</td>
<td align="center">
<input id="downloadconfig" type="button" onclick="requestFileDownload(this);""
value="download config"></input>
</td>
</tr>
</table>
</div>

View File

@ -4,6 +4,8 @@
// For a complete list returned from your particular aqualinkd instance
// use the below URL and look at the ID value for each device.
// http://aqualink.ip.address/api/devices
var devices = [
"Filter_Pump",
"Spa",
@ -38,6 +40,7 @@
"CHEM/ORP",
"Solar_Heater",
"Extra_Aux",
"Chiller",
"Aux_V1",
"Aux_V2",
"Aux_V3",
@ -53,32 +56,18 @@
"Aux_V13",
"Aux_V14",
"Aux_V15",
"Chiller",
"Sensor/CPU",
"Aux_S1",
"Aux_S2",
"Aux_S3",
"Aux_S4",
"Aux_S5",
"Aux_S6",
"Aux_S7",
"Aux_S8",
"Aux_S9",
"Aux_S10",
];
// This get's picked up by dynamic_config.js and used as mode 0
// As version 2.5.0 Please use aqualinkd.conf for this configuration.
var light_program = [
"Voodoo Lounge - Show",
"Blue Sea",
"Royal Blue",
"Afternoon Skies",
"Aqua Green",
"Emerald",
"Cloud White",
"Warm Red",
"Flamingo",
"Vivid Violet",
"Sangria",
"Twilight - Show",
"Tranquility - Show",
"Gemstone - Show",
"USA - Show",
"Mardi Gras - Show",
"Cool Cabaret - Show"
];
// all SWG return a status number, some have different meanings. Change the text below to suit, NOT THE NUMBER.
var swgStatus = {
0: "On",

View File

@ -5,4 +5,9 @@ _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"
_confighelp["event_check_use_scheduler_times"]="Turn on filter pump from events that can cause it to turn off"
_confighelp["sync_panel_time"]="Keep panel time synced with computer"
_confighelp["ftdi_low_latency"]="Give RS485 adapter higher priority in kernel (FTDI chips only)"
_confighelp["rs485_frame_delay"]="Time for AqualinkD to reply to RS485 messages"
_confighelp["light_programming_mode"]="Valid only for AqualinkD programming light color (button_??_light_mode = 0)"
//_confighelp["light_program_01"]="Light colors for AqualinkD programmed lights ie (button_??_light_mode = 0)"

View File

@ -961,7 +961,7 @@
function switchTileState(id, details) {
if (details) {
console.log("TILE DETAILS WHAY ARE WE HERE??? '" + id + "'");
console.log("TILE DETAILS WHY ARE WE HERE??? '" + id + "'");
} else {
setTileState(id, (document.getElementById(id).getAttribute('status') == 'off'))
}
@ -1050,8 +1050,8 @@
setTileOn(id, 'on');
}
} catch (e) {
// NSF Do we need to create the device?
console.log('ERROR id=' + id + ' Line '+new Error().lineNumber+' | element = '+document.getElementById(id));
// Devices that are hidden are not found
//console.log('ERROR id=' + id + ' Line '+new Error().lineNumber+' | element = '+document.getElementById(id));
}
//document.getElementById(id + '_tile_icon_value').textContent = value;
var tile;
@ -1284,13 +1284,21 @@
return num.toString().padStart(2, "0");
}
function caseInsensitiveIndexOf(array, searchString) {
const lowerCaseArray = array.map(element => String(element).toLowerCase());
return lowerCaseArray.indexOf(searchString.toLowerCase());
}
function createTile(object) {
//console.log("Create tile "+object.id);
if (object.name == 'NONE') {
//console.log("Create tile <exclude name> "+object);
return;
}
if (typeof devices !== 'undefined' && devices.indexOf(object.id) < 0) {
//console.log("Create tile <exclude list> "+object.label);
//if (typeof devices !== 'undefined' && devices.indexOf(object.id) < 0) {
if (typeof devices !== 'undefined' && caseInsensitiveIndexOf(devices, object.id) < 0) {
//console.log("Create tile <exclude list> "+object);
//console.log(object);
return;
}
//if (object.type == 'switch' || object.type == 'switch_program') {
@ -1784,12 +1792,26 @@
setTileState(id, state);
}
} else if (type == 'light_dimmer') {
//console.log("DIMMER "+state+" - "+tile.getAttribute('status'));
var value = slider.value;
if (state == (tile.getAttribute('status') == 'off')) {
if (state == false) { //Turn off
//console.log("State off");
setTileState(id, state);
} else if (sp_value != slider.value && dimmer_slider_changed == true) {
//console.log("SLIDER CHANGED");
setThermostatSetpoint(id, slider.value);
} else {
//console.log("State on/off");
setTileState(id, state);
}
/*
if (state == (tile.getAttribute('status') == 'off')) {
console.log("ON/OFF "+state+" = "+(tile.getAttribute('status') == 'off'));
setTileState(id, state);
} else if (sp_value != slider.value && dimmer_slider_changed == true) {
console.log("SLIDER CHANGED");
setThermostatSetpoint(id, slider.value);
}*/
} else if (type == 'setpoint_swg') {
//console.log ("Boost attribute = "+tile.getAttribute('boost'));
//console.log ("state = "+state);
@ -2350,7 +2372,8 @@
for (var obj in data.sensors) {
//console.log("Set Value `Sensor/"+obj.toString()+"' to "+data.sensors[obj]);
setTileValue("Sensor/"+obj.toString(), data.sensors[obj]);
//setTileValue("Sensor/"+obj.toString(), data.sensors[obj]);
setTileValue(obj.toString(), data.sensors[obj]);
}
for (var obj in data.alternate_modes) {
@ -2480,8 +2503,10 @@
var pa;
var pb;
try {
pa = devices.indexOf(a.id);
pb = devices.indexOf(b.id);
//pa = devices.indexOf(a.id);
//pb = devices.indexOf(b.id);
pa = caseInsensitiveIndexOf(devices,a.id);
pb = caseInsensitiveIndexOf(devices,b.id);
if (pa > pb)
return 1;
else if (pa < pb)