diff --git a/README.md b/README.md index 2dfaf99..9370236 100644 --- a/README.md +++ b/README.md @@ -139,9 +139,12 @@ NEED TO FIX FOR THIS RELEASE. # Updates in 3.0.0 (dev) * WARNING V3.0 has undergone a significant amount code changes and refactoring, there may be issues. * Serial optimization for AqualinkD HAT. -* upgraded network library ( HTTP(S), MQTT(S), WS ) +* Can now edit webconfig in aqmanager, added many UI customization options. + * web/config.js is now web/config.json any custom settings will need to be migrated. + * Added example plugin of how to get HomeAssistant devices to show up in AqualinkD UI. +* upgraded network library ( HTTP(S), MQTT(S), WS ) * Added support for HTTPS and MQTTS -* Optimized updates to MQTT and WebUI (only update when absolutely necessary) +* Optimized updates to MQTT, web sockets & WebUI (only update when absolutely necessary) * Added options to force upgrades in aqmanager. (add ?upgrade or ?devupgrade to url to enable upgrade button) * MQTT Discovery for all supporting hubs (HomeAssistant Domoticz Hubitat OpenHAB etc) * Moved Domoticz support over to MQTT autodiscovery. @@ -159,7 +162,6 @@ NEED TO FIX FOR THIS RELEASE. * Added preliminary support for Jandy Infinite water color lights - Need to finish off :- * HAT serial optimizations broke some USB serial adapters - * WebUI Config in aqmanager. # Updates in 2.6.11 (Sept 14 2025) diff --git a/docker/aqualinkd-docker.cmd b/docker/aqualinkd-docker.cmd index 9e2baed..dc44b7c 100755 --- a/docker/aqualinkd-docker.cmd +++ b/docker/aqualinkd-docker.cmd @@ -22,6 +22,10 @@ if [ -d "$CONFDIR" ]; then if [ -f "$CONFDIR/config.js" ]; then ln -sf "$CONFDIR/config.js" /var/www/aqualinkd/config.js fi + # If we have a web config, replace the local filesystem with mounted + if [ -f "$CONFDIR/config.json" ]; then + ln -sf "$CONFDIR/config.json" /var/www/aqualinkd/config.json + fi # If don't have a cron file, create one if [ ! -f "$CONFDIR/aqualinkd.schedule" ]; then diff --git a/release/aqualinkd-arm64 b/release/aqualinkd-arm64 index b2165df..fa78b2e 100755 Binary files a/release/aqualinkd-arm64 and b/release/aqualinkd-arm64 differ diff --git a/release/aqualinkd-armhf b/release/aqualinkd-armhf index 013bc9b..1e728d1 100755 Binary files a/release/aqualinkd-armhf and b/release/aqualinkd-armhf differ diff --git a/release/install.sh b/release/install.sh index 1205d4a..8d1202c 100755 --- a/release/install.sh +++ b/release/install.sh @@ -218,21 +218,25 @@ else log "Please install cron, if not the AqualinkD Scheduler will not work" fi +# V3.0.0 uses config.json not config.js +if [ -f "$WEBLocation/config.js" ]; then + log "AqualinkD web config is old, please migrate and settings from $WEBLocation/config.js to $WEBLocation/config.json" +fi # V2.3.9 & V2.6.0 has kind-a breaking change for config.js, so check existing and rename if needed # we added Aux_V? to the button list -if [ -f "$WEBLocation/config.js" ]; then +#if [ -f "$WEBLocation/config.js" ]; then # Test is if has AUX_V1 in file AND "Spa" is in file (Spa_mode changed to Spa) # Version 2.6.0 added Chiller as well - if ! grep -q '"Aux_V1"' "$WEBLocation/config.js" || - ! grep -q '"Spa"' "$WEBLocation/config.js" || - ! grep -q '"Chiller"' "$WEBLocation/config.js" || - ! grep -q '"Aux_S1"' "$WEBLocation/config.js"; then - dateext=`date +%Y%m%d_%H_%M_%S` - log "AqualinkD web config is old, making copy to $WEBLocation/config.js.$dateext" - log "Please make changes to new version $WEBLocation/config.js" - mv $WEBLocation/config.js $WEBLocation/config.js.$dateext - fi -fi + #if ! grep -q '"Aux_V1"' "$WEBLocation/config.js" || + # ! grep -q '"Spa"' "$WEBLocation/config.js" || + # ! grep -q '"Chiller"' "$WEBLocation/config.js" || + # ! grep -q '"Aux_S1"' "$WEBLocation/config.js"; then + # dateext=`date +%Y%m%d_%H_%M_%S` + # log "AqualinkD web config is old, making copy to $WEBLocation/config.js.$dateext" + # log "Please make changes to new version $WEBLocation/config.js" + # mv $WEBLocation/config.js $WEBLocation/config.js.$dateext + #fi +#fi @@ -268,14 +272,14 @@ if [ ! -d "$WEBLocation" ]; then mkdir -p $WEBLocation fi -if [ -f "$WEBLocation/config.js" ]; then - log "AqualinkD web config exists, did not copy new config, you may need to edit existing $WEBLocation/config.js " +if [ -f "$WEBLocation/config.json" ]; then + log "AqualinkD web config exists, did not copy new config, you may need to edit existing $WEBLocation/config.json " if command -v "rsync" &>/dev/null; then - rsync -avq --exclude='config.js' $BUILD/../web/* $WEBLocation + rsync -avq --exclude='config.json' $BUILD/../web/* $WEBLocation else # This isn;t the right way to do it, but seems to work. shopt -s extglob - `cp -r "$BUILD/../web/"!(*config.js) "$WEBLocation"` + `cp -r "$BUILD/../web/"!(*config.json) "$WEBLocation"` shopt -u extglob # Below should work, but doesn't. #shopt -s extglob diff --git a/source/aqualink.h b/source/aqualink.h index 8288340..3689573 100644 --- a/source/aqualink.h +++ b/source/aqualink.h @@ -10,7 +10,8 @@ #include "sensors.h" //#include "aq_panel.h" // Moved to later in file to overcome circular dependancy. (crappy I know) -#define PRINTF(format, ...) printf("%s:%d: " format, __FILE__, __LINE__, ##__VA_ARGS__) +//#define PRINTF(format, ...) printf("%s:%d: " format, __FILE__, __LINE__, ##__VA_ARGS__) +#define PRINTF(format, ...) #define isMASK_SET(bitmask, mask) ((bitmask & mask) == mask) #define setMASK(bitmask, mask) (bitmask |= mask) diff --git a/source/net_services.c b/source/net_services.c index 9137375..d90110e 100644 --- a/source/net_services.c +++ b/source/net_services.c @@ -948,7 +948,7 @@ void mqtt_broadcast_aqualinkstate(struct mg_connection *nc) } -typedef enum {uActioned, uBad, uDevices, uStatus, uHomebridge, uDynamicconf, uDebugStatus, uDebugDownload, uSimulator, uSchedules, uSetSchedules, uAQmanager, uLogDownload, uNotAvailable, uConfig, uSaveConfig, uConfigDownload} uriAtype; +typedef enum {uActioned, uBad, uDevices, uStatus, uHomebridge, uDynamicconf, uDebugStatus, uDebugDownload, uSimulator, uSchedules, uSetSchedules, uAQmanager, uLogDownload, uNotAvailable, uConfig, uSaveConfig, uConfigDownload, uSaveWebConfig} uriAtype; //typedef enum {NET_MQTT=0, NET_API, NET_WS, DZ_MQTT} netRequest; const char actionName[][5] = {"MQTT", "API", "WS", "DZ"}; @@ -1045,6 +1045,8 @@ uriAtype action_URI(request_source from, const char *URI, int uri_length, float return uConfigDownload; } else if (strncmp(ri1, "config/set", 10) == 0) { return uSaveConfig; + } else if (strncmp(ri1, "webconfig/set", 13) == 0) { + return uSaveWebConfig; } else if (strncmp(ri1, "config", 6) == 0) { return uConfig; } else if (strncmp(ri1, "simulator", 9) == 0 && from == NET_WS) { // Only valid from websocket. @@ -1652,7 +1654,7 @@ void action_web_request(struct mg_connection *nc, struct mg_http_message *http_m { char message[JSON_BUFFER_SIZE]; DEBUG_TIMER_START(&tid2); - build_webconfig_js(_aqualink_data, message, JSON_BUFFER_SIZE); + build_dynamic_webconfig_js(_aqualink_data, message, JSON_BUFFER_SIZE); DEBUG_TIMER_STOP(tid2, NET_LOG, "action_web_request() build_webconfig_js took"); mg_http_reply(nc, 200, CONTENT_JS, message); } @@ -1855,6 +1857,14 @@ void action_websocket_request(struct mg_connection *nc, struct mg_ws_message *wm DEBUG_TIMER_STOP(tid, NET_LOG, "action_websocket_request() save_config_js took"); ws_send(nc, message); } + case uSaveWebConfig: + { + DEBUG_TIMER_START(&tid); + char message[JSON_BUFFER_SIZE]; + save_web_config_json((char *)wm->data.buf, wm->data.len, message, JSON_BUFFER_SIZE, _aqualink_data); + DEBUG_TIMER_STOP(tid, NET_LOG, "action_websocket_request() save_web_config_json took"); + ws_send(nc, message); + } break; case uBad: default: diff --git a/source/web_config.c b/source/web_config.c index b59e8d7..1680217 100644 --- a/source/web_config.c +++ b/source/web_config.c @@ -17,13 +17,19 @@ #include #include +#include #include "config.h" #include "color_lights.h" #include "utils.h" +#include "aq_systemutils.h" -int build_webconfig_js(struct aqualinkdata *aqdata, char* buffer, int size) +#define WEBCONFIGFILE "/config.json" +void fprintf_json(FILE *fp,const char *json_string); + +// This should be called dynamic web config, not webconfig to avoid confusion. +int build_dynamic_webconfig_js(struct aqualinkdata *aqdata, char* buffer, int size) { memset(&buffer[0], 0, size); int length = 0; @@ -33,4 +39,126 @@ int build_webconfig_js(struct aqualinkdata *aqdata, char* buffer, int size) length += sprintf(buffer+length, "var _enable_schedules = %s;\n",(_aqconfig_.enable_scheduler)?"true":"false"); return length; -} \ No newline at end of file +} + + + +char* find_nth_char(const char* str, int character, int n) { + char* p = (char*)str; + int count = 0; + + while (count < n && (p = strchr(p, character)) != NULL) { + count++; + if (count == n) { + return p; + } + p++; // Move pointer past the found character for the next search + } + return NULL; // Return NULL if the Nth instance is not found +} + + + + +int save_web_config_json(const char* inBuf, int inSize, char* outBuf, int outSize, struct aqualinkdata *aqdata) +{ + FILE *fp; + char configfile[256]; + bool ro_root; + bool created_file; + char *contents; + + snprintf(configfile, 256, "%s%s", _aqconfig_.web_directory,WEBCONFIGFILE); + + + fp = aq_open_file(configfile, &ro_root, &created_file); + + if (fp == NULL) { + LOG(AQUA_LOG,LOG_ERR, "Open config file failed '%s'\n", configfile); + //remount_root_ro(true); + //fprintf(stdout, "Open file failed 'sprinkler.cron'\n"); + return false; + } + + char* secondColonPos = find_nth_char(inBuf, ':', 2); + // Check if the pointer is not NULL, and calculate the index inside the printf + if (secondColonPos) { + contents = malloc(sizeof(char) * inSize); + int pos = (secondColonPos - inBuf) + 1; + // Need to strip off {"uri":"webconfig/set","values": from beginning and the trailing } + snprintf(contents, inSize - pos, "%s", inBuf+pos); + + // Below 2 will print string to file, but on one line + // use fprintf_json to make look nicer + //fprintf(fp, "%s", contents); + //fprintf(fp,"\n"); + fprintf_json(fp,contents); + + free(contents); + } else { + // Error bad string of something. + LOG(AQUA_LOG,LOG_ERR, "Bad web config '%s'\n", inBuf); + } + //fclose(fp); + aq_close_file(fp, ro_root); + + + return sprintf(outBuf, "{\"message\":\"Saved Web Config\"}"); +} + + + + +void fprint_indentation(FILE *fp, int indent_level) { + for (int i = 0; i < indent_level; i++) { + fprintf(fp," "); // Use 2 spaces for indentation + } +} + +void fprintf_json(FILE *fp, const char *json_string) { + int indent_level = 0; + int in_string = 0; // Flag to track if inside a string literal + + for (int i = 0; i < strlen(json_string); i++) { + char c = json_string[i]; + + if (in_string) { + fprintf(fp,"%c", c); + if (c == '"' && json_string[i - 1] != '\\') { // End of string, handle escaped quotes + in_string = 0; + } + } else { + switch (c) { + case '{': + case '[': + fprintf(fp,"%c\n", c); + indent_level++; + fprint_indentation(fp,indent_level); + break; + case '}': + case ']': + fprintf(fp,"\n"); + indent_level--; + fprint_indentation(fp,indent_level); + fprintf(fp,"%c", c); + break; + case ',': + fprintf(fp,"%c\n", c); + fprint_indentation(fp,indent_level); + break; + case ':': + fprintf(fp,"%c ", c); + break; + case '"': + fprintf(fp,"%c", c); + in_string = 1; + break; + default: + fprintf(fp,"%c", c); + break; + } + } + } + fprintf(fp,"\n"); +} + diff --git a/source/web_config.h b/source/web_config.h index 3715b2f..13deff7 100644 --- a/source/web_config.h +++ b/source/web_config.h @@ -3,7 +3,8 @@ -int build_webconfig_js(struct aqualinkdata *aqdata, char* buffer, int size); +int build_dynamic_webconfig_js(struct aqualinkdata *aqdata, char* buffer, int size); +int save_web_config_json(const char* inBuf, int inSize, char* outBuf, int outSize, struct aqualinkdata *aqdata); #endif \ No newline at end of file diff --git a/web/HA_tilePlugin.js b/web/HA_tilePlugin.js new file mode 100644 index 0000000..26c5f1e --- /dev/null +++ b/web/HA_tilePlugin.js @@ -0,0 +1,234 @@ + + +/* + For manual setup, follow the below. + For easy setup, read the online documentation + + 1) put below in aqualinkd config.json + + "external_script": "/HA_tilePlugin.js" + + 2) Configure any custom icons in aqualinkd config.json, example below 'light.back_floodlights' is HA ID:- + + "light.back_floodlights": { + "display": "true", + "on_icon": "extra/light-on.png", + "off_icon": "extra/light-off-grey.png" + }, + + 3) Add the HA ID's you want to the setTile function below :- + homeassistantAction("light.back_floodlights"); + + 4) IN HOME ASSISTANT you will need to allow Cross-Origin Resource Sharing + edit configuration.yaml, and add below (change aqualinkd to the machine name running aqualinkd) + + http: + cors_allowed_origins + - http://aqualinkd + + 5) Add your HA API token & server below. +*/ + +// Add your HA API token +var HA_TOKEN = ''; +// Change to your HA server IP +var HA_SERVER = 'http://192.168.1.255:8123'; + + +setupTiles(); + + +function setupTiles() { + // If we have specific user agents setup, make sure one matches. + if (_config?.HA_tilePlugin && _config?.HA_tilePlugin?.userAgents) { + var found=false; + for (const agent of _config.HA_tilePlugin.userAgents) { + if (navigator.userAgent.search(agent) != -1) { + found = true; + } + } + // User agent doesn't match the list, return and tdo nothing. + if (!found) { + return; + } + } + + // If aqualinkd has not added tiles, wait. + if ( document.getElementById("Filter_Pump") === null) { + setTimeout(setupTiles, 100); + return; + } + + if (!_config?.HA_tilePlugin) { + // If you are not using config.json to add them, Add your HA ID's below. replace the below examples. + /* ########################################################### + # + # Add manual entries here + # + ########################################################### */ + //homeassistantAction("light.back_floodlights"); + } else { + HA_TOKEN = _config.HA_tilePlugin.HA_token; + HA_SERVER = _config.HA_tilePlugin.HA_server; + for (const id of _config.HA_tilePlugin.HA_entity_ids) { + homeassistantAction(id); + } + } +} + +/*. Some helper / formating functions */ +function getStringAfterCharacter(mainString, character) { + const index = mainString.indexOf(character); + if (index === -1) { + // Character not found, return the original string or an empty string as desired + return mainString; + } + return mainString.slice(index + 1); +} +function getStringBeforeCharacter(mainString, character) { + const index = mainString.indexOf(character); + if (index === -1) { + // Character not found, return the original string or an empty string as desired + return mainString; + } + return mainString.slice(0, index); +} + +function formatTwoDecimalsOrInteger(num) { + const roundedNum = Math.round(num * 100) / 100; + let result = String(roundedNum); + + // Check if the string ends with '.00' and remove it if present + if (result.endsWith('.00')) { + result = result.slice(0, -3); // Remove the last 3 characters (".00") + } + + return result; +} + + +function HA_switchTileState(id) { + state = (document.getElementById(id).getAttribute('status') == 'off') ? 'on' : 'off'; + + homeassistantAction(id, state); + + setTileOn(id, state); +} + + +function HA_updateDevice(data) { + var tile + var name + + if (!data || !data.state) { + console.log("Error, missing JSON values ID="+data?.entity_id+", Name="+data?.attributes?.friendly_name+", State="+data?.state); + return; + } else if (!data.attributes.friendly_name) { + name = getStringAfterCharacter(data.entity_id, "."); + } else { + name = data.attributes.friendly_name; + } + + const service = getStringBeforeCharacter(data.entity_id, "."); + + // If the tile doesn't exist, create it. + if ((tile = document.getElementById(data.entity_id)) == null) { + var tile = {}; + tile["id"] = data.entity_id; + tile["name"] = name; + tile["display"] = "true"; + + if ( service == "sensor" ) { + tile["type"] = "value"; + tile["value"] = data.state; + } else { + tile["type"] = "switch"; // switch or value + } + + if ( service == "cover") { + tile["state"] = ((data.state == 'closed') ? 'off' : 'on') + } else { + tile["state"] = ((data.state == 'off') ? 'off' : 'on'); + } + tile["status"] = tile["state"]; + + createTile(tile); + + // Make sure we use out own callback for button press. + subdiv = document.getElementById(data.entity_id); + subdiv.setAttribute('onclick', "HA_switchTileState('" + data.entity_id + "')"); + tile = document.getElementById(data.entity_id); + } + + switch (service) { + case "sensor": + setTileValue(data.entity_id, formatTwoDecimalsOrInteger(data.state)); + break; + case "cover": + setTileOn(data.entity_id, ((data.state == 'closed') ? 'off' : 'on'), null); + break; + case "light": + case "switch": + case "input_boolean": + default: + setTileOn(data.entity_id, ((data.state == 'off') ? 'off' : 'on'), null); + break; + } + +} + + + + +function getURL(service, action) { + switch (service) { + case "sensor": + return ""; + break; + case "cover": + return '/api/services/'+service+'/'+(action=="on"?"open":"close")+"_cover"; + break; + case "light": + case "switch": + case "input_boolean": + default: + return '/api/services/'+service+'/turn_'+action; + break; + } +} + +function homeassistantAction(id, action="status") { + var http = new XMLHttpRequest(); + if (http) { + http.onreadystatechange = function() { + if (http.readyState === 4) { + if (http.status == 200) { + var data = JSON.parse(http.responseText); + // Sending action on/off returns blank from HA, so only bother acting on status messages + if (action == "status") { + HA_updateDevice(data); + } + } else { + console.error("Http error "+http.status); + } + } + } + }; + + if (action == "status") { + http.open('GET', HA_SERVER+'/api/states/' + id, true); + http.setRequestHeader("Content-Type", "application/json"); + http.setRequestHeader("Authorization", "Bearer "+HA_TOKEN); + _poller = setTimeout(function() { homeassistantAction(id, action); }, 5000); + http.send(null); + } else { + const service = getStringBeforeCharacter(id, "."); + // Below should be api/services/cover/open_cover for cover. + http.open('POST', HA_SERVER+getURL(service,action), true); + http.setRequestHeader("Content-Type", "application/json"); + http.setRequestHeader("Authorization", "Bearer "+HA_TOKEN); + http.send('{"entity_id": "'+id+'"}'); + } + +} + diff --git a/web/aqmanager.html b/web/aqmanager.html index 0e65839..0ffd5a1 100644 --- a/web/aqmanager.html +++ b/web/aqmanager.html @@ -337,6 +337,36 @@ .config_options_added { background-color: white; } + + .web_config_options { + top: 10px; + position: fixed; + display: flex; + justify-content: center; + align-items: center; + width: 100vw; + /*height: 100vw;*/ + } + .web_config_options_pane { + background-color: var(--options_pane_background); + border: 2px solid var(--options_pane_bordercolor); + border-radius: 20px; + justify-content: center; + align-items: center; + width: 700px; + /*max-height: 870px;*/ + max-height: var(--max-config-height); + overflow-x: hidden; + overflow-y: auto; + } + .web_config_textarea { + width:90%; + /*height:700px;*/ + height:var(--web-config-textarea-height); + box-sizing: border-box; + resize: vertical; + } + .hide { display: none; filter: alpha(opacity=0); @@ -358,6 +388,35 @@ height: 18px !important; } + .valid_json { + background-color: var(--options_pane_background); + } + .invalid_json { + background-color: rgb(255, 100, 0); + } + + #alertOverlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); /* Semi-transparent black */ + z-index: 999; +} + +#alertCustomDialog { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: white; + padding: 20px; + border: 1px solid #ccc; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + z-index: 1000; +} + /* .helptxt { @@ -383,6 +442,8 @@ _addedBlankSensor = false; _addedBlankVirtualButton = false; + const _webConfigFile = "/config.json"; + //var _cfgRestartAlertShown = false; let _AlertsShown = {}; @@ -1546,6 +1607,182 @@ */ } + + async function showWebConfig() { + var h = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); + //console.log("Height="+h); + document.documentElement.style.setProperty('--max-config-height', (h-50)+'px'); + + document.documentElement.style.setProperty('--web-config-textarea-height', (h-160)+'px'); + + document.getElementById("web_config_options").classList.remove("hide"); + document.getElementById("web_config_options_pane").classList.remove("hide"); + + + + const jsonInput = document.getElementById('webconfig-json-input'); + + try { + // Fetch the file from the specified path (make sure config.json exists) + const response = await fetch(_webConfigFile, { cache: 'no-store' }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + // Get the response as plain text + const configText = await response.text(); + + // Populate the textarea + jsonInput.value = configText; + jsonInput.placeholder = "Enter or edit your JSON here..."; + webconfig_validateAndDisplay(); // Optional: Validate immediately upon loading + + } catch (error) { + console.error('There was a problem fetching the config file:', error); + jsonInput.value = ''; // Clear placeholder + jsonInput.placeholder = "Could not load "+_webConfigFile+". Enter JSON manually."; + const resultArea = document.getElementById('validation-result'); + resultArea.classList.add('invalid'); + resultArea.textContent = 'Error: Could not load '+_webConfigFile+' from server.'; + } + //populateconfigtable(data); + } + + function closeWebConfig() { + clearconfigtable(); + + document.getElementById("web_config_options").classList.add("hide"); + document.getElementById("web_config_options_pane").classList.add("hide"); + } + + function saveWebConfig() { + if (!webconfig_validateAndDisplay()) { + alert("bad web config, please validate"); + //timedAlert("bad web config, please validate", 5); + return; + } + + const jsonInput = document.getElementById('webconfig-json-input'); + const jsonString = jsonInput.value; + const jsonObject = JSON.parse(jsonString); + + var json_ordered = { + uri: "webconfig/set", + values: jsonObject + }; + send_command(json_ordered); + closeWebConfig(); + } + + // Function to validate and display the JSON + function webconfig_validateAndDisplay() { + const jsonInput = document.getElementById('webconfig-json-input'); + const resultArea = document.getElementById('validation-result'); + const jsonString = jsonInput.value; + + + // Clear previous results + resultArea.className = ''; + resultArea.textContent = ''; + + try { + // Attempt to parse the string into a JavaScript object + const jsonObject = JSON.parse(jsonString); + + // If parsing succeeds, format the JSON nicely (pretty print) + const prettyJsonString = JSON.stringify(jsonObject, null, 2); + + // Update the text area with the pretty-printed JSON + jsonInput.value = prettyJsonString; + + // Display success message + resultArea.classList.add('valid_json'); + resultArea.textContent = 'JSON is VALID.'; + + return true; + } catch (error) { + // If parsing fails, catch the error + + // Display error message + resultArea.classList.add('invalid_json'); + // The error object provides useful information about where the syntax error is + resultArea.textContent = 'JSON is INVALID: ' + error.message; + } + return false; + } + + async function updateWebConfigFromMaster() { + let masterJSON; + // Get new master + try { + // Fetch the file from the specified path (make sure config.json exists) + // BELOW file should be from github. File with any new config keys in it + const response = await fetch("/config.json.tmp", { cache: 'no-store' }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + // Get the response as plain text + const text = await response.text(); + masterJSON = JSON.parse(text) + // Populate the textarea + //jsonInput.value = configText; + //jsonInput.placeholder = "Enter or edit your JSON here..."; + //webconfig_validateAndDisplay(); // Optional: Validate immediately upon loading + + } catch (error) { + alert("Error loading master cfg "+error); + } + // Now get local + try{ + const jsonInput = document.getElementById('webconfig-json-input'); + const jsonString = jsonInput.value; + var webcfgJSON = JSON.parse(jsonString); + + webcfgJSON = mergeMissingKeysRecursive(webcfgJSON, masterJSON); + + jsonInput.value = JSON.stringify(webcfgJSON); + alert(jsonInput.value); + webconfig_validateAndDisplay(); + } catch (error) { + alert("Error "+error); + } + + } + + function mergeMissingKeysRecursive(target, source) { + // Ensure we are dealing with actual objects at this level + if (typeof target !== 'object' || target === null || typeof source !== 'object' || source === null) { + return target; + } + // Iterate over all keys in the source object + for (const key in source) { + if (Object.hasOwn(source, key)) { + const sourceValue = source[key]; + const targetValue = target[key]; + + // If the source value is a nested object and the target value is also + // a nested object (and not an array or null), recurse. + if ( + typeof sourceValue === 'object' && sourceValue !== null && !Array.isArray(sourceValue) && + typeof targetValue === 'object' && targetValue !== null && !Array.isArray(targetValue) + ) { + // Recursively merge the nested objects + target[key] = mergeMissingKeysRecursive(targetValue, sourceValue); + } else if (targetValue === undefined) { + // Otherwise, if the key is missing entirely in the target, add it + target[key] = sourceValue; + // console.log("Added missing key:", key); + } + // If the key already exists and is not an object pair ready for recursion, it is ignored (target takes precedence). + } + } + return target; + } + + function update_status_message(message, error = false) { try { if (error || message.substring(0, 5).toLowerCase() == "error") @@ -2008,6 +2245,21 @@ xmlhttp.setRequestHeader("Accept","application/vnd.github.raw"); xmlhttp.send(); } +/* + function timedAlert(message, timeout) { + document.getElementById('alertDialogMessage').innerText = message; + document.getElementById('alertOverlay').style.display = 'block'; + document.getElementById('alertCustomDialog').style.display = 'block'; + + // Set a timeout to automatically close the dialog + setTimeout(closeAlert, timeout * 1000); + } + + function closeAlert() { + document.getElementById('alertOverlay').style.display = 'none'; + document.getElementById('alertCustomDialog').style.display = 'none'; + } +*/ @@ -2128,6 +2380,10 @@ + + + @@ -2179,9 +2435,51 @@ - + + +
+
> + + + + + + + + + + + + + + + + + +
AqualinkD Web Configuration
+ +
+ + + + + + + +
+
+
+
+
+ \ No newline at end of file diff --git a/web/config.js b/web/config.js deleted file mode 100644 index 7f9085c..0000000 --- a/web/config.js +++ /dev/null @@ -1,180 +0,0 @@ -// Display order of devices. Tiles will be displayed in the order below, - // any devices you don't want to see you can comment the ID. (with // e.g. `//"Solar_Heater",` ) - // If the device isn't listed below is will NOT be shown. - // For a complete list returned from your particular aqualinkd instance - // use the below URL and look at the ID value for each device. - // http://aqualink.ip.address/api/devices - - var config_js=true; - - var devices = [ - "Filter_Pump", - "Spa", - "Aux_1", - "Aux_2", - "Aux_3", - "Aux_4", - "Aux_5", - "Aux_6", - "Aux_7", - "Aux_B1", - "Aux_B2", - "Aux_B3", - "Aux_B4", - "Aux_B5", - "Aux_B6", - "Aux_B7", - "Aux_B8", - "Pool_Heater", - "Spa_Heater", - "SWG", - //"SWG/Percent", - "SWG/PPM", - //"SWG/Boost", - "Temperature/Air", - "Temperature/Pool", - "Temperature/Spa", - "Pool_Water", - "Spa_Water", - "Freeze_Protect", - "CHEM/pH", - "CHEM/ORP", - "Solar_Heater", - "Extra_Aux", - "Chiller", - "Aux_V1", - "Aux_V2", - "Aux_V3", - "Aux_V4", - "Aux_V5", - "Aux_V6", - "Aux_V7", - "Aux_V8", - "Aux_V9", - "Aux_V10", - "Aux_V11", - "Aux_V12", - "Aux_V13", - "Aux_V14", - "Aux_V15", - "Aux_S1", - "Aux_S2", - "Aux_S3", - "Aux_S4", - "Aux_S5", - "Aux_S6", - "Aux_S7", - "Aux_S8", - "Aux_S9", - "Aux_S10", - ]; - - // all SWG return a status number, some have different meanings. Change the text below to suit, NOT THE NUMBER. - var swgStatus = { - 0: "On", - 1: "No flow", - 2: "Low salt", - 4: "High salt", - 8: "Clean cell", - 9: "Turning off", - 16: "High current", - 32: "Low volts", - 64: "Low temp", - 128: "Check PCB", - 253: "General Fault", - 254: "Unknown", - 255: "Off" - } - - - /* - * BELOW IS NOT RELIVENT FOR simple.html or simple inteface - * - */ - // Background image, delete or leave blank for solid color - //var background_url = "http://192.168.144.224/snap.jpeg"; - var background_url='hk/background.jpg'; - //var background_url=''; - // Reload background image every X seconds.(useful if camera snapshot) - // 0 means only load once when page loads. - //var background_reload = 10; - //var background_reload = 0; - - // By default all Variable Speed Pumps will show RPM or GPM depending on how they are controlled. - // this will show RPM for all pumps (ie Jandy VF pumps) - //var show_vsp_gpm=false; - - // Keeps sensor tiles in on state showing last reading. (default true= turn off tiles when device is off. ie SWG=off turn off PPM tile) - //var turn_off_sensortiles = false; - - // This will turn on/off the Spa Heater when you turn on/off Spa Mode. - //var link_spa_and_spa_heater = true; - - /* Example of setting custom range and appropriate messages - var tile_thresholds = { - "SWG/PPM": { - outofrange: {min: 2600, max: 3500, mintext:"Add Salt"}, - attention: {min: 2700, max: 3400, mintext:"Add Salt"} - }, - "CHEM/pH": { - outofrange: {min: 7, max: 8}, - attention: {min: 7.2, max: 7.8, mintext:"Low", maxtext:"High"} - }, - "CHEM/ORP": { - outofrange: {min: 560, max: 900}, - attention: {min: 650, max: 850, mintext:"Low", maxtext:"High"} - }, - // Example of how to set color to Aux_S1 (if Aux_S1 was a CPU temperature) - //"Aux_S1": { - // outofrange: {min: 0, max: 170, maxtext:"ALERT High"}, - // attention: {min: 0, max: 140, maxtext:"High"} - //}, - } - */ - - // Change the min max for heater slider - var heater_slider_min = 36; - var heater_slider_max = 104; - // Change the slider for timers - var timer_slider_min = 0; - //var timer_slider_max = 360; - //var timer_slider_step = 10; - var timer_slider_max = 120; - var timer_slider_step = 1; - - // Colors - - var body_background = "#EBEBEA"; - var body_text = "#000000"; - - var options_pane_background = "#F5F5F5"; - var options_pane_bordercolor = "#7C7C7C"; - var options_slider_highlight = "#2196F3"; - var options_slider_lowlight = "#D3D3D3"; - - var head_background = "#2B6A8F"; - var head_text = "#FFFFFF)"; - var error_background = "#8F2B2B"; - - var tile_background = "#DCDCDC"; - var tile_text = "#6E6E6E"; - var tile_on_background = "#FFFFFF"; - var tile_on_text = "#000000"; - var tile_status_text = "#575757"; - - // Change the default color for vales in and out of range. - //var value_tile_normal_color = "#4ec400ff"; - //var value_tile_attention_color = "#ffbf00ff"; - //var value_tile_outofrange_color = "#ff0000"; - - // Dark colors - // var body_background = "#000000"; - // var tile_background = "#646464"; - // var tile_text = "#B9B9B9"; - // var tile_status_text = "#B2B2B2"; - // var head_background = "#000D53"; - - // REMOVE THIS. - //document.writeln(""); - - diff --git a/web/config.json b/web/config.json new file mode 100644 index 0000000..76c5f9d --- /dev/null +++ b/web/config.json @@ -0,0 +1,302 @@ +{ + "background_image": { + "url": "-hk/background.jpg", + "url_reload": 0 + }, + "colors": { + "body_background": "#EBEBEB", + "body_text": "#000000", + "options_pane_background": "#F5F5F5", + "options_pane_bordercolor": "#7C7C7C", + "options_slider_highlight": "#2196F3", + "options_slider_lowlight": "#D3D3D3", + "head_background": "#2B6A8F", + "head_text": "#FFFFFF", + "error_background": "#8F2B2B", + "tile_background": "#DCDCDC", + "tile_text": "#6E6E6E", + "tile_on_background": "#FFFFFF", + "tile_on_text": "#000000", + "tile_status_text": "#575757", + "value_tile_normal_color": "#049FF8", + "value_tile_normal_color_": "#4ec400ff", + "value_tile_attention_color": "#FF8000", + "value_tile_outofrange_color": "#FF6400", + "options_radio_highlight": "#2196F3", + "options_radio_lowlight": "#D3D3D3", + "tile_icon_background_color_heat": "rgb(255, 123, 0)", + "tile_icon_background_color_cool": "rgb(4, 159, 248)", + "tile_icon_background_color_enabled": "rgb(78, 196, 0)", + "tile_icon_background_color_disabled": "rgb(110, 110, 110)" + }, + "devices": { + "Filter_Pump": { + "display": "true" + }, + "Spa": { + "display": "true" + }, + "Aux_1": { + "display": "true" + }, + "Aux_2": { + "display": "true" + }, + "Aux_3": { + "display": "true" + }, + "Aux_4": { + "display": "true" + }, + "Aux_5": { + "display": "true" + }, + "Aux_6": { + "display": "true" + }, + "Aux_7": { + "display": "true" + }, + "Aux_B1": { + "display": "true" + }, + "Aux_B2": { + "display": "true" + }, + "Aux_B3": { + "display": "true" + }, + "Aux_B4": { + "display": "true" + }, + "Aux_B5": { + "display": "true" + }, + "Aux_B6": { + "display": "true" + }, + "Aux_B7": { + "display": "true" + }, + "Aux_B8": { + "display": "true" + }, + "Pool_Heater": { + "display": "true" + }, + "Spa_Heater": { + "display": "true" + }, + "SWG": { + "display": "true" + }, + "SWG/PPM": { + "display": "true" + }, + "SWG/Percent": { + "display": "false" + }, + "SWG/Boost": { + "display": "false" + }, + "Temperature/Air": { + "display": "true" + }, + "Temperature/Pool": { + "display": "true" + }, + "Temperature/Spa": { + "display": "true" + }, + "Pool_Water": { + "display": "true" + }, + "Spa_Water": { + "display": "true" + }, + "Freeze_Protect": { + "display": "true" + }, + "CHEM/pH": { + "display": "true" + }, + "CHEM/ORP": { + "display": "true" + }, + "Solar_Heater": { + "display": "true" + }, + "Extra_Aux": { + "display": "true" + }, + "Chiller": { + "display": "true" + }, + "Aux_V1": { + "display": "true" + }, + "Aux_V2": { + "display": "true" + }, + "Aux_V3": { + "display": "true" + }, + "Aux_V4": { + "display": "true" + }, + "Aux_V5": { + "display": "true" + }, + "Aux_V6": { + "display": "true" + }, + "Aux_V7": { + "display": "true" + }, + "Aux_V8": { + "display": "true" + }, + "Aux_V9": { + "display": "true" + }, + "Aux_V10": { + "display": "true" + }, + "Aux_V11": { + "display": "true" + }, + "Aux_V12": { + "display": "true" + }, + "Aux_V13": { + "display": "true" + }, + "Aux_V14": { + "display": "true" + }, + "Aux_V15": { + "display": "true" + }, + "Sensor/Aux_S1": { + "display": "true" + }, + "Sensor/Aux_S2": { + "display": "true" + }, + "Sensor/Aux_S3": { + "display": "true" + }, + "Sensor/Aux_S4": { + "display": "true" + }, + "Sensor/Aux_S5": { + "display": "true" + }, + "Sensor/Aux_S6": { + "display": "true" + }, + "Sensor/Aux_S7": { + "display": "true" + }, + "Sensor/Aux_S8": { + "display": "true" + }, + "Sensor/Aux_S9": { + "display": "true" + }, + "Sensor/Aux_S10": { + "display": "true" + } + }, + "slider_range": { + "heater_slider_min": 36, + "heater_slider_max": 104, + "timer_slider_min": 0, + "timer_slider_max": 120, + "timer_slider_step": 1 + }, + "tile_thresholds": { + "SWG/PPM": { + "outofrange": { + "min": 2600, + "max": 3500, + "mintext": "Add Salt" + }, + "attention": { + "min": 2700, + "max": 3400, + "mintext": "Add Salt" + } + }, + "CHEM/pH": { + "outofrange": { + "min": 7, + "max": 8 + }, + "attention": { + "min": 7.2, + "max": 7.8, + "mintext": "Low", + "maxtext": "High" + } + }, + "CHEM/ORP": { + "outofrange": { + "min": 560, + "max": 900 + }, + "attention": { + "min": 650, + "max": 850, + "mintext": "Low", + "maxtext": "High" + } + } + }, + "swg_status": { + "0": "On", + "1": "No flow", + "2": "Low salt", + "4": "High salt", + "8": "Clean cell", + "9": "Turning off", + "16": "High current", + "32": "Low volts", + "64": "Low temp", + "128": "Check PCB", + "253": "General Fault", + "254": "Unknown", + "255": "Off" + }, + "tile_settings": { + "turn_off_sensortiles": "true", + "show_vsp_gpm": "true", + "disable_off_icon_background": "true" + }, + "EXAMPLE_colors_dark": { + "body_background": "#121212", + "body_text": "#FFFFFF", + "options_pane_background": "#1E1E1E", + "options_pane_bordercolor": "#555555", + "options_slider_highlight": "#64B5F6", + "options_slider_lowlight": "#555555", + "head_background": "#004d7a", + "head_text": "#FFFFFF", + "error_background": "#b00020", + "tile_background": "#333333", + "tile_text": "#AAAAAA", + "tile_on_background": "#BBBBBB", + "tile_on_text": "#000000", + "tile_status_text": "#888888", + "value_tile_normal_color": "#049FF8", + "value_tile_normal_color_": "#69F0AE", + "value_tile_attention_color": "#FFB74D", + "value_tile_outofrange_color": "#FF8A65", + "options_radio_highlight": "#64B5F6", + "options_radio_lowlight": "#555555", + "tile_icon_background_color_heat": "rgb(255, 123, 0)", + "tile_icon_background_color_cool": "rgb(4, 159, 248)", + "tile_icon_background_color_enabled": "rgb(78, 196, 0)", + "tile_icon_background_color_disabled": "rgb(110, 110, 110)" + } +} diff --git a/web/controller.html b/web/controller.html deleted file mode 100644 index 0c39106..0000000 --- a/web/controller.html +++ /dev/null @@ -1,3347 +0,0 @@ - - - - - - AqualinkD - - - - - - - - - - - - - - - - - -
- -
- -
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - -
-
-
- -
-
-
-
- -
-
- -
-
-
- -
-
- - - - - - - - - - - - - - - - - -
-
- - -
-
-
- -
-
- -
-
-
- -
-
- - - - - - - - - - - - - - - - - - - - - - - -
-
- - -
- -
-
-
- -
-
- -
-
-
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - -
-
-
- -
-
-
-
- -
-
- -
-
-
- -
-
- - - - - - - - - - - - - - - - - - - - - - - -
-
- - -
-
-
- -
-
-
-
- -
-
- -
-
-
- -
-
- - - - - - - - - - - - - - - - - -
-
- - -
-
-
- -
-
- -
-
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - -
 
