pull/94/head
sf 2023-05-16 11:13:42 -05:00
parent 6f16015593
commit a146f1d171
19 changed files with 464 additions and 55 deletions

View File

@ -23,9 +23,10 @@ https://github.com/sfeakes/AqualinkD/wiki
For information on Control panel versions and upgrading the chips.<br>
https://github.com/sfeakes/AqualinkD/wiki/Upgrading-Jandy-Aqualink-PDA-to-RS-panel
<!--
Here's where I started to document what I know about the Jandy RS485 protocol.<br>
https://github.com/sfeakes/AqualinkD/wiki/Jandy-Aqualink-RS485-protocol
-->
## AqualinkD built in WEB Interface(s).
@ -74,10 +75,9 @@ Designed to mimic AqualinkRS6 All Button keypad and (like the keypad) is used to
* http://aqualink.ip/debug.html <- (Turn on/off debug/serial debug & download logs)
#<a name="release"></a>
# ToDo (future release)
* Allow selecting of pre-defined VSP programs
* Update homekit-aqualinkd to use new API & features.
* Add light programming to Aqualink Touch protocol.
* Add set time to Aqualink OneTouch protocol
* Allow selecting of pre-defined VSP programs (Aqualink Touch & OneTouch protocols.)
* Add light programming to Aqualink Touch & OneTouch protocols.
* Add set time to OneTouch protocol.
# Update in Release 2.3.0 (pre release)
* This is pre-release, please treat it as such.

View File

