V3 dev updates

pull/476/head
sfeakes 2025-11-10 17:07:17 -06:00
parent 7913ee5959
commit abf1e30f6c
12 changed files with 181 additions and 21 deletions

View File

@ -155,10 +155,10 @@ NEED TO FIX FOR THIS RELEASE.
* Autoconfigure will *try* to work for PDA panels.
* Added example script to generate HTTPS certificates. (./extras/generate-certs.sh)
* Cleaned up exit & errors when running as daemon and docker.
* Fixed issues with external sensors and homekit.
* Added preliminary support for Jandy Infinite water color lights
- Need to finish off :-
* HAT serial optimizations broke some USB serial adapters
* Finish off assigning light mode & functionality to a vbutton (for Jandy Infinite water color support )
* cleanup rs_msg_utils.c
* WebUI Config in aqmanager.

Binary file not shown.

Binary file not shown.

View File

@ -62,6 +62,7 @@
#define LIGHT_DIMMER_VALUE_TOPIC "/brightness"
#define SENSOR_TOPIC "Sensor"
#define FULL_SENSOR_TOPIC SENSOR_TOPIC "/"
/*
#define AIR_TEMPERATURE "Air"

View File

@ -11,13 +11,19 @@
Jandy Colors
Jandy LED Light
Sam/SL
Color Logic
Color Logic
Intelibright
Haywood Universal Color
*/
bool isShowMode(const char *mode);
/*
Jandy Colors
Jandy LED Light
SAm/SAL Light
IntelliBrite
Hayw Univ Color
*/
/****** This list MUST be in order of clight_type enum *******/
char *_color_light_options[NUMBER_LIGHT_COLOR_TYPES][LIGHT_COLOR_OPTIONS] =

View File

