Release 2.3.2

pull/69/merge V2.3.2
sfeakes 2023-07-08 11:14:44 -05:00
parent 2d0647ab8d
commit a97d058dda
23 changed files with 524 additions and 188 deletions

View File

@ -79,8 +79,10 @@ Designed to mimic AqualinkRS6 All Button keypad and (like the keypad) is used to
* Add set time to OneTouch protocol.
* Publish AqualinkD Management console. (Configure, Restart, run serial_logger) within AqualinkD.
# Update in Release 2.3.2 (Current Development)
# Update in Release 2.3.2
* Added support for VSP on panel versions REV 0.1 & 0.2
* Can change heater sliver min/max values in web UI. `./web/config.js`
* Added reading ePump RPM/Watts directly from RS485 bus.
# Update in Release 2.3.1
* Changed a lot of logic around different protocols.
@ -104,7 +106,7 @@ Designed to mimic AqualinkRS6 All Button keypad and (like the keypad) is used to
# Update in Release 2.2.2
* Fixed some Web UI bugs
* Color lights now quicker when selecting existing comor mode.
* Color lights now quicker when selecting existing color mode.
# Update in Release 2.2.1
* Supports serial adapter protocol `rssa_device_id`, (provides instant heater setpoint changes & setpoint increment)

View File

@ -148,6 +148,20 @@ const char* get_packet_type(unsigned char* packet , int length)
case RSSA_DEV_READY:
return "RSSA SendCommand";
break;
case CMD_EPUMP_STATUS:
if (packet[4] == CMD_EPUMP_RPM)
return "ePump RPM";
else if (packet[4] == CMD_EPUMP_WATTS)
return "ePump Watts";
else
return "ePump (unknown)";
break;
case CMD_EPUMP_RPM:
return "ePump set RPM";
break;
case CMD_EPUMP_WATTS:
return "ePump get Watts";
break;
default:
sprintf(buf, "Unknown '0x%02hhx'", packet[PKT_CMD]);
return buf;

View File

