mirror of https://github.com/sfeakes/AqualinkD.git
Update
parent
7c37a7cc23
commit
767745c97d
2
Makefile
2
Makefile
|
@ -77,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 =
|
||||
|
|
12
README.md
12
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
|
||||
|
@ -87,7 +87,6 @@ Designed to mimic AqualinkRS devices, used to fully configure the master control
|
|||
|
||||
# Update in Release 2.3.5
|
||||
* NEED TO FIX
|
||||
* Not saying programming mode
|
||||
* 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 ???)
|
||||
|
@ -98,6 +97,9 @@ Designed to mimic AqualinkRS devices, used to fully configure the master control
|
|||
* Panel version
|
||||
* can't use iaquatouch panel / wireless
|
||||
|
||||
* 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.
|
||||
* Added iAqualinkTouch support for PDA only panels that can use that protocol.
|
||||
|
@ -119,11 +121,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.
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#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"
|
||||
|
||||
|
@ -42,8 +43,8 @@
|
|||
|
||||
#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"
|
||||
|
|
|
@ -590,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);
|
||||
|
|
|
@ -298,8 +298,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
|
||||
|
|
|
@ -231,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;
|
||||
|
||||
|
|
18
aqualinkd.c
18
aqualinkd.c
|
@ -364,6 +364,7 @@ int16_t RS16_endswithLEDstate(char *msg)
|
|||
#endif
|
||||
|
||||
|
||||
|
||||
void _processMessage(char *message, bool reset);
|
||||
|
||||
void processMessage(char *message)
|
||||
|
@ -460,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;
|
||||
}
|
||||
|
@ -625,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);
|
||||
|
@ -736,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;
|
||||
|
@ -996,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.
|
||||
|
@ -1322,6 +1327,8 @@ 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));
|
||||
|
||||
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);
|
||||
|
@ -1541,6 +1548,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);
|
||||
|
@ -1558,6 +1567,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);
|
||||
|
|
14
config.c
14
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;
|
||||
|
@ -576,6 +587,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;
|
||||
|
|
3
config.h
3
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
|
||||
|
@ -51,6 +52,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 +81,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
|
||||
|
|
|
@ -218,6 +218,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;
|
||||
|
@ -225,6 +226,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);
|
||||
|
@ -232,10 +244,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);
|
||||
|
@ -255,6 +272,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;
|
||||
|
@ -398,6 +416,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;
|
||||
|
@ -471,6 +492,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");
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 198 KiB |
|
@ -0,0 +1,44 @@
|
|||
#!/bin/bash
|
||||
|
||||
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,436 @@
|
|||
|
||||
#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 "\""
|
||||
|
||||
|
||||
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"
|
||||
"}";
|
||||
|
||||
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 }}\""
|
||||
"}";
|
||||
|
||||
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
|
||||
|
||||
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"
|
||||
"}";
|
||||
|
||||
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\""
|
||||
"}";
|
||||
|
||||
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\""
|
||||
"}";
|
||||
|
||||
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\""
|
||||
"}";
|
||||
|
||||
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\""
|
||||
"}";
|
||||
|
||||
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\""
|
||||
"}";
|
||||
|
||||
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_
|
|
@ -306,7 +306,7 @@ void processPageButton(unsigned char *message, int length, struct aqualinkdata *
|
|||
else {
|
||||
button = &_pageButtons[index];
|
||||
// if _currentPageLoading = 0x00 then we should use current page
|
||||
LOG(IAQT_LOG,LOG_NOTICE, "Not sure where to add Button %d %s - LoadingPage = %s\n",index,button->name,iaqt_page_name(_currentPageLoading));
|
||||
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];
|
||||
|
|
|
@ -616,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
|
||||
|
@ -696,7 +697,6 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
|
|||
cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//LOG(NET_LOG,LOG_INFO, "mqtt_broadcast_aqualinkstate: START\n");
|
||||
|
||||
|
@ -823,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");
|
||||
}
|
||||
|
@ -973,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);
|
||||
|
@ -1346,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;
|
||||
|
@ -1842,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:
|
||||
|
@ -1899,6 +1922,12 @@ void reset_last_mqtt_status()
|
|||
_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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void start_mqtt(struct mg_mgr *mgr) {
|
||||
|
|
|
@ -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
|
||||
|
|
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.
|
@ -168,6 +168,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.
|
||||
|
|
Binary file not shown.
|
@ -21,6 +21,7 @@
|
|||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <regex.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "rs_msg_utils.h"
|
||||
|
@ -112,7 +113,7 @@ int rsm_get_boardcpu(char *dest, int dest_len, const char *src, int src_len)
|
|||
|
||||
begin = (int)match.rm_so;
|
||||
end = (int)match.rm_eo;
|
||||
len = MIN((end-begin), dest_len);
|
||||
len = AQ_MIN((end-begin), dest_len);
|
||||
|
||||
strncpy(dest, src+match.rm_so, len );
|
||||
|
||||
|
@ -340,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)
|
||||
{
|
||||
|
@ -363,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.
|
||||
|
@ -375,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);
|
||||
|
@ -426,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)
|
||||
|
@ -465,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,7 +1,7 @@
|
|||
#ifndef RS_MSG_UTILS_H_
|
||||
#define RS_MSG_UTILS_H_
|
||||
|
||||
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
||||
//#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);
|
||||
|
@ -25,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_
|
||||
|
|
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_SHORT_NAME "AqualinkD"
|
||||
#define AQUALINKD_VERSION "2.3.5"
|
||||
|
|
|
@ -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')
|
||||
|
|
Loading…
Reference in New Issue