@ -73,6 +73,8 @@ unsigned char _currentPage;
unsigned char _lastMsgType = 0x00;
//unsigned char _last_kick_type = -1;
static char _popupMsg[AQ_MSGLONGLEN + 1];
int _deviceStatusLines = 0;
char _homeStatus[IAQ_STATUS_PAGE_LINES][AQ_MSGLEN+1];
char _deviceStatus[IAQ_STATUS_PAGE_LINES][AQ_MSGLEN+1];
@ -89,6 +91,11 @@ unsigned char iaqtLastMsg()
return _lastMsgType;
}
const char *iaqtPopupMsg()
{
return _popupMsg;
}
void set_iaqtouch_lastmsg(unsigned char msgtype)
{
_lastMsgType = msgtype;
@ -836,7 +843,7 @@ bool process_iaqtouch_packet(unsigned char *packet, int length, struct aqualinkd
//static int _pollCnt = 0;
//static int probesSinceLastPageCMD=0;
static bool gotStatus = true;
static char message[AQ_MSGLONGLEN + 1];
//static char message[AQ_MSGLONGLEN + 1];
bool fake_pageend = false;
//char buff[1024];
// NSF Take this out
@ -967,17 +974,17 @@ bool process_iaqtouch_packet(unsigned char *packet, int length, struct aqualinkd
} else if (packet[PKT_CMD] == CMD_IAQ_MSG_LONG) {
char *sp;
// Set disply message if PDA panel
memset(message, 0, AQ_MSGLONGLEN + 1);
rsm_strncpy(message, packet + 6, AQ_MSGLONGLEN, length-9);
LOG(IAQT_LOG,LOG_NOTICE, "Popup message '%s'\n",message);
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);
// 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(message, "To change colors press Ok now", strlen(message))) != NULL)
if ((sp = rsm_strncasestr(_popupMsg, "To change colors press Ok now", strlen(_popupMsg))) != NULL)
{
*sp = '\0';
}
SET_IF_CHANGED_STRCPY(aqdata->last_display_message, message, aqdata->is_dirty); // Also display the message on web UI
SET_IF_CHANGED_STRCPY(aqdata->last_display_message, _popupMsg, aqdata->is_dirty); // Also display the message on web UI
if (in_programming_mode(aqdata)) {
SET_IF_CHANGED(aqdata->is_display_message_programming, true, aqdata->is_dirty);
@ -1070,6 +1077,13 @@ if not programming && poll packet {
// But may be better to simply increase FULL_STATUS_POLL_COUNT when it's not enabled.
uint8_t nextPageRequestKey = KEY_IAQTCH_HOME;
if (_pollCnt > FULL_STATUS_POLL_COUNT + 5) {
LOG(IAQT_LOG,LOG_ERR,"Poll count=%d, too high, looks like page is stuck\n",_pollCnt);
_pollCnt=0;
} else {
LOG(IAQT_LOG,LOG_DEBUG,"Poll count=%d, Curent Page=0x%02hhx\n",_pollCnt, _currentPage);
}
if (_pollCnt++ > FULL_STATUS_POLL_COUNT) {
switch(_currentPage) {
case IAQ_PAGE_DEVICES:

View File

@ -22,6 +22,7 @@ struct iaqt_page_button *iaqtFindButtonByIndex(int index);
const char *iaqtGetMessageLine(int index);
const char *iaqtGetTableInfoLine(int index);
unsigned char iaqtLastMsg();
const char *iaqtPopupMsg();
const char *iaqt_page_name(const unsigned char page);
int num2iaqtRSset (unsigned char* packetbuffer, int num, bool pad4unknownreason);
int char2iaqtRSset (unsigned char* packetbuffer, char *msg, int msg_len);

View File

@ -836,21 +836,41 @@ PRINTF("******** WAIT for next page\n");
pButton = iaqtFindButtonByLabel(mode_name);
if (pButton == NULL) {
LOG(IAQT_LOG, LOG_ERR, "IAQ Touch did find color '%s' in color light page\n",mode_name);
LOG(IAQT_LOG, LOG_ERR, "IAQ Touch didn't find color '%s' in color light page\n",mode_name);
goto f_end;
}
LOG(IAQT_LOG, LOG_DEBUG, "IAQ Touch found '%s' sending keycode '0x%02hhx'\n", mode_name, pButton->keycode);
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());
send_aqt_cmd(pButton->keycode);
// NSF Key code is +16 for some reason. ie key 0x07=(send 0x17). 0x0a=(send 0x1a)
send_aqt_cmd(pButton->keycode + 16);
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)
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);
if (pmsg[0] == ' ') {
PRINTF("******** popupMsg clear ********\n");
break;
}
if (++i > 5) {
LOG(IAQT_LOG, LOG_WARNING, "IAQ Touch didn't see correct messages, color may no change\n");
break;
}
}
/*
unsigned char page;
PRINTF("******** 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
}
*/
//LOG(IAQT_LOG, LOG_ERR, "IAQ Touch WAIYING FOR 1 MESSAGES\n");
//waitfor_iaqt_messages(aqdata, 1);

View File

@ -26,6 +26,8 @@
#include "serialadapter.h"
#include "rs_msg_utils.h"
#include "color_lights.h"
#define IAQUA_QLEN 20
typedef struct iaqulnkcmd
@ -330,6 +332,83 @@ void iAqSetButtonState(struct aqualinkdata *aqdata, int index, const unsigned ch
}
}
const char* colorLightNameFromHex(unsigned char hexid) {
switch (hexid) {
case 0x01:
return "Jandy Color Light";
break;
case 0x02:
return "Sam/SAL light";
break;
case 0x03:
return "Color Logic (Guess)";
break;
case 0x04:
return "Jandy LED Light";
break;
case 0x05:
return "Intelibrite Light";
break;
case 0x06:
return "Haywood universal color Light";
break;
default:
return "Unknown Color Light";
break;
}
}
const char* decodeTypeBits(unsigned char BIT2, unsigned char BIT3, unsigned char BIT4) {
switch (BIT3) {
case 0x01:
return "Dimable light";
break;
case 0x02:
return colorLightNameFromHex(BIT4);
break;
case 0x00:
default:
return "";
break;
}
}
/*
This is not right. BIT2 is not the mode. it's always 0x07 for color light
const char* getColorLightMode(unsigned char BIT2, unsigned char BIT3, unsigned char BIT4) {
// BIT2 = Mode
// BIT4 = Light Type.
switch (BIT4) {
case 0x01:
return light_mode_name(LC_JANDY, BIT2, IAQUALNK); //"Jandy Color Light";
break;
case 0x02:
return light_mode_name(LC_SAL, BIT2, IAQUALNK); //"Sam/SAL light";
break;
case 0x03:
return light_mode_name(LC_CLOGIG, BIT2, IAQUALNK); //"Color Logic (Guess)";
break;
case 0x04:
return light_mode_name(LC_JANDYLED, BIT2, IAQUALNK); //"Jandy LED Light";
break;
case 0x05:
return light_mode_name(LC_INTELLIB, BIT2, IAQUALNK); //"Intelibrite Light";
break;
case 0x06:
return light_mode_name(LC_HAYWCL, BIT2, IAQUALNK); //"Haywood universal color Light";
break;
default:
return "Unknown Mode";
break;
}
}
*/
/*
Status packets are requested on iAqualink ID 0xA? but received on AqualinkTouch ID 0x3?
They are also sent when iAqualink is connected and a device changes.
@ -509,6 +588,31 @@ bool process_iAqualinkStatusPacket(unsigned char *packet, int length, struct aqu
}
else if (packet[PKT_CMD] == CMD_IAQ_AUX_STATUS)
{
/*
Example output. We can get color light information from here.
Aux7 bit2=0x07. (This is not the MODE. it's always 0x07 for color light)
Bit3 = (0x01=Dimmer, 0x02=ColorLight)
Bit4 = (Type of color light)
Info: iAqualink2:Cleaner = On | bit1=0x01 bit2=0x01 bit3=0x00 bit4=0x00
Info: iAqualink2:Waterfall = On | bit1=0x01 bit2=0x01 bit3=0x00 bit4=0x00
Info: iAqualink2:Air Blower = Off | bit1=0x00 bit2=0x01 bit3=0x00 bit4=0x00
Info: iAqualink2:Pool Light = Off | bit1=0x00 bit2=0x01 bit3=0x00 bit4=0x00
Info: iAqualink2:Aux5 = Off | bit1=0x00 bit2=0x01 bit3=0x00 bit4=0x00
Info: iAqualink2:Aux6 = Off | bit1=0x00 bit2=0x01 bit3=0x00 bit4=0x00
Info: iAqualink2:Aux7 = On | bit1=0x01 bit2=0x07 bit3=0x02 bit4=0x01
Info: iAqualink2:Extra Aux = Off | bit1=0x00 bit2=0x01 bit3=0x00 bit4=0x00
Jandy Color = JC
Info: iAqualink2:Aux7 = On | bit1=0x01 bit2=0x07 bit3=0x02 bit4=0x01
Jandy LED = JL
Info: iAqualink2:Aux7 = On | bit1=0x01 bit2=0x07 bit3=0x02 bit4=0x04
Sam/SAL = SL
Info: iAqualink2:Aux7 = On | bit1=0x01 bit2=0x07 bit3=0x02 bit4=0x02
Intelibrite = IB
Info: iAqualink2:Aux7 = On | bit1=0x01 bit2=0x07 bit3=0x02 bit4=0x05
Haywood universal color = HU
Info: iAqualink2:Aux7 = On | bit1=0x01 bit2=0x07 bit3=0x02 bit4=0x06
*/
logPacket(IAQL_LOG, LOG_INFO, packet, length, true);
// Look at notes in iaqualink.c for how this packet is made up
// Since this is so similar to above CMD_IAQ_1TOUCH_STATUS, we should look at using same logic for both.
@ -521,7 +625,15 @@ bool process_iAqualinkStatusPacket(unsigned char *packet, int length, struct aqu
int labellen = packet[status + 4];
if (labelstart + labellen < length)
{
LOG(IAQL_LOG, LOG_INFO, "%-15.*s = %s | bit1=0x%02hhx bit2=0x%02hhx bit3=0x%02hhx bit4=0x%02hhx\n", labellen, &packet[labelstart], (packet[status] == 0x00 ? "Off" : "On "), packet[status], packet[status + 1], packet[status + 2], packet[status + 3]);
LOG(IAQL_LOG, LOG_INFO, "%-15.*s = %s | bit1=0x%02hhx bit2=0x%02hhx bit3=0x%02hhx bit4=0x%02hhx %s\n",
labellen,
&packet[labelstart],
(packet[status] == 0x00 ? "Off" : "On "),
packet[status], packet[status + 1],
packet[status + 2],
packet[status + 3],
decodeTypeBits(packet[status + 1], packet[status + 2], packet[status + 3])
);
}
if (isPDA_PANEL) {
for (int bi=2 ; bi < aqdata->total_buttons ; bi++) {
@ -544,6 +656,8 @@ bool process_iAqualinkStatusPacket(unsigned char *packet, int length, struct aqu
return true;
}
bool process_iaqualink_packet(unsigned char *packet, int length, struct aqualinkdata *aqdata)
{

View File

@ -556,26 +556,30 @@ int build_device_JSON(struct aqualinkdata *aqdata, char* buffer, int size, bool
temperatureUOM t_uom = getTemperatureUOM(aqdata->sensors[i].uom);
if (aqdata->sensors[i].uom == NULL) {
length += sprintf(buffer+length, "{\"type\": \"value\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"on\", \"value\": \"%.*f\", \"uom\": \"\" },",
length += sprintf(buffer+length, "{\"type\": \"value\", \"id\": \"%s%s\", \"name\": \"%s\", \"state\": \"on\", \"value\": \"%.*f\", \"uom\": \"\" },",
FULL_SENSOR_TOPIC,
aqdata->sensors[i].ID,
aqdata->sensors[i].label,
2,
aqdata->sensors[i].value);
} else if (t_uom == UNKNOWN) {
length += sprintf(buffer+length, "{\"type\": \"value\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"on\", \"value\": \"%.*f\", \"uom\": \"%s\" },",
length += sprintf(buffer+length, "{\"type\": \"value\", \"id\": \"%s%s\", \"name\": \"%s\", \"state\": \"on\", \"value\": \"%.*f\", \"uom\": \"%s\" },",
FULL_SENSOR_TOPIC,
aqdata->sensors[i].ID,
aqdata->sensors[i].label,
2,
aqdata->sensors[i].value,
aqdata->sensors[i].uom);
} else if ( !homekit && (aqdata->temp_units == FAHRENHEIT && t_uom == CELSIUS) ) {
length += sprintf(buffer+length, "{\"type\": \"temperature\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"on\", \"value\": \"%.*f\" },",
length += sprintf(buffer+length, "{\"type\": \"temperature\", \"id\": \"%s%s\", \"name\": \"%s\", \"state\": \"on\", \"value\": \"%.*f\" },",
FULL_SENSOR_TOPIC,
aqdata->sensors[i].ID,
aqdata->sensors[i].label,
2,
degCtoF(aqdata->sensors[i].value));
} else {
length += sprintf(buffer+length, "{\"type\": \"temperature\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"value\": \"%.*f\" },",
length += sprintf(buffer+length, "{\"type\": \"temperature\", \"id\": \"%s%s\", \"name\": \"%s\", \"state\": \"%s\", \"value\": \"%.*f\" },",
FULL_SENSOR_TOPIC,
aqdata->sensors[i].ID,
aqdata->sensors[i].label,
"on",

View File

@ -739,7 +739,7 @@ void publish_mqtt_discovery(struct aqualinkdata *aqdata, struct mg_connection *n
for (i=0; i < aqdata->num_sensors; i++) {
//sprintf(idbuf, "%s_%s","sensor",aqdata->sensors[i].label);
//sprintf(topic, "%s/%s",SENSOR_TOPIC,aqdata->sensors[i].label);
sprintf(topic, "%s/%s",SENSOR_TOPIC,aqdata->sensors[i].ID);
sprintf(topic, "%s%s",FULL_SENSOR_TOPIC,aqdata->sensors[i].ID);
rsm_char_replace(idbuf, topic, "/", "_");
//sprintf(msg, HASSIO_SENSOR_DISCOVER,connections,_aqconfig_.mqtt_aq_topic,idbuf,aqdata->sensors[i].label,_aqconfig_.mqtt_aq_topic,topic, "°C", "mdi:thermometer");
// Use HASSIO_TEMP_SENSOR_DISCOVER over HASSIO_SENSOR_DISCOVER since it has device class temperature and HA will convert automatically.

View File

@ -940,7 +940,7 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
for (i=0; i < _aqualink_data->num_sensors; i++) {
if ( _aqualink_data->sensors[i].value != TEMP_UNKNOWN && _last_mqtt_aqualinkdata.sensors[i].value != _aqualink_data->sensors[i].value) {
char topic[50];
sprintf(topic, "%s/%s", SENSOR_TOPIC, _aqualink_data->sensors[i].ID);
sprintf(topic, "%s%s", FULL_SENSOR_TOPIC, _aqualink_data->sensors[i].ID);
send_mqtt_float_msg(nc, topic, _aqualink_data->sensors[i].value);
_last_mqtt_aqualinkdata.sensors[i].value = _aqualink_data->sensors[i].value;
}