@ -36,10 +36,14 @@
# Jandy pump ID's
# 0x78, 0x79, 0x7A, 0x7B
*/
#define PENTAIR_DEC_PUMP_MIN 96
#define PENTAIR_DEC_PUMP_MAX 111
#define JANDY_DEC_PUMP_MIN 120
#define JANDY_DEC_PUMP_MAX 123
#define PENTAIR_DEC_PUMP_MIN 96 // 0x60
#define PENTAIR_DEC_PUMP_MAX 111 // 0x6F
#define JANDY_DEC_PUMP_MIN 120 // 0x80
#define JANDY_DEC_PUMP_MAX 123 // 0x83
#define JANDY_DEC_LX_MIN 56 // 0x40
#define JANDY_DEC_LX_MAX 59 // 0x43
#define JANDY_DEC_LXI_MIN 104 // 0x60
#define JANDY_DEC_LXI_MAX 107 // 0x6B
// PACKET DEFINES Jandy
@ -315,6 +319,10 @@ SPILLOVER IS DISABLED WHILE SPA IS ON
#define CMD_PDA_SHIFTLINES 0x0F
#define CMD_PDA_HIGHLIGHTCHARS 0x10
/* ePump */
#define CMD_EPUMP_STATUS 0x1F
#define CMD_EPUMP_RPM 0x44
#define CMD_EPUMP_WATTS 0x45
// One Touch commands
//#define CMD_PDA_0x04 0x04 // No idea, might be building menu
@ -417,7 +425,8 @@ SPILLOVER IS DISABLED WHILE SPA IS ON
typedef enum {
DRS_NONE,
DRS_SWG,
DRS_EPUMP
DRS_EPUMP,
DRS_LXI
} rsDeviceType;
typedef enum {

View File

@ -1220,11 +1220,14 @@ int startup(char *self, char *cfgFile)
_aqconfig_.log_raw_bytes = true;
#ifdef AQ_MANAGER
setLoggingPrms(_aqconfig_.log_level, _aqconfig_.deamonize, (_aqconfig_.display_warnings_web?_aqualink_data.last_display_message:NULL));
#else
if (_aqconfig_.display_warnings_web == true)
setLoggingPrms(_aqconfig_.log_level, _aqconfig_.deamonize, _aqconfig_.log_file, _aqualink_data.last_display_message);
else
setLoggingPrms(_aqconfig_.log_level, _aqconfig_.deamonize, _aqconfig_.log_file, NULL);
#endif
LOG(AQUA_LOG,LOG_NOTICE, "%s v%s\n", AQUALINKD_NAME, AQUALINKD_VERSION);
/*
@ -1284,13 +1287,16 @@ int startup(char *self, char *cfgFile)
#endif
LOG(AQUA_LOG,LOG_NOTICE, "Config force SWG = %s\n", bool2text(_aqconfig_.force_swg));
LOG(AQUA_LOG,LOG_NOTICE, "Config force PS setpoint = %s\n", bool2text(_aqconfig_.force_ps_setpoints));
LOG(AQUA_LOG,LOG_NOTICE, "Config force Freeze Prot = %s\n", bool2text(_aqconfig_.force_frzprotect_setpoints));
/* removed until domoticz has a better virtual thermostat
LOG(AQUA_LOG,LOG_NOTICE, "Config idx pool thermostat = %d\n", _aqconfig_.dzidx_pool_thermostat);
LOG(AQUA_LOG,LOG_NOTICE, "Config idx spa thermostat = %d\n", _aqconfig_.dzidx_spa_thermostat);
*/
LOG(AQUA_LOG,LOG_NOTICE, "Config deamonize = %s\n", bool2text(_aqconfig_.deamonize));
#ifndef AQ_MANAGER
LOG(AQUA_LOG,LOG_NOTICE, "Config log_file = %s\n", _aqconfig_.log_file);
#endif
LOG(AQUA_LOG,LOG_NOTICE, "Config enable scheduler = %s\n", bool2text(_aqconfig_.enable_scheduler));
LOG(AQUA_LOG,LOG_NOTICE, "Config light_pgm_mode = %.2f\n", _aqconfig_.light_programming_mode);
LOG(AQUA_LOG,LOG_NOTICE, "Debug RS485 protocol = %s\n", bool2text(_aqconfig_.log_protocol_packets));
@ -1741,7 +1747,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)
@ -1795,11 +1800,18 @@ void main_loop()
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 (!_aqconfig_.readahead_b4_write) {
if (serial_blockingmode()) {
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;
@ -1957,4 +1969,25 @@ void main_loop()
}
/*
void debugtestePump()
{
LOG(DJAN_LOG, LOG_INFO, "Jandy Pump code check\n");
unsigned char toPumpWatts[] = {0x10,0x02,0x78,0x45,0x00,0x05,0xd4,0x10,0x03};
unsigned char fromPumpWatts[] = {0x10,0x02,0x00,0x1f,0x45,0x00,0x05,0x1d,0x05,0x9d,0x10,0x03};
unsigned char toPumpRPM[] = {0x10,0x02,0x78,0x44,0x00,0x60,0x27,0x55,0x10,0x03};
unsigned char fromPumpRPM[] = {0x10,0x02,0x00,0x1f,0x44,0x00,0x60,0x27,0x00,0xfc,0x10,0x03};
processJandyPacket(toPumpWatts, 8, &_aqualink_data);
processJandyPacket(fromPumpWatts, 11, &_aqualink_data);
processJandyPacket(toPumpRPM, 8, &_aqualink_data);
processJandyPacket(fromPumpRPM, 11, &_aqualink_data);
}
*/

View File

@ -110,7 +110,9 @@ void init_parameters (struct aqconfig * parms)
//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
@ -126,6 +128,7 @@ void init_parameters (struct aqconfig * parms)
parms->force_swg = false;
parms->force_ps_setpoints = false;
parms->force_frzprotect_setpoints = false;
//parms->swg_pool_and_spa = false;
parms->swg_zero_ignore = DEFAULT_SWG_ZERO_IGNORE_COUNT;
parms->display_warnings_web = false;
@ -428,9 +431,11 @@ bool setConfigValue(struct aqualinkdata *aqdata, char *param, char *value) {
} else if (strncasecmp(param, "web_directory", 13) == 0) {
_aqconfig_.web_directory = cleanalloc(value);
rtn=true;
#ifndef AQ_MANAGER
} else if (strncasecmp(param, "log_file", 8) == 0) {
_aqconfig_.log_file = cleanalloc(value);
rtn=true;
#endif
} else if (strncasecmp(param, "mqtt_address", 12) == 0) {
_aqconfig_.mqtt_server = cleanalloc(value);
rtn=true;
@ -550,6 +555,12 @@ bool setConfigValue(struct aqualinkdata *aqdata, char *param, char *value) {
else
_aqconfig_.read_RS485_devmask &= ~READ_RS485_PEN_PUMP;
rtn=true;
} else if (strncasecmp (param, "read_RS485_LXi", 14) == 0) {
if (text2bool(value))
_aqconfig_.read_RS485_devmask |= READ_RS485_JAN_LXI;
else
_aqconfig_.read_RS485_devmask &= ~READ_RS485_JAN_LXI;
rtn=true;
} else if (strncasecmp (param, "use_panel_aux_labels", 20) == 0) {
_aqconfig_.use_panel_aux_labels = text2bool(value);
rtn=true;
@ -559,6 +570,9 @@ bool setConfigValue(struct aqualinkdata *aqdata, char *param, char *value) {
} else if (strncasecmp (param, "force_ps_setpoints", 18) == 0) {
_aqconfig_.force_ps_setpoints = text2bool(value);
rtn=true;
} else if (strncasecmp (param, "force_frzprotect_setpoints", 26) == 0) {
_aqconfig_.force_frzprotect_setpoints = text2bool(value);
rtn=true;
} else if (strncasecmp (param, "debug_RSProtocol_bytes", 22) == 0) {
_aqconfig_.log_raw_bytes = text2bool(value);
rtn=true;

View File

@ -27,6 +27,8 @@
#define READ_RS485_SWG (1 << 0) // 1 SWG
#define READ_RS485_JAN_PUMP (1 << 1) // 2 Jandy Pump
#define READ_RS485_PEN_PUMP (1 << 2) // 4 Pentair Pump
#define READ_RS485_JAN_LXI (1 << 3) // Jandy LX & LXi heater
//#define READ_RS485_JAN_LXI (1 << 4) // Jandy LXi heater
struct aqconfig
{
@ -43,7 +45,9 @@ struct aqconfig
bool extended_device_id_programming;
#endif
bool deamonize;
#ifndef AQ_MANAGER // Need to uncomment and clean up referances in future.
char *log_file;
#endif
char *mqtt_dz_sub_topic;
char *mqtt_dz_pub_topic;
char *mqtt_aq_topic;
@ -74,6 +78,7 @@ struct aqconfig
bool use_panel_aux_labels;
bool force_swg;
bool force_ps_setpoints;
bool force_frzprotect_setpoints;
int swg_zero_ignore;
bool display_warnings_web;
bool log_protocol_packets; // Read & Write as packets
@ -102,6 +107,7 @@ struct aqconfig _aqconfig_;
#define READ_RSDEV_SWG ((_aqconfig_.read_RS485_devmask & READ_RS485_SWG) == READ_RS485_SWG)
#define READ_RSDEV_ePUMP ((_aqconfig_.read_RS485_devmask & READ_RS485_JAN_PUMP) == READ_RS485_JAN_PUMP)
#define READ_RSDEV_vsfPUMP ((_aqconfig_.read_RS485_devmask & READ_RS485_PEN_PUMP) == READ_RS485_PEN_PUMP)
#define READ_RSDEV_LXI ((_aqconfig_.read_RS485_devmask & READ_RS485_JAN_LXI) == READ_RS485_JAN_LXI)
//#define isPDA ((_aqconfig_.paneltype_mask & RSP_PDA) == RSP_PDA)

View File

@ -15,6 +15,7 @@
*/
#include <stdio.h>
#include <string.h>
#include "devices_jandy.h"
#include "aq_serial.h"
@ -47,7 +48,11 @@ bool processJandyPacket(unsigned char *packet_buffer, int packet_length, struct
}
else if (interestedInNextAck == DRS_EPUMP)
{
rtn = processPacketFromJandyPump(packet_buffer, packet_length, aqdata);
rtn = processPacketFromJandyPump(packet_buffer, packet_length, aqdata, previous_packet_to);
}
else if (interestedInNextAck == DRS_LXI)
{
rtn = processPacketFromJandyHeater(packet_buffer, packet_length, aqdata, previous_packet_to);
}
interestedInNextAck = DRS_NONE;
previous_packet_to = NUL;
@ -78,12 +83,25 @@ bool processJandyPacket(unsigned char *packet_buffer, int packet_length, struct
rtn = processPacketToJandyPump(packet_buffer, packet_length, aqdata);
previous_packet_to = packet_buffer[PKT_DEST];
}
else if (READ_RSDEV_LXI && ( (packet_buffer[PKT_DEST] >= JANDY_DEC_LX_MIN && packet_buffer[PKT_DEST] <= JANDY_DEC_LX_MAX)
|| (packet_buffer[PKT_DEST] >= JANDY_DEC_LXI_MIN && packet_buffer[PKT_DEST] <= JANDY_DEC_LXI_MAX)))
{
interestedInNextAck = DRS_LXI;
rtn = processPacketToJandyHeater(packet_buffer, packet_length, aqdata);
previous_packet_to = packet_buffer[PKT_DEST];
}
else
{
interestedInNextAck = DRS_NONE;
previous_packet_to = NUL;
}
/*
if (packet_buffer[PKT_CMD] != CMD_PROBE && getLogLevel(DJAN_LOG) >= LOG_DEBUG) {
char msg[1000];
beautifyPacket(msg, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_DEBUG, "Jandy : %s\n", msg);
}
*/
return rtn;
}
@ -188,8 +206,10 @@ bool isSWGDeviceErrorState(unsigned char status)
}
void setSWGdeviceStatus(struct aqualinkdata *aqdata, emulation_type requester, unsigned char status) {
if (aqdata->ar_swg_device_status == status)
if (aqdata->ar_swg_device_status == status) {
//LOG(DJAN_LOG, LOG_DEBUG, "Set SWG device state to '0x%02hhx', request from %d\n", aqdata->ar_swg_device_status, requester);
return;
}
// If we get (ALLBUTTON, SWG_STATUS_CHECK_PCB), it sends this for many status, like clean cell.
// So if we are in one of those states, don't use it.
@ -197,7 +217,7 @@ void setSWGdeviceStatus(struct aqualinkdata *aqdata, emulation_type requester, u
if (requester == ALLBUTTON && status == SWG_STATUS_CHECK_PCB ) {
if (aqdata->ar_swg_device_status > SWG_STATUS_ON &&
aqdata->ar_swg_device_status < SWG_STATUS_TURNING_OFF) {
LOG(DJAN_LOG, LOG_DEBUG, "Ignoreing set SWG device state to '0x%02hhx', request from %d\n", aqdata->ar_swg_device_status, requester);
LOG(DJAN_LOG, LOG_DEBUG, "Ignoring set SWG device state to '0x%02hhx', request from %d\n", aqdata->ar_swg_device_status, requester);
return;
}
}
@ -437,17 +457,45 @@ void get_swg_status_mqtt(struct aqualinkdata *aqdata, char *message, int *status
}
}
#define EP_HI_B_WAT 8
#define EP_LO_B_WAT 7
#define EP_HI_B_RPM 7
#define EP_LO_B_RPM 6
bool processPacketToJandyPump(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata)
{
char msg[1000];
//logMessage(LOG_DEBUG, "Need to log ePump message here for future\n");
beautifyPacket(msg, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_DEBUG, "To ePump: %s\n", msg);
/*
Set & Sataus Watts. Looks like send to ePump type 0x45, return type 0xf1|0x45
JandyDvce: To ePump: Read To 0x78 of type Unknown '0x45' | HEX: 0x10|0x02|0x78|0x45|0x00|0x05|0xd4|0x10|0x03|
JandyDvce: From ePump: Read To 0x00 of type Unknown '0x1f' | HEX: 0x10|0x02|0x00|0x1f|0x45|0x00|0x05|0x1d|0x05|0x9d|0x10|0x03|
JandyDvce: From ePump: Read To 0x00 of type Unknown '0x1f' | HEX: 0x10|0x02|0x00|0x1f| 69| 0| 5| 29| 5|0x9d|0x10|0x03| (Decimal)
Type 0x1F and cmd 0x45 is Watts = 5 * (256) + 29 = 1309 or Byte 8 * 265 + Byte 7
*/
/*
Set & Sataus RPM. Looks like send to ePump type 0x44, return type 0xf1|0x44
JandyDvce: To ePump: Read To 0x78 of type Unknown '0x44' | HEX: 0x10|0x02|0x78|0x44|0x00|0x60|0x27|0x55|0x10|0x03|
JandyDvce: From ePump: Read To 0x00 of type Unknown '0x1f' | HEX: 0x10|0x02|0x00|0x1f|0x44|0x00|0x60|0x27|0x00|0xfc|0x10|0x03|
JandyDvce: From ePump: Read To 0x00 of type Unknown '0x1f' | HEX: 0x10|0x02|0x00|0x1f| 68| 0| 96| 39| 0|0xfc|0x10|0x03| (Decimal)
PDA: PDA Menu Line 3 = SET TO 2520 RPM
Type 0x1F and cmd 0x45 is RPM = 39 * (256) + 96 / 4 = 2520 or Byte 8 * 265 + Byte 7 / 4
*/
// If type 0x45 and 0x44 set to interested in next command.
if (packet_buffer[3] == CMD_EPUMP_RPM) {
// All we need to do is set we are interested in next packet, but ca lling function already did this.
LOG(DJAN_LOG, LOG_DEBUG, "ControlPanel request Pump ID 0x%02hhx set RPM to %d\n",packet_buffer[PKT_DEST], ( (packet_buffer[EP_HI_B_RPM-1] * 256) + packet_buffer[EP_LO_B_RPM-1]) / 4 );
} else if (packet_buffer[3] == CMD_EPUMP_WATTS) {
LOG(DJAN_LOG, LOG_DEBUG, "ControlPanel request Pump ID 0x%02hhx get watts\n",packet_buffer[PKT_DEST]);
}
if (getLogLevel(DJAN_LOG) >= LOG_DEBUG) {
char msg[1000];
beautifyPacket(msg, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_DEBUG, "To ePump: %s\n", msg);
//find pump for message
if ( 1 == 0 /*SOME_DEBUG_TEST*/) {
int i;
for (i=0; i < aqdata->num_pumps; i++) {
for (int i=0; i < aqdata->num_pumps; i++) {
if (aqdata->pumps[i].pumpID == packet_buffer[PKT_DEST]) {
LOG(DJAN_LOG, LOG_DEBUG, "Last panel info RPM:%d GPM:%d WATTS:%d\n", aqdata->pumps[i].rpm, aqdata->pumps[i].gpm, aqdata->pumps[i].watts);
break;
@ -457,26 +505,110 @@ bool processPacketToJandyPump(unsigned char *packet_buffer, int packet_length, s
return false;
}
bool processPacketFromJandyPump(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata)
bool processPacketFromJandyPump(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata, const unsigned char previous_packet_to)
{
char msg[1000];
//logMessage(LOG_DEBUG, "Need to log ePump message here for future\n");
beautifyPacket(msg, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_DEBUG, "From ePump: %s\n", msg);
bool found=false;
if (packet_buffer[3] == CMD_EPUMP_STATUS && packet_buffer[4] == CMD_EPUMP_RPM) {
for (int i = 0; i < MAX_PUMPS; i++) {
if ( aqdata->pumps[i].prclType == JANDY && aqdata->pumps[i].pumpID == previous_packet_to ) {
LOG(DJAN_LOG, LOG_INFO, "Jandy Pump Status message = RPM %d\n",( (packet_buffer[EP_HI_B_RPM] * 256) + packet_buffer[EP_LO_B_RPM]) / 4 );
aqdata->pumps[i].rpm = ( (packet_buffer[EP_HI_B_RPM] * 256) + packet_buffer[EP_LO_B_RPM] ) / 4;
found=true;
}
}
} else if (packet_buffer[3] == CMD_EPUMP_STATUS && packet_buffer[4] == CMD_EPUMP_WATTS) {
for (int i = 0; i < MAX_PUMPS; i++) {
if ( aqdata->pumps[i].prclType == JANDY && aqdata->pumps[i].pumpID == previous_packet_to ) {
LOG(DJAN_LOG, LOG_INFO, "Jandy Pump Status message = WATTS %d\n", (packet_buffer[EP_HI_B_WAT] * 256) + packet_buffer[EP_LO_B_WAT]);
aqdata->pumps[i].watts = (packet_buffer[EP_HI_B_WAT] * 256) + packet_buffer[EP_LO_B_WAT];
found=true;
}
}
}
if (!found) {
if (packet_buffer[4] == CMD_EPUMP_RPM)
LOG(DJAN_LOG, LOG_NOTICE, "Jandy Pump found at ID 0x%02hhx with RPM %d, but not configured, information ignored!\n",previous_packet_to,( (packet_buffer[EP_HI_B_RPM] * 256) + packet_buffer[EP_LO_B_RPM]) / 4 );
else if (packet_buffer[4] == CMD_EPUMP_WATTS)
LOG(DJAN_LOG, LOG_NOTICE, "Jandy Pump found at ID 0x%02hhx with WATTS %d, but not configured, information ignored!\n",previous_packet_to, (packet_buffer[EP_HI_B_WAT] * 256) + packet_buffer[EP_LO_B_WAT]);
}
if (getLogLevel(DJAN_LOG) >= LOG_DEBUG) {
char msg[1000];
//logMessage(LOG_DEBUG, "Need to log ePump message here for future\n");
beautifyPacket(msg, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_DEBUG, "From ePump: %s\n", msg);
}
return false;
}
void processMissingAckPacketFromJandyPump(unsigned char destination, struct aqualinkdata *aqdata)
{
// Do nothing for the moment.
return;
}
/*
Messages to ePump so far.
Debug: To ePump: Jandy Packet | HEX: 0x10|0x02|0x78|0x42|0xcc|0x10|0x03|
Debug: To ePump: Jandy Packet | HEX: 0x10|0x02|0x78|0x44|0x00|0x10|0x27|0x05|0x10|0x03|
Debug: To ePump: Jandy Packet | HEX: 0x10|0x02|0x78|0x44|0x00|0x58|0x1b|0x41|0x10|0x03|
Debug: To ePump: Jandy Packet | HEX: 0x10|0x02|0x78|0x45|0x00|0x05|0xd4|0x10|0x03|
Debug: To ePump: Jandy Packet | HEX: 0x10|0x02|0x78|0x46|0x00|0x00|0x03|0xd3|0x10|0x03|
Debug: To ePump: Jandy Packet | HEX: 0x10|0x02|0x78|0x46|0x00|0x04|0x00|0xd4|0x10|0x03|
*/
bool processPacketToJandyHeater(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata)
{
char msg[1000];
int length = 0;
beautifyPacket(msg, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_INFO, "To Heater: %s\n", msg);
length += sprintf(msg+length, "Last panel info ");
for (int i=0; i < aqdata->total_buttons; i++)
{
if ( strcmp(BTN_POOL_HTR,aqdata->aqbuttons[i].name) == 0) {
length += sprintf(msg+length, ", Pool Heat LED=%d ",aqdata->aqbuttons[i].led->state);
}
if ( strcmp(BTN_SPA_HTR,aqdata->aqbuttons[i].name) == 0) {
length += sprintf(msg+length, ", Spa Heat LED=%d ",aqdata->aqbuttons[i].led->state);
}
}
length += sprintf(msg+length, ", Pool SP=%d, Spa SP=%d",aqdata->pool_htr_set_point, aqdata->spa_htr_set_point);
length += sprintf(msg+length, ", Pool temp=%d, Spa temp=%d",aqdata->pool_temp, aqdata->spa_temp);
LOG(DJAN_LOG, LOG_INFO, "%s\n", msg);
return false;
}
bool processPacketFromJandyHeater(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata, const unsigned char previous_packet_to)
{
char msg[1000];
int length = 0;
beautifyPacket(msg, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_INFO, "From Heater: %s\n", msg);
length += sprintf(msg+length, "Last panel info ");
for (int i=0; i < aqdata->total_buttons; i++)
{
if ( strcmp(BTN_POOL_HTR,aqdata->aqbuttons[i].name) == 0) {
length += sprintf(msg+length, ", Pool Heat LED=%d ",aqdata->aqbuttons[i].led->state);
}
if ( strcmp(BTN_SPA_HTR,aqdata->aqbuttons[i].name) == 0) {
length += sprintf(msg+length, ", Spa Heat LED=%d ",aqdata->aqbuttons[i].led->state);
}
}
length += sprintf(msg+length, ", Pool SP=%d, Spa SP=%d",aqdata->pool_htr_set_point, aqdata->spa_htr_set_point);
length += sprintf(msg+length, ", Pool temp=%d, Spa temp=%d",aqdata->pool_temp, aqdata->spa_temp);
LOG(DJAN_LOG, LOG_INFO, "%s\n", msg);
return false;
}

View File

@ -10,10 +10,13 @@ bool processJandyPacket(unsigned char *packet_buffer, int packet_length, struct
bool processPacketToSWG(unsigned char *packet, int packet_length, struct aqualinkdata *aqdata, int swg_zero_ignore);
bool processPacketFromSWG(unsigned char *packet, int packet_length, struct aqualinkdata *aqdata);
bool processPacketToJandyPump(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata);
bool processPacketFromJandyPump(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata);
bool processPacketFromJandyPump(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata, const unsigned char previous_packet_to);
void processMissingAckPacketFromSWG(unsigned char destination, struct aqualinkdata *aqdata);
void processMissingAckPacketFromJandyPump(unsigned char destination, struct aqualinkdata *aqdata);
bool processPacketFromJandyHeater(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata, const unsigned char previous_packet_to );
bool processPacketToJandyHeater(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata);
void get_swg_status_mqtt(struct aqualinkdata *aqdata, char *message, int *status, int *dzalert);
aqledstate get_swg_led_state(struct aqualinkdata *aqdata);

35
epump.h
View File

@ -15,6 +15,41 @@ Nothing seems to change these, need real pump to test
0x10|0x02|0x7a|0x41|0xcd|0x10|0x03|
----------------
Set & Sataus WATTS. Looks like send to ePump type 0x45, return type 0xf1|0x45
JandyDvce: To ePump: Read To 0x78 of type Unknown '0x45' | HEX: 0x10|0x02|0x78|0x45|0x00|0x05|0xd4|0x10|0x03|
JandyDvce: From ePump: Read To 0x00 of type Unknown '0x1f' | HEX: 0x10|0x02|0x00|0x1f|0x45|0x00|0x05|0x1d|0x05|0x9d|0x10|0x03|
JandyDvce: From ePump: Read To 0x00 of type Unknown '0x1f' | HEX: 0x10|0x02|0x00|0x1f| 69| 0| 5| 29| 5|0x9d|0x10|0x03|
Jun-24-23 08:30:53 AM Debug: PDA: PDA Menu Line 4 = WATTS: 1309
Type 0x1f (command 0x45 or 69)
Watts = 5 * (256) + 29 = 1309 or Byte 8 * 265 + Byte 7
----------------
Set & Sataus RPM. Looks like send to ePump type 0x44, return type 0xf1|0x44
JandyDvce: From ePump: Read To 0x00 of type Unknown '0x1f' | HEX: 0x10|0x02|0x00|0x1f|0x44|0x00|0xe8|0x35|0x00|0x92|0x10|0x03|
JandyDvce: From ePump: Read To 0x00 of type Unknown '0x1f' | HEX: 0x10|0x02|0x00|0x1f| 68| 0| 232| 53| 0|0x92|0x10|0x03|
PDA Menu Line 3 = SET TO 3450 RPM
JandyDvce: From ePump: Read To 0x00 of type Unknown '0x1f' | HEX: 0x10|0x02|0x00|0x1f|0x44|0x00|0x50|0x2d|0x00|0xf2|0x10|0x03|
JandyDvce: From ePump: Read To 0x00 of type Unknown '0x1f' | HEX: 0x10|0x02|0x00|0x1f| 68| 0| 80| 45| 0|0xf2|0x10|0x03|
PDA: PDA Menu Line 3 = SET TO 2900 RPM
JandyDvce: To ePump: Read To 0x78 of type Unknown '0x44' | HEX: 0x10|0x02|0x78|0x44|0x00|0x60|0x27|0x55|0x10|0x03|
JandyDvce: From ePump: Read To 0x00 of type Unknown '0x1f' | HEX: 0x10|0x02|0x00|0x1f|0x44|0x00|0x60|0x27|0x00|0xfc|0x10|0x03|
JandyDvce: From ePump: Read To 0x00 of type Unknown '0x1f' | HEX: 0x10|0x02|0x00|0x1f| 68| 0| 96| 39| 0|0xfc|0x10|0x03|
PDA: PDA Menu Line 3 = SET TO 2520 RPM
Type 0x1F and cmd 0x44 is RPM = 39 * (256) + 96 / 4 = 2520 or Byte 7 * 265 + Byte 6 / 4
Other commands on 0x1f are 67 & 68 (0x43 )
Some form of ping for being alive
Jun-23-23 17:41:00 PM Debug: JandyDvce: To ePump: Read To 0x78 of type Unknown '0x41' | HEX: 0x10|0x02|0x78|0x41|0xcb|0x10|0x03|

View File

@ -62,8 +62,10 @@ int json_chars(char *dest, const char *src, int dest_len, int src_len)
}
i--;
/* // Don't delete trailing whitespace
while (dest[i] == ' ')
i--;
*/
if (dest[i] != '\0') {
if (i < (dest_len-1))
@ -71,7 +73,7 @@ int json_chars(char *dest, const char *src, int dest_len, int src_len)
dest[i] = '\0';
}
return i;
}
@ -268,7 +270,7 @@ int build_device_JSON(struct aqualinkdata *aqdata, char* buffer, int size, bool
int i;
// IF temp units are F assume homekit is using F
bool homekit_f = (homekit && aqdata->temp_units==FAHRENHEIT);
bool homekit_f = (homekit && ( aqdata->temp_units==FAHRENHEIT || aqdata->temp_units == UNKNOWN) );
length += sprintf(buffer+length, "{\"type\": \"devices\"");
length += sprintf(buffer+length, ",\"date\":\"%s\"",aqdata->date );//"09/01/16 THU",
@ -351,7 +353,7 @@ int build_device_JSON(struct aqualinkdata *aqdata, char* buffer, int size, bool
}*/
}
if ( aqdata->frz_protect_set_point != TEMP_UNKNOWN && aqdata->air_temp != TEMP_UNKNOWN) {
if ( _aqconfig_.force_frzprotect_setpoints || (aqdata->frz_protect_set_point != TEMP_UNKNOWN && aqdata->air_temp != TEMP_UNKNOWN) ) {
length += sprintf(buffer+length, "{\"type\": \"setpoint_freeze\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"spvalue\": \"%.*f\", \"value\": \"%.*f\", \"int_status\": \"%d\" },",
FREEZE_PROTECT,
"Freeze Protection",
@ -390,6 +392,7 @@ int build_device_JSON(struct aqualinkdata *aqdata, char* buffer, int size, bool
((homekit_f)?2:0),
((homekit_f)?degFtoC(aqdata->swg_percent):aqdata->swg_percent));
//if (!homekit) { // For the moment keep boost off homekit
length += sprintf(buffer+length, "{\"type\": \"switch\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"int_status\": \"%d\"},",
SWG_BOOST_TOPIC,
"SWG Boost",
@ -624,8 +627,9 @@ int build_aqualink_status_JSON(struct aqualinkdata *aqdata, char* buffer, int si
length += sprintf(buffer+length, ", \"%s\": \"%s\"", SWG_BOOST_TOPIC, aqdata->boost?JSON_ON:JSON_OFF);
}
//NSF Need to come back and read what the display states when Freeze protection is on
if ( aqdata->frz_protect_set_point != TEMP_UNKNOWN ) {
length += sprintf(buffer+length, ", \"%s\": \"%s\"", FREEZE_PROTECT, aqdata->frz_protect_state==ON?JSON_ON:JSON_ENABLED);
if ( aqdata->frz_protect_set_point != TEMP_UNKNOWN || _aqconfig_.force_frzprotect_setpoints ) {
//length += sprintf(buffer+length, ", \"%s\": \"%s\"", FREEZE_PROTECT, aqdata->frz_protect_state==ON?JSON_ON:JSON_ENABLED);
length += sprintf(buffer+length, ", \"%s\": \"%s\"", FREEZE_PROTECT, LED2text(aqdata->frz_protect_state) );
}
//length += sprintf(buffer+length, "}, \"extra\":{" );

View File

@ -155,6 +155,41 @@ void ws_send_logmsg(struct mg_connection *nc, char *msg) {
}
}
sd_journal *open_journal() {
sd_journal *journal;
char filter[51];
if (sd_journal_open(&journal, SD_JOURNAL_LOCAL_ONLY) < 0)
{
LOGSystemError(errno, NET_LOG, "Failed to open journal");
return journal;
}
snprintf(filter, 50, "SYSLOG_IDENTIFIER=%s",_aqualink_data->self );
if (sd_journal_add_match(journal, filter, 0) < 0)
{
LOGSystemError(errno, NET_LOG, "Failed to set journal syslog filter");
sd_journal_close(journal);
return journal;
}
// Daemon will change PID after printing startup message, so don't filter on current PID
if (_aqconfig_.deamonize != true) {
snprintf(filter, 50, "_PID=%d",getpid());
if (sd_journal_add_match(journal, filter, 0) < 0)
{
LOGSystemError(errno, NET_LOG, "Failed to set journal pid filter");
sd_journal_close(journal);
return journal;
}
}
if (sd_journal_set_data_threshold(journal, LOGBUFFER) < 0)
{
LOG(NET_LOG, LOG_WARNING, "Failed to set journal message size\n");
}
return journal;
}
void find_aqualinkd_startupmsg(sd_journal *journal)
{
static bool once=false;
@ -184,14 +219,26 @@ void find_aqualinkd_startupmsg(sd_journal *journal)
sd_journal_previous_skip(journal, 100);
}
#define BLANK_JOURNAL_READ_RESET 100
bool _broadcast_systemd_logmessages(bool aqMgrActive, bool reOpenStaleConnection);
bool broadcast_systemd_logmessages(bool aqMgrActive) {
return _broadcast_systemd_logmessages(aqMgrActive, false);
}
bool _broadcast_systemd_logmessages(bool aqMgrActive, bool reOpenStaleConnection) {
static sd_journal *journal;
static bool active = false;
char msg[WS_LOG_LENGTH];
static int cnt=0;
static char *cursor = NULL;
char filter[51];
//char filter[51];
if (reOpenStaleConnection) {
sd_journal_close(journal);
active = false;
}
if (!aqMgrActive) {
if (!active) {
return true;
@ -204,25 +251,11 @@ bool broadcast_systemd_logmessages(bool aqMgrActive) {
}
// aqManager is active
if (!active) {
if ( sd_journal_open(&journal, SD_JOURNAL_LOCAL_ONLY) < 0) {
if ( (journal = open_journal()) < 0) {
build_logmsg_JSON(msg, LOG_ERR, "Failed to open journal", WS_LOG_LENGTH,22);
ws_send_logmsg(_mgr.active_connections, msg);
return false;
}
snprintf(filter, 50, "SYSLOG_IDENTIFIER=%s",_aqualink_data->self );
if (sd_journal_add_match(journal, "SYSLOG_IDENTIFIER=aqualinkd", 0) < 0) {
build_logmsg_JSON(msg, LOG_ERR, "Failed to set journal syslog filter", WS_LOG_LENGTH,35);
ws_send_logmsg(_mgr.active_connections, msg);
sd_journal_close(journal);
return false;
}
snprintf(filter, 50, "_PID=%d",getpid());
if (sd_journal_add_match(journal, filter, 0) < 0) {
build_logmsg_JSON(msg, LOG_ERR, "Failed to set journal PID filter", WS_LOG_LENGTH,32);
ws_send_logmsg(_mgr.active_connections, msg);
sd_journal_close(journal);
return false;
}
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);
@ -268,17 +301,26 @@ bool broadcast_systemd_logmessages(bool aqMgrActive) {
} else if (rtn == 0) {
// Sometimes we get no errors, and nothing to read, even when their is.
// So if we get too many, restart but don;t reset the cursor.
// Could tesd moving sd_journal_get_cursor(journal, &cursor); line to here from above.
if (cnt++ == 100) {
//printf("**** %d Too many blank reads, resetting!! ****\n",cnt);
sd_journal_close(journal);
active = false;
// Could test moving sd_journal_get_cursor(journal, &cursor); line to here from above.
// Quick way to times blank reads by log level, since less logs written higher number of blank reads
if ( cnt++ >= BLANK_JOURNAL_READ_RESET * ( (LOG_DEBUG_SERIAL+1) - getSystemLogLevel() ) ) {
/* Stale connection, call ourselves to reopen*/
if (!reOpenStaleConnection) {
//LOG(NET_LOG, LOG_WARNING, "**** %d Too many blank reads, resetting!! ****\n",cnt);
return _broadcast_systemd_logmessages(aqMgrActive, true);
}
//LOG(NET_LOG, LOG_WARNING, "**** Reset didn't work ****\n",cnt);
//return false;
}
}
return true;
}
#define USEC_PER_SEC 1000000L
bool write_systemd_logmessages_2file(char *fname, int lines)
{
FILE *fp = NULL;
@ -287,35 +329,21 @@ bool write_systemd_logmessages_2file(char *fname, int lines)
size_t len;
const void *pri;
size_t plen;
char filter[51];
char tsbuffer[20];
uint64_t realtime;
struct tm tm;
time_t sec;
fp = fopen (fname, "w");
if (fp == NULL) {
LOG(NET_LOG, LOG_WARNING, "Failed to open tmp log file '%s'\n",fname);
return false;
}
if (sd_journal_open(&journal, SD_JOURNAL_LOCAL_ONLY) < 0)
{
LOG(NET_LOG, LOG_WARNING, "Failed to open journal");
if ( (journal = open_journal()) < 0) {
fclose (fp);
return false;
}
snprintf(filter, 50, "SYSLOG_IDENTIFIER=%s",_aqualink_data->self );
if (sd_journal_add_match(journal, "SYSLOG_IDENTIFIER=aqualinkd", 0) < 0)
{
LOG(NET_LOG, LOG_WARNING, "Failed to set journal syslog filter");
fclose (fp);
sd_journal_close(journal);
return false;
}
snprintf(filter, 50, "_PID=%d",getpid());
if (sd_journal_add_match(journal, filter, 0) < 0)
{
LOG(NET_LOG, LOG_WARNING, "Failed to set journal pid filter");
fclose (fp);
sd_journal_close(journal);
return false;
}
if (sd_journal_seek_tail(journal) < 0)
{
LOG(NET_LOG, LOG_WARNING, "Failed to seek to journal end");
@ -336,9 +364,14 @@ bool write_systemd_logmessages_2file(char *fname, int lines)
if (sd_journal_get_data(journal, "MESSAGE", &log, &len) < 0) {
LOG(NET_LOG, LOG_WARNING, "Failed to get journal message");
} else if (sd_journal_get_data(journal, "PRIORITY", &pri, &plen) < 0) {
LOG(NET_LOG, LOG_WARNING, "Failed to seek to journal message priority");
LOG(NET_LOG, LOG_WARNING, "Failed to get journal message priority");
} else if (sd_journal_get_realtime_usec(journal, &realtime) < 0) {
LOG(NET_LOG, LOG_WARNING, "Failed to get journal message timestamp");
} else {
fprintf(fp, "%-7s %.*s\n",elevel2text(atoi((const char *)pri+9)), (int)len-8,(const char *)log+8);
sec = (time_t)(realtime/USEC_PER_SEC);
localtime_r(&sec, &tm);
strftime(tsbuffer, sizeof(tsbuffer), "%b %d %T", &tm); // need to capture return of this
fprintf(fp, "%-15s %-7s %.*s\n",tsbuffer,elevel2text(atoi((const char *)pri+9)), (int)len-8,(const char *)log+8);
}
}
@ -350,61 +383,6 @@ bool write_systemd_logmessages_2file(char *fname, int lines)
#endif
/* superseded with systemd/sd-journal
#define MAX_LOGSTACK 30
#define WS_LOG_LENGTH 200
char _logstack[MAX_LOGSTACK][WS_LOG_LENGTH];
int _logstack_place=0;
pthread_mutex_t logmsg_mutex;
void send_ws_logmessages()
{
pthread_mutex_lock(&logmsg_mutex);
// This pulls them off in the wrong order.
while (_logstack_place > 0) {
ws_send_logmsg(_mgr.active_connections, _logstack[0]);
memmove(&_logstack[0], &_logstack[1], WS_LOG_LENGTH * _logstack_place ) ;
_logstack_place--;
}
pthread_mutex_unlock(&logmsg_mutex);
}
void broadcast_log(char *msg) {
// NSF This causes mongoose to core dump after a period of time due to number of messages
// so remove until get time to update to new mongoose version.
//return;
#ifdef AQ_NO_THREAD_NETSERVICE
if (_keepNetServicesRunning && !_aqconfig_.thread_netservices && _aqualink_data != NULL && _aqualink_data->aqManagerActive)
{
char message[WS_LOG_LENGTH];
build_logmsg_JSON(message, msg, WS_LOG_LENGTH, strlen(msg));
_ws_send_logmsg(_mgr.active_connections , message);
return;
}
#endif
// See if we have and manager runnig first so we return ASAP.
// Since this get's called long before net_Services is started, also check we are running.
if (_keepNetServicesRunning && _net_thread_id != 0 && _aqualink_data != NULL && _aqualink_data->aqManagerActive)
{
pthread_mutex_lock(&logmsg_mutex);
if (_logstack_place < MAX_LOGSTACK)
{
//printf("**** Add message %s\n",msg);
// This need mutex lock on _logstack
build_logmsg_JSON(_logstack[_logstack_place++], msg, WS_LOG_LENGTH, strlen(msg) );
} else {
// Need to figure this out, can't send error message as that will put us in a infinate loop.
fprintf(stderr, "*** ERROR Log queue full ***\n");
// Use the last message to let UI know
build_logmsg_JSON(_logstack[MAX_LOGSTACK-1], "Error: *** Logs truncated, see server to complete list of message ***\n", LOGBUFFER, strlen(msg) );
}
pthread_mutex_unlock(&logmsg_mutex);
}
}
*/
void _broadcast_aqualinkstate(struct mg_connection *nc)
{
static int mqtt_count=0;
@ -997,7 +975,7 @@ uriAtype action_URI(request_source from, const char *URI, int uri_length, float
} else if (strncmp(ri1, "removelogmask", 13) == 0 && from == NET_WS) { // Only valid from websocket.
removeDebugLogMask(round(value));
return uAQmanager; // Want to resent updated status
} else if (strncmp(ri1, "log2file", 5) == 0) {
} else if (strncmp(ri1, "logfile", 7) == 0) {
/*
if (ri2 != NULL && strncmp(ri2, "start", 5) == 0) {
startInlineLog2File();
@ -1485,8 +1463,16 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg)
break;
#else
case uLogDownload:
LOG(NET_LOG, LOG_DEBUG, "Downloading log of max %d lines\n",value>0?(int)value:1000);
if (write_systemd_logmessages_2file("/dev/shm/aqualinkd.log", value>0?(int)value:1000) ) {
//int lines = 1000;
#define DEFAULT_LOG_DOWNLOAD_LINES 100
// If lines was passed in post use it, if not see if it's next path in URI is a number
if (value == 0.0) {
// /api/<downloadmsg>/<lines>
char *pt = rsm_lastindexof(buf, "/", strlen(buf));
value = atoi(pt+1);
}
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\""));
remove("/dev/shm/aqualinkd.log");
}

View File

@ -56,16 +56,16 @@ void print_onetouch_menu()
int i;
for (i=0; i < ONETOUCH_LINES; i++) {
//printf("PDA Line %d = %s\n",i,_menu[i]);
LOG(ONET_LOG,LOG_INFO, "OneTouch Menu Line %d = %s\n",i,_menu[i]);
LOG(ONET_LOG,LOG_INFO, "Menu Line %d = %s\n",i,_menu[i]);
}
if (_ot_hlightcharindexstart > -1) {
LOG(ONET_LOG,LOG_INFO, "OneTouch Menu highlighted line = %d, '%s' hligh-char(s) '%.*s'\n",
LOG(ONET_LOG,LOG_INFO, "Menu highlighted line = %d, '%s' hligh-char(s) '%.*s'\n",
_ot_hlightindex,_menu[_ot_hlightindex],
(_ot_hlightcharindexstop - _ot_hlightcharindexstart + 1),
&_menu[_ot_hlightindex][_ot_hlightcharindexstart]);
} else if (_ot_hlightindex > -1) {
LOG(ONET_LOG,LOG_INFO, "OneTouch Menu highlighted line = %d = %s\n",_ot_hlightindex,_menu[_ot_hlightindex]);
LOG(ONET_LOG,LOG_INFO, "Menu highlighted line = %d = %s\n",_ot_hlightindex,_menu[_ot_hlightindex]);
}
}
@ -180,7 +180,7 @@ bool process_onetouch_menu_packet(struct aqualinkdata *aq_data, unsigned char* p
_ot_hlightcharindexstart = -1;
_ot_hlightcharindexstart = -1;
}
LOG(ONET_LOG,LOG_DEBUG, "OneTouch Menu highlighted line = %d = %s\n",_ot_hlightindex,_menu[_ot_hlightindex]);
LOG(ONET_LOG,LOG_DEBUG, "Menu highlighted line = %d = %s\n",_ot_hlightindex,_menu[_ot_hlightindex]);
//if (getLogLevel() >= LOG_DEBUG){print_onetouch_menu();}
break;
case CMD_PDA_HIGHLIGHTCHARS:
@ -195,7 +195,7 @@ bool process_onetouch_menu_packet(struct aqualinkdata *aq_data, unsigned char* p
_ot_hlightcharindexstart = -1;
_ot_hlightcharindexstart = -1;
}
LOG(ONET_LOG,LOG_DEBUG, "OneTouch Menu highlighted line = %d, '%s' chars '%.*s'\n",
LOG(ONET_LOG,LOG_DEBUG, "Menu highlighted line = %d, '%s' chars '%.*s'\n",
_ot_hlightindex,
_menu[_ot_hlightindex],
(_ot_hlightcharindexstop - _ot_hlightcharindexstart) + 1,
@ -368,7 +368,7 @@ bool get_pumpinfo_from_menu_OLD(struct aqualinkdata *aq_data, int menuLineIdx)
rpm = PUMP_ERROR;
}
LOG(ONET_LOG, LOG_DEBUG, "Found OneTouch Pump '%s', Index %d, RPM %d, Watts %d, GPM %d\n", _menu[menuLineIdx], pump_index, rpm, watts, gpm);
LOG(ONET_LOG, LOG_DEBUG, "Found Pump '%s', Index %d, RPM %d, Watts %d, GPM %d\n", _menu[menuLineIdx], pump_index, rpm, watts, gpm);
for (int i = 0; i < aq_data->num_pumps; i++)
{
@ -381,7 +381,7 @@ bool get_pumpinfo_from_menu_OLD(struct aqualinkdata *aq_data, int menuLineIdx)
aq_data->pumps[i].watts = watts;
aq_data->pumps[i].gpm = gpm;
// LOG(ONET_LOG,LOG_INFO, "Matched OneTouch Pump to Index %d, RPM %d, Watts %d, GPM %d\n",i,rpm,watts,gpm);
LOG(ONET_LOG, LOG_INFO, "Matched OneTouch Pump to '%s', Index %d, RPM %d, Watts %d, GPM %d\n", aq_data->pumps[i].button->name, i, rpm, watts, gpm);
LOG(ONET_LOG, LOG_INFO, "Matched Pump to '%s', Index %d, RPM %d, Watts %d, GPM %d\n", aq_data->pumps[i].button->name, i, rpm, watts, gpm);
if (aq_data->pumps[i].pumpType == PT_UNKNOWN)
{
if (rsm_strcmp(_menu[2], "Intelliflo VS") == 0)
@ -392,7 +392,7 @@ bool get_pumpinfo_from_menu_OLD(struct aqualinkdata *aq_data, int menuLineIdx)
rsm_strcmp(_menu[2], "ePump AC") == 0)
aq_data->pumps[i].pumpType = EPUMP;
LOG(ONET_LOG, LOG_INFO, "OneTouch Pump index %d set PumpType to %d\n", i, aq_data->pumps[i].pumpType);
LOG(ONET_LOG, LOG_INFO, "Pump index %d set PumpType to %d\n", i, aq_data->pumps[i].pumpType);
}
return true;
}
@ -463,7 +463,7 @@ bool get_pumpinfo_from_menu(struct aqualinkdata *aq_data, int menuLineIdx, int p
return true;
}
}
LOG(ONET_LOG,LOG_WARNING, "PDA Could not find config for Pump %s, Number %d, RPM %d, Watts %d, GPM %d\n",_menu[menuLineIdx],pump_number,rpm,watts,gpm);
LOG(ONET_LOG,LOG_WARNING, "Could not find config for Pump %s, Number %d, RPM %d, Watts %d, GPM %d\n",_menu[menuLineIdx],pump_number,rpm,watts,gpm);
return false;
}
@ -480,7 +480,7 @@ bool get_chemlinkinfo_from_menu(struct aqualinkdata *aq_data, int menuLineIdx)
int orp = atoi(&_menu[menuLineIdx + 1][4]);
char *indx = strchr(_menu[menuLineIdx + 1], '/');
float ph = atof(indx + 3);
LOG(ONET_LOG, LOG_INFO, "OneTouch Cemlink ORP = %d PH = %f\n", orp, ph);
LOG(ONET_LOG, LOG_INFO, "Cemlink ORP = %d PH = %f\n", orp, ph);
if (aq_data->ph != ph || aq_data->orp != orp)
{
aq_data->ph = ph;
@ -517,7 +517,7 @@ bool get_aquapureinfo_from_menu(struct aqualinkdata *aq_data, int menuLineIdx)
aq_data->swg_ppm = ppm;
rtn = true;
}
LOG(ONET_LOG, LOG_INFO, "OneTouch Aquapure SWG %d%, %d PPM\n", swgp, ppm);
LOG(ONET_LOG, LOG_INFO, "Aquapure SWG %d%, %d PPM\n", swgp, ppm);
}
#endif
@ -533,7 +533,7 @@ bool get_RS16buttoninfo_from_menu(struct aqualinkdata *aq_data, int menuLineIdx)
if (rsm_strcmp(_menu[menuLineIdx], aq_data->aqbuttons[i].label) == 0)
{
// Matched must be on.
LOG(ONET_LOG, LOG_INFO, "OneTouch RS16 equiptment status '%s' matched '%s'\n", _menu[menuLineIdx], aq_data->aqbuttons[i].label);
LOG(ONET_LOG, LOG_INFO, "RS16 equiptment status '%s' matched '%s'\n", _menu[menuLineIdx], aq_data->aqbuttons[i].label);
rs16led_update(aq_data, i);
aq_data->aqbuttons[i].led->state = ON;
return true;

Binary file not shown.

View File

@ -161,6 +161,10 @@ force_SWG = no
# heaters to be listed as thermostats vs switches on startup, helps with homekit.
force_PS_setpoints = no
# AqualinkD can take sime time to find freeze protect (if panel supports it), This will force the freeze protect
# to be listed as thermostat on startup.
force_Frzprotect_setpoints = no
# Lights can be programmed by control panel or AqualinkD (if controlpanel doesn;t support specific light or light mode you want)
# IF YOU WANT AQUALINKD TO PROGRAM THE LIGHT, IT MUST NOT BE CONFIGURED AS A COLOR LIGHT IN THE JANDY CONTROL PANEL.
# Light probramming mode. 0=safe mode, but slow.

Binary file not shown.

View File

@ -196,6 +196,28 @@ int rsm_strcmp(const char *haystack, const char *needle)
return strncasecmp(sp1, sp2, strlen(sp2));
}
/*
* Find last index of char in string.
* char *sp;
* sp = rsm_lastindexof("/api/crap/something/100", "/", 23);
* printf("Next char after '/' = %c\n",*sp+1);
* printf("Next string after '/' = %s\n",sp+1);
*/
char *rsm_lastindexof(const char *haystack, const char *needle, size_t length)
{
char *ep = (char *)haystack + length;
for ( ; ep != (char *)haystack; ep--) {
if ( *ep == *needle) {
return ep;
}
}
return NULL;
}
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))

View File

@ -9,6 +9,7 @@ char *rsm_strstr(const char *haystack, const char *needle);
//char *rsm_strnstr(const char *haystack, const char *needle, int length);
char *rsm_strnstr(const char *haystack, const char *needle, size_t slen);
char *rsm_strncasestr(const char *haystack, const char *needle, size_t length);
char *rsm_lastindexof(const char *haystack, const char *needle, size_t length);
int rsm_strncpy(char *dest, const unsigned char *src, int dest_len, int src_len);
int rsm_strcmp(const char *s1, const char *s2);

View File

@ -110,13 +110,14 @@ int serial_logger (int rs_fd, char *port_name, int logLevel) {
#define KEYPAD " <-- RS Keypad"
#define SPA_R " <-- Spa remote"
#define AQUA " <-- Aqualink (iAqualink / Touch)"
#define HEATER " <-- LX Heater"
#define LX_HEATER " <-- LX Heater"
#define ONE_T " <-- Onetouch device"
#define RS_SERL " <-- RS Serial Adapter"
#define PC_DOCK " <-- PC Interface (RS485 to RS232)"
#define PDA " <-- PDA Remote"
#define EPUMP " <-- Jandy VSP ePump"
#define CHEM " <-- Chemlink"
#define LXI_LRZ_HEATER " <-- LXi / LRZ Heater"
#define UNKNOWN " <-- Unknown Device"
@ -140,7 +141,7 @@ const char *getDevice(unsigned char ID) {
if (ID >= 0x30 && ID <= 0x33)
return AQUA;
if (ID >= 0x38 && ID <= 0x3B)
return HEATER;
return LX_HEATER;
if (ID >= 0x40 && ID <= 0x43)
return ONE_T;
if (ID >= 0x48 && ID <= 0x4B)
@ -149,6 +150,8 @@ const char *getDevice(unsigned char ID) {
return PC_DOCK;
if (ID >= 0x60 && ID <= 0x63)
return PDA;
if (ID >= 0x68 && ID <= 0x6B)
return LXI_LRZ_HEATER;
//if (ID >= 0x70 && ID <= 0x73)
if (ID >= 0x78 && ID <= 0x7B)
return EPUMP;
@ -422,7 +425,11 @@ int main(int argc, char *argv[]) {
}
}
#ifdef AQ_MANAGER
setLoggingPrms(logLevel, false, NULL);
#else
setLoggingPrms(logLevel, false, false, NULL);
#endif
if (_playback_file) {
rs_fd = open(argv[1], O_RDONLY | O_NOCTTY | O_NONBLOCK | O_NDELAY);

48
utils.c
View File

@ -54,20 +54,29 @@ void broadcast_log(char *msg);
static bool _daemonise = false;
#ifndef AD_MANAGER
#ifndef AQ_MANAGER
static bool _log2file = false;
//static int _log_level = LOG_ERR;
static int _log_level = LOG_WARNING;
static char *_log_filename = NULL;
static bool _cfg_log2file;
static int _cfg_log_level;
#endif
static int _log_level = LOG_WARNING;
static char *_loq_display_message = NULL;
int16_t _logforcemask = 0;
//static char _log_filename[256];
#ifdef AQ_MANAGER
void setLoggingPrms(int level , bool deamonized, char *error_messages)
{
_log_level = level;
_daemonise = deamonized;
_loq_display_message = error_messages;
//_cfg_log_level = _log_level;
}
#else
void setLoggingPrms(int level , bool deamonized, char* log_file, char *error_messages)
{
_log_level = level;
@ -85,6 +94,7 @@ void setLoggingPrms(int level , bool deamonized, char* log_file, char *error_mes
//strcpy(_log_filename, log_file);
}
}
#endif // AQ_MANAGER
void setSystemLogLevel( int level)
{
@ -174,10 +184,9 @@ bool islogFileReady()
/*
* This function reports the error and
* exits back to the shell:
* This function reports the last error
*/
void displayLastSystemError (const char *on_what)
void LOGSystemError (int errnum, int16_t from, const char *on_what)
{
fputs (strerror (errno), stderr);
fputs (": ", stderr);
@ -187,10 +196,27 @@ void displayLastSystemError (const char *on_what)
if (_daemonise == TRUE)
{
//logMessage (LOG_ERR, "%d : %s", errno, on_what);
LOG(AQUA_LOG, LOG_ERR, "%d : %s", errno, on_what);
LOG(AQUA_LOG, LOG_ERR, "%s (%d) : %s\n", strerror (errno), errno, on_what);
closelog ();
}
}
void displayLastSystemError (const char *on_what)
{
LOGSystemError(errno, AQUA_LOG, on_what);
/*
fputs (strerror (errno), stderr);
fputs (": ", stderr);
fputs (on_what, stderr);
fputc ('\n', stderr);
if (_daemonise == TRUE)
{
//logMessage (LOG_ERR, "%d : %s", errno, on_what);
LOG(AQUA_LOG, LOG_ERR, "%s (%d) : %s", strerror (errno), errno, on_what);
closelog ();
}
*/
}
/*
From -- syslog.h --
@ -540,7 +566,7 @@ void _LOG(int16_t from, int msg_level, char *message)
closelog ();
}
#ifdef AQ_MANAGER // Always use syslog with aqmanager
#ifdef AQ_MANAGER // Always use systemd journel with aqmanager
//sd_journal_print()
//openlog("aqualinkd", 0, LOG_DAEMON);
if (msg_level > LOG_DEBUG) // Let's not confuse syslog with custom levels
@ -560,7 +586,7 @@ void _LOG(int16_t from, int msg_level, char *message)
closelog ();
//return;
}
#endif
#endif //AQ_MANAGER
//int len;
message[8] = ' ';
@ -583,6 +609,7 @@ void _LOG(int16_t from, int msg_level, char *message)
snprintf(_loq_display_message, 127, "%s\n",message);
}
#ifndef AQ_MANAGER
if (_log2file == TRUE && _log_filename != NULL) {
char time[TIMESTAMP_LENGTH];
int fp = open(_log_filename, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
@ -604,7 +631,8 @@ void _LOG(int16_t from, int msg_level, char *message)
fprintf (stderr, "Can't open debug log %s\n %s", _log_filename, message);
}
}
#endif //AQ_MANAGER
if (_daemonise == FALSE) {
if (msg_level == LOG_ERR) {
fprintf(stderr, "%s", message);

View File

@ -53,13 +53,17 @@ typedef enum
} bool;
*/
//void setLoggingPrms(int level , bool deamonized, char* log_file);
#ifdef AQ_MANAGER
void setLoggingPrms(int level , bool deamonized, char *error_messages);
#else
void setLoggingPrms(int level , bool deamonized, char* log_file, char *error_messages);
#endif
int getLogLevel(int16_t from);
int getSystemLogLevel();
void setSystemLogLevel( int level);
void daemonise ( char *pidFile, void (*main_function)(void) );
//void debugPrint (char *format, ...);
void displayLastSystemError (const char *on_what);
void addDebugLogMask(int16_t flag);
@ -76,6 +80,8 @@ const char* loglevel2name(int level);
//void LOG(int from, int level, char *format, ...);
void LOG(int16_t from, int msg_level, const char *format, ...);
void LOGSystemError (int errnum, int16_t from, const char *on_what);
void displayLastSystemError (const char *on_what);
int count_characters(const char *str, char character);
//void readCfg (char *cfgFile);

View File

@ -135,6 +135,7 @@
flex-direction: column-reverse;
background-color: #2b2b2b;
color: white;
white-space: pre;
}
.toggle {
@ -264,7 +265,7 @@
// disable the download log button on startup
//disablebutton("downloadlog");
//disabletoggle("log2file");
//disabletoggle("logfile");
//disabletoggle("seriallog");
}
@ -310,6 +311,8 @@
}
var last_message_element = null;
var log_element_count = 0;
const LOGS2DISPLAY = 500;
function update_log_message(message) {
var element = document.createElement("div");
@ -317,8 +320,22 @@
element.classList.add("logmsgerror");
}
var logcontainer = document.getElementById("logs");
if (log_element_count >= LOGS2DISPLAY) {
try {
console.log("Removing "+logcontainer.lastElementChild.innerHTML);
logcontainer.lastElementChild.remove();
} catch(e) {
console.log("ERROR Removing log '"+logcontainer.lastElementChild.innerHTML+"'");
log_element_count++;
}
} else {
log_element_count++;
}
element.appendChild(document.createTextNode(message));
document.getElementById("logs").insertBefore(element, last_message_element);
logcontainer.insertBefore(element, last_message_element);
last_message_element = element;
}
@ -352,7 +369,8 @@
}
function setlogfile(caller) {
var msg = {};
if (caller.id == "log2file") {
/*
if (caller.id == "logfile") {
var startstop = "";
if (caller.checked) {
startstop = "start";
@ -360,18 +378,19 @@
startstop = "stop";
}
msg = {
uri: "log2file/" + startstop,
uri: "logfile/" + startstop,
value: 0
};
} else if (caller.id == "cleanlog") {
msg = {
uri: "log2file/clean",
uri: "logfile/clean",
value: 0
};
} else if (caller.id == "downloadlog") {
} else*/
if (caller.id == "downloadlog") {
//window.location = '/api/debug/download';
downloadFile('/api/log2file/download/aqualinkd.log', document.getElementById("logsize").value);
downloadFile('/api/logfile/download', document.getElementById("logsize").value);
return;
}
@ -380,23 +399,26 @@
function downloadFile(filePath, lines) {
console.log("Lines=" + lines);
/*
var link = document.createElement('a');
link.href = filePath;
link.download = filePath.substr(filePath.lastIndexOf('/') + 1);
link.href = filePath+"/"+lines;
//link.download = filePath.substr(filePath.lastIndexOf('/') + 1);
link.download = "aqualinkd.log"
link.click();
*/
// Can't get a form post to cleanly download, and "a download" above can only do get. So add value
/*
var form = document.createElement('form');
form.setAttribute("style", "display: none;");
form.method='POST';
form.action=filePath;
form.target='aqd'; // Set trget to iframe "aqd"(aqualinkd), this stops WS for resetting
element=document.createElement('INPUT');
element.name='value';
element.value=lines;
form.appendChild(element);
document.body.appendChild(form);
form.submit();
*/
}
@ -461,14 +483,16 @@
if logfilename=NULL we can turn on/off logging to file.
logging2file will tell us if it's currently on or off
*/
console.log("deamonized=" + data['deamonized']);
console.log("logfilename=" + data['logfilename']);
console.log("logfileready=" + data['logfileready']);
if (data['deamonized'] == 'off') {
console.log("deamonized=" + data['deamonized'] + " Need to rename Restart to Reload");
}
/*
if (data['logfilename'] == "(null)")
enabletoggle("log2file");
enabletoggle("logfile");
else
disabletoggle("log2file");
disabletoggle("logfile");
*/
/*
if (data['logfileready'] == "on") {
enablebutton("downloadlog");
@ -633,7 +657,7 @@
disablebutton("restart");
disablebutton("seriallog");
disablebutton("downloadlog");
//disabletoggle("log2file");
//disabletoggle("logfile");
var coll = document.getElementsByClassName("collapsible");
var i;
@ -723,7 +747,7 @@
<button class="collapsible">Log File</button>
<div class="content" id="loglevels">
<!--
<label class="toggle logtoggle"><input class="toggle-checkbox" type="checkbox" id="log2file"
<label class="toggle logtoggle"><input class="toggle-checkbox" type="checkbox" id="logfile"
onclick="setlogfile(this);">
<div class="toggle-switch"></div><span class="toggle-label">Log to file</span>
</label>
@ -752,6 +776,7 @@
<!--<div class="inner">END</div>-->
</div>
</div>
<!--<iframe src='about:blank' id="logdownload"></iframe>-->
</div>
</body>

View File

@ -99,6 +99,9 @@
// This will turn on/off the Spa Heater when you turn on/off Spa Mode.
//var link_spa_and_spa_heater = true;
// Change the min max for heater slider
var heater_slider_min = 36;
var heater_slider_max = 104;
// Change the slider for timers
var timer_slider_min = 0;
//var timer_slider_max = 360;

View File

@ -1389,8 +1389,10 @@
}
} else if (type == 'setpoint_thermo') {
if (_temperature_units != 'c') { // Change to DegF
slider.min = 36;
slider.max = 104;
//slider.min = 36;
//slider.max = 104;
slider.min = ((typeof heater_slider_min !== 'undefined')?heater_slider_min:36);
slider.max = ((typeof heater_slider_max !== 'undefined')?heater_slider_max:104);
slider.step = 1;
} else { // Change to DegF
slider.min = 0;