Version 2.3.0

pull/94/head
sf 2023-05-14 16:35:13 -05:00
parent c4d9472870
commit 3156756927
220 changed files with 68400 additions and 522 deletions

View File

@ -19,7 +19,8 @@ AQ_IAQTOUCH = true
PI_OS_VERSION = $(shell cat /etc/os-release | grep VERSION= | cut -d\" -f2)
$(info OS: $(PI_OS_VERSION) )
GLIBC_VERSION = $(shell ldd --version | grep ldd)
$(info GLIBC: $(GLIBC_VERSION) )
$(info GLIBC build with: $(GLIBC_VERSION) )
$(info GLIBC Prefered : 2.24-11+deb9u1 2.24 )
# define the C compiler to use
@ -52,7 +53,7 @@ MGFLAGS = -D MG_DISABLE_MD5 -D MG_DISABLE_HTTP_DIGEST_AUTH -D MG_DISABLE_MD5 -D
# pda_aq_programmer.c devices_jandy.c onetouch.c onetouch_aq_programmer.c packetLogger.c devices_pentair.c color_lights.c mongoose.c
SRCS = aqualinkd.c utils.c config.c aq_serial.c aq_panel.c aq_programmer.c net_services.c json_messages.c rs_msg_utils.c\
devices_jandy.c packetLogger.c devices_pentair.c color_lights.c serialadapter.c mongoose.c
devices_jandy.c packetLogger.c devices_pentair.c color_lights.c serialadapter.c aq_timer.c aq_scheduler.c web_config.c mongoose.c
AQ_FLAGS =

View File

@ -43,7 +43,7 @@ https://github.com/sfeakes/AqualinkD/wiki/Jandy-Aqualink-RS485-protocol
<ul>
<li>The layout and functionality are from the Apple HomeKit interface. This works in any browser or on any mobile device.</li>
<li>Customizable tile icons & background images. (Tiles not used can be hidden).</li>
<li>Thermostat, SWG & Light tiles have more options (ie: setting heater temperature, salt generating percentage and light mode etc). These options are accessible by pressing and holding the tile icon.</li>
<li>Thermostat, Switch, SWG & Light tiles have more options (ie: setting heater temperature, timers, salt generating percentage and light mode etc). These options are accessible by pressing and holding the tile icon.</li>
<li>Supports live background images (ie: poll camera for still image every X seconds).</li>
</ul>
</td></tr>
@ -74,16 +74,29 @@ Designed to mimic AqualinkRS6 All Button keypad and (like the keypad) is used to
#<a name="release"></a>
# ToDo (future release)
* Allow selecting of pre-defined VSP programs
* Timed based actions / programs (undecided on cron & AqualinkD or use Jandy programs )
* One off timed actions (ir turn pump on for 2 hours)
* Put back some form of Jandy panel simulator. (existing was removed in V2.2.0)
* Add RS Serial protocol. AqualinkD has all Jandy control protocols except RS Serial.
* Update homekit-aqualinkd to use new API & features.
* Add light programming to Aqualink Touch protocol.
* Add set time to Aqualink OneTouch protocol
# Update in Release 2.3.0 (pre release)
* This is pre-release, please treat it as such.
* Don't use this release on PDA panels unless you can debug/change code <b>I have not been able to test it fully.</b>
* Changed a lot of logic around different protocols.
* AqualinkD will find out the fastest way to change something depending on the protocols available.
* Added scheduler (click time in web ui). supports full calendar year (ie seasons), See wiki for details.
* Added timers for devices (ie can turn on Pump for x minutes), Long press on device in WebUI.
* Timers supported in MQTT/API.
* Serial logging / error checking enhancements.
* Added simulator back. (still a number of issues).
* Fix issue with incorrect device state after duplicate MQTT messages being sent in rapid succession ( < 0.5 second).
* Found workaround for panel firmware bug in iAqualink Touch protocol where VSP updates sometimes got lost.
* Fix bug in IntelliBrite color lights
* Install script checks for cron and it's config (needed for scheduler)
# Update in Release 2.2.2
* Fixed some Web UI bugs
* Color lights now quicker when selecting existing comor mode.
# Update in Release 2.2.1
* Supports serial adapter protocol `rssa_device_id`, (provides instant heater setpoint changes & setpoint increment)
* Can use seperate threads for network & RS485 interface. (optimisation for busy RS485 bus implimentations)

61
ToDo.md Normal file
View File

@ -0,0 +1,61 @@
MUST DO BEFORE RELEASE
done - Check serial blocking mode for startup on getting probes.
done - Serialport timeouts / recovery in blockingmode
done - Serialport timeouts / recovery in non blocking mode.
done - Config "read_extra_devices" "read_pentair_packets" change to read_swg / read_pentair_pump / read_jandy_pump
done - Post MQTT status messages. (clean up duplicates and non terminated strings)
done - tcdrain in blocking mode.
done - setpoint+1
done - doesn't look like protocol supports it. RS16 LED States in serialadapter (on/off, check flash & enable)
done - Link Spa Mode & Spa Heater in web UI ?
done - Programming mode, getting message before it expects message. https://github.com/sfeakes/AqualinkD/issues/111
done (CHECK) - SWG enable/on mismatch when in Boost (restarting aqualinkd)
done - Added Boost to homekit
done - Timers.
done - crashed when passing -c but not file
Light mode from this post https://aqualinkd.freeforums.net/thread/109/lights-turning-on-lightmode?page=1&scrollTo=611
Spa reports 0 (not -17.78) on startup.
Set time (add ~5sec to the functions)
Fix Color lights turn on one press if aqualinkd controlled (looks like bug in programmer)
Fix PRESTATE_ONOFF with domoticz
Fix SWG state messages unknonw 0x0b 0x03 (and others)
Timer when Delay ()
Logging for ePump
simulate panel doesn;t turn off simulate_panel
Really remove simulate control panel
SWG "General fault" in panel, different message from SWG "clean cell"
Do I want to use RSSA to test service / timeout modes?
1/2 done - Colored light (dam hit enter), test if better over RS Serial Adapter. (also hit enter on allbutton)
change all send_ack/message to bool types, and no pop off send queue until actually sent correctly. (for readaheadb4write)
Should I change DEBUG_SERIAL to RSSD_LOG ? (in config debug logging would be all masks except RSSD_LOG.)
Startup programmers from A interface starting before B interface. (look into further)
Pump update fails "can't find pump" in iAq when pumps are on during aqualinkd startup. This may be when iAq starts before rsallbutton
Update Wiki PDA section. (can't support it)
core dump on web port blocked in threadded mode
also check MQTT connection error
NICE
serial_logger add options to connect to panel, reply to ID allbutton ID messages.
OTHER
Update homekit to new API
Look at using specific MQTT topics to ocercome multiple requests messages when changing setpoints?
get_aux_information Loops too much over special devices
add ignore_swg_messags from (Allbutton, iAqua, onetou, RSbuss), probably ignore_pent_pump from above as well.

View File

@ -15,12 +15,17 @@
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "config.h"
#include "domoticz.h"
#include "aq_panel.h"
#include "serialadapter.h"
#include "aq_timer.h"
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)
{
@ -482,9 +487,285 @@ void initPanelButtons(struct aqualinkdata *aqdata, bool rs, int size, bool combo
//void create_PDA_on_off_request(aqkey *button, bool isON);
//bool create_panel_request(struct aqualinkdata *aqdata, netRequest requester, int buttonIndex, int value, bool timer);
//void create_program_request(struct aqualinkdata *aqdata, netRequest requester, action_type type, int value, int id); // id is only valid for PUMP RPM
//bool setDeviceState(aqkey *button, bool isON)
bool setDeviceState(struct aqualinkdata *aqdata, int deviceIndex, bool isON)
{
aqkey *button = &aqdata->aqbuttons[deviceIndex];
if ((button->led->state == OFF && isON == false) ||
(isON > 0 && (button->led->state == ON || button->led->state == FLASH ||
button->led->state == ENABLE))) {
LOG(AQUA_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(AQUA_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) {
char msg[PTHREAD_ARG];
sprintf(msg, "%-5d%-5d", deviceIndex, (isON == false ? OFF : ON));
aq_programmer(AQ_PDA_DEVICE_ON_OFF, msg, aqdata);
} 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 ((button->special_mask & PROGRAM_LIGHT) == PROGRAM_LIGHT && button->led->state == OFF) {
// 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.
if (isRSSA_ENABLED) {
set_aqualink_rssadapter_aux_state(deviceIndex, true);
} else {
//set_light_mode("0", deviceIndex); // 0 means use current light mode
programDeviceLightMode(aqdata, 0, deviceIndex); // 0 means use current light mode
}
} else {
aq_send_cmd(button->code);
}
// 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 ((button->code == KEY_POOL_HTR || button->code == KEY_SPA_HTR ||
button->code == KEY_SOLAR_HTR) &&
isON > 0) {
button->led->state = ENABLE; // if heater and set to on, set pre-status to enable.
//_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
//_aqualink_data->updated = true;
}
#endif
}
}
return TRUE;
}
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)
LOG(AQUA_LOG,LOG_ERR, "about to overwrite unactioned panel program\n");
if (type == POOL_HTR_SETOINT || type == SPA_HTR_SETOINT || type == FREEZE_SETPOINT || type == SWG_SETPOINT ) {
aqdata->unactioned.value = setpoint_check(type, value, aqdata);
if (value != aqdata->unactioned.value)
LOG(AQUA_LOG,LOG_NOTICE, "requested setpoint value %d is invalid, change to %d\n", value, aqdata->unactioned.value);
} else if (type == PUMP_RPM) {
aqdata->unactioned.value = value;
} else if (type == PUMP_VSPROGRAM) {
LOG(AQUA_LOG,LOG_ERR, "requested Pump vsp program is not implimented yet\n", value, aqdata->unactioned.value);
} else {
// SWG_BOOST & PUMP_RPM & SETPOINT incrment
aqdata->unactioned.value = value;
}
aqdata->unactioned.type = type;
aqdata->unactioned.id = id; // This is only valid for pump.
// Should probably limit this to setpoint and no aq_serial protocol.
if (expectMultiple) // We can get multiple MQTT requests from some, so this will wait for last one to come in.
time(&aqdata->unactioned.requested);
else
aqdata->unactioned.requested = 0;
return true;
}
//void programDeviceLightMode(struct aqualinkdata *aqdata, char *value, int button)
void programDeviceLightMode(struct aqualinkdata *aqdata, int value, int button)
{
int i;
clight_detail *light = NULL;
#ifdef AQ_PDA
if (isPDA_PANEL) {
LOG(AQUA_LOG,LOG_ERR, "Light mode control not supported in PDA mode\n");
return;
}
#endif
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;
}
}
if (light == NULL) {
LOG(AQUA_LOG,LOG_ERR, "Light mode control not configured for button %d\n",button);
return;
}
char buf[LIGHT_MODE_BUFER];
if (light->lightType == LC_PROGRAMABLE ) {
//sprintf(buf, "%-5s%-5d%-5d%-5d%.2f",value,
sprintf(buf, "%-5d%-5d%-5d%-5d%.2f",value,
button,
_aqconfig_.light_programming_initial_on,
_aqconfig_.light_programming_initial_off,
_aqconfig_.light_programming_mode );
aq_programmer(AQ_SET_LIGHTPROGRAM_MODE, buf, aqdata);
} else {
//sprintf(buf, "%-5s%-5d%-5d",value, button, light->lightType);
sprintf(buf, "%-5d%-5d%-5d",value, button, light->lightType);
aq_programmer(AQ_SET_LIGHTCOLOR_MODE, buf, aqdata);
}
}
/*
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
subIndex = index of pump if required
source = This will delay request to allow for multiple messages and only execute the last. (ie this stops multiple programming mode threads when setpoint changes are stepped)
*/
//bool panel_device_request(struct aqualinkdata *aqdata, action_type type, int deviceIndex, int value, int subIndex, bool fromMQTT)
bool panel_device_request(struct aqualinkdata *aqdata, action_type type, int deviceIndex, int value, request_source source)
{
switch (type) {
case ON_OFF:
//setDeviceState(&aqdata->aqbuttons[deviceIndex], value<=0?false:true, deviceIndex );
setDeviceState(aqdata, deviceIndex, value<=0?false:true );
break;
case TIMER:
//setDeviceState(&aqdata->aqbuttons[deviceIndex], true);
setDeviceState(aqdata, deviceIndex, true);
start_timer(aqdata, &aqdata->aqbuttons[deviceIndex], value);
break;
case LIGHT_MODE:
programDeviceLightMode(aqdata, value, deviceIndex);
break;
case POOL_HTR_SETOINT:
case SPA_HTR_SETOINT:
case FREEZE_SETPOINT:
case SWG_SETPOINT:
case SWG_BOOST:
case PUMP_RPM:
case PUMP_VSPROGRAM:
case POOL_HTR_INCREMENT:
case SPA_HTR_INCREMENT:
programDeviceValue(aqdata, type, value, deviceIndex, (source==NET_MQTT?true:false) );
break;
case DATE_TIME:
aq_programmer(AQ_SET_TIME, NULL, aqdata);
break;
default:
LOG(AQUA_LOG,LOG_ERR, "Unknown device request type %d for deviceindex %d\n",type,deviceIndex);
break;
}
return TRUE;
}
/*
bool create_panel_request(struct aqualinkdata *aqdata, netRequest requester, int buttonIndex, int value, bool timer) {
// if value = 0 is OFF,
// if value = 1 is ON if (timer = false).
// if value > 0 (timer should be true, and vaue is duration).
if ((_aqualink_data->aqbuttons[buttonIndex].led->state == OFF && value == 0) ||
(value > 0 && (_aqualink_data->aqbuttons[buttonIndex].led->state == ON || _aqualink_data->aqbuttons[buttonIndex].led->state == FLASH ||
_aqualink_data->aqbuttons[buttonIndex].led->state == ENABLE))) {
LOG(NET_LOG, LOG_INFO, "%s: received '%s' for '%s', already '%s', Ignoring\n", actionName[requester], (value == 0 ? "OFF" : "ON"), _aqualink_data->aqbuttons[buttonIndex].name, (value == 0 ? "OFF" : "ON"));
//return false;
} else {
LOG(NET_LOG, LOG_INFO, "%s: received '%s' for '%s', turning '%s'\n", actionName[requester], (value == 0 ? "OFF" : "ON"), _aqualink_data->aqbuttons[buttonIndex].name, (value == 0 ? "OFF" : "ON"));
#ifdef AQ_PDA
if (isPDA_PANEL) {
char msg[PTHREAD_ARG];
sprintf(msg, "%-5d%-5d", buttonIndex, (value == 0 ? OFF : ON));
aq_programmer(AQ_PDA_DEVICE_ON_OFF, msg, _aqualink_data);
} 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 ((_aqualink_data->aqbuttons[buttonIndex].special_mask & PROGRAM_LIGHT) == PROGRAM_LIGHT && _aqualink_data->aqbuttons[buttonIndex].led->state == OFF) {
// 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.
if (isRSSA_ENABLED) {
set_aqualink_rssadapter_aux_state(buttonIndex, true);
} else {
set_light_mode("0", buttonIndex); // 0 means use current light mode
}
} else {
aq_send_cmd(_aqualink_data->aqbuttons[buttonIndex].code);
}
// 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 ((_aqualink_data->aqbuttons[buttonIndex].code == KEY_POOL_HTR || _aqualink_data->aqbuttons[buttonIndex].code == KEY_SPA_HTR ||
_aqualink_data->aqbuttons[buttonIndex].code == KEY_SOLAR_HTR) &&
value > 0) {
_aqualink_data->aqbuttons[buttonIndex].led->state = ENABLE; // if heater and set to on, set pre-status to enable.
//_aqualink_data->updated = true;
} else if (isRSSA_ENABLED || ((_aqualink_data->aqbuttons[buttonIndex].special_mask & PROGRAM_LIGHT) != PROGRAM_LIGHT)) {
_aqualink_data->aqbuttons[buttonIndex].led->state = (value == 0 ? OFF : ON); // as long as it's not programmable light , pre-set to on/off
//_aqualink_data->updated = true;
}
#endif
}
}
// If it's a timer, start the timer
if (timer) {
start_timer(_aqualink_data, &_aqualink_data->aqbuttons[buttonIndex], value);
}
return true;
}
void create_program_request(netRequest requester, action_type type, int value, int id) // id is only valid for PUMP RPM
{
if (_aqualink_data->unactioned.type != NO_ACTION && type != _aqualink_data->unactioned.type)
LOG(NET_LOG,LOG_ERR, "%s: About to overwrite unactioned panel program\n",actionName[requester]);
if (type == POOL_HTR_SETOINT || type == SPA_HTR_SETOINT || type == FREEZE_SETPOINT || type == SWG_SETPOINT ) {
_aqualink_data->unactioned.value = setpoint_check(type, value, _aqualink_data);
if (value != _aqualink_data->unactioned.value)
LOG(NET_LOG,LOG_NOTICE, "%s: requested setpoint value %d is invalid, change to %d\n",actionName[requester], value, _aqualink_data->unactioned.value);
} else if (type == PUMP_RPM) {
//_aqualink_data->unactioned.value = RPM_check(_aqualink_data->pumps[id].pumpType , value, _aqualink_data);
_aqualink_data->unactioned.value = value;
//if (value != _aqualink_data->unactioned.value)
// LOG(NET_LOG,LOG_NOTICE, "%s: requested Pump value %d is invalid, change to %d\n",actionName[requester], value, _aqualink_data->unactioned.value);
} else if (type == PUMP_VSPROGRAM) {
//_aqualink_data->unactioned.value = value;
//if (value != _aqualink_data->unactioned.value)
LOG(NET_LOG,LOG_ERR, "%s: requested Pump vsp program is not implimented yet\n",actionName[requester], value, _aqualink_data->unactioned.value);
} else {
// SWG_BOOST & PUMP_RPM
_aqualink_data->unactioned.value = value;
}
_aqualink_data->unactioned.type = type;
_aqualink_data->unactioned.id = id; // This is only valid for pump.
if (requester == NET_MQTT) // We can get multiple MQTT requests from some, so this will wait for last one to come in.
time(&_aqualink_data->unactioned.requested);
else
_aqualink_data->unactioned.requested = 0;
}
#ifdef AQ_PDA
void create_PDA_on_off_request(aqkey *button, bool isON)
{
int i;
char msg[PTHREAD_ARG];
for (i=0; i < _aqualink_data->total_buttons; i++) {
if (_aqualink_data->aqbuttons[i].code == button->code) {
sprintf(msg, "%-5d%-5d", i, (isON? ON : OFF));
aq_programmer(AQ_PDA_DEVICE_ON_OFF, msg, _aqualink_data);
break;
}
}
}
#endif
*/

View File

@ -1,8 +1,9 @@
#ifndef INIT_BUTTONS_H_
#define INIT_BUTTONS_H_
#ifndef AQ_PANEL_H_
#define AQ_PANEL_H_
#include "config.h"
#include "aqualink.h"
#define PUMP_INDEX 0
#define SPA_INDEX 1
@ -33,6 +34,8 @@
void setPanelByName(struct aqualinkdata *aqdata, const char *str);
void setPanel(struct aqualinkdata *aqdata, bool rs, int size, bool combo, bool dual);
bool panel_device_request(struct aqualinkdata *aqdata, action_type type, int deviceIndex, int value, request_source source);
void changePanelToMode_Only();
void addPanelOneTouchInterface();
void addPanelIAQTouchInterface();

BIN
aq_panel.o Normal file

Binary file not shown.

1187
aq_panel.x.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -64,9 +64,6 @@ void *get_aqualink_aux_labels( void *ptr );
//void *threadded_send_cmd( void *ptr );
void *set_aqualink_light_programmode( void *ptr );
void *set_aqualink_light_colormode( void *ptr );
#ifdef AQ_PDA
void *set_aqualink_PDA_init( void *ptr );
#endif
void *set_aqualink_SWG( void *ptr );
void *set_aqualink_boost( void *ptr );
/*
@ -138,9 +135,26 @@ int get_aq_cmd_length()
return _stack_place;
}
int _expectNextMessage = 0;
unsigned char _last_sent_command = NUL;
unsigned char pop_aq_cmd(struct aqualinkdata *aq_data)
{
unsigned char cmd = NUL;
if ( _expectNextMessage > 0 &&
(aq_data->last_packet_type == CMD_MSG || aq_data->last_packet_type == CMD_MSG_LONG || aq_data->last_packet_type == CMD_MSG_LOOP_ST))
{
_expectNextMessage=0;
} else if (_expectNextMessage > 3) {
// NSF Should probably check this is a status command AND we are in programming mode.
LOG(PROG_LOG, LOG_ERR, "Did not receive expected reply from last RS SEND command, resending '0x%02hhx'\n", _last_sent_command);
_expectNextMessage=0;
return _last_sent_command;
} else if (_expectNextMessage > 0) {
_expectNextMessage++;
}
// Only send commands on status messages
// Are we in programming mode and it's not ONETOUCH programming mode
if (in_programming_mode(aq_data) && ( in_ot_programming_mode(aq_data) == false && in_iaqt_programming_mode(aq_data) == false )) {
@ -165,6 +179,11 @@ unsigned char pop_aq_cmd(struct aqualinkdata *aq_data)
//printf("RSM sending cmd '0x%02hhx' in reply to '0x%02hhx'\n",cmd,aq_data->last_packet_type);
if (cmd == KEY_ENTER || cmd == KEY_RIGHT || cmd == KEY_LEFT || cmd == KEY_MENU ) { //KEY_CANCEL KEY_HOLD KEY_OVERRIDE
_expectNextMessage=1;
_last_sent_command = cmd;
}
return cmd;
}
@ -364,6 +383,18 @@ void kick_aq_program_thread(struct aqualinkdata *aq_data)
}
}
*/
bool in_light_programming_mode(struct aqualinkdata *aq_data)
{
if ( ( aq_data->active_thread.thread_id != 0 ) &&
( aq_data->active_thread.ptype == AQ_SET_LIGHTPROGRAM_MODE ||
aq_data->active_thread.ptype == AQ_SET_LIGHTCOLOR_MODE)
) {
return true;
}
return false;
}
bool in_swg_programming_mode(struct aqualinkdata *aq_data)
{
if ( ( aq_data->active_thread.thread_id != 0 ) &&
@ -518,6 +549,9 @@ void aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_data
program_type type = r_type;
// RS SerialAdapter is quickest for changing thermostat temps, so use that if enabeled.
// VSP RPM can only be changed with oneTouch or iAquatouch so check / use those
// VSP Program is only available with iAquatouch, so check / use that.
if (isRSSA_ENABLED && (r_type == AQ_SET_POOL_HEATER_TEMP || r_type == AQ_SET_SPA_HEATER_TEMP)) {
if (r_type == AQ_SET_POOL_HEATER_TEMP)
type = AQ_SET_RSSADAPTER_POOL_HEATER_TEMP;
@ -561,6 +595,10 @@ void aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_data
case AQ_SET_BOOST:
type = AQ_SET_ONETOUCH_BOOST;
break;
// NSF ONE TOUCH TIME IS NOT WORKING YET
//case AQ_SET_TIME:
// type = AQ_SET_ONETOUCH_TIME;
//break;
default:
type = r_type;
break;
@ -612,13 +650,16 @@ void aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_data
#endif
type != AQ_GET_POOL_SPA_HEATER_TEMPS &&
type != AQ_SET_FRZ_PROTECTION_TEMP &&
type != AQ_SET_BOOST) {
type != AQ_SET_BOOST &&
type != AQ_SET_TIME) {
LOG(PROG_LOG, LOG_ERR, "Selected Programming mode '%d' not supported with PDA mode control panel\n",type);
return;
}
}
#endif
LOG(PROG_LOG, LOG_INFO, "Starting programming thread '%s'\n",ptypeName(type));
programmingthread->aq_data = aq_data;
programmingthread->thread_id = 0;
//programmingthread->thread_args = args;
@ -952,6 +993,7 @@ void waitForSingleThreadOrTerminate(struct programmingThreadCtrl *threadCtrl, pr
void cleanAndTerminateThread(struct programmingThreadCtrl *threadCtrl)
{
waitfor_queue2empty();
#ifndef AQ_DEBUG
LOG(PROG_LOG, LOG_DEBUG, "Thread %d,%p (%s) finished\n",threadCtrl->aq_data->active_thread.ptype, threadCtrl->thread_id,ptypeName(threadCtrl->aq_data->active_thread.ptype));
#else
@ -1454,7 +1496,7 @@ void *set_aqualink_light_programmode( void *ptr )
return ptr;
}
int seconds = 1000;
const int seconds = 1000;
// Needs to start programming sequence with light on, if off we need to turn on for 15 seconds
// before we can send the next off.
if ( button->led->state != ON ) {
@ -1476,20 +1518,24 @@ void *set_aqualink_light_programmode( void *ptr )
for (i = 1; i < (val * 2); i++) {
LOG(PROG_LOG, LOG_INFO, "Light Programming button press number %d - %s of %d\n", i, i % 2 == 0 ? "Off" : "On", val);
send_cmd(code);
waitfor_queue2empty();
delay(pmode * seconds); // 0.3 works, but using 0.4 to be safe
}
} else {
for (i = 1; i < val; i++) {
const int dt = 0.5; // Time to wait after receiving conformation of light on/off
LOG(PROG_LOG, LOG_INFO, "Light Programming button press number %d - %s of %d\n", i, "ON", val);
send_cmd(code);
waitForButtonState(aq_data, button, ON, 2);
delay(dt * seconds);
LOG(PROG_LOG, LOG_INFO, "Light Programming button press number %d - %s of %d\n", i, "OFF", val);
send_cmd(code);
waitForButtonState(aq_data, button, OFF, 2);
delay(dt * seconds);
}
LOG(PROG_LOG, LOG_INFO, "Finished - Light Programming button press number %d - %s of %d\n", i, "ON", val);
send_cmd(code);
waitfor_queue2empty();
}
//waitForButtonState(aq_data, &aq_data->aqbuttons[btn], ON, 2);
@ -1702,7 +1748,7 @@ void *set_aqualink_freeze_heater_temps( void *ptr )
LOG(PROG_LOG, LOG_ERR, "%s failed\n", ptypeName( aq_data->active_thread.ptype ) );
cancel_menu();
cleanAndTerminateThread(threadCtrl);
return ptr;
return ptr;
}
setAqualinkNumericField(aq_data, "FRZ", val);
@ -1723,9 +1769,23 @@ void *set_aqualink_time( void *ptr )
waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_TIME);
//LOG(PROG_LOG, LOG_NOTICE, "Setting time on aqualink\n");
#ifdef AQ_PDA
if (isPDA_PANEL) {
set_PDA_aqualink_time(aq_data);
cleanAndTerminateThread(threadCtrl);
return ptr;
}
#endif
time_t now = time(0); // get time now
struct tm *result = localtime(&now);
char hour[20];
// Add 10 seconds to time since this can take a while to program.
// 10 to 20 seconds whould be right, but since there are no seconds we can set, add 30 seconds to get close to minute.
// Should probably set this to program the next minute then wait before hitting the final enter command.
result->tm_sec += 30;
mktime(result);
if (result->tm_hour == 0)
sprintf(hour, "HOUR 12 AM");
@ -1962,9 +2022,12 @@ void _waitfor_queue2empty(bool longwait)
{
int i=0;
LOG(PROG_LOG, LOG_DEBUG, "Waiting for queue to empty\n");
while ( (_pgm_command != NUL) && ( i++ < (PROGRAMMING_POLL_COUNTER*(longwait?2:1) ) ) ) {
delay(PROGRAMMING_POLL_DELAY_TIME);
}
/*
//while ( (_pgm_command != NUL) && ( i++ < (30*(longwait?2:1) ) ) ) {
while ( (_pgm_command != NUL) && ( i++ < (50*(longwait?2:1) ) ) ) {
@ -1983,6 +2046,8 @@ void _waitfor_queue2empty(bool longwait)
}
#endif
LOG(PROG_LOG, LOG_WARNING, "Send command Queue did not empty, timeout\n");
} else {
LOG(PROG_LOG, LOG_DEBUG, "Queue now empty!\n");
}
}
@ -2103,7 +2168,8 @@ bool waitForEitherMessage(struct aqualinkdata *aq_data, char* message1, char* me
bool waitForMessage(struct aqualinkdata *aq_data, char* message, int numMessageReceived)
{
LOG(PROG_LOG, LOG_DEBUG, "waitForMessage %s %d\n",message,numMessageReceived);
waitfor_queue2empty(); // MAke sure the last command was sent
// NSF Need to come back to this, as it stops on test enviornment but not real panel, so must be speed related.
//waitfor_queue2empty(); // MAke sure the last command was sent
int i=0;
pthread_mutex_lock(&aq_data->active_thread.thread_mutex);
@ -2215,6 +2281,7 @@ bool select_sub_menu_item(struct aqualinkdata *aq_data, char* item_string)
{
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);
}

View File

@ -5,7 +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_COUNTER 200
// need to get the C values from aqualink manual and add those just incase
@ -107,6 +109,7 @@ bool in_programming_mode(struct aqualinkdata *aq_data);
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);
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);

BIN
aq_programmer.o Normal file

Binary file not shown.

257
aq_scheduler.c Normal file
View File

@ -0,0 +1,257 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/mount.h>
#include <sys/statvfs.h>
#include <regex.h>
#include "mongoose.h"
#include "aqualink.h"
#include "aq_scheduler.h"
#include "config.h"
//#include "utils.h"
/*
Example /etc/cron.d/aqualinkd
01 10 1 * * curl localhost:80/api/Filter_Pump/set -d value=2 -X PUT
*/
bool remount_root_ro(bool readonly) {
// NSF Check if config is RO_ROOT set
if (readonly) {
LOG(SCHD_LOG,LOG_INFO, "reMounting root RO\n");
mount (NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
return true;
} else {
struct statvfs fsinfo;
statvfs("/", &fsinfo);
if ((fsinfo.f_flag & ST_RDONLY) == 0) // We are readwrite, ignore
return false;
LOG(SCHD_LOG,LOG_INFO, "reMounting root RW\n");
mount (NULL, "/", NULL, MS_REMOUNT, NULL);
return true;
}
}
bool passJson_scObj(char* line, int length, aqs_cron *values)
{
int keystart=0;
int keyend=0;
int valuestart=0;
int captured=0;
bool readingvalue=false;
bool invalue=false;
//char value;
LOG(SCHD_LOG,LOG_DEBUG, "Obj body:'%.*s'\n", length, line);
for (int i=0; i < length; i++) {
if (line[i] == '}') {
return (captured >= 7)?true:false;
} else if (line[i] == '"' && keystart==0 && invalue==false && readingvalue==false) {
keystart=i+1;
} else if (line[i] == '"' && keystart > 0 && invalue==false && readingvalue==false) {
keyend=i;
} else if (line[i] == ':' && keystart > 0 ) {
invalue=true;
} else if (line[i] == '"' && invalue == true && readingvalue == false && keystart > 0 ) {
readingvalue=true;
valuestart=i+1;
} else if (line[i] == '"' && readingvalue == true) {
// i is end of key
if ( strncmp(&line[keystart], "min", 3) == 0) {
strncpy(values->minute, &line[valuestart], (i-valuestart) );
values->minute[i-valuestart] = '\0';
captured++;
} else if( strncmp(&line[keystart], "hour", 4) == 0) {
strncpy(values->hour, &line[valuestart], (i-valuestart) );
values->hour[i-valuestart] = '\0';
captured++;
} else if( strncmp(&line[keystart], "daym", 4) == 0) {
strncpy(values->daym, &line[valuestart], (i-valuestart) );
values->daym[i-valuestart] = '\0';
captured++;
} else if( strncmp(&line[keystart], "month", 5) == 0) {
strncpy(values->month, &line[valuestart], (i-valuestart) );
values->month[i-valuestart] = '\0';
captured++;
} else if( strncmp(&line[keystart], "dayw", 4) == 0) {
strncpy(values->dayw, &line[valuestart], (i-valuestart) );
values->dayw[i-valuestart] = '\0';
captured++;
} else if( strncmp(&line[keystart], "url", 3) == 0) {
strncpy(values->url, &line[valuestart], (i-valuestart) );
values->url[i-valuestart] = '\0';
captured++;
} else if( strncmp(&line[keystart], "value", 5) == 0) {
strncpy(values->value, &line[valuestart], (i-valuestart) );
values->value[i-valuestart] = '\0';
captured++;
}
keystart=0;
keyend=0;
valuestart=0;
invalue=false;
readingvalue=false;
}
}
}
int save_schedules_js(char* inBuf, int inSize, char* outBuf, int outSize)
{
int length=0;
FILE *fp;
int i;
bool inarray = false;
aqs_cron cline;
if ( !_aqconfig_.enable_scheduler) {
LOG(SCHD_LOG,LOG_WARNING, "Schedules are disabled\n");
length += sprintf(outBuf, "{\"message\":\"Error Schedules disabled\"}");
return length;
}
LOG(SCHD_LOG,LOG_NOTICE, "Saving Schedule:\n");
bool fs = remount_root_ro(false);
fp = fopen(CRON_FILE, "w");
if (fp == NULL) {
LOG(SCHD_LOG,LOG_ERR, "Open file failed '%s'\n", CRON_FILE);
remount_root_ro(true);
length += sprintf(outBuf, "{\"message\":\"Error Saving Schedules\"}");
return length;
}
fprintf(fp, "#***** AUTO GENERATED DO NOT EDIT *****\n");
fprintf(fp, "PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin\n");
LOG(SCHD_LOG,LOG_DEBUG, "Schedules Message body:\n'%.*s'\n", inSize, inBuf);
length += sprintf(outBuf, "{\"message\":\"Saved Schedules\"}");
for (i=0; i < inSize; i++) {
if ( inBuf[i] == '[' ) {
inarray=true;
} else if ( inBuf[i] == ']' ) {
inarray=false;
} else if ( inarray && inBuf[i] == '{') {
passJson_scObj( &inBuf[i], (inSize-i), &cline);
//LOG(SCHD_LOG,LOG_NOTICE,"Saving Cron %s %s %s %s %s %s %s\n",cline.minute, cline.hour, cline.daym, cline.month, cline.dayw, cline.url, cline.value);
LOG(SCHD_LOG,LOG_INFO, "Write to cron Min=%s Hour=%s DayM=%s Month=%s DayW %s URL %s Value %s\n",cline.minute,cline.hour,cline.daym,cline.month,cline.dayw,cline.url,cline.value);
LOG(SCHD_LOG,LOG_INFO, "%s %s %s %s %s curl localhost:%s%s -d value=%s -X PUT\n",cline.minute, cline.hour, cline.daym, cline.month, cline.dayw, _aqconfig_.socket_port, cline.url, cline.value);
fprintf(fp, "%s %s %s %s %s root curl localhost:%s%s -d value=%s -X PUT\n",cline.minute, cline.hour, cline.daym, cline.month, cline.dayw, _aqconfig_.socket_port, cline.url, cline.value);
} else if ( inarray && inBuf[i] == '}') {
//inobj=false;
//objed=i;
}
}
fprintf(fp, "#***** AUTO GENERATED DO NOT EDIT *****\n");
fclose(fp);
remount_root_ro(fs);
return length;
}
int build_schedules_js(char* buffer, int size)
{
memset(&buffer[0], 0, size);
FILE *fp;
char *line = NULL;
int length = 0;
int rc;
aqs_cron cline;
size_t len = 0;
ssize_t read_size;
regex_t regexCompiled;
if ( !_aqconfig_.enable_scheduler) {
LOG(SCHD_LOG,LOG_WARNING, "Schedules are disabled\n");
length += sprintf(buffer, "{\"message\":\"Error Schedules disabled\"}");
return length;
}
// Below works for curl but not /usr/bin/curl in command. NSF come back and fix the regexp
//char *regexString="([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s.*(/api/.*)\\s-d value=([^\\d]+)\\s(.*)";
// \d doesn't seem to be supported, so using [0-9]+ instead
//char *regexString="([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s.*(\\/api\\/.*\\/set).* value=([0-9]+).*";
char *regexString="([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s.*(\\/api\\/.*\\/set).* value=([0-9]+).*";
//char *regexString="([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s.*(/api/.*/set).*value=([0-9]+).*";
size_t maxGroups = 15;
regmatch_t groupArray[maxGroups];
//static char buf[100];
length += sprintf(buffer+length,"{\"type\": \"schedules\",");
if (0 != (rc = regcomp(&regexCompiled, regexString, REG_EXTENDED))) {
LOG(SCHD_LOG,LOG_ERR, "regcomp() failed, returning nonzero (%d)\n", rc);
length += sprintf(buffer+length,"\"message\": \"Error reading schedules\"}");
return length;
}
fp = fopen(CRON_FILE, "r");
if (fp == NULL) {
LOG(SCHD_LOG,LOG_ERR, "Open file failed '%s'\n", CRON_FILE);
length += sprintf(buffer+length,"\"message\": \"Error reading schedules\"}");
return length;
}
length += sprintf(buffer+length,"\"schedules\": [ ");
while ((read_size = getline(&line, &len, fp)) != -1) {
//printf("Read from cron:-\n %s", line);
//lc++;
//rc = regexec(&regexCompiled, line, maxGroups, groupArray, 0);
if (0 == (rc = regexec(&regexCompiled, line, maxGroups, groupArray, REG_EXTENDED))) {
// Group 1 is minute
// Group 2 is hour
// Group 3 is day of month
// Group 4 is month
// Group 5 is day of week
// Group 6 is URL
// Group 7 is value
if (groupArray[8].rm_so == (size_t)-1) {
LOG(SCHD_LOG,LOG_ERR, "No matching information from cron file\n");
} else {
sprintf(cline.minute, "%.*s", (groupArray[1].rm_eo - groupArray[1].rm_so), (line + groupArray[1].rm_so));
sprintf(cline.hour, "%.*s", (groupArray[2].rm_eo - groupArray[2].rm_so), (line + groupArray[2].rm_so));
sprintf(cline.daym, "%.*s", (groupArray[3].rm_eo - groupArray[3].rm_so), (line + groupArray[3].rm_so));
sprintf(cline.month, "%.*s", (groupArray[4].rm_eo - groupArray[4].rm_so), (line + groupArray[4].rm_so));
sprintf(cline.dayw, "%.*s", (groupArray[5].rm_eo - groupArray[5].rm_so), (line + groupArray[5].rm_so));
sprintf(cline.url, "%.*s", (groupArray[8].rm_eo - groupArray[8].rm_so), (line + groupArray[8].rm_so));
sprintf(cline.value, "%.*s", (groupArray[9].rm_eo - groupArray[9].rm_so), (line + groupArray[9].rm_so));
LOG(SCHD_LOG,LOG_INFO, "Read from cron. Min=%s Hour=%s DayM=%s Month=%s DayW=%s URL=%s Value=%s\n",cline.minute,cline.hour,cline.daym,cline.month,cline.dayw,cline.url,cline.value);
length += sprintf(buffer+length, "{\"min\": \"%s\",\"hour\": \"%s\",\"daym\": \"%s\",\"month\": \"%s\",\"dayw\": \"%s\",\"url\": \"%s\",\"value\": \"%s\"},",
cline.minute,
cline.hour,
cline.daym,
cline.month,
cline.dayw,
cline.url,
cline.value);
//LOG(SCHD_LOG,LOG_DEBUG, "Read from cron Day %d | Time %d:%d | Zone %d | Runtime %d\n",day,hour,minute,zone,runtime);
}
} else {
LOG(SCHD_LOG,LOG_DEBUG, "regexp no match (%d) %s\n", rc, line);
}
}
buffer[--length] = '\0';
length += sprintf(buffer+length,"]}\n");
fclose(fp);
regfree(&regexCompiled);
return length;
}

30
aq_scheduler.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef AQ_SCHEDULER_H_
#define AQ_SCHEDULER_H_
#define CRON_FILE "/etc/cron.d/aqualinkd"
#define CURL "curl"
#define CV_SIZE 20
typedef struct aqs_cron
{
char minute[CV_SIZE];
char hour[CV_SIZE];
char daym[CV_SIZE];
char month[CV_SIZE];
char dayw[CV_SIZE];
char url[CV_SIZE * 2];
char value[CV_SIZE];
} aqs_cron;
int build_schedules_js(char* buffer, int size);
int save_schedules_js(char* inBuf, int inSize, char* outBuf, int outSize);
//void read_schedules();
//void write_schedules();
#endif // AQ_SCHEDULER_H_

BIN
aq_scheduler.o Normal file

Binary file not shown.

View File

@ -163,7 +163,7 @@ bool check_jandy_checksum(unsigned char* packet, int length)
LOG(RSSD_LOG,LOG_INFO, "Ignoring bad checksum, seems to be bug in Jandy protocol\n");
if (getLogLevel(RSSD_LOG) >= LOG_DEBUG) {
static char buf[1000];
beautifyPacket(buf,packet,length);
beautifyPacket(buf,packet,length,true);
LOG(RSSD_LOG,LOG_DEBUG, "Packetin question %s\n",buf);
}
return true;
@ -646,11 +646,15 @@ void send_packet(int fd, unsigned char *packet, int length)
}
#endif
*/
if ( getLogLevel(RSSD_LOG) >= LOG_DEBUG_SERIAL) {
LOG(RSSD_LOG,LOG_DEBUG_SERIAL, "Serial write %d bytes\n",length-2);
logPacketWrite(&packet[1], length-2);
/*
if ( getLogLevel(RSSD_LOG) >= LOG_DEBUG_SERIAL || ) {
// Packet is padded with 0x00, so discard for logging
LOG(RSSD_LOG,LOG_DEBUG_SERIAL, "Serial write %d bytes\n",length-2);
logPacket(&packet[1], length-2);
} /*else if (getLogLevel(RSSD_LOG) >= LOG_DEBUG) {
}*/ /*else if (getLogLevel(RSSD_LOG) >= LOG_DEBUG) {
static char buf[1000];
beautifyPacket(buf,&packet[1],length-2);
LOG(RSSD_LOG,LOG_DEBUG, "Serial write %s\n",buf);
@ -695,7 +699,7 @@ void send_extended_ack(int fd, unsigned char ack_type, unsigned char command)
{
_send_ack(fd, ack_type, command);
}
/*
int _get_packet(int fd, unsigned char* packet, bool rawlog);
int get_packet(int fd, unsigned char* packet)
@ -707,6 +711,8 @@ int get_packet_lograw(int fd, unsigned char* packet)
return _get_packet(fd, packet, true);
}
int _get_packet(int fd, unsigned char* packet, bool rawlog)
*/
int get_packet(int fd, unsigned char* packet)
{
unsigned char byte = 0x00;
int bytesRead;
@ -751,7 +757,7 @@ int _get_packet(int fd, unsigned char* packet, bool rawlog)
delay(1);
} else if (bytesRead == 1) {
retry = 0;
if (rawlog)
if (_aqconfig_.log_raw_bytes)
logPacketByte(&byte);
if (lastByteDLE == true && byte == NUL)
@ -904,7 +910,8 @@ int _get_packet(int fd, unsigned char* packet, bool rawlog)
}
LOG(RSSD_LOG,LOG_DEBUG_SERIAL, "Serial read %d bytes\n",index);
logPacket(packet, index);
if (_aqconfig_.log_protocol_packets)
logPacketRead(packet, index);
// Return the packet length.
return index;
}

View File

@ -297,7 +297,7 @@ SPILLOVER IS DISABLED WHILE SPA IS ON
#define SWG_STATUS_LOW_VOLTS 0x20 // low voltage 0x20
#define SWG_STATUS_LOW_TEMP 0x40 // low watertemp 0x40
#define SWG_STATUS_CHECK_PCB 0x80 // check PCB 0x80
// Other SWG codes not deciphered yes 0x03 & 0x0b seem to be messages when salt is low and turning on / off
#define CMD_PDA_0x04 0x04 // No idea, might be building menu
#define CMD_PDA_0x05 0x05 // No idea
@ -457,7 +457,7 @@ void send_ack(int file_descriptor, unsigned char command);
void send_extended_ack(int fd, unsigned char ack_type, unsigned char command);
//void send_cmd(int file_descriptor, unsigned char cmd, unsigned char args);
int get_packet(int file_descriptor, unsigned char* packet);
int get_packet_lograw(int fd, unsigned char* packet);
//int get_packet_lograw(int fd, unsigned char* packet);
//int get_packet_new(int fd, unsigned char* packet);
//int get_packet_new_lograw(int fd, unsigned char* packet);

BIN
aq_serial.o Normal file

Binary file not shown.

159
aq_timer.c Normal file
View File

@ -0,0 +1,159 @@
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include "aqualink.h"
#include "utils.h"
#include "aq_timer.h"
struct timerthread {
pthread_t thread_id;
pthread_mutex_t thread_mutex;
pthread_cond_t thread_cond;
aqkey *button;
struct aqualinkdata *aq_data;
int duration_min;
struct timespec timeout;
struct timerthread *next;
struct timerthread *prev;
};
/*volatile*/ static struct timerthread *_timerthread_ll = NULL;
void *timer_worker( void *ptr );
void start_timer(struct aqualinkdata *aq_data, aqkey *button, int duration)
{
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) {
LOG(TIMR_LOG, LOG_INFO, "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 *tmthread = calloc(1, sizeof(struct timerthread));
tmthread->aq_data = aq_data;
tmthread->button = button;
tmthread->thread_id = 0;
tmthread->duration_min = duration;
tmthread->next = NULL;
if( pthread_create( &tmthread->thread_id , NULL , timer_worker, (void*)tmthread) < 0) {
LOG(TIMR_LOG, LOG_ERR, "could not create timer thread for button '%s'\n",button->name);
free(tmthread);
return;
}
if (_timerthread_ll == NULL) {
_timerthread_ll = tmthread;
_timerthread_ll->prev = NULL;
//LOG(TIMR_LOG, LOG_NOTICE, "Added Timer '%s' at beginning LL\n",_timerthread_ll->button->name);
}
else
{
for (t_ptr = _timerthread_ll; t_ptr->next != NULL; t_ptr = t_ptr->next) {} // Simply run to the end of the list
t_ptr->next = tmthread;
tmthread->prev = t_ptr;
//LOG(TIMR_LOG, LOG_NOTICE, "Added Timer '%s' at end LL \n",tmthread->button->name);
}
if ( tmthread->thread_id != 0 ) {
pthread_detach(tmthread->thread_id);
}
}
#define WAIT_TIME_BEFORE_ON_CHECK 1000 // 1 second
void *timer_worker( void *ptr )
{
struct timerthread *tmthread;
tmthread = (struct timerthread *) ptr;
int retval = 0;
LOG(TIMR_LOG, LOG_NOTICE, "Started for button '%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);
#endif
// device should be on, but check, ignore for PDA as that may not have been turned on yet
if (!isPDA_PANEL && tmthread->button->led->state == OFF) {
if ((tmthread->button->special_mask & PROGRAM_LIGHT) == PROGRAM_LIGHT && in_light_programming_mode(tmthread->aq_data)) {
LOG(TIMR_LOG, LOG_NOTICE, "Not turning on '%s' as programmer is\n",tmthread->button->name);
} else {
// crap way to do this, need to use net_service logic in teh future, but should never actually get here
LOG(TIMR_LOG, LOG_NOTICE, "turning on '%s'\n",tmthread->button->name);
aq_send_cmd(tmthread->button->code);
}
}
pthread_mutex_lock(&tmthread->thread_mutex);
do {
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;
}
clock_gettime(CLOCK_REALTIME, &tmthread->timeout);
tmthread->timeout.tv_sec += (tmthread->duration_min * 60);
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);
if (tmthread->button->led->state != OFF) {
LOG(TIMR_LOG, LOG_INFO, "waking, turning off '%s'\n",tmthread->button->name);
#ifdef AQ_PDA
if (isPDA_PANEL)
create_PDA_on_off_request(tmthread->button, false);
else
#endif
aq_send_cmd(tmthread->button->code);
} else {
LOG(TIMR_LOG, LOG_INFO, "waking '%s' is already off\n",tmthread->button->name);
}
// remove mask so we know timer is dead
tmthread->button->special_mask &= ~ TIMER_ACTIVE;
if (tmthread->next != NULL && tmthread->prev != NULL){
// Middle of linked list
tmthread->next->prev = tmthread->prev;
tmthread->prev->next = tmthread->next;
//LOG(TIMR_LOG, LOG_NOTICE, "Removed Timer '%s' from middle LL\n",tmthread->button->name);
} else if (tmthread->next == NULL && tmthread->prev != NULL){
// end of linked list
tmthread->prev->next = NULL;
//LOG(TIMR_LOG, LOG_NOTICE, "Removed Timer '%s' from end LL\n",tmthread->button->name);
} else if (tmthread->next != NULL && tmthread->prev == NULL){
// beginning of linked list
_timerthread_ll = tmthread->next;
_timerthread_ll->prev = NULL;
//LOG(TIMR_LOG, LOG_NOTICE, "Removed Timer '%s' from beginning LL\n",tmthread->button->name);
} else if (tmthread->next == NULL && tmthread->prev == NULL){
// only item in list
_timerthread_ll = NULL;
//LOG(TIMR_LOG, LOG_NOTICE, "Removed Timer '%s' last LL\n",tmthread->button->name);
}
free(tmthread);
pthread_exit(0);
return ptr;
}

14
aq_timer.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef AQ_TIMER_H_
#define AQ_TIMER_H_
#include "aqualink.h"
void start_timer(struct aqualinkdata *aq_data, aqkey *button, int duration);
// 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);
#endif
#endif // AQ_TIMER_H_

BIN
aq_timer.o Normal file

Binary file not shown.

3589
aqua.log Normal file

File diff suppressed because it is too large Load Diff

View File

@ -4,9 +4,10 @@
#include <pthread.h>
#include <stdbool.h>
#include <stdint.h>
#include "aq_serial.h"
#include "aq_programmer.h"
#include "aq_panel.h"
//#include "aq_panel.h" // Moved to later in file to overcome circular dependancy. (crappy I know)
#define DEFAULT_POLL_SPEED -1
#define DEFAULT_POLL_SPEED_NON_THREADDED 2
@ -20,9 +21,17 @@
#define MAX_ZERO_READ_BEFORE_RECONNECT 10000 // 2k normally
void intHandler(int dummy);
void setUnits(const char *msg);
// 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.
#define PRESTATE_ONOFF
#define PRESTATE_SWG_SETPOINT
//#define PRESTATE_HEATER_SETPOINT // This one is not implimented yet
void intHandler(int dummy);
#ifdef AQ_PDA
bool checkAqualinkTime(); // Only need to externalise this for PDA
#endif
// There are cases where SWG will read 80% in allbutton and 0% in onetouch/aqualinktouch, this will compile that in or our
//#define READ_SWG_FROM_EXTENDED_ID
@ -37,6 +46,7 @@ void setUnits(const char *msg);
#endif
*/
#define TEMP_UNKNOWN -999
#define TEMP_REFRESH -998
//#define UNKNOWN TEMP_UNKNOWN
#define DATE_STRING_LEN 30
@ -64,7 +74,7 @@ typedef struct aqualinkkey
uint8_t special_mask;
} aqkey;
// special_mask
// 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
@ -79,6 +89,8 @@ struct programmingthread {
//void *thread_args;
};
typedef enum action_type {
NO_ACTION = -1,
POOL_HTR_SETOINT,
@ -89,7 +101,11 @@ typedef enum action_type {
PUMP_RPM,
PUMP_VSPROGRAM,
POOL_HTR_INCREMENT, // Setpoint add value (can be negative)
SPA_HTR_INCREMENT // Setpoint add value
SPA_HTR_INCREMENT, // Setpoint add value
ON_OFF,
TIMER,
LIGHT_MODE,
DATE_TIME
} action_type;
struct action {
@ -141,12 +157,22 @@ typedef enum clight_type {
LC_INTELLIB
} clight_type;
typedef enum {
NET_MQTT=0,
NET_API,
NET_WS,
NET_DZMQTT} request_source;
typedef struct clightd
{
clight_type lightType;
aqkey *button;
} clight_detail;
#include "aq_panel.h"
struct aqualinkdata
{
char version[AQ_MSGLEN*2];

View File

@ -133,6 +133,9 @@ bool checkAqualinkTime()
struct tm aq_tm;
time_t aqualink_time;
if (_aqconfig_.sync_panel_time != true)
return true;
time_difference = (int)difftime(now, last_checked);
if (time_difference < TIME_CHECK_INTERVAL)
{
@ -146,15 +149,35 @@ bool checkAqualinkTime()
}
char datestr[DATE_STRING_LEN];
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)
#ifdef AQ_PDA
if (isPDA_PANEL) {
// date is simply a day or week for PDA.
localtime_r(&now, &aq_tm);
int real_wday = aq_tm.tm_wday; // NSF Need to do this better, we could be off by 7 days
snprintf(datestr, DATE_STRING_LEN, "%s %s",_aqualink_data.date,_aqualink_data.time);
if (strptime(datestr, "%A %I:%M%p", &aq_tm) == NULL) {
LOG(AQUA_LOG,LOG_ERR, "Could not convert PDA RS time string '%s'", datestr);
last_checked = (time_t)NULL;
return true;
}
if (real_wday != aq_tm.tm_wday) {
LOG(PDA_LOG,LOG_INFO, "Day of the week incorrect - request time set\n");
return false;
}
}
else
#endif // AQ_PDA
{
LOG(AQUA_LOG,LOG_ERR, "Could not convert RS time string '%s'", datestr);
last_checked = (time_t)NULL;
return true;
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)
{
LOG(AQUA_LOG,LOG_ERR, "Could not convert RS time string '%s'", datestr);
last_checked = (time_t)NULL;
return true;
}
}
aq_tm.tm_isdst = -1; // Force mktime to use local timezone
@ -174,26 +197,18 @@ bool checkAqualinkTime()
}
void setUnits(const char *msg)
void setUnits(char *msg)
{
char buf[AQ_MSGLEN*3];
rsm_strncpy(buf, (unsigned char *)msg, AQ_MSGLEN*3, AQ_MSGLONGLEN);
// Back up until we find a non space character.
int l = strlen(buf);
while (l > 0) {
if (!isspace(buf[l-1]))
break;
l--;
}
//ascii(buf, msg);
LOG(AQUA_LOG,LOG_DEBUG, "Getting temp units from message '%s', looking at '%c'\n", buf, buf[l - 1]);
LOG(AQUA_LOG,LOG_DEBUG, "Getting temp units from message '%s', looking at '%c'\n", buf, buf[strlen(buf) - 1]);
if (msg[l - 1] == 'F')
if (msg[strlen(msg) - 1] == 'F')
_aqualink_data.temp_units = FAHRENHEIT;
else if (msg[l - 1] == 'C')
else if (msg[strlen(msg) - 1] == 'C')
_aqualink_data.temp_units = CELSIUS;
else
_aqualink_data.temp_units = UNKNOWN;
@ -549,7 +564,7 @@ void _processMessage(char *message, bool reset)
{ // time in format '9:45 AM'
strcpy(_aqualink_data.time, msg);
// Setting time takes a long time, so don't try until we have all other programmed data.
if (_aqconfig_.sync_panel_time == true && _initWithRS == true && strlen(_aqualink_data.date) > 1 && checkAqualinkTime() != true)
if (_initWithRS == true && strlen(_aqualink_data.date) > 1 && checkAqualinkTime() != true)
{
LOG(AQRS_LOG,LOG_NOTICE, "RS time is NOT accurate '%s %s', re-setting on controller!\n", _aqualink_data.time, _aqualink_data.date);
aq_programmer(AQ_SET_TIME, NULL, &_aqualink_data);
@ -638,7 +653,7 @@ void _processMessage(char *message, bool reset)
else if ( (strncasecmp(msg, "BOOST POOL", 10) == 0) && (strcasestr(msg, "REMAINING") != NULL) ) {
// Ignore messages if in programming mode. We get one of these turning off for some strange reason.
if (in_programming_mode(&_aqualink_data) == false) {
snprintf(_aqualink_data.boost_msg, 6, &msg[11]);
snprintf(_aqualink_data.boost_msg, 6, "%s", &msg[11]);
_aqualink_data.boost = true;
msg_loop |= MSG_BOOST;
msg_loop |= MSG_SWG;
@ -653,7 +668,7 @@ void _processMessage(char *message, bool reset)
{
LOG(AQRS_LOG,LOG_DEBUG_SERIAL, "Ignoring '%s'\n", msg);
//_aqualink_data.display_message = msg;
if (in_programming_mode(&_aqualink_data) == false &&
if (in_programming_mode(&_aqualink_data) == false && _aqualink_data.simulate_panel == false &&
stristr(msg, "JANDY AquaLinkRS") == NULL &&
//stristr(msg, "PUMP O") == NULL &&// Catch 'PUMP ON' and 'PUMP OFF' but not 'PUMP WILL TURN ON'
strncasecmp(msg, "PUMP O", 6) != 0 &&// Catch 'PUMP ON' and 'PUMP OFF' but not 'PUMP WILL TURN ON'
@ -671,8 +686,8 @@ void _processMessage(char *message, bool reset)
}
// Send every message if we are in simulate panel mode
if (_aqualink_data.simulate_panel)
strcpy(_aqualink_data.last_display_message, msg);
//if (_aqualink_data.simulate_panel)
// strcpy(_aqualink_data.last_display_message, msg);
//rsm_strncpy(_aqualink_data.last_display_message, (unsigned char *)msg, AQ_MSGLONGLEN, AQ_MSGLONGLEN);
//ascii(_aqualink_data.last_display_message, msg);
@ -681,6 +696,8 @@ void _processMessage(char *message, bool reset)
// We processed the next message, kick any threads waiting on the message.
//printf ("Message kicking\n");
kick_aq_program_thread(&_aqualink_data, ALLBUTTON);
}
@ -717,7 +734,7 @@ bool process_packet(unsigned char *packet, int length)
}
#endif
if ( packet[PKT_CMD] == CMD_STATUS && packet[length-3] == last_checksum)
if ( packet[PKT_CMD] == CMD_STATUS && packet[length-3] == last_checksum && ! in_programming_mode(&_aqualink_data) )
{
LOG(AQRS_LOG,LOG_DEBUG_SERIAL, "RS Received duplicate, ignoring.\n", length);
return false;
@ -764,8 +781,8 @@ bool process_packet(unsigned char *packet, int length)
}
// COLOR MODE programming relies on state changes, so let any threads know
if (_aqualink_data.active_thread.ptype == AQ_SET_LIGHTPROGRAM_MODE) {
//printf ("Light thread kicking\n");
//if (_aqualink_data.active_thread.ptype == AQ_SET_LIGHTPROGRAM_MODE) {
if ( in_light_programming_mode(&_aqualink_data) ) {
kick_aq_program_thread(&_aqualink_data, ALLBUTTON);
}
break;
@ -782,7 +799,8 @@ bool process_packet(unsigned char *packet, int length)
//LOG(AQRS_LOG,LOG_ERR, "Message %s\n",message);
} else {
//strncpy(&message[(processing_long_msg * AQ_MSGLEN)], (char *)packet + PKT_DATA + 1, AQ_MSGLEN);
rsm_strncpy(&message[(processing_long_msg * AQ_MSGLEN)], (unsigned char *)packet + PKT_DATA + 1, AQ_MSGLONGLEN, AQ_MSGLEN);
//rsm_strncpy(&message[(processing_long_msg * AQ_MSGLEN)], (unsigned char *)packet + PKT_DATA + 1, AQ_MSGLONGLEN, AQ_MSGLEN);
rsm_strncpy(&message[( (index-1) * AQ_MSGLEN)], (unsigned char *)packet + PKT_DATA + 1, AQ_MSGLONGLEN, AQ_MSGLEN);
//LOG(AQRS_LOG,LOG_ERR, "Long Message %s\n",message);
if (++processing_long_msg != index) {
LOG(AQRS_LOG,LOG_DEBUG, "Long message index %d doesn't match buffer %d\n",index,processing_long_msg);
@ -795,6 +813,11 @@ bool process_packet(unsigned char *packet, int length)
if (index == 0 || index == 5) {
//printf("RSM process message '%s'\n",message);
// MOVED FROM LINE 701 see if less errors
//kick_aq_program_thread(&_aqualink_data, ALLBUTTON);
LOG(AQRS_LOG,LOG_DEBUG, "Processing Message - '%s'\n",message);
processMessage(message); // This will kick thread
}
@ -892,7 +915,9 @@ void action_delayed_request()
}
// Let's just tell everyone we set it, before we actually did. Makes homekit happy, and it will re-correct on error.
//_aqualink_data.swg_percent = _aqualink_data.unactioned.value;
#ifdef PRESTATE_SWG_SETPOINT
setSWGpercent(&_aqualink_data, _aqualink_data.unactioned.value);
#endif
}
else if (_aqualink_data.unactioned.type == SWG_BOOST)
{
@ -964,6 +989,9 @@ int main(int argc, char *argv[])
bool cmdln_debugRS485 = false;
bool cmdln_lograwRS485 = false;
//printf ("TIMER = %d\n",TIMR_LOG);
#ifdef AQ_MEMCMP
memset(&_aqualink_data, 0, sizeof (struct aqualinkdata));
#endif
@ -1098,10 +1126,10 @@ int main(int argc, char *argv[])
_aqconfig_.log_level = cmdln_loglevel;
if (cmdln_debugRS485)
_aqconfig_.debug_RSProtocol_packets = true;
_aqconfig_.log_protocol_packets = true;
if (cmdln_lograwRS485)
_aqconfig_.log_raw_RS_bytes = true;
_aqconfig_.log_raw_bytes = true;
if (_aqconfig_.display_warnings_web == true)
@ -1141,11 +1169,23 @@ int main(int argc, char *argv[])
LOG(AQUA_LOG,LOG_NOTICE, "Config mqtt_user = %s\n", _aqconfig_.mqtt_user);
LOG(AQUA_LOG,LOG_NOTICE, "Config mqtt_passwd = %s\n", _aqconfig_.mqtt_passwd);
LOG(AQUA_LOG,LOG_NOTICE, "Config mqtt_ID = %s\n", _aqconfig_.mqtt_ID);
LOG(AQUA_LOG,LOG_NOTICE, "Config idx water temp = %d\n", _aqconfig_.dzidx_air_temp);
LOG(AQUA_LOG,LOG_NOTICE, "Config idx pool temp = %d\n", _aqconfig_.dzidx_pool_water_temp);
LOG(AQUA_LOG,LOG_NOTICE, "Config idx spa temp = %d\n", _aqconfig_.dzidx_spa_water_temp);
LOG(AQUA_LOG,LOG_NOTICE, "Config idx SWG Percent = %d\n", _aqconfig_.dzidx_swg_percent);
LOG(AQUA_LOG,LOG_NOTICE, "Config idx SWG PPM = %d\n", _aqconfig_.dzidx_swg_ppm);
if (_aqconfig_.dzidx_air_temp !=TEMP_UNKNOWN) {
LOG(AQUA_LOG,LOG_NOTICE, "Config idx water temp = %d\n", _aqconfig_.dzidx_air_temp);
}
if (_aqconfig_.dzidx_pool_water_temp !=TEMP_UNKNOWN) {
LOG(AQUA_LOG,LOG_NOTICE, "Config idx pool temp = %d\n", _aqconfig_.dzidx_pool_water_temp);
}
if (_aqconfig_.dzidx_spa_water_temp !=TEMP_UNKNOWN) {
LOG(AQUA_LOG,LOG_NOTICE, "Config idx spa temp = %d\n", _aqconfig_.dzidx_spa_water_temp);
}
if (_aqconfig_.dzidx_swg_percent !=TEMP_UNKNOWN) {
LOG(AQUA_LOG,LOG_NOTICE, "Config idx SWG Percent = %d\n", _aqconfig_.dzidx_swg_percent);
}
if (_aqconfig_.dzidx_swg_ppm !=TEMP_UNKNOWN) {
LOG(AQUA_LOG,LOG_NOTICE, "Config idx SWG PPM = %d\n", _aqconfig_.dzidx_swg_ppm);
}
#endif // MG_DISABLE_MQTT
#ifdef AQ_PDA
if (isPDA_PANEL) {
LOG(AQUA_LOG,LOG_NOTICE, "Config PDA Mode = %s\n", bool2text(isPDA_PANEL));
@ -1157,11 +1197,13 @@ int main(int argc, char *argv[])
LOG(AQUA_LOG,LOG_NOTICE, "Config idx pool thermostat = %d\n", _aqconfig_.dzidx_pool_thermostat);
LOG(AQUA_LOG,LOG_NOTICE, "Config idx spa thermostat = %d\n", _aqconfig_.dzidx_spa_thermostat);
*/
#endif // MG_DISABLE_MQTT
LOG(AQUA_LOG,LOG_NOTICE, "Config deamonize = %s\n", bool2text(_aqconfig_.deamonize));
LOG(AQUA_LOG,LOG_NOTICE, "Config log_file = %s\n", _aqconfig_.log_file);
LOG(AQUA_LOG,LOG_NOTICE, "Config enable scheduler = %s\n", bool2text(_aqconfig_.enable_scheduler));
LOG(AQUA_LOG,LOG_NOTICE, "Config light_pgm_mode = %.2f\n", _aqconfig_.light_programming_mode);
LOG(AQUA_LOG,LOG_NOTICE, "Debug RS485 protocol = %s\n", bool2text(_aqconfig_.debug_RSProtocol_packets));
LOG(AQUA_LOG,LOG_NOTICE, "Debug RS485 protocol = %s\n", bool2text(_aqconfig_.log_protocol_packets));
LOG(AQUA_LOG,LOG_NOTICE, "Debug RS485 protocol raw = %s\n", bool2text(_aqconfig_.log_raw_bytes));
//LOG(AQUA_LOG,LOG_NOTICE, "Use PDA 4 auxiliary info = %s\n", bool2text(_aqconfig_.use_PDA_auxiliary));
//LOG(AQUA_LOG,LOG_NOTICE, "Read Pentair Packets = %s\n", bool2text(_aqconfig_.read_pentair_packets));
// logMessage (LOG_NOTICE, "Config serial_port = %s\n", config_parameters->serial_port);
@ -1467,7 +1509,7 @@ void main_loop()
exit(EXIT_FAILURE);
}
startPacketLogger(_aqconfig_.debug_RSProtocol_packets, _aqconfig_.read_pentair_packets);
startPacketLogger();
signal(SIGINT, intHandler);
signal(SIGTERM, intHandler);
@ -1565,12 +1607,14 @@ void main_loop()
LOG(AQUA_LOG,LOG_ERR, "I'm done, exiting, please check '%s'\n",_aqconfig_.serial_port);
return;
}
/*
if (_aqconfig_.log_raw_RS_bytes)
packet_length = get_packet_lograw(rs_fd, packet_buffer);
else
packet_length = get_packet(rs_fd, packet_buffer);
*/
packet_length = get_packet(rs_fd, packet_buffer);
if (packet_length > 0 && _aqconfig_.device_id == 0x00) {
blank_read = 0;
_aqconfig_.device_id = find_unused_address(packet_buffer);
@ -1705,10 +1749,13 @@ void main_loop()
blank_read = 0;
}
packet_length = get_packet(rs_fd, packet_buffer);
/*
if (_aqconfig_.log_raw_RS_bytes)
packet_length = get_packet_lograw(rs_fd, packet_buffer);
else
packet_length = get_packet(rs_fd, packet_buffer);
*/
/*
if (packet_length == AQSERR_READ || packet_length == AQSERR_TIMEOUT)
{
@ -1720,7 +1767,8 @@ void main_loop()
blank_read++;
}
}
else*/ if (packet_length <= 0)
else*/
if (packet_length <= 0)
{
if (_aqconfig_.rs_poll_speed < 0) {
LOG(AQUA_LOG,LOG_ERR, "Nothing read on blocking serial port\n");
@ -1742,8 +1790,10 @@ void main_loop()
if (packet_length > 0 && packet_buffer[PKT_DEST] == _aqconfig_.device_id && getProtocolType(packet_buffer) == JANDY)
{
if (getLogLevel(AQUA_LOG) >= LOG_DEBUG)
if (getLogLevel(AQUA_LOG) >= LOG_DEBUG) {
LOG(AQUA_LOG,LOG_DEBUG, "RS received packet of type %s length %d\n", get_packet_type(packet_buffer, packet_length), packet_length);
logPacketRead(packet_buffer, packet_length);
}
_aqualink_data.updated = process_packet(packet_buffer, packet_length);
#ifdef AQ_PDA

1876
aqualinkd.c.unknown Normal file

File diff suppressed because it is too large Load Diff

BIN
aqualinkd.o Normal file

Binary file not shown.

1911
aqualinkd.x.c Normal file

File diff suppressed because it is too large Load Diff

138
aquarite_logger.c Normal file
View File

@ -0,0 +1,138 @@
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "aq_serial.h"
#include "utils.h"
#define SLOG_MAX 80
#define PACKET_MAX 10000000000
bool _keepRunning = true;
void intHandler(int dummy) {
_keepRunning = false;
logMessage(LOG_NOTICE, "Stopping!");
}
void printHex(char *pk, int length)
{
int i=0;
for (i=0;i<length;i++)
{
printf("0x%02hhx|",pk[i]);
}
}
void printPacket(unsigned char ID, unsigned char *packet_buffer, int packet_length)
{
if (packet_buffer[PKT_DEST] != 0x00)
printf("\n");
printf("%4.4s 0x%02hhx of type %8.8s", (packet_buffer[PKT_DEST]==0x00?"From":"To"), (packet_buffer[PKT_DEST]==0x00?ID:packet_buffer[PKT_DEST]), get_packet_type(packet_buffer, packet_length));
printf(" | HEX: ");
printHex((char *)packet_buffer, packet_length);
if (packet_buffer[PKT_CMD] == CMD_MSG || packet_buffer[PKT_CMD] == CMD_MSG_LONG) {
printf(" Message : ");
//fwrite(packet_buffer + 4, 1, AQ_MSGLEN+1, stdout);
fwrite(packet_buffer + 4, 1, packet_length-7, stdout);
}
printf("\n");
}
int main(int argc, char *argv[]) {
int rs_fd;
int packet_length;
unsigned char packet_buffer[AQ_MAXPKTLEN];
unsigned char lastID;
int received_packets = 0;
if (getuid() != 0) {
fprintf(stderr, "ERROR %s Can only be run as root\n", argv[0]);
return EXIT_FAILURE;
}
//if (idMode)
setLoggingPrms(LOG_DEBUG, false, false);
//else
// setLoggingPrms(LOG_DEBUG_SERIAL, false, false);
if (argc < 2) {
logMessage(LOG_DEBUG, "ERROR, first param must be serial port, ie:-\n %s /dev/ttyUSB0\n\n", argv[0]);
return 1;
}
rs_fd = init_serial_port(argv[1]);
signal(SIGINT, intHandler);
signal(SIGTERM, intHandler);
while (_keepRunning == true) {
if (rs_fd < 0) {
logMessage(LOG_DEBUG, "ERROR, serial port disconnect\n");
}
packet_length = get_packet(rs_fd, packet_buffer);
received_packets++;
if (packet_length == -1) {
// Unrecoverable read error. Force an attempt to reconnect.
logMessage(LOG_DEBUG, "ERROR, on serial port\n");
_keepRunning = false;
} else if (packet_length == 0) {
// Nothing read
} else if (packet_length > 0) {
if ((packet_buffer[PKT_DEST] == DEV_MASTER && (lastID == 0x50 || lastID == 0x58))
|| packet_buffer[PKT_DEST] == 0x50 || packet_buffer[PKT_DEST] == 0x58)
printPacket(lastID, packet_buffer, packet_length);
else if (packet_buffer[PKT_CMD] == CMD_MSG || packet_buffer[PKT_CMD] == CMD_MSG_LONG) {
printf(" To 0x%02hhx of Type Message | ",packet_buffer[PKT_DEST]);
fwrite(packet_buffer + 4, 1, packet_length-7, stdout);
printf("\n");
} else {
printPacket(lastID, packet_buffer, packet_length);
}
//send_messaged(0, 0x00, "AquaPure");
/*
if (received_packets == 10 || received_packets == 20) {
send_test_cmd(rs_fd, 0x50, 0x11, 0x32, 0x7d);
//send_test_cmd(rs_fd, 0x80, CMD_PROBE, NUL, NUL);
//send_test_cmd(rs_fd, 0x50, 0x11, 0x0a, NUL); // Set salt to 10%
//send_test_cmd(rs_fd, 0x50, 0x14, 0x01, 0x77); // Send initial string AquaPure (0x14 is the comand, others should be NUL)
//0x10|0x02|0x50|0x14|0x01|0x77|0x10|0x03|
//0x10|0x02|0x50|0x14|0x01|0x77|0x10|0x03|
// 10 02 50 11 0a 7d 10 03
// 0x11 is set %
printf(" *** SENT TEST *** \n");
}
*/
lastID = packet_buffer[PKT_DEST];
//received_packets++;
//delay(100);
sleep(1);
}
if (received_packets >= PACKET_MAX) {
_keepRunning = false;
}
}
return 0;
}

View File

@ -124,7 +124,7 @@ int build_color_lights_js(struct aqualinkdata *aqdata, char* buffer, int size)
length += sprintf(buffer+length, "var _light_program = [];\n");
length += sprintf(buffer+length, "_light_program[0] = light_program;\n");
for (i=1; i < LIGHT_COLOR_TYPES; i++) {
for (i=1; i <= LIGHT_COLOR_TYPES; i++) {
length += sprintf(buffer+length, "_light_program[%d] = [", i);
for (j=0; j < LIGHT_COLOR_OPTIONS; j++) {
if (_color_light_options[i][j] != NULL)

BIN
color_lights.o Normal file

Binary file not shown.

View File

@ -19,6 +19,7 @@
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <libgen.h>
@ -96,6 +97,8 @@ void init_parameters (struct aqconfig * parms)
parms->dzidx_air_temp = TEMP_UNKNOWN;
parms->dzidx_pool_water_temp = TEMP_UNKNOWN;
parms->dzidx_spa_water_temp = TEMP_UNKNOWN;
parms->dzidx_swg_percent = TEMP_UNKNOWN;
parms->dzidx_swg_ppm = TEMP_UNKNOWN;
//parms->dzidx_pool_thermostat = TEMP_UNKNOWN; // removed until domoticz has a better virtual thermostat
//parms->dzidx_spa_thermostat = TEMP_UNKNOWN; // removed until domoticz has a better virtual thermostat
parms->light_programming_mode = 0;
@ -117,12 +120,15 @@ void init_parameters (struct aqconfig * parms)
//parms->read_pentair_packets = false;
parms->read_RS485_devmask = 0;
parms->use_panel_aux_labels = false;
parms->debug_RSProtocol_packets = false;
parms->force_swg = false;
//parms->swg_pool_and_spa = false;
parms->swg_zero_ignore = DEFAULT_SWG_ZERO_IGNORE_COUNT;
parms->display_warnings_web = false;
parms->log_raw_RS_bytes = false;
parms->log_protocol_packets = false; // Read & Write as packets write to file
parms->log_raw_bytes = false; // bytes read and write to file
parms->sync_panel_time = true;
// Default parameters for threading and USB blocking
@ -130,11 +136,16 @@ void init_parameters (struct aqconfig * parms)
parms->rs_poll_speed = DEFAULT_POLL_SPEED;
parms->thread_netservices = true;
parms->enable_scheduler = true;
generate_mqtt_id(parms->mqtt_ID, MQTT_ID_LEN);
}
char *cleanalloc(char*str)
{
if (str == NULL)
return str;
char *result;
str = cleanwhitespace(str);
@ -533,8 +544,11 @@ bool setConfigValue(struct aqualinkdata *aqdata, char *param, char *value) {
} else if (strncasecmp (param, "force_SWG", 9) == 0) {
_aqconfig_.force_swg = text2bool(value);
rtn=true;
} else if (strncasecmp (param, "debug_RSProtocol_bytes", 22) == 0) {
_aqconfig_.log_raw_bytes = text2bool(value);
rtn=true;
} else if (strncasecmp (param, "debug_RSProtocol_packets", 24) == 0) {
_aqconfig_.debug_RSProtocol_packets = text2bool(value);
_aqconfig_.log_protocol_packets = text2bool(value);
rtn=true;
} else if (strncasecmp (param, "swg_zero_ignore_count", 21) == 0) {
_aqconfig_.swg_zero_ignore = strtoul(value, NULL, 10);
@ -561,8 +575,12 @@ bool setConfigValue(struct aqualinkdata *aqdata, char *param, char *value) {
} else if (strncasecmp (param, "thread_netservices", 18) == 0) {
_aqconfig_.thread_netservices = text2bool(value);
rtn=true;
} else if (strncasecmp (param, "enable_scheduler", 16) == 0) {
_aqconfig_.enable_scheduler = text2bool(value);
rtn=true;
}
else if (strncasecmp(param, "button_", 7) == 0) {
// Check we have inichalized panel information, if not use any settings we may have
if (_aqconfig_.paneltype_mask == 0)
@ -725,7 +743,8 @@ void read_config (struct aqualinkdata *aqdata, char *cfgFile)
} else {
/* error processing, couldn't open file */
LOG(AQUA_LOG,LOG_ERR, "Error reading config file '%s'\n",cfgFile);
displayLastSystemError(cfgFile);
errno = EBADF;
displayLastSystemError("Error reading config file");
exit (EXIT_FAILURE);
}
@ -765,10 +784,11 @@ char *errorlevel2text(int level)
return "";
}
/*
bool remount_root_ro(bool readonly) {
// NSF Check if config is RO_ROOT set
if (readonly) {} // Dummy to stop compile warnings.
/*
if (readonly) {
LOG(AQUA_LOG,LOG_INFO, "reMounting root RO\n");
mount (NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
@ -783,10 +803,10 @@ bool remount_root_ro(bool readonly) {
mount (NULL, "/", NULL, MS_REMOUNT, NULL);
return true;
}
*/
return true;
}
*/
void writeCharValue (FILE *fp, char *msg, char *value)
{
if (value == NULL)

View File

@ -75,14 +75,15 @@ struct aqconfig
bool force_swg;
int swg_zero_ignore;
bool display_warnings_web;
bool read_pentair_packets;
bool debug_RSProtocol_packets;
bool log_raw_RS_bytes;
bool log_protocol_packets; // Read & Write as packets
bool log_raw_bytes; // Read as bytes
//bool log_raw_RS_bytes;
bool readahead_b4_write;
bool mqtt_timed_update;
bool sync_panel_time;
int rs_poll_speed;
bool thread_netservices;
bool enable_scheduler;
};
#ifndef CONFIG_C

BIN
config.o Normal file

Binary file not shown.

770
config_new.c Normal file
View File

@ -0,0 +1,770 @@
/*
* Copyright (c) 2017 Shaun Feakes - All rights reserved
*
* You may use redistribute and/or modify this code under the terms of
* the GNU General Public License version 2 as published by the
* Free Software Foundation. For the terms of this license,
* see <http://www.gnu.org/licenses/>.
*
* You are free to use this software under the terms of the GNU General
* Public License, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* https://github.com/sfeakes/aqualinkd
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <libgen.h>
#include <sys/ioctl.h>
//#include <sys/socket.h>
//#include <sys/time.h>
//#include <syslog.h>
//#include <unistd.h>
#include <netdb.h>
//#include <linux/if.h>
//#include <sys/types.h>
#include <unistd.h>
#include <net/if.h>
#define CONFIG_C
#include "aqualink.h"
#include "config.h"
#include "utils.h"
#include "aq_serial.h"
#define MAXCFGLINE 256
typedef enum cfg_type{cfg_ptr, cfg_str, cfg_int, cfg_bool, cfg_hex, cfg_float, cfg_loglevel} cfg_type;
struct cfg_line {
cfg_type type;
void **value;
void *value_ptr;
char *description;
//char name[20];
char *name;
int size;
};
#define SERIAL_PORT_DESC "Serial Port for RS485"
#define LOG_LEVEL_DESC "The level for logging. [DEBUG_SERIAL, DEBUG, INFO, NOTICE, WARNING, ERROR]"
#define SOCKET_PORT_DESC "Port for web interface, usually 80"
#define WEB_DIRECTORY_DESC "Root web directory, usuall /var/www/aqualinkd"
#define DEVICE_ID_DESC "Main device ID for RS485"
#define ONETOUCH_ID_DESC "Device ID for extended RS485 commands (ie variable speed pump)"
#define DEAMONIZE_DESC "Run process as service, ie deamonize?"
#define LOG_FILE_DESC "Log to a file and not syslog, use log filename ie /var/log/aqualinkd.log"
#define MQTT_DZ_SUB_TOPIC_DESC "Domoticz MQTT subscription topic (usually domoticz/out, comment out if you don't use domoticz)"
#define MQTT_DZ_PUB_TOPIC_DESC "Domoticz MQTT publish topic (usually domoticz/in, comment out if you don't use domoticz)"
#define MQTT_AQ_TOPIC_DESC "AqualinkD MQTT topic (usually aqualinkd, comment out if you don't want to publish events to MQTT"
#define MQTT_ADDRESS_DESC "MQTT Server and port (machinename:port)"
#define MQTT_USER_DESC "MQTT Username (commant out if you don't use)"
#define MQTT_PASSWD_DESC "MQTT Password (commant out if you don't use)"
#define MQTT_ID_DESC "MQTT ID. Unique identifyer for MQTT subscription, (leave blank, AqualinkD will generate one)"
#define AIR_TEMP_DZIDX_DESC "dzidx_air_temp"
#define POOL_TEMP_DZIDX_DESC "dzidx_pool_water_temp"
#define SPA_TEMP_DZIDX_DESC "dzidx_spa_water_temp"
#define SWG_PERCENT_DZIDX_DESC "SWG_percent_dzidx"
#define SWG_PPM_DZIDX_DESC "SWG_PPM_dzidx"
#define SWG_STATUS_DZIDX_DESC "SWG_Status_dzidx"
#define LIGHT_PROGM_MODE_DESC "light_programming_mode"
#define LIGHT_PROGM_INIT_ON_DESC "light_programming_initial_on"
#define LIGHT_PROGM_INIT_OFF_DESC "light_programming_initial_off"
#define LIGHT_PROGM_SPA_DESC "Button for spa light if programable. (leave blank if not programable)"
#define LIGHT_PROGM_POOL_DESC "Button for pool light if programable. (leave blank if not programable)"
#define OVERRIDE_FRZ_PROTECT_DESC "override_freeze_protect"
#define PDA_MODE_DESC "pda_mode"
#define PDA_SLEEP_MODE_DESC "pda_sleep_mode"
#define CONVERT_MQTT_TEMP_2C_DESC "convert_mqtt_temp_to_c"
#define CONVERT_DZ_TEMP_2C_DESC "convert_dz_temp_to_c"
#define REPORT_ZERO_SPA_TEMP_DESC "report_zero_spa_temp"
#define REPORT_ZERO_POOL_TEMP_DESC "report_zero_pool_temp"
#define READ_ALL_DEVICES_DESC "read_all_devices"
#define READ_PENTAIR_PACKETS_DESC "read_pentair_packets"
#define USE_PANEL_AUX_LABELS_DESC "use_panel_aux_labels"
#define FORCE_SWG_DESC "force_swg"
#define DEBUG_RSPROTOCOL_DESC "debug_RSProtocol_packets"
#define DISPLAY_WARN_WEB "display_warnings_in_web"
#define NONE TEMP_UNKNOWN
// Need to be global
void cfg_init ();
void readCfgFile(struct aqualinkdata *aqdata, char *cfgFile);
bool writeCfgFile(struct aqualinkdata *aqdata);
struct cfg_line *_cfg_lines;
char *generate_mqtt_id(char *buf, int len);
void setup_cfg_param(struct cfg_line *cfg, char *name, void *ptr, cfg_type type, char *des);
void set_cfg_param(struct cfg_line *cfg, char *name, void *ptr, void *def, cfg_type type, char *des);
bool setConfigButtonValue(struct aqconfig *cfg_prms, struct aqualinkdata *aqdata, char *param, char *value);
char *errorlevel2text(int level);
pump_detail *getpump(struct aqualinkdata *aqdata, int button);
void cfg_init () {
set_cfg_param(&_cfg_lines[__COUNTER__], "serial_port", &_aqconfig_.serial_port, (void *)DEFAULT_SERIALPORT, cfg_ptr, SERIAL_PORT_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "log_level", &_aqconfig_.log_level, (void *)DEFAULT_LOG_LEVEL, cfg_loglevel, LOG_LEVEL_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "socket_port", &_aqconfig_.socket_port, (void *)DEFAULT_WEBPORT, cfg_ptr, SOCKET_PORT_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "web_directory", &_aqconfig_.web_directory,(void*)DEFAULT_WEBROOT, cfg_ptr, WEB_DIRECTORY_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "device_id", &_aqconfig_.device_id, (void*)strtoul(DEFAULT_DEVICE_ID, NULL, 16), cfg_hex, DEVICE_ID_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "extended_device_id", &_aqconfig_.onetouch_device_id, (void*)0x00, cfg_hex, ONETOUCH_ID_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "deamonize", &_aqconfig_.deamonize, (void *)true, cfg_bool, DEAMONIZE_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "log_file", &_aqconfig_.log_file, NULL, cfg_ptr, LOG_FILE_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "mqtt_aq_topic", &_aqconfig_.mqtt_aq_topic,(void*)DEFAULT_MQTT_AQ_TP, cfg_ptr, MQTT_AQ_TOPIC_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "mqtt_address", &_aqconfig_.mqtt_server, NULL, cfg_ptr, MQTT_ADDRESS_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "mqtt_user", &_aqconfig_.mqtt_user, NULL, cfg_ptr, MQTT_USER_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "mqtt_passwd", &_aqconfig_.mqtt_passwd, NULL, cfg_ptr, MQTT_PASSWD_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "mqtt_ID", &_aqconfig_.mqtt_ID, NULL, cfg_str, MQTT_ID_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "mqtt_dz_sub_topic", &_aqconfig_.mqtt_dz_sub_topic, NULL, cfg_ptr, MQTT_DZ_SUB_TOPIC_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "mqtt_dz_pub_topic", &_aqconfig_.mqtt_dz_pub_topic, NULL, cfg_ptr, MQTT_DZ_PUB_TOPIC_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "air_temp_dzidx", &_aqconfig_.dzidx_air_temp, NULL, cfg_int, AIR_TEMP_DZIDX_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "pool_water_temp_dzidx", &_aqconfig_.dzidx_pool_water_temp, NULL, cfg_int, POOL_TEMP_DZIDX_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "spa_water_temp_dzidx", &_aqconfig_.dzidx_spa_water_temp, NULL, cfg_int, SPA_TEMP_DZIDX_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "SWG_percent_dzidx", &_aqconfig_.dzidx_swg_percent, NULL, cfg_int, SWG_PERCENT_DZIDX_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "SWG_PPM_dzidx", &_aqconfig_.dzidx_swg_ppm, NULL, cfg_int, SWG_PPM_DZIDX_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "SWG_Status_dzidx", &_aqconfig_.dzidx_swg_status, NULL, cfg_int, SWG_STATUS_DZIDX_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "light_programming_mode", &_aqconfig_.light_programming_mode, (void*)0, cfg_float, LIGHT_PROGM_MODE_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "light_programming_initial_on", &_aqconfig_.light_programming_initial_on, (void*)15, cfg_int, LIGHT_PROGM_INIT_ON_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "light_programming_initial_off",&_aqconfig_.light_programming_initial_off, (void*)12, cfg_int, LIGHT_PROGM_INIT_OFF_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "light_programming_button_pool",&_aqconfig_.light_programming_button_pool, NULL, cfg_int, LIGHT_PROGM_POOL_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "light_programming_button_spa", &_aqconfig_.light_programming_button_spa, NULL, cfg_int, LIGHT_PROGM_SPA_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "override_freeze_protect", &_aqconfig_.override_freeze_protect, (void*)false, cfg_bool, OVERRIDE_FRZ_PROTECT_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "pda_mode", &_aqconfig_.pda_mode, (void*)false, cfg_bool, PDA_MODE_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "pda_sleep_mode", &_aqconfig_.pda_sleep_mode, (void*)false, cfg_bool, PDA_SLEEP_MODE_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "convert_mqtt_temp_to_c", &_aqconfig_.convert_mqtt_temp, (void*)true, cfg_bool, CONVERT_MQTT_TEMP_2C_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "convert_dz_temp_to_c", &_aqconfig_.convert_dz_temp, (void*)true, cfg_bool, CONVERT_DZ_TEMP_2C_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "report_zero_spa_temp", &_aqconfig_.report_zero_spa_temp, (void*)false, cfg_bool, REPORT_ZERO_SPA_TEMP_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "report_zero_pool_temp", &_aqconfig_.report_zero_pool_temp, (void*)false, cfg_bool, REPORT_ZERO_POOL_TEMP_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "read_all_devices", &_aqconfig_.read_all_devices, (void*)true, cfg_bool, READ_ALL_DEVICES_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "read_pentair_packets", &_aqconfig_.read_pentair_packets, (void*)true, cfg_bool, READ_PENTAIR_PACKETS_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "use_panel_aux_labels", &_aqconfig_.use_panel_aux_labels, (void*)false, cfg_bool, USE_PANEL_AUX_LABELS_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "force_SWG", &_aqconfig_.force_swg, (void*)false, cfg_bool, FORCE_SWG_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "debug_RSProtocol_packets", &_aqconfig_.debug_RSProtocol_packets, (void*)false, cfg_bool, DEBUG_RSPROTOCOL_DESC);
set_cfg_param(&_cfg_lines[__COUNTER__], "display_warnings_in_web", &_aqconfig_.display_warnings_web, (void*)true, cfg_bool, DISPLAY_WARN_WEB);
}
#define CFG_LEN __COUNTER__
// NSF, need to fix why passing 0 for def doesn't work, but every other number does
// NULL expands to (void *)0, not sure how to overcome that.
void set_cfg_param(struct cfg_line *cfg, char *name, void *ptr, void *def, cfg_type type, char *des) {
cfg->type = type;
cfg->name = name;
cfg->description = des;
switch (type) {
case cfg_ptr:
cfg->value = ptr;
break;
case cfg_str:
case cfg_bool:
case cfg_int:
case cfg_float:
case cfg_loglevel:
case cfg_hex:
cfg->value_ptr = ptr;
cfg->value = cfg->value_ptr;
break;
}
*cfg->value = def;
}
void readCfgFile(struct aqualinkdata *aqdata, char *cfgFile)
{
FILE *fp;
char bufr[MAXCFGLINE];
char *b_ptr;
int i;
bool matched = false;
_aqconfig_.config_file = cleanalloc(cfgFile);
if ((fp = fopen(cfgFile, "r")) != NULL)
{
while (!feof(fp))
{
if (fgets(bufr, MAXCFGLINE, fp) != NULL)
{
b_ptr = &bufr[0];
char *indx;
// Eat leading whitespace
while (isspace(*b_ptr))
b_ptr++;
if (b_ptr[0] != '\0' && b_ptr[0] != '#')
{
matched = false;
indx = strchr(b_ptr, '=');
if (indx != NULL)
{
// key = b_ptr
// value = indx+1
for (i = 0; i < CFG_LEN; i++)
{
if (strncmp(b_ptr, _cfg_lines[i].name, strlen(_cfg_lines[i].name)) == 0)
{
matched = true;
switch (_cfg_lines[i].type)
{
case cfg_ptr:
*_cfg_lines[i].value = cleanalloc(indx + 1);
break;
case cfg_str:
sprintf((char *)_cfg_lines[i].value, stripwhitespace(indx + 1));
break;
case cfg_bool:
*((bool *)_cfg_lines[i].value) = text2bool(indx + 1);
break;
case cfg_int:
*((int *)_cfg_lines[i].value) = strtoul(indx + 1, NULL, 10);
break;
case cfg_float:
*((float *)_cfg_lines[i].value) = atof(stripwhitespace(indx + 1));
break;
case cfg_loglevel:
*((unsigned long *)_cfg_lines[i].value) = text2elevel(stripwhitespace(indx + 1));
break;
case cfg_hex:
*((unsigned char *)_cfg_lines[i].value) = strtoul(cleanalloc(indx + 1), NULL, 16);
break;
}
}
}
if (!matched)
{
if ( ! setConfigButtonValue(&_aqconfig_, aqdata, b_ptr, indx+1))
logMessage(LOG_ERR, "Unknown config parameter '%.*s'\n",strlen(b_ptr)-1, b_ptr);
}
}
}
}
}
fclose(fp);
}
else
{
/* error processing, couldn't open file */
logMessage(LOG_ERR, "Error reading config file '%s'\n", cfgFile);
displayLastSystemError(cfgFile);
exit(EXIT_FAILURE);
}
}
bool writeCfgFile(struct aqualinkdata *aqdata)
{
FILE *fp;
char bufr[MAXCFGLINE*5];
int i = 0;
int pi=0;
int indx = 0;
_aqconfig_.config_file = "crap.cfg";
//fclose(fp);
if ((fp = fopen(_aqconfig_.config_file, "w")) == NULL)
{
/* error processing, couldn't open file */
logMessage(LOG_ERR, "Error writing config file '%s'\n", _aqconfig_.config_file);
displayLastSystemError(_aqconfig_.config_file);
return false;
}
for (i = 0; i < CFG_LEN; i++)
{
if (_cfg_lines[i].name == NULL)
{
continue;
}
indx = sprintf(bufr, "#%s\n%s = ", _cfg_lines[i].description, _cfg_lines[i].name);
switch (_cfg_lines[i].type)
{
case cfg_bool:
indx += sprintf(&bufr[indx], "%s\n\n", *((bool *)_cfg_lines[i].value) == true ? "Yes" : "No");
break;
case cfg_str:
indx += sprintf(&bufr[indx], "%s\n\n", (char *)_cfg_lines[i].value);
break;
case cfg_ptr:
indx += sprintf(&bufr[indx], "%s\n\n",(char *)*_cfg_lines[i].value!=NULL?*_cfg_lines[i].value:"");
break;
case cfg_int:
indx += sprintf(&bufr[indx], "%d\n\n",*((int *)_cfg_lines[i].value));
break;
case cfg_hex:
indx += sprintf(&bufr[indx], "0x%02hhx\n\n",*((unsigned char *)_cfg_lines[i].value));
break;
case cfg_float:
indx += sprintf(&bufr[indx], "%.2f\n\n",*((float *)_cfg_lines[i].value));
break;
case cfg_loglevel:
indx += sprintf(&bufr[indx], "%s\n\n",errorlevel2text(*((int *)_cfg_lines[i].value)));
break;
}
//printf("%s",bufr);
fwrite(bufr,sizeof(char), strlen(bufr), fp);
}
for (i=0; i < TOTAL_BUTTONS; i++)
{
indx = sprintf(bufr, "button_%.2d_label = %s\n", i+1, aqdata->aqbuttons[i].label);
indx += sprintf(&bufr[indx], "button_%.2d_dzidx = %d\n", i+1, aqdata->aqbuttons[i].dz_idx);
if (aqdata->aqbuttons[i].pda_label != NULL)
indx += sprintf(&bufr[indx], "button_%.2d_PDA_label = %s\n", i+1, aqdata->aqbuttons[i].pda_label);
for(pi=0; pi < MAX_PUMPS; pi++) {
if ( aqdata->pumps[pi].button == &aqdata->aqbuttons[i] ) {
indx += sprintf(&bufr[indx],"button_%.2d_pumpID = 0x%02hhx\n", i+1, aqdata->pumps[pi].pumpID);
indx += sprintf(&bufr[indx],"button_%.2d_pumpIndex = %d\n", i+1, aqdata->pumps[pi].pumpIndex);
}
}
//button_04_pumpID=0x79
//button_04_pumpIndex=2
indx += sprintf(&bufr[indx],"\n");
//printf("%s",bufr);
fwrite(bufr,sizeof(char), strlen(bufr), fp);
}
fclose(fp);
return true;
}
void print_cfg(struct aqualinkdata *aqdata)
{
int i,j;
logMessage(LOG_NOTICE, "+---------------------------- Configuration ----------------------------+\n");
for (i = 0; i < CFG_LEN; i++)
{
if (_cfg_lines[i].name == NULL)
{
continue;
}
switch (_cfg_lines[i].type)
{
case cfg_bool:
logMessage(LOG_NOTICE, "Config %30s = %s\n", _cfg_lines[i].name, *((bool *)_cfg_lines[i].value) == true ? "Yes" : "No");
break;
case cfg_str:
logMessage(LOG_NOTICE, "Config %30s = %s\n",_cfg_lines[i].name,(char *)_cfg_lines[i].value);
break;
case cfg_ptr:
logMessage(LOG_NOTICE, "Config %30s = %s\n",_cfg_lines[i].name,(char *)*_cfg_lines[i].value!=NULL?*_cfg_lines[i].value:"");
break;
case cfg_int:
logMessage(LOG_NOTICE, "Config %30s = %d\n",_cfg_lines[i].name,*((int *)_cfg_lines[i].value));
break;
case cfg_hex:
logMessage(LOG_NOTICE, "Config %30s = 0x%02hhx\n",_cfg_lines[i].name,*((unsigned char *)_cfg_lines[i].value));
break;
case cfg_float:
logMessage(LOG_NOTICE, "Config %30s = %.2f\n",_cfg_lines[i].name,*((float *)_cfg_lines[i].value));
break;
case cfg_loglevel:
//printf("Config %30s = %lu\n",_cfg_lines[i].name,*((unsigned long *)_cfg_lines[i].value));
logMessage(LOG_NOTICE, "Config %30s = %s\n",_cfg_lines[i].name,elevel2text((int)*_cfg_lines[i].value));
break;
}
}
logMessage(LOG_NOTICE, "+------------------------------ Button Labels & Pumps -------------------------------+\n");
for (i = 0; i < TOTAL_BUTTONS; i++)
{
char vsp[] = "None";
int alid = 0;
for (j = 0; j < aqdata->num_pumps; j++) {
//if (_aqualink_data.pumps[j].buttonID == i) {
if (aqdata->pumps[j].button == &aqdata->aqbuttons[i]) {
sprintf(vsp,"0x%02hhx",aqdata->pumps[j].pumpID);
alid = aqdata->pumps[j].pumpIndex;
//printf("Pump %d %d %d\n",_aqualink_data.pumps[j].pumpID, _aqualink_data.pumps[j].buttonID, _aqualink_data.pumps[j].ptype);
}
}
if (!_aqconfig_.pda_mode) {
logMessage(LOG_NOTICE, "Config BTN %-13s = label %-15s | VSP ID %-4s | AL ID %-1d | dzidx %-3d | %s\n",
aqdata->aqbuttons[i].name, aqdata->aqbuttons[i].label, vsp, alid, aqdata->aqbuttons[i].dz_idx,
(i>0 && (i==_aqconfig_.light_programming_button_pool || i==_aqconfig_.light_programming_button_spa)?"Programable":"") );
} else {
logMessage(LOG_NOTICE, "Config BTN %-13s = label %-15s | VSP ID %-4s | AL ID %-1d | PDAlabel %-15s | dzidx %d\n",
aqdata->aqbuttons[i].name, aqdata->aqbuttons[i].label, vsp, alid,
aqdata->aqbuttons[i].pda_label, aqdata->aqbuttons[i].dz_idx );
}
//logMessage(LOG_NOTICE, "Button %d\n", i+1, _aqualink_data.aqbuttons[i].label , _aqualink_data.aqbuttons[i].dz_idx);
}
}
void print_json_cfg(struct aqualinkdata *aqdata)
{
char bufr[MAXCFGLINE*100];
int i = 0;
int pi=0;
int indx = 0;
indx += sprintf(&bufr[indx], "{\"config\":[");
for (i = 0; i < CFG_LEN; i++)
{
if (_cfg_lines[i].name == NULL)
{
continue;
}
indx += sprintf(&bufr[indx], "{\"name\":\"%s\",\"desc\":\"%s\",", _cfg_lines[i].name,_cfg_lines[i].description);
switch (_cfg_lines[i].type)
{
case cfg_bool:
indx += sprintf(&bufr[indx], "\"type\":\"%s\",\"value\":\"%s\"},", "bool", *((bool *)_cfg_lines[i].value) == true ? "Yes" : "No");
break;
case cfg_str:
indx += sprintf(&bufr[indx], "\"type\":\"%s\",\"value\":\"%s\"},", "string", (char *)_cfg_lines[i].value);
break;
case cfg_ptr:
indx += sprintf(&bufr[indx], "\"type\":\"%s\",\"value\":\"%s\"},", "ptr", (char *)*_cfg_lines[i].value!=NULL?*_cfg_lines[i].value:"");
break;
case cfg_int:
indx += sprintf(&bufr[indx], "\"type\":\"%s\",\"value\":\"%d\"},", "int", *((int *)_cfg_lines[i].value));
break;
case cfg_hex:
indx += sprintf(&bufr[indx], "\"type\":\"%s\",\"value\":\"0x%02hhx\"},","hex",*((unsigned char *)_cfg_lines[i].value));
break;
case cfg_float:
indx += sprintf(&bufr[indx], "\"type\":\"%s\",\"value\":\"%.2f\"},","float",*((float *)_cfg_lines[i].value));
break;
case cfg_loglevel:
indx += sprintf(&bufr[indx], "\"type\":\"%s\",\"value\":\"%s\"},","loglevel",errorlevel2text(*((int *)_cfg_lines[i].value)));
break;
default:
indx += sprintf(&bufr[indx], "},");
break;
}
}
if (bufr[indx-1] == ',')
indx--;
indx += sprintf(&bufr[indx], "], \"buttons\":[");
for (i = 0; i < TOTAL_BUTTONS; i++)
{
int j;
unsigned char vsp = NULL;
int alid = 0;
for (j = 0; j < aqdata->num_pumps; j++) {
if (aqdata->pumps[j].button == &aqdata->aqbuttons[i]) {
vsp = aqdata->pumps[j].pumpID;
alid = aqdata->pumps[j].pumpIndex;
}
}
if (!_aqconfig_.pda_mode) {
indx += sprintf(&bufr[indx], "{\"button\":\"%d\",\"label\":\"%s\",\"dzidx\":\"%d\"",i,aqdata->aqbuttons[i].label,aqdata->aqbuttons[i].dz_idx);
if (vsp != NULL)
indx += sprintf(&bufr[indx], ",\"pumpID\":\"0x%02hhx\"",vsp);
if (alid > 0)
indx += sprintf(&bufr[indx], ",\"pumpIndex\":\"%d\"",alid);
indx += sprintf(&bufr[indx], "},");
} else {
logMessage(LOG_NOTICE, "Config BTN %-13s = label %-15s | VSP ID %-4s | AL ID %-1d | PDAlabel %-15s | dzidx %d\n",
aqdata->aqbuttons[i].name, aqdata->aqbuttons[i].label, vsp, alid,
aqdata->aqbuttons[i].pda_label, aqdata->aqbuttons[i].dz_idx );
}
}
if (bufr[indx-1] == ',')
indx--;
indx += sprintf(&bufr[indx], "]}\n");
printf("%s\n",bufr);
}
void init_config(struct aqualinkdata *aqdata, char *cfgFile) {
//struct aqconfig cfg_prms;
//char name[50];
//struct aqualinkdata aqdata;
//memset(aqdata, 0, sizeof(struct aqualinkdata));
_cfg_lines = (struct cfg_line*)malloc(sizeof(struct cfg_line) * CFG_LEN );
initButtons(aqdata);
//sprintf(name, "./release/aqualinkd.test.conf");
cfg_init();
print_cfg(aqdata);
//printf("cfg MQTT CP Address=%p value='%s'\n",&cfg_prms.mqtt_ID,cfg_prms.mqtt_ID);
//printf("\n");
readCfgFile(aqdata, cfgFile);
// (re)set any params
if (strlen(_aqconfig_.mqtt_ID) <= 0)
generate_mqtt_id(_aqconfig_.mqtt_ID, MQTT_ID_LEN);
if (_aqconfig_.device_id >= 0x60 && _aqconfig_.device_id <= 0x63) {
_aqconfig_.pda_mode = true;
}
print_cfg(aqdata);
print_json_cfg(aqdata);
//writeCfgFile(cfg_prms, aqdata);
/*
printf("Force SWG = %s\n",cfg_prms->force_swg==true?"Yes":"No");
printf("Read All Devices = %s\n",cfg_prms->read_all_devices==true?"Yes":"No");
printf("Zero Spa Temp = %s\n",cfg_prms->report_zero_spa_temp==true?"Yes":"No");
printf("Read Pentair = %s\n",cfg_prms->read_pentair_packets==true?"Yes":"No");
*/
//print_cfg();
}
int main(int argc, char *argv[]) {
//struct aqconfig cfg_prms;
char name[50];
struct aqualinkdata aqdata;
memset(&aqdata, 0, sizeof(aqdata));
sprintf(name, "./release/aqualinkd.test.conf");
setLoggingPrms(10 , false, NULL, NULL);
init_config(&aqdata, name);
writeCfgFile(&aqdata);
}
char *cleanalloc(char*str)
{
char *result;
str = cleanwhitespace(str);
result = (char*)malloc(strlen(str)+1);
strcpy ( result, str );
//printf("Result=%s\n",result);
return result;
}
// Find the first network interface with valid MAC and put mac address into buffer upto length
bool mac(char *buf, int len)
{
struct ifreq s;
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
struct if_nameindex *if_nidxs, *intf;
if_nidxs = if_nameindex();
if (if_nidxs != NULL)
{
for (intf = if_nidxs; intf->if_index != 0 || intf->if_name != NULL; intf++)
{
strcpy(s.ifr_name, intf->if_name);
if (0 == ioctl(fd, SIOCGIFHWADDR, &s))
{
int i;
if ( s.ifr_addr.sa_data[0] == 0 &&
s.ifr_addr.sa_data[1] == 0 &&
s.ifr_addr.sa_data[2] == 0 &&
s.ifr_addr.sa_data[3] == 0 &&
s.ifr_addr.sa_data[4] == 0 &&
s.ifr_addr.sa_data[5] == 0 ) {
continue;
}
for (i = 0; i < 6 && i * 2 < len; ++i)
{
sprintf(&buf[i * 2], "%02x", (unsigned char)s.ifr_addr.sa_data[i]);
}
return true;
}
}
}
return false;
}
char *generate_mqtt_id(char *buf, int len) {
extern char *__progname; // glibc populates this
int i;
strncpy(buf, basename(__progname), len);
i = strlen(buf);
if (i < len) {
buf[i++] = '_';
// If we can't get MAC to pad mqtt id then use PID
if (!mac(&buf[i], len - i)) {
sprintf(&buf[i], "%.*d", (len-i), getpid());
}
}
buf[len] = '\0';
return buf;
}
bool setConfigButtonValue(struct aqconfig *cfg_prms, struct aqualinkdata *aqdata, char *param, char *value) {
bool rtn = false;
static int pi=0;
if (strncasecmp(param, "button_", 7) == 0) {
int num = strtoul(param + 7, NULL, 10) - 1;
if (strncasecmp(param + 9, "_label", 6) == 0) {
aqdata->aqbuttons[num].label = cleanalloc(value);
rtn=true;
} else if (strncasecmp(param + 9, "_dzidx", 6) == 0) {
aqdata->aqbuttons[num].dz_idx = strtoul(value, NULL, 10);
rtn=true;
} else if (strncasecmp(param + 9, "_PDA_label", 10) == 0) {
aqdata->aqbuttons[num].pda_label = cleanalloc(value);
rtn=true;
} else if (strncasecmp(param + 9, "_pumpID", 7) == 0) {
pump_detail *pump = getpump(aqdata, num);
if (pump != NULL) {
pump->pumpID = strtoul(cleanalloc(value), NULL, 16);
if (pump->pumpID < 119) {
pump->ptype = PENTAIR;
} else {
pump->ptype = JANDY;
//pump->pumpType = EPUMP; // For testing let the interface set this
}
} else {
logMessage(LOG_ERR, "Config error, VSP Pumps limited to %d, ignoring %s'\n",MAX_PUMPS-1,param);
}
rtn=true;
} else if (strncasecmp(param + 9, "_pumpIndex", 10) == 0) { //button_01_pumpIndex=1
pump_detail *pump = getpump(aqdata, num);
if (pump != NULL) {
pump->pumpIndex = strtoul(value, NULL, 10);
} else {
logMessage(LOG_ERR, "Config error, VSP Pumps limited to %d, ignoring %s'\n",MAX_PUMPS-1,param);
}
rtn=true;
}
}
return rtn;
}
pump_detail *getpump(struct aqualinkdata *aqdata, int button)
{
//static int _pumpindex = 0;
//aqdata->num_pumps
int pi;
// Does it exist
for (pi=0; pi < aqdata->num_pumps; pi++) {
if (aqdata->pumps[pi].button == &aqdata->aqbuttons[button]) {
//printf ("Found pump %d\n",button);
return &aqdata->pumps[pi];
}
}
// Create new entry
if (aqdata->num_pumps < MAX_PUMPS) {
//printf ("Creating pump %d\n",button);
aqdata->pumps[aqdata->num_pumps].button = &aqdata->aqbuttons[button];
aqdata->pumps[aqdata->num_pumps].pumpType = PT_UNKNOWN;
aqdata->pumps[aqdata->num_pumps].rpm = TEMP_UNKNOWN;
aqdata->pumps[aqdata->num_pumps].watts = TEMP_UNKNOWN;
aqdata->pumps[aqdata->num_pumps].gpm = TEMP_UNKNOWN;
aqdata->num_pumps++;
return &aqdata->pumps[aqdata->num_pumps-1];
}
return NULL;
}
//DEBUG_DERIAL, DEBUG, INFO, NOTICE, WARNING, ERROR
char *errorlevel2text(int level)
{
switch(level) {
case LOG_DEBUG_SERIAL:
return "DEBUG_SERIAL";
break;
case LOG_DEBUG:
return "DEBUG";
break;
case LOG_INFO:
return "INFO";
break;
case LOG_NOTICE:
return "NOTICE";
break;
case LOG_WARNING:
return "WARNING";
break;
case LOG_ERR:
default:
return "ERROR";
break;
}
return "";
}
bool remount_root_ro(bool readonly) {
// NSF Check if config is RO_ROOT set
if (readonly) {} // Dummy to stop compile warnings.
/*
if (readonly) {
logMessage(LOG_INFO, "reMounting root RO\n");
mount (NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
return true;
} else {
struct statvfs fsinfo;
statvfs("/", &fsinfo);
if ((fsinfo.f_flag & ST_RDONLY) == 0) // We are readwrite, ignore
return false;
logMessage(LOG_INFO, "reMounting root RW\n");
mount (NULL, "/", NULL, MS_REMOUNT, NULL);
return true;
}
*/
return true;
}

1023
crap.out Normal file

File diff suppressed because it is too large Load Diff

View File

@ -49,19 +49,22 @@ bool processPacketToSWG(unsigned char *packet, int packet_length, struct aqualin
swg_zero_cnt++;
} else if (swg_zero_cnt > swg_zero_ignore && packet[4] == 0x00) {
if (aqdata->swg_percent != (int)packet[4]) {
aqdata->swg_percent = (int)packet[4];
//aqdata->swg_percent = (int)packet[4];
setSWGpercent(aqdata, (int)packet[4]);
changedAnything = true;
aqdata->updated = true;
LOG(DJAN_LOG, LOG_DEBUG, "Set SWG %% to %d from control panel packet to SWG\n", aqdata->swg_percent);
LOG(DJAN_LOG, LOG_DEBUG, "Set SWG %% to %d from reading control panel packet sent to SWG (received %d messages)\n", aqdata->swg_percent, swg_zero_cnt);
}
// LOG(DJAN_LOG, LOG_DEBUG, "SWG set to %d due to packet packet count %d <= %d from control panel to SWG 0x%02hhx 0x%02hhx\n",
// (int)packet[4],swg_zero_cnt,SWG_ZERO_IGNORE_COUNT,packet[4],packet[5]); swg_zero_cnt++;
} else {
swg_zero_cnt = 0;
if (aqdata->swg_percent != (int)packet[4]) {
aqdata->swg_percent = (int)packet[4];
//aqdata->swg_percent = (int)packet[4];
setSWGpercent(aqdata, (int)packet[4]);
changedAnything = true;
aqdata->updated = true;
LOG(DJAN_LOG, LOG_DEBUG, "Set SWG %% to %d from control panel packet to SWG\n", aqdata->swg_percent);
}
// LOG(DJAN_LOG, LOG_DEBUG, "SWG set to %d due to packet from control panel to SWG 0x%02hhx 0x%02hhx\n",
// aqdata.swg_percent,packet[4],packet[5]);
@ -166,12 +169,12 @@ void setSWGdeviceStatus(struct aqualinkdata *aqdata, emulation_type requester, u
aqdata->swg_led_state = OFF;
break;
default:
LOG(DJAN_LOG, LOG_ERR, "Ignoring set SWG device to state '0x%02hhx', state is unknown\n", status);
LOG(DJAN_LOG, LOG_WARNING, "Ignoring set SWG device to state '0x%02hhx', state is unknown\n", status);
return;
break;
}
LOG(DJAN_LOG, LOG_DEBUG, "Set SWG device state to '0x%02hhx', request from %d\n", aqdata->ar_swg_device_status, requester);
LOG(DJAN_LOG, LOG_DEBUG, "Set SWG device state to '0x%02hhx', request from %d, LED state = %d\n", aqdata->ar_swg_device_status, requester, aqdata->swg_led_state);
}
@ -243,6 +246,7 @@ void setSWGpercent(struct aqualinkdata *aqdata, int percent) {
aqdata->updated = true;
if (aqdata->swg_percent > 0) {
//LOG(DJAN_LOG, LOG_DEBUG, "swg_led_state=%d, swg_led_state=%d, isSWGDeviceErrorState=%d, ar_swg_device_status=%d\n",aqdata->swg_led_state, aqdata->swg_led_state, isSWGDeviceErrorState(aqdata->ar_swg_device_status),aqdata->ar_swg_device_status);
if (aqdata->swg_led_state == OFF || (aqdata->swg_led_state == ENABLE && ! isSWGDeviceErrorState(aqdata->ar_swg_device_status)) ) // Don't change ENABLE / FLASH
aqdata->swg_led_state = ON;
@ -304,6 +308,14 @@ aqledstate get_swg_led_state(struct aqualinkdata *aqdata)
}
}
void get_swg_status_msg(struct aqualinkdata *aqdata, char *message)
{
int tmp1;
int tmp2;
return get_swg_status_mqtt(aqdata, message, &tmp1, &tmp2);
}
void get_swg_status_mqtt(struct aqualinkdata *aqdata, char *message, int *status, int *dzalert)
{
switch (aqdata->ar_swg_device_status) {
@ -375,15 +387,27 @@ bool processPacketToJandyPump(unsigned char *packet_buffer, int packet_length, s
{
char msg[1000];
//logMessage(LOG_DEBUG, "Need to log ePump message here for future\n");
beautifyPacket(msg, packet_buffer, packet_length);
beautifyPacket(msg, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_DEBUG, "To ePump: %s\n", msg);
//find pump for message
if ( 1 == 0 /*SOME_DEBUG_TEST*/) {
int i;
for (i=0; i < aqdata->num_pumps; i++) {
if (aqdata->pumps[i].pumpID == packet_buffer[PKT_DEST]) {
LOG(DJAN_LOG, LOG_DEBUG, "Last panel info RPM:%d GPM:%d WATTS:%d\n", aqdata->pumps[i].rpm, aqdata->pumps[i].gpm, aqdata->pumps[i].watts);
break;
}
}
}
return false;
}
bool processPacketFromJandyPump(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata)
{
char msg[1000];
//logMessage(LOG_DEBUG, "Need to log ePump message here for future\n");
beautifyPacket(msg, packet_buffer, packet_length);
beautifyPacket(msg, packet_buffer, packet_length, true);
LOG(DJAN_LOG, LOG_DEBUG, "From ePump: %s\n", msg);
return false;
}

BIN
devices_jandy.o Normal file

Binary file not shown.

BIN
devices_pentair.o Normal file

Binary file not shown.

120
extras/sync-lights.sh Executable file
View File

@ -0,0 +1,120 @@
#!/bin/bash
#
# To re-synchronize your lights,
# turn the switch on,
# then back off,
# then wait between 11 and 14 seconds and turn the switch back on.
# When the lights come back on, they should enter program #1, and be synchronized.
# Different way to sync
# To re-synchronize your lights, start with the lights off and follow the steps below:
# 1. Turn lights on.
# 2. Turn off light for between 11-15 seconds,.
# 3. Turn lights on.
# Different again
# https://images-na.ssl-images-amazon.com/images/I/A1aZApuRWQS.pdf
# Manual
# https://hayward-pool-assets.com/assets/documents/pools/pdf/manuals/colorlogic-installation-operation.pdf
#
# Warm up time
# https://www.troublefreepool.com/threads/pool-lights-wont-sync-up.177510/
URL="http://pool/api/Pool%20Light/set"
function delay {
echo "Sleeping $1 seconds"
sleep $1
}
function on {
echo -n "Turning light on. "
curl -X PUT -d value=1 $URL
echo ""
}
function off {
echo -n "Turning light off. "
curl -X PUT -d value=0 $URL
echo ""
}
# This is light mode check
function mode_check {
on
delay 120
off
delay 12
on
delay 1
off
delay 12
on
delay 1
off
delay 12
on
delay 1
off
delay 12
on
echo "Lights should be flashing Red and White, power down for at least 1 minute"
}
# This is color recync. or color program.
function resync {
on
delay 70
off
delay 12
on
echo "Lights should be acting the same color mode"
}
function change_mode {
off
delay 12
on
echo "Lights should be acting the same color mode"
}
function mode {
on
delay 1
off
delay 13
on
delay 1
off
delay 13
on
delay 1
off
delay 13
on
echo "Lights should blink red"
}
resync
read -p "Y/N " input
while [ "$input" == "n" ]; do
change_mode
read -p "Y/N " input
done
echo "Leave Lights off for at least 2 mins"
off
exit
#mode_check
resync

View File

@ -517,6 +517,7 @@ void processPage(struct aqualinkdata *aq_data)
bool process_iaqtouch_packet(unsigned char *packet, int length, struct aqualinkdata *aq_data)
{
static int cnt = 0;
static bool gotStatus = true;
//char buff[1024];
if (packet[PKT_CMD] == CMD_IAQ_PAGE_START) {
@ -527,6 +528,9 @@ bool process_iaqtouch_packet(unsigned char *packet, int length, struct aqualinkd
memset(_pageButtons, 0, IAQ_PAGE_BUTTONS * sizeof(struct iaqt_page_button));
memset(_deviceStatus, 0, sizeof(char) * IAQ_STATUS_PAGE_LINES * AQ_MSGLEN+1 );
memset(_tableInformation, 0, sizeof(char) * IAQ_MSG_TABLE_LINES * AQ_MSGLEN+1 );
// Fix bug with control panel where after a few hours status page disapears and you need to hit menu.
if (gotStatus == false)
gotStatus = true;
//[IAQ_STATUS_PAGE_LINES][AQ_MSGLEN+1];
} else if (packet[PKT_CMD] == CMD_IAQ_PAGE_END) {
set_iaq_cansend(true);
@ -580,11 +584,18 @@ bool process_iaqtouch_packet(unsigned char *packet, int length, struct aqualinkd
}
// Standard ack/poll not interested in printing or kicking threads
if (packet[3] == 0x30) {
// Load status page every 1000 messages
//LOG(IAQT_LOG,LOG_DEBUG, "poll count %d\n",cnt);
// Load status page every 50 messages
if (cnt++ > REQUEST_STATUS_POLL_COUNT && in_programming_mode(aq_data) == false ) {
iaqt_queue_cmd(KEY_IAQTCH_STATUS);
gotStatus = false; // Reset if we got status page, for fix panel bug.
//aq_programmer(AQ_GET_IAQTOUCH_VSP_ASSIGNMENT, NULL, aq_data);
cnt = 0;
} else if (gotStatus == false && cnt > 3) {
// Fix bug with control panel where after a few hours status page disapears and you need to hit menu.
LOG(IAQT_LOG,LOG_INFO, "Overcomming Jandy control panel bug, (missing status, goto menu)\n",cnt);
iaqt_queue_cmd(KEY_IAQTCH_HOME);
iaqt_queue_cmd(KEY_IAQTCH_STATUS);
} else if (in_programming_mode(aq_data) == true) {
// Set count to something close to above, so we will pull latest info once programming has finished.
// This is goot for VSP GPM programming as it takes number of seconds to register once finished programming.

BIN
iaqtouch.o Normal file

Binary file not shown.

View File

@ -137,7 +137,7 @@ int ref_iaqt_control_cmd(unsigned char **cmd)
if ( getLogLevel(IAQT_LOG) >= LOG_DEBUG ) {
char buff[1000];
//sprintf("Sending control command:")
beautifyPacket(buff, _iaqt_control_cmd, _iaqt_control_cmd_len);
beautifyPacket(buff, _iaqt_control_cmd, _iaqt_control_cmd_len, false);
LOG(IAQT_LOG,LOG_DEBUG, "Sending commandsed : %s\n", buff);
}

BIN
iaqtouch_aq_programmer.o Normal file

Binary file not shown.

View File

@ -206,7 +206,7 @@ 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\"");
length += sprintf(buffer, ",\"type_ext\": \"switch_timer\", \"timer_active\":\"%s\"", (((button->special_mask & TIMER_ACTIVE) == TIMER_ACTIVE)?JSON_ON:JSON_OFF) );
return buffer;
}
@ -236,7 +236,7 @@ int build_device_JSON(struct aqualinkdata *aqdata, char* buffer, int size, bool
for (i=0; i < aqdata->total_buttons; i++)
{
if ( strcmp(BTN_POOL_HTR,aqdata->aqbuttons[i].name) == 0 && aqdata->pool_htr_set_point != TEMP_UNKNOWN) {
length += sprintf(buffer+length, "{\"type\": \"setpoint_thermo\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"spvalue\": \"%.*f\", \"value\": \"%.*f\", \"int_status\": \"%d\" },",
length += sprintf(buffer+length, "{\"type\": \"setpoint_thermo\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"spvalue\": \"%.*f\", \"value\": \"%.*f\", \"int_status\": \"%d\", \"timer_active\":\"%s\" },",
aqdata->aqbuttons[i].name,
aqdata->aqbuttons[i].label,
aqdata->aqbuttons[i].led->state==ON?JSON_ON:JSON_OFF,
@ -245,9 +245,11 @@ int build_device_JSON(struct aqualinkdata *aqdata, char* buffer, int size, bool
((homekit_f)?degFtoC(aqdata->pool_htr_set_point):aqdata->pool_htr_set_point),
((homekit)?2:0),
((homekit_f)?degFtoC(aqdata->pool_temp):aqdata->pool_temp),
LED2int(aqdata->aqbuttons[i].led->state));
LED2int(aqdata->aqbuttons[i].led->state),
((aqdata->aqbuttons[i].special_mask & TIMER_ACTIVE) == TIMER_ACTIVE?JSON_ON:JSON_OFF) );
} else if ( strcmp(BTN_SPA_HTR,aqdata->aqbuttons[i].name)==0 && aqdata->spa_htr_set_point != TEMP_UNKNOWN) {
length += sprintf(buffer+length, "{\"type\": \"setpoint_thermo\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"spvalue\": \"%.*f\", \"value\": \"%.*f\", \"int_status\": \"%d\" },",
length += sprintf(buffer+length, "{\"type\": \"setpoint_thermo\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"spvalue\": \"%.*f\", \"value\": \"%.*f\", \"int_status\": \"%d\", \"timer_active\":\"%s\" },",
aqdata->aqbuttons[i].name,
aqdata->aqbuttons[i].label,
aqdata->aqbuttons[i].led->state==ON?JSON_ON:JSON_OFF,
@ -256,7 +258,9 @@ int build_device_JSON(struct aqualinkdata *aqdata, char* buffer, int size, bool
((homekit_f)?degFtoC(aqdata->spa_htr_set_point):aqdata->spa_htr_set_point),
((homekit)?2:0),
((homekit_f)?degFtoC(aqdata->spa_temp):aqdata->spa_temp),
LED2int(aqdata->aqbuttons[i].led->state));
LED2int(aqdata->aqbuttons[i].led->state),
((aqdata->aqbuttons[i].special_mask & TIMER_ACTIVE) == TIMER_ACTIVE?JSON_ON:JSON_OFF));
} else {
get_aux_information(&aqdata->aqbuttons[i], aqdata, aux_info);
//length += sprintf(buffer+length, "{\"type\": \"switch\", \"type_ext\": \"switch_vsp\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"int_status\": \"%d\" %s},",
@ -336,14 +340,14 @@ int build_device_JSON(struct aqualinkdata *aqdata, char* buffer, int size, bool
"on",
((homekit_f)?2:0),
((homekit_f)?degFtoC(aqdata->swg_percent):aqdata->swg_percent));
if (!homekit) { // For the moment keep boost off homekit
//if (!homekit) { // For the moment keep boost off homekit
length += sprintf(buffer+length, "{\"type\": \"switch\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%s\", \"int_status\": \"%d\"},",
SWG_BOOST_TOPIC,
"SWG Boost",
aqdata->boost?JSON_ON:JSON_OFF,
aqdata->boost?JSON_ON:JSON_OFF,
aqdata->boost?LED2int(ON):LED2int(OFF));
}
//}
}
if ( aqdata->swg_ppm != TEMP_UNKNOWN ) {
@ -396,14 +400,14 @@ int build_device_JSON(struct aqualinkdata *aqdata, char* buffer, int size, bool
"Pool Water Temperature",
"on",
((homekit)?2:0),
((homekit_f)?degFtoC(aqdata->air_temp):aqdata->pool_temp));
((homekit_f)?degFtoC(aqdata->pool_temp):aqdata->pool_temp));
length += sprintf(buffer+length, "{\"type\": \"temperature\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"value\": \"%.*f\" }",
SPA_TEMP_TOPIC,
/*SPA_TEMPERATURE,*/
"Spa Water Temperature",
"on",
((homekit)?2:0),
((homekit_f)?degFtoC(aqdata->air_temp):aqdata->spa_temp));
((homekit_f)?degFtoC(aqdata->spa_temp):aqdata->spa_temp));
/*
length += sprintf(buffer+length, "], \"aux_device_detail\": [");
@ -433,6 +437,7 @@ int build_aqualink_status_JSON(struct aqualinkdata *aqdata, char* buffer, int si
length += sprintf(buffer+length, "{\"type\": \"status\"");
length += sprintf(buffer+length, ",\"status\":\"%s\"",getStatus(aqdata) );
length += sprintf(buffer+length, ",\"panel_message\":\"%s\"",aqdata->last_message );
//length += sprintf(buffer+length, ",\"message\":\"%s\"",aqdata->message );
length += sprintf(buffer+length, ",\"version\":\"%s\"",aqdata->version );//8157 REV MMM",
length += sprintf(buffer+length, ",\"aqualinkd_version\":\"%s\"", AQUALINKD_VERSION ); //1.0b,
@ -496,7 +501,7 @@ int build_aqualink_status_JSON(struct aqualinkdata *aqdata, char* buffer, int si
{
char *state = LED2text(aqdata->aqbuttons[i].led->state);
length += sprintf(buffer+length, "\"%s\": \"%s\"", aqdata->aqbuttons[i].name, state);
if (i+1 < aqdata->total_buttons)
length += sprintf(buffer+length, "," );
}
@ -532,10 +537,22 @@ printf("Pump Type %d\n",aqdata->pumps[i].pumpType);
(aqdata->pumps[i].pumpType==VFPUMP?"vfPump":(aqdata->pumps[i].pumpType==VSPUMP?"vsPump":"ePump")));
}
}
if (buffer[length-1] == ',')
length--;
length += sprintf(buffer+length, ",\"timers\":{" );
for (i=0; i < aqdata->total_buttons; i++)
{
if ((aqdata->aqbuttons[i].special_mask & TIMER_ACTIVE) == TIMER_ACTIVE) {
length += sprintf(buffer+length, "\"%s\": \"on\",", aqdata->aqbuttons[i].name);
}
}
if (buffer[length-1] == ',')
length--;
length += sprintf(buffer+length, "}");
length += sprintf(buffer+length, "}" );
buffer[length] = '\0';

BIN
json_messages.o Normal file

Binary file not shown.

16548
mongoose-new/mongoose.c Normal file

File diff suppressed because it is too large Load Diff

6683
mongoose-new/mongoose.h Normal file

File diff suppressed because it is too large Load Diff

BIN
mongoose-new/mongoose.o Normal file

Binary file not shown.

16361
mongoose-old/mongoose.c Normal file

File diff suppressed because it is too large Load Diff

6138
mongoose-old/mongoose.h Normal file

File diff suppressed because it is too large Load Diff

BIN
mongoose-old/mongoose.o Normal file

Binary file not shown.

BIN
mongoose.o Normal file

Binary file not shown.

View File

@ -34,9 +34,11 @@
#include "domoticz.h"
#include "aq_mqtt.h"
#include "devices_jandy.h"
#include "color_lights.h"
#include "web_config.h"
#include "debug_timer.h"
#include "serialadapter.h"
#include "aq_timer.h"
#include "aq_scheduler.h"
#ifdef AQ_PDA
#include "pda.h"
@ -56,8 +58,10 @@ static int _mqtt_exit_flag = false;
// Will remove this once we deprecate V1 API's
#ifdef INCLUDE_V1_API
void OLD_action_web_request(struct mg_connection *nc, struct http_message *http_msg);
void OLD_action_websocket_request(struct mg_connection *nc, struct websocket_message *wm);
#endif
#ifndef MG_DISABLE_MQTT
void start_mqtt(struct mg_mgr *mgr);
@ -86,16 +90,16 @@ static void net_signal_handler(int sig_num) {
static int is_websocket(const struct mg_connection *nc) {
return nc->flags & MG_F_IS_WEBSOCKET && !(nc->flags & MG_F_USER_2);
//return nc->flags & MG_F_IS_WEBSOCKET && !(nc->flags & MG_F_USER_2); // WS only, not WS simulator
return nc->flags & MG_F_IS_WEBSOCKET;
}
static void set_websocket_RSraw(struct mg_connection *nc) {
static void set_websocket_simulator(struct mg_connection *nc) {
nc->flags |= MG_F_USER_2;
}
/*
static int is_websocket_RSraw(const struct mg_connection *nc) {
static int is_websocket_simulator(const struct mg_connection *nc) {
return nc->flags & MG_F_USER_2;
}
*/
static int is_mqtt(const struct mg_connection *nc) {
return nc->flags & MG_F_USER_1;
}
@ -109,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)
@ -239,6 +243,15 @@ 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_state_msg(struct mg_connection *nc, char *dev_name, aqkey *button)
{
static char mqtt_pub_topic[250];
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 );
}
//void send_mqtt_aux_msg(struct mg_connection *nc, char *root_topic, int dev_index, char *dev_topic, int value)
void send_mqtt_aux_msg(struct mg_connection *nc, char *dev_name, char *dev_topic, int value)
{
@ -302,7 +315,9 @@ void send_mqtt_temp_msg(struct mg_connection *nc, char *dev_name, long value)
{
static char mqtt_pub_topic[250];
static char degC[10];
sprintf(degC, "%.2f", (_aqualink_data->temp_units==FAHRENHEIT && _aqconfig_.convert_mqtt_temp)?degFtoC(value):value );
// Use "not CELS" over "equal FAHR" so we default to FAHR for unknown units
//sprintf(degC, "%.2f", (_aqualink_data->temp_units==FAHRENHEIT && _aqconfig_.convert_mqtt_temp)?degFtoC(value):value );
sprintf(degC, "%.2f", (_aqualink_data->temp_units!=CELSIUS && _aqconfig_.convert_mqtt_temp)?degFtoC(value):value );
sprintf(mqtt_pub_topic, "%s/%s", _aqconfig_.mqtt_aq_topic, dev_name);
send_mqtt(nc, mqtt_pub_topic, degC);
}
@ -322,8 +337,9 @@ void send_mqtt_setpoint_msg(struct mg_connection *nc, char *dev_name, long value
{
static char mqtt_pub_topic[250];
static char degC[10];
sprintf(degC, "%.2f", (_aqualink_data->temp_units==FAHRENHEIT && _aqconfig_.convert_mqtt_temp)?degFtoC(value):value );
// Use "not CELS" over "equal FAHR" so we default to FAHR for unknown units
//sprintf(degC, "%.2f", (_aqualink_data->temp_units==FAHRENHEIT && _aqconfig_.convert_mqtt_temp)?degFtoC(value):value );
sprintf(degC, "%.2f", (_aqualink_data->temp_units!=CELSIUS && _aqconfig_.convert_mqtt_temp)?degFtoC(value):value );
sprintf(mqtt_pub_topic, "%s/%s/setpoint", _aqconfig_.mqtt_aq_topic, dev_name);
send_mqtt(nc, mqtt_pub_topic, degC);
}
@ -369,6 +385,7 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
{
static int cnt=0;
int i;
const char *status;
if (_aqconfig_.mqtt_timed_update) {
if (cnt > 300) { // 100 = about every 2 minutes.
@ -387,12 +404,14 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
send_mqtt_string_msg(nc, SERVICE_MODE_TOPIC, _aqualink_data->service_mode_state==OFF?MQTT_OFF:(_aqualink_data->service_mode_state==FLASH?MQTT_FLASH:MQTT_ON));
}
const char *status = getAqualinkDStatusMessage(_aqualink_data);
if (strcmp(status, _last_mqtt_aqualinkdata.last_display_message) != 0) {
strcpy(_last_mqtt_aqualinkdata.last_display_message, status);
send_mqtt_string_msg(nc, DISPLAY_MSG_TOPIC, status);
}
// Only send to display messag topic if not in simulator mode
//if (!_aqualink_data->simulate_panel) {
status = getAqualinkDStatusMessage(_aqualink_data);
if (strcmp(status, _last_mqtt_aqualinkdata.last_display_message) != 0) {
strcpy(_last_mqtt_aqualinkdata.last_display_message, status);
send_mqtt_string_msg(nc, DISPLAY_MSG_TOPIC, status);
}
//}
if (_aqualink_data->air_temp != TEMP_UNKNOWN && _aqualink_data->air_temp != _last_mqtt_aqualinkdata.air_temp) {
_last_mqtt_aqualinkdata.air_temp = _aqualink_data->air_temp;
@ -404,7 +423,9 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
if (_aqualink_data->pool_temp != _last_mqtt_aqualinkdata.pool_temp) {
if (_aqualink_data->pool_temp == TEMP_UNKNOWN && _aqconfig_.report_zero_pool_temp) {
_last_mqtt_aqualinkdata.pool_temp = TEMP_UNKNOWN;
send_mqtt_temp_msg(nc, POOL_TEMP_TOPIC, (_aqconfig_.convert_mqtt_temp?-18:0));
send_mqtt_temp_msg(nc, POOL_TEMP_TOPIC, 0);
} if (_aqualink_data->pool_temp == TEMP_UNKNOWN && ! _aqconfig_.report_zero_pool_temp) {
// Don't post anything in this case, ie leave last posted value alone
} else if (_aqualink_data->pool_temp != TEMP_UNKNOWN) {
_last_mqtt_aqualinkdata.pool_temp = _aqualink_data->pool_temp;
send_mqtt_temp_msg(nc, POOL_TEMP_TOPIC, _aqualink_data->pool_temp);
@ -418,7 +439,13 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
if (_aqualink_data->spa_temp != _last_mqtt_aqualinkdata.spa_temp) {
if (_aqualink_data->spa_temp == TEMP_UNKNOWN && _aqconfig_.report_zero_spa_temp) {
_last_mqtt_aqualinkdata.spa_temp = TEMP_UNKNOWN;
send_mqtt_temp_msg(nc, SPA_TEMP_TOPIC, (_aqconfig_.convert_mqtt_temp?-18:0));
send_mqtt_temp_msg(nc, SPA_TEMP_TOPIC, 0);
} if (_aqualink_data->spa_temp == TEMP_UNKNOWN && ! _aqconfig_.report_zero_spa_temp && _aqualink_data->pool_temp != TEMP_UNKNOWN ) {
// Use Pool Temp as spa temp
if (_last_mqtt_aqualinkdata.spa_temp != _aqualink_data->pool_temp) {
_last_mqtt_aqualinkdata.spa_temp = _aqualink_data->pool_temp;
send_mqtt_temp_msg(nc, SPA_TEMP_TOPIC, _aqualink_data->pool_temp);
}
} else if (_aqualink_data->spa_temp != TEMP_UNKNOWN) {
_last_mqtt_aqualinkdata.spa_temp = _aqualink_data->spa_temp;
send_mqtt_temp_msg(nc, SPA_TEMP_TOPIC, _aqualink_data->spa_temp);
@ -533,7 +560,9 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
} else {
send_mqtt_state_msg(nc, _aqualink_data->aqbuttons[i].name, _aqualink_data->aqbuttons[i].led->state);
}
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)
send_domoticz_mqtt_state_msg(nc, _aqualink_data->aqbuttons[i].dz_idx, (_aqualink_data->aqbuttons[i].led->state==OFF?DZ_OFF:DZ_ON));
}
@ -566,23 +595,8 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
//
int getTempforMeteohub(char *buffer)
{
int length = 0;
if (_aqualink_data->air_temp != TEMP_UNKNOWN)
length += sprintf(buffer+length, "t0 %d\n",(int)degFtoC(_aqualink_data->air_temp)*10);
else
length += sprintf(buffer+length, "t0 \n");
if (_aqualink_data->pool_temp != TEMP_UNKNOWN)
length += sprintf(buffer+length, "t1 %d\n",(int)degFtoC(_aqualink_data->pool_temp)*10);
else
length += sprintf(buffer+length, "t1 \n");
return strlen(buffer);
}
/*
void set_light_mode(char *value, int button)
{
int i;
@ -622,11 +636,11 @@ void set_light_mode(char *value, int button)
aq_programmer(AQ_SET_LIGHTCOLOR_MODE, buf, _aqualink_data);
}
}
*/
typedef enum {uActioned, uBad, uDevices, uStatus, uHomebridge, uDynamicconf, uDebugStatus, uDebugDownload} uriAtype;
typedef enum {NET_MQTT=0, NET_API, NET_WS} netRequest;
const char actionName[][5] = {"MQTT", "API", "WS"};
typedef enum {uActioned, uBad, uDevices, uStatus, uHomebridge, uDynamicconf, uDebugStatus, uDebugDownload, uSimulator, uSchedules, uSetSchedules} uriAtype;
//typedef enum {NET_MQTT=0, NET_API, NET_WS, DZ_MQTT} netRequest;
const char actionName[][5] = {"MQTT", "API", "WS", "DZ"};
#define BAD_SETPOINT "No device for setpoint found"
#define NO_PLIGHT_DEVICE "No programable light found"
@ -637,9 +651,12 @@ const char actionName[][5] = {"MQTT", "API", "WS"};
#define NOCHANGE_IGNORING "No change, device is already in that state"
#define UNKNOWN_REQUEST "Didn't understand request"
void create_program_request(netRequest requester, action_type type, int value, int id) // id is only valid for PUMP RPM
/*
void create_program_request(request_source requester, action_type type, int value, int id) // id is only valid for PUMP RPM
{
//panel_device_request(struct aqualinkdata *aqdata, action_type type, int deviceIndex, int value, int subIndex, request_source source);
//panel_device_request(_aqualink_data, type, id, value, requester);
if (_aqualink_data->unactioned.type != NO_ACTION && type != _aqualink_data->unactioned.type)
LOG(NET_LOG,LOG_ERR, "%s: About to overwrite unactioned panel program\n",actionName[requester]);
@ -669,14 +686,94 @@ void create_program_request(netRequest requester, action_type type, int value, i
else
_aqualink_data->unactioned.requested = 0;
}
*/
#ifdef AQ_PDA
void create_PDA_on_off_request(aqkey *button, bool isON)
{
int i;
char msg[PTHREAD_ARG];
for (i=0; i < _aqualink_data->total_buttons; i++) {
if (_aqualink_data->aqbuttons[i].code == button->code) {
sprintf(msg, "%-5d%-5d", i, (isON? ON : OFF));
aq_programmer(AQ_PDA_DEVICE_ON_OFF, msg, _aqualink_data);
break;
}
}
}
#endif
/*
bool create_panel_request(request_source requester, int buttonIndex, int value, bool timer) {
//if (timer)
// return panel_device_request(_aqualink_data, TIMER, buttonIndex, value, requester);
//else
// return panel_device_request(_aqualink_data, ON_OFF, buttonIndex, value, requester);
// if value = 0 is OFF,
// if value = 1 is ON if (timer = false).
// if value > 0 (timer should be true, and vaue is duration).
if ((_aqualink_data->aqbuttons[buttonIndex].led->state == OFF && value == 0) ||
(value > 0 && (_aqualink_data->aqbuttons[buttonIndex].led->state == ON || _aqualink_data->aqbuttons[buttonIndex].led->state == FLASH ||
_aqualink_data->aqbuttons[buttonIndex].led->state == ENABLE))) {
LOG(NET_LOG, LOG_INFO, "%s: received '%s' for '%s', already '%s', Ignoring\n", actionName[requester], (value == 0 ? "OFF" : "ON"), _aqualink_data->aqbuttons[buttonIndex].name, (value == 0 ? "OFF" : "ON"));
//return false;
} else {
LOG(NET_LOG, LOG_INFO, "%s: received '%s' for '%s', turning '%s'\n", actionName[requester], (value == 0 ? "OFF" : "ON"), _aqualink_data->aqbuttons[buttonIndex].name, (value == 0 ? "OFF" : "ON"));
#ifdef AQ_PDA
if (isPDA_PANEL) {
char msg[PTHREAD_ARG];
sprintf(msg, "%-5d%-5d", buttonIndex, (value == 0 ? OFF : ON));
aq_programmer(AQ_PDA_DEVICE_ON_OFF, msg, _aqualink_data);
} 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 ((_aqualink_data->aqbuttons[buttonIndex].special_mask & PROGRAM_LIGHT) == PROGRAM_LIGHT && _aqualink_data->aqbuttons[buttonIndex].led->state == OFF) {
// 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.
if (isRSSA_ENABLED) {
set_aqualink_rssadapter_aux_state(buttonIndex, true);
} else {
set_light_mode("0", buttonIndex); // 0 means use current light mode
}
} else {
aq_send_cmd(_aqualink_data->aqbuttons[buttonIndex].code);
}
// 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 ((_aqualink_data->aqbuttons[buttonIndex].code == KEY_POOL_HTR || _aqualink_data->aqbuttons[buttonIndex].code == KEY_SPA_HTR ||
_aqualink_data->aqbuttons[buttonIndex].code == KEY_SOLAR_HTR) &&
value > 0) {
_aqualink_data->aqbuttons[buttonIndex].led->state = ENABLE; // if heater and set to on, set pre-status to enable.
//_aqualink_data->updated = true;
} else if (isRSSA_ENABLED || ((_aqualink_data->aqbuttons[buttonIndex].special_mask & PROGRAM_LIGHT) != PROGRAM_LIGHT)) {
_aqualink_data->aqbuttons[buttonIndex].led->state = (value == 0 ? OFF : ON); // as long as it's not programmable light , pre-set to on/off
//_aqualink_data->updated = true;
}
#endif
}
}
// If it's a timer, start the timer
if (timer) {
start_timer(_aqualink_data, &_aqualink_data->aqbuttons[buttonIndex], value);
}
return true;
}
*/
//uriAtype action_URI(char *from, const char *URI, int uri_length, float value, bool convertTemp) {
//uriAtype action_URI(netRequest from, const char *URI, int uri_length, float value, bool convertTemp) {
uriAtype action_URI(netRequest from, const char *URI, int uri_length, float value, bool convertTemp, char **rtnmsg) {
uriAtype action_URI(request_source from, const char *URI, int uri_length, float value, bool convertTemp, char **rtnmsg) {
/* Example URI ()
* Note URI is NOT terminated
* devices
@ -721,6 +818,15 @@ uriAtype action_URI(netRequest from, const char *URI, int uri_length, float valu
return uHomebridge;
} else if (strncmp(ri1, "dynamicconfig", 13) == 0) {
return uDynamicconf;
} else if (strncmp(ri1, "schedules/set", 13) == 0) {
return uSetSchedules;
} else if (strncmp(ri1, "schedules", 9) == 0) {
return uSchedules;
} else if (strncmp(ri1, "simulator", 9) == 0 && from == NET_WS) { // Only valid from websocket.
return uSimulator;
} else if (strncmp(ri1, "rawcommand", 10) == 0 && from == NET_WS) { // Only valid from websocket.
aq_send_cmd((unsigned char)value);
return uActioned;
} else if (strncmp(ri1, "debug", 5) == 0) {
if (ri2 != NULL && strncmp(ri2, "start", 5) == 0) {
startInlineDebug();
@ -739,7 +845,8 @@ uriAtype action_URI(netRequest from, const char *URI, int uri_length, float valu
return uDebugStatus;
// couple of debug items for testing
} else if (strncmp(ri1, "set_date_time", 13) == 0) {
aq_programmer(AQ_SET_TIME, NULL, _aqualink_data);
//aq_programmer(AQ_SET_TIME, NULL, _aqualink_data);
panel_device_request(_aqualink_data, DATE_TIME, 0, 0, from);
return uActioned;
} else if (strncmp(ri1, "startup_program", 15) == 0) {
if(isRS_PANEL)
@ -770,9 +877,11 @@ uriAtype action_URI(netRequest from, const char *URI, int uri_length, float valu
int val = round(value);
if (strncmp(ri1, BTN_POOL_HTR, strlen(BTN_POOL_HTR)) == 0) {
create_program_request(from, POOL_HTR_INCREMENT, val, 0);
//create_program_request(from, POOL_HTR_INCREMENT, val, 0);
panel_device_request(_aqualink_data, POOL_HTR_INCREMENT, 0, val, from);
} else if (strncmp(ri1, BTN_SPA_HTR, strlen(BTN_SPA_HTR)) == 0) {
create_program_request(from, SPA_HTR_INCREMENT, val, 0);
//create_program_request(from, SPA_HTR_INCREMENT, val, 0);
panel_device_request(_aqualink_data, SPA_HTR_INCREMENT, 0, val, from);
} else {
LOG(NET_LOG,LOG_WARNING, "%s: ignoring %.*s setpoint add only valid for pool & spa\n", actionName[from], uri_length, URI);
*rtnmsg = BAD_SETPOINT;
@ -782,15 +891,19 @@ uriAtype action_URI(netRequest from, const char *URI, int uri_length, float valu
} else if (ri3 != NULL && (strncasecmp(ri2, "setpoint", 8) == 0) && (strncasecmp(ri3, "set", 3) == 0)) {
int val = convertTemp? round(degCtoF(value)) : round(value);
if (strncmp(ri1, BTN_POOL_HTR, strlen(BTN_POOL_HTR)) == 0) {
create_program_request(from, POOL_HTR_SETOINT, val, 0);
//create_program_request(from, POOL_HTR_SETOINT, val, 0);
panel_device_request(_aqualink_data, POOL_HTR_SETOINT, 0, val, from);
} else if (strncmp(ri1, BTN_SPA_HTR, strlen(BTN_SPA_HTR)) == 0) {
create_program_request(from, SPA_HTR_SETOINT, val, 0);
//create_program_request(from, SPA_HTR_SETOINT, val, 0);
panel_device_request(_aqualink_data, SPA_HTR_SETOINT, 0, val, from);
} else if (strncmp(ri1, FREEZE_PROTECT, strlen(FREEZE_PROTECT)) == 0) {
create_program_request(from, FREEZE_SETPOINT, val, 0);
//create_program_request(from, FREEZE_SETPOINT, val, 0);
panel_device_request(_aqualink_data, FREEZE_SETPOINT, 0, val, from);
} else if (strncmp(ri1, "SWG", 3) == 0) { // If we get SWG percent as setpoint message it's from homebridge so use the convert
//int val = round(degCtoF(value));
//int val = convertTemp? round(degCtoF(value)) : round(value);
create_program_request(from, SWG_SETPOINT, val, 0);
//create_program_request(from, SWG_SETPOINT, val, 0);
panel_device_request(_aqualink_data, SWG_SETPOINT, 0, val, from);
} else {
// Not sure what the setpoint is, ignore.
LOG(NET_LOG,LOG_WARNING, "%s: ignoring %.*s don't recognise button setpoint\n", actionName[from], uri_length, URI);
@ -812,12 +925,17 @@ uriAtype action_URI(netRequest from, const char *URI, int uri_length, float valu
} else {
val = _aqualink_data->unactioned.value = round(value);
}
create_program_request(from, SWG_SETPOINT, val, 0);
//create_program_request(from, SWG_SETPOINT, val, 0);
panel_device_request(_aqualink_data, SWG_SETPOINT, 0, val, from);
rtn = uActioned;
// Action a SWG boost message
} else if ((ri3 != NULL && (strncmp(ri1, "SWG", 3) == 0) && (strncasecmp(ri2, "Boost", 5) == 0) && (strncasecmp(ri3, "set", 3) == 0))) {
create_program_request(from, SWG_BOOST, round(value), 0);
rtn = uActioned;
//create_program_request(from, SWG_BOOST, round(value), 0);
panel_device_request(_aqualink_data, SWG_BOOST, 0, round(value), from);
if (_aqualink_data->swg_led_state == OFF)
rtn = uBad; // Return bad so we repost a mqtt update
else
rtn = uActioned;
// Action Light program.
} else if ((ri3 != NULL && ((strncasecmp(ri2, "color", 5) == 0) || (strncasecmp(ri2, "program", 7) == 0)) && (strncasecmp(ri3, "set", 3) == 0))) {
found = false;
@ -825,10 +943,11 @@ uriAtype action_URI(netRequest from, const char *URI, int uri_length, float valu
if (strncmp(ri1, _aqualink_data->aqbuttons[i].name, strlen(_aqualink_data->aqbuttons[i].name)) == 0 ||
strncmp(ri1, _aqualink_data->aqbuttons[i].label, strlen(_aqualink_data->aqbuttons[i].label)) == 0)
{
char buf[5];
//char buf[5];
found = true;
sprintf(buf,"%.0f",value);
set_light_mode(buf, i);
//sprintf(buf,"%.0f",value);
//set_light_mode(buf, i);
panel_device_request(_aqualink_data, LIGHT_MODE, i, value, from);
break;
}
}
@ -859,7 +978,8 @@ uriAtype action_URI(netRequest from, const char *URI, int uri_length, float valu
}
} else {
LOG(NET_LOG,LOG_NOTICE, "%s: request to change pump %d %s to %d\n",actionName[from],pumpIndex+1, (strncasecmp(ri2, "GPM", 3) == 0)?"GPM":"RPM", round(value));
create_program_request(from, PUMP_RPM, round(value), pumpIndex);
//create_program_request(from, PUMP_RPM, round(value), pumpIndex);
panel_device_request(_aqualink_data, PUMP_RPM, pumpIndex, round(value), from);
}
//_aqualink_data->unactioned.type = PUMP_RPM;
//_aqualink_data->unactioned.value = round(value);
@ -888,7 +1008,8 @@ uriAtype action_URI(netRequest from, const char *URI, int uri_length, float valu
}
} else {
LOG(NET_LOG,LOG_NOTICE, "%s: request to change pump %d %s to %d\n",actionName[from], pi+1, (strncasecmp(ri2, "GPM", 3) == 0)?"GPM":"RPM", round(value));
create_program_request(from, PUMP_RPM, round(value), _aqualink_data->pumps[pi].pumpIndex);
//create_program_request(from, PUMP_RPM, round(value), _aqualink_data->pumps[pi].pumpIndex);
panel_device_request(_aqualink_data, PUMP_RPM, _aqualink_data->pumps[pi].pumpIndex, round(value), from);
}
//_aqualink_data->unactioned.type = PUMP_RPM;
//_aqualink_data->unactioned.value = round(value);
@ -908,60 +1029,48 @@ uriAtype action_URI(netRequest from, const char *URI, int uri_length, float valu
rtn = uActioned;
}
// Action a Turn on / off message
} else if (ri2 != NULL && (strncasecmp(ri2, "set", 3) == 0) && (strncasecmp(ri2, "setpoint", 8) != 0)) {
} else if ( (ri2 != NULL && (strncasecmp(ri2, "set", 3) == 0) && (strncasecmp(ri2, "setpoint", 8) != 0)) ||
(ri2 != NULL && ri3 != NULL && (strncasecmp(ri2, "timer", 5) == 0) && (strncasecmp(ri3, "set", 3) == 0)) ) {
// Must be a switch on / off
rtn = uActioned;
found = false;
//bool istimer = false;
action_type atype = ON_OFF;
//int timer=0;
if (strncasecmp(ri2, "timer", 5) == 0) {
//istimer = true;
atype = TIMER;
//timer = value; // Save off timer
//value = 1; // Make sure we turn device on if timer.
} else if ( value > 1 || value < 0) {
LOG(NET_LOG,LOG_WARNING, "%s: URI %s has invalid value %.2f\n",actionName[from], URI, value);
*rtnmsg = INVALID_VALUE;
rtn = uBad;
return rtn;
}
for (i=0; i < _aqualink_data->total_buttons; i++) {
// If Label = "Spa", "Spa_Heater" will turn on "Spa", so need to check '/' on label as next character
if (strncmp(ri1, _aqualink_data->aqbuttons[i].name, strlen(_aqualink_data->aqbuttons[i].name)) == 0 ||
strncmp(ri1, _aqualink_data->aqbuttons[i].label, strlen(_aqualink_data->aqbuttons[i].label)) == 0)
(strncmp(ri1, _aqualink_data->aqbuttons[i].label, strlen(_aqualink_data->aqbuttons[i].label)) == 0 && ri1[strlen(_aqualink_data->aqbuttons[i].label)] == '/'))
{
found = true;
//create_panel_request(from, i, value, istimer);
panel_device_request(_aqualink_data, atype, i, value, from);
//LOG(NET_LOG,LOG_INFO, "%s: MATCH %s to topic %.*s\n",from,_aqualink_data->aqbuttons[i].name,uri_length, URI);
// Message is either a 1 or 0 for on or off
//int status = atoi(msg->payload.p);
/*
if ( value > 1 || value < 0) {
LOG(NET_LOG,LOG_WARNING, "%s: URI %s has invalid value %.2f\n",actionName[from], URI, value);
*rtnmsg = INVALID_VALUE;
rtn = uBad;
}
else if ( (_aqualink_data->aqbuttons[i].led->state == OFF && value==0) ||
(value == 1 && (_aqualink_data->aqbuttons[i].led->state == ON ||
_aqualink_data->aqbuttons[i].led->state == FLASH ||
_aqualink_data->aqbuttons[i].led->state == ENABLE))) {
LOG(NET_LOG,LOG_INFO, "%s: received '%s' for '%s', already '%s', Ignoring\n",actionName[from], (value==0?"OFF":"ON"), _aqualink_data->aqbuttons[i].name, (value==0?"OFF":"ON"));
*rtnmsg = NOCHANGE_IGNORING;
rtn = uBad;
if (timer > 0) {
create_panel_request(from, i, timer, true);
} else {
LOG(NET_LOG,LOG_INFO, "%s: received '%s' for '%s', turning '%s'\n",actionName[from], (value==0?"OFF":"ON"), _aqualink_data->aqbuttons[i].name,(value==0?"OFF":"ON"));
#ifdef AQ_PDA
if (isPDA_PANEL) {
char msg[PTHREAD_ARG];
sprintf(msg, "%-5d%-5d",i, (value==0?OFF:ON) );
aq_programmer(AQ_PDA_DEVICE_ON_OFF, msg, _aqualink_data);
} 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 ((_aqualink_data->aqbuttons[i].special_mask & PROGRAM_LIGHT) == PROGRAM_LIGHT &&
_aqualink_data->aqbuttons[i].led->state == OFF ) {
// 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.
if (isRSSA_ENABLED) {
set_aqualink_rssadapter_aux_state(i, true);
} else {
set_light_mode("0", i); // 0 means use current light mode
}
} else {
aq_send_cmd(_aqualink_data->aqbuttons[i].code);
}
}
// Some circumstances these are ignored by control panel, so furce the MQTT state to be sent again on next poll just ncase it doesn't change
// This causes a good MQTT button to flash on/off once before getting the latest update. Need to find a better way
//_last_mqtt_aqualinkdata.aqualinkleds[i].state = LED_S_UNKNOWN;
// NSF Update above to cater for ignored buttons.
}
break;
create_panel_request(from, i, value, false);
}*/
}
}
if(!found) {
@ -988,9 +1097,8 @@ void action_mqtt_message(struct mg_connection *nc, struct mg_mqtt_message *msg)
//unsigned int i;
//LOG(NET_LOG,LOG_DEBUG, "MQTT: topic %.*s %.2f\n",msg->topic.len, msg->topic.p, atof(msg->payload.p));
// If message doesn't end in set or increment we don't care about it.
if (((msg->topic.len < 4) || (strncmp(&msg->topic.p[msg->topic.len -4], "/set", 4) != 0)) &&
((msg->topic.len < 10) || (strncmp(&msg->topic.p[msg->topic.len -10], "/increment", 10) != 0))) {
LOG(NET_LOG,LOG_DEBUG, "MQTT: Ignore %.*s %.*s\n", msg->topic.len, msg->topic.p, msg->payload.len, msg->payload.p);
if (strncmp(&msg->topic.p[msg->topic.len -4], "/set", 4) != 0 && strncmp(&msg->topic.p[msg->topic.len -10], "/increment", 10) != 0) {
LOG(NET_LOG,LOG_DEBUG, "MQTT: Ignore %.*s %.*s\n",msg->topic.len, msg->topic.p, msg->payload.len, msg->payload.p);
return;
}
LOG(NET_LOG,LOG_DEBUG, "MQTT: topic %.*s %.*s\n",msg->topic.len, msg->topic.p, msg->payload.len, msg->payload.p);
@ -1005,7 +1113,23 @@ void action_mqtt_message(struct mg_connection *nc, struct mg_mqtt_message *msg)
//int val = _aqualink_data->unactioned.value = (_aqualink_data->temp_units != CELSIUS && _aqconfig_.convert_mqtt_temp) ? round(degCtoF(value)) : round(value);
bool convert = (_aqualink_data->temp_units != CELSIUS && _aqconfig_.convert_mqtt_temp)?true:false;
int offset = strlen(_aqconfig_.mqtt_aq_topic)+1;
action_URI(NET_MQTT, &msg->topic.p[offset], msg->topic.len - offset, value, convert, &rtnmsg);
if ( action_URI(NET_MQTT, &msg->topic.p[offset], msg->topic.len - offset, value, convert, &rtnmsg) == uBad ) {
// Check if it was something that can't be changed, if so send back current state. Homekit thermostat for SWG and Freezeprotect.
if ( strncmp(&msg->topic.p[offset], FREEZE_PROTECT, strlen(FREEZE_PROTECT)) == 0) {
if (_aqualink_data->frz_protect_set_point != TEMP_UNKNOWN ) {
send_mqtt_setpoint_msg(nc, FREEZE_PROTECT, _aqualink_data->frz_protect_set_point);
send_mqtt_string_msg(nc, FREEZE_PROTECT_ENABELED, MQTT_ON);
} else {
send_mqtt_string_msg(nc, FREEZE_PROTECT_ENABELED, MQTT_OFF);
}
send_mqtt_string_msg(nc, FREEZE_PROTECT, _aqualink_data->frz_protect_state==ON?MQTT_ON:MQTT_OFF);
} else if ( strncmp(&msg->topic.p[offset], SWG_TOPIC, strlen(SWG_TOPIC)) == 0) {
if (_aqualink_data->swg_led_state != LED_S_UNKNOWN) {
send_mqtt_swg_state_msg(nc, SWG_TOPIC, _aqualink_data->swg_led_state);
send_mqtt_int_msg(nc, SWG_BOOST_TOPIC, _aqualink_data->boost);
}
}
}
DEBUG_TIMER_STOP(tid, NET_LOG, "action_mqtt_message() completed, took ");
}
@ -1063,12 +1187,16 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg)
//LOG(NET_LOG,LOG_INFO, "Message request:\n'%.*s'\n", http_msg->message.len, http_msg->message.p);
// If we have a get request, pass it
if ((http_msg->uri.len < 4) || (strncmp(http_msg->uri.p, "/api", 4 ) != 0)) {
if ((mg_vcasecmp(&http_msg->method, "GET")==0) && http_msg->query_string.len > 0) {
if (strncmp(http_msg->uri.p, "/api", 4 ) != 0) {
if (strstr(http_msg->method.p, "GET") && http_msg->query_string.len > 0) {
#ifdef INCLUDE_V1_API
LOG(NET_LOG,LOG_WARNING, "WEB: Old stanza, using old method to action\n");
DEBUG_TIMER_START(&tid);
OLD_action_web_request(nc, http_msg);
DEBUG_TIMER_STOP(tid, NET_LOG, "action_web_request() serve Old stanza took");
#else
LOG(NET_LOG,LOG_ERR, "WEB: Old API stanza requested, ignoring client request\n");
#endif
} else {
DEBUG_TIMER_START(&tid);
mg_serve_http(nc, http_msg, _http_server_opts);
@ -1076,7 +1204,7 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg)
}
//} else if (strstr(http_msg->method.p, "PUT")) {
} else {
char buf[50];
char buf[JSON_BUFFER_SIZE];
float value = 0;
DEBUG_TIMER_START(&tid);
@ -1090,7 +1218,7 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg)
int len = mg_url_decode(http_msg->uri.p, http_msg->uri.len, buf, 50, 0);
if ((http_msg->uri.len >= 5) && (strncmp(http_msg->uri.p, "/api/", 5) == 0)) {
if (strncmp(http_msg->uri.p, "/api/",4) == 0) {
switch (action_URI(NET_API, &buf[5], len-5, value, false, &msg)) {
case uActioned:
mg_send_head(nc, 200, strlen(GET_RTN_OK), CONTENT_TEXT);
@ -1128,8 +1256,29 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg)
{
char message[JSON_BUFFER_SIZE];
DEBUG_TIMER_START(&tid2);
int size = build_color_lights_js(_aqualink_data, message, JSON_BUFFER_SIZE);
DEBUG_TIMER_STOP(tid2, NET_LOG, "action_web_request() build_color_lights_js took");
int size = build_webconfig_js(_aqualink_data, message, JSON_BUFFER_SIZE);
DEBUG_TIMER_STOP(tid2, NET_LOG, "action_web_request() build_webconfig_js took");
mg_send_head(nc, 200, size, CONTENT_JS);
mg_send(nc, message, size);
}
break;
case uSchedules:
{
char message[JSON_BUFFER_SIZE];
DEBUG_TIMER_START(&tid2);
int size = build_schedules_js(message, JSON_BUFFER_SIZE);
DEBUG_TIMER_STOP(tid2, NET_LOG, "action_web_request() build_schedules_js took");
mg_send_head(nc, 200, size, CONTENT_JS);
mg_send(nc, message, size);
}
break;
case uSetSchedules:
{
char message[JSON_BUFFER_SIZE];
DEBUG_TIMER_START(&tid2);
//int size = save_schedules_js(_aqualink_data, &http_msg->body, message, JSON_BUFFER_SIZE);
int size = save_schedules_js((char *)&http_msg->body, http_msg->body.len, message, JSON_BUFFER_SIZE);
DEBUG_TIMER_STOP(tid2, NET_LOG, "action_web_request() save_schedules_js took");
mg_send_head(nc, 200, size, CONTENT_JS);
mg_send(nc, message, size);
}
@ -1160,7 +1309,8 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg)
mg_send_head(nc, 200, strlen(GET_RTN_UNKNOWN), CONTENT_TEXT);
mg_send(nc, GET_RTN_UNKNOWN, strlen(GET_RTN_UNKNOWN));
}
sprintf(buf, "action_web_request() request '%.*s' took",http_msg->uri.len, http_msg->uri.p);
sprintf(buf, "action_web_request() request '%.*s' took",(int)http_msg->uri.len, http_msg->uri.p);
DEBUG_TIMER_STOP(tid, NET_LOG, buf);
}
@ -1199,8 +1349,13 @@ void action_websocket_request(struct mg_connection *nc, struct websocket_message
}
if (uri == NULL) {
#ifdef INCLUDE_V1_API
LOG(NET_LOG,LOG_WARNING, "WS: Old stanza, using old method to action\n");
return OLD_action_websocket_request(nc, wm);
#else
LOG(NET_LOG,LOG_ERR, "WEB: Old websocket stanza requested, ignoring client request\n");
return;
#endif
}
switch ( action_URI(NET_WS, uri, strlen(uri), (value!=NULL?atof(value):TEMP_UNKNOWN), false, &msg)) {
@ -1226,6 +1381,35 @@ void action_websocket_request(struct mg_connection *nc, struct websocket_message
ws_send(nc, message);
}
break;
case uSimulator:
{
LOG(NET_LOG,LOG_DEBUG, "Started Simulator Mode\n");
set_websocket_simulator(nc);
_aqualink_data->simulate_panel = true;
DEBUG_TIMER_START(&tid);
char message[JSON_BUFFER_SIZE];
build_aqualink_status_JSON(_aqualink_data, message, JSON_BUFFER_SIZE); // Should change this to simulator.
DEBUG_TIMER_STOP(tid, NET_LOG, "action_websocket_request() build_aqualink_status_JSON took");
ws_send(nc, message);
}
break;
case uSchedules:
{
DEBUG_TIMER_START(&tid);
char message[JSON_BUFFER_SIZE];
build_schedules_js(message, JSON_BUFFER_SIZE);
DEBUG_TIMER_STOP(tid, NET_LOG, "action_websocket_request() build_schedules_js took");
ws_send(nc, message);
}
break;
case uSetSchedules:
{
DEBUG_TIMER_START(&tid);
char message[JSON_BUFFER_SIZE];
save_schedules_js((char *)wm->data, wm->size, message, JSON_BUFFER_SIZE);
DEBUG_TIMER_STOP(tid, NET_LOG, "action_websocket_request() save_schedules_js took");
ws_send(nc, message);
}
case uBad:
default:
if (msg == NULL)
@ -1259,6 +1443,9 @@ void action_domoticz_mqtt_message(struct mg_connection *nc, struct mg_mqtt_messa
LOG(NET_LOG,LOG_NOTICE, "MQTT: DZ: received '%s' for '%s', IGNORING as we are programming light mode\n", (nvalue==DZ_OFF?"OFF":"ON"), _aqualink_data->aqbuttons[i].name);
} else {
LOG(NET_LOG,LOG_INFO, "MQTT: DZ: received '%s' for '%s', turning '%s'\n", (nvalue==DZ_OFF?"OFF":"ON"), _aqualink_data->aqbuttons[i].name,(nvalue==DZ_OFF?"OFF":"ON"));
//create_panel_request(NET_DZMQTT, i, (nvalue == DZ_OFF?0:1), false);
panel_device_request(_aqualink_data, ON_OFF, i, (nvalue == DZ_OFF?0:1), NET_DZMQTT);
/*
#ifdef AQ_PDA
if (isPDA_PANEL) {
char msg[PTHREAD_ARG];
@ -1269,6 +1456,7 @@ void action_domoticz_mqtt_message(struct mg_connection *nc, struct mg_mqtt_messa
{
aq_send_cmd(_aqualink_data->aqbuttons[i].code);
}
*/
}
}
break; // no need to continue in for loop, we found button.
@ -1321,6 +1509,11 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
if (is_websocket(nc)) {
_aqualink_data->open_websockets--;
LOG(NET_LOG,LOG_DEBUG, "-- Websocket left\n");
// Need something below to detect is_websocket_simulator() and turn off aq_data.simulate_panel
if (is_websocket_simulator(nc)) {
_aqualink_data->simulate_panel = false;
LOG(NET_LOG,LOG_DEBUG, "Stoped Simulator Mode\n");
}
} else if (is_mqtt(nc)) {
LOG(NET_LOG,LOG_WARNING, "MQTT Connection closed\n");
_mqtt_exit_flag = true;
@ -1403,18 +1596,13 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
LOG(NET_LOG,LOG_DEBUG, "MQTT: received (msg_id: %d), looks like my own message, ignoring\n", mqtt_msg->message_id);
}
// NSF Need to change strlen to a global so it's not executed every time we check a topic
if ((_aqconfig_.mqtt_aq_topic != NULL) &&
(mqtt_msg->topic.len >= strlen(_aqconfig_.mqtt_aq_topic)) &&
(strncmp(mqtt_msg->topic.p, _aqconfig_.mqtt_aq_topic,
strlen(_aqconfig_.mqtt_aq_topic)) == 0))
if (_aqconfig_.mqtt_aq_topic != NULL && strncmp(mqtt_msg->topic.p, _aqconfig_.mqtt_aq_topic, strlen(_aqconfig_.mqtt_aq_topic)) == 0)
{
DEBUG_TIMER_START(&tid);
action_mqtt_message(nc, mqtt_msg);
DEBUG_TIMER_STOP(tid, NET_LOG, "MQTT Request action_mqtt_message() took");
}
if ((_aqconfig_.mqtt_dz_sub_topic != NULL) &&
(mqtt_msg->topic.len >= strlen(_aqconfig_.mqtt_dz_sub_topic)) &&
(strncmp(mqtt_msg->topic.p, _aqconfig_.mqtt_dz_sub_topic, strlen(_aqconfig_.mqtt_dz_sub_topic)) == 0)) {
if (_aqconfig_.mqtt_dz_sub_topic != NULL && strncmp(mqtt_msg->topic.p, _aqconfig_.mqtt_dz_sub_topic, strlen(_aqconfig_.mqtt_dz_sub_topic)) == 0) {
action_domoticz_mqtt_message(nc, mqtt_msg);
}
break;
@ -1431,6 +1619,9 @@ void reset_last_mqtt_status()
}
_last_mqtt_aqualinkdata.ar_swg_device_status = SWG_STATUS_UNKNOWN;
_last_mqtt_aqualinkdata.swg_led_state = LED_S_UNKNOWN;
_last_mqtt_aqualinkdata.air_temp = TEMP_REFRESH;
_last_mqtt_aqualinkdata.pool_temp = TEMP_REFRESH;
_last_mqtt_aqualinkdata.spa_temp = TEMP_REFRESH;
//_last_mqtt_aqualinkdata.sw .ar_swg_device_status = SWG_STATUS_UNKNOWN;
_last_mqtt_aqualinkdata.battery = -1;
_last_mqtt_aqualinkdata.frz_protect_state = -1;
@ -1632,9 +1823,32 @@ void stop_net_services(struct mg_mgr *mgr) {
#ifdef INCLUDE_V1_API
/* OLD Functions to be deprecated */
int getTempforMeteohub(char *buffer)
{
int length = 0;
if (_aqualink_data->air_temp != TEMP_UNKNOWN)
length += sprintf(buffer+length, "t0 %d\n",(int)degFtoC(_aqualink_data->air_temp)*10);
else
length += sprintf(buffer+length, "t0 \n");
if (_aqualink_data->pool_temp != TEMP_UNKNOWN)
length += sprintf(buffer+length, "t1 %d\n",(int)degFtoC(_aqualink_data->pool_temp)*10);
else
length += sprintf(buffer+length, "t1 \n");
return strlen(buffer);
}
/* Leave the old API / web function intact until we deprecate */
void OLD_action_web_request(struct mg_connection *nc, struct http_message *http_msg) {
@ -1940,9 +2154,9 @@ void OLD_action_websocket_request(struct mg_connection *nc, struct websocket_mes
//build_device_JSON(_aqualink_data, _aqconfig_.light_programming_button, message, JSON_LABEL_SIZE*10);
//ws_send(nc, message);
} else if (strcmp(request.first.key, "mode") == 0) {
if (strcmp(request.first.value, "onetouchraw") == 0) {
set_websocket_RSraw(nc);
}
//if (strcmp(request.first.value, "onetouchraw") == 0) {
// set_websocket_RSraw(nc);
// }
} else if (strcmp(request.first.key, "command") == 0) {
_aqualink_data->simulate_panel = false;
if (strcmp(request.first.value, "GET_AUX_LABELS") == 0) {
@ -2016,7 +2230,8 @@ void OLD_action_websocket_request(struct mg_connection *nc, struct websocket_mes
for (i = 0; i < _aqualink_data->total_buttons; i++) {
// NSF I could pull the text here for the real light color name. 4th value in json
if (strcmp(request.third.value, _aqualink_data->aqbuttons[i].name) == 0) {
set_light_mode(request.second.value, i);
//set_light_mode(request.second.value, i);
panel_device_request(_aqualink_data, LIGHT_MODE, i, request.second.value, NET_API);
break;
}
}
@ -2045,3 +2260,5 @@ void OLD_action_websocket_request(struct mg_connection *nc, struct websocket_mes
}
}
}
#endif // INCLUDE_V1_API

View File

@ -22,4 +22,5 @@ void broadcast_aqualinkstate(struct mg_connection *nc);
void broadcast_aqualinkstate_error(struct mg_connection *nc, char *msg);
#endif // WEB_SERVER_H_

BIN
net_services.o Normal file

Binary file not shown.

View File

@ -60,7 +60,8 @@ void print_onetouch_menu()
if (_ot_hlightcharindexstart > -1) {
LOG(ONET_LOG,LOG_INFO, "OneTouch Menu highlighted line = %d, '%s' hligh-char(s) '%.*s'\n",
_ot_hlightindex,_menu[_ot_hlightindex],
(_ot_hlightcharindexstart - _ot_hlightcharindexstop + 1), &_menu[_ot_hlightindex][_ot_hlightcharindexstart]);
(_ot_hlightcharindexstop - _ot_hlightcharindexstart + 1),
&_menu[_ot_hlightindex][_ot_hlightcharindexstart]);
} else if (_ot_hlightindex > -1) {
LOG(ONET_LOG,LOG_INFO, "OneTouch Menu highlighted line = %d = %s\n",_ot_hlightindex,_menu[_ot_hlightindex]);
}
@ -71,6 +72,11 @@ int onetouch_menu_hlightindex()
return _ot_hlightindex;
}
int onetouch_menu_hlightcharindex()
{
return _ot_hlightcharindexstart;
}
char *onetouch_menu_hlight()
{
return onetouch_menu_line(_ot_hlightindex);
@ -180,14 +186,18 @@ bool process_onetouch_menu_packet(struct aqualinkdata *aq_data, unsigned char* p
_ot_hlightindex = packet[4];
_ot_hlightcharindexstart = packet[5];
_ot_hlightcharindexstop = packet[6];
//printf("**** hlight Line %d, start %d, stop %d\n",_ot_hlightindex, _ot_hlightcharindexstart, _ot_hlightcharindexstop);
} else {
//printf("**** hlight Packet 4 = %d\n", packet[4]);
_ot_hlightindex = -1;
_ot_hlightcharindexstart = -1;
_ot_hlightcharindexstart = -1;
}
LOG(ONET_LOG,LOG_DEBUG, "OneTouch Menu highlighted line = %d, '%s' chars '%.*s'\n",
_ot_hlightindex,_menu[_ot_hlightindex],
(_ot_hlightcharindexstart - _ot_hlightcharindexstop + 1), &_menu[_ot_hlightindex][_ot_hlightcharindexstart]);
_ot_hlightindex,
_menu[_ot_hlightindex],
(_ot_hlightcharindexstop - _ot_hlightcharindexstart) + 1,
&_menu[_ot_hlightindex][_ot_hlightcharindexstart]);
//if (getLogLevel() >= LOG_DEBUG){print_onetouch_menu();}
break;
case CMD_PDA_SHIFTLINES:

View File

@ -65,6 +65,7 @@ unsigned char *last_onetouch_packet();
int thread_kick_type();
int onetouch_menu_hlightindex();
int onetouch_menu_hlightcharindex();
char *onetouch_menu_hlight();
char *onetouch_menu_line(int index);
char *onetouch_menu_hlightchars(int *len);

BIN
onetouch.o Normal file

Binary file not shown.

View File

@ -865,6 +865,42 @@ void *set_aqualink_onetouch_freezeprotect( void *ptr )
return ptr;
}
bool set_numeric_value(struct aqualinkdata *aq_data, int val) {
int len;
int cval;
int diff;
int i;
unsigned char direction = KEY_ONET_UP;
cval = atoi(onetouch_menu_hlightchars(&len));
diff = val - cval;
LOG(ONET_LOG,LOG_DEBUG, "** OneTouch set value val=%d cval=%d diff=%d \n", val,cval,diff);
if (diff > 0) {
direction = KEY_ONET_UP;
} else if (diff < 0) {
direction = KEY_ONET_DOWN;
diff=-diff;
}
if (diff > 0) {
for (i=0; i < diff; i++) {
send_ot_cmd(direction);
waitfor_ot_queue2empty();
}
waitForOT_MessageTypes(aq_data,CMD_PDA_HIGHLIGHTCHARS,0x00,5); // CMD_PDA_0x04 is just a packer.
cval = atoi(onetouch_menu_hlightchars(&len));
if ( val != cval ) {
LOG(ONET_LOG,LOG_ERR, "** OneTouch set value failed, val=%d cval=%d\n", val,cval);
return false;
}
}
return true;
}
void *set_aqualink_onetouch_time( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
@ -880,7 +916,7 @@ void *set_aqualink_onetouch_time( void *ptr )
if ( !goto_onetouch_menu(aq_data, OTM_SET_TIME) ){
LOG(ONET_LOG,LOG_ERR, "OneTouch device programmer failed to get time menu\n");
} else {
/*
// MM/DD/YY MON Just change MM/DD/YY
// H:MM AM Have to cycle H to get AM/PM, just one digit
time_t now = time(0); // get time now
@ -888,23 +924,72 @@ void *set_aqualink_onetouch_time( void *ptr )
int hour;
char ap;
result->tm_mday // day of month starts at 1
result->tm_mon // Month started at 0
result->tm_min // Min
if (result->tm_hour == 0) //12 AM ie midnight
hour = 12
if (result->tm_hour == 0) { //12 AM ie midnight
hour = 12;
ap = 'A';
else if (result->tm_hour == 12) // 12 PM
hour = 12
} else if (result->tm_hour == 12) {// 12 PM
hour = 12;
ap = 'P';
else if (result->tm_hour <= 11)
hour = result->tm_hour
} else if (result->tm_hour <= 11) {
hour = result->tm_hour;
ap = 'A';
else // Must be 13 or more
} else {// Must be 13 or more
hour = result->tm_hour - 12;
ap = 'P';
*/
}
LOG(ONET_LOG,LOG_DEBUG, "OneTouch set time to :-\n");
LOG(ONET_LOG,LOG_DEBUG, " %d/%d/%d\n",(result->tm_mon + 1), result->tm_mday, result->tm_year % 100 );
LOG(ONET_LOG,LOG_DEBUG, " %d:%d %c\n",hour,result->tm_min, ap);
//Line 3 startchar 2 for Month
//Line 3 startchar 5 for Day
//Line 3 startchat 8 for year
while ( (onetouch_menu_hlightindex() != 3) || (onetouch_menu_hlightcharindex() != 2) )
waitForOT_MessageTypes(aq_data,CMD_PDA_HIGHLIGHTCHARS,0x00,15); // CMD_PDA_0x04 is just a packer.
printf("*** Setting month. line=%d, char=%d\n",onetouch_menu_hlightindex(), onetouch_menu_hlightcharindex());
set_numeric_value(aq_data, (result->tm_mon + 1) );
send_ot_cmd(KEY_ONET_SELECT);
waitfor_ot_queue2empty();
while ( (onetouch_menu_hlightindex() != 3) || (onetouch_menu_hlightcharindex() != 5) )
waitForOT_MessageTypes(aq_data,CMD_PDA_HIGHLIGHTCHARS,0x00,15); // CMD_PDA_0x04 is just a packer.
printf("*** Setting day. line=%d, char=%d\n",onetouch_menu_hlightindex(), onetouch_menu_hlightcharindex());
set_numeric_value(aq_data, result->tm_mday );
send_ot_cmd(KEY_ONET_SELECT);
waitfor_ot_queue2empty();
while ( (onetouch_menu_hlightindex() != 3) || (onetouch_menu_hlightcharindex() != 8) )
waitForOT_MessageTypes(aq_data,CMD_PDA_HIGHLIGHTCHARS,0x00,15); // CMD_PDA_0x04 is just a packer.
printf("*** Setting year. line=%d, char=%d\n",onetouch_menu_hlightindex(), onetouch_menu_hlightcharindex());
set_numeric_value(aq_data, result->tm_year % 100 );
send_ot_cmd(KEY_ONET_SELECT);
waitfor_ot_queue2empty();
// highlightline 4 char 3 or 4 for Hour
// highlightline 4 char 5 or 6 for Min
// highlightline 4 char 9
waitForOT_MessageTypes(aq_data,CMD_PDA_HIGHLIGHTCHARS,0x00,15); // CMD_PDA_0x04 is just a packer.
// Need to check AM/PM here
set_numeric_value(aq_data, hour );
send_ot_cmd(KEY_ONET_SELECT);
waitfor_ot_queue2empty();
waitForOT_MessageTypes(aq_data,CMD_PDA_HIGHLIGHTCHARS,0x00,15); // CMD_PDA_0x04 is just a packer.
set_numeric_value(aq_data, result->tm_min );
send_ot_cmd(KEY_ONET_SELECT);
waitfor_ot_queue2empty();
send_ot_cmd(KEY_ONET_SELECT);
waitfor_ot_queue2empty();
send_ot_cmd(KEY_ONET_BACK);
waitfor_ot_queue2empty();
}
if (! goto_onetouch_menu(aq_data, OTM_SYSTEM) ){

BIN
onetouch_aq_programmer.o Normal file

Binary file not shown.

633
out.dump Normal file
View File

@ -0,0 +1,633 @@
AqualinkD serial_logger V1.2
Notice: Logging serial information!
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x42|0x00|0x54|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x08|0x03|0x00|0x20|0x20|0x31|0x30|0x2f|0x32|0x31|0x2f|0x31|0x39|0x20|0x4d|0x4f|0x4e|0x20|0x20|0x33|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x43|0x00|0x55|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x10|0x00|0x22|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x18|0x00|0x2a|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x20|0x00|0x32|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x08|0x03|0x00|0x20|0x20|0x20|0x20|0x31|0x30|0x3a|0x30|0x39|0x20|0x41|0x4d|0x20|0x20|0x20|0x20|0xcf|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x21|0x00|0x33|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x22|0x00|0x34|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x23|0x00|0x35|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x28|0x00|0x3a|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x08|0x03|0x00|0x20|0x41|0x49|0x52|0x20|0x54|0x45|0x4d|0x50|0x20|0x38|0x31|0xdf|0x46|0x20|0x20|0x5d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x29|0x00|0x3b|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x2a|0x00|0x3c|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x2b|0x00|0x3d|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x08|0x03|0x00|0x50|0x4f|0x4f|0x4c|0x20|0x54|0x45|0x4d|0x50|0x20|0x20|0x38|0x31|0xdf|0x46|0x20|0x9b|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x38|0x00|0x4a|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x39|0x00|0x4b|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x3a|0x00|0x4c|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x3b|0x00|0x4d|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x08|0x03|0x00|0x20|0x20|0x41|0x71|0x75|0x61|0x50|0x75|0x72|0x65|0x20|0x33|0x35|0x25|0x20|0x20|0x6e|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x48|0x00|0x5a|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x58|0x00|0x6a|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x09|0x00|0x1b|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x0a|0x00|0x1c|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x08|0x03|0x00|0x20|0x53|0x41|0x4c|0x54|0x20|0x33|0x31|0x30|0x30|0x20|0x50|0x50|0x4d|0x20|0x20|0xa2|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x0b|0x00|0x1d|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x40|0x00|0x52|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x41|0x00|0x53|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x42|0x00|0x54|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x08|0x03|0x00|0x20|0x20|0x31|0x30|0x2f|0x32|0x31|0x2f|0x31|0x39|0x20|0x4d|0x4f|0x4e|0x20|0x20|0x33|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x43|0x00|0x55|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x10|0x00|0x22|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x18|0x00|0x2a|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x20|0x00|0x32|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x08|0x03|0x00|0x20|0x20|0x20|0x20|0x31|0x30|0x3a|0x31|0x30|0x20|0x41|0x4d|0x20|0x20|0x20|0x20|0xc7|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x21|0x00|0x33|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x22|0x00|0x34|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x23|0x00|0x35|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x28|0x00|0x3a|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x08|0x03|0x00|0x4a|0x41|0x4e|0x44|0x59|0x20|0x41|0x71|0x75|0x61|0x4c|0x69|0x6e|0x6b|0x52|0x53|0x6e|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x29|0x00|0x3b|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x2a|0x00|0x3c|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x2b|0x00|0x3d|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x38|0x00|0x4a|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x08|0x03|0x00|0x20|0x41|0x49|0x52|0x20|0x54|0x45|0x4d|0x50|0x20|0x38|0x31|0xdf|0x46|0x20|0x20|0x5d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x39|0x00|0x4b|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x3a|0x00|0x4c|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x3b|0x00|0x4d|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x48|0x00|0x5a|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x08|0x03|0x00|0x50|0x4f|0x4f|0x4c|0x20|0x54|0x45|0x4d|0x50|0x20|0x20|0x38|0x31|0xdf|0x46|0x20|0x9b|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x58|0x00|0x6a|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x09|0x00|0x1b|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x0a|0x00|0x1c|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x08|0x03|0x00|0x20|0x20|0x41|0x71|0x75|0x61|0x50|0x75|0x72|0x65|0x20|0x33|0x35|0x25|0x20|0x20|0x6e|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x0b|0x00|0x1d|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x40|0x00|0x52|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x41|0x00|0x53|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x42|0x00|0x54|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x08|0x03|0x00|0x20|0x53|0x41|0x4c|0x54|0x20|0x33|0x31|0x30|0x30|0x20|0x50|0x50|0x4d|0x20|0x20|0xa2|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x43|0x00|0x55|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x10|0x00|0x22|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x18|0x00|0x2a|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x20|0x00|0x32|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x08|0x03|0x00|0x20|0x20|0x31|0x30|0x2f|0x32|0x31|0x2f|0x31|0x39|0x20|0x4d|0x4f|0x4e|0x20|0x20|0x33|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x21|0x00|0x33|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x22|0x00|0x34|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x23|0x00|0x35|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x28|0x00|0x3a|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x08|0x03|0x00|0x20|0x20|0x20|0x20|0x31|0x30|0x3a|0x31|0x30|0x20|0x41|0x4d|0x20|0x20|0x20|0x20|0xc7|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x29|0x00|0x3b|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x2a|0x00|0x3c|0x10|0x03|
0x10|0x02|0x08|0x02|0x00|0x11|0x00|0x00|0x00|0x2d|0x10|0x03|
0x10|0x02|0x00|0x01|0x00|0x00|0x13|0x10|0x03|
0x10|0x02|0x50|0x11|0x23|0x96|0x10|0x03|
0x10|0x02|0x00|0x16|0x1f|0x00|0x00|0x00|0x47|0x10|0x03|
0x10|0x02|0x60|0x00|0x72|0x10|0x03|
Debug:
Notice: Jandy ID's found
Notice: ID 0x08 is in use <-- RS Keypad
Notice: ID 0x50 is in use <-- Salt Water Generator (Aquarite mode)
Notice: ID 0x42 is not used <-- can use for Aqualinkd (Extended Device ID)
Notice: ID 0x60 is not used <-- can use for Aqualinkd (PDA mode only)
Notice: ID 0x43 is not used <-- can use for Aqualinkd (Extended Device ID)
Notice: ID 0x10 is not used
Notice: ID 0x18 is not used
Notice: ID 0x20 is not used
Notice: ID 0x21 is not used
Notice: ID 0x22 is not used
Notice: ID 0x23 is not used
Notice: ID 0x28 is not used
Notice: ID 0x29 is not used
Notice: ID 0x2a is not used
Notice: ID 0x2b is not used
Notice: ID 0x38 is not used
Notice: ID 0x39 is not used
Notice: ID 0x3a is not used
Notice: ID 0x3b is not used
Notice: ID 0x48 is not used
Notice: ID 0x58 is not used
Notice: ID 0x09 is not used <-- can use for Aqualinkd
Notice: ID 0x0a is not used <-- can use for Aqualinkd
Notice: ID 0x0b is not used <-- can use for Aqualinkd
Notice: ID 0x40 is not used <-- can use for Aqualinkd (Extended Device ID)
Notice: ID 0x41 is not used <-- can use for Aqualinkd (Extended Device ID)
Notice:

64
out.log Normal file
View File

@ -0,0 +1,64 @@
Notice: AqualinkD: Aqualink Daemon v2.2.1
Notice: AqualinkD: Panel set to RS-8 Combo Pool/Spa
Notice: AqualinkD: Config log_level = 5
Notice: AqualinkD: Config device_id = 0x0a
Notice: AqualinkD: Config rssa_device_id = 0x00
Notice: AqualinkD: Config extra_device_id = 0x00
Notice: AqualinkD: Config extra_device_prog = YES
Notice: AqualinkD: Config serial_port = /dev/ttyUSB0
Notice: AqualinkD: Config socket_port = 88
Notice: AqualinkD: Config web_directory = /nas/data/Development/Raspberry/AqualinkD/web
Notice: AqualinkD: Config read_all_devices = NO
Notice: AqualinkD: Config use_aux_labels = NO
Notice: AqualinkD: Config override frz prot = NO
Notice: AqualinkD: Config mqtt_server = trident:1883
Notice: AqualinkD: Config mqtt_dz_sub_topic = (null)
Notice: AqualinkD: Config mqtt_dz_pub_topic = (null)
Notice: AqualinkD: Config mqtt_aq_topic = aqualinkd-test
Notice: AqualinkD: Config mqtt_user = (null)
Notice: AqualinkD: Config mqtt_passwd = (null)
Notice: AqualinkD: Config mqtt_ID = aqualinkd_b827ebba
Notice: AqualinkD: Config idx water temp = -999
Notice: AqualinkD: Config idx pool temp = -999
Notice: AqualinkD: Config idx spa temp = -999
Notice: AqualinkD: Config idx SWG Percent = 0
Notice: AqualinkD: Config idx SWG PPM = 0
Notice: AqualinkD: Config force SWG = YES
Notice: AqualinkD: Config deamonize = NO
Notice: AqualinkD: Config log_file = (null)
Notice: AqualinkD: Config light_pgm_mode = 0.00
Notice: AqualinkD: Debug RS485 protocol = NO
Notice: AqualinkD: Read Pentair Packets = NO
Notice: AqualinkD: Display warnings in web = YES
Notice: AqualinkD: Keep panle time in sync = YES
Notice: AqualinkD: Ignore SWG 0 msg count = 20
Notice: AqualinkD: Serial Read Ahead Write = YES
Notice: AqualinkD: RS Poll Speed = -1
Notice: AqualinkD: Thread Network Services = YES
Warning: AqualinkD: Serial Read Ahead Write is not valid when using Negative RS Poll Speed, turning Serial Read Ahead Write off
Warning: AqualinkD: **************************************************************************************************
Warning: AqualinkD: * RS Poll Speed of -1 is expermental, this put's USB in complete blocking mode *
Warning: AqualinkD: * if USB becomes unstable / connection to panel lost, AqualinkD may need to be killed manually *
Warning: AqualinkD: * eg:- sudo kill -9 <list of aqualinkd process ID's> *
Warning: AqualinkD: **************************************************************************************************
Notice: AqualinkD: Config BTN Filter_Pump = label Filter Pump | VSP ID 0x78 | PMP ID 1 |
Notice: AqualinkD: Config BTN Spa_Mode = label Spa Mode |
Notice: AqualinkD: Config BTN Aux_1 = label Aux 1 | VSP ID 0x61 | PMP ID 3 |
Notice: AqualinkD: Config BTN Aux_2 = label Aux 2 | VSP ID 0x60 | PMP ID 2 |
Notice: AqualinkD: Config BTN Aux_3 = label Aux 3 |
Notice: AqualinkD: Config BTN Aux_4 = label Aux 4 | Light Progm | CTYPE 1 |
Notice: AqualinkD: Config BTN Aux_5 = label Aux 5 |
Notice: AqualinkD: Config BTN Aux_6 = label Aux 6 |
Notice: AqualinkD: Config BTN Aux_7 = label Aux 7 |
Notice: AqualinkD: Config BTN Pool_Heater = label Pool Heater |
Notice: AqualinkD: Config BTN Spa_Heater = label Spa Heater |
Notice: AqualinkD: Config BTN Solar_Heater = label Solar Heater |
Notice: NetService:Starting network services thread
Notice: NetService:Starting web server on port 88
Notice: NetService:Starting MQTT client to trident:1883
Notice: AqualinkD: Listening to Aqualink RS8 on serial port: /dev/ttyUSB0
Notice: AqualinkD: Waiting for Control Panel probe
Notice: AqualinkD: Stopping!
Warning: RS Serial: Read error: 9 - Bad file descriptor
Notice: AqualinkD: Starting communication with Control Panel
Notice: AqualinkD: Exit!

View File

@ -6,19 +6,28 @@
#include "packetLogger.h"
#include "aq_serial.h"
#include "utils.h"
#include "config.h"
static FILE *_packetLogFile = NULL;
static FILE *_byteLogFile = NULL;
static bool _logfile_raw = false;
static bool _logfile_packets = false;
//static bool _includePentair = false;
static FILE *_packetLogFile = NULL;
static FILE *_byteLogFile = NULL;
static bool _log2file = false;
static bool _includePentair = false;
void _logPacket(int16_t from, unsigned char *packet_buffer, int packet_length, bool error, bool force, bool is_read);
int _beautifyPacket(char *buff, unsigned char *packet_buffer, int packet_length, bool error, bool is_read);
void _logPacket(int16_t from, unsigned char *packet_buffer, int packet_length, bool error, bool force);
int _beautifyPacket(char *buff, unsigned char *packet_buffer, int packet_length, bool error);
//void startPacketLogger(bool debug_RSProtocol_packets) {
void startPacketLogger() {
// Make local copy of variables so we can turn on/off as needed.
_logfile_raw = _aqconfig_.log_raw_bytes;
_logfile_packets = _aqconfig_.log_protocol_packets;
}
void startPacketLogger(bool debug_RSProtocol_packets, bool read_pentair_packets) {
_log2file = debug_RSProtocol_packets;
_includePentair = read_pentair_packets;
void startPacketLogging(bool log_protocol_packets, bool log_raw_bytes)
{
_logfile_raw = log_raw_bytes;
_logfile_packets = log_protocol_packets;
}
void stopPacketLogger() {
@ -27,10 +36,16 @@ void stopPacketLogger() {
if (_byteLogFile != NULL)
fclose(_byteLogFile);
_logfile_raw = false;
_logfile_packets = false;
}
// Log passed packets
void writePacketLog(char *buffer) {
if (!_logfile_packets)
return;
if (_packetLogFile == NULL)
_packetLogFile = fopen(RS485LOGFILE, "w");
@ -39,45 +54,57 @@ void writePacketLog(char *buffer) {
}
}
// Log Raw Bytes
void logPacketByte(unsigned char *byte)
{
if (!_logfile_raw)
return;
char buff[10];
if (_byteLogFile == NULL)
_byteLogFile = fopen(RS485BYTELOGFILE, "w");
if (_byteLogFile != NULL) {
sprintf(buff, "0x%02hhx|",*byte);
fputs( buff, _byteLogFile);
}
//}
}
/*
void logPacket(unsigned char *packet_buffer, int packet_length) {
_logPacket(RSSD_LOG, packet_buffer, packet_length, false, false);
}
*/
void logPacketRead(unsigned char *packet_buffer, int packet_length) {
_logPacket(RSSD_LOG, packet_buffer, packet_length, false, false, true);
}
void logPacketWrite(unsigned char *packet_buffer, int packet_length) {
_logPacket(RSSD_LOG, packet_buffer, packet_length, false, false, false);
}
void logPacketError(unsigned char *packet_buffer, int packet_length) {
_logPacket(RSSD_LOG, packet_buffer, packet_length, true, false);
_logPacket(RSSD_LOG, packet_buffer, packet_length, true, false, true);
}
void debuglogPacket(int16_t from, unsigned char *packet_buffer, int packet_length) {
void debuglogPacket(int16_t from, unsigned char *packet_buffer, int packet_length, bool is_read) {
if ( getLogLevel(from) >= LOG_DEBUG )
_logPacket(from, packet_buffer, packet_length, false, true);
_logPacket(from, packet_buffer, packet_length, false, true, is_read);
}
void _logPacket(int16_t from, unsigned char *packet_buffer, int packet_length, bool error, bool force)
void _logPacket(int16_t from, unsigned char *packet_buffer, int packet_length, bool error, bool force, bool is_read)
{
// No point in continuing if loglevel is < debug_serial and not writing to file
if ( force == false && error == false && getLogLevel(from) < LOG_DEBUG_SERIAL && _log2file == false)
if ( force == false && error == false && getLogLevel(from) < LOG_DEBUG_SERIAL /*&& _logfile_raw == false*/ && _logfile_packets == false) {
return;
char buff[1000];
//int i = 0;
//int cnt = 0;
_beautifyPacket(buff, packet_buffer, packet_length, error);
/*
if (_includePentair) {
cnt = sprintf(buff, "%s%8.8s Packet | HEX: ",(error?"BAD PACKET ":""),getProtocolType(packet_buffer)==JANDY?"Jandy":"Pentair");
} else {
cnt = sprintf(buff, "%sTo 0x%02hhx of type %8.8s | HEX: ",(error?"BAD PACKET ":""), packet_buffer[PKT_DEST], get_packet_type(packet_buffer, packet_length));
}
char buff[1000];
for (i = 0; i < packet_length; i++)
cnt += sprintf(buff + cnt, "0x%02hhx|", packet_buffer[i]);
_beautifyPacket(buff, packet_buffer, packet_length, error, is_read);
cnt += sprintf(buff + cnt, "\n");
*/
if (_log2file)
if (_logfile_packets)
writePacketLog(buff);
if (error == true)
LOG(from,LOG_WARNING, "%s", buff);
else {
@ -88,19 +115,20 @@ void _logPacket(int16_t from, unsigned char *packet_buffer, int packet_length, b
}
}
int beautifyPacket(char *buff, unsigned char *packet_buffer, int packet_length)
int beautifyPacket(char *buff, unsigned char *packet_buffer, int packet_length, bool is_read)
{
return _beautifyPacket(buff, packet_buffer, packet_length, false);
return _beautifyPacket(buff, packet_buffer, packet_length, false, is_read);
}
int _beautifyPacket(char *buff, unsigned char *packet_buffer, int packet_length, bool error)
int _beautifyPacket(char *buff, unsigned char *packet_buffer, int packet_length, bool error, bool is_read)
{
int i = 0;
int cnt = 0;
if (_includePentair) {
cnt = sprintf(buff, "%s%8.8s Packet | HEX: ",(error?"BAD PACKET ":""),getProtocolType(packet_buffer)==PENTAIR?"Pentair":"Jandy");
if (getProtocolType(packet_buffer)==PENTAIR) {
// Listing Jandy below if redundant. need to clean this up.
cnt = sprintf(buff, "%5.5s %s%8.8s Packet | HEX: ",(is_read?"Read":"Write"),(error?"BAD PACKET ":""),getProtocolType(packet_buffer)==PENTAIR?"Pentair":"Jandy");
} else {
cnt = sprintf(buff, "%sTo 0x%02hhx of type %16.16s | HEX: ",(error?"BAD PACKET ":""), packet_buffer[PKT_DEST], get_packet_type(packet_buffer, packet_length));
cnt = sprintf(buff, "%5.5s %sTo 0x%02hhx of type %16.16s | HEX: ",(is_read?"Read":"Write"),(error?"BAD PACKET ":""), packet_buffer[PKT_DEST], get_packet_type(packet_buffer, packet_length));
}
for (i = 0; i < packet_length; i++)
@ -111,23 +139,3 @@ int _beautifyPacket(char *buff, unsigned char *packet_buffer, int packet_length,
return cnt;
}
//#define RAW_BUFFER_SIZE 100
// Log Raw Bytes
void logPacketByte(unsigned char *byte)
{
char buff[10];
//static int _length = 0;
//static unsigned char _bytes[RAW_BUFFER_SIZE];
//_bytes[_length++] = byte;
//if (_length >= RAW_BUFFER_SIZE) {
if (_byteLogFile == NULL)
_byteLogFile = fopen(RS485BYTELOGFILE, "w");
if (_byteLogFile != NULL) {
sprintf(buff, "0x%02hhx|",*byte);
fputs( buff, _byteLogFile);
}
//}
}

View File

@ -7,16 +7,21 @@
#define RS485LOGFILE "/tmp/RS485.log"
#define RS485BYTELOGFILE "/tmp/RS485raw.log"
void startPacketLogger(bool debug_RSProtocol_packets, bool read_pentair_packets);
void startPacketLogger(); // use what ever config has
void startPacketLogging(bool debug_protocol_packets, bool debug_raw_bytes); // Set custom options
void stopPacketLogger();
//void logPacket(unsigned char *packet_buffer, int packet_length, bool checksumerror);
void logPacket(unsigned char *packet_buffer, int packet_length);
//void logPacket(unsigned char *packet_buffer, int packet_length);
void logPacketRead(unsigned char *packet_buffer, int packet_length);
void logPacketWrite(unsigned char *packet_buffer, int packet_length);
void logPacketError(unsigned char *packet_buffer, int packet_length);
void logPacketByte(unsigned char *byte);
// Only use for manual debugging
//void debuglogPacket(unsigned char *packet_buffer, int packet_length);
void debuglogPacket(int16_t from, unsigned char *packet_buffer, int packet_length);
int beautifyPacket(char *buff, unsigned char *packet_buffer, int packet_length);
void debuglogPacket(int16_t from, unsigned char *packet_buffer, int packet_length, bool is_read);
int beautifyPacket(char *buff, unsigned char *packet_buffer, int packet_length, bool is_read);
#endif //PACKETLOGGER_H_

BIN
packetLogger.o Normal file

Binary file not shown.

219
pda.c
View File

@ -20,6 +20,7 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include "aqualink.h"
@ -30,6 +31,10 @@
#include "packetLogger.h"
#include "devices_jandy.h"
// This needs to be tested on a real panel.
#define NEW_UPDATE_METHOD
// static struct aqualinkdata _aqualink_data;
static struct aqualinkdata *_aqualink_data;
static unsigned char _last_packet_type;
@ -131,7 +136,33 @@ void set_pda_led(struct aqualinkled *led, char state)
}
}
void pass_pda_equiptment_status_item(char *msg)
#ifdef NEW_UPDATE_METHOD
void equiptment_update_cycle(int eqID) {
// If you have a -1, it's a reset to clear / update information.
int i;
static uint32_t update_equiptment_bitmask = 0;
if (eqID == -1) {
LOG(PDA_LOG,LOG_DEBUG, "(not implimented) Start new equiptment cycle\n");
for (i=0; i < _aqualink_data->total_buttons; i++) {
if ((update_equiptment_bitmask & (1 << (i+1))) != (1 << (i+1))) {
if (_aqualink_data->aqbuttons[i].led->state != OFF) {
_aqualink_data->aqbuttons[i].led->state = OFF;
_aqualink_data->updated = true;
LOG(PDA_LOG,LOG_DEBUG, "(not implimented) Turn off equiptment id %d %s not seen in last cycle\n", i, _aqualink_data->aqbuttons[i].name);
}
}
}
update_equiptment_bitmask = 0;
} else {
update_equiptment_bitmask |= (1 << (eqID+1));
LOG(PDA_LOG,LOG_DEBUG, "(not implimented) Added equiptment id %d %s to updated cycle\n", eqID, _aqualink_data->aqbuttons[eqID].name);
}
}
#endif
void pass_pda_equiptment_status_item_OLD(char *msg)
{
static char *index;
int i;
@ -218,6 +249,9 @@ void pass_pda_equiptment_status_item(char *msg)
{
if (strcasecmp(msg, _aqualink_data->aqbuttons[i].label) == 0)
{
#ifdef NEW_UPDATE_METHOD
equiptment_update_cycle(i);
#endif
LOG(PDA_LOG,LOG_DEBUG, "*** Found Status for %s = '%.*s'\n", _aqualink_data->aqbuttons[i].label, AQ_MSGLEN, msg);
// It's on (or delayed) if it's listed here.
if (_aqualink_data->aqbuttons[i].led->state != FLASH)
@ -240,6 +274,7 @@ void process_pda_packet_msg_long_temp(const char *msg)
// 'AIR '
// ' 86` '
// 'AIR WATER' // In case of single device.
_aqualink_data->temp_units = FAHRENHEIT; // Force FAHRENHEIT
if (stristr(pda_m_line(1), "AIR") != NULL)
_aqualink_data->air_temp = atoi(msg);
@ -282,7 +317,12 @@ void process_pda_packet_msg_long_time(const char *msg)
strncpy(_aqualink_data->time, msg + 9, 7);
}
strncpy(_aqualink_data->date, msg + 5, 3);
// :TODO: NSF Come back and change the above to correctly check date and time in future
if (checkAqualinkTime() != true)
{
LOG(AQRS_LOG,LOG_NOTICE, "RS time is NOT accurate '%s %s', re-setting on controller!\n", _aqualink_data->time, _aqualink_data->date);
aq_programmer(AQ_SET_TIME, NULL, _aqualink_data);
}
}
void process_pda_packet_msg_long_equipment_control(const char *msg)
@ -371,39 +411,29 @@ void setSingleDeviceMode()
void process_pda_packet_msg_long_set_temp(const char *msg)
{
// message 'TEMP1 26`C '
// 'TEMP2 15`C '
LOG(PDA_LOG,LOG_DEBUG, "process_pda_packet_msg_long_set_temp\n");
if (stristr(msg, "POOL HEAT") != NULL)
{
_aqualink_data->pool_htr_set_point = atoi(msg + 10);
LOG(PDA_LOG,LOG_DEBUG, "pool_htr_set_point = %d\n", _aqualink_data->pool_htr_set_point);
if (_aqualink_data->temp_units == UNKNOWN)
setUnits(msg);
}
else if (stristr(msg, "SPA HEAT") != NULL)
{
_aqualink_data->spa_htr_set_point = atoi(msg + 10);
LOG(PDA_LOG,LOG_DEBUG, "spa_htr_set_point = %d\n", _aqualink_data->spa_htr_set_point);
if (_aqualink_data->temp_units == UNKNOWN)
setUnits(msg);
}
else if (stristr(msg, "TEMP1") != NULL)
{
setSingleDeviceMode();
_aqualink_data->pool_htr_set_point = atoi(msg + 10);
LOG(PDA_LOG,LOG_DEBUG, "pool_htr_set_point = %d\n", _aqualink_data->pool_htr_set_point);
if (_aqualink_data->temp_units == UNKNOWN)
setUnits(msg);
}
else if (stristr(msg, "TEMP2") != NULL)
{
setSingleDeviceMode();
_aqualink_data->spa_htr_set_point = atoi(msg + 10);
LOG(PDA_LOG,LOG_DEBUG, "spa_htr_set_point = %d\n", _aqualink_data->spa_htr_set_point);
if (_aqualink_data->temp_units == UNKNOWN)
setUnits(msg);
}
@ -437,13 +467,10 @@ void process_pda_packet_msg_long_pool_heat(const char *msg)
void process_pda_packet_msg_long_freeze_protect(const char *msg)
{
// message 'TEMP 3`C '
if (strncasecmp(msg, "TEMP ", 10) == 0)
{
_aqualink_data->frz_protect_set_point = atoi(msg + 10);
LOG(PDA_LOG,LOG_DEBUG, "frz_protect_set_point = %d\n", _aqualink_data->frz_protect_set_point);
if (_aqualink_data->temp_units == UNKNOWN)
setUnits(msg);
}
}
@ -492,6 +519,124 @@ void process_pda_packet_msg_long_unknown(const char *msg)
}
}
void process_pda_packet_msg_long_equiptment_status(const char *msg_line, int lineindex, bool reset)
{
//pass_pda_equiptment_status_item(msg);
if (reset) {
equiptment_update_cycle(-1);
LOG(PDA_LOG,LOG_DEBUG, "*************** Equiptment reset\n");
return;
}
LOG(PDA_LOG,LOG_DEBUG, "*************** Pass Equiptment msg %.16s\n", msg_line);
static char *index;
int i;
char *msg = (char *)msg_line;
while(isspace(*msg)) msg++;
// EQUIPMENT STATUS
//
// AquaPure 100%
// SALT 25500 PPM
// FILTER PUMP
// POOL HEAT
// SPA HEAT ENA
// EQUIPMENT STATUS
//
// FREEZE PROTECT
// AquaPure 100%
// SALT 25500 PPM
// CHECK AquaPure
// GENERAL FAULT
// FILTER PUMP
// CLEANER
//
// Equipment Status
//
// Intelliflo VS 1
// RPM: 1700
// Watts: 367
//
//
//
//
//
// Check message for status of device
// Loop through all buttons and match the PDA text.
if ((index = strcasestr(msg, "CHECK AquaPure")) != NULL)
{
LOG(PDA_LOG,LOG_DEBUG, "CHECK AquaPure\n");
}
else if ((index = strcasestr(msg, "FREEZE PROTECT")) != NULL)
{
_aqualink_data->frz_protect_state = ON;
}
else if ((index = strcasestr(msg, MSG_SWG_PCT)) != NULL)
{
changeSWGpercent(_aqualink_data, atoi(index + strlen(MSG_SWG_PCT)));
//_aqualink_data->swg_percent = atoi(index + strlen(MSG_SWG_PCT));
//if (_aqualink_data->ar_swg_status == SWG_STATUS_OFF) {_aqualink_data->ar_swg_status = SWG_STATUS_ON;}
LOG(PDA_LOG,LOG_DEBUG, "AquaPure = %d\n", _aqualink_data->swg_percent);
}
else if ((index = strcasestr(msg, MSG_SWG_PPM)) != NULL)
{
_aqualink_data->swg_ppm = atoi(index + strlen(MSG_SWG_PPM));
//if (_aqualink_data->ar_swg_status == SWG_STATUS_OFF) {_aqualink_data->ar_swg_status = SWG_STATUS_ON;}
LOG(PDA_LOG,LOG_DEBUG, "SALT = %d\n", _aqualink_data->swg_ppm);
}
else if ((index = strcasestr(msg, MSG_PMP_RPM)) != NULL)
{ // Default to pump 0, should check for correct pump
_aqualink_data->pumps[0].rpm = atoi(index + strlen(MSG_PMP_RPM));
LOG(PDA_LOG,LOG_DEBUG, "RPM = %d\n", _aqualink_data->pumps[0].rpm);
}
else if ((index = strcasestr(msg, MSG_PMP_WAT)) != NULL)
{ // Default to pump 0, should check for correct pump
_aqualink_data->pumps[0].watts = atoi(index + strlen(MSG_PMP_WAT));
LOG(PDA_LOG,LOG_DEBUG, "Watts = %d\n", _aqualink_data->pumps[0].watts);
}
else
{
char labelBuff[AQ_MSGLEN + 2];
strncpy(labelBuff, msg, AQ_MSGLEN + 1);
msg = stripwhitespace(labelBuff);
if (strcasecmp(msg, "POOL HEAT ENA") == 0)
{
_aqualink_data->aqbuttons[_aqualink_data->pool_heater_index].led->state = ENABLE;
}
else if (strcasecmp(msg, "SPA HEAT ENA") == 0)
{
_aqualink_data->aqbuttons[_aqualink_data->spa_heater_index].led->state = ENABLE;
}
else
{
for (i = 0; i < _aqualink_data->total_buttons; i++)
{
if (strcasecmp(msg, _aqualink_data->aqbuttons[i].label) == 0)
{
#ifdef NEW_UPDATE_METHOD
equiptment_update_cycle(i);
#endif
LOG(PDA_LOG,LOG_DEBUG, "*** Found Status for %s = '%.*s'\n", _aqualink_data->aqbuttons[i].label, AQ_MSGLEN, msg);
// It's on (or delayed) if it's listed here.
if (_aqualink_data->aqbuttons[i].led->state != FLASH)
{
_aqualink_data->aqbuttons[i].led->state = ON;
}
break;
}
}
}
}
}
void process_pda_packet_msg_long_level_aux_device(const char *msg)
{
#ifdef BETA_PDA_AUTOLABEL
@ -557,13 +702,15 @@ void process_pda_freeze_protect_devices()
bool process_pda_packet(unsigned char *packet, int length)
{
bool rtn = true;
int i;
//int i;
char *msg;
int index = -1;
static bool equiptment_updated = false;
//static bool init = false;
if (getLogLevel(PDA_LOG) == LOG_DEBUG) {
char buff[1024];
beautifyPacket(buff, packet, length);
beautifyPacket(buff, packet, length, true);
LOG(PDA_LOG,LOG_DEBUG, "%s", buff);
}
/*
@ -599,13 +746,19 @@ bool process_pda_packet(unsigned char *packet, int length)
case CMD_STATUS:
_aqualink_data->last_display_message[0] = '\0';
if (equiptment_updated == true && pda_m_type() != PM_EQUIPTMENT_STATUS)
{
process_pda_packet_msg_long_equiptment_status(NULL, 0, true);
equiptment_updated = false;
}
/*
// If we get a status packet, and we are on the status menu, this is a list of what's on
// or pending so unless flash turn everything off, and just turn on items that are listed.
// This is the only way to update a device that's been turned off by a real PDA / keypad.
// Note: if the last line of the status menu is present it may be cut off
if (pda_m_type() == PM_EQUIPTMENT_STATUS)
{
if (_aqualink_data->frz_protect_state == ON)
_aqualink_data->frz_protect_state = ENABLE;
@ -615,6 +768,8 @@ bool process_pda_packet(unsigned char *packet, int length)
if (_aqualink_data->swg_led_state == ON)
setSWGenabled(_aqualink_data);
// Need to remove this when new way works.
#ifndef NEW_UPDATE_METHOD
if (pda_m_line(PDA_LINES - 1)[0] == '\0')
{
for (i = 0; i < _aqualink_data->total_buttons; i++)
@ -629,16 +784,22 @@ bool process_pda_packet(unsigned char *packet, int length)
{
LOG(PDA_LOG,LOG_DEBUG, "PDA Equipment status may be truncated.\n");
}
for (i = 1; i < PDA_LINES; i++)
{
pass_pda_equiptment_status_item(pda_m_line(i));
#else
if (!equiptment_updated) {
equiptment_updated = true;
for (i = 1; i < PDA_LINES; i++) {
pass_pda_equiptment_status_item(pda_m_line(i));
}
}
#endif
}
if (pda_m_type() == PM_FREEZE_PROTECT_DEVICES)
{
process_pda_freeze_protect_devices();
}
}*/
break;
case CMD_MSG_LONG:
{
@ -646,12 +807,19 @@ bool process_pda_packet(unsigned char *packet, int length)
//printf ("menu type %d\n",pda_m_type());
msg = (char *)packet + PKT_DATA + 1;
index = packet[PKT_DATA] & 0xF;
//strcpy(_aqualink_data->last_message, msg);
if (packet[PKT_DATA] == 0x82)
{ // Air & Water temp is always this ID
process_pda_packet_msg_long_temp(msg);
#ifdef NEW_UPDATE_METHOD
// if (!equiptment_updated) {
// equiptment_update_cycle(-1); // Reset equiptment cycle
// equiptment_updated = false;
// }
#endif
}
else if (packet[PKT_DATA] == 0x40)
{ // Time is always on this ID
@ -669,6 +837,10 @@ bool process_pda_packet(unsigned char *packet, int length)
case PM_BUILDING_HOME:
process_pda_packet_msg_long_home(msg);
break;
case PM_EQUIPTMENT_STATUS:
process_pda_packet_msg_long_equiptment_status(msg, index, false);
equiptment_updated = true;
break;
case PM_SET_TEMP:
process_pda_packet_msg_long_set_temp(msg);
break;
@ -687,6 +859,11 @@ bool process_pda_packet(unsigned char *packet, int length)
case PM_AUX_LABEL_DEVICE:
process_pda_packet_msg_long_level_aux_device(msg);
break;
/*
case PM_SET_TIME:
process_pda_packet_msg_long_set_time(index, msg);
break;
*/
//case PM_FW_VERSION:
// process_pda_packet_msg_long_FW_version(msg);
//break;

BIN
pda.o Normal file

Binary file not shown.

View File

@ -56,7 +56,6 @@ static pda_type _PDA_Type;
// Each RS message / call to this function is around 0.2 seconds apart
//#define MAX_ACK_FOR_THREAD 200 // ~40 seconds (Init takes 30)
#define MAX_ACK_FOR_THREAD 60 // ~12 seconds (testing, will stop every thread)
// *** DELETE THIS WHEN PDA IS OUT OF BETA ****
void pda_programming_thread_check(struct aqualinkdata *aq_data)
{
@ -67,7 +66,6 @@ void pda_programming_thread_check(struct aqualinkdata *aq_data)
static struct timespec now;
struct timespec elapsed;
#endif
// Check for long lasting threads
if (aq_data->active_thread.thread_id != 0) {
if (thread_id != *aq_data->active_thread.thread_id) {
@ -90,7 +88,6 @@ void pda_programming_thread_check(struct aqualinkdata *aq_data)
#else
LOG(PDA_LOG,LOG_ERR, "Thread %d,%p FAILED to finished in reasonable time, killing it!\n", aq_data->active_thread.ptype, aq_data->active_thread.thread_id)
#endif
if (pthread_cancel(*aq_data->active_thread.thread_id) != 0)
LOG(PDA_LOG,LOG_ERR, "Thread kill failed\n");
else {
@ -784,13 +781,9 @@ bool waitForPDAMessageTypes(struct aqualinkdata *aq_data, unsigned char mtype1,
return waitForPDAMessageTypesOrMenu(aq_data, mtype1, mtype2, numMessageReceived, NULL, 0);
}
bool set_PDA_numeric_field_value(struct aqualinkdata *aq_data, int val, int *cur_val, char *select_label, int step) {
bool set_PDA_numeric_field_value(struct aqualinkdata *aq_data, int val, int cur_val, char *select_label, int step) {
int i=0;
if (val == *cur_val) {
LOG(PDA_LOG,LOG_INFO, "PDA %s value : already at %d\n", select_label, val);
return true;
}
if (select_label != NULL) {
// :TODO: Should probably change below to call find_pda_menu_item(), rather than doing it here
// If we lease this, need to limit on the number of loops
@ -808,24 +801,22 @@ bool set_PDA_numeric_field_value(struct aqualinkdata *aq_data, int val, int *cur
send_cmd(KEY_PDA_SELECT);
}
if (val < *cur_val) {
LOG(PDA_LOG,LOG_DEBUG, "PDA %s value : lower from %d to %d\n", select_label, *cur_val, val);
for (i = *cur_val; i > val; i=i-step) {
if (val < cur_val) {
LOG(PDA_LOG,LOG_DEBUG, "PDA %s value : lower from %d to %d\n", select_label, cur_val, val);
for (i = cur_val; i > val; i=i-step) {
send_cmd(KEY_PDA_DOWN);
}
} else if (val > *cur_val) {
LOG(PDA_LOG,LOG_DEBUG, "PDA %s value : raise from %d to %d\n", select_label, *cur_val, val);
for (i = *cur_val; i < val; i=i+step) {
} else if (val > cur_val) {
LOG(PDA_LOG,LOG_DEBUG, "PDA %s value : raise from %d to %d\n", select_label, cur_val, val);
for (i = cur_val; i < val; i=i+step) {
send_cmd(KEY_PDA_UP);
}
} else {
LOG(PDA_LOG,LOG_INFO, "PDA %s value : already at %d\n", select_label, val);
send_cmd(KEY_PDA_BACK);
return true;
}
send_cmd(KEY_PDA_SELECT);
LOG(PDA_LOG,LOG_DEBUG, "PDA %s value : set to %d\n", select_label, *cur_val);
LOG(PDA_LOG,LOG_DEBUG, "PDA %s value : set to %d\n", select_label, val);
return true;
}
@ -837,9 +828,9 @@ bool set_PDA_aqualink_SWG_setpoint(struct aqualinkdata *aq_data, int val) {
}
if (aq_data->aqbuttons[SPA_INDEX].led->state != OFF)
return set_PDA_numeric_field_value(aq_data, val, &aq_data->swg_percent, "SET SPA", 5);
return set_PDA_numeric_field_value(aq_data, val, aq_data->swg_percent, "SET SPA", 5);
else
return set_PDA_numeric_field_value(aq_data, val, &aq_data->swg_percent, "SET POOL", 5);
return set_PDA_numeric_field_value(aq_data, val, aq_data->swg_percent, "SET POOL", 5);
//return true;
}
@ -889,27 +880,27 @@ bool set_PDA_aqualink_boost(struct aqualinkdata *aq_data, bool val)
bool set_PDA_aqualink_heater_setpoint(struct aqualinkdata *aq_data, int val, bool isPool) {
char label[10];
int *cur_val;
int cur_val;
if ( isCOMBO_PANEL ) {
if (isPool) {
sprintf(label, "POOL HEAT");
cur_val = &aq_data->pool_htr_set_point;
cur_val = aq_data->pool_htr_set_point;
} else {
sprintf(label, "SPA HEAT");
cur_val = &aq_data->spa_htr_set_point;
cur_val = aq_data->spa_htr_set_point;
}
} else {
if (isPool) {
sprintf(label, "TEMP1");
cur_val = &aq_data->pool_htr_set_point;
cur_val = aq_data->pool_htr_set_point;
} else {
sprintf(label, "TEMP2");
cur_val = &aq_data->spa_htr_set_point;
cur_val = aq_data->spa_htr_set_point;
}
}
if (val == *cur_val) {
if (val == cur_val) {
LOG(PDA_LOG,LOG_INFO, "PDA %s setpoint : temp already %d\n", label, val);
send_cmd(KEY_PDA_BACK);
return true;
@ -933,7 +924,7 @@ bool set_PDA_aqualink_freezeprotect_setpoint(struct aqualinkdata *aq_data, int v
} else if (! goto_pda_menu(aq_data, PM_FREEZE_PROTECT)) {
LOG(PDA_LOG,LOG_ERR, "Error finding freeze protect setpoints menu\n");
return false;
} else if (! set_PDA_numeric_field_value(aq_data, val, &aq_data->frz_protect_set_point, NULL, 1)) {
} else if (! set_PDA_numeric_field_value(aq_data, val, aq_data->frz_protect_set_point, NULL, 1)) {
LOG(PDA_LOG,LOG_ERR, "Error failed to set freeze protect temp value\n");
return false;
} else {
@ -941,6 +932,73 @@ bool set_PDA_aqualink_freezeprotect_setpoint(struct aqualinkdata *aq_data, int v
}
}
bool set_PDA_aqualink_time(struct aqualinkdata *aq_data) {
if (! goto_pda_menu(aq_data, PM_SET_TIME)) {
LOG(PDA_LOG,LOG_ERR, "Error finding freeze protect setpoints menu\n");
return false;
}
struct tm tm;
time_t now;
static char result[30];
time(&now); // get time now
localtime_r(&now, &tm);
LOG(PDA_LOG,LOG_DEBUG, "set_PDA_aqualink_time %s\n", asctime_r(&tm,result));
/*
Debug: PDA: PDA Menu Line 0 = Set Time
Debug: PDA: PDA Menu Line 1 =
Debug: PDA: PDA Menu Line 2 = 01/18/11 Tue
Debug: PDA: PDA Menu Line 3 = 2:51 PM
Debug: PDA: PDA Menu Line 4 =
Debug: PDA: PDA Menu Line 5 =
Debug: PDA: PDA Menu Line 6 = Use Arrow Keys
Debug: PDA: PDA Menu Line 7 = to set value.
Debug: PDA: PDA Menu Line 8 = Press SELECT
Debug: PDA: PDA Menu Line 9 = to continue.
*/
// Crap way to do this, we should use highlight chars, but I don't have enough debug/packet data
// info to code that at present. So just pull from lines.
//int cbuf = 0;
char *line = pda_m_hlight();
// Basic check for date line.
printf("***** Char %c ****\n",line[4]);
if (line[4] == '/') {
send_cmd(KEY_PDA_SELECT);
if (! set_PDA_numeric_field_value(aq_data, tm.tm_mon, atoi(&line[2]), NULL, 1)) {
LOG(PDA_LOG,LOG_ERR, "Error failed to set month\n");
}
} else {
LOG(PDA_LOG,LOG_ERR, "Error set time failed\n");
send_cmd(KEY_PDA_BACK);
send_cmd(KEY_PDA_BACK);
return true;
}
/*
if (! set_PDA_numeric_field_value(aq_data, tm.tm_mon, aq_data->tm.tm_mon, NULL, 1)) {
LOG(PDA_LOG,LOG_ERR, "Error failed to set month\n");
} else if (! set_PDA_numeric_field_value(aq_data, tm.tm_mday, aq_data->tm.tm_mday, NULL, 1)) {
LOG(PDA_LOG,LOG_ERR, "Error failed to set day\n");
} else if (! set_PDA_numeric_field_value(aq_data, tm.tm_year, aq_data->tm.tm_year, NULL, 1)) {
LOG(PDA_LOG,LOG_ERR, "Error failed to set year\n");
} else if (! set_PDA_numeric_field_value(aq_data, tm.tm_hour, aq_data->tm.tm_hour, NULL, 1)) {
LOG(PDA_LOG,LOG_ERR, "Error failed to set hour\n");
} else {
time(&now); // update time
localtime_r(&now, &tm);
if (! set_PDA_numeric_field_value(aq_data, tm.tm_min, aq_data->tm.tm_min, NULL, 1)) {
LOG(PDA_LOG,LOG_ERR, "Error failed to set min\n");
}
waitForPDAnextMenu(aq_data);
}
*/
return true;
}
// Test ine this.
bool get_PDA_aqualink_aux_labels(struct aqualinkdata *aq_data) {
#ifdef BETA_PDA_AUTOLABEL
@ -989,7 +1047,6 @@ bool waitForPDAMessage(struct aqualinkdata *aq_data, int numMessageReceived, uns
LOG(PDA_LOG,LOG_DEBUG, "Programming mode: loop %d of %d looking for '%s' received message '%s'\n",i,numMessageReceived,message,aq_data->last_message);
else
LOG(PDA_LOG,LOG_DEBUG, "Programming mode: loop %d of %d waiting for next message, received '%s'\n",i,numMessageReceived,aq_data->last_message);
if (message != NULL) {
ptr = stristr(aq_data->last_message, msgS);
if (ptr != NULL) { // match
@ -1018,7 +1075,6 @@ bool waitForPDAMessage(struct aqualinkdata *aq_data, int numMessageReceived, uns
return true;
}
*/
@ -1030,7 +1086,6 @@ https://www.jandy.com/-/media/zodiac/global/downloads/h/h0574200.pdf
/*
List of how menu's display
PDA Line 0 =
PDA Line 1 = AquaPalm
PDA Line 2 =
@ -1041,7 +1096,6 @@ PDA Line 6 =
PDA Line 7 =
PDA Line 8 =
PDA Line 9 =
PDA Line 0 =
PDA Line 1 = AquaPalm
PDA Line 2 =
@ -1052,7 +1106,6 @@ PDA Line 6 =
PDA Line 7 =
PDA Line 8 =
PDA Line 9 =
PDA Menu Line 0 =
PDA Menu Line 1 = PDA-P4 Only
PDA Menu Line 2 =
@ -1063,7 +1116,6 @@ PDA Menu Line 6 =
PDA Menu Line 7 =
PDA Menu Line 8 =
PDA Menu Line 9 =
************** The above have different menu to below rev/version *********
***************** Think this is startup different rev *************
PDA Menu Line 0 =
@ -1072,7 +1124,6 @@ PDA Menu Line 2 =
PDA Menu Line 3 = Firmware Version
PDA Menu Line 4 =
PDA Menu Line 5 = PPD: PDA 1.2
PDA Line 0 =
PDA Line 1 = AIR POOL
PDA Line 2 =
@ -1083,7 +1134,6 @@ PDA Line 6 = SPA MODE OFF
PDA Line 7 = SPA HEATER OFF
PDA Line 8 = MENU
PDA Line 9 = EQUIPMENT ON/OFF
PDA Line 0 = MAIN MENU
PDA Line 1 =
PDA Line 2 = SET TEMP >
@ -1094,7 +1144,6 @@ PDA Line 6 =
PDA Line 7 = BOOST POOL
PDA Line 8 =
PDA Line 9 =
**************** OPTION 2 FOR THIS MENU ********************
PDA Line 0 = MAIN MENU
PDA Line 1 =
@ -1106,9 +1155,7 @@ PDA Line 6 = PDA OPTIONS >
PDA Line 7 = SYSTEM SETUP >
PDA Line 8 =
PDA Line 9 = BOOST
********** Guess at SYSTEM SETUP Menu (not on Rev MMM or before)************
// PDA Line 0 = SYSTEM SETUP
// PDA Line 1 = LABEL AUX >
// PDA Line 2 = FREEZE PROTECT >
@ -1123,9 +1170,6 @@ PDA Line 9 = BOOST
// PDA Line 6 = SPA SWITCH >
// PDA Line 7 = SERVICE INFO >
// PDA Line 8 = CLEAR MEMORY >
PDA Line 0 = PALM OPTIONS
PDA Line 1 =
PDA Line 2 =
@ -1136,7 +1180,6 @@ PDA Line 6 =
PDA Line 7 = Choose setting
PDA Line 8 = and press SELECT
PDA Line 9 =
PDA Line 0 = SET AquaPure
PDA Line 1 =
PDA Line 2 =
@ -1147,7 +1190,6 @@ PDA Line 6 =
PDA Line 7 = Highlight an
PDA Line 8 = item and press
PDA Line 9 = SELECT
PDA Line 0 = SET TIME
PDA Line 1 =
PDA Line 2 = 05/22/19 WED
@ -1158,7 +1200,6 @@ PDA Line 6 = Use ARROW KEYS
PDA Line 7 = to set value.
PDA Line 8 = Press SELECT
PDA Line 9 = to continue.
PDA Line 0 = SET TEMP
PDA Line 1 =
PDA Line 2 = POOL HEAT 70`F
@ -1169,11 +1210,8 @@ PDA Line 6 =
PDA Line 7 = Highlight an
PDA Line 8 = item and press
PDA Line 9 = SELECT
******* GUSSING AT BELOW *******
when single mode (pool OR spa) not (pool AND spa) temps are different.
PDA Line 0 = SET TEMP
PDA Line 1 =
PDA Line 2 = TEMP1 70`F
@ -1184,11 +1222,6 @@ PDA Line 6 =
PDA Line 7 = Highlight an
PDA Line 8 = item and press
PDA Line 9 = SELECT
PDA Line 0 = EQUIPMENT
PDA Line 1 = FILTER PUMP ON
PDA Line 2 = SPA OFF
@ -1199,7 +1232,6 @@ PDA Line 6 = WATERFALL OFF
PDA Line 7 = AIR BLOWER OFF
PDA Line 8 = LIGHT OFF
PDA Line 9 = ^^ MORE __
PDA Line 0 = EQUIPMENT
PDA Line 1 = WATERFALL OFF
PDA Line 2 = AIR BLOWER OFF
@ -1210,7 +1242,6 @@ PDA Line 6 = SPA MODE OFF
PDA Line 7 = CLEAN MODE OFF
PDA Line 8 = ALL OFF
PDA Line 9 =
// This is from a single device setup (pool OR spa not pool AND spa)
PDA Menu Line 0 = EQUIPMENT
PDA Menu Line 1 =
@ -1222,7 +1253,6 @@ PDA Menu Line 6 = Pool Light ON
PDA Menu Line 7 = AUX3 OFF
PDA Menu Line 8 = EXTRA AUX OFF
PDA Menu Line 9 = ALL OFF
PDA Line 0 = Equipment Status
PDA Line 1 =
PDA Line 2 = Intelliflo VS 1
@ -1233,7 +1263,6 @@ PDA Line 6 =
PDA Line 7 =
PDA Line 8 =
PDA Line 9 =
PDA Line 0 = Equipment Status
PDA Line 1 =
PDA Line 2 = AquaPure 20%
@ -1244,17 +1273,13 @@ PDA Line 6 =
PDA Line 7 =
PDA Line 8 =
PDA Line 9 =
VSP Motes.
four types of variable speed pumps,
Jandy ePumpTM DC,
Jandy ePumpTM AC,
IntelliFlo® 1 VF,
IntelliFlo® VS.
The SCALE setting is fixed to RPM for the Jandy ePumpTM DC, Jandy ePumpTM AC, and IntelliFlo® VS.
The SCALE setting is fixed to GPM for the IntelliFlo® VF
There are eight (8) default speed presets for each variable speed pump.
*/
*/

View File

@ -10,6 +10,7 @@ typedef enum pda_type {
void *get_aqualink_PDA_device_status( void *ptr );
void *set_aqualink_PDA_device_on_off( void *ptr );
void *set_aqualink_PDA_wakeinit( void *ptr );
void *set_aqualink_PDA_init( void *ptr );
bool set_PDA_aqualink_heater_setpoint(struct aqualinkdata *aq_data, int val, bool isPool);
bool set_PDA_aqualink_SWG_setpoint(struct aqualinkdata *aq_data, int val);
@ -21,6 +22,7 @@ bool get_PDA_freeze_protect_temp(struct aqualinkdata *aq_data);
bool get_PDA_aqualink_aux_labels(struct aqualinkdata *aq_data);
bool set_PDA_aqualink_boost(struct aqualinkdata *aq_data, bool val);
bool set_PDA_aqualink_time(struct aqualinkdata *aq_data);
//void pda_programming_thread_check(struct aqualinkdata *aq_data);

BIN
pda_aq_programmer.o Normal file

Binary file not shown.

View File

@ -191,6 +191,8 @@ bool process_pda_menu_packet(unsigned char* packet, int length)
signed char last_line;
signed char line_shift;
signed char i;
int index = 0;
switch (packet[PKT_CMD]) {
case CMD_PDA_CLEAR:
@ -198,10 +200,17 @@ bool process_pda_menu_packet(unsigned char* packet, int length)
memset(_menu, 0, PDA_LINES * (AQ_MSGLEN+1));
break;
case CMD_MSG_LONG:
/*
if (packet[PKT_DATA] < 10) {
memset(_menu[packet[PKT_DATA]], 0, AQ_MSGLEN);
strncpy(_menu[packet[PKT_DATA]], (char*)packet+PKT_DATA+1, AQ_MSGLEN);
_menu[packet[PKT_DATA]][AQ_MSGLEN] = '\0';
}*/
index = packet[PKT_DATA] & 0xF;
if (index < 10) {
memset(_menu[index], 0, AQ_MSGLEN);
strncpy(_menu[index], (char*)packet+PKT_DATA+1, AQ_MSGLEN);
_menu[index][AQ_MSGLEN] = '\0';
}
if (getLogLevel(PDA_LOG) >= LOG_DEBUG){print_menu();}
break;

BIN
pda_menu.o Normal file

Binary file not shown.

BIN
release/._home.conf Normal file

Binary file not shown.

Binary file not shown.

View File

@ -95,7 +95,7 @@ keep_paneltime_synced = yes
# ignored. You can force these to work by setting the below.
override_freeze_protect = no
# Confert Deg F to Deg C when posting to Domoticz or MQTT.
# Convert Deg F to Deg C when posting to Domoticz or MQTT.
# If using homebridge-aqualinkd convert_mqtt_temp_to_c must be set to yes.
convert_mqtt_temp_to_c = yes
convert_dz_temp_to_c = yes
@ -104,7 +104,7 @@ convert_dz_temp_to_c = yes
# enable below to report 0 as the spa temp when spa is off.
# This is for MQTT cnnections only, WEB socket and WEB API always report TEMP_UNKNOWN (-999) allowing the consumer to
# decide how to report.
report_zero_spa_temp = no
report_zero_spa_temp = yes
# default is to not report changes to pool temp when the filter pump is off or in spa mode
# enable below to report 0 as the pool temp when the filter pump is off or when in spa mode.
@ -124,8 +124,9 @@ report_zero_pool_temp = no
#mqtt_timed_update = no
# Please see forum for this, only set to yes when logging information to support
# new devices. Inflrmation will be written to /tmp/RS485.log
# new devices. Inflrmation will be written to /tmp/RS485.log & /tmp/RS485_raw.log respectively
#debug_RSProtocol_packets = no
#debug_RSProtocol_bytes = no
# Not documented. These will change how RS485 / Serial works, Only use if asked to for problem solving purposes.
#serial_readahead_b4_write = yes
@ -134,6 +135,11 @@ report_zero_pool_temp = no
#swg_zero_ignore_count = 20
# Enable AqualinkD scheduler.
# A version of cron that supports cron.d must be installed for the scheduler to work.
# If you used the install script and didn;t receive any cron warnings, you should be good to go.
enable_scheduler = yes
# Put AqualinkD to sleep when in PDA mode after inactivity.
# Ignore if you are not using PDA mode.
# If you have Jandy PDA then this MUST be set to yes as the controller can only support one PDA.

168
release/aqualinkd.pda.conf Normal file
View File

@ -0,0 +1,168 @@
# aqualinkd.conf
#
# The directory where the web files are stored
#web_directory=/var/www/aqualinkd/
web_directory=/nas/data/Development/Raspberry/AqualinkD/web
# Log to file, comment out if you do not want to log to file
#log_file=/var/log/aqualinkd.log
# The log level. [DEBUG, INFO, NOTICE, WARNING, ERROR]
# Pick the highest level, and all levels below will be sent to syslog.
# your syslog settings may be set to only display messages above a certian level
# in which case make sure you use the log_file settings to capture everything
# you want when debugging
# so, NOTICE also prints WARNING & ERROR
# DEBUG would print everything possible
#log_level=DEBUG_SERIAL
#log_level=DEBUG_SERIAL
#log_level=DEBUG
log_level=INFO
#log_level=NOTICE
display_warnings_in_web = yes
# The socket port that the daemon listens to
# If you change this from 80, remember to update aqualink.service.avahi
socket_port=88
# The serial port the daemon access to read the Aqualink RS8
serial_port=/dev/ttyUSB0
override_freeze_protect = no
# mqtt stuff
mqtt_address = trident:1883
#mqtt_user = someusername
#mqtt_passwd = somepassword
#mqtt_dz_pub_topic = domoticz/in
#mqtt_dz_sub_topic = domoticz/out
mqtt_aq_topic = aqualinkd-pda
# The id of the Aqualink terminal device. Devices probed by RS8 master are:
# 08-0b, 10-13, 18-1b, 20-23, 28-2b, 30-33, 38-3b, 40-43
#
# Working RS 0x0a 0x0b 0x09 0x08
panel_type = PD-6 Combo
#device_id=0x09
#device_id=0x00
#device_id=0x40
#rs_panel_size = 8
# PDA is 0x60
device_id=0x60
#pda_mode = yes
pda_sleep_mode = yes
#use_PDA_auxiliary = yes
read_pentair_packets = no
read_all_devices = yes
convert_mqtt_temp_to_c = yes
convert_dz_temp_to_c = yes
force_SWG = yes
# by default use pool temp as spa temp when spa is off, enable below to report 0 as spa temp when off.
report_zero_spa_temp = yes
# Button inxed light probramming button is assigned to. (look at your button labels below)
light_programming_button = 6
# Light probramming mode. 0=safe mode, but slow.
# any number greater is seconds to wait between button presses.
# 0.4 seems to be the minimum. (workd for light modes below 10 presses)
# 0.6 seems to work about 95% of the time, but above 20 presses can be hit or miss.
# 0 will simply wait for the controler to send the response back before sending the next, so is equivelent to about 1.2
light_programming_mode=0
# Light programming assumes light needs to be on before sending pulse (above setting)
# If the light is off when request is made to change "light show", then the below value are used
light_programming_initial_on=15
# Turn the light off for below time before start programmig puleses.
light_programming_initial_off=12
# Try to use labels from Control Panel.
#use_panel_aux_labels=no
use_panel_aux_labels=yes
# If you have a SWG, set this to yes. AqualinkD can only detect a SWG if it's on, so after a restart
# you will not see/access a SWG until the the next time the pump is on.
#force_SWG = yes
# Domoticz ID's for temps.
air_temp_dzidx=13
pool_water_temp_dzidx=14
spa_water_temp_dzidx=15
#SWG_percent_dzidx=998
#SWG_PPM_dzidx=999
SWG_percent_dzidx=153
SWG_PPM_dzidx=152
SWG_Status_dzidx=157
#
# Pentair pump ID's
# 0x60 to 0x6F (0x60, 0x61 0x61, 0x62, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F)
# Jandy pump ID's
# 0x78, 0x79, 0x7A, 0x7B
# Labels for standard butons (shown in web UI), and domoticz idx's
button_01_label=Filter Pump
#button_01_dzidx=37
#button_01_pumpID=0x60
#button_01_PDA_label=FILTER PUMP
button_02_label=Spa Mode
#button_02_dzidx=38
#button_02_PDA_label=SPA
button_03_label=Cleaner
#button_03_dzidx=39
#button_03_PDA_label=CLEANER
button_04_label=Waterfall
#button_04_dzidx=40
#button_04_pumpID=0x78
#button_04_PDA_label=WATERFALL
button_05_label=Spa Blower
#button_05_dzidx=41
#button_05_PDA_label=Pool Light
button_06_label=Light
#button_06_dzidx=42
#button_06_PDA_label=LIGHT
button_07_label=AUX5
#button_07_label=NONE
#button_07_dzidx=43
#button_07_PDA_label=AUX5
#button_08_label=NONE
#button_08_dzidx=NONE
#button_08_PDA_label=NONE
#button_09_label=NONE
#button_09_dzidx=NONE
#button_09_PDA_label=NONE
button_10_label=Pool Heat
#button_10_dzidx=44
#button_10_PDA_label=POOL HEAT
button_11_label=Spa Heat
#button_11_dzidx=56
#button_11_PDA_label=SPA HEAT
button_12_label=EXTRA AUX
#button_12_label=Solar Heater
#button_12_dzidx=NONE
#button_12_PDA_label=EXTRA AUX

175
release/aqualinkd.playback.conf Executable file
View File

@ -0,0 +1,175 @@
# aqualinkd.conf
#
# The directory where the web files are stored
web_directory=/nas/data/Development/Raspberry/AqualinkD/web/
# Log to file, comment out if you do not want to log to file
#log_file=/var/log/aqualinkd.log
# The log level. [DEBUG_DERIAL, DEBUG, INFO, NOTICE, WARNING, ERROR]
# Pick the highest level, and all levels below will be sent to syslog.
# your syslog settings may be set to only display messages above a certian level
# in which case make sure you use the log_file settings to capture everything
# you want when debugging
# so, NOTICE also prints WARNING & ERROR
# DEBUG_SERIAL would print everything possible
#log_level=DEBUG_SERIAL
log_level=DEBUG
#log_level=INFO
#log_level=NOTICE
swg_zero_ignore_count = 50
display_warnings_in_web = no
# The socket port that the daemon listens to
# If you change this from 80, remember to update aqualink.service.avahi
socket_port=88
# The serial port the daemon access to read the Aqualink RS8
#serial_port=/dev/ttyUSB0
#serial_port=./scratch/logs/raw.log
#serial_port=./scratch/player.log
serial_port=./scratch/logs/RS485-debug2.raw
#serial_port=./tmp.tmp
read_pentair_packets=yes
# If equiptment is in freeze protect mode some commands like pump_off / spa_on are
# ignored. You can force these to work by setting the below.
override_freeze_protect = no
# Confert Deg F to Deg C when posting to Domoticz or MQTT.
convert_mqtt_temp_to_c = yes
convert_dz_temp_to_c = yes
# default is to use pool water temp as spa water temp when spa is off (and there for not able to report water temp)
# enable below to report 0 as the spa temp when spa is off.
# This is for MQTT cnnections only, WEB socket and WEB API always report TEMP_UNKNOWN (-999) allowing the consumer to
# decide how to report.
report_zero_spa_temp = no
# default is to not report changes to pool temp when the filter pump is off or in spa mode
# enable below to report 0 as the pool temp when the filter pump is off or when in spa mode.
# This is for MQTT cnnections only, WEB socket and WEB API always report TEMP_UNKNOWN (-999) allowing the consumer to
# decide how to report.
report_zero_pool_temp = no
# mqtt stuff
mqtt_address = trident:1883
#mqtt_user = someusername
#mqtt_passwd = somepassword
#mqtt_dz_pub_topic = domoticz/in
#mqtt_dz_sub_topic = domoticz/out
mqtt_aq_topic = aqualinkd-play
# The id of the Aqualink terminal device. Devices probed by RS8 master are:
# 08-0b, 10-13, 18-1b, 20-23, 28-2b, 30-33, 38-3b, 40-43
# Working RS ID's are 0x0a 0x0b 0x09 0x08 <- 0x08 is usually taken
device_id=0x0a
# Please see forum for this, only set to yes when logging information to support
# new devices. Inflrmation will be written to /tmp/RS485.log
#debug_RSProtocol_packets = no
#Only for PDA mode
# set PDA mode
#pda_mode = yes
#
# Put AqualinkD to sleep when in PDA mode after inactivity.
# If you have Jandy PDA then this MUST be set to yes as the controller can only support one PDA.
# If you don't have a Jandy PDA leave this at no as AqualinkD will be a lot quicker.
# Sleep timer is around 2 mins of inactivity, then wake after 2 mins of sleep.
#pda_sleep_mode = yes
# Read status information from other devices on the RS485 bus.
# At the moment just Salt Water Generators are supported.
read_all_devices = yes
# If you have a SWG connected to the control panel, set this to yes.
# AqualinkD can only detect a SWG if it's on, so after a restart you will not see/access a SWG until the the next time the pump is on.
force_SWG = no
# Button inxed light probramming button is assigned to. (look at your button labels below)
light_programming_button_pool = 6
# Light probramming mode. 0=safe mode, but slow.
# any number greater is seconds to wait between button presses.
# 0.4 seems to be the minimum. (workd for light modes below 10 presses)
# 0.6 seems to work about 95% of the time, but above 20 presses can be hit or miss.
# 0 will simply wait for the controler to send the response back before sending the next, so is equivelent to about 1.2
light_programming_mode=0
# Light programming assumes light needs to be on before sending pulse (above setting)
# If the light is off when request is made to change "light show", then the below value are used
light_programming_initial_on=15
# Turn the light off for below time before start programmig puleses.
light_programming_initial_off=12
# Everything below here, if it ends with dzidx, then that's the ID for domoticz,
# so not needed if you are not suing dooticz.
# Domoticz ID's for temps.
air_temp_dzidx=0
pool_water_temp_dzidx=0
spa_water_temp_dzidx=0
SWG_percent_dzidx=0
SWG_PPM_dzidx=0
# Try to use labels from Control Panel.
use_panel_aux_labels=yes
# Labels for standard butons (shown in web UI), and domoticz idx's
button_01_label=Filter Pump
#button_01_dzidx=37
#button_01_PDA_label=FILTER PUMP
button_01_pumpID=0x60
button_02_label=Spa Mode
#button_02_dzidx=38
#button_02_PDA_label=SPA
button_03_label=Cleaner
#button_03_dzidx=39
#button_03_PDA_label=AUX1
button_04_label=Waterfall
#button_04_dzidx=40
#button_04_PDA_label=AUX2
button_04_pumpID=0x61
button_05_label=Spa Blower
#button_05_dzidx=41
#button_05_PDA_label=AUX3
button_06_label=Pool Light
#button_06_dzidx=42
#button_06_PDA_label=AUX4
button_07_label=Spa Light
#button_07_dzidx=43
#button_07_PDA_label=AUX5
button_08_label=NONE
#button_08_dzidx=NONE
#button_08_PDA_label=AUX6
button_09_label=NONE
#button_09_dzidx=NONE
#button_09_PDA_label=AUX7
button_10_label=Pool Heater
#button_10_dzidx=44
#button_09_PDA_label=POOL HEAT
button_11_label=Spa Heater
#button_11_dzidx=56
#button_09_PDA_label=SPA HEAT
button_12_label=Solar Heater
#button_12_dzidx=NONE
#button_09_PDA_label=EXTRA AUX

287
release/aqualinkd.test.conf Normal file
View File

@ -0,0 +1,287 @@
# aqualinkd.conf
#
# The directory where the web files are stored
crap_test = error
#web_directory=/var/www/aqualinkd/
web_directory=/nas/data/Development/Raspberry/AqualinkD/web
# Log to file, comment out if you do not want to log to file
#log_file=/var/log/aqualinkd.log
# The log level. [DEBUG, INFO, NOTICE, WARNING, ERROR]
# Pick the highest level, and all levels below will be sent to syslog.
# your syslog settings may be set to only display messages above a certian level
# in which case make sure you use the log_file settings to capture everything
# you want when debugging
# so, NOTICE also prints WARNING & ERROR
# DEBUG would print everything possible
#log_level=DEBUG_SERIAL
#log_level=DEBUG
#log_level=INFO
log_level=NOTICE
# AQUA_LOG 1
# NET_LOG 2
# AQRS_LOG 4
# ONET_LOG 8
# IAQT_LOG 16
# PDA_LOG 32
# RSSA_LOG 64
# DJAN_LOG 128
# DPEN_LOG 256
# RSSD_LOG 512
# PROG_LOG 1024
# DBGT_LOG 2048 // Only used when compiled aqdebug
# TIMR_LOG 4096
#debug_log_mask = 1
#debug_log_mask = 2
#debug_log_mask = 4
#debug_log_mask = 8
#debug_log_mask = 16
#debug_log_mask = 32
#debug_log_mask = 64
#debug_log_mask = 256
#debug_log_mask = 512
#debug_log_mask = 1024
#debug_log_mask = 2048
#debug_log_mask = 4096
display_warnings_in_web = yes
# The socket port that the daemon listens to
# If you change this from 80, remember to update aqualink.service.avahi
socket_port = 80
# The serial port the daemon access to read the Aqualink RS8
serial_port=/dev/ttyUSB0
#serial_port=/dev/ttyUSB1
#serial_port=/dev/null
override_freeze_protect = no
# mqtt stuff
mqtt_address = trident:1883
#mqtt_user = someusername
#mqtt_passwd = somepassword
#mqtt_dz_pub_topic = domoticz/in
#mqtt_dz_sub_topic = domoticz/out
mqtt_aq_topic = aqualinkd-test
# The id of the Aqualink terminal device. Devices probed by RS8 master are:
# 08-0b, 10-13, 18-1b, 20-23, 28-2b, 30-33, 38-3b, 40-43
#
# Working RS 0x0a 0x0b 0x09 0x08
device_id=0x0a
#device_id=0xFF # For testing one touch, don't use kaypad
#device_id=0x00
#device_id=0x60
#rssa_device_id=0x48
# The ID for extended settings, These are ONE TOUCH MACROS & VARIABLE SPEED PUMP RPM
# Do not enable this if you don't use either, you'll just waste memory and cpu cycles
# Valid ID's are 0x40, 0x41, 0x42 & 0x43.
# If you have a one touch remote do not use Ox40
extended_device_id=0x43
#extended_device_id=0x31
# If you have extended_device_id set, then you can also use that ID for programming some features.
# This means that you can turn things on/off while AqualinkD is programming certian features.
# At the moment only heater setpoints & swg boost is on the extended device programming
extended_device_id_programming = yes
#extended_device_id_programming = no
# Not documented
# Negative poll speed will use serial port in blocking mode,
# 0 or positive will use serialport in non blocking mode, and value will be wait time between reads in ms
# readahead is check serial clear before writing
# thread_net is exactly that.
#serial_readahead_b4_write = yes
#mqtt_timed_update = no
thread_netservices = yes
#rs_poll_speed = -1
rs_poll_speed = 0
# Your RS panel size. ie 4, 6, 8, 12 or 16 relates to RS4, RS6, RS8, RS12 or RS16.
# VERY important that you select 12 or 16, if you have either of those size panels.
# Also don't think setting a 12 when you have a 8 will give you 4 more accessories to control, it won't the
# panel information is needed as different panels use different bits within the RS protocol for status and key
# presses.
#rs_panel_size = 12
#panel_type = RS-2/6 Dual
#panel_type = RS-4 Only
#panel_type = RS-4 Combo
#panel_type = RS-6 Only
panel_type = RS-8 Combo
#panel_type = PD-8 Combo
#panel_type = RS-16 Combo
#panel_type = RS-2/14 Dual
#panel_type = RS-8 Only
#panel_type_size = 8
#panel_type_combo = yes
#panel_type_dual = no
#panel_type_pda = no
#panel_type_rs = yes
#network_poll_speed = 1
keep_paneltime_synced = yes
#pda_mode = yes
#use_PDA_auxiliary = yes
# Read information from these devices directly from the RS485 bus as well as control panel.
#read_RS485_swg = yes
#read_RS485_ePump = no
#read_RS485_vsfPump = no
# F to C conversions
convert_mqtt_temp_to_c = yes
convert_dz_temp_to_c = yes
# by default use pool temp as spa temp when spa is off, enable below to report 0 as spa temp when off.
report_zero_spa_temp = yes
report_zero_pool_temp = no
# Light probramming mode. 0=safe mode, but slow.
# any number greater is seconds to wait between button presses.
# 0.4 seems to be the minimum. (workd for light modes below 10 presses)
# 0.6 seems to work about 95% of the time, but above 20 presses can be hit or miss.
# 0 will simply wait for the controler to send the response back before sending the next, so is equivelent to about 1.2
light_programming_mode=0
# Light programming assumes light needs to be on before sending pulse (above setting)
# If the light is off when request is made to change "light show", then the below value are used
light_programming_initial_on=15
# Turn the light off for below time before start programmig puleses.
light_programming_initial_off=12
# Try to use labels from Control Panel.
#use_panel_aux_labels=yes
# If you have a SWG, set this to yes. AqualinkD can only detect a SWG if it's on, so after a restart
# you will not see/access a SWG until the the next time the pump is on.
force_SWG = yes
# Please see forum for this, only set to yes when logging information to support
# new devices. Inflrmation will be written to /tmp/RS485.log & /tmp/RS485_raw.log respectively
debug_RSProtocol_packets = no
debug_RSProtocol_bytes = no
# Domoticz ID's for temps.
#air_temp_dzidx=13
#pool_water_temp_dzidx=14
#spa_water_temp_dzidx=15
#SWG_percent_dzidx=998
#SWG_PPM_dzidx=999
#SWG_percent_dzidx=153
#SWG_PPM_dzidx=152
#SWG_Status_dzidx=157
#
# | RS-6 Combo | RS-6 Only | RS-8 Combo | RS-2/6 Dual | RS-2/10 Dual | RS-16 Combo |
# --------------------------------------------------------------------------------------------
# Button_01 | Filter Pump | Filter Pump | Filter Pump | Filter Pump | Filter Pump | Filter Pump |
# Button_02 | Spa | Aux_1 | Spa | Spa | Spa | Spa |
# Button_03 | Aux 1 | Aux 2 | Aux 1 | Aux 1 | Aux 1 | Aux 1 |
# Button_04 | Aux 2 | Aux 3 | Aux 2 | Aux 2 | Aux 2 | Aux 2 |
# Button_05 | Aux 3 | Aux 4 | Aux 3 | Aux 3 | Aux 3 | Aux 3 |
# Button_06 | Aux 4 | Aux 5 | Aux 4 | Aux 4 | Aux 4 | Aux 4 |
# Button_07 | Aux 5 | Temp 1 | Aux 5 | Aux 5 | Aux 5 | Aux 5 |
# Button_08 | Pool Heater | Temp 2 | Aux 6 | Aux 6 | Aux 6 | Aux 6 |
# Button_09 | Spa Heater | Solar Heater | Aux 7 | Pool Heater | Aux B1 | Aux 7 |
# Button_10 | Solar Heater | | Pool Heater | Spa Heater | Aux B2 | Aux B1 |
# Button_11 | | | Spa Heater | Solar Heater | Aux B3 | Aux B2 |
# Button_12 | | | Solar Heater | | Aux B4 | Aux B3 |
# Button_13 | | | | | Pool Heater | Aux B4 |
# Button_14 | | | | | Spa Heater | Aux B5 |
# Button_15 | | | | | Solar Heater | Aux B6 |
# Button_16 | | | | | | Aux B7 |
# Button_17 | | | | | | Aux B8 |
# Button_18 | | | | | | Pool Heater |
# Button_19 | | | | | | Spa Heater |
# Button_20 | | | | | | Solar Heater |
#
# Pentair pump ID's
# 0x60 to 0x6F (0x60, 0x61 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F)
# Jandy pump ID's
# 0x78, 0x79, 0x7A, 0x7B
# button_xx_lightMode = (0=Aqualink program, 1=Jandy, 2=Jandy LED, 3=SAm/SAL, 4=Color Logic, 5=Intellibrite)
# Labels for standard butons (shown in web UI), and domoticz idx's
button_01_label=Filter Pump
#button_01_dzidx=37
button_01_pumpID=0x78
#button_01_PDA_label=FILTER PUMP
button_01_pumpIndex=1
button_02_label=Spa
#button_02_dzidx=38
#button_02_pumpID= 0x78
#button_02_pumpIndex=3
#button_02_PDA_label=SPA
button_03_label=Cleaner
#button_03_dzidx=39
button_03_pumpID=0x61
button_03_pumpIndex=3
#button_04_label=Waterfall
#button_04_dzidx=40
button_04_pumpID=0x60
button_04_pumpIndex=2
button_05_label=Light
#button_05_dzidx=41
button_05_lightMode=0
#button_06_label=Aux Pump
#button_06_dzidx=42
#button_06_lightMode=1
#button_06_pumpIndex=4
#button_07_label=Air Blower
#button_07_label=NONE
#button_07_dzidx=43
#button_07_lightMode=0
#button_08_label=Chemical Feed
#button_09_label=Fountain
#button_10_label=Pool Heater
#button_10_dzidx=44
#button_11_label=Spa Heater
#button_11_dzidx=56
#button_12_label=Solar Heater
#button_12_label=Solar Heater
#button_12_dzidx=NONE
# RS-8 & RS-6 STOP HERE
# This is for RS-12 & RS-16 only.
#button_12_label=Aux B1
#button_13_label=
#button_14_label=
#button_15_label=
#button_16_label=
#button_17_label=Pool Heater
#button_18_label=Spa Heater
#button_19_label=Solar Heater

View File

@ -0,0 +1,282 @@
# aqualinkd.conf
#
# The directory where the web files are stored
#web_directory=/var/www/aqualinkd/
web_directory=/nas/data/Development/Raspberry/AqualinkD/web
# Log to file, comment out if you do not want to log to file
#log_file=/var/log/aqualinkd.log
# The log level. [DEBUG, INFO, NOTICE, WARNING, ERROR]
# Pick the highest level, and all levels below will be sent to syslog.
# your syslog settings may be set to only display messages above a certian level
# in which case make sure you use the log_file settings to capture everything
# you want when debugging
# so, NOTICE also prints WARNING & ERROR
# DEBUG would print everything possible
#log_level=DEBUG_SERIAL
#log_level=DEBUG
log_level=INFO
#log_level=NOTICE
# AQUA_LOG 1
# NET_LOG 2
# AQRS_LOG 4
# ONET_LOG 8
# IAQT_LOG 16
# PDA_LOG 32
# RSSA_LOG 64
# DJAN_LOG 128
# DPEN_LOG 256
# RSSD_LOG 512
# PROG_LOG 1024
# DBGT_LOG 2048
# TIMR_LOG 4096
#debug_log_mask = 1
#debug_log_mask = 2
#debug_log_mask = 4
#debug_log_mask = 8
#debug_log_mask = 16
debug_log_mask = 32
#debug_log_mask = 64
#debug_log_mask = 256
#debug_log_mask = 512
#debug_log_mask = 1024
#debug_log_mask = 2048
#debug_log_mask = 4096
display_warnings_in_web = yes
# The socket port that the daemon listens to
# If you change this from 80, remember to update aqualink.service.avahi
socket_port = 88
# The serial port the daemon access to read the Aqualink RS8
serial_port=/dev/ttyUSB0
#serial_port=/dev/ttyUSB1
#serial_port=/dev/null
override_freeze_protect = no
# mqtt stuff
mqtt_address = trident:1883
#mqtt_user = someusername
#mqtt_passwd = somepassword
#mqtt_dz_pub_topic = domoticz/in
#mqtt_dz_sub_topic = domoticz/out
mqtt_aq_topic = aqualinkd-test
# The id of the Aqualink terminal device. Devices probed by RS8 master are:
# 08-0b, 10-13, 18-1b, 20-23, 28-2b, 30-33, 38-3b, 40-43
#
# Working RS 0x0a 0x0b 0x09 0x08
#device_id=0x0a
#device_id=0xFF # For testing one touch, don't use kaypad
#device_id=0x00
device_id=0x60
#rssa_device_id=0x48
# The ID for extended settings, These are ONE TOUCH MACROS & VARIABLE SPEED PUMP RPM
# Do not enable this if you don't use either, you'll just waste memory and cpu cycles
# Valid ID's are 0x40, 0x41, 0x42 & 0x43.
# If you have a one touch remote do not use Ox40
#extended_device_id=0x43
#extended_device_id=0x31
# If you have extended_device_id set, then you can also use that ID for programming some features.
# This means that you can turn things on/off while AqualinkD is programming certian features.
# At the moment only heater setpoints & swg boost is on the extended device programming
#extended_device_id_programming = yes
#extended_device_id_programming = no
# Not documented
serial_readahead_b4_write = yes
mqtt_timed_update = no
thread_netservices = yes
rs_poll_speed = -1
#rs_poll_speed = 1
# Your RS panel size. ie 4, 6, 8, 12 or 16 relates to RS4, RS6, RS8, RS12 or RS16.
# VERY important that you select 12 or 16, if you have either of those size panels.
# Also don't think setting a 12 when you have a 8 will give you 4 more accessories to control, it won't the
# panel information is needed as different panels use different bits within the RS protocol for status and key
# presses.
#rs_panel_size = 12
#panel_type = RS-2/6 Dual
#panel_type = RS-4 Only
#panel_type = RS-4 Combo
#panel_type = RS-6 Only
#panel_type = RS-8 Combo
panel_type = PD-8 Combo
#panel_type = RS-16 Combo
#panel_type = RS-2/14 Dual
#panel_type = RS-8 Only
#panel_type_size = 8
#panel_type_combo = yes
#panel_type_dual = no
#panel_type_pda = no
#panel_type_rs = yes
#network_poll_speed = 1
keep_paneltime_synced = no
#pda_mode = yes
#use_PDA_auxiliary = yes
# Read information from these devices directly from the RS485 bus as well as control panel.
#read_RS485_swg = yes
#read_RS485_ePump = no
#read_RS485_vsfPump = no
# F to C conversions
convert_mqtt_temp_to_c = yes
convert_dz_temp_to_c = yes
# by default use pool temp as spa temp when spa is off, enable below to report 0 as spa temp when off.
report_zero_spa_temp = yes
report_zero_pool_temp = no
# Light probramming mode. 0=safe mode, but slow.
# any number greater is seconds to wait between button presses.
# 0.4 seems to be the minimum. (workd for light modes below 10 presses)
# 0.6 seems to work about 95% of the time, but above 20 presses can be hit or miss.
# 0 will simply wait for the controler to send the response back before sending the next, so is equivelent to about 1.2
light_programming_mode=0
# Light programming assumes light needs to be on before sending pulse (above setting)
# If the light is off when request is made to change "light show", then the below value are used
light_programming_initial_on=15
# Turn the light off for below time before start programmig puleses.
light_programming_initial_off=12
# Try to use labels from Control Panel.
#use_panel_aux_labels=yes
# If you have a SWG, set this to yes. AqualinkD can only detect a SWG if it's on, so after a restart
# you will not see/access a SWG until the the next time the pump is on.
force_SWG = yes
# Please see forum for this, only set to yes when logging information to support
# new devices. Inflrmation will be written to /tmp/RS485.log & /tmp/RS485_raw.log respectively
debug_RSProtocol_packets = no
debug_RSProtocol_bytes = no
# Domoticz ID's for temps.
#air_temp_dzidx=13
#pool_water_temp_dzidx=14
#spa_water_temp_dzidx=15
#SWG_percent_dzidx=998
#SWG_PPM_dzidx=999
#SWG_percent_dzidx=153
#SWG_PPM_dzidx=152
#SWG_Status_dzidx=157
#
# | RS-6 Combo | RS-6 Only | RS-8 Combo | RS-2/6 Dual | RS-2/10 Dual | RS-16 Combo |
# --------------------------------------------------------------------------------------------
# Button_01 | Filter Pump | Filter Pump | Filter Pump | Filter Pump | Filter Pump | Filter Pump |
# Button_02 | Spa | Aux_1 | Spa | Spa | Spa | Spa |
# Button_03 | Aux 1 | Aux 2 | Aux 1 | Aux 1 | Aux 1 | Aux 1 |
# Button_04 | Aux 2 | Aux 3 | Aux 2 | Aux 2 | Aux 2 | Aux 2 |
# Button_05 | Aux 3 | Aux 4 | Aux 3 | Aux 3 | Aux 3 | Aux 3 |
# Button_06 | Aux 4 | Aux 5 | Aux 4 | Aux 4 | Aux 4 | Aux 4 |
# Button_07 | Aux 5 | Temp 1 | Aux 5 | Aux 5 | Aux 5 | Aux 5 |
# Button_08 | Pool Heater | Temp 2 | Aux 6 | Aux 6 | Aux 6 | Aux 6 |
# Button_09 | Spa Heater | Solar Heater | Aux 7 | Pool Heater | Aux B1 | Aux 7 |
# Button_10 | Solar Heater | | Pool Heater | Spa Heater | Aux B2 | Aux B1 |
# Button_11 | | | Spa Heater | Solar Heater | Aux B3 | Aux B2 |
# Button_12 | | | Solar Heater | | Aux B4 | Aux B3 |
# Button_13 | | | | | Pool Heater | Aux B4 |
# Button_14 | | | | | Spa Heater | Aux B5 |
# Button_15 | | | | | Solar Heater | Aux B6 |
# Button_16 | | | | | | Aux B7 |
# Button_17 | | | | | | Aux B8 |
# Button_18 | | | | | | Pool Heater |
# Button_19 | | | | | | Spa Heater |
# Button_20 | | | | | | Solar Heater |
#
# Pentair pump ID's
# 0x60 to 0x6F (0x60, 0x61 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F)
# Jandy pump ID's
# 0x78, 0x79, 0x7A, 0x7B
# button_xx_lightMode = (0=Aqualink program, 1=Jandy, 2=Jandy LED, 3=SAm/SAL, 4=Color Logic, 5=Intellibrite)
# Labels for standard butons (shown in web UI), and domoticz idx's
button_01_label=Filter Pump
#button_01_dzidx=37
button_01_pumpID=0x78
#button_01_PDA_label=FILTER PUMP
button_01_pumpIndex=1
button_02_label=Spa Mode
#button_02_dzidx=38
#button_02_pumpID= 0x78
#button_02_pumpIndex=3
#button_02_PDA_label=SPA
button_03_label=Aux1
#button_03_dzidx=39
button_03_pumpID=0x61
button_03_pumpIndex=3
button_04_label=Aux2
#button_04_dzidx=40
button_04_pumpID=0x60
button_04_pumpIndex=2
button_05_label=Aux3
#button_05_dzidx=41
button_05_lightMode=4
button_06_label=Aux4
#button_06_dzidx=42
#button_06_lightMode=1
#button_06_pumpIndex=4
button_07_label=Aux5
#button_07_label=NONE
#button_07_dzidx=43
#button_07_lightMode=0
button_08_label=Aux6
#button_09_label=Fountain
#button_10_label=Pool Heater
#button_10_dzidx=44
#button_11_label=Spa Heater
#button_11_dzidx=56
#button_12_label=Solar Heater
#button_12_label=Solar Heater
#button_12_dzidx=NONE
# RS-8 & RS-6 STOP HERE
# This is for RS-12 & RS-16 only.
#button_12_label=Aux B1
#button_13_label=
#button_14_label=
#button_15_label=
#button_16_label=
#button_17_label=Pool Heater
#button_18_label=Spa Heater
#button_19_label=Solar Heater

47
release/git_version.sh Executable file
View File

@ -0,0 +1,47 @@
#!/bin/bash
#SELF=${0##*/}
SELF=${0}
ME=`whoami`
CWD=`pwd`
GIT_HOST="tiger"
echo -n "Did you remember to 'make' and 'make slog' ? :"
read answer
if [ "$answer" = "n" ]; then
exit 0
fi
if ! [ "$HOSTNAME" = "$GIT_HOST" ]; then
ssh $ME@$GIT_HOST 'cd '"$CWD"'; '"$SELF"';'
else
command -v git >/dev/null 2>&1 || { echo >&2 "GIT is not installed. Aborting."; exit 1; }
VER=$(cat version.h | grep AQUALINKD_VERSION | cut -d\" -f2)
git tag v$VER
echo "Version set to $VER"
echo -n "Upload to github ? (y or n):"
read answer
if [ "$answer" = "y" ]; then
git add --all
if [ $? -ne 0 ]; then
echo "Error from running 'git add --all'"
exit 1
fi
git commit -m "Version $VER"
if [ $? -ne 0 ]; then
echo "Error from running 'git commit -m \"Version $VER\"'"
exit 1
fi
git push
fi
fi

233
release/home.conf Executable file
View File

@ -0,0 +1,233 @@
# aqualinkd.conf
#
# The directory where the web files are stored
crap_test = error
#web_directory=/var/www/aqualinkd/
web_directory=/nas/data/Development/Raspberry/AqualinkD/web
# Log to file, comment out if you do not want to log to file
#log_file=/var/log/aqualinkd.log
# The log level. [DEBUG, INFO, NOTICE, WARNING, ERROR]
# Pick the highest level, and all levels below will be sent to syslog.
# your syslog settings may be set to only display messages above a certian level
# in which case make sure you use the log_file settings to capture everything
# you want when debugging
# so, NOTICE also prints WARNING & ERROR
# DEBUG would print everything possible
#log_level=DEBUG_SERIAL
#log_level=DEBUG
#log_level=INFO
log_level=NOTICE
# AQUA_LOG 1
# NET_LOG 2
# AQRS_LOG 4
# ONET_LOG 8
# IAQT_LOG 16
# PDA_LOG 32
# RSSA_LOG 64
# DJAN_LOG 128
# DPEN_LOG 256
# RSSD_LOG 512
# PROG_LOG 1024
# DBGT_LOG 2048
# TIMR_LOG 4096
debug_log_mask = 1
#debug_log_mask = 2
#debug_log_mask = 4
#debug_log_mask = 8
#debug_log_mask = 16
#debug_log_mask = 64
#debug_log_mask = 256
#debug_log_mask = 128
#debug_log_mask = 512
debug_log_mask = 1024
#debug_log_mask = 2048
#debug_log_mask = 4096
display_warnings_in_web = yes
# The socket port that the daemon listens to
# If you change this from 80, remember to update aqualink.service.avahi
socket_port = 80
# The serial port the daemon access to read the Aqualink RS8
serial_port=/dev/ttyUSB0
#serial_port=/dev/null
override_freeze_protect = no
# mqtt stuff
mqtt_address = trident:1883
#mqtt_user = someusername
#mqtt_passwd = somepassword
mqtt_dz_pub_topic = domoticz/in
mqtt_dz_sub_topic = domoticz/out
mqtt_aq_topic = aqualinkd
mqtt_timed_update = yes
# The id of the Aqualink terminal device. Devices probed by RS8 master are:
# 08-0b, 10-13, 18-1b, 20-23, 28-2b, 30-33, 38-3b, 40-43
#
# Working RS 0x0a 0x0b 0x09 0x08
device_id=0x0a
#device_id=0xFF # For testing one touch, don't use kaypad
#device_id=0x00
#device_id=0x60
rssa_device_id=0x48
# The ID for extended settings to allow for faster programming
# VARIABLE SPEED PUMP are only supported with this option.
# Do not enable this if you don't use either, you'll just waste memory and cpu cycles
# Valid ID's are 0x40, 0x41, 0x42 & 0x43. for ONE Touch
# Valid ID's are 0x30, 0x31, 0x32 & 0x33. for Aqualink Touch
extended_device_id=0x40
##extended_device_id=0x31
# If you have extended_device_id set, then you can also use that ID for programming some features.
# This means that you can turn things on/off while AqualinkD is programming certian features.
# At the moment only heater setpoints & swg boost is on the extended device programming
#extended_device_id_programming = yes
#extended_device_id_programming = no
# Your RS panel size. ie 4, 6, 8, 12 or 16 relates to RS4, RS6, RS8, RS12 or RS16.
# VERY important that you select 12 or 16, if you have either of those size panels.
# Also don't think setting a 12 when you have a 8 will give you 4 more accessories to control, it won't the
# panel information is needed as different panels use different bits within the RS protocol for status and key
# presses.
#rs_panel_size = 12
panel_type = RS-6 Combo
#panel_type = PD-8 Combo
#panel_type = RS-16 Combo
#panel_type = RS-2/14 Dual
#panel_type = RS-4 Combo
#panel_type = RS-8 Only
# Read information from these devices directly from the RS485 bus as well as control panel.
read_RS485_swg = yes
#read_RS485_ePump = no
#read_RS485_vsfPump = no
# Not documented
serial_readahead_b4_write = yes
mqtt_timed_update = no
thread_netservices = yes
rs_poll_speed = -1
#pda_mode = yes
keep_paneltime_synced = yes
convert_mqtt_temp_to_c = yes
convert_dz_temp_to_c = yes
# by default use pool temp as spa temp when spa is off, enable below to report 0 as spa temp when off.
report_zero_spa_temp = yes
report_zero_pool_temp = no
# Light probramming mode. 0=safe mode, but slow.
# any number greater is seconds to wait between button presses.
# 0.4 seems to be the minimum. (workd for light modes below 10 presses)
# 0.6 seems to work about 95% of the time, but above 20 presses can be hit or miss.
# 0 will simply wait for the controler to send the response back before sending the next, so is equivelent to about 1.2
light_programming_mode=0
# Light programming assumes light needs to be on before sending pulse (above setting)
# If the light is off when request is made to change "light show", then the below value are used
light_programming_initial_on=15
# Turn the light off for below time before start programmig puleses.
light_programming_initial_off=12
# Try to use labels from Control Panel.
#use_panel_aux_labels=yes
# If you have a SWG, set this to yes. AqualinkD can only detect a SWG if it's on, so after a restart
# you will not see/access a SWG until the the next time the pump is on.
force_SWG = yes
#swg_zero_ignore_count = 10
enable_scheduler = yes
# Domoticz ID's for temps.
air_temp_dzidx=13
pool_water_temp_dzidx=14
spa_water_temp_dzidx=15
SWG_percent_dzidx=153
SWG_PPM_dzidx=152
SWG_Status_dzidx=157
#
# Pentair pump ID's
# 0x60 to 0x6F (0x60, 0x61 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F)
# Jandy pump ID's
# 0x78, 0x79, 0x7A, 0x7B
#
# | RS-6 Combo | RS-6 Only | RS-8 Combo | RS-2/6 Dual | RS-2/10 Dual | RS-16 Combo |
# --------------------------------------------------------------------------------------------
# Button_01 | Filter Pump | Filter Pump | Filter Pump | Filter Pump | Filter Pump | Filter Pump |
# Button_02 | Spa | Aux_1 | Spa | Spa | Spa | Spa |
# Button_03 | Aux 1 | Aux 2 | Aux 1 | Aux 1 | Aux 1 | Aux 1 |
# Button_04 | Aux 2 | Aux 3 | Aux 2 | Aux 2 | Aux 2 | Aux 2 |
# Button_05 | Aux 3 | Aux 4 | Aux 3 | Aux 3 | Aux 3 | Aux 3 |
# Button_06 | Aux 4 | Aux 5 | Aux 4 | Aux 4 | Aux 4 | Aux 4 |
# Button_07 | Aux 5 | Temp 1 | Aux 5 | Aux 5 | Aux 5 | Aux 5 |
# Button_08 | Pool Heater | Temp 2 | Aux 6 | Aux 6 | Aux 6 | Aux 6 |
# Button_09 | Spa Heater | Solar Heater | Aux 7 | Pool Heater | Aux B1 | Aux 7 |
# Button_10 | Solar Heater | | Pool Heater | Spa Heater | Aux B2 | Aux B1 |
# Button_11 | | | Spa Heater | Solar Heater | Aux B3 | Aux B2 |
# Button_12 | | | Solar Heater | | Aux B4 | Aux B3 |
# Button_13 | | | | | Pool Heater | Aux B4 |
# Button_14 | | | | | Spa Heater | Aux B5 |
# Button_15 | | | | | Solar Heater | Aux B6 |
# Button_16 | | | | | | Aux B7 |
# Button_17 | | | | | | Aux B8 |
# Button_18 | | | | | | Pool Heater |
# Button_19 | | | | | | Spa Heater |
# Button_20 | | | | | | Solar Heater |
# button_xx_lightMode = (0=Aqualink program, 1=Jandy, 2=Jandy LED, 3=SAm/SAL, 4=Color Logic, 5=Intellibrite)
# Labels for standard butons (shown in web UI), and domoticz idx's
button_01_label=Filter Pump
button_01_dzidx=37
button_02_label=Spa Mode
button_02_dzidx=38
button_03_label=Cleaner
button_03_dzidx=39
button_04_label=Waterfall
button_04_dzidx=40
button_05_label=Spa Blower
button_05_dzidx=41
button_06_label=Pool Light
button_06_dzidx=42
button_06_lightMode=0
button_07_label=NONE
button_8_label=Pool Heater
button_9_label=Spa Heater
button_10_label=NONE

170
release/home.pda.conf Normal file
View File

@ -0,0 +1,170 @@
# aqualinkd.conf
#
# The directory where the web files are stored
#web_directory=/var/www/aqualinkd/
web_directory=/nas/data/Development/Raspberry/AqualinkD/web
# Log to file, comment out if you do not want to log to file
#log_file=/var/log/aqualinkd.log
# The log level. [DEBUG, INFO, NOTICE, WARNING, ERROR]
# Pick the highest level, and all levels below will be sent to syslog.
# your syslog settings may be set to only display messages above a certian level
# in which case make sure you use the log_file settings to capture everything
# you want when debugging
# so, NOTICE also prints WARNING & ERROR
# DEBUG would print everything possible
#log_level=DEBUG_SERIAL
#log_level=DEBUG_SERIAL
#log_level=DEBUG
log_level=INFO
#log_level=NOTICE
#debug_log_mask = 32
display_warnings_in_web = yes
# The socket port that the daemon listens to
# If you change this from 80, remember to update aqualink.service.avahi
socket_port=88
# The serial port the daemon access to read the Aqualink RS8
serial_port=/dev/ttyUSB0
override_freeze_protect = no
# mqtt stuff
mqtt_address = trident:1883
#mqtt_user = someusername
#mqtt_passwd = somepassword
#mqtt_dz_pub_topic = domoticz/in
#mqtt_dz_sub_topic = domoticz/out
mqtt_aq_topic = aqualinkd-pda
# The id of the Aqualink terminal device. Devices probed by RS8 master are:
# 08-0b, 10-13, 18-1b, 20-23, 28-2b, 30-33, 38-3b, 40-43
#
# Working RS 0x0a 0x0b 0x09 0x08
panel_type = PD-6 Combo
#device_id=0x09
#device_id=0x00
#device_id=0x40
#rs_panel_size = 8
# PDA is 0x60
device_id=0x60
#pda_mode = yes
pda_sleep_mode = no
#use_PDA_auxiliary = yes
#read_pentair_packets = no
#read_all_devices = yes
convert_mqtt_temp_to_c = yes
convert_dz_temp_to_c = yes
force_SWG = yes
# by default use pool temp as spa temp when spa is off, enable below to report 0 as spa temp when off.
report_zero_spa_temp = yes
# Button inxed light probramming button is assigned to. (look at your button labels below)
light_programming_button = 6
# Light probramming mode. 0=safe mode, but slow.
# any number greater is seconds to wait between button presses.
# 0.4 seems to be the minimum. (workd for light modes below 10 presses)
# 0.6 seems to work about 95% of the time, but above 20 presses can be hit or miss.
# 0 will simply wait for the controler to send the response back before sending the next, so is equivelent to about 1.2
light_programming_mode=0
# Light programming assumes light needs to be on before sending pulse (above setting)
# If the light is off when request is made to change "light show", then the below value are used
light_programming_initial_on=15
# Turn the light off for below time before start programmig puleses.
light_programming_initial_off=12
# Try to use labels from Control Panel.
#use_panel_aux_labels=no
use_panel_aux_labels=yes
keep_paneltime_synced = yes
# If you have a SWG, set this to yes. AqualinkD can only detect a SWG if it's on, so after a restart
# you will not see/access a SWG until the the next time the pump is on.
#force_SWG = yes
# Domoticz ID's for temps.
air_temp_dzidx=13
pool_water_temp_dzidx=14
spa_water_temp_dzidx=15
#SWG_percent_dzidx=998
#SWG_PPM_dzidx=999
SWG_percent_dzidx=153
SWG_PPM_dzidx=152
SWG_Status_dzidx=157
#
# Pentair pump ID's
# 0x60 to 0x6F (0x60, 0x61 0x61, 0x62, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F)
# Jandy pump ID's
# 0x78, 0x79, 0x7A, 0x7B
# Labels for standard butons (shown in web UI), and domoticz idx's
button_01_label=Filter Pump
#button_01_dzidx=37
#button_01_pumpID=0x60
#button_01_PDA_label=FILTER PUMP
button_02_label=Spa
#button_02_dzidx=38
#button_02_PDA_label=SPA
button_03_label=Cleaner
#button_03_dzidx=39
#button_03_PDA_label=CLEANER
button_04_label=Waterfall
#button_04_dzidx=40
#button_04_pumpID=0x78
#button_04_PDA_label=WATERFALL
button_05_label=Spa Blower
#button_05_dzidx=41
#button_05_PDA_label=Pool Light
button_06_label=Light
#button_06_dzidx=42
#button_06_PDA_label=LIGHT
button_07_label=AUX5
#button_07_label=NONE
#button_07_dzidx=43
#button_07_PDA_label=AUX5
#button_08_label=NONE
#button_08_dzidx=NONE
#button_08_PDA_label=NONE
#button_09_label=NONE
#button_09_dzidx=NONE
#button_09_PDA_label=NONE
button_10_label=Pool Heat
#button_10_dzidx=44
#button_10_PDA_label=POOL HEAT
button_11_label=Spa Heat
#button_11_dzidx=56
#button_11_PDA_label=SPA HEAT
button_12_label=EXTRA AUX
#button_12_label=Solar Heater
#button_12_dzidx=NONE
#button_12_PDA_label=EXTRA AUX

View File

@ -61,6 +61,23 @@ if [ "$1" == "clean" ]; then
exit
fi
# Check cron.d options
if [ ! -d "/etc/cron.d" ]; then
echo "The version of Cron may not support chron.d, if so AqualinkD Scheduler will not work"
echo "Please check before starting"
else
if [ -f "/etc/default/cron" ]; then
CD=$(cat /etc/default/cron | grep -v ^# | grep "\-l")
if [ -z "$CD" ]; then
echo "Please enabled cron.d support, if not AqualinkD Scheduler will not work"
echo "Edit /etc/default/cron and look for the -l option, usually in EXTRA_OPTS"
fi
else
echo "Please make sure the version if Cron supports chron.d, if not the AqualinkD Scheduler will not work"
fi
fi
# copy files to locations, but only copy cfg if it doesn;t already exist
cp $BUILD/$BIN $BINLocation/$BIN

Binary file not shown.

BIN
rs-test Executable file

Binary file not shown.

1
rs-test.c Symbolic link
View File

@ -0,0 +1 @@
./touch/touch.c

BIN
rs_msg_utils.o Normal file

Binary file not shown.

View File

@ -31,10 +31,14 @@
#include "packetLogger.h"
#include "rs_msg_utils.h"
// Make us look lie config.c when we load config.h
#define CONFIG_C
#include "config.h"
#define SLOG_MAX 80
#define PACKET_MAX 600
#define VERSION "serial_logger V1.4"
#define VERSION "serial_logger V1.5"
/*
typedef enum used {
@ -45,12 +49,15 @@ typedef enum used {
*/
// Bogus config to keep aq_serial.c happy
/*
struct aqconfig
{
bool readahead_b4_write;
bool log_protocol_packets; // Read & Write as packets write to file
bool log_raw_bytes; // bytes read and write to file
};
struct aqconfig _aqconfig_;
*/
char _panelType[AQ_MSGLEN];
char _panelRev[AQ_MSGLEN];
@ -337,7 +344,6 @@ int main(int argc, char *argv[]) {
int received_packets = 0;
int logPackets = PACKET_MAX;
int logLevel = LOG_NOTICE;
bool rsRawDebug = false;
bool panleProbe = true;
bool rsSerialSpeedTest = false;
bool serialBlocking = true;
@ -353,8 +359,10 @@ int main(int argc, char *argv[]) {
//char buffer[256];
//bool idMode = true;
// Keep bogus crap happy for aq_serial.c
// aq_serial.c uses the following
_aqconfig_.readahead_b4_write = false;
_aqconfig_.log_protocol_packets = false;
_aqconfig_.log_raw_bytes = false;
printf("AqualinkD %s\n",VERSION);
@ -373,7 +381,8 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "\t-i <ID> (just log these ID's, can use multiple -i)\n");
fprintf(stderr, "\t-r (raw)\n");
fprintf(stderr, "\t-s (Serial Speed Test / OS caching issues)\n");
fprintf(stderr, "\t-rsrd (log raw RS bytes to %s)\n",RS485BYTELOGFILE);
fprintf(stderr, "\t-lpack (log RS packets to %s)\n",RS485LOGFILE);
fprintf(stderr, "\t-lrawb (log raw RS bytes to %s)\n",RS485BYTELOGFILE);
fprintf(stderr, "\t-e (monitor errors)\n");
fprintf(stderr, "\nie:\t%s /dev/ttyUSB0 -d -p 1000 -i 0x08 -i 0x0a\n\n", argv[0]);
return 1;
@ -397,8 +406,10 @@ int main(int argc, char *argv[]) {
logLevel = LOG_DEBUG;
} else if (strcmp(argv[i], "-f") == 0) {
_playback_file = true;
} else if (strcmp(argv[i], "-rsrd") == 0) {
rsRawDebug = true;
} else if (strcmp(argv[i], "-lpack") == 0) {
_aqconfig_.log_protocol_packets = true;
} else if (strcmp(argv[i], "-lrawb") == 0) {
_aqconfig_.log_raw_bytes = true;
} else if (strcmp(argv[i], "-n") == 0) {
panleProbe = false;
} else if (strcmp(argv[i], "-s") == 0) {
@ -433,9 +444,17 @@ int main(int argc, char *argv[]) {
} else {
LOG(RSSD_LOG, LOG_NOTICE, "Logging serial errors!\n");
}
if (_aqconfig_.log_protocol_packets)
LOG(RSSD_LOG, LOG_NOTICE, "Logging packets to %s!\n",RS485LOGFILE);
if (_aqconfig_.log_raw_bytes)
LOG(RSSD_LOG, LOG_NOTICE, "Logging raw bytes to %s!\n",RS485BYTELOGFILE);
if (logLevel < LOG_DEBUG && errorMonitor==false )
printf("Please wait.");
startPacketLogger();
//startPacketLogging(true,true);
clock_gettime(CLOCK_REALTIME, &start_time);
while (_keepRunning == true) {
@ -443,11 +462,7 @@ int main(int argc, char *argv[]) {
LOG(RSSD_LOG, LOG_ERR, "ERROR, serial port disconnect\n");
}
//packet_length = get_packet(rs_fd, packet_buffer);
if (rsRawDebug)
packet_length = get_packet_lograw(rs_fd, packet_buffer);
else
packet_length = get_packet(rs_fd, packet_buffer);
packet_length = get_packet(rs_fd, packet_buffer);
if (packet_length == AQSERR_READ) {
// Unrecoverable read error. Force an attempt to reconnect.
@ -461,7 +476,7 @@ int main(int argc, char *argv[]) {
// Error condition
if (errorMonitor && last_packet_length > 0) { // Error packet wwould have already been printed.
char buff[900];
beautifyPacket(buff, last_packet_buffer, last_packet_length);
beautifyPacket(buff, last_packet_buffer, last_packet_length, true);
LOG(RSSD_LOG, LOG_NOTICE, "Previous packet (before error)\n");
LOG(RSSD_LOG, LOG_NOTICE, "%s------------------------------\n",buff);
//LOG(RSSD_LOG, LOG_NOTICE, "\n");
@ -541,9 +556,6 @@ int main(int argc, char *argv[]) {
// NSF
// Test Serial speed & caching
if (rsSerialSpeedTest) {
if (rsRawDebug)
packet_length = get_packet_lograw(rs_fd, packet_buffer);
else
packet_length = get_packet(rs_fd, packet_buffer);
if (packet_length > 0 && packet_buffer[PKT_DEST] != 0x00) {
@ -573,6 +585,8 @@ int main(int argc, char *argv[]) {
clock_gettime(CLOCK_REALTIME, &end_time);
stopPacketLogger();
if (errorMonitor) {
return 0;
}

BIN
serial_logger.o Normal file

Binary file not shown.

BIN
serialadapter.o Normal file

Binary file not shown.

BIN
test Executable file

Binary file not shown.

287
test.c Normal file
View File

@ -0,0 +1,287 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stdbool.h>
typedef enum cfg_type{cfg_ptr, cfg_str, cfg_int, cfg_bool, cfg_hex, cfg_float, cfg_loglevel} cfg_type;
struct cfg_line {
cfg_type type;
void **value;
void *value_ptr;
char *description;
//char name[20];
char *name;
int size;
};
struct test_cfg
{
char *p_val;
char c_val[30];
bool b_val;
//int b_val;
int i_val;
float f_val;
unsigned char uc_val;
unsigned int ul_val;
};
//struct cfg_line _cfg_lines[];
struct cfg_line *_cfg_lines;
void test_cfg_init (struct test_cfg *config_parameters);
void test_print_cfg();
void test_write_tocfg();
void test_free_cfg();
void set_cfg_param(struct cfg_line *cfg, char *name, void *ptr, cfg_type type, char *des);
void test_cfg_init (struct test_cfg *config_parameters) {
config_parameters->p_val = "Initial p_val setting";
strcpy(config_parameters->c_val, "Initial c_val setting");
config_parameters->b_val = false;
config_parameters->i_val = 10;
config_parameters->f_val = 0.01;
config_parameters->ul_val = 5;
config_parameters->uc_val = 0x9a;
set_cfg_param(&_cfg_lines[__COUNTER__], "pointer test", &config_parameters->p_val, cfg_ptr, "pointer test description");
set_cfg_param(&_cfg_lines[__COUNTER__], "char test", &config_parameters->c_val, cfg_str, "pointer char description");
set_cfg_param(&_cfg_lines[__COUNTER__], "int test", &config_parameters->i_val, cfg_int, "int test description");
set_cfg_param(&_cfg_lines[__COUNTER__], "float test", &config_parameters->f_val, cfg_float, "float test description");
set_cfg_param(&_cfg_lines[__COUNTER__], "ulong test", &config_parameters->ul_val, cfg_loglevel, "ulong test description");
set_cfg_param(&_cfg_lines[__COUNTER__], "hex test", &config_parameters->uc_val, cfg_hex, "hex test description");
set_cfg_param(&_cfg_lines[__COUNTER__], "bool test", &config_parameters->b_val, cfg_bool, "bool test description");
printf("Config init %30s = %s\n",_cfg_lines[0].name,(char *)*_cfg_lines[0].value);
printf("Config init %30s = %s\n",_cfg_lines[1].name,(char *)_cfg_lines[1].value);
printf("Config init %30s = %d\n",_cfg_lines[2].name,*((int *)_cfg_lines[2].value));
printf("Config init %30s = %.3f\n",_cfg_lines[3].name,*((float *)_cfg_lines[3].value));
printf("Config init %30s = %lu\n",_cfg_lines[4].name,*((unsigned long *)_cfg_lines[4].value));
printf("Config init %30s = 0x%02hhx\n",_cfg_lines[5].name,*((unsigned char *)_cfg_lines[5].value));
printf("Config init %30s = %d\n",_cfg_lines[6].name,*((bool *)_cfg_lines[6].value));
//printf("Config init %30s = %d | %s\n",_cfg_lines[6].name,*((bool *)*_cfg_lines[6].value),*((bool *)*_cfg_lines[6].value)==true?"True":"False");
}
#define CFG_LEN __COUNTER__
#include "utils.h"
int main(int argc, char *argv[]) {
int ppm = 2900;
printf("Roundf %f\n",roundf(degFtoC(ppm)));
printf("Convert %f\n",degFtoC(ppm));
printf("Round %d\n",round(degFtoC(ppm)));
return 0;
struct test_cfg config_parameters;
/*
int num = 5;
void *ptr = &num;
void **xptr = &ptr;
printf ("values:%d\n", *((int *)*xptr));
*/
_cfg_lines = (struct cfg_line*)malloc(sizeof(struct cfg_line) * CFG_LEN );
bool val = false;
void *pval;
void **ppval;
pval = &val;
ppval = &pval;
printf("%d %d %d\n",val,*((bool *)pval),*((bool *)*ppval));
val = true;
printf("%d %d %d\n",val,*((bool *)pval),*((bool *)*ppval));
*((bool *)*ppval) = false;
printf("%d %d %d\n",val,*((bool *)pval),*((bool *)*ppval));
*((bool *)*ppval) = true;
printf("%d %d %d\n",val,*((bool *)pval),*((bool *)*ppval));
int ival = 11;
void *pival;
void **ppival;
pival = &ival;
ppival = &pival;
printf("%d %d %d\n",ival,*((int *)pival),*((int *)*ppival));
test_cfg_init(&config_parameters);
printf("b_val = %d\n",config_parameters.b_val);
//printf("b_val ptr = %d\n",_cfg_lines[6].value);
//printf("b_val ptr = %d\n",&_cfg_lines[6].value);
//printf("b_val ptr = %d\n",*_cfg_lines[6].value);
//printf("b_val ptrs = %p %p %p\n",config_parameters.b_val,_cfg_lines[6].value_ptr,_cfg_lines[6].value);
//return 0;
//printf("c_val = %s\n",config_parameters.p_val);
//printf("p_val = %s\n",config_parameters.p_val);
//printf("c_val = %s\n",_cfg_lines[0].value);
//printf("c_val = %s\n",&_cfg_lines[0].value);
//printf("c_val = %s\n",*_cfg_lines[0].value);
//config_parameters.b_val = true;
//printf("c_val = %s\n",&(*_cfg_lines[0].value)[0]);
test_print_cfg();
config_parameters.b_val = true;
strcpy(config_parameters.c_val, "Changed c_val setting");
config_parameters.p_val = "Changed p_val setting";
config_parameters.f_val = 1.02;
config_parameters.i_val = 11;
config_parameters.uc_val = 0x0b;
config_parameters.ul_val = 6;
printf("********* Changed by cfg**************\n");
printf("b_val direct set to true= %d\n",config_parameters.b_val);
test_print_cfg();
test_write_tocfg();
printf("b_val test_write_tocfg() = %d\n",config_parameters.b_val);
printf("********* Changed by cfg address **************\n");
// *_cfg_lines[0].value = "FINALLY";
test_print_cfg();
printf("********* actual **************\n");
printf("p_val = %s\n",config_parameters.p_val);
printf("c_val = %s\n",config_parameters.c_val);
printf("i_val = %d\n",config_parameters.i_val);
printf("f_val = %.2f\n",config_parameters.f_val);
printf("ul_val = %u\n",config_parameters.ul_val);
printf("uc_val = 0x%02hhx\n",config_parameters.uc_val);
printf("bool_val = %d\n",config_parameters.b_val);
test_free_cfg();
return 0;
}
void test_free_cfg()
{
int i;
for (i=0; i < CFG_LEN; i++) {
if (_cfg_lines[i].name == NULL) {
continue;
}
if (_cfg_lines[i].type == cfg_ptr ) {
printf("Free: %s\n",_cfg_lines[i].name);
free(*_cfg_lines[i].value);
}
}
}
void test_write_tocfg()
{
int i;
for (i=0; i < CFG_LEN; i++) {
if (_cfg_lines[i].name == NULL) {
continue;
}
switch (_cfg_lines[i].type) {
case cfg_str:
sprintf((char *)_cfg_lines[i].value, "Last value");
break;
case cfg_ptr:
{
char *val = malloc(sizeof(char *)*50);
sprintf(val, "xxxxx");
*_cfg_lines[i].value = val;
}
break;
case cfg_int:
*((int *)_cfg_lines[i].value) = 12;
break;
case cfg_hex:
*((unsigned char *)_cfg_lines[i].value) = 0x78;
break;
case cfg_float:
*((float *)_cfg_lines[i].value) = 999.99;
break;
case cfg_loglevel:
*((unsigned long *)_cfg_lines[i].value) = 98;
break;
case cfg_bool:
*((bool *)_cfg_lines[i].value) = false;
break;
}
}
}
//void set_cfg_param(struct cfg_line *cfg, void *ptr, char *name, char *des, cfg_type type) {
void set_cfg_param(struct cfg_line *cfg, char *name, void *ptr, cfg_type type, char *des) {
cfg->type = type;
cfg->name = name;
cfg->description = des;
switch (type) {
case cfg_ptr:
cfg->value = ptr;
break;
case cfg_str:
case cfg_bool:
case cfg_int:
case cfg_float:
case cfg_loglevel:
case cfg_hex:
cfg->value_ptr = ptr;
cfg->value = cfg->value_ptr;
break;
}
}
void test_print_cfg()
{
int i;
for (i=0; i < CFG_LEN; i++) {
if (_cfg_lines[i].name == NULL) {
continue;
}
switch (_cfg_lines[i].type) {
case cfg_bool:
printf("Config %30s = %d | %s\n",_cfg_lines[i].name,*((bool *)_cfg_lines[i].value),*((bool *)_cfg_lines[i].value)==true?"True":"False");
break;
case cfg_str:
printf("Config %30s = %s\n",_cfg_lines[i].name,(char *)_cfg_lines[i].value);
break;
case cfg_ptr:
printf("Config %30s = %s\n",_cfg_lines[i].name,(char *)*_cfg_lines[i].value);
break;
case cfg_int:
printf("Config %30s = %d\n",_cfg_lines[i].name,*((int *)_cfg_lines[i].value));
break;
case cfg_hex:
printf("Config %30s = 0x%02hhx\n",_cfg_lines[i].name,*((unsigned char *)_cfg_lines[i].value));
break;
case cfg_float:
printf("Config %30s = %.2f\n",_cfg_lines[i].name,*((float *)_cfg_lines[i].value));
break;
case cfg_loglevel:
printf("Config %30s = %lu\n",_cfg_lines[i].name,*((unsigned long *)_cfg_lines[i].value));
break;
}
}
}

794
total_buttons Normal file
View File

@ -0,0 +1,794 @@
aq_panel.c: aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[7-1];
aq_panel.c: aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[index].label = rs?name2label(BTN_PUMP):BTN_PDA_PUMP;
aq_panel.c: aqdata->aqbuttons[index].name = BTN_PUMP;
aq_panel.c: aqdata->aqbuttons[index].code = KEY_PUMP;
aq_panel.c: aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[index].special_mask = 0;
aq_panel.c: aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[6-1];
aq_panel.c: aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[index].label = rs?name2label(BTN_SPA):BTN_PDA_SPA;
aq_panel.c: aqdata->aqbuttons[index].name = BTN_SPA;
aq_panel.c: aqdata->aqbuttons[index].code = KEY_SPA;
aq_panel.c: aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[index].special_mask = 0;
aq_panel.c: aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[5-1];
aq_panel.c: aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[index].label = rs?name2label(BTN_AUX1):BTN_PDA_AUX1;
aq_panel.c: aqdata->aqbuttons[index].name = BTN_AUX1;
aq_panel.c: aqdata->aqbuttons[index].code = KEY_AUX1;
aq_panel.c: aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[index].special_mask = 0;
aq_panel.c: aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[4-1];
aq_panel.c: aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[index].label = rs?name2label(BTN_AUX2):BTN_PDA_AUX2;
aq_panel.c: aqdata->aqbuttons[index].name = BTN_AUX2;
aq_panel.c: aqdata->aqbuttons[index].code = KEY_AUX2;
aq_panel.c: aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[index].special_mask = 0;
aq_panel.c: aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[3-1];
aq_panel.c: aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[index].label = rs?name2label(BTN_AUX3):BTN_PDA_AUX3;
aq_panel.c: aqdata->aqbuttons[index].name = BTN_AUX3;
aq_panel.c: aqdata->aqbuttons[index].code = KEY_AUX3;
aq_panel.c: aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[index].special_mask = 0;
aq_panel.c: aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[9-1];
aq_panel.c: aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[index].label = rs?name2label(BTN_AUX4):BTN_PDA_AUX4;
aq_panel.c: aqdata->aqbuttons[index].name = BTN_AUX4;
aq_panel.c: aqdata->aqbuttons[index].code = KEY_AUX4;
aq_panel.c: aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[index].special_mask = 0;
aq_panel.c: aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[8-1];
aq_panel.c: aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[index].label = rs?name2label(BTN_AUX5):BTN_PDA_AUX5;
aq_panel.c: aqdata->aqbuttons[index].name = BTN_AUX5;
aq_panel.c: aqdata->aqbuttons[index].code = KEY_AUX5;
aq_panel.c: aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[index].special_mask = 0;
aq_panel.c: aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[12-1];
aq_panel.c: aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[index].label = rs?name2label(BTN_AUX6):BTN_PDA_AUX6;
aq_panel.c: aqdata->aqbuttons[index].name = BTN_AUX6;
aq_panel.c: aqdata->aqbuttons[index].code = KEY_AUX6;
aq_panel.c: aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[index].special_mask = 0;
aq_panel.c: aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[1-1];
aq_panel.c: aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[index].label = rs?name2label(BTN_AUX7):BTN_PDA_AUX7;
aq_panel.c: aqdata->aqbuttons[index].name = BTN_AUX7;
aq_panel.c: aqdata->aqbuttons[index].code = KEY_AUX7;
aq_panel.c: aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[index].special_mask = 0;
aq_panel.c: aqdata->aqbuttons[index-4].led = &aqdata->aqualinkleds[2-1]; // Change
aq_panel.c: aqdata->aqbuttons[index-4].code = KEY_RS16_AUX4;
aq_panel.c: aqdata->aqbuttons[index-3].led = &aqdata->aqualinkleds[11-1]; // Change
aq_panel.c: aqdata->aqbuttons[index-3].code = KEY_RS16_AUX5;
aq_panel.c: aqdata->aqbuttons[index-2].led = &aqdata->aqualinkleds[10-1]; // Change
aq_panel.c: aqdata->aqbuttons[index-2].code = KEY_RS16_AUX6;
aq_panel.c: aqdata->aqbuttons[index-1].led = &aqdata->aqualinkleds[9-1]; // change
aq_panel.c: aqdata->aqbuttons[index-1].code = KEY_RS16_AUX7;
aq_panel.c: aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[8-1];
aq_panel.c: aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[index].label = name2label(BTN_AUXB1); // AUX8
aq_panel.c: aqdata->aqbuttons[index].name = BTN_AUXB1;
aq_panel.c: aqdata->aqbuttons[index].code = KEY_AUXB1;
aq_panel.c: aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[index].special_mask = 0;
aq_panel.c: aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[12-1];
aq_panel.c: aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[index].label = name2label(BTN_AUXB2); // AUX9
aq_panel.c: aqdata->aqbuttons[index].name = BTN_AUXB2;
aq_panel.c: aqdata->aqbuttons[index].code = KEY_AUXB2;
aq_panel.c: aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[index].special_mask = 0;
aq_panel.c: aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[1-1];
aq_panel.c: aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[index].label = name2label(BTN_AUXB3); // AUX10
aq_panel.c: aqdata->aqbuttons[index].name = BTN_AUXB3;
aq_panel.c: aqdata->aqbuttons[index].code = KEY_AUXB3;
aq_panel.c: aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[index].special_mask = 0;
aq_panel.c: aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[13-1];
aq_panel.c: aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[index].label = name2label(BTN_AUXB4); // AUX11
aq_panel.c: aqdata->aqbuttons[index].name = BTN_AUXB4;
aq_panel.c: aqdata->aqbuttons[index].code = KEY_AUXB4;
aq_panel.c: aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[index].special_mask = 0;
aq_panel.c: aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[21-1]; // doesn't actually exist
aq_panel.c: aqdata->aqbuttons[index].led->state = OFF; // Since there is no LED in data, set to off and allow messages to turn it on
aq_panel.c: aqdata->aqbuttons[index].label = name2label(BTN_AUXB5);
aq_panel.c: aqdata->aqbuttons[index].name = BTN_AUXB5;
aq_panel.c: aqdata->aqbuttons[index].code = KEY_AUXB5;
aq_panel.c: aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[index].special_mask = 0;
aq_panel.c: aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[22-1]; // doesn't actually exist
aq_panel.c: aqdata->aqbuttons[index].led->state = OFF; // Since there is no LED in data, set to off and allow messages to turn it on
aq_panel.c: aqdata->aqbuttons[index].label = name2label(BTN_AUXB6);
aq_panel.c: aqdata->aqbuttons[index].name = BTN_AUXB6;
aq_panel.c: aqdata->aqbuttons[index].code = KEY_AUXB6;
aq_panel.c: aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[index].special_mask = 0;
aq_panel.c: aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[23-1]; // doesn't actually exist
aq_panel.c: aqdata->aqbuttons[index].led->state = OFF; // Since there is no LED in data, set to off and allow messages to turn it on
aq_panel.c: aqdata->aqbuttons[index].label = name2label(BTN_AUXB7);
aq_panel.c: aqdata->aqbuttons[index].name = BTN_AUXB7;
aq_panel.c: aqdata->aqbuttons[index].code = KEY_AUXB7;
aq_panel.c: aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[24-1]; // doesn't actually exist
aq_panel.c: aqdata->aqbuttons[index].led->state = OFF; // Since there is no LED in data, set to off and allow messages to turn it on
aq_panel.c: aqdata->aqbuttons[index].label = name2label(BTN_AUXB8);
aq_panel.c: aqdata->aqbuttons[index].name = BTN_AUXB8;
aq_panel.c: aqdata->aqbuttons[index].code = KEY_AUXB8;
aq_panel.c: aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[index].special_mask = 0;
aq_panel.c: aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[12-1];
aq_panel.c: aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[index].label = name2label(BTN_AUX6);
aq_panel.c: aqdata->aqbuttons[index].name = BTN_AUX6;
aq_panel.c: aqdata->aqbuttons[index].code = KEY_AUX6;
aq_panel.c: aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[index].special_mask = 0;
aq_panel.c: aqdata->aqbuttons[i].name = aqdata->aqbuttons[i+1].name;
aq_panel.c: aqdata->aqbuttons[i].label = aqdata->aqbuttons[i+1].label;
aq_panel.c: aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[15-1];
aq_panel.c: aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[index].label = rs?name2label(combo?BTN_POOL_HTR:BTN_TEMP1_HTR):BTN_PDA_POOL_HTR;
aq_panel.c: aqdata->aqbuttons[index].name = BTN_POOL_HTR;
aq_panel.c: aqdata->aqbuttons[index].code = KEY_POOL_HTR;
aq_panel.c: aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[index].special_mask = 0;
aq_panel.c: aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[17-1];
aq_panel.c: aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[index].label = rs?name2label(combo?BTN_SPA_HTR:BTN_TEMP2_HTR):BTN_PDA_SPA_HTR;
aq_panel.c: aqdata->aqbuttons[index].name = BTN_SPA_HTR;
aq_panel.c: aqdata->aqbuttons[index].code = KEY_SPA_HTR;
aq_panel.c: aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[index].special_mask = 0;
aq_panel.c: aqdata->aqbuttons[index].led = &aqdata->aqualinkleds[19-1];
aq_panel.c: aqdata->aqbuttons[index].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[index].label = rs?name2label(BTN_SOLAR_HTR):BTN_PDA_SOLAR_HTR;
aq_panel.c: aqdata->aqbuttons[index].name = BTN_SOLAR_HTR;
aq_panel.c: aqdata->aqbuttons[index].code = KEY_SOLAR_HTR;
aq_panel.c: aqdata->aqbuttons[index].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[index].special_mask = 0;
aq_panel.c: aqdata->total_buttons = index;
aq_panel.c: //aqdata->single_device = !combo;
aq_panel.c: aqdata->rs16_vbutton_start = 13 - (combo?0:1);
aq_panel.c: aqdata->rs16_vbutton_end = 16 - (combo?0:1);
aq_panel.c: aqdata->pool_heater_index = index-3;
aq_panel.c: aqdata->spa_heater_index = index-2;
aq_panel.c: aqdata->solar_heater_index = index-1;
aq_panel.c: aqdata->aqbuttons[0].led = &aqdata->aqualinkleds[7-1];
aq_panel.c: aqdata->aqbuttons[0].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[0].label = name2label(BTN_PUMP);
aq_panel.c: aqdata->aqbuttons[0].name = BTN_PUMP;
aq_panel.c: aqdata->aqbuttons[0].code = KEY_PUMP;
aq_panel.c: aqdata->aqbuttons[0].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[1].led = &aqdata->aqualinkleds[6-1];
aq_panel.c: aqdata->aqbuttons[1].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[1].label = name2label(BTN_SPA);
aq_panel.c: aqdata->aqbuttons[1].name = BTN_SPA;
aq_panel.c: aqdata->aqbuttons[1].code = KEY_SPA;
aq_panel.c: aqdata->aqbuttons[1].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[2].led = &aqdata->aqualinkleds[5-1];
aq_panel.c: aqdata->aqbuttons[2].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[2].label = name2label(BTN_AUX1);
aq_panel.c: aqdata->aqbuttons[2].name = BTN_AUX1;
aq_panel.c: aqdata->aqbuttons[2].code = KEY_AUX1;
aq_panel.c: aqdata->aqbuttons[2].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[3].led = &aqdata->aqualinkleds[4-1];
aq_panel.c: aqdata->aqbuttons[3].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[3].label = name2label(BTN_AUX2);
aq_panel.c: aqdata->aqbuttons[3].name = BTN_AUX2;
aq_panel.c: aqdata->aqbuttons[3].code = KEY_AUX2;
aq_panel.c: aqdata->aqbuttons[3].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[4].led = &aqdata->aqualinkleds[3-1];
aq_panel.c: aqdata->aqbuttons[4].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[4].label = name2label(BTN_AUX3);
aq_panel.c: aqdata->aqbuttons[4].name = BTN_AUX3;
aq_panel.c: aqdata->aqbuttons[4].code = KEY_AUX3;
aq_panel.c: aqdata->aqbuttons[4].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[5].led = &aqdata->aqualinkleds[9-1];
aq_panel.c: aqdata->aqbuttons[5].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[5].label = name2label(BTN_AUX4);
aq_panel.c: aqdata->aqbuttons[5].name = BTN_AUX4;
aq_panel.c: aqdata->aqbuttons[5].code = KEY_AUX4;
aq_panel.c: aqdata->aqbuttons[5].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[6].led = &aqdata->aqualinkleds[8-1];
aq_panel.c: aqdata->aqbuttons[6].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[6].label = name2label(BTN_AUX5);
aq_panel.c: aqdata->aqbuttons[6].name = BTN_AUX5;
aq_panel.c: aqdata->aqbuttons[6].code = KEY_AUX5;
aq_panel.c: aqdata->aqbuttons[6].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[7].led = &aqdata->aqualinkleds[12-1];
aq_panel.c: aqdata->aqbuttons[7].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[7].label = name2label(BTN_AUX6);
aq_panel.c: aqdata->aqbuttons[7].name = BTN_AUX6;
aq_panel.c: aqdata->aqbuttons[7].code = KEY_AUX6;
aq_panel.c: aqdata->aqbuttons[7].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[8].led = &aqdata->aqualinkleds[1-1];
aq_panel.c: aqdata->aqbuttons[8].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[8].label = name2label(BTN_AUX7);
aq_panel.c: aqdata->aqbuttons[8].name = BTN_AUX7;
aq_panel.c: aqdata->aqbuttons[8].code = KEY_AUX7;
aq_panel.c: aqdata->aqbuttons[8].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[9].led = &aqdata->aqualinkleds[15-1];
aq_panel.c: aqdata->aqbuttons[9].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[9].label = name2label(BTN_POOL_HTR);
aq_panel.c: aqdata->aqbuttons[9].name = BTN_POOL_HTR;
aq_panel.c: aqdata->aqbuttons[9].code = KEY_POOL_HTR;
aq_panel.c: aqdata->aqbuttons[9].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[10].led = &aqdata->aqualinkleds[17-1];
aq_panel.c: aqdata->aqbuttons[10].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[10].label = name2label(BTN_SPA_HTR);
aq_panel.c: aqdata->aqbuttons[10].name = BTN_SPA_HTR;
aq_panel.c: aqdata->aqbuttons[10].code = KEY_SPA_HTR;
aq_panel.c: aqdata->aqbuttons[10].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[11].led = &aqdata->aqualinkleds[19-1];
aq_panel.c: aqdata->aqbuttons[11].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[11].label = name2label(BTN_SOLAR_HTR);
aq_panel.c: aqdata->aqbuttons[11].name = BTN_SOLAR_HTR;
aq_panel.c: aqdata->aqbuttons[11].code = KEY_SOLAR_HTR;
aq_panel.c: aqdata->aqbuttons[11].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[0].pda_label = BTN_PDA_PUMP;
aq_panel.c: aqdata->aqbuttons[1].pda_label = BTN_PDA_SPA;
aq_panel.c: aqdata->aqbuttons[2].pda_label = BTN_PDA_AUX1;
aq_panel.c: aqdata->aqbuttons[3].pda_label = BTN_PDA_AUX2;
aq_panel.c: aqdata->aqbuttons[4].pda_label = BTN_PDA_AUX3;
aq_panel.c: aqdata->aqbuttons[5].pda_label = BTN_PDA_AUX4;
aq_panel.c: aqdata->aqbuttons[6].pda_label = BTN_PDA_AUX5;
aq_panel.c: aqdata->aqbuttons[7].pda_label = BTN_PDA_AUX6;
aq_panel.c: aqdata->aqbuttons[7].pda_label = BTN_PDA_AUX6;
aq_panel.c: aqdata->aqbuttons[8].pda_label = BTN_PDA_AUX7;
aq_panel.c: aqdata->aqbuttons[9].pda_label = BTN_PDA_POOL_HTR;
aq_panel.c: aqdata->aqbuttons[10].pda_label = BTN_PDA_SPA_HTR;
aq_panel.c: aqdata->aqbuttons[11].pda_label = BTN_PDA_SOLAR_HTR;
aq_panel.c: aqdata->aqbuttons[5].led = &aqdata->aqualinkleds[2-1]; // Change
aq_panel.c: aqdata->aqbuttons[5].code = KEY_RS16_AUX4;
aq_panel.c: aqdata->aqbuttons[6].led = &aqdata->aqualinkleds[11-1]; // Change
aq_panel.c: aqdata->aqbuttons[6].code = KEY_RS16_AUX5;
aq_panel.c: aqdata->aqbuttons[7].led = &aqdata->aqualinkleds[10-1]; // Change
aq_panel.c: aqdata->aqbuttons[7].code = KEY_RS16_AUX6;
aq_panel.c: aqdata->aqbuttons[8].led = &aqdata->aqualinkleds[9-1]; // change
aq_panel.c: aqdata->aqbuttons[8].code = KEY_RS16_AUX7;
aq_panel.c: aqdata->aqbuttons[9].led = &aqdata->aqualinkleds[8-1];
aq_panel.c: aqdata->aqbuttons[9].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[9].label = name2label(BTN_AUXB1); // AUX8
aq_panel.c: aqdata->aqbuttons[9].name = BTN_AUXB1;
aq_panel.c: aqdata->aqbuttons[9].code = KEY_AUXB1;
aq_panel.c: aqdata->aqbuttons[9].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[10].led = &aqdata->aqualinkleds[12-1];
aq_panel.c: aqdata->aqbuttons[10].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[10].label = name2label(BTN_AUXB2); // AUX9
aq_panel.c: aqdata->aqbuttons[10].name = BTN_AUXB2;
aq_panel.c: aqdata->aqbuttons[10].code = KEY_AUXB2;
aq_panel.c: aqdata->aqbuttons[10].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[11].led = &aqdata->aqualinkleds[1-1];
aq_panel.c: aqdata->aqbuttons[11].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[11].label = name2label(BTN_AUXB3); // AUX10
aq_panel.c: aqdata->aqbuttons[11].name = BTN_AUXB3;
aq_panel.c: aqdata->aqbuttons[11].code = KEY_AUXB3;
aq_panel.c: aqdata->aqbuttons[11].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[12].led = &aqdata->aqualinkleds[13-1];
aq_panel.c: aqdata->aqbuttons[12].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[12].label = name2label(BTN_AUXB4); // AUX11
aq_panel.c: aqdata->aqbuttons[12].name = BTN_AUXB4;
aq_panel.c: aqdata->aqbuttons[12].code = KEY_AUXB4;
aq_panel.c: aqdata->aqbuttons[12].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[13].led = &aqdata->aqualinkleds[21-1]; // doesn't actually exist
aq_panel.c: aqdata->aqbuttons[13].led->state = OFF; // Since there is no LED in data, set to off and allow messages to turn it on
aq_panel.c: aqdata->aqbuttons[13].label = name2label(BTN_AUXB5);
aq_panel.c: aqdata->aqbuttons[13].name = BTN_AUXB5;
aq_panel.c: aqdata->aqbuttons[13].code = KEY_AUXB5;
aq_panel.c: aqdata->aqbuttons[13].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[14].led = &aqdata->aqualinkleds[22-1]; // doesn't actually exist
aq_panel.c: aqdata->aqbuttons[14].led->state = OFF; // Since there is no LED in data, set to off and allow messages to turn it on
aq_panel.c: aqdata->aqbuttons[14].label = name2label(BTN_AUXB6);
aq_panel.c: aqdata->aqbuttons[14].name = BTN_AUXB6;
aq_panel.c: aqdata->aqbuttons[14].code = KEY_AUXB6;
aq_panel.c: aqdata->aqbuttons[14].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[15].led = &aqdata->aqualinkleds[23-1]; // doesn't actually exist
aq_panel.c: aqdata->aqbuttons[15].led->state = OFF; // Since there is no LED in data, set to off and allow messages to turn it on
aq_panel.c: aqdata->aqbuttons[15].label = name2label(BTN_AUXB7);
aq_panel.c: aqdata->aqbuttons[15].name = BTN_AUXB7;
aq_panel.c: aqdata->aqbuttons[15].code = KEY_AUXB7;
aq_panel.c: aqdata->aqbuttons[15].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[16].led = &aqdata->aqualinkleds[24-1]; // doesn't actually exist
aq_panel.c: aqdata->aqbuttons[16].led->state = OFF; // Since there is no LED in data, set to off and allow messages to turn it on
aq_panel.c: aqdata->aqbuttons[16].label = name2label(BTN_AUXB8);
aq_panel.c: aqdata->aqbuttons[16].name = BTN_AUXB8;
aq_panel.c: aqdata->aqbuttons[16].code = KEY_AUXB8;
aq_panel.c: aqdata->aqbuttons[16].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[17].led = &aqdata->aqualinkleds[15-1];
aq_panel.c: aqdata->aqbuttons[17].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[17].label = name2label(BTN_POOL_HTR);
aq_panel.c: aqdata->aqbuttons[17].name = BTN_POOL_HTR;
aq_panel.c: aqdata->aqbuttons[17].code = KEY_POOL_HTR;
aq_panel.c: aqdata->aqbuttons[17].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[18].led = &aqdata->aqualinkleds[17-1];
aq_panel.c: aqdata->aqbuttons[18].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[18].label = name2label(BTN_SPA_HTR);
aq_panel.c: aqdata->aqbuttons[18].name = BTN_SPA_HTR;
aq_panel.c: aqdata->aqbuttons[18].code = KEY_SPA_HTR;
aq_panel.c: aqdata->aqbuttons[18].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[19].led = &aqdata->aqualinkleds[19-1];
aq_panel.c: aqdata->aqbuttons[19].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[19].label = name2label(BTN_SOLAR_HTR);
aq_panel.c: aqdata->aqbuttons[19].name = BTN_SOLAR_HTR;
aq_panel.c: aqdata->aqbuttons[19].code = KEY_SOLAR_HTR;
aq_panel.c: aqdata->aqbuttons[19].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[9].pda_label = BTN_PDA_AUX1;
aq_panel.c: aqdata->aqbuttons[10].pda_label = BTN_PDA_AUX2;
aq_panel.c: aqdata->aqbuttons[11].pda_label = BTN_PDA_AUX3;
aq_panel.c: aqdata->aqbuttons[12].pda_label = BTN_PDA_AUX4;
aq_panel.c: aqdata->aqbuttons[13].pda_label = BTN_PDA_AUX5;
aq_panel.c: aqdata->aqbuttons[14].pda_label = BTN_PDA_AUX6;
aq_panel.c: aqdata->aqbuttons[15].pda_label = BTN_PDA_AUX7;
aq_panel.c: aqdata->aqbuttons[16].pda_label = BTN_PDA_AUX7;
aq_panel.c: aqdata->aqbuttons[17].pda_label = BTN_PDA_POOL_HTR;
aq_panel.c: aqdata->aqbuttons[18].pda_label = BTN_PDA_SPA_HTR;
aq_panel.c: aqdata->aqbuttons[19].pda_label = BTN_PDA_SOLAR_HTR;
aq_panel.c: aqdata->aqbuttons[0].led = &aqdata->aqualinkleds[7-1];
aq_panel.c: aqdata->aqbuttons[0].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[0].label = name2label(BTN_PUMP);
aq_panel.c: aqdata->aqbuttons[0].name = BTN_PUMP;
aq_panel.c: aqdata->aqbuttons[0].code = KEY_PUMP;
aq_panel.c: aqdata->aqbuttons[0].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[0].pda_label = BTN_PDA_PUMP;
aq_panel.c: aqdata->aqbuttons[1].led = &aqdata->aqualinkleds[6-1];
aq_panel.c: aqdata->aqbuttons[1].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[1].label = name2label(BTN_SPA);
aq_panel.c: aqdata->aqbuttons[1].name = BTN_SPA;
aq_panel.c: aqdata->aqbuttons[1].code = KEY_SPA;
aq_panel.c: aqdata->aqbuttons[1].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[1].pda_label = BTN_PDA_SPA;
aq_panel.c: aqdata->aqbuttons[2].led = &aqdata->aqualinkleds[5-1];
aq_panel.c: aqdata->aqbuttons[2].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[2].label = name2label(BTN_AUX1);
aq_panel.c: aqdata->aqbuttons[2].name = BTN_AUX1;
aq_panel.c: aqdata->aqbuttons[2].code = KEY_AUX1;
aq_panel.c: aqdata->aqbuttons[2].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[2].pda_label = BTN_PDA_AUX1;
aq_panel.c: aqdata->aqbuttons[3].led = &aqdata->aqualinkleds[4-1];
aq_panel.c: aqdata->aqbuttons[3].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[3].label = name2label(BTN_AUX2);
aq_panel.c: aqdata->aqbuttons[3].name = BTN_AUX2;
aq_panel.c: aqdata->aqbuttons[3].code = KEY_AUX2;
aq_panel.c: aqdata->aqbuttons[3].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[3].pda_label = BTN_PDA_AUX2;
aq_panel.c: aqdata->aqbuttons[4].led = &aqdata->aqualinkleds[3-1];
aq_panel.c: aqdata->aqbuttons[4].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[4].label = BTN_AUX3;
aq_panel.c: aqdata->aqbuttons[4].name = BTN_AUX3;
aq_panel.c: aqdata->aqbuttons[4].code = KEY_AUX3;
aq_panel.c: aqdata->aqbuttons[4].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[4].pda_label = BTN_PDA_AUX3;
aq_panel.c: aqdata->aqbuttons[5].led = &aqdata->aqualinkleds[2-1]; // Change
aq_panel.c: aqdata->aqbuttons[5].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[5].label = name2label(BTN_AUX4);
aq_panel.c: aqdata->aqbuttons[5].name = BTN_AUX4;
aq_panel.c: aqdata->aqbuttons[5].code = KEY_AUX4;
aq_panel.c: aqdata->aqbuttons[5].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[5].pda_label = BTN_PDA_AUX4;
aq_panel.c: aqdata->aqbuttons[6].led = &aqdata->aqualinkleds[11-1]; // Change
aq_panel.c: aqdata->aqbuttons[6].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[6].label = name2label(BTN_AUX5);
aq_panel.c: aqdata->aqbuttons[6].name = BTN_AUX5;
aq_panel.c: aqdata->aqbuttons[6].code = KEY_AUX5;
aq_panel.c: aqdata->aqbuttons[6].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[6].pda_label = BTN_PDA_AUX5;
aq_panel.c: aqdata->aqbuttons[7].led = &aqdata->aqualinkleds[10-1]; // Change
aq_panel.c: aqdata->aqbuttons[7].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[7].label = name2label(BTN_AUX6);
aq_panel.c: aqdata->aqbuttons[7].name = BTN_AUX6;
aq_panel.c: aqdata->aqbuttons[7].code = KEY_AUX6;
aq_panel.c: aqdata->aqbuttons[7].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[7].pda_label = BTN_PDA_AUX6;
aq_panel.c: aqdata->aqbuttons[8].led = &aqdata->aqualinkleds[9-1]; // change
aq_panel.c: aqdata->aqbuttons[8].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[8].label = name2label(BTN_AUX7);
aq_panel.c: aqdata->aqbuttons[8].name = BTN_AUX7;
aq_panel.c: aqdata->aqbuttons[8].code = KEY_AUX7;
aq_panel.c: aqdata->aqbuttons[8].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[8].pda_label = BTN_PDA_AUX7;
aq_panel.c: aqdata->aqbuttons[9].led = &aqdata->aqualinkleds[8-1];
aq_panel.c: aqdata->aqbuttons[9].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[9].label = name2label(BTN_AUXB1); // AUX8
aq_panel.c: aqdata->aqbuttons[9].name = BTN_AUXB1;
aq_panel.c: aqdata->aqbuttons[9].code = KEY_AUXB1;
aq_panel.c: aqdata->aqbuttons[9].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[9].pda_label = BTN_PDA_AUX1;
aq_panel.c: aqdata->aqbuttons[10].led = &aqdata->aqualinkleds[12-1];
aq_panel.c: aqdata->aqbuttons[10].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[10].label = name2label(BTN_AUXB2); // AUX9
aq_panel.c: aqdata->aqbuttons[10].name = BTN_AUXB2;
aq_panel.c: aqdata->aqbuttons[10].code = KEY_AUXB2;
aq_panel.c: aqdata->aqbuttons[10].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[10].pda_label = BTN_PDA_AUX2;
aq_panel.c: aqdata->aqbuttons[11].led = &aqdata->aqualinkleds[1-1];
aq_panel.c: aqdata->aqbuttons[11].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[11].label = name2label(BTN_AUXB3); // AUX10
aq_panel.c: aqdata->aqbuttons[11].name = BTN_AUXB3;
aq_panel.c: aqdata->aqbuttons[11].code = KEY_AUXB3;
aq_panel.c: aqdata->aqbuttons[11].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[11].pda_label = BTN_PDA_AUX3;
aq_panel.c: aqdata->aqbuttons[12].led = &aqdata->aqualinkleds[13-1];
aq_panel.c: aqdata->aqbuttons[12].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[12].label = name2label(BTN_AUXB4); // AUX11
aq_panel.c: aqdata->aqbuttons[12].name = BTN_AUXB4;
aq_panel.c: aqdata->aqbuttons[12].code = KEY_AUXB4;
aq_panel.c: aqdata->aqbuttons[12].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[12].pda_label = BTN_PDA_AUX4;
aq_panel.c: aqdata->aqbuttons[13].led = &aqdata->aqualinkleds[21-1]; // doesn't actually exist
aq_panel.c: aqdata->aqbuttons[13].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[13].label = name2label(BTN_AUXB5);
aq_panel.c: aqdata->aqbuttons[13].name = BTN_AUXB5;
aq_panel.c: aqdata->aqbuttons[13].code = KEY_AUXB5;
aq_panel.c: aqdata->aqbuttons[13].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[13].pda_label = BTN_PDA_AUX5;
aq_panel.c: aqdata->aqbuttons[14].led = &aqdata->aqualinkleds[22-1]; // doesn't actually exist
aq_panel.c: aqdata->aqbuttons[14].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[14].label = name2label(BTN_AUXB6);
aq_panel.c: aqdata->aqbuttons[14].name = BTN_AUXB6;
aq_panel.c: aqdata->aqbuttons[14].code = KEY_AUXB6;
aq_panel.c: aqdata->aqbuttons[14].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[14].pda_label = BTN_PDA_AUX6;
aq_panel.c: aqdata->aqbuttons[15].led = &aqdata->aqualinkleds[23-1]; // doesn't actually exist
aq_panel.c: aqdata->aqbuttons[15].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[15].label = name2label(BTN_AUXB7);
aq_panel.c: aqdata->aqbuttons[15].name = BTN_AUXB7;
aq_panel.c: aqdata->aqbuttons[15].code = KEY_AUXB7;
aq_panel.c: aqdata->aqbuttons[15].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[15].pda_label = BTN_PDA_AUX7;
aq_panel.c: aqdata->aqbuttons[16].led = &aqdata->aqualinkleds[24-1]; // doesn't actually exist
aq_panel.c: aqdata->aqbuttons[16].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[16].label = name2label(BTN_AUXB8);
aq_panel.c: aqdata->aqbuttons[16].name = BTN_AUXB8;
aq_panel.c: aqdata->aqbuttons[16].code = KEY_AUXB8;
aq_panel.c: aqdata->aqbuttons[16].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[16].pda_label = BTN_PDA_AUX7;
aq_panel.c: aqdata->aqbuttons[17].led = &aqdata->aqualinkleds[15-1];
aq_panel.c: aqdata->aqbuttons[17].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[17].label = name2label(BTN_POOL_HTR);
aq_panel.c: aqdata->aqbuttons[17].name = BTN_POOL_HTR;
aq_panel.c: aqdata->aqbuttons[17].code = KEY_POOL_HTR;
aq_panel.c: aqdata->aqbuttons[17].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[17].pda_label = BTN_PDA_POOL_HTR;
aq_panel.c: aqdata->aqbuttons[18].led = &aqdata->aqualinkleds[17-1];
aq_panel.c: aqdata->aqbuttons[18].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[18].label = name2label(BTN_SPA_HTR);
aq_panel.c: aqdata->aqbuttons[18].name = BTN_SPA_HTR;
aq_panel.c: aqdata->aqbuttons[18].code = KEY_SPA_HTR;
aq_panel.c: aqdata->aqbuttons[18].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[18].pda_label = BTN_PDA_SPA_HTR;
aq_panel.c: aqdata->aqbuttons[19].led = &aqdata->aqualinkleds[19-1];
aq_panel.c: aqdata->aqbuttons[19].led->state = LED_S_UNKNOWN;
aq_panel.c: aqdata->aqbuttons[19].label = name2label(BTN_SOLAR_HTR);
aq_panel.c: aqdata->aqbuttons[19].name = BTN_SOLAR_HTR;
aq_panel.c: aqdata->aqbuttons[19].code = KEY_SOLAR_HTR;
aq_panel.c: aqdata->aqbuttons[19].dz_idx = DZ_NULL_IDX;
aq_panel.c: aqdata->aqbuttons[19].pda_label = BTN_PDA_SOLAR_HTR;
aq_programmer.c: if ( aqdata->temp_units == CELSIUS ) {
aq_programmer.c: aqdata->spa_htr_set_point != TEMP_UNKNOWN &&
aq_programmer.c: min <= aqdata->spa_htr_set_point)
aq_programmer.c: min = aqdata->spa_htr_set_point + 1;
aq_programmer.c: if ( aqdata->temp_units == CELSIUS ) {
aq_programmer.c: aqdata->pool_htr_set_point != TEMP_UNKNOWN &&
aq_programmer.c: max >= aqdata->pool_htr_set_point)
aq_programmer.c: max = aqdata->pool_htr_set_point - 1;
aq_programmer.c: if ( aqdata->temp_units == CELSIUS ) {
aqualinkd.c: // Loop over only aqdata->aqbuttons[13] to aqdata->aqbuttons[16]
config.c: aqdata->aqbuttons[num].label = cleanalloc(indx+1);
config.c: aqdata->aqbuttons[num].dz_idx = strtoul(indx+1, NULL, 10);
config.c: aqdata->aqbuttons[num].pda_label = cleanalloc(indx+1);
config.c: aqdata->aqbuttons[num].label = cleanalloc(value);
config.c: aqdata->aqbuttons[num].dz_idx = strtoul(value, NULL, 10);
config.c: //aqdata->aqbuttons[num].pda_label = cleanalloc(value);
config.c: aqdata->aqbuttons[num].label = cleanalloc(value);
config.c: if (aqdata->num_lights < MAX_LIGHTS) {
config.c: aqdata->lights[aqdata->num_lights].button = &aqdata->aqbuttons[num];
config.c: aqdata->lights[aqdata->num_lights].lightType = type;
config.c: aqdata->num_lights++;
config.c: aqdata->aqbuttons[num].special_mask |= PROGRAM_LIGHT;
config.c: //aqdata->aqbuttons[num].pda_label = cleanalloc(value);
config.c: aqdata->pumps[pi].button = &aqdata->aqbuttons[num];
config.c: aqdata->pumps[pi].pumpID = strtoul(cleanalloc(value), NULL, 16);
config.c: aqdata->pumps[pi].pumpIndex = pi+1;
config.c: //aqdata->pumps[pi].buttonID = num;
config.c: if (aqdata->pumps[pi].pumpID < 119)
config.c: aqdata->pumps[pi].ptype = PENTAIR;
config.c: aqdata->pumps[pi].ptype = JANDY;
config.c: //aqdata->num_pumps
config.c: for (pi=0; pi < aqdata->num_pumps; pi++) {
config.c: if (aqdata->pumps[pi].button == &aqdata->aqbuttons[button]) {
config.c: return &aqdata->pumps[pi];
config.c: if (aqdata->num_pumps < MAX_PUMPS) {
config.c: aqdata->aqbuttons[button].special_mask |= VS_PUMP;
config.c: aqdata->pumps[aqdata->num_pumps].button = &aqdata->aqbuttons[button];
config.c: aqdata->pumps[aqdata->num_pumps].pumpType = PT_UNKNOWN;
config.c: aqdata->pumps[aqdata->num_pumps].rpm = TEMP_UNKNOWN;
config.c: aqdata->pumps[aqdata->num_pumps].watts = TEMP_UNKNOWN;
config.c: aqdata->pumps[aqdata->num_pumps].gpm = TEMP_UNKNOWN;
config.c: aqdata->num_pumps++;
config.c: return &aqdata->pumps[aqdata->num_pumps-1];
config.c: fprintf(fp, "button_%.2d_label = %s\n", i+1, aqdata->aqbuttons[i].label);
config.c: if (aqdata->aqbuttons[i].dz_idx > 0)
config.c: fprintf(fp, "button_%.2d_dzidx = %d\n", i+1, aqdata->aqbuttons[i].dz_idx);
config.c: if (aqdata->aqbuttons[i].pda_label != NULL)
config.c: fprintf(fp, "button_%.2d_PDA_label = %s\n", i+1, aqdata->aqbuttons[i].pda_label);
config_new.c: indx = sprintf(bufr, "button_%.2d_label = %s\n", i+1, aqdata->aqbuttons[i].label);
config_new.c: indx += sprintf(&bufr[indx], "button_%.2d_dzidx = %d\n", i+1, aqdata->aqbuttons[i].dz_idx);
config_new.c: if (aqdata->aqbuttons[i].pda_label != NULL)
config_new.c: indx += sprintf(&bufr[indx], "button_%.2d_PDA_label = %s\n", i+1, aqdata->aqbuttons[i].pda_label);
config_new.c: if ( aqdata->pumps[pi].button == &aqdata->aqbuttons[i] ) {
config_new.c: indx += sprintf(&bufr[indx],"button_%.2d_pumpID = 0x%02hhx\n", i+1, aqdata->pumps[pi].pumpID);
config_new.c: indx += sprintf(&bufr[indx],"button_%.2d_pumpIndex = %d\n", i+1, aqdata->pumps[pi].pumpIndex);
config_new.c: for (j = 0; j < aqdata->num_pumps; j++) {
config_new.c: if (aqdata->pumps[j].button == &aqdata->aqbuttons[i]) {
config_new.c: sprintf(vsp,"0x%02hhx",aqdata->pumps[j].pumpID);
config_new.c: alid = aqdata->pumps[j].pumpIndex;
config_new.c: aqdata->aqbuttons[i].name, aqdata->aqbuttons[i].label, vsp, alid, aqdata->aqbuttons[i].dz_idx,
config_new.c: aqdata->aqbuttons[i].name, aqdata->aqbuttons[i].label, vsp, alid,
config_new.c: aqdata->aqbuttons[i].pda_label, aqdata->aqbuttons[i].dz_idx );
config_new.c: for (j = 0; j < aqdata->num_pumps; j++) {
config_new.c: if (aqdata->pumps[j].button == &aqdata->aqbuttons[i]) {
config_new.c: vsp = aqdata->pumps[j].pumpID;
config_new.c: alid = aqdata->pumps[j].pumpIndex;
config_new.c: indx += sprintf(&bufr[indx], "{\"button\":\"%d\",\"label\":\"%s\",\"dzidx\":\"%d\"",i,aqdata->aqbuttons[i].label,aqdata->aqbuttons[i].dz_idx);
config_new.c: aqdata->aqbuttons[i].name, aqdata->aqbuttons[i].label, vsp, alid,
config_new.c: aqdata->aqbuttons[i].pda_label, aqdata->aqbuttons[i].dz_idx );
config_new.c: aqdata->aqbuttons[num].label = cleanalloc(value);
config_new.c: aqdata->aqbuttons[num].dz_idx = strtoul(value, NULL, 10);
config_new.c: aqdata->aqbuttons[num].pda_label = cleanalloc(value);
config_new.c: //aqdata->num_pumps
config_new.c: for (pi=0; pi < aqdata->num_pumps; pi++) {
config_new.c: if (aqdata->pumps[pi].button == &aqdata->aqbuttons[button]) {
config_new.c: return &aqdata->pumps[pi];
config_new.c: if (aqdata->num_pumps < MAX_PUMPS) {
config_new.c: aqdata->pumps[aqdata->num_pumps].button = &aqdata->aqbuttons[button];
config_new.c: aqdata->pumps[aqdata->num_pumps].pumpType = PT_UNKNOWN;
config_new.c: aqdata->pumps[aqdata->num_pumps].rpm = TEMP_UNKNOWN;
config_new.c: aqdata->pumps[aqdata->num_pumps].watts = TEMP_UNKNOWN;
config_new.c: aqdata->pumps[aqdata->num_pumps].gpm = TEMP_UNKNOWN;
config_new.c: aqdata->num_pumps++;
config_new.c: return &aqdata->pumps[aqdata->num_pumps-1];
devices_jandy.c: if (packet[3] == CMD_PERCENT && aqdata->active_thread.thread_id == 0 && packet[4] != 0xFF) {
devices_jandy.c: if (aqdata->swg_percent != (int)packet[4]) {
devices_jandy.c: aqdata->swg_percent = (int)packet[4];
devices_jandy.c: aqdata->updated = true;
devices_jandy.c: LOG(DJAN_LOG, LOG_DEBUG, "Set SWG %% to %d from control panel packet to SWG\n", aqdata->swg_percent);
devices_jandy.c: if (aqdata->swg_percent != (int)packet[4]) {
devices_jandy.c: aqdata->swg_percent = (int)packet[4];
devices_jandy.c: aqdata->updated = true;
devices_jandy.c: if (aqdata->swg_percent > 100)
devices_jandy.c: aqdata->boost = true;
devices_jandy.c: aqdata->boost = false;
devices_jandy.c: //aqdata->ar_swg_device_status = packet[5];
devices_jandy.c: if (aqdata->swg_delayed_percent != TEMP_UNKNOWN && aqdata->ar_swg_device_status == SWG_STATUS_ON) { // We have a delayed % to set.
devices_jandy.c: snprintf(sval, 9, "%d", aqdata->swg_delayed_percent);
devices_jandy.c: LOG(DJAN_LOG, LOG_NOTICE, "Setting SWG %% to %d, from delayed message\n", aqdata->swg_delayed_percent);
devices_jandy.c: aqdata->swg_delayed_percent = TEMP_UNKNOWN;
devices_jandy.c: if ( (packet[4] * 100) != aqdata->swg_ppm ) {
devices_jandy.c: aqdata->swg_ppm = packet[4] * 100;
devices_jandy.c: LOG(DJAN_LOG, LOG_DEBUG, "Set SWG PPM to %d from SWG packet\n", aqdata->swg_ppm);
devices_jandy.c: aqdata->updated = true;
devices_jandy.c: if ( aqdata->ar_swg_device_status != SWG_STATUS_UNKNOWN && isIAQT_ENABLED == false && isONET_ENABLED == false )
devices_jandy.c: if (aqdata->ar_swg_device_status == status)
devices_jandy.c: if (aqdata->ar_swg_device_status > SWG_STATUS_ON &&
devices_jandy.c: aqdata->ar_swg_device_status < SWG_STATUS_TURNING_OFF) {
devices_jandy.c: LOG(DJAN_LOG, LOG_DEBUG, "Ignoreing set SWG device state to '0x%02hhx', request from %d\n", aqdata->ar_swg_device_status, requester);
devices_jandy.c: aqdata->ar_swg_device_status = status;
devices_jandy.c: aqdata->swg_led_state = isSWGDeviceErrorState(status)?ENABLE:ON;
devices_jandy.c: aqdata->ar_swg_device_status = status;
devices_jandy.c: aqdata->swg_led_state = OFF;
devices_jandy.c: LOG(DJAN_LOG, LOG_DEBUG, "Set SWG device state to '0x%02hhx', request from %d\n", aqdata->ar_swg_device_status, requester);
devices_jandy.c: aqdata->boost = false;
devices_jandy.c: aqdata->boost_msg[0] = '\0';
devices_jandy.c: aqdata->swg_percent = 0;
devices_jandy.c: aqdata->boost = true;
devices_jandy.c: aqdata->swg_percent = 101;
devices_jandy.c: LOG(DJAN_LOG, LOG_DEBUG, "Ignoring set SWG %% to %d due to programming SWG\n", aqdata->swg_percent);
devices_jandy.c: if (aqdata->ar_swg_device_status != SWG_STATUS_OFF || aqdata->swg_led_state != OFF)
devices_jandy.c: aqdata->updated = true;
devices_jandy.c: aqdata->ar_swg_device_status = SWG_STATUS_OFF;
devices_jandy.c: aqdata->swg_led_state = OFF;
devices_jandy.c: if (aqdata->swg_led_state != ENABLE) {
devices_jandy.c: aqdata->updated = true;
devices_jandy.c: aqdata->swg_led_state = ENABLE;
devices_jandy.c: aqdata->swg_percent = percent;
devices_jandy.c: aqdata->updated = true;
devices_jandy.c: if (aqdata->swg_percent > 0) {
devices_jandy.c: if (aqdata->swg_led_state == OFF || (aqdata->swg_led_state == ENABLE && ! isSWGDeviceErrorState(aqdata->ar_swg_device_status)) ) // Don't change ENABLE / FLASH
devices_jandy.c: aqdata->swg_led_state = ON;
devices_jandy.c: if (aqdata->ar_swg_device_status == SWG_STATUS_UNKNOWN)
devices_jandy.c: aqdata->ar_swg_device_status = SWG_STATUS_ON;
devices_jandy.c: } if ( aqdata->swg_percent == 0 ) {
devices_jandy.c: if (aqdata->swg_led_state == ON)
devices_jandy.c: aqdata->swg_led_state = ENABLE; // Don't change OFF
devices_jandy.c: if (aqdata->ar_swg_device_status == SWG_STATUS_UNKNOWN)
devices_jandy.c: aqdata->ar_swg_device_status = SWG_STATUS_ON; // Maybe this should be off
devices_jandy.c: LOG(DJAN_LOG, LOG_DEBUG, "Set SWG %% to %d, LED=%d, FullStatus=0x%02hhx\n", aqdata->swg_percent, aqdata->swg_led_state, aqdata->ar_swg_device_status);
devices_jandy.c: switch (aqdata->ar_swg_device_status) {
devices_jandy.c: return (aqdata->swg_percent > 0?ON:ENABLE);
devices_jandy.c: return (aqdata->swg_percent > 0?ON:ENABLE);
devices_jandy.c: return (aqdata->swg_percent > 0?ON:ENABLE);
devices_jandy.c: return (aqdata->swg_percent > 0?ON:ENABLE);
devices_jandy.c: return (aqdata->swg_percent > 0?ON:ENABLE);
devices_jandy.c: return (aqdata->swg_percent > 0?ON:ENABLE);
devices_jandy.c: switch (aqdata->ar_swg_device_status) {
devices_jandy.c: *status = (aqdata->swg_percent > 0?SWG_ON:SWG_OFF);
devices_jandy.c: *status = (aqdata->swg_percent > 0?SWG_ON:SWG_OFF);
devices_jandy.c: *status = (aqdata->swg_percent > 0?SWG_ON:SWG_OFF);
devices_jandy.c: *status = (aqdata->swg_percent > 0?SWG_ON:SWG_OFF);
devices_jandy.c: *status = (aqdata->swg_percent > 0?SWG_ON:SWG_OFF);
devices_jandy.c: *status = (aqdata->swg_percent > 0?SWG_ON:SWG_OFF);
devices_jandy.c: *status = (aqdata->swg_percent > 0?SWG_ON:SWG_OFF);
devices_jandy.c: for (i=0; i < aqdata->num_pumps; i++) {
devices_jandy.c: if (aqdata->pumps[i].pumpID == packet_buffer[PKT_DEST]) {
devices_jandy.c: LOG(DJAN_LOG, LOG_DEBUG, "Last panel info RPM:%d GPM:%d WATTS:%d\n", aqdata->pumps[i].rpm, aqdata->pumps[i].gpm, aqdata->pumps[i].watts);
devices_pentair.c: if ( aqdata->pumps[i].prclType == PENTAIR && aqdata->pumps[i].pumpID == packet[PEN_PKT_FROM] ) {
devices_pentair.c: aqdata->pumps[i].rpm = (packet[PEN_HI_B_RPM] * 256) + packet[PEN_LO_B_RPM];
devices_pentair.c: aqdata->pumps[i].watts = (packet[PEN_HI_B_WAT] * 256) + packet[PEN_LO_B_WAT];
devices_pentair.c: if ( pumpIndex < aqdata->num_pumps && pumpIndex < 0) {
devices_pentair.c: aqdata->pumps[pumpIndex-1].rpm = atoi((char *) &packet_buffer[13]);
devices_pentair.c: aqdata->pumps[pumpIndex-1].gph = atoi((char *) &packet_buffer[13]);
devices_pentair.c: aqdata->pumps[pumpIndex-1].watts = atoi((char *) &packet_buffer[13]);
json_messages.c: if (aqdata->active_thread.thread_id != 0 && !aqdata->simulate_panel) {
json_messages.c: return programtypeDisplayName(aqdata->active_thread.ptype);
json_messages.c: //if (aqdata->last_message != NULL && stristr(aqdata->last_message, "SERVICE") != NULL ) {
json_messages.c: if (aqdata->service_mode_state == ON) {
json_messages.c: } else if (aqdata->service_mode_state == FLASH) {
json_messages.c: if (aqdata->last_display_message[0] != '\0') {
json_messages.c: for(i=0; i < strlen(aqdata->last_display_message); i++ ) {
json_messages.c: if (aqdata->last_display_message[i] <= 31 || aqdata->last_display_message[i] >= 127) {
json_messages.c: aqdata->last_display_message[i] = ' ';
json_messages.c: switch (aqdata->last_display_message[i]) {
json_messages.c: aqdata->last_display_message[i] = ' ';
json_messages.c: //printf("JSON Sending '%s'\n",aqdata->last_display_message);
json_messages.c: return aqdata->last_display_message;
json_messages.c: if (aqdata->display_last_message == true) {
json_messages.c: return aqdata->last_message;
json_messages.c: for (i=0; i < aqdata->num_pumps; i++) {
json_messages.c: if (button == aqdata->pumps[i].button) {
json_messages.c: aqdata->pumps[i].rpm,aqdata->pumps[i].gpm,aqdata->pumps[i].watts,
json_messages.c: (aqdata->pumps[i].pumpType==VFPUMP?"vfPump":(aqdata->pumps[i].pumpType==VSPUMP?"vsPump":"ePump")));
json_messages.c: for (i=0; i < aqdata->num_lights; i++) {
json_messages.c: if (button == aqdata->lights[i].button) {
json_messages.c: length += sprintf(buffer, ",\"type_ext\": \"switch_program\", \"Light_Type\":\"%d\"", aqdata->lights[i].lightType);
json_messages.c: bool homekit_f = (homekit && aqdata->temp_units==FAHRENHEIT);
json_messages.c: length += sprintf(buffer+length, ",\"date\":\"%s\"",aqdata->date );//"09/01/16 THU",
json_messages.c: length += sprintf(buffer+length, ",\"time\":\"%s\"",aqdata->time );//"1:16 PM",
json_messages.c: if ( aqdata->temp_units == FAHRENHEIT )
json_messages.c: else if ( aqdata->temp_units == CELSIUS )
json_messages.c: for (i=0; i < aqdata->total_buttons; i++)
json_messages.c: if ( strcmp(BTN_POOL_HTR,aqdata->aqbuttons[i].name) == 0 && aqdata->pool_htr_set_point != TEMP_UNKNOWN) {
json_messages.c: aqdata->aqbuttons[i].name,
json_messages.c: aqdata->aqbuttons[i].label,
json_messages.c: aqdata->aqbuttons[i].led->state==ON?JSON_ON:JSON_OFF,
json_messages.c: LED2text(aqdata->aqbuttons[i].led->state),
json_messages.c: ((homekit_f)?degFtoC(aqdata->pool_htr_set_point):aqdata->pool_htr_set_point),
json_messages.c: ((homekit_f)?degFtoC(aqdata->pool_temp):aqdata->pool_temp),
json_messages.c: LED2int(aqdata->aqbuttons[i].led->state));
json_messages.c: } else if ( strcmp(BTN_SPA_HTR,aqdata->aqbuttons[i].name)==0 && aqdata->spa_htr_set_point != TEMP_UNKNOWN) {
json_messages.c: aqdata->aqbuttons[i].name,
json_messages.c: aqdata->aqbuttons[i].label,
json_messages.c: aqdata->aqbuttons[i].led->state==ON?JSON_ON:JSON_OFF,
json_messages.c: LED2text(aqdata->aqbuttons[i].led->state),
json_messages.c: ((homekit_f)?degFtoC(aqdata->spa_htr_set_point):aqdata->spa_htr_set_point),
json_messages.c: ((homekit_f)?degFtoC(aqdata->spa_temp):aqdata->spa_temp),
json_messages.c: LED2int(aqdata->aqbuttons[i].led->state));
json_messages.c: get_aux_information(&aqdata->aqbuttons[i], aqdata, aux_info);
json_messages.c: aqdata->aqbuttons[i].name,
json_messages.c: aqdata->aqbuttons[i].label,
json_messages.c: aqdata->aqbuttons[i].led->state==ON?JSON_ON:JSON_OFF,
json_messages.c: LED2text(aqdata->aqbuttons[i].led->state),
json_messages.c: LED2int(aqdata->aqbuttons[i].led->state),
json_messages.c: aqdata->aqbuttons[i].name,
json_messages.c: aqdata->aqbuttons[i].label,
json_messages.c: aqdata->aqbuttons[i].led->state==ON?JSON_ON:JSON_OFF,
json_messages.c: LED2text(aqdata->aqbuttons[i].led->state),
json_messages.c: LED2int(aqdata->aqbuttons[i].led->state));
json_messages.c: if ( get_aux_information(&aqdata->aqbuttons[i], aqdata, aux_info)[0] == '\0' ) {
json_messages.c: aqdata->aqbuttons[i].name,
json_messages.c: aqdata->aqbuttons[i].label,
json_messages.c: aqdata->aqbuttons[i].led->state==ON?JSON_ON:JSON_OFF,
json_messages.c: LED2text(aqdata->aqbuttons[i].led->state),
json_messages.c: LED2int(aqdata->aqbuttons[i].led->state));
json_messages.c: aqdata->aqbuttons[i].name,
json_messages.c: aqdata->aqbuttons[i].label,
json_messages.c: aqdata->aqbuttons[i].led->state==ON?JSON_ON:JSON_OFF,
json_messages.c: LED2text(aqdata->aqbuttons[i].led->state),
json_messages.c: LED2int(aqdata->aqbuttons[i].led->state),
json_messages.c: //get_aux_information(&aqdata->aqbuttons[i], aqdata, aux_info));
json_messages.c: if ( aqdata->frz_protect_set_point != TEMP_UNKNOWN && aqdata->air_temp != TEMP_UNKNOWN) {
json_messages.c: aqdata->frz_protect_state==ON?JSON_ON:JSON_OFF,
json_messages.c: aqdata->frz_protect_state==ON?LED2text(ON):LED2text(ENABLE),
json_messages.c: ((homekit_f)?degFtoC(aqdata->frz_protect_set_point):aqdata->frz_protect_set_point),
json_messages.c: ((homekit_f)?degFtoC(aqdata->air_temp):aqdata->air_temp),
json_messages.c: aqdata->frz_protect_state==ON?1:0);
json_messages.c: if (aqdata->swg_led_state != LED_S_UNKNOWN) {
json_messages.c: if ( aqdata->swg_percent != TEMP_UNKNOWN ) {
json_messages.c: //aqdata->ar_swg_status == SWG_STATUS_OFF?JSON_OFF:JSON_ON,
json_messages.c: aqdata->swg_led_state == OFF?JSON_OFF:JSON_ON,
json_messages.c: LED2text(aqdata->swg_led_state),
json_messages.c: ((homekit_f)?degFtoC(aqdata->swg_percent):aqdata->swg_percent),
json_messages.c: ((homekit_f)?degFtoC(aqdata->swg_percent):aqdata->swg_percent),
json_messages.c: LED2int(aqdata->swg_led_state) );
json_messages.c: //aqdata->ar_swg_status == SWG_STATUS_OFF?LED2int(OFF):LED2int(ON));
json_messages.c: ((homekit_f)?degFtoC(aqdata->swg_percent):aqdata->swg_percent));
json_messages.c: aqdata->boost?JSON_ON:JSON_OFF,
json_messages.c: aqdata->boost?JSON_ON:JSON_OFF,
json_messages.c: aqdata->boost?LED2int(ON):LED2int(OFF));
json_messages.c: if ( aqdata->swg_ppm != TEMP_UNKNOWN ) {
json_messages.c: ((homekit_f)?roundf(degFtoC(aqdata->swg_ppm)):aqdata->swg_ppm));
json_messages.c: aqdata->swg_ppm);
json_messages.c: if ( aqdata->ph != TEMP_UNKNOWN ) {
json_messages.c: ((homekit_f)?(degFtoC(aqdata->ph)):aqdata->ph));
json_messages.c: if ( aqdata->orp != TEMP_UNKNOWN ) {
json_messages.c: ((homekit_f)?(degFtoC(aqdata->orp)):aqdata->orp));
json_messages.c: ((homekit_f)?degFtoC(aqdata->air_temp):aqdata->air_temp));
json_messages.c: ((homekit_f)?degFtoC(aqdata->air_temp):aqdata->pool_temp));
json_messages.c: ((homekit_f)?degFtoC(aqdata->air_temp):aqdata->spa_temp));
json_messages.c: length += sprintf(buffer+length, ",\"panel_message\":\"%s\"",aqdata->last_message );
json_messages.c: //length += sprintf(buffer+length, ",\"message\":\"%s\"",aqdata->message );
json_messages.c: length += sprintf(buffer+length, ",\"version\":\"%s\"",aqdata->version );//8157 REV MMM",
json_messages.c: length += sprintf(buffer+length, ",\"date\":\"%s\"",aqdata->date );//"09/01/16 THU",
json_messages.c: length += sprintf(buffer+length, ",\"time\":\"%s\"",aqdata->time );//"1:16 PM",
json_messages.c: //length += sprintf(buffer+length, ",\"air_temp\":\"%d\"",aqdata->air_temp );//"96",
json_messages.c: //length += sprintf(buffer+length, ",\"pool_temp\":\"%d\"",aqdata->pool_temp );//"86",
json_messages.c: //length += sprintf(buffer+length, ",\"spa_temp\":\"%d\"",aqdata->spa_temp );//" ",
json_messages.c: length += sprintf(buffer+length, ",\"pool_htr_set_pnt\":\"%d\"",aqdata->pool_htr_set_point );//"85",
json_messages.c: length += sprintf(buffer+length, ",\"spa_htr_set_pnt\":\"%d\"",aqdata->spa_htr_set_point );//"99",
json_messages.c: //length += sprintf(buffer+length, ",\"freeze_protection":\"%s\"",aqdata->frz_protect_set_point );//"off",
json_messages.c: length += sprintf(buffer+length, ",\"frz_protect_set_pnt\":\"%d\"",aqdata->frz_protect_set_point );//"0",
json_messages.c: if ( aqdata->air_temp == TEMP_UNKNOWN )
json_messages.c: length += sprintf(buffer+length, ",\"air_temp\":\"%d\"",aqdata->air_temp );
json_messages.c: if ( aqdata->pool_temp == TEMP_UNKNOWN )
json_messages.c: length += sprintf(buffer+length, ",\"pool_temp\":\"%d\"",aqdata->pool_temp );
json_messages.c: if ( aqdata->spa_temp == TEMP_UNKNOWN )
json_messages.c: length += sprintf(buffer+length, ",\"spa_temp\":\"%d\"",aqdata->spa_temp );
json_messages.c: if (aqdata->swg_led_state != LED_S_UNKNOWN) {
json_messages.c: if ( aqdata->swg_percent != TEMP_UNKNOWN )
json_messages.c: length += sprintf(buffer+length, ",\"swg_percent\":\"%d\"",aqdata->swg_percent );
json_messages.c: if ( aqdata->swg_ppm != TEMP_UNKNOWN )
json_messages.c: length += sprintf(buffer+length, ",\"swg_ppm\":\"%d\"",aqdata->swg_ppm );
json_messages.c: if ( aqdata->temp_units == FAHRENHEIT )
json_messages.c: else if ( aqdata->temp_units == CELSIUS )
json_messages.c: if (aqdata->battery == OK)
json_messages.c: if ( aqdata->swg_percent == 101 )
json_messages.c: length += sprintf(buffer+length, ",\"swg_boost_msg\":\"%s\"",aqdata->boost_msg );
json_messages.c: if ( aqdata->ph != TEMP_UNKNOWN )
json_messages.c: length += sprintf(buffer+length, ",\"chem_ph\":\"%.1f\"",aqdata->ph );
json_messages.c: if ( aqdata->orp != TEMP_UNKNOWN )
json_messages.c: length += sprintf(buffer+length, ",\"chem_orp\":\"%d\"",aqdata->orp );
json_messages.c: for (i=0; i < aqdata->total_buttons; i++)
json_messages.c: char *state = LED2text(aqdata->aqbuttons[i].led->state);
json_messages.c: length += sprintf(buffer+length, "\"%s\": \"%s\"", aqdata->aqbuttons[i].name, state);
json_messages.c: if (i+1 < aqdata->total_buttons)
json_messages.c: if ( aqdata->swg_percent != TEMP_UNKNOWN && aqdata->swg_led_state != LED_S_UNKNOWN ) {
json_messages.c: length += sprintf(buffer+length, ", \"%s\": \"%s\"", SWG_TOPIC, LED2text(aqdata->swg_led_state));
json_messages.c: //length += sprintf(buffer+length, ", \"%s\": \"%s\"", SWG_TOPIC, aqdata->ar_swg_status == SWG_STATUS_OFF?JSON_OFF:JSON_ON);
json_messages.c: length += sprintf(buffer+length, ", \"%s\": \"%s\"", SWG_BOOST_TOPIC, aqdata->boost?JSON_ON:JSON_OFF);
json_messages.c: if ( aqdata->frz_protect_set_point != TEMP_UNKNOWN ) {
json_messages.c: length += sprintf(buffer+length, ", \"%s\": \"%s\"", FREEZE_PROTECT, aqdata->frz_protect_state==ON?JSON_ON:JSON_ENABLED);
json_messages.c: for (i=0; i < aqdata->num_pumps; i++) {
json_messages.c:printf("Pump Label %s\n",aqdata->pumps[i].button->label);
json_messages.c:printf("Pump Name %s\n",aqdata->pumps[i].button->name);
json_messages.c:printf("Pump RPM %d\n",aqdata->pumps[i].rpm);
json_messages.c:printf("Pump GPM %d\n",aqdata->pumps[i].gpm);
json_messages.c:printf("Pump GPM %d\n",aqdata->pumps[i].watts);
json_messages.c:printf("Pump Type %d\n",aqdata->pumps[i].pumpType);
json_messages.c: if (aqdata->pumps[i].pumpType != PT_UNKNOWN && (aqdata->pumps[i].rpm != TEMP_UNKNOWN || aqdata->pumps[i].gpm != TEMP_UNKNOWN || aqdata->pumps[i].watts != TEMP_UNKNOWN)) {
json_messages.c: i+1,aqdata->pumps[i].button->label,aqdata->pumps[i].button->name,aqdata->pumps[i].rpm,aqdata->pumps[i].gpm,aqdata->pumps[i].watts,
json_messages.c: (aqdata->pumps[i].pumpType==VFPUMP?"vfPump":(aqdata->pumps[i].pumpType==VSPUMP?"vsPump":"ePump")));
json_messages.c: for (i=0; i < aqdata->total_buttons; i++)
json_messages.c: length += sprintf(buffer+length, ",\"%s\": \"%s\"", aqdata->aqbuttons[i].name, aqdata->aqbuttons[i].label);
net_services.c: if (aqdata->updated == true /*|| _broadcast == true*/) {
net_services.c: aqdata->updated = false;
serialadapter.c: val = setpoint_check(POOL_HTR_SETOINT, aqdata->pool_htr_set_point + val, aqdata);
serialadapter.c: LOG(RSSA_LOG,LOG_DEBUG, "Increasing pool heater from %d to %d\n",aqdata->pool_htr_set_point,val);
serialadapter.c: val = setpoint_check(SPA_HTR_SETOINT, aqdata->spa_htr_set_point + val, aqdata);
serialadapter.c: LOG(RSSA_LOG,LOG_DEBUG, "Increasing spa heater from %d to %d\n",aqdata->spa_htr_set_point,val);

33
utils.c
View File

@ -247,6 +247,9 @@ const char* logmask2name(int16_t from)
case DBGT_LOG:
return "AQ Timing: ";
break;
case TIMR_LOG:
return "Schd/Timer:";
break;
case AQUA_LOG:
default:
return "AqualinkD: ";
@ -269,6 +272,9 @@ char *cleanwhitespace(char *str)
{
char *end;
if (str == NULL)
return str;
// Trim leading space
while(isspace(*str)) str++;
@ -423,6 +429,8 @@ void logMessage(int msg_level, const char *format, ...)
_LOG(AQUA_LOG, msg_level, buffer);
}
*/
#define LOGBUFFER 4096
void LOG(int16_t from, int msg_level, const char * format, ...)
{
//printf("msg_level=%d _log_level=%d mask=%d\n",msg_level,_log_level,(_logforcemask & from));
@ -434,7 +442,7 @@ void LOG(int16_t from, int msg_level, const char * format, ...)
if ( msg_level > getLogLevel(from))
return;
char buffer[1024];
char buffer[LOGBUFFER];
va_list args;
va_start(args, format);
strncpy(buffer, " ", 20);
@ -450,7 +458,7 @@ void _LOG(int16_t from, int msg_level, char *message)
int i;
// Make all printable chars
for(i = 8; i+8 < strlen(&message[8]) && i < 1024; i++) {
for(i = 8; i+8 < strlen(&message[8]) && i < LOGBUFFER; i++) {
if ( (message[i] < 32 || message[i] > 125) && message[i] != 10 ) {
//printf ("Change %c to %c in %s\n",message[i], ' ', message);
message[i] = ' ';
@ -466,7 +474,7 @@ void _LOG(int16_t from, int msg_level, char *message)
// Logging has not been setup yet, so STD error & syslog
if (_log_level == -1) {
fprintf (stderr, message);
fprintf (stderr, "%s\n", message);
syslog (msg_level, "%s", &message[9]);
closelog ();
}
@ -494,7 +502,7 @@ void _LOG(int16_t from, int msg_level, char *message)
// Sent the log to the UI if configured.
if (msg_level <= LOG_ERR && _loq_display_message != NULL) {
snprintf(_loq_display_message, 127, message);
snprintf(_loq_display_message, 127, "%s\n",message);
}
if (_log2file == TRUE && _log_filename != NULL) {
@ -742,20 +750,3 @@ char *prittyString(char *str)
return str;
}
/*
static FILE *_packetLogFile = NULL;
void writePacketLog(char *buffer) {
if (_packetLogFile == NULL)
_packetLogFile = fopen("/tmp/RS485.log", "a");
if (_packetLogFile != NULL) {
fputs(buffer, _packetLogFile);
}
}
void closePacketLog() {
fclose(_packetLogFile);
}
*/

View File

@ -39,6 +39,11 @@
#define RSSD_LOG (1 << 9) // RS485 Connection /dev/ttyUSB?
#define PROG_LOG (1 << 10) // Programmer
#define DBGT_LOG (1 << 11) // Debug Timer
#define TIMR_LOG (1 << 12) // Timers
// Set scheduler log to aqualink general
#define SCHD_LOG AQUA_LOG
/*
typedef enum
{

BIN
utils.o Normal file

Binary file not shown.

View File

@ -1,4 +1,4 @@
#define AQUALINKD_NAME "Aqualink Daemon"
#define AQUALINKD_VERSION "2.2.2a"
#define AQUALINKD_VERSION "2.3.0"

BIN
web-old/aqualink-black.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
web-old/aqualinkd-new.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
web-old/aqualinkd-white.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
web-old/aqualinkd.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Some files were not shown because too many files have changed in this diff Show More