diff --git a/README.md b/README.md index 6198f8e..0c06d0b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # Aqualinkd Linux daemon to control Aqualink RS pool controllers. Provides web UI, MQTT client & HTTP API endpoints. Control your pool equipment from any phone/tablet or computer. Is also compatible with most Home control systems including Apple HomeKit, Home Assistant, Samsung, Alexa, Google, etc. +
Binaries are supplied for Raspberry Pi both 32 & 64 bit OS, Has been, and can be compiled for many different SBC's, and a Docker is also available. diff --git a/release/aqualinkd-arm64 b/release/aqualinkd-arm64 index 0b9da61..f2b2acf 100755 Binary files a/release/aqualinkd-arm64 and b/release/aqualinkd-arm64 differ diff --git a/release/aqualinkd-armhf b/release/aqualinkd-armhf index 4296d94..a037168 100755 Binary files a/release/aqualinkd-armhf and b/release/aqualinkd-armhf differ diff --git a/release/serial_logger-arm64 b/release/serial_logger-arm64 index 3028af5..ea486bb 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 ea93ba8..787dc98 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 2b5aede..4732c4a 100644 --- a/source/allbutton_aq_programmer.c +++ b/source/allbutton_aq_programmer.c @@ -499,14 +499,14 @@ void *set_allbutton_light_colormode( void *ptr ) use_current_mode = true; LOG(ALLB_LOG, LOG_INFO, "Light Programming #: %d, on button: %s, color light type: %d, using current mode\n", val, button->label, ((clight_detail *)button->special_mask_ptr)->lightType); } else { - mode_name = light_mode_name(((clight_detail *)button->special_mask_ptr)->lightType, val-1, ALLBUTTON); + mode_name = light_mode_name(((clight_detail *)button->special_mask_ptr)->lightType, val, ALLBUTTON); // NSF DO NOT -1 from value (check WS logic if you do) use_current_mode = false; if (mode_name == NULL) { LOG(ALLB_LOG, LOG_ERR, "Light Programming #: %d, on button: %s, color light type: %d, couldn't find mode name '%s'\n", val, button->label, ((clight_detail *)button->special_mask_ptr)->lightType, mode_name); cleanAndTerminateThread(threadCtrl); return ptr; } else { - LOG(ALLB_LOG, LOG_INFO, "Light Programming #: %d, on button: %s, color light type: %d, name '%s'\n", val, button->label, ((clight_detail *)button->special_mask_ptr)->lightType, mode_name); + LOG(ALLB_LOG, LOG_INFO, "Light Programming #: %d, on button: %s, color light type: %d, color name '%s'\n", val, button->label, ((clight_detail *)button->special_mask_ptr)->lightType, mode_name); } } /* diff --git a/source/aq_panel.c b/source/aq_panel.c index 6a4d6fd..1ee7b0e 100644 --- a/source/aq_panel.c +++ b/source/aq_panel.c @@ -28,10 +28,17 @@ #include "allbutton_aq_programmer.h" #include "rs_msg_utils.h" #include "iaqualink.h" +#include "color_lights.h" + + +#define USE_LAST_VALUE 101 void initPanelButtons(struct aqualinkdata *aqdata, bool rspda, int size, bool combo, bool dual); -void programDeviceLightMode(struct aqualinkdata *aqdata, int value, int button); + +void programDeviceLightMode(struct aqualinkdata *aqdata, int value, int button, bool expectMultiple, request_source source); 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); uint16_t getPanelBitmaskFromName(const char *str); @@ -458,6 +465,9 @@ uint16_t setPanelSupport(struct aqualinkdata *aqdata) if (aqdata->panel_rev[0] >= 82) // R in ascii aqdata->panel_support_options |= RSP_SUP_IAQL; + if (aqdata->panel_rev[0] >= 84) // T in ascii + aqdata->panel_support_options |= RSP_SUP_CLIT_RSSA; + if (aqdata->panel_rev[0] >= 87) {// W in ascii aqdata->panel_support_options |= RSP_SUP_PLAB; aqdata->panel_support_options |= RSP_SUP_IAQL3; @@ -1363,152 +1373,10 @@ int getWaterTemp(struct aqualinkdata *aqdata) -//bool setDeviceState(aqkey *button, bool isON) -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)) { - // Virtual Button with VSP is always on. - LOG(PANL_LOG, LOG_INFO, "received '%s' for '%s', virtual pump is always on, ignoring", (isON == false ? "OFF" : "ON"), button->name); - button->led->state = ON; - return false; - } - - if ((button->led->state == OFF && isON == false) || - (isON > 0 && (button->led->state == ON || button->led->state == FLASH || - button->led->state == ENABLE))) { - LOG(PANL_LOG, LOG_INFO, "received '%s' for '%s', already '%s', Ignoring\n", (isON == false ? "OFF" : "ON"), button->name, (isON == false ? "OFF" : "ON")); - //return false; - } else { - LOG(PANL_LOG, LOG_INFO, "received '%s' for '%s', turning '%s'\n", (isON == false ? "OFF" : "ON"), button->name, (isON == false ? "OFF" : "ON")); -#ifdef AQ_PDA - if (isPDA_PANEL) { - if (button->special_mask & PROGRAM_LIGHT && isPDA_IAQT) { - // AqualinkTouch in PDA mode, we can program light. (if turing off, use standard AQ_PDA_DEVICE_ON_OFF below) - programDeviceLightMode(aqdata, (isON?0:-1), deviceIndex); // -1 means off 0 means use current light mode - } else { - // If we are using AqualinkTouch with iAqualink enabled, we can send button on/off much faster using that. - if ( isPDA_IAQT && isIAQL_ACTIVE) { - set_iaqualink_aux_state(button, isON); - } else { -#ifdef NEW_AQ_PROGRAMMER - aq_programmer(AQ_PDA_DEVICE_ON_OFF, button, (isON == false ? OFF : ON), deviceIndex, aqdata); -#else - char msg[PTHREAD_ARG]; - sprintf(msg, "%-5d%-5d", deviceIndex, (isON == false ? OFF : ON)); - aq_programmer(AQ_PDA_DEVICE_ON_OFF, msg, aqdata); -#endif - } - } - } else -#endif - { - // 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) && isVBUTTON(button->special_mask)) { - programDeviceLightMode(aqdata, (isON?0:-1), deviceIndex); // -1 means off 0 means use current light mode - //aq_program(AQ_SET_IAQTOUCH_LIGHTCOLOR_MODE, button, (isON?0:-1), 0, aqdata); // should use this in teh furuter, or get programDeviceLightMode to call it. - } else 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. - 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 ( ((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)) { - // Virtual buttons only supported with Aqualink Touch - LOG(PANL_LOG, LOG_INFO, "Set state for Virtual Button %s code=0x%02hhx iAqualink2 enabled=%s\n",button->name, button->rssd_code, isIAQT_ENABLED?"Yes":"No"); - if (isIAQT_ENABLED) { - // If it's one of the pre-defined onces & iaqualink is enabled, we can set it easile with button. - - if ( isIAQL_ACTIVE && button->rssd_code && button->rssd_code != NUL) - { - //LOG(PANL_LOG, LOG_NOTICE, "********** USE iaqualink2 ********\n"); - set_iaqualink_aux_state(button, isON); - } else { -#ifdef NEW_AQ_PROGRAMMER - aq_programmer(AQ_SET_IAQTOUCH_DEVICE_ON_OFF, button, (isON == false ? OFF : ON), deviceIndex, aqdata); -#else - char msg[PTHREAD_ARG]; - sprintf(msg, "%-5d%-5d", deviceIndex, (isON == false ? OFF : ON)); - aq_programmer(AQ_SET_IAQTOUCH_DEVICE_ON_OFF, msg, aqdata); -#endif - 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); - //} else { - // LOG(PANL_LOG, LOG_ERR, "Configuration! do not understand code for Virtual Buttons"); - //} - } - } else { - LOG(PANL_LOG, LOG_ERR, "Can only use Aqualink Touch protocol for Virtual Buttons"); - } - } else if (isPLIGHT(button->special_mask) && isRSSA_ENABLED) { - // If off and program light, use the RS serial adapter since that is overiding the state now. - set_aqualink_rssadapter_aux_state(button, isON); - } else { - //set_iaqualink_aux_state(button, isON); - //set_aqualink_rssadapter_aux_state(button, isON); - aq_send_allb_cmd(button->code); - } - - - // If we turned off a aqualinkd programmable light, set the mode to 0 - if (isPLIGHT(button->special_mask) && ! isON && ((clight_detail *)button->special_mask_ptr)->lightType == LC_PROGRAMABLE ) { - updateButtonLightProgram(aqdata, 0, deviceIndex); - } - -#ifdef CLIGHT_PANEL_FIX - if (isPLIGHT(button->special_mask) && isRSSA_ENABLED) { - get_aqualink_rssadapter_button_status(button); - } -#endif - -// 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 && set_pre_state) { - if ((button->code == KEY_POOL_HTR || button->code == KEY_SPA_HTR || - button->code == KEY_EXT_AUX) && - isON > 0) { - button->led->state = ENABLE; // if heater and set to on, set pre-status to enable. - LOG(PANL_LOG, LOG_INFO, "Pre-set state of %s to enable\n",button->label); - //_aqualink_data->updated = true; - } else if (isRSSA_ENABLED || ((button->special_mask & PROGRAM_LIGHT) != PROGRAM_LIGHT)) { - button->led->state = (isON == false ? OFF : ON); // as long as it's not programmable light , pre-set to on/off - LOG(PANL_LOG, LOG_INFO, "Pre-set state of %s to %s\n",button->label,(isON == false ? "Off" : "On")); - //_aqualink_data->updated = true; - } - } -//#endif - } - } - return TRUE; -} - +/* + Basic programming that are not too involved, ie no Jandy bugs to overcome or aq_programmer can make decision on protocol to use. + Simply set them all as unactioned, and let the delayed_request code pick them up and pass to aq_programmer +*/ bool programDeviceValue(struct aqualinkdata *aqdata, action_type type, int value, int id, bool expectMultiple) // id is only valid for PUMP RPM { if (aqdata->unactioned.type != NO_ACTION && type != aqdata->unactioned.type) @@ -1562,224 +1430,359 @@ bool programDeviceValue(struct aqualinkdata *aqdata, action_type type, int value } -void programDeviceLightBrightness(struct aqualinkdata *aqdata, int value, int deviceIndex, bool expectMultiple, request_source source) +bool setDeviceState(struct aqualinkdata *aqdata, int deviceIndex, bool isON, request_source source) { - // If Value is 101 + 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)) { + // Virtual Button with VSP is always on. + LOG(PANL_LOG, LOG_INFO, "received '%s' for '%s', virtual pump is always on, ignoring", (isON == false ? "OFF" : "ON"), button->name); + button->led->state = ON; + return false; + } + + if ((button->led->state == OFF && isON == false) || + (isON > 0 && (button->led->state == ON || button->led->state == FLASH || + button->led->state == ENABLE))) { + LOG(PANL_LOG, LOG_INFO, "received '%s' for '%s', already '%s', Ignoring\n", (isON == false ? "OFF" : "ON"), button->name, (isON == false ? "OFF" : "ON")); + return false; + } + + LOG(PANL_LOG, LOG_INFO, "received '%s' for '%s', turning '%s'\n", (isON == false ? "OFF" : "ON"), button->name, (isON == false ? "OFF" : "ON")); +#ifdef AQ_PDA + if (isPDA_PANEL) { + if (button->special_mask & PROGRAM_LIGHT && isPDA_IAQT) { + // AqualinkTouch in PDA mode, we can program light. (if turing off, use standard AQ_PDA_DEVICE_ON_OFF below) + programDeviceLightMode(aqdata, (isON?USE_LAST_VALUE:0), deviceIndex, (source==NET_MQTT?true:false), source); + } else { + // If we are using AqualinkTouch with iAqualink enabled, we can send button on/off much faster using that. + if ( isPDA_IAQT && isIAQL_ACTIVE) { + set_iaqualink_aux_state(button, isON); + } else { + aq_programmer(AQ_PDA_DEVICE_ON_OFF, button, (isON == false ? OFF : ON), deviceIndex, aqdata); + } + } + } else +#endif + { + if (isPLIGHT(button->special_mask)) { + //programDeviceLightMode_(aqdata, (isON?USE_LAST_VALUE:0), deviceIndex ,(source==NET_MQTT?true:false), source); + // NSF we could let programDeviceLightMode() handle ALL these cases. + + if (isMASK_SET(button->special_mask, VIRTUAL_BUTTON)) { + // No quick way to turn on or off and virtual button light. + programDeviceLightMode(aqdata, (isON?USE_LAST_VALUE:0), deviceIndex ,(source==NET_MQTT?true:false), source); + } + else if ( ((clight_detail *)button->special_mask_ptr)->lightType == LC_PROGRAMABLE ) { + LOG(PANL_LOG,LOG_DEBUG, "Turning light %s %s with allbutton key\n", button->label, isON?"On":"Off"); + aq_send_allb_cmd(button->code); + if (isON) { // Will come back on in old state. + updateButtonLightProgram(aqdata, ((clight_detail *)button->special_mask_ptr)->lastValue, deviceIndex); + } else { + updateButtonLightProgram(aqdata, 0, deviceIndex); + } + set_pre_state = false; + } else if ( ((clight_detail *)button->special_mask_ptr)->lightType == LC_DIMMER2 || + ((clight_detail *)button->special_mask_ptr)->lightType == LC_DIMMER) { + if (isON) { // Dimmer light can get stuck turning on from RSSA, so use allbutton + LOG(PANL_LOG,LOG_DEBUG, "Turning light %s On with allbutton dimmer program\n", button->label); + aq_program(AQ_SET_ALLB_LIGHTDIMMER, button, 4, true, aqdata); // 4 = 100% since it uses light mode name + set_pre_state = false; + } else { + LOG(PANL_LOG,LOG_DEBUG, "Turning light %s Off with allbutton key\n", button->label); + aq_send_allb_cmd(button->code); + } + } else if (isRSSA_ENABLED) { + LOG(PANL_LOG,LOG_DEBUG, "Turning light %s %s with all RS serial on/off key\n", button->label, isON?"On":"Off"); + set_aqualink_rssadapter_aux_state(button, isON); + } else { + if (isON) { + LOG(PANL_LOG,LOG_DEBUG, "Turning light %s On with allbutton light mode program\n", button->label); + aq_programmer(AQ_SET_LIGHTCOLOR_MODE, button, 1, true, aqdata); + set_pre_state = false; + } else { + LOG(PANL_LOG,LOG_DEBUG, "Turning light %s Off with allbutton key\n", button->label); + aq_send_allb_cmd(button->code); + } + } + + } else if (isVBUTTON(button->special_mask)) { + // Virtual buttons only supported with Aqualink Touch + LOG(PANL_LOG, LOG_INFO, "Set state for Virtual Button %s code=0x%02hhx iAqualink2 enabled=%s\n",button->name, button->rssd_code, isIAQT_ENABLED?"Yes":"No"); + if (isIAQT_ENABLED) { + // If it's one of the pre-defined onces & iaqualink is enabled, we can set it easile with button. + + if ( isIAQL_ACTIVE && button->rssd_code && button->rssd_code != NUL) + { + //LOG(PANL_LOG, LOG_NOTICE, "********** USE iaqualink2 ********\n"); + set_iaqualink_aux_state(button, isON); + } else { + aq_programmer(AQ_SET_IAQTOUCH_DEVICE_ON_OFF, button, (isON == false ? OFF : ON), deviceIndex, aqdata); + set_pre_state = false; + } + } else { + LOG(PANL_LOG, LOG_ERR, "Can only use Aqualink Touch protocol for Virtual Buttons"); + } + } else { + // Everything else, simply send the button code. + //set_iaqualink_aux_state(button, isON); + //set_aqualink_rssadapter_aux_state(button, isON); + aq_send_allb_cmd(button->code); + } + } + +#ifdef CLIGHT_PANEL_FIX + if (isPLIGHT(button->special_mask) && isRSSA_ENABLED) { + get_aqualink_rssadapter_button_status(button); + } +#endif + +// 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 && set_pre_state) { + if ((button->code == KEY_POOL_HTR || button->code == KEY_SPA_HTR || + button->code == KEY_EXT_AUX) && + isON > 0) { + button->led->state = ENABLE; // if heater and set to on, set pre-status to enable. + LOG(PANL_LOG, LOG_INFO, "Pre-set state of %s to enable\n",button->label); + //_aqualink_data->updated = true; + //} else if (isRSSA_ENABLED || ((button->special_mask & PROGRAM_LIGHT) != PROGRAM_LIGHT)) { + } else if (isRSSA_ENABLED || !isPLIGHT(button->special_mask)) { + button->led->state = (isON == false ? OFF : ON); // as long as it's not programmable light , pre-set to on/off + LOG(PANL_LOG, LOG_INFO, "Pre-set state of %s to %s\n",button->label,(isON == false ? "Off" : "On")); + //_aqualink_data->updated = true; + } + } +//#endif + + + return TRUE; +} + + +/* + value 0 = off + value 101/USE_LAST_VALUE = On use default mode if you can + value is % for LC_DIMMER and LC_DIMMER2 +*/ +void programDeviceLightBrightness(struct aqualinkdata *aqdata, int value, int deviceIndex, bool expectMultiple, request_source source) +{ + //int extra_value = false; + + if (value < 0 || value > 100) { + LOG(PANL_LOG,LOG_ERR, "Dimmer value %d is not valid, using %d\n",value,AQ_CLAMP(value,0,100)); + value = AQ_CLAMP(value,0,100); + } + + if (expectMultiple) { + // Queue up a request, this will call us back through with expectMultiple=false + time(&aqdata->unactioned.requested); + aqdata->unactioned.value = value; + aqdata->unactioned.type = LIGHT_BRIGHTNESS; + aqdata->unactioned.id = deviceIndex; + return; + } 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) { - //Should probably try to get the last value in this case, and use the closest 25% rather than default to 100. (ie 4) - //int val = ((clight_detail *)button->special_mask_ptr)->lastValue>0?((clight_detail *)button->special_mask_ptr)->lastValue:100; - 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; - } - */ + if (light == NULL || (light->lightType != LC_DIMMER2 && light->lightType != LC_DIMMER)) { + LOG(PANL_LOG,LOG_ERR, "Can not set light brightness on device '%s'\n",aqdata->aqbuttons[deviceIndex].label); return; } + + if (!isRSSA_ENABLED && light->lightType == LC_DIMMER2) { + LOG(PANL_LOG,LOG_ERR, "Light mode brightness 11 is only supported when `rssa_device_id` is set\n"); + return; + } + + if (value == 0) { + // We simply need to turn the light off at this point, so use allbutton key as it's the quickest. + // but can't turn off a virtual light. + if (light->button->led->state == ON && !isMASK_SET(light->button->special_mask, VIRTUAL_BUTTON)) { + LOG(PANL_LOG,LOG_DEBUG, "Turning light %s Off with allbutton key\n", light->button->label); + aq_send_allb_cmd(light->button->code); + // Could also check isRSSA_ENABLED and use set_aqualink_rssadapter_aux_state(light->button, FALSE); + return; + } else if (light->button->led->state != ON ) { + LOG(PANL_LOG,LOG_WARNING, "Request to turn Light brightness '%s' to 0, already off, ignoring!\n",light->button->label); + return; + } + } + + /* + logic. + set brightness through RSSA. (RSSA only protocol supports full dimmer range) + RSSA has bug / issue. Sometimes panel will lock device forcing a panel delete/re-add of light. + to overcome this seems to be turn it on with allbutton, if it's on then use RSSA to set the %. + + if off turn on with allbutton. + + */ + - - if (!isRSSA_ENABLED) { - LOG(PANL_LOG,LOG_ERR, "Light mode brightness is only supported with `rssa_device_id` set\n"); - return; - } - - if (light == NULL) { - LOG(PANL_LOG,LOG_ERR, "Light mode control not configured for button %d\n",deviceIndex); - return; - } - - // 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) { - if (value <= 0) { - // Consider this a bad/malformed request to turn the light off. - panel_device_request(aqdata, ON_OFF, deviceIndex, 0, source); + if (aqdata->aqbuttons[deviceIndex].led->state == ON) { + // Light is on, Simple set brightness through RSSD if we can + if (isRSSA_ENABLED) { + LOG(PANL_LOG,LOG_DEBUG, "Turning light %s to %d with RS Serial extended state\n", light->button->label, value); + set_aqualink_rssadapter_aux_extended_state(light->button, value + RSSD_COLOR_LIGHT_OFFSET_WRITE); } else { - programDeviceLightMode(aqdata, value, deviceIndex); + LOG(PANL_LOG,LOG_DEBUG, "Turning light %s to %d with allbutton dimmer program\n", light->button->label, value); + aq_program(AQ_SET_ALLB_LIGHTDIMMER, &aqdata->aqbuttons[deviceIndex], dimmer_percent_to_mode_index(value), false, aqdata); + } + } else { + // Light is off, turn on with allbutton then reset the %. + if (value == USE_LAST_VALUE) { + // 101 is used last value, param #4 of true in ap_program means use default value if can, otherwise use param #3 = 4 = 100% + LOG(PANL_LOG,LOG_DEBUG, "Turning light %s to Last mode with allbutton dimmer program\n", light->button->label); + aq_program(AQ_SET_ALLB_LIGHTDIMMER, &aqdata->aqbuttons[deviceIndex], 4, true, aqdata); // 4 = 100% since it uses light mode name + } else { + int calVal = dimmer_percent_to_mode_index(value); + LOG(PANL_LOG,LOG_INFO, "Rounded dimmer value to %d for on command\n",calVal * 25); + LOG(PANL_LOG,LOG_DEBUG, "Turning light %s to %d with allbutton dimmer program\n", light->button->label, calVal); + aq_program(AQ_SET_ALLB_LIGHTDIMMER, &aqdata->aqbuttons[deviceIndex], calVal, false, aqdata); + if (value != 25 && value !=50 && value !=75 && value != 100 && isRSSA_ENABLED) { + // Setup the rssd to set the light to the right value. + time(&aqdata->unactioned.requested); + aqdata->unactioned.requested += 5; // This should give enough time for the allbutton to finish + aqdata->unactioned.value = value; + aqdata->unactioned.type = LIGHT_BRIGHTNESS; + aqdata->unactioned.id = deviceIndex; + } + } - return; } - time(&aqdata->unactioned.requested); - aqdata->unactioned.value = value; - aqdata->unactioned.type = LIGHT_MODE; - aqdata->unactioned.id = deviceIndex; - return; } - -//void programDeviceLightMode(struct aqualinkdata *aqdata, char *value, int button) -//void programDeviceLightMode(struct aqualinkdata *aqdata, int value, int button) -void programDeviceLightMode(struct aqualinkdata *aqdata, int value, int deviceIndex) +/* + value 0 = off + value 101/USE_LAST_VALUE = On use default mode if you can +*/ +void programDeviceLightMode(struct aqualinkdata *aqdata, int value, int deviceIndex, bool expectMultiple, request_source source) { - -#ifdef AQ_PDA - if (isPDA_PANEL && !isPDA_IAQT) { - LOG(PANL_LOG,LOG_ERR, "Light mode control not supported in PDA mode\n"); + int extra_value=false; + /*. We should never get multiple requests for mode, only brightness uses slider. But leave here incase we need it in the future + if (expectMultiple) { + // Queue up a request, this will call us back through with expectMultiple=false + time(&aqdata->unactioned.requested); + aqdata->unactioned.value = value; + aqdata->unactioned.type = LIGHT_MODE; + aqdata->unactioned.id = deviceIndex; return; } -#endif - - - /* - int i; - clight_detail *light = NULL; - for (i=0; i < aqdata->num_lights; i++) { - if (&aqdata->aqbuttons[button] == aqdata->lights[i].button) { - // Found the programmable light - light = &aqdata->lights[i]; - break; - } - }*/ + */ clight_detail *light = getProgramableLight(aqdata, deviceIndex); if (light == NULL) { - LOG(PANL_LOG,LOG_ERR, "Light mode control not configured for button %d\n",deviceIndex); + LOG(PANL_LOG,LOG_ERR, "Light mode control not configured for button %d\n", deviceIndex); return; } - if (isMASK_SET(light->button->special_mask, VIRTUAL_BUTTON)) { - // We can only program a light on virtual button on iaqtouch or onetouch - if (isIAQT_ENABLED ) { -#ifdef NEW_AQ_PROGRAMMER - aq_programmer(AQ_SET_IAQTOUCH_LIGHTCOLOR_MODE, light->button, value, AQP_NULL, aqdata); -#else - char buf[LIGHT_MODE_BUFER]; - sprintf(buf, "%-5d%-5d%-5d",value, deviceIndex, light->lightType); - aq_programmer(AQ_SET_IAQTOUCH_LIGHTCOLOR_MODE, buf, aqdata); -#endif - } else if (isONET_ENABLED ) { - LOG(PANL_LOG,LOG_ERR, "Light mode on virtual button not implimented on OneTouch protocol (needs AqualinkTouch)\n"); - } else { - LOG(PANL_LOG,LOG_ERR, "Light mode on virtual button needs AqualinkTouch protocol\n"); - } - } else if (light->lightType == LC_PROGRAMABLE ) { -#ifdef NEW_AQ_PROGRAMMER - aq_programmer(AQ_SET_LIGHTPROGRAM_MODE, light->button, value, AQP_NULL, aqdata); -#else - char buf[LIGHT_MODE_BUFER]; - sprintf(buf, "%-5d%-5d%-5d%-5d%.2f",value, - deviceIndex, - _aqconfig_.light_programming_initial_on, - _aqconfig_.light_programming_initial_off, - _aqconfig_.light_programming_mode ); - aq_programmer(AQ_SET_LIGHTPROGRAM_MODE, buf, aqdata); -#endif - } else if (isRSSA_ENABLED ) { - // If we are using rs-serial then turn light on first. - if (light->button->led->state != ON) { - set_aqualink_rssadapter_aux_state(light->button, TRUE); - //set_aqualink_rssadapter_aux_extended_state(light->button, RS_SA_ON); - //set_aqualink_rssadapter_aux_extended_state(light->button, 100); - // Add a few delays to slow it down. 0 is get status - //set_aqualink_rssadapter_aux_extended_state(light->button, 0); - //set_aqualink_rssadapter_aux_extended_state(light->button, 0); - } - if (light->lightType == LC_DIMMER) { - // 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); - set_aqualink_rssadapter_aux_extended_state(light->button, 100); - } - } else { - // Dimmer or any color light can simply be set with value - set_aqualink_rssadapter_aux_extended_state(light->button, value + 128); - } - /* - } else if (isRSSA_ENABLED && light->lightType == LC_DIMMER2) { - // Dimmer needs to be turned on before you set dimmer level - if (light->button->led->state != ON) { - set_aqualink_rssadapter_aux_extended_state(light->button, RS_SA_ON); - } - set_aqualink_rssadapter_aux_extended_state(light->button, value); - } else if (isRSSA_ENABLED && light->lightType == LC_DIMMER) { - // Dimmer needs to be turned on first - if (light->button->led->state != ON) { - set_aqualink_rssadapter_aux_extended_state(light->button, RS_SA_ON); - } - // Value 1 = 25, 2 = 50, 3 = 75, 4 = 100 (need to convert value into binary) - if (value >= 1 && value <= 4) { - // If value is not on of those vales, then ignore - unsigned char rssd_value = value * 25; - 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); - } - } else if (isRSSA_ENABLED && light->lightType != LC_PROGRAMABLE) { - // Any programmable COLOR light (that's programmed by panel) - set_aqualink_rssadapter_aux_extended_state(light->button, value);*/ - } else { -#ifdef NEW_AQ_PROGRAMMER - aq_programmer(AQ_SET_LIGHTCOLOR_MODE, light->button, value, AQP_NULL, aqdata); -#else - char buf[LIGHT_MODE_BUFER]; - sprintf(buf, "%-5d%-5d%-5d",value, deviceIndex, light->lightType); - aq_programmer(AQ_SET_LIGHTCOLOR_MODE, buf, aqdata); -#endif + if (! is_valid_light_mode(light->lightType, value)) { + LOG(PANL_LOG,LOG_ERR, "Light mode '%d' is not valid for light '%s', %s\n", value, lightTypeName(light->lightType), light->button->label); + return; } + if (light->lightType == LC_DIMMER2 || light->lightType == LC_DIMMER) {// DIMMER + programDeviceLightBrightness(aqdata, (light->lightType== LC_DIMMER?dimmer_mode_to_percent(value):value), deviceIndex, expectMultiple, source); + return; + } + + // Turn on a light with no mode set. + if (value == USE_LAST_VALUE) { + extra_value = true; + value = 1; // Do we need to reset this? + // Anything but VIRTUAL_BUTTON we can turn on with no mode simply. + if (!isMASK_SET(light->button->special_mask, VIRTUAL_BUTTON)) { + // for LC_PROGRAMMABLE is a simple ON command, so send it and be done. + if (light->lightType == LC_PROGRAMABLE) { + aq_send_allb_cmd(light->button->code); + light->currentValue = light->lastValue; // will come back on in last mode, so set that. + // easiest way is to turn on with RSSA then no questions asked. + } else if (isRSSA_ENABLED) { + set_aqualink_rssadapter_aux_state(light->button, true); + } else { + aq_programmer(AQ_SET_LIGHTCOLOR_MODE, light->button, 1, extra_value, aqdata); + } + return; + } + } else if (value == 0) { + // We simply need to turn the light off at this point, so use allbutton key as it's the quickest. + // but can't turn off a virtual light. + if (light->button->led->state == ON && !isMASK_SET(light->button->special_mask, VIRTUAL_BUTTON)) { + //DPRINTF("allbutton off"); + aq_send_allb_cmd(light->button->code); + // Could also check isRSSA_ENABLED and use set_aqualink_rssadapter_aux_state(light->button, FALSE); + return; + } else if (light->button->led->state != ON ) { + LOG(PANL_LOG,LOG_WARNING, "Request to turn off Light mode '%s' to off, already off, ignoring!\n",light->button->label); + return; + } + } + + // Logic, select one of 3 programming options, RSSD / AllButton / AqualinkTouch + // virtual button light can only be done with AqualinkTouch + // LC_PROGRAMABLE can only be done with AllButton + // user can set a default + // dimmer light NEEDS to be turned on with allbutton. + + // Check Virtual Button requires IAQT protocol + if (isMASK_SET(light->button->special_mask, VIRTUAL_BUTTON) && !isIAQT_ENABLED) + { + // Log an error and stop execution if a Virtual Button is used without IAQT. + LOG(PANL_LOG, LOG_ERR, "Light mode on virtual button needs AqualinkTouch protocol\n"); + return; + } + // Use allbutton if LC_PROGRAMABLE light, or no other protocol options + else if (light->lightType == LC_PROGRAMABLE) + { + //DPRINTF("AQ_SET_LIGHTPROGRAM_MODE"); + aq_programmer(AQ_SET_LIGHTPROGRAM_MODE, light->button, value, extra_value, aqdata); + } + // Use allbutton if explicitly set, or no other protocol options + else if ((!isRSSA_ENABLED && !isIAQT_ENABLED) || + (_aqconfig_.light_programming_interface == LIGHT_PROTOCOL_ALLB)) + { + //DPRINTF("AQ_SET_ALLB_LIGHTCOLOR_MODE"); + aq_programmer(AQ_SET_ALLB_LIGHTCOLOR_MODE, light->button, value, extra_value, aqdata); + } + // Use AqualinkTouch if explicitly set or virtual button. + else if (isIAQT_ENABLED && + (_aqconfig_.light_programming_interface == LIGHT_PROTOCOL_AQLT || + isMASK_SET(light->button->special_mask, VIRTUAL_BUTTON))) + { + //DPRINTF("AQ_SET_IAQTOUCH_LIGHTCOLOR_MODE"); + // This depends on the button label being accurate with panel. + aq_programmer(AQ_SET_IAQTOUCH_LIGHTCOLOR_MODE, light->button, value, extra_value, aqdata); + } + // Use RS-Serial Adapter protocol + else if (isRSSA_ENABLED) + { + //DPRINTF("set_aqualink_rssadapter_aux_state"); + unsigned char rssd_value = value + RSSD_COLOR_LIGHT_OFFSET_WRITE; + // If light is off, turn it on first + if (light->button->led->state != ON) + { + set_aqualink_rssadapter_aux_state(light->button, TRUE); + } + // Adjust the value for Dimmer lights (map color index to full range %). + if (light->lightType == LC_DIMMER || light->lightType == LC_DIMMER2) + { + rssd_value = value * 25; // Dimmer is full range % on RSSD + } + set_aqualink_rssadapter_aux_extended_state(light->button, rssd_value); + } + // Default Fallback (Should rarely be reached) + else { + //DPRINTF("AQ_SET_LIGHTCOLOR_MODE"); + aq_programmer(AQ_SET_LIGHTCOLOR_MODE, light->button, value, extra_value, aqdata); + } + + // Use function so can be called from programming thread if we decide to in future. if (light->lightType != LC_PROGRAMABLE ) { // Only update if a panel programed light. If AqualinkD programs, the programmer needs to know the last mode. @@ -1787,6 +1790,7 @@ void programDeviceLightMode(struct aqualinkdata *aqdata, int value, int deviceIn } } + /* deviceIndex = button index on button list (or pumpIndex for VSP action_types) value = value to set (0=off 1=on, or value for setpoints / rpm / timer) action_type will depend on this value @@ -1833,6 +1837,7 @@ bool panel_device_request(struct aqualinkdata *aqdata, action_type type, int dev start_timer(aqdata, deviceIndex, value); break; case LIGHT_BRIGHTNESS: + // Allow value=0 here (unlike LIGHT_MODE) since we could get multiple requests from a slider. (aka HomeKit) programDeviceLightBrightness(aqdata, value, deviceIndex, (source==NET_MQTT?true:false), source); break; case LIGHT_MODE: @@ -1840,7 +1845,7 @@ bool panel_device_request(struct aqualinkdata *aqdata, action_type type, int dev // Consider this a bad/malformed request to turn the light off. panel_device_request(aqdata, ON_OFF, deviceIndex, 0, source); } else { - programDeviceLightMode(aqdata, value, deviceIndex); + programDeviceLightMode(aqdata, value, deviceIndex, (source==NET_MQTT?true:false), source); } break; case POOL_HTR_SETPOINT: diff --git a/source/aq_panel.h b/source/aq_panel.h index 07a605a..376baf4 100644 --- a/source/aq_panel.h +++ b/source/aq_panel.h @@ -57,7 +57,7 @@ const uint32_t PANEL_COMPARISON_MASK = (1U << 12) - 1; // We only care about bi #define RSP_SUP_PLAB (1 << 11) // Pump VSP by Label and not number #define RSP_SUP_HPCHIL (1 << 12) // Heat Pump chiller #define RSP_SUP_PCDOC (1 << 13) // PC Dock - +#define RSP_SUP_CLIT_RSSA (1 << 14) // Color Lights can be set over serial adapter. #define PANEL_CPU (1 << 0) #define PANEL_REV (1 << 1) diff --git a/source/aq_programmer.c b/source/aq_programmer.c index 7fa3bc7..fc6360a 100644 --- a/source/aq_programmer.c +++ b/source/aq_programmer.c @@ -73,6 +73,10 @@ const func_ptr _prog_functions[AQP_RSSADAPTER_MAX] = { [AQ_SET_SWG_PERCENT] = set_allbutton_SWG, [AQ_GET_AUX_LABELS] = get_allbutton_aux_labels, [AQ_SET_BOOST] = set_allbutton_boost, + // Specific one to force all button for light programming + [AQ_SET_ALLB_LIGHTCOLOR_MODE] = set_allbutton_light_colormode, + [AQ_SET_ALLB_LIGHTDIMMER] = set_allbutton_light_dimmer, + [AQ_SET_ONETOUCH_PUMP_RPM] = set_aqualink_onetouch_pump_rpm, [AQ_GET_ONETOUCH_FREEZEPROTECT] = get_aqualink_onetouch_freezeprotect, [AQ_GET_ONETOUCH_SETPOINTS] = get_aqualink_onetouch_setpoints, @@ -909,9 +913,11 @@ const char *ptypeName(program_type type) return "Set light color (using AqualinkD)"; break; case AQ_SET_LIGHTCOLOR_MODE: - return "Set light color (using Panel)"; + case AQ_SET_ALLB_LIGHTCOLOR_MODE: + return "Set light color"; break; case AQ_SET_LIGHTDIMMER: + case AQ_SET_ALLB_LIGHTDIMMER: return "Set light dimmer"; break; case AQ_SET_SWG_PERCENT: @@ -1004,7 +1010,7 @@ const char *ptypeName(program_type type) return "Set AqualinkTouch OneTouch On/Off"; break; case AQ_SET_IAQTOUCH_LIGHTCOLOR_MODE: - return "Set AqualinkTouch Light Color (using panel)"; + return "Set AqualinkTouch Light Mode"; break; case AQ_SET_IAQTOUCH_SWG_BOOST: return "Set AqualinkTouch Boost"; @@ -1123,6 +1129,8 @@ const char *programtypeDisplayName(program_type type) case AQ_SET_LIGHTCOLOR_MODE: case AQ_SET_IAQTOUCH_LIGHTCOLOR_MODE: case AQ_SET_LIGHTDIMMER: + case AQ_SET_ALLB_LIGHTDIMMER: + case AQ_SET_ALLB_LIGHTCOLOR_MODE: return "Programming: setting light mode"; break; case AQ_SET_SWG_PERCENT: diff --git a/source/aq_programmer.h b/source/aq_programmer.h index 021e1a6..5ba619c 100644 --- a/source/aq_programmer.h +++ b/source/aq_programmer.h @@ -68,6 +68,9 @@ typedef enum { AQ_SET_PUMP_RPM, AQ_SET_PUMP_VS_PROGRAM, AQ_SET_CHILLER_TEMP, + // **** Specific allputon light program. (maybe create all all button in future) + AQ_SET_ALLB_LIGHTCOLOR_MODE, + AQ_SET_ALLB_LIGHTDIMMER, // ******** PDA Delimiter make sure to change MAX/MIN below AQ_PDA_INIT, AQ_PDA_WAKE_INIT, diff --git a/source/aqualink.h b/source/aqualink.h index 4aa56df..5c34dac 100644 --- a/source/aqualink.h +++ b/source/aqualink.h @@ -10,8 +10,8 @@ #include "sensors.h" //#include "aq_panel.h" // Moved to later in file to overcome circular dependancy. (crappy I know) -//#define PRINTF(format, ...) printf("%s:%d: " format, __FILE__, __LINE__, ##__VA_ARGS__) -#define PRINTF(format, ...) +//#define DPRINTF(format, ...) printf("%s:%d: " format, __FILE__, __LINE__, ##__VA_ARGS__) +//#define DPRINTF(format, ...) #define isMASK_SET(bitmask, mask) ((bitmask & mask) == mask) #define setMASK(bitmask, mask) (bitmask |= mask) diff --git a/source/aqualinkd.c b/source/aqualinkd.c index eaf8645..0234c75 100644 --- a/source/aqualinkd.c +++ b/source/aqualinkd.c @@ -434,6 +434,9 @@ void action_delayed_request() else if (_aqualink_data.unactioned.type == LIGHT_MODE) { panel_device_request(&_aqualink_data, LIGHT_MODE, _aqualink_data.unactioned.id, _aqualink_data.unactioned.value, UNACTION_TIMER); } + else if (_aqualink_data.unactioned.type == LIGHT_BRIGHTNESS) { + panel_device_request(&_aqualink_data, LIGHT_BRIGHTNESS, _aqualink_data.unactioned.id, _aqualink_data.unactioned.value, UNACTION_TIMER); + } else { LOG(AQUA_LOG,LOG_ERR, "Unknown request of type %d\n", _aqualink_data.unactioned.type); diff --git a/source/color_lights.c b/source/color_lights.c index f495062..2d34e06 100644 --- a/source/color_lights.c +++ b/source/color_lights.c @@ -43,7 +43,7 @@ char *_color_light_options[NUMBER_LIGHT_COLOR_TYPES][LIGHT_COLOR_OPTIONS] = "Magenta", "Garnet Red", "Violet", - "Color Splash" + "Color Splash" // 0x4b }, { // 2 = Jandy LED "Off", @@ -182,6 +182,28 @@ void setColorLightsPanelVersion(uint8_t supported) set = true; } */ + +bool is_valid_light_mode(clight_type type, int index) +{ + printf("Checking _color_light_options[%d][%d]\n",type,index); + + if (type == LC_DIMMER2) { + if (index >= 0 && index <=100) { + return true; + } else { + return false; + } + } + + if (index < 0 || index > LIGHT_COLOR_OPTIONS || _color_light_options[type][index] == NULL ){ + return false; + } + + printf("result = %s\n", _color_light_options[type][index]); + + return true; +} + void clear_aqualinkd_light_modes() { //_color_light_options[0] = _aqualinkd_custom_colors; @@ -271,6 +293,8 @@ const char *get_currentlight_mode_name(clight_detail light, emulation_type proto return _color_light_options[light.lightType][light.currentValue]; } + + // This should not be uses for getting current lightmode name since it doesn;t have full logic const char *light_mode_name(clight_type type, int index, emulation_type protocol) @@ -346,6 +370,68 @@ bool set_currentlight_value(clight_detail *light, int index) return rtn; } + // Take a % and return 0,1,2,3,4 for 0=0, 1=25, 2=50, 3=75, 4=100, ie the index of _color_light_options[10][??] +int dimmer_percent_to_mode_index(int value) +{ + /* + value = round(value / 25); + if (value > 4 ) {value=4;} + */ + value = round( (value+12) / 25); // Round up/down + if (value > 4 ) {value=4;} + + return value; +} + +int dimmer_mode_to_percent(int value) +{ + return value * 25; +} + + +const char* lightTypeName(clight_type type) +{ + switch (type) { + case LC_PROGRAMABLE: + return "AqualinkD Programmable"; + break; + case LC_JANDY: + return "Jandy Color"; + break; + case LC_JANDYLED: + return "Jandy LED"; + break; + case LC_SAL: + return "SAm/SAL"; + break; + case LC_CLOGIG: + return "Color Logic"; + break; + case LC_INTELLIB: + return "Intelibrite"; + break; + case LC_HAYWCL: + return "Haywood Universal"; + break; + case LC_JANDYINFINATE: + return "Jandy Infinite"; + break; + case LC_DIMMER: + return "Dimmer"; + break; + case LC_DIMMER2: + return "Dimmer (full range)"; + break; + + default: + case LC_SPARE_2: + case LC_SPARE_3: + return "unknown"; + break; + } + return "unknown"; +} + // Used for dynamic config JS int build_color_lights_js(struct aqualinkdata *aqdata, char* buffer, int size) { diff --git a/source/color_lights.h b/source/color_lights.h index edfdcd9..d6f7f2d 100644 --- a/source/color_lights.h +++ b/source/color_lights.h @@ -11,9 +11,13 @@ //#define LIGHT_COLOR_TYPES LC_DIMMER+1 // The status returned from RS Serial Adapter has this added as a base. -#define RSSD_COLOR_LIGHT_OFFSET 64 +#define RSSD_COLOR_LIGHT_OFFSET_WRITE 128 // when writing its 128 +#define RSSD_COLOR_LIGHT_OFFSET_READ 64 // when reading is 64 #define RSSD_DIMMER_LIGHT_OFFSET 128 +// When selecting a color light, the Button press keycode add this offset. +#define IAQ_COLOR_LIGHT_OFFSET 16 + //#define DIMMER_LIGHT_TYPE_INDEX 10 /* @@ -24,7 +28,7 @@ typedef enum clight_type { LC_JANDYLED, LC_SAL, LC_CLOGIG, - LC_INTELLIB + LC_INTELLIB, } clight_type; */ //const char *light_mode_name(clight_type type, int index); @@ -32,9 +36,12 @@ const char *get_currentlight_mode_name(clight_detail light, emulation_type proto const char *light_mode_name(clight_type type, int index, emulation_type protocol); int build_color_lights_js(struct aqualinkdata *aqdata, char* buffer, int size); int build_color_light_jsonarray(int index, char* buffer, int size); - +int dimmer_percent_to_mode_index(int value); +int dimmer_mode_to_percent(int value); void clear_aqualinkd_light_modes(); bool set_currentlight_value(clight_detail *light, int index); +bool is_valid_light_mode(clight_type type, int index); +const char* lightTypeName(clight_type type); bool set_aqualinkd_light_mode_name(char *name, int index, bool isShow); const char *get_aqualinkd_light_mode_name(int index, bool *isShow); diff --git a/source/config.c b/source/config.c index 6e2806d..bb20b98 100644 --- a/source/config.c +++ b/source/config.c @@ -264,6 +264,7 @@ void init_parameters (struct aqconfig * parms) _cfgParams[_numCfgParams].name = CFG_N_rssa_device_id; _cfgParams[_numCfgParams].valid_values = CFG_V_rssa_device_id; _cfgParams[_numCfgParams].default_value = (void *)&_dcfg_unknownHex; + _cfgParams[_numCfgParams].config_mask |= CFG_FORCE_RESTART; _numCfgParams++; _cfgParams[_numCfgParams].value_ptr = &_aqconfig_.extended_device_id_programming; @@ -279,6 +280,7 @@ void init_parameters (struct aqconfig * parms) _cfgParams[_numCfgParams].name = CFG_N_extended_device_id; _cfgParams[_numCfgParams].valid_values = CFG_V_extended_device_id; _cfgParams[_numCfgParams].default_value = (void *)&_dcfg_unknownHex; + _cfgParams[_numCfgParams].config_mask |= CFG_FORCE_RESTART; _numCfgParams++; _cfgParams[_numCfgParams].value_ptr = &_aqconfig_.enable_iaqualink; @@ -286,6 +288,7 @@ void init_parameters (struct aqconfig * parms) _cfgParams[_numCfgParams].name = CFG_N_enable_iaqualink; _cfgParams[_numCfgParams].valid_values = CFG_V_BOOL; _cfgParams[_numCfgParams].default_value = (void *)&_dcfg_false; + _cfgParams[_numCfgParams].config_mask |= CFG_FORCE_RESTART; #ifndef AQ_MANAGER _numCfgParams++; @@ -311,6 +314,7 @@ void init_parameters (struct aqconfig * parms) _cfgParams[_numCfgParams].name = CFG_N_mqtt_user; _cfgParams[_numCfgParams].default_value = (void *)NULL; _cfgParams[_numCfgParams].config_mask |= CFG_ALLOW_BLANK; + _cfgParams[_numCfgParams].config_mask |= CFG_FORCE_RESTART; _numCfgParams++; _cfgParams[_numCfgParams].value_ptr = &_aqconfig_.mqtt_passwd; @@ -319,6 +323,7 @@ void init_parameters (struct aqconfig * parms) _cfgParams[_numCfgParams].config_mask |= CFG_PASSWD_MASK; _cfgParams[_numCfgParams].default_value = (void *)NULL; _cfgParams[_numCfgParams].config_mask |= CFG_ALLOW_BLANK; + _cfgParams[_numCfgParams].config_mask |= CFG_FORCE_RESTART; _numCfgParams++; _cfgParams[_numCfgParams].value_ptr = &_aqconfig_.mqtt_aq_topic; @@ -326,6 +331,7 @@ void init_parameters (struct aqconfig * parms) _cfgParams[_numCfgParams].name = CFG_N_mqtt_aq_topic; _cfgParams[_numCfgParams].default_value = (void *)_dcfg_mqtt_aq_tp; _cfgParams[_numCfgParams].config_mask |= CFG_ALLOW_BLANK; + _cfgParams[_numCfgParams].config_mask |= CFG_FORCE_RESTART; _numCfgParams++; _cfgParams[_numCfgParams].value_ptr = &_aqconfig_.mqtt_discovery_topic; @@ -333,12 +339,14 @@ void init_parameters (struct aqconfig * parms) _cfgParams[_numCfgParams].name = CFG_N_mqtt_discovery_topic; _cfgParams[_numCfgParams].default_value = (void *)_dcfg_mqtt_discovery; _cfgParams[_numCfgParams].config_mask |= CFG_ALLOW_BLANK; + _cfgParams[_numCfgParams].config_mask |= CFG_FORCE_RESTART; _numCfgParams++; _cfgParams[_numCfgParams].value_ptr = &_aqconfig_.mqtt_discovery_use_mac; _cfgParams[_numCfgParams].value_type = CFG_BOOL; _cfgParams[_numCfgParams].name = CFG_N_mqtt_discovery_use_mac; _cfgParams[_numCfgParams].default_value = (void *)&_dcfg_true; + _cfgParams[_numCfgParams].config_mask |= CFG_FORCE_RESTART; #if MG_TLS > 0 @@ -372,6 +380,14 @@ void init_parameters (struct aqconfig * parms) _cfgParams[_numCfgParams].config_mask |= CFG_GRP_ADVANCED; _cfgParams[_numCfgParams].default_value = (void *)&_dcfg_light_programming_mode; + _numCfgParams++; + _cfgParams[_numCfgParams].value_ptr = &_aqconfig_.light_programming_interface; + _cfgParams[_numCfgParams].value_type = CFG_INT; + _cfgParams[_numCfgParams].name = CFG_N_light_programming_interface; + _cfgParams[_numCfgParams].config_mask |= CFG_GRP_ADVANCED; + _cfgParams[_numCfgParams].config_mask |= CFG_READONLY; + _cfgParams[_numCfgParams].default_value = (void *)&_dcfg_zero; + _numCfgParams++; _cfgParams[_numCfgParams].value_ptr = &_aqconfig_.light_programming_initial_on; _cfgParams[_numCfgParams].value_type = CFG_INT; @@ -1751,6 +1767,39 @@ void check_print_config (struct aqualinkdata *aqdata) } } + // Color lights check infinate water color (RS485, is set as vbutton) + for (i=1; i < aqdata->num_lights; i++) { + if (aqdata->lights[i].lightType == LC_JANDYINFINATE) { + // Check we have a virtual light button + if ( !isMASK_SET(aqdata->lights[i].button->special_mask, VIRTUAL_BUTTON)) { + LOG(AQUA_LOG,LOG_WARNING, "Config error, Light mode %d is only valid for a virtual button\n",LC_JANDYINFINATE); + } + } + } + + // Check valid setting + if (_aqconfig_.light_programming_interface > LIGHT_PROTOCOL_AQLD) { + if (_aqconfig_.device_id == 0x60) { + LOG(AQUA_LOG,LOG_WARNING, "Config error, %s set to %d, not supported in PDA mode",CFG_N_light_programming_interface,_aqconfig_.light_programming_interface); + _aqconfig_.light_programming_interface = LIGHT_PROTOCOL_AQLD; + } + + if(_aqconfig_.light_programming_interface == LIGHT_PROTOCOL_AQLT) { + if (! is_aqualink_touch_id(_aqconfig_.extended_device_id)) { + LOG(AQUA_LOG,LOG_WARNING, "Config error, %s set to AqualinkTouch, %s is not set for AqualinkTouch ID",CFG_N_light_programming_interface,CFG_N_extended_device_id); + _aqconfig_.light_programming_interface = LIGHT_PROTOCOL_AQLD; + } + // Below not supported yet + //} else if(_aqconfig_.light_programming_interface == LIGHT_PROTOCOL_ONET) { + // if (! is_onetouch_id(_aqconfig_.extended_device_id)) { + // LOG(AQUA_LOG,LOG_WARNING, "Config error, %s set to OneTouch, %s is not set for OneTouch ID",CFG_N_light_programming_interface,CFG_N_extended_device_id); + // _aqconfig_.light_programming_interface = LIGHT_PROTOCOL_AQLD; + // } + } else if ( _aqconfig_.light_programming_interface > LIGHT_PROTOCOL_ALLB ) { + LOG(AQUA_LOG,LOG_WARNING, "Config error, %s set to %d, not supported",CFG_N_light_programming_interface,_aqconfig_.light_programming_interface); + _aqconfig_.light_programming_interface = LIGHT_PROTOCOL_AQLD; + } + } /* PDA Mode diff --git a/source/config.h b/source/config.h index ac6c51b..cdec5ed 100644 --- a/source/config.h +++ b/source/config.h @@ -40,6 +40,22 @@ #define MAX_RSSD_LOG_FILTERS 4 +/* +#define LIGHT_PROGRAM_INTERFACE_AQLD 0 +#define LIGHT_PROGRAM_INTERFACE_ALLB 1 +#define LIGHT_PROGRAM_INTERFACE_ONET 2 +#define LIGHT_PROGRAM_INTERFACE_AQLT 3 +#define LIGHT_PROGRAM_INTERFACE_IAQL 4 +*/ +// Going to explicitly set these numbers since they come from config file +typedef enum { + LIGHT_PROTOCOL_AQLD = 0, + LIGHT_PROTOCOL_ALLB = 1, + LIGHT_PROTOCOL_ONET = 2, + LIGHT_PROTOCOL_AQLT = 3, + LIGHT_PROTOCOL_IAQL = 4 +} light_program_interface; + struct aqconfig { #if MG_TLS > 0 @@ -77,6 +93,7 @@ struct aqconfig int light_programming_initial_on; int light_programming_initial_off; bool light_programming_advance_mode; + light_program_interface light_programming_interface; // 0=let AqualinkD decide, 1=allbutton, 2=onetouch(N/A), 3=AqualinkTouch, 4=iaqualink(N/A) bool override_freeze_protect; #ifdef AQ_PDA bool pda_sleep_mode; @@ -277,11 +294,12 @@ int _numCfgParams; #define CFG_N_override_freeze_protect "override_freeze_protect" #define CFG_N_pda_sleep_mode "pda_sleep_mode" #define CFG_N_convert_mqtt_temp "mqtt_convert_temp_to_c" +#define CFG_N_light_programming_interface "light_programming_interface" #define CFG_N_report_zero_spa_temp "report_zero_spa_temp" #define CFG_N_report_zero_pool_temp "report_zero_pool_temp" #define CFG_N_read_RS485_devmask "read_RS485_devmask" -#define CFG_N_use_panel_aux_labels "use_panel_aux_labels" +//#define CFG_N_use_panel_aux_labels "use_panel_aux_labels" #define CFG_N_force_swg "force_swg" #define CFG_N_force_ps_setpoints "force_ps_setpoints" #define CFG_N_force_frzprotect_setpoints "force_frzprotect_setpoints" @@ -308,7 +326,7 @@ int _numCfgParams; #define CFG_N_event_check_freezeprotectoff "event_freezeprotectoff_check_pump" #define CFG_N_event_check_boostoff "event_boostoff_check_pump" #define CFG_N_event_check_pumpon_hour "event_check_pumpon_hour" -#define CFG_N_event_check_pumpoff_hour "event_check_pumpoff_hour" +#define CFG_N_event_check_pumpoff_hour "event_check_pumpoff_hour" #define CFG_N_event_check_usecron "event_check_use_scheduler_times" #define CFG_N_event_check_booston_device "event_booston_check_device" diff --git a/source/devices_jandy.c b/source/devices_jandy.c index 535c249..00215b7 100644 --- a/source/devices_jandy.c +++ b/source/devices_jandy.c @@ -53,11 +53,19 @@ typedef enum heatpumpmsgfrom{ void updateHeatPumpLed(heatpumpstate state, aqledstate ledstate, struct aqualinkdata *aqdata, heatpumpmsgfrom from); +bool shouldPrintJAndyDebugPacket() +{ + if (getLogLevel(DJAN_LOG) == LOG_DEBUG && getLogLevel(RSSD_LOG) < LOG_DEBUG ) { + return true; + } + return false; +} void printJandyDebugPacket (const char *msg, const unsigned char *packet, int packet_length) { // Only log if we are jandy debug mode and not serial debug (otherwise it'll print twice) - if (getLogLevel(DJAN_LOG) == LOG_DEBUG && getLogLevel(RSSD_LOG) < LOG_DEBUG ) { + //if (getLogLevel(DJAN_LOG) == LOG_DEBUG && getLogLevel(RSSD_LOG) < LOG_DEBUG ) { + if (shouldPrintJAndyDebugPacket()) { char frame[1024]; //beautifyPacket(frame, 1024, packet, packet_length, true); @@ -914,28 +922,40 @@ bool processPacketFromJandyLXHeater(unsigned char *packet_buffer, int packet_len bool processPacketToJandyChemFeeder(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata) { + /* char msg[1024]; int length = 0; length += sprintf(msg+length, "Last panel info "); - length += sprintf(msg+length, ", pH=%f, ORP=%d",aqdata->ph, aqdata->orp); - LOG(DJAN_LOG, LOG_INFO, "%s\n", msg); +*/ + LOG(DJAN_LOG, LOG_INFO, "Last panel info : pH=%f, ORP=%d",aqdata->ph, aqdata->orp); + if (!shouldPrintJAndyDebugPacket()) { + // if we are not going to print the packet as debug, print it here + logPacket(DJAN_LOG, LOG_INFO, packet_buffer, packet_length, true); + } + return false; } bool processPacketFromJandyChemFeeder(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata, const unsigned char previous_packet_to){ +/* char msg[1024]; int length = 0; length += sprintf(msg+length, "Last panel info "); - length += sprintf(msg+length, ", pH=%f, ORP=%d",aqdata->ph, aqdata->orp); - LOG(DJAN_LOG, LOG_INFO, "%s\n", msg); - +*/ + LOG(DJAN_LOG, LOG_INFO, "Last panel info : pH=%f, ORP=%d",aqdata->ph, aqdata->orp); + + if (!shouldPrintJAndyDebugPacket()) { + // if we are not going to print the packet as debug, print it here + logPacket(DJAN_LOG, LOG_INFO, packet_buffer, packet_length, true); + } + /* I think the below may be accurate ph_setpoint = float(raw_data[8]) / 10 @@ -1214,7 +1234,10 @@ bool processPacketToJandyLight(unsigned char *packet_buffer, int packet_length, LOG(DJAN_LOG, LOG_INFO, "Request to set Jandy Light RGB to %d:%d:%d\n", packet_buffer[6],packet_buffer[7],packet_buffer[8]); } - logPacket(DJAN_LOG, LOG_INFO, packet_buffer, packet_length, true); + if (!shouldPrintJAndyDebugPacket()) { + // if we are not going to print the packet as debug, print it here + logPacket(DJAN_LOG, LOG_INFO, packet_buffer, packet_length, true); + } return true; } @@ -1226,7 +1249,10 @@ bool processPacketFromJandyLight(unsigned char *packet_buffer, int packet_length LOG(DJAN_LOG, LOG_INFO, "Light brightness=%d\n", packet_buffer[38]); } - logPacket(DJAN_LOG, LOG_INFO, packet_buffer, packet_length, true); + if (!shouldPrintJAndyDebugPacket()) { + // if we are not going to print the packet as debug, print it here + logPacket(DJAN_LOG, LOG_INFO, packet_buffer, packet_length, true); + } return true; } diff --git a/source/iaqtouch.c b/source/iaqtouch.c index 503a765..c7e8ad0 100644 --- a/source/iaqtouch.c +++ b/source/iaqtouch.c @@ -356,6 +356,8 @@ void updateAQButtonFromPageButton(struct aqualinkdata *aqdata, struct iaqt_page_ break; case 0x01: SET_IF_CHANGED(aqdata->aqbuttons[i].led->state, ON, aqdata->is_dirty); + //NSF might want to check virtual light here, if so remove the color mode name, since + // we didnt turn the light on, we cant state the color mode break; case 0x02: SET_IF_CHANGED(aqdata->aqbuttons[i].led->state, FLASH, aqdata->is_dirty); @@ -976,7 +978,7 @@ bool process_iaqtouch_packet(unsigned char *packet, int length, struct aqualinkd // Set disply message if PDA panel memset(_popupMsg, 0, AQ_MSGLONGLEN + 1); rsm_strncpy(_popupMsg, packet + 6, AQ_MSGLONGLEN, length-9); - LOG(IAQT_LOG,LOG_NOTICE, "Popup message '%s'\n",_popupMsg); + LOG(IAQT_LOG,LOG_INFO, "Popup message '%s'\n",_popupMsg); // Change this message, since you can't press OK. 'Light will turn off in 5 seconds. To change colors press Ok now.' if ((sp = rsm_strncasestr(_popupMsg, "To change colors press Ok now", strlen(_popupMsg))) != NULL) diff --git a/source/iaqtouch_aq_programmer.c b/source/iaqtouch_aq_programmer.c index 07741ae..34f6ca4 100644 --- a/source/iaqtouch_aq_programmer.c +++ b/source/iaqtouch_aq_programmer.c @@ -771,7 +771,7 @@ void *set_aqualink_iaqtouch_light_colormode( void *ptr ) // See if it's on the current page pButton = iaqtFindButtonByLabel(key->label); -PRINTF("First button find = %s\n",pButton==NULL?"null":pButton->name); +//DPRINTF("First button find = %s\n",pButton==NULL?"null":pButton->name); if (pButton == NULL) { // No luck, go to the device page @@ -780,22 +780,24 @@ PRINTF("First button find = %s\n",pButton==NULL?"null":pButton->name); pButton = iaqtFindButtonByLabel(key->label); -PRINTF("Second button find = %s\n",pButton==NULL?"null":pButton->name); +//DPRINTF("Second button find = %s\n",pButton==NULL?"null":pButton->name); // If not found see if page has next if (pButton == NULL && iaqtFindButtonByIndex(16)->type == 0x03 ) { iaqt_queue_cmd(KEY_IAQTCH_NEXT_PAGE); waitfor_iaqt_nextPage(aqdata); // This will fail, since not looking at device page 2 buttons pButton = iaqtFindButtonByLabel(key->label); -PRINTF("Third button find = %s\n",pButton==NULL?"null":pButton->name); +//DPRINTF("Third button find = %s\n",pButton==NULL?"null":pButton->name); } } if (pButton == NULL) { - LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did not find '%s' button on device list\n", key->label); + LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did not find '%s' button on device list, please check your config line '%s'\n", + key->label, + isMASK_SET(key->special_mask, VIRTUAL_BUTTON)?"virtual_button_??_label":"button_??_label" ); goto f_end; } -PRINTF("FOUND button = %s\n",pButton==NULL?"null":pButton->name); +//DPRINTF("FOUND button = %s\n",pButton==NULL?"null":pButton->name); // WE have a iaqualink button, press it. LOG(IAQT_LOG, LOG_DEBUG, "IAQ Touch found '%s' sending keycode '0x%02hhx'\n", key->label, pButton->keycode); send_aqt_cmd(pButton->keycode); @@ -805,18 +807,23 @@ PRINTF("FOUND button = %s\n",pButton==NULL?"null":pButton->name); // After pressing the button, Just need to wait for 5 seconds and it will :- // a) if off turn on and default to last color. // b) if on, turn off. (pain that we need to wait 5 seconds.) -PRINTF("******** WAIT for next page\n"); +//DPRINTF("******** WAIT for next page\n"); waitfor_iaqt_queue2empty(); waitfor_iaqt_nextPage(aqdata); if (use_current_mode) { // Their is no message for this, so give one. - sprintf(aqdata->last_display_message, "Light will turn on in 5 seconds"); + sprintf(aqdata->last_display_message, "Light will turn %s in 5 seconds", turn_off?"off":"on"); aqdata->is_display_message_programming = true; SET_DIRTY(aqdata->is_dirty); } // Wait for next page maybe? // Below needs a timeout. while (waitfor_iaqt_nextPage(aqdata) == IAQ_PAGE_COLOR_LIGHT); + + // Pre set key to off since it'll take ages + if (turn_off) + key->led->state = OFF; + goto f_end; } @@ -841,22 +848,26 @@ PRINTF("******** WAIT for next page\n"); } LOG(IAQT_LOG, LOG_DEBUG, "IAQ Touch found '%s' sending keycode '0x%02hhx' = '0x%02hhx' for light selection\n", mode_name, pButton->keycode, pButton->keycode+16); - PRINTF("******** current page is '0x%02hhx'\n",iaqtCurrentPage()); + //DPRINTF("******** current page is '0x%02hhx'\n",iaqtCurrentPage()); // NSF Key code is +16 for some reason. ie key 0x07=(send 0x17). 0x0a=(send 0x1a) - send_aqt_cmd(pButton->keycode + 16); + send_aqt_cmd(pButton->keycode + IAQ_COLOR_LIGHT_OFFSET); waitfor_iaqt_queue2empty(); // Wait for popup message to disapera // This is iAq Popup messag | HEX: 0x10|0x02|0x33|0x2c|0x00|0x01|0x50|0x6c|0x65|0x61|0x73|0x65|0x20|0x77|0x61|0x69|0x74|0x2e|0x2e|0x2e|0x0a|0x20|0x43|0x79|0x63|0x6c|0x69|0x6e|0x67|0x20|0x74|0x6f|0x20|0x63|0x68|0x6f|0x73|0x65|0x6e|0x20|0x63|0x6f|0x6c|0x6f|0x72|0x2e|0x00|0x00|0x00|0x00|0x2e|0x10|0x03| // This is popup message clear | HEX: 0x10|0x02|0x33|0x2c|0x00|0x00|0x20|0x00|0x00|0x00|0x00|0x91|0x10|0x03| // 5th bit 0x00 = clear. (anything else message) + // Pre set light to on, since it'll take ages after this finished. + if (!turn_off) + key->led->state = ON; + unsigned char msg; int i=0; while ( (msg = waitfor_iaqt_nextMessage(aqdata, CMD_IAQ_MSG_LONG)) != NUL) { const char *pmsg = iaqtPopupMsg(); - PRINTF("******** popupMsg = %s ********\n",pmsg); + //DPRINTF("******** popupMsg = %s ********\n",pmsg); if (pmsg[0] == ' ') { - PRINTF("******** popupMsg clear ********\n"); + //DPRINTF("******** popupMsg clear ********\n"); break; } if (++i > 5) { @@ -866,9 +877,9 @@ PRINTF("******** WAIT for next page\n"); } /* unsigned char page; - PRINTF("******** current page is '0x%02hhx'\n",iaqtCurrentPage()); + DPRINTF("******** current page is '0x%02hhx'\n",iaqtCurrentPage()); while ( (page = waitfor_iaqt_nextPage(aqdata)) != NUL) { - PRINTF("******** next page is '0x%02hhx'\n",page); // page = 0x48 IAQ_PAGE_COLOR_LIGHT + DPRINTF("******** next page is '0x%02hhx'\n",page); // page = 0x48 IAQ_PAGE_COLOR_LIGHT } */ //LOG(IAQT_LOG, LOG_ERR, "IAQ Touch WAIYING FOR 1 MESSAGES\n"); diff --git a/source/iaqtouch_aq_programmer.h b/source/iaqtouch_aq_programmer.h index 0a2b4df..50f428b 100644 --- a/source/iaqtouch_aq_programmer.h +++ b/source/iaqtouch_aq_programmer.h @@ -4,7 +4,6 @@ #define IAQ_TOUCH_PROGRAMMER_H_ - unsigned char pop_iaqt_cmd(unsigned char receive_type); void set_iaq_cansend(bool cansend); diff --git a/source/json_messages.c b/source/json_messages.c index 9b23147..fd3953d 100644 --- a/source/json_messages.c +++ b/source/json_messages.c @@ -1077,6 +1077,7 @@ bool parseJSONrequest(char *buffer, struct JSONkvptr *request) return true; } +/* bool parseJSONwebrequest(char *buffer, struct JSONwebrequest *request) { int i=0; @@ -1148,7 +1149,7 @@ bool parseJSONwebrequest(char *buffer, struct JSONwebrequest *request) return true; } - +*/ /* int json_cfg_element_OLD(char* buffer, int size, const char *name, const void *value, cfg_value_type type, char *valid_val) { int result = 0; diff --git a/source/json_messages.h b/source/json_messages.h index 0f82596..0b69c02 100644 --- a/source/json_messages.h +++ b/source/json_messages.h @@ -49,7 +49,7 @@ const char* getAqualinkDStatusMessage(struct aqualinkdata *aqdata); int build_aqualink_status_JSON(struct aqualinkdata *aqdata, char* buffer, int size); int build_aux_labels_JSON(struct aqualinkdata *aqdata, char* buffer, int size); -bool parseJSONwebrequest(char *buffer, struct JSONwebrequest *request); +//bool parseJSONwebrequest(char *buffer, struct JSONwebrequest *request); bool parseJSONrequest(char *buffer, struct JSONkvptr *request); int build_logmsg_JSON(char *dest, int loglevel, const char *src, int dest_len, int src_len); int build_mqtt_status_JSON(char* buffer, int size, int idx, int nvalue, float setpoint/*char *svalue*/); diff --git a/source/net_services.c b/source/net_services.c index 01e42eb..8eadca4 100644 --- a/source/net_services.c +++ b/source/net_services.c @@ -65,6 +65,14 @@ struct mg_connection *mg_next(struct mg_mgr *s, struct mg_connection *conn) { tolower((unsigned char)(str)[(len)-1]) == tolower((unsigned char)(SUFFIX)[2]) \ ) +#define FAST_SUFFIX_4_CI(str, len, SUFFIX) ( \ + (len) >= 4 && \ + tolower((unsigned char)(str)[(len)-4]) == tolower((unsigned char)(SUFFIX)[0]) && \ + tolower((unsigned char)(str)[(len)-3]) == tolower((unsigned char)(SUFFIX)[1]) && \ + tolower((unsigned char)(str)[(len)-2]) == tolower((unsigned char)(SUFFIX)[2]) && \ + tolower((unsigned char)(str)[(len)-1]) == tolower((unsigned char)(SUFFIX)[3]) \ +) + /* #if defined AQ_DEBUG || defined AQ_TM_DEBUG @@ -1611,7 +1619,8 @@ void action_web_request(struct mg_connection *nc, struct mg_http_message *http_m // If we have a get request, pass it if (strncmp(http_msg->uri.buf, "/api", 4 ) != 0) { DEBUG_TIMER_START(&tid); - if ( FAST_SUFFIX_3_CI(http_msg->uri.buf, http_msg->uri.len, ".js") ) { + //if ( FAST_SUFFIX_3_CI(http_msg->uri.buf, http_msg->uri.len, ".js") ) { + if ( FAST_SUFFIX_4_CI(http_msg->uri.buf, http_msg->uri.len, "json") ) { mg_http_serve_dir(nc, http_msg, &_http_server_opts_nocache); } else { mg_http_serve_dir(nc, http_msg, &_http_server_opts); @@ -1754,6 +1763,8 @@ void action_websocket_request(struct mg_connection *nc, struct mg_ws_message *wm int i; char *uri = NULL; char *value = NULL; + //char *id = NULL; + //char *text_value = NULL; char *msg = NULL; #ifdef AQ_TM_DEBUG int tid; @@ -1775,8 +1786,12 @@ void action_websocket_request(struct mg_connection *nc, struct mg_ws_message *wm if (jsonkv.kv[i].key != NULL && strncmp(jsonkv.kv[i].key, "uri", 3) == 0) uri = jsonkv.kv[i].value; - else if (jsonkv.kv[i].key != NULL && strncmp(jsonkv.kv[i].key, "value", 4) == 0) + else if (jsonkv.kv[i].key != NULL && strncmp(jsonkv.kv[i].key, "value", 5) == 0) value = jsonkv.kv[i].value; + //else if (jsonkv.kv[i].key != NULL && strncmp(jsonkv.kv[i].key, "button", 6) == 0) + // id = jsonkv.kv[i].value; + //else if (jsonkv.kv[i].key != NULL && strncmp(jsonkv.kv[i].key, "text_value", 10) == 0) + // text_value = jsonkv.kv[i].value; } if (uri == NULL) { @@ -1784,6 +1799,7 @@ void action_websocket_request(struct mg_connection *nc, struct mg_ws_message *wm return; } + // NSF in future change action_URI to accept button and text_value switch ( action_URI(NET_WS, uri, strlen(uri), (value!=NULL?atof(value):TEMP_UNKNOWN), false, &msg)) { case uActioned: sprintf(buffer, "{\"message\":\"ok\"}"); diff --git a/source/serialadapter.c b/source/serialadapter.c index 821cdaf..c54cd84 100644 --- a/source/serialadapter.c +++ b/source/serialadapter.c @@ -372,7 +372,7 @@ bool process_rssadapter_packet(unsigned char *packet, int length, struct aqualin aqdata->lights[i].button->label, packet[6]==0x00?"OFF":"ON", packet[6], - packet[6]==0x00?"--":light_mode_name( aqdata->lights[i].lightType,(packet[6] - RSSD_COLOR_LIGHT_OFFSET), RSSADAPTER) ); + packet[6]==0x00?"--":light_mode_name( aqdata->lights[i].lightType,(packet[6] - RSSD_COLOR_LIGHT_OFFSET_READ), RSSADAPTER) ); } SET_IF_CHANGED(aqdata->lights[i].RSSDstate, (packet[6]==0x00?OFF:ON), aqdata->is_dirty); @@ -395,7 +395,7 @@ bool process_rssadapter_packet(unsigned char *packet, int length, struct aqualin rtn |= set_currentlight_value(&aqdata->lights[i], (packet[6] - RSSD_DIMMER_LIGHT_OFFSET)); break; default: - rtn |= set_currentlight_value(&aqdata->lights[i], (packet[6] - RSSD_COLOR_LIGHT_OFFSET)); + rtn |= set_currentlight_value(&aqdata->lights[i], (packet[6] - RSSD_COLOR_LIGHT_OFFSET_READ)); break; } /* diff --git a/source/utils.h b/source/utils.h index b048c7e..b940e85 100644 --- a/source/utils.h +++ b/source/utils.h @@ -73,6 +73,7 @@ typedef int32_t logmask_t; #define AQ_MAX(x, y) (((x) > (y)) ? (x) : (y)) #define AQ_MIN(x, y) (((x) < (y)) ? (x) : (y)) +#define AQ_CLAMP(value, min_val, max_val) ((value) > (max_val) ? (max_val) : ((value) < (min_val) ? (min_val) : (value))) /* diff --git a/source/version.h b/source/version.h index c1b47a4..e1562a2 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 "3.0.0 (beta 2)" +#define AQUALINKD_VERSION "3.0.0 (beta 3)" \ No newline at end of file diff --git a/web/aqmanager.html b/web/aqmanager.html index c99bcd6..65ae90e 100644 --- a/web/aqmanager.html +++ b/web/aqmanager.html @@ -518,24 +518,27 @@