Skip to content

Commit

Permalink
Updates for 2.5.0
Browse files Browse the repository at this point in the history
  • Loading branch information
sfeakes committed Oct 12, 2024
1 parent 32f3d8b commit cbf6567
Show file tree
Hide file tree
Showing 24 changed files with 765 additions and 284 deletions.
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,16 +132,20 @@ NEED TO FIX FOR THIS RELEASE.
# pickup speed faster on iaqualinktouch after change
-->

# Updates in 2.4.1 (under development)
# Updates in 2.5.0 (under development)
* PDA panel Rev 6.0 or newer that do not have a Jandy iAqualink device attached can use the AqualinkTouch protocol rather than PDA protocol.
* This is faster, more reliable and does not intefear with the physical PDA device (like existing implimentation)
* Please consider this very much BETA at the moment.
* use `device_id=0x33` in aqualinkd.conf
* use `device_id=0x33` in aqualinkd.conf.
* PDA panel Rev 6.0 of newer WITH a Jandy iAqualink device attached can use `read_RS485_iAqualink = yes` to speed up device state change detection.
* Added MQTT vsp_pump/speed/set for setting speed (RPM/GPM) by %, for automation hubs.
* Added full dimmer range support for dimmable lights (not limited to 0,25,50,75,100 anymore)
* Added vsp and dimmer to hassio and homebridge-aqualinkd plugin as full range dimmer controls.
* Updated UI for support fullrange dimmer.
* cleaned up code for spa_mode and spa for newer pannels.
* Allow VSP to be asigned to virtual button.
* Fixed bug with timer not starting.
* Increase Speed of detecting device state changes.
* Increase Speed of detecting device state changes.

# Updates in Release 2.4.0
* <b>WARNING</b> Breaking change if you use dimmer (please change button_??_lightMode from 6 to 10)
Expand Down
Binary file modified release/aqualinkd-amd64
Binary file not shown.
Binary file modified release/aqualinkd-arm64
Binary file not shown.
Binary file modified release/aqualinkd-armhf
Binary file not shown.
8 changes: 5 additions & 3 deletions release/aqualinkd.conf
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,14 @@ device_id=0x00
# JXi = Jandy JXi heater (might also be LXi heaters)
# LX = Jandy LX & LT heaters
# Chem = Jandy Chemical Feeder
# iAqualink = Read iAqualink2 (wifi device). Only relivent in PDA mode IF you have iAqualink2/3 device
#read_RS485_swg = yes
#read_RS485_ePump = yes
#read_RS485_vsfPump = yes
#read_RS485_JXi = yes
#read_RS485_LX = yes
#read_RS485_Chem = yes
#read_RS485_iAqualink = yes

# Keep the panel time synced with systemtime. Make sure to set systemtime / NTP correctly.
keep_paneltime_synced = yes
Expand Down Expand Up @@ -146,7 +148,7 @@ report_zero_spa_temp = yes
# dead.
# ~40 and we will be replying too slowley, so keep below that.
# 10~20 is about what most device reply in. But 0-4 works well.
rs485_frame_delay = 4
rs485_frame_delay = 0

# Get rid of the startup warning message about no low latency. BETTER option is to buy a better adapter.
#ftdi_low_latency = no
Expand Down Expand Up @@ -264,8 +266,8 @@ use_panel_aux_labels=no
# button_01_pumpIndex=1
# If you have assigned this pump an index number in your Aqualink control panel, (Between 1 & 4), put it here for VSP, RPM, Primp information to be captured.
#
# button_xx_lightMode = (0=Aqualink program, 1=Jandy, 2=Jandy LED, 3=SAm/SAL, 4=Color Logic, 5=Intellibrite, 6=Hayw Univ Color, 7,8,9(future), 10=Dimmer)
#
# button_xx_lightMode = (0=Aqualink program, 1=Jandy, 2=Jandy LED, 3=SAm/SAL, 4=Color Logic, 5=Intellibrite, 6=Hayw Univ Color, 7,8,9(future), 10=Dimmer, 11=Full Range Dimmer)
#
# Below are settings for standard buttons on RS-8 Combo panel used as example.
button_01_label=Filter Pump

Expand Down
Binary file modified release/serial_logger-arm64
Binary file not shown.
Binary file modified release/serial_logger-armhf
Binary file not shown.
2 changes: 2 additions & 0 deletions source/aq_mqtt.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
#define PUMP_SPEED_TOPIC "/Speed"

