3.0.0 (beta 3)

pull/492/head v3.0.0-beta.3
sfeakes 2025-12-03 17:11:02 -06:00
parent 295f267d93
commit 60d0a41aef
30 changed files with 794 additions and 537 deletions

View File

@ -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.
<br>
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.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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);
}
}
/*

View File

@ -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:

View File

@ -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)

View File

@ -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:

View File

@ -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,

View File

@ -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)

View File

@ -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);

View File

@ -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)
{

View File

@ -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);

View File

@ -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

View File

@ -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"

View File

@ -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;
}

View File

@ -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)

View File

@ -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");

View File

@ -4,7 +4,6 @@
#define IAQ_TOUCH_PROGRAMMER_H_
unsigned char pop_iaqt_cmd(unsigned char receive_type);
void set_iaq_cansend(bool cansend);

View File

@ -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;

View File

@ -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*/);

View File

@ -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\"}");

View File

@ -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;
}
/*

View File

@ -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)))
/*

View File

@ -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)"

View File

@ -518,24 +518,27 @@
</style>
<script type='text/javascript'>
'use strict';
var _panel_size = 6;
var _panel_set = 0;
var _currentVersion = 0;
var _latestVersionAvailable = 0;
var _latestDevVersionAvailable = 0;
//var _latestDevVersionAvailable = 0;
var _rssd_logmask = 0;
const RSSD_MASK_ID = 512; // Must match RSSD_LOG in utils.c
_addedBlankLightProgram = false;
_addedBlankSensor = false;
_addedBlankVirtualButton = false;
var _addedBlankLightProgram = false;
var _addedBlankSensor = false;
var _addedBlankVirtualButton = false;
const _webConfigFile = "/config.json";
//var _cfgRestartAlertShown = false;
let _AlertsShown = {};
var _config = {};
const VERSION_URL = "https://api.github.com/repos/aqualinkd/AqualinkD/releases"
let _aqualinkVersions = {};
const _urlParams = new URLSearchParams(window.location.search);
@ -587,6 +590,13 @@
button.classList.remove("disablebutton");
}
}
function setbuttontext(id, text) {
//console.log("enable " + id);
var button = document.getElementById(id);
if (button) {
button.value = text;
}
}
function disabletoggle(id) {
disablebutton(id);
try {
@ -894,7 +904,7 @@
//console.log(data[obj]["valid values"]);
//const
input = document.createElement("select");
array = data[obj]["valid values"];
const array = data[obj]["valid values"];
for (var i = 0; i < array.length; i++) {
var option = document.createElement("option");
option.value = array[i];
@ -994,7 +1004,7 @@
//}
const buttonTypelist = ["pumpIndex", "pumpID", "pumpType", "pumpName", "pumpMaxSpeed", "pumpMinSpeed", "lightMode"];
const virtButtonTypelist = ["pumpIndex", "pumpID", "pumpType", "pumpName", "pumpMaxSpeed", "pumpMinSpeed", "onetouchID", "altLabel"];
const virtButtonTypelist = ["pumpIndex", "pumpID", "pumpType", "pumpName", "pumpMaxSpeed", "pumpMinSpeed", "lightMode", "onetouchID", "altLabel"];
const configtable = document.getElementById("config_table");
const newRow = configtable.insertRow();
@ -1060,7 +1070,7 @@
var cell3 = newRow.insertCell();
cell1.style.paddingLeft = "20px";
key = 'light_program_'+String(num+1).padStart(2, '0')
const key = 'light_program_'+String(num+1).padStart(2, '0')
cell1.innerHTML = 'Add '+key;
cell1.setAttribute('label', key);
@ -1093,14 +1103,14 @@
return;
}
const virtButtonTypelist = ["pumpIndex", "pumpID", "pumpType", "pumpName", "pumpMaxSpeed", "pumpMinSpeed", "onetouchID", "altLabel"];
const virtButtonTypelist = ["pumpIndex", "pumpID", "pumpType", "pumpName", "pumpMaxSpeed", "pumpMinSpeed", "lightMode", "onetouchID", "altLabel"];
const configtable = document.getElementById("config_table");
vbname="virtual_button_"+String(num).padStart(2, '0');
vblabel=vbname+"_label";
const vbname="virtual_button_"+String(num).padStart(2, '0');
const vblabel=vbname+"_label";
newRow = configtable.insertRow();
var newRow = configtable.insertRow();
var cell1 = newRow.insertCell();
cell1.colSpan = 3;
cell1.style.fontSize = "small";
@ -1273,7 +1283,7 @@
newRow.style.borderTop = "1px solid black";
}
key = 'sensor_'+String(num+1).padStart(2, '0');
const key = 'sensor_'+String(num+1).padStart(2, '0');
// Add sensor_xx_path row
var cell1 = newRow.insertCell();
@ -1496,7 +1506,7 @@
const configtable = document.getElementById("config_table");
showAdvanced = document.getElementById("show_advanced").checked;
var showAdvanced = document.getElementById("show_advanced").checked;
//console.log (data);
if (data != null)
_config = data;
@ -1620,9 +1630,11 @@
function cfgAlertForceRestart(event) {
var key = event.srcElement.getAttribute('key');
var value = event.srcElement.value;
//console.log( _AlertsShown[key]);
var key = event.srcElement.getAttribute('key');
if (key === undefined || key === null) {
key = event.srcElement.parentNode.getAttribute('key');
}
if ( _AlertsShown[key] == undefined || _AlertsShown[key] == false) {
_AlertsShown[key] = true;
if ("force_restart_msg" in _config[key]) {
@ -1938,16 +1950,14 @@
}, 100);
} else {
//console.log("VERSION call="+call+" - "+currentVersion+". latest "+_latestVersionAvailable);
if ( isNewerVersion(_latestVersionAvailable, currentVersion ) || _urlParams.get('upgrade') != null) {
enablebutton("upgrade");
setbuttontext("upgrade", "Update AqualinkD");
} else {
disablebutton("upgrade");
}
if (_urlParams.get('devupgrade') != null) {
//disablebutton("upgrade");
enablebutton("upgrade");
var button = document.getElementById("upgrade");
button.setAttribute("upgrade_type", "dev")
setbuttontext("upgrade", "Check for Updates");
}
}
}
@ -1970,6 +1980,7 @@
enablebutton("restart");
if (data['aqualinkd_version']) {
_currentVersion = data['aqualinkd_version'];
//document.getElementById("aqualinkdversion").innerHTML = data['aqualinkd_version'];
if (data['aqualinkd_version'] == _latestVersionAvailable ) {
document.getElementById("latesversionavailable").classList.add("hidden");
@ -1978,11 +1989,11 @@
}
delayedVersionCheck(data['aqualinkd_version']);
/*
if (_urlParams.get('upgrade') != null) {
enablebutton("upgrade");
}
*/
}
}
@ -2177,19 +2188,20 @@
if (patch1 === undefined) { patch1 = 0; }
if (patch2 === undefined) { patch2 = 0; }
//console.log(version1+" major="+major1+", "+version2+" major="+major2);
if (major1 > major2) return true;
if (major1 < major2) return false;
//console.log(version1+" minor="+minor1+", "+version2+" minor="+minor2);
if (minor1 > minor2) return true;
if (minor1 < minor2) return false;
//Look for any strings that might be a pre-release
if ( version2.toLowerCase().includes("dev") ||
version2.toLowerCase().includes("pre") ) {
version2.toLowerCase().includes("dev") ||
version2.toLowerCase().includes("beta") ) {
patch2--;
}
//console.log(version1+" patch="+patch1+", "+version2+" patch="+patch2);
return patch1 > patch2;
} catch (e) {
return false;
@ -2240,7 +2252,7 @@
startWebsockets();
getLatestVersion();
getLatestDevVersion();
//getLatestDevVersion();
}
function set_unavailable(message) {
@ -2278,35 +2290,15 @@
switch (source.id) {
case "upgrade":
getVersions();
aqdAlert("Loading");
aqdAlert("Searching release versions");
//let dlg = document.getElementById("alertDialogHeader");
//dlg.innerHTML='<input type="list"
return;
/*
if (source.getAttribute("upgrade_type") == "dev") {
if (confirm("Are you sure you want to proceed installing AqualinkD to Dev version '"+_latestDevVersionAvailable+"'' ?")) {
//console.log("Upgrading to dev release");
update_log_message("***** AqualinkD upgrade in progress *****");
} else {
return;
}
cmd.uri = "installdevrelease"
} else {
if (confirm("Are you sure you want to proceed upgrading AqualinkD to '"+_latestVersionAvailable+"'' ?")) {
console.log("Upgrading");
update_log_message("***** AqualinkD upgrade in progress *****");
} else {
return;
}
cmd.uri = "upgrade"
}
*/
// NEED TO REGET aqmanager after restart.
break;
case "installVersion":
let version = document.getElementById("selectedrelease").value;
cmd.uri = "installrelease/"+version
cmd.value = version;
cmd.text_value = version;
update_log_message("***** AqualinkD upgrade in progress *****");
break;
case "restart":
@ -2350,72 +2342,100 @@
send_command(cmd);
}
const VERSION_URL = "https://api.github.com/repos/aqualinkd/AqualinkD/releases"
let _aqualinkVersions = {};
function populateVersionSelection(caller=null) {
if (caller && caller.checked)
pre = true;
else
pre = false;
try {
let dlg_head = document.getElementById("alertDialogHeader");
let dlg_msg = document.getElementById("alertDialogMessage");
let dropdown = '<select id="selectedrelease" name="version selection">';
//for (const versionNumber of _aqualinkVersions) {
for (const [versionNumber, details] of Object.entries(_aqualinkVersions)) {
//alert(versionNumber);
if (pre || _aqualinkVersions[versionNumber].prerelease === false) {
dropdown += '<option value="'+versionNumber+'">'+versionNumber+'</option>';
}
}
dropdown += '</select>';
dropdown += '&nbsp;&nbsp;&nbsp;Include Dev Releases<input type="checkbox" onchange="populateVersionSelection(this)" name="prerelease" value="PreRelease" '+(pre==true?"checked":"")+'/>';
dropdown += '<br><input type="button" id="installVersion" onclick="send(this);aqdAlertClose();" value="Install"/>';
dlg_head.innerHTML = dropdown;
dlg_msg.innerText = "Select a version to install"
} catch (e){
alert("load error "+e);
function populateVersionSelection(caller = null) {
let prerelease = false;
let showversionwarning=false
if (caller && caller.id == "useprerelease" && caller.checked){
prerelease = true;
} else if (caller && caller.id == "selectedrelease" && caller.value) {
if (isNewerVersion(_currentVersion, caller.value.replace(/[^\d.-]/g, '')))
showversionwarning=true;
prerelease = document.getElementById("useprerelease")?.checked?true:false;
}
try {
let dlg_head = document.getElementById("alertDialogHeader");
let dlg_msg = document.getElementById("alertDialogMessage");
let dropdown = '<select id="selectedrelease" name="version selection" onchange="populateVersionSelection(this)">';
//for (const versionNumber of _aqualinkVersions) {
for (const [versionNumber, details] of Object.entries(_aqualinkVersions)) {
//alert(versionNumber);
if (prerelease || _aqualinkVersions[versionNumber].prerelease === false) {
let exttext='';
let selected='';
if (versionNumber.replace(/[^\d.-]/g, '') == _latestVersionAvailable) {
exttext = ' (latest)';
}
if (versionNumber === caller?.value) {
selected = 'selected';
}
//alert("loaded");
dropdown += '<option value="' + versionNumber + '"'+selected+'>' + versionNumber + exttext +'</option>';
}
}
dropdown += '</select>';
dropdown += '&nbsp;&nbsp;&nbsp;Include Dev Releases<input id="useprerelease" type="checkbox" onchange="populateVersionSelection(this)" name="prerelease" value="PreRelease" ' + (prerelease == true ? "checked" : "") + '/>';
dropdown += '<br>'
if (_latestVersionAvailable == _currentVersion) {
dropdown += '<br>latest Release version already installed';
}
dropdown += '<br><input type="button" id="installVersion" onclick="send(this);aqdAlertClose();" value="Install"/>';
dlg_head.innerHTML = dropdown;
if (showversionwarning) {
dlg_msg.innerHTML = 'Warning:<p style="font-size: small;">Config files are not always backward compatible when downgrading.<br>'+
'You may need to manually update <b>aqualinkd.conf & config.json</b>.</p>';
} else {
dlg_msg.innerText = "";
}
} catch (e) {
alert("load error " + e);
}
//alert("loaded");
}
async function getVersions() {
async function getVersions() {
try {
// Fetch the file from the specified path (make sure config.json exists)
const response = await fetch(VERSION_URL, { cache: 'no-store' });
// Fetch the file from the specified path (make sure config.json exists)
const response = await fetch(VERSION_URL, { cache: 'no-store' });
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
// Get the response as plain text
const versions = await response.text();
const versionObject = JSON.parse(versions);
for (const release of versionObject) {
if (!release.assets || release.prerelease == "true" || release.assets.length === 0) {
continue;
}
// Get the response as plain text
const versions = await response.text();
const versionObject = JSON.parse(versions);
for (const release of versionObject) {
if (!release.assets || release.prerelease == "true" || release.assets.length === 0) {
continue;
}
const downloadUrl = release.assets[0].browser_download_url;
if (downloadUrl) {
_aqualinkVersions[release.tag_name] = {};
_aqualinkVersions[release.tag_name].url = downloadUrl;
_aqualinkVersions[release.tag_name].prerelease = release.prerelease;
}
const downloadUrl = release.assets[0].browser_download_url;
if (downloadUrl) {
_aqualinkVersions[release.tag_name] = {};
_aqualinkVersions[release.tag_name].url = downloadUrl;
_aqualinkVersions[release.tag_name].prerelease = release.prerelease;
}
}
populateVersionSelection();
populateVersionSelection();
} catch (error) {
console.error('There was a problem fetching the config file:', error);
console.error('There was a problem fetching the config file:', error);
let dlg_msg = document.getElementById("alertDialogMessage");
//dlg_msg.innerText = "Error connecting to github, please check connection!\n\n"+VERSION_URL;
dlg_msg.innerHTML = '<p>Error connecting to github, please check connection</p<<hr><p style="font-size: small;">'+VERSION_URL+'</p>';
}
}
}
function getLatestVersion(url="https://api.github.com/repos/AqualinkD/AqualinkD/releases/latest", tryagain=true) {
var xmlhttp = new XMLHttpRequest();
@ -2435,7 +2455,7 @@ let _aqualinkVersions = {};
xmlhttp.open("GET", url);
xmlhttp.send();
}
/*
function getLatestDevVersion(url="https://api.github.com/repos/AqualinkD/AqualinkD/contents/source/version.h", tryagain=true) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
@ -2453,7 +2473,7 @@ let _aqualinkVersions = {};
xmlhttp.setRequestHeader("Accept","application/vnd.github.raw");
xmlhttp.send();
}
*/
function aqdAlert(message) {
document.getElementById("alertDialogHeader").innerText = '';
document.getElementById('alertDialogMessage').innerText = message;
@ -2496,7 +2516,7 @@ let _aqualinkVersions = {};
</tr>
<tr>
<td colspan="1" align="center"><input id="restart" type="button" onclick="send(this);" value="Restart AqualinkD"></td>
<td colspan="1" align="center"><input id="upgrade" type="button" onclick="send(this);" value="Upgrade AqualinkD"></td>
<td colspan="1" align="center"><input id="upgrade" type="button" onclick="send(this);" value="Update AqualinkD"></td>
</tr>
<!--
<tr>

View File

@ -20,7 +20,6 @@
"tile_on_text": "#000000",
"tile_status_text": "#575757",
"value_tile_normal_color": "#049FF8",
"value_tile_normal_color_": "#4ec400ff",
"value_tile_attention_color": "#FF8000",
"value_tile_outofrange_color": "#FF6400",
"options_radio_highlight": "#2196F3",

View File

@ -4,7 +4,7 @@ _confighelp["https_cert_dir"]="For two way auth only. Directory where crt.pem, k
_confighelp["serial_port"]="Device of your RS465 adapter, examples /dev/ttyUSB1, /dev/ttyS2, /dev/serial0"
_confighelp["panel_type"]="Your RS panel type & size. ie 4, 6, 8, 12 or 16 relates to RS4, RS6, RS8, RS12 or RS16. Must be in format XX-N ZZZZ (XX=RS or PD, N=Circuits, ZZZ=Combo or Only or Dual)";
_confighelp["device_id"]="The id of the AqualinkD to use, use serial_logger to find ID's. If your panel is a PDA only model then PDA device ID is 0x60. set to device_id to 0xFF for to autoconfigure all this section";
_confighelp["mqtt_address"]="MQTT address has to be set to ip:port enable MQTT ig mqtt://192.168.1.99:1883"
_confighelp["mqtt_address"]="MQTT address has to be set to protocol://ip:port enable MQTT eg mqtt://192.168.1.99:1883"
_confighelp["read_RS485_swg"]="Read device information directly from RS485 bus"
_confighelp["force_swg"]="Force any devices to be active at startup. Must set these for Home Assistant integration"
_confighelp["enable_scheduler"]="AqualinkD's internal scheduler"

View File

@ -268,7 +268,7 @@
}
50% {
background-color: var(--tile_background);
opacity: 0.9;
opacity: 0.8; /* 0.9 */
}
100% {
background-color: var(--tile_on_background);
@ -285,13 +285,36 @@
}
50% {
background-color: var(--tile_background);
opacity: 0.9;
opacity: 0.8; /* 0.9 */
}
100% {
background-color: var(--tile_on_background);
opacity: 1.0;
}
}
/*
.flash-icon {
color: var(--tile_on_text);
filter: alpha(opacity=100);
opacity: 0.9;
-webkit-animation-name: tile-icon-flash;
-webkit-animation-duration: 2s;
animation-name: tile-icon-flash;
animation-duration: 2s;
animation-iteration-count: infinite;
}
@keyframes tile-icon-flash {
0% {
opacity: 1.0;
}
50% {
opacity: 0.7;
}
100% {
opacity: 1.0;
}
}
*/
/*
.off {
@ -869,9 +892,11 @@
if (window.self !== window.top) {
//console.log("The code is running inside an iframe.");
// Change the link under the cog/hamburger icon
ele = document.getElementById('cog_link');
ele.setAttribute("href", "/");
ele.setAttribute("target", "_parent");
const ele = document.getElementById('cog_link');
if (ele) {
ele.setAttribute("href", "/");
ele.setAttribute("target", "_parent");
}
} else {
//console.log("The code is not running inside an iframe.");
}
@ -1140,6 +1165,7 @@
//console.log(name+" = "+type+" "+subtype);
if (type.startsWith("switch")) { // Use default SVG for icon images
if (off_imgurl == null) {
var valdiv = document.createElement('div');
if (_DisableOffIconCircle) {
@ -1271,13 +1297,14 @@
try {
if (state == (document.getElementById(id).getAttribute('status') == 'off')) {
send_command(id, (state==true?1:0) );
//console.log("Switch state "+id+" to "+(state)?"on":"off");
} else {
//console.log("state "+id+" to "+(state)?"on":"off")
}
setTileOn(id, ((state) ? "on" : "off"));
} catch(exception) {}
} catch(exception) {
console.error("setTileState() :-" + exception);
}
}
function setThermostatSetpoint(id, sp_value) {
@ -1290,37 +1317,7 @@
}
/*
function setTileValue(id, value) {
var ext = '';
var type;
if (value == undefined || value.startsWith("-999") || value.startsWith(" "))
value = '--';
else {
try {
if ((type = document.getElementById(id).getAttribute('type')) != null) {
if (type == 'temperature' || type == 'setpoint_thermo' || type == 'setpoint_freeze')
ext = '&deg;';
else if (type == 'setpoint_swg')
ext = '%';
if ((type == 'temperature' || type == 'value') && turn_on_sensortiles == true) {
if (value == 0 || value == '--')
setTileOn(id, 'off');
else
setTileOn(id, 'on');
}
}
} catch (e) {
//console.log('ERROR id=' + id + ' Line 764 | element = '+document.getElementById(id));
}
}
//document.getElementById(id + '_tile_icon_value').textContent = value;
var tile;
if ((tile = document.getElementById(id + '_tile_icon_value')) != null)
tile.innerHTML = value + ext;
}
*/
function setTileValue(id, value) {
var ext = '';
var type = null;
@ -1331,6 +1328,7 @@
if (value == undefined || value.startsWith("-999") || value.startsWith(" ")) {
value = '--';
} else {
let uom;
if ((uom = document.getElementById(id).getAttribute('UOM')) != null) {
ext = uom;
} else if ((type = document.getElementById(id).getAttribute('type')) != null) {
@ -1364,7 +1362,7 @@
}
} catch (e) {
// Devices that are hidden are not found
//console.log('ERROR id=' + id + ' Line '+new Error().lineNumber+' | element = '+document.getElementById(id));
//console.log('ERROR id=' + id + ' Line '+new Error().lineNumber+' | element = '+document.getElementById(id)+"\n"+e);
}
//document.getElementById(id + '_tile_icon_value').textContent = value;
var tile;
@ -1518,7 +1516,6 @@
//console.log("Error unknown ID '"+id+"' can't set to '"+status+"'");
return;
}
// Display is a special status to "brighten up" sensor values.
if (status != "on" && status != "off" && status != "enabled" && status != "flash"
&& status != "rangecritical" && status != "rangewarning" && status != "rangeoptimal") {
@ -1535,13 +1532,17 @@
tile.classList.add("flash");
tile.classList.remove("on");
text = "Delay";
//const tile_icon = document.getElementById(id + "_tile_icon_value");
//if (tile_icon) {tile_icon.classList.add("flash-icon");}
} else {
tile.classList.add("on");
tile.classList.remove("flash");
text = "On";
//const tile_icon = document.getElementById(id + "_tile_icon_value");
//if (tile_icon) {tile_icon.classList.remove("flash-icon");}
}
try{
if (tile.getAttribute('type').startsWith("switch")) {
if (tile.getAttribute('type').startsWith("switch") || tile.getAttribute('type').startsWith("light")) {
var el = document.getElementById(id + '_tile_icon_value');
if (el) { // standard SVG icon
el.classList.add('switch-on');
@ -1603,7 +1604,7 @@
/*
*/
try {
if (tile.getAttribute('type').startsWith("switch")) {
if (tile.getAttribute('type').startsWith("switch") || tile.getAttribute('type').startsWith("light")) {
var el = document.getElementById(id + '_tile_icon_value');
if (el) { // standard SVG icon
el.classList.add('switch-off');
@ -1723,6 +1724,7 @@
const tile_icon_classes = [
"enabled",
//"flash",
//"flash-icon",
"disabled",
"cool",
"heat",
@ -1740,7 +1742,7 @@
if (_DisableOffIconCircle) {
try{
if (document.getElementById(id).getAttribute('type').startsWith("switch")) {
if (document.getElementById(id).getAttribute('type').startsWith("switch") || document.getElementById(id).getAttribute('type').startsWith("light")) {
allowed.delete("disabled");
}
} catch (e){console.log(e)}
@ -3166,7 +3168,7 @@
//console.trace("Tracing");
socket_di.send(JSON.stringify(_cmd));
if ( _link_spa_and_heater && cmd == "Spa_Mode" ) {
if ( _config.tile_settings.link_spa_and_spa_heater && cmd == "Spa_Mode" ) {
var ncmd = {};
ncmd.command = "Spa_Heater";
ncmd.uri = "Spa_Heater/set";