Dev 3.0.0 update

Initial update for serial HAT. (USB not working)
Initial mongoose update (working needs cleanup)
pull/465/head
sfeakes 2025-09-21 18:27:13 -05:00
parent b3219ac0b4
commit 819cab1e7a
28 changed files with 29255 additions and 22408 deletions

View File

@ -51,8 +51,10 @@ DBGFLAGS = -g -O0 -Wall -D AQ_DEBUG -D AQ_TM_DEBUG
# Mongoose flags
#MGFLAGS = -D MG_DISABLE_MD5 -D MG_DISABLE_HTTP_DIGEST_AUTH -D MG_DISABLE_MD5 -D MG_DISABLE_JSON_RPC
# Mongoose 6.18 flags
MGFLAGS = -D MG_ENABLE_HTTP_SSI=0 -D MG_ENABLE_DIRECTORY_LISTING=0 -D MG_ENABLE_HTTP_CGI=0
#MGFLAGS =
#MGFLAGS = -D MG_ENABLE_HTTP_SSI=0 -D MG_ENABLE_DIRECTORY_LISTING=0 -D MG_ENABLE_HTTP_CGI=0
# Mongoose 7.19 flags
MGFLAGS = -D MG_ENABLE_HTTP_SSI=0
# Detect OS and set some specifics
ifeq ($(OS),Windows_NT)

View File