#define LIGHT_PROGRAM_TOPIC "/program"
#define LIGHT_DIMMER_VALUE_TOPIC "/brightness"

/*
#define AIR_TEMPERATURE "Air"
#define POOL_TEMPERATURE "Pool_Water"
Expand Down
91 changes: 83 additions & 8 deletions source/aq_panel.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
void initPanelButtons(struct aqualinkdata *aqdata, bool rspda, int size, bool combo, bool dual);
void programDeviceLightMode(struct aqualinkdata *aqdata, int value, int button);


char *name2label(char *str)
{
int len = strlen(str);
Expand Down Expand Up @@ -687,6 +688,9 @@ const char* getRequestName(request_source source)
case NET_TIMER:
return "Timer";
break;
case UNACTION_TIMER:
return "UnactionTimer";
break;
}

static char buf[25];
Expand Down Expand Up @@ -740,6 +744,9 @@ const char* getActionName(action_type type)
case DATE_TIME:
return "Date Time";
break;
case LIGHT_BRIGHTNESS:
return "Light Brightness";
break;
}

static char buf[25];
Expand Down Expand Up @@ -821,11 +828,15 @@ bool setDeviceState(struct aqualinkdata *aqdata, int deviceIndex, bool isON, req
// Domoticz has a bad habbit of resending the same state back to us, when we use the PRESTATE_ONOFF option
// since allbutton (default) is stateless, and rssaadapter is statefull, use rssaadapter for any domoricz requests
set_aqualink_rssadapter_aux_state(button, isON);
//} else if ( source == NET_TIMER && isRSSA_ENABLED ) {
// Timer will sometimes send duplicate, so use RSSA since that protocol has on/off rather than toggle
// set_aqualink_rssadapter_aux_state(button, isON);
} else if (button->special_mask & PROGRAM_LIGHT && 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);
}

Expand Down Expand Up @@ -882,24 +893,62 @@ bool programDeviceValue(struct aqualinkdata *aqdata, action_type type, int value
return true;
}


void programDeviceLightBrightness(struct aqualinkdata *aqdata, int value, int button, bool expectMultiple)
{
clight_detail *light = getProgramableLight(aqdata, button);

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",button);
return;
}

// DIMMER is 0,25,50,100 DIMMER2 is range
if (light->lightType == LC_DIMMER) {
value = round(value / 25);
}

if (!expectMultiple) {
programDeviceLightMode(aqdata, value, button);
return;
}

time(&aqdata->unactioned.requested);
aqdata->unactioned.value = value;
aqdata->unactioned.type = LIGHT_MODE;
aqdata->unactioned.id = button;

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 button)
{
int i;
clight_detail *light = NULL;
#ifdef AQ_PDA
if (isPDA_PANEL && !isPDA_IAQT) {
LOG(PANL_LOG,LOG_ERR, "Light mode control not supported in PDA mode\n");
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, button);

if (light == NULL) {
LOG(PANL_LOG,LOG_ERR, "Light mode control not configured for button %d\n",button);
Expand All @@ -916,13 +965,18 @@ void programDeviceLightMode(struct aqualinkdata *aqdata, int value, int button)
_aqconfig_.light_programming_initial_off,
_aqconfig_.light_programming_mode );
aq_programmer(AQ_SET_LIGHTPROGRAM_MODE, buf, aqdata);
} else if (isRSSA_ENABLED && light->lightType != LC_DIMMER) {
unsigned char rssd_value = value;
set_aqualink_rssadapter_aux_extended_state(light->button, rssd_value);
} 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
set_aqualink_rssadapter_aux_extended_state(light->button, RS_SA_ON);
// Value 1 = 25, 1 = 50, 3 = 75, 4 = 100 (need to convert value into binary)
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;
Expand Down Expand Up @@ -979,6 +1033,9 @@ bool panel_device_request(struct aqualinkdata *aqdata, action_type type, int dev
//start_timer(aqdata, &aqdata->aqbuttons[deviceIndex], deviceIndex, value);
start_timer(aqdata, deviceIndex, value);
break;
case LIGHT_BRIGHTNESS:
programDeviceLightBrightness(aqdata, value, deviceIndex, (source==NET_MQTT?true:false));
break;
case LIGHT_MODE:
if (value <= 0) {
// Consider this a bad/malformed request to turn the light off.
Expand Down Expand Up @@ -1013,6 +1070,7 @@ bool panel_device_request(struct aqualinkdata *aqdata, action_type type, int dev
// Programmable light has been updated, so update the status in AqualinkD
void updateButtonLightProgram(struct aqualinkdata *aqdata, int value, int button)
{
/*
int i;
clight_detail *light = NULL;
Expand All @@ -1023,6 +1081,8 @@ void updateButtonLightProgram(struct aqualinkdata *aqdata, int value, int button
break;
}
}
*/
clight_detail *light = getProgramableLight(aqdata, button);