EnableMinuteHourDay
Month
MonthDay
Week
DeviceCmdValueDel
- -      - -
- - - - - - - - - - diff --git a/web/controller.old.html b/web/controller.old.html deleted file mode 100644 index 9b4aaf9..0000000 --- a/web/controller.old.html +++ /dev/null @@ -1,1212 +0,0 @@ - - - - - - AqualinkD - - - - - - - - - - - - - - - -
- -
-
- - - - - -
AqualinkD - - -
-
-
-
-
- - - - - - - - - - - - - - - - - -
-
- - -
-
-
- -
-
- -
-
-
-
-
- - - - - - - - - - - - - -
-
-
-
- -
-
- -
-
-
-
-
- - - - - - - - - - - - - - -
-
- - -
- -
- -
-
-
-
- - - - diff --git a/web/controller.simtest.html b/web/controller.simtest.html deleted file mode 100644 index 3c831cc..0000000 --- a/web/controller.simtest.html +++ /dev/null @@ -1,2810 +0,0 @@ - - - - - - AqualinkD - - - - - - - - - - - - - - - - - -
- -
- - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - -
-
-
- -
-
-
-
- -
-
- -
-
-
-
-
- - - - - - - - - - - - - - - - - -
-
- - -
-
-
- -
-
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - -
-
- - -
- -
-
-
- -
-
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - -
-
-
- -
-
-
-
- -
-
- -
-
-
-
-
- - - - - - - - - - - - - - - - - -
-
- - -
-
-
- -
-
- -
-
-
- - -
-
- - -
-
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - -
 
