From 2e6937e2dbb68a431d0555621987faa68596d391 Mon Sep 17 00:00:00 2001 From: Lee Ballard Date: Fri, 17 May 2019 22:42:30 -0500 Subject: [PATCH] ballle98/AqualinkD#17: Status sync into ha-bridge --- Makefile | 9 +-- aq_panel.c | 25 ++++++- aqualink.h | 1 + aqualinkd.c | 18 ++++- config.c | 18 ++++- config.h | 2 + net_services_habridge.c | 141 ++++++++++++++++++++++++++++++++++++++++ net_services_habridge.h | 18 +++++ release/aqualinkd.conf | 2 +- 9 files changed, 223 insertions(+), 11 deletions(-) create mode 100644 net_services_habridge.c create mode 100644 net_services_habridge.h diff --git a/Makefile b/Makefile index f63173b6..46eddff9 100755 --- a/Makefile +++ b/Makefile @@ -72,10 +72,11 @@ 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 - +SRCS = aqualinkd.c utils.c config.c aq_serial.c aq_panel.c aq_programmer.c \ + net_services.c net_services_habridge.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 AQ_FLAGS = # Add source and flags depending on protocols to support. diff --git a/aq_panel.c b/aq_panel.c index 520dad3b..6bc8b0a9 100644 --- a/aq_panel.c +++ b/aq_panel.c @@ -263,6 +263,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo aqdata->aqbuttons[index].name = BTN_PUMP; aqdata->aqbuttons[index].code = KEY_PUMP; aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX; + aqdata->aqbuttons[index].hab_id = 0; aqdata->aqbuttons[index].special_mask = 0; index++; @@ -273,6 +274,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo aqdata->aqbuttons[index].name = BTN_SPA; aqdata->aqbuttons[index].code = KEY_SPA; aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX; + aqdata->aqbuttons[index].hab_id = 0; aqdata->aqbuttons[index].special_mask = 0; index++; } @@ -283,6 +285,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo aqdata->aqbuttons[index].name = BTN_AUX1; aqdata->aqbuttons[index].code = KEY_AUX1; aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX; + aqdata->aqbuttons[index].hab_id = 0; aqdata->aqbuttons[index].special_mask = 0; index++; @@ -292,6 +295,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo aqdata->aqbuttons[index].name = BTN_AUX2; aqdata->aqbuttons[index].code = KEY_AUX2; aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX; + aqdata->aqbuttons[index].hab_id = 0; aqdata->aqbuttons[index].special_mask = 0; index++; @@ -301,6 +305,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo aqdata->aqbuttons[index].name = BTN_AUX3; aqdata->aqbuttons[index].code = KEY_AUX3; aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX; + aqdata->aqbuttons[index].hab_id = 0; aqdata->aqbuttons[index].special_mask = 0; index++; @@ -312,6 +317,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo aqdata->aqbuttons[index].name = BTN_AUX4; aqdata->aqbuttons[index].code = KEY_AUX4; aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX; + aqdata->aqbuttons[index].hab_id = 0; aqdata->aqbuttons[index].special_mask = 0; index++; @@ -321,6 +327,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo aqdata->aqbuttons[index].name = BTN_AUX5; aqdata->aqbuttons[index].code = KEY_AUX5; aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX; + aqdata->aqbuttons[index].hab_id = 0; aqdata->aqbuttons[index].special_mask = 0; index++; } @@ -332,6 +339,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo aqdata->aqbuttons[index].name = BTN_AUX6; aqdata->aqbuttons[index].code = KEY_AUX6; aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX; + aqdata->aqbuttons[index].hab_id = 0; aqdata->aqbuttons[index].special_mask = 0; index++; @@ -341,6 +349,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo aqdata->aqbuttons[index].name = BTN_AUX7; aqdata->aqbuttons[index].code = KEY_AUX7; aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX; + aqdata->aqbuttons[index].hab_id = 0; aqdata->aqbuttons[index].special_mask = 0; index++; } @@ -362,6 +371,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo aqdata->aqbuttons[index].name = BTN_AUXB1; aqdata->aqbuttons[index].code = KEY_AUXB1; aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX; + aqdata->aqbuttons[index].hab_id = 0; aqdata->aqbuttons[index].special_mask = 0; index++; @@ -371,6 +381,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo aqdata->aqbuttons[index].name = BTN_AUXB2; aqdata->aqbuttons[index].code = KEY_AUXB2; aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX; + aqdata->aqbuttons[index].hab_id = 0; aqdata->aqbuttons[index].special_mask = 0; index++; @@ -380,6 +391,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo aqdata->aqbuttons[index].name = BTN_AUXB3; aqdata->aqbuttons[index].code = KEY_AUXB3; aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX; + aqdata->aqbuttons[index].hab_id = 0; aqdata->aqbuttons[index].special_mask = 0; index++; @@ -389,6 +401,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo aqdata->aqbuttons[index].name = BTN_AUXB4; aqdata->aqbuttons[index].code = KEY_AUXB4; aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX; + aqdata->aqbuttons[index].hab_id = 0; aqdata->aqbuttons[index].special_mask = 0; index++; } @@ -400,6 +413,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo aqdata->aqbuttons[index].name = BTN_AUXB5; aqdata->aqbuttons[index].code = KEY_AUXB5; aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX; + aqdata->aqbuttons[index].hab_id = 0; aqdata->aqbuttons[index].special_mask = 0; index++; @@ -409,6 +423,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo aqdata->aqbuttons[index].name = BTN_AUXB6; aqdata->aqbuttons[index].code = KEY_AUXB6; aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX; + aqdata->aqbuttons[index].hab_id = 0; aqdata->aqbuttons[index].special_mask = 0; index++; @@ -418,7 +433,8 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo aqdata->aqbuttons[index].name = BTN_AUXB7; aqdata->aqbuttons[index].code = KEY_AUXB7; aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX; - index++; + aqdata->aqbuttons[index].hab_id = 0; + index++; aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[24-1]; // doesn't actually exist aqdata->aqbuttons[index].led->state = OFF; // Since there is no LED in data, set to off and allow messages to turn it on @@ -426,6 +442,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo aqdata->aqbuttons[index].name = BTN_AUXB8; aqdata->aqbuttons[index].code = KEY_AUXB8; aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX; + aqdata->aqbuttons[index].hab_id = 0; aqdata->aqbuttons[index].special_mask = 0; index++; } @@ -440,6 +457,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo aqdata->aqbuttons[index].name = BTN_AUX6; aqdata->aqbuttons[index].code = KEY_AUX6; aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX; + aqdata->aqbuttons[index].hab_id = 0; aqdata->aqbuttons[index].special_mask = 0; index++; } @@ -461,6 +479,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo aqdata->aqbuttons[index].name = BTN_POOL_HTR; aqdata->aqbuttons[index].code = KEY_POOL_HTR; aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX; + aqdata->aqbuttons[index].hab_id = 0; aqdata->aqbuttons[index].special_mask = 0; index++; @@ -470,6 +489,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo aqdata->aqbuttons[index].name = BTN_SPA_HTR; aqdata->aqbuttons[index].code = KEY_SPA_HTR; aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX; + aqdata->aqbuttons[index].hab_id = 0; aqdata->aqbuttons[index].special_mask = 0; index++; @@ -479,6 +499,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo aqdata->aqbuttons[index].name = BTN_SOLAR_HTR; aqdata->aqbuttons[index].code = KEY_SOLAR_HTR; aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX; + aqdata->aqbuttons[index].hab_id = 0; aqdata->aqbuttons[index].special_mask = 0; index++; @@ -1232,4 +1253,4 @@ void initButtons_OLD_RS16(struct aqualinkdata *aqdata) } -#endif \ No newline at end of file +#endif diff --git a/aqualink.h b/aqualink.h index 64f1f5fc..166b626d 100644 --- a/aqualink.h +++ b/aqualink.h @@ -82,6 +82,7 @@ typedef struct aqualinkkey //#endif unsigned char code; int dz_idx; + int hab_id; uint8_t special_mask; } aqkey; diff --git a/aqualinkd.c b/aqualinkd.c index 2af10853..329f51c5 100644 --- a/aqualinkd.c +++ b/aqualinkd.c @@ -55,6 +55,7 @@ #ifdef AQ_MANAGER #include "serial_logger.h" #endif +#include "net_services_habridge.h" /* #if defined AQ_DEBUG || defined AQ_TM_DEBUG @@ -1292,7 +1293,8 @@ int startup(char *self, char *cfgFile) LOG(AQUA_LOG,LOG_NOTICE, "Config idx pool thermostat = %d\n", _aqconfig_.dzidx_pool_thermostat); LOG(AQUA_LOG,LOG_NOTICE, "Config idx spa thermostat = %d\n", _aqconfig_.dzidx_spa_thermostat); */ - + LOG(AQUA_LOG,LOG_NOTICE, "Config habridge_server = %s\n", _aqconfig_.habridge_server); + LOG(AQUA_LOG,LOG_NOTICE, "Config habridge_user = %s\n", _aqconfig_.habridge_user); LOG(AQUA_LOG,LOG_NOTICE, "Config deamonize = %s\n", bool2text(_aqconfig_.deamonize)); #ifndef AQ_MANAGER LOG(AQUA_LOG,LOG_NOTICE, "Config log_file = %s\n", _aqconfig_.log_file); @@ -1342,7 +1344,6 @@ int startup(char *self, char *cfgFile) } #endif - //for (i = 0; i < TOTAL_BUTONS; i++) for (i = 0; i < _aqualink_data.total_buttons; i++) { //char ext[] = " VSP ID None | AL ID 0 "; @@ -1358,8 +1359,13 @@ int startup(char *self, char *cfgFile) sprintf(ext,"Light Progm | CTYPE %-1d |",_aqualink_data.lights[j].lightType); } } - if (_aqualink_data.aqbuttons[i].dz_idx > 0) + if (_aqualink_data.aqbuttons[i].dz_idx > 0) { sprintf(ext+strlen(ext), "dzidx %-3d", _aqualink_data.aqbuttons[i].dz_idx); + } + if (_aqualink_data.aqbuttons[i].hab_id > 0) { + sprintf(ext+strlen(ext), "habid %-3d", _aqualink_data.aqbuttons[i].hab_id); + } + /* #ifdef AQ_PDA if (isPDA_PANEL) { @@ -1552,6 +1558,11 @@ void main_loop() exit(EXIT_FAILURE); } + if (!start_habridge_updater(&_aqconfig_, &_aqualink_data)) { + LOG(AQUA_LOG,LOG_ERR, "Can not start start_habridge_updater\n"); + exit(EXIT_FAILURE); + } + startPacketLogger(); int blank_read = 0; @@ -1920,6 +1931,7 @@ void main_loop() #ifdef AQ_NO_THREAD_NETSERVICE if (_aqualink_data.updated) { broadcast_aqualinkstate(); + update_habridge_state(&_aqconfig_, &_aqualink_data); } #endif } diff --git a/config.c b/config.c index a6e52b2e..b29d0d43 100644 --- a/config.c +++ b/config.c @@ -96,7 +96,8 @@ void init_parameters (struct aqconfig * parms) parms->mqtt_server = DEFAULT_MQTT_SERVER; parms->mqtt_user = DEFAULT_MQTT_USER; parms->mqtt_passwd = DEFAULT_MQTT_PASSWD; - + parms->habridge_server = NULL; + parms->habridge_user = NULL; parms->dzidx_air_temp = TEMP_UNKNOWN; parms->dzidx_pool_water_temp = TEMP_UNKNOWN; parms->dzidx_spa_water_temp = TEMP_UNKNOWN; @@ -454,6 +455,12 @@ bool setConfigValue(struct aqualinkdata *aqdata, char *param, char *value) { } else if (strncasecmp(param, "mqtt_passwd", 11) == 0) { _aqconfig_.mqtt_passwd = cleanalloc(value); rtn=true; + } else if (strncasecmp(param, "habridge_server", 15) == 0) { + _aqconfig_.habridge_server = cleanalloc(value); + rtn=true; + } else if (strncasecmp(param, "habridge_user", 13) == 0) { + _aqconfig_.habridge_user = cleanalloc(value); + rtn=true; } else if (strncasecmp(param, "air_temp_dzidx", 14) == 0) { _aqconfig_.dzidx_air_temp = strtoul(value, NULL, 10); rtn=true; @@ -689,6 +696,9 @@ bool setConfigValue(struct aqualinkdata *aqdata, char *param, char *value) { LOG(AQUA_LOG,LOG_ERR, "Config error, VSP Pumps limited to %d, ignoring %s'\n",MAX_PUMPS,param); } rtn=true; + } else if (strncasecmp(param + 9, "_habid", 6) == 0) { + aqdata->aqbuttons[num].hab_id = strtoul(value, NULL, 10); + rtn=true; } /* } else if (strncasecmp(param + 9, "_pumpID", 7) == 0) { @@ -906,6 +916,10 @@ bool writeCfg (struct aqualinkdata *aqdata) writeCharValue(fp, "mqtt_user", _aqconfig_.mqtt_user); writeCharValue(fp, "mqtt_passwd", _aqconfig_.mqtt_passwd); + fprintf(fp, "\n#** HA Bridge Configuration **\n"); + writeCharValue(fp, "habridge_server", config_parameters->habridge_server); + writeCharValue(fp, "habridge_user", config_parameters->habridge_user); + fprintf(fp, "\n#** General **\n"); fprintf(fp, "convert_mqtt_temp_to_c = %s\n", bool2text(_aqconfig_.convert_mqtt_temp)); fprintf(fp, "override_freeze_protect = %s\n", bool2text(_aqconfig_.override_freeze_protect)); @@ -944,6 +958,8 @@ bool writeCfg (struct aqualinkdata *aqdata) fprintf(fp, "button_%.2d_dzidx = %d\n", i+1, aqdata->aqbuttons[i].dz_idx); if (aqdata->aqbuttons[i].pda_label != NULL) fprintf(fp, "button_%.2d_PDA_label = %s\n", i+1, aqdata->aqbuttons[i].pda_label); + if (aqdata->aqbuttons[i].hab_id > 0) + fprintf(fp, "button_%.2d_habid = %d\n", i+1, aqdata->aqbuttons[i].hab_id); } fclose(fp); remount_root_ro(fs); diff --git a/config.h b/config.h index a9fea001..3a3c2117 100644 --- a/config.h +++ b/config.h @@ -54,6 +54,8 @@ struct aqconfig char *mqtt_server; char *mqtt_user; char *mqtt_passwd; + char *habridge_server; + char *habridge_user; char mqtt_ID[MQTT_ID_LEN]; int dzidx_air_temp; int dzidx_pool_water_temp; diff --git a/net_services_habridge.c b/net_services_habridge.c new file mode 100644 index 00000000..f68ac9fe --- /dev/null +++ b/net_services_habridge.c @@ -0,0 +1,141 @@ +/* + * net_services_habridge.c + * + * Created on: Apr 29, 2019 + * Author: Lee_Ballard + */ + +#include +#include +#include "net_services_habridge.h" +#include "aqualink.h" + +static pthread_mutex_t _habridge_state_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t _habridge_update_cond = PTHREAD_COND_INITIALIZER; +static time_t _last_habridge_forced_update; + +static aqledstate _last_habridge_ledstate[TOTAL_BUTTONS] = { + LED_S_UNKNOWN,LED_S_UNKNOWN,LED_S_UNKNOWN,LED_S_UNKNOWN,LED_S_UNKNOWN, + LED_S_UNKNOWN,LED_S_UNKNOWN,LED_S_UNKNOWN,LED_S_UNKNOWN,LED_S_UNKNOWN, + LED_S_UNKNOWN,LED_S_UNKNOWN +}; + +struct habridge_updater_data +{ + struct aqconfig *aqconfig; + struct aqualinkdata *aqdata; +}; + +// see https://github.com/bwssytems/ha-bridge#update-bridge-internal-light-state +//static const char *s_url = +// "http://127.0.0.1:8080/api/username/lights/1/bridgeupdatestate"; + +void* habridge_updater_routine(void* data) +{ + int ret = 0; + struct aqualinkdata *aqdata = ((struct habridge_updater_data *)data)->aqdata; + struct aqconfig *aqconfig = ((struct habridge_updater_data *)data)->aqconfig; + char cmd_buff[256]; + int i; + + while(true) + { + pthread_mutex_lock(&_habridge_state_mutex); + if ((ret = pthread_cond_wait(&_habridge_update_cond, + &_habridge_state_mutex))) + { + LOG(NET_LOG, LOG_ERR, "start_habridge_updater cond wait err %s\n", + strerror(ret)); + } + pthread_mutex_unlock(&_habridge_state_mutex); + + for (i=0; i < TOTAL_BUTTONS; i++) + { + if ((aqdata->aqbuttons[i].hab_id) && + (_last_habridge_ledstate[i] != aqdata->aqbuttons[i].led->state)) + { + const char *on_value = "false"; + + _last_habridge_ledstate[i] = aqdata->aqbuttons[i].led->state; + if (aqdata->aqbuttons[i].led->state == ON) + { + on_value = "true"; + } + snprintf(cmd_buff, sizeof(cmd_buff), + "curl -sS -X PUT -d '{\"on\": %s}' 'http://%s/api/%s/lights/%d/bridgeupdatestate' > /dev/null", + on_value, aqconfig->habridge_server, + aqconfig->habridge_user, aqdata->aqbuttons[i].hab_id); + LOG(NET_LOG, LOG_DEBUG, "habridge_updater_routine %s\n", cmd_buff); + if ((ret = system(cmd_buff))) + { + LOG(NET_LOG, LOG_ERR, "start_habridge_updater system err %s\n", + strerror(ret)); + } + else + { + LOG(NET_LOG, LOG_DEBUG, "habridge_updater_routine success\n"); + } + } + } + } + + return NULL; +} + +bool start_habridge_updater(struct aqconfig *aqconfig, struct aqualinkdata *aqdata) +{ + static struct habridge_updater_data data; + pthread_t thread_id; + data.aqconfig = aqconfig; + data.aqdata = aqdata; + + if (aqconfig->habridge_server != NULL) + { + time(&_last_habridge_forced_update); + if( pthread_create( &thread_id , NULL , habridge_updater_routine, (void*)&data) < 0) { + LOG(NET_LOG, LOG_ERR, "start_habridge_updater: could not create thread\n"); + return false; + } + } + else + { + LOG(NET_LOG, LOG_DEBUG, "start_habridge_updater - habridge_server not set\n"); + } + return true; +} + +bool update_habridge_state(struct aqconfig *aqconfig, + struct aqualinkdata *aqdata) +{ + time_t now; + int i; + + if (aqconfig->habridge_server == NULL) + { + return true; + } + + time(&now); + + if (difftime(now, _last_habridge_forced_update) > 60) + { + LOG(NET_LOG, LOG_DEBUG, "update_habridge_state - force update\n"); + for (i=0; i < TOTAL_BUTTONS; i++) + { + _last_habridge_ledstate[i] = LED_S_UNKNOWN; + } + time(&_last_habridge_forced_update); + } + + for (i=0; i < TOTAL_BUTTONS; i++) + { + if ((aqdata->aqbuttons[i].hab_id) && + (_last_habridge_ledstate[i] != aqdata->aqbuttons[i].led->state)) + { + pthread_cond_signal(&_habridge_update_cond); + break; + } + } + + return true; +} diff --git a/net_services_habridge.h b/net_services_habridge.h new file mode 100644 index 00000000..0f37a5e6 --- /dev/null +++ b/net_services_habridge.h @@ -0,0 +1,18 @@ +/* + * net_services_habridge.h + * + * Created on: Apr 29, 2019 + * Author: Lee_Ballard + */ + +#ifndef NET_SERVICES_HABRIDGE_H_ +#define NET_SERVICES_HABRIDGE_H_ + +#include +#include "mongoose.h" +#include "config.h" + +bool start_habridge_updater(struct aqconfig *aqconfig, struct aqualinkdata *aqdata); +bool update_habridge_state(struct aqconfig *aqconfig, struct aqualinkdata *aqdata); + +#endif /* NET_SERVICES_HABRIDGE_H_ */ diff --git a/release/aqualinkd.conf b/release/aqualinkd.conf index cfba533f..1e54c9e1 100755 --- a/release/aqualinkd.conf +++ b/release/aqualinkd.conf @@ -8,7 +8,7 @@ web_directory=/var/www/aqualinkd/ # Log to file, comment out if you do not want to log to file #log_file=/var/log/aqualinkd.log -# The log level. [DEBUG_DERIAL, DEBUG, INFO, NOTICE, WARNING, ERROR] +# The log level. [DEBUG_SERIAL, DEBUG, INFO, NOTICE, WARNING, ERROR] # Pick the highest level, and all levels below will be sent to syslog. # your syslog settings may be set to only display messages above a certian level # in which case make sure you use the log_file settings to capture everything