@ -627,6 +627,10 @@ bool panel_device_request(struct aqualinkdata *aqdata, action_type type, int dev
case ON_OFF:
//setDeviceState(&aqdata->aqbuttons[deviceIndex], value<=0?false:true, deviceIndex );
setDeviceState(aqdata, deviceIndex, value<=0?false:true );
// Clear timer if off request and timer is active
if (value<=0 && (aqdata->aqbuttons[deviceIndex].special_mask & TIMER_ACTIVE) == TIMER_ACTIVE ) {
clear_timer(aqdata, &aqdata->aqbuttons[deviceIndex]);
}
break;
case TIMER:
//setDeviceState(&aqdata->aqbuttons[deviceIndex], true);

View File

@ -84,6 +84,8 @@ bool push_aq_cmd(unsigned char cmd);
void waitfor_queue2empty();
void longwaitfor_queue2empty();
void _aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_data, bool allowOveride);
#define MAX_STACK 20
int _stack_place = 0;
unsigned char _commands[MAX_STACK];
@ -222,6 +224,50 @@ unsigned char pop_aq_cmd_OLD(struct aqualinkdata *aq_data)
}
*/
/* Maybe use in future, not needed for moment
Idea here was to convert all button on/off commands to rs_serial_adapter commands to use if in all button probramming mode */
/*
unsigned char AllButton2RSsrialAdapter(unsigned char abcmd)
{
switch(abcmd) {
case KEY_PUMP:
return RS_SA_PUMP;
break;
case KEY_SPA:
return RS_SA_SPA;
break;
case KEY_AUX1:
return RS_SA_AUX1;
break;
case KEY_AUX2:
return RS_SA_AUX2;
break;
case KEY_AUX3:
return RS_SA_AUX3;
break;
case KEY_AUX4:
return RS_SA_AUX4;
break;
case KEY_AUX5:
return RS_SA_AUX5;
break;
case KEY_AUX6:
return RS_SA_AUX6;
break;
case KEY_AUX7:
return RS_SA_AUX7;
break;
case KEY_POOL_HTR:
return RS_SA_POOLHT;
break;
case KEY_SPA_HTR:
return RS_SA_SPAHT;
break;
}
return NUL;
}
*/
int roundTo(int num, int denominator) {
return ((num + (denominator/2) ) / denominator )* denominator;
}
@ -333,7 +379,78 @@ int setpoint_check(int type, int value, struct aqualinkdata *aqdata)
return rtn;
}
/*
Figure out the fastest way in get all needed startup data depending on what protocols
are available and what's just called us
*/
void queueGetProgramData(emulation_type source_type, struct aqualinkdata *aq_data)
{
LOG(PROG_LOG, LOG_INFO, "Initial setup call from %s with RSSA=%s ONETouch=%s IAQTouch=%s ExtendedProgramming=%s\n",
(source_type == ALLBUTTON)?"AllButton":((source_type == RSSADAPTER)?"RSSA":((source_type == ONETOUCH)?"OneTouch":((source_type == IAQTOUCH)?"IAQTouch":"PDA"))),
(isRSSA_ENABLED)?"enabled":"disabled",
(isONET_ENABLED)?"enabled":"disabled",
(isIAQT_ENABLED)?"enabled":"disabled",
(isEXTP_ENABLED)?"enabled":"disabled"
);
if (isRSSA_ENABLED && isEXTP_ENABLED == true) {
// serial adapter enabled and extended programming
if (source_type == RSSADAPTER) {
_aq_programmer(AQ_GET_RSSADAPTER_SETPOINTS, NULL, aq_data, false);
} else if (source_type == ONETOUCH && isEXTP_ENABLED) {
//_aq_programmer(AQ_GET_ONETOUCH_FREEZEPROTECT, NULL, aq_data, false); // Add back and remove below once tested and working
//_aq_programmer(AQ_GET_ONETOUCH_SETPOINTS, NULL, aq_data, false);
} else if (source_type == IAQTOUCH && isEXTP_ENABLED) {
//_aq_programmer(AQ_GET_IAQTOUCH_FREEZEPROTECT, NULL, aq_data, false); // Add back and remove below once tested and working
//_aq_programmer(AQ_GET_IAQTOUCH_SETPOINTS, NULL, aq_data, false); // This get's freeze & heaters, we should just get freeze if isRSSA_ENABLED
} else if (source_type == ALLBUTTON) {
_aq_programmer(AQ_GET_FREEZE_PROTECT_TEMP, NULL, aq_data, false); // This is still quicker that IAQ or ONE Touch protocols at the moment.
if (_aqconfig_.use_panel_aux_labels) {
_aq_programmer(AQ_GET_AUX_LABELS, NULL, aq_data, false);
}
}
} else if (isRSSA_ENABLED && isEXTP_ENABLED == false) {
// serial adapter enabled with no extended programming
if (source_type == RSSADAPTER) {
_aq_programmer(AQ_GET_RSSADAPTER_SETPOINTS, NULL, aq_data, false);
} else if (source_type == ALLBUTTON) {
_aq_programmer(AQ_GET_FREEZE_PROTECT_TEMP, NULL, aq_data, false);
if (_aqconfig_.use_panel_aux_labels) {
_aq_programmer(AQ_GET_AUX_LABELS, NULL, aq_data, false);
}
}
} else if (!isRSSA_ENABLED && isEXTP_ENABLED && isONET_ENABLED) {
// One touch extended and no serial adapter
if (source_type == ONETOUCH) {
_aq_programmer(AQ_GET_ONETOUCH_SETPOINTS, NULL, aq_data, false);
} else if (source_type == ALLBUTTON) {
if (_aqconfig_.use_panel_aux_labels) {
_aq_programmer(AQ_GET_AUX_LABELS, NULL, aq_data, false);
}
}
} else if (!isRSSA_ENABLED && isEXTP_ENABLED && isIAQT_ENABLED) {
// IAQ touch extended and no serial adapter
if (source_type == IAQTOUCH) {
_aq_programmer(AQ_GET_IAQTOUCH_SETPOINTS, NULL, aq_data, false);
} else if (source_type == ALLBUTTON) {
if (_aqconfig_.use_panel_aux_labels) {
_aq_programmer(AQ_GET_AUX_LABELS, NULL, aq_data, false);
}
}
#ifdef AQ_PDA
} else if ( source_type == AQUAPDA) {
aq_programmer(AQ_PDA_INIT, NULL, aq_data);
#endif
} else { // Must be all button only
aq_programmer(AQ_GET_POOL_SPA_HEATER_TEMPS, NULL, aq_data);
aq_programmer(AQ_GET_FREEZE_PROTECT_TEMP, NULL, aq_data);
if (_aqconfig_.use_panel_aux_labels) {
aq_programmer(AQ_GET_AUX_LABELS, NULL, aq_data);
}
}
}
void queueGetProgramData_OLD(emulation_type source_type, struct aqualinkdata *aq_data)
{
// Wait for onetouch if enabeled.
//if ( source_type == ALLBUTTON && ( onetouch_enabled() == false || extended_device_id_programming() == false ) ) {
@ -341,15 +458,20 @@ void queueGetProgramData(emulation_type source_type, struct aqualinkdata *aq_dat
aq_programmer(AQ_GET_RSSADAPTER_SETPOINTS, NULL, aq_data);
} else if ( source_type == ALLBUTTON && ( isEXTP_ENABLED == false || (isONET_ENABLED == false && isIAQT_ENABLED == false)) ) {
aq_send_cmd(NUL);
if (!isRSSA_ENABLED)
if (!isRSSA_ENABLED) {
aq_programmer(AQ_GET_POOL_SPA_HEATER_TEMPS, NULL, aq_data);
}
aq_programmer(AQ_GET_FREEZE_PROTECT_TEMP, NULL, aq_data);
if (_aqconfig_.use_panel_aux_labels)
if (_aqconfig_.use_panel_aux_labels) {
aq_programmer(AQ_GET_AUX_LABELS, NULL, aq_data);
}
#ifdef AQ_ONETOUCH
} else if ( source_type == ONETOUCH) {
//if (!isRSSA_ENABLED)
aq_programmer(AQ_GET_ONETOUCH_SETPOINTS, NULL, aq_data); // This get's freeze & heaters, we should just get freeze if isRSSA_ENABLED
if (!isRSSA_ENABLED) {
aq_programmer(AQ_GET_ONETOUCH_FREEZEPROTECT, NULL, aq_data);
} else {
aq_programmer(AQ_GET_ONETOUCH_SETPOINTS, NULL, aq_data); // This get's freeze & heaters, we should just get freeze if isRSSA_ENABLED
}
if (_aqconfig_.use_panel_aux_labels)
aq_programmer(AQ_GET_AUX_LABELS, NULL, aq_data);
#endif
@ -454,6 +576,34 @@ bool in_iaqt_programming_mode(struct aqualinkdata *aq_data)
return false;
}
/*
Don't need this at the moment, it's kind-a inverse of above two functions
bool in_allb_programming_mode(struct aqualinkdata *aq_data)
{
if ( ( aq_data->active_thread.thread_id != 0 ) &&
( aq_data->active_thread.ptype == AQ_GET_POOL_SPA_HEATER_TEMPS ||
aq_data->active_thread.ptype == AQ_GET_FREEZE_PROTECT_TEMP ||
aq_data->active_thread.ptype == AQ_SET_TIME ||
aq_data->active_thread.ptype == AQ_SET_POOL_HEATER_TEMP ||
aq_data->active_thread.ptype == AQ_SET_SPA_HEATER_TEMP ||
aq_data->active_thread.ptype == AQ_SET_FRZ_PROTECTION_TEMP ||
aq_data->active_thread.ptype == AQ_GET_DIAGNOSTICS_MODEL ||
aq_data->active_thread.ptype == AQ_GET_PROGRAMS ||
aq_data->active_thread.ptype == AQ_SET_LIGHTPROGRAM_MODE ||
aq_data->active_thread.ptype == AQ_SET_LIGHTCOLOR_MODE ||
aq_data->active_thread.ptype == AQ_SET_SWG_PERCENT ||
aq_data->active_thread.ptype == AQ_GET_AUX_LABELS ||
aq_data->active_thread.ptype == AQ_SET_BOOST ||
aq_data->active_thread.ptype == AQ_SET_PUMP_RPM ||
aq_data->active_thread.ptype == AQ_SET_PUMP_VS_PROGRAM)
) {
return true;
}
return false;
}
*/
bool in_programming_mode(struct aqualinkdata *aq_data)
{
if ( aq_data->active_thread.thread_id != 0 ) {
@ -468,7 +618,7 @@ void kick_aq_program_thread(struct aqualinkdata *aq_data, emulation_type source_
if ( aq_data->active_thread.thread_id != 0 ) {
if ( (source_type == ONETOUCH) && in_ot_programming_mode(aq_data))
{
LOG(PROG_LOG, LOG_DEBUG, "Kicking OneTouch thread %d,%p\n",aq_data->active_thread.ptype, aq_data->active_thread.thread_id);
LOG(ONET_LOG, LOG_DEBUG, "Kicking OneTouch thread %d,%p\n",aq_data->active_thread.ptype, aq_data->active_thread.thread_id);
pthread_cond_broadcast(&aq_data->active_thread.thread_cond);
}
else if (source_type == ALLBUTTON && !in_ot_programming_mode(aq_data)) {
@ -476,19 +626,23 @@ void kick_aq_program_thread(struct aqualinkdata *aq_data, emulation_type source_
pthread_cond_broadcast(&aq_data->active_thread.thread_cond);
}
else if (source_type == IAQTOUCH && in_iaqt_programming_mode(aq_data)) {
LOG(PROG_LOG, LOG_DEBUG, "Kicking IAQ Touch thread %d,%p\n",aq_data->active_thread.ptype, aq_data->active_thread.thread_id);
LOG(IAQT_LOG, LOG_DEBUG, "Kicking IAQ Touch thread %d,%p\n",aq_data->active_thread.ptype, aq_data->active_thread.thread_id);
pthread_cond_broadcast(&aq_data->active_thread.thread_cond);
}
#ifdef AQ_PDA
else if (source_type == AQUAPDA && !in_ot_programming_mode(aq_data)) {
LOG(PROG_LOG, LOG_DEBUG, "Kicking PDA thread %d,%p\n",aq_data->active_thread.ptype, aq_data->active_thread.thread_id);
LOG(PDA_LOG, LOG_DEBUG, "Kicking PDA thread %d,%p\n",aq_data->active_thread.ptype, aq_data->active_thread.thread_id);
pthread_cond_broadcast(&aq_data->active_thread.thread_cond);
}
#endif
}
}
void aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_data)
void aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_data){
_aq_programmer(r_type, args, aq_data, true);
}
void _aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_data, bool allowOveride)
{
struct programmingThreadCtrl *programmingthread = malloc(sizeof(struct programmingThreadCtrl));
@ -580,7 +734,7 @@ void aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_data
else if (isONET_ENABLED && isEXTP_ENABLED) {
switch (r_type){
case AQ_GET_POOL_SPA_HEATER_TEMPS:
case AQ_GET_FREEZE_PROTECT_TEMP:
//case AQ_GET_FREEZE_PROTECT_TEMP:
type = AQ_GET_ONETOUCH_SETPOINTS;
break;
case AQ_SET_POOL_HEATER_TEMP:
@ -610,7 +764,7 @@ void aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_data
// IAQ Touch programming modes that should overite standard ones.
switch (r_type){
case AQ_GET_POOL_SPA_HEATER_TEMPS:
case AQ_GET_FREEZE_PROTECT_TEMP:
//case AQ_GET_FREEZE_PROTECT_TEMP:
type = AQ_GET_IAQTOUCH_SETPOINTS;
break;
case AQ_SET_SWG_PERCENT:
@ -658,7 +812,12 @@ void aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_data
}
#endif
LOG(PROG_LOG, LOG_INFO, "Starting programming thread '%s'\n",ptypeName(type));
if (!allowOveride) {
// Reset anything back from changes above.
type = r_type;
}
LOG(PROG_LOG, LOG_NOTICE, "Starting programming thread '%s'\n",ptypeName(type));
programmingthread->aq_data = aq_data;
programmingthread->thread_id = 0;
@ -794,6 +953,12 @@ void aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_data
return;
}
break;
case AQ_GET_ONETOUCH_FREEZEPROTECT:
if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_onetouch_freezeprotect, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_GET_ONETOUCH_SETPOINTS:
if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_onetouch_setpoints, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
@ -856,6 +1021,12 @@ void aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_data
return;
}
break;
case AQ_GET_IAQTOUCH_FREEZEPROTECT:
if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_iaqtouch_freezeprotect, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_GET_IAQTOUCH_AUX_LABELS:
if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_iaqtouch_aux_labels, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
@ -967,8 +1138,8 @@ void waitForSingleThreadOrTerminate(struct programmingThreadCtrl *threadCtrl, pr
if (i >= tries) {
//LOG(PROG_LOG, LOG_ERR, "Thread %d timeout waiting, ending\n",threadCtrl->thread_id);
LOG(PROG_LOG, LOG_ERR, "Thread %d,%p timeout waiting for thread %d,%p to finish\n",
type, &threadCtrl->thread_id, threadCtrl->aq_data->active_thread.ptype,
LOG(PROG_LOG, LOG_ERR, "Thread (%s) %p timeout waiting for thread (%s) %p to finish\n",
ptypeName(type), &threadCtrl->thread_id, ptypeName(threadCtrl->aq_data->active_thread.ptype),
threadCtrl->aq_data->active_thread.thread_id);
free(threadCtrl);
pthread_exit(0);
@ -983,7 +1154,7 @@ void waitForSingleThreadOrTerminate(struct programmingThreadCtrl *threadCtrl, pr
clock_gettime(CLOCK_REALTIME, &threadCtrl->aq_data->start_active_time);
#endif
LOG(PROG_LOG, LOG_NOTICE, "Programming: %s, %d\n", ptypeName(threadCtrl->aq_data->active_thread.ptype), threadCtrl->aq_data->active_thread.ptype);
LOG(PROG_LOG, LOG_INFO, "Programming: %s, %d\n", ptypeName(threadCtrl->aq_data->active_thread.ptype), threadCtrl->aq_data->active_thread.ptype);
LOG(PROG_LOG, LOG_DEBUG, "Thread %d,%p is active (%s)\n",
threadCtrl->aq_data->active_thread.ptype,
@ -1073,6 +1244,8 @@ bool setAqualinkNumericField_new(struct aqualinkdata *aq_data, char *value_label
return true;
}
bool OLD_setAqualinkNumericField_OLD(struct aqualinkdata *aq_data, char *value_label, int value)
{ // Works for everything but not SWG
LOG(PROG_LOG, LOG_DEBUG,"Setting menu item '%s' to %d\n",value_label, value);
@ -2277,12 +2450,15 @@ bool select_sub_menu_item(struct aqualinkdata *aq_data, char* item_string)
int wait_messages = 28;
int i=0;
waitfor_queue2empty();
while( (stristr(aq_data->last_message, item_string) == NULL) && ( i++ < wait_messages) )
{
LOG(PROG_LOG, LOG_DEBUG, "Find item in Menu: loop %d of %d looking for '%s' received message '%s'\n",i,wait_messages,item_string,aq_data->last_message);
send_cmd(KEY_RIGHT);
waitfor_queue2empty(); // ADDED BACK MAY 2023
waitForMessage(aq_data, NULL, 1);
waitfor_queue2empty(); // ADDED BACK MAY 2023 setting time warked better
//waitForMessage(aq_data, NULL, 1);
waitForMessage(aq_data, item_string, 1);
}
if (stristr(aq_data->last_message, item_string) == NULL) {
@ -2406,6 +2582,9 @@ const char *ptypeName(program_type type)
break;
case AQ_GET_ONETOUCH_SETPOINTS:
return "Get OneTouch setpoints";
break;
case AQ_GET_ONETOUCH_FREEZEPROTECT:
return "Get OneTouch freezeprotect";
break;
case AQ_SET_ONETOUCH_TIME:
return "Set OneTouch time";
@ -2439,6 +2618,9 @@ const char *ptypeName(program_type type)
case AQ_GET_IAQTOUCH_SETPOINTS:
return "Get iAqualink Touch Setpoints";
break;
case AQ_GET_IAQTOUCH_FREEZEPROTECT:
return "Get iAqualink Touch Freezeprotect";
break;
case AQ_GET_IAQTOUCH_AUX_LABELS:
return "Get iAqualink AUX Labels";
break;
@ -2488,6 +2670,8 @@ const char *programtypeDisplayName(program_type type)
case AQ_GET_FREEZE_PROTECT_TEMP:
case AQ_GET_IAQTOUCH_SETPOINTS:
case AQ_GET_RSSADAPTER_SETPOINTS:
case AQ_GET_ONETOUCH_FREEZEPROTECT:
case AQ_GET_IAQTOUCH_FREEZEPROTECT:
#ifdef AQ_PDA
case AQ_PDA_INIT:
#endif

View File

@ -5,9 +5,9 @@
#include <pthread.h>
//#include "aqualink.h"
//#define PROGRAMMING_POLL_DELAY_TIME 10
#define PROGRAMMING_POLL_DELAY_TIME 10
//#define PROGRAMMING_POLL_DELAY_TIME 2
#define PROGRAMMING_POLL_DELAY_TIME 5
//#define PROGRAMMING_POLL_DELAY_TIME 5
#define PROGRAMMING_POLL_COUNTER 200
// need to get the C values from aqualink manual and add those just incase
@ -64,6 +64,7 @@ typedef enum {
AQ_GET_ONETOUCH_SETPOINTS,
AQ_SET_ONETOUCH_POOL_HEATER_TEMP,
AQ_SET_ONETOUCH_SPA_HEATER_TEMP,
AQ_GET_ONETOUCH_FREEZEPROTECT,
AQ_SET_ONETOUCH_FREEZEPROTECT,
AQ_SET_ONETOUCH_TIME,
AQ_SET_ONETOUCH_BOOST,
@ -72,6 +73,8 @@ typedef enum {
AQ_SET_IAQTOUCH_PUMP_VS_PROGRAM,
AQ_GET_IAQTOUCH_VSP_ASSIGNMENT,
AQ_GET_IAQTOUCH_SETPOINTS,
AQ_GET_IAQTOUCH_FREEZEPROTECT,
AQ_SET_IAQTOUCH_FREEZEPROTECT,
AQ_GET_IAQTOUCH_AUX_LABELS,
AQ_SET_IAQTOUCH_SWG_PERCENT,
AQ_SET_IAQTOUCH_SWG_BOOST,
@ -110,6 +113,7 @@ bool in_ot_programming_mode(struct aqualinkdata *aq_data);
bool in_iaqt_programming_mode(struct aqualinkdata *aq_data);
bool in_swg_programming_mode(struct aqualinkdata *aq_data);
bool in_light_programming_mode(struct aqualinkdata *aq_data);
bool in_allb_programming_mode(struct aqualinkdata *aq_data);
void aq_send_cmd(unsigned char cmd);
void queueGetProgramData(emulation_type source_type, struct aqualinkdata *aq_data);
//void queueGetExtendedProgramData(emulation_type source_type, struct aqualinkdata *aq_data, bool labels);

View File

@ -19,6 +19,7 @@ struct timerthread {
struct aqualinkdata *aq_data;
int duration_min;
struct timespec timeout;
time_t started_at;
struct timerthread *next;
struct timerthread *prev;
};
@ -27,8 +28,56 @@ struct timerthread {
void *timer_worker( void *ptr );
struct timerthread *find_timerthread(aqkey *button)
{
struct timerthread *t_ptr;
if (_timerthread_ll != NULL) {
for (t_ptr = _timerthread_ll; t_ptr != NULL; t_ptr = t_ptr->next) {
if (t_ptr->button == button) {
return t_ptr;
}
}
}
return NULL;
}
int get_timer_left(aqkey *button)
{
struct timerthread *t_ptr = find_timerthread(button);
if (t_ptr != NULL) {
time_t now = time(0);
double seconds = difftime(now, t_ptr->started_at);
return (int) ((t_ptr->duration_min - (seconds / 60)) +0.5) ;
}
return 0;
}
void clear_timer(struct aqualinkdata *aq_data, aqkey *button)
{
struct timerthread *t_ptr = find_timerthread(button);
if (t_ptr != NULL) {
LOG(TIMR_LOG, LOG_INFO, "Clearing timer for '%s'\n",t_ptr->button->name);
t_ptr->duration_min = 0;
pthread_cond_broadcast(&t_ptr->thread_cond);
}
}
void start_timer(struct aqualinkdata *aq_data, aqkey *button, int duration)
{
struct timerthread *t_ptr = find_timerthread(button);
if (t_ptr != NULL) {
LOG(TIMR_LOG, LOG_INFO, "Timer already active for '%s', resetting\n",t_ptr->button->name);
t_ptr->duration_min = duration;
pthread_cond_broadcast(&t_ptr->thread_cond);
return;
}
/*
struct timerthread *t_ptr;
if (_timerthread_ll != NULL) {
@ -40,7 +89,7 @@ void start_timer(struct aqualinkdata *aq_data, aqkey *button, int duration)
return;
}
}
}
}*/
struct timerthread *tmthread = calloc(1, sizeof(struct timerthread));
tmthread->aq_data = aq_data;
@ -81,7 +130,7 @@ void *timer_worker( void *ptr )
tmthread = (struct timerthread *) ptr;
int retval = 0;
LOG(TIMR_LOG, LOG_NOTICE, "Started for button '%s'\n",tmthread->button->name);
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;
@ -108,17 +157,23 @@ void *timer_worker( void *ptr )
if (retval != 0) {
LOG(TIMR_LOG, LOG_ERR, "pthread_cond_timedwait failed for '%s', error %d %s\n",tmthread->button->name,retval,strerror(retval));
break;
} else if (tmthread->duration_min <= 0) {
//LOG(TIMR_LOG, LOG_INFO, "Timer has been reset to 0 for '%s'\n",tmthread->button->name);
break;
}
clock_gettime(CLOCK_REALTIME, &tmthread->timeout);
tmthread->timeout.tv_sec += (tmthread->duration_min * 60);
tmthread->started_at = time(0);
LOG(TIMR_LOG, LOG_INFO, "Will turn off '%s' in %d minutes\n",tmthread->button->name, tmthread->duration_min);
} while ((retval = pthread_cond_timedwait(&tmthread->thread_cond, &tmthread->thread_mutex, &tmthread->timeout)) != ETIMEDOUT);
pthread_mutex_unlock(&tmthread->thread_mutex);
LOG(TIMR_LOG, LOG_NOTICE, "End timer for '%s'\n",tmthread->button->name);
if (tmthread->button->led->state != OFF) {
LOG(TIMR_LOG, LOG_INFO, "waking, turning off '%s'\n",tmthread->button->name);
LOG(TIMR_LOG, LOG_INFO, "Timer waking turning '%s' off\n",tmthread->button->name);
#ifdef AQ_PDA
if (isPDA_PANEL)
create_PDA_on_off_request(tmthread->button, false);
@ -126,7 +181,7 @@ void *timer_worker( void *ptr )
#endif
aq_send_cmd(tmthread->button->code);
} else {
LOG(TIMR_LOG, LOG_INFO, "waking '%s' is already off\n",tmthread->button->name);
LOG(TIMR_LOG, LOG_INFO, "Timer waking '%s' is already off\n",tmthread->button->name);
}
// remove mask so we know timer is dead

View File

@ -5,7 +5,8 @@
#include "aqualink.h"
void start_timer(struct aqualinkdata *aq_data, aqkey *button, int duration);
int get_timer_left(aqkey *button);
void clear_timer(struct aqualinkdata *aq_data, aqkey *button);
// Not best place for this, but leave it here so all requests are in net services, this is forward decleration of function in net_services.c
#ifdef AQ_PDA
void create_PDA_on_off_request(aqkey *button, bool isON);

View File

@ -13,13 +13,14 @@
#define DEFAULT_POLL_SPEED_NON_THREADDED 2
#define TIME_CHECK_INTERVAL 3600
//#define TIME_CHECK_INTERVAL 100 // DEBUG ONLY
#define ACCEPTABLE_TIME_DIFF 120
// Use these settings to test time
//#define TIME_CHECK_INTERVAL 100
//#define ACCEPTABLE_TIME_DIFF 10
#define MAX_ZERO_READ_BEFORE_RECONNECT 10000 // 2k normally
#define MAX_ZERO_READ_BEFORE_RECONNECT 20000 // 2k normally
// The below will change state of devices before that are actually set on the control panel, this helps
// with duplicate messages that come in quick succession that can catch the state before it happens.
@ -77,7 +78,7 @@ typedef struct aqualinkkey
// special_mask for above aqualinkkey structure.
#define VS_PUMP (1 << 0)
#define PROGRAM_LIGHT (1 << 1)
#define TIMER_ACTIVE (1 << 2) // Not used yet, but will need to timer
#define TIMER_ACTIVE (1 << 2)
//typedef struct ProgramThread ProgramThread; // Definition is later

View File

@ -168,27 +168,54 @@ bool checkAqualinkTime()
else
#endif // AQ_PDA
{
strcpy(&datestr[0], _aqualink_data.date);
strcpy(&datestr[12], " ");
strcpy(&datestr[13], _aqualink_data.time);
if (strptime(datestr, "%m/%d/%y %a %I:%M %p", &aq_tm) == NULL)
datestr[8] = ' ';
strcpy(&datestr[9], _aqualink_data.time);
//datestr[16] = ' ';
if (strlen(_aqualink_data.time) <= 7) {
datestr[13] = ' ';
datestr[16] ='\0';
} else {
datestr[14] = ' ';
datestr[17] ='\0';
}
if (strptime(datestr, "%m/%d/%y %I:%M %p", &aq_tm) == NULL)
//sprintf(datestr, "%s %s", _aqualink_data.date, _aqualink_data.time);
//if (strptime(datestr, "%m/%d/%y %a %I:%M %p", &aq_tm) == NULL)
{
LOG(AQUA_LOG,LOG_ERR, "Could not convert RS time string '%s'", datestr);
LOG(AQUA_LOG,LOG_ERR, "Could not convert RS time string '%s'\n", datestr);
last_checked = (time_t)NULL;
return true;
}
}
aq_tm.tm_isdst = -1; // Force mktime to use local timezone
aq_tm.tm_isdst = localtime(&now)->tm_isdst; // ( Might need to use -1) set daylight savings to same as system time
aq_tm.tm_sec = 0; // Set seconds to time. Really messes up when we don't do this.
char buff[20];
LOG(AQUA_LOG,LOG_DEBUG, "Aqualinkd created time from : %s\n", datestr);
strftime(buff, 20, "%Y-%m-%d %H:%M:%S", &aq_tm);
LOG(AQUA_LOG,LOG_DEBUG, "Aqualinkd created time : %s\n", buff);
aqualink_time = mktime(&aq_tm);
strftime(buff, 20, "%Y-%m-%d %H:%M:%S", localtime(&aqualink_time));
LOG(AQUA_LOG,LOG_DEBUG, "Aqualinkd converted time : %s\n", buff);
strftime(buff, 20, "%Y-%m-%d %H:%M:%S", localtime(&now));
LOG(AQUA_LOG,LOG_DEBUG, "System time : %s\n", buff);
time_difference = (int)difftime(now, aqualink_time);
LOG(AQUA_LOG,LOG_INFO, "Aqualink time is off by %d seconds...\n", time_difference);
strftime(buff, 20, "%m/%d/%y %I:%M %p", localtime(&now));
LOG(AQUA_LOG,LOG_NOTICE, "Aqualink time '%s' is off system time '%s' by %d seconds...\n", datestr, buff, time_difference);
if (abs(time_difference) <= ACCEPTABLE_TIME_DIFF)
if (abs(time_difference) < ACCEPTABLE_TIME_DIFF)
{
// Time difference is less than or equal to 90 seconds (1 1/2 minutes).
// Time difference is less than or equal to ACCEPTABLE_TIME_DIFF seconds (1 1/2 minutes).
// Set the return value to true.
return true;
}
@ -595,6 +622,7 @@ void _processMessage(char *message, bool reset)
LOG(AQRS_LOG,LOG_NOTICE, "Control Panel %s\n", msg);
if (_initWithRS == false)
{
//LOG(ALLBUTTON,LOG_NOTICE, "Standard protocol initialization complete\n");
queueGetProgramData(ALLBUTTON, &_aqualink_data);
//queueGetExtendedProgramData(ALLBUTTON, &_aqualink_data, _aqconfig_.use_panel_aux_labels);
_initWithRS = true;
@ -1624,7 +1652,7 @@ void main_loop()
blank_read = 0;
if (packet_buffer[PKT_CMD] == CMD_PROBE) {
got_probe = true;
LOG(AQUA_LOG,LOG_DEBUG, "Got probe on '0x%02hhx'\n",_aqconfig_.device_id);
LOG(AQUA_LOG,LOG_NOTICE, "Got probe on '0x%02hhx' Standard Protocol\n",_aqconfig_.device_id);
} else {
if(!print_once) {
LOG(AQUA_LOG,LOG_NOTICE, "Got message but no probe on '0x%02hhx', did we start too soon? (waiting for probe)\n",_aqconfig_.device_id);
@ -1637,7 +1665,7 @@ void main_loop()
blank_read = 0;
if (packet_buffer[PKT_CMD] == CMD_PROBE) {
got_probe_rssa = true;
LOG(AQUA_LOG,LOG_DEBUG, "Got probe on '0x%02hhx'\n",_aqconfig_.rssa_device_id);
LOG(AQUA_LOG,LOG_NOTICE, "Got probe on '0x%02hhx' RS SerialAdapter Protocol\n",_aqconfig_.rssa_device_id);
} else {
if(!print_once) {
LOG(AQUA_LOG,LOG_NOTICE, "Got message but no probe on '0x%02hhx', did we start too soon? (waiting for probe)\n",_aqconfig_.rssa_device_id);
@ -1651,7 +1679,7 @@ void main_loop()
blank_read = 0;
if (packet_buffer[PKT_CMD] == CMD_PROBE) {
got_probe_extended = true;
LOG(AQUA_LOG,LOG_DEBUG, "Got probe on '0x%02hhx'\n",_aqconfig_.extended_device_id);
LOG(AQUA_LOG,LOG_NOTICE, "Got probe on '0x%02hhx' Extended Protocol\n",_aqconfig_.extended_device_id);
} else {
if(!print_once) {
LOG(AQUA_LOG,LOG_NOTICE, "Got message but no probe on '0x%02hhx', did we start too soon? (waiting for probe)\n",_aqconfig_.extended_device_id);

View File

@ -545,7 +545,39 @@ void *set_aqualink_iaqtouch_vsp_assignments( void *ptr )
return ptr;
}
void *get_aqualink_iaqtouch_freezeprotect( void *ptr )
{
LOG(IAQT_LOG,LOG_ERR, "IAQ Touch get_aqualink_iaqtouch_freezeprotect has not beed tested\n");
LOG(IAQT_LOG,LOG_ERR, "**** We should not be here ****\n");
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
waitForSingleThreadOrTerminate(threadCtrl, AQ_GET_IAQTOUCH_FREEZEPROTECT);
if ( goto_iaqt_page(IAQ_PAGE_FREEZE_PROTECT, aq_data) == false )
goto f_end;
// The Message at index 0 is the deg that freeze protect is set to.
int frz = rsm_atoi(iaqtGetMessageLine(0));
if (frz >= 0) {
aq_data->frz_protect_set_point = frz;
LOG(IAQT_LOG,LOG_NOTICE, "IAQ Touch Freeze Protection setpoint %d\n",frz);
}
// Need to run over table messages and check ens with X for on off.
// Go to status page on startup to read devices
goto_iaqt_page(IAQ_PAGE_STATUS, aq_data);
f_end:
goto_iaqt_page(IAQ_PAGE_HOME, aq_data);
cleanAndTerminateThread(threadCtrl);
// just stop compiler error, ptr is not valid as it's just been freed
return ptr;
}
void *get_aqualink_iaqtouch_setpoints( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;

View File

@ -11,6 +11,7 @@ bool iaqt_queue_cmd(unsigned char cmd);
void *set_aqualink_iaqtouch_pump_rpm( void *ptr );
void *set_aqualink_iaqtouch_vsp_assignments( void *ptr );
void *get_aqualink_iaqtouch_setpoints( void *ptr );
void *get_aqualink_iaqtouch_freezeprotect( void *ptr );
void *get_aqualink_iaqtouch_aux_labels( void *ptr );
void *set_aqualink_iaqtouch_swg_percent( void *ptr );
void *set_aqualink_iaqtouch_swg_boost( void *ptr );

View File

@ -30,6 +30,7 @@
#include "aq_mqtt.h"
#include "devices_jandy.h"
#include "version.h"
#include "aq_timer.h"
//#define test_message "{\"type\": \"status\",\"version\": \"8157 REV MMM\",\"date\": \"09/01/16 THU\",\"time\": \"1:16 PM\",\"temp_units\": \"F\",\"air_temp\": \"96\",\"pool_temp\": \"86\",\"spa_temp\": \" \",\"battery\": \"ok\",\"pool_htr_set_pnt\": \"85\",\"spa_htr_set_pnt\": \"99\",\"freeze_protection\": \"off\",\"frz_protect_set_pnt\": \"0\",\"leds\": {\"pump\": \"on\",\"spa\": \"off\",\"aux1\": \"off\",\"aux2\": \"off\",\"aux3\": \"off\",\"aux4\": \"off\",\"aux5\": \"off\",\"aux6\": \"off\",\"aux7\": \"off\",\"pool_heater\": \"off\",\"spa_heater\": \"off\",\"solar_heater\": \"off\"}}"
@ -207,6 +208,9 @@ char *get_aux_information(aqkey *button, struct aqualinkdata *aqdata, char *buff
//printf("Button %s is Switch\n", button->name);
length += sprintf(buffer, ",\"type_ext\": \"switch_timer\", \"timer_active\":\"%s\"", (((button->special_mask & TIMER_ACTIVE) == TIMER_ACTIVE)?JSON_ON:JSON_OFF) );
if ((button->special_mask & TIMER_ACTIVE) == TIMER_ACTIVE) {
length += sprintf(buffer+length,",\"timer_duration\":\"%d\"", get_timer_left(button));
}
return buffer;
}
@ -545,13 +549,26 @@ printf("Pump Type %d\n",aqdata->pumps[i].pumpType);
{
if ((aqdata->aqbuttons[i].special_mask & TIMER_ACTIVE) == TIMER_ACTIVE) {
length += sprintf(buffer+length, "\"%s\": \"on\",", aqdata->aqbuttons[i].name);
//length += sprintf(buffer+length, "\"%s_duration\": \"%d\",", aqdata->aqbuttons[i].name, get_timer_left(&aqdata->aqbuttons[i]) );
}
}
if (buffer[length-1] == ',')
length--;
length += sprintf(buffer+length, "}");
length += sprintf(buffer+length, ",\"timer_durations\":{" );
for (i=0; i < aqdata->total_buttons; i++)
{
if ((aqdata->aqbuttons[i].special_mask & TIMER_ACTIVE) == TIMER_ACTIVE) {
length += sprintf(buffer+length, "\"%s\": \"%d\",", aqdata->aqbuttons[i].name, get_timer_left(&aqdata->aqbuttons[i]) );
}
}
if (buffer[length-1] == ',')
length--;
length += sprintf(buffer+length, "}");
length += sprintf(buffer+length, "}" );

View File

@ -113,7 +113,7 @@ static void ws_send(struct mg_connection *nc, char *msg)
mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, msg, size);
LOG(NET_LOG,LOG_DEBUG, "WS: Sent %d characters '%s'\n",size, msg);
//LOG(NET_LOG,LOG_DEBUG, "WS: Sent %d characters '%s'\n",size, msg);
}
void _broadcast_aqualinkstate_error(struct mg_connection *nc, char *msg)
@ -243,6 +243,20 @@ void send_mqtt_state_msg(struct mg_connection *nc, char *dev_name, aqledstate st
send_mqtt(nc, mqtt_pub_topic, (state==OFF?MQTT_OFF:MQTT_ON));
}
void send_mqtt_timer_duration_msg(struct mg_connection *nc, char *dev_name, aqkey *button)
{
static char mqtt_pub_topic[250];
sprintf(mqtt_pub_topic, "%s/%s/timer/duration",_aqconfig_.mqtt_aq_topic, dev_name);
if ((button->special_mask & TIMER_ACTIVE) == TIMER_ACTIVE) {
char val[10];
sprintf(val, "%d", get_timer_left(button));
send_mqtt(nc, mqtt_pub_topic, val);
} else {
send_mqtt(nc, mqtt_pub_topic, "0");
}
}
void send_mqtt_timer_state_msg(struct mg_connection *nc, char *dev_name, aqkey *button)
{
static char mqtt_pub_topic[250];
@ -250,6 +264,8 @@ void send_mqtt_timer_state_msg(struct mg_connection *nc, char *dev_name, aqkey *
sprintf(mqtt_pub_topic, "%s/%s/timer",_aqconfig_.mqtt_aq_topic, dev_name);
send_mqtt(nc, mqtt_pub_topic, ( ((button->special_mask & TIMER_ACTIVE) == TIMER_ACTIVE) && (button->led->state != OFF) )?MQTT_ON:MQTT_OFF );
send_mqtt_timer_duration_msg(nc, dev_name, button);
}
//void send_mqtt_aux_msg(struct mg_connection *nc, char *root_topic, int dev_index, char *dev_topic, int value)
@ -562,9 +578,16 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
}
send_mqtt_timer_state_msg(nc, _aqualink_data->aqbuttons[i].name, &_aqualink_data->aqbuttons[i]);
if (_aqualink_data->aqbuttons[i].dz_idx != DZ_NULL_IDX)
if (_aqualink_data->aqbuttons[i].dz_idx != DZ_NULL_IDX) {
send_domoticz_mqtt_state_msg(nc, _aqualink_data->aqbuttons[i].dz_idx, (_aqualink_data->aqbuttons[i].led->state==OFF?DZ_OFF:DZ_ON));
}
} else if ((_aqualink_data->aqbuttons[i].special_mask & TIMER_ACTIVE) == TIMER_ACTIVE) {
//send_mqtt_timer_duration_msg(nc, _aqualink_data->aqbuttons[i].name, &_aqualink_data->aqbuttons[i]);
// send_mqtt_timer_state_msg will call send_mqtt_timer_duration_msg so no need to do it here.
// Have to use send_mqtt_timer_state_msg due to a timer being set on a device that's already on, (ir no state change so above code does't get hit)
send_mqtt_timer_state_msg(nc, _aqualink_data->aqbuttons[i].name, &_aqualink_data->aqbuttons[i]);
}
}

View File

@ -541,6 +541,20 @@ void *set_aqualink_onetouch_pump_rpm( void *ptr )
return ptr;
}
void *set_aqualink_onetouch_freezeprotect( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_ONETOUCH_FREEZEPROTECT);
LOG(ONET_LOG,LOG_ERR, "***** OneTouch set freeze protect not implimented *****\n");
cleanAndTerminateThread(threadCtrl);
return ptr;
}
void *set_aqualink_onetouch_macro( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
@ -565,6 +579,36 @@ void *set_aqualink_onetouch_macro( void *ptr )
return ptr;
}
void *get_aqualink_onetouch_freezeprotect( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
waitForSingleThreadOrTerminate(threadCtrl, AQ_GET_ONETOUCH_FREEZEPROTECT);
LOG(ONET_LOG,LOG_DEBUG, "OneTouch get Freezeprotect\n");
if ( !goto_onetouch_menu(aq_data, OTM_SET_TEMP) ){
LOG(ONET_LOG,LOG_ERR, "OneTouch device programmer failed to get heater temp menu\n");
}
if ( !goto_onetouch_menu(aq_data, OTM_FREEZE_PROTECT) ){
LOG(ONET_LOG,LOG_ERR, "OneTouch device programmer failed to get freeze protect menu\n");
}
if (! goto_onetouch_menu(aq_data, OTM_SYSTEM) ){
LOG(ONET_LOG,LOG_ERR, "OneTouch device programmer didn't get back to System menu\n");
}
cleanAndTerminateThread(threadCtrl);
// just stop compiler error, ptr is not valid as it's just been freed
return ptr;
}
/*
This will get all setpoints, including freeze protect, above just gets freeze protect.
*/
void *get_aqualink_onetouch_setpoints( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
@ -860,10 +904,7 @@ void *set_aqualink_onetouch_swg_percent( void *ptr )
return ptr;
}
void *set_aqualink_onetouch_freezeprotect( void *ptr )
{
return ptr;
}
bool set_numeric_value(struct aqualinkdata *aq_data, int val) {
int len;

View File

@ -15,6 +15,7 @@ void *set_aqualink_onetouch_pool_heater_temp( void *ptr );
void *set_aqualink_onetouch_swg_percent( void *ptr );
void *set_aqualink_onetouch_boost( void *ptr );
void *set_aqualink_onetouch_time( void *ptr );
void *get_aqualink_onetouch_freezeprotect( void *ptr );
void *set_aqualink_onetouch_freezeprotect( void *ptr );
#endif // ONETOUCH_AQ_PROGRAMMER_H_

Binary file not shown.

Binary file not shown.

View File

@ -38,7 +38,7 @@
#define SLOG_MAX 80
#define PACKET_MAX 600
#define VERSION "serial_logger V1.5"
#define VERSION "serial_logger V1.6"
/*
typedef enum used {
@ -613,7 +613,7 @@ int main(int argc, char *argv[]) {
//LOG(RSSD_LOG, LOG_NOTICE, "ID 0x%02hhx is %s %s\n", slog[i].ID, (slog[i].inuse == true) ? "in use" : "not used",
// (slog[i].inuse == false && canUse(slog[i].ID) == true)? " <-- can use for Aqualinkd" : "");
if (logLevel >= LOG_DEBUG || slog[i].inuse == true || canUse(slog[i].ID) == true) {
LOG(RSSD_LOG, LOG_NOTICE, "ID 0x%02hhx is %s %s\n", slog[i].ID, (slog[i].inuse == true) ? "in use" : "not used",
LOG(RSSD_LOG, LOG_NOTICE, "ID 0x%02hhx is %s %s\n", slog[i].ID, (slog[i].inuse == true) ? "in use " : "not used",
(slog[i].inuse == false)?canUseExtended(slog[i].ID):getDevice(slog[i].ID));
}
}
@ -623,7 +623,7 @@ int main(int argc, char *argv[]) {
LOG(RSSD_LOG, LOG_NOTICE, "Pentair ID's found\n");
}
for (i=0; i < pent_sindex; i++) {
LOG(RSSD_LOG, LOG_NOTICE, "ID 0x%02hhx is %s %s\n", pent_slog[i].ID, (pent_slog[i].inuse == true) ? "in use" : "not used",
LOG(RSSD_LOG, LOG_NOTICE, "ID 0x%02hhx is %s %s\n", pent_slog[i].ID, (pent_slog[i].inuse == true) ? "in use " : "not used",
(pent_slog[i].inuse == false)?canUseExtended(pent_slog[i].ID):getPentairDevice(pent_slog[i].ID));
}

View File

@ -429,7 +429,7 @@ void logMessage(int msg_level, const char *format, ...)
_LOG(AQUA_LOG, msg_level, buffer);
}
*/
#define LOGBUFFER 4096
#define LOGBUFFER 5096
void LOG(int16_t from, int msg_level, const char * format, ...)
{

View File

@ -1105,6 +1105,15 @@
}
}
function toHoursAndMinutes(totalMinutes) {
const hours = Math.floor(totalMinutes / 60);
const minutes = totalMinutes % 60;
return `${padToTwoDigits(hours)}:${padToTwoDigits(minutes)}`;
}
function padToTwoDigits(num) {
return num.toString().padStart(2, "0");
}
function createTile(object) {
if (object.name == 'NONE') {
return;
@ -1132,7 +1141,11 @@
if (typeof object.timer_active !== 'undefined' && object.timer_active) {
//console.log("Timer for "+object.name+" = "+object.timer_active);
if (object.timer_active == 'on') {
setTileOnText(object.id, 'On (timer)');
if (typeof object.timer_duration !== 'undefined') {
setTileOnText(object.id, 'On (timer '+toHoursAndMinutes(object.timer_duration)+')');
} else {
setTileOnText(object.id, 'On (timer)');
}
}
}
} else if (typeof object.type_ext !== 'undefined' && object.type_ext == 'switch_vsp') {
@ -1998,6 +2011,10 @@
for (var obj in data.timers) {
setTileOnText(obj.toString(),"On (timer)");
}
for (var obj in data.timer_durations) {
setTileOnText(obj.toString(),"Timer "+toHoursAndMinutes(data.timer_durations[obj]));
}
if (data.swg_boost_msg != null ) {
var tile = document.getElementById('SWG');