@ -136,9 +136,18 @@ NEED TO FIX FOR THIS RELEASE.
* Use set_allbutton_light_dimmer for all lights (ie color lights)
-->
# Updates in 2.6.12 (dev)
# Updates in 3.0.0 (dev)
* Serial optimization for HAT.
* Added options to force upgrades in aqmanager. (add ?upgrade or ?devupgrade to url to enable upgrade button)
* Need to finish off :-
* ToDo HAT serial optimizations broke some USB serial adapters
* Code cleanup of old stuff. (THREAD_NET_SERVICE / BLOCKING SERIAL PORT)
* Reading TruSense. (Jandy protocols working, need to finish off read_RS485_TruSense support)
* ToDo assigning light mode & functionality to a vbutton (for JAndy Infinate water color support)
* ToDo Cleanup the upgrade of Mongoose code
* ToDo Finish off assigning light mode & functionality to a vbutton (for Jandy Infinite water color support )
* ToDo Remove Domoticz support ???????
* ToDo cleanup rs_msg_utils.c
# Updates in 2.6.11 (Sept 14 2025)
* Cleaned up exit codes.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -689,8 +689,9 @@ aqkey *addVirtualButton(struct aqualinkdata *aqdata, char *label, int vindex) {
snprintf(name, 9, "%s%d", BTN_VAUX, index);
button->name = name;
button->special_mask_ptr = malloc(sizeof(vbutton_detail));
((vbutton_detail *)button->special_mask_ptr)->altlabel = NUL;
// NSF THIS NEEDS TO BE REMOVED
//button->special_mask_ptr = malloc(sizeof(altlabel_detail));
//((altlabel_detail *)button->special_mask_ptr)->altlabel = NUL;
if (label == NULL || strlen(label) <= 0) {
//button->label = name;
@ -701,7 +702,8 @@ aqkey *addVirtualButton(struct aqualinkdata *aqdata, char *label, int vindex) {
button->code = NUL;
button->dz_idx = DZ_NULL_IDX;
button->special_mask |= VIRTUAL_BUTTON; // Could change to special mask vbutton
setButtonSpecialMask(button, VIRTUAL_BUTTON);
//button->special_mask |= VIRTUAL_BUTTON; // Could change to special mask vbutton
button->led->state = OFF;
return button;
@ -727,13 +729,22 @@ bool setVirtualButtonLabel(aqkey *button, const char *label) {
return true;
}
bool setVirtualButtonAltLabel(aqkey *button, const char *label) {
bool setVirtualButtonAltLabel(aqkey *button, char *label) {
if (label == NULL )
return false;
((vbutton_detail *)button->special_mask_ptr)->altlabel = (char *)label;
((vbutton_detail *)button->special_mask_ptr)->in_alt_mode = false;
button->special_mask |= VIRTUAL_BUTTON_ALT_LABEL;
if (! isMASK_SET(button->special_mask, VIRTUAL_BUTTON_ALT_LABEL) ) {
button->special_mask_ptr = malloc(sizeof(altlabel_detail));
((altlabel_detail *)button->special_mask_ptr)->altlabel = cleanalloc(label);
setButtonSpecialMask(button, VIRTUAL_BUTTON_ALT_LABEL);
}
((altlabel_detail *)button->special_mask_ptr)->altlabel = (char *)label;
((altlabel_detail *)button->special_mask_ptr)->in_alt_mode = false;
//setButtonSpecialMask(button, VIRTUAL_BUTTON_ALT_LABEL);
//button->special_mask |= VIRTUAL_BUTTON_ALT_LABEL;
return true;
}
@ -1475,7 +1486,17 @@ void programDeviceLightMode(struct aqualinkdata *aqdata, int value, int deviceIn
char buf[LIGHT_MODE_BUFER];
if (light->lightType == LC_PROGRAMABLE ) {
if (isMASK_SET(light->button->special_mask, VIRTUAL_BUTTON)) {
// We can only program a light on virtual button on iaqtouch or onetouch
if (isIAQT_ENABLED ) {
sprintf(buf, "%-5d%-5d%-5d",value, deviceIndex, light->lightType);
aq_programmer(AQ_SET_IAQTOUCH_LIGHTCOLOR_MODE, buf, aqdata);
} else if (isONET_ENABLED ) {
LOG(PANL_LOG,LOG_ERR, "Light mode on virtual button not implimented on OneTouch protocol (needs AqualinkTouch)\n");
} else {
LOG(PANL_LOG,LOG_ERR, "Light mode on virtual button needs AqualinkTouch protocol\n");
}
} else if (light->lightType == LC_PROGRAMABLE ) {
//sprintf(buf, "%-5s%-5d%-5d%-5d%.2f",value,
sprintf(buf, "%-5d%-5d%-5d%-5d%.2f",value,
deviceIndex,
@ -1483,7 +1504,7 @@ void programDeviceLightMode(struct aqualinkdata *aqdata, int value, int deviceIn
_aqconfig_.light_programming_initial_off,
_aqconfig_.light_programming_mode );
aq_programmer(AQ_SET_LIGHTPROGRAM_MODE, buf, aqdata);
} else if (isRSSA_ENABLED) {
} 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_state(light->button, TRUE);
@ -1678,3 +1699,58 @@ pump_detail *getPumpDetail(struct aqualinkdata *aqdata, int button)
return NULL;
}
const char *getButtontSpecialMaskName(uint16_t mask) {
switch(mask) {
case VS_PUMP:
return "VSpump";
break;
case PROGRAM_LIGHT:
return "lightMode";
break;
case VIRTUAL_BUTTON_ALT_LABEL:
return "altLabel";
break;
case VIRTUAL_BUTTON:
return "Virtual Button";
break;
case VIRTUAL_BUTTON_CHILLER:
return "Virtual Button Chiller";
default:
return "unknown";
break;
}
}
void checkButtonSpecialMask(aqkey *button, uint16_t mask2remove) {
if (isMASK_SET(button->special_mask,mask2remove)) {
LOG(AQUA_LOG,LOG_ERR, "Config error can only have one type for button `%s`, removing `%s`\n",
button->name,
getButtontSpecialMaskName(mask2remove));
removeMASK(button->special_mask, mask2remove);
}
}
void setButtonSpecialMask(aqkey *button, uint16_t mask2set)
{
switch(mask2set) {
case VS_PUMP: // assign struct pump_detail
checkButtonSpecialMask(button, PROGRAM_LIGHT);
checkButtonSpecialMask(button, VIRTUAL_BUTTON_ALT_LABEL);
break;
case PROGRAM_LIGHT: // assign struct clight_detail
checkButtonSpecialMask(button, VIRTUAL_BUTTON_ALT_LABEL);
checkButtonSpecialMask(button, VS_PUMP);
break;
case VIRTUAL_BUTTON_ALT_LABEL: // assign struct altlabel_detail (Maybe delete VIRTUAL_BUTTON)
checkButtonSpecialMask(button, PROGRAM_LIGHT);
checkButtonSpecialMask(button, VS_PUMP);
break;
case VIRTUAL_BUTTON: // Type can be added to above
break;
case VIRTUAL_BUTTON_CHILLER: // Type can be added to above
break;
default:
break;
}
button->special_mask |= mask2set;
}

View File

@ -87,10 +87,11 @@ uint16_t getPanelSupport( char *rev_string, int rev_len);
aqkey *addVirtualButton(struct aqualinkdata *aqdata, char *label, int vindex);
bool setVirtualButtonLabel(aqkey *button, const char *label);
bool setVirtualButtonAltLabel(aqkey *button, const char *label);
bool setVirtualButtonAltLabel(aqkey *button, char *label);
clight_detail *getProgramableLight(struct aqualinkdata *aqdata, int button);
pump_detail *getPumpDetail(struct aqualinkdata *aqdata, int button);
void setButtonSpecialMask(aqkey *button, uint16_t masktoset);
//void panneltest();

View File

@ -26,36 +26,22 @@
// Below is needed to set low latency.
#include <linux/serial.h>
#include "aq_serial.h"
#include "utils.h"
#include "config.h"
#include "packetLogger.h"
#include "timespec_subtract.h"
#include "aqualink.h"
#include <sys/select.h>
/*
Notes for serial usb speed
File should exist if using ftdi chip, ie ftdi_sio driver.
/sys/bus/usb-serial/devices/ttyUSB0/latency_timer
Set to 1 for fastest latency.
#define SERIAL_READ_TIMEOUT_SEC 1;
Can also be set in code
ioctl(fd, TIOCGSERIAL, &serial);
serial.flags |= ASYNC_LOW_LATENCY;
ioctl(fd, TIOCSSERIAL, &serial);
*/
// Default to send command with leading NUL, this changes that
//#define SEND_CMD_WITH_TRAILING_NUL
//#define BLOCKING_MODE
static bool _blocking_mode = false;
static int _blocking_fds = -1;
static struct termios _oldtio;
static int _RS485_fds = -1;
static struct timespec _last_serial_read_time;
@ -420,98 +406,6 @@ protocolType getProtocolType(const unsigned char* packet) {
return P_UNKNOWN;
}
/*
unsigned char getProtocolType(unsigned char* packet) {
if (packet[0] == DLE)
return PCOL_JANDY;
else if (packet[0] == PP1)
return PCOL_PENTAIR;
return PCOL_UNKNOWN;
}
*/
#ifndef PLAYBACK_MODE
/*
Open and Initialize the serial communications port to the Aqualink RS8 device.
Arg is tty or port designation string
returns the file descriptor
*/
//#define TXDEN_DUMMY_RS485_MODE
#ifdef TXDEN_DUMMY_RS485_MODE
#include <linux/serial.h>
/* RS485 ioctls: */
#define TIOCGRS485 0x542E
#define TIOCSRS485 0x542F
int init_serial_port_Pi(const char* tty)
{
struct serial_rs485 rs485conf = {0};
//int fd = open(tty, O_RDWR | O_NOCTTY | O_NONBLOCK);
int fd = open(tty, O_RDWR);
if (fd < 0) {
LOG(RSSD_LOG,LOG_ERR, "Unable to open port: %s\n", tty);
return -1;
}
LOG(RSSD_LOG,LOG_DEBUG_SERIAL, "Openeded serial port %s\n",tty);
if (ioctl (fd, TIOCGRS485, &rs485conf) < 0) {
LOG(RSSD_LOG,LOG_ERR, "Error reading ioctl port (%d): %s\n", errno, strerror( errno ));
return -1;
}
LOG(RSSD_LOG,LOG_DEBUG, "Port currently RS485 mode is %s\n", (rs485conf.flags & SER_RS485_ENABLED) ? "set" : "NOT set");
/* Enable RS485 mode: */
rs485conf.flags |= SER_RS485_ENABLED;
/* Set logical level for RTS pin equal to 1 when sending: */
rs485conf.flags |= SER_RS485_RTS_ON_SEND;
/* or, set logical level for RTS pin equal to 0 when sending: */
//rs485conf.flags &= ~(SER_RS485_RTS_ON_SEND);
/* Set logical level for RTS pin equal to 1 after sending: */
rs485conf.flags |= SER_RS485_RTS_AFTER_SEND;
/* or, set logical level for RTS pin equal to 0 after sending: */
//rs485conf.flags &= ~(SER_RS485_RTS_AFTER_SEND);
/* Set this flag if you want to receive data even whilst sending data */
//rs485conf.flags |= SER_RS485_RX_DURING_TX;
if (ioctl (fd, TIOCSRS485, &rs485conf) < 0) {
LOG(RSSD_LOG,LOG_ERR, "Unable to set port to RS485 %s (%d): %s\n", tty, errno, strerror( errno ));
return -1;
}
return fd;
}
#endif // TXDEN_DUMMY_RS485_MODE
int _init_serial_port(const char* tty, bool blocking, bool readahead);
int init_serial_port(const char* tty)
{
#ifdef AQ_NO_THREAD_NETSERVICE
if (_aqconfig_.rs_poll_speed < 0) {
return init_blocking_serial_port(_aqconfig_.serial_port);
}
#else
return init_blocking_serial_port(_aqconfig_.serial_port);
#endif
}
int init_blocking_serial_port(const char* tty)
{
_blocking_fds = _init_serial_port(tty, true, false);
return _blocking_fds;
}
int set_port_low_latency(int fd, const char* tty)
@ -573,112 +467,137 @@ int is_valid_port(int fd) {
}
void print_file_flags(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1) {
perror("fcntl(F_GETFL)");
return;
}
if (flags & O_RDONLY) {
printf(" - O_RDONLY (Read-only access)\n");
}
if (flags & O_WRONLY) {
printf(" - O_WRONLY (Write-only access)\n");
}
if (flags & O_RDWR) {
printf(" - O_RDWR (Read/write access)\n");
}
if (flags & O_ACCMODE) {
printf(" - O_ACCMODE (mask for above modes)\n");
}
if (flags & O_NONBLOCK) {
printf(" - O_NONBLOCK (Non-blocking I/O)\n");
}
if (flags & O_APPEND) {
printf(" - O_APPEND (Append mode)\n");
}
/*
if (flags & O_SHLOCK) {
printf(" - O_SHLOCK (open with shared file lock)\n");
}
if (flags & O_EXLOCK) {
printf(" - O_EXLOCK (open with exclusive file lock)\n");
}
*/
if (flags & O_ASYNC) {
printf(" - O_ASYNC (Asynchronous I/O signal)\n");
}
if (flags & O_NOFOLLOW) {
printf(" - O_NOFOLLOW (don't follow symlinks)\n");
}
if (flags & O_DSYNC) {
printf(" - O_DSYNC (Synchronous I/O data integrity)\n");
}
if (flags & O_SYNC) {
printf(" - O_SYNC (Synchronous I/O file integrity)\n");
}
if (flags & O_NOCTTY) {
printf(" - O_NOCTTY (don't assign controlling terminal)\n");
}
}
// https://www.cmrr.umn.edu/~strupp/serial.html#2_5_2
// http://unixwiz.net/techtips/termios-vmin-vtime.html
// Unless AQ_RS_EXTRA_OPTS is defined, blocking will always be true
int _init_serial_port(const char* tty, bool blocking, bool readahead)
int init_serial_port(const char* port)
{
//B1200, B2400, B4800, B9600, B19200, B38400, B57600, B115200, B230400
const int BAUD = B9600;
const int PARITY = 0;
struct termios newtio;
struct termios tty;
_blocking_mode = blocking;
// Have to open with O_NONBLOCK so we don't wait for the Data Carrier Detect (DCD) signal to go high
int _RS485_fds = open(port, O_RDWR | O_NOCTTY | O_NONBLOCK | O_CLOEXEC);
//int fd = open(tty, O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY);
int fd = open(tty, O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY | O_CLOEXEC);
//int fd = open(tty, O_RDWR | O_NOCTTY | O_SYNC); // This is way to slow at reading
if (fd < 0) {
LOG(RSSD_LOG,LOG_ERR, "Unable to open port: %s, error %d\n", tty, errno);
if (_RS485_fds < 0) {
LOG(RSSD_LOG,LOG_ERR, "Unable to open port: %s, error %d\n", port, errno);
return -1;
}
LOG(RSSD_LOG,LOG_DEBUG, "Openeded serial port %s\n",tty);
LOG(RSSD_LOG,LOG_DEBUG, "Openeded serial port %s\n",port);
//print_file_flags(fd);
if (tcgetattr(fd, &newtio) != 0) {
LOG(RSSD_LOG,LOG_ERR, "Unable to get port attributes: %s, error %d\n", tty,errno);
return -1;
}
if ( lock_port(fd, tty) < 0) {
//LOG(RSSD_LOG,LOG_ERR, "Unable to lock port: %s, error %d\n", tty, errno);
return -1;
if ( lock_port(_RS485_fds, port) < 0) {
LOG(RSSD_LOG,LOG_ERR, "Unable to lock port: %s, error %d\n", tty, errno);
//return -1;
}
if (_aqconfig_.ftdi_low_latency)
set_port_low_latency(fd, tty);
memcpy(&_oldtio, &newtio, sizeof(struct termios));
cfsetospeed(&newtio, BAUD);
cfsetispeed(&newtio, BAUD);
newtio.c_cflag = (newtio.c_cflag & ~CSIZE) | CS8; // 8-bit chars
// disable IGNBRK for mismatched speed tests; otherwise receive break
// as \000 chars
//newtio.c_iflag &= ~IGNBRK; // disable break processing
newtio.c_iflag = 0; // raw input
newtio.c_lflag = 0; // no signaling chars, no echo,
// no canonical processing
newtio.c_oflag = 0; // no remapping, no delays, raw output
if (_blocking_mode) {
fcntl(fd, F_SETFL, 0); //efficient blocking for the read
//newtio.c_cc[VMIN] = 1; // read blocks for 1 character or timeout below
//newtio.c_cc[VTIME] = 0; // 0.5 seconds read timeout
//newtio.c_cc[VTIME] = 255; // 25 seconds read timeout
//newtio.c_cc[VTIME] = 10; // (1 to 255) 1 = 0.1 sec, 255 = 25.5 sec
newtio.c_cc[VTIME] = SERIAL_BLOCKING_TIME;
newtio.c_cc[VMIN] = 0;
} else {
newtio.c_cc[VMIN]= 0; // read doesn't block
//newtio.c_cc[VTIME]= 1;
newtio.c_cc[VTIME]= (readahead?0:1);
}
/*
Raw output is selected by resetting the OPOST option in the c_oflag member:
newtio.c_oflag &= ~OPOST;
When the OPOST option is disabled, all other option bits in c_oflag are ignored.
*/
//newtio.c_oflag &= ~OPOST; // Raw output
newtio.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl
newtio.c_cflag |= (CLOCAL | CREAD); // ignore modem controls,
// enable reading
newtio.c_cflag &= ~(PARENB | PARODD); // shut off parity
newtio.c_cflag |= PARITY;
newtio.c_cflag &= ~CSTOPB;
newtio.c_cflag &= ~CRTSCTS;
tcflush(fd, TCIFLUSH);
if (tcsetattr(fd, TCSANOW, &newtio) != 0) {
LOG(RSSD_LOG,LOG_ERR, "Unable to set port attributes: %s, error %d\n", tty,errno);
set_port_low_latency(_RS485_fds, port);
if (tcgetattr(_RS485_fds, &tty) != 0) {
LOG(RSSD_LOG,LOG_ERR, "Unable to get port attributes: %s, error %d\n", port,errno);
return -1;
}
LOG(RSSD_LOG,LOG_INFO, "Port %s set I/O %s attributes\n",tty,_blocking_mode?"blocking":"non blocking");
// Set up Modbus protocol in raw mode (no canonical processing)
// This function automatically unsets ICRNL and other processing flags. (stops conversion of 0x0d to 0x0a rc to lf)
cfmakeraw(&tty); // Going to be more precise using below
return fd;
}
// Set baud rates to 9600
cfsetispeed(&tty, B9600);
cfsetospeed(&tty, B9600);
// 8 data bits (CS8), no parity (PARENB cleared), 1 stop bit (CSTOPB cleared)
// Note: CSIZE is a mask, so we must first clear it before setting CS8.
tty.c_cflag &= ~PARENB; // No parity
tty.c_cflag &= ~CSTOPB; // 1 stop bit
tty.c_cflag &= ~CSIZE; // Clear all data bit size flags.
tty.c_cflag |= CS8; // 8 data bits.
// Disable hardware (RTS/CTS) and software (XON/XOFF) flow control
tty.c_cflag &= ~CRTSCTS; // Disable hardware flow control
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Disable software flow control
void close_blocking_serial_port()
{
if (_blocking_fds >= 0) {
LOG(RSSD_LOG,LOG_INFO, "Forcing close of blocking serial port, ignore following read errors\n");
close_serial_port(_blocking_fds);
} else {
LOG(RSSD_LOG,LOG_ERR, "Didn't find valid blocking serial port file descriptor\n");
// Set other control options for "raw" mode
tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // Non-canonical input, disable echo
tty.c_oflag &= ~OPOST; // Raw output
// Enable receiver and ignore modem control lines
tty.c_cflag |= (CREAD | CLOCAL);
// Set timeout for read operations
// VMIN = 0, VTIME > 0 for a read timeout
// In this case, 1 second (10 * 0.1s) timeout.
tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 10;
// Below resets the open with O_NONBLOCK
//fcntl(fd, F_SETFL, 0);
// Write the modified settings
if (tcsetattr(_RS485_fds, TCSANOW, &tty) != 0) {
LOG(RSSD_LOG,LOG_ERR,"Error %i from tcsetattr: %s\n", errno, strerror(errno));
return -1;
}
// Clear out buffer
if (tcflush(_RS485_fds, TCIFLUSH) == -1) {
LOG(RSSD_LOG,LOG_ERR,"Error %i from tcflush: %s\n", errno, strerror(errno));
}
return _RS485_fds;
}
/* close tty port */
void close_serial_port(int fd)
void _close_serial_port(int fd)
{
if ( fcntl(fd, F_GETFD, 0) == -1 || errno == EBADF ) {
// Looks like bad fd or already closed. return with no error since we can get called twice
@ -686,14 +605,23 @@ void close_serial_port(int fd)
}
unlock_port(fd);
tcsetattr(fd, TCSANOW, &_oldtio);
close(fd);
LOG(RSSD_LOG,LOG_DEBUG_SERIAL, "Closed serial port\n");
}
bool serial_blockingmode()
// Can pass NULL for serialport
void close_serial_port(int port)
{
return _blocking_mode;
if (port >= 0) {
_close_serial_port(port);
} else {
if (_RS485_fds < 0) {
LOG(RSSD_LOG,LOG_ERR, "Didn't find valid blocking serial port file descriptor\n");
return;
}
LOG(RSSD_LOG,LOG_INFO, "Forcing close of serial port, ignore following read errors\n");
_close_serial_port(_RS485_fds);
}
}
@ -890,7 +818,7 @@ void send_packet(int fd, unsigned char *packet, int length)
clock_gettime(CLOCK_REALTIME, &now);
if (_blocking_mode) {
if (true) {
//int nwrite = write(fd, packet, length);
//LOG(RSSD_LOG,LOG_DEBUG, "Serial write %d bytes of %d\n",nwrite,length);
int nwrite = write(fd, packet, length);
@ -945,8 +873,7 @@ void send_packet(int fd, unsigned char *packet, int length)
#ifndef SERIAL_LOGGER
if (_aqconfig_.frame_delay > 0) {
timespec_subtract(&elapsed_time, &now, &_last_serial_read_time);
LOG(RSTM_LOG, LOG_DEBUG, "Time from recv to %s send is %.3f sec\n",
(_blocking_mode?"blocking":"non-blocking"),
LOG(RSTM_LOG, LOG_DEBUG, "Time from recv to send is %.3f sec\n",
roundf3(timespec2float(&elapsed_time)));
}
#endif
@ -1144,6 +1071,14 @@ int fix_packet(unsigned char *packet_buffer, int packet_length, bool getCached)
#endif
/*
LXi status | HEX: 0x10|0x02|0x00|0x0d|0x00|0x00|0x00|0x1f|0x10|0x03|
HEX: 0x10|0x02|0x00|0x0a|0x00|0x00|0x00|0x1f|0x10|0x03| // New read seems to use 0x0a and not 0x0d
*/
int get_packet(int fd, unsigned char* packet)
{
unsigned char byte = 0x00;
@ -1161,46 +1096,48 @@ int get_packet(int fd, unsigned char* packet)
struct timespec packet_elapsed;
struct timespec packet_end_time;
int wait_val;
struct timeval read_tv;
read_tv.tv_sec = SERIAL_READ_TIMEOUT_SEC; // 1-second timeout
read_tv.tv_usec = 0;
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
//#ifndef OLD_SERIAL_INIT .. Need to re-do ERROR like EAGAIN with new init
while (!endOfPacket) {
//printf("READ SERIAL\n");
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
// Wait for up to read_tv.tv_sec second for data to become available
wait_val = select(fd + 1, &readfds, NULL, NULL, &read_tv);
if (wait_val == -1) {
return AQSERR_READ;
} else if (wait_val == 0) {
//return AQSERR_TIMEOUT;
return 0; // Should probably change to above
}
bytesRead = read(fd, &byte, 1);
//printf("Read %d 0x%02hhx err=%d fd=%d\n",bytesRead,byte,errno,fd);
//if (bytesRead < 0 && errno == EAGAIN && packetStarted == FALSE && lastByteDLE == FALSE) {
//if (bytesRead < 0 && (errno == EAGAIN || errno == 0) &&
if (bytesRead <= 0 && (errno == EAGAIN || errno == 0 || errno == ENOTTY) ) { // We also get ENOTTY on some non FTDI adapters
if (_blocking_mode) {
// Something is wrong wrong
return AQSERR_TIMEOUT;
} else if (jandyPacketStarted == false && pentairPacketStarted == false && lastByteDLE == false) {
// We just have nothing else to read
if (bytesRead <= 0 && errno == EAGAIN ) { // We also get ENOTTY on some non FTDI adapters
if (jandyPacketStarted == false && pentairPacketStarted == false && lastByteDLE == false) {
return 0;
} else if (++retry > 120 ) {
} else if (++retry > 10 ) {
LOG(RSSD_LOG,LOG_WARNING, "Serial read timeout\n");
//log_packet(LOG_WARNING, "Bad receive packet ", packet, index);
if (index > 0) { logPacketError(packet, index); }
return AQSERR_TIMEOUT;
return AQSERR_READ;
} else {
continue;
}
} else if(bytesRead <= 0) {
if (! isAqualinkDStopping() ) {
return AQSERR_READ;
} else {
return 0;
}
delay(1);
} else if (bytesRead == 1) {
retry = 0;
if (_aqconfig_.log_raw_bytes)
@ -1283,20 +1220,6 @@ int get_packet(int fd, unsigned char* packet)
else if (byte != PP1) // Don't reset counter if multiple PP1's
PentairPreCnt = 0;
}
} else if(bytesRead < 0) {
// Got a read error. Wait one millisecond for the next byte to
// arrive.
if (! isAqualinkDStopping() ) {
LOG(RSSD_LOG,LOG_WARNING, "Read error: %d - %s\n", errno, strerror(errno));
if(errno == 9) {
// Bad file descriptor. Port has been disconnected for some reason.
// Return a -1.
return AQSERR_READ;
}
delay(100);
} else {
return 0;
}
}
// Break out of the loop if we exceed maximum packet
@ -1309,7 +1232,6 @@ int get_packet(int fd, unsigned char* packet)
break;
}
}
// Report any unusual size packets.
if (index >= AQ_MAXPKTLEN_WARNING) {
@ -1388,19 +1310,6 @@ int get_packet(int fd, unsigned char* packet)
#else // PLAYBACKMODE
// Need to re-write this if we ever use playback mode again. Pull info from aq_serial.old.c
#endif

View File

@ -14,8 +14,8 @@ const char *getJandyDeviceName(emulation_type etype);
#define CONNECTION_RUNNING_SLOG "Running serial_logger, this will take some time"
#endif
//#define SERIAL_BLOCKING_TIME 50 // (1 to 255) in 1/10th second so 1 = 0.1 sec, 255 = 25.5 sec
#define SERIAL_BLOCKING_TIME 10
#define SERIAL_BLOCKING_TIME 50 // (1 to 255) in 1/10th second so 1 = 0.1 sec, 255 = 25.5 sec
//#define SERIAL_BLOCKING_TIME 10
// Protocol types
#define PCOL_JANDY 0xFF
@ -575,8 +575,8 @@ int init_blocking_serial_port(const char* tty);
//int init_readahead_serial_port(const char* tty);
void close_serial_port(int file_descriptor);
void close_blocking_serial_port();
bool serial_blockingmode();
//void close_blocking_serial_port();
//bool serial_blockingmode();
//#ifdef AQ_PDA
//void set_pda_mode(bool mode);

View File

@ -135,17 +135,21 @@ bool copy_file(const char *source_path, const char *destination_path)
return true;
}
bool run_aqualinkd_upgrade(bool onlycheck)
bool run_aqualinkd_upgrade(uint8_t type)
{
int pipe_curl_to_bash[2];
pid_t pid_curl, pid_bash;
//char *curl_args[] = {"curl", "-fsSl", "http://tiger/scratch/remote_install.sh", NULL};
char *curl_args[] = {"curl", "-fsSl", "-H", "Accept: application/vnd.github.raw", "https://api.github.com/repos/AqualinkD/AqualinkD/contents/release/remote_install.sh", NULL};
char *bash_args[] = {"bash", "-s", "--", "check", NULL};
char *bash_args[] = {"bash", "-s", "--", "", NULL};
int status_curl, status_bash;
if (!onlycheck) {
bash_args[3] = NULL;
if (isMASK_SET(type, CHECKONLY)) {
bash_args[3] = "check";
} else {
if (isMASK_SET(type, INSTALLDEVRELEASE)) {
bash_args[3] = "development";
}
}
if (pipe(pipe_curl_to_bash) == -1)

View File

@ -5,7 +5,7 @@
FILE *aq_open_file( char *filename, bool *ro_root, bool* created_file);
bool aq_close_file(FILE *file, bool ro_root);
bool copy_file(const char *source_path, const char *destination_path);
bool run_aqualinkd_upgrade(bool onlycheck);
bool run_aqualinkd_upgrade(uint8_t type);

View File

@ -17,26 +17,14 @@
#define SIGRESTART SIGUSR1
#define SIGRUPGRADE SIGUSR2
#ifdef AQ_NO_THREAD_NETSERVICE
#define DEFAULT_POLL_SPEED -1
#define DEFAULT_POLL_SPEED_NON_THREADDED 2
#endif
#define CLIGHT_PANEL_FIX // Overcome bug in some jandy panels where color light status of on is not in LED status
#define TIME_CHECK_INTERVAL 3600
//#define TIME_CHECK_INTERVAL 100 // DEBUG ONLY
#define ACCEPTABLE_TIME_DIFF 120
// Use these settings to test time
//#define TIME_CHECK_INTERVAL 100
//#define ACCEPTABLE_TIME_DIFF 10
#define MAX_ZERO_READ_BEFORE_RECONNECT_NONBLOCKING 100000 // 10k normally
#define MAX_ZERO_READ_BEFORE_RECONNECT_BLOCKING (25 / (SERIAL_BLOCKING_TIME / 10) ) // Want this to be 25 seconds, so it's depdand on how long the serial blocking is
// Time in ms to delay between read requests in non blocking serial port. Have to set something to stop CPU spiking.
#define NONBLOCKING_SERIAL_DELAY 2
#define MAX_ZERO_READ_BEFORE_RECONNECT 10
// The below will change state of devices before that are actually set on the control panel, this helps
// with duplicate messages that come in quick succession that can catch the state before it happens.
@ -257,6 +245,12 @@ typedef enum panel_vsp_status
#define ERROR_NO_DEVICE_ID ( 1 << 8 ) // maybe covered in NOT_CONNECTED
#define ERROR_SERIAL ( 1 << 9 )
#define INSTALLDEVRELEASE ( 1 << 0 )
#define UPDATERELEASE ( 1 << 1 )
#define CHECKONLY ( 1 << 3 )
typedef struct pumpd
{
int rpm;
@ -304,13 +298,13 @@ typedef enum {
MD_HEATPUMP
} heatmump_mode;
*/
typedef struct vbuttond
typedef struct altlabeld
{
char *altlabel;
bool in_alt_mode; // Example if altlabel="chiller", if last seen was chiller message this is true.
//heatmump_mode chiller_mode;
// Add any other special params for virtual button
} vbutton_detail;
} altlabel_detail;
typedef enum {
NET_MQTT=0,
@ -343,7 +337,7 @@ struct aqualinkdata
uint16_t status_mask;
char version[AQ_MSGLEN*2]; // Will be replaced by below in future
char revision[AQ_MSGLEN]; // Will be replaced by below in future
uint8_t updatetype;
// The below 4 are set (sometimes) but not used yet
char panel_rev[AQ_MSGLEN]; // From panel
char panel_cpu[AQ_MSGLEN]; // From panel

View File

@ -104,7 +104,7 @@ bool isAqualinkDStopping() {
void intHandler(int sig_num)
{
if (sig_num == SIGRUPGRADE) {
if (! run_aqualinkd_upgrade(false)) {
if (! run_aqualinkd_upgrade(_aqualink_data.updatetype)) {
LOG(AQUA_LOG,LOG_ERR, "AqualinkD upgrade failed!\n");
}
return; // Let the upgrade process terminate us.
@ -132,20 +132,9 @@ void intHandler(int sig_num)
//LOG(AQUA_LOG,LOG_NOTICE, "Stopping!\n");
//if (dummy){}// stop compile warnings
// In blocking mode, die as cleanly as possible.
#ifdef AQ_NO_THREAD_NETSERVICE
if (_aqconfig_.rs_poll_speed < 0) {
stopPacketLogger();
// This should force port to close and do somewhat gracefull exit.
close_blocking_serial_port();
//exit(-1);
}
#else
stopPacketLogger();
// This should force port to close and do somewhat gracefull exit.
if (serial_blockingmode())
close_blocking_serial_port();
#endif
close_serial_port(-1);
}
bool isVirtualButtonEnabled() {
@ -941,7 +930,7 @@ void main_loop()
bool got_probe_extended = false;
bool got_probe_rssa = false;
bool print_once = false;
int blank_read_reconnect = MAX_ZERO_READ_BEFORE_RECONNECT_BLOCKING; // Will get reset if non blocking
int blank_read_reconnect = MAX_ZERO_READ_BEFORE_RECONNECT; // Will get reset if non blocking
bool auto_config_complete = true;
@ -1067,8 +1056,8 @@ void main_loop()
AddAQDstatusMask(ERROR_SERIAL);
}
if (!serial_blockingmode())
blank_read_reconnect = MAX_ZERO_READ_BEFORE_RECONNECT_NONBLOCKING;
//if (!serial_blockingmode())
// blank_read_reconnect = MAX_ZERO_READ_BEFORE_RECONNECT_NONBLOCKING;
#ifdef AQ_PDA
if (isPDA_PANEL) {
@ -1207,15 +1196,7 @@ void main_loop()
else if (packet_length <= 0) {
blank_read++;
#ifdef AQ_NO_THREAD_NETSERVICE
if (_aqconfig_.rs_poll_speed < 0)
LOG(AQUA_LOG,LOG_DEBUG, "Blank RS485 read\n");
else
delay(2);
#else
if (serial_blockingmode())
LOG(AQUA_LOG,LOG_DEBUG, "Blank RS485 read\n");
#endif
LOG(AQUA_LOG,LOG_DEBUG, "Blank RS485 read\n");
}
else if (packet_length > 0) {
blank_read = 0;
@ -1306,10 +1287,6 @@ void main_loop()
LOG(AQUA_LOG,LOG_NOTICE, "Starting communication with Control Panel\n");
// Not the best way to do this, but ok for moment
#ifdef AQ_NO_THREAD_NETSERVICE
if (_aqconfig_.rs_poll_speed == 0)
blank_read_reconnect = blank_read_reconnect * 50;
#endif
//int loopnum=0;
blank_read = 0;
@ -1331,10 +1308,6 @@ void main_loop()
//broadcast_aqualinkstate_error(CONNECTION_ERROR);
broadcast_aqualinkstate_error(getAqualinkDStatusMessage(&_aqualink_data));
sleep(10);
#ifdef AQ_NO_THREAD_NETSERVICE
poll_net_services(1000);
poll_net_services(3000);
#endif
// broadcast_aqualinkstate_error(mgr.active_connections, "No connection to RS control panel");
}
else
@ -1368,29 +1341,26 @@ void main_loop()
}
#endif
packet_length = get_packet(rs_fd, packet_buffer);
if (packet_length <= 0)
{
// AQSERR_2SMALL // no reset (-5)
// AQSERR_2LARGE // no reset (-4)
// AQSERR_CHKSUM // no reset (-3)
// AQSERR_TIMEOUT // reset blocking mode (-2)
// AQSERR_READ // reset (-1)
#ifdef AQ_NO_THREAD_NETSERVICE
if (_aqconfig_.rs_poll_speed < 0) {
#else
if (serial_blockingmode() && (packet_length == AQSERR_READ || packet_length == AQSERR_TIMEOUT) ) {
#endif
LOG(AQUA_LOG,LOG_ERR, "Nothing read on blocking serial port\n");
blank_read = blank_read_reconnect;
// AQSERR_READ // reset (-1)
if (packet_length == AQSERR_TIMEOUT) {
LOG(AQUA_LOG,LOG_WARNING, "Timeout read on serial port\n");
//blank_read = blank_read_reconnect;
} else if (packet_length == AQSERR_READ) {
LOG(AQUA_LOG,LOG_ERR, "Error read on serial port, resetting\n");
blank_read = blank_read_reconnect;
} else {
// In non blocking, so sleep for 2 milliseconds
delay(NONBLOCKING_SERIAL_DELAY);
//delay(NONBLOCKING_SERIAL_DELAY);
LOG(AQUA_LOG,LOG_WARNING, "Nothing read on serial port\n");
}
//if (blank_read > max_blank_read) {
// LOG(AQUA_LOG,LOG_NOTICE, "Nothing read on serial %d\n",blank_read);
@ -1495,19 +1465,7 @@ void main_loop()
} else {
DEBUG_TIMER_CLEAR(_rs_packet_timer); // Clear timer, no need to print anything
}
#ifdef AQ_NO_THREAD_NETSERVICE
if (_aqualink_data.updated) {
broadcast_aqualinkstate();
}
#endif
}
#ifdef AQ_NO_THREAD_NETSERVICE
poll_net_services(packet_length>0?0:_aqconfig_.rs_poll_speed); // Don;t wait if we read something.
#endif
// NSF might want to wait if we are on a non blocking serial port.
// Any unactioned commands
if (_aqualink_data.unactioned.type != NO_ACTION)
{

View File

@ -60,6 +60,7 @@ 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);
bool populateAltLabel(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);
@ -168,6 +169,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 +205,16 @@ void init_parameters (struct aqconfig * parms)
_cfgParams[_numCfgParams].valid_values = CFG_V_log_level;
_cfgParams[_numCfgParams].default_value = (void *) &_dcfg_loglevel;
_numCfgParams++;
_cfgParams[_numCfgParams].value_ptr = &_aqconfig_.mg_log_level;
_cfgParams[_numCfgParams].value_type = CFG_INT; // Set with _aqconfig_.log_level = text2elevel(cleanalloc(value));
_cfgParams[_numCfgParams].name = CFG_N_MG_log_level;
_cfgParams[_numCfgParams].config_mask |= CFG_READONLY;
_cfgParams[_numCfgParams].default_value = (void *) &_dcfg_zero;
_numCfgParams++;
_cfgParams[_numCfgParams].value_ptr = &_aqconfig_.web_directory;
_cfgParams[_numCfgParams].value_type = CFG_STRING;
@ -691,11 +703,6 @@ void init_parameters (struct aqconfig * parms)
parms->log_protocol_packets = false; // Read & Write as packets write to file
parms->log_raw_bytes = false; // bytes read and write to file
#ifdef AQ_NO_THREAD_NETSERVICE
parms->rs_poll_speed = DEFAULT_POLL_SPEED;
parms->thread_netservices = true;
#endif
parms->device_pre_state = true;
clearDebugLogMask();
@ -707,103 +714,6 @@ void init_parameters (struct aqconfig * parms)
generate_mqtt_id(parms->mqtt_ID, MQTT_ID_LEN);
set_config_defaults();
//set_config_defaults();
//int i;
//char *p;
//parms->rs_panel_size = 8;
/*
parms->serial_port = DEFAULT_SERIALPORT;
parms->log_level = DEFAULT_LOG_LEVEL;
parms->socket_port = DEFAULT_WEBPORT;
parms->web_directory = DEFAULT_WEBROOT;
//parms->device_id = strtoul(DEFAULT_DEVICE_ID, &p, 16);
parms->device_id = strtoul(DEFAULT_DEVICE_ID, NULL, 16);
parms->rssa_device_id = NUL;
*/
/*
parms->extended_device_id = NUL;
parms->extended_device_id_programming = false;
*/
/*
//sscanf(DEFAULT_DEVICE_ID, "0x%x", &parms->device_id);
parms->override_freeze_protect = FALSE;
parms->mqtt_dz_sub_topic = DEFAULT_MQTT_DZ_OUT;
parms->mqtt_dz_pub_topic = DEFAULT_MQTT_DZ_IN;
parms->mqtt_hass_discover_topic = DEFAULT_HASS_DISCOVER;
parms->mqtt_aq_topic = DEFAULT_MQTT_AQ_TP;
parms->mqtt_server = DEFAULT_MQTT_SERVER;
parms->mqtt_user = DEFAULT_MQTT_USER;
parms->mqtt_passwd = DEFAULT_MQTT_PASSWD;
parms->mqtt_hass_discover_use_mac = false;
parms->dzidx_air_temp = TEMP_UNKNOWN;
parms->dzidx_pool_water_temp = TEMP_UNKNOWN;
parms->dzidx_spa_water_temp = TEMP_UNKNOWN;
parms->dzidx_swg_percent = TEMP_UNKNOWN;
parms->dzidx_swg_ppm = TEMP_UNKNOWN;
parms->dzidx_swg_status = TEMP_UNKNOWN;
//parms->dzidx_pool_thermostat = TEMP_UNKNOWN; // removed until domoticz has a better virtual thermostat
//parms->dzidx_spa_thermostat = TEMP_UNKNOWN; // removed until domoticz has a better virtual thermostat
parms->light_programming_mode = 0;
parms->light_programming_initial_on = 15;
parms->light_programming_initial_off = 12;
//parms->light_programming_button_pool = TEMP_UNKNOWN;
//parms->light_programming_button_spa = TEMP_UNKNOWN;
parms->deamonize = true;
#ifndef AQ_MANAGER
parms->log_file = '\0';
#endif
#ifdef AQ_PDA
parms->pda_sleep_mode = false;
#endif
//parms->onetouch_mode = false;
parms->convert_mqtt_temp = true;
parms->convert_dz_temp = true;
parms->report_zero_pool_temp = true;
parms->report_zero_spa_temp = true;
//parms->read_all_devices = true;
//parms->read_pentair_packets = false;
parms->read_RS485_devmask = 0;
parms->use_panel_aux_labels = false;
//parms->force_swg = false;
//parms->force_ps_setpoints = false;
//parms->force_frzprotect_setpoints = false;
//parms->force_chem_feeder = false;
//parms->swg_pool_and_spa = false;
//parms->swg_zero_ignore = DEFAULT_SWG_ZERO_IGNORE_COUNT;
parms->display_warnings_web = false;
parms->log_protocol_packets = false; // Read & Write as packets write to file
parms->log_raw_bytes = false; // bytes read and write to file
parms->sync_panel_time = true;
#ifdef AQ_NO_THREAD_NETSERVICE
parms->rs_poll_speed = DEFAULT_POLL_SPEED;
parms->thread_netservices = true;
#endif
parms->enable_scheduler = true;
parms->schedule_event_mask = 0;
//parms->sched_chk_poweron = false;
//parms->sched_chk_freezeprotectoff = false;
//parms->sched_chk_boostoff = false;
parms->sched_chk_pumpon_hour = 0;
parms->sched_chk_pumpoff_hour = 0;
parms->ftdi_low_latency = true;
parms->frame_delay = 0;
parms->device_pre_state = true;
*/
}
@ -1127,6 +1037,7 @@ if (strlen(cleanwhitespace(value)) <= 0) {
_aqconfig_.read_RS485_devmask |= READ_RS485_JAN_CHEM_FEDR;
else
_aqconfig_.read_RS485_devmask &= ~ READ_RS485_JAN_CHEM_FEDR;
rtn=true;
} else if ((strncasecmp(param, CFG_N_mqtt_hass_discover_topic, strlen(CFG_N_mqtt_hass_discover_topic)) == 0) ||
(strncasecmp(param, "mqtt_hassio_discover_topic", 26) == 0) ||
(strncasecmp(param, "mqtt_hass_discover_topic", 24) == 0)) {
@ -1273,7 +1184,7 @@ if (strlen(cleanwhitespace(value)) <= 0) {
if ( ! populateLightData(aqdata, param + 10, &aqdata->aqbuttons[num], value) )
{
LOG(AQUA_LOG,LOG_ERR, "Config error, %s=%s Ignored!",param,value);
LOG(AQUA_LOG,LOG_ERR, "Config error, %s Ignored!",param,value);
}
rtn=true;
@ -1318,10 +1229,14 @@ if (strlen(cleanwhitespace(value)) <= 0) {
}
rtn=true;
} else if (strncasecmp(param + 17, "_altLabel", 9) == 0) {
char *label = cleanalloc(value);
aqkey *button = getVirtualButton(aqdata, num);
if (button != NULL) {
setVirtualButtonAltLabel(button, label);
//char *label = cleanalloc(value);
aqkey *vbutton = getVirtualButton(aqdata, num);
if (vbutton != NULL) {
//setVirtualButtonAltLabel(vbutton, label);
if ( ! populateAltLabel(vbutton, value) )
{
LOG(AQUA_LOG,LOG_ERR, "Config error, setting alt_label for %d\n",vbutton->label);
}
} else {
LOG(AQUA_LOG,LOG_WARNING, "Error with '%s', total buttons=%d, config has %d already, ignoring!\n",param, TOTAL_BUTTONS, aqdata->total_buttons);
}
@ -1343,7 +1258,7 @@ if (strlen(cleanwhitespace(value)) <= 0) {
if (vbutton != NULL) {
if ( ! populateLightData(aqdata, param + 18, vbutton, value) )
{
LOG(AQUA_LOG,LOG_ERR, "Config error, %s=%s Ignored!",param,value);
LOG(AQUA_LOG,LOG_ERR, "Config error, couldn't find light. `%s` Ignored!",param);
}
} else {
LOG(AQUA_LOG,LOG_ERR, "Config error, could not find vitrual button for `%s`",param);
@ -1500,6 +1415,20 @@ bool populatePumpData(struct aqualinkdata *aqdata, char *pumpcfg ,aqkey *button,
return true;
}
bool populateAltLabel(aqkey *button, char *value)
{
return setVirtualButtonAltLabel(button, cleanalloc(value));
/*
setButtonSpecialMask(button, VIRTUAL_BUTTON_ALT_LABEL);
button->special_mask_ptr = malloc(sizeof(altlabel_detail));
((altlabel_detail *)button->special_mask_ptr)->altlabel = cleanalloc(value);
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)
{
@ -1533,6 +1462,8 @@ bool populateLightData(struct aqualinkdata *aqdata, char *lightcfg ,aqkey *butto
return false;
}
pump_detail *getPumpFromButtonID(struct aqualinkdata *aqdata, aqkey *button)
{
int pi;
@ -1547,7 +1478,10 @@ pump_detail *getPumpFromButtonID(struct aqualinkdata *aqdata, aqkey *button)
// Create new entry
if (aqdata->num_pumps < MAX_PUMPS) {
//printf ("Creating pump %d\n",button);
button->special_mask |= VS_PUMP;
setButtonSpecialMask(button, VS_PUMP);
//button->special_mask |= VS_PUMP;
button->special_mask_ptr = (void*)&aqdata->pumps[aqdata->num_pumps];
aqdata->pumps[aqdata->num_pumps].button = button;
aqdata->pumps[aqdata->num_pumps].pumpType = PT_UNKNOWN;
@ -1580,13 +1514,16 @@ clight_detail *getLightFromButtonID(struct aqualinkdata *aqdata, aqkey *button)
// Create new entry
if (aqdata->num_lights < MAX_LIGHTS) {
button->special_mask |= PROGRAM_LIGHT;
setButtonSpecialMask(button, PROGRAM_LIGHT);
//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];
}
LOG(AQUA_LOG,LOG_ERR, "Config error couldn't add light, max lights of %d reached\n",MAX_LIGHTS);
return NULL;
}
@ -1764,7 +1701,7 @@ void check_print_config (struct aqualinkdata *aqdata)
if (_aqconfig_.extended_device_id >= 0x30 && _aqconfig_.extended_device_id <= 0x33) {
for (i = 0; i < aqdata->total_buttons; i++)
{
if (isVBUTTON_ALTLABEL(aqdata->aqbuttons[i].special_mask) && (rsm_strmatch(((vbutton_detail *)aqdata->aqbuttons[i].special_mask_ptr)->altlabel, "Chiller") == 0) ){
if (isVBUTTON_ALTLABEL(aqdata->aqbuttons[i].special_mask) && (rsm_strmatch(((altlabel_detail *)aqdata->aqbuttons[i].special_mask_ptr)->altlabel, "Chiller") == 0) ){
aqdata->chiller_button = &aqdata->aqbuttons[i];
//aqdata->chiller_button->special_mask |= VIRTUAL_BUTTON_CHILLER;
setMASK(aqdata->chiller_button->special_mask, VIRTUAL_BUTTON_CHILLER);
@ -1992,7 +1929,7 @@ void check_print_config (struct aqualinkdata *aqdata)
for (i = 0; i < aqdata->total_buttons; i++)
{
//char ext[] = " VSP ID None | AL ID 0 ";
char ext[60];
char ext[120];
ext[0] = '\0';
for (j = 0; j < aqdata->num_pumps; j++) {
if (aqdata->pumps[j].button == &aqdata->aqbuttons[i]) {
@ -2004,16 +1941,16 @@ void check_print_config (struct aqualinkdata *aqdata)
}
for (j = 0; j < aqdata->num_lights; j++) {
if (aqdata->lights[j].button == &aqdata->aqbuttons[i]) {
sprintf(ext,"Light Progm | CTYPE %-1d |",aqdata->lights[j].lightType);
sprintf(ext,"Light Progm %-1d |",aqdata->lights[j].lightType);
}
}
if (isVBUTTON(aqdata->aqbuttons[i].special_mask)) {
if (aqdata->aqbuttons[i].rssd_code != NUL) {
sprintf(ext,"OneTouch %d |",aqdata->aqbuttons[i].rssd_code - 15);
sprintf(ext,"OneTouch %d |",aqdata->aqbuttons[i].rssd_code - 15);
}
}
if (isVBUTTON_ALTLABEL(aqdata->aqbuttons[i].special_mask)) {
sprintf(ext,"%-12s|", ((vbutton_detail *)aqdata->aqbuttons[i].special_mask_ptr)->altlabel);
sprintf(ext,"%-12s|", ((altlabel_detail *)aqdata->aqbuttons[i].special_mask_ptr)->altlabel);
}
if (aqdata->aqbuttons[i].dz_idx > 0) {
sprintf(ext+strlen(ext), "dzidx %-3d",aqdata->aqbuttons[i].dz_idx);
@ -2434,7 +2371,7 @@ bool writeCfg (struct aqualinkdata *aqdata)
}
if (isVBUTTON_ALTLABEL(aqdata->aqbuttons[i].special_mask)) {
fprintf(fp,"%s_altLabel=%s\n", prefix, ((vbutton_detail *)aqdata->aqbuttons[i].special_mask_ptr)->altlabel);
fprintf(fp,"%s_altLabel=%s\n", prefix, ((altlabel_detail *)aqdata->aqbuttons[i].special_mask_ptr)->altlabel);
}
}

View File

@ -45,6 +45,7 @@ struct aqconfig
char *config_file;
char *serial_port;
unsigned int log_level;
unsigned int mg_log_level;
char *socket_port;
char *web_directory;
unsigned char device_id;
@ -112,10 +113,6 @@ struct aqconfig
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
#endif
};
#ifndef CONFIG_C
@ -175,6 +172,8 @@ bool mac(char *buf, int len, bool useDelimiter);
char *cleanalloc(char *str);
char *ncleanalloc(char *str, int length);
const char *pumpType2String(pump_type ptype);
int save_config_js(const char* inBuf, int inSize, char* outBuf, int outSize, struct aqualinkdata *aqdata);
@ -236,6 +235,7 @@ int _numCfgParams;
#define CFG_N_serial_port "serial_port"
#define CFG_N_log_level "log_level"
#define CFG_N_MG_log_level "mg_log_level"
#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"

View File

@ -977,6 +977,7 @@ bool processPacketFromJandyChemFeeder(unsigned char *packet_buffer, int packet_l
LOG(DJAN_LOG, LOG_INFO, "%s\n", msg);
/*
I think the below may be accurate
ph_setpoint = float(raw_data[8]) / 10
acl_setpoint = raw_data[9] * 10
ph_current = float(raw_data[10]) / 10
@ -988,6 +989,61 @@ bool processPacketFromJandyChemFeeder(unsigned char *packet_buffer, int packet_l
// ---- pH ----
float ph_from_counts(int counts, float temp_c) {
// Convert ADC counts + water temperature to pH.
// counts : ADC counts (0..4095)
// temp_c : water temperature in °C
// returns : pH value
const int n_bits = 12;
const float v_ref = 3.3f; // ADC reference voltage
const float v_mid = 1.650f; // mid-rail voltage representing pH 7
const float gain_pH = 12.745f; // amplifier gain
// Step 1: counts -> voltage
float v = ((float)counts / (float)((1 << n_bits) - 1)) * v_ref;
// Step 2: electrode mV after gain removal
float v_elec_mV = (v - v_mid) * 1000.0f / gain_pH;
// Step 3: Nernst slope at water temperature
float slope = 59.16f * (temp_c + 273.15f) / 298.15f; // mV/pH
// Step 4: pH
return 7.0f + (v_elec_mV / slope);
}
// ---- ORP ----
float orp_from_counts(int counts) {
/*
Convert ADC counts to ORP (mV)
counts : ADC counts (0..4095)
returns: ORP in millivolts
*/
return -1909.25f + 0.93294f * (float)counts;
}
// ---- Example usage ----
/*
int main(void) {
int counts_ph = 2341; // example from Group 1
int counts_orp = 2916; // example from Group 2
float temp_c = 38.0f; // water temperature in °C
float ph_value = ph_from_counts(counts_ph, temp_c);
float orp_value = orp_from_counts(counts_orp);
printf("pH = %.2f\n", ph_value);
printf("ORP = %.0f mV\n", orp_value);
return 0;
}
*/
bool processPacketToJandyChemAnalyzer(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata)
{
@ -998,13 +1054,34 @@ bool processPacketToJandyChemAnalyzer(unsigned char *packet_buffer, int packet_l
bool processPacketFromJandyChemAnalyzer(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata, const unsigned char previous_packet_to){
int watertemp = 0;
if (isCOMBO_PANEL && aqdata->aqbuttons[SPA_INDEX].led->state == ON) {
watertemp = aqdata->spa_temp;
LOG(DJAN_LOG, LOG_INFO, "Last panel info pH=%f, ORP=%d, Spa water temp=%d (Spamode)\n",aqdata->ph, aqdata->orp, aqdata->spa_temp );
} else {
watertemp = aqdata->pool_temp;
LOG(DJAN_LOG, LOG_INFO, "Last panel info pH=%f, ORP=%d, Pool water temp=%d\n",aqdata->ph, aqdata->orp, aqdata->pool_temp );
}
if (watertemp <= 0) {
if (previous_packet_to == 0x84){
if (packet_buffer[3] == 0x28 && packet_buffer[5] == 0x01) {
float ph = ph_from_counts( ((packet_buffer[6] * 256) + packet_buffer[7]),
aqdata->temp_units==FAHRENHEIT?roundf(degFtoC(watertemp)):watertemp);
LOG(DJAN_LOG, LOG_INFO, "Guess at caculating pH=%f\n", ph);
}
} else if (previous_packet_to == 0x84){
if (packet_buffer[3] == 0x28 && packet_buffer[5] == 0x02) {
float orp = orp_from_counts( ((packet_buffer[6] * 256) + packet_buffer[7]));
LOG(DJAN_LOG, LOG_INFO, "Guess at caculating ORP=%f\n", orp);
}
}
} else {
LOG(DJAN_LOG, LOG_INFO, "ORP & pH not caculated as watertemp %d is out of range\n", watertemp);
}
return false;
}
@ -1108,7 +1185,7 @@ void processHeatPumpDisplayMessage(char *msg, struct aqualinkdata *aqdata) {
// are we heat pump or chiller
if (stristr(msg,"Chiller") != NULL) {
// NSF Should check alt_mode is Chiller and not Heat Pump
((vbutton_detail *)aqdata->chiller_button->special_mask_ptr)->in_alt_mode = true;
((altlabel_detail *)aqdata->chiller_button->special_mask_ptr)->in_alt_mode = true;
hpstate = HP_COOL;
}
if (stristr(msg," ENA") != NULL) {
@ -1120,7 +1197,7 @@ void processHeatPumpDisplayMessage(char *msg, struct aqualinkdata *aqdata) {
}
LOG(AQUA_LOG,LOG_DEBUG, "Set %s to %s from message '%s'",
((vbutton_detail *)aqdata->chiller_button->special_mask_ptr)->in_alt_mode?((vbutton_detail *)aqdata->chiller_button->special_mask_ptr)->altlabel:aqdata->chiller_button->label,
((altlabel_detail *)aqdata->chiller_button->special_mask_ptr)->in_alt_mode?((altlabel_detail *)aqdata->chiller_button->special_mask_ptr)->altlabel:aqdata->chiller_button->label,
LED2text(aqdata->chiller_button->led->state), msg);
}
@ -1142,9 +1219,9 @@ void updateHeatPumpLed(heatpumpstate state, aqledstate ledstate, struct aqualink
}
if (state == HP_COOL) {
((vbutton_detail *)aqdata->chiller_button->special_mask_ptr)->in_alt_mode = true;
((altlabel_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;
((altlabel_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.
@ -1383,4 +1460,83 @@ packet To 0x00 of type iAq receive read | HEX: 0x10|0x02|0x00|0x31|0x2d|0x06|0x0
*/
/*
**************
TruSense
Below is the repeat look (looks like 2 ID's) = is one for ORP and other pH ?????
First 3 to 0x84, are requests 0x00,0x01,0x03. So ignore that in reply and everything else you get data at
Last 2 bytes (below)
0x02|0x14
0x09|0x21
0x00|0x00
Similar for 0x86 at you get
0x0b|0x6a
As two independent bytes 9 and 33
Unsigned 16-bit, big-endian (0x09 is high byte)
0x09 × 256 + 0x21 = 2304 + 33 = 2337
0x09×256+0x21= 2304+33 =2337
Unsigned 16-bit, little-endian (0x21 is high byte)
0x21 × 256 + 0x09 = 8448 + 9 = 8457
0x21×256+0x09= 8448+9 = 8457
To 0x84 of type Probe | HEX: 0x10|0x02|0x84|0x00|0x96|0x10|0x03|
To 0x00 of type Ack | HEX: 0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
To 0x84 of type Unknown '0x20' | HEX: 0x10|0x02|0x84|0x20|0x00|0xb6|0x10|0x03|
To 0x00 of type iAq PageEnd | HEX: 0x10|0x02|0x00|0x28|0x20|0x00|0x02|0x14|0x70|0x10|0x03|
To 0x84 of type Unknown '0x20' | HEX: 0x10|0x02|0x84|0x20|0x01|0xb7|0x10|0x03|
To 0x00 of type iAq PageEnd | HEX: 0x10|0x02|0x00|0x28|0x20|0x01|0x09|0x21|0x85|0x10|0x03|
To 0x84 of type Unknown '0x20' | HEX: 0x10|0x02|0x84|0x20|0x03|0xb9|0x10|0x03|
To 0x00 of type iAq PageEnd | HEX: 0x10|0x02|0x00|0x28|0x20|0x03|0x00|0x00|0x5d|0x10|0x03|
To 0x86 of type Probe | HEX: 0x10|0x02|0x86|0x00|0x98|0x10|0x03|
To 0x00 of type Ack | HEX: 0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
To 0x86 of type Unknown '0x20' | HEX: 0x10|0x02|0x86|0x20|0x02|0xba|0x10|0x03|
To 0x00 of type iAq PageEnd | HEX: 0x10|0x02|0x00|0x28|0x20|0x02|0x0b|0x6a|0xd1|0x10|0x03|
# list of unique 0x84 returns all represent ORP:810 or pH:7.3 (water temp 100 and 102)
To 0x00 of type Ack | HEX: 0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
To 0x00 of type iAq PageEnd | HEX: 0x10|0x02|0x00|0x28|0x20|0x00|0x02|0x14|0x70|0x10|0x03|
To 0x00 of type iAq PageEnd | HEX: 0x10|0x02|0x00|0x28|0x20|0x01|0x09|0x21|0x85|0x10|0x03|
To 0x00 of type iAq PageEnd | HEX: 0x10|0x02|0x00|0x28|0x20|0x01|0x09|0x22|0x86|0x10|0x03|
To 0x00 of type iAq PageEnd | HEX: 0x10|0x02|0x00|0x28|0x20|0x01|0x09|0x27|0x8b|0x10|0x03|
To 0x00 of type iAq PageEnd | HEX: 0x10|0x02|0x00|0x28|0x20|0x01|0x09|0x28|0x8c|0x10|0x03|
To 0x00 of type iAq PageEnd | HEX: 0x10|0x02|0x00|0x28|0x20|0x03|0x00|0x00|0x5d|0x10|0x03|
# list of unique 0x86 returns all represent ORP:810 or pH:7.3 (water temp 100 and 102)
To 0x00 of type Ack | HEX: 0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
To 0x00 of type iAq PageEnd | HEX: 0x10|0x02|0x00|0x28|0x20|0x02|0x0b|0x54|0xbb|0x10|0x03|
To 0x00 of type iAq PageEnd | HEX: 0x10|0x02|0x00|0x28|0x20|0x02|0x0b|0x55|0xbc|0x10|0x03|
To 0x00 of type iAq PageEnd | HEX: 0x10|0x02|0x00|0x28|0x20|0x02|0x0b|0x57|0xbe|0x10|0x03|
To 0x00 of type iAq PageEnd | HEX: 0x10|0x02|0x00|0x28|0x20|0x02|0x0b|0x59|0xc0|0x10|0x03|
To 0x00 of type iAq PageEnd | HEX: 0x10|0x02|0x00|0x28|0x20|0x02|0x0b|0x69|0xd0|0x10|0x03|
To 0x00 of type iAq PageEnd | HEX: 0x10|0x02|0x00|0x28|0x20|0x02|0x0b|0x6a|0xd1|0x10|0x03|
To 0x00 of type iAq PageEnd | HEX: 0x10|0x02|0x00|0x28|0x20|0x02|0x0b|0x6b|0xd2|0x10|0x03|
To 0x00 of type iAq PageEnd | HEX: 0x10|0x02|0x00|0x28|0x20|0x02|0x0b|0x6c|0xd3|0x10|0x03|
To 0x00 of type iAq PageEnd | HEX: 0x10|0x02|0x00|0x28|0x20|0x02|0x0b|0x6d|0xd4|0x10|0x03|
*/

View File

@ -294,13 +294,13 @@ int matchLabel2Button(const char* pageButtonName, aqkey *aqbutton, int ignorecha
int rtn = rsm_strmatch_ignore(pageButtonName, aqbutton->label, ignorechars);
if (rtn == 0 && isVBUTTON_ALTLABEL(aqbutton->special_mask) ) {
((vbutton_detail *)aqbutton->special_mask_ptr)->in_alt_mode = false;
LOG(IAQT_LOG,LOG_DEBUG, "Virtual Button `%s` is NOT in alternate state of `%s`\n", aqbutton->label, ((vbutton_detail *)aqbutton->special_mask_ptr)->altlabel);
((altlabel_detail *)aqbutton->special_mask_ptr)->in_alt_mode = false;
LOG(IAQT_LOG,LOG_DEBUG, "Virtual Button `%s` is NOT in alternate state of `%s`\n", aqbutton->label, ((altlabel_detail *)aqbutton->special_mask_ptr)->altlabel);
} else if (rtn != 0 && isVBUTTON_ALTLABEL(aqbutton->special_mask) ) {
rtn = rsm_strmatch_ignore(pageButtonName, ((vbutton_detail *)aqbutton->special_mask_ptr)->altlabel,ignorechars );
rtn = rsm_strmatch_ignore(pageButtonName, ((altlabel_detail *)aqbutton->special_mask_ptr)->altlabel,ignorechars );
if (rtn == 0 ) {
((vbutton_detail *)aqbutton->special_mask_ptr)->in_alt_mode = true;
LOG(IAQT_LOG,LOG_DEBUG, "Virtual Button `%s` is in alternate state of `%s`\n", aqbutton->label, ((vbutton_detail *)aqbutton->special_mask_ptr)->altlabel);
((altlabel_detail *)aqbutton->special_mask_ptr)->in_alt_mode = true;
LOG(IAQT_LOG,LOG_DEBUG, "Virtual Button `%s` is in alternate state of `%s`\n", aqbutton->label, ((altlabel_detail *)aqbutton->special_mask_ptr)->altlabel);
}
}

View File

@ -512,7 +512,7 @@ void *set_aqualink_iaqtouch_device_on_off( void *ptr )
button = iaqtFindButtonByLabel(aq_data->aqbuttons[device].label);
if (button == NULL && isVBUTTON_ALTLABEL(aq_data->aqbuttons[device].special_mask) ) { // Try alt button name
button = iaqtFindButtonByLabel(((vbutton_detail *)aq_data->aqbuttons[device].special_mask_ptr)->altlabel);
button = iaqtFindButtonByLabel(((altlabel_detail *)aq_data->aqbuttons[device].special_mask_ptr)->altlabel);
}
if (button == NULL) {
@ -523,7 +523,7 @@ void *set_aqualink_iaqtouch_device_on_off( void *ptr )
button = iaqtFindButtonByLabel(aq_data->aqbuttons[device].label);
if (button == NULL && isVBUTTON_ALTLABEL(aq_data->aqbuttons[device].special_mask) ) { // Try alt button name
button = iaqtFindButtonByLabel(((vbutton_detail *)aq_data->aqbuttons[device].special_mask_ptr)->altlabel);
button = iaqtFindButtonByLabel(((altlabel_detail *)aq_data->aqbuttons[device].special_mask_ptr)->altlabel);
}
// If not found see if page has next

View File

@ -311,7 +311,7 @@ char *get_aux_information(aqkey *button, struct aqualinkdata *aqdata, char *buff
}
if (isVBUTTON_ALTLABEL(button->special_mask))
{
length += sprintf(buffer, ",\"alt_label\":\"%s\", \"in_alt_mode\": \"%s\" ",((vbutton_detail *)button->special_mask_ptr)->altlabel, ((vbutton_detail *)button->special_mask_ptr)->in_alt_mode?JSON_ON:JSON_OFF );
length += sprintf(buffer, ",\"alt_label\":\"%s\", \"in_alt_mode\": \"%s\" ",((altlabel_detail *)button->special_mask_ptr)->altlabel, ((altlabel_detail *)button->special_mask_ptr)->in_alt_mode?JSON_ON:JSON_OFF );
//return buffer;
}
@ -588,6 +588,12 @@ int build_device_JSON(struct aqualinkdata *aqdata, char* buffer, int size, bool
length += sprintf(buffer+length, "]}");
// Really crap test
if (length >= size) {
LOG(NET_LOG,LOG_ERR, "JSON: %s went over buffer size %d of %d\n", homekit?"homebridge":"web", length, size);
buffer[size - 1] = '\0';
}
LOG(NET_LOG,LOG_DEBUG, "JSON: %s used %d of %d\n", homekit?"homebridge":"web", length, size);
buffer[length] = '\0';
@ -717,7 +723,7 @@ int build_aqualink_status_JSON(struct aqualinkdata *aqdata, char* buffer, int si
if ( (ENABLE_CHILLER || aqdata->chiller_set_point != TEMP_UNKNOWN) && aqdata->chiller_button != NULL) {
length += sprintf(buffer+length, ",\"chiller_set_pnt\":\"%d\"",aqdata->chiller_set_point );//"0",
if (isVBUTTON_CHILLER(aqdata->chiller_button->special_mask))
length += sprintf(buffer+length, ",\"chiller_mode\":\"%s\"",((vbutton_detail *)aqdata->chiller_button->special_mask_ptr)->in_alt_mode?"cool":"heat");
length += sprintf(buffer+length, ",\"chiller_mode\":\"%s\"",((altlabel_detail *)aqdata->chiller_button->special_mask_ptr)->in_alt_mode?"cool":"heat");
}
if ( aqdata->air_temp == TEMP_UNKNOWN )
@ -860,7 +866,7 @@ printf("Pump Type %d\n",aqdata->pumps[i].pumpType);
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 );
length += sprintf(buffer+length, "\"%s\": \"%s\",",aqdata->aqbuttons[i].name, ((altlabel_detail *)aqdata->aqbuttons[i].special_mask_ptr)->in_alt_mode?JSON_ON:JSON_OFF );
}
}
if (buffer[length-1] == ',')
@ -1561,7 +1567,7 @@ int build_aqualink_config_JSON(char* buffer, int size, struct aqualinkdata *aq_d
length += result;
} else if ( isVBUTTON_ALTLABEL(aq_data->aqbuttons[i].special_mask)) {
sprintf(buf,"%s_altlabel", prefix);
if ((result = json_cfg_element(buffer+length, size-length, buf, &((vbutton_detail *)aq_data->aqbuttons[i].special_mask_ptr)->altlabel, CFG_STRING, 0, NULL, 0)) <= 0) {
if ((result = json_cfg_element(buffer+length, size-length, buf, &((altlabel_detail *)aq_data->aqbuttons[i].special_mask_ptr)->altlabel, CFG_STRING, 0, NULL, 0)) <= 0) {
LOG(NET_LOG,LOG_ERR, "Config json buffer full in, result truncated! size=%d curently used=%d\n",size,length);
return length;
} else

View File

@ -0,0 +1,45 @@
#ifndef AQ_MONGOOSE_ADDITIONAL_H_
#define AQ_MONGOOSE_ADDITIONAL_H_
/* Flags left for application */
/*. mongoose 6.x had the below that we used & depend on.
#define MG_F_USER_1 (1 << 20)
#define MG_F_USER_2 (1 << 21)
#define MG_F_USER_3 (1 << 22)
#define MG_F_USER_4 (1 << 23)
#define MG_F_USER_5 (1 << 24)
#define MG_F_USER_6 (1 << 25)
struct mg_connection {
....
...
..
unsigned long flags;
}
*/
#define AQ_M_USER_1 (1 << 0)
#define AQ_M_USER_2 (1 << 1)
#define AQ_M_USER_3 (1 << 2)
#define AQ_M_USER_4 (1 << 3)
#define AQ_M_USER_5 (1 << 4)
#define AQ_M_USER_6 (1 << 5)
#define MG_F_USER_1 AQ_M_USER_1
#define MG_F_USER_2 AQ_M_USER_2
#define MG_F_USER_3 AQ_M_USER_3
#define MG_F_USER_4 AQ_M_USER_4
#define MG_F_USER_5 AQ_M_USER_5
/*
struct mg_connection {
unsigned short aq_flags;
}
*/
#endif //AQ_MONGOOSE_ADDITIONAL_H_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@
*
* https://github.com/sfeakes/aqualinkd
*/
#define _GNU_SOURCE 1 // for strcasestr
#include <stdio.h>
#include <stdlib.h>
@ -52,6 +53,12 @@
#include "pda.h"
#endif
struct mg_connection *mg_next(struct mg_mgr *s, struct mg_connection *conn) {
return conn == NULL ? s->conns : conn->next;
}
/*
#if defined AQ_DEBUG || defined AQ_TM_DEBUG
#include "timespec_subtract.h"
@ -79,31 +86,19 @@ void reset_last_mqtt_status();
bool uri_strcmp(const char *uri, const char *string);
//static const char *s_http_port = "8080";
static struct mg_serve_http_opts _http_server_opts;
//static struct mg_serve_http_opts _http_server_opts;
static struct mg_http_serve_opts _http_server_opts;
#ifdef AQ_NO_THREAD_NETSERVICE
static sig_atomic_t s_signal_received = 0;
#endif
static void net_signal_handler(int sig_num) {
//printf("** net_signal_handler **\n");
#ifdef AQ_NO_THREAD_NETSERVICE
if (!_aqconfig_.thread_netservices) {
signal(sig_num, net_signal_handler); // Reinstantiate signal handler to aqualinkd.c
s_signal_received = sig_num;
} else {
intHandler(sig_num); // Force signal handler to aqualinkd.c
}
#else
intHandler(sig_num); // Force signal handler to aqualinkd.c
#endif
}
static int is_websocket(const struct mg_connection *nc) {
//return nc->flags & MG_F_IS_WEBSOCKET && !(nc->flags & MG_F_USER_2); // WS only, not WS simulator
return nc->flags & MG_F_IS_WEBSOCKET;
//return nc->flags & MG_F_IS_WEBSOCKET;
return nc->is_websocket;
}
static void set_websocket_simulator(struct mg_connection *nc) {
nc->flags |= MG_F_USER_2;
@ -128,7 +123,7 @@ static void ws_send(struct mg_connection *nc, char *msg)
{
int size = strlen(msg);
mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, msg, size);
mg_ws_send(nc, msg, size, WEBSOCKET_OP_TEXT);
//LOG(NET_LOG,LOG_DEBUG, "WS: Sent %d characters '%s'\n",size, msg);
}
@ -291,13 +286,13 @@ bool _broadcast_systemd_logmessages(bool aqMgrActive, bool reOpenStaleConnection
if ( (journal = open_journal()) == NULL) {
//printf("Open faied\n");
build_logmsg_JSON(msg, LOG_ERR, "Failed to open journal", WS_LOG_LENGTH,22);
ws_send_logmsg(_mgr.active_connections, msg);
ws_send_logmsg(_mgr.conns, msg);
return false;
}
//printf("Open good %d\n",journal);
if (sd_journal_seek_tail(journal) < 0) {
build_logmsg_JSON(msg, LOG_ERR, "Failed to seek to journal end", WS_LOG_LENGTH,29);
ws_send_logmsg(_mgr.active_connections, msg);
ws_send_logmsg(_mgr.conns, msg);
sd_journal_close(journal);
return false;
}
@ -322,20 +317,20 @@ bool _broadcast_systemd_logmessages(bool aqMgrActive, bool reOpenStaleConnection
{
if (sd_journal_get_data(journal, "MESSAGE", &log, &len) < 0) {
build_logmsg_JSON(msg, LOG_ERR, "Failed to get journal message", WS_LOG_LENGTH,29);
ws_send_logmsg(_mgr.active_connections, msg);
ws_send_logmsg(_mgr.conns, msg);
} else if (sd_journal_get_data(journal, "PRIORITY", &pri, &plen) < 0) {
build_logmsg_JSON(msg, LOG_ERR, "Failed to seek to journal message priority", WS_LOG_LENGTH,42);
ws_send_logmsg(_mgr.active_connections, msg);
ws_send_logmsg(_mgr.conns, msg);
} else {
build_logmsg_JSON(msg, atoi((const char *)pri+9), (const char *)log+8, WS_LOG_LENGTH,(int)len-8);
ws_send_logmsg(_mgr.active_connections, msg);
ws_send_logmsg(_mgr.conns, msg);
cnt=0;
sd_journal_get_cursor(journal, &cursor);
}
}
if (rtn < 0) {
build_logmsg_JSON(msg, LOG_ERR, "Failed to seek to next journal message", WS_LOG_LENGTH,42);
ws_send_logmsg(_mgr.active_connections, msg);
ws_send_logmsg(_mgr.conns, msg);
sd_journal_close(journal);
active = false;
} else if (rtn == 0) {
@ -477,15 +472,21 @@ void _broadcast_aqualinkstate(struct mg_connection *nc)
void send_mqtt(struct mg_connection *nc, const char *toppic, const char *message)
{
static uint16_t msg_id = 0;
//static uint16_t msg_id = 0;
if (toppic == NULL)
return;
if (msg_id >= 65535){msg_id=1;}else{msg_id++;}
//if (msg_id >= 65535){msg_id=1;}else{msg_id++;}
//mg_mqtt_publish(nc, toppic, msg_id, MG_MQTT_QOS(0), message, strlen(message));
mg_mqtt_publish(nc, toppic, msg_id, MG_MQTT_RETAIN | MG_MQTT_QOS(1), message, strlen(message));
//mg_mqtt_publish(nc, toppic, msg_id, MG_MQTT_RETAIN | MG_MQTT_QOS(1), message, strlen(message));
struct mg_mqtt_opts pub_opts = {.topic = mg_str(toppic),
.message = mg_str(message),
.qos = 1,
.retain = true};
uint16_t msg_id = mg_mqtt_pub(nc, &pub_opts);
LOG(NET_LOG,LOG_INFO, "MQTT: Published id=%d: %s %s\n", msg_id, toppic, message);
}
@ -649,7 +650,7 @@ void send_mqtt_temp_msg_new(struct mg_connection *nc, char *dev_name, long value
void send_mqtt_setpoint_msg(struct mg_connection *nc, char *dev_name, long value)
{
static char mqtt_pub_topic[250];
static char degC[10];
static char degC[11];
// Use "not CELS" over "equal FAHR" so we default to FAHR for unknown units
//sprintf(degC, "%.2f", (_aqualink_data->temp_units==FAHRENHEIT && _aqconfig_.convert_mqtt_temp)?degFtoC(value):value );
sprintf(degC, "%.2f", (_aqualink_data->temp_units!=CELSIUS && _aqconfig_.convert_mqtt_temp)?degFtoC(value):value );
@ -659,7 +660,7 @@ void send_mqtt_setpoint_msg(struct mg_connection *nc, char *dev_name, long value
void send_mqtt_numeric_msg(struct mg_connection *nc, char *dev_name, int value)
{
static char mqtt_pub_topic[250];
static char msg[10];
static char msg[11];
sprintf(msg, "%d", value);
sprintf(mqtt_pub_topic, "%s/%s", _aqconfig_.mqtt_aq_topic, dev_name);
@ -667,7 +668,7 @@ void send_mqtt_numeric_msg(struct mg_connection *nc, char *dev_name, int value)
}
void send_mqtt_float_msg(struct mg_connection *nc, char *dev_name, float value) {
static char mqtt_pub_topic[250];
static char msg[10];
static char msg[11];
sprintf(msg, "%.2f", value);
sprintf(mqtt_pub_topic, "%s/%s", _aqconfig_.mqtt_aq_topic, dev_name);
@ -807,13 +808,13 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
}
// Chiller is only on when in_alt_mode = true and led != off
if ( _aqualink_data->chiller_button != NULL && ((vbutton_detail *) _aqualink_data->chiller_button->special_mask_ptr)->in_alt_mode == false ) {
if ( _aqualink_data->chiller_button != NULL && ((altlabel_detail *) _aqualink_data->chiller_button->special_mask_ptr)->in_alt_mode == false ) {
// Chiller is off (in heat pump mode)
if (OFF != _last_mqtt_chiller_led.state) {
_last_mqtt_chiller_led.state = OFF;
send_mqtt_led_state_msg(nc, CHILLER, OFF, MQTT_COOL, MQTT_OFF);
}
} else if (_aqualink_data->chiller_button != NULL && ((vbutton_detail *) _aqualink_data->chiller_button->special_mask_ptr)->in_alt_mode == true ) {
} else if (_aqualink_data->chiller_button != NULL && ((altlabel_detail *) _aqualink_data->chiller_button->special_mask_ptr)->in_alt_mode == true ) {
// post actual LED state, in chiller mode
if (_aqualink_data->chiller_button->led->state != _last_mqtt_chiller_led.state) {
_last_mqtt_chiller_led.state = _aqualink_data->chiller_button->led->state;
@ -1193,6 +1194,12 @@ uriAtype action_URI(request_source from, const char *URI, int uri_length, float
return uActioned;
} else if (strncmp(ri1, "upgrade", 7) == 0 && from == NET_WS) { // Only valid from websocket.
LOG(NET_LOG,LOG_NOTICE, "Received upgrade request!\n");
setMASK(_aqualink_data->updatetype, UPDATERELEASE);
raise(SIGRUPGRADE);
return uActioned;
} else if (strncmp(ri1, "installdevrelease", 17) == 0 && from == NET_WS) { // Only valid from websocket.
LOG(NET_LOG,LOG_NOTICE, "Received install dev release request!\n");
setMASK(_aqualink_data->updatetype, INSTALLDEVRELEASE);
raise(SIGRUPGRADE);
return uActioned;
} else if (strncmp(ri1, "seriallogger", 12) == 0 && from == NET_WS) { // Only valid from websocket.
@ -1201,7 +1208,7 @@ uriAtype action_URI(request_source from, const char *URI, int uri_length, float
_aqualink_data->slogger_packets = round(value);
if (ri2 != NULL) {
//MIN( 19, (ri3 - ri2));
snprintf(_aqualink_data->slogger_ids, MIN( 19, (ri3 - ri2)+1 ), ri2); // 0x01 0x02 0x03 0x04
snprintf(_aqualink_data->slogger_ids, AQ_MIN( 19, (ri3 - ri2)+1 ), ri2); // 0x01 0x02 0x03 0x04
} else {
_aqualink_data->slogger_ids[0] = '\0';
}
@ -1410,7 +1417,7 @@ uriAtype action_URI(request_source from, const char *URI, int uri_length, float
for (i=0; i < _aqualink_data->total_buttons ; i++) {
//if (strncmp(ri1, _aqualink_data->aqbuttons[i].name, strlen(_aqualink_data->aqbuttons[i].name)) == 0 ){
if ( uri_strcmp(ri1, _aqualink_data->aqbuttons[i].name) ||
( isVBUTTON_ALTLABEL(_aqualink_data->aqbuttons[i].special_mask) && uri_strcmp(ri1, ((vbutton_detail *)_aqualink_data->aqbuttons[i].special_mask_ptr)->altlabel)) ) {
( isVBUTTON_ALTLABEL(_aqualink_data->aqbuttons[i].special_mask) && uri_strcmp(ri1, ((altlabel_detail *)_aqualink_data->aqbuttons[i].special_mask_ptr)->altlabel)) ) {
int pi;
for (pi=0; pi < _aqualink_data->num_pumps; pi++) {
if (_aqualink_data->pumps[pi].button == &_aqualink_data->aqbuttons[i]) {
@ -1497,7 +1504,7 @@ uriAtype action_URI(request_source from, const char *URI, int uri_length, float
//if ( uri_strcmp(ri1, _aqualink_data->aqbuttons[i].name) || uri_strcmp(ri1, _aqualink_data->aqbuttons[i].label) )
if ( uri_strcmp(ri1, _aqualink_data->aqbuttons[i].name) || uri_strcmp(ri1, _aqualink_data->aqbuttons[i].label) ||
( isVBUTTON_ALTLABEL(_aqualink_data->aqbuttons[i].special_mask) && uri_strcmp(ri1, ((vbutton_detail *)_aqualink_data->aqbuttons[i].special_mask_ptr)->altlabel)) )
( isVBUTTON_ALTLABEL(_aqualink_data->aqbuttons[i].special_mask) && uri_strcmp(ri1, ((altlabel_detail *)_aqualink_data->aqbuttons[i].special_mask_ptr)->altlabel)) )
{
found = true;
//create_panel_request(from, i, value, istimer);
@ -1550,19 +1557,19 @@ void action_mqtt_message(struct mg_connection *nc, struct mg_mqtt_message *msg)
int tid;
#endif
//unsigned int i;
//LOG(NET_LOG,LOG_DEBUG, "MQTT: topic %.*s %.2f\n",msg->topic.len, msg->topic.p, atof(msg->payload.p));
//LOG(NET_LOG,LOG_DEBUG, "MQTT: topic %.*s %.2f\n",msg->topic.len, msg->topic.buf, atof(msg->data.buf));
// If message doesn't end in set or increment we don't care about it.
if (strncmp(&msg->topic.p[msg->topic.len -4], "/set", 4) != 0 && strncmp(&msg->topic.p[msg->topic.len -10], "/increment", 10) != 0) {
LOG(NET_LOG,LOG_DEBUG, "MQTT: Ignore %.*s %.*s\n",msg->topic.len, msg->topic.p, msg->payload.len, msg->payload.p);
if (strncmp(&msg->topic.buf[msg->topic.len -4], "/set", 4) != 0 && strncmp(&msg->topic.buf[msg->topic.len -10], "/increment", 10) != 0) {
LOG(NET_LOG,LOG_DEBUG, "MQTT: Ignore %.*s %.*s\n",msg->topic.len, msg->topic.buf, msg->data.len, msg->data.buf);
return;
}
LOG(NET_LOG,LOG_DEBUG, "MQTT: topic %.*s %.*s\n",msg->topic.len, msg->topic.p, msg->payload.len, msg->payload.p);
LOG(NET_LOG,LOG_DEBUG, "MQTT: topic %.*s %.*s\n",msg->topic.len, msg->topic.buf, msg->data.len, msg->data.buf);
DEBUG_TIMER_START(&tid);
//Need to do this in a better manor, but for present it's ok.
static char tmp[20];
strncpy(tmp, msg->payload.p, msg->payload.len);
tmp[msg->payload.len] = '\0';
strncpy(tmp, msg->data.buf, msg->data.len);
tmp[msg->data.len] = '\0';
//float value = atof(tmp);
@ -1575,16 +1582,16 @@ void action_mqtt_message(struct mg_connection *nc, struct mg_mqtt_message *msg)
if (rsm_strcmp(tmp, "on")==0 || rsm_strcmp(tmp, "heat")==0 || rsm_strcmp(tmp, "cool")==0)
value = 1;
LOG(NET_LOG,LOG_NOTICE, "MQTT: converted value from '%s' to '%.0f', from message '%.*s'\n",tmp,value,msg->topic.len, msg->topic.p);
LOG(NET_LOG,LOG_NOTICE, "MQTT: converted value from '%s' to '%.0f', from message '%.*s'\n",tmp,value,msg->topic.len, msg->topic.buf);
}
//int val = _aqualink_data->unactioned.value = (_aqualink_data->temp_units != CELSIUS && _aqconfig_.convert_mqtt_temp) ? round(degCtoF(value)) : round(value);
bool convert = (_aqualink_data->temp_units != CELSIUS && _aqconfig_.convert_mqtt_temp)?true:false;
int offset = strlen(_aqconfig_.mqtt_aq_topic)+1;
if ( action_URI(NET_MQTT, &msg->topic.p[offset], msg->topic.len - offset, value, convert, &rtnmsg) == uBad ) {
if ( action_URI(NET_MQTT, &msg->topic.buf[offset], msg->topic.len - offset, value, convert, &rtnmsg) == uBad ) {
// Check if it was something that can't be changed, if so send back current state. Homekit thermostat for SWG and Freezeprotect.
if ( strncmp(&msg->topic.p[offset], FREEZE_PROTECT, strlen(FREEZE_PROTECT)) == 0) {
if ( strncmp(&msg->topic.buf[offset], FREEZE_PROTECT, strlen(FREEZE_PROTECT)) == 0) {
if (_aqualink_data->frz_protect_set_point != TEMP_UNKNOWN ) {
send_mqtt_setpoint_msg(nc, FREEZE_PROTECT, _aqualink_data->frz_protect_set_point);
send_mqtt_string_msg(nc, FREEZE_PROTECT_ENABELED, MQTT_ON);
@ -1592,7 +1599,7 @@ void action_mqtt_message(struct mg_connection *nc, struct mg_mqtt_message *msg)
send_mqtt_string_msg(nc, FREEZE_PROTECT_ENABELED, MQTT_OFF);
}
send_mqtt_string_msg(nc, FREEZE_PROTECT, _aqualink_data->frz_protect_state==ON?MQTT_ON:MQTT_OFF);
} else if ( strncmp(&msg->topic.p[offset], SWG_TOPIC, strlen(SWG_TOPIC)) == 0) {
} else if ( strncmp(&msg->topic.buf[offset], SWG_TOPIC, strlen(SWG_TOPIC)) == 0) {
if (_aqualink_data->swg_led_state != LED_S_UNKNOWN) {
send_mqtt_swg_state_msg(nc, SWG_TOPIC, _aqualink_data->swg_led_state);
send_mqtt_int_msg(nc, SWG_BOOST_TOPIC, _aqualink_data->boost);
@ -1607,7 +1614,7 @@ void action_mqtt_message(struct mg_connection *nc, struct mg_mqtt_message *msg)
float pass_mg_body(struct mg_str *body) {
LOG(NET_LOG,LOG_INFO, "Message body:'%.*s'\n", body->len, body->p);
LOG(NET_LOG,LOG_INFO, "Message body:'%.*s'\n", body->len, body->buf);
// Quick n dirty pass value from either of below.
// value=1.5&arg2=val2
// {"value":"1.5"}
@ -1622,11 +1629,11 @@ float pass_mg_body(struct mg_str *body) {
// NSF Really need to come back and clean this up
for (i=0; i < len; i++) {
if ( body->p[i] == '=' || body->p[i] == ':' ) {
while (!isdigit((unsigned char) body->p[i]) && body->p[i] != '-' && i < len) {i++;}
if ( body->buf[i] == '=' || body->buf[i] == ':' ) {
while (!isdigit((unsigned char) body->buf[i]) && body->buf[i] != '-' && i < len) {i++;}
if(i < len) {
// Need to copy to buffer so we can terminate correctly.
strncpy(buf, &body->p[i], len - i);
strncpy(buf, &body->buf[i], len - i);
buf[len - i] = '\0';
return atof(buf);
}
@ -1636,18 +1643,18 @@ float pass_mg_body(struct mg_str *body) {
return TEMP_UNKNOWN;
}
void log_http_request(int level, char *message, struct http_message *http_msg) {
char *uri = (char *)malloc(http_msg->uri.len + http_msg->query_string.len + 2);
void log_http_request(int level, char *message, struct mg_http_message *http_msg) {
char *uri = (char *)malloc(http_msg->uri.len + http_msg->query.len + 2);
strncpy(uri, http_msg->uri.p, http_msg->uri.len + http_msg->query_string.len + 1);
uri[http_msg->uri.len + http_msg->query_string.len + 1] = '\0';
strncpy(uri, http_msg->uri.buf, http_msg->uri.len + http_msg->query.len + 1);
uri[http_msg->uri.len + http_msg->query.len + 1] = '\0';
LOG(NET_LOG,level, "%s: '%s'\n", message, uri);
free(uri);
}
void action_web_request(struct mg_connection *nc, struct http_message *http_msg) {
void action_web_request(struct mg_connection *nc, struct mg_http_message *http_msg) {
char *msg = NULL;
// struct http_message *http_msg = (struct http_message *)ev_data;
#ifdef AQ_TM_DEBUG
@ -1671,12 +1678,17 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg)
//LOG(NET_LOG,LOG_INFO, "Message request:\n'%.*s'\n", http_msg->message.len, http_msg->message.p);
// If we have a get request, pass it
if (strncmp(http_msg->uri.p, "/api", 4 ) != 0) {
if (strncmp(http_msg->uri.buf, "/api", 4 ) != 0) {
//if (strstr(http_msg->method.p, "GET") && http_msg->query_string.len > 0) {
// log_http_request(LOG_ERR, "Old API stanza requested, ignoring request :", http_msg);
//} else {
DEBUG_TIMER_START(&tid);
mg_serve_http(nc, http_msg, _http_server_opts);
//mg_serve_http(nc, http_msg, _http_server_opts);
//mg_http_serve_file(nc, http_msg, _http_server_opts.root_dir, &_http_server_opts);
mg_http_serve_dir(nc, http_msg, &_http_server_opts);
// _aqconfig_.web_directory
DEBUG_TIMER_STOP(tid, NET_LOG, "action_web_request() serve file took");
//}
//} else if (strstr(http_msg->method.p, "PUT")) {
@ -1686,96 +1698,106 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg)
DEBUG_TIMER_START(&tid);
// If query string.
if (http_msg->query_string.len > 1) {
mg_get_http_var(&http_msg->query_string, "value", buf, sizeof(buf));
if (http_msg->query.len > 1) {
//mg_get_http_var(&http_msg->query, "value", buf, sizeof(buf)); // Old mosquitto
mg_http_get_var(&http_msg->query, "value", buf, sizeof(buf));
value = atof(buf);
} else if (http_msg->body.len > 1) {
value = pass_mg_body(&http_msg->body);
}
int len = mg_url_decode(http_msg->uri.p, http_msg->uri.len, buf, 50, 0);
int len = mg_url_decode(http_msg->uri.buf, http_msg->uri.len, buf, 50, 0);
if (strncmp(http_msg->uri.p, "/api/",4) == 0) {
if (strncmp(http_msg->uri.buf, "/api/",4) == 0) {
switch (action_URI(NET_API, &buf[5], len-5, value, false, &msg)) {
case uActioned:
mg_send_head(nc, 200, strlen(GET_RTN_OK), CONTENT_TEXT);
mg_send(nc, GET_RTN_OK, strlen(GET_RTN_OK));
//mg_send_head(nc, 200, strlen(GET_RTN_OK), CONTENT_TEXT);
//mg_send(nc, GET_RTN_OK, strlen(GET_RTN_OK));
mg_http_reply(nc, 200, CONTENT_TEXT, GET_RTN_OK);
break;
case uDevices:
{
char message[JSON_BUFFER_SIZE];
DEBUG_TIMER_START(&tid2);
int size = build_device_JSON(_aqualink_data, message, JSON_BUFFER_SIZE, false);
build_device_JSON(_aqualink_data, message, JSON_BUFFER_SIZE, false);
DEBUG_TIMER_STOP(tid2, NET_LOG, "action_web_request() build_device_JSON took");
mg_send_head(nc, 200, size, CONTENT_JSON);
mg_send(nc, message, size);
//mg_send_head(nc, 200, size, CONTENT_JSON);
//mg_send(nc, message, size);
mg_http_reply(nc, 200, CONTENT_JSON, message);
}
break;
case uHomebridge:
{
char message[JSON_BUFFER_SIZE];
int size = build_device_JSON(_aqualink_data, message, JSON_BUFFER_SIZE, true);
mg_send_head(nc, 200, size, CONTENT_JSON);
mg_send(nc, message, size);
build_device_JSON(_aqualink_data, message, JSON_BUFFER_SIZE, true);
//mg_send_head(nc, 200, size, CONTENT_JSON);
//mg_send(nc, message, size);
mg_http_reply(nc, 200, CONTENT_JSON, message);
}
break;
case uStatus:
{
char message[JSON_BUFFER_SIZE];
DEBUG_TIMER_START(&tid2);
int size = build_aqualink_status_JSON(_aqualink_data, message, JSON_BUFFER_SIZE);
build_aqualink_status_JSON(_aqualink_data, message, JSON_BUFFER_SIZE);
DEBUG_TIMER_STOP(tid2, NET_LOG, "action_web_request() build_aqualink_status_JSON took");
mg_send_head(nc, 200, size, CONTENT_JSON);
mg_send(nc, message, size);
//mg_send_head(nc, 200, size, CONTENT_JSON);
//mg_send(nc, message, size);
mg_http_reply(nc, 200, CONTENT_JSON, message);
}
break;
case uDynamicconf:
{
char message[JSON_BUFFER_SIZE];
DEBUG_TIMER_START(&tid2);
int size = build_webconfig_js(_aqualink_data, message, JSON_BUFFER_SIZE);
build_webconfig_js(_aqualink_data, message, JSON_BUFFER_SIZE);
DEBUG_TIMER_STOP(tid2, NET_LOG, "action_web_request() build_webconfig_js took");
mg_send_head(nc, 200, size, CONTENT_JS);
mg_send(nc, message, size);
//mg_send_head(nc, 200, size, CONTENT_JS);
//mg_send(nc, message, size);
mg_http_reply(nc, 200, CONTENT_JSON, message);
}
break;
case uSchedules:
{
char message[JSON_BUFFER_SIZE];
DEBUG_TIMER_START(&tid2);
int size = build_schedules_js(message, JSON_BUFFER_SIZE);
build_schedules_js(message, JSON_BUFFER_SIZE);
DEBUG_TIMER_STOP(tid2, NET_LOG, "action_web_request() build_schedules_js took");
mg_send_head(nc, 200, size, CONTENT_JSON);
mg_send(nc, message, size);
//mg_send_head(nc, 200, size, CONTENT_JSON);
//mg_send(nc, message, size);
mg_http_reply(nc, 200, CONTENT_JSON, message);
}
break;
case uSetSchedules:
{
char message[JSON_BUFFER_SIZE];
DEBUG_TIMER_START(&tid2);
int size = save_schedules_js(http_msg->body.p, http_msg->body.len, message, JSON_BUFFER_SIZE);
save_schedules_js(http_msg->body.buf, http_msg->body.len, message, JSON_BUFFER_SIZE);
DEBUG_TIMER_STOP(tid2, NET_LOG, "action_web_request() save_schedules_js took");
mg_send_head(nc, 200, size, CONTENT_JSON);
mg_send(nc, message, size);
//mg_send_head(nc, 200, size, CONTENT_JSON);
//mg_send(nc, message, size);
mg_http_reply(nc, 200, CONTENT_JSON, message);
}
break;
case uConfig:
{
char message[JSON_BUFFER_SIZE];
DEBUG_TIMER_START(&tid2);
int size = build_aqualink_config_JSON(message, JSON_BUFFER_SIZE, _aqualink_data);
build_aqualink_config_JSON(message, JSON_BUFFER_SIZE, _aqualink_data);
DEBUG_TIMER_STOP(tid2, NET_LOG, "action_web_request() build_aqualink_config_JSON took");
mg_send_head(nc, 200, size, CONTENT_JSON);
mg_send(nc, message, size);
//mg_send_head(nc, 200, size, CONTENT_JSON);
//mg_send(nc, message, size);
mg_http_reply(nc, 200, CONTENT_JSON, message);
}
break;
#ifndef AQ_MANAGER
case uDebugStatus:
{
char message[JSON_BUFFER_SIZE];
int size = snprintf(message,80,"{\"sLevel\":\"%s\", \"iLevel\":%d, \"logReady\":\"%s\"}\n",elevel2text(getLogLevel(NET_LOG)),getLogLevel(NET_LOG),islogFileReady()?"true":"false" );
mg_send_head(nc, 200, size, CONTENT_JS);
mg_send(nc, message, size);
snprintf(message,80,"{\"sLevel\":\"%s\", \"iLevel\":%d, \"logReady\":\"%s\"}\n",elevel2text(getLogLevel(NET_LOG)),getLogLevel(NET_LOG),islogFileReady()?"true":"false" );
//mg_send_head(nc, 200, size, CONTENT_JS);
//mg_send(nc, message, size);
mg_http_reply(nc, 200, CONTENT_JS, message);
}
break;
#else
@ -1790,40 +1812,45 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg)
}
LOG(NET_LOG, LOG_DEBUG, "Downloading log of max %d lines\n",value>0?(int)value:DEFAULT_LOG_DOWNLOAD_LINES);
if (write_systemd_logmessages_2file("/dev/shm/aqualinkd.log", value>0?(int)value:DEFAULT_LOG_DOWNLOAD_LINES) ) {
mg_http_serve_file(nc, http_msg, "/dev/shm/aqualinkd.log", mg_mk_str("text/plain"), mg_mk_str("Content-Disposition: attachment; filename=\"aqualinkd.log\""));
//mg_http_serve_file(nc, http_msg, "/dev/shm/aqualinkd.log", mg_mk_str("text/plain"), mg_mk_str("Content-Disposition: attachment; filename=\"aqualinkd.log\""));
mg_http_serve_file(nc, http_msg, "/dev/shm/aqualinkd.log", &_http_server_opts);
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\""));
//mg_http_serve_file(nc, http_msg, _aqconfig_.config_file, mg_mk_str("text/plain"), mg_mk_str("Content-Disposition: attachment; filename=\"aqualinkd.conf\""));
mg_http_serve_file(nc, http_msg, _aqconfig_.config_file, &_http_server_opts);
break;
#endif
case uBad:
default:
if (msg == NULL) {
mg_send_head(nc, 400, strlen(GET_RTN_UNKNOWN), CONTENT_TEXT);
mg_send(nc, GET_RTN_UNKNOWN, strlen(GET_RTN_UNKNOWN));
//mg_send_head(nc, 400, strlen(GET_RTN_UNKNOWN), CONTENT_TEXT);
//mg_send(nc, GET_RTN_UNKNOWN, strlen(GET_RTN_UNKNOWN));
mg_http_reply(nc, 400, CONTENT_TEXT, GET_RTN_UNKNOWN);
} else {
mg_send_head(nc, 400, strlen(msg), CONTENT_TEXT);
mg_send(nc, msg, strlen(msg));
//mg_send_head(nc, 400, strlen(msg), CONTENT_TEXT);
//mg_send(nc, msg, strlen(msg));
mg_http_reply(nc, 400, CONTENT_TEXT, msg);
}
break;
}
} else {
mg_send_head(nc, 200, strlen(GET_RTN_UNKNOWN), CONTENT_TEXT);
mg_send(nc, GET_RTN_UNKNOWN, strlen(GET_RTN_UNKNOWN));
//mg_send_head(nc, 200, strlen(GET_RTN_UNKNOWN), CONTENT_TEXT);
//mg_send(nc, GET_RTN_UNKNOWN, strlen(GET_RTN_UNKNOWN));
mg_http_reply(nc, 400, CONTENT_TEXT, GET_RTN_UNKNOWN);
}
sprintf(buf, "action_web_request() request '%.*s' took",(int)http_msg->uri.len, http_msg->uri.p);
sprintf(buf, "action_web_request() request '%.*s' took",(int)http_msg->uri.len, http_msg->uri.buf);
DEBUG_TIMER_STOP(tid, NET_LOG, buf);
}
}
void action_websocket_request(struct mg_connection *nc, struct websocket_message *wm) {
void action_websocket_request(struct mg_connection *nc, struct mg_ws_message *wm) {
char buffer[100];
struct JSONkvptr jsonkv;
int i;
@ -1839,8 +1866,8 @@ void action_websocket_request(struct mg_connection *nc, struct websocket_message
pda_reset_sleep();
#endif
strncpy(buffer, (char *)wm->data, MIN(wm->size, 99));
buffer[wm->size] = '\0';
strncpy(buffer, (char *)wm->data.buf, AQ_MIN(wm->data.len, 99));
buffer[wm->data.len] = '\0';
parseJSONrequest(buffer, &jsonkv);
@ -1927,7 +1954,7 @@ void action_websocket_request(struct mg_connection *nc, struct websocket_message
{
DEBUG_TIMER_START(&tid);
char message[JSON_BUFFER_SIZE];
save_schedules_js((char *)wm->data, wm->size, message, JSON_BUFFER_SIZE);
save_schedules_js((char *)wm->data.buf, wm->data.len, message, JSON_BUFFER_SIZE);
DEBUG_TIMER_STOP(tid, NET_LOG, "action_websocket_request() save_schedules_js took");
ws_send(nc, message);
}
@ -1945,7 +1972,7 @@ void action_websocket_request(struct mg_connection *nc, struct websocket_message
{
DEBUG_TIMER_START(&tid);
char message[JSON_BUFFER_SIZE];
save_config_js((char *)wm->data, wm->size, message, JSON_BUFFER_SIZE, _aqualink_data);
save_config_js((char *)wm->data.buf, wm->data.len, message, JSON_BUFFER_SIZE, _aqualink_data);
DEBUG_TIMER_STOP(tid, NET_LOG, "action_websocket_request() save_config_js took");
ws_send(nc, message);
}
@ -1967,7 +1994,7 @@ void action_domoticz_mqtt_message(struct mg_connection *nc, struct mg_mqtt_messa
int i;
char svalue[DZ_SVALUE_LEN+1];
if (parseJSONmqttrequest(msg->payload.p, msg->payload.len, &idx, &nvalue, svalue) && idx > 0) {
if (parseJSONmqttrequest(msg->data.buf, msg->data.len, &idx, &nvalue, svalue) && idx > 0) {
for (i=0; i < _aqualink_data->total_buttons; i++) {
if (_aqualink_data->aqbuttons[i].dz_idx == idx){
LOG(NET_LOG,LOG_DEBUG, "MQTT: DZ: Received message IDX=%d nValue=%d sValue=%s\n", idx, nvalue, svalue);
@ -2001,8 +2028,8 @@ void action_domoticz_mqtt_message(struct mg_connection *nc, struct mg_mqtt_messa
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
struct mg_mqtt_message *mqtt_msg;
struct http_message *http_msg;
struct websocket_message *ws_msg;
struct mg_http_message *http_msg;
struct mg_ws_message *ws_msg;
char aq_topic[30];
#ifdef AQ_TM_DEBUG
int tid;
@ -2011,23 +2038,35 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
// LOG(NET_LOG,LOG_DEBUG, "Event\n");
switch (ev) {
case MG_EV_HTTP_REQUEST:
//case MG_EV_HTTP_REQUEST:
case MG_EV_HTTP_MSG:
//nc->user_data = WEB;
http_msg = (struct http_message *)ev_data;
http_msg = (struct mg_http_message *)ev_data;
//if ( strstr(http_msg->head.buf, "Upgrade: websocket") ) {
//if ( strstr(http_msg->head.buf, "Sec-WebSocket-Key") ) {
if ( mg_http_get_header(http_msg, "Sec-WebSocket-Key") != NULL) {
LOG(NET_LOG,LOG_DEBUG, "Enable websockets\n");
mg_ws_upgrade(nc, http_msg, NULL);
break;
}
DEBUG_TIMER_START(&tid);
action_web_request(nc, http_msg);
DEBUG_TIMER_STOP(tid, NET_LOG, "WEB Request action_web_request() took");
LOG(NET_LOG,LOG_DEBUG, "Served WEB request\n");
break;
case MG_EV_WEBSOCKET_HANDSHAKE_DONE:
//case MG_EV_WEBSOCKET_HANDSHAKE_DONE:
case MG_EV_WS_OPEN:
//nc->user_data = WS;
_aqualink_data->open_websockets++;
LOG(NET_LOG,LOG_DEBUG, "++ Websocket joined\n");
break;
case MG_EV_WEBSOCKET_FRAME:
ws_msg = (struct websocket_message *)ev_data;
//case MG_EV_WEBSOCKET_FRAME:
case MG_EV_WS_MSG:
ws_msg = (struct mg_ws_message *)ev_data;
DEBUG_TIMER_START(&tid);
action_websocket_request(nc, ws_msg);
DEBUG_TIMER_STOP(tid, NET_LOG, "Websocket Request action_websocket_request() took");
@ -2051,90 +2090,59 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
break;
case MG_EV_CONNECT: {
//nc->user_data = MQTT;
//nc->flags |= MG_F_USER_1; // NFS Need to readup on this
set_mqtt(nc);
_mqtt_exit_flag = false;
//char *MQTT_id = "AQUALINK_MQTT_TEST_ID";
struct mg_send_mqtt_handshake_opts opts;
memset(&opts, 0, sizeof(opts));
opts.user_name = _aqconfig_.mqtt_user;
opts.password = _aqconfig_.mqtt_passwd;
opts.keep_alive = 5;
opts.flags |= MG_MQTT_CLEAN_SESSION; // NFS Need to readup on this
snprintf(aq_topic, 24, "%s/%s", _aqconfig_.mqtt_aq_topic,MQTT_LWM_TOPIC);
opts.will_topic = aq_topic;
opts.will_message = MQTT_OFF;
mg_set_protocol_mqtt(nc);
mg_send_mqtt_handshake_opt(nc, _aqconfig_.mqtt_ID, opts);
LOG(NET_LOG,LOG_INFO, "MQTT: Subscribing mqtt with id of: %s\n", _aqconfig_.mqtt_ID);
//last_control_time = mg_time();
LOG(NET_LOG,LOG_DEBUG, "MQTT: Connected to : %s, id : %s\n", _aqconfig_.mqtt_server, _aqconfig_.mqtt_ID);
} break;
case MG_EV_MQTT_CONNACK:
//case MG_EV_MQTT_CONNACK:
case MG_EV_MQTT_OPEN:
{
struct mg_mqtt_topic_expression topics[2];
int qos=0;// can't be bothered with ack, so set to 0
//struct mg_mqtt_opts sub_opts
static uint8_t qos=1;// PUT IN FUNCTION HEADDER can't be bothered with ack, so set to 0
LOG(NET_LOG,LOG_DEBUG, "MQTT: Connection open %lu\n", nc->id);
LOG(NET_LOG,LOG_DEBUG, "MQTT: Connection acknowledged\n");
mqtt_msg = (struct mg_mqtt_message *)ev_data;
if (mqtt_msg->connack_ret_code != MG_EV_MQTT_CONNACK_ACCEPTED) {
LOG(NET_LOG,LOG_WARNING, "Got mqtt connection error: %d\n", mqtt_msg->connack_ret_code);
_mqtt_exit_flag = true;
}
snprintf(aq_topic, 29, "%s/#", _aqconfig_.mqtt_aq_topic);
if (_aqconfig_.mqtt_aq_topic != NULL && _aqconfig_.mqtt_dz_sub_topic != NULL) {
topics[0].topic = aq_topic;
topics[0].qos = qos;
topics[1].topic = _aqconfig_.mqtt_dz_sub_topic;
topics[1].qos = qos;
mg_mqtt_subscribe(nc, topics, 2, 42);
LOG(NET_LOG,LOG_INFO, "MQTT: Subscribing to '%s'\n", aq_topic);
LOG(NET_LOG,LOG_INFO, "MQTT: Subscribing to '%s'\n", _aqconfig_.mqtt_dz_sub_topic);
}
else if (_aqconfig_.mqtt_aq_topic != NULL) {
topics[0].topic = aq_topic;
topics[0].qos = qos;
mg_mqtt_subscribe(nc, topics, 1, 42);
LOG(NET_LOG,LOG_INFO, "MQTT: Subscribing to '%s'\n", aq_topic);
}
else if (_aqconfig_.mqtt_dz_sub_topic != NULL) {
topics[0].topic = _aqconfig_.mqtt_dz_sub_topic;;
topics[0].qos = qos;
mg_mqtt_subscribe(nc, topics, 1, 42);
LOG(NET_LOG,LOG_INFO, "MQTT: Subscribing to '%s'\n", _aqconfig_.mqtt_dz_sub_topic);
struct mg_mqtt_opts sub_opts;
memset(&sub_opts, 0, sizeof(sub_opts));
sub_opts.topic = mg_str(aq_topic);
sub_opts.qos = qos;
mg_mqtt_sub(nc, &sub_opts);
LOG(NET_LOG,LOG_INFO, "MQTT: Subscribing to '%s'\n", aq_topic);
if (_aqconfig_.mqtt_dz_sub_topic != NULL) {
LOG(NET_LOG,LOG_ERR,"MQTT: Domoticz not supported\n");
}
LOG(NET_LOG,LOG_INFO, "MQTT: send last will message\n");
snprintf(aq_topic, 24, "%s/%s", _aqconfig_.mqtt_aq_topic,MQTT_LWM_TOPIC);
send_mqtt(nc, aq_topic ,MQTT_ON);
publish_mqtt_hassio_discover( _aqualink_data, nc);
}
break;
case MG_EV_MQTT_PUBACK:
mqtt_msg = (struct mg_mqtt_message *)ev_data;
LOG(NET_LOG,LOG_DEBUG, "MQTT: Message publishing acknowledged (msg_id: %d)\n", mqtt_msg->message_id);
case MG_EV_MQTT_CMD:
//LOG(NET_LOG,LOG_NOTICE, "MQTT: MG_EV_MQTT_CMD command, add code / need to replocate MG_EV_MQTT_PUBACK MG_EV_MQTT_SUBACK\n");
break;
case MG_EV_MQTT_SUBACK:
LOG(NET_LOG,LOG_INFO, "MQTT: Subscription(s) acknowledged\n");
snprintf(aq_topic, 24, "%s/%s", _aqconfig_.mqtt_aq_topic,MQTT_LWM_TOPIC);
send_mqtt(nc, aq_topic ,MQTT_ON);
break;
case MG_EV_MQTT_PUBLISH:
//case MG_EV_MQTT_PUBLISH:
case MG_EV_MQTT_MSG:
mqtt_msg = (struct mg_mqtt_message *)ev_data;
if (mqtt_msg->message_id != 0) {
LOG(NET_LOG,LOG_DEBUG, "MQTT: received (msg_id: %d), looks like my own message, ignoring\n", mqtt_msg->message_id);
if (mqtt_msg->id != 0) { // NSF Not good check mongoose.h # 2842
LOG(NET_LOG,LOG_DEBUG, "MQTT: received (msg_id: %d), looks like my own message, ignoring\n", mqtt_msg->id);
}
// NSF Need to change strlen to a global so it's not executed every time we check a topic
if (_aqconfig_.mqtt_aq_topic != NULL && strncmp(mqtt_msg->topic.p, _aqconfig_.mqtt_aq_topic, strlen(_aqconfig_.mqtt_aq_topic)) == 0)
if (_aqconfig_.mqtt_aq_topic != NULL && strncmp(mqtt_msg->topic.buf, _aqconfig_.mqtt_aq_topic, strlen(_aqconfig_.mqtt_aq_topic)) == 0)
{
DEBUG_TIMER_START(&tid);
action_mqtt_message(nc, mqtt_msg);
DEBUG_TIMER_STOP(tid, NET_LOG, "MQTT Request action_mqtt_message() took");
}
if (_aqconfig_.mqtt_dz_sub_topic != NULL && strncmp(mqtt_msg->topic.p, _aqconfig_.mqtt_dz_sub_topic, strlen(_aqconfig_.mqtt_dz_sub_topic)) == 0) {
if (_aqconfig_.mqtt_dz_sub_topic != NULL && strncmp(mqtt_msg->topic.buf, _aqconfig_.mqtt_dz_sub_topic, strlen(_aqconfig_.mqtt_dz_sub_topic)) == 0) {
action_domoticz_mqtt_message(nc, mqtt_msg);
}
break;
@ -2196,14 +2204,32 @@ void reset_last_mqtt_status()
void start_mqtt(struct mg_mgr *mgr) {
//LOG(NET_LOG,LOG_WARNING, "NOT Starting MQTT client, need to check code\n");
if ( _aqconfig_.mqtt_server == NULL ||
( _aqconfig_.mqtt_aq_topic == NULL && _aqconfig_.mqtt_dz_pub_topic == NULL && _aqconfig_.mqtt_dz_sub_topic == NULL) )
return;
char aq_topic[30];
LOG(NET_LOG,LOG_NOTICE, "Starting MQTT client to %s\n", _aqconfig_.mqtt_server);
if (mg_connect(mgr, _aqconfig_.mqtt_server, ev_handler) == NULL) {
LOG(NET_LOG,LOG_ERR, "Failed to create MQTT listener to %s\n", _aqconfig_.mqtt_server);
snprintf(aq_topic, 24, "%s/%s", _aqconfig_.mqtt_aq_topic,MQTT_LWM_TOPIC);
struct mg_mqtt_opts opts;
memset(&opts, 0, sizeof(opts));
opts.user = mg_str(_aqconfig_.mqtt_user);
opts.pass = mg_str(_aqconfig_.mqtt_passwd);
opts.client_id = mg_str(_aqconfig_.mqtt_ID);
//opts.keepalive = 5; // This seems to kill connection for some reason, and not sent heartbeat
opts.clean = true;
//opts.version = 4; // Maybe 5
opts.message = mg_str(MQTT_OFF); // will_message
opts.topic = mg_str(aq_topic); // will_topic
if ( mg_mqtt_connect(mgr, _aqconfig_.mqtt_server, &opts, ev_handler, NULL) == NULL ) {
LOG(NET_LOG,LOG_ERR, "Failed to create MQTT listener to %s\n", _aqconfig_.mqtt_server);
} else {
//int i;
#ifdef AQ_MEMCMP
@ -2212,6 +2238,7 @@ void start_mqtt(struct mg_mgr *mgr) {
reset_last_mqtt_status();
_mqtt_exit_flag = false; // set here to stop multiple connects, if it fails truley fails it will get set to false.
}
}
//bool start_web_server(struct mg_mgr *mgr, struct aqualinkdata *aqdata, char *port, char* web_root) {
@ -2227,28 +2254,30 @@ bool _start_net_services(struct mg_mgr *mgr, struct aqualinkdata *aqdata) {
setvbuf(stdout, NULL, _IOLBF, 0);
setvbuf(stderr, NULL, _IOLBF, 0);
mg_mgr_init(mgr, NULL);
LOG(NET_LOG,LOG_NOTICE, "Starting web server on port %s\n", _aqconfig_.socket_port);
nc = mg_bind(mgr, _aqconfig_.socket_port, ev_handler);
mg_log_set(_aqconfig_.mg_log_level);
mg_mgr_init(mgr);
char url[256];
if ( strcasestr(_aqconfig_.socket_port, "http") != NULL ) {
sprintf(url, "%s",_aqconfig_.socket_port);
} else {
sprintf(url, "http://0.0.0.0:%s",_aqconfig_.socket_port);
}
LOG(NET_LOG,LOG_NOTICE, "Starting web server on %s\n", url);
//nc = mg_bind(mgr, _aqconfig_.socket_port, ev_handler);
nc = mg_http_listen(mgr, url, ev_handler, mgr);
if (nc == NULL) {
LOG(NET_LOG,LOG_ERR, "Failed to create listener on port %s\n",_aqconfig_.socket_port);
LOG(NET_LOG,LOG_ERR, "Failed to create listener on port %s\n",url);
return false;
}
// Set up HTTP server parameters
mg_set_protocol_http_websocket(nc);
// Set default web options
//struct mg_serve_http_opts opts;
//memset(&opts, 0, sizeof(opts)); // Reset all options to defaults
//opts.document_root = _web_root; // Serve files from the current directory
//opts.extra_headers = "Cache-Control: public, max-age=604800, immutable";
memset(&_http_server_opts, 0, sizeof(_http_server_opts)); // Reset all options to defaults
_http_server_opts.document_root = _aqconfig_.web_directory; // Serve current directory
_http_server_opts.enable_directory_listing = "yes";
//_http_server_opts.extra_headers = "Cache-Control: public, max-age=604800, immutable";
_http_server_opts.extra_headers = "Cache-Control: public,max-age=31536000,immutable"; // Let's be as agressive on browser caching.
LOG(NET_LOG,LOG_WARNING, "TODO web server TODO disable directory_listing somehow\n");
_http_server_opts.root_dir = _aqconfig_.web_directory; // Serve current directory
_http_server_opts.extra_headers = "Cache-Control: public, max-age=604800, immutable"; // 7 days
//_http_server_opts.extra_headers = "Cache-Control: public,max-age=31536000,immutable"; // 1 year. Let's be as agressive on browser caching.
// Need to disable directory_listing somehow
#ifndef MG_DISABLE_MQTT
// Start MQTT
@ -2274,6 +2303,7 @@ void *net_services_thread( void *ptr )
struct aqualinkdata *aqdata = (struct aqualinkdata *) ptr;
int journald_fail = 0;
//struct mg_mgr mgr;
if (!_start_net_services(&_mgr, aqdata)) {
//LOG(NET_LOG,LOG_ERR, "Failed to start network services\n");
// Not the best way to do this (have thread exit process), but forks for the moment.
@ -2292,7 +2322,7 @@ void *net_services_thread( void *ptr )
if (aqdata->updated == true /*|| _broadcast == true*/) {
//LOG(NET_LOG,LOG_DEBUG, "********** Broadcast ************\n");
_broadcast_aqualinkstate(_mgr.active_connections);
_broadcast_aqualinkstate(_mgr.conns);
aqdata->updated = false;
}
#ifdef AQ_MANAGER
@ -2308,7 +2338,7 @@ void *net_services_thread( void *ptr )
} else if (journald_fail == JOURNAL_FAIL_RETRY) {
char msg[WS_LOG_LENGTH];
build_logmsg_JSON(msg, LOG_ERR, "Giving up on journal, don't expect to see logs", WS_LOG_LENGTH,46);
ws_send_logmsg(_mgr.active_connections, msg);
ws_send_logmsg(_mgr.conns, msg);
journald_fail = JOURNAL_FAIL_RETRY+1;
}
// Reset failures when manager is not active.
@ -2320,7 +2350,7 @@ void *net_services_thread( void *ptr )
*/
#endif
if (aqdata->simulator_active != SIM_NONE && aqdata->simulator_packet_updated == true ) {
_broadcast_simulator_message(_mgr.active_connections);
_broadcast_simulator_message(_mgr.conns);
}
}
@ -2331,17 +2361,11 @@ f_end:
pthread_exit(0);
}
#ifndef AQ_NO_THREAD_NETSERVICE
void broadcast_aqualinkstate() {
_aqualink_data->updated = true;
}
void broadcast_aqualinkstate_error(const char *msg) {
_broadcast_aqualinkstate_error(_mgr.active_connections, msg);
_broadcast_aqualinkstate_error(_mgr.conns, msg);
}
void broadcast_simulator_message() {
_aqualink_data->simulator_packet_updated = true;
@ -2375,72 +2399,3 @@ bool start_net_services(struct aqualinkdata *aqdata)
return true;
}
#else // DON'T THREAD NET SERVICES
void stop_net_services() {
if ( ! _aqconfig_.thread_netservices) {
mg_mgr_free(&_mgr);
return;
}
}
void broadcast_aqualinkstate(/*struct mg_connection *nc*/)
{
if ( ! _aqconfig_.thread_netservices) {
_broadcast_aqualinkstate(_mgr.active_connections);
_aqualink_data->updated = false;
return;
}
}
void broadcast_aqualinkstate_error(/*struct mg_connection *nc,*/ char *msg)
{
if ( ! _aqconfig_.thread_netservices) {
return _broadcast_aqualinkstate_error(_mgr.active_connections, msg);
}
LOG(NET_LOG,LOG_NOTICE, "Broadcast error to network\n");
}
void broadcast_simulator_message() {
if ( ! _aqconfig_.thread_netservices) {
return _broadcast_simulator_message();
}
}
time_t poll_net_services(/*struct mg_mgr *mgr,*/ int timeout_ms)
{
if (timeout_ms < 0)
timeout_ms = 0;
if ( ! _aqconfig_.thread_netservices) {
//return mg_mgr_poll(mgr, timeout_ms);
return mg_mgr_poll(&_mgr, timeout_ms);
}
if (timeout_ms > 5)
delay(5);
else if (timeout_ms > 0)
delay(timeout_ms);
//LOG(NET_LOG,LOG_NOTICE, "Poll network services\n");
return 0;
}
bool start_net_services(/*struct mg_mgr *mgr, */struct aqualinkdata *aqdata)
{
_keepNetServicesRunning = true;
if ( ! _aqconfig_.thread_netservices) {
//return _start_net_services(mgr, aqdata);
return _start_net_services(&_mgr, aqdata);
}
LOG(NET_LOG,LOG_NOTICE, "Starting network services thread\n");
if( pthread_create( &_net_thread_id , NULL , net_services_thread, (void*)aqdata) < 0) {
LOG(NET_LOG, LOG_ERR, "could not create network thread\n");
return false;
}
pthread_detach(_net_thread_id);
return true;
}
#endif

View File

@ -7,23 +7,19 @@
#define GET_RTN_ERROR "Error"
#define CONTENT_JSON "Content-Type: application/json"
#define CONTENT_JS "Content-Type: text/javascript"
#define CONTENT_TEXT "Content-Type: text/plain"
//#define CONTENT_JSON "Content-Type: application/json"
//#define CONTENT_JS "Content-Type: text/javascript"
//#define CONTENT_TEXT "Content-Type: text/plain"
#define CONTENT_JSON "Content-Type: application/json\r\n"
#define CONTENT_JS "Content-Type: text/javascript\r\n"
#define CONTENT_TEXT "Content-Type: text/plain\r\n"
//void main_server();
//void main_server_TEST(struct aqualinkdata *aqdata, char *s_http_port);
//bool start_web_server(struct mg_mgr *mgr, struct aqualinkdata *aqdata, char *port, char* web_root);
//bool start_net_services(struct mg_mgr *mgr, struct aqualinkdata *aqdata, struct aqconfig *aqconfig);
/*
#ifdef AQ_NO_THREAD_NETSERVICE
bool start_net_services(struct mg_mgr *mgr, struct aqualinkdata *aqdata);
void stop_net_services(struct mg_mgr *mgr);
time_t poll_net_services(struct mg_mgr *mgr, int timeout_ms);
void broadcast_aqualinkstate(struct mg_connection *nc);
void broadcast_aqualinkstate_error(struct mg_connection *nc, char *msg);
#else*/
bool start_net_services(struct aqualinkdata *aqdata);
void stop_net_services();
time_t poll_net_services(int timeout_ms);

View File

@ -42,7 +42,7 @@
#define PACKET_MAX 1200
#define PROBE_CYCLES 2
#define VERSION "serial_logger V2.10"
#define VERSION "serial_logger V2.11"
/*
typedef enum used {
@ -553,7 +553,6 @@ int main(int argc, char *argv[]) {
int logLevel = LOG_NOTICE;
bool panleProbe = true;
bool rsSerialSpeedTest = false;
bool serialBlocking = true;
bool errorMonitor = false;
bool printAllIDs = false;
bool timePackets = false;
@ -626,7 +625,6 @@ int main(int argc, char *argv[]) {
panleProbe = false;
} else if (strcmp(argv[i], "-s") == 0) {
rsSerialSpeedTest = true;
serialBlocking = false;
} else if (strcmp(argv[i], "-e") == 0) {
errorMonitor = true;
} else if (strcmp(argv[i], "-a") == 0) {
@ -651,11 +649,7 @@ int main(int argc, char *argv[]) {
return -1;
}
} else {
if (!serialBlocking)
rs_fd = init_serial_port(argv[1]);
else
rs_fd = init_blocking_serial_port(argv[1]);
rs_fd = init_serial_port(argv[1]);
if (rs_fd < 0) {
LOG(SLOG_LOG, LOG_ERR, "Unable to open port: %s\n", argv[1]);
displayLastSystemError(argv[1]);

View File

@ -4,5 +4,5 @@
#define AQUALINKD_SHORT_NAME "AqualinkD"
// Use Magor . Minor . Patch
#define AQUALINKD_VERSION "2.6.12 (dev 1)"
#define AQUALINKD_VERSION "3.0.0 (dev)"

View File

@ -149,10 +149,13 @@
}
.statusmsg {
font-size: clamp(0.5rem, 1vw, 1rem);
font-size: clamp(0.2rem, 1vw, 1rem);
white-space: nowrap; /* Keep text on a single line */
overflow: hidden;
text-overflow: ellipsis;
/*text-overflow: clip;*/
width: var(--aqualinkd-container);
display:inline-block;
}
.error {
@ -368,6 +371,7 @@
var _panel_size = 6;
var _panel_set = 0;
var _latestVersionAvailable = 0;
var _latestDevVersionAvailable = 0;
var _rssd_logmask = 0;
const RSSD_MASK_ID = 512; // Must match RSSD_LOG in utils.c
@ -1567,6 +1571,12 @@
} else {
disablebutton("upgrade");
}
if (_urlParams.get('devupgrade') != null) {
enablebutton("upgrade");
var button = document.getElementById("upgrade");
button.setAttribute("upgrade_type", "dev")
}
}
}
@ -1596,10 +1606,11 @@
}
delayedVersionCheck(data['aqualinkd_version']);
/*
if (_urlParams.get('upgrade') != null) {
enablebutton("upgrade");
}
*/
}
}
@ -1857,6 +1868,7 @@
startWebsockets();
getLatestVersion();
getLatestDevVersion();
}
function set_unavailable(message) {
@ -1893,13 +1905,23 @@
//cmd.uri = "rawcommand"
switch (source.id) {
case "upgrade":
if (confirm("Are you sure you want to proceed upgrading AqualinkD?")) {
console.log("Upgrading");
update_log_message("***** AqualinkD upgrade in progress *****");
} else {
return;
if (source.getAttribute("upgrade_type") == "dev") {
if (confirm("Are you sure you want to proceed installing AqualinkD to Dev version '"+_latestDevVersionAvailable+"'' ?")) {
//console.log("Upgrading to dev release");
update_log_message("***** AqualinkD upgrade in progress *****");
} else {
return;
}
cmd.uri = "installdevrelease"
} else {
if (confirm("Are you sure you want to proceed upgrading AqualinkD to '"+_latestVersionAvailable+"'' ?")) {
console.log("Upgrading");
update_log_message("***** AqualinkD upgrade in progress *****");
} else {
return;
}
cmd.uri = "upgrade"
}
cmd.uri = "upgrade"
// NEED TO REGET aqmanager after restart.
break;
case "restart":
@ -1961,6 +1983,24 @@
xmlhttp.open("GET", url);
xmlhttp.send();
}
function getLatestDevVersion(url="https://api.github.com/repos/AqualinkD/AqualinkD/contents/source/version.h", tryagain=true) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
//console.log(this.responseText);
_latestDevVersionAvailable = this.responseText.match(/AQUALINKD_VERSION "(.*)"/i)[1];
} else if (this.readyState == 4 && this.status == 404) {
if (tryagain) {
// Try sfeakes repo
getLatestDevVersion("https://api.github.com/repos/AqualinkD/AqualinkD/contents/source/version.h", false);
}
}
};
xmlhttp.open("GET", url);
xmlhttp.setRequestHeader("Accept","application/vnd.github.raw");
xmlhttp.send();
}
</script>
<body onload="init();init_collapsible();">