mirror of https://github.com/sfeakes/AqualinkD.git
Update v3.2.5
parent
675b0f52d1
commit
dbbd0dda0c
3
Makefile
3
Makefile
|
@ -14,6 +14,7 @@ AQ_PDA = true
|
|||
AQ_ONETOUCH = true
|
||||
AQ_IAQTOUCH = true
|
||||
AQ_MANAGER =true
|
||||
|
||||
#AQ_RS_EXTRA_OPTS = false
|
||||
#AQ_CONTAINER = false // this is for compiling for containers
|
||||
#AQ_MEMCMP = true // Not implimented correctly yet.
|
||||
|
@ -76,7 +77,7 @@ endif
|
|||
# Main source files
|
||||
SRCS = aqualinkd.c utils.c config.c aq_serial.c aq_panel.c aq_programmer.c net_services.c json_messages.c rs_msg_utils.c\
|
||||
devices_jandy.c packetLogger.c devices_pentair.c color_lights.c serialadapter.c aq_timer.c aq_scheduler.c web_config.c\
|
||||
serial_logger.c mongoose.c simulator.c timespec_subtract.c
|
||||
serial_logger.c mongoose.c hassio.c simulator.c timespec_subtract.c
|
||||
|
||||
|
||||
AQ_FLAGS =
|
||||
|
|
40
README.md
40
README.md
|
@ -67,7 +67,7 @@ Designed to mimic AqualinkRS devices, used to fully configure the master control
|
|||
* Full support for homekit scenes: ie: Create a "Spa scene" to: "turn spa on, set spa heater to X temperature and turn spa blower on", etc etc).
|
||||
|
||||
### In Home Assistant
|
||||
<img src="extras/HomeAssistant2.png?raw=true" width="800"></img>
|
||||
<img src="extras/HASSIO.png?raw=true" width="800"></img>
|
||||
|
||||
## All Web interfaces.
|
||||
* http://aqualink.ip/ <- (Standard WEB UI
|
||||
|
@ -83,7 +83,39 @@ Designed to mimic AqualinkRS devices, used to fully configure the master control
|
|||
* Add set time to OneTouch protocol.
|
||||
* Update AqualinkD Management console to manage configuration
|
||||
* Create iAqualink Touch Simulator
|
||||
* Probably decoded enough protocols for AuqlinkD to self configure.
|
||||
|
||||
<!--
|
||||
* NEED TO FIX for PDA and iAQT protocol.
|
||||
* Not always doing on/off
|
||||
* Heaters are slow to turn on, need to hit extra button
|
||||
* Spa turns on Spa Heat (first button on home page ???)
|
||||
* SWG Stays on
|
||||
* serial_logger
|
||||
* Add wiki documentation
|
||||
* about Heat vs Heater
|
||||
* Panel version
|
||||
* can't use iaquatouch panel / wireless
|
||||
|
||||
* Added iAqualinkTouch support for PDA only panels that can use that protocol.
|
||||
* PDA panel needs to be Rev 6.0 or newer.
|
||||
* This makes the PDA only panels quicker and less error prone.
|
||||
* Introduces color light support and VSP
|
||||
* Consider this PDA support Beta.
|
||||
* Read PDA Wiki
|
||||
-->
|
||||
|
||||
# Call for Help.
|
||||
* The only Jandy devices I have not decoded yet are LX heater & Chemical Feeder. If you have either of these devices and are willing to post some logs, please let me know, or post in the [Discussions area](https://github.com/sfeakes/AqualinkD/discussions)
|
||||
|
||||
# Update in Release 2.3.5
|
||||
* Added Home Assistant integration through MQTT discover
|
||||
* Please read the Wiki section on this [Wiki - HASSIO](https://github.com/sfeakes/AqualinkD/wiki#HASSIO)
|
||||
* There are still some enhacments to come on this.
|
||||
* Added support for reading extended information for Jandy JXi heaters.
|
||||
* Added Color Light to iAqualinkTouch protocol.
|
||||
* Fixed issue mqtt_timed_update (1~2 min rather than between 2 & 20 min)
|
||||
|
||||
# Update in Release 2.3.4
|
||||
* Changes for Docker
|
||||
* Updated simulator code base and added new simulators for AllButton, OneTouch & PDA.
|
||||
|
@ -95,11 +127,11 @@ Designed to mimic AqualinkRS devices, used to fully configure the master control
|
|||
|
||||
# Update in Release 2.3.3
|
||||
* Introduced Aqualink Manager UI http://aqualink.ip/aqmanager.html
|
||||
* [AqualinkD Manager](#AQManager)
|
||||
* [AqualinkD Manager](https://github.com/sfeakes/AqualinkD/wiki#AQManager)
|
||||
* Moved logging into systemd/journal (journalctl) from syslog
|
||||
* [AqualinkD Log](#Log)
|
||||
* [AqualinkD Log](https://github.com/sfeakes/AqualinkD/wiki#Log)
|
||||
* Updated to scheduler
|
||||
* [AqualinkD Scheduler](#Scheduler)
|
||||
* [AqualinkD Scheduler](https://github.com/sfeakes/AqualinkD/wiki#Scheduler)
|
||||
* Introduced RS485 frame delay / timer.
|
||||
* Improve PDA panels reliability (PDA pannels are slower than RS panels)
|
||||
* Potentially fixed Pentair VSP / SWG problems since Pentair VSP use a different protocol, this will allow a timed delay for the VSP to post a status messages. Seems to only effect RS485 bus when both a Pentair VSP and Jandy SWG are present.
|
||||
|
|
12
aq_mqtt.h
12
aq_mqtt.h
|
@ -23,6 +23,9 @@
|
|||
#define SWG_SETPOINT_TOPIC SWG_TOPIC "/setpoint"
|
||||
#define SWG_EXTENDED_TOPIC SWG_TOPIC "/fullstatus"
|
||||
#define SWG_BOOST_TOPIC SWG_TOPIC "/Boost"
|
||||
#define SWG_BOOST_DURATION_TOPIC SWG_BOOST_TOPIC "/duration"
|
||||
|
||||
#define SWG_STATUS_MSG_TOPIC SWG_TOPIC "/Display_Message"
|
||||
|
||||
#define CHEM_TOPIC "CHEM"
|
||||
#define CHEM_PH_TOPIC CHEM_TOPIC "/pH"
|
||||
|
@ -30,13 +33,18 @@
|
|||
#define CHEM_ORP_TOPIC CHEM_TOPIC "/ORP"
|
||||
#define CHRM_ORP_F_TOPIC CHEM_TOPIC "/ORP_f"
|
||||
|
||||
#define LXI_TOPIC "LXi"
|
||||
#define LXI_STATUS LXI_TOPIC "/Status"
|
||||
#define LXI_ERROR_CODE LXI_TOPIC "/Error"
|
||||
#define LXI_ERROR_MESSAGE LXI_TOPIC "/Error_Message"
|
||||
|
||||
#define FREEZE_PROTECT "Freeze_Protect"
|
||||
#define FREEZE_PROTECT_ENABELED FREEZE_PROTECT ENABELED_SUBT
|
||||
|
||||
#define BATTERY_STATE "Battery"
|
||||
|
||||
#define POOL_THERMO_TEMP_TOPIC BTN_POOL_HTR "/Temperature"
|
||||
#define SPA_THERMO_TEMP_TOPIC BTN_SPA_HTR "/Temperature"
|
||||
//#define POOL_THERMO_TEMP_TOPIC BTN_POOL_HTR "/Temperature"
|
||||
//#define SPA_THERMO_TEMP_TOPIC BTN_SPA_HTR "/Temperature"
|
||||
|
||||
//#define PUMP_TOPIC "Pump_"
|
||||
#define PUMP_RPM_TOPIC "/RPM"
|
||||
|
|
15
aq_panel.c
15
aq_panel.c
|
@ -525,9 +525,14 @@ bool setDeviceState(struct aqualinkdata *aqdata, int deviceIndex, bool isON)
|
|||
LOG(AQUA_LOG, LOG_INFO, "received '%s' for '%s', turning '%s'\n", (isON == false ? "OFF" : "ON"), button->name, (isON == false ? "OFF" : "ON"));
|
||||
#ifdef AQ_PDA
|
||||
if (isPDA_PANEL) {
|
||||
char msg[PTHREAD_ARG];
|
||||
sprintf(msg, "%-5d%-5d", deviceIndex, (isON == false ? OFF : ON));
|
||||
aq_programmer(AQ_PDA_DEVICE_ON_OFF, msg, aqdata);
|
||||
if (button->special_mask & PROGRAM_LIGHT && isPDA_IAQT) {
|
||||
// AqualinkTouch in PDA mode, we can program light. (if turing off, use standard AQ_PDA_DEVICE_ON_OFF below)
|
||||
programDeviceLightMode(aqdata, (isON?0:-1), deviceIndex); // -1 means off 0 means use current light mode
|
||||
} else {
|
||||
char msg[PTHREAD_ARG];
|
||||
sprintf(msg, "%-5d%-5d", deviceIndex, (isON == false ? OFF : ON));
|
||||
aq_programmer(AQ_PDA_DEVICE_ON_OFF, msg, aqdata);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
|
@ -598,7 +603,7 @@ void programDeviceLightMode(struct aqualinkdata *aqdata, int value, int button)
|
|||
int i;
|
||||
clight_detail *light = NULL;
|
||||
#ifdef AQ_PDA
|
||||
if (isPDA_PANEL) {
|
||||
if (isPDA_PANEL && !isPDA_IAQT) {
|
||||
LOG(AQUA_LOG,LOG_ERR, "Light mode control not supported in PDA mode\n");
|
||||
return;
|
||||
}
|
||||
|
@ -642,7 +647,7 @@ void programDeviceLightMode(struct aqualinkdata *aqdata, int value, int button)
|
|||
//bool panel_device_request(struct aqualinkdata *aqdata, action_type type, int deviceIndex, int value, int subIndex, bool fromMQTT)
|
||||
bool panel_device_request(struct aqualinkdata *aqdata, action_type type, int deviceIndex, int value, request_source source)
|
||||
{
|
||||
//LOG(AQUA_LOG,LOG_NOTICE, "Device request type %d for deviceindex %d of value %d from %d\n",type,deviceIndex, value, source);
|
||||
LOG(AQUA_LOG,LOG_INFO, "Device request type %d for deviceindex %d of value %d from %d\n",type,deviceIndex, value, source);
|
||||
switch (type) {
|
||||
case ON_OFF:
|
||||
//setDeviceState(&aqdata->aqbuttons[deviceIndex], value<=0?false:true, deviceIndex );
|
||||
|
|
|
@ -397,8 +397,15 @@ void queueGetProgramData(emulation_type source_type, struct aqualinkdata *aq_dat
|
|||
}
|
||||
}
|
||||
#ifdef AQ_PDA
|
||||
} else if ( source_type == AQUAPDA) {
|
||||
} else if ( isPDA_PANEL && source_type == AQUAPDA) {
|
||||
aq_programmer(AQ_PDA_INIT, NULL, aq_data);
|
||||
} else if ( isPDA_PANEL && source_type == IAQTOUCH) {
|
||||
//aq_programmer(AQ_PDA_INIT, NULL, aq_data);
|
||||
if (_aqconfig_.use_panel_aux_labels) {
|
||||
aq_programmer(AQ_GET_AUX_LABELS, NULL, aq_data);
|
||||
}
|
||||
aq_programmer(AQ_GET_IAQTOUCH_SETPOINTS, NULL, aq_data);
|
||||
|
||||
#endif
|
||||
} else { // Must be all button only
|
||||
aq_programmer(AQ_GET_POOL_SPA_HEATER_TEMPS, NULL, aq_data);
|
||||
|
@ -468,7 +475,8 @@ bool in_light_programming_mode(struct aqualinkdata *aq_data)
|
|||
{
|
||||
if ( ( aq_data->active_thread.thread_id != 0 ) &&
|
||||
( aq_data->active_thread.ptype == AQ_SET_LIGHTPROGRAM_MODE ||
|
||||
aq_data->active_thread.ptype == AQ_SET_LIGHTCOLOR_MODE)
|
||||
aq_data->active_thread.ptype == AQ_SET_LIGHTCOLOR_MODE ||
|
||||
aq_data->active_thread.ptype == AQ_SET_IAQTOUCH_LIGHTCOLOR_MODE)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
@ -527,7 +535,9 @@ bool in_iaqt_programming_mode(struct aqualinkdata *aq_data)
|
|||
aq_data->active_thread.ptype == AQ_SET_IAQTOUCH_POOL_HEATER_TEMP ||
|
||||
aq_data->active_thread.ptype == AQ_SET_IAQTOUCH_SPA_HEATER_TEMP ||
|
||||
aq_data->active_thread.ptype == AQ_SET_IAQTOUCH_SET_TIME ||
|
||||
aq_data->active_thread.ptype == AQ_SET_IAQTOUCH_PUMP_VS_PROGRAM)
|
||||
aq_data->active_thread.ptype == AQ_SET_IAQTOUCH_PUMP_VS_PROGRAM ||
|
||||
aq_data->active_thread.ptype == AQ_SET_IAQTOUCH_DEVICE_ON_OFF ||
|
||||
aq_data->active_thread.ptype == AQ_SET_IAQTOUCH_LIGHTCOLOR_MODE)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
@ -580,14 +590,14 @@ void kick_aq_program_thread(struct aqualinkdata *aq_data, emulation_type source_
|
|||
LOG(ONET_LOG, LOG_DEBUG, "Kicking OneTouch thread %d,%p\n",aq_data->active_thread.ptype, aq_data->active_thread.thread_id);
|
||||
pthread_cond_broadcast(&aq_data->active_thread.thread_cond);
|
||||
}
|
||||
else if (source_type == ALLBUTTON && !in_ot_programming_mode(aq_data)) {
|
||||
LOG(PROG_LOG, LOG_DEBUG, "Kicking RS thread %d,%p message '%s'\n",aq_data->active_thread.ptype, aq_data->active_thread.thread_id,aq_data->last_message);
|
||||
pthread_cond_broadcast(&aq_data->active_thread.thread_cond);
|
||||
}
|
||||
else if (source_type == IAQTOUCH && in_iaqt_programming_mode(aq_data)) {
|
||||
LOG(IAQT_LOG, LOG_DEBUG, "Kicking IAQ Touch thread %d,%p\n",aq_data->active_thread.ptype, aq_data->active_thread.thread_id);
|
||||
pthread_cond_broadcast(&aq_data->active_thread.thread_cond);
|
||||
}
|
||||
else if (source_type == ALLBUTTON && !in_ot_programming_mode(aq_data) && !in_iaqt_programming_mode(aq_data)) {
|
||||
LOG(PROG_LOG, LOG_DEBUG, "Kicking RS Allbutton thread %d,%p message '%s'\n",aq_data->active_thread.ptype, aq_data->active_thread.thread_id,aq_data->last_message);
|
||||
pthread_cond_broadcast(&aq_data->active_thread.thread_cond);
|
||||
}
|
||||
#ifdef AQ_PDA
|
||||
else if (source_type == AQUAPDA && !in_ot_programming_mode(aq_data)) {
|
||||
LOG(PDA_LOG, LOG_DEBUG, "Kicking PDA thread %d,%p\n",aq_data->active_thread.ptype, aq_data->active_thread.thread_id);
|
||||
|
@ -671,7 +681,7 @@ void _aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_dat
|
|||
else if (r_type == AQ_SET_SPA_HEATER_TEMP)
|
||||
type = AQ_SET_RSSADAPTER_SPA_HEATER_TEMP;
|
||||
} else if (r_type == AQ_SET_PUMP_RPM) {
|
||||
if (isONET_ENABLED)
|
||||
if (isONET_ENABLED || isPDA_IAQT)
|
||||
type = AQ_SET_ONETOUCH_PUMP_RPM;
|
||||
else if (isIAQT_ENABLED)
|
||||
type = AQ_SET_IAQTOUCH_PUMP_RPM;
|
||||
|
@ -719,7 +729,7 @@ void _aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_dat
|
|||
}
|
||||
#endif
|
||||
#ifdef AQ_IAQTOUCH
|
||||
else if (isIAQT_ENABLED && isEXTP_ENABLED) {
|
||||
else if ((isIAQT_ENABLED && isEXTP_ENABLED) || isPDA_IAQT) {
|
||||
// IAQ Touch programming modes that should overite standard ones.
|
||||
switch (r_type){
|
||||
case AQ_GET_POOL_SPA_HEATER_TEMPS:
|
||||
|
@ -741,15 +751,30 @@ void _aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_dat
|
|||
case AQ_SET_TIME:
|
||||
type = AQ_SET_IAQTOUCH_SET_TIME;
|
||||
break;
|
||||
case AQ_PDA_DEVICE_ON_OFF:
|
||||
if (isPDA_IAQT) {
|
||||
type = AQ_SET_IAQTOUCH_DEVICE_ON_OFF;
|
||||
}
|
||||
break;
|
||||
// This isn;t going to work outside of PDA mode, if the labels are incorrect.
|
||||
case AQ_SET_LIGHTCOLOR_MODE:
|
||||
if (isPDA_IAQT) {
|
||||
type = AQ_SET_IAQTOUCH_LIGHTCOLOR_MODE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
type = r_type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef AQ_PDA
|
||||
// Check we are doing something valid request
|
||||
if (isPDA_PANEL) {
|
||||
if (isPDA_PANEL && !isPDA_IAQT)
|
||||
{
|
||||
pda_reset_sleep();
|
||||
if (type != AQ_PDA_INIT &&
|
||||
type != AQ_PDA_WAKE_INIT &&
|
||||
|
@ -1028,6 +1053,12 @@ void _aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_dat
|
|||
return;
|
||||
}
|
||||
break;
|
||||
case AQ_SET_IAQTOUCH_LIGHTCOLOR_MODE:
|
||||
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_iaqtouch_light_colormode, (void*)programmingthread) < 0) {
|
||||
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef AQ_PDA
|
||||
case AQ_PDA_INIT:
|
||||
|
@ -1054,6 +1085,12 @@ void _aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_dat
|
|||
return;
|
||||
}
|
||||
break;
|
||||
case AQ_SET_IAQTOUCH_DEVICE_ON_OFF:
|
||||
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_iaqtouch_device_on_off, (void*)programmingthread) < 0) {
|
||||
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
|
@ -1522,7 +1559,7 @@ void *set_aqualink_light_colormode( void *ptr )
|
|||
use_current_mode = true;
|
||||
LOG(PROG_LOG, LOG_INFO, "Light Programming #: %d, on button: %s, color light type: %d, using current mode\n", val, button->label, typ);
|
||||
} else {
|
||||
mode_name = light_mode_name(typ, val-1);
|
||||
mode_name = light_mode_name(typ, val-1, ALLBUTTON);
|
||||
use_current_mode = false;
|
||||
if (mode_name == NULL) {
|
||||
LOG(PROG_LOG, LOG_ERR, "Light Programming #: %d, on button: %s, color light type: %d, couldn't find mode name '%s'\n", val, button->label, typ, mode_name);
|
||||
|
@ -2615,6 +2652,12 @@ const char *ptypeName(program_type type)
|
|||
case AQ_SET_IAQTOUCH_SET_TIME:
|
||||
return "Set iAqualink Set Time";
|
||||
break;
|
||||
case AQ_SET_IAQTOUCH_DEVICE_ON_OFF:
|
||||
return "Set iAqualink Device On/Off";
|
||||
break;
|
||||
case AQ_SET_IAQTOUCH_LIGHTCOLOR_MODE:
|
||||
return "Set iAqualink Light Color (using panel)";
|
||||
break;
|
||||
#endif
|
||||
#ifdef AQ_PDA
|
||||
case AQ_PDA_INIT:
|
||||
|
@ -2680,6 +2723,7 @@ const char *programtypeDisplayName(program_type type)
|
|||
break;
|
||||
case AQ_SET_LIGHTPROGRAM_MODE:
|
||||
case AQ_SET_LIGHTCOLOR_MODE:
|
||||
case AQ_SET_IAQTOUCH_LIGHTCOLOR_MODE:
|
||||
return "Programming: setting light color";
|
||||
break;
|
||||
case AQ_SET_SWG_PERCENT:
|
||||
|
@ -2709,6 +2753,9 @@ const char *programtypeDisplayName(program_type type)
|
|||
case AQ_GET_IAQTOUCH_VSP_ASSIGNMENT:
|
||||
return "Get Pump Assignment";
|
||||
break;
|
||||
case AQ_SET_IAQTOUCH_DEVICE_ON_OFF:
|
||||
return "Programming: setting device on/off";
|
||||
break;
|
||||
#ifdef AQ_PDA
|
||||
case AQ_PDA_DEVICE_STATUS:
|
||||
return "Programming: retrieving PDA Device status";
|
||||
|
|
|
@ -83,6 +83,8 @@ typedef enum {
|
|||
AQ_SET_IAQTOUCH_POOL_HEATER_TEMP,
|
||||
AQ_SET_IAQTOUCH_SPA_HEATER_TEMP,
|
||||
AQ_SET_IAQTOUCH_SET_TIME,
|
||||
AQ_SET_IAQTOUCH_DEVICE_ON_OFF,
|
||||
AQ_SET_IAQTOUCH_LIGHTCOLOR_MODE,
|
||||
AQ_GET_RSSADAPTER_SETPOINTS,
|
||||
AQ_SET_RSSADAPTER_POOL_HEATER_TEMP,
|
||||
AQ_SET_RSSADAPTER_SPA_HEATER_TEMP,
|
||||
|
|
19
aq_serial.c
19
aq_serial.c
|
@ -140,6 +140,12 @@ const char* get_packet_type(unsigned char* packet , int length)
|
|||
case CMD_IAQ_STARTUP:
|
||||
return "iAq init";
|
||||
break;
|
||||
case CMD_IAQ_TITLE_MESSAGE:
|
||||
return "iAq ProductName";
|
||||
break;
|
||||
case CMD_IAQ_MSG_LONG:
|
||||
return "iAq Popup message";
|
||||
break;
|
||||
case RSSA_DEV_STATUS:
|
||||
// This is a fail reply 0x10|0x02|0x48|0x13|0x02|0x00|0x10|0x00|0x7f|0x10|0x03|
|
||||
// Rather than check all, just check 0x02 and checksum sin't I'm not sure 0x10 means faiure without 0x00 around it.
|
||||
|
@ -165,6 +171,19 @@ const char* get_packet_type(unsigned char* packet , int length)
|
|||
case CMD_EPUMP_WATTS:
|
||||
return "ePump get Watts";
|
||||
break;
|
||||
case CMD_JXI_PING:
|
||||
if (packet[4] == 0x19)
|
||||
return "LXi heater on";
|
||||
else // 0x11 is normal off
|
||||
return "LXi heater ping";
|
||||
break;
|
||||
case CMD_JXI_STATUS:
|
||||
if (packet[6] == 0x10)
|
||||
return "LXi error";
|
||||
else
|
||||
return "LXi status";
|
||||
break;
|
||||
|
||||
default:
|
||||
sprintf(buf, "Unknown '0x%02hhx'", packet[PKT_CMD]);
|
||||
return buf;
|
||||
|
|
40
aq_serial.h
40
aq_serial.h
|
@ -38,12 +38,16 @@
|
|||
*/
|
||||
#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
|
||||
#define JANDY_DEC_SWG_MIN 80 // 0x50
|
||||
#define JANDY_DEC_SWG_MAX 83 // 0x53
|
||||
#define JANDY_DEC_PUMP_MIN 120 // 0x78
|
||||
#define JANDY_DEC_PUMP_MAX 123 // 0x7b
|
||||
#define JANDY_DEC_JXI_MIN 104 // 0x68
|
||||
#define JANDY_DEC_JXI_MAX 107 // 0x6B
|
||||
#define JANDY_DEC_LX_MIN 56 // 0x38
|
||||
#define JANDY_DEC_LX_MAX 59 // 0x3B
|
||||
#define JANDY_DEC_CHEM_MIN 128 // 0x80
|
||||
#define JANDY_DEC_CHEM_MAX 131 // 0x83
|
||||
|
||||
|
||||
// PACKET DEFINES Jandy
|
||||
|
@ -125,6 +129,10 @@
|
|||
#define CMD_PERCENT 0x11 // Set Percent
|
||||
#define CMD_PPM 0x16 // Received PPM
|
||||
|
||||
/* LXi Heater commands */
|
||||
#define CMD_JXI_PING 0x0c
|
||||
#define CMD_JXI_STATUS 0x0d
|
||||
|
||||
/* PDA KEY CODES */ // Just plating at the moment
|
||||
#define KEY_PDA_UP 0x06
|
||||
#define KEY_PDA_DOWN 0x05
|
||||
|
@ -294,8 +302,9 @@ SPILLOVER IS DISABLED WHILE SPA IS ON
|
|||
//#define SWG_STATUS_OFFLINE 0xFE
|
||||
|
||||
//#define SWG_STATUS_UNKNOWN -128 // Idiot. unsigned char....Derr.
|
||||
#define SWG_STATUS_OFF 0xFF // Documented this as off in API, so don't change.
|
||||
#define SWG_STATUS_UNKNOWN 0xFE
|
||||
#define SWG_STATUS_OFF 0xFF // Documented this as off in API, so don't change.
|
||||
#define SWG_STATUS_UNKNOWN 0xFE
|
||||
#define SWG_STATUS_GENFAULT 0xFD //This is displayed in the panel, so adding it
|
||||
// These are actual from RS485
|
||||
|
||||
#define SWG_STATUS_ON 0x00
|
||||
|
@ -337,7 +346,7 @@ SPILLOVER IS DISABLED WHILE SPA IS ON
|
|||
#define CMD_IAQ_POLL 0x30 // Poll message or ready to receive command
|
||||
#define CMD_IAQ_CTRL_READY 0x31 // Get this when we can send big control command
|
||||
#define CMD_IAQ_PAGE_CONTINUE 0x40 // Seems we get this on AUX device page when there is another page, keeps circuling through pages.
|
||||
|
||||
#define CMD_IAQ_TITLE_MESSAGE 0x2d // This is what the product name is set to (Jandy RS) usually
|
||||
//#define CMD_IAQ_VSP_ERROR 0x2c // Error when setting speed too high
|
||||
#define CMD_IAQ_MSG_LONG 0x2c // This this is display popup message. Next 2 bytes 0x00|0x01 = wait and then 0x00|0x00 clear
|
||||
/*
|
||||
|
@ -355,6 +364,10 @@ SPILLOVER IS DISABLED WHILE SPA IS ON
|
|||
#define KEY_IAQTCH_STATUS 0x06
|
||||
#define KEY_IAQTCH_PREV_PAGE 0x20
|
||||
#define KEY_IAQTCH_NEXT_PAGE 0x21
|
||||
#define KEY_IAQTCH_OK 0x01 //HEX: 0x10|0x02|0x00|0x01|0x00|0x01|0x14|0x10|0x03|. OK BUTTON
|
||||
|
||||
#define KEY_IAQTCH_PREV_PAGE_ALTERNATE 0x1d // System setup prev
|
||||
#define KEY_IAQTCH_NEXT_PAGE_ALTERNATE 0x1e // System setup next
|
||||
|
||||
// PAGE1 (Horosontal keys) (These are duplicate so probable delete)
|
||||
#define KEY_IAQTCH_HOMEP_KEY01 0x11
|
||||
|
@ -390,6 +403,7 @@ SPILLOVER IS DISABLED WHILE SPA IS ON
|
|||
#define IAQ_PAGE_STATUS2 0x2a // Something get this for Status rather than 0x5b
|
||||
#define IAQ_PAGE_DEVICES 0x36
|
||||
#define IAQ_PAGE_DEVICES2 0x35
|
||||
#define IAQ_PAGE_DEVICES3 0x51
|
||||
#define IAQ_PAGE_SET_TEMP 0x39
|
||||
#define IAQ_PAGE_MENU 0x0f
|
||||
#define IAQ_PAGE_SET_VSP 0x1e
|
||||
|
@ -401,10 +415,14 @@ SPILLOVER IS DISABLED WHILE SPA IS ON
|
|||
#define IAQ_PAGE_ONETOUCH 0x4d
|
||||
#define IAQ_PAGE_COLOR_LIGHT 0x48
|
||||
#define IAQ_PAGE_SYSTEM_SETUP 0x14
|
||||
#define IAQ_PAGE_SYSTEM_SETUP2 0x49
|
||||
#define IAQ_PAGE_SYSTEM_SETUP3 0x4a
|
||||
#define IAQ_PAGE_VSP_SETUP 0x2d
|
||||
#define IAQ_PAGE_FREEZE_PROTECT 0x11
|
||||
#define IAQ_PAGE_LABEL_AUX 0x32
|
||||
#define IAQ_PAGE_HELP 0x0c
|
||||
//#define IAQ_PAGE_START_BOOST 0x3f
|
||||
//#define IAQ_PAGE_DEGREES 0xFF // Added this as never want to actually select the page, just go to it.
|
||||
|
||||
|
||||
|
||||
|
@ -426,7 +444,9 @@ typedef enum {
|
|||
DRS_NONE,
|
||||
DRS_SWG,
|
||||
DRS_EPUMP,
|
||||
DRS_LXI
|
||||
DRS_JXI,
|
||||
DRS_LX,
|
||||
DRS_CHEM
|
||||
} rsDeviceType;
|
||||
|
||||
typedef enum {
|
||||
|
|
|
@ -205,6 +205,7 @@ struct aqualinkdata
|
|||
char time[AQ_MSGLEN];
|
||||
char last_message[AQ_MSGLONGLEN+1]; // Last ascii message from panel - allbutton (or PDA) protocol
|
||||
char last_display_message[AQ_MSGLONGLEN+1]; // Last message to display in web UI
|
||||
bool is_display_message_programming;
|
||||
aqled aqualinkleds[TOTAL_LEDS];
|
||||
aqkey aqbuttons[TOTAL_BUTTONS];
|
||||
unsigned short total_buttons;
|
||||
|
@ -220,6 +221,7 @@ struct aqualinkdata
|
|||
int swg_percent;
|
||||
int swg_ppm;
|
||||
unsigned char ar_swg_device_status; // Actual state
|
||||
unsigned char heater_err_status;
|
||||
aqledstate swg_led_state; // Display state for UI's
|
||||
aqledstate service_mode_state;
|
||||
aqledstate frz_protect_state;
|
||||
|
@ -229,6 +231,7 @@ struct aqualinkdata
|
|||
clight_detail lights[MAX_LIGHTS];
|
||||
bool boost;
|
||||
char boost_msg[10];
|
||||
int boost_duration; // need to remove boost message and use this
|
||||
float ph;
|
||||
int orp;
|
||||
|
||||
|
|
44
aqualinkd.c
44
aqualinkd.c
|
@ -180,6 +180,12 @@ bool checkAqualinkTime()
|
|||
LOG(AQUA_LOG,LOG_DEBUG, "time not checked, will check in %d seconds\n", TIME_CHECK_INTERVAL - time_difference);
|
||||
return true;
|
||||
}
|
||||
else if (strlen(_aqualink_data.date) <=0 ||
|
||||
strlen(_aqualink_data.time) <=0)
|
||||
{
|
||||
LOG(AQUA_LOG,LOG_DEBUG, "time not checked, no time from panel\n");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
last_checked = now;
|
||||
|
@ -188,7 +194,7 @@ bool checkAqualinkTime()
|
|||
|
||||
char datestr[DATE_STRING_LEN];
|
||||
#ifdef AQ_PDA
|
||||
if (isPDA_PANEL) {
|
||||
if (isPDA_PANEL && !isPDA_IAQT) {
|
||||
LOG(AQUA_LOG,LOG_DEBUG, "PDA Time Check\n");
|
||||
// date is simply a day or week for PDA.
|
||||
localtime_r(&now, &aq_tm);
|
||||
|
@ -358,6 +364,7 @@ int16_t RS16_endswithLEDstate(char *msg)
|
|||
#endif
|
||||
|
||||
|
||||
|
||||
void _processMessage(char *message, bool reset);
|
||||
|
||||
void processMessage(char *message)
|
||||
|
@ -454,6 +461,7 @@ void _processMessage(char *message, bool reset)
|
|||
if ((msg_loop & MSG_BOOST) != MSG_BOOST) {
|
||||
_aqualink_data.boost = false;
|
||||
_aqualink_data.boost_msg[0] = '\0';
|
||||
_aqualink_data.boost_duration = 0;
|
||||
//if (_aqualink_data.swg_percent >= 101)
|
||||
// _aqualink_data.swg_percent = 0;
|
||||
}
|
||||
|
@ -619,7 +627,8 @@ void _processMessage(char *message, bool reset)
|
|||
else if (strcasestr(msg, MSG_SWG_HIGH_SALT) != NULL)
|
||||
setSWGdeviceStatus(&_aqualink_data, ALLBUTTON, SWG_STATUS_HI_SALT);
|
||||
else if (strcasestr(msg, MSG_SWG_FAULT) != NULL)
|
||||
setSWGdeviceStatus(&_aqualink_data, ALLBUTTON, SWG_STATUS_CHECK_PCB);
|
||||
setSWGdeviceStatus(&_aqualink_data, ALLBUTTON, SWG_STATUS_GENFAULT);
|
||||
//setSWGdeviceStatus(&_aqualink_data, ALLBUTTON, SWG_STATUS_CHECK_PCB);
|
||||
|
||||
// Any of these messages want to display.
|
||||
strcpy(_aqualink_data.last_display_message, msg);
|
||||
|
@ -730,9 +739,11 @@ void _processMessage(char *message, bool reset)
|
|||
// Ignore messages if in programming mode. We get one of these turning off for some strange reason.
|
||||
if (in_programming_mode(&_aqualink_data) == false) {
|
||||
snprintf(_aqualink_data.boost_msg, 6, "%s", &msg[11]);
|
||||
_aqualink_data.boost_duration = rsm_HHMM2min(_aqualink_data.boost_msg);
|
||||
_aqualink_data.boost = true;
|
||||
msg_loop |= MSG_BOOST;
|
||||
msg_loop |= MSG_SWG;
|
||||
//convert_boost_to_duration(_aqualink_data.boost_msg)
|
||||
//if (_aqualink_data.ar_swg_status != SWG_STATUS_ON) {_aqualink_data.ar_swg_status = SWG_STATUS_ON;}
|
||||
if (_aqualink_data.swg_percent != 101) {changeSWGpercent(&_aqualink_data, 101);}
|
||||
//boost_msg_count = 0;
|
||||
|
@ -807,6 +818,9 @@ bool process_packet(unsigned char *packet, int length)
|
|||
#ifdef AQ_PDA
|
||||
if (isPDA_PANEL)
|
||||
{
|
||||
if (isPDA_IAQT) {
|
||||
return false;
|
||||
}
|
||||
return process_pda_packet(packet, length);
|
||||
}
|
||||
#endif
|
||||
|
@ -987,7 +1001,7 @@ void action_delayed_request()
|
|||
}
|
||||
else
|
||||
{
|
||||
LOG(AQUA_LOG,LOG_NOTICE, "SWG % is already %d, not changing\n", _aqualink_data.unactioned.value);
|
||||
LOG(AQUA_LOG,LOG_NOTICE, "SWG %% is already %d, not changing\n", _aqualink_data.unactioned.value);
|
||||
}
|
||||
}
|
||||
// Let's just tell everyone we set it, before we actually did. Makes homekit happy, and it will re-correct on error.
|
||||
|
@ -1168,7 +1182,7 @@ int startup(char *self, char *cfgFile)
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
} else if (isPDA_PANEL) {
|
||||
if (_aqconfig_.device_id >= 0x60 && _aqconfig_.device_id <= 0x63) {
|
||||
if ( (_aqconfig_.device_id >= 0x60 && _aqconfig_.device_id <= 0x63) || _aqconfig_.device_id == 0x33 ) {
|
||||
// We are good
|
||||
} else {
|
||||
LOG(AQUA_LOG,LOG_ERR, "Device ID 0x%02hhx does not match PDA panel, please check config!\n", _aqconfig_.device_id);
|
||||
|
@ -1313,6 +1327,9 @@ int startup(char *self, char *cfgFile)
|
|||
LOG(AQUA_LOG,LOG_NOTICE, "Read SWG direct = %s\n", bool2text(READ_RSDEV_SWG));
|
||||
LOG(AQUA_LOG,LOG_NOTICE, "Read ePump direct = %s\n", bool2text(READ_RSDEV_ePUMP));
|
||||
LOG(AQUA_LOG,LOG_NOTICE, "Read vsfPump direct = %s\n", bool2text(READ_RSDEV_vsfPUMP));
|
||||
LOG(AQUA_LOG,LOG_NOTICE, "Read JXi heater direct = %s\n", bool2text(READ_RSDEV_JXI));
|
||||
LOG(AQUA_LOG,LOG_NOTICE, "Read LX heater direct = %s\n", bool2text(READ_RSDEV_LX));
|
||||
LOG(AQUA_LOG,LOG_NOTICE, "Read Chem Feeder direct = %s\n", bool2text(READ_RSDEV_CHEM));
|
||||
|
||||
if (READ_RSDEV_SWG && _aqconfig_.swg_zero_ignore != DEFAULT_SWG_ZERO_IGNORE_COUNT)
|
||||
LOG(AQUA_LOG,LOG_NOTICE, "Ignore SWG 0 msg count = %d\n", _aqconfig_.swg_zero_ignore);
|
||||
|
@ -1507,6 +1524,7 @@ void main_loop()
|
|||
int blank_read_reconnect = MAX_ZERO_READ_BEFORE_RECONNECT_BLOCKING; // Will get reset if non blocking
|
||||
|
||||
sprintf(_aqualink_data.last_display_message, "%s", "Connecting to Control Panel");
|
||||
_aqualink_data.is_display_message_programming = false;
|
||||
//_aqualink_data.simulate_panel = false;
|
||||
_aqualink_data.active_thread.thread_id = 0;
|
||||
_aqualink_data.air_temp = TEMP_UNKNOWN;
|
||||
|
@ -1519,6 +1537,7 @@ void main_loop()
|
|||
_aqualink_data.swg_percent = TEMP_UNKNOWN;
|
||||
_aqualink_data.swg_ppm = TEMP_UNKNOWN;
|
||||
_aqualink_data.ar_swg_device_status = SWG_STATUS_UNKNOWN;
|
||||
_aqualink_data.heater_err_status = NUL; // 0x00 is no error
|
||||
_aqualink_data.swg_led_state = LED_S_UNKNOWN;
|
||||
_aqualink_data.swg_delayed_percent = TEMP_UNKNOWN;
|
||||
_aqualink_data.temp_units = UNKNOWN;
|
||||
|
@ -1530,6 +1549,8 @@ void main_loop()
|
|||
_aqualink_data.orp = TEMP_UNKNOWN;
|
||||
_aqualink_data.simulator_id = NUL;
|
||||
_aqualink_data.simulator_active = SIM_NONE;
|
||||
_aqualink_data.boost_duration = 0;
|
||||
_aqualink_data.boost = false;
|
||||
|
||||
pthread_mutex_init(&_aqualink_data.active_thread.thread_mutex, NULL);
|
||||
pthread_cond_init(&_aqualink_data.active_thread.thread_cond, NULL);
|
||||
|
@ -1547,6 +1568,11 @@ void main_loop()
|
|||
_aqualink_data.swg_ppm = 0;
|
||||
}
|
||||
|
||||
if (_aqconfig_.force_chem_feeder == true) {
|
||||
_aqualink_data.ph = 0;
|
||||
_aqualink_data.orp = 0;
|
||||
}
|
||||
|
||||
signal(SIGINT, intHandler);
|
||||
signal(SIGTERM, intHandler);
|
||||
signal(SIGQUIT, intHandler);
|
||||
|
@ -1881,7 +1907,15 @@ void main_loop()
|
|||
#ifdef AQ_PDA
|
||||
if (isPDA_PANEL) {
|
||||
// If we are in simulator mode, the sim has already send the ack
|
||||
if (_aqualink_data.simulator_active == SIM_NONE) {
|
||||
if (isPDA_IAQT) {
|
||||
//printf("****PDA IAQT Code\n");
|
||||
_aqualink_data.updated = process_iaqtouch_packet(packet_buffer, packet_length, &_aqualink_data);
|
||||
_aqualink_data.updated = true; // FORCE UPDATE SINCE THIS IS NOT WORKING YET
|
||||
caculate_ack_packet(rs_fd, packet_buffer, IAQTOUCH);
|
||||
if (checkAqualinkTime() == false) // Need to do this better.
|
||||
{aq_programmer(AQ_SET_TIME, NULL, &_aqualink_data);}
|
||||
}
|
||||
else /*if (_aqualink_data.simulator_active == SIM_NONE)*/ {
|
||||
caculate_ack_packet(rs_fd, packet_buffer, AQUAPDA);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
//#define COLOR_LIGHTS_C_
|
||||
#include "color_lights.h"
|
||||
|
||||
|
||||
/****** This list MUST be in order of clight_type enum *******/
|
||||
const char *_color_light_options[NUMBER_LIGHT_COLOR_TYPES][LIGHT_COLOR_OPTIONS] =
|
||||
{
|
||||
|
@ -50,9 +51,9 @@ const char *_color_light_options[NUMBER_LIGHT_COLOR_TYPES][LIGHT_COLOR_OPTIONS]
|
|||
},
|
||||
{ // Color Logic
|
||||
"Voodoo Lounge",
|
||||
"Blue Sea",
|
||||
"Royal Blue",
|
||||
"Afternoon Skies",
|
||||
"Deep Blue Sea",
|
||||
//"Royal Blue",
|
||||
"Afternoon Skies", // 'Afternoon Sky' on allbutton, Skies on iaqtouch
|
||||
//"Aqua Green",
|
||||
"Emerald",
|
||||
"Sangria",
|
||||
|
@ -91,8 +92,15 @@ const char *_color_light_options[NUMBER_LIGHT_COLOR_TYPES][LIGHT_COLOR_OPTIONS]
|
|||
};
|
||||
|
||||
|
||||
const char *light_mode_name(clight_type type, int index)
|
||||
const char *light_mode_name(clight_type type, int index, emulation_type protocol)
|
||||
{
|
||||
// Rename any modes depending on emulation type
|
||||
if (protocol == ALLBUTTON) {
|
||||
if (strcmp(_color_light_options[type][index],"Afternoon Skies") == 0) {
|
||||
return "Afternoon Sky";
|
||||
}
|
||||
}
|
||||
|
||||
return _color_light_options[type][index];
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#define COLOR_LIGHTS_H_
|
||||
|
||||
#include "aqualink.h"
|
||||
#include "aq_programmer.h"
|
||||
|
||||
#define LIGHT_COLOR_NAME 16
|
||||
#define LIGHT_COLOR_OPTIONS 17
|
||||
|
@ -19,7 +20,8 @@ typedef enum clight_type {
|
|||
LC_INTELLIB
|
||||
} clight_type;
|
||||
*/
|
||||
const char *light_mode_name(clight_type type, int index);
|
||||
//const char *light_mode_name(clight_type type, int index);
|
||||
const char *light_mode_name(clight_type type, int index, emulation_type protocol);
|
||||
int build_color_lights_js(struct aqualinkdata *aqdata, char* buffer, int size);
|
||||
|
||||
|
||||
|
|
32
config.c
32
config.c
|
@ -92,6 +92,7 @@ void init_parameters (struct aqconfig * parms)
|
|||
|
||||
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;
|
||||
|
@ -129,6 +130,7 @@ void init_parameters (struct aqconfig * parms)
|
|||
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;
|
||||
|
@ -442,6 +444,15 @@ bool setConfigValue(struct aqualinkdata *aqdata, char *param, char *value) {
|
|||
} else if (strncasecmp(param, "mqtt_dz_pub_topic", 17) == 0) {
|
||||
_aqconfig_.mqtt_dz_pub_topic = cleanalloc(value);
|
||||
rtn=true;
|
||||
} else if (strncasecmp(param, "mqtt_hassio_discover_topic", 26) == 0) {
|
||||
_aqconfig_.mqtt_hass_discover_topic = cleanalloc(value);
|
||||
/* It might also make sence to also set these to true. Since aqualinkd does not know the state at the time discover topics are published.
|
||||
_aqconfig_.force_swg;
|
||||
_aqconfig_.force_ps_setpoints;
|
||||
_aqconfig_.force_frzprotect_setpoints;
|
||||
force_chem_feeder
|
||||
*/
|
||||
rtn=true;
|
||||
} else if (strncasecmp(param, "mqtt_aq_topic", 13) == 0) {
|
||||
_aqconfig_.mqtt_aq_topic = cleanalloc(value);
|
||||
rtn=true;
|
||||
|
@ -552,11 +563,23 @@ 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) {
|
||||
} else if (strncasecmp (param, "read_RS485_JXi", 14) == 0) {
|
||||
if (text2bool(value))
|
||||
_aqconfig_.read_RS485_devmask |= READ_RS485_JAN_LXI;
|
||||
_aqconfig_.read_RS485_devmask |= READ_RS485_JAN_JXI;
|
||||
else
|
||||
_aqconfig_.read_RS485_devmask &= ~READ_RS485_JAN_LXI;
|
||||
_aqconfig_.read_RS485_devmask &= ~READ_RS485_JAN_JXI;
|
||||
rtn=true;
|
||||
} else if (strncasecmp (param, "read_RS485_LX", 14) == 0) {
|
||||
if (text2bool(value))
|
||||
_aqconfig_.read_RS485_devmask |= READ_RS485_JAN_LX;
|
||||
else
|
||||
_aqconfig_.read_RS485_devmask &= ~READ_RS485_JAN_LX;
|
||||
rtn=true;
|
||||
} else if (strncasecmp (param, "read_RS485_Chem", 14) == 0) {
|
||||
if (text2bool(value))
|
||||
_aqconfig_.read_RS485_devmask |= READ_RS485_JAN_CHEM;
|
||||
else
|
||||
_aqconfig_.read_RS485_devmask &= ~READ_RS485_JAN_CHEM;
|
||||
rtn=true;
|
||||
} else if (strncasecmp (param, "use_panel_aux_labels", 20) == 0) {
|
||||
_aqconfig_.use_panel_aux_labels = text2bool(value);
|
||||
|
@ -570,6 +593,9 @@ bool setConfigValue(struct aqualinkdata *aqdata, char *param, char *value) {
|
|||
} else if (strncasecmp (param, "force_frzprotect_setpoints", 26) == 0) {
|
||||
_aqconfig_.force_frzprotect_setpoints = text2bool(value);
|
||||
rtn=true;
|
||||
} else if (strncasecmp (param, "force_chem_feeder", 17) == 0) {
|
||||
_aqconfig_.force_chem_feeder = text2bool(value);
|
||||
rtn=true;
|
||||
} else if (strncasecmp (param, "debug_RSProtocol_bytes", 22) == 0) {
|
||||
_aqconfig_.log_raw_bytes = text2bool(value);
|
||||
rtn=true;
|
||||
|
|
13
config.h
13
config.h
|
@ -15,6 +15,7 @@
|
|||
#define DEFAULT_DEVICE_ID "0x0a"
|
||||
#define DEFAULT_MQTT_DZ_IN NULL
|
||||
#define DEFAULT_MQTT_DZ_OUT NULL
|
||||
#define DEFAULT_HASS_DISCOVER NULL
|
||||
#define DEFAULT_MQTT_AQ_TP NULL
|
||||
#define DEFAULT_MQTT_SERVER NULL
|
||||
#define DEFAULT_MQTT_USER NULL
|
||||
|
@ -27,8 +28,9 @@
|
|||
#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
|
||||
#define READ_RS485_JAN_JXI (1 << 3) // Jandy JX & LXi heater
|
||||
#define READ_RS485_JAN_LX (1 << 4) // Jandy LX heater
|
||||
#define READ_RS485_JAN_CHEM (1 << 5) // Jandy Chemical Feeder
|
||||
|
||||
struct aqconfig
|
||||
{
|
||||
|
@ -51,6 +53,7 @@ struct aqconfig
|
|||
char *mqtt_dz_sub_topic;
|
||||
char *mqtt_dz_pub_topic;
|
||||
char *mqtt_aq_topic;
|
||||
char *mqtt_hass_discover_topic;
|
||||
char *mqtt_server;
|
||||
char *mqtt_user;
|
||||
char *mqtt_passwd;
|
||||
|
@ -79,6 +82,7 @@ struct aqconfig
|
|||
bool force_swg;
|
||||
bool force_ps_setpoints;
|
||||
bool force_frzprotect_setpoints;
|
||||
bool force_chem_feeder;
|
||||
int swg_zero_ignore;
|
||||
bool display_warnings_web;
|
||||
bool log_protocol_packets; // Read & Write as packets
|
||||
|
@ -112,8 +116,11 @@ 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 READ_RSDEV_JXI ((_aqconfig_.read_RS485_devmask & READ_RS485_JAN_JXI) == READ_RS485_JAN_JXI)
|
||||
#define READ_RSDEV_LX ((_aqconfig_.read_RS485_devmask & READ_RS485_JAN_LX) == READ_RS485_JAN_LX)
|
||||
#define READ_RSDEV_CHEM ((_aqconfig_.read_RS485_devmask & READ_RS485_JAN_CHEM) == READ_RS485_JAN_CHEM)
|
||||
|
||||
#define isPDA_IAQT (_aqconfig_.device_id == 0x33)
|
||||
//#define isPDA ((_aqconfig_.paneltype_mask & RSP_PDA) == RSP_PDA)
|
||||
|
||||
|
||||
|
|
393
devices_jandy.c
393
devices_jandy.c
|
@ -39,6 +39,7 @@ bool processJandyPacket(unsigned char *packet_buffer, int packet_length, struct
|
|||
static rsDeviceType interestedInNextAck = DRS_NONE;
|
||||
static unsigned char previous_packet_to = NUL; // bad name, it's not previous, it's previous that we were interested in.
|
||||
int rtn = false;
|
||||
|
||||
// We received the ack from a Jandy device we are interested in
|
||||
if (packet_buffer[PKT_DEST] == DEV_MASTER && interestedInNextAck != DRS_NONE)
|
||||
{
|
||||
|
@ -50,9 +51,17 @@ bool processJandyPacket(unsigned char *packet_buffer, int packet_length, struct
|
|||
{
|
||||
rtn = processPacketFromJandyPump(packet_buffer, packet_length, aqdata, previous_packet_to);
|
||||
}
|
||||
else if (interestedInNextAck == DRS_LXI)
|
||||
else if (interestedInNextAck == DRS_JXI)
|
||||
{
|
||||
rtn = processPacketFromJandyHeater(packet_buffer, packet_length, aqdata, previous_packet_to);
|
||||
rtn = processPacketFromJandyJXiHeater(packet_buffer, packet_length, aqdata, previous_packet_to);
|
||||
}
|
||||
else if (interestedInNextAck == DRS_LX)
|
||||
{
|
||||
rtn = processPacketFromJandyLXHeater(packet_buffer, packet_length, aqdata, previous_packet_to);
|
||||
}
|
||||
else if (interestedInNextAck == DRS_CHEM)
|
||||
{
|
||||
rtn = processPacketFromJandyChemFeeder(packet_buffer, packet_length, aqdata, previous_packet_to);
|
||||
}
|
||||
interestedInNextAck = DRS_NONE;
|
||||
previous_packet_to = NUL;
|
||||
|
@ -71,7 +80,7 @@ bool processJandyPacket(unsigned char *packet_buffer, int packet_length, struct
|
|||
interestedInNextAck = DRS_NONE;
|
||||
previous_packet_to = NUL;
|
||||
}
|
||||
else if (READ_RSDEV_SWG && packet_buffer[PKT_DEST] == SWG_DEV_ID)
|
||||
else if (READ_RSDEV_SWG && packet_buffer[PKT_DEST] >= JANDY_DEC_SWG_MIN && packet_buffer[PKT_DEST] <= JANDY_DEC_SWG_MAX)
|
||||
{
|
||||
interestedInNextAck = DRS_SWG;
|
||||
rtn = processPacketToSWG(packet_buffer, packet_length, aqdata, _aqconfig_.swg_zero_ignore);
|
||||
|
@ -83,11 +92,22 @@ 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)))
|
||||
else if (READ_RSDEV_JXI && packet_buffer[PKT_DEST] >= JANDY_DEC_JXI_MIN && packet_buffer[PKT_DEST] <= JANDY_DEC_JXI_MAX)
|
||||
{
|
||||
interestedInNextAck = DRS_LXI;
|
||||
rtn = processPacketToJandyHeater(packet_buffer, packet_length, aqdata);
|
||||
interestedInNextAck = DRS_JXI;
|
||||
rtn = processPacketToJandyJXiHeater(packet_buffer, packet_length, aqdata);
|
||||
previous_packet_to = packet_buffer[PKT_DEST];
|
||||
}
|
||||
else if (READ_RSDEV_LX && packet_buffer[PKT_DEST] >= JANDY_DEC_LX_MIN && packet_buffer[PKT_DEST] <= JANDY_DEC_LX_MAX)
|
||||
{
|
||||
interestedInNextAck = DRS_LX;
|
||||
rtn = processPacketToJandyLXHeater(packet_buffer, packet_length, aqdata);
|
||||
previous_packet_to = packet_buffer[PKT_DEST];
|
||||
}
|
||||
else if (READ_RSDEV_CHEM && packet_buffer[PKT_DEST] >= JANDY_DEC_CHEM_MIN && packet_buffer[PKT_DEST] <= JANDY_DEC_CHEM_MAX)
|
||||
{
|
||||
interestedInNextAck = DRS_CHEM;
|
||||
rtn = processPacketToJandyChemFeeder(packet_buffer, packet_length, aqdata);
|
||||
previous_packet_to = packet_buffer[PKT_DEST];
|
||||
}
|
||||
else
|
||||
|
@ -109,13 +129,13 @@ bool processPacketToSWG(unsigned char *packet, int packet_length, struct aqualin
|
|||
static int swg_zero_cnt = 0;
|
||||
bool changedAnything = false;
|
||||
|
||||
if (getLogLevel(DJAN_LOG) == LOG_DEBUG) {
|
||||
// Only log if we are jandy debug move and not serial (otherwise it'll print twice)
|
||||
if (getLogLevel(DJAN_LOG) == LOG_DEBUG && getLogLevel(RSSD_LOG) < LOG_DEBUG ) {
|
||||
char buff[1024];
|
||||
beautifyPacket(buff, packet, packet_length, false);
|
||||
LOG(DJAN_LOG,LOG_DEBUG, "%s", buff);
|
||||
beautifyPacket(buff, packet, packet_length, true);
|
||||
LOG(DJAN_LOG,LOG_DEBUG, "To SWG: %s", buff);
|
||||
}
|
||||
|
||||
|
||||
// Only read message from controller to SWG to set SWG Percent if we are not programming, as we might be changing this
|
||||
if (packet[3] == CMD_PERCENT && aqdata->active_thread.thread_id == 0 && packet[4] != 0xFF) {
|
||||
// In service or timeout mode SWG set % message is very strange. AR %% | HEX: 0x10|0x02|0x50|0x11|0xff|0x72|0x10|0x03|
|
||||
|
@ -161,10 +181,11 @@ bool processPacketFromSWG(unsigned char *packet, int packet_length, struct aqual
|
|||
bool changedAnything = false;
|
||||
_swg_noreply_cnt = 0;
|
||||
|
||||
if (getLogLevel(DJAN_LOG) == LOG_DEBUG) {
|
||||
// Only log if we are jandy debug move and not serial (otherwise it'll print twice)
|
||||
if (getLogLevel(DJAN_LOG) == LOG_DEBUG && getLogLevel(RSSD_LOG) < LOG_DEBUG ) {
|
||||
char buff[1024];
|
||||
beautifyPacket(buff, packet, packet_length, true);
|
||||
LOG(DJAN_LOG,LOG_DEBUG, "%s", buff);
|
||||
LOG(DJAN_LOG,LOG_DEBUG, "From SWG: %s", buff);
|
||||
}
|
||||
|
||||
if (packet[PKT_CMD] == CMD_PPM) {
|
||||
|
@ -213,6 +234,7 @@ bool isSWGDeviceErrorState(unsigned char status)
|
|||
status == SWG_STATUS_LOW_TEMP ||
|
||||
status == SWG_STATUS_HIGH_CURRENT ||
|
||||
status == SWG_STATUS_NO_FLOW)
|
||||
// Maybe add CLEAN_CELL and GENFAULT here
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
|
@ -220,6 +242,17 @@ bool isSWGDeviceErrorState(unsigned char status)
|
|||
|
||||
void setSWGdeviceStatus(struct aqualinkdata *aqdata, emulation_type requester, unsigned char status) {
|
||||
static unsigned char last_status = SWG_STATUS_UNKNOWN;
|
||||
/* This is only needed for DEBUG
|
||||
static bool haveSeenRSSWG = false;
|
||||
|
||||
if (requester == JANDY_DEVICE) {
|
||||
haveSeenRSSWG = true;
|
||||
}
|
||||
*/
|
||||
// If we are reading state directly from RS458, then ignore everything else.
|
||||
if ( READ_RSDEV_SWG && requester != JANDY_DEVICE ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((aqdata->ar_swg_device_status == status) || (last_status == status)) {
|
||||
//LOG(DJAN_LOG, LOG_DEBUG, "Set SWG device state to '0x%02hhx', request from %d\n", aqdata->ar_swg_device_status, requester);
|
||||
|
@ -227,10 +260,15 @@ void setSWGdeviceStatus(struct aqualinkdata *aqdata, emulation_type requester, u
|
|||
}
|
||||
last_status = status;
|
||||
|
||||
// If we get (ALLBUTTON, SWG_STATUS_CHECK_PCB), it sends this for many status, like clean cell.
|
||||
// If we get (ALLBUTTON, SWG_STATUS_CHECK_PCB // GENFAULT), it sends this for many status, like clean cell.
|
||||
// So if we are in one of those states, don't use it.
|
||||
|
||||
if (requester == ALLBUTTON && status == SWG_STATUS_CHECK_PCB ) {
|
||||
// Need to rethink this. Use general fault only if we are not reading SWG status direct from device
|
||||
//if ( READ_RSDEV_SWG && requester == ALLBUTTON && status == SWG_STATUS_GENFAULT ) {
|
||||
|
||||
// SWG_STATUS_GENFAULT is shown on panels for many reasons, if we are NOT reading the status directly from the SWG
|
||||
// then use it, otherwise disguard it as we will have a better status
|
||||
if (requester == ALLBUTTON && status == SWG_STATUS_GENFAULT ) {
|
||||
if (aqdata->ar_swg_device_status > SWG_STATUS_ON &&
|
||||
aqdata->ar_swg_device_status < SWG_STATUS_TURNING_OFF) {
|
||||
LOG(DJAN_LOG, LOG_DEBUG, "Ignoring set SWG device state to '0x%02hhx', request from %d\n", aqdata->ar_swg_device_status, requester);
|
||||
|
@ -250,6 +288,7 @@ void setSWGdeviceStatus(struct aqualinkdata *aqdata, emulation_type requester, u
|
|||
case SWG_STATUS_LOW_VOLTS:
|
||||
case SWG_STATUS_LOW_TEMP:
|
||||
case SWG_STATUS_CHECK_PCB:
|
||||
case SWG_STATUS_GENFAULT:
|
||||
aqdata->ar_swg_device_status = status;
|
||||
aqdata->swg_led_state = isSWGDeviceErrorState(status)?ENABLE:ON;
|
||||
break;
|
||||
|
@ -393,6 +432,9 @@ aqledstate get_swg_led_state(struct aqualinkdata *aqdata)
|
|||
case SWG_STATUS_OFF: // THIS IS OUR OFF STATUS, NOT AQUAPURE
|
||||
return OFF;
|
||||
break;
|
||||
case SWG_STATUS_GENFAULT:
|
||||
return ENABLE;
|
||||
break;
|
||||
default:
|
||||
return (aqdata->swg_percent > 0?ON:ENABLE);
|
||||
break;
|
||||
|
@ -466,6 +508,11 @@ void get_swg_status_mqtt(struct aqualinkdata *aqdata, char *message, int *status
|
|||
sprintf(message, "AQUAPURE OFF");
|
||||
*dzalert = 0;
|
||||
break;
|
||||
case SWG_STATUS_GENFAULT:
|
||||
*status = (aqdata->swg_percent > 0?SWG_ON:SWG_OFF);
|
||||
sprintf(message, "AQUAPURE GENERAL FAULT");
|
||||
*dzalert = 2;
|
||||
break;
|
||||
default:
|
||||
*status = (aqdata->swg_percent > 0?SWG_ON:SWG_OFF);
|
||||
sprintf(message, "AQUAPURE UNKNOWN STATUS");
|
||||
|
@ -498,6 +545,12 @@ bool processPacketToJandyPump(unsigned char *packet_buffer, int packet_length, s
|
|||
|
||||
Type 0x1F and cmd 0x45 is RPM = 39 * (256) + 96 / 4 = 2520 or Byte 8 * 265 + Byte 7 / 4
|
||||
*/
|
||||
// Only log if we are jandy debug move and not serial (otherwise it'll print twice)
|
||||
if (getLogLevel(DJAN_LOG) == LOG_DEBUG && getLogLevel(RSSD_LOG) < LOG_DEBUG ) {
|
||||
char msg[1000];
|
||||
beautifyPacket(msg, packet_buffer, packet_length, true);
|
||||
LOG(DJAN_LOG, LOG_DEBUG, "To ePump: %s\n", msg);
|
||||
}
|
||||
|
||||
// If type 0x45 and 0x44 set to interested in next command.
|
||||
if (packet_buffer[3] == CMD_EPUMP_RPM) {
|
||||
|
@ -506,11 +559,8 @@ bool processPacketToJandyPump(unsigned char *packet_buffer, int packet_length, s
|
|||
} 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);
|
||||
|
||||
if (getLogLevel(DJAN_LOG) == LOG_DEBUG) {
|
||||
//find pump for message
|
||||
for (int i=0; i < aqdata->num_pumps; i++) {
|
||||
if (aqdata->pumps[i].pumpID == packet_buffer[PKT_DEST]) {
|
||||
|
@ -527,6 +577,14 @@ bool processPacketFromJandyPump(unsigned char *packet_buffer, int packet_length,
|
|||
{
|
||||
bool found=false;
|
||||
|
||||
// Only log if we are jandy debug move and not serial (otherwise it'll print twice)
|
||||
if (getLogLevel(DJAN_LOG) == LOG_DEBUG && getLogLevel(RSSD_LOG) < LOG_DEBUG ) {
|
||||
char msg[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);
|
||||
}
|
||||
|
||||
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 ) {
|
||||
|
@ -552,12 +610,7 @@ bool processPacketFromJandyPump(unsigned char *packet_buffer, int packet_length,
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -572,13 +625,173 @@ void processMissingAckPacketFromJandyPump(unsigned char destination, struct aqua
|
|||
|
||||
|
||||
|
||||
bool processPacketToJandyHeater(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata)
|
||||
bool processPacketToJandyJXiHeater(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata)
|
||||
{
|
||||
|
||||
if (getLogLevel(DJAN_LOG) == LOG_DEBUG && getLogLevel(RSSD_LOG) < LOG_DEBUG ) {
|
||||
char msg[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 JXi: %s\n", msg);
|
||||
}
|
||||
|
||||
if (packet_buffer[3] != CMD_JXI_PING) {
|
||||
// Not sure what this message is, so ignore
|
||||
// Maybe print a messsage.
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
Below counfing first as bit 0
|
||||
4th bit 0x00 no pump on (nothing)
|
||||
0x10 seems to be JXi came online. nothing more
|
||||
0x11 (pool mode)
|
||||
0x12 (spa mode)
|
||||
0x19 heat pool
|
||||
0x1a heat spa
|
||||
5th bit 0x55 = 85 deg. (current pool setpoint)
|
||||
6th bit 0x66 = 102 deg. (current spa setpoint)
|
||||
7th bit 0x4f = current water temp 79 (0xFF is off / 255)
|
||||
*/
|
||||
|
||||
if (packet_buffer[5] != aqdata->pool_htr_set_point) {
|
||||
LOG(DJAN_LOG, LOG_DEBUG, "JXi pool setpoint %d, Pool heater sp %d (changing to LXi)\n", packet_buffer[5], aqdata->pool_htr_set_point);
|
||||
aqdata->pool_htr_set_point = packet_buffer[5];
|
||||
}
|
||||
|
||||
if (packet_buffer[6] != aqdata->spa_htr_set_point) {
|
||||
LOG(DJAN_LOG, LOG_DEBUG, "JXi spa setpoint %d, Spa heater sp %d (changing to LXi)\n", packet_buffer[6], aqdata->spa_htr_set_point);
|
||||
aqdata->spa_htr_set_point = packet_buffer[6];
|
||||
}
|
||||
|
||||
if (packet_buffer[7] != 0xff && packet_buffer[4] != 0x00) {
|
||||
if (packet_buffer[4] == 0x11 || packet_buffer[4] == 0x19) {
|
||||
if (aqdata->pool_temp != packet_buffer[7]) {
|
||||
LOG(DJAN_LOG, LOG_DEBUG, "JXi pool water temp %d, pool water temp %d (changing to LXi)\n", packet_buffer[7], aqdata->pool_temp);
|
||||
aqdata->pool_temp = packet_buffer[7];
|
||||
}
|
||||
} else if (packet_buffer[4] == 0x12 || packet_buffer[4] == 0x1a) {
|
||||
if (aqdata->spa_temp != packet_buffer[7]) {
|
||||
LOG(DJAN_LOG, LOG_DEBUG, "JXi spa water temp %d, spa water temp %d (changing to LXi)\n", packet_buffer[7], aqdata->spa_temp);
|
||||
aqdata->spa_temp = packet_buffer[7];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (packet_buffer[4]) {
|
||||
case 0x11: // Pool heat off or enabled
|
||||
break;
|
||||
case 0x12: // Pool Heat enabled or heating
|
||||
break;
|
||||
case 0x19: // Spa heat off or enabled
|
||||
break;
|
||||
case 0x1a: // Spa Hear Heat enabled or heating
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
char msg[1000];
|
||||
int length = 0;
|
||||
|
||||
beautifyPacket(msg, packet_buffer, packet_length, true);
|
||||
LOG(DJAN_LOG, LOG_INFO, "To Heater: %s\n", msg);
|
||||
LOG(DJAN_LOG, LOG_INFO, "To JXi 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;
|
||||
*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void getJandyHeaterError(struct aqualinkdata *aqdata, char *message)
|
||||
{
|
||||
if (aqdata->heater_err_status == NUL) {
|
||||
return;
|
||||
}
|
||||
|
||||
int size = sprintf(message, "JXi Heater ");
|
||||
getJandyHeaterErrorMQTT(aqdata, message+size);
|
||||
}
|
||||
|
||||
void getJandyHeaterErrorMQTT(struct aqualinkdata *aqdata, char *message)
|
||||
{
|
||||
switch (aqdata->heater_err_status) {
|
||||
case 0x00:
|
||||
//sprintf(message, "");
|
||||
break;
|
||||
case 0x10:
|
||||
sprintf(message, "FAULT HIGH LIMIT");
|
||||
break;
|
||||
case 0x02:
|
||||
sprintf(message, "FAULT H20 SENSOR");
|
||||
break;
|
||||
case 0x08:
|
||||
sprintf(message, "FAULT AUX MONITOR");
|
||||
break;
|
||||
default:
|
||||
//
|
||||
/* Error we haven't decoded yet
|
||||
?x?? check flow
|
||||
0x10 Fault high limit
|
||||
?x?? Fault High Flu temp
|
||||
?x?? Fault Check Igntion Control
|
||||
0x02 Fault Short H20 sensor (or Fault open water sensor)
|
||||
?x?? Pump fault
|
||||
0x08 AUX Monitor
|
||||
*/
|
||||
sprintf(message, "FAULT 0x%02hhx",aqdata->heater_err_status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool processPacketFromJandyJXiHeater(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata, const unsigned char previous_packet_to)
|
||||
{
|
||||
if (getLogLevel(DJAN_LOG) == LOG_DEBUG && getLogLevel(RSSD_LOG) < LOG_DEBUG ) {
|
||||
char msg[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 JXi: %s\n", msg);
|
||||
}
|
||||
|
||||
if (packet_buffer[3] != CMD_JXI_STATUS) {
|
||||
// Not sure what this message is, so ignore
|
||||
// Maybe print a messsage.
|
||||
return false;
|
||||
}
|
||||
|
||||
// No error is 0x00, so blindly set it.
|
||||
aqdata->heater_err_status = packet_buffer[6];
|
||||
// Check if error first
|
||||
if (packet_buffer[6] != 0x00) {
|
||||
|
||||
} else if (packet_buffer[4] == 0x00) {
|
||||
// Not heating.
|
||||
// Heater off or enabeled
|
||||
} else if (packet_buffer[4] == 0x08) {
|
||||
// Heating
|
||||
// Heater on of enabled
|
||||
}
|
||||
/*
|
||||
char msg[1000];
|
||||
int length = 0;
|
||||
|
||||
beautifyPacket(msg, packet_buffer, packet_length, true);
|
||||
LOG(DJAN_LOG, LOG_INFO, "From JXi Heater: %s\n", msg);
|
||||
|
||||
length += sprintf(msg+length, "Last panel info ");
|
||||
|
||||
|
@ -598,15 +811,47 @@ bool processPacketToJandyHeater(unsigned char *packet_buffer, int packet_length,
|
|||
LOG(DJAN_LOG, LOG_INFO, "%s\n", msg);
|
||||
|
||||
return false;
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
bool processPacketFromJandyHeater(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata, const unsigned char previous_packet_to)
|
||||
bool processPacketToJandyLXHeater(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 LX: %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 processPacketFromJandyLXHeater(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);
|
||||
LOG(DJAN_LOG, LOG_INFO, "From LX: %s\n", msg);
|
||||
|
||||
length += sprintf(msg+length, "Last panel info ");
|
||||
|
||||
|
@ -629,3 +874,91 @@ bool processPacketFromJandyHeater(unsigned char *packet_buffer, int packet_lengt
|
|||
}
|
||||
|
||||
|
||||
bool processPacketToJandyChemFeeder(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 Chem: %s\n", msg);
|
||||
|
||||
length += sprintf(msg+length, "Last panel info ");
|
||||
|
||||
length += sprintf(msg+length, ", pH=%f, ORP=%d",aqdata->ph, aqdata->orp);
|
||||
|
||||
LOG(DJAN_LOG, LOG_INFO, "%s\n", msg);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool processPacketFromJandyChemFeeder(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 Chem: %s\n", msg);
|
||||
|
||||
length += sprintf(msg+length, "Last panel info ");
|
||||
|
||||
length += sprintf(msg+length, ", pH=%f, ORP=%d",aqdata->ph, aqdata->orp);
|
||||
|
||||
LOG(DJAN_LOG, LOG_INFO, "%s\n", msg);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
// JXi Heater
|
||||
|
||||
// Normal ping and return
|
||||
5th bit 0x00 no pump on (nothing)
|
||||
0x10 seems to be JXi came online. nothing more
|
||||
0x11 (pool mode)
|
||||
0x12 (spa mode)
|
||||
0x19 heat pool
|
||||
0x1a heat spa
|
||||
6th bit 0x55 = 85 deg. (current pool setpoint)
|
||||
7th bit 0x66 = 102 deg. (current spa setpoint)
|
||||
8th bit 0x4f = current water temp 79 (0xFF is off / 255)
|
||||
|
||||
Jandy To 0x68 of type Unknown '0x0c' | HEX: 0x10|0x02|0x68|0x0c|0x11|0x55|0x66|0x4f|0xa1|0x10|0x03|
|
||||
Jandy From 0x68 of type Unknown '0x0d' | HEX: 0x10|0x02|0x00|0x0d|0x00|0x00|0x00|0x1f|0x10|0x03|
|
||||
|
||||
Request to turn on 85
|
||||
5th bit 0x19 looks like turn on
|
||||
6th bit 0x55 = 85 deg.
|
||||
7th bit 0x4f = current temp 79
|
||||
Jandy To 0x68 of type Unknown '0x0c' | HEX: 0x10|0x02|0x68|0x0c|0x19|0x55|0x66|0x4f|0xa9|0x10|0x03|
|
||||
Jandy From 0x68 of type Unknown '0x0d' | HEX: 0x10|0x02|0x00|0x0d|0x08|0x00|0x00|0x27|0x10|0x03|
|
||||
|
||||
Request to turn on 90
|
||||
5th bit 0x19 looks like turn on
|
||||
6th bit 0x5a = 90 deg.
|
||||
Jandy To 0x68 of type Unknown '0x0c' | HEX: 0x10|0x02|0x68|0x0c|0x19|0x5a|0x66|0x4f|0xae|0x10|0x03|
|
||||
Jandy From 0x68 of type Unknown '0x0d' | HEX: 0x10|0x02|0x00|0x0d|0x08|0x00|0x00|0x27|0x10|0x03|
|
||||
|
||||
Request to turn off (standard ping) // return had hi limit error in it
|
||||
Jandy To 0x68 of type Unknown '0x0c' | HEX: 0x10|0x02|0x68|0x0c|0x11|0x55|0x66|0x4f|0xa1|0x10|0x03|
|
||||
Jandy From 0x68 of type Unknown '0x0d' | HEX: 0x10|0x02|0x00|0x0d|0x00|0x00|0x10|0x2f|0x10|0x03|
|
||||
|
||||
Returns
|
||||
|
||||
5th bit is type 0x00 nothing (or enabeled) - 0x08 looks like heat
|
||||
Hi limit error return
|
||||
7th bit 0x10 looks like the error
|
||||
Jandy To 0x68 of type Unknown '0x0c' | HEX: 0x10|0x02|0x68|0x0c|0x19|0x5a|0x66|0x4f|0xae|0x10|0x03|
|
||||
Jandy From 0x68 of type Unknown '0x0d' | HEX: 0x10|0x02|0x00|0x0d|0x08|0x00|0x10|0x37|0x10|0x03|
|
||||
|
||||
Errors are ->
|
||||
check flow
|
||||
Fault high limit -> 0x10
|
||||
Fault High Flu temp
|
||||
Fault Check Igntion Control
|
||||
Fault Short H20 sensor (or Fault open water sensor) -> 0x02
|
||||
Pump fault
|
||||
AUX Monitor -> 0x08
|
||||
*/
|
|
@ -14,8 +14,13 @@ bool processPacketFromJandyPump(unsigned char *packet_buffer, int packet_length,
|
|||
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);
|
||||
bool processPacketFromJandyJXiHeater(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata, const unsigned char previous_packet_to );
|
||||
bool processPacketToJandyJXiHeater(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata);
|
||||
bool processPacketFromJandyLXHeater(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata, const unsigned char previous_packet_to );
|
||||
bool processPacketToJandyLXHeater(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata);
|
||||
|
||||
bool processPacketFromJandyChemFeeder(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata, const unsigned char previous_packet_to );
|
||||
bool processPacketToJandyChemFeeder(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);
|
||||
|
@ -27,4 +32,7 @@ void setSWGenabled(struct aqualinkdata *aqdata);
|
|||
bool setSWGboost(struct aqualinkdata *aqdata, bool on);
|
||||
void setSWGdeviceStatus(struct aqualinkdata *aqdata, emulation_type requester, unsigned char status);
|
||||
|
||||
void getJandyHeaterError(struct aqualinkdata *aqdata, char *message);
|
||||
void getJandyHeaterErrorMQTT(struct aqualinkdata *aqdata, char *message);
|
||||
|
||||
#endif // AQUAPURE_H_
|
|
@ -24,11 +24,20 @@
|
|||
#include "aq_serial.h"
|
||||
#include "devices_pentair.h"
|
||||
#include "utils.h"
|
||||
#include "packetLogger.h"
|
||||
|
||||
bool processPentairPacket(unsigned char *packet, int packet_length, struct aqualinkdata *aqdata)
|
||||
{
|
||||
bool changedAnything = false;
|
||||
int i;
|
||||
|
||||
// Only log if we are pentair debug move and not serial (otherwise it'll print twice)
|
||||
if (getLogLevel(DPEN_LOG) == LOG_DEBUG && getLogLevel(RSSD_LOG) < LOG_DEBUG ) {
|
||||
char buff[1024];
|
||||
beautifyPacket(buff, packet, packet_length, true);
|
||||
LOG(DPEN_LOG,LOG_DEBUG, "%s", buff);
|
||||
}
|
||||
|
||||
//ID's 96 to 111 = Pentair (or 0x60 to 0x6F)
|
||||
|
||||
// Need to find a better way to support pump index
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 143 KiB |
|
@ -182,33 +182,12 @@ function check_git_installed {
|
|||
function check_curl_installed {
|
||||
command -v curl >/dev/null 2>&1 || { echoerr "curl is not installed. Installing"; exit 1; }
|
||||
}
|
||||
|
||||
#
|
||||
# Main
|
||||
#
|
||||
|
||||
# Check we are on a arm 32 bit machine.
|
||||
|
||||
# below is compatable on all linux.
|
||||
#if file -Lb /usr/bin/ld | grep -s -i armhf > /dev/null; then
|
||||
# dpkg is good for debain distros.
|
||||
if dpkg --print-architecture | grep -s -i armhf > /dev/null; then
|
||||
echo "Architecture is armhf";
|
||||
else
|
||||
echo "Architecture is '`dpkg --print-architecture`'"
|
||||
echo "This does not look like linux running on a arm 32 platform"
|
||||
echo "Please use another install method"
|
||||
echo "https://github.com/sfeakes/AqualinkD/wiki#Install"
|
||||
exit
|
||||
fi
|
||||
|
||||
# Check something was passed
|
||||
if [[ $# -eq 0 ]]; then
|
||||
print_usage
|
||||
exit
|
||||
fi
|
||||
|
||||
|
||||
# Pass command line
|
||||
if [ "$1" == "release" ]; then
|
||||
check_curl_installed
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
#!/bin/bash
|
||||
|
||||
|
||||
# Script to start AqualinkD inside container.
|
||||
|
||||
CONFDIR=/aquadconf
|
||||
AQUA_CONF=$CONFDIR/aqualinkd.conf
|
||||
|
||||
# Check we have a config directory
|
||||
if [ -d "$CONFDIR" ]; then
|
||||
|
||||
# Check we have config file, if not copy default
|
||||
if [ ! -f "$AQUA_CONF" ]; then
|
||||
echo "Warning no aqualinkd.conf in $CONFDIR", using default
|
||||
cp /etc/aqualinkd.conf $CONFDIR
|
||||
fi
|
||||
|
||||
# Replace local filesystem config with mounted config
|
||||
ln -sf "$AQUA_CONF" /etc/aqualinkd.conf
|
||||
|
||||
# If we have a web config, replace the local filesystem with mounted
|
||||
if [ -f "$CONFDIR/config.js" ]; then
|
||||
ln -sf "$CONFDIR/config.js" /var/www/aqualinkd/config.js
|
||||
fi
|
||||
|
||||
# If don't have a cron file, create one
|
||||
if [ ! -f "$CONFDIR/aqualinkd.schedule" ]; then
|
||||
echo "#***** AUTO GENERATED DO NOT EDIT *****" > "$CONFDIR/aqualinkd.schedule"
|
||||
fi
|
||||
|
||||
# link mounted cron file to local filesystem.
|
||||
ln -sf "$CONFDIR/aqualinkd.schedule" /etc/cron.d/aqualinkd
|
||||
chmod 644 "$CONFDIR/aqualinkd.schedule"
|
||||
else
|
||||
# No conig dir, show warning
|
||||
echo "WARNING no config directory, AqualinkD starting with default config, no changes will be saved"
|
||||
AQUA_CONF="/etc/aqualinkd.conf"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# Start cron
|
||||
service cron start
|
||||
|
||||
# Start AqualinkD not in daemon mode
|
||||
/usr/local/bin/aqualinkd -d -c $AQUA_CONF
|
||||
|
|
@ -0,0 +1,444 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mongoose.h"
|
||||
|
||||
#include "aqualink.h"
|
||||
#include "net_services.h"
|
||||
#include "json_messages.h"
|
||||
#include "aq_mqtt.h"
|
||||
#include "rs_msg_utils.h"
|
||||
#include "config.h"
|
||||
#include "version.h"
|
||||
|
||||
|
||||
// NSF Need to find a better way, this is not thread safe, so don;t want to expost it from net_services.h.
|
||||
void send_mqtt(struct mg_connection *nc, const char *toppic, const char *message);
|
||||
|
||||
#define HASS_DEVICE "\"identifiers\": " \
|
||||
"[\"" AQUALINKD_SHORT_NAME "\"]," \
|
||||
" \"sw_version\": \"" AQUALINKD_VERSION "\"," \
|
||||
" \"model\": \"" AQUALINKD_NAME "\"," \
|
||||
" \"name\": \"AqualinkD\"," \
|
||||
" \"manufacturer\": \"" AQUALINKD_SHORT_NAME "\"," \
|
||||
" \"suggested_area\": \"pool\""
|
||||
|
||||
#define HASS_AVAILABILITY "\"payload_available\" : \"1\"," \
|
||||
"\"payload_not_available\" : \"0\"," \
|
||||
"\"topic\": \"%s/" MQTT_LWM_TOPIC "\""
|
||||
|
||||
|
||||
const char *HASSIO_CLIMATE_DISCOVER = "{"
|
||||
"\"device\": {" HASS_DEVICE "},"
|
||||
"\"availability\": {" HASS_AVAILABILITY "},"
|
||||
"\"type\": \"climate\","
|
||||
"\"unique_id\": \"aqualinkd_%s\","
|
||||
"\"name\": \"%s\","
|
||||
"\"modes\": [\"off\", \"heat\"],"
|
||||
"\"send_if_off\": true,"
|
||||
"\"initial\": 36,"
|
||||
"\"power_command_topic\": \"%s/%s/set\","
|
||||
"\"payload_on\": \"1\","
|
||||
"\"payload_off\": \"0\","
|
||||
"\"current_temperature_topic\": \"%s/%s\","
|
||||
"\"min_temp\": 36,"
|
||||
"\"max_temp\": 104,"
|
||||
"\"mode_command_topic\": \"%s/%s/set\","
|
||||
"\"mode_state_topic\": \"%s/%s/enabled\","
|
||||
"\"mode_state_template\": \"{%% set values = { '0':'off', '1':'heat'} %%}{{ values[value] if value in values.keys() else 'off' }}\","
|
||||
"\"temperature_command_topic\": \"%s/%s/setpoint/set\","
|
||||
"\"temperature_state_topic\": \"%s/%s/setpoint\","
|
||||
"\"temperature_state_template\": \"{{ value_json }}\","
|
||||
"\"qos\": 1,"
|
||||
"\"retain\": false"
|
||||
"}";
|
||||
|
||||
const char *HASSIO_FREEZE_PROTECT_DISCOVER = "{"
|
||||
"\"device\": {" HASS_DEVICE "},"
|
||||
"\"availability\": {" HASS_AVAILABILITY "},"
|
||||
"\"type\": \"climate\","
|
||||
"\"unique_id\": \"aqualinkd_%s\","
|
||||
"\"name\": \"Freeze Protect\","
|
||||
"\"modes\": [\"off\", \"auto\"],"
|
||||
"\"send_if_off\": true,"
|
||||
"\"initial\": 34,"
|
||||
"\"payload_on\": \"1\","
|
||||
"\"payload_off\": \"0\","
|
||||
"\"current_temperature_topic\": \"%s/%s\","
|
||||
"\"min_temp\": 34,"
|
||||
"\"max_temp\": 42,"
|
||||
"\"mode_state_topic\": \"%s/%s\","
|
||||
"\"mode_state_template\": \"{%% set values = { '0':'off', '1':'auto'} %%}{{ values[value] if value in values.keys() else 'off' }}\","
|
||||
"\"temperature_command_topic\": \"%s/%s/setpoint/set\","
|
||||
"\"temperature_state_topic\": \"%s/%s/setpoint\","
|
||||
"\"temperature_state_template\": \"{{ value_json }}\""
|
||||
"}";
|
||||
|
||||
const char *HASSIO_SWG_DISCOVER = "{"
|
||||
"\"device\": {" HASS_DEVICE "},"
|
||||
"\"availability\": {" HASS_AVAILABILITY "},"
|
||||
"\"type\": \"humidifier\","
|
||||
"\"device_class\": \"humidifier\","
|
||||
"\"unique_id\": \"aqualinkd_%s\","
|
||||
"\"name\": \"Salt Water Generator\","
|
||||
"\"state_topic\": \"%s/%s\","
|
||||
"\"state_template\": \"{%% set values = { '0':'off', '2':'on'} %%}{{ values[value] if value in values.keys() else 'off' }}\","
|
||||
"\"command_topic\": \"%s/%s/set\","
|
||||
"\"current_humidity_topic\": \"%s/%s\","
|
||||
"\"target_humidity_command_topic\": \"%s/%s/set\","
|
||||
"\"target_humidity_state_topic\": \"%s/%s\","
|
||||
"\"payload_on\": \"2\","
|
||||
"\"payload_off\": \"0\","
|
||||
"\"min_humidity\":0,"
|
||||
"\"max_humidity\":100,"
|
||||
"\"optimistic\": false"
|
||||
"}";
|
||||
|
||||
// Need to add timer attributes to the switches, once figure out how to use in homeassistant
|
||||
// ie aqualinkd/Filter_Pump/timer/duration
|
||||
|
||||
const char *HASSIO_SWITCH_DISCOVER = "{"
|
||||
"\"device\": {" HASS_DEVICE "},"
|
||||
"\"availability\": {" HASS_AVAILABILITY "},"
|
||||
"\"type\": \"switch\","
|
||||
"\"unique_id\": \"aqualinkd_%s\","
|
||||
"\"name\": \"%s\","
|
||||
"\"state_topic\": \"%s/%s\","
|
||||
"\"command_topic\": \"%s/%s/set\","
|
||||
"\"json_attributes_topic\": \"%s/%s/delay\","
|
||||
"\"json_attributes_topic\": \"%s/%s/delay\","
|
||||
"\"json_attributes_template\": \"{{ {'delay': value|int} | tojson }}\","
|
||||
"\"payload_on\": \"1\","
|
||||
"\"payload_off\": \"0\","
|
||||
"\"qos\": 1,"
|
||||
"\"retain\": false"
|
||||
"}";
|
||||
|
||||
const char *HASSIO_TEMP_SENSOR_DISCOVER = "{"
|
||||
"\"device\": {" HASS_DEVICE "},"
|
||||
"\"availability\": {" HASS_AVAILABILITY "},"
|
||||
"\"type\": \"sensor\","
|
||||
"\"unique_id\": \"aqualinkd_%s\","
|
||||
"\"name\": \"%s Temp\","
|
||||
"\"state_topic\": \"%s/%s\","
|
||||
"\"value_template\": \"{{ value_json }}\","
|
||||
"\"unit_of_measurement\": \"°F\","
|
||||
"\"device_class\": \"temperature\","
|
||||
"\"icon\": \"%s\""
|
||||
"}";
|
||||
|
||||
const char *HASSIO_SENSOR_DISCOVER = "{"
|
||||
"\"device\": {" HASS_DEVICE "},"
|
||||
"\"availability\": {" HASS_AVAILABILITY "},"
|
||||
"\"type\": \"sensor\","
|
||||
"\"unique_id\": \"aqualinkd_%s\","
|
||||
"\"name\": \"%s\","
|
||||
"\"state_topic\": \"%s/%s\","
|
||||
"\"value_template\": \"{{ value_json }}\","
|
||||
"\"unit_of_measurement\": \"%s\","
|
||||
"\"icon\": \"%s\""
|
||||
"}";
|
||||
|
||||
const char *HASSIO_ONOFF_SENSOR_DISCOVER = "{"
|
||||
"\"device\": {" HASS_DEVICE "},"
|
||||
"\"availability\": {" HASS_AVAILABILITY "},"
|
||||
"\"type\": \"sensor\","
|
||||
"\"unique_id\": \"aqualinkd_%s\","
|
||||
"\"name\": \"%s\","
|
||||
"\"state_topic\": \"%s/%s\","
|
||||
"\"payload_on\": \"1\","
|
||||
"\"payload_off\": \"0\","
|
||||
"\"value_template\": \"{%% set values = { '0':'off', '1':'on'} %%}{{ values[value] if value in values.keys() else 'off' }}\","
|
||||
"\"icon\": \"%s\""
|
||||
"}";
|
||||
|
||||
const char *HASSIO_PUMP_SENSOR_DISCOVER = "{"
|
||||
"\"device\": {" HASS_DEVICE "},"
|
||||
"\"availability\": {" HASS_AVAILABILITY "},"
|
||||
"\"type\": \"sensor\","
|
||||
"\"unique_id\": \"aqualinkd_%s%d_%s\","
|
||||
"\"name\": \"%s %s %s\","
|
||||
"\"state_topic\": \"%s/%s%s\","
|
||||
"\"value_template\": \"{{ value_json }}\","
|
||||
"\"unit_of_measurement\": \"%s\","
|
||||
"\"icon\": \"mdi:pump\""
|
||||
"}";
|
||||
|
||||
const char *HASSIO_TEXT_SENSOR_DISCOVER = "{"
|
||||
"\"device\": {" HASS_DEVICE "},"
|
||||
"\"availability\": {" HASS_AVAILABILITY "},"
|
||||
"\"type\": \"sensor\","
|
||||
"\"unique_id\": \"aqualinkd_%s\","
|
||||
"\"name\": \"%s\","
|
||||
"\"state_topic\": \"%s/%s\","
|
||||
"\"icon\": \"mdi:card-text\""
|
||||
"}";
|
||||
|
||||
const char *HASSIO_SWG_TEXT_SENSOR_DISCOVER = "{"
|
||||
"\"device\": {" HASS_DEVICE "},"
|
||||
"\"availability\": {" HASS_AVAILABILITY "},"
|
||||
"\"type\": \"sensor\","
|
||||
"\"unique_id\": \"%s\","
|
||||
"\"name\": \"%s\","
|
||||
"\"state_topic\": \"%s/%s\","
|
||||
"\"payload_on\": \"0\","
|
||||
"\"payload_off\": \"255\","
|
||||
"\"value_template\": \"{%% set values = { '0':'Generating',"
|
||||
"'1':'No flow', "
|
||||
"'2':'low salt', "
|
||||
"'4':'high salt', "
|
||||
"'8':'clean cell', "
|
||||
"'9':'turning off', "
|
||||
"'16':'high current', "
|
||||
"'32':'low volts', "
|
||||
"'64':'low temp', "
|
||||
"'128':'Check PCB',"
|
||||
"'253':'General Fault',"
|
||||
"'254':'Unknown',"
|
||||
"'255':'Off'} %%}"
|
||||
"{{ values[value] if value in values.keys() else 'off' }}\","
|
||||
"\"icon\": \"mdi:card-text\""
|
||||
"}";
|
||||
|
||||
/*
|
||||
char *HASSIO_TEXT_DISCOVER = "{"
|
||||
"\"device\": {" HASS_DEVICE "},"
|
||||
"\"type\": \"text\","
|
||||
"\"unique_id\": \"aqualinkd_%s\","
|
||||
"\"name\": \"%s\","
|
||||
"\"command_topic\": \"junk/null\","
|
||||
"\"state_topic\": \"%s/%s\""
|
||||
"}";
|
||||
*/
|
||||
/*
|
||||
char *HASSIO_SERVICE_MODE_DISCOVER = "{"
|
||||
"\"type\": \"sensor\","
|
||||
"\"unique_id\": \"aqualinkd_Service_Mode\","
|
||||
"\"name\": \"Service Mode\","
|
||||
"\"state_topic\": \"aqualinkd/Service_Mode\","
|
||||
"\"value_template\": \"{% set values = { '0':'off', '1':'on'} %}{{ values[value] if value in values.keys() else 'off' }}\","
|
||||
"\"icon\": \"mdi:account-wrench\""
|
||||
"}";
|
||||
*/
|
||||
|
||||
/*
|
||||
Others to add
|
||||
|
||||
{
|
||||
"type": "text",
|
||||
"unique_id": "display",
|
||||
"name": "AqualinkD Display Message",
|
||||
"command_topic": "junk/null",
|
||||
"state_topic": "aqualinkd/Display_Message"
|
||||
}
|
||||
|
||||
{
|
||||
"type": "sensor",
|
||||
"unique_id": "Service_Mode",
|
||||
"name": "Service Mode",
|
||||
"state_topic": "aqualinkd/Service_Mode",
|
||||
"value_template": "{% set values = { '0':'off', '1':'on'} %}{{ values[value] if value in values.keys() else 'off' }}",
|
||||
"icon": "mdi:account-wrench"
|
||||
}
|
||||
|
||||
mdi:pump
|
||||
|
||||
mdi:water-outline // orph, ph, ppm, swg
|
||||
|
||||
mdi:water-thermometer // water
|
||||
mdi:thermometer // air
|
||||
|
||||
mdi:account-wrench // server
|
||||
*/
|
||||
|
||||
|
||||
void publish_mqtt_hassio_discover(struct aqualinkdata *aqdata, struct mg_connection *nc)
|
||||
{
|
||||
if (_aqconfig_.mqtt_hass_discover_topic == NULL)
|
||||
return;
|
||||
|
||||
int i;
|
||||
char msg[JSON_STATUS_SIZE];
|
||||
char topic[250];
|
||||
char idbuf[128];
|
||||
|
||||
LOG(NET_LOG,LOG_INFO, "MQTT: Publishing discover messages to '%s'\n", _aqconfig_.mqtt_hass_discover_topic);
|
||||
|
||||
for (i=0; i < aqdata->total_buttons; i++)
|
||||
{ // Heaters
|
||||
if ( (strcmp(BTN_POOL_HTR,aqdata->aqbuttons[i].name) == 0 && (_aqconfig_.force_ps_setpoints || aqdata->pool_htr_set_point != TEMP_UNKNOWN)) ||
|
||||
(strcmp(BTN_SPA_HTR,aqdata->aqbuttons[i].name)==0 && (_aqconfig_.force_ps_setpoints || aqdata->spa_htr_set_point != TEMP_UNKNOWN)) ) {
|
||||
sprintf(msg,HASSIO_CLIMATE_DISCOVER,
|
||||
_aqconfig_.mqtt_aq_topic,
|
||||
aqdata->aqbuttons[i].name,
|
||||
aqdata->aqbuttons[i].label,
|
||||
_aqconfig_.mqtt_aq_topic,aqdata->aqbuttons[i].name,
|
||||
_aqconfig_.mqtt_aq_topic,(strcmp(BTN_POOL_HTR,aqdata->aqbuttons[i].name) == 0)?POOL_TEMP_TOPIC:SPA_TEMP_TOPIC,
|
||||
_aqconfig_.mqtt_aq_topic,aqdata->aqbuttons[i].name,
|
||||
_aqconfig_.mqtt_aq_topic,aqdata->aqbuttons[i].name,
|
||||
_aqconfig_.mqtt_aq_topic,aqdata->aqbuttons[i].name,
|
||||
_aqconfig_.mqtt_aq_topic,aqdata->aqbuttons[i].name);
|
||||
sprintf(topic, "%s/climate/aqualinkd/aqualinkd_%s/config", _aqconfig_.mqtt_hass_discover_topic, aqdata->aqbuttons[i].name);
|
||||
send_mqtt(nc, topic, msg);
|
||||
} else if (strcmp("NONE",aqdata->aqbuttons[i].label) != 0 ) {
|
||||
// Switches
|
||||
//sprintf(msg,"{\"type\": \"switch\",\"unique_id\": \"%s\",\"name\": \"%s\",\"state_topic\": \"aqualinkd/%s\",\"command_topic\": \"aqualinkd/%s/set\",\"json_attributes_topic\": \"aqualinkd/%s/delay\",\"json_attributes_topic\": \"aqualinkd/%s/delay\",\"json_attributes_template\": \"{{ {'delay': value|int} | tojson }}\",\"payload_on\": \"1\",\"payload_off\": \"0\",\"qos\": 1,\"retain\": false}" ,
|
||||
sprintf(msg, HASSIO_SWITCH_DISCOVER,
|
||||
_aqconfig_.mqtt_aq_topic,
|
||||
aqdata->aqbuttons[i].name,
|
||||
aqdata->aqbuttons[i].label,
|
||||
_aqconfig_.mqtt_aq_topic,aqdata->aqbuttons[i].name,
|
||||
_aqconfig_.mqtt_aq_topic,aqdata->aqbuttons[i].name,
|
||||
_aqconfig_.mqtt_aq_topic,aqdata->aqbuttons[i].name,
|
||||
_aqconfig_.mqtt_aq_topic,aqdata->aqbuttons[i].name);
|
||||
sprintf(topic, "%s/switch/aqualinkd/aqualinkd_%s/config", _aqconfig_.mqtt_hass_discover_topic, aqdata->aqbuttons[i].name);
|
||||
send_mqtt(nc, topic, msg);
|
||||
}
|
||||
}
|
||||
|
||||
// Freezeprotect
|
||||
if ( _aqconfig_.force_frzprotect_setpoints || (aqdata->frz_protect_set_point != TEMP_UNKNOWN && aqdata->air_temp != TEMP_UNKNOWN) ) {
|
||||
sprintf(msg, HASSIO_FREEZE_PROTECT_DISCOVER,
|
||||
_aqconfig_.mqtt_aq_topic,
|
||||
FREEZE_PROTECT,
|
||||
_aqconfig_.mqtt_aq_topic,AIR_TEMP_TOPIC,
|
||||
_aqconfig_.mqtt_aq_topic,FREEZE_PROTECT_ENABELED,
|
||||
_aqconfig_.mqtt_aq_topic,FREEZE_PROTECT,
|
||||
_aqconfig_.mqtt_aq_topic,FREEZE_PROTECT,
|
||||
_aqconfig_.mqtt_aq_topic,FREEZE_PROTECT
|
||||
);
|
||||
sprintf(topic, "%s/climate/aqualinkd/aqualinkd_%s/config", _aqconfig_.mqtt_hass_discover_topic, FREEZE_PROTECT);
|
||||
send_mqtt(nc, topic, msg);
|
||||
}
|
||||
|
||||
|
||||
// SWG
|
||||
if ( aqdata->swg_percent != TEMP_UNKNOWN ) {
|
||||
|
||||
sprintf(msg, HASSIO_SWG_DISCOVER,
|
||||
_aqconfig_.mqtt_aq_topic,
|
||||
SWG_TOPIC,
|
||||
_aqconfig_.mqtt_aq_topic,SWG_TOPIC,
|
||||
_aqconfig_.mqtt_aq_topic,SWG_PERCENT_TOPIC,
|
||||
_aqconfig_.mqtt_aq_topic,SWG_PERCENT_TOPIC,
|
||||
_aqconfig_.mqtt_aq_topic,SWG_PERCENT_TOPIC,
|
||||
_aqconfig_.mqtt_aq_topic,SWG_PERCENT_TOPIC
|
||||
);
|
||||
sprintf(topic, "%s/humidifier/aqualinkd/aqualinkd_%s/config", _aqconfig_.mqtt_hass_discover_topic, SWG_TOPIC);
|
||||
send_mqtt(nc, topic, msg);
|
||||
|
||||
rsm_char_replace(idbuf, SWG_BOOST_TOPIC, "/", "_");
|
||||
sprintf(msg, HASSIO_SWITCH_DISCOVER,
|
||||
_aqconfig_.mqtt_aq_topic,
|
||||
idbuf,
|
||||
"SWG Boost",
|
||||
_aqconfig_.mqtt_aq_topic,SWG_BOOST_TOPIC,
|
||||
_aqconfig_.mqtt_aq_topic,SWG_BOOST_TOPIC,
|
||||
_aqconfig_.mqtt_aq_topic,SWG_BOOST_TOPIC,
|
||||
_aqconfig_.mqtt_aq_topic,SWG_BOOST_TOPIC);
|
||||
sprintf(topic, "%s/switch/aqualinkd/aqualinkd_%s/config", _aqconfig_.mqtt_hass_discover_topic, idbuf);
|
||||
send_mqtt(nc, topic, msg);
|
||||
|
||||
rsm_char_replace(idbuf, SWG_PERCENT_TOPIC, "/", "_");
|
||||
sprintf(msg, HASSIO_SENSOR_DISCOVER,_aqconfig_.mqtt_aq_topic,idbuf,"SWG Percent",_aqconfig_.mqtt_aq_topic,SWG_PERCENT_TOPIC, "%", "mdi:water-outline");
|
||||
sprintf(topic, "%s/sensor/aqualinkd/aqualinkd_%s/config", _aqconfig_.mqtt_hass_discover_topic, idbuf);
|
||||
send_mqtt(nc, topic, msg);
|
||||
|
||||
rsm_char_replace(idbuf, SWG_PPM_TOPIC, "/", "_");
|
||||
sprintf(msg, HASSIO_SENSOR_DISCOVER,_aqconfig_.mqtt_aq_topic,idbuf,"SWG PPM",_aqconfig_.mqtt_aq_topic,SWG_PPM_TOPIC, "ppm", "mdi:water-outline");
|
||||
sprintf(topic, "%s/sensor/aqualinkd/aqualinkd_%s/config", _aqconfig_.mqtt_hass_discover_topic, idbuf);
|
||||
send_mqtt(nc, topic, msg);
|
||||
|
||||
rsm_char_replace(idbuf, SWG_EXTENDED_TOPIC, "/", "_");
|
||||
sprintf(msg, HASSIO_SWG_TEXT_SENSOR_DISCOVER,_aqconfig_.mqtt_aq_topic,idbuf,"SWG Msg",_aqconfig_.mqtt_aq_topic,SWG_EXTENDED_TOPIC);
|
||||
sprintf(topic, "%s/sensor/aqualinkd/aqualinkd_%s/config", _aqconfig_.mqtt_hass_discover_topic, idbuf);
|
||||
send_mqtt(nc, topic, msg);
|
||||
/*
|
||||
// SWG Display message (move to SWG area)
|
||||
rsm_char_replace(idbuf, SWG_STATUS_MSG_TOPIC, "/", "_");
|
||||
sprintf(msg, HASSIO_TEXT_SENSOR_DISCOVER,idbuf,"SWG Msg",_aqconfig_.mqtt_aq_topic,SWG_STATUS_MSG_TOPIC);
|
||||
sprintf(topic, "%s/sensor/aqualinkd/aqualinkd_%s/config", _aqconfig_.mqtt_hass_discover_topic, idbuf);
|
||||
send_mqtt(nc, topic, msg);
|
||||
*/
|
||||
}
|
||||
|
||||
// Temperatures
|
||||
sprintf(msg, HASSIO_TEMP_SENSOR_DISCOVER,_aqconfig_.mqtt_aq_topic,"Pool","Pool",_aqconfig_.mqtt_aq_topic,POOL_TEMP_TOPIC,"mdi:water-thermometer");
|
||||
sprintf(topic, "%s/sensor/aqualinkd/aqualinkd_%s/config", _aqconfig_.mqtt_hass_discover_topic, "Pool");
|
||||
send_mqtt(nc, topic, msg);
|
||||
|
||||
sprintf(msg, HASSIO_TEMP_SENSOR_DISCOVER,_aqconfig_.mqtt_aq_topic,"Spa","Spa",_aqconfig_.mqtt_aq_topic,SPA_TEMP_TOPIC,"mdi:water-thermometer");
|
||||
sprintf(topic, "%s/sensor/aqualinkd/aqualinkd_%s/config", _aqconfig_.mqtt_hass_discover_topic, "Spa");
|
||||
send_mqtt(nc, topic, msg);
|
||||
|
||||
sprintf(msg, HASSIO_TEMP_SENSOR_DISCOVER,_aqconfig_.mqtt_aq_topic,"Air","Air",_aqconfig_.mqtt_aq_topic,AIR_TEMP_TOPIC,"mdi:thermometer");
|
||||
sprintf(topic, "%s/sensor/aqualinkd/aqualinkd_%s/config", _aqconfig_.mqtt_hass_discover_topic, "Air");
|
||||
send_mqtt(nc, topic, msg);
|
||||
|
||||
// Pumps
|
||||
for (i=0; i < aqdata->num_pumps; i++) {
|
||||
int pn=i+1;
|
||||
if (aqdata->pumps[i].pumpType==VFPUMP) {
|
||||
// We have GPM info
|
||||
sprintf(msg, HASSIO_PUMP_SENSOR_DISCOVER,
|
||||
_aqconfig_.mqtt_aq_topic,
|
||||
"Pump",pn,"GPM",
|
||||
aqdata->pumps[i].button->label,(rsm_strncasestr(aqdata->pumps[i].button->label,"pump",strlen(aqdata->pumps[i].button->label))!=NULL)?"":"Pump","GPM",
|
||||
_aqconfig_.mqtt_aq_topic,aqdata->pumps[i].button->name ,PUMP_GPM_TOPIC,
|
||||
"GPM");
|
||||
sprintf(topic, "%s/sensor/aqualinkd/aqualinkd_%s%d_%s/config", _aqconfig_.mqtt_hass_discover_topic, "Pump",pn,"GPM");
|
||||
send_mqtt(nc, topic, msg);
|
||||
}
|
||||
sprintf(msg, HASSIO_PUMP_SENSOR_DISCOVER,
|
||||
_aqconfig_.mqtt_aq_topic,
|
||||
"Pump",pn,"RPM",
|
||||
aqdata->pumps[i].button->label,(rsm_strncasestr(aqdata->pumps[i].button->label,"pump",strlen(aqdata->pumps[i].button->label))!=NULL)?"":"Pump","RPM",
|
||||
_aqconfig_.mqtt_aq_topic,aqdata->pumps[i].button->name ,PUMP_RPM_TOPIC,
|
||||
"RPM");
|
||||
sprintf(topic, "%s/sensor/aqualinkd/aqualinkd_%s%d_%s/config", _aqconfig_.mqtt_hass_discover_topic, "Pump",pn,"RPM");
|
||||
send_mqtt(nc, topic, msg);
|
||||
|
||||
sprintf(msg, HASSIO_PUMP_SENSOR_DISCOVER,
|
||||
_aqconfig_.mqtt_aq_topic,
|
||||
"Pump",pn,"Watts",
|
||||
aqdata->pumps[i].button->label,(rsm_strncasestr(aqdata->pumps[i].button->label,"pump",strlen(aqdata->pumps[i].button->label))!=NULL)?"":"Pump","Watts",
|
||||
_aqconfig_.mqtt_aq_topic,aqdata->pumps[i].button->name ,PUMP_WATTS_TOPIC,
|
||||
"Watts");
|
||||
sprintf(topic, "%s/sensor/aqualinkd/aqualinkd_%s%d_%s/config", _aqconfig_.mqtt_hass_discover_topic, "Pump",pn,"Watts");
|
||||
send_mqtt(nc, topic, msg);
|
||||
}
|
||||
|
||||
// Chem feeder (ph/orp)
|
||||
if (_aqconfig_.force_chem_feeder || aqdata->ph != TEMP_UNKNOWN) {
|
||||
rsm_char_replace(idbuf, CHEM_PH_TOPIC, "/", "_");
|
||||
sprintf(msg, HASSIO_SENSOR_DISCOVER,_aqconfig_.mqtt_aq_topic,idbuf,"Water Chemistry pH",_aqconfig_.mqtt_aq_topic,CHEM_PH_TOPIC, "pH", "mdi:water-outline");
|
||||
sprintf(topic, "%s/sensor/aqualinkd/aqualinkd_%s/config", _aqconfig_.mqtt_hass_discover_topic, idbuf);
|
||||
send_mqtt(nc, topic, msg);
|
||||
}
|
||||
|
||||
if (_aqconfig_.force_chem_feeder || aqdata->orp != TEMP_UNKNOWN) {
|
||||
rsm_char_replace(idbuf, CHEM_ORP_TOPIC, "/", "_");
|
||||
sprintf(msg, HASSIO_SENSOR_DISCOVER,_aqconfig_.mqtt_aq_topic,idbuf,"Water Chemistry ORP",_aqconfig_.mqtt_aq_topic,CHEM_ORP_TOPIC, "orp", "mdi:water-outline");
|
||||
sprintf(topic, "%s/sensor/aqualinkd/aqualinkd_%s/config", _aqconfig_.mqtt_hass_discover_topic, idbuf);
|
||||
send_mqtt(nc, topic, msg);
|
||||
}
|
||||
|
||||
// Misc stuff
|
||||
sprintf(msg, HASSIO_ONOFF_SENSOR_DISCOVER,_aqconfig_.mqtt_aq_topic,SERVICE_MODE_TOPIC,"Service Mode",_aqconfig_.mqtt_aq_topic,SERVICE_MODE_TOPIC, "mdi:account-wrench");
|
||||
sprintf(topic, "%s/sensor/aqualinkd/aqualinkd_%s/config", _aqconfig_.mqtt_hass_discover_topic, SERVICE_MODE_TOPIC);
|
||||
send_mqtt(nc, topic, msg);
|
||||
|
||||
/* // Leave below if we decide to go back to a text box
|
||||
sprintf(msg, HASSIO_TEXT_DISCOVER,DISPLAY_MSG_TOPIC,"Display Messages",_aqconfig_.mqtt_aq_topic,DISPLAY_MSG_TOPIC);
|
||||
sprintf(topic, "%s/text/aqualinkd/aqualinkd_%s/config", _aqconfig_.mqtt_hass_discover_topic, DISPLAY_MSG_TOPIC);
|
||||
*/
|
||||
// It actually works better posting this to sensor and not text.
|
||||
sprintf(msg, HASSIO_TEXT_SENSOR_DISCOVER,_aqconfig_.mqtt_aq_topic,DISPLAY_MSG_TOPIC,"Display Msg",_aqconfig_.mqtt_aq_topic,DISPLAY_MSG_TOPIC);
|
||||
sprintf(topic, "%s/sensor/aqualinkd/aqualinkd_%s/config", _aqconfig_.mqtt_hass_discover_topic, DISPLAY_MSG_TOPIC);
|
||||
send_mqtt(nc, topic, msg);
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef HASSIO_H_
|
||||
#define HASSIO_H_
|
||||
|
||||
|
||||
|
||||
void publish_mqtt_hassio_discover(struct aqualinkdata *aqdata, struct mg_connection *nc);
|
||||
|
||||
#endif // HASSIO_H_
|
430
iaqtouch.c
430
iaqtouch.c
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "aq_serial.h"
|
||||
#include "aqualink.h"
|
||||
|
@ -69,14 +70,15 @@ unsigned char _lastMsgType = 0x00;
|
|||
//unsigned char _last_kick_type = -1;
|
||||
|
||||
int _deviceStatusLines = 0;
|
||||
char _homeStatus[IAQ_STATUS_PAGE_LINES][AQ_MSGLEN+1];
|
||||
char _deviceStatus[IAQ_STATUS_PAGE_LINES][AQ_MSGLEN+1];
|
||||
char _tableInformation[IAQ_MSG_TABLE_LINES][IAQT_TABLE_MSGLEN+1];
|
||||
struct iaqt_page_button _pageButtons[IAQ_PAGE_BUTTONS];
|
||||
struct iaqt_page_button _homeButtons[IAQ_PAGE_BUTTONS];
|
||||
|
||||
// Need to cache these two pages, as only get updates after initial load.
|
||||
struct iaqt_page_button _devicePageButtons[IAQ_PAGE_BUTTONS];
|
||||
struct iaqt_page_button _devicePage2Buttons[IAQ_PAGE_BUTTONS];
|
||||
struct iaqt_page_button _deviceSystemSetupButtons[IAQ_PAGE_BUTTONS];
|
||||
// Need to cache these pages, as only get updates after initial load.
|
||||
struct iaqt_page_button _devicePageButtons[3][IAQ_PAGE_BUTTONS];
|
||||
struct iaqt_page_button _deviceSystemSetupButtons[3][IAQ_PAGE_BUTTONS];
|
||||
|
||||
unsigned char iaqtLastMsg()
|
||||
{
|
||||
|
@ -110,6 +112,11 @@ unsigned char iaqtCurrentPage()
|
|||
{
|
||||
return _currentPage;
|
||||
}
|
||||
unsigned char iaqtCurrentPageLoading()
|
||||
{
|
||||
return _currentPageLoading;
|
||||
}
|
||||
|
||||
|
||||
const char *iaqtGetMessageLine(int index) {
|
||||
if (index < IAQ_STATUS_PAGE_LINES)
|
||||
|
@ -129,12 +136,20 @@ struct iaqt_page_button *iaqtFindButtonByIndex(int index) {
|
|||
struct iaqt_page_button *buttons;
|
||||
|
||||
// NSF Need to merge this from iaqtFindButtonByLabel function
|
||||
if (_currentPage == IAQ_PAGE_DEVICES )
|
||||
buttons = _devicePageButtons;
|
||||
else if (_currentPage == IAQ_PAGE_DEVICES2 )
|
||||
buttons = _devicePage2Buttons;
|
||||
if (_currentPage == IAQ_PAGE_DEVICES)
|
||||
buttons = _devicePageButtons[0];
|
||||
else if (_currentPage == IAQ_PAGE_DEVICES2)
|
||||
buttons = _devicePageButtons[1];
|
||||
else if (_currentPage == IAQ_PAGE_DEVICES3)
|
||||
buttons = _devicePageButtons[2];
|
||||
else if (_currentPage == IAQ_PAGE_SYSTEM_SETUP )
|
||||
buttons = _deviceSystemSetupButtons;
|
||||
buttons = _deviceSystemSetupButtons[0];
|
||||
else if (_currentPage == IAQ_PAGE_SYSTEM_SETUP2 )
|
||||
buttons = _deviceSystemSetupButtons[1];
|
||||
else if (_currentPage == IAQ_PAGE_SYSTEM_SETUP3 )
|
||||
buttons = _deviceSystemSetupButtons[2];
|
||||
else if (_currentPage == IAQ_PAGE_HOME )
|
||||
buttons = _homeButtons;
|
||||
else
|
||||
buttons = _pageButtons;
|
||||
|
||||
|
@ -145,16 +160,24 @@ struct iaqt_page_button *iaqtFindButtonByIndex(int index) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
struct iaqt_page_button *iaqtFindButtonByLabel(char *label) {
|
||||
struct iaqt_page_button *iaqtFindButtonByLabel(const char *label) {
|
||||
int i;
|
||||
struct iaqt_page_button *buttons;
|
||||
|
||||
if (_currentPage == IAQ_PAGE_DEVICES )
|
||||
buttons = _devicePageButtons;
|
||||
else if (_currentPage == IAQ_PAGE_DEVICES2 )
|
||||
buttons = _devicePage2Buttons;
|
||||
if (_currentPage == IAQ_PAGE_DEVICES)
|
||||
buttons = _devicePageButtons[0];
|
||||
else if (_currentPage == IAQ_PAGE_DEVICES2)
|
||||
buttons = _devicePageButtons[1];
|
||||
else if (_currentPage == IAQ_PAGE_DEVICES3)
|
||||
buttons = _devicePageButtons[2];
|
||||
else if (_currentPage == IAQ_PAGE_SYSTEM_SETUP )
|
||||
buttons = _deviceSystemSetupButtons;
|
||||
buttons = _deviceSystemSetupButtons[0];
|
||||
else if (_currentPage == IAQ_PAGE_SYSTEM_SETUP2 )
|
||||
buttons = _deviceSystemSetupButtons[1];
|
||||
else if (_currentPage == IAQ_PAGE_SYSTEM_SETUP3 )
|
||||
buttons = _deviceSystemSetupButtons[2];
|
||||
else if (_currentPage == IAQ_PAGE_HOME )
|
||||
buttons = _homeButtons;
|
||||
else
|
||||
buttons = _pageButtons;
|
||||
|
||||
|
@ -237,8 +260,10 @@ void processPageMessage(unsigned char *message, int length)
|
|||
LOG(IAQT_LOG,LOG_ERR, "Run out of IAQT message buffer, need %d have %d\n",(int)message[PKT_IAQT_MSGINDX],IAQ_STATUS_PAGE_LINES);
|
||||
return;
|
||||
}
|
||||
// 2nd page of device status doesn;t gine us new page message
|
||||
if (_currentPageLoading == IAQ_PAGE_STATUS || _currentPage == IAQ_PAGE_STATUS) {
|
||||
|
||||
if (_currentPageLoading == IAQ_PAGE_HOME || _currentPage == IAQ_PAGE_HOME) {
|
||||
rsm_strncpy(_homeStatus[(int)message[PKT_IAQT_MSGINDX]], &message[PKT_IAQT_MSGDATA], AQ_MSGLEN, length-PKT_IAQT_MSGDATA-3);
|
||||
} else if (_currentPageLoading == IAQ_PAGE_STATUS || _currentPage == IAQ_PAGE_STATUS) { // 2nd page of device status doesn;t gine us new page message
|
||||
//sprintf(_deviceStatus[(int)message[4]], message[5], AQ_MSGLEN);
|
||||
//strncpy(_deviceStatus[(int)message[PKT_IAQT_MSGINDX]], (char *)message + PKT_IAQT_MSGDATA, AQ_MSGLEN);
|
||||
rsm_strncpy(_deviceStatus[(int)message[PKT_IAQT_MSGINDX]], &message[PKT_IAQT_MSGDATA], AQ_MSGLEN, length-PKT_IAQT_MSGDATA-3);
|
||||
|
@ -259,19 +284,30 @@ void processTableMessage(unsigned char *message, int length)
|
|||
LOG(IAQT_LOG,LOG_ERR, "Run out of IAQT table buffer, need %d have %d\n",(int)message[5],IAQ_MSG_TABLE_LINES);
|
||||
}
|
||||
|
||||
void processPageButton(unsigned char *message, int length)
|
||||
void processPageButton(unsigned char *message, int length, struct aqualinkdata *aq_data)
|
||||
{
|
||||
struct iaqt_page_button *button;
|
||||
int index = (int)message[PKT_IAQT_BUTINDX];
|
||||
|
||||
if (_currentPageLoading == IAQ_PAGE_DEVICES )
|
||||
button = &_devicePageButtons[index];
|
||||
else if (_currentPageLoading == IAQ_PAGE_DEVICES2 )
|
||||
button = &_devicePage2Buttons[index];
|
||||
else if (_currentPageLoading == IAQ_PAGE_SYSTEM_SETUP )
|
||||
button = &_deviceSystemSetupButtons[index];
|
||||
else
|
||||
if (_currentPageLoading == IAQ_PAGE_DEVICES)
|
||||
button = &_devicePageButtons[0][index];
|
||||
else if (_currentPageLoading == IAQ_PAGE_DEVICES2)
|
||||
button = &_devicePageButtons[1][index];
|
||||
else if (_currentPageLoading == IAQ_PAGE_DEVICES3)
|
||||
button = &_devicePageButtons[2][index];
|
||||
else if (_currentPageLoading == IAQ_PAGE_SYSTEM_SETUP)
|
||||
button = &_deviceSystemSetupButtons[0][index];
|
||||
else if (_currentPageLoading == IAQ_PAGE_SYSTEM_SETUP2)
|
||||
button = &_deviceSystemSetupButtons[1][index];
|
||||
else if (_currentPageLoading == IAQ_PAGE_SYSTEM_SETUP3)
|
||||
button = &_deviceSystemSetupButtons[2][index];
|
||||
else if (_currentPageLoading == IAQ_PAGE_HOME )
|
||||
button = &_homeButtons[index];
|
||||
else {
|
||||
button = &_pageButtons[index];
|
||||
// if _currentPageLoading = 0x00 then we should use current page
|
||||
LOG(IAQT_LOG,LOG_INFO, "Not sure where to add Button %d %s - LoadingPage = %s\n",index,button->name,iaqt_page_name(_currentPageLoading));
|
||||
}
|
||||
|
||||
button->state = message[PKT_IAQT_BUTSTATE];
|
||||
button->type = message[PKT_IAQT_BUTTYPE];
|
||||
|
@ -285,19 +321,74 @@ void processPageButton(unsigned char *message, int length)
|
|||
// This doesn't work with return which is 0x00
|
||||
|
||||
//strncpy(&button->name, (char *)message + PKT_IAQT_BUTDATA, AQ_MSGLEN);
|
||||
memset(button->name, 0, sizeof(button->name));
|
||||
rsm_strncpy_nul2sp((char *)button->name, &message[PKT_IAQT_BUTDATA], IAQT_MSGLEN, length-PKT_IAQT_BUTDATA-3);
|
||||
|
||||
LOG(IAQT_LOG,LOG_DEBUG, "Added Button %d %s\n",index,button->name);
|
||||
LOG(IAQT_LOG,LOG_DEBUG, "Added Button %d %s - LoadingPage = %s\n",index,button->name,iaqt_page_name(_currentPageLoading));
|
||||
|
||||
/*
|
||||
_pageButtons[index].state = (int)message[5];
|
||||
_pageButtons[index].type = (int)message[7];
|
||||
_pageButtons[index].unknownByte = (int)message[6];
|
||||
// This doesn't work with return which is 0x00
|
||||
strncpy(&_pageButtons[index].name, (char *)message + 8, AQ_MSGLEN);
|
||||
// This get's called or every device state change in PDA mode, since we page over all the devices.
|
||||
// So capture and update the device state
|
||||
|
||||
LOG(IAQT_LOG,LOG_NOTICE, "Added Button %d %s\n",index,_pageButtons[index].name);
|
||||
*/
|
||||
if (isPDA_PANEL || PANEL_SIZE() >= 16 ) {
|
||||
int start = 0;
|
||||
int end = aq_data->total_buttons;
|
||||
|
||||
#ifdef AQ_RS16
|
||||
if (PANEL_SIZE() >= 16) {
|
||||
start = aq_data->rs16_vbutton_start;
|
||||
end = aq_data->rs16_vbutton_end + 1; // Using < in comparison and not <=, so +1
|
||||
//printf("************ CHECK RS16 BUTTONS ************\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
for (int i = start; i < end; i++)
|
||||
{
|
||||
|
||||
int rtn=-1;
|
||||
//LOG(IAQT_LOG,LOG_DEBUG, "Button compare '%s' to '%s'\n",button->name, aq_data->aqbuttons[i].label);
|
||||
// If we are loading HOME page then simply button name is the label ie "Aux3"
|
||||
// If loading DEVICES? page then button name + statusis "Aux3 OFF "
|
||||
|
||||
if (_currentPageLoading == IAQ_PAGE_HOME)
|
||||
rtn = rsm_strmatch((const char *)button->name, aq_data->aqbuttons[i].label);
|
||||
else
|
||||
rtn = rsm_strmatch_ignore((const char *)button->name, aq_data->aqbuttons[i].label,5); // 5 = 3 chars and 2 spaces ' OFF '
|
||||
|
||||
if (rtn == 0)
|
||||
{
|
||||
LOG(IAQT_LOG,LOG_DEBUG, "*** Found Status for %s state 0x%02hhx\n", aq_data->aqbuttons[i].label, button->state);
|
||||
switch(button->state) {
|
||||
case 0x00:
|
||||
if (aq_data->aqbuttons[i].led->state != OFF) {
|
||||
aq_data->aqbuttons[i].led->state = OFF;
|
||||
aq_data->updated = true;
|
||||
}
|
||||
break;
|
||||
case 0x01:
|
||||
if (aq_data->aqbuttons[i].led->state != ON) {
|
||||
aq_data->aqbuttons[i].led->state = ON;
|
||||
aq_data->updated = true;
|
||||
}
|
||||
break;
|
||||
case 0x02:
|
||||
if (aq_data->aqbuttons[i].led->state != FLASH) {
|
||||
aq_data->aqbuttons[i].led->state = FLASH;
|
||||
aq_data->updated = true;
|
||||
}
|
||||
break;
|
||||
case 0x03:
|
||||
if (aq_data->aqbuttons[i].led->state != ENABLE) {
|
||||
aq_data->aqbuttons[i].led->state = ENABLE;
|
||||
aq_data->updated = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG(IAQT_LOG,LOG_NOTICE, "Unknown state 0x%02hhx for button %s\n",button->state,button->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -367,39 +458,45 @@ void passDeviceStatusPage(struct aqualinkdata *aq_data)
|
|||
continue;
|
||||
|
||||
} else if (rsm_strcmp(_deviceStatus[i],"RPM:") == 0) {
|
||||
if (pump != NULL)
|
||||
if (pump != NULL) {
|
||||
pump->rpm = rsm_atoi(&_deviceStatus[i][9]);
|
||||
else
|
||||
aq_data->updated = true;
|
||||
} else
|
||||
LOG(IAQT_LOG,LOG_WARNING, "Got pump message '%s' but can't find pump\n",_deviceStatus[i]);
|
||||
continue;
|
||||
} else if (rsm_strcmp(_deviceStatus[i],"GPM:") == 0) {
|
||||
if (pump != NULL)
|
||||
if (pump != NULL) {
|
||||
pump->gpm = rsm_atoi(&_deviceStatus[i][9]);
|
||||
else
|
||||
aq_data->updated = true;
|
||||
} else
|
||||
LOG(IAQT_LOG,LOG_WARNING, "Got pump message '%s' but can't find pump\n",_deviceStatus[i]);
|
||||
continue;
|
||||
} else if (rsm_strcmp(_deviceStatus[i],"Watts:") == 0) {
|
||||
if (pump != NULL)
|
||||
if (pump != NULL) {
|
||||
pump->watts = rsm_atoi(&_deviceStatus[i][9]);
|
||||
else
|
||||
aq_data->updated = true;
|
||||
} else
|
||||
LOG(IAQT_LOG,LOG_WARNING, "Got pump message '%s' but can't find pump\n",_deviceStatus[i]);
|
||||
continue;
|
||||
} else if (rsm_strcmp(_deviceStatus[i],"*** Priming ***") == 0) {
|
||||
if (pump != NULL)
|
||||
if (pump != NULL) {
|
||||
pump->rpm = PUMP_PRIMING;
|
||||
else
|
||||
aq_data->updated = true;
|
||||
} else
|
||||
LOG(IAQT_LOG,LOG_WARNING, "Got pump message '%s' but can't find pump\n",_deviceStatus[i]);
|
||||
continue;
|
||||
} else if (rsm_strcmp(_deviceStatus[i],"(Offline)") == 0) {
|
||||
if (pump != NULL)
|
||||
if (pump != NULL) {
|
||||
pump->rpm = PUMP_OFFLINE;
|
||||
else
|
||||
aq_data->updated = true;
|
||||
} else
|
||||
LOG(IAQT_LOG,LOG_WARNING, "Got pump message '%s' but can't find pump\n",_deviceStatus[i]);
|
||||
continue;
|
||||
} else if (rsm_strcmp(_deviceStatus[i],"(Priming Error)") == 0) {
|
||||
if (pump != NULL)
|
||||
if (pump != NULL) {
|
||||
pump->rpm = PUMP_ERROR;
|
||||
else
|
||||
aq_data->updated = true;
|
||||
} else
|
||||
LOG(IAQT_LOG,LOG_WARNING, "Got pump message '%s' but can't find pump\n",_deviceStatus[i]);
|
||||
continue;
|
||||
// Need to catch messages like
|
||||
|
@ -422,22 +519,27 @@ void passDeviceStatusPage(struct aqualinkdata *aq_data)
|
|||
aq_data->ph = ph;
|
||||
aq_data->orp = orp;
|
||||
}
|
||||
aq_data->updated = true;
|
||||
LOG(IAQT_LOG,LOG_INFO, "Set Cemlink ORP = %d PH = %f from message '%s'\n",orp,ph,_deviceStatus[i]);
|
||||
}
|
||||
}
|
||||
#ifdef READ_SWG_FROM_EXTENDED_ID
|
||||
else if (rsm_strcmp(_deviceStatus[i],"AQUAPURE") == 0) {
|
||||
//#ifdef READ_SWG_FROM_EXTENDED_ID
|
||||
else if (isPDA_PANEL) {
|
||||
if (rsm_strcmp(_deviceStatus[i],"AQUAPURE") == 0) {
|
||||
//aq_data->swg_percent = rsm_atoi(&_deviceStatus[i][9]);
|
||||
if (changeSWGpercent(aq_data, rsm_atoi(&_deviceStatus[i][9])))
|
||||
LOG(IAQT_LOG,LOG_DEBUG, "Set swg %% to %d from message'%s'\n",aq_data->swg_percent,_deviceStatus[i]);
|
||||
} else if (rsm_strcmp(_deviceStatus[i],"salt") == 0) {
|
||||
} else if (rsm_strcmp(_deviceStatus[i],"salt") == 0) {
|
||||
aq_data->swg_ppm = rsm_atoi(&_deviceStatus[i][5]);
|
||||
aq_data->updated = true;
|
||||
LOG(IAQT_LOG,LOG_DEBUG, "Set swg PPM to %d from message'%s'\n",aq_data->swg_ppm,_deviceStatus[i]);
|
||||
} else if (rsm_strcmp(_deviceStatus[i],"Boost Pool") == 0) {
|
||||
} else if (rsm_strcmp(_deviceStatus[i],"Boost Pool") == 0) {
|
||||
aq_data->boost = true;
|
||||
aq_data->updated = true;
|
||||
// Let RS pickup time remaing message.
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
//#endif
|
||||
|
||||
} // for
|
||||
}
|
||||
|
@ -451,9 +553,13 @@ void debugPrintButtons(struct iaqt_page_button buttons[])
|
|||
}
|
||||
}
|
||||
|
||||
//#define member_size(type, member) (sizeof( ((type*)0)->member ))
|
||||
|
||||
void processPage(struct aqualinkdata *aq_data)
|
||||
{
|
||||
//static int _home_cnt = 0;
|
||||
int i;
|
||||
int dp = 0;
|
||||
|
||||
LOG(IAQT_LOG,LOG_INFO, "Page: %s | 0x%02hhx\n",iaqt_page_name(_currentPage),_currentPage);
|
||||
|
||||
|
@ -468,18 +574,36 @@ void processPage(struct aqualinkdata *aq_data)
|
|||
debugPrintButtons(_pageButtons);
|
||||
passDeviceStatusPage(aq_data);
|
||||
// If button 1 is type 0x02 then there is a next page. Since status page isn't used for programming, hit the next page button.
|
||||
if (_pageButtons[1].type == 0x02)
|
||||
if (_pageButtons[1].type == 0x02) {
|
||||
iaqt_queue_cmd(KEY_IAQTCH_KEY02);
|
||||
else
|
||||
} else {
|
||||
iaqt_pump_update(aq_data, -1); // Reset pumps.
|
||||
if ( (isPDA_PANEL || PANEL_SIZE() >= 16) && !in_iaqt_programming_mode(aq_data) ) {
|
||||
iaqt_queue_cmd(KEY_IAQTCH_HOME);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IAQ_PAGE_DEVICES:
|
||||
//LOG(IAQT_LOG,LOG_INFO, "Devices Page #1:-\n");
|
||||
debugPrintButtons(_devicePageButtons);
|
||||
break;
|
||||
case IAQ_PAGE_DEVICES2:
|
||||
//LOG(IAQT_LOG,LOG_INFO, "Devices Page #2:-\n");
|
||||
debugPrintButtons(_devicePage2Buttons);
|
||||
case IAQ_PAGE_DEVICES3:
|
||||
if (_currentPage == IAQ_PAGE_DEVICES)
|
||||
dp = 0;
|
||||
else if (_currentPage == IAQ_PAGE_DEVICES2)
|
||||
dp = 1;
|
||||
else if (_currentPage == IAQ_PAGE_DEVICES3)
|
||||
dp = 2;
|
||||
//LOG(IAQT_LOG,LOG_INFO, "Devices Page #1:-\n");
|
||||
debugPrintButtons(_devicePageButtons[dp]);
|
||||
|
||||
// If Button 15 has type 0x02 then we have previous, if 0x00 nothing (previous send code KEY_IAQTCH_PREV_PAGE)
|
||||
// If Button 16 has type 0x03 then we have next, if 0x00 nothing (next send code KEY_IAQTCH_NEXT_PAGE)
|
||||
if ( (isPDA_PANEL || PANEL_SIZE() >= 16) && !in_iaqt_programming_mode(aq_data) ) {
|
||||
if (_devicePageButtons[dp][16].type == 0x03) {
|
||||
iaqt_queue_cmd(KEY_IAQTCH_NEXT_PAGE);
|
||||
} else {
|
||||
iaqt_queue_cmd(KEY_IAQTCH_STATUS);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IAQ_PAGE_COLOR_LIGHT:
|
||||
//LOG(IAQT_LOG,LOG_INFO, "Color Light Page :-\n");
|
||||
|
@ -491,15 +615,68 @@ void processPage(struct aqualinkdata *aq_data)
|
|||
if (_deviceStatus[i][0] != 0)
|
||||
LOG(IAQT_LOG,LOG_INFO, "Status page %.2d| %s\n",i,_deviceStatus[i]);
|
||||
|
||||
debugPrintButtons(_pageButtons);
|
||||
for (i=0; i <IAQ_STATUS_PAGE_LINES; i++ )
|
||||
if (_homeStatus[i][0] != 0)
|
||||
LOG(IAQT_LOG,LOG_INFO, "Home Status page %.2d| %s\n",i,_homeStatus[i]);
|
||||
|
||||
//Info: iAQ Touch: Home Status page 00| 65
|
||||
//Info: iAQ Touch: Home Status page 01| 72
|
||||
//Info: iAQ Touch: Home Status page 04| Spa Temp
|
||||
//Info: iAQ Touch: Home Status page 05| Air Temp
|
||||
if (isPDA_PANEL) { // Set temp if PDA panel
|
||||
if (rsm_strcmp(_homeStatus[5],"Air Temp") == 0) {
|
||||
aq_data->air_temp = atoi(_homeStatus[1]);
|
||||
LOG(IAQT_LOG,LOG_DEBUG, "Air Temp set to %d\n",aq_data->air_temp);
|
||||
aq_data->updated = true;
|
||||
}
|
||||
if (rsm_strcmp(_homeStatus[4],"Pool Temp") == 0) {
|
||||
aq_data->pool_temp = atoi(_homeStatus[0]);
|
||||
LOG(IAQT_LOG,LOG_DEBUG, "Pool Temp set to %d\n",aq_data->air_temp);
|
||||
aq_data->updated = true;
|
||||
} else if (rsm_strcmp(_homeStatus[4],"Spa Temp") == 0) {
|
||||
aq_data->spa_temp = atoi(_homeStatus[0]);
|
||||
LOG(IAQT_LOG,LOG_DEBUG, "Spa Temp set to %d\n",aq_data->spa_temp);
|
||||
aq_data->updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
//passHomePage(aq_data);
|
||||
debugPrintButtons(_homeButtons);
|
||||
break;
|
||||
case IAQ_PAGE_SYSTEM_SETUP:
|
||||
//LOG(IAQT_LOG,LOG_INFO, "System Setup :-\n");
|
||||
debugPrintButtons(_deviceSystemSetupButtons);
|
||||
debugPrintButtons(_deviceSystemSetupButtons[0]);
|
||||
break;
|
||||
case IAQ_PAGE_SYSTEM_SETUP2:
|
||||
//LOG(IAQT_LOG,LOG_INFO, "System Setup :-\n");
|
||||
debugPrintButtons(_deviceSystemSetupButtons[1]);
|
||||
break;
|
||||
case IAQ_PAGE_SYSTEM_SETUP3:
|
||||
//LOG(IAQT_LOG,LOG_INFO, "System Setup :-\n");
|
||||
debugPrintButtons(_deviceSystemSetupButtons[2]);
|
||||
break;
|
||||
case IAQ_PAGE_SET_VSP:
|
||||
debugPrintButtons(_pageButtons);
|
||||
break;
|
||||
case IAQ_PAGE_HELP:
|
||||
/*
|
||||
Info: iAQ Touch: Table Messages 01| Interface: AquaLink Touch
|
||||
Info: iAQ Touch: Table Messages 02| Model: RS-8 Combo
|
||||
Info: iAQ Touch: Table Messages 03| AquaLink: REV T.0.1
|
||||
Info: iAQ Touch: Table Messages 04| CPU p/n: B0029221
|
||||
Info: iAQ Touch: Table Messages 05| TL Rev:
|
||||
*/
|
||||
if (isPDA_PANEL && ((char *)_tableInformation[03]) > 0) {
|
||||
if ( rsm_get_revision(aq_data->revision,(char *)_tableInformation[3], sizeof(aq_data->revision) ) == TRUE) {
|
||||
int len = rsm_get_boardcpu(aq_data->version, sizeof(aq_data->version), (char *)_tableInformation[4], IAQT_TABLE_MSGLEN );
|
||||
sprintf(aq_data->version+len, " REV %s",aq_data->revision);
|
||||
LOG(IAQT_LOG,LOG_NOTICE, "Control Panel revision %s\n", aq_data->revision);
|
||||
LOG(IAQT_LOG,LOG_NOTICE, "Control Panel version %s\n", aq_data->version);
|
||||
aq_data->updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
//LOG(IAQT_LOG,LOG_INFO, "** UNKNOWN PAGE 0x%02hhx **\n",_currentPage);
|
||||
|
@ -521,11 +698,27 @@ void processPage(struct aqualinkdata *aq_data)
|
|||
|
||||
bool process_iaqtouch_packet(unsigned char *packet, int length, struct aqualinkdata *aq_data)
|
||||
{
|
||||
static bool gotInit = false;
|
||||
static int cnt = 0;
|
||||
static bool gotStatus = true;
|
||||
static char message[AQ_MSGLONGLEN + 1];
|
||||
bool fake_pageend = false;
|
||||
//char buff[1024];
|
||||
// NSF Take this out
|
||||
|
||||
if ( packet[3] != CMD_IAQ_POLL && getLogLevel(IAQT_LOG) >= LOG_DEBUG ) {
|
||||
//if ( getLogLevel(IAQT_LOG) >= LOG_DEBUG ) {
|
||||
char buff[1000];
|
||||
beautifyPacket(buff, packet, length, false);
|
||||
LOG(IAQT_LOG,LOG_DEBUG, "Received message : %s", buff);
|
||||
}
|
||||
|
||||
|
||||
if (packet[PKT_CMD] == CMD_IAQ_PAGE_START) {
|
||||
// Reset and messages on new page
|
||||
aq_data->last_display_message[0] = ' ';
|
||||
aq_data->last_display_message[1] = '\0';
|
||||
aq_data->is_display_message_programming = false;
|
||||
LOG(IAQT_LOG,LOG_DEBUG, "Turning IAQ SEND off\n");
|
||||
set_iaq_cansend(false);
|
||||
_currentPageLoading = packet[PKT_IAQT_PAGTYPE];
|
||||
|
@ -533,6 +726,7 @@ bool process_iaqtouch_packet(unsigned char *packet, int length, struct aqualinkd
|
|||
memset(_pageButtons, 0, IAQ_PAGE_BUTTONS * sizeof(struct iaqt_page_button));
|
||||
memset(_deviceStatus, 0, sizeof(char) * IAQ_STATUS_PAGE_LINES * AQ_MSGLEN+1 );
|
||||
memset(_tableInformation, 0, sizeof(char) * IAQ_MSG_TABLE_LINES * AQ_MSGLEN+1 );
|
||||
//memset(_devicePageButtons, 0, IAQ_PAGE_BUTTONS * sizeof(struct iaqt_page_button));
|
||||
// Fix bug with control panel where after a few hours status page disapears and you need to hit menu.
|
||||
if (gotStatus == false)
|
||||
gotStatus = true;
|
||||
|
@ -542,17 +736,32 @@ bool process_iaqtouch_packet(unsigned char *packet, int length, struct aqualinkd
|
|||
LOG(IAQT_LOG,LOG_DEBUG, "Turning IAQ SEND on\n");
|
||||
if (_currentPageLoading != NUL) {
|
||||
_currentPage = _currentPageLoading;
|
||||
_currentPageLoading = NUL;
|
||||
//_currentPageLoading = NUL;
|
||||
} else {
|
||||
LOG(IAQT_LOG,LOG_DEBUG, "Page end message without proceding page start, ignoring!\n");
|
||||
}
|
||||
|
||||
if (isPDA_PANEL) {
|
||||
// Time is in the page end command
|
||||
// 1/18/2011 13:42
|
||||
//Hex |0x10|0x02|0x33|0x28|0x01|0x12|0x0b|0x0d|0x2a|0xc2|0x10|0x03|
|
||||
//Dec | 16| 2| 51| 40| 1| 18| 11| 13| 42| 194| 16| 3
|
||||
//Ascii | | | 3| (| | | | | *| | |
|
||||
snprintf(aq_data->date, sizeof(aq_data->date), "%02d/%02d/%02d", packet[4],packet[5],packet[6]);
|
||||
if (packet[7] <= 12)
|
||||
snprintf(aq_data->time, sizeof(aq_data->date), "%d:%02d am", packet[7],packet[8]);
|
||||
else
|
||||
snprintf(aq_data->time, sizeof(aq_data->date), "%d:%02d pm", (packet[7] - 12),packet[8]);
|
||||
}
|
||||
|
||||
processPage(aq_data);
|
||||
|
||||
} else if (packet[PKT_CMD] == CMD_IAQ_TABLE_MSG) {
|
||||
processTableMessage(packet, length);
|
||||
} else if (packet[PKT_CMD] == CMD_IAQ_PAGE_MSG) {
|
||||
processPageMessage(packet, length);
|
||||
} else if (packet[PKT_CMD] == CMD_IAQ_PAGE_BUTTON) {
|
||||
processPageButton(packet, length);
|
||||
processPageButton(packet, length, aq_data);
|
||||
// Second page on status doesn't send start & end, but button is message, so use that to kick off next page.
|
||||
if (_currentPage == IAQ_PAGE_STATUS) {
|
||||
/* Notice: Added Button 1
|
||||
|
@ -567,32 +776,71 @@ bool process_iaqtouch_packet(unsigned char *packet, int length, struct aqualinkd
|
|||
|
||||
// if we get a button with 0x00 state on Light Page, that's the end of page.
|
||||
if (_currentPageLoading == IAQ_PAGE_COLOR_LIGHT) {
|
||||
if (packet[7] == 0x00) {
|
||||
if (packet[7] == 0x00 && packet[4] == 0x0e) { // packet[4] is button number 0x0e is 14 (last button)
|
||||
//printf("** MANUAL PAGE END\n");
|
||||
LOG(IAQT_LOG,LOG_DEBUG, "MANUAL PAGE END\n");
|
||||
_currentPage = _currentPageLoading;
|
||||
_currentPageLoading = NUL;
|
||||
//_currentPageLoading = NUL;
|
||||
processPage(aq_data);
|
||||
set_iaq_cansend(true);
|
||||
// For programming mode
|
||||
fake_pageend = true;
|
||||
// Also END page here, as you can send commands.
|
||||
// NEED to rethink this approach. ie, selecting light needs to hold open while showing page, no page end, then select light color, then message "please wait", the finally done
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (isPDA_PANEL && packet[PKT_CMD] == CMD_IAQ_MSG_LONG) {
|
||||
char *sp;
|
||||
// Set disply message if PDA panel
|
||||
memset(message, 0, AQ_MSGLONGLEN + 1);
|
||||
rsm_strncpy(message, packet + 6, AQ_MSGLONGLEN, length-9);
|
||||
LOG(IAQT_LOG,LOG_NOTICE, "Popup message '%s'\n",message);
|
||||
|
||||
// Change this message, since you can't press OK. 'Light will turn off in 5 seconds. To change colors press Ok now.'
|
||||
if ((sp = rsm_strncasestr(message, "To change colors press Ok now", strlen(message))) != NULL)
|
||||
{
|
||||
*sp = '\0';
|
||||
}
|
||||
|
||||
strcpy(aq_data->last_display_message, message); // Also display the message on web UI
|
||||
|
||||
if (in_programming_mode(aq_data)) {
|
||||
aq_data->is_display_message_programming = true;
|
||||
} else {
|
||||
aq_data->is_display_message_programming = false;
|
||||
}
|
||||
/*
|
||||
for(int i=0; i<length; i++) {
|
||||
printf("0x%02hhx|",packet[i]);
|
||||
}
|
||||
printf("\n");
|
||||
*/
|
||||
}
|
||||
|
||||
if (packet[3] == 0x29) {
|
||||
//printf("***** iAqualink Touch STARTUP Message ******* \n");
|
||||
LOG(IAQT_LOG,LOG_DEBUG, "STARTUP Message\n");
|
||||
queueGetProgramData(IAQTOUCH, aq_data);
|
||||
if (gotInit == false) {
|
||||
LOG(IAQT_LOG,LOG_DEBUG, "STARTUP Message\n");
|
||||
//LOG(IAQT_LOG,LOG_ERR, "STARTUP REMOVED GET PANEL DATA FOR TESTING\n");
|
||||
queueGetProgramData(IAQTOUCH, aq_data);
|
||||
gotInit = true;
|
||||
}
|
||||
|
||||
//aq_programmer(AQ_SET_IAQTOUCH_SET_TIME, NULL, aq_data);
|
||||
}
|
||||
// Standard ack/poll not interested in printing or kicking threads
|
||||
if (packet[3] == 0x30) {
|
||||
/*
|
||||
NEED TO ALTERNATE SEND KEY_IAQTCH_HOMEP_KEY08 KEY and KEY_IAQTCH_STATUS BELOW FOR PDA
|
||||
*/
|
||||
// Standard ack/poll
|
||||
if (packet[3] == CMD_IAQ_POLL) {
|
||||
//LOG(IAQT_LOG,LOG_DEBUG, "poll count %d\n",cnt);
|
||||
// Load status page every 50 messages
|
||||
if (cnt++ > REQUEST_STATUS_POLL_COUNT && in_programming_mode(aq_data) == false ) {
|
||||
iaqt_queue_cmd(KEY_IAQTCH_STATUS);
|
||||
if (isPDA_PANEL || PANEL_SIZE() >= 16) {
|
||||
iaqt_queue_cmd(KEY_IAQTCH_HOMEP_KEY08);
|
||||
} else {
|
||||
iaqt_queue_cmd(KEY_IAQTCH_STATUS);
|
||||
}
|
||||
gotStatus = false; // Reset if we got status page, for fix panel bug.
|
||||
//aq_programmer(AQ_GET_IAQTOUCH_VSP_ASSIGNMENT, NULL, aq_data);
|
||||
cnt = 0;
|
||||
|
@ -600,21 +848,32 @@ bool process_iaqtouch_packet(unsigned char *packet, int length, struct aqualinkd
|
|||
// Fix bug with control panel where after a few hours status page disapears and you need to hit menu.
|
||||
LOG(IAQT_LOG,LOG_INFO, "Overcomming Jandy control panel bug, (missing status, goto menu)\n",cnt);
|
||||
iaqt_queue_cmd(KEY_IAQTCH_HOME);
|
||||
iaqt_queue_cmd(KEY_IAQTCH_STATUS);
|
||||
cnt = REQUEST_STATUS_POLL_COUNT - 5;
|
||||
/*
|
||||
if (isPDA_PANEL) {
|
||||
iaqt_queue_cmd(KEY_IAQTCH_HOMEP_KEY08);
|
||||
} else {
|
||||
iaqt_queue_cmd(KEY_IAQTCH_STATUS);
|
||||
}
|
||||
*/
|
||||
} else if (in_programming_mode(aq_data) == true) {
|
||||
// Set count to something close to above, so we will pull latest info once programming has finished.
|
||||
// This is goot for VSP GPM programming as it takes number of seconds to register once finished programming.
|
||||
// -5 seems to be too quick for VSP/GPM so using 10
|
||||
cnt = REQUEST_STATUS_POLL_COUNT - 10;
|
||||
}
|
||||
|
||||
// On poll no need to kick programming threads
|
||||
return false;
|
||||
}
|
||||
|
||||
//debuglogPacket(IAQT_LOG ,packet, length);
|
||||
|
||||
//_lastMsgType = packet[PKT_CMD];
|
||||
set_iaqtouch_lastmsg(packet[PKT_CMD]);
|
||||
if (fake_pageend){
|
||||
set_iaqtouch_lastmsg(CMD_IAQ_PAGE_END);
|
||||
} else {
|
||||
set_iaqtouch_lastmsg(packet[PKT_CMD]);
|
||||
}
|
||||
//debuglogPacket(IAQT_LOG ,packet, length);
|
||||
//beautifyPacket(buff, packet, length);
|
||||
//LOG(IAQT_LOG,LOG_DEBUG, "%s", buff);
|
||||
|
@ -623,12 +882,15 @@ bool process_iaqtouch_packet(unsigned char *packet, int length, struct aqualinkd
|
|||
|
||||
kick_aq_program_thread(aq_data, IAQTOUCH);
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
//char _namebuf[40];
|
||||
|
||||
const char *iaqt_page_name(const unsigned char page)
|
||||
{
|
||||
static char _namebuf[40];
|
||||
|
||||
switch (page){
|
||||
case IAQ_PAGE_HOME:
|
||||
return "HOME";
|
||||
|
@ -645,6 +907,9 @@ const char *iaqt_page_name(const unsigned char page)
|
|||
case IAQ_PAGE_DEVICES2:
|
||||
return "Devices #2";
|
||||
break;
|
||||
case IAQ_PAGE_DEVICES3:
|
||||
return "Devices #2";
|
||||
break;
|
||||
case IAQ_PAGE_SET_TEMP:
|
||||
return "Set Temp";
|
||||
break;
|
||||
|
@ -678,6 +943,12 @@ const char *iaqt_page_name(const unsigned char page)
|
|||
case IAQ_PAGE_SYSTEM_SETUP:
|
||||
return "System Setup";
|
||||
break;
|
||||
case IAQ_PAGE_SYSTEM_SETUP2:
|
||||
return "System Setup #2";
|
||||
break;
|
||||
case IAQ_PAGE_SYSTEM_SETUP3:
|
||||
return "System Setup #3";
|
||||
break;
|
||||
case IAQ_PAGE_VSP_SETUP:
|
||||
return "VSP Setup";
|
||||
break;
|
||||
|
@ -687,8 +958,13 @@ const char *iaqt_page_name(const unsigned char page)
|
|||
case IAQ_PAGE_LABEL_AUX:
|
||||
return "Label Aux";
|
||||
break;
|
||||
case IAQ_PAGE_HELP:
|
||||
return "Help Page";
|
||||
break;
|
||||
default:
|
||||
return "** Unknown **";
|
||||
sprintf (_namebuf,"** Unknown 0x%02hhx **",page);
|
||||
return _namebuf;
|
||||
//return "** Unknown **";
|
||||
break;
|
||||
}
|
||||
return "";
|
||||
|
@ -921,4 +1197,4 @@ Notice: Jandy Packet | HEX: 0x10|0x02|0x31|0x24|0x01|0x00|0x00|0x02|0x00|0x00
|
|||
Button | 1 | 0x02 | |-| |-| -off-
|
||||
|
||||
|
||||
*/
|
||||
*/
|
||||
|
|
|
@ -15,8 +15,9 @@ struct iaqt_page_button {
|
|||
bool process_iaqtouch_packet(unsigned char *packet, int length, struct aqualinkdata *aq_data);
|
||||
unsigned char iaqtThreadKickType();
|
||||
unsigned char iaqtCurrentPage();
|
||||
unsigned char iaqtCurrentPageLoading();
|
||||
bool wasiaqtThreadKickTypePage();
|
||||
struct iaqt_page_button *iaqtFindButtonByLabel(char *label);
|
||||
struct iaqt_page_button *iaqtFindButtonByLabel(const char *label);
|
||||
struct iaqt_page_button *iaqtFindButtonByIndex(int index);
|
||||
const char *iaqtGetMessageLine(int index);
|
||||
const char *iaqtGetTableInfoLine(int index);
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "config.h"
|
||||
#include "devices_jandy.h"
|
||||
#include "packetLogger.h"
|
||||
#include "color_lights.h"
|
||||
|
||||
// System Page is obfiously fixed and not dynamic loaded, so set buttons to stop confustion.
|
||||
|
||||
|
@ -98,6 +99,12 @@ void waitfor_iaqt_queue2empty()
|
|||
delay(PROGRAMMING_POLL_DELAY_TIME);
|
||||
}
|
||||
|
||||
// Initial startup can take some time, _cansend should be false during this time.
|
||||
// If we start programming before we receive the first status page, nothing works, this forces that wait
|
||||
while(_cansend == false) {
|
||||
delay(PROGRAMMING_POLL_DELAY_TIME * 2);
|
||||
}
|
||||
|
||||
if (_iaqt_pgm_command != NUL) {
|
||||
// Wait for longer interval
|
||||
while ( (_iaqt_pgm_command != NUL) && ( i++ < PROGRAMMING_POLL_COUNTER * 2 ) ) {
|
||||
|
@ -167,9 +174,64 @@ bool waitfor_iaqt_ctrl_queue2empty()
|
|||
}
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
unsigned const char _waitfor_iaqt_nextPage(struct aqualinkdata *aq_data, int numMessageReceived) {
|
||||
|
||||
waitfor_iaqt_queue2empty();
|
||||
|
||||
int i=0;
|
||||
|
||||
pthread_mutex_lock(&aq_data->active_thread.thread_mutex);
|
||||
|
||||
while( ++i <= numMessageReceived)
|
||||
{
|
||||
//LOG(IAQT_LOG,LOG_DEBUG, "waitfor_iaqt_nextPage (%d of %d)\n",i,numMessageReceived);
|
||||
pthread_cond_wait(&aq_data->active_thread.thread_cond, &aq_data->active_thread.thread_mutex);
|
||||
if(wasiaqtThreadKickTypePage()) break;
|
||||
}
|
||||
|
||||
LOG(IAQT_LOG,LOG_DEBUG, "waitfor_iaqt_nextPage finished in (%d of %d)\n",i,numMessageReceived);
|
||||
|
||||
pthread_mutex_unlock(&aq_data->active_thread.thread_mutex);
|
||||
|
||||
if(wasiaqtThreadKickTypePage())
|
||||
return iaqtCurrentPage();
|
||||
else
|
||||
return NUL;
|
||||
|
||||
}
|
||||
|
||||
unsigned const char shortwaitfor_iaqt_nextPage(struct aqualinkdata *aq_data) {
|
||||
return _waitfor_iaqt_nextPage(aq_data, 3);
|
||||
}
|
||||
*/
|
||||
unsigned const char waitfor_iaqt_messages(struct aqualinkdata *aq_data, int numMessageReceived) {
|
||||
//return _waitfor_iaqt_nextPage(aq_data, 30);
|
||||
|
||||
waitfor_iaqt_queue2empty();
|
||||
|
||||
int i=0;
|
||||
|
||||
LOG(IAQT_LOG,LOG_DEBUG, "waitfor_iaqt_messages (%d of %d)\n",i,numMessageReceived);
|
||||
|
||||
pthread_mutex_lock(&aq_data->active_thread.thread_mutex);
|
||||
|
||||
while( ++i <= numMessageReceived)
|
||||
{
|
||||
//LOG(IAQT_LOG,LOG_DEBUG, "waitfor_iaqt_nextPage (%d of %d)\n",i,numMessageReceived);
|
||||
pthread_cond_wait(&aq_data->active_thread.thread_cond, &aq_data->active_thread.thread_mutex);
|
||||
|
||||
}
|
||||
|
||||
LOG(IAQT_LOG,LOG_DEBUG, "waitfor_iaqt_messages finished in (%d of %d)\n",i,numMessageReceived);
|
||||
|
||||
pthread_mutex_unlock(&aq_data->active_thread.thread_mutex);
|
||||
|
||||
return iaqtLastMsg();
|
||||
}
|
||||
|
||||
unsigned const char waitfor_iaqt_nextPage(struct aqualinkdata *aq_data) {
|
||||
//return _waitfor_iaqt_nextPage(aq_data, 30);
|
||||
|
||||
waitfor_iaqt_queue2empty();
|
||||
|
||||
|
@ -193,9 +255,10 @@ unsigned const char waitfor_iaqt_nextPage(struct aqualinkdata *aq_data) {
|
|||
return iaqtCurrentPage();
|
||||
else
|
||||
return NUL;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
unsigned const char waitfor_iaqt_nextMessage(struct aqualinkdata *aq_data, const unsigned char msg_type) {
|
||||
|
||||
waitfor_iaqt_queue2empty();
|
||||
|
@ -317,7 +380,8 @@ bool goto_iaqt_page(const unsigned char pageID, struct aqualinkdata *aq_data) {
|
|||
LOG(IAQT_LOG, LOG_DEBUG, "IAQ Touch got to Device page\n");
|
||||
return true;
|
||||
} else if (pageID == IAQ_PAGE_MENU || pageID == IAQ_PAGE_SET_TEMP || pageID == IAQ_PAGE_SET_TIME || pageID == IAQ_PAGE_SET_SWG ||
|
||||
pageID == IAQ_PAGE_SYSTEM_SETUP || pageID == IAQ_PAGE_FREEZE_PROTECT || pageID == IAQ_PAGE_LABEL_AUX || pageID == IAQ_PAGE_VSP_SETUP) {
|
||||
pageID == IAQ_PAGE_SYSTEM_SETUP || pageID == IAQ_PAGE_FREEZE_PROTECT || pageID == IAQ_PAGE_LABEL_AUX ||
|
||||
pageID == IAQ_PAGE_VSP_SETUP) {
|
||||
// All other pages require us to go to Menu page
|
||||
send_aqt_cmd(KEY_IAQTCH_MENU);
|
||||
if (waitfor_iaqt_nextPage(aq_data) != IAQ_PAGE_MENU) {
|
||||
|
@ -388,8 +452,14 @@ bool goto_iaqt_page(const unsigned char pageID, struct aqualinkdata *aq_data) {
|
|||
|
||||
button = iaqtFindButtonByLabel(menuText);
|
||||
if (button == NULL) {
|
||||
LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did not find '%s' button on page setup\n", menuText);
|
||||
return false;
|
||||
//send_aqt_cmd(KEY_IAQTCH_NEXT_PAGE);
|
||||
// Try Next Page
|
||||
//unsigned char page = waitfor_iaqt_nextPage(aq_data);
|
||||
//LOG(IAQT_LOG, LOG_ERR, "PAGE RETURN IS 0x%02hhx\n",page);
|
||||
//if (waitfor_iaqt_nextPage(aq_data) != pageID) {
|
||||
LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did not find '%s' button on page setup\n", menuText);
|
||||
return false;
|
||||
//}
|
||||
}
|
||||
// send_aqt_cmd(KEY_IAQTCH_KEY01);
|
||||
send_aqt_cmd(button->keycode);
|
||||
|
@ -406,6 +476,207 @@ bool goto_iaqt_page(const unsigned char pageID, struct aqualinkdata *aq_data) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void *set_aqualink_iaqtouch_device_on_off( void *ptr )
|
||||
{
|
||||
struct programmingThreadCtrl *threadCtrl;
|
||||
threadCtrl = (struct programmingThreadCtrl *) ptr;
|
||||
struct aqualinkdata *aq_data = threadCtrl->aq_data;
|
||||
char *buf = (char*)threadCtrl->thread_args;
|
||||
//char device_name[15];
|
||||
struct iaqt_page_button *button;
|
||||
|
||||
unsigned int device = atoi(&buf[0]);
|
||||
unsigned int state = atoi(&buf[5]);
|
||||
|
||||
waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_IAQTOUCH_DEVICE_ON_OFF);
|
||||
|
||||
if (device > aq_data->total_buttons) {
|
||||
LOG(IAQT_LOG,LOG_ERR, "(PDA mode) Device On/Off :- bad device number '%d'\n",device);
|
||||
cleanAndTerminateThread(threadCtrl);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
LOG(IAQT_LOG,LOG_INFO, "PDA Device On/Off, device '%s', state %d\n",aq_data->aqbuttons[device].label,state);
|
||||
|
||||
// See if it's on the current page
|
||||
button = iaqtFindButtonByLabel(aq_data->aqbuttons[device].label);
|
||||
|
||||
if (button == NULL) {
|
||||
// No luck, go to the device page
|
||||
if ( goto_iaqt_page(IAQ_PAGE_DEVICES, aq_data) == false )
|
||||
goto f_end;
|
||||
|
||||
button = iaqtFindButtonByLabel(aq_data->aqbuttons[device].label);
|
||||
|
||||
// If not found see if page has next
|
||||
if (button == NULL && iaqtFindButtonByIndex(16)->type == 0x03 ) {
|
||||
iaqt_queue_cmd(KEY_IAQTCH_NEXT_PAGE);
|
||||
waitfor_iaqt_nextPage(aq_data);
|
||||
// This will fail, since not looking at device page 2 buttons
|
||||
button = iaqtFindButtonByLabel(aq_data->aqbuttons[device].label);
|
||||
}
|
||||
}
|
||||
|
||||
if (button == NULL) {
|
||||
LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did not find '%s' button on device list\n", aq_data->aqbuttons[device].label);
|
||||
goto f_end;
|
||||
}
|
||||
|
||||
send_aqt_cmd(button->keycode);
|
||||
//LOG(IAQT_LOG, LOG_ERR, "******* CURRENT MENU '0x%02hhx' *****\n",iaqtCurrentPage());
|
||||
// NSF NEED TO CHECK FOR POPUP MESSAGE, AND KILL IT.
|
||||
//page IAQ_PAGE_SET_TEMP hit button 0
|
||||
//page IAQ_PAGE_COLOR_LIGHT hit button ???????
|
||||
// Heater popup can be cleared with a home button and still turn on.
|
||||
// Color light can be cleared with a home button, but won;t turn on.
|
||||
|
||||
waitfor_iaqt_queue2empty();
|
||||
//waitfor_iaqt_messages(aq_data,1);
|
||||
|
||||
// OR maybe use waitfor_iaqt_messages(aq_data,1)
|
||||
|
||||
// Turn spa on when pump off, ned to remove message "spa will turn on after safty delay", home doesn't clear.
|
||||
send_aqt_cmd(KEY_IAQTCH_HOME);
|
||||
|
||||
// Turn spa off need to read message if heater is on AND hit ok......
|
||||
|
||||
f_end:
|
||||
goto_iaqt_page(IAQ_PAGE_HOME, aq_data);
|
||||
cleanAndTerminateThread(threadCtrl);
|
||||
|
||||
// just stop compiler error, ptr is not valid as it's just been freed
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *set_aqualink_iaqtouch_light_colormode( void *ptr )
|
||||
{
|
||||
struct programmingThreadCtrl *threadCtrl;
|
||||
threadCtrl = (struct programmingThreadCtrl *) ptr;
|
||||
struct aqualinkdata *aq_data = threadCtrl->aq_data;
|
||||
char *buf = (char*)threadCtrl->thread_args;
|
||||
//char device_name[15];
|
||||
struct iaqt_page_button *button;
|
||||
const char *mode_name;
|
||||
int val = atoi(&buf[0]);
|
||||
int btn = atoi(&buf[5]);
|
||||
int typ = atoi(&buf[10]);
|
||||
bool use_current_mode = false;
|
||||
bool turn_off = false;
|
||||
|
||||
waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_IAQTOUCH_LIGHTCOLOR_MODE);
|
||||
|
||||
//char *buf = (char*)threadCtrl->thread_args;
|
||||
|
||||
|
||||
if (btn < 0 || btn >= aq_data->total_buttons ) {
|
||||
LOG(IAQT_LOG, LOG_ERR, "Can't program light mode on button %d\n", btn);
|
||||
cleanAndTerminateThread(threadCtrl);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
aqkey *key = &aq_data->aqbuttons[btn];
|
||||
//unsigned char code = key->code;
|
||||
|
||||
// We also need to cater for light being ON AND changing the color mode. we have extra OK to hit.
|
||||
if (val == 0) {
|
||||
use_current_mode = true;
|
||||
LOG(IAQT_LOG, LOG_INFO, "Light Programming #: %d, button: %s, color light type: %d, using current mode\n", val, key->label, typ);
|
||||
// NOT SURE WHAT TO DO HERE..... No color mode and iaatouch doesn;t support last color in PDA mode.
|
||||
} else if (val == -1) {
|
||||
turn_off = true;
|
||||
LOG(IAQT_LOG, LOG_INFO, "Light Programming #: %d, button: %s, color light type: %d, Turning off\n", val, key->label, typ);
|
||||
} else {
|
||||
mode_name = light_mode_name(typ, val-1, IAQTOUCH);
|
||||
use_current_mode = false;
|
||||
if (mode_name == NULL) {
|
||||
LOG(IAQT_LOG, LOG_ERR, "Light Programming #: %d, button: %s, color light type: %d, couldn't find mode name '%s'\n", val, key->label, typ, mode_name);
|
||||
cleanAndTerminateThread(threadCtrl);
|
||||
return ptr;
|
||||
} else {
|
||||
LOG(IAQT_LOG, LOG_INFO, "Light Programming #: %d, button: %s, color light type: %d, name '%s'\n", val, key->label, typ, mode_name);
|
||||
}
|
||||
}
|
||||
|
||||
// See if it's on the current page
|
||||
button = iaqtFindButtonByLabel(key->label);
|
||||
|
||||
if (button == NULL) {
|
||||
// No luck, go to the device page
|
||||
if ( goto_iaqt_page(IAQ_PAGE_DEVICES, aq_data) == false )
|
||||
goto f_end;
|
||||
|
||||
button = iaqtFindButtonByLabel(key->label);
|
||||
|
||||
// If not found see if page has next
|
||||
if (button == NULL && iaqtFindButtonByIndex(16)->type == 0x03 ) {
|
||||
iaqt_queue_cmd(KEY_IAQTCH_NEXT_PAGE);
|
||||
waitfor_iaqt_nextPage(aq_data);
|
||||
// This will fail, since not looking at device page 2 buttons
|
||||
button = iaqtFindButtonByLabel(key->label);
|
||||
}
|
||||
}
|
||||
|
||||
if (button == NULL) {
|
||||
LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did not find '%s' button on device list\n", key->label);
|
||||
goto f_end;
|
||||
}
|
||||
// WE have a iaqualink button, press it.
|
||||
send_aqt_cmd(button->keycode);
|
||||
|
||||
// See if we want to use the last color, or turn it off
|
||||
if (use_current_mode || turn_off) {
|
||||
// After pressing the button, Just need to wait for 5 seconds and it will :-
|
||||
// a) if off turn on and default to last color.
|
||||
// b) if on, turn off. (pain that we need to wait 5 seconds.)
|
||||
waitfor_iaqt_queue2empty();
|
||||
waitfor_iaqt_nextPage(aq_data);
|
||||
if (use_current_mode) {
|
||||
// Their is no message for this, so give one.
|
||||
sprintf(aq_data->last_display_message, "Light will turn on in 5 seconds");
|
||||
aq_data->is_display_message_programming = true;
|
||||
aq_data->updated = true;
|
||||
}
|
||||
// Wait for next page maybe?
|
||||
// Below needs a timeout.
|
||||
while (waitfor_iaqt_nextPage(aq_data) == IAQ_PAGE_COLOR_LIGHT);
|
||||
goto f_end;
|
||||
}
|
||||
|
||||
// BELOW WE NEED TO CATER FOR OK POPUP IF LIGHT IS ALREADY ON
|
||||
if (button->state == 0x01) { // Light is on, need to select the BUTTON
|
||||
waitfor_iaqt_queue2empty();
|
||||
// We Should wait for popup message, before sending code
|
||||
send_aqt_cmd(KEY_IAQTCH_OK);
|
||||
}
|
||||
|
||||
if (waitfor_iaqt_nextPage(aq_data) != IAQ_PAGE_COLOR_LIGHT) {
|
||||
LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did not color light page\n");
|
||||
goto f_end;
|
||||
}
|
||||
|
||||
button = iaqtFindButtonByLabel(mode_name);
|
||||
|
||||
if (button == NULL) {
|
||||
LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did find color '%s' in color light page\n",mode_name);
|
||||
goto f_end;
|
||||
}
|
||||
|
||||
//LOG(IAQT_LOG, LOG_ERR, "IAQ Touch WAIYING FOR 1 MESSAGES\n");
|
||||
//waitfor_iaqt_messages(aq_data, 1);
|
||||
|
||||
// Finally found the color. select it
|
||||
send_aqt_cmd(button->keycode);
|
||||
waitfor_iaqt_nextPage(aq_data);
|
||||
|
||||
f_end:
|
||||
goto_iaqt_page(IAQ_PAGE_HOME, aq_data);
|
||||
cleanAndTerminateThread(threadCtrl);
|
||||
|
||||
// just stop compiler error, ptr is not valid as it's just been freed
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
void *set_aqualink_iaqtouch_pump_rpm( void *ptr )
|
||||
{
|
||||
struct programmingThreadCtrl *threadCtrl;
|
||||
|
@ -486,7 +757,9 @@ void *set_aqualink_iaqtouch_pump_rpm( void *ptr )
|
|||
//send_aqt_cmd(0x80);
|
||||
|
||||
// Go to status page on startup to read devices
|
||||
goto_iaqt_page(IAQ_PAGE_STATUS, aq_data);
|
||||
// This is too soon
|
||||
//goto_iaqt_page(IAQ_PAGE_STATUS, aq_data);
|
||||
//waitfor_iaqt_nextPage(aq_data);
|
||||
|
||||
f_end:
|
||||
goto_iaqt_page(IAQ_PAGE_HOME, aq_data);
|
||||
|
@ -587,6 +860,10 @@ void *get_aqualink_iaqtouch_setpoints( void *ptr )
|
|||
|
||||
waitForSingleThreadOrTerminate(threadCtrl, AQ_GET_IAQTOUCH_SETPOINTS);
|
||||
|
||||
// Get product info.
|
||||
send_aqt_cmd(KEY_IAQTCH_HELP);
|
||||
waitfor_iaqt_nextPage(aq_data);
|
||||
|
||||
if ( goto_iaqt_page(IAQ_PAGE_SET_TEMP, aq_data) == false )
|
||||
goto f_end;
|
||||
|
||||
|
@ -636,6 +913,40 @@ void *get_aqualink_iaqtouch_setpoints( void *ptr )
|
|||
LOG(IAQT_LOG,LOG_NOTICE, "IAQ Touch Freeze Protection setpoint %d\n",frz);
|
||||
}
|
||||
|
||||
// Get the temperature units if we are in iaq touch PDA mode
|
||||
if (isPDA_PANEL) {
|
||||
// If we are here, hit back then next button to get button with degrees on it.
|
||||
// Only if in PDA mode
|
||||
send_aqt_cmd(KEY_IAQTCH_BACK); // Clear the feeze protect menu and go back to system setup
|
||||
|
||||
if ( waitfor_iaqt_nextPage(aq_data) != IAQ_PAGE_SYSTEM_SETUP )
|
||||
{
|
||||
LOG(IAQT_LOG,LOG_ERR, "Couldn't get back to setup page, Temperature units unknown, default to DegF\n");
|
||||
aq_data->temp_units = FAHRENHEIT;
|
||||
goto f_end;
|
||||
}
|
||||
|
||||
send_aqt_cmd(KEY_IAQTCH_NEXT_PAGE_ALTERNATE); // Go to page 2
|
||||
|
||||
if ( waitfor_iaqt_nextPage(aq_data) != IAQ_PAGE_SYSTEM_SETUP2 )
|
||||
{
|
||||
LOG(IAQT_LOG,LOG_ERR, "Couldn't get back to setup page, Temperature units unknown, default to DegF\n");
|
||||
aq_data->temp_units = FAHRENHEIT;
|
||||
goto f_end;
|
||||
}
|
||||
|
||||
button = iaqtFindButtonByLabel("Degrees");
|
||||
|
||||
if (button != NULL) {
|
||||
LOG(IAQT_LOG,LOG_NOTICE, "Temperature units are '%s'\n",button->name);
|
||||
if (*button->name[8] == 'C') {
|
||||
aq_data->temp_units = CELSIUS;
|
||||
} else {
|
||||
aq_data->temp_units = FAHRENHEIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Need to run over table messages and check ens with X for on off.
|
||||
|
||||
// Go to status page on startup to read devices
|
||||
|
|
|
@ -19,6 +19,8 @@ void *set_aqualink_iaqtouch_spa_heater_temp( void *ptr );
|
|||
void *set_aqualink_iaqtouch_pool_heater_temp( void *ptr );
|
||||
void *set_aqualink_iaqtouch_time( void *ptr );
|
||||
void *set_aqualink_iaqtouch_pump_vs_program( void *ptr );
|
||||
void *set_aqualink_iaqtouch_light_colormode( void *ptr );
|
||||
void *set_aqualink_iaqtouch_device_on_off( void *ptr ); // For PDA only
|
||||
|
||||
int ref_iaqt_control_cmd(unsigned char **cmd);
|
||||
void rem_iaqt_control_cmd(unsigned char *cmd);
|
||||
|
|
|
@ -98,6 +98,11 @@ const char* _getStatus(struct aqualinkdata *aqdata, const char *blankmsg)
|
|||
return programtypeDisplayName(aqdata->active_thread.ptype);
|
||||
}
|
||||
*/
|
||||
if (aqdata->active_thread.thread_id != 0) {
|
||||
if (!aqdata->is_display_message_programming || rsm_isempy(aqdata->last_display_message,strlen(aqdata->last_display_message))){
|
||||
return programtypeDisplayName(aqdata->active_thread.ptype);
|
||||
}
|
||||
}
|
||||
|
||||
//if (aqdata->last_message != NULL && stristr(aqdata->last_message, "SERVICE") != NULL ) {
|
||||
if (aqdata->service_mode_state == ON) {
|
||||
|
@ -611,7 +616,7 @@ int build_aqualink_status_JSON(struct aqualinkdata *aqdata, char* buffer, int si
|
|||
if ( aqdata->orp != TEMP_UNKNOWN )
|
||||
length += sprintf(buffer+length, ",\"chem_orp\":\"%d\"",aqdata->orp );
|
||||
|
||||
if ( READ_RSDEV_SWG )
|
||||
//if ( READ_RSDEV_SWG )
|
||||
length += sprintf(buffer+length, ",\"swg_fullstatus\": \"%d\"", aqdata->ar_swg_device_status);
|
||||
|
||||
length += sprintf(buffer+length, ",\"leds\":{" );
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include "aq_scheduler.h"
|
||||
#include "rs_msg_utils.h"
|
||||
#include "simulator.h"
|
||||
#include "hassio.h"
|
||||
#include "version.h"
|
||||
|
||||
#ifdef AQ_PDA
|
||||
|
@ -685,14 +686,17 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
|
|||
const char *status;
|
||||
|
||||
if (_aqconfig_.mqtt_timed_update) {
|
||||
#ifdef AQ_NO_THREAD_NETSERVICE
|
||||
if (cnt > 300) { // 100 = about every 2 minutes.
|
||||
#else
|
||||
if (cnt > 30) { // 30 = about every 2 minutes.
|
||||
#endif
|
||||
reset_last_mqtt_status();
|
||||
cnt = 0;
|
||||
} else {
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//LOG(NET_LOG,LOG_INFO, "mqtt_broadcast_aqualinkstate: START\n");
|
||||
|
||||
|
@ -819,6 +823,12 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
|
|||
send_mqtt_int_msg(nc, SWG_BOOST_TOPIC, _aqualink_data->boost);
|
||||
_last_mqtt_aqualinkdata.boost = _aqualink_data->boost;
|
||||
}
|
||||
|
||||
if ( _aqualink_data->boost_duration != _last_mqtt_aqualinkdata.boost_duration ) {
|
||||
send_mqtt_int_msg(nc, SWG_BOOST_DURATION_TOPIC, _aqualink_data->boost_duration);
|
||||
_last_mqtt_aqualinkdata.boost_duration = _aqualink_data->boost_duration;
|
||||
}
|
||||
|
||||
} else {
|
||||
//LOG(NET_LOG,LOG_DEBUG, "SWG status unknown\n");
|
||||
}
|
||||
|
@ -831,9 +841,10 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
|
|||
int dzalert;
|
||||
|
||||
get_swg_status_mqtt(_aqualink_data, message, &status, &dzalert);
|
||||
send_domoticz_mqtt_status_message(nc, _aqconfig_.dzidx_swg_status, dzalert, &message[9]);
|
||||
|
||||
send_domoticz_mqtt_status_message(nc, _aqconfig_.dzidx_swg_status, dzalert, &message[9]);
|
||||
send_mqtt_int_msg(nc, SWG_EXTENDED_TOPIC, (int)_aqualink_data->ar_swg_device_status);
|
||||
send_mqtt_string_msg(nc, SWG_STATUS_MSG_TOPIC, message);
|
||||
|
||||
_last_mqtt_aqualinkdata.ar_swg_device_status = _aqualink_data->ar_swg_device_status;
|
||||
//LOG(NET_LOG,LOG_DEBUG, "SWG Extended sending cur=%d sent=%d\n",_aqualink_data->ar_swg_device_status,_last_mqtt_aqualinkdata.ar_swg_device_status);
|
||||
|
@ -844,6 +855,22 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
|
|||
//LOG(NET_LOG,LOG_DEBUG, "SWG Extended unknown\n");
|
||||
}
|
||||
|
||||
if (READ_RSDEV_JXI && _aqualink_data->heater_err_status != _last_mqtt_aqualinkdata.heater_err_status) {
|
||||
char message[30];
|
||||
|
||||
if (_aqualink_data->heater_err_status == NUL) {
|
||||
send_mqtt_int_msg(nc, LXI_ERROR_CODE, (int)_aqualink_data->heater_err_status);
|
||||
send_mqtt_string_msg(nc, LXI_ERROR_MESSAGE, "");
|
||||
} else {
|
||||
//send_mqtt_int_msg(nc, LXI_STATUS, (int)_aqualink_data->heater_err_status);
|
||||
send_mqtt_int_msg(nc, LXI_ERROR_CODE, (int)_aqualink_data->heater_err_status);
|
||||
getJandyHeaterErrorMQTT(_aqualink_data, message);
|
||||
send_mqtt_string_msg(nc, LXI_ERROR_MESSAGE, status);
|
||||
}
|
||||
|
||||
_last_mqtt_aqualinkdata.heater_err_status = _aqualink_data->heater_err_status;
|
||||
}
|
||||
|
||||
// LOG(NET_LOG,LOG_INFO, "mqtt_broadcast_aqualinkstate: START LEDs\n");
|
||||
|
||||
// if (time(NULL) % 2) {} <-- use to determin odd/even second in time to make state flash on enabled.
|
||||
|
@ -952,6 +979,7 @@ uriAtype action_URI(request_source from, const char *URI, int uri_length, float
|
|||
char *ri1 = (char *)URI;
|
||||
char *ri2 = NULL;
|
||||
char *ri3 = NULL;
|
||||
//bool charvalue=false;
|
||||
//char *ri4 = NULL;
|
||||
|
||||
LOG(NET_LOG,LOG_DEBUG, "%s: URI Request '%.*s': value %.2f\n", actionName[from], uri_length, URI, value);
|
||||
|
@ -1325,7 +1353,21 @@ void action_mqtt_message(struct mg_connection *nc, struct mg_mqtt_message *msg)
|
|||
strncpy(tmp, msg->payload.p, msg->payload.len);
|
||||
tmp[msg->payload.len] = '\0';
|
||||
|
||||
float value = atof(tmp);
|
||||
//float value = atof(tmp);
|
||||
|
||||
// Check value like on/off/heat/cool and convery to int.
|
||||
// HASSIO doesn't support `mode_command_template` so easier to code around their limotation here.
|
||||
char *end;
|
||||
float value = strtof(tmp, &end);
|
||||
if (tmp == end) { // Not a number
|
||||
// See if any test resembeling 1, of not leave at zero.
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
//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;
|
||||
|
@ -1821,6 +1863,8 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
|||
mg_mqtt_subscribe(nc, topics, 1, 42);
|
||||
LOG(NET_LOG,LOG_INFO, "MQTT: Subscribing to '%s'\n", _aqconfig_.mqtt_dz_sub_topic);
|
||||
}
|
||||
|
||||
publish_mqtt_hassio_discover( _aqualink_data, nc);
|
||||
}
|
||||
break;
|
||||
case MG_EV_MQTT_PUBACK:
|
||||
|
@ -1876,6 +1920,13 @@ void reset_last_mqtt_status()
|
|||
_last_mqtt_aqualinkdata.boost = -1;
|
||||
_last_mqtt_aqualinkdata.swg_percent = -1;
|
||||
_last_mqtt_aqualinkdata.swg_ppm = -1;
|
||||
_last_mqtt_aqualinkdata.heater_err_status = NUL; // 0x00
|
||||
|
||||
for (i=0; i < _aqualink_data->num_pumps; i++) {
|
||||
_last_mqtt_aqualinkdata.pumps[i].gpm = -1;
|
||||
_last_mqtt_aqualinkdata.pumps[i].rpm = -1;
|
||||
_last_mqtt_aqualinkdata.pumps[i].watts = -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,11 @@ void broadcast_aqualinkstate();
|
|||
void broadcast_aqualinkstate_error(char *msg);
|
||||
void broadcast_simulator_message();
|
||||
|
||||
|
||||
|
||||
// NSF Need to find a better way, this is not thread safe, so don;t like exposting it.
|
||||
//void send_mqtt(struct mg_connection *nc, const char *toppic, const char *message);
|
||||
|
||||
// superseded with systemd/sd-journal
|
||||
//void broadcast_log(char *msg);
|
||||
//#endif
|
||||
|
|
|
@ -589,8 +589,8 @@ bool log_qeuiptment_status_VP2(struct aqualinkdata *aq_data)
|
|||
} else if (PANEL_SIZE() >= 16 ) {
|
||||
// Loop over RS 16 buttons.
|
||||
get_RS16buttoninfo_from_menu(aq_data, i);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return rtn;
|
||||
|
|
1
pda.c
1
pda.c
|
@ -687,6 +687,7 @@ void process_pda_packet_msg_long_equiptment_status(const char *msg_line, int lin
|
|||
//snprintf(_aqualink_data->boost_msg, sizeof(_aqualink_data->boost_msg), "%s", msg+2);
|
||||
//Message is ' 23:21 Remain', we only want time part
|
||||
snprintf(_aqualink_data->boost_msg, 6, "%s", msg);
|
||||
_aqualink_data->boost_duration = rsm_HHMM2min(_aqualink_data->boost_msg);
|
||||
}
|
||||
else if ((index = rsm_strncasestr(msg, MSG_SWG_PCT, AQ_MSGLEN)) != NULL)
|
||||
{
|
||||
|
|
Binary file not shown.
|
@ -77,16 +77,22 @@ device_id=0x0a
|
|||
# If you have extended_device_id set, then you can also use that ID for programming some features.
|
||||
# This means that you can turn things on/off while AqualinkD is programming certian features.
|
||||
# If you are using Aqualink Touch protocol for extended_device_id then this is highly recomended
|
||||
# as it will speed up programming substantially. if One Touch it's 50/50.
|
||||
# as it will speed up programming substantially. if One Touch it's 50/50.
|
||||
#extended_device_id_programming = yes
|
||||
|
||||
# Read information from these devices directly from the RS485 bus as well as control panel.
|
||||
# swg = Salt Water Generator
|
||||
# ePump = Jandy ePump or ePump AC
|
||||
# vsfPump = Pentair VS,VF,VSF pump
|
||||
# JXi = Jandy JXi heater (might also be LXi heaters)
|
||||
# LX = Jandy LX & LT heaters
|
||||
# Chem = Jandy Chemical Feeder
|
||||
#read_RS485_swg = yes
|
||||
#read_RS485_ePump = yes
|
||||
#read_RS485_vsfPump = yes
|
||||
#read_RS485_JXi = yes
|
||||
#read_RS485_LX = yes
|
||||
#read_RS485_Chem = yes
|
||||
|
||||
# Keep the panel time synced with systemtime. Make sure to set systemtime / NTP correctly.
|
||||
keep_paneltime_synced = yes
|
||||
|
@ -164,6 +170,10 @@ force_PS_setpoints = no
|
|||
# to be listed as thermostat on startup.
|
||||
force_Frzprotect_setpoints = no
|
||||
|
||||
# AqualinkD can take sime time to find chemical feeder (if panel supports it), This will force the chemical feeder
|
||||
# to be listed on startup.
|
||||
force_chem_feeder = no
|
||||
|
||||
# Lights can be programmed by control panel or AqualinkD (if controlpanel doesn;t support specific light or light mode you want)
|
||||
# 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.
|
||||
|
|
|
@ -18,8 +18,8 @@ web_directory=/nas/data/Development/Raspberry/AqualinkD/web
|
|||
|
||||
#log_level=DEBUG_SERIAL
|
||||
#log_level=DEBUG
|
||||
log_level=INFO
|
||||
#log_level=NOTICE
|
||||
#log_level=INFO
|
||||
log_level=NOTICE
|
||||
|
||||
# AQUA_LOG 1
|
||||
# NET_LOG 2
|
||||
|
@ -40,15 +40,19 @@ log_level=INFO
|
|||
#debug_log_mask = 2
|
||||
#debug_log_mask = 4
|
||||
#debug_log_mask = 8
|
||||
#debug_log_mask = 16
|
||||
debug_log_mask = 16
|
||||
#debug_log_mask = 32
|
||||
#debug_log_mask = 64
|
||||
#debug_log_mask = 256
|
||||
#debug_log_mask = 512
|
||||
#debug_log_mask = 1024
|
||||
debug_log_mask = 1024
|
||||
#debug_log_mask = 2048
|
||||
#debug_log_mask = 4096
|
||||
|
||||
# Log any packets from this device. (less outpit that DEBUG_SERIAL)
|
||||
#debug_log_mask = 512 - MUST be set for this to work.
|
||||
RSSD_LOG_filter = 0x33
|
||||
|
||||
display_warnings_in_web = yes
|
||||
rs485_frame_delay = 4
|
||||
|
||||
|
@ -80,7 +84,8 @@ mqtt_aq_topic = aqualinkd-test
|
|||
#device_id=0x0a
|
||||
#device_id=0xFF # For testing one touch, don't use kaypad
|
||||
#device_id=0x00
|
||||
device_id=0x60
|
||||
#device_id=0x60
|
||||
device_id=0x33
|
||||
|
||||
#rssa_device_id=0x48
|
||||
|
||||
|
@ -89,7 +94,7 @@ device_id=0x60
|
|||
# Valid ID's are 0x40, 0x41, 0x42 & 0x43.
|
||||
# If you have a one touch remote do not use Ox40
|
||||
#extended_device_id=0x43
|
||||
#extended_device_id=0x31
|
||||
#extended_device_id=0x33
|
||||
|
||||
# If you have extended_device_id set, then you can also use that ID for programming some features.
|
||||
# This means that you can turn things on/off while AqualinkD is programming certian features.
|
||||
|
@ -227,7 +232,7 @@ button_01_pumpID=0x78
|
|||
#button_01_PDA_label=FILTER PUMP
|
||||
button_01_pumpIndex=1
|
||||
|
||||
button_02_label=Spa Mode
|
||||
button_02_label=Spa
|
||||
#button_02_dzidx=38
|
||||
#button_02_pumpID= 0x78
|
||||
#button_02_pumpIndex=3
|
||||
|
|
Binary file not shown.
162
rs_msg_utils.c
162
rs_msg_utils.c
|
@ -20,6 +20,8 @@
|
|||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <regex.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "rs_msg_utils.h"
|
||||
|
@ -83,6 +85,42 @@ bool rsm_get_revision(char *dest, const char *src, int src_len)
|
|||
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
pull board CPU from strings line
|
||||
' CPU p/n: B0029221'
|
||||
'B0029221 REV T.0.1'
|
||||
'E0260801 REV. O.2'
|
||||
*/
|
||||
int rsm_get_boardcpu(char *dest, int dest_len, const char *src, int src_len)
|
||||
{
|
||||
//char *regexString="/\\w\\d{4,10}/gi";
|
||||
//char *regexString="/[[:alpha:]][[:digit:]]{4,10}/gi";
|
||||
char *regexString="[[:alpha:]][[:digit:]]{4,10}";
|
||||
regex_t regexCompiled;
|
||||
regmatch_t match;
|
||||
int rc, begin, end, len;
|
||||
|
||||
if (0 != (rc = regcomp(®exCompiled, regexString, REG_EXTENDED))) {
|
||||
LOG(AQUA_LOG,LOG_ERR, "regcomp() failed, returning nonzero (%d)\n", rc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((regexec(®exCompiled,src,1,&match,0)) != 0) {
|
||||
regfree(®exCompiled);
|
||||
printf("********** ERROR didn;t line match \n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
begin = (int)match.rm_so;
|
||||
end = (int)match.rm_eo;
|
||||
len = AQ_MIN((end-begin), dest_len);
|
||||
|
||||
strncpy(dest, src+match.rm_so, len );
|
||||
|
||||
regfree(®exCompiled);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
Find first char after a space in haystack after searching needle.
|
||||
|
@ -110,6 +148,18 @@ char *rsm_charafterstr(const char *haystack, const char *needle, int length)
|
|||
return ++sp;
|
||||
}
|
||||
|
||||
/*
|
||||
Check if string has printable chars and is not empty
|
||||
*/
|
||||
bool rsm_isempy(const char *src, int length)
|
||||
{
|
||||
int i;
|
||||
for(i=0; i < length; i++) {
|
||||
if (src[i] > 32 && src[i] < 127) // 32 is space
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
Can probably replace this with rsm_strncasestr in all code.
|
||||
*/
|
||||
|
@ -193,9 +243,82 @@ int rsm_strcmp(const char *haystack, const char *needle)
|
|||
return -1;
|
||||
// Need to write this myself for speed
|
||||
//LOG(AQUA_LOG,LOG_DEBUG, "Compare (reset)%d chars of '%s' to '%s'\n",strlen(sp2),sp1,sp2);
|
||||
//printf("***** rsm_strcmp Compare (reset)%d chars of '%s' to '%s'\n",strlen(sp2),sp1,sp2);
|
||||
return strncasecmp(sp1, sp2, strlen(sp2));
|
||||
}
|
||||
|
||||
// Match two strings, used for button labels
|
||||
// exact character length once white space removed is used for match
|
||||
// use case insensative for match.
|
||||
// so 'spa' !- 'spa mode'
|
||||
int rsm_strmatch(const char *haystack, const char *needle)
|
||||
{
|
||||
return rsm_strmatch_ignore(haystack, needle, 0);
|
||||
/*
|
||||
char *sp1 = (char *)haystack;
|
||||
char *sp2 = (char *)needle;
|
||||
|
||||
char *ep1 = (char *)sp1 + strlen(sp1) - 1;
|
||||
char *ep2 = (char *)sp2 + strlen(sp2) - 1;
|
||||
//int i=0;
|
||||
// Get rid of all padding
|
||||
while(isspace(*sp1)) sp1++;
|
||||
while(isspace(*sp2)) sp2++;
|
||||
while(isspace(*ep1) && (ep1 >= sp1)) ep1--;
|
||||
while(isspace(*ep2) && (ep2 >= sp2)) ep2--;
|
||||
|
||||
int l1 = ep1 - sp1 +1;
|
||||
int l2 = ep2 - sp2 +1;
|
||||
|
||||
//printf("***** rsm_strmatch Compare %d chars of '%s' to %d chars in '%s'\n",l2,sp2,l1,sp1);
|
||||
|
||||
if ( l1 != l2 || (ep1 - sp1) <= 0 || (ep2 - sp2) <= 0 ) {
|
||||
return -1;
|
||||
}
|
||||
// Need to write this myself for speed
|
||||
//LOG(AQUA_LOG,LOG_DEBUG, "Compare (reset)%d chars of '%s' to '%s'\n",strlen(sp2),sp1,sp2);
|
||||
|
||||
return strncasecmp(sp1, sp2, l2);
|
||||
*/
|
||||
}
|
||||
|
||||
// Match two strings, used for button labels
|
||||
// exact character length once white space removed is used for match
|
||||
// ignore_chars will delete the last X chars from haystack.
|
||||
// use case insensative for match.
|
||||
// so 'spa' !- 'spa mode'
|
||||
int rsm_strmatch_ignore(const char *haystack, const char *needle, int ignore_chars)
|
||||
{
|
||||
char *sp1 = (char *)haystack;
|
||||
char *sp2 = (char *)needle;
|
||||
|
||||
char *ep1 = (char *)sp1 + strlen(sp1) - 1;
|
||||
char *ep2 = (char *)sp2 + strlen(sp2) - 1;
|
||||
//int i=0;
|
||||
// Get rid of all padding
|
||||
while(isspace(*sp1)) sp1++;
|
||||
while(isspace(*sp2)) sp2++;
|
||||
while(isspace(*ep2) && (ep2 >= sp2)) ep2--;
|
||||
if (ignore_chars > 0)
|
||||
ep1 = ep1 - ignore_chars;
|
||||
else
|
||||
while(isspace(*ep1) && (ep1 >= sp1)) ep1--;
|
||||
|
||||
|
||||
int l1 = ep1 - sp1 +1;
|
||||
int l2 = ep2 - sp2 +1;
|
||||
|
||||
//printf("***** %s() Compare %d chars of '%s' to %d chars in '%s'\n",(ignore_chars==0?"rsm_strmatch":"rsm_strmatch_ignore"),l2,sp2,l1,sp1);
|
||||
|
||||
if ( l1 != l2 || (ep1 - sp1) <= 0 || (ep2 - sp2) <= 0 ) {
|
||||
return -1;
|
||||
}
|
||||
// Need to write this myself for speed
|
||||
//LOG(AQUA_LOG,LOG_DEBUG, "Compare (reset)%d chars of '%s' to '%s'\n",strlen(sp2),sp1,sp2);
|
||||
|
||||
return strncasecmp(sp1, sp2, l2);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Find last index of char in string.
|
||||
|
@ -218,8 +341,7 @@ char *rsm_lastindexof(const char *haystack, const char *needle, size_t length)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
||||
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
||||
|
||||
|
||||
int rsm_strncmp(const char *haystack, const char *needle, int length)
|
||||
{
|
||||
|
@ -241,7 +363,27 @@ int rsm_strncmp(const char *haystack, const char *needle, int length)
|
|||
//LOG(AQUA_LOG,LOG_DEBUG, "CHECK haystack SP1='%c' EP1='%c' SP2='%c' '%.*s' for '%s' length=%d\n",*sp1,*ep1,*sp2,(ep1-sp1)+1,sp1,sp2,(ep1-sp1)+1);
|
||||
// Need to write this myself for speed
|
||||
// Need to check if full length string (no space on end), that the +1 is accurate. MIN should do it
|
||||
return strncasecmp(sp1, sp2, MIN((ep1-sp1)+1,length));
|
||||
return strncasecmp(sp1, sp2, AQ_MIN((ep1-sp1)+1,length));
|
||||
}
|
||||
|
||||
|
||||
char *rsm_char_replace(char *replaced , char *search, char *find, char *replace)
|
||||
{
|
||||
int len;
|
||||
int i;
|
||||
char *fp = find;
|
||||
char *rp = replace;
|
||||
|
||||
len = strlen(search);
|
||||
for(i = 0; i < len; i++){
|
||||
if (search[i] == *fp)
|
||||
replaced[i] = *rp;
|
||||
else
|
||||
replaced[i] = search[i];
|
||||
}
|
||||
replaced[i] = '\0';
|
||||
|
||||
return replaced;
|
||||
}
|
||||
|
||||
// NSF Check is this works correctly.
|
||||
|
@ -253,7 +395,7 @@ char *rsm_strncpycut(char *dest, const char *src, int dest_len, int src_len)
|
|||
while(isspace(*sp)) sp++;
|
||||
while(isspace(*ep)) ep--;
|
||||
|
||||
int length=MIN((ep-sp)+1,dest_len);
|
||||
int length=AQ_MIN((ep-sp)+1,dest_len);
|
||||
|
||||
memset(dest, '\0',dest_len);
|
||||
return strncpy(dest, sp, length);
|
||||
|
@ -304,8 +446,6 @@ int rsm_strncpy_nul2sp(char *dest, const unsigned char *src, int dest_len, int s
|
|||
return _rsm_strncpy(dest, src, dest_len, src_len, true);
|
||||
}
|
||||
|
||||
#define INT_MAX +2147483647
|
||||
#define INT_MIN -2147483647
|
||||
|
||||
// atoi that can have blank start
|
||||
int rsm_atoi(const char* str)
|
||||
|
@ -343,4 +483,14 @@ float rsm_atof(const char* str)
|
|||
}
|
||||
|
||||
return atof(&str[i]);
|
||||
}
|
||||
|
||||
// MEssages as HH:MM ie 01:23
|
||||
int rsm_HHMM2min(char *message) {
|
||||
char *ptr;
|
||||
|
||||
int hour = strtoul(message, &ptr, 10);
|
||||
int min = strtoul(message+3, &ptr, 10);
|
||||
|
||||
return (hour*60)+min;
|
||||
}
|
|
@ -1,16 +1,23 @@
|
|||
#ifndef RS_MSG_UTILS_H_
|
||||
#define RS_MSG_UTILS_H_
|
||||
|
||||
//#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
||||
|
||||
bool rsm_get_revision(char *dest, const char *src, int src_len);
|
||||
int rsm_get_boardcpu(char *dest, int dest_len, const char *src, int src_len);
|
||||
|
||||
char *rsm_charafterstr(const char *haystack, const char *needle, int length);
|
||||
|
||||
bool rsm_isempy(const char *src, int length);
|
||||
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_strmatch(const char *haystack, const char *needle);
|
||||
int rsm_strmatch_ignore(const char *haystack, const char *needle, int ignore_chars);
|
||||
|
||||
int rsm_strncpy(char *dest, const unsigned char *src, int dest_len, int src_len);
|
||||
int rsm_strcmp(const char *s1, const char *s2);
|
||||
int rsm_strncmp(const char *haystack, const char *needle, int length);
|
||||
|
@ -18,6 +25,8 @@ int rsm_strncpy_nul2sp(char *dest, const unsigned char *src, int dest_len, int s
|
|||
int rsm_atoi(const char* str);
|
||||
float rsm_atof(const char* str);
|
||||
char *rsm_strncpycut(char *dest, const char *src, int dest_len, int src_len);
|
||||
|
||||
//char *rsm_char_replace(char *replaced , char *search, const char find, const char replace);
|
||||
char *rsm_char_replace(char *replaced , char *search, char *find, char *replace);
|
||||
int rsm_HHMM2min(char *message);
|
||||
|
||||
#endif //RS_MSG_UTILS_H_
|
||||
|
|
|
@ -117,7 +117,7 @@ int serial_logger (int rs_fd, char *port_name, int logLevel) {
|
|||
#define PDA " <-- PDA Remote"
|
||||
#define EPUMP " <-- Jandy VSP ePump"
|
||||
#define CHEM " <-- Chemlink"
|
||||
#define LXI_LRZ_HEATER " <-- LXi / LRZ Heater"
|
||||
#define JXI_HEATER " <-- LXi / LRZ Heater"
|
||||
|
||||
#define UNKNOWN " <-- Unknown Device"
|
||||
|
||||
|
@ -151,7 +151,7 @@ const char *getDevice(unsigned char ID) {
|
|||
if (ID >= 0x60 && ID <= 0x63)
|
||||
return PDA;
|
||||
if (ID >= 0x68 && ID <= 0x6B)
|
||||
return LXI_LRZ_HEATER;
|
||||
return JXI_HEATER;
|
||||
//if (ID >= 0x70 && ID <= 0x73)
|
||||
if (ID >= 0x78 && ID <= 0x7B)
|
||||
return EPUMP;
|
||||
|
|
10
utils.h
10
utils.h
|
@ -47,6 +47,16 @@
|
|||
// Set scheduler log to timer log
|
||||
#define SCHD_LOG TIMR_LOG
|
||||
|
||||
|
||||
/*
|
||||
#define INT_MAX +2147483647
|
||||
#define INT_MIN -2147483647
|
||||
*/
|
||||
|
||||
#define AQ_MAX(x, y) (((x) > (y)) ? (x) : (y))
|
||||
#define AQ_MIN(x, y) (((x) < (y)) ? (x) : (y))
|
||||
|
||||
|
||||
/*
|
||||
typedef enum
|
||||
{
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
|
||||
#define AQUALINKD_NAME "Aqualink Daemon"
|
||||
#define AQUALINKD_VERSION "2.3.4"
|
||||
#define AQUALINKD_SHORT_NAME "AqualinkD"
|
||||
#define AQUALINKD_VERSION "2.3.5"
|
||||
|
|
|
@ -35,8 +35,8 @@
|
|||
"Spa_Water",
|
||||
"Freeze_Protect",
|
||||
"CHEM/pH",
|
||||
"CHEM/ORP"
|
||||
//"Solar_Heater",
|
||||
"CHEM/ORP",
|
||||
"Solar_Heater",
|
||||
];
|
||||
|
||||
// This get's picked up by dynamic_config.js and used as mode 0
|
||||
|
@ -72,6 +72,8 @@
|
|||
32: "Low volts",
|
||||
64: "Low temp",
|
||||
128: "Check PCB",
|
||||
253: "General Fault",
|
||||
254: "Unknown",
|
||||
255: "Off"
|
||||
}
|
||||
|
||||
|
|
|
@ -590,6 +590,21 @@
|
|||
var _scheduler_devices_json= [];
|
||||
|
||||
|
||||
var _swgALLStatus = {
|
||||
0: "On",
|
||||
1: "No flow",
|
||||
2: "Low salt",
|
||||
4: "High salt",
|
||||
8: "Clean cell",
|
||||
9: "Turning off",
|
||||
16: "High current",
|
||||
32: "Low volts",
|
||||
64: "Low temp",
|
||||
128: "Check PCB",
|
||||
253: "General Fault",
|
||||
254: "Unknown",
|
||||
255: "Off"
|
||||
}
|
||||
|
||||
if (typeof show_vsp_gpm !== 'undefined' && show_vsp_gpm == false)
|
||||
var _show_vsp_gpm=false;
|
||||
|
@ -605,19 +620,7 @@
|
|||
if (typeof swgStatus !== 'undefined') {
|
||||
var _swgStatus = swgStatus;
|
||||
} else {
|
||||
var _swgStatus = {
|
||||
0: "On",
|
||||
1: "No flow",
|
||||
2: "Low salt",
|
||||
4: "High salt",
|
||||
8: "Clean cell",
|
||||
9: "Turning off",
|
||||
16: "High current",
|
||||
32: "Low volts",
|
||||
64: "Low temp",
|
||||
128: "Check PCB",
|
||||
255: "Off"
|
||||
}
|
||||
var _swgStatus = _swgALLStatus
|
||||
}
|
||||
|
||||
function init() {
|
||||
|
@ -1100,6 +1103,9 @@
|
|||
if (exstatus > 0 && exstatus < 255) {// Not off or on
|
||||
//text = swgFullstatus2String(exstatus);
|
||||
text = _swgStatus[exstatus];
|
||||
if (typeof text == 'undefined') {
|
||||
text = _swgALLStatus[exstatus];
|
||||
}
|
||||
} else if (tile.getAttribute('Boost') == 'on')
|
||||
text = "Boost";
|
||||
else if (status == 'enabled')
|
||||
|
|
|
@ -1 +1 @@
|
|||
switch-off.png
|
||||
./switch-off.png
|
|
@ -1 +1 @@
|
|||
switch-on.png
|
||||
./switch-on.png
|
|
@ -1 +1 @@
|
|||
switch-on.png
|
||||
./switch-on.png
|
|
@ -1 +1 @@
|
|||
switch-off.png
|
||||
./switch-off.png
|
|
@ -1 +1 @@
|
|||
switch-on.png
|
||||
./switch-on.png
|
|
@ -1 +1 @@
|
|||
switch-off.png
|
||||
./switch-off.png
|
|
@ -1 +1 @@
|
|||
switch-on.png
|
||||
./switch-on.png
|
|
@ -1 +1 @@
|
|||
switch-off.png
|
||||
./switch-off.png
|
|
@ -1 +1 @@
|
|||
switch-on.png
|
||||
./switch-on.png
|
|
@ -1 +1 @@
|
|||
switch-off.png
|
||||
./switch-off.png
|
|
@ -1 +1 @@
|
|||
switch-on.png
|
||||
./switch-on.png
|
|
@ -1 +1 @@
|
|||
switch-off.png
|
||||
./switch-off.png
|
|
@ -1 +1 @@
|
|||
switch-on.png
|
||||
./switch-on.png
|
|
@ -1 +1 @@
|
|||
switch-off.png
|
||||
./switch-off.png
|
|
@ -1 +1 @@
|
|||
switch-on.png
|
||||
./switch-on.png
|
|
@ -1 +1 @@
|
|||
switch-off.png
|
||||
./switch-off.png
|
|
@ -1 +1 @@
|
|||
switch-on.png
|
||||
./switch-on.png
|
|
@ -1 +1 @@
|
|||
switch-off.png
|
||||
./switch-off.png
|
|
@ -1 +1 @@
|
|||
switch-on.png
|
||||
./switch-on.png
|
|
@ -1 +1 @@
|
|||
switch-off.png
|
||||
./switch-off.png
|
|
@ -1 +1 @@
|
|||
switch-on.png
|
||||
./switch-on.png
|
|
@ -1 +1 @@
|
|||
switch-off.png
|
||||
./switch-off.png
|
|
@ -1 +1 @@
|
|||
switch-on.png
|
||||
./switch-on.png
|
|
@ -1 +1 @@
|
|||
switch-off.png
|
||||
./switch-off.png
|
|
@ -1 +1 @@
|
|||
switch-on.png
|
||||
./switch-on.png
|
|
@ -1 +1 @@
|
|||
switch-off.png
|
||||
./switch-off.png
|
|
@ -1 +1 @@
|
|||
switch-on.png
|
||||
./switch-on.png
|
|
@ -1 +1 @@
|
|||
switch-off.png
|
||||
./switch-off.png
|
|
@ -1 +1 @@
|
|||
switch-on.png
|
||||
./switch-on.png
|
|
@ -1 +1 @@
|
|||
switch-off.png
|
||||
./switch-off.png
|
|
@ -1 +1 @@
|
|||
switch-on.png
|
||||
./switch-on.png
|
|
@ -1 +1 @@
|
|||
switch-off.png
|
||||
./switch-off.png
|
|
@ -1 +1 @@
|
|||
switch-on.png
|
||||
./switch-on.png
|
|
@ -1 +1 @@
|
|||
switch-off.png
|
||||
./switch-off.png
|
|
@ -1 +1 @@
|
|||
switch-on.png
|
||||
./switch-on.png
|
|
@ -1 +1 @@
|
|||
switch-off.png
|
||||
./switch-off.png
|
|
@ -1 +1 @@
|
|||
switch-on.png
|
||||
./switch-on.png
|
|
@ -1 +1 @@
|
|||
switch-off.png
|
||||
./switch-off.png
|
|
@ -1 +1 @@
|
|||
switch-on.png
|
||||
./switch-on.png
|
|
@ -1 +1 @@
|
|||
switch-off.png
|
||||
./switch-off.png
|
|
@ -1 +1 @@
|
|||
switch-on.png
|
||||
./switch-on.png
|
|
@ -1 +1 @@
|
|||
switch-off.png
|
||||
./switch-off.png
|
|
@ -1 +1 @@
|
|||
switch-on.png
|
||||
./switch-on.png
|
|
@ -1 +1 @@
|
|||
switch-off.png
|
||||
./switch-off.png
|
|
@ -1 +1 @@
|
|||
switch-on.png
|
||||
./switch-on.png
|
|
@ -421,13 +421,14 @@ console.log("SIM LOAD");
|
|||
status.classList.remove("error");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function update_status(data) {
|
||||
// Some form of error if PDA only panel.
|
||||
if (data.panel_type.startsWith("PDA")) {
|
||||
//document.getElementById("status").innerHTML = ' !!! PDA only panels are not Supported !!! '
|
||||
//document.getElementById("status").classList.add("error");
|
||||
update_status_message("PDA only panels are not Supported", true);
|
||||
update_status_message("Some PDA panels do not support OneTouch", true);
|
||||
}
|
||||
|
||||
const versionlabel = document.getElementById("version");
|
||||
|
|
Loading…
Reference in New Issue