LArge update with new features.

pull/11/head
shaun feakes 2018-07-15 14:36:19 -05:00
parent dcc67b28bd
commit 5168855287
30 changed files with 754 additions and 334 deletions

View File

@ -76,6 +76,33 @@ I have my own scripts to do this for me, and probably won't ever document or pub
Please see the [aqualinkd.conf]
(https://github.com/sfeakes/aqualinkd/blob/master/extras/aqualinkd.conf)
example in the release directory. Many things are turned off by default, and you may need to enable or configure them for your setup.
Main item to configure is the Aqualink RS485 address so it doesn't conflict with any existing control panels or equiptment. By default it's set to 0x0a which is the second usable address of an allbutton control panel. Included is a serial loging tool that can let you know all information on the RS485 buss, you can use this to find a good address.
```
make slog <-- will make the serial logging tool
./release/serial_logger /dev/ttyUSB0 <- is probably all you'll need to run.
--- example output ---
Notice: Logging serial information, please wait!
Notice: ID's found
Notice: ID 0x08 is in use
Notice: ID 0x60 is not used
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
Notice: ID 0x41 is not used
Notice: ID 0x42 is not used
Notice: ID 0x43 is not used
<-- snip -->
Notice: ID 0x50 is not used
Notice: ID 0x58 is not used
Notice: ID 0x09 is not used <-- can use for Aqualinkd
Notice: ID 0x88 is in use
Any address listed with Aqualinkd can be used. This is the `device-id` address in the config file.
Other command like options for serial_ligger are :-
-d (print out every packet)
-p 1000 (log 1000 packets, default is 200)
```
Specifically, make sure you configure your MQTT, Pool Equiptment Labels & Domoticz ID's in there, looking at the file it should be self explanatory.
## Configuration with home automation hubs
@ -110,12 +137,19 @@ aqualinkd/Spa_Mode
aqualinkd/Aux_1
....Aux_2-6....
aqualinkd/Aux_7
aqualinkd/Pool_Heater
aqualinkd/Spa_Heater
aqualinkd/Pool_Heater (0 off, 1 on and heating)
aqualinkd/Pool_Heater/enabeled (0 off, 1 on but not heating - water has reached target temp)
aqualinkd/Spa_Heater (0 off, 1 on and heating )
aqualinkd/Spa_Heater/enabeled (0 off, 1 but not heating - water has reached target temp)
aqualinkd/SWG (0 off, 2 on generating salt)
aqualinkd/SWG/enabeled (0 off, 2 on but not generating salt - SWG reported no-flow or equiv)
Other Information (Salt Water Generator)
aqualinkd/SWG/Percent
aqualinkd/SWG/PPM
aqualinkd/SWG/Percent/set (set the SWG percent)
aqualinkd/SWG/Percent_f (since we use a homekit thermostat for SWG and use degC as %, we need to pass degF for US phone)
aqualinkd/SWG/Percent_f/set
```
To turn something on, or set information, simply add `set` to the end of the above topics, and post 1 or 0 in the message for a button, or a number for a setpoint. Topics Aqualinkd will act on.
@ -166,7 +200,18 @@ For the moment, native Homekit support has been removed, it will be added back i
Recomended option for HomeKit support is to make use of the MQTT interface and use [HomeKit2MQTT](https://www.npmjs.com/package/homekit2mqtt) to bridge between Aqualinkd and you Apple (phone/tablet/tv & hub).
* If you don't already have an MQTT broker Installed, install one. Mosquitto is recomended, this can usually be installed with apt-get
* Install [HomeKit2MQTT](https://www.npmjs.com/package/homekit2mqtt). (see webpage for install)
* Then copy the [homekit2mqtt configuration](https://github.com/sfeakes/aqualinkd/blob/master/extras/homekit2mqtt.json) file found in the extras directory `homekit2mqtt.json`
* Then copy the [`homekit2mqtt.json`](https://github.com/sfeakes/aqualinkd/blob/master/extras/homekit2mqtt.json) configuration file found in the extras directory to your homekit2mqtt storage directory.
* If you want to run homekit2mqtt as a daemon service, there are files in the extras directory to help you do this.
* copy [`homekit2mqtt.service`] to `/etc/systemd/system/homekit2mqtt.service`
* copy [`homekit2mqtt.defaults`] to `/etc/defaults/homekit2mqtt`
* create directory `/var/lib/homekitmqtt`
* copy [`homekit2mqtt.json`](https://github.com/sfeakes/aqualinkd/blob/master/extras/homekit2mqtt.json) to `/var/lib/homekitmqtt`
* edit `/etc/defaults/homekit2mqtt` to change and homekit2mqtt config parameters.
* install and start service
* `sudo systemctl enable homekit2mqtt`
* `sudo systemctl daemon-reload`
* `sudo systemctl start homekit2mqtt`
You can of course use a myriad of other HomeKit bridges with the URL endpoints listed in the `All other hubs section`, or MQTT topics listed in the `MQTT` section. The majority of them (including HomeBridge the most popular) use Node and HAP-Node.JS, neither of which I am a fan of for the RaspberryPI. But HomeKit2MQTT seemed to have the least overhead of them all. So that's why the recomendation.

View File

@ -8,12 +8,27 @@
#define SPA_TEMP_TOPIC "Temperature/Spa"
//#define POOL_SETPT_TOPIC "Pool_Heater/setpoint"
//#define SPA_SETPT_TOPIC "Spa_Heater/setpoint"
#define SWG_PERCENT_TOPIC "SWG/Percent"
#define SWG_PERCENT_F_TOPIC "SWG/Percent_f"
#define SWG_PPM_TOPIC "SWG/PPM"
#define SWG_TOPIC "SWG"
#define SWG_ON 1
#define SWG_TOPIC "SWG"
#define SWG_PPM_TOPIC SWG_TOPIC "/PPM"
#define SWG_ENABELED_TOPIC SWG_TOPIC "/enabeled"
#define SWG_PERCENT_TOPIC SWG_TOPIC "/Percent"
#define SWG_PERCENT_F_TOPIC SWG_TOPIC "/Percent_f"
#define POOL_THERMO_TEMP_TOPIC BTN_POOL_HTR "/Temperature"
#define SPA_THERMO_TEMP_TOPIC BTN_SPA_HTR "/Temperature"
#define AIR_TEMPERATURE "Air"
#define POOL_TEMPERATURE "Pool_Water"
#define SPA_TEMPERATURE "Spa_Water"
/*
#define AIR_TEMPERATURE_TOPIC AIR_TEMPERATURE "/Temperature"
#define POOL_TEMPERATURE_TOPIC POOL_TEMPERATURE "/Temperature"
#define SPA_TEMPERATURE_TOPIC SPA_TEMPERATURE "/Temperature"
*/
#define SWG_ON 2
#define SWG_OFF 0
#define FREEZE_PROTECT "Freeze_Protect"

View File

@ -431,7 +431,9 @@ void *set_aqualink_light_colormode( void *ptr )
char *buf = (char*)threadCtrl->thread_args;
int val = atoi(&buf[0]);
int btn = atoi(&buf[5]);
float pmode = atof(&buf[10]);
int iOn = atoi(&buf[10]);
int iOff = atoi(&buf[15]);
float pmode = atof(&buf[20]);
if (btn < 0 || btn >= TOTAL_BUTTONS ) {
logMessage(LOG_ERR, "Can't program light mode on button %d\n", btn);
@ -442,7 +444,7 @@ void *set_aqualink_light_colormode( void *ptr )
aqkey *button = &aq_data->aqbuttons[btn];
unsigned char code = button->code;
logMessage(LOG_NOTICE, "Pool Light Programming #: %d, on button: %s, with mode: %f\n", val, button->label, pmode);
logMessage(LOG_NOTICE, "Pool Light Programming #: %d, on button: %s, with pause mode: %f (initial on=%d, initial off=%d)\n", val, button->label, pmode, iOn, iOff);
// Simply turn the light off if value is 0
if (val <= 0) {
@ -459,13 +461,15 @@ void *set_aqualink_light_colormode( void *ptr )
if ( button->led->state != ON ) {
logMessage(LOG_INFO, "Pool Light Initial state off, turning on for 15 seconds\n");
send_cmd(code, aq_data);
delay(15 * seconds);
//delay(15 * seconds);
delay(iOn * seconds);
}
logMessage(LOG_INFO, "Pool Light turn off for 12 seconds\n");
// Now need to turn off for between 11 and 14 seconds to reset light.
send_cmd(code, aq_data);
delay(12 * seconds);
//delay(12 * seconds);
delay(iOff * seconds);
// Now light is reset, pulse the appropiate number of times to advance program.
logMessage(LOG_INFO, "Pool Light button pulsing on/off %d times\n", val);

View File

@ -21,10 +21,12 @@ typedef enum {
struct programmingThreadCtrl {
pthread_t thread_id;
//void *thread_args;
char thread_args[20];
char thread_args[25];
struct aqualinkdata *aq_data;
};
#define LIGHT_MODE_BUFER 25
//void aq_programmer(program_type type, void *args, struct aqualinkdata *aq_data);
void aq_programmer(program_type type, char *args, struct aqualinkdata *aq_data);
void kick_aq_program_thread(struct aqualinkdata *aq_data);

BIN
aq_programmer.o Normal file

Binary file not shown.

View File

@ -12,7 +12,8 @@
#define PKT_STATUS_BYTES 5
#define DEV_MASTER 0
#define DEV_MASTER 0x00
#define SWG_DEV_ID 0x50
// PACKET DEFINES
#define NUL 0x00

BIN
aq_serial.o Normal file

Binary file not shown.

View File

@ -780,10 +780,10 @@ void main_loop() {
}
} else if (packet_length > 0) {
// printf("packet not for us %02x\n",packet_buffer[PKT_DEST]);
if (packet_buffer[PKT_DEST] == 0x00 && interestedInNextAck == true) {
if (packet_buffer[PKT_DEST] == DEV_MASTER && interestedInNextAck == true) {
if ( packet_buffer[PKT_CMD] == CMD_PPM ) {
_aqualink_data.ar_swg_status = packet_buffer[5];
if (_aqualink_data.swg_delayed_percent != TEMP_UNKNOWN && _aqualink_data.ar_swg_status == 0x00) { // We have a delayed % to set.
if (_aqualink_data.swg_delayed_percent != TEMP_UNKNOWN && _aqualink_data.ar_swg_status == DEV_MASTER) { // We have a delayed % to set.
char sval[10];
snprintf(sval, 9, "%d", _aqualink_data.swg_delayed_percent);
aq_programmer(AQ_SET_SWG_PERCENT, sval, &_aqualink_data);
@ -792,10 +792,10 @@ void main_loop() {
}
}
interestedInNextAck = false;
} else if (interestedInNextAck == true && packet_buffer[PKT_DEST] != 0x00) {
} else if (interestedInNextAck == true && packet_buffer[PKT_DEST] != DEV_MASTER) {
_aqualink_data.ar_swg_status = SWG_STATUS_OFF;
interestedInNextAck = false;
} else if (packet_buffer[PKT_DEST] == 0x50) {
} else if (packet_buffer[PKT_DEST] == SWG_DEV_ID) {
interestedInNextAck = true;
} else {
interestedInNextAck = false;

BIN
aqualinkd.o Normal file

Binary file not shown.

View File

@ -69,9 +69,15 @@ void init_parameters (struct aqconfig * parms)
//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;
parms->light_programming_initial_on = 15;
parms->light_programming_initial_off = 12;
parms->light_programming_button = 5;
parms->deamonize = true;
parms->log_file = '\0';
parms->pda_mode = false;
parms->convert_mqtt_temp = true;
parms->convert_dz_temp = true;
parms->report_zero_spa_temp = false;
generate_mqtt_id(parms->mqtt_ID, MQTT_ID_LEN);
}
@ -237,6 +243,12 @@ void readCfg (struct aqconfig *config_parameters, struct aqualinkdata *aqdata, c
config_parameters->dzidx_spa_water_temp = strtoul(indx+1, NULL, 10);
} else if (strncasecmp (b_ptr, "light_programming_mode", 21) == 0) {
config_parameters->light_programming_mode = atof(cleanalloc(indx+1)); // should free this
} else if (strncasecmp (b_ptr, "light_programming_initial_on", 27) == 0) {
config_parameters->light_programming_initial_on = strtoul(indx+1, NULL, 10);
} else if (strncasecmp (b_ptr, "light_programming_initial_off", 28) == 0) {
config_parameters->light_programming_initial_off = strtoul(indx+1, NULL, 10);
} else if (strncasecmp (b_ptr, "light_programming_button", 21) == 0) {
config_parameters->light_programming_button = strtoul(indx+1, NULL, 10) - 1;
} else if (strncasecmp (b_ptr, "SWG_percent_dzidx", 17) == 0) {
config_parameters->dzidx_swg_percent = strtoul(indx+1, NULL, 10);
} else if (strncasecmp (b_ptr, "SWG_PPM_dzidx", 13) == 0) {
@ -247,6 +259,14 @@ void readCfg (struct aqconfig *config_parameters, struct aqualinkdata *aqdata, c
config_parameters->override_freeze_protect = text2bool(indx+1);
} else if (strncasecmp (b_ptr, "pda_mode", 8) == 0) {
config_parameters->pda_mode = text2bool(indx+1);
} else if (strncasecmp (b_ptr, "convert_mqtt_temp_to_c", 22) == 0) {
config_parameters->convert_mqtt_temp = text2bool(indx+1);
} else if (strncasecmp (b_ptr, "convert_dz_temp_to_c", 21) == 0) {
config_parameters->convert_dz_temp = text2bool(indx+1);
} else if (strncasecmp (b_ptr, "flash_mqtt_buttons", 18) == 0) {
config_parameters->flash_mqtt_buttons = text2bool(indx+1);
} else if (strncasecmp (b_ptr, "report_zero_spa_temp", 20) == 0) {
config_parameters->report_zero_spa_temp = text2bool(indx+1);
}/*else if (strncasecmp (b_ptr, "pool_thermostat_dzidx", 21) == 0) { // removed until domoticz has a better virtual thermostat
config_parameters->dzidx_pool_thermostat = strtoul(indx+1, NULL, 10);
} else if (strncasecmp (b_ptr, "spa_thermostat_dzidx", 20) == 0) {

View File

@ -44,8 +44,15 @@ struct aqconfig
int dzidx_swg_ppm;
int dzidx_swg_status;
float light_programming_mode;
int light_programming_initial_on;
int light_programming_initial_off;
int light_programming_button;
bool override_freeze_protect;
bool pda_mode;
bool convert_mqtt_temp;
bool convert_dz_temp;
bool flash_mqtt_buttons;
bool report_zero_spa_temp;
//int dzidx_pool_thermostat; // Domoticz virtual thermostats are crap removed until better
//int dzidx_spa_thermostat; // Domoticz virtual thermostats are crap removed until better
//char mqtt_pub_topic[250];

BIN
config.o Normal file

Binary file not shown.

View File

@ -0,0 +1,4 @@
# Defaults / Configuration options for homebridge
# The following settings tells homebridge where to find the config.json file and where to persist the data (i.e. pairing and others)
HOMEKITMQTT_OPTS=--p 51827 -u mqtt://localhost -m /var/lib/homekit2mqtt/homekit2mqtt.json -s /var/lib/homekit2mqtt -b "Aqualinkd bridge" -a "CC:22:3D:E3:CE:F6" -c "031-45-154" -x --disable-json-parse

View File

@ -2,224 +2,289 @@
"Aqualinkd Pool Air Temperature": {
"id": "AqualinkdPoolAirTemperatureSensor",
"name": "Pool Air Temp",
"service": "TemperatureSensor",
"manufacturer": "Feakes Inc",
"model": "AqualinkDTemperatureSensor",
"topic": {
"statusTemperature": "aqualinkd/Temperature/Air"
},
"payload": {},
"config": {}
},
"Aqualinkd Pool Water Temperature": {
"id": "AqualinkdPoolWaterTemperatureSensor",
"name": "Pool Water Temp",
"service": "TemperatureSensor",
"manufacturer": "Feakes Inc",
"model": "AqualinkDTemperatureSensor",
"topic": {
"statusTemperature": "aqualinkd/Temperature/Pool"
},
"payload": {},
"config": {}
},
"Aqualinkd Spa Water Temperature": {
"id": "AqualinkdSpaWaterTemperatureSensor",
"name": "Spa Water Temp",
"service": "TemperatureSensor",
"manufacturer": "Feakes Inc",
"model": "AqualinkDTemperatureSensor",
"topic": {
"statusTemperature": "aqualinkd/Temperature/Spa"
},
"payload": {},
"config": {}
"model": "AqualinkD Temperature Sensor",
"services": [
{
"name": "Pool Air Temp",
"service": "TemperatureSensor",
"topic": {
"statusTemperature": "aqualinkd/Temperature/Air"
},
"payload": {},
"config": {},
"props": {}
}
]
},
"Aqualinkd Filter Pump": {
"id": "AqualinkdFilterPump",
"name": "Filter Pump",
"service": "Switch",
"manufacturer": "Feakes Inc",
"model": "AqualinkDSwitch",
"topic": {
"setOn": "aqualinkd/Filter_Pump/set",
"statusOn": "aqualinkd/Filter_Pump"
},
"payload": {
"onFalse": 0,
"onTrue": 1
},
"config": {}
"model": "AqualinkD Switch",
"services": [
{
"name": "Filter Pump",
"service": "Switch",
"topic": {
"setOn": "aqualinkd/Filter_Pump/set",
"statusOn": "aqualinkd/Filter_Pump"
},
"payload": {
"onFalse": 0,
"onTrue": 1
},
"config": {},
"props": {}
}
]
},
"Aqualinkd Spa Mode": {
"id": "AqualinkdSpaMode",
"name": "Spa Mode",
"service": "Switch",
"manufacturer": "Feakes Inc",
"model": "AqualinkDSwitch",
"topic": {
"setOn": "aqualinkd/Spa_Mode/set",
"statusOn": "aqualinkd/Spa_Mode"
},
"payload": {
"onFalse": 0,
"onTrue": 1
},
"config": {}
"model": "AqualinkD Switch",
"services": [
{
"name": "Spa Mode",
"service": "Switch",
"topic": {
"setOn": "aqualinkd/Spa_Mode/set",
"statusOn": "aqualinkd/Spa_Mode"
},
"payload": {
"onFalse": 0,
"onTrue": 1
},
"config": {},
"props": {}
}
]
},
"Aqualinkd Aux1": {
"id": "AqualinkdAux1Button",
"name": "Aux1",
"service": "Switch",
"name": "Cleaner",
"manufacturer": "Feakes Inc",
"model": "AqualinkdAuxButton",
"topic": {
"setOn": "aqualinkd/Aux_1/set",
"statusOn": "aqualinkd/Aux_1"
},
"payload": {
"onTrue": 1,
"onFalse": 0
}
"model": "AqualinkD Switch",
"services": [
{
"name": "Aux1",
"service": "Switch",
"topic": {
"setOn": "aqualinkd/Aux_1/set",
"statusOn": "aqualinkd/Aux_1"
},
"payload": {
"onTrue": 1,
"onFalse": 0
},
"config": {},
"props": {}
}
]
},
"Aqualinkd Aux2": {
"id": "AqualinkdAux2Button",
"name": "Aux2",
"service": "Switch",
"name": "Waterfall",
"manufacturer": "Feakes Inc",
"model": "AqualinkdAuxButton",
"topic": {
"setOn": "aqualinkd/Aux_2/set",
"statusOn": "aqualinkd/Aux_2"
},
"payload": {
"onTrue": 1,
"onFalse": 0
}
"model": "AqualinkD Switch",
"services": [
{
"name": "Aux2",
"service": "Switch",
"topic": {
"setOn": "aqualinkd/Aux_2/set",
"statusOn": "aqualinkd/Aux_2"
},
"payload": {
"onTrue": 1,
"onFalse": 0
},
"config": {},
"props": {}
}
]
},
"Aqualinkd Aux3": {
"id": "AqualinkdAux3Button",
"name": "Aux3",
"service": "Switch",
"name": "Spa Blower",
"manufacturer": "Feakes Inc",
"model": "AqualinkdAuxButton",
"topic": {
"setOn": "aqualinkd/Aux_3/set",
"statusOn": "aqualinkd/Aux_3"
},
"payload": {
"onTrue": 1,
"onFalse": 0
}
"model": "AqualinkD Switch",
"services": [
{
"name": "Aux3",
"service": "Switch",
"topic": {
"setOn": "aqualinkd/Aux_3/set",
"statusOn": "aqualinkd/Aux_3"
},
"payload": {
"onTrue": 1,
"onFalse": 0
},
"config": {},
"props": {}
}
]
},
"Aqualinkd Aux4": {
"id": "AqualinkdAux4Button",
"name": "Aux4 ",
"service": "Switch",
"name": "Pool Light",
"manufacturer": "Feakes Inc",
"model": "AqualinkdAuxButton",
"topic": {
"setOn": "aqualinkd/Aux_4/set",
"statusOn": "aqualinkd/Aux_4"
},
"payload": {
"onTrue": 1,
"onFalse": 0
}
},
"Aqualinkd Aux5": {
"id": "AqualinkdAux5Button",
"name": "Aux5",
"service": "Switch",
"manufacturer": "Feakes Inc",
"model": "AqualinkdAuxButton",
"topic": {
"setOn": "aqualinkd/Aux_5/set",
"statusOn": "aqualinkd/Aux_5"
},
"payload": {
"onTrue": 1,
"onFalse": 0
}
},
"Aqualinkd Aux6": {
"id": "AqualinkdAux6Button",
"name": "Aux6",
"service": "Switch",
"manufacturer": "Feakes Inc",
"model": "AqualinkdAuxButton",
"topic": {
"setOn": "aqualinkd/Aux_6/set",
"statusOn": "aqualinkd/Aux_6"
},
"payload": {
"onTrue": 1,
"onFalse": 0
}
},
"Aqualinkd Aux7": {
"id": "AqualinkdAux7Button",
"name": "Aux7",
"service": "Switch",
"manufacturer": "Feakes Inc",
"model": "AqualinkdAuxButton",
"topic": {
"setOn": "aqualinkd/Aux_7/set",
"statusOn": "aqualinkd/Aux_7"
},
"payload": {
"onTrue": 1,
"onFalse": 0
}
"model": "AqualinkD Switch",
"services": [
{
"name": "Aux4",
"service": "Switch",
"topic": {
"setOn": "aqualinkd/Aux_4/set",
"statusOn": "aqualinkd/Aux_4"
},
"payload": {
"onTrue": 1,
"onFalse": 0
},
"config": {},
"props": {}
}
]
},
"Aqualinkd PoolHeater": {
"id": "AqualinkdPoolHeater",
"name": "Pool Heater",
"service": "Thermostat",
"manufacturer": "Feakes Inc",
"model": "AqualinkDThermostat",
"topic": {
"setTargetTemperature": "aqualinkd/Pool_Heater/setpoint/set",
"statusTargetTemperature": "aqualinkd/Pool_Heater/setpoint",
"statusCurrentTemperature": "aqualinkd/Temperature/Pool",
"setTargetHeatingCoolingState": "aqualinkd/Pool_Heater/set",
"statusTargetHeatingCoolingState": "aqualinkd/Pool_Heater"
},
"props": {
"TargetTemperature": {
"maxValue": 40,
"minValue": 4
},
"TargetHeatingCoolingState": {
"validValues": [0, 1]
"model": "AqualinkD Thermostat",
"services": [
{
"name": "Pool Heater",
"service": "Thermostat",
"topic": {
"setTargetTemperature": "aqualinkd/Pool_Heater/setpoint/set",
"statusTargetTemperature": "aqualinkd/Pool_Heater/setpoint",
"statusCurrentTemperature": "aqualinkd/Temperature/Pool",
"setTargetHeatingCoolingState": "aqualinkd/Pool_Heater/set",
"statusTargetHeatingCoolingState": "aqualinkd/Pool_Heater/enabeled",
"statusCurrentHeatingCoolingState": "aqualinkd/Pool_Heater"
},
"payload": {},
"config": {},
"props": {
"TargetTemperature": {
"maxValue": 40,
"minValue": 4
},
"CurrentTemperature": {
"maxValue": 44,
"minValue": -17.8
},
"TargetHeatingCoolingState": {
"validValues": [
0,
1
]
}
}
}
},
"payload": {},
"config": {}
]
},
"Aqualinkd SpaHeater": {
"id": "AqualinkdSpaHeater",
"name": "Spa Heater",
"service": "Thermostat",
"manufacturer": "Feakes Inc",
"model": "AqualinkDThermostat",
"topic": {
"setTargetTemperature": "aqualinkd/Spa_Heater/setpoint/set",
"statusTargetTemperature": "aqualinkd/Spa_Heater/setpoint",
"statusCurrentTemperature": "aqualinkd/Temperature/Spa",
"setTargetHeatingCoolingState": "aqualinkd/Spa_Heater/set",
"statusTargetHeatingCoolingState": "aqualinkd/Spa_Heater"
},
"props": {
"TargetTemperature": {
"maxValue": 40,
"minValue": 4
},
"TargetHeatingCoolingState": {
"validValues": [0, 1]
"model": "AqualinkD Thermostat",
"services": [
{
"name": "Spa Heater",
"service": "Thermostat",
"topic": {
"setTargetTemperature": "aqualinkd/Spa_Heater/setpoint/set",
"statusTargetTemperature": "aqualinkd/Spa_Heater/setpoint",
"statusCurrentTemperature": "aqualinkd/Temperature/Spa",
"setTargetHeatingCoolingState": "aqualinkd/Spa_Heater/set",
"statusTargetHeatingCoolingState": "aqualinkd/Spa_Heater/enabeled",
"statusCurrentHeatingCoolingState": "aqualinkd/Spa_Heater"
},
"payload": {},
"config": {},
"props": {
"TargetTemperature": {
"maxValue": 40,
"minValue": 4
},
"CurrentTemperature": {
"maxValue": 44,
"minValue": -17.8
},
"TargetHeatingCoolingState": {
"validValues": [
0,
1
]
}
}
}
},
"payload": {},
"config": {}
]
},
"Aqualinkd Chlorine Generator": {
"id": "AqualinkdChlorine",
"name": "Chlorine Generaor",
"manufacturer": "Feakes Inc",
"model": "AqualinkD SWG (Thermostat)",
"services": [
{
"name": "Chlorine Generaor",
"service": "Thermostat",
"topic": {
"setTargetTemperature": "aqualinkd/SWG/Percent_f/set",
"statusTargetTemperature": "aqualinkd/SWG/Percent_f",
"statusCurrentTemperature": "aqualinkd/SWG/Percent_f",
"statusTargetHeatingCoolingState": "aqualinkd/SWG/enabeled",
"statusCurrentHeatingCoolingState": "aqualinkd/SWG"
},
"payload": {},
"config": {
"TemperatureDisplayUnits": 1
},
"props": {
"TargetTemperature": {
"maxValue": 38.5,
"minValue": -18,
"---minStep": 1
},
"CurrentTemperature": {
"maxValue": 38.4,
"minValue": -17.8
},
"TargetHeatingCoolingState": {
"validValues": [ 0 ]
}
}
}
]
},
"Aqualinkd Salt Parts Per Million": {
"id": "AqualinkdSaltPPMSensor",
"name": "Salt PPM",
"manufacturer": "Feakes Inc",
"model": "AqualinkD Salt PPM Sensor",
"services": [
{
"name": "Salt PPM",
"service": "TemperatureSensor",
"topic": {
"statusTemperature": "aqualinkd/SWG/PPM"
},
"payload": {
"fahrenheit": "true"
},
"config": {},
"props": {
"CurrentTemperature": {
"maxValue": 4000,
"minValue": 0
}
}
}
]
}
}

View File

@ -0,0 +1,27 @@
#[Unit]
#Description=HomeKitMQTT Service
#After=multi-user.target
#
#[Service]
#User=pi
#Type=idle
#ExecStart=/usr/bin/homekitmqtt
#
#[Install]
#WantedBy=multi-user.target
#
[Unit]
Description=Node.js HomeKit Server for MQTT
After=syslog.target network-online.target
[Service]
Type=simple
User=homebridge
EnvironmentFile=/etc/default/homekitmqtt
ExecStart=/usr/bin/homekit2mqtt $HOMEKITMQTT_OPTS
Restart=on-failure
RestartSec=10
KillMode=process
[Install]
WantedBy=multi-user.target

BIN
init_buttons.o Normal file

Binary file not shown.

View File

@ -27,6 +27,7 @@
//#include "web_server.h"
#include "json_messages.h"
#include "domoticz.h"
#include "aq_mqtt.h"
//#define test_message "{\"type\": \"status\",\"version\": \"8157 REV MMM\",\"date\": \"09/01/16 THU\",\"time\": \"1:16 PM\",\"temp_units\": \"F\",\"air_temp\": \"96\",\"pool_temp\": \"86\",\"spa_temp\": \" \",\"battery\": \"ok\",\"pool_htr_set_pnt\": \"85\",\"spa_htr_set_pnt\": \"99\",\"freeze_protection\": \"off\",\"frz_protect_set_pnt\": \"0\",\"leds\": {\"pump\": \"on\",\"spa\": \"off\",\"aux1\": \"off\",\"aux2\": \"off\",\"aux3\": \"off\",\"aux4\": \"off\",\"aux5\": \"off\",\"aux6\": \"off\",\"aux7\": \"off\",\"pool_heater\": \"off\",\"spa_heater\": \"off\",\"solar_heater\": \"off\"}}"
@ -85,6 +86,86 @@ int build_aqualink_error_status_JSON(char* buffer, int size, char *msg)
}
int build_homebridge_JSON(struct aqualinkdata *aqdata, char* buffer, int size)
{
memset(&buffer[0], 0, size);
int length = 0;
int i;
length += sprintf(buffer+length, "{\"type\": \"devices\",");
length += sprintf(buffer+length, " \"devices\": [");
for (i=0; i < 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_themo\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%d\", \"spvalue\": \"%d\", \"value\": \"%d\" },",
aqdata->aqbuttons[i].name,
aqdata->aqbuttons[i].label,
aqdata->aqbuttons[i].led->state==ON?"on":"off",
aqdata->aqbuttons[i].led->state,
aqdata->pool_htr_set_point,
aqdata->pool_temp);
} else if ( strcmp(BTN_SPA_HTR,aqdata->aqbuttons[i].name)==0 && aqdata->spa_htr_set_point != TEMP_UNKNOWN) {
length += sprintf(buffer+length, "{\"type\": \"setpoint_themo\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%d\", \"spvalue\": \"%d\", \"value\": \"%d\" },",
aqdata->aqbuttons[i].name,
aqdata->aqbuttons[i].label,
aqdata->aqbuttons[i].led->state==ON?"on":"off",
aqdata->aqbuttons[i].led->state,
aqdata->spa_htr_set_point,
aqdata->spa_temp);
} else {
length += sprintf(buffer+length, "{\"type\": \"switch\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"status\": \"%d\" },",
aqdata->aqbuttons[i].name,
aqdata->aqbuttons[i].label,
aqdata->aqbuttons[i].led->state==ON?"on":"off",
aqdata->aqbuttons[i].led->state);
}
}
//FREEZE_PROTECT // could add freeze setpoint in future.
if ( aqdata->swg_percent != TEMP_UNKNOWN ) {
length += sprintf(buffer+length, "{\"type\": \"setpoint_swg\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"spvalue\": \"%d\", \"value\": \"%d\" },",
SWG_PERCENT_TOPIC,
"Salt Water Genrator %",
aqdata->ar_swg_status == 0x00?"on":"off",
aqdata->swg_percent,
aqdata->swg_percent);
length += sprintf(buffer+length, "{\"type\": \"Value\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"value\": \"%d\" },",
SWG_PPM_TOPIC,
"Salt Water Generator PPM",
"on",
aqdata->swg_ppm);
}
length += sprintf(buffer+length, "{\"type\": \"Temperature\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"value\": \"%d\" },",
AIR_TEMPERATURE,
"Pool Air Temperature",
"on",
aqdata->air_temp);
length += sprintf(buffer+length, "{\"type\": \"Temperature\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"value\": \"%d\" },",
POOL_TEMPERATURE,
"Pool Water Temperature",
"on",
aqdata->pool_temp);
length += sprintf(buffer+length, "{\"type\": \"Temperature\", \"id\": \"%s\", \"name\": \"%s\", \"state\": \"%s\", \"value\": \"%d\" }",
SPA_TEMPERATURE,
"Spa Water Temperature",
"on",
aqdata->spa_temp);
length += sprintf(buffer+length, "]}");
logMessage(LOG_DEBUG, "WEB: homebridge used %d of %d", length, size);
buffer[length] = '\0';
return strlen(buffer);
//return length;
}
int build_aqualink_status_JSON(struct aqualinkdata *aqdata, char* buffer, int size)
{
//strncpy(buffer, test_message, strlen(test_message)+1);

View File

@ -40,6 +40,7 @@ int build_mqtt_status_JSON(char* buffer, int size, int idx, int nvalue, float se
bool parseJSONmqttrequest(const char *str, size_t len, int *idx, int *nvalue, char *svalue);
int build_aqualink_error_status_JSON(char* buffer, int size, char *msg);
int build_mqtt_status_message_JSON(char* buffer, int size, int idx, int nvalue, char *svalue);
int build_homebridge_JSON(struct aqualinkdata *aqdata, char* buffer, int size);
#endif /* JSON_MESSAGES_H_ */

BIN
json_messages.o Normal file

Binary file not shown.

BIN
mongoose.o Normal file

Binary file not shown.

View File

@ -160,7 +160,7 @@ void send_domoticz_mqtt_temp_msg(struct mg_connection *nc, int idx, int value)
return;
char mqtt_msg[JSON_MQTT_MSG_SIZE];
build_mqtt_status_JSON(mqtt_msg ,JSON_MQTT_MSG_SIZE, idx, 0, (_aqualink_data->temp_units==FAHRENHEIT)?roundf(degFtoC(value)):value);
build_mqtt_status_JSON(mqtt_msg ,JSON_MQTT_MSG_SIZE, idx, 0, (_aqualink_data->temp_units==FAHRENHEIT && _aqualink_config->convert_dz_temp)?roundf(degFtoC(value)):value);
send_mqtt(nc, _aqualink_config->mqtt_dz_pub_topic, mqtt_msg);
}
void send_domoticz_mqtt_numeric_msg(struct mg_connection *nc, int idx, int value)
@ -189,21 +189,51 @@ void send_mqtt_state_msg(struct mg_connection *nc, char *dev_name, aqledstate st
sprintf(mqtt_pub_topic, "%s/%s",_aqualink_config->mqtt_aq_topic, dev_name);
send_mqtt(nc, mqtt_pub_topic, (state==OFF?"0":"1"));
}
void send_mqtt_heater_state_msg(struct mg_connection *nc, char *dev_name, aqledstate state)
{
static char mqtt_pub_topic[250];
sprintf(mqtt_pub_topic, "%s/%s",_aqualink_config->mqtt_aq_topic, dev_name);
if (state == ENABLE) {
send_mqtt(nc, mqtt_pub_topic, "0");
sprintf(mqtt_pub_topic, "%s/%s/enabeled",_aqualink_config->mqtt_aq_topic, dev_name);
send_mqtt(nc, mqtt_pub_topic, "1");
} else {
send_mqtt(nc, mqtt_pub_topic, (state==OFF?"0":"1"));
sprintf(mqtt_pub_topic, "%s/%s/enabeled",_aqualink_config->mqtt_aq_topic, dev_name);
send_mqtt(nc, mqtt_pub_topic, (state==OFF?"0":"1"));
}
}
// NSF need to change this function to the _new once finished.
void send_mqtt_temp_msg(struct mg_connection *nc, char *dev_name, long value)
{
static char mqtt_pub_topic[250];
static char degC[5];
sprintf(degC, "%.2f", (_aqualink_data->temp_units==FAHRENHEIT)?degFtoC(value):value );
sprintf(degC, "%.2f", (_aqualink_data->temp_units==FAHRENHEIT && _aqualink_config->convert_mqtt_temp)?degFtoC(value):value );
sprintf(mqtt_pub_topic, "%s/%s", _aqualink_config->mqtt_aq_topic, dev_name);
send_mqtt(nc, mqtt_pub_topic, degC);
}
/*
void send_mqtt_temp_msg_new(struct mg_connection *nc, char *dev_name, long value)
{
static char mqtt_pub_topic[250];
static char degC[5];
// NSF remove false below once we have finished.
sprintf(degC, "%.2f", (false && _aqualink_data->temp_units==FAHRENHEIT && _aqualink_config->convert_mqtt_temp)?degFtoC(value):value );
//sprintf(degC, "%d", value );
sprintf(mqtt_pub_topic, "%s/%s", _aqualink_config->mqtt_aq_topic, dev_name);
send_mqtt(nc, mqtt_pub_topic, degC);
}
*/
void send_mqtt_setpoint_msg(struct mg_connection *nc, char *dev_name, long value)
{
static char mqtt_pub_topic[250];
static char degC[5];
sprintf(degC, "%.2f", (_aqualink_data->temp_units==FAHRENHEIT)?degFtoC(value):value );
sprintf(degC, "%.2f", (_aqualink_data->temp_units==FAHRENHEIT && _aqualink_config->convert_mqtt_temp)?degFtoC(value):value );
sprintf(mqtt_pub_topic, "%s/%s/setpoint", _aqualink_config->mqtt_aq_topic, dev_name);
send_mqtt(nc, mqtt_pub_topic, degC);
}
@ -237,6 +267,8 @@ void send_mqtt_int_msg(struct mg_connection *nc, char *dev_name, int value) {
void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
{
static int cnt=0;
static long int lastFlashTm = 0;
static bool lastFlash = false;
bool force_update = false;
if (cnt > 300) { // 100 = about every 2 minutes.
@ -253,34 +285,47 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
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;
send_mqtt_temp_msg(nc, AIR_TEMP_TOPIC, _aqualink_data->air_temp);
//send_mqtt_temp_msg_new(nc, AIR_TEMPERATURE_TOPIC, _aqualink_data->air_temp);
send_domoticz_mqtt_temp_msg(nc, _aqualink_config->dzidx_air_temp, _aqualink_data->air_temp);
}
if (_aqualink_data->pool_temp != TEMP_UNKNOWN && _aqualink_data->pool_temp != _last_mqtt_aqualinkdata.pool_temp) {
_last_mqtt_aqualinkdata.pool_temp = _aqualink_data->pool_temp;
send_mqtt_temp_msg(nc, POOL_TEMP_TOPIC, _aqualink_data->pool_temp);
send_domoticz_mqtt_temp_msg(nc, _aqualink_config->dzidx_pool_water_temp, _aqualink_data->pool_temp);
// If we get pool temp, we know spa is inactive, so set spa temp to pool if configured
_aqualink_data->spa_temp = _aqualink_config->report_zero_spa_temp?32:_aqualink_data->pool_temp;
}
if (_aqualink_data->spa_temp != TEMP_UNKNOWN && _aqualink_data->spa_temp != _last_mqtt_aqualinkdata.spa_temp) {
_last_mqtt_aqualinkdata.spa_temp = _aqualink_data->spa_temp;
send_mqtt_temp_msg(nc, SPA_TEMP_TOPIC, _aqualink_data->spa_temp);
send_domoticz_mqtt_temp_msg(nc, _aqualink_config->dzidx_spa_water_temp, _aqualink_data->spa_temp);
} /*else if (_aqualink_data->spa_temp == TEMP_UNKNOWN && _aqualink_config->report_zero_spa_temp ) {
_aqualink_data->spa_temp = _last_mqtt_aqualinkdata.spa_temp = _aqualink_config->report_zero_spa_temp?32:_aqualink_data->pool_temp;
} else if (_aqualink_data->pool_temp != TEMP_UNKNOWN && _aqualink_data->pool_temp != _last_mqtt_aqualinkdata.spa_temp && _aqualink_config->report_zero_spa_temp == false) {
// Use Pool Temp is Spa is not available
_last_mqtt_aqualinkdata.spa_temp = _aqualink_data->pool_temp;
send_mqtt_temp_msg(nc, SPA_TEMP_TOPIC, _aqualink_data->spa_temp);
send_domoticz_mqtt_temp_msg(nc, _aqualink_config->dzidx_spa_water_temp, _aqualink_data->spa_temp);
} else if (_aqualink_data->pool_temp != TEMP_UNKNOWN && _aqualink_data->pool_temp != _last_mqtt_aqualinkdata.spa_temp) {
// Use Pool Temp is Spa is not available
_last_mqtt_aqualinkdata.spa_temp = _aqualink_data->pool_temp;
send_mqtt_temp_msg(nc, SPA_TEMP_TOPIC, _aqualink_data->pool_temp);
send_domoticz_mqtt_temp_msg(nc, _aqualink_config->dzidx_spa_water_temp, _aqualink_data->pool_temp);
}
_aqualink_data->spa_temp = _last_mqtt_aqualinkdata.spa_temp = _aqualink_config->report_zero_spa_temp?32:_aqualink_data->pool_temp;
//_last_mqtt_aqualinkdata.spa_temp = _aqualink_data->pool_temp;
send_mqtt_temp_msg(nc, SPA_TEMP_TOPIC, _aqualink_data->spa_temp);
send_domoticz_mqtt_temp_msg(nc, _aqualink_config->dzidx_spa_water_temp, _aqualink_data->spa_temp);
}
*/
if (_aqualink_data->pool_htr_set_point != TEMP_UNKNOWN && _aqualink_data->pool_htr_set_point != _last_mqtt_aqualinkdata.pool_htr_set_point) {
_last_mqtt_aqualinkdata.pool_htr_set_point = _aqualink_data->pool_htr_set_point;
send_mqtt_setpoint_msg(nc, BTN_POOL_HTR, _aqualink_data->pool_htr_set_point);
// removed until domoticz has a better virtuel thermostat
//send_domoticz_mqtt_temp_msg(nc, _aqualink_config->dzidx_pool_thermostat, _aqualink_data->pool_htr_set_point);
}
if (_aqualink_data->spa_htr_set_point != TEMP_UNKNOWN && _aqualink_data->spa_htr_set_point != _last_mqtt_aqualinkdata.spa_htr_set_point) {
_last_mqtt_aqualinkdata.spa_htr_set_point = _aqualink_data->spa_htr_set_point;
send_mqtt_setpoint_msg(nc, BTN_SPA_HTR, _aqualink_data->spa_htr_set_point);
// removed until domoticz has a better virtuel thermostat
//send_domoticz_mqtt_temp_msg(nc, _aqualink_config->dzidx_spa_thermostat, _aqualink_data->spa_htr_set_point);
}
if (_aqualink_data->frz_protect_set_point != TEMP_UNKNOWN && _aqualink_data->frz_protect_set_point != _last_mqtt_aqualinkdata.frz_protect_set_point) {
_last_mqtt_aqualinkdata.frz_protect_set_point = _aqualink_data->frz_protect_set_point;
@ -301,6 +346,11 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
}
}
if (_aqualink_data->ar_swg_status != _last_mqtt_aqualinkdata.ar_swg_status) {
if (_aqualink_data->ar_swg_status == SWG_STATUS_OFF)
send_mqtt_int_msg(nc, SWG_ENABELED_TOPIC, SWG_OFF);
else
send_mqtt_int_msg(nc, SWG_ENABELED_TOPIC, SWG_ON);
switch (_aqualink_data->ar_swg_status) {
// Level = (0=gray, 1=green, 2=yellow, 3=orange, 4=red)
case 0x00:
@ -364,7 +414,22 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc)
if (_last_mqtt_aqualinkdata.aqualinkleds[i].state != _aqualink_data->aqbuttons[i].led->state){
_last_mqtt_aqualinkdata.aqualinkleds[i].state = _aqualink_data->aqbuttons[i].led->state;
if (_aqualink_data->aqbuttons[i].dz_idx != DZ_NULL_IDX) {
send_mqtt_state_msg(nc, _aqualink_data->aqbuttons[i].name, _aqualink_data->aqbuttons[i].led->state);
if (_aqualink_data->aqbuttons[i].code == KEY_POOL_HTR || _aqualink_data->aqbuttons[i].code == KEY_SPA_HTR) {
send_mqtt_heater_state_msg(nc, _aqualink_data->aqbuttons[i].name, _aqualink_data->aqbuttons[i].led->state);
} else if (_aqualink_data->aqbuttons[i].led->state == FLASH && _aqualink_config->flash_mqtt_buttons == true) {
time_t now;
now = time(NULL);
if ( now != lastFlashTm ) {
lastFlashTm = now;
_aqualink_data->aqbuttons[i].led->state = lastFlash;
_last_mqtt_aqualinkdata.aqualinkleds[i].state = !lastFlash;
send_mqtt_state_msg(nc, _aqualink_data->aqbuttons[i].name, _last_mqtt_aqualinkdata.aqualinkleds[i].state);
logMessage(LOG_DEBUG, "Flash button : %s\n",_aqualink_data->aqbuttons[i].name);
}
} else {
send_mqtt_state_msg(nc, _aqualink_data->aqbuttons[i].name, _aqualink_data->aqbuttons[i].led->state);
}
send_domoticz_mqtt_state_msg(nc, _aqualink_data->aqbuttons[i].dz_idx, (_aqualink_data->aqbuttons[i].led->state==OFF?DZ_OFF:DZ_ON));
}
// Send mqtt
@ -395,9 +460,14 @@ int getTempforMeteohub(char *buffer)
void set_light_mode(char *value)
{
char buf[20];
char buf[LIGHT_MODE_BUFER];
// 5 below is light index, need to look this up so it's not hard coded.
sprintf(buf, "%-5s%-5d%.2f",value, 5, _aqualink_config->light_programming_mode );
sprintf(buf, "%-5s%-5d%-5d%-5d%.2f",value,
_aqualink_config->light_programming_button,
_aqualink_config->light_programming_initial_on ,
_aqualink_config->light_programming_initial_off,
_aqualink_config->light_programming_mode );
//logMessage(LOG_NOTICE, "WEB: requset light mode %s\n", buf);
aq_programmer(AQ_SET_COLORMODE, buf, _aqualink_data);
}
@ -432,7 +502,6 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg)
int size = getTempforMeteohub(data);
mg_send_head(nc, 200, size, "Content-Type: text/plain");
mg_send(nc, data, size);
} else if (strcmp(command, "poollightmode") == 0) {
char value[20];
mg_get_http_var(&http_msg->query_string, "value", value, sizeof(value));
@ -440,7 +509,6 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg)
set_light_mode(value);
mg_send_head(nc, 200, strlen(GET_RTN_OK), "Content-Type: text/plain");
mg_send(nc, GET_RTN_OK, strlen(GET_RTN_OK));
} else if (strcmp(command, "diag") == 0) {
aq_programmer(AQ_GET_DIAGNOSTICS_MODEL, NULL, _aqualink_data);
mg_send_head(nc, 200, strlen(GET_RTN_OK), "Content-Type: text/plain");
@ -462,6 +530,11 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg)
} else if (strcmp(command, "pool_htr_set_pnt") == 0) {
} else if (strcmp(command, "spa_htr_set_pnt") == 0) {
} else if (strcmp(command, "frz_protect_set_pnt") == 0) {
} else if (strcmp(command, "homebridge") == 0) {
char message[JSON_LABEL_SIZE*10];
int size = build_homebridge_JSON(_aqualink_data, message, JSON_LABEL_SIZE*10);
mg_send_head(nc, 200, size, "Content-Type: application/json");
mg_send(nc, message, size);
} else {
int i;
for (i = 0; i < TOTAL_BUTTONS; i++) {
@ -520,6 +593,7 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg)
// logMessage (LOG_DEBUG, "Doc root=%s\n",opts.document_root);
mg_serve_http(nc, http_msg, s_http_server_opts);
}
}
void action_websocket_request(struct mg_connection *nc, struct websocket_message *wm) {
@ -637,8 +711,14 @@ void action_mqtt_message(struct mg_connection *nc, struct mg_mqtt_message *msg)
}
// logMessage(LOG_INFO, "MQTT: topic %.*s %.2f, setting %s\n",msg->topic.len, msg->topic.p, value);
time(&_aqualink_data->unactioned.requested);
} else if ((pt3 != NULL && (strncmp(pt1, "SWG", 3) == 0) && (strncmp(pt2, "Percent_f", 8) == 0) && (strncmp(pt3, "set", 3) == 0))) {
int val = _aqualink_data->unactioned.value = (_aqualink_data->temp_units == FAHRENHEIT) ? round(degCtoF(value)) : round(value);
//} else if ((pt3 != NULL && (strncmp(pt1, "SWG", 3) == 0) && (strncmp(pt2, "Percent_f", 8) == 0) && (strncmp(pt3, "set", 3) == 0))) {
} else if ((pt3 != NULL && (strncmp(pt1, "SWG", 3) == 0) && (strncmp(pt2, "Percent", 7) == 0) && (strncmp(pt3, "set", 3) == 0))) {
int val;
if ( (strncmp(pt2, "Percent_f", 9) == 0) ) {
val = _aqualink_data->unactioned.value = round(degCtoF(value));
} else {
val = _aqualink_data->unactioned.value = round(value);
}
// Convert number to nearest 5, since those are the incruments, NSF check 100 or 101
if (0 != (val % 5) )
val = _aqualink_data->unactioned.value = ((val + 5) / 10) * 10;
@ -745,6 +825,7 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
//nc->user_data = WEB;
http_msg = (struct http_message *)ev_data;
action_web_request(nc, http_msg);
logMessage(LOG_DEBUG, "Served WEB request\n");
break;
case MG_EV_WEBSOCKET_HANDSHAKE_DONE:

BIN
net_services.o Normal file

Binary file not shown.

Binary file not shown.

View File

@ -31,6 +31,16 @@ serial_port=/dev/ttyUSB0
# 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
# Flash MQTT button on/off like control panel does in cool down mode.
flash_mqtt_buttons = 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 = no
# mqtt stuff
#mqtt_address = localhost:1883
#mqtt_user = someusername
@ -44,6 +54,8 @@ override_freeze_protect = no
# Working RS ID's are 0x0a 0x0b 0x09 0x08 <- 0x08 is usually taken
device_id=0x0a
# 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.
@ -52,6 +64,15 @@ device_id=0x0a
# 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=13
pool_water_temp_dzidx=14
@ -61,37 +82,37 @@ SWG_PPM_dzidx=153
# Labels for standard butons (shown in web UI), and domoticz idx's
button_01_label=Filter Pump
button_01_dzidx=37
#button_01_dzidx=37
button_02_label=Spa Mode
button_02_dzidx=38
#button_02_dzidx=38
button_03_label=Cleaner
button_03_dzidx=39
#button_03_dzidx=39
button_04_label=Waterfall
button_04_dzidx=40
#button_04_dzidx=40
button_05_label=Spa Blower
button_05_dzidx=41
#button_05_dzidx=41
button_06_label=Pool Light
button_06_dzidx=42
#button_06_dzidx=42
button_07_label=Spa Light
button_07_dzidx=43
#button_07_dzidx=43
button_08_label=NONE
button_08_dzidx=NONE
#button_08_dzidx=NONE
button_09_label=NONE
button_09_dzidx=NONE
#button_09_dzidx=NONE
button_10_label=Pool Heater
button_10_dzidx=44
#button_10_dzidx=44
button_11_label=Spa Heater
button_11_dzidx=56
#button_11_dzidx=56
button_12_label=Solar Heater
button_12_dzidx=NONE
#button_12_dzidx=NONE

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

@ -0,0 +1,121 @@
# aqualinkd.conf
#
# The directory where the web files are stored
web_directory=/var/www/aqualinkd/
# 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
# 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
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
# 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=0x09
convert_mqtt_temp_to_c = yes
convert_dz_temp_to_c = yes
flash_mqtt_buttons = 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 = no
#device_id=0x60
#pda_mode = 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
# 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
# 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_07_label=Spa Light
button_07_dzidx=43
button_08_label=NONE
button_08_dzidx=NONE
button_09_label=NONE
button_09_dzidx=NONE
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_dzidx=NONE

View File

@ -1,88 +0,0 @@
#!/bin/bash
#
# ROOT=/nas/data/Development/Raspberry/gpiocrtl/test-install
#
BUILD="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
SERVICE="aquarited"
BIN="aquarited"
CFG="aquarited.conf"
SRV="aquarited.service"
DEF="aquarited"
ARC="aquarited_cache.sh"
BINLocation="/usr/local/bin"
CFGLocation="/etc"
SRVLocation="/etc/systemd/system"
DEFLocation="/etc/default"
#WEBLocation="/var/www/aquarited/"
#MDNSLocation="/etc/avahi/services/"
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root"
exit 1
fi
if [[ $(mount | grep " / " | grep "(ro,") ]]; then
echo "Root filesystem is readonly, can't install"
exit 1
fi
# Exit if we can't find systemctl
command -v systemctl >/dev/null 2>&1 || { echo "This script needs systemd's systemctl manager, Please check path or install manually" >&2; exit 1; }
# stop service, hide any error, as the service may not be installed yet
systemctl stop $SERVICE > /dev/null 2>&1
SERVICE_EXISTS=$(echo $?)
# mount / rw since stopping deamon will set root_ro
if [[ $(mount | grep " / " | grep "(ro,") ]]; then
mount / -o remount,rw
fi
# copy files to locations, but only copy cfg if it doesn;t already exist
cp $BUILD/$BIN $BINLocation/$BIN
cp $BUILD/$SRV $SRVLocation/$SRV
cp $BUILD/$ARC $BINLocation/$ARC
if [ -f $CFGLocation/$CFG ]; then
echo "Config exists, did not copy new config, you may need to edit existing! $CFGLocation/$CFG"
else
cp $BUILD/$CFG $CFGLocation/$CFG
fi
if [ -f $DEFLocation/$DEF ]; then
echo "Defaults exists, did not copy new defaults to $DEFLocation/$DEF"
else
cp $BUILD/$DEF.defaults $DEFLocation/$DEF
fi
#if [ -f $MDNSLocation/$MDNS ]; then
# echo "Avahi/mDNS defaults exists, did not copy new defaults to $MDNSLocation/$MDNS"
#else
# if [ -d "$MDNSLocation" ]; then
# cp $BUILD/$MDNS.avahi $MDNSLocation/$MDNS
# else
# echo "Avahi/mDNS may not be installed, not copying $MDNSLocation/$MDNS"
# fi
#fi
#
#if [ ! -d "$WEBLocation" ]; then
# mkdir -p $WEBLocation
#fi
#
#cp -r $BUILD/../web/* $WEBLocation
systemctl enable $SERVICE
systemctl daemon-reload
if [ $SERVICE_EXISTS -eq 0 ]; then
echo "Starting daemon $SERVICE"
systemctl start $SERVICE
fi

Binary file not shown.

View File

@ -11,7 +11,7 @@
#include "utils.h"
#define SLOG_MAX 80
#define PACKET_MAX 10000
#define PACKET_MAX 200
/*
typedef enum used {
@ -86,46 +86,59 @@ int main(int argc, char *argv[]) {
serial_id_log slog[SLOG_MAX];
int sindex = 0;
int received_packets = 0;
int logPackets = PACKET_MAX;
int logLevel = LOG_NOTICE;
//int logLevel;
//char buffer[256];
bool idMode = true;
//bool idMode = true;
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]);
if (argc < 2 || access( argv[1], F_OK ) == -1 ) {
fprintf(stderr, "ERROR, first param must be valid serial port, ie:-\n\t%s /dev/ttyUSB0\n\n", argv[0]);
fprintf(stderr, "Optional parameters are -d (debug) & -p <number> (log # packets) ie:=\n\t%s /dev/ttyUSB0 -d -p 1000\n\n", argv[0]);
return 1;
}
for (i = 2; i < argc; i++) {
if (strcmp(argv[i], "-d") == 0) {
logLevel = LOG_DEBUG;
} else if (strcmp(argv[i], "-p") == 0 && i+1 < argc) {
logPackets = atoi(argv[i+1]);
}
}
setLoggingPrms(logLevel, false, false);
rs_fd = init_serial_port(argv[1]);
signal(SIGINT, intHandler);
signal(SIGTERM, intHandler);
logMessage(LOG_NOTICE, "Logging serial information, please wait!\n");
while (_keepRunning == true) {
if (rs_fd < 0) {
logMessage(LOG_DEBUG, "ERROR, serial port disconnect\n");
logMessage(LOG_ERR, "ERROR, serial port disconnect\n");
}
packet_length = get_packet(rs_fd, packet_buffer);
if (packet_length == -1) {
// Unrecoverable read error. Force an attempt to reconnect.
logMessage(LOG_DEBUG, "ERROR, on serial port\n");
logMessage(LOG_ERR, "ERROR, on serial port\n");
_keepRunning = false;
} else if (packet_length == 0) {
// Nothing read
} else if (packet_length > 0) {
//logMessage(LOG_DEBUG_SERIAL, "Received Packet for ID 0x%02hhx of type %s\n", packet_buffer[PKT_DEST], get_packet_type(packet_buffer, packet_length));
printPacket(lastID, packet_buffer, packet_length);
if (logLevel > LOG_NOTICE)
printPacket(lastID, packet_buffer, packet_length);
if (packet_buffer[PKT_DEST] != DEV_MASTER) {
found = false;
@ -157,18 +170,18 @@ int main(int argc, char *argv[]) {
received_packets++;
}
if (received_packets >= PACKET_MAX) {
if (received_packets >= logPackets) {
_keepRunning = false;
}
}
logMessage(LOG_DEBUG, "\n");
if (sindex >= SLOG_MAX)
logMessage(LOG_DEBUG, "Ran out of storage, some ID's were not captured, please increase SLOG_MAX and recompile\n");
logMessage(LOG_DEBUG, "ID's found\n");
logMessage(LOG_ERR, "Ran out of storage, some ID's were not captured, please increase SLOG_MAX and recompile\n");
logMessage(LOG_NOTICE, "ID's found\n");
for (i = 0; i <= sindex; i++) {
logMessage(LOG_DEBUG, "ID 0x%02hhx is %s %s\n", slog[i].ID, slog[i].inuse == true ? "in use" : "not used",
slog[i].inuse == false && canUse(slog[i].ID) == true ? " <-- can use for Aqualinkd" : "");
logMessage(LOG_NOTICE, "ID 0x%02hhx is %s %s\n", slog[i].ID, (slog[i].inuse == true) ? "in use" : "not used",
(slog[i].inuse == false && canUse(slog[i].ID) == true)? " <-- can use for Aqualinkd" : "");
}
return 0;

BIN
utils.o Normal file

Binary file not shown.

View File

@ -1,4 +1,4 @@
#define AQUALINKD_NAME "Aqualink Daemon"
#define AQUALINKD_VERSION "0.9k"
#define AQUALINKD_VERSION "1.0"