EnableMinuteHourDay
Month
MonthDay
Week
DeviceCmdValueDel
- -      - -
- - - - - - - - - - diff --git a/web/debug.html b/web/debug.html deleted file mode 120000 index b6c9940..0000000 --- a/web/debug.html +++ /dev/null @@ -1 +0,0 @@ -aqmanager.html \ No newline at end of file diff --git a/web/hk/Aux_1-off.png b/web/hk/Aux_1-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_1-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_1-on.png b/web/hk/Aux_1-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_1-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_12-on.png b/web/hk/Aux_12-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_12-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_2-off.png b/web/hk/Aux_2-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_2-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_2-on.png b/web/hk/Aux_2-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_2-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_3-off.png b/web/hk/Aux_3-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_3-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_3-on.png b/web/hk/Aux_3-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_3-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_4-off.png b/web/hk/Aux_4-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_4-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_4-on.png b/web/hk/Aux_4-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_4-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_5-off.png b/web/hk/Aux_5-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_5-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_5-on.png b/web/hk/Aux_5-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_5-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_6-off.png b/web/hk/Aux_6-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_6-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_6-on.png b/web/hk/Aux_6-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_6-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_7-off.png b/web/hk/Aux_7-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_7-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_7-on.png b/web/hk/Aux_7-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_7-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_8-off.png b/web/hk/Aux_8-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_8-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_8-on.png b/web/hk/Aux_8-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_8-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_B1-off.png b/web/hk/Aux_B1-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_B1-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_B1-on.png b/web/hk/Aux_B1-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_B1-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_B2-off.png b/web/hk/Aux_B2-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_B2-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_B2-on.png b/web/hk/Aux_B2-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_B2-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_B3-off.png b/web/hk/Aux_B3-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_B3-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_B3-on.png b/web/hk/Aux_B3-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_B3-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_B4-off.png b/web/hk/Aux_B4-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_B4-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_B4-on.png b/web/hk/Aux_B4-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_B4-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_B5-off.png b/web/hk/Aux_B5-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_B5-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_B5-on.png b/web/hk/Aux_B5-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_B5-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_B6-off.png b/web/hk/Aux_B6-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_B6-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_B6-on.png b/web/hk/Aux_B6-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_B6-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_B7-off.png b/web/hk/Aux_B7-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_B7-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_B7-on.png b/web/hk/Aux_B7-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_B7-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_B8-off.png b/web/hk/Aux_B8-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_B8-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_B8-on.png b/web/hk/Aux_B8-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_B8-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_V1-off.png b/web/hk/Aux_V1-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_V1-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_V1-on.png b/web/hk/Aux_V1-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_V1-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_V10-off.png b/web/hk/Aux_V10-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_V10-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_V10-on.png b/web/hk/Aux_V10-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_V10-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_V11-off.png b/web/hk/Aux_V11-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_V11-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_V11-on.png b/web/hk/Aux_V11-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_V11-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_V12-off.png b/web/hk/Aux_V12-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_V12-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_V12-on.png b/web/hk/Aux_V12-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_V12-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_V13-off.png b/web/hk/Aux_V13-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_V13-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_V13-on.png b/web/hk/Aux_V13-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_V13-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_V14-off.png b/web/hk/Aux_V14-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_V14-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_V14-on.png b/web/hk/Aux_V14-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_V14-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_V15-off.png b/web/hk/Aux_V15-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_V15-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_V15-on.png b/web/hk/Aux_V15-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_V15-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_V2-off.png b/web/hk/Aux_V2-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_V2-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_V2-on.png b/web/hk/Aux_V2-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_V2-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_V3-off.png b/web/hk/Aux_V3-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_V3-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_V3-on.png b/web/hk/Aux_V3-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_V3-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_V4-off.png b/web/hk/Aux_V4-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_V4-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_V4-on.png b/web/hk/Aux_V4-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_V4-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_V5-off.png b/web/hk/Aux_V5-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_V5-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_V5-on.png b/web/hk/Aux_V5-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_V5-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_V6-off.png b/web/hk/Aux_V6-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_V6-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_V6-on.png b/web/hk/Aux_V6-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_V6-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_V7-off.png b/web/hk/Aux_V7-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_V7-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_V7-on.png b/web/hk/Aux_V7-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_V7-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_V8-off.png b/web/hk/Aux_V8-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_V8-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_V8-on.png b/web/hk/Aux_V8-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_V8-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Aux_V9-off.png b/web/hk/Aux_V9-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Aux_V9-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Aux_V9-on.png b/web/hk/Aux_V9-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Aux_V9-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Extra_Aux-off.png b/web/hk/Extra_Aux-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Extra_Aux-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Extra_Aux-on.png b/web/hk/Extra_Aux-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Extra_Aux-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Filter_Pump-off.png b/web/hk/Filter_Pump-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Filter_Pump-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Filter_Pump-on.png b/web/hk/Filter_Pump-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Filter_Pump-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Pool_Heater-off.png b/web/hk/Pool_Heater-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Pool_Heater-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Pool_Heater-on.png b/web/hk/Pool_Heater-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Pool_Heater-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/SWG_Boost-off.png b/web/hk/SWG_Boost-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/SWG_Boost-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/SWG_Boost-on.png b/web/hk/SWG_Boost-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/SWG_Boost-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Solar_Heater-off.png b/web/hk/Solar_Heater-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Solar_Heater-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Solar_Heater-on.png b/web/hk/Solar_Heater-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Solar_Heater-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Spa-off.png b/web/hk/Spa-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Spa-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Spa-on.png b/web/hk/Spa-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Spa-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Spa_Heater-off.png b/web/hk/Spa_Heater-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Spa_Heater-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Spa_Heater-on.png b/web/hk/Spa_Heater-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Spa_Heater-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/Spa_Mode-off.png b/web/hk/Spa_Mode-off.png deleted file mode 120000 index 5f60636..0000000 --- a/web/hk/Spa_Mode-off.png +++ /dev/null @@ -1 +0,0 @@ -./switch-off.png \ No newline at end of file diff --git a/web/hk/Spa_Mode-on.png b/web/hk/Spa_Mode-on.png deleted file mode 120000 index 6afbc0b..0000000 --- a/web/hk/Spa_Mode-on.png +++ /dev/null @@ -1 +0,0 @@ -./switch-on.png \ No newline at end of file diff --git a/web/hk/aqualinkd.png b/web/hk/aqualinkd.png deleted file mode 100644 index 9ffcfd0..0000000 Binary files a/web/hk/aqualinkd.png and /dev/null differ diff --git a/web/hk/switch-off-black.png b/web/hk/switch-off-black.png deleted file mode 100644 index 9aff2aa..0000000 Binary files a/web/hk/switch-off-black.png and /dev/null differ diff --git a/web/hk/switch-off.png b/web/hk/switch-off.png deleted file mode 100644 index 2e7afc7..0000000 Binary files a/web/hk/switch-off.png and /dev/null differ diff --git a/web/hk/switch-on-new.png b/web/hk/switch-on-new.png deleted file mode 100644 index 5b3b609..0000000 Binary files a/web/hk/switch-on-new.png and /dev/null differ diff --git a/web/hk/switch-on.png b/web/hk/switch-on.png deleted file mode 100644 index 417c673..0000000 Binary files a/web/hk/switch-on.png and /dev/null differ diff --git a/web/index.html b/web/index.html deleted file mode 120000 index 3204260..0000000 --- a/web/index.html +++ /dev/null @@ -1 +0,0 @@ -controller.html \ No newline at end of file diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..ea8201f --- /dev/null +++ b/web/index.html @@ -0,0 +1,3623 @@ + + + + + + AqualinkD + + + + + + + + + + + + + + + + + +
+ +
+
+ + + + + + + +
+ + + + + + AqualinkD + + X +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+
+
+ +
+
+
+
+ +
+
+ +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + +
+
+ + +
+
+
+ +
+
+ +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+
+
+ +
+
+ +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+
+
+ +
+
+
+
+ +
+
+ +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+
+
+ +
+
+
+
+ +
+
+ +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + +
+
+ + +
+
+
+ +
+
+ +
+
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
 
EnableMinuteHourDay
Month
MonthDay
Week
DeviceCmdValueDel
+ +      + +
+ + + + + + + + + + diff --git a/web/simple.html b/web/simple.html deleted file mode 100644 index 599c8ce..0000000 --- a/web/simple.html +++ /dev/null @@ -1,620 +0,0 @@ - - - - - - AqualinkD - - - - - - - - - - - -
-
- - - -
AqualinkD
-
- -