mirror of https://github.com/sfeakes/AqualinkD.git
V1.3.1
parent
4f3fc5927c
commit
9a68a8161d
Binary file not shown.
75
Makefile
75
Makefile
|
@ -15,40 +15,35 @@ DBG =
|
|||
#GCCFLAGS = -Wall -ffunction-sections -fdata-sections
|
||||
|
||||
# define any compile-time flags
|
||||
GCCFLAGS = -Wall
|
||||
GCCFLAGS = -Wall -O3
|
||||
#GCCFLAGS = -Wall
|
||||
|
||||
#CFLAGS = -Wall -g -lpthread -lwiringPi -lm -I.
|
||||
#CFLAGS = -Wall -g $(LIBS) -I/usr/local/include/ -L/usr/local/lib/
|
||||
#CFLAGS = -Wall -g $(LIBS) -std=gnu11 -I/nas/data/Development/Raspberry/aqualink/libwebsockets-2.0-stable/lib -L/nas/data/Development/Raspberry/aqualink/libwebsockets-2.0-stable/lib
|
||||
#CFLAGS = -Wall -g $(LIBS)
|
||||
#CFLAGS = -Wall -g $(LIBS) -std=gnu11
|
||||
CFLAGS = $(GCCFLAGS) $(DBG) $(LIBS) -D MG_DISABLE_MD5 -D MG_DISABLE_HTTP_DIGEST_AUTH -D MG_DISABLE_MD5 -D MG_DISABLE_JSON_RPC
|
||||
#CFLAGS = -Wall $(DBG) $(LIBS) -D MG_DISABLE_MQTT -D MG_DISABLE_MD5 -D MG_DISABLE_HTTP_DIGEST_AUTH -D MG_DISABLE_MD5 -D MG_DISABLE_JSON_RPC
|
||||
|
||||
INCLUDES = -I/nas/data/Development/Raspberry/aqualink/aqualinkd
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
|
||||
# define the C source files
|
||||
SRCS = aqualinkd.c utils.c config.c aq_serial.c init_buttons.c aq_programmer.c net_services.c json_messages.c pda.c pda_menu.c pda_aq_programmer.c mongoose.c
|
||||
SRCS = aqualinkd.c utils.c config.c aq_serial.c init_buttons.c aq_programmer.c net_services.c json_messages.c pda.c pda_menu.c pda_aq_programmer.c iAqualink_messages.c mongoose.c
|
||||
|
||||
SL_SRC = serial_logger.c aq_serial.c utils.c
|
||||
PDA_SRC = pda_test.c pda_menu.c aq_serial.c utils.c
|
||||
#AL_SRC = aquarite_logger.c aq_serial.c utils.c
|
||||
#AR_SRC = aquarite/aquarited.c aquarite/ar_net_services.c aquarite/ar_config.c aq_serial.c utils.c mongoose.c json_messages.c config.c
|
||||
LR_SRC = log_reader.c aq_serial.c utils.c
|
||||
PL_EXSRC = aq_serial.c
|
||||
PL_SRC := $(filter-out aq_serial.c, $(SRCS))
|
||||
|
||||
OBJS = $(SRCS:.c=.o)
|
||||
|
||||
SL_OBJS = $(SL_SRC:.c=.o)
|
||||
PDA_OBJS = $(PDA_SRC:.c=.o)
|
||||
#AL_OBJS = $(AL_SRC:.c=.o)
|
||||
#AR_OBJS = $(AR_SRC:.c=.o)
|
||||
LR_OBJS = $(LR_SRC:.c=.o)
|
||||
PL_OBJS = $(PL_SRC:.c=.o)
|
||||
|
||||
# define the executable file
|
||||
MAIN = ./release/aqualinkd
|
||||
SLOG = ./release/serial_logger
|
||||
PDA = ./release/pda_test
|
||||
#AQUARITELOG = ./release/aquarite_logger
|
||||
AQUARITED = ./release/aquarited
|
||||
LOGR = ./release/log_reader
|
||||
PLAY = ./release/aqualinkd-player
|
||||
|
||||
all: $(MAIN)
|
||||
@echo: $(MAIN) have been compiled
|
||||
|
@ -56,31 +51,26 @@ all: $(MAIN)
|
|||
$(MAIN): $(OBJS)
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -o $(MAIN) $(OBJS) $(LFLAGS) $(LIBS)
|
||||
|
||||
|
||||
slog: $(SLOG)
|
||||
@echo: $(SLOG) have been compiled
|
||||
|
||||
$(SLOG): $(SL_OBJS)
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -o $(SLOG) $(SL_OBJS)
|
||||
|
||||
pda: $(PDA)
|
||||
@echo: $(PDA) have been compiled
|
||||
logr: $(LOGR)
|
||||
@echo: $(LOGR) have been compiled
|
||||
|
||||
$(PDA): $(PDA_OBJS)
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -o $(PDA) $(PDA_OBJS)
|
||||
$(LOGR): $(LR_OBJS)
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -o $(LOGR) $(LR_OBJS)
|
||||
|
||||
player: $(PLAY)
|
||||
@echo: $(PLAY) have been compiled
|
||||
|
||||
#aquaritelog: $(AQUARITELOG)
|
||||
# @echo: $(AQUARITELOG) have been compiled
|
||||
aq_serial_player.o: aq_serial.c
|
||||
$(CC) $(CFLAGS) -D PLAYBACK_MODE $(INCLUDES) -c aq_serial.c -o aq_serial_player.o
|
||||
|
||||
#$(AQUARITELOG): $(AL_OBJS)
|
||||
# $(CC) $(CFLAGS) $(INCLUDES) -o $(AQUARITELOG) $(AL_OBJS)
|
||||
|
||||
aquarited: $(AQUARITED)
|
||||
@echo: $(AQUARITED) have been compiled
|
||||
|
||||
$(AQUARITED): $(AR_OBJS)
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -o $(AQUARITED) $(AR_OBJS)
|
||||
$(PLAY): $(PL_OBJS) aq_serial_player.o
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -o $(PLAY) $(PL_OBJS) aq_serial_player.o
|
||||
|
||||
# this is a suffix replacement rule for building .o's from .c's
|
||||
# it uses automatic variables $<: the name of the prerequisite of
|
||||
|
@ -90,32 +80,11 @@ $(AQUARITED): $(AR_OBJS)
|
|||
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
|
||||
|
||||
clean:
|
||||
$(RM) *.o *~ $(MAIN) $(MAIN_U)
|
||||
$(RM) *.o *~ $(MAIN) $(MAIN_U) $(PLAY)
|
||||
|
||||
depend: $(SRCS)
|
||||
makedepend $(INCLUDES) $^
|
||||
|
||||
install: $(MAIN)
|
||||
./release/install.sh
|
||||
|
||||
|
||||
# All Target
|
||||
#all: aqualinkd
|
||||
#
|
||||
# Tool invocations
|
||||
#aqualinkd: $(OBJS) $(USER_OBJS)
|
||||
# @echo 'Building target: $@'
|
||||
# @echo 'Invoking: GCC C Linker'
|
||||
# gcc -L/home/perry/workspace/libwebsockets/Debug -pg -o"aqualinkd" $(OBJS) $(USER_OBJS) $(LIBS)
|
||||
# @echo 'Finished building target: $@'
|
||||
# @echo ' '
|
||||
#
|
||||
# Other Targets
|
||||
#clean:
|
||||
# -$(RM) $(OBJS)$(C_DEPS)$(EXECUTABLES) aqualinkd
|
||||
# -@echo ' '
|
||||
#
|
||||
#.PHONY: all clean dependents
|
||||
#.SECONDARY:
|
||||
#
|
||||
#-include ../makefile.targets
|
||||
|
|
|
@ -63,6 +63,10 @@ Designed to mimic AqualinkRS6 All Button keypad, and just like the keypad you ca
|
|||
* http://aqualink.ip/simple.html <- (Simple opion if you don't like the above)
|
||||
* http://aqualink.ip/simulator.html <- (RS8 All Button Control Panel simulator)
|
||||
#<a name="release"></a>
|
||||
## Update in Release 1.3.1
|
||||
* Changed the way PDA mode will sleep.
|
||||
* Added preliminary support for Variable Speed Pumps. (Limited to only reading status and post to MQTT/WebSocket/API if iAqualink is active on the RS485 bus)
|
||||
* Added int status to Web API
|
||||
## Update in Release 1.3.0
|
||||
* Large update for PDA only control panels (Majority of this is ballle98 work)
|
||||
* Can distinguish between AquaPalm and PDA supported control panels.
|
||||
|
|
|
@ -29,6 +29,11 @@
|
|||
|
||||
#define POOL_THERMO_TEMP_TOPIC BTN_POOL_HTR "/Temperature"
|
||||
#define SPA_THERMO_TEMP_TOPIC BTN_SPA_HTR "/Temperature"
|
||||
|
||||
#define PUMP_TOPIC "Pump_"
|
||||
#define PUMP_RPM_TOPIC "/RPM"
|
||||
#define PUMP_GPH_TOPIC "/GPH"
|
||||
#define PUMP_WATTS_TOPIC "/Watts"
|
||||
/*
|
||||
#define AIR_TEMPERATURE "Air"
|
||||
#define POOL_TEMPERATURE "Pool_Water"
|
||||
|
|
|
@ -1153,7 +1153,7 @@ bool waitForEitherMessage(struct aqualinkdata *aq_data, char* message1, char* me
|
|||
pthread_mutex_lock(&aq_data->active_thread.thread_mutex);
|
||||
char* msgS1;
|
||||
char* msgS2;
|
||||
char* ptr;
|
||||
char* ptr = NULL;
|
||||
|
||||
|
||||
if (message1 != NULL) {
|
||||
|
@ -1221,7 +1221,7 @@ bool waitForMessage(struct aqualinkdata *aq_data, char* message, int numMessageR
|
|||
pthread_mutex_init(&aq_data->active_thread.thread_mutex, NULL);
|
||||
pthread_mutex_lock(&aq_data->active_thread.thread_mutex);
|
||||
char* msgS;
|
||||
char* ptr;
|
||||
char* ptr = NULL;
|
||||
|
||||
if (message != NULL) {
|
||||
if (message[0] == '^')
|
||||
|
|
192
aq_serial.c
192
aq_serial.c
|
@ -140,6 +140,12 @@ const char* get_packet_type(unsigned char* packet , int length)
|
|||
case CMD_PDA_HIGHLIGHTCHARS:
|
||||
return "PDA C_HlightChar";
|
||||
break;
|
||||
case CMD_IAQ_MSG:
|
||||
return "iAq Message";
|
||||
break;
|
||||
case CMD_IAQ_MENU_MSG:
|
||||
return "iAq Menu";
|
||||
break;
|
||||
default:
|
||||
sprintf(buf, "Unknown '0x%02hhx'", packet[PKT_CMD]);
|
||||
return buf;
|
||||
|
@ -148,6 +154,68 @@ const char* get_packet_type(unsigned char* packet , int length)
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Generate and return checksum of packet.
|
||||
int generate_checksum(unsigned char* packet, int length)
|
||||
{
|
||||
int i, sum, n;
|
||||
|
||||
n = length - 3;
|
||||
sum = 0;
|
||||
for (i = 0; i < n; i++)
|
||||
sum += (int) packet[i];
|
||||
return(sum & 0x0ff);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Send an ack packet to the Aqualink RS8 master device.
|
||||
// fd: the file descriptor of the serial port connected to the device
|
||||
// command: the command byte to send to the master device, NUL if no command
|
||||
//
|
||||
// NUL = '\x00'
|
||||
// DLE = '\x10'
|
||||
// STX = '\x02'
|
||||
// ETX = '\x03'
|
||||
//
|
||||
// masterAddr = '\x00' # address of Aqualink controller
|
||||
//
|
||||
//msg = DLE+STX+dest+cmd+args
|
||||
//msg = msg+self.checksum(msg)+DLE+ETX
|
||||
// DLE+STX+DEST+CMD+ARGS+CHECKSUM+DLE+ETX
|
||||
|
||||
|
||||
void print_hex(char *pk, int length)
|
||||
{
|
||||
int i=0;
|
||||
for (i=0;i<length;i++)
|
||||
{
|
||||
printf("0x%02hhx|",pk[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void test_cmd()
|
||||
{
|
||||
const int length = 11;
|
||||
unsigned char ackPacket[] = { NUL, DLE, STX, DEV_MASTER, CMD_ACK, NUL, NUL, 0x13, DLE, ETX, NUL };
|
||||
//send_cmd(fd, CMD_ACK, command);
|
||||
|
||||
print_hex((char *)ackPacket, length);
|
||||
|
||||
ackPacket[7] = generate_checksum(ackPacket, length-1);
|
||||
print_hex((char *)ackPacket, length);
|
||||
|
||||
ackPacket[6] = 0x02;
|
||||
ackPacket[7] = generate_checksum(ackPacket, length-1);
|
||||
print_hex((char *)ackPacket, length);
|
||||
}
|
||||
|
||||
#ifndef PLAYBACK_MODE
|
||||
|
||||
/*
|
||||
Open and Initialize the serial communications port to the Aqualink RS8 device.
|
||||
Arg is tty or port designation string
|
||||
|
@ -231,64 +299,6 @@ void close_serial_port(int fd)
|
|||
logMessage(LOG_DEBUG_SERIAL, "Closed serial port\n");
|
||||
}
|
||||
|
||||
// Generate and return checksum of packet.
|
||||
int generate_checksum(unsigned char* packet, int length)
|
||||
{
|
||||
int i, sum, n;
|
||||
|
||||
n = length - 3;
|
||||
sum = 0;
|
||||
for (i = 0; i < n; i++)
|
||||
sum += (int) packet[i];
|
||||
return(sum & 0x0ff);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Send an ack packet to the Aqualink RS8 master device.
|
||||
// fd: the file descriptor of the serial port connected to the device
|
||||
// command: the command byte to send to the master device, NUL if no command
|
||||
//
|
||||
// NUL = '\x00'
|
||||
// DLE = '\x10'
|
||||
// STX = '\x02'
|
||||
// ETX = '\x03'
|
||||
//
|
||||
// masterAddr = '\x00' # address of Aqualink controller
|
||||
//
|
||||
//msg = DLE+STX+dest+cmd+args
|
||||
//msg = msg+self.checksum(msg)+DLE+ETX
|
||||
// DLE+STX+DEST+CMD+ARGS+CHECKSUM+DLE+ETX
|
||||
|
||||
|
||||
void print_hex(char *pk, int length)
|
||||
{
|
||||
int i=0;
|
||||
for (i=0;i<length;i++)
|
||||
{
|
||||
printf("0x%02hhx|",pk[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void test_cmd()
|
||||
{
|
||||
const int length = 11;
|
||||
unsigned char ackPacket[] = { NUL, DLE, STX, DEV_MASTER, CMD_ACK, NUL, NUL, 0x13, DLE, ETX, NUL };
|
||||
//send_cmd(fd, CMD_ACK, command);
|
||||
|
||||
print_hex((char *)ackPacket, length);
|
||||
|
||||
ackPacket[7] = generate_checksum(ackPacket, length-1);
|
||||
print_hex((char *)ackPacket, length);
|
||||
|
||||
ackPacket[6] = 0x02;
|
||||
ackPacket[7] = generate_checksum(ackPacket, length-1);
|
||||
print_hex((char *)ackPacket, length);
|
||||
}
|
||||
|
||||
void send_test_cmd(int fd, unsigned char destination, unsigned char b1, unsigned char b2, unsigned char b3)
|
||||
{
|
||||
const int length = 11;
|
||||
|
@ -548,6 +558,72 @@ int get_packet(int fd, unsigned char* packet)
|
|||
return index;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#else // PLAYBACK_MODE
|
||||
#include <stdlib.h>
|
||||
FILE *_fp;
|
||||
|
||||
int init_serial_port(char* file)
|
||||
{
|
||||
_fp = fopen ( file, "r" );
|
||||
if ( _fp == NULL )
|
||||
{
|
||||
perror ( file ); /* why didn't the file open? */
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void close_serial_port(int fd)
|
||||
{
|
||||
fclose(_fp);
|
||||
}
|
||||
|
||||
int get_packet(int fd, unsigned char* packet_buffer)
|
||||
{
|
||||
int packet_length = 0;
|
||||
char line[256];
|
||||
char hex[6];
|
||||
int i;
|
||||
|
||||
if ( fgets ( line, sizeof line, _fp ) != NULL ) /* read a line */
|
||||
{
|
||||
packet_length=0;
|
||||
for (i=0; i < strlen(line); i=i+5)
|
||||
{
|
||||
strncpy(hex, &line[i], 4);
|
||||
hex[5] = '\0';
|
||||
packet_buffer[packet_length] = (int)strtol(hex, NULL, 16);
|
||||
//printf("%s = 0x%02hhx = %c\n", hex, packet_buffer[packet_length], packet_buffer[packet_length]);
|
||||
packet_length++;
|
||||
}
|
||||
packet_length--;
|
||||
|
||||
logMessage(LOG_DEBUG_SERIAL, "PLAYBACK read %d bytes\n",packet_length);
|
||||
//printf("To 0x%02hhx, type %15.15s, length %2.2d ", packet_buffer[PKT_DEST], get_packet_type(packet_buffer, packet_length),packet_length);
|
||||
//fputs ( line, stdout );
|
||||
|
||||
return packet_length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void send_extended_ack(int fd, unsigned char ack_type, unsigned char command)
|
||||
{}
|
||||
|
||||
void send_ack(int fd, unsigned char command)
|
||||
{}
|
||||
|
||||
void send_messaged(int fd, unsigned char destination, char *message)
|
||||
{}
|
||||
|
||||
void send_command(int fd, unsigned char destination, unsigned char b1, unsigned char b2, unsigned char b3)
|
||||
{}
|
||||
#endif // PLAYBACK_MODE
|
||||
|
||||
|
||||
|
||||
// Reads the bytes of the next incoming packet, and
|
||||
// returns when a good packet is available in packet
|
||||
// fd: the file descriptor to read the bytes from
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#define DEV_MASTER 0x00
|
||||
#define SWG_DEV_ID 0x50
|
||||
#define IAQ_DEV_ID 0x33
|
||||
|
||||
// PACKET DEFINES
|
||||
#define NUL 0x00
|
||||
|
@ -184,6 +185,12 @@ SPILLOVER IS DISABLED WHILE SPA IS ON
|
|||
#define CMD_PDA_SHIFTLINES 0x0F
|
||||
#define CMD_PDA_HIGHLIGHTCHARS 0x10
|
||||
|
||||
/* iAqualink */
|
||||
#define CMD_IAQ_MSG 0x25
|
||||
#define CMD_IAQ_MENU_MSG 0x24
|
||||
|
||||
|
||||
|
||||
typedef enum {
|
||||
ON,
|
||||
OFF,
|
||||
|
|
11
aqualink.h
11
aqualink.h
|
@ -19,6 +19,8 @@
|
|||
//#define UNKNOWN TEMP_UNKNOWN
|
||||
#define DATE_STRING_LEN 30
|
||||
|
||||
#define MAX_PUMPS 2
|
||||
|
||||
enum {
|
||||
FAHRENHEIT,
|
||||
CELSIUS,
|
||||
|
@ -63,6 +65,13 @@ struct action {
|
|||
//char value[10];
|
||||
};
|
||||
|
||||
typedef struct pumpd
|
||||
{
|
||||
int rpm;
|
||||
int gph;
|
||||
int watts;
|
||||
} pump_detail;
|
||||
|
||||
struct aqualinkdata
|
||||
{
|
||||
//char crap[AQ_MSGLEN];
|
||||
|
@ -98,6 +107,8 @@ struct aqualinkdata
|
|||
aqledstate service_mode_state;
|
||||
aqledstate frz_protect_state;
|
||||
unsigned char last_packet_type;
|
||||
pump_detail pumps[MAX_PUMPS];
|
||||
int open_websockets;
|
||||
//bool last_msg_was_status;
|
||||
//bool ar_swg_connected;
|
||||
};
|
||||
|
|
50
aqualinkd.c
50
aqualinkd.c
|
@ -38,6 +38,7 @@
|
|||
#include "net_services.h"
|
||||
#include "pda_menu.h"
|
||||
#include "pda.h"
|
||||
#include "iAqualink_messages.h"
|
||||
#include "version.h"
|
||||
|
||||
#define DEFAULT_CONFIG_FILE "./aqualinkd.conf"
|
||||
|
@ -727,6 +728,7 @@ int main(int argc, char *argv[])
|
|||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
void debugPacketPrint(unsigned char ID, unsigned char *packet_buffer, int packet_length)
|
||||
{
|
||||
char buff[1000];
|
||||
|
@ -791,6 +793,26 @@ void logPacket(unsigned char *packet_buffer, int packet_length)
|
|||
debugPacketPrint(last_packet_buffer[PKT_DEST], packet_buffer, packet_length);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void logPacket(unsigned char *packet_buffer, int packet_length)
|
||||
{
|
||||
char buff[1000];
|
||||
int i = 0;
|
||||
int cnt = 0;
|
||||
|
||||
cnt = sprintf(buff, "To 0x%02hhx of type %8.8s", packet_buffer[PKT_DEST], get_packet_type(packet_buffer, packet_length));
|
||||
cnt += sprintf(buff + cnt, " | HEX: ");
|
||||
for (i = 0; i < packet_length; i++)
|
||||
cnt += sprintf(buff + cnt, "0x%02hhx|", packet_buffer[i]);
|
||||
|
||||
if (_config_parameters.debug_RSProtocol_packets)
|
||||
writePacketLog(buff);
|
||||
else
|
||||
logMessage(LOG_DEBUG_SERIAL, "%s", buff);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define MAX_BLOCK_ACK 12
|
||||
#define MAX_BUSY_ACK (50 + MAX_BLOCK_ACK)
|
||||
|
@ -842,7 +864,8 @@ void main_loop()
|
|||
int rs_fd;
|
||||
int packet_length;
|
||||
unsigned char packet_buffer[AQ_MAXPKTLEN];
|
||||
bool interestedInNextAck;
|
||||
bool interestedInNextAck = false;
|
||||
int i;
|
||||
//int delayAckCnt = 0;
|
||||
|
||||
// NSF need to find a better place to init this.
|
||||
|
@ -866,6 +889,13 @@ void main_loop()
|
|||
_aqualink_data.frz_protect_state = OFF;
|
||||
_aqualink_data.service_mode_state = OFF;
|
||||
_aqualink_data.battery = OK;
|
||||
_aqualink_data.open_websockets = 0;
|
||||
|
||||
for (i=0; i < MAX_PUMPS; i++) {
|
||||
_aqualink_data.pumps[i].rpm = TEMP_UNKNOWN;
|
||||
_aqualink_data.pumps[i].gph = TEMP_UNKNOWN;
|
||||
_aqualink_data.pumps[i].watts = TEMP_UNKNOWN;
|
||||
}
|
||||
|
||||
if (_config_parameters.force_swg == true)
|
||||
_aqualink_data.swg_percent = 0;
|
||||
|
@ -926,7 +956,8 @@ void main_loop()
|
|||
{
|
||||
blank_read = 0;
|
||||
|
||||
if (_config_parameters.debug_RSProtocol_packets) logPacket(packet_buffer, packet_length);
|
||||
if (_config_parameters.debug_RSProtocol_packets || getLogLevel() >= LOG_DEBUG_SERIAL)
|
||||
logPacket(packet_buffer, packet_length);
|
||||
|
||||
if (packet_length > 0 && packet_buffer[PKT_DEST] == _config_parameters.device_id)
|
||||
{
|
||||
|
@ -991,12 +1022,17 @@ void main_loop()
|
|||
{
|
||||
interestedInNextAck = false;
|
||||
}
|
||||
|
||||
if (packet_buffer[PKT_DEST] == IAQ_DEV_ID && packet_buffer[PKT_CMD] == CMD_IAQ_MSG) {
|
||||
if (processiAqualinkMsg(packet_buffer, packet_length, &_aqualink_data) != false)
|
||||
broadcast_aqualinkstate(mgr.active_connections);
|
||||
}
|
||||
|
||||
//}
|
||||
|
||||
// logMessage(LOG_DEBUG_SERIAL, "Received Packet for ID 0x%02hhx of type %s %s\n",packet_buffer[PKT_DEST], get_packet_type(packet_buffer, packet_length),
|
||||
// (packet_buffer[PKT_DEST] == _config_parameters.device_id)?" <-- Aqualinkd ID":"");
|
||||
}
|
||||
/*
|
||||
if (getLogLevel() >= LOG_DEBUG_SERIAL) {
|
||||
logMessage(LOG_DEBUG_SERIAL, "Received Packet for ID 0x%02hhx of type %s %s\n",packet_buffer[PKT_DEST], get_packet_type(packet_buffer, packet_length),
|
||||
(packet_buffer[PKT_DEST] == _config_parameters.device_id)?" <-- Aqualinkd ID":"");
|
||||
}*/
|
||||
}
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "aqualink.h"
|
||||
#include "iAqualink_messages.h"
|
||||
#include "utils.h"
|
||||
|
||||
bool processiAqualinkMsg(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata)
|
||||
{
|
||||
bool changedAnything = false;
|
||||
static char lastmessage[AQ_MSGLONGLEN];
|
||||
|
||||
//static char message[AQ_MSGLONGLEN + 1];
|
||||
static int pumpIndex = 1;
|
||||
|
||||
/*
|
||||
Pump type are like // Not sure how to read this accuratly.
|
||||
"Jandy ePUMP 1"
|
||||
"Intelliflo VS 1"
|
||||
|
||||
RPM message always comes after the above, so maybe saving last string
|
||||
then when see RPM go back to get pump number.
|
||||
|
||||
' RPM: 2950'
|
||||
' Watts: 1028'
|
||||
' GPM: 1028'
|
||||
*/
|
||||
|
||||
if (packet_buffer[9] == 'R' && packet_buffer[10] == 'P' && packet_buffer[11] == 'M' && packet_buffer[12] == ':') {
|
||||
|
||||
pumpIndex = atoi((char *) &lastmessage[14]);
|
||||
|
||||
if ( pumpIndex < MAX_PUMPS && pumpIndex < 0) {
|
||||
pumpIndex = 1;
|
||||
logMessage(LOG_ERR, "Can't find pump index for messsage '%.*s' in string '%.*s' using %d\n",AQ_MSGLEN, packet_buffer+4, AQ_MSGLEN, lastmessage, pumpIndex);
|
||||
}
|
||||
|
||||
aqdata->pumps[pumpIndex-1].rpm = atoi((char *) &packet_buffer[13]);
|
||||
|
||||
logMessage(LOG_DEBUG, "Read message '%.*s' from iAqualink device\n",AQ_MSGLEN, packet_buffer+4);
|
||||
changedAnything = true;
|
||||
}
|
||||
else if (packet_buffer[9] == 'G' && packet_buffer[10] == 'P' && packet_buffer[11] == 'H' && packet_buffer[12] == ':') {
|
||||
aqdata->pumps[pumpIndex-1].gph = atoi((char *) &packet_buffer[13]);
|
||||
logMessage(LOG_DEBUG, "Read message '%.*s' from iAqualink device\n",AQ_MSGLEN, packet_buffer+4);
|
||||
changedAnything = true;
|
||||
}
|
||||
else if (packet_buffer[7] == 'W' && packet_buffer[8] == 'a' && packet_buffer[9] == 't' && packet_buffer[10] == 't' && packet_buffer[11] == 's' && packet_buffer[12] == ':') {
|
||||
//printf("Punp %d, Watts = %d\n", pumpIndex, atoi((char *) &packet_buffer[13]));
|
||||
aqdata->pumps[pumpIndex-1].watts = atoi((char *) &packet_buffer[13]);
|
||||
logMessage(LOG_DEBUG, "Read message '%.*s' from iAqualink device\n",AQ_MSGLEN, packet_buffer+4);
|
||||
changedAnything = true;
|
||||
}
|
||||
|
||||
//printf("Message : '");
|
||||
//fwrite(packet_buffer + 4, 1, packet_length-7, stdout);
|
||||
//printf("'\n");
|
||||
|
||||
strncpy(lastmessage, (char *)&packet_buffer[4], packet_length-7);
|
||||
|
||||
return changedAnything;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
#ifndef IAQ_MESSAGES_H_
|
||||
#define IAQ_MESSAGES_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
bool processiAqualinkMsg(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata);
|
||||
|
||||
#endif // IAQ_MESSAGES_H_
|
|
@ -117,6 +117,28 @@ char *LED2text(aqledstate state)
|
|||
}
|
||||
}
|
||||
|
||||
int LED2int(aqledstate state)
|
||||
{
|
||||
switch (state) {
|
||||
case ON:
|
||||
return 1;
|
||||
break;
|
||||
case OFF:
|
||||
return 0;
|
||||
break;
|
||||
case FLASH:
|
||||
return 2;
|
||||
break;
|
||||
case ENABLE:
|
||||
return 3;
|
||||
break;
|
||||
case LED_S_UNKNOWN:
|
||||
default:
|
||||
return 4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int build_device_JSON(struct aqualinkdata *aqdata, int programable_switch, char* buffer, int size, bool homekit)
|
||||
{
|
||||
memset(&buffer[0], 0, size);
|
||||
|
@ -138,7 +160,7 @@ int build_device_JSON(struct aqualinkdata *aqdata, int programable_switch, char*
|
|||
for (i=0; i < TOTAL_BUTTONS; i++)
|
||||
{
|
||||
if ( strcmp(BTN_POOL_HTR,aqdata->aqbuttons[i].name) == 0 && aqdata->pool_htr_set_point != TEMP_UNKNOWN) {
|
||||
length += sprintf(buffer+length, "{\"type\": \"setpoint_thermo\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"spvalue\": \"%.*f\", \"value\": \"%.*f\" },",
|
||||
length += sprintf(buffer+length, "{\"type\": \"setpoint_thermo\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"spvalue\": \"%.*f\", \"value\": \"%.*f\", \"int_status\": \"%d\" },",
|
||||
aqdata->aqbuttons[i].name,
|
||||
aqdata->aqbuttons[i].label,
|
||||
aqdata->aqbuttons[i].led->state==ON?JSON_ON:JSON_OFF,
|
||||
|
@ -146,9 +168,10 @@ int build_device_JSON(struct aqualinkdata *aqdata, int programable_switch, char*
|
|||
((homekit)?2:0),
|
||||
((homekit && aqdata->temp_units==FAHRENHEIT)?degFtoC(aqdata->pool_htr_set_point):aqdata->pool_htr_set_point),
|
||||
((homekit)?2:0),
|
||||
((homekit && aqdata->temp_units==FAHRENHEIT)?degFtoC(aqdata->pool_temp):aqdata->pool_temp));
|
||||
((homekit && aqdata->temp_units==FAHRENHEIT)?degFtoC(aqdata->pool_temp):aqdata->pool_temp),
|
||||
LED2int(aqdata->aqbuttons[i].led->state));
|
||||
} else if ( strcmp(BTN_SPA_HTR,aqdata->aqbuttons[i].name)==0 && aqdata->spa_htr_set_point != TEMP_UNKNOWN) {
|
||||
length += sprintf(buffer+length, "{\"type\": \"setpoint_thermo\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"spvalue\": \"%.*f\", \"value\": \"%.*f\" },",
|
||||
length += sprintf(buffer+length, "{\"type\": \"setpoint_thermo\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"spvalue\": \"%.*f\", \"value\": \"%.*f\", \"int_status\": \"%d\" },",
|
||||
aqdata->aqbuttons[i].name,
|
||||
aqdata->aqbuttons[i].label,
|
||||
aqdata->aqbuttons[i].led->state==ON?JSON_ON:JSON_OFF,
|
||||
|
@ -156,24 +179,27 @@ int build_device_JSON(struct aqualinkdata *aqdata, int programable_switch, char*
|
|||
((homekit)?2:0),
|
||||
((homekit && aqdata->temp_units==FAHRENHEIT)?degFtoC(aqdata->spa_htr_set_point):aqdata->spa_htr_set_point),
|
||||
((homekit)?2:0),
|
||||
((homekit && aqdata->temp_units==FAHRENHEIT)?degFtoC(aqdata->spa_temp):aqdata->spa_temp));
|
||||
((homekit && aqdata->temp_units==FAHRENHEIT)?degFtoC(aqdata->spa_temp):aqdata->spa_temp),
|
||||
LED2int(aqdata->aqbuttons[i].led->state));
|
||||
} else if (programable_switch > 0 && programable_switch == i) {
|
||||
length += sprintf(buffer+length, "{\"type\": \"switch_program\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\" },",
|
||||
length += sprintf(buffer+length, "{\"type\": \"switch_program\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"int_status\": \"%d\" },",
|
||||
aqdata->aqbuttons[i].name,
|
||||
aqdata->aqbuttons[i].label,
|
||||
aqdata->aqbuttons[i].led->state==ON?JSON_ON:JSON_OFF,
|
||||
LED2text(aqdata->aqbuttons[i].led->state));
|
||||
LED2text(aqdata->aqbuttons[i].led->state),
|
||||
LED2int(aqdata->aqbuttons[i].led->state));
|
||||
} else {
|
||||
length += sprintf(buffer+length, "{\"type\": \"switch\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\" },",
|
||||
length += sprintf(buffer+length, "{\"type\": \"switch\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"int_status\": \"%d\" },",
|
||||
aqdata->aqbuttons[i].name,
|
||||
aqdata->aqbuttons[i].label,
|
||||
aqdata->aqbuttons[i].led->state==ON?JSON_ON:JSON_OFF,
|
||||
LED2text(aqdata->aqbuttons[i].led->state));
|
||||
LED2text(aqdata->aqbuttons[i].led->state),
|
||||
LED2int(aqdata->aqbuttons[i].led->state));
|
||||
}
|
||||
}
|
||||
|
||||
if ( aqdata->frz_protect_set_point != TEMP_UNKNOWN && aqdata->air_temp != TEMP_UNKNOWN) {
|
||||
length += sprintf(buffer+length, "{\"type\": \"setpoint_freeze\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"spvalue\": \"%.*f\", \"value\": \"%.*f\" },",
|
||||
length += sprintf(buffer+length, "{\"type\": \"setpoint_freeze\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"spvalue\": \"%.*f\", \"value\": \"%.*f\", \"int_status\": \"%d\" },",
|
||||
FREEZE_PROTECT,
|
||||
"Freeze Protection",
|
||||
//JSON_OFF,
|
||||
|
@ -183,11 +209,12 @@ int build_device_JSON(struct aqualinkdata *aqdata, int programable_switch, char*
|
|||
((homekit)?2:0),
|
||||
((homekit && aqdata->temp_units==FAHRENHEIT)?degFtoC(aqdata->frz_protect_set_point):aqdata->frz_protect_set_point),
|
||||
((homekit)?2:0),
|
||||
((homekit && aqdata->temp_units==FAHRENHEIT)?degFtoC(aqdata->air_temp):aqdata->air_temp));
|
||||
((homekit && aqdata->temp_units==FAHRENHEIT)?degFtoC(aqdata->air_temp):aqdata->air_temp),
|
||||
aqdata->frz_protect_state==ON?1:0);
|
||||
}
|
||||
|
||||
if ( aqdata->swg_percent != TEMP_UNKNOWN ) {
|
||||
length += sprintf(buffer+length, "{\"type\": \"setpoint_swg\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"spvalue\": \"%.*f\", \"value\": \"%.*f\" },",
|
||||
length += sprintf(buffer+length, "{\"type\": \"setpoint_swg\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"spvalue\": \"%.*f\", \"value\": \"%.*f\", \"int_status\": \"%d\" },",
|
||||
SWG_TOPIC,
|
||||
"Salt Water Generator",
|
||||
aqdata->ar_swg_status == 0x00?JSON_ON:JSON_OFF,
|
||||
|
@ -195,7 +222,8 @@ int build_device_JSON(struct aqualinkdata *aqdata, int programable_switch, char*
|
|||
((homekit)?2:0),
|
||||
((homekit)?degFtoC(aqdata->swg_percent):aqdata->swg_percent),
|
||||
((homekit)?2:0),
|
||||
((homekit)?degFtoC(aqdata->swg_percent):aqdata->swg_percent));
|
||||
((homekit)?degFtoC(aqdata->swg_percent):aqdata->swg_percent),
|
||||
aqdata->ar_swg_status == 0x00?1:0);
|
||||
|
||||
//length += sprintf(buffer+length, "{\"type\": \"value\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"value\": \"%d\" },",
|
||||
length += sprintf(buffer+length, "{\"type\": \"value\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"value\": \"%.*f\" },",
|
||||
|
@ -236,6 +264,11 @@ int build_device_JSON(struct aqualinkdata *aqdata, int programable_switch, char*
|
|||
((homekit)?2:0),
|
||||
((homekit && aqdata->temp_units==FAHRENHEIT)?degFtoC(aqdata->air_temp):aqdata->spa_temp));
|
||||
|
||||
/*
|
||||
length += sprintf(buffer+length, "], \"aux_device_detail\": [");
|
||||
for (i=0; i < MAX_PUMPS; i++) {
|
||||
}
|
||||
*/
|
||||
length += sprintf(buffer+length, "]}");
|
||||
|
||||
logMessage(LOG_DEBUG, "WEB: homebridge used %d of %d", length, size);
|
||||
|
@ -309,7 +342,7 @@ int build_aqualink_status_JSON(struct aqualinkdata *aqdata, char* buffer, int si
|
|||
length += sprintf(buffer+length, ",\"leds\":{" );
|
||||
for (i=0; i < TOTAL_BUTTONS; i++)
|
||||
{
|
||||
char *state;
|
||||
char *state = JSON_OFF;
|
||||
switch (aqdata->aqbuttons[i].led->state)
|
||||
{
|
||||
case ON:
|
||||
|
@ -340,6 +373,17 @@ int build_aqualink_status_JSON(struct aqualinkdata *aqdata, char* buffer, int si
|
|||
length += sprintf(buffer+length, ", \"%s\": \"%s\"", FREEZE_PROTECT, aqdata->frz_protect_state==ON?JSON_ON:JSON_ENABLED);
|
||||
}
|
||||
|
||||
|
||||
length += sprintf(buffer+length, "}, \"extra\":{" );
|
||||
for (i=0; i < MAX_PUMPS; i++) {
|
||||
if (aqdata->pumps[i].rpm != TEMP_UNKNOWN || aqdata->pumps[i].gph != TEMP_UNKNOWN || aqdata->pumps[i].watts != TEMP_UNKNOWN) {
|
||||
length += sprintf(buffer+length, "\"Pump_%d\":{\"RPM\":\"%d\",\"GPH\":\"%d\",\"WATTS\":\"%d\"},",i+1,aqdata->pumps[i].rpm,aqdata->pumps[i].gph,aqdata->pumps[i].watts);
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer[length-1] == ',')
|
||||
length--;
|
||||
|
||||
length += sprintf(buffer+length, "}}" );
|
||||
|
||||
buffer[length] = '\0';
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "aq_serial.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
void logiAqualinkMsg(unsigned char *packet_buffer, int packet_length) {
|
||||
static int pumpIndex = 0;
|
||||
|
||||
/*
|
||||
Pump type are like // Not sure how to read this accuratly.
|
||||
"Jandy ePUMP 1"
|
||||
"Intelliflo VS 1"
|
||||
|
||||
RPM message always comes after the above, so maybe saving last string
|
||||
then when see RPM go back to get pump number.
|
||||
|
||||
' RPM: 2950'
|
||||
' Watts: 1028'
|
||||
*/
|
||||
|
||||
if (packet_buffer[9] == 'R' && packet_buffer[10] == 'P' && packet_buffer[11] == 'M' && packet_buffer[12] == ':')
|
||||
printf("RPM = %d\n", atoi((char *) &packet_buffer[13]));
|
||||
else if (packet_buffer[7] == 'W' && packet_buffer[8] == 'a' && packet_buffer[9] == 't' && packet_buffer[10] == 't' && packet_buffer[11] == 's' && packet_buffer[12] == ':')
|
||||
printf("Watts = %d\n", atoi((char *) &packet_buffer[13]));
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
//unsigned char packet_buffer[10];
|
||||
unsigned char packet_buffer[AQ_MAXPKTLEN];
|
||||
int packet_length = 0;
|
||||
char hex[5];
|
||||
int i;
|
||||
//int num;
|
||||
//int pi;
|
||||
|
||||
if (argc < 1 || access( argv[1], F_OK ) == -1 ) {
|
||||
fprintf(stderr, "ERROR, first param must be valid log file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
FILE *file = fopen ( argv[1], "r" );
|
||||
if ( file == NULL )
|
||||
{
|
||||
perror ( argv[1] ); /* why didn't the file open? */
|
||||
return 1;
|
||||
}
|
||||
|
||||
char line [ 128 ]; /* or other suitable maximum line size */
|
||||
while ( fgets ( line, sizeof line, file ) != NULL ) /* read a line */
|
||||
{
|
||||
packet_length=0;
|
||||
for (i=0; i < strlen(line); i=i+5)
|
||||
{
|
||||
strncpy(hex, &line[i], 4);
|
||||
hex[5] = '\0';
|
||||
packet_buffer[packet_length] = (int)strtol(hex, NULL, 16);
|
||||
//printf("%s = 0x%02hhx = %c\n", hex, packet_buffer[packet_length], packet_buffer[packet_length]);
|
||||
packet_length++;
|
||||
}
|
||||
packet_length--;
|
||||
|
||||
if (packet_buffer[PKT_DEST] == 0x33 && packet_buffer[PKT_CMD] == 0x25)
|
||||
logiAqualinkMsg(packet_buffer, packet_length);
|
||||
|
||||
//if (packet_buffer[PKT_CMD] == 0x24 || packet_buffer[PKT_CMD] == 0x25) {
|
||||
|
||||
printf("To 0x%02hhx, type %15.15s, length %2.2d ", packet_buffer[PKT_DEST], get_packet_type(packet_buffer, packet_length),packet_length);
|
||||
fputs ( line, stdout );
|
||||
//printf("Message : '");
|
||||
//fwrite(packet_buffer + 4, 1, packet_length-7, stdout);
|
||||
//printf("'\n");
|
||||
//}
|
||||
}
|
||||
fclose ( file );
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*** NOTES ***
|
||||
|
||||
iAqualink is ID 0x33
|
||||
Two TEXT Message Types 0x24 & 0x25
|
||||
0x24 seems to be menu options
|
||||
0x25 seems to be status messages
|
||||
|
||||
*/
|
|
@ -194,6 +194,17 @@ void send_mqtt_state_msg(struct mg_connection *nc, char *dev_name, aqledstate st
|
|||
send_mqtt(nc, mqtt_pub_topic, (state==OFF?MQTT_OFF:MQTT_ON));
|
||||
}
|
||||
|
||||
void send_mqtt_aux_msg(struct mg_connection *nc, char *root_topic, int dev_index, char *dev_topic, int value)
|
||||
{
|
||||
static char mqtt_pub_topic[250];
|
||||
static char msg[10];
|
||||
|
||||
sprintf(msg, "%d", value);
|
||||
|
||||
sprintf(mqtt_pub_topic, "%s/%s%d%s",_aqualink_config->mqtt_aq_topic, root_topic, dev_index, dev_topic);
|
||||
send_mqtt(nc, mqtt_pub_topic, msg);
|
||||
}
|
||||
|
||||
void send_mqtt_heater_state_msg(struct mg_connection *nc, char *dev_name, aqledstate state)
|
||||
{
|
||||
|
||||
|
@ -513,6 +524,27 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
|
|||
send_domoticz_mqtt_state_msg(nc, _aqualink_data->aqbuttons[i].dz_idx, (_aqualink_data->aqbuttons[i].led->state==OFF?DZ_OFF:DZ_ON));
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over Pumps
|
||||
for (i=0; i < MAX_PUMPS; i++) {
|
||||
//_aqualink_data->pumps[i].rpm = TEMP_UNKNOWN;
|
||||
//_aqualink_data->pumps[i].gph = TEMP_UNKNOWN;
|
||||
//_aqualink_data->pumps[i].watts = TEMP_UNKNOWN;
|
||||
|
||||
if (_aqualink_data->pumps[i].rpm != TEMP_UNKNOWN && _aqualink_data->pumps[i].rpm != _last_mqtt_aqualinkdata.pumps[i].rpm) {
|
||||
_last_mqtt_aqualinkdata.pumps[i].rpm = _aqualink_data->pumps[i].rpm;
|
||||
send_mqtt_aux_msg(nc, PUMP_TOPIC, i+1, PUMP_RPM_TOPIC, _aqualink_data->pumps[i].rpm);
|
||||
}
|
||||
if (_aqualink_data->pumps[i].gph != TEMP_UNKNOWN && _aqualink_data->pumps[i].gph != _last_mqtt_aqualinkdata.pumps[i].gph) {
|
||||
_last_mqtt_aqualinkdata.pumps[i].gph = _aqualink_data->pumps[i].gph;
|
||||
send_mqtt_aux_msg(nc, PUMP_TOPIC, i+1, PUMP_GPH_TOPIC, _aqualink_data->pumps[i].gph);
|
||||
}
|
||||
if (_aqualink_data->pumps[i].watts != TEMP_UNKNOWN && _aqualink_data->pumps[i].watts != _last_mqtt_aqualinkdata.pumps[i].watts) {
|
||||
_last_mqtt_aqualinkdata.pumps[i].watts = _aqualink_data->pumps[i].watts;
|
||||
send_mqtt_aux_msg(nc, PUMP_TOPIC, i+1, PUMP_WATTS_TOPIC, _aqualink_data->pumps[i].watts);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Loop over LED's and send any changes.
|
||||
for (i=0; i < TOTAL_BUTTONS; i++) {
|
||||
|
@ -1046,6 +1078,7 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
|||
|
||||
case MG_EV_WEBSOCKET_HANDSHAKE_DONE:
|
||||
//nc->user_data = WS;
|
||||
_aqualink_data->open_websockets++;
|
||||
logMessage(LOG_DEBUG, "++ Websocket joined\n");
|
||||
break;
|
||||
|
||||
|
@ -1056,6 +1089,7 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
|||
|
||||
case MG_EV_CLOSE:
|
||||
if (is_websocket(nc)) {
|
||||
_aqualink_data->open_websockets--;
|
||||
logMessage(LOG_DEBUG, "-- Websocket left\n");
|
||||
} else if (is_mqtt(nc)) {
|
||||
logMessage(LOG_WARNING, "MQTT Connection closed\n");
|
||||
|
|
57
pda.c
57
pda.c
|
@ -18,17 +18,43 @@ static unsigned long _pda_loop_cnt = 0;
|
|||
static bool _initWithRS = false;
|
||||
|
||||
// Each RS message is around 0.5 seconds apart, so 2 mins = 120 seconds = 240 polls
|
||||
#define PDA_LOOP_COUNT 240 // 2 mins in poll (sleep timer)
|
||||
//#define PDA_LOOP_COUNT 240 // 2 mins in poll (sleep timer)
|
||||
|
||||
#define PDA_SLEEP_FOR 60 // 30 seconds
|
||||
#define PDA_WAKE_FOR 6 // 3 seconds
|
||||
|
||||
void init_pda(struct aqualinkdata *aqdata)
|
||||
{
|
||||
_aqualink_data = aqdata;
|
||||
set_pda_mode(true);
|
||||
//clock_gettime(CLOCK_REALTIME, &_aqualink_data->last_active_time);
|
||||
//aq_programmer(AQ_PDA_INIT, NULL, _aqualink_data); // NEED TO MOVE THIS. Can't run this here incase serial connection fails / needs to be cleaned up.
|
||||
}
|
||||
|
||||
bool pda_shouldSleep() {
|
||||
//logMessage(LOG_DEBUG, "PDA loop count %d, will sleep at %d\n",_pda_loop_cnt,PDA_LOOP_COUNT);
|
||||
if (_pda_loop_cnt++ < PDA_WAKE_FOR) {
|
||||
return false;
|
||||
} else if (_pda_loop_cnt > PDA_WAKE_FOR + PDA_SLEEP_FOR) {
|
||||
_pda_loop_cnt = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// NSF NEED TO CHECK ACTIVE THREADS.
|
||||
if (_aqualink_data->active_thread.thread_id != 0) {
|
||||
logMessage(LOG_DEBUG, "PDA can't sleep as thread is active");
|
||||
_pda_loop_cnt = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Last see if there are any open websockets. (don't sleep if the web UI is open)
|
||||
if ( _aqualink_data->open_websockets > 0 ) {
|
||||
logMessage(LOG_DEBUG, "PDA can't sleep as websocket is active");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
bool pda_shouldSleep() {
|
||||
//logMessage(LOG_DEBUG, "PDA loop count %d, will sleep at %d\n",_pda_loop_cnt,PDA_LOOP_COUNT);
|
||||
if (_pda_loop_cnt++ < PDA_LOOP_COUNT) {
|
||||
|
@ -40,6 +66,7 @@ bool pda_shouldSleep() {
|
|||
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
|
||||
void pda_wake() {
|
||||
pda_reset_sleep();
|
||||
|
@ -396,12 +423,6 @@ bool process_pda_packet(unsigned char *packet, int length)
|
|||
int i;
|
||||
char *msg;
|
||||
static bool init = false;
|
||||
static time_t _lastStatus = 0;
|
||||
|
||||
if (_lastStatus == 0)
|
||||
{
|
||||
time(&_lastStatus);
|
||||
}
|
||||
|
||||
process_pda_menu_packet(packet, length);
|
||||
|
||||
|
@ -454,21 +475,7 @@ bool process_pda_packet(unsigned char *packet, int length)
|
|||
{
|
||||
pass_pda_equiptment_status_item(pda_m_line(i));
|
||||
}
|
||||
time(&_lastStatus);
|
||||
}
|
||||
else
|
||||
{
|
||||
time_t now;
|
||||
time(&now);
|
||||
if (init && difftime(now, _lastStatus) > 60)
|
||||
{
|
||||
logMessage(LOG_DEBUG, "OVER 60 SECONDS SINCE LAST STATUS UPDATE, forcing refresh\n");
|
||||
// Reset aquapure to nothing since it must be off at this point
|
||||
_aqualink_data->pool_temp = TEMP_UNKNOWN;
|
||||
_aqualink_data->spa_temp = TEMP_UNKNOWN;
|
||||
time(&_lastStatus);
|
||||
aq_programmer(AQ_PDA_DEVICE_STATUS, NULL, _aqualink_data);
|
||||
}
|
||||
|
||||
}
|
||||
if (pda_m_type() == PM_FREEZE_PROTECT_DEVICES)
|
||||
{
|
||||
|
@ -556,4 +563,4 @@ bool process_pda_packet(unsigned char *packet, int length)
|
|||
kick_aq_program_thread(_aqualink_data);
|
||||
}
|
||||
return rtn;
|
||||
}
|
||||
}
|
||||
|
|
313
pda_test.c
313
pda_test.c
|
@ -1,313 +0,0 @@
|
|||
#define _GNU_SOURCE // for strcasestr
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "pda_menu.h"
|
||||
#include "aq_serial.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
|
||||
#define SLOG_MAX 80
|
||||
#define PACKET_MAX 600
|
||||
|
||||
/*
|
||||
typedef enum used {
|
||||
yes,
|
||||
no,
|
||||
unknown
|
||||
} used;
|
||||
*/
|
||||
#define PDA_ID 0x60
|
||||
//#define PDA_ID 0x58
|
||||
#define PDA_LINES 10 // There is only 9 lines, but add buffer to make shifting easier
|
||||
/*
|
||||
typedef struct serial_id_log {
|
||||
unsigned char ID;
|
||||
bool inuse;
|
||||
} serial_id_log;
|
||||
*/
|
||||
|
||||
bool _keepRunning = true;
|
||||
//int _lineindex = -1;
|
||||
//char _menu[PDA_LINES][AQ_MSGLEN+1];
|
||||
//bool _readMenu = false;
|
||||
//unsigned char _goodID[] = {0x0a, 0x0b, 0x08, 0x09};
|
||||
unsigned char _filter = PDA_ID;
|
||||
|
||||
void intHandler(int dummy) {
|
||||
_keepRunning = false;
|
||||
logMessage(LOG_NOTICE, "Stopping!");
|
||||
}
|
||||
|
||||
/*
|
||||
bool canUse(unsigned char ID) {
|
||||
int i;
|
||||
for (i = 0; i < strlen((char *)_goodID); i++) {
|
||||
if (ID == _goodID[i])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
void printHex(char *pk, int length)
|
||||
{
|
||||
int i=0;
|
||||
for (i=0;i<length;i++)
|
||||
{
|
||||
printf("0x%02hhx|",pk[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void printPacket(unsigned char ID, unsigned char *packet_buffer, int packet_length)
|
||||
{
|
||||
//if (_filter != 0x00 && ID != _filter && packet_buffer[PKT_DEST] != _filter )
|
||||
// return;
|
||||
|
||||
if (_filter != 0x00) {
|
||||
if ( packet_buffer[PKT_DEST]==0x00 && ID != _filter )
|
||||
return;
|
||||
if ( packet_buffer[PKT_DEST]!=0x00 && packet_buffer[PKT_DEST] != _filter )
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet_buffer[PKT_DEST] != 0x00)
|
||||
printf("\n");
|
||||
|
||||
printf("%4.4s 0x%02hhx of type %8.8s", (packet_buffer[PKT_DEST]==0x00?"From":"To"), (packet_buffer[PKT_DEST]==0x00?ID:packet_buffer[PKT_DEST]), get_packet_type(packet_buffer, packet_length));
|
||||
printf(" | HEX: ");
|
||||
printHex((char *)packet_buffer, packet_length);
|
||||
|
||||
if (packet_buffer[PKT_CMD] == CMD_MSG || packet_buffer[PKT_CMD] == CMD_MSG_LONG) {
|
||||
printf(" Message : ");
|
||||
//fwrite(packet_buffer + 4, 1, AQ_MSGLEN+1, stdout);
|
||||
fwrite(packet_buffer + 4, 1, packet_length-7, stdout);
|
||||
}
|
||||
|
||||
//if (packet_buffer[PKT_DEST]==0x00)
|
||||
// printf("\n\n");
|
||||
//else
|
||||
printf("\n");
|
||||
}
|
||||
/*
|
||||
int addLine(unsigned char *packet_buffer)
|
||||
{
|
||||
//strcpy (_menu[20], ;
|
||||
|
||||
if (packet_buffer[PKT_DATA] < 10) {
|
||||
memset(_menu[packet_buffer[PKT_DATA]], 0, AQ_MSGLEN);
|
||||
strncpy(_menu[packet_buffer[PKT_DATA]], (char*)packet_buffer+PKT_DATA+1, AQ_MSGLEN);
|
||||
_menu[packet_buffer[PKT_DATA]][AQ_MSGLEN+1] = '\0';
|
||||
//printf ("Added line\n");
|
||||
} else if (packet_buffer[PKT_DATA] == 0x82) {
|
||||
printf("AIR TEMP = %s\n",(char*)packet_buffer+PKT_DATA+1);
|
||||
} else if (packet_buffer[PKT_DATA] == 0x40) {
|
||||
printf("TIME = %s\n",(char*)packet_buffer+PKT_DATA+1);
|
||||
}
|
||||
|
||||
{
|
||||
int i;
|
||||
for (i=0; i < PDA_LINES; i++)
|
||||
printf("Line %d = %s\n",i,_menu[i]);
|
||||
printf("Highlight = %d = %s\n",_lineindex,_menu[_lineindex]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
/*
|
||||
Line 2 = AquaPure 60%
|
||||
Line 3 = SALT 3200 PPM
|
||||
Line 4 = FILTER PUMP
|
||||
Line 5 = SPA HEAT ENA
|
||||
|
||||
Line 4 = POOL MODE ON
|
||||
Line 5 = POOL HEATER OFF
|
||||
Line 6 = SPA MODE OFF
|
||||
Line 7 = SPA HEATER OFF
|
||||
|
||||
Line 1 = FILTER PUMP ON
|
||||
Line 2 = SPA OFF
|
||||
Line 3 = POOL HEAT OFF
|
||||
Line 4 = SPA HEAT OFF
|
||||
Line 5 = CLEANER ON
|
||||
Line 6 = AUX2 OFF
|
||||
Line 7 = AUX3 OFF
|
||||
Line 8 = AUX4 OFF
|
||||
*/
|
||||
bool process_pda_packet(unsigned char* packet, int length)
|
||||
{
|
||||
bool rtn = true;
|
||||
|
||||
process_pda_menu_packet(packet, length);
|
||||
|
||||
switch (packet[PKT_CMD]) {
|
||||
|
||||
case CMD_MSG_LONG: {
|
||||
char *msg = (char*)packet+PKT_DATA+1;
|
||||
|
||||
if (packet[PKT_DATA] == 0x82) { // Air & Water temp is always this ID
|
||||
// message = "73` 66`"
|
||||
//_aqualink_data.temp_units = FAHRENHEIT; // Force FAHRENHEIT
|
||||
//_aqualink_data.air_temp = atoi(msg);
|
||||
//_aqualink_data.pool_temp = atoi(msg+7);
|
||||
printf("Air Temp = %d | Water Temp = %d\n",atoi(msg),atoi(msg+7));
|
||||
} else if (packet[PKT_DATA] == 0x40) {
|
||||
// message " SAT 8:46AM"
|
||||
|
||||
//NSF strcpy(_aqualink_data.time, msg);
|
||||
if (msg[8] == ' ')
|
||||
printf("TIME = %.*s\n",6,msg+9);
|
||||
else
|
||||
printf("TIME = %.*s\n",7,msg+8);
|
||||
|
||||
// NSF strcpy(_aqualink_data.date, msg);
|
||||
if (msg[4] == ' ')
|
||||
printf("DAY = %.*s\n",3,msg+5);
|
||||
else
|
||||
printf("DAY = %.*s\n",3,msg+4);
|
||||
// NSF Come back and change the above to correctly check date and time in future
|
||||
} else if (pda_m_hlightindex() == -1) { // There is a chance this is a message we are interested in.
|
||||
char *index;
|
||||
if( (index = strcasestr(msg, MSG_SWG_PCT)) != NULL) {
|
||||
int aq = atoi(index + strlen(MSG_SWG_PCT));
|
||||
printf("Aquapure PERCENT message %d\n", aq);
|
||||
}
|
||||
if( (index = strcasestr(msg, MSG_SWG_PPM)) != NULL) {
|
||||
int aq = atoi(index + strlen(MSG_SWG_PPM));
|
||||
printf("Aquapure SALT message %d\n", aq);
|
||||
}
|
||||
} else if (strcmp(pda_m_line(0), " EQUIPMENT ") != 0) {
|
||||
// Check message for status of device
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return rtn;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int rs_fd;
|
||||
int packet_length;
|
||||
unsigned char packet_buffer[AQ_MAXPKTLEN];
|
||||
unsigned char lastID;
|
||||
int i = 0;
|
||||
//bool found;
|
||||
//serial_id_log slog[SLOG_MAX];
|
||||
//int sindex = 0;
|
||||
int received_packets = 0;
|
||||
int logPackets = PACKET_MAX;
|
||||
int logLevel = LOG_NOTICE;
|
||||
unsigned char NextMsg = NUL;
|
||||
//int logLevel;
|
||||
//char buffer[256];
|
||||
//bool idMode = true;
|
||||
|
||||
if (getuid() != 0) {
|
||||
fprintf(stderr, "ERROR %s Can only be run as root\n", argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (argc < 2 || access( argv[1], F_OK ) == -1 ) {
|
||||
fprintf(stderr, "ERROR, first param must be valid serial port, ie:-\n\t%s /dev/ttyUSB0\n\n", argv[0]);
|
||||
fprintf(stderr, "Optional parameters are -d (debug) & -p <number> (log # packets) & -i <ID> ie:=\n\t%s /dev/ttyUSB0 -d -p 1000 -i 0x08\n\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
for (i = 2; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-d") == 0) {
|
||||
logLevel = LOG_DEBUG;
|
||||
} else if (strcmp(argv[i], "-p") == 0 && i+1 < argc) {
|
||||
logPackets = atoi(argv[i+1]);
|
||||
} else if (strcmp(argv[i], "-i") == 0 && i+1 < argc) {
|
||||
unsigned int n;
|
||||
sscanf(argv[i+1], "0x%2x", &n);
|
||||
_filter = n;
|
||||
logLevel = LOG_DEBUG; // no point in filtering on ID if we're not going to print it.
|
||||
}
|
||||
}
|
||||
|
||||
setLoggingPrms(logLevel, false, false);
|
||||
|
||||
rs_fd = init_serial_port(argv[1]);
|
||||
|
||||
signal(SIGINT, intHandler);
|
||||
signal(SIGTERM, intHandler);
|
||||
|
||||
logMessage(LOG_NOTICE, "Logging serial information!\n");
|
||||
if (logLevel < LOG_DEBUG)
|
||||
printf("Please wait.");
|
||||
|
||||
while (_keepRunning == true) {
|
||||
if (rs_fd < 0) {
|
||||
logMessage(LOG_ERR, "ERROR, serial port disconnect\n");
|
||||
}
|
||||
|
||||
packet_length = get_packet(rs_fd, packet_buffer);
|
||||
|
||||
if (packet_length == -1) {
|
||||
// Unrecoverable read error. Force an attempt to reconnect.
|
||||
logMessage(LOG_ERR, "ERROR, on serial port\n");
|
||||
_keepRunning = false;
|
||||
} else if (packet_length == 0) {
|
||||
// Nothing read
|
||||
} else if (packet_length > 0) {
|
||||
|
||||
//logMessage(LOG_DEBUG_SERIAL, "Received Packet for ID 0x%02hhx of type %s\n", packet_buffer[PKT_DEST], get_packet_type(packet_buffer, packet_length));
|
||||
if (logLevel > LOG_NOTICE)
|
||||
printPacket(lastID, packet_buffer, packet_length);
|
||||
|
||||
|
||||
if (packet_buffer[PKT_DEST] == PDA_ID) {
|
||||
|
||||
if (packet_buffer[PKT_CMD] == CMD_STATUS) {
|
||||
send_extended_ack(rs_fd, 0x40, NextMsg);
|
||||
NextMsg = NUL;
|
||||
} else {
|
||||
//printf("****** Send ACK *******\n");
|
||||
send_extended_ack(rs_fd, ACK_PDA, NUL);
|
||||
}
|
||||
|
||||
process_pda_packet(packet_buffer, packet_length);
|
||||
|
||||
switch (packet_buffer[PKT_CMD]) {
|
||||
case CMD_MSG_LONG:
|
||||
if (strcasestr(pda_m_line(0), "EQUIPMENT STATUS") != NULL) {
|
||||
//NextMsg = KEY_PDA_BACK;
|
||||
//printf("****** queue BACK *******\n");
|
||||
}
|
||||
//if (_readMenu)
|
||||
// addLine(packet_buffer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
char *selection = pda_m_hlight();
|
||||
if (selection != NULL && strcmp(selection, "EQUIPMENT ON/OFF") != 0) {
|
||||
NextMsg = KEY_PDA_DOWN;
|
||||
} else if (selection != NULL && strcmp(selection, "EQUIPMENT ON/OFF") == 0) {
|
||||
NextMsg = KEY_PDA_SELECT;
|
||||
}
|
||||
|
||||
lastID = packet_buffer[PKT_DEST];
|
||||
received_packets++;
|
||||
}
|
||||
|
||||
if (logPackets != 0 && received_packets >= logPackets) {
|
||||
_keepRunning = false;
|
||||
}
|
||||
|
||||
//sleep(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Binary file not shown.
Binary file not shown.
|
@ -19,13 +19,13 @@ web_directory=/nas/data/Development/Raspberry/AqualinkD/web
|
|||
# DEBUG would print everything possible
|
||||
|
||||
#log_level=DEBUG_SERIAL
|
||||
#log_level=DEBUG
|
||||
log_level=INFO
|
||||
log_level=DEBUG
|
||||
#log_level=INFO
|
||||
#log_level=NOTICE
|
||||
|
||||
# The socket port that the daemon listens to
|
||||
# If you change this from 80, remember to update aqualink.service.avahi
|
||||
socket_port=80
|
||||
socket_port=88
|
||||
|
||||
# The serial port the daemon access to read the Aqualink RS8
|
||||
serial_port=/dev/ttyUSB0
|
||||
|
@ -54,8 +54,6 @@ pda_sleep_mode = yes
|
|||
convert_mqtt_temp_to_c = yes
|
||||
convert_dz_temp_to_c = yes
|
||||
|
||||
# Bug, leave at no for the moment
|
||||
flash_mqtt_buttons = yes
|
||||
|
||||
# by default use pool temp as spa temp when spa is off, enable below to report 0 as spa temp when off.
|
||||
report_zero_spa_temp = yes
|
||||
|
|
Binary file not shown.
BIN
release/pda_test
BIN
release/pda_test
Binary file not shown.
Binary file not shown.
|
@ -137,7 +137,7 @@ int main(int argc, char *argv[]) {
|
|||
int rs_fd;
|
||||
int packet_length;
|
||||
unsigned char packet_buffer[AQ_MAXPKTLEN];
|
||||
unsigned char lastID;
|
||||
unsigned char lastID = 0x00;
|
||||
int i = 0;
|
||||
bool found;
|
||||
serial_id_log slog[SLOG_MAX];
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
|
||||
#define AQUALINKD_NAME "Aqualink Daemon"
|
||||
#define AQUALINKD_VERSION "1.3.0"
|
||||
#define AQUALINKD_VERSION "1.3.1"
|
||||
|
|
|
@ -1106,6 +1106,14 @@
|
|||
for (var obj in data.leds) {
|
||||
setTileOn(obj.toString(), data.leds[obj])
|
||||
}
|
||||
|
||||
// NSF Really quick hack to show RPM on filter pump. Need to come back and do this correctly.
|
||||
if (typeof data.extra["Pump_1"].RPM !== 'undefined') {
|
||||
console.log(data.extra["Pump_1"].RPM);
|
||||
if (document.getElementById('Filter_Pump').getAttribute('status') == 'on') {
|
||||
document.getElementById('Filter_Pump_status').innerHTML = 'RPM:'+data.extra["Pump_1"].RPM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function deviceSort(a, b) {
|
||||
|
|
Loading…
Reference in New Issue