if (light == NULL) {
LOG(PANL_LOG,LOG_ERR, "Button not found for light button index=%d\n",button);
Expand All @@ -1032,6 +1092,21 @@ void updateButtonLightProgram(struct aqualinkdata *aqdata, int value, int button
light->currentValue = value;
}

clight_detail *getProgramableLight(struct aqualinkdata *aqdata, int button)
{
if ( isPLIGHT(aqdata->aqbuttons[button].special_mask) ) {
return (clight_detail *)aqdata->aqbuttons[button].special_mask_ptr;
}
return NULL;
}

pump_detail *getPumpDetail(struct aqualinkdata *aqdata, int button)
{
if ( isVS_PUMP(aqdata->aqbuttons[button].special_mask) ) {
return (pump_detail *)aqdata->aqbuttons[button].special_mask_ptr;
}
return NULL;
}

#ifdef DO_NOT_COMPILE

Expand Down
4 changes: 4 additions & 0 deletions source/aq_panel.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ int convertPumpPercentToSpeed(pump_detail *pump, int value); // This is probable
uint16_t getPanelSupport( char *rev_string, int rev_len);

aqkey *addVirtualButton(struct aqualinkdata *aqdata, char *label, int vindex);

clight_detail *getProgramableLight(struct aqualinkdata *aqdata, int button);
pump_detail *getPumpDetail(struct aqualinkdata *aqdata, int button);

//void panneltest();

#define isPDA_PANEL ((_aqconfig_.paneltype_mask & RSP_PDA) == RSP_PDA)
Expand Down
34 changes: 31 additions & 3 deletions source/aq_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,19 +114,22 @@ void start_timer(struct aqualinkdata *aq_data, /*aqkey *button,*/ int deviceInde
}
}

#define WAIT_TIME_BEFORE_ON_CHECK 1000 // 1 second
#define WAIT_TIME_BEFORE_ON_CHECK 1000
//#define WAIT_TIME_BEFORE_ON_CHECK 1000000 // 1 second

