diff --git a/README.md b/README.md index ecc5cd9..75fca71 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,6 @@ NEED TO FIX FOR THIS RELEASE. * Heat Pump / Chiller for OneTouch -* Try an auto-update * Update Mongoose * PDA Crap. @@ -133,8 +132,15 @@ NEED TO FIX FOR THIS RELEASE. * FIX Panel name in config to accept output from panel actual name +* Move all programming threads over to using struct programmerArgs. +* Use set_allbutton_light_dimmer for all lights (ie color lights) --> +# Updates in 2.6.10 (DEV) +* UI now supports UOM for external sensors +* Updates to VSP status +* Fix for Jandy Panel Rev T dimmer lights bug. +* Autoconfig changes for older panels. # Updates in 2.6.9 (July 26 2025) * Config fixes for 0x33 ID / PDA diff --git a/release/aqualinkd-arm64 b/release/aqualinkd-arm64 index 0260548..808414b 100755 Binary files a/release/aqualinkd-arm64 and b/release/aqualinkd-arm64 differ diff --git a/release/aqualinkd-armhf b/release/aqualinkd-armhf index 13b6129..252aaa0 100755 Binary files a/release/aqualinkd-armhf and b/release/aqualinkd-armhf differ diff --git a/release/aqualinkd.conf b/release/aqualinkd.conf index 40ea384..b14cd1b 100755 --- a/release/aqualinkd.conf +++ b/release/aqualinkd.conf @@ -298,13 +298,23 @@ report_zero_pool_temp = yes # These will depend a lot on the board & OS you are running. # the "factor" is the number the sensor is multiplied by to get an accurate result. example below is (millidegrees Celsius to Celsius) +# Poll tile in seconds +#sensor_poll_time=30 + #sensor_01_path = /sys/class/thermal/thermal_zone0/temp #sensor_01_label = CPU #sensor_01_factor = 0.001 +#sensor_01_uom=°C # Boards like Radxa Zero3 have others sensors like below. #sensor_02_path = /sys/class/thermal/thermal_zone1/temp #sensor_02_label = GPU #sensor_02_factor = 0.001 +#sensor_02_uom=°C - +# Linux load average +#sensor_03_path = /proc/loadavg +#sensor_03_label = CPU load +#sensor_03_factor = 100 +#sensor_03_uom = % +#sensor_03_regexp = ([0-9|\.]*)\s \ No newline at end of file diff --git a/release/serial_logger-arm64 b/release/serial_logger-arm64 index 43eaecf..50696d1 100755 Binary files a/release/serial_logger-arm64 and b/release/serial_logger-arm64 differ diff --git a/release/serial_logger-armhf b/release/serial_logger-armhf index 45844a1..e893a87 100755 Binary files a/release/serial_logger-armhf and b/release/serial_logger-armhf differ diff --git a/source/allbutton_aq_programmer.c b/source/allbutton_aq_programmer.c index c654c78..05c7cc4 100644 --- a/source/allbutton_aq_programmer.c +++ b/source/allbutton_aq_programmer.c @@ -13,6 +13,8 @@ #include "color_lights.h" #include "devices_jandy.h" + + bool waitForButtonState(struct aqualinkdata *aq_data, aqkey* button, aqledstate state, int numMessageReceived); bool waitForMessage(struct aqualinkdata *aq_data, char* message, int numMessageReceived); bool waitForEitherMessage(struct aqualinkdata *aq_data, char* message1, char* message2, int numMessageReceived); @@ -638,6 +640,153 @@ void *set_allbutton_light_programmode( void *ptr ) // just stop compiler error, ptr is not valid as it's just been freed return ptr; } + +void *set_allbutton_light_dimmer( void *ptr ) +{ + int i; + struct programmingThreadCtrl *threadCtrl; + threadCtrl = (struct programmingThreadCtrl *) ptr; + struct aqualinkdata *aq_data = threadCtrl->aq_data; + + waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_LIGHTDIMMER); + + /* + char *buf = (char*)threadCtrl->thread_args; + const char *mode_name; + int val = atoi(&buf[0]); + int btn = atoi(&buf[5]); + int typ = atoi(&buf[10]); + bool use_current_mode = false; + + if (btn < 0 || btn >= aq_data->total_buttons ) { + LOG(ALLB_LOG, LOG_ERR, "Can't program light dimmer on button %d\n", btn); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + aqkey *button = &aq_data->aqbuttons[btn]; + unsigned char code = button->code; + */ + + struct programmerArgs *pargs = &threadCtrl->pArgs; + //unsigned char code = threadCtrl->pArgs.button.code; + //aqkey *button2 = pargs->button; + aqkey *button = threadCtrl->pArgs.button; + //unsigned char code = pargs->button->code; + int val = pargs->value; + bool useDefaultIfValid = pargs->alt_value; + bool use_current_mode = false; + const char *mode_name; + + //printf("****** set_allbutton_light_dimmer Light %s on/off=%s use-current-state=%s\n",button->label, button->led->state==ON?"on":"off", use_current_mode==false?"no":"yes"); + + if (!isPLIGHT(button->special_mask)) { + LOG(ALLB_LOG, LOG_ERR, "Can't program light for button '%d', configuration is incorrect\n", button->label); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + + clight_detail *light = (clight_detail *)button->special_mask_ptr; + + if (pargs->value <= 0) { + use_current_mode = true; + LOG(ALLB_LOG, LOG_INFO, "Light Dimmer Programming #: %d, on button: %s, using current mode\n", val, button->label); + } else { + //mode_name = light_mode_name(typ, val-1, ALLBUTTON); + //mode_name = light_mode_name(((clight_detail *)button->special_mask_ptr)->lightType, val, ALLBUTTON); + mode_name = light_mode_name(LC_DIMMER, val, ALLBUTTON); + use_current_mode = false; + if (mode_name == NULL) { + LOG(ALLB_LOG, LOG_ERR, "Light Dimmer Programming #: %d, on button: %s, couldn't find mode name '%s'\n", val, button->label, mode_name); + cleanAndTerminateThread(threadCtrl); + return ptr; + } else { + LOG(ALLB_LOG, LOG_INFO, "Light Dimmer Programming #: %d, on button: %s, name '%s'\n", val, button->label, mode_name); + } + } + + //printf("****** set_allbutton_light_dimmer Light %s on/off=%d %s\n",button->label, pargs->button->led->state, button->led->state==ON?"on":"off"); + // Needs to start programming sequence with light off + + if ( button->led->state == ON ) { + LOG(ALLB_LOG, LOG_INFO, "Light Programming Initial state on, turning off\n"); + send_cmd(button->code); + waitfor_queue2empty(); + if ( !waitForMessage(threadCtrl->aq_data, "OFF", 5)) // Message like 'Aux3 Off' + LOG(ALLB_LOG, LOG_ERR, "Light Programming didn't receive OFF message\n"); + } + + // Look for <>* + // Now turn on and wait for the message "color mode name<>*" + send_cmd(button->code); + waitfor_queue2empty(); + i=0; + int waitCounter=12; + + do{ + LOG(ALLB_LOG, LOG_INFO,"Light program wait for message\n"); + if ( !waitForMessage(threadCtrl->aq_data, "~*", waitCounter)) + LOG(ALLB_LOG, LOG_ERR, "Light Programming didn't receive light mode message\n"); + + // Wait for less messages after first try. We get a lot of repeat messages before the one we need. + waitCounter = 3; + + if (useDefaultIfValid) { + // If 0 in last message then it's NOT valid and we will change to 100% (5 char =0, 6 char=%) + if (aq_data->last_message[0] == '0' && aq_data->last_message[1] == '%' ) { + //printf("******** Light stuck ************\n"); + LOG(ALLB_LOG, LOG_WARNING, "Light Programming detected Jandy panel light%% bug, re-setting to %d%%\n",val * 25); + useDefaultIfValid=false; + } else { + // printf("******** Light good ************ '%c' '%c'\n",aq_data->last_message[0],aq_data->last_message[1]); + send_cmd(KEY_ENTER); + waitfor_queue2empty(); + break; + } + } else if (use_current_mode) { + LOG(ALLB_LOG, LOG_INFO, "Light Programming using mode %s\n",aq_data->last_message); + send_cmd(KEY_ENTER); + waitfor_queue2empty(); + break; + } else if (strncasecmp(aq_data->last_message, mode_name, strlen(mode_name)) == 0) { + LOG(ALLB_LOG, LOG_INFO, "Light Programming found mode %s\n",mode_name); + send_cmd(KEY_ENTER); + waitfor_queue2empty(); + break; + } + + send_cmd(KEY_RIGHT); + waitfor_queue2empty(); + // Just clear current message before waiting for next, since the test in the do can't distinguish + // as both messages end in "~*" + waitForMessage(threadCtrl->aq_data, NULL, 1); + + i++; + } while (i <= 8); + + if (i == 8) { + LOG(ALLB_LOG, LOG_ERR, "Light Programming didn't receive light mode message for '%s'\n",use_current_mode?"light program":mode_name); + } else { + // update status before we are exit. + if (light->lightType == LC_DIMMER2 ) { + // value or Dimmer2 is the actual %, while Dimmer & colorlight is an index into an array + updateLightProgram(aq_data, val * 25, light); + } else { + updateLightProgram(aq_data, val, light); + } + } + + cleanAndTerminateThread(threadCtrl); + + // just stop compiler error, ptr is not valid as it's just been freed + return ptr; +} + + + + + + void *set_allbutton_pool_heater_temps( void *ptr ) { struct programmingThreadCtrl *threadCtrl; diff --git a/source/allbutton_aq_programmer.h b/source/allbutton_aq_programmer.h index e41f5f2..2e616a4 100644 --- a/source/allbutton_aq_programmer.h +++ b/source/allbutton_aq_programmer.h @@ -14,6 +14,7 @@ void *get_allbutton_aux_labels( void *ptr ); //void *threadded_send_cmd( void *ptr ); void *set_allbutton_light_programmode( void *ptr ); void *set_allbutton_light_colormode( void *ptr ); +void *set_allbutton_light_dimmer( void *ptr ); void *set_allbutton_SWG( void *ptr ); void *set_allbutton_boost( void *ptr ); diff --git a/source/aq_panel.c b/source/aq_panel.c index b53f14d..68b0848 100644 --- a/source/aq_panel.c +++ b/source/aq_panel.c @@ -30,6 +30,7 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rspda, int size, bool combo, bool dual); void programDeviceLightMode(struct aqualinkdata *aqdata, int value, int button); +void programDeviceLightBrightness(struct aqualinkdata *aqdata, int value, int deviceIndex, bool expectMultiple, request_source source); void printPanelSupport(struct aqualinkdata *aqdata); uint16_t setPanelSupport(struct aqualinkdata *aqdata); @@ -185,7 +186,7 @@ pull board CPU, revision & panel string from strings like uint8_t setPanelInformationFromPanelMsg(struct aqualinkdata *aqdata, const char *input, uint8_t type, emulation_type source) { const char *rev_pos = NULL; uint8_t rtn = 0; -printf("Calculate panel from %s\n",input); +//printf("Calculate panel from %s\n",input); //const char *rev_pos = strstr(input, "REV"); // Find the position of "REV" const char *sp; int length = 0; @@ -282,7 +283,7 @@ uint16_t setPanelSupport(struct aqualinkdata *aqdata) // Rev >= F Dimmer. But need serial protocol so set to I // Rev >= H (Think this was first RS485) - // Rev >= HH Serial Adapter. (first support) + // Rev >= HH - (some panels support serial adapter, some don't) // Rev >= I Serial Adapter. // Rev >= I One Touch protocol // Rev >= L JandyColors Smart Light Control @@ -310,10 +311,10 @@ uint16_t setPanelSupport(struct aqualinkdata *aqdata) // Rev >= Y TruSense Water Chemistry Analyzer // Rev >= Yg Virtual Device called Label Auxiliraries - +/* if (aqdata->panel_rev[0] >= 79) // O in ascii aqdata->panel_support_options |= RSP_SUP_VSP; - +*/ if (aqdata->panel_rev[0] >= 73){ // I in ascii aqdata->panel_support_options |= RSP_SUP_ONET; aqdata->panel_support_options |= RSP_SUP_RSSA; @@ -1160,6 +1161,7 @@ int getWaterTemp(struct aqualinkdata *aqdata) bool setDeviceState(struct aqualinkdata *aqdata, int deviceIndex, bool isON, request_source source) { aqkey *button = &aqdata->aqbuttons[deviceIndex]; + bool set_pre_state = true; //if ( button->special_mask & VIRTUAL_BUTTON && button->special_mask & VS_PUMP) { if ( isVS_PUMP(button->special_mask) && isVBUTTON(button->special_mask)) { @@ -1197,17 +1199,30 @@ bool setDeviceState(struct aqualinkdata *aqdata, int deviceIndex, bool isON, req // Check for panel programmable light. if so simple ON isn't going to work well // Could also add "light mode" check, as this is only valid for panel configured light not aqualinkd configured light. if (isPLIGHT(button->special_mask) && button->led->state == OFF) { + // Full range dimmer can get stuck off on rev T.2 (maybe others), to overcome use allbutton with any % other than 0 + if ( ((clight_detail *)button->special_mask_ptr)->lightType == LC_DIMMER2 || + ((clight_detail *)button->special_mask_ptr)->lightType == LC_DIMMER ) // NSF should remove this once figured out Line #1354 programDeviceLightBrightness() + { + // programLightBrightness has appropiate code to call allbutton programmer. + /* + int val = ((clight_detail *)button->special_mask_ptr)->lastValue>0?((clight_detail *)button->special_mask_ptr)->lastValue:100; + programDeviceLightBrightness(aqdata, val, deviceIndex, (source==NET_MQTT?true:false), source); + */ + programDeviceLightBrightness(aqdata, 101, deviceIndex, (source==NET_MQTT?true:false), source); + set_pre_state = false; + } // OK Programable light, and no light mode selected. Now let's work out best way to turn it on. serial_adapter protocol will to it without questions, // all other will require programmig. - if (isRSSA_ENABLED) { + else if (isRSSA_ENABLED) { set_aqualink_rssadapter_aux_state(button, true); } else { //set_light_mode("0", deviceIndex); // 0 means use current light mode programDeviceLightMode(aqdata, 0, deviceIndex); // 0 means use current light mode + set_pre_state = false; } // If aqualinkd programmable light, it will come on at last state, so set that. - if ( /*isPLIGHT(button->special_mask) &&*/ ((clight_detail *)button->special_mask_ptr)->lightType == LC_PROGRAMABLE ) { + if ( ((clight_detail *)button->special_mask_ptr)->lightType == LC_PROGRAMABLE ) { ((clight_detail *)button->special_mask_ptr)->currentValue = ((clight_detail *)button->special_mask_ptr)->lastValue; } } else if (isVBUTTON(button->special_mask)) { @@ -1226,6 +1241,7 @@ bool setDeviceState(struct aqualinkdata *aqdata, int deviceIndex, bool isON, req //if (button->rssd_code != VBUTTON_RSSD) { //LOG(PANL_LOG, LOG_NOTICE, "********** USE AQ_SET_IAQTOUCH_DEVICE_ON_OFF ********\n"); aq_programmer(AQ_SET_IAQTOUCH_DEVICE_ON_OFF, msg, aqdata); + set_pre_state = false; //} else if (button->rssd_code != VBUTTON_ONETOUCH_RSSD) { // LOG(PANL_LOG, LOG_NOTICE, "********** USE AQ_SET_IAQTOUCH_ONETOUCH_ON_OFF ********\n"); // aq_programmer(AQ_SET_IAQTOUCH_ONETOUCH_ON_OFF, msg, aqdata); @@ -1266,7 +1282,7 @@ bool setDeviceState(struct aqualinkdata *aqdata, int deviceIndex, bool isON, req // Pre set device to state, next status will correct if state didn't take, but this will stop multiple ON messages setting on/off //#ifdef PRESTATE_ONOFF - if (_aqconfig_.device_pre_state) { + if (_aqconfig_.device_pre_state && set_pre_state) { if ((button->code == KEY_POOL_HTR || button->code == KEY_SPA_HTR || button->code == KEY_EXT_AUX) && isON > 0) { @@ -1325,7 +1341,66 @@ bool programDeviceValue(struct aqualinkdata *aqdata, action_type type, int value void programDeviceLightBrightness(struct aqualinkdata *aqdata, int value, int deviceIndex, bool expectMultiple, request_source source) { + // If Value is 101 + clight_detail *light = getProgramableLight(aqdata, deviceIndex); + // Light mode 10 + + //printf("******* Light Brightness *** value=%d device=%d multiple=%d\n",value,deviceIndex,expectMultiple); + //printf("****** Light %s on/off=%d %s\n",aqdata->aqbuttons[deviceIndex].label, aqdata->aqbuttons[deviceIndex].led->state, aqdata->aqbuttons[deviceIndex].led->state==ON?"on":"off"); + + // With changes to fix LC_DIMMER2, LC_DIMMER has now started having similar issues. + // Need to come back and look at why, LC_DIMMER should be removed from below IF once sorted./ + if ( (light->lightType == LC_DIMMER2 || light->lightType == LC_DIMMER) && aqdata->aqbuttons[deviceIndex].led->state == OFF ) { + // Light is off, we will turn in on but due to Jandy bug on rev T, have to use all button so values of 25/50/75/100. + // value = 101, means use default (if if it comes on at 33% leave that, if not 100%). + // value != 25/50/75/100 use all button to turn on, then reset with rssserial adapter. + LOG(PANL_LOG,LOG_DEBUG, "Using allbutton programmer to set light dimmer\n"); + + if (value == 101) { + aq_program(AQ_SET_LIGHTDIMMER, &aqdata->aqbuttons[deviceIndex], 4, true, aqdata); // 4 = 100% since it uses light mode name + } else { + int calVal = round( (value+12) / 25); // Round up/down + if (calVal > 4 ) {calVal=4;} + LOG(PANL_LOG,LOG_INFO, "Rounded dimmer value to %d for on command\n",calVal * 25); + aq_program(AQ_SET_LIGHTDIMMER, &aqdata->aqbuttons[deviceIndex], calVal, false, aqdata); + + if (light->lightType == LC_DIMMER2 ) { + if (value != 25 && value !=50 && value !=75 && value != 100) { + // Setup the rssd to set the light to the right value. + //printf("Second programming for %%%d\n",value); + time(&aqdata->unactioned.requested); + aqdata->unactioned.requested += 5; + aqdata->unactioned.value = value; + aqdata->unactioned.type = LIGHT_MODE; + aqdata->unactioned.id = deviceIndex; + } + } + } + + /* + int calVal = round(value / 25); + LOG(PANL_LOG,LOG_DEBUG, "Using allbutton programmer to set light\n"); + if (calVal > 4 ) {calVal=4;} + LOG(PANL_LOG,LOG_INFO, "Rounded dimmer value to %d for on command\n",calVal * 25); + if (value != 25 && value !=50 && value !=75 && value != 100) { + aq_program(AQ_SET_LIGHTDIMMER, &aqdata->aqbuttons[deviceIndex], calVal, false, aqdata); + } else { + aq_program(AQ_SET_LIGHTDIMMER, &aqdata->aqbuttons[deviceIndex], calVal, true, aqdata); + }*/ + /* + if (value != 25 && value !=50 && value !=75 && value != 100) { + printf("Second programming for %%%d\n",value); + time(&aqdata->unactioned.requested); + aqdata->unactioned.requested += 5; + aqdata->unactioned.value = value; + aqdata->unactioned.type = LIGHT_MODE; + aqdata->unactioned.id = deviceIndex; + } + */ + return; + } + if (!isRSSA_ENABLED) { LOG(PANL_LOG,LOG_ERR, "Light mode brightness is only supported with `rssa_device_id` set\n"); @@ -1340,6 +1415,10 @@ void programDeviceLightBrightness(struct aqualinkdata *aqdata, int value, int de // DIMMER is 0,25,50,100 DIMMER2 is range if (light->lightType == LC_DIMMER) { value = round(value / 25); + if (value > 4 ) {value=4;} + LOG(PANL_LOG,LOG_INFO, "Rounded dimmer value to %d\n",value * 25); + } else { + } if (!expectMultiple) { @@ -1416,6 +1495,7 @@ void programDeviceLightMode(struct aqualinkdata *aqdata, int value, int deviceIn // Value 1 = 25, 2 = 50, 3 = 75, 4 = 100 (need to convert value into binary) if (value >= 1 && value <= 4) { unsigned char rssd_value = value * 25; + //rssd_value +=128; set_aqualink_rssadapter_aux_extended_state(light->button, rssd_value); } else { LOG(PANL_LOG,LOG_ERR, "Light mode %d is not valid for '%s'\n",value, light->button->label); @@ -1423,7 +1503,7 @@ void programDeviceLightMode(struct aqualinkdata *aqdata, int value, int deviceIn } } else { // Dimmer or any color light can simply be set with value - set_aqualink_rssadapter_aux_extended_state(light->button, value); + set_aqualink_rssadapter_aux_extended_state(light->button, value + 128); } /* } else if (isRSSA_ENABLED && light->lightType == LC_DIMMER2) { @@ -1533,6 +1613,18 @@ bool panel_device_request(struct aqualinkdata *aqdata, action_type type, int dev // Programmable light has been updated, so update the status in AqualinkD +void updateLightProgram(struct aqualinkdata *aqdata, int value, clight_detail *light) +{ + light->currentValue = value; + if (value > 0 && light->lastValue != value) { + light->lastValue = value; + if (_aqconfig_.save_light_programming_value && light->lightType == LC_PROGRAMABLE ) { + LOG(PANL_LOG,LOG_NOTICE, "Writing light programming value to config for %s\n",light->button->label); + writeCfg(aqdata); + } + } +} + void updateButtonLightProgram(struct aqualinkdata *aqdata, int value, int button) { /* @@ -1554,6 +1646,9 @@ void updateButtonLightProgram(struct aqualinkdata *aqdata, int value, int button return; } + updateLightProgram(aqdata, value, light); + + /* light->currentValue = value; if (value > 0 && light->lastValue != value) { light->lastValue = value; @@ -1562,6 +1657,7 @@ void updateButtonLightProgram(struct aqualinkdata *aqdata, int value, int button writeCfg(aqdata); } } + */ } clight_detail *getProgramableLight(struct aqualinkdata *aqdata, int button) diff --git a/source/aq_panel.h b/source/aq_panel.h index 300c72c..3fbbc47 100644 --- a/source/aq_panel.h +++ b/source/aq_panel.h @@ -68,7 +68,8 @@ const char* getShortPanelString(); bool panel_device_request(struct aqualinkdata *aqdata, action_type type, int deviceIndex, int value, request_source source); -void updateButtonLightProgram(struct aqualinkdata *aqdata, int value, int button); +void updateLightProgram(struct aqualinkdata *aqdata, int value, clight_detail *light); +void updateButtonLightProgram(struct aqualinkdata *aqdata, int value, int button); int getWaterTemp(struct aqualinkdata *aqdata); diff --git a/source/aq_programmer.c b/source/aq_programmer.c index 29b1edb..128f655 100644 --- a/source/aq_programmer.c +++ b/source/aq_programmer.c @@ -68,7 +68,8 @@ const func_ptr _prog_functions[AQP_RSSADAPTER_MAX] = { [AQ_GET_DIAGNOSTICS_MODEL] = get_allbutton_diag_model, [AQ_GET_PROGRAMS] = get_allbutton_programs, [AQ_SET_LIGHTPROGRAM_MODE] = set_allbutton_light_programmode, - [AQ_SET_LIGHTCOLOR_MODE] = set_allbutton_light_colormode, + [AQ_SET_LIGHTCOLOR_MODE] = set_allbutton_light_colormode, + [AQ_SET_LIGHTDIMMER] = set_allbutton_light_dimmer, [AQ_SET_SWG_PERCENT] = set_allbutton_SWG, [AQ_GET_AUX_LABELS] = get_allbutton_aux_labels, [AQ_SET_BOOST] = set_allbutton_boost, @@ -449,11 +450,22 @@ void kick_aq_program_thread(struct aqualinkdata *aq_data, emulation_type source_ } } +void _aq_programmer_(program_type r_type, char *args, aqkey *button, int value, int alt_value, struct aqualinkdata *aq_data, bool allowOveride); + +void aq_program(program_type r_type, aqkey *button, int value, int alt_value, struct aqualinkdata *aq_data){ + _aq_programmer_(r_type, NULL, button, value, alt_value, aq_data, true); +} void aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_data){ _aq_programmer(r_type, args, aq_data, true); } + void _aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_data, bool allowOveride) +{ + _aq_programmer_(r_type, args, NULL, -1, -1, aq_data, allowOveride); +} + +void _aq_programmer_(program_type r_type, char *args, aqkey *button, int value, int alt_value, struct aqualinkdata *aq_data, bool allowOveride) { struct programmingThreadCtrl *programmingthread = malloc(sizeof(struct programmingThreadCtrl)); @@ -648,6 +660,11 @@ void _aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_dat if (args != NULL /*&& type != AQ_SEND_CMD*/) strncpy(programmingthread->thread_args, args, sizeof(programmingthread->thread_args)-1); + programmingthread->pArgs.button = button; + programmingthread->pArgs.value = value; + programmingthread->pArgs.alt_value = alt_value; + + switch(type) { case AQ_GET_RSSADAPTER_SETPOINTS: get_aqualink_rssadapter_setpoints(); @@ -814,6 +831,9 @@ const char *ptypeName(program_type type) break; case AQ_SET_LIGHTCOLOR_MODE: return "Set light color (using Panel)"; + break; + case AQ_SET_LIGHTDIMMER: + return "Set light dimmer"; break; case AQ_SET_SWG_PERCENT: return "Set SWG percent"; @@ -1023,7 +1043,8 @@ const char *programtypeDisplayName(program_type type) case AQ_SET_LIGHTPROGRAM_MODE: case AQ_SET_LIGHTCOLOR_MODE: case AQ_SET_IAQTOUCH_LIGHTCOLOR_MODE: - return "Programming: setting light color"; + case AQ_SET_LIGHTDIMMER: + return "Programming: setting light mode"; break; case AQ_SET_SWG_PERCENT: case AQ_SET_ONETOUCH_SWG_PERCENT: diff --git a/source/aq_programmer.h b/source/aq_programmer.h index d827542..fc01e36 100644 --- a/source/aq_programmer.h +++ b/source/aq_programmer.h @@ -56,7 +56,8 @@ typedef enum { AQ_GET_DIAGNOSTICS_MODEL, AQ_GET_PROGRAMS, AQ_SET_LIGHTPROGRAM_MODE, - AQ_SET_LIGHTCOLOR_MODE, + AQ_SET_LIGHTCOLOR_MODE, + AQ_SET_LIGHTDIMMER, AQ_SET_SWG_PERCENT, AQ_GET_AUX_LABELS, AQ_SET_BOOST, @@ -144,13 +145,35 @@ typedef enum { #define AQP_RSSADAPTER_MAX AQ_ADD_RSSADAPTER_SPA_HEATER_TEMP + +struct aqualinkdata; +typedef struct aqualinkkey aqkey; + +/* +struct aqualinkkey; +typedef struct aqualinkkey aqkey; +*/ +/* +struct aqualinkdata; +struct programmingThreadCtrl; +*/ + + +struct programmerArgs { + aqkey *button; + int value; + int alt_value; + //char cval[PTHREAD_ARG]; +}; + struct programmingThreadCtrl { pthread_t thread_id; //void *thread_args; + struct programmerArgs pArgs; char thread_args[PTHREAD_ARG]; struct aqualinkdata *aq_data; }; - + typedef enum pump_type { PT_UNKNOWN = -1, @@ -162,7 +185,10 @@ typedef enum pump_type { //void aq_programmer(program_type type, void *args, struct aqualinkdata *aq_data); void aq_programmer(program_type type, char *args, struct aqualinkdata *aq_data); -//void kick_aq_program_thread(struct aqualinkdata *aq_data); +// Below is NEW version of above. +void aq_program(program_type r_type, aqkey *button, int value, int value2, struct aqualinkdata *aq_data); + +//void kick_aq_program_thread(struct aqualinkdata *aq_data); void kick_aq_program_thread(struct aqualinkdata *aq_data, emulation_type source_type); bool in_programming_mode(struct aqualinkdata *aq_data); bool in_ot_programming_mode(struct aqualinkdata *aq_data); diff --git a/source/aq_serial.h b/source/aq_serial.h index 43a6e69..baaaef5 100644 --- a/source/aq_serial.h +++ b/source/aq_serial.h @@ -531,6 +531,7 @@ typedef enum { DRS_JLIGHT } rsDeviceType; +/* typedef enum { ON, OFF, @@ -544,7 +545,7 @@ typedef struct aqualinkled //int number; aqledstate state; } aqled; - +*/ // Battery Status Identifiers enum { OK = 0, diff --git a/source/aqualink.h b/source/aqualink.h index f1c8983..249b541 100644 --- a/source/aqualink.h +++ b/source/aqualink.h @@ -91,10 +91,24 @@ typedef enum temperatureUOM { } temperatureUOM; */ +typedef enum { + ON, + OFF, + FLASH, + ENABLE, + LED_S_UNKNOWN +} aqledstate; + +typedef struct aqualinkled +{ + //int number; + aqledstate state; +} aqled; + typedef struct aqualinkkey { //int number; - //aqledstate *state; + //aqledstate ledstate; // In the future there is no need to aqled struct so move code over to this. aqled *led; char *label; char *name; @@ -108,6 +122,9 @@ typedef struct aqualinkkey void *special_mask_ptr; } aqkey; + +//#include "aq_programmer.h" + // special_mask for above aqualinkkey structure. #define VS_PUMP (1 << 0) #define PROGRAM_LIGHT (1 << 1) @@ -126,7 +143,21 @@ struct programmingthread { program_type ptype; //void *thread_args; }; +/* +struct programmerArgs { + aqkey *button; + int value; + //char cval[PTHREAD_ARG]; +}; +struct programmingThreadCtrl { + pthread_t thread_id; + //void *thread_args; + struct programmerArgs pArgs; + char thread_args[PTHREAD_ARG]; + struct aqualinkdata *aq_data; +}; +*/ /* typedef enum panel_status { CONNECTED, diff --git a/source/aqualinkd.c b/source/aqualinkd.c index aeffc7e..5a852c4 100644 --- a/source/aqualinkd.c +++ b/source/aqualinkd.c @@ -756,6 +756,7 @@ bool auto_configure(unsigned char* packet, int rs_fd) { static unsigned char lastID = 0x00; static bool seen_iAqualink2 = false; static bool ignore_AqualinkTouch = false; + static bool ignore_OneTouch = false; static int foundIDs = 0; static int packetsReceived=0; @@ -799,6 +800,17 @@ bool auto_configure(unsigned char* packet, int rs_fd) { foundIDs--; } } + if ( !isMASKSET(_aqualink_data.panel_support_options, RSP_SUP_ONET)) { + LOG(AQUA_LOG,LOG_NOTICE, "Ignoring OneTouch probes due to panel rev\n"); + ignore_OneTouch = true; + if ( _aqconfig_.extended_device_id >= 0x40 && _aqconfig_.extended_device_id <= 0x43 ) { + _aqconfig_.extended_device_id = 0x00; + //_aqconfig_.enable_iaqualink = false; + //_aqconfig_.read_RS485_devmask &= ~ READ_RS485_IAQUALNK; + //firstprobe = 0x00; + foundIDs--; + } + } } } caculate_ack_packet(rs_fd, packet, ALLBUTTON); @@ -833,14 +845,14 @@ bool auto_configure(unsigned char* packet, int rs_fd) { _aqconfig_.rssa_device_id = lastID; LOG(AQUA_LOG,LOG_NOTICE, "Found valid unused RSSA ID 0x%02hhx\n",lastID); foundIDs++; - } else if ( (lastID >= 0x40 && lastID <= 0x43) && + } else if ( (lastID >= 0x40 && lastID <= 0x43) && ignore_OneTouch == false && (_aqconfig_.extended_device_id == 0x00 || _aqconfig_.extended_device_id == 0xFF) ) { _aqconfig_.extended_device_id = lastID; _aqconfig_.extended_device_id_programming = true; // Don't increase foundIDs as we prefer not to use this one. LOG(AQUA_LOG,LOG_NOTICE, "Found valid unused extended ID 0x%02hhx\n",lastID); } else if ( (lastID >= 0x30 && lastID <= 0x33) && ignore_AqualinkTouch == false && - (_aqconfig_.extended_device_id < 0x30 || _aqconfig_.extended_device_id > 0x33)) { //Overide is it's been set to Touch or not set. + (_aqconfig_.extended_device_id < 0x30 || _aqconfig_.extended_device_id > 0x33)) { //Overide if it's been set to Touch or not set. _aqconfig_.extended_device_id = lastID; _aqconfig_.extended_device_id_programming = true; if (!seen_iAqualink2) { @@ -1205,19 +1217,30 @@ void main_loop() blank_read = 0; if (i++ > 1000) { if(!got_probe) { - LOG(AQUA_LOG,LOG_ERR, "No probe on '0x%02hhx', giving up! (please check config)\n",_aqconfig_.device_id); + if (_aqconfig_.deamonize) { + LOG(AQUA_LOG,LOG_ERR, "No probe on device_id '0x%02hhx', Can't start! (please check config)\n",_aqconfig_.device_id); + i=0; + } else { + LOG(AQUA_LOG,LOG_ERR, "No probe on device_id '0x%02hhx', giving up! (please check config)\n",_aqconfig_.device_id); + stopPacketLogger(); + close_serial_port(rs_fd); + stop_net_services(); + stop_sensors_thread(); + return; + } } if(!got_probe_rssa) { - LOG(AQUA_LOG,LOG_ERR, "No probe on '0x%02hhx', giving up! (please check config)\n",_aqconfig_.rssa_device_id); + LOG(AQUA_LOG,LOG_ERR, "No probe on '0x%02hhx', disabling rssa_device_id (please check config)\n",_aqconfig_.rssa_device_id); + _aqconfig_.rssa_device_id = 0x00; + got_probe_rssa = true; } if(!got_probe_extended) { - LOG(AQUA_LOG,LOG_ERR, "No probe on '0x%02hhx', giving up! (please check config)\n",_aqconfig_.extended_device_id); + LOG(AQUA_LOG,LOG_ERR, "No probe on '0x%02hhx', disabling extended_device_id (please check config)\n",_aqconfig_.extended_device_id); + _aqconfig_.extended_device_id = 0x00; + _aqconfig_.extended_device_id_programming = false; + _aqconfig_.enable_iaqualink = false; + got_probe_extended = true; } - stopPacketLogger(); - close_serial_port(rs_fd); - stop_net_services(); - stop_sensors_thread(); - return; } } } diff --git a/source/color_lights.h b/source/color_lights.h index f373c57..1868b86 100644 --- a/source/color_lights.h +++ b/source/color_lights.h @@ -7,6 +7,7 @@ #define LIGHT_COLOR_NAME 16 #define LIGHT_COLOR_OPTIONS 19 +//#define LIGHT_DIMER_OPTIONS 4 //#define LIGHT_COLOR_TYPES LC_DIMMER+1 // The status returned from RS Serial Adapter has this added as a base. diff --git a/source/config.c b/source/config.c index 4771ea8..531da13 100644 --- a/source/config.c +++ b/source/config.c @@ -1712,6 +1712,7 @@ void check_print_config (struct aqualinkdata *aqdata) { int i, j; char name[MAX_PRINTLEN]; + bool dzset = true; // Sanity checks @@ -1897,12 +1898,26 @@ void check_print_config (struct aqualinkdata *aqdata) _cfgParams[i].config_mask |= CFG_GREYED_OUT; } - // Don't show PDA stuff on RS panel + // Don't show PDA stuff in config editor on RS panel if ( strcmp(_cfgParams[i].name, CFG_N_pda_sleep_mode) == 0 && !isPDA_PANEL) { _cfgParams[i].config_mask |= CFG_GREYED_OUT; _cfgParams[i].config_mask |= CFG_READONLY; } + // Don't print domoticz settings if off. + if ( strstr(_cfgParams[i].name, "dzidx" ) != NULL || + strcmp(_cfgParams[i].name, CFG_N_convert_dz_temp) == 0 ) { + if (!dzset || ( _cfgParams[i].value_type == CFG_INT && *(int *)_cfgParams[i].value_ptr <= 0) ) { + continue; + } + } else if ( strcmp(_cfgParams[i].name, CFG_N_mqtt_dz_sub_topic) == 0 || + strcmp(_cfgParams[i].name, CFG_N_mqtt_dz_pub_topic) == 0) { + if (*(char **)_cfgParams[i].value_ptr == NULL) { + dzset = false; + continue; + } + } + rsm_nchar_replace(name, MAX_PRINTLEN, _cfgParams[i].name, "_", " "); switch (_cfgParams[i].value_type) { case CFG_STRING: diff --git a/source/devices_pentair.c b/source/devices_pentair.c index ad22a0a..f09effa 100644 --- a/source/devices_pentair.c +++ b/source/devices_pentair.c @@ -70,6 +70,15 @@ bool processPentairPacket(unsigned char *packet, int packet_length, struct aqual aqdata->pumps[i].status = (packet[PEN_HI_B_STATUS] * 256) + packet[PEN_LO_B_STATUS]; aqdata->pumps[i].pressureCurve = packet[PEN_PPC]; + // This is for RS485 mode only (no info from OneTouch or iAqualinkTouch) + if (!isONET_ENABLED && !isIAQT_ENABLED) { + if ( /*aqdata->pumps[i].mode > 0 ||*/ aqdata->pumps[i].rpm > 0 || aqdata->pumps[i].gpm > 0 || aqdata->pumps[i].watts > 0) { + aqdata->pumps[i].pStatus = PS_OK; + } else { + aqdata->pumps[i].pStatus = PS_OFF; + } + } + changedAnything = true; break; } diff --git a/source/json_messages.c b/source/json_messages.c index f98f6ab..ccfa6f0 100644 --- a/source/json_messages.c +++ b/source/json_messages.c @@ -556,11 +556,12 @@ int build_device_JSON(struct aqualinkdata *aqdata, char* buffer, int size, bool temperatureUOM uom = getTemperatureUOM(aqdata->sensors[i].uom); if (uom == UNKNOWN) { - length += sprintf(buffer+length, "{\"type\": \"value\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"on\", \"value\": \"%.*f\" },", + length += sprintf(buffer+length, "{\"type\": \"value\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"on\", \"value\": \"%.*f\", \"uom\": \"%s\" },", aqdata->sensors[i].ID, aqdata->sensors[i].label, 2, - aqdata->sensors[i].value); + aqdata->sensors[i].value, + aqdata->sensors[i].uom); } else if ( !homekit && (aqdata->temp_units == FAHRENHEIT && uom == CELSIUS) ) { length += sprintf(buffer+length, "{\"type\": \"temperature\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"on\", \"value\": \"%.*f\" },", aqdata->sensors[i].ID, diff --git a/source/version.h b/source/version.h index 1379665..a7069be 100644 --- a/source/version.h +++ b/source/version.h @@ -4,5 +4,5 @@ #define AQUALINKD_SHORT_NAME "AqualinkD" // Use Magor . Minor . Patch -#define AQUALINKD_VERSION "2.6.9" +#define AQUALINKD_VERSION "2.6.10" \ No newline at end of file diff --git a/web/controller.html b/web/controller.html index 05baf00..85a7d38 100644 --- a/web/controller.html +++ b/web/controller.html @@ -1030,7 +1030,9 @@ if (value == undefined || value.startsWith("-999") || value.startsWith(" ")) { value = '--'; } else { - if ((type = document.getElementById(id).getAttribute('type')) != null) { + if ((uom = document.getElementById(id).getAttribute('UOM')) != null) { + ext = uom; + } else if ((type = document.getElementById(id).getAttribute('type')) != null) { if (type == 'temperature' || type == 'setpoint_thermo' || type == 'setpoint_freeze' || type == 'setpoint_chiller') ext = '°'; else if (type == 'setpoint_swg') @@ -1059,6 +1061,13 @@ tile.innerHTML = value + ext; } + function setTileUOM(id, uom) { + try { + type = document.getElementById(id).setAttribute('UOM', uom); + console.log("Set UOM to "+uom+" for "+id); + } catch (e) {} + } + function setTileOnText(id, text) { try { var tile = document.getElementById(id); @@ -1347,6 +1356,10 @@ } } else if (object.type == 'value' || object.type == 'temperature') { add_tile(object.id, object.name, object.state, 'value', object.type); + + if (object.uom !== undefined) { + setTileUOM(object.id, object.uom); + } setTileValue(object.id, object.value); } else if (object.type == 'setpoint_thermo' || object.type == 'setpoint_swg' || object.type == 'setpoint_freeze' || object.type == 'setpoint_chiller') { add_tile(object.id, object.name, object.state, 'thermostat', object.type);