void *timer_worker( void *ptr )
{
struct timerthread *tmthread;
tmthread = (struct timerthread *) ptr;
int retval = 0;
int cnt=0;

LOG(TIMR_LOG, LOG_NOTICE, "Start timer for '%s'\n",tmthread->button->name);

// Add mask so we know timer is active
tmthread->button->special_mask |= TIMER_ACTIVE;

/*
#ifndef PRESTATE_ONOFF
delay(WAIT_TIME_BEFORE_ON_CHECK);
LOG(TIMR_LOG, LOG_DEBUG, "wait finished for button state '%s'\n",tmthread->button->name);
Expand All @@ -141,6 +144,19 @@ void *timer_worker( void *ptr )
panel_device_request(tmthread->aq_data, ON_OFF, tmthread->deviceIndex, false, NET_TIMER);
}
}
*/

while (tmthread->button->led->state == OFF) {
LOG(TIMR_LOG, LOG_DEBUG, "waiting for button state '%s' to change\n",tmthread->button->name);
delay(WAIT_TIME_BEFORE_ON_CHECK);
if (cnt++ == 5 && !isPDA_PANEL) {
LOG(TIMR_LOG, LOG_NOTICE, "turning on '%s'\n",tmthread->button->name);
panel_device_request(tmthread->aq_data, ON_OFF, tmthread->deviceIndex, true, NET_TIMER);
} else if (cnt == 10) {
LOG(TIMR_LOG, LOG_ERR, "button state never turned on'%s'\n",tmthread->button->name);
break;
}
}

pthread_mutex_lock(&tmthread->thread_mutex);

Expand All @@ -163,12 +179,24 @@ void *timer_worker( void *ptr )

LOG(TIMR_LOG, LOG_NOTICE, "End timer for '%s'\n",tmthread->button->name);

if (tmthread->button->led->state != OFF) {
// We need to detect if we ended on time or were killed.
// If killed the device is probable off (or being set to off), so we should probably poll a few times before turning off.
// Either that of change ap_panel to not turn off device if timer is set.

//LOG(TIMR_LOG, LOG_NOTICE, "End timer duration '%d'\n",tmthread->duration_min);

// if duration_min is 0 we were killed, if not we got here on timeout, so turn off device.

if (tmthread->duration_min != 0 && tmthread->button->led->state != OFF) {
LOG(TIMR_LOG, LOG_INFO, "Timer waking turning '%s' off\n",tmthread->button->name);
panel_device_request(tmthread->aq_data, ON_OFF, tmthread->deviceIndex, false, NET_TIMER);
} else {
} else if (tmthread->button->led->state == OFF) {
LOG(TIMR_LOG, LOG_INFO, "Timer waking '%s' is already off\n",tmthread->button->name);
}

if (tmthread->button->led->state != OFF) {
// Need to wait
}

// remove mask so we know timer is dead
tmthread->button->special_mask &= ~ TIMER_ACTIVE;
Expand Down
8 changes: 6 additions & 2 deletions source/aqualink.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ typedef struct aqualinkkey
unsigned char rssd_code;
int dz_idx;
uint8_t special_mask;
void *special_mask_ptr;
} aqkey;

// special_mask for above aqualinkkey structure.
Expand Down Expand Up @@ -128,6 +129,7 @@ typedef enum action_type {
ON_OFF,
TIMER,
LIGHT_MODE,
LIGHT_BRIGHTNESS,
DATE_TIME
} action_type;

Expand Down Expand Up @@ -216,7 +218,8 @@ typedef enum clight_type {
LC_SPARE_1,
LC_SPARE_2,
LC_SPARE_3,
LC_DIMMER,
LC_DIMMER, // use 0, 25, 50, 100
LC_DIMMER2, // use range 0 to 100
NUMBER_LIGHT_COLOR_TYPES // This is used to size and count so add more prior to this
} clight_type;

Expand All @@ -225,7 +228,8 @@ typedef enum {
NET_API,
NET_WS,
NET_DZMQTT,
NET_TIMER // Not used yet, need to change aq_timer.c
NET_TIMER, // Not used yet, need to change aq_timer.c
UNACTION_TIMER
} request_source;

typedef struct clightd
Expand Down
10 changes: 10 additions & 0 deletions source/aqualinkd.c
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,9 @@ void action_delayed_request()
LOG(AQUA_LOG,LOG_NOTICE, "Changing spa heater setpoint by %d\n", _aqualink_data.unactioned.value);
aq_programmer(AQ_ADD_RSSADAPTER_SPA_HEATER_TEMP, sval, &_aqualink_data);
}
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
{
LOG(AQUA_LOG,LOG_ERR, "Unknown request of type %d\n", _aqualink_data.unactioned.type);
Expand Down Expand Up @@ -496,6 +499,10 @@ int startup(char *self, char *cfgFile)
}
} else if (isPDA_PANEL) {
if ( (_aqconfig_.device_id >= 0x60 && _aqconfig_.device_id <= 0x63) || _aqconfig_.device_id == 0x33 ) {
if ( _aqconfig_.device_id == 0x33 ) {
LOG(AQUA_LOG,LOG_NOTICE, "Enabeling iAqualink protocol.\n");
_aqconfig_.enable_iaqualink = true;
}
// We are good
} else {
LOG(AQUA_LOG,LOG_ERR, "Device ID 0x%02hhx does not match PDA panel, please check config!\n", _aqconfig_.device_id);
Expand Down Expand Up @@ -572,6 +579,9 @@ int startup(char *self, char *cfgFile)
LOG(AQUA_LOG,LOG_NOTICE, "Config rssa_device_id = 0x%02hhx\n", _aqconfig_.rssa_device_id);
#if defined AQ_ONETOUCH || defined AQ_IAQTOUCH
LOG(AQUA_LOG,LOG_NOTICE, "Config extra_device_id = 0x%02hhx\n", _aqconfig_.extended_device_id);
if (_aqconfig_.enable_iaqualink) {
LOG(AQUA_LOG,LOG_NOTICE, "Config enable_iaqualink = %s\n", bool2text(_aqconfig_.enable_iaqualink));
}
LOG(AQUA_LOG,LOG_NOTICE, "Config extra_device_prog = %s\n", bool2text(_aqconfig_.extended_device_id_programming));
#endif
LOG(AQUA_LOG,LOG_NOTICE, "Config serial_port = %s\n", _aqconfig_.serial_port);
Expand Down
Loading

0 comments on commit cbf6567

Please